diff options
410 files changed, 27223 insertions, 4379 deletions
diff --git a/Documentation/dvb/bt8xx.txt b/Documentation/dvb/bt8xx.txt index 4e7614e..ecb47ad 100644 --- a/Documentation/dvb/bt8xx.txt +++ b/Documentation/dvb/bt8xx.txt @@ -9,19 +9,29 @@ for accessing the i2c bus and the gpio pins of the bt8xx chipset. Please see Documentation/dvb/cards.txt => o Cards based on the Conexant Bt8xx PCI bridge: Compiling kernel please enable: -a.)"Device drivers" => "Multimedia devices" => "Video For Linux" => "BT848 Video For Linux" -b.)"Device drivers" => "Multimedia devices" => "Digital Video Broadcasting Devices" - => "DVB for Linux" "DVB Core Support" "Bt8xx based PCI Cards" +a.)"Device drivers" => "Multimedia devices" => "Video For Linux" => "Enable Video for Linux API 1 (DEPRECATED)" +b.)"Device drivers" => "Multimedia devices" => "Video For Linux" => "Video Capture Adapters" => "BT848 Video For Linux" +c.)"Device drivers" => "Multimedia devices" => "Digital Video Broadcasting Devices" => "DVB for Linux" "DVB Core Support" "Bt8xx based PCI Cards" -2) Loading Modules -================== +Please use the following options with care as deselection of drivers which are in fact necessary +may result in DVB devices that cannot be tuned due to lack of driver support: +You can save RAM by deselecting every frontend module that your DVB card does not need. + +First please remove the static dependency of DVB card drivers on all frontend modules for all possible card variants by enabling: +d.) "Device drivers" => "Multimedia devices" => "Digital Video Broadcasting Devices" + => "DVB for Linux" "DVB Core Support" "Load and attach frontend modules as needed" -In default cases bttv is loaded automatically. -To load the backend either place dvb-bt8xx in etc/modules, or apply manually: +If you know the frontend driver that your card needs please enable: +e.)"Device drivers" => "Multimedia devices" => "Digital Video Broadcasting Devices" + => "DVB for Linux" "DVB Core Support" "Customise DVB Frontends" => "Customise the frontend modules to build" + Then please select your card-specific frontend module. - $ modprobe dvb-bt8xx +2) Loading Modules +================== -All frontends will be loaded automatically. +Regular case: If the bttv driver detects a bt8xx-based DVB card, all frontend and backend modules will be loaded automatically. +Exceptions are: +- Old TwinHan DST cards or clones with or without CA slot and not containing an Eeprom. People running udev please see Documentation/dvb/udev.txt. In the following cases overriding the PCI type detection for dvb-bt8xx might be necessary: @@ -30,7 +40,6 @@ In the following cases overriding the PCI type detection for dvb-bt8xx might be ------------------------------ $ modprobe bttv card=113 - $ modprobe dvb-bt8xx $ modprobe dst Useful parameters for verbosity level and debugging the dst module: @@ -65,10 +74,9 @@ DViCO FusionHDTV 5 Lite: 135 Notice: The order of the card ID should be uprising: Example: $ modprobe bttv card=113 card=135 - $ modprobe dvb-bt8xx For a full list of card ID's please see Documentation/video4linux/CARDLIST.bttv. -In case of further problems send questions to the mailing list: www.linuxdvb.org. +In case of further problems please subscribe and send questions to the mailing list: linux-dvb@linuxtv.org. Authors: Richard Walker, Jamie Honan, diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware index 4820366..b4d306a 100644 --- a/Documentation/dvb/get_dvb_firmware +++ b/Documentation/dvb/get_dvb_firmware @@ -24,7 +24,8 @@ use IO::Handle; @components = ( "sp8870", "sp887x", "tda10045", "tda10046", "tda10046lifeview", "av7110", "dec2000t", "dec2540t", "dec3000s", "vp7041", "dibusb", "nxt2002", "nxt2004", - "or51211", "or51132_qam", "or51132_vsb", "bluebird"); + "or51211", "or51132_qam", "or51132_vsb", "bluebird", + "opera1"); # Check args syntax() if (scalar(@ARGV) != 1); @@ -56,7 +57,7 @@ syntax(); sub sp8870 { my $sourcefile = "tt_Premium_217g.zip"; - my $url = "http://www.technotrend.de/new/217g/$sourcefile"; + my $url = "http://www.softwarepatch.pl/9999ccd06a4813cb827dbb0005071c71/$sourcefile"; my $hash = "53970ec17a538945a6d8cb608a7b3899"; my $outfile = "dvb-fe-sp8870.fw"; my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 1); @@ -210,6 +211,45 @@ sub dec3000s { $outfile; } +sub opera1{ + my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 0); + + checkstandard(); + my $fwfile1="dvb-usb-opera1-fpga-01.fw"; + my $fwfile2="dvb-usb-opera-01.fw"; + extract("2830SCap2.sys", 0x62e8, 55024, "$tmpdir/opera1-fpga.fw"); + extract("2830SLoad2.sys",0x3178,0x3685-0x3178,"$tmpdir/fw1part1"); + extract("2830SLoad2.sys",0x0980,0x3150-0x0980,"$tmpdir/fw1part2"); + delzero("$tmpdir/fw1part1","$tmpdir/fw1part1-1"); + delzero("$tmpdir/fw1part2","$tmpdir/fw1part2-1"); + verify("$tmpdir/fw1part1-1","5e0909858fdf0b5b09ad48b9fe622e70"); + verify("$tmpdir/fw1part2-1","d6e146f321427e931df2c6fcadac37a1"); + verify("$tmpdir/opera1-fpga.fw","0f8133f5e9051f5f3c1928f7e5a1b07d"); + + my $RES1="\x01\x92\x7f\x00\x01\x00"; + my $RES0="\x01\x92\x7f\x00\x00\x00"; + my $DAT1="\x01\x00\xe6\x00\x01\x00"; + my $DAT0="\x01\x00\xe6\x00\x00\x00"; + open FW,">$tmpdir/opera.fw"; + print FW "$RES1"; + print FW "$DAT1"; + print FW "$RES1"; + print FW "$DAT1"; + appendfile(FW,"$tmpdir/fw1part1-1"); + print FW "$RES0"; + print FW "$DAT0"; + print FW "$RES1"; + print FW "$DAT1"; + appendfile(FW,"$tmpdir/fw1part2-1"); + print FW "$RES1"; + print FW "$DAT1"; + print FW "$RES0"; + print FW "$DAT0"; + copy ("$tmpdir/opera1-fpga.fw",$fwfile1); + copy ("$tmpdir/opera.fw",$fwfile2); + + $fwfile1.",".$fwfile2; +} sub vp7041 { my $sourcefile = "2.422.zip"; @@ -440,6 +480,25 @@ sub appendfile { close(INFILE); } +sub delzero{ + my ($infile,$outfile) =@_; + + open INFILE,"<$infile"; + open OUTFILE,">$outfile"; + while (1){ + $rcount=sysread(INFILE,$buf,22); + $len=ord(substr($buf,0,1)); + print OUTFILE substr($buf,0,1); + print OUTFILE substr($buf,2,$len+3); + last if ($rcount<1); + printf OUTFILE "%c",0; +#print $len." ".length($buf)."\n"; + + } + close(INFILE); + close(OUTFILE); +} + sub syntax() { print STDERR "syntax: get_dvb_firmware <component>\n"; print STDERR "Supported components:\n"; diff --git a/Documentation/dvb/opera-firmware.txt b/Documentation/dvb/opera-firmware.txt new file mode 100644 index 0000000..93e784c --- /dev/null +++ b/Documentation/dvb/opera-firmware.txt @@ -0,0 +1,27 @@ +To extract the firmware for the Opera DVB-S1 USB-Box +you need to copy the files: + +2830SCap2.sys +2830SLoad2.sys + +from the windriver disk into this directory. + +Then run + +./get_dvb_firware opera1 + +and after that you have 2 files: + +dvb-usb-opera-01.fw +dvb-usb-opera1-fpga-01.fw + +in here. + +Copy them into /lib/firmware/ . + +After that the driver can load the firmware +(if you have enabled firmware loading +in kernel config and have hotplug running). + + +Marco Gittler <g.marco@freenet.de>
\ No newline at end of file diff --git a/Documentation/video4linux/CARDLIST.bttv b/Documentation/video4linux/CARDLIST.bttv index b606391..177159c 100644 --- a/Documentation/video4linux/CARDLIST.bttv +++ b/Documentation/video4linux/CARDLIST.bttv @@ -66,7 +66,7 @@ 65 -> Lifeview FlyVideo 2000S LR90 66 -> Terratec TValueRadio [153b:1135,153b:ff3b] 67 -> IODATA GV-BCTV4/PCI [10fc:4050] - 68 -> 3Dfx VoodooTV FM (Euro), VoodooTV 200 (USA) [121a:3000,10b4:2637] + 68 -> 3Dfx VoodooTV FM (Euro) [10b4:2637] 69 -> Active Imaging AIMMS 70 -> Prolink Pixelview PV-BT878P+ (Rev.4C,8E) 71 -> Lifeview FlyVideo 98EZ (capture only) LR51 [1851:1851] @@ -145,3 +145,5 @@ 144 -> MagicTV 145 -> SSAI Security Video Interface [4149:5353] 146 -> SSAI Ultrasound Video Interface [414a:5353] +147 -> VoodooTV 200 (USA) [121a:3000] +148 -> DViCO FusionHDTV 2 [dbc0:d200] diff --git a/Documentation/video4linux/CARDLIST.cx88 b/Documentation/video4linux/CARDLIST.cx88 index 60f838b..82ac825 100644 --- a/Documentation/video4linux/CARDLIST.cx88 +++ b/Documentation/video4linux/CARDLIST.cx88 @@ -55,3 +55,4 @@ 54 -> Norwood Micro TV Tuner 55 -> Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM [c180:c980] 56 -> Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder [0070:9600,0070:9601,0070:9602] + 57 -> ADS Tech Instant Video PCI [1421:0390] diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index 712e8c8..3f8aeab 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -114,3 +114,4 @@ 113 -> Elitegroup ECS TVP3XP FM1246 Tuner Card (PAL,FM) [1019:4cb6] 114 -> KWorld DVB-T 210 [17de:7250] 115 -> Sabrent PCMCIA TV-PCB05 [0919:2003] +116 -> 10MOONS TM300 TV Card [1131:2304] diff --git a/Documentation/video4linux/CARDLIST.tuner b/Documentation/video4linux/CARDLIST.tuner index 44134f0..a88c02d 100644 --- a/Documentation/video4linux/CARDLIST.tuner +++ b/Documentation/video4linux/CARDLIST.tuner @@ -40,7 +40,7 @@ tuner=38 - Philips PAL/SECAM multi (FM1216ME MK3) tuner=39 - LG NTSC (newer TAPC series) tuner=40 - HITACHI V7-J180AT tuner=41 - Philips PAL_MK (FI1216 MK) -tuner=42 - Philips 1236D ATSC/NTSC dual in +tuner=42 - Philips FCV1236D ATSC/NTSC dual in tuner=43 - Philips NTSC MK3 (FM1236MK3 or FM1236/F) tuner=44 - Philips 4 in 1 (ATI TV Wonder Pro/Conexant) tuner=45 - Microtune 4049 FM5 @@ -72,3 +72,4 @@ tuner=70 - Samsung TCPN 2121P30A tuner=71 - Xceive xc3028 tuner=72 - Thomson FE6600 tuner=73 - Samsung TCPG 6121P30A +tuner=75 - Philips TEA5761 FM Radio diff --git a/Documentation/video4linux/sn9c102.txt b/Documentation/video4linux/sn9c102.txt index 279717c..1ffad19 100644 --- a/Documentation/video4linux/sn9c102.txt +++ b/Documentation/video4linux/sn9c102.txt @@ -436,7 +436,7 @@ HV7131D Hynix Semiconductor | Yes No No No HV7131R Hynix Semiconductor | No Yes Yes Yes MI-0343 Micron Technology | Yes No No No MI-0360 Micron Technology | No Yes Yes Yes -OV7630 OmniVision Technologies | Yes Yes No No +OV7630 OmniVision Technologies | Yes Yes Yes Yes OV7660 OmniVision Technologies | No No Yes Yes PAS106B PixArt Imaging | Yes No No No PAS202B PixArt Imaging | Yes Yes No No @@ -583,6 +583,7 @@ order): - Bertrik Sikken, who reverse-engineered and documented the Huffman compression algorithm used in the SN9C101, SN9C102 and SN9C103 controllers and implemented the first decoder; +- Ronny Standke for the donation of a webcam; - Mizuno Takafumi for the donation of a webcam; - an "anonymous" donator (who didn't want his name to be revealed) for the donation of a webcam. diff --git a/Documentation/video4linux/zr364xx.txt b/Documentation/video4linux/zr364xx.txt index c76992d..4d9a0c3 100644 --- a/Documentation/video4linux/zr364xx.txt +++ b/Documentation/video4linux/zr364xx.txt @@ -62,4 +62,4 @@ Vendor Product Distributor Model 0x0784 0x0040 Traveler Slimline X5 0x06d6 0x0034 Trust Powerc@m 750 0x0a17 0x0062 Pentax Optio 50L - +0x06d6 0x003b Trust Powerc@m 970Z diff --git a/MAINTAINERS b/MAINTAINERS index a9615a56..e78f62f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1740,6 +1740,7 @@ S: Maintained i386 SETUP CODE / CPU ERRATA WORKAROUNDS P: H. Peter Anvin M: hpa@zytor.com +T: git.kernel.org:/pub/scm/linux/kernel/git/hpa/linux-2.6-x86setup.git S: Maintained IA64 (Itanium) PLATFORM @@ -2393,7 +2394,7 @@ P: Artem Bityutskiy M: dedekind@infradead.org W: http://www.linux-mtd.infradead.org/ L: linux-mtd@lists.infradead.org -T: git git://git.infradead.org/ubi-2.6.git +T: git git://git.infradead.org/~dedekind/ubi-2.6.git S: Maintained MICROTEK X6 SCANNER diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig index c7c9c2a..7a11b90 100644 --- a/arch/i386/Kconfig +++ b/arch/i386/Kconfig @@ -222,6 +222,8 @@ config PARAVIRT However, when run without a hypervisor the kernel is theoretically slower. If in doubt, say N. +source "arch/i386/xen/Kconfig" + config VMI bool "VMI Paravirt-ops support" depends on PARAVIRT diff --git a/arch/i386/Makefile b/arch/i386/Makefile index 181cc29..01f0ff0 100644 --- a/arch/i386/Makefile +++ b/arch/i386/Makefile @@ -93,6 +93,9 @@ mflags-$(CONFIG_X86_ES7000) := -Iinclude/asm-i386/mach-es7000 mcore-$(CONFIG_X86_ES7000) := mach-default core-$(CONFIG_X86_ES7000) := arch/i386/mach-es7000/ +# Xen paravirtualization support +core-$(CONFIG_XEN) += arch/i386/xen/ + # default subarch .h files mflags-y += -Iinclude/asm-i386/mach-default diff --git a/arch/i386/boot/Makefile b/arch/i386/boot/Makefile index 08678a0a..93386a4 100644 --- a/arch/i386/boot/Makefile +++ b/arch/i386/boot/Makefile @@ -39,7 +39,7 @@ setup-y += printf.o string.o tty.o video.o version.o voyager.o setup-y += video-vga.o setup-y += video-vesa.o setup-y += video-bios.o - +targets += $(setup-y) hostprogs-y := tools/build HOSTCFLAGS_build.o := $(LINUXINCLUDE) diff --git a/arch/i386/boot/boot.h b/arch/i386/boot/boot.h index 0329c4f..dec70c9 100644 --- a/arch/i386/boot/boot.h +++ b/arch/i386/boot/boot.h @@ -56,7 +56,7 @@ static inline u16 inw(u16 port) static inline void outl(u32 v, u16 port) { - asm volatile("outl %0,%1" : : "a" (v), "dn" (port)); + asm volatile("outl %0,%1" : : "a" (v), "dN" (port)); } static inline u32 inl(u32 port) { diff --git a/arch/i386/boot/compressed/relocs.c b/arch/i386/boot/compressed/relocs.c index ce4fda2..b0e21c3 100644 --- a/arch/i386/boot/compressed/relocs.c +++ b/arch/i386/boot/compressed/relocs.c @@ -31,6 +31,8 @@ static const char* safe_abs_relocs[] = { "__kernel_rt_sigreturn", "__kernel_sigreturn", "SYSENTER_RETURN", + "xen_irq_disable_direct_reloc", + "xen_save_fl_direct_reloc", }; static int is_safe_abs_reloc(const char* sym_name) diff --git a/arch/i386/boot/cpucheck.c b/arch/i386/boot/cpucheck.c index 8b0f447..991e8ce 100644 --- a/arch/i386/boot/cpucheck.c +++ b/arch/i386/boot/cpucheck.c @@ -115,8 +115,8 @@ static int has_eflag(u32 mask) "pushfl ; " "popl %1 ; " "popfl" - : "=r" (f0), "=r" (f1) - : "g" (mask)); + : "=&r" (f0), "=&r" (f1) + : "ri" (mask)); return !!((f0^f1) & mask); } diff --git a/arch/i386/boot/mca.c b/arch/i386/boot/mca.c index 9b68bd1..68222f2 100644 --- a/arch/i386/boot/mca.c +++ b/arch/i386/boot/mca.c @@ -26,7 +26,7 @@ int query_mca(void) "setc %0 ; " "movw %%es, %1 ; " "popw %%es" - : "=acdSDm" (err), "=acdSDm" (es), "=b" (bx) + : "=acd" (err), "=acdSD" (es), "=b" (bx) : "a" (0xc000)); if (err) diff --git a/arch/i386/boot/pm.c b/arch/i386/boot/pm.c index 3fa53e1..1df025c 100644 --- a/arch/i386/boot/pm.c +++ b/arch/i386/boot/pm.c @@ -65,7 +65,7 @@ static void move_kernel_around(void) "popw %%ds ; " "popw %%es" : "+c" (dwords) - : "rm" (dst_seg), "rm" (src_seg) + : "r" (dst_seg), "r" (src_seg) : "esi", "edi"); syssize -= paras; diff --git a/arch/i386/boot/tools/build.c b/arch/i386/boot/tools/build.c index 886f47d8..b424874 100644 --- a/arch/i386/boot/tools/build.c +++ b/arch/i386/boot/tools/build.c @@ -5,7 +5,7 @@ */ /* - * This file builds a disk-image from three different files: + * This file builds a disk-image from two different files: * * - setup: 8086 machine code, sets up system parm * - system: 80386 code for actual system diff --git a/arch/i386/boot/tty.c b/arch/i386/boot/tty.c index a8db787..9c668aa 100644 --- a/arch/i386/boot/tty.c +++ b/arch/i386/boot/tty.c @@ -31,7 +31,7 @@ void __attribute__((section(".inittext"))) putchar(int ch) /* int $0x10 is known to have bugs involving touching registers it shouldn't. Be extra conservative... */ - asm volatile("pushal; int $0x10; popal" + asm volatile("pushal; pushw %%ds; int $0x10; popw %%ds; popal" : : "b" (0x0007), "c" (0x0001), "a" (0x0e00|ch)); } diff --git a/arch/i386/boot/video.c b/arch/i386/boot/video.c index 3bb3573..958130e 100644 --- a/arch/i386/boot/video.c +++ b/arch/i386/boot/video.c @@ -195,7 +195,7 @@ static void vga_recalc_vertical(void) { unsigned int font_size, rows; u16 crtc; - u8 ov; + u8 pt, ov; set_fs(0); font_size = rdfs8(0x485); /* BIOS: font size (pixels) */ @@ -206,7 +206,12 @@ static void vga_recalc_vertical(void) crtc = vga_crtc(); + pt = in_idx(crtc, 0x11); + pt &= ~0x80; /* Unlock CR0-7 */ + out_idx(pt, crtc, 0x11); + out_idx((u8)rows, crtc, 0x12); /* Lower height register */ + ov = in_idx(crtc, 0x07); /* Overflow register */ ov &= 0xbd; ov |= (rows >> (8-1)) & 0x02; @@ -411,7 +416,7 @@ static void restore_screen(void) "1: rep;stosl ; " "popw %%es" : "+D" (dst), "+c" (npad) - : "bdSm" (video_segment), + : "bdS" (video_segment), "a" (0x07200720)); } diff --git a/arch/i386/boot/video.h b/arch/i386/boot/video.h index 29eca17..b92447d 100644 --- a/arch/i386/boot/video.h +++ b/arch/i386/boot/video.h @@ -117,8 +117,15 @@ extern int graphic_mode; /* Graphics mode with linear frame buffer */ * int $0x10 is notorious for touching registers it shouldn't. * gcc doesn't like %ebp being clobbered, so define it as a push/pop * sequence here. + * + * A number of systems, including the original PC can clobber %bp in + * certain circumstances, like when scrolling. There exists at least + * one Trident video card which could clobber DS under a set of + * circumstances that we are unlikely to encounter (scrolling when + * using an extended graphics mode of more than 800x600 pixels), but + * it's cheap insurance to deal with that here. */ -#define INT10 "pushl %%ebp; int $0x10; popl %%ebp" +#define INT10 "pushl %%ebp; pushw %%ds; int $0x10; popw %%ds; popl %%ebp" /* Accessing VGA indexed registers */ static inline u8 in_idx(u16 port, u8 index) diff --git a/arch/i386/boot/voyager.c b/arch/i386/boot/voyager.c index 9221614..61c8fe0 100644 --- a/arch/i386/boot/voyager.c +++ b/arch/i386/boot/voyager.c @@ -32,7 +32,7 @@ int query_voyager(void) "setc %0 ; " "movw %%es, %1 ; " "popw %%es" - : "=qm" (err), "=rm" (es), "=D" (di) + : "=q" (err), "=r" (es), "=D" (di) : "a" (0xffc0)); if (err) diff --git a/arch/i386/kernel/asm-offsets.c b/arch/i386/kernel/asm-offsets.c index 27a776c..25f7eb5 100644 --- a/arch/i386/kernel/asm-offsets.c +++ b/arch/i386/kernel/asm-offsets.c @@ -17,6 +17,8 @@ #include <asm/thread_info.h> #include <asm/elf.h> +#include <xen/interface/xen.h> + #define DEFINE(sym, val) \ asm volatile("\n->" #sym " %0 " #val : : "i" (val)) @@ -59,6 +61,7 @@ void foo(void) OFFSET(TI_addr_limit, thread_info, addr_limit); OFFSET(TI_restart_block, thread_info, restart_block); OFFSET(TI_sysenter_return, thread_info, sysenter_return); + OFFSET(TI_cpu, thread_info, cpu); BLANK(); OFFSET(GDS_size, Xgt_desc_struct, size); @@ -115,4 +118,10 @@ void foo(void) OFFSET(PARAVIRT_iret, paravirt_ops, iret); OFFSET(PARAVIRT_read_cr0, paravirt_ops, read_cr0); #endif + +#ifdef CONFIG_XEN + BLANK(); + OFFSET(XEN_vcpu_info_mask, vcpu_info, evtchn_upcall_mask); + OFFSET(XEN_vcpu_info_pending, vcpu_info, evtchn_upcall_pending); +#endif } diff --git a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S index 3c3c220..a714d6b 100644 --- a/arch/i386/kernel/entry.S +++ b/arch/i386/kernel/entry.S @@ -409,8 +409,6 @@ restore_nocheck_notrace: 1: INTERRUPT_RETURN .section .fixup,"ax" iret_exc: - TRACE_IRQS_ON - ENABLE_INTERRUPTS(CLBR_NONE) pushl $0 # no error code pushl $do_iret_error jmp error_code @@ -1023,6 +1021,91 @@ ENTRY(kernel_thread_helper) CFI_ENDPROC ENDPROC(kernel_thread_helper) +#ifdef CONFIG_XEN +ENTRY(xen_hypervisor_callback) + CFI_STARTPROC + pushl $0 + CFI_ADJUST_CFA_OFFSET 4 + SAVE_ALL + TRACE_IRQS_OFF + + /* Check to see if we got the event in the critical + region in xen_iret_direct, after we've reenabled + events and checked for pending events. This simulates + iret instruction's behaviour where it delivers a + pending interrupt when enabling interrupts. */ + movl PT_EIP(%esp),%eax + cmpl $xen_iret_start_crit,%eax + jb 1f + cmpl $xen_iret_end_crit,%eax + jae 1f + + call xen_iret_crit_fixup + +1: mov %esp, %eax + call xen_evtchn_do_upcall + jmp ret_from_intr + CFI_ENDPROC +ENDPROC(xen_hypervisor_callback) + +# Hypervisor uses this for application faults while it executes. +# We get here for two reasons: +# 1. Fault while reloading DS, ES, FS or GS +# 2. Fault while executing IRET +# Category 1 we fix up by reattempting the load, and zeroing the segment +# register if the load fails. +# Category 2 we fix up by jumping to do_iret_error. We cannot use the +# normal Linux return path in this case because if we use the IRET hypercall +# to pop the stack frame we end up in an infinite loop of failsafe callbacks. +# We distinguish between categories by maintaining a status value in EAX. +ENTRY(xen_failsafe_callback) + CFI_STARTPROC + pushl %eax + CFI_ADJUST_CFA_OFFSET 4 + movl $1,%eax +1: mov 4(%esp),%ds +2: mov 8(%esp),%es +3: mov 12(%esp),%fs +4: mov 16(%esp),%gs + testl %eax,%eax + popl %eax + CFI_ADJUST_CFA_OFFSET -4 + lea 16(%esp),%esp + CFI_ADJUST_CFA_OFFSET -16 + jz 5f + addl $16,%esp + jmp iret_exc # EAX != 0 => Category 2 (Bad IRET) +5: pushl $0 # EAX == 0 => Category 1 (Bad segment) + CFI_ADJUST_CFA_OFFSET 4 + SAVE_ALL + jmp ret_from_exception + CFI_ENDPROC + +.section .fixup,"ax" +6: xorl %eax,%eax + movl %eax,4(%esp) + jmp 1b +7: xorl %eax,%eax + movl %eax,8(%esp) + jmp 2b +8: xorl %eax,%eax + movl %eax,12(%esp) + jmp 3b +9: xorl %eax,%eax + movl %eax,16(%esp) + jmp 4b +.previous +.section __ex_table,"a" + .align 4 + .long 1b,6b + .long 2b,7b + .long 3b,8b + .long 4b,9b +.previous +ENDPROC(xen_failsafe_callback) + +#endif /* CONFIG_XEN */ + .section .rodata,"a" #include "syscall_table.S" diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S index 8271466..7c52b22 100644 --- a/arch/i386/kernel/head.S +++ b/arch/i386/kernel/head.S @@ -510,7 +510,8 @@ ENTRY(_stext) /* * BSS section */ -.section ".bss.page_aligned","w" +.section ".bss.page_aligned","wa" + .align PAGE_SIZE_asm ENTRY(swapper_pg_dir) .fill 1024,4,0 ENTRY(swapper_pg_pmd) @@ -538,6 +539,8 @@ fault_msg: .ascii "Int %d: CR2 %p err %p EIP %p CS %p flags %p\n" .asciz "Stack: %p %p %p %p %p %p %p %p\n" +#include "../xen/xen-head.S" + /* * The IDT and GDT 'descriptors' are a strange 48-bit object * only used by the lidt and lgdt instructions. They are not diff --git a/arch/i386/kernel/paravirt.c b/arch/i386/kernel/paravirt.c index faab09a..53f07a8 100644 --- a/arch/i386/kernel/paravirt.c +++ b/arch/i386/kernel/paravirt.c @@ -228,6 +228,41 @@ static int __init print_banner(void) } core_initcall(print_banner); +static struct resource reserve_ioports = { + .start = 0, + .end = IO_SPACE_LIMIT, + .name = "paravirt-ioport", + .flags = IORESOURCE_IO | IORESOURCE_BUSY, +}; + +static struct resource reserve_iomem = { + .start = 0, + .end = -1, + .name = "paravirt-iomem", + .flags = IORESOURCE_MEM | IORESOURCE_BUSY, +}; + +/* + * Reserve the whole legacy IO space to prevent any legacy drivers + * from wasting time probing for their hardware. This is a fairly + * brute-force approach to disabling all non-virtual drivers. + * + * Note that this must be called very early to have any effect. + */ +int paravirt_disable_iospace(void) +{ + int ret; + + ret = request_resource(&ioport_resource, &reserve_ioports); + if (ret == 0) { + ret = request_resource(&iomem_resource, &reserve_iomem); + if (ret) + release_resource(&reserve_ioports); + } + + return ret; +} + struct paravirt_ops paravirt_ops = { .name = "bare hardware", .paravirt_enabled = 0, @@ -267,7 +302,7 @@ struct paravirt_ops paravirt_ops = { .write_msr = native_write_msr_safe, .read_tsc = native_read_tsc, .read_pmc = native_read_pmc, - .get_scheduled_cycles = native_read_tsc, + .sched_clock = native_sched_clock, .get_cpu_khz = native_calculate_cpu_khz, .load_tr_desc = native_load_tr_desc, .set_ldt = native_set_ldt, diff --git a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c index 1c075f5..0c8f00e 100644 --- a/arch/i386/kernel/ptrace.c +++ b/arch/i386/kernel/ptrace.c @@ -164,14 +164,22 @@ static unsigned long convert_eip_to_linear(struct task_struct *child, struct pt_ u32 *desc; unsigned long base; - down(&child->mm->context.sem); - desc = child->mm->context.ldt + (seg & ~7); - base = (desc[0] >> 16) | ((desc[1] & 0xff) << 16) | (desc[1] & 0xff000000); + seg &= ~7UL; - /* 16-bit code segment? */ - if (!((desc[1] >> 22) & 1)) - addr &= 0xffff; - addr += base; + down(&child->mm->context.sem); + if (unlikely((seg >> 3) >= child->mm->context.size)) + addr = -1L; /* bogus selector, access would fault */ + else { + desc = child->mm->context.ldt + seg; + base = ((desc[0] >> 16) | + ((desc[1] & 0xff) << 16) | + (desc[1] & 0xff000000)); + + /* 16-bit code segment? */ + if (!((desc[1] >> 22) & 1)) + addr &= 0xffff; + addr += base; + } up(&child->mm->context.sem); } return addr; diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 2d61e65..74871d0 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -601,6 +601,8 @@ void __init setup_arch(char **cmdline_p) * NOTE: at this point the bootmem allocator is fully available. */ + paravirt_post_allocator_init(); + dmi_scan_machine(); #ifdef CONFIG_X86_GENERICARCH diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c index 6299c08..2d35d85 100644 --- a/arch/i386/kernel/smp.c +++ b/arch/i386/kernel/smp.c @@ -22,6 +22,7 @@ #include <asm/mtrr.h> #include <asm/tlbflush.h> +#include <asm/mmu_context.h> #include <mach_apic.h> /* @@ -249,13 +250,13 @@ static unsigned long flush_va; static DEFINE_SPINLOCK(tlbstate_lock); /* - * We cannot call mmdrop() because we are in interrupt context, + * We cannot call mmdrop() because we are in interrupt context, * instead update mm->cpu_vm_mask. * * We need to reload %cr3 since the page tables may be going * away from under us.. */ -static inline void leave_mm (unsigned long cpu) +void leave_mm(unsigned long cpu) { if (per_cpu(cpu_tlbstate, cpu).state == TLBSTATE_OK) BUG(); diff --git a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c index 0b29545..5910d3f 100644 --- a/arch/i386/kernel/smpboot.c +++ b/arch/i386/kernel/smpboot.c @@ -148,7 +148,7 @@ void __init smp_alloc_memory(void) * a given CPU */ -static void __cpuinit smp_store_cpu_info(int id) +void __cpuinit smp_store_cpu_info(int id) { struct cpuinfo_x86 *c = cpu_data + id; @@ -308,8 +308,7 @@ cpumask_t cpu_coregroup_map(int cpu) /* representing cpus for which sibling maps can be computed */ static cpumask_t cpu_sibling_setup_map; -static inline void -set_cpu_sibling_map(int cpu) +void set_cpu_sibling_map(int cpu) { int i; struct cpuinfo_x86 *c = cpu_data; @@ -1144,8 +1143,7 @@ void __init native_smp_prepare_boot_cpu(void) } #ifdef CONFIG_HOTPLUG_CPU -static void -remove_siblinginfo(int cpu) +void remove_siblinginfo(int cpu) { int sibling; struct cpuinfo_x86 *c = cpu_data; diff --git a/arch/i386/kernel/syscall_table.S b/arch/i386/kernel/syscall_table.S index bf6adce..8344c70 100644 --- a/arch/i386/kernel/syscall_table.S +++ b/arch/i386/kernel/syscall_table.S @@ -323,3 +323,4 @@ ENTRY(sys_call_table) .long sys_signalfd .long sys_timerfd .long sys_eventfd + .long sys_fallocate diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 18c1c28..d32fd4b 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -518,10 +518,12 @@ fastcall void do_##name(struct pt_regs * regs, long error_code) \ do_trap(trapnr, signr, str, 0, regs, error_code, NULL); \ } -#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \ +#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr, irq) \ fastcall void do_##name(struct pt_regs * regs, long error_code) \ { \ siginfo_t info; \ + if (irq) \ + local_irq_enable(); \ info.si_signo = signr; \ info.si_errno = 0; \ info.si_code = sicode; \ @@ -561,13 +563,13 @@ DO_VM86_ERROR( 3, SIGTRAP, "int3", int3) #endif DO_VM86_ERROR( 4, SIGSEGV, "overflow", overflow) DO_VM86_ERROR( 5, SIGSEGV, "bounds", bounds) -DO_ERROR_INFO( 6, SIGILL, "invalid opcode", invalid_op, ILL_ILLOPN, regs->eip) +DO_ERROR_INFO( 6, SIGILL, "invalid opcode", invalid_op, ILL_ILLOPN, regs->eip, 0) DO_ERROR( 9, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun) DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS) DO_ERROR(11, SIGBUS, "segment not present", segment_not_present) DO_ERROR(12, SIGBUS, "stack segment", stack_segment) -DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0) -DO_ERROR_INFO(32, SIGSEGV, "iret exception", iret_error, ILL_BADSTK, 0) +DO_ERROR_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0, 0) +DO_ERROR_INFO(32, SIGSEGV, "iret exception", iret_error, ILL_BADSTK, 0, 1) fastcall void __kprobes do_general_protection(struct pt_regs * regs, long error_code) diff --git a/arch/i386/kernel/tsc.c b/arch/i386/kernel/tsc.c index ea63a30..252f901 100644 --- a/arch/i386/kernel/tsc.c +++ b/arch/i386/kernel/tsc.c @@ -84,7 +84,7 @@ static inline int check_tsc_unstable(void) * * -johnstul@us.ibm.com "math is hard, lets go shopping!" */ -static unsigned long cyc2ns_scale __read_mostly; +unsigned long cyc2ns_scale __read_mostly; #define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */ @@ -93,15 +93,10 @@ static inline void set_cyc2ns_scale(unsigned long cpu_khz) cyc2ns_scale = (1000000 << CYC2NS_SCALE_FACTOR)/cpu_khz; } -static inline unsigned long long cycles_2_ns(unsigned long long cyc) -{ - return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR; -} - /* * Scheduler clock - returns current time in nanosec units. */ -unsigned long long sched_clock(void) +unsigned long long native_sched_clock(void) { unsigned long long this_offset; @@ -118,12 +113,24 @@ unsigned long long sched_clock(void) return (jiffies_64 - INITIAL_JIFFIES) * (1000000000 / HZ); /* read the Time Stamp Counter: */ - get_scheduled_cycles(this_offset); + rdtscll(this_offset); /* return the value in ns */ return cycles_2_ns(this_offset); } +/* We need to define a real function for sched_clock, to override the + weak default version */ +#ifdef CONFIG_PARAVIRT +unsigned long long sched_clock(void) +{ + return paravirt_sched_clock(); +} +#else +unsigned long long sched_clock(void) + __attribute__((alias("native_sched_clock"))); +#endif + unsigned long native_calculate_cpu_khz(void) { unsigned long long start, end; diff --git a/arch/i386/kernel/vmi.c b/arch/i386/kernel/vmi.c index c12720d..72042bb 100644 --- a/arch/i386/kernel/vmi.c +++ b/arch/i386/kernel/vmi.c @@ -362,7 +362,7 @@ static void *vmi_kmap_atomic_pte(struct page *page, enum km_type type) } #endif -static void vmi_allocate_pt(u32 pfn) +static void vmi_allocate_pt(struct mm_struct *mm, u32 pfn) { vmi_set_page_type(pfn, VMI_PAGE_L1); vmi_ops.allocate_page(pfn, VMI_PAGE_L1, 0, 0, 0); @@ -891,7 +891,7 @@ static inline int __init activate_vmi(void) paravirt_ops.setup_boot_clock = vmi_time_bsp_init; paravirt_ops.setup_secondary_clock = vmi_time_ap_init; #endif - paravirt_ops.get_scheduled_cycles = vmi_get_sched_cycles; + paravirt_ops.sched_clock = vmi_sched_clock; paravirt_ops.get_cpu_khz = vmi_cpu_khz; /* We have true wallclock functions; disable CMOS clock sync */ diff --git a/arch/i386/kernel/vmiclock.c b/arch/i386/kernel/vmiclock.c index 26a37f8..f9b845f 100644 --- a/arch/i386/kernel/vmiclock.c +++ b/arch/i386/kernel/vmiclock.c @@ -64,10 +64,10 @@ int vmi_set_wallclock(unsigned long now) return 0; } -/* paravirt_ops.get_scheduled_cycles = vmi_get_sched_cycles */ -unsigned long long vmi_get_sched_cycles(void) +/* paravirt_ops.sched_clock = vmi_sched_clock */ +unsigned long long vmi_sched_clock(void) { - return vmi_timer_ops.get_cycle_counter(VMI_CYCLES_AVAILABLE); + return cycles_2_ns(vmi_timer_ops.get_cycle_counter(VMI_CYCLES_AVAILABLE)); } /* paravirt_ops.get_cpu_khz = vmi_cpu_khz */ diff --git a/arch/i386/kernel/vmlinux.lds.S b/arch/i386/kernel/vmlinux.lds.S index aa87b06..00f1bc4 100644 --- a/arch/i386/kernel/vmlinux.lds.S +++ b/arch/i386/kernel/vmlinux.lds.S @@ -88,6 +88,7 @@ SECTIONS . = ALIGN(4096); .data.page_aligned : AT(ADDR(.data.page_aligned) - LOAD_OFFSET) { + *(.data.page_aligned) *(.data.idt) } diff --git a/arch/i386/kernel/vsyscall-note.S b/arch/i386/kernel/vsyscall-note.S index d4b5be4..271f16a 100644 --- a/arch/i386/kernel/vsyscall-note.S +++ b/arch/i386/kernel/vsyscall-note.S @@ -3,23 +3,40 @@ * Here we can supply some information useful to userland. */ -#include <linux/uts.h> #include <linux/version.h> +#include <linux/elfnote.h> -#define ASM_ELF_NOTE_BEGIN(name, flags, vendor, type) \ - .section name, flags; \ - .balign 4; \ - .long 1f - 0f; /* name length */ \ - .long 3f - 2f; /* data length */ \ - .long type; /* note type */ \ -0: .asciz vendor; /* vendor name */ \ -1: .balign 4; \ -2: +/* Ideally this would use UTS_NAME, but using a quoted string here + doesn't work. Remember to change this when changing the + kernel's name. */ +ELFNOTE_START(Linux, 0, "a") + .long LINUX_VERSION_CODE +ELFNOTE_END -#define ASM_ELF_NOTE_END \ -3: .balign 4; /* pad out section */ \ - .previous +#ifdef CONFIG_XEN - ASM_ELF_NOTE_BEGIN(".note.kernel-version", "a", UTS_SYSNAME, 0) - .long LINUX_VERSION_CODE - ASM_ELF_NOTE_END +/* + * Add a special note telling glibc's dynamic linker a fake hardware + * flavor that it will use to choose the search path for libraries in the + * same way it uses real hardware capabilities like "mmx". + * We supply "nosegneg" as the fake capability, to indicate that we + * do not like negative offsets in instructions using segment overrides, + * since we implement those inefficiently. This makes it possible to + * install libraries optimized to avoid those access patterns in someplace + * like /lib/i686/tls/nosegneg. Note that an /etc/ld.so.conf.d/file + * corresponding to the bits here is needed to make ldconfig work right. + * It should contain: + * hwcap 1 nosegneg + * to match the mapping of bit to name that we give here. + */ + +/* Bit used for the pseudo-hwcap for non-negative segments. We use + bit 1 to avoid bugs in some versions of glibc when bit 0 is + used; the choice is otherwise arbitrary. */ +#define VDSO_NOTE_NONEGSEG_BIT 1 + +ELFNOTE_START(GNU, 2, "a") + .long 1, 1<<VDSO_NOTE_NONEGSEG_BIT /* ncaps, mask */ + .byte VDSO_NOTE_NONEGSEG_BIT; .asciz "nosegneg" /* bit, name */ +ELFNOTE_END +#endif diff --git a/arch/i386/mach-voyager/voyager_thread.c b/arch/i386/mach-voyager/voyager_thread.c index b4b24e0..f9d5953 100644 --- a/arch/i386/mach-voyager/voyager_thread.c +++ b/arch/i386/mach-voyager/voyager_thread.c @@ -52,7 +52,7 @@ execute(const char *string) NULL, }; - if ((ret = call_usermodehelper(argv[0], argv, envp, 1)) != 0) { + if ((ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC)) != 0) { printk(KERN_ERR "Voyager failed to run \"%s\": %i\n", string, ret); } diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c index 7135946..6a68b1a 100644 --- a/arch/i386/mm/init.c +++ b/arch/i386/mm/init.c @@ -87,7 +87,7 @@ static pte_t * __init one_page_table_init(pmd_t *pmd) if (!(pmd_val(*pmd) & _PAGE_PRESENT)) { pte_t *page_table = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); - paravirt_alloc_pt(__pa(page_table) >> PAGE_SHIFT); + paravirt_alloc_pt(&init_mm, __pa(page_table) >> PAGE_SHIFT); set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE)); BUG_ON(page_table != pte_offset_kernel(pmd, 0)); } @@ -473,6 +473,7 @@ void zap_low_mappings (void) static int disable_nx __initdata = 0; u64 __supported_pte_mask __read_mostly = ~_PAGE_NX; +EXPORT_SYMBOL_GPL(__supported_pte_mask); /* * noexec = on|off diff --git a/arch/i386/mm/pageattr.c b/arch/i386/mm/pageattr.c index 2eb14a7..37992ff 100644 --- a/arch/i386/mm/pageattr.c +++ b/arch/i386/mm/pageattr.c @@ -60,7 +60,7 @@ static struct page *split_large_page(unsigned long address, pgprot_t prot, address = __pa(address); addr = address & LARGE_PAGE_MASK; pbase = (pte_t *)page_address(base); - paravirt_alloc_pt(page_to_pfn(base)); + paravirt_alloc_pt(&init_mm, page_to_pfn(base)); for (i = 0; i < PTRS_PER_PTE; i++, addr += PAGE_SIZE) { set_pte(&pbase[i], pfn_pte(addr >> PAGE_SHIFT, addr == address ? prot : ref_prot)); diff --git a/arch/i386/xen/Kconfig b/arch/i386/xen/Kconfig new file mode 100644 index 0000000..9df99e1 --- /dev/null +++ b/arch/i386/xen/Kconfig @@ -0,0 +1,11 @@ +# +# This Kconfig describes xen options +# + +config XEN + bool "Enable support for Xen hypervisor" + depends on PARAVIRT && X86_CMPXCHG && X86_TSC && !NEED_MULTIPLE_NODES + help + This is the Linux Xen port. Enabling this will allow the + kernel to boot in a paravirtualized environment under the + Xen hypervisor. diff --git a/arch/i386/xen/Makefile b/arch/i386/xen/Makefile new file mode 100644 index 0000000..343df24 --- /dev/null +++ b/arch/i386/xen/Makefile @@ -0,0 +1,4 @@ +obj-y := enlighten.o setup.o features.o multicalls.o mmu.o \ + events.o time.o manage.o xen-asm.o + +obj-$(CONFIG_SMP) += smp.o diff --git a/arch/i386/xen/enlighten.c b/arch/i386/xen/enlighten.c new file mode 100644 index 0000000..9a8c118 --- /dev/null +++ b/arch/i386/xen/enlighten.c @@ -0,0 +1,1144 @@ +/* + * Core of Xen paravirt_ops implementation. + * + * This file contains the xen_paravirt_ops structure itself, and the + * implementations for: + * - privileged instructions + * - interrupt flags + * - segment operations + * - booting and setup + * + * Jeremy Fitzhardinge <jeremy@xensource.com>, XenSource Inc, 2007 + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/preempt.h> +#include <linux/hardirq.h> +#include <linux/percpu.h> +#include <linux/delay.h> +#include <linux/start_kernel.h> +#include <linux/sched.h> +#include <linux/bootmem.h> +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/page-flags.h> +#include <linux/highmem.h> +#include <linux/smp.h> + +#include <xen/interface/xen.h> +#include <xen/interface/physdev.h> +#include <xen/interface/vcpu.h> +#include <xen/interface/sched.h> +#include <xen/features.h> +#include <xen/page.h> + +#include <asm/paravirt.h> +#include <asm/page.h> +#include <asm/xen/hypercall.h> +#include <asm/xen/hypervisor.h> +#include <asm/fixmap.h> +#include <asm/processor.h> +#include <asm/setup.h> +#include <asm/desc.h> +#include <asm/pgtable.h> +#include <asm/tlbflush.h> +#include <asm/reboot.h> + +#include "xen-ops.h" +#include "mmu.h" +#include "multicalls.h" + +EXPORT_SYMBOL_GPL(hypercall_page); + +DEFINE_PER_CPU(enum paravirt_lazy_mode, xen_lazy_mode); + +DEFINE_PER_CPU(struct vcpu_info *, xen_vcpu); +DEFINE_PER_CPU(struct vcpu_info, xen_vcpu_info); +DEFINE_PER_CPU(unsigned long, xen_cr3); + +struct start_info *xen_start_info; +EXPORT_SYMBOL_GPL(xen_start_info); + +static /* __initdata */ struct shared_info dummy_shared_info; + +/* + * Point at some empty memory to start with. We map the real shared_info + * page as soon as fixmap is up and running. + */ +struct shared_info *HYPERVISOR_shared_info = (void *)&dummy_shared_info; + +/* + * Flag to determine whether vcpu info placement is available on all + * VCPUs. We assume it is to start with, and then set it to zero on + * the first failure. This is because it can succeed on some VCPUs + * and not others, since it can involve hypervisor memory allocation, + * or because the guest failed to guarantee all the appropriate + * constraints on all VCPUs (ie buffer can't cross a page boundary). + * + * Note that any particular CPU may be using a placed vcpu structure, + * but we can only optimise if the all are. + * + * 0: not available, 1: available + */ +static int have_vcpu_info_placement = 1; + +static void __init xen_vcpu_setup(int cpu) +{ + struct vcpu_register_vcpu_info info; + int err; + struct vcpu_info *vcpup; + + per_cpu(xen_vcpu, cpu) = &HYPERVISOR_shared_info->vcpu_info[cpu]; + + if (!have_vcpu_info_placement) + return; /* already tested, not available */ + + vcpup = &per_cpu(xen_vcpu_info, cpu); + + info.mfn = virt_to_mfn(vcpup); + info.offset = offset_in_page(vcpup); + + printk(KERN_DEBUG "trying to map vcpu_info %d at %p, mfn %x, offset %d\n", + cpu, vcpup, info.mfn, info.offset); + + /* Check to see if the hypervisor will put the vcpu_info + structure where we want it, which allows direct access via + a percpu-variable. */ + err = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_info, cpu, &info); + + if (err) { + printk(KERN_DEBUG "register_vcpu_info failed: err=%d\n", err); + have_vcpu_info_placement = 0; + } else { + /* This cpu is using the registered vcpu info, even if + later ones fail to. */ + per_cpu(xen_vcpu, cpu) = vcpup; + + printk(KERN_DEBUG "cpu %d using vcpu_info at %p\n", + cpu, vcpup); + } +} + +static void __init xen_banner(void) +{ + printk(KERN_INFO "Booting paravirtualized kernel on %s\n", + paravirt_ops.name); + printk(KERN_INFO "Hypervisor signature: %s\n", xen_start_info->magic); +} + +static void xen_cpuid(unsigned int *eax, unsigned int *ebx, + unsigned int *ecx, unsigned int *edx) +{ + unsigned maskedx = ~0; + + /* + * Mask out inconvenient features, to try and disable as many + * unsupported kernel subsystems as possible. + */ + if (*eax == 1) + maskedx = ~((1 << X86_FEATURE_APIC) | /* disable APIC */ + (1 << X86_FEATURE_ACPI) | /* disable ACPI */ + (1 << X86_FEATURE_ACC)); /* thermal monitoring */ + + asm(XEN_EMULATE_PREFIX "cpuid" + : "=a" (*eax), + "=b" (*ebx), + "=c" (*ecx), + "=d" (*edx) + : "0" (*eax), "2" (*ecx)); + *edx &= maskedx; +} + +static void xen_set_debugreg(int reg, unsigned long val) +{ + HYPERVISOR_set_debugreg(reg, val); +} + +static unsigned long xen_get_debugreg(int reg) +{ + return HYPERVISOR_get_debugreg(reg); +} + +static unsigned long xen_save_fl(void) +{ + struct vcpu_info *vcpu; + unsigned long flags; + + vcpu = x86_read_percpu(xen_vcpu); + + /* flag has opposite sense of mask */ + flags = !vcpu->evtchn_upcall_mask; + + /* convert to IF type flag + -0 -> 0x00000000 + -1 -> 0xffffffff + */ + return (-flags) & X86_EFLAGS_IF; +} + +static void xen_restore_fl(unsigned long flags) +{ + struct vcpu_info *vcpu; + + /* convert from IF type flag */ + flags = !(flags & X86_EFLAGS_IF); + + /* There's a one instruction preempt window here. We need to + make sure we're don't switch CPUs between getting the vcpu + pointer and updating the mask. */ + preempt_disable(); + vcpu = x86_read_percpu(xen_vcpu); + vcpu->evtchn_upcall_mask = flags; + preempt_enable_no_resched(); + + /* Doesn't matter if we get preempted here, because any + pending event will get dealt with anyway. */ + + if (flags == 0) { + preempt_check_resched(); + barrier(); /* unmask then check (avoid races) */ + if (unlikely(vcpu->evtchn_upcall_pending)) + force_evtchn_callback(); + } +} + +static void xen_irq_disable(void) +{ + /* There's a one instruction preempt window here. We need to + make sure we're don't switch CPUs between getting the vcpu + pointer and updating the mask. */ + preempt_disable(); + x86_read_percpu(xen_vcpu)->evtchn_upcall_mask = 1; + preempt_enable_no_resched(); +} + +static void xen_irq_enable(void) +{ + struct vcpu_info *vcpu; + + /* There's a one instruction preempt window here. We need to + make sure we're don't switch CPUs between getting the vcpu + pointer and updating the mask. */ + preempt_disable(); + vcpu = x86_read_percpu(xen_vcpu); + vcpu->evtchn_upcall_mask = 0; + preempt_enable_no_resched(); + + /* Doesn't matter if we get preempted here, because any + pending event will get dealt with anyway. */ + + barrier(); /* unmask then check (avoid races) */ + if (unlikely(vcpu->evtchn_upcall_pending)) + force_evtchn_callback(); +} + +static void xen_safe_halt(void) +{ + /* Blocking includes an implicit local_irq_enable(). */ + if (HYPERVISOR_sched_op(SCHEDOP_block, 0) != 0) + BUG(); +} + +static void xen_halt(void) +{ + if (irqs_disabled()) + HYPERVISOR_vcpu_op(VCPUOP_down, smp_processor_id(), NULL); + else + xen_safe_halt(); +} + +static void xen_set_lazy_mode(enum paravirt_lazy_mode mode) +{ + BUG_ON(preemptible()); + + switch (mode) { + case PARAVIRT_LAZY_NONE: + BUG_ON(x86_read_percpu(xen_lazy_mode) == PARAVIRT_LAZY_NONE); + break; + + case PARAVIRT_LAZY_MMU: + case PARAVIRT_LAZY_CPU: + BUG_ON(x86_read_percpu(xen_lazy_mode) != PARAVIRT_LAZY_NONE); + break; + + case PARAVIRT_LAZY_FLUSH: + /* flush if necessary, but don't change state */ + if (x86_read_percpu(xen_lazy_mode) != PARAVIRT_LAZY_NONE) + xen_mc_flush(); + return; + } + + xen_mc_flush(); + x86_write_percpu(xen_lazy_mode, mode); +} + +static unsigned long xen_store_tr(void) +{ + return 0; +} + +static void xen_set_ldt(const void *addr, unsigned entries) +{ + unsigned long linear_addr = (unsigned long)addr; + struct mmuext_op *op; + struct multicall_space mcs = xen_mc_entry(sizeof(*op)); + + op = mcs.args; + op->cmd = MMUEXT_SET_LDT; + if (linear_addr) { + /* ldt my be vmalloced, use arbitrary_virt_to_machine */ + xmaddr_t maddr; + maddr = arbitrary_virt_to_machine((unsigned long)addr); + linear_addr = (unsigned long)maddr.maddr; + } + op->arg1.linear_addr = linear_addr; + op->arg2.nr_ents = entries; + + MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); + + xen_mc_issue(PARAVIRT_LAZY_CPU); +} + +static void xen_load_gdt(const struct Xgt_desc_struct *dtr) +{ + unsigned long *frames; + unsigned long va = dtr->address; + unsigned int size = dtr->size + 1; + unsigned pages = (size + PAGE_SIZE - 1) / PAGE_SIZE; + int f; + struct multicall_space mcs; + + /* A GDT can be up to 64k in size, which corresponds to 8192 + 8-byte entries, or 16 4k pages.. */ + + BUG_ON(size > 65536); + BUG_ON(va & ~PAGE_MASK); + + mcs = xen_mc_entry(sizeof(*frames) * pages); + frames = mcs.args; + + for (f = 0; va < dtr->address + size; va += PAGE_SIZE, f++) { + frames[f] = virt_to_mfn(va); + make_lowmem_page_readonly((void *)va); + } + + MULTI_set_gdt(mcs.mc, frames, size / sizeof(struct desc_struct)); + + xen_mc_issue(PARAVIRT_LAZY_CPU); +} + +static void load_TLS_descriptor(struct thread_struct *t, + unsigned int cpu, unsigned int i) +{ + struct desc_struct *gdt = get_cpu_gdt_table(cpu); + xmaddr_t maddr = virt_to_machine(&gdt[GDT_ENTRY_TLS_MIN+i]); + struct multicall_space mc = __xen_mc_entry(0); + + MULTI_update_descriptor(mc.mc, maddr.maddr, t->tls_array[i]); +} + +static void xen_load_tls(struct thread_struct *t, unsigned int cpu) +{ + xen_mc_batch(); + + load_TLS_descriptor(t, cpu, 0); + load_TLS_descriptor(t, cpu, 1); + load_TLS_descriptor(t, cpu, 2); + + xen_mc_issue(PARAVIRT_LAZY_CPU); + + /* + * XXX sleazy hack: If we're being called in a lazy-cpu zone, + * it means we're in a context switch, and %gs has just been + * saved. This means we can zero it out to prevent faults on + * exit from the hypervisor if the next process has no %gs. + * Either way, it has been saved, and the new value will get + * loaded properly. This will go away as soon as Xen has been + * modified to not save/restore %gs for normal hypercalls. + */ + if (xen_get_lazy_mode() == PARAVIRT_LAZY_CPU) + loadsegment(gs, 0); +} + +static void xen_write_ldt_entry(struct desc_struct *dt, int entrynum, + u32 low, u32 high) +{ + unsigned long lp = (unsigned long)&dt[entrynum]; + xmaddr_t mach_lp = virt_to_machine(lp); + u64 entry = (u64)high << 32 | low; + + preempt_disable(); + + xen_mc_flush(); + if (HYPERVISOR_update_descriptor(mach_lp.maddr, entry)) + BUG(); + + preempt_enable(); +} + +static int cvt_gate_to_trap(int vector, u32 low, u32 high, + struct trap_info *info) +{ + u8 type, dpl; + + type = (high >> 8) & 0x1f; + dpl = (high >> 13) & 3; + + if (type != 0xf && type != 0xe) + return 0; + + info->vector = vector; + info->address = (high & 0xffff0000) | (low & 0x0000ffff); + info->cs = low >> 16; + info->flags = dpl; + /* interrupt gates clear IF */ + if (type == 0xe) + info->flags |= 4; + + return 1; +} + +/* Locations of each CPU's IDT */ +static DEFINE_PER_CPU(struct Xgt_desc_struct, idt_desc); + +/* Set an IDT entry. If the entry is part of the current IDT, then + also update Xen. */ +static void xen_write_idt_entry(struct desc_struct *dt, int entrynum, + u32 low, u32 high) +{ + unsigned long p = (unsigned long)&dt[entrynum]; + unsigned long start, end; + + preempt_disable(); + + start = __get_cpu_var(idt_desc).address; + end = start + __get_cpu_var(idt_desc).size + 1; + + xen_mc_flush(); + + write_dt_entry(dt, entrynum, low, high); + + if (p >= start && (p + 8) <= end) { + struct trap_info info[2]; + + info[1].address = 0; + + if (cvt_gate_to_trap(entrynum, low, high, &info[0])) + if (HYPERVISOR_set_trap_table(info)) + BUG(); + } + + preempt_enable(); +} + +static void xen_convert_trap_info(const struct Xgt_desc_struct *desc, + struct trap_info *traps) +{ + unsigned in, out, count; + + count = (desc->size+1) / 8; + BUG_ON(count > 256); + + for (in = out = 0; in < count; in++) { + const u32 *entry = (u32 *)(desc->address + in * 8); + + if (cvt_gate_to_trap(in, entry[0], entry[1], &traps[out])) + out++; + } + traps[out].address = 0; +} + +void xen_copy_trap_info(struct trap_info *traps) +{ + const struct Xgt_desc_struct *desc = &__get_cpu_var(idt_desc); + + xen_convert_trap_info(desc, traps); +} + +/* Load a new IDT into Xen. In principle this can be per-CPU, so we + hold a spinlock to protect the static traps[] array (static because + it avoids allocation, and saves stack space). */ +static void xen_load_idt(const struct Xgt_desc_struct *desc) +{ + static DEFINE_SPINLOCK(lock); + static struct trap_info traps[257]; + + spin_lock(&lock); + + __get_cpu_var(idt_desc) = *desc; + + xen_convert_trap_info(desc, traps); + + xen_mc_flush(); + if (HYPERVISOR_set_trap_table(traps)) + BUG(); + + spin_unlock(&lock); +} + +/* Write a GDT descriptor entry. Ignore LDT descriptors, since + they're handled differently. */ +static void xen_write_gdt_entry(struct desc_struct *dt, int entry, + u32 low, u32 high) +{ + preempt_disable(); + + switch ((high >> 8) & 0xff) { + case DESCTYPE_LDT: + case DESCTYPE_TSS: + /* ignore */ + break; + + default: { + xmaddr_t maddr = virt_to_machine(&dt[entry]); + u64 desc = (u64)high << 32 | low; + + xen_mc_flush(); + if (HYPERVISOR_update_descriptor(maddr.maddr, desc)) + BUG(); + } + + } + + preempt_enable(); +} + +static void xen_load_esp0(struct tss_struct *tss, + struct thread_struct *thread) +{ + struct multicall_space mcs = xen_mc_entry(0); + MULTI_stack_switch(mcs.mc, __KERNEL_DS, thread->esp0); + xen_mc_issue(PARAVIRT_LAZY_CPU); +} + +static void xen_set_iopl_mask(unsigned mask) +{ + struct physdev_set_iopl set_iopl; + + /* Force the change at ring 0. */ + set_iopl.iopl = (mask == 0) ? 1 : (mask >> 12) & 3; + HYPERVISOR_physdev_op(PHYSDEVOP_set_iopl, &set_iopl); +} + +static void xen_io_delay(void) +{ +} + +#ifdef CONFIG_X86_LOCAL_APIC +static unsigned long xen_apic_read(unsigned long reg) +{ + return 0; +} + +static void xen_apic_write(unsigned long reg, unsigned long val) +{ + /* Warn to see if there's any stray references */ + WARN_ON(1); +} +#endif + +static void xen_flush_tlb(void) +{ + struct mmuext_op *op; + struct multicall_space mcs = xen_mc_entry(sizeof(*op)); + + op = mcs.args; + op->cmd = MMUEXT_TLB_FLUSH_LOCAL; + MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); + + xen_mc_issue(PARAVIRT_LAZY_MMU); +} + +static void xen_flush_tlb_single(unsigned long addr) +{ + struct mmuext_op *op; + struct multicall_space mcs = xen_mc_entry(sizeof(*op)); + + op = mcs.args; + op->cmd = MMUEXT_INVLPG_LOCAL; + op->arg1.linear_addr = addr & PAGE_MASK; + MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); + + xen_mc_issue(PARAVIRT_LAZY_MMU); +} + +static void xen_flush_tlb_others(const cpumask_t *cpus, struct mm_struct *mm, + unsigned long va) +{ + struct { + struct mmuext_op op; + cpumask_t mask; + } *args; + cpumask_t cpumask = *cpus; + struct multicall_space mcs; + + /* + * A couple of (to be removed) sanity checks: + * + * - current CPU must not be in mask + * - mask must exist :) + */ + BUG_ON(cpus_empty(cpumask)); + BUG_ON(cpu_isset(smp_processor_id(), cpumask)); + BUG_ON(!mm); + + /* If a CPU which we ran on has gone down, OK. */ + cpus_and(cpumask, cpumask, cpu_online_map); + if (cpus_empty(cpumask)) + return; + + mcs = xen_mc_entry(sizeof(*args)); + args = mcs.args; + args->mask = cpumask; + args->op.arg2.vcpumask = &args->mask; + + if (va == TLB_FLUSH_ALL) { + args->op.cmd = MMUEXT_TLB_FLUSH_MULTI; + } else { + args->op.cmd = MMUEXT_INVLPG_MULTI; + args->op.arg1.linear_addr = va; + } + + MULTI_mmuext_op(mcs.mc, &args->op, 1, NULL, DOMID_SELF); + + xen_mc_issue(PARAVIRT_LAZY_MMU); +} + +static void xen_write_cr2(unsigned long cr2) +{ + x86_read_percpu(xen_vcpu)->arch.cr2 = cr2; +} + +static unsigned long xen_read_cr2(void) +{ + return x86_read_percpu(xen_vcpu)->arch.cr2; +} + +static unsigned long xen_read_cr2_direct(void) +{ + return x86_read_percpu(xen_vcpu_info.arch.cr2); +} + +static void xen_write_cr4(unsigned long cr4) +{ + /* never allow TSC to be disabled */ + native_write_cr4(cr4 & ~X86_CR4_TSD); +} + +static unsigned long xen_read_cr3(void) +{ + return x86_read_percpu(xen_cr3); +} + +static void xen_write_cr3(unsigned long cr3) +{ + BUG_ON(preemptible()); + + if (cr3 == x86_read_percpu(xen_cr3)) { + /* just a simple tlb flush */ + xen_flush_tlb(); + return; + } + + x86_write_percpu(xen_cr3, cr3); + + + { + struct mmuext_op *op; + struct multicall_space mcs = xen_mc_entry(sizeof(*op)); + unsigned long mfn = pfn_to_mfn(PFN_DOWN(cr3)); + + op = mcs.args; + op->cmd = MMUEXT_NEW_BASEPTR; + op->arg1.mfn = mfn; + + MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); + + xen_mc_issue(PARAVIRT_LAZY_CPU); + } +} + +/* Early in boot, while setting up the initial pagetable, assume + everything is pinned. */ +static __init void xen_alloc_pt_init(struct mm_struct *mm, u32 pfn) +{ + BUG_ON(mem_map); /* should only be used early */ + make_lowmem_page_readonly(__va(PFN_PHYS(pfn))); +} + +/* This needs to make sure the new pte page is pinned iff its being + attached to a pinned pagetable. */ +static void xen_alloc_pt(struct mm_struct *mm, u32 pfn) +{ + struct page *page = pfn_to_page(pfn); + + if (PagePinned(virt_to_page(mm->pgd))) { + SetPagePinned(page); + + if (!PageHighMem(page)) + make_lowmem_page_readonly(__va(PFN_PHYS(pfn))); + else + /* make sure there are no stray mappings of + this page */ + kmap_flush_unused(); + } +} + +/* This should never happen until we're OK to use struct page */ +static void xen_release_pt(u32 pfn) +{ + struct page *page = pfn_to_page(pfn); + + if (PagePinned(page)) { + if (!PageHighMem(page)) + make_lowmem_page_readwrite(__va(PFN_PHYS(pfn))); + } +} + +#ifdef CONFIG_HIGHPTE +static void *xen_kmap_atomic_pte(struct page *page, enum km_type type) +{ + pgprot_t prot = PAGE_KERNEL; + + if (PagePinned(page)) + prot = PAGE_KERNEL_RO; + + if (0 && PageHighMem(page)) + printk("mapping highpte %lx type %d prot %s\n", + page_to_pfn(page), type, + (unsigned long)pgprot_val(prot) & _PAGE_RW ? "WRITE" : "READ"); + + return kmap_atomic_prot(page, type, prot); +} +#endif + +static __init pte_t mask_rw_pte(pte_t *ptep, pte_t pte) +{ + /* If there's an existing pte, then don't allow _PAGE_RW to be set */ + if (pte_val_ma(*ptep) & _PAGE_PRESENT) + pte = __pte_ma(((pte_val_ma(*ptep) & _PAGE_RW) | ~_PAGE_RW) & + pte_val_ma(pte)); + + return pte; +} + +/* Init-time set_pte while constructing initial pagetables, which + doesn't allow RO pagetable pages to be remapped RW */ +static __init void xen_set_pte_init(pte_t *ptep, pte_t pte) +{ + pte = mask_rw_pte(ptep, pte); + + xen_set_pte(ptep, pte); +} + +static __init void xen_pagetable_setup_start(pgd_t *base) +{ + pgd_t *xen_pgd = (pgd_t *)xen_start_info->pt_base; + + /* special set_pte for pagetable initialization */ + paravirt_ops.set_pte = xen_set_pte_init; + + init_mm.pgd = base; + /* + * copy top-level of Xen-supplied pagetable into place. For + * !PAE we can use this as-is, but for PAE it is a stand-in + * while we copy the pmd pages. + */ + memcpy(base, xen_pgd, PTRS_PER_PGD * sizeof(pgd_t)); + + if (PTRS_PER_PMD > 1) { + int i; + /* + * For PAE, need to allocate new pmds, rather than + * share Xen's, since Xen doesn't like pmd's being + * shared between address spaces. + */ + for (i = 0; i < PTRS_PER_PGD; i++) { + if (pgd_val_ma(xen_pgd[i]) & _PAGE_PRESENT) { + pmd_t *pmd = (pmd_t *)alloc_bootmem_low_pages(PAGE_SIZE); + + memcpy(pmd, (void *)pgd_page_vaddr(xen_pgd[i]), + PAGE_SIZE); + + make_lowmem_page_readonly(pmd); + + set_pgd(&base[i], __pgd(1 + __pa(pmd))); + } else + pgd_clear(&base[i]); + } + } + + /* make sure zero_page is mapped RO so we can use it in pagetables */ + make_lowmem_page_readonly(empty_zero_page); + make_lowmem_page_readonly(base); + /* + * Switch to new pagetable. This is done before + * pagetable_init has done anything so that the new pages + * added to the table can be prepared properly for Xen. + */ + xen_write_cr3(__pa(base)); +} + +static __init void xen_pagetable_setup_done(pgd_t *base) +{ + /* This will work as long as patching hasn't happened yet + (which it hasn't) */ + paravirt_ops.alloc_pt = xen_alloc_pt; + paravirt_ops.set_pte = xen_set_pte; + + if (!xen_feature(XENFEAT_auto_translated_physmap)) { + /* + * Create a mapping for the shared info page. + * Should be set_fixmap(), but shared_info is a machine + * address with no corresponding pseudo-phys address. + */ + set_pte_mfn(fix_to_virt(FIX_PARAVIRT_BOOTMAP), + PFN_DOWN(xen_start_info->shared_info), + PAGE_KERNEL); + + HYPERVISOR_shared_info = + (struct shared_info *)fix_to_virt(FIX_PARAVIRT_BOOTMAP); + + } else + HYPERVISOR_shared_info = + (struct shared_info *)__va(xen_start_info->shared_info); + + /* Actually pin the pagetable down, but we can't set PG_pinned + yet because the page structures don't exist yet. */ + { + struct mmuext_op op; +#ifdef CONFIG_X86_PAE + op.cmd = MMUEXT_PIN_L3_TABLE; +#else + op.cmd = MMUEXT_PIN_L3_TABLE; +#endif + op.arg1.mfn = pfn_to_mfn(PFN_DOWN(__pa(base))); + if (HYPERVISOR_mmuext_op(&op, 1, NULL, DOMID_SELF)) + BUG(); + } +} + +/* This is called once we have the cpu_possible_map */ +void __init xen_setup_vcpu_info_placement(void) +{ + int cpu; + + for_each_possible_cpu(cpu) + xen_vcpu_setup(cpu); + + /* xen_vcpu_setup managed to place the vcpu_info within the + percpu area for all cpus, so make use of it */ + if (have_vcpu_info_placement) { + printk(KERN_INFO "Xen: using vcpu_info placement\n"); + + paravirt_ops.save_fl = xen_save_fl_direct; + paravirt_ops.restore_fl = xen_restore_fl_direct; + paravirt_ops.irq_disable = xen_irq_disable_direct; + paravirt_ops.irq_enable = xen_irq_enable_direct; + paravirt_ops.read_cr2 = xen_read_cr2_direct; + paravirt_ops.iret = xen_iret_direct; + } +} + +static unsigned xen_patch(u8 type, u16 clobbers, void *insns, unsigned len) +{ + char *start, *end, *reloc; + unsigned ret; + + start = end = reloc = NULL; + +#define SITE(x) \ + case PARAVIRT_PATCH(x): \ + if (have_vcpu_info_placement) { \ + start = (char *)xen_##x##_direct; \ + end = xen_##x##_direct_end; \ + reloc = xen_##x##_direct_reloc; \ + } \ + goto patch_site + + switch (type) { + SITE(irq_enable); + SITE(irq_disable); + SITE(save_fl); + SITE(restore_fl); +#undef SITE + + patch_site: + if (start == NULL || (end-start) > len) + goto default_patch; + + ret = paravirt_patch_insns(insns, len, start, end); + + /* Note: because reloc is assigned from something that + appears to be an array, gcc assumes it's non-null, + but doesn't know its relationship with start and + end. */ + if (reloc > start && reloc < end) { + int reloc_off = reloc - start; + long *relocp = (long *)(insns + reloc_off); + long delta = start - (char *)insns; + + *relocp += delta; + } + break; + + default_patch: + default: + ret = paravirt_patch_default(type, clobbers, insns, len); + break; + } + + return ret; +} + +static const struct paravirt_ops xen_paravirt_ops __initdata = { + .paravirt_enabled = 1, + .shared_kernel_pmd = 0, + + .name = "Xen", + .banner = xen_banner, + + .patch = xen_patch, + + .memory_setup = xen_memory_setup, + .arch_setup = xen_arch_setup, + .init_IRQ = xen_init_IRQ, + .post_allocator_init = xen_mark_init_mm_pinned, + + .time_init = xen_time_init, + .set_wallclock = xen_set_wallclock, + .get_wallclock = xen_get_wallclock, + .get_cpu_khz = xen_cpu_khz, + .sched_clock = xen_sched_clock, + + .cpuid = xen_cpuid, + + .set_debugreg = xen_set_debugreg, + .get_debugreg = xen_get_debugreg, + + .clts = native_clts, + + .read_cr0 = native_read_cr0, + .write_cr0 = native_write_cr0, + + .read_cr2 = xen_read_cr2, + .write_cr2 = xen_write_cr2, + + .read_cr3 = xen_read_cr3, + .write_cr3 = xen_write_cr3, + + .read_cr4 = native_read_cr4, + .read_cr4_safe = native_read_cr4_safe, + .write_cr4 = xen_write_cr4, + + .save_fl = xen_save_fl, + .restore_fl = xen_restore_fl, + .irq_disable = xen_irq_disable, + .irq_enable = xen_irq_enable, + .safe_halt = xen_safe_halt, + .halt = xen_halt, + .wbinvd = native_wbinvd, + + .read_msr = native_read_msr_safe, + .write_msr = native_write_msr_safe, + .read_tsc = native_read_tsc, + .read_pmc = native_read_pmc, + + .iret = (void *)&hypercall_page[__HYPERVISOR_iret], + .irq_enable_sysexit = NULL, /* never called */ + + .load_tr_desc = paravirt_nop, + .set_ldt = xen_set_ldt, + .load_gdt = xen_load_gdt, + .load_idt = xen_load_idt, + .load_tls = xen_load_tls, + + .store_gdt = native_store_gdt, + .store_idt = native_store_idt, + .store_tr = xen_store_tr, + + .write_ldt_entry = xen_write_ldt_entry, + .write_gdt_entry = xen_write_gdt_entry, + .write_idt_entry = xen_write_idt_entry, + .load_esp0 = xen_load_esp0, + + .set_iopl_mask = xen_set_iopl_mask, + .io_delay = xen_io_delay, + +#ifdef CONFIG_X86_LOCAL_APIC + .apic_write = xen_apic_write, + .apic_write_atomic = xen_apic_write, + .apic_read = xen_apic_read, + .setup_boot_clock = paravirt_nop, + .setup_secondary_clock = paravirt_nop, + .startup_ipi_hook = paravirt_nop, +#endif + + .flush_tlb_user = xen_flush_tlb, + .flush_tlb_kernel = xen_flush_tlb, + .flush_tlb_single = xen_flush_tlb_single, + .flush_tlb_others = xen_flush_tlb_others, + + .pte_update = paravirt_nop, + .pte_update_defer = paravirt_nop, + + .pagetable_setup_start = xen_pagetable_setup_start, + .pagetable_setup_done = xen_pagetable_setup_done, + + .alloc_pt = xen_alloc_pt_init, + .release_pt = xen_release_pt, + .alloc_pd = paravirt_nop, + .alloc_pd_clone = paravirt_nop, + .release_pd = paravirt_nop, + +#ifdef CONFIG_HIGHPTE + .kmap_atomic_pte = xen_kmap_atomic_pte, +#endif + + .set_pte = NULL, /* see xen_pagetable_setup_* */ + .set_pte_at = xen_set_pte_at, + .set_pmd = xen_set_pmd, + + .pte_val = xen_pte_val, + .pgd_val = xen_pgd_val, + + .make_pte = xen_make_pte, + .make_pgd = xen_make_pgd, + +#ifdef CONFIG_X86_PAE + .set_pte_atomic = xen_set_pte_atomic, + .set_pte_present = xen_set_pte_at, + .set_pud = xen_set_pud, + .pte_clear = xen_pte_clear, + .pmd_clear = xen_pmd_clear, + + .make_pmd = xen_make_pmd, + .pmd_val = xen_pmd_val, +#endif /* PAE */ + + .activate_mm = xen_activate_mm, + .dup_mmap = xen_dup_mmap, + .exit_mmap = xen_exit_mmap, + + .set_lazy_mode = xen_set_lazy_mode, +}; + +#ifdef CONFIG_SMP +static const struct smp_ops xen_smp_ops __initdata = { + .smp_prepare_boot_cpu = xen_smp_prepare_boot_cpu, + .smp_prepare_cpus = xen_smp_prepare_cpus, + .cpu_up = xen_cpu_up, + .smp_cpus_done = xen_smp_cpus_done, + + .smp_send_stop = xen_smp_send_stop, + .smp_send_reschedule = xen_smp_send_reschedule, + .smp_call_function_mask = xen_smp_call_function_mask, +}; +#endif /* CONFIG_SMP */ + +static void xen_reboot(int reason) +{ +#ifdef CONFIG_SMP + smp_send_stop(); +#endif + + if (HYPERVISOR_sched_op(SCHEDOP_shutdown, reason)) + BUG(); +} + +static void xen_restart(char *msg) +{ + xen_reboot(SHUTDOWN_reboot); +} + +static void xen_emergency_restart(void) +{ + xen_reboot(SHUTDOWN_reboot); +} + +static void xen_machine_halt(void) +{ + xen_reboot(SHUTDOWN_poweroff); +} + +static void xen_crash_shutdown(struct pt_regs *regs) +{ + xen_reboot(SHUTDOWN_crash); +} + +static const struct machine_ops __initdata xen_machine_ops = { + .restart = xen_restart, + .halt = xen_machine_halt, + .power_off = xen_machine_halt, + .shutdown = xen_machine_halt, + .crash_shutdown = xen_crash_shutdown, + .emergency_restart = xen_emergency_restart, +}; + + +/* First C function to be called on Xen boot */ +asmlinkage void __init xen_start_kernel(void) +{ + pgd_t *pgd; + + if (!xen_start_info) + return; + + BUG_ON(memcmp(xen_start_info->magic, "xen-3.0", 7) != 0); + + /* Install Xen paravirt ops */ + paravirt_ops = xen_paravirt_ops; + machine_ops = xen_machine_ops; + +#ifdef CONFIG_SMP + smp_ops = xen_smp_ops; +#endif + + xen_setup_features(); + + /* Get mfn list */ + if (!xen_feature(XENFEAT_auto_translated_physmap)) + phys_to_machine_mapping = (unsigned long *)xen_start_info->mfn_list; + + pgd = (pgd_t *)xen_start_info->pt_base; + + init_pg_tables_end = __pa(pgd) + xen_start_info->nr_pt_frames*PAGE_SIZE; + + init_mm.pgd = pgd; /* use the Xen pagetables to start */ + + /* keep using Xen gdt for now; no urgent need to change it */ + + x86_write_percpu(xen_cr3, __pa(pgd)); + +#ifdef CONFIG_SMP + /* Don't do the full vcpu_info placement stuff until we have a + possible map. */ + per_cpu(xen_vcpu, 0) = &HYPERVISOR_shared_info->vcpu_info[0]; +#else + /* May as well do it now, since there's no good time to call + it later on UP. */ + xen_setup_vcpu_info_placement(); +#endif + + paravirt_ops.kernel_rpl = 1; + if (xen_feature(XENFEAT_supervisor_mode_kernel)) + paravirt_ops.kernel_rpl = 0; + + /* set the limit of our address space */ + reserve_top_address(-HYPERVISOR_VIRT_START + 2 * PAGE_SIZE); + + /* set up basic CPUID stuff */ + cpu_detect(&new_cpu_data); + new_cpu_data.hard_math = 1; + new_cpu_data.x86_capability[0] = cpuid_edx(1); + + /* Poke various useful things into boot_params */ + LOADER_TYPE = (9 << 4) | 0; + INITRD_START = xen_start_info->mod_start ? __pa(xen_start_info->mod_start) : 0; + INITRD_SIZE = xen_start_info->mod_len; + + /* Start the world */ + start_kernel(); +} diff --git a/arch/i386/xen/events.c b/arch/i386/xen/events.c new file mode 100644 index 0000000..8904acc --- /dev/null +++ b/arch/i386/xen/events.c @@ -0,0 +1,590 @@ +/* + * Xen event channels + * + * Xen models interrupts with abstract event channels. Because each + * domain gets 1024 event channels, but NR_IRQ is not that large, we + * must dynamically map irqs<->event channels. The event channels + * interface with the rest of the kernel by defining a xen interrupt + * chip. When an event is recieved, it is mapped to an irq and sent + * through the normal interrupt processing path. + * + * There are four kinds of events which can be mapped to an event + * channel: + * + * 1. Inter-domain notifications. This includes all the virtual + * device events, since they're driven by front-ends in another domain + * (typically dom0). + * 2. VIRQs, typically used for timers. These are per-cpu events. + * 3. IPIs. + * 4. Hardware interrupts. Not supported at present. + * + * Jeremy Fitzhardinge <jeremy@xensource.com>, XenSource Inc, 2007 + */ + +#include <linux/linkage.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/string.h> + +#include <asm/ptrace.h> +#include <asm/irq.h> +#include <asm/sync_bitops.h> +#include <asm/xen/hypercall.h> + +#include <xen/events.h> +#include <xen/interface/xen.h> +#include <xen/interface/event_channel.h> + +#include "xen-ops.h" + +/* + * This lock protects updates to the following mapping and reference-count + * arrays. The lock does not need to be acquired to read the mapping tables. + */ +static DEFINE_SPINLOCK(irq_mapping_update_lock); + +/* IRQ <-> VIRQ mapping. */ +static DEFINE_PER_CPU(int, virq_to_irq[NR_VIRQS]) = {[0 ... NR_VIRQS-1] = -1}; + +/* IRQ <-> IPI mapping */ +static DEFINE_PER_CPU(int, ipi_to_irq[XEN_NR_IPIS]) = {[0 ... XEN_NR_IPIS-1] = -1}; + +/* Packed IRQ information: binding type, sub-type index, and event channel. */ +struct packed_irq +{ + unsigned short evtchn; + unsigned char index; + unsigned char type; +}; + +static struct packed_irq irq_info[NR_IRQS]; + +/* Binding types. */ +enum { + IRQT_UNBOUND, + IRQT_PIRQ, + IRQT_VIRQ, + IRQT_IPI, + IRQT_EVTCHN +}; + +/* Convenient shorthand for packed representation of an unbound IRQ. */ +#define IRQ_UNBOUND mk_irq_info(IRQT_UNBOUND, 0, 0) + +static int evtchn_to_irq[NR_EVENT_CHANNELS] = { + [0 ... NR_EVENT_CHANNELS-1] = -1 +}; +static unsigned long cpu_evtchn_mask[NR_CPUS][NR_EVENT_CHANNELS/BITS_PER_LONG]; +static u8 cpu_evtchn[NR_EVENT_CHANNELS]; + +/* Reference counts for bindings to IRQs. */ +static int irq_bindcount[NR_IRQS]; + +/* Xen will never allocate port zero for any purpose. */ +#define VALID_EVTCHN(chn) ((chn) != 0) + +/* + * Force a proper event-channel callback from Xen after clearing the + * callback mask. We do this in a very simple manner, by making a call + * down into Xen. The pending flag will be checked by Xen on return. + */ +void force_evtchn_callback(void) +{ + (void)HYPERVISOR_xen_version(0, NULL); +} +EXPORT_SYMBOL_GPL(force_evtchn_callback); + +static struct irq_chip xen_dynamic_chip; + +/* Constructor for packed IRQ information. */ +static inline struct packed_irq mk_irq_info(u32 type, u32 index, u32 evtchn) +{ + return (struct packed_irq) { evtchn, index, type }; +} + +/* + * Accessors for packed IRQ information. + */ +static inline unsigned int evtchn_from_irq(int irq) +{ + return irq_info[irq].evtchn; +} + +static inline unsigned int index_from_irq(int irq) +{ + return irq_info[irq].index; +} + +static inline unsigned int type_from_irq(int irq) +{ + return irq_info[irq].type; +} + +static inline unsigned long active_evtchns(unsigned int cpu, + struct shared_info *sh, + unsigned int idx) +{ + return (sh->evtchn_pending[idx] & + cpu_evtchn_mask[cpu][idx] & + ~sh->evtchn_mask[idx]); +} + +static void bind_evtchn_to_cpu(unsigned int chn, unsigned int cpu) +{ + int irq = evtchn_to_irq[chn]; + + BUG_ON(irq == -1); +#ifdef CONFIG_SMP + irq_desc[irq].affinity = cpumask_of_cpu(cpu); +#endif + + __clear_bit(chn, cpu_evtchn_mask[cpu_evtchn[chn]]); + __set_bit(chn, cpu_evtchn_mask[cpu]); + + cpu_evtchn[chn] = cpu; +} + +static void init_evtchn_cpu_bindings(void) +{ +#ifdef CONFIG_SMP + int i; + /* By default all event channels notify CPU#0. */ + for (i = 0; i < NR_IRQS; i++) + irq_desc[i].affinity = cpumask_of_cpu(0); +#endif + + memset(cpu_evtchn, 0, sizeof(cpu_evtchn)); + memset(cpu_evtchn_mask[0], ~0, sizeof(cpu_evtchn_mask[0])); +} + +static inline unsigned int cpu_from_evtchn(unsigned int evtchn) +{ + return cpu_evtchn[evtchn]; +} + +static inline void clear_evtchn(int port) +{ + struct shared_info *s = HYPERVISOR_shared_info; + sync_clear_bit(port, &s->evtchn_pending[0]); +} + +static inline void set_evtchn(int port) +{ + struct shared_info *s = HYPERVISOR_shared_info; + sync_set_bit(port, &s->evtchn_pending[0]); +} + + +/** + * notify_remote_via_irq - send event to remote end of event channel via irq + * @irq: irq of event channel to send event to + * + * Unlike notify_remote_via_evtchn(), this is safe to use across + * save/restore. Notifications on a broken connection are silently + * dropped. + */ +void notify_remote_via_irq(int irq) +{ + int evtchn = evtchn_from_irq(irq); + + if (VALID_EVTCHN(evtchn)) + notify_remote_via_evtchn(evtchn); +} +EXPORT_SYMBOL_GPL(notify_remote_via_irq); + +static void mask_evtchn(int port) +{ + struct shared_info *s = HYPERVISOR_shared_info; + sync_set_bit(port, &s->evtchn_mask[0]); +} + +static void unmask_evtchn(int port) +{ + struct shared_info *s = HYPERVISOR_shared_info; + unsigned int cpu = get_cpu(); + + BUG_ON(!irqs_disabled()); + + /* Slow path (hypercall) if this is a non-local port. */ + if (unlikely(cpu != cpu_from_evtchn(port))) { + struct evtchn_unmask unmask = { .port = port }; + (void)HYPERVISOR_event_channel_op(EVTCHNOP_unmask, &unmask); + } else { + struct vcpu_info *vcpu_info = __get_cpu_var(xen_vcpu); + + sync_clear_bit(port, &s->evtchn_mask[0]); + + /* + * The following is basically the equivalent of + * 'hw_resend_irq'. Just like a real IO-APIC we 'lose + * the interrupt edge' if the channel is masked. + */ + if (sync_test_bit(port, &s->evtchn_pending[0]) && + !sync_test_and_set_bit(port / BITS_PER_LONG, + &vcpu_info->evtchn_pending_sel)) + vcpu_info->evtchn_upcall_pending = 1; + } + + put_cpu(); +} + +static int find_unbound_irq(void) +{ + int irq; + + /* Only allocate from dynirq range */ + for (irq = 0; irq < NR_IRQS; irq++) + if (irq_bindcount[irq] == 0) + break; + + if (irq == NR_IRQS) + panic("No available IRQ to bind to: increase NR_IRQS!\n"); + + return irq; +} + +int bind_evtchn_to_irq(unsigned int evtchn) +{ + int irq; + + spin_lock(&irq_mapping_update_lock); + + irq = evtchn_to_irq[evtchn]; + + if (irq == -1) { + irq = find_unbound_irq(); + + dynamic_irq_init(irq); + set_irq_chip_and_handler_name(irq, &xen_dynamic_chip, + handle_level_irq, "event"); + + evtchn_to_irq[evtchn] = irq; + irq_info[irq] = mk_irq_info(IRQT_EVTCHN, 0, evtchn); + } + + irq_bindcount[irq]++; + + spin_unlock(&irq_mapping_update_lock); + + return irq; +} +EXPORT_SYMBOL_GPL(bind_evtchn_to_irq); + +static int bind_ipi_to_irq(unsigned int ipi, unsigned int cpu) +{ + struct evtchn_bind_ipi bind_ipi; + int evtchn, irq; + + spin_lock(&irq_mapping_update_lock); + + irq = per_cpu(ipi_to_irq, cpu)[ipi]; + if (irq == -1) { + irq = find_unbound_irq(); + if (irq < 0) + goto out; + + dynamic_irq_init(irq); + set_irq_chip_and_handler_name(irq, &xen_dynamic_chip, + handle_level_irq, "ipi"); + + bind_ipi.vcpu = cpu; + if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_ipi, + &bind_ipi) != 0) + BUG(); + evtchn = bind_ipi.port; + + evtchn_to_irq[evtchn] = irq; + irq_info[irq] = mk_irq_info(IRQT_IPI, ipi, evtchn); + + per_cpu(ipi_to_irq, cpu)[ipi] = irq; + + bind_evtchn_to_cpu(evtchn, cpu); + } + + irq_bindcount[irq]++; + + out: + spin_unlock(&irq_mapping_update_lock); + return irq; +} + + +static int bind_virq_to_irq(unsigned int virq, unsigned int cpu) +{ + struct evtchn_bind_virq bind_virq; + int evtchn, irq; + + spin_lock(&irq_mapping_update_lock); + + irq = per_cpu(virq_to_irq, cpu)[virq]; + + if (irq == -1) { + bind_virq.virq = virq; + bind_virq.vcpu = cpu; + if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, + &bind_virq) != 0) + BUG(); + evtchn = bind_virq.port; + + irq = find_unbound_irq(); + + dynamic_irq_init(irq); + set_irq_chip_and_handler_name(irq, &xen_dynamic_chip, + handle_level_irq, "virq"); + + evtchn_to_irq[evtchn] = irq; + irq_info[irq] = mk_irq_info(IRQT_VIRQ, virq, evtchn); + + per_cpu(virq_to_irq, cpu)[virq] = irq; + + bind_evtchn_to_cpu(evtchn, cpu); + } + + irq_bindcount[irq]++; + + spin_unlock(&irq_mapping_update_lock); + + return irq; +} + +static void unbind_from_irq(unsigned int irq) +{ + struct evtchn_close close; + int evtchn = evtchn_from_irq(irq); + + spin_lock(&irq_mapping_update_lock); + + if (VALID_EVTCHN(evtchn) && (--irq_bindcount[irq] == 0)) { + close.port = evtchn; + if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close) != 0) + BUG(); + + switch (type_from_irq(irq)) { + case IRQT_VIRQ: + per_cpu(virq_to_irq, cpu_from_evtchn(evtchn)) + [index_from_irq(irq)] = -1; + break; + default: + break; + } + + /* Closed ports are implicitly re-bound to VCPU0. */ + bind_evtchn_to_cpu(evtchn, 0); + + evtchn_to_irq[evtchn] = -1; + irq_info[irq] = IRQ_UNBOUND; + + dynamic_irq_init(irq); + } + + spin_unlock(&irq_mapping_update_lock); +} + +int bind_evtchn_to_irqhandler(unsigned int evtchn, + irqreturn_t (*handler)(int, void *), + unsigned long irqflags, + const char *devname, void *dev_id) +{ + unsigned int irq; + int retval; + + irq = bind_evtchn_to_irq(evtchn); + retval = request_irq(irq, handler, irqflags, devname, dev_id); + if (retval != 0) { + unbind_from_irq(irq); + return retval; + } + + return irq; +} +EXPORT_SYMBOL_GPL(bind_evtchn_to_irqhandler); + +int bind_virq_to_irqhandler(unsigned int virq, unsigned int cpu, + irqreturn_t (*handler)(int, void *), + unsigned long irqflags, const char *devname, void *dev_id) +{ + unsigned int irq; + int retval; + + irq = bind_virq_to_irq(virq, cpu); + retval = request_irq(irq, handler, irqflags, devname, dev_id); + if (retval != 0) { + unbind_from_irq(irq); + return retval; + } + + return irq; +} +EXPORT_SYMBOL_GPL(bind_virq_to_irqhandler); + +int bind_ipi_to_irqhandler(enum ipi_vector ipi, + unsigned int cpu, + irq_handler_t handler, + unsigned long irqflags, + const char *devname, + void *dev_id) +{ + int irq, retval; + + irq = bind_ipi_to_irq(ipi, cpu); + if (irq < 0) + return irq; + + retval = request_irq(irq, handler, irqflags, devname, dev_id); + if (retval != 0) { + unbind_from_irq(irq); + return retval; + } + + return irq; +} + +void unbind_from_irqhandler(unsigned int irq, void *dev_id) +{ + free_irq(irq, dev_id); + unbind_from_irq(irq); +} +EXPORT_SYMBOL_GPL(unbind_from_irqhandler); + +void xen_send_IPI_one(unsigned int cpu, enum ipi_vector vector) +{ + int irq = per_cpu(ipi_to_irq, cpu)[vector]; + BUG_ON(irq < 0); + notify_remote_via_irq(irq); +} + + +/* + * Search the CPUs pending events bitmasks. For each one found, map + * the event number to an irq, and feed it into do_IRQ() for + * handling. + * + * Xen uses a two-level bitmap to speed searching. The first level is + * a bitset of words which contain pending event bits. The second + * level is a bitset of pending events themselves. + */ +fastcall void xen_evtchn_do_upcall(struct pt_regs *regs) +{ + int cpu = get_cpu(); + struct shared_info *s = HYPERVISOR_shared_info; + struct vcpu_info *vcpu_info = __get_cpu_var(xen_vcpu); + unsigned long pending_words; + + vcpu_info->evtchn_upcall_pending = 0; + + /* NB. No need for a barrier here -- XCHG is a barrier on x86. */ + pending_words = xchg(&vcpu_info->evtchn_pending_sel, 0); + while (pending_words != 0) { + unsigned long pending_bits; + int word_idx = __ffs(pending_words); + pending_words &= ~(1UL << word_idx); + + while ((pending_bits = active_evtchns(cpu, s, word_idx)) != 0) { + int bit_idx = __ffs(pending_bits); + int port = (word_idx * BITS_PER_LONG) + bit_idx; + int irq = evtchn_to_irq[port]; + + if (irq != -1) { + regs->orig_eax = ~irq; + do_IRQ(regs); + } + } + } + + put_cpu(); +} + +/* Rebind an evtchn so that it gets delivered to a specific cpu */ +static void rebind_irq_to_cpu(unsigned irq, unsigned tcpu) +{ + struct evtchn_bind_vcpu bind_vcpu; + int evtchn = evtchn_from_irq(irq); + + if (!VALID_EVTCHN(evtchn)) + return; + + /* Send future instances of this interrupt to other vcpu. */ + bind_vcpu.port = evtchn; + bind_vcpu.vcpu = tcpu; + + /* + * If this fails, it usually just indicates that we're dealing with a + * virq or IPI channel, which don't actually need to be rebound. Ignore + * it, but don't do the xenlinux-level rebind in that case. + */ + if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_vcpu, &bind_vcpu) >= 0) + bind_evtchn_to_cpu(evtchn, tcpu); +} + + +static void set_affinity_irq(unsigned irq, cpumask_t dest) +{ + unsigned tcpu = first_cpu(dest); + rebind_irq_to_cpu(irq, tcpu); +} + +static void enable_dynirq(unsigned int irq) +{ + int evtchn = evtchn_from_irq(irq); + + if (VALID_EVTCHN(evtchn)) + unmask_evtchn(evtchn); +} + +static void disable_dynirq(unsigned int irq) +{ + int evtchn = evtchn_from_irq(irq); + + if (VALID_EVTCHN(evtchn)) + mask_evtchn(evtchn); +} + +static void ack_dynirq(unsigned int irq) +{ + int evtchn = evtchn_from_irq(irq); + + move_native_irq(irq); + + if (VALID_EVTCHN(evtchn)) + clear_evtchn(evtchn); +} + +static int retrigger_dynirq(unsigned int irq) +{ + int evtchn = evtchn_from_irq(irq); + int ret = 0; + + if (VALID_EVTCHN(evtchn)) { + set_evtchn(evtchn); + ret = 1; + } + + return ret; +} + +static struct irq_chip xen_dynamic_chip __read_mostly = { + .name = "xen-dyn", + .mask = disable_dynirq, + .unmask = enable_dynirq, + .ack = ack_dynirq, + .set_affinity = set_affinity_irq, + .retrigger = retrigger_dynirq, +}; + +void __init xen_init_IRQ(void) +{ + int i; + + init_evtchn_cpu_bindings(); + + /* No event channels are 'live' right now. */ + for (i = 0; i < NR_EVENT_CHANNELS; i++) + mask_evtchn(i); + + /* Dynamic IRQ space is currently unbound. Zero the refcnts. */ + for (i = 0; i < NR_IRQS; i++) + irq_bindcount[i] = 0; + + irq_ctx_init(smp_processor_id()); +} diff --git a/arch/i386/xen/features.c b/arch/i386/xen/features.c new file mode 100644 index 0000000..0707714 --- /dev/null +++ b/arch/i386/xen/features.c @@ -0,0 +1,29 @@ +/****************************************************************************** + * features.c + * + * Xen feature flags. + * + * Copyright (c) 2006, Ian Campbell, XenSource Inc. + */ +#include <linux/types.h> +#include <linux/cache.h> +#include <linux/module.h> +#include <asm/xen/hypervisor.h> +#include <xen/features.h> + +u8 xen_features[XENFEAT_NR_SUBMAPS * 32] __read_mostly; +EXPORT_SYMBOL_GPL(xen_features); + +void xen_setup_features(void) +{ + struct xen_feature_info fi; + int i, j; + + for (i = 0; i < XENFEAT_NR_SUBMAPS; i++) { + fi.submap_idx = i; + if (HYPERVISOR_xen_version(XENVER_get_features, &fi) < 0) + break; + for (j = 0; j < 32; j++) + xen_features[i * 32 + j] = !!(fi.submap & 1<<j); + } +} diff --git a/arch/i386/xen/manage.c b/arch/i386/xen/manage.c new file mode 100644 index 0000000..aa7af9e --- /dev/null +++ b/arch/i386/xen/manage.c @@ -0,0 +1,143 @@ +/* + * Handle extern requests for shutdown, reboot and sysrq + */ +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/reboot.h> +#include <linux/sysrq.h> + +#include <xen/xenbus.h> + +#define SHUTDOWN_INVALID -1 +#define SHUTDOWN_POWEROFF 0 +#define SHUTDOWN_SUSPEND 2 +/* Code 3 is SHUTDOWN_CRASH, which we don't use because the domain can only + * report a crash, not be instructed to crash! + * HALT is the same as POWEROFF, as far as we're concerned. The tools use + * the distinction when we return the reason code to them. + */ +#define SHUTDOWN_HALT 4 + +/* Ignore multiple shutdown requests. */ +static int shutting_down = SHUTDOWN_INVALID; + +static void shutdown_handler(struct xenbus_watch *watch, + const char **vec, unsigned int len) +{ + char *str; + struct xenbus_transaction xbt; + int err; + + if (shutting_down != SHUTDOWN_INVALID) + return; + + again: + err = xenbus_transaction_start(&xbt); + if (err) + return; + + str = (char *)xenbus_read(xbt, "control", "shutdown", NULL); + /* Ignore read errors and empty reads. */ + if (XENBUS_IS_ERR_READ(str)) { + xenbus_transaction_end(xbt, 1); + return; + } + + xenbus_write(xbt, "control", "shutdown", ""); + + err = xenbus_transaction_end(xbt, 0); + if (err == -EAGAIN) { + kfree(str); + goto again; + } + + if (strcmp(str, "poweroff") == 0 || + strcmp(str, "halt") == 0) + orderly_poweroff(false); + else if (strcmp(str, "reboot") == 0) + ctrl_alt_del(); + else { + printk(KERN_INFO "Ignoring shutdown request: %s\n", str); + shutting_down = SHUTDOWN_INVALID; + } + + kfree(str); +} + +static void sysrq_handler(struct xenbus_watch *watch, const char **vec, + unsigned int len) +{ + char sysrq_key = '\0'; + struct xenbus_transaction xbt; + int err; + + again: + err = xenbus_transaction_start(&xbt); + if (err) + return; + if (!xenbus_scanf(xbt, "control", "sysrq", "%c", &sysrq_key)) { + printk(KERN_ERR "Unable to read sysrq code in " + "control/sysrq\n"); + xenbus_transaction_end(xbt, 1); + return; + } + + if (sysrq_key != '\0') + xenbus_printf(xbt, "control", "sysrq", "%c", '\0'); + + err = xenbus_transaction_end(xbt, 0); + if (err == -EAGAIN) + goto again; + + if (sysrq_key != '\0') + handle_sysrq(sysrq_key, NULL); +} + +static struct xenbus_watch shutdown_watch = { + .node = "control/shutdown", + .callback = shutdown_handler +}; + +static struct xenbus_watch sysrq_watch = { + .node = "control/sysrq", + .callback = sysrq_handler +}; + +static int setup_shutdown_watcher(void) +{ + int err; + + err = register_xenbus_watch(&shutdown_watch); + if (err) { + printk(KERN_ERR "Failed to set shutdown watcher\n"); + return err; + } + + err = register_xenbus_watch(&sysrq_watch); + if (err) { + printk(KERN_ERR "Failed to set sysrq watcher\n"); + return err; + } + + return 0; +} + +static int shutdown_event(struct notifier_block *notifier, + unsigned long event, + void *data) +{ + setup_shutdown_watcher(); + return NOTIFY_DONE; +} + +static int __init setup_shutdown_event(void) +{ + static struct notifier_block xenstore_notifier = { + .notifier_call = shutdown_event + }; + register_xenstore_notifier(&xenstore_notifier); + + return 0; +} + +subsys_initcall(setup_shutdown_event); diff --git a/arch/i386/xen/mmu.c b/arch/i386/xen/mmu.c new file mode 100644 index 0000000..4ae038a --- /dev/null +++ b/arch/i386/xen/mmu.c @@ -0,0 +1,564 @@ +/* + * Xen mmu operations + * + * This file contains the various mmu fetch and update operations. + * The most important job they must perform is the mapping between the + * domain's pfn and the overall machine mfns. + * + * Xen allows guests to directly update the pagetable, in a controlled + * fashion. In other words, the guest modifies the same pagetable + * that the CPU actually uses, which eliminates the overhead of having + * a separate shadow pagetable. + * + * In order to allow this, it falls on the guest domain to map its + * notion of a "physical" pfn - which is just a domain-local linear + * address - into a real "machine address" which the CPU's MMU can + * use. + * + * A pgd_t/pmd_t/pte_t will typically contain an mfn, and so can be + * inserted directly into the pagetable. When creating a new + * pte/pmd/pgd, it converts the passed pfn into an mfn. Conversely, + * when reading the content back with __(pgd|pmd|pte)_val, it converts + * the mfn back into a pfn. + * + * The other constraint is that all pages which make up a pagetable + * must be mapped read-only in the guest. This prevents uncontrolled + * guest updates to the pagetable. Xen strictly enforces this, and + * will disallow any pagetable update which will end up mapping a + * pagetable page RW, and will disallow using any writable page as a + * pagetable. + * + * Naively, when loading %cr3 with the base of a new pagetable, Xen + * would need to validate the whole pagetable before going on. + * Naturally, this is quite slow. The solution is to "pin" a + * pagetable, which enforces all the constraints on the pagetable even + * when it is not actively in use. This menas that Xen can be assured + * that it is still valid when you do load it into %cr3, and doesn't + * need to revalidate it. + * + * Jeremy Fitzhardinge <jeremy@xensource.com>, XenSource Inc, 2007 + */ +#include <linux/sched.h> +#include <linux/highmem.h> +#include <linux/bug.h> +#include <linux/sched.h> + +#include <asm/pgtable.h> +#include <asm/tlbflush.h> +#include <asm/mmu_context.h> +#include <asm/paravirt.h> + +#include <asm/xen/hypercall.h> +#include <asm/xen/hypervisor.h> + +#include <xen/page.h> +#include <xen/interface/xen.h> + +#include "multicalls.h" +#include "mmu.h" + +xmaddr_t arbitrary_virt_to_machine(unsigned long address) +{ + pte_t *pte = lookup_address(address); + unsigned offset = address & PAGE_MASK; + + BUG_ON(pte == NULL); + + return XMADDR((pte_mfn(*pte) << PAGE_SHIFT) + offset); +} + +void make_lowmem_page_readonly(void *vaddr) +{ + pte_t *pte, ptev; + unsigned long address = (unsigned long)vaddr; + + pte = lookup_address(address); + BUG_ON(pte == NULL); + + ptev = pte_wrprotect(*pte); + + if (HYPERVISOR_update_va_mapping(address, ptev, 0)) + BUG(); +} + +void make_lowmem_page_readwrite(void *vaddr) +{ + pte_t *pte, ptev; + unsigned long address = (unsigned long)vaddr; + + pte = lookup_address(address); + BUG_ON(pte == NULL); + + ptev = pte_mkwrite(*pte); + + if (HYPERVISOR_update_va_mapping(address, ptev, 0)) + BUG(); +} + + +void xen_set_pmd(pmd_t *ptr, pmd_t val) +{ + struct multicall_space mcs; + struct mmu_update *u; + + preempt_disable(); + + mcs = xen_mc_entry(sizeof(*u)); + u = mcs.args; + u->ptr = virt_to_machine(ptr).maddr; + u->val = pmd_val_ma(val); + MULTI_mmu_update(mcs.mc, u, 1, NULL, DOMID_SELF); + + xen_mc_issue(PARAVIRT_LAZY_MMU); + + preempt_enable(); +} + +/* + * Associate a virtual page frame with a given physical page frame + * and protection flags for that frame. + */ +void set_pte_mfn(unsigned long vaddr, unsigned long mfn, pgprot_t flags) +{ + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + + pgd = swapper_pg_dir + pgd_index(vaddr); + if (pgd_none(*pgd)) { + BUG(); + return; + } + pud = pud_offset(pgd, vaddr); + if (pud_none(*pud)) { + BUG(); + return; + } + pmd = pmd_offset(pud, vaddr); + if (pmd_none(*pmd)) { + BUG(); + return; + } + pte = pte_offset_kernel(pmd, vaddr); + /* <mfn,flags> stored as-is, to permit clearing entries */ + xen_set_pte(pte, mfn_pte(mfn, flags)); + + /* + * It's enough to flush this one mapping. + * (PGE mappings get flushed as well) + */ + __flush_tlb_one(vaddr); +} + +void xen_set_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pteval) +{ + if (mm == current->mm || mm == &init_mm) { + if (xen_get_lazy_mode() == PARAVIRT_LAZY_MMU) { + struct multicall_space mcs; + mcs = xen_mc_entry(0); + + MULTI_update_va_mapping(mcs.mc, addr, pteval, 0); + xen_mc_issue(PARAVIRT_LAZY_MMU); + return; + } else + if (HYPERVISOR_update_va_mapping(addr, pteval, 0) == 0) + return; + } + xen_set_pte(ptep, pteval); +} + +#ifdef CONFIG_X86_PAE +void xen_set_pud(pud_t *ptr, pud_t val) +{ + struct multicall_space mcs; + struct mmu_update *u; + + preempt_disable(); + + mcs = xen_mc_entry(sizeof(*u)); + u = mcs.args; + u->ptr = virt_to_machine(ptr).maddr; + u->val = pud_val_ma(val); + MULTI_mmu_update(mcs.mc, u, 1, NULL, DOMID_SELF); + + xen_mc_issue(PARAVIRT_LAZY_MMU); + + preempt_enable(); +} + +void xen_set_pte(pte_t *ptep, pte_t pte) +{ + ptep->pte_high = pte.pte_high; + smp_wmb(); + ptep->pte_low = pte.pte_low; +} + +void xen_set_pte_atomic(pte_t *ptep, pte_t pte) +{ + set_64bit((u64 *)ptep, pte_val_ma(pte)); +} + +void xen_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) +{ + ptep->pte_low = 0; + smp_wmb(); /* make sure low gets written first */ + ptep->pte_high = 0; +} + +void xen_pmd_clear(pmd_t *pmdp) +{ + xen_set_pmd(pmdp, __pmd(0)); +} + +unsigned long long xen_pte_val(pte_t pte) +{ + unsigned long long ret = 0; + + if (pte.pte_low) { + ret = ((unsigned long long)pte.pte_high << 32) | pte.pte_low; + ret = machine_to_phys(XMADDR(ret)).paddr | 1; + } + + return ret; +} + +unsigned long long xen_pmd_val(pmd_t pmd) +{ + unsigned long long ret = pmd.pmd; + if (ret) + ret = machine_to_phys(XMADDR(ret)).paddr | 1; + return ret; +} + +unsigned long long xen_pgd_val(pgd_t pgd) +{ + unsigned long long ret = pgd.pgd; + if (ret) + ret = machine_to_phys(XMADDR(ret)).paddr | 1; + return ret; +} + +pte_t xen_make_pte(unsigned long long pte) +{ + if (pte & 1) + pte = phys_to_machine(XPADDR(pte)).maddr; + + return (pte_t){ pte, pte >> 32 }; +} + +pmd_t xen_make_pmd(unsigned long long pmd) +{ + if (pmd & 1) + pmd = phys_to_machine(XPADDR(pmd)).maddr; + + return (pmd_t){ pmd }; +} + +pgd_t xen_make_pgd(unsigned long long pgd) +{ + if (pgd & _PAGE_PRESENT) + pgd = phys_to_machine(XPADDR(pgd)).maddr; + + return (pgd_t){ pgd }; +} +#else /* !PAE */ +void xen_set_pte(pte_t *ptep, pte_t pte) +{ + *ptep = pte; +} + +unsigned long xen_pte_val(pte_t pte) +{ + unsigned long ret = pte.pte_low; + + if (ret & _PAGE_PRESENT) + ret = machine_to_phys(XMADDR(ret)).paddr; + + return ret; +} + +unsigned long xen_pgd_val(pgd_t pgd) +{ + unsigned long ret = pgd.pgd; + if (ret) + ret = machine_to_phys(XMADDR(ret)).paddr | 1; + return ret; +} + +pte_t xen_make_pte(unsigned long pte) +{ + if (pte & _PAGE_PRESENT) + pte = phys_to_machine(XPADDR(pte)).maddr; + + return (pte_t){ pte }; +} + +pgd_t xen_make_pgd(unsigned long pgd) +{ + if (pgd & _PAGE_PRESENT) + pgd = phys_to_machine(XPADDR(pgd)).maddr; + + return (pgd_t){ pgd }; +} +#endif /* CONFIG_X86_PAE */ + + + +/* + (Yet another) pagetable walker. This one is intended for pinning a + pagetable. This means that it walks a pagetable and calls the + callback function on each page it finds making up the page table, + at every level. It walks the entire pagetable, but it only bothers + pinning pte pages which are below pte_limit. In the normal case + this will be TASK_SIZE, but at boot we need to pin up to + FIXADDR_TOP. But the important bit is that we don't pin beyond + there, because then we start getting into Xen's ptes. +*/ +static int pgd_walk(pgd_t *pgd_base, int (*func)(struct page *, unsigned), + unsigned long limit) +{ + pgd_t *pgd = pgd_base; + int flush = 0; + unsigned long addr = 0; + unsigned long pgd_next; + + BUG_ON(limit > FIXADDR_TOP); + + if (xen_feature(XENFEAT_auto_translated_physmap)) + return 0; + + for (; addr != FIXADDR_TOP; pgd++, addr = pgd_next) { + pud_t *pud; + unsigned long pud_limit, pud_next; + + pgd_next = pud_limit = pgd_addr_end(addr, FIXADDR_TOP); + + if (!pgd_val(*pgd)) + continue; + + pud = pud_offset(pgd, 0); + + if (PTRS_PER_PUD > 1) /* not folded */ + flush |= (*func)(virt_to_page(pud), 0); + + for (; addr != pud_limit; pud++, addr = pud_next) { + pmd_t *pmd; + unsigned long pmd_limit; + + pud_next = pud_addr_end(addr, pud_limit); + + if (pud_next < limit) + pmd_limit = pud_next; + else + pmd_limit = limit; + + if (pud_none(*pud)) + continue; + + pmd = pmd_offset(pud, 0); + + if (PTRS_PER_PMD > 1) /* not folded */ + flush |= (*func)(virt_to_page(pmd), 0); + + for (; addr != pmd_limit; pmd++) { + addr += (PAGE_SIZE * PTRS_PER_PTE); + if ((pmd_limit-1) < (addr-1)) { + addr = pmd_limit; + break; + } + + if (pmd_none(*pmd)) + continue; + + flush |= (*func)(pmd_page(*pmd), 0); + } + } + } + + flush |= (*func)(virt_to_page(pgd_base), UVMF_TLB_FLUSH); + + return flush; +} + +static int pin_page(struct page *page, unsigned flags) +{ + unsigned pgfl = test_and_set_bit(PG_pinned, &page->flags); + int flush; + + if (pgfl) + flush = 0; /* already pinned */ + else if (PageHighMem(page)) + /* kmaps need flushing if we found an unpinned + highpage */ + flush = 1; + else { + void *pt = lowmem_page_address(page); + unsigned long pfn = page_to_pfn(page); + struct multicall_space mcs = __xen_mc_entry(0); + + flush = 0; + + MULTI_update_va_mapping(mcs.mc, (unsigned long)pt, + pfn_pte(pfn, PAGE_KERNEL_RO), + flags); + } + + return flush; +} + +/* This is called just after a mm has been created, but it has not + been used yet. We need to make sure that its pagetable is all + read-only, and can be pinned. */ +void xen_pgd_pin(pgd_t *pgd) +{ + struct multicall_space mcs; + struct mmuext_op *op; + + xen_mc_batch(); + + if (pgd_walk(pgd, pin_page, TASK_SIZE)) { + /* re-enable interrupts for kmap_flush_unused */ + xen_mc_issue(0); + kmap_flush_unused(); + xen_mc_batch(); + } + + mcs = __xen_mc_entry(sizeof(*op)); + op = mcs.args; + +#ifdef CONFIG_X86_PAE + op->cmd = MMUEXT_PIN_L3_TABLE; +#else + op->cmd = MMUEXT_PIN_L2_TABLE; +#endif + op->arg1.mfn = pfn_to_mfn(PFN_DOWN(__pa(pgd))); + MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); + + xen_mc_issue(0); +} + +/* The init_mm pagetable is really pinned as soon as its created, but + that's before we have page structures to store the bits. So do all + the book-keeping now. */ +static __init int mark_pinned(struct page *page, unsigned flags) +{ + SetPagePinned(page); + return 0; +} + +void __init xen_mark_init_mm_pinned(void) +{ + pgd_walk(init_mm.pgd, mark_pinned, FIXADDR_TOP); +} + +static int unpin_page(struct page *page, unsigned flags) +{ + unsigned pgfl = test_and_clear_bit(PG_pinned, &page->flags); + + if (pgfl && !PageHighMem(page)) { + void *pt = lowmem_page_address(page); + unsigned long pfn = page_to_pfn(page); + struct multicall_space mcs = __xen_mc_entry(0); + + MULTI_update_va_mapping(mcs.mc, (unsigned long)pt, + pfn_pte(pfn, PAGE_KERNEL), + flags); + } + + return 0; /* never need to flush on unpin */ +} + +/* Release a pagetables pages back as normal RW */ +static void xen_pgd_unpin(pgd_t *pgd) +{ + struct mmuext_op *op; + struct multicall_space mcs; + + xen_mc_batch(); + + mcs = __xen_mc_entry(sizeof(*op)); + + op = mcs.args; + op->cmd = MMUEXT_UNPIN_TABLE; + op->arg1.mfn = pfn_to_mfn(PFN_DOWN(__pa(pgd))); + + MULTI_mmuext_op(mcs.mc, op, 1, NULL, DOMID_SELF); + + pgd_walk(pgd, unpin_page, TASK_SIZE); + + xen_mc_issue(0); +} + +void xen_activate_mm(struct mm_struct *prev, struct mm_struct *next) +{ + spin_lock(&next->page_table_lock); + xen_pgd_pin(next->pgd); + spin_unlock(&next->page_table_lock); +} + +void xen_dup_mmap(struct mm_struct *oldmm, struct mm_struct *mm) +{ + spin_lock(&mm->page_table_lock); + xen_pgd_pin(mm->pgd); + spin_unlock(&mm->page_table_lock); +} + + +#ifdef CONFIG_SMP +/* Another cpu may still have their %cr3 pointing at the pagetable, so + we need to repoint it somewhere else before we can unpin it. */ +static void drop_other_mm_ref(void *info) +{ + struct mm_struct *mm = info; + + if (__get_cpu_var(cpu_tlbstate).active_mm == mm) + leave_mm(smp_processor_id()); +} + +static void drop_mm_ref(struct mm_struct *mm) +{ + if (current->active_mm == mm) { + if (current->mm == mm) + load_cr3(swapper_pg_dir); + else + leave_mm(smp_processor_id()); + } + + if (!cpus_empty(mm->cpu_vm_mask)) + xen_smp_call_function_mask(mm->cpu_vm_mask, drop_other_mm_ref, + mm, 1); +} +#else +static void drop_mm_ref(struct mm_struct *mm) +{ + if (current->active_mm == mm) + load_cr3(swapper_pg_dir); +} +#endif + +/* + * While a process runs, Xen pins its pagetables, which means that the + * hypervisor forces it to be read-only, and it controls all updates + * to it. This means that all pagetable updates have to go via the + * hypervisor, which is moderately expensive. + * + * Since we're pulling the pagetable down, we switch to use init_mm, + * unpin old process pagetable and mark it all read-write, which + * allows further operations on it to be simple memory accesses. + * + * The only subtle point is that another CPU may be still using the + * pagetable because of lazy tlb flushing. This means we need need to + * switch all CPUs off this pagetable before we can unpin it. + */ +void xen_exit_mmap(struct mm_struct *mm) +{ + get_cpu(); /* make sure we don't move around */ + drop_mm_ref(mm); + put_cpu(); + + spin_lock(&mm->page_table_lock); + xen_pgd_unpin(mm->pgd); + spin_unlock(&mm->page_table_lock); +} diff --git a/arch/i386/xen/mmu.h b/arch/i386/xen/mmu.h new file mode 100644 index 0000000..c9ff27f --- /dev/null +++ b/arch/i386/xen/mmu.h @@ -0,0 +1,60 @@ +#ifndef _XEN_MMU_H + +#include <linux/linkage.h> +#include <asm/page.h> + +/* + * Page-directory addresses above 4GB do not fit into architectural %cr3. + * When accessing %cr3, or equivalent field in vcpu_guest_context, guests + * must use the following accessor macros to pack/unpack valid MFNs. + * + * Note that Xen is using the fact that the pagetable base is always + * page-aligned, and putting the 12 MSB of the address into the 12 LSB + * of cr3. + */ +#define xen_pfn_to_cr3(pfn) (((unsigned)(pfn) << 12) | ((unsigned)(pfn) >> 20)) +#define xen_cr3_to_pfn(cr3) (((unsigned)(cr3) >> 12) | ((unsigned)(cr3) << 20)) + + +void set_pte_mfn(unsigned long vaddr, unsigned long pfn, pgprot_t flags); + +void xen_set_pte(pte_t *ptep, pte_t pteval); +void xen_set_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pteval); +void xen_set_pmd(pmd_t *pmdp, pmd_t pmdval); + +void xen_activate_mm(struct mm_struct *prev, struct mm_struct *next); +void xen_dup_mmap(struct mm_struct *oldmm, struct mm_struct *mm); +void xen_exit_mmap(struct mm_struct *mm); + +void xen_pgd_pin(pgd_t *pgd); +//void xen_pgd_unpin(pgd_t *pgd); + +#ifdef CONFIG_X86_PAE +unsigned long long xen_pte_val(pte_t); +unsigned long long xen_pmd_val(pmd_t); +unsigned long long xen_pgd_val(pgd_t); + +pte_t xen_make_pte(unsigned long long); +pmd_t xen_make_pmd(unsigned long long); +pgd_t xen_make_pgd(unsigned long long); + +void xen_set_pte_at(struct mm_struct *mm, unsigned long addr, + pte_t *ptep, pte_t pteval); +void xen_set_pte_atomic(pte_t *ptep, pte_t pte); +void xen_set_pud(pud_t *ptr, pud_t val); +void xen_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep); +void xen_pmd_clear(pmd_t *pmdp); + + +#else +unsigned long xen_pte_val(pte_t); +unsigned long xen_pmd_val(pmd_t); +unsigned long xen_pgd_val(pgd_t); + +pte_t xen_make_pte(unsigned long); +pmd_t xen_make_pmd(unsigned long); +pgd_t xen_make_pgd(unsigned long); +#endif + +#endif /* _XEN_MMU_H */ diff --git a/arch/i386/xen/multicalls.c b/arch/i386/xen/multicalls.c new file mode 100644 index 0000000..c837e8e --- /dev/null +++ b/arch/i386/xen/multicalls.c @@ -0,0 +1,90 @@ +/* + * Xen hypercall batching. + * + * Xen allows multiple hypercalls to be issued at once, using the + * multicall interface. This allows the cost of trapping into the + * hypervisor to be amortized over several calls. + * + * This file implements a simple interface for multicalls. There's a + * per-cpu buffer of outstanding multicalls. When you want to queue a + * multicall for issuing, you can allocate a multicall slot for the + * call and its arguments, along with storage for space which is + * pointed to by the arguments (for passing pointers to structures, + * etc). When the multicall is actually issued, all the space for the + * commands and allocated memory is freed for reuse. + * + * Multicalls are flushed whenever any of the buffers get full, or + * when explicitly requested. There's no way to get per-multicall + * return results back. It will BUG if any of the multicalls fail. + * + * Jeremy Fitzhardinge <jeremy@xensource.com>, XenSource Inc, 2007 + */ +#include <linux/percpu.h> +#include <linux/hardirq.h> + +#include <asm/xen/hypercall.h> + +#include "multicalls.h" + +#define MC_BATCH 32 +#define MC_ARGS (MC_BATCH * 16 / sizeof(u64)) + +struct mc_buffer { + struct multicall_entry entries[MC_BATCH]; + u64 args[MC_ARGS]; + unsigned mcidx, argidx; +}; + +static DEFINE_PER_CPU(struct mc_buffer, mc_buffer); +DEFINE_PER_CPU(unsigned long, xen_mc_irq_flags); + +void xen_mc_flush(void) +{ + struct mc_buffer *b = &__get_cpu_var(mc_buffer); + int ret = 0; + unsigned long flags; + + BUG_ON(preemptible()); + + /* Disable interrupts in case someone comes in and queues + something in the middle */ + local_irq_save(flags); + + if (b->mcidx) { + int i; + + if (HYPERVISOR_multicall(b->entries, b->mcidx) != 0) + BUG(); + for (i = 0; i < b->mcidx; i++) + if (b->entries[i].result < 0) + ret++; + b->mcidx = 0; + b->argidx = 0; + } else + BUG_ON(b->argidx != 0); + + local_irq_restore(flags); + + BUG_ON(ret); +} + +struct multicall_space __xen_mc_entry(size_t args) +{ + struct mc_buffer *b = &__get_cpu_var(mc_buffer); + struct multicall_space ret; + unsigned argspace = (args + sizeof(u64) - 1) / sizeof(u64); + + BUG_ON(preemptible()); + BUG_ON(argspace > MC_ARGS); + + if (b->mcidx == MC_BATCH || + (b->argidx + argspace) > MC_ARGS) + xen_mc_flush(); + + ret.mc = &b->entries[b->mcidx]; + b->mcidx++; + ret.args = &b->args[b->argidx]; + b->argidx += argspace; + + return ret; +} diff --git a/arch/i386/xen/multicalls.h b/arch/i386/xen/multicalls.h new file mode 100644 index 0000000..e6f7530 --- /dev/null +++ b/arch/i386/xen/multicalls.h @@ -0,0 +1,45 @@ +#ifndef _XEN_MULTICALLS_H +#define _XEN_MULTICALLS_H + +#include "xen-ops.h" + +/* Multicalls */ +struct multicall_space +{ + struct multicall_entry *mc; + void *args; +}; + +/* Allocate room for a multicall and its args */ +struct multicall_space __xen_mc_entry(size_t args); + +DECLARE_PER_CPU(unsigned long, xen_mc_irq_flags); + +/* Call to start a batch of multiple __xen_mc_entry()s. Must be + paired with xen_mc_issue() */ +static inline void xen_mc_batch(void) +{ + /* need to disable interrupts until this entry is complete */ + local_irq_save(__get_cpu_var(xen_mc_irq_flags)); +} + +static inline struct multicall_space xen_mc_entry(size_t args) +{ + xen_mc_batch(); + return __xen_mc_entry(args); +} + +/* Flush all pending multicalls */ +void xen_mc_flush(void); + +/* Issue a multicall if we're not in a lazy mode */ +static inline void xen_mc_issue(unsigned mode) +{ + if ((xen_get_lazy_mode() & mode) == 0) + xen_mc_flush(); + + /* restore flags saved in xen_mc_batch */ + local_irq_restore(x86_read_percpu(xen_mc_irq_flags)); +} + +#endif /* _XEN_MULTICALLS_H */ diff --git a/arch/i386/xen/setup.c b/arch/i386/xen/setup.c new file mode 100644 index 0000000..2fe6eac --- /dev/null +++ b/arch/i386/xen/setup.c @@ -0,0 +1,96 @@ +/* + * Machine specific setup for xen + * + * Jeremy Fitzhardinge <jeremy@xensource.com>, XenSource Inc, 2007 + */ + +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/pm.h> + +#include <asm/elf.h> +#include <asm/e820.h> +#include <asm/setup.h> +#include <asm/xen/hypervisor.h> +#include <asm/xen/hypercall.h> + +#include <xen/interface/physdev.h> +#include <xen/features.h> + +#include "xen-ops.h" + +/* These are code, but not functions. Defined in entry.S */ +extern const char xen_hypervisor_callback[]; +extern const char xen_failsafe_callback[]; + +unsigned long *phys_to_machine_mapping; +EXPORT_SYMBOL(phys_to_machine_mapping); + +/** + * machine_specific_memory_setup - Hook for machine specific memory setup. + **/ + +char * __init xen_memory_setup(void) +{ + unsigned long max_pfn = xen_start_info->nr_pages; + + e820.nr_map = 0; + add_memory_region(0, PFN_PHYS(max_pfn), E820_RAM); + + return "Xen"; +} + +static void xen_idle(void) +{ + local_irq_disable(); + + if (need_resched()) + local_irq_enable(); + else { + current_thread_info()->status &= ~TS_POLLING; + smp_mb__after_clear_bit(); + safe_halt(); + current_thread_info()->status |= TS_POLLING; + } +} + +void __init xen_arch_setup(void) +{ + struct physdev_set_iopl set_iopl; + int rc; + + HYPERVISOR_vm_assist(VMASST_CMD_enable, VMASST_TYPE_4gb_segments); + HYPERVISOR_vm_assist(VMASST_CMD_enable, VMASST_TYPE_writable_pagetables); + + if (!xen_feature(XENFEAT_auto_translated_physmap)) + HYPERVISOR_vm_assist(VMASST_CMD_enable, VMASST_TYPE_pae_extended_cr3); + + HYPERVISOR_set_callbacks(__KERNEL_CS, (unsigned long)xen_hypervisor_callback, + __KERNEL_CS, (unsigned long)xen_failsafe_callback); + + set_iopl.iopl = 1; + rc = HYPERVISOR_physdev_op(PHYSDEVOP_set_iopl, &set_iopl); + if (rc != 0) + printk(KERN_INFO "physdev_op failed %d\n", rc); + +#ifdef CONFIG_ACPI + if (!(xen_start_info->flags & SIF_INITDOMAIN)) { + printk(KERN_INFO "ACPI in unprivileged domain disabled\n"); + disable_acpi(); + } +#endif + + memcpy(boot_command_line, xen_start_info->cmd_line, + MAX_GUEST_CMDLINE > COMMAND_LINE_SIZE ? + COMMAND_LINE_SIZE : MAX_GUEST_CMDLINE); + + pm_idle = xen_idle; + +#ifdef CONFIG_SMP + /* fill cpus_possible with all available cpus */ + xen_fill_possible_map(); +#endif + + paravirt_disable_iospace(); +} diff --git a/arch/i386/xen/smp.c b/arch/i386/xen/smp.c new file mode 100644 index 0000000..557b8e2 --- /dev/null +++ b/arch/i386/xen/smp.c @@ -0,0 +1,404 @@ +/* + * Xen SMP support + * + * This file implements the Xen versions of smp_ops. SMP under Xen is + * very straightforward. Bringing a CPU up is simply a matter of + * loading its initial context and setting it running. + * + * IPIs are handled through the Xen event mechanism. + * + * Because virtual CPUs can be scheduled onto any real CPU, there's no + * useful topology information for the kernel to make use of. As a + * result, all CPUs are treated as if they're single-core and + * single-threaded. + * + * This does not handle HOTPLUG_CPU yet. + */ +#include <linux/sched.h> +#include <linux/err.h> +#include <linux/smp.h> + +#include <asm/paravirt.h> +#include <asm/desc.h> +#include <asm/pgtable.h> +#include <asm/cpu.h> + +#include <xen/interface/xen.h> +#include <xen/interface/vcpu.h> + +#include <asm/xen/interface.h> +#include <asm/xen/hypercall.h> + +#include <xen/page.h> +#include <xen/events.h> + +#include "xen-ops.h" +#include "mmu.h" + +static cpumask_t cpu_initialized_map; +static DEFINE_PER_CPU(int, resched_irq); +static DEFINE_PER_CPU(int, callfunc_irq); + +/* + * Structure and data for smp_call_function(). This is designed to minimise + * static memory requirements. It also looks cleaner. + */ +static DEFINE_SPINLOCK(call_lock); + +struct call_data_struct { + void (*func) (void *info); + void *info; + atomic_t started; + atomic_t finished; + int wait; +}; + +static irqreturn_t xen_call_function_interrupt(int irq, void *dev_id); + +static struct call_data_struct *call_data; + +/* + * Reschedule call back. Nothing to do, + * all the work is done automatically when + * we return from the interrupt. + */ +static irqreturn_t xen_reschedule_interrupt(int irq, void *dev_id) +{ + return IRQ_HANDLED; +} + +static __cpuinit void cpu_bringup_and_idle(void) +{ + int cpu = smp_processor_id(); + + cpu_init(); + + preempt_disable(); + per_cpu(cpu_state, cpu) = CPU_ONLINE; + + xen_setup_cpu_clockevents(); + + /* We can take interrupts now: we're officially "up". */ + local_irq_enable(); + + wmb(); /* make sure everything is out */ + cpu_idle(); +} + +static int xen_smp_intr_init(unsigned int cpu) +{ + int rc; + const char *resched_name, *callfunc_name; + + per_cpu(resched_irq, cpu) = per_cpu(callfunc_irq, cpu) = -1; + + resched_name = kasprintf(GFP_KERNEL, "resched%d", cpu); + rc = bind_ipi_to_irqhandler(XEN_RESCHEDULE_VECTOR, + cpu, + xen_reschedule_interrupt, + IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING, + resched_name, + NULL); + if (rc < 0) + goto fail; + per_cpu(resched_irq, cpu) = rc; + + callfunc_name = kasprintf(GFP_KERNEL, "callfunc%d", cpu); + rc = bind_ipi_to_irqhandler(XEN_CALL_FUNCTION_VECTOR, + cpu, + xen_call_function_interrupt, + IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING, + callfunc_name, + NULL); + if (rc < 0) + goto fail; + per_cpu(callfunc_irq, cpu) = rc; + + return 0; + + fail: + if (per_cpu(resched_irq, cpu) >= 0) + unbind_from_irqhandler(per_cpu(resched_irq, cpu), NULL); + if (per_cpu(callfunc_irq, cpu) >= 0) + unbind_from_irqhandler(per_cpu(callfunc_irq, cpu), NULL); + return rc; +} + +void __init xen_fill_possible_map(void) +{ + int i, rc; + + for (i = 0; i < NR_CPUS; i++) { + rc = HYPERVISOR_vcpu_op(VCPUOP_is_up, i, NULL); + if (rc >= 0) + cpu_set(i, cpu_possible_map); + } +} + +void __init xen_smp_prepare_boot_cpu(void) +{ + int cpu; + + BUG_ON(smp_processor_id() != 0); + native_smp_prepare_boot_cpu(); + + /* We've switched to the "real" per-cpu gdt, so make sure the + old memory can be recycled */ + make_lowmem_page_readwrite(&per_cpu__gdt_page); + + for (cpu = 0; cpu < NR_CPUS; cpu++) { + cpus_clear(cpu_sibling_map[cpu]); + cpus_clear(cpu_core_map[cpu]); + } + + xen_setup_vcpu_info_placement(); +} + +void __init xen_smp_prepare_cpus(unsigned int max_cpus) +{ + unsigned cpu; + + for (cpu = 0; cpu < NR_CPUS; cpu++) { + cpus_clear(cpu_sibling_map[cpu]); + cpus_clear(cpu_core_map[cpu]); + } + + smp_store_cpu_info(0); + set_cpu_sibling_map(0); + + if (xen_smp_intr_init(0)) + BUG(); + + cpu_initialized_map = cpumask_of_cpu(0); + + /* Restrict the possible_map according to max_cpus. */ + while ((num_possible_cpus() > 1) && (num_possible_cpus() > max_cpus)) { + for (cpu = NR_CPUS-1; !cpu_isset(cpu, cpu_possible_map); cpu--) + continue; + cpu_clear(cpu, cpu_possible_map); + } + + for_each_possible_cpu (cpu) { + struct task_struct *idle; + + if (cpu == 0) + continue; + + idle = fork_idle(cpu); + if (IS_ERR(idle)) + panic("failed fork for CPU %d", cpu); + + cpu_set(cpu, cpu_present_map); + } + + //init_xenbus_allowed_cpumask(); +} + +static __cpuinit int +cpu_initialize_context(unsigned int cpu, struct task_struct *idle) +{ + struct vcpu_guest_context *ctxt; + struct gdt_page *gdt = &per_cpu(gdt_page, cpu); + + if (cpu_test_and_set(cpu, cpu_initialized_map)) + return 0; + + ctxt = kzalloc(sizeof(*ctxt), GFP_KERNEL); + if (ctxt == NULL) + return -ENOMEM; + + ctxt->flags = VGCF_IN_KERNEL; + ctxt->user_regs.ds = __USER_DS; + ctxt->user_regs.es = __USER_DS; + ctxt->user_regs.fs = __KERNEL_PERCPU; + ctxt->user_regs.gs = 0; + ctxt->user_regs.ss = __KERNEL_DS; + ctxt->user_regs.eip = (unsigned long)cpu_bringup_and_idle; + ctxt->user_regs.eflags = 0x1000; /* IOPL_RING1 */ + + memset(&ctxt->fpu_ctxt, 0, sizeof(ctxt->fpu_ctxt)); + + xen_copy_trap_info(ctxt->trap_ctxt); + + ctxt->ldt_ents = 0; + + BUG_ON((unsigned long)gdt->gdt & ~PAGE_MASK); + make_lowmem_page_readonly(gdt->gdt); + + ctxt->gdt_frames[0] = virt_to_mfn(gdt->gdt); + ctxt->gdt_ents = ARRAY_SIZE(gdt->gdt); + + ctxt->user_regs.cs = __KERNEL_CS; + ctxt->user_regs.esp = idle->thread.esp0 - sizeof(struct pt_regs); + + ctxt->kernel_ss = __KERNEL_DS; + ctxt->kernel_sp = idle->thread.esp0; + + ctxt->event_callback_cs = __KERNEL_CS; + ctxt->event_callback_eip = (unsigned long)xen_hypervisor_callback; + ctxt->failsafe_callback_cs = __KERNEL_CS; + ctxt->failsafe_callback_eip = (unsigned long)xen_failsafe_callback; + + per_cpu(xen_cr3, cpu) = __pa(swapper_pg_dir); + ctxt->ctrlreg[3] = xen_pfn_to_cr3(virt_to_mfn(swapper_pg_dir)); + + if (HYPERVISOR_vcpu_op(VCPUOP_initialise, cpu, ctxt)) + BUG(); + + kfree(ctxt); + return 0; +} + +int __cpuinit xen_cpu_up(unsigned int cpu) +{ + struct task_struct *idle = idle_task(cpu); + int rc; + +#if 0 + rc = cpu_up_check(cpu); + if (rc) + return rc; +#endif + + init_gdt(cpu); + per_cpu(current_task, cpu) = idle; + irq_ctx_init(cpu); + xen_setup_timer(cpu); + + /* make sure interrupts start blocked */ + per_cpu(xen_vcpu, cpu)->evtchn_upcall_mask = 1; + + rc = cpu_initialize_context(cpu, idle); + if (rc) + return rc; + + if (num_online_cpus() == 1) + alternatives_smp_switch(1); + + rc = xen_smp_intr_init(cpu); + if (rc) + return rc; + + smp_store_cpu_info(cpu); + set_cpu_sibling_map(cpu); + /* This must be done before setting cpu_online_map */ + wmb(); + + cpu_set(cpu, cpu_online_map); + + rc = HYPERVISOR_vcpu_op(VCPUOP_up, cpu, NULL); + BUG_ON(rc); + + return 0; +} + +void xen_smp_cpus_done(unsigned int max_cpus) +{ +} + +static void stop_self(void *v) +{ + int cpu = smp_processor_id(); + + /* make sure we're not pinning something down */ + load_cr3(swapper_pg_dir); + /* should set up a minimal gdt */ + + HYPERVISOR_vcpu_op(VCPUOP_down, cpu, NULL); + BUG(); +} + +void xen_smp_send_stop(void) +{ + smp_call_function(stop_self, NULL, 0, 0); +} + +void xen_smp_send_reschedule(int cpu) +{ + xen_send_IPI_one(cpu, XEN_RESCHEDULE_VECTOR); +} + + +static void xen_send_IPI_mask(cpumask_t mask, enum ipi_vector vector) +{ + unsigned cpu; + + cpus_and(mask, mask, cpu_online_map); + + for_each_cpu_mask(cpu, mask) + xen_send_IPI_one(cpu, vector); +} + +static irqreturn_t xen_call_function_interrupt(int irq, void *dev_id) +{ + void (*func) (void *info) = call_data->func; + void *info = call_data->info; + int wait = call_data->wait; + + /* + * Notify initiating CPU that I've grabbed the data and am + * about to execute the function + */ + mb(); + atomic_inc(&call_data->started); + /* + * At this point the info structure may be out of scope unless wait==1 + */ + irq_enter(); + (*func)(info); + irq_exit(); + + if (wait) { + mb(); /* commit everything before setting finished */ + atomic_inc(&call_data->finished); + } + + return IRQ_HANDLED; +} + +int xen_smp_call_function_mask(cpumask_t mask, void (*func)(void *), + void *info, int wait) +{ + struct call_data_struct data; + int cpus; + + /* Holding any lock stops cpus from going down. */ + spin_lock(&call_lock); + + cpu_clear(smp_processor_id(), mask); + + cpus = cpus_weight(mask); + if (!cpus) { + spin_unlock(&call_lock); + return 0; + } + + /* Can deadlock when called with interrupts disabled */ + WARN_ON(irqs_disabled()); + + data.func = func; + data.info = info; + atomic_set(&data.started, 0); + data.wait = wait; + if (wait) + atomic_set(&data.finished, 0); + + call_data = &data; + mb(); /* write everything before IPI */ + + /* Send a message to other CPUs and wait for them to respond */ + xen_send_IPI_mask(mask, XEN_CALL_FUNCTION_VECTOR); + + /* Make sure other vcpus get a chance to run. + XXX too severe? Maybe we should check the other CPU's states? */ + HYPERVISOR_sched_op(SCHEDOP_yield, 0); + + /* Wait for response */ + while (atomic_read(&data.started) != cpus || + (wait && atomic_read(&data.finished) != cpus)) + cpu_relax(); + + spin_unlock(&call_lock); + + return 0; +} diff --git a/arch/i386/xen/time.c b/arch/i386/xen/time.c new file mode 100644 index 0000000..51fdabf --- /dev/null +++ b/arch/i386/xen/time.c @@ -0,0 +1,590 @@ +/* + * Xen time implementation. + * + * This is implemented in terms of a clocksource driver which uses + * the hypervisor clock as a nanosecond timebase, and a clockevent + * driver which uses the hypervisor's timer mechanism. + * + * Jeremy Fitzhardinge <jeremy@xensource.com>, XenSource Inc, 2007 + */ +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/clocksource.h> +#include <linux/clockchips.h> +#include <linux/kernel_stat.h> + +#include <asm/xen/hypervisor.h> +#include <asm/xen/hypercall.h> + +#include <xen/events.h> +#include <xen/interface/xen.h> +#include <xen/interface/vcpu.h> + +#include "xen-ops.h" + +#define XEN_SHIFT 22 + +/* Xen may fire a timer up to this many ns early */ +#define TIMER_SLOP 100000 +#define NS_PER_TICK (1000000000LL / HZ) + +static cycle_t xen_clocksource_read(void); + +/* These are perodically updated in shared_info, and then copied here. */ +struct shadow_time_info { + u64 tsc_timestamp; /* TSC at last update of time vals. */ + u64 system_timestamp; /* Time, in nanosecs, since boot. */ + u32 tsc_to_nsec_mul; + int tsc_shift; + u32 version; +}; + +static DEFINE_PER_CPU(struct shadow_time_info, shadow_time); + +/* runstate info updated by Xen */ +static DEFINE_PER_CPU(struct vcpu_runstate_info, runstate); + +/* snapshots of runstate info */ +static DEFINE_PER_CPU(struct vcpu_runstate_info, runstate_snapshot); + +/* unused ns of stolen and blocked time */ +static DEFINE_PER_CPU(u64, residual_stolen); +static DEFINE_PER_CPU(u64, residual_blocked); + +/* return an consistent snapshot of 64-bit time/counter value */ +static u64 get64(const u64 *p) +{ + u64 ret; + + if (BITS_PER_LONG < 64) { + u32 *p32 = (u32 *)p; + u32 h, l; + + /* + * Read high then low, and then make sure high is + * still the same; this will only loop if low wraps + * and carries into high. + * XXX some clean way to make this endian-proof? + */ + do { + h = p32[1]; + barrier(); + l = p32[0]; + barrier(); + } while (p32[1] != h); + + ret = (((u64)h) << 32) | l; + } else + ret = *p; + + return ret; +} + +/* + * Runstate accounting + */ +static void get_runstate_snapshot(struct vcpu_runstate_info *res) +{ + u64 state_time; + struct vcpu_runstate_info *state; + + BUG_ON(preemptible()); + + state = &__get_cpu_var(runstate); + + /* + * The runstate info is always updated by the hypervisor on + * the current CPU, so there's no need to use anything + * stronger than a compiler barrier when fetching it. + */ + do { + state_time = get64(&state->state_entry_time); + barrier(); + *res = *state; + barrier(); + } while (get64(&state->state_entry_time) != state_time); +} + +static void setup_runstate_info(int cpu) +{ + struct vcpu_register_runstate_memory_area area; + + area.addr.v = &per_cpu(runstate, cpu); + + if (HYPERVISOR_vcpu_op(VCPUOP_register_runstate_memory_area, + cpu, &area)) + BUG(); +} + +static void do_stolen_accounting(void) +{ + struct vcpu_runstate_info state; + struct vcpu_runstate_info *snap; + s64 blocked, runnable, offline, stolen; + cputime_t ticks; + + get_runstate_snapshot(&state); + + WARN_ON(state.state != RUNSTATE_running); + + snap = &__get_cpu_var(runstate_snapshot); + + /* work out how much time the VCPU has not been runn*ing* */ + blocked = state.time[RUNSTATE_blocked] - snap->time[RUNSTATE_blocked]; + runnable = state.time[RUNSTATE_runnable] - snap->time[RUNSTATE_runnable]; + offline = state.time[RUNSTATE_offline] - snap->time[RUNSTATE_offline]; + + *snap = state; + + /* Add the appropriate number of ticks of stolen time, + including any left-overs from last time. Passing NULL to + account_steal_time accounts the time as stolen. */ + stolen = runnable + offline + __get_cpu_var(residual_stolen); + + if (stolen < 0) + stolen = 0; + + ticks = 0; + while (stolen >= NS_PER_TICK) { + ticks++; + stolen -= NS_PER_TICK; + } + __get_cpu_var(residual_stolen) = stolen; + account_steal_time(NULL, ticks); + + /* Add the appropriate number of ticks of blocked time, + including any left-overs from last time. Passing idle to + account_steal_time accounts the time as idle/wait. */ + blocked += __get_cpu_var(residual_blocked); + + if (blocked < 0) + blocked = 0; + + ticks = 0; + while (blocked >= NS_PER_TICK) { + ticks++; + blocked -= NS_PER_TICK; + } + __get_cpu_var(residual_blocked) = blocked; + account_steal_time(idle_task(smp_processor_id()), ticks); +} + +/* + * Xen sched_clock implementation. Returns the number of unstolen + * nanoseconds, which is nanoseconds the VCPU spent in RUNNING+BLOCKED + * states. + */ +unsigned long long xen_sched_clock(void) +{ + struct vcpu_runstate_info state; + cycle_t now; + u64 ret; + s64 offset; + + /* + * Ideally sched_clock should be called on a per-cpu basis + * anyway, so preempt should already be disabled, but that's + * not current practice at the moment. + */ + preempt_disable(); + + now = xen_clocksource_read(); + + get_runstate_snapshot(&state); + + WARN_ON(state.state != RUNSTATE_running); + + offset = now - state.state_entry_time; + if (offset < 0) + offset = 0; + + ret = state.time[RUNSTATE_blocked] + + state.time[RUNSTATE_running] + + offset; + + preempt_enable(); + + return ret; +} + + +/* Get the CPU speed from Xen */ +unsigned long xen_cpu_khz(void) +{ + u64 cpu_khz = 1000000ULL << 32; + const struct vcpu_time_info *info = + &HYPERVISOR_shared_info->vcpu_info[0].time; + + do_div(cpu_khz, info->tsc_to_system_mul); + if (info->tsc_shift < 0) + cpu_khz <<= -info->tsc_shift; + else + cpu_khz >>= info->tsc_shift; + + return cpu_khz; +} + +/* + * Reads a consistent set of time-base values from Xen, into a shadow data + * area. + */ +static unsigned get_time_values_from_xen(void) +{ + struct vcpu_time_info *src; + struct shadow_time_info *dst; + + /* src is shared memory with the hypervisor, so we need to + make sure we get a consistent snapshot, even in the face of + being preempted. */ + src = &__get_cpu_var(xen_vcpu)->time; + dst = &__get_cpu_var(shadow_time); + + do { + dst->version = src->version; + rmb(); /* fetch version before data */ + dst->tsc_timestamp = src->tsc_timestamp; + dst->system_timestamp = src->system_time; + dst->tsc_to_nsec_mul = src->tsc_to_system_mul; + dst->tsc_shift = src->tsc_shift; + rmb(); /* test version after fetching data */ + } while ((src->version & 1) | (dst->version ^ src->version)); + + return dst->version; +} + +/* + * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction, + * yielding a 64-bit result. + */ +static inline u64 scale_delta(u64 delta, u32 mul_frac, int shift) +{ + u64 product; +#ifdef __i386__ + u32 tmp1, tmp2; +#endif + + if (shift < 0) + delta >>= -shift; + else + delta <<= shift; + +#ifdef __i386__ + __asm__ ( + "mul %5 ; " + "mov %4,%%eax ; " + "mov %%edx,%4 ; " + "mul %5 ; " + "xor %5,%5 ; " + "add %4,%%eax ; " + "adc %5,%%edx ; " + : "=A" (product), "=r" (tmp1), "=r" (tmp2) + : "a" ((u32)delta), "1" ((u32)(delta >> 32)), "2" (mul_frac) ); +#elif __x86_64__ + __asm__ ( + "mul %%rdx ; shrd $32,%%rdx,%%rax" + : "=a" (product) : "0" (delta), "d" ((u64)mul_frac) ); +#else +#error implement me! +#endif + + return product; +} + +static u64 get_nsec_offset(struct shadow_time_info *shadow) +{ + u64 now, delta; + now = native_read_tsc(); + delta = now - shadow->tsc_timestamp; + return scale_delta(delta, shadow->tsc_to_nsec_mul, shadow->tsc_shift); +} + +static cycle_t xen_clocksource_read(void) +{ + struct shadow_time_info *shadow = &get_cpu_var(shadow_time); + cycle_t ret; + unsigned version; + + do { + version = get_time_values_from_xen(); + barrier(); + ret = shadow->system_timestamp + get_nsec_offset(shadow); + barrier(); + } while (version != __get_cpu_var(xen_vcpu)->time.version); + + put_cpu_var(shadow_time); + + return ret; +} + +static void xen_read_wallclock(struct timespec *ts) +{ + const struct shared_info *s = HYPERVISOR_shared_info; + u32 version; + u64 delta; + struct timespec now; + + /* get wallclock at system boot */ + do { + version = s->wc_version; + rmb(); /* fetch version before time */ + now.tv_sec = s->wc_sec; + now.tv_nsec = s->wc_nsec; + rmb(); /* fetch time before checking version */ + } while ((s->wc_version & 1) | (version ^ s->wc_version)); + + delta = xen_clocksource_read(); /* time since system boot */ + delta += now.tv_sec * (u64)NSEC_PER_SEC + now.tv_nsec; + + now.tv_nsec = do_div(delta, NSEC_PER_SEC); + now.tv_sec = delta; + + set_normalized_timespec(ts, now.tv_sec, now.tv_nsec); +} + +unsigned long xen_get_wallclock(void) +{ + struct timespec ts; + + xen_read_wallclock(&ts); + + return ts.tv_sec; +} + +int xen_set_wallclock(unsigned long now) +{ + /* do nothing for domU */ + return -1; +} + +static struct clocksource xen_clocksource __read_mostly = { + .name = "xen", + .rating = 400, + .read = xen_clocksource_read, + .mask = ~0, + .mult = 1<<XEN_SHIFT, /* time directly in nanoseconds */ + .shift = XEN_SHIFT, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +/* + Xen clockevent implementation + + Xen has two clockevent implementations: + + The old timer_op one works with all released versions of Xen prior + to version 3.0.4. This version of the hypervisor provides a + single-shot timer with nanosecond resolution. However, sharing the + same event channel is a 100Hz tick which is delivered while the + vcpu is running. We don't care about or use this tick, but it will + cause the core time code to think the timer fired too soon, and + will end up resetting it each time. It could be filtered, but + doing so has complications when the ktime clocksource is not yet + the xen clocksource (ie, at boot time). + + The new vcpu_op-based timer interface allows the tick timer period + to be changed or turned off. The tick timer is not useful as a + periodic timer because events are only delivered to running vcpus. + The one-shot timer can report when a timeout is in the past, so + set_next_event is capable of returning -ETIME when appropriate. + This interface is used when available. +*/ + + +/* + Get a hypervisor absolute time. In theory we could maintain an + offset between the kernel's time and the hypervisor's time, and + apply that to a kernel's absolute timeout. Unfortunately the + hypervisor and kernel times can drift even if the kernel is using + the Xen clocksource, because ntp can warp the kernel's clocksource. +*/ +static s64 get_abs_timeout(unsigned long delta) +{ + return xen_clocksource_read() + delta; +} + +static void xen_timerop_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + /* unsupported */ + WARN_ON(1); + break; + + case CLOCK_EVT_MODE_ONESHOT: + break; + + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + HYPERVISOR_set_timer_op(0); /* cancel timeout */ + break; + } +} + +static int xen_timerop_set_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + WARN_ON(evt->mode != CLOCK_EVT_MODE_ONESHOT); + + if (HYPERVISOR_set_timer_op(get_abs_timeout(delta)) < 0) + BUG(); + + /* We may have missed the deadline, but there's no real way of + knowing for sure. If the event was in the past, then we'll + get an immediate interrupt. */ + + return 0; +} + +static const struct clock_event_device xen_timerop_clockevent = { + .name = "xen", + .features = CLOCK_EVT_FEAT_ONESHOT, + + .max_delta_ns = 0xffffffff, + .min_delta_ns = TIMER_SLOP, + + .mult = 1, + .shift = 0, + .rating = 500, + + .set_mode = xen_timerop_set_mode, + .set_next_event = xen_timerop_set_next_event, +}; + + + +static void xen_vcpuop_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + int cpu = smp_processor_id(); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + WARN_ON(1); /* unsupported */ + break; + + case CLOCK_EVT_MODE_ONESHOT: + if (HYPERVISOR_vcpu_op(VCPUOP_stop_periodic_timer, cpu, NULL)) + BUG(); + break; + + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + if (HYPERVISOR_vcpu_op(VCPUOP_stop_singleshot_timer, cpu, NULL) || + HYPERVISOR_vcpu_op(VCPUOP_stop_periodic_timer, cpu, NULL)) + BUG(); + break; + } +} + +static int xen_vcpuop_set_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + int cpu = smp_processor_id(); + struct vcpu_set_singleshot_timer single; + int ret; + + WARN_ON(evt->mode != CLOCK_EVT_MODE_ONESHOT); + + single.timeout_abs_ns = get_abs_timeout(delta); + single.flags = VCPU_SSHOTTMR_future; + + ret = HYPERVISOR_vcpu_op(VCPUOP_set_singleshot_timer, cpu, &single); + + BUG_ON(ret != 0 && ret != -ETIME); + + return ret; +} + +static const struct clock_event_device xen_vcpuop_clockevent = { + .name = "xen", + .features = CLOCK_EVT_FEAT_ONESHOT, + + .max_delta_ns = 0xffffffff, + .min_delta_ns = TIMER_SLOP, + + .mult = 1, + .shift = 0, + .rating = 500, + + .set_mode = xen_vcpuop_set_mode, + .set_next_event = xen_vcpuop_set_next_event, +}; + +static const struct clock_event_device *xen_clockevent = + &xen_timerop_clockevent; +static DEFINE_PER_CPU(struct clock_event_device, xen_clock_events); + +static irqreturn_t xen_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = &__get_cpu_var(xen_clock_events); + irqreturn_t ret; + + ret = IRQ_NONE; + if (evt->event_handler) { + evt->event_handler(evt); + ret = IRQ_HANDLED; + } + + do_stolen_accounting(); + + return ret; +} + +void xen_setup_timer(int cpu) +{ + const char *name; + struct clock_event_device *evt; + int irq; + + printk(KERN_INFO "installing Xen timer for CPU %d\n", cpu); + + name = kasprintf(GFP_KERNEL, "timer%d", cpu); + if (!name) + name = "<timer kasprintf failed>"; + + irq = bind_virq_to_irqhandler(VIRQ_TIMER, cpu, xen_timer_interrupt, + IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING, + name, NULL); + + evt = &per_cpu(xen_clock_events, cpu); + memcpy(evt, xen_clockevent, sizeof(*evt)); + + evt->cpumask = cpumask_of_cpu(cpu); + evt->irq = irq; + + setup_runstate_info(cpu); +} + +void xen_setup_cpu_clockevents(void) +{ + BUG_ON(preemptible()); + + clockevents_register_device(&__get_cpu_var(xen_clock_events)); +} + +__init void xen_time_init(void) +{ + int cpu = smp_processor_id(); + + get_time_values_from_xen(); + + clocksource_register(&xen_clocksource); + + if (HYPERVISOR_vcpu_op(VCPUOP_stop_periodic_timer, cpu, NULL) == 0) { + /* Successfully turned off 100Hz tick, so we have the + vcpuop-based timer interface */ + printk(KERN_DEBUG "Xen: using vcpuop timer interface\n"); + xen_clockevent = &xen_vcpuop_clockevent; + } + + /* Set initial system time with full resolution */ + xen_read_wallclock(&xtime); + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); + + tsc_disable = 0; + + xen_setup_timer(cpu); + xen_setup_cpu_clockevents(); +} diff --git a/arch/i386/xen/xen-asm.S b/arch/i386/xen/xen-asm.S new file mode 100644 index 0000000..1a43b60 --- /dev/null +++ b/arch/i386/xen/xen-asm.S @@ -0,0 +1,291 @@ +/* + Asm versions of Xen pv-ops, suitable for either direct use or inlining. + The inline versions are the same as the direct-use versions, with the + pre- and post-amble chopped off. + + This code is encoded for size rather than absolute efficiency, + with a view to being able to inline as much as possible. + + We only bother with direct forms (ie, vcpu in pda) of the operations + here; the indirect forms are better handled in C, since they're + generally too large to inline anyway. + */ + +#include <linux/linkage.h> + +#include <asm/asm-offsets.h> +#include <asm/thread_info.h> +#include <asm/percpu.h> +#include <asm/processor-flags.h> +#include <asm/segment.h> + +#include <xen/interface/xen.h> + +#define RELOC(x, v) .globl x##_reloc; x##_reloc=v +#define ENDPATCH(x) .globl x##_end; x##_end=. + +/* Pseudo-flag used for virtual NMI, which we don't implement yet */ +#define XEN_EFLAGS_NMI 0x80000000 + +/* + Enable events. This clears the event mask and tests the pending + event status with one and operation. If there are pending + events, then enter the hypervisor to get them handled. + */ +ENTRY(xen_irq_enable_direct) + /* Clear mask and test pending */ + andw $0x00ff, PER_CPU_VAR(xen_vcpu_info)+XEN_vcpu_info_pending + /* Preempt here doesn't matter because that will deal with + any pending interrupts. The pending check may end up being + run on the wrong CPU, but that doesn't hurt. */ + jz 1f +2: call check_events +1: +ENDPATCH(xen_irq_enable_direct) + ret + ENDPROC(xen_irq_enable_direct) + RELOC(xen_irq_enable_direct, 2b+1) + + +/* + Disabling events is simply a matter of making the event mask + non-zero. + */ +ENTRY(xen_irq_disable_direct) + movb $1, PER_CPU_VAR(xen_vcpu_info)+XEN_vcpu_info_mask +ENDPATCH(xen_irq_disable_direct) + ret + ENDPROC(xen_irq_disable_direct) + RELOC(xen_irq_disable_direct, 0) + +/* + (xen_)save_fl is used to get the current interrupt enable status. + Callers expect the status to be in X86_EFLAGS_IF, and other bits + may be set in the return value. We take advantage of this by + making sure that X86_EFLAGS_IF has the right value (and other bits + in that byte are 0), but other bits in the return value are + undefined. We need to toggle the state of the bit, because + Xen and x86 use opposite senses (mask vs enable). + */ +ENTRY(xen_save_fl_direct) + testb $0xff, PER_CPU_VAR(xen_vcpu_info)+XEN_vcpu_info_mask + setz %ah + addb %ah,%ah +ENDPATCH(xen_save_fl_direct) + ret + ENDPROC(xen_save_fl_direct) + RELOC(xen_save_fl_direct, 0) + + +/* + In principle the caller should be passing us a value return + from xen_save_fl_direct, but for robustness sake we test only + the X86_EFLAGS_IF flag rather than the whole byte. After + setting the interrupt mask state, it checks for unmasked + pending events and enters the hypervisor to get them delivered + if so. + */ +ENTRY(xen_restore_fl_direct) + testb $X86_EFLAGS_IF>>8, %ah + setz PER_CPU_VAR(xen_vcpu_info)+XEN_vcpu_info_mask + /* Preempt here doesn't matter because that will deal with + any pending interrupts. The pending check may end up being + run on the wrong CPU, but that doesn't hurt. */ + + /* check for unmasked and pending */ + cmpw $0x0001, PER_CPU_VAR(xen_vcpu_info)+XEN_vcpu_info_pending + jz 1f +2: call check_events +1: +ENDPATCH(xen_restore_fl_direct) + ret + ENDPROC(xen_restore_fl_direct) + RELOC(xen_restore_fl_direct, 2b+1) + +/* + This is run where a normal iret would be run, with the same stack setup: + 8: eflags + 4: cs + esp-> 0: eip + + This attempts to make sure that any pending events are dealt + with on return to usermode, but there is a small window in + which an event can happen just before entering usermode. If + the nested interrupt ends up setting one of the TIF_WORK_MASK + pending work flags, they will not be tested again before + returning to usermode. This means that a process can end up + with pending work, which will be unprocessed until the process + enters and leaves the kernel again, which could be an + unbounded amount of time. This means that a pending signal or + reschedule event could be indefinitely delayed. + + The fix is to notice a nested interrupt in the critical + window, and if one occurs, then fold the nested interrupt into + the current interrupt stack frame, and re-process it + iteratively rather than recursively. This means that it will + exit via the normal path, and all pending work will be dealt + with appropriately. + + Because the nested interrupt handler needs to deal with the + current stack state in whatever form its in, we keep things + simple by only using a single register which is pushed/popped + on the stack. + + Non-direct iret could be done in the same way, but it would + require an annoying amount of code duplication. We'll assume + that direct mode will be the common case once the hypervisor + support becomes commonplace. + */ +ENTRY(xen_iret_direct) + /* test eflags for special cases */ + testl $(X86_EFLAGS_VM | XEN_EFLAGS_NMI), 8(%esp) + jnz hyper_iret + + push %eax + ESP_OFFSET=4 # bytes pushed onto stack + + /* Store vcpu_info pointer for easy access. Do it this + way to avoid having to reload %fs */ +#ifdef CONFIG_SMP + GET_THREAD_INFO(%eax) + movl TI_cpu(%eax),%eax + movl __per_cpu_offset(,%eax,4),%eax + lea per_cpu__xen_vcpu_info(%eax),%eax +#else + movl $per_cpu__xen_vcpu_info, %eax +#endif + + /* check IF state we're restoring */ + testb $X86_EFLAGS_IF>>8, 8+1+ESP_OFFSET(%esp) + + /* Maybe enable events. Once this happens we could get a + recursive event, so the critical region starts immediately + afterwards. However, if that happens we don't end up + resuming the code, so we don't have to be worried about + being preempted to another CPU. */ + setz XEN_vcpu_info_mask(%eax) +xen_iret_start_crit: + + /* check for unmasked and pending */ + cmpw $0x0001, XEN_vcpu_info_pending(%eax) + + /* If there's something pending, mask events again so we + can jump back into xen_hypervisor_callback */ + sete XEN_vcpu_info_mask(%eax) + + popl %eax + + /* From this point on the registers are restored and the stack + updated, so we don't need to worry about it if we're preempted */ +iret_restore_end: + + /* Jump to hypervisor_callback after fixing up the stack. + Events are masked, so jumping out of the critical + region is OK. */ + je xen_hypervisor_callback + + iret +xen_iret_end_crit: + +hyper_iret: + /* put this out of line since its very rarely used */ + jmp hypercall_page + __HYPERVISOR_iret * 32 + + .globl xen_iret_start_crit, xen_iret_end_crit + +/* + This is called by xen_hypervisor_callback in entry.S when it sees + that the EIP at the time of interrupt was between xen_iret_start_crit + and xen_iret_end_crit. We're passed the EIP in %eax so we can do + a more refined determination of what to do. + + The stack format at this point is: + ---------------- + ss : (ss/esp may be present if we came from usermode) + esp : + eflags } outer exception info + cs } + eip } + ---------------- <- edi (copy dest) + eax : outer eax if it hasn't been restored + ---------------- + eflags } nested exception info + cs } (no ss/esp because we're nested + eip } from the same ring) + orig_eax }<- esi (copy src) + - - - - - - - - + fs } + es } + ds } SAVE_ALL state + eax } + : : + ebx } + ---------------- + return addr <- esp + ---------------- + + In order to deliver the nested exception properly, we need to shift + everything from the return addr up to the error code so it + sits just under the outer exception info. This means that when we + handle the exception, we do it in the context of the outer exception + rather than starting a new one. + + The only caveat is that if the outer eax hasn't been + restored yet (ie, it's still on stack), we need to insert + its value into the SAVE_ALL state before going on, since + it's usermode state which we eventually need to restore. + */ +ENTRY(xen_iret_crit_fixup) + /* offsets +4 for return address */ + + /* + Paranoia: Make sure we're really coming from userspace. + One could imagine a case where userspace jumps into the + critical range address, but just before the CPU delivers a GP, + it decides to deliver an interrupt instead. Unlikely? + Definitely. Easy to avoid? Yes. The Intel documents + explicitly say that the reported EIP for a bad jump is the + jump instruction itself, not the destination, but some virtual + environments get this wrong. + */ + movl PT_CS+4(%esp), %ecx + andl $SEGMENT_RPL_MASK, %ecx + cmpl $USER_RPL, %ecx + je 2f + + lea PT_ORIG_EAX+4(%esp), %esi + lea PT_EFLAGS+4(%esp), %edi + + /* If eip is before iret_restore_end then stack + hasn't been restored yet. */ + cmp $iret_restore_end, %eax + jae 1f + + movl 0+4(%edi),%eax /* copy EAX */ + movl %eax, PT_EAX+4(%esp) + + lea ESP_OFFSET(%edi),%edi /* move dest up over saved regs */ + + /* set up the copy */ +1: std + mov $(PT_EIP+4) / 4, %ecx /* copy ret+saved regs up to orig_eax */ + rep movsl + cld + + lea 4(%edi),%esp /* point esp to new frame */ +2: ret + + +/* + Force an event check by making a hypercall, + but preserve regs before making the call. + */ +check_events: + push %eax + push %ecx + push %edx + call force_evtchn_callback + pop %edx + pop %ecx + pop %eax + ret diff --git a/arch/i386/xen/xen-head.S b/arch/i386/xen/xen-head.S new file mode 100644 index 0000000..2998d55 --- /dev/null +++ b/arch/i386/xen/xen-head.S @@ -0,0 +1,36 @@ +/* Xen-specific pieces of head.S, intended to be included in the right + place in head.S */ + +#ifdef CONFIG_XEN + +#include <linux/elfnote.h> +#include <asm/boot.h> +#include <xen/interface/elfnote.h> + +ENTRY(startup_xen) + movl %esi,xen_start_info + cld + movl $(init_thread_union+THREAD_SIZE),%esp + jmp xen_start_kernel + +.pushsection ".bss.page_aligned" + .align PAGE_SIZE_asm +ENTRY(hypercall_page) + .skip 0x1000 +.popsection + + ELFNOTE(Xen, XEN_ELFNOTE_GUEST_OS, .asciz "linux") + ELFNOTE(Xen, XEN_ELFNOTE_GUEST_VERSION, .asciz "2.6") + ELFNOTE(Xen, XEN_ELFNOTE_XEN_VERSION, .asciz "xen-3.0") + ELFNOTE(Xen, XEN_ELFNOTE_VIRT_BASE, .long __PAGE_OFFSET) + ELFNOTE(Xen, XEN_ELFNOTE_ENTRY, .long startup_xen) + ELFNOTE(Xen, XEN_ELFNOTE_HYPERCALL_PAGE, .long hypercall_page) + ELFNOTE(Xen, XEN_ELFNOTE_FEATURES, .asciz "!writable_page_tables|pae_pgdir_above_4gb") +#ifdef CONFIG_X86_PAE + ELFNOTE(Xen, XEN_ELFNOTE_PAE_MODE, .asciz "yes") +#else + ELFNOTE(Xen, XEN_ELFNOTE_PAE_MODE, .asciz "no") +#endif + ELFNOTE(Xen, XEN_ELFNOTE_LOADER, .asciz "generic") + +#endif /*CONFIG_XEN */ diff --git a/arch/i386/xen/xen-ops.h b/arch/i386/xen/xen-ops.h new file mode 100644 index 0000000..b9aaea4 --- /dev/null +++ b/arch/i386/xen/xen-ops.h @@ -0,0 +1,71 @@ +#ifndef XEN_OPS_H +#define XEN_OPS_H + +#include <linux/init.h> + +/* These are code, but not functions. Defined in entry.S */ +extern const char xen_hypervisor_callback[]; +extern const char xen_failsafe_callback[]; + +void xen_copy_trap_info(struct trap_info *traps); + +DECLARE_PER_CPU(struct vcpu_info *, xen_vcpu); +DECLARE_PER_CPU(unsigned long, xen_cr3); + +extern struct start_info *xen_start_info; +extern struct shared_info *HYPERVISOR_shared_info; + +char * __init xen_memory_setup(void); +void __init xen_arch_setup(void); +void __init xen_init_IRQ(void); + +void xen_setup_timer(int cpu); +void xen_setup_cpu_clockevents(void); +unsigned long xen_cpu_khz(void); +void __init xen_time_init(void); +unsigned long xen_get_wallclock(void); +int xen_set_wallclock(unsigned long time); +unsigned long long xen_sched_clock(void); + +void xen_mark_init_mm_pinned(void); + +DECLARE_PER_CPU(enum paravirt_lazy_mode, xen_lazy_mode); + +static inline unsigned xen_get_lazy_mode(void) +{ + return x86_read_percpu(xen_lazy_mode); +} + +void __init xen_fill_possible_map(void); + +void __init xen_setup_vcpu_info_placement(void); +void xen_smp_prepare_boot_cpu(void); +void xen_smp_prepare_cpus(unsigned int max_cpus); +int xen_cpu_up(unsigned int cpu); +void xen_smp_cpus_done(unsigned int max_cpus); + +void xen_smp_send_stop(void); +void xen_smp_send_reschedule(int cpu); +int xen_smp_call_function (void (*func) (void *info), void *info, int nonatomic, + int wait); +int xen_smp_call_function_single(int cpu, void (*func) (void *info), void *info, + int nonatomic, int wait); + +int xen_smp_call_function_mask(cpumask_t mask, void (*func)(void *), + void *info, int wait); + + +/* Declare an asm function, along with symbols needed to make it + inlineable */ +#define DECL_ASM(ret, name, ...) \ + ret name(__VA_ARGS__); \ + extern char name##_end[]; \ + extern char name##_reloc[] \ + +DECL_ASM(void, xen_irq_enable_direct, void); +DECL_ASM(void, xen_irq_disable_direct, void); +DECL_ASM(unsigned long, xen_save_fl_direct, void); +DECL_ASM(void, xen_restore_fl_direct, unsigned long); + +void xen_iret_direct(void); +#endif /* XEN_OPS_H */ diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index faf5ef3..94b4a02 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c @@ -156,11 +156,14 @@ static DEVICE_ATTR(devspec, S_IRUGO, pci_show_devspec, NULL); #endif /* CONFIG_PPC_OF */ /* Add sysfs properties */ -void pcibios_add_platform_entries(struct pci_dev *pdev) +int pcibios_add_platform_entries(struct pci_dev *pdev) { #ifdef CONFIG_PPC_OF - device_create_file(&pdev->dev, &dev_attr_devspec); + return device_create_file(&pdev->dev, &dev_attr_devspec); +#else + return 0; #endif /* CONFIG_PPC_OF */ + } char __init *pcibios_setup(char *str) diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c index bc43bba..6018178 100644 --- a/arch/powerpc/kernel/setup_64.c +++ b/arch/powerpc/kernel/setup_64.c @@ -350,11 +350,13 @@ void __init setup_system(void) { DBG(" -> setup_system()\n"); - /* Apply CPUs-specific fixups to kernel text (nop out sections - * not relevant to this CPU) + /* Apply the CPUs-specific and firmware specific fixups to kernel + * text (nop out sections not relevant to this CPU or this firmware) */ do_feature_fixups(cur_cpu_spec->cpu_features, &__start___ftr_fixup, &__stop___ftr_fixup); + do_feature_fixups(powerpc_firmware_features, + &__start___fw_ftr_fixup, &__stop___fw_ftr_fixup); /* * Unflatten the device-tree passed by prom_init or kexec @@ -392,12 +394,6 @@ void __init setup_system(void) if (ppc_md.init_early) ppc_md.init_early(); - /* Apply firmware specific fixups to kernel text (nop out - * sections not relevant to this firmware) - */ - do_feature_fixups(powerpc_firmware_features, - &__start___fw_ftr_fixup, &__stop___fw_ftr_fixup); - /* * We can discover serial ports now since the above did setup the * hash table management for us, thus ioremap works. We do that early diff --git a/arch/powerpc/kernel/sys_ppc32.c b/arch/powerpc/kernel/sys_ppc32.c index b42cbf1..bd85b5f 100644 --- a/arch/powerpc/kernel/sys_ppc32.c +++ b/arch/powerpc/kernel/sys_ppc32.c @@ -773,6 +773,13 @@ asmlinkage int compat_sys_truncate64(const char __user * path, u32 reg4, return sys_truncate(path, (high << 32) | low); } +asmlinkage long compat_sys_fallocate(int fd, int mode, u32 offhi, u32 offlo, + u32 lenhi, u32 lenlo) +{ + return sys_fallocate(fd, mode, ((loff_t)offhi << 32) | offlo, + ((loff_t)lenhi << 32) | lenlo); +} + asmlinkage int compat_sys_ftruncate64(unsigned int fd, u32 reg4, unsigned long high, unsigned long low) { diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 73df711..603d83a 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -21,6 +21,9 @@ config GENERIC_ISA_DMA bool default y +config ARCH_NO_VIRT_TO_BUS + def_bool y + source "init/Kconfig" menu "General machine setup" diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig index b84b6af..df6ee71 100644 --- a/arch/sparc64/Kconfig +++ b/arch/sparc64/Kconfig @@ -62,6 +62,9 @@ config AUDIT_ARCH bool default y +config ARCH_NO_VIRT_TO_BUS + def_bool y + choice prompt "Kernel page size" default SPARC64_PAGE_SIZE_8KB diff --git a/arch/sparc64/kernel/ds.c b/arch/sparc64/kernel/ds.c index ba01533..fa1f04d 100644 --- a/arch/sparc64/kernel/ds.c +++ b/arch/sparc64/kernel/ds.c @@ -1013,6 +1013,19 @@ static void ds_up(struct ds_info *dp) dp->hs_state = DS_HS_START; } +static void ds_reset(struct ds_info *dp) +{ + int i; + + dp->hs_state = 0; + + for (i = 0; i < ARRAY_SIZE(ds_states); i++) { + struct ds_cap_state *cp = &ds_states[i]; + + cp->state = CAP_STATE_UNKNOWN; + } +} + static void ds_event(void *arg, int event) { struct ds_info *dp = arg; @@ -1028,6 +1041,12 @@ static void ds_event(void *arg, int event) return; } + if (event == LDC_EVENT_RESET) { + ds_reset(dp); + spin_unlock_irqrestore(&ds_lock, flags); + return; + } + if (event != LDC_EVENT_DATA_READY) { printk(KERN_WARNING PFX "Unexpected LDC event %d\n", event); spin_unlock_irqrestore(&ds_lock, flags); diff --git a/arch/sparc64/kernel/mdesc.c b/arch/sparc64/kernel/mdesc.c index de5310f..302ba5e 100644 --- a/arch/sparc64/kernel/mdesc.c +++ b/arch/sparc64/kernel/mdesc.c @@ -137,7 +137,7 @@ static struct mdesc_handle *mdesc_kmalloc(unsigned int mdesc_size) sizeof(struct mdesc_hdr) + mdesc_size); - base = kmalloc(handle_size + 15, GFP_KERNEL); + base = kmalloc(handle_size + 15, GFP_KERNEL | __GFP_NOFAIL); if (base) { struct mdesc_handle *hp; unsigned long addr; @@ -214,18 +214,83 @@ void mdesc_release(struct mdesc_handle *hp) } EXPORT_SYMBOL(mdesc_release); +static DEFINE_MUTEX(mdesc_mutex); +static struct mdesc_notifier_client *client_list; + +void mdesc_register_notifier(struct mdesc_notifier_client *client) +{ + u64 node; + + mutex_lock(&mdesc_mutex); + client->next = client_list; + client_list = client; + + mdesc_for_each_node_by_name(cur_mdesc, node, client->node_name) + client->add(cur_mdesc, node); + + mutex_unlock(&mdesc_mutex); +} + +/* Run 'func' on nodes which are in A but not in B. */ +static void invoke_on_missing(const char *name, + struct mdesc_handle *a, + struct mdesc_handle *b, + void (*func)(struct mdesc_handle *, u64)) +{ + u64 node; + + mdesc_for_each_node_by_name(a, node, name) { + const u64 *id = mdesc_get_property(a, node, "id", NULL); + int found = 0; + u64 fnode; + + mdesc_for_each_node_by_name(b, fnode, name) { + const u64 *fid = mdesc_get_property(b, fnode, + "id", NULL); + + if (*id == *fid) { + found = 1; + break; + } + } + if (!found) + func(a, node); + } +} + +static void notify_one(struct mdesc_notifier_client *p, + struct mdesc_handle *old_hp, + struct mdesc_handle *new_hp) +{ + invoke_on_missing(p->node_name, old_hp, new_hp, p->remove); + invoke_on_missing(p->node_name, new_hp, old_hp, p->add); +} + +static void mdesc_notify_clients(struct mdesc_handle *old_hp, + struct mdesc_handle *new_hp) +{ + struct mdesc_notifier_client *p = client_list; + + while (p) { + notify_one(p, old_hp, new_hp); + p = p->next; + } +} + void mdesc_update(void) { unsigned long len, real_len, status; struct mdesc_handle *hp, *orig_hp; unsigned long flags; + mutex_lock(&mdesc_mutex); + (void) sun4v_mach_desc(0UL, 0UL, &len); hp = mdesc_alloc(len, &kmalloc_mdesc_memops); if (!hp) { printk(KERN_ERR "MD: mdesc alloc fails\n"); - return; + goto out; } status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len); @@ -234,18 +299,25 @@ void mdesc_update(void) status); atomic_dec(&hp->refcnt); mdesc_free(hp); - return; + goto out; } spin_lock_irqsave(&mdesc_lock, flags); orig_hp = cur_mdesc; cur_mdesc = hp; + spin_unlock_irqrestore(&mdesc_lock, flags); + mdesc_notify_clients(orig_hp, hp); + + spin_lock_irqsave(&mdesc_lock, flags); if (atomic_dec_and_test(&orig_hp->refcnt)) mdesc_free(orig_hp); else list_add(&orig_hp->list, &mdesc_zombie_list); spin_unlock_irqrestore(&mdesc_lock, flags); + +out: + mutex_unlock(&mdesc_mutex); } static struct mdesc_elem *node_block(struct mdesc_hdr *mdesc) diff --git a/arch/sparc64/kernel/vio.c b/arch/sparc64/kernel/vio.c index 49569b4..8d3cc4f 100644 --- a/arch/sparc64/kernel/vio.c +++ b/arch/sparc64/kernel/vio.c @@ -201,10 +201,11 @@ static void vio_fill_channel_info(struct mdesc_handle *hp, u64 mp, static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, struct device *parent) { - const char *type, *compat; + const char *type, *compat, *bus_id_name; struct device_node *dp; struct vio_dev *vdev; int err, tlen, clen; + const u64 *id; type = mdesc_get_property(hp, mp, "device-type", &tlen); if (!type) { @@ -220,6 +221,16 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, return NULL; } + bus_id_name = type; + if (!strcmp(type, "domain-services-port")) + bus_id_name = "ds"; + + if (strlen(bus_id_name) >= KOBJ_NAME_LEN - 4) { + printk(KERN_ERR "VIO: bus_id_name [%s] is too long.\n", + bus_id_name); + return NULL; + } + compat = mdesc_get_property(hp, mp, "device-type", &clen); if (!compat) { clen = 0; @@ -249,7 +260,14 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, vio_fill_channel_info(hp, mp, vdev); - snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%lx", mp); + id = mdesc_get_property(hp, mp, "id", NULL); + if (!id) + snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%s", + bus_id_name); + else + snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%s-%lu", + bus_id_name, *id); + vdev->dev.parent = parent; vdev->dev.bus = &vio_bus_type; vdev->dev.release = vio_dev_release; @@ -269,6 +287,8 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, } vdev->dp = dp; + printk(KERN_ERR "VIO: Adding device %s\n", vdev->dev.bus_id); + err = device_register(&vdev->dev); if (err) { printk(KERN_ERR "VIO: Could not register device %s, err=%d\n", @@ -283,46 +303,46 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp, return vdev; } -static void walk_tree(struct mdesc_handle *hp, u64 n, struct vio_dev *parent) +static void vio_add(struct mdesc_handle *hp, u64 node) { - u64 a; - - mdesc_for_each_arc(a, hp, n, MDESC_ARC_TYPE_FWD) { - struct vio_dev *vdev; - u64 target; - - target = mdesc_arc_target(hp, a); - vdev = vio_create_one(hp, target, &parent->dev); - if (vdev) - walk_tree(hp, target, vdev); - } + (void) vio_create_one(hp, node, &root_vdev->dev); } -static void create_devices(struct mdesc_handle *hp, u64 root) +static int vio_md_node_match(struct device *dev, void *arg) { - u64 mp; + struct vio_dev *vdev = to_vio_dev(dev); - root_vdev = vio_create_one(hp, root, NULL); - if (!root_vdev) { - printk(KERN_ERR "VIO: Coult not create root device.\n"); - return; - } + if (vdev->mp == (u64) arg) + return 1; - walk_tree(hp, root, root_vdev); + return 0; +} + +static void vio_remove(struct mdesc_handle *hp, u64 node) +{ + struct device *dev; - /* Domain services is odd as it doesn't sit underneath the - * channel-devices node, so we plug it in manually. - */ - mp = mdesc_node_by_name(hp, MDESC_NODE_NULL, "domain-services"); - if (mp != MDESC_NODE_NULL) { - struct vio_dev *parent = vio_create_one(hp, mp, - &root_vdev->dev); + dev = device_find_child(&root_vdev->dev, (void *) node, + vio_md_node_match); + if (dev) { + printk(KERN_INFO "VIO: Removing device %s\n", dev->bus_id); - if (parent) - walk_tree(hp, mp, parent); + device_unregister(dev); } } +static struct mdesc_notifier_client vio_device_notifier = { + .add = vio_add, + .remove = vio_remove, + .node_name = "virtual-device-port", +}; + +static struct mdesc_notifier_client vio_ds_notifier = { + .add = vio_add, + .remove = vio_remove, + .node_name = "domain-services-port", +}; + const char *channel_devices_node = "channel-devices"; const char *channel_devices_compat = "SUNW,sun4v-channel-devices"; const char *cfg_handle_prop = "cfg-handle"; @@ -381,11 +401,19 @@ static int __init vio_init(void) cdev_cfg_handle = *cfg_handle; - create_devices(hp, root); + root_vdev = vio_create_one(hp, root, NULL); + err = -ENODEV; + if (!root_vdev) { + printk(KERN_ERR "VIO: Coult not create root device.\n"); + goto out_release; + } + + mdesc_register_notifier(&vio_device_notifier); + mdesc_register_notifier(&vio_ds_notifier); mdesc_release(hp); - return 0; + return err; out_release: mdesc_release(hp); diff --git a/arch/sparc64/kernel/viohs.c b/arch/sparc64/kernel/viohs.c index 15613ad..09126fc 100644 --- a/arch/sparc64/kernel/viohs.c +++ b/arch/sparc64/kernel/viohs.c @@ -78,6 +78,24 @@ static int start_handshake(struct vio_driver_state *vio) return 0; } +static void flush_rx_dring(struct vio_driver_state *vio) +{ + struct vio_dring_state *dr; + u64 ident; + + BUG_ON(!(vio->dr_state & VIO_DR_STATE_RXREG)); + + dr = &vio->drings[VIO_DRIVER_RX_RING]; + ident = dr->ident; + + BUG_ON(!vio->desc_buf); + kfree(vio->desc_buf); + vio->desc_buf = NULL; + + memset(dr, 0, sizeof(*dr)); + dr->ident = ident; +} + void vio_link_state_change(struct vio_driver_state *vio, int event) { if (event == LDC_EVENT_UP) { @@ -98,6 +116,16 @@ void vio_link_state_change(struct vio_driver_state *vio, int event) break; } start_handshake(vio); + } else if (event == LDC_EVENT_RESET) { + vio->hs_state = VIO_HS_INVALID; + + if (vio->dr_state & VIO_DR_STATE_RXREG) + flush_rx_dring(vio); + + vio->dr_state = 0x00; + memset(&vio->ver, 0, sizeof(vio->ver)); + + ldc_disconnect(vio->lp); } } EXPORT_SYMBOL(vio_link_state_change); @@ -396,6 +424,8 @@ static int process_dreg_info(struct vio_driver_state *vio, if (vio->dr_state & VIO_DR_STATE_RXREG) goto send_nack; + BUG_ON(vio->desc_buf); + vio->desc_buf = kzalloc(pkt->descr_size, GFP_ATOMIC); if (!vio->desc_buf) goto send_nack; diff --git a/arch/x86_64/ia32/ia32entry.S b/arch/x86_64/ia32/ia32entry.S index 782dea8..3f66e970 100644 --- a/arch/x86_64/ia32/ia32entry.S +++ b/arch/x86_64/ia32/ia32entry.S @@ -719,4 +719,5 @@ ia32_sys_call_table: .quad compat_sys_signalfd .quad compat_sys_timerfd .quad sys_eventfd + .quad sys32_fallocate ia32_syscall_end: diff --git a/arch/x86_64/ia32/sys_ia32.c b/arch/x86_64/ia32/sys_ia32.c index 99a78a3..bee96d6 100644 --- a/arch/x86_64/ia32/sys_ia32.c +++ b/arch/x86_64/ia32/sys_ia32.c @@ -879,3 +879,11 @@ asmlinkage long sys32_fadvise64(int fd, unsigned offset_lo, unsigned offset_hi, return sys_fadvise64_64(fd, ((u64)offset_hi << 32) | offset_lo, len, advice); } + +asmlinkage long sys32_fallocate(int fd, int mode, unsigned offset_lo, + unsigned offset_hi, unsigned len_lo, + unsigned len_hi) +{ + return sys_fallocate(fd, mode, ((u64)offset_hi << 32) | offset_lo, + ((u64)len_hi << 32) | len_lo); +} diff --git a/arch/x86_64/kernel/early_printk.c b/arch/x86_64/kernel/early_printk.c index 296d2b0..fd9aff3 100644 --- a/arch/x86_64/kernel/early_printk.c +++ b/arch/x86_64/kernel/early_printk.c @@ -6,6 +6,7 @@ #include <asm/io.h> #include <asm/processor.h> #include <asm/fcntl.h> +#include <xen/hvc-console.h> /* Simple VGA output */ @@ -242,6 +243,10 @@ static int __init setup_early_printk(char *buf) simnow_init(buf + 6); early_console = &simnow_console; keep_early = 1; +#ifdef CONFIG_HVC_XEN + } else if (!strncmp(buf, "xen", 3)) { + early_console = &xenboot_console; +#endif } if (keep_early) diff --git a/arch/x86_64/kernel/mce.c b/arch/x86_64/kernel/mce.c index aa1d159..f3fb817 100644 --- a/arch/x86_64/kernel/mce.c +++ b/arch/x86_64/kernel/mce.c @@ -174,7 +174,7 @@ static void do_mce_trigger(void) if (events != atomic_read(&mce_logged) && trigger[0]) { /* Small race window, but should be harmless. */ atomic_set(&mce_logged, events); - call_usermodehelper(trigger, trigger_argv, NULL, -1); + call_usermodehelper(trigger, trigger_argv, NULL, UMH_NO_WAIT); } } diff --git a/arch/x86_64/kernel/ptrace.c b/arch/x86_64/kernel/ptrace.c index fa6775e..e83cc67 100644 --- a/arch/x86_64/kernel/ptrace.c +++ b/arch/x86_64/kernel/ptrace.c @@ -102,16 +102,25 @@ unsigned long convert_rip_to_linear(struct task_struct *child, struct pt_regs *r u32 *desc; unsigned long base; - down(&child->mm->context.sem); - desc = child->mm->context.ldt + (seg & ~7); - base = (desc[0] >> 16) | ((desc[1] & 0xff) << 16) | (desc[1] & 0xff000000); + seg &= ~7UL; - /* 16-bit code segment? */ - if (!((desc[1] >> 22) & 1)) - addr &= 0xffff; - addr += base; + down(&child->mm->context.sem); + if (unlikely((seg >> 3) >= child->mm->context.size)) + addr = -1L; /* bogus selector, access would fault */ + else { + desc = child->mm->context.ldt + seg; + base = ((desc[0] >> 16) | + ((desc[1] & 0xff) << 16) | + (desc[1] & 0xff000000)); + + /* 16-bit code segment? */ + if (!((desc[1] >> 22) & 1)) + addr &= 0xffff; + addr += base; + } up(&child->mm->context.sem); } + return addr; } diff --git a/drivers/Makefile b/drivers/Makefile index 503d825..6d9d7fa 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -15,6 +15,8 @@ obj-$(CONFIG_ACPI) += acpi/ obj-$(CONFIG_PNP) += pnp/ obj-$(CONFIG_ARM_AMBA) += amba/ +obj-$(CONFIG_XEN) += xen/ + # char/ comes before serial/ etc so that the VT console is the boot-time # default. obj-y += char/ diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 88a6fc7..58f1338 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -40,6 +40,7 @@ #include <linux/jiffies.h> #include <linux/kmod.h> #include <linux/seq_file.h> +#include <linux/reboot.h> #include <asm/uaccess.h> #include <acpi/acpi_bus.h> @@ -59,7 +60,6 @@ #define ACPI_THERMAL_NOTIFY_CRITICAL 0xF0 #define ACPI_THERMAL_NOTIFY_HOT 0xF1 #define ACPI_THERMAL_MODE_ACTIVE 0x00 -#define ACPI_THERMAL_PATH_POWEROFF "/sbin/poweroff" #define ACPI_THERMAL_MAX_ACTIVE 10 #define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65 @@ -419,26 +419,6 @@ static int acpi_thermal_get_devices(struct acpi_thermal *tz) return 0; } -static int acpi_thermal_call_usermode(char *path) -{ - char *argv[2] = { NULL, NULL }; - char *envp[3] = { NULL, NULL, NULL }; - - - if (!path) - return -EINVAL; - - argv[0] = path; - - /* minimal command environment */ - envp[0] = "HOME=/"; - envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; - - call_usermodehelper(argv[0], argv, envp, 0); - - return 0; -} - static int acpi_thermal_critical(struct acpi_thermal *tz) { if (!tz || !tz->trips.critical.flags.valid) @@ -456,7 +436,7 @@ static int acpi_thermal_critical(struct acpi_thermal *tz) acpi_bus_generate_event(tz->device, ACPI_THERMAL_NOTIFY_CRITICAL, tz->trips.critical.flags.enabled); - acpi_thermal_call_usermode(ACPI_THERMAL_PATH_POWEROFF); + orderly_poweroff(true); return 0; } diff --git a/drivers/atm/Kconfig b/drivers/atm/Kconfig index bb4ae628..bed9f58 100644 --- a/drivers/atm/Kconfig +++ b/drivers/atm/Kconfig @@ -172,7 +172,7 @@ config ATM_ZATM_DEBUG config ATM_NICSTAR tristate "IDT 77201 (NICStAR) (ForeRunnerLE)" - depends on PCI && !64BIT + depends on PCI && !64BIT && VIRT_TO_BUS help The NICStAR chipset family is used in a large number of ATM NICs for 25 and for 155 Mbps, including IDT cards and the Fore ForeRunnerLE diff --git a/drivers/atm/eni.c b/drivers/atm/eni.c index 77637e7..41b2204 100644 --- a/drivers/atm/eni.c +++ b/drivers/atm/eni.c @@ -1738,7 +1738,8 @@ static int __devinit eni_do_init(struct atm_dev *dev) printk(KERN_ERR KERN_ERR DEV_LABEL "(itf %d): bad " "magic - expected 0x%x, got 0x%x\n",dev->number, ENI155_MAGIC,(unsigned) readl(&eprom->magic)); - return -EINVAL; + error = -EINVAL; + goto unmap; } } eni_dev->phy = base+PHY_BASE; @@ -1765,17 +1766,27 @@ static int __devinit eni_do_init(struct atm_dev *dev) printk(")\n"); printk(KERN_ERR DEV_LABEL "(itf %d): ERROR - wrong id 0x%x\n", dev->number,(unsigned) eni_in(MID_RES_ID_MCON)); - return -EINVAL; + error = -EINVAL; + goto unmap; } error = eni_dev->asic ? get_esi_asic(dev) : get_esi_fpga(dev,base); - if (error) return error; + if (error) + goto unmap; for (i = 0; i < ESI_LEN; i++) printk("%s%02X",i ? "-" : "",dev->esi[i]); printk(")\n"); printk(KERN_NOTICE DEV_LABEL "(itf %d): %s,%s\n",dev->number, eni_in(MID_RES_ID_MCON) & 0x200 ? "ASIC" : "FPGA", media_name[eni_in(MID_RES_ID_MCON) & DAUGTHER_ID]); - return suni_init(dev); + + error = suni_init(dev); + if (error) + goto unmap; +out: + return error; +unmap: + iounmap(base); + goto out; } diff --git a/drivers/atm/firestream.c b/drivers/atm/firestream.c index 38b688f..737cea4 100644 --- a/drivers/atm/firestream.c +++ b/drivers/atm/firestream.c @@ -1710,7 +1710,7 @@ static int __devinit fs_init (struct fs_dev *dev) /* This bit is documented as "RESERVED" */ if (isr & ISR_INIT_ERR) { printk (KERN_ERR "Error initializing the FS... \n"); - return 1; + goto unmap; } if (isr & ISR_INIT) { fs_dprintk (FS_DEBUG_INIT, "Ha! Initialized OK!\n"); @@ -1723,7 +1723,7 @@ static int __devinit fs_init (struct fs_dev *dev) if (!to) { printk (KERN_ERR "timeout initializing the FS... \n"); - return 1; + goto unmap; } /* XXX fix for fs155 */ @@ -1803,7 +1803,7 @@ static int __devinit fs_init (struct fs_dev *dev) if (!dev->atm_vccs) { printk (KERN_WARNING "Couldn't allocate memory for VCC buffers. Woops!\n"); /* XXX Clean up..... */ - return 1; + goto unmap; } dev->tx_inuse = kzalloc (dev->nchannels / 8 /* bits/byte */ , GFP_KERNEL); @@ -1813,7 +1813,7 @@ static int __devinit fs_init (struct fs_dev *dev) if (!dev->tx_inuse) { printk (KERN_WARNING "Couldn't allocate memory for tx_inuse bits!\n"); /* XXX Clean up..... */ - return 1; + goto unmap; } /* -- RAS1 : FS155 and 50 differ. Default (0) should be OK for both */ /* -- RAS2 : FS50 only: Default is OK. */ @@ -1840,7 +1840,7 @@ static int __devinit fs_init (struct fs_dev *dev) if (request_irq (dev->irq, fs_irq, IRQF_SHARED, "firestream", dev)) { printk (KERN_WARNING "couldn't get irq %d for firestream.\n", pci_dev->irq); /* XXX undo all previous stuff... */ - return 1; + goto unmap; } fs_dprintk (FS_DEBUG_INIT, "Grabbed irq %d for dev at %p.\n", dev->irq, dev); @@ -1890,6 +1890,9 @@ static int __devinit fs_init (struct fs_dev *dev) func_exit (); return 0; +unmap: + iounmap(dev->base); + return 1; } static int __devinit firestream_init_one (struct pci_dev *pci_dev, @@ -2012,6 +2015,7 @@ static void __devexit firestream_remove_one (struct pci_dev *pdev) for (i=0;i < FS_NR_RX_QUEUES;i++) free_queue (dev, &dev->rx_rq[i]); + iounmap(dev->base); fs_dprintk (FS_DEBUG_ALLOC, "Free fs-dev: %p\n", dev); nxtdev = dev->next; kfree (dev); diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c index 8f995ce..f8b1700 100644 --- a/drivers/atm/idt77252.c +++ b/drivers/atm/idt77252.c @@ -65,7 +65,7 @@ static char const rcsid[] = static unsigned int vpibits = 1; -#define CONFIG_ATM_IDT77252_SEND_IDLE 1 +#define ATM_IDT77252_SEND_IDLE 1 /* @@ -3404,7 +3404,7 @@ init_card(struct atm_dev *dev) conf = SAR_CFG_TX_FIFO_SIZE_9 | /* Use maximum fifo size */ SAR_CFG_RXSTQ_SIZE_8k | /* Receive Status Queue is 8k */ SAR_CFG_IDLE_CLP | /* Set CLP on idle cells */ -#ifndef CONFIG_ATM_IDT77252_SEND_IDLE +#ifndef ATM_IDT77252_SEND_IDLE SAR_CFG_NO_IDLE | /* Do not send idle cells */ #endif 0; @@ -3541,7 +3541,7 @@ init_card(struct atm_dev *dev) printk("%s: Linkrate on ATM line : %u bit/s, %u cell/s.\n", card->name, linkrate, card->link_pcr); -#ifdef CONFIG_ATM_IDT77252_SEND_IDLE +#ifdef ATM_IDT77252_SEND_IDLE card->utopia_pcr = card->link_pcr; #else card->utopia_pcr = (160000000 / 8 / 54); diff --git a/drivers/atm/lanai.c b/drivers/atm/lanai.c index 0e2c1ae..55fd1b4 100644 --- a/drivers/atm/lanai.c +++ b/drivers/atm/lanai.c @@ -552,8 +552,8 @@ static inline void sram_write(const struct lanai_dev *lanai, writel(val, sram_addr(lanai, offset)); } -static int __init sram_test_word( - const struct lanai_dev *lanai, int offset, u32 pattern) +static int __devinit sram_test_word(const struct lanai_dev *lanai, + int offset, u32 pattern) { u32 readback; sram_write(lanai, pattern, offset); diff --git a/drivers/atm/nicstarmac.c b/drivers/atm/nicstarmac.c index 480947f..842e26c 100644 --- a/drivers/atm/nicstarmac.c +++ b/drivers/atm/nicstarmac.c @@ -134,7 +134,7 @@ nicstar_read_eprom_status( virt_addr_t base ) /* Send read instruction */ val = NICSTAR_REG_READ( base, NICSTAR_REG_GENERAL_PURPOSE ) & 0xFFFFFFF0; - for (i=0; i<sizeof rdsrtab/sizeof rdsrtab[0]; i++) + for (i=0; i<ARRAY_SIZE(rdsrtab); i++) { NICSTAR_REG_WRITE( base, NICSTAR_REG_GENERAL_PURPOSE, (val | rdsrtab[i]) ); diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 8f65b88..a4a3119 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -427,4 +427,13 @@ config XILINX_SYSACE help Include support for the Xilinx SystemACE CompactFlash interface +config XEN_BLKDEV_FRONTEND + tristate "Xen virtual block device support" + depends on XEN + default y + help + This driver implements the front-end of the Xen virtual + block device driver. It communicates with a back-end driver + in another domain which drives the actual block device. + endif # BLK_DEV diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 9ee08ab..3e31532 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -29,3 +29,4 @@ obj-$(CONFIG_VIODASD) += viodasd.o obj-$(CONFIG_BLK_DEV_SX8) += sx8.o obj-$(CONFIG_BLK_DEV_UB) += ub.o +obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += xen-blkfront.o diff --git a/drivers/block/sunvdc.c b/drivers/block/sunvdc.c index 0f5e3ca..2288b55 100644 --- a/drivers/block/sunvdc.c +++ b/drivers/block/sunvdc.c @@ -45,8 +45,6 @@ struct vdc_req_entry { struct vdc_port { struct vio_driver_state vio; - struct vdc *vp; - struct gendisk *disk; struct vdc_completion *cmp; @@ -72,8 +70,6 @@ struct vdc_port { struct vio_disk_geom geom; struct vio_disk_vtoc label; - - struct list_head list; }; static inline struct vdc_port *to_vdc_port(struct vio_driver_state *vio) @@ -81,15 +77,6 @@ static inline struct vdc_port *to_vdc_port(struct vio_driver_state *vio) return container_of(vio, struct vdc_port, vio); } -struct vdc { - /* Protects prot_list. */ - spinlock_t lock; - - struct vio_dev *dev; - - struct list_head port_list; -}; - /* Ordered from largest major to lowest */ static struct vio_version vdc_versions[] = { { .major = 1, .minor = 0 }, @@ -747,21 +734,23 @@ static struct vio_driver_ops vdc_vio_ops = { .handshake_complete = vdc_handshake_complete, }; +static void print_version(void) +{ + static int version_printed; + + if (version_printed++ == 0) + printk(KERN_INFO "%s", version); +} + static int __devinit vdc_port_probe(struct vio_dev *vdev, const struct vio_device_id *id) { struct mdesc_handle *hp; struct vdc_port *port; - unsigned long flags; - struct vdc *vp; const u64 *port_id; int err; - vp = dev_get_drvdata(vdev->dev.parent); - if (!vp) { - printk(KERN_ERR PFX "Cannot find port parent vdc.\n"); - return -ENODEV; - } + print_version(); hp = mdesc_grab(); @@ -783,7 +772,6 @@ static int __devinit vdc_port_probe(struct vio_dev *vdev, goto err_out_release_mdesc; } - port->vp = vp; port->dev_no = *port_id; if (port->dev_no >= 26) @@ -818,12 +806,6 @@ static int __devinit vdc_port_probe(struct vio_dev *vdev, if (err) goto err_out_free_tx_ring; - INIT_LIST_HEAD(&port->list); - - spin_lock_irqsave(&vp->lock, flags); - list_add(&port->list, &vp->port_list); - spin_unlock_irqrestore(&vp->lock, flags); - dev_set_drvdata(&vdev->dev, port); mdesc_release(hp); @@ -879,58 +861,6 @@ static struct vio_driver vdc_port_driver = { } }; -static int __devinit vdc_probe(struct vio_dev *vdev, - const struct vio_device_id *id) -{ - static int vdc_version_printed; - struct vdc *vp; - - if (vdc_version_printed++ == 0) - printk(KERN_INFO "%s", version); - - vp = kzalloc(sizeof(struct vdc), GFP_KERNEL); - if (!vp) - return -ENOMEM; - - spin_lock_init(&vp->lock); - vp->dev = vdev; - INIT_LIST_HEAD(&vp->port_list); - - dev_set_drvdata(&vdev->dev, vp); - - return 0; -} - -static int vdc_remove(struct vio_dev *vdev) -{ - - struct vdc *vp = dev_get_drvdata(&vdev->dev); - - if (vp) { - kfree(vp); - dev_set_drvdata(&vdev->dev, NULL); - } - return 0; -} - -static struct vio_device_id vdc_match[] = { - { - .type = "block", - }, - {}, -}; -MODULE_DEVICE_TABLE(vio, vdc_match); - -static struct vio_driver vdc_driver = { - .id_table = vdc_match, - .probe = vdc_probe, - .remove = vdc_remove, - .driver = { - .name = "vdc", - .owner = THIS_MODULE, - } -}; - static int __init vdc_init(void) { int err; @@ -940,19 +870,13 @@ static int __init vdc_init(void) goto out_err; vdc_major = err; - err = vio_register_driver(&vdc_driver); - if (err) - goto out_unregister_blkdev; err = vio_register_driver(&vdc_port_driver); if (err) - goto out_unregister_vdc; + goto out_unregister_blkdev; return 0; -out_unregister_vdc: - vio_unregister_driver(&vdc_driver); - out_unregister_blkdev: unregister_blkdev(vdc_major, VDCBLK_NAME); vdc_major = 0; @@ -964,7 +888,6 @@ out_err: static void __exit vdc_exit(void) { vio_unregister_driver(&vdc_port_driver); - vio_unregister_driver(&vdc_driver); unregister_blkdev(vdc_major, VDCBLK_NAME); } diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c new file mode 100644 index 0000000..6746c29 --- /dev/null +++ b/drivers/block/xen-blkfront.c @@ -0,0 +1,988 @@ +/* + * blkfront.c + * + * XenLinux virtual block device driver. + * + * Copyright (c) 2003-2004, Keir Fraser & Steve Hand + * Modifications by Mark A. Williamson are (c) Intel Research Cambridge + * Copyright (c) 2004, Christian Limpach + * Copyright (c) 2004, Andrew Warfield + * Copyright (c) 2005, Christopher Clark + * Copyright (c) 2005, XenSource Ltd + * + * 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; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <linux/interrupt.h> +#include <linux/blkdev.h> +#include <linux/module.h> + +#include <xen/xenbus.h> +#include <xen/grant_table.h> +#include <xen/events.h> +#include <xen/page.h> + +#include <xen/interface/grant_table.h> +#include <xen/interface/io/blkif.h> + +#include <asm/xen/hypervisor.h> + +enum blkif_state { + BLKIF_STATE_DISCONNECTED, + BLKIF_STATE_CONNECTED, + BLKIF_STATE_SUSPENDED, +}; + +struct blk_shadow { + struct blkif_request req; + unsigned long request; + unsigned long frame[BLKIF_MAX_SEGMENTS_PER_REQUEST]; +}; + +static struct block_device_operations xlvbd_block_fops; + +#define BLK_RING_SIZE __RING_SIZE((struct blkif_sring *)0, PAGE_SIZE) + +/* + * We have one of these per vbd, whether ide, scsi or 'other'. They + * hang in private_data off the gendisk structure. We may end up + * putting all kinds of interesting stuff here :-) + */ +struct blkfront_info +{ + struct xenbus_device *xbdev; + dev_t dev; + struct gendisk *gd; + int vdevice; + blkif_vdev_t handle; + enum blkif_state connected; + int ring_ref; + struct blkif_front_ring ring; + unsigned int evtchn, irq; + struct request_queue *rq; + struct work_struct work; + struct gnttab_free_callback callback; + struct blk_shadow shadow[BLK_RING_SIZE]; + unsigned long shadow_free; + int feature_barrier; + + /** + * The number of people holding this device open. We won't allow a + * hot-unplug unless this is 0. + */ + int users; +}; + +static DEFINE_SPINLOCK(blkif_io_lock); + +#define MAXIMUM_OUTSTANDING_BLOCK_REQS \ + (BLKIF_MAX_SEGMENTS_PER_REQUEST * BLK_RING_SIZE) +#define GRANT_INVALID_REF 0 + +#define PARTS_PER_DISK 16 + +#define BLKIF_MAJOR(dev) ((dev)>>8) +#define BLKIF_MINOR(dev) ((dev) & 0xff) + +#define DEV_NAME "xvd" /* name in /dev */ + +/* Information about our VBDs. */ +#define MAX_VBDS 64 +static LIST_HEAD(vbds_list); + +static int get_id_from_freelist(struct blkfront_info *info) +{ + unsigned long free = info->shadow_free; + BUG_ON(free > BLK_RING_SIZE); + info->shadow_free = info->shadow[free].req.id; + info->shadow[free].req.id = 0x0fffffee; /* debug */ + return free; +} + +static void add_id_to_freelist(struct blkfront_info *info, + unsigned long id) +{ + info->shadow[id].req.id = info->shadow_free; + info->shadow[id].request = 0; + info->shadow_free = id; +} + +static void blkif_restart_queue_callback(void *arg) +{ + struct blkfront_info *info = (struct blkfront_info *)arg; + schedule_work(&info->work); +} + +/* + * blkif_queue_request + * + * request block io + * + * id: for guest use only. + * operation: BLKIF_OP_{READ,WRITE,PROBE} + * buffer: buffer to read/write into. this should be a + * virtual address in the guest os. + */ +static int blkif_queue_request(struct request *req) +{ + struct blkfront_info *info = req->rq_disk->private_data; + unsigned long buffer_mfn; + struct blkif_request *ring_req; + struct bio *bio; + struct bio_vec *bvec; + int idx; + unsigned long id; + unsigned int fsect, lsect; + int ref; + grant_ref_t gref_head; + + if (unlikely(info->connected != BLKIF_STATE_CONNECTED)) + return 1; + + if (gnttab_alloc_grant_references( + BLKIF_MAX_SEGMENTS_PER_REQUEST, &gref_head) < 0) { + gnttab_request_free_callback( + &info->callback, + blkif_restart_queue_callback, + info, + BLKIF_MAX_SEGMENTS_PER_REQUEST); + return 1; + } + + /* Fill out a communications ring structure. */ + ring_req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt); + id = get_id_from_freelist(info); + info->shadow[id].request = (unsigned long)req; + + ring_req->id = id; + ring_req->sector_number = (blkif_sector_t)req->sector; + ring_req->handle = info->handle; + + ring_req->operation = rq_data_dir(req) ? + BLKIF_OP_WRITE : BLKIF_OP_READ; + if (blk_barrier_rq(req)) + ring_req->operation = BLKIF_OP_WRITE_BARRIER; + + ring_req->nr_segments = 0; + rq_for_each_bio (bio, req) { + bio_for_each_segment (bvec, bio, idx) { + BUG_ON(ring_req->nr_segments + == BLKIF_MAX_SEGMENTS_PER_REQUEST); + buffer_mfn = pfn_to_mfn(page_to_pfn(bvec->bv_page)); + fsect = bvec->bv_offset >> 9; + lsect = fsect + (bvec->bv_len >> 9) - 1; + /* install a grant reference. */ + ref = gnttab_claim_grant_reference(&gref_head); + BUG_ON(ref == -ENOSPC); + + gnttab_grant_foreign_access_ref( + ref, + info->xbdev->otherend_id, + buffer_mfn, + rq_data_dir(req) ); + + info->shadow[id].frame[ring_req->nr_segments] = + mfn_to_pfn(buffer_mfn); + + ring_req->seg[ring_req->nr_segments] = + (struct blkif_request_segment) { + .gref = ref, + .first_sect = fsect, + .last_sect = lsect }; + + ring_req->nr_segments++; + } + } + + info->ring.req_prod_pvt++; + + /* Keep a private copy so we can reissue requests when recovering. */ + info->shadow[id].req = *ring_req; + + gnttab_free_grant_references(gref_head); + + return 0; +} + + +static inline void flush_requests(struct blkfront_info *info) +{ + int notify; + + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->ring, notify); + + if (notify) + notify_remote_via_irq(info->irq); +} + +/* + * do_blkif_request + * read a block; request is in a request queue + */ +static void do_blkif_request(request_queue_t *rq) +{ + struct blkfront_info *info = NULL; + struct request *req; + int queued; + + pr_debug("Entered do_blkif_request\n"); + + queued = 0; + + while ((req = elv_next_request(rq)) != NULL) { + info = req->rq_disk->private_data; + if (!blk_fs_request(req)) { + end_request(req, 0); + continue; + } + + if (RING_FULL(&info->ring)) + goto wait; + + pr_debug("do_blk_req %p: cmd %p, sec %lx, " + "(%u/%li) buffer:%p [%s]\n", + req, req->cmd, (unsigned long)req->sector, + req->current_nr_sectors, + req->nr_sectors, req->buffer, + rq_data_dir(req) ? "write" : "read"); + + + blkdev_dequeue_request(req); + if (blkif_queue_request(req)) { + blk_requeue_request(rq, req); +wait: + /* Avoid pointless unplugs. */ + blk_stop_queue(rq); + break; + } + + queued++; + } + + if (queued != 0) + flush_requests(info); +} + +static int xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size) +{ + request_queue_t *rq; + + rq = blk_init_queue(do_blkif_request, &blkif_io_lock); + if (rq == NULL) + return -1; + + elevator_init(rq, "noop"); + + /* Hard sector size and max sectors impersonate the equiv. hardware. */ + blk_queue_hardsect_size(rq, sector_size); + blk_queue_max_sectors(rq, 512); + + /* Each segment in a request is up to an aligned page in size. */ + blk_queue_segment_boundary(rq, PAGE_SIZE - 1); + blk_queue_max_segment_size(rq, PAGE_SIZE); + + /* Ensure a merged request will fit in a single I/O ring slot. */ + blk_queue_max_phys_segments(rq, BLKIF_MAX_SEGMENTS_PER_REQUEST); + blk_queue_max_hw_segments(rq, BLKIF_MAX_SEGMENTS_PER_REQUEST); + + /* Make sure buffer addresses are sector-aligned. */ + blk_queue_dma_alignment(rq, 511); + + gd->queue = rq; + + return 0; +} + + +static int xlvbd_barrier(struct blkfront_info *info) +{ + int err; + + err = blk_queue_ordered(info->rq, + info->feature_barrier ? QUEUE_ORDERED_DRAIN : QUEUE_ORDERED_NONE, + NULL); + + if (err) + return err; + + printk(KERN_INFO "blkfront: %s: barriers %s\n", + info->gd->disk_name, + info->feature_barrier ? "enabled" : "disabled"); + return 0; +} + + +static int xlvbd_alloc_gendisk(int minor, blkif_sector_t capacity, + int vdevice, u16 vdisk_info, u16 sector_size, + struct blkfront_info *info) +{ + struct gendisk *gd; + int nr_minors = 1; + int err = -ENODEV; + + BUG_ON(info->gd != NULL); + BUG_ON(info->rq != NULL); + + if ((minor % PARTS_PER_DISK) == 0) + nr_minors = PARTS_PER_DISK; + + gd = alloc_disk(nr_minors); + if (gd == NULL) + goto out; + + if (nr_minors > 1) + sprintf(gd->disk_name, "%s%c", DEV_NAME, + 'a' + minor / PARTS_PER_DISK); + else + sprintf(gd->disk_name, "%s%c%d", DEV_NAME, + 'a' + minor / PARTS_PER_DISK, + minor % PARTS_PER_DISK); + + gd->major = XENVBD_MAJOR; + gd->first_minor = minor; + gd->fops = &xlvbd_block_fops; + gd->private_data = info; + gd->driverfs_dev = &(info->xbdev->dev); + set_capacity(gd, capacity); + + if (xlvbd_init_blk_queue(gd, sector_size)) { + del_gendisk(gd); + goto out; + } + + info->rq = gd->queue; + info->gd = gd; + + if (info->feature_barrier) + xlvbd_barrier(info); + + if (vdisk_info & VDISK_READONLY) + set_disk_ro(gd, 1); + + if (vdisk_info & VDISK_REMOVABLE) + gd->flags |= GENHD_FL_REMOVABLE; + + if (vdisk_info & VDISK_CDROM) + gd->flags |= GENHD_FL_CD; + + return 0; + + out: + return err; +} + +static void kick_pending_request_queues(struct blkfront_info *info) +{ + if (!RING_FULL(&info->ring)) { + /* Re-enable calldowns. */ + blk_start_queue(info->rq); + /* Kick things off immediately. */ + do_blkif_request(info->rq); + } +} + +static void blkif_restart_queue(struct work_struct *work) +{ + struct blkfront_info *info = container_of(work, struct blkfront_info, work); + + spin_lock_irq(&blkif_io_lock); + if (info->connected == BLKIF_STATE_CONNECTED) + kick_pending_request_queues(info); + spin_unlock_irq(&blkif_io_lock); +} + +static void blkif_free(struct blkfront_info *info, int suspend) +{ + /* Prevent new requests being issued until we fix things up. */ + spin_lock_irq(&blkif_io_lock); + info->connected = suspend ? + BLKIF_STATE_SUSPENDED : BLKIF_STATE_DISCONNECTED; + /* No more blkif_request(). */ + if (info->rq) + blk_stop_queue(info->rq); + /* No more gnttab callback work. */ + gnttab_cancel_free_callback(&info->callback); + spin_unlock_irq(&blkif_io_lock); + + /* Flush gnttab callback work. Must be done with no locks held. */ + flush_scheduled_work(); + + /* Free resources associated with old device channel. */ + if (info->ring_ref != GRANT_INVALID_REF) { + gnttab_end_foreign_access(info->ring_ref, 0, + (unsigned long)info->ring.sring); + info->ring_ref = GRANT_INVALID_REF; + info->ring.sring = NULL; + } + if (info->irq) + unbind_from_irqhandler(info->irq, info); + info->evtchn = info->irq = 0; + +} + +static void blkif_completion(struct blk_shadow *s) +{ + int i; + for (i = 0; i < s->req.nr_segments; i++) + gnttab_end_foreign_access(s->req.seg[i].gref, 0, 0UL); +} + +static irqreturn_t blkif_interrupt(int irq, void *dev_id) +{ + struct request *req; + struct blkif_response *bret; + RING_IDX i, rp; + unsigned long flags; + struct blkfront_info *info = (struct blkfront_info *)dev_id; + int uptodate; + + spin_lock_irqsave(&blkif_io_lock, flags); + + if (unlikely(info->connected != BLKIF_STATE_CONNECTED)) { + spin_unlock_irqrestore(&blkif_io_lock, flags); + return IRQ_HANDLED; + } + + again: + rp = info->ring.sring->rsp_prod; + rmb(); /* Ensure we see queued responses up to 'rp'. */ + + for (i = info->ring.rsp_cons; i != rp; i++) { + unsigned long id; + int ret; + + bret = RING_GET_RESPONSE(&info->ring, i); + id = bret->id; + req = (struct request *)info->shadow[id].request; + + blkif_completion(&info->shadow[id]); + + add_id_to_freelist(info, id); + + uptodate = (bret->status == BLKIF_RSP_OKAY); + switch (bret->operation) { + case BLKIF_OP_WRITE_BARRIER: + if (unlikely(bret->status == BLKIF_RSP_EOPNOTSUPP)) { + printk(KERN_WARNING "blkfront: %s: write barrier op failed\n", + info->gd->disk_name); + uptodate = -EOPNOTSUPP; + info->feature_barrier = 0; + xlvbd_barrier(info); + } + /* fall through */ + case BLKIF_OP_READ: + case BLKIF_OP_WRITE: + if (unlikely(bret->status != BLKIF_RSP_OKAY)) + dev_dbg(&info->xbdev->dev, "Bad return from blkdev data " + "request: %x\n", bret->status); + + ret = end_that_request_first(req, uptodate, + req->hard_nr_sectors); + BUG_ON(ret); + end_that_request_last(req, uptodate); + break; + default: + BUG(); + } + } + + info->ring.rsp_cons = i; + + if (i != info->ring.req_prod_pvt) { + int more_to_do; + RING_FINAL_CHECK_FOR_RESPONSES(&info->ring, more_to_do); + if (more_to_do) + goto again; + } else + info->ring.sring->rsp_event = i + 1; + + kick_pending_request_queues(info); + + spin_unlock_irqrestore(&blkif_io_lock, flags); + + return IRQ_HANDLED; +} + + +static int setup_blkring(struct xenbus_device *dev, + struct blkfront_info *info) +{ + struct blkif_sring *sring; + int err; + + info->ring_ref = GRANT_INVALID_REF; + + sring = (struct blkif_sring *)__get_free_page(GFP_KERNEL); + if (!sring) { + xenbus_dev_fatal(dev, -ENOMEM, "allocating shared ring"); + return -ENOMEM; + } + SHARED_RING_INIT(sring); + FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE); + + err = xenbus_grant_ring(dev, virt_to_mfn(info->ring.sring)); + if (err < 0) { + free_page((unsigned long)sring); + info->ring.sring = NULL; + goto fail; + } + info->ring_ref = err; + + err = xenbus_alloc_evtchn(dev, &info->evtchn); + if (err) + goto fail; + + err = bind_evtchn_to_irqhandler(info->evtchn, + blkif_interrupt, + IRQF_SAMPLE_RANDOM, "blkif", info); + if (err <= 0) { + xenbus_dev_fatal(dev, err, + "bind_evtchn_to_irqhandler failed"); + goto fail; + } + info->irq = err; + + return 0; +fail: + blkif_free(info, 0); + return err; +} + + +/* Common code used when first setting up, and when resuming. */ +static int talk_to_backend(struct xenbus_device *dev, + struct blkfront_info *info) +{ + const char *message = NULL; + struct xenbus_transaction xbt; + int err; + + /* Create shared ring, alloc event channel. */ + err = setup_blkring(dev, info); + if (err) + goto out; + +again: + err = xenbus_transaction_start(&xbt); + if (err) { + xenbus_dev_fatal(dev, err, "starting transaction"); + goto destroy_blkring; + } + + err = xenbus_printf(xbt, dev->nodename, + "ring-ref", "%u", info->ring_ref); + if (err) { + message = "writing ring-ref"; + goto abort_transaction; + } + err = xenbus_printf(xbt, dev->nodename, + "event-channel", "%u", info->evtchn); + if (err) { + message = "writing event-channel"; + goto abort_transaction; + } + + err = xenbus_transaction_end(xbt, 0); + if (err) { + if (err == -EAGAIN) + goto again; + xenbus_dev_fatal(dev, err, "completing transaction"); + goto destroy_blkring; + } + + xenbus_switch_state(dev, XenbusStateInitialised); + + return 0; + + abort_transaction: + xenbus_transaction_end(xbt, 1); + if (message) + xenbus_dev_fatal(dev, err, "%s", message); + destroy_blkring: + blkif_free(info, 0); + out: + return err; +} + + +/** + * Entry point to this code when a new device is created. Allocate the basic + * structures and the ring buffer for communication with the backend, and + * inform the backend of the appropriate details for those. Switch to + * Initialised state. + */ +static int blkfront_probe(struct xenbus_device *dev, + const struct xenbus_device_id *id) +{ + int err, vdevice, i; + struct blkfront_info *info; + + /* FIXME: Use dynamic device id if this is not set. */ + err = xenbus_scanf(XBT_NIL, dev->nodename, + "virtual-device", "%i", &vdevice); + if (err != 1) { + xenbus_dev_fatal(dev, err, "reading virtual-device"); + return err; + } + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); + return -ENOMEM; + } + + info->xbdev = dev; + info->vdevice = vdevice; + info->connected = BLKIF_STATE_DISCONNECTED; + INIT_WORK(&info->work, blkif_restart_queue); + + for (i = 0; i < BLK_RING_SIZE; i++) + info->shadow[i].req.id = i+1; + info->shadow[BLK_RING_SIZE-1].req.id = 0x0fffffff; + + /* Front end dir is a number, which is used as the id. */ + info->handle = simple_strtoul(strrchr(dev->nodename, '/')+1, NULL, 0); + dev->dev.driver_data = info; + + err = talk_to_backend(dev, info); + if (err) { + kfree(info); + dev->dev.driver_data = NULL; + return err; + } + + return 0; +} + + +static int blkif_recover(struct blkfront_info *info) +{ + int i; + struct blkif_request *req; + struct blk_shadow *copy; + int j; + + /* Stage 1: Make a safe copy of the shadow state. */ + copy = kmalloc(sizeof(info->shadow), GFP_KERNEL); + if (!copy) + return -ENOMEM; + memcpy(copy, info->shadow, sizeof(info->shadow)); + + /* Stage 2: Set up free list. */ + memset(&info->shadow, 0, sizeof(info->shadow)); + for (i = 0; i < BLK_RING_SIZE; i++) + info->shadow[i].req.id = i+1; + info->shadow_free = info->ring.req_prod_pvt; + info->shadow[BLK_RING_SIZE-1].req.id = 0x0fffffff; + + /* Stage 3: Find pending requests and requeue them. */ + for (i = 0; i < BLK_RING_SIZE; i++) { + /* Not in use? */ + if (copy[i].request == 0) + continue; + + /* Grab a request slot and copy shadow state into it. */ + req = RING_GET_REQUEST(&info->ring, info->ring.req_prod_pvt); + *req = copy[i].req; + + /* We get a new request id, and must reset the shadow state. */ + req->id = get_id_from_freelist(info); + memcpy(&info->shadow[req->id], ©[i], sizeof(copy[i])); + + /* Rewrite any grant references invalidated by susp/resume. */ + for (j = 0; j < req->nr_segments; j++) + gnttab_grant_foreign_access_ref( + req->seg[j].gref, + info->xbdev->otherend_id, + pfn_to_mfn(info->shadow[req->id].frame[j]), + rq_data_dir( + (struct request *) + info->shadow[req->id].request)); + info->shadow[req->id].req = *req; + + info->ring.req_prod_pvt++; + } + + kfree(copy); + + xenbus_switch_state(info->xbdev, XenbusStateConnected); + + spin_lock_irq(&blkif_io_lock); + + /* Now safe for us to use the shared ring */ + info->connected = BLKIF_STATE_CONNECTED; + + /* Send off requeued requests */ + flush_requests(info); + + /* Kick any other new requests queued since we resumed */ + kick_pending_request_queues(info); + + spin_unlock_irq(&blkif_io_lock); + + return 0; +} + +/** + * We are reconnecting to the backend, due to a suspend/resume, or a backend + * driver restart. We tear down our blkif structure and recreate it, but + * leave the device-layer structures intact so that this is transparent to the + * rest of the kernel. + */ +static int blkfront_resume(struct xenbus_device *dev) +{ + struct blkfront_info *info = dev->dev.driver_data; + int err; + + dev_dbg(&dev->dev, "blkfront_resume: %s\n", dev->nodename); + + blkif_free(info, info->connected == BLKIF_STATE_CONNECTED); + + err = talk_to_backend(dev, info); + if (info->connected == BLKIF_STATE_SUSPENDED && !err) + err = blkif_recover(info); + + return err; +} + + +/* + * Invoked when the backend is finally 'ready' (and has told produced + * the details about the physical device - #sectors, size, etc). + */ +static void blkfront_connect(struct blkfront_info *info) +{ + unsigned long long sectors; + unsigned long sector_size; + unsigned int binfo; + int err; + + if ((info->connected == BLKIF_STATE_CONNECTED) || + (info->connected == BLKIF_STATE_SUSPENDED) ) + return; + + dev_dbg(&info->xbdev->dev, "%s:%s.\n", + __func__, info->xbdev->otherend); + + err = xenbus_gather(XBT_NIL, info->xbdev->otherend, + "sectors", "%llu", §ors, + "info", "%u", &binfo, + "sector-size", "%lu", §or_size, + NULL); + if (err) { + xenbus_dev_fatal(info->xbdev, err, + "reading backend fields at %s", + info->xbdev->otherend); + return; + } + + err = xenbus_gather(XBT_NIL, info->xbdev->otherend, + "feature-barrier", "%lu", &info->feature_barrier, + NULL); + if (err) + info->feature_barrier = 0; + + err = xlvbd_alloc_gendisk(BLKIF_MINOR(info->vdevice), + sectors, info->vdevice, + binfo, sector_size, info); + if (err) { + xenbus_dev_fatal(info->xbdev, err, "xlvbd_add at %s", + info->xbdev->otherend); + return; + } + + xenbus_switch_state(info->xbdev, XenbusStateConnected); + + /* Kick pending requests. */ + spin_lock_irq(&blkif_io_lock); + info->connected = BLKIF_STATE_CONNECTED; + kick_pending_request_queues(info); + spin_unlock_irq(&blkif_io_lock); + + add_disk(info->gd); +} + +/** + * Handle the change of state of the backend to Closing. We must delete our + * device-layer structures now, to ensure that writes are flushed through to + * the backend. Once is this done, we can switch to Closed in + * acknowledgement. + */ +static void blkfront_closing(struct xenbus_device *dev) +{ + struct blkfront_info *info = dev->dev.driver_data; + unsigned long flags; + + dev_dbg(&dev->dev, "blkfront_closing: %s removed\n", dev->nodename); + + if (info->rq == NULL) + goto out; + + spin_lock_irqsave(&blkif_io_lock, flags); + + del_gendisk(info->gd); + + /* No more blkif_request(). */ + blk_stop_queue(info->rq); + + /* No more gnttab callback work. */ + gnttab_cancel_free_callback(&info->callback); + spin_unlock_irqrestore(&blkif_io_lock, flags); + + /* Flush gnttab callback work. Must be done with no locks held. */ + flush_scheduled_work(); + + blk_cleanup_queue(info->rq); + info->rq = NULL; + + out: + xenbus_frontend_closed(dev); +} + +/** + * Callback received when the backend's state changes. + */ +static void backend_changed(struct xenbus_device *dev, + enum xenbus_state backend_state) +{ + struct blkfront_info *info = dev->dev.driver_data; + struct block_device *bd; + + dev_dbg(&dev->dev, "blkfront:backend_changed.\n"); + + switch (backend_state) { + case XenbusStateInitialising: + case XenbusStateInitWait: + case XenbusStateInitialised: + case XenbusStateUnknown: + case XenbusStateClosed: + break; + + case XenbusStateConnected: + blkfront_connect(info); + break; + + case XenbusStateClosing: + bd = bdget(info->dev); + if (bd == NULL) + xenbus_dev_fatal(dev, -ENODEV, "bdget failed"); + + mutex_lock(&bd->bd_mutex); + if (info->users > 0) + xenbus_dev_error(dev, -EBUSY, + "Device in use; refusing to close"); + else + blkfront_closing(dev); + mutex_unlock(&bd->bd_mutex); + bdput(bd); + break; + } +} + +static int blkfront_remove(struct xenbus_device *dev) +{ + struct blkfront_info *info = dev->dev.driver_data; + + dev_dbg(&dev->dev, "blkfront_remove: %s removed\n", dev->nodename); + + blkif_free(info, 0); + + kfree(info); + + return 0; +} + +static int blkif_open(struct inode *inode, struct file *filep) +{ + struct blkfront_info *info = inode->i_bdev->bd_disk->private_data; + info->users++; + return 0; +} + +static int blkif_release(struct inode *inode, struct file *filep) +{ + struct blkfront_info *info = inode->i_bdev->bd_disk->private_data; + info->users--; + if (info->users == 0) { + /* Check whether we have been instructed to close. We will + have ignored this request initially, as the device was + still mounted. */ + struct xenbus_device *dev = info->xbdev; + enum xenbus_state state = xenbus_read_driver_state(dev->otherend); + + if (state == XenbusStateClosing) + blkfront_closing(dev); + } + return 0; +} + +static struct block_device_operations xlvbd_block_fops = +{ + .owner = THIS_MODULE, + .open = blkif_open, + .release = blkif_release, +}; + + +static struct xenbus_device_id blkfront_ids[] = { + { "vbd" }, + { "" } +}; + +static struct xenbus_driver blkfront = { + .name = "vbd", + .owner = THIS_MODULE, + .ids = blkfront_ids, + .probe = blkfront_probe, + .remove = blkfront_remove, + .resume = blkfront_resume, + .otherend_changed = backend_changed, +}; + +static int __init xlblk_init(void) +{ + if (!is_running_on_xen()) + return -ENODEV; + + if (register_blkdev(XENVBD_MAJOR, DEV_NAME)) { + printk(KERN_WARNING "xen_blk: can't get major %d with name %s\n", + XENVBD_MAJOR, DEV_NAME); + return -ENODEV; + } + + return xenbus_register_frontend(&blkfront); +} +module_init(xlblk_init); + + +static void xlblk_exit(void) +{ + return xenbus_unregister_driver(&blkfront); +} +module_exit(xlblk_exit); + +MODULE_DESCRIPTION("Xen virtual block device frontend"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_BLOCKDEV_MAJOR(XENVBD_MAJOR); diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 97bd71b..9e8f214 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -604,6 +604,14 @@ config HVC_BEAT help Toshiba's Cell Reference Set Beat Console device driver +config HVC_XEN + bool "Xen Hypervisor Console support" + depends on XEN + select HVC_DRIVER + default y + help + Xen virtual console device driver + config HVCS tristate "IBM Hypervisor Virtual Console Server support" depends on PPC_PSERIES diff --git a/drivers/char/Makefile b/drivers/char/Makefile index f2996a9..8852b8d 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_HVC_ISERIES) += hvc_iseries.o obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o obj-$(CONFIG_HVC_BEAT) += hvc_beat.o obj-$(CONFIG_HVC_DRIVER) += hvc_console.o +obj-$(CONFIG_HVC_XEN) += hvc_xen.o obj-$(CONFIG_RAW_DRIVER) += raw.o obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o obj-$(CONFIG_MSPEC) += mspec.o diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c new file mode 100644 index 0000000..dd68f85 --- /dev/null +++ b/drivers/char/hvc_xen.c @@ -0,0 +1,159 @@ +/* + * xen console driver interface to hvc_console.c + * + * (c) 2007 Gerd Hoffmann <kraxel@suse.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/console.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/types.h> + +#include <asm/xen/hypervisor.h> +#include <xen/page.h> +#include <xen/events.h> +#include <xen/interface/io/console.h> +#include <xen/hvc-console.h> + +#include "hvc_console.h" + +#define HVC_COOKIE 0x58656e /* "Xen" in hex */ + +static struct hvc_struct *hvc; +static int xencons_irq; + +/* ------------------------------------------------------------------ */ + +static inline struct xencons_interface *xencons_interface(void) +{ + return mfn_to_virt(xen_start_info->console.domU.mfn); +} + +static inline void notify_daemon(void) +{ + /* Use evtchn: this is called early, before irq is set up. */ + notify_remote_via_evtchn(xen_start_info->console.domU.evtchn); +} + +static int write_console(uint32_t vtermno, const char *data, int len) +{ + struct xencons_interface *intf = xencons_interface(); + XENCONS_RING_IDX cons, prod; + int sent = 0; + + cons = intf->out_cons; + prod = intf->out_prod; + mb(); /* update queue values before going on */ + BUG_ON((prod - cons) > sizeof(intf->out)); + + while ((sent < len) && ((prod - cons) < sizeof(intf->out))) + intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++]; + + wmb(); /* write ring before updating pointer */ + intf->out_prod = prod; + + notify_daemon(); + return sent; +} + +static int read_console(uint32_t vtermno, char *buf, int len) +{ + struct xencons_interface *intf = xencons_interface(); + XENCONS_RING_IDX cons, prod; + int recv = 0; + + cons = intf->in_cons; + prod = intf->in_prod; + mb(); /* get pointers before reading ring */ + BUG_ON((prod - cons) > sizeof(intf->in)); + + while (cons != prod && recv < len) + buf[recv++] = intf->in[MASK_XENCONS_IDX(cons++, intf->in)]; + + mb(); /* read ring before consuming */ + intf->in_cons = cons; + + notify_daemon(); + return recv; +} + +static struct hv_ops hvc_ops = { + .get_chars = read_console, + .put_chars = write_console, +}; + +static int __init xen_init(void) +{ + struct hvc_struct *hp; + + if (!is_running_on_xen()) + return 0; + + xencons_irq = bind_evtchn_to_irq(xen_start_info->console.domU.evtchn); + if (xencons_irq < 0) + xencons_irq = 0 /* NO_IRQ */; + hp = hvc_alloc(HVC_COOKIE, xencons_irq, &hvc_ops, 256); + if (IS_ERR(hp)) + return PTR_ERR(hp); + + hvc = hp; + return 0; +} + +static void __exit xen_fini(void) +{ + if (hvc) + hvc_remove(hvc); +} + +static int xen_cons_init(void) +{ + if (!is_running_on_xen()) + return 0; + + hvc_instantiate(HVC_COOKIE, 0, &hvc_ops); + return 0; +} + +module_init(xen_init); +module_exit(xen_fini); +console_initcall(xen_cons_init); + +static void xenboot_write_console(struct console *console, const char *string, + unsigned len) +{ + unsigned int linelen, off = 0; + const char *pos; + + while (off < len && NULL != (pos = strchr(string+off, '\n'))) { + linelen = pos-string+off; + if (off + linelen > len) + break; + write_console(0, string+off, linelen); + write_console(0, "\r\n", 2); + off += linelen + 1; + } + if (off < len) + write_console(0, string+off, len-off); +} + +struct console xenboot_console = { + .name = "xenboot", + .write = xenboot_write_console, + .flags = CON_PRINTBUFFER | CON_BOOT, +}; diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c index dbb2240..3d90fc0 100644 --- a/drivers/macintosh/therm_pm72.c +++ b/drivers/macintosh/therm_pm72.c @@ -1770,7 +1770,8 @@ static int call_critical_overtemp(void) "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; - return call_usermodehelper(critical_overtemp_path, argv, envp, 0); + return call_usermodehelper(critical_overtemp_path, + argv, envp, UMH_WAIT_EXEC); } diff --git a/drivers/macintosh/windfarm_core.c b/drivers/macintosh/windfarm_core.c index e18d265..516d943 100644 --- a/drivers/macintosh/windfarm_core.c +++ b/drivers/macintosh/windfarm_core.c @@ -80,7 +80,8 @@ int wf_critical_overtemp(void) "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; - return call_usermodehelper(critical_overtemp_path, argv, envp, 0); + return call_usermodehelper(critical_overtemp_path, + argv, envp, UMH_WAIT_EXEC); } EXPORT_SYMBOL_GPL(wf_critical_overtemp); diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 624b21c..d9d033e 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -80,8 +80,12 @@ config VIDEO_BUF_DVB config VIDEO_BTCX tristate +config VIDEO_IR_I2C + tristate + config VIDEO_IR tristate + select VIDEO_IR_I2C if I2C config VIDEO_TVEEPROM tristate diff --git a/drivers/media/common/ir-functions.c b/drivers/media/common/ir-functions.c index fcb1941..fe447a0 100644 --- a/drivers/media/common/ir-functions.c +++ b/drivers/media/common/ir-functions.c @@ -107,21 +107,20 @@ void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir, } /* -------------------------------------------------------------------------- */ - +/* extract mask bits out of data and pack them into the result */ u32 ir_extract_bits(u32 data, u32 mask) { - int mbit, vbit; - u32 value; + u32 vbit = 1, value = 0; + + do { + if (mask&1) { + if (data&1) + value |= vbit; + vbit<<=1; + } + data>>=1; + } while (mask>>=1); - value = 0; - vbit = 0; - for (mbit = 0; mbit < 32; mbit++) { - if (!(mask & ((u32)1 << mbit))) - continue; - if (data & ((u32)1 << mbit)) - value |= (1 << vbit); - vbit++; - } return value; } diff --git a/drivers/media/common/saa7146_core.c b/drivers/media/common/saa7146_core.c index ef3e54c..ba6701e 100644 --- a/drivers/media/common/saa7146_core.c +++ b/drivers/media/common/saa7146_core.c @@ -27,7 +27,7 @@ static int saa7146_num; unsigned int saa7146_debug; -module_param(saa7146_debug, int, 0644); +module_param(saa7146_debug, uint, 0644); MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)"); #if 0 @@ -130,10 +130,10 @@ static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages) /********************************************************************************/ /* common page table functions */ -char *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt) +void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt) { int pages = (length+PAGE_SIZE-1)/PAGE_SIZE; - char *mem = vmalloc_32(length); + void *mem = vmalloc_32(length); int slen = 0; if (NULL == mem) @@ -168,7 +168,7 @@ err_null: return NULL; } -void saa7146_vfree_destroy_pgtable(struct pci_dev *pci, char *mem, struct saa7146_pgtable *pt) +void saa7146_vfree_destroy_pgtable(struct pci_dev *pci, void *mem, struct saa7146_pgtable *pt) { pci_unmap_sg(pci, pt->slist, pt->nents, PCI_DMA_FROMDEVICE); saa7146_pgtable_free(pci, pt); diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index e3d04a4..664280c 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -889,9 +889,9 @@ int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int DEB_EE(("VIDIOC_QUERYCAP\n")); - strcpy(cap->driver, "saa7146 v4l2"); - strlcpy(cap->card, dev->ext->name, sizeof(cap->card)); - sprintf(cap->bus_info,"PCI:%s", pci_name(dev->pci)); + strcpy((char *)cap->driver, "saa7146 v4l2"); + strlcpy((char *)cap->card, dev->ext->name, sizeof(cap->card)); + sprintf((char *)cap->bus_info,"PCI:%s", pci_name(dev->pci)); cap->version = SAA7146_VERSION_CODE; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | @@ -968,7 +968,7 @@ int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int } memset(f,0,sizeof(*f)); f->index = index; - strlcpy(f->description,formats[index].name,sizeof(f->description)); + strlcpy((char *)f->description,formats[index].name,sizeof(f->description)); f->pixelformat = formats[index].pixelformat; break; } diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig index a0dcd59..3197aeb 100644 --- a/drivers/media/dvb/b2c2/Kconfig +++ b/drivers/media/dvb/b2c2/Kconfig @@ -1,7 +1,7 @@ config DVB_B2C2_FLEXCOP tristate "Technisat/B2C2 FlexCopII(b) and FlexCopIII adapters" depends on DVB_CORE && I2C - select DVB_PLL + select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_MT352 if !DVB_FE_CUSTOMISE select DVB_MT312 if !DVB_FE_CUSTOMISE diff --git a/drivers/media/dvb/b2c2/Makefile b/drivers/media/dvb/b2c2/Makefile index bff00b5..e97ff60 100644 --- a/drivers/media/dvb/b2c2/Makefile +++ b/drivers/media/dvb/b2c2/Makefile @@ -12,4 +12,4 @@ obj-$(CONFIG_DVB_B2C2_FLEXCOP_PCI) += b2c2-flexcop-pci.o b2c2-flexcop-usb-objs = flexcop-usb.o obj-$(CONFIG_DVB_B2C2_FLEXCOP_USB) += b2c2-flexcop-usb.o -EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ diff --git a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index b02c2fd..0378fd6 100644 --- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -500,13 +500,13 @@ int flexcop_frontend_init(struct flexcop_device *fc) /* try the air atsc 2nd generation (nxt2002) */ if ((fc->fe = dvb_attach(nxt200x_attach, &samsung_tbmv_config, &fc->i2c_adap)) != NULL) { fc->dev_type = FC_AIR_ATSC2; - dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL, &dvb_pll_samsung_tbmv); + dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL, DVB_PLL_SAMSUNG_TBMV); info("found the nxt2002 at i2c address: 0x%02x",samsung_tbmv_config.demod_address); } else /* try the air atsc 3nd generation (lgdt3303) */ if ((fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, &fc->i2c_adap)) != NULL) { fc->dev_type = FC_AIR_ATSC3; - dvb_attach(dvb_pll_attach, fc->fe, 0x61, &fc->i2c_adap, &dvb_pll_lg_tdvs_h06xf); + dvb_attach(dvb_pll_attach, fc->fe, 0x61, &fc->i2c_adap, DVB_PLL_LG_TDVS_H06XF); info("found the lgdt3303 at i2c address: 0x%02x",air2pc_atsc_hd5000_config.demod_address); } else /* try the air atsc 1nd generation (bcm3510)/panasonic ct10s */ diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig index cfd6fb7..ea66617 100644 --- a/drivers/media/dvb/bt8xx/Kconfig +++ b/drivers/media/dvb/bt8xx/Kconfig @@ -7,7 +7,7 @@ config DVB_BT8XX select DVB_CX24110 if !DVB_FE_CUSTOMISE select DVB_OR51211 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_PLL + select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE select FW_LOADER help diff --git a/drivers/media/dvb/bt8xx/Makefile b/drivers/media/dvb/bt8xx/Makefile index 9d197ef..84cf705 100644 --- a/drivers/media/dvb/bt8xx/Makefile +++ b/drivers/media/dvb/bt8xx/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o dst_ca.o -EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/video/bt8xx -Idrivers/media/dvb/frontends +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/video/bt8xx -Idrivers/media/dvb/frontends diff --git a/drivers/media/dvb/bt8xx/dst.c b/drivers/media/dvb/bt8xx/dst.c index e908e3c..b7a17e6 100644 --- a/drivers/media/dvb/bt8xx/dst.c +++ b/drivers/media/dvb/bt8xx/dst.c @@ -1652,7 +1652,7 @@ static int dst_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_paramet static int dst_tune_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters* p, unsigned int mode_flags, - int *delay, + unsigned int *delay, fe_status_t *status) { struct dst_state *state = fe->demodulator_priv; diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c index 4f1c09b..67613eb 100644 --- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c +++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c @@ -611,7 +611,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter); if (card->fe != NULL) { dvb_attach(dvb_pll_attach, card->fe, 0x61, - card->i2c_adapter, &dvb_pll_lg_tdvs_h06xf); + card->i2c_adapter, DVB_PLL_LG_TDVS_H06XF); dprintk ("dvb_bt8xx: lgdt330x detected\n"); } break; @@ -692,6 +692,9 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) case BTTV_BOARD_PC_HDTV: card->fe = dvb_attach(or51211_attach, &or51211_config, card->i2c_adapter); + if (card->fe != NULL) + dvb_attach(dvb_pll_attach, card->fe, 0x61, + card->i2c_adapter, DVB_PLL_FCV1236D); break; } diff --git a/drivers/media/dvb/cinergyT2/Makefile b/drivers/media/dvb/cinergyT2/Makefile index c51aece..d762d8c 100644 --- a/drivers/media/dvb/cinergyT2/Makefile +++ b/drivers/media/dvb/cinergyT2/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_DVB_CINERGYT2) += cinergyT2.o -EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ diff --git a/drivers/media/dvb/cinergyT2/cinergyT2.c b/drivers/media/dvb/cinergyT2/cinergyT2.c index b40af48..5a1449f 100644 --- a/drivers/media/dvb/cinergyT2/cinergyT2.c +++ b/drivers/media/dvb/cinergyT2/cinergyT2.c @@ -829,7 +829,7 @@ static int cinergyt2_register_rc(struct cinergyt2 *cinergyt2) input_dev->id.vendor = cinergyt2->udev->descriptor.idVendor; input_dev->id.product = cinergyt2->udev->descriptor.idProduct; input_dev->id.version = 1; - input_dev->cdev.dev = &cinergyt2->udev->dev; + input_dev->dev.parent = &cinergyt2->udev->dev; err = input_register_device(input_dev); if (err) { @@ -1000,18 +1000,15 @@ static int cinergyt2_suspend (struct usb_interface *intf, pm_message_t state) if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->wq_sem)) return -ERESTARTSYS; - if (1) { - cinergyt2_suspend_rc(cinergyt2); - cancel_rearming_delayed_work(&cinergyt2->query_work); + cinergyt2_suspend_rc(cinergyt2); + cancel_rearming_delayed_work(&cinergyt2->query_work); - mutex_lock(&cinergyt2->sem); - if (cinergyt2->streaming) - cinergyt2_stop_stream_xfer(cinergyt2); - cinergyt2_sleep(cinergyt2, 1); - mutex_unlock(&cinergyt2->sem); - } + mutex_lock(&cinergyt2->sem); + if (cinergyt2->streaming) + cinergyt2_stop_stream_xfer(cinergyt2); + cinergyt2_sleep(cinergyt2, 1); + mutex_unlock(&cinergyt2->sem); - mutex_unlock(&cinergyt2->wq_sem); return 0; } diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c index 275df65..5394de2 100644 --- a/drivers/media/dvb/dvb-core/dmxdev.c +++ b/drivers/media/dvb/dvb-core/dmxdev.c @@ -97,7 +97,7 @@ static ssize_t dvb_dmxdev_buffer_read(struct dvb_ringbuffer *src, if (avail > todo) avail = todo; - ret = dvb_ringbuffer_read(src, buf, avail, 1); + ret = dvb_ringbuffer_read(src, (u8 *)buf, avail, 1); if (ret < 0) break; diff --git a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c index 2a03bf5..4fadddb 100644 --- a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c +++ b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c @@ -175,7 +175,7 @@ static int dvb_ca_en50221_write_data(struct dvb_ca_private *ca, int slot, u8 * e * @param nlen Number of bytes in needle. * @return Pointer into haystack needle was found at, or NULL if not found. */ -static u8 *findstr(u8 * haystack, int hlen, u8 * needle, int nlen) +static char *findstr(char * haystack, int hlen, char * needle, int nlen) { int i; @@ -482,7 +482,7 @@ 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); + dvb_str = findstr((char *)tuple, tupleLength, "DVB_CI_V", 8); if (dvb_str == NULL) return -EINVAL; if (tupleLength < ((dvb_str - (char *) tuple) + 12)) @@ -513,8 +513,8 @@ static int dvb_ca_en50221_parse_attributes(struct dvb_ca_private *ca, int slot) 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)) + if ((findstr((char *)tuple, tupleLength, "DVB_HOST", 8) == NULL) || + (findstr((char *)tuple, tupleLength, "DVB_CI_MODULE", 13) == NULL)) break; got_cftableentry = 1; @@ -1300,7 +1300,7 @@ static ssize_t dvb_ca_en50221_io_write(struct file *file, struct dvb_ca_private *ca = dvbdev->priv; u8 slot, connection_id; int status; - char fragbuf[HOST_LINK_BUF_SIZE]; + u8 fragbuf[HOST_LINK_BUF_SIZE]; int fragpos = 0; int fraglen; unsigned long timeout; @@ -1486,7 +1486,7 @@ static ssize_t dvb_ca_en50221_io_read(struct file *file, char __user * buf, } if ((status = dvb_ringbuffer_pkt_read(&ca->slot_info[slot].rx_buffer, idx, 2, - buf + pktlen, fraglen, 1)) < 0) { + (u8 *)buf + pktlen, fraglen, 1)) < 0) { goto exit; } pktlen += fraglen; diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c index 6d8d1c3d..cb6987f 100644 --- a/drivers/media/dvb/dvb-core/dvb_demux.c +++ b/drivers/media/dvb/dvb-core/dvb_demux.c @@ -1068,7 +1068,7 @@ static int dvbdmx_write(struct dmx_demux *demux, const char *buf, size_t count) if (mutex_lock_interruptible(&dvbdemux->mutex)) return -ERESTARTSYS; - dvb_dmx_swfilter(dvbdemux, buf, count); + dvb_dmx_swfilter(dvbdemux, (u8 *)buf, count); mutex_unlock(&dvbdemux->mutex); if (signal_pending(current)) diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h index f233d78..a770a87 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -103,7 +103,7 @@ struct dvb_frontend_ops { int (*tune)(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, unsigned int mode_flags, - int *delay, + unsigned int *delay, fe_status_t *status); /* get frontend tuning algorithm from the module */ int (*get_frontend_algo)(struct dvb_frontend *fe); diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c index 4ebf33a5..acf0263 100644 --- a/drivers/media/dvb/dvb-core/dvb_net.c +++ b/drivers/media/dvb/dvb-core/dvb_net.c @@ -347,7 +347,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) { struct dvb_net_priv *priv = dev->priv; unsigned long skipped = 0L; - u8 *ts, *ts_end, *from_where = NULL, ts_remain = 0, how_much = 0, new_ts = 1; + const u8 *ts, *ts_end, *from_where = NULL; + u8 ts_remain = 0, how_much = 0, new_ts = 1; struct ethhdr *ethh = NULL; #ifdef ULE_DEBUG @@ -364,7 +365,7 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) /* 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. */ ) { + for (ts = buf, ts_end = buf + buf_len; ts < ts_end; /* no default incr. */ ) { if (new_ts) { /* We are about to process a new TS cell. */ diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c index a9fa333..9ef0c00 100644 --- a/drivers/media/dvb/dvb-core/dvbdev.c +++ b/drivers/media/dvb/dvb-core/dvbdev.c @@ -208,7 +208,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, if ((id = dvbdev_get_free_id (adap, type)) < 0){ mutex_unlock(&dvbdev_register_lock); *pdvbdev = NULL; - printk ("%s: could get find free device id...\n", __FUNCTION__); + printk(KERN_ERR "%s: couldn't find free device id\n", __FUNCTION__); return -ENFILE; } @@ -252,7 +252,7 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, return PTR_ERR(clsdev); } - dprintk("DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n", + dprintk(KERN_DEBUG "DVB: register adapter%d/%s%d @ minor: %i (0x%02x)\n", adap->num, dnames[type], id, nums2minor(adap->num, type, id), nums2minor(adap->num, type, id)); @@ -311,7 +311,7 @@ int dvb_register_adapter(struct dvb_adapter *adap, const char *name, struct modu memset (adap, 0, sizeof(struct dvb_adapter)); INIT_LIST_HEAD (&adap->device_list); - printk ("DVB: registering new adapter (%s).\n", name); + printk(KERN_INFO "DVB: registering new adapter (%s)\n", name); adap->num = num; adap->name = name; @@ -407,13 +407,13 @@ static int __init init_dvbdev(void) dev_t dev = MKDEV(DVB_MAJOR, 0); if ((retval = register_chrdev_region(dev, MAX_DVB_MINORS, "DVB")) != 0) { - printk("dvb-core: unable to get major %d\n", DVB_MAJOR); + printk(KERN_ERR "dvb-core: unable to get major %d\n", DVB_MAJOR); return retval; } cdev_init(&dvb_device_cdev, &dvb_device_fops); if ((retval = cdev_add(&dvb_device_cdev, dev, MAX_DVB_MINORS)) != 0) { - printk("dvb-core: unable to get major %d\n", DVB_MAJOR); + printk(KERN_ERR "dvb-core: unable register character device\n"); goto error; } diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index 5448873..40e41f2 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -2,7 +2,6 @@ config DVB_USB tristate "Support for various USB DVB devices" depends on DVB_CORE && USB && I2C select FW_LOADER - select DVB_PLL help By enabling this you will be able to choose the various supported USB1.1 and USB2.0 DVB devices. @@ -27,13 +26,14 @@ config DVB_USB_A800 depends on DVB_USB select DVB_DIB3000MC select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE + select DVB_PLL if !DVB_FE_CUSTOMISE help Say Y here to support the AVerMedia AverTV DVB-T USB 2.0 (A800) receiver. config DVB_USB_DIBUSB_MB tristate "DiBcom USB DVB-T devices (based on the DiB3000M-B) (see help for device list)" depends on DVB_USB - select DVB_PLL + select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_DIB3000MB select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE help @@ -89,7 +89,7 @@ config DVB_USB_DIB0700 config DVB_USB_UMT_010 tristate "HanfTek UMT-010 DVB-T USB2.0 support" depends on DVB_USB - select DVB_PLL + select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_DIB3000MC select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE help @@ -98,7 +98,7 @@ config DVB_USB_UMT_010 config DVB_USB_CXUSB tristate "Conexant USB2.0 hybrid reference design support" depends on DVB_USB - select DVB_PLL + select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_CX22702 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE select DVB_MT352 if !DVB_FE_CUSTOMISE @@ -142,7 +142,7 @@ config DVB_USB_AU6610 config DVB_USB_DIGITV tristate "Nebula Electronics uDigiTV DVB-T USB2.0 support" depends on DVB_USB - select DVB_PLL + select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_NXT6000 if !DVB_FE_CUSTOMISE select DVB_MT352 if !DVB_FE_CUSTOMISE help @@ -188,6 +188,7 @@ config DVB_USB_NOVA_T_USB2 depends on DVB_USB select DVB_DIB3000MC select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE + select DVB_PLL if !DVB_FE_CUSTOMISE help Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver. @@ -216,5 +217,23 @@ config DVB_USB_OPERA1 tristate "Opera1 DVB-S USB2.0 receiver" depends on DVB_USB select DVB_STV0299 if !DVB_FE_CUSTOMISE + select DVB_PLL if !DVB_FE_CUSTOMISE help Say Y here to support the Opera DVB-S USB2.0 receiver. + +config DVB_USB_AF9005 + tristate "Afatech AF9005 DVB-T USB1.1 support" + depends on DVB_USB && EXPERIMENTAL + select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE + select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE + help + Say Y here to support the Afatech AF9005 based DVB-T USB1.1 receiver + and the TerraTec Cinergy T USB XE (Rev.1) + +config DVB_USB_AF9005_REMOTE + tristate "Afatech AF9005 default remote control support" + depends on DVB_USB_AF9005 + help + Say Y here to support the default remote control decoding for the + Afatech AF9005 based receiver. + diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index 976f840..73ac0a9 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -55,4 +55,10 @@ dvb-usb-opera-objs = opera1.o obj-$(CONFIG_DVB_USB_OPERA1) += dvb-usb-opera.o -EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ +dvb-usb-af9005-objs = af9005.o af9005-fe.o +obj-$(CONFIG_DVB_USB_AF9005) += dvb-usb-af9005.o + +dvb-usb-af9005-remote-objs = af9005-remote.o +obj-$(CONFIG_DVB_USB_AF9005_REMOTE) += dvb-usb-af9005-remote.o + +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ diff --git a/drivers/media/dvb/dvb-usb/af9005-fe.c b/drivers/media/dvb/dvb-usb/af9005-fe.c new file mode 100644 index 0000000..7195c94 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/af9005-fe.c @@ -0,0 +1,1503 @@ +/* Frontend part of the Linux driver for the Afatech 9005 + * USB1.1 DVB-T receiver. + * + * Copyright (C) 2007 Luca Olivetti (luca@ventoso.org) + * + * Thanks to Afatech who kindly provided information. + * + * 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. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "af9005.h" +#include "af9005-script.h" +#include "mt2060.h" +#include "qt1010.h" +#include <asm/div64.h> + +struct af9005_fe_state { + struct dvb_usb_device *d; + struct dvb_frontend *tuner; + + fe_status_t stat; + + /* retraining parameters */ + u32 original_fcw; + u16 original_rf_top; + u16 original_if_top; + u16 original_if_min; + u16 original_aci0_if_top; + u16 original_aci1_if_top; + u16 original_aci0_if_min; + u8 original_if_unplug_th; + u8 original_rf_unplug_th; + u8 original_dtop_if_unplug_th; + u8 original_dtop_rf_unplug_th; + + /* statistics */ + u32 pre_vit_error_count; + u32 pre_vit_bit_count; + u32 ber; + u32 post_vit_error_count; + u32 post_vit_bit_count; + u32 unc; + u16 abort_count; + + int opened; + int strong; + unsigned long next_status_check; + struct dvb_frontend frontend; +}; + +static int af9005_write_word_agc(struct dvb_usb_device *d, u16 reghi, + u16 reglo, u8 pos, u8 len, u16 value) +{ + int ret; + u8 temp; + + if ((ret = af9005_write_ofdm_register(d, reglo, (u8) (value & 0xff)))) + return ret; + temp = (u8) ((value & 0x0300) >> 8); + return af9005_write_register_bits(d, reghi, pos, len, + (u8) ((value & 0x300) >> 8)); +} + +static int af9005_read_word_agc(struct dvb_usb_device *d, u16 reghi, + u16 reglo, u8 pos, u8 len, u16 * value) +{ + int ret; + u8 temp0, temp1; + + if ((ret = af9005_read_ofdm_register(d, reglo, &temp0))) + return ret; + if ((ret = af9005_read_ofdm_register(d, reghi, &temp1))) + return ret; + switch (pos) { + case 0: + *value = ((u16) (temp1 & 0x03) << 8) + (u16) temp0; + break; + case 2: + *value = ((u16) (temp1 & 0x0C) << 6) + (u16) temp0; + break; + case 4: + *value = ((u16) (temp1 & 0x30) << 4) + (u16) temp0; + break; + case 6: + *value = ((u16) (temp1 & 0xC0) << 2) + (u16) temp0; + break; + default: + err("invalid pos in read word agc"); + return -EINVAL; + } + return 0; + +} + +static int af9005_is_fecmon_available(struct dvb_frontend *fe, int *available) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + int ret; + u8 temp; + + *available = false; + + ret = af9005_read_register_bits(state->d, xd_p_fec_vtb_rsd_mon_en, + fec_vtb_rsd_mon_en_pos, + fec_vtb_rsd_mon_en_len, &temp); + if (ret) + return ret; + if (temp & 1) { + ret = + af9005_read_register_bits(state->d, + xd_p_reg_ofsm_read_rbc_en, + reg_ofsm_read_rbc_en_pos, + reg_ofsm_read_rbc_en_len, &temp); + if (ret) + return ret; + if ((temp & 1) == 0) + *available = true; + + } + return 0; +} + +static int af9005_get_post_vit_err_cw_count(struct dvb_frontend *fe, + u32 * post_err_count, + u32 * post_cw_count, + u16 * abort_count) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + int ret; + u32 err_count; + u32 cw_count; + u8 temp, temp0, temp1, temp2; + u16 loc_abort_count; + + *post_err_count = 0; + *post_cw_count = 0; + + /* check if error bit count is ready */ + ret = + af9005_read_register_bits(state->d, xd_r_fec_rsd_ber_rdy, + fec_rsd_ber_rdy_pos, fec_rsd_ber_rdy_len, + &temp); + if (ret) + return ret; + if (!temp) { + deb_info("rsd counter not ready\n"); + return 100; + } + /* get abort count */ + ret = + af9005_read_ofdm_register(state->d, + xd_r_fec_rsd_abort_packet_cnt_7_0, + &temp0); + if (ret) + return ret; + ret = + af9005_read_ofdm_register(state->d, + xd_r_fec_rsd_abort_packet_cnt_15_8, + &temp1); + if (ret) + return ret; + loc_abort_count = ((u16) temp1 << 8) + temp0; + + /* get error count */ + ret = + af9005_read_ofdm_register(state->d, xd_r_fec_rsd_bit_err_cnt_7_0, + &temp0); + if (ret) + return ret; + ret = + af9005_read_ofdm_register(state->d, xd_r_fec_rsd_bit_err_cnt_15_8, + &temp1); + if (ret) + return ret; + ret = + af9005_read_ofdm_register(state->d, xd_r_fec_rsd_bit_err_cnt_23_16, + &temp2); + if (ret) + return ret; + err_count = ((u32) temp2 << 16) + ((u32) temp1 << 8) + temp0; + *post_err_count = err_count - (u32) loc_abort_count *8 * 8; + + /* get RSD packet number */ + ret = + af9005_read_ofdm_register(state->d, xd_p_fec_rsd_packet_unit_7_0, + &temp0); + if (ret) + return ret; + ret = + af9005_read_ofdm_register(state->d, xd_p_fec_rsd_packet_unit_15_8, + &temp1); + if (ret) + return ret; + cw_count = ((u32) temp1 << 8) + temp0; + if (cw_count == 0) { + err("wrong RSD packet count"); + return -EIO; + } + deb_info("POST abort count %d err count %d rsd packets %d\n", + loc_abort_count, err_count, cw_count); + *post_cw_count = cw_count - (u32) loc_abort_count; + *abort_count = loc_abort_count; + return 0; + +} + +static int af9005_get_post_vit_ber(struct dvb_frontend *fe, + u32 * post_err_count, u32 * post_cw_count, + u16 * abort_count) +{ + u32 loc_cw_count = 0, loc_err_count; + u16 loc_abort_count; + int ret; + + ret = + af9005_get_post_vit_err_cw_count(fe, &loc_err_count, &loc_cw_count, + &loc_abort_count); + if (ret) + return ret; + *post_err_count = loc_err_count; + *post_cw_count = loc_cw_count * 204 * 8; + *abort_count = loc_abort_count; + + return 0; +} + +static int af9005_get_pre_vit_err_bit_count(struct dvb_frontend *fe, + u32 * pre_err_count, + u32 * pre_bit_count) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + u8 temp, temp0, temp1, temp2; + u32 super_frame_count, x, bits; + int ret; + + ret = + af9005_read_register_bits(state->d, xd_r_fec_vtb_ber_rdy, + fec_vtb_ber_rdy_pos, fec_vtb_ber_rdy_len, + &temp); + if (ret) + return ret; + if (!temp) { + deb_info("viterbi counter not ready\n"); + return 101; /* ERR_APO_VTB_COUNTER_NOT_READY; */ + } + ret = + af9005_read_ofdm_register(state->d, xd_r_fec_vtb_err_bit_cnt_7_0, + &temp0); + if (ret) + return ret; + ret = + af9005_read_ofdm_register(state->d, xd_r_fec_vtb_err_bit_cnt_15_8, + &temp1); + if (ret) + return ret; + ret = + af9005_read_ofdm_register(state->d, xd_r_fec_vtb_err_bit_cnt_23_16, + &temp2); + if (ret) + return ret; + *pre_err_count = ((u32) temp2 << 16) + ((u32) temp1 << 8) + temp0; + + ret = + af9005_read_ofdm_register(state->d, xd_p_fec_super_frm_unit_7_0, + &temp0); + if (ret) + return ret; + ret = + af9005_read_ofdm_register(state->d, xd_p_fec_super_frm_unit_15_8, + &temp1); + if (ret) + return ret; + super_frame_count = ((u32) temp1 << 8) + temp0; + if (super_frame_count == 0) { + deb_info("super frame count 0\n"); + return 102; + } + + /* read fft mode */ + ret = + af9005_read_register_bits(state->d, xd_g_reg_tpsd_txmod, + reg_tpsd_txmod_pos, reg_tpsd_txmod_len, + &temp); + if (ret) + return ret; + if (temp == 0) { + /* 2K */ + x = 1512; + } else if (temp == 1) { + /* 8k */ + x = 6048; + } else { + err("Invalid fft mode"); + return -EINVAL; + } + + /* read constellation mode */ + ret = + af9005_read_register_bits(state->d, xd_g_reg_tpsd_const, + reg_tpsd_const_pos, reg_tpsd_const_len, + &temp); + if (ret) + return ret; + switch (temp) { + case 0: /* QPSK */ + bits = 2; + break; + case 1: /* QAM_16 */ + bits = 4; + break; + case 2: /* QAM_64 */ + bits = 6; + break; + default: + err("invalid constellation mode"); + return -EINVAL; + } + *pre_bit_count = super_frame_count * 68 * 4 * x * bits; + deb_info("PRE err count %d frame count %d bit count %d\n", + *pre_err_count, super_frame_count, *pre_bit_count); + return 0; +} + +static int af9005_reset_pre_viterbi(struct dvb_frontend *fe) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + int ret; + + /* set super frame count to 1 */ + ret = + af9005_write_ofdm_register(state->d, xd_p_fec_super_frm_unit_7_0, + 1 & 0xff); + if (ret) + return ret; + af9005_write_ofdm_register(state->d, xd_p_fec_super_frm_unit_15_8, + 1 >> 8); + if (ret) + return ret; + /* reset pre viterbi error count */ + ret = + af9005_write_register_bits(state->d, xd_p_fec_vtb_ber_rst, + fec_vtb_ber_rst_pos, fec_vtb_ber_rst_len, + 1); + + return ret; +} + +static int af9005_reset_post_viterbi(struct dvb_frontend *fe) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + int ret; + + /* set packet unit */ + ret = + af9005_write_ofdm_register(state->d, xd_p_fec_rsd_packet_unit_7_0, + 10000 & 0xff); + if (ret) + return ret; + ret = + af9005_write_ofdm_register(state->d, xd_p_fec_rsd_packet_unit_15_8, + 10000 >> 8); + if (ret) + return ret; + /* reset post viterbi error count */ + ret = + af9005_write_register_bits(state->d, xd_p_fec_rsd_ber_rst, + fec_rsd_ber_rst_pos, fec_rsd_ber_rst_len, + 1); + + return ret; +} + +static int af9005_get_statistic(struct dvb_frontend *fe) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + int ret, fecavailable; + u64 numerator, denominator; + + deb_info("GET STATISTIC\n"); + ret = af9005_is_fecmon_available(fe, &fecavailable); + if (ret) + return ret; + if (!fecavailable) { + deb_info("fecmon not available\n"); + return 0; + } + + ret = af9005_get_pre_vit_err_bit_count(fe, &state->pre_vit_error_count, + &state->pre_vit_bit_count); + if (ret == 0) { + af9005_reset_pre_viterbi(fe); + if (state->pre_vit_bit_count > 0) { + /* according to v 0.0.4 of the dvb api ber should be a multiple + of 10E-9 so we have to multiply the error count by + 10E9=1000000000 */ + numerator = + (u64) state->pre_vit_error_count * (u64) 1000000000; + denominator = (u64) state->pre_vit_bit_count; + state->ber = do_div(numerator, denominator); + } else { + state->ber = 0xffffffff; + } + } + + ret = af9005_get_post_vit_ber(fe, &state->post_vit_error_count, + &state->post_vit_bit_count, + &state->abort_count); + if (ret == 0) { + ret = af9005_reset_post_viterbi(fe); + state->unc += state->abort_count; + if (ret) + return ret; + } + return 0; +} + +static int af9005_fe_refresh_state(struct dvb_frontend *fe) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + if (time_after(jiffies, state->next_status_check)) { + deb_info("REFRESH STATE\n"); + + /* statistics */ + if (af9005_get_statistic(fe)) + err("get_statistic_failed"); + state->next_status_check = jiffies + 250 * HZ / 1000; + } + return 0; +} + +static int af9005_fe_read_status(struct dvb_frontend *fe, fe_status_t * stat) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + u8 temp; + int ret; + + if (state->tuner == NULL) + return -ENODEV; + + *stat = 0; + ret = af9005_read_register_bits(state->d, xd_p_agc_lock, + agc_lock_pos, agc_lock_len, &temp); + if (ret) + return ret; + if (temp) + *stat |= FE_HAS_SIGNAL; + + ret = af9005_read_register_bits(state->d, xd_p_fd_tpsd_lock, + fd_tpsd_lock_pos, fd_tpsd_lock_len, + &temp); + if (ret) + return ret; + if (temp) + *stat |= FE_HAS_CARRIER; + + ret = af9005_read_register_bits(state->d, + xd_r_mp2if_sync_byte_locked, + mp2if_sync_byte_locked_pos, + mp2if_sync_byte_locked_pos, &temp); + if (ret) + return ret; + if (temp) + *stat |= FE_HAS_SYNC | FE_HAS_VITERBI | FE_HAS_LOCK; + if (state->opened) + af9005_led_control(state->d, *stat & FE_HAS_LOCK); + + ret = + af9005_read_register_bits(state->d, xd_p_reg_strong_sginal_detected, + reg_strong_sginal_detected_pos, + reg_strong_sginal_detected_len, &temp); + if (ret) + return ret; + if (temp != state->strong) { + deb_info("adjust for strong signal %d\n", temp); + state->strong = temp; + } + return 0; +} + +static int af9005_fe_read_ber(struct dvb_frontend *fe, u32 * ber) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + if (state->tuner == NULL) + return -ENODEV; + af9005_fe_refresh_state(fe); + *ber = state->ber; + return 0; +} + +static int af9005_fe_read_unc_blocks(struct dvb_frontend *fe, u32 * unc) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + if (state->tuner == NULL) + return -ENODEV; + af9005_fe_refresh_state(fe); + *unc = state->unc; + return 0; +} + +static int af9005_fe_read_signal_strength(struct dvb_frontend *fe, + u16 * strength) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + int ret; + u8 if_gain, rf_gain; + + if (state->tuner == NULL) + return -ENODEV; + ret = + af9005_read_ofdm_register(state->d, xd_r_reg_aagc_rf_gain, + &rf_gain); + if (ret) + return ret; + ret = + af9005_read_ofdm_register(state->d, xd_r_reg_aagc_if_gain, + &if_gain); + if (ret) + return ret; + /* this value has no real meaning, but i don't have the tables that relate + the rf and if gain with the dbm, so I just scale the value */ + *strength = (512 - rf_gain - if_gain) << 7; + return 0; +} + +static int af9005_fe_read_snr(struct dvb_frontend *fe, u16 * snr) +{ + /* the snr can be derived from the ber and the constellation + but I don't think this kind of complex calculations belong + in the driver. I may be wrong.... */ + return -ENOSYS; +} + +static int af9005_fe_program_cfoe(struct dvb_usb_device *d, fe_bandwidth_t bw) +{ + u8 temp0, temp1, temp2, temp3, buf[4]; + int ret; + u32 NS_coeff1_2048Nu; + u32 NS_coeff1_8191Nu; + u32 NS_coeff1_8192Nu; + u32 NS_coeff1_8193Nu; + u32 NS_coeff2_2k; + u32 NS_coeff2_8k; + + switch (bw) { + case BANDWIDTH_6_MHZ: + NS_coeff1_2048Nu = 0x2ADB6DC; + NS_coeff1_8191Nu = 0xAB7313; + NS_coeff1_8192Nu = 0xAB6DB7; + NS_coeff1_8193Nu = 0xAB685C; + NS_coeff2_2k = 0x156DB6E; + NS_coeff2_8k = 0x55B6DC; + break; + + case BANDWIDTH_7_MHZ: + NS_coeff1_2048Nu = 0x3200001; + NS_coeff1_8191Nu = 0xC80640; + NS_coeff1_8192Nu = 0xC80000; + NS_coeff1_8193Nu = 0xC7F9C0; + NS_coeff2_2k = 0x1900000; + NS_coeff2_8k = 0x640000; + break; + + case BANDWIDTH_8_MHZ: + NS_coeff1_2048Nu = 0x3924926; + NS_coeff1_8191Nu = 0xE4996E; + NS_coeff1_8192Nu = 0xE49249; + NS_coeff1_8193Nu = 0xE48B25; + NS_coeff2_2k = 0x1C92493; + NS_coeff2_8k = 0x724925; + break; + default: + err("Invalid bandwith %d.", bw); + return -EINVAL; + } + + /* + * write NS_coeff1_2048Nu + */ + + temp0 = (u8) (NS_coeff1_2048Nu & 0x000000FF); + temp1 = (u8) ((NS_coeff1_2048Nu & 0x0000FF00) >> 8); + temp2 = (u8) ((NS_coeff1_2048Nu & 0x00FF0000) >> 16); + temp3 = (u8) ((NS_coeff1_2048Nu & 0x03000000) >> 24); + + /* big endian to make 8051 happy */ + buf[0] = temp3; + buf[1] = temp2; + buf[2] = temp1; + buf[3] = temp0; + + /* cfoe_NS_2k_coeff1_25_24 */ + ret = af9005_write_ofdm_register(d, 0xAE00, buf[0]); + if (ret) + return ret; + + /* cfoe_NS_2k_coeff1_23_16 */ + ret = af9005_write_ofdm_register(d, 0xAE01, buf[1]); + if (ret) + return ret; + + /* cfoe_NS_2k_coeff1_15_8 */ + ret = af9005_write_ofdm_register(d, 0xAE02, buf[2]); + if (ret) + return ret; + + /* cfoe_NS_2k_coeff1_7_0 */ + ret = af9005_write_ofdm_register(d, 0xAE03, buf[3]); + if (ret) + return ret; + + /* + * write NS_coeff2_2k + */ + + temp0 = (u8) ((NS_coeff2_2k & 0x0000003F)); + temp1 = (u8) ((NS_coeff2_2k & 0x00003FC0) >> 6); + temp2 = (u8) ((NS_coeff2_2k & 0x003FC000) >> 14); + temp3 = (u8) ((NS_coeff2_2k & 0x01C00000) >> 22); + + /* big endian to make 8051 happy */ + buf[0] = temp3; + buf[1] = temp2; + buf[2] = temp1; + buf[3] = temp0; + + ret = af9005_write_ofdm_register(d, 0xAE04, buf[0]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE05, buf[1]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE06, buf[2]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE07, buf[3]); + if (ret) + return ret; + + /* + * write NS_coeff1_8191Nu + */ + + temp0 = (u8) ((NS_coeff1_8191Nu & 0x000000FF)); + temp1 = (u8) ((NS_coeff1_8191Nu & 0x0000FF00) >> 8); + temp2 = (u8) ((NS_coeff1_8191Nu & 0x00FFC000) >> 16); + temp3 = (u8) ((NS_coeff1_8191Nu & 0x03000000) >> 24); + + /* big endian to make 8051 happy */ + buf[0] = temp3; + buf[1] = temp2; + buf[2] = temp1; + buf[3] = temp0; + + ret = af9005_write_ofdm_register(d, 0xAE08, buf[0]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE09, buf[1]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE0A, buf[2]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE0B, buf[3]); + if (ret) + return ret; + + /* + * write NS_coeff1_8192Nu + */ + + temp0 = (u8) (NS_coeff1_8192Nu & 0x000000FF); + temp1 = (u8) ((NS_coeff1_8192Nu & 0x0000FF00) >> 8); + temp2 = (u8) ((NS_coeff1_8192Nu & 0x00FFC000) >> 16); + temp3 = (u8) ((NS_coeff1_8192Nu & 0x03000000) >> 24); + + /* big endian to make 8051 happy */ + buf[0] = temp3; + buf[1] = temp2; + buf[2] = temp1; + buf[3] = temp0; + + ret = af9005_write_ofdm_register(d, 0xAE0C, buf[0]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE0D, buf[1]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE0E, buf[2]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE0F, buf[3]); + if (ret) + return ret; + + /* + * write NS_coeff1_8193Nu + */ + + temp0 = (u8) ((NS_coeff1_8193Nu & 0x000000FF)); + temp1 = (u8) ((NS_coeff1_8193Nu & 0x0000FF00) >> 8); + temp2 = (u8) ((NS_coeff1_8193Nu & 0x00FFC000) >> 16); + temp3 = (u8) ((NS_coeff1_8193Nu & 0x03000000) >> 24); + + /* big endian to make 8051 happy */ + buf[0] = temp3; + buf[1] = temp2; + buf[2] = temp1; + buf[3] = temp0; + + ret = af9005_write_ofdm_register(d, 0xAE10, buf[0]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE11, buf[1]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE12, buf[2]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE13, buf[3]); + if (ret) + return ret; + + /* + * write NS_coeff2_8k + */ + + temp0 = (u8) ((NS_coeff2_8k & 0x0000003F)); + temp1 = (u8) ((NS_coeff2_8k & 0x00003FC0) >> 6); + temp2 = (u8) ((NS_coeff2_8k & 0x003FC000) >> 14); + temp3 = (u8) ((NS_coeff2_8k & 0x01C00000) >> 22); + + /* big endian to make 8051 happy */ + buf[0] = temp3; + buf[1] = temp2; + buf[2] = temp1; + buf[3] = temp0; + + ret = af9005_write_ofdm_register(d, 0xAE14, buf[0]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE15, buf[1]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE16, buf[2]); + if (ret) + return ret; + + ret = af9005_write_ofdm_register(d, 0xAE17, buf[3]); + return ret; + +} + +static int af9005_fe_select_bw(struct dvb_usb_device *d, fe_bandwidth_t bw) +{ + u8 temp; + switch (bw) { + case BANDWIDTH_6_MHZ: + temp = 0; + break; + case BANDWIDTH_7_MHZ: + temp = 1; + break; + case BANDWIDTH_8_MHZ: + temp = 2; + break; + default: + err("Invalid bandwith %d.", bw); + return -EINVAL; + } + return af9005_write_register_bits(d, xd_g_reg_bw, reg_bw_pos, + reg_bw_len, temp); +} + +static int af9005_fe_power(struct dvb_frontend *fe, int on) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + u8 temp = on; + int ret; + deb_info("power %s tuner\n", on ? "on" : "off"); + ret = af9005_send_command(state->d, 0x03, &temp, 1, NULL, 0); + return ret; +} + +static struct mt2060_config af9005_mt2060_config = { + 0xC0 +}; + +static struct qt1010_config af9005_qt1010_config = { + 0xC4 +}; + +static int af9005_fe_init(struct dvb_frontend *fe) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + struct dvb_usb_adapter *adap = fe->dvb->priv; + int ret, i, scriptlen; + u8 temp, temp0 = 0, temp1 = 0, temp2 = 0; + u8 buf[2]; + u16 if1; + + deb_info("in af9005_fe_init\n"); + + /* reset */ + deb_info("reset\n"); + if ((ret = + af9005_write_register_bits(state->d, xd_I2C_reg_ofdm_rst_en, + 4, 1, 0x01))) + return ret; + if ((ret = af9005_write_ofdm_register(state->d, APO_REG_RESET, 0))) + return ret; + /* clear ofdm reset */ + deb_info("clear ofdm reset\n"); + for (i = 0; i < 150; i++) { + if ((ret = + af9005_read_ofdm_register(state->d, + xd_I2C_reg_ofdm_rst, &temp))) + return ret; + if (temp & (regmask[reg_ofdm_rst_len - 1] << reg_ofdm_rst_pos)) + break; + msleep(10); + } + if (i == 150) + return -ETIMEDOUT; + + /*FIXME in the dump + write B200 A9 + write xd_g_reg_ofsm_clk 7 + read eepr c6 (2) + read eepr c7 (2) + misc ctrl 3 -> 1 + read eepr ca (6) + write xd_g_reg_ofsm_clk 0 + write B200 a1 + */ + ret = af9005_write_ofdm_register(state->d, 0xb200, 0xa9); + if (ret) + return ret; + ret = af9005_write_ofdm_register(state->d, xd_g_reg_ofsm_clk, 0x07); + if (ret) + return ret; + temp = 0x01; + ret = af9005_send_command(state->d, 0x03, &temp, 1, NULL, 0); + if (ret) + return ret; + ret = af9005_write_ofdm_register(state->d, xd_g_reg_ofsm_clk, 0x00); + if (ret) + return ret; + ret = af9005_write_ofdm_register(state->d, 0xb200, 0xa1); + if (ret) + return ret; + + temp = regmask[reg_ofdm_rst_len - 1] << reg_ofdm_rst_pos; + if ((ret = + af9005_write_register_bits(state->d, xd_I2C_reg_ofdm_rst, + reg_ofdm_rst_pos, reg_ofdm_rst_len, 1))) + return ret; + if ((ret = + af9005_write_register_bits(state->d, xd_I2C_reg_ofdm_rst, + reg_ofdm_rst_pos, reg_ofdm_rst_len, 0))) + return ret; + + if (ret) + return ret; + /* don't know what register aefc is, but this is what the windows driver does */ + ret = af9005_write_ofdm_register(state->d, 0xaefc, 0); + if (ret) + return ret; + + /* set stand alone chip */ + deb_info("set stand alone chip\n"); + if ((ret = + af9005_write_register_bits(state->d, xd_p_reg_dca_stand_alone, + reg_dca_stand_alone_pos, + reg_dca_stand_alone_len, 1))) + return ret; + + /* set dca upper & lower chip */ + deb_info("set dca upper & lower chip\n"); + if ((ret = + af9005_write_register_bits(state->d, xd_p_reg_dca_upper_chip, + reg_dca_upper_chip_pos, + reg_dca_upper_chip_len, 0))) + return ret; + if ((ret = + af9005_write_register_bits(state->d, xd_p_reg_dca_lower_chip, + reg_dca_lower_chip_pos, + reg_dca_lower_chip_len, 0))) + return ret; + + /* set 2wire master clock to 0x14 (for 60KHz) */ + deb_info("set 2wire master clock to 0x14 (for 60KHz)\n"); + if ((ret = + af9005_write_ofdm_register(state->d, xd_I2C_i2c_m_period, 0x14))) + return ret; + + /* clear dca enable chip */ + deb_info("clear dca enable chip\n"); + if ((ret = + af9005_write_register_bits(state->d, xd_p_reg_dca_en, + reg_dca_en_pos, reg_dca_en_len, 0))) + return ret; + /* FIXME these are register bits, but I don't know which ones */ + ret = af9005_write_ofdm_register(state->d, 0xa16c, 1); + if (ret) + return ret; + ret = af9005_write_ofdm_register(state->d, 0xa3c1, 0); + if (ret) + return ret; + + /* init other parameters: program cfoe and select bandwith */ + deb_info("program cfoe\n"); + if ((ret = af9005_fe_program_cfoe(state->d, BANDWIDTH_6_MHZ))) + return ret; + /* set read-update bit for constellation */ + deb_info("set read-update bit for constellation\n"); + if ((ret = + af9005_write_register_bits(state->d, xd_p_reg_feq_read_update, + reg_feq_read_update_pos, + reg_feq_read_update_len, 1))) + return ret; + + /* sample code has a set MPEG TS code here + but sniffing reveals that it doesn't do it */ + + /* set read-update bit to 1 for DCA constellation */ + deb_info("set read-update bit 1 for DCA constellation\n"); + if ((ret = + af9005_write_register_bits(state->d, xd_p_reg_dca_read_update, + reg_dca_read_update_pos, + reg_dca_read_update_len, 1))) + return ret; + + /* enable fec monitor */ + deb_info("enable fec monitor\n"); + if ((ret = + af9005_write_register_bits(state->d, xd_p_fec_vtb_rsd_mon_en, + fec_vtb_rsd_mon_en_pos, + fec_vtb_rsd_mon_en_len, 1))) + return ret; + + /* FIXME should be register bits, I don't know which ones */ + ret = af9005_write_ofdm_register(state->d, 0xa601, 0); + + /* set api_retrain_never_freeze */ + deb_info("set api_retrain_never_freeze\n"); + if ((ret = af9005_write_ofdm_register(state->d, 0xaefb, 0x01))) + return ret; + + /* load init script */ + deb_info("load init script\n"); + scriptlen = sizeof(script) / sizeof(RegDesc); + for (i = 0; i < scriptlen; i++) { + if ((ret = + af9005_write_register_bits(state->d, script[i].reg, + script[i].pos, + script[i].len, script[i].val))) + return ret; + /* save 3 bytes of original fcw */ + if (script[i].reg == 0xae18) + temp2 = script[i].val; + if (script[i].reg == 0xae19) + temp1 = script[i].val; + if (script[i].reg == 0xae1a) + temp0 = script[i].val; + + /* save original unplug threshold */ + if (script[i].reg == xd_p_reg_unplug_th) + state->original_if_unplug_th = script[i].val; + if (script[i].reg == xd_p_reg_unplug_rf_gain_th) + state->original_rf_unplug_th = script[i].val; + if (script[i].reg == xd_p_reg_unplug_dtop_if_gain_th) + state->original_dtop_if_unplug_th = script[i].val; + if (script[i].reg == xd_p_reg_unplug_dtop_rf_gain_th) + state->original_dtop_rf_unplug_th = script[i].val; + + } + state->original_fcw = + ((u32) temp2 << 16) + ((u32) temp1 << 8) + (u32) temp0; + + + /* save original TOPs */ + deb_info("save original TOPs\n"); + + /* RF TOP */ + ret = + af9005_read_word_agc(state->d, + xd_p_reg_aagc_rf_top_numerator_9_8, + xd_p_reg_aagc_rf_top_numerator_7_0, 0, 2, + &state->original_rf_top); + if (ret) + return ret; + + /* IF TOP */ + ret = + af9005_read_word_agc(state->d, + xd_p_reg_aagc_if_top_numerator_9_8, + xd_p_reg_aagc_if_top_numerator_7_0, 0, 2, + &state->original_if_top); + if (ret) + return ret; + + /* ACI 0 IF TOP */ + ret = + af9005_read_word_agc(state->d, 0xA60E, 0xA60A, 4, 2, + &state->original_aci0_if_top); + if (ret) + return ret; + + /* ACI 1 IF TOP */ + ret = + af9005_read_word_agc(state->d, 0xA60E, 0xA60B, 6, 2, + &state->original_aci1_if_top); + if (ret) + return ret; + + /* attach tuner and init */ + if (state->tuner == NULL) { + /* read tuner and board id from eeprom */ + ret = af9005_read_eeprom(adap->dev, 0xc6, buf, 2); + if (ret) { + err("Impossible to read EEPROM\n"); + return ret; + } + deb_info("Tuner id %d, board id %d\n", buf[0], buf[1]); + switch (buf[0]) { + case 2: /* MT2060 */ + /* read if1 from eeprom */ + ret = af9005_read_eeprom(adap->dev, 0xc8, buf, 2); + if (ret) { + err("Impossible to read EEPROM\n"); + return ret; + } + if1 = (u16) (buf[0] << 8) + buf[1]; + state->tuner = + dvb_attach(mt2060_attach, fe, &adap->dev->i2c_adap, + &af9005_mt2060_config, if1); + if (state->tuner == NULL) { + deb_info("MT2060 attach failed\n"); + return -ENODEV; + } + break; + case 3: /* QT1010 */ + case 9: /* QT1010B */ + state->tuner = + dvb_attach(qt1010_attach, fe, &adap->dev->i2c_adap, + &af9005_qt1010_config); + if (state->tuner == NULL) { + deb_info("QT1010 attach failed\n"); + return -ENODEV; + } + break; + default: + err("Unsupported tuner type %d", buf[0]); + return -ENODEV; + } + ret = state->tuner->ops.tuner_ops.init(state->tuner); + if (ret) + return ret; + } + + deb_info("profit!\n"); + return 0; +} + +static int af9005_fe_sleep(struct dvb_frontend *fe) +{ + return af9005_fe_power(fe, 0); +} + +static int af9005_ts_bus_ctrl(struct dvb_frontend *fe, int acquire) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + + if (acquire) { + state->opened++; + } else { + + state->opened--; + if (!state->opened) + af9005_led_control(state->d, 0); + } + return 0; +} + +static int af9005_fe_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fep) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + int ret; + u8 temp, temp0, temp1, temp2; + + deb_info("af9005_fe_set_frontend freq %d bw %d\n", fep->frequency, + fep->u.ofdm.bandwidth); + if (state->tuner == NULL) { + err("Tuner not attached"); + return -ENODEV; + } + + deb_info("turn off led\n"); + /* not in the log */ + ret = af9005_led_control(state->d, 0); + if (ret) + return ret; + /* not sure about the bits */ + ret = af9005_write_register_bits(state->d, XD_MP2IF_MISC, 2, 1, 0); + if (ret) + return ret; + + /* set FCW to default value */ + deb_info("set FCW to default value\n"); + temp0 = (u8) (state->original_fcw & 0x000000ff); + temp1 = (u8) ((state->original_fcw & 0x0000ff00) >> 8); + temp2 = (u8) ((state->original_fcw & 0x00ff0000) >> 16); + ret = af9005_write_ofdm_register(state->d, 0xae1a, temp0); + if (ret) + return ret; + ret = af9005_write_ofdm_register(state->d, 0xae19, temp1); + if (ret) + return ret; + ret = af9005_write_ofdm_register(state->d, 0xae18, temp2); + if (ret) + return ret; + + /* restore original TOPs */ + deb_info("restore original TOPs\n"); + ret = + af9005_write_word_agc(state->d, + xd_p_reg_aagc_rf_top_numerator_9_8, + xd_p_reg_aagc_rf_top_numerator_7_0, 0, 2, + state->original_rf_top); + if (ret) + return ret; + ret = + af9005_write_word_agc(state->d, + xd_p_reg_aagc_if_top_numerator_9_8, + xd_p_reg_aagc_if_top_numerator_7_0, 0, 2, + state->original_if_top); + if (ret) + return ret; + ret = + af9005_write_word_agc(state->d, 0xA60E, 0xA60A, 4, 2, + state->original_aci0_if_top); + if (ret) + return ret; + ret = + af9005_write_word_agc(state->d, 0xA60E, 0xA60B, 6, 2, + state->original_aci1_if_top); + if (ret) + return ret; + + /* select bandwith */ + deb_info("select bandwidth"); + ret = af9005_fe_select_bw(state->d, fep->u.ofdm.bandwidth); + if (ret) + return ret; + ret = af9005_fe_program_cfoe(state->d, fep->u.ofdm.bandwidth); + if (ret) + return ret; + + /* clear easy mode flag */ + deb_info("clear easy mode flag\n"); + ret = af9005_write_ofdm_register(state->d, 0xaefd, 0); + if (ret) + return ret; + + /* set unplug threshold to original value */ + deb_info("set unplug threshold to original value\n"); + ret = + af9005_write_ofdm_register(state->d, xd_p_reg_unplug_th, + state->original_if_unplug_th); + if (ret) + return ret; + /* set tuner */ + deb_info("set tuner\n"); + ret = state->tuner->ops.tuner_ops.set_params(state->tuner, fep); + if (ret) + return ret; + + /* trigger ofsm */ + deb_info("trigger ofsm\n"); + temp = 0; + ret = af9005_write_tuner_registers(state->d, 0xffff, &temp, 1); + if (ret) + return ret; + + /* clear retrain and freeze flag */ + deb_info("clear retrain and freeze flag\n"); + ret = + af9005_write_register_bits(state->d, + xd_p_reg_api_retrain_request, + reg_api_retrain_request_pos, 2, 0); + if (ret) + return ret; + + /* reset pre viterbi and post viterbi registers and statistics */ + af9005_reset_pre_viterbi(fe); + af9005_reset_post_viterbi(fe); + state->pre_vit_error_count = 0; + state->pre_vit_bit_count = 0; + state->ber = 0; + state->post_vit_error_count = 0; + /* state->unc = 0; commented out since it should be ever increasing */ + state->abort_count = 0; + + state->next_status_check = jiffies; + state->strong = -1; + + return 0; +} + +static int af9005_fe_get_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fep) +{ + struct af9005_fe_state *state = fe->demodulator_priv; + int ret; + u8 temp; + + /* mode */ + ret = + af9005_read_register_bits(state->d, xd_g_reg_tpsd_const, + reg_tpsd_const_pos, reg_tpsd_const_len, + &temp); + if (ret) + return ret; + deb_info("===== fe_get_frontend ==============\n"); + deb_info("CONSTELLATION "); + switch (temp) { + case 0: + fep->u.ofdm.constellation = QPSK; + deb_info("QPSK\n"); + break; + case 1: + fep->u.ofdm.constellation = QAM_16; + deb_info("QAM_16\n"); + break; + case 2: + fep->u.ofdm.constellation = QAM_64; + deb_info("QAM_64\n"); + break; + } + + /* tps hierarchy and alpha value */ + ret = + af9005_read_register_bits(state->d, xd_g_reg_tpsd_hier, + reg_tpsd_hier_pos, reg_tpsd_hier_len, + &temp); + if (ret) + return ret; + deb_info("HIERARCHY "); + switch (temp) { + case 0: + fep->u.ofdm.hierarchy_information = HIERARCHY_NONE; + deb_info("NONE\n"); + break; + case 1: + fep->u.ofdm.hierarchy_information = HIERARCHY_1; + deb_info("1\n"); + break; + case 2: + fep->u.ofdm.hierarchy_information = HIERARCHY_2; + deb_info("2\n"); + break; + case 3: + fep->u.ofdm.hierarchy_information = HIERARCHY_4; + deb_info("4\n"); + break; + } + + /* high/low priority */ + ret = + af9005_read_register_bits(state->d, xd_g_reg_dec_pri, + reg_dec_pri_pos, reg_dec_pri_len, &temp); + if (ret) + return ret; + /* if temp is set = high priority */ + deb_info("PRIORITY %s\n", temp ? "high" : "low"); + + /* high coderate */ + ret = + af9005_read_register_bits(state->d, xd_g_reg_tpsd_hpcr, + reg_tpsd_hpcr_pos, reg_tpsd_hpcr_len, + &temp); + if (ret) + return ret; + deb_info("CODERATE HP "); + switch (temp) { + case 0: + fep->u.ofdm.code_rate_HP = FEC_1_2; + deb_info("FEC_1_2\n"); + break; + case 1: + fep->u.ofdm.code_rate_HP = FEC_2_3; + deb_info("FEC_2_3\n"); + break; + case 2: + fep->u.ofdm.code_rate_HP = FEC_3_4; + deb_info("FEC_3_4\n"); + break; + case 3: + fep->u.ofdm.code_rate_HP = FEC_5_6; + deb_info("FEC_5_6\n"); + break; + case 4: + fep->u.ofdm.code_rate_HP = FEC_7_8; + deb_info("FEC_7_8\n"); + break; + } + + /* low coderate */ + ret = + af9005_read_register_bits(state->d, xd_g_reg_tpsd_lpcr, + reg_tpsd_lpcr_pos, reg_tpsd_lpcr_len, + &temp); + if (ret) + return ret; + deb_info("CODERATE LP "); + switch (temp) { + case 0: + fep->u.ofdm.code_rate_LP = FEC_1_2; + deb_info("FEC_1_2\n"); + break; + case 1: + fep->u.ofdm.code_rate_LP = FEC_2_3; + deb_info("FEC_2_3\n"); + break; + case 2: + fep->u.ofdm.code_rate_LP = FEC_3_4; + deb_info("FEC_3_4\n"); + break; + case 3: + fep->u.ofdm.code_rate_LP = FEC_5_6; + deb_info("FEC_5_6\n"); + break; + case 4: + fep->u.ofdm.code_rate_LP = FEC_7_8; + deb_info("FEC_7_8\n"); + break; + } + + /* guard interval */ + ret = + af9005_read_register_bits(state->d, xd_g_reg_tpsd_gi, + reg_tpsd_gi_pos, reg_tpsd_gi_len, &temp); + if (ret) + return ret; + deb_info("GUARD INTERVAL "); + switch (temp) { + case 0: + fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; + deb_info("1_32\n"); + break; + case 1: + fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; + deb_info("1_16\n"); + break; + case 2: + fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; + deb_info("1_8\n"); + break; + case 3: + fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; + deb_info("1_4\n"); + break; + } + + /* fft */ + ret = + af9005_read_register_bits(state->d, xd_g_reg_tpsd_txmod, + reg_tpsd_txmod_pos, reg_tpsd_txmod_len, + &temp); + if (ret) + return ret; + deb_info("TRANSMISSION MODE "); + switch (temp) { + case 0: + fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; + deb_info("2K\n"); + break; + case 1: + fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; + deb_info("8K\n"); + break; + } + + /* bandwidth */ + ret = + af9005_read_register_bits(state->d, xd_g_reg_bw, reg_bw_pos, + reg_bw_len, &temp); + deb_info("BANDWIDTH "); + switch (temp) { + case 0: + fep->u.ofdm.bandwidth = BANDWIDTH_6_MHZ; + deb_info("6\n"); + break; + case 1: + fep->u.ofdm.bandwidth = BANDWIDTH_7_MHZ; + deb_info("7\n"); + break; + case 2: + fep->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; + deb_info("8\n"); + break; + } + return 0; +} + +static void af9005_fe_release(struct dvb_frontend *fe) +{ + struct af9005_fe_state *state = + (struct af9005_fe_state *)fe->demodulator_priv; + if (state->tuner != NULL && state->tuner->ops.tuner_ops.release != NULL) { + state->tuner->ops.tuner_ops.release(state->tuner); +#ifdef CONFIG_DVB_CORE_ATTACH + symbol_put_addr(state->tuner->ops.tuner_ops.release); +#endif + } + kfree(state); +} + +static struct dvb_frontend_ops af9005_fe_ops; + +struct dvb_frontend *af9005_fe_attach(struct dvb_usb_device *d) +{ + struct af9005_fe_state *state = NULL; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct af9005_fe_state), GFP_KERNEL); + if (state == NULL) + goto error; + + deb_info("attaching frontend af9005\n"); + + state->d = d; + state->tuner = NULL; + state->opened = 0; + + memcpy(&state->frontend.ops, &af9005_fe_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + return &state->frontend; + error: + return NULL; +} + +static struct dvb_frontend_ops af9005_fe_ops = { + .info = { + .name = "AF9005 USB DVB-T", + .type = FE_OFDM, + .frequency_min = 44250000, + .frequency_max = 867250000, + .frequency_stepsize = 250000, + .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_RECOVER | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = af9005_fe_release, + + .init = af9005_fe_init, + .sleep = af9005_fe_sleep, + .ts_bus_ctrl = af9005_ts_bus_ctrl, + + .set_frontend = af9005_fe_set_frontend, + .get_frontend = af9005_fe_get_frontend, + + .read_status = af9005_fe_read_status, + .read_ber = af9005_fe_read_ber, + .read_signal_strength = af9005_fe_read_signal_strength, + .read_snr = af9005_fe_read_snr, + .read_ucblocks = af9005_fe_read_unc_blocks, +}; diff --git a/drivers/media/dvb/dvb-usb/af9005-remote.c b/drivers/media/dvb/dvb-usb/af9005-remote.c new file mode 100644 index 0000000..ff00c0e --- /dev/null +++ b/drivers/media/dvb/dvb-usb/af9005-remote.c @@ -0,0 +1,157 @@ +/* DVB USB compliant Linux driver for the Afatech 9005 + * USB1.1 DVB-T receiver. + * + * Standard remote decode function + * + * Copyright (C) 2007 Luca Olivetti (luca@ventoso.org) + * + * Thanks to Afatech who kindly provided information. + * + * 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. + * + * see Documentation/dvb/REDME.dvb-usb for more information + */ +#include "af9005.h" +/* debug */ +int dvb_usb_af9005_remote_debug; +module_param_named(debug, dvb_usb_af9005_remote_debug, int, 0644); +MODULE_PARM_DESC(debug, + "enable (1) or disable (0) debug messages." + DVB_USB_DEBUG_STATUS); + +#define deb_decode(args...) dprintk(dvb_usb_af9005_remote_debug,0x01,args) + +struct dvb_usb_rc_key af9005_rc_keys[] = { + + {0x01, 0xb7, KEY_POWER}, + {0x01, 0xa7, KEY_VOLUMEUP}, + {0x01, 0x87, KEY_CHANNELUP}, + {0x01, 0x7f, KEY_MUTE}, + {0x01, 0xbf, KEY_VOLUMEDOWN}, + {0x01, 0x3f, KEY_CHANNELDOWN}, + {0x01, 0xdf, KEY_1}, + {0x01, 0x5f, KEY_2}, + {0x01, 0x9f, KEY_3}, + {0x01, 0x1f, KEY_4}, + {0x01, 0xef, KEY_5}, + {0x01, 0x6f, KEY_6}, + {0x01, 0xaf, KEY_7}, + {0x01, 0x27, KEY_8}, + {0x01, 0x07, KEY_9}, + {0x01, 0xcf, KEY_ZOOM}, + {0x01, 0x4f, KEY_0}, + {0x01, 0x8f, KEY_GOTO}, /* marked jump on the remote */ + + {0x00, 0xbd, KEY_POWER}, + {0x00, 0x7d, KEY_VOLUMEUP}, + {0x00, 0xfd, KEY_CHANNELUP}, + {0x00, 0x9d, KEY_MUTE}, + {0x00, 0x5d, KEY_VOLUMEDOWN}, + {0x00, 0xdd, KEY_CHANNELDOWN}, + {0x00, 0xad, KEY_1}, + {0x00, 0x6d, KEY_2}, + {0x00, 0xed, KEY_3}, + {0x00, 0x8d, KEY_4}, + {0x00, 0x4d, KEY_5}, + {0x00, 0xcd, KEY_6}, + {0x00, 0xb5, KEY_7}, + {0x00, 0x75, KEY_8}, + {0x00, 0xf5, KEY_9}, + {0x00, 0x95, KEY_ZOOM}, + {0x00, 0x55, KEY_0}, + {0x00, 0xd5, KEY_GOTO}, /* marked jump on the remote */ +}; + +int af9005_rc_keys_size = ARRAY_SIZE(af9005_rc_keys); + +static int repeatable_keys[] = { + KEY_VOLUMEUP, + KEY_VOLUMEDOWN, + KEY_CHANNELUP, + KEY_CHANNELDOWN +}; + +int af9005_rc_decode(struct dvb_usb_device *d, u8 * data, int len, u32 * event, + int *state) +{ + u16 mark, space; + u32 result; + u8 cust, dat, invdat; + int i; + + if (len >= 6) { + mark = (u16) (data[0] << 8) + data[1]; + space = (u16) (data[2] << 8) + data[3]; + if (space * 3 < mark) { + for (i = 0; i < ARRAY_SIZE(repeatable_keys); i++) { + if (d->last_event == repeatable_keys[i]) { + *state = REMOTE_KEY_REPEAT; + *event = d->last_event; + deb_decode("repeat key, event %x\n", + *event); + return 0; + } + } + deb_decode("repeated key ignored (non repeatable)\n"); + return 0; + } else if (len >= 33 * 4) { /*32 bits + start code */ + result = 0; + for (i = 4; i < 4 + 32 * 4; i += 4) { + result <<= 1; + mark = (u16) (data[i] << 8) + data[i + 1]; + mark >>= 1; + space = (u16) (data[i + 2] << 8) + data[i + 3]; + space >>= 1; + if (mark * 2 > space) + result += 1; + } + deb_decode("key pressed, raw value %x\n", result); + if ((result & 0xff000000) != 0xfe000000) { + deb_decode + ("doesn't start with 0xfe, ignored\n"); + return 0; + } + cust = (result >> 16) & 0xff; + dat = (result >> 8) & 0xff; + invdat = (~result) & 0xff; + if (dat != invdat) { + deb_decode("code != inverted code\n"); + return 0; + } + for (i = 0; i < af9005_rc_keys_size; i++) { + if (af9005_rc_keys[i].custom == cust + && af9005_rc_keys[i].data == dat) { + *event = af9005_rc_keys[i].event; + *state = REMOTE_KEY_PRESSED; + deb_decode + ("key pressed, event %x\n", *event); + return 0; + } + } + deb_decode("not found in table\n"); + } + } + return 0; +} + +EXPORT_SYMBOL(af9005_rc_keys); +EXPORT_SYMBOL(af9005_rc_keys_size); +EXPORT_SYMBOL(af9005_rc_decode); + +MODULE_AUTHOR("Luca Olivetti <luca@ventoso.org>"); +MODULE_DESCRIPTION + ("Standard remote control decoder for Afatech 9005 DVB-T USB1.1 stick"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/af9005-script.h b/drivers/media/dvb/dvb-usb/af9005-script.h new file mode 100644 index 0000000..6eeaae5 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/af9005-script.h @@ -0,0 +1,203 @@ +/* +File automatically generated by createinit.py using data +extracted from AF05BDA.sys (windows driver): + +dd if=AF05BDA.sys of=initsequence bs=1 skip=88316 count=1110 +python createinit.py > af9005-script.h + +*/ + +typedef struct { + u16 reg; + u8 pos; + u8 len; + u8 val; +} RegDesc; + +RegDesc script[] = { + {0xa180, 0x0, 0x8, 0xa}, + {0xa181, 0x0, 0x8, 0xd7}, + {0xa182, 0x0, 0x8, 0xa3}, + {0xa0a0, 0x0, 0x8, 0x0}, + {0xa0a1, 0x0, 0x5, 0x0}, + {0xa0a1, 0x5, 0x1, 0x1}, + {0xa0c0, 0x0, 0x4, 0x1}, + {0xa20e, 0x4, 0x4, 0xa}, + {0xa20f, 0x0, 0x8, 0x40}, + {0xa210, 0x0, 0x8, 0x8}, + {0xa32a, 0x0, 0x4, 0xa}, + {0xa32c, 0x0, 0x8, 0x20}, + {0xa32b, 0x0, 0x8, 0x15}, + {0xa1a0, 0x1, 0x1, 0x1}, + {0xa000, 0x0, 0x1, 0x1}, + {0xa000, 0x1, 0x1, 0x0}, + {0xa001, 0x1, 0x1, 0x1}, + {0xa001, 0x0, 0x1, 0x0}, + {0xa001, 0x5, 0x1, 0x0}, + {0xa00e, 0x0, 0x5, 0x10}, + {0xa00f, 0x0, 0x3, 0x4}, + {0xa00f, 0x3, 0x3, 0x5}, + {0xa010, 0x0, 0x3, 0x4}, + {0xa010, 0x3, 0x3, 0x5}, + {0xa016, 0x4, 0x4, 0x3}, + {0xa01f, 0x0, 0x6, 0xa}, + {0xa020, 0x0, 0x6, 0xa}, + {0xa2bc, 0x0, 0x1, 0x1}, + {0xa2bc, 0x5, 0x1, 0x1}, + {0xa015, 0x0, 0x8, 0x50}, + {0xa016, 0x0, 0x1, 0x0}, + {0xa02a, 0x0, 0x8, 0x50}, + {0xa029, 0x0, 0x8, 0x4b}, + {0xa614, 0x0, 0x8, 0x46}, + {0xa002, 0x0, 0x5, 0x19}, + {0xa003, 0x0, 0x5, 0x1a}, + {0xa004, 0x0, 0x5, 0x19}, + {0xa005, 0x0, 0x5, 0x1a}, + {0xa008, 0x0, 0x8, 0x69}, + {0xa009, 0x0, 0x2, 0x2}, + {0xae1b, 0x0, 0x8, 0x69}, + {0xae1c, 0x0, 0x8, 0x2}, + {0xae1d, 0x0, 0x8, 0x2a}, + {0xa022, 0x0, 0x8, 0xaa}, + {0xa006, 0x0, 0x8, 0xc8}, + {0xa007, 0x0, 0x2, 0x0}, + {0xa00c, 0x0, 0x8, 0xba}, + {0xa00d, 0x0, 0x2, 0x2}, + {0xa608, 0x0, 0x8, 0xba}, + {0xa60e, 0x0, 0x2, 0x2}, + {0xa609, 0x0, 0x8, 0x80}, + {0xa60e, 0x2, 0x2, 0x3}, + {0xa00a, 0x0, 0x8, 0xb6}, + {0xa00b, 0x0, 0x2, 0x0}, + {0xa011, 0x0, 0x8, 0xb9}, + {0xa012, 0x0, 0x2, 0x0}, + {0xa013, 0x0, 0x8, 0xbd}, + {0xa014, 0x0, 0x2, 0x2}, + {0xa366, 0x0, 0x1, 0x1}, + {0xa2bc, 0x3, 0x1, 0x0}, + {0xa2bd, 0x0, 0x8, 0xa}, + {0xa2be, 0x0, 0x8, 0x14}, + {0xa2bf, 0x0, 0x8, 0x8}, + {0xa60a, 0x0, 0x8, 0xbd}, + {0xa60e, 0x4, 0x2, 0x2}, + {0xa60b, 0x0, 0x8, 0x86}, + {0xa60e, 0x6, 0x2, 0x3}, + {0xa001, 0x2, 0x2, 0x1}, + {0xa1c7, 0x0, 0x8, 0xf5}, + {0xa03d, 0x0, 0x8, 0xb1}, + {0xa616, 0x0, 0x8, 0xff}, + {0xa617, 0x0, 0x8, 0xad}, + {0xa618, 0x0, 0x8, 0xad}, + {0xa61e, 0x3, 0x1, 0x1}, + {0xae1a, 0x0, 0x8, 0x0}, + {0xae19, 0x0, 0x8, 0xc8}, + {0xae18, 0x0, 0x8, 0x61}, + {0xa140, 0x0, 0x8, 0x0}, + {0xa141, 0x0, 0x8, 0xc8}, + {0xa142, 0x0, 0x7, 0x61}, + {0xa023, 0x0, 0x8, 0xff}, + {0xa021, 0x0, 0x8, 0xad}, + {0xa026, 0x0, 0x1, 0x0}, + {0xa024, 0x0, 0x8, 0xff}, + {0xa025, 0x0, 0x8, 0xff}, + {0xa1c8, 0x0, 0x8, 0xf}, + {0xa2bc, 0x1, 0x1, 0x0}, + {0xa60c, 0x0, 0x4, 0x5}, + {0xa60c, 0x4, 0x4, 0x6}, + {0xa60d, 0x0, 0x8, 0xa}, + {0xa371, 0x0, 0x1, 0x1}, + {0xa366, 0x1, 0x3, 0x7}, + {0xa338, 0x0, 0x8, 0x10}, + {0xa339, 0x0, 0x6, 0x7}, + {0xa33a, 0x0, 0x6, 0x1f}, + {0xa33b, 0x0, 0x8, 0xf6}, + {0xa33c, 0x3, 0x5, 0x4}, + {0xa33d, 0x4, 0x4, 0x0}, + {0xa33d, 0x1, 0x1, 0x1}, + {0xa33d, 0x2, 0x1, 0x1}, + {0xa33d, 0x3, 0x1, 0x1}, + {0xa16d, 0x0, 0x4, 0xf}, + {0xa161, 0x0, 0x5, 0x5}, + {0xa162, 0x0, 0x4, 0x5}, + {0xa165, 0x0, 0x8, 0xff}, + {0xa166, 0x0, 0x8, 0x9c}, + {0xa2c3, 0x0, 0x4, 0x5}, + {0xa61a, 0x0, 0x6, 0xf}, + {0xb200, 0x0, 0x8, 0xa1}, + {0xb201, 0x0, 0x8, 0x7}, + {0xa093, 0x0, 0x1, 0x0}, + {0xa093, 0x1, 0x5, 0xf}, + {0xa094, 0x0, 0x8, 0xff}, + {0xa095, 0x0, 0x8, 0xf}, + {0xa080, 0x2, 0x5, 0x3}, + {0xa081, 0x0, 0x4, 0x0}, + {0xa081, 0x4, 0x4, 0x9}, + {0xa082, 0x0, 0x5, 0x1f}, + {0xa08d, 0x0, 0x8, 0x1}, + {0xa083, 0x0, 0x8, 0x32}, + {0xa084, 0x0, 0x1, 0x0}, + {0xa08e, 0x0, 0x8, 0x3}, + {0xa085, 0x0, 0x8, 0x32}, + {0xa086, 0x0, 0x3, 0x0}, + {0xa087, 0x0, 0x8, 0x6e}, + {0xa088, 0x0, 0x5, 0x15}, + {0xa089, 0x0, 0x8, 0x0}, + {0xa08a, 0x0, 0x5, 0x19}, + {0xa08b, 0x0, 0x8, 0x92}, + {0xa08c, 0x0, 0x5, 0x1c}, + {0xa120, 0x0, 0x8, 0x0}, + {0xa121, 0x0, 0x5, 0x10}, + {0xa122, 0x0, 0x8, 0x0}, + {0xa123, 0x0, 0x7, 0x40}, + {0xa123, 0x7, 0x1, 0x0}, + {0xa124, 0x0, 0x8, 0x13}, + {0xa125, 0x0, 0x7, 0x10}, + {0xa1c0, 0x0, 0x8, 0x0}, + {0xa1c1, 0x0, 0x5, 0x4}, + {0xa1c2, 0x0, 0x8, 0x0}, + {0xa1c3, 0x0, 0x5, 0x10}, + {0xa1c3, 0x5, 0x3, 0x0}, + {0xa1c4, 0x0, 0x6, 0x0}, + {0xa1c5, 0x0, 0x7, 0x10}, + {0xa100, 0x0, 0x8, 0x0}, + {0xa101, 0x0, 0x5, 0x10}, + {0xa102, 0x0, 0x8, 0x0}, + {0xa103, 0x0, 0x7, 0x40}, + {0xa103, 0x7, 0x1, 0x0}, + {0xa104, 0x0, 0x8, 0x18}, + {0xa105, 0x0, 0x7, 0xa}, + {0xa106, 0x0, 0x8, 0x20}, + {0xa107, 0x0, 0x8, 0x40}, + {0xa108, 0x0, 0x4, 0x0}, + {0xa38c, 0x0, 0x8, 0xfc}, + {0xa38d, 0x0, 0x8, 0x0}, + {0xa38e, 0x0, 0x8, 0x7e}, + {0xa38f, 0x0, 0x8, 0x0}, + {0xa390, 0x0, 0x8, 0x2f}, + {0xa60f, 0x5, 0x1, 0x1}, + {0xa170, 0x0, 0x8, 0xdc}, + {0xa171, 0x0, 0x2, 0x0}, + {0xa2ae, 0x0, 0x1, 0x1}, + {0xa2ae, 0x1, 0x1, 0x1}, + {0xa392, 0x0, 0x1, 0x1}, + {0xa391, 0x2, 0x1, 0x0}, + {0xabc1, 0x0, 0x8, 0xff}, + {0xabc2, 0x0, 0x8, 0x0}, + {0xabc8, 0x0, 0x8, 0x8}, + {0xabca, 0x0, 0x8, 0x10}, + {0xabcb, 0x0, 0x1, 0x0}, + {0xabc3, 0x5, 0x3, 0x7}, + {0xabc0, 0x6, 0x1, 0x0}, + {0xabc0, 0x4, 0x2, 0x0}, + {0xa344, 0x4, 0x4, 0x1}, + {0xabc0, 0x7, 0x1, 0x1}, + {0xabc0, 0x2, 0x1, 0x1}, + {0xa345, 0x0, 0x8, 0x66}, + {0xa346, 0x0, 0x8, 0x66}, + {0xa347, 0x0, 0x4, 0x0}, + {0xa343, 0x0, 0x4, 0xa}, + {0xa347, 0x4, 0x4, 0x2}, + {0xa348, 0x0, 0x4, 0xc}, + {0xa348, 0x4, 0x4, 0x7}, + {0xa349, 0x0, 0x6, 0x2}, +}; diff --git a/drivers/media/dvb/dvb-usb/af9005.c b/drivers/media/dvb/dvb-usb/af9005.c new file mode 100644 index 0000000..7db6eee --- /dev/null +++ b/drivers/media/dvb/dvb-usb/af9005.c @@ -0,0 +1,1141 @@ +/* DVB USB compliant Linux driver for the Afatech 9005 + * USB1.1 DVB-T receiver. + * + * Copyright (C) 2007 Luca Olivetti (luca@ventoso.org) + * + * Thanks to Afatech who kindly provided information. + * + * 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. + * + * see Documentation/dvb/REDME.dvb-usb for more information + */ +#include "af9005.h" + +/* debug */ +int dvb_usb_af9005_debug; +module_param_named(debug, dvb_usb_af9005_debug, int, 0644); +MODULE_PARM_DESC(debug, + "set debugging level (1=info,xfer=2,rc=4,reg=8,i2c=16,fw=32 (or-able))." + DVB_USB_DEBUG_STATUS); +/* enable obnoxious led */ +int dvb_usb_af9005_led = 1; +module_param_named(led, dvb_usb_af9005_led, bool, 0644); +MODULE_PARM_DESC(led, "enable led (default: 1)."); + +/* eeprom dump */ +int dvb_usb_af9005_dump_eeprom = 0; +module_param_named(dump_eeprom, dvb_usb_af9005_dump_eeprom, int, 0); +MODULE_PARM_DESC(dump_eeprom, "dump contents of the eeprom."); + +/* remote control decoder */ +int (*rc_decode) (struct dvb_usb_device * d, u8 * data, int len, u32 * event, + int *state); +void *rc_keys; +int *rc_keys_size; + +u8 regmask[8] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff }; + +struct af9005_device_state { + u8 sequence; + int led_state; +}; + +int af9005_usb_generic_rw(struct dvb_usb_device *d, u8 * wbuf, u16 wlen, + u8 * rbuf, u16 rlen, int delay_ms) +{ + int actlen, ret = -ENOMEM; + + if (wbuf == NULL || wlen == 0) + return -EINVAL; + + if ((ret = mutex_lock_interruptible(&d->usb_mutex))) + return ret; + + deb_xfer(">>> "); + debug_dump(wbuf, wlen, deb_xfer); + + ret = usb_bulk_msg(d->udev, usb_sndbulkpipe(d->udev, + 2), wbuf, wlen, + &actlen, 2000); + + if (ret) + err("bulk message failed: %d (%d/%d)", ret, wlen, actlen); + else + ret = actlen != wlen ? -1 : 0; + + /* an answer is expected, and no error before */ + if (!ret && rbuf && rlen) { + if (delay_ms) + msleep(delay_ms); + + ret = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev, + 0x01), rbuf, + rlen, &actlen, 2000); + + if (ret) + err("recv bulk message failed: %d", ret); + else { + deb_xfer("<<< "); + debug_dump(rbuf, actlen, deb_xfer); + } + } + + mutex_unlock(&d->usb_mutex); + return ret; +} + +int af9005_usb_generic_write(struct dvb_usb_device *d, u8 * buf, u16 len) +{ + return af9005_usb_generic_rw(d, buf, len, NULL, 0, 0); +} + +int af9005_generic_read_write(struct dvb_usb_device *d, u16 reg, + int readwrite, int type, u8 * values, int len) +{ + struct af9005_device_state *st = d->priv; + u8 obuf[16] = { 0 }; + u8 ibuf[17] = { 0 }; + u8 command; + int i; + int ret; + + if (len < 1) { + err("generic read/write, less than 1 byte. Makes no sense."); + return -EINVAL; + } + if (len > 8) { + err("generic read/write, more than 8 bytes. Not supported."); + return -EINVAL; + } + + obuf[0] = 14; /* rest of buffer length low */ + obuf[1] = 0; /* rest of buffer length high */ + + obuf[2] = AF9005_REGISTER_RW; /* register operation */ + obuf[3] = 12; /* rest of buffer length */ + + obuf[4] = st->sequence++; /* sequence number */ + + obuf[5] = (u8) (reg >> 8); /* register address */ + obuf[6] = (u8) (reg & 0xff); + + if (type == AF9005_OFDM_REG) { + command = AF9005_CMD_OFDM_REG; + } else { + command = AF9005_CMD_TUNER; + } + + if (len > 1) + command |= + AF9005_CMD_BURST | AF9005_CMD_AUTOINC | (len - 1) << 3; + command |= readwrite; + if (readwrite == AF9005_CMD_WRITE) + for (i = 0; i < len; i++) + obuf[8 + i] = values[i]; + else if (type == AF9005_TUNER_REG) + /* read command for tuner, the first byte contains the i2c address */ + obuf[8] = values[0]; + obuf[7] = command; + + ret = af9005_usb_generic_rw(d, obuf, 16, ibuf, 17, 0); + if (ret) + return ret; + + /* sanity check */ + if (ibuf[2] != AF9005_REGISTER_RW_ACK) { + err("generic read/write, wrong reply code."); + return -EIO; + } + if (ibuf[3] != 0x0d) { + err("generic read/write, wrong length in reply."); + return -EIO; + } + if (ibuf[4] != obuf[4]) { + err("generic read/write, wrong sequence in reply."); + return -EIO; + } + /* + Windows driver doesn't check these fields, in fact sometimes + the register in the reply is different that what has been sent + + if (ibuf[5] != obuf[5] || ibuf[6] != obuf[6]) { + err("generic read/write, wrong register in reply."); + return -EIO; + } + if (ibuf[7] != command) { + err("generic read/write wrong command in reply."); + return -EIO; + } + */ + if (ibuf[16] != 0x01) { + err("generic read/write wrong status code in reply."); + return -EIO; + } + if (readwrite == AF9005_CMD_READ) + for (i = 0; i < len; i++) + values[i] = ibuf[8 + i]; + + return 0; + +} + +int af9005_read_ofdm_register(struct dvb_usb_device *d, u16 reg, u8 * value) +{ + int ret; + deb_reg("read register %x ", reg); + ret = af9005_generic_read_write(d, reg, + AF9005_CMD_READ, AF9005_OFDM_REG, + value, 1); + if (ret) + deb_reg("failed\n"); + else + deb_reg("value %x\n", *value); + return ret; +} + +int af9005_read_ofdm_registers(struct dvb_usb_device *d, u16 reg, + u8 * values, int len) +{ + int ret; + deb_reg("read %d registers %x ", len, reg); + ret = af9005_generic_read_write(d, reg, + AF9005_CMD_READ, AF9005_OFDM_REG, + values, len); + if (ret) + deb_reg("failed\n"); + else + debug_dump(values, len, deb_reg); + return ret; +} + +int af9005_write_ofdm_register(struct dvb_usb_device *d, u16 reg, u8 value) +{ + int ret; + u8 temp = value; + deb_reg("write register %x value %x ", reg, value); + ret = af9005_generic_read_write(d, reg, + AF9005_CMD_WRITE, AF9005_OFDM_REG, + &temp, 1); + if (ret) + deb_reg("failed\n"); + else + deb_reg("ok\n"); + return ret; +} + +int af9005_write_ofdm_registers(struct dvb_usb_device *d, u16 reg, + u8 * values, int len) +{ + int ret; + deb_reg("write %d registers %x values ", len, reg); + debug_dump(values, len, deb_reg); + + ret = af9005_generic_read_write(d, reg, + AF9005_CMD_WRITE, AF9005_OFDM_REG, + values, len); + if (ret) + deb_reg("failed\n"); + else + deb_reg("ok\n"); + return ret; +} + +int af9005_read_register_bits(struct dvb_usb_device *d, u16 reg, u8 pos, + u8 len, u8 * value) +{ + u8 temp; + int ret; + deb_reg("read bits %x %x %x", reg, pos, len); + ret = af9005_read_ofdm_register(d, reg, &temp); + if (ret) { + deb_reg(" failed\n"); + return ret; + } + *value = (temp >> pos) & regmask[len - 1]; + deb_reg(" value %x\n", *value); + return 0; + +} + +int af9005_write_register_bits(struct dvb_usb_device *d, u16 reg, u8 pos, + u8 len, u8 value) +{ + u8 temp, mask; + int ret; + deb_reg("write bits %x %x %x value %x\n", reg, pos, len, value); + if (pos == 0 && len == 8) + return af9005_write_ofdm_register(d, reg, value); + ret = af9005_read_ofdm_register(d, reg, &temp); + if (ret) + return ret; + mask = regmask[len - 1] << pos; + temp = (temp & ~mask) | ((value << pos) & mask); + return af9005_write_ofdm_register(d, reg, temp); + +} + +static int af9005_usb_read_tuner_registers(struct dvb_usb_device *d, + u16 reg, u8 * values, int len) +{ + return af9005_generic_read_write(d, reg, + AF9005_CMD_READ, AF9005_TUNER_REG, + values, len); +} + +static int af9005_usb_write_tuner_registers(struct dvb_usb_device *d, + u16 reg, u8 * values, int len) +{ + return af9005_generic_read_write(d, reg, + AF9005_CMD_WRITE, + AF9005_TUNER_REG, values, len); +} + +int af9005_write_tuner_registers(struct dvb_usb_device *d, u16 reg, + u8 * values, int len) +{ + /* don't let the name of this function mislead you: it's just used + as an interface from the firmware to the i2c bus. The actual + i2c addresses are contained in the data */ + int ret, i, done = 0, fail = 0; + u8 temp; + ret = af9005_usb_write_tuner_registers(d, reg, values, len); + if (ret) + return ret; + if (reg != 0xffff) { + /* check if write done (0xa40d bit 1) or fail (0xa40d bit 2) */ + for (i = 0; i < 200; i++) { + ret = + af9005_read_ofdm_register(d, + xd_I2C_i2c_m_status_wdat_done, + &temp); + if (ret) + return ret; + done = temp & (regmask[i2c_m_status_wdat_done_len - 1] + << i2c_m_status_wdat_done_pos); + if (done) + break; + fail = temp & (regmask[i2c_m_status_wdat_fail_len - 1] + << i2c_m_status_wdat_fail_pos); + if (fail) + break; + msleep(50); + } + if (i == 200) + return -ETIMEDOUT; + if (fail) { + /* clear write fail bit */ + af9005_write_register_bits(d, + xd_I2C_i2c_m_status_wdat_fail, + i2c_m_status_wdat_fail_pos, + i2c_m_status_wdat_fail_len, + 1); + return -EIO; + } + /* clear write done bit */ + ret = + af9005_write_register_bits(d, + xd_I2C_i2c_m_status_wdat_fail, + i2c_m_status_wdat_done_pos, + i2c_m_status_wdat_done_len, 1); + if (ret) + return ret; + } + return 0; +} + +int af9005_read_tuner_registers(struct dvb_usb_device *d, u16 reg, u8 addr, + u8 * values, int len) +{ + /* don't let the name of this function mislead you: it's just used + as an interface from the firmware to the i2c bus. The actual + i2c addresses are contained in the data */ + int ret, i; + u8 temp, buf[2]; + + buf[0] = addr; /* tuner i2c address */ + buf[1] = values[0]; /* tuner register */ + + values[0] = addr + 0x01; /* i2c read address */ + + if (reg == APO_REG_I2C_RW_SILICON_TUNER) { + /* write tuner i2c address to tuner, 0c00c0 undocumented, found by sniffing */ + ret = af9005_write_tuner_registers(d, 0x00c0, buf, 2); + if (ret) + return ret; + } + + /* send read command to ofsm */ + ret = af9005_usb_read_tuner_registers(d, reg, values, 1); + if (ret) + return ret; + + /* check if read done */ + for (i = 0; i < 200; i++) { + ret = af9005_read_ofdm_register(d, 0xa408, &temp); + if (ret) + return ret; + if (temp & 0x01) + break; + msleep(50); + } + if (i == 200) + return -ETIMEDOUT; + + /* clear read done bit (by writing 1) */ + ret = af9005_write_ofdm_register(d, xd_I2C_i2c_m_data8, 1); + if (ret) + return ret; + + /* get read data (available from 0xa400) */ + for (i = 0; i < len; i++) { + ret = af9005_read_ofdm_register(d, 0xa400 + i, &temp); + if (ret) + return ret; + values[i] = temp; + } + return 0; +} + +static int af9005_i2c_write(struct dvb_usb_device *d, u8 i2caddr, u8 reg, + u8 * data, int len) +{ + int ret, i; + u8 buf[3]; + deb_i2c("i2c_write i2caddr %x, reg %x, len %d data ", i2caddr, + reg, len); + debug_dump(data, len, deb_i2c); + + for (i = 0; i < len; i++) { + buf[0] = i2caddr; + buf[1] = reg + (u8) i; + buf[2] = data[i]; + ret = + af9005_write_tuner_registers(d, + APO_REG_I2C_RW_SILICON_TUNER, + buf, 3); + if (ret) { + deb_i2c("i2c_write failed\n"); + return ret; + } + } + deb_i2c("i2c_write ok\n"); + return 0; +} + +static int af9005_i2c_read(struct dvb_usb_device *d, u8 i2caddr, u8 reg, + u8 * data, int len) +{ + int ret, i; + u8 temp; + deb_i2c("i2c_read i2caddr %x, reg %x, len %d\n ", i2caddr, reg, len); + for (i = 0; i < len; i++) { + temp = reg + i; + ret = + af9005_read_tuner_registers(d, + APO_REG_I2C_RW_SILICON_TUNER, + i2caddr, &temp, 1); + if (ret) { + deb_i2c("i2c_read failed\n"); + return ret; + } + data[i] = temp; + } + deb_i2c("i2c data read: "); + debug_dump(data, len, deb_i2c); + return 0; +} + +static int af9005_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + /* only implements what the mt2060 module does, don't know how + to make it really generic */ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int ret; + u8 reg, addr; + u8 *value; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + if (num > 2) + warn("more than 2 i2c messages at a time is not handled yet. TODO."); + + if (num == 2) { + /* reads a single register */ + reg = *msg[0].buf; + addr = msg[0].addr; + value = msg[1].buf; + ret = af9005_i2c_read(d, addr, reg, value, 1); + if (ret == 0) + ret = 2; + } else { + /* write one or more registers */ + reg = msg[0].buf[0]; + addr = msg[0].addr; + value = &msg[0].buf[1]; + ret = af9005_i2c_write(d, addr, reg, value, msg[0].len - 1); + if (ret == 0) + ret = 1; + } + + mutex_unlock(&d->i2c_mutex); + return ret; +} + +static u32 af9005_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm af9005_i2c_algo = { + .master_xfer = af9005_i2c_xfer, + .functionality = af9005_i2c_func, +}; + +int af9005_send_command(struct dvb_usb_device *d, u8 command, u8 * wbuf, + int wlen, u8 * rbuf, int rlen) +{ + struct af9005_device_state *st = d->priv; + + int ret, i, packet_len; + u8 buf[64]; + u8 ibuf[64]; + + if (wlen < 0) { + err("send command, wlen less than 0 bytes. Makes no sense."); + return -EINVAL; + } + if (wlen > 54) { + err("send command, wlen more than 54 bytes. Not supported."); + return -EINVAL; + } + if (rlen > 54) { + err("send command, rlen more than 54 bytes. Not supported."); + return -EINVAL; + } + packet_len = wlen + 5; + buf[0] = (u8) (packet_len & 0xff); + buf[1] = (u8) ((packet_len & 0xff00) >> 8); + + buf[2] = 0x26; /* packet type */ + buf[3] = wlen + 3; + buf[4] = st->sequence++; + buf[5] = command; + buf[6] = wlen; + for (i = 0; i < wlen; i++) + buf[7 + i] = wbuf[i]; + ret = af9005_usb_generic_rw(d, buf, wlen + 7, ibuf, rlen + 7, 0); + if (ret) + return ret; + if (ibuf[2] != 0x27) { + err("send command, wrong reply code."); + return -EIO; + } + if (ibuf[4] != buf[4]) { + err("send command, wrong sequence in reply."); + return -EIO; + } + if (ibuf[5] != 0x01) { + err("send command, wrong status code in reply."); + return -EIO; + } + if (ibuf[6] != rlen) { + err("send command, invalid data length in reply."); + return -EIO; + } + for (i = 0; i < rlen; i++) + rbuf[i] = ibuf[i + 7]; + return 0; +} + +int af9005_read_eeprom(struct dvb_usb_device *d, u8 address, u8 * values, + int len) +{ + struct af9005_device_state *st = d->priv; + u8 obuf[16], ibuf[14]; + int ret, i; + + memset(obuf, 0, sizeof(obuf)); + memset(ibuf, 0, sizeof(ibuf)); + + obuf[0] = 14; /* length of rest of packet low */ + obuf[1] = 0; /* length of rest of packer high */ + + obuf[2] = 0x2a; /* read/write eeprom */ + + obuf[3] = 12; /* size */ + + obuf[4] = st->sequence++; + + obuf[5] = 0; /* read */ + + obuf[6] = len; + obuf[7] = address; + ret = af9005_usb_generic_rw(d, obuf, 16, ibuf, 14, 0); + if (ret) + return ret; + if (ibuf[2] != 0x2b) { + err("Read eeprom, invalid reply code"); + return -EIO; + } + if (ibuf[3] != 10) { + err("Read eeprom, invalid reply length"); + return -EIO; + } + if (ibuf[4] != obuf[4]) { + err("Read eeprom, wrong sequence in reply "); + return -EIO; + } + if (ibuf[5] != 1) { + err("Read eeprom, wrong status in reply "); + return -EIO; + } + for (i = 0; i < len; i++) { + values[i] = ibuf[6 + i]; + } + return 0; +} + +static int af9005_boot_packet(struct usb_device *udev, int type, u8 * reply) +{ + u8 buf[FW_BULKOUT_SIZE + 2]; + u16 checksum; + int act_len, i, ret; + memset(buf, 0, sizeof(buf)); + buf[0] = (u8) (FW_BULKOUT_SIZE & 0xff); + buf[1] = (u8) ((FW_BULKOUT_SIZE >> 8) & 0xff); + switch (type) { + case FW_CONFIG: + buf[2] = 0x11; + buf[3] = 0x04; + buf[4] = 0x00; /* sequence number, original driver doesn't increment it here */ + buf[5] = 0x03; + checksum = buf[4] + buf[5]; + buf[6] = (u8) ((checksum >> 8) & 0xff); + buf[7] = (u8) (checksum & 0xff); + break; + case FW_CONFIRM: + buf[2] = 0x11; + buf[3] = 0x04; + buf[4] = 0x00; /* sequence number, original driver doesn't increment it here */ + buf[5] = 0x01; + checksum = buf[4] + buf[5]; + buf[6] = (u8) ((checksum >> 8) & 0xff); + buf[7] = (u8) (checksum & 0xff); + break; + case FW_BOOT: + buf[2] = 0x10; + buf[3] = 0x08; + buf[4] = 0x00; /* sequence number, original driver doesn't increment it here */ + buf[5] = 0x97; + buf[6] = 0xaa; + buf[7] = 0x55; + buf[8] = 0xa5; + buf[9] = 0x5a; + checksum = 0; + for (i = 4; i <= 9; i++) + checksum += buf[i]; + buf[10] = (u8) ((checksum >> 8) & 0xff); + buf[11] = (u8) (checksum & 0xff); + break; + default: + err("boot packet invalid boot packet type"); + return -EINVAL; + } + deb_fw(">>> "); + debug_dump(buf, FW_BULKOUT_SIZE + 2, deb_fw); + + ret = usb_bulk_msg(udev, + usb_sndbulkpipe(udev, 0x02), + buf, FW_BULKOUT_SIZE + 2, &act_len, 2000); + if (ret) + err("boot packet bulk message failed: %d (%d/%d)", ret, + FW_BULKOUT_SIZE + 2, act_len); + else + ret = act_len != FW_BULKOUT_SIZE + 2 ? -1 : 0; + if (ret) + return ret; + memset(buf, 0, 9); + ret = usb_bulk_msg(udev, + usb_rcvbulkpipe(udev, 0x01), buf, 9, &act_len, 2000); + if (ret) { + err("boot packet recv bulk message failed: %d", ret); + return ret; + } + deb_fw("<<< "); + debug_dump(buf, act_len, deb_fw); + checksum = 0; + switch (type) { + case FW_CONFIG: + if (buf[2] != 0x11) { + err("boot bad config header."); + return -EIO; + } + if (buf[3] != 0x05) { + err("boot bad config size."); + return -EIO; + } + if (buf[4] != 0x00) { + err("boot bad config sequence."); + return -EIO; + } + if (buf[5] != 0x04) { + err("boot bad config subtype."); + return -EIO; + } + for (i = 4; i <= 6; i++) + checksum += buf[i]; + if (buf[7] * 256 + buf[8] != checksum) { + err("boot bad config checksum."); + return -EIO; + } + *reply = buf[6]; + break; + case FW_CONFIRM: + if (buf[2] != 0x11) { + err("boot bad confirm header."); + return -EIO; + } + if (buf[3] != 0x05) { + err("boot bad confirm size."); + return -EIO; + } + if (buf[4] != 0x00) { + err("boot bad confirm sequence."); + return -EIO; + } + if (buf[5] != 0x02) { + err("boot bad confirm subtype."); + return -EIO; + } + for (i = 4; i <= 6; i++) + checksum += buf[i]; + if (buf[7] * 256 + buf[8] != checksum) { + err("boot bad confirm checksum."); + return -EIO; + } + *reply = buf[6]; + break; + case FW_BOOT: + if (buf[2] != 0x10) { + err("boot bad boot header."); + return -EIO; + } + if (buf[3] != 0x05) { + err("boot bad boot size."); + return -EIO; + } + if (buf[4] != 0x00) { + err("boot bad boot sequence."); + return -EIO; + } + if (buf[5] != 0x01) { + err("boot bad boot pattern 01."); + return -EIO; + } + if (buf[6] != 0x10) { + err("boot bad boot pattern 10."); + return -EIO; + } + for (i = 4; i <= 6; i++) + checksum += buf[i]; + if (buf[7] * 256 + buf[8] != checksum) { + err("boot bad boot checksum."); + return -EIO; + } + break; + + } + + return 0; +} + +int af9005_download_firmware(struct usb_device *udev, const struct firmware *fw) +{ + int i, packets, ret, act_len; + + u8 buf[FW_BULKOUT_SIZE + 2]; + u8 reply; + + ret = af9005_boot_packet(udev, FW_CONFIG, &reply); + if (ret) + return ret; + if (reply != 0x01) { + err("before downloading firmware, FW_CONFIG expected 0x01, received 0x%x", reply); + return -EIO; + } + packets = fw->size / FW_BULKOUT_SIZE; + buf[0] = (u8) (FW_BULKOUT_SIZE & 0xff); + buf[1] = (u8) ((FW_BULKOUT_SIZE >> 8) & 0xff); + for (i = 0; i < packets; i++) { + memcpy(&buf[2], fw->data + i * FW_BULKOUT_SIZE, + FW_BULKOUT_SIZE); + deb_fw(">>> "); + debug_dump(buf, FW_BULKOUT_SIZE + 2, deb_fw); + ret = usb_bulk_msg(udev, + usb_sndbulkpipe(udev, 0x02), + buf, FW_BULKOUT_SIZE + 2, &act_len, 1000); + if (ret) { + err("firmware download failed at packet %d with code %d", i, ret); + return ret; + } + } + ret = af9005_boot_packet(udev, FW_CONFIRM, &reply); + if (ret) + return ret; + if (reply != (u8) (packets & 0xff)) { + err("after downloading firmware, FW_CONFIRM expected 0x%x, received 0x%x", packets & 0xff, reply); + return -EIO; + } + ret = af9005_boot_packet(udev, FW_BOOT, &reply); + if (ret) + return ret; + ret = af9005_boot_packet(udev, FW_CONFIG, &reply); + if (ret) + return ret; + if (reply != 0x02) { + err("after downloading firmware, FW_CONFIG expected 0x02, received 0x%x", reply); + return -EIO; + } + + return 0; + +} + +int af9005_led_control(struct dvb_usb_device *d, int onoff) +{ + struct af9005_device_state *st = d->priv; + int temp, ret; + + if (onoff && dvb_usb_af9005_led) + temp = 1; + else + temp = 0; + if (st->led_state != temp) { + ret = + af9005_write_register_bits(d, xd_p_reg_top_locken1, + reg_top_locken1_pos, + reg_top_locken1_len, temp); + if (ret) + return ret; + ret = + af9005_write_register_bits(d, xd_p_reg_top_lock1, + reg_top_lock1_pos, + reg_top_lock1_len, temp); + if (ret) + return ret; + st->led_state = temp; + } + return 0; +} + +static int af9005_frontend_attach(struct dvb_usb_adapter *adap) +{ + u8 buf[8]; + int i; + + /* without these calls the first commands after downloading + the firmware fail. I put these calls here to simulate + what it is done in dvb-usb-init.c. + */ + struct usb_device *udev = adap->dev->udev; + usb_clear_halt(udev, usb_sndbulkpipe(udev, 2)); + usb_clear_halt(udev, usb_rcvbulkpipe(udev, 1)); + if (dvb_usb_af9005_dump_eeprom) { + printk("EEPROM DUMP\n"); + for (i = 0; i < 255; i += 8) { + af9005_read_eeprom(adap->dev, i, buf, 8); + printk("ADDR %x ", i); + debug_dump(buf, 8, printk); + } + } + adap->fe = af9005_fe_attach(adap->dev); + return 0; +} + +static int af9005_rc_query(struct dvb_usb_device *d, u32 * event, int *state) +{ + struct af9005_device_state *st = d->priv; + int ret, len; + + u8 obuf[5]; + u8 ibuf[256]; + + *state = REMOTE_NO_KEY_PRESSED; + if (rc_decode == NULL) { + /* it shouldn't never come here */ + return 0; + } + /* deb_info("rc_query\n"); */ + obuf[0] = 3; /* rest of packet length low */ + obuf[1] = 0; /* rest of packet lentgh high */ + obuf[2] = 0x40; /* read remote */ + obuf[3] = 1; /* rest of packet length */ + obuf[4] = st->sequence++; /* sequence number */ + ret = af9005_usb_generic_rw(d, obuf, 5, ibuf, 256, 0); + if (ret) { + err("rc query failed"); + return ret; + } + if (ibuf[2] != 0x41) { + err("rc query bad header."); + return -EIO; + } + if (ibuf[4] != obuf[4]) { + err("rc query bad sequence."); + return -EIO; + } + len = ibuf[5]; + if (len > 246) { + err("rc query invalid length"); + return -EIO; + } + if (len > 0) { + deb_rc("rc data (%d) ", len); + debug_dump((ibuf + 6), len, deb_rc); + ret = rc_decode(d, &ibuf[6], len, event, state); + if (ret) { + err("rc_decode failed"); + return ret; + } else { + deb_rc("rc_decode state %x event %x\n", *state, *event); + if (*state == REMOTE_KEY_REPEAT) + *event = d->last_event; + } + } + return 0; +} + +static int af9005_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + + return 0; +} + +static int af9005_pid_filter_control(struct dvb_usb_adapter *adap, int onoff) +{ + int ret; + deb_info("pid filter control onoff %d\n", onoff); + if (onoff) { + ret = + af9005_write_ofdm_register(adap->dev, XD_MP2IF_DMX_CTRL, 1); + if (ret) + return ret; + ret = + af9005_write_register_bits(adap->dev, + XD_MP2IF_DMX_CTRL, 1, 1, 1); + if (ret) + return ret; + ret = + af9005_write_ofdm_register(adap->dev, XD_MP2IF_DMX_CTRL, 1); + } else + ret = + af9005_write_ofdm_register(adap->dev, XD_MP2IF_DMX_CTRL, 0); + if (ret) + return ret; + deb_info("pid filter control ok\n"); + return 0; +} + +static int af9005_pid_filter(struct dvb_usb_adapter *adap, int index, + u16 pid, int onoff) +{ + u8 cmd = index & 0x1f; + int ret; + deb_info("set pid filter, index %d, pid %x, onoff %d\n", index, + pid, onoff); + if (onoff) { + /* cannot use it as pid_filter_ctrl since it has to be done + before setting the first pid */ + if (adap->feedcount == 1) { + deb_info("first pid set, enable pid table\n"); + ret = af9005_pid_filter_control(adap, onoff); + if (ret) + return ret; + } + ret = + af9005_write_ofdm_register(adap->dev, + XD_MP2IF_PID_DATA_L, + (u8) (pid & 0xff)); + if (ret) + return ret; + ret = + af9005_write_ofdm_register(adap->dev, + XD_MP2IF_PID_DATA_H, + (u8) (pid >> 8)); + if (ret) + return ret; + cmd |= 0x20 | 0x40; + } else { + if (adap->feedcount == 0) { + deb_info("last pid unset, disable pid table\n"); + ret = af9005_pid_filter_control(adap, onoff); + if (ret) + return ret; + } + } + ret = af9005_write_ofdm_register(adap->dev, XD_MP2IF_PID_IDX, cmd); + if (ret) + return ret; + deb_info("set pid ok\n"); + return 0; +} + +static int af9005_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, + int *cold) +{ + int ret; + u8 reply; + ret = af9005_boot_packet(udev, FW_CONFIG, &reply); + if (ret) + return ret; + deb_info("result of FW_CONFIG in identify state %d\n", reply); + if (reply == 0x01) + *cold = 1; + else if (reply == 0x02) + *cold = 0; + else + return -EIO; + deb_info("Identify state cold = %d\n", *cold); + return 0; +} + +static struct dvb_usb_device_properties af9005_properties; + +static int af9005_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return dvb_usb_device_init(intf, &af9005_properties, THIS_MODULE, NULL); +} + +static struct usb_device_id af9005_usb_table[] = { + {USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9005)}, + {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_USB_XE)}, + {0}, +}; + +MODULE_DEVICE_TABLE(usb, af9005_usb_table); + +static struct dvb_usb_device_properties af9005_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "af9005.fw", + .download_firmware = af9005_download_firmware, + .no_reconnect = 1, + + .size_of_priv = sizeof(struct af9005_device_state), + + .num_adapters = 1, + .adapter = { + { + .caps = + DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = af9005_pid_filter, + /* .pid_filter_ctrl = af9005_pid_filter_control, */ + .frontend_attach = af9005_frontend_attach, + /* .tuner_attach = af9005_tuner_attach, */ + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 10, + .endpoint = 0x04, + .u = { + .bulk = { + .buffersize = 4096, /* actual size seen is 3948 */ + } + } + }, + } + }, + .power_ctrl = af9005_power_ctrl, + .identify_state = af9005_identify_state, + + .i2c_algo = &af9005_i2c_algo, + + .rc_interval = 200, + .rc_key_map = NULL, + .rc_key_map_size = 0, + .rc_query = af9005_rc_query, + + .num_device_descs = 2, + .devices = { + {.name = "Afatech DVB-T USB1.1 stick", + .cold_ids = {&af9005_usb_table[0], NULL}, + .warm_ids = {NULL}, + }, + {.name = "TerraTec Cinergy T USB XE", + .cold_ids = {&af9005_usb_table[1], NULL}, + .warm_ids = {NULL}, + }, + {NULL}, + } +}; + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver af9005_usb_driver = { + .name = "dvb_usb_af9005", + .probe = af9005_usb_probe, + .disconnect = dvb_usb_device_exit, + .id_table = af9005_usb_table, +}; + +/* module stuff */ +static int __init af9005_usb_module_init(void) +{ + int result; + if ((result = usb_register(&af9005_usb_driver))) { + err("usb_register failed. (%d)", result); + return result; + } + rc_decode = symbol_request(af9005_rc_decode); + rc_keys = symbol_request(af9005_rc_keys); + rc_keys_size = symbol_request(af9005_rc_keys_size); + if (rc_decode == NULL || rc_keys == NULL || rc_keys_size == NULL) { + err("af9005_rc_decode function not found, disabling remote"); + af9005_properties.rc_query = NULL; + } else { + af9005_properties.rc_key_map = rc_keys; + af9005_properties.rc_key_map_size = *rc_keys_size; + } + + return 0; +} + +static void __exit af9005_usb_module_exit(void) +{ + /* release rc decode symbols */ + if (rc_decode != NULL) + symbol_put(af9005_rc_decode); + if (rc_keys != NULL) + symbol_put(af9005_rc_keys); + if (rc_keys_size != NULL) + symbol_put(af9005_rc_keys_size); + /* deregister this driver from the USB subsystem */ + usb_deregister(&af9005_usb_driver); +} + +module_init(af9005_usb_module_init); +module_exit(af9005_usb_module_exit); + +MODULE_AUTHOR("Luca Olivetti <luca@ventoso.org>"); +MODULE_DESCRIPTION("Driver for Afatech 9005 DVB-T USB1.1 stick"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/af9005.h b/drivers/media/dvb/dvb-usb/af9005.h new file mode 100644 index 0000000..0bc48a0 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/af9005.h @@ -0,0 +1,3496 @@ +/* Common header-file of the Linux driver for the Afatech 9005 + * USB1.1 DVB-T receiver. + * + * Copyright (C) 2007 Luca Olivetti (luca@ventoso.org) + * + * Thanks to Afatech who kindly provided information. + * + * 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. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#ifndef _DVB_USB_AF9005_H_ +#define _DVB_USB_AF9005_H_ + +#define DVB_USB_LOG_PREFIX "af9005" +#include "dvb-usb.h" + +extern int dvb_usb_af9005_debug; +#define deb_info(args...) dprintk(dvb_usb_af9005_debug,0x01,args) +#define deb_xfer(args...) dprintk(dvb_usb_af9005_debug,0x02,args) +#define deb_rc(args...) dprintk(dvb_usb_af9005_debug,0x04,args) +#define deb_reg(args...) dprintk(dvb_usb_af9005_debug,0x08,args) +#define deb_i2c(args...) dprintk(dvb_usb_af9005_debug,0x10,args) +#define deb_fw(args...) dprintk(dvb_usb_af9005_debug,0x20,args) + +extern int dvb_usb_af9005_led; + +/* firmware */ +#define FW_BULKOUT_SIZE 250 +enum { + FW_CONFIG, + FW_CONFIRM, + FW_BOOT +}; + +/* af9005 commands */ +#define AF9005_OFDM_REG 0 +#define AF9005_TUNER_REG 1 + +#define AF9005_REGISTER_RW 0x20 +#define AF9005_REGISTER_RW_ACK 0x21 + +#define AF9005_CMD_OFDM_REG 0x00 +#define AF9005_CMD_TUNER 0x80 +#define AF9005_CMD_BURST 0x02 +#define AF9005_CMD_AUTOINC 0x04 +#define AF9005_CMD_READ 0x00 +#define AF9005_CMD_WRITE 0x01 + +/* af9005 registers */ +#define APO_REG_RESET 0xAEFF + +#define APO_REG_I2C_RW_CAN_TUNER 0xF000 +#define APO_REG_I2C_RW_SILICON_TUNER 0xF001 +#define APO_REG_GPIO_RW_SILICON_TUNER 0xFFFE /* also for OFSM */ +#define APO_REG_TRIGGER_OFSM 0xFFFF /* also for OFSM */ + +/*********************************************************************** + * Apollo Registers from VLSI * + ***********************************************************************/ +#define xd_p_reg_aagc_inverted_agc 0xA000 +#define reg_aagc_inverted_agc_pos 0 +#define reg_aagc_inverted_agc_len 1 +#define reg_aagc_inverted_agc_lsb 0 +#define xd_p_reg_aagc_sign_only 0xA000 +#define reg_aagc_sign_only_pos 1 +#define reg_aagc_sign_only_len 1 +#define reg_aagc_sign_only_lsb 0 +#define xd_p_reg_aagc_slow_adc_en 0xA000 +#define reg_aagc_slow_adc_en_pos 2 +#define reg_aagc_slow_adc_en_len 1 +#define reg_aagc_slow_adc_en_lsb 0 +#define xd_p_reg_aagc_slow_adc_scale 0xA000 +#define reg_aagc_slow_adc_scale_pos 3 +#define reg_aagc_slow_adc_scale_len 5 +#define reg_aagc_slow_adc_scale_lsb 0 +#define xd_p_reg_aagc_check_slow_adc_lock 0xA001 +#define reg_aagc_check_slow_adc_lock_pos 0 +#define reg_aagc_check_slow_adc_lock_len 1 +#define reg_aagc_check_slow_adc_lock_lsb 0 +#define xd_p_reg_aagc_init_control 0xA001 +#define reg_aagc_init_control_pos 1 +#define reg_aagc_init_control_len 1 +#define reg_aagc_init_control_lsb 0 +#define xd_p_reg_aagc_total_gain_sel 0xA001 +#define reg_aagc_total_gain_sel_pos 2 +#define reg_aagc_total_gain_sel_len 2 +#define reg_aagc_total_gain_sel_lsb 0 +#define xd_p_reg_aagc_out_inv 0xA001 +#define reg_aagc_out_inv_pos 5 +#define reg_aagc_out_inv_len 1 +#define reg_aagc_out_inv_lsb 0 +#define xd_p_reg_aagc_int_en 0xA001 +#define reg_aagc_int_en_pos 6 +#define reg_aagc_int_en_len 1 +#define reg_aagc_int_en_lsb 0 +#define xd_p_reg_aagc_lock_change_flag 0xA001 +#define reg_aagc_lock_change_flag_pos 7 +#define reg_aagc_lock_change_flag_len 1 +#define reg_aagc_lock_change_flag_lsb 0 +#define xd_p_reg_aagc_rf_loop_bw_scale_acquire 0xA002 +#define reg_aagc_rf_loop_bw_scale_acquire_pos 0 +#define reg_aagc_rf_loop_bw_scale_acquire_len 5 +#define reg_aagc_rf_loop_bw_scale_acquire_lsb 0 +#define xd_p_reg_aagc_rf_loop_bw_scale_track 0xA003 +#define reg_aagc_rf_loop_bw_scale_track_pos 0 +#define reg_aagc_rf_loop_bw_scale_track_len 5 +#define reg_aagc_rf_loop_bw_scale_track_lsb 0 +#define xd_p_reg_aagc_if_loop_bw_scale_acquire 0xA004 +#define reg_aagc_if_loop_bw_scale_acquire_pos 0 +#define reg_aagc_if_loop_bw_scale_acquire_len 5 +#define reg_aagc_if_loop_bw_scale_acquire_lsb 0 +#define xd_p_reg_aagc_if_loop_bw_scale_track 0xA005 +#define reg_aagc_if_loop_bw_scale_track_pos 0 +#define reg_aagc_if_loop_bw_scale_track_len 5 +#define reg_aagc_if_loop_bw_scale_track_lsb 0 +#define xd_p_reg_aagc_max_rf_agc_7_0 0xA006 +#define reg_aagc_max_rf_agc_7_0_pos 0 +#define reg_aagc_max_rf_agc_7_0_len 8 +#define reg_aagc_max_rf_agc_7_0_lsb 0 +#define xd_p_reg_aagc_max_rf_agc_9_8 0xA007 +#define reg_aagc_max_rf_agc_9_8_pos 0 +#define reg_aagc_max_rf_agc_9_8_len 2 +#define reg_aagc_max_rf_agc_9_8_lsb 8 +#define xd_p_reg_aagc_min_rf_agc_7_0 0xA008 +#define reg_aagc_min_rf_agc_7_0_pos 0 +#define reg_aagc_min_rf_agc_7_0_len 8 +#define reg_aagc_min_rf_agc_7_0_lsb 0 +#define xd_p_reg_aagc_min_rf_agc_9_8 0xA009 +#define reg_aagc_min_rf_agc_9_8_pos 0 +#define reg_aagc_min_rf_agc_9_8_len 2 +#define reg_aagc_min_rf_agc_9_8_lsb 8 +#define xd_p_reg_aagc_max_if_agc_7_0 0xA00A +#define reg_aagc_max_if_agc_7_0_pos 0 +#define reg_aagc_max_if_agc_7_0_len 8 +#define reg_aagc_max_if_agc_7_0_lsb 0 +#define xd_p_reg_aagc_max_if_agc_9_8 0xA00B +#define reg_aagc_max_if_agc_9_8_pos 0 +#define reg_aagc_max_if_agc_9_8_len 2 +#define reg_aagc_max_if_agc_9_8_lsb 8 +#define xd_p_reg_aagc_min_if_agc_7_0 0xA00C +#define reg_aagc_min_if_agc_7_0_pos 0 +#define reg_aagc_min_if_agc_7_0_len 8 +#define reg_aagc_min_if_agc_7_0_lsb 0 +#define xd_p_reg_aagc_min_if_agc_9_8 0xA00D +#define reg_aagc_min_if_agc_9_8_pos 0 +#define reg_aagc_min_if_agc_9_8_len 2 +#define reg_aagc_min_if_agc_9_8_lsb 8 +#define xd_p_reg_aagc_lock_sample_scale 0xA00E +#define reg_aagc_lock_sample_scale_pos 0 +#define reg_aagc_lock_sample_scale_len 5 +#define reg_aagc_lock_sample_scale_lsb 0 +#define xd_p_reg_aagc_rf_agc_lock_scale_acquire 0xA00F +#define reg_aagc_rf_agc_lock_scale_acquire_pos 0 +#define reg_aagc_rf_agc_lock_scale_acquire_len 3 +#define reg_aagc_rf_agc_lock_scale_acquire_lsb 0 +#define xd_p_reg_aagc_rf_agc_lock_scale_track 0xA00F +#define reg_aagc_rf_agc_lock_scale_track_pos 3 +#define reg_aagc_rf_agc_lock_scale_track_len 3 +#define reg_aagc_rf_agc_lock_scale_track_lsb 0 +#define xd_p_reg_aagc_if_agc_lock_scale_acquire 0xA010 +#define reg_aagc_if_agc_lock_scale_acquire_pos 0 +#define reg_aagc_if_agc_lock_scale_acquire_len 3 +#define reg_aagc_if_agc_lock_scale_acquire_lsb 0 +#define xd_p_reg_aagc_if_agc_lock_scale_track 0xA010 +#define reg_aagc_if_agc_lock_scale_track_pos 3 +#define reg_aagc_if_agc_lock_scale_track_len 3 +#define reg_aagc_if_agc_lock_scale_track_lsb 0 +#define xd_p_reg_aagc_rf_top_numerator_7_0 0xA011 +#define reg_aagc_rf_top_numerator_7_0_pos 0 +#define reg_aagc_rf_top_numerator_7_0_len 8 +#define reg_aagc_rf_top_numerator_7_0_lsb 0 +#define xd_p_reg_aagc_rf_top_numerator_9_8 0xA012 +#define reg_aagc_rf_top_numerator_9_8_pos 0 +#define reg_aagc_rf_top_numerator_9_8_len 2 +#define reg_aagc_rf_top_numerator_9_8_lsb 8 +#define xd_p_reg_aagc_if_top_numerator_7_0 0xA013 +#define reg_aagc_if_top_numerator_7_0_pos 0 +#define reg_aagc_if_top_numerator_7_0_len 8 +#define reg_aagc_if_top_numerator_7_0_lsb 0 +#define xd_p_reg_aagc_if_top_numerator_9_8 0xA014 +#define reg_aagc_if_top_numerator_9_8_pos 0 +#define reg_aagc_if_top_numerator_9_8_len 2 +#define reg_aagc_if_top_numerator_9_8_lsb 8 +#define xd_p_reg_aagc_adc_out_desired_7_0 0xA015 +#define reg_aagc_adc_out_desired_7_0_pos 0 +#define reg_aagc_adc_out_desired_7_0_len 8 +#define reg_aagc_adc_out_desired_7_0_lsb 0 +#define xd_p_reg_aagc_adc_out_desired_8 0xA016 +#define reg_aagc_adc_out_desired_8_pos 0 +#define reg_aagc_adc_out_desired_8_len 1 +#define reg_aagc_adc_out_desired_8_lsb 0 +#define xd_p_reg_aagc_fixed_gain 0xA016 +#define reg_aagc_fixed_gain_pos 3 +#define reg_aagc_fixed_gain_len 1 +#define reg_aagc_fixed_gain_lsb 0 +#define xd_p_reg_aagc_lock_count_th 0xA016 +#define reg_aagc_lock_count_th_pos 4 +#define reg_aagc_lock_count_th_len 4 +#define reg_aagc_lock_count_th_lsb 0 +#define xd_p_reg_aagc_fixed_rf_agc_control_7_0 0xA017 +#define reg_aagc_fixed_rf_agc_control_7_0_pos 0 +#define reg_aagc_fixed_rf_agc_control_7_0_len 8 +#define reg_aagc_fixed_rf_agc_control_7_0_lsb 0 +#define xd_p_reg_aagc_fixed_rf_agc_control_15_8 0xA018 +#define reg_aagc_fixed_rf_agc_control_15_8_pos 0 +#define reg_aagc_fixed_rf_agc_control_15_8_len 8 +#define reg_aagc_fixed_rf_agc_control_15_8_lsb 8 +#define xd_p_reg_aagc_fixed_rf_agc_control_23_16 0xA019 +#define reg_aagc_fixed_rf_agc_control_23_16_pos 0 +#define reg_aagc_fixed_rf_agc_control_23_16_len 8 +#define reg_aagc_fixed_rf_agc_control_23_16_lsb 16 +#define xd_p_reg_aagc_fixed_rf_agc_control_30_24 0xA01A +#define reg_aagc_fixed_rf_agc_control_30_24_pos 0 +#define reg_aagc_fixed_rf_agc_control_30_24_len 7 +#define reg_aagc_fixed_rf_agc_control_30_24_lsb 24 +#define xd_p_reg_aagc_fixed_if_agc_control_7_0 0xA01B +#define reg_aagc_fixed_if_agc_control_7_0_pos 0 +#define reg_aagc_fixed_if_agc_control_7_0_len 8 +#define reg_aagc_fixed_if_agc_control_7_0_lsb 0 +#define xd_p_reg_aagc_fixed_if_agc_control_15_8 0xA01C +#define reg_aagc_fixed_if_agc_control_15_8_pos 0 +#define reg_aagc_fixed_if_agc_control_15_8_len 8 +#define reg_aagc_fixed_if_agc_control_15_8_lsb 8 +#define xd_p_reg_aagc_fixed_if_agc_control_23_16 0xA01D +#define reg_aagc_fixed_if_agc_control_23_16_pos 0 +#define reg_aagc_fixed_if_agc_control_23_16_len 8 +#define reg_aagc_fixed_if_agc_control_23_16_lsb 16 +#define xd_p_reg_aagc_fixed_if_agc_control_30_24 0xA01E +#define reg_aagc_fixed_if_agc_control_30_24_pos 0 +#define reg_aagc_fixed_if_agc_control_30_24_len 7 +#define reg_aagc_fixed_if_agc_control_30_24_lsb 24 +#define xd_p_reg_aagc_rf_agc_unlock_numerator 0xA01F +#define reg_aagc_rf_agc_unlock_numerator_pos 0 +#define reg_aagc_rf_agc_unlock_numerator_len 6 +#define reg_aagc_rf_agc_unlock_numerator_lsb 0 +#define xd_p_reg_aagc_if_agc_unlock_numerator 0xA020 +#define reg_aagc_if_agc_unlock_numerator_pos 0 +#define reg_aagc_if_agc_unlock_numerator_len 6 +#define reg_aagc_if_agc_unlock_numerator_lsb 0 +#define xd_p_reg_unplug_th 0xA021 +#define reg_unplug_th_pos 0 +#define reg_unplug_th_len 8 +#define reg_aagc_rf_x0_lsb 0 +#define xd_p_reg_weak_signal_rfagc_thr 0xA022 +#define reg_weak_signal_rfagc_thr_pos 0 +#define reg_weak_signal_rfagc_thr_len 8 +#define reg_weak_signal_rfagc_thr_lsb 0 +#define xd_p_reg_unplug_rf_gain_th 0xA023 +#define reg_unplug_rf_gain_th_pos 0 +#define reg_unplug_rf_gain_th_len 8 +#define reg_unplug_rf_gain_th_lsb 0 +#define xd_p_reg_unplug_dtop_rf_gain_th 0xA024 +#define reg_unplug_dtop_rf_gain_th_pos 0 +#define reg_unplug_dtop_rf_gain_th_len 8 +#define reg_unplug_dtop_rf_gain_th_lsb 0 +#define xd_p_reg_unplug_dtop_if_gain_th 0xA025 +#define reg_unplug_dtop_if_gain_th_pos 0 +#define reg_unplug_dtop_if_gain_th_len 8 +#define reg_unplug_dtop_if_gain_th_lsb 0 +#define xd_p_reg_top_recover_at_unplug_en 0xA026 +#define reg_top_recover_at_unplug_en_pos 0 +#define reg_top_recover_at_unplug_en_len 1 +#define reg_top_recover_at_unplug_en_lsb 0 +#define xd_p_reg_aagc_rf_x6 0xA027 +#define reg_aagc_rf_x6_pos 0 +#define reg_aagc_rf_x6_len 8 +#define reg_aagc_rf_x6_lsb 0 +#define xd_p_reg_aagc_rf_x7 0xA028 +#define reg_aagc_rf_x7_pos 0 +#define reg_aagc_rf_x7_len 8 +#define reg_aagc_rf_x7_lsb 0 +#define xd_p_reg_aagc_rf_x8 0xA029 +#define reg_aagc_rf_x8_pos 0 +#define reg_aagc_rf_x8_len 8 +#define reg_aagc_rf_x8_lsb 0 +#define xd_p_reg_aagc_rf_x9 0xA02A +#define reg_aagc_rf_x9_pos 0 +#define reg_aagc_rf_x9_len 8 +#define reg_aagc_rf_x9_lsb 0 +#define xd_p_reg_aagc_rf_x10 0xA02B +#define reg_aagc_rf_x10_pos 0 +#define reg_aagc_rf_x10_len 8 +#define reg_aagc_rf_x10_lsb 0 +#define xd_p_reg_aagc_rf_x11 0xA02C +#define reg_aagc_rf_x11_pos 0 +#define reg_aagc_rf_x11_len 8 +#define reg_aagc_rf_x11_lsb 0 +#define xd_p_reg_aagc_rf_x12 0xA02D +#define reg_aagc_rf_x12_pos 0 +#define reg_aagc_rf_x12_len 8 +#define reg_aagc_rf_x12_lsb 0 +#define xd_p_reg_aagc_rf_x13 0xA02E +#define reg_aagc_rf_x13_pos 0 +#define reg_aagc_rf_x13_len 8 +#define reg_aagc_rf_x13_lsb 0 +#define xd_p_reg_aagc_if_x0 0xA02F +#define reg_aagc_if_x0_pos 0 +#define reg_aagc_if_x0_len 8 +#define reg_aagc_if_x0_lsb 0 +#define xd_p_reg_aagc_if_x1 0xA030 +#define reg_aagc_if_x1_pos 0 +#define reg_aagc_if_x1_len 8 +#define reg_aagc_if_x1_lsb 0 +#define xd_p_reg_aagc_if_x2 0xA031 +#define reg_aagc_if_x2_pos 0 +#define reg_aagc_if_x2_len 8 +#define reg_aagc_if_x2_lsb 0 +#define xd_p_reg_aagc_if_x3 0xA032 +#define reg_aagc_if_x3_pos 0 +#define reg_aagc_if_x3_len 8 +#define reg_aagc_if_x3_lsb 0 +#define xd_p_reg_aagc_if_x4 0xA033 +#define reg_aagc_if_x4_pos 0 +#define reg_aagc_if_x4_len 8 +#define reg_aagc_if_x4_lsb 0 +#define xd_p_reg_aagc_if_x5 0xA034 +#define reg_aagc_if_x5_pos 0 +#define reg_aagc_if_x5_len 8 +#define reg_aagc_if_x5_lsb 0 +#define xd_p_reg_aagc_if_x6 0xA035 +#define reg_aagc_if_x6_pos 0 +#define reg_aagc_if_x6_len 8 +#define reg_aagc_if_x6_lsb 0 +#define xd_p_reg_aagc_if_x7 0xA036 +#define reg_aagc_if_x7_pos 0 +#define reg_aagc_if_x7_len 8 +#define reg_aagc_if_x7_lsb 0 +#define xd_p_reg_aagc_if_x8 0xA037 +#define reg_aagc_if_x8_pos 0 +#define reg_aagc_if_x8_len 8 +#define reg_aagc_if_x8_lsb 0 +#define xd_p_reg_aagc_if_x9 0xA038 +#define reg_aagc_if_x9_pos 0 +#define reg_aagc_if_x9_len 8 +#define reg_aagc_if_x9_lsb 0 +#define xd_p_reg_aagc_if_x10 0xA039 +#define reg_aagc_if_x10_pos 0 +#define reg_aagc_if_x10_len 8 +#define reg_aagc_if_x10_lsb 0 +#define xd_p_reg_aagc_if_x11 0xA03A +#define reg_aagc_if_x11_pos 0 +#define reg_aagc_if_x11_len 8 +#define reg_aagc_if_x11_lsb 0 +#define xd_p_reg_aagc_if_x12 0xA03B +#define reg_aagc_if_x12_pos 0 +#define reg_aagc_if_x12_len 8 +#define reg_aagc_if_x12_lsb 0 +#define xd_p_reg_aagc_if_x13 0xA03C +#define reg_aagc_if_x13_pos 0 +#define reg_aagc_if_x13_len 8 +#define reg_aagc_if_x13_lsb 0 +#define xd_p_reg_aagc_min_rf_ctl_8bit_for_dca 0xA03D +#define reg_aagc_min_rf_ctl_8bit_for_dca_pos 0 +#define reg_aagc_min_rf_ctl_8bit_for_dca_len 8 +#define reg_aagc_min_rf_ctl_8bit_for_dca_lsb 0 +#define xd_p_reg_aagc_min_if_ctl_8bit_for_dca 0xA03E +#define reg_aagc_min_if_ctl_8bit_for_dca_pos 0 +#define reg_aagc_min_if_ctl_8bit_for_dca_len 8 +#define reg_aagc_min_if_ctl_8bit_for_dca_lsb 0 +#define xd_r_reg_aagc_total_gain_7_0 0xA070 +#define reg_aagc_total_gain_7_0_pos 0 +#define reg_aagc_total_gain_7_0_len 8 +#define reg_aagc_total_gain_7_0_lsb 0 +#define xd_r_reg_aagc_total_gain_15_8 0xA071 +#define reg_aagc_total_gain_15_8_pos 0 +#define reg_aagc_total_gain_15_8_len 8 +#define reg_aagc_total_gain_15_8_lsb 8 +#define xd_p_reg_aagc_in_sat_cnt_7_0 0xA074 +#define reg_aagc_in_sat_cnt_7_0_pos 0 +#define reg_aagc_in_sat_cnt_7_0_len 8 +#define reg_aagc_in_sat_cnt_7_0_lsb 0 +#define xd_p_reg_aagc_in_sat_cnt_15_8 0xA075 +#define reg_aagc_in_sat_cnt_15_8_pos 0 +#define reg_aagc_in_sat_cnt_15_8_len 8 +#define reg_aagc_in_sat_cnt_15_8_lsb 8 +#define xd_p_reg_aagc_in_sat_cnt_23_16 0xA076 +#define reg_aagc_in_sat_cnt_23_16_pos 0 +#define reg_aagc_in_sat_cnt_23_16_len 8 +#define reg_aagc_in_sat_cnt_23_16_lsb 16 +#define xd_p_reg_aagc_in_sat_cnt_31_24 0xA077 +#define reg_aagc_in_sat_cnt_31_24_pos 0 +#define reg_aagc_in_sat_cnt_31_24_len 8 +#define reg_aagc_in_sat_cnt_31_24_lsb 24 +#define xd_r_reg_aagc_digital_rf_volt_7_0 0xA078 +#define reg_aagc_digital_rf_volt_7_0_pos 0 +#define reg_aagc_digital_rf_volt_7_0_len 8 +#define reg_aagc_digital_rf_volt_7_0_lsb 0 +#define xd_r_reg_aagc_digital_rf_volt_9_8 0xA079 +#define reg_aagc_digital_rf_volt_9_8_pos 0 +#define reg_aagc_digital_rf_volt_9_8_len 2 +#define reg_aagc_digital_rf_volt_9_8_lsb 8 +#define xd_r_reg_aagc_digital_if_volt_7_0 0xA07A +#define reg_aagc_digital_if_volt_7_0_pos 0 +#define reg_aagc_digital_if_volt_7_0_len 8 +#define reg_aagc_digital_if_volt_7_0_lsb 0 +#define xd_r_reg_aagc_digital_if_volt_9_8 0xA07B +#define reg_aagc_digital_if_volt_9_8_pos 0 +#define reg_aagc_digital_if_volt_9_8_len 2 +#define reg_aagc_digital_if_volt_9_8_lsb 8 +#define xd_r_reg_aagc_rf_gain 0xA07C +#define reg_aagc_rf_gain_pos 0 +#define reg_aagc_rf_gain_len 8 +#define reg_aagc_rf_gain_lsb 0 +#define xd_r_reg_aagc_if_gain 0xA07D +#define reg_aagc_if_gain_pos 0 +#define reg_aagc_if_gain_len 8 +#define reg_aagc_if_gain_lsb 0 +#define xd_p_tinr_imp_indicator 0xA080 +#define tinr_imp_indicator_pos 0 +#define tinr_imp_indicator_len 2 +#define tinr_imp_indicator_lsb 0 +#define xd_p_reg_tinr_fifo_size 0xA080 +#define reg_tinr_fifo_size_pos 2 +#define reg_tinr_fifo_size_len 5 +#define reg_tinr_fifo_size_lsb 0 +#define xd_p_reg_tinr_saturation_cnt_th 0xA081 +#define reg_tinr_saturation_cnt_th_pos 0 +#define reg_tinr_saturation_cnt_th_len 4 +#define reg_tinr_saturation_cnt_th_lsb 0 +#define xd_p_reg_tinr_saturation_th_3_0 0xA081 +#define reg_tinr_saturation_th_3_0_pos 4 +#define reg_tinr_saturation_th_3_0_len 4 +#define reg_tinr_saturation_th_3_0_lsb 0 +#define xd_p_reg_tinr_saturation_th_8_4 0xA082 +#define reg_tinr_saturation_th_8_4_pos 0 +#define reg_tinr_saturation_th_8_4_len 5 +#define reg_tinr_saturation_th_8_4_lsb 4 +#define xd_p_reg_tinr_imp_duration_th_2k_7_0 0xA083 +#define reg_tinr_imp_duration_th_2k_7_0_pos 0 +#define reg_tinr_imp_duration_th_2k_7_0_len 8 +#define reg_tinr_imp_duration_th_2k_7_0_lsb 0 +#define xd_p_reg_tinr_imp_duration_th_2k_8 0xA084 +#define reg_tinr_imp_duration_th_2k_8_pos 0 +#define reg_tinr_imp_duration_th_2k_8_len 1 +#define reg_tinr_imp_duration_th_2k_8_lsb 0 +#define xd_p_reg_tinr_imp_duration_th_8k_7_0 0xA085 +#define reg_tinr_imp_duration_th_8k_7_0_pos 0 +#define reg_tinr_imp_duration_th_8k_7_0_len 8 +#define reg_tinr_imp_duration_th_8k_7_0_lsb 0 +#define xd_p_reg_tinr_imp_duration_th_8k_10_8 0xA086 +#define reg_tinr_imp_duration_th_8k_10_8_pos 0 +#define reg_tinr_imp_duration_th_8k_10_8_len 3 +#define reg_tinr_imp_duration_th_8k_10_8_lsb 8 +#define xd_p_reg_tinr_freq_ratio_6m_7_0 0xA087 +#define reg_tinr_freq_ratio_6m_7_0_pos 0 +#define reg_tinr_freq_ratio_6m_7_0_len 8 +#define reg_tinr_freq_ratio_6m_7_0_lsb 0 +#define xd_p_reg_tinr_freq_ratio_6m_12_8 0xA088 +#define reg_tinr_freq_ratio_6m_12_8_pos 0 +#define reg_tinr_freq_ratio_6m_12_8_len 5 +#define reg_tinr_freq_ratio_6m_12_8_lsb 8 +#define xd_p_reg_tinr_freq_ratio_7m_7_0 0xA089 +#define reg_tinr_freq_ratio_7m_7_0_pos 0 +#define reg_tinr_freq_ratio_7m_7_0_len 8 +#define reg_tinr_freq_ratio_7m_7_0_lsb 0 +#define xd_p_reg_tinr_freq_ratio_7m_12_8 0xA08A +#define reg_tinr_freq_ratio_7m_12_8_pos 0 +#define reg_tinr_freq_ratio_7m_12_8_len 5 +#define reg_tinr_freq_ratio_7m_12_8_lsb 8 +#define xd_p_reg_tinr_freq_ratio_8m_7_0 0xA08B +#define reg_tinr_freq_ratio_8m_7_0_pos 0 +#define reg_tinr_freq_ratio_8m_7_0_len 8 +#define reg_tinr_freq_ratio_8m_7_0_lsb 0 +#define xd_p_reg_tinr_freq_ratio_8m_12_8 0xA08C +#define reg_tinr_freq_ratio_8m_12_8_pos 0 +#define reg_tinr_freq_ratio_8m_12_8_len 5 +#define reg_tinr_freq_ratio_8m_12_8_lsb 8 +#define xd_p_reg_tinr_imp_duration_th_low_2k 0xA08D +#define reg_tinr_imp_duration_th_low_2k_pos 0 +#define reg_tinr_imp_duration_th_low_2k_len 8 +#define reg_tinr_imp_duration_th_low_2k_lsb 0 +#define xd_p_reg_tinr_imp_duration_th_low_8k 0xA08E +#define reg_tinr_imp_duration_th_low_8k_pos 0 +#define reg_tinr_imp_duration_th_low_8k_len 8 +#define reg_tinr_imp_duration_th_low_8k_lsb 0 +#define xd_r_reg_tinr_counter_7_0 0xA090 +#define reg_tinr_counter_7_0_pos 0 +#define reg_tinr_counter_7_0_len 8 +#define reg_tinr_counter_7_0_lsb 0 +#define xd_r_reg_tinr_counter_15_8 0xA091 +#define reg_tinr_counter_15_8_pos 0 +#define reg_tinr_counter_15_8_len 8 +#define reg_tinr_counter_15_8_lsb 8 +#define xd_p_reg_tinr_adative_tinr_en 0xA093 +#define reg_tinr_adative_tinr_en_pos 0 +#define reg_tinr_adative_tinr_en_len 1 +#define reg_tinr_adative_tinr_en_lsb 0 +#define xd_p_reg_tinr_peak_fifo_size 0xA093 +#define reg_tinr_peak_fifo_size_pos 1 +#define reg_tinr_peak_fifo_size_len 5 +#define reg_tinr_peak_fifo_size_lsb 0 +#define xd_p_reg_tinr_counter_rst 0xA093 +#define reg_tinr_counter_rst_pos 6 +#define reg_tinr_counter_rst_len 1 +#define reg_tinr_counter_rst_lsb 0 +#define xd_p_reg_tinr_search_period_7_0 0xA094 +#define reg_tinr_search_period_7_0_pos 0 +#define reg_tinr_search_period_7_0_len 8 +#define reg_tinr_search_period_7_0_lsb 0 +#define xd_p_reg_tinr_search_period_15_8 0xA095 +#define reg_tinr_search_period_15_8_pos 0 +#define reg_tinr_search_period_15_8_len 8 +#define reg_tinr_search_period_15_8_lsb 8 +#define xd_p_reg_ccifs_fcw_7_0 0xA0A0 +#define reg_ccifs_fcw_7_0_pos 0 +#define reg_ccifs_fcw_7_0_len 8 +#define reg_ccifs_fcw_7_0_lsb 0 +#define xd_p_reg_ccifs_fcw_12_8 0xA0A1 +#define reg_ccifs_fcw_12_8_pos 0 +#define reg_ccifs_fcw_12_8_len 5 +#define reg_ccifs_fcw_12_8_lsb 8 +#define xd_p_reg_ccifs_spec_inv 0xA0A1 +#define reg_ccifs_spec_inv_pos 5 +#define reg_ccifs_spec_inv_len 1 +#define reg_ccifs_spec_inv_lsb 0 +#define xd_p_reg_gp_trigger 0xA0A2 +#define reg_gp_trigger_pos 0 +#define reg_gp_trigger_len 1 +#define reg_gp_trigger_lsb 0 +#define xd_p_reg_trigger_sel 0xA0A2 +#define reg_trigger_sel_pos 1 +#define reg_trigger_sel_len 2 +#define reg_trigger_sel_lsb 0 +#define xd_p_reg_debug_ofdm 0xA0A2 +#define reg_debug_ofdm_pos 3 +#define reg_debug_ofdm_len 2 +#define reg_debug_ofdm_lsb 0 +#define xd_p_reg_trigger_module_sel 0xA0A3 +#define reg_trigger_module_sel_pos 0 +#define reg_trigger_module_sel_len 6 +#define reg_trigger_module_sel_lsb 0 +#define xd_p_reg_trigger_set_sel 0xA0A4 +#define reg_trigger_set_sel_pos 0 +#define reg_trigger_set_sel_len 6 +#define reg_trigger_set_sel_lsb 0 +#define xd_p_reg_fw_int_mask_n 0xA0A4 +#define reg_fw_int_mask_n_pos 6 +#define reg_fw_int_mask_n_len 1 +#define reg_fw_int_mask_n_lsb 0 +#define xd_p_reg_debug_group 0xA0A5 +#define reg_debug_group_pos 0 +#define reg_debug_group_len 4 +#define reg_debug_group_lsb 0 +#define xd_p_reg_odbg_clk_sel 0xA0A5 +#define reg_odbg_clk_sel_pos 4 +#define reg_odbg_clk_sel_len 2 +#define reg_odbg_clk_sel_lsb 0 +#define xd_p_reg_ccif_sc 0xA0C0 +#define reg_ccif_sc_pos 0 +#define reg_ccif_sc_len 4 +#define reg_ccif_sc_lsb 0 +#define xd_r_reg_ccif_saturate 0xA0C1 +#define reg_ccif_saturate_pos 0 +#define reg_ccif_saturate_len 2 +#define reg_ccif_saturate_lsb 0 +#define xd_r_reg_antif_saturate 0xA0C1 +#define reg_antif_saturate_pos 2 +#define reg_antif_saturate_len 4 +#define reg_antif_saturate_lsb 0 +#define xd_r_reg_acif_saturate 0xA0C2 +#define reg_acif_saturate_pos 0 +#define reg_acif_saturate_len 8 +#define reg_acif_saturate_lsb 0 +#define xd_p_reg_tmr_timer0_threshold_7_0 0xA0C8 +#define reg_tmr_timer0_threshold_7_0_pos 0 +#define reg_tmr_timer0_threshold_7_0_len 8 +#define reg_tmr_timer0_threshold_7_0_lsb 0 +#define xd_p_reg_tmr_timer0_threshold_15_8 0xA0C9 +#define reg_tmr_timer0_threshold_15_8_pos 0 +#define reg_tmr_timer0_threshold_15_8_len 8 +#define reg_tmr_timer0_threshold_15_8_lsb 8 +#define xd_p_reg_tmr_timer0_enable 0xA0CA +#define reg_tmr_timer0_enable_pos 0 +#define reg_tmr_timer0_enable_len 1 +#define reg_tmr_timer0_enable_lsb 0 +#define xd_p_reg_tmr_timer0_clk_sel 0xA0CA +#define reg_tmr_timer0_clk_sel_pos 1 +#define reg_tmr_timer0_clk_sel_len 1 +#define reg_tmr_timer0_clk_sel_lsb 0 +#define xd_p_reg_tmr_timer0_int 0xA0CA +#define reg_tmr_timer0_int_pos 2 +#define reg_tmr_timer0_int_len 1 +#define reg_tmr_timer0_int_lsb 0 +#define xd_p_reg_tmr_timer0_rst 0xA0CA +#define reg_tmr_timer0_rst_pos 3 +#define reg_tmr_timer0_rst_len 1 +#define reg_tmr_timer0_rst_lsb 0 +#define xd_r_reg_tmr_timer0_count_7_0 0xA0CB +#define reg_tmr_timer0_count_7_0_pos 0 +#define reg_tmr_timer0_count_7_0_len 8 +#define reg_tmr_timer0_count_7_0_lsb 0 +#define xd_r_reg_tmr_timer0_count_15_8 0xA0CC +#define reg_tmr_timer0_count_15_8_pos 0 +#define reg_tmr_timer0_count_15_8_len 8 +#define reg_tmr_timer0_count_15_8_lsb 8 +#define xd_p_reg_suspend 0xA0CD +#define reg_suspend_pos 0 +#define reg_suspend_len 1 +#define reg_suspend_lsb 0 +#define xd_p_reg_suspend_rdy 0xA0CD +#define reg_suspend_rdy_pos 1 +#define reg_suspend_rdy_len 1 +#define reg_suspend_rdy_lsb 0 +#define xd_p_reg_resume 0xA0CD +#define reg_resume_pos 2 +#define reg_resume_len 1 +#define reg_resume_lsb 0 +#define xd_p_reg_resume_rdy 0xA0CD +#define reg_resume_rdy_pos 3 +#define reg_resume_rdy_len 1 +#define reg_resume_rdy_lsb 0 +#define xd_p_reg_fmf 0xA0CE +#define reg_fmf_pos 0 +#define reg_fmf_len 8 +#define reg_fmf_lsb 0 +#define xd_p_ccid_accumulate_num_2k_7_0 0xA100 +#define ccid_accumulate_num_2k_7_0_pos 0 +#define ccid_accumulate_num_2k_7_0_len 8 +#define ccid_accumulate_num_2k_7_0_lsb 0 +#define xd_p_ccid_accumulate_num_2k_12_8 0xA101 +#define ccid_accumulate_num_2k_12_8_pos 0 +#define ccid_accumulate_num_2k_12_8_len 5 +#define ccid_accumulate_num_2k_12_8_lsb 8 +#define xd_p_ccid_accumulate_num_8k_7_0 0xA102 +#define ccid_accumulate_num_8k_7_0_pos 0 +#define ccid_accumulate_num_8k_7_0_len 8 +#define ccid_accumulate_num_8k_7_0_lsb 0 +#define xd_p_ccid_accumulate_num_8k_14_8 0xA103 +#define ccid_accumulate_num_8k_14_8_pos 0 +#define ccid_accumulate_num_8k_14_8_len 7 +#define ccid_accumulate_num_8k_14_8_lsb 8 +#define xd_p_ccid_desired_level_0 0xA103 +#define ccid_desired_level_0_pos 7 +#define ccid_desired_level_0_len 1 +#define ccid_desired_level_0_lsb 0 +#define xd_p_ccid_desired_level_8_1 0xA104 +#define ccid_desired_level_8_1_pos 0 +#define ccid_desired_level_8_1_len 8 +#define ccid_desired_level_8_1_lsb 1 +#define xd_p_ccid_apply_delay 0xA105 +#define ccid_apply_delay_pos 0 +#define ccid_apply_delay_len 7 +#define ccid_apply_delay_lsb 0 +#define xd_p_ccid_CCID_Threshold1 0xA106 +#define ccid_CCID_Threshold1_pos 0 +#define ccid_CCID_Threshold1_len 8 +#define ccid_CCID_Threshold1_lsb 0 +#define xd_p_ccid_CCID_Threshold2 0xA107 +#define ccid_CCID_Threshold2_pos 0 +#define ccid_CCID_Threshold2_len 8 +#define ccid_CCID_Threshold2_lsb 0 +#define xd_p_reg_ccid_gain_scale 0xA108 +#define reg_ccid_gain_scale_pos 0 +#define reg_ccid_gain_scale_len 4 +#define reg_ccid_gain_scale_lsb 0 +#define xd_p_reg_ccid2_passband_gain_set 0xA108 +#define reg_ccid2_passband_gain_set_pos 4 +#define reg_ccid2_passband_gain_set_len 4 +#define reg_ccid2_passband_gain_set_lsb 0 +#define xd_r_ccid_multiplier_7_0 0xA109 +#define ccid_multiplier_7_0_pos 0 +#define ccid_multiplier_7_0_len 8 +#define ccid_multiplier_7_0_lsb 0 +#define xd_r_ccid_multiplier_15_8 0xA10A +#define ccid_multiplier_15_8_pos 0 +#define ccid_multiplier_15_8_len 8 +#define ccid_multiplier_15_8_lsb 8 +#define xd_r_ccid_right_shift_bits 0xA10B +#define ccid_right_shift_bits_pos 0 +#define ccid_right_shift_bits_len 4 +#define ccid_right_shift_bits_lsb 0 +#define xd_r_reg_ccid_sx_7_0 0xA10C +#define reg_ccid_sx_7_0_pos 0 +#define reg_ccid_sx_7_0_len 8 +#define reg_ccid_sx_7_0_lsb 0 +#define xd_r_reg_ccid_sx_15_8 0xA10D +#define reg_ccid_sx_15_8_pos 0 +#define reg_ccid_sx_15_8_len 8 +#define reg_ccid_sx_15_8_lsb 8 +#define xd_r_reg_ccid_sx_21_16 0xA10E +#define reg_ccid_sx_21_16_pos 0 +#define reg_ccid_sx_21_16_len 6 +#define reg_ccid_sx_21_16_lsb 16 +#define xd_r_reg_ccid_sy_7_0 0xA110 +#define reg_ccid_sy_7_0_pos 0 +#define reg_ccid_sy_7_0_len 8 +#define reg_ccid_sy_7_0_lsb 0 +#define xd_r_reg_ccid_sy_15_8 0xA111 +#define reg_ccid_sy_15_8_pos 0 +#define reg_ccid_sy_15_8_len 8 +#define reg_ccid_sy_15_8_lsb 8 +#define xd_r_reg_ccid_sy_23_16 0xA112 +#define reg_ccid_sy_23_16_pos 0 +#define reg_ccid_sy_23_16_len 8 +#define reg_ccid_sy_23_16_lsb 16 +#define xd_r_reg_ccid2_sz_7_0 0xA114 +#define reg_ccid2_sz_7_0_pos 0 +#define reg_ccid2_sz_7_0_len 8 +#define reg_ccid2_sz_7_0_lsb 0 +#define xd_r_reg_ccid2_sz_15_8 0xA115 +#define reg_ccid2_sz_15_8_pos 0 +#define reg_ccid2_sz_15_8_len 8 +#define reg_ccid2_sz_15_8_lsb 8 +#define xd_r_reg_ccid2_sz_23_16 0xA116 +#define reg_ccid2_sz_23_16_pos 0 +#define reg_ccid2_sz_23_16_len 8 +#define reg_ccid2_sz_23_16_lsb 16 +#define xd_r_reg_ccid2_sz_25_24 0xA117 +#define reg_ccid2_sz_25_24_pos 0 +#define reg_ccid2_sz_25_24_len 2 +#define reg_ccid2_sz_25_24_lsb 24 +#define xd_r_reg_ccid2_sy_7_0 0xA118 +#define reg_ccid2_sy_7_0_pos 0 +#define reg_ccid2_sy_7_0_len 8 +#define reg_ccid2_sy_7_0_lsb 0 +#define xd_r_reg_ccid2_sy_15_8 0xA119 +#define reg_ccid2_sy_15_8_pos 0 +#define reg_ccid2_sy_15_8_len 8 +#define reg_ccid2_sy_15_8_lsb 8 +#define xd_r_reg_ccid2_sy_23_16 0xA11A +#define reg_ccid2_sy_23_16_pos 0 +#define reg_ccid2_sy_23_16_len 8 +#define reg_ccid2_sy_23_16_lsb 16 +#define xd_r_reg_ccid2_sy_25_24 0xA11B +#define reg_ccid2_sy_25_24_pos 0 +#define reg_ccid2_sy_25_24_len 2 +#define reg_ccid2_sy_25_24_lsb 24 +#define xd_p_dagc1_accumulate_num_2k_7_0 0xA120 +#define dagc1_accumulate_num_2k_7_0_pos 0 +#define dagc1_accumulate_num_2k_7_0_len 8 +#define dagc1_accumulate_num_2k_7_0_lsb 0 +#define xd_p_dagc1_accumulate_num_2k_12_8 0xA121 +#define dagc1_accumulate_num_2k_12_8_pos 0 +#define dagc1_accumulate_num_2k_12_8_len 5 +#define dagc1_accumulate_num_2k_12_8_lsb 8 +#define xd_p_dagc1_accumulate_num_8k_7_0 0xA122 +#define dagc1_accumulate_num_8k_7_0_pos 0 +#define dagc1_accumulate_num_8k_7_0_len 8 +#define dagc1_accumulate_num_8k_7_0_lsb 0 +#define xd_p_dagc1_accumulate_num_8k_14_8 0xA123 +#define dagc1_accumulate_num_8k_14_8_pos 0 +#define dagc1_accumulate_num_8k_14_8_len 7 +#define dagc1_accumulate_num_8k_14_8_lsb 8 +#define xd_p_dagc1_desired_level_0 0xA123 +#define dagc1_desired_level_0_pos 7 +#define dagc1_desired_level_0_len 1 +#define dagc1_desired_level_0_lsb 0 +#define xd_p_dagc1_desired_level_8_1 0xA124 +#define dagc1_desired_level_8_1_pos 0 +#define dagc1_desired_level_8_1_len 8 +#define dagc1_desired_level_8_1_lsb 1 +#define xd_p_dagc1_apply_delay 0xA125 +#define dagc1_apply_delay_pos 0 +#define dagc1_apply_delay_len 7 +#define dagc1_apply_delay_lsb 0 +#define xd_p_dagc1_bypass_scale_ctl 0xA126 +#define dagc1_bypass_scale_ctl_pos 0 +#define dagc1_bypass_scale_ctl_len 2 +#define dagc1_bypass_scale_ctl_lsb 0 +#define xd_p_reg_dagc1_in_sat_cnt_7_0 0xA127 +#define reg_dagc1_in_sat_cnt_7_0_pos 0 +#define reg_dagc1_in_sat_cnt_7_0_len 8 +#define reg_dagc1_in_sat_cnt_7_0_lsb 0 +#define xd_p_reg_dagc1_in_sat_cnt_15_8 0xA128 +#define reg_dagc1_in_sat_cnt_15_8_pos 0 +#define reg_dagc1_in_sat_cnt_15_8_len 8 +#define reg_dagc1_in_sat_cnt_15_8_lsb 8 +#define xd_p_reg_dagc1_in_sat_cnt_23_16 0xA129 +#define reg_dagc1_in_sat_cnt_23_16_pos 0 +#define reg_dagc1_in_sat_cnt_23_16_len 8 +#define reg_dagc1_in_sat_cnt_23_16_lsb 16 +#define xd_p_reg_dagc1_in_sat_cnt_31_24 0xA12A +#define reg_dagc1_in_sat_cnt_31_24_pos 0 +#define reg_dagc1_in_sat_cnt_31_24_len 8 +#define reg_dagc1_in_sat_cnt_31_24_lsb 24 +#define xd_p_reg_dagc1_out_sat_cnt_7_0 0xA12B +#define reg_dagc1_out_sat_cnt_7_0_pos 0 +#define reg_dagc1_out_sat_cnt_7_0_len 8 +#define reg_dagc1_out_sat_cnt_7_0_lsb 0 +#define xd_p_reg_dagc1_out_sat_cnt_15_8 0xA12C +#define reg_dagc1_out_sat_cnt_15_8_pos 0 +#define reg_dagc1_out_sat_cnt_15_8_len 8 +#define reg_dagc1_out_sat_cnt_15_8_lsb 8 +#define xd_p_reg_dagc1_out_sat_cnt_23_16 0xA12D +#define reg_dagc1_out_sat_cnt_23_16_pos 0 +#define reg_dagc1_out_sat_cnt_23_16_len 8 +#define reg_dagc1_out_sat_cnt_23_16_lsb 16 +#define xd_p_reg_dagc1_out_sat_cnt_31_24 0xA12E +#define reg_dagc1_out_sat_cnt_31_24_pos 0 +#define reg_dagc1_out_sat_cnt_31_24_len 8 +#define reg_dagc1_out_sat_cnt_31_24_lsb 24 +#define xd_r_dagc1_multiplier_7_0 0xA136 +#define dagc1_multiplier_7_0_pos 0 +#define dagc1_multiplier_7_0_len 8 +#define dagc1_multiplier_7_0_lsb 0 +#define xd_r_dagc1_multiplier_15_8 0xA137 +#define dagc1_multiplier_15_8_pos 0 +#define dagc1_multiplier_15_8_len 8 +#define dagc1_multiplier_15_8_lsb 8 +#define xd_r_dagc1_right_shift_bits 0xA138 +#define dagc1_right_shift_bits_pos 0 +#define dagc1_right_shift_bits_len 4 +#define dagc1_right_shift_bits_lsb 0 +#define xd_p_reg_bfs_fcw_7_0 0xA140 +#define reg_bfs_fcw_7_0_pos 0 +#define reg_bfs_fcw_7_0_len 8 +#define reg_bfs_fcw_7_0_lsb 0 +#define xd_p_reg_bfs_fcw_15_8 0xA141 +#define reg_bfs_fcw_15_8_pos 0 +#define reg_bfs_fcw_15_8_len 8 +#define reg_bfs_fcw_15_8_lsb 8 +#define xd_p_reg_bfs_fcw_22_16 0xA142 +#define reg_bfs_fcw_22_16_pos 0 +#define reg_bfs_fcw_22_16_len 7 +#define reg_bfs_fcw_22_16_lsb 16 +#define xd_p_reg_antif_sf_7_0 0xA144 +#define reg_antif_sf_7_0_pos 0 +#define reg_antif_sf_7_0_len 8 +#define reg_antif_sf_7_0_lsb 0 +#define xd_p_reg_antif_sf_11_8 0xA145 +#define reg_antif_sf_11_8_pos 0 +#define reg_antif_sf_11_8_len 4 +#define reg_antif_sf_11_8_lsb 8 +#define xd_r_bfs_fcw_q_7_0 0xA150 +#define bfs_fcw_q_7_0_pos 0 +#define bfs_fcw_q_7_0_len 8 +#define bfs_fcw_q_7_0_lsb 0 +#define xd_r_bfs_fcw_q_15_8 0xA151 +#define bfs_fcw_q_15_8_pos 0 +#define bfs_fcw_q_15_8_len 8 +#define bfs_fcw_q_15_8_lsb 8 +#define xd_r_bfs_fcw_q_22_16 0xA152 +#define bfs_fcw_q_22_16_pos 0 +#define bfs_fcw_q_22_16_len 7 +#define bfs_fcw_q_22_16_lsb 16 +#define xd_p_reg_dca_enu 0xA160 +#define reg_dca_enu_pos 0 +#define reg_dca_enu_len 1 +#define reg_dca_enu_lsb 0 +#define xd_p_reg_dca_enl 0xA160 +#define reg_dca_enl_pos 1 +#define reg_dca_enl_len 1 +#define reg_dca_enl_lsb 0 +#define xd_p_reg_dca_lower_chip 0xA160 +#define reg_dca_lower_chip_pos 2 +#define reg_dca_lower_chip_len 1 +#define reg_dca_lower_chip_lsb 0 +#define xd_p_reg_dca_upper_chip 0xA160 +#define reg_dca_upper_chip_pos 3 +#define reg_dca_upper_chip_len 1 +#define reg_dca_upper_chip_lsb 0 +#define xd_p_reg_dca_platch 0xA160 +#define reg_dca_platch_pos 4 +#define reg_dca_platch_len 1 +#define reg_dca_platch_lsb 0 +#define xd_p_reg_dca_th 0xA161 +#define reg_dca_th_pos 0 +#define reg_dca_th_len 5 +#define reg_dca_th_lsb 0 +#define xd_p_reg_dca_scale 0xA162 +#define reg_dca_scale_pos 0 +#define reg_dca_scale_len 4 +#define reg_dca_scale_lsb 0 +#define xd_p_reg_dca_tone_7_0 0xA163 +#define reg_dca_tone_7_0_pos 0 +#define reg_dca_tone_7_0_len 8 +#define reg_dca_tone_7_0_lsb 0 +#define xd_p_reg_dca_tone_12_8 0xA164 +#define reg_dca_tone_12_8_pos 0 +#define reg_dca_tone_12_8_len 5 +#define reg_dca_tone_12_8_lsb 8 +#define xd_p_reg_dca_time_7_0 0xA165 +#define reg_dca_time_7_0_pos 0 +#define reg_dca_time_7_0_len 8 +#define reg_dca_time_7_0_lsb 0 +#define xd_p_reg_dca_time_15_8 0xA166 +#define reg_dca_time_15_8_pos 0 +#define reg_dca_time_15_8_len 8 +#define reg_dca_time_15_8_lsb 8 +#define xd_r_dcasm 0xA167 +#define dcasm_pos 0 +#define dcasm_len 3 +#define dcasm_lsb 0 +#define xd_p_reg_qnt_valuew_7_0 0xA168 +#define reg_qnt_valuew_7_0_pos 0 +#define reg_qnt_valuew_7_0_len 8 +#define reg_qnt_valuew_7_0_lsb 0 +#define xd_p_reg_qnt_valuew_10_8 0xA169 +#define reg_qnt_valuew_10_8_pos 0 +#define reg_qnt_valuew_10_8_len 3 +#define reg_qnt_valuew_10_8_lsb 8 +#define xd_p_dca_sbx_gain_diff_7_0 0xA16A +#define dca_sbx_gain_diff_7_0_pos 0 +#define dca_sbx_gain_diff_7_0_len 8 +#define dca_sbx_gain_diff_7_0_lsb 0 +#define xd_p_dca_sbx_gain_diff_9_8 0xA16B +#define dca_sbx_gain_diff_9_8_pos 0 +#define dca_sbx_gain_diff_9_8_len 2 +#define dca_sbx_gain_diff_9_8_lsb 8 +#define xd_p_reg_dca_stand_alone 0xA16C +#define reg_dca_stand_alone_pos 0 +#define reg_dca_stand_alone_len 1 +#define reg_dca_stand_alone_lsb 0 +#define xd_p_reg_dca_upper_out_en 0xA16C +#define reg_dca_upper_out_en_pos 1 +#define reg_dca_upper_out_en_len 1 +#define reg_dca_upper_out_en_lsb 0 +#define xd_p_reg_dca_rc_en 0xA16C +#define reg_dca_rc_en_pos 2 +#define reg_dca_rc_en_len 1 +#define reg_dca_rc_en_lsb 0 +#define xd_p_reg_dca_retrain_send 0xA16C +#define reg_dca_retrain_send_pos 3 +#define reg_dca_retrain_send_len 1 +#define reg_dca_retrain_send_lsb 0 +#define xd_p_reg_dca_retrain_rec 0xA16C +#define reg_dca_retrain_rec_pos 4 +#define reg_dca_retrain_rec_len 1 +#define reg_dca_retrain_rec_lsb 0 +#define xd_p_reg_dca_api_tpsrdy 0xA16C +#define reg_dca_api_tpsrdy_pos 5 +#define reg_dca_api_tpsrdy_len 1 +#define reg_dca_api_tpsrdy_lsb 0 +#define xd_p_reg_dca_symbol_gap 0xA16D +#define reg_dca_symbol_gap_pos 0 +#define reg_dca_symbol_gap_len 4 +#define reg_dca_symbol_gap_lsb 0 +#define xd_p_reg_qnt_nfvaluew_7_0 0xA16E +#define reg_qnt_nfvaluew_7_0_pos 0 +#define reg_qnt_nfvaluew_7_0_len 8 +#define reg_qnt_nfvaluew_7_0_lsb 0 +#define xd_p_reg_qnt_nfvaluew_10_8 0xA16F +#define reg_qnt_nfvaluew_10_8_pos 0 +#define reg_qnt_nfvaluew_10_8_len 3 +#define reg_qnt_nfvaluew_10_8_lsb 8 +#define xd_p_reg_qnt_flatness_thr_7_0 0xA170 +#define reg_qnt_flatness_thr_7_0_pos 0 +#define reg_qnt_flatness_thr_7_0_len 8 +#define reg_qnt_flatness_thr_7_0_lsb 0 +#define xd_p_reg_qnt_flatness_thr_9_8 0xA171 +#define reg_qnt_flatness_thr_9_8_pos 0 +#define reg_qnt_flatness_thr_9_8_len 2 +#define reg_qnt_flatness_thr_9_8_lsb 8 +#define xd_p_reg_dca_tone_idx_5_0 0xA171 +#define reg_dca_tone_idx_5_0_pos 2 +#define reg_dca_tone_idx_5_0_len 6 +#define reg_dca_tone_idx_5_0_lsb 0 +#define xd_p_reg_dca_tone_idx_12_6 0xA172 +#define reg_dca_tone_idx_12_6_pos 0 +#define reg_dca_tone_idx_12_6_len 7 +#define reg_dca_tone_idx_12_6_lsb 6 +#define xd_p_reg_dca_data_vld 0xA173 +#define reg_dca_data_vld_pos 0 +#define reg_dca_data_vld_len 1 +#define reg_dca_data_vld_lsb 0 +#define xd_p_reg_dca_read_update 0xA173 +#define reg_dca_read_update_pos 1 +#define reg_dca_read_update_len 1 +#define reg_dca_read_update_lsb 0 +#define xd_r_reg_dca_data_re_5_0 0xA173 +#define reg_dca_data_re_5_0_pos 2 +#define reg_dca_data_re_5_0_len 6 +#define reg_dca_data_re_5_0_lsb 0 +#define xd_r_reg_dca_data_re_10_6 0xA174 +#define reg_dca_data_re_10_6_pos 0 +#define reg_dca_data_re_10_6_len 5 +#define reg_dca_data_re_10_6_lsb 6 +#define xd_r_reg_dca_data_im_7_0 0xA175 +#define reg_dca_data_im_7_0_pos 0 +#define reg_dca_data_im_7_0_len 8 +#define reg_dca_data_im_7_0_lsb 0 +#define xd_r_reg_dca_data_im_10_8 0xA176 +#define reg_dca_data_im_10_8_pos 0 +#define reg_dca_data_im_10_8_len 3 +#define reg_dca_data_im_10_8_lsb 8 +#define xd_r_reg_dca_data_h2_7_0 0xA178 +#define reg_dca_data_h2_7_0_pos 0 +#define reg_dca_data_h2_7_0_len 8 +#define reg_dca_data_h2_7_0_lsb 0 +#define xd_r_reg_dca_data_h2_9_8 0xA179 +#define reg_dca_data_h2_9_8_pos 0 +#define reg_dca_data_h2_9_8_len 2 +#define reg_dca_data_h2_9_8_lsb 8 +#define xd_p_reg_f_adc_7_0 0xA180 +#define reg_f_adc_7_0_pos 0 +#define reg_f_adc_7_0_len 8 +#define reg_f_adc_7_0_lsb 0 +#define xd_p_reg_f_adc_15_8 0xA181 +#define reg_f_adc_15_8_pos 0 +#define reg_f_adc_15_8_len 8 +#define reg_f_adc_15_8_lsb 8 +#define xd_p_reg_f_adc_23_16 0xA182 +#define reg_f_adc_23_16_pos 0 +#define reg_f_adc_23_16_len 8 +#define reg_f_adc_23_16_lsb 16 +#define xd_r_intp_mu_7_0 0xA190 +#define intp_mu_7_0_pos 0 +#define intp_mu_7_0_len 8 +#define intp_mu_7_0_lsb 0 +#define xd_r_intp_mu_15_8 0xA191 +#define intp_mu_15_8_pos 0 +#define intp_mu_15_8_len 8 +#define intp_mu_15_8_lsb 8 +#define xd_r_intp_mu_19_16 0xA192 +#define intp_mu_19_16_pos 0 +#define intp_mu_19_16_len 4 +#define intp_mu_19_16_lsb 16 +#define xd_p_reg_agc_rst 0xA1A0 +#define reg_agc_rst_pos 0 +#define reg_agc_rst_len 1 +#define reg_agc_rst_lsb 0 +#define xd_p_rf_agc_en 0xA1A0 +#define rf_agc_en_pos 1 +#define rf_agc_en_len 1 +#define rf_agc_en_lsb 0 +#define xd_p_rf_agc_dis 0xA1A0 +#define rf_agc_dis_pos 2 +#define rf_agc_dis_len 1 +#define rf_agc_dis_lsb 0 +#define xd_p_if_agc_rst 0xA1A0 +#define if_agc_rst_pos 3 +#define if_agc_rst_len 1 +#define if_agc_rst_lsb 0 +#define xd_p_if_agc_en 0xA1A0 +#define if_agc_en_pos 4 +#define if_agc_en_len 1 +#define if_agc_en_lsb 0 +#define xd_p_if_agc_dis 0xA1A0 +#define if_agc_dis_pos 5 +#define if_agc_dis_len 1 +#define if_agc_dis_lsb 0 +#define xd_p_agc_lock 0xA1A0 +#define agc_lock_pos 6 +#define agc_lock_len 1 +#define agc_lock_lsb 0 +#define xd_p_reg_tinr_rst 0xA1A1 +#define reg_tinr_rst_pos 0 +#define reg_tinr_rst_len 1 +#define reg_tinr_rst_lsb 0 +#define xd_p_reg_tinr_en 0xA1A1 +#define reg_tinr_en_pos 1 +#define reg_tinr_en_len 1 +#define reg_tinr_en_lsb 0 +#define xd_p_reg_ccifs_en 0xA1A2 +#define reg_ccifs_en_pos 0 +#define reg_ccifs_en_len 1 +#define reg_ccifs_en_lsb 0 +#define xd_p_reg_ccifs_dis 0xA1A2 +#define reg_ccifs_dis_pos 1 +#define reg_ccifs_dis_len 1 +#define reg_ccifs_dis_lsb 0 +#define xd_p_reg_ccifs_rst 0xA1A2 +#define reg_ccifs_rst_pos 2 +#define reg_ccifs_rst_len 1 +#define reg_ccifs_rst_lsb 0 +#define xd_p_reg_ccifs_byp 0xA1A2 +#define reg_ccifs_byp_pos 3 +#define reg_ccifs_byp_len 1 +#define reg_ccifs_byp_lsb 0 +#define xd_p_reg_ccif_en 0xA1A3 +#define reg_ccif_en_pos 0 +#define reg_ccif_en_len 1 +#define reg_ccif_en_lsb 0 +#define xd_p_reg_ccif_dis 0xA1A3 +#define reg_ccif_dis_pos 1 +#define reg_ccif_dis_len 1 +#define reg_ccif_dis_lsb 0 +#define xd_p_reg_ccif_rst 0xA1A3 +#define reg_ccif_rst_pos 2 +#define reg_ccif_rst_len 1 +#define reg_ccif_rst_lsb 0 +#define xd_p_reg_ccif_byp 0xA1A3 +#define reg_ccif_byp_pos 3 +#define reg_ccif_byp_len 1 +#define reg_ccif_byp_lsb 0 +#define xd_p_dagc1_rst 0xA1A4 +#define dagc1_rst_pos 0 +#define dagc1_rst_len 1 +#define dagc1_rst_lsb 0 +#define xd_p_dagc1_en 0xA1A4 +#define dagc1_en_pos 1 +#define dagc1_en_len 1 +#define dagc1_en_lsb 0 +#define xd_p_dagc1_mode 0xA1A4 +#define dagc1_mode_pos 2 +#define dagc1_mode_len 2 +#define dagc1_mode_lsb 0 +#define xd_p_dagc1_done 0xA1A4 +#define dagc1_done_pos 4 +#define dagc1_done_len 1 +#define dagc1_done_lsb 0 +#define xd_p_ccid_rst 0xA1A5 +#define ccid_rst_pos 0 +#define ccid_rst_len 1 +#define ccid_rst_lsb 0 +#define xd_p_ccid_en 0xA1A5 +#define ccid_en_pos 1 +#define ccid_en_len 1 +#define ccid_en_lsb 0 +#define xd_p_ccid_mode 0xA1A5 +#define ccid_mode_pos 2 +#define ccid_mode_len 2 +#define ccid_mode_lsb 0 +#define xd_p_ccid_done 0xA1A5 +#define ccid_done_pos 4 +#define ccid_done_len 1 +#define ccid_done_lsb 0 +#define xd_r_ccid_deted 0xA1A5 +#define ccid_deted_pos 5 +#define ccid_deted_len 1 +#define ccid_deted_lsb 0 +#define xd_p_ccid2_en 0xA1A5 +#define ccid2_en_pos 6 +#define ccid2_en_len 1 +#define ccid2_en_lsb 0 +#define xd_p_ccid2_done 0xA1A5 +#define ccid2_done_pos 7 +#define ccid2_done_len 1 +#define ccid2_done_lsb 0 +#define xd_p_reg_bfs_en 0xA1A6 +#define reg_bfs_en_pos 0 +#define reg_bfs_en_len 1 +#define reg_bfs_en_lsb 0 +#define xd_p_reg_bfs_dis 0xA1A6 +#define reg_bfs_dis_pos 1 +#define reg_bfs_dis_len 1 +#define reg_bfs_dis_lsb 0 +#define xd_p_reg_bfs_rst 0xA1A6 +#define reg_bfs_rst_pos 2 +#define reg_bfs_rst_len 1 +#define reg_bfs_rst_lsb 0 +#define xd_p_reg_bfs_byp 0xA1A6 +#define reg_bfs_byp_pos 3 +#define reg_bfs_byp_len 1 +#define reg_bfs_byp_lsb 0 +#define xd_p_reg_antif_en 0xA1A7 +#define reg_antif_en_pos 0 +#define reg_antif_en_len 1 +#define reg_antif_en_lsb 0 +#define xd_p_reg_antif_dis 0xA1A7 +#define reg_antif_dis_pos 1 +#define reg_antif_dis_len 1 +#define reg_antif_dis_lsb 0 +#define xd_p_reg_antif_rst 0xA1A7 +#define reg_antif_rst_pos 2 +#define reg_antif_rst_len 1 +#define reg_antif_rst_lsb 0 +#define xd_p_reg_antif_byp 0xA1A7 +#define reg_antif_byp_pos 3 +#define reg_antif_byp_len 1 +#define reg_antif_byp_lsb 0 +#define xd_p_intp_en 0xA1A8 +#define intp_en_pos 0 +#define intp_en_len 1 +#define intp_en_lsb 0 +#define xd_p_intp_dis 0xA1A8 +#define intp_dis_pos 1 +#define intp_dis_len 1 +#define intp_dis_lsb 0 +#define xd_p_intp_rst 0xA1A8 +#define intp_rst_pos 2 +#define intp_rst_len 1 +#define intp_rst_lsb 0 +#define xd_p_intp_byp 0xA1A8 +#define intp_byp_pos 3 +#define intp_byp_len 1 +#define intp_byp_lsb 0 +#define xd_p_reg_acif_en 0xA1A9 +#define reg_acif_en_pos 0 +#define reg_acif_en_len 1 +#define reg_acif_en_lsb 0 +#define xd_p_reg_acif_dis 0xA1A9 +#define reg_acif_dis_pos 1 +#define reg_acif_dis_len 1 +#define reg_acif_dis_lsb 0 +#define xd_p_reg_acif_rst 0xA1A9 +#define reg_acif_rst_pos 2 +#define reg_acif_rst_len 1 +#define reg_acif_rst_lsb 0 +#define xd_p_reg_acif_byp 0xA1A9 +#define reg_acif_byp_pos 3 +#define reg_acif_byp_len 1 +#define reg_acif_byp_lsb 0 +#define xd_p_reg_acif_sync_mode 0xA1A9 +#define reg_acif_sync_mode_pos 4 +#define reg_acif_sync_mode_len 1 +#define reg_acif_sync_mode_lsb 0 +#define xd_p_dagc2_rst 0xA1AA +#define dagc2_rst_pos 0 +#define dagc2_rst_len 1 +#define dagc2_rst_lsb 0 +#define xd_p_dagc2_en 0xA1AA +#define dagc2_en_pos 1 +#define dagc2_en_len 1 +#define dagc2_en_lsb 0 +#define xd_p_dagc2_mode 0xA1AA +#define dagc2_mode_pos 2 +#define dagc2_mode_len 2 +#define dagc2_mode_lsb 0 +#define xd_p_dagc2_done 0xA1AA +#define dagc2_done_pos 4 +#define dagc2_done_len 1 +#define dagc2_done_lsb 0 +#define xd_p_reg_dca_en 0xA1AB +#define reg_dca_en_pos 0 +#define reg_dca_en_len 1 +#define reg_dca_en_lsb 0 +#define xd_p_dagc2_accumulate_num_2k_7_0 0xA1C0 +#define dagc2_accumulate_num_2k_7_0_pos 0 +#define dagc2_accumulate_num_2k_7_0_len 8 +#define dagc2_accumulate_num_2k_7_0_lsb 0 +#define xd_p_dagc2_accumulate_num_2k_12_8 0xA1C1 +#define dagc2_accumulate_num_2k_12_8_pos 0 +#define dagc2_accumulate_num_2k_12_8_len 5 +#define dagc2_accumulate_num_2k_12_8_lsb 8 +#define xd_p_dagc2_accumulate_num_8k_7_0 0xA1C2 +#define dagc2_accumulate_num_8k_7_0_pos 0 +#define dagc2_accumulate_num_8k_7_0_len 8 +#define dagc2_accumulate_num_8k_7_0_lsb 0 +#define xd_p_dagc2_accumulate_num_8k_12_8 0xA1C3 +#define dagc2_accumulate_num_8k_12_8_pos 0 +#define dagc2_accumulate_num_8k_12_8_len 5 +#define dagc2_accumulate_num_8k_12_8_lsb 8 +#define xd_p_dagc2_desired_level_2_0 0xA1C3 +#define dagc2_desired_level_2_0_pos 5 +#define dagc2_desired_level_2_0_len 3 +#define dagc2_desired_level_2_0_lsb 0 +#define xd_p_dagc2_desired_level_8_3 0xA1C4 +#define dagc2_desired_level_8_3_pos 0 +#define dagc2_desired_level_8_3_len 6 +#define dagc2_desired_level_8_3_lsb 3 +#define xd_p_dagc2_apply_delay 0xA1C5 +#define dagc2_apply_delay_pos 0 +#define dagc2_apply_delay_len 7 +#define dagc2_apply_delay_lsb 0 +#define xd_p_dagc2_bypass_scale_ctl 0xA1C6 +#define dagc2_bypass_scale_ctl_pos 0 +#define dagc2_bypass_scale_ctl_len 3 +#define dagc2_bypass_scale_ctl_lsb 0 +#define xd_p_dagc2_programmable_shift1 0xA1C7 +#define dagc2_programmable_shift1_pos 0 +#define dagc2_programmable_shift1_len 8 +#define dagc2_programmable_shift1_lsb 0 +#define xd_p_dagc2_programmable_shift2 0xA1C8 +#define dagc2_programmable_shift2_pos 0 +#define dagc2_programmable_shift2_len 8 +#define dagc2_programmable_shift2_lsb 0 +#define xd_p_reg_dagc2_in_sat_cnt_7_0 0xA1C9 +#define reg_dagc2_in_sat_cnt_7_0_pos 0 +#define reg_dagc2_in_sat_cnt_7_0_len 8 +#define reg_dagc2_in_sat_cnt_7_0_lsb 0 +#define xd_p_reg_dagc2_in_sat_cnt_15_8 0xA1CA +#define reg_dagc2_in_sat_cnt_15_8_pos 0 +#define reg_dagc2_in_sat_cnt_15_8_len 8 +#define reg_dagc2_in_sat_cnt_15_8_lsb 8 +#define xd_p_reg_dagc2_in_sat_cnt_23_16 0xA1CB +#define reg_dagc2_in_sat_cnt_23_16_pos 0 +#define reg_dagc2_in_sat_cnt_23_16_len 8 +#define reg_dagc2_in_sat_cnt_23_16_lsb 16 +#define xd_p_reg_dagc2_in_sat_cnt_31_24 0xA1CC +#define reg_dagc2_in_sat_cnt_31_24_pos 0 +#define reg_dagc2_in_sat_cnt_31_24_len 8 +#define reg_dagc2_in_sat_cnt_31_24_lsb 24 +#define xd_p_reg_dagc2_out_sat_cnt_7_0 0xA1CD +#define reg_dagc2_out_sat_cnt_7_0_pos 0 +#define reg_dagc2_out_sat_cnt_7_0_len 8 +#define reg_dagc2_out_sat_cnt_7_0_lsb 0 +#define xd_p_reg_dagc2_out_sat_cnt_15_8 0xA1CE +#define reg_dagc2_out_sat_cnt_15_8_pos 0 +#define reg_dagc2_out_sat_cnt_15_8_len 8 +#define reg_dagc2_out_sat_cnt_15_8_lsb 8 +#define xd_p_reg_dagc2_out_sat_cnt_23_16 0xA1CF +#define reg_dagc2_out_sat_cnt_23_16_pos 0 +#define reg_dagc2_out_sat_cnt_23_16_len 8 +#define reg_dagc2_out_sat_cnt_23_16_lsb 16 +#define xd_p_reg_dagc2_out_sat_cnt_31_24 0xA1D0 +#define reg_dagc2_out_sat_cnt_31_24_pos 0 +#define reg_dagc2_out_sat_cnt_31_24_len 8 +#define reg_dagc2_out_sat_cnt_31_24_lsb 24 +#define xd_r_dagc2_multiplier_7_0 0xA1D6 +#define dagc2_multiplier_7_0_pos 0 +#define dagc2_multiplier_7_0_len 8 +#define dagc2_multiplier_7_0_lsb 0 +#define xd_r_dagc2_multiplier_15_8 0xA1D7 +#define dagc2_multiplier_15_8_pos 0 +#define dagc2_multiplier_15_8_len 8 +#define dagc2_multiplier_15_8_lsb 8 +#define xd_r_dagc2_right_shift_bits 0xA1D8 +#define dagc2_right_shift_bits_pos 0 +#define dagc2_right_shift_bits_len 4 +#define dagc2_right_shift_bits_lsb 0 +#define xd_p_cfoe_NS_coeff1_7_0 0xA200 +#define cfoe_NS_coeff1_7_0_pos 0 +#define cfoe_NS_coeff1_7_0_len 8 +#define cfoe_NS_coeff1_7_0_lsb 0 +#define xd_p_cfoe_NS_coeff1_15_8 0xA201 +#define cfoe_NS_coeff1_15_8_pos 0 +#define cfoe_NS_coeff1_15_8_len 8 +#define cfoe_NS_coeff1_15_8_lsb 8 +#define xd_p_cfoe_NS_coeff1_23_16 0xA202 +#define cfoe_NS_coeff1_23_16_pos 0 +#define cfoe_NS_coeff1_23_16_len 8 +#define cfoe_NS_coeff1_23_16_lsb 16 +#define xd_p_cfoe_NS_coeff1_25_24 0xA203 +#define cfoe_NS_coeff1_25_24_pos 0 +#define cfoe_NS_coeff1_25_24_len 2 +#define cfoe_NS_coeff1_25_24_lsb 24 +#define xd_p_cfoe_NS_coeff2_5_0 0xA203 +#define cfoe_NS_coeff2_5_0_pos 2 +#define cfoe_NS_coeff2_5_0_len 6 +#define cfoe_NS_coeff2_5_0_lsb 0 +#define xd_p_cfoe_NS_coeff2_13_6 0xA204 +#define cfoe_NS_coeff2_13_6_pos 0 +#define cfoe_NS_coeff2_13_6_len 8 +#define cfoe_NS_coeff2_13_6_lsb 6 +#define xd_p_cfoe_NS_coeff2_21_14 0xA205 +#define cfoe_NS_coeff2_21_14_pos 0 +#define cfoe_NS_coeff2_21_14_len 8 +#define cfoe_NS_coeff2_21_14_lsb 14 +#define xd_p_cfoe_NS_coeff2_24_22 0xA206 +#define cfoe_NS_coeff2_24_22_pos 0 +#define cfoe_NS_coeff2_24_22_len 3 +#define cfoe_NS_coeff2_24_22_lsb 22 +#define xd_p_cfoe_lf_c1_4_0 0xA206 +#define cfoe_lf_c1_4_0_pos 3 +#define cfoe_lf_c1_4_0_len 5 +#define cfoe_lf_c1_4_0_lsb 0 +#define xd_p_cfoe_lf_c1_12_5 0xA207 +#define cfoe_lf_c1_12_5_pos 0 +#define cfoe_lf_c1_12_5_len 8 +#define cfoe_lf_c1_12_5_lsb 5 +#define xd_p_cfoe_lf_c1_20_13 0xA208 +#define cfoe_lf_c1_20_13_pos 0 +#define cfoe_lf_c1_20_13_len 8 +#define cfoe_lf_c1_20_13_lsb 13 +#define xd_p_cfoe_lf_c1_25_21 0xA209 +#define cfoe_lf_c1_25_21_pos 0 +#define cfoe_lf_c1_25_21_len 5 +#define cfoe_lf_c1_25_21_lsb 21 +#define xd_p_cfoe_lf_c2_2_0 0xA209 +#define cfoe_lf_c2_2_0_pos 5 +#define cfoe_lf_c2_2_0_len 3 +#define cfoe_lf_c2_2_0_lsb 0 +#define xd_p_cfoe_lf_c2_10_3 0xA20A +#define cfoe_lf_c2_10_3_pos 0 +#define cfoe_lf_c2_10_3_len 8 +#define cfoe_lf_c2_10_3_lsb 3 +#define xd_p_cfoe_lf_c2_18_11 0xA20B +#define cfoe_lf_c2_18_11_pos 0 +#define cfoe_lf_c2_18_11_len 8 +#define cfoe_lf_c2_18_11_lsb 11 +#define xd_p_cfoe_lf_c2_25_19 0xA20C +#define cfoe_lf_c2_25_19_pos 0 +#define cfoe_lf_c2_25_19_len 7 +#define cfoe_lf_c2_25_19_lsb 19 +#define xd_p_cfoe_ifod_7_0 0xA20D +#define cfoe_ifod_7_0_pos 0 +#define cfoe_ifod_7_0_len 8 +#define cfoe_ifod_7_0_lsb 0 +#define xd_p_cfoe_ifod_10_8 0xA20E +#define cfoe_ifod_10_8_pos 0 +#define cfoe_ifod_10_8_len 3 +#define cfoe_ifod_10_8_lsb 8 +#define xd_p_cfoe_Divg_ctr_th 0xA20E +#define cfoe_Divg_ctr_th_pos 4 +#define cfoe_Divg_ctr_th_len 4 +#define cfoe_Divg_ctr_th_lsb 0 +#define xd_p_cfoe_FOT_divg_th 0xA20F +#define cfoe_FOT_divg_th_pos 0 +#define cfoe_FOT_divg_th_len 8 +#define cfoe_FOT_divg_th_lsb 0 +#define xd_p_cfoe_FOT_cnvg_th 0xA210 +#define cfoe_FOT_cnvg_th_pos 0 +#define cfoe_FOT_cnvg_th_len 8 +#define cfoe_FOT_cnvg_th_lsb 0 +#define xd_p_reg_cfoe_offset_7_0 0xA211 +#define reg_cfoe_offset_7_0_pos 0 +#define reg_cfoe_offset_7_0_len 8 +#define reg_cfoe_offset_7_0_lsb 0 +#define xd_p_reg_cfoe_offset_9_8 0xA212 +#define reg_cfoe_offset_9_8_pos 0 +#define reg_cfoe_offset_9_8_len 2 +#define reg_cfoe_offset_9_8_lsb 8 +#define xd_p_reg_cfoe_ifoe_sign_corr 0xA212 +#define reg_cfoe_ifoe_sign_corr_pos 2 +#define reg_cfoe_ifoe_sign_corr_len 1 +#define reg_cfoe_ifoe_sign_corr_lsb 0 +#define xd_r_cfoe_fot_LF_output_7_0 0xA218 +#define cfoe_fot_LF_output_7_0_pos 0 +#define cfoe_fot_LF_output_7_0_len 8 +#define cfoe_fot_LF_output_7_0_lsb 0 +#define xd_r_cfoe_fot_LF_output_15_8 0xA219 +#define cfoe_fot_LF_output_15_8_pos 0 +#define cfoe_fot_LF_output_15_8_len 8 +#define cfoe_fot_LF_output_15_8_lsb 8 +#define xd_r_cfoe_ifo_metric_7_0 0xA21A +#define cfoe_ifo_metric_7_0_pos 0 +#define cfoe_ifo_metric_7_0_len 8 +#define cfoe_ifo_metric_7_0_lsb 0 +#define xd_r_cfoe_ifo_metric_15_8 0xA21B +#define cfoe_ifo_metric_15_8_pos 0 +#define cfoe_ifo_metric_15_8_len 8 +#define cfoe_ifo_metric_15_8_lsb 8 +#define xd_r_cfoe_ifo_metric_23_16 0xA21C +#define cfoe_ifo_metric_23_16_pos 0 +#define cfoe_ifo_metric_23_16_len 8 +#define cfoe_ifo_metric_23_16_lsb 16 +#define xd_p_ste_Nu 0xA220 +#define ste_Nu_pos 0 +#define ste_Nu_len 2 +#define ste_Nu_lsb 0 +#define xd_p_ste_GI 0xA220 +#define ste_GI_pos 2 +#define ste_GI_len 3 +#define ste_GI_lsb 0 +#define xd_p_ste_symbol_num 0xA221 +#define ste_symbol_num_pos 0 +#define ste_symbol_num_len 2 +#define ste_symbol_num_lsb 0 +#define xd_p_ste_sample_num 0xA221 +#define ste_sample_num_pos 2 +#define ste_sample_num_len 2 +#define ste_sample_num_lsb 0 +#define xd_p_reg_ste_buf_en 0xA221 +#define reg_ste_buf_en_pos 7 +#define reg_ste_buf_en_len 1 +#define reg_ste_buf_en_lsb 0 +#define xd_p_ste_FFT_offset_7_0 0xA222 +#define ste_FFT_offset_7_0_pos 0 +#define ste_FFT_offset_7_0_len 8 +#define ste_FFT_offset_7_0_lsb 0 +#define xd_p_ste_FFT_offset_11_8 0xA223 +#define ste_FFT_offset_11_8_pos 0 +#define ste_FFT_offset_11_8_len 4 +#define ste_FFT_offset_11_8_lsb 8 +#define xd_p_reg_ste_tstmod 0xA223 +#define reg_ste_tstmod_pos 5 +#define reg_ste_tstmod_len 1 +#define reg_ste_tstmod_lsb 0 +#define xd_p_ste_adv_start_7_0 0xA224 +#define ste_adv_start_7_0_pos 0 +#define ste_adv_start_7_0_len 8 +#define ste_adv_start_7_0_lsb 0 +#define xd_p_ste_adv_start_10_8 0xA225 +#define ste_adv_start_10_8_pos 0 +#define ste_adv_start_10_8_len 3 +#define ste_adv_start_10_8_lsb 8 +#define xd_p_ste_adv_stop 0xA226 +#define ste_adv_stop_pos 0 +#define ste_adv_stop_len 8 +#define ste_adv_stop_lsb 0 +#define xd_r_ste_P_value_7_0 0xA228 +#define ste_P_value_7_0_pos 0 +#define ste_P_value_7_0_len 8 +#define ste_P_value_7_0_lsb 0 +#define xd_r_ste_P_value_10_8 0xA229 +#define ste_P_value_10_8_pos 0 +#define ste_P_value_10_8_len 3 +#define ste_P_value_10_8_lsb 8 +#define xd_r_ste_M_value_7_0 0xA22A +#define ste_M_value_7_0_pos 0 +#define ste_M_value_7_0_len 8 +#define ste_M_value_7_0_lsb 0 +#define xd_r_ste_M_value_10_8 0xA22B +#define ste_M_value_10_8_pos 0 +#define ste_M_value_10_8_len 3 +#define ste_M_value_10_8_lsb 8 +#define xd_r_ste_H1 0xA22C +#define ste_H1_pos 0 +#define ste_H1_len 7 +#define ste_H1_lsb 0 +#define xd_r_ste_H2 0xA22D +#define ste_H2_pos 0 +#define ste_H2_len 7 +#define ste_H2_lsb 0 +#define xd_r_ste_H3 0xA22E +#define ste_H3_pos 0 +#define ste_H3_len 7 +#define ste_H3_lsb 0 +#define xd_r_ste_H4 0xA22F +#define ste_H4_pos 0 +#define ste_H4_len 7 +#define ste_H4_lsb 0 +#define xd_r_ste_Corr_value_I_7_0 0xA230 +#define ste_Corr_value_I_7_0_pos 0 +#define ste_Corr_value_I_7_0_len 8 +#define ste_Corr_value_I_7_0_lsb 0 +#define xd_r_ste_Corr_value_I_15_8 0xA231 +#define ste_Corr_value_I_15_8_pos 0 +#define ste_Corr_value_I_15_8_len 8 +#define ste_Corr_value_I_15_8_lsb 8 +#define xd_r_ste_Corr_value_I_23_16 0xA232 +#define ste_Corr_value_I_23_16_pos 0 +#define ste_Corr_value_I_23_16_len 8 +#define ste_Corr_value_I_23_16_lsb 16 +#define xd_r_ste_Corr_value_I_27_24 0xA233 +#define ste_Corr_value_I_27_24_pos 0 +#define ste_Corr_value_I_27_24_len 4 +#define ste_Corr_value_I_27_24_lsb 24 +#define xd_r_ste_Corr_value_Q_7_0 0xA234 +#define ste_Corr_value_Q_7_0_pos 0 +#define ste_Corr_value_Q_7_0_len 8 +#define ste_Corr_value_Q_7_0_lsb 0 +#define xd_r_ste_Corr_value_Q_15_8 0xA235 +#define ste_Corr_value_Q_15_8_pos 0 +#define ste_Corr_value_Q_15_8_len 8 +#define ste_Corr_value_Q_15_8_lsb 8 +#define xd_r_ste_Corr_value_Q_23_16 0xA236 +#define ste_Corr_value_Q_23_16_pos 0 +#define ste_Corr_value_Q_23_16_len 8 +#define ste_Corr_value_Q_23_16_lsb 16 +#define xd_r_ste_Corr_value_Q_27_24 0xA237 +#define ste_Corr_value_Q_27_24_pos 0 +#define ste_Corr_value_Q_27_24_len 4 +#define ste_Corr_value_Q_27_24_lsb 24 +#define xd_r_ste_J_num_7_0 0xA238 +#define ste_J_num_7_0_pos 0 +#define ste_J_num_7_0_len 8 +#define ste_J_num_7_0_lsb 0 +#define xd_r_ste_J_num_15_8 0xA239 +#define ste_J_num_15_8_pos 0 +#define ste_J_num_15_8_len 8 +#define ste_J_num_15_8_lsb 8 +#define xd_r_ste_J_num_23_16 0xA23A +#define ste_J_num_23_16_pos 0 +#define ste_J_num_23_16_len 8 +#define ste_J_num_23_16_lsb 16 +#define xd_r_ste_J_num_31_24 0xA23B +#define ste_J_num_31_24_pos 0 +#define ste_J_num_31_24_len 8 +#define ste_J_num_31_24_lsb 24 +#define xd_r_ste_J_den_7_0 0xA23C +#define ste_J_den_7_0_pos 0 +#define ste_J_den_7_0_len 8 +#define ste_J_den_7_0_lsb 0 +#define xd_r_ste_J_den_15_8 0xA23D +#define ste_J_den_15_8_pos 0 +#define ste_J_den_15_8_len 8 +#define ste_J_den_15_8_lsb 8 +#define xd_r_ste_J_den_18_16 0xA23E +#define ste_J_den_18_16_pos 0 +#define ste_J_den_18_16_len 3 +#define ste_J_den_18_16_lsb 16 +#define xd_r_ste_Beacon_Indicator 0xA23E +#define ste_Beacon_Indicator_pos 4 +#define ste_Beacon_Indicator_len 1 +#define ste_Beacon_Indicator_lsb 0 +#define xd_r_tpsd_Frame_Num 0xA250 +#define tpsd_Frame_Num_pos 0 +#define tpsd_Frame_Num_len 2 +#define tpsd_Frame_Num_lsb 0 +#define xd_r_tpsd_Constel 0xA250 +#define tpsd_Constel_pos 2 +#define tpsd_Constel_len 2 +#define tpsd_Constel_lsb 0 +#define xd_r_tpsd_GI 0xA250 +#define tpsd_GI_pos 4 +#define tpsd_GI_len 2 +#define tpsd_GI_lsb 0 +#define xd_r_tpsd_Mode 0xA250 +#define tpsd_Mode_pos 6 +#define tpsd_Mode_len 2 +#define tpsd_Mode_lsb 0 +#define xd_r_tpsd_CR_HP 0xA251 +#define tpsd_CR_HP_pos 0 +#define tpsd_CR_HP_len 3 +#define tpsd_CR_HP_lsb 0 +#define xd_r_tpsd_CR_LP 0xA251 +#define tpsd_CR_LP_pos 3 +#define tpsd_CR_LP_len 3 +#define tpsd_CR_LP_lsb 0 +#define xd_r_tpsd_Hie 0xA252 +#define tpsd_Hie_pos 0 +#define tpsd_Hie_len 3 +#define tpsd_Hie_lsb 0 +#define xd_r_tpsd_Res_Bits 0xA252 +#define tpsd_Res_Bits_pos 3 +#define tpsd_Res_Bits_len 5 +#define tpsd_Res_Bits_lsb 0 +#define xd_r_tpsd_Res_Bits_0 0xA253 +#define tpsd_Res_Bits_0_pos 0 +#define tpsd_Res_Bits_0_len 1 +#define tpsd_Res_Bits_0_lsb 0 +#define xd_r_tpsd_LengthInd 0xA253 +#define tpsd_LengthInd_pos 1 +#define tpsd_LengthInd_len 6 +#define tpsd_LengthInd_lsb 0 +#define xd_r_tpsd_Cell_Id_7_0 0xA254 +#define tpsd_Cell_Id_7_0_pos 0 +#define tpsd_Cell_Id_7_0_len 8 +#define tpsd_Cell_Id_7_0_lsb 0 +#define xd_r_tpsd_Cell_Id_15_8 0xA255 +#define tpsd_Cell_Id_15_8_pos 0 +#define tpsd_Cell_Id_15_8_len 8 +#define tpsd_Cell_Id_15_8_lsb 0 +#define xd_p_reg_fft_mask_tone0_7_0 0xA260 +#define reg_fft_mask_tone0_7_0_pos 0 +#define reg_fft_mask_tone0_7_0_len 8 +#define reg_fft_mask_tone0_7_0_lsb 0 +#define xd_p_reg_fft_mask_tone0_12_8 0xA261 +#define reg_fft_mask_tone0_12_8_pos 0 +#define reg_fft_mask_tone0_12_8_len 5 +#define reg_fft_mask_tone0_12_8_lsb 8 +#define xd_p_reg_fft_mask_tone1_7_0 0xA262 +#define reg_fft_mask_tone1_7_0_pos 0 +#define reg_fft_mask_tone1_7_0_len 8 +#define reg_fft_mask_tone1_7_0_lsb 0 +#define xd_p_reg_fft_mask_tone1_12_8 0xA263 +#define reg_fft_mask_tone1_12_8_pos 0 +#define reg_fft_mask_tone1_12_8_len 5 +#define reg_fft_mask_tone1_12_8_lsb 8 +#define xd_p_reg_fft_mask_tone2_7_0 0xA264 +#define reg_fft_mask_tone2_7_0_pos 0 +#define reg_fft_mask_tone2_7_0_len 8 +#define reg_fft_mask_tone2_7_0_lsb 0 +#define xd_p_reg_fft_mask_tone2_12_8 0xA265 +#define reg_fft_mask_tone2_12_8_pos 0 +#define reg_fft_mask_tone2_12_8_len 5 +#define reg_fft_mask_tone2_12_8_lsb 8 +#define xd_p_reg_fft_mask_tone3_7_0 0xA266 +#define reg_fft_mask_tone3_7_0_pos 0 +#define reg_fft_mask_tone3_7_0_len 8 +#define reg_fft_mask_tone3_7_0_lsb 0 +#define xd_p_reg_fft_mask_tone3_12_8 0xA267 +#define reg_fft_mask_tone3_12_8_pos 0 +#define reg_fft_mask_tone3_12_8_len 5 +#define reg_fft_mask_tone3_12_8_lsb 8 +#define xd_p_reg_fft_mask_from0_7_0 0xA268 +#define reg_fft_mask_from0_7_0_pos 0 +#define reg_fft_mask_from0_7_0_len 8 +#define reg_fft_mask_from0_7_0_lsb 0 +#define xd_p_reg_fft_mask_from0_12_8 0xA269 +#define reg_fft_mask_from0_12_8_pos 0 +#define reg_fft_mask_from0_12_8_len 5 +#define reg_fft_mask_from0_12_8_lsb 8 +#define xd_p_reg_fft_mask_to0_7_0 0xA26A +#define reg_fft_mask_to0_7_0_pos 0 +#define reg_fft_mask_to0_7_0_len 8 +#define reg_fft_mask_to0_7_0_lsb 0 +#define xd_p_reg_fft_mask_to0_12_8 0xA26B +#define reg_fft_mask_to0_12_8_pos 0 +#define reg_fft_mask_to0_12_8_len 5 +#define reg_fft_mask_to0_12_8_lsb 8 +#define xd_p_reg_fft_mask_from1_7_0 0xA26C +#define reg_fft_mask_from1_7_0_pos 0 +#define reg_fft_mask_from1_7_0_len 8 +#define reg_fft_mask_from1_7_0_lsb 0 +#define xd_p_reg_fft_mask_from1_12_8 0xA26D +#define reg_fft_mask_from1_12_8_pos 0 +#define reg_fft_mask_from1_12_8_len 5 +#define reg_fft_mask_from1_12_8_lsb 8 +#define xd_p_reg_fft_mask_to1_7_0 0xA26E +#define reg_fft_mask_to1_7_0_pos 0 +#define reg_fft_mask_to1_7_0_len 8 +#define reg_fft_mask_to1_7_0_lsb 0 +#define xd_p_reg_fft_mask_to1_12_8 0xA26F +#define reg_fft_mask_to1_12_8_pos 0 +#define reg_fft_mask_to1_12_8_len 5 +#define reg_fft_mask_to1_12_8_lsb 8 +#define xd_p_reg_cge_idx0_7_0 0xA280 +#define reg_cge_idx0_7_0_pos 0 +#define reg_cge_idx0_7_0_len 8 +#define reg_cge_idx0_7_0_lsb 0 +#define xd_p_reg_cge_idx0_12_8 0xA281 +#define reg_cge_idx0_12_8_pos 0 +#define reg_cge_idx0_12_8_len 5 +#define reg_cge_idx0_12_8_lsb 8 +#define xd_p_reg_cge_idx1_7_0 0xA282 +#define reg_cge_idx1_7_0_pos 0 +#define reg_cge_idx1_7_0_len 8 +#define reg_cge_idx1_7_0_lsb 0 +#define xd_p_reg_cge_idx1_12_8 0xA283 +#define reg_cge_idx1_12_8_pos 0 +#define reg_cge_idx1_12_8_len 5 +#define reg_cge_idx1_12_8_lsb 8 +#define xd_p_reg_cge_idx2_7_0 0xA284 +#define reg_cge_idx2_7_0_pos 0 +#define reg_cge_idx2_7_0_len 8 +#define reg_cge_idx2_7_0_lsb 0 +#define xd_p_reg_cge_idx2_12_8 0xA285 +#define reg_cge_idx2_12_8_pos 0 +#define reg_cge_idx2_12_8_len 5 +#define reg_cge_idx2_12_8_lsb 8 +#define xd_p_reg_cge_idx3_7_0 0xA286 +#define reg_cge_idx3_7_0_pos 0 +#define reg_cge_idx3_7_0_len 8 +#define reg_cge_idx3_7_0_lsb 0 +#define xd_p_reg_cge_idx3_12_8 0xA287 +#define reg_cge_idx3_12_8_pos 0 +#define reg_cge_idx3_12_8_len 5 +#define reg_cge_idx3_12_8_lsb 8 +#define xd_p_reg_cge_idx4_7_0 0xA288 +#define reg_cge_idx4_7_0_pos 0 +#define reg_cge_idx4_7_0_len 8 +#define reg_cge_idx4_7_0_lsb 0 +#define xd_p_reg_cge_idx4_12_8 0xA289 +#define reg_cge_idx4_12_8_pos 0 +#define reg_cge_idx4_12_8_len 5 +#define reg_cge_idx4_12_8_lsb 8 +#define xd_p_reg_cge_idx5_7_0 0xA28A +#define reg_cge_idx5_7_0_pos 0 +#define reg_cge_idx5_7_0_len 8 +#define reg_cge_idx5_7_0_lsb 0 +#define xd_p_reg_cge_idx5_12_8 0xA28B +#define reg_cge_idx5_12_8_pos 0 +#define reg_cge_idx5_12_8_len 5 +#define reg_cge_idx5_12_8_lsb 8 +#define xd_p_reg_cge_idx6_7_0 0xA28C +#define reg_cge_idx6_7_0_pos 0 +#define reg_cge_idx6_7_0_len 8 +#define reg_cge_idx6_7_0_lsb 0 +#define xd_p_reg_cge_idx6_12_8 0xA28D +#define reg_cge_idx6_12_8_pos 0 +#define reg_cge_idx6_12_8_len 5 +#define reg_cge_idx6_12_8_lsb 8 +#define xd_p_reg_cge_idx7_7_0 0xA28E +#define reg_cge_idx7_7_0_pos 0 +#define reg_cge_idx7_7_0_len 8 +#define reg_cge_idx7_7_0_lsb 0 +#define xd_p_reg_cge_idx7_12_8 0xA28F +#define reg_cge_idx7_12_8_pos 0 +#define reg_cge_idx7_12_8_len 5 +#define reg_cge_idx7_12_8_lsb 8 +#define xd_p_reg_cge_idx8_7_0 0xA290 +#define reg_cge_idx8_7_0_pos 0 +#define reg_cge_idx8_7_0_len 8 +#define reg_cge_idx8_7_0_lsb 0 +#define xd_p_reg_cge_idx8_12_8 0xA291 +#define reg_cge_idx8_12_8_pos 0 +#define reg_cge_idx8_12_8_len 5 +#define reg_cge_idx8_12_8_lsb 8 +#define xd_p_reg_cge_idx9_7_0 0xA292 +#define reg_cge_idx9_7_0_pos 0 +#define reg_cge_idx9_7_0_len 8 +#define reg_cge_idx9_7_0_lsb 0 +#define xd_p_reg_cge_idx9_12_8 0xA293 +#define reg_cge_idx9_12_8_pos 0 +#define reg_cge_idx9_12_8_len 5 +#define reg_cge_idx9_12_8_lsb 8 +#define xd_p_reg_cge_idx10_7_0 0xA294 +#define reg_cge_idx10_7_0_pos 0 +#define reg_cge_idx10_7_0_len 8 +#define reg_cge_idx10_7_0_lsb 0 +#define xd_p_reg_cge_idx10_12_8 0xA295 +#define reg_cge_idx10_12_8_pos 0 +#define reg_cge_idx10_12_8_len 5 +#define reg_cge_idx10_12_8_lsb 8 +#define xd_p_reg_cge_idx11_7_0 0xA296 +#define reg_cge_idx11_7_0_pos 0 +#define reg_cge_idx11_7_0_len 8 +#define reg_cge_idx11_7_0_lsb 0 +#define xd_p_reg_cge_idx11_12_8 0xA297 +#define reg_cge_idx11_12_8_pos 0 +#define reg_cge_idx11_12_8_len 5 +#define reg_cge_idx11_12_8_lsb 8 +#define xd_p_reg_cge_idx12_7_0 0xA298 +#define reg_cge_idx12_7_0_pos 0 +#define reg_cge_idx12_7_0_len 8 +#define reg_cge_idx12_7_0_lsb 0 +#define xd_p_reg_cge_idx12_12_8 0xA299 +#define reg_cge_idx12_12_8_pos 0 +#define reg_cge_idx12_12_8_len 5 +#define reg_cge_idx12_12_8_lsb 8 +#define xd_p_reg_cge_idx13_7_0 0xA29A +#define reg_cge_idx13_7_0_pos 0 +#define reg_cge_idx13_7_0_len 8 +#define reg_cge_idx13_7_0_lsb 0 +#define xd_p_reg_cge_idx13_12_8 0xA29B +#define reg_cge_idx13_12_8_pos 0 +#define reg_cge_idx13_12_8_len 5 +#define reg_cge_idx13_12_8_lsb 8 +#define xd_p_reg_cge_idx14_7_0 0xA29C +#define reg_cge_idx14_7_0_pos 0 +#define reg_cge_idx14_7_0_len 8 +#define reg_cge_idx14_7_0_lsb 0 +#define xd_p_reg_cge_idx14_12_8 0xA29D +#define reg_cge_idx14_12_8_pos 0 +#define reg_cge_idx14_12_8_len 5 +#define reg_cge_idx14_12_8_lsb 8 +#define xd_p_reg_cge_idx15_7_0 0xA29E +#define reg_cge_idx15_7_0_pos 0 +#define reg_cge_idx15_7_0_len 8 +#define reg_cge_idx15_7_0_lsb 0 +#define xd_p_reg_cge_idx15_12_8 0xA29F +#define reg_cge_idx15_12_8_pos 0 +#define reg_cge_idx15_12_8_len 5 +#define reg_cge_idx15_12_8_lsb 8 +#define xd_r_reg_fft_crc 0xA2A8 +#define reg_fft_crc_pos 0 +#define reg_fft_crc_len 8 +#define reg_fft_crc_lsb 0 +#define xd_p_fd_fft_shift_max 0xA2A9 +#define fd_fft_shift_max_pos 0 +#define fd_fft_shift_max_len 4 +#define fd_fft_shift_max_lsb 0 +#define xd_r_fd_fft_shift 0xA2A9 +#define fd_fft_shift_pos 4 +#define fd_fft_shift_len 4 +#define fd_fft_shift_lsb 0 +#define xd_r_fd_fft_frame_num 0xA2AA +#define fd_fft_frame_num_pos 0 +#define fd_fft_frame_num_len 2 +#define fd_fft_frame_num_lsb 0 +#define xd_r_fd_fft_symbol_count 0xA2AB +#define fd_fft_symbol_count_pos 0 +#define fd_fft_symbol_count_len 7 +#define fd_fft_symbol_count_lsb 0 +#define xd_r_reg_fft_idx_max_7_0 0xA2AC +#define reg_fft_idx_max_7_0_pos 0 +#define reg_fft_idx_max_7_0_len 8 +#define reg_fft_idx_max_7_0_lsb 0 +#define xd_r_reg_fft_idx_max_12_8 0xA2AD +#define reg_fft_idx_max_12_8_pos 0 +#define reg_fft_idx_max_12_8_len 5 +#define reg_fft_idx_max_12_8_lsb 8 +#define xd_p_reg_cge_program 0xA2AE +#define reg_cge_program_pos 0 +#define reg_cge_program_len 1 +#define reg_cge_program_lsb 0 +#define xd_p_reg_cge_fixed 0xA2AE +#define reg_cge_fixed_pos 1 +#define reg_cge_fixed_len 1 +#define reg_cge_fixed_lsb 0 +#define xd_p_reg_fft_rotate_en 0xA2AE +#define reg_fft_rotate_en_pos 2 +#define reg_fft_rotate_en_len 1 +#define reg_fft_rotate_en_lsb 0 +#define xd_p_reg_fft_rotate_base_4_0 0xA2AE +#define reg_fft_rotate_base_4_0_pos 3 +#define reg_fft_rotate_base_4_0_len 5 +#define reg_fft_rotate_base_4_0_lsb 0 +#define xd_p_reg_fft_rotate_base_12_5 0xA2AF +#define reg_fft_rotate_base_12_5_pos 0 +#define reg_fft_rotate_base_12_5_len 8 +#define reg_fft_rotate_base_12_5_lsb 5 +#define xd_p_reg_gp_trigger_fd 0xA2B8 +#define reg_gp_trigger_fd_pos 0 +#define reg_gp_trigger_fd_len 1 +#define reg_gp_trigger_fd_lsb 0 +#define xd_p_reg_trigger_sel_fd 0xA2B8 +#define reg_trigger_sel_fd_pos 1 +#define reg_trigger_sel_fd_len 2 +#define reg_trigger_sel_fd_lsb 0 +#define xd_p_reg_trigger_module_sel_fd 0xA2B9 +#define reg_trigger_module_sel_fd_pos 0 +#define reg_trigger_module_sel_fd_len 6 +#define reg_trigger_module_sel_fd_lsb 0 +#define xd_p_reg_trigger_set_sel_fd 0xA2BA +#define reg_trigger_set_sel_fd_pos 0 +#define reg_trigger_set_sel_fd_len 6 +#define reg_trigger_set_sel_fd_lsb 0 +#define xd_p_reg_fd_noname_7_0 0xA2BC +#define reg_fd_noname_7_0_pos 0 +#define reg_fd_noname_7_0_len 8 +#define reg_fd_noname_7_0_lsb 0 +#define xd_p_reg_fd_noname_15_8 0xA2BD +#define reg_fd_noname_15_8_pos 0 +#define reg_fd_noname_15_8_len 8 +#define reg_fd_noname_15_8_lsb 8 +#define xd_p_reg_fd_noname_23_16 0xA2BE +#define reg_fd_noname_23_16_pos 0 +#define reg_fd_noname_23_16_len 8 +#define reg_fd_noname_23_16_lsb 16 +#define xd_p_reg_fd_noname_31_24 0xA2BF +#define reg_fd_noname_31_24_pos 0 +#define reg_fd_noname_31_24_len 8 +#define reg_fd_noname_31_24_lsb 24 +#define xd_r_fd_fpcc_cp_corr_signn 0xA2C0 +#define fd_fpcc_cp_corr_signn_pos 0 +#define fd_fpcc_cp_corr_signn_len 8 +#define fd_fpcc_cp_corr_signn_lsb 0 +#define xd_p_reg_feq_s1 0xA2C1 +#define reg_feq_s1_pos 0 +#define reg_feq_s1_len 5 +#define reg_feq_s1_lsb 0 +#define xd_p_fd_fpcc_cp_corr_tone_th 0xA2C2 +#define fd_fpcc_cp_corr_tone_th_pos 0 +#define fd_fpcc_cp_corr_tone_th_len 6 +#define fd_fpcc_cp_corr_tone_th_lsb 0 +#define xd_p_fd_fpcc_cp_corr_symbol_log_th 0xA2C3 +#define fd_fpcc_cp_corr_symbol_log_th_pos 0 +#define fd_fpcc_cp_corr_symbol_log_th_len 4 +#define fd_fpcc_cp_corr_symbol_log_th_lsb 0 +#define xd_p_fd_fpcc_cp_corr_int 0xA2C4 +#define fd_fpcc_cp_corr_int_pos 0 +#define fd_fpcc_cp_corr_int_len 1 +#define fd_fpcc_cp_corr_int_lsb 0 +#define xd_p_reg_sfoe_ns_7_0 0xA320 +#define reg_sfoe_ns_7_0_pos 0 +#define reg_sfoe_ns_7_0_len 8 +#define reg_sfoe_ns_7_0_lsb 0 +#define xd_p_reg_sfoe_ns_14_8 0xA321 +#define reg_sfoe_ns_14_8_pos 0 +#define reg_sfoe_ns_14_8_len 7 +#define reg_sfoe_ns_14_8_lsb 8 +#define xd_p_reg_sfoe_c1_7_0 0xA322 +#define reg_sfoe_c1_7_0_pos 0 +#define reg_sfoe_c1_7_0_len 8 +#define reg_sfoe_c1_7_0_lsb 0 +#define xd_p_reg_sfoe_c1_15_8 0xA323 +#define reg_sfoe_c1_15_8_pos 0 +#define reg_sfoe_c1_15_8_len 8 +#define reg_sfoe_c1_15_8_lsb 8 +#define xd_p_reg_sfoe_c1_17_16 0xA324 +#define reg_sfoe_c1_17_16_pos 0 +#define reg_sfoe_c1_17_16_len 2 +#define reg_sfoe_c1_17_16_lsb 16 +#define xd_p_reg_sfoe_c2_7_0 0xA325 +#define reg_sfoe_c2_7_0_pos 0 +#define reg_sfoe_c2_7_0_len 8 +#define reg_sfoe_c2_7_0_lsb 0 +#define xd_p_reg_sfoe_c2_15_8 0xA326 +#define reg_sfoe_c2_15_8_pos 0 +#define reg_sfoe_c2_15_8_len 8 +#define reg_sfoe_c2_15_8_lsb 8 +#define xd_p_reg_sfoe_c2_17_16 0xA327 +#define reg_sfoe_c2_17_16_pos 0 +#define reg_sfoe_c2_17_16_len 2 +#define reg_sfoe_c2_17_16_lsb 16 +#define xd_r_reg_sfoe_out_9_2 0xA328 +#define reg_sfoe_out_9_2_pos 0 +#define reg_sfoe_out_9_2_len 8 +#define reg_sfoe_out_9_2_lsb 0 +#define xd_r_reg_sfoe_out_1_0 0xA329 +#define reg_sfoe_out_1_0_pos 0 +#define reg_sfoe_out_1_0_len 2 +#define reg_sfoe_out_1_0_lsb 0 +#define xd_p_reg_sfoe_lm_counter_th 0xA32A +#define reg_sfoe_lm_counter_th_pos 0 +#define reg_sfoe_lm_counter_th_len 4 +#define reg_sfoe_lm_counter_th_lsb 0 +#define xd_p_reg_sfoe_convg_th 0xA32B +#define reg_sfoe_convg_th_pos 0 +#define reg_sfoe_convg_th_len 8 +#define reg_sfoe_convg_th_lsb 0 +#define xd_p_reg_sfoe_divg_th 0xA32C +#define reg_sfoe_divg_th_pos 0 +#define reg_sfoe_divg_th_len 8 +#define reg_sfoe_divg_th_lsb 0 +#define xd_p_fd_tpsd_en 0xA330 +#define fd_tpsd_en_pos 0 +#define fd_tpsd_en_len 1 +#define fd_tpsd_en_lsb 0 +#define xd_p_fd_tpsd_dis 0xA330 +#define fd_tpsd_dis_pos 1 +#define fd_tpsd_dis_len 1 +#define fd_tpsd_dis_lsb 0 +#define xd_p_fd_tpsd_rst 0xA330 +#define fd_tpsd_rst_pos 2 +#define fd_tpsd_rst_len 1 +#define fd_tpsd_rst_lsb 0 +#define xd_p_fd_tpsd_lock 0xA330 +#define fd_tpsd_lock_pos 3 +#define fd_tpsd_lock_len 1 +#define fd_tpsd_lock_lsb 0 +#define xd_r_fd_tpsd_s19 0xA330 +#define fd_tpsd_s19_pos 4 +#define fd_tpsd_s19_len 1 +#define fd_tpsd_s19_lsb 0 +#define xd_r_fd_tpsd_s17 0xA330 +#define fd_tpsd_s17_pos 5 +#define fd_tpsd_s17_len 1 +#define fd_tpsd_s17_lsb 0 +#define xd_p_fd_sfr_ste_en 0xA331 +#define fd_sfr_ste_en_pos 0 +#define fd_sfr_ste_en_len 1 +#define fd_sfr_ste_en_lsb 0 +#define xd_p_fd_sfr_ste_dis 0xA331 +#define fd_sfr_ste_dis_pos 1 +#define fd_sfr_ste_dis_len 1 +#define fd_sfr_ste_dis_lsb 0 +#define xd_p_fd_sfr_ste_rst 0xA331 +#define fd_sfr_ste_rst_pos 2 +#define fd_sfr_ste_rst_len 1 +#define fd_sfr_ste_rst_lsb 0 +#define xd_p_fd_sfr_ste_mode 0xA331 +#define fd_sfr_ste_mode_pos 3 +#define fd_sfr_ste_mode_len 1 +#define fd_sfr_ste_mode_lsb 0 +#define xd_p_fd_sfr_ste_done 0xA331 +#define fd_sfr_ste_done_pos 4 +#define fd_sfr_ste_done_len 1 +#define fd_sfr_ste_done_lsb 0 +#define xd_p_reg_cfoe_ffoe_en 0xA332 +#define reg_cfoe_ffoe_en_pos 0 +#define reg_cfoe_ffoe_en_len 1 +#define reg_cfoe_ffoe_en_lsb 0 +#define xd_p_reg_cfoe_ffoe_dis 0xA332 +#define reg_cfoe_ffoe_dis_pos 1 +#define reg_cfoe_ffoe_dis_len 1 +#define reg_cfoe_ffoe_dis_lsb 0 +#define xd_p_reg_cfoe_ffoe_rst 0xA332 +#define reg_cfoe_ffoe_rst_pos 2 +#define reg_cfoe_ffoe_rst_len 1 +#define reg_cfoe_ffoe_rst_lsb 0 +#define xd_p_reg_cfoe_ifoe_en 0xA332 +#define reg_cfoe_ifoe_en_pos 3 +#define reg_cfoe_ifoe_en_len 1 +#define reg_cfoe_ifoe_en_lsb 0 +#define xd_p_reg_cfoe_ifoe_dis 0xA332 +#define reg_cfoe_ifoe_dis_pos 4 +#define reg_cfoe_ifoe_dis_len 1 +#define reg_cfoe_ifoe_dis_lsb 0 +#define xd_p_reg_cfoe_ifoe_rst 0xA332 +#define reg_cfoe_ifoe_rst_pos 5 +#define reg_cfoe_ifoe_rst_len 1 +#define reg_cfoe_ifoe_rst_lsb 0 +#define xd_p_reg_cfoe_fot_en 0xA332 +#define reg_cfoe_fot_en_pos 6 +#define reg_cfoe_fot_en_len 1 +#define reg_cfoe_fot_en_lsb 0 +#define xd_p_reg_cfoe_fot_lm_en 0xA332 +#define reg_cfoe_fot_lm_en_pos 7 +#define reg_cfoe_fot_lm_en_len 1 +#define reg_cfoe_fot_lm_en_lsb 0 +#define xd_p_reg_cfoe_fot_rst 0xA333 +#define reg_cfoe_fot_rst_pos 0 +#define reg_cfoe_fot_rst_len 1 +#define reg_cfoe_fot_rst_lsb 0 +#define xd_r_fd_cfoe_ffoe_done 0xA333 +#define fd_cfoe_ffoe_done_pos 1 +#define fd_cfoe_ffoe_done_len 1 +#define fd_cfoe_ffoe_done_lsb 0 +#define xd_p_fd_cfoe_metric_vld 0xA333 +#define fd_cfoe_metric_vld_pos 2 +#define fd_cfoe_metric_vld_len 1 +#define fd_cfoe_metric_vld_lsb 0 +#define xd_p_reg_cfoe_ifod_vld 0xA333 +#define reg_cfoe_ifod_vld_pos 3 +#define reg_cfoe_ifod_vld_len 1 +#define reg_cfoe_ifod_vld_lsb 0 +#define xd_r_fd_cfoe_ifoe_done 0xA333 +#define fd_cfoe_ifoe_done_pos 4 +#define fd_cfoe_ifoe_done_len 1 +#define fd_cfoe_ifoe_done_lsb 0 +#define xd_r_fd_cfoe_fot_valid 0xA333 +#define fd_cfoe_fot_valid_pos 5 +#define fd_cfoe_fot_valid_len 1 +#define fd_cfoe_fot_valid_lsb 0 +#define xd_p_reg_cfoe_divg_int 0xA333 +#define reg_cfoe_divg_int_pos 6 +#define reg_cfoe_divg_int_len 1 +#define reg_cfoe_divg_int_lsb 0 +#define xd_r_reg_cfoe_divg_flag 0xA333 +#define reg_cfoe_divg_flag_pos 7 +#define reg_cfoe_divg_flag_len 1 +#define reg_cfoe_divg_flag_lsb 0 +#define xd_p_reg_sfoe_en 0xA334 +#define reg_sfoe_en_pos 0 +#define reg_sfoe_en_len 1 +#define reg_sfoe_en_lsb 0 +#define xd_p_reg_sfoe_dis 0xA334 +#define reg_sfoe_dis_pos 1 +#define reg_sfoe_dis_len 1 +#define reg_sfoe_dis_lsb 0 +#define xd_p_reg_sfoe_rst 0xA334 +#define reg_sfoe_rst_pos 2 +#define reg_sfoe_rst_len 1 +#define reg_sfoe_rst_lsb 0 +#define xd_p_reg_sfoe_vld_int 0xA334 +#define reg_sfoe_vld_int_pos 3 +#define reg_sfoe_vld_int_len 1 +#define reg_sfoe_vld_int_lsb 0 +#define xd_p_reg_sfoe_lm_en 0xA334 +#define reg_sfoe_lm_en_pos 4 +#define reg_sfoe_lm_en_len 1 +#define reg_sfoe_lm_en_lsb 0 +#define xd_p_reg_sfoe_divg_int 0xA334 +#define reg_sfoe_divg_int_pos 5 +#define reg_sfoe_divg_int_len 1 +#define reg_sfoe_divg_int_lsb 0 +#define xd_r_reg_sfoe_divg_flag 0xA334 +#define reg_sfoe_divg_flag_pos 6 +#define reg_sfoe_divg_flag_len 1 +#define reg_sfoe_divg_flag_lsb 0 +#define xd_p_reg_fft_rst 0xA335 +#define reg_fft_rst_pos 0 +#define reg_fft_rst_len 1 +#define reg_fft_rst_lsb 0 +#define xd_p_reg_fft_fast_beacon 0xA335 +#define reg_fft_fast_beacon_pos 1 +#define reg_fft_fast_beacon_len 1 +#define reg_fft_fast_beacon_lsb 0 +#define xd_p_reg_fft_fast_valid 0xA335 +#define reg_fft_fast_valid_pos 2 +#define reg_fft_fast_valid_len 1 +#define reg_fft_fast_valid_lsb 0 +#define xd_p_reg_fft_mask_en 0xA335 +#define reg_fft_mask_en_pos 3 +#define reg_fft_mask_en_len 1 +#define reg_fft_mask_en_lsb 0 +#define xd_p_reg_fft_crc_en 0xA335 +#define reg_fft_crc_en_pos 4 +#define reg_fft_crc_en_len 1 +#define reg_fft_crc_en_lsb 0 +#define xd_p_reg_finr_en 0xA336 +#define reg_finr_en_pos 0 +#define reg_finr_en_len 1 +#define reg_finr_en_lsb 0 +#define xd_p_fd_fste_en 0xA337 +#define fd_fste_en_pos 1 +#define fd_fste_en_len 1 +#define fd_fste_en_lsb 0 +#define xd_p_fd_sqi_tps_level_shift 0xA338 +#define fd_sqi_tps_level_shift_pos 0 +#define fd_sqi_tps_level_shift_len 8 +#define fd_sqi_tps_level_shift_lsb 0 +#define xd_p_fd_pilot_ma_len 0xA339 +#define fd_pilot_ma_len_pos 0 +#define fd_pilot_ma_len_len 6 +#define fd_pilot_ma_len_lsb 0 +#define xd_p_fd_tps_ma_len 0xA33A +#define fd_tps_ma_len_pos 0 +#define fd_tps_ma_len_len 6 +#define fd_tps_ma_len_lsb 0 +#define xd_p_fd_sqi_s3 0xA33B +#define fd_sqi_s3_pos 0 +#define fd_sqi_s3_len 8 +#define fd_sqi_s3_lsb 0 +#define xd_p_fd_sqi_dummy_reg_0 0xA33C +#define fd_sqi_dummy_reg_0_pos 0 +#define fd_sqi_dummy_reg_0_len 1 +#define fd_sqi_dummy_reg_0_lsb 0 +#define xd_p_fd_sqi_debug_sel 0xA33C +#define fd_sqi_debug_sel_pos 1 +#define fd_sqi_debug_sel_len 2 +#define fd_sqi_debug_sel_lsb 0 +#define xd_p_fd_sqi_s2 0xA33C +#define fd_sqi_s2_pos 3 +#define fd_sqi_s2_len 5 +#define fd_sqi_s2_lsb 0 +#define xd_p_fd_sqi_dummy_reg_1 0xA33D +#define fd_sqi_dummy_reg_1_pos 0 +#define fd_sqi_dummy_reg_1_len 1 +#define fd_sqi_dummy_reg_1_lsb 0 +#define xd_p_fd_inr_ignore 0xA33D +#define fd_inr_ignore_pos 1 +#define fd_inr_ignore_len 1 +#define fd_inr_ignore_lsb 0 +#define xd_p_fd_pilot_ignore 0xA33D +#define fd_pilot_ignore_pos 2 +#define fd_pilot_ignore_len 1 +#define fd_pilot_ignore_lsb 0 +#define xd_p_fd_etps_ignore 0xA33D +#define fd_etps_ignore_pos 3 +#define fd_etps_ignore_len 1 +#define fd_etps_ignore_lsb 0 +#define xd_p_fd_sqi_s1 0xA33D +#define fd_sqi_s1_pos 4 +#define fd_sqi_s1_len 4 +#define fd_sqi_s1_lsb 0 +#define xd_p_reg_fste_ehw_7_0 0xA33E +#define reg_fste_ehw_7_0_pos 0 +#define reg_fste_ehw_7_0_len 8 +#define reg_fste_ehw_7_0_lsb 0 +#define xd_p_reg_fste_ehw_9_8 0xA33F +#define reg_fste_ehw_9_8_pos 0 +#define reg_fste_ehw_9_8_len 2 +#define reg_fste_ehw_9_8_lsb 8 +#define xd_p_reg_fste_i_adj_vld 0xA33F +#define reg_fste_i_adj_vld_pos 2 +#define reg_fste_i_adj_vld_len 1 +#define reg_fste_i_adj_vld_lsb 0 +#define xd_p_reg_fste_phase_ini_7_0 0xA340 +#define reg_fste_phase_ini_7_0_pos 0 +#define reg_fste_phase_ini_7_0_len 8 +#define reg_fste_phase_ini_7_0_lsb 0 +#define xd_p_reg_fste_phase_ini_11_8 0xA341 +#define reg_fste_phase_ini_11_8_pos 0 +#define reg_fste_phase_ini_11_8_len 4 +#define reg_fste_phase_ini_11_8_lsb 8 +#define xd_p_reg_fste_phase_inc_3_0 0xA341 +#define reg_fste_phase_inc_3_0_pos 4 +#define reg_fste_phase_inc_3_0_len 4 +#define reg_fste_phase_inc_3_0_lsb 0 +#define xd_p_reg_fste_phase_inc_11_4 0xA342 +#define reg_fste_phase_inc_11_4_pos 0 +#define reg_fste_phase_inc_11_4_len 8 +#define reg_fste_phase_inc_11_4_lsb 4 +#define xd_p_reg_fste_acum_cost_cnt_max 0xA343 +#define reg_fste_acum_cost_cnt_max_pos 0 +#define reg_fste_acum_cost_cnt_max_len 4 +#define reg_fste_acum_cost_cnt_max_lsb 0 +#define xd_p_reg_fste_step_size_std 0xA343 +#define reg_fste_step_size_std_pos 4 +#define reg_fste_step_size_std_len 4 +#define reg_fste_step_size_std_lsb 0 +#define xd_p_reg_fste_step_size_max 0xA344 +#define reg_fste_step_size_max_pos 0 +#define reg_fste_step_size_max_len 4 +#define reg_fste_step_size_max_lsb 0 +#define xd_p_reg_fste_step_size_min 0xA344 +#define reg_fste_step_size_min_pos 4 +#define reg_fste_step_size_min_len 4 +#define reg_fste_step_size_min_lsb 0 +#define xd_p_reg_fste_frac_step_size_7_0 0xA345 +#define reg_fste_frac_step_size_7_0_pos 0 +#define reg_fste_frac_step_size_7_0_len 8 +#define reg_fste_frac_step_size_7_0_lsb 0 +#define xd_p_reg_fste_frac_step_size_15_8 0xA346 +#define reg_fste_frac_step_size_15_8_pos 0 +#define reg_fste_frac_step_size_15_8_len 8 +#define reg_fste_frac_step_size_15_8_lsb 8 +#define xd_p_reg_fste_frac_step_size_19_16 0xA347 +#define reg_fste_frac_step_size_19_16_pos 0 +#define reg_fste_frac_step_size_19_16_len 4 +#define reg_fste_frac_step_size_19_16_lsb 16 +#define xd_p_reg_fste_rpd_dir_cnt_max 0xA347 +#define reg_fste_rpd_dir_cnt_max_pos 4 +#define reg_fste_rpd_dir_cnt_max_len 4 +#define reg_fste_rpd_dir_cnt_max_lsb 0 +#define xd_p_reg_fste_ehs 0xA348 +#define reg_fste_ehs_pos 0 +#define reg_fste_ehs_len 4 +#define reg_fste_ehs_lsb 0 +#define xd_p_reg_fste_frac_cost_cnt_max_3_0 0xA348 +#define reg_fste_frac_cost_cnt_max_3_0_pos 4 +#define reg_fste_frac_cost_cnt_max_3_0_len 4 +#define reg_fste_frac_cost_cnt_max_3_0_lsb 0 +#define xd_p_reg_fste_frac_cost_cnt_max_9_4 0xA349 +#define reg_fste_frac_cost_cnt_max_9_4_pos 0 +#define reg_fste_frac_cost_cnt_max_9_4_len 6 +#define reg_fste_frac_cost_cnt_max_9_4_lsb 4 +#define xd_p_reg_fste_w0_7_0 0xA34A +#define reg_fste_w0_7_0_pos 0 +#define reg_fste_w0_7_0_len 8 +#define reg_fste_w0_7_0_lsb 0 +#define xd_p_reg_fste_w0_11_8 0xA34B +#define reg_fste_w0_11_8_pos 0 +#define reg_fste_w0_11_8_len 4 +#define reg_fste_w0_11_8_lsb 8 +#define xd_p_reg_fste_w1_3_0 0xA34B +#define reg_fste_w1_3_0_pos 4 +#define reg_fste_w1_3_0_len 4 +#define reg_fste_w1_3_0_lsb 0 +#define xd_p_reg_fste_w1_11_4 0xA34C +#define reg_fste_w1_11_4_pos 0 +#define reg_fste_w1_11_4_len 8 +#define reg_fste_w1_11_4_lsb 4 +#define xd_p_reg_fste_w2_7_0 0xA34D +#define reg_fste_w2_7_0_pos 0 +#define reg_fste_w2_7_0_len 8 +#define reg_fste_w2_7_0_lsb 0 +#define xd_p_reg_fste_w2_11_8 0xA34E +#define reg_fste_w2_11_8_pos 0 +#define reg_fste_w2_11_8_len 4 +#define reg_fste_w2_11_8_lsb 8 +#define xd_p_reg_fste_w3_3_0 0xA34E +#define reg_fste_w3_3_0_pos 4 +#define reg_fste_w3_3_0_len 4 +#define reg_fste_w3_3_0_lsb 0 +#define xd_p_reg_fste_w3_11_4 0xA34F +#define reg_fste_w3_11_4_pos 0 +#define reg_fste_w3_11_4_len 8 +#define reg_fste_w3_11_4_lsb 4 +#define xd_p_reg_fste_w4_7_0 0xA350 +#define reg_fste_w4_7_0_pos 0 +#define reg_fste_w4_7_0_len 8 +#define reg_fste_w4_7_0_lsb 0 +#define xd_p_reg_fste_w4_11_8 0xA351 +#define reg_fste_w4_11_8_pos 0 +#define reg_fste_w4_11_8_len 4 +#define reg_fste_w4_11_8_lsb 8 +#define xd_p_reg_fste_w5_3_0 0xA351 +#define reg_fste_w5_3_0_pos 4 +#define reg_fste_w5_3_0_len 4 +#define reg_fste_w5_3_0_lsb 0 +#define xd_p_reg_fste_w5_11_4 0xA352 +#define reg_fste_w5_11_4_pos 0 +#define reg_fste_w5_11_4_len 8 +#define reg_fste_w5_11_4_lsb 4 +#define xd_p_reg_fste_w6_7_0 0xA353 +#define reg_fste_w6_7_0_pos 0 +#define reg_fste_w6_7_0_len 8 +#define reg_fste_w6_7_0_lsb 0 +#define xd_p_reg_fste_w6_11_8 0xA354 +#define reg_fste_w6_11_8_pos 0 +#define reg_fste_w6_11_8_len 4 +#define reg_fste_w6_11_8_lsb 8 +#define xd_p_reg_fste_w7_3_0 0xA354 +#define reg_fste_w7_3_0_pos 4 +#define reg_fste_w7_3_0_len 4 +#define reg_fste_w7_3_0_lsb 0 +#define xd_p_reg_fste_w7_11_4 0xA355 +#define reg_fste_w7_11_4_pos 0 +#define reg_fste_w7_11_4_len 8 +#define reg_fste_w7_11_4_lsb 4 +#define xd_p_reg_fste_w8_7_0 0xA356 +#define reg_fste_w8_7_0_pos 0 +#define reg_fste_w8_7_0_len 8 +#define reg_fste_w8_7_0_lsb 0 +#define xd_p_reg_fste_w8_11_8 0xA357 +#define reg_fste_w8_11_8_pos 0 +#define reg_fste_w8_11_8_len 4 +#define reg_fste_w8_11_8_lsb 8 +#define xd_p_reg_fste_w9_3_0 0xA357 +#define reg_fste_w9_3_0_pos 4 +#define reg_fste_w9_3_0_len 4 +#define reg_fste_w9_3_0_lsb 0 +#define xd_p_reg_fste_w9_11_4 0xA358 +#define reg_fste_w9_11_4_pos 0 +#define reg_fste_w9_11_4_len 8 +#define reg_fste_w9_11_4_lsb 4 +#define xd_p_reg_fste_wa_7_0 0xA359 +#define reg_fste_wa_7_0_pos 0 +#define reg_fste_wa_7_0_len 8 +#define reg_fste_wa_7_0_lsb 0 +#define xd_p_reg_fste_wa_11_8 0xA35A +#define reg_fste_wa_11_8_pos 0 +#define reg_fste_wa_11_8_len 4 +#define reg_fste_wa_11_8_lsb 8 +#define xd_p_reg_fste_wb_3_0 0xA35A +#define reg_fste_wb_3_0_pos 4 +#define reg_fste_wb_3_0_len 4 +#define reg_fste_wb_3_0_lsb 0 +#define xd_p_reg_fste_wb_11_4 0xA35B +#define reg_fste_wb_11_4_pos 0 +#define reg_fste_wb_11_4_len 8 +#define reg_fste_wb_11_4_lsb 4 +#define xd_r_fd_fste_i_adj 0xA35C +#define fd_fste_i_adj_pos 0 +#define fd_fste_i_adj_len 5 +#define fd_fste_i_adj_lsb 0 +#define xd_r_fd_fste_f_adj_7_0 0xA35D +#define fd_fste_f_adj_7_0_pos 0 +#define fd_fste_f_adj_7_0_len 8 +#define fd_fste_f_adj_7_0_lsb 0 +#define xd_r_fd_fste_f_adj_15_8 0xA35E +#define fd_fste_f_adj_15_8_pos 0 +#define fd_fste_f_adj_15_8_len 8 +#define fd_fste_f_adj_15_8_lsb 8 +#define xd_r_fd_fste_f_adj_19_16 0xA35F +#define fd_fste_f_adj_19_16_pos 0 +#define fd_fste_f_adj_19_16_len 4 +#define fd_fste_f_adj_19_16_lsb 16 +#define xd_p_reg_feq_Leak_Bypass 0xA366 +#define reg_feq_Leak_Bypass_pos 0 +#define reg_feq_Leak_Bypass_len 1 +#define reg_feq_Leak_Bypass_lsb 0 +#define xd_p_reg_feq_Leak_Mneg1 0xA366 +#define reg_feq_Leak_Mneg1_pos 1 +#define reg_feq_Leak_Mneg1_len 3 +#define reg_feq_Leak_Mneg1_lsb 0 +#define xd_p_reg_feq_Leak_B_ShiftQ 0xA366 +#define reg_feq_Leak_B_ShiftQ_pos 4 +#define reg_feq_Leak_B_ShiftQ_len 4 +#define reg_feq_Leak_B_ShiftQ_lsb 0 +#define xd_p_reg_feq_Leak_B_Float0 0xA367 +#define reg_feq_Leak_B_Float0_pos 0 +#define reg_feq_Leak_B_Float0_len 8 +#define reg_feq_Leak_B_Float0_lsb 0 +#define xd_p_reg_feq_Leak_B_Float1 0xA368 +#define reg_feq_Leak_B_Float1_pos 0 +#define reg_feq_Leak_B_Float1_len 8 +#define reg_feq_Leak_B_Float1_lsb 0 +#define xd_p_reg_feq_Leak_B_Float2 0xA369 +#define reg_feq_Leak_B_Float2_pos 0 +#define reg_feq_Leak_B_Float2_len 8 +#define reg_feq_Leak_B_Float2_lsb 0 +#define xd_p_reg_feq_Leak_B_Float3 0xA36A +#define reg_feq_Leak_B_Float3_pos 0 +#define reg_feq_Leak_B_Float3_len 8 +#define reg_feq_Leak_B_Float3_lsb 0 +#define xd_p_reg_feq_Leak_B_Float4 0xA36B +#define reg_feq_Leak_B_Float4_pos 0 +#define reg_feq_Leak_B_Float4_len 8 +#define reg_feq_Leak_B_Float4_lsb 0 +#define xd_p_reg_feq_Leak_B_Float5 0xA36C +#define reg_feq_Leak_B_Float5_pos 0 +#define reg_feq_Leak_B_Float5_len 8 +#define reg_feq_Leak_B_Float5_lsb 0 +#define xd_p_reg_feq_Leak_B_Float6 0xA36D +#define reg_feq_Leak_B_Float6_pos 0 +#define reg_feq_Leak_B_Float6_len 8 +#define reg_feq_Leak_B_Float6_lsb 0 +#define xd_p_reg_feq_Leak_B_Float7 0xA36E +#define reg_feq_Leak_B_Float7_pos 0 +#define reg_feq_Leak_B_Float7_len 8 +#define reg_feq_Leak_B_Float7_lsb 0 +#define xd_r_reg_feq_data_h2_7_0 0xA36F +#define reg_feq_data_h2_7_0_pos 0 +#define reg_feq_data_h2_7_0_len 8 +#define reg_feq_data_h2_7_0_lsb 0 +#define xd_r_reg_feq_data_h2_9_8 0xA370 +#define reg_feq_data_h2_9_8_pos 0 +#define reg_feq_data_h2_9_8_len 2 +#define reg_feq_data_h2_9_8_lsb 8 +#define xd_p_reg_feq_leak_use_slice_tps 0xA371 +#define reg_feq_leak_use_slice_tps_pos 0 +#define reg_feq_leak_use_slice_tps_len 1 +#define reg_feq_leak_use_slice_tps_lsb 0 +#define xd_p_reg_feq_read_update 0xA371 +#define reg_feq_read_update_pos 1 +#define reg_feq_read_update_len 1 +#define reg_feq_read_update_lsb 0 +#define xd_p_reg_feq_data_vld 0xA371 +#define reg_feq_data_vld_pos 2 +#define reg_feq_data_vld_len 1 +#define reg_feq_data_vld_lsb 0 +#define xd_p_reg_feq_tone_idx_4_0 0xA371 +#define reg_feq_tone_idx_4_0_pos 3 +#define reg_feq_tone_idx_4_0_len 5 +#define reg_feq_tone_idx_4_0_lsb 0 +#define xd_p_reg_feq_tone_idx_12_5 0xA372 +#define reg_feq_tone_idx_12_5_pos 0 +#define reg_feq_tone_idx_12_5_len 8 +#define reg_feq_tone_idx_12_5_lsb 5 +#define xd_r_reg_feq_data_re_7_0 0xA373 +#define reg_feq_data_re_7_0_pos 0 +#define reg_feq_data_re_7_0_len 8 +#define reg_feq_data_re_7_0_lsb 0 +#define xd_r_reg_feq_data_re_10_8 0xA374 +#define reg_feq_data_re_10_8_pos 0 +#define reg_feq_data_re_10_8_len 3 +#define reg_feq_data_re_10_8_lsb 8 +#define xd_r_reg_feq_data_im_7_0 0xA375 +#define reg_feq_data_im_7_0_pos 0 +#define reg_feq_data_im_7_0_len 8 +#define reg_feq_data_im_7_0_lsb 0 +#define xd_r_reg_feq_data_im_10_8 0xA376 +#define reg_feq_data_im_10_8_pos 0 +#define reg_feq_data_im_10_8_len 3 +#define reg_feq_data_im_10_8_lsb 8 +#define xd_r_reg_feq_y_re 0xA377 +#define reg_feq_y_re_pos 0 +#define reg_feq_y_re_len 8 +#define reg_feq_y_re_lsb 0 +#define xd_r_reg_feq_y_im 0xA378 +#define reg_feq_y_im_pos 0 +#define reg_feq_y_im_len 8 +#define reg_feq_y_im_lsb 0 +#define xd_r_reg_feq_h_re_7_0 0xA379 +#define reg_feq_h_re_7_0_pos 0 +#define reg_feq_h_re_7_0_len 8 +#define reg_feq_h_re_7_0_lsb 0 +#define xd_r_reg_feq_h_re_8 0xA37A +#define reg_feq_h_re_8_pos 0 +#define reg_feq_h_re_8_len 1 +#define reg_feq_h_re_8_lsb 0 +#define xd_r_reg_feq_h_im_7_0 0xA37B +#define reg_feq_h_im_7_0_pos 0 +#define reg_feq_h_im_7_0_len 8 +#define reg_feq_h_im_7_0_lsb 0 +#define xd_r_reg_feq_h_im_8 0xA37C +#define reg_feq_h_im_8_pos 0 +#define reg_feq_h_im_8_len 1 +#define reg_feq_h_im_8_lsb 0 +#define xd_p_fec_super_frm_unit_7_0 0xA380 +#define fec_super_frm_unit_7_0_pos 0 +#define fec_super_frm_unit_7_0_len 8 +#define fec_super_frm_unit_7_0_lsb 0 +#define xd_p_fec_super_frm_unit_15_8 0xA381 +#define fec_super_frm_unit_15_8_pos 0 +#define fec_super_frm_unit_15_8_len 8 +#define fec_super_frm_unit_15_8_lsb 8 +#define xd_r_fec_vtb_err_bit_cnt_7_0 0xA382 +#define fec_vtb_err_bit_cnt_7_0_pos 0 +#define fec_vtb_err_bit_cnt_7_0_len 8 +#define fec_vtb_err_bit_cnt_7_0_lsb 0 +#define xd_r_fec_vtb_err_bit_cnt_15_8 0xA383 +#define fec_vtb_err_bit_cnt_15_8_pos 0 +#define fec_vtb_err_bit_cnt_15_8_len 8 +#define fec_vtb_err_bit_cnt_15_8_lsb 8 +#define xd_r_fec_vtb_err_bit_cnt_23_16 0xA384 +#define fec_vtb_err_bit_cnt_23_16_pos 0 +#define fec_vtb_err_bit_cnt_23_16_len 8 +#define fec_vtb_err_bit_cnt_23_16_lsb 16 +#define xd_p_fec_rsd_packet_unit_7_0 0xA385 +#define fec_rsd_packet_unit_7_0_pos 0 +#define fec_rsd_packet_unit_7_0_len 8 +#define fec_rsd_packet_unit_7_0_lsb 0 +#define xd_p_fec_rsd_packet_unit_15_8 0xA386 +#define fec_rsd_packet_unit_15_8_pos 0 +#define fec_rsd_packet_unit_15_8_len 8 +#define fec_rsd_packet_unit_15_8_lsb 8 +#define xd_r_fec_rsd_bit_err_cnt_7_0 0xA387 +#define fec_rsd_bit_err_cnt_7_0_pos 0 +#define fec_rsd_bit_err_cnt_7_0_len 8 +#define fec_rsd_bit_err_cnt_7_0_lsb 0 +#define xd_r_fec_rsd_bit_err_cnt_15_8 0xA388 +#define fec_rsd_bit_err_cnt_15_8_pos 0 +#define fec_rsd_bit_err_cnt_15_8_len 8 +#define fec_rsd_bit_err_cnt_15_8_lsb 8 +#define xd_r_fec_rsd_bit_err_cnt_23_16 0xA389 +#define fec_rsd_bit_err_cnt_23_16_pos 0 +#define fec_rsd_bit_err_cnt_23_16_len 8 +#define fec_rsd_bit_err_cnt_23_16_lsb 16 +#define xd_r_fec_rsd_abort_packet_cnt_7_0 0xA38A +#define fec_rsd_abort_packet_cnt_7_0_pos 0 +#define fec_rsd_abort_packet_cnt_7_0_len 8 +#define fec_rsd_abort_packet_cnt_7_0_lsb 0 +#define xd_r_fec_rsd_abort_packet_cnt_15_8 0xA38B +#define fec_rsd_abort_packet_cnt_15_8_pos 0 +#define fec_rsd_abort_packet_cnt_15_8_len 8 +#define fec_rsd_abort_packet_cnt_15_8_lsb 8 +#define xd_p_fec_RSD_PKT_NUM_PER_UNIT_7_0 0xA38C +#define fec_RSD_PKT_NUM_PER_UNIT_7_0_pos 0 +#define fec_RSD_PKT_NUM_PER_UNIT_7_0_len 8 +#define fec_RSD_PKT_NUM_PER_UNIT_7_0_lsb 0 +#define xd_p_fec_RSD_PKT_NUM_PER_UNIT_15_8 0xA38D +#define fec_RSD_PKT_NUM_PER_UNIT_15_8_pos 0 +#define fec_RSD_PKT_NUM_PER_UNIT_15_8_len 8 +#define fec_RSD_PKT_NUM_PER_UNIT_15_8_lsb 8 +#define xd_p_fec_RS_TH_1_7_0 0xA38E +#define fec_RS_TH_1_7_0_pos 0 +#define fec_RS_TH_1_7_0_len 8 +#define fec_RS_TH_1_7_0_lsb 0 +#define xd_p_fec_RS_TH_1_15_8 0xA38F +#define fec_RS_TH_1_15_8_pos 0 +#define fec_RS_TH_1_15_8_len 8 +#define fec_RS_TH_1_15_8_lsb 8 +#define xd_p_fec_RS_TH_2 0xA390 +#define fec_RS_TH_2_pos 0 +#define fec_RS_TH_2_len 8 +#define fec_RS_TH_2_lsb 0 +#define xd_p_fec_mon_en 0xA391 +#define fec_mon_en_pos 0 +#define fec_mon_en_len 1 +#define fec_mon_en_lsb 0 +#define xd_p_reg_b8to47 0xA391 +#define reg_b8to47_pos 1 +#define reg_b8to47_len 1 +#define reg_b8to47_lsb 0 +#define xd_p_reg_rsd_sync_rep 0xA391 +#define reg_rsd_sync_rep_pos 2 +#define reg_rsd_sync_rep_len 1 +#define reg_rsd_sync_rep_lsb 0 +#define xd_p_fec_rsd_retrain_rst 0xA391 +#define fec_rsd_retrain_rst_pos 3 +#define fec_rsd_retrain_rst_len 1 +#define fec_rsd_retrain_rst_lsb 0 +#define xd_r_fec_rsd_ber_rdy 0xA391 +#define fec_rsd_ber_rdy_pos 4 +#define fec_rsd_ber_rdy_len 1 +#define fec_rsd_ber_rdy_lsb 0 +#define xd_p_fec_rsd_ber_rst 0xA391 +#define fec_rsd_ber_rst_pos 5 +#define fec_rsd_ber_rst_len 1 +#define fec_rsd_ber_rst_lsb 0 +#define xd_r_fec_vtb_ber_rdy 0xA391 +#define fec_vtb_ber_rdy_pos 6 +#define fec_vtb_ber_rdy_len 1 +#define fec_vtb_ber_rdy_lsb 0 +#define xd_p_fec_vtb_ber_rst 0xA391 +#define fec_vtb_ber_rst_pos 7 +#define fec_vtb_ber_rst_len 1 +#define fec_vtb_ber_rst_lsb 0 +#define xd_p_reg_vtb_clk40en 0xA392 +#define reg_vtb_clk40en_pos 0 +#define reg_vtb_clk40en_len 1 +#define reg_vtb_clk40en_lsb 0 +#define xd_p_fec_vtb_rsd_mon_en 0xA392 +#define fec_vtb_rsd_mon_en_pos 1 +#define fec_vtb_rsd_mon_en_len 1 +#define fec_vtb_rsd_mon_en_lsb 0 +#define xd_p_reg_fec_data_en 0xA392 +#define reg_fec_data_en_pos 2 +#define reg_fec_data_en_len 1 +#define reg_fec_data_en_lsb 0 +#define xd_p_fec_dummy_reg_2 0xA392 +#define fec_dummy_reg_2_pos 3 +#define fec_dummy_reg_2_len 3 +#define fec_dummy_reg_2_lsb 0 +#define xd_p_reg_sync_chk 0xA392 +#define reg_sync_chk_pos 6 +#define reg_sync_chk_len 1 +#define reg_sync_chk_lsb 0 +#define xd_p_fec_rsd_bypass 0xA392 +#define fec_rsd_bypass_pos 7 +#define fec_rsd_bypass_len 1 +#define fec_rsd_bypass_lsb 0 +#define xd_p_fec_sw_rst 0xA393 +#define fec_sw_rst_pos 0 +#define fec_sw_rst_len 1 +#define fec_sw_rst_lsb 0 +#define xd_r_fec_vtb_pm_crc 0xA394 +#define fec_vtb_pm_crc_pos 0 +#define fec_vtb_pm_crc_len 8 +#define fec_vtb_pm_crc_lsb 0 +#define xd_r_fec_vtb_tb_7_crc 0xA395 +#define fec_vtb_tb_7_crc_pos 0 +#define fec_vtb_tb_7_crc_len 8 +#define fec_vtb_tb_7_crc_lsb 0 +#define xd_r_fec_vtb_tb_6_crc 0xA396 +#define fec_vtb_tb_6_crc_pos 0 +#define fec_vtb_tb_6_crc_len 8 +#define fec_vtb_tb_6_crc_lsb 0 +#define xd_r_fec_vtb_tb_5_crc 0xA397 +#define fec_vtb_tb_5_crc_pos 0 +#define fec_vtb_tb_5_crc_len 8 +#define fec_vtb_tb_5_crc_lsb 0 +#define xd_r_fec_vtb_tb_4_crc 0xA398 +#define fec_vtb_tb_4_crc_pos 0 +#define fec_vtb_tb_4_crc_len 8 +#define fec_vtb_tb_4_crc_lsb 0 +#define xd_r_fec_vtb_tb_3_crc 0xA399 +#define fec_vtb_tb_3_crc_pos 0 +#define fec_vtb_tb_3_crc_len 8 +#define fec_vtb_tb_3_crc_lsb 0 +#define xd_r_fec_vtb_tb_2_crc 0xA39A +#define fec_vtb_tb_2_crc_pos 0 +#define fec_vtb_tb_2_crc_len 8 +#define fec_vtb_tb_2_crc_lsb 0 +#define xd_r_fec_vtb_tb_1_crc 0xA39B +#define fec_vtb_tb_1_crc_pos 0 +#define fec_vtb_tb_1_crc_len 8 +#define fec_vtb_tb_1_crc_lsb 0 +#define xd_r_fec_vtb_tb_0_crc 0xA39C +#define fec_vtb_tb_0_crc_pos 0 +#define fec_vtb_tb_0_crc_len 8 +#define fec_vtb_tb_0_crc_lsb 0 +#define xd_r_fec_rsd_bank0_crc 0xA39D +#define fec_rsd_bank0_crc_pos 0 +#define fec_rsd_bank0_crc_len 8 +#define fec_rsd_bank0_crc_lsb 0 +#define xd_r_fec_rsd_bank1_crc 0xA39E +#define fec_rsd_bank1_crc_pos 0 +#define fec_rsd_bank1_crc_len 8 +#define fec_rsd_bank1_crc_lsb 0 +#define xd_r_fec_idi_vtb_crc 0xA39F +#define fec_idi_vtb_crc_pos 0 +#define fec_idi_vtb_crc_len 8 +#define fec_idi_vtb_crc_lsb 0 +#define xd_g_reg_tpsd_txmod 0xA3C0 +#define reg_tpsd_txmod_pos 0 +#define reg_tpsd_txmod_len 2 +#define reg_tpsd_txmod_lsb 0 +#define xd_g_reg_tpsd_gi 0xA3C0 +#define reg_tpsd_gi_pos 2 +#define reg_tpsd_gi_len 2 +#define reg_tpsd_gi_lsb 0 +#define xd_g_reg_tpsd_hier 0xA3C0 +#define reg_tpsd_hier_pos 4 +#define reg_tpsd_hier_len 3 +#define reg_tpsd_hier_lsb 0 +#define xd_g_reg_bw 0xA3C1 +#define reg_bw_pos 2 +#define reg_bw_len 2 +#define reg_bw_lsb 0 +#define xd_g_reg_dec_pri 0xA3C1 +#define reg_dec_pri_pos 4 +#define reg_dec_pri_len 1 +#define reg_dec_pri_lsb 0 +#define xd_g_reg_tpsd_const 0xA3C1 +#define reg_tpsd_const_pos 6 +#define reg_tpsd_const_len 2 +#define reg_tpsd_const_lsb 0 +#define xd_g_reg_tpsd_hpcr 0xA3C2 +#define reg_tpsd_hpcr_pos 0 +#define reg_tpsd_hpcr_len 3 +#define reg_tpsd_hpcr_lsb 0 +#define xd_g_reg_tpsd_lpcr 0xA3C2 +#define reg_tpsd_lpcr_pos 3 +#define reg_tpsd_lpcr_len 3 +#define reg_tpsd_lpcr_lsb 0 +#define xd_g_reg_ofsm_clk 0xA3D0 +#define reg_ofsm_clk_pos 0 +#define reg_ofsm_clk_len 3 +#define reg_ofsm_clk_lsb 0 +#define xd_g_reg_fclk_cfg 0xA3D1 +#define reg_fclk_cfg_pos 0 +#define reg_fclk_cfg_len 1 +#define reg_fclk_cfg_lsb 0 +#define xd_g_reg_fclk_idi 0xA3D1 +#define reg_fclk_idi_pos 1 +#define reg_fclk_idi_len 1 +#define reg_fclk_idi_lsb 0 +#define xd_g_reg_fclk_odi 0xA3D1 +#define reg_fclk_odi_pos 2 +#define reg_fclk_odi_len 1 +#define reg_fclk_odi_lsb 0 +#define xd_g_reg_fclk_rsd 0xA3D1 +#define reg_fclk_rsd_pos 3 +#define reg_fclk_rsd_len 1 +#define reg_fclk_rsd_lsb 0 +#define xd_g_reg_fclk_vtb 0xA3D1 +#define reg_fclk_vtb_pos 4 +#define reg_fclk_vtb_len 1 +#define reg_fclk_vtb_lsb 0 +#define xd_g_reg_fclk_cste 0xA3D1 +#define reg_fclk_cste_pos 5 +#define reg_fclk_cste_len 1 +#define reg_fclk_cste_lsb 0 +#define xd_g_reg_fclk_mp2if 0xA3D1 +#define reg_fclk_mp2if_pos 6 +#define reg_fclk_mp2if_len 1 +#define reg_fclk_mp2if_lsb 0 +#define xd_I2C_i2c_m_slave_addr 0xA400 +#define i2c_m_slave_addr_pos 0 +#define i2c_m_slave_addr_len 8 +#define i2c_m_slave_addr_lsb 0 +#define xd_I2C_i2c_m_data1 0xA401 +#define i2c_m_data1_pos 0 +#define i2c_m_data1_len 8 +#define i2c_m_data1_lsb 0 +#define xd_I2C_i2c_m_data2 0xA402 +#define i2c_m_data2_pos 0 +#define i2c_m_data2_len 8 +#define i2c_m_data2_lsb 0 +#define xd_I2C_i2c_m_data3 0xA403 +#define i2c_m_data3_pos 0 +#define i2c_m_data3_len 8 +#define i2c_m_data3_lsb 0 +#define xd_I2C_i2c_m_data4 0xA404 +#define i2c_m_data4_pos 0 +#define i2c_m_data4_len 8 +#define i2c_m_data4_lsb 0 +#define xd_I2C_i2c_m_data5 0xA405 +#define i2c_m_data5_pos 0 +#define i2c_m_data5_len 8 +#define i2c_m_data5_lsb 0 +#define xd_I2C_i2c_m_data6 0xA406 +#define i2c_m_data6_pos 0 +#define i2c_m_data6_len 8 +#define i2c_m_data6_lsb 0 +#define xd_I2C_i2c_m_data7 0xA407 +#define i2c_m_data7_pos 0 +#define i2c_m_data7_len 8 +#define i2c_m_data7_lsb 0 +#define xd_I2C_i2c_m_data8 0xA408 +#define i2c_m_data8_pos 0 +#define i2c_m_data8_len 8 +#define i2c_m_data8_lsb 0 +#define xd_I2C_i2c_m_data9 0xA409 +#define i2c_m_data9_pos 0 +#define i2c_m_data9_len 8 +#define i2c_m_data9_lsb 0 +#define xd_I2C_i2c_m_data10 0xA40A +#define i2c_m_data10_pos 0 +#define i2c_m_data10_len 8 +#define i2c_m_data10_lsb 0 +#define xd_I2C_i2c_m_data11 0xA40B +#define i2c_m_data11_pos 0 +#define i2c_m_data11_len 8 +#define i2c_m_data11_lsb 0 +#define xd_I2C_i2c_m_cmd_rw 0xA40C +#define i2c_m_cmd_rw_pos 0 +#define i2c_m_cmd_rw_len 1 +#define i2c_m_cmd_rw_lsb 0 +#define xd_I2C_i2c_m_cmd_rwlen 0xA40C +#define i2c_m_cmd_rwlen_pos 3 +#define i2c_m_cmd_rwlen_len 4 +#define i2c_m_cmd_rwlen_lsb 0 +#define xd_I2C_i2c_m_status_cmd_exe 0xA40D +#define i2c_m_status_cmd_exe_pos 0 +#define i2c_m_status_cmd_exe_len 1 +#define i2c_m_status_cmd_exe_lsb 0 +#define xd_I2C_i2c_m_status_wdat_done 0xA40D +#define i2c_m_status_wdat_done_pos 1 +#define i2c_m_status_wdat_done_len 1 +#define i2c_m_status_wdat_done_lsb 0 +#define xd_I2C_i2c_m_status_wdat_fail 0xA40D +#define i2c_m_status_wdat_fail_pos 2 +#define i2c_m_status_wdat_fail_len 1 +#define i2c_m_status_wdat_fail_lsb 0 +#define xd_I2C_i2c_m_period 0xA40E +#define i2c_m_period_pos 0 +#define i2c_m_period_len 8 +#define i2c_m_period_lsb 0 +#define xd_I2C_i2c_m_reg_msb_lsb 0xA40F +#define i2c_m_reg_msb_lsb_pos 0 +#define i2c_m_reg_msb_lsb_len 1 +#define i2c_m_reg_msb_lsb_lsb 0 +#define xd_I2C_reg_ofdm_rst 0xA40F +#define reg_ofdm_rst_pos 1 +#define reg_ofdm_rst_len 1 +#define reg_ofdm_rst_lsb 0 +#define xd_I2C_reg_sample_period_on_tuner 0xA40F +#define reg_sample_period_on_tuner_pos 2 +#define reg_sample_period_on_tuner_len 1 +#define reg_sample_period_on_tuner_lsb 0 +#define xd_I2C_reg_rst_i2c 0xA40F +#define reg_rst_i2c_pos 3 +#define reg_rst_i2c_len 1 +#define reg_rst_i2c_lsb 0 +#define xd_I2C_reg_ofdm_rst_en 0xA40F +#define reg_ofdm_rst_en_pos 4 +#define reg_ofdm_rst_en_len 1 +#define reg_ofdm_rst_en_lsb 0 +#define xd_I2C_reg_tuner_sda_sync_on 0xA40F +#define reg_tuner_sda_sync_on_pos 5 +#define reg_tuner_sda_sync_on_len 1 +#define reg_tuner_sda_sync_on_lsb 0 +#define xd_p_mp2if_data_access_disable_ofsm 0xA500 +#define mp2if_data_access_disable_ofsm_pos 0 +#define mp2if_data_access_disable_ofsm_len 1 +#define mp2if_data_access_disable_ofsm_lsb 0 +#define xd_p_reg_mp2_sw_rst_ofsm 0xA500 +#define reg_mp2_sw_rst_ofsm_pos 1 +#define reg_mp2_sw_rst_ofsm_len 1 +#define reg_mp2_sw_rst_ofsm_lsb 0 +#define xd_p_reg_mp2if_clk_en_ofsm 0xA500 +#define reg_mp2if_clk_en_ofsm_pos 2 +#define reg_mp2if_clk_en_ofsm_len 1 +#define reg_mp2if_clk_en_ofsm_lsb 0 +#define xd_r_mp2if_sync_byte_locked 0xA500 +#define mp2if_sync_byte_locked_pos 3 +#define mp2if_sync_byte_locked_len 1 +#define mp2if_sync_byte_locked_lsb 0 +#define xd_r_mp2if_ts_not_188 0xA500 +#define mp2if_ts_not_188_pos 4 +#define mp2if_ts_not_188_len 1 +#define mp2if_ts_not_188_lsb 0 +#define xd_r_mp2if_psb_empty 0xA500 +#define mp2if_psb_empty_pos 5 +#define mp2if_psb_empty_len 1 +#define mp2if_psb_empty_lsb 0 +#define xd_r_mp2if_psb_overflow 0xA500 +#define mp2if_psb_overflow_pos 6 +#define mp2if_psb_overflow_len 1 +#define mp2if_psb_overflow_lsb 0 +#define xd_p_mp2if_keep_sf_sync_byte_ofsm 0xA500 +#define mp2if_keep_sf_sync_byte_ofsm_pos 7 +#define mp2if_keep_sf_sync_byte_ofsm_len 1 +#define mp2if_keep_sf_sync_byte_ofsm_lsb 0 +#define xd_r_mp2if_psb_mp2if_num_pkt 0xA501 +#define mp2if_psb_mp2if_num_pkt_pos 0 +#define mp2if_psb_mp2if_num_pkt_len 6 +#define mp2if_psb_mp2if_num_pkt_lsb 0 +#define xd_p_reg_mpeg_full_speed_ofsm 0xA501 +#define reg_mpeg_full_speed_ofsm_pos 6 +#define reg_mpeg_full_speed_ofsm_len 1 +#define reg_mpeg_full_speed_ofsm_lsb 0 +#define xd_p_mp2if_mpeg_ser_mode_ofsm 0xA501 +#define mp2if_mpeg_ser_mode_ofsm_pos 7 +#define mp2if_mpeg_ser_mode_ofsm_len 1 +#define mp2if_mpeg_ser_mode_ofsm_lsb 0 +#define xd_p_reg_sw_mon51 0xA600 +#define reg_sw_mon51_pos 0 +#define reg_sw_mon51_len 8 +#define reg_sw_mon51_lsb 0 +#define xd_p_reg_top_pcsel 0xA601 +#define reg_top_pcsel_pos 0 +#define reg_top_pcsel_len 1 +#define reg_top_pcsel_lsb 0 +#define xd_p_reg_top_rs232 0xA601 +#define reg_top_rs232_pos 1 +#define reg_top_rs232_len 1 +#define reg_top_rs232_lsb 0 +#define xd_p_reg_top_pcout 0xA601 +#define reg_top_pcout_pos 2 +#define reg_top_pcout_len 1 +#define reg_top_pcout_lsb 0 +#define xd_p_reg_top_debug 0xA601 +#define reg_top_debug_pos 3 +#define reg_top_debug_len 1 +#define reg_top_debug_lsb 0 +#define xd_p_reg_top_adcdly 0xA601 +#define reg_top_adcdly_pos 4 +#define reg_top_adcdly_len 2 +#define reg_top_adcdly_lsb 0 +#define xd_p_reg_top_pwrdw 0xA601 +#define reg_top_pwrdw_pos 6 +#define reg_top_pwrdw_len 1 +#define reg_top_pwrdw_lsb 0 +#define xd_p_reg_top_pwrdw_inv 0xA601 +#define reg_top_pwrdw_inv_pos 7 +#define reg_top_pwrdw_inv_len 1 +#define reg_top_pwrdw_inv_lsb 0 +#define xd_p_reg_top_int_inv 0xA602 +#define reg_top_int_inv_pos 0 +#define reg_top_int_inv_len 1 +#define reg_top_int_inv_lsb 0 +#define xd_p_reg_top_dio_sel 0xA602 +#define reg_top_dio_sel_pos 1 +#define reg_top_dio_sel_len 1 +#define reg_top_dio_sel_lsb 0 +#define xd_p_reg_top_gpioon0 0xA603 +#define reg_top_gpioon0_pos 0 +#define reg_top_gpioon0_len 1 +#define reg_top_gpioon0_lsb 0 +#define xd_p_reg_top_gpioon1 0xA603 +#define reg_top_gpioon1_pos 1 +#define reg_top_gpioon1_len 1 +#define reg_top_gpioon1_lsb 0 +#define xd_p_reg_top_gpioon2 0xA603 +#define reg_top_gpioon2_pos 2 +#define reg_top_gpioon2_len 1 +#define reg_top_gpioon2_lsb 0 +#define xd_p_reg_top_gpioon3 0xA603 +#define reg_top_gpioon3_pos 3 +#define reg_top_gpioon3_len 1 +#define reg_top_gpioon3_lsb 0 +#define xd_p_reg_top_lockon1 0xA603 +#define reg_top_lockon1_pos 4 +#define reg_top_lockon1_len 1 +#define reg_top_lockon1_lsb 0 +#define xd_p_reg_top_lockon2 0xA603 +#define reg_top_lockon2_pos 5 +#define reg_top_lockon2_len 1 +#define reg_top_lockon2_lsb 0 +#define xd_p_reg_top_gpioo0 0xA604 +#define reg_top_gpioo0_pos 0 +#define reg_top_gpioo0_len 1 +#define reg_top_gpioo0_lsb 0 +#define xd_p_reg_top_gpioo1 0xA604 +#define reg_top_gpioo1_pos 1 +#define reg_top_gpioo1_len 1 +#define reg_top_gpioo1_lsb 0 +#define xd_p_reg_top_gpioo2 0xA604 +#define reg_top_gpioo2_pos 2 +#define reg_top_gpioo2_len 1 +#define reg_top_gpioo2_lsb 0 +#define xd_p_reg_top_gpioo3 0xA604 +#define reg_top_gpioo3_pos 3 +#define reg_top_gpioo3_len 1 +#define reg_top_gpioo3_lsb 0 +#define xd_p_reg_top_lock1 0xA604 +#define reg_top_lock1_pos 4 +#define reg_top_lock1_len 1 +#define reg_top_lock1_lsb 0 +#define xd_p_reg_top_lock2 0xA604 +#define reg_top_lock2_pos 5 +#define reg_top_lock2_len 1 +#define reg_top_lock2_lsb 0 +#define xd_p_reg_top_gpioen0 0xA605 +#define reg_top_gpioen0_pos 0 +#define reg_top_gpioen0_len 1 +#define reg_top_gpioen0_lsb 0 +#define xd_p_reg_top_gpioen1 0xA605 +#define reg_top_gpioen1_pos 1 +#define reg_top_gpioen1_len 1 +#define reg_top_gpioen1_lsb 0 +#define xd_p_reg_top_gpioen2 0xA605 +#define reg_top_gpioen2_pos 2 +#define reg_top_gpioen2_len 1 +#define reg_top_gpioen2_lsb 0 +#define xd_p_reg_top_gpioen3 0xA605 +#define reg_top_gpioen3_pos 3 +#define reg_top_gpioen3_len 1 +#define reg_top_gpioen3_lsb 0 +#define xd_p_reg_top_locken1 0xA605 +#define reg_top_locken1_pos 4 +#define reg_top_locken1_len 1 +#define reg_top_locken1_lsb 0 +#define xd_p_reg_top_locken2 0xA605 +#define reg_top_locken2_pos 5 +#define reg_top_locken2_len 1 +#define reg_top_locken2_lsb 0 +#define xd_r_reg_top_gpioi0 0xA606 +#define reg_top_gpioi0_pos 0 +#define reg_top_gpioi0_len 1 +#define reg_top_gpioi0_lsb 0 +#define xd_r_reg_top_gpioi1 0xA606 +#define reg_top_gpioi1_pos 1 +#define reg_top_gpioi1_len 1 +#define reg_top_gpioi1_lsb 0 +#define xd_r_reg_top_gpioi2 0xA606 +#define reg_top_gpioi2_pos 2 +#define reg_top_gpioi2_len 1 +#define reg_top_gpioi2_lsb 0 +#define xd_r_reg_top_gpioi3 0xA606 +#define reg_top_gpioi3_pos 3 +#define reg_top_gpioi3_len 1 +#define reg_top_gpioi3_lsb 0 +#define xd_r_reg_top_locki1 0xA606 +#define reg_top_locki1_pos 4 +#define reg_top_locki1_len 1 +#define reg_top_locki1_lsb 0 +#define xd_r_reg_top_locki2 0xA606 +#define reg_top_locki2_pos 5 +#define reg_top_locki2_len 1 +#define reg_top_locki2_lsb 0 +#define xd_p_reg_dummy_7_0 0xA608 +#define reg_dummy_7_0_pos 0 +#define reg_dummy_7_0_len 8 +#define reg_dummy_7_0_lsb 0 +#define xd_p_reg_dummy_15_8 0xA609 +#define reg_dummy_15_8_pos 0 +#define reg_dummy_15_8_len 8 +#define reg_dummy_15_8_lsb 8 +#define xd_p_reg_dummy_23_16 0xA60A +#define reg_dummy_23_16_pos 0 +#define reg_dummy_23_16_len 8 +#define reg_dummy_23_16_lsb 16 +#define xd_p_reg_dummy_31_24 0xA60B +#define reg_dummy_31_24_pos 0 +#define reg_dummy_31_24_len 8 +#define reg_dummy_31_24_lsb 24 +#define xd_p_reg_dummy_39_32 0xA60C +#define reg_dummy_39_32_pos 0 +#define reg_dummy_39_32_len 8 +#define reg_dummy_39_32_lsb 32 +#define xd_p_reg_dummy_47_40 0xA60D +#define reg_dummy_47_40_pos 0 +#define reg_dummy_47_40_len 8 +#define reg_dummy_47_40_lsb 40 +#define xd_p_reg_dummy_55_48 0xA60E +#define reg_dummy_55_48_pos 0 +#define reg_dummy_55_48_len 8 +#define reg_dummy_55_48_lsb 48 +#define xd_p_reg_dummy_63_56 0xA60F +#define reg_dummy_63_56_pos 0 +#define reg_dummy_63_56_len 8 +#define reg_dummy_63_56_lsb 56 +#define xd_p_reg_dummy_71_64 0xA610 +#define reg_dummy_71_64_pos 0 +#define reg_dummy_71_64_len 8 +#define reg_dummy_71_64_lsb 64 +#define xd_p_reg_dummy_79_72 0xA611 +#define reg_dummy_79_72_pos 0 +#define reg_dummy_79_72_len 8 +#define reg_dummy_79_72_lsb 72 +#define xd_p_reg_dummy_87_80 0xA612 +#define reg_dummy_87_80_pos 0 +#define reg_dummy_87_80_len 8 +#define reg_dummy_87_80_lsb 80 +#define xd_p_reg_dummy_95_88 0xA613 +#define reg_dummy_95_88_pos 0 +#define reg_dummy_95_88_len 8 +#define reg_dummy_95_88_lsb 88 +#define xd_p_reg_dummy_103_96 0xA614 +#define reg_dummy_103_96_pos 0 +#define reg_dummy_103_96_len 8 +#define reg_dummy_103_96_lsb 96 + +#define xd_p_reg_unplug_flag 0xA615 +#define reg_unplug_flag_pos 0 +#define reg_unplug_flag_len 1 +#define reg_unplug_flag_lsb 104 + +#define xd_p_reg_api_dca_stes_request 0xA615 +#define reg_api_dca_stes_request_pos 1 +#define reg_api_dca_stes_request_len 1 +#define reg_api_dca_stes_request_lsb 0 + +#define xd_p_reg_back_to_dca_flag 0xA615 +#define reg_back_to_dca_flag_pos 2 +#define reg_back_to_dca_flag_len 1 +#define reg_back_to_dca_flag_lsb 106 + +#define xd_p_reg_api_retrain_request 0xA615 +#define reg_api_retrain_request_pos 3 +#define reg_api_retrain_request_len 1 +#define reg_api_retrain_request_lsb 0 + +#define xd_p_reg_Dyn_Top_Try_flag 0xA615 +#define reg_Dyn_Top_Try_flag_pos 3 +#define reg_Dyn_Top_Try_flag_len 1 +#define reg_Dyn_Top_Try_flag_lsb 107 + +#define xd_p_reg_API_retrain_freeze_flag 0xA615 +#define reg_API_retrain_freeze_flag_pos 4 +#define reg_API_retrain_freeze_flag_len 1 +#define reg_API_retrain_freeze_flag_lsb 108 + +#define xd_p_reg_dummy_111_104 0xA615 +#define reg_dummy_111_104_pos 0 +#define reg_dummy_111_104_len 8 +#define reg_dummy_111_104_lsb 104 +#define xd_p_reg_dummy_119_112 0xA616 +#define reg_dummy_119_112_pos 0 +#define reg_dummy_119_112_len 8 +#define reg_dummy_119_112_lsb 112 +#define xd_p_reg_dummy_127_120 0xA617 +#define reg_dummy_127_120_pos 0 +#define reg_dummy_127_120_len 8 +#define reg_dummy_127_120_lsb 120 +#define xd_p_reg_dummy_135_128 0xA618 +#define reg_dummy_135_128_pos 0 +#define reg_dummy_135_128_len 8 +#define reg_dummy_135_128_lsb 128 + +#define xd_p_reg_dummy_143_136 0xA619 +#define reg_dummy_143_136_pos 0 +#define reg_dummy_143_136_len 8 +#define reg_dummy_143_136_lsb 136 + +#define xd_p_reg_CCIR_dis 0xA619 +#define reg_CCIR_dis_pos 0 +#define reg_CCIR_dis_len 1 +#define reg_CCIR_dis_lsb 0 + +#define xd_p_reg_dummy_151_144 0xA61A +#define reg_dummy_151_144_pos 0 +#define reg_dummy_151_144_len 8 +#define reg_dummy_151_144_lsb 144 + +#define xd_p_reg_dummy_159_152 0xA61B +#define reg_dummy_159_152_pos 0 +#define reg_dummy_159_152_len 8 +#define reg_dummy_159_152_lsb 152 + +#define xd_p_reg_dummy_167_160 0xA61C +#define reg_dummy_167_160_pos 0 +#define reg_dummy_167_160_len 8 +#define reg_dummy_167_160_lsb 160 + +#define xd_p_reg_dummy_175_168 0xA61D +#define reg_dummy_175_168_pos 0 +#define reg_dummy_175_168_len 8 +#define reg_dummy_175_168_lsb 168 + +#define xd_p_reg_dummy_183_176 0xA61E +#define reg_dummy_183_176_pos 0 +#define reg_dummy_183_176_len 8 +#define reg_dummy_183_176_lsb 176 + +#define xd_p_reg_ofsm_read_rbc_en 0xA61E +#define reg_ofsm_read_rbc_en_pos 2 +#define reg_ofsm_read_rbc_en_len 1 +#define reg_ofsm_read_rbc_en_lsb 0 + +#define xd_p_reg_ce_filter_selection_dis 0xA61E +#define reg_ce_filter_selection_dis_pos 1 +#define reg_ce_filter_selection_dis_len 1 +#define reg_ce_filter_selection_dis_lsb 0 + +#define xd_p_reg_OFSM_version_control_7_0 0xA611 +#define reg_OFSM_version_control_7_0_pos 0 +#define reg_OFSM_version_control_7_0_len 8 +#define reg_OFSM_version_control_7_0_lsb 0 + +#define xd_p_reg_OFSM_version_control_15_8 0xA61F +#define reg_OFSM_version_control_15_8_pos 0 +#define reg_OFSM_version_control_15_8_len 8 +#define reg_OFSM_version_control_15_8_lsb 0 + +#define xd_p_reg_OFSM_version_control_23_16 0xA620 +#define reg_OFSM_version_control_23_16_pos 0 +#define reg_OFSM_version_control_23_16_len 8 +#define reg_OFSM_version_control_23_16_lsb 0 + +#define xd_p_reg_dummy_191_184 0xA61F +#define reg_dummy_191_184_pos 0 +#define reg_dummy_191_184_len 8 +#define reg_dummy_191_184_lsb 184 + +#define xd_p_reg_dummy_199_192 0xA620 +#define reg_dummy_199_192_pos 0 +#define reg_dummy_199_192_len 8 +#define reg_dummy_199_192_lsb 192 + +#define xd_p_reg_ce_en 0xABC0 +#define reg_ce_en_pos 0 +#define reg_ce_en_len 1 +#define reg_ce_en_lsb 0 +#define xd_p_reg_ce_fctrl_en 0xABC0 +#define reg_ce_fctrl_en_pos 1 +#define reg_ce_fctrl_en_len 1 +#define reg_ce_fctrl_en_lsb 0 +#define xd_p_reg_ce_fste_tdi 0xABC0 +#define reg_ce_fste_tdi_pos 2 +#define reg_ce_fste_tdi_len 1 +#define reg_ce_fste_tdi_lsb 0 +#define xd_p_reg_ce_dynamic 0xABC0 +#define reg_ce_dynamic_pos 3 +#define reg_ce_dynamic_len 1 +#define reg_ce_dynamic_lsb 0 +#define xd_p_reg_ce_conf 0xABC0 +#define reg_ce_conf_pos 4 +#define reg_ce_conf_len 2 +#define reg_ce_conf_lsb 0 +#define xd_p_reg_ce_dyn12 0xABC0 +#define reg_ce_dyn12_pos 6 +#define reg_ce_dyn12_len 1 +#define reg_ce_dyn12_lsb 0 +#define xd_p_reg_ce_derot_en 0xABC0 +#define reg_ce_derot_en_pos 7 +#define reg_ce_derot_en_len 1 +#define reg_ce_derot_en_lsb 0 +#define xd_p_reg_ce_dynamic_th_7_0 0xABC1 +#define reg_ce_dynamic_th_7_0_pos 0 +#define reg_ce_dynamic_th_7_0_len 8 +#define reg_ce_dynamic_th_7_0_lsb 0 +#define xd_p_reg_ce_dynamic_th_15_8 0xABC2 +#define reg_ce_dynamic_th_15_8_pos 0 +#define reg_ce_dynamic_th_15_8_len 8 +#define reg_ce_dynamic_th_15_8_lsb 8 +#define xd_p_reg_ce_s1 0xABC3 +#define reg_ce_s1_pos 0 +#define reg_ce_s1_len 5 +#define reg_ce_s1_lsb 0 +#define xd_p_reg_ce_var_forced_value 0xABC3 +#define reg_ce_var_forced_value_pos 5 +#define reg_ce_var_forced_value_len 3 +#define reg_ce_var_forced_value_lsb 0 +#define xd_p_reg_ce_data_im_7_0 0xABC4 +#define reg_ce_data_im_7_0_pos 0 +#define reg_ce_data_im_7_0_len 8 +#define reg_ce_data_im_7_0_lsb 0 +#define xd_p_reg_ce_data_im_8 0xABC5 +#define reg_ce_data_im_8_pos 0 +#define reg_ce_data_im_8_len 1 +#define reg_ce_data_im_8_lsb 0 +#define xd_p_reg_ce_data_re_6_0 0xABC5 +#define reg_ce_data_re_6_0_pos 1 +#define reg_ce_data_re_6_0_len 7 +#define reg_ce_data_re_6_0_lsb 0 +#define xd_p_reg_ce_data_re_8_7 0xABC6 +#define reg_ce_data_re_8_7_pos 0 +#define reg_ce_data_re_8_7_len 2 +#define reg_ce_data_re_8_7_lsb 7 +#define xd_p_reg_ce_tone_5_0 0xABC6 +#define reg_ce_tone_5_0_pos 2 +#define reg_ce_tone_5_0_len 6 +#define reg_ce_tone_5_0_lsb 0 +#define xd_p_reg_ce_tone_12_6 0xABC7 +#define reg_ce_tone_12_6_pos 0 +#define reg_ce_tone_12_6_len 7 +#define reg_ce_tone_12_6_lsb 6 +#define xd_p_reg_ce_centroid_drift_th 0xABC8 +#define reg_ce_centroid_drift_th_pos 0 +#define reg_ce_centroid_drift_th_len 8 +#define reg_ce_centroid_drift_th_lsb 0 +#define xd_p_reg_ce_centroid_count_max 0xABC9 +#define reg_ce_centroid_count_max_pos 0 +#define reg_ce_centroid_count_max_len 4 +#define reg_ce_centroid_count_max_lsb 0 +#define xd_p_reg_ce_centroid_bias_inc_7_0 0xABCA +#define reg_ce_centroid_bias_inc_7_0_pos 0 +#define reg_ce_centroid_bias_inc_7_0_len 8 +#define reg_ce_centroid_bias_inc_7_0_lsb 0 +#define xd_p_reg_ce_centroid_bias_inc_8 0xABCB +#define reg_ce_centroid_bias_inc_8_pos 0 +#define reg_ce_centroid_bias_inc_8_len 1 +#define reg_ce_centroid_bias_inc_8_lsb 0 +#define xd_p_reg_ce_var_th0_7_0 0xABCC +#define reg_ce_var_th0_7_0_pos 0 +#define reg_ce_var_th0_7_0_len 8 +#define reg_ce_var_th0_7_0_lsb 0 +#define xd_p_reg_ce_var_th0_15_8 0xABCD +#define reg_ce_var_th0_15_8_pos 0 +#define reg_ce_var_th0_15_8_len 8 +#define reg_ce_var_th0_15_8_lsb 8 +#define xd_p_reg_ce_var_th1_7_0 0xABCE +#define reg_ce_var_th1_7_0_pos 0 +#define reg_ce_var_th1_7_0_len 8 +#define reg_ce_var_th1_7_0_lsb 0 +#define xd_p_reg_ce_var_th1_15_8 0xABCF +#define reg_ce_var_th1_15_8_pos 0 +#define reg_ce_var_th1_15_8_len 8 +#define reg_ce_var_th1_15_8_lsb 8 +#define xd_p_reg_ce_var_th2_7_0 0xABD0 +#define reg_ce_var_th2_7_0_pos 0 +#define reg_ce_var_th2_7_0_len 8 +#define reg_ce_var_th2_7_0_lsb 0 +#define xd_p_reg_ce_var_th2_15_8 0xABD1 +#define reg_ce_var_th2_15_8_pos 0 +#define reg_ce_var_th2_15_8_len 8 +#define reg_ce_var_th2_15_8_lsb 8 +#define xd_p_reg_ce_var_th3_7_0 0xABD2 +#define reg_ce_var_th3_7_0_pos 0 +#define reg_ce_var_th3_7_0_len 8 +#define reg_ce_var_th3_7_0_lsb 0 +#define xd_p_reg_ce_var_th3_15_8 0xABD3 +#define reg_ce_var_th3_15_8_pos 0 +#define reg_ce_var_th3_15_8_len 8 +#define reg_ce_var_th3_15_8_lsb 8 +#define xd_p_reg_ce_var_th4_7_0 0xABD4 +#define reg_ce_var_th4_7_0_pos 0 +#define reg_ce_var_th4_7_0_len 8 +#define reg_ce_var_th4_7_0_lsb 0 +#define xd_p_reg_ce_var_th4_15_8 0xABD5 +#define reg_ce_var_th4_15_8_pos 0 +#define reg_ce_var_th4_15_8_len 8 +#define reg_ce_var_th4_15_8_lsb 8 +#define xd_p_reg_ce_var_th5_7_0 0xABD6 +#define reg_ce_var_th5_7_0_pos 0 +#define reg_ce_var_th5_7_0_len 8 +#define reg_ce_var_th5_7_0_lsb 0 +#define xd_p_reg_ce_var_th5_15_8 0xABD7 +#define reg_ce_var_th5_15_8_pos 0 +#define reg_ce_var_th5_15_8_len 8 +#define reg_ce_var_th5_15_8_lsb 8 +#define xd_p_reg_ce_var_th6_7_0 0xABD8 +#define reg_ce_var_th6_7_0_pos 0 +#define reg_ce_var_th6_7_0_len 8 +#define reg_ce_var_th6_7_0_lsb 0 +#define xd_p_reg_ce_var_th6_15_8 0xABD9 +#define reg_ce_var_th6_15_8_pos 0 +#define reg_ce_var_th6_15_8_len 8 +#define reg_ce_var_th6_15_8_lsb 8 +#define xd_p_reg_ce_fctrl_reset 0xABDA +#define reg_ce_fctrl_reset_pos 0 +#define reg_ce_fctrl_reset_len 1 +#define reg_ce_fctrl_reset_lsb 0 +#define xd_p_reg_ce_cent_auto_clr_en 0xABDA +#define reg_ce_cent_auto_clr_en_pos 1 +#define reg_ce_cent_auto_clr_en_len 1 +#define reg_ce_cent_auto_clr_en_lsb 0 +#define xd_p_reg_ce_fctrl_auto_reset_en 0xABDA +#define reg_ce_fctrl_auto_reset_en_pos 2 +#define reg_ce_fctrl_auto_reset_en_len 1 +#define reg_ce_fctrl_auto_reset_en_lsb 0 +#define xd_p_reg_ce_var_forced_en 0xABDA +#define reg_ce_var_forced_en_pos 3 +#define reg_ce_var_forced_en_len 1 +#define reg_ce_var_forced_en_lsb 0 +#define xd_p_reg_ce_cent_forced_en 0xABDA +#define reg_ce_cent_forced_en_pos 4 +#define reg_ce_cent_forced_en_len 1 +#define reg_ce_cent_forced_en_lsb 0 +#define xd_p_reg_ce_var_max 0xABDA +#define reg_ce_var_max_pos 5 +#define reg_ce_var_max_len 3 +#define reg_ce_var_max_lsb 0 +#define xd_p_reg_ce_cent_forced_value_7_0 0xABDB +#define reg_ce_cent_forced_value_7_0_pos 0 +#define reg_ce_cent_forced_value_7_0_len 8 +#define reg_ce_cent_forced_value_7_0_lsb 0 +#define xd_p_reg_ce_cent_forced_value_11_8 0xABDC +#define reg_ce_cent_forced_value_11_8_pos 0 +#define reg_ce_cent_forced_value_11_8_len 4 +#define reg_ce_cent_forced_value_11_8_lsb 8 +#define xd_p_reg_ce_fctrl_rd 0xABDD +#define reg_ce_fctrl_rd_pos 0 +#define reg_ce_fctrl_rd_len 1 +#define reg_ce_fctrl_rd_lsb 0 +#define xd_p_reg_ce_centroid_max_6_0 0xABDD +#define reg_ce_centroid_max_6_0_pos 1 +#define reg_ce_centroid_max_6_0_len 7 +#define reg_ce_centroid_max_6_0_lsb 0 +#define xd_p_reg_ce_centroid_max_11_7 0xABDE +#define reg_ce_centroid_max_11_7_pos 0 +#define reg_ce_centroid_max_11_7_len 5 +#define reg_ce_centroid_max_11_7_lsb 7 +#define xd_p_reg_ce_var 0xABDF +#define reg_ce_var_pos 0 +#define reg_ce_var_len 3 +#define reg_ce_var_lsb 0 +#define xd_p_reg_ce_fctrl_rdy 0xABDF +#define reg_ce_fctrl_rdy_pos 3 +#define reg_ce_fctrl_rdy_len 1 +#define reg_ce_fctrl_rdy_lsb 0 +#define xd_p_reg_ce_centroid_out_3_0 0xABDF +#define reg_ce_centroid_out_3_0_pos 4 +#define reg_ce_centroid_out_3_0_len 4 +#define reg_ce_centroid_out_3_0_lsb 0 +#define xd_p_reg_ce_centroid_out_11_4 0xABE0 +#define reg_ce_centroid_out_11_4_pos 0 +#define reg_ce_centroid_out_11_4_len 8 +#define reg_ce_centroid_out_11_4_lsb 4 +#define xd_p_reg_ce_bias_7_0 0xABE1 +#define reg_ce_bias_7_0_pos 0 +#define reg_ce_bias_7_0_len 8 +#define reg_ce_bias_7_0_lsb 0 +#define xd_p_reg_ce_bias_11_8 0xABE2 +#define reg_ce_bias_11_8_pos 0 +#define reg_ce_bias_11_8_len 4 +#define reg_ce_bias_11_8_lsb 8 +#define xd_p_reg_ce_m1_3_0 0xABE2 +#define reg_ce_m1_3_0_pos 4 +#define reg_ce_m1_3_0_len 4 +#define reg_ce_m1_3_0_lsb 0 +#define xd_p_reg_ce_m1_11_4 0xABE3 +#define reg_ce_m1_11_4_pos 0 +#define reg_ce_m1_11_4_len 8 +#define reg_ce_m1_11_4_lsb 4 +#define xd_p_reg_ce_rh0_7_0 0xABE4 +#define reg_ce_rh0_7_0_pos 0 +#define reg_ce_rh0_7_0_len 8 +#define reg_ce_rh0_7_0_lsb 0 +#define xd_p_reg_ce_rh0_15_8 0xABE5 +#define reg_ce_rh0_15_8_pos 0 +#define reg_ce_rh0_15_8_len 8 +#define reg_ce_rh0_15_8_lsb 8 +#define xd_p_reg_ce_rh0_23_16 0xABE6 +#define reg_ce_rh0_23_16_pos 0 +#define reg_ce_rh0_23_16_len 8 +#define reg_ce_rh0_23_16_lsb 16 +#define xd_p_reg_ce_rh0_31_24 0xABE7 +#define reg_ce_rh0_31_24_pos 0 +#define reg_ce_rh0_31_24_len 8 +#define reg_ce_rh0_31_24_lsb 24 +#define xd_p_reg_ce_rh3_real_7_0 0xABE8 +#define reg_ce_rh3_real_7_0_pos 0 +#define reg_ce_rh3_real_7_0_len 8 +#define reg_ce_rh3_real_7_0_lsb 0 +#define xd_p_reg_ce_rh3_real_15_8 0xABE9 +#define reg_ce_rh3_real_15_8_pos 0 +#define reg_ce_rh3_real_15_8_len 8 +#define reg_ce_rh3_real_15_8_lsb 8 +#define xd_p_reg_ce_rh3_real_23_16 0xABEA +#define reg_ce_rh3_real_23_16_pos 0 +#define reg_ce_rh3_real_23_16_len 8 +#define reg_ce_rh3_real_23_16_lsb 16 +#define xd_p_reg_ce_rh3_real_31_24 0xABEB +#define reg_ce_rh3_real_31_24_pos 0 +#define reg_ce_rh3_real_31_24_len 8 +#define reg_ce_rh3_real_31_24_lsb 24 +#define xd_p_reg_ce_rh3_imag_7_0 0xABEC +#define reg_ce_rh3_imag_7_0_pos 0 +#define reg_ce_rh3_imag_7_0_len 8 +#define reg_ce_rh3_imag_7_0_lsb 0 +#define xd_p_reg_ce_rh3_imag_15_8 0xABED +#define reg_ce_rh3_imag_15_8_pos 0 +#define reg_ce_rh3_imag_15_8_len 8 +#define reg_ce_rh3_imag_15_8_lsb 8 +#define xd_p_reg_ce_rh3_imag_23_16 0xABEE +#define reg_ce_rh3_imag_23_16_pos 0 +#define reg_ce_rh3_imag_23_16_len 8 +#define reg_ce_rh3_imag_23_16_lsb 16 +#define xd_p_reg_ce_rh3_imag_31_24 0xABEF +#define reg_ce_rh3_imag_31_24_pos 0 +#define reg_ce_rh3_imag_31_24_len 8 +#define reg_ce_rh3_imag_31_24_lsb 24 +#define xd_p_reg_feq_fix_eh2_7_0 0xABF0 +#define reg_feq_fix_eh2_7_0_pos 0 +#define reg_feq_fix_eh2_7_0_len 8 +#define reg_feq_fix_eh2_7_0_lsb 0 +#define xd_p_reg_feq_fix_eh2_15_8 0xABF1 +#define reg_feq_fix_eh2_15_8_pos 0 +#define reg_feq_fix_eh2_15_8_len 8 +#define reg_feq_fix_eh2_15_8_lsb 8 +#define xd_p_reg_feq_fix_eh2_23_16 0xABF2 +#define reg_feq_fix_eh2_23_16_pos 0 +#define reg_feq_fix_eh2_23_16_len 8 +#define reg_feq_fix_eh2_23_16_lsb 16 +#define xd_p_reg_feq_fix_eh2_31_24 0xABF3 +#define reg_feq_fix_eh2_31_24_pos 0 +#define reg_feq_fix_eh2_31_24_len 8 +#define reg_feq_fix_eh2_31_24_lsb 24 +#define xd_p_reg_ce_m2_central_7_0 0xABF4 +#define reg_ce_m2_central_7_0_pos 0 +#define reg_ce_m2_central_7_0_len 8 +#define reg_ce_m2_central_7_0_lsb 0 +#define xd_p_reg_ce_m2_central_15_8 0xABF5 +#define reg_ce_m2_central_15_8_pos 0 +#define reg_ce_m2_central_15_8_len 8 +#define reg_ce_m2_central_15_8_lsb 8 +#define xd_p_reg_ce_fftshift 0xABF6 +#define reg_ce_fftshift_pos 0 +#define reg_ce_fftshift_len 4 +#define reg_ce_fftshift_lsb 0 +#define xd_p_reg_ce_fftshift1 0xABF6 +#define reg_ce_fftshift1_pos 4 +#define reg_ce_fftshift1_len 4 +#define reg_ce_fftshift1_lsb 0 +#define xd_p_reg_ce_fftshift2 0xABF7 +#define reg_ce_fftshift2_pos 0 +#define reg_ce_fftshift2_len 4 +#define reg_ce_fftshift2_lsb 0 +#define xd_p_reg_ce_top_mobile 0xABF7 +#define reg_ce_top_mobile_pos 4 +#define reg_ce_top_mobile_len 1 +#define reg_ce_top_mobile_lsb 0 +#define xd_p_reg_strong_sginal_detected 0xA2BC +#define reg_strong_sginal_detected_pos 2 +#define reg_strong_sginal_detected_len 1 +#define reg_strong_sginal_detected_lsb 0 + +#define XD_MP2IF_BASE 0xB000 +#define XD_MP2IF_CSR (0x00 + XD_MP2IF_BASE) +#define XD_MP2IF_DMX_CTRL (0x03 + XD_MP2IF_BASE) +#define XD_MP2IF_PID_IDX (0x04 + XD_MP2IF_BASE) +#define XD_MP2IF_PID_DATA_L (0x05 + XD_MP2IF_BASE) +#define XD_MP2IF_PID_DATA_H (0x06 + XD_MP2IF_BASE) +#define XD_MP2IF_MISC (0x07 + XD_MP2IF_BASE) + +extern struct dvb_frontend *af9005_fe_attach(struct dvb_usb_device *d); +extern int af9005_read_ofdm_register(struct dvb_usb_device *d, u16 reg, + u8 * value); +extern int af9005_read_ofdm_registers(struct dvb_usb_device *d, u16 reg, + u8 * values, int len); +extern int af9005_write_ofdm_register(struct dvb_usb_device *d, u16 reg, + u8 value); +extern int af9005_write_ofdm_registers(struct dvb_usb_device *d, u16 reg, + u8 * values, int len); +extern int af9005_read_tuner_registers(struct dvb_usb_device *d, u16 reg, + u8 addr, u8 * values, int len); +extern int af9005_write_tuner_registers(struct dvb_usb_device *d, u16 reg, + u8 * values, int len); +extern int af9005_read_register_bits(struct dvb_usb_device *d, u16 reg, + u8 pos, u8 len, u8 * value); +extern int af9005_write_register_bits(struct dvb_usb_device *d, u16 reg, + u8 pos, u8 len, u8 value); +extern int af9005_send_command(struct dvb_usb_device *d, u8 command, + u8 * wbuf, int wlen, u8 * rbuf, int rlen); +extern int af9005_read_eeprom(struct dvb_usb_device *d, u8 address, + u8 * values, int len); +extern int af9005_tuner_attach(struct dvb_usb_adapter *adap); +extern int af9005_led_control(struct dvb_usb_device *d, int onoff); + +extern u8 regmask[8]; + +/* remote control decoder */ +extern int af9005_rc_decode(struct dvb_usb_device *d, u8 * data, int len, + u32 * event, int *state); +extern struct dvb_usb_rc_key af9005_rc_keys[]; +extern int af9005_rc_keys_size; + +#endif diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index bac2ae3..04e31cf 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/drivers/media/dvb/dvb-usb/cxusb.c @@ -354,41 +354,35 @@ static struct mt352_config cxusb_mt352_config = { /* Callbacks for DVB USB */ static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap) { - u8 bpll[4] = { 0x0b, 0xdc, 0x9c, 0xa0 }; - adap->pll_addr = 0x61; - memcpy(adap->pll_init, bpll, 4); - adap->pll_desc = &dvb_pll_fmd1216me; - - adap->fe->ops.tuner_ops.init = dvb_usb_tuner_init_i2c; - adap->fe->ops.tuner_ops.set_params = dvb_usb_tuner_set_params_i2c; - + dvb_attach(dvb_pll_attach, adap->fe, 0x61, &adap->dev->i2c_adap, + DVB_PLL_FMD1216ME); return 0; } static int cxusb_dee1601_tuner_attach(struct dvb_usb_adapter *adap) { dvb_attach(dvb_pll_attach, adap->fe, 0x61, - NULL, &dvb_pll_thomson_dtt7579); + NULL, DVB_PLL_THOMSON_DTT7579); return 0; } static int cxusb_lgz201_tuner_attach(struct dvb_usb_adapter *adap) { - dvb_attach(dvb_pll_attach, adap->fe, 0x61, NULL, &dvb_pll_lg_z201); + dvb_attach(dvb_pll_attach, adap->fe, 0x61, NULL, DVB_PLL_LG_Z201); return 0; } static int cxusb_dtt7579_tuner_attach(struct dvb_usb_adapter *adap) { dvb_attach(dvb_pll_attach, adap->fe, 0x60, - NULL, &dvb_pll_thomson_dtt7579); + NULL, DVB_PLL_THOMSON_DTT7579); return 0; } static int cxusb_lgh064f_tuner_attach(struct dvb_usb_adapter *adap) { dvb_attach(dvb_pll_attach, adap->fe, 0x61, &adap->dev->i2c_adap, - &dvb_pll_lg_tdvs_h06xf); + DVB_PLL_LG_TDVS_H06XF); return 0; } diff --git a/drivers/media/dvb/dvb-usb/dibusb-common.c b/drivers/media/dvb/dvb-usb/dibusb-common.c index 5143e42..9a184da 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-common.c +++ b/drivers/media/dvb/dvb-usb/dibusb-common.c @@ -295,7 +295,7 @@ int dibusb_dib3000mc_tuner_attach(struct dvb_usb_adapter *adap) tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe, 1); if (dvb_attach(mt2060_attach, adap->fe, tun_i2c, &stk3000p_mt2060_config, if1) == NULL) { /* not found - use panasonic pll parameters */ - if (dvb_attach(dvb_pll_attach, adap->fe, 0x60, tun_i2c, &dvb_pll_env57h1xd5) == NULL) + if (dvb_attach(dvb_pll_attach, adap->fe, 0x60, tun_i2c, DVB_PLL_ENV57H1XD5) == NULL) return -ENOMEM; } else { st->mt2060_present = 1; diff --git a/drivers/media/dvb/dvb-usb/dibusb-mb.c b/drivers/media/dvb/dvb-usb/dibusb-mb.c index 7a6ae8f..043cada 100644 --- a/drivers/media/dvb/dvb-usb/dibusb-mb.c +++ b/drivers/media/dvb/dvb-usb/dibusb-mb.c @@ -14,6 +14,14 @@ */ #include "dibusb.h" +static int dib3000mb_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct dvb_usb_adapter *adap = fe->dvb->priv; + struct dibusb_state *st = adap->priv; + + return st->ops.tuner_pass_ctrl(fe, enable, st->tuner_addr); +} + static int dibusb_dib3000mb_frontend_attach(struct dvb_usb_adapter *adap) { struct dib3000_config demod_cfg; @@ -21,21 +29,34 @@ static int dibusb_dib3000mb_frontend_attach(struct dvb_usb_adapter *adap) demod_cfg.demod_address = 0x8; - if ((adap->fe = dib3000mb_attach(&demod_cfg,&adap->dev->i2c_adap,&st->ops)) == NULL) + if ((adap->fe = dvb_attach(dib3000mb_attach, &demod_cfg, + &adap->dev->i2c_adap, &st->ops)) == NULL) return -ENODEV; - adap->fe->ops.tuner_ops.init = dvb_usb_tuner_init_i2c; - adap->fe->ops.tuner_ops.set_params = dvb_usb_tuner_set_params_i2c; - - adap->tuner_pass_ctrl = st->ops.tuner_pass_ctrl; + adap->fe->ops.i2c_gate_ctrl = dib3000mb_i2c_gate_ctrl; return 0; } static int dibusb_thomson_tuner_attach(struct dvb_usb_adapter *adap) { - adap->pll_addr = 0x61; - adap->pll_desc = &dvb_pll_tua6010xs; + struct dibusb_state *st = adap->priv; + + st->tuner_addr = 0x61; + + dvb_attach(dvb_pll_attach, adap->fe, 0x61, &adap->dev->i2c_adap, + DVB_PLL_TUA6010XS); + return 0; +} + +static int dibusb_panasonic_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dibusb_state *st = adap->priv; + + st->tuner_addr = 0x60; + + dvb_attach(dvb_pll_attach, adap->fe, 0x60, &adap->dev->i2c_adap, + DVB_PLL_TDA665X); return 0; } @@ -50,30 +71,28 @@ static int dibusb_tuner_probe_and_attach(struct dvb_usb_adapter *adap) { .flags = 0, .buf = b, .len = 2 }, { .flags = I2C_M_RD, .buf = b2, .len = 1 }, }; + struct dibusb_state *st = adap->priv; /* the Panasonic sits on I2C addrass 0x60, the Thomson on 0x61 */ - msg[0].addr = msg[1].addr = 0x60; + msg[0].addr = msg[1].addr = st->tuner_addr = 0x60; - if (adap->tuner_pass_ctrl) - adap->tuner_pass_ctrl(adap->fe,1,msg[0].addr); + if (adap->fe->ops.i2c_gate_ctrl) + adap->fe->ops.i2c_gate_ctrl(adap->fe,1); if (i2c_transfer(&adap->dev->i2c_adap, msg, 2) != 2) { err("tuner i2c write failed."); ret = -EREMOTEIO; } - if (adap->tuner_pass_ctrl) - adap->tuner_pass_ctrl(adap->fe,0,msg[0].addr); + if (adap->fe->ops.i2c_gate_ctrl) + adap->fe->ops.i2c_gate_ctrl(adap->fe,0); if (b2[0] == 0xfe) { info("This device has the Thomson Cable onboard. Which is default."); - dibusb_thomson_tuner_attach(adap); + ret = dibusb_thomson_tuner_attach(adap); } else { - u8 bpll[4] = { 0x0b, 0xf5, 0x85, 0xab }; info("This device has the Panasonic ENV77H11D5 onboard."); - adap->pll_addr = 0x60; - memcpy(adap->pll_init,bpll,4); - adap->pll_desc = &dvb_pll_tda665x; + ret = dibusb_panasonic_tuner_attach(adap); } return ret; diff --git a/drivers/media/dvb/dvb-usb/dibusb.h b/drivers/media/dvb/dvb-usb/dibusb.h index b607810..8e847aa 100644 --- a/drivers/media/dvb/dvb-usb/dibusb.h +++ b/drivers/media/dvb/dvb-usb/dibusb.h @@ -99,6 +99,7 @@ struct dibusb_state { struct dib_fe_xfer_ops ops; int mt2060_present; + u8 tuner_addr; }; struct dibusb_device_state { diff --git a/drivers/media/dvb/dvb-usb/digitv.c b/drivers/media/dvb/dvb-usb/digitv.c index b5acb11..bca1e09 100644 --- a/drivers/media/dvb/dvb-usb/digitv.c +++ b/drivers/media/dvb/dvb-usb/digitv.c @@ -118,7 +118,8 @@ static int digitv_nxt6000_tuner_set_params(struct dvb_frontend *fe, struct dvb_f { struct dvb_usb_adapter *adap = fe->dvb->priv; u8 b[5]; - dvb_usb_tuner_calc_regs(fe,fep,b, 5); + + fe->ops.tuner_ops.calc_regs(fe, fep, b, sizeof(b)); if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); return digitv_ctrl_msg(adap->dev, USB_WRITE_TUNER, 0, &b[1], 4, NULL, 0); @@ -130,12 +131,14 @@ static struct nxt6000_config digitv_nxt6000_config = { static int digitv_frontend_attach(struct dvb_usb_adapter *adap) { + struct digitv_state *st = adap->dev->priv; + if ((adap->fe = dvb_attach(mt352_attach, &digitv_mt352_config, &adap->dev->i2c_adap)) != NULL) { - adap->fe->ops.tuner_ops.calc_regs = dvb_usb_tuner_calc_regs; + st->is_nxt6000 = 0; return 0; } if ((adap->fe = dvb_attach(nxt6000_attach, &digitv_nxt6000_config, &adap->dev->i2c_adap)) != NULL) { - adap->fe->ops.tuner_ops.set_params = digitv_nxt6000_tuner_set_params; + st->is_nxt6000 = 1; return 0; } return -EIO; @@ -143,8 +146,14 @@ static int digitv_frontend_attach(struct dvb_usb_adapter *adap) static int digitv_tuner_attach(struct dvb_usb_adapter *adap) { - adap->pll_addr = 0x60; - adap->pll_desc = &dvb_pll_tded4; + struct digitv_state *st = adap->dev->priv; + + if (!dvb_attach(dvb_pll_attach, adap->fe, 0x60, NULL, DVB_PLL_TDED4)) + return -ENODEV; + + if (st->is_nxt6000) + adap->fe->ops.tuner_ops.set_params = digitv_nxt6000_tuner_set_params; + return 0; } @@ -273,6 +282,8 @@ static struct dvb_usb_device_properties digitv_properties = { .usb_ctrl = CYPRESS_FX2, .firmware = "dvb-usb-digitv-02.fw", + .size_of_priv = sizeof(struct digitv_state), + .num_adapters = 1, .adapter = { { diff --git a/drivers/media/dvb/dvb-usb/digitv.h b/drivers/media/dvb/dvb-usb/digitv.h index 477ee42..8b43e3d 100644 --- a/drivers/media/dvb/dvb-usb/digitv.h +++ b/drivers/media/dvb/dvb-usb/digitv.h @@ -4,6 +4,10 @@ #define DVB_USB_LOG_PREFIX "digitv" #include "dvb-usb.h" +struct digitv_state { + int is_nxt6000; +}; + extern int dvb_usb_digitv_debug; #define deb_rc(args...) dprintk(dvb_usb_digitv_debug,0x01,args) diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c b/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c index 088b6de..23428cd 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-i2c.c @@ -46,82 +46,3 @@ int dvb_usb_i2c_exit(struct dvb_usb_device *d) d->state &= ~DVB_USB_STATE_I2C; return 0; } - -int dvb_usb_tuner_init_i2c(struct dvb_frontend *fe) -{ - struct dvb_usb_adapter *adap = fe->dvb->priv; - struct i2c_msg msg = { .addr = adap->pll_addr, .flags = 0, .buf = adap->pll_init, .len = 4 }; - int ret = 0; - - /* if pll_desc is not used */ - if (adap->pll_desc == NULL) - return 0; - - if (adap->tuner_pass_ctrl) - adap->tuner_pass_ctrl(fe, 1, adap->pll_addr); - - deb_pll("pll init: %x\n",adap->pll_addr); - deb_pll("pll-buf: %x %x %x %x\n",adap->pll_init[0], adap->pll_init[1], - adap->pll_init[2], adap->pll_init[3]); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer (&adap->dev->i2c_adap, &msg, 1) != 1) { - err("tuner i2c write failed for pll_init."); - ret = -EREMOTEIO; - } - msleep(1); - - if (adap->tuner_pass_ctrl) - adap->tuner_pass_ctrl(fe,0,adap->pll_addr); - return ret; -} -EXPORT_SYMBOL(dvb_usb_tuner_init_i2c); - -int dvb_usb_tuner_calc_regs(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep, u8 *b, int buf_len) -{ - struct dvb_usb_adapter *adap = fe->dvb->priv; - - if (buf_len != 5) - return -EINVAL; - if (adap->pll_desc == NULL) - return 0; - - deb_pll("pll addr: %x, freq: %d %p\n",adap->pll_addr, fep->frequency, adap->pll_desc); - - b[0] = adap->pll_addr; - dvb_pll_configure(adap->pll_desc, &b[1], fep->frequency, fep->u.ofdm.bandwidth); - - deb_pll("pll-buf: %x %x %x %x %x\n",b[0],b[1],b[2],b[3],b[4]); - - return 5; -} -EXPORT_SYMBOL(dvb_usb_tuner_calc_regs); - -int dvb_usb_tuner_set_params_i2c(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) -{ - struct dvb_usb_adapter *adap = fe->dvb->priv; - int ret = 0; - u8 b[5]; - struct i2c_msg msg = { .addr = adap->pll_addr, .flags = 0, .buf = &b[1], .len = 4 }; - - dvb_usb_tuner_calc_regs(fe,fep,b,5); - - if (adap->tuner_pass_ctrl) - adap->tuner_pass_ctrl(fe, 1, adap->pll_addr); - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - - if (i2c_transfer(&adap->dev->i2c_adap, &msg, 1) != 1) { - err("tuner i2c write failed for pll_set."); - ret = -EREMOTEIO; - } - msleep(1); - - if (adap->tuner_pass_ctrl) - adap->tuner_pass_ctrl(fe, 0, adap->pll_addr); - - return ret; -} -EXPORT_SYMBOL(dvb_usb_tuner_set_params_i2c); diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 4030816..4dfab02 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -11,7 +11,9 @@ /* Vendor IDs */ #define USB_VID_ADSTECH 0x06e1 +#define USB_VID_AFATECH 0x15a4 #define USB_VID_ALCOR_MICRO 0x058f +#define USB_VID_ALINK 0x05e3 #define USB_VID_ANCHOR 0x0547 #define USB_VID_ANUBIS_ELECTRONIC 0x10fd #define USB_VID_AVERMEDIA 0x07ca @@ -35,6 +37,7 @@ #define USB_VID_MSI 0x0db0 #define USB_VID_OPERA1 0x695c #define USB_VID_PINNACLE 0x2304 +#define USB_VID_TERRATEC 0x0ccd #define USB_VID_VISIONPLUS 0x13d3 #define USB_VID_TWINHAN 0x1822 #define USB_VID_ULTIMA_ELECTRONIC 0x05d8 @@ -44,6 +47,8 @@ /* Product IDs */ #define USB_PID_ADSTECH_USB2_COLD 0xa333 #define USB_PID_ADSTECH_USB2_WARM 0xa334 +#define USB_PID_AFATECH_AF9005 0x9020 +#define USB_VID_ALINK_DTU 0xf170 #define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001 #define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002 #define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800 @@ -69,6 +74,7 @@ #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_TERRATEC_CINERGY_T_USB_XE 0x0055 #define USB_PID_TWINHAN_VP7041_COLD 0x3201 #define USB_PID_TWINHAN_VP7041_WARM 0x3202 #define USB_PID_TWINHAN_VP7020_COLD 0x3203 diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c index 9200a30..7b9f35b 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c @@ -110,7 +110,7 @@ int dvb_usb_remote_init(struct dvb_usb_device *d) input_dev->name = "IR-receiver inside an USB DVB receiver"; input_dev->phys = d->rc_phys; usb_to_input_id(d->udev, &input_dev->id); - input_dev->cdev.dev = &d->udev->dev; + input_dev->dev.parent = &d->udev->dev; /* set the bits for the keys */ deb_rc("key map size: %d\n", d->props.rc_key_map_size); diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h index 6f824a5..d1b3c7b 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb.h @@ -297,12 +297,6 @@ struct dvb_usb_adapter { int feedcount; int pid_filtering; - /* tuner programming information */ - u8 pll_addr; - u8 pll_init[4]; - struct dvb_pll_desc *pll_desc; - int (*tuner_pass_ctrl) (struct dvb_frontend *, int, u8); - /* dvb */ struct dvb_adapter dvb_adap; struct dmxdev dmxdev; @@ -388,11 +382,6 @@ extern int dvb_usb_generic_write(struct dvb_usb_device *, u8 *, u16); /* commonly used remote control parsing */ extern int dvb_usb_nec_rc_key_to_event(struct dvb_usb_device *, u8[], u32 *, int *); -/* commonly used pll init and set functions */ -extern int dvb_usb_tuner_init_i2c(struct dvb_frontend *); -extern int dvb_usb_tuner_calc_regs(struct dvb_frontend *, struct dvb_frontend_parameters *, u8 *buf, int buf_len); -extern int dvb_usb_tuner_set_params_i2c(struct dvb_frontend *, struct dvb_frontend_parameters *); - /* commonly used firmware download types and function */ struct hexline { u8 len; diff --git a/drivers/media/dvb/dvb-usb/gl861.c b/drivers/media/dvb/dvb-usb/gl861.c index e0587e6..f01d99c 100644 --- a/drivers/media/dvb/dvb-usb/gl861.c +++ b/drivers/media/dvb/dvb-usb/gl861.c @@ -157,6 +157,7 @@ static int gl861_probe(struct usb_interface *intf, static struct usb_device_id gl861_table [] = { { USB_DEVICE(USB_VID_MSI, USB_PID_MSI_MEGASKY580_55801) }, + { USB_DEVICE(USB_VID_ALINK, USB_VID_ALINK_DTU) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, gl861_table); @@ -187,12 +188,16 @@ static struct dvb_usb_device_properties gl861_properties = { }}, .i2c_algo = &gl861_i2c_algo, - .num_device_descs = 1, + .num_device_descs = 2, .devices = { { "MSI Mega Sky 55801 DVB-T USB2.0", { &gl861_table[0], NULL }, { NULL }, }, + { "A-LINK DTU DVB-T USB2.0", + { &gl861_table[1], NULL }, + { NULL }, + }, } }; diff --git a/drivers/media/dvb/dvb-usb/m920x.c b/drivers/media/dvb/dvb-usb/m920x.c index c546dde..a956bc5 100644 --- a/drivers/media/dvb/dvb-usb/m920x.c +++ b/drivers/media/dvb/dvb-usb/m920x.c @@ -22,6 +22,8 @@ static int dvb_usb_m920x_debug; module_param_named(debug,dvb_usb_m920x_debug, int, 0644); MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS); +static int m920x_set_filter(struct dvb_usb_device *d, int type, int idx, int pid); + static inline int m920x_read(struct usb_device *udev, u8 request, u16 value, u16 index, void *data, int size) { @@ -57,7 +59,8 @@ static inline int m920x_write(struct usb_device *udev, u8 request, static int m920x_init(struct dvb_usb_device *d, struct m920x_inits *rc_seq) { - int ret = 0; + int ret = 0, i, epi, flags = 0; + int adap_enabled[M9206_MAX_ADAPTERS] = { 0 }; /* Remote controller init. */ if (d->props.rc_query) { @@ -76,9 +79,51 @@ static int m920x_init(struct dvb_usb_device *d, struct m920x_inits *rc_seq) deb("Initialising remote control success\n"); } + for (i = 0; i < d->props.num_adapters; i++) + flags |= d->adapter[i].props.caps; + + /* Some devices(Dposh) might crash if we attempt touch at all. */ + if (flags & DVB_USB_ADAP_HAS_PID_FILTER) { + for (i = 0; i < d->props.num_adapters; i++) { + epi = d->adapter[i].props.stream.endpoint - 0x81; + + if (epi < 0 || epi >= M9206_MAX_ADAPTERS) { + printk(KERN_INFO "m920x: Unexpected adapter endpoint!\n"); + return -EINVAL; + } + + adap_enabled[epi] = 1; + } + + for (i = 0; i < M9206_MAX_ADAPTERS; i++) { + if (adap_enabled[i]) + continue; + + if ((ret = m920x_set_filter(d, 0x81 + i, 0, 0x0)) != 0) + return ret; + + if ((ret = m920x_set_filter(d, 0x81 + i, 0, 0x02f5)) != 0) + return ret; + } + } + return ret; } +static int m920x_init_ep(struct usb_interface *intf) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_host_interface *alt; + + if ((alt = usb_altnum_to_altsetting(intf, 1)) == NULL) { + deb("No alt found!\n"); + return -ENODEV; + } + + return usb_set_interface(udev, alt->desc.bInterfaceNumber, + alt->desc.bAlternateSetting); +} + static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) { struct m920x_state *m = d->priv; @@ -211,8 +256,7 @@ static struct i2c_algorithm m920x_i2c_algo = { }; /* pid filter */ -static int m920x_set_filter(struct dvb_usb_adapter *adap, - int type, int idx, int pid) +static int m920x_set_filter(struct dvb_usb_device *d, int type, int idx, int pid) { int ret = 0; @@ -221,10 +265,10 @@ static int m920x_set_filter(struct dvb_usb_adapter *adap, pid |= 0x8000; - if ((ret = m920x_write(adap->dev->udev, M9206_FILTER, pid, (type << 8) | (idx * 4) )) != 0) + if ((ret = m920x_write(d->udev, M9206_FILTER, pid, (type << 8) | (idx * 4) )) != 0) return ret; - if ((ret = m920x_write(adap->dev->udev, M9206_FILTER, 0, (type << 8) | (idx * 4) )) != 0) + if ((ret = m920x_write(d->udev, M9206_FILTER, 0, (type << 8) | (idx * 4) )) != 0) return ret; return ret; @@ -233,40 +277,35 @@ static int m920x_set_filter(struct dvb_usb_adapter *adap, static int m920x_update_filters(struct dvb_usb_adapter *adap) { struct m920x_state *m = adap->dev->priv; - int enabled = m->filtering_enabled; + int enabled = m->filtering_enabled[adap->id]; int i, ret = 0, filter = 0; + int ep = adap->props.stream.endpoint; for (i = 0; i < M9206_MAX_FILTERS; i++) - if (m->filters[i] == 8192) + if (m->filters[adap->id][i] == 8192) enabled = 0; /* Disable all filters */ - if ((ret = m920x_set_filter(adap, 0x81, 1, enabled)) != 0) + if ((ret = m920x_set_filter(adap->dev, ep, 1, enabled)) != 0) return ret; for (i = 0; i < M9206_MAX_FILTERS; i++) - if ((ret = m920x_set_filter(adap, 0x81, i + 2, 0)) != 0) + if ((ret = m920x_set_filter(adap->dev, ep, i + 2, 0)) != 0) return ret; - if ((ret = m920x_set_filter(adap, 0x82, 0, 0x0)) != 0) - return ret; - /* Set */ if (enabled) { for (i = 0; i < M9206_MAX_FILTERS; i++) { - if (m->filters[i] == 0) + if (m->filters[adap->id][i] == 0) continue; - if ((ret = m920x_set_filter(adap, 0x81, filter + 2, m->filters[i])) != 0) + if ((ret = m920x_set_filter(adap->dev, ep, filter + 2, m->filters[adap->id][i])) != 0) return ret; filter++; } } - if ((ret = m920x_set_filter(adap, 0x82, 0, 0x02f5)) != 0) - return ret; - return ret; } @@ -274,7 +313,7 @@ static int m920x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) { struct m920x_state *m = adap->dev->priv; - m->filtering_enabled = onoff ? 1 : 0; + m->filtering_enabled[adap->id] = onoff ? 1 : 0; return m920x_update_filters(adap); } @@ -283,7 +322,7 @@ static int m920x_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, in { struct m920x_state *m = adap->dev->priv; - m->filters[index] = onoff ? pid : 0; + m->filters[adap->id][index] = onoff ? pid : 0; return m920x_update_filters(adap); } @@ -368,6 +407,7 @@ static int m920x_identify_state(struct usb_device *udev, /* demod configurations */ static int m920x_mt352_demod_init(struct dvb_frontend *fe) { + int ret; u8 config[] = { CONFIG, 0x3d }; u8 clock[] = { CLOCK_CTL, 0x30 }; u8 reset[] = { RESET, 0x80 }; @@ -377,17 +417,25 @@ static int m920x_mt352_demod_init(struct dvb_frontend *fe) u8 unk1[] = { 0x93, 0x1a }; u8 unk2[] = { 0xb5, 0x7a }; - mt352_write(fe, config, ARRAY_SIZE(config)); - mt352_write(fe, clock, ARRAY_SIZE(clock)); - mt352_write(fe, reset, ARRAY_SIZE(reset)); - mt352_write(fe, adc_ctl, ARRAY_SIZE(adc_ctl)); - mt352_write(fe, agc, ARRAY_SIZE(agc)); - mt352_write(fe, sec_agc, ARRAY_SIZE(sec_agc)); - mt352_write(fe, unk1, ARRAY_SIZE(unk1)); - mt352_write(fe, unk2, ARRAY_SIZE(unk2)); - deb("Demod init!\n"); + if ((ret = mt352_write(fe, config, ARRAY_SIZE(config))) != 0) + return ret; + if ((ret = mt352_write(fe, clock, ARRAY_SIZE(clock))) != 0) + return ret; + if ((ret = mt352_write(fe, reset, ARRAY_SIZE(reset))) != 0) + return ret; + if ((ret = mt352_write(fe, adc_ctl, ARRAY_SIZE(adc_ctl))) != 0) + return ret; + if ((ret = mt352_write(fe, agc, ARRAY_SIZE(agc))) != 0) + return ret; + if ((ret = mt352_write(fe, sec_agc, ARRAY_SIZE(sec_agc))) != 0) + return ret; + if ((ret = mt352_write(fe, unk1, ARRAY_SIZE(unk1))) != 0) + return ret; + if ((ret = mt352_write(fe, unk2, ARRAY_SIZE(unk2))) != 0) + return ret; + return 0; } @@ -558,8 +606,7 @@ static struct dvb_usb_device_properties dposh_properties; static int m920x_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct dvb_usb_device *d; - struct usb_host_interface *alt; + struct dvb_usb_device *d = NULL; int ret; struct m920x_inits *rc_init_seq = NULL; int bInterfaceNumber = intf->cur_altsetting->desc.bInterfaceNumber; @@ -604,23 +651,13 @@ static int m920x_probe(struct usb_interface *intf, * tvwalkertwin_properties already configured both * tuners, so there is nothing for us to do here */ - - return -ENODEV; } found: - alt = usb_altnum_to_altsetting(intf, 1); - if (alt == NULL) { - deb("No alt found!\n"); - return -ENODEV; - } - - ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber, - alt->desc.bAlternateSetting); - if (ret < 0) + if ((ret = m920x_init_ep(intf)) < 0) return ret; - if ((ret = m920x_init(d, rc_init_seq)) != 0) + if (d && (ret = m920x_init(d, rc_init_seq)) != 0) return ret; return ret; @@ -737,9 +774,9 @@ static struct dvb_usb_device_properties digivox_mini_ii_properties = { * * LifeView TV Walker Twin has 1 x M9206, 2 x TDA10046, 2 x TDA8275A * TDA10046 #0 is located at i2c address 0x08 - * TDA10046 #1 is located at i2c address 0x0b (presently disabled - not yet working) + * TDA10046 #1 is located at i2c address 0x0b * TDA8275A #0 is located at i2c address 0x60 - * TDA8275A #1 is located at i2c address 0x61 (presently disabled - not yet working) + * TDA8275A #1 is located at i2c address 0x61 */ static struct dvb_usb_device_properties tvwalkertwin_properties = { .caps = DVB_USB_IS_AN_I2C_ADAPTER, @@ -756,7 +793,7 @@ static struct dvb_usb_device_properties tvwalkertwin_properties = { .size_of_priv = sizeof(struct m920x_state), .identify_state = m920x_identify_state, - .num_adapters = 1, + .num_adapters = 2, .adapter = {{ .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, diff --git a/drivers/media/dvb/dvb-usb/m920x.h b/drivers/media/dvb/dvb-usb/m920x.h index 2c8942d..3753289 100644 --- a/drivers/media/dvb/dvb-usb/m920x.h +++ b/drivers/media/dvb/dvb-usb/m920x.h @@ -18,6 +18,7 @@ #define M9206_FW 0x30 #define M9206_MAX_FILTERS 8 +#define M9206_MAX_ADAPTERS 2 /* sequences found in logs: @@ -60,8 +61,8 @@ response to a write, is unknown. */ struct m920x_state { - u16 filters[M9206_MAX_FILTERS]; - int filtering_enabled; + u16 filters[M9206_MAX_ADAPTERS][M9206_MAX_FILTERS]; + int filtering_enabled[M9206_MAX_ADAPTERS]; int rep_count; }; diff --git a/drivers/media/dvb/dvb-usb/opera1.c b/drivers/media/dvb/dvb-usb/opera1.c index 518d7ad..d7c0495 100644 --- a/drivers/media/dvb/dvb-usb/opera1.c +++ b/drivers/media/dvb/dvb-usb/opera1.c @@ -263,7 +263,7 @@ static int opera1_tuner_attach(struct dvb_usb_adapter *adap) { dvb_attach( dvb_pll_attach, adap->fe, 0xc0>>1, - &adap->dev->i2c_adap, &dvb_pll_opera1 + &adap->dev->i2c_adap, DVB_PLL_OPERA1 ); return 0; } @@ -435,9 +435,9 @@ static int opera1_xilinx_load_firmware(struct usb_device *dev, { const struct firmware *fw = NULL; u8 *b, *p; - int ret = 0, i; + int ret = 0, i,fpgasize=40; u8 testval; - info("start downloading fpga firmware"); + info("start downloading fpga firmware %s",filename); if ((ret = request_firmware(&fw, filename, &dev->dev)) != 0) { err("did not find the firmware file. (%s) " @@ -454,17 +454,20 @@ static int opera1_xilinx_load_firmware(struct usb_device *dev, /* clear fpga ? */ opera1_xilinx_rw(dev, 0xbc, 0xaa, &fpga_command, 1, OPERA_WRITE_MSG); - for (i = 0; p[i] != 0 && i < fw->size;) { + for (i = 0; i < fw->size;) { + if ( (fw->size - i) <fpgasize){ + fpgasize=fw->size-i; + } b = (u8 *) p + i; if (opera1_xilinx_rw - (dev, OPERA_WRITE_FX2, 0x0, b + 1, b[0], - OPERA_WRITE_MSG) != b[0] + (dev, OPERA_WRITE_FX2, 0x0, b , fpgasize, + OPERA_WRITE_MSG) != fpgasize ) { err("error while transferring firmware"); ret = -EINVAL; break; } - i = i + 1 + b[0]; + i = i + fpgasize; } /* restart the CPU */ if (ret || opera1_xilinx_rw @@ -534,18 +537,16 @@ static struct dvb_usb_device_properties opera1_properties = { static int opera1_probe(struct usb_interface *intf, const struct usb_device_id *id) { - struct dvb_usb_device *d; struct usb_device *udev = interface_to_usbdev(intf); if (udev->descriptor.idProduct == USB_PID_OPERA1_WARM && udev->descriptor.idVendor == USB_VID_OPERA1 && - (d == NULL - || opera1_xilinx_load_firmware(udev, "dvb-usb-opera1-fpga.fw") != 0) - ) { + opera1_xilinx_load_firmware(udev, "dvb-usb-opera1-fpga-01.fw") != 0 + ) { return -EINVAL; } - if (dvb_usb_device_init(intf, &opera1_properties, THIS_MODULE, &d) != 0) + if (dvb_usb_device_init(intf, &opera1_properties, THIS_MODULE, NULL) != 0) return -EINVAL; return 0; } diff --git a/drivers/media/dvb/dvb-usb/umt-010.c b/drivers/media/dvb/dvb-usb/umt-010.c index f77b48f..0dcab3d 100644 --- a/drivers/media/dvb/dvb-usb/umt-010.c +++ b/drivers/media/dvb/dvb-usb/umt-010.c @@ -65,9 +65,7 @@ static int umt_mt352_frontend_attach(struct dvb_usb_adapter *adap) static int umt_tuner_attach (struct dvb_usb_adapter *adap) { - adap->pll_addr = 0x61; - adap->pll_desc = &dvb_pll_tua6034; - adap->fe->ops.tuner_ops.calc_regs = dvb_usb_tuner_calc_regs; + dvb_attach(dvb_pll_attach, adap->fe, 0x61, NULL, DVB_PLL_TUA6034); return 0; } @@ -84,8 +82,8 @@ static int umt_probe(struct usb_interface *intf, /* do not change the order of the ID table */ static struct usb_device_id umt_table [] = { -/* 00 */ { USB_DEVICE(USB_VID_HANFTEK, USB_PID_HANFTEK_UMT_010_COLD) }, -/* 01 */ { USB_DEVICE(USB_VID_HANFTEK, USB_PID_HANFTEK_UMT_010_WARM) }, +/* 00 */ { USB_DEVICE(USB_VID_HANFTEK, USB_PID_HANFTEK_UMT_010_COLD) }, +/* 01 */ { USB_DEVICE(USB_VID_HANFTEK, USB_PID_HANFTEK_UMT_010_WARM) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, umt_table); diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 27f3865..156b062 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -2,7 +2,7 @@ # Makefile for the kernel DVB frontend device drivers. # -EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ obj-$(CONFIG_DVB_PLL) += dvb-pll.o obj-$(CONFIG_DVB_STV0299) += stv0299.o diff --git a/drivers/media/dvb/frontends/cx22702.c b/drivers/media/dvb/frontends/cx22702.c index 335219e..1dc164d 100644 --- a/drivers/media/dvb/frontends/cx22702.c +++ b/drivers/media/dvb/frontends/cx22702.c @@ -32,7 +32,6 @@ #include <linux/slab.h> #include <linux/delay.h> #include "dvb_frontend.h" -#include "dvb-pll.h" #include "cx22702.h" diff --git a/drivers/media/dvb/frontends/cx24123.c b/drivers/media/dvb/frontends/cx24123.c index 732e94a..0834c06 100644 --- a/drivers/media/dvb/frontends/cx24123.c +++ b/drivers/media/dvb/frontends/cx24123.c @@ -917,7 +917,7 @@ static int cx24123_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) static int cx24123_tune(struct dvb_frontend* fe, struct dvb_frontend_parameters* params, unsigned int mode_flags, - int *delay, + unsigned int *delay, fe_status_t *status) { int retval = 0; diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c index 5f96ffd..0c0b947 100644 --- a/drivers/media/dvb/frontends/dvb-pll.c +++ b/drivers/media/dvb/frontends/dvb-pll.c @@ -24,6 +24,23 @@ #include "dvb-pll.h" +struct dvb_pll_desc { + char *name; + u32 min; + u32 max; + u32 iffreq; + void (*set)(u8 *buf, const struct dvb_frontend_parameters *params); + u8 *initdata; + u8 *sleepdata; + int count; + struct { + u32 limit; + u32 stepsize; + u8 config; + u8 cb; + } entries[12]; +}; + /* ----------------------------------------------------------- */ /* descriptions */ @@ -38,7 +55,13 @@ 0x50 = AGC Take over point = 103 dBuV */ static u8 tua603x_agc103[] = { 2, 0x80|0x40|0x18|0x06|0x01, 0x00|0x50 }; -struct dvb_pll_desc dvb_pll_thomson_dtt7579 = { +/* 0x04 = 166.67 kHz divider + + 0x80 = AGC Time constant 50ms Iagc = 9 uA + 0x20 = AGC Take over point = 112 dBuV */ +static u8 tua603x_agc112[] = { 2, 0x80|0x40|0x18|0x04|0x01, 0x80|0x20 }; + +static struct dvb_pll_desc dvb_pll_thomson_dtt7579 = { .name = "Thomson dtt7579", .min = 177000000, .max = 858000000, @@ -52,9 +75,8 @@ struct dvb_pll_desc dvb_pll_thomson_dtt7579 = { { 999999999, 166667, 0xf4, 0x08 }, }, }; -EXPORT_SYMBOL(dvb_pll_thomson_dtt7579); -struct dvb_pll_desc dvb_pll_thomson_dtt7610 = { +static struct dvb_pll_desc dvb_pll_thomson_dtt7610 = { .name = "Thomson dtt7610", .min = 44000000, .max = 958000000, @@ -66,19 +88,19 @@ struct dvb_pll_desc dvb_pll_thomson_dtt7610 = { { 999999999, 62500, 0x8e, 0x3c }, }, }; -EXPORT_SYMBOL(dvb_pll_thomson_dtt7610); -static void thomson_dtt759x_bw(u8 *buf, u32 freq, int bandwidth) +static void thomson_dtt759x_bw(u8 *buf, + const struct dvb_frontend_parameters *params) { - if (BANDWIDTH_7_MHZ == bandwidth) + if (BANDWIDTH_7_MHZ == params->u.ofdm.bandwidth) buf[3] |= 0x10; } -struct dvb_pll_desc dvb_pll_thomson_dtt759x = { +static struct dvb_pll_desc dvb_pll_thomson_dtt759x = { .name = "Thomson dtt759x", .min = 177000000, .max = 896000000, - .setbw = thomson_dtt759x_bw, + .set = thomson_dtt759x_bw, .iffreq= 36166667, .sleepdata = (u8[]){ 2, 0x84, 0x03 }, .count = 5, @@ -90,9 +112,8 @@ struct dvb_pll_desc dvb_pll_thomson_dtt759x = { { 999999999, 166667, 0xfc, 0x08 }, }, }; -EXPORT_SYMBOL(dvb_pll_thomson_dtt759x); -struct dvb_pll_desc dvb_pll_lg_z201 = { +static struct dvb_pll_desc dvb_pll_lg_z201 = { .name = "LG z201", .min = 174000000, .max = 862000000, @@ -107,9 +128,8 @@ struct dvb_pll_desc dvb_pll_lg_z201 = { { 999999999, 166667, 0xfc, 0x04 }, }, }; -EXPORT_SYMBOL(dvb_pll_lg_z201); -struct dvb_pll_desc dvb_pll_microtune_4042 = { +static struct dvb_pll_desc dvb_pll_microtune_4042 = { .name = "Microtune 4042 FI5", .min = 57000000, .max = 858000000, @@ -121,9 +141,8 @@ struct dvb_pll_desc dvb_pll_microtune_4042 = { { 999999999, 62500, 0x8e, 0x31 }, }, }; -EXPORT_SYMBOL(dvb_pll_microtune_4042); -struct dvb_pll_desc dvb_pll_thomson_dtt761x = { +static struct dvb_pll_desc dvb_pll_thomson_dtt761x = { /* DTT 7611 7611A 7612 7613 7613A 7614 7615 7615A */ .name = "Thomson dtt761x", .min = 57000000, @@ -137,9 +156,8 @@ struct dvb_pll_desc dvb_pll_thomson_dtt761x = { { 999999999, 62500, 0x8e, 0x3c }, }, }; -EXPORT_SYMBOL(dvb_pll_thomson_dtt761x); -struct dvb_pll_desc dvb_pll_unknown_1 = { +static struct dvb_pll_desc dvb_pll_unknown_1 = { .name = "unknown 1", /* used by dntv live dvb-t */ .min = 174000000, .max = 862000000, @@ -157,12 +175,11 @@ struct dvb_pll_desc dvb_pll_unknown_1 = { { 999999999, 166667, 0xfc, 0x08 }, }, }; -EXPORT_SYMBOL(dvb_pll_unknown_1); /* Infineon TUA6010XS * used in Thomson Cable Tuner */ -struct dvb_pll_desc dvb_pll_tua6010xs = { +static struct dvb_pll_desc dvb_pll_tua6010xs = { .name = "Infineon TUA6010XS", .min = 44250000, .max = 858000000, @@ -174,10 +191,9 @@ struct dvb_pll_desc dvb_pll_tua6010xs = { { 999999999, 62500, 0x8e, 0x85 }, }, }; -EXPORT_SYMBOL(dvb_pll_tua6010xs); /* Panasonic env57h1xd5 (some Philips PLL ?) */ -struct dvb_pll_desc dvb_pll_env57h1xd5 = { +static struct dvb_pll_desc dvb_pll_env57h1xd5 = { .name = "Panasonic ENV57H1XD5", .min = 44250000, .max = 858000000, @@ -190,23 +206,23 @@ struct dvb_pll_desc dvb_pll_env57h1xd5 = { { 999999999, 166667, 0xc2, 0xa4 }, }, }; -EXPORT_SYMBOL(dvb_pll_env57h1xd5); /* Philips TDA6650/TDA6651 * used in Panasonic ENV77H11D5 */ -static void tda665x_bw(u8 *buf, u32 freq, int bandwidth) +static void tda665x_bw(u8 *buf, const struct dvb_frontend_parameters *params) { - if (bandwidth == BANDWIDTH_8_MHZ) + if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) buf[3] |= 0x08; } -struct dvb_pll_desc dvb_pll_tda665x = { +static struct dvb_pll_desc dvb_pll_tda665x = { .name = "Philips TDA6650/TDA6651", .min = 44250000, .max = 858000000, - .setbw = tda665x_bw, + .set = tda665x_bw, .iffreq= 36166667, + .initdata = (u8[]){ 4, 0x0b, 0xf5, 0x85, 0xab }, .count = 12, .entries = { { 93834000, 166667, 0xca, 0x61 /* 011 0 0 0 01 */ }, @@ -223,36 +239,34 @@ struct dvb_pll_desc dvb_pll_tda665x = { { 861000000, 166667, 0xca, 0xe4 /* 111 0 0 1 00 */ }, } }; -EXPORT_SYMBOL(dvb_pll_tda665x); /* Infineon TUA6034 * used in LG TDTP E102P */ -static void tua6034_bw(u8 *buf, u32 freq, int bandwidth) +static void tua6034_bw(u8 *buf, const struct dvb_frontend_parameters *params) { - if (BANDWIDTH_7_MHZ != bandwidth) + if (BANDWIDTH_7_MHZ != params->u.ofdm.bandwidth) buf[3] |= 0x08; } -struct dvb_pll_desc dvb_pll_tua6034 = { +static struct dvb_pll_desc dvb_pll_tua6034 = { .name = "Infineon TUA6034", .min = 44250000, .max = 858000000, .iffreq= 36166667, .count = 3, - .setbw = tua6034_bw, + .set = tua6034_bw, .entries = { { 174500000, 62500, 0xce, 0x01 }, { 230000000, 62500, 0xce, 0x02 }, { 999999999, 62500, 0xce, 0x04 }, }, }; -EXPORT_SYMBOL(dvb_pll_tua6034); /* Infineon TUA6034 * used in LG TDVS-H061F, LG TDVS-H062F and LG TDVS-H064F */ -struct dvb_pll_desc dvb_pll_lg_tdvs_h06xf = { +static struct dvb_pll_desc dvb_pll_lg_tdvs_h06xf = { .name = "LG TDVS-H06xF", .min = 54000000, .max = 863000000, @@ -265,23 +279,25 @@ struct dvb_pll_desc dvb_pll_lg_tdvs_h06xf = { { 999999999, 62500, 0xce, 0x04 }, }, }; -EXPORT_SYMBOL(dvb_pll_lg_tdvs_h06xf); /* Philips FMD1216ME * used in Medion Hybrid PCMCIA card and USB Box */ -static void fmd1216me_bw(u8 *buf, u32 freq, int bandwidth) +static void fmd1216me_bw(u8 *buf, const struct dvb_frontend_parameters *params) { - if (bandwidth == BANDWIDTH_8_MHZ && freq >= 158870000) + if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ && + params->frequency >= 158870000) buf[3] |= 0x08; } -struct dvb_pll_desc dvb_pll_fmd1216me = { +static struct dvb_pll_desc dvb_pll_fmd1216me = { .name = "Philips FMD1216ME", .min = 50870000, .max = 858000000, .iffreq= 36125000, - .setbw = fmd1216me_bw, + .set = fmd1216me_bw, + .initdata = tua603x_agc112, + .sleepdata = (u8[]){ 4, 0x9c, 0x60, 0x85, 0x54 }, .count = 7, .entries = { { 143870000, 166667, 0xbc, 0x41 }, @@ -293,23 +309,22 @@ struct dvb_pll_desc dvb_pll_fmd1216me = { { 999999999, 166667, 0xfc, 0x44 }, } }; -EXPORT_SYMBOL(dvb_pll_fmd1216me); /* ALPS TDED4 * used in Nebula-Cards and USB boxes */ -static void tded4_bw(u8 *buf, u32 freq, int bandwidth) +static void tded4_bw(u8 *buf, const struct dvb_frontend_parameters *params) { - if (bandwidth == BANDWIDTH_8_MHZ) + if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) buf[3] |= 0x04; } -struct dvb_pll_desc dvb_pll_tded4 = { +static struct dvb_pll_desc dvb_pll_tded4 = { .name = "ALPS TDED4", .min = 47000000, .max = 863000000, .iffreq= 36166667, - .setbw = tded4_bw, + .set = tded4_bw, .count = 4, .entries = { { 153000000, 166667, 0x85, 0x01 }, @@ -318,12 +333,11 @@ struct dvb_pll_desc dvb_pll_tded4 = { { 999999999, 166667, 0x85, 0x88 }, } }; -EXPORT_SYMBOL(dvb_pll_tded4); /* ALPS TDHU2 * used in AverTVHD MCE A180 */ -struct dvb_pll_desc dvb_pll_tdhu2 = { +static struct dvb_pll_desc dvb_pll_tdhu2 = { .name = "ALPS TDHU2", .min = 54000000, .max = 864000000, @@ -336,16 +350,29 @@ struct dvb_pll_desc dvb_pll_tdhu2 = { { 999999999, 62500, 0x85, 0x88 }, } }; -EXPORT_SYMBOL(dvb_pll_tdhu2); /* Philips TUV1236D * used in ATI HDTV Wonder */ -struct dvb_pll_desc dvb_pll_tuv1236d = { +static void tuv1236d_rf(u8 *buf, const struct dvb_frontend_parameters *params) +{ + switch (params->u.vsb.modulation) { + case QAM_64: + case QAM_256: + buf[3] |= 0x08; + break; + case VSB_8: + default: + buf[3] &= ~0x08; + } +} + +static struct dvb_pll_desc dvb_pll_tuv1236d = { .name = "Philips TUV1236D", .min = 54000000, .max = 864000000, .iffreq= 44000000, + .set = tuv1236d_rf, .count = 3, .entries = { { 157250000, 62500, 0xc6, 0x41 }, @@ -353,12 +380,11 @@ struct dvb_pll_desc dvb_pll_tuv1236d = { { 999999999, 62500, 0xc6, 0x44 }, }, }; -EXPORT_SYMBOL(dvb_pll_tuv1236d); /* Samsung TBMV30111IN / TBMV30712IN1 * used in Air2PC ATSC - 2nd generation (nxt2002) */ -struct dvb_pll_desc dvb_pll_samsung_tbmv = { +static struct dvb_pll_desc dvb_pll_samsung_tbmv = { .name = "Samsung TBMV30111IN / TBMV30712IN1", .min = 54000000, .max = 860000000, @@ -373,12 +399,11 @@ struct dvb_pll_desc dvb_pll_samsung_tbmv = { { 999999999, 166667, 0xfc, 0x02 }, } }; -EXPORT_SYMBOL(dvb_pll_samsung_tbmv); /* * Philips SD1878 Tuner. */ -struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = { +static struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = { .name = "Philips SD1878", .min = 950000, .max = 2150000, @@ -391,19 +416,18 @@ struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = { { 2150000, 500, 0xc4, 0xc0}, }, }; -EXPORT_SYMBOL(dvb_pll_philips_sd1878_tda8261); /* * Philips TD1316 Tuner. */ -static void td1316_bw(u8 *buf, u32 freq, int bandwidth) +static void td1316_bw(u8 *buf, const struct dvb_frontend_parameters *params) { u8 band; /* determine band */ - if (freq < 161000000) + if (params->frequency < 161000000) band = 1; - else if (freq < 444000000) + else if (params->frequency < 444000000) band = 2; else band = 4; @@ -411,16 +435,16 @@ static void td1316_bw(u8 *buf, u32 freq, int bandwidth) buf[3] |= band; /* setup PLL filter */ - if (bandwidth == BANDWIDTH_8_MHZ) + if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) buf[3] |= 1 << 3; } -struct dvb_pll_desc dvb_pll_philips_td1316 = { +static struct dvb_pll_desc dvb_pll_philips_td1316 = { .name = "Philips TD1316", .min = 87000000, .max = 895000000, .iffreq= 36166667, - .setbw = td1316_bw, + .set = td1316_bw, .count = 9, .entries = { { 93834000, 166667, 0xca, 0x60}, @@ -434,10 +458,9 @@ struct dvb_pll_desc dvb_pll_philips_td1316 = { { 858834000, 166667, 0xca, 0xe0}, }, }; -EXPORT_SYMBOL(dvb_pll_philips_td1316); /* FE6600 used on DViCO Hybrid */ -struct dvb_pll_desc dvb_pll_thomson_fe6600 = { +static struct dvb_pll_desc dvb_pll_thomson_fe6600 = { .name = "Thomson FE6600", .min = 44250000, .max = 858000000, @@ -450,19 +473,19 @@ struct dvb_pll_desc dvb_pll_thomson_fe6600 = { { 999999999, 166667, 0xf4, 0x18 }, } }; -EXPORT_SYMBOL(dvb_pll_thomson_fe6600); -static void opera1_bw(u8 *buf, u32 freq, int bandwidth) + +static void opera1_bw(u8 *buf, const struct dvb_frontend_parameters *params) { - if (bandwidth == BANDWIDTH_8_MHZ) + if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) buf[2] |= 0x08; } -struct dvb_pll_desc dvb_pll_opera1 = { +static struct dvb_pll_desc dvb_pll_opera1 = { .name = "Opera Tuner", .min = 900000, .max = 2250000, .iffreq= 0, - .setbw = opera1_bw, + .set = opera1_bw, .count = 8, .entries = { { 1064000, 500, 0xe5, 0xc6 }, @@ -475,7 +498,54 @@ struct dvb_pll_desc dvb_pll_opera1 = { { 2250000, 500, 0xe5, 0xc4 }, } }; -EXPORT_SYMBOL(dvb_pll_opera1); + +/* Philips FCV1236D + */ +struct dvb_pll_desc dvb_pll_fcv1236d = { +/* Bit_0: RF Input select + * Bit_1: 0=digital, 1=analog + */ + .name = "Philips FCV1236D", + .min = 53000000, + .max = 803000000, + .iffreq= 44000000, + .count = 3, + .entries = { + { 159000000, 62500, 0x8e, 0xa0 }, + { 453000000, 62500, 0x8e, 0x90 }, + { 999999999, 62500, 0x8e, 0x30 }, + }, +}; + +/* ----------------------------------------------------------- */ + +static struct dvb_pll_desc *pll_list[] = { + [DVB_PLL_UNDEFINED] = NULL, + [DVB_PLL_THOMSON_DTT7579] = &dvb_pll_thomson_dtt7579, + [DVB_PLL_THOMSON_DTT759X] = &dvb_pll_thomson_dtt759x, + [DVB_PLL_THOMSON_DTT7610] = &dvb_pll_thomson_dtt7610, + [DVB_PLL_LG_Z201] = &dvb_pll_lg_z201, + [DVB_PLL_MICROTUNE_4042] = &dvb_pll_microtune_4042, + [DVB_PLL_THOMSON_DTT761X] = &dvb_pll_thomson_dtt761x, + [DVB_PLL_UNKNOWN_1] = &dvb_pll_unknown_1, + [DVB_PLL_TUA6010XS] = &dvb_pll_tua6010xs, + [DVB_PLL_ENV57H1XD5] = &dvb_pll_env57h1xd5, + [DVB_PLL_TUA6034] = &dvb_pll_tua6034, + [DVB_PLL_LG_TDVS_H06XF] = &dvb_pll_lg_tdvs_h06xf, + [DVB_PLL_TDA665X] = &dvb_pll_tda665x, + [DVB_PLL_FMD1216ME] = &dvb_pll_fmd1216me, + [DVB_PLL_TDED4] = &dvb_pll_tded4, + [DVB_PLL_TUV1236D] = &dvb_pll_tuv1236d, + [DVB_PLL_TDHU2] = &dvb_pll_tdhu2, + [DVB_PLL_SAMSUNG_TBMV] = &dvb_pll_samsung_tbmv, + [DVB_PLL_PHILIPS_SD1878_TDA8261] = &dvb_pll_philips_sd1878_tda8261, + [DVB_PLL_PHILIPS_TD1316] = &dvb_pll_philips_td1316, + [DVB_PLL_THOMSON_FE6600] = &dvb_pll_thomson_fe6600, + [DVB_PLL_OPERA1] = &dvb_pll_opera1, + [DVB_PLL_FCV1236D] = &dvb_pll_fcv1236d, +}; + +/* ----------------------------------------------------------- */ struct dvb_pll_priv { /* i2c details */ @@ -497,35 +567,37 @@ static int debug = 0; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable verbose debug messages"); -int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, - u32 freq, int bandwidth) +static int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, + const struct dvb_frontend_parameters *params) { u32 div; int i; - if (freq != 0 && (freq < desc->min || freq > desc->max)) - return -EINVAL; + if (params->frequency != 0 && (params->frequency < desc->min || + params->frequency > desc->max)) + return -EINVAL; for (i = 0; i < desc->count; i++) { - if (freq > desc->entries[i].limit) + if (params->frequency > desc->entries[i].limit) continue; break; } + if (debug) - printk("pll: %s: freq=%d bw=%d | i=%d/%d\n", - desc->name, freq, bandwidth, i, desc->count); + printk("pll: %s: freq=%d | i=%d/%d\n", desc->name, + params->frequency, i, desc->count); if (i == desc->count) return -EINVAL; - div = (freq + desc->iffreq + desc->entries[i].stepsize/2) / - desc->entries[i].stepsize; + div = (params->frequency + desc->iffreq + + desc->entries[i].stepsize/2) / desc->entries[i].stepsize; buf[0] = div >> 8; buf[1] = div & 0xff; buf[2] = desc->entries[i].config; buf[3] = desc->entries[i].cb; - if (desc->setbw) - desc->setbw(buf, freq, bandwidth); + if (desc->set) + desc->set(buf, params); if (debug) printk("pll: %s: div=%d | buf=0x%02x,0x%02x,0x%02x,0x%02x\n", @@ -534,7 +606,6 @@ int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, // calculate the frequency we set it to return (div * desc->entries[i].stepsize) - desc->iffreq; } -EXPORT_SYMBOL(dvb_pll_configure); static int dvb_pll_release(struct dvb_frontend *fe) { @@ -578,18 +649,12 @@ static int dvb_pll_set_params(struct dvb_frontend *fe, { .addr = priv->pll_i2c_address, .flags = 0, .buf = buf, .len = sizeof(buf) }; int result; - u32 bandwidth = 0, frequency = 0; + u32 frequency = 0; if (priv->i2c == NULL) return -EINVAL; - // DVBT bandwidth only just now - if (fe->ops.info.type == FE_OFDM) { - bandwidth = params->u.ofdm.bandwidth; - } - - if ((result = dvb_pll_configure(priv->pll_desc, buf, - params->frequency, bandwidth)) < 0) + if ((result = dvb_pll_configure(priv->pll_desc, buf, params)) < 0) return result; else frequency = result; @@ -601,7 +666,7 @@ static int dvb_pll_set_params(struct dvb_frontend *fe, } priv->frequency = frequency; - priv->bandwidth = bandwidth; + priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0; return 0; } @@ -612,18 +677,12 @@ static int dvb_pll_calc_regs(struct dvb_frontend *fe, { struct dvb_pll_priv *priv = fe->tuner_priv; int result; - u32 bandwidth = 0, frequency = 0; + u32 frequency = 0; if (buf_len < 5) return -EINVAL; - // DVBT bandwidth only just now - if (fe->ops.info.type == FE_OFDM) { - bandwidth = params->u.ofdm.bandwidth; - } - - if ((result = dvb_pll_configure(priv->pll_desc, buf+1, - params->frequency, bandwidth)) < 0) + if ((result = dvb_pll_configure(priv->pll_desc, buf+1, params)) < 0) return result; else frequency = result; @@ -631,7 +690,7 @@ static int dvb_pll_calc_regs(struct dvb_frontend *fe, buf[0] = priv->pll_i2c_address; priv->frequency = frequency; - priv->bandwidth = bandwidth; + priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0; return 5; } @@ -687,13 +746,18 @@ static struct dvb_tuner_ops dvb_pll_tuner_ops = { struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, - struct dvb_pll_desc *desc) + unsigned int pll_desc_id) { u8 b1 [] = { 0 }; struct i2c_msg msg = { .addr = pll_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 }; struct dvb_pll_priv *priv = NULL; int ret; + struct dvb_pll_desc *desc; + + BUG_ON(pll_desc_id < 1 || pll_desc_id >= ARRAY_SIZE(pll_list)); + + desc = pll_list[pll_desc_id]; if (i2c != NULL) { if (fe->ops.i2c_gate_ctrl) diff --git a/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h index 5209f46..e93a810 100644 --- a/drivers/media/dvb/frontends/dvb-pll.h +++ b/drivers/media/dvb/frontends/dvb-pll.h @@ -8,50 +8,29 @@ #include <linux/i2c.h> #include "dvb_frontend.h" -struct dvb_pll_desc { - char *name; - u32 min; - u32 max; - u32 iffreq; - void (*setbw)(u8 *buf, u32 freq, int bandwidth); - u8 *initdata; - u8 *sleepdata; - int count; - struct { - u32 limit; - u32 stepsize; - u8 config; - u8 cb; - } entries[12]; -}; - -extern struct dvb_pll_desc dvb_pll_thomson_dtt7579; -extern struct dvb_pll_desc dvb_pll_thomson_dtt759x; -extern struct dvb_pll_desc dvb_pll_thomson_dtt7610; -extern struct dvb_pll_desc dvb_pll_lg_z201; -extern struct dvb_pll_desc dvb_pll_microtune_4042; -extern struct dvb_pll_desc dvb_pll_thomson_dtt761x; -extern struct dvb_pll_desc dvb_pll_unknown_1; - -extern struct dvb_pll_desc dvb_pll_tua6010xs; -extern struct dvb_pll_desc dvb_pll_env57h1xd5; -extern struct dvb_pll_desc dvb_pll_tua6034; -extern struct dvb_pll_desc dvb_pll_lg_tdvs_h06xf; -extern struct dvb_pll_desc dvb_pll_tda665x; -extern struct dvb_pll_desc dvb_pll_fmd1216me; -extern struct dvb_pll_desc dvb_pll_tded4; - -extern struct dvb_pll_desc dvb_pll_tuv1236d; -extern struct dvb_pll_desc dvb_pll_tdhu2; -extern struct dvb_pll_desc dvb_pll_samsung_tbmv; -extern struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261; -extern struct dvb_pll_desc dvb_pll_philips_td1316; - -extern struct dvb_pll_desc dvb_pll_thomson_fe6600; -extern struct dvb_pll_desc dvb_pll_opera1; - -extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, - u32 freq, int bandwidth); +#define DVB_PLL_UNDEFINED 0 +#define DVB_PLL_THOMSON_DTT7579 1 +#define DVB_PLL_THOMSON_DTT759X 2 +#define DVB_PLL_THOMSON_DTT7610 3 +#define DVB_PLL_LG_Z201 4 +#define DVB_PLL_MICROTUNE_4042 5 +#define DVB_PLL_THOMSON_DTT761X 6 +#define DVB_PLL_UNKNOWN_1 7 +#define DVB_PLL_TUA6010XS 8 +#define DVB_PLL_ENV57H1XD5 9 +#define DVB_PLL_TUA6034 10 +#define DVB_PLL_LG_TDVS_H06XF 11 +#define DVB_PLL_TDA665X 12 +#define DVB_PLL_FMD1216ME 13 +#define DVB_PLL_TDED4 14 +#define DVB_PLL_TUV1236D 15 +#define DVB_PLL_TDHU2 16 +#define DVB_PLL_SAMSUNG_TBMV 17 +#define DVB_PLL_PHILIPS_SD1878_TDA8261 18 +#define DVB_PLL_PHILIPS_TD1316 19 +#define DVB_PLL_THOMSON_FE6600 20 +#define DVB_PLL_OPERA1 21 +#define DVB_PLL_FCV1236D 22 /** * Attach a dvb-pll to the supplied frontend structure. @@ -59,19 +38,19 @@ extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, * @param fe Frontend to attach to. * @param pll_addr i2c address of the PLL (if used). * @param i2c i2c adapter to use (set to NULL if not used). - * @param desc dvb_pll_desc to use. + * @param pll_desc_id dvb_pll_desc to use. * @return Frontend pointer on success, NULL on failure */ #if defined(CONFIG_DVB_PLL) || (defined(CONFIG_DVB_PLL_MODULE) && defined(MODULE)) extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, - struct dvb_pll_desc *desc); + unsigned int pll_desc_id); #else static inline struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, - struct dvb_pll_desc *desc) + unsigned int pll_desc_id) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); return NULL; diff --git a/drivers/media/dvb/frontends/nxt200x.c b/drivers/media/dvb/frontends/nxt200x.c index b809f83..ddc8489 100644 --- a/drivers/media/dvb/frontends/nxt200x.c +++ b/drivers/media/dvb/frontends/nxt200x.c @@ -49,7 +49,6 @@ #include <linux/string.h> #include "dvb_frontend.h" -#include "dvb-pll.h" #include "nxt200x.h" struct nxt200x_state { @@ -546,11 +545,6 @@ static int nxt200x_setup_frontend_parameters (struct dvb_frontend* fe, nxt200x_writebytes(state, 0x17, buf, 1); } - /* get tuning information */ - if (fe->ops.tuner_ops.calc_regs) { - fe->ops.tuner_ops.calc_regs(fe, p, buf, 5); - } - /* set additional params */ switch (p->u.vsb.modulation) { case QAM_64: @@ -559,27 +553,24 @@ static int nxt200x_setup_frontend_parameters (struct dvb_frontend* fe, /* This is just a guess since I am unable to test it */ if (state->config->set_ts_params) state->config->set_ts_params(fe, 1); - - /* set input */ - if (state->config->set_pll_input) - state->config->set_pll_input(buf+1, 1); break; case VSB_8: /* Set non-punctured clock for VSB */ if (state->config->set_ts_params) state->config->set_ts_params(fe, 0); - - /* set input */ - if (state->config->set_pll_input) - state->config->set_pll_input(buf+1, 0); break; default: return -EINVAL; break; } - /* write frequency information */ - nxt200x_writetuner(state, buf); + if (fe->ops.tuner_ops.calc_regs) { + /* get tuning information */ + fe->ops.tuner_ops.calc_regs(fe, p, buf, 5); + + /* write frequency information */ + nxt200x_writetuner(state, buf); + } /* reset the agc now that tuning has been completed */ nxt200x_agc_reset(state); diff --git a/drivers/media/dvb/frontends/nxt200x.h b/drivers/media/dvb/frontends/nxt200x.h index 28bc559..bb0ef58 100644 --- a/drivers/media/dvb/frontends/nxt200x.h +++ b/drivers/media/dvb/frontends/nxt200x.h @@ -38,9 +38,6 @@ struct nxt200x_config /* the demodulator's i2c address */ u8 demod_address; - /* used to set pll input */ - int (*set_pll_input)(u8* buf, int input); - /* need to set device param for start_dma */ int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured); }; diff --git a/drivers/media/dvb/frontends/or51132.c b/drivers/media/dvb/frontends/or51132.c index 4e0aca7..3cc8b44 100644 --- a/drivers/media/dvb/frontends/or51132.c +++ b/drivers/media/dvb/frontends/or51132.c @@ -45,7 +45,6 @@ #include "dvb_math.h" #include "dvb_frontend.h" -#include "dvb-pll.h" #include "or51132.h" static int debug; diff --git a/drivers/media/dvb/frontends/or51211.c b/drivers/media/dvb/frontends/or51211.c index 048d7cf..f46d5a4 100644 --- a/drivers/media/dvb/frontends/or51211.c +++ b/drivers/media/dvb/frontends/or51211.c @@ -223,38 +223,13 @@ static int or51211_set_parameters(struct dvb_frontend* fe, struct dvb_frontend_parameters *param) { struct or51211_state* state = fe->demodulator_priv; - u32 freq = 0; - u16 tunerfreq = 0; - u8 buf[4]; /* Change only if we are actually changing the channel */ if (state->current_frequency != param->frequency) { - freq = 44000 + (param->frequency/1000); - tunerfreq = freq * 16/1000; - - dprintk("set_parameters frequency = %d (tunerfreq = %d)\n", - param->frequency,tunerfreq); - - buf[0] = (tunerfreq >> 8) & 0x7F; - buf[1] = (tunerfreq & 0xFF); - buf[2] = 0x8E; - - if (param->frequency < 157250000) { - buf[3] = 0xA0; - dprintk("set_parameters VHF low range\n"); - } else if (param->frequency < 454000000) { - buf[3] = 0x90; - dprintk("set_parameters VHF high range\n"); - } else { - buf[3] = 0x30; - dprintk("set_parameters UHF range\n"); + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe, param); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); } - dprintk("set_parameters tuner bytes: 0x%02x 0x%02x " - "0x%02x 0x%02x\n",buf[0],buf[1],buf[2],buf[3]); - - if (i2c_writebytes(state,0xC2>>1,buf,4)) - printk(KERN_WARNING "or51211:set_parameters error " - "writing to tuner\n"); /* Set to ATSC mode */ or51211_setmode(fe,0); diff --git a/drivers/media/dvb/frontends/stv0299.c b/drivers/media/dvb/frontends/stv0299.c index 18768d2..6c60730 100644 --- a/drivers/media/dvb/frontends/stv0299.c +++ b/drivers/media/dvb/frontends/stv0299.c @@ -249,7 +249,7 @@ static int stv0299_get_symbolrate (struct stv0299_state* state) dprintk ("%s\n", __FUNCTION__); stv0299_readregs (state, 0x1f, sfr, 3); - stv0299_readregs (state, 0x1a, &rtf, 1); + stv0299_readregs (state, 0x1a, (u8 *)&rtf, 1); srate = (sfr[0] << 8) | sfr[1]; srate *= Mclk; diff --git a/drivers/media/dvb/frontends/tda10023.c b/drivers/media/dvb/frontends/tda10023.c index da796e7..4bb06f9 100644 --- a/drivers/media/dvb/frontends/tda10023.c +++ b/drivers/media/dvb/frontends/tda10023.c @@ -478,7 +478,7 @@ struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config, state->i2c = i2c; memcpy(&state->frontend.ops, &tda10023_ops, sizeof(struct dvb_frontend_ops)); state->pwm = pwm; - for (i=0; i < sizeof(tda10023_inittab)/sizeof(*tda10023_inittab);i+=3) { + for (i=0; i < ARRAY_SIZE(tda10023_inittab);i+=3) { if (tda10023_inittab[i] == 0x00) { state->reg0 = tda10023_inittab[i+2]; break; diff --git a/drivers/media/dvb/pluto2/Makefile b/drivers/media/dvb/pluto2/Makefile index ce6a9aa..7ac1287 100644 --- a/drivers/media/dvb/pluto2/Makefile +++ b/drivers/media/dvb/pluto2/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_DVB_PLUTO2) += pluto2.o -EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig index 7751628..6d53289 100644 --- a/drivers/media/dvb/ttpci/Kconfig +++ b/drivers/media/dvb/ttpci/Kconfig @@ -108,7 +108,7 @@ config DVB_BUDGET_AV tristate "Budget cards with analog video inputs" depends on DVB_CORE && PCI && I2C && VIDEO_V4L1 select VIDEO_SAA7146_VV - select DVB_PLL + select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_TDA1004X if !DVB_FE_CUSTOMISE select DVB_TDA10021 if !DVB_FE_CUSTOMISE diff --git a/drivers/media/dvb/ttpci/Makefile b/drivers/media/dvb/ttpci/Makefile index aa85ecd..2c11452 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/ -Idrivers/media/dvb/frontends/ +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 ef1108c..2cee9e3 100644 --- a/drivers/media/dvb/ttpci/av7110.c +++ b/drivers/media/dvb/ttpci/av7110.c @@ -137,6 +137,15 @@ static void init_av7110_av(struct av7110 *av7110) if (ret < 0) printk("dvb-ttpci:cannot set internal volume to maximum:%d\n",ret); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetMonitorType, + 1, (u16) av7110->display_ar); + if (ret < 0) + printk("dvb-ttpci: unable to set aspect ratio\n"); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, + 1, av7110->display_panscan); + if (ret < 0) + printk("dvb-ttpci: unable to set pan scan\n"); + ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetWSSConfig, 2, 2, wss_cfg_4_3); if (ret < 0) printk("dvb-ttpci: unable to configure 4:3 wss\n"); @@ -2639,12 +2648,12 @@ static int __devinit av7110_attach(struct saa7146_dev* dev, av7110->mixer.volume_left = volume; av7110->mixer.volume_right = volume; - init_av7110_av(av7110); - ret = av7110_register(av7110); if (ret < 0) goto err_arm_thread_stop_10; + init_av7110_av(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... */ diff --git a/drivers/media/dvb/ttpci/av7110.h b/drivers/media/dvb/ttpci/av7110.h index 115002b..0cb4395 100644 --- a/drivers/media/dvb/ttpci/av7110.h +++ b/drivers/media/dvb/ttpci/av7110.h @@ -194,6 +194,7 @@ struct av7110 { int video_blank; struct video_status videostate; + u16 display_panscan; int display_ar; int trickmode; #define TRICK_NONE 0 diff --git a/drivers/media/dvb/ttpci/av7110_av.c b/drivers/media/dvb/ttpci/av7110_av.c index 58678c0..d75e7e4 100644 --- a/drivers/media/dvb/ttpci/av7110_av.c +++ b/drivers/media/dvb/ttpci/av7110_av.c @@ -391,7 +391,7 @@ static int get_video_format(struct av7110 *av7110, u8 *buf, int count) ****************************************************************************/ static inline long aux_ring_buffer_write(struct dvb_ringbuffer *rbuf, - const char *buf, unsigned long count) + const u8 *buf, unsigned long count) { unsigned long todo = count; int free; @@ -436,7 +436,7 @@ static void play_audio_cb(u8 *buf, int count, void *priv) #define FREE_COND (dvb_ringbuffer_free(&av7110->avout) >= 20 * 1024 && \ dvb_ringbuffer_free(&av7110->aout) >= 20 * 1024) -static ssize_t dvb_play(struct av7110 *av7110, const u8 __user *buf, +static ssize_t dvb_play(struct av7110 *av7110, const char __user *buf, unsigned long count, int nonblock, int type) { unsigned long todo = count, n; @@ -499,7 +499,7 @@ static ssize_t dvb_play_kernel(struct av7110 *av7110, const u8 *buf, return count - todo; } -static ssize_t dvb_aplay(struct av7110 *av7110, const u8 __user *buf, +static ssize_t dvb_aplay(struct av7110 *av7110, const char __user *buf, unsigned long count, int nonblock, int type) { unsigned long todo = count, n; @@ -959,7 +959,7 @@ static u8 iframe_header[] = { 0x00, 0x00, 0x01, 0xe0, 0x00, 0x00, 0x80, 0x00, 0x #define MIN_IFRAME 400000 -static int play_iframe(struct av7110 *av7110, u8 __user *buf, unsigned int len, int nonblock) +static int play_iframe(struct av7110 *av7110, char __user *buf, unsigned int len, int nonblock) { int i, n; @@ -1082,19 +1082,18 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file, case VIDEO_SET_DISPLAY_FORMAT: { video_displayformat_t format = (video_displayformat_t) arg; - u16 val = 0; switch (format) { case VIDEO_PAN_SCAN: - val = VID_PAN_SCAN_PREF; + av7110->display_panscan = VID_PAN_SCAN_PREF; break; case VIDEO_LETTER_BOX: - val = VID_VC_AND_PS_PREF; + av7110->display_panscan = VID_VC_AND_PS_PREF; break; case VIDEO_CENTER_CUT_OUT: - val = VID_CENTRE_CUT_PREF; + av7110->display_panscan = VID_CENTRE_CUT_PREF; break; default: @@ -1104,7 +1103,7 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file, break; av7110->videostate.display_format = format; ret = av7110_fw_cmd(av7110, COMTYPE_ENCODER, SetPanScanType, - 1, (u16) val); + 1, av7110->display_panscan); break; } @@ -1466,8 +1465,9 @@ int av7110_av_register(struct av7110 *av7110) av7110->videostate.play_state = VIDEO_STOPPED; av7110->videostate.stream_source = VIDEO_SOURCE_DEMUX; av7110->videostate.video_format = VIDEO_FORMAT_4_3; - av7110->videostate.display_format = VIDEO_CENTER_CUT_OUT; + av7110->videostate.display_format = VIDEO_LETTER_BOX; av7110->display_ar = VIDEO_FORMAT_4_3; + av7110->display_panscan = VID_VC_AND_PS_PREF; init_waitqueue_head(&av7110->video_events.wait_queue); spin_lock_init(&av7110->video_events.lock); diff --git a/drivers/media/dvb/ttpci/av7110_ca.c b/drivers/media/dvb/ttpci/av7110_ca.c index e1c1294..c58e3fc 100644 --- a/drivers/media/dvb/ttpci/av7110_ca.c +++ b/drivers/media/dvb/ttpci/av7110_ca.c @@ -151,7 +151,7 @@ static ssize_t ci_ll_write(struct dvb_ringbuffer *cibuf, struct file *file, { int free; int non_blocking = file->f_flags & O_NONBLOCK; - char *page = (char *)__get_free_page(GFP_USER); + u8 *page = (u8 *)__get_free_page(GFP_USER); int res; if (!page) @@ -208,7 +208,7 @@ static ssize_t ci_ll_read(struct dvb_ringbuffer *cibuf, struct file *file, return -EINVAL; DVB_RINGBUFFER_SKIP(cibuf, 2); - return dvb_ringbuffer_read(cibuf, buf, len, 1); + return dvb_ringbuffer_read(cibuf, (u8 *)buf, len, 1); } static int dvb_ca_open(struct inode *inode, struct file *file) diff --git a/drivers/media/dvb/ttpci/av7110_hw.c b/drivers/media/dvb/ttpci/av7110_hw.c index 70aee4e..515e823 100644 --- a/drivers/media/dvb/ttpci/av7110_hw.c +++ b/drivers/media/dvb/ttpci/av7110_hw.c @@ -158,7 +158,7 @@ static int load_dram(struct av7110 *av7110, u32 *data, int len) } dprintk(4, "writing DRAM block %d\n", i); mwdebi(av7110, DEBISWAB, bootblock, - ((char*)data) + i * AV7110_BOOT_MAX_SIZE, AV7110_BOOT_MAX_SIZE); + ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, AV7110_BOOT_MAX_SIZE); bootblock ^= 0x1400; iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), 4); iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, AV7110_BOOT_MAX_SIZE, 2); @@ -173,10 +173,10 @@ static int load_dram(struct av7110 *av7110, u32 *data, int len) } if (rest > 4) mwdebi(av7110, DEBISWAB, bootblock, - ((char*)data) + i * AV7110_BOOT_MAX_SIZE, rest); + ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE, rest); else mwdebi(av7110, DEBISWAB, bootblock, - ((char*)data) + i * AV7110_BOOT_MAX_SIZE - 4, rest + 4); + ((u8 *)data) + i * AV7110_BOOT_MAX_SIZE - 4, rest + 4); iwdebi(av7110, DEBISWAB, AV7110_BOOT_BASE, swab32(base), 4); iwdebi(av7110, DEBINOSWAP, AV7110_BOOT_SIZE, rest, 2); @@ -751,7 +751,7 @@ static int FlushText(struct av7110 *av7110) return 0; } -static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, u8* buf) +static int WriteText(struct av7110 *av7110, u8 win, u16 x, u16 y, char *buf) { int i, ret; unsigned long start; diff --git a/drivers/media/dvb/ttpci/av7110_hw.h b/drivers/media/dvb/ttpci/av7110_hw.h index 673d9b3..74d940f 100644 --- a/drivers/media/dvb/ttpci/av7110_hw.h +++ b/drivers/media/dvb/ttpci/av7110_hw.h @@ -393,7 +393,7 @@ static inline void iwdebi(struct av7110 *av7110, u32 config, int addr, u32 val, } /* buffer writes */ -static inline void mwdebi(struct av7110 *av7110, u32 config, int addr, char *val, int count) +static inline void mwdebi(struct av7110 *av7110, u32 config, int addr, u8 *val, int count) { memcpy(av7110->debi_virt, val, count); av7110_debiwrite(av7110, config, addr, 0, count); diff --git a/drivers/media/dvb/ttpci/av7110_ir.c b/drivers/media/dvb/ttpci/av7110_ir.c index a97f166..6322800 100644 --- a/drivers/media/dvb/ttpci/av7110_ir.c +++ b/drivers/media/dvb/ttpci/av7110_ir.c @@ -356,7 +356,7 @@ int __devinit av7110_ir_init(struct av7110 *av7110) input_dev->id.vendor = av7110->dev->pci->vendor; input_dev->id.product = av7110->dev->pci->device; } - input_dev->cdev.dev = &av7110->dev->pci->dev; + input_dev->dev.parent = &av7110->dev->pci->dev; /* initial keymap */ memcpy(av7110->ir.key_map, default_key_map, sizeof av7110->ir.key_map); input_register_keys(&av7110->ir); diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c index fcd9994..87afaeb 100644 --- a/drivers/media/dvb/ttpci/av7110_v4l.c +++ b/drivers/media/dvb/ttpci/av7110_v4l.c @@ -333,7 +333,7 @@ static int av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg) return -EINVAL; memset(t, 0, sizeof(*t)); - strcpy(t->name, "Television"); + strcpy((char *)t->name, "Television"); t->type = V4L2_TUNER_ANALOG_TV; t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c index 0e817d6..0aee7a1 100644 --- a/drivers/media/dvb/ttpci/budget-av.c +++ b/drivers/media/dvb/ttpci/budget-av.c @@ -828,29 +828,6 @@ static u8 philips_sd1878_inittab[] = { 0xff, 0xff }; -static int philips_sd1878_tda8261_tuner_set_params(struct dvb_frontend *fe, - struct dvb_frontend_parameters *params) -{ - u8 buf[4]; - int rc; - struct i2c_msg tuner_msg = {.addr=0x60,.flags=0,.buf=buf,.len=sizeof(buf)}; - struct budget *budget = (struct budget *) fe->dvb->priv; - - if((params->frequency < 950000) || (params->frequency > 2150000)) - return -EINVAL; - - rc=dvb_pll_configure(&dvb_pll_philips_sd1878_tda8261, buf, - params->frequency, 0); - if(rc < 0) return rc; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if(i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) - return -EIO; - - return 0; -} - static int philips_sd1878_ci_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio) { @@ -921,6 +898,7 @@ static u8 read_pwm(struct budget_av *budget_av) #define SUBID_DVBS_TV_STAR 0x0014 #define SUBID_DVBS_TV_STAR_CI 0x0016 #define SUBID_DVBS_EASYWATCH_1 0x001a +#define SUBID_DVBS_EASYWATCH_2 0x001b #define SUBID_DVBS_EASYWATCH 0x001e #define SUBID_DVBC_EASYWATCH 0x002a @@ -982,10 +960,13 @@ static void frontend_init(struct budget_av *budget_av) case SUBID_DVBS_TV_STAR_CI: case SUBID_DVBS_CYNERGY1200N: case SUBID_DVBS_EASYWATCH: + case SUBID_DVBS_EASYWATCH_2: fe = dvb_attach(stv0299_attach, &philips_sd1878_config, &budget_av->budget.i2c_adap); if (fe) { - fe->ops.tuner_ops.set_params = philips_sd1878_tda8261_tuner_set_params; + dvb_attach(dvb_pll_attach, fe, 0x60, + &budget_av->budget.i2c_adap, + DVB_PLL_PHILIPS_SD1878_TDA8261); } break; @@ -1264,6 +1245,7 @@ MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BUDGET_KNC1T); MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR); MAKE_BUDGET_INFO(satewpls, "Satelco EasyWatch DVB-S light", BUDGET_TVSTAR); MAKE_BUDGET_INFO(satewpls1, "Satelco EasyWatch DVB-S light", BUDGET_KNC1S); +MAKE_BUDGET_INFO(satewps, "Satelco EasyWatch DVB-S", BUDGET_KNC1S); MAKE_BUDGET_INFO(satewplc, "Satelco EasyWatch DVB-C", BUDGET_KNC1CP); MAKE_BUDGET_INFO(satewcmk3, "Satelco EasyWatch DVB-C MK3", BUDGET_KNC1C_MK3); MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP); @@ -1287,6 +1269,7 @@ static struct pci_device_id pci_tbl[] = { MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016), MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e), MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a), + MAKE_EXTENSION_PCI(satewps, 0x1894, 0x001b), MAKE_EXTENSION_PCI(satewplc, 0x1894, 0x002a), MAKE_EXTENSION_PCI(satewcmk3, 0x1894, 0x002c), MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020), diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index 9d42f88..873c3ba 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -206,7 +206,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci) input_dev->id.vendor = saa->pci->vendor; input_dev->id.product = saa->pci->device; } - input_dev->cdev.dev = &saa->pci->dev; + input_dev->dev.parent = &saa->pci->dev; /* Select keymap and address */ switch (budget_ci->budget.dev->pci->subsystem_device) { diff --git a/drivers/media/dvb/ttusb-budget/Makefile b/drivers/media/dvb/ttusb-budget/Makefile index 6ab97f6..fbe2b95 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/ -Idrivers/media/dvb/frontends +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends diff --git a/drivers/media/dvb/ttusb-dec/Makefile b/drivers/media/dvb/ttusb-dec/Makefile index b41bf1f..2d70a82 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 ttusbdecfe.o -EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 194b102..f8bf9fe 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -324,8 +324,8 @@ config RADIO_ZOLTRIX_PORT Enter the I/O port of your Zoltrix radio card. config USB_DSBR - tristate "D-Link USB FM radio support (EXPERIMENTAL)" - depends on USB && VIDEO_V4L2 && EXPERIMENTAL + tristate "D-Link/GemTek USB FM radio support" + depends on USB && VIDEO_V4L2 ---help--- Say Y here if you want to connect this type of radio to your computer's USB port. Note that the audio is not digital, and diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c index 5adc27c..ce940b1 100644 --- a/drivers/media/radio/radio-aimslab.c +++ b/drivers/media/radio/radio-aimslab.c @@ -392,7 +392,6 @@ static struct video_device rtrack_radio= .owner = THIS_MODULE, .name = "RadioTrack radio", .type = VID_TYPE_TUNER, - .hardware = 0, .fops = &rtrack_fops, .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, diff --git a/drivers/media/radio/radio-aztech.c b/drivers/media/radio/radio-aztech.c index 9f1adda..9b1f7a9 100644 --- a/drivers/media/radio/radio-aztech.c +++ b/drivers/media/radio/radio-aztech.c @@ -355,7 +355,6 @@ static struct video_device aztech_radio= .owner = THIS_MODULE, .name = "Aztech radio", .type = VID_TYPE_TUNER, - .hardware = 0, .fops = &aztech_fops, .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, diff --git a/drivers/media/radio/radio-gemtek-pci.c b/drivers/media/radio/radio-gemtek-pci.c index 5e6f17d..4db05b2 100644 --- a/drivers/media/radio/radio-gemtek-pci.c +++ b/drivers/media/radio/radio-gemtek-pci.c @@ -377,7 +377,6 @@ static struct video_device vdev_template = { .owner = THIS_MODULE, .name = "Gemtek PCI Radio", .type = VID_TYPE_TUNER, - .hardware = 0, .fops = &gemtek_pci_fops, .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c index b04b6a7..eab8c80 100644 --- a/drivers/media/radio/radio-gemtek.c +++ b/drivers/media/radio/radio-gemtek.c @@ -330,7 +330,6 @@ static struct video_device gemtek_radio= .owner = THIS_MODULE, .name = "GemTek radio", .type = VID_TYPE_TUNER, - .hardware = 0, .fops = &gemtek_fops, .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c index 9b493b3..82aedfc 100644 --- a/drivers/media/radio/radio-rtrack2.c +++ b/drivers/media/radio/radio-rtrack2.c @@ -297,7 +297,6 @@ static struct video_device rtrack2_radio= .owner = THIS_MODULE, .name = "RadioTrack II radio", .type = VID_TYPE_TUNER, - .hardware = 0, .fops = &rtrack2_fops, .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c index dc33f19..3951653 100644 --- a/drivers/media/radio/radio-sf16fmi.c +++ b/drivers/media/radio/radio-sf16fmi.c @@ -297,7 +297,6 @@ static struct video_device fmi_radio= .owner = THIS_MODULE, .name = "SF16FMx radio", .type = VID_TYPE_TUNER, - .hardware = 0, .fops = &fmi_fops, .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c index e6c125d..c432c44 100644 --- a/drivers/media/radio/radio-sf16fmr2.c +++ b/drivers/media/radio/radio-sf16fmr2.c @@ -442,7 +442,6 @@ static struct video_device fmr2_radio= .owner = THIS_MODULE, .name = "SF16FMR2 radio", . type = VID_TYPE_TUNER, - .hardware = 0, .fops = &fmr2_fops, .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c index e43acfd..7e1911c 100644 --- a/drivers/media/radio/radio-terratec.c +++ b/drivers/media/radio/radio-terratec.c @@ -369,7 +369,6 @@ static struct video_device terratec_radio= .owner = THIS_MODULE, .name = "TerraTec ActiveRadio", .type = VID_TYPE_TUNER, - .hardware = 0, .fops = &terratec_fops, .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, diff --git a/drivers/media/radio/radio-trust.c b/drivers/media/radio/radio-trust.c index c27c629..c11981f 100644 --- a/drivers/media/radio/radio-trust.c +++ b/drivers/media/radio/radio-trust.c @@ -349,7 +349,6 @@ static struct video_device trust_radio= .owner = THIS_MODULE, .name = "Trust FM Radio", .type = VID_TYPE_TUNER, - .hardware = 0, .fops = &trust_fops, .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, diff --git a/drivers/media/radio/radio-typhoon.c b/drivers/media/radio/radio-typhoon.c index 8ff5a23..1366326 100644 --- a/drivers/media/radio/radio-typhoon.c +++ b/drivers/media/radio/radio-typhoon.c @@ -349,7 +349,6 @@ static struct video_device typhoon_radio = .owner = THIS_MODULE, .name = "Typhoon Radio", .type = VID_TYPE_TUNER, - .hardware = 0, .fops = &typhoon_fops, .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = vidioc_g_tuner, diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 4d45a40..9dcbffd 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -489,6 +489,15 @@ config TUNER_3036 Say Y here to include support for Philips SAB3036 compatible tuners. If in doubt, say N. +config TUNER_TEA5761 + bool "TEA 5761 radio tuner (EXPERIMENTAL)" + depends on EXPERIMENTAL + depends on I2C + select VIDEO_TUNER + help + Say Y here to include support for Philips TEA5761 radio tuner. + If in doubt, say N. + config VIDEO_VINO tristate "SGI Vino Video For Linux (EXPERIMENTAL)" depends on I2C && SGI_IP22 && EXPERIMENTAL && VIDEO_V4L2 diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 9c2de50..10b4d44 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -7,6 +7,8 @@ zr36067-objs := zoran_procfs.o zoran_device.o \ tuner-objs := tuner-core.o tuner-types.o tuner-simple.o \ mt20xx.o tda8290.o tea5767.o tda9887.o +tuner-$(CONFIG_TUNER_TEA5761) += tea5761.o + msp3400-objs := msp3400-driver.o msp3400-kthreads.o obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o compat_ioctl32.o @@ -16,7 +18,7 @@ ifeq ($(CONFIG_VIDEO_V4L1_COMPAT),y) endif obj-$(CONFIG_VIDEO_BT848) += bt8xx/ -obj-$(CONFIG_VIDEO_BT848) += ir-kbd-i2c.o +obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o obj-$(CONFIG_VIDEO_TDA9875) += tda9875.o @@ -59,7 +61,7 @@ obj-$(CONFIG_VIDEO_CPIA) += cpia.o obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o obj-$(CONFIG_VIDEO_MEYE) += meye.o -obj-$(CONFIG_VIDEO_SAA7134) += ir-kbd-i2c.o saa7134/ +obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ obj-$(CONFIG_VIDEO_CX88) += cx88/ obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ diff --git a/drivers/media/video/adv7170.c b/drivers/media/video/adv7170.c index 823cd6c..cbab53f 100644 --- a/drivers/media/video/adv7170.c +++ b/drivers/media/video/adv7170.c @@ -38,23 +38,23 @@ #include <linux/slab.h> #include <linux/mm.h> #include <linux/signal.h> +#include <linux/types.h> +#include <linux/i2c.h> #include <asm/io.h> #include <asm/pgtable.h> #include <asm/page.h> -#include <linux/types.h> +#include <asm/uaccess.h> #include <linux/videodev.h> -#include <asm/uaccess.h> +#include <linux/video_encoder.h> MODULE_DESCRIPTION("Analog Devices ADV7170 video encoder driver"); MODULE_AUTHOR("Maxim Yevtyushkin"); MODULE_LICENSE("GPL"); -#include <linux/i2c.h> #define I2C_NAME(x) (x)->name -#include <linux/video_encoder.h> static int debug = 0; module_param(debug, int, 0); diff --git a/drivers/media/video/adv7175.c b/drivers/media/video/adv7175.c index 05c7820..0d0c554 100644 --- a/drivers/media/video/adv7175.c +++ b/drivers/media/video/adv7175.c @@ -34,23 +34,23 @@ #include <linux/slab.h> #include <linux/mm.h> #include <linux/signal.h> +#include <linux/types.h> +#include <linux/i2c.h> #include <asm/io.h> #include <asm/pgtable.h> #include <asm/page.h> -#include <linux/types.h> +#include <asm/uaccess.h> #include <linux/videodev.h> -#include <asm/uaccess.h> +#include <linux/video_encoder.h> MODULE_DESCRIPTION("Analog Devices ADV7175 video encoder driver"); MODULE_AUTHOR("Dave Perks"); MODULE_LICENSE("GPL"); -#include <linux/i2c.h> #define I2C_NAME(s) (s)->name -#include <linux/video_encoder.h> static int debug = 0; module_param(debug, int, 0); diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c index 59a4360..12d1b92 100644 --- a/drivers/media/video/bt819.c +++ b/drivers/media/video/bt819.c @@ -38,23 +38,24 @@ #include <linux/slab.h> #include <linux/mm.h> #include <linux/signal.h> +#include <linux/types.h> +#include <linux/i2c.h> #include <asm/io.h> #include <asm/pgtable.h> #include <asm/page.h> -#include <linux/types.h> +#include <asm/uaccess.h> #include <linux/videodev.h> -#include <asm/uaccess.h> +#include <linux/video_decoder.h> + MODULE_DESCRIPTION("Brooktree-819 video decoder driver"); MODULE_AUTHOR("Mike Bernson & Dave Perks"); MODULE_LICENSE("GPL"); -#include <linux/i2c.h> #define I2C_NAME(s) (s)->name -#include <linux/video_decoder.h> static int debug = 0; module_param(debug, int, 0); diff --git a/drivers/media/video/bt856.c b/drivers/media/video/bt856.c index 853b1a3..e1028a7 100644 --- a/drivers/media/video/bt856.c +++ b/drivers/media/video/bt856.c @@ -38,23 +38,23 @@ #include <linux/slab.h> #include <linux/mm.h> #include <linux/signal.h> +#include <linux/types.h> +#include <linux/i2c.h> +#include <linux/video_encoder.h> #include <asm/io.h> #include <asm/pgtable.h> #include <asm/page.h> -#include <linux/types.h> +#include <asm/uaccess.h> #include <linux/videodev.h> -#include <asm/uaccess.h> MODULE_DESCRIPTION("Brooktree-856A video encoder driver"); MODULE_AUTHOR("Mike Bernson & Dave Perks"); MODULE_LICENSE("GPL"); -#include <linux/i2c.h> #define I2C_NAME(s) (s)->name -#include <linux/video_encoder.h> static int debug = 0; module_param(debug, int, 0); diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index 6b31e50..2aea09c 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -178,8 +178,8 @@ static struct CARD { /* this seems to happen as well ... */ { 0xff1211bd, BTTV_BOARD_PINNACLE, "Pinnacle PCTV" }, - { 0x3000121a, BTTV_BOARD_VOODOOTV_FM, "3Dfx VoodooTV FM/ VoodooTV 200" }, - { 0x263710b4, BTTV_BOARD_VOODOOTV_FM, "3Dfx VoodooTV FM/ VoodooTV 200" }, + { 0x3000121a, BTTV_BOARD_VOODOOTV_200, "3Dfx VoodooTV 200" }, + { 0x263710b4, BTTV_BOARD_VOODOOTV_FM, "3Dfx VoodooTV FM" }, { 0x3060121a, BTTV_BOARD_STB2, "3Dfx VoodooTV 100/ STB OEM" }, { 0x3000144f, BTTV_BOARD_MAGICTVIEW063, "(Askey Magic/others) TView99 CPH06x" }, @@ -313,6 +313,7 @@ static struct CARD { { 0xdb1118ac, BTTV_BOARD_DVICO_DVBT_LITE, "Ultraview DVB-T Lite" }, { 0xd50018ac, BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE, "DViCO FusionHDTV 5 Lite" }, { 0x00261822, BTTV_BOARD_TWINHAN_DST, "DNTV Live! Mini "}, + { 0xd200dbc0, BTTV_BOARD_DVICO_FUSIONHDTV_2, "DViCO FusionHDTV 2" }, { 0, -1, NULL } }; @@ -329,7 +330,7 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .muxsel = { 2, 3, 1, 0 }, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -344,7 +345,7 @@ struct tvcard bttv_tvcards[] = { .gpiomux = { 2, 0, 0, 0 }, .gpiomute = 10, .needs_tvaudio = 1, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -359,7 +360,7 @@ struct tvcard bttv_tvcards[] = { .gpiomux = { 0, 1, 2, 3 }, .gpiomute = 4, .needs_tvaudio = 1, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -387,13 +388,13 @@ struct tvcard bttv_tvcards[] = { .name = "Intel Create and Share PCI/ Smart Video Recorder III", .video_inputs = 4, .audio_inputs = 0, - .tuner = -1, + .tuner = UNSET, .svhs = 2, .gpiomask = 0, .muxsel = { 2, 3, 1, 1 }, .gpiomux = { 0 }, .needs_tvaudio = 0, - .tuner_type = 4, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -408,7 +409,7 @@ struct tvcard bttv_tvcards[] = { .gpiomux = { 0, 1, 0, 1 }, .gpiomute = 3, .needs_tvaudio = 1, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -423,7 +424,7 @@ struct tvcard bttv_tvcards[] = { .gpiomux = { 0x0c, 0x04, 0x08, 0x04 }, /* 0x04 for some cards ?? */ .needs_tvaudio = 1, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .audio_hook = avermedia_tvphone_audio, @@ -433,13 +434,13 @@ struct tvcard bttv_tvcards[] = { .name = "MATRIX-Vision MV-Delta", .video_inputs = 5, .audio_inputs = 1, - .tuner = -1, + .tuner = UNSET, .svhs = 3, .gpiomask = 0, .muxsel = { 2, 3, 1, 0, 0 }, .gpiomux = { 0 }, .needs_tvaudio = 1, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -457,7 +458,7 @@ struct tvcard bttv_tvcards[] = { .gpiomute = 0xc00, .needs_tvaudio = 1, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -488,7 +489,7 @@ struct tvcard bttv_tvcards[] = { .gpiomute = 4, .needs_tvaudio = 1, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -503,7 +504,7 @@ struct tvcard bttv_tvcards[] = { .gpiomux = { 0x20001,0x10001, 0, 0 }, .gpiomute = 10, .needs_tvaudio = 1, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -519,7 +520,7 @@ struct tvcard bttv_tvcards[] = { .muxsel = { 2, 3, 1, 1 }, .gpiomux = { 13, 14, 11, 7 }, .needs_tvaudio = 1, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -553,7 +554,7 @@ struct tvcard bttv_tvcards[] = { .gpiomute = 4, .needs_tvaudio = 1, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -568,7 +569,7 @@ struct tvcard bttv_tvcards[] = { .gpiomux = { 0, 0, 1, 0 }, .gpiomute = 10, .needs_tvaudio = 1, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -587,7 +588,7 @@ struct tvcard bttv_tvcards[] = { .gpiomute = 0x002000, .needs_tvaudio = 1, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, }, [BTTV_BOARD_WINVIEW_601] = { .name = "Leadtek WinView 601", @@ -600,7 +601,7 @@ struct tvcard bttv_tvcards[] = { .gpiomux = { 0x4fa007,0xcfa007,0xcfa007,0xcfa007 }, .gpiomute = 0xcfa007, .needs_tvaudio = 1, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .audio_hook = winview_audio, @@ -616,7 +617,7 @@ struct tvcard bttv_tvcards[] = { .muxsel = { 2, 3, 1, 1 }, .gpiomux = { 1, 0, 0, 0 }, .needs_tvaudio = 1, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -624,13 +625,13 @@ struct tvcard bttv_tvcards[] = { .name = "Lifeview FlyVideo II EZ /FlyKit LR38 Bt848 (capture only)", .video_inputs = 4, .audio_inputs = 1, - .tuner = -1, - .svhs = -1, + .tuner = UNSET, + .svhs = UNSET, .gpiomask = 0x8dff00, .muxsel = { 2, 3, 1, 1 }, .gpiomux = { 0 }, .no_msp34xx = 1, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -643,7 +644,7 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .muxsel = { 2, 3, 1, 1 }, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -674,7 +675,7 @@ struct tvcard bttv_tvcards[] = { .gpiomute = 0xc00, .needs_tvaudio = 1, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -683,7 +684,7 @@ struct tvcard bttv_tvcards[] = { .video_inputs = 3, .audio_inputs = 1, .tuner = 0, - .svhs = -1, + .svhs = UNSET, .gpiomask = 7, .muxsel = { 2, 3, -1 }, .digital_mode = DIGITAL_MODE_CAMERA, @@ -708,7 +709,7 @@ struct tvcard bttv_tvcards[] = { .gpiomute = 0xc00, .needs_tvaudio = 1, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .has_remote = 1, @@ -740,7 +741,7 @@ struct tvcard bttv_tvcards[] = { .gpiomux = { 0, 1, 2, 3 }, .gpiomute = 4, .needs_tvaudio = 1, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -813,13 +814,13 @@ struct tvcard bttv_tvcards[] = { .name = "Imagenation PXC200", .video_inputs = 5, .audio_inputs = 1, - .tuner = -1, + .tuner = UNSET, .svhs = 1, /* was: 4 */ .gpiomask = 0, .muxsel = { 2, 3, 1, 0, 0}, .gpiomux = { 0 }, .needs_tvaudio = 1, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .muxsel_hook = PXC200_muxsel, @@ -836,7 +837,7 @@ struct tvcard bttv_tvcards[] = { .gpiomux = { 0, 0x0800, 0x1000, 0x1000 }, .gpiomute = 0x1800, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -860,13 +861,13 @@ struct tvcard bttv_tvcards[] = { .name = "Intel Create and Share PCI/ Smart Video Recorder III", .video_inputs = 4, .audio_inputs = 0, - .tuner = -1, + .tuner = UNSET, .svhs = 2, .gpiomask = 0, .muxsel = { 2, 3, 1, 1 }, .gpiomux = { 0 }, .needs_tvaudio = 0, - .tuner_type = 4, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -911,7 +912,7 @@ struct tvcard bttv_tvcards[] = { .needs_tvaudio = 0, .pll = PLL_28, .has_radio = 1, - .tuner_type = 5, /* default for now, gpio reads BFFF06 for Pal bg+dk */ + .tuner_type = TUNER_PHILIPS_PAL, /* default for now, gpio reads BFFF06 for Pal bg+dk */ .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .audio_hook = winfast2000_audio, @@ -928,7 +929,7 @@ struct tvcard bttv_tvcards[] = { .gpiomux = { 0, 0x800, 0x1000, 0x1000 }, .gpiomute = 0x1800, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -945,7 +946,7 @@ struct tvcard bttv_tvcards[] = { .gpiomux = { 0, 0x800, 0x1000, 0x1000 }, .gpiomute = 0x1800, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .has_radio = 1, @@ -962,7 +963,7 @@ struct tvcard bttv_tvcards[] = { .gpiomute = 0x29, .no_msp34xx = 1, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -978,7 +979,7 @@ struct tvcard bttv_tvcards[] = { .gpiomute = 0x551c00, .needs_tvaudio = 1, .pll = PLL_28, - .tuner_type = 1, + .tuner_type = TUNER_PHILIPS_PAL_I, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .has_remote = 1, @@ -995,7 +996,7 @@ struct tvcard bttv_tvcards[] = { .gpiomute = 1, .needs_tvaudio = 0, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -1030,7 +1031,7 @@ struct tvcard bttv_tvcards[] = { .gpiomux = { 13, 4, 11, 7 }, .needs_tvaudio = 1, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .has_radio = 1, @@ -1048,7 +1049,7 @@ struct tvcard bttv_tvcards[] = { .needs_tvaudio = 1, .no_msp34xx = 1, .pll = PLL_28, - .tuner_type = 1, + .tuner_type = TUNER_PHILIPS_PAL_I, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -1063,7 +1064,7 @@ struct tvcard bttv_tvcards[] = { .gpiomux = { 0xff9ff6, 0xff9ff6, 0xff1ff7, 0 }, .gpiomute = 0xff3ffc, .no_msp34xx = 1, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -1074,14 +1075,14 @@ struct tvcard bttv_tvcards[] = { .video_inputs = 2, .audio_inputs = 1, .tuner = 0, - .svhs = -1, + .svhs = UNSET, .gpiomask = 3, .muxsel = { 2, 3, 1, 1 }, .gpiomux = { 1, 1, 0, 2 }, .gpiomute = 3, .no_msp34xx = 1, .pll = PLL_NONE, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -1089,14 +1090,14 @@ struct tvcard bttv_tvcards[] = { .name = "MATRIX-Vision MV-Delta 2", .video_inputs = 5, .audio_inputs = 1, - .tuner = -1, + .tuner = UNSET, .svhs = 3, .gpiomask = 0, .muxsel = { 2, 3, 1, 0, 0 }, .gpiomux = { 0 }, .no_msp34xx = 1, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -1112,7 +1113,7 @@ struct tvcard bttv_tvcards[] = { .gpiomute = 0xbcb03f, .no_msp34xx = 1, .pll = PLL_28, - .tuner_type = 21, + .tuner_type = TUNER_TEMIC_4039FR5_NTSC, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -1129,7 +1130,7 @@ struct tvcard bttv_tvcards[] = { .needs_tvaudio = 1, .no_msp34xx = 1, .pll = PLL_35, - .tuner_type = 1, + .tuner_type = TUNER_PHILIPS_PAL_I, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .has_radio = 1, @@ -1148,7 +1149,7 @@ struct tvcard bttv_tvcards[] = { .gpiomute = 1, .needs_tvaudio = 1, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -1206,7 +1207,7 @@ struct tvcard bttv_tvcards[] = { .gpiomux = { 0, 1, 2, 3 }, .gpiomute = 4, .pll = PLL_28, - .tuner_type = -1 /* TUNER_ALPS_TMDH2_NTSC */, + .tuner_type = UNSET /* TUNER_ALPS_TMDH2_NTSC */, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -1234,7 +1235,7 @@ struct tvcard bttv_tvcards[] = { 1= FM stereo Radio from Tuner */ .needs_tvaudio = 0, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -1277,7 +1278,7 @@ struct tvcard bttv_tvcards[] = { 0x0080: Tuner A2 SAP (second audio program = Zweikanalton) 0x0880: Tuner A2 stereo */ .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -1313,7 +1314,7 @@ struct tvcard bttv_tvcards[] = { .gpiomux = { 0, 0x800, 0x1000, 0x1000 }, .gpiomute = 0x1800, .pll = PLL_28, - .tuner_type = 5, + .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -1324,7 +1325,7 @@ struct tvcard bttv_tvcards[] = { .name = "GrandTec 'Grand Video Capture' (Bt848)", .video_inputs = 2, .audio_inputs = 0, - .tuner = -1, + .tuner = UNSET, .svhs = 1, .gpiomask = 0, .muxsel = { 3, 1 }, @@ -1332,7 +1333,7 @@ struct tvcard bttv_tvcards[] = { .needs_tvaudio = 0, .no_msp34xx = 1, .pll = PLL_35, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -1365,7 +1366,7 @@ struct tvcard bttv_tvcards[] = { .gpiomux = { 2, 0, 0, 0 }, .gpiomute = 1, .pll = PLL_28, - .tuner_type = 0, + .tuner_type = TUNER_TEMIC_PAL, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -1377,7 +1378,7 @@ struct tvcard bttv_tvcards[] = { .video_inputs = 2, .audio_inputs = 2, .tuner = 0, - .svhs = -1, + .svhs = UNSET, .gpiomask = 11, .muxsel = { 2, 3, 1, 1 }, .gpiomux = { 2, 0, 0, 1 }, @@ -1392,7 +1393,7 @@ struct tvcard bttv_tvcards[] = { .name = "AG Electronics GMV1", .video_inputs = 2, .audio_inputs = 0, - .tuner = -1, + .tuner = UNSET, .svhs = 1, .gpiomask = 0xF, .muxsel = { 2, 2 }, @@ -1400,7 +1401,7 @@ struct tvcard bttv_tvcards[] = { .no_msp34xx = 1, .needs_tvaudio = 0, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -1447,7 +1448,7 @@ struct tvcard bttv_tvcards[] = { .video_inputs = 2, .audio_inputs = 1, .tuner = 0, - .svhs = -1, + .svhs = UNSET, .gpiomask = 1, .muxsel = { 2, 3, 0, 1 }, .gpiomux = { 0, 0, 1, 0 }, @@ -1476,7 +1477,7 @@ struct tvcard bttv_tvcards[] = { .no_tda9875 = 1, .needs_tvaudio = 1, .pll = PLL_28, - .tuner_type = 5, + .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -1517,13 +1518,35 @@ struct tvcard bttv_tvcards[] = { /* ---- card 0x44 ---------------------------------- */ [BTTV_BOARD_VOODOOTV_FM] = { - .name = "3Dfx VoodooTV FM (Euro), VoodooTV 200 (USA)", + .name = "3Dfx VoodooTV FM (Euro)", + /* try "insmod msp3400 simple=0" if you have + * sound problems with this card. */ + .video_inputs = 4, + .audio_inputs = 1, + .tuner = 0, + .svhs = UNSET, + .gpiomask = 0x4f8a00, + /* 0x100000: 1=MSP enabled (0=disable again) + * 0x010000: Connected to "S0" on tda9880 (0=Pal/BG, 1=NTSC) */ + .gpiomux = {0x947fff, 0x987fff,0x947fff,0x947fff }, + .gpiomute = 0x947fff, + /* tvtuner, radio, external,internal, mute, stereo + * tuner, Composit, SVid, Composit-on-Svid-adapter */ + .muxsel = { 2, 3 ,0 ,1 }, + .tuner_type = TUNER_MT2032, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .pll = PLL_28, + .has_radio = 1, + }, + [BTTV_BOARD_VOODOOTV_200] = { + .name = "VoodooTV 200 (USA)", /* try "insmod msp3400 simple=0" if you have * sound problems with this card. */ .video_inputs = 4, .audio_inputs = 1, .tuner = 0, - .svhs = -1, + .svhs = UNSET, .gpiomask = 0x4f8a00, /* 0x100000: 1=MSP enabled (0=disable again) * 0x010000: Connected to "S0" on tda9880 (0=Pal/BG, 1=NTSC) */ @@ -1543,8 +1566,8 @@ struct tvcard bttv_tvcards[] = { .name = "Active Imaging AIMMS", .video_inputs = 1, .audio_inputs = 0, - .tuner = -1, - .tuner_type = -1, + .tuner = UNSET, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .pll = PLL_28, @@ -1564,7 +1587,7 @@ struct tvcard bttv_tvcards[] = { .gpiomute = 13, .needs_tvaudio = 1, .pll = PLL_28, - .tuner_type = 25, + .tuner_type = TUNER_LG_PAL_I_FM, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .has_remote = 1, @@ -1580,7 +1603,7 @@ struct tvcard bttv_tvcards[] = { .name = "Lifeview FlyVideo 98EZ (capture only) LR51", .video_inputs = 4, .audio_inputs = 0, - .tuner = -1, + .tuner = UNSET, .svhs = 2, .muxsel = { 2, 3, 1, 1 }, /* AV1, AV2, SVHS, CVid adapter on SVHS */ .pll = PLL_28, @@ -1606,7 +1629,7 @@ struct tvcard bttv_tvcards[] = { .no_msp34xx = 1, .no_tda9875 = 1, .pll = PLL_28, - .tuner_type = 5, + .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .audio_hook = pvbt878p9b_audio, /* Note: not all cards have stereo */ @@ -1626,13 +1649,13 @@ struct tvcard bttv_tvcards[] = { .name = "Sensoray 311", .video_inputs = 5, .audio_inputs = 0, - .tuner = -1, + .tuner = UNSET, .svhs = 4, .gpiomask = 0, .muxsel = { 2, 3, 1, 0, 0 }, .gpiomux = { 0 }, .needs_tvaudio = 0, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -1641,15 +1664,15 @@ struct tvcard bttv_tvcards[] = { .name = "RemoteVision MX (RV605)", .video_inputs = 16, .audio_inputs = 0, - .tuner = -1, - .svhs = -1, + .tuner = UNSET, + .svhs = UNSET, .gpiomask = 0x00, .gpiomask2 = 0x07ff, .muxsel = { 0x33, 0x13, 0x23, 0x43, 0xf3, 0x73, 0xe3, 0x03, 0xd3, 0xb3, 0xc3, 0x63, 0x93, 0x53, 0x83, 0xa3 }, .no_msp34xx = 1, .no_tda9875 = 1, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .muxsel_hook = rv605_muxsel, @@ -1693,15 +1716,15 @@ struct tvcard bttv_tvcards[] = { .name = "GrandTec Multi Capture Card (Bt878)", .video_inputs = 4, .audio_inputs = 0, - .tuner = -1, - .svhs = -1, + .tuner = UNSET, + .svhs = UNSET, .gpiomask = 0, .muxsel = { 2, 3, 1, 0 }, .gpiomux = { 0 }, .needs_tvaudio = 0, .no_msp34xx = 1, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -1724,7 +1747,7 @@ struct tvcard bttv_tvcards[] = { .needs_tvaudio = 0, .no_msp34xx = 1, .pll = PLL_28, - .tuner_type = 5, + .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, /* Samsung TCPA9095PC27A (BG+DK), philips compatible, w/FM, stereo and @@ -1744,10 +1767,10 @@ struct tvcard bttv_tvcards[] = { /* Arthur Tetzlaff-Deas, DSP Design Ltd <software@dspdesign.com> */ .name = "DSP Design TCVIDEO", .video_inputs = 4, - .svhs = -1, + .svhs = UNSET, .muxsel = { 2, 3, 1, 0 }, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -1762,7 +1785,7 @@ struct tvcard bttv_tvcards[] = { .muxsel = { 2, 0, 1, 1 }, .needs_tvaudio = 1, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, @@ -1791,11 +1814,11 @@ struct tvcard bttv_tvcards[] = { .name = "Osprey 100/150 (878)", /* 0x1(2|3)-45C6-C1 */ .video_inputs = 4, /* id-inputs-clock */ .audio_inputs = 0, - .tuner = -1, + .tuner = UNSET, .svhs = 3, .muxsel = { 3, 2, 0, 1 }, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .no_msp34xx = 1, @@ -1806,11 +1829,11 @@ struct tvcard bttv_tvcards[] = { .name = "Osprey 100/150 (848)", /* 0x04-54C0-C1 & older boards */ .video_inputs = 3, .audio_inputs = 0, - .tuner = -1, + .tuner = UNSET, .svhs = 2, .muxsel = { 2, 3, 1 }, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .no_msp34xx = 1, @@ -1823,11 +1846,11 @@ struct tvcard bttv_tvcards[] = { .name = "Osprey 101 (848)", /* 0x05-40C0-C1 */ .video_inputs = 2, .audio_inputs = 0, - .tuner = -1, + .tuner = UNSET, .svhs = 1, .muxsel = { 3, 1 }, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .no_msp34xx = 1, @@ -1838,11 +1861,11 @@ struct tvcard bttv_tvcards[] = { .name = "Osprey 101/151", /* 0x1(4|5)-0004-C4 */ .video_inputs = 1, .audio_inputs = 0, - .tuner = -1, - .svhs = -1, + .tuner = UNSET, + .svhs = UNSET, .muxsel = { 0 }, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .no_msp34xx = 1, @@ -1853,11 +1876,11 @@ struct tvcard bttv_tvcards[] = { .name = "Osprey 101/151 w/ svid", /* 0x(16|17|20)-00C4-C1 */ .video_inputs = 2, .audio_inputs = 0, - .tuner = -1, + .tuner = UNSET, .svhs = 1, .muxsel = { 0, 1 }, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .no_msp34xx = 1, @@ -1868,8 +1891,8 @@ struct tvcard bttv_tvcards[] = { .name = "Osprey 200/201/250/251", /* 0x1(8|9|E|F)-0004-C4 */ .video_inputs = 1, .audio_inputs = 1, - .tuner = -1, - .svhs = -1, + .tuner = UNSET, + .svhs = UNSET, .muxsel = { 0 }, .pll = PLL_28, .tuner_type = UNSET, @@ -1885,7 +1908,7 @@ struct tvcard bttv_tvcards[] = { .name = "Osprey 200/250", /* 0x1(A|B)-00C4-C1 */ .video_inputs = 2, .audio_inputs = 1, - .tuner = -1, + .tuner = UNSET, .svhs = 1, .muxsel = { 0, 1 }, .pll = PLL_28, @@ -1900,7 +1923,7 @@ struct tvcard bttv_tvcards[] = { .name = "Osprey 210/220/230", /* 0x1(A|B)-04C0-C1 */ .video_inputs = 2, .audio_inputs = 1, - .tuner = -1, + .tuner = UNSET, .svhs = 1, .muxsel = { 2, 3 }, .pll = PLL_28, @@ -1915,11 +1938,11 @@ struct tvcard bttv_tvcards[] = { .name = "Osprey 500", /* 500 */ .video_inputs = 2, .audio_inputs = 1, - .tuner = -1, + .tuner = UNSET, .svhs = 1, .muxsel = { 2, 3 }, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .no_msp34xx = 1, @@ -1930,9 +1953,9 @@ struct tvcard bttv_tvcards[] = { .name = "Osprey 540", /* 540 */ .video_inputs = 4, .audio_inputs = 1, - .tuner = -1, + .tuner = UNSET, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .no_msp34xx = 1, @@ -1945,7 +1968,7 @@ struct tvcard bttv_tvcards[] = { .name = "Osprey 2000", /* 2000 */ .video_inputs = 2, .audio_inputs = 1, - .tuner = -1, + .tuner = UNSET, .svhs = 1, .muxsel = { 2, 3 }, .pll = PLL_28, @@ -1961,11 +1984,11 @@ struct tvcard bttv_tvcards[] = { .name = "IDS Eagle", .video_inputs = 4, .audio_inputs = 0, - .tuner = -1, - .tuner_type = -1, + .tuner = UNSET, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .svhs = -1, + .svhs = UNSET, .gpiomask = 0, .muxsel = { 0, 1, 2, 3 }, .muxsel_hook = eagle_muxsel, @@ -1978,8 +2001,8 @@ struct tvcard bttv_tvcards[] = { .video_inputs = 2, .audio_inputs = 0, .svhs = 1, - .tuner = -1, - .tuner_type = -1, + .tuner = UNSET, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .no_msp34xx = 1, @@ -2020,13 +2043,13 @@ struct tvcard bttv_tvcards[] = { .video_inputs = 3, .audio_inputs = 1, .tuner = 0, - .svhs = -1, + .svhs = UNSET, .gpiomask = 7, .muxsel = { 2, 3, 1, 1}, .gpiomux = { 0, 1, 2, 3}, .gpiomute = 4, .needs_tvaudio = 1, - .tuner_type = 5, + .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .pll = PLL_28, @@ -2035,7 +2058,7 @@ struct tvcard bttv_tvcards[] = { .name = "Euresys Picolo", .video_inputs = 3, .audio_inputs = 0, - .tuner = -1, + .tuner = UNSET, .svhs = 2, .gpiomask = 0, .no_msp34xx = 1, @@ -2052,8 +2075,8 @@ struct tvcard bttv_tvcards[] = { .name = "ProVideo PV150", /* 0x4f */ .video_inputs = 2, .audio_inputs = 0, - .tuner = -1, - .svhs = -1, + .tuner = UNSET, + .svhs = UNSET, .gpiomask = 0, .muxsel = { 2, 3 }, .gpiomux = { 0 }, @@ -2080,7 +2103,7 @@ struct tvcard bttv_tvcards[] = { .needs_tvaudio = 0, .no_msp34xx = 1, .pll = PLL_28, - .tuner_type = 2, + .tuner_type = TUNER_PHILIPS_NTSC, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .audio_hook = adtvk503_audio, @@ -2098,7 +2121,7 @@ struct tvcard bttv_tvcards[] = { .needs_tvaudio = 1, .no_msp34xx = 1, .pll = PLL_28, - .tuner_type = 5, + .tuner_type = TUNER_PHILIPS_PAL, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, /* Notes: @@ -2121,7 +2144,7 @@ struct tvcard bttv_tvcards[] = { .gpiomask = 0, .no_tda9875 = 1, .no_tda7432 = 1, - .tuner_type = 1, + .tuner_type = TUNER_PHILIPS_PAL_I, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .has_radio = 1, @@ -2138,11 +2161,11 @@ struct tvcard bttv_tvcards[] = { .name = "IVC-200", .video_inputs = 1, .audio_inputs = 0, - .tuner = -1, - .tuner_type = -1, + .tuner = UNSET, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .svhs = -1, + .svhs = UNSET, .gpiomask = 0xdf, .muxsel = { 2 }, .pll = PLL_28, @@ -2151,9 +2174,9 @@ struct tvcard bttv_tvcards[] = { .name = "Grand X-Guard / Trust 814PCI", .video_inputs = 16, .audio_inputs = 0, - .tuner = -1, - .svhs = -1, - .tuner_type = 4, + .tuner = UNSET, + .svhs = UNSET, + .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .gpiomask2 = 0xff, @@ -2169,14 +2192,14 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_NEBULA_DIGITV] = { .name = "Nebula Electronics DigiTV", .video_inputs = 1, - .tuner = -1, - .svhs = -1, + .tuner = UNSET, + .svhs = UNSET, .muxsel = { 2, 3, 1, 0 }, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .has_dvb = 1, @@ -2189,15 +2212,15 @@ struct tvcard bttv_tvcards[] = { .name = "ProVideo PV143", .video_inputs = 4, .audio_inputs = 0, - .tuner = -1, - .svhs = -1, + .tuner = UNSET, + .svhs = UNSET, .gpiomask = 0, .muxsel = { 2, 3, 1, 0 }, .gpiomux = { 0 }, .needs_tvaudio = 0, .no_msp34xx = 1, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -2206,14 +2229,14 @@ struct tvcard bttv_tvcards[] = { .name = "PHYTEC VD-009-X1 MiniDIN (bt878)", .video_inputs = 4, .audio_inputs = 0, - .tuner = -1, /* card has no tuner */ + .tuner = UNSET, /* card has no tuner */ .svhs = 3, .gpiomask = 0x00, .muxsel = { 2, 3, 1, 0 }, .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ .needs_tvaudio = 1, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -2221,14 +2244,14 @@ struct tvcard bttv_tvcards[] = { .name = "PHYTEC VD-009-X1 Combi (bt878)", .video_inputs = 4, .audio_inputs = 0, - .tuner = -1, /* card has no tuner */ + .tuner = UNSET, /* card has no tuner */ .svhs = 3, .gpiomask = 0x00, .muxsel = { 2, 3, 1, 1 }, .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ .needs_tvaudio = 1, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -2238,7 +2261,7 @@ struct tvcard bttv_tvcards[] = { .name = "PHYTEC VD-009 MiniDIN (bt878)", .video_inputs = 10, .audio_inputs = 0, - .tuner = -1, /* card has no tuner */ + .tuner = UNSET, /* card has no tuner */ .svhs = 9, .gpiomask = 0x00, .gpiomask2 = 0x03, /* gpiomask2 defines the bits used to switch audio @@ -2248,7 +2271,7 @@ struct tvcard bttv_tvcards[] = { .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ .needs_tvaudio = 1, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -2256,7 +2279,7 @@ struct tvcard bttv_tvcards[] = { .name = "PHYTEC VD-009 Combi (bt878)", .video_inputs = 10, .audio_inputs = 0, - .tuner = -1, /* card has no tuner */ + .tuner = UNSET, /* card has no tuner */ .svhs = 9, .gpiomask = 0x00, .gpiomask2 = 0x03, /* gpiomask2 defines the bits used to switch audio @@ -2266,7 +2289,7 @@ struct tvcard bttv_tvcards[] = { .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */ .needs_tvaudio = 1, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -2274,11 +2297,11 @@ struct tvcard bttv_tvcards[] = { .name = "IVC-100", .video_inputs = 4, .audio_inputs = 0, - .tuner = -1, - .tuner_type = -1, + .tuner = UNSET, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .svhs = -1, + .svhs = UNSET, .gpiomask = 0xdf, .muxsel = { 2, 3, 1, 0 }, .pll = PLL_28, @@ -2288,11 +2311,11 @@ struct tvcard bttv_tvcards[] = { .name = "IVC-120G", .video_inputs = 16, .audio_inputs = 0, /* card has no audio */ - .tuner = -1, /* card has no tuner */ - .tuner_type = -1, + .tuner = UNSET, /* card has no tuner */ + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .svhs = -1, /* card has no svhs */ + .svhs = UNSET, /* card has no svhs */ .needs_tvaudio = 0, .no_msp34xx = 1, .no_tda9875 = 1, @@ -2333,7 +2356,7 @@ struct tvcard bttv_tvcards[] = { .video_inputs = 3, .audio_inputs = 0, .svhs = 1, - .tuner = -1, + .tuner = UNSET, .muxsel = { 3, 1, 1, 3 }, /* Vid In, SVid In, Vid over SVid in connector */ .no_msp34xx = 1, .no_tda9875 = 1, @@ -2364,9 +2387,9 @@ struct tvcard bttv_tvcards[] = { .name = "SIMUS GVC1100", .video_inputs = 4, .audio_inputs = 0, - .tuner = -1, - .svhs = -1, - .tuner_type = -1, + .tuner = UNSET, + .svhs = UNSET, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .pll = PLL_28, @@ -2395,14 +2418,14 @@ struct tvcard bttv_tvcards[] = { .name = "LMLBT4", .video_inputs = 4, /* IN1,IN2,IN3,IN4 */ .audio_inputs = 0, - .tuner = -1, - .svhs = -1, + .tuner = UNSET, + .svhs = UNSET, .muxsel = { 2, 3, 1, 0 }, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, .needs_tvaudio = 0, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -2452,8 +2475,8 @@ struct tvcard bttv_tvcards[] = { .name = "Euresys Picolo Tetra", .video_inputs = 4, .audio_inputs = 0, - .tuner = -1, - .svhs = -1, + .tuner = UNSET, + .svhs = UNSET, .gpiomask = 0, .gpiomask2 = 0x3C<<16,/*Set the GPIO[18]->GPIO[21] as output pin.==> drive the video inputs through analog multiplexers*/ .no_msp34xx = 1, @@ -2464,7 +2487,7 @@ struct tvcard bttv_tvcards[] = { .pll = PLL_28, .needs_tvaudio = 0, .muxsel_hook = picolo_tetra_muxsel,/*Required as it doesn't follow the classic input selection policy*/ - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -2490,7 +2513,7 @@ struct tvcard bttv_tvcards[] = { .name = "AVerMedia AVerTV DVB-T 771", .video_inputs = 2, .svhs = 1, - .tuner = -1, + .tuner = UNSET, .tuner_type = TUNER_ABSENT, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, @@ -2509,14 +2532,14 @@ struct tvcard bttv_tvcards[] = { /* Based on the Nebula card data - added remote and new card number - BTTV_BOARD_AVDVBT_761, see also ir-kbd-gpio.c */ .name = "AverMedia AverTV DVB-T 761", .video_inputs = 2, - .tuner = -1, + .tuner = UNSET, .svhs = 1, .muxsel = { 3, 1, 2, 0 }, /* Comp0, S-Video, ?, ? */ .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .has_dvb = 1, @@ -2528,8 +2551,8 @@ struct tvcard bttv_tvcards[] = { .name = "MATRIX Vision Sigma-SQ", .video_inputs = 16, .audio_inputs = 0, - .tuner = -1, - .svhs = -1, + .tuner = UNSET, + .svhs = UNSET, .gpiomask = 0x0, .muxsel = { 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3 }, @@ -2537,7 +2560,7 @@ struct tvcard bttv_tvcards[] = { .gpiomux = { 0 }, .no_msp34xx = 1, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -2546,15 +2569,15 @@ struct tvcard bttv_tvcards[] = { .name = "MATRIX Vision Sigma-SLC", .video_inputs = 4, .audio_inputs = 0, - .tuner = -1, - .svhs = -1, + .tuner = UNSET, + .svhs = UNSET, .gpiomask = 0x0, .muxsel = { 2, 2, 2, 2 }, .muxsel_hook = sigmaSLC_muxsel, .gpiomux = { 0 }, .no_msp34xx = 1, .pll = PLL_28, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -2566,7 +2589,7 @@ struct tvcard bttv_tvcards[] = { .video_inputs = 2, .audio_inputs = 1, .tuner = 0, - .svhs = -1, + .svhs = UNSET, .gpiomask = 0xFF, .muxsel = { 2, 3, 1, 1 }, .gpiomux = { 2, 0, 0, 0 }, @@ -2584,14 +2607,14 @@ struct tvcard bttv_tvcards[] = { [BTTV_BOARD_DVICO_DVBT_LITE] = { /* Chris Pascoe <c.pascoe@itee.uq.edu.au> */ .name = "DViCO FusionHDTV DVB-T Lite", - .tuner = -1, + .tuner = UNSET, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, .pll = PLL_28, .no_video = 1, .has_dvb = 1, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -2634,14 +2657,14 @@ struct tvcard bttv_tvcards[] = { .name = "Tibet Systems 'Progress DVR' CS16", .video_inputs = 16, .audio_inputs = 0, - .tuner = -1, - .svhs = -1, + .tuner = UNSET, + .svhs = UNSET, .muxsel = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }, .pll = PLL_28, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .muxsel_hook = tibetCS16_muxsel, @@ -2661,11 +2684,11 @@ struct tvcard bttv_tvcards[] = { .name = "Kodicom 4400R (master)", .video_inputs = 16, .audio_inputs = 0, - .tuner = -1, - .tuner_type = -1, + .tuner = UNSET, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .svhs = -1, + .svhs = UNSET, /* GPIO bits 0-9 used for analog switch: * 00 - 03: camera selector * 04 - 06: channel (controller) selector @@ -2693,11 +2716,11 @@ struct tvcard bttv_tvcards[] = { .name = "Kodicom 4400R (slave)", .video_inputs = 16, .audio_inputs = 0, - .tuner = -1, - .tuner_type = -1, + .tuner = UNSET, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .svhs = -1, + .svhs = UNSET, .gpiomask = 0x010000, .no_gpioirq = 1, .muxsel = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }, @@ -2717,7 +2740,7 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .muxsel = { 2, 3, 1, 0 }, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .pll = PLL_28, @@ -2824,7 +2847,7 @@ struct tvcard bttv_tvcards[] = { .name = "Osprey 440", .video_inputs = 1, .audio_inputs = 1, - .tuner = -1, + .tuner = UNSET, .svhs = 1, .muxsel = { 2 }, .pll = PLL_28, @@ -2848,7 +2871,7 @@ struct tvcard bttv_tvcards[] = { .gpiomute = 1, .needs_tvaudio = 1, .pll = PLL_28, - .tuner_type = 2, + .tuner_type = TUNER_PHILIPS_NTSC, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -2875,14 +2898,14 @@ struct tvcard bttv_tvcards[] = { .name = "Hauppauge ImpactVCB (bt878)", .video_inputs = 4, .audio_inputs = 0, - .tuner = -1, - .svhs = -1, + .tuner = UNSET, + .svhs = UNSET, .gpiomask = 0x0f, /* old: 7 */ .muxsel = { 0, 1, 3, 2 }, /* Composite 0-3 */ .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -2914,10 +2937,10 @@ struct tvcard bttv_tvcards[] = { .name = "SSAI Security Video Interface", .video_inputs = 4, .audio_inputs = 0, - .tuner = -1, - .svhs = -1, + .tuner = UNSET, + .svhs = UNSET, .muxsel = { 0, 1, 2, 3 }, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, @@ -2925,13 +2948,31 @@ struct tvcard bttv_tvcards[] = { .name = "SSAI Ultrasound Video Interface", .video_inputs = 2, .audio_inputs = 0, - .tuner = -1, + .tuner = UNSET, .svhs = 1, .muxsel = { 2, 0, 1, 3 }, - .tuner_type = -1, + .tuner_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, }, + /* ---- card 0x94---------------------------------- */ + [BTTV_BOARD_DVICO_FUSIONHDTV_2] = { + .name = "DViCO FusionHDTV 2", + .tuner = 0, + .tuner_type = TUNER_PHILIPS_ATSC, /* FCV1236D */ + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .video_inputs = 3, + .audio_inputs = 1, + .svhs = 2, + .muxsel = { 2, 3, 1 }, + .gpiomask = 0x00e00007, + .gpiomux = { 0x00400005, 0, 0x00000001, 0 }, + .gpiomute = 0x00c00007, + .no_msp34xx = 1, + .no_tda9875 = 1, + .no_tda7432 = 1, + }, }; static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards); @@ -3040,7 +3081,7 @@ static void identify_by_eeprom(struct bttv *btv, unsigned char eeprom_data[256]) static void flyvideo_gpio(struct bttv *btv) { int gpio,has_remote,has_radio,is_capture_only,is_lr90,has_tda9820_tda9821; - int tuner=-1,ttype; + int tuner=UNSET,ttype; gpio_inout(0xffffff, 0); udelay(8); /* without this we would see the 0x1800 mask */ @@ -3085,7 +3126,7 @@ static void flyvideo_gpio(struct bttv *btv) * gpio & 0x001000 output bit for audio routing */ if(is_capture_only) - tuner=4; /* No tuner present */ + tuner = TUNER_ABSENT; /* No tuner present */ printk(KERN_INFO "bttv%d: FlyVideo Radio=%s RemoteControl=%s Tuner=%d gpio=0x%06x\n", btv->c.nr, has_radio? "yes":"no ", has_remote? "yes":"no ", tuner, gpio); @@ -3093,7 +3134,7 @@ static void flyvideo_gpio(struct bttv *btv) btv->c.nr, is_lr90?"yes":"no ", has_tda9820_tda9821?"yes":"no ", is_capture_only?"yes":"no "); - if(tuner!= -1) /* only set if known tuner autodetected, else let insmod option through */ + if (tuner != UNSET) /* only set if known tuner autodetected, else let insmod option through */ btv->tuner_type = tuner; btv->has_radio = has_radio; @@ -3302,6 +3343,7 @@ void __devinit bttv_init_card1(struct bttv *btv) case BTTV_BOARD_HAUPPAUGE878: boot_msp34xx(btv,5); break; + case BTTV_BOARD_VOODOOTV_200: case BTTV_BOARD_VOODOOTV_FM: boot_msp34xx(btv,20); break; @@ -3328,10 +3370,9 @@ void __devinit bttv_init_card1(struct bttv *btv) /* initialization part two -- after registering i2c bus */ void __devinit bttv_init_card2(struct bttv *btv) { - int tda9887; int addr=ADDR_UNSET; - btv->tuner_type = -1; + btv->tuner_type = UNSET; if (BTTV_BOARD_UNKNOWN == btv->c.type) { bttv_readee(btv,eeprom_data,0xa0); @@ -3479,7 +3520,15 @@ void __devinit bttv_init_card2(struct bttv *btv) btv->tuner_type = bttv_tvcards[btv->c.type].tuner_type; if (UNSET != tuner[btv->c.nr]) btv->tuner_type = tuner[btv->c.nr]; - printk("bttv%d: using tuner=%d\n",btv->c.nr,btv->tuner_type); + + if (btv->tuner_type == TUNER_ABSENT || + bttv_tvcards[btv->c.type].tuner == UNSET) + printk(KERN_INFO "bttv%d: tuner absent\n", btv->c.nr); + else if(btv->tuner_type == UNSET) + printk(KERN_WARNING "bttv%d: tuner type unset\n", btv->c.nr); + else + printk(KERN_INFO "bttv%d: tuner type=%d\n", btv->c.nr, + btv->tuner_type); if (btv->tuner_type != UNSET) { struct tuner_setup tun_setup; @@ -3521,6 +3570,9 @@ void __devinit bttv_init_card2(struct bttv *btv) if (!autoload) return; + if (bttv_tvcards[btv->c.type].tuner == UNSET) + return; /* no tuner or related drivers to load */ + /* try to detect audio/fader chips */ if (!bttv_tvcards[btv->c.type].no_msp34xx && bttv_I2CRead(btv, I2C_ADDR_MSP3400, "MSP34xx") >=0) @@ -3541,17 +3593,7 @@ void __devinit bttv_init_card2(struct bttv *btv) if (bttv_tvcards[btv->c.type].needs_tvaudio) request_module("tvaudio"); - /* tuner modules */ - tda9887 = 0; - if (btv->tda9887_conf) - tda9887 = 1; - if (0 == tda9887 && 0 == bttv_tvcards[btv->c.type].has_dvb && - bttv_I2CRead(btv, I2C_ADDR_TDA9887, "TDA9887") >=0) - tda9887 = 1; - /* Hybrid DVB card, DOES have a tda9887 */ - if (btv->c.type == BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE) - tda9887 = 1; - if (btv->tuner_type != UNSET) + if (btv->tuner_type != UNSET && btv->tuner_type != TUNER_ABSENT) request_module("tuner"); } @@ -3865,11 +3907,15 @@ void bttv_tda9880_setnorm(struct bttv *btv, int norm) if(norm==VIDEO_MODE_NTSC) { bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].gpiomux[TVAUDIO_INPUT_TUNER]=0x957fff; bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].gpiomute=0x957fff; + bttv_tvcards[BTTV_BOARD_VOODOOTV_200].gpiomux[TVAUDIO_INPUT_TUNER]=0x957fff; + bttv_tvcards[BTTV_BOARD_VOODOOTV_200].gpiomute=0x957fff; dprintk("bttv_tda9880_setnorm to NTSC\n"); } else { bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].gpiomux[TVAUDIO_INPUT_TUNER]=0x947fff; bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].gpiomute=0x947fff; + bttv_tvcards[BTTV_BOARD_VOODOOTV_200].gpiomux[TVAUDIO_INPUT_TUNER]=0x947fff; + bttv_tvcards[BTTV_BOARD_VOODOOTV_200].gpiomute=0x947fff; dprintk("bttv_tda9880_setnorm to PAL\n"); } /* set GPIO according */ diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index b1fedb0..cb555f2 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -1218,7 +1218,14 @@ audio_mux(struct bttv *btv, int input, int mute) break; case TVAUDIO_INPUT_TUNER: default: - route.input = MSP_INPUT_DEFAULT; + /* This is the only card that uses TUNER2, and afaik, + is the only difference between the VOODOOTV_FM + and VOODOOTV_200 */ + if (btv->c.type == BTTV_BOARD_VOODOOTV_200) + route.input = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER2, \ + MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER); + else + route.input = MSP_INPUT_DEFAULT; break; } route.output = MSP_OUTPUT_DEFAULT; @@ -1253,7 +1260,7 @@ i2c_vidiocschan(struct bttv *btv) v4l2_std_id std = bttv_tvnorms[btv->tvnorm].v4l2_id; bttv_call_i2c_clients(btv, VIDIOC_S_STD, &std); - if (btv->c.type == BTTV_BOARD_VOODOOTV_FM) + if (btv->c.type == BTTV_BOARD_VOODOOTV_FM || btv->c.type == BTTV_BOARD_VOODOOTV_200) bttv_tda9880_setnorm(btv,btv->tvnorm); } @@ -1323,6 +1330,7 @@ set_tvnorm(struct bttv *btv, unsigned int norm) switch (btv->c.type) { case BTTV_BOARD_VOODOOTV_FM: + case BTTV_BOARD_VOODOOTV_200: bttv_tda9880_setnorm(btv,norm); break; } @@ -2251,6 +2259,24 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) printk(KERN_INFO "bttv%d: ================== END STATUS CARD #%d ==================\n", btv->c.nr, btv->c.nr); return 0; } +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_DBG_G_REGISTER: + case VIDIOC_DBG_S_REGISTER: + { + struct v4l2_register *reg = arg; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return -EINVAL; + /* bt848 has a 12-bit register space */ + reg->reg &= 0xfff; + if (cmd == VIDIOC_DBG_G_REGISTER) + reg->val = btread(reg->reg); + else + btwrite(reg->val, reg->reg); + return 0; + } +#endif default: return -ENOIOCTLCMD; @@ -3561,6 +3587,8 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_G_FREQUENCY: case VIDIOC_S_FREQUENCY: case VIDIOC_LOG_STATUS: + case VIDIOC_DBG_G_REGISTER: + case VIDIOC_DBG_S_REGISTER: return bttv_common_ioctls(btv,cmd,arg); default: @@ -3943,6 +3971,8 @@ static int radio_do_ioctl(struct inode *inode, struct file *file, case VIDIOCGAUDIO: case VIDIOCSAUDIO: case VIDIOC_LOG_STATUS: + case VIDIOC_DBG_G_REGISTER: + case VIDIOC_DBG_S_REGISTER: return bttv_common_ioctls(btv,cmd,arg); default: diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c index 6f74c80..94a13d0 100644 --- a/drivers/media/video/bt8xx/bttv-input.c +++ b/drivers/media/video/bt8xx/bttv-input.c @@ -313,7 +313,7 @@ int bttv_input_init(struct bttv *btv) input_dev->id.vendor = btv->c.pci->vendor; input_dev->id.product = btv->c.pci->device; } - input_dev->cdev.dev = &btv->c.pci->dev; + input_dev->dev.parent = &btv->c.pci->dev; btv->remote = ir; bttv_ir_start(btv, ir); diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h index f821ba6..dcc847d 100644 --- a/drivers/media/video/bt8xx/bttv.h +++ b/drivers/media/video/bt8xx/bttv.h @@ -170,6 +170,8 @@ #define BTTV_BOARD_MACHTV_MAGICTV 0x90 #define BTTV_BOARD_SSAI_SECURITY 0x91 #define BTTV_BOARD_SSAI_ULTRASOUND 0x92 +#define BTTV_BOARD_VOODOOTV_200 0x93 +#define BTTV_BOARD_DVICO_FUSIONHDTV_2 0x94 /* more card-specific defines */ #define PT2254_L_CHANNEL 0x10 diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h index 8f44f02..bd85f6d 100644 --- a/drivers/media/video/bt8xx/bttvp.h +++ b/drivers/media/video/bt8xx/bttvp.h @@ -33,12 +33,12 @@ #include <linux/i2c.h> #include <linux/i2c-algo-bit.h> #include <linux/videodev.h> -#include <media/v4l2-common.h> #include <linux/pci.h> #include <linux/input.h> #include <linux/mutex.h> #include <asm/scatterlist.h> #include <asm/io.h> +#include <media/v4l2-common.h> #include <linux/device.h> #include <media/video-buf.h> diff --git a/drivers/media/video/cpia2/cpia2_core.c b/drivers/media/video/cpia2/cpia2_core.c index fd771c7..55aab8d 100644 --- a/drivers/media/video/cpia2/cpia2_core.c +++ b/drivers/media/video/cpia2/cpia2_core.c @@ -663,15 +663,13 @@ int cpia2_reset_camera(struct camera_data *cam) cpia2_send_command(cam, &cmd); } - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(100 * HZ / 1000); /* wait for 100 msecs */ + schedule_timeout_interruptible(msecs_to_jiffies(100)); if (cam->params.pnp_id.device_type == DEVICE_STV_672) retval = apply_vp_patch(cam); /* wait for vp to go to sleep */ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(100 * HZ / 1000); /* wait for 100 msecs */ + schedule_timeout_interruptible(msecs_to_jiffies(100)); /*** * If this is a 676, apply VP5 fixes before we start streaming @@ -720,8 +718,7 @@ int cpia2_reset_camera(struct camera_data *cam) set_default_user_mode(cam); /* Give VP time to wake up */ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(100 * HZ / 1000); /* wait for 100 msecs */ + schedule_timeout_interruptible(msecs_to_jiffies(100)); set_all_properties(cam); diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index 1bda7ad..92778cd 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/drivers/media/video/cpia2/cpia2_v4l.c @@ -105,7 +105,7 @@ static struct control_menu_info framerate_controls[] = { CPIA2_VP_FRAMERATE_25, "25 fps" }, { CPIA2_VP_FRAMERATE_30, "30 fps" }, }; -#define NUM_FRAMERATE_CONTROLS (sizeof(framerate_controls)/sizeof(framerate_controls[0])) +#define NUM_FRAMERATE_CONTROLS (ARRAY_SIZE(framerate_controls)) static struct control_menu_info flicker_controls[] = { @@ -113,7 +113,7 @@ static struct control_menu_info flicker_controls[] = { FLICKER_50, "50 Hz" }, { FLICKER_60, "60 Hz" }, }; -#define NUM_FLICKER_CONTROLS (sizeof(flicker_controls)/sizeof(flicker_controls[0])) +#define NUM_FLICKER_CONTROLS (ARRAY_SIZE(flicker_controls)) static struct control_menu_info lights_controls[] = { @@ -122,7 +122,7 @@ static struct control_menu_info lights_controls[] = { 128, "Bottom" }, { 192, "Both" }, }; -#define NUM_LIGHTS_CONTROLS (sizeof(lights_controls)/sizeof(lights_controls[0])) +#define NUM_LIGHTS_CONTROLS (ARRAY_SIZE(lights_controls)) #define GPIO_LIGHTS_MASK 192 static struct v4l2_queryctrl controls[] = { @@ -235,7 +235,7 @@ static struct v4l2_queryctrl controls[] = { .default_value = 0, }, }; -#define NUM_CONTROLS (sizeof(controls)/sizeof(controls[0])) +#define NUM_CONTROLS (ARRAY_SIZE(controls)) /****************************************************************************** diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig index 0f9d969..f750a54 100644 --- a/drivers/media/video/cx88/Kconfig +++ b/drivers/media/video/cx88/Kconfig @@ -47,7 +47,7 @@ config VIDEO_CX88_DVB tristate "DVB/ATSC Support for cx2388x based TV cards" depends on VIDEO_CX88 && DVB_CORE select VIDEO_BUF_DVB - select DVB_PLL + select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_MT352 if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE select DVB_OR51132 if !DVB_FE_CUSTOMISE diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index a80b1cb..f2fcdb9 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -56,8 +56,7 @@ MODULE_PARM_DESC(debug,"enable debug messages [blackbird]"); /* ------------------------------------------------------------------ */ -#define OLD_BLACKBIRD_FIRM_IMAGE_SIZE 262144 -#define BLACKBIRD_FIRM_IMAGE_SIZE 376836 +#define BLACKBIRD_FIRM_IMAGE_SIZE 376836 /* defines below are from ivtv-driver.h */ @@ -405,7 +404,7 @@ static int blackbird_find_mailbox(struct cx8802_dev *dev) u32 value; int i; - for (i = 0; i < dev->fw_size; i++) { + for (i = 0; i < BLACKBIRD_FIRM_IMAGE_SIZE; i++) { memory_read(dev->core, i, &value); if (value == signature[signaturecnt]) signaturecnt++; @@ -453,15 +452,12 @@ static int blackbird_load_firmware(struct cx8802_dev *dev) return -1; } - if ((firmware->size != BLACKBIRD_FIRM_IMAGE_SIZE) && - (firmware->size != OLD_BLACKBIRD_FIRM_IMAGE_SIZE)) { - dprintk(0, "ERROR: Firmware size mismatch (have %zd, expected %d or %d)\n", - firmware->size, BLACKBIRD_FIRM_IMAGE_SIZE, - OLD_BLACKBIRD_FIRM_IMAGE_SIZE); + if (firmware->size != BLACKBIRD_FIRM_IMAGE_SIZE) { + dprintk(0, "ERROR: Firmware size mismatch (have %zd, expected %d)\n", + firmware->size, BLACKBIRD_FIRM_IMAGE_SIZE); release_firmware(firmware); return -1; } - dev->fw_size = firmware->size; if (0 != memcmp(firmware->data, magic, 8)) { dprintk(0, "ERROR: Firmware magic mismatch, wrong file?\n"); diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index e61102d..6a136dd 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -1335,6 +1335,26 @@ struct cx88_board cx88_boards[] = { /* fixme: Add radio support */ .mpeg = CX88_MPEG_DVB | CX88_MPEG_BLACKBIRD, }, + [CX88_BOARD_ADSTECH_PTV_390] = { + .name = "ADS Tech Instant Video PCI", + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_DEBUG, + .vmux = 3, + .gpio0 = 0x04ff, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x07fa, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x07fa, + }}, + }, }; const unsigned int cx88_bcount = ARRAY_SIZE(cx88_boards); @@ -1641,6 +1661,10 @@ struct cx88_subid cx88_subids[] = { .subvendor = 0x1421, .subdevice = 0x0341, /* ADS Tech InstantTV DVB-S */ .card = CX88_BOARD_KWORLD_DVBS_100, + },{ + .subvendor = 0x1421, + .subdevice = 0x0390, + .card = CX88_BOARD_ADSTECH_PTV_390, }, }; const unsigned int cx88_idcount = ARRAY_SIZE(cx88_subids); diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index dbfe4dc..1773b40 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -35,9 +35,7 @@ #include "mt352.h" #include "mt352_priv.h" -#if defined(CONFIG_VIDEO_CX88_VP3054) || defined(CONFIG_VIDEO_CX88_VP3054_MODULE) -# include "cx88-vp3054-i2c.h" -#endif +#include "cx88-vp3054-i2c.h" #include "zl10353.h" #include "cx22702.h" #include "or51132.h" @@ -199,7 +197,7 @@ static struct mt352_config dvico_fusionhdtv_dual = { .demod_init = dvico_dual_demod_init, }; -#if defined(CONFIG_VIDEO_CX88_VP3054) || defined(CONFIG_VIDEO_CX88_VP3054_MODULE) +#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE)) static int dntv_live_dvbt_pro_demod_init(struct dvb_frontend* fe) { static u8 clock_config [] = { 0x89, 0x38, 0x38 }; @@ -223,64 +221,6 @@ static int dntv_live_dvbt_pro_demod_init(struct dvb_frontend* fe) return 0; } -static int philips_fmd1216_pll_init(struct dvb_frontend *fe) -{ - struct cx8802_dev *dev= fe->dvb->priv; - - /* this message is to set up ATC and ALC */ - static u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0xa0 }; - struct i2c_msg msg = - { .addr = dev->core->pll_addr, .flags = 0, - .buf = fmd1216_init, .len = sizeof(fmd1216_init) }; - int err; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if ((err = i2c_transfer(&dev->core->i2c_adap, &msg, 1)) != 1) { - if (err < 0) - return err; - else - return -EREMOTEIO; - } - - return 0; -} - -static int dntv_live_dvbt_pro_tuner_set_params(struct dvb_frontend* fe, - struct dvb_frontend_parameters* params) -{ - struct cx8802_dev *dev= fe->dvb->priv; - u8 buf[4]; - struct i2c_msg msg = - { .addr = dev->core->pll_addr, .flags = 0, - .buf = buf, .len = 4 }; - int err; - - /* Switch PLL to DVB mode */ - err = philips_fmd1216_pll_init(fe); - if (err) - return err; - - /* Tune PLL */ - dvb_pll_configure(dev->core->pll_desc, buf, - params->frequency, - params->u.ofdm.bandwidth); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if ((err = i2c_transfer(&dev->core->i2c_adap, &msg, 1)) != 1) { - - printk(KERN_WARNING "cx88-dvb: %s error " - "(addr %02x <- %02x, err = %i)\n", - __FUNCTION__, dev->core->pll_addr, buf[0], err); - if (err < 0) - return err; - else - return -EREMOTEIO; - } - - return 0; -} - static struct mt352_config dntv_live_dvbt_pro_config = { .demod_address = 0x0f, .no_tuner = 1, @@ -370,18 +310,8 @@ static int nxt200x_set_ts_param(struct dvb_frontend* fe, int is_punctured) return 0; } -static int nxt200x_set_pll_input(u8* buf, int input) -{ - if (input) - buf[3] |= 0x08; - else - buf[3] &= ~0x08; - return 0; -} - static struct nxt200x_config ati_hdtvwonder = { .demod_address = 0x0a, - .set_pll_input = nxt200x_set_pll_input, .set_ts_params = nxt200x_set_ts_param, }; @@ -456,7 +386,7 @@ static int dvb_register(struct cx8802_dev *dev) if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, &dev->core->i2c_adap, - &dvb_pll_thomson_dtt759x); + DVB_PLL_THOMSON_DTT759X); } break; case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1: @@ -469,7 +399,7 @@ static int dvb_register(struct cx8802_dev *dev) if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x60, &dev->core->i2c_adap, - &dvb_pll_thomson_dtt7579); + DVB_PLL_THOMSON_DTT7579); } break; case CX88_BOARD_WINFAST_DTV2000H: @@ -482,7 +412,7 @@ static int dvb_register(struct cx8802_dev *dev) &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, &dvb_pll_fmd1216me); + &dev->core->i2c_adap, DVB_PLL_FMD1216ME); } break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS: @@ -491,7 +421,7 @@ static int dvb_register(struct cx8802_dev *dev) &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x60, - NULL, &dvb_pll_thomson_dtt7579); + NULL, DVB_PLL_THOMSON_DTT7579); break; } /* ZL10353 replaces MT352 on later cards */ @@ -500,7 +430,7 @@ static int dvb_register(struct cx8802_dev *dev) &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x60, - NULL, &dvb_pll_thomson_dtt7579); + NULL, DVB_PLL_THOMSON_DTT7579); } break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL: @@ -511,7 +441,7 @@ static int dvb_register(struct cx8802_dev *dev) &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - NULL, &dvb_pll_thomson_dtt7579); + NULL, DVB_PLL_THOMSON_DTT7579); break; } /* ZL10353 replaces MT352 on later cards */ @@ -520,7 +450,7 @@ static int dvb_register(struct cx8802_dev *dev) &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - NULL, &dvb_pll_thomson_dtt7579); + NULL, DVB_PLL_THOMSON_DTT7579); } break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1: @@ -529,7 +459,7 @@ static int dvb_register(struct cx8802_dev *dev) &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - NULL, &dvb_pll_lg_z201); + NULL, DVB_PLL_LG_Z201); } break; case CX88_BOARD_KWORLD_DVB_T: @@ -540,17 +470,16 @@ static int dvb_register(struct cx8802_dev *dev) &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - NULL, &dvb_pll_unknown_1); + NULL, DVB_PLL_UNKNOWN_1); } break; case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: -#if defined(CONFIG_VIDEO_CX88_VP3054) || defined(CONFIG_VIDEO_CX88_VP3054_MODULE) - dev->core->pll_addr = 0x61; - dev->core->pll_desc = &dvb_pll_fmd1216me; +#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE)) dev->dvb.frontend = dvb_attach(mt352_attach, &dntv_live_dvbt_pro_config, &((struct vp3054_i2c_state *)dev->card_priv)->adap); if (dev->dvb.frontend != NULL) { - dev->dvb.frontend->ops.tuner_ops.set_params = dntv_live_dvbt_pro_tuner_set_params; + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + &dev->core->i2c_adap, DVB_PLL_FMD1216ME); } #else printk("%s: built without vp3054 support\n", dev->core->name); @@ -563,7 +492,7 @@ static int dvb_register(struct cx8802_dev *dev) if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, &dev->core->i2c_adap, - &dvb_pll_thomson_fe6600); + DVB_PLL_THOMSON_FE6600); } break; case CX88_BOARD_PCHDTV_HD3000: @@ -572,7 +501,7 @@ static int dvb_register(struct cx8802_dev *dev) if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, &dev->core->i2c_adap, - &dvb_pll_thomson_dtt761x); + DVB_PLL_THOMSON_DTT761X); } break; case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q: @@ -594,7 +523,7 @@ static int dvb_register(struct cx8802_dev *dev) if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, &dev->core->i2c_adap, - &dvb_pll_microtune_4042); + DVB_PLL_MICROTUNE_4042); } } break; @@ -614,7 +543,7 @@ static int dvb_register(struct cx8802_dev *dev) if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, &dev->core->i2c_adap, - &dvb_pll_thomson_dtt761x); + DVB_PLL_THOMSON_DTT761X); } } break; @@ -634,7 +563,7 @@ static int dvb_register(struct cx8802_dev *dev) if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, &dev->core->i2c_adap, - &dvb_pll_lg_tdvs_h06xf); + DVB_PLL_LG_TDVS_H06XF); } } break; @@ -654,7 +583,7 @@ static int dvb_register(struct cx8802_dev *dev) if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, &dev->core->i2c_adap, - &dvb_pll_lg_tdvs_h06xf); + DVB_PLL_LG_TDVS_H06XF); } } break; @@ -664,7 +593,7 @@ static int dvb_register(struct cx8802_dev *dev) &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - NULL, &dvb_pll_tuv1236d); + NULL, DVB_PLL_TUV1236D); } break; case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: @@ -705,10 +634,6 @@ static int dvb_register(struct cx8802_dev *dev) return -1; } - if (dev->core->pll_desc) { - dev->dvb.frontend->ops.info.frequency_min = dev->core->pll_desc->min; - dev->dvb.frontend->ops.info.frequency_max = dev->core->pll_desc->max; - } /* Ensure all frontends negotiate bus access */ dev->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl; @@ -778,11 +703,10 @@ static int cx8802_dvb_probe(struct cx8802_driver *drv) if (!(cx88_boards[core->board].mpeg & CX88_MPEG_DVB)) goto fail_core; -#if defined(CONFIG_VIDEO_CX88_VP3054) || defined(CONFIG_VIDEO_CX88_VP3054_MODULE) + /* If vp3054 isn't enabled, a stub will just return 0 */ err = vp3054_i2c_probe(dev); if (0 != err) goto fail_core; -#endif /* dvb stuff */ printk("%s/2: cx2388x based dvb card\n", core->name); @@ -807,9 +731,7 @@ static int cx8802_dvb_remove(struct cx8802_driver *drv) /* dvb */ videobuf_dvb_unregister(&dev->dvb); -#if defined(CONFIG_VIDEO_CX88_VP3054) || defined(CONFIG_VIDEO_CX88_VP3054_MODULE) vp3054_i2c_remove(dev); -#endif return 0; } diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c index 7919a1f..78bbcfa 100644 --- a/drivers/media/video/cx88/cx88-i2c.c +++ b/drivers/media/video/cx88/cx88-i2c.c @@ -160,7 +160,7 @@ void cx88_call_i2c_clients(struct cx88_core *core, unsigned int cmd, void *arg) i2c_clients_command(&core->i2c_adap, cmd, arg); } -static struct i2c_algo_bit_data cx8800_i2c_algo_template = { +static const struct i2c_algo_bit_data cx8800_i2c_algo_template = { .setsda = cx8800_bit_setsda, .setscl = cx8800_bit_setscl, .getsda = cx8800_bit_getsda, @@ -171,18 +171,6 @@ static struct i2c_algo_bit_data cx8800_i2c_algo_template = { /* ----------------------------------------------------------------------- */ -static struct i2c_adapter cx8800_i2c_adap_template = { - .name = "cx2388x", - .owner = THIS_MODULE, - .id = I2C_HW_B_CX2388x, - .client_register = attach_inform, - .client_unregister = detach_inform, -}; - -static struct i2c_client cx8800_i2c_client_template = { - .name = "cx88xx internal", -}; - static char *i2c_devs[128] = { [ 0x1c >> 1 ] = "lgdt330x", [ 0x86 >> 1 ] = "tda9887/cx22702", @@ -212,14 +200,9 @@ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci) /* Prevents usage of invalid delay values */ if (i2c_udelay<5) i2c_udelay=5; - cx8800_i2c_algo_template.udelay=i2c_udelay; - memcpy(&core->i2c_adap, &cx8800_i2c_adap_template, - sizeof(core->i2c_adap)); memcpy(&core->i2c_algo, &cx8800_i2c_algo_template, sizeof(core->i2c_algo)); - memcpy(&core->i2c_client, &cx8800_i2c_client_template, - sizeof(core->i2c_client)); if (core->tuner_type != TUNER_ABSENT) core->i2c_adap.class |= I2C_CLASS_TV_ANALOG; @@ -228,10 +211,16 @@ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci) core->i2c_adap.dev.parent = &pci->dev; strlcpy(core->i2c_adap.name,core->name,sizeof(core->i2c_adap.name)); + core->i2c_adap.owner = THIS_MODULE; + core->i2c_adap.id = I2C_HW_B_CX2388x; + core->i2c_adap.client_register = attach_inform; + core->i2c_adap.client_unregister = detach_inform; + core->i2c_algo.udelay = i2c_udelay; core->i2c_algo.data = core; i2c_set_adapdata(&core->i2c_adap,core); core->i2c_adap.algo_data = &core->i2c_algo; core->i2c_client.adapter = &core->i2c_adap; + strlcpy(core->i2c_client.name, "cx88xx internal", I2C_NAME_SIZE); cx8800_bit_setscl(core,1); cx8800_bit_setsda(core,1); diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index 8136673..f5d4a56 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -74,7 +74,8 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) /* read gpio value */ gpio = cx_read(ir->gpio_addr); - if (core->board == CX88_BOARD_NPGTECH_REALTV_TOP10FM) { + switch (core->board) { + case CX88_BOARD_NPGTECH_REALTV_TOP10FM: /* This board apparently uses a combination of 2 GPIO to represent the keys. Additionally, the second GPIO can be used for parity. @@ -90,9 +91,14 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) auxgpio = cx_read(MO_GP1_IO); /* Take out the parity part */ gpio=(gpio & 0x7fd) + (auxgpio & 0xef); - } else + break; + case CX88_BOARD_WINFAST_DTV1000: + gpio = (gpio & 0x6ff) | ((cx_read(MO_GP1_IO) << 8) & 0x900); auxgpio = gpio; - + break; + default: + auxgpio = gpio; + } if (ir->polling) { if (ir->last_gpio == auxgpio) return; @@ -148,20 +154,16 @@ static void ir_timer(unsigned long data) static void cx88_ir_work(struct work_struct *work) { struct cx88_IR *ir = container_of(work, struct cx88_IR, work); - unsigned long timeout; cx88_ir_handle_key(ir); - timeout = jiffies + (ir->polling * HZ / 1000); - mod_timer(&ir->timer, timeout); + mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); } static void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir) { if (ir->polling) { + setup_timer(&ir->timer, ir_timer, (unsigned long)ir); INIT_WORK(&ir->work, cx88_ir_work); - init_timer(&ir->timer); - ir->timer.function = ir_timer; - ir->timer.data = (unsigned long)ir; schedule_work(&ir->work); } if (ir->sampling) { @@ -222,7 +224,6 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: case CX88_BOARD_HAUPPAUGE_HVR1100: - case CX88_BOARD_HAUPPAUGE_HVR1300: case CX88_BOARD_HAUPPAUGE_HVR3000: ir_codes = ir_codes_hauppauge_new; ir_type = IR_TYPE_RC5; @@ -236,6 +237,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) ir->polling = 50; /* ms */ break; case CX88_BOARD_WINFAST2000XP_EXPERT: + case CX88_BOARD_WINFAST_DTV1000: ir_codes = ir_codes_winfast; ir->gpio_addr = MO_GP0_IO; ir->mask_keycode = 0x8f8; @@ -328,7 +330,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) input_dev->id.vendor = pci->vendor; input_dev->id.product = pci->device; } - input_dev->cdev.dev = &pci->dev; + input_dev->dev.parent = &pci->dev; /* record handles to ourself */ ir->core = core; core->ir = ir; @@ -442,7 +444,6 @@ void cx88_ir_irq(struct cx88_core *core) case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: case CX88_BOARD_HAUPPAUGE_HVR1100: - case CX88_BOARD_HAUPPAUGE_HVR1300: case CX88_BOARD_HAUPPAUGE_HVR3000: ircode = ir_decode_biphase(ir->samples, ir->scount, 5, 7); ir_dprintk("biphase decoded: %x\n", ircode); diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index 543b05e..317a2a3 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -336,7 +336,7 @@ static void cx8802_timeout(unsigned long data) { struct cx8802_dev *dev = (struct cx8802_dev*)data; - dprintk(0, "%s\n",__FUNCTION__); + dprintk(1, "%s\n",__FUNCTION__); if (debug) cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]); diff --git a/drivers/media/video/cx88/cx88-vp3054-i2c.c b/drivers/media/video/cx88/cx88-vp3054-i2c.c index 82bc3a2..cd08776 100644 --- a/drivers/media/video/cx88/cx88-vp3054-i2c.c +++ b/drivers/media/video/cx88/cx88-vp3054-i2c.c @@ -94,7 +94,7 @@ static int vp3054_bit_getsda(void *data) /* ----------------------------------------------------------------------- */ -static struct i2c_algo_bit_data vp3054_i2c_algo_template = { +static const struct i2c_algo_bit_data vp3054_i2c_algo_template = { .setsda = vp3054_bit_setsda, .setscl = vp3054_bit_setscl, .getsda = vp3054_bit_getsda, @@ -105,12 +105,6 @@ static struct i2c_algo_bit_data vp3054_i2c_algo_template = { /* ----------------------------------------------------------------------- */ -static struct i2c_adapter vp3054_i2c_adap_template = { - .name = "cx2388x", - .owner = THIS_MODULE, - .id = I2C_HW_B_CX2388x, -}; - int vp3054_i2c_probe(struct cx8802_dev *dev) { struct cx88_core *core = dev->core; @@ -125,8 +119,6 @@ int vp3054_i2c_probe(struct cx8802_dev *dev) return -ENOMEM; vp3054_i2c = dev->card_priv; - memcpy(&vp3054_i2c->adap, &vp3054_i2c_adap_template, - sizeof(vp3054_i2c->adap)); memcpy(&vp3054_i2c->algo, &vp3054_i2c_algo_template, sizeof(vp3054_i2c->algo)); @@ -135,6 +127,8 @@ int vp3054_i2c_probe(struct cx8802_dev *dev) vp3054_i2c->adap.dev.parent = &dev->pci->dev; strlcpy(vp3054_i2c->adap.name, core->name, sizeof(vp3054_i2c->adap.name)); + vp3054_i2c->adap.owner = THIS_MODULE; + vp3054_i2c->adap.id = I2C_HW_B_CX2388x; vp3054_i2c->algo.data = dev; i2c_set_adapdata(&vp3054_i2c->adap, dev); vp3054_i2c->adap.algo_data = &vp3054_i2c->algo; diff --git a/drivers/media/video/cx88/cx88-vp3054-i2c.h b/drivers/media/video/cx88/cx88-vp3054-i2c.h index 637a7d2..be99c93 100644 --- a/drivers/media/video/cx88/cx88-vp3054-i2c.h +++ b/drivers/media/video/cx88/cx88-vp3054-i2c.h @@ -30,5 +30,12 @@ struct vp3054_i2c_state { }; /* ----------------------------------------------------------------------- */ +#if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE)) int vp3054_i2c_probe(struct cx8802_dev *dev); void vp3054_i2c_remove(struct cx8802_dev *dev); +#else +static inline int vp3054_i2c_probe(struct cx8802_dev *dev) +{ return 0; } +static inline void vp3054_i2c_remove(struct cx8802_dev *dev) +{ } +#endif diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index 738d4f2..c4f656e 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -209,6 +209,7 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_NORWOOD_MICRO 54 #define CX88_BOARD_TE_DTV_250_OEM_SWANN 55 #define CX88_BOARD_HAUPPAUGE_HVR1300 56 +#define CX88_BOARD_ADSTECH_PTV_390 57 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, @@ -316,8 +317,6 @@ struct cx88_core { /* config info -- dvb */ #if defined(CONFIG_VIDEO_BUF_DVB) || defined(CONFIG_VIDEO_BUF_DVB_MODULE) - struct dvb_pll_desc *pll_desc; - unsigned int pll_addr; int (*prev_set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage); #endif @@ -463,13 +462,10 @@ struct cx8802_dev { u32 mailbox; int width; int height; - int fw_size; #if defined(CONFIG_VIDEO_BUF_DVB) || defined(CONFIG_VIDEO_BUF_DVB_MODULE) /* for dvb only */ struct videobuf_dvb dvb; - void* fe_handle; - int (*fe_release)(void *handle); void *card_priv; #endif diff --git a/drivers/media/video/et61x251/Kconfig b/drivers/media/video/et61x251/Kconfig index 664676f..dcc1a03 100644 --- a/drivers/media/video/et61x251/Kconfig +++ b/drivers/media/video/et61x251/Kconfig @@ -1,6 +1,6 @@ config USB_ET61X251 tristate "USB ET61X[12]51 PC Camera Controller support" - depends on VIDEO_V4L1 + depends on VIDEO_V4L2 ---help--- Say Y here if you want support for cameras based on Etoms ET61X151 or ET61X251 PC Camera Controllers. diff --git a/drivers/media/video/et61x251/et61x251.h b/drivers/media/video/et61x251/et61x251.h index 262f98e..02c741d 100644 --- a/drivers/media/video/et61x251/et61x251.h +++ b/drivers/media/video/et61x251/et61x251.h @@ -36,6 +36,7 @@ #include <linux/mutex.h> #include <linux/stddef.h> #include <linux/string.h> +#include <linux/kref.h> #include "et61x251_sensor.h" @@ -134,7 +135,7 @@ struct et61x251_module_param { }; static DEFINE_MUTEX(et61x251_sysfs_lock); -static DECLARE_RWSEM(et61x251_disconnect); +static DECLARE_RWSEM(et61x251_dev_lock); struct et61x251_device { struct video_device* v4ldev; @@ -158,12 +159,14 @@ struct et61x251_device { struct et61x251_sysfs_attr sysfs; struct et61x251_module_param module_param; + struct kref kref; enum et61x251_dev_state state; u8 users; - struct mutex dev_mutex, fileop_mutex; + struct completion probe; + struct mutex open_mutex, fileop_mutex; spinlock_t queue_lock; - wait_queue_head_t open, wait_frame, wait_stream; + wait_queue_head_t wait_open, wait_frame, wait_stream; }; /*****************************************************************************/ @@ -177,7 +180,7 @@ et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id) void et61x251_attach_sensor(struct et61x251_device* cam, - struct et61x251_sensor* sensor) + const struct et61x251_sensor* sensor) { memcpy(&cam->sensor, sensor, sizeof(struct et61x251_sensor)); } @@ -195,8 +198,8 @@ do { \ else if ((level) == 2) \ dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ else if ((level) >= 3) \ - dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ - __FUNCTION__, __LINE__ , ## args); \ + dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \ + __FILE__, __FUNCTION__, __LINE__ , ## args); \ } \ } while (0) # define KDBG(level, fmt, args...) \ @@ -205,8 +208,8 @@ do { \ if ((level) == 1 || (level) == 2) \ pr_info("et61x251: " fmt "\n", ## args); \ else if ((level) == 3) \ - pr_debug("et61x251: [%s:%d] " fmt "\n", __FUNCTION__, \ - __LINE__ , ## args); \ + pr_debug("sn9c102: [%s:%s:%d] " fmt "\n", __FILE__, \ + __FUNCTION__, __LINE__ , ## args); \ } \ } while (0) # define V4LDBG(level, name, cmd) \ @@ -222,8 +225,8 @@ do { \ #undef PDBG #define PDBG(fmt, args...) \ -dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ - __FUNCTION__, __LINE__ , ## args) +dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __FUNCTION__, \ + __LINE__ , ## args) #undef PDBGG #define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c index a652551..585bd1f 100644 --- a/drivers/media/video/et61x251/et61x251_core.c +++ b/drivers/media/video/et61x251/et61x251_core.c @@ -45,11 +45,11 @@ #define ET61X251_MODULE_NAME "V4L2 driver for ET61X[12]51 " \ "PC Camera Controllers" -#define ET61X251_MODULE_AUTHOR "(C) 2006 Luca Risolia" +#define ET61X251_MODULE_AUTHOR "(C) 2006-2007 Luca Risolia" #define ET61X251_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>" #define ET61X251_MODULE_LICENSE "GPL" -#define ET61X251_MODULE_VERSION "1:1.04" -#define ET61X251_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 4) +#define ET61X251_MODULE_VERSION "1:1.09" +#define ET61X251_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 9) /*****************************************************************************/ @@ -245,7 +245,8 @@ int et61x251_read_reg(struct et61x251_device* cam, u16 index) static int -et61x251_i2c_wait(struct et61x251_device* cam, struct et61x251_sensor* sensor) +et61x251_i2c_wait(struct et61x251_device* cam, + const struct et61x251_sensor* sensor) { int i, r; @@ -270,7 +271,7 @@ et61x251_i2c_wait(struct et61x251_device* cam, struct et61x251_sensor* sensor) int et61x251_i2c_try_read(struct et61x251_device* cam, - struct et61x251_sensor* sensor, u8 address) + const struct et61x251_sensor* sensor, u8 address) { struct usb_device* udev = cam->usbdev; u8* data = cam->control_buffer; @@ -303,7 +304,8 @@ et61x251_i2c_try_read(struct et61x251_device* cam, int et61x251_i2c_try_write(struct et61x251_device* cam, - struct et61x251_sensor* sensor, u8 address, u8 value) + const struct et61x251_sensor* sensor, u8 address, + u8 value) { struct usb_device* udev = cam->usbdev; u8* data = cam->control_buffer; @@ -615,7 +617,7 @@ static int et61x251_start_transfer(struct et61x251_device* cam) return 0; free_urbs: - for (i = 0; (i < ET61X251_URBS) && cam->urb[i]; i++) + for (i = 0; (i < ET61X251_URBS) && cam->urb[i]; i++) usb_free_urb(cam->urb[i]); free_buffers: @@ -682,7 +684,7 @@ static u8 et61x251_strtou8(const char* buff, size_t len, ssize_t* count) if (len < 4) { strncpy(str, buff, len); - str[len+1] = '\0'; + str[len] = '\0'; } else { strncpy(str, buff, 4); str[4] = '\0'; @@ -977,30 +979,30 @@ static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, static int et61x251_create_sysfs(struct et61x251_device* cam) { - struct video_device *v4ldev = cam->v4ldev; + struct class_device *classdev = &(cam->v4ldev->class_dev); int err = 0; - if ((err = video_device_create_file(v4ldev, &class_device_attr_reg))) + if ((err = class_device_create_file(classdev, &class_device_attr_reg))) goto err_out; - if ((err = video_device_create_file(v4ldev, &class_device_attr_val))) + if ((err = class_device_create_file(classdev, &class_device_attr_val))) goto err_reg; if (cam->sensor.sysfs_ops) { - if ((err = video_device_create_file(v4ldev, + if ((err = class_device_create_file(classdev, &class_device_attr_i2c_reg))) goto err_val; - if ((err = video_device_create_file(v4ldev, + if ((err = class_device_create_file(classdev, &class_device_attr_i2c_val))) goto err_i2c_reg; } err_i2c_reg: if (cam->sensor.sysfs_ops) - video_device_remove_file(v4ldev, &class_device_attr_i2c_reg); + class_device_remove_file(classdev, &class_device_attr_i2c_reg); err_val: - video_device_remove_file(v4ldev, &class_device_attr_val); + class_device_remove_file(classdev, &class_device_attr_val); err_reg: - video_device_remove_file(v4ldev, &class_device_attr_reg); + class_device_remove_file(classdev, &class_device_attr_reg); err_out: return err; } @@ -1103,7 +1105,8 @@ static int et61x251_init(struct et61x251_device* cam) int err = 0; if (!(cam->state & DEV_INITIALIZED)) { - init_waitqueue_head(&cam->open); + mutex_init(&cam->open_mutex); + init_waitqueue_head(&cam->wait_open); qctrl = s->qctrl; rect = &(s->cropcap.defrect); cam->compression.quality = ET61X251_COMPRESSION_QUALITY; @@ -1177,64 +1180,80 @@ static int et61x251_init(struct et61x251_device* cam) return 0; } +/*****************************************************************************/ -static void et61x251_release_resources(struct et61x251_device* cam) +static void et61x251_release_resources(struct kref *kref) { + struct et61x251_device *cam; + mutex_lock(&et61x251_sysfs_lock); + cam = container_of(kref, struct et61x251_device, kref); + DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor); video_set_drvdata(cam->v4ldev, NULL); video_unregister_device(cam->v4ldev); + usb_put_dev(cam->usbdev); + kfree(cam->control_buffer); + kfree(cam); mutex_unlock(&et61x251_sysfs_lock); - - kfree(cam->control_buffer); } -/*****************************************************************************/ static int et61x251_open(struct inode* inode, struct file* filp) { struct et61x251_device* cam; int err = 0; - /* - This is the only safe way to prevent race conditions with - disconnect - */ - if (!down_read_trylock(&et61x251_disconnect)) + if (!down_read_trylock(&et61x251_dev_lock)) return -ERESTARTSYS; cam = video_get_drvdata(video_devdata(filp)); - if (mutex_lock_interruptible(&cam->dev_mutex)) { - up_read(&et61x251_disconnect); + if (wait_for_completion_interruptible(&cam->probe)) { + up_read(&et61x251_dev_lock); return -ERESTARTSYS; } + kref_get(&cam->kref); + + if (mutex_lock_interruptible(&cam->open_mutex)) { + kref_put(&cam->kref, et61x251_release_resources); + up_read(&et61x251_dev_lock); + return -ERESTARTSYS; + } + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + err = -ENODEV; + goto out; + } + if (cam->users) { - DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor); + DBG(2, "Device /dev/video%d is already in use", + cam->v4ldev->minor); + DBG(3, "Simultaneous opens are not supported"); if ((filp->f_flags & O_NONBLOCK) || (filp->f_flags & O_NDELAY)) { err = -EWOULDBLOCK; goto out; } - mutex_unlock(&cam->dev_mutex); - err = wait_event_interruptible_exclusive(cam->open, - cam->state & DEV_DISCONNECTED + DBG(2, "A blocking open() has been requested. Wait for the " + "device to be released..."); + up_read(&et61x251_dev_lock); + err = wait_event_interruptible_exclusive(cam->wait_open, + (cam->state & DEV_DISCONNECTED) || !cam->users); - if (err) { - up_read(&et61x251_disconnect); - return err; - } + down_read(&et61x251_dev_lock); + if (err) + goto out; if (cam->state & DEV_DISCONNECTED) { - up_read(&et61x251_disconnect); - return -ENODEV; + err = -ENODEV; + goto out; } - mutex_lock(&cam->dev_mutex); } - if (cam->state & DEV_MISCONFIGURED) { err = et61x251_init(cam); if (err) { @@ -1259,36 +1278,32 @@ static int et61x251_open(struct inode* inode, struct file* filp) DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor); out: - mutex_unlock(&cam->dev_mutex); - up_read(&et61x251_disconnect); + mutex_unlock(&cam->open_mutex); + if (err) + kref_put(&cam->kref, et61x251_release_resources); + up_read(&et61x251_dev_lock); return err; } static int et61x251_release(struct inode* inode, struct file* filp) { - struct et61x251_device* cam = video_get_drvdata(video_devdata(filp)); + struct et61x251_device* cam; - mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */ + down_write(&et61x251_dev_lock); - et61x251_stop_transfer(cam); + cam = video_get_drvdata(video_devdata(filp)); + et61x251_stop_transfer(cam); et61x251_release_buffers(cam); - - if (cam->state & DEV_DISCONNECTED) { - et61x251_release_resources(cam); - usb_put_dev(cam->usbdev); - mutex_unlock(&cam->dev_mutex); - kfree(cam); - return 0; - } - cam->users--; - wake_up_interruptible_nr(&cam->open, 1); + wake_up_interruptible_nr(&cam->wait_open, 1); DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor); - mutex_unlock(&cam->dev_mutex); + kref_put(&cam->kref, et61x251_release_resources); + + up_write(&et61x251_dev_lock); return 0; } @@ -1324,7 +1339,7 @@ et61x251_read(struct file* filp, char __user * buf, DBG(3, "Close and open the device again to choose the read " "method"); mutex_unlock(&cam->fileop_mutex); - return -EINVAL; + return -EBUSY; } if (cam->io == IO_NONE) { @@ -1504,7 +1519,12 @@ static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma) return -EIO; } - if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) || + if (!(vma->vm_flags & (VM_WRITE | VM_READ))) { + mutex_unlock(&cam->fileop_mutex); + return -EACCES; + } + + if (cam->io != IO_MMAP || size != PAGE_ALIGN(cam->frame[0].buf.length)) { mutex_unlock(&cam->fileop_mutex); return -EINVAL; @@ -1535,7 +1555,6 @@ static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma) vma->vm_ops = &et61x251_vm_ops; vma->vm_private_data = &cam->frame[i]; - et61x251_vm_open(vma); mutex_unlock(&cam->fileop_mutex); @@ -1764,7 +1783,7 @@ et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg) if (cam->frame[i].vma_use_count) { DBG(3, "VIDIOC_S_CROP failed. " "Unmap the buffers first."); - return -EINVAL; + return -EBUSY; } /* Preserve R,G or B origin */ @@ -1921,6 +1940,8 @@ et61x251_vidioc_g_fmt(struct et61x251_device* cam, void __user * arg) if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; + pfmt->colorspace = (pfmt->pixelformat == V4L2_PIX_FMT_ET61X251) ? + 0 : V4L2_COLORSPACE_SRGB; pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_ET61X251) ? 0 : (pfmt->width * pfmt->priv) / 8; pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); @@ -1996,6 +2017,8 @@ et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd, pix->pixelformat != V4L2_PIX_FMT_SBGGR8) pix->pixelformat = pfmt->pixelformat; pix->priv = pfmt->priv; /* bpp */ + pix->colorspace = (pix->pixelformat == V4L2_PIX_FMT_ET61X251) ? + 0 : V4L2_COLORSPACE_SRGB; pix->colorspace = pfmt->colorspace; pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_ET61X251) ? 0 : (pix->width * pix->priv) / 8; @@ -2013,7 +2036,7 @@ et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd, if (cam->frame[i].vma_use_count) { DBG(3, "VIDIOC_S_FMT failed. " "Unmap the buffers first."); - return -EINVAL; + return -EBUSY; } if (cam->stream == STREAM_ON) @@ -2129,14 +2152,14 @@ et61x251_vidioc_reqbufs(struct et61x251_device* cam, void __user * arg) if (cam->io == IO_READ) { DBG(3, "Close and open the device again to choose the mmap " "I/O method"); - return -EINVAL; + return -EBUSY; } for (i = 0; i < cam->nbuffers; i++) if (cam->frame[i].vma_use_count) { DBG(3, "VIDIOC_REQBUFS failed. " "Previous buffers are still mapped."); - return -EINVAL; + return -EBUSY; } if (cam->stream == STREAM_ON) @@ -2284,9 +2307,6 @@ et61x251_vidioc_streamon(struct et61x251_device* cam, void __user * arg) if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) return -EINVAL; - if (list_empty(&cam->inqueue)) - return -EINVAL; - cam->stream = STREAM_ON; DBG(3, "Stream on"); @@ -2535,8 +2555,6 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) goto fail; } - mutex_init(&cam->dev_mutex); - DBG(2, "ET61X[12]51 PC Camera Controller detected " "(vid/pid 0x%04X:0x%04X)",id->idVendor, id->idProduct); @@ -2568,7 +2586,7 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) cam->v4ldev->release = video_device_release; video_set_drvdata(cam->v4ldev, cam); - mutex_lock(&cam->dev_mutex); + init_completion(&cam->probe); err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, video_nr[dev_nr]); @@ -2578,7 +2596,7 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) DBG(1, "Free /dev/videoX node not found"); video_nr[dev_nr] = -1; dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0; - mutex_unlock(&cam->dev_mutex); + complete_all(&cam->probe); goto fail; } @@ -2599,11 +2617,15 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) "device controlling. Error #%d", err); #else DBG(2, "Optional device control through 'sysfs' interface disabled"); + DBG(3, "Compile the kernel with the 'CONFIG_VIDEO_ADV_DEBUG' " + "configuration option to enable it."); #endif usb_set_intfdata(intf, cam); + kref_init(&cam->kref); + usb_get_dev(cam->usbdev); - mutex_unlock(&cam->dev_mutex); + complete_all(&cam->probe); return 0; @@ -2620,40 +2642,31 @@ fail: static void et61x251_usb_disconnect(struct usb_interface* intf) { - struct et61x251_device* cam = usb_get_intfdata(intf); - - if (!cam) - return; + struct et61x251_device* cam; - down_write(&et61x251_disconnect); + down_write(&et61x251_dev_lock); - mutex_lock(&cam->dev_mutex); + cam = usb_get_intfdata(intf); DBG(2, "Disconnecting %s...", cam->v4ldev->name); - wake_up_interruptible_all(&cam->open); - if (cam->users) { DBG(2, "Device /dev/video%d is open! Deregistration and " - "memory deallocation are deferred on close.", + "memory deallocation are deferred.", cam->v4ldev->minor); cam->state |= DEV_MISCONFIGURED; et61x251_stop_transfer(cam); cam->state |= DEV_DISCONNECTED; wake_up_interruptible(&cam->wait_frame); wake_up(&cam->wait_stream); - usb_get_dev(cam->usbdev); - } else { + } else cam->state |= DEV_DISCONNECTED; - et61x251_release_resources(cam); - } - mutex_unlock(&cam->dev_mutex); + wake_up_interruptible_all(&cam->wait_open); - if (!cam->users) - kfree(cam); + kref_put(&cam->kref, et61x251_release_resources); - up_write(&et61x251_disconnect); + up_write(&et61x251_dev_lock); } diff --git a/drivers/media/video/et61x251/et61x251_sensor.h b/drivers/media/video/et61x251/et61x251_sensor.h index 5fadb5d..e145863 100644 --- a/drivers/media/video/et61x251/et61x251_sensor.h +++ b/drivers/media/video/et61x251/et61x251_sensor.h @@ -22,7 +22,7 @@ #define _ET61X251_SENSOR_H_ #include <linux/usb.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <linux/device.h> #include <linux/stddef.h> #include <linux/errno.h> @@ -47,7 +47,7 @@ et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id); extern void et61x251_attach_sensor(struct et61x251_device* cam, - struct et61x251_sensor* sensor); + const struct et61x251_sensor* sensor); /*****************************************************************************/ @@ -56,10 +56,10 @@ extern int et61x251_read_reg(struct et61x251_device*, u16 index); extern int et61x251_i2c_write(struct et61x251_device*, u8 address, u8 value); extern int et61x251_i2c_read(struct et61x251_device*, u8 address); extern int et61x251_i2c_try_write(struct et61x251_device*, - struct et61x251_sensor*, u8 address, + const struct et61x251_sensor*, u8 address, u8 value); extern int et61x251_i2c_try_read(struct et61x251_device*, - struct et61x251_sensor*, u8 address); + const struct et61x251_sensor*, u8 address); extern int et61x251_i2c_raw_write(struct et61x251_device*, u8 n, u8 data1, u8 data2, u8 data3, u8 data4, u8 data5, u8 data6, u8 data7, u8 data8, u8 address); diff --git a/drivers/media/video/et61x251/et61x251_tas5130d1b.c b/drivers/media/video/et61x251/et61x251_tas5130d1b.c index b066434..04b7fbb 100644 --- a/drivers/media/video/et61x251/et61x251_tas5130d1b.c +++ b/drivers/media/video/et61x251/et61x251_tas5130d1b.c @@ -69,7 +69,7 @@ static int tas5130d1b_set_ctrl(struct et61x251_device* cam, } -static struct et61x251_sensor tas5130d1b = { +static const struct et61x251_sensor tas5130d1b = { .name = "TAS5130D1B", .interface = ET61X251_I2C_3WIRES, .rsta = ET61X251_I2C_RSTA_STOP, diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index ed92b6f..2d709e0 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -37,6 +37,7 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/i2c.h> +#include <linux/i2c-id.h> #include <linux/workqueue.h> #include <asm/semaphore.h> @@ -60,21 +61,22 @@ MODULE_PARM_DESC(hauppauge, "Specify Hauppauge remote: 0=black, 1=grey (defaults /* ----------------------------------------------------------------------- */ -static int get_key_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, + int size, int offset) { - unsigned char buf[3]; + unsigned char buf[6]; int start, range, toggle, dev, code; /* poll IR chip */ - if (3 != i2c_master_recv(&ir->c,buf,3)) + if (size != i2c_master_recv(&ir->c,buf,size)) return -EIO; /* split rc5 data block ... */ - start = (buf[0] >> 7) & 1; - range = (buf[0] >> 6) & 1; - toggle = (buf[0] >> 5) & 1; - dev = buf[0] & 0x1f; - code = (buf[1] >> 2) & 0x3f; + start = (buf[offset] >> 7) & 1; + range = (buf[offset] >> 6) & 1; + toggle = (buf[offset] >> 5) & 1; + dev = buf[offset] & 0x1f; + code = (buf[offset+1] >> 2) & 0x3f; /* rc5 has two start bits * the first bit must be one @@ -96,6 +98,16 @@ static int get_key_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } +static inline int get_key_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + return get_key_haup_common (ir, ir_key, ir_raw, 3, 0); +} + +static inline int get_key_haup_xvr(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + return get_key_haup_common (ir, ir_key, ir_raw, 6, 3); +} + static int get_key_pixelview(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { unsigned char b; @@ -270,8 +282,9 @@ static void ir_timer(unsigned long data) static void ir_work(struct work_struct *work) { struct IR_i2c *ir = container_of(work, struct IR_i2c, work); + ir_key_poll(ir); - mod_timer(&ir->timer, jiffies+HZ/10); + mod_timer(&ir->timer, jiffies + msecs_to_jiffies(100)); } /* ----------------------------------------------------------------------- */ @@ -354,9 +367,21 @@ static int ir_attach(struct i2c_adapter *adap, int addr, case 0x7a: case 0x47: case 0x71: - /* Handled by saa7134-input */ - name = "SAA713x remote"; - ir_type = IR_TYPE_OTHER; + if (adap->id == I2C_HW_B_CX2388x) { + /* Handled by cx88-input */ + name = "CX2388x remote"; + ir_type = IR_TYPE_RC5; + ir->get_key = get_key_haup_xvr; + if (hauppauge == 1) { + ir_codes = ir_codes_hauppauge_new; + } else { + ir_codes = ir_codes_rc5_tv; + } + } else { + /* Handled by saa7134-input */ + name = "SAA713x remote"; + ir_type = IR_TYPE_OTHER; + } break; default: /* shouldn't happen */ @@ -450,6 +475,7 @@ static int ir_probe(struct i2c_adapter *adap) static const int probe_bttv[] = { 0x1a, 0x18, 0x4b, 0x64, 0x30, -1}; static const int probe_saa7134[] = { 0x7a, 0x47, 0x71, -1 }; static const int probe_em28XX[] = { 0x30, 0x47, -1 }; + static const int probe_cx88[] = { 0x18, 0x71, -1 }; const int *probe = NULL; struct i2c_client c; unsigned char buf; @@ -468,6 +494,9 @@ static int ir_probe(struct i2c_adapter *adap) case I2C_HW_B_EM28XX: probe = probe_em28XX; break; + case I2C_HW_B_CX2388x: + probe = probe_cx88; + break; } if (NULL == probe) return 0; diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index efc6635..4c93466 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -181,7 +181,7 @@ MODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC"); MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K"); MODULE_PARM_DESC(debug, "Debug level (bitmask). Default: errors only\n" - "\t\t\t(debug = 511 gives full debugging)"); + "\t\t\t(debug = 1023 gives full debugging)"); MODULE_PARM_DESC(ivtv_pci_latency, "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n" "\t\t\tDefault: Yes"); @@ -339,6 +339,7 @@ static void ivtv_process_eeprom(struct ivtv *itv) /* In a few cases the PCI subsystem IDs do not correctly identify the card. A better method is to check the model number from the eeprom instead. */ + case 30012 ... 30039: /* Low profile PVR250 */ case 32000 ... 32999: case 48000 ... 48099: /* 48??? range are PVR250s with a cx23415 */ case 48400 ... 48599: @@ -622,6 +623,7 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv) itv->enc_mbox.max_mbox = 2; /* the encoder has 3 mailboxes (0-2) */ itv->dec_mbox.max_mbox = 1; /* the decoder has 2 mailboxes (0-1) */ + mutex_init(&itv->serialize_lock); mutex_init(&itv->i2c_bus_lock); mutex_init(&itv->udma.lock); @@ -1288,10 +1290,7 @@ static void ivtv_remove(struct pci_dev *pci_dev) IVTV_DEBUG_INFO(" Releasing irq.\n"); free_irq(itv->dev->irq, (void *)itv); - - if (itv->dev) { - ivtv_iounmap(itv); - } + ivtv_iounmap(itv); IVTV_DEBUG_INFO(" Releasing mem.\n"); release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); @@ -1326,9 +1325,9 @@ static int module_start(void) return -1; } - if (ivtv_debug < 0 || ivtv_debug > 511) { + if (ivtv_debug < 0 || ivtv_debug > 1023) { ivtv_debug = 0; - printk(KERN_INFO "ivtv: debug value must be >= 0 and <= 511!\n"); + printk(KERN_INFO "ivtv: debug value must be >= 0 and <= 1023!\n"); } if (pci_register_driver(&ivtv_pci_driver)) { diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h index e6e56f1..6c1a85f 100644 --- a/drivers/media/video/ivtv/ivtv-driver.h +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -268,6 +268,8 @@ extern const u32 yuv_offset[4]; #define IVTV_DBGFLG_IRQ (1 << 6) #define IVTV_DBGFLG_DEC (1 << 7) #define IVTV_DBGFLG_YUV (1 << 8) +/* Flag to turn on high volume debugging */ +#define IVTV_DBGFLG_HIGHVOL (1 << 9) /* NOTE: extra space before comma in 'itv->num , ## args' is required for gcc-2.95, otherwise it won't compile. */ @@ -286,6 +288,21 @@ extern const u32 yuv_offset[4]; #define IVTV_DEBUG_DEC(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args) #define IVTV_DEBUG_YUV(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args) +#define IVTV_DEBUG_HIGH_VOL(x, type, fmt, args...) \ + do { \ + if (((x) & ivtv_debug) && (ivtv_debug & IVTV_DBGFLG_HIGHVOL)) \ + printk(KERN_INFO "ivtv%d " type ": " fmt, itv->num , ## args); \ + } while (0) +#define IVTV_DEBUG_HI_WARN(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_WARN, "warning", fmt , ## args) +#define IVTV_DEBUG_HI_INFO(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_INFO, "info",fmt , ## args) +#define IVTV_DEBUG_HI_API(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_API, "api", fmt , ## args) +#define IVTV_DEBUG_HI_DMA(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_DMA, "dma", fmt , ## args) +#define IVTV_DEBUG_HI_IOCTL(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args) +#define IVTV_DEBUG_HI_I2C(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_I2C, "i2c", fmt , ## args) +#define IVTV_DEBUG_HI_IRQ(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_IRQ, "irq", fmt , ## args) +#define IVTV_DEBUG_HI_DEC(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_DEC, "dec", fmt , ## args) +#define IVTV_DEBUG_HI_YUV(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_YUV, "yuv", fmt , ## args) + #define IVTV_FB_DEBUG(x, type, fmt, args...) \ do { \ if ((x) & ivtv_debug) \ @@ -650,7 +667,6 @@ struct vbi_info { /* convenience pointer to sliced struct in vbi_in union */ struct v4l2_sliced_vbi_format *sliced_in; u32 service_set_in; - u32 service_set_out; int insert_mpeg; /* Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines. @@ -723,6 +739,7 @@ struct ivtv { int search_pack_header; spinlock_t dma_reg_lock; /* lock access to DMA engine registers */ + struct mutex serialize_lock; /* lock used to serialize starting streams */ /* User based DMA for OSD */ struct ivtv_user_dma udma; diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c index 555d5e6..ee7e884 100644 --- a/drivers/media/video/ivtv/ivtv-fileops.c +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -406,7 +406,7 @@ static ssize_t ivtv_read_pos(struct ivtv_stream *s, char __user *ubuf, size_t co ssize_t rc = count ? ivtv_read(s, ubuf, count, non_block) : 0; struct ivtv *itv = s->itv; - IVTV_DEBUG_INFO("read %zd from %s, got %zd\n", count, s->name, rc); + IVTV_DEBUG_HI_INFO("read %zd from %s, got %zd\n", count, s->name, rc); if (rc > 0) pos += rc; return rc; @@ -497,7 +497,7 @@ ssize_t ivtv_v4l2_read(struct file * filp, char __user *buf, size_t count, loff_ struct ivtv_stream *s = &itv->streams[id->type]; int rc; - IVTV_DEBUG_IOCTL("read %zd bytes from %s\n", count, s->name); + IVTV_DEBUG_HI_IOCTL("read %zd bytes from %s\n", count, s->name); rc = ivtv_start_capture(id); if (rc) @@ -535,7 +535,7 @@ ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t c int rc; DEFINE_WAIT(wait); - IVTV_DEBUG_IOCTL("write %zd bytes to %s\n", count, s->name); + IVTV_DEBUG_HI_IOCTL("write %zd bytes to %s\n", count, s->name); if (s->type != IVTV_DEC_STREAM_TYPE_MPG && s->type != IVTV_DEC_STREAM_TYPE_YUV && @@ -643,7 +643,7 @@ retry: to transfer the rest. */ if (count && !(filp->f_flags & O_NONBLOCK)) goto retry; - IVTV_DEBUG_INFO("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused); + IVTV_DEBUG_HI_INFO("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused); return bytes_written; } diff --git a/drivers/media/video/ivtv/ivtv-firmware.c b/drivers/media/video/ivtv/ivtv-firmware.c index d4c910b..2b6208a 100644 --- a/drivers/media/video/ivtv/ivtv-firmware.c +++ b/drivers/media/video/ivtv/ivtv-firmware.c @@ -56,9 +56,7 @@ retry: volatile u32 __iomem *dst = (volatile u32 __iomem *)mem; const u32 *src = (const u32 *)fw->data; - /* temporarily allow 256 KB encoding firmwares as well for - compatibility with blackbird cards */ - if (fw->size != size && fw->size != 256 * 1024) { + if (fw->size != size) { /* Due to race conditions in firmware loading (esp. with udev <0.95) the wrong file was sometimes loaded. So we check filesizes to see if at least the right-sized file was loaded. If not, then we diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c index bc8f8ca..676418c 100644 --- a/drivers/media/video/ivtv/ivtv-gpio.c +++ b/drivers/media/video/ivtv/ivtv-gpio.c @@ -115,8 +115,7 @@ void ivtv_reset_ir_gpio(struct ivtv *itv) curout = (curout & ~0xF) | 1; write_reg(curout, IVTV_REG_GPIO_OUT); /* We could use something else for smaller time */ - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); + schedule_timeout_interruptible(msecs_to_jiffies(1)); curout |= 2; write_reg(curout, IVTV_REG_GPIO_OUT); curdir &= ~0x80; @@ -138,13 +137,11 @@ int ivtv_reset_tuner_gpio(enum v4l2_tuner_type mode, void *priv, int ptr) curout &= ~(1 << 12); write_reg(curout, IVTV_REG_GPIO_OUT); - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); + schedule_timeout_interruptible(msecs_to_jiffies(1)); curout |= (1 << 12); write_reg(curout, IVTV_REG_GPIO_OUT); - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(1); + schedule_timeout_interruptible(msecs_to_jiffies(1)); return 0; } diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index 57af176..4773453 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -1159,7 +1159,7 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void memset(fb, 0, sizeof(*fb)); if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) - break; + return -EINVAL; fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY | V4L2_FBUF_CAP_CHROMAKEY | V4L2_FBUF_CAP_LOCAL_ALPHA | V4L2_FBUF_CAP_GLOBAL_ALPHA; fb->fmt.pixelformat = itv->osd_pixelformat; @@ -1179,7 +1179,7 @@ int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void struct v4l2_framebuffer *fb = arg; if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) - break; + return -EINVAL; itv->osd_global_alpha_state = (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0; itv->osd_local_alpha_state = (fb->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) != 0; itv->osd_color_key_state = (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0; diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c index ba98bf0..1a3ee46 100644 --- a/drivers/media/video/ivtv/ivtv-irq.c +++ b/drivers/media/video/ivtv/ivtv-irq.c @@ -48,7 +48,7 @@ static void ivtv_pio_work_handler(struct ivtv *itv) struct list_head *p; int i = 0; - IVTV_DEBUG_DMA("ivtv_pio_work_handler\n"); + IVTV_DEBUG_HI_DMA("ivtv_pio_work_handler\n"); if (itv->cur_pio_stream < 0 || itv->cur_pio_stream >= IVTV_MAX_STREAMS || s->v4l2dev == NULL || !ivtv_use_pio(s)) { itv->cur_pio_stream = -1; @@ -56,7 +56,7 @@ static void ivtv_pio_work_handler(struct ivtv *itv) write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44); return; } - IVTV_DEBUG_DMA("Process PIO %s\n", s->name); + IVTV_DEBUG_HI_DMA("Process PIO %s\n", s->name); buf = list_entry(s->q_dma.list.next, struct ivtv_buffer, list); list_for_each(p, &s->q_dma.list) { struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list); @@ -187,7 +187,7 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MA bytes_needed += UVsize; } - IVTV_DEBUG_DMA("%s %s: 0x%08x bytes at 0x%08x\n", + IVTV_DEBUG_HI_DMA("%s %s: 0x%08x bytes at 0x%08x\n", ivtv_use_pio(s) ? "PIO" : "DMA", s->name, bytes_needed, offset); rc = ivtv_queue_move(s, &s->q_free, &s->q_full, &s->q_predma, bytes_needed); @@ -242,7 +242,7 @@ static void dma_post(struct ivtv_stream *s) u32 *u32buf; int x = 0; - IVTV_DEBUG_DMA("%s %s completed (%x)\n", ivtv_use_pio(s) ? "PIO" : "DMA", + IVTV_DEBUG_HI_DMA("%s %s completed (%x)\n", ivtv_use_pio(s) ? "PIO" : "DMA", s->name, s->dma_offset); list_for_each(p, &s->q_dma.list) { buf = list_entry(p, struct ivtv_buffer, list); @@ -321,7 +321,7 @@ void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock) unsigned long flags = 0; int idx = 0; - IVTV_DEBUG_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset); + IVTV_DEBUG_HI_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset); buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list); list_for_each(p, &s->q_predma.list) { struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list); @@ -368,7 +368,7 @@ static void ivtv_dma_enc_start(struct ivtv_stream *s) struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; int i; - IVTV_DEBUG_DMA("start %s for %s\n", ivtv_use_dma(s) ? "DMA" : "PIO", s->name); + IVTV_DEBUG_HI_DMA("start %s for %s\n", ivtv_use_dma(s) ? "DMA" : "PIO", s->name); if (s->q_predma.bytesused) ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused); @@ -397,7 +397,7 @@ static void ivtv_dma_enc_start(struct ivtv_stream *s) itv->vbi.dma_offset = s_vbi->dma_offset; s_vbi->SG_length = 0; set_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags); - IVTV_DEBUG_DMA("include DMA for %s\n", s->name); + IVTV_DEBUG_HI_DMA("include DMA for %s\n", s->name); } /* Mark last buffer size for Interrupt flag */ @@ -431,7 +431,7 @@ static void ivtv_dma_dec_start(struct ivtv_stream *s) if (s->q_predma.bytesused) ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused); - IVTV_DEBUG_DMA("start DMA for %s\n", s->name); + IVTV_DEBUG_HI_DMA("start DMA for %s\n", s->name); /* put SG Handle into register 0x0c */ write_reg(s->SG_handle, IVTV_REG_DECDMAADDR); write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER); @@ -447,7 +447,7 @@ static void ivtv_irq_dma_read(struct ivtv *itv) struct ivtv_buffer *buf; int hw_stream_type; - IVTV_DEBUG_IRQ("DEC DMA READ\n"); + IVTV_DEBUG_HI_IRQ("DEC DMA READ\n"); del_timer(&itv->dma_timer); if (read_reg(IVTV_REG_DMASTATUS) & 0x14) { IVTV_DEBUG_WARN("DEC DMA ERROR %x\n", read_reg(IVTV_REG_DMASTATUS)); @@ -462,7 +462,7 @@ static void ivtv_irq_dma_read(struct ivtv *itv) s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; hw_stream_type = 0; } - IVTV_DEBUG_DMA("DEC DATA READ %s: %d\n", s->name, s->q_dma.bytesused); + IVTV_DEBUG_HI_DMA("DEC DATA READ %s: %d\n", s->name, s->q_dma.bytesused); ivtv_stream_sync_for_cpu(s); @@ -495,7 +495,7 @@ static void ivtv_irq_enc_dma_complete(struct ivtv *itv) del_timer(&itv->dma_timer); ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data); - IVTV_DEBUG_IRQ("ENC DMA COMPLETE %x %d\n", data[0], data[1]); + IVTV_DEBUG_HI_IRQ("ENC DMA COMPLETE %x %d\n", data[0], data[1]); if (test_and_clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags)) data[1] = 3; else if (data[1] > 2) @@ -532,7 +532,7 @@ static void ivtv_irq_enc_pio_complete(struct ivtv *itv) return; } s = &itv->streams[itv->cur_pio_stream]; - IVTV_DEBUG_IRQ("ENC PIO COMPLETE %s\n", s->name); + IVTV_DEBUG_HI_IRQ("ENC PIO COMPLETE %s\n", s->name); s->SG_length = 0; clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags); clear_bit(IVTV_F_I_PIO, &itv->i_flags); @@ -590,7 +590,7 @@ static void ivtv_irq_enc_start_cap(struct ivtv *itv) /* Get DMA destination and size arguments from card */ ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA, data); - IVTV_DEBUG_IRQ("ENC START CAP %d: %08x %08x\n", data[0], data[1], data[2]); + IVTV_DEBUG_HI_IRQ("ENC START CAP %d: %08x %08x\n", data[0], data[1], data[2]); if (data[0] > 2 || data[1] == 0 || data[2] == 0) { IVTV_DEBUG_WARN("Unknown input: %08x %08x %08x\n", @@ -610,7 +610,7 @@ static void ivtv_irq_enc_vbi_cap(struct ivtv *itv) u32 data[CX2341X_MBOX_MAX_DATA]; struct ivtv_stream *s; - IVTV_DEBUG_IRQ("ENC START VBI CAP\n"); + IVTV_DEBUG_HI_IRQ("ENC START VBI CAP\n"); s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; /* If more than two VBI buffers are pending, then @@ -644,7 +644,7 @@ static void ivtv_irq_dec_vbi_reinsert(struct ivtv *itv) u32 data[CX2341X_MBOX_MAX_DATA]; struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI]; - IVTV_DEBUG_IRQ("DEC VBI REINSERT\n"); + IVTV_DEBUG_HI_IRQ("DEC VBI REINSERT\n"); if (test_bit(IVTV_F_S_CLAIMED, &s->s_flags) && !stream_enc_dma_append(s, data)) { set_bit(IVTV_F_S_PIO_PENDING, &s->s_flags); @@ -669,7 +669,7 @@ static void ivtv_irq_dec_data_req(struct ivtv *itv) itv->dma_data_req_offset = data[1]; s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; } - IVTV_DEBUG_IRQ("DEC DATA REQ %s: %d %08x %u\n", s->name, s->q_full.bytesused, + IVTV_DEBUG_HI_IRQ("DEC DATA REQ %s: %d %08x %u\n", s->name, s->q_full.bytesused, itv->dma_data_req_offset, itv->dma_data_req_size); if (itv->dma_data_req_size == 0 || s->q_full.bytesused < itv->dma_data_req_size) { set_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags); @@ -791,10 +791,10 @@ irqreturn_t ivtv_irq_handler(int irq, void *dev_id) /* Exclude interrupts noted below from the output, otherwise the log is flooded with these messages */ if (combo & ~0xff6d0400) - IVTV_DEBUG_IRQ("======= valid IRQ bits: 0x%08x ======\n", combo); + IVTV_DEBUG_HI_IRQ("======= valid IRQ bits: 0x%08x ======\n", combo); if (combo & IVTV_IRQ_DEC_DMA_COMPLETE) { - IVTV_DEBUG_IRQ("DEC DMA COMPLETE\n"); + IVTV_DEBUG_HI_IRQ("DEC DMA COMPLETE\n"); } if (combo & IVTV_IRQ_DMA_READ) { diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index 6af88ae..2871171 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -446,6 +446,9 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) if (s->v4l2dev == NULL) return -EINVAL; + /* Big serialization lock to ensure no two streams are started + simultaneously: that can give all sorts of weird results. */ + mutex_lock(&itv->serialize_lock); IVTV_DEBUG_INFO("Start encoder stream %s\n", s->name); switch (s->type) { @@ -487,6 +490,7 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) 0, sizeof(itv->vbi.sliced_mpeg_size)); break; default: + mutex_unlock(&itv->serialize_lock); return -EINVAL; } s->subtype = subtype; @@ -568,6 +572,7 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) if (ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, captype, subtype)) { IVTV_DEBUG_WARN( "Error starting capture!\n"); + mutex_unlock(&itv->serialize_lock); return -EINVAL; } @@ -583,6 +588,7 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) /* you're live! sit back and await interrupts :) */ atomic_inc(&itv->capturing); + mutex_unlock(&itv->serialize_lock); return 0; } @@ -762,17 +768,6 @@ int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end) /* when: 0 = end of GOP 1 = NOW!, type: 0 = mpeg, subtype: 3 = video+audio */ ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, stopmode, cap_type, s->subtype); - /* only run these if we're shutting down the last cap */ - if (atomic_read(&itv->capturing) - 1 == 0) { - /* event notification (off) */ - if (test_and_clear_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) { - /* type: 0 = refresh */ - /* on/off: 0 = off, intr: 0x10000000, mbox_id: -1: none */ - ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_ENC_VIM_RST, -1); - ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST); - } - } - then = jiffies; if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) { @@ -812,7 +807,6 @@ int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end) then = jiffies; /* Make sure DMA is complete */ add_wait_queue(&s->waitq, &wait); - set_current_state(TASK_INTERRUPTIBLE); do { /* check if DMA is pending */ if ((s->type == IVTV_ENC_STREAM_TYPE_MPG) && /* MPG Only */ @@ -827,9 +821,7 @@ int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end) } else if (read_reg(IVTV_REG_DMASTATUS) & 0x02) { break; } - - ivtv_sleep_timeout(HZ / 100, 1); - } while (then + HZ * 2 > jiffies); + } while (!ivtv_sleep_timeout(HZ / 100, 1) && then + HZ * 2 > jiffies); set_current_state(TASK_RUNNING); remove_wait_queue(&s->waitq, &wait); @@ -840,17 +832,30 @@ int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end) /* Clear capture and no-read bits */ clear_bit(IVTV_F_S_STREAMING, &s->s_flags); + /* ensure these global cleanup actions are done only once */ + mutex_lock(&itv->serialize_lock); + if (s->type == IVTV_ENC_STREAM_TYPE_VBI) ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP); if (atomic_read(&itv->capturing) > 0) { + mutex_unlock(&itv->serialize_lock); return 0; } /* Set the following Interrupt mask bits for capture */ ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE); + /* event notification (off) */ + if (test_and_clear_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) { + /* type: 0 = refresh */ + /* on/off: 0 = off, intr: 0x10000000, mbox_id: -1: none */ + ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_ENC_VIM_RST, -1); + ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST); + } + wake_up(&s->waitq); + mutex_unlock(&itv->serialize_lock); return 0; } diff --git a/drivers/media/video/ivtv/ivtv-vbi.c b/drivers/media/video/ivtv/ivtv-vbi.c index 3ba46e0..a7282a9 100644 --- a/drivers/media/video/ivtv/ivtv-vbi.c +++ b/drivers/media/video/ivtv/ivtv-vbi.c @@ -219,31 +219,23 @@ ssize_t ivtv_write_vbi(struct ivtv *itv, const char __user *ubuf, size_t count) int found_cc = 0; int cc_pos = itv->vbi.cc_pos; - if (itv->vbi.service_set_out == 0) - return -EPERM; - while (count >= sizeof(struct v4l2_sliced_vbi_data)) { switch (p->id) { case V4L2_SLICED_CAPTION_525: - if (p->id == V4L2_SLICED_CAPTION_525 && - p->line == 21 && - (itv->vbi.service_set_out & - V4L2_SLICED_CAPTION_525) == 0) { - break; - } - found_cc = 1; - if (p->field) { - cc[2] = p->data[0]; - cc[3] = p->data[1]; - } else { - cc[0] = p->data[0]; - cc[1] = p->data[1]; + if (p->line == 21) { + found_cc = 1; + if (p->field) { + cc[2] = p->data[0]; + cc[3] = p->data[1]; + } else { + cc[0] = p->data[0]; + cc[1] = p->data[1]; + } } break; case V4L2_SLICED_VPS: - if (p->line == 16 && p->field == 0 && - (itv->vbi.service_set_out & V4L2_SLICED_VPS)) { + if (p->line == 16 && p->field == 0) { itv->vbi.vps[0] = p->data[2]; itv->vbi.vps[1] = p->data[8]; itv->vbi.vps[2] = p->data[9]; @@ -255,8 +247,7 @@ ssize_t ivtv_write_vbi(struct ivtv *itv, const char __user *ubuf, size_t count) break; case V4L2_SLICED_WSS_625: - if (p->line == 23 && p->field == 0 && - (itv->vbi.service_set_out & V4L2_SLICED_WSS_625)) { + if (p->line == 23 && p->field == 0) { /* No lock needed for WSS */ itv->vbi.wss = p->data[0] | (p->data[1] << 8); itv->vbi.wss_found = 1; diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index 3bb7d66..507b1d4 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -157,8 +157,7 @@ static int msp_read(struct i2c_client *client, int dev, int addr) break; v4l_warn(client, "I/O error #%d (read 0x%02x/0x%02x)\n", err, dev, addr); - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(msecs_to_jiffies(10)); + schedule_timeout_interruptible(msecs_to_jiffies(10)); } if (err == 3) { v4l_warn(client, "giving up, resetting chip. Sound will go off, sorry folks :-|\n"); @@ -197,8 +196,7 @@ static int msp_write(struct i2c_client *client, int dev, int addr, int val) break; v4l_warn(client, "I/O error #%d (write 0x%02x/0x%02x)\n", err, dev, addr); - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(msecs_to_jiffies(10)); + schedule_timeout_interruptible(msecs_to_jiffies(10)); } if (err == 3) { v4l_warn(client, "giving up, resetting chip. Sound will go off, sorry folks :-|\n"); diff --git a/drivers/media/video/mt20xx.c b/drivers/media/video/mt20xx.c index c7c9f3f8..7549114 100644 --- a/drivers/media/video/mt20xx.c +++ b/drivers/media/video/mt20xx.c @@ -7,7 +7,7 @@ #include <linux/i2c.h> #include <linux/videodev.h> #include <linux/moduleparam.h> -#include <media/tuner.h> +#include "tuner-driver.h" /* ---------------------------------------------------------------------- */ @@ -37,6 +37,19 @@ static char *microtune_part[] = { [ MT2050 ] = "MT2050", }; +struct microtune_priv { + unsigned int xogc; + unsigned int radio_if2; +}; + +static void microtune_release(struct i2c_client *c) +{ + struct tuner *t = i2c_get_clientdata(c); + + kfree(t->priv); + t->priv = NULL; +} + // IsSpurInBand()? static int mt2032_spurcheck(struct i2c_client *c, int f1, int f2, int spectrum_from,int spectrum_to) @@ -218,6 +231,7 @@ static void mt2032_set_if_freq(struct i2c_client *c, unsigned int rfin, unsigned char buf[21]; int lint_try,ret,sel,lock=0; struct tuner *t = i2c_get_clientdata(c); + struct microtune_priv *priv = t->priv; tuner_dbg("mt2032_set_if_freq rfin=%d if1=%d if2=%d from=%d to=%d\n", rfin,if1,if2,from,to); @@ -227,7 +241,7 @@ static void mt2032_set_if_freq(struct i2c_client *c, unsigned int rfin, i2c_master_recv(c,buf,21); buf[0]=0; - ret=mt2032_compute_freq(c,rfin,if1,if2,from,to,&buf[1],&sel,t->xogc); + ret=mt2032_compute_freq(c,rfin,if1,if2,from,to,&buf[1],&sel,priv->xogc); if (ret<0) return; @@ -251,10 +265,10 @@ static void mt2032_set_if_freq(struct i2c_client *c, unsigned int rfin, tuner_dbg("mt2032: re-init PLLs by LINT\n"); buf[0]=7; - buf[1]=0x80 +8+t->xogc; // set LINT to re-init PLLs + buf[1]=0x80 +8+priv->xogc; // set LINT to re-init PLLs i2c_master_send(c,buf,2); mdelay(10); - buf[1]=8+t->xogc; + buf[1]=8+priv->xogc; i2c_master_send(c,buf,2); } @@ -294,17 +308,25 @@ static void mt2032_set_tv_freq(struct i2c_client *c, unsigned int freq) static void mt2032_set_radio_freq(struct i2c_client *c, unsigned int freq) { struct tuner *t = i2c_get_clientdata(c); - int if2 = t->radio_if2; + struct microtune_priv *priv = t->priv; + int if2 = priv->radio_if2; // per Manual for FM tuning: first if center freq. 1085 MHz mt2032_set_if_freq(c, freq * 1000 / 16, 1085*1000*1000,if2,if2,if2); } +static struct tuner_operations mt2032_tuner_ops = { + .set_tv_freq = mt2032_set_tv_freq, + .set_radio_freq = mt2032_set_radio_freq, + .release = microtune_release, +}; + // Initalization as described in "MT203x Programming Procedures", Rev 1.2, Feb.2001 static int mt2032_init(struct i2c_client *c) { struct tuner *t = i2c_get_clientdata(c); + struct microtune_priv *priv = t->priv; unsigned char buf[21]; int ret,xogc,xok=0; @@ -351,23 +373,23 @@ static int mt2032_init(struct i2c_client *c) if (ret!=2) tuner_warn("i2c i/o error: rc == %d (should be 2)\n",ret); } while (xok != 1 ); - t->xogc=xogc; + priv->xogc=xogc; + + memcpy(&t->ops, &mt2032_tuner_ops, sizeof(struct tuner_operations)); - t->set_tv_freq = mt2032_set_tv_freq; - t->set_radio_freq = mt2032_set_radio_freq; return(1); } static void mt2050_set_antenna(struct i2c_client *c, unsigned char antenna) { struct tuner *t = i2c_get_clientdata(c); - unsigned char buf[2]; - int ret; + unsigned char buf[2]; + int ret; - buf[0] = 6; - buf[1] = antenna ? 0x11 : 0x10; - ret=i2c_master_send(c,buf,2); - tuner_dbg("mt2050: enabled antenna connector %d\n", antenna); + buf[0] = 6; + buf[1] = antenna ? 0x11 : 0x10; + ret=i2c_master_send(c,buf,2); + tuner_dbg("mt2050: enabled antenna connector %d\n", antenna); } static void mt2050_set_if_freq(struct i2c_client *c,unsigned int freq, unsigned int if2) @@ -456,12 +478,19 @@ static void mt2050_set_tv_freq(struct i2c_client *c, unsigned int freq) static void mt2050_set_radio_freq(struct i2c_client *c, unsigned int freq) { struct tuner *t = i2c_get_clientdata(c); - int if2 = t->radio_if2; + struct microtune_priv *priv = t->priv; + int if2 = priv->radio_if2; mt2050_set_if_freq(c, freq * 1000 / 16, if2); mt2050_set_antenna(c, radio_antenna); } +static struct tuner_operations mt2050_tuner_ops = { + .set_tv_freq = mt2050_set_tv_freq, + .set_radio_freq = mt2050_set_radio_freq, + .release = microtune_release, +}; + static int mt2050_init(struct i2c_client *c) { struct tuner *t = i2c_get_clientdata(c); @@ -481,28 +510,35 @@ static int mt2050_init(struct i2c_client *c) i2c_master_recv(c,buf,1); tuner_dbg("mt2050: sro is %x\n",buf[0]); - t->set_tv_freq = mt2050_set_tv_freq; - t->set_radio_freq = mt2050_set_radio_freq; + + memcpy(&t->ops, &mt2050_tuner_ops, sizeof(struct tuner_operations)); + return 0; } int microtune_init(struct i2c_client *c) { + struct microtune_priv *priv = NULL; struct tuner *t = i2c_get_clientdata(c); char *name; unsigned char buf[21]; int company_code; + priv = kzalloc(sizeof(struct microtune_priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + t->priv = priv; + + priv->radio_if2 = 10700 * 1000; /* 10.7MHz - FM radio */ + memset(buf,0,sizeof(buf)); - t->set_tv_freq = NULL; - t->set_radio_freq = NULL; - t->standby = NULL; + if (t->std & V4L2_STD_525_60) { tuner_dbg("pinnacle ntsc\n"); - t->radio_if2 = 41300 * 1000; + priv->radio_if2 = 41300 * 1000; } else { tuner_dbg("pinnacle pal\n"); - t->radio_if2 = 33300 * 1000; + priv->radio_if2 = 33300 * 1000; } name = "unknown"; diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c index 3ceb8a6..f8f21dd 100644 --- a/drivers/media/video/ov7670.c +++ b/drivers/media/video/ov7670.c @@ -617,7 +617,7 @@ static struct ov7670_win_size { }, }; -#define N_WIN_SIZES (sizeof(ov7670_win_sizes)/sizeof(ov7670_win_sizes[0])) +#define N_WIN_SIZES (ARRAY_SIZE(ov7670_win_sizes)) /* @@ -1183,7 +1183,7 @@ static struct ov7670_control { .query = ov7670_q_hflip, }, }; -#define N_CONTROLS (sizeof(ov7670_controls)/sizeof(ov7670_controls[0])) +#define N_CONTROLS (ARRAY_SIZE(ov7670_controls)) static struct ov7670_control *ov7670_find_control(__u32 id) { diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 085332a..9c0e8d1 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -1099,7 +1099,7 @@ static int pwc_video_open(struct inode *inode, struct file *file) return -EBUSY; } - down(&pdev->modlock); + mutex_lock(&pdev->modlock); if (!pdev->usb_init) { PWC_DEBUG_OPEN("Doing first time initialization.\n"); pdev->usb_init = 1; @@ -1131,7 +1131,7 @@ static int pwc_video_open(struct inode *inode, struct file *file) if (i < 0) { PWC_DEBUG_OPEN("Failed to allocate buffers memory.\n"); pwc_free_buffers(pdev); - up(&pdev->modlock); + mutex_unlock(&pdev->modlock); return i; } @@ -1172,7 +1172,7 @@ static int pwc_video_open(struct inode *inode, struct file *file) if (i) { PWC_DEBUG_OPEN("Second attempt at set_video_mode failed.\n"); pwc_free_buffers(pdev); - up(&pdev->modlock); + mutex_unlock(&pdev->modlock); return i; } @@ -1181,7 +1181,7 @@ static int pwc_video_open(struct inode *inode, struct file *file) PWC_DEBUG_OPEN("Failed to init ISOC stuff = %d.\n", i); pwc_isoc_cleanup(pdev); pwc_free_buffers(pdev); - up(&pdev->modlock); + mutex_unlock(&pdev->modlock); return i; } @@ -1191,7 +1191,7 @@ static int pwc_video_open(struct inode *inode, struct file *file) pdev->vopen++; file->private_data = vdev; - up(&pdev->modlock); + mutex_unlock(&pdev->modlock); PWC_DEBUG_OPEN("<< video_open() returns 0.\n"); return 0; } @@ -1685,7 +1685,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->angle_range.tilt_max = 2500; } - init_MUTEX(&pdev->modlock); + mutex_init(&pdev->modlock); spin_lock_init(&pdev->ptrlock); pdev->udev = udev; diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index acbb931..910a04f 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -31,7 +31,7 @@ #include <linux/wait.h> #include <linux/smp_lock.h> #include <linux/version.h> -#include <asm/semaphore.h> +#include <linux/mutex.h> #include <asm/errno.h> #include <linux/videodev.h> #include <media/v4l2-common.h> @@ -244,7 +244,7 @@ struct pwc_device int image_read_pos; /* In case we read data in pieces, keep track of were we are in the imagebuffer */ int image_used[MAX_IMAGES]; /* For MCAPTURE and SYNC */ - struct semaphore modlock; /* to prevent races in video_open(), etc */ + struct mutex modlock; /* to prevent races in video_open(), etc */ spinlock_t ptrlock; /* for manipulating the buffer pointers */ /*** motorized pan/tilt feature */ diff --git a/drivers/media/video/saa7111.c b/drivers/media/video/saa7111.c index c1a392e..7ae2d64 100644 --- a/drivers/media/video/saa7111.c +++ b/drivers/media/video/saa7111.c @@ -37,23 +37,23 @@ #include <linux/slab.h> #include <linux/mm.h> #include <linux/signal.h> +#include <linux/types.h> +#include <linux/i2c.h> #include <asm/io.h> #include <asm/pgtable.h> #include <asm/page.h> -#include <linux/types.h> +#include <asm/uaccess.h> #include <linux/videodev.h> -#include <asm/uaccess.h> +#include <linux/video_decoder.h> MODULE_DESCRIPTION("Philips SAA7111 video decoder driver"); MODULE_AUTHOR("Dave Perks"); MODULE_LICENSE("GPL"); -#include <linux/i2c.h> #define I2C_NAME(s) (s)->name -#include <linux/video_decoder.h> static int debug = 0; module_param(debug, int, 0644); diff --git a/drivers/media/video/saa7114.c b/drivers/media/video/saa7114.c index 87c3144..677df51 100644 --- a/drivers/media/video/saa7114.c +++ b/drivers/media/video/saa7114.c @@ -35,28 +35,26 @@ #include <linux/fs.h> #include <linux/kernel.h> #include <linux/major.h> - #include <linux/slab.h> - #include <linux/mm.h> #include <linux/signal.h> +#include <linux/types.h> +#include <linux/i2c.h> #include <asm/io.h> #include <asm/pgtable.h> #include <asm/page.h> -#include <linux/types.h> +#include <asm/uaccess.h> #include <linux/videodev.h> -#include <asm/uaccess.h> +#include <linux/video_decoder.h> MODULE_DESCRIPTION("Philips SAA7114H video decoder driver"); MODULE_AUTHOR("Maxim Yevtyushkin"); MODULE_LICENSE("GPL"); -#include <linux/i2c.h> #define I2C_NAME(x) (x)->name -#include <linux/video_decoder.h> static int debug = 0; module_param(debug, int, 0); diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig index 309dca3..9f1417a 100644 --- a/drivers/media/video/saa7134/Kconfig +++ b/drivers/media/video/saa7134/Kconfig @@ -40,7 +40,7 @@ config VIDEO_SAA7134_DVB depends on VIDEO_SAA7134 && DVB_CORE select VIDEO_BUF_DVB select FW_LOADER - select DVB_PLL + select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_MT352 if !DVB_FE_CUSTOMISE select DVB_TDA1004X if !DVB_FE_CUSTOMISE select DVB_NXT200X if !DVB_FE_CUSTOMISE diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c index ffb0f64..3c0fc90 100644 --- a/drivers/media/video/saa7134/saa7134-alsa.c +++ b/drivers/media/video/saa7134/saa7134-alsa.c @@ -75,7 +75,8 @@ typedef struct snd_card_saa7134 { struct saa7134_dev *dev; unsigned long iobase; - int irq; + s16 irq; + u16 mute_was_on; spinlock_t lock; } snd_card_saa7134_t; @@ -589,8 +590,10 @@ static int snd_card_saa7134_capture_close(struct snd_pcm_substream * substream) snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream); struct saa7134_dev *dev = saa7134->dev; - dev->ctl_mute = 1; - saa7134_tvaudio_setmute(dev); + if (saa7134->mute_was_on) { + dev->ctl_mute = 1; + saa7134_tvaudio_setmute(dev); + } return 0; } @@ -637,8 +640,11 @@ static int snd_card_saa7134_capture_open(struct snd_pcm_substream * substream) runtime->private_free = snd_card_saa7134_runtime_free; runtime->hw = snd_card_saa7134_capture; - dev->ctl_mute = 0; - saa7134_tvaudio_setmute(dev); + if (dev->ctl_mute != 0) { + saa7134->mute_was_on = 1; + dev->ctl_mute = 0; + saa7134_tvaudio_setmute(dev); + } if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0) return err; diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 50f15ad..8ec83bd 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -400,7 +400,7 @@ struct saa7134_board saa7134_boards[] = { .inputs = {{ .name = name_tv, .vmux = 1, - .amux = LINE2, + .amux = TV, .tv = 1, .gpio = 0x20000, },{ @@ -3502,6 +3502,38 @@ struct saa7134_board saa7134_boards[] = { .amux = TV, }, }, + [SAA7134_BOARD_10MOONSTVMASTER3] = { + /* Tony Wan <aloha_cn@hotmail.com> */ + .name = "10MOONS TM300 TV Card", + .audio_clock = 0x00200000, + .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 0x7000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = LINE2, + .gpio = 0x0000, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x2000, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x2000, + }}, + .mute = { + .name = name_mute, + .amux = LINE2, + .gpio = 0x3000, + }, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -4219,6 +4251,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0x2003, /* OEM cardbus */ .driver_data = SAA7134_BOARD_SABRENT_TV_PCB05, },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = PCI_VENDOR_ID_PHILIPS, + .subdevice = 0x2304, + .driver_data = SAA7134_BOARD_10MOONSTVMASTER3, + },{ /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -4330,6 +4368,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_AVERMEDIA_A16AR: case SAA7134_BOARD_ENCORE_ENLTV: case SAA7134_BOARD_ENCORE_ENLTV_FM: + case SAA7134_BOARD_10MOONSTVMASTER3: dev->has_remote = SAA7134_REMOTE_GPIO; break; case SAA7134_BOARD_FLYDVBS_LR300: diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index e0eec80..1f6bd33 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -175,18 +175,6 @@ static int mt352_pinnacle_tuner_set_params(struct dvb_frontend* fe, return mt352_pinnacle_init(fe); } -static int mt352_aver777_tuner_calc_regs(struct dvb_frontend *fe, struct dvb_frontend_parameters *params, u8* pllbuf, int buf_len) -{ - if (buf_len < 5) - return -EINVAL; - - pllbuf[0] = 0x61; - dvb_pll_configure(&dvb_pll_philips_td1316, pllbuf+1, - params->frequency, - params->u.ofdm.bandwidth); - return 5; -} - static struct mt352_config pinnacle_300i = { .demod_address = 0x3c >> 1, .adc_clock = 20333, @@ -444,135 +432,6 @@ static struct tda1004x_config philips_europa_config = { /* ------------------------------------------------------------------ */ -static int philips_fmd1216_tuner_init(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - struct tda1004x_state *state = fe->demodulator_priv; - u8 addr = state->config->tuner_address; - /* this message is to set up ATC and ALC */ - static u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0xa0 }; - struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) }; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) - return -EIO; - msleep(1); - - return 0; -} - -static int philips_fmd1216_tuner_sleep(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - struct tda1004x_state *state = fe->demodulator_priv; - u8 addr = state->config->tuner_address; - /* this message actually turns the tuner back to analog mode */ - u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0x60 }; - struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) }; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(&dev->i2c_adap, &tuner_msg, 1); - msleep(1); - fmd1216_init[2] = 0x86; - fmd1216_init[3] = 0x54; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(&dev->i2c_adap, &tuner_msg, 1); - msleep(1); - return 0; -} - -static int philips_fmd1216_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) -{ - struct saa7134_dev *dev = fe->dvb->priv; - struct tda1004x_state *state = fe->demodulator_priv; - u8 addr = state->config->tuner_address; - u8 tuner_buf[4]; - struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tuner_buf,.len = - sizeof(tuner_buf) }; - int tuner_frequency = 0; - int divider = 0; - u8 band, mode, cp; - - /* determine charge pump */ - tuner_frequency = params->frequency + 36130000; - if (tuner_frequency < 87000000) - return -EINVAL; - /* low band */ - else if (tuner_frequency < 180000000) { - band = 1; - mode = 7; - cp = 0; - } else if (tuner_frequency < 195000000) { - band = 1; - mode = 6; - cp = 1; - /* mid band */ - } else if (tuner_frequency < 366000000) { - if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) { - band = 10; - } else { - band = 2; - } - mode = 7; - cp = 0; - } else if (tuner_frequency < 478000000) { - if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) { - band = 10; - } else { - band = 2; - } - mode = 6; - cp = 1; - /* high band */ - } else if (tuner_frequency < 662000000) { - if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) { - band = 12; - } else { - band = 4; - } - mode = 7; - cp = 0; - } else if (tuner_frequency < 840000000) { - if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) { - band = 12; - } else { - band = 4; - } - mode = 6; - cp = 1; - } else { - if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) { - band = 12; - } else { - band = 4; - } - mode = 7; - cp = 1; - - } - /* calculate divisor */ - /* ((36166000 + Finput) / 166666) rounded! */ - divider = (tuner_frequency + 83333) / 166667; - - /* setup tuner buffer */ - tuner_buf[0] = (divider >> 8) & 0x7f; - tuner_buf[1] = divider & 0xff; - tuner_buf[2] = 0x80 | (cp << 6) | (mode << 3) | 4; - tuner_buf[3] = 0x40 | band; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) { - wprintk("could not write to tuner at addr: 0x%02x\n", - addr << 1); - return -EIO; - } - return 0; -} - static struct tda1004x_config medion_cardbus = { .demod_address = 0x08, .invert = 1, @@ -958,18 +817,8 @@ static struct nxt200x_config avertvhda180 = { .demod_address = 0x0a, }; -static int nxt200x_set_pll_input(u8 *buf, int input) -{ - if (input) - buf[3] |= 0x08; - else - buf[3] &= ~0x08; - return 0; -} - static struct nxt200x_config kworldatsc110 = { .demod_address = 0x0a, - .set_pll_input = nxt200x_set_pll_input, }; /* ================================================================== @@ -1005,7 +854,8 @@ static int dvb_init(struct saa7134_dev *dev) dev->dvb.frontend = dvb_attach(mt352_attach, &avermedia_777, &dev->i2c_adap); if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.calc_regs = mt352_aver777_tuner_calc_regs; + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + NULL, DVB_PLL_PHILIPS_TD1316); } break; case SAA7134_BOARD_MD7134: @@ -1013,9 +863,8 @@ static int dvb_init(struct saa7134_dev *dev) &medion_cardbus, &dev->i2c_adap); if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = philips_fmd1216_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_fmd1216_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_fmd1216_tuner_set_params; + dvb_attach(dvb_pll_attach, dev->dvb.frontend, medion_cardbus.tuner_address, + &dev->i2c_adap, DVB_PLL_FMD1216ME); } break; case SAA7134_BOARD_PHILIPS_TOUGH: @@ -1113,7 +962,7 @@ static int dvb_init(struct saa7134_dev *dev) &dev->i2c_adap); if (dev->dvb.frontend) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - NULL, &dvb_pll_tdhu2); + NULL, DVB_PLL_TDHU2); } break; case SAA7134_BOARD_KWORLD_ATSC110: @@ -1121,7 +970,7 @@ static int dvb_init(struct saa7134_dev *dev) &dev->i2c_adap); if (dev->dvb.frontend) { dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - NULL, &dvb_pll_tuv1236d); + NULL, DVB_PLL_TUV1236D); } break; case SAA7134_BOARD_FLYDVBS_LR300: @@ -1144,9 +993,9 @@ static int dvb_init(struct saa7134_dev *dev) if (dev->dvb.frontend) { dev->original_demod_sleep = dev->dvb.frontend->ops.sleep; dev->dvb.frontend->ops.sleep = philips_europa_demod_sleep; - dev->dvb.frontend->ops.tuner_ops.init = philips_fmd1216_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_fmd1216_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_fmd1216_tuner_set_params; + + dvb_attach(dvb_pll_attach, dev->dvb.frontend, medion_cardbus.tuner_address, + &dev->i2c_adap, DVB_PLL_FMD1216ME); } break; case SAA7134_BOARD_VIDEOMATE_DVBT_200A: diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c index f521603..fc260ec 100644 --- a/drivers/media/video/saa7134/saa7134-empress.c +++ b/drivers/media/video/saa7134/saa7134-empress.c @@ -96,6 +96,10 @@ static int ts_open(struct inode *inode, struct file *file) if (dev->empress_users) goto done_up; + /* Unmute audio */ + saa_writeb(SAA7134_AUDIO_MUTE_CTRL, + saa_readb(SAA7134_AUDIO_MUTE_CTRL) & ~(1 << 6)); + dev->empress_users++; file->private_data = dev; err = 0; @@ -121,6 +125,10 @@ static int ts_release(struct inode *inode, struct file *file) /* stop the encoder */ ts_reset_encoder(dev); + /* Mute audio */ + saa_writeb(SAA7134_AUDIO_MUTE_CTRL, + saa_readb(SAA7134_AUDIO_MUTE_CTRL) | (1 << 6)); + mutex_unlock(&dev->empress_tsq.lock); return 0; } diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index c0de37e..1b6dfd8 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -153,21 +153,18 @@ void saa7134_input_irq(struct saa7134_dev *dev) static void saa7134_input_timer(unsigned long data) { - struct saa7134_dev *dev = (struct saa7134_dev*)data; + struct saa7134_dev *dev = (struct saa7134_dev *)data; struct card_ir *ir = dev->remote; - unsigned long timeout; build_key(dev); - timeout = jiffies + (ir->polling * HZ / 1000); - mod_timer(&ir->timer, timeout); + mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); } static void saa7134_ir_start(struct saa7134_dev *dev, struct card_ir *ir) { if (ir->polling) { - init_timer(&ir->timer); - ir->timer.function = saa7134_input_timer; - ir->timer.data = (unsigned long)dev; + setup_timer(&ir->timer, saa7134_input_timer, + (unsigned long)dev); ir->timer.expires = jiffies + HZ; add_timer(&ir->timer); } else if (ir->rc5_gpio) { @@ -314,6 +311,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keycode = 0x003F00; mask_keyup = 0x040000; break; + case SAA7134_BOARD_FLYDVBS_LR300: case SAA7134_BOARD_FLYDVBT_LR301: case SAA7134_BOARD_FLYDVBTDUO: ir_codes = ir_codes_flydvb; @@ -333,6 +331,12 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keyup = 0x040000; polling = 50; // ms break; + case SAA7134_BOARD_10MOONSTVMASTER3: + ir_codes = ir_codes_encore_enltv; + mask_keycode = 0x5f80000; + mask_keyup = 0x8000000; + polling = 50; //ms + break; } if (NULL == ir_codes) { printk("%s: Oops: IR config error [card=%d]\n", @@ -374,7 +378,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) input_dev->id.vendor = dev->pci->vendor; input_dev->id.product = dev->pci->device; } - input_dev->cdev.dev = &dev->pci->dev; + input_dev->dev.parent = &dev->pci->dev; dev->remote = ir; saa7134_ir_start(dev, ir); diff --git a/drivers/media/video/saa7134/saa7134-tvaudio.c b/drivers/media/video/saa7134/saa7134-tvaudio.c index 30395d6..18b4817 100644 --- a/drivers/media/video/saa7134/saa7134-tvaudio.c +++ b/drivers/media/video/saa7134/saa7134-tvaudio.c @@ -25,6 +25,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> +#include <linux/kthread.h> #include <linux/slab.h> #include <linux/delay.h> #include <asm/div64.h> @@ -341,10 +342,8 @@ static void tvaudio_setmode(struct saa7134_dev *dev, static int tvaudio_sleep(struct saa7134_dev *dev, int timeout) { - DECLARE_WAITQUEUE(wait, current); - - add_wait_queue(&dev->thread.wq, &wait); - if (dev->thread.scan1 == dev->thread.scan2 && !dev->thread.shutdown) { + if (dev->thread.scan1 == dev->thread.scan2 && + !kthread_should_stop()) { if (timeout < 0) { set_current_state(TASK_INTERRUPTIBLE); schedule(); @@ -353,7 +352,6 @@ static int tvaudio_sleep(struct saa7134_dev *dev, int timeout) (msecs_to_jiffies(timeout)); } } - remove_wait_queue(&dev->thread.wq, &wait); return dev->thread.scan1 != dev->thread.scan2; } @@ -505,11 +503,10 @@ static int tvaudio_thread(void *data) unsigned int i, audio, nscan; int max1,max2,carrier,rx,mode,lastmode,default_carrier; - daemonize("%s", dev->name); allow_signal(SIGTERM); for (;;) { tvaudio_sleep(dev,-1); - if (dev->thread.shutdown || signal_pending(current)) + if (kthread_should_stop() || signal_pending(current)) goto done; restart: @@ -618,7 +615,7 @@ static int tvaudio_thread(void *data) for (;;) { if (tvaudio_sleep(dev,5000)) goto restart; - if (dev->thread.shutdown || signal_pending(current)) + if (kthread_should_stop() || signal_pending(current)) break; if (UNSET == dev->thread.mode) { rx = tvaudio_getstereo(dev,&tvaudio[i]); @@ -634,7 +631,6 @@ static int tvaudio_thread(void *data) } done: - complete_and_exit(&dev->thread.exit, 0); return 0; } @@ -782,7 +778,6 @@ static int tvaudio_thread_ddep(void *data) struct saa7134_dev *dev = data; u32 value, norms, clock; - daemonize("%s", dev->name); allow_signal(SIGTERM); clock = saa7134_boards[dev->board].audio_clock; @@ -796,7 +791,7 @@ static int tvaudio_thread_ddep(void *data) for (;;) { tvaudio_sleep(dev,-1); - if (dev->thread.shutdown || signal_pending(current)) + if (kthread_should_stop() || signal_pending(current)) goto done; restart: @@ -876,7 +871,6 @@ static int tvaudio_thread_ddep(void *data) } done: - complete_and_exit(&dev->thread.exit, 0); return 0; } @@ -973,7 +967,6 @@ int saa7134_tvaudio_getstereo(struct saa7134_dev *dev) int saa7134_tvaudio_init2(struct saa7134_dev *dev) { - DECLARE_MUTEX_LOCKED(sem); int (*my_thread)(void *data) = NULL; switch (dev->pci->device) { @@ -986,15 +979,15 @@ int saa7134_tvaudio_init2(struct saa7134_dev *dev) break; } - dev->thread.pid = -1; + dev->thread.thread = NULL; if (my_thread) { /* start tvaudio thread */ - init_waitqueue_head(&dev->thread.wq); - init_completion(&dev->thread.exit); - dev->thread.pid = kernel_thread(my_thread,dev,0); - if (dev->thread.pid < 0) + dev->thread.thread = kthread_run(my_thread, dev, "%s", dev->name); + if (IS_ERR(dev->thread.thread)) { printk(KERN_WARNING "%s: kernel_thread() failed\n", dev->name); + /* XXX: missing error handling here */ + } saa7134_tvaudio_do_scan(dev); } @@ -1005,11 +998,9 @@ int saa7134_tvaudio_init2(struct saa7134_dev *dev) int saa7134_tvaudio_fini(struct saa7134_dev *dev) { /* shutdown tvaudio thread */ - if (dev->thread.pid > 0) { - dev->thread.shutdown = 1; - wake_up_interruptible(&dev->thread.wq); - wait_for_completion(&dev->thread.exit); - } + if (dev->thread.thread) + kthread_stop(dev->thread.thread); + saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x07, 0x00); /* LINE1 */ return 0; } @@ -1020,10 +1011,10 @@ int saa7134_tvaudio_do_scan(struct saa7134_dev *dev) dprintk("sound IF not in use, skipping scan\n"); dev->automute = 0; saa7134_tvaudio_setmute(dev); - } else if (dev->thread.pid >= 0) { + } else if (dev->thread.thread) { dev->thread.mode = UNSET; dev->thread.scan2++; - wake_up_interruptible(&dev->thread.wq); + wake_up_process(dev->thread.thread); } else { dev->automute = 0; saa7134_tvaudio_setmute(dev); @@ -1040,4 +1031,3 @@ EXPORT_SYMBOL(saa7134_tvaudio_setmute); * c-basic-offset: 8 * End: */ - diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 15623b2..d32a856 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -238,6 +238,7 @@ struct saa7134_format { #define SAA7134_BOARD_ECS_TVP3XP_4CB6 113 #define SAA7134_BOARD_KWORLD_DVBT_210 114 #define SAA7134_BOARD_SABRENT_TV_PCB05 115 +#define SAA7134_BOARD_10MOONSTVMASTER3 116 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 @@ -327,10 +328,7 @@ struct saa7134_pgtable { /* tvaudio thread status */ struct saa7134_thread { - pid_t pid; - struct completion exit; - wait_queue_head_t wq; - unsigned int shutdown; + struct task_struct *thread; unsigned int scan1; unsigned int scan2; unsigned int mode; diff --git a/drivers/media/video/saa7185.c b/drivers/media/video/saa7185.c index 339592e..66cc92c 100644 --- a/drivers/media/video/saa7185.c +++ b/drivers/media/video/saa7185.c @@ -34,23 +34,23 @@ #include <linux/slab.h> #include <linux/mm.h> #include <linux/signal.h> +#include <linux/types.h> +#include <linux/i2c.h> #include <asm/io.h> #include <asm/pgtable.h> #include <asm/page.h> -#include <linux/types.h> +#include <asm/uaccess.h> #include <linux/videodev.h> -#include <asm/uaccess.h> +#include <linux/video_encoder.h> MODULE_DESCRIPTION("Philips SAA7185 video encoder driver"); MODULE_AUTHOR("Dave Perks"); MODULE_LICENSE("GPL"); -#include <linux/i2c.h> #define I2C_NAME(s) (s)->name -#include <linux/video_encoder.h> static int debug = 0; module_param(debug, int, 0); diff --git a/drivers/media/video/sn9c102/sn9c102.h b/drivers/media/video/sn9c102/sn9c102.h index 11fcb49..2e3c3de 100644 --- a/drivers/media/video/sn9c102/sn9c102.h +++ b/drivers/media/video/sn9c102/sn9c102.h @@ -36,6 +36,7 @@ #include <linux/mutex.h> #include <linux/string.h> #include <linux/stddef.h> +#include <linux/kref.h> #include "sn9c102_config.h" #include "sn9c102_sensor.h" @@ -94,7 +95,7 @@ struct sn9c102_module_param { }; static DEFINE_MUTEX(sn9c102_sysfs_lock); -static DECLARE_RWSEM(sn9c102_disconnect); +static DECLARE_RWSEM(sn9c102_dev_lock); struct sn9c102_device { struct video_device* v4ldev; @@ -122,12 +123,14 @@ struct sn9c102_device { struct sn9c102_module_param module_param; + struct kref kref; enum sn9c102_dev_state state; u8 users; - struct mutex dev_mutex, fileop_mutex; + struct completion probe; + struct mutex open_mutex, fileop_mutex; spinlock_t queue_lock; - wait_queue_head_t open, wait_frame, wait_stream; + wait_queue_head_t wait_open, wait_frame, wait_stream; }; /*****************************************************************************/ diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c index 74a204f..36d8a45 100644 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ b/drivers/media/video/sn9c102/sn9c102_core.c @@ -48,8 +48,8 @@ #define SN9C102_MODULE_AUTHOR "(C) 2004-2007 Luca Risolia" #define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>" #define SN9C102_MODULE_LICENSE "GPL" -#define SN9C102_MODULE_VERSION "1:1.44" -#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 44) +#define SN9C102_MODULE_VERSION "1:1.47" +#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 47) /*****************************************************************************/ @@ -64,9 +64,10 @@ MODULE_LICENSE(SN9C102_MODULE_LICENSE); static short video_nr[] = {[0 ... SN9C102_MAX_DEVICES-1] = -1}; module_param_array(video_nr, short, NULL, 0444); MODULE_PARM_DESC(video_nr, - "\n<-1|n[,...]> Specify V4L2 minor mode number." - "\n -1 = use next available (default)" - "\n n = use minor number n (integer >= 0)" + " <-1|n[,...]>" + "\nSpecify V4L2 minor mode number." + "\n-1 = use next available (default)" + "\n n = use minor number n (integer >= 0)" "\nYou can specify up to "__MODULE_STRING(SN9C102_MAX_DEVICES) " cameras this way." "\nFor example:" @@ -79,13 +80,14 @@ static short force_munmap[] = {[0 ... SN9C102_MAX_DEVICES-1] = SN9C102_FORCE_MUNMAP}; module_param_array(force_munmap, bool, NULL, 0444); MODULE_PARM_DESC(force_munmap, - "\n<0|1[,...]> Force the application to unmap previously" + " <0|1[,...]>" + "\nForce the application to unmap previously" "\nmapped buffer memory before calling any VIDIOC_S_CROP or" "\nVIDIOC_S_FMT ioctl's. Not all the applications support" "\nthis feature. This parameter is specific for each" "\ndetected camera." - "\n 0 = do not force memory unmapping" - "\n 1 = force memory unmapping (save memory)" + "\n0 = do not force memory unmapping" + "\n1 = force memory unmapping (save memory)" "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." "\n"); @@ -93,7 +95,8 @@ static unsigned int frame_timeout[] = {[0 ... SN9C102_MAX_DEVICES-1] = SN9C102_FRAME_TIMEOUT}; module_param_array(frame_timeout, uint, NULL, 0644); MODULE_PARM_DESC(frame_timeout, - "\n<0|n[,...]> Timeout for a video frame in seconds before" + " <0|n[,...]>" + "\nTimeout for a video frame in seconds before" "\nreturning an I/O error; 0 for infinity." "\nThis parameter is specific for each detected camera." "\nDefault value is "__MODULE_STRING(SN9C102_FRAME_TIMEOUT)"." @@ -103,7 +106,8 @@ MODULE_PARM_DESC(frame_timeout, static unsigned short debug = SN9C102_DEBUG_LEVEL; module_param(debug, ushort, 0644); MODULE_PARM_DESC(debug, - "\n<n> Debugging information level, from 0 to 3:" + " <n>" + "\nDebugging information level, from 0 to 3:" "\n0 = none (use carefully)" "\n1 = critical errors" "\n2 = significant informations" @@ -1616,7 +1620,8 @@ static int sn9c102_init(struct sn9c102_device* cam) int err = 0; if (!(cam->state & DEV_INITIALIZED)) { - init_waitqueue_head(&cam->open); + mutex_init(&cam->open_mutex); + init_waitqueue_head(&cam->wait_open); qctrl = s->qctrl; rect = &(s->cropcap.defrect); } else { /* use current values */ @@ -1706,21 +1711,27 @@ static int sn9c102_init(struct sn9c102_device* cam) return 0; } +/*****************************************************************************/ -static void sn9c102_release_resources(struct sn9c102_device* cam) +static void sn9c102_release_resources(struct kref *kref) { + struct sn9c102_device *cam; + mutex_lock(&sn9c102_sysfs_lock); + cam = container_of(kref, struct sn9c102_device, kref); + DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor); video_set_drvdata(cam->v4ldev, NULL); video_unregister_device(cam->v4ldev); + usb_put_dev(cam->usbdev); + kfree(cam->control_buffer); + kfree(cam); mutex_unlock(&sn9c102_sysfs_lock); - kfree(cam->control_buffer); } -/*****************************************************************************/ static int sn9c102_open(struct inode* inode, struct file* filp) { @@ -1728,43 +1739,78 @@ static int sn9c102_open(struct inode* inode, struct file* filp) int err = 0; /* - This is the only safe way to prevent race conditions with - disconnect + A read_trylock() in open() is the only safe way to prevent race + conditions with disconnect(), one close() and multiple (not + necessarily simultaneous) attempts to open(). For example, it + prevents from waiting for a second access, while the device + structure is being deallocated, after a possible disconnect() and + during a following close() holding the write lock: given that, after + this deallocation, no access will be possible anymore, using the + non-trylock version would have let open() gain the access to the + device structure improperly. + For this reason the lock must also not be per-device. */ - if (!down_read_trylock(&sn9c102_disconnect)) + if (!down_read_trylock(&sn9c102_dev_lock)) return -ERESTARTSYS; cam = video_get_drvdata(video_devdata(filp)); - if (mutex_lock_interruptible(&cam->dev_mutex)) { - up_read(&sn9c102_disconnect); + if (wait_for_completion_interruptible(&cam->probe)) { + up_read(&sn9c102_dev_lock); + return -ERESTARTSYS; + } + + kref_get(&cam->kref); + + /* + Make sure to isolate all the simultaneous opens. + */ + if (mutex_lock_interruptible(&cam->open_mutex)) { + kref_put(&cam->kref, sn9c102_release_resources); + up_read(&sn9c102_dev_lock); return -ERESTARTSYS; } + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + err = -ENODEV; + goto out; + } + if (cam->users) { - DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor); + DBG(2, "Device /dev/video%d is already in use", + cam->v4ldev->minor); DBG(3, "Simultaneous opens are not supported"); + /* + open() must follow the open flags and should block + eventually while the device is in use. + */ if ((filp->f_flags & O_NONBLOCK) || (filp->f_flags & O_NDELAY)) { err = -EWOULDBLOCK; goto out; } - mutex_unlock(&cam->dev_mutex); - err = wait_event_interruptible_exclusive(cam->open, - cam->state & DEV_DISCONNECTED + DBG(2, "A blocking open() has been requested. Wait for the " + "device to be released..."); + up_read(&sn9c102_dev_lock); + /* + We will not release the "open_mutex" lock, so that only one + process can be in the wait queue below. This way the process + will be sleeping while holding the lock, without loosing its + priority after any wake_up(). + */ + err = wait_event_interruptible_exclusive(cam->wait_open, + (cam->state & DEV_DISCONNECTED) || !cam->users); - if (err) { - up_read(&sn9c102_disconnect); - return err; - } + down_read(&sn9c102_dev_lock); + if (err) + goto out; if (cam->state & DEV_DISCONNECTED) { - up_read(&sn9c102_disconnect); - return -ENODEV; + err = -ENODEV; + goto out; } - mutex_lock(&cam->dev_mutex); } - if (cam->state & DEV_MISCONFIGURED) { err = sn9c102_init(cam); if (err) { @@ -1789,36 +1835,33 @@ static int sn9c102_open(struct inode* inode, struct file* filp) DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor); out: - mutex_unlock(&cam->dev_mutex); - up_read(&sn9c102_disconnect); + mutex_unlock(&cam->open_mutex); + if (err) + kref_put(&cam->kref, sn9c102_release_resources); + + up_read(&sn9c102_dev_lock); return err; } static int sn9c102_release(struct inode* inode, struct file* filp) { - struct sn9c102_device* cam = video_get_drvdata(video_devdata(filp)); + struct sn9c102_device* cam; - mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */ + down_write(&sn9c102_dev_lock); - sn9c102_stop_transfer(cam); + cam = video_get_drvdata(video_devdata(filp)); + sn9c102_stop_transfer(cam); sn9c102_release_buffers(cam); - - if (cam->state & DEV_DISCONNECTED) { - sn9c102_release_resources(cam); - usb_put_dev(cam->usbdev); - mutex_unlock(&cam->dev_mutex); - kfree(cam); - return 0; - } - cam->users--; - wake_up_interruptible_nr(&cam->open, 1); + wake_up_interruptible_nr(&cam->wait_open, 1); DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor); - mutex_unlock(&cam->dev_mutex); + kref_put(&cam->kref, sn9c102_release_resources); + + up_write(&sn9c102_dev_lock); return 0; } @@ -2085,7 +2128,6 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) vma->vm_ops = &sn9c102_vm_ops; vma->vm_private_data = &cam->frame[i]; - sn9c102_vm_open(vma); mutex_unlock(&cam->fileop_mutex); @@ -3215,8 +3257,6 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) goto fail; } - mutex_init(&cam->dev_mutex); - r = sn9c102_read_reg(cam, 0x00); if (r < 0 || (r != 0x10 && r != 0x11 && r != 0x12)) { DBG(1, "Sorry, this is not a SN9C1xx-based camera " @@ -3282,7 +3322,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) cam->v4ldev->release = video_device_release; video_set_drvdata(cam->v4ldev, cam); - mutex_lock(&cam->dev_mutex); + init_completion(&cam->probe); err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, video_nr[dev_nr]); @@ -3292,7 +3332,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) DBG(1, "Free /dev/videoX node not found"); video_nr[dev_nr] = -1; dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; - mutex_unlock(&cam->dev_mutex); + complete_all(&cam->probe); goto fail; } @@ -3318,8 +3358,10 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) #endif usb_set_intfdata(intf, cam); + kref_init(&cam->kref); + usb_get_dev(cam->usbdev); - mutex_unlock(&cam->dev_mutex); + complete_all(&cam->probe); return 0; @@ -3336,40 +3378,31 @@ fail: static void sn9c102_usb_disconnect(struct usb_interface* intf) { - struct sn9c102_device* cam = usb_get_intfdata(intf); - - if (!cam) - return; + struct sn9c102_device* cam; - down_write(&sn9c102_disconnect); + down_write(&sn9c102_dev_lock); - mutex_lock(&cam->dev_mutex); + cam = usb_get_intfdata(intf); DBG(2, "Disconnecting %s...", cam->v4ldev->name); - wake_up_interruptible_all(&cam->open); - if (cam->users) { DBG(2, "Device /dev/video%d is open! Deregistration and " - "memory deallocation are deferred on close.", + "memory deallocation are deferred.", cam->v4ldev->minor); cam->state |= DEV_MISCONFIGURED; sn9c102_stop_transfer(cam); cam->state |= DEV_DISCONNECTED; wake_up_interruptible(&cam->wait_frame); wake_up(&cam->wait_stream); - usb_get_dev(cam->usbdev); - } else { + } else cam->state |= DEV_DISCONNECTED; - sn9c102_release_resources(cam); - } - mutex_unlock(&cam->dev_mutex); + wake_up_interruptible_all(&cam->wait_open); - if (!cam->users) - kfree(cam); + kref_put(&cam->kref, sn9c102_release_resources); - up_write(&sn9c102_disconnect); + up_write(&sn9c102_dev_lock); } diff --git a/drivers/media/video/sn9c102/sn9c102_ov7630.c b/drivers/media/video/sn9c102/sn9c102_ov7630.c index e683234..e4856fd 100644 --- a/drivers/media/video/sn9c102/sn9c102_ov7630.c +++ b/drivers/media/video/sn9c102/sn9c102_ov7630.c @@ -104,6 +104,145 @@ static int ov7630_init(struct sn9c102_device* cam) err += sn9c102_i2c_write(cam, 0x74, 0x21); err += sn9c102_i2c_write(cam, 0x7d, 0xf7); break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + err = sn9c102_write_const_regs(cam, {0x40, 0x02}, {0x00, 0x03}, + {0x1a, 0x04}, {0x03, 0x10}, + {0x0a, 0x14}, {0xe2, 0x17}, + {0x0b, 0x18}, {0x00, 0x19}, + {0x1d, 0x1a}, {0x10, 0x1b}, + {0x02, 0x1c}, {0x03, 0x1d}, + {0x0f, 0x1e}, {0x0c, 0x1f}, + {0x00, 0x20}, {0x24, 0x21}, + {0x3b, 0x22}, {0x47, 0x23}, + {0x60, 0x24}, {0x71, 0x25}, + {0x80, 0x26}, {0x8f, 0x27}, + {0x9d, 0x28}, {0xaa, 0x29}, + {0xb8, 0x2a}, {0xc4, 0x2b}, + {0xd1, 0x2c}, {0xdd, 0x2d}, + {0xe8, 0x2e}, {0xf4, 0x2f}, + {0xff, 0x30}, {0x00, 0x3f}, + {0xc7, 0x40}, {0x01, 0x41}, + {0x44, 0x42}, {0x00, 0x43}, + {0x44, 0x44}, {0x00, 0x45}, + {0x44, 0x46}, {0x00, 0x47}, + {0xc7, 0x48}, {0x01, 0x49}, + {0xc7, 0x4a}, {0x01, 0x4b}, + {0xc7, 0x4c}, {0x01, 0x4d}, + {0x44, 0x4e}, {0x00, 0x4f}, + {0x44, 0x50}, {0x00, 0x51}, + {0x44, 0x52}, {0x00, 0x53}, + {0xc7, 0x54}, {0x01, 0x55}, + {0xc7, 0x56}, {0x01, 0x57}, + {0xc7, 0x58}, {0x01, 0x59}, + {0x44, 0x5a}, {0x00, 0x5b}, + {0x44, 0x5c}, {0x00, 0x5d}, + {0x44, 0x5e}, {0x00, 0x5f}, + {0xc7, 0x60}, {0x01, 0x61}, + {0xc7, 0x62}, {0x01, 0x63}, + {0xc7, 0x64}, {0x01, 0x65}, + {0x44, 0x66}, {0x00, 0x67}, + {0x44, 0x68}, {0x00, 0x69}, + {0x44, 0x6a}, {0x00, 0x6b}, + {0xc7, 0x6c}, {0x01, 0x6d}, + {0xc7, 0x6e}, {0x01, 0x6f}, + {0xc7, 0x70}, {0x01, 0x71}, + {0x44, 0x72}, {0x00, 0x73}, + {0x44, 0x74}, {0x00, 0x75}, + {0x44, 0x76}, {0x00, 0x77}, + {0xc7, 0x78}, {0x01, 0x79}, + {0xc7, 0x7a}, {0x01, 0x7b}, + {0xc7, 0x7c}, {0x01, 0x7d}, + {0x44, 0x7e}, {0x00, 0x7f}, + {0x17, 0x84}, {0x00, 0x85}, + {0x2e, 0x86}, {0x00, 0x87}, + {0x09, 0x88}, {0x00, 0x89}, + {0xe8, 0x8a}, {0x0f, 0x8b}, + {0xda, 0x8c}, {0x0f, 0x8d}, + {0x40, 0x8e}, {0x00, 0x8f}, + {0x37, 0x90}, {0x00, 0x91}, + {0xcf, 0x92}, {0x0f, 0x93}, + {0xfa, 0x94}, {0x0f, 0x95}, + {0x00, 0x96}, {0x00, 0x97}, + {0x00, 0x98}, {0x66, 0x99}, + {0x00, 0x9a}, {0x40, 0x9b}, + {0x20, 0x9c}, {0x00, 0x9d}, + {0x00, 0x9e}, {0x00, 0x9f}, + {0x2d, 0xc0}, {0x2d, 0xc1}, + {0x3a, 0xc2}, {0x00, 0xc3}, + {0x04, 0xc4}, {0x3f, 0xc5}, + {0x00, 0xc6}, {0x00, 0xc7}, + {0x50, 0xc8}, {0x3c, 0xc9}, + {0x28, 0xca}, {0xd8, 0xcb}, + {0x14, 0xcc}, {0xec, 0xcd}, + {0x32, 0xce}, {0xdd, 0xcf}, + {0x32, 0xd0}, {0xdd, 0xd1}, + {0x6a, 0xd2}, {0x50, 0xd3}, + {0x60, 0xd4}, {0x00, 0xd5}, + {0x00, 0xd6}); + + err += sn9c102_i2c_write(cam, 0x12, 0x80); + err += sn9c102_i2c_write(cam, 0x12, 0x48); + err += sn9c102_i2c_write(cam, 0x01, 0x80); + err += sn9c102_i2c_write(cam, 0x02, 0x80); + err += sn9c102_i2c_write(cam, 0x03, 0x80); + err += sn9c102_i2c_write(cam, 0x04, 0x10); + err += sn9c102_i2c_write(cam, 0x05, 0x20); + err += sn9c102_i2c_write(cam, 0x06, 0x80); + err += sn9c102_i2c_write(cam, 0x11, 0x00); + err += sn9c102_i2c_write(cam, 0x0c, 0x20); + err += sn9c102_i2c_write(cam, 0x0d, 0x20); + err += sn9c102_i2c_write(cam, 0x15, 0x80); + err += sn9c102_i2c_write(cam, 0x16, 0x03); + err += sn9c102_i2c_write(cam, 0x17, 0x1b); + err += sn9c102_i2c_write(cam, 0x18, 0xbd); + err += sn9c102_i2c_write(cam, 0x19, 0x05); + err += sn9c102_i2c_write(cam, 0x1a, 0xf6); + err += sn9c102_i2c_write(cam, 0x1b, 0x04); + err += sn9c102_i2c_write(cam, 0x21, 0x1b); + err += sn9c102_i2c_write(cam, 0x22, 0x00); + err += sn9c102_i2c_write(cam, 0x23, 0xde); + err += sn9c102_i2c_write(cam, 0x24, 0x10); + err += sn9c102_i2c_write(cam, 0x25, 0x8a); + err += sn9c102_i2c_write(cam, 0x26, 0xa0); + err += sn9c102_i2c_write(cam, 0x27, 0xca); + err += sn9c102_i2c_write(cam, 0x28, 0xa2); + err += sn9c102_i2c_write(cam, 0x29, 0x74); + err += sn9c102_i2c_write(cam, 0x2a, 0x88); + err += sn9c102_i2c_write(cam, 0x2b, 0x34); + err += sn9c102_i2c_write(cam, 0x2c, 0x88); + err += sn9c102_i2c_write(cam, 0x2e, 0x00); + err += sn9c102_i2c_write(cam, 0x2f, 0x00); + err += sn9c102_i2c_write(cam, 0x30, 0x00); + err += sn9c102_i2c_write(cam, 0x32, 0xc2); + err += sn9c102_i2c_write(cam, 0x33, 0x08); + err += sn9c102_i2c_write(cam, 0x4c, 0x40); + err += sn9c102_i2c_write(cam, 0x4d, 0xf3); + err += sn9c102_i2c_write(cam, 0x60, 0x05); + err += sn9c102_i2c_write(cam, 0x61, 0x40); + err += sn9c102_i2c_write(cam, 0x62, 0x12); + err += sn9c102_i2c_write(cam, 0x63, 0x57); + err += sn9c102_i2c_write(cam, 0x64, 0x73); + err += sn9c102_i2c_write(cam, 0x65, 0x00); + err += sn9c102_i2c_write(cam, 0x66, 0x55); + err += sn9c102_i2c_write(cam, 0x67, 0x01); + err += sn9c102_i2c_write(cam, 0x68, 0xac); + err += sn9c102_i2c_write(cam, 0x69, 0x38); + err += sn9c102_i2c_write(cam, 0x6f, 0x1f); + err += sn9c102_i2c_write(cam, 0x70, 0x01); + err += sn9c102_i2c_write(cam, 0x71, 0x00); + err += sn9c102_i2c_write(cam, 0x72, 0x10); + err += sn9c102_i2c_write(cam, 0x73, 0x50); + err += sn9c102_i2c_write(cam, 0x74, 0x20); + err += sn9c102_i2c_write(cam, 0x76, 0x01); + err += sn9c102_i2c_write(cam, 0x77, 0xf3); + err += sn9c102_i2c_write(cam, 0x78, 0x90); + err += sn9c102_i2c_write(cam, 0x79, 0x98); + err += sn9c102_i2c_write(cam, 0x7a, 0x98); + err += sn9c102_i2c_write(cam, 0x7b, 0x00); + err += sn9c102_i2c_write(cam, 0x7c, 0x38); + err += sn9c102_i2c_write(cam, 0x7d, 0xff); + break; default: break; } @@ -115,6 +254,7 @@ static int ov7630_init(struct sn9c102_device* cam) static int ov7630_get_ctrl(struct sn9c102_device* cam, struct v4l2_control* ctrl) { + enum sn9c102_bridge bridge = sn9c102_get_bridge(cam); int err = 0; switch (ctrl->id) { @@ -123,13 +263,20 @@ static int ov7630_get_ctrl(struct sn9c102_device* cam, return -EIO; break; case V4L2_CID_RED_BALANCE: - ctrl->value = sn9c102_pread_reg(cam, 0x07); + if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) + ctrl->value = sn9c102_pread_reg(cam, 0x05); + else + ctrl->value = sn9c102_pread_reg(cam, 0x07); break; case V4L2_CID_BLUE_BALANCE: ctrl->value = sn9c102_pread_reg(cam, 0x06); break; case SN9C102_V4L2_CID_GREEN_BALANCE: - ctrl->value = sn9c102_pread_reg(cam, 0x05); + if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) + ctrl->value = sn9c102_pread_reg(cam, 0x07); + else + ctrl->value = sn9c102_pread_reg(cam, 0x05); + break; break; case V4L2_CID_GAIN: if ((ctrl->value = sn9c102_i2c_read(cam, 0x00)) < 0) @@ -177,6 +324,7 @@ static int ov7630_get_ctrl(struct sn9c102_device* cam, static int ov7630_set_ctrl(struct sn9c102_device* cam, const struct v4l2_control* ctrl) { + enum sn9c102_bridge bridge = sn9c102_get_bridge(cam); int err = 0; switch (ctrl->id) { @@ -184,13 +332,19 @@ static int ov7630_set_ctrl(struct sn9c102_device* cam, err += sn9c102_i2c_write(cam, 0x10, ctrl->value); break; case V4L2_CID_RED_BALANCE: - err += sn9c102_write_reg(cam, ctrl->value, 0x07); + if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) + err += sn9c102_write_reg(cam, ctrl->value, 0x05); + else + err += sn9c102_write_reg(cam, ctrl->value, 0x07); break; case V4L2_CID_BLUE_BALANCE: err += sn9c102_write_reg(cam, ctrl->value, 0x06); break; case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_write_reg(cam, ctrl->value, 0x05); + if (bridge == BRIDGE_SN9C105 || bridge == BRIDGE_SN9C120) + err += sn9c102_write_reg(cam, ctrl->value, 0x07); + else + err += sn9c102_write_reg(cam, ctrl->value, 0x05); break; case V4L2_CID_GAIN: err += sn9c102_i2c_write(cam, 0x00, ctrl->value); @@ -227,8 +381,21 @@ static int ov7630_set_crop(struct sn9c102_device* cam, { struct sn9c102_sensor* s = sn9c102_get_sensor(cam); int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + u8 h_start = 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1; + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4; + break; + default: + break; + } err += sn9c102_write_reg(cam, h_start, 0x12); err += sn9c102_write_reg(cam, v_start, 0x13); @@ -242,10 +409,28 @@ static int ov7630_set_pix_format(struct sn9c102_device* cam, { int err = 0; - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x20, 0x19); - else - err += sn9c102_write_reg(cam, 0x50, 0x19); + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) + err += sn9c102_write_reg(cam, 0x50, 0x19); + else + err += sn9c102_write_reg(cam, 0x20, 0x19); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { + err += sn9c102_write_reg(cam, 0xe5, 0x17); + err += sn9c102_i2c_write(cam, 0x11, 0x04); + } else { + err += sn9c102_write_reg(cam, 0xe2, 0x17); + err += sn9c102_i2c_write(cam, 0x11, 0x02); + } + break; + default: + break; + } return err; } @@ -254,7 +439,8 @@ static int ov7630_set_pix_format(struct sn9c102_device* cam, static const struct sn9c102_sensor ov7630 = { .name = "OV7630", .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", - .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103 | + BRIDGE_SN9C105 | BRIDGE_SN9C120, .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_2WIRES, @@ -417,6 +603,12 @@ int sn9c102_probe_ov7630(struct sn9c102_device* cam) err += sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}); break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + err = sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, + {0x29, 0x01}, {0x74, 0x02}, + {0x0e, 0x01}, {0x44, 0x01}); + break; default: break; } diff --git a/drivers/media/video/sn9c102/sn9c102_ov7660.c b/drivers/media/video/sn9c102/sn9c102_ov7660.c index 4b64740..8aae416 100644 --- a/drivers/media/video/sn9c102/sn9c102_ov7660.c +++ b/drivers/media/video/sn9c102/sn9c102_ov7660.c @@ -41,65 +41,65 @@ static int ov7660_init(struct sn9c102_device* cam) {0xbb, 0x2a}, {0xc7, 0x2b}, {0xd3, 0x2c}, {0xde, 0x2d}, {0xea, 0x2e}, {0xf4, 0x2f}, - {0xff, 0x30}, {0x00, 0x3F}, - {0xC7, 0x40}, {0x01, 0x41}, + {0xff, 0x30}, {0x00, 0x3f}, + {0xc7, 0x40}, {0x01, 0x41}, {0x44, 0x42}, {0x00, 0x43}, {0x44, 0x44}, {0x00, 0x45}, {0x44, 0x46}, {0x00, 0x47}, - {0xC7, 0x48}, {0x01, 0x49}, - {0xC7, 0x4A}, {0x01, 0x4B}, - {0xC7, 0x4C}, {0x01, 0x4D}, - {0x44, 0x4E}, {0x00, 0x4F}, + {0xc7, 0x48}, {0x01, 0x49}, + {0xc7, 0x4a}, {0x01, 0x4b}, + {0xc7, 0x4c}, {0x01, 0x4d}, + {0x44, 0x4e}, {0x00, 0x4f}, {0x44, 0x50}, {0x00, 0x51}, {0x44, 0x52}, {0x00, 0x53}, - {0xC7, 0x54}, {0x01, 0x55}, - {0xC7, 0x56}, {0x01, 0x57}, - {0xC7, 0x58}, {0x01, 0x59}, - {0x44, 0x5A}, {0x00, 0x5B}, - {0x44, 0x5C}, {0x00, 0x5D}, - {0x44, 0x5E}, {0x00, 0x5F}, - {0xC7, 0x60}, {0x01, 0x61}, - {0xC7, 0x62}, {0x01, 0x63}, - {0xC7, 0x64}, {0x01, 0x65}, + {0xc7, 0x54}, {0x01, 0x55}, + {0xc7, 0x56}, {0x01, 0x57}, + {0xc7, 0x58}, {0x01, 0x59}, + {0x44, 0x5a}, {0x00, 0x5b}, + {0x44, 0x5c}, {0x00, 0x5d}, + {0x44, 0x5e}, {0x00, 0x5f}, + {0xc7, 0x60}, {0x01, 0x61}, + {0xc7, 0x62}, {0x01, 0x63}, + {0xc7, 0x64}, {0x01, 0x65}, {0x44, 0x66}, {0x00, 0x67}, {0x44, 0x68}, {0x00, 0x69}, - {0x44, 0x6A}, {0x00, 0x6B}, - {0xC7, 0x6C}, {0x01, 0x6D}, - {0xC7, 0x6E}, {0x01, 0x6F}, - {0xC7, 0x70}, {0x01, 0x71}, + {0x44, 0x6a}, {0x00, 0x6b}, + {0xc7, 0x6c}, {0x01, 0x6d}, + {0xc7, 0x6e}, {0x01, 0x6f}, + {0xc7, 0x70}, {0x01, 0x71}, {0x44, 0x72}, {0x00, 0x73}, {0x44, 0x74}, {0x00, 0x75}, {0x44, 0x76}, {0x00, 0x77}, - {0xC7, 0x78}, {0x01, 0x79}, - {0xC7, 0x7A}, {0x01, 0x7B}, - {0xC7, 0x7C}, {0x01, 0x7D}, - {0x44, 0x7E}, {0x00, 0x7F}, + {0xc7, 0x78}, {0x01, 0x79}, + {0xc7, 0x7a}, {0x01, 0x7b}, + {0xc7, 0x7c}, {0x01, 0x7d}, + {0x44, 0x7e}, {0x00, 0x7f}, {0x14, 0x84}, {0x00, 0x85}, {0x27, 0x86}, {0x00, 0x87}, {0x07, 0x88}, {0x00, 0x89}, - {0xEC, 0x8A}, {0x0f, 0x8B}, - {0xD8, 0x8C}, {0x0f, 0x8D}, - {0x3D, 0x8E}, {0x00, 0x8F}, - {0x3D, 0x90}, {0x00, 0x91}, - {0xCD, 0x92}, {0x0f, 0x93}, + {0xec, 0x8a}, {0x0f, 0x8b}, + {0xd8, 0x8c}, {0x0f, 0x8d}, + {0x3d, 0x8e}, {0x00, 0x8f}, + {0x3d, 0x90}, {0x00, 0x91}, + {0xcd, 0x92}, {0x0f, 0x93}, {0xf7, 0x94}, {0x0f, 0x95}, - {0x0C, 0x96}, {0x00, 0x97}, + {0x0c, 0x96}, {0x00, 0x97}, {0x00, 0x98}, {0x66, 0x99}, - {0x05, 0x9A}, {0x00, 0x9B}, - {0x04, 0x9C}, {0x00, 0x9D}, - {0x08, 0x9E}, {0x00, 0x9F}, - {0x2D, 0xC0}, {0x2D, 0xC1}, - {0x3A, 0xC2}, {0x05, 0xC3}, - {0x04, 0xC4}, {0x3F, 0xC5}, - {0x00, 0xC6}, {0x00, 0xC7}, - {0x50, 0xC8}, {0x3C, 0xC9}, - {0x28, 0xCA}, {0xD8, 0xCB}, - {0x14, 0xCC}, {0xEC, 0xCD}, - {0x32, 0xCE}, {0xDD, 0xCF}, - {0x32, 0xD0}, {0xDD, 0xD1}, - {0x6A, 0xD2}, {0x50, 0xD3}, - {0x00, 0xD4}, {0x00, 0xD5}, - {0x00, 0xD6}); + {0x05, 0x9a}, {0x00, 0x9b}, + {0x04, 0x9c}, {0x00, 0x9d}, + {0x08, 0x9e}, {0x00, 0x9f}, + {0x2d, 0xc0}, {0x2d, 0xc1}, + {0x3a, 0xc2}, {0x05, 0xc3}, + {0x04, 0xc4}, {0x3f, 0xc5}, + {0x00, 0xc6}, {0x00, 0xc7}, + {0x50, 0xc8}, {0x3C, 0xc9}, + {0x28, 0xca}, {0xd8, 0xcb}, + {0x14, 0xcc}, {0xec, 0xcd}, + {0x32, 0xce}, {0xdd, 0xcf}, + {0x32, 0xd0}, {0xdd, 0xd1}, + {0x6a, 0xd2}, {0x50, 0xd3}, + {0x00, 0xd4}, {0x00, 0xd5}, + {0x00, 0xd6}); err += sn9c102_i2c_write(cam, 0x12, 0x80); err += sn9c102_i2c_write(cam, 0x11, 0x09); diff --git a/drivers/media/video/stradis.c b/drivers/media/video/stradis.c index 3e736be..eb22046 100644 --- a/drivers/media/video/stradis.c +++ b/drivers/media/video/stradis.c @@ -1321,7 +1321,7 @@ static int saa_ioctl(struct inode *inode, struct file *file, u32 format; if (copy_from_user(&p, arg, sizeof(p))) return -EFAULT; - if (p.palette < sizeof(palette2fmt) / sizeof(u32)) { + if (p.palette < ARRAY_SIZE(palette2fmt)) { format = palette2fmt[p.palette]; saa->win.color_fmt = format; saawrite(format | 0x60, diff --git a/drivers/media/video/stv680.c b/drivers/media/video/stv680.c index bf3aa8d..4dc5bc7 100644 --- a/drivers/media/video/stv680.c +++ b/drivers/media/video/stv680.c @@ -715,8 +715,11 @@ static int stv680_start_stream (struct usb_stv *stv680) stv680_video_irq, stv680); stv680->urb[i] = urb; err = usb_submit_urb (stv680->urb[i], GFP_KERNEL); - if (err) - PDEBUG (0, "STV(e): urb burned down in start stream"); + if (err) { + PDEBUG (0, "STV(e): urb burned down with err " + "%d in start stream %d", err, i); + goto nomem_err; + } } /* i STV680_NUMSBUF */ stv680->framecount = 0; diff --git a/drivers/media/video/tda8290.c b/drivers/media/video/tda8290.c index 1a1bef0..59cff5a 100644 --- a/drivers/media/video/tda8290.c +++ b/drivers/media/video/tda8290.c @@ -21,7 +21,17 @@ #include <linux/i2c.h> #include <linux/videodev.h> #include <linux/delay.h> -#include <media/tuner.h> +#include "tuner-driver.h" + +/* ---------------------------------------------------------------------- */ + +struct tda8290_priv { + unsigned char tda8290_easy_mode; + unsigned char tda827x_lpsel; + unsigned char tda827x_addr; + unsigned char tda827x_ver; + unsigned int sgIF; +}; /* ---------------------------------------------------------------------- */ @@ -76,7 +86,8 @@ static void tda827x_tune(struct i2c_client *c, u16 ifc, unsigned int freq) u32 N; int i; struct tuner *t = i2c_get_clientdata(c); - struct i2c_msg msg = {.addr = t->tda827x_addr, .flags = 0}; + struct tda8290_priv *priv = t->priv; + struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags = 0}; if (t->mode == V4L2_TUNER_RADIO) freq = freq / 1000; @@ -95,7 +106,7 @@ static void tda827x_tune(struct i2c_client *c, u16 ifc, unsigned int freq) tuner_reg[1] = (unsigned char)(N>>8); tuner_reg[2] = (unsigned char) N; tuner_reg[3] = 0x40; - tuner_reg[4] = 0x52 + (t->tda827x_lpsel << 5); + tuner_reg[4] = 0x52 + (priv->tda827x_lpsel << 5); tuner_reg[5] = (tda827x_analog[i].spd << 6) + (tda827x_analog[i].div1p5 <<5) + (tda827x_analog[i].bs <<3) + tda827x_analog[i].bp; tuner_reg[6] = 0x8f + (tda827x_analog[i].gc3 << 4); @@ -146,8 +157,9 @@ static void tda827x_tune(struct i2c_client *c, u16 ifc, unsigned int freq) static void tda827x_agcf(struct i2c_client *c) { struct tuner *t = i2c_get_clientdata(c); + struct tda8290_priv *priv = t->priv; unsigned char data[] = {0x80, 0x0c}; - struct i2c_msg msg = {.addr = t->tda827x_addr, .buf = data, + struct i2c_msg msg = {.addr = priv->tda827x_addr, .buf = data, .flags = 0, .len = 2}; i2c_transfer(c->adapter, &msg, 1); } @@ -234,7 +246,8 @@ static void tda827xa_tune(struct i2c_client *c, u16 ifc, unsigned int freq) u32 N; int i; struct tuner *t = i2c_get_clientdata(c); - struct i2c_msg msg = {.addr = t->tda827x_addr, .flags = 0, .buf = tuner_reg}; + struct tda8290_priv *priv = t->priv; + struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags = 0, .buf = tuner_reg}; tda827xa_lna_gain( c, 1); msleep(10); @@ -271,7 +284,7 @@ static void tda827xa_tune(struct i2c_client *c, u16 ifc, unsigned int freq) tuner_reg[1] = 0xff; tuner_reg[2] = 0xe0; tuner_reg[3] = 0; - tuner_reg[4] = 0x99 + (t->tda827x_lpsel << 1); + tuner_reg[4] = 0x99 + (priv->tda827x_lpsel << 1); msg.len = 5; i2c_transfer(c->adapter, &msg, 1); @@ -311,15 +324,16 @@ static void tda827xa_tune(struct i2c_client *c, u16 ifc, unsigned int freq) i2c_transfer(c->adapter, &msg, 1); tuner_reg[0] = 0xc0; - tuner_reg[1] = 0x19 + (t->tda827x_lpsel << 1); + tuner_reg[1] = 0x19 + (priv->tda827x_lpsel << 1); i2c_transfer(c->adapter, &msg, 1); } static void tda827xa_agcf(struct i2c_client *c) { struct tuner *t = i2c_get_clientdata(c); + struct tda8290_priv *priv = t->priv; unsigned char data[] = {0x80, 0x2c}; - struct i2c_msg msg = {.addr = t->tda827x_addr, .buf = data, + struct i2c_msg msg = {.addr = priv->tda827x_addr, .buf = data, .flags = 0, .len = 2}; i2c_transfer(c->adapter, &msg, 1); } @@ -347,8 +361,9 @@ static void tda8290_i2c_bridge(struct i2c_client *c, int close) static int tda8290_tune(struct i2c_client *c, u16 ifc, unsigned int freq) { struct tuner *t = i2c_get_clientdata(c); + struct tda8290_priv *priv = t->priv; unsigned char soft_reset[] = { 0x00, 0x00 }; - unsigned char easy_mode[] = { 0x01, t->tda8290_easy_mode }; + unsigned char easy_mode[] = { 0x01, priv->tda8290_easy_mode }; unsigned char expert_mode[] = { 0x01, 0x80 }; unsigned char agc_out_on[] = { 0x02, 0x00 }; unsigned char gainset_off[] = { 0x28, 0x14 }; @@ -375,18 +390,18 @@ static int tda8290_tune(struct i2c_client *c, u16 ifc, unsigned int freq) i2c_master_send(c, soft_reset, 2); msleep(1); - expert_mode[1] = t->tda8290_easy_mode + 0x80; + expert_mode[1] = priv->tda8290_easy_mode + 0x80; i2c_master_send(c, expert_mode, 2); i2c_master_send(c, gainset_off, 2); i2c_master_send(c, if_agc_spd, 2); - if (t->tda8290_easy_mode & 0x60) + if (priv->tda8290_easy_mode & 0x60) i2c_master_send(c, adc_head_9, 2); else i2c_master_send(c, adc_head_6, 2); i2c_master_send(c, pll_bw_nom, 2); tda8290_i2c_bridge(c, 1); - if (t->tda827x_ver != 0) + if (priv->tda827x_ver != 0) tda827xa_tune(c, ifc, freq); else tda827x_tune(c, ifc, freq); @@ -418,7 +433,7 @@ static int tda8290_tune(struct i2c_client *c, u16 ifc, unsigned int freq) if ((agc_stat > 115) || !(pll_stat & 0x80)) { tuner_dbg("adjust gain, step 2. Agc: %d, lock: %d\n", agc_stat, pll_stat & 0x80); - if (t->tda827x_ver != 0) + if (priv->tda827x_ver != 0) tda827xa_agcf(c); else tda827x_agcf(c); @@ -437,7 +452,7 @@ static int tda8290_tune(struct i2c_client *c, u16 ifc, unsigned int freq) } /* l/ l' deadlock? */ - if(t->tda8290_easy_mode & 0x60) { + if(priv->tda8290_easy_mode & 0x60) { i2c_master_send(c, &addr_adc_sat, 1); i2c_master_recv(c, &adc_sat, 1); i2c_master_send(c, &addr_pll_stat, 1); @@ -459,41 +474,42 @@ static int tda8290_tune(struct i2c_client *c, u16 ifc, unsigned int freq) static void set_audio(struct tuner *t) { + struct tda8290_priv *priv = t->priv; char* mode; - t->tda827x_lpsel = 0; + priv->tda827x_lpsel = 0; if (t->std & V4L2_STD_MN) { - t->sgIF = 92; - t->tda8290_easy_mode = 0x01; - t->tda827x_lpsel = 1; + priv->sgIF = 92; + priv->tda8290_easy_mode = 0x01; + priv->tda827x_lpsel = 1; mode = "MN"; } else if (t->std & V4L2_STD_B) { - t->sgIF = 108; - t->tda8290_easy_mode = 0x02; + priv->sgIF = 108; + priv->tda8290_easy_mode = 0x02; mode = "B"; } else if (t->std & V4L2_STD_GH) { - t->sgIF = 124; - t->tda8290_easy_mode = 0x04; + priv->sgIF = 124; + priv->tda8290_easy_mode = 0x04; mode = "GH"; } else if (t->std & V4L2_STD_PAL_I) { - t->sgIF = 124; - t->tda8290_easy_mode = 0x08; + priv->sgIF = 124; + priv->tda8290_easy_mode = 0x08; mode = "I"; } else if (t->std & V4L2_STD_DK) { - t->sgIF = 124; - t->tda8290_easy_mode = 0x10; + priv->sgIF = 124; + priv->tda8290_easy_mode = 0x10; mode = "DK"; } else if (t->std & V4L2_STD_SECAM_L) { - t->sgIF = 124; - t->tda8290_easy_mode = 0x20; + priv->sgIF = 124; + priv->tda8290_easy_mode = 0x20; mode = "L"; } else if (t->std & V4L2_STD_SECAM_LC) { - t->sgIF = 20; - t->tda8290_easy_mode = 0x40; + priv->sgIF = 20; + priv->tda8290_easy_mode = 0x40; mode = "LC"; } else { - t->sgIF = 124; - t->tda8290_easy_mode = 0x10; + priv->sgIF = 124; + priv->tda8290_easy_mode = 0x10; mode = "xx"; } tuner_dbg("setting tda8290 to system %s\n", mode); @@ -502,9 +518,10 @@ static void set_audio(struct tuner *t) static void set_tv_freq(struct i2c_client *c, unsigned int freq) { struct tuner *t = i2c_get_clientdata(c); + struct tda8290_priv *priv = t->priv; set_audio(t); - tda8290_tune(c, t->sgIF, freq); + tda8290_tune(c, priv->sgIF, freq); } static void set_radio_freq(struct i2c_client *c, unsigned int freq) @@ -528,13 +545,14 @@ static int has_signal(struct i2c_client *c) static void standby(struct i2c_client *c) { struct tuner *t = i2c_get_clientdata(c); + struct tda8290_priv *priv = t->priv; unsigned char cb1[] = { 0x30, 0xD0 }; unsigned char tda8290_standby[] = { 0x00, 0x02 }; unsigned char tda8290_agc_tri[] = { 0x02, 0x20 }; - struct i2c_msg msg = {.addr = t->tda827x_addr, .flags=0, .buf=cb1, .len = 2}; + struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags=0, .buf=cb1, .len = 2}; tda8290_i2c_bridge(c, 1); - if (t->tda827x_ver != 0) + if (priv->tda827x_ver != 0) cb1[1] = 0x90; i2c_transfer(c->adapter, &msg, 1); tda8290_i2c_bridge(c, 0); @@ -560,13 +578,14 @@ static void tda8290_init_if(struct i2c_client *c) static void tda8290_init_tuner(struct i2c_client *c) { struct tuner *t = i2c_get_clientdata(c); + struct tda8290_priv *priv = t->priv; unsigned char tda8275_init[] = { 0x00, 0x00, 0x00, 0x40, 0xdC, 0x04, 0xAf, 0x3F, 0x2A, 0x04, 0xFF, 0x00, 0x00, 0x40 }; unsigned char tda8275a_init[] = { 0x00, 0x00, 0x00, 0x00, 0xdC, 0x05, 0x8b, 0x0c, 0x04, 0x20, 0xFF, 0x00, 0x00, 0x4b }; - struct i2c_msg msg = {.addr = t->tda827x_addr, .flags=0, + struct i2c_msg msg = {.addr = priv->tda827x_addr, .flags=0, .buf=tda8275_init, .len = 14}; - if (t->tda827x_ver != 0) + if (priv->tda827x_ver != 0) msg.buf = tda8275a_init; tda8290_i2c_bridge(c, 1); @@ -576,14 +595,36 @@ static void tda8290_init_tuner(struct i2c_client *c) /*---------------------------------------------------------------------*/ +static void tda8290_release(struct i2c_client *c) +{ + struct tuner *t = i2c_get_clientdata(c); + + kfree(t->priv); + t->priv = NULL; +} + +static struct tuner_operations tda8290_tuner_ops = { + .set_tv_freq = set_tv_freq, + .set_radio_freq = set_radio_freq, + .has_signal = has_signal, + .standby = standby, + .release = tda8290_release, +}; + int tda8290_init(struct i2c_client *c) { + struct tda8290_priv *priv = NULL; struct tuner *t = i2c_get_clientdata(c); u8 data; int i, ret, tuners_found; u32 tuner_addrs; struct i2c_msg msg = {.flags=I2C_M_RD, .buf=&data, .len = 1}; + priv = kzalloc(sizeof(struct tda8290_priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + t->priv = priv; + tda8290_i2c_bridge(c, 1); /* probe for tuner chip */ tuners_found = 0; @@ -618,7 +659,7 @@ int tda8290_init(struct i2c_client *c) tuner_addrs = tuner_addrs & 0xff; tuner_info ("setting tuner address to %x\n", tuner_addrs); } - t->tda827x_addr = tuner_addrs; + priv->tda827x_addr = tuner_addrs; msg.addr = tuner_addrs; tda8290_i2c_bridge(c, 1); @@ -627,18 +668,16 @@ int tda8290_init(struct i2c_client *c) tuner_warn ("TDA827x access failed!\n"); if ((data & 0x3c) == 0) { strlcpy(c->name, "tda8290+75", sizeof(c->name)); - t->tda827x_ver = 0; + priv->tda827x_ver = 0; } else { strlcpy(c->name, "tda8290+75a", sizeof(c->name)); - t->tda827x_ver = 2; + priv->tda827x_ver = 2; } tuner_info("type set to %s\n", c->name); - t->set_tv_freq = set_tv_freq; - t->set_radio_freq = set_radio_freq; - t->has_signal = has_signal; - t->standby = standby; - t->tda827x_lpsel = 0; + memcpy(&t->ops, &tda8290_tuner_ops, sizeof(struct tuner_operations)); + + priv->tda827x_lpsel = 0; t->mode = V4L2_TUNER_ANALOG_TV; tda8290_init_tuner(c); diff --git a/drivers/media/video/tda9887.c b/drivers/media/video/tda9887.c index fde576f..a8f7732 100644 --- a/drivers/media/video/tda9887.c +++ b/drivers/media/video/tda9887.c @@ -11,6 +11,7 @@ #include <media/v4l2-common.h> #include <media/tuner.h> +#include "tuner-driver.h" /* Chips: @@ -29,6 +30,9 @@ printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.name, \ i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0) +struct tda9887_priv { + unsigned char data[4]; +}; /* ---------------------------------------------------------------------- */ @@ -508,10 +512,11 @@ static int tda9887_status(struct tuner *t) static void tda9887_configure(struct i2c_client *client) { struct tuner *t = i2c_get_clientdata(client); + struct tda9887_priv *priv = t->priv; int rc; - memset(t->tda9887_data,0,sizeof(t->tda9887_data)); - tda9887_set_tvnorm(t,t->tda9887_data); + memset(priv->data,0,sizeof(priv->data)); + tda9887_set_tvnorm(t,priv->data); /* A note on the port settings: These settings tend to depend on the specifics of the board. @@ -526,22 +531,22 @@ static void tda9887_configure(struct i2c_client *client) the ports should be set to active (0), but, again, that may differ depending on the precise hardware configuration. */ - t->tda9887_data[1] |= cOutputPort1Inactive; - t->tda9887_data[1] |= cOutputPort2Inactive; + priv->data[1] |= cOutputPort1Inactive; + priv->data[1] |= cOutputPort2Inactive; - tda9887_set_config(t,t->tda9887_data); - tda9887_set_insmod(t,t->tda9887_data); + tda9887_set_config(t,priv->data); + tda9887_set_insmod(t,priv->data); if (t->mode == T_STANDBY) { - t->tda9887_data[1] |= cForcedMuteAudioON; + priv->data[1] |= cForcedMuteAudioON; } tda9887_dbg("writing: b=0x%02x c=0x%02x e=0x%02x\n", - t->tda9887_data[1],t->tda9887_data[2],t->tda9887_data[3]); + priv->data[1],priv->data[2],priv->data[3]); if (tuner_debug > 1) - dump_write_message(t, t->tda9887_data); + dump_write_message(t, priv->data); - if (4 != (rc = i2c_master_send(&t->i2c,t->tda9887_data,4))) + if (4 != (rc = i2c_master_send(&t->i2c,priv->data,4))) tda9887_info("i2c i/o error: rc == %d (should be 4)\n",rc); if (tuner_debug > 2) { @@ -555,7 +560,8 @@ static void tda9887_configure(struct i2c_client *client) static void tda9887_tuner_status(struct i2c_client *client) { struct tuner *t = i2c_get_clientdata(client); - tda9887_info("Data bytes: b=0x%02x c=0x%02x e=0x%02x\n", t->tda9887_data[1], t->tda9887_data[2], t->tda9887_data[3]); + struct tda9887_priv *priv = t->priv; + tda9887_info("Data bytes: b=0x%02x c=0x%02x e=0x%02x\n", priv->data[1], priv->data[2], priv->data[3]); } static int tda9887_get_afc(struct i2c_client *client) @@ -586,20 +592,39 @@ static void tda9887_set_freq(struct i2c_client *client, unsigned int freq) tda9887_configure(client); } +static void tda9887_release(struct i2c_client *c) +{ + struct tuner *t = i2c_get_clientdata(c); + + kfree(t->priv); + t->priv = NULL; +} + +static struct tuner_operations tda9887_tuner_ops = { + .set_tv_freq = tda9887_set_freq, + .set_radio_freq = tda9887_set_freq, + .standby = tda9887_standby, + .tuner_status = tda9887_tuner_status, + .get_afc = tda9887_get_afc, + .release = tda9887_release, +}; + int tda9887_tuner_init(struct i2c_client *c) { + struct tda9887_priv *priv = NULL; struct tuner *t = i2c_get_clientdata(c); + priv = kzalloc(sizeof(struct tda9887_priv), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + t->priv = priv; + strlcpy(c->name, "tda9887", sizeof(c->name)); tda9887_info("tda988[5/6/7] found @ 0x%x (%s)\n", t->i2c.addr, t->i2c.driver->driver.name); - t->set_tv_freq = tda9887_set_freq; - t->set_radio_freq = tda9887_set_freq; - t->standby = tda9887_standby; - t->tuner_status = tda9887_tuner_status; - t->get_afc = tda9887_get_afc; + memcpy(&t->ops, &tda9887_tuner_ops, sizeof(struct tuner_operations)); return 0; } diff --git a/drivers/media/video/tea5761.c b/drivers/media/video/tea5761.c new file mode 100644 index 0000000..ae105c2 --- /dev/null +++ b/drivers/media/video/tea5761.c @@ -0,0 +1,243 @@ +/* + * For Philips TEA5761 FM Chip + * I2C address is allways 0x20 (0x10 at 7-bit mode). + * + * Copyright (c) 2005-2007 Mauro Carvalho Chehab (mchehab@infradead.org) + * This code is placed under the terms of the GNUv2 General Public License + * + */ + +#include <linux/i2c.h> +#include <linux/videodev.h> +#include <linux/delay.h> +#include <media/tuner.h> +#include "tuner-driver.h" + +#define PREFIX "TEA5761 " + +/* from tuner-core.c */ +extern int tuner_debug; + +/*****************************************************************************/ + +/*************************** + * TEA5761HN I2C registers * + ***************************/ + +/* INTREG - Read: bytes 0 and 1 / Write: byte 0 */ + + /* first byte for reading */ +#define TEA5761_INTREG_IFFLAG 0x10 +#define TEA5761_INTREG_LEVFLAG 0x8 +#define TEA5761_INTREG_FRRFLAG 0x2 +#define TEA5761_INTREG_BLFLAG 0x1 + + /* second byte for reading / byte for writing */ +#define TEA5761_INTREG_IFMSK 0x10 +#define TEA5761_INTREG_LEVMSK 0x8 +#define TEA5761_INTREG_FRMSK 0x2 +#define TEA5761_INTREG_BLMSK 0x1 + +/* FRQSET - Read: bytes 2 and 3 / Write: byte 1 and 2 */ + + /* First byte */ +#define TEA5761_FRQSET_SEARCH_UP 0x80 /* 1=Station search from botton to up */ +#define TEA5761_FRQSET_SEARCH_MODE 0x40 /* 1=Search mode */ + + /* Bits 0-5 for divider MSB */ + + /* Second byte */ + /* Bits 0-7 for divider LSB */ + +/* TNCTRL - Read: bytes 4 and 5 / Write: Bytes 3 and 4 */ + + /* first byte */ + +#define TEA5761_TNCTRL_PUPD_0 0x40 /* Power UP/Power Down MSB */ +#define TEA5761_TNCTRL_BLIM 0X20 /* 1= Japan Frequencies, 0= European frequencies */ +#define TEA5761_TNCTRL_SWPM 0x10 /* 1= software port is FRRFLAG */ +#define TEA5761_TNCTRL_IFCTC 0x08 /* 1= IF count time 15.02 ms, 0= IF count time 2.02 ms */ +#define TEA5761_TNCTRL_AFM 0x04 +#define TEA5761_TNCTRL_SMUTE 0x02 /* 1= Soft mute */ +#define TEA5761_TNCTRL_SNC 0x01 + + /* second byte */ + +#define TEA5761_TNCTRL_MU 0x80 /* 1=Hard mute */ +#define TEA5761_TNCTRL_SSL_1 0x40 +#define TEA5761_TNCTRL_SSL_0 0x20 +#define TEA5761_TNCTRL_HLSI 0x10 +#define TEA5761_TNCTRL_MST 0x08 /* 1 = mono */ +#define TEA5761_TNCTRL_SWP 0x04 +#define TEA5761_TNCTRL_DTC 0x02 /* 1 = deemphasis 50 us, 0 = deemphasis 75 us */ +#define TEA5761_TNCTRL_AHLSI 0x01 + +/* FRQCHECK - Read: bytes 6 and 7 */ + /* First byte */ + + /* Bits 0-5 for divider MSB */ + + /* Second byte */ + /* Bits 0-7 for divider LSB */ + +/* TUNCHECK - Read: bytes 8 and 9 */ + + /* First byte */ +#define TEA5761_TUNCHECK_IF_MASK 0x7e /* IF count */ +#define TEA5761_TUNCHECK_TUNTO 0x01 + + /* Second byte */ +#define TEA5761_TUNCHECK_LEV_MASK 0xf0 /* Level Count */ +#define TEA5761_TUNCHECK_LD 0x08 +#define TEA5761_TUNCHECK_STEREO 0x04 + +/* TESTREG - Read: bytes 10 and 11 / Write: bytes 5 and 6 */ + + /* All zero = no test mode */ + +/* MANID - Read: bytes 12 and 13 */ + + /* First byte - should be 0x10 */ +#define TEA5767_MANID_VERSION_MASK 0xf0 /* Version = 1 */ +#define TEA5767_MANID_ID_MSB_MASK 0x0f /* Manufacurer ID - should be 0 */ + + /* Second byte - Should be 0x2b */ + +#define TEA5767_MANID_ID_LSB_MASK 0xfe /* Manufacturer ID - should be 0x15 */ +#define TEA5767_MANID_IDAV 0x01 /* 1 = Chip has ID, 0 = Chip has no ID */ + +/* Chip ID - Read: bytes 14 and 15 */ + + /* First byte - should be 0x57 */ + + /* Second byte - should be 0x61 */ + +/*****************************************************************************/ + +static void set_tv_freq(struct i2c_client *c, unsigned int freq) +{ + struct tuner *t = i2c_get_clientdata(c); + + tuner_warn("This tuner doesn't support TV freq.\n"); +} + +#define FREQ_OFFSET 0 /* for TEA5767, it is 700 to give the right freq */ +static void tea5761_status_dump(unsigned char *buffer) +{ + unsigned int div, frq; + + div = ((buffer[2] & 0x3f) << 8) | buffer[3]; + + frq = 1000 * (div * 32768 / 1000 + FREQ_OFFSET + 225) / 4; /* Freq in KHz */ + + printk(PREFIX "Frequency %d.%03d KHz (divider = 0x%04x)\n", + frq / 1000, frq % 1000, div); +} + +/* Freq should be specifyed at 62.5 Hz */ +static void set_radio_freq(struct i2c_client *c, unsigned int frq) +{ + struct tuner *t = i2c_get_clientdata(c); + unsigned char buffer[7] = {0, 0, 0, 0, 0, 0, 0 }; + unsigned div; + int rc; + + tuner_dbg (PREFIX "radio freq counter %d\n", frq); + + if (t->mode == T_STANDBY) { + tuner_dbg("TEA5761 set to standby mode\n"); + buffer[5] |= TEA5761_TNCTRL_MU; + } else { + buffer[4] |= TEA5761_TNCTRL_PUPD_0; + } + + + if (t->audmode == V4L2_TUNER_MODE_MONO) { + tuner_dbg("TEA5761 set to mono\n"); + buffer[5] |= TEA5761_TNCTRL_MST; +; + } else { + tuner_dbg("TEA5761 set to stereo\n"); + } + + div = (1000 * (frq * 4 / 16 + 700 + 225) ) >> 15; + buffer[1] = (div >> 8) & 0x3f; + buffer[2] = div & 0xff; + + if (tuner_debug) + tea5761_status_dump(buffer); + + if (7 != (rc = i2c_master_send(c, buffer, 7))) + tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); +} + +static int tea5761_signal(struct i2c_client *c) +{ + unsigned char buffer[16]; + int rc; + struct tuner *t = i2c_get_clientdata(c); + + memset(buffer, 0, sizeof(buffer)); + if (16 != (rc = i2c_master_recv(c, buffer, 16))) + tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); + + return ((buffer[9] & TEA5761_TUNCHECK_LEV_MASK) << (13 - 4)); +} + +static int tea5761_stereo(struct i2c_client *c) +{ + unsigned char buffer[16]; + int rc; + struct tuner *t = i2c_get_clientdata(c); + + memset(buffer, 0, sizeof(buffer)); + if (16 != (rc = i2c_master_recv(c, buffer, 16))) + tuner_warn("i2c i/o error: rc == %d (should be 5)\n", rc); + + rc = buffer[9] & TEA5761_TUNCHECK_STEREO; + + tuner_dbg("TEA5761 radio ST GET = %02x\n", rc); + + return (rc ? V4L2_TUNER_SUB_STEREO : 0); +} + +int tea5761_autodetection(struct i2c_client *c) +{ + unsigned char buffer[16]; + int rc; + struct tuner *t = i2c_get_clientdata(c); + + if (16 != (rc = i2c_master_recv(c, buffer, 16))) { + tuner_warn("it is not a TEA5761. Received %i chars.\n", rc); + return EINVAL; + } + + if (!((buffer[13] != 0x2b) || (buffer[14] != 0x57) || (buffer[15] != 0x061))) { + tuner_warn("Manufacturer ID= 0x%02x, Chip ID = %02x%02x. It is not a TEA5761\n",buffer[13],buffer[14],buffer[15]); + return EINVAL; + } + tuner_warn("TEA5761 detected.\n"); + return 0; +} + +static struct tuner_operations tea5761_tuner_ops = { + .set_tv_freq = set_tv_freq, + .set_radio_freq = set_radio_freq, + .has_signal = tea5761_signal, + .is_stereo = tea5761_stereo, +}; + +int tea5761_tuner_init(struct i2c_client *c) +{ + struct tuner *t = i2c_get_clientdata(c); + + if (tea5761_autodetection(c) == EINVAL) + return EINVAL; + + tuner_info("type set to %d (%s)\n", t->type, "Philips TEA5761HN FM Radio"); + strlcpy(c->name, "tea5761", sizeof(c->name)); + + memcpy(&t->ops, &tea5761_tuner_ops, sizeof(struct tuner_operations)); + + return (0); +} diff --git a/drivers/media/video/tea5767.c b/drivers/media/video/tea5767.c index d1c4178..4985d47 100644 --- a/drivers/media/video/tea5767.c +++ b/drivers/media/video/tea5767.c @@ -13,7 +13,7 @@ #include <linux/i2c.h> #include <linux/videodev.h> #include <linux/delay.h> -#include <media/tuner.h> +#include "tuner-driver.h" #define PREFIX "TEA5767 " @@ -343,6 +343,14 @@ int tea5767_autodetection(struct i2c_client *c) return 0; } +static struct tuner_operations tea5767_tuner_ops = { + .set_tv_freq = set_tv_freq, + .set_radio_freq = set_radio_freq, + .has_signal = tea5767_signal, + .is_stereo = tea5767_stereo, + .standby = tea5767_standby, +}; + int tea5767_tuner_init(struct i2c_client *c) { struct tuner *t = i2c_get_clientdata(c); @@ -350,11 +358,7 @@ int tea5767_tuner_init(struct i2c_client *c) tuner_info("type set to %d (%s)\n", t->type, "Philips TEA5767HN FM Radio"); strlcpy(c->name, "tea5767", sizeof(c->name)); - t->set_tv_freq = set_tv_freq; - t->set_radio_freq = set_radio_freq; - t->has_signal = tea5767_signal; - t->is_stereo = tea5767_stereo; - t->standby = tea5767_standby; + memcpy(&t->ops, &tea5767_tuner_ops, sizeof(struct tuner_operations)); return (0); } diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 505591a..e646465 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -20,11 +20,15 @@ #include <media/tuner.h> #include <media/v4l2-common.h> +#include "tuner-driver.h" #define UNSET (-1U) /* standard i2c insmod options */ static unsigned short normal_i2c[] = { +#ifdef CONFIG_TUNER_TEA5761 + 0x10, +#endif 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, @@ -77,7 +81,7 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq) tuner_warn ("tuner type not set\n"); return; } - if (NULL == t->set_tv_freq) { + if (NULL == t->ops.set_tv_freq) { tuner_warn ("Tuner has no way to set tv freq\n"); return; } @@ -92,7 +96,7 @@ static void set_tv_freq(struct i2c_client *c, unsigned int freq) else freq = tv_range[1] * 16; } - t->set_tv_freq(c, freq); + t->ops.set_tv_freq(c, freq); } static void set_radio_freq(struct i2c_client *c, unsigned int freq) @@ -103,7 +107,7 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq) tuner_warn ("tuner type not set\n"); return; } - if (NULL == t->set_radio_freq) { + if (NULL == t->ops.set_radio_freq) { tuner_warn ("tuner has no way to set radio frequency\n"); return; } @@ -119,7 +123,7 @@ static void set_radio_freq(struct i2c_client *c, unsigned int freq) freq = radio_range[1] * 16000; } - t->set_radio_freq(c, freq); + t->ops.set_radio_freq(c, freq); } static void set_freq(struct i2c_client *c, unsigned long freq) @@ -174,6 +178,14 @@ static void set_type(struct i2c_client *c, unsigned int type, return; } + /* discard private data, in case set_type() was previously called */ + if (t->ops.release) + t->ops.release(c); + else { + kfree(t->priv); + t->priv = NULL; + } + switch (t->type) { case TUNER_MT2032: microtune_init(c); @@ -189,6 +201,16 @@ static void set_type(struct i2c_client *c, unsigned int type, } t->mode_mask = T_RADIO; break; +#ifdef CONFIG_TUNER_TEA5761 + case TUNER_TEA5761: + if (tea5761_tuner_init(c) == EINVAL) { + t->type = TUNER_ABSENT; + t->mode_mask = T_UNINITIALIZED; + return; + } + t->mode_mask = T_RADIO; + break; +#endif case TUNER_PHILIPS_FMD1216ME_MK3: buffer[0] = 0x0b; buffer[1] = 0xdc; @@ -408,11 +430,11 @@ static void tuner_status(struct i2c_client *client) tuner_info("Standard: 0x%08lx\n", (unsigned long)t->std); if (t->mode != V4L2_TUNER_RADIO) return; - if (t->has_signal) { - tuner_info("Signal strength: %d\n", t->has_signal(client)); + if (t->ops.has_signal) { + tuner_info("Signal strength: %d\n", t->ops.has_signal(client)); } - if (t->is_stereo) { - tuner_info("Stereo: %s\n", t->is_stereo(client) ? "yes" : "no"); + if (t->ops.is_stereo) { + tuner_info("Stereo: %s\n", t->ops.is_stereo(client) ? "yes" : "no"); } } @@ -437,10 +459,9 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) memcpy(&t->i2c, &client_template, sizeof(struct i2c_client)); i2c_set_clientdata(&t->i2c, t); t->type = UNSET; - t->radio_if2 = 10700 * 1000; /* 10.7MHz - FM radio */ t->audmode = V4L2_TUNER_MODE_STEREO; t->mode_mask = T_UNINITIALIZED; - t->tuner_status = tuner_status; + t->ops.tuner_status = tuner_status; if (show_i2c) { unsigned char buffer[16]; @@ -460,6 +481,19 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) /* autodetection code based on the i2c addr */ if (!no_autodetect) { switch (addr) { +#ifdef CONFIG_TUNER_TEA5761 + case 0x10: + if (tea5761_autodetection(&t->i2c) != EINVAL) { + t->type = TUNER_TEA5761; + t->mode_mask = T_RADIO; + t->mode = T_STANDBY; + t->radio_freq = 87.5 * 16000; /* Sets freq to FM range */ + default_mode_mask &= ~T_RADIO; + + goto register_client; + } + break; +#endif case 0x42: case 0x43: case 0x4a: @@ -533,6 +567,11 @@ static int tuner_detach(struct i2c_client *client) return err; } + if (t->ops.release) + t->ops.release(client); + else { + kfree(t->priv); + } kfree(t); return 0; } @@ -553,8 +592,8 @@ static inline int set_mode(struct i2c_client *client, struct tuner *t, int mode, if (check_mode(t, cmd) == EINVAL) { t->mode = T_STANDBY; - if (t->standby) - t->standby (client); + if (t->ops.standby) + t->ops.standby (client); return EINVAL; } return 0; @@ -602,8 +641,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) if (check_mode(t, "TUNER_SET_STANDBY") == EINVAL) return 0; t->mode = T_STANDBY; - if (t->standby) - t->standby (client); + if (t->ops.standby) + t->ops.standby (client); break; #ifdef CONFIG_VIDEO_V4L1 case VIDIOCSAUDIO: @@ -662,10 +701,10 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) return 0; if (V4L2_TUNER_RADIO == t->mode) { - if (t->has_signal) - vt->signal = t->has_signal(client); - if (t->is_stereo) { - if (t->is_stereo(client)) + if (t->ops.has_signal) + vt->signal = t->ops.has_signal(client); + if (t->ops.is_stereo) { + if (t->ops.is_stereo(client)) vt->flags |= VIDEO_TUNER_STEREO_ON; else @@ -693,8 +732,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) if (check_v4l2(t) == EINVAL) return 0; - if (V4L2_TUNER_RADIO == t->mode && t->is_stereo) - va->mode = t->is_stereo(client) + if (V4L2_TUNER_RADIO == t->mode && t->ops.is_stereo) + va->mode = t->ops.is_stereo(client) ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO; return 0; } @@ -759,8 +798,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) switch_v4l2(); tuner->type = t->mode; - if (t->get_afc) - tuner->afc=t->get_afc(client); + if (t->ops.get_afc) + tuner->afc=t->ops.get_afc(client); if (t->mode == V4L2_TUNER_ANALOG_TV) tuner->capability |= V4L2_TUNER_CAP_NORM; if (t->mode != V4L2_TUNER_RADIO) { @@ -770,13 +809,13 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) } /* radio mode */ - if (t->has_signal) - tuner->signal = t->has_signal(client); + if (t->ops.has_signal) + tuner->signal = t->ops.has_signal(client); tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - if (t->is_stereo) { - tuner->rxsubchans = t->is_stereo(client) ? + if (t->ops.is_stereo) { + tuner->rxsubchans = t->ops.is_stereo(client) ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; } @@ -804,8 +843,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) break; } case VIDIOC_LOG_STATUS: - if (t->tuner_status) - t->tuner_status(client); + if (t->ops.tuner_status) + t->ops.tuner_status(client); break; } diff --git a/drivers/media/video/tuner-driver.h b/drivers/media/video/tuner-driver.h new file mode 100644 index 0000000..0334a91 --- /dev/null +++ b/drivers/media/video/tuner-driver.h @@ -0,0 +1,107 @@ +/* + tuner-driver.h - interface for different tuners + + Copyright (C) 1997 Markus Schroeder (schroedm@uni-duesseldorf.de) + minor modifications by Ralph Metzler (rjkm@thp.uni-koeln.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 __TUNER_HW_H__ +#define __TUNER_HW_H__ + +#include <linux/videodev2.h> +#include <linux/i2c.h> + +extern unsigned const int tuner_count; + +struct tuner_operations { + void (*set_tv_freq)(struct i2c_client *c, unsigned int freq); + void (*set_radio_freq)(struct i2c_client *c, unsigned int freq); + int (*has_signal)(struct i2c_client *c); + int (*is_stereo)(struct i2c_client *c); + int (*get_afc)(struct i2c_client *c); + void (*tuner_status)(struct i2c_client *c); + void (*standby)(struct i2c_client *c); + void (*release)(struct i2c_client *c); +}; + +struct tuner { + /* device */ + struct i2c_client i2c; + + unsigned int type; /* chip type */ + + unsigned int mode; + unsigned int mode_mask; /* Combination of allowable modes */ + + unsigned int tv_freq; /* keep track of the current settings */ + unsigned int radio_freq; + u16 last_div; + unsigned int audmode; + v4l2_std_id std; + + int using_v4l2; + void *priv; + + /* used by tda9887 */ + unsigned int tda9887_config; + + unsigned int config; + int (*tuner_callback) (void *dev, int command,int arg); + + struct tuner_operations ops; +}; + +/* ------------------------------------------------------------------------ */ + +extern int default_tuner_init(struct i2c_client *c); + +extern int tda9887_tuner_init(struct i2c_client *c); + +extern int microtune_init(struct i2c_client *c); + +extern int tda8290_init(struct i2c_client *c); +extern int tda8290_probe(struct i2c_client *c); + +extern int tea5761_tuner_init(struct i2c_client *c); +extern int tea5761_autodetection(struct i2c_client *c); + +extern int tea5767_autodetection(struct i2c_client *c); +extern int tea5767_tuner_init(struct i2c_client *c); + +/* ------------------------------------------------------------------------ */ + +#define tuner_warn(fmt, arg...) do {\ + printk(KERN_WARNING "%s %d-%04x: " fmt, t->i2c.driver->driver.name, \ + i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0) +#define tuner_info(fmt, arg...) do {\ + printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.driver->driver.name, \ + i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0) +#define tuner_dbg(fmt, arg...) do {\ + extern int tuner_debug; \ + if (tuner_debug) \ + printk(KERN_DEBUG "%s %d-%04x: " fmt, t->i2c.driver->driver.name, \ + i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0) + +#endif /* __TUNER_HW_H__ */ + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/video/tuner-simple.c b/drivers/media/video/tuner-simple.c index c40b92c..2d57e8b 100644 --- a/drivers/media/video/tuner-simple.c +++ b/drivers/media/video/tuner-simple.c @@ -8,6 +8,8 @@ #include <linux/videodev.h> #include <media/tuner.h> #include <media/v4l2-common.h> +#include <media/tuner-types.h> +#include "tuner-driver.h" static int offset = 0; module_param(offset, int, 0664); @@ -54,9 +56,9 @@ MODULE_PARM_DESC(offset,"Allows to specify an offset for tuner"); sound 2 33.16 - - NICAM 33.05 33.05 39.80 */ -#define PHILIPS_MF_SET_BG 0x01 /* Bit 2 must be zero, Bit 3 is system output */ -#define PHILIPS_MF_SET_PAL_L 0x03 // France -#define PHILIPS_MF_SET_PAL_L2 0x02 // L' +#define PHILIPS_MF_SET_STD_BG 0x01 /* Bit 2 must be zero, Bit 3 is system output */ +#define PHILIPS_MF_SET_STD_L 0x03 /* Used on Secam France */ +#define PHILIPS_MF_SET_STD_LC 0x02 /* Used on SECAM L' */ /* Control byte */ @@ -207,11 +209,11 @@ static void default_set_tv_freq(struct i2c_client *c, unsigned int freq) /* 0x04 -> ??? PAL others / SECAM others ??? */ cb &= ~0x03; if (t->std & V4L2_STD_SECAM_L) //also valid for V4L2_STD_SECAM - cb |= PHILIPS_MF_SET_PAL_L; + cb |= PHILIPS_MF_SET_STD_L; else if (t->std & V4L2_STD_SECAM_LC) - cb |= PHILIPS_MF_SET_PAL_L2; + cb |= PHILIPS_MF_SET_STD_LC; else /* V4L2_STD_B|V4L2_STD_GH */ - cb |= PHILIPS_MF_SET_BG; + cb |= PHILIPS_MF_SET_STD_BG; break; case TUNER_TEMIC_4046FM5: @@ -479,6 +481,13 @@ static void default_set_radio_freq(struct i2c_client *c, unsigned int freq) tuner_warn("i2c i/o error: rc == %d (should be 4)\n",rc); } +static struct tuner_operations simple_tuner_ops = { + .set_tv_freq = default_set_tv_freq, + .set_radio_freq = default_set_radio_freq, + .has_signal = tuner_signal, + .is_stereo = tuner_stereo, +}; + int default_tuner_init(struct i2c_client *c) { struct tuner *t = i2c_get_clientdata(c); @@ -487,11 +496,7 @@ int default_tuner_init(struct i2c_client *c) t->type, tuners[t->type].name); strlcpy(c->name, tuners[t->type].name, sizeof(c->name)); - t->set_tv_freq = default_set_tv_freq; - t->set_radio_freq = default_set_radio_freq; - t->has_signal = tuner_signal; - t->is_stereo = tuner_stereo; - t->standby = NULL; + memcpy(&t->ops, &simple_tuner_ops, sizeof(struct tuner_operations)); return 0; } diff --git a/drivers/media/video/tuner-types.c b/drivers/media/video/tuner-types.c index 74c3e6f..417f642b 100644 --- a/drivers/media/video/tuner-types.c +++ b/drivers/media/video/tuner-types.c @@ -594,19 +594,19 @@ static struct tuner_params tuner_philips_pal_mk_params[] = { }, }; -/* ------------ TUNER_PHILIPS_ATSC - Philips ATSC ------------ */ +/* ---- TUNER_PHILIPS_ATSC - Philips FCV1236D (ATSC/NTSC) ---- */ -static struct tuner_range tuner_philips_atsc_ranges[] = { +static struct tuner_range tuner_philips_fcv1236d_ranges[] = { { 16 * 157.25 /*MHz*/, 0x8e, 0xa0, }, - { 16 * 454.00 /*MHz*/, 0x8e, 0x90, }, + { 16 * 451.25 /*MHz*/, 0x8e, 0x90, }, { 16 * 999.99 , 0x8e, 0x30, }, }; -static struct tuner_params tuner_philips_atsc_params[] = { +static struct tuner_params tuner_philips_fcv1236d_params[] = { { .type = TUNER_PARAM_TYPE_NTSC, - .ranges = tuner_philips_atsc_ranges, - .count = ARRAY_SIZE(tuner_philips_atsc_ranges), + .ranges = tuner_philips_fcv1236d_ranges, + .count = ARRAY_SIZE(tuner_philips_fcv1236d_ranges), }, }; @@ -1296,9 +1296,9 @@ struct tunertype tuners[] = { .count = ARRAY_SIZE(tuner_philips_pal_mk_params), }, [TUNER_PHILIPS_ATSC] = { /* Philips ATSC */ - .name = "Philips 1236D ATSC/NTSC dual in", - .params = tuner_philips_atsc_params, - .count = ARRAY_SIZE(tuner_philips_atsc_params), + .name = "Philips FCV1236D ATSC/NTSC dual in", + .params = tuner_philips_fcv1236d_params, + .count = ARRAY_SIZE(tuner_philips_fcv1236d_params), }, [TUNER_PHILIPS_FM1236_MK3] = { /* Philips NTSC */ .name = "Philips NTSC MK3 (FM1236MK3 or FM1236/F)", @@ -1463,6 +1463,10 @@ struct tunertype tuners[] = { .name = "Philips TDA988[5,6,7] IF PLL Demodulator", /* see tda9887.c for details */ }, + [TUNER_TEA5761] = { /* Philips RADIO */ + .name = "Philips TEA5761 FM Radio", + /* see tea5767.c for details */ + }, }; unsigned const int tuner_count = ARRAY_SIZE(tuners); diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index a1136da..fdc3def 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -183,7 +183,7 @@ hauppauge_tuner[] = { TUNER_ABSENT, "Silicon TDA8275C1 8290 FM"}, { TUNER_ABSENT, "Thompson DTT757"}, /* 80-89 */ - { TUNER_ABSENT, "Philips FQ1216LME MK3"}, + { TUNER_PHILIPS_FM1216ME_MK3, "Philips FQ1216LME MK3"}, { TUNER_LG_PAL_NEW_TAPC, "LG TAPC G701D"}, { TUNER_LG_NTSC_NEW_TAPC, "LG TAPC H791F"}, { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MB 3"}, @@ -490,7 +490,7 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, to indicate 4052 mux was removed in favor of using MSP inputs directly. */ audioic = eeprom_data[i+2] & 0x7f; - if (audioic < sizeof(audioIC)/sizeof(*audioIC)) + if (audioic < ARRAY_SIZE(audioIC)) tvee->audio_processor = audioIC[audioic].id; else tvee->audio_processor = AUDIO_CHIP_UNKNOWN; @@ -523,7 +523,7 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, to indicate 4052 mux was removed in favor of using MSP inputs directly. */ audioic = eeprom_data[i+1] & 0x7f; - if (audioic < sizeof(audioIC)/sizeof(*audioIC)) + if (audioic < ARRAY_SIZE(audioIC)) tvee->audio_processor = audioIC[audioic].id; else tvee->audio_processor = AUDIO_CHIP_UNKNOWN; @@ -678,7 +678,7 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, tveeprom_info("audio processor is unknown (no idx)\n"); tvee->audio_processor=AUDIO_CHIP_UNKNOWN; } else { - if (audioic < sizeof(audioIC)/sizeof(*audioIC)) + if (audioic < ARRAY_SIZE(audioIC)) tveeprom_info("audio processor is %s (idx %d)\n", audioIC[audioic].name,audioic); else diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index d5ec05f..e2f1c97 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -1006,7 +1006,7 @@ static int tvp5150_command(struct i2c_client *c, { struct v4l2_control *ctrl = arg; u8 i, n; - n = sizeof(tvp5150_qctrl) / sizeof(tvp5150_qctrl[0]); + n = ARRAY_SIZE(tvp5150_qctrl); for (i = 0; i < n; i++) if (ctrl->id == tvp5150_qctrl[i].id) { if (ctrl->value < diff --git a/drivers/media/video/usbvideo/konicawc.c b/drivers/media/video/usbvideo/konicawc.c index abe2146..491505d 100644 --- a/drivers/media/video/usbvideo/konicawc.c +++ b/drivers/media/video/usbvideo/konicawc.c @@ -236,7 +236,7 @@ static void konicawc_register_input(struct konicawc *cam, struct usb_device *dev input_dev->name = "Konicawc snapshot button"; input_dev->phys = cam->input_physname; usb_to_input_id(dev, &input_dev->id); - input_dev->cdev.dev = &dev->dev; + input_dev->dev.parent = &dev->dev; input_dev->evbit[0] = BIT(EV_KEY); input_dev->keybit[LONG(BTN_0)] = BIT(BTN_0); diff --git a/drivers/media/video/usbvideo/quickcam_messenger.c b/drivers/media/video/usbvideo/quickcam_messenger.c index ec0ff22..dd1a6d6 100644 --- a/drivers/media/video/usbvideo/quickcam_messenger.c +++ b/drivers/media/video/usbvideo/quickcam_messenger.c @@ -100,7 +100,7 @@ static void qcm_register_input(struct qcm *cam, struct usb_device *dev) input_dev->name = "QCM button"; input_dev->phys = cam->input_physname; usb_to_input_id(dev, &input_dev->id); - input_dev->cdev.dev = &dev->dev; + input_dev->dev.parent = &dev->dev; input_dev->evbit[0] = BIT(EV_KEY); input_dev->keybit[LONG(BTN_0)] = BIT(BTN_0); @@ -439,7 +439,7 @@ static int qcm_sensor_init(struct uvd *uvd) int ret; int i; - for (i=0; i < sizeof(regval_table)/sizeof(regval_table[0]) ; i++) { + for (i=0; i < ARRAY_SIZE(regval_table) ; i++) { CHECK_RET(ret, qcm_stv_setb(uvd->dev, regval_table[i].reg, regval_table[i].val)); diff --git a/drivers/media/video/usbvideo/vicam.c b/drivers/media/video/usbvideo/vicam.c index 982b115..2d9c0dd 100644 --- a/drivers/media/video/usbvideo/vicam.c +++ b/drivers/media/video/usbvideo/vicam.c @@ -42,7 +42,6 @@ #include <linux/usb.h> #include <linux/vmalloc.h> #include <linux/slab.h> -#include <linux/proc_fs.h> #include <linux/mutex.h> #include "usbvideo.h" @@ -417,11 +416,6 @@ struct vicam_camera { u8 open_count; u8 bulkEndpoint; int needsDummyRead; - -#if defined(CONFIG_VIDEO_PROC_FS) - struct proc_dir_entry *proc_dir; -#endif - }; static int vicam_probe( struct usb_interface *intf, const struct usb_device_id *id); @@ -1065,175 +1059,6 @@ vicam_mmap(struct file *file, struct vm_area_struct *vma) return 0; } -#if defined(CONFIG_VIDEO_PROC_FS) - -static struct proc_dir_entry *vicam_proc_root = NULL; - -static int vicam_read_helper(char *page, char **start, off_t off, - int count, int *eof, int value) -{ - char *out = page; - int len; - - out += sprintf(out, "%d",value); - - len = out - page; - len -= off; - if (len < count) { - *eof = 1; - if (len <= 0) - return 0; - } else - len = count; - - *start = page + off; - return len; -} - -static int vicam_read_proc_shutter(char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - return vicam_read_helper(page,start,off,count,eof, - ((struct vicam_camera *)data)->shutter_speed); -} - -static int vicam_read_proc_gain(char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - return vicam_read_helper(page,start,off,count,eof, - ((struct vicam_camera *)data)->gain); -} - -static int -vicam_write_proc_shutter(struct file *file, const char *buffer, - unsigned long count, void *data) -{ - u16 stmp; - char kbuf[8]; - struct vicam_camera *cam = (struct vicam_camera *) data; - - if (count > 6) - return -EINVAL; - - if (copy_from_user(kbuf, buffer, count)) - return -EFAULT; - - stmp = (u16) simple_strtoul(kbuf, NULL, 10); - if (stmp < 4 || stmp > 32000) - return -EINVAL; - - cam->shutter_speed = stmp; - - return count; -} - -static int -vicam_write_proc_gain(struct file *file, const char *buffer, - unsigned long count, void *data) -{ - u16 gtmp; - char kbuf[8]; - - struct vicam_camera *cam = (struct vicam_camera *) data; - - if (count > 4) - return -EINVAL; - - if (copy_from_user(kbuf, buffer, count)) - return -EFAULT; - - gtmp = (u16) simple_strtoul(kbuf, NULL, 10); - if (gtmp > 255) - return -EINVAL; - cam->gain = gtmp; - - return count; -} - -static void -vicam_create_proc_root(void) -{ - vicam_proc_root = proc_mkdir("video/vicam", NULL); - - if (vicam_proc_root) - vicam_proc_root->owner = THIS_MODULE; - else - printk(KERN_ERR - "could not create /proc entry for vicam!"); -} - -static void -vicam_destroy_proc_root(void) -{ - if (vicam_proc_root) - remove_proc_entry("video/vicam", 0); -} - -static void -vicam_create_proc_entry(struct vicam_camera *cam) -{ - char name[64]; - struct proc_dir_entry *ent; - - DBG(KERN_INFO "vicam: creating proc entry\n"); - - if (!vicam_proc_root || !cam) { - printk(KERN_INFO - "vicam: could not create proc entry, %s pointer is null.\n", - (!cam ? "camera" : "root")); - return; - } - - sprintf(name, "video%d", cam->vdev.minor); - - cam->proc_dir = proc_mkdir(name, vicam_proc_root); - - if ( !cam->proc_dir ) - return; // FIXME: We should probably return an error here - - ent = create_proc_entry("shutter", S_IFREG | S_IRUGO | S_IWUSR, - cam->proc_dir); - if (ent) { - ent->data = cam; - ent->read_proc = vicam_read_proc_shutter; - ent->write_proc = vicam_write_proc_shutter; - ent->size = 64; - } - - ent = create_proc_entry("gain", S_IFREG | S_IRUGO | S_IWUSR, - cam->proc_dir); - if (ent) { - ent->data = cam; - ent->read_proc = vicam_read_proc_gain; - ent->write_proc = vicam_write_proc_gain; - ent->size = 64; - } -} - -static void -vicam_destroy_proc_entry(void *ptr) -{ - struct vicam_camera *cam = (struct vicam_camera *) ptr; - char name[16]; - - if ( !cam->proc_dir ) - return; - - sprintf(name, "video%d", cam->vdev.minor); - remove_proc_entry("shutter", cam->proc_dir); - remove_proc_entry("gain", cam->proc_dir); - remove_proc_entry(name,vicam_proc_root); - cam->proc_dir = NULL; - -} - -#else -static inline void vicam_create_proc_root(void) { } -static inline void vicam_destroy_proc_root(void) { } -static inline void vicam_create_proc_entry(struct vicam_camera *cam) { } -static inline void vicam_destroy_proc_entry(void *ptr) { } -#endif - static const struct file_operations vicam_fops = { .owner = THIS_MODULE, .open = vicam_open, @@ -1330,8 +1155,6 @@ vicam_probe( struct usb_interface *intf, const struct usb_device_id *id) return -EIO; } - vicam_create_proc_entry(cam); - printk(KERN_INFO "ViCam webcam driver now controlling video device %d\n",cam->vdev.minor); usb_set_intfdata (intf, cam); @@ -1363,8 +1186,6 @@ vicam_disconnect(struct usb_interface *intf) cam->udev = NULL; - vicam_destroy_proc_entry(cam); - /* the only thing left to do is synchronize with * our close/release function on who should release * the camera memory. if there are any users using the @@ -1390,7 +1211,6 @@ usb_vicam_init(void) { int retval; DBG(KERN_INFO "ViCam-based WebCam driver startup\n"); - vicam_create_proc_root(); retval = usb_register(&vicam_driver); if (retval) printk(KERN_WARNING "usb_register failed!\n"); @@ -1404,7 +1224,6 @@ usb_vicam_exit(void) "ViCam-based WebCam driver shutdown\n"); usb_deregister(&vicam_driver); - vicam_destroy_proc_root(); } module_init(usb_vicam_init); diff --git a/drivers/media/video/usbvision/usbvision-cards.c b/drivers/media/video/usbvision/usbvision-cards.c index 51ab265..380564c 100644 --- a/drivers/media/video/usbvision/usbvision-cards.c +++ b/drivers/media/video/usbvision/usbvision-cards.c @@ -79,7 +79,7 @@ struct usbvision_device_data_st usbvision_device_data[] = { .Interface = -1, .Codec = CODEC_SAA7113, .VideoChannels = 2, - .VideoNorm = V4L2_STD_PAL, + .VideoNorm = V4L2_STD_NTSC, .AudioChannels = 1, .Radio = 0, .vbi = 1, @@ -311,8 +311,8 @@ struct usbvision_device_data_st usbvision_device_data[] = { .vbi = 1, .Tuner = 1, .TunerType = TUNER_PHILIPS_SECAM, - .X_Offset = -1, - .Y_Offset = -1, + .X_Offset = 0x80, + .Y_Offset = 0x16, .ModelString = "Hauppauge WinTV USB (PAL/SECAM L)", }, [HPG_WINTV_PAL_D_K] = { @@ -586,7 +586,7 @@ struct usbvision_device_data_st usbvision_device_data[] = { .Radio = 0, .vbi = 1, .Tuner = 1, - .TunerType = TUNER_PHILIPS_PAL, + .TunerType = TUNER_LG_PAL_NEW_TAPC, .X_Offset = 0, .Y_Offset = 3, .Dvi_yuv_override = 1, diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index 7df071eb..5b1e346 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -1742,7 +1742,7 @@ static int usbvision_set_video_format(struct usb_usbvision *usbvision, int forma format = ISOC_MODE_YUV420; } value[0] = 0x0A; //TODO: See the effect of the filter - value[1] = format; + value[1] = format; // Sets the VO_MODE register which follows FILT_CONT rc = usb_control_msg(usbvision->dev, usb_sndctrlpipe(usbvision->dev, 1), USBVISION_OP_CODE, USB_DIR_OUT | USB_TYPE_VENDOR | @@ -1831,10 +1831,10 @@ int usbvision_set_output(struct usb_usbvision *usbvision, int width, frameRate = FRAMERATE_MAX; } - if (usbvision->tvnorm->id & V4L2_STD_625_50) { + if (usbvision->tvnormId & V4L2_STD_625_50) { frameDrop = frameRate * 32 / 25 - 1; } - else if (usbvision->tvnorm->id & V4L2_STD_525_60) { + else if (usbvision->tvnormId & V4L2_STD_525_60) { frameDrop = frameRate * 32 / 30 - 1; } @@ -2067,7 +2067,7 @@ int usbvision_set_input(struct usb_usbvision *usbvision) } - if (usbvision->tvnorm->id & V4L2_STD_PAL) { + if (usbvision->tvnormId & V4L2_STD_PAL) { value[0] = 0xC0; value[1] = 0x02; //0x02C0 -> 704 Input video line length value[2] = 0x20; @@ -2076,7 +2076,7 @@ int usbvision_set_input(struct usb_usbvision *usbvision) value[5] = 0x00; //0x0060 -> 96 Input video h offset value[6] = 0x16; value[7] = 0x00; //0x0016 -> 22 Input video v offset - } else if (usbvision->tvnorm->id & V4L2_STD_SECAM) { + } else if (usbvision->tvnormId & V4L2_STD_SECAM) { value[0] = 0xC0; value[1] = 0x02; //0x02C0 -> 704 Input video line length value[2] = 0x20; @@ -2537,7 +2537,9 @@ void usbvision_stop_isoc(struct usb_usbvision *usbvision) int usbvision_muxsel(struct usb_usbvision *usbvision, int channel) { - int mode[4]; + /* inputs #0 and #3 are constant for every SAA711x. */ + /* inputs #1 and #2 are variable for SAA7111 and SAA7113 */ + int mode[4]= {SAA7115_COMPOSITE0, 0, 0, SAA7115_COMPOSITE3}; int audio[]= {1, 0, 0, 0}; struct v4l2_routing route; //channel 0 is TV with audiochannel 1 (tuner mono) @@ -2547,10 +2549,6 @@ int usbvision_muxsel(struct usb_usbvision *usbvision, int channel) RESTRICT_TO_RANGE(channel, 0, usbvision->video_inputs); usbvision->ctl_input = channel; - route.input = SAA7115_COMPOSITE1; - route.output = 0; - call_i2c_clients(usbvision, VIDIOC_INT_S_VIDEO_ROUTING,&route); - call_i2c_clients(usbvision, VIDIOC_S_INPUT, &usbvision->ctl_input); // set the new channel // Regular USB TV Tuners -> channel: 0 = Television, 1 = Composite, 2 = S-Video @@ -2558,28 +2556,27 @@ int usbvision_muxsel(struct usb_usbvision *usbvision, int channel) switch (usbvision_device_data[usbvision->DevModel].Codec) { case CODEC_SAA7113: - if (SwitchSVideoInput) { // To handle problems with S-Video Input for some devices. Use SwitchSVideoInput parameter when loading the module. - mode[2] = 1; + mode[1] = SAA7115_COMPOSITE2; + if (SwitchSVideoInput) { + /* To handle problems with S-Video Input for + * some devices. Use SwitchSVideoInput + * parameter when loading the module.*/ + mode[2] = SAA7115_COMPOSITE1; } else { - mode[2] = 7; - } - if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { - mode[0] = 0; mode[1] = 2; mode[3] = 3; // Special for four input devices - } - else { - mode[0] = 0; mode[1] = 2; //modes for regular saa7113 devices + mode[2] = SAA7115_SVIDEO1; } break; case CODEC_SAA7111: - mode[0] = 0; mode[1] = 1; mode[2] = 7; //modes for saa7111 - break; default: - mode[0] = 0; mode[1] = 1; mode[2] = 7; //default modes + /* modes for saa7111 */ + mode[1] = SAA7115_COMPOSITE1; + mode[2] = SAA7115_SVIDEO1; + break; } route.input = mode[channel]; + route.output = 0; call_i2c_clients(usbvision, VIDIOC_INT_S_VIDEO_ROUTING,&route); - usbvision->channel = channel; usbvision_set_audio(usbvision, audio[channel]); return 0; } diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index aa3258b..868b688 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -36,7 +36,8 @@ * - use submit_urb for all setup packets * - Fix memory settings for nt1004. It is 4 times as big as the * nt1003 memory. - * - Add audio on endpoint 3 for nt1004 chip. Seems impossible, needs a codec interface. Which one? + * - Add audio on endpoint 3 for nt1004 chip. + * Seems impossible, needs a codec interface. Which one? * - Clean up the driver. * - optimization for performance. * - Add Videotext capability (VBI). Working on it..... @@ -77,7 +78,8 @@ #include "usbvision.h" #include "usbvision-cards.h" -#define DRIVER_AUTHOR "Joerg Heckenbach <joerg@heckenbach-aw.de>, Dwaine Garden <DwaineGarden@rogers.com>" +#define DRIVER_AUTHOR "Joerg Heckenbach <joerg@heckenbach-aw.de>,\ + Dwaine Garden <DwaineGarden@rogers.com>" #define DRIVER_NAME "usbvision" #define DRIVER_ALIAS "USBVision" #define DRIVER_DESC "USBVision USB Video Device Driver for Linux" @@ -85,20 +87,25 @@ #define USBVISION_DRIVER_VERSION_MAJOR 0 #define USBVISION_DRIVER_VERSION_MINOR 9 #define USBVISION_DRIVER_VERSION_PATCHLEVEL 9 -#define USBVISION_DRIVER_VERSION KERNEL_VERSION(USBVISION_DRIVER_VERSION_MAJOR,USBVISION_DRIVER_VERSION_MINOR,USBVISION_DRIVER_VERSION_PATCHLEVEL) -#define USBVISION_VERSION_STRING __stringify(USBVISION_DRIVER_VERSION_MAJOR) "." __stringify(USBVISION_DRIVER_VERSION_MINOR) "." __stringify(USBVISION_DRIVER_VERSION_PATCHLEVEL) +#define USBVISION_DRIVER_VERSION KERNEL_VERSION(USBVISION_DRIVER_VERSION_MAJOR,\ +USBVISION_DRIVER_VERSION_MINOR,\ +USBVISION_DRIVER_VERSION_PATCHLEVEL) +#define USBVISION_VERSION_STRING __stringify(USBVISION_DRIVER_VERSION_MAJOR)\ + "." __stringify(USBVISION_DRIVER_VERSION_MINOR)\ + "." __stringify(USBVISION_DRIVER_VERSION_PATCHLEVEL) #define ENABLE_HEXDUMP 0 /* Enable if you need it */ #ifdef USBVISION_DEBUG #define PDEBUG(level, fmt, args...) \ - if (video_debug & (level)) info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ , ## args) + if (video_debug & (level)) \ + info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ ,\ + ## args) #else #define PDEBUG(level, fmt, args...) do {} while(0) #endif -#define DBG_IOCTL 1<<0 #define DBG_IO 1<<1 #define DBG_PROBE 1<<2 #define DBG_MMAP 1<<3 @@ -108,7 +115,8 @@ #define goto2next(str) while(*str!=' ') str++; while(*str==' ') str++; -static int usbvision_nr = 0; // sequential number of usbvision device +/* sequential number of usbvision device */ +static int usbvision_nr = 0; static struct usbvision_v4l2_format_st usbvision_v4l2_format[] = { { 1, 1, 8, V4L2_PIX_FMT_GREY , "GREY" }, @@ -121,55 +129,32 @@ static struct usbvision_v4l2_format_st usbvision_v4l2_format[] = { { 1, 2, 16, V4L2_PIX_FMT_YUV422P , "YUV422P" } }; -/* supported tv norms */ -static struct usbvision_tvnorm tvnorms[] = { - { - .name = "PAL", - .id = V4L2_STD_PAL, - }, { - .name = "NTSC", - .id = V4L2_STD_NTSC, - }, { - .name = "SECAM", - .id = V4L2_STD_SECAM, - }, { - .name = "PAL-M", - .id = V4L2_STD_PAL_M, - } -}; - -#define TVNORMS ARRAY_SIZE(tvnorms) - -// Function prototypes +/* Function prototypes */ static void usbvision_release(struct usb_usbvision *usbvision); -// Default initalization of device driver parameters -static int isocMode = ISOC_MODE_COMPRESS; // Set the default format for ISOC endpoint -static int video_debug = 0; // Set the default Debug Mode of the device driver -static int PowerOnAtOpen = 1; // Set the default device to power on at startup -static int video_nr = -1; // Sequential Number of Video Device -static int radio_nr = -1; // Sequential Number of Radio Device -static int vbi_nr = -1; // Sequential Number of VBI Device - -// Grab parameters for the device driver - -#if defined(module_param) // Showing parameters under SYSFS +/* Default initalization of device driver parameters */ +/* Set the default format for ISOC endpoint */ +static int isocMode = ISOC_MODE_COMPRESS; +/* Set the default Debug Mode of the device driver */ +static int video_debug = 0; +/* Set the default device to power on at startup */ +static int PowerOnAtOpen = 1; +/* Sequential Number of Video Device */ +static int video_nr = -1; +/* Sequential Number of Radio Device */ +static int radio_nr = -1; +/* Sequential Number of VBI Device */ +static int vbi_nr = -1; + +/* Grab parameters for the device driver */ + +/* Showing parameters under SYSFS */ module_param(isocMode, int, 0444); module_param(video_debug, int, 0444); module_param(PowerOnAtOpen, int, 0444); module_param(video_nr, int, 0444); module_param(radio_nr, int, 0444); module_param(vbi_nr, int, 0444); -#else // Old Style -MODULE_PARAM(isocMode, "i"); -MODULE_PARM(video_debug, "i"); // Grab the Debug Mode of the device driver -MODULE_PARM(adjustCompression, "i"); // Grab the compression to be adaptive -MODULE_PARM(PowerOnAtOpen, "i"); // Grab the device to power on at startup -MODULE_PARM(SwitchSVideoInput, "i"); // To help people with Black and White output with using s-video input. Some cables and input device are wired differently. -MODULE_PARM(video_nr, "i"); // video_nr option allows to specify a certain /dev/videoX device (like /dev/video0 or /dev/video1 ...) -MODULE_PARM(radio_nr, "i"); // radio_nr option allows to specify a certain /dev/radioX device (like /dev/radio0 or /dev/radio1 ...) -MODULE_PARM(vbi_nr, "i"); // vbi_nr option allows to specify a certain /dev/vbiX device (like /dev/vbi0 or /dev/vbi1 ...) -#endif MODULE_PARM_DESC(isocMode, " Set the default format for ISOC endpoint. Default: 0x60 (Compression On)"); MODULE_PARM_DESC(video_debug, " Set the default Debug Mode of the device driver. Default: 0 (Off)"); @@ -187,19 +172,21 @@ MODULE_VERSION(USBVISION_VERSION_STRING); MODULE_ALIAS(DRIVER_ALIAS); -/****************************************************************************************/ -/* SYSFS Code - Copied from the stv680.c usb module. */ -/* Device information is located at /sys/class/video4linux/video0 */ -/* Device parameters information is located at /sys/module/usbvision */ -/* Device USB Information is located at /sys/bus/usb/drivers/USBVision Video Grabber */ -/****************************************************************************************/ +/*****************************************************************************/ +/* SYSFS Code - Copied from the stv680.c usb module. */ +/* Device information is located at /sys/class/video4linux/video0 */ +/* Device parameters information is located at /sys/module/usbvision */ +/* Device USB Information is located at */ +/* /sys/bus/usb/drivers/USBVision Video Grabber */ +/*****************************************************************************/ #define YES_NO(x) ((x) ? "Yes" : "No") static inline struct usb_usbvision *cd_to_usbvision(struct class_device *cd) { - struct video_device *vdev = container_of(cd, struct video_device, class_dev); + struct video_device *vdev = + container_of(cd, struct video_device, class_dev); return video_get_drvdata(vdev); } @@ -211,15 +198,18 @@ static CLASS_DEVICE_ATTR(version, S_IRUGO, show_version, NULL); static ssize_t show_model(struct class_device *cd, char *buf) { - struct video_device *vdev = container_of(cd, struct video_device, class_dev); + struct video_device *vdev = + container_of(cd, struct video_device, class_dev); struct usb_usbvision *usbvision = video_get_drvdata(vdev); - return sprintf(buf, "%s\n", usbvision_device_data[usbvision->DevModel].ModelString); + return sprintf(buf, "%s\n", + usbvision_device_data[usbvision->DevModel].ModelString); } static CLASS_DEVICE_ATTR(model, S_IRUGO, show_model, NULL); static ssize_t show_hue(struct class_device *cd, char *buf) { - struct video_device *vdev = container_of(cd, struct video_device, class_dev); + struct video_device *vdev = + container_of(cd, struct video_device, class_dev); struct usb_usbvision *usbvision = video_get_drvdata(vdev); struct v4l2_control ctrl; ctrl.id = V4L2_CID_HUE; @@ -232,7 +222,8 @@ static CLASS_DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL); static ssize_t show_contrast(struct class_device *cd, char *buf) { - struct video_device *vdev = container_of(cd, struct video_device, class_dev); + struct video_device *vdev = + container_of(cd, struct video_device, class_dev); struct usb_usbvision *usbvision = video_get_drvdata(vdev); struct v4l2_control ctrl; ctrl.id = V4L2_CID_CONTRAST; @@ -245,7 +236,8 @@ static CLASS_DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL); static ssize_t show_brightness(struct class_device *cd, char *buf) { - struct video_device *vdev = container_of(cd, struct video_device, class_dev); + struct video_device *vdev = + container_of(cd, struct video_device, class_dev); struct usb_usbvision *usbvision = video_get_drvdata(vdev); struct v4l2_control ctrl; ctrl.id = V4L2_CID_BRIGHTNESS; @@ -258,7 +250,8 @@ static CLASS_DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL); static ssize_t show_saturation(struct class_device *cd, char *buf) { - struct video_device *vdev = container_of(cd, struct video_device, class_dev); + struct video_device *vdev = + container_of(cd, struct video_device, class_dev); struct usb_usbvision *usbvision = video_get_drvdata(vdev); struct v4l2_control ctrl; ctrl.id = V4L2_CID_SATURATION; @@ -271,23 +264,28 @@ static CLASS_DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL); static ssize_t show_streaming(struct class_device *cd, char *buf) { - struct video_device *vdev = container_of(cd, struct video_device, class_dev); + struct video_device *vdev = + container_of(cd, struct video_device, class_dev); struct usb_usbvision *usbvision = video_get_drvdata(vdev); - return sprintf(buf, "%s\n", YES_NO(usbvision->streaming==Stream_On?1:0)); + return sprintf(buf, "%s\n", + YES_NO(usbvision->streaming==Stream_On?1:0)); } static CLASS_DEVICE_ATTR(streaming, S_IRUGO, show_streaming, NULL); static ssize_t show_compression(struct class_device *cd, char *buf) { - struct video_device *vdev = container_of(cd, struct video_device, class_dev); + struct video_device *vdev = + container_of(cd, struct video_device, class_dev); struct usb_usbvision *usbvision = video_get_drvdata(vdev); - return sprintf(buf, "%s\n", YES_NO(usbvision->isocMode==ISOC_MODE_COMPRESS)); + return sprintf(buf, "%s\n", + YES_NO(usbvision->isocMode==ISOC_MODE_COMPRESS)); } static CLASS_DEVICE_ATTR(compression, S_IRUGO, show_compression, NULL); static ssize_t show_device_bridge(struct class_device *cd, char *buf) { - struct video_device *vdev = container_of(cd, struct video_device, class_dev); + struct video_device *vdev = + container_of(cd, struct video_device, class_dev); struct usb_usbvision *usbvision = video_get_drvdata(vdev); return sprintf(buf, "%d\n", usbvision->bridgeType); } @@ -376,7 +374,8 @@ static void usbvision_remove_sysfs(struct video_device *vdev) static int usbvision_v4l2_open(struct inode *inode, struct file *file) { struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); int errCode = 0; PDEBUG(DBG_IO, "open"); @@ -390,7 +389,8 @@ static int usbvision_v4l2_open(struct inode *inode, struct file *file) /* Allocate memory for the scratch ring buffer */ errCode = usbvision_scratch_alloc(usbvision); if (isocMode==ISOC_MODE_COMPRESS) { - /* Allocate intermediate decompression buffers only if needed */ + /* Allocate intermediate decompression buffers + only if needed */ errCode = usbvision_decompress_alloc(usbvision); } if (errCode) { @@ -421,11 +421,10 @@ static int usbvision_v4l2_open(struct inode *inode, struct file *file) if (!errCode) { usbvision_begin_streaming(usbvision); errCode = usbvision_init_isoc(usbvision); - /* device needs to be initialized before isoc transfer */ + /* device must be initialized before isoc transfer */ usbvision_muxsel(usbvision,0); usbvision->user++; - } - else { + } else { if (PowerOnAtOpen) { usbvision_i2c_unregister(usbvision); usbvision_power_off(usbvision); @@ -456,7 +455,8 @@ static int usbvision_v4l2_open(struct inode *inode, struct file *file) static int usbvision_v4l2_close(struct inode *inode, struct file *file) { struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); PDEBUG(DBG_IO, "close"); down(&usbvision->lock); @@ -473,7 +473,8 @@ static int usbvision_v4l2_close(struct inode *inode, struct file *file) usbvision->user--; if (PowerOnAtOpen) { - /* power off in a little while to avoid off/on every close/open short sequences */ + /* power off in a little while + to avoid off/on every close/open short sequences */ usbvision_set_powerOffTimer(usbvision); usbvision->initialized = 0; } @@ -498,583 +499,612 @@ static int usbvision_v4l2_close(struct inode *inode, struct file *file) * This is part of Video 4 Linux API. The procedure handles ioctl() calls. * */ -static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_register (struct file *file, void *priv, + struct v4l2_register *reg) { struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); - - if (!USBVISION_IS_OPERATIONAL(usbvision)) - return -EFAULT; + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); + int errCode; - switch (cmd) { + if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return -EINVAL; + /* NT100x has a 8-bit register space */ + errCode = usbvision_read_reg(usbvision, reg->reg&0xff); + if (errCode < 0) { + err("%s: VIDIOC_DBG_G_REGISTER failed: error %d", + __FUNCTION__, errCode); + return errCode; + } + return 0; +} -#ifdef CONFIG_VIDEO_ADV_DEBUG - /* ioctls to allow direct acces to the NT100x registers */ - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *reg = arg; - int errCode; - - if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - /* NT100x has a 8-bit register space */ - if (cmd == VIDIOC_DBG_G_REGISTER) - errCode = usbvision_read_reg(usbvision, reg->reg&0xff); - else - errCode = usbvision_write_reg(usbvision, reg->reg&0xff, reg->val); - if (errCode < 0) { - err("%s: VIDIOC_DBG_%c_REGISTER failed: error %d", __FUNCTION__, - cmd == VIDIOC_DBG_G_REGISTER ? 'G' : 'S', errCode); - return errCode; - } - if (cmd == VIDIOC_DBG_S_REGISTER) - reg->val = (u8)errCode; +static int vidioc_s_register (struct file *file, void *priv, + struct v4l2_register *reg) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); + int errCode; - PDEBUG(DBG_IOCTL, "VIDIOC_DBG_%c_REGISTER reg=0x%02X, value=0x%02X", - cmd == VIDIOC_DBG_G_REGISTER ? 'G' : 'S', - (unsigned int)reg->reg, (unsigned int)reg->val); - return 0; - } + if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return -EINVAL; + /* NT100x has a 8-bit register space */ + reg->val = (u8)usbvision_write_reg(usbvision, reg->reg&0xff, reg->val); + if (reg->val < 0) { + err("%s: VIDIOC_DBG_S_REGISTER failed: error %d", + __FUNCTION__, errCode); + return errCode; + } + return 0; +} #endif - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *vc=arg; - - memset(vc, 0, sizeof(*vc)); - strlcpy(vc->driver, "USBVision", sizeof(vc->driver)); - strlcpy(vc->card, usbvision_device_data[usbvision->DevModel].ModelString, - sizeof(vc->card)); - strlcpy(vc->bus_info, usbvision->dev->dev.bus_id, - sizeof(vc->bus_info)); - vc->version = USBVISION_DRIVER_VERSION; - vc->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_AUDIO | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING | - (usbvision->have_tuner ? V4L2_CAP_TUNER : 0); - PDEBUG(DBG_IOCTL, "VIDIOC_QUERYCAP"); - return 0; - } - case VIDIOC_ENUMINPUT: - { - struct v4l2_input *vi = arg; - int chan; - - if ((vi->index >= usbvision->video_inputs) || (vi->index < 0) ) - return -EINVAL; - if (usbvision->have_tuner) { - chan = vi->index; - } - else { - chan = vi->index + 1; //skip Television string - } - switch(chan) { - case 0: - if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { - strcpy(vi->name, "White Video Input"); - } - else { - strcpy(vi->name, "Television"); - vi->type = V4L2_INPUT_TYPE_TUNER; - vi->audioset = 1; - vi->tuner = chan; - vi->std = V4L2_STD_PAL | V4L2_STD_NTSC | V4L2_STD_SECAM; - } - break; - case 1: - vi->type = V4L2_INPUT_TYPE_CAMERA; - if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { - strcpy(vi->name, "Green Video Input"); - } - else { - strcpy(vi->name, "Composite Video Input"); - } - vi->std = V4L2_STD_PAL; - break; - case 2: - vi->type = V4L2_INPUT_TYPE_CAMERA; - if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { - strcpy(vi->name, "Yellow Video Input"); - } - else { - strcpy(vi->name, "S-Video Input"); - } - vi->std = V4L2_STD_PAL; - break; - case 3: - vi->type = V4L2_INPUT_TYPE_CAMERA; - strcpy(vi->name, "Red Video Input"); - vi->std = V4L2_STD_PAL; - break; - } - PDEBUG(DBG_IOCTL, "VIDIOC_ENUMINPUT name=%s:%d tuners=%d type=%d norm=%x", - vi->name, vi->index, vi->tuner,vi->type,(int)vi->std); - return 0; - } - case VIDIOC_ENUMSTD: - { - struct v4l2_standard *e = arg; - unsigned int i; - int ret; - - i = e->index; - if (i >= TVNORMS) - return -EINVAL; - ret = v4l2_video_std_construct(e, tvnorms[e->index].id, - tvnorms[e->index].name); - e->index = i; - if (ret < 0) - return ret; - return 0; + +static int vidioc_querycap (struct file *file, void *priv, + struct v4l2_capability *vc) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); + + strlcpy(vc->driver, "USBVision", sizeof(vc->driver)); + strlcpy(vc->card, + usbvision_device_data[usbvision->DevModel].ModelString, + sizeof(vc->card)); + strlcpy(vc->bus_info, usbvision->dev->dev.bus_id, + sizeof(vc->bus_info)); + vc->version = USBVISION_DRIVER_VERSION; + vc->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_AUDIO | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING | + (usbvision->have_tuner ? V4L2_CAP_TUNER : 0); + return 0; +} + +static int vidioc_enum_input (struct file *file, void *priv, + struct v4l2_input *vi) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); + int chan; + + if ((vi->index >= usbvision->video_inputs) || (vi->index < 0) ) + return -EINVAL; + if (usbvision->have_tuner) { + chan = vi->index; + } else { + chan = vi->index + 1; /*skip Television string*/ + } + /* Determine the requested input characteristics + specific for each usbvision card model */ + switch(chan) { + case 0: + if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { + strcpy(vi->name, "White Video Input"); + } else { + strcpy(vi->name, "Television"); + vi->type = V4L2_INPUT_TYPE_TUNER; + vi->audioset = 1; + vi->tuner = chan; + vi->std = USBVISION_NORMS; } - case VIDIOC_G_INPUT: - { - int *input = arg; - *input = usbvision->ctl_input; - return 0; + break; + case 1: + vi->type = V4L2_INPUT_TYPE_CAMERA; + if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { + strcpy(vi->name, "Green Video Input"); + } else { + strcpy(vi->name, "Composite Video Input"); } - case VIDIOC_S_INPUT: - { - int *input = arg; - if ((*input >= usbvision->video_inputs) || (*input < 0) ) - return -EINVAL; - usbvision->ctl_input = *input; - - down(&usbvision->lock); - usbvision_muxsel(usbvision, usbvision->ctl_input); - usbvision_set_input(usbvision); - usbvision_set_output(usbvision, usbvision->curwidth, usbvision->curheight); - up(&usbvision->lock); - return 0; + vi->std = V4L2_STD_PAL; + break; + case 2: + vi->type = V4L2_INPUT_TYPE_CAMERA; + if (usbvision_device_data[usbvision->DevModel].VideoChannels == 4) { + strcpy(vi->name, "Yellow Video Input"); + } else { + strcpy(vi->name, "S-Video Input"); } - case VIDIOC_G_STD: - { - v4l2_std_id *id = arg; + vi->std = V4L2_STD_PAL; + break; + case 3: + vi->type = V4L2_INPUT_TYPE_CAMERA; + strcpy(vi->name, "Red Video Input"); + vi->std = V4L2_STD_PAL; + break; + } + return 0; +} - *id = usbvision->tvnorm->id; +static int vidioc_g_input (struct file *file, void *priv, unsigned int *input) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); - PDEBUG(DBG_IOCTL, "VIDIOC_G_STD std_id=%s", usbvision->tvnorm->name); - return 0; - } - case VIDIOC_S_STD: - { - v4l2_std_id *id = arg; - unsigned int i; - - for (i = 0; i < TVNORMS; i++) - if (*id == tvnorms[i].id) - break; - if (i == TVNORMS) - for (i = 0; i < TVNORMS; i++) - if (*id & tvnorms[i].id) - break; - if (i == TVNORMS) - return -EINVAL; - - down(&usbvision->lock); - usbvision->tvnorm = &tvnorms[i]; - - call_i2c_clients(usbvision, VIDIOC_S_STD, - &usbvision->tvnorm->id); + *input = usbvision->ctl_input; + return 0; +} - up(&usbvision->lock); +static int vidioc_s_input (struct file *file, void *priv, unsigned int input) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); - PDEBUG(DBG_IOCTL, "VIDIOC_S_STD std_id=%s", usbvision->tvnorm->name); - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *vt = arg; - - if (!usbvision->have_tuner || vt->index) // Only tuner 0 - return -EINVAL; - strcpy(vt->name, "Television"); - /* Let clients fill in the remainder of this struct */ - call_i2c_clients(usbvision,VIDIOC_G_TUNER,vt); - - PDEBUG(DBG_IOCTL, "VIDIOC_G_TUNER signal=%x, afc=%x",vt->signal,vt->afc); - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *vt = arg; - - // Only no or one tuner for now - if (!usbvision->have_tuner || vt->index) - return -EINVAL; - /* let clients handle this */ - call_i2c_clients(usbvision,VIDIOC_S_TUNER,vt); - - PDEBUG(DBG_IOCTL, "VIDIOC_S_TUNER"); - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *freq = arg; - - freq->tuner = 0; // Only one tuner - freq->type = V4L2_TUNER_ANALOG_TV; - freq->frequency = usbvision->freq; - PDEBUG(DBG_IOCTL, "VIDIOC_G_FREQUENCY freq=0x%X", (unsigned)freq->frequency); - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *freq = arg; - - // Only no or one tuner for now - if (!usbvision->have_tuner || freq->tuner) - return -EINVAL; - - usbvision->freq = freq->frequency; - call_i2c_clients(usbvision, cmd, freq); - PDEBUG(DBG_IOCTL, "VIDIOC_S_FREQUENCY freq=0x%X", (unsigned)freq->frequency); - return 0; - } - case VIDIOC_G_AUDIO: - { - struct v4l2_audio *v = arg; - memset(v,0, sizeof(v)); - strcpy(v->name, "TV"); - PDEBUG(DBG_IOCTL, "VIDIOC_G_AUDIO"); - return 0; - } - case VIDIOC_S_AUDIO: - { - struct v4l2_audio *v = arg; - if(v->index) { - return -EINVAL; - } - PDEBUG(DBG_IOCTL, "VIDIOC_S_AUDIO"); - return 0; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *ctrl = arg; - int id=ctrl->id; + if ((input >= usbvision->video_inputs) || (input < 0) ) + return -EINVAL; - memset(ctrl,0,sizeof(*ctrl)); - ctrl->id=id; + down(&usbvision->lock); + usbvision_muxsel(usbvision, input); + usbvision_set_input(usbvision); + usbvision_set_output(usbvision, + usbvision->curwidth, + usbvision->curheight); + up(&usbvision->lock); + return 0; +} - call_i2c_clients(usbvision, cmd, arg); +static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); + usbvision->tvnormId=*id; - if (ctrl->type) - return 0; - else - return -EINVAL; + down(&usbvision->lock); + call_i2c_clients(usbvision, VIDIOC_S_STD, + &usbvision->tvnormId); + up(&usbvision->lock); + /* propagate the change to the decoder */ + usbvision_muxsel(usbvision, usbvision->ctl_input); - PDEBUG(DBG_IOCTL,"VIDIOC_QUERYCTRL id=%x value=%x",ctrl->id,ctrl->type); - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - call_i2c_clients(usbvision, VIDIOC_G_CTRL, ctrl); - PDEBUG(DBG_IOCTL,"VIDIOC_G_CTRL id=%x value=%x",ctrl->id,ctrl->value); - return 0; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; + return 0; +} - PDEBUG(DBG_IOCTL, "VIDIOC_S_CTRL id=%x value=%x",ctrl->id,ctrl->value); - call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl); - return 0; - } - case VIDIOC_REQBUFS: - { - struct v4l2_requestbuffers *vr = arg; - int ret; +static int vidioc_g_tuner (struct file *file, void *priv, + struct v4l2_tuner *vt) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); - RESTRICT_TO_RANGE(vr->count,1,USBVISION_NUMFRAMES); + if (!usbvision->have_tuner || vt->index) // Only tuner 0 + return -EINVAL; + if(usbvision->radio) { + strcpy(vt->name, "Radio"); + vt->type = V4L2_TUNER_RADIO; + } else { + strcpy(vt->name, "Television"); + } + /* Let clients fill in the remainder of this struct */ + call_i2c_clients(usbvision,VIDIOC_G_TUNER,vt); - // Check input validity : the user must do a VIDEO CAPTURE and MMAP method. - if((vr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) || - (vr->memory != V4L2_MEMORY_MMAP)) - return -EINVAL; + return 0; +} - if(usbvision->streaming == Stream_On) { - if ((ret = usbvision_stream_interrupt(usbvision))) - return ret; - } +static int vidioc_s_tuner (struct file *file, void *priv, + struct v4l2_tuner *vt) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); - usbvision_frames_free(usbvision); - usbvision_empty_framequeues(usbvision); - vr->count = usbvision_frames_alloc(usbvision,vr->count); + // Only no or one tuner for now + if (!usbvision->have_tuner || vt->index) + return -EINVAL; + /* let clients handle this */ + call_i2c_clients(usbvision,VIDIOC_S_TUNER,vt); - usbvision->curFrame = NULL; + return 0; +} - PDEBUG(DBG_IOCTL, "VIDIOC_REQBUFS count=%d",vr->count); - return 0; - } - case VIDIOC_QUERYBUF: - { - struct v4l2_buffer *vb = arg; - struct usbvision_frame *frame; +static int vidioc_g_frequency (struct file *file, void *priv, + struct v4l2_frequency *freq) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); - // FIXME : must control that buffers are mapped (VIDIOC_REQBUFS has been called) + freq->tuner = 0; // Only one tuner + if(usbvision->radio) { + freq->type = V4L2_TUNER_RADIO; + } else { + freq->type = V4L2_TUNER_ANALOG_TV; + } + freq->frequency = usbvision->freq; - if(vb->type != V4L2_CAP_VIDEO_CAPTURE) { - return -EINVAL; - } - if(vb->index>=usbvision->num_frames) { - return -EINVAL; - } - // Updating the corresponding frame state - vb->flags = 0; - frame = &usbvision->frame[vb->index]; - if(frame->grabstate >= FrameState_Ready) - vb->flags |= V4L2_BUF_FLAG_QUEUED; - if(frame->grabstate >= FrameState_Done) - vb->flags |= V4L2_BUF_FLAG_DONE; - if(frame->grabstate == FrameState_Unused) - vb->flags |= V4L2_BUF_FLAG_MAPPED; - vb->memory = V4L2_MEMORY_MMAP; - - vb->m.offset = vb->index*PAGE_ALIGN(usbvision->max_frame_size); - - vb->memory = V4L2_MEMORY_MMAP; - vb->field = V4L2_FIELD_NONE; - vb->length = usbvision->curwidth*usbvision->curheight*usbvision->palette.bytes_per_pixel; - vb->timestamp = usbvision->frame[vb->index].timestamp; - vb->sequence = usbvision->frame[vb->index].sequence; - return 0; - } - case VIDIOC_QBUF: - { - struct v4l2_buffer *vb = arg; - struct usbvision_frame *frame; - unsigned long lock_flags; - - // FIXME : works only on VIDEO_CAPTURE MODE, MMAP. - if(vb->type != V4L2_CAP_VIDEO_CAPTURE) { - return -EINVAL; - } - if(vb->index>=usbvision->num_frames) { - return -EINVAL; - } + return 0; +} - frame = &usbvision->frame[vb->index]; +static int vidioc_s_frequency (struct file *file, void *priv, + struct v4l2_frequency *freq) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); - if (frame->grabstate != FrameState_Unused) { - return -EAGAIN; - } + // Only no or one tuner for now + if (!usbvision->have_tuner || freq->tuner) + return -EINVAL; - /* Mark it as ready and enqueue frame */ - frame->grabstate = FrameState_Ready; - frame->scanstate = ScanState_Scanning; - frame->scanlength = 0; /* Accumulated in usbvision_parse_data() */ + usbvision->freq = freq->frequency; + call_i2c_clients(usbvision, VIDIOC_S_FREQUENCY, freq); - vb->flags &= ~V4L2_BUF_FLAG_DONE; + return 0; +} - /* set v4l2_format index */ - frame->v4l2_format = usbvision->palette; +static int vidioc_g_audio (struct file *file, void *priv, struct v4l2_audio *a) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); - spin_lock_irqsave(&usbvision->queue_lock, lock_flags); - list_add_tail(&usbvision->frame[vb->index].frame, &usbvision->inqueue); - spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + memset(a,0,sizeof(*a)); + if(usbvision->radio) { + strcpy(a->name,"Radio"); + } else { + strcpy(a->name, "TV"); + } - PDEBUG(DBG_IOCTL, "VIDIOC_QBUF frame #%d",vb->index); - return 0; - } - case VIDIOC_DQBUF: - { - struct v4l2_buffer *vb = arg; - int ret; - struct usbvision_frame *f; - unsigned long lock_flags; - - if (vb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (list_empty(&(usbvision->outqueue))) { - if (usbvision->streaming == Stream_Idle) - return -EINVAL; - ret = wait_event_interruptible - (usbvision->wait_frame, - !list_empty(&(usbvision->outqueue))); - if (ret) - return ret; - } + return 0; +} - spin_lock_irqsave(&usbvision->queue_lock, lock_flags); - f = list_entry(usbvision->outqueue.next, - struct usbvision_frame, frame); - list_del(usbvision->outqueue.next); - spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); - - f->grabstate = FrameState_Unused; - - vb->memory = V4L2_MEMORY_MMAP; - vb->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE; - vb->index = f->index; - vb->sequence = f->sequence; - vb->timestamp = f->timestamp; - vb->field = V4L2_FIELD_NONE; - vb->bytesused = f->scanlength; - - return 0; - } - case VIDIOC_STREAMON: - { - int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; +static int vidioc_s_audio (struct file *file, void *fh, + struct v4l2_audio *a) +{ + if(a->index) { + return -EINVAL; + } - usbvision->streaming = Stream_On; + return 0; +} - call_i2c_clients(usbvision,VIDIOC_STREAMON , &b); +static int vidioc_queryctrl (struct file *file, void *priv, + struct v4l2_queryctrl *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); + int id=ctrl->id; - PDEBUG(DBG_IOCTL, "VIDIOC_STREAMON"); + memset(ctrl,0,sizeof(*ctrl)); + ctrl->id=id; - return 0; - } - case VIDIOC_STREAMOFF: - { - int *type = arg; - int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; - - if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if(usbvision->streaming == Stream_On) { - usbvision_stream_interrupt(usbvision); - // Stop all video streamings - call_i2c_clients(usbvision,VIDIOC_STREAMOFF , &b); - } - usbvision_empty_framequeues(usbvision); + call_i2c_clients(usbvision, VIDIOC_QUERYCTRL, ctrl); - PDEBUG(DBG_IOCTL, "VIDIOC_STREAMOFF"); - return 0; - } - case VIDIOC_ENUM_FMT: - { - struct v4l2_fmtdesc *vfd = arg; + if (!ctrl->type) + return -EINVAL; - if(vfd->index>=USBVISION_SUPPORTED_PALETTES-1) { - return -EINVAL; - } - vfd->flags = 0; - vfd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - strcpy(vfd->description,usbvision_v4l2_format[vfd->index].desc); - vfd->pixelformat = usbvision_v4l2_format[vfd->index].format; - memset(vfd->reserved, 0, sizeof(vfd->reserved)); - return 0; - } - case VIDIOC_G_FMT: - { - struct v4l2_format *vf = arg; - - switch (vf->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - { - vf->fmt.pix.width = usbvision->curwidth; - vf->fmt.pix.height = usbvision->curheight; - vf->fmt.pix.pixelformat = usbvision->palette.format; - vf->fmt.pix.bytesperline = usbvision->curwidth*usbvision->palette.bytes_per_pixel; - vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline*usbvision->curheight; - vf->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - vf->fmt.pix.field = V4L2_FIELD_NONE; /* Always progressive image */ - PDEBUG(DBG_IOCTL, "VIDIOC_G_FMT w=%d, h=%d, format=%s", - vf->fmt.pix.width, vf->fmt.pix.height,usbvision->palette.desc); - return 0; - } - default: - PDEBUG(DBG_IOCTL, "VIDIOC_G_FMT invalid type %d",vf->type); - return -EINVAL; - } - return 0; - } - case VIDIOC_TRY_FMT: - case VIDIOC_S_FMT: - { - struct v4l2_format *vf = arg; - int formatIdx,ret; - - switch(vf->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - { - /* Find requested format in available ones */ - for(formatIdx=0;formatIdx<USBVISION_SUPPORTED_PALETTES;formatIdx++) { - if(vf->fmt.pix.pixelformat == usbvision_v4l2_format[formatIdx].format) { - usbvision->palette = usbvision_v4l2_format[formatIdx]; - break; - } - } - /* robustness */ - if(formatIdx == USBVISION_SUPPORTED_PALETTES) { - return -EINVAL; - } - RESTRICT_TO_RANGE(vf->fmt.pix.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH); - RESTRICT_TO_RANGE(vf->fmt.pix.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT); - - vf->fmt.pix.bytesperline = vf->fmt.pix.width*usbvision->palette.bytes_per_pixel; - vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline*vf->fmt.pix.height; - - if(cmd == VIDIOC_TRY_FMT) { - PDEBUG(DBG_IOCTL, "VIDIOC_TRY_FMT grabdisplay w=%d, h=%d, format=%s", - vf->fmt.pix.width, vf->fmt.pix.height,usbvision->palette.desc); - return 0; - } - - /* stop io in case it is already in progress */ - if(usbvision->streaming == Stream_On) { - if ((ret = usbvision_stream_interrupt(usbvision))) - return ret; - } - usbvision_frames_free(usbvision); - usbvision_empty_framequeues(usbvision); - - usbvision->curFrame = NULL; - - // by now we are committed to the new data... - down(&usbvision->lock); - usbvision_set_output(usbvision, vf->fmt.pix.width, vf->fmt.pix.height); - up(&usbvision->lock); - - PDEBUG(DBG_IOCTL, "VIDIOC_S_FMT grabdisplay w=%d, h=%d, format=%s", - vf->fmt.pix.width, vf->fmt.pix.height,usbvision->palette.desc); - return 0; - } - default: - return -EINVAL; - } - } - default: - return -ENOIOCTLCMD; + return 0; +} + +static int vidioc_g_ctrl (struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); + call_i2c_clients(usbvision, VIDIOC_G_CTRL, ctrl); + + return 0; +} + +static int vidioc_s_ctrl (struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); + call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl); + + return 0; +} + +static int vidioc_reqbufs (struct file *file, + void *priv, struct v4l2_requestbuffers *vr) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); + int ret; + + RESTRICT_TO_RANGE(vr->count,1,USBVISION_NUMFRAMES); + + /* Check input validity: + the user must do a VIDEO CAPTURE and MMAP method. */ + if((vr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) || + (vr->memory != V4L2_MEMORY_MMAP)) + return -EINVAL; + + if(usbvision->streaming == Stream_On) { + if ((ret = usbvision_stream_interrupt(usbvision))) + return ret; } + + usbvision_frames_free(usbvision); + usbvision_empty_framequeues(usbvision); + vr->count = usbvision_frames_alloc(usbvision,vr->count); + + usbvision->curFrame = NULL; + return 0; } -static int usbvision_v4l2_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_querybuf (struct file *file, + void *priv, struct v4l2_buffer *vb) { - return video_usercopy(inode, file, cmd, arg, usbvision_v4l2_do_ioctl); + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); + struct usbvision_frame *frame; + + /* FIXME : must control + that buffers are mapped (VIDIOC_REQBUFS has been called) */ + if(vb->type != V4L2_CAP_VIDEO_CAPTURE) { + return -EINVAL; + } + if(vb->index>=usbvision->num_frames) { + return -EINVAL; + } + /* Updating the corresponding frame state */ + vb->flags = 0; + frame = &usbvision->frame[vb->index]; + if(frame->grabstate >= FrameState_Ready) + vb->flags |= V4L2_BUF_FLAG_QUEUED; + if(frame->grabstate >= FrameState_Done) + vb->flags |= V4L2_BUF_FLAG_DONE; + if(frame->grabstate == FrameState_Unused) + vb->flags |= V4L2_BUF_FLAG_MAPPED; + vb->memory = V4L2_MEMORY_MMAP; + + vb->m.offset = vb->index*PAGE_ALIGN(usbvision->max_frame_size); + + vb->memory = V4L2_MEMORY_MMAP; + vb->field = V4L2_FIELD_NONE; + vb->length = usbvision->curwidth* + usbvision->curheight* + usbvision->palette.bytes_per_pixel; + vb->timestamp = usbvision->frame[vb->index].timestamp; + vb->sequence = usbvision->frame[vb->index].sequence; + return 0; +} + +static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *vb) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); + struct usbvision_frame *frame; + unsigned long lock_flags; + + /* FIXME : works only on VIDEO_CAPTURE MODE, MMAP. */ + if(vb->type != V4L2_CAP_VIDEO_CAPTURE) { + return -EINVAL; + } + if(vb->index>=usbvision->num_frames) { + return -EINVAL; + } + + frame = &usbvision->frame[vb->index]; + + if (frame->grabstate != FrameState_Unused) { + return -EAGAIN; + } + + /* Mark it as ready and enqueue frame */ + frame->grabstate = FrameState_Ready; + frame->scanstate = ScanState_Scanning; + frame->scanlength = 0; /* Accumulated in usbvision_parse_data() */ + + vb->flags &= ~V4L2_BUF_FLAG_DONE; + + /* set v4l2_format index */ + frame->v4l2_format = usbvision->palette; + + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + list_add_tail(&usbvision->frame[vb->index].frame, &usbvision->inqueue); + spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + + return 0; } +static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *vb) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); + int ret; + struct usbvision_frame *f; + unsigned long lock_flags; + + if (vb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (list_empty(&(usbvision->outqueue))) { + if (usbvision->streaming == Stream_Idle) + return -EINVAL; + ret = wait_event_interruptible + (usbvision->wait_frame, + !list_empty(&(usbvision->outqueue))); + if (ret) + return ret; + } + + spin_lock_irqsave(&usbvision->queue_lock, lock_flags); + f = list_entry(usbvision->outqueue.next, + struct usbvision_frame, frame); + list_del(usbvision->outqueue.next); + spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + + f->grabstate = FrameState_Unused; + + vb->memory = V4L2_MEMORY_MMAP; + vb->flags = V4L2_BUF_FLAG_MAPPED | + V4L2_BUF_FLAG_QUEUED | + V4L2_BUF_FLAG_DONE; + vb->index = f->index; + vb->sequence = f->sequence; + vb->timestamp = f->timestamp; + vb->field = V4L2_FIELD_NONE; + vb->bytesused = f->scanlength; + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); + int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; + + usbvision->streaming = Stream_On; + call_i2c_clients(usbvision,VIDIOC_STREAMON , &b); + + return 0; +} + +static int vidioc_streamoff(struct file *file, + void *priv, enum v4l2_buf_type type) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); + int b=V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if(usbvision->streaming == Stream_On) { + usbvision_stream_interrupt(usbvision); + /* Stop all video streamings */ + call_i2c_clients(usbvision,VIDIOC_STREAMOFF , &b); + } + usbvision_empty_framequeues(usbvision); + + return 0; +} + +static int vidioc_enum_fmt_cap (struct file *file, void *priv, + struct v4l2_fmtdesc *vfd) +{ + if(vfd->index>=USBVISION_SUPPORTED_PALETTES-1) { + return -EINVAL; + } + vfd->flags = 0; + vfd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + strcpy(vfd->description,usbvision_v4l2_format[vfd->index].desc); + vfd->pixelformat = usbvision_v4l2_format[vfd->index].format; + memset(vfd->reserved, 0, sizeof(vfd->reserved)); + return 0; +} + +static int vidioc_g_fmt_cap (struct file *file, void *priv, + struct v4l2_format *vf) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); + vf->fmt.pix.width = usbvision->curwidth; + vf->fmt.pix.height = usbvision->curheight; + vf->fmt.pix.pixelformat = usbvision->palette.format; + vf->fmt.pix.bytesperline = + usbvision->curwidth*usbvision->palette.bytes_per_pixel; + vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline*usbvision->curheight; + vf->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + vf->fmt.pix.field = V4L2_FIELD_NONE; /* Always progressive image */ + + return 0; +} + +static int vidioc_try_fmt_cap (struct file *file, void *priv, + struct v4l2_format *vf) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); + int formatIdx; + + /* Find requested format in available ones */ + for(formatIdx=0;formatIdx<USBVISION_SUPPORTED_PALETTES;formatIdx++) { + if(vf->fmt.pix.pixelformat == + usbvision_v4l2_format[formatIdx].format) { + usbvision->palette = usbvision_v4l2_format[formatIdx]; + break; + } + } + /* robustness */ + if(formatIdx == USBVISION_SUPPORTED_PALETTES) { + return -EINVAL; + } + RESTRICT_TO_RANGE(vf->fmt.pix.width, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH); + RESTRICT_TO_RANGE(vf->fmt.pix.height, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT); + + vf->fmt.pix.bytesperline = vf->fmt.pix.width* + usbvision->palette.bytes_per_pixel; + vf->fmt.pix.sizeimage = vf->fmt.pix.bytesperline*vf->fmt.pix.height; + + return 0; +} + +static int vidioc_s_fmt_cap(struct file *file, void *priv, + struct v4l2_format *vf) +{ + struct video_device *dev = video_devdata(file); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); + int ret; + + if( 0 != (ret=vidioc_try_fmt_cap (file, priv, vf)) ) { + return ret; + } + + /* stop io in case it is already in progress */ + if(usbvision->streaming == Stream_On) { + if ((ret = usbvision_stream_interrupt(usbvision))) + return ret; + } + usbvision_frames_free(usbvision); + usbvision_empty_framequeues(usbvision); + + usbvision->curFrame = NULL; + + /* by now we are committed to the new data... */ + down(&usbvision->lock); + usbvision_set_output(usbvision, vf->fmt.pix.width, vf->fmt.pix.height); + up(&usbvision->lock); + + return 0; +} static ssize_t usbvision_v4l2_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); int noblock = file->f_flags & O_NONBLOCK; unsigned long lock_flags; int ret,i; struct usbvision_frame *frame; - PDEBUG(DBG_IO, "%s: %ld bytes, noblock=%d", __FUNCTION__, (unsigned long)count, noblock); + PDEBUG(DBG_IO, "%s: %ld bytes, noblock=%d", __FUNCTION__, + (unsigned long)count, noblock); if (!USBVISION_IS_OPERATIONAL(usbvision) || (buf == NULL)) return -EFAULT; - /* This entry point is compatible with the mmap routines so that a user can do either - VIDIOC_QBUF/VIDIOC_DQBUF to get frames or call read on the device. */ + /* This entry point is compatible with the mmap routines + so that a user can do either VIDIOC_QBUF/VIDIOC_DQBUF + to get frames or call read on the device. */ if(!usbvision->num_frames) { - /* First, allocate some frames to work with if this has not been done with - VIDIOC_REQBUF */ + /* First, allocate some frames to work with + if this has not been done with VIDIOC_REQBUF */ usbvision_frames_free(usbvision); usbvision_empty_framequeues(usbvision); usbvision_frames_alloc(usbvision,USBVISION_NUMFRAMES); @@ -1086,21 +1116,24 @@ static ssize_t usbvision_v4l2_read(struct file *file, char __user *buf, call_i2c_clients(usbvision,VIDIOC_STREAMON , NULL); } - /* Then, enqueue as many frames as possible (like a user of VIDIOC_QBUF would do) */ + /* Then, enqueue as many frames as possible + (like a user of VIDIOC_QBUF would do) */ for(i=0;i<usbvision->num_frames;i++) { frame = &usbvision->frame[i]; if(frame->grabstate == FrameState_Unused) { /* Mark it as ready and enqueue frame */ frame->grabstate = FrameState_Ready; frame->scanstate = ScanState_Scanning; - frame->scanlength = 0; /* Accumulated in usbvision_parse_data() */ + /* Accumulated in usbvision_parse_data() */ + frame->scanlength = 0; /* set v4l2_format index */ frame->v4l2_format = usbvision->palette; spin_lock_irqsave(&usbvision->queue_lock, lock_flags); list_add_tail(&frame->frame, &usbvision->inqueue); - spin_unlock_irqrestore(&usbvision->queue_lock, lock_flags); + spin_unlock_irqrestore(&usbvision->queue_lock, + lock_flags); } } @@ -1128,8 +1161,9 @@ static ssize_t usbvision_v4l2_read(struct file *file, char __user *buf, return 0; } - PDEBUG(DBG_IO, "%s: frmx=%d, bytes_read=%ld, scanlength=%ld", __FUNCTION__, - frame->index, frame->bytes_read, frame->scanlength); + PDEBUG(DBG_IO, "%s: frmx=%d, bytes_read=%ld, scanlength=%ld", + __FUNCTION__, + frame->index, frame->bytes_read, frame->scanlength); /* copy bytes to user space; we allow for partials reads */ if ((count + frame->bytes_read) > (unsigned long)frame->scanlength) @@ -1140,10 +1174,11 @@ static ssize_t usbvision_v4l2_read(struct file *file, char __user *buf, } frame->bytes_read += count; - PDEBUG(DBG_IO, "%s: {copy} count used=%ld, new bytes_read=%ld", __FUNCTION__, - (unsigned long)count, frame->bytes_read); + PDEBUG(DBG_IO, "%s: {copy} count used=%ld, new bytes_read=%ld", + __FUNCTION__, + (unsigned long)count, frame->bytes_read); - // For now, forget the frame if it has not been read in one shot. + /* For now, forget the frame if it has not been read in one shot. */ /* if (frame->bytes_read >= frame->scanlength) {// All data has been read */ frame->bytes_read = 0; @@ -1162,7 +1197,8 @@ static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma) u32 i; struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); PDEBUG(DBG_MMAP, "mmap"); @@ -1180,11 +1216,13 @@ static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma) } for (i = 0; i < usbvision->num_frames; i++) { - if (((PAGE_ALIGN(usbvision->max_frame_size)*i) >> PAGE_SHIFT) == vma->vm_pgoff) + if (((PAGE_ALIGN(usbvision->max_frame_size)*i) >> PAGE_SHIFT) == + vma->vm_pgoff) break; } if (i == usbvision->num_frames) { - PDEBUG(DBG_MMAP, "mmap: user supplied mapping address is out of range"); + PDEBUG(DBG_MMAP, + "mmap: user supplied mapping address is out of range"); up(&usbvision->lock); return -EINVAL; } @@ -1218,8 +1256,8 @@ static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma) static int usbvision_radio_open(struct inode *inode, struct file *file) { struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); - struct v4l2_frequency freq; + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); int errCode = 0; PDEBUG(DBG_IO, "%s:", __FUNCTION__); @@ -1249,8 +1287,6 @@ static int usbvision_radio_open(struct inode *inode, struct file *file) // If so far no errors then we shall start the radio usbvision->radio = 1; call_i2c_clients(usbvision,AUDC_SET_RADIO,&usbvision->tuner_type); - freq.frequency = 1517; //SWR3 @ 94.8MHz - call_i2c_clients(usbvision, VIDIOC_S_FREQUENCY, &freq); usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO); usbvision->user++; } @@ -1270,7 +1306,8 @@ static int usbvision_radio_open(struct inode *inode, struct file *file) static int usbvision_radio_close(struct inode *inode, struct file *file) { struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + struct usb_usbvision *usbvision = + (struct usb_usbvision *) video_get_drvdata(dev); int errCode = 0; PDEBUG(DBG_IO, ""); @@ -1304,149 +1341,6 @@ static int usbvision_radio_close(struct inode *inode, struct file *file) return errCode; } -static int usbvision_do_radio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) -{ - struct video_device *dev = video_devdata(file); - struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); - - if (!USBVISION_IS_OPERATIONAL(usbvision)) - return -EIO; - - switch (cmd) { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *vc=arg; - - memset(vc, 0, sizeof(*vc)); - strlcpy(vc->driver, "USBVision", sizeof(vc->driver)); - strlcpy(vc->card, usbvision_device_data[usbvision->DevModel].ModelString, - sizeof(vc->card)); - strlcpy(vc->bus_info, usbvision->dev->dev.bus_id, - sizeof(vc->bus_info)); - vc->version = USBVISION_DRIVER_VERSION; - vc->capabilities = (usbvision->have_tuner ? V4L2_CAP_TUNER : 0); - PDEBUG(DBG_IO, "VIDIOC_QUERYCAP"); - return 0; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *ctrl = arg; - int id=ctrl->id; - - memset(ctrl,0,sizeof(*ctrl)); - ctrl->id=id; - - call_i2c_clients(usbvision, cmd, arg); - PDEBUG(DBG_IO,"VIDIOC_QUERYCTRL id=%x value=%x",ctrl->id,ctrl->type); - - if (ctrl->type) - return 0; - else - return -EINVAL; - - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - - call_i2c_clients(usbvision, VIDIOC_G_CTRL, ctrl); - PDEBUG(DBG_IO,"VIDIOC_G_CTRL id=%x value=%x",ctrl->id,ctrl->value); - return 0; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - - call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl); - PDEBUG(DBG_IO, "VIDIOC_S_CTRL id=%x value=%x",ctrl->id,ctrl->value); - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *t = arg; - - if (t->index > 0) - return -EINVAL; - - memset(t,0,sizeof(*t)); - strcpy(t->name, "Radio"); - t->type = V4L2_TUNER_RADIO; - - /* Let clients fill in the remainder of this struct */ - call_i2c_clients(usbvision,VIDIOC_G_TUNER,t); - PDEBUG(DBG_IO, "VIDIOC_G_TUNER signal=%x, afc=%x",t->signal,t->afc); - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *vt = arg; - - // Only no or one tuner for now - if (!usbvision->have_tuner || vt->index) - return -EINVAL; - /* let clients handle this */ - call_i2c_clients(usbvision,VIDIOC_S_TUNER,vt); - - PDEBUG(DBG_IO, "VIDIOC_S_TUNER"); - return 0; - } - case VIDIOC_G_AUDIO: - { - struct v4l2_audio *a = arg; - - memset(a,0,sizeof(*a)); - strcpy(a->name,"Radio"); - PDEBUG(DBG_IO, "VIDIOC_G_AUDIO"); - return 0; - } - case VIDIOC_S_AUDIO: - case VIDIOC_S_INPUT: - case VIDIOC_S_STD: - return 0; - - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; - - memset(f,0,sizeof(*f)); - - f->type = V4L2_TUNER_RADIO; - f->frequency = usbvision->freq; - call_i2c_clients(usbvision, cmd, f); - PDEBUG(DBG_IO, "VIDIOC_G_FREQUENCY freq=0x%X", (unsigned)f->frequency); - - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; - - if (f->tuner != 0) - return -EINVAL; - usbvision->freq = f->frequency; - call_i2c_clients(usbvision, cmd, f); - PDEBUG(DBG_IO, "VIDIOC_S_FREQUENCY freq=0x%X", (unsigned)f->frequency); - - return 0; - } - default: - { - PDEBUG(DBG_IO, "%s: Unknown command %x", __FUNCTION__, cmd); - return -ENOIOCTLCMD; - } - } - return 0; -} - - -static int usbvision_radio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(inode, file, cmd, arg, usbvision_do_radio_ioctl); -} - - /* * Here comes the stuff for vbi on usbvision based devices * @@ -1454,21 +1348,21 @@ static int usbvision_radio_ioctl(struct inode *inode, struct file *file, static int usbvision_vbi_open(struct inode *inode, struct file *file) { /* TODO */ - return -EINVAL; + return -ENODEV; } static int usbvision_vbi_close(struct inode *inode, struct file *file) { /* TODO */ - return -EINVAL; + return -ENODEV; } static int usbvision_do_vbi_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { /* TODO */ - return -EINVAL; + return -ENOIOCTLCMD; } static int usbvision_vbi_ioctl(struct inode *inode, struct file *file, @@ -1489,8 +1383,11 @@ static const struct file_operations usbvision_fops = { .release = usbvision_v4l2_close, .read = usbvision_v4l2_read, .mmap = usbvision_v4l2_mmap, - .ioctl = usbvision_v4l2_ioctl, + .ioctl = video_ioctl2, .llseek = no_llseek, +/* .poll = video_poll, */ + .mmap = usbvision_v4l2_mmap, + .compat_ioctl = v4l_compat_ioctl32, }; static struct video_device usbvision_video_template = { .owner = THIS_MODULE, @@ -1500,6 +1397,39 @@ static struct video_device usbvision_video_template = { .name = "usbvision-video", .release = video_device_release, .minor = -1, + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap, + .vidioc_g_fmt_cap = vidioc_g_fmt_cap, + .vidioc_try_fmt_cap = vidioc_try_fmt_cap, + .vidioc_s_fmt_cap = vidioc_s_fmt_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_g_audio = vidioc_s_audio, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, +#ifdef CONFIG_VIDEO_V4L1_COMPAT +/* .vidiocgmbuf = vidiocgmbuf, */ +#endif + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif + .tvnorms = USBVISION_NORMS, + .current_norm = V4L2_STD_PAL }; @@ -1508,8 +1438,9 @@ static const struct file_operations usbvision_radio_fops = { .owner = THIS_MODULE, .open = usbvision_radio_open, .release = usbvision_radio_close, - .ioctl = usbvision_radio_ioctl, + .ioctl = video_ioctl2, .llseek = no_llseek, + .compat_ioctl = v4l_compat_ioctl32, }; static struct video_device usbvision_radio_template= @@ -1518,12 +1449,27 @@ static struct video_device usbvision_radio_template= .type = VID_TYPE_TUNER, .hardware = VID_HARDWARE_USBVISION, .fops = &usbvision_radio_fops, - .release = video_device_release, .name = "usbvision-radio", + .release = video_device_release, .minor = -1, + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_g_audio = vidioc_s_audio, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + + .tvnorms = USBVISION_NORMS, + .current_norm = V4L2_STD_PAL }; - // vbi template static const struct file_operations usbvision_vbi_fops = { .owner = THIS_MODULE, @@ -1531,6 +1477,7 @@ static const struct file_operations usbvision_vbi_fops = { .release = usbvision_vbi_close, .ioctl = usbvision_vbi_ioctl, .llseek = no_llseek, + .compat_ioctl = v4l_compat_ioctl32, }; static struct video_device usbvision_vbi_template= @@ -1574,11 +1521,11 @@ static void usbvision_unregister_video(struct usb_usbvision *usbvision) { // vbi Device: if (usbvision->vbi) { - PDEBUG(DBG_PROBE, "unregister /dev/vbi%d [v4l2]", usbvision->vbi->minor & 0x1f); + PDEBUG(DBG_PROBE, "unregister /dev/vbi%d [v4l2]", + usbvision->vbi->minor & 0x1f); if (usbvision->vbi->minor != -1) { video_unregister_device(usbvision->vbi); - } - else { + } else { video_device_release(usbvision->vbi); } usbvision->vbi = NULL; @@ -1586,11 +1533,11 @@ static void usbvision_unregister_video(struct usb_usbvision *usbvision) // Radio Device: if (usbvision->rdev) { - PDEBUG(DBG_PROBE, "unregister /dev/radio%d [v4l2]", usbvision->rdev->minor & 0x1f); + PDEBUG(DBG_PROBE, "unregister /dev/radio%d [v4l2]", + usbvision->rdev->minor & 0x1f); if (usbvision->rdev->minor != -1) { video_unregister_device(usbvision->rdev); - } - else { + } else { video_device_release(usbvision->rdev); } usbvision->rdev = NULL; @@ -1598,11 +1545,11 @@ static void usbvision_unregister_video(struct usb_usbvision *usbvision) // Video Device: if (usbvision->vdev) { - PDEBUG(DBG_PROBE, "unregister /dev/video%d [v4l2]", usbvision->vdev->minor & 0x1f); + PDEBUG(DBG_PROBE, "unregister /dev/video%d [v4l2]", + usbvision->vdev->minor & 0x1f); if (usbvision->vdev->minor != -1) { video_unregister_device(usbvision->vdev); - } - else { + } else { video_device_release(usbvision->vdev); } usbvision->vdev = NULL; @@ -1613,37 +1560,52 @@ static void usbvision_unregister_video(struct usb_usbvision *usbvision) static int __devinit usbvision_register_video(struct usb_usbvision *usbvision) { // Video Device: - usbvision->vdev = usbvision_vdev_init(usbvision, &usbvision_video_template, "USBVision Video"); + usbvision->vdev = usbvision_vdev_init(usbvision, + &usbvision_video_template, + "USBVision Video"); if (usbvision->vdev == NULL) { goto err_exit; } - if (video_register_device(usbvision->vdev, VFL_TYPE_GRABBER, video_nr)<0) { + if (video_register_device(usbvision->vdev, + VFL_TYPE_GRABBER, + video_nr)<0) { goto err_exit; } - printk(KERN_INFO "USBVision[%d]: registered USBVision Video device /dev/video%d [v4l2]\n", usbvision->nr,usbvision->vdev->minor & 0x1f); + printk(KERN_INFO "USBVision[%d]: registered USBVision Video device /dev/video%d [v4l2]\n", + usbvision->nr,usbvision->vdev->minor & 0x1f); // Radio Device: if (usbvision_device_data[usbvision->DevModel].Radio) { // usbvision has radio - usbvision->rdev = usbvision_vdev_init(usbvision, &usbvision_radio_template, "USBVision Radio"); + usbvision->rdev = usbvision_vdev_init(usbvision, + &usbvision_radio_template, + "USBVision Radio"); if (usbvision->rdev == NULL) { goto err_exit; } - if (video_register_device(usbvision->rdev, VFL_TYPE_RADIO, radio_nr)<0) { + if (video_register_device(usbvision->rdev, + VFL_TYPE_RADIO, + radio_nr)<0) { goto err_exit; } - printk(KERN_INFO "USBVision[%d]: registered USBVision Radio device /dev/radio%d [v4l2]\n", usbvision->nr, usbvision->rdev->minor & 0x1f); + printk(KERN_INFO "USBVision[%d]: registered USBVision Radio device /dev/radio%d [v4l2]\n", + usbvision->nr, usbvision->rdev->minor & 0x1f); } // vbi Device: if (usbvision_device_data[usbvision->DevModel].vbi) { - usbvision->vbi = usbvision_vdev_init(usbvision, &usbvision_vbi_template, "USBVision VBI"); + usbvision->vbi = usbvision_vdev_init(usbvision, + &usbvision_vbi_template, + "USBVision VBI"); if (usbvision->vdev == NULL) { goto err_exit; } - if (video_register_device(usbvision->vbi, VFL_TYPE_VBI, vbi_nr)<0) { + if (video_register_device(usbvision->vbi, + VFL_TYPE_VBI, + vbi_nr)<0) { goto err_exit; } - printk(KERN_INFO "USBVision[%d]: registered USBVision VBI device /dev/vbi%d [v4l2] (Not Working Yet!)\n", usbvision->nr,usbvision->vbi->minor & 0x1f); + printk(KERN_INFO "USBVision[%d]: registered USBVision VBI device /dev/vbi%d [v4l2] (Not Working Yet!)\n", + usbvision->nr,usbvision->vbi->minor & 0x1f); } // all done return 0; @@ -1657,7 +1619,8 @@ static int __devinit usbvision_register_video(struct usb_usbvision *usbvision) /* * usbvision_alloc() * - * This code allocates the struct usb_usbvision. It is filled with default values. + * This code allocates the struct usb_usbvision. + * It is filled with default values. * * Returns NULL on error, a pointer to usb_usbvision else. * @@ -1666,7 +1629,8 @@ static struct usb_usbvision *usbvision_alloc(struct usb_device *dev) { struct usb_usbvision *usbvision; - if ((usbvision = kzalloc(sizeof(struct usb_usbvision), GFP_KERNEL)) == NULL) { + if ((usbvision = kzalloc(sizeof(struct usb_usbvision), GFP_KERNEL)) == + NULL) { goto err_exit; } @@ -1728,11 +1692,11 @@ static void usbvision_release(struct usb_usbvision *usbvision) } -/******************************** usb interface *****************************************/ +/*********************** usb interface **********************************/ static void usbvision_configure_video(struct usb_usbvision *usbvision) { - int model,i; + int model; if (usbvision == NULL) return; @@ -1741,25 +1705,23 @@ static void usbvision_configure_video(struct usb_usbvision *usbvision) usbvision->palette = usbvision_v4l2_format[2]; // V4L2_PIX_FMT_RGB24; if (usbvision_device_data[usbvision->DevModel].Vin_Reg2_override) { - usbvision->Vin_Reg2_Preset = usbvision_device_data[usbvision->DevModel].Vin_Reg2; + usbvision->Vin_Reg2_Preset = + usbvision_device_data[usbvision->DevModel].Vin_Reg2; } else { usbvision->Vin_Reg2_Preset = 0; } - for (i = 0; i < TVNORMS; i++) - if (usbvision_device_data[model].VideoNorm == tvnorms[i].mode) - break; - if (i == TVNORMS) - i = 0; - usbvision->tvnorm = &tvnorms[i]; /* set default norm */ + usbvision->tvnormId = usbvision_device_data[model].VideoNorm; usbvision->video_inputs = usbvision_device_data[model].VideoChannels; usbvision->ctl_input = 0; /* This should be here to make i2c clients to be able to register */ - usbvision_audio_off(usbvision); //first switch off audio + /* first switch off audio */ + usbvision_audio_off(usbvision); if (!PowerOnAtOpen) { - usbvision_power_on(usbvision); //and then power up the noisy tuner + /* and then power up the noisy tuner */ + usbvision_power_on(usbvision); usbvision_i2c_register(usbvision); } } @@ -1796,18 +1758,22 @@ static int __devinit usbvision_probe(struct usb_interface *intf, if (usbvision_device_data[model].Interface >= 0) { interface = &dev->actconfig->interface[usbvision_device_data[model].Interface]->altsetting[0]; - } - else { + } else { interface = &dev->actconfig->interface[ifnum]->altsetting[0]; } endpoint = &interface->endpoint[1].desc; - if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC) { - err("%s: interface %d. has non-ISO endpoint!", __FUNCTION__, ifnum); - err("%s: Endpoint attributes %d", __FUNCTION__, endpoint->bmAttributes); + if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != + USB_ENDPOINT_XFER_ISOC) { + err("%s: interface %d. has non-ISO endpoint!", + __FUNCTION__, ifnum); + err("%s: Endpoint attributes %d", + __FUNCTION__, endpoint->bmAttributes); return -ENODEV; } - if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { - err("%s: interface %d. has ISO OUT endpoint!", __FUNCTION__, ifnum); + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == + USB_DIR_OUT) { + err("%s: interface %d. has ISO OUT endpoint!", + __FUNCTION__, ifnum); return -ENODEV; } @@ -1818,11 +1784,9 @@ static int __devinit usbvision_probe(struct usb_interface *intf, if (dev->descriptor.bNumConfigurations > 1) { usbvision->bridgeType = BRIDGE_NT1004; - } - else if (model == DAZZLE_DVC_90_REV_1_SECAM) { + } else if (model == DAZZLE_DVC_90_REV_1_SECAM) { usbvision->bridgeType = BRIDGE_NT1005; - } - else { + } else { usbvision->bridgeType = BRIDGE_NT1003; } PDEBUG(DBG_PROBE, "bridgeType %d", usbvision->bridgeType); @@ -1919,11 +1883,11 @@ static void __devexit usbvision_disconnect(struct usb_interface *intf) up(&usbvision->lock); if (usbvision->user) { - printk(KERN_INFO "%s: In use, disconnect pending\n", __FUNCTION__); + printk(KERN_INFO "%s: In use, disconnect pending\n", + __FUNCTION__); wake_up_interruptible(&usbvision->wait_frame); wake_up_interruptible(&usbvision->wait_stream); - } - else { + } else { usbvision_release(usbvision); } @@ -1950,7 +1914,6 @@ static int __init usbvision_init(void) PDEBUG(DBG_PROBE, ""); - PDEBUG(DBG_IOCTL, "IOCTL debugging is enabled [video]"); PDEBUG(DBG_IO, "IO debugging is enabled [video]"); PDEBUG(DBG_PROBE, "PROBE debugging is enabled [video]"); PDEBUG(DBG_MMAP, "MMAP debugging is enabled [video]"); diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h index c759d00..c5b6c50 100644 --- a/drivers/media/video/usbvision/usbvision.h +++ b/drivers/media/video/usbvision/usbvision.h @@ -221,6 +221,8 @@ enum { #define I2C_USB_ADAP_MAX 16 +#define USBVISION_NORMS (V4L2_STD_PAL | V4L2_STD_NTSC | V4L2_STD_SECAM | V4L2_STD_PAL_M) + /* ----------------------------------------------------------------- */ /* usbvision video structures */ /* ----------------------------------------------------------------- */ @@ -301,14 +303,6 @@ struct usbvision_frame_header { __u16 frameHeight; /* 10 - 11 after endian correction*/ }; -/* tvnorms */ -struct usbvision_tvnorm { - char *name; - v4l2_std_id id; - /* mode for saa7113h */ - int mode; -}; - struct usbvision_frame { char *data; /* Frame buffer */ struct usbvision_frame_header isocHeader; /* Header from stream */ @@ -386,7 +380,6 @@ struct usb_usbvision { int tuner_type; int tuner_addr; int bridgeType; // NT1003, NT1004, NT1005 - int channel; int radio; int video_inputs; // # of inputs unsigned long freq; @@ -441,7 +434,7 @@ struct usb_usbvision { struct v4l2_capability vcap; /* Video capabilities */ unsigned int ctl_input; /* selected input */ - struct usbvision_tvnorm *tvnorm; /* selected tv norm */ + v4l2_std_id tvnormId; /* selected tv norm */ unsigned char video_endp; /* 0x82 for USBVISION devices based */ // Decompression stuff: diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c index 0c658b7..e94a9a6 100644 --- a/drivers/media/video/vino.c +++ b/drivers/media/video/vino.c @@ -2077,12 +2077,10 @@ static int vino_wait_for_frame(struct vino_channel_settings *vcs) init_waitqueue_entry(&wait, current); /* add ourselves into wait queue */ add_wait_queue(&vcs->fb_queue.frame_wait_queue, &wait); - /* and set current state */ - set_current_state(TASK_INTERRUPTIBLE); /* to ensure that schedule_timeout will return immediately - * if VINO interrupt was triggred meanwhile */ - schedule_timeout(HZ / 10); + * if VINO interrupt was triggered meanwhile */ + schedule_timeout_interruptible(HZ / 10); if (signal_pending(current)) err = -EINTR; diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 3ef4d01..f6d3a94 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -25,6 +25,7 @@ #include <linux/pci.h> #include <linux/random.h> #include <linux/version.h> +#include <linux/mutex.h> #include <linux/videodev2.h> #include <linux/dma-mapping.h> #ifdef CONFIG_VIDEO_V4L1_COMPAT @@ -145,9 +146,6 @@ struct vivi_buffer { struct vivi_fmt *fmt; -#ifdef CONFIG_VIVI_SCATTER - struct sg_to_addr *to_addr; -#endif }; struct vivi_dmaqueue { @@ -168,7 +166,7 @@ static LIST_HEAD(vivi_devlist); struct vivi_dev { struct list_head vivi_devlist; - struct semaphore lock; + struct mutex lock; int users; @@ -232,68 +230,13 @@ static u8 bars[8][3] = { #define TSTAMP_MAX_Y TSTAMP_MIN_Y+15 #define TSTAMP_MIN_X 64 -#ifdef CONFIG_VIVI_SCATTER -static void prep_to_addr(struct sg_to_addr to_addr[], - struct videobuf_buffer *vb) -{ - int i, pos=0; - - for (i=0;i<vb->dma.nr_pages;i++) { - to_addr[i].sg=&vb->dma.sglist[i]; - to_addr[i].pos=pos; - pos += vb->dma.sglist[i].length; - } -} - -static int get_addr_pos(int pos, int pages, struct sg_to_addr to_addr[]) -{ - int p1=0,p2=pages-1,p3=pages/2; - - /* Sanity test */ - BUG_ON (pos>=to_addr[p2].pos+to_addr[p2].sg->length); - - while (p1+1<p2) { - if (pos < to_addr[p3].pos) { - p2=p3; - } else { - p1=p3; - } - p3=(p1+p2)/2; - } - if (pos >= to_addr[p2].pos) - p1=p2; - - return (p1); -} -#endif -#ifdef CONFIG_VIVI_SCATTER -static void gen_line(struct sg_to_addr to_addr[],int inipos,int pages,int wmax, - int hmax, int line, char *timestr) -#else static void gen_line(char *basep,int inipos,int wmax, int hmax, int line, char *timestr) -#endif { int w,i,j,pos=inipos,y; char *p,*s; u8 chr,r,g,b,color; -#ifdef CONFIG_VIVI_SCATTER - int pgpos,oldpg; - char *basep; - struct page *pg; - - unsigned long flags; - spinlock_t spinlock; - - spin_lock_init(&spinlock); - - /* Get first addr pointed to pixel position */ - oldpg=get_addr_pos(pos,pages,to_addr); - pg=pfn_to_page(sg_dma_address(to_addr[oldpg].sg) >> PAGE_SHIFT); - spin_lock_irqsave(&spinlock,flags); - basep = kmap_atomic(pg, KM_BOUNCE_READ)+to_addr[oldpg].sg->offset; -#endif /* We will just duplicate the second pixel at the packet */ wmax/=2; @@ -305,18 +248,7 @@ static void gen_line(char *basep,int inipos,int wmax, b=bars[w*7/wmax][2]; for (color=0;color<4;color++) { -#ifdef CONFIG_VIVI_SCATTER - pgpos=get_addr_pos(pos,pages,to_addr); - if (pgpos!=oldpg) { - pg=pfn_to_page(sg_dma_address(to_addr[pgpos].sg) >> PAGE_SHIFT); - kunmap_atomic(basep, KM_BOUNCE_READ); - basep= kmap_atomic(pg, KM_BOUNCE_READ)+to_addr[pgpos].sg->offset; - oldpg=pgpos; - } - p=basep+pos-to_addr[pgpos].pos; -#else p=basep+pos; -#endif switch (color) { case 0: @@ -361,23 +293,7 @@ static void gen_line(char *basep,int inipos,int wmax, pos=inipos+j*2; for (color=0;color<4;color++) { -#ifdef CONFIG_VIVI_SCATTER - pgpos=get_addr_pos(pos,pages,to_addr); - if (pgpos!=oldpg) { - pg=pfn_to_page(sg_dma_address( - to_addr[pgpos].sg) - >> PAGE_SHIFT); - kunmap_atomic(basep, - KM_BOUNCE_READ); - basep= kmap_atomic(pg, - KM_BOUNCE_READ)+ - to_addr[pgpos].sg->offset; - oldpg=pgpos; - } - p=basep+pos-to_addr[pgpos].pos; -#else p=basep+pos; -#endif y=TO_Y(r,g,b); @@ -402,12 +318,7 @@ static void gen_line(char *basep,int inipos,int wmax, end: -#ifdef CONFIG_VIVI_SCATTER - kunmap_atomic(basep, KM_BOUNCE_READ); - spin_unlock_irqrestore(&spinlock,flags); -#else return; -#endif } static void vivi_fillbuff(struct vivi_dev *dev,struct vivi_buffer *buf) { @@ -415,35 +326,16 @@ static void vivi_fillbuff(struct vivi_dev *dev,struct vivi_buffer *buf) int hmax = buf->vb.height; int wmax = buf->vb.width; struct timeval ts; -#ifdef CONFIG_VIVI_SCATTER - struct sg_to_addr *to_addr=buf->to_addr; - struct videobuf_buffer *vb=&buf->vb; -#else char *tmpbuf; -#endif - -#ifdef CONFIG_VIVI_SCATTER - /* Test if DMA mapping is ready */ - if (!sg_dma_address(&vb->dma.sglist[0])) - return; - - prep_to_addr(to_addr,vb); - /* Check if there is enough memory */ - BUG_ON(buf->vb.dma.nr_pages << PAGE_SHIFT < (buf->vb.width*buf->vb.height)*2); -#else if (buf->vb.dma.varea) { tmpbuf=kmalloc (wmax*2, GFP_KERNEL); } else { tmpbuf=buf->vb.dma.vmalloc; } -#endif for (h=0;h<hmax;h++) { -#ifdef CONFIG_VIVI_SCATTER - gen_line(to_addr,pos,vb->dma.nr_pages,wmax,hmax,h,dev->timestr); -#else if (buf->vb.dma.varea) { gen_line(tmpbuf,0,wmax,hmax,h,dev->timestr); /* FIXME: replacing to __copy_to_user */ @@ -452,7 +344,6 @@ static void vivi_fillbuff(struct vivi_dev *dev,struct vivi_buffer *buf) } else { gen_line(tmpbuf,pos,wmax,hmax,h,dev->timestr); } -#endif pos += wmax*2; } @@ -718,11 +609,6 @@ static void free_buffer(struct videobuf_queue *vq, struct vivi_buffer *buf) if (in_interrupt()) BUG(); -#ifdef CONFIG_VIVI_SCATTER - /*FIXME: Maybe a spinlock is required here */ - kfree(buf->to_addr); - buf->to_addr=NULL; -#endif videobuf_waiton(&buf->vb,0,0); videobuf_dma_unmap(vq, &buf->vb.dma); @@ -768,12 +654,6 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, buf->vb.state = STATE_PREPARED; -#ifdef CONFIG_VIVI_SCATTER - if (NULL == (buf->to_addr = kmalloc(sizeof(*buf->to_addr) * vb->dma.nr_pages,GFP_KERNEL))) { - rc=-ENOMEM; - goto fail; - } -#endif return 0; fail: @@ -838,40 +718,6 @@ static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb free_buffer(vq,buf); } -#ifdef CONFIG_VIVI_SCATTER -static int vivi_map_sg(void *dev, struct scatterlist *sg, int nents, - int direction) -{ - int i; - - dprintk(1,"%s, number of pages=%d\n",__FUNCTION__,nents); - BUG_ON(direction == DMA_NONE); - - for (i = 0; i < nents; i++ ) { - BUG_ON(!sg[i].page); - - sg_dma_address(&sg[i]) = page_to_phys(sg[i].page) + sg[i].offset; - } - - return nents; -} - -static int vivi_unmap_sg(void *dev,struct scatterlist *sglist,int nr_pages, - int direction) -{ - dprintk(1,"%s\n",__FUNCTION__); - return 0; -} - -static int vivi_dma_sync_sg(void *dev,struct scatterlist *sglist, int nr_pages, - int direction) -{ -// dprintk(1,"%s\n",__FUNCTION__); - -// flush_write_buffers(); - return 0; -} -#endif static struct videobuf_queue_ops vivi_video_qops = { .buf_setup = buffer_setup, @@ -893,16 +739,16 @@ static struct videobuf_queue_ops vivi_video_qops = { static int res_get(struct vivi_dev *dev, struct vivi_fh *fh) { /* is it free? */ - down(&dev->lock); + mutex_lock(&dev->lock); if (dev->resources) { /* no, someone else uses it */ - up(&dev->lock); + mutex_unlock(&dev->lock); return 0; } /* it's free, grab it */ dev->resources =1; dprintk(1,"res: get\n"); - up(&dev->lock); + mutex_unlock(&dev->lock); return 1; } @@ -913,10 +759,10 @@ static int res_locked(struct vivi_dev *dev) static void res_free(struct vivi_dev *dev, struct vivi_fh *fh) { - down(&dev->lock); + mutex_lock(&dev->lock); dev->resources = 0; dprintk(1,"res: put\n"); - up(&dev->lock); + mutex_lock(&dev->lock); } /* ------------------------------------------------------------------ @@ -1260,19 +1106,11 @@ static int vivi_open(struct inode *inode, struct file *file) sprintf(dev->timestr,"%02d:%02d:%02d:%03d", dev->h,dev->m,dev->s,(dev->us+500)/1000); -#ifdef CONFIG_VIVI_SCATTER - videobuf_queue_init(&fh->vb_vidq,VIDEOBUF_DMA_SCATTER, &vivi_video_qops, - NULL, NULL, - fh->type, - V4L2_FIELD_INTERLACED, - sizeof(struct vivi_buffer),fh); -#else videobuf_queue_init(&fh->vb_vidq, &vivi_video_qops, NULL, NULL, fh->type, V4L2_FIELD_INTERLACED, sizeof(struct vivi_buffer),fh); -#endif return 0; } @@ -1423,7 +1261,7 @@ static int __init vivi_init(void) init_waitqueue_head(&dev->vidq.wq); /* initialize locks */ - init_MUTEX(&dev->lock); + mutex_init(&dev->lock); dev->vidq.timeout.function = vivi_vid_timeout; dev->vidq.timeout.data = (unsigned long)dev; diff --git a/drivers/media/video/zc0301/Kconfig b/drivers/media/video/zc0301/Kconfig index 47cd93f..edb0029 100644 --- a/drivers/media/video/zc0301/Kconfig +++ b/drivers/media/video/zc0301/Kconfig @@ -1,6 +1,6 @@ config USB_ZC0301 tristate "USB ZC0301[P] Image Processor and Control Chip support" - depends on VIDEO_V4L1 + depends on VIDEO_V4L2 ---help--- Say Y here if you want support for cameras based on the ZC0301 or ZC0301P Image Processors and Control Chips. diff --git a/drivers/media/video/zc0301/zc0301.h b/drivers/media/video/zc0301/zc0301.h index 710f12e..a2de50e 100644 --- a/drivers/media/video/zc0301/zc0301.h +++ b/drivers/media/video/zc0301/zc0301.h @@ -36,6 +36,7 @@ #include <linux/rwsem.h> #include <linux/stddef.h> #include <linux/string.h> +#include <linux/kref.h> #include "zc0301_sensor.h" @@ -98,7 +99,7 @@ struct zc0301_module_param { u16 frame_timeout; }; -static DECLARE_RWSEM(zc0301_disconnect); +static DECLARE_RWSEM(zc0301_dev_lock); struct zc0301_device { struct video_device* v4ldev; @@ -121,12 +122,14 @@ struct zc0301_device { struct zc0301_module_param module_param; + struct kref kref; enum zc0301_dev_state state; u8 users; - struct mutex dev_mutex, fileop_mutex; + struct completion probe; + struct mutex open_mutex, fileop_mutex; spinlock_t queue_lock; - wait_queue_head_t open, wait_frame, wait_stream; + wait_queue_head_t wait_open, wait_frame, wait_stream; }; /*****************************************************************************/ @@ -156,8 +159,8 @@ do { \ else if ((level) == 2) \ dev_info(&cam->usbdev->dev, fmt "\n", ## args); \ else if ((level) >= 3) \ - dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ - __FUNCTION__, __LINE__ , ## args); \ + dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \ + __FILE__, __FUNCTION__, __LINE__ , ## args); \ } \ } while (0) # define KDBG(level, fmt, args...) \ @@ -166,8 +169,8 @@ do { \ if ((level) == 1 || (level) == 2) \ pr_info("zc0301: " fmt "\n", ## args); \ else if ((level) == 3) \ - pr_debug("zc0301: [%s:%d] " fmt "\n", __FUNCTION__, \ - __LINE__ , ## args); \ + pr_debug("sn9c102: [%s:%s:%d] " fmt "\n", __FILE__, \ + __FUNCTION__, __LINE__ , ## args); \ } \ } while (0) # define V4LDBG(level, name, cmd) \ @@ -183,8 +186,8 @@ do { \ #undef PDBG #define PDBG(fmt, args...) \ -dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ - __FUNCTION__, __LINE__ , ## args) +dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __FUNCTION__, \ + __LINE__ , ## args) #undef PDBGG #define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ diff --git a/drivers/media/video/zc0301/zc0301_core.c b/drivers/media/video/zc0301/zc0301_core.c index f112055..703b741 100644 --- a/drivers/media/video/zc0301/zc0301_core.c +++ b/drivers/media/video/zc0301/zc0301_core.c @@ -49,11 +49,11 @@ #define ZC0301_MODULE_NAME "V4L2 driver for ZC0301[P] " \ "Image Processor and Control Chip" -#define ZC0301_MODULE_AUTHOR "(C) 2006 Luca Risolia" +#define ZC0301_MODULE_AUTHOR "(C) 2006-2007 Luca Risolia" #define ZC0301_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>" #define ZC0301_MODULE_LICENSE "GPL" -#define ZC0301_MODULE_VERSION "1:1.07" -#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 7) +#define ZC0301_MODULE_VERSION "1:1.10" +#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 10) /*****************************************************************************/ @@ -573,7 +573,8 @@ static int zc0301_init(struct zc0301_device* cam) int err = 0; if (!(cam->state & DEV_INITIALIZED)) { - init_waitqueue_head(&cam->open); + mutex_init(&cam->open_mutex); + init_waitqueue_head(&cam->wait_open); qctrl = s->qctrl; rect = &(s->cropcap.defrect); cam->compression.quality = ZC0301_COMPRESSION_QUALITY; @@ -634,59 +635,73 @@ static int zc0301_init(struct zc0301_device* cam) return 0; } +/*****************************************************************************/ -static void zc0301_release_resources(struct zc0301_device* cam) +static void zc0301_release_resources(struct kref *kref) { + struct zc0301_device *cam = container_of(kref, struct zc0301_device, + kref); DBG(2, "V4L2 device /dev/video%d deregistered", cam->v4ldev->minor); video_set_drvdata(cam->v4ldev, NULL); video_unregister_device(cam->v4ldev); + usb_put_dev(cam->usbdev); kfree(cam->control_buffer); + kfree(cam); } -/*****************************************************************************/ static int zc0301_open(struct inode* inode, struct file* filp) { struct zc0301_device* cam; int err = 0; - /* - This is the only safe way to prevent race conditions with - disconnect - */ - if (!down_read_trylock(&zc0301_disconnect)) + if (!down_read_trylock(&zc0301_dev_lock)) return -ERESTARTSYS; cam = video_get_drvdata(video_devdata(filp)); - if (mutex_lock_interruptible(&cam->dev_mutex)) { - up_read(&zc0301_disconnect); + if (wait_for_completion_interruptible(&cam->probe)) { + up_read(&zc0301_dev_lock); return -ERESTARTSYS; } + kref_get(&cam->kref); + + if (mutex_lock_interruptible(&cam->open_mutex)) { + kref_put(&cam->kref, zc0301_release_resources); + up_read(&zc0301_dev_lock); + return -ERESTARTSYS; + } + + if (cam->state & DEV_DISCONNECTED) { + DBG(1, "Device not present"); + err = -ENODEV; + goto out; + } + if (cam->users) { DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor); + DBG(3, "Simultaneous opens are not supported"); if ((filp->f_flags & O_NONBLOCK) || (filp->f_flags & O_NDELAY)) { err = -EWOULDBLOCK; goto out; } - mutex_unlock(&cam->dev_mutex); - err = wait_event_interruptible_exclusive(cam->open, - cam->state & DEV_DISCONNECTED + DBG(2, "A blocking open() has been requested. Wait for the " + "device to be released..."); + up_read(&zc0301_dev_lock); + err = wait_event_interruptible_exclusive(cam->wait_open, + (cam->state & DEV_DISCONNECTED) || !cam->users); - if (err) { - up_read(&zc0301_disconnect); - return err; - } + down_read(&zc0301_dev_lock); + if (err) + goto out; if (cam->state & DEV_DISCONNECTED) { - up_read(&zc0301_disconnect); - return -ENODEV; + err = -ENODEV; + goto out; } - mutex_lock(&cam->dev_mutex); } - if (cam->state & DEV_MISCONFIGURED) { err = zc0301_init(cam); if (err) { @@ -711,36 +726,32 @@ static int zc0301_open(struct inode* inode, struct file* filp) DBG(3, "Video device /dev/video%d is open", cam->v4ldev->minor); out: - mutex_unlock(&cam->dev_mutex); - up_read(&zc0301_disconnect); + mutex_unlock(&cam->open_mutex); + if (err) + kref_put(&cam->kref, zc0301_release_resources); + up_read(&zc0301_dev_lock); return err; } static int zc0301_release(struct inode* inode, struct file* filp) { - struct zc0301_device* cam = video_get_drvdata(video_devdata(filp)); + struct zc0301_device* cam; - mutex_lock(&cam->dev_mutex); /* prevent disconnect() to be called */ + down_write(&zc0301_dev_lock); - zc0301_stop_transfer(cam); + cam = video_get_drvdata(video_devdata(filp)); + zc0301_stop_transfer(cam); zc0301_release_buffers(cam); - - if (cam->state & DEV_DISCONNECTED) { - zc0301_release_resources(cam); - usb_put_dev(cam->usbdev); - mutex_unlock(&cam->dev_mutex); - kfree(cam); - return 0; - } - cam->users--; - wake_up_interruptible_nr(&cam->open, 1); + wake_up_interruptible_nr(&cam->wait_open, 1); DBG(3, "Video device /dev/video%d closed", cam->v4ldev->minor); - mutex_unlock(&cam->dev_mutex); + kref_put(&cam->kref, zc0301_release_resources); + + up_write(&zc0301_dev_lock); return 0; } @@ -775,7 +786,7 @@ zc0301_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) DBG(3, "Close and open the device again to choose the read " "method"); mutex_unlock(&cam->fileop_mutex); - return -EINVAL; + return -EBUSY; } if (cam->io == IO_NONE) { @@ -953,7 +964,12 @@ static int zc0301_mmap(struct file* filp, struct vm_area_struct *vma) return -EIO; } - if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) || + if (!(vma->vm_flags & (VM_WRITE | VM_READ))) { + mutex_unlock(&cam->fileop_mutex); + return -EACCES; + } + + if (cam->io != IO_MMAP || size != PAGE_ALIGN(cam->frame[0].buf.length)) { mutex_unlock(&cam->fileop_mutex); return -EINVAL; @@ -984,7 +1000,6 @@ static int zc0301_mmap(struct file* filp, struct vm_area_struct *vma) vma->vm_ops = &zc0301_vm_ops; vma->vm_private_data = &cam->frame[i]; - zc0301_vm_open(vma); mutex_unlock(&cam->fileop_mutex); @@ -1211,7 +1226,7 @@ zc0301_vidioc_s_crop(struct zc0301_device* cam, void __user * arg) if (cam->frame[i].vma_use_count) { DBG(3, "VIDIOC_S_CROP failed. " "Unmap the buffers first."); - return -EINVAL; + return -EBUSY; } if (!s->set_crop) { @@ -1434,7 +1449,7 @@ zc0301_vidioc_try_s_fmt(struct zc0301_device* cam, unsigned int cmd, if (cam->frame[i].vma_use_count) { DBG(3, "VIDIOC_S_FMT failed. " "Unmap the buffers first."); - return -EINVAL; + return -EBUSY; } if (cam->stream == STREAM_ON) @@ -1544,14 +1559,14 @@ zc0301_vidioc_reqbufs(struct zc0301_device* cam, void __user * arg) if (cam->io == IO_READ) { DBG(3, "Close and open the device again to choose the mmap " "I/O method"); - return -EINVAL; + return -EBUSY; } for (i = 0; i < cam->nbuffers; i++) if (cam->frame[i].vma_use_count) { DBG(3, "VIDIOC_REQBUFS failed. " "Previous buffers are still mapped."); - return -EINVAL; + return -EBUSY; } if (cam->stream == STREAM_ON) @@ -1699,9 +1714,6 @@ zc0301_vidioc_streamon(struct zc0301_device* cam, void __user * arg) if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) return -EINVAL; - if (list_empty(&cam->inqueue)) - return -EINVAL; - cam->stream = STREAM_ON; DBG(3, "Stream on"); @@ -1949,8 +1961,6 @@ zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) goto fail; } - mutex_init(&cam->dev_mutex); - DBG(2, "ZC0301[P] Image Processor and Control Chip detected " "(vid/pid 0x%04X:0x%04X)",id->idVendor, id->idProduct); @@ -1982,7 +1992,7 @@ zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) cam->v4ldev->release = video_device_release; video_set_drvdata(cam->v4ldev, cam); - mutex_lock(&cam->dev_mutex); + init_completion(&cam->probe); err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, video_nr[dev_nr]); @@ -1992,7 +2002,7 @@ zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) DBG(1, "Free /dev/videoX node not found"); video_nr[dev_nr] = -1; dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0; - mutex_unlock(&cam->dev_mutex); + complete_all(&cam->probe); goto fail; } @@ -2004,8 +2014,10 @@ zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) dev_nr = (dev_nr < ZC0301_MAX_DEVICES-1) ? dev_nr+1 : 0; usb_set_intfdata(intf, cam); + kref_init(&cam->kref); + usb_get_dev(cam->usbdev); - mutex_unlock(&cam->dev_mutex); + complete_all(&cam->probe); return 0; @@ -2022,40 +2034,31 @@ fail: static void zc0301_usb_disconnect(struct usb_interface* intf) { - struct zc0301_device* cam = usb_get_intfdata(intf); - - if (!cam) - return; + struct zc0301_device* cam; - down_write(&zc0301_disconnect); + down_write(&zc0301_dev_lock); - mutex_lock(&cam->dev_mutex); + cam = usb_get_intfdata(intf); DBG(2, "Disconnecting %s...", cam->v4ldev->name); - wake_up_interruptible_all(&cam->open); - if (cam->users) { DBG(2, "Device /dev/video%d is open! Deregistration and " - "memory deallocation are deferred on close.", + "memory deallocation are deferred.", cam->v4ldev->minor); cam->state |= DEV_MISCONFIGURED; zc0301_stop_transfer(cam); cam->state |= DEV_DISCONNECTED; wake_up_interruptible(&cam->wait_frame); wake_up(&cam->wait_stream); - usb_get_dev(cam->usbdev); - } else { + } else cam->state |= DEV_DISCONNECTED; - zc0301_release_resources(cam); - } - mutex_unlock(&cam->dev_mutex); + wake_up_interruptible_all(&cam->wait_open); - if (!cam->users) - kfree(cam); + kref_put(&cam->kref, zc0301_release_resources); - up_write(&zc0301_disconnect); + up_write(&zc0301_dev_lock); } diff --git a/drivers/media/video/zc0301/zc0301_pas202bcb.c b/drivers/media/video/zc0301/zc0301_pas202bcb.c index 3efb92a..24b0dfb 100644 --- a/drivers/media/video/zc0301/zc0301_pas202bcb.c +++ b/drivers/media/video/zc0301/zc0301_pas202bcb.c @@ -327,6 +327,7 @@ static struct zc0301_sensor pas202bcb = { .height = 480, .pixelformat = V4L2_PIX_FMT_JPEG, .priv = 8, + .colorspace = V4L2_COLORSPACE_JPEG, }, }; diff --git a/drivers/media/video/zc0301/zc0301_pb0330.c b/drivers/media/video/zc0301/zc0301_pb0330.c index 5784b1d..9519aba 100644 --- a/drivers/media/video/zc0301/zc0301_pb0330.c +++ b/drivers/media/video/zc0301/zc0301_pb0330.c @@ -157,6 +157,7 @@ static struct zc0301_sensor pb0330 = { .height = 480, .pixelformat = V4L2_PIX_FMT_JPEG, .priv = 8, + .colorspace = V4L2_COLORSPACE_JPEG, }, }; diff --git a/drivers/media/video/zc0301/zc0301_sensor.h b/drivers/media/video/zc0301/zc0301_sensor.h index 44e82cf..70fe6fc 100644 --- a/drivers/media/video/zc0301/zc0301_sensor.h +++ b/drivers/media/video/zc0301/zc0301_sensor.h @@ -23,7 +23,7 @@ #define _ZC0301_SENSOR_H_ #include <linux/usb.h> -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <linux/device.h> #include <linux/stddef.h> #include <linux/errno.h> diff --git a/drivers/media/video/zoran_driver.c b/drivers/media/video/zoran_driver.c index cf0ed6c..17118a4 100644 --- a/drivers/media/video/zoran_driver.c +++ b/drivers/media/video/zoran_driver.c @@ -183,14 +183,7 @@ static const int zoran_num_formats = (sizeof(zoran_formats) / sizeof(struct zoran_format)); // RJ: Test only - want to test BUZ_USE_HIMEM even when CONFIG_BIGPHYS_AREA is defined -#if !defined(CONFIG_BIGPHYS_AREA) -//#undef CONFIG_BIGPHYS_AREA -#define BUZ_USE_HIMEM -#endif -#if defined(CONFIG_BIGPHYS_AREA) -# include <linux/bigphysarea.h> -#endif extern int *zr_debug; @@ -250,7 +243,6 @@ static void jpg_fbuffer_free(struct file *file); * Linux with the necessary memory left over). */ -#if defined(BUZ_USE_HIMEM) && !defined(CONFIG_BIGPHYS_AREA) static unsigned long get_high_mem (unsigned long size) { @@ -314,7 +306,6 @@ get_high_mem (unsigned long size) return hi_mem_ph; } -#endif static int v4l_fbuffer_alloc (struct file *file) @@ -323,9 +314,7 @@ v4l_fbuffer_alloc (struct file *file) struct zoran *zr = fh->zr; int i, off; unsigned char *mem; -#if defined(BUZ_USE_HIMEM) && !defined(CONFIG_BIGPHYS_AREA) unsigned long pmem = 0; -#endif /* we might have old buffers lying around... */ if (fh->v4l_buffers.ready_to_be_freed) { @@ -369,39 +358,6 @@ v4l_fbuffer_alloc (struct file *file) ZR_DEVNAME(zr), i, (unsigned long) mem, virt_to_bus(mem)); } else { -#if defined(CONFIG_BIGPHYS_AREA) - /* Use bigphysarea_alloc_pages */ - - int n = - (fh->v4l_buffers.buffer_size + PAGE_SIZE - - 1) / PAGE_SIZE; - - mem = - (unsigned char *) bigphysarea_alloc_pages(n, 0, - GFP_KERNEL); - if (mem == 0) { - dprintk(1, - KERN_ERR - "%s: v4l_fbuffer_alloc() - bigphysarea_alloc_pages for V4L buf %d failed\n", - ZR_DEVNAME(zr), i); - v4l_fbuffer_free(file); - return -ENOBUFS; - } - fh->v4l_buffers.buffer[i].fbuffer = mem; - fh->v4l_buffers.buffer[i].fbuffer_phys = - virt_to_phys(mem); - fh->v4l_buffers.buffer[i].fbuffer_bus = - virt_to_bus(mem); - dprintk(4, - KERN_INFO - "%s: Bigphysarea frame %d mem 0x%x (bus: 0x%x)\n", - ZR_DEVNAME(zr), i, (unsigned) mem, - (unsigned) virt_to_bus(mem)); - - /* Zero out the allocated memory */ - memset(fh->v4l_buffers.buffer[i].fbuffer, 0, - fh->v4l_buffers.buffer_size); -#elif defined(BUZ_USE_HIMEM) /* Use high memory which has been left at boot time */ @@ -441,20 +397,6 @@ v4l_fbuffer_alloc (struct file *file) fh->v4l_buffers.buffer[i].fbuffer_bus = pmem + i * fh->v4l_buffers.buffer_size; } -#else - /* No bigphysarea present, usage of high memory disabled, - * but user wants buffers of more than MAX_KMALLOC_MEM */ - dprintk(1, - KERN_ERR - "%s: v4l_fbuffer_alloc() - no bigphysarea_patch present, usage of high memory disabled,\n", - ZR_DEVNAME(zr)); - dprintk(1, - KERN_ERR - "%s: v4l_fbuffer_alloc() - sorry, could not allocate %d V4L buffers of size %d KB.\n", - ZR_DEVNAME(zr), fh->v4l_buffers.num_buffers, - fh->v4l_buffers.buffer_size >> 10); - return -ENOBUFS; -#endif } } @@ -485,11 +427,6 @@ v4l_fbuffer_free (struct file *file) ClearPageReserved(MAP_NR(mem + off)); kfree((void *) fh->v4l_buffers.buffer[i].fbuffer); } -#if defined(CONFIG_BIGPHYS_AREA) - else - bigphysarea_free_pages((void *) fh->v4l_buffers. - buffer[i].fbuffer); -#endif fh->v4l_buffers.buffer[i].fbuffer = NULL; } diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c index b5d3364..6f18925 100644 --- a/drivers/media/video/zr364xx.c +++ b/drivers/media/video/zr364xx.c @@ -92,6 +92,7 @@ static struct usb_device_id device_table[] = { {USB_DEVICE(0x0784, 0x0040), .driver_info = METHOD1 }, {USB_DEVICE(0x06d6, 0x0034), .driver_info = METHOD0 }, {USB_DEVICE(0x0a17, 0x0062), .driver_info = METHOD2 }, + {USB_DEVICE(0x06d6, 0x003b), .driver_info = METHOD0 }, {} /* Terminating entry */ }; @@ -792,6 +793,7 @@ static int zr364xx_probe(struct usb_interface *intf, { struct usb_device *udev = interface_to_usbdev(intf); struct zr364xx_camera *cam = NULL; + int err; DBG("probing..."); @@ -799,12 +801,11 @@ static int zr364xx_probe(struct usb_interface *intf, info("model %04x:%04x detected", udev->descriptor.idVendor, udev->descriptor.idProduct); - if ((cam = - kmalloc(sizeof(struct zr364xx_camera), GFP_KERNEL)) == NULL) { + cam = kzalloc(sizeof(struct zr364xx_camera), GFP_KERNEL); + if (cam == NULL) { info("cam: out of memory !"); - return -ENODEV; + return -ENOMEM; } - memset(cam, 0x00, sizeof(struct zr364xx_camera)); /* save the init method used by this camera */ cam->method = id->driver_info; @@ -812,7 +813,7 @@ static int zr364xx_probe(struct usb_interface *intf, if (cam->vdev == NULL) { info("cam->vdev: out of memory !"); kfree(cam); - return -ENODEV; + return -ENOMEM; } memcpy(cam->vdev, &zr364xx_template, sizeof(zr364xx_template)); video_set_drvdata(cam->vdev, cam); @@ -858,12 +859,13 @@ static int zr364xx_probe(struct usb_interface *intf, cam->brightness = 64; mutex_init(&cam->lock); - if (video_register_device(cam->vdev, VFL_TYPE_GRABBER, -1) == -1) { + err = video_register_device(cam->vdev, VFL_TYPE_GRABBER, -1); + if (err) { info("video_register_device failed"); video_device_release(cam->vdev); kfree(cam->buffer); kfree(cam); - return -ENODEV; + return err; } usb_set_intfdata(intf, cam); @@ -905,7 +907,7 @@ static struct usb_driver zr364xx_driver = { static int __init zr364xx_init(void) { int retval; - retval = usb_register(&zr364xx_driver) < 0; + retval = usb_register(&zr364xx_driver); if (retval) info("usb_register failed!"); else diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 555d594..1cb22bf 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -33,6 +33,7 @@ #include <linux/moduleparam.h> #include <linux/stringify.h> #include <linux/stat.h> +#include <linux/log2.h> #include "ubi.h" /* Maximum length of the 'mtd=' parameter */ @@ -369,7 +370,7 @@ static int attach_by_scanning(struct ubi_device *ubi) out_wl: ubi_wl_close(ubi); out_vtbl: - kfree(ubi->vtbl); + vfree(ubi->vtbl); out_si: ubi_scan_destroy_si(si); return err; @@ -422,8 +423,7 @@ static int io_init(struct ubi_device *ubi) ubi->hdrs_min_io_size = ubi->mtd->writesize >> ubi->mtd->subpage_sft; /* Make sure minimal I/O unit is power of 2 */ - if (ubi->min_io_size == 0 || - (ubi->min_io_size & (ubi->min_io_size - 1))) { + if (!is_power_of_2(ubi->min_io_size)) { ubi_err("bad min. I/O unit"); return -EINVAL; } @@ -593,8 +593,6 @@ static int attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset, if (err) goto out_detach; - ubi_devices_cnt += 1; - ubi_msg("attached mtd%d to ubi%d", ubi->mtd->index, ubi_devices_cnt); ubi_msg("MTD device name: \"%s\"", ubi->mtd->name); ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20); @@ -624,12 +622,13 @@ static int attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset, wake_up_process(ubi->bgt_thread); } + ubi_devices_cnt += 1; return 0; out_detach: ubi_eba_close(ubi); ubi_wl_close(ubi); - kfree(ubi->vtbl); + vfree(ubi->vtbl); out_free: kfree(ubi); out_mtd: @@ -650,7 +649,7 @@ static void detach_mtd_dev(struct ubi_device *ubi) uif_close(ubi); ubi_eba_close(ubi); ubi_wl_close(ubi); - kfree(ubi->vtbl); + vfree(ubi->vtbl); put_mtd_device(ubi->mtd); kfree(ubi_devices[ubi_num]); ubi_devices[ubi_num] = NULL; @@ -686,13 +685,6 @@ static int __init ubi_init(void) struct mtd_dev_param *p = &mtd_dev_param[i]; cond_resched(); - - if (!p->name) { - dbg_err("empty name"); - err = -EINVAL; - goto out_detach; - } - err = attach_mtd_dev(p->name, p->vid_hdr_offs, p->data_offs); if (err) goto out_detach; @@ -799,7 +791,7 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) /* Get rid of the final newline */ if (buf[len - 1] == '\n') - buf[len - 1] = 0; + buf[len - 1] = '\0'; for (i = 0; i < 3; i++) tokens[i] = strsep(&pbuf, ","); @@ -809,9 +801,6 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) return -EINVAL; } - if (tokens[0] == '\0') - return -EINVAL; - p = &mtd_dev_param[mtd_devs]; strcpy(&p->name[0], tokens[0]); diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index 6612eb7..fe4da1e 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -64,6 +64,7 @@ static struct ubi_device *major_to_device(int major) if (ubi_devices[i] && ubi_devices[i]->major == major) return ubi_devices[i]; BUG(); + return NULL; } /** @@ -153,7 +154,7 @@ static int vol_cdev_release(struct inode *inode, struct file *file) ubi_warn("update of volume %d not finished, volume is damaged", vol->vol_id); vol->updating = 0; - kfree(vol->upd_buf); + vfree(vol->upd_buf); } ubi_close_volume(desc); @@ -232,7 +233,7 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, tbuf_size = vol->usable_leb_size; if (count < tbuf_size) tbuf_size = ALIGN(count, ubi->min_io_size); - tbuf = kmalloc(tbuf_size, GFP_KERNEL); + tbuf = vmalloc(tbuf_size); if (!tbuf) return -ENOMEM; @@ -271,7 +272,7 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, len = count > tbuf_size ? tbuf_size : count; } while (count); - kfree(tbuf); + vfree(tbuf); return err ? err : count_save - count; } @@ -320,7 +321,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, tbuf_size = vol->usable_leb_size; if (count < tbuf_size) tbuf_size = ALIGN(count, ubi->min_io_size); - tbuf = kmalloc(tbuf_size, GFP_KERNEL); + tbuf = vmalloc(tbuf_size); if (!tbuf) return -ENOMEM; @@ -355,7 +356,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, len = count > tbuf_size ? tbuf_size : count; } - kfree(tbuf); + vfree(tbuf); return err ? err : count_save - count; } @@ -397,6 +398,7 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf, vol->corrupted = 1; } vol->checked = 1; + ubi_gluebi_updated(vol); revoke_exclusive(desc, UBI_READWRITE); } @@ -413,19 +415,7 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, struct ubi_device *ubi = vol->ubi; void __user *argp = (void __user *)arg; - if (_IOC_NR(cmd) > VOL_CDEV_IOC_MAX_SEQ || - _IOC_TYPE(cmd) != UBI_VOL_IOC_MAGIC) - return -ENOTTY; - - if (_IOC_DIR(cmd) && _IOC_READ) - err = !access_ok(VERIFY_WRITE, argp, _IOC_SIZE(cmd)); - else if (_IOC_DIR(cmd) && _IOC_WRITE) - err = !access_ok(VERIFY_READ, argp, _IOC_SIZE(cmd)); - if (err) - return -EFAULT; - switch (cmd) { - /* Volume update command */ case UBI_IOCVOLUP: { @@ -471,7 +461,7 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file, { int32_t lnum; - err = __get_user(lnum, (__user int32_t *)argp); + err = get_user(lnum, (__user int32_t *)argp); if (err) { err = -EFAULT; break; @@ -587,17 +577,6 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, struct ubi_volume_desc *desc; void __user *argp = (void __user *)arg; - if (_IOC_NR(cmd) > UBI_CDEV_IOC_MAX_SEQ || - _IOC_TYPE(cmd) != UBI_IOC_MAGIC) - return -ENOTTY; - - if (_IOC_DIR(cmd) && _IOC_READ) - err = !access_ok(VERIFY_WRITE, argp, _IOC_SIZE(cmd)); - else if (_IOC_DIR(cmd) && _IOC_WRITE) - err = !access_ok(VERIFY_READ, argp, _IOC_SIZE(cmd)); - if (err) - return -EFAULT; - if (!capable(CAP_SYS_RESOURCE)) return -EPERM; @@ -612,7 +591,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, struct ubi_mkvol_req req; dbg_msg("create volume"); - err = __copy_from_user(&req, argp, + err = copy_from_user(&req, argp, sizeof(struct ubi_mkvol_req)); if (err) { err = -EFAULT; @@ -629,7 +608,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, if (err) break; - err = __put_user(req.vol_id, (__user int32_t *)argp); + err = put_user(req.vol_id, (__user int32_t *)argp); if (err) err = -EFAULT; @@ -642,7 +621,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, int vol_id; dbg_msg("remove volume"); - err = __get_user(vol_id, (__user int32_t *)argp); + err = get_user(vol_id, (__user int32_t *)argp); if (err) { err = -EFAULT; break; @@ -669,7 +648,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, struct ubi_rsvol_req req; dbg_msg("re-size volume"); - err = __copy_from_user(&req, argp, + err = copy_from_user(&req, argp, sizeof(struct ubi_rsvol_req)); if (err) { err = -EFAULT; @@ -707,7 +686,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file, struct file_operations ubi_cdev_operations = { .owner = THIS_MODULE, .ioctl = ubi_cdev_ioctl, - .llseek = no_llseek + .llseek = no_llseek, }; /* UBI volume character device operations */ @@ -718,5 +697,5 @@ struct file_operations ubi_vol_cdev_operations = { .llseek = vol_cdev_llseek, .read = vol_cdev_read, .write = vol_cdev_write, - .ioctl = vol_cdev_ioctl + .ioctl = vol_cdev_ioctl, }; diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c index 8636422..310341e 100644 --- a/drivers/mtd/ubi/debug.c +++ b/drivers/mtd/ubi/debug.c @@ -35,12 +35,12 @@ void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr) { dbg_msg("erase counter header dump:"); - dbg_msg("magic %#08x", ubi32_to_cpu(ec_hdr->magic)); + dbg_msg("magic %#08x", be32_to_cpu(ec_hdr->magic)); dbg_msg("version %d", (int)ec_hdr->version); - dbg_msg("ec %llu", (long long)ubi64_to_cpu(ec_hdr->ec)); - dbg_msg("vid_hdr_offset %d", ubi32_to_cpu(ec_hdr->vid_hdr_offset)); - dbg_msg("data_offset %d", ubi32_to_cpu(ec_hdr->data_offset)); - dbg_msg("hdr_crc %#08x", ubi32_to_cpu(ec_hdr->hdr_crc)); + dbg_msg("ec %llu", (long long)be64_to_cpu(ec_hdr->ec)); + dbg_msg("vid_hdr_offset %d", be32_to_cpu(ec_hdr->vid_hdr_offset)); + dbg_msg("data_offset %d", be32_to_cpu(ec_hdr->data_offset)); + dbg_msg("hdr_crc %#08x", be32_to_cpu(ec_hdr->hdr_crc)); dbg_msg("erase counter header hexdump:"); ubi_dbg_hexdump(ec_hdr, UBI_EC_HDR_SIZE); } @@ -52,20 +52,20 @@ void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr) void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr) { dbg_msg("volume identifier header dump:"); - dbg_msg("magic %08x", ubi32_to_cpu(vid_hdr->magic)); + dbg_msg("magic %08x", be32_to_cpu(vid_hdr->magic)); dbg_msg("version %d", (int)vid_hdr->version); dbg_msg("vol_type %d", (int)vid_hdr->vol_type); dbg_msg("copy_flag %d", (int)vid_hdr->copy_flag); dbg_msg("compat %d", (int)vid_hdr->compat); - dbg_msg("vol_id %d", ubi32_to_cpu(vid_hdr->vol_id)); - dbg_msg("lnum %d", ubi32_to_cpu(vid_hdr->lnum)); - dbg_msg("leb_ver %u", ubi32_to_cpu(vid_hdr->leb_ver)); - dbg_msg("data_size %d", ubi32_to_cpu(vid_hdr->data_size)); - dbg_msg("used_ebs %d", ubi32_to_cpu(vid_hdr->used_ebs)); - dbg_msg("data_pad %d", ubi32_to_cpu(vid_hdr->data_pad)); + dbg_msg("vol_id %d", be32_to_cpu(vid_hdr->vol_id)); + dbg_msg("lnum %d", be32_to_cpu(vid_hdr->lnum)); + dbg_msg("leb_ver %u", be32_to_cpu(vid_hdr->leb_ver)); + dbg_msg("data_size %d", be32_to_cpu(vid_hdr->data_size)); + dbg_msg("used_ebs %d", be32_to_cpu(vid_hdr->used_ebs)); + dbg_msg("data_pad %d", be32_to_cpu(vid_hdr->data_pad)); dbg_msg("sqnum %llu", - (unsigned long long)ubi64_to_cpu(vid_hdr->sqnum)); - dbg_msg("hdr_crc %08x", ubi32_to_cpu(vid_hdr->hdr_crc)); + (unsigned long long)be64_to_cpu(vid_hdr->sqnum)); + dbg_msg("hdr_crc %08x", be32_to_cpu(vid_hdr->hdr_crc)); dbg_msg("volume identifier header hexdump:"); } @@ -91,7 +91,7 @@ void ubi_dbg_dump_vol_info(const struct ubi_volume *vol) if (vol->name_len <= UBI_VOL_NAME_MAX && strnlen(vol->name, vol->name_len + 1) == vol->name_len) { - dbg_msg("name %s", vol->name); + dbg_msg("name %s", vol->name); } else { dbg_msg("the 1st 5 characters of the name: %c%c%c%c%c", vol->name[0], vol->name[1], vol->name[2], @@ -106,30 +106,30 @@ void ubi_dbg_dump_vol_info(const struct ubi_volume *vol) */ void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx) { - int name_len = ubi16_to_cpu(r->name_len); + int name_len = be16_to_cpu(r->name_len); dbg_msg("volume table record %d dump:", idx); - dbg_msg("reserved_pebs %d", ubi32_to_cpu(r->reserved_pebs)); - dbg_msg("alignment %d", ubi32_to_cpu(r->alignment)); - dbg_msg("data_pad %d", ubi32_to_cpu(r->data_pad)); + dbg_msg("reserved_pebs %d", be32_to_cpu(r->reserved_pebs)); + dbg_msg("alignment %d", be32_to_cpu(r->alignment)); + dbg_msg("data_pad %d", be32_to_cpu(r->data_pad)); dbg_msg("vol_type %d", (int)r->vol_type); dbg_msg("upd_marker %d", (int)r->upd_marker); dbg_msg("name_len %d", name_len); if (r->name[0] == '\0') { - dbg_msg("name NULL"); + dbg_msg("name NULL"); return; } if (name_len <= UBI_VOL_NAME_MAX && strnlen(&r->name[0], name_len + 1) == name_len) { - dbg_msg("name %s", &r->name[0]); + dbg_msg("name %s", &r->name[0]); } else { dbg_msg("1st 5 characters of the name: %c%c%c%c%c", r->name[0], r->name[1], r->name[2], r->name[3], r->name[4]); } - dbg_msg("crc %#08x", ubi32_to_cpu(r->crc)); + dbg_msg("crc %#08x", be32_to_cpu(r->crc)); } /** diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h index f816ad9..ff8f395 100644 --- a/drivers/mtd/ubi/debug.h +++ b/drivers/mtd/ubi/debug.h @@ -52,7 +52,6 @@ struct ubi_scan_volume; struct ubi_scan_leb; struct ubi_mkvol_req; -void ubi_dbg_print(int type, const char *func, const char *fmt, ...); void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr); void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr); void ubi_dbg_dump_vol_info(const struct ubi_volume *vol); @@ -66,7 +65,6 @@ void ubi_dbg_hexdump(const void *buf, int size); #define dbg_msg(fmt, ...) ({}) #define ubi_dbg_dump_stack() ({}) -#define ubi_dbg_print(func, fmt, ...) ({}) #define ubi_dbg_dump_ec_hdr(ec_hdr) ({}) #define ubi_dbg_dump_vid_hdr(vid_hdr) ({}) #define ubi_dbg_dump_vol_info(vol) ({}) diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c index 7c6b223..8aff938 100644 --- a/drivers/mtd/ubi/eba.c +++ b/drivers/mtd/ubi/eba.c @@ -425,10 +425,10 @@ retry: } else if (err == UBI_IO_BITFLIPS) scrub = 1; - ubi_assert(lnum < ubi32_to_cpu(vid_hdr->used_ebs)); - ubi_assert(len == ubi32_to_cpu(vid_hdr->data_size)); + ubi_assert(lnum < be32_to_cpu(vid_hdr->used_ebs)); + ubi_assert(len == be32_to_cpu(vid_hdr->data_size)); - crc = ubi32_to_cpu(vid_hdr->data_crc); + crc = be32_to_cpu(vid_hdr->data_crc); ubi_free_vid_hdr(ubi, vid_hdr); } @@ -518,13 +518,13 @@ retry: goto out_put; } - vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); + vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr); if (err) goto write_error; data_size = offset + len; - new_buf = kmalloc(data_size, GFP_KERNEL); + new_buf = vmalloc(data_size); if (!new_buf) { err = -ENOMEM; goto out_put; @@ -535,7 +535,7 @@ retry: if (offset > 0) { err = ubi_io_read_data(ubi, new_buf, pnum, 0, offset); if (err && err != UBI_IO_BITFLIPS) { - kfree(new_buf); + vfree(new_buf); goto out_put; } } @@ -544,11 +544,11 @@ retry: err = ubi_io_write_data(ubi, new_buf, new_pnum, 0, data_size); if (err) { - kfree(new_buf); + vfree(new_buf); goto write_error; } - kfree(new_buf); + vfree(new_buf); ubi_free_vid_hdr(ubi, vid_hdr); vol->eba_tbl[lnum] = new_pnum; @@ -634,11 +634,11 @@ int ubi_eba_write_leb(struct ubi_device *ubi, int vol_id, int lnum, } vid_hdr->vol_type = UBI_VID_DYNAMIC; - vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); - vid_hdr->vol_id = cpu_to_ubi32(vol_id); - vid_hdr->lnum = cpu_to_ubi32(lnum); + vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + vid_hdr->vol_id = cpu_to_be32(vol_id); + vid_hdr->lnum = cpu_to_be32(lnum); vid_hdr->compat = ubi_get_compat(ubi, vol_id); - vid_hdr->data_pad = cpu_to_ubi32(vol->data_pad); + vid_hdr->data_pad = cpu_to_be32(vol->data_pad); retry: pnum = ubi_wl_get_peb(ubi, dtype); @@ -692,7 +692,7 @@ write_error: return err; } - vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); + vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); ubi_msg("try another PEB"); goto retry; } @@ -748,17 +748,17 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, int vol_id, int lnum, return err; } - vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); - vid_hdr->vol_id = cpu_to_ubi32(vol_id); - vid_hdr->lnum = cpu_to_ubi32(lnum); + vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + vid_hdr->vol_id = cpu_to_be32(vol_id); + vid_hdr->lnum = cpu_to_be32(lnum); vid_hdr->compat = ubi_get_compat(ubi, vol_id); - vid_hdr->data_pad = cpu_to_ubi32(vol->data_pad); + vid_hdr->data_pad = cpu_to_be32(vol->data_pad); crc = crc32(UBI_CRC32_INIT, buf, data_size); vid_hdr->vol_type = UBI_VID_STATIC; - vid_hdr->data_size = cpu_to_ubi32(data_size); - vid_hdr->used_ebs = cpu_to_ubi32(used_ebs); - vid_hdr->data_crc = cpu_to_ubi32(crc); + vid_hdr->data_size = cpu_to_be32(data_size); + vid_hdr->used_ebs = cpu_to_be32(used_ebs); + vid_hdr->data_crc = cpu_to_be32(crc); retry: pnum = ubi_wl_get_peb(ubi, dtype); @@ -813,7 +813,7 @@ write_error: return err; } - vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); + vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); ubi_msg("try another PEB"); goto retry; } @@ -854,17 +854,17 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, int vol_id, int lnum, return err; } - vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); - vid_hdr->vol_id = cpu_to_ubi32(vol_id); - vid_hdr->lnum = cpu_to_ubi32(lnum); + vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); + vid_hdr->vol_id = cpu_to_be32(vol_id); + vid_hdr->lnum = cpu_to_be32(lnum); vid_hdr->compat = ubi_get_compat(ubi, vol_id); - vid_hdr->data_pad = cpu_to_ubi32(vol->data_pad); + vid_hdr->data_pad = cpu_to_be32(vol->data_pad); crc = crc32(UBI_CRC32_INIT, buf, len); - vid_hdr->vol_type = UBI_VID_STATIC; - vid_hdr->data_size = cpu_to_ubi32(len); + vid_hdr->vol_type = UBI_VID_DYNAMIC; + vid_hdr->data_size = cpu_to_be32(len); vid_hdr->copy_flag = 1; - vid_hdr->data_crc = cpu_to_ubi32(crc); + vid_hdr->data_crc = cpu_to_be32(crc); retry: pnum = ubi_wl_get_peb(ubi, dtype); @@ -891,11 +891,13 @@ retry: goto write_error; } - err = ubi_wl_put_peb(ubi, vol->eba_tbl[lnum], 1); - if (err) { - ubi_free_vid_hdr(ubi, vid_hdr); - leb_write_unlock(ubi, vol_id, lnum); - return err; + if (vol->eba_tbl[lnum] >= 0) { + err = ubi_wl_put_peb(ubi, vol->eba_tbl[lnum], 1); + if (err) { + ubi_free_vid_hdr(ubi, vid_hdr); + leb_write_unlock(ubi, vol_id, lnum); + return err; + } } vol->eba_tbl[lnum] = pnum; @@ -924,7 +926,7 @@ write_error: return err; } - vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); + vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); ubi_msg("try another PEB"); goto retry; } @@ -965,19 +967,19 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, uint32_t crc; void *buf, *buf1 = NULL; - vol_id = ubi32_to_cpu(vid_hdr->vol_id); - lnum = ubi32_to_cpu(vid_hdr->lnum); + vol_id = be32_to_cpu(vid_hdr->vol_id); + lnum = be32_to_cpu(vid_hdr->lnum); dbg_eba("copy LEB %d:%d, PEB %d to PEB %d", vol_id, lnum, from, to); if (vid_hdr->vol_type == UBI_VID_STATIC) { - data_size = ubi32_to_cpu(vid_hdr->data_size); + data_size = be32_to_cpu(vid_hdr->data_size); aldata_size = ALIGN(data_size, ubi->min_io_size); } else data_size = aldata_size = - ubi->leb_size - ubi32_to_cpu(vid_hdr->data_pad); + ubi->leb_size - be32_to_cpu(vid_hdr->data_pad); - buf = kmalloc(aldata_size, GFP_KERNEL); + buf = vmalloc(aldata_size); if (!buf) return -ENOMEM; @@ -987,7 +989,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, */ err = leb_write_lock(ubi, vol_id, lnum); if (err) { - kfree(buf); + vfree(buf); return err; } @@ -1054,10 +1056,10 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, */ if (data_size > 0) { vid_hdr->copy_flag = 1; - vid_hdr->data_size = cpu_to_ubi32(data_size); - vid_hdr->data_crc = cpu_to_ubi32(crc); + vid_hdr->data_size = cpu_to_be32(data_size); + vid_hdr->data_crc = cpu_to_be32(crc); } - vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); + vid_hdr->sqnum = cpu_to_be64(next_sqnum(ubi)); err = ubi_io_write_vid_hdr(ubi, to, vid_hdr); if (err) @@ -1082,7 +1084,7 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, * We've written the data and are going to read it back to make * sure it was written correctly. */ - buf1 = kmalloc(aldata_size, GFP_KERNEL); + buf1 = vmalloc(aldata_size); if (!buf1) { err = -ENOMEM; goto out_unlock; @@ -1111,15 +1113,15 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, vol->eba_tbl[lnum] = to; leb_write_unlock(ubi, vol_id, lnum); - kfree(buf); - kfree(buf1); + vfree(buf); + vfree(buf1); return 0; out_unlock: leb_write_unlock(ubi, vol_id, lnum); - kfree(buf); - kfree(buf1); + vfree(buf); + vfree(buf1); return err; } diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c index fc9478d..41ff74c 100644 --- a/drivers/mtd/ubi/gluebi.c +++ b/drivers/mtd/ubi/gluebi.c @@ -282,7 +282,6 @@ int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol) mtd->flags = MTD_WRITEABLE; mtd->writesize = ubi->min_io_size; mtd->owner = THIS_MODULE; - mtd->size = vol->usable_leb_size * vol->reserved_pebs; mtd->erasesize = vol->usable_leb_size; mtd->read = gluebi_read; mtd->write = gluebi_write; @@ -290,6 +289,15 @@ int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol) mtd->get_device = gluebi_get_device; mtd->put_device = gluebi_put_device; + /* + * In case of dynamic volume, MTD device size is just volume size. In + * case of a static volume the size is equivalent to the amount of data + * bytes, which is zero at this moment and will be changed after volume + * update. + */ + if (vol->vol_type == UBI_DYNAMIC_VOLUME) + mtd->size = vol->usable_leb_size * vol->reserved_pebs; + if (add_mtd_device(mtd)) { ubi_err("cannot not add MTD device\n"); kfree(mtd->name); @@ -321,3 +329,20 @@ int ubi_destroy_gluebi(struct ubi_volume *vol) kfree(mtd->name); return 0; } + +/** + * ubi_gluebi_updated - UBI volume was updated notifier. + * @vol: volume description object + * + * This function is called every time an UBI volume is updated. This function + * does nothing if volume @vol is dynamic, and changes MTD device size if the + * volume is static. This is needed because static volumes cannot be read past + * data they contain. + */ +void ubi_gluebi_updated(struct ubi_volume *vol) +{ + struct mtd_info *mtd = &vol->gluebi_mtd; + + if (vol->vol_type == UBI_STATIC_VOLUME) + mtd->size = vol->used_bytes; +} diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index 438914d..b0d8f4c 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -125,9 +125,9 @@ static int paranoid_check_all_ff(const struct ubi_device *ubi, int pnum, * o %UBI_IO_BITFLIPS if all the requested data were successfully read, but * correctable bit-flips were detected; this is harmless but may indicate * that this eraseblock may become bad soon (but do not have to); - * o %-EBADMSG if the MTD subsystem reported about data data integrity - * problems, for example it can me an ECC error in case of NAND; this most - * probably means that the data is corrupted; + * o %-EBADMSG if the MTD subsystem reported about data integrity problems, for + * example it can be an ECC error in case of NAND; this most probably means + * that the data is corrupted; * o %-EIO if some I/O error occurred; * o other negative error codes in case of other errors. */ @@ -298,7 +298,7 @@ retry: memset(&ei, 0, sizeof(struct erase_info)); ei.mtd = ubi->mtd; - ei.addr = pnum * ubi->peb_size; + ei.addr = (loff_t)pnum * ubi->peb_size; ei.len = ubi->peb_size; ei.callback = erase_callback; ei.priv = (unsigned long)&wq; @@ -382,7 +382,7 @@ static int torture_peb(const struct ubi_device *ubi, int pnum) void *buf; int err, i, patt_count; - buf = kmalloc(ubi->peb_size, GFP_KERNEL); + buf = vmalloc(ubi->peb_size); if (!buf) return -ENOMEM; @@ -437,7 +437,7 @@ out: * physical eraseblock which means something is wrong with it. */ err = -EIO; - kfree(buf); + vfree(buf); return err; } @@ -557,9 +557,9 @@ static int validate_ec_hdr(const struct ubi_device *ubi, long long ec; int vid_hdr_offset, leb_start; - ec = ubi64_to_cpu(ec_hdr->ec); - vid_hdr_offset = ubi32_to_cpu(ec_hdr->vid_hdr_offset); - leb_start = ubi32_to_cpu(ec_hdr->data_offset); + ec = be64_to_cpu(ec_hdr->ec); + vid_hdr_offset = be32_to_cpu(ec_hdr->vid_hdr_offset); + leb_start = be32_to_cpu(ec_hdr->data_offset); if (ec_hdr->version != UBI_VERSION) { ubi_err("node with incompatible UBI version found: " @@ -640,7 +640,7 @@ int ubi_io_read_ec_hdr(const struct ubi_device *ubi, int pnum, read_err = err; } - magic = ubi32_to_cpu(ec_hdr->magic); + magic = be32_to_cpu(ec_hdr->magic); if (magic != UBI_EC_HDR_MAGIC) { /* * The magic field is wrong. Let's check if we have read all @@ -684,7 +684,7 @@ int ubi_io_read_ec_hdr(const struct ubi_device *ubi, int pnum, } crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC); - hdr_crc = ubi32_to_cpu(ec_hdr->hdr_crc); + hdr_crc = be32_to_cpu(ec_hdr->hdr_crc); if (hdr_crc != crc) { if (verbose) { @@ -729,12 +729,12 @@ int ubi_io_write_ec_hdr(const struct ubi_device *ubi, int pnum, dbg_io("write EC header to PEB %d", pnum); ubi_assert(pnum >= 0 && pnum < ubi->peb_count); - ec_hdr->magic = cpu_to_ubi32(UBI_EC_HDR_MAGIC); + ec_hdr->magic = cpu_to_be32(UBI_EC_HDR_MAGIC); ec_hdr->version = UBI_VERSION; - ec_hdr->vid_hdr_offset = cpu_to_ubi32(ubi->vid_hdr_offset); - ec_hdr->data_offset = cpu_to_ubi32(ubi->leb_start); + ec_hdr->vid_hdr_offset = cpu_to_be32(ubi->vid_hdr_offset); + ec_hdr->data_offset = cpu_to_be32(ubi->leb_start); crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC); - ec_hdr->hdr_crc = cpu_to_ubi32(crc); + ec_hdr->hdr_crc = cpu_to_be32(crc); err = paranoid_check_ec_hdr(ubi, pnum, ec_hdr); if (err) @@ -757,13 +757,13 @@ static int validate_vid_hdr(const struct ubi_device *ubi, { int vol_type = vid_hdr->vol_type; int copy_flag = vid_hdr->copy_flag; - int vol_id = ubi32_to_cpu(vid_hdr->vol_id); - int lnum = ubi32_to_cpu(vid_hdr->lnum); + int vol_id = be32_to_cpu(vid_hdr->vol_id); + int lnum = be32_to_cpu(vid_hdr->lnum); int compat = vid_hdr->compat; - int data_size = ubi32_to_cpu(vid_hdr->data_size); - int used_ebs = ubi32_to_cpu(vid_hdr->used_ebs); - int data_pad = ubi32_to_cpu(vid_hdr->data_pad); - int data_crc = ubi32_to_cpu(vid_hdr->data_crc); + int data_size = be32_to_cpu(vid_hdr->data_size); + int used_ebs = be32_to_cpu(vid_hdr->used_ebs); + int data_pad = be32_to_cpu(vid_hdr->data_pad); + int data_crc = be32_to_cpu(vid_hdr->data_crc); int usable_leb_size = ubi->leb_size - data_pad; if (copy_flag != 0 && copy_flag != 1) { @@ -914,7 +914,7 @@ int ubi_io_read_vid_hdr(const struct ubi_device *ubi, int pnum, read_err = err; } - magic = ubi32_to_cpu(vid_hdr->magic); + magic = be32_to_cpu(vid_hdr->magic); if (magic != UBI_VID_HDR_MAGIC) { /* * If we have read all 0xFF bytes, the VID header probably does @@ -957,7 +957,7 @@ int ubi_io_read_vid_hdr(const struct ubi_device *ubi, int pnum, } crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_VID_HDR_SIZE_CRC); - hdr_crc = ubi32_to_cpu(vid_hdr->hdr_crc); + hdr_crc = be32_to_cpu(vid_hdr->hdr_crc); if (hdr_crc != crc) { if (verbose) { @@ -1007,10 +1007,10 @@ int ubi_io_write_vid_hdr(const struct ubi_device *ubi, int pnum, if (err) return err > 0 ? -EINVAL: err; - vid_hdr->magic = cpu_to_ubi32(UBI_VID_HDR_MAGIC); + vid_hdr->magic = cpu_to_be32(UBI_VID_HDR_MAGIC); vid_hdr->version = UBI_VERSION; crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_VID_HDR_SIZE_CRC); - vid_hdr->hdr_crc = cpu_to_ubi32(crc); + vid_hdr->hdr_crc = cpu_to_be32(crc); err = paranoid_check_vid_hdr(ubi, pnum, vid_hdr); if (err) @@ -1060,7 +1060,7 @@ static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum, int err; uint32_t magic; - magic = ubi32_to_cpu(ec_hdr->magic); + magic = be32_to_cpu(ec_hdr->magic); if (magic != UBI_EC_HDR_MAGIC) { ubi_err("bad magic %#08x, must be %#08x", magic, UBI_EC_HDR_MAGIC); @@ -1105,7 +1105,7 @@ static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum) goto exit; crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC); - hdr_crc = ubi32_to_cpu(ec_hdr->hdr_crc); + hdr_crc = be32_to_cpu(ec_hdr->hdr_crc); if (hdr_crc != crc) { ubi_err("bad CRC, calculated %#08x, read %#08x", crc, hdr_crc); ubi_err("paranoid check failed for PEB %d", pnum); @@ -1137,7 +1137,7 @@ static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum, int err; uint32_t magic; - magic = ubi32_to_cpu(vid_hdr->magic); + magic = be32_to_cpu(vid_hdr->magic); if (magic != UBI_VID_HDR_MAGIC) { ubi_err("bad VID header magic %#08x at PEB %d, must be %#08x", magic, pnum, UBI_VID_HDR_MAGIC); @@ -1187,7 +1187,7 @@ static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum) goto exit; crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_EC_HDR_SIZE_CRC); - hdr_crc = ubi32_to_cpu(vid_hdr->hdr_crc); + hdr_crc = be32_to_cpu(vid_hdr->hdr_crc); if (hdr_crc != crc) { ubi_err("bad VID header CRC at PEB %d, calculated %#08x, " "read %#08x", pnum, crc, hdr_crc); @@ -1224,9 +1224,10 @@ static int paranoid_check_all_ff(const struct ubi_device *ubi, int pnum, void *buf; loff_t addr = (loff_t)pnum * ubi->peb_size + offset; - buf = kzalloc(len, GFP_KERNEL); + buf = vmalloc(len); if (!buf) return -ENOMEM; + memset(buf, 0, len); err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf); if (err && err != -EUCLEAN) { @@ -1242,7 +1243,7 @@ static int paranoid_check_all_ff(const struct ubi_device *ubi, int pnum, goto fail; } - kfree(buf); + vfree(buf); return 0; fail: @@ -1252,7 +1253,7 @@ fail: err = 1; error: ubi_dbg_dump_stack(); - kfree(buf); + vfree(buf); return err; } diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index d352c45..4a458e8 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -37,14 +37,9 @@ int ubi_get_device_info(int ubi_num, struct ubi_device_info *di) { const struct ubi_device *ubi; - if (!try_module_get(THIS_MODULE)) - return -ENODEV; - if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES || - !ubi_devices[ubi_num]) { - module_put(THIS_MODULE); + !ubi_devices[ubi_num]) return -ENODEV; - } ubi = ubi_devices[ubi_num]; di->ubi_num = ubi->ubi_num; @@ -52,7 +47,6 @@ int ubi_get_device_info(int ubi_num, struct ubi_device_info *di) di->min_io_size = ubi->min_io_size; di->ro_mode = ubi->ro_mode; di->cdev = MKDEV(ubi->major, 0); - module_put(THIS_MODULE); return 0; } EXPORT_SYMBOL_GPL(ubi_get_device_info); @@ -319,9 +313,14 @@ int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset, offset + len > vol->usable_leb_size) return -EINVAL; - if (vol->vol_type == UBI_STATIC_VOLUME && lnum == vol->used_ebs - 1 && - offset + len > vol->last_eb_bytes) - return -EINVAL; + if (vol->vol_type == UBI_STATIC_VOLUME) { + if (vol->used_ebs == 0) + /* Empty static UBI volume */ + return 0; + if (lnum == vol->used_ebs - 1 && + offset + len > vol->last_eb_bytes) + return -EINVAL; + } if (vol->upd_marker) return -EBADF; diff --git a/drivers/mtd/ubi/misc.c b/drivers/mtd/ubi/misc.c index 38d4e67..9e2338c 100644 --- a/drivers/mtd/ubi/misc.c +++ b/drivers/mtd/ubi/misc.c @@ -67,7 +67,7 @@ int ubi_check_volume(struct ubi_device *ubi, int vol_id) if (vol->vol_type != UBI_STATIC_VOLUME) return 0; - buf = kmalloc(vol->usable_leb_size, GFP_KERNEL); + buf = vmalloc(vol->usable_leb_size); if (!buf) return -ENOMEM; @@ -87,7 +87,7 @@ int ubi_check_volume(struct ubi_device *ubi, int vol_id) } } - kfree(buf); + vfree(buf); return err; } diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index 473f320..94ee549 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -24,7 +24,7 @@ * This unit is responsible for scanning the flash media, checking UBI * headers and providing complete information about the UBI flash image. * - * The scanning information is reoresented by a &struct ubi_scan_info' object. + * The scanning information is represented by a &struct ubi_scan_info' object. * Information about found volumes is represented by &struct ubi_scan_volume * objects which are kept in volume RB-tree with root at the @volumes field. * The RB-tree is indexed by the volume ID. @@ -55,8 +55,19 @@ static int paranoid_check_si(const struct ubi_device *ubi, static struct ubi_ec_hdr *ech; static struct ubi_vid_hdr *vidh; -int ubi_scan_add_to_list(struct ubi_scan_info *si, int pnum, int ec, - struct list_head *list) +/** + * add_to_list - add physical eraseblock to a list. + * @si: scanning information + * @pnum: physical eraseblock number to add + * @ec: erase counter of the physical eraseblock + * @list: the list to add to + * + * This function adds physical eraseblock @pnum to free, erase, corrupted or + * alien lists. Returns zero in case of success and a negative error code in + * case of failure. + */ +static int add_to_list(struct ubi_scan_info *si, int pnum, int ec, + struct list_head *list) { struct ubi_scan_leb *seb; @@ -121,9 +132,9 @@ static int validate_vid_hdr(const struct ubi_vid_hdr *vid_hdr, const struct ubi_scan_volume *sv, int pnum) { int vol_type = vid_hdr->vol_type; - int vol_id = ubi32_to_cpu(vid_hdr->vol_id); - int used_ebs = ubi32_to_cpu(vid_hdr->used_ebs); - int data_pad = ubi32_to_cpu(vid_hdr->data_pad); + int vol_id = be32_to_cpu(vid_hdr->vol_id); + int used_ebs = be32_to_cpu(vid_hdr->used_ebs); + int data_pad = be32_to_cpu(vid_hdr->data_pad); if (sv->leb_count != 0) { int sv_vol_type; @@ -189,7 +200,7 @@ static struct ubi_scan_volume *add_volume(struct ubi_scan_info *si, int vol_id, struct ubi_scan_volume *sv; struct rb_node **p = &si->volumes.rb_node, *parent = NULL; - ubi_assert(vol_id == ubi32_to_cpu(vid_hdr->vol_id)); + ubi_assert(vol_id == be32_to_cpu(vid_hdr->vol_id)); /* Walk the volume RB-tree to look if this volume is already present */ while (*p) { @@ -211,11 +222,10 @@ static struct ubi_scan_volume *add_volume(struct ubi_scan_info *si, int vol_id, return ERR_PTR(-ENOMEM); sv->highest_lnum = sv->leb_count = 0; - si->max_sqnum = 0; sv->vol_id = vol_id; sv->root = RB_ROOT; - sv->used_ebs = ubi32_to_cpu(vid_hdr->used_ebs); - sv->data_pad = ubi32_to_cpu(vid_hdr->data_pad); + sv->used_ebs = be32_to_cpu(vid_hdr->used_ebs); + sv->data_pad = be32_to_cpu(vid_hdr->data_pad); sv->compat = vid_hdr->compat; sv->vol_type = vid_hdr->vol_type == UBI_VID_DYNAMIC ? UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME; @@ -257,10 +267,10 @@ static int compare_lebs(const struct ubi_device *ubi, int len, err, second_is_newer, bitflips = 0, corrupted = 0; uint32_t data_crc, crc; struct ubi_vid_hdr *vidh = NULL; - unsigned long long sqnum2 = ubi64_to_cpu(vid_hdr->sqnum); + unsigned long long sqnum2 = be64_to_cpu(vid_hdr->sqnum); if (seb->sqnum == 0 && sqnum2 == 0) { - long long abs, v1 = seb->leb_ver, v2 = ubi32_to_cpu(vid_hdr->leb_ver); + long long abs, v1 = seb->leb_ver, v2 = be32_to_cpu(vid_hdr->leb_ver); /* * UBI constantly increases the logical eraseblock version @@ -344,8 +354,8 @@ static int compare_lebs(const struct ubi_device *ubi, /* Read the data of the copy and check the CRC */ - len = ubi32_to_cpu(vid_hdr->data_size); - buf = kmalloc(len, GFP_KERNEL); + len = be32_to_cpu(vid_hdr->data_size); + buf = vmalloc(len); if (!buf) { err = -ENOMEM; goto out_free_vidh; @@ -355,7 +365,7 @@ static int compare_lebs(const struct ubi_device *ubi, if (err && err != UBI_IO_BITFLIPS) goto out_free_buf; - data_crc = ubi32_to_cpu(vid_hdr->data_crc); + data_crc = be32_to_cpu(vid_hdr->data_crc); crc = crc32(UBI_CRC32_INIT, buf, len); if (crc != data_crc) { dbg_bld("PEB %d CRC error: calculated %#08x, must be %#08x", @@ -368,7 +378,7 @@ static int compare_lebs(const struct ubi_device *ubi, bitflips = !!err; } - kfree(buf); + vfree(buf); ubi_free_vid_hdr(ubi, vidh); if (second_is_newer) @@ -379,7 +389,7 @@ static int compare_lebs(const struct ubi_device *ubi, return second_is_newer | (bitflips << 1) | (corrupted << 2); out_free_buf: - kfree(buf); + vfree(buf); out_free_vidh: ubi_free_vid_hdr(ubi, vidh); ubi_assert(err < 0); @@ -396,8 +406,12 @@ out_free_vidh: * @vid_hdr: the volume identifier header * @bitflips: if bit-flips were detected when this physical eraseblock was read * - * This function returns zero in case of success and a negative error code in - * case of failure. + * This function adds information about a used physical eraseblock to the + * 'used' tree of the corresponding volume. The function is rather complex + * because it has to handle cases when this is not the first physical + * eraseblock belonging to the same logical eraseblock, and the newer one has + * to be picked, while the older one has to be dropped. This function returns + * zero in case of success and a negative error code in case of failure. */ int ubi_scan_add_used(const struct ubi_device *ubi, struct ubi_scan_info *si, int pnum, int ec, const struct ubi_vid_hdr *vid_hdr, @@ -410,10 +424,10 @@ int ubi_scan_add_used(const struct ubi_device *ubi, struct ubi_scan_info *si, struct ubi_scan_leb *seb; struct rb_node **p, *parent = NULL; - vol_id = ubi32_to_cpu(vid_hdr->vol_id); - lnum = ubi32_to_cpu(vid_hdr->lnum); - sqnum = ubi64_to_cpu(vid_hdr->sqnum); - leb_ver = ubi32_to_cpu(vid_hdr->leb_ver); + vol_id = be32_to_cpu(vid_hdr->vol_id); + lnum = be32_to_cpu(vid_hdr->lnum); + sqnum = be64_to_cpu(vid_hdr->sqnum); + leb_ver = be32_to_cpu(vid_hdr->leb_ver); dbg_bld("PEB %d, LEB %d:%d, EC %d, sqnum %llu, ver %u, bitflips %d", pnum, vol_id, lnum, ec, sqnum, leb_ver, bitflips); @@ -422,6 +436,9 @@ int ubi_scan_add_used(const struct ubi_device *ubi, struct ubi_scan_info *si, if (IS_ERR(sv) < 0) return PTR_ERR(sv); + if (si->max_sqnum < sqnum) + si->max_sqnum = sqnum; + /* * Walk the RB-tree of logical eraseblocks of volume @vol_id to look * if this is the first instance of this logical eraseblock or not. @@ -492,11 +509,11 @@ int ubi_scan_add_used(const struct ubi_device *ubi, struct ubi_scan_info *si, return err; if (cmp_res & 4) - err = ubi_scan_add_to_list(si, seb->pnum, - seb->ec, &si->corr); + err = add_to_list(si, seb->pnum, seb->ec, + &si->corr); else - err = ubi_scan_add_to_list(si, seb->pnum, - seb->ec, &si->erase); + err = add_to_list(si, seb->pnum, seb->ec, + &si->erase); if (err) return err; @@ -508,7 +525,7 @@ int ubi_scan_add_used(const struct ubi_device *ubi, struct ubi_scan_info *si, if (sv->highest_lnum == lnum) sv->last_data_size = - ubi32_to_cpu(vid_hdr->data_size); + be32_to_cpu(vid_hdr->data_size); return 0; } else { @@ -517,11 +534,9 @@ int ubi_scan_add_used(const struct ubi_device *ubi, struct ubi_scan_info *si, * previously. */ if (cmp_res & 4) - return ubi_scan_add_to_list(si, pnum, ec, - &si->corr); + return add_to_list(si, pnum, ec, &si->corr); else - return ubi_scan_add_to_list(si, pnum, ec, - &si->erase); + return add_to_list(si, pnum, ec, &si->erase); } } @@ -547,12 +562,9 @@ int ubi_scan_add_used(const struct ubi_device *ubi, struct ubi_scan_info *si, if (sv->highest_lnum <= lnum) { sv->highest_lnum = lnum; - sv->last_data_size = ubi32_to_cpu(vid_hdr->data_size); + sv->last_data_size = be32_to_cpu(vid_hdr->data_size); } - if (si->max_sqnum < sqnum) - si->max_sqnum = sqnum; - sv->leb_count += 1; rb_link_node(&seb->u.rb, parent, p); rb_insert_color(&seb->u.rb, &sv->root); @@ -674,7 +686,7 @@ int ubi_scan_erase_peb(const struct ubi_device *ubi, return -EINVAL; } - ec_hdr->ec = cpu_to_ubi64(ec); + ec_hdr->ec = cpu_to_be64(ec); err = ubi_io_sync_erase(ubi, pnum, 0); if (err < 0) @@ -754,7 +766,7 @@ struct ubi_scan_leb *ubi_scan_get_free_peb(const struct ubi_device *ubi, * @si: scanning information * @pnum: the physical eraseblock number * - * This function returns a zero if the physical eraseblock was succesfully + * This function returns a zero if the physical eraseblock was successfully * handled and a negative error code in case of failure. */ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum) @@ -783,8 +795,7 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum else if (err == UBI_IO_BITFLIPS) bitflips = 1; else if (err == UBI_IO_PEB_EMPTY) - return ubi_scan_add_to_list(si, pnum, UBI_SCAN_UNKNOWN_EC, - &si->erase); + return add_to_list(si, pnum, UBI_SCAN_UNKNOWN_EC, &si->erase); else if (err == UBI_IO_BAD_EC_HDR) { /* * We have to also look at the VID header, possibly it is not @@ -806,7 +817,7 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum return -EINVAL; } - ec = ubi64_to_cpu(ech->ec); + ec = be64_to_cpu(ech->ec); if (ec > UBI_MAX_ERASECOUNTER) { /* * Erase counter overflow. The EC headers have 64 bits @@ -832,28 +843,28 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum else if (err == UBI_IO_BAD_VID_HDR || (err == UBI_IO_PEB_FREE && ec_corr)) { /* VID header is corrupted */ - err = ubi_scan_add_to_list(si, pnum, ec, &si->corr); + err = add_to_list(si, pnum, ec, &si->corr); if (err) return err; goto adjust_mean_ec; } else if (err == UBI_IO_PEB_FREE) { /* No VID header - the physical eraseblock is free */ - err = ubi_scan_add_to_list(si, pnum, ec, &si->free); + err = add_to_list(si, pnum, ec, &si->free); if (err) return err; goto adjust_mean_ec; } - vol_id = ubi32_to_cpu(vidh->vol_id); + vol_id = be32_to_cpu(vidh->vol_id); if (vol_id > UBI_MAX_VOLUMES && vol_id != UBI_LAYOUT_VOL_ID) { - int lnum = ubi32_to_cpu(vidh->lnum); + int lnum = be32_to_cpu(vidh->lnum); /* Unsupported internal volume */ switch (vidh->compat) { case UBI_COMPAT_DELETE: ubi_msg("\"delete\" compatible internal volume %d:%d" " found, remove it", vol_id, lnum); - err = ubi_scan_add_to_list(si, pnum, ec, &si->corr); + err = add_to_list(si, pnum, ec, &si->corr); if (err) return err; break; @@ -868,7 +879,7 @@ static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum case UBI_COMPAT_PRESERVE: ubi_msg("\"preserve\" compatible internal volume %d:%d" " found", vol_id, lnum); - err = ubi_scan_add_to_list(si, pnum, ec, &si->alien); + err = add_to_list(si, pnum, ec, &si->alien); if (err) return err; si->alien_peb_count += 1; @@ -1109,7 +1120,7 @@ static int paranoid_check_si(const struct ubi_device *ubi, uint8_t *buf; /* - * At first, check that scanning information is ok. + * At first, check that scanning information is OK. */ ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) { int leb_count = 0; @@ -1249,12 +1260,12 @@ static int paranoid_check_si(const struct ubi_device *ubi, goto bad_vid_hdr; } - if (seb->sqnum != ubi64_to_cpu(vidh->sqnum)) { + if (seb->sqnum != be64_to_cpu(vidh->sqnum)) { ubi_err("bad sqnum %llu", seb->sqnum); goto bad_vid_hdr; } - if (sv->vol_id != ubi32_to_cpu(vidh->vol_id)) { + if (sv->vol_id != be32_to_cpu(vidh->vol_id)) { ubi_err("bad vol_id %d", sv->vol_id); goto bad_vid_hdr; } @@ -1264,22 +1275,22 @@ static int paranoid_check_si(const struct ubi_device *ubi, goto bad_vid_hdr; } - if (seb->lnum != ubi32_to_cpu(vidh->lnum)) { + if (seb->lnum != be32_to_cpu(vidh->lnum)) { ubi_err("bad lnum %d", seb->lnum); goto bad_vid_hdr; } - if (sv->used_ebs != ubi32_to_cpu(vidh->used_ebs)) { + if (sv->used_ebs != be32_to_cpu(vidh->used_ebs)) { ubi_err("bad used_ebs %d", sv->used_ebs); goto bad_vid_hdr; } - if (sv->data_pad != ubi32_to_cpu(vidh->data_pad)) { + if (sv->data_pad != be32_to_cpu(vidh->data_pad)) { ubi_err("bad data_pad %d", sv->data_pad); goto bad_vid_hdr; } - if (seb->leb_ver != ubi32_to_cpu(vidh->leb_ver)) { + if (seb->leb_ver != be32_to_cpu(vidh->leb_ver)) { ubi_err("bad leb_ver %u", seb->leb_ver); goto bad_vid_hdr; } @@ -1288,12 +1299,12 @@ static int paranoid_check_si(const struct ubi_device *ubi, if (!last_seb) continue; - if (sv->highest_lnum != ubi32_to_cpu(vidh->lnum)) { + if (sv->highest_lnum != be32_to_cpu(vidh->lnum)) { ubi_err("bad highest_lnum %d", sv->highest_lnum); goto bad_vid_hdr; } - if (sv->last_data_size != ubi32_to_cpu(vidh->data_size)) { + if (sv->last_data_size != be32_to_cpu(vidh->data_size)) { ubi_err("bad last_data_size %d", sv->last_data_size); goto bad_vid_hdr; } @@ -1310,8 +1321,10 @@ static int paranoid_check_si(const struct ubi_device *ubi, memset(buf, 1, ubi->peb_count); for (pnum = 0; pnum < ubi->peb_count; pnum++) { err = ubi_io_is_bad(ubi, pnum); - if (err < 0) + if (err < 0) { + kfree(buf); return err; + } else if (err) buf[pnum] = 0; } diff --git a/drivers/mtd/ubi/scan.h b/drivers/mtd/ubi/scan.h index 3949f61..140e82e 100644 --- a/drivers/mtd/ubi/scan.h +++ b/drivers/mtd/ubi/scan.h @@ -147,8 +147,6 @@ static inline void ubi_scan_move_to_list(struct ubi_scan_volume *sv, list_add_tail(&seb->u.list, list); } -int ubi_scan_add_to_list(struct ubi_scan_info *si, int pnum, int ec, - struct list_head *list); int ubi_scan_add_used(const struct ubi_device *ubi, struct ubi_scan_info *si, int pnum, int ec, const struct ubi_vid_hdr *vid_hdr, int bitflips); diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h index feb647f..5959f91 100644 --- a/drivers/mtd/ubi/ubi.h +++ b/drivers/mtd/ubi/ubi.h @@ -35,6 +35,7 @@ #include <linux/cdev.h> #include <linux/device.h> #include <linux/string.h> +#include <linux/vmalloc.h> #include <linux/mtd/mtd.h> #include <mtd/ubi-header.h> @@ -374,9 +375,11 @@ void ubi_calculate_reserved(struct ubi_device *ubi); #ifdef CONFIG_MTD_UBI_GLUEBI int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol); int ubi_destroy_gluebi(struct ubi_volume *vol); +void ubi_gluebi_updated(struct ubi_volume *vol); #else #define ubi_create_gluebi(ubi, vol) 0 #define ubi_destroy_gluebi(vol) 0 +#define ubi_gluebi_updated(vol) #endif /* eba.c */ diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c index 8925b97..0efc586 100644 --- a/drivers/mtd/ubi/upd.c +++ b/drivers/mtd/ubi/upd.c @@ -150,7 +150,7 @@ int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes) vol->updating = 0; } - vol->upd_buf = kmalloc(ubi->leb_size, GFP_KERNEL); + vol->upd_buf = vmalloc(ubi->leb_size); if (!vol->upd_buf) return -ENOMEM; @@ -339,7 +339,7 @@ int ubi_more_update_data(struct ubi_device *ubi, int vol_id, err = ubi_wl_flush(ubi); if (err == 0) { err = to_write; - kfree(vol->upd_buf); + vfree(vol->upd_buf); vol->updating = 0; } } diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c index 622d0d1..ea0d5c8 100644 --- a/drivers/mtd/ubi/vmt.c +++ b/drivers/mtd/ubi/vmt.c @@ -228,7 +228,7 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) for (i = 0; i < ubi->vtbl_slots; i++) if (ubi->volumes[i] && ubi->volumes[i]->name_len == req->name_len && - strcmp(ubi->volumes[i]->name, req->name) == 0) { + !strcmp(ubi->volumes[i]->name, req->name)) { dbg_err("volume \"%s\" exists (ID %d)", req->name, i); goto out_unlock; } @@ -243,7 +243,6 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) /* Reserve physical eraseblocks */ if (vol->reserved_pebs > ubi->avail_pebs) { dbg_err("not enough PEBs, only %d available", ubi->avail_pebs); - spin_unlock(&ubi->volumes_lock); err = -ENOSPC; goto out_unlock; } @@ -281,7 +280,8 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) if (vol->vol_type == UBI_DYNAMIC_VOLUME) { vol->used_ebs = vol->reserved_pebs; vol->last_eb_bytes = vol->usable_leb_size; - vol->used_bytes = vol->used_ebs * vol->usable_leb_size; + vol->used_bytes = + (long long)vol->used_ebs * vol->usable_leb_size; } else { bytes = vol->used_bytes; vol->last_eb_bytes = do_div(bytes, vol->usable_leb_size); @@ -320,10 +320,10 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) /* Fill volume table record */ memset(&vtbl_rec, 0, sizeof(struct ubi_vtbl_record)); - vtbl_rec.reserved_pebs = cpu_to_ubi32(vol->reserved_pebs); - vtbl_rec.alignment = cpu_to_ubi32(vol->alignment); - vtbl_rec.data_pad = cpu_to_ubi32(vol->data_pad); - vtbl_rec.name_len = cpu_to_ubi16(vol->name_len); + vtbl_rec.reserved_pebs = cpu_to_be32(vol->reserved_pebs); + vtbl_rec.alignment = cpu_to_be32(vol->alignment); + vtbl_rec.data_pad = cpu_to_be32(vol->data_pad); + vtbl_rec.name_len = cpu_to_be16(vol->name_len); if (vol->vol_type == UBI_DYNAMIC_VOLUME) vtbl_rec.vol_type = UBI_VID_DYNAMIC; else @@ -352,6 +352,7 @@ out_acc: spin_lock(&ubi->volumes_lock); ubi->rsvd_pebs -= vol->reserved_pebs; ubi->avail_pebs += vol->reserved_pebs; + ubi->volumes[vol_id] = NULL; out_unlock: spin_unlock(&ubi->volumes_lock); kfree(vol); @@ -368,6 +369,7 @@ out_sysfs: spin_lock(&ubi->volumes_lock); ubi->rsvd_pebs -= vol->reserved_pebs; ubi->avail_pebs += vol->reserved_pebs; + ubi->volumes[vol_id] = NULL; spin_unlock(&ubi->volumes_lock); volume_sysfs_close(vol); return err; @@ -503,7 +505,7 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) /* Change volume table record */ memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record)); - vtbl_rec.reserved_pebs = cpu_to_ubi32(reserved_pebs); + vtbl_rec.reserved_pebs = cpu_to_be32(reserved_pebs); err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); if (err) goto out_acc; @@ -537,7 +539,8 @@ int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) if (vol->vol_type == UBI_DYNAMIC_VOLUME) { vol->used_ebs = reserved_pebs; vol->last_eb_bytes = vol->usable_leb_size; - vol->used_bytes = vol->used_ebs * vol->usable_leb_size; + vol->used_bytes = + (long long)vol->used_ebs * vol->usable_leb_size; } paranoid_check_volumes(ubi); @@ -643,21 +646,33 @@ void ubi_free_volume(struct ubi_device *ubi, int vol_id) * @ubi: UBI device description object * @vol_id: volume ID */ -static void paranoid_check_volume(const struct ubi_device *ubi, int vol_id) +static void paranoid_check_volume(struct ubi_device *ubi, int vol_id) { int idx = vol_id2idx(ubi, vol_id); int reserved_pebs, alignment, data_pad, vol_type, name_len, upd_marker; - const struct ubi_volume *vol = ubi->volumes[idx]; + const struct ubi_volume *vol; long long n; const char *name; - reserved_pebs = ubi32_to_cpu(ubi->vtbl[vol_id].reserved_pebs); + spin_lock(&ubi->volumes_lock); + reserved_pebs = be32_to_cpu(ubi->vtbl[vol_id].reserved_pebs); + vol = ubi->volumes[idx]; if (!vol) { if (reserved_pebs) { ubi_err("no volume info, but volume exists"); goto fail; } + spin_unlock(&ubi->volumes_lock); + return; + } + + if (vol->exclusive) { + /* + * The volume may be being created at the moment, do not check + * it (e.g., it may be in the middle of ubi_create_volume(). + */ + spin_unlock(&ubi->volumes_lock); return; } @@ -726,7 +741,7 @@ static void paranoid_check_volume(const struct ubi_device *ubi, int vol_id) goto fail; } - n = vol->used_ebs * vol->usable_leb_size; + n = (long long)vol->used_ebs * vol->usable_leb_size; if (vol->vol_type == UBI_DYNAMIC_VOLUME) { if (vol->corrupted != 0) { ubi_err("corrupted dynamic volume"); @@ -765,9 +780,9 @@ static void paranoid_check_volume(const struct ubi_device *ubi, int vol_id) } } - alignment = ubi32_to_cpu(ubi->vtbl[vol_id].alignment); - data_pad = ubi32_to_cpu(ubi->vtbl[vol_id].data_pad); - name_len = ubi16_to_cpu(ubi->vtbl[vol_id].name_len); + alignment = be32_to_cpu(ubi->vtbl[vol_id].alignment); + data_pad = be32_to_cpu(ubi->vtbl[vol_id].data_pad); + name_len = be16_to_cpu(ubi->vtbl[vol_id].name_len); upd_marker = ubi->vtbl[vol_id].upd_marker; name = &ubi->vtbl[vol_id].name[0]; if (ubi->vtbl[vol_id].vol_type == UBI_VID_DYNAMIC) @@ -782,12 +797,14 @@ static void paranoid_check_volume(const struct ubi_device *ubi, int vol_id) goto fail; } + spin_unlock(&ubi->volumes_lock); return; fail: - ubi_err("paranoid check failed"); + ubi_err("paranoid check failed for volume %d", vol_id); ubi_dbg_dump_vol_info(vol); ubi_dbg_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id); + spin_unlock(&ubi->volumes_lock); BUG(); } @@ -800,10 +817,8 @@ static void paranoid_check_volumes(struct ubi_device *ubi) int i; mutex_lock(&ubi->vtbl_mutex); - spin_lock(&ubi->volumes_lock); for (i = 0; i < ubi->vtbl_slots; i++) paranoid_check_volume(ubi, i); - spin_unlock(&ubi->volumes_lock); mutex_unlock(&ubi->vtbl_mutex); } #endif diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c index b6fd6bb..bc5df50 100644 --- a/drivers/mtd/ubi/vtbl.c +++ b/drivers/mtd/ubi/vtbl.c @@ -93,12 +93,9 @@ int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, vtbl_rec = &empty_vtbl_record; else { crc = crc32(UBI_CRC32_INIT, vtbl_rec, UBI_VTBL_RECORD_SIZE_CRC); - vtbl_rec->crc = cpu_to_ubi32(crc); + vtbl_rec->crc = cpu_to_be32(crc); } - dbg_msg("change record %d", idx); - ubi_dbg_dump_vtbl_record(vtbl_rec, idx); - mutex_lock(&ubi->vtbl_mutex); memcpy(&ubi->vtbl[idx], vtbl_rec, sizeof(struct ubi_vtbl_record)); for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) { @@ -141,18 +138,18 @@ static int vtbl_check(const struct ubi_device *ubi, for (i = 0; i < ubi->vtbl_slots; i++) { cond_resched(); - reserved_pebs = ubi32_to_cpu(vtbl[i].reserved_pebs); - alignment = ubi32_to_cpu(vtbl[i].alignment); - data_pad = ubi32_to_cpu(vtbl[i].data_pad); + reserved_pebs = be32_to_cpu(vtbl[i].reserved_pebs); + alignment = be32_to_cpu(vtbl[i].alignment); + data_pad = be32_to_cpu(vtbl[i].data_pad); upd_marker = vtbl[i].upd_marker; vol_type = vtbl[i].vol_type; - name_len = ubi16_to_cpu(vtbl[i].name_len); + name_len = be16_to_cpu(vtbl[i].name_len); name = &vtbl[i].name[0]; crc = crc32(UBI_CRC32_INIT, &vtbl[i], UBI_VTBL_RECORD_SIZE_CRC); - if (ubi32_to_cpu(vtbl[i].crc) != crc) { + if (be32_to_cpu(vtbl[i].crc) != crc) { ubi_err("bad CRC at record %u: %#08x, not %#08x", - i, crc, ubi32_to_cpu(vtbl[i].crc)); + i, crc, be32_to_cpu(vtbl[i].crc)); ubi_dbg_dump_vtbl_record(&vtbl[i], i); return 1; } @@ -225,8 +222,8 @@ static int vtbl_check(const struct ubi_device *ubi, /* Checks that all names are unique */ for (i = 0; i < ubi->vtbl_slots - 1; i++) { for (n = i + 1; n < ubi->vtbl_slots; n++) { - int len1 = ubi16_to_cpu(vtbl[i].name_len); - int len2 = ubi16_to_cpu(vtbl[n].name_len); + int len1 = be16_to_cpu(vtbl[i].name_len); + int len2 = be16_to_cpu(vtbl[n].name_len); if (len1 > 0 && len1 == len2 && !strncmp(vtbl[i].name, vtbl[n].name, len1)) { @@ -288,13 +285,13 @@ retry: } vid_hdr->vol_type = UBI_VID_DYNAMIC; - vid_hdr->vol_id = cpu_to_ubi32(UBI_LAYOUT_VOL_ID); + vid_hdr->vol_id = cpu_to_be32(UBI_LAYOUT_VOL_ID); vid_hdr->compat = UBI_LAYOUT_VOLUME_COMPAT; vid_hdr->data_size = vid_hdr->used_ebs = - vid_hdr->data_pad = cpu_to_ubi32(0); - vid_hdr->lnum = cpu_to_ubi32(copy); - vid_hdr->sqnum = cpu_to_ubi64(++si->max_sqnum); - vid_hdr->leb_ver = cpu_to_ubi32(old_seb ? old_seb->leb_ver + 1: 0); + vid_hdr->data_pad = cpu_to_be32(0); + vid_hdr->lnum = cpu_to_be32(copy); + vid_hdr->sqnum = cpu_to_be64(++si->max_sqnum); + vid_hdr->leb_ver = cpu_to_be32(old_seb ? old_seb->leb_ver + 1: 0); /* The EC header is already there, write the VID header */ err = ubi_io_write_vid_hdr(ubi, new_seb->pnum, vid_hdr); @@ -317,14 +314,15 @@ retry: return err; write_error: - kfree(new_seb); - /* May be this physical eraseblock went bad, try to pick another one */ - if (++tries <= 5) { - err = ubi_scan_add_to_list(si, new_seb->pnum, new_seb->ec, - &si->corr); - if (!err) - goto retry; + if (err == -EIO && ++tries <= 5) { + /* + * Probably this physical eraseblock went bad, try to pick + * another one. + */ + list_add_tail(&new_seb->u.list, &si->corr); + goto retry; } + kfree(new_seb); out_free: ubi_free_vid_hdr(ubi, vid_hdr); return err; @@ -380,11 +378,12 @@ static struct ubi_vtbl_record *process_lvol(const struct ubi_device *ubi, /* Read both LEB 0 and LEB 1 into memory */ ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) { - leb[seb->lnum] = kzalloc(ubi->vtbl_size, GFP_KERNEL); + leb[seb->lnum] = vmalloc(ubi->vtbl_size); if (!leb[seb->lnum]) { err = -ENOMEM; goto out_free; } + memset(leb[seb->lnum], 0, ubi->vtbl_size); err = ubi_io_read_data(ubi, leb[seb->lnum], seb->pnum, 0, ubi->vtbl_size); @@ -415,7 +414,7 @@ static struct ubi_vtbl_record *process_lvol(const struct ubi_device *ubi, } /* Both LEB 1 and LEB 2 are OK and consistent */ - kfree(leb[1]); + vfree(leb[1]); return leb[0]; } else { /* LEB 0 is corrupted or does not exist */ @@ -436,13 +435,13 @@ static struct ubi_vtbl_record *process_lvol(const struct ubi_device *ubi, goto out_free; ubi_msg("volume table was restored"); - kfree(leb[0]); + vfree(leb[0]); return leb[1]; } out_free: - kfree(leb[0]); - kfree(leb[1]); + vfree(leb[0]); + vfree(leb[1]); return ERR_PTR(err); } @@ -460,9 +459,10 @@ static struct ubi_vtbl_record *create_empty_lvol(const struct ubi_device *ubi, int i; struct ubi_vtbl_record *vtbl; - vtbl = kzalloc(ubi->vtbl_size, GFP_KERNEL); + vtbl = vmalloc(ubi->vtbl_size); if (!vtbl) return ERR_PTR(-ENOMEM); + memset(vtbl, 0, ubi->vtbl_size); for (i = 0; i < ubi->vtbl_slots; i++) memcpy(&vtbl[i], &empty_vtbl_record, UBI_VTBL_RECORD_SIZE); @@ -472,7 +472,7 @@ static struct ubi_vtbl_record *create_empty_lvol(const struct ubi_device *ubi, err = create_vtbl(ubi, si, i, vtbl); if (err) { - kfree(vtbl); + vfree(vtbl); return ERR_PTR(err); } } @@ -500,19 +500,19 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, for (i = 0; i < ubi->vtbl_slots; i++) { cond_resched(); - if (ubi32_to_cpu(vtbl[i].reserved_pebs) == 0) + if (be32_to_cpu(vtbl[i].reserved_pebs) == 0) continue; /* Empty record */ vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL); if (!vol) return -ENOMEM; - vol->reserved_pebs = ubi32_to_cpu(vtbl[i].reserved_pebs); - vol->alignment = ubi32_to_cpu(vtbl[i].alignment); - vol->data_pad = ubi32_to_cpu(vtbl[i].data_pad); + vol->reserved_pebs = be32_to_cpu(vtbl[i].reserved_pebs); + vol->alignment = be32_to_cpu(vtbl[i].alignment); + vol->data_pad = be32_to_cpu(vtbl[i].data_pad); vol->vol_type = vtbl[i].vol_type == UBI_VID_DYNAMIC ? UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME; - vol->name_len = ubi16_to_cpu(vtbl[i].name_len); + vol->name_len = be16_to_cpu(vtbl[i].name_len); vol->usable_leb_size = ubi->leb_size - vol->data_pad; memcpy(vol->name, vtbl[i].name, vol->name_len); vol->name[vol->name_len] = '\0'; @@ -531,7 +531,8 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, if (vol->vol_type == UBI_DYNAMIC_VOLUME) { vol->used_ebs = vol->reserved_pebs; vol->last_eb_bytes = vol->usable_leb_size; - vol->used_bytes = vol->used_ebs * vol->usable_leb_size; + vol->used_bytes = + (long long)vol->used_ebs * vol->usable_leb_size; continue; } @@ -561,7 +562,8 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, } vol->used_ebs = sv->used_ebs; - vol->used_bytes = (vol->used_ebs - 1) * vol->usable_leb_size; + vol->used_bytes = + (long long)(vol->used_ebs - 1) * vol->usable_leb_size; vol->used_bytes += sv->last_data_size; vol->last_eb_bytes = sv->last_data_size; } @@ -578,7 +580,8 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, vol->usable_leb_size = ubi->leb_size; vol->used_ebs = vol->reserved_pebs; vol->last_eb_bytes = vol->reserved_pebs; - vol->used_bytes = vol->used_ebs * (ubi->leb_size - vol->data_pad); + vol->used_bytes = + (long long)vol->used_ebs * (ubi->leb_size - vol->data_pad); vol->vol_id = UBI_LAYOUT_VOL_ID; ubi_assert(!ubi->volumes[i]); @@ -718,7 +721,7 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si) int i, err; struct ubi_scan_volume *sv; - empty_vtbl_record.crc = cpu_to_ubi32(0xf116c36b); + empty_vtbl_record.crc = cpu_to_be32(0xf116c36b); /* * The number of supported volumes is limited by the eraseblock size @@ -783,7 +786,7 @@ int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si) return 0; out_free: - kfree(ubi->vtbl); + vfree(ubi->vtbl); for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) if (ubi->volumes[i]) { kfree(ubi->volumes[i]); diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index ab2174a..9de9537 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -667,7 +667,7 @@ static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, int tortur dbg_wl("erased PEB %d, new EC %llu", e->pnum, ec); - ec_hdr->ec = cpu_to_ubi64(ec); + ec_hdr->ec = cpu_to_be64(ec); err = ubi_io_write_ec_hdr(ubi, e->pnum, ec_hdr); if (err) @@ -1060,9 +1060,8 @@ out_unlock: static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, int cancel) { - int err; struct ubi_wl_entry *e = wl_wrk->e; - int pnum = e->pnum; + int pnum = e->pnum, err, need; if (cancel) { dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec); @@ -1097,62 +1096,70 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, kfree(wl_wrk); kmem_cache_free(wl_entries_slab, e); - if (err != -EIO) { + if (err == -EINTR || err == -ENOMEM || err == -EAGAIN || + err == -EBUSY) { + int err1; + + /* Re-schedule the LEB for erasure */ + err1 = schedule_erase(ubi, e, 0); + if (err1) { + err = err1; + goto out_ro; + } + return err; + } else if (err != -EIO) { /* * If this is not %-EIO, we have no idea what to do. Scheduling * this physical eraseblock for erasure again would cause * errors again and again. Well, lets switch to RO mode. */ - ubi_ro_mode(ubi); - return err; + goto out_ro; } /* It is %-EIO, the PEB went bad */ if (!ubi->bad_allowed) { ubi_err("bad physical eraseblock %d detected", pnum); - ubi_ro_mode(ubi); - err = -EIO; - } else { - int need; - - spin_lock(&ubi->volumes_lock); - need = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs + 1; - if (need > 0) { - need = ubi->avail_pebs >= need ? need : ubi->avail_pebs; - ubi->avail_pebs -= need; - ubi->rsvd_pebs += need; - ubi->beb_rsvd_pebs += need; - if (need > 0) - ubi_msg("reserve more %d PEBs", need); - } + goto out_ro; + } - if (ubi->beb_rsvd_pebs == 0) { - spin_unlock(&ubi->volumes_lock); - ubi_err("no reserved physical eraseblocks"); - ubi_ro_mode(ubi); - return -EIO; - } + spin_lock(&ubi->volumes_lock); + need = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs + 1; + if (need > 0) { + need = ubi->avail_pebs >= need ? need : ubi->avail_pebs; + ubi->avail_pebs -= need; + ubi->rsvd_pebs += need; + ubi->beb_rsvd_pebs += need; + if (need > 0) + ubi_msg("reserve more %d PEBs", need); + } + if (ubi->beb_rsvd_pebs == 0) { spin_unlock(&ubi->volumes_lock); - ubi_msg("mark PEB %d as bad", pnum); + ubi_err("no reserved physical eraseblocks"); + goto out_ro; + } - err = ubi_io_mark_bad(ubi, pnum); - if (err) { - ubi_ro_mode(ubi); - return err; - } + spin_unlock(&ubi->volumes_lock); + ubi_msg("mark PEB %d as bad", pnum); - spin_lock(&ubi->volumes_lock); - ubi->beb_rsvd_pebs -= 1; - ubi->bad_peb_count += 1; - ubi->good_peb_count -= 1; - ubi_calculate_reserved(ubi); - if (ubi->beb_rsvd_pebs == 0) - ubi_warn("last PEB from the reserved pool was used"); - spin_unlock(&ubi->volumes_lock); - } + err = ubi_io_mark_bad(ubi, pnum); + if (err) + goto out_ro; + + spin_lock(&ubi->volumes_lock); + ubi->beb_rsvd_pebs -= 1; + ubi->bad_peb_count += 1; + ubi->good_peb_count -= 1; + ubi_calculate_reserved(ubi); + if (ubi->beb_rsvd_pebs == 0) + ubi_warn("last PEB from the reserved pool was used"); + spin_unlock(&ubi->volumes_lock); + + return err; +out_ro: + ubi_ro_mode(ubi); return err; } @@ -1634,7 +1641,7 @@ static int paranoid_check_ec(const struct ubi_device *ubi, int pnum, int ec) goto out_free; } - read_ec = ubi64_to_cpu(ec_hdr->ec); + read_ec = be64_to_cpu(ec_hdr->ec); if (ec != read_ec) { ubi_err("paranoid check failed for PEB %d", pnum); ubi_err("read EC is %lld, should be %d", read_ec, ec); diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 43d0317..5fb659f 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2486,6 +2486,18 @@ source "drivers/atm/Kconfig" source "drivers/s390/net/Kconfig" +config XEN_NETDEV_FRONTEND + tristate "Xen network device frontend driver" + depends on XEN + default y + help + The network device frontend driver allows the kernel to + access network devices exported exported by a virtual + machine containing a physical network device driver. The + frontend driver is intended for unprivileged guest domains; + if you are compiling a kernel for a Xen guest, you almost + certainly want to enable this. + config ISERIES_VETH tristate "iSeries Virtual Ethernet driver support" depends on PPC_ISERIES diff --git a/drivers/net/Makefile b/drivers/net/Makefile index eb41676..0e286ab 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -127,6 +127,8 @@ obj-$(CONFIG_PPPOL2TP) += pppox.o pppol2tp.o obj-$(CONFIG_SLIP) += slip.o obj-$(CONFIG_SLHC) += slhc.o +obj-$(CONFIG_XEN_NETDEV_FRONTEND) += xen-netfront.o + obj-$(CONFIG_DUMMY) += dummy.o obj-$(CONFIG_IFB) += ifb.o obj-$(CONFIG_MACVLAN) += macvlan.o diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index d23861c..a729da0 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c @@ -54,8 +54,8 @@ #define DRV_MODULE_NAME "bnx2" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "1.6.2" -#define DRV_MODULE_RELDATE "July 6, 2007" +#define DRV_MODULE_VERSION "1.6.3" +#define DRV_MODULE_RELDATE "July 16, 2007" #define RUN_AT(x) (jiffies + (x)) @@ -126,91 +126,102 @@ static struct pci_device_id bnx2_pci_tbl[] = { static struct flash_spec flash_table[] = { +#define BUFFERED_FLAGS (BNX2_NV_BUFFERED | BNX2_NV_TRANSLATE) +#define NONBUFFERED_FLAGS (BNX2_NV_WREN) /* Slow EEPROM */ {0x00000000, 0x40830380, 0x009f0081, 0xa184a053, 0xaf000400, - 1, SEEPROM_PAGE_BITS, SEEPROM_PAGE_SIZE, + BUFFERED_FLAGS, SEEPROM_PAGE_BITS, SEEPROM_PAGE_SIZE, SEEPROM_BYTE_ADDR_MASK, SEEPROM_TOTAL_SIZE, "EEPROM - slow"}, /* Expansion entry 0001 */ {0x08000002, 0x4b808201, 0x00050081, 0x03840253, 0xaf020406, - 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, + NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, 0, "Entry 0001"}, /* Saifun SA25F010 (non-buffered flash) */ /* strap, cfg1, & write1 need updates */ {0x04000001, 0x47808201, 0x00050081, 0x03840253, 0xaf020406, - 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, + NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE*2, "Non-buffered flash (128kB)"}, /* Saifun SA25F020 (non-buffered flash) */ /* strap, cfg1, & write1 need updates */ {0x0c000003, 0x4f808201, 0x00050081, 0x03840253, 0xaf020406, - 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, + NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE*4, "Non-buffered flash (256kB)"}, /* Expansion entry 0100 */ {0x11000000, 0x53808201, 0x00050081, 0x03840253, 0xaf020406, - 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, + NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, 0, "Entry 0100"}, /* Entry 0101: ST M45PE10 (non-buffered flash, TetonII B0) */ {0x19000002, 0x5b808201, 0x000500db, 0x03840253, 0xaf020406, - 0, ST_MICRO_FLASH_PAGE_BITS, ST_MICRO_FLASH_PAGE_SIZE, + NONBUFFERED_FLAGS, ST_MICRO_FLASH_PAGE_BITS, ST_MICRO_FLASH_PAGE_SIZE, ST_MICRO_FLASH_BYTE_ADDR_MASK, ST_MICRO_FLASH_BASE_TOTAL_SIZE*2, "Entry 0101: ST M45PE10 (128kB non-bufferred)"}, /* Entry 0110: ST M45PE20 (non-buffered flash)*/ {0x15000001, 0x57808201, 0x000500db, 0x03840253, 0xaf020406, - 0, ST_MICRO_FLASH_PAGE_BITS, ST_MICRO_FLASH_PAGE_SIZE, + NONBUFFERED_FLAGS, ST_MICRO_FLASH_PAGE_BITS, ST_MICRO_FLASH_PAGE_SIZE, ST_MICRO_FLASH_BYTE_ADDR_MASK, ST_MICRO_FLASH_BASE_TOTAL_SIZE*4, "Entry 0110: ST M45PE20 (256kB non-bufferred)"}, /* Saifun SA25F005 (non-buffered flash) */ /* strap, cfg1, & write1 need updates */ {0x1d000003, 0x5f808201, 0x00050081, 0x03840253, 0xaf020406, - 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, + NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, SAIFUN_FLASH_BASE_TOTAL_SIZE, "Non-buffered flash (64kB)"}, /* Fast EEPROM */ {0x22000000, 0x62808380, 0x009f0081, 0xa184a053, 0xaf000400, - 1, SEEPROM_PAGE_BITS, SEEPROM_PAGE_SIZE, + BUFFERED_FLAGS, SEEPROM_PAGE_BITS, SEEPROM_PAGE_SIZE, SEEPROM_BYTE_ADDR_MASK, SEEPROM_TOTAL_SIZE, "EEPROM - fast"}, /* Expansion entry 1001 */ {0x2a000002, 0x6b808201, 0x00050081, 0x03840253, 0xaf020406, - 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, + NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, 0, "Entry 1001"}, /* Expansion entry 1010 */ {0x26000001, 0x67808201, 0x00050081, 0x03840253, 0xaf020406, - 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, + NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, 0, "Entry 1010"}, /* ATMEL AT45DB011B (buffered flash) */ {0x2e000003, 0x6e808273, 0x00570081, 0x68848353, 0xaf000400, - 1, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE, + BUFFERED_FLAGS, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE, BUFFERED_FLASH_BYTE_ADDR_MASK, BUFFERED_FLASH_TOTAL_SIZE, "Buffered flash (128kB)"}, /* Expansion entry 1100 */ {0x33000000, 0x73808201, 0x00050081, 0x03840253, 0xaf020406, - 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, + NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, 0, "Entry 1100"}, /* Expansion entry 1101 */ {0x3b000002, 0x7b808201, 0x00050081, 0x03840253, 0xaf020406, - 0, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, + NONBUFFERED_FLAGS, SAIFUN_FLASH_PAGE_BITS, SAIFUN_FLASH_PAGE_SIZE, SAIFUN_FLASH_BYTE_ADDR_MASK, 0, "Entry 1101"}, /* Ateml Expansion entry 1110 */ {0x37000001, 0x76808273, 0x00570081, 0x68848353, 0xaf000400, - 1, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE, + BUFFERED_FLAGS, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE, BUFFERED_FLASH_BYTE_ADDR_MASK, 0, "Entry 1110 (Atmel)"}, /* ATMEL AT45DB021B (buffered flash) */ {0x3f000003, 0x7e808273, 0x00570081, 0x68848353, 0xaf000400, - 1, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE, + BUFFERED_FLAGS, BUFFERED_FLASH_PAGE_BITS, BUFFERED_FLASH_PAGE_SIZE, BUFFERED_FLASH_BYTE_ADDR_MASK, BUFFERED_FLASH_TOTAL_SIZE*2, "Buffered flash (256kB)"}, }; +static struct flash_spec flash_5709 = { + .flags = BNX2_NV_BUFFERED, + .page_bits = BCM5709_FLASH_PAGE_BITS, + .page_size = BCM5709_FLASH_PAGE_SIZE, + .addr_mask = BCM5709_FLASH_BYTE_ADDR_MASK, + .total_size = BUFFERED_FLASH_TOTAL_SIZE*2, + .name = "5709 Buffered flash (256kB)", +}; + MODULE_DEVICE_TABLE(pci, bnx2_pci_tbl); static inline u32 bnx2_tx_avail(struct bnx2 *bp) @@ -3289,7 +3300,7 @@ bnx2_enable_nvram_write(struct bnx2 *bp) val = REG_RD(bp, BNX2_MISC_CFG); REG_WR(bp, BNX2_MISC_CFG, val | BNX2_MISC_CFG_NVM_WR_EN_PCI); - if (!bp->flash_info->buffered) { + if (bp->flash_info->flags & BNX2_NV_WREN) { int j; REG_WR(bp, BNX2_NVM_COMMAND, BNX2_NVM_COMMAND_DONE); @@ -3349,7 +3360,7 @@ bnx2_nvram_erase_page(struct bnx2 *bp, u32 offset) u32 cmd; int j; - if (bp->flash_info->buffered) + if (bp->flash_info->flags & BNX2_NV_BUFFERED) /* Buffered flash, no erase needed */ return 0; @@ -3392,8 +3403,8 @@ bnx2_nvram_read_dword(struct bnx2 *bp, u32 offset, u8 *ret_val, u32 cmd_flags) /* Build the command word. */ cmd = BNX2_NVM_COMMAND_DOIT | cmd_flags; - /* Calculate an offset of a buffered flash. */ - if (bp->flash_info->buffered) { + /* Calculate an offset of a buffered flash, not needed for 5709. */ + if (bp->flash_info->flags & BNX2_NV_TRANSLATE) { offset = ((offset / bp->flash_info->page_size) << bp->flash_info->page_bits) + (offset % bp->flash_info->page_size); @@ -3439,8 +3450,8 @@ bnx2_nvram_write_dword(struct bnx2 *bp, u32 offset, u8 *val, u32 cmd_flags) /* Build the command word. */ cmd = BNX2_NVM_COMMAND_DOIT | BNX2_NVM_COMMAND_WR | cmd_flags; - /* Calculate an offset of a buffered flash. */ - if (bp->flash_info->buffered) { + /* Calculate an offset of a buffered flash, not needed for 5709. */ + if (bp->flash_info->flags & BNX2_NV_TRANSLATE) { offset = ((offset / bp->flash_info->page_size) << bp->flash_info->page_bits) + (offset % bp->flash_info->page_size); @@ -3478,15 +3489,19 @@ static int bnx2_init_nvram(struct bnx2 *bp) { u32 val; - int j, entry_count, rc; + int j, entry_count, rc = 0; struct flash_spec *flash; + if (CHIP_NUM(bp) == CHIP_NUM_5709) { + bp->flash_info = &flash_5709; + goto get_flash_size; + } + /* Determine the selected interface. */ val = REG_RD(bp, BNX2_NVM_CFG1); entry_count = sizeof(flash_table) / sizeof(struct flash_spec); - rc = 0; if (val & 0x40000000) { /* Flash interface has been reconfigured */ @@ -3542,6 +3557,7 @@ bnx2_init_nvram(struct bnx2 *bp) return -ENODEV; } +get_flash_size: val = REG_RD_IND(bp, bp->shmem_base + BNX2_SHARED_HW_CFG_CONFIG2); val &= BNX2_SHARED_HW_CFG2_NVM_SIZE_MASK; if (val) @@ -3706,7 +3722,7 @@ bnx2_nvram_write(struct bnx2 *bp, u32 offset, u8 *data_buf, buf = align_buf; } - if (bp->flash_info->buffered == 0) { + if (!(bp->flash_info->flags & BNX2_NV_BUFFERED)) { flash_buffer = kmalloc(264, GFP_KERNEL); if (flash_buffer == NULL) { rc = -ENOMEM; @@ -3739,7 +3755,7 @@ bnx2_nvram_write(struct bnx2 *bp, u32 offset, u8 *data_buf, bnx2_enable_nvram_access(bp); cmd_flags = BNX2_NVM_COMMAND_FIRST; - if (bp->flash_info->buffered == 0) { + if (!(bp->flash_info->flags & BNX2_NV_BUFFERED)) { int j; /* Read the whole page into the buffer @@ -3767,7 +3783,7 @@ bnx2_nvram_write(struct bnx2 *bp, u32 offset, u8 *data_buf, /* Loop to write back the buffer data from page_start to * data_start */ i = 0; - if (bp->flash_info->buffered == 0) { + if (!(bp->flash_info->flags & BNX2_NV_BUFFERED)) { /* Erase the page */ if ((rc = bnx2_nvram_erase_page(bp, page_start)) != 0) goto nvram_write_end; @@ -3791,7 +3807,7 @@ bnx2_nvram_write(struct bnx2 *bp, u32 offset, u8 *data_buf, /* Loop to write the new data from data_start to data_end */ for (addr = data_start; addr < data_end; addr += 4, i += 4) { if ((addr == page_end - 4) || - ((bp->flash_info->buffered) && + ((bp->flash_info->flags & BNX2_NV_BUFFERED) && (addr == data_end - 4))) { cmd_flags |= BNX2_NVM_COMMAND_LAST; @@ -3808,7 +3824,7 @@ bnx2_nvram_write(struct bnx2 *bp, u32 offset, u8 *data_buf, /* Loop to write back the buffer data from data_end * to page_end */ - if (bp->flash_info->buffered == 0) { + if (!(bp->flash_info->flags & BNX2_NV_BUFFERED)) { for (addr = data_end; addr < page_end; addr += 4, i += 4) { @@ -4107,7 +4123,7 @@ bnx2_init_chip(struct bnx2 *bp) if (CHIP_NUM(bp) == CHIP_NUM_5708) REG_WR(bp, BNX2_HC_STATS_TICKS, 0); else - REG_WR(bp, BNX2_HC_STATS_TICKS, bp->stats_ticks & 0xffff00); + REG_WR(bp, BNX2_HC_STATS_TICKS, bp->stats_ticks); REG_WR(bp, BNX2_HC_STAT_COLLECT_TICKS, 0xbb8); /* 3ms */ if (CHIP_ID(bp) == CHIP_ID_5706_A1) @@ -4127,10 +4143,6 @@ bnx2_init_chip(struct bnx2 *bp) REG_WR(bp, BNX2_HC_ATTN_BITS_ENABLE, STATUS_ATTN_EVENTS); - if (REG_RD_IND(bp, bp->shmem_base + BNX2_PORT_FEATURE) & - BNX2_PORT_FEATURE_ASF_ENABLED) - bp->flags |= ASF_ENABLE_FLAG; - /* Initialize the receive filter. */ bnx2_set_rx_mode(bp->dev); @@ -5786,8 +5798,9 @@ bnx2_set_coalesce(struct net_device *dev, struct ethtool_coalesce *coal) if (bp->stats_ticks != 0 && bp->stats_ticks != USEC_PER_SEC) bp->stats_ticks = USEC_PER_SEC; } - if (bp->stats_ticks > 0xffff00) bp->stats_ticks = 0xffff00; - bp->stats_ticks &= 0xffff00; + if (bp->stats_ticks > BNX2_HC_STATS_TICKS_HC_STAT_TICKS) + bp->stats_ticks = BNX2_HC_STATS_TICKS_HC_STAT_TICKS; + bp->stats_ticks &= BNX2_HC_STATS_TICKS_HC_STAT_TICKS; if (netif_running(bp->dev)) { bnx2_netif_stop(bp); @@ -6629,6 +6642,18 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) if (i != 2) bp->fw_version[j++] = '.'; } + if (REG_RD_IND(bp, bp->shmem_base + BNX2_PORT_FEATURE) & + BNX2_PORT_FEATURE_ASF_ENABLED) { + bp->flags |= ASF_ENABLE_FLAG; + + for (i = 0; i < 30; i++) { + reg = REG_RD_IND(bp, bp->shmem_base + + BNX2_BC_STATE_CONDITION); + if (reg & BNX2_CONDITION_MFW_RUN_MASK) + break; + msleep(10); + } + } reg = REG_RD_IND(bp, bp->shmem_base + BNX2_BC_STATE_CONDITION); reg &= BNX2_CONDITION_MFW_RUN_MASK; if (reg != BNX2_CONDITION_MFW_RUN_UNKNOWN && @@ -6672,7 +6697,7 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) bp->rx_ticks_int = 18; bp->rx_ticks = 18; - bp->stats_ticks = 1000000 & 0xffff00; + bp->stats_ticks = USEC_PER_SEC & BNX2_HC_STATS_TICKS_HC_STAT_TICKS; bp->timer_interval = HZ; bp->current_interval = HZ; diff --git a/drivers/net/bnx2.h b/drivers/net/bnx2.h index d8cd1af..102adfe 100644 --- a/drivers/net/bnx2.h +++ b/drivers/net/bnx2.h @@ -6433,6 +6433,11 @@ struct sw_bd { #define ST_MICRO_FLASH_PAGE_SIZE 256 #define ST_MICRO_FLASH_BASE_TOTAL_SIZE 65536 +#define BCM5709_FLASH_PAGE_BITS 8 +#define BCM5709_FLASH_PHY_PAGE_SIZE (1 << BCM5709_FLASH_PAGE_BITS) +#define BCM5709_FLASH_BYTE_ADDR_MASK (BCM5709_FLASH_PHY_PAGE_SIZE-1) +#define BCM5709_FLASH_PAGE_SIZE 256 + #define NVRAM_TIMEOUT_COUNT 30000 @@ -6449,7 +6454,10 @@ struct flash_spec { u32 config2; u32 config3; u32 write1; - u32 buffered; + u32 flags; +#define BNX2_NV_BUFFERED 0x00000001 +#define BNX2_NV_TRANSLATE 0x00000002 +#define BNX2_NV_WREN 0x00000004 u32 page_bits; u32 page_size; u32 addr_mask; diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c index 84aa211..355c6cf 100644 --- a/drivers/net/hamradio/baycom_epp.c +++ b/drivers/net/hamradio/baycom_epp.c @@ -320,7 +320,7 @@ static int eppconfig(struct baycom_state *bc) sprintf(portarg, "%ld", bc->pdev->port->base); printk(KERN_DEBUG "%s: %s -s -p %s -m %s\n", bc_drvname, eppconfig_path, portarg, modearg); - return call_usermodehelper(eppconfig_path, argv, envp, 1); + return call_usermodehelper(eppconfig_path, argv, envp, UMH_WAIT_PROC); } /* ---------------------------------------------------------------------- */ diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c index 5891a0f..f871760 100644 --- a/drivers/net/pppol2tp.c +++ b/drivers/net/pppol2tp.c @@ -824,6 +824,7 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh struct pppol2tp_session *session; struct pppol2tp_tunnel *tunnel; struct udphdr *uh; + unsigned int len; error = -ENOTCONN; if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) @@ -912,14 +913,15 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh } /* Queue the packet to IP for output */ + len = skb->len; error = ip_queue_xmit(skb, 1); /* Update stats */ if (error >= 0) { tunnel->stats.tx_packets++; - tunnel->stats.tx_bytes += skb->len; + tunnel->stats.tx_bytes += len; session->stats.tx_packets++; - session->stats.tx_bytes += skb->len; + session->stats.tx_bytes += len; } else { tunnel->stats.tx_errors++; session->stats.tx_errors++; @@ -958,6 +960,7 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) __wsum csum = 0; struct sk_buff *skb2 = NULL; struct udphdr *uh; + unsigned int len; if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) goto abort; @@ -1046,18 +1049,25 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) printk("\n"); } + memset(&(IPCB(skb2)->opt), 0, sizeof(IPCB(skb2)->opt)); + IPCB(skb2)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | + IPSKB_REROUTED); + nf_reset(skb2); + /* Get routing info from the tunnel socket */ + dst_release(skb2->dst); skb2->dst = sk_dst_get(sk_tun); /* Queue the packet to IP for output */ + len = skb2->len; rc = ip_queue_xmit(skb2, 1); /* Update stats */ if (rc >= 0) { tunnel->stats.tx_packets++; - tunnel->stats.tx_bytes += skb2->len; + tunnel->stats.tx_bytes += len; session->stats.tx_packets++; - session->stats.tx_bytes += skb2->len; + session->stats.tx_bytes += len; } else { tunnel->stats.tx_errors++; session->stats.tx_errors++; diff --git a/drivers/net/sunvnet.c b/drivers/net/sunvnet.c index 8a667c1..b801e3b 100644 --- a/drivers/net/sunvnet.c +++ b/drivers/net/sunvnet.c @@ -12,6 +12,7 @@ #include <linux/netdevice.h> #include <linux/ethtool.h> #include <linux/etherdevice.h> +#include <linux/mutex.h> #include <asm/vio.h> #include <asm/ldc.h> @@ -497,6 +498,8 @@ static void vnet_event(void *arg, int event) vio_link_state_change(vio, event); spin_unlock_irqrestore(&vio->lock, flags); + if (event == LDC_EVENT_RESET) + vio_port_up(vio); return; } @@ -875,6 +878,115 @@ err_out: return err; } +static LIST_HEAD(vnet_list); +static DEFINE_MUTEX(vnet_list_mutex); + +static struct vnet * __devinit vnet_new(const u64 *local_mac) +{ + struct net_device *dev; + struct vnet *vp; + int err, i; + + dev = alloc_etherdev(sizeof(*vp)); + if (!dev) { + printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n"); + return ERR_PTR(-ENOMEM); + } + + for (i = 0; i < ETH_ALEN; i++) + dev->dev_addr[i] = (*local_mac >> (5 - i) * 8) & 0xff; + + memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); + + vp = netdev_priv(dev); + + spin_lock_init(&vp->lock); + vp->dev = dev; + + INIT_LIST_HEAD(&vp->port_list); + for (i = 0; i < VNET_PORT_HASH_SIZE; i++) + INIT_HLIST_HEAD(&vp->port_hash[i]); + INIT_LIST_HEAD(&vp->list); + vp->local_mac = *local_mac; + + dev->open = vnet_open; + dev->stop = vnet_close; + dev->set_multicast_list = vnet_set_rx_mode; + dev->set_mac_address = vnet_set_mac_addr; + dev->tx_timeout = vnet_tx_timeout; + dev->ethtool_ops = &vnet_ethtool_ops; + dev->watchdog_timeo = VNET_TX_TIMEOUT; + dev->change_mtu = vnet_change_mtu; + dev->hard_start_xmit = vnet_start_xmit; + + err = register_netdev(dev); + if (err) { + printk(KERN_ERR PFX "Cannot register net device, " + "aborting.\n"); + goto err_out_free_dev; + } + + printk(KERN_INFO "%s: Sun LDOM vnet ", dev->name); + + for (i = 0; i < 6; i++) + printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); + + list_add(&vp->list, &vnet_list); + + return vp; + +err_out_free_dev: + free_netdev(dev); + + return ERR_PTR(err); +} + +static struct vnet * __devinit vnet_find_or_create(const u64 *local_mac) +{ + struct vnet *iter, *vp; + + mutex_lock(&vnet_list_mutex); + vp = NULL; + list_for_each_entry(iter, &vnet_list, list) { + if (iter->local_mac == *local_mac) { + vp = iter; + break; + } + } + if (!vp) + vp = vnet_new(local_mac); + mutex_unlock(&vnet_list_mutex); + + return vp; +} + +static const char *local_mac_prop = "local-mac-address"; + +static struct vnet * __devinit vnet_find_parent(struct mdesc_handle *hp, + u64 port_node) +{ + const u64 *local_mac = NULL; + u64 a; + + mdesc_for_each_arc(a, hp, port_node, MDESC_ARC_TYPE_BACK) { + u64 target = mdesc_arc_target(hp, a); + const char *name; + + name = mdesc_get_property(hp, target, "name", NULL); + if (!name || strcmp(name, "network")) + continue; + + local_mac = mdesc_get_property(hp, target, + local_mac_prop, NULL); + if (local_mac) + break; + } + if (!local_mac) + return ERR_PTR(-ENODEV); + + return vnet_find_or_create(local_mac); +} + static struct ldc_channel_config vnet_ldc_cfg = { .event = vnet_event, .mtu = 64, @@ -887,6 +999,14 @@ static struct vio_driver_ops vnet_vio_ops = { .handshake_complete = vnet_handshake_complete, }; +static void print_version(void) +{ + static int version_printed; + + if (version_printed++ == 0) + printk(KERN_INFO "%s", version); +} + const char *remote_macaddr_prop = "remote-mac-address"; static int __devinit vnet_port_probe(struct vio_dev *vdev, @@ -899,14 +1019,17 @@ static int __devinit vnet_port_probe(struct vio_dev *vdev, const u64 *rmac; int len, i, err, switch_port; - vp = dev_get_drvdata(vdev->dev.parent); - if (!vp) { - printk(KERN_ERR PFX "Cannot find port parent vnet.\n"); - return -ENODEV; - } + print_version(); hp = mdesc_grab(); + vp = vnet_find_parent(hp, vdev->mp); + if (IS_ERR(vp)) { + printk(KERN_ERR PFX "Cannot find port parent vnet.\n"); + err = PTR_ERR(vp); + goto err_out_put_mdesc; + } + rmac = mdesc_get_property(hp, vdev->mp, remote_macaddr_prop, &len); err = -ENODEV; if (!rmac) { @@ -1025,139 +1148,14 @@ static struct vio_driver vnet_port_driver = { } }; -const char *local_mac_prop = "local-mac-address"; - -static int __devinit vnet_probe(struct vio_dev *vdev, - const struct vio_device_id *id) -{ - static int vnet_version_printed; - struct mdesc_handle *hp; - struct net_device *dev; - struct vnet *vp; - const u64 *mac; - int err, i, len; - - if (vnet_version_printed++ == 0) - printk(KERN_INFO "%s", version); - - hp = mdesc_grab(); - - mac = mdesc_get_property(hp, vdev->mp, local_mac_prop, &len); - if (!mac) { - printk(KERN_ERR PFX "vnet lacks %s property.\n", - local_mac_prop); - err = -ENODEV; - goto err_out; - } - - dev = alloc_etherdev(sizeof(*vp)); - if (!dev) { - printk(KERN_ERR PFX "Etherdev alloc failed, aborting.\n"); - err = -ENOMEM; - goto err_out; - } - - for (i = 0; i < ETH_ALEN; i++) - dev->dev_addr[i] = (*mac >> (5 - i) * 8) & 0xff; - - memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); - - SET_NETDEV_DEV(dev, &vdev->dev); - - vp = netdev_priv(dev); - - spin_lock_init(&vp->lock); - vp->dev = dev; - vp->vdev = vdev; - - INIT_LIST_HEAD(&vp->port_list); - for (i = 0; i < VNET_PORT_HASH_SIZE; i++) - INIT_HLIST_HEAD(&vp->port_hash[i]); - - dev->open = vnet_open; - dev->stop = vnet_close; - dev->set_multicast_list = vnet_set_rx_mode; - dev->set_mac_address = vnet_set_mac_addr; - dev->tx_timeout = vnet_tx_timeout; - dev->ethtool_ops = &vnet_ethtool_ops; - dev->watchdog_timeo = VNET_TX_TIMEOUT; - dev->change_mtu = vnet_change_mtu; - dev->hard_start_xmit = vnet_start_xmit; - - err = register_netdev(dev); - if (err) { - printk(KERN_ERR PFX "Cannot register net device, " - "aborting.\n"); - goto err_out_free_dev; - } - - printk(KERN_INFO "%s: Sun LDOM vnet ", dev->name); - - for (i = 0; i < 6; i++) - printk("%2.2x%c", dev->dev_addr[i], i == 5 ? '\n' : ':'); - - dev_set_drvdata(&vdev->dev, vp); - - mdesc_release(hp); - - return 0; - -err_out_free_dev: - free_netdev(dev); - -err_out: - mdesc_release(hp); - return err; -} - -static int vnet_remove(struct vio_dev *vdev) -{ - - struct vnet *vp = dev_get_drvdata(&vdev->dev); - - if (vp) { - /* XXX unregister port, or at least check XXX */ - unregister_netdevice(vp->dev); - dev_set_drvdata(&vdev->dev, NULL); - } - return 0; -} - -static struct vio_device_id vnet_match[] = { - { - .type = "network", - }, - {}, -}; -MODULE_DEVICE_TABLE(vio, vnet_match); - -static struct vio_driver vnet_driver = { - .id_table = vnet_match, - .probe = vnet_probe, - .remove = vnet_remove, - .driver = { - .name = "vnet", - .owner = THIS_MODULE, - } -}; - static int __init vnet_init(void) { - int err = vio_register_driver(&vnet_driver); - - if (!err) { - err = vio_register_driver(&vnet_port_driver); - if (err) - vio_unregister_driver(&vnet_driver); - } - - return err; + return vio_register_driver(&vnet_port_driver); } static void __exit vnet_exit(void) { vio_unregister_driver(&vnet_port_driver); - vio_unregister_driver(&vnet_driver); } module_init(vnet_init); diff --git a/drivers/net/sunvnet.h b/drivers/net/sunvnet.h index 1c88730..7d3a0ca 100644 --- a/drivers/net/sunvnet.h +++ b/drivers/net/sunvnet.h @@ -60,11 +60,13 @@ struct vnet { struct net_device *dev; u32 msg_enable; - struct vio_dev *vdev; struct list_head port_list; struct hlist_head port_hash[VNET_PORT_HASH_SIZE]; + + struct list_head list; + u64 local_mac; }; #endif /* _SUNVNET_H */ diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c new file mode 100644 index 0000000..489f69c --- /dev/null +++ b/drivers/net/xen-netfront.c @@ -0,0 +1,1863 @@ +/* + * Virtual network driver for conversing with remote driver backends. + * + * Copyright (c) 2002-2005, K A Fraser + * Copyright (c) 2005, XenSource Ltd + * + * 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; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/ethtool.h> +#include <linux/if_ether.h> +#include <linux/tcp.h> +#include <linux/udp.h> +#include <linux/moduleparam.h> +#include <linux/mm.h> +#include <net/ip.h> + +#include <xen/xenbus.h> +#include <xen/events.h> +#include <xen/page.h> +#include <xen/grant_table.h> + +#include <xen/interface/io/netif.h> +#include <xen/interface/memory.h> +#include <xen/interface/grant_table.h> + +static struct ethtool_ops xennet_ethtool_ops; + +struct netfront_cb { + struct page *page; + unsigned offset; +}; + +#define NETFRONT_SKB_CB(skb) ((struct netfront_cb *)((skb)->cb)) + +#define RX_COPY_THRESHOLD 256 + +#define GRANT_INVALID_REF 0 + +#define NET_TX_RING_SIZE __RING_SIZE((struct xen_netif_tx_sring *)0, PAGE_SIZE) +#define NET_RX_RING_SIZE __RING_SIZE((struct xen_netif_rx_sring *)0, PAGE_SIZE) +#define TX_MAX_TARGET min_t(int, NET_RX_RING_SIZE, 256) + +struct netfront_info { + struct list_head list; + struct net_device *netdev; + + struct net_device_stats stats; + + struct xen_netif_tx_front_ring tx; + struct xen_netif_rx_front_ring rx; + + spinlock_t tx_lock; + spinlock_t rx_lock; + + unsigned int evtchn; + + /* Receive-ring batched refills. */ +#define RX_MIN_TARGET 8 +#define RX_DFL_MIN_TARGET 64 +#define RX_MAX_TARGET min_t(int, NET_RX_RING_SIZE, 256) + unsigned rx_min_target, rx_max_target, rx_target; + struct sk_buff_head rx_batch; + + struct timer_list rx_refill_timer; + + /* + * {tx,rx}_skbs store outstanding skbuffs. Free tx_skb entries + * are linked from tx_skb_freelist through skb_entry.link. + * + * NB. Freelist index entries are always going to be less than + * PAGE_OFFSET, whereas pointers to skbs will always be equal or + * greater than PAGE_OFFSET: we use this property to distinguish + * them. + */ + union skb_entry { + struct sk_buff *skb; + unsigned link; + } tx_skbs[NET_TX_RING_SIZE]; + grant_ref_t gref_tx_head; + grant_ref_t grant_tx_ref[NET_TX_RING_SIZE]; + unsigned tx_skb_freelist; + + struct sk_buff *rx_skbs[NET_RX_RING_SIZE]; + grant_ref_t gref_rx_head; + grant_ref_t grant_rx_ref[NET_RX_RING_SIZE]; + + struct xenbus_device *xbdev; + int tx_ring_ref; + int rx_ring_ref; + + unsigned long rx_pfn_array[NET_RX_RING_SIZE]; + struct multicall_entry rx_mcl[NET_RX_RING_SIZE+1]; + struct mmu_update rx_mmu[NET_RX_RING_SIZE]; +}; + +struct netfront_rx_info { + struct xen_netif_rx_response rx; + struct xen_netif_extra_info extras[XEN_NETIF_EXTRA_TYPE_MAX - 1]; +}; + +/* + * Access macros for acquiring freeing slots in tx_skbs[]. + */ + +static void add_id_to_freelist(unsigned *head, union skb_entry *list, + unsigned short id) +{ + list[id].link = *head; + *head = id; +} + +static unsigned short get_id_from_freelist(unsigned *head, + union skb_entry *list) +{ + unsigned int id = *head; + *head = list[id].link; + return id; +} + +static int xennet_rxidx(RING_IDX idx) +{ + return idx & (NET_RX_RING_SIZE - 1); +} + +static struct sk_buff *xennet_get_rx_skb(struct netfront_info *np, + RING_IDX ri) +{ + int i = xennet_rxidx(ri); + struct sk_buff *skb = np->rx_skbs[i]; + np->rx_skbs[i] = NULL; + return skb; +} + +static grant_ref_t xennet_get_rx_ref(struct netfront_info *np, + RING_IDX ri) +{ + int i = xennet_rxidx(ri); + grant_ref_t ref = np->grant_rx_ref[i]; + np->grant_rx_ref[i] = GRANT_INVALID_REF; + return ref; +} + +#ifdef CONFIG_SYSFS +static int xennet_sysfs_addif(struct net_device *netdev); +static void xennet_sysfs_delif(struct net_device *netdev); +#else /* !CONFIG_SYSFS */ +#define xennet_sysfs_addif(dev) (0) +#define xennet_sysfs_delif(dev) do { } while (0) +#endif + +static int xennet_can_sg(struct net_device *dev) +{ + return dev->features & NETIF_F_SG; +} + + +static void rx_refill_timeout(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + netif_rx_schedule(dev); +} + +static int netfront_tx_slot_available(struct netfront_info *np) +{ + return ((np->tx.req_prod_pvt - np->tx.rsp_cons) < + (TX_MAX_TARGET - MAX_SKB_FRAGS - 2)); +} + +static void xennet_maybe_wake_tx(struct net_device *dev) +{ + struct netfront_info *np = netdev_priv(dev); + + if (unlikely(netif_queue_stopped(dev)) && + netfront_tx_slot_available(np) && + likely(netif_running(dev))) + netif_wake_queue(dev); +} + +static void xennet_alloc_rx_buffers(struct net_device *dev) +{ + unsigned short id; + struct netfront_info *np = netdev_priv(dev); + struct sk_buff *skb; + struct page *page; + int i, batch_target, notify; + RING_IDX req_prod = np->rx.req_prod_pvt; + struct xen_memory_reservation reservation; + grant_ref_t ref; + unsigned long pfn; + void *vaddr; + int nr_flips; + struct xen_netif_rx_request *req; + + if (unlikely(!netif_carrier_ok(dev))) + return; + + /* + * Allocate skbuffs greedily, even though we batch updates to the + * receive ring. This creates a less bursty demand on the memory + * allocator, so should reduce the chance of failed allocation requests + * both for ourself and for other kernel subsystems. + */ + batch_target = np->rx_target - (req_prod - np->rx.rsp_cons); + for (i = skb_queue_len(&np->rx_batch); i < batch_target; i++) { + skb = __netdev_alloc_skb(dev, RX_COPY_THRESHOLD, + GFP_ATOMIC | __GFP_NOWARN); + if (unlikely(!skb)) + goto no_skb; + + page = alloc_page(GFP_ATOMIC | __GFP_NOWARN); + if (!page) { + kfree_skb(skb); +no_skb: + /* Any skbuffs queued for refill? Force them out. */ + if (i != 0) + goto refill; + /* Could not allocate any skbuffs. Try again later. */ + mod_timer(&np->rx_refill_timer, + jiffies + (HZ/10)); + break; + } + + skb_shinfo(skb)->frags[0].page = page; + skb_shinfo(skb)->nr_frags = 1; + __skb_queue_tail(&np->rx_batch, skb); + } + + /* Is the batch large enough to be worthwhile? */ + if (i < (np->rx_target/2)) { + if (req_prod > np->rx.sring->req_prod) + goto push; + return; + } + + /* Adjust our fill target if we risked running out of buffers. */ + if (((req_prod - np->rx.sring->rsp_prod) < (np->rx_target / 4)) && + ((np->rx_target *= 2) > np->rx_max_target)) + np->rx_target = np->rx_max_target; + + refill: + for (nr_flips = i = 0; ; i++) { + skb = __skb_dequeue(&np->rx_batch); + if (skb == NULL) + break; + + skb->dev = dev; + + id = xennet_rxidx(req_prod + i); + + BUG_ON(np->rx_skbs[id]); + np->rx_skbs[id] = skb; + + ref = gnttab_claim_grant_reference(&np->gref_rx_head); + BUG_ON((signed short)ref < 0); + np->grant_rx_ref[id] = ref; + + pfn = page_to_pfn(skb_shinfo(skb)->frags[0].page); + vaddr = page_address(skb_shinfo(skb)->frags[0].page); + + req = RING_GET_REQUEST(&np->rx, req_prod + i); + gnttab_grant_foreign_access_ref(ref, + np->xbdev->otherend_id, + pfn_to_mfn(pfn), + 0); + + req->id = id; + req->gref = ref; + } + + if (nr_flips != 0) { + reservation.extent_start = np->rx_pfn_array; + reservation.nr_extents = nr_flips; + reservation.extent_order = 0; + reservation.address_bits = 0; + reservation.domid = DOMID_SELF; + + if (!xen_feature(XENFEAT_auto_translated_physmap)) { + /* After all PTEs have been zapped, flush the TLB. */ + np->rx_mcl[i-1].args[MULTI_UVMFLAGS_INDEX] = + UVMF_TLB_FLUSH|UVMF_ALL; + + /* Give away a batch of pages. */ + np->rx_mcl[i].op = __HYPERVISOR_memory_op; + np->rx_mcl[i].args[0] = XENMEM_decrease_reservation; + np->rx_mcl[i].args[1] = (unsigned long)&reservation; + + /* Zap PTEs and give away pages in one big + * multicall. */ + (void)HYPERVISOR_multicall(np->rx_mcl, i+1); + + /* Check return status of HYPERVISOR_memory_op(). */ + if (unlikely(np->rx_mcl[i].result != i)) + panic("Unable to reduce memory reservation\n"); + } else { + if (HYPERVISOR_memory_op(XENMEM_decrease_reservation, + &reservation) != i) + panic("Unable to reduce memory reservation\n"); + } + } else { + wmb(); /* barrier so backend seens requests */ + } + + /* Above is a suitable barrier to ensure backend will see requests. */ + np->rx.req_prod_pvt = req_prod + i; + push: + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&np->rx, notify); + if (notify) + notify_remote_via_irq(np->netdev->irq); +} + +static int xennet_open(struct net_device *dev) +{ + struct netfront_info *np = netdev_priv(dev); + + memset(&np->stats, 0, sizeof(np->stats)); + + spin_lock_bh(&np->rx_lock); + if (netif_carrier_ok(dev)) { + xennet_alloc_rx_buffers(dev); + np->rx.sring->rsp_event = np->rx.rsp_cons + 1; + if (RING_HAS_UNCONSUMED_RESPONSES(&np->rx)) + netif_rx_schedule(dev); + } + spin_unlock_bh(&np->rx_lock); + + xennet_maybe_wake_tx(dev); + + return 0; +} + +static void xennet_tx_buf_gc(struct net_device *dev) +{ + RING_IDX cons, prod; + unsigned short id; + struct netfront_info *np = netdev_priv(dev); + struct sk_buff *skb; + + BUG_ON(!netif_carrier_ok(dev)); + + do { + prod = np->tx.sring->rsp_prod; + rmb(); /* Ensure we see responses up to 'rp'. */ + + for (cons = np->tx.rsp_cons; cons != prod; cons++) { + struct xen_netif_tx_response *txrsp; + + txrsp = RING_GET_RESPONSE(&np->tx, cons); + if (txrsp->status == NETIF_RSP_NULL) + continue; + + id = txrsp->id; + skb = np->tx_skbs[id].skb; + if (unlikely(gnttab_query_foreign_access( + np->grant_tx_ref[id]) != 0)) { + printk(KERN_ALERT "xennet_tx_buf_gc: warning " + "-- grant still in use by backend " + "domain.\n"); + BUG(); + } + gnttab_end_foreign_access_ref( + np->grant_tx_ref[id], GNTMAP_readonly); + gnttab_release_grant_reference( + &np->gref_tx_head, np->grant_tx_ref[id]); + np->grant_tx_ref[id] = GRANT_INVALID_REF; + add_id_to_freelist(&np->tx_skb_freelist, np->tx_skbs, id); + dev_kfree_skb_irq(skb); + } + + np->tx.rsp_cons = prod; + + /* + * Set a new event, then check for race with update of tx_cons. + * Note that it is essential to schedule a callback, no matter + * how few buffers are pending. Even if there is space in the + * transmit ring, higher layers may be blocked because too much + * data is outstanding: in such cases notification from Xen is + * likely to be the only kick that we'll get. + */ + np->tx.sring->rsp_event = + prod + ((np->tx.sring->req_prod - prod) >> 1) + 1; + mb(); /* update shared area */ + } while ((cons == prod) && (prod != np->tx.sring->rsp_prod)); + + xennet_maybe_wake_tx(dev); +} + +static void xennet_make_frags(struct sk_buff *skb, struct net_device *dev, + struct xen_netif_tx_request *tx) +{ + struct netfront_info *np = netdev_priv(dev); + char *data = skb->data; + unsigned long mfn; + RING_IDX prod = np->tx.req_prod_pvt; + int frags = skb_shinfo(skb)->nr_frags; + unsigned int offset = offset_in_page(data); + unsigned int len = skb_headlen(skb); + unsigned int id; + grant_ref_t ref; + int i; + + /* While the header overlaps a page boundary (including being + larger than a page), split it it into page-sized chunks. */ + while (len > PAGE_SIZE - offset) { + tx->size = PAGE_SIZE - offset; + tx->flags |= NETTXF_more_data; + len -= tx->size; + data += tx->size; + offset = 0; + + id = get_id_from_freelist(&np->tx_skb_freelist, np->tx_skbs); + np->tx_skbs[id].skb = skb_get(skb); + tx = RING_GET_REQUEST(&np->tx, prod++); + tx->id = id; + ref = gnttab_claim_grant_reference(&np->gref_tx_head); + BUG_ON((signed short)ref < 0); + + mfn = virt_to_mfn(data); + gnttab_grant_foreign_access_ref(ref, np->xbdev->otherend_id, + mfn, GNTMAP_readonly); + + tx->gref = np->grant_tx_ref[id] = ref; + tx->offset = offset; + tx->size = len; + tx->flags = 0; + } + + /* Grant backend access to each skb fragment page. */ + for (i = 0; i < frags; i++) { + skb_frag_t *frag = skb_shinfo(skb)->frags + i; + + tx->flags |= NETTXF_more_data; + + id = get_id_from_freelist(&np->tx_skb_freelist, np->tx_skbs); + np->tx_skbs[id].skb = skb_get(skb); + tx = RING_GET_REQUEST(&np->tx, prod++); + tx->id = id; + ref = gnttab_claim_grant_reference(&np->gref_tx_head); + BUG_ON((signed short)ref < 0); + + mfn = pfn_to_mfn(page_to_pfn(frag->page)); + gnttab_grant_foreign_access_ref(ref, np->xbdev->otherend_id, + mfn, GNTMAP_readonly); + + tx->gref = np->grant_tx_ref[id] = ref; + tx->offset = frag->page_offset; + tx->size = frag->size; + tx->flags = 0; + } + + np->tx.req_prod_pvt = prod; +} + +static int xennet_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + unsigned short id; + struct netfront_info *np = netdev_priv(dev); + struct xen_netif_tx_request *tx; + struct xen_netif_extra_info *extra; + char *data = skb->data; + RING_IDX i; + grant_ref_t ref; + unsigned long mfn; + int notify; + int frags = skb_shinfo(skb)->nr_frags; + unsigned int offset = offset_in_page(data); + unsigned int len = skb_headlen(skb); + + frags += (offset + len + PAGE_SIZE - 1) / PAGE_SIZE; + if (unlikely(frags > MAX_SKB_FRAGS + 1)) { + printk(KERN_ALERT "xennet: skb rides the rocket: %d frags\n", + frags); + dump_stack(); + goto drop; + } + + spin_lock_irq(&np->tx_lock); + + if (unlikely(!netif_carrier_ok(dev) || + (frags > 1 && !xennet_can_sg(dev)) || + netif_needs_gso(dev, skb))) { + spin_unlock_irq(&np->tx_lock); + goto drop; + } + + i = np->tx.req_prod_pvt; + + id = get_id_from_freelist(&np->tx_skb_freelist, np->tx_skbs); + np->tx_skbs[id].skb = skb; + + tx = RING_GET_REQUEST(&np->tx, i); + + tx->id = id; + ref = gnttab_claim_grant_reference(&np->gref_tx_head); + BUG_ON((signed short)ref < 0); + mfn = virt_to_mfn(data); + gnttab_grant_foreign_access_ref( + ref, np->xbdev->otherend_id, mfn, GNTMAP_readonly); + tx->gref = np->grant_tx_ref[id] = ref; + tx->offset = offset; + tx->size = len; + extra = NULL; + + tx->flags = 0; + if (skb->ip_summed == CHECKSUM_PARTIAL) + /* local packet? */ + tx->flags |= NETTXF_csum_blank | NETTXF_data_validated; + else if (skb->ip_summed == CHECKSUM_UNNECESSARY) + /* remote but checksummed. */ + tx->flags |= NETTXF_data_validated; + + if (skb_shinfo(skb)->gso_size) { + struct xen_netif_extra_info *gso; + + gso = (struct xen_netif_extra_info *) + RING_GET_REQUEST(&np->tx, ++i); + + if (extra) + extra->flags |= XEN_NETIF_EXTRA_FLAG_MORE; + else + tx->flags |= NETTXF_extra_info; + + gso->u.gso.size = skb_shinfo(skb)->gso_size; + gso->u.gso.type = XEN_NETIF_GSO_TYPE_TCPV4; + gso->u.gso.pad = 0; + gso->u.gso.features = 0; + + gso->type = XEN_NETIF_EXTRA_TYPE_GSO; + gso->flags = 0; + extra = gso; + } + + np->tx.req_prod_pvt = i + 1; + + xennet_make_frags(skb, dev, tx); + tx->size = skb->len; + + RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&np->tx, notify); + if (notify) + notify_remote_via_irq(np->netdev->irq); + + xennet_tx_buf_gc(dev); + + if (!netfront_tx_slot_available(np)) + netif_stop_queue(dev); + + spin_unlock_irq(&np->tx_lock); + + np->stats.tx_bytes += skb->len; + np->stats.tx_packets++; + + return 0; + + drop: + np->stats.tx_dropped++; + dev_kfree_skb(skb); + return 0; +} + +static int xennet_close(struct net_device *dev) +{ + struct netfront_info *np = netdev_priv(dev); + netif_stop_queue(np->netdev); + return 0; +} + +static struct net_device_stats *xennet_get_stats(struct net_device *dev) +{ + struct netfront_info *np = netdev_priv(dev); + return &np->stats; +} + +static void xennet_move_rx_slot(struct netfront_info *np, struct sk_buff *skb, + grant_ref_t ref) +{ + int new = xennet_rxidx(np->rx.req_prod_pvt); + + BUG_ON(np->rx_skbs[new]); + np->rx_skbs[new] = skb; + np->grant_rx_ref[new] = ref; + RING_GET_REQUEST(&np->rx, np->rx.req_prod_pvt)->id = new; + RING_GET_REQUEST(&np->rx, np->rx.req_prod_pvt)->gref = ref; + np->rx.req_prod_pvt++; +} + +static int xennet_get_extras(struct netfront_info *np, + struct xen_netif_extra_info *extras, + RING_IDX rp) + +{ + struct xen_netif_extra_info *extra; + struct device *dev = &np->netdev->dev; + RING_IDX cons = np->rx.rsp_cons; + int err = 0; + + do { + struct sk_buff *skb; + grant_ref_t ref; + + if (unlikely(cons + 1 == rp)) { + if (net_ratelimit()) + dev_warn(dev, "Missing extra info\n"); + err = -EBADR; + break; + } + + extra = (struct xen_netif_extra_info *) + RING_GET_RESPONSE(&np->rx, ++cons); + + if (unlikely(!extra->type || + extra->type >= XEN_NETIF_EXTRA_TYPE_MAX)) { + if (net_ratelimit()) + dev_warn(dev, "Invalid extra type: %d\n", + extra->type); + err = -EINVAL; + } else { + memcpy(&extras[extra->type - 1], extra, + sizeof(*extra)); + } + + skb = xennet_get_rx_skb(np, cons); + ref = xennet_get_rx_ref(np, cons); + xennet_move_rx_slot(np, skb, ref); + } while (extra->flags & XEN_NETIF_EXTRA_FLAG_MORE); + + np->rx.rsp_cons = cons; + return err; +} + +static int xennet_get_responses(struct netfront_info *np, + struct netfront_rx_info *rinfo, RING_IDX rp, + struct sk_buff_head *list) +{ + struct xen_netif_rx_response *rx = &rinfo->rx; + struct xen_netif_extra_info *extras = rinfo->extras; + struct device *dev = &np->netdev->dev; + RING_IDX cons = np->rx.rsp_cons; + struct sk_buff *skb = xennet_get_rx_skb(np, cons); + grant_ref_t ref = xennet_get_rx_ref(np, cons); + int max = MAX_SKB_FRAGS + (rx->status <= RX_COPY_THRESHOLD); + int frags = 1; + int err = 0; + unsigned long ret; + + if (rx->flags & NETRXF_extra_info) { + err = xennet_get_extras(np, extras, rp); + cons = np->rx.rsp_cons; + } + + for (;;) { + if (unlikely(rx->status < 0 || + rx->offset + rx->status > PAGE_SIZE)) { + if (net_ratelimit()) + dev_warn(dev, "rx->offset: %x, size: %u\n", + rx->offset, rx->status); + xennet_move_rx_slot(np, skb, ref); + err = -EINVAL; + goto next; + } + + /* + * This definitely indicates a bug, either in this driver or in + * the backend driver. In future this should flag the bad + * situation to the system controller to reboot the backed. + */ + if (ref == GRANT_INVALID_REF) { + if (net_ratelimit()) + dev_warn(dev, "Bad rx response id %d.\n", + rx->id); + err = -EINVAL; + goto next; + } + + ret = gnttab_end_foreign_access_ref(ref, 0); + BUG_ON(!ret); + + gnttab_release_grant_reference(&np->gref_rx_head, ref); + + __skb_queue_tail(list, skb); + +next: + if (!(rx->flags & NETRXF_more_data)) + break; + + if (cons + frags == rp) { + if (net_ratelimit()) + dev_warn(dev, "Need more frags\n"); + err = -ENOENT; + break; + } + + rx = RING_GET_RESPONSE(&np->rx, cons + frags); + skb = xennet_get_rx_skb(np, cons + frags); + ref = xennet_get_rx_ref(np, cons + frags); + frags++; + } + + if (unlikely(frags > max)) { + if (net_ratelimit()) + dev_warn(dev, "Too many frags\n"); + err = -E2BIG; + } + + if (unlikely(err)) + np->rx.rsp_cons = cons + frags; + + return err; +} + +static int xennet_set_skb_gso(struct sk_buff *skb, + struct xen_netif_extra_info *gso) +{ + if (!gso->u.gso.size) { + if (net_ratelimit()) + printk(KERN_WARNING "GSO size must not be zero.\n"); + return -EINVAL; + } + + /* Currently only TCPv4 S.O. is supported. */ + if (gso->u.gso.type != XEN_NETIF_GSO_TYPE_TCPV4) { + if (net_ratelimit()) + printk(KERN_WARNING "Bad GSO type %d.\n", gso->u.gso.type); + return -EINVAL; + } + + skb_shinfo(skb)->gso_size = gso->u.gso.size; + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; + + /* Header must be checked, and gso_segs computed. */ + skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY; + skb_shinfo(skb)->gso_segs = 0; + + return 0; +} + +static RING_IDX xennet_fill_frags(struct netfront_info *np, + struct sk_buff *skb, + struct sk_buff_head *list) +{ + struct skb_shared_info *shinfo = skb_shinfo(skb); + int nr_frags = shinfo->nr_frags; + RING_IDX cons = np->rx.rsp_cons; + skb_frag_t *frag = shinfo->frags + nr_frags; + struct sk_buff *nskb; + + while ((nskb = __skb_dequeue(list))) { + struct xen_netif_rx_response *rx = + RING_GET_RESPONSE(&np->rx, ++cons); + + frag->page = skb_shinfo(nskb)->frags[0].page; + frag->page_offset = rx->offset; + frag->size = rx->status; + + skb->data_len += rx->status; + + skb_shinfo(nskb)->nr_frags = 0; + kfree_skb(nskb); + + frag++; + nr_frags++; + } + + shinfo->nr_frags = nr_frags; + return cons; +} + +static int skb_checksum_setup(struct sk_buff *skb) +{ + struct iphdr *iph; + unsigned char *th; + int err = -EPROTO; + + if (skb->protocol != htons(ETH_P_IP)) + goto out; + + iph = (void *)skb->data; + th = skb->data + 4 * iph->ihl; + if (th >= skb_tail_pointer(skb)) + goto out; + + skb->csum_start = th - skb->head; + switch (iph->protocol) { + case IPPROTO_TCP: + skb->csum_offset = offsetof(struct tcphdr, check); + break; + case IPPROTO_UDP: + skb->csum_offset = offsetof(struct udphdr, check); + break; + default: + if (net_ratelimit()) + printk(KERN_ERR "Attempting to checksum a non-" + "TCP/UDP packet, dropping a protocol" + " %d packet", iph->protocol); + goto out; + } + + if ((th + skb->csum_offset + 2) > skb_tail_pointer(skb)) + goto out; + + err = 0; + +out: + return err; +} + +static int handle_incoming_queue(struct net_device *dev, + struct sk_buff_head *rxq) +{ + struct netfront_info *np = netdev_priv(dev); + int packets_dropped = 0; + struct sk_buff *skb; + + while ((skb = __skb_dequeue(rxq)) != NULL) { + struct page *page = NETFRONT_SKB_CB(skb)->page; + void *vaddr = page_address(page); + unsigned offset = NETFRONT_SKB_CB(skb)->offset; + + memcpy(skb->data, vaddr + offset, + skb_headlen(skb)); + + if (page != skb_shinfo(skb)->frags[0].page) + __free_page(page); + + /* Ethernet work: Delayed to here as it peeks the header. */ + skb->protocol = eth_type_trans(skb, dev); + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + if (skb_checksum_setup(skb)) { + kfree_skb(skb); + packets_dropped++; + np->stats.rx_errors++; + continue; + } + } + + np->stats.rx_packets++; + np->stats.rx_bytes += skb->len; + + /* Pass it up. */ + netif_receive_skb(skb); + dev->last_rx = jiffies; + } + + return packets_dropped; +} + +static int xennet_poll(struct net_device *dev, int *pbudget) +{ + struct netfront_info *np = netdev_priv(dev); + struct sk_buff *skb; + struct netfront_rx_info rinfo; + struct xen_netif_rx_response *rx = &rinfo.rx; + struct xen_netif_extra_info *extras = rinfo.extras; + RING_IDX i, rp; + int work_done, budget, more_to_do = 1; + struct sk_buff_head rxq; + struct sk_buff_head errq; + struct sk_buff_head tmpq; + unsigned long flags; + unsigned int len; + int err; + + spin_lock(&np->rx_lock); + + if (unlikely(!netif_carrier_ok(dev))) { + spin_unlock(&np->rx_lock); + return 0; + } + + skb_queue_head_init(&rxq); + skb_queue_head_init(&errq); + skb_queue_head_init(&tmpq); + + budget = *pbudget; + if (budget > dev->quota) + budget = dev->quota; + rp = np->rx.sring->rsp_prod; + rmb(); /* Ensure we see queued responses up to 'rp'. */ + + i = np->rx.rsp_cons; + work_done = 0; + while ((i != rp) && (work_done < budget)) { + memcpy(rx, RING_GET_RESPONSE(&np->rx, i), sizeof(*rx)); + memset(extras, 0, sizeof(rinfo.extras)); + + err = xennet_get_responses(np, &rinfo, rp, &tmpq); + + if (unlikely(err)) { +err: + while ((skb = __skb_dequeue(&tmpq))) + __skb_queue_tail(&errq, skb); + np->stats.rx_errors++; + i = np->rx.rsp_cons; + continue; + } + + skb = __skb_dequeue(&tmpq); + + if (extras[XEN_NETIF_EXTRA_TYPE_GSO - 1].type) { + struct xen_netif_extra_info *gso; + gso = &extras[XEN_NETIF_EXTRA_TYPE_GSO - 1]; + + if (unlikely(xennet_set_skb_gso(skb, gso))) { + __skb_queue_head(&tmpq, skb); + np->rx.rsp_cons += skb_queue_len(&tmpq); + goto err; + } + } + + NETFRONT_SKB_CB(skb)->page = skb_shinfo(skb)->frags[0].page; + NETFRONT_SKB_CB(skb)->offset = rx->offset; + + len = rx->status; + if (len > RX_COPY_THRESHOLD) + len = RX_COPY_THRESHOLD; + skb_put(skb, len); + + if (rx->status > len) { + skb_shinfo(skb)->frags[0].page_offset = + rx->offset + len; + skb_shinfo(skb)->frags[0].size = rx->status - len; + skb->data_len = rx->status - len; + } else { + skb_shinfo(skb)->frags[0].page = NULL; + skb_shinfo(skb)->nr_frags = 0; + } + + i = xennet_fill_frags(np, skb, &tmpq); + + /* + * Truesize approximates the size of true data plus + * any supervisor overheads. Adding hypervisor + * overheads has been shown to significantly reduce + * achievable bandwidth with the default receive + * buffer size. It is therefore not wise to account + * for it here. + * + * After alloc_skb(RX_COPY_THRESHOLD), truesize is set + * to RX_COPY_THRESHOLD + the supervisor + * overheads. Here, we add the size of the data pulled + * in xennet_fill_frags(). + * + * We also adjust for any unused space in the main + * data area by subtracting (RX_COPY_THRESHOLD - + * len). This is especially important with drivers + * which split incoming packets into header and data, + * using only 66 bytes of the main data area (see the + * e1000 driver for example.) On such systems, + * without this last adjustement, our achievable + * receive throughout using the standard receive + * buffer size was cut by 25%(!!!). + */ + skb->truesize += skb->data_len - (RX_COPY_THRESHOLD - len); + skb->len += skb->data_len; + + if (rx->flags & NETRXF_csum_blank) + skb->ip_summed = CHECKSUM_PARTIAL; + else if (rx->flags & NETRXF_data_validated) + skb->ip_summed = CHECKSUM_UNNECESSARY; + + __skb_queue_tail(&rxq, skb); + + np->rx.rsp_cons = ++i; + work_done++; + } + + while ((skb = __skb_dequeue(&errq))) + kfree_skb(skb); + + work_done -= handle_incoming_queue(dev, &rxq); + + /* If we get a callback with very few responses, reduce fill target. */ + /* NB. Note exponential increase, linear decrease. */ + if (((np->rx.req_prod_pvt - np->rx.sring->rsp_prod) > + ((3*np->rx_target) / 4)) && + (--np->rx_target < np->rx_min_target)) + np->rx_target = np->rx_min_target; + + xennet_alloc_rx_buffers(dev); + + *pbudget -= work_done; + dev->quota -= work_done; + + if (work_done < budget) { + local_irq_save(flags); + + RING_FINAL_CHECK_FOR_RESPONSES(&np->rx, more_to_do); + if (!more_to_do) + __netif_rx_complete(dev); + + local_irq_restore(flags); + } + + spin_unlock(&np->rx_lock); + + return more_to_do; +} + +static int xennet_change_mtu(struct net_device *dev, int mtu) +{ + int max = xennet_can_sg(dev) ? 65535 - ETH_HLEN : ETH_DATA_LEN; + + if (mtu > max) + return -EINVAL; + dev->mtu = mtu; + return 0; +} + +static void xennet_release_tx_bufs(struct netfront_info *np) +{ + struct sk_buff *skb; + int i; + + for (i = 0; i < NET_TX_RING_SIZE; i++) { + /* Skip over entries which are actually freelist references */ + if ((unsigned long)np->tx_skbs[i].skb < PAGE_OFFSET) + continue; + + skb = np->tx_skbs[i].skb; + gnttab_end_foreign_access_ref(np->grant_tx_ref[i], + GNTMAP_readonly); + gnttab_release_grant_reference(&np->gref_tx_head, + np->grant_tx_ref[i]); + np->grant_tx_ref[i] = GRANT_INVALID_REF; + add_id_to_freelist(&np->tx_skb_freelist, np->tx_skbs, i); + dev_kfree_skb_irq(skb); + } +} + +static void xennet_release_rx_bufs(struct netfront_info *np) +{ + struct mmu_update *mmu = np->rx_mmu; + struct multicall_entry *mcl = np->rx_mcl; + struct sk_buff_head free_list; + struct sk_buff *skb; + unsigned long mfn; + int xfer = 0, noxfer = 0, unused = 0; + int id, ref; + + dev_warn(&np->netdev->dev, "%s: fix me for copying receiver.\n", + __func__); + return; + + skb_queue_head_init(&free_list); + + spin_lock_bh(&np->rx_lock); + + for (id = 0; id < NET_RX_RING_SIZE; id++) { + ref = np->grant_rx_ref[id]; + if (ref == GRANT_INVALID_REF) { + unused++; + continue; + } + + skb = np->rx_skbs[id]; + mfn = gnttab_end_foreign_transfer_ref(ref); + gnttab_release_grant_reference(&np->gref_rx_head, ref); + np->grant_rx_ref[id] = GRANT_INVALID_REF; + + if (0 == mfn) { + skb_shinfo(skb)->nr_frags = 0; + dev_kfree_skb(skb); + noxfer++; + continue; + } + + if (!xen_feature(XENFEAT_auto_translated_physmap)) { + /* Remap the page. */ + struct page *page = skb_shinfo(skb)->frags[0].page; + unsigned long pfn = page_to_pfn(page); + void *vaddr = page_address(page); + + MULTI_update_va_mapping(mcl, (unsigned long)vaddr, + mfn_pte(mfn, PAGE_KERNEL), + 0); + mcl++; + mmu->ptr = ((u64)mfn << PAGE_SHIFT) + | MMU_MACHPHYS_UPDATE; + mmu->val = pfn; + mmu++; + + set_phys_to_machine(pfn, mfn); + } + __skb_queue_tail(&free_list, skb); + xfer++; + } + + dev_info(&np->netdev->dev, "%s: %d xfer, %d noxfer, %d unused\n", + __func__, xfer, noxfer, unused); + + if (xfer) { + if (!xen_feature(XENFEAT_auto_translated_physmap)) { + /* Do all the remapping work and M2P updates. */ + MULTI_mmu_update(mcl, np->rx_mmu, mmu - np->rx_mmu, + 0, DOMID_SELF); + mcl++; + HYPERVISOR_multicall(np->rx_mcl, mcl - np->rx_mcl); + } + } + + while ((skb = __skb_dequeue(&free_list)) != NULL) + dev_kfree_skb(skb); + + spin_unlock_bh(&np->rx_lock); +} + +static void xennet_uninit(struct net_device *dev) +{ + struct netfront_info *np = netdev_priv(dev); + xennet_release_tx_bufs(np); + xennet_release_rx_bufs(np); + gnttab_free_grant_references(np->gref_tx_head); + gnttab_free_grant_references(np->gref_rx_head); +} + +static struct net_device * __devinit xennet_create_dev(struct xenbus_device *dev) +{ + int i, err; + struct net_device *netdev; + struct netfront_info *np; + + netdev = alloc_etherdev(sizeof(struct netfront_info)); + if (!netdev) { + printk(KERN_WARNING "%s> alloc_etherdev failed.\n", + __func__); + return ERR_PTR(-ENOMEM); + } + + np = netdev_priv(netdev); + np->xbdev = dev; + + spin_lock_init(&np->tx_lock); + spin_lock_init(&np->rx_lock); + + skb_queue_head_init(&np->rx_batch); + np->rx_target = RX_DFL_MIN_TARGET; + np->rx_min_target = RX_DFL_MIN_TARGET; + np->rx_max_target = RX_MAX_TARGET; + + init_timer(&np->rx_refill_timer); + np->rx_refill_timer.data = (unsigned long)netdev; + np->rx_refill_timer.function = rx_refill_timeout; + + /* Initialise tx_skbs as a free chain containing every entry. */ + np->tx_skb_freelist = 0; + for (i = 0; i < NET_TX_RING_SIZE; i++) { + np->tx_skbs[i].link = i+1; + np->grant_tx_ref[i] = GRANT_INVALID_REF; + } + + /* Clear out rx_skbs */ + for (i = 0; i < NET_RX_RING_SIZE; i++) { + np->rx_skbs[i] = NULL; + np->grant_rx_ref[i] = GRANT_INVALID_REF; + } + + /* A grant for every tx ring slot */ + if (gnttab_alloc_grant_references(TX_MAX_TARGET, + &np->gref_tx_head) < 0) { + printk(KERN_ALERT "#### netfront can't alloc tx grant refs\n"); + err = -ENOMEM; + goto exit; + } + /* A grant for every rx ring slot */ + if (gnttab_alloc_grant_references(RX_MAX_TARGET, + &np->gref_rx_head) < 0) { + printk(KERN_ALERT "#### netfront can't alloc rx grant refs\n"); + err = -ENOMEM; + goto exit_free_tx; + } + + netdev->open = xennet_open; + netdev->hard_start_xmit = xennet_start_xmit; + netdev->stop = xennet_close; + netdev->get_stats = xennet_get_stats; + netdev->poll = xennet_poll; + netdev->uninit = xennet_uninit; + netdev->change_mtu = xennet_change_mtu; + netdev->weight = 64; + netdev->features = NETIF_F_IP_CSUM; + + SET_ETHTOOL_OPS(netdev, &xennet_ethtool_ops); + SET_MODULE_OWNER(netdev); + SET_NETDEV_DEV(netdev, &dev->dev); + + np->netdev = netdev; + + netif_carrier_off(netdev); + + return netdev; + + exit_free_tx: + gnttab_free_grant_references(np->gref_tx_head); + exit: + free_netdev(netdev); + return ERR_PTR(err); +} + +/** + * Entry point to this code when a new device is created. Allocate the basic + * structures and the ring buffers for communication with the backend, and + * inform the backend of the appropriate details for those. + */ +static int __devinit netfront_probe(struct xenbus_device *dev, + const struct xenbus_device_id *id) +{ + int err; + struct net_device *netdev; + struct netfront_info *info; + + netdev = xennet_create_dev(dev); + if (IS_ERR(netdev)) { + err = PTR_ERR(netdev); + xenbus_dev_fatal(dev, err, "creating netdev"); + return err; + } + + info = netdev_priv(netdev); + dev->dev.driver_data = info; + + err = register_netdev(info->netdev); + if (err) { + printk(KERN_WARNING "%s: register_netdev err=%d\n", + __func__, err); + goto fail; + } + + err = xennet_sysfs_addif(info->netdev); + if (err) { + unregister_netdev(info->netdev); + printk(KERN_WARNING "%s: add sysfs failed err=%d\n", + __func__, err); + goto fail; + } + + return 0; + + fail: + free_netdev(netdev); + dev->dev.driver_data = NULL; + return err; +} + +static void xennet_end_access(int ref, void *page) +{ + /* This frees the page as a side-effect */ + if (ref != GRANT_INVALID_REF) + gnttab_end_foreign_access(ref, 0, (unsigned long)page); +} + +static void xennet_disconnect_backend(struct netfront_info *info) +{ + /* Stop old i/f to prevent errors whilst we rebuild the state. */ + spin_lock_bh(&info->rx_lock); + spin_lock_irq(&info->tx_lock); + netif_carrier_off(info->netdev); + spin_unlock_irq(&info->tx_lock); + spin_unlock_bh(&info->rx_lock); + + if (info->netdev->irq) + unbind_from_irqhandler(info->netdev->irq, info->netdev); + info->evtchn = info->netdev->irq = 0; + + /* End access and free the pages */ + xennet_end_access(info->tx_ring_ref, info->tx.sring); + xennet_end_access(info->rx_ring_ref, info->rx.sring); + + info->tx_ring_ref = GRANT_INVALID_REF; + info->rx_ring_ref = GRANT_INVALID_REF; + info->tx.sring = NULL; + info->rx.sring = NULL; +} + +/** + * We are reconnecting to the backend, due to a suspend/resume, or a backend + * driver restart. We tear down our netif structure and recreate it, but + * leave the device-layer structures intact so that this is transparent to the + * rest of the kernel. + */ +static int netfront_resume(struct xenbus_device *dev) +{ + struct netfront_info *info = dev->dev.driver_data; + + dev_dbg(&dev->dev, "%s\n", dev->nodename); + + xennet_disconnect_backend(info); + return 0; +} + +static int xen_net_read_mac(struct xenbus_device *dev, u8 mac[]) +{ + char *s, *e, *macstr; + int i; + + macstr = s = xenbus_read(XBT_NIL, dev->nodename, "mac", NULL); + if (IS_ERR(macstr)) + return PTR_ERR(macstr); + + for (i = 0; i < ETH_ALEN; i++) { + mac[i] = simple_strtoul(s, &e, 16); + if ((s == e) || (*e != ((i == ETH_ALEN-1) ? '\0' : ':'))) { + kfree(macstr); + return -ENOENT; + } + s = e+1; + } + + kfree(macstr); + return 0; +} + +static irqreturn_t xennet_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = dev_id; + struct netfront_info *np = netdev_priv(dev); + unsigned long flags; + + spin_lock_irqsave(&np->tx_lock, flags); + + if (likely(netif_carrier_ok(dev))) { + xennet_tx_buf_gc(dev); + /* Under tx_lock: protects access to rx shared-ring indexes. */ + if (RING_HAS_UNCONSUMED_RESPONSES(&np->rx)) + netif_rx_schedule(dev); + } + + spin_unlock_irqrestore(&np->tx_lock, flags); + + return IRQ_HANDLED; +} + +static int setup_netfront(struct xenbus_device *dev, struct netfront_info *info) +{ + struct xen_netif_tx_sring *txs; + struct xen_netif_rx_sring *rxs; + int err; + struct net_device *netdev = info->netdev; + + info->tx_ring_ref = GRANT_INVALID_REF; + info->rx_ring_ref = GRANT_INVALID_REF; + info->rx.sring = NULL; + info->tx.sring = NULL; + netdev->irq = 0; + + err = xen_net_read_mac(dev, netdev->dev_addr); + if (err) { + xenbus_dev_fatal(dev, err, "parsing %s/mac", dev->nodename); + goto fail; + } + + txs = (struct xen_netif_tx_sring *)get_zeroed_page(GFP_KERNEL); + if (!txs) { + err = -ENOMEM; + xenbus_dev_fatal(dev, err, "allocating tx ring page"); + goto fail; + } + SHARED_RING_INIT(txs); + FRONT_RING_INIT(&info->tx, txs, PAGE_SIZE); + + err = xenbus_grant_ring(dev, virt_to_mfn(txs)); + if (err < 0) { + free_page((unsigned long)txs); + goto fail; + } + + info->tx_ring_ref = err; + rxs = (struct xen_netif_rx_sring *)get_zeroed_page(GFP_KERNEL); + if (!rxs) { + err = -ENOMEM; + xenbus_dev_fatal(dev, err, "allocating rx ring page"); + goto fail; + } + SHARED_RING_INIT(rxs); + FRONT_RING_INIT(&info->rx, rxs, PAGE_SIZE); + + err = xenbus_grant_ring(dev, virt_to_mfn(rxs)); + if (err < 0) { + free_page((unsigned long)rxs); + goto fail; + } + info->rx_ring_ref = err; + + err = xenbus_alloc_evtchn(dev, &info->evtchn); + if (err) + goto fail; + + err = bind_evtchn_to_irqhandler(info->evtchn, xennet_interrupt, + IRQF_SAMPLE_RANDOM, netdev->name, + netdev); + if (err < 0) + goto fail; + netdev->irq = err; + return 0; + + fail: + return err; +} + +/* Common code used when first setting up, and when resuming. */ +static int talk_to_backend(struct xenbus_device *dev, + struct netfront_info *info) +{ + const char *message; + struct xenbus_transaction xbt; + int err; + + /* Create shared ring, alloc event channel. */ + err = setup_netfront(dev, info); + if (err) + goto out; + +again: + err = xenbus_transaction_start(&xbt); + if (err) { + xenbus_dev_fatal(dev, err, "starting transaction"); + goto destroy_ring; + } + + err = xenbus_printf(xbt, dev->nodename, "tx-ring-ref", "%u", + info->tx_ring_ref); + if (err) { + message = "writing tx ring-ref"; + goto abort_transaction; + } + err = xenbus_printf(xbt, dev->nodename, "rx-ring-ref", "%u", + info->rx_ring_ref); + if (err) { + message = "writing rx ring-ref"; + goto abort_transaction; + } + err = xenbus_printf(xbt, dev->nodename, + "event-channel", "%u", info->evtchn); + if (err) { + message = "writing event-channel"; + goto abort_transaction; + } + + err = xenbus_printf(xbt, dev->nodename, "request-rx-copy", "%u", + 1); + if (err) { + message = "writing request-rx-copy"; + goto abort_transaction; + } + + err = xenbus_printf(xbt, dev->nodename, "feature-rx-notify", "%d", 1); + if (err) { + message = "writing feature-rx-notify"; + goto abort_transaction; + } + + err = xenbus_printf(xbt, dev->nodename, "feature-sg", "%d", 1); + if (err) { + message = "writing feature-sg"; + goto abort_transaction; + } + + err = xenbus_printf(xbt, dev->nodename, "feature-gso-tcpv4", "%d", 1); + if (err) { + message = "writing feature-gso-tcpv4"; + goto abort_transaction; + } + + err = xenbus_transaction_end(xbt, 0); + if (err) { + if (err == -EAGAIN) + goto again; + xenbus_dev_fatal(dev, err, "completing transaction"); + goto destroy_ring; + } + + return 0; + + abort_transaction: + xenbus_transaction_end(xbt, 1); + xenbus_dev_fatal(dev, err, "%s", message); + destroy_ring: + xennet_disconnect_backend(info); + out: + return err; +} + +static int xennet_set_sg(struct net_device *dev, u32 data) +{ + if (data) { + struct netfront_info *np = netdev_priv(dev); + int val; + + if (xenbus_scanf(XBT_NIL, np->xbdev->otherend, "feature-sg", + "%d", &val) < 0) + val = 0; + if (!val) + return -ENOSYS; + } else if (dev->mtu > ETH_DATA_LEN) + dev->mtu = ETH_DATA_LEN; + + return ethtool_op_set_sg(dev, data); +} + +static int xennet_set_tso(struct net_device *dev, u32 data) +{ + if (data) { + struct netfront_info *np = netdev_priv(dev); + int val; + + if (xenbus_scanf(XBT_NIL, np->xbdev->otherend, + "feature-gso-tcpv4", "%d", &val) < 0) + val = 0; + if (!val) + return -ENOSYS; + } + + return ethtool_op_set_tso(dev, data); +} + +static void xennet_set_features(struct net_device *dev) +{ + /* Turn off all GSO bits except ROBUST. */ + dev->features &= (1 << NETIF_F_GSO_SHIFT) - 1; + dev->features |= NETIF_F_GSO_ROBUST; + xennet_set_sg(dev, 0); + + /* We need checksum offload to enable scatter/gather and TSO. */ + if (!(dev->features & NETIF_F_IP_CSUM)) + return; + + if (!xennet_set_sg(dev, 1)) + xennet_set_tso(dev, 1); +} + +static int xennet_connect(struct net_device *dev) +{ + struct netfront_info *np = netdev_priv(dev); + int i, requeue_idx, err; + struct sk_buff *skb; + grant_ref_t ref; + struct xen_netif_rx_request *req; + unsigned int feature_rx_copy; + + err = xenbus_scanf(XBT_NIL, np->xbdev->otherend, + "feature-rx-copy", "%u", &feature_rx_copy); + if (err != 1) + feature_rx_copy = 0; + + if (!feature_rx_copy) { + dev_info(&dev->dev, + "backend does not support copying recieve path"); + return -ENODEV; + } + + err = talk_to_backend(np->xbdev, np); + if (err) + return err; + + xennet_set_features(dev); + + spin_lock_bh(&np->rx_lock); + spin_lock_irq(&np->tx_lock); + + /* Step 1: Discard all pending TX packet fragments. */ + xennet_release_tx_bufs(np); + + /* Step 2: Rebuild the RX buffer freelist and the RX ring itself. */ + for (requeue_idx = 0, i = 0; i < NET_RX_RING_SIZE; i++) { + if (!np->rx_skbs[i]) + continue; + + skb = np->rx_skbs[requeue_idx] = xennet_get_rx_skb(np, i); + ref = np->grant_rx_ref[requeue_idx] = xennet_get_rx_ref(np, i); + req = RING_GET_REQUEST(&np->rx, requeue_idx); + + gnttab_grant_foreign_access_ref( + ref, np->xbdev->otherend_id, + pfn_to_mfn(page_to_pfn(skb_shinfo(skb)-> + frags->page)), + 0); + req->gref = ref; + req->id = requeue_idx; + + requeue_idx++; + } + + np->rx.req_prod_pvt = requeue_idx; + + /* + * Step 3: All public and private state should now be sane. Get + * ready to start sending and receiving packets and give the driver + * domain a kick because we've probably just requeued some + * packets. + */ + netif_carrier_on(np->netdev); + notify_remote_via_irq(np->netdev->irq); + xennet_tx_buf_gc(dev); + xennet_alloc_rx_buffers(dev); + + spin_unlock_irq(&np->tx_lock); + spin_unlock_bh(&np->rx_lock); + + return 0; +} + +/** + * Callback received when the backend's state changes. + */ +static void backend_changed(struct xenbus_device *dev, + enum xenbus_state backend_state) +{ + struct netfront_info *np = dev->dev.driver_data; + struct net_device *netdev = np->netdev; + + dev_dbg(&dev->dev, "%s\n", xenbus_strstate(backend_state)); + + switch (backend_state) { + case XenbusStateInitialising: + case XenbusStateInitialised: + case XenbusStateConnected: + case XenbusStateUnknown: + case XenbusStateClosed: + break; + + case XenbusStateInitWait: + if (dev->state != XenbusStateInitialising) + break; + if (xennet_connect(netdev) != 0) + break; + xenbus_switch_state(dev, XenbusStateConnected); + break; + + case XenbusStateClosing: + xenbus_frontend_closed(dev); + break; + } +} + +static struct ethtool_ops xennet_ethtool_ops = +{ + .get_tx_csum = ethtool_op_get_tx_csum, + .set_tx_csum = ethtool_op_set_tx_csum, + .get_sg = ethtool_op_get_sg, + .set_sg = xennet_set_sg, + .get_tso = ethtool_op_get_tso, + .set_tso = xennet_set_tso, + .get_link = ethtool_op_get_link, +}; + +#ifdef CONFIG_SYSFS +static ssize_t show_rxbuf_min(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct net_device *netdev = to_net_dev(dev); + struct netfront_info *info = netdev_priv(netdev); + + return sprintf(buf, "%u\n", info->rx_min_target); +} + +static ssize_t store_rxbuf_min(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct net_device *netdev = to_net_dev(dev); + struct netfront_info *np = netdev_priv(netdev); + char *endp; + unsigned long target; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + target = simple_strtoul(buf, &endp, 0); + if (endp == buf) + return -EBADMSG; + + if (target < RX_MIN_TARGET) + target = RX_MIN_TARGET; + if (target > RX_MAX_TARGET) + target = RX_MAX_TARGET; + + spin_lock_bh(&np->rx_lock); + if (target > np->rx_max_target) + np->rx_max_target = target; + np->rx_min_target = target; + if (target > np->rx_target) + np->rx_target = target; + + xennet_alloc_rx_buffers(netdev); + + spin_unlock_bh(&np->rx_lock); + return len; +} + +static ssize_t show_rxbuf_max(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct net_device *netdev = to_net_dev(dev); + struct netfront_info *info = netdev_priv(netdev); + + return sprintf(buf, "%u\n", info->rx_max_target); +} + +static ssize_t store_rxbuf_max(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct net_device *netdev = to_net_dev(dev); + struct netfront_info *np = netdev_priv(netdev); + char *endp; + unsigned long target; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + target = simple_strtoul(buf, &endp, 0); + if (endp == buf) + return -EBADMSG; + + if (target < RX_MIN_TARGET) + target = RX_MIN_TARGET; + if (target > RX_MAX_TARGET) + target = RX_MAX_TARGET; + + spin_lock_bh(&np->rx_lock); + if (target < np->rx_min_target) + np->rx_min_target = target; + np->rx_max_target = target; + if (target < np->rx_target) + np->rx_target = target; + + xennet_alloc_rx_buffers(netdev); + + spin_unlock_bh(&np->rx_lock); + return len; +} + +static ssize_t show_rxbuf_cur(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct net_device *netdev = to_net_dev(dev); + struct netfront_info *info = netdev_priv(netdev); + + return sprintf(buf, "%u\n", info->rx_target); +} + +static struct device_attribute xennet_attrs[] = { + __ATTR(rxbuf_min, S_IRUGO|S_IWUSR, show_rxbuf_min, store_rxbuf_min), + __ATTR(rxbuf_max, S_IRUGO|S_IWUSR, show_rxbuf_max, store_rxbuf_max), + __ATTR(rxbuf_cur, S_IRUGO, show_rxbuf_cur, NULL), +}; + +static int xennet_sysfs_addif(struct net_device *netdev) +{ + int i; + int err; + + for (i = 0; i < ARRAY_SIZE(xennet_attrs); i++) { + err = device_create_file(&netdev->dev, + &xennet_attrs[i]); + if (err) + goto fail; + } + return 0; + + fail: + while (--i >= 0) + device_remove_file(&netdev->dev, &xennet_attrs[i]); + return err; +} + +static void xennet_sysfs_delif(struct net_device *netdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(xennet_attrs); i++) + device_remove_file(&netdev->dev, &xennet_attrs[i]); +} + +#endif /* CONFIG_SYSFS */ + +static struct xenbus_device_id netfront_ids[] = { + { "vif" }, + { "" } +}; + + +static int __devexit xennet_remove(struct xenbus_device *dev) +{ + struct netfront_info *info = dev->dev.driver_data; + + dev_dbg(&dev->dev, "%s\n", dev->nodename); + + unregister_netdev(info->netdev); + + xennet_disconnect_backend(info); + + del_timer_sync(&info->rx_refill_timer); + + xennet_sysfs_delif(info->netdev); + + free_netdev(info->netdev); + + return 0; +} + +static struct xenbus_driver netfront = { + .name = "vif", + .owner = THIS_MODULE, + .ids = netfront_ids, + .probe = netfront_probe, + .remove = __devexit_p(xennet_remove), + .resume = netfront_resume, + .otherend_changed = backend_changed, +}; + +static int __init netif_init(void) +{ + if (!is_running_on_xen()) + return -ENODEV; + + if (is_initial_xendomain()) + return 0; + + printk(KERN_INFO "Initialising Xen virtual ethernet driver.\n"); + + return xenbus_register_frontend(&netfront); +} +module_init(netif_init); + + +static void __exit netif_exit(void) +{ + if (is_initial_xendomain()) + return; + + return xenbus_unregister_driver(&netfront); +} +module_exit(netif_exit); + +MODULE_DESCRIPTION("Xen virtual network device frontend"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pnp/pnpbios/core.c b/drivers/pnp/pnpbios/core.c index 03baf1c..ed112ee 100644 --- a/drivers/pnp/pnpbios/core.c +++ b/drivers/pnp/pnpbios/core.c @@ -147,7 +147,7 @@ static int pnp_dock_event(int dock, struct pnp_docking_station_info *info) info->location_id, info->serial, info->capabilities); envp[i] = NULL; - value = call_usermodehelper (argv [0], argv, envp, 0); + value = call_usermodehelper (argv [0], argv, envp, UMH_WAIT_EXEC); kfree (buf); kfree (envp); return 0; diff --git a/drivers/sbus/char/bbc_envctrl.c b/drivers/sbus/char/bbc_envctrl.c index a54e414..e821a15 100644 --- a/drivers/sbus/char/bbc_envctrl.c +++ b/drivers/sbus/char/bbc_envctrl.c @@ -7,6 +7,7 @@ #include <linux/kthread.h> #include <linux/delay.h> #include <linux/kmod.h> +#include <linux/reboot.h> #include <asm/oplib.h> #include <asm/ebus.h> @@ -170,8 +171,6 @@ static void get_current_temps(struct bbc_cpu_temperature *tp) static void do_envctrl_shutdown(struct bbc_cpu_temperature *tp) { static int shutting_down = 0; - static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; - char *argv[] = { "/sbin/shutdown", "-h", "now", NULL }; char *type = "???"; s8 val = -1; @@ -195,7 +194,7 @@ static void do_envctrl_shutdown(struct bbc_cpu_temperature *tp) printk(KERN_CRIT "kenvctrld: Shutting down the system now.\n"); shutting_down = 1; - if (call_usermodehelper("/sbin/shutdown", argv, envp, 0) < 0) + if (orderly_poweroff(true) < 0) printk(KERN_CRIT "envctrl: shutdown execution failed\n"); } diff --git a/drivers/sbus/char/envctrl.c b/drivers/sbus/char/envctrl.c index 8328aca..dadabef 100644 --- a/drivers/sbus/char/envctrl.c +++ b/drivers/sbus/char/envctrl.c @@ -26,6 +26,7 @@ #include <linux/ioport.h> #include <linux/miscdevice.h> #include <linux/kmod.h> +#include <linux/reboot.h> #include <asm/ebus.h> #include <asm/uaccess.h> @@ -966,10 +967,6 @@ static struct i2c_child_t *envctrl_get_i2c_child(unsigned char mon_type) static void envctrl_do_shutdown(void) { static int inprog = 0; - static char *envp[] = { - "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL }; - char *argv[] = { - "/sbin/shutdown", "-h", "now", NULL }; int ret; if (inprog != 0) @@ -977,7 +974,7 @@ static void envctrl_do_shutdown(void) inprog = 1; printk(KERN_CRIT "kenvctrld: WARNING: Shutting down the system now.\n"); - ret = call_usermodehelper("/sbin/shutdown", argv, envp, 0); + ret = orderly_poweroff(true); if (ret < 0) { printk(KERN_CRIT "kenvctrld: WARNING: system shutdown failed!\n"); inprog = 0; /* unlikely to succeed, but we could try again */ diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile new file mode 100644 index 0000000..56592f0 --- /dev/null +++ b/drivers/xen/Makefile @@ -0,0 +1,2 @@ +obj-y += grant-table.o +obj-y += xenbus/ diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c new file mode 100644 index 0000000..ea94dba --- /dev/null +++ b/drivers/xen/grant-table.c @@ -0,0 +1,582 @@ +/****************************************************************************** + * grant_table.c + * + * Granting foreign access to our memory reservation. + * + * Copyright (c) 2005-2006, Christopher Clark + * Copyright (c) 2004-2005, K A Fraser + * + * 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; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/vmalloc.h> +#include <linux/uaccess.h> + +#include <xen/interface/xen.h> +#include <xen/page.h> +#include <xen/grant_table.h> + +#include <asm/pgtable.h> +#include <asm/sync_bitops.h> + + +/* External tools reserve first few grant table entries. */ +#define NR_RESERVED_ENTRIES 8 +#define GNTTAB_LIST_END 0xffffffff +#define GREFS_PER_GRANT_FRAME (PAGE_SIZE / sizeof(struct grant_entry)) + +static grant_ref_t **gnttab_list; +static unsigned int nr_grant_frames; +static unsigned int boot_max_nr_grant_frames; +static int gnttab_free_count; +static grant_ref_t gnttab_free_head; +static DEFINE_SPINLOCK(gnttab_list_lock); + +static struct grant_entry *shared; + +static struct gnttab_free_callback *gnttab_free_callback_list; + +static int gnttab_expand(unsigned int req_entries); + +#define RPP (PAGE_SIZE / sizeof(grant_ref_t)) + +static inline grant_ref_t *__gnttab_entry(grant_ref_t entry) +{ + return &gnttab_list[(entry) / RPP][(entry) % RPP]; +} +/* This can be used as an l-value */ +#define gnttab_entry(entry) (*__gnttab_entry(entry)) + +static int get_free_entries(unsigned count) +{ + unsigned long flags; + int ref, rc; + grant_ref_t head; + + spin_lock_irqsave(&gnttab_list_lock, flags); + + if ((gnttab_free_count < count) && + ((rc = gnttab_expand(count - gnttab_free_count)) < 0)) { + spin_unlock_irqrestore(&gnttab_list_lock, flags); + return rc; + } + + ref = head = gnttab_free_head; + gnttab_free_count -= count; + while (count-- > 1) + head = gnttab_entry(head); + gnttab_free_head = gnttab_entry(head); + gnttab_entry(head) = GNTTAB_LIST_END; + + spin_unlock_irqrestore(&gnttab_list_lock, flags); + + return ref; +} + +static void do_free_callbacks(void) +{ + struct gnttab_free_callback *callback, *next; + + callback = gnttab_free_callback_list; + gnttab_free_callback_list = NULL; + + while (callback != NULL) { + next = callback->next; + if (gnttab_free_count >= callback->count) { + callback->next = NULL; + callback->fn(callback->arg); + } else { + callback->next = gnttab_free_callback_list; + gnttab_free_callback_list = callback; + } + callback = next; + } +} + +static inline void check_free_callbacks(void) +{ + if (unlikely(gnttab_free_callback_list)) + do_free_callbacks(); +} + +static void put_free_entry(grant_ref_t ref) +{ + unsigned long flags; + spin_lock_irqsave(&gnttab_list_lock, flags); + gnttab_entry(ref) = gnttab_free_head; + gnttab_free_head = ref; + gnttab_free_count++; + check_free_callbacks(); + spin_unlock_irqrestore(&gnttab_list_lock, flags); +} + +static void update_grant_entry(grant_ref_t ref, domid_t domid, + unsigned long frame, unsigned flags) +{ + /* + * Introducing a valid entry into the grant table: + * 1. Write ent->domid. + * 2. Write ent->frame: + * GTF_permit_access: Frame to which access is permitted. + * GTF_accept_transfer: Pseudo-phys frame slot being filled by new + * frame, or zero if none. + * 3. Write memory barrier (WMB). + * 4. Write ent->flags, inc. valid type. + */ + shared[ref].frame = frame; + shared[ref].domid = domid; + wmb(); + shared[ref].flags = flags; +} + +/* + * Public grant-issuing interface functions + */ +void gnttab_grant_foreign_access_ref(grant_ref_t ref, domid_t domid, + unsigned long frame, int readonly) +{ + update_grant_entry(ref, domid, frame, + GTF_permit_access | (readonly ? GTF_readonly : 0)); +} +EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access_ref); + +int gnttab_grant_foreign_access(domid_t domid, unsigned long frame, + int readonly) +{ + int ref; + + ref = get_free_entries(1); + if (unlikely(ref < 0)) + return -ENOSPC; + + gnttab_grant_foreign_access_ref(ref, domid, frame, readonly); + + return ref; +} +EXPORT_SYMBOL_GPL(gnttab_grant_foreign_access); + +int gnttab_query_foreign_access(grant_ref_t ref) +{ + u16 nflags; + + nflags = shared[ref].flags; + + return (nflags & (GTF_reading|GTF_writing)); +} +EXPORT_SYMBOL_GPL(gnttab_query_foreign_access); + +int gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly) +{ + u16 flags, nflags; + + nflags = shared[ref].flags; + do { + flags = nflags; + if (flags & (GTF_reading|GTF_writing)) { + printk(KERN_ALERT "WARNING: g.e. still in use!\n"); + return 0; + } + } while ((nflags = sync_cmpxchg(&shared[ref].flags, flags, 0)) != flags); + + return 1; +} +EXPORT_SYMBOL_GPL(gnttab_end_foreign_access_ref); + +void gnttab_end_foreign_access(grant_ref_t ref, int readonly, + unsigned long page) +{ + if (gnttab_end_foreign_access_ref(ref, readonly)) { + put_free_entry(ref); + if (page != 0) + free_page(page); + } else { + /* XXX This needs to be fixed so that the ref and page are + placed on a list to be freed up later. */ + printk(KERN_WARNING + "WARNING: leaking g.e. and page still in use!\n"); + } +} +EXPORT_SYMBOL_GPL(gnttab_end_foreign_access); + +int gnttab_grant_foreign_transfer(domid_t domid, unsigned long pfn) +{ + int ref; + + ref = get_free_entries(1); + if (unlikely(ref < 0)) + return -ENOSPC; + gnttab_grant_foreign_transfer_ref(ref, domid, pfn); + + return ref; +} +EXPORT_SYMBOL_GPL(gnttab_grant_foreign_transfer); + +void gnttab_grant_foreign_transfer_ref(grant_ref_t ref, domid_t domid, + unsigned long pfn) +{ + update_grant_entry(ref, domid, pfn, GTF_accept_transfer); +} +EXPORT_SYMBOL_GPL(gnttab_grant_foreign_transfer_ref); + +unsigned long gnttab_end_foreign_transfer_ref(grant_ref_t ref) +{ + unsigned long frame; + u16 flags; + + /* + * If a transfer is not even yet started, try to reclaim the grant + * reference and return failure (== 0). + */ + while (!((flags = shared[ref].flags) & GTF_transfer_committed)) { + if (sync_cmpxchg(&shared[ref].flags, flags, 0) == flags) + return 0; + cpu_relax(); + } + + /* If a transfer is in progress then wait until it is completed. */ + while (!(flags & GTF_transfer_completed)) { + flags = shared[ref].flags; + cpu_relax(); + } + + rmb(); /* Read the frame number /after/ reading completion status. */ + frame = shared[ref].frame; + BUG_ON(frame == 0); + + return frame; +} +EXPORT_SYMBOL_GPL(gnttab_end_foreign_transfer_ref); + +unsigned long gnttab_end_foreign_transfer(grant_ref_t ref) +{ + unsigned long frame = gnttab_end_foreign_transfer_ref(ref); + put_free_entry(ref); + return frame; +} +EXPORT_SYMBOL_GPL(gnttab_end_foreign_transfer); + +void gnttab_free_grant_reference(grant_ref_t ref) +{ + put_free_entry(ref); +} +EXPORT_SYMBOL_GPL(gnttab_free_grant_reference); + +void gnttab_free_grant_references(grant_ref_t head) +{ + grant_ref_t ref; + unsigned long flags; + int count = 1; + if (head == GNTTAB_LIST_END) + return; + spin_lock_irqsave(&gnttab_list_lock, flags); + ref = head; + while (gnttab_entry(ref) != GNTTAB_LIST_END) { + ref = gnttab_entry(ref); + count++; + } + gnttab_entry(ref) = gnttab_free_head; + gnttab_free_head = head; + gnttab_free_count += count; + check_free_callbacks(); + spin_unlock_irqrestore(&gnttab_list_lock, flags); +} +EXPORT_SYMBOL_GPL(gnttab_free_grant_references); + +int gnttab_alloc_grant_references(u16 count, grant_ref_t *head) +{ + int h = get_free_entries(count); + + if (h < 0) + return -ENOSPC; + + *head = h; + + return 0; +} +EXPORT_SYMBOL_GPL(gnttab_alloc_grant_references); + +int gnttab_empty_grant_references(const grant_ref_t *private_head) +{ + return (*private_head == GNTTAB_LIST_END); +} +EXPORT_SYMBOL_GPL(gnttab_empty_grant_references); + +int gnttab_claim_grant_reference(grant_ref_t *private_head) +{ + grant_ref_t g = *private_head; + if (unlikely(g == GNTTAB_LIST_END)) + return -ENOSPC; + *private_head = gnttab_entry(g); + return g; +} +EXPORT_SYMBOL_GPL(gnttab_claim_grant_reference); + +void gnttab_release_grant_reference(grant_ref_t *private_head, + grant_ref_t release) +{ + gnttab_entry(release) = *private_head; + *private_head = release; +} +EXPORT_SYMBOL_GPL(gnttab_release_grant_reference); + +void gnttab_request_free_callback(struct gnttab_free_callback *callback, + void (*fn)(void *), void *arg, u16 count) +{ + unsigned long flags; + spin_lock_irqsave(&gnttab_list_lock, flags); + if (callback->next) + goto out; + callback->fn = fn; + callback->arg = arg; + callback->count = count; + callback->next = gnttab_free_callback_list; + gnttab_free_callback_list = callback; + check_free_callbacks(); +out: + spin_unlock_irqrestore(&gnttab_list_lock, flags); +} +EXPORT_SYMBOL_GPL(gnttab_request_free_callback); + +void gnttab_cancel_free_callback(struct gnttab_free_callback *callback) +{ + struct gnttab_free_callback **pcb; + unsigned long flags; + + spin_lock_irqsave(&gnttab_list_lock, flags); + for (pcb = &gnttab_free_callback_list; *pcb; pcb = &(*pcb)->next) { + if (*pcb == callback) { + *pcb = callback->next; + break; + } + } + spin_unlock_irqrestore(&gnttab_list_lock, flags); +} +EXPORT_SYMBOL_GPL(gnttab_cancel_free_callback); + +static int grow_gnttab_list(unsigned int more_frames) +{ + unsigned int new_nr_grant_frames, extra_entries, i; + + new_nr_grant_frames = nr_grant_frames + more_frames; + extra_entries = more_frames * GREFS_PER_GRANT_FRAME; + + for (i = nr_grant_frames; i < new_nr_grant_frames; i++) { + gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_ATOMIC); + if (!gnttab_list[i]) + goto grow_nomem; + } + + + for (i = GREFS_PER_GRANT_FRAME * nr_grant_frames; + i < GREFS_PER_GRANT_FRAME * new_nr_grant_frames - 1; i++) + gnttab_entry(i) = i + 1; + + gnttab_entry(i) = gnttab_free_head; + gnttab_free_head = GREFS_PER_GRANT_FRAME * nr_grant_frames; + gnttab_free_count += extra_entries; + + nr_grant_frames = new_nr_grant_frames; + + check_free_callbacks(); + + return 0; + +grow_nomem: + for ( ; i >= nr_grant_frames; i--) + free_page((unsigned long) gnttab_list[i]); + return -ENOMEM; +} + +static unsigned int __max_nr_grant_frames(void) +{ + struct gnttab_query_size query; + int rc; + + query.dom = DOMID_SELF; + + rc = HYPERVISOR_grant_table_op(GNTTABOP_query_size, &query, 1); + if ((rc < 0) || (query.status != GNTST_okay)) + return 4; /* Legacy max supported number of frames */ + + return query.max_nr_frames; +} + +static inline unsigned int max_nr_grant_frames(void) +{ + unsigned int xen_max = __max_nr_grant_frames(); + + if (xen_max > boot_max_nr_grant_frames) + return boot_max_nr_grant_frames; + return xen_max; +} + +static int map_pte_fn(pte_t *pte, struct page *pmd_page, + unsigned long addr, void *data) +{ + unsigned long **frames = (unsigned long **)data; + + set_pte_at(&init_mm, addr, pte, mfn_pte((*frames)[0], PAGE_KERNEL)); + (*frames)++; + return 0; +} + +static int unmap_pte_fn(pte_t *pte, struct page *pmd_page, + unsigned long addr, void *data) +{ + + set_pte_at(&init_mm, addr, pte, __pte(0)); + return 0; +} + +static int gnttab_map(unsigned int start_idx, unsigned int end_idx) +{ + struct gnttab_setup_table setup; + unsigned long *frames; + unsigned int nr_gframes = end_idx + 1; + int rc; + + frames = kmalloc(nr_gframes * sizeof(unsigned long), GFP_ATOMIC); + if (!frames) + return -ENOMEM; + + setup.dom = DOMID_SELF; + setup.nr_frames = nr_gframes; + setup.frame_list = frames; + + rc = HYPERVISOR_grant_table_op(GNTTABOP_setup_table, &setup, 1); + if (rc == -ENOSYS) { + kfree(frames); + return -ENOSYS; + } + + BUG_ON(rc || setup.status); + + if (shared == NULL) { + struct vm_struct *area; + area = alloc_vm_area(PAGE_SIZE * max_nr_grant_frames()); + BUG_ON(area == NULL); + shared = area->addr; + } + rc = apply_to_page_range(&init_mm, (unsigned long)shared, + PAGE_SIZE * nr_gframes, + map_pte_fn, &frames); + BUG_ON(rc); + frames -= nr_gframes; /* adjust after map_pte_fn() */ + + kfree(frames); + + return 0; +} + +static int gnttab_resume(void) +{ + if (max_nr_grant_frames() < nr_grant_frames) + return -ENOSYS; + return gnttab_map(0, nr_grant_frames - 1); +} + +static int gnttab_suspend(void) +{ + apply_to_page_range(&init_mm, (unsigned long)shared, + PAGE_SIZE * nr_grant_frames, + unmap_pte_fn, NULL); + + return 0; +} + +static int gnttab_expand(unsigned int req_entries) +{ + int rc; + unsigned int cur, extra; + + cur = nr_grant_frames; + extra = ((req_entries + (GREFS_PER_GRANT_FRAME-1)) / + GREFS_PER_GRANT_FRAME); + if (cur + extra > max_nr_grant_frames()) + return -ENOSPC; + + rc = gnttab_map(cur, cur + extra - 1); + if (rc == 0) + rc = grow_gnttab_list(extra); + + return rc; +} + +static int __devinit gnttab_init(void) +{ + int i; + unsigned int max_nr_glist_frames; + unsigned int nr_init_grefs; + + if (!is_running_on_xen()) + return -ENODEV; + + nr_grant_frames = 1; + boot_max_nr_grant_frames = __max_nr_grant_frames(); + + /* Determine the maximum number of frames required for the + * grant reference free list on the current hypervisor. + */ + max_nr_glist_frames = (boot_max_nr_grant_frames * + GREFS_PER_GRANT_FRAME / + (PAGE_SIZE / sizeof(grant_ref_t))); + + gnttab_list = kmalloc(max_nr_glist_frames * sizeof(grant_ref_t *), + GFP_KERNEL); + if (gnttab_list == NULL) + return -ENOMEM; + + for (i = 0; i < nr_grant_frames; i++) { + gnttab_list[i] = (grant_ref_t *)__get_free_page(GFP_KERNEL); + if (gnttab_list[i] == NULL) + goto ini_nomem; + } + + if (gnttab_resume() < 0) + return -ENODEV; + + nr_init_grefs = nr_grant_frames * GREFS_PER_GRANT_FRAME; + + for (i = NR_RESERVED_ENTRIES; i < nr_init_grefs - 1; i++) + gnttab_entry(i) = i + 1; + + gnttab_entry(nr_init_grefs - 1) = GNTTAB_LIST_END; + gnttab_free_count = nr_init_grefs - NR_RESERVED_ENTRIES; + gnttab_free_head = NR_RESERVED_ENTRIES; + + printk("Grant table initialized\n"); + return 0; + + ini_nomem: + for (i--; i >= 0; i--) + free_page((unsigned long)gnttab_list[i]); + kfree(gnttab_list); + return -ENOMEM; +} + +core_initcall(gnttab_init); diff --git a/drivers/xen/xenbus/Makefile b/drivers/xen/xenbus/Makefile new file mode 100644 index 0000000..5571f5b --- /dev/null +++ b/drivers/xen/xenbus/Makefile @@ -0,0 +1,7 @@ +obj-y += xenbus.o + +xenbus-objs = +xenbus-objs += xenbus_client.o +xenbus-objs += xenbus_comms.o +xenbus-objs += xenbus_xs.o +xenbus-objs += xenbus_probe.o diff --git a/drivers/xen/xenbus/xenbus_client.c b/drivers/xen/xenbus/xenbus_client.c new file mode 100644 index 0000000..9fd2f70 --- /dev/null +++ b/drivers/xen/xenbus/xenbus_client.c @@ -0,0 +1,569 @@ +/****************************************************************************** + * Client-facing interface for the Xenbus driver. In other words, the + * interface between the Xenbus and the device-specific code, be it the + * frontend or the backend of that driver. + * + * Copyright (C) 2005 XenSource Ltd + * + * 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; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <linux/types.h> +#include <linux/vmalloc.h> +#include <asm/xen/hypervisor.h> +#include <xen/interface/xen.h> +#include <xen/interface/event_channel.h> +#include <xen/events.h> +#include <xen/grant_table.h> +#include <xen/xenbus.h> + +const char *xenbus_strstate(enum xenbus_state state) +{ + static const char *const name[] = { + [ XenbusStateUnknown ] = "Unknown", + [ XenbusStateInitialising ] = "Initialising", + [ XenbusStateInitWait ] = "InitWait", + [ XenbusStateInitialised ] = "Initialised", + [ XenbusStateConnected ] = "Connected", + [ XenbusStateClosing ] = "Closing", + [ XenbusStateClosed ] = "Closed", + }; + return (state < ARRAY_SIZE(name)) ? name[state] : "INVALID"; +} +EXPORT_SYMBOL_GPL(xenbus_strstate); + +/** + * xenbus_watch_path - register a watch + * @dev: xenbus device + * @path: path to watch + * @watch: watch to register + * @callback: callback to register + * + * Register a @watch on the given path, using the given xenbus_watch structure + * for storage, and the given @callback function as the callback. Return 0 on + * success, or -errno on error. On success, the given @path will be saved as + * @watch->node, and remains the caller's to free. On error, @watch->node will + * be NULL, the device will switch to %XenbusStateClosing, and the error will + * be saved in the store. + */ +int xenbus_watch_path(struct xenbus_device *dev, const char *path, + struct xenbus_watch *watch, + void (*callback)(struct xenbus_watch *, + const char **, unsigned int)) +{ + int err; + + watch->node = path; + watch->callback = callback; + + err = register_xenbus_watch(watch); + + if (err) { + watch->node = NULL; + watch->callback = NULL; + xenbus_dev_fatal(dev, err, "adding watch on %s", path); + } + + return err; +} +EXPORT_SYMBOL_GPL(xenbus_watch_path); + + +/** + * xenbus_watch_pathfmt - register a watch on a sprintf-formatted path + * @dev: xenbus device + * @watch: watch to register + * @callback: callback to register + * @pathfmt: format of path to watch + * + * Register a watch on the given @path, using the given xenbus_watch + * structure for storage, and the given @callback function as the callback. + * Return 0 on success, or -errno on error. On success, the watched path + * (@path/@path2) will be saved as @watch->node, and becomes the caller's to + * kfree(). On error, watch->node will be NULL, so the caller has nothing to + * free, the device will switch to %XenbusStateClosing, and the error will be + * saved in the store. + */ +int xenbus_watch_pathfmt(struct xenbus_device *dev, + struct xenbus_watch *watch, + void (*callback)(struct xenbus_watch *, + const char **, unsigned int), + const char *pathfmt, ...) +{ + int err; + va_list ap; + char *path; + + va_start(ap, pathfmt); + path = kvasprintf(GFP_KERNEL, pathfmt, ap); + va_end(ap); + + if (!path) { + xenbus_dev_fatal(dev, -ENOMEM, "allocating path for watch"); + return -ENOMEM; + } + err = xenbus_watch_path(dev, path, watch, callback); + + if (err) + kfree(path); + return err; +} +EXPORT_SYMBOL_GPL(xenbus_watch_pathfmt); + + +/** + * xenbus_switch_state + * @dev: xenbus device + * @xbt: transaction handle + * @state: new state + * + * Advertise in the store a change of the given driver to the given new_state. + * Return 0 on success, or -errno on error. On error, the device will switch + * to XenbusStateClosing, and the error will be saved in the store. + */ +int xenbus_switch_state(struct xenbus_device *dev, enum xenbus_state state) +{ + /* We check whether the state is currently set to the given value, and + if not, then the state is set. We don't want to unconditionally + write the given state, because we don't want to fire watches + unnecessarily. Furthermore, if the node has gone, we don't write + to it, as the device will be tearing down, and we don't want to + resurrect that directory. + + Note that, because of this cached value of our state, this function + will not work inside a Xenstore transaction (something it was + trying to in the past) because dev->state would not get reset if + the transaction was aborted. + + */ + + int current_state; + int err; + + if (state == dev->state) + return 0; + + err = xenbus_scanf(XBT_NIL, dev->nodename, "state", "%d", + ¤t_state); + if (err != 1) + return 0; + + err = xenbus_printf(XBT_NIL, dev->nodename, "state", "%d", state); + if (err) { + if (state != XenbusStateClosing) /* Avoid looping */ + xenbus_dev_fatal(dev, err, "writing new state"); + return err; + } + + dev->state = state; + + return 0; +} +EXPORT_SYMBOL_GPL(xenbus_switch_state); + +int xenbus_frontend_closed(struct xenbus_device *dev) +{ + xenbus_switch_state(dev, XenbusStateClosed); + complete(&dev->down); + return 0; +} +EXPORT_SYMBOL_GPL(xenbus_frontend_closed); + +/** + * Return the path to the error node for the given device, or NULL on failure. + * If the value returned is non-NULL, then it is the caller's to kfree. + */ +static char *error_path(struct xenbus_device *dev) +{ + return kasprintf(GFP_KERNEL, "error/%s", dev->nodename); +} + + +static void xenbus_va_dev_error(struct xenbus_device *dev, int err, + const char *fmt, va_list ap) +{ + int ret; + unsigned int len; + char *printf_buffer = NULL; + char *path_buffer = NULL; + +#define PRINTF_BUFFER_SIZE 4096 + printf_buffer = kmalloc(PRINTF_BUFFER_SIZE, GFP_KERNEL); + if (printf_buffer == NULL) + goto fail; + + len = sprintf(printf_buffer, "%i ", -err); + ret = vsnprintf(printf_buffer+len, PRINTF_BUFFER_SIZE-len, fmt, ap); + + BUG_ON(len + ret > PRINTF_BUFFER_SIZE-1); + + dev_err(&dev->dev, "%s\n", printf_buffer); + + path_buffer = error_path(dev); + + if (path_buffer == NULL) { + dev_err(&dev->dev, "failed to write error node for %s (%s)\n", + dev->nodename, printf_buffer); + goto fail; + } + + if (xenbus_write(XBT_NIL, path_buffer, "error", printf_buffer) != 0) { + dev_err(&dev->dev, "failed to write error node for %s (%s)\n", + dev->nodename, printf_buffer); + goto fail; + } + +fail: + kfree(printf_buffer); + kfree(path_buffer); +} + + +/** + * xenbus_dev_error + * @dev: xenbus device + * @err: error to report + * @fmt: error message format + * + * Report the given negative errno into the store, along with the given + * formatted message. + */ +void xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + xenbus_va_dev_error(dev, err, fmt, ap); + va_end(ap); +} +EXPORT_SYMBOL_GPL(xenbus_dev_error); + +/** + * xenbus_dev_fatal + * @dev: xenbus device + * @err: error to report + * @fmt: error message format + * + * Equivalent to xenbus_dev_error(dev, err, fmt, args), followed by + * xenbus_switch_state(dev, NULL, XenbusStateClosing) to schedule an orderly + * closedown of this driver and its peer. + */ + +void xenbus_dev_fatal(struct xenbus_device *dev, int err, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + xenbus_va_dev_error(dev, err, fmt, ap); + va_end(ap); + + xenbus_switch_state(dev, XenbusStateClosing); +} +EXPORT_SYMBOL_GPL(xenbus_dev_fatal); + +/** + * xenbus_grant_ring + * @dev: xenbus device + * @ring_mfn: mfn of ring to grant + + * Grant access to the given @ring_mfn to the peer of the given device. Return + * 0 on success, or -errno on error. On error, the device will switch to + * XenbusStateClosing, and the error will be saved in the store. + */ +int xenbus_grant_ring(struct xenbus_device *dev, unsigned long ring_mfn) +{ + int err = gnttab_grant_foreign_access(dev->otherend_id, ring_mfn, 0); + if (err < 0) + xenbus_dev_fatal(dev, err, "granting access to ring page"); + return err; +} +EXPORT_SYMBOL_GPL(xenbus_grant_ring); + + +/** + * Allocate an event channel for the given xenbus_device, assigning the newly + * created local port to *port. Return 0 on success, or -errno on error. On + * error, the device will switch to XenbusStateClosing, and the error will be + * saved in the store. + */ +int xenbus_alloc_evtchn(struct xenbus_device *dev, int *port) +{ + struct evtchn_alloc_unbound alloc_unbound; + int err; + + alloc_unbound.dom = DOMID_SELF; + alloc_unbound.remote_dom = dev->otherend_id; + + err = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, + &alloc_unbound); + if (err) + xenbus_dev_fatal(dev, err, "allocating event channel"); + else + *port = alloc_unbound.port; + + return err; +} +EXPORT_SYMBOL_GPL(xenbus_alloc_evtchn); + + +/** + * Bind to an existing interdomain event channel in another domain. Returns 0 + * on success and stores the local port in *port. On error, returns -errno, + * switches the device to XenbusStateClosing, and saves the error in XenStore. + */ +int xenbus_bind_evtchn(struct xenbus_device *dev, int remote_port, int *port) +{ + struct evtchn_bind_interdomain bind_interdomain; + int err; + + bind_interdomain.remote_dom = dev->otherend_id; + bind_interdomain.remote_port = remote_port; + + err = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain, + &bind_interdomain); + if (err) + xenbus_dev_fatal(dev, err, + "binding to event channel %d from domain %d", + remote_port, dev->otherend_id); + else + *port = bind_interdomain.local_port; + + return err; +} +EXPORT_SYMBOL_GPL(xenbus_bind_evtchn); + + +/** + * Free an existing event channel. Returns 0 on success or -errno on error. + */ +int xenbus_free_evtchn(struct xenbus_device *dev, int port) +{ + struct evtchn_close close; + int err; + + close.port = port; + + err = HYPERVISOR_event_channel_op(EVTCHNOP_close, &close); + if (err) + xenbus_dev_error(dev, err, "freeing event channel %d", port); + + return err; +} +EXPORT_SYMBOL_GPL(xenbus_free_evtchn); + + +/** + * xenbus_map_ring_valloc + * @dev: xenbus device + * @gnt_ref: grant reference + * @vaddr: pointer to address to be filled out by mapping + * + * Based on Rusty Russell's skeleton driver's map_page. + * Map a page of memory into this domain from another domain's grant table. + * xenbus_map_ring_valloc allocates a page of virtual address space, maps the + * page to that address, and sets *vaddr to that address. + * Returns 0 on success, and GNTST_* (see xen/include/interface/grant_table.h) + * or -ENOMEM on error. If an error is returned, device will switch to + * XenbusStateClosing and the error message will be saved in XenStore. + */ +int xenbus_map_ring_valloc(struct xenbus_device *dev, int gnt_ref, void **vaddr) +{ + struct gnttab_map_grant_ref op = { + .flags = GNTMAP_host_map, + .ref = gnt_ref, + .dom = dev->otherend_id, + }; + struct vm_struct *area; + + *vaddr = NULL; + + area = alloc_vm_area(PAGE_SIZE); + if (!area) + return -ENOMEM; + + op.host_addr = (unsigned long)area->addr; + + if (HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1)) + BUG(); + + if (op.status != GNTST_okay) { + free_vm_area(area); + xenbus_dev_fatal(dev, op.status, + "mapping in shared page %d from domain %d", + gnt_ref, dev->otherend_id); + return op.status; + } + + /* Stuff the handle in an unused field */ + area->phys_addr = (unsigned long)op.handle; + + *vaddr = area->addr; + return 0; +} +EXPORT_SYMBOL_GPL(xenbus_map_ring_valloc); + + +/** + * xenbus_map_ring + * @dev: xenbus device + * @gnt_ref: grant reference + * @handle: pointer to grant handle to be filled + * @vaddr: address to be mapped to + * + * Map a page of memory into this domain from another domain's grant table. + * xenbus_map_ring does not allocate the virtual address space (you must do + * this yourself!). It only maps in the page to the specified address. + * Returns 0 on success, and GNTST_* (see xen/include/interface/grant_table.h) + * or -ENOMEM on error. If an error is returned, device will switch to + * XenbusStateClosing and the error message will be saved in XenStore. + */ +int xenbus_map_ring(struct xenbus_device *dev, int gnt_ref, + grant_handle_t *handle, void *vaddr) +{ + struct gnttab_map_grant_ref op = { + .host_addr = (unsigned long)vaddr, + .flags = GNTMAP_host_map, + .ref = gnt_ref, + .dom = dev->otherend_id, + }; + + if (HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1)) + BUG(); + + if (op.status != GNTST_okay) { + xenbus_dev_fatal(dev, op.status, + "mapping in shared page %d from domain %d", + gnt_ref, dev->otherend_id); + } else + *handle = op.handle; + + return op.status; +} +EXPORT_SYMBOL_GPL(xenbus_map_ring); + + +/** + * xenbus_unmap_ring_vfree + * @dev: xenbus device + * @vaddr: addr to unmap + * + * Based on Rusty Russell's skeleton driver's unmap_page. + * Unmap a page of memory in this domain that was imported from another domain. + * Use xenbus_unmap_ring_vfree if you mapped in your memory with + * xenbus_map_ring_valloc (it will free the virtual address space). + * Returns 0 on success and returns GNTST_* on error + * (see xen/include/interface/grant_table.h). + */ +int xenbus_unmap_ring_vfree(struct xenbus_device *dev, void *vaddr) +{ + struct vm_struct *area; + struct gnttab_unmap_grant_ref op = { + .host_addr = (unsigned long)vaddr, + }; + + /* It'd be nice if linux/vmalloc.h provided a find_vm_area(void *addr) + * method so that we don't have to muck with vmalloc internals here. + * We could force the user to hang on to their struct vm_struct from + * xenbus_map_ring_valloc, but these 6 lines considerably simplify + * this API. + */ + read_lock(&vmlist_lock); + for (area = vmlist; area != NULL; area = area->next) { + if (area->addr == vaddr) + break; + } + read_unlock(&vmlist_lock); + + if (!area) { + xenbus_dev_error(dev, -ENOENT, + "can't find mapped virtual address %p", vaddr); + return GNTST_bad_virt_addr; + } + + op.handle = (grant_handle_t)area->phys_addr; + + if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1)) + BUG(); + + if (op.status == GNTST_okay) + free_vm_area(area); + else + xenbus_dev_error(dev, op.status, + "unmapping page at handle %d error %d", + (int16_t)area->phys_addr, op.status); + + return op.status; +} +EXPORT_SYMBOL_GPL(xenbus_unmap_ring_vfree); + + +/** + * xenbus_unmap_ring + * @dev: xenbus device + * @handle: grant handle + * @vaddr: addr to unmap + * + * Unmap a page of memory in this domain that was imported from another domain. + * Returns 0 on success and returns GNTST_* on error + * (see xen/include/interface/grant_table.h). + */ +int xenbus_unmap_ring(struct xenbus_device *dev, + grant_handle_t handle, void *vaddr) +{ + struct gnttab_unmap_grant_ref op = { + .host_addr = (unsigned long)vaddr, + .handle = handle, + }; + + if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1)) + BUG(); + + if (op.status != GNTST_okay) + xenbus_dev_error(dev, op.status, + "unmapping page at handle %d error %d", + handle, op.status); + + return op.status; +} +EXPORT_SYMBOL_GPL(xenbus_unmap_ring); + + +/** + * xenbus_read_driver_state + * @path: path for driver + * + * Return the state of the driver rooted at the given store path, or + * XenbusStateUnknown if no state can be read. + */ +enum xenbus_state xenbus_read_driver_state(const char *path) +{ + enum xenbus_state result; + int err = xenbus_gather(XBT_NIL, path, "state", "%d", &result, NULL); + if (err) + result = XenbusStateUnknown; + + return result; +} +EXPORT_SYMBOL_GPL(xenbus_read_driver_state); diff --git a/drivers/xen/xenbus/xenbus_comms.c b/drivers/xen/xenbus/xenbus_comms.c new file mode 100644 index 0000000..6efbe3f --- /dev/null +++ b/drivers/xen/xenbus/xenbus_comms.c @@ -0,0 +1,233 @@ +/****************************************************************************** + * xenbus_comms.c + * + * Low level code to talks to Xen Store: ringbuffer and event channel. + * + * Copyright (C) 2005 Rusty Russell, IBM Corporation + * + * 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; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <linux/wait.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/err.h> +#include <xen/xenbus.h> +#include <asm/xen/hypervisor.h> +#include <xen/events.h> +#include <xen/page.h> +#include "xenbus_comms.h" + +static int xenbus_irq; + +static DECLARE_WORK(probe_work, xenbus_probe); + +static DECLARE_WAIT_QUEUE_HEAD(xb_waitq); + +static irqreturn_t wake_waiting(int irq, void *unused) +{ + if (unlikely(xenstored_ready == 0)) { + xenstored_ready = 1; + schedule_work(&probe_work); + } + + wake_up(&xb_waitq); + return IRQ_HANDLED; +} + +static int check_indexes(XENSTORE_RING_IDX cons, XENSTORE_RING_IDX prod) +{ + return ((prod - cons) <= XENSTORE_RING_SIZE); +} + +static void *get_output_chunk(XENSTORE_RING_IDX cons, + XENSTORE_RING_IDX prod, + char *buf, uint32_t *len) +{ + *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(prod); + if ((XENSTORE_RING_SIZE - (prod - cons)) < *len) + *len = XENSTORE_RING_SIZE - (prod - cons); + return buf + MASK_XENSTORE_IDX(prod); +} + +static const void *get_input_chunk(XENSTORE_RING_IDX cons, + XENSTORE_RING_IDX prod, + const char *buf, uint32_t *len) +{ + *len = XENSTORE_RING_SIZE - MASK_XENSTORE_IDX(cons); + if ((prod - cons) < *len) + *len = prod - cons; + return buf + MASK_XENSTORE_IDX(cons); +} + +/** + * xb_write - low level write + * @data: buffer to send + * @len: length of buffer + * + * Returns 0 on success, error otherwise. + */ +int xb_write(const void *data, unsigned len) +{ + struct xenstore_domain_interface *intf = xen_store_interface; + XENSTORE_RING_IDX cons, prod; + int rc; + + while (len != 0) { + void *dst; + unsigned int avail; + + rc = wait_event_interruptible( + xb_waitq, + (intf->req_prod - intf->req_cons) != + XENSTORE_RING_SIZE); + if (rc < 0) + return rc; + + /* Read indexes, then verify. */ + cons = intf->req_cons; + prod = intf->req_prod; + if (!check_indexes(cons, prod)) { + intf->req_cons = intf->req_prod = 0; + return -EIO; + } + + dst = get_output_chunk(cons, prod, intf->req, &avail); + if (avail == 0) + continue; + if (avail > len) + avail = len; + + /* Must write data /after/ reading the consumer index. */ + mb(); + + memcpy(dst, data, avail); + data += avail; + len -= avail; + + /* Other side must not see new producer until data is there. */ + wmb(); + intf->req_prod += avail; + + /* Implies mb(): other side will see the updated producer. */ + notify_remote_via_evtchn(xen_store_evtchn); + } + + return 0; +} + +int xb_data_to_read(void) +{ + struct xenstore_domain_interface *intf = xen_store_interface; + return (intf->rsp_cons != intf->rsp_prod); +} + +int xb_wait_for_data_to_read(void) +{ + return wait_event_interruptible(xb_waitq, xb_data_to_read()); +} + +int xb_read(void *data, unsigned len) +{ + struct xenstore_domain_interface *intf = xen_store_interface; + XENSTORE_RING_IDX cons, prod; + int rc; + + while (len != 0) { + unsigned int avail; + const char *src; + + rc = xb_wait_for_data_to_read(); + if (rc < 0) + return rc; + + /* Read indexes, then verify. */ + cons = intf->rsp_cons; + prod = intf->rsp_prod; + if (!check_indexes(cons, prod)) { + intf->rsp_cons = intf->rsp_prod = 0; + return -EIO; + } + + src = get_input_chunk(cons, prod, intf->rsp, &avail); + if (avail == 0) + continue; + if (avail > len) + avail = len; + + /* Must read data /after/ reading the producer index. */ + rmb(); + + memcpy(data, src, avail); + data += avail; + len -= avail; + + /* Other side must not see free space until we've copied out */ + mb(); + intf->rsp_cons += avail; + + pr_debug("Finished read of %i bytes (%i to go)\n", avail, len); + + /* Implies mb(): other side will see the updated consumer. */ + notify_remote_via_evtchn(xen_store_evtchn); + } + + return 0; +} + +/** + * xb_init_comms - Set up interrupt handler off store event channel. + */ +int xb_init_comms(void) +{ + struct xenstore_domain_interface *intf = xen_store_interface; + int err; + + if (intf->req_prod != intf->req_cons) + printk(KERN_ERR "XENBUS request ring is not quiescent " + "(%08x:%08x)!\n", intf->req_cons, intf->req_prod); + + if (intf->rsp_prod != intf->rsp_cons) { + printk(KERN_WARNING "XENBUS response ring is not quiescent " + "(%08x:%08x): fixing up\n", + intf->rsp_cons, intf->rsp_prod); + intf->rsp_cons = intf->rsp_prod; + } + + if (xenbus_irq) + unbind_from_irqhandler(xenbus_irq, &xb_waitq); + + err = bind_evtchn_to_irqhandler( + xen_store_evtchn, wake_waiting, + 0, "xenbus", &xb_waitq); + if (err <= 0) { + printk(KERN_ERR "XENBUS request irq failed %i\n", err); + return err; + } + + xenbus_irq = err; + + return 0; +} diff --git a/drivers/xen/xenbus/xenbus_comms.h b/drivers/xen/xenbus/xenbus_comms.h new file mode 100644 index 0000000..c21db75 --- /dev/null +++ b/drivers/xen/xenbus/xenbus_comms.h @@ -0,0 +1,46 @@ +/* + * Private include for xenbus communications. + * + * Copyright (C) 2005 Rusty Russell, IBM Corporation + * + * 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; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef _XENBUS_COMMS_H +#define _XENBUS_COMMS_H + +int xs_init(void); +int xb_init_comms(void); + +/* Low level routines. */ +int xb_write(const void *data, unsigned len); +int xb_read(void *data, unsigned len); +int xb_data_to_read(void); +int xb_wait_for_data_to_read(void); +int xs_input_avail(void); +extern struct xenstore_domain_interface *xen_store_interface; +extern int xen_store_evtchn; + +#endif /* _XENBUS_COMMS_H */ diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c new file mode 100644 index 0000000..0b769f7 --- /dev/null +++ b/drivers/xen/xenbus/xenbus_probe.c @@ -0,0 +1,935 @@ +/****************************************************************************** + * Talks to Xen Store to figure out what devices we have. + * + * Copyright (C) 2005 Rusty Russell, IBM Corporation + * Copyright (C) 2005 Mike Wray, Hewlett-Packard + * Copyright (C) 2005, 2006 XenSource Ltd + * + * 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; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#define DPRINTK(fmt, args...) \ + pr_debug("xenbus_probe (%s:%d) " fmt ".\n", \ + __func__, __LINE__, ##args) + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/fcntl.h> +#include <linux/mm.h> +#include <linux/notifier.h> +#include <linux/kthread.h> +#include <linux/mutex.h> +#include <linux/io.h> + +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/xen/hypervisor.h> +#include <xen/xenbus.h> +#include <xen/events.h> +#include <xen/page.h> + +#include "xenbus_comms.h" +#include "xenbus_probe.h" + +int xen_store_evtchn; +struct xenstore_domain_interface *xen_store_interface; +static unsigned long xen_store_mfn; + +static BLOCKING_NOTIFIER_HEAD(xenstore_chain); + +static void wait_for_devices(struct xenbus_driver *xendrv); + +static int xenbus_probe_frontend(const char *type, const char *name); + +static void xenbus_dev_shutdown(struct device *_dev); + +/* If something in array of ids matches this device, return it. */ +static const struct xenbus_device_id * +match_device(const struct xenbus_device_id *arr, struct xenbus_device *dev) +{ + for (; *arr->devicetype != '\0'; arr++) { + if (!strcmp(arr->devicetype, dev->devicetype)) + return arr; + } + return NULL; +} + +int xenbus_match(struct device *_dev, struct device_driver *_drv) +{ + struct xenbus_driver *drv = to_xenbus_driver(_drv); + + if (!drv->ids) + return 0; + + return match_device(drv->ids, to_xenbus_device(_dev)) != NULL; +} + +/* device/<type>/<id> => <type>-<id> */ +static int frontend_bus_id(char bus_id[BUS_ID_SIZE], const char *nodename) +{ + nodename = strchr(nodename, '/'); + if (!nodename || strlen(nodename + 1) >= BUS_ID_SIZE) { + printk(KERN_WARNING "XENBUS: bad frontend %s\n", nodename); + return -EINVAL; + } + + strlcpy(bus_id, nodename + 1, BUS_ID_SIZE); + if (!strchr(bus_id, '/')) { + printk(KERN_WARNING "XENBUS: bus_id %s no slash\n", bus_id); + return -EINVAL; + } + *strchr(bus_id, '/') = '-'; + return 0; +} + + +static void free_otherend_details(struct xenbus_device *dev) +{ + kfree(dev->otherend); + dev->otherend = NULL; +} + + +static void free_otherend_watch(struct xenbus_device *dev) +{ + if (dev->otherend_watch.node) { + unregister_xenbus_watch(&dev->otherend_watch); + kfree(dev->otherend_watch.node); + dev->otherend_watch.node = NULL; + } +} + + +int read_otherend_details(struct xenbus_device *xendev, + char *id_node, char *path_node) +{ + int err = xenbus_gather(XBT_NIL, xendev->nodename, + id_node, "%i", &xendev->otherend_id, + path_node, NULL, &xendev->otherend, + NULL); + if (err) { + xenbus_dev_fatal(xendev, err, + "reading other end details from %s", + xendev->nodename); + return err; + } + if (strlen(xendev->otherend) == 0 || + !xenbus_exists(XBT_NIL, xendev->otherend, "")) { + xenbus_dev_fatal(xendev, -ENOENT, + "unable to read other end from %s. " + "missing or inaccessible.", + xendev->nodename); + free_otherend_details(xendev); + return -ENOENT; + } + + return 0; +} + + +static int read_backend_details(struct xenbus_device *xendev) +{ + return read_otherend_details(xendev, "backend-id", "backend"); +} + + +/* Bus type for frontend drivers. */ +static struct xen_bus_type xenbus_frontend = { + .root = "device", + .levels = 2, /* device/type/<id> */ + .get_bus_id = frontend_bus_id, + .probe = xenbus_probe_frontend, + .bus = { + .name = "xen", + .match = xenbus_match, + .probe = xenbus_dev_probe, + .remove = xenbus_dev_remove, + .shutdown = xenbus_dev_shutdown, + }, +}; + +static void otherend_changed(struct xenbus_watch *watch, + const char **vec, unsigned int len) +{ + struct xenbus_device *dev = + container_of(watch, struct xenbus_device, otherend_watch); + struct xenbus_driver *drv = to_xenbus_driver(dev->dev.driver); + enum xenbus_state state; + + /* Protect us against watches firing on old details when the otherend + details change, say immediately after a resume. */ + if (!dev->otherend || + strncmp(dev->otherend, vec[XS_WATCH_PATH], + strlen(dev->otherend))) { + dev_dbg(&dev->dev, "Ignoring watch at %s", vec[XS_WATCH_PATH]); + return; + } + + state = xenbus_read_driver_state(dev->otherend); + + dev_dbg(&dev->dev, "state is %d, (%s), %s, %s", + state, xenbus_strstate(state), dev->otherend_watch.node, + vec[XS_WATCH_PATH]); + + /* + * Ignore xenbus transitions during shutdown. This prevents us doing + * work that can fail e.g., when the rootfs is gone. + */ + if (system_state > SYSTEM_RUNNING) { + struct xen_bus_type *bus = bus; + bus = container_of(dev->dev.bus, struct xen_bus_type, bus); + /* If we're frontend, drive the state machine to Closed. */ + /* This should cause the backend to release our resources. */ + if ((bus == &xenbus_frontend) && (state == XenbusStateClosing)) + xenbus_frontend_closed(dev); + return; + } + + if (drv->otherend_changed) + drv->otherend_changed(dev, state); +} + + +static int talk_to_otherend(struct xenbus_device *dev) +{ + struct xenbus_driver *drv = to_xenbus_driver(dev->dev.driver); + + free_otherend_watch(dev); + free_otherend_details(dev); + + return drv->read_otherend_details(dev); +} + + +static int watch_otherend(struct xenbus_device *dev) +{ + return xenbus_watch_pathfmt(dev, &dev->otherend_watch, otherend_changed, + "%s/%s", dev->otherend, "state"); +} + + +int xenbus_dev_probe(struct device *_dev) +{ + struct xenbus_device *dev = to_xenbus_device(_dev); + struct xenbus_driver *drv = to_xenbus_driver(_dev->driver); + const struct xenbus_device_id *id; + int err; + + DPRINTK("%s", dev->nodename); + + if (!drv->probe) { + err = -ENODEV; + goto fail; + } + + id = match_device(drv->ids, dev); + if (!id) { + err = -ENODEV; + goto fail; + } + + err = talk_to_otherend(dev); + if (err) { + dev_warn(&dev->dev, "talk_to_otherend on %s failed.\n", + dev->nodename); + return err; + } + + err = drv->probe(dev, id); + if (err) + goto fail; + + err = watch_otherend(dev); + if (err) { + dev_warn(&dev->dev, "watch_otherend on %s failed.\n", + dev->nodename); + return err; + } + + return 0; +fail: + xenbus_dev_error(dev, err, "xenbus_dev_probe on %s", dev->nodename); + xenbus_switch_state(dev, XenbusStateClosed); + return -ENODEV; +} + +int xenbus_dev_remove(struct device *_dev) +{ + struct xenbus_device *dev = to_xenbus_device(_dev); + struct xenbus_driver *drv = to_xenbus_driver(_dev->driver); + + DPRINTK("%s", dev->nodename); + + free_otherend_watch(dev); + free_otherend_details(dev); + + if (drv->remove) + drv->remove(dev); + + xenbus_switch_state(dev, XenbusStateClosed); + return 0; +} + +static void xenbus_dev_shutdown(struct device *_dev) +{ + struct xenbus_device *dev = to_xenbus_device(_dev); + unsigned long timeout = 5*HZ; + + DPRINTK("%s", dev->nodename); + + get_device(&dev->dev); + if (dev->state != XenbusStateConnected) { + printk(KERN_INFO "%s: %s: %s != Connected, skipping\n", __func__, + dev->nodename, xenbus_strstate(dev->state)); + goto out; + } + xenbus_switch_state(dev, XenbusStateClosing); + timeout = wait_for_completion_timeout(&dev->down, timeout); + if (!timeout) + printk(KERN_INFO "%s: %s timeout closing device\n", + __func__, dev->nodename); + out: + put_device(&dev->dev); +} + +int xenbus_register_driver_common(struct xenbus_driver *drv, + struct xen_bus_type *bus, + struct module *owner, + const char *mod_name) +{ + drv->driver.name = drv->name; + drv->driver.bus = &bus->bus; + drv->driver.owner = owner; + drv->driver.mod_name = mod_name; + + return driver_register(&drv->driver); +} + +int __xenbus_register_frontend(struct xenbus_driver *drv, + struct module *owner, const char *mod_name) +{ + int ret; + + drv->read_otherend_details = read_backend_details; + + ret = xenbus_register_driver_common(drv, &xenbus_frontend, + owner, mod_name); + if (ret) + return ret; + + /* If this driver is loaded as a module wait for devices to attach. */ + wait_for_devices(drv); + + return 0; +} +EXPORT_SYMBOL_GPL(__xenbus_register_frontend); + +void xenbus_unregister_driver(struct xenbus_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(xenbus_unregister_driver); + +struct xb_find_info +{ + struct xenbus_device *dev; + const char *nodename; +}; + +static int cmp_dev(struct device *dev, void *data) +{ + struct xenbus_device *xendev = to_xenbus_device(dev); + struct xb_find_info *info = data; + + if (!strcmp(xendev->nodename, info->nodename)) { + info->dev = xendev; + get_device(dev); + return 1; + } + return 0; +} + +struct xenbus_device *xenbus_device_find(const char *nodename, + struct bus_type *bus) +{ + struct xb_find_info info = { .dev = NULL, .nodename = nodename }; + + bus_for_each_dev(bus, NULL, &info, cmp_dev); + return info.dev; +} + +static int cleanup_dev(struct device *dev, void *data) +{ + struct xenbus_device *xendev = to_xenbus_device(dev); + struct xb_find_info *info = data; + int len = strlen(info->nodename); + + DPRINTK("%s", info->nodename); + + /* Match the info->nodename path, or any subdirectory of that path. */ + if (strncmp(xendev->nodename, info->nodename, len)) + return 0; + + /* If the node name is longer, ensure it really is a subdirectory. */ + if ((strlen(xendev->nodename) > len) && (xendev->nodename[len] != '/')) + return 0; + + info->dev = xendev; + get_device(dev); + return 1; +} + +static void xenbus_cleanup_devices(const char *path, struct bus_type *bus) +{ + struct xb_find_info info = { .nodename = path }; + + do { + info.dev = NULL; + bus_for_each_dev(bus, NULL, &info, cleanup_dev); + if (info.dev) { + device_unregister(&info.dev->dev); + put_device(&info.dev->dev); + } + } while (info.dev); +} + +static void xenbus_dev_release(struct device *dev) +{ + if (dev) + kfree(to_xenbus_device(dev)); +} + +static ssize_t xendev_show_nodename(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", to_xenbus_device(dev)->nodename); +} +DEVICE_ATTR(nodename, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_nodename, NULL); + +static ssize_t xendev_show_devtype(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", to_xenbus_device(dev)->devicetype); +} +DEVICE_ATTR(devtype, S_IRUSR | S_IRGRP | S_IROTH, xendev_show_devtype, NULL); + + +int xenbus_probe_node(struct xen_bus_type *bus, + const char *type, + const char *nodename) +{ + int err; + struct xenbus_device *xendev; + size_t stringlen; + char *tmpstring; + + enum xenbus_state state = xenbus_read_driver_state(nodename); + + if (state != XenbusStateInitialising) { + /* Device is not new, so ignore it. This can happen if a + device is going away after switching to Closed. */ + return 0; + } + + stringlen = strlen(nodename) + 1 + strlen(type) + 1; + xendev = kzalloc(sizeof(*xendev) + stringlen, GFP_KERNEL); + if (!xendev) + return -ENOMEM; + + xendev->state = XenbusStateInitialising; + + /* Copy the strings into the extra space. */ + + tmpstring = (char *)(xendev + 1); + strcpy(tmpstring, nodename); + xendev->nodename = tmpstring; + + tmpstring += strlen(tmpstring) + 1; + strcpy(tmpstring, type); + xendev->devicetype = tmpstring; + init_completion(&xendev->down); + + xendev->dev.bus = &bus->bus; + xendev->dev.release = xenbus_dev_release; + + err = bus->get_bus_id(xendev->dev.bus_id, xendev->nodename); + if (err) + goto fail; + + /* Register with generic device framework. */ + err = device_register(&xendev->dev); + if (err) + goto fail; + + err = device_create_file(&xendev->dev, &dev_attr_nodename); + if (err) + goto fail_unregister; + + err = device_create_file(&xendev->dev, &dev_attr_devtype); + if (err) + goto fail_remove_file; + + return 0; +fail_remove_file: + device_remove_file(&xendev->dev, &dev_attr_nodename); +fail_unregister: + device_unregister(&xendev->dev); +fail: + kfree(xendev); + return err; +} + +/* device/<typename>/<name> */ +static int xenbus_probe_frontend(const char *type, const char *name) +{ + char *nodename; + int err; + + nodename = kasprintf(GFP_KERNEL, "%s/%s/%s", + xenbus_frontend.root, type, name); + if (!nodename) + return -ENOMEM; + + DPRINTK("%s", nodename); + + err = xenbus_probe_node(&xenbus_frontend, type, nodename); + kfree(nodename); + return err; +} + +static int xenbus_probe_device_type(struct xen_bus_type *bus, const char *type) +{ + int err = 0; + char **dir; + unsigned int dir_n = 0; + int i; + + dir = xenbus_directory(XBT_NIL, bus->root, type, &dir_n); + if (IS_ERR(dir)) + return PTR_ERR(dir); + + for (i = 0; i < dir_n; i++) { + err = bus->probe(type, dir[i]); + if (err) + break; + } + kfree(dir); + return err; +} + +int xenbus_probe_devices(struct xen_bus_type *bus) +{ + int err = 0; + char **dir; + unsigned int i, dir_n; + + dir = xenbus_directory(XBT_NIL, bus->root, "", &dir_n); + if (IS_ERR(dir)) + return PTR_ERR(dir); + + for (i = 0; i < dir_n; i++) { + err = xenbus_probe_device_type(bus, dir[i]); + if (err) + break; + } + kfree(dir); + return err; +} + +static unsigned int char_count(const char *str, char c) +{ + unsigned int i, ret = 0; + + for (i = 0; str[i]; i++) + if (str[i] == c) + ret++; + return ret; +} + +static int strsep_len(const char *str, char c, unsigned int len) +{ + unsigned int i; + + for (i = 0; str[i]; i++) + if (str[i] == c) { + if (len == 0) + return i; + len--; + } + return (len == 0) ? i : -ERANGE; +} + +void xenbus_dev_changed(const char *node, struct xen_bus_type *bus) +{ + int exists, rootlen; + struct xenbus_device *dev; + char type[BUS_ID_SIZE]; + const char *p, *root; + + if (char_count(node, '/') < 2) + return; + + exists = xenbus_exists(XBT_NIL, node, ""); + if (!exists) { + xenbus_cleanup_devices(node, &bus->bus); + return; + } + + /* backend/<type>/... or device/<type>/... */ + p = strchr(node, '/') + 1; + snprintf(type, BUS_ID_SIZE, "%.*s", (int)strcspn(p, "/"), p); + type[BUS_ID_SIZE-1] = '\0'; + + rootlen = strsep_len(node, '/', bus->levels); + if (rootlen < 0) + return; + root = kasprintf(GFP_KERNEL, "%.*s", rootlen, node); + if (!root) + return; + + dev = xenbus_device_find(root, &bus->bus); + if (!dev) + xenbus_probe_node(bus, type, root); + else + put_device(&dev->dev); + + kfree(root); +} + +static void frontend_changed(struct xenbus_watch *watch, + const char **vec, unsigned int len) +{ + DPRINTK(""); + + xenbus_dev_changed(vec[XS_WATCH_PATH], &xenbus_frontend); +} + +/* We watch for devices appearing and vanishing. */ +static struct xenbus_watch fe_watch = { + .node = "device", + .callback = frontend_changed, +}; + +static int suspend_dev(struct device *dev, void *data) +{ + int err = 0; + struct xenbus_driver *drv; + struct xenbus_device *xdev; + + DPRINTK(""); + + if (dev->driver == NULL) + return 0; + drv = to_xenbus_driver(dev->driver); + xdev = container_of(dev, struct xenbus_device, dev); + if (drv->suspend) + err = drv->suspend(xdev); + if (err) + printk(KERN_WARNING + "xenbus: suspend %s failed: %i\n", dev->bus_id, err); + return 0; +} + +static int suspend_cancel_dev(struct device *dev, void *data) +{ + int err = 0; + struct xenbus_driver *drv; + struct xenbus_device *xdev; + + DPRINTK(""); + + if (dev->driver == NULL) + return 0; + drv = to_xenbus_driver(dev->driver); + xdev = container_of(dev, struct xenbus_device, dev); + if (drv->suspend_cancel) + err = drv->suspend_cancel(xdev); + if (err) + printk(KERN_WARNING + "xenbus: suspend_cancel %s failed: %i\n", + dev->bus_id, err); + return 0; +} + +static int resume_dev(struct device *dev, void *data) +{ + int err; + struct xenbus_driver *drv; + struct xenbus_device *xdev; + + DPRINTK(""); + + if (dev->driver == NULL) + return 0; + + drv = to_xenbus_driver(dev->driver); + xdev = container_of(dev, struct xenbus_device, dev); + + err = talk_to_otherend(xdev); + if (err) { + printk(KERN_WARNING + "xenbus: resume (talk_to_otherend) %s failed: %i\n", + dev->bus_id, err); + return err; + } + + xdev->state = XenbusStateInitialising; + + if (drv->resume) { + err = drv->resume(xdev); + if (err) { + printk(KERN_WARNING + "xenbus: resume %s failed: %i\n", + dev->bus_id, err); + return err; + } + } + + err = watch_otherend(xdev); + if (err) { + printk(KERN_WARNING + "xenbus_probe: resume (watch_otherend) %s failed: " + "%d.\n", dev->bus_id, err); + return err; + } + + return 0; +} + +void xenbus_suspend(void) +{ + DPRINTK(""); + + bus_for_each_dev(&xenbus_frontend.bus, NULL, NULL, suspend_dev); + xenbus_backend_suspend(suspend_dev); + xs_suspend(); +} +EXPORT_SYMBOL_GPL(xenbus_suspend); + +void xenbus_resume(void) +{ + xb_init_comms(); + xs_resume(); + bus_for_each_dev(&xenbus_frontend.bus, NULL, NULL, resume_dev); + xenbus_backend_resume(resume_dev); +} +EXPORT_SYMBOL_GPL(xenbus_resume); + +void xenbus_suspend_cancel(void) +{ + xs_suspend_cancel(); + bus_for_each_dev(&xenbus_frontend.bus, NULL, NULL, suspend_cancel_dev); + xenbus_backend_resume(suspend_cancel_dev); +} +EXPORT_SYMBOL_GPL(xenbus_suspend_cancel); + +/* A flag to determine if xenstored is 'ready' (i.e. has started) */ +int xenstored_ready = 0; + + +int register_xenstore_notifier(struct notifier_block *nb) +{ + int ret = 0; + + if (xenstored_ready > 0) + ret = nb->notifier_call(nb, 0, NULL); + else + blocking_notifier_chain_register(&xenstore_chain, nb); + + return ret; +} +EXPORT_SYMBOL_GPL(register_xenstore_notifier); + +void unregister_xenstore_notifier(struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&xenstore_chain, nb); +} +EXPORT_SYMBOL_GPL(unregister_xenstore_notifier); + +void xenbus_probe(struct work_struct *unused) +{ + BUG_ON((xenstored_ready <= 0)); + + /* Enumerate devices in xenstore and watch for changes. */ + xenbus_probe_devices(&xenbus_frontend); + register_xenbus_watch(&fe_watch); + xenbus_backend_probe_and_watch(); + + /* Notify others that xenstore is up */ + blocking_notifier_call_chain(&xenstore_chain, 0, NULL); +} + +static int __init xenbus_probe_init(void) +{ + int err = 0; + + DPRINTK(""); + + err = -ENODEV; + if (!is_running_on_xen()) + goto out_error; + + /* Register ourselves with the kernel bus subsystem */ + err = bus_register(&xenbus_frontend.bus); + if (err) + goto out_error; + + err = xenbus_backend_bus_register(); + if (err) + goto out_unreg_front; + + /* + * Domain0 doesn't have a store_evtchn or store_mfn yet. + */ + if (is_initial_xendomain()) { + /* dom0 not yet supported */ + } else { + xenstored_ready = 1; + xen_store_evtchn = xen_start_info->store_evtchn; + xen_store_mfn = xen_start_info->store_mfn; + } + xen_store_interface = mfn_to_virt(xen_store_mfn); + + /* Initialize the interface to xenstore. */ + err = xs_init(); + if (err) { + printk(KERN_WARNING + "XENBUS: Error initializing xenstore comms: %i\n", err); + goto out_unreg_back; + } + + if (!is_initial_xendomain()) + xenbus_probe(NULL); + + return 0; + + out_unreg_back: + xenbus_backend_bus_unregister(); + + out_unreg_front: + bus_unregister(&xenbus_frontend.bus); + + out_error: + return err; +} + +postcore_initcall(xenbus_probe_init); + +MODULE_LICENSE("GPL"); + +static int is_disconnected_device(struct device *dev, void *data) +{ + struct xenbus_device *xendev = to_xenbus_device(dev); + struct device_driver *drv = data; + + /* + * A device with no driver will never connect. We care only about + * devices which should currently be in the process of connecting. + */ + if (!dev->driver) + return 0; + + /* Is this search limited to a particular driver? */ + if (drv && (dev->driver != drv)) + return 0; + + return (xendev->state != XenbusStateConnected); +} + +static int exists_disconnected_device(struct device_driver *drv) +{ + return bus_for_each_dev(&xenbus_frontend.bus, NULL, drv, + is_disconnected_device); +} + +static int print_device_status(struct device *dev, void *data) +{ + struct xenbus_device *xendev = to_xenbus_device(dev); + struct device_driver *drv = data; + + /* Is this operation limited to a particular driver? */ + if (drv && (dev->driver != drv)) + return 0; + + if (!dev->driver) { + /* Information only: is this too noisy? */ + printk(KERN_INFO "XENBUS: Device with no driver: %s\n", + xendev->nodename); + } else if (xendev->state != XenbusStateConnected) { + printk(KERN_WARNING "XENBUS: Timeout connecting " + "to device: %s (state %d)\n", + xendev->nodename, xendev->state); + } + + return 0; +} + +/* We only wait for device setup after most initcalls have run. */ +static int ready_to_wait_for_devices; + +/* + * On a 10 second timeout, wait for all devices currently configured. We need + * to do this to guarantee that the filesystems and / or network devices + * needed for boot are available, before we can allow the boot to proceed. + * + * This needs to be on a late_initcall, to happen after the frontend device + * drivers have been initialised, but before the root fs is mounted. + * + * A possible improvement here would be to have the tools add a per-device + * flag to the store entry, indicating whether it is needed at boot time. + * This would allow people who knew what they were doing to accelerate their + * boot slightly, but of course needs tools or manual intervention to set up + * those flags correctly. + */ +static void wait_for_devices(struct xenbus_driver *xendrv) +{ + unsigned long timeout = jiffies + 10*HZ; + struct device_driver *drv = xendrv ? &xendrv->driver : NULL; + + if (!ready_to_wait_for_devices || !is_running_on_xen()) + return; + + while (exists_disconnected_device(drv)) { + if (time_after(jiffies, timeout)) + break; + schedule_timeout_interruptible(HZ/10); + } + + bus_for_each_dev(&xenbus_frontend.bus, NULL, drv, + print_device_status); +} + +#ifndef MODULE +static int __init boot_wait_for_devices(void) +{ + ready_to_wait_for_devices = 1; + wait_for_devices(NULL); + return 0; +} + +late_initcall(boot_wait_for_devices); +#endif diff --git a/drivers/xen/xenbus/xenbus_probe.h b/drivers/xen/xenbus/xenbus_probe.h new file mode 100644 index 0000000..e09b194 --- /dev/null +++ b/drivers/xen/xenbus/xenbus_probe.h @@ -0,0 +1,74 @@ +/****************************************************************************** + * xenbus_probe.h + * + * Talks to Xen Store to figure out what devices we have. + * + * Copyright (C) 2005 Rusty Russell, IBM Corporation + * Copyright (C) 2005 XenSource Ltd. + * + * 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; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef _XENBUS_PROBE_H +#define _XENBUS_PROBE_H + +#ifdef CONFIG_XEN_BACKEND +extern void xenbus_backend_suspend(int (*fn)(struct device *, void *)); +extern void xenbus_backend_resume(int (*fn)(struct device *, void *)); +extern void xenbus_backend_probe_and_watch(void); +extern int xenbus_backend_bus_register(void); +extern void xenbus_backend_bus_unregister(void); +#else +static inline void xenbus_backend_suspend(int (*fn)(struct device *, void *)) {} +static inline void xenbus_backend_resume(int (*fn)(struct device *, void *)) {} +static inline void xenbus_backend_probe_and_watch(void) {} +static inline int xenbus_backend_bus_register(void) { return 0; } +static inline void xenbus_backend_bus_unregister(void) {} +#endif + +struct xen_bus_type +{ + char *root; + unsigned int levels; + int (*get_bus_id)(char bus_id[BUS_ID_SIZE], const char *nodename); + int (*probe)(const char *type, const char *dir); + struct bus_type bus; +}; + +extern int xenbus_match(struct device *_dev, struct device_driver *_drv); +extern int xenbus_dev_probe(struct device *_dev); +extern int xenbus_dev_remove(struct device *_dev); +extern int xenbus_register_driver_common(struct xenbus_driver *drv, + struct xen_bus_type *bus, + struct module *owner, + const char *mod_name); +extern int xenbus_probe_node(struct xen_bus_type *bus, + const char *type, + const char *nodename); +extern int xenbus_probe_devices(struct xen_bus_type *bus); + +extern void xenbus_dev_changed(const char *node, struct xen_bus_type *bus); + +#endif diff --git a/drivers/xen/xenbus/xenbus_xs.c b/drivers/xen/xenbus/xenbus_xs.c new file mode 100644 index 0000000..9e943fb --- /dev/null +++ b/drivers/xen/xenbus/xenbus_xs.c @@ -0,0 +1,861 @@ +/****************************************************************************** + * xenbus_xs.c + * + * This is the kernel equivalent of the "xs" library. We don't need everything + * and we use xenbus_comms for communication. + * + * Copyright (C) 2005 Rusty Russell, IBM Corporation + * + * 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; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include <linux/unistd.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/uio.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/fcntl.h> +#include <linux/kthread.h> +#include <linux/rwsem.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <xen/xenbus.h> +#include "xenbus_comms.h" + +struct xs_stored_msg { + struct list_head list; + + struct xsd_sockmsg hdr; + + union { + /* Queued replies. */ + struct { + char *body; + } reply; + + /* Queued watch events. */ + struct { + struct xenbus_watch *handle; + char **vec; + unsigned int vec_size; + } watch; + } u; +}; + +struct xs_handle { + /* A list of replies. Currently only one will ever be outstanding. */ + struct list_head reply_list; + spinlock_t reply_lock; + wait_queue_head_t reply_waitq; + + /* + * Mutex ordering: transaction_mutex -> watch_mutex -> request_mutex. + * response_mutex is never taken simultaneously with the other three. + */ + + /* One request at a time. */ + struct mutex request_mutex; + + /* Protect xenbus reader thread against save/restore. */ + struct mutex response_mutex; + + /* Protect transactions against save/restore. */ + struct rw_semaphore transaction_mutex; + + /* Protect watch (de)register against save/restore. */ + struct rw_semaphore watch_mutex; +}; + +static struct xs_handle xs_state; + +/* List of registered watches, and a lock to protect it. */ +static LIST_HEAD(watches); +static DEFINE_SPINLOCK(watches_lock); + +/* List of pending watch callback events, and a lock to protect it. */ +static LIST_HEAD(watch_events); +static DEFINE_SPINLOCK(watch_events_lock); + +/* + * Details of the xenwatch callback kernel thread. The thread waits on the + * watch_events_waitq for work to do (queued on watch_events list). When it + * wakes up it acquires the xenwatch_mutex before reading the list and + * carrying out work. + */ +static pid_t xenwatch_pid; +static DEFINE_MUTEX(xenwatch_mutex); +static DECLARE_WAIT_QUEUE_HEAD(watch_events_waitq); + +static int get_error(const char *errorstring) +{ + unsigned int i; + + for (i = 0; strcmp(errorstring, xsd_errors[i].errstring) != 0; i++) { + if (i == ARRAY_SIZE(xsd_errors) - 1) { + printk(KERN_WARNING + "XENBUS xen store gave: unknown error %s", + errorstring); + return EINVAL; + } + } + return xsd_errors[i].errnum; +} + +static void *read_reply(enum xsd_sockmsg_type *type, unsigned int *len) +{ + struct xs_stored_msg *msg; + char *body; + + spin_lock(&xs_state.reply_lock); + + while (list_empty(&xs_state.reply_list)) { + spin_unlock(&xs_state.reply_lock); + /* XXX FIXME: Avoid synchronous wait for response here. */ + wait_event(xs_state.reply_waitq, + !list_empty(&xs_state.reply_list)); + spin_lock(&xs_state.reply_lock); + } + + msg = list_entry(xs_state.reply_list.next, + struct xs_stored_msg, list); + list_del(&msg->list); + + spin_unlock(&xs_state.reply_lock); + + *type = msg->hdr.type; + if (len) + *len = msg->hdr.len; + body = msg->u.reply.body; + + kfree(msg); + + return body; +} + +void *xenbus_dev_request_and_reply(struct xsd_sockmsg *msg) +{ + void *ret; + struct xsd_sockmsg req_msg = *msg; + int err; + + if (req_msg.type == XS_TRANSACTION_START) + down_read(&xs_state.transaction_mutex); + + mutex_lock(&xs_state.request_mutex); + + err = xb_write(msg, sizeof(*msg) + msg->len); + if (err) { + msg->type = XS_ERROR; + ret = ERR_PTR(err); + } else + ret = read_reply(&msg->type, &msg->len); + + mutex_unlock(&xs_state.request_mutex); + + if ((msg->type == XS_TRANSACTION_END) || + ((req_msg.type == XS_TRANSACTION_START) && + (msg->type == XS_ERROR))) + up_read(&xs_state.transaction_mutex); + + return ret; +} + +/* Send message to xs, get kmalloc'ed reply. ERR_PTR() on error. */ +static void *xs_talkv(struct xenbus_transaction t, + enum xsd_sockmsg_type type, + const struct kvec *iovec, + unsigned int num_vecs, + unsigned int *len) +{ + struct xsd_sockmsg msg; + void *ret = NULL; + unsigned int i; + int err; + + msg.tx_id = t.id; + msg.req_id = 0; + msg.type = type; + msg.len = 0; + for (i = 0; i < num_vecs; i++) + msg.len += iovec[i].iov_len; + + mutex_lock(&xs_state.request_mutex); + + err = xb_write(&msg, sizeof(msg)); + if (err) { + mutex_unlock(&xs_state.request_mutex); + return ERR_PTR(err); + } + + for (i = 0; i < num_vecs; i++) { + err = xb_write(iovec[i].iov_base, iovec[i].iov_len); + if (err) { + mutex_unlock(&xs_state.request_mutex); + return ERR_PTR(err); + } + } + + ret = read_reply(&msg.type, len); + + mutex_unlock(&xs_state.request_mutex); + + if (IS_ERR(ret)) + return ret; + + if (msg.type == XS_ERROR) { + err = get_error(ret); + kfree(ret); + return ERR_PTR(-err); + } + + if (msg.type != type) { + if (printk_ratelimit()) + printk(KERN_WARNING + "XENBUS unexpected type [%d], expected [%d]\n", + msg.type, type); + kfree(ret); + return ERR_PTR(-EINVAL); + } + return ret; +} + +/* Simplified version of xs_talkv: single message. */ +static void *xs_single(struct xenbus_transaction t, + enum xsd_sockmsg_type type, + const char *string, + unsigned int *len) +{ + struct kvec iovec; + + iovec.iov_base = (void *)string; + iovec.iov_len = strlen(string) + 1; + return xs_talkv(t, type, &iovec, 1, len); +} + +/* Many commands only need an ack, don't care what it says. */ +static int xs_error(char *reply) +{ + if (IS_ERR(reply)) + return PTR_ERR(reply); + kfree(reply); + return 0; +} + +static unsigned int count_strings(const char *strings, unsigned int len) +{ + unsigned int num; + const char *p; + + for (p = strings, num = 0; p < strings + len; p += strlen(p) + 1) + num++; + + return num; +} + +/* Return the path to dir with /name appended. Buffer must be kfree()'ed. */ +static char *join(const char *dir, const char *name) +{ + char *buffer; + + if (strlen(name) == 0) + buffer = kasprintf(GFP_KERNEL, "%s", dir); + else + buffer = kasprintf(GFP_KERNEL, "%s/%s", dir, name); + return (!buffer) ? ERR_PTR(-ENOMEM) : buffer; +} + +static char **split(char *strings, unsigned int len, unsigned int *num) +{ + char *p, **ret; + + /* Count the strings. */ + *num = count_strings(strings, len); + + /* Transfer to one big alloc for easy freeing. */ + ret = kmalloc(*num * sizeof(char *) + len, GFP_KERNEL); + if (!ret) { + kfree(strings); + return ERR_PTR(-ENOMEM); + } + memcpy(&ret[*num], strings, len); + kfree(strings); + + strings = (char *)&ret[*num]; + for (p = strings, *num = 0; p < strings + len; p += strlen(p) + 1) + ret[(*num)++] = p; + + return ret; +} + +char **xenbus_directory(struct xenbus_transaction t, + const char *dir, const char *node, unsigned int *num) +{ + char *strings, *path; + unsigned int len; + + path = join(dir, node); + if (IS_ERR(path)) + return (char **)path; + + strings = xs_single(t, XS_DIRECTORY, path, &len); + kfree(path); + if (IS_ERR(strings)) + return (char **)strings; + + return split(strings, len, num); +} +EXPORT_SYMBOL_GPL(xenbus_directory); + +/* Check if a path exists. Return 1 if it does. */ +int xenbus_exists(struct xenbus_transaction t, + const char *dir, const char *node) +{ + char **d; + int dir_n; + + d = xenbus_directory(t, dir, node, &dir_n); + if (IS_ERR(d)) + return 0; + kfree(d); + return 1; +} +EXPORT_SYMBOL_GPL(xenbus_exists); + +/* Get the value of a single file. + * Returns a kmalloced value: call free() on it after use. + * len indicates length in bytes. + */ +void *xenbus_read(struct xenbus_transaction t, + const char *dir, const char *node, unsigned int *len) +{ + char *path; + void *ret; + + path = join(dir, node); + if (IS_ERR(path)) + return (void *)path; + + ret = xs_single(t, XS_READ, path, len); + kfree(path); + return ret; +} +EXPORT_SYMBOL_GPL(xenbus_read); + +/* Write the value of a single file. + * Returns -err on failure. + */ +int xenbus_write(struct xenbus_transaction t, + const char *dir, const char *node, const char *string) +{ + const char *path; + struct kvec iovec[2]; + int ret; + + path = join(dir, node); + if (IS_ERR(path)) + return PTR_ERR(path); + + iovec[0].iov_base = (void *)path; + iovec[0].iov_len = strlen(path) + 1; + iovec[1].iov_base = (void *)string; + iovec[1].iov_len = strlen(string); + + ret = xs_error(xs_talkv(t, XS_WRITE, iovec, ARRAY_SIZE(iovec), NULL)); + kfree(path); + return ret; +} +EXPORT_SYMBOL_GPL(xenbus_write); + +/* Create a new directory. */ +int xenbus_mkdir(struct xenbus_transaction t, + const char *dir, const char *node) +{ + char *path; + int ret; + + path = join(dir, node); + if (IS_ERR(path)) + return PTR_ERR(path); + + ret = xs_error(xs_single(t, XS_MKDIR, path, NULL)); + kfree(path); + return ret; +} +EXPORT_SYMBOL_GPL(xenbus_mkdir); + +/* Destroy a file or directory (directories must be empty). */ +int xenbus_rm(struct xenbus_transaction t, const char *dir, const char *node) +{ + char *path; + int ret; + + path = join(dir, node); + if (IS_ERR(path)) + return PTR_ERR(path); + + ret = xs_error(xs_single(t, XS_RM, path, NULL)); + kfree(path); + return ret; +} +EXPORT_SYMBOL_GPL(xenbus_rm); + +/* Start a transaction: changes by others will not be seen during this + * transaction, and changes will not be visible to others until end. + */ +int xenbus_transaction_start(struct xenbus_transaction *t) +{ + char *id_str; + + down_read(&xs_state.transaction_mutex); + + id_str = xs_single(XBT_NIL, XS_TRANSACTION_START, "", NULL); + if (IS_ERR(id_str)) { + up_read(&xs_state.transaction_mutex); + return PTR_ERR(id_str); + } + + t->id = simple_strtoul(id_str, NULL, 0); + kfree(id_str); + return 0; +} +EXPORT_SYMBOL_GPL(xenbus_transaction_start); + +/* End a transaction. + * If abandon is true, transaction is discarded instead of committed. + */ +int xenbus_transaction_end(struct xenbus_transaction t, int abort) +{ + char abortstr[2]; + int err; + + if (abort) + strcpy(abortstr, "F"); + else + strcpy(abortstr, "T"); + + err = xs_error(xs_single(t, XS_TRANSACTION_END, abortstr, NULL)); + + up_read(&xs_state.transaction_mutex); + + return err; +} +EXPORT_SYMBOL_GPL(xenbus_transaction_end); + +/* Single read and scanf: returns -errno or num scanned. */ +int xenbus_scanf(struct xenbus_transaction t, + const char *dir, const char *node, const char *fmt, ...) +{ + va_list ap; + int ret; + char *val; + + val = xenbus_read(t, dir, node, NULL); + if (IS_ERR(val)) + return PTR_ERR(val); + + va_start(ap, fmt); + ret = vsscanf(val, fmt, ap); + va_end(ap); + kfree(val); + /* Distinctive errno. */ + if (ret == 0) + return -ERANGE; + return ret; +} +EXPORT_SYMBOL_GPL(xenbus_scanf); + +/* Single printf and write: returns -errno or 0. */ +int xenbus_printf(struct xenbus_transaction t, + const char *dir, const char *node, const char *fmt, ...) +{ + va_list ap; + int ret; +#define PRINTF_BUFFER_SIZE 4096 + char *printf_buffer; + + printf_buffer = kmalloc(PRINTF_BUFFER_SIZE, GFP_KERNEL); + if (printf_buffer == NULL) + return -ENOMEM; + + va_start(ap, fmt); + ret = vsnprintf(printf_buffer, PRINTF_BUFFER_SIZE, fmt, ap); + va_end(ap); + + BUG_ON(ret > PRINTF_BUFFER_SIZE-1); + ret = xenbus_write(t, dir, node, printf_buffer); + + kfree(printf_buffer); + + return ret; +} +EXPORT_SYMBOL_GPL(xenbus_printf); + +/* Takes tuples of names, scanf-style args, and void **, NULL terminated. */ +int xenbus_gather(struct xenbus_transaction t, const char *dir, ...) +{ + va_list ap; + const char *name; + int ret = 0; + + va_start(ap, dir); + while (ret == 0 && (name = va_arg(ap, char *)) != NULL) { + const char *fmt = va_arg(ap, char *); + void *result = va_arg(ap, void *); + char *p; + + p = xenbus_read(t, dir, name, NULL); + if (IS_ERR(p)) { + ret = PTR_ERR(p); + break; + } + if (fmt) { + if (sscanf(p, fmt, result) == 0) + ret = -EINVAL; + kfree(p); + } else + *(char **)result = p; + } + va_end(ap); + return ret; +} +EXPORT_SYMBOL_GPL(xenbus_gather); + +static int xs_watch(const char *path, const char *token) +{ + struct kvec iov[2]; + + iov[0].iov_base = (void *)path; + iov[0].iov_len = strlen(path) + 1; + iov[1].iov_base = (void *)token; + iov[1].iov_len = strlen(token) + 1; + + return xs_error(xs_talkv(XBT_NIL, XS_WATCH, iov, + ARRAY_SIZE(iov), NULL)); +} + +static int xs_unwatch(const char *path, const char *token) +{ + struct kvec iov[2]; + + iov[0].iov_base = (char *)path; + iov[0].iov_len = strlen(path) + 1; + iov[1].iov_base = (char *)token; + iov[1].iov_len = strlen(token) + 1; + + return xs_error(xs_talkv(XBT_NIL, XS_UNWATCH, iov, + ARRAY_SIZE(iov), NULL)); +} + +static struct xenbus_watch *find_watch(const char *token) +{ + struct xenbus_watch *i, *cmp; + + cmp = (void *)simple_strtoul(token, NULL, 16); + + list_for_each_entry(i, &watches, list) + if (i == cmp) + return i; + + return NULL; +} + +/* Register callback to watch this node. */ +int register_xenbus_watch(struct xenbus_watch *watch) +{ + /* Pointer in ascii is the token. */ + char token[sizeof(watch) * 2 + 1]; + int err; + + sprintf(token, "%lX", (long)watch); + + down_read(&xs_state.watch_mutex); + + spin_lock(&watches_lock); + BUG_ON(find_watch(token)); + list_add(&watch->list, &watches); + spin_unlock(&watches_lock); + + err = xs_watch(watch->node, token); + + /* Ignore errors due to multiple registration. */ + if ((err != 0) && (err != -EEXIST)) { + spin_lock(&watches_lock); + list_del(&watch->list); + spin_unlock(&watches_lock); + } + + up_read(&xs_state.watch_mutex); + + return err; +} +EXPORT_SYMBOL_GPL(register_xenbus_watch); + +void unregister_xenbus_watch(struct xenbus_watch *watch) +{ + struct xs_stored_msg *msg, *tmp; + char token[sizeof(watch) * 2 + 1]; + int err; + + sprintf(token, "%lX", (long)watch); + + down_read(&xs_state.watch_mutex); + + spin_lock(&watches_lock); + BUG_ON(!find_watch(token)); + list_del(&watch->list); + spin_unlock(&watches_lock); + + err = xs_unwatch(watch->node, token); + if (err) + printk(KERN_WARNING + "XENBUS Failed to release watch %s: %i\n", + watch->node, err); + + up_read(&xs_state.watch_mutex); + + /* Make sure there are no callbacks running currently (unless + its us) */ + if (current->pid != xenwatch_pid) + mutex_lock(&xenwatch_mutex); + + /* Cancel pending watch events. */ + spin_lock(&watch_events_lock); + list_for_each_entry_safe(msg, tmp, &watch_events, list) { + if (msg->u.watch.handle != watch) + continue; + list_del(&msg->list); + kfree(msg->u.watch.vec); + kfree(msg); + } + spin_unlock(&watch_events_lock); + + if (current->pid != xenwatch_pid) + mutex_unlock(&xenwatch_mutex); +} +EXPORT_SYMBOL_GPL(unregister_xenbus_watch); + +void xs_suspend(void) +{ + down_write(&xs_state.transaction_mutex); + down_write(&xs_state.watch_mutex); + mutex_lock(&xs_state.request_mutex); + mutex_lock(&xs_state.response_mutex); +} + +void xs_resume(void) +{ + struct xenbus_watch *watch; + char token[sizeof(watch) * 2 + 1]; + + mutex_unlock(&xs_state.response_mutex); + mutex_unlock(&xs_state.request_mutex); + up_write(&xs_state.transaction_mutex); + + /* No need for watches_lock: the watch_mutex is sufficient. */ + list_for_each_entry(watch, &watches, list) { + sprintf(token, "%lX", (long)watch); + xs_watch(watch->node, token); + } + + up_write(&xs_state.watch_mutex); +} + +void xs_suspend_cancel(void) +{ + mutex_unlock(&xs_state.response_mutex); + mutex_unlock(&xs_state.request_mutex); + up_write(&xs_state.watch_mutex); + up_write(&xs_state.transaction_mutex); +} + +static int xenwatch_thread(void *unused) +{ + struct list_head *ent; + struct xs_stored_msg *msg; + + for (;;) { + wait_event_interruptible(watch_events_waitq, + !list_empty(&watch_events)); + + if (kthread_should_stop()) + break; + + mutex_lock(&xenwatch_mutex); + + spin_lock(&watch_events_lock); + ent = watch_events.next; + if (ent != &watch_events) + list_del(ent); + spin_unlock(&watch_events_lock); + + if (ent != &watch_events) { + msg = list_entry(ent, struct xs_stored_msg, list); + msg->u.watch.handle->callback( + msg->u.watch.handle, + (const char **)msg->u.watch.vec, + msg->u.watch.vec_size); + kfree(msg->u.watch.vec); + kfree(msg); + } + + mutex_unlock(&xenwatch_mutex); + } + + return 0; +} + +static int process_msg(void) +{ + struct xs_stored_msg *msg; + char *body; + int err; + + /* + * We must disallow save/restore while reading a xenstore message. + * A partial read across s/r leaves us out of sync with xenstored. + */ + for (;;) { + err = xb_wait_for_data_to_read(); + if (err) + return err; + mutex_lock(&xs_state.response_mutex); + if (xb_data_to_read()) + break; + /* We raced with save/restore: pending data 'disappeared'. */ + mutex_unlock(&xs_state.response_mutex); + } + + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (msg == NULL) { + err = -ENOMEM; + goto out; + } + + err = xb_read(&msg->hdr, sizeof(msg->hdr)); + if (err) { + kfree(msg); + goto out; + } + + body = kmalloc(msg->hdr.len + 1, GFP_KERNEL); + if (body == NULL) { + kfree(msg); + err = -ENOMEM; + goto out; + } + + err = xb_read(body, msg->hdr.len); + if (err) { + kfree(body); + kfree(msg); + goto out; + } + body[msg->hdr.len] = '\0'; + + if (msg->hdr.type == XS_WATCH_EVENT) { + msg->u.watch.vec = split(body, msg->hdr.len, + &msg->u.watch.vec_size); + if (IS_ERR(msg->u.watch.vec)) { + kfree(msg); + err = PTR_ERR(msg->u.watch.vec); + goto out; + } + + spin_lock(&watches_lock); + msg->u.watch.handle = find_watch( + msg->u.watch.vec[XS_WATCH_TOKEN]); + if (msg->u.watch.handle != NULL) { + spin_lock(&watch_events_lock); + list_add_tail(&msg->list, &watch_events); + wake_up(&watch_events_waitq); + spin_unlock(&watch_events_lock); + } else { + kfree(msg->u.watch.vec); + kfree(msg); + } + spin_unlock(&watches_lock); + } else { + msg->u.reply.body = body; + spin_lock(&xs_state.reply_lock); + list_add_tail(&msg->list, &xs_state.reply_list); + spin_unlock(&xs_state.reply_lock); + wake_up(&xs_state.reply_waitq); + } + + out: + mutex_unlock(&xs_state.response_mutex); + return err; +} + +static int xenbus_thread(void *unused) +{ + int err; + + for (;;) { + err = process_msg(); + if (err) + printk(KERN_WARNING "XENBUS error %d while reading " + "message\n", err); + if (kthread_should_stop()) + break; + } + + return 0; +} + +int xs_init(void) +{ + int err; + struct task_struct *task; + + INIT_LIST_HEAD(&xs_state.reply_list); + spin_lock_init(&xs_state.reply_lock); + init_waitqueue_head(&xs_state.reply_waitq); + + mutex_init(&xs_state.request_mutex); + mutex_init(&xs_state.response_mutex); + init_rwsem(&xs_state.transaction_mutex); + init_rwsem(&xs_state.watch_mutex); + + /* Initialize the shared memory rings to talk to xenstored */ + err = xb_init_comms(); + if (err) + return err; + + task = kthread_run(xenwatch_thread, NULL, "xenwatch"); + if (IS_ERR(task)) + return PTR_ERR(task); + xenwatch_pid = task->pid; + + task = kthread_run(xenbus_thread, NULL, "xenbus"); + if (IS_ERR(task)) + return PTR_ERR(task); + + return 0; +} @@ -251,7 +251,7 @@ config JBD2 config JBD2_DEBUG bool "JBD2 (ext4dev/ext4) debugging support" - depends on JBD2 + depends on JBD2 && DEBUG_FS help If you are using the ext4dev/ext4 journaled file system (or potentially any other filesystem/device using JBD2), this option @@ -260,10 +260,10 @@ config JBD2_DEBUG By default, the debugging output will be turned off. If you select Y here, then you will be able to turn on debugging - with "echo N > /proc/sys/fs/jbd2-debug", where N is a number between - 1 and 5. The higher the number, the more debugging output is - generated. To turn debugging off again, do - "echo 0 > /proc/sys/fs/jbd2-debug". + with "echo N > /sys/kernel/debug/jbd2/jbd2-debug", where N is a + number between 1 and 5. The higher the number, the more debugging + output is generated. To turn debugging off again, do + "echo 0 > /sys/kernel/debug/jbd2/jbd2-debug". config FS_MBCACHE # Meta block cache for Extended Attributes (ext2/ext3/ext4) diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index 9de54ae..e53b4af 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -517,7 +517,7 @@ do_more: /* * An HJ special. This is expensive... */ -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG jbd_unlock_bh_state(bitmap_bh); { struct buffer_head *debug_bh; @@ -1597,7 +1597,7 @@ allocated: performed_allocation = 1; -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG { struct buffer_head *debug_bh; diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index b9ce2412..750c46f 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c @@ -39,6 +39,7 @@ #include <linux/quotaops.h> #include <linux/string.h> #include <linux/slab.h> +#include <linux/falloc.h> #include <linux/ext4_fs_extents.h> #include <asm/uaccess.h> @@ -91,36 +92,6 @@ static void ext4_idx_store_pblock(struct ext4_extent_idx *ix, ext4_fsblk_t pb) ix->ei_leaf_hi = cpu_to_le16((unsigned long) ((pb >> 31) >> 1) & 0xffff); } -static int ext4_ext_check_header(const char *function, struct inode *inode, - struct ext4_extent_header *eh) -{ - const char *error_msg = NULL; - - if (unlikely(eh->eh_magic != EXT4_EXT_MAGIC)) { - error_msg = "invalid magic"; - goto corrupted; - } - if (unlikely(eh->eh_max == 0)) { - error_msg = "invalid eh_max"; - goto corrupted; - } - if (unlikely(le16_to_cpu(eh->eh_entries) > le16_to_cpu(eh->eh_max))) { - error_msg = "invalid eh_entries"; - goto corrupted; - } - return 0; - -corrupted: - ext4_error(inode->i_sb, function, - "bad header in inode #%lu: %s - magic %x, " - "entries %u, max %u, depth %u", - inode->i_ino, error_msg, le16_to_cpu(eh->eh_magic), - le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max), - le16_to_cpu(eh->eh_depth)); - - return -EIO; -} - static handle_t *ext4_ext_journal_restart(handle_t *handle, int needed) { int err; @@ -269,6 +240,70 @@ static int ext4_ext_space_root_idx(struct inode *inode) return size; } +static int +ext4_ext_max_entries(struct inode *inode, int depth) +{ + int max; + + if (depth == ext_depth(inode)) { + if (depth == 0) + max = ext4_ext_space_root(inode); + else + max = ext4_ext_space_root_idx(inode); + } else { + if (depth == 0) + max = ext4_ext_space_block(inode); + else + max = ext4_ext_space_block_idx(inode); + } + + return max; +} + +static int __ext4_ext_check_header(const char *function, struct inode *inode, + struct ext4_extent_header *eh, + int depth) +{ + const char *error_msg; + int max = 0; + + if (unlikely(eh->eh_magic != EXT4_EXT_MAGIC)) { + error_msg = "invalid magic"; + goto corrupted; + } + if (unlikely(le16_to_cpu(eh->eh_depth) != depth)) { + error_msg = "unexpected eh_depth"; + goto corrupted; + } + if (unlikely(eh->eh_max == 0)) { + error_msg = "invalid eh_max"; + goto corrupted; + } + max = ext4_ext_max_entries(inode, depth); + if (unlikely(le16_to_cpu(eh->eh_max) > max)) { + error_msg = "too large eh_max"; + goto corrupted; + } + if (unlikely(le16_to_cpu(eh->eh_entries) > le16_to_cpu(eh->eh_max))) { + error_msg = "invalid eh_entries"; + goto corrupted; + } + return 0; + +corrupted: + ext4_error(inode->i_sb, function, + "bad header in inode #%lu: %s - magic %x, " + "entries %u, max %u(%u), depth %u(%u)", + inode->i_ino, error_msg, le16_to_cpu(eh->eh_magic), + le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max), + max, le16_to_cpu(eh->eh_depth), depth); + + return -EIO; +} + +#define ext4_ext_check_header(inode, eh, depth) \ + __ext4_ext_check_header(__FUNCTION__, inode, eh, depth) + #ifdef EXT_DEBUG static void ext4_ext_show_path(struct inode *inode, struct ext4_ext_path *path) { @@ -282,7 +317,7 @@ static void ext4_ext_show_path(struct inode *inode, struct ext4_ext_path *path) } else if (path->p_ext) { ext_debug(" %d:%d:%llu ", le32_to_cpu(path->p_ext->ee_block), - le16_to_cpu(path->p_ext->ee_len), + ext4_ext_get_actual_len(path->p_ext), ext_pblock(path->p_ext)); } else ext_debug(" []"); @@ -305,7 +340,7 @@ static void ext4_ext_show_leaf(struct inode *inode, struct ext4_ext_path *path) for (i = 0; i < le16_to_cpu(eh->eh_entries); i++, ex++) { ext_debug("%d:%d:%llu ", le32_to_cpu(ex->ee_block), - le16_to_cpu(ex->ee_len), ext_pblock(ex)); + ext4_ext_get_actual_len(ex), ext_pblock(ex)); } ext_debug("\n"); } @@ -329,6 +364,7 @@ static void ext4_ext_drop_refs(struct ext4_ext_path *path) /* * ext4_ext_binsearch_idx: * binary search for the closest index of the given block + * the header must be checked before calling this */ static void ext4_ext_binsearch_idx(struct inode *inode, struct ext4_ext_path *path, int block) @@ -336,27 +372,25 @@ ext4_ext_binsearch_idx(struct inode *inode, struct ext4_ext_path *path, int bloc struct ext4_extent_header *eh = path->p_hdr; struct ext4_extent_idx *r, *l, *m; - BUG_ON(eh->eh_magic != EXT4_EXT_MAGIC); - BUG_ON(le16_to_cpu(eh->eh_entries) > le16_to_cpu(eh->eh_max)); - BUG_ON(le16_to_cpu(eh->eh_entries) <= 0); ext_debug("binsearch for %d(idx): ", block); l = EXT_FIRST_INDEX(eh) + 1; - r = EXT_FIRST_INDEX(eh) + le16_to_cpu(eh->eh_entries) - 1; + r = EXT_LAST_INDEX(eh); while (l <= r) { m = l + (r - l) / 2; if (block < le32_to_cpu(m->ei_block)) r = m - 1; else l = m + 1; - ext_debug("%p(%u):%p(%u):%p(%u) ", l, l->ei_block, - m, m->ei_block, r, r->ei_block); + ext_debug("%p(%u):%p(%u):%p(%u) ", l, le32_to_cpu(l->ei_block), + m, le32_to_cpu(m->ei_block), + r, le32_to_cpu(r->ei_block)); } path->p_idx = l - 1; ext_debug(" -> %d->%lld ", le32_to_cpu(path->p_idx->ei_block), - idx_block(path->p_idx)); + idx_pblock(path->p_idx)); #ifdef CHECK_BINSEARCH { @@ -388,6 +422,7 @@ ext4_ext_binsearch_idx(struct inode *inode, struct ext4_ext_path *path, int bloc /* * ext4_ext_binsearch: * binary search for closest extent of the given block + * the header must be checked before calling this */ static void ext4_ext_binsearch(struct inode *inode, struct ext4_ext_path *path, int block) @@ -395,9 +430,6 @@ ext4_ext_binsearch(struct inode *inode, struct ext4_ext_path *path, int block) struct ext4_extent_header *eh = path->p_hdr; struct ext4_extent *r, *l, *m; - BUG_ON(eh->eh_magic != EXT4_EXT_MAGIC); - BUG_ON(le16_to_cpu(eh->eh_entries) > le16_to_cpu(eh->eh_max)); - if (eh->eh_entries == 0) { /* * this leaf is empty: @@ -409,7 +441,7 @@ ext4_ext_binsearch(struct inode *inode, struct ext4_ext_path *path, int block) ext_debug("binsearch for %d: ", block); l = EXT_FIRST_EXTENT(eh) + 1; - r = EXT_FIRST_EXTENT(eh) + le16_to_cpu(eh->eh_entries) - 1; + r = EXT_LAST_EXTENT(eh); while (l <= r) { m = l + (r - l) / 2; @@ -417,15 +449,16 @@ ext4_ext_binsearch(struct inode *inode, struct ext4_ext_path *path, int block) r = m - 1; else l = m + 1; - ext_debug("%p(%u):%p(%u):%p(%u) ", l, l->ee_block, - m, m->ee_block, r, r->ee_block); + ext_debug("%p(%u):%p(%u):%p(%u) ", l, le32_to_cpu(l->ee_block), + m, le32_to_cpu(m->ee_block), + r, le32_to_cpu(r->ee_block)); } path->p_ext = l - 1; ext_debug(" -> %d:%llu:%d ", le32_to_cpu(path->p_ext->ee_block), ext_pblock(path->p_ext), - le16_to_cpu(path->p_ext->ee_len)); + ext4_ext_get_actual_len(path->p_ext)); #ifdef CHECK_BINSEARCH { @@ -468,11 +501,10 @@ ext4_ext_find_extent(struct inode *inode, int block, struct ext4_ext_path *path) short int depth, i, ppos = 0, alloc = 0; eh = ext_inode_hdr(inode); - BUG_ON(eh == NULL); - if (ext4_ext_check_header(__FUNCTION__, inode, eh)) + depth = ext_depth(inode); + if (ext4_ext_check_header(inode, eh, depth)) return ERR_PTR(-EIO); - i = depth = ext_depth(inode); /* account possible depth increase */ if (!path) { @@ -484,10 +516,12 @@ ext4_ext_find_extent(struct inode *inode, int block, struct ext4_ext_path *path) } path[0].p_hdr = eh; + i = depth; /* walk through the tree */ while (i) { ext_debug("depth %d: num %d, max %d\n", ppos, le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max)); + ext4_ext_binsearch_idx(inode, path + ppos, block); path[ppos].p_block = idx_pblock(path[ppos].p_idx); path[ppos].p_depth = i; @@ -504,7 +538,7 @@ ext4_ext_find_extent(struct inode *inode, int block, struct ext4_ext_path *path) path[ppos].p_hdr = eh; i--; - if (ext4_ext_check_header(__FUNCTION__, inode, eh)) + if (ext4_ext_check_header(inode, eh, i)) goto err; } @@ -513,9 +547,6 @@ ext4_ext_find_extent(struct inode *inode, int block, struct ext4_ext_path *path) path[ppos].p_ext = NULL; path[ppos].p_idx = NULL; - if (ext4_ext_check_header(__FUNCTION__, inode, eh)) - goto err; - /* find extent */ ext4_ext_binsearch(inode, path + ppos, block); @@ -553,7 +584,7 @@ static int ext4_ext_insert_index(handle_t *handle, struct inode *inode, if (curp->p_idx != EXT_LAST_INDEX(curp->p_hdr)) { len = (len - 1) * sizeof(struct ext4_extent_idx); len = len < 0 ? 0 : len; - ext_debug("insert new index %d after: %d. " + ext_debug("insert new index %d after: %llu. " "move %d from 0x%p to 0x%p\n", logical, ptr, len, (curp->p_idx + 1), (curp->p_idx + 2)); @@ -564,7 +595,7 @@ static int ext4_ext_insert_index(handle_t *handle, struct inode *inode, /* insert before */ len = len * sizeof(struct ext4_extent_idx); len = len < 0 ? 0 : len; - ext_debug("insert new index %d before: %d. " + ext_debug("insert new index %d before: %llu. " "move %d from 0x%p to 0x%p\n", logical, ptr, len, curp->p_idx, (curp->p_idx + 1)); @@ -686,7 +717,7 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode, ext_debug("move %d:%llu:%d in new leaf %llu\n", le32_to_cpu(path[depth].p_ext->ee_block), ext_pblock(path[depth].p_ext), - le16_to_cpu(path[depth].p_ext->ee_len), + ext4_ext_get_actual_len(path[depth].p_ext), newblock); /*memmove(ex++, path[depth].p_ext++, sizeof(struct ext4_extent)); @@ -764,7 +795,7 @@ static int ext4_ext_split(handle_t *handle, struct inode *inode, BUG_ON(EXT_MAX_INDEX(path[i].p_hdr) != EXT_LAST_INDEX(path[i].p_hdr)); while (path[i].p_idx <= EXT_MAX_INDEX(path[i].p_hdr)) { - ext_debug("%d: move %d:%d in new index %llu\n", i, + ext_debug("%d: move %d:%llu in new index %llu\n", i, le32_to_cpu(path[i].p_idx->ei_block), idx_pblock(path[i].p_idx), newblock); @@ -893,8 +924,13 @@ static int ext4_ext_grow_indepth(handle_t *handle, struct inode *inode, curp->p_hdr->eh_max = cpu_to_le16(ext4_ext_space_root_idx(inode)); curp->p_hdr->eh_entries = cpu_to_le16(1); curp->p_idx = EXT_FIRST_INDEX(curp->p_hdr); - /* FIXME: it works, but actually path[0] can be index */ - curp->p_idx->ei_block = EXT_FIRST_EXTENT(path[0].p_hdr)->ee_block; + + if (path[0].p_hdr->eh_depth) + curp->p_idx->ei_block = + EXT_FIRST_INDEX(path[0].p_hdr)->ei_block; + else + curp->p_idx->ei_block = + EXT_FIRST_EXTENT(path[0].p_hdr)->ee_block; ext4_idx_store_pblock(curp->p_idx, newblock); neh = ext_inode_hdr(inode); @@ -1106,7 +1142,24 @@ static int ext4_can_extents_be_merged(struct inode *inode, struct ext4_extent *ex1, struct ext4_extent *ex2) { - if (le32_to_cpu(ex1->ee_block) + le16_to_cpu(ex1->ee_len) != + unsigned short ext1_ee_len, ext2_ee_len, max_len; + + /* + * Make sure that either both extents are uninitialized, or + * both are _not_. + */ + if (ext4_ext_is_uninitialized(ex1) ^ ext4_ext_is_uninitialized(ex2)) + return 0; + + if (ext4_ext_is_uninitialized(ex1)) + max_len = EXT_UNINIT_MAX_LEN; + else + max_len = EXT_INIT_MAX_LEN; + + ext1_ee_len = ext4_ext_get_actual_len(ex1); + ext2_ee_len = ext4_ext_get_actual_len(ex2); + + if (le32_to_cpu(ex1->ee_block) + ext1_ee_len != le32_to_cpu(ex2->ee_block)) return 0; @@ -1115,19 +1168,66 @@ ext4_can_extents_be_merged(struct inode *inode, struct ext4_extent *ex1, * as an RO_COMPAT feature, refuse to merge to extents if * this can result in the top bit of ee_len being set. */ - if (le16_to_cpu(ex1->ee_len) + le16_to_cpu(ex2->ee_len) > EXT_MAX_LEN) + if (ext1_ee_len + ext2_ee_len > max_len) return 0; #ifdef AGGRESSIVE_TEST if (le16_to_cpu(ex1->ee_len) >= 4) return 0; #endif - if (ext_pblock(ex1) + le16_to_cpu(ex1->ee_len) == ext_pblock(ex2)) + if (ext_pblock(ex1) + ext1_ee_len == ext_pblock(ex2)) return 1; return 0; } /* + * This function tries to merge the "ex" extent to the next extent in the tree. + * It always tries to merge towards right. If you want to merge towards + * left, pass "ex - 1" as argument instead of "ex". + * Returns 0 if the extents (ex and ex+1) were _not_ merged and returns + * 1 if they got merged. + */ +int ext4_ext_try_to_merge(struct inode *inode, + struct ext4_ext_path *path, + struct ext4_extent *ex) +{ + struct ext4_extent_header *eh; + unsigned int depth, len; + int merge_done = 0; + int uninitialized = 0; + + depth = ext_depth(inode); + BUG_ON(path[depth].p_hdr == NULL); + eh = path[depth].p_hdr; + + while (ex < EXT_LAST_EXTENT(eh)) { + if (!ext4_can_extents_be_merged(inode, ex, ex + 1)) + break; + /* merge with next extent! */ + if (ext4_ext_is_uninitialized(ex)) + uninitialized = 1; + ex->ee_len = cpu_to_le16(ext4_ext_get_actual_len(ex) + + ext4_ext_get_actual_len(ex + 1)); + if (uninitialized) + ext4_ext_mark_uninitialized(ex); + + if (ex + 1 < EXT_LAST_EXTENT(eh)) { + len = (EXT_LAST_EXTENT(eh) - ex - 1) + * sizeof(struct ext4_extent); + memmove(ex + 1, ex + 2, len); + } + eh->eh_entries = cpu_to_le16(le16_to_cpu(eh->eh_entries) - 1); + merge_done = 1; + WARN_ON(eh->eh_entries == 0); + if (!eh->eh_entries) + ext4_error(inode->i_sb, "ext4_ext_try_to_merge", + "inode#%lu, eh->eh_entries = 0!", inode->i_ino); + } + + return merge_done; +} + +/* * check if a portion of the "newext" extent overlaps with an * existing extent. * @@ -1144,7 +1244,7 @@ unsigned int ext4_ext_check_overlap(struct inode *inode, unsigned int ret = 0; b1 = le32_to_cpu(newext->ee_block); - len1 = le16_to_cpu(newext->ee_len); + len1 = ext4_ext_get_actual_len(newext); depth = ext_depth(inode); if (!path[depth].p_ext) goto out; @@ -1191,8 +1291,9 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode, struct ext4_extent *nearex; /* nearest extent */ struct ext4_ext_path *npath = NULL; int depth, len, err, next; + unsigned uninitialized = 0; - BUG_ON(newext->ee_len == 0); + BUG_ON(ext4_ext_get_actual_len(newext) == 0); depth = ext_depth(inode); ex = path[depth].p_ext; BUG_ON(path[depth].p_hdr == NULL); @@ -1200,14 +1301,24 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode, /* try to insert block into found extent and return */ if (ex && ext4_can_extents_be_merged(inode, ex, newext)) { ext_debug("append %d block to %d:%d (from %llu)\n", - le16_to_cpu(newext->ee_len), + ext4_ext_get_actual_len(newext), le32_to_cpu(ex->ee_block), - le16_to_cpu(ex->ee_len), ext_pblock(ex)); + ext4_ext_get_actual_len(ex), ext_pblock(ex)); err = ext4_ext_get_access(handle, inode, path + depth); if (err) return err; - ex->ee_len = cpu_to_le16(le16_to_cpu(ex->ee_len) - + le16_to_cpu(newext->ee_len)); + + /* + * ext4_can_extents_be_merged should have checked that either + * both extents are uninitialized, or both aren't. Thus we + * need to check only one of them here. + */ + if (ext4_ext_is_uninitialized(ex)) + uninitialized = 1; + ex->ee_len = cpu_to_le16(ext4_ext_get_actual_len(ex) + + ext4_ext_get_actual_len(newext)); + if (uninitialized) + ext4_ext_mark_uninitialized(ex); eh = path[depth].p_hdr; nearex = ex; goto merge; @@ -1263,7 +1374,7 @@ has_space: ext_debug("first extent in the leaf: %d:%llu:%d\n", le32_to_cpu(newext->ee_block), ext_pblock(newext), - le16_to_cpu(newext->ee_len)); + ext4_ext_get_actual_len(newext)); path[depth].p_ext = EXT_FIRST_EXTENT(eh); } else if (le32_to_cpu(newext->ee_block) > le32_to_cpu(nearex->ee_block)) { @@ -1276,7 +1387,7 @@ has_space: "move %d from 0x%p to 0x%p\n", le32_to_cpu(newext->ee_block), ext_pblock(newext), - le16_to_cpu(newext->ee_len), + ext4_ext_get_actual_len(newext), nearex, len, nearex + 1, nearex + 2); memmove(nearex + 2, nearex + 1, len); } @@ -1289,7 +1400,7 @@ has_space: "move %d from 0x%p to 0x%p\n", le32_to_cpu(newext->ee_block), ext_pblock(newext), - le16_to_cpu(newext->ee_len), + ext4_ext_get_actual_len(newext), nearex, len, nearex + 1, nearex + 2); memmove(nearex + 1, nearex, len); path[depth].p_ext = nearex; @@ -1304,20 +1415,7 @@ has_space: merge: /* try to merge extents to the right */ - while (nearex < EXT_LAST_EXTENT(eh)) { - if (!ext4_can_extents_be_merged(inode, nearex, nearex + 1)) - break; - /* merge with next extent! */ - nearex->ee_len = cpu_to_le16(le16_to_cpu(nearex->ee_len) - + le16_to_cpu(nearex[1].ee_len)); - if (nearex + 1 < EXT_LAST_EXTENT(eh)) { - len = (EXT_LAST_EXTENT(eh) - nearex - 1) - * sizeof(struct ext4_extent); - memmove(nearex + 1, nearex + 2, len); - } - eh->eh_entries = cpu_to_le16(le16_to_cpu(eh->eh_entries)-1); - BUG_ON(eh->eh_entries == 0); - } + ext4_ext_try_to_merge(inode, path, nearex); /* try to merge extents to the left */ @@ -1379,8 +1477,8 @@ int ext4_ext_walk_space(struct inode *inode, unsigned long block, end = le32_to_cpu(ex->ee_block); if (block + num < end) end = block + num; - } else if (block >= - le32_to_cpu(ex->ee_block) + le16_to_cpu(ex->ee_len)) { + } else if (block >= le32_to_cpu(ex->ee_block) + + ext4_ext_get_actual_len(ex)) { /* need to allocate space after found extent */ start = block; end = block + num; @@ -1392,7 +1490,8 @@ int ext4_ext_walk_space(struct inode *inode, unsigned long block, * by found extent */ start = block; - end = le32_to_cpu(ex->ee_block) + le16_to_cpu(ex->ee_len); + end = le32_to_cpu(ex->ee_block) + + ext4_ext_get_actual_len(ex); if (block + num < end) end = block + num; exists = 1; @@ -1408,7 +1507,7 @@ int ext4_ext_walk_space(struct inode *inode, unsigned long block, cbex.ec_type = EXT4_EXT_CACHE_GAP; } else { cbex.ec_block = le32_to_cpu(ex->ee_block); - cbex.ec_len = le16_to_cpu(ex->ee_len); + cbex.ec_len = ext4_ext_get_actual_len(ex); cbex.ec_start = ext_pblock(ex); cbex.ec_type = EXT4_EXT_CACHE_EXTENT; } @@ -1481,15 +1580,15 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path, ext_debug("cache gap(before): %lu [%lu:%lu]", (unsigned long) block, (unsigned long) le32_to_cpu(ex->ee_block), - (unsigned long) le16_to_cpu(ex->ee_len)); + (unsigned long) ext4_ext_get_actual_len(ex)); } else if (block >= le32_to_cpu(ex->ee_block) - + le16_to_cpu(ex->ee_len)) { + + ext4_ext_get_actual_len(ex)) { lblock = le32_to_cpu(ex->ee_block) - + le16_to_cpu(ex->ee_len); + + ext4_ext_get_actual_len(ex); len = ext4_ext_next_allocated_block(path); ext_debug("cache gap(after): [%lu:%lu] %lu", (unsigned long) le32_to_cpu(ex->ee_block), - (unsigned long) le16_to_cpu(ex->ee_len), + (unsigned long) ext4_ext_get_actual_len(ex), (unsigned long) block); BUG_ON(len == lblock); len = len - lblock; @@ -1619,12 +1718,12 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode, unsigned long from, unsigned long to) { struct buffer_head *bh; + unsigned short ee_len = ext4_ext_get_actual_len(ex); int i; #ifdef EXTENTS_STATS { struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); - unsigned short ee_len = le16_to_cpu(ex->ee_len); spin_lock(&sbi->s_ext_stats_lock); sbi->s_ext_blocks += ee_len; sbi->s_ext_extents++; @@ -1638,12 +1737,12 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode, } #endif if (from >= le32_to_cpu(ex->ee_block) - && to == le32_to_cpu(ex->ee_block) + le16_to_cpu(ex->ee_len) - 1) { + && to == le32_to_cpu(ex->ee_block) + ee_len - 1) { /* tail removal */ unsigned long num; ext4_fsblk_t start; - num = le32_to_cpu(ex->ee_block) + le16_to_cpu(ex->ee_len) - from; - start = ext_pblock(ex) + le16_to_cpu(ex->ee_len) - num; + num = le32_to_cpu(ex->ee_block) + ee_len - from; + start = ext_pblock(ex) + ee_len - num; ext_debug("free last %lu blocks starting %llu\n", num, start); for (i = 0; i < num; i++) { bh = sb_find_get_block(inode->i_sb, start + i); @@ -1651,12 +1750,12 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode, } ext4_free_blocks(handle, inode, start, num); } else if (from == le32_to_cpu(ex->ee_block) - && to <= le32_to_cpu(ex->ee_block) + le16_to_cpu(ex->ee_len) - 1) { + && to <= le32_to_cpu(ex->ee_block) + ee_len - 1) { printk("strange request: removal %lu-%lu from %u:%u\n", - from, to, le32_to_cpu(ex->ee_block), le16_to_cpu(ex->ee_len)); + from, to, le32_to_cpu(ex->ee_block), ee_len); } else { printk("strange request: removal(2) %lu-%lu from %u:%u\n", - from, to, le32_to_cpu(ex->ee_block), le16_to_cpu(ex->ee_len)); + from, to, le32_to_cpu(ex->ee_block), ee_len); } return 0; } @@ -1671,21 +1770,23 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, unsigned a, b, block, num; unsigned long ex_ee_block; unsigned short ex_ee_len; + unsigned uninitialized = 0; struct ext4_extent *ex; + /* the header must be checked already in ext4_ext_remove_space() */ ext_debug("truncate since %lu in leaf\n", start); if (!path[depth].p_hdr) path[depth].p_hdr = ext_block_hdr(path[depth].p_bh); eh = path[depth].p_hdr; BUG_ON(eh == NULL); - BUG_ON(le16_to_cpu(eh->eh_entries) > le16_to_cpu(eh->eh_max)); - BUG_ON(eh->eh_magic != EXT4_EXT_MAGIC); /* find where to start removing */ ex = EXT_LAST_EXTENT(eh); ex_ee_block = le32_to_cpu(ex->ee_block); - ex_ee_len = le16_to_cpu(ex->ee_len); + if (ext4_ext_is_uninitialized(ex)) + uninitialized = 1; + ex_ee_len = ext4_ext_get_actual_len(ex); while (ex >= EXT_FIRST_EXTENT(eh) && ex_ee_block + ex_ee_len > start) { @@ -1753,6 +1854,12 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, ex->ee_block = cpu_to_le32(block); ex->ee_len = cpu_to_le16(num); + /* + * Do not mark uninitialized if all the blocks in the + * extent have been removed. + */ + if (uninitialized && num) + ext4_ext_mark_uninitialized(ex); err = ext4_ext_dirty(handle, inode, path + depth); if (err) @@ -1762,7 +1869,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, ext_pblock(ex)); ex--; ex_ee_block = le32_to_cpu(ex->ee_block); - ex_ee_len = le16_to_cpu(ex->ee_len); + ex_ee_len = ext4_ext_get_actual_len(ex); } if (correct_index && eh->eh_entries) @@ -1825,7 +1932,7 @@ int ext4_ext_remove_space(struct inode *inode, unsigned long start) return -ENOMEM; } path[0].p_hdr = ext_inode_hdr(inode); - if (ext4_ext_check_header(__FUNCTION__, inode, path[0].p_hdr)) { + if (ext4_ext_check_header(inode, path[0].p_hdr, depth)) { err = -EIO; goto out; } @@ -1846,17 +1953,8 @@ int ext4_ext_remove_space(struct inode *inode, unsigned long start) if (!path[i].p_hdr) { ext_debug("initialize header\n"); path[i].p_hdr = ext_block_hdr(path[i].p_bh); - if (ext4_ext_check_header(__FUNCTION__, inode, - path[i].p_hdr)) { - err = -EIO; - goto out; - } } - BUG_ON(le16_to_cpu(path[i].p_hdr->eh_entries) - > le16_to_cpu(path[i].p_hdr->eh_max)); - BUG_ON(path[i].p_hdr->eh_magic != EXT4_EXT_MAGIC); - if (!path[i].p_idx) { /* this level hasn't been touched yet */ path[i].p_idx = EXT_LAST_INDEX(path[i].p_hdr); @@ -1873,17 +1971,27 @@ int ext4_ext_remove_space(struct inode *inode, unsigned long start) i, EXT_FIRST_INDEX(path[i].p_hdr), path[i].p_idx); if (ext4_ext_more_to_rm(path + i)) { + struct buffer_head *bh; /* go to the next level */ ext_debug("move to level %d (block %llu)\n", i + 1, idx_pblock(path[i].p_idx)); memset(path + i + 1, 0, sizeof(*path)); - path[i+1].p_bh = - sb_bread(sb, idx_pblock(path[i].p_idx)); - if (!path[i+1].p_bh) { + bh = sb_bread(sb, idx_pblock(path[i].p_idx)); + if (!bh) { /* should we reset i_size? */ err = -EIO; break; } + if (WARN_ON(i + 1 > depth)) { + err = -EIO; + break; + } + if (ext4_ext_check_header(inode, ext_block_hdr(bh), + depth - i - 1)) { + err = -EIO; + break; + } + path[i + 1].p_bh = bh; /* save actual number of indexes since this * number is changed at the next iteration */ @@ -1977,15 +2085,158 @@ void ext4_ext_release(struct super_block *sb) #endif } +/* + * This function is called by ext4_ext_get_blocks() if someone tries to write + * to an uninitialized extent. It may result in splitting the uninitialized + * extent into multiple extents (upto three - one initialized and two + * uninitialized). + * There are three possibilities: + * a> There is no split required: Entire extent should be initialized + * b> Splits in two extents: Write is happening at either end of the extent + * c> Splits in three extents: Somone is writing in middle of the extent + */ +int ext4_ext_convert_to_initialized(handle_t *handle, struct inode *inode, + struct ext4_ext_path *path, + ext4_fsblk_t iblock, + unsigned long max_blocks) +{ + struct ext4_extent *ex, newex; + struct ext4_extent *ex1 = NULL; + struct ext4_extent *ex2 = NULL; + struct ext4_extent *ex3 = NULL; + struct ext4_extent_header *eh; + unsigned int allocated, ee_block, ee_len, depth; + ext4_fsblk_t newblock; + int err = 0; + int ret = 0; + + depth = ext_depth(inode); + eh = path[depth].p_hdr; + ex = path[depth].p_ext; + ee_block = le32_to_cpu(ex->ee_block); + ee_len = ext4_ext_get_actual_len(ex); + allocated = ee_len - (iblock - ee_block); + newblock = iblock - ee_block + ext_pblock(ex); + ex2 = ex; + + /* ex1: ee_block to iblock - 1 : uninitialized */ + if (iblock > ee_block) { + ex1 = ex; + ex1->ee_len = cpu_to_le16(iblock - ee_block); + ext4_ext_mark_uninitialized(ex1); + ex2 = &newex; + } + /* + * for sanity, update the length of the ex2 extent before + * we insert ex3, if ex1 is NULL. This is to avoid temporary + * overlap of blocks. + */ + if (!ex1 && allocated > max_blocks) + ex2->ee_len = cpu_to_le16(max_blocks); + /* ex3: to ee_block + ee_len : uninitialised */ + if (allocated > max_blocks) { + unsigned int newdepth; + ex3 = &newex; + ex3->ee_block = cpu_to_le32(iblock + max_blocks); + ext4_ext_store_pblock(ex3, newblock + max_blocks); + ex3->ee_len = cpu_to_le16(allocated - max_blocks); + ext4_ext_mark_uninitialized(ex3); + err = ext4_ext_insert_extent(handle, inode, path, ex3); + if (err) + goto out; + /* + * The depth, and hence eh & ex might change + * as part of the insert above. + */ + newdepth = ext_depth(inode); + if (newdepth != depth) { + depth = newdepth; + path = ext4_ext_find_extent(inode, iblock, NULL); + if (IS_ERR(path)) { + err = PTR_ERR(path); + path = NULL; + goto out; + } + eh = path[depth].p_hdr; + ex = path[depth].p_ext; + if (ex2 != &newex) + ex2 = ex; + } + allocated = max_blocks; + } + /* + * If there was a change of depth as part of the + * insertion of ex3 above, we need to update the length + * of the ex1 extent again here + */ + if (ex1 && ex1 != ex) { + ex1 = ex; + ex1->ee_len = cpu_to_le16(iblock - ee_block); + ext4_ext_mark_uninitialized(ex1); + ex2 = &newex; + } + /* ex2: iblock to iblock + maxblocks-1 : initialised */ + ex2->ee_block = cpu_to_le32(iblock); + ex2->ee_start = cpu_to_le32(newblock); + ext4_ext_store_pblock(ex2, newblock); + ex2->ee_len = cpu_to_le16(allocated); + if (ex2 != ex) + goto insert; + err = ext4_ext_get_access(handle, inode, path + depth); + if (err) + goto out; + /* + * New (initialized) extent starts from the first block + * in the current extent. i.e., ex2 == ex + * We have to see if it can be merged with the extent + * on the left. + */ + if (ex2 > EXT_FIRST_EXTENT(eh)) { + /* + * To merge left, pass "ex2 - 1" to try_to_merge(), + * since it merges towards right _only_. + */ + ret = ext4_ext_try_to_merge(inode, path, ex2 - 1); + if (ret) { + err = ext4_ext_correct_indexes(handle, inode, path); + if (err) + goto out; + depth = ext_depth(inode); + ex2--; + } + } + /* + * Try to Merge towards right. This might be required + * only when the whole extent is being written to. + * i.e. ex2 == ex and ex3 == NULL. + */ + if (!ex3) { + ret = ext4_ext_try_to_merge(inode, path, ex2); + if (ret) { + err = ext4_ext_correct_indexes(handle, inode, path); + if (err) + goto out; + } + } + /* Mark modified extent as dirty */ + err = ext4_ext_dirty(handle, inode, path + depth); + goto out; +insert: + err = ext4_ext_insert_extent(handle, inode, path, &newex); +out: + return err ? err : allocated; +} + int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, ext4_fsblk_t iblock, unsigned long max_blocks, struct buffer_head *bh_result, int create, int extend_disksize) { struct ext4_ext_path *path = NULL; + struct ext4_extent_header *eh; struct ext4_extent newex, *ex; ext4_fsblk_t goal, newblock; - int err = 0, depth; + int err = 0, depth, ret; unsigned long allocated = 0; __clear_bit(BH_New, &bh_result->b_state); @@ -1998,8 +2249,10 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, if (goal) { if (goal == EXT4_EXT_CACHE_GAP) { if (!create) { - /* block isn't allocated yet and - * user doesn't want to allocate it */ + /* + * block isn't allocated yet and + * user doesn't want to allocate it + */ goto out2; } /* we should allocate requested block */ @@ -2033,21 +2286,19 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, * this is why assert can't be put in ext4_ext_find_extent() */ BUG_ON(path[depth].p_ext == NULL && depth != 0); + eh = path[depth].p_hdr; ex = path[depth].p_ext; if (ex) { unsigned long ee_block = le32_to_cpu(ex->ee_block); ext4_fsblk_t ee_start = ext_pblock(ex); - unsigned short ee_len = le16_to_cpu(ex->ee_len); + unsigned short ee_len; /* - * Allow future support for preallocated extents to be added - * as an RO_COMPAT feature: * Uninitialized extents are treated as holes, except that - * we avoid (fail) allocating new blocks during a write. + * we split out initialized portions during a write. */ - if (ee_len > EXT_MAX_LEN) - goto out2; + ee_len = ext4_ext_get_actual_len(ex); /* if found extent covers block, simply return it */ if (iblock >= ee_block && iblock < ee_block + ee_len) { newblock = iblock - ee_block + ee_start; @@ -2055,9 +2306,27 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, allocated = ee_len - (iblock - ee_block); ext_debug("%d fit into %lu:%d -> %llu\n", (int) iblock, ee_block, ee_len, newblock); - ext4_ext_put_in_cache(inode, ee_block, ee_len, - ee_start, EXT4_EXT_CACHE_EXTENT); - goto out; + + /* Do not put uninitialized extent in the cache */ + if (!ext4_ext_is_uninitialized(ex)) { + ext4_ext_put_in_cache(inode, ee_block, + ee_len, ee_start, + EXT4_EXT_CACHE_EXTENT); + goto out; + } + if (create == EXT4_CREATE_UNINITIALIZED_EXT) + goto out; + if (!create) + goto out2; + + ret = ext4_ext_convert_to_initialized(handle, inode, + path, iblock, + max_blocks); + if (ret <= 0) + goto out2; + else + allocated = ret; + goto outnew; } } @@ -2066,8 +2335,10 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, * we couldn't try to create block if create flag is zero */ if (!create) { - /* put just found gap into cache to speed up - * subsequent requests */ + /* + * put just found gap into cache to speed up + * subsequent requests + */ ext4_ext_put_gap_in_cache(inode, path, iblock); goto out2; } @@ -2081,6 +2352,19 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, /* allocate new block */ goal = ext4_ext_find_goal(inode, path, iblock); + /* + * See if request is beyond maximum number of blocks we can have in + * a single extent. For an initialized extent this limit is + * EXT_INIT_MAX_LEN and for an uninitialized extent this limit is + * EXT_UNINIT_MAX_LEN. + */ + if (max_blocks > EXT_INIT_MAX_LEN && + create != EXT4_CREATE_UNINITIALIZED_EXT) + max_blocks = EXT_INIT_MAX_LEN; + else if (max_blocks > EXT_UNINIT_MAX_LEN && + create == EXT4_CREATE_UNINITIALIZED_EXT) + max_blocks = EXT_UNINIT_MAX_LEN; + /* Check if we can really insert (iblock)::(iblock+max_blocks) extent */ newex.ee_block = cpu_to_le32(iblock); newex.ee_len = cpu_to_le16(max_blocks); @@ -2098,6 +2382,8 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, /* try to insert new extent into found leaf and return */ ext4_ext_store_pblock(&newex, newblock); newex.ee_len = cpu_to_le16(allocated); + if (create == EXT4_CREATE_UNINITIALIZED_EXT) /* Mark uninitialized */ + ext4_ext_mark_uninitialized(&newex); err = ext4_ext_insert_extent(handle, inode, path, &newex); if (err) { /* free data blocks we just allocated */ @@ -2111,10 +2397,13 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, /* previous routine could use block we allocated */ newblock = ext_pblock(&newex); +outnew: __set_bit(BH_New, &bh_result->b_state); - ext4_ext_put_in_cache(inode, iblock, allocated, newblock, - EXT4_EXT_CACHE_EXTENT); + /* Cache only when it is _not_ an uninitialized extent */ + if (create != EXT4_CREATE_UNINITIALIZED_EXT) + ext4_ext_put_in_cache(inode, iblock, allocated, newblock, + EXT4_EXT_CACHE_EXTENT); out: if (allocated > max_blocks) allocated = max_blocks; @@ -2178,7 +2467,8 @@ void ext4_ext_truncate(struct inode * inode, struct page *page) err = ext4_ext_remove_space(inode, last_block); /* In a multi-transaction truncate, we only make the final - * transaction synchronous. */ + * transaction synchronous. + */ if (IS_SYNC(inode)) handle->h_sync = 1; @@ -2217,3 +2507,127 @@ int ext4_ext_writepage_trans_blocks(struct inode *inode, int num) return needed; } + +/* + * preallocate space for a file. This implements ext4's fallocate inode + * operation, which gets called from sys_fallocate system call. + * For block-mapped files, posix_fallocate should fall back to the method + * of writing zeroes to the required new blocks (the same behavior which is + * expected for file systems which do not support fallocate() system call). + */ +long ext4_fallocate(struct inode *inode, int mode, loff_t offset, loff_t len) +{ + handle_t *handle; + ext4_fsblk_t block, max_blocks; + ext4_fsblk_t nblocks = 0; + int ret = 0; + int ret2 = 0; + int retries = 0; + struct buffer_head map_bh; + unsigned int credits, blkbits = inode->i_blkbits; + + /* + * currently supporting (pre)allocate mode for extent-based + * files _only_ + */ + if (!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)) + return -EOPNOTSUPP; + + /* preallocation to directories is currently not supported */ + if (S_ISDIR(inode->i_mode)) + return -ENODEV; + + block = offset >> blkbits; + max_blocks = (EXT4_BLOCK_ALIGN(len + offset, blkbits) >> blkbits) + - block; + + /* + * credits to insert 1 extent into extent tree + buffers to be able to + * modify 1 super block, 1 block bitmap and 1 group descriptor. + */ + credits = EXT4_DATA_TRANS_BLOCKS(inode->i_sb) + 3; +retry: + while (ret >= 0 && ret < max_blocks) { + block = block + ret; + max_blocks = max_blocks - ret; + handle = ext4_journal_start(inode, credits); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + break; + } + + ret = ext4_ext_get_blocks(handle, inode, block, + max_blocks, &map_bh, + EXT4_CREATE_UNINITIALIZED_EXT, 0); + WARN_ON(!ret); + if (!ret) { + ext4_error(inode->i_sb, "ext4_fallocate", + "ext4_ext_get_blocks returned 0! inode#%lu" + ", block=%llu, max_blocks=%llu", + inode->i_ino, block, max_blocks); + ret = -EIO; + ext4_mark_inode_dirty(handle, inode); + ret2 = ext4_journal_stop(handle); + break; + } + if (ret > 0) { + /* check wrap through sign-bit/zero here */ + if ((block + ret) < 0 || (block + ret) < block) { + ret = -EIO; + ext4_mark_inode_dirty(handle, inode); + ret2 = ext4_journal_stop(handle); + break; + } + if (buffer_new(&map_bh) && ((block + ret) > + (EXT4_BLOCK_ALIGN(i_size_read(inode), blkbits) + >> blkbits))) + nblocks = nblocks + ret; + } + + /* Update ctime if new blocks get allocated */ + if (nblocks) { + struct timespec now; + + now = current_fs_time(inode->i_sb); + if (!timespec_equal(&inode->i_ctime, &now)) + inode->i_ctime = now; + } + + ext4_mark_inode_dirty(handle, inode); + ret2 = ext4_journal_stop(handle); + if (ret2) + break; + } + + if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries)) + goto retry; + + /* + * Time to update the file size. + * Update only when preallocation was requested beyond the file size. + */ + if (!(mode & FALLOC_FL_KEEP_SIZE) && + (offset + len) > i_size_read(inode)) { + if (ret > 0) { + /* + * if no error, we assume preallocation succeeded + * completely + */ + mutex_lock(&inode->i_mutex); + i_size_write(inode, offset + len); + EXT4_I(inode)->i_disksize = i_size_read(inode); + mutex_unlock(&inode->i_mutex); + } else if (ret < 0 && nblocks) { + /* Handle partial allocation scenario */ + loff_t newsize; + + mutex_lock(&inode->i_mutex); + newsize = (nblocks << blkbits) + i_size_read(inode); + i_size_write(inode, EXT4_BLOCK_ALIGN(newsize, blkbits)); + EXT4_I(inode)->i_disksize = i_size_read(inode); + mutex_unlock(&inode->i_mutex); + } + } + + return ret > 0 ? ret2 : ret; +} diff --git a/fs/ext4/file.c b/fs/ext4/file.c index d4c8186..1a81cd6 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -134,5 +134,6 @@ const struct inode_operations ext4_file_inode_operations = { .removexattr = generic_removexattr, #endif .permission = ext4_permission, + .fallocate = ext4_fallocate, }; diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index c88b439..427f830 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -563,7 +563,8 @@ got: inode->i_ino = ino; /* This is the optimal IO size (for stat), not the fs block size */ inode->i_blocks = 0; - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC; + inode->i_mtime = inode->i_atime = inode->i_ctime = ei->i_crtime = + ext4_current_time(inode); memset(ei->i_data, 0, sizeof(ei->i_data)); ei->i_dir_start_lookup = 0; @@ -595,9 +596,8 @@ got: spin_unlock(&sbi->s_next_gen_lock); ei->i_state = EXT4_STATE_NEW; - ei->i_extra_isize = - (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) ? - sizeof(struct ext4_inode) - EXT4_GOOD_OLD_INODE_SIZE : 0; + + ei->i_extra_isize = EXT4_SB(sb)->s_want_extra_isize; ret = inode; if(DQUOT_ALLOC_INODE(inode)) { diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 8416fa2..de26c25 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -726,7 +726,7 @@ static int ext4_splice_branch(handle_t *handle, struct inode *inode, /* We are done with atomic stuff, now do the rest of housekeeping */ - inode->i_ctime = CURRENT_TIME_SEC; + inode->i_ctime = ext4_current_time(inode); ext4_mark_inode_dirty(handle, inode); /* had we spliced it onto indirect block? */ @@ -1766,7 +1766,6 @@ int ext4_block_truncate_page(handle_t *handle, struct page *page, struct inode *inode = mapping->host; struct buffer_head *bh; int err = 0; - void *kaddr; blocksize = inode->i_sb->s_blocksize; length = blocksize - (offset & (blocksize - 1)); @@ -1778,10 +1777,7 @@ int ext4_block_truncate_page(handle_t *handle, struct page *page, */ if (!page_has_buffers(page) && test_opt(inode->i_sb, NOBH) && ext4_should_writeback_data(inode) && PageUptodate(page)) { - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + offset, 0, length); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, offset, length, KM_USER0); set_page_dirty(page); goto unlock; } @@ -1834,10 +1830,7 @@ int ext4_block_truncate_page(handle_t *handle, struct page *page, goto unlock; } - kaddr = kmap_atomic(page, KM_USER0); - memset(kaddr + offset, 0, length); - flush_dcache_page(page); - kunmap_atomic(kaddr, KM_USER0); + zero_user_page(page, offset, length, KM_USER0); BUFFER_TRACE(bh, "zeroed end of block"); @@ -2375,7 +2368,7 @@ do_indirects: ext4_discard_reservation(inode); mutex_unlock(&ei->truncate_mutex); - inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC; + inode->i_mtime = inode->i_ctime = ext4_current_time(inode); ext4_mark_inode_dirty(handle, inode); /* @@ -2583,6 +2576,25 @@ void ext4_set_inode_flags(struct inode *inode) inode->i_flags |= S_DIRSYNC; } +/* Propagate flags from i_flags to EXT4_I(inode)->i_flags */ +void ext4_get_inode_flags(struct ext4_inode_info *ei) +{ + unsigned int flags = ei->vfs_inode.i_flags; + + ei->i_flags &= ~(EXT4_SYNC_FL|EXT4_APPEND_FL| + EXT4_IMMUTABLE_FL|EXT4_NOATIME_FL|EXT4_DIRSYNC_FL); + if (flags & S_SYNC) + ei->i_flags |= EXT4_SYNC_FL; + if (flags & S_APPEND) + ei->i_flags |= EXT4_APPEND_FL; + if (flags & S_IMMUTABLE) + ei->i_flags |= EXT4_IMMUTABLE_FL; + if (flags & S_NOATIME) + ei->i_flags |= EXT4_NOATIME_FL; + if (flags & S_DIRSYNC) + ei->i_flags |= EXT4_DIRSYNC_FL; +} + void ext4_read_inode(struct inode * inode) { struct ext4_iloc iloc; @@ -2610,10 +2622,6 @@ void ext4_read_inode(struct inode * inode) } inode->i_nlink = le16_to_cpu(raw_inode->i_links_count); inode->i_size = le32_to_cpu(raw_inode->i_size); - inode->i_atime.tv_sec = (signed)le32_to_cpu(raw_inode->i_atime); - inode->i_ctime.tv_sec = (signed)le32_to_cpu(raw_inode->i_ctime); - inode->i_mtime.tv_sec = (signed)le32_to_cpu(raw_inode->i_mtime); - inode->i_atime.tv_nsec = inode->i_ctime.tv_nsec = inode->i_mtime.tv_nsec = 0; ei->i_state = 0; ei->i_dir_start_lookup = 0; @@ -2691,6 +2699,11 @@ void ext4_read_inode(struct inode * inode) } else ei->i_extra_isize = 0; + EXT4_INODE_GET_XTIME(i_ctime, inode, raw_inode); + EXT4_INODE_GET_XTIME(i_mtime, inode, raw_inode); + EXT4_INODE_GET_XTIME(i_atime, inode, raw_inode); + EXT4_EINODE_GET_XTIME(i_crtime, ei, raw_inode); + if (S_ISREG(inode->i_mode)) { inode->i_op = &ext4_file_inode_operations; inode->i_fop = &ext4_file_operations; @@ -2744,6 +2757,7 @@ static int ext4_do_update_inode(handle_t *handle, if (ei->i_state & EXT4_STATE_NEW) memset(raw_inode, 0, EXT4_SB(inode->i_sb)->s_inode_size); + ext4_get_inode_flags(ei); raw_inode->i_mode = cpu_to_le16(inode->i_mode); if(!(test_opt(inode->i_sb, NO_UID32))) { raw_inode->i_uid_low = cpu_to_le16(low_16_bits(inode->i_uid)); @@ -2771,9 +2785,12 @@ static int ext4_do_update_inode(handle_t *handle, } raw_inode->i_links_count = cpu_to_le16(inode->i_nlink); raw_inode->i_size = cpu_to_le32(ei->i_disksize); - raw_inode->i_atime = cpu_to_le32(inode->i_atime.tv_sec); - raw_inode->i_ctime = cpu_to_le32(inode->i_ctime.tv_sec); - raw_inode->i_mtime = cpu_to_le32(inode->i_mtime.tv_sec); + + EXT4_INODE_SET_XTIME(i_ctime, inode, raw_inode); + EXT4_INODE_SET_XTIME(i_mtime, inode, raw_inode); + EXT4_INODE_SET_XTIME(i_atime, inode, raw_inode); + EXT4_EINODE_SET_XTIME(i_crtime, ei, raw_inode); + raw_inode->i_blocks = cpu_to_le32(inode->i_blocks); raw_inode->i_dtime = cpu_to_le32(ei->i_dtime); raw_inode->i_flags = cpu_to_le32(ei->i_flags); @@ -3082,6 +3099,39 @@ ext4_reserve_inode_write(handle_t *handle, struct inode *inode, } /* + * Expand an inode by new_extra_isize bytes. + * Returns 0 on success or negative error number on failure. + */ +int ext4_expand_extra_isize(struct inode *inode, unsigned int new_extra_isize, + struct ext4_iloc iloc, handle_t *handle) +{ + struct ext4_inode *raw_inode; + struct ext4_xattr_ibody_header *header; + struct ext4_xattr_entry *entry; + + if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) + return 0; + + raw_inode = ext4_raw_inode(&iloc); + + header = IHDR(inode, raw_inode); + entry = IFIRST(header); + + /* No extended attributes present */ + if (!(EXT4_I(inode)->i_state & EXT4_STATE_XATTR) || + header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC)) { + memset((void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE, 0, + new_extra_isize); + EXT4_I(inode)->i_extra_isize = new_extra_isize; + return 0; + } + + /* try to expand with EAs present */ + return ext4_expand_extra_isize_ea(inode, new_extra_isize, + raw_inode, handle); +} + +/* * What we do here is to mark the in-core inode as clean with respect to inode * dirtiness (it may still be data-dirty). * This means that the in-core inode may be reaped by prune_icache @@ -3105,10 +3155,38 @@ ext4_reserve_inode_write(handle_t *handle, struct inode *inode, int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode) { struct ext4_iloc iloc; - int err; + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); + static unsigned int mnt_count; + int err, ret; might_sleep(); err = ext4_reserve_inode_write(handle, inode, &iloc); + if (EXT4_I(inode)->i_extra_isize < sbi->s_want_extra_isize && + !(EXT4_I(inode)->i_state & EXT4_STATE_NO_EXPAND)) { + /* + * We need extra buffer credits since we may write into EA block + * with this same handle. If journal_extend fails, then it will + * only result in a minor loss of functionality for that inode. + * If this is felt to be critical, then e2fsck should be run to + * force a large enough s_min_extra_isize. + */ + if ((jbd2_journal_extend(handle, + EXT4_DATA_TRANS_BLOCKS(inode->i_sb))) == 0) { + ret = ext4_expand_extra_isize(inode, + sbi->s_want_extra_isize, + iloc, handle); + if (ret) { + EXT4_I(inode)->i_state |= EXT4_STATE_NO_EXPAND; + if (mnt_count != sbi->s_es->s_mnt_count) { + ext4_warning(inode->i_sb, __FUNCTION__, + "Unable to expand inode %lu. Delete" + " some EAs or run e2fsck.", + inode->i_ino); + mnt_count = sbi->s_es->s_mnt_count; + } + } + } + } if (!err) err = ext4_mark_iloc_dirty(handle, inode, &iloc); return err; @@ -3197,7 +3275,7 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val) */ journal = EXT4_JOURNAL(inode); - if (is_journal_aborted(journal) || IS_RDONLY(inode)) + if (is_journal_aborted(journal)) return -EROFS; jbd2_journal_lock_updates(journal); diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 7b4aa45..c04c7cc 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -28,6 +28,7 @@ int ext4_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, switch (cmd) { case EXT4_IOC_GETFLAGS: + ext4_get_inode_flags(ei); flags = ei->i_flags & EXT4_FL_USER_VISIBLE; return put_user(flags, (int __user *) arg); case EXT4_IOC_SETFLAGS: { @@ -96,7 +97,7 @@ int ext4_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, ei->i_flags = flags; ext4_set_inode_flags(inode); - inode->i_ctime = CURRENT_TIME_SEC; + inode->i_ctime = ext4_current_time(inode); err = ext4_mark_iloc_dirty(handle, inode, &iloc); flags_err: @@ -133,14 +134,14 @@ flags_err: return PTR_ERR(handle); err = ext4_reserve_inode_write(handle, inode, &iloc); if (err == 0) { - inode->i_ctime = CURRENT_TIME_SEC; + inode->i_ctime = ext4_current_time(inode); inode->i_generation = generation; err = ext4_mark_iloc_dirty(handle, inode, &iloc); } ext4_journal_stop(handle); return err; } -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG case EXT4_IOC_WAIT_FOR_READONLY: /* * This is racy - by the time we're woken up and running, @@ -282,7 +283,7 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case EXT4_IOC32_SETVERSION_OLD: cmd = EXT4_IOC_SETVERSION_OLD; break; -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG case EXT4_IOC32_WAIT_FOR_READONLY: cmd = EXT4_IOC_WAIT_FOR_READONLY; break; diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index 2de339d..da22497 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -1295,7 +1295,7 @@ static int add_dirent_to_buf(handle_t *handle, struct dentry *dentry, * happen is that the times are slightly out of date * and/or different from the directory change time. */ - dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC; + dir->i_mtime = dir->i_ctime = ext4_current_time(dir); ext4_update_dx_flag(dir); dir->i_version++; ext4_mark_inode_dirty(handle, dir); @@ -1629,6 +1629,35 @@ static int ext4_delete_entry (handle_t *handle, return -ENOENT; } +/* + * DIR_NLINK feature is set if 1) nlinks > EXT4_LINK_MAX or 2) nlinks == 2, + * since this indicates that nlinks count was previously 1. + */ +static void ext4_inc_count(handle_t *handle, struct inode *inode) +{ + inc_nlink(inode); + if (is_dx(inode) && inode->i_nlink > 1) { + /* limit is 16-bit i_links_count */ + if (inode->i_nlink >= EXT4_LINK_MAX || inode->i_nlink == 2) { + inode->i_nlink = 1; + EXT4_SET_RO_COMPAT_FEATURE(inode->i_sb, + EXT4_FEATURE_RO_COMPAT_DIR_NLINK); + } + } +} + +/* + * If a directory had nlink == 1, then we should let it be 1. This indicates + * directory has >EXT4_LINK_MAX subdirs. + */ +static void ext4_dec_count(handle_t *handle, struct inode *inode) +{ + drop_nlink(inode); + if (S_ISDIR(inode->i_mode) && inode->i_nlink == 0) + inc_nlink(inode); +} + + static int ext4_add_nondir(handle_t *handle, struct dentry *dentry, struct inode *inode) { @@ -1725,7 +1754,7 @@ static int ext4_mkdir(struct inode * dir, struct dentry * dentry, int mode) struct ext4_dir_entry_2 * de; int err, retries = 0; - if (dir->i_nlink >= EXT4_LINK_MAX) + if (EXT4_DIR_LINK_MAX(dir)) return -EMLINK; retry: @@ -1748,7 +1777,7 @@ retry: inode->i_size = EXT4_I(inode)->i_disksize = inode->i_sb->s_blocksize; dir_block = ext4_bread (handle, inode, 0, 1, &err); if (!dir_block) { - drop_nlink(inode); /* is this nlink == 0? */ + ext4_dec_count(handle, inode); /* is this nlink == 0? */ ext4_mark_inode_dirty(handle, inode); iput (inode); goto out_stop; @@ -1780,7 +1809,7 @@ retry: iput (inode); goto out_stop; } - inc_nlink(dir); + ext4_inc_count(handle, dir); ext4_update_dx_flag(dir); ext4_mark_inode_dirty(handle, dir); d_instantiate(dentry, inode); @@ -2045,9 +2074,9 @@ static int ext4_rmdir (struct inode * dir, struct dentry *dentry) retval = ext4_delete_entry(handle, dir, de, bh); if (retval) goto end_rmdir; - if (inode->i_nlink != 2) + if (!EXT4_DIR_LINK_EMPTY(inode)) ext4_warning (inode->i_sb, "ext4_rmdir", - "empty directory has nlink!=2 (%d)", + "empty directory has too many links (%d)", inode->i_nlink); inode->i_version++; clear_nlink(inode); @@ -2056,9 +2085,9 @@ static int ext4_rmdir (struct inode * dir, struct dentry *dentry) * recovery. */ inode->i_size = 0; ext4_orphan_add(handle, inode); - inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC; + inode->i_ctime = dir->i_ctime = dir->i_mtime = ext4_current_time(inode); ext4_mark_inode_dirty(handle, inode); - drop_nlink(dir); + ext4_dec_count(handle, dir); ext4_update_dx_flag(dir); ext4_mark_inode_dirty(handle, dir); @@ -2106,13 +2135,13 @@ static int ext4_unlink(struct inode * dir, struct dentry *dentry) retval = ext4_delete_entry(handle, dir, de, bh); if (retval) goto end_unlink; - dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC; + dir->i_ctime = dir->i_mtime = ext4_current_time(dir); ext4_update_dx_flag(dir); ext4_mark_inode_dirty(handle, dir); - drop_nlink(inode); + ext4_dec_count(handle, inode); if (!inode->i_nlink) ext4_orphan_add(handle, inode); - inode->i_ctime = dir->i_ctime; + inode->i_ctime = ext4_current_time(inode); ext4_mark_inode_dirty(handle, inode); retval = 0; @@ -2159,7 +2188,7 @@ retry: err = __page_symlink(inode, symname, l, mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS); if (err) { - drop_nlink(inode); + ext4_dec_count(handle, inode); ext4_mark_inode_dirty(handle, inode); iput (inode); goto out_stop; @@ -2185,8 +2214,9 @@ static int ext4_link (struct dentry * old_dentry, struct inode *inode = old_dentry->d_inode; int err, retries = 0; - if (inode->i_nlink >= EXT4_LINK_MAX) + if (EXT4_DIR_LINK_MAX(inode)) return -EMLINK; + /* * Return -ENOENT if we've raced with unlink and i_nlink is 0. Doing * otherwise has the potential to corrupt the orphan inode list. @@ -2203,8 +2233,8 @@ retry: if (IS_DIRSYNC(dir)) handle->h_sync = 1; - inode->i_ctime = CURRENT_TIME_SEC; - inc_nlink(inode); + inode->i_ctime = ext4_current_time(inode); + ext4_inc_count(handle, inode); atomic_inc(&inode->i_count); err = ext4_add_nondir(handle, dentry, inode); @@ -2305,7 +2335,7 @@ static int ext4_rename (struct inode * old_dir, struct dentry *old_dentry, * Like most other Unix systems, set the ctime for inodes on a * rename. */ - old_inode->i_ctime = CURRENT_TIME_SEC; + old_inode->i_ctime = ext4_current_time(old_inode); ext4_mark_inode_dirty(handle, old_inode); /* @@ -2337,10 +2367,10 @@ static int ext4_rename (struct inode * old_dir, struct dentry *old_dentry, } if (new_inode) { - drop_nlink(new_inode); - new_inode->i_ctime = CURRENT_TIME_SEC; + ext4_dec_count(handle, new_inode); + new_inode->i_ctime = ext4_current_time(new_inode); } - old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME_SEC; + old_dir->i_ctime = old_dir->i_mtime = ext4_current_time(old_dir); ext4_update_dx_flag(old_dir); if (dir_bh) { BUFFER_TRACE(dir_bh, "get_write_access"); @@ -2348,11 +2378,13 @@ static int ext4_rename (struct inode * old_dir, struct dentry *old_dentry, PARENT_INO(dir_bh->b_data) = cpu_to_le32(new_dir->i_ino); BUFFER_TRACE(dir_bh, "call ext4_journal_dirty_metadata"); ext4_journal_dirty_metadata(handle, dir_bh); - drop_nlink(old_dir); + ext4_dec_count(handle, old_dir); if (new_inode) { - drop_nlink(new_inode); + /* checked empty_dir above, can't have another parent, + * ext3_dec_count() won't work for many-linked dirs */ + new_inode->i_nlink = 0; } else { - inc_nlink(new_dir); + ext4_inc_count(handle, new_dir); ext4_update_dx_flag(new_dir); ext4_mark_inode_dirty(handle, new_dir); } diff --git a/fs/ext4/super.c b/fs/ext4/super.c index b806e68..6dcbb28 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -36,6 +36,7 @@ #include <linux/namei.h> #include <linux/quotaops.h> #include <linux/seq_file.h> +#include <linux/log2.h> #include <asm/uaccess.h> @@ -734,7 +735,7 @@ enum { Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota, Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_quota, Opt_noquota, Opt_ignore, Opt_barrier, Opt_err, Opt_resize, Opt_usrquota, - Opt_grpquota, Opt_extents, + Opt_grpquota, Opt_extents, Opt_noextents, }; static match_table_t tokens = { @@ -785,6 +786,7 @@ static match_table_t tokens = { {Opt_usrquota, "usrquota"}, {Opt_barrier, "barrier=%u"}, {Opt_extents, "extents"}, + {Opt_noextents, "noextents"}, {Opt_err, NULL}, {Opt_resize, "resize"}, }; @@ -1120,6 +1122,9 @@ clear_qf_name: case Opt_extents: set_opt (sbi->s_mount_opt, EXTENTS); break; + case Opt_noextents: + clear_opt (sbi->s_mount_opt, EXTENTS); + break; default: printk (KERN_ERR "EXT4-fs: Unrecognized mount option \"%s\" " @@ -1551,6 +1556,12 @@ static int ext4_fill_super (struct super_block *sb, void *data, int silent) set_opt(sbi->s_mount_opt, RESERVATION); + /* + * turn on extents feature by default in ext4 filesystem + * User -o noextents to turn it off + */ + set_opt(sbi->s_mount_opt, EXTENTS); + if (!parse_options ((char *) data, sb, &journal_inum, &journal_devnum, NULL, 0)) goto failed_mount; @@ -1634,13 +1645,15 @@ static int ext4_fill_super (struct super_block *sb, void *data, int silent) sbi->s_inode_size = le16_to_cpu(es->s_inode_size); sbi->s_first_ino = le32_to_cpu(es->s_first_ino); if ((sbi->s_inode_size < EXT4_GOOD_OLD_INODE_SIZE) || - (sbi->s_inode_size & (sbi->s_inode_size - 1)) || + (!is_power_of_2(sbi->s_inode_size)) || (sbi->s_inode_size > blocksize)) { printk (KERN_ERR "EXT4-fs: unsupported inode size: %d\n", sbi->s_inode_size); goto failed_mount; } + if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE) + sb->s_time_gran = 1 << (EXT4_EPOCH_BITS - 2); } sbi->s_frag_size = EXT4_MIN_FRAG_SIZE << le32_to_cpu(es->s_log_frag_size); @@ -1803,6 +1816,13 @@ static int ext4_fill_super (struct super_block *sb, void *data, int silent) goto failed_mount3; } + if (ext4_blocks_count(es) > 0xffffffffULL && + !jbd2_journal_set_features(EXT4_SB(sb)->s_journal, 0, 0, + JBD2_FEATURE_INCOMPAT_64BIT)) { + printk(KERN_ERR "ext4: Failed to set 64-bit journal feature\n"); + goto failed_mount4; + } + /* We have now updated the journal if required, so we can * validate the data journaling mode. */ switch (test_opt(sb, DATA_FLAGS)) { @@ -1857,6 +1877,32 @@ static int ext4_fill_super (struct super_block *sb, void *data, int silent) } ext4_setup_super (sb, es, sb->s_flags & MS_RDONLY); + + /* determine the minimum size of new large inodes, if present */ + if (sbi->s_inode_size > EXT4_GOOD_OLD_INODE_SIZE) { + sbi->s_want_extra_isize = sizeof(struct ext4_inode) - + EXT4_GOOD_OLD_INODE_SIZE; + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, + EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE)) { + if (sbi->s_want_extra_isize < + le16_to_cpu(es->s_want_extra_isize)) + sbi->s_want_extra_isize = + le16_to_cpu(es->s_want_extra_isize); + if (sbi->s_want_extra_isize < + le16_to_cpu(es->s_min_extra_isize)) + sbi->s_want_extra_isize = + le16_to_cpu(es->s_min_extra_isize); + } + } + /* Check if enough inode space is available */ + if (EXT4_GOOD_OLD_INODE_SIZE + sbi->s_want_extra_isize > + sbi->s_inode_size) { + sbi->s_want_extra_isize = sizeof(struct ext4_inode) - + EXT4_GOOD_OLD_INODE_SIZE; + printk(KERN_INFO "EXT4-fs: required extra inode space not" + "available.\n"); + } + /* * akpm: core read_super() calls in here with the superblock locked. * That deadlocks, because orphan cleanup needs to lock the superblock diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index e832e96..b10d68f 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -66,13 +66,6 @@ #define BFIRST(bh) ENTRY(BHDR(bh)+1) #define IS_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0) -#define IHDR(inode, raw_inode) \ - ((struct ext4_xattr_ibody_header *) \ - ((void *)raw_inode + \ - EXT4_GOOD_OLD_INODE_SIZE + \ - EXT4_I(inode)->i_extra_isize)) -#define IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr)+1)) - #ifdef EXT4_XATTR_DEBUG # define ea_idebug(inode, f...) do { \ printk(KERN_DEBUG "inode %s:%lu: ", \ @@ -508,6 +501,24 @@ out: return; } +/* + * Find the available free space for EAs. This also returns the total number of + * bytes used by EA entries. + */ +static size_t ext4_xattr_free_space(struct ext4_xattr_entry *last, + size_t *min_offs, void *base, int *total) +{ + for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { + *total += EXT4_XATTR_LEN(last->e_name_len); + if (!last->e_value_block && last->e_value_size) { + size_t offs = le16_to_cpu(last->e_value_offs); + if (offs < *min_offs) + *min_offs = offs; + } + } + return (*min_offs - ((void *)last - base) - sizeof(__u32)); +} + struct ext4_xattr_info { int name_index; const char *name; @@ -1013,7 +1024,9 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index, } if (!error) { ext4_xattr_update_super_block(handle, inode->i_sb); - inode->i_ctime = CURRENT_TIME_SEC; + inode->i_ctime = ext4_current_time(inode); + if (!value) + EXT4_I(inode)->i_state &= ~EXT4_STATE_NO_EXPAND; error = ext4_mark_iloc_dirty(handle, inode, &is.iloc); /* * The bh is consumed by ext4_mark_iloc_dirty, even with @@ -1067,6 +1080,253 @@ retry: } /* + * Shift the EA entries in the inode to create space for the increased + * i_extra_isize. + */ +static void ext4_xattr_shift_entries(struct ext4_xattr_entry *entry, + int value_offs_shift, void *to, + void *from, size_t n, int blocksize) +{ + struct ext4_xattr_entry *last = entry; + int new_offs; + + /* Adjust the value offsets of the entries */ + for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { + if (!last->e_value_block && last->e_value_size) { + new_offs = le16_to_cpu(last->e_value_offs) + + value_offs_shift; + BUG_ON(new_offs + le32_to_cpu(last->e_value_size) + > blocksize); + last->e_value_offs = cpu_to_le16(new_offs); + } + } + /* Shift the entries by n bytes */ + memmove(to, from, n); +} + +/* + * Expand an inode by new_extra_isize bytes when EAs are present. + * Returns 0 on success or negative error number on failure. + */ +int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize, + struct ext4_inode *raw_inode, handle_t *handle) +{ + struct ext4_xattr_ibody_header *header; + struct ext4_xattr_entry *entry, *last, *first; + struct buffer_head *bh = NULL; + struct ext4_xattr_ibody_find *is = NULL; + struct ext4_xattr_block_find *bs = NULL; + char *buffer = NULL, *b_entry_name = NULL; + size_t min_offs, free; + int total_ino, total_blk; + void *base, *start, *end; + int extra_isize = 0, error = 0, tried_min_extra_isize = 0; + int s_min_extra_isize = EXT4_SB(inode->i_sb)->s_es->s_min_extra_isize; + + down_write(&EXT4_I(inode)->xattr_sem); +retry: + if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) { + up_write(&EXT4_I(inode)->xattr_sem); + return 0; + } + + header = IHDR(inode, raw_inode); + entry = IFIRST(header); + + /* + * Check if enough free space is available in the inode to shift the + * entries ahead by new_extra_isize. + */ + + base = start = entry; + end = (void *)raw_inode + EXT4_SB(inode->i_sb)->s_inode_size; + min_offs = end - base; + last = entry; + total_ino = sizeof(struct ext4_xattr_ibody_header); + + free = ext4_xattr_free_space(last, &min_offs, base, &total_ino); + if (free >= new_extra_isize) { + entry = IFIRST(header); + ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize + - new_extra_isize, (void *)raw_inode + + EXT4_GOOD_OLD_INODE_SIZE + new_extra_isize, + (void *)header, total_ino, + inode->i_sb->s_blocksize); + EXT4_I(inode)->i_extra_isize = new_extra_isize; + error = 0; + goto cleanup; + } + + /* + * Enough free space isn't available in the inode, check if + * EA block can hold new_extra_isize bytes. + */ + if (EXT4_I(inode)->i_file_acl) { + bh = sb_bread(inode->i_sb, EXT4_I(inode)->i_file_acl); + error = -EIO; + if (!bh) + goto cleanup; + if (ext4_xattr_check_block(bh)) { + ext4_error(inode->i_sb, __FUNCTION__, + "inode %lu: bad block %llu", inode->i_ino, + EXT4_I(inode)->i_file_acl); + error = -EIO; + goto cleanup; + } + base = BHDR(bh); + first = BFIRST(bh); + end = bh->b_data + bh->b_size; + min_offs = end - base; + free = ext4_xattr_free_space(first, &min_offs, base, + &total_blk); + if (free < new_extra_isize) { + if (!tried_min_extra_isize && s_min_extra_isize) { + tried_min_extra_isize++; + new_extra_isize = s_min_extra_isize; + brelse(bh); + goto retry; + } + error = -1; + goto cleanup; + } + } else { + free = inode->i_sb->s_blocksize; + } + + while (new_extra_isize > 0) { + size_t offs, size, entry_size; + struct ext4_xattr_entry *small_entry = NULL; + struct ext4_xattr_info i = { + .value = NULL, + .value_len = 0, + }; + unsigned int total_size; /* EA entry size + value size */ + unsigned int shift_bytes; /* No. of bytes to shift EAs by? */ + unsigned int min_total_size = ~0U; + + is = kzalloc(sizeof(struct ext4_xattr_ibody_find), GFP_NOFS); + bs = kzalloc(sizeof(struct ext4_xattr_block_find), GFP_NOFS); + if (!is || !bs) { + error = -ENOMEM; + goto cleanup; + } + + is->s.not_found = -ENODATA; + bs->s.not_found = -ENODATA; + is->iloc.bh = NULL; + bs->bh = NULL; + + last = IFIRST(header); + /* Find the entry best suited to be pushed into EA block */ + entry = NULL; + for (; !IS_LAST_ENTRY(last); last = EXT4_XATTR_NEXT(last)) { + total_size = + EXT4_XATTR_SIZE(le32_to_cpu(last->e_value_size)) + + EXT4_XATTR_LEN(last->e_name_len); + if (total_size <= free && total_size < min_total_size) { + if (total_size < new_extra_isize) { + small_entry = last; + } else { + entry = last; + min_total_size = total_size; + } + } + } + + if (entry == NULL) { + if (small_entry) { + entry = small_entry; + } else { + if (!tried_min_extra_isize && + s_min_extra_isize) { + tried_min_extra_isize++; + new_extra_isize = s_min_extra_isize; + goto retry; + } + error = -1; + goto cleanup; + } + } + offs = le16_to_cpu(entry->e_value_offs); + size = le32_to_cpu(entry->e_value_size); + entry_size = EXT4_XATTR_LEN(entry->e_name_len); + i.name_index = entry->e_name_index, + buffer = kmalloc(EXT4_XATTR_SIZE(size), GFP_NOFS); + b_entry_name = kmalloc(entry->e_name_len + 1, GFP_NOFS); + if (!buffer || !b_entry_name) { + error = -ENOMEM; + goto cleanup; + } + /* Save the entry name and the entry value */ + memcpy(buffer, (void *)IFIRST(header) + offs, + EXT4_XATTR_SIZE(size)); + memcpy(b_entry_name, entry->e_name, entry->e_name_len); + b_entry_name[entry->e_name_len] = '\0'; + i.name = b_entry_name; + + error = ext4_get_inode_loc(inode, &is->iloc); + if (error) + goto cleanup; + + error = ext4_xattr_ibody_find(inode, &i, is); + if (error) + goto cleanup; + + /* Remove the chosen entry from the inode */ + error = ext4_xattr_ibody_set(handle, inode, &i, is); + + entry = IFIRST(header); + if (entry_size + EXT4_XATTR_SIZE(size) >= new_extra_isize) + shift_bytes = new_extra_isize; + else + shift_bytes = entry_size + size; + /* Adjust the offsets and shift the remaining entries ahead */ + ext4_xattr_shift_entries(entry, EXT4_I(inode)->i_extra_isize - + shift_bytes, (void *)raw_inode + + EXT4_GOOD_OLD_INODE_SIZE + extra_isize + shift_bytes, + (void *)header, total_ino - entry_size, + inode->i_sb->s_blocksize); + + extra_isize += shift_bytes; + new_extra_isize -= shift_bytes; + EXT4_I(inode)->i_extra_isize = extra_isize; + + i.name = b_entry_name; + i.value = buffer; + i.value_len = cpu_to_le32(size); + error = ext4_xattr_block_find(inode, &i, bs); + if (error) + goto cleanup; + + /* Add entry which was removed from the inode into the block */ + error = ext4_xattr_block_set(handle, inode, &i, bs); + if (error) + goto cleanup; + kfree(b_entry_name); + kfree(buffer); + brelse(is->iloc.bh); + kfree(is); + kfree(bs); + } + brelse(bh); + up_write(&EXT4_I(inode)->xattr_sem); + return 0; + +cleanup: + kfree(b_entry_name); + kfree(buffer); + if (is) + brelse(is->iloc.bh); + kfree(is); + kfree(bs); + brelse(bh); + up_write(&EXT4_I(inode)->xattr_sem); + return error; +} + + + +/* * ext4_xattr_delete_inode() * * Free extended attribute resources associated with this inode. This diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h index 79432b3..d7f5d6a 100644 --- a/fs/ext4/xattr.h +++ b/fs/ext4/xattr.h @@ -56,6 +56,13 @@ struct ext4_xattr_entry { #define EXT4_XATTR_SIZE(size) \ (((size) + EXT4_XATTR_ROUND) & ~EXT4_XATTR_ROUND) +#define IHDR(inode, raw_inode) \ + ((struct ext4_xattr_ibody_header *) \ + ((void *)raw_inode + \ + EXT4_GOOD_OLD_INODE_SIZE + \ + EXT4_I(inode)->i_extra_isize)) +#define IFIRST(hdr) ((struct ext4_xattr_entry *)((hdr)+1)) + # ifdef CONFIG_EXT4DEV_FS_XATTR extern struct xattr_handler ext4_xattr_user_handler; @@ -74,6 +81,9 @@ extern int ext4_xattr_set_handle(handle_t *, struct inode *, int, const char *, extern void ext4_xattr_delete_inode(handle_t *, struct inode *); extern void ext4_xattr_put_super(struct super_block *); +extern int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize, + struct ext4_inode *raw_inode, handle_t *handle); + extern int init_ext4_xattr(void); extern void exit_ext4_xattr(void); @@ -129,6 +139,13 @@ exit_ext4_xattr(void) { } +static inline int +ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize, + struct ext4_inode *raw_inode, handle_t *handle) +{ + return -EOPNOTSUPP; +} + #define ext4_xattr_handlers NULL # endif /* CONFIG_EXT4DEV_FS_XATTR */ diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 78d63b8..f290cb7 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -35,6 +35,7 @@ #include <linux/kthread.h> #include <linux/poison.h> #include <linux/proc_fs.h> +#include <linux/debugfs.h> #include <asm/uaccess.h> #include <asm/page.h> @@ -528,7 +529,7 @@ int jbd2_log_wait_commit(journal_t *journal, tid_t tid) { int err = 0; -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG spin_lock(&journal->j_state_lock); if (!tid_geq(journal->j_commit_request, tid)) { printk(KERN_EMERG @@ -1709,7 +1710,7 @@ void jbd2_slab_free(void *ptr, size_t size) * Journal_head storage management */ static struct kmem_cache *jbd2_journal_head_cache; -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG static atomic_t nr_journal_heads = ATOMIC_INIT(0); #endif @@ -1747,7 +1748,7 @@ static struct journal_head *journal_alloc_journal_head(void) struct journal_head *ret; static unsigned long last_warning; -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG atomic_inc(&nr_journal_heads); #endif ret = kmem_cache_alloc(jbd2_journal_head_cache, GFP_NOFS); @@ -1768,7 +1769,7 @@ static struct journal_head *journal_alloc_journal_head(void) static void journal_free_journal_head(struct journal_head *jh) { -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG atomic_dec(&nr_journal_heads); memset(jh, JBD_POISON_FREE, sizeof(*jh)); #endif @@ -1951,64 +1952,50 @@ void jbd2_journal_put_journal_head(struct journal_head *jh) } /* - * /proc tunables + * debugfs tunables */ -#if defined(CONFIG_JBD_DEBUG) -int jbd2_journal_enable_debug; +#if defined(CONFIG_JBD2_DEBUG) +u8 jbd2_journal_enable_debug; EXPORT_SYMBOL(jbd2_journal_enable_debug); #endif -#if defined(CONFIG_JBD_DEBUG) && defined(CONFIG_PROC_FS) +#if defined(CONFIG_JBD2_DEBUG) && defined(CONFIG_DEBUG_FS) -static struct proc_dir_entry *proc_jbd_debug; +#define JBD2_DEBUG_NAME "jbd2-debug" -static int read_jbd_debug(char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int ret; +struct dentry *jbd2_debugfs_dir, *jbd2_debug; - ret = sprintf(page + off, "%d\n", jbd2_journal_enable_debug); - *eof = 1; - return ret; +static void __init jbd2_create_debugfs_entry(void) +{ + jbd2_debugfs_dir = debugfs_create_dir("jbd2", NULL); + if (jbd2_debugfs_dir) + jbd2_debug = debugfs_create_u8(JBD2_DEBUG_NAME, S_IRUGO, + jbd2_debugfs_dir, + &jbd2_journal_enable_debug); } -static int write_jbd_debug(struct file *file, const char __user *buffer, - unsigned long count, void *data) +static void __exit jbd2_remove_debugfs_entry(void) { - char buf[32]; - - if (count > ARRAY_SIZE(buf) - 1) - count = ARRAY_SIZE(buf) - 1; - if (copy_from_user(buf, buffer, count)) - return -EFAULT; - buf[ARRAY_SIZE(buf) - 1] = '\0'; - jbd2_journal_enable_debug = simple_strtoul(buf, NULL, 10); - return count; + if (jbd2_debug) + debugfs_remove(jbd2_debug); + if (jbd2_debugfs_dir) + debugfs_remove(jbd2_debugfs_dir); } -#define JBD_PROC_NAME "sys/fs/jbd2-debug" +#else -static void __init create_jbd_proc_entry(void) +static void __init jbd2_create_debugfs_entry(void) { - proc_jbd_debug = create_proc_entry(JBD_PROC_NAME, 0644, NULL); - if (proc_jbd_debug) { - /* Why is this so hard? */ - proc_jbd_debug->read_proc = read_jbd_debug; - proc_jbd_debug->write_proc = write_jbd_debug; - } + do { + } while (0); } -static void __exit jbd2_remove_jbd_proc_entry(void) +static void __exit jbd2_remove_debugfs_entry(void) { - if (proc_jbd_debug) - remove_proc_entry(JBD_PROC_NAME, NULL); + do { + } while (0); } -#else - -#define create_jbd_proc_entry() do {} while (0) -#define jbd2_remove_jbd_proc_entry() do {} while (0) - #endif struct kmem_cache *jbd2_handle_cache; @@ -2067,18 +2054,18 @@ static int __init journal_init(void) ret = journal_init_caches(); if (ret != 0) jbd2_journal_destroy_caches(); - create_jbd_proc_entry(); + jbd2_create_debugfs_entry(); return ret; } static void __exit journal_exit(void) { -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG int n = atomic_read(&nr_journal_heads); if (n) printk(KERN_EMERG "JBD: leaked %d journal_heads!\n", n); #endif - jbd2_remove_jbd_proc_entry(); + jbd2_remove_debugfs_entry(); jbd2_journal_destroy_caches(); } diff --git a/fs/jbd2/recovery.c b/fs/jbd2/recovery.c index 395c92a..e7730a0 100644 --- a/fs/jbd2/recovery.c +++ b/fs/jbd2/recovery.c @@ -295,7 +295,7 @@ int jbd2_journal_skip_recovery(journal_t *journal) printk(KERN_ERR "JBD: error %d scanning journal\n", err); ++journal->j_transaction_sequence; } else { -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG int dropped = info.end_transaction - be32_to_cpu(sb->s_sequence); #endif jbd_debug(0, diff --git a/fs/ocfs2/heartbeat.c b/fs/ocfs2/heartbeat.c index 352eb4a..c4c3617 100644 --- a/fs/ocfs2/heartbeat.c +++ b/fs/ocfs2/heartbeat.c @@ -209,7 +209,7 @@ void ocfs2_stop_heartbeat(struct ocfs2_super *osb) envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; envp[2] = NULL; - ret = call_usermodehelper(argv[0], argv, envp, 1); + ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); if (ret < 0) mlog_errno(ret); } @@ -26,6 +26,7 @@ #include <linux/syscalls.h> #include <linux/rcupdate.h> #include <linux/audit.h> +#include <linux/falloc.h> int vfs_statfs(struct dentry *dentry, struct kstatfs *buf) { @@ -352,6 +353,64 @@ asmlinkage long sys_ftruncate64(unsigned int fd, loff_t length) } #endif +asmlinkage long sys_fallocate(int fd, int mode, loff_t offset, loff_t len) +{ + struct file *file; + struct inode *inode; + long ret = -EINVAL; + + if (offset < 0 || len <= 0) + goto out; + + /* Return error if mode is not supported */ + ret = -EOPNOTSUPP; + if (mode && !(mode & FALLOC_FL_KEEP_SIZE)) + goto out; + + ret = -EBADF; + file = fget(fd); + if (!file) + goto out; + if (!(file->f_mode & FMODE_WRITE)) + goto out_fput; + /* + * Revalidate the write permissions, in case security policy has + * changed since the files were opened. + */ + ret = security_file_permission(file, MAY_WRITE); + if (ret) + goto out_fput; + + inode = file->f_path.dentry->d_inode; + + ret = -ESPIPE; + if (S_ISFIFO(inode->i_mode)) + goto out_fput; + + ret = -ENODEV; + /* + * Let individual file system decide if it supports preallocation + * for directories or not. + */ + if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode)) + goto out_fput; + + ret = -EFBIG; + /* Check for wrap through zero too */ + if (((offset + len) > inode->i_sb->s_maxbytes) || ((offset + len) < 0)) + goto out_fput; + + if (inode->i_op && inode->i_op->fallocate) + ret = inode->i_op->fallocate(inode, mode, offset, len); + else + ret = -ENOSYS; + +out_fput: + fput(file); +out: + return ret; +} + /* * access() needs to use the real uid/gid, not the effective uid/gid. * We do this by temporarily clearing all FS-related capabilities and diff --git a/include/asm-i386/irq.h b/include/asm-i386/irq.h index 9e15ce0..36f3106 100644 --- a/include/asm-i386/irq.h +++ b/include/asm-i386/irq.h @@ -41,6 +41,7 @@ extern int irqbalance_disable(char *str); extern void fixup_irqs(cpumask_t map); #endif +unsigned int do_IRQ(struct pt_regs *regs); void init_IRQ(void); void __init native_init_IRQ(void); diff --git a/include/asm-i386/mach-default/irq_vectors_limits.h b/include/asm-i386/mach-default/irq_vectors_limits.h index 7f161e7..a90c7a6 100644 --- a/include/asm-i386/mach-default/irq_vectors_limits.h +++ b/include/asm-i386/mach-default/irq_vectors_limits.h @@ -1,7 +1,7 @@ #ifndef _ASM_IRQ_VECTORS_LIMITS_H #define _ASM_IRQ_VECTORS_LIMITS_H -#ifdef CONFIG_X86_IO_APIC +#if defined(CONFIG_X86_IO_APIC) || defined(CONFIG_PARAVIRT) #define NR_IRQS 224 # if (224 >= 32 * NR_CPUS) # define NR_IRQ_VECTORS NR_IRQS diff --git a/include/asm-i386/mmu_context.h b/include/asm-i386/mmu_context.h index 8198d1c..7eb0b0b 100644 --- a/include/asm-i386/mmu_context.h +++ b/include/asm-i386/mmu_context.h @@ -32,6 +32,8 @@ static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk) #endif } +void leave_mm(unsigned long cpu); + static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk) diff --git a/include/asm-i386/paravirt.h b/include/asm-i386/paravirt.h index 7f846a7..7df88be 100644 --- a/include/asm-i386/paravirt.h +++ b/include/asm-i386/paravirt.h @@ -52,6 +52,8 @@ struct paravirt_ops /* Basic arch-specific setup */ void (*arch_setup)(void); char *(*memory_setup)(void); + void (*post_allocator_init)(void); + void (*init_IRQ)(void); void (*time_init)(void); @@ -116,7 +118,7 @@ struct paravirt_ops u64 (*read_tsc)(void); u64 (*read_pmc)(void); - u64 (*get_scheduled_cycles)(void); + unsigned long long (*sched_clock)(void); unsigned long (*get_cpu_khz)(void); /* Segment descriptor handling */ @@ -173,7 +175,7 @@ struct paravirt_ops unsigned long va); /* Hooks for allocating/releasing pagetable pages */ - void (*alloc_pt)(u32 pfn); + void (*alloc_pt)(struct mm_struct *mm, u32 pfn); void (*alloc_pd)(u32 pfn); void (*alloc_pd_clone)(u32 pfn, u32 clonepfn, u32 start, u32 count); void (*release_pt)(u32 pfn); @@ -260,6 +262,7 @@ unsigned paravirt_patch_default(u8 type, u16 clobbers, void *site, unsigned len) unsigned paravirt_patch_insns(void *site, unsigned len, const char *start, const char *end); +int paravirt_disable_iospace(void); /* * This generates an indirect call based on the operation type number. @@ -563,7 +566,10 @@ static inline u64 paravirt_read_tsc(void) #define rdtscll(val) (val = paravirt_read_tsc()) -#define get_scheduled_cycles(val) (val = paravirt_ops.get_scheduled_cycles()) +static inline unsigned long long paravirt_sched_clock(void) +{ + return PVOP_CALL0(unsigned long long, sched_clock); +} #define calculate_cpu_khz() (paravirt_ops.get_cpu_khz()) #define write_tsc(val1,val2) wrmsr(0x10, val1, val2) @@ -669,6 +675,12 @@ static inline void setup_secondary_clock(void) } #endif +static inline void paravirt_post_allocator_init(void) +{ + if (paravirt_ops.post_allocator_init) + (*paravirt_ops.post_allocator_init)(); +} + static inline void paravirt_pagetable_setup_start(pgd_t *base) { if (paravirt_ops.pagetable_setup_start) @@ -725,9 +737,9 @@ static inline void flush_tlb_others(cpumask_t cpumask, struct mm_struct *mm, PVOP_VCALL3(flush_tlb_others, &cpumask, mm, va); } -static inline void paravirt_alloc_pt(unsigned pfn) +static inline void paravirt_alloc_pt(struct mm_struct *mm, unsigned pfn) { - PVOP_VCALL1(alloc_pt, pfn); + PVOP_VCALL2(alloc_pt, mm, pfn); } static inline void paravirt_release_pt(unsigned pfn) { diff --git a/include/asm-i386/pgalloc.h b/include/asm-i386/pgalloc.h index d07b7af..f2fc33c 100644 --- a/include/asm-i386/pgalloc.h +++ b/include/asm-i386/pgalloc.h @@ -7,7 +7,7 @@ #ifdef CONFIG_PARAVIRT #include <asm/paravirt.h> #else -#define paravirt_alloc_pt(pfn) do { } while (0) +#define paravirt_alloc_pt(mm, pfn) do { } while (0) #define paravirt_alloc_pd(pfn) do { } while (0) #define paravirt_alloc_pd(pfn) do { } while (0) #define paravirt_alloc_pd_clone(pfn, clonepfn, start, count) do { } while (0) @@ -17,13 +17,13 @@ #define pmd_populate_kernel(mm, pmd, pte) \ do { \ - paravirt_alloc_pt(__pa(pte) >> PAGE_SHIFT); \ + paravirt_alloc_pt(mm, __pa(pte) >> PAGE_SHIFT); \ set_pmd(pmd, __pmd(_PAGE_TABLE + __pa(pte))); \ } while (0) #define pmd_populate(mm, pmd, pte) \ do { \ - paravirt_alloc_pt(page_to_pfn(pte)); \ + paravirt_alloc_pt(mm, page_to_pfn(pte)); \ set_pmd(pmd, __pmd(_PAGE_TABLE + \ ((unsigned long long)page_to_pfn(pte) << \ (unsigned long long) PAGE_SHIFT))); \ diff --git a/include/asm-i386/setup.h b/include/asm-i386/setup.h index 0d5bff9..7862fe8 100644 --- a/include/asm-i386/setup.h +++ b/include/asm-i386/setup.h @@ -81,6 +81,10 @@ void __init add_memory_region(unsigned long long start, extern unsigned long init_pg_tables_end; +#ifndef CONFIG_PARAVIRT +#define paravirt_post_allocator_init() do {} while (0) +#endif + #endif /* __ASSEMBLY__ */ #endif /* __KERNEL__ */ diff --git a/include/asm-i386/smp.h b/include/asm-i386/smp.h index 0c71327..1f73bde 100644 --- a/include/asm-i386/smp.h +++ b/include/asm-i386/smp.h @@ -43,9 +43,12 @@ extern u8 x86_cpu_to_apicid[]; #define cpu_physical_id(cpu) x86_cpu_to_apicid[cpu] +extern void set_cpu_sibling_map(int cpu); + #ifdef CONFIG_HOTPLUG_CPU extern void cpu_exit_clear(void); extern void cpu_uninit(void); +extern void remove_siblinginfo(int cpu); #endif struct smp_ops @@ -129,6 +132,8 @@ extern int __cpu_disable(void); extern void __cpu_die(unsigned int cpu); extern unsigned int num_processors; +void __cpuinit smp_store_cpu_info(int id); + #endif /* !__ASSEMBLY__ */ #else /* CONFIG_SMP */ diff --git a/include/asm-i386/timer.h b/include/asm-i386/timer.h index 153770e..51a713e 100644 --- a/include/asm-i386/timer.h +++ b/include/asm-i386/timer.h @@ -15,8 +15,38 @@ extern int no_sync_cmos_clock; extern int recalibrate_cpu_khz(void); #ifndef CONFIG_PARAVIRT -#define get_scheduled_cycles(val) rdtscll(val) #define calculate_cpu_khz() native_calculate_cpu_khz() #endif +/* Accellerators for sched_clock() + * convert from cycles(64bits) => nanoseconds (64bits) + * basic equation: + * ns = cycles / (freq / ns_per_sec) + * ns = cycles * (ns_per_sec / freq) + * ns = cycles * (10^9 / (cpu_khz * 10^3)) + * ns = cycles * (10^6 / cpu_khz) + * + * Then we use scaling math (suggested by george@mvista.com) to get: + * ns = cycles * (10^6 * SC / cpu_khz) / SC + * ns = cycles * cyc2ns_scale / SC + * + * And since SC is a constant power of two, we can convert the div + * into a shift. + * + * We can use khz divisor instead of mhz to keep a better percision, since + * cyc2ns_scale is limited to 10^6 * 2^10, which fits in 32 bits. + * (mathieu.desnoyers@polymtl.ca) + * + * -johnstul@us.ibm.com "math is hard, lets go shopping!" + */ +extern unsigned long cyc2ns_scale __read_mostly; + +#define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */ + +static inline unsigned long long cycles_2_ns(unsigned long long cyc) +{ + return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR; +} + + #endif diff --git a/include/asm-i386/unistd.h b/include/asm-i386/unistd.h index e84ace1..9b15545 100644 --- a/include/asm-i386/unistd.h +++ b/include/asm-i386/unistd.h @@ -329,10 +329,11 @@ #define __NR_signalfd 321 #define __NR_timerfd 322 #define __NR_eventfd 323 +#define __NR_fallocate 324 #ifdef __KERNEL__ -#define NR_syscalls 324 +#define NR_syscalls 325 #define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_OLD_READDIR diff --git a/include/asm-i386/vmi_time.h b/include/asm-i386/vmi_time.h index 213930b..4781881 100644 --- a/include/asm-i386/vmi_time.h +++ b/include/asm-i386/vmi_time.h @@ -49,7 +49,7 @@ extern struct vmi_timer_ops { extern void __init vmi_time_init(void); extern unsigned long vmi_get_wallclock(void); extern int vmi_set_wallclock(unsigned long now); -extern unsigned long long vmi_get_sched_cycles(void); +extern unsigned long long vmi_sched_clock(void); extern unsigned long vmi_cpu_khz(void); #ifdef CONFIG_X86_LOCAL_APIC diff --git a/include/asm-i386/xen/hypercall.h b/include/asm-i386/xen/hypercall.h new file mode 100644 index 0000000..bc0ee7d --- /dev/null +++ b/include/asm-i386/xen/hypercall.h @@ -0,0 +1,413 @@ +/****************************************************************************** + * hypercall.h + * + * Linux-specific hypervisor handling. + * + * Copyright (c) 2002-2004, K A Fraser + * + * 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; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef __HYPERCALL_H__ +#define __HYPERCALL_H__ + +#include <linux/errno.h> +#include <linux/string.h> + +#include <xen/interface/xen.h> +#include <xen/interface/sched.h> +#include <xen/interface/physdev.h> + +extern struct { char _entry[32]; } hypercall_page[]; + +#define _hypercall0(type, name) \ +({ \ + long __res; \ + asm volatile ( \ + "call %[call]" \ + : "=a" (__res) \ + : [call] "m" (hypercall_page[__HYPERVISOR_##name]) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall1(type, name, a1) \ +({ \ + long __res, __ign1; \ + asm volatile ( \ + "call %[call]" \ + : "=a" (__res), "=b" (__ign1) \ + : "1" ((long)(a1)), \ + [call] "m" (hypercall_page[__HYPERVISOR_##name]) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall2(type, name, a1, a2) \ +({ \ + long __res, __ign1, __ign2; \ + asm volatile ( \ + "call %[call]" \ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2) \ + : "1" ((long)(a1)), "2" ((long)(a2)), \ + [call] "m" (hypercall_page[__HYPERVISOR_##name]) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall3(type, name, a1, a2, a3) \ +({ \ + long __res, __ign1, __ign2, __ign3; \ + asm volatile ( \ + "call %[call]" \ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ + "=d" (__ign3) \ + : "1" ((long)(a1)), "2" ((long)(a2)), \ + "3" ((long)(a3)), \ + [call] "m" (hypercall_page[__HYPERVISOR_##name]) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall4(type, name, a1, a2, a3, a4) \ +({ \ + long __res, __ign1, __ign2, __ign3, __ign4; \ + asm volatile ( \ + "call %[call]" \ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ + "=d" (__ign3), "=S" (__ign4) \ + : "1" ((long)(a1)), "2" ((long)(a2)), \ + "3" ((long)(a3)), "4" ((long)(a4)), \ + [call] "m" (hypercall_page[__HYPERVISOR_##name]) \ + : "memory" ); \ + (type)__res; \ +}) + +#define _hypercall5(type, name, a1, a2, a3, a4, a5) \ +({ \ + long __res, __ign1, __ign2, __ign3, __ign4, __ign5; \ + asm volatile ( \ + "call %[call]" \ + : "=a" (__res), "=b" (__ign1), "=c" (__ign2), \ + "=d" (__ign3), "=S" (__ign4), "=D" (__ign5) \ + : "1" ((long)(a1)), "2" ((long)(a2)), \ + "3" ((long)(a3)), "4" ((long)(a4)), \ + "5" ((long)(a5)), \ + [call] "m" (hypercall_page[__HYPERVISOR_##name]) \ + : "memory" ); \ + (type)__res; \ +}) + +static inline int +HYPERVISOR_set_trap_table(struct trap_info *table) +{ + return _hypercall1(int, set_trap_table, table); +} + +static inline int +HYPERVISOR_mmu_update(struct mmu_update *req, int count, + int *success_count, domid_t domid) +{ + return _hypercall4(int, mmu_update, req, count, success_count, domid); +} + +static inline int +HYPERVISOR_mmuext_op(struct mmuext_op *op, int count, + int *success_count, domid_t domid) +{ + return _hypercall4(int, mmuext_op, op, count, success_count, domid); +} + +static inline int +HYPERVISOR_set_gdt(unsigned long *frame_list, int entries) +{ + return _hypercall2(int, set_gdt, frame_list, entries); +} + +static inline int +HYPERVISOR_stack_switch(unsigned long ss, unsigned long esp) +{ + return _hypercall2(int, stack_switch, ss, esp); +} + +static inline int +HYPERVISOR_set_callbacks(unsigned long event_selector, + unsigned long event_address, + unsigned long failsafe_selector, + unsigned long failsafe_address) +{ + return _hypercall4(int, set_callbacks, + event_selector, event_address, + failsafe_selector, failsafe_address); +} + +static inline int +HYPERVISOR_fpu_taskswitch(int set) +{ + return _hypercall1(int, fpu_taskswitch, set); +} + +static inline int +HYPERVISOR_sched_op(int cmd, unsigned long arg) +{ + return _hypercall2(int, sched_op, cmd, arg); +} + +static inline long +HYPERVISOR_set_timer_op(u64 timeout) +{ + unsigned long timeout_hi = (unsigned long)(timeout>>32); + unsigned long timeout_lo = (unsigned long)timeout; + return _hypercall2(long, set_timer_op, timeout_lo, timeout_hi); +} + +static inline int +HYPERVISOR_set_debugreg(int reg, unsigned long value) +{ + return _hypercall2(int, set_debugreg, reg, value); +} + +static inline unsigned long +HYPERVISOR_get_debugreg(int reg) +{ + return _hypercall1(unsigned long, get_debugreg, reg); +} + +static inline int +HYPERVISOR_update_descriptor(u64 ma, u64 desc) +{ + return _hypercall4(int, update_descriptor, ma, ma>>32, desc, desc>>32); +} + +static inline int +HYPERVISOR_memory_op(unsigned int cmd, void *arg) +{ + return _hypercall2(int, memory_op, cmd, arg); +} + +static inline int +HYPERVISOR_multicall(void *call_list, int nr_calls) +{ + return _hypercall2(int, multicall, call_list, nr_calls); +} + +static inline int +HYPERVISOR_update_va_mapping(unsigned long va, pte_t new_val, + unsigned long flags) +{ + unsigned long pte_hi = 0; +#ifdef CONFIG_X86_PAE + pte_hi = new_val.pte_high; +#endif + return _hypercall4(int, update_va_mapping, va, + new_val.pte_low, pte_hi, flags); +} + +static inline int +HYPERVISOR_event_channel_op(int cmd, void *arg) +{ + int rc = _hypercall2(int, event_channel_op, cmd, arg); + if (unlikely(rc == -ENOSYS)) { + struct evtchn_op op; + op.cmd = cmd; + memcpy(&op.u, arg, sizeof(op.u)); + rc = _hypercall1(int, event_channel_op_compat, &op); + memcpy(arg, &op.u, sizeof(op.u)); + } + return rc; +} + +static inline int +HYPERVISOR_xen_version(int cmd, void *arg) +{ + return _hypercall2(int, xen_version, cmd, arg); +} + +static inline int +HYPERVISOR_console_io(int cmd, int count, char *str) +{ + return _hypercall3(int, console_io, cmd, count, str); +} + +static inline int +HYPERVISOR_physdev_op(int cmd, void *arg) +{ + int rc = _hypercall2(int, physdev_op, cmd, arg); + if (unlikely(rc == -ENOSYS)) { + struct physdev_op op; + op.cmd = cmd; + memcpy(&op.u, arg, sizeof(op.u)); + rc = _hypercall1(int, physdev_op_compat, &op); + memcpy(arg, &op.u, sizeof(op.u)); + } + return rc; +} + +static inline int +HYPERVISOR_grant_table_op(unsigned int cmd, void *uop, unsigned int count) +{ + return _hypercall3(int, grant_table_op, cmd, uop, count); +} + +static inline int +HYPERVISOR_update_va_mapping_otherdomain(unsigned long va, pte_t new_val, + unsigned long flags, domid_t domid) +{ + unsigned long pte_hi = 0; +#ifdef CONFIG_X86_PAE + pte_hi = new_val.pte_high; +#endif + return _hypercall5(int, update_va_mapping_otherdomain, va, + new_val.pte_low, pte_hi, flags, domid); +} + +static inline int +HYPERVISOR_vm_assist(unsigned int cmd, unsigned int type) +{ + return _hypercall2(int, vm_assist, cmd, type); +} + +static inline int +HYPERVISOR_vcpu_op(int cmd, int vcpuid, void *extra_args) +{ + return _hypercall3(int, vcpu_op, cmd, vcpuid, extra_args); +} + +static inline int +HYPERVISOR_suspend(unsigned long srec) +{ + return _hypercall3(int, sched_op, SCHEDOP_shutdown, + SHUTDOWN_suspend, srec); +} + +static inline int +HYPERVISOR_nmi_op(unsigned long op, unsigned long arg) +{ + return _hypercall2(int, nmi_op, op, arg); +} + +static inline void +MULTI_update_va_mapping(struct multicall_entry *mcl, unsigned long va, + pte_t new_val, unsigned long flags) +{ + mcl->op = __HYPERVISOR_update_va_mapping; + mcl->args[0] = va; +#ifdef CONFIG_X86_PAE + mcl->args[1] = new_val.pte_low; + mcl->args[2] = new_val.pte_high; +#else + mcl->args[1] = new_val.pte_low; + mcl->args[2] = 0; +#endif + mcl->args[3] = flags; +} + +static inline void +MULTI_grant_table_op(struct multicall_entry *mcl, unsigned int cmd, + void *uop, unsigned int count) +{ + mcl->op = __HYPERVISOR_grant_table_op; + mcl->args[0] = cmd; + mcl->args[1] = (unsigned long)uop; + mcl->args[2] = count; +} + +static inline void +MULTI_update_va_mapping_otherdomain(struct multicall_entry *mcl, unsigned long va, + pte_t new_val, unsigned long flags, + domid_t domid) +{ + mcl->op = __HYPERVISOR_update_va_mapping_otherdomain; + mcl->args[0] = va; +#ifdef CONFIG_X86_PAE + mcl->args[1] = new_val.pte_low; + mcl->args[2] = new_val.pte_high; +#else + mcl->args[1] = new_val.pte_low; + mcl->args[2] = 0; +#endif + mcl->args[3] = flags; + mcl->args[4] = domid; +} + +static inline void +MULTI_update_descriptor(struct multicall_entry *mcl, u64 maddr, + struct desc_struct desc) +{ + mcl->op = __HYPERVISOR_update_descriptor; + mcl->args[0] = maddr; + mcl->args[1] = maddr >> 32; + mcl->args[2] = desc.a; + mcl->args[3] = desc.b; +} + +static inline void +MULTI_memory_op(struct multicall_entry *mcl, unsigned int cmd, void *arg) +{ + mcl->op = __HYPERVISOR_memory_op; + mcl->args[0] = cmd; + mcl->args[1] = (unsigned long)arg; +} + +static inline void +MULTI_mmu_update(struct multicall_entry *mcl, struct mmu_update *req, + int count, int *success_count, domid_t domid) +{ + mcl->op = __HYPERVISOR_mmu_update; + mcl->args[0] = (unsigned long)req; + mcl->args[1] = count; + mcl->args[2] = (unsigned long)success_count; + mcl->args[3] = domid; +} + +static inline void +MULTI_mmuext_op(struct multicall_entry *mcl, struct mmuext_op *op, int count, + int *success_count, domid_t domid) +{ + mcl->op = __HYPERVISOR_mmuext_op; + mcl->args[0] = (unsigned long)op; + mcl->args[1] = count; + mcl->args[2] = (unsigned long)success_count; + mcl->args[3] = domid; +} + +static inline void +MULTI_set_gdt(struct multicall_entry *mcl, unsigned long *frames, int entries) +{ + mcl->op = __HYPERVISOR_set_gdt; + mcl->args[0] = (unsigned long)frames; + mcl->args[1] = entries; +} + +static inline void +MULTI_stack_switch(struct multicall_entry *mcl, + unsigned long ss, unsigned long esp) +{ + mcl->op = __HYPERVISOR_stack_switch; + mcl->args[0] = ss; + mcl->args[1] = esp; +} + +#endif /* __HYPERCALL_H__ */ diff --git a/include/asm-i386/xen/hypervisor.h b/include/asm-i386/xen/hypervisor.h new file mode 100644 index 0000000..8e15dd2 --- /dev/null +++ b/include/asm-i386/xen/hypervisor.h @@ -0,0 +1,73 @@ +/****************************************************************************** + * hypervisor.h + * + * Linux-specific hypervisor handling. + * + * Copyright (c) 2002-2004, K A Fraser + * + * 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; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef __HYPERVISOR_H__ +#define __HYPERVISOR_H__ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/version.h> + +#include <xen/interface/xen.h> +#include <xen/interface/version.h> + +#include <asm/ptrace.h> +#include <asm/page.h> +#include <asm/desc.h> +#if defined(__i386__) +# ifdef CONFIG_X86_PAE +# include <asm-generic/pgtable-nopud.h> +# else +# include <asm-generic/pgtable-nopmd.h> +# endif +#endif +#include <asm/xen/hypercall.h> + +/* arch/i386/kernel/setup.c */ +extern struct shared_info *HYPERVISOR_shared_info; +extern struct start_info *xen_start_info; +#define is_initial_xendomain() (xen_start_info->flags & SIF_INITDOMAIN) + +/* arch/i386/mach-xen/evtchn.c */ +/* Force a proper event-channel callback from Xen. */ +extern void force_evtchn_callback(void); + +/* Turn jiffies into Xen system time. */ +u64 jiffies_to_st(unsigned long jiffies); + + +#define MULTI_UVMFLAGS_INDEX 3 +#define MULTI_UVMDOMID_INDEX 4 + +#define is_running_on_xen() (xen_start_info ? 1 : 0) + +#endif /* __HYPERVISOR_H__ */ diff --git a/include/asm-i386/xen/interface.h b/include/asm-i386/xen/interface.h new file mode 100644 index 0000000..165c396 --- /dev/null +++ b/include/asm-i386/xen/interface.h @@ -0,0 +1,188 @@ +/****************************************************************************** + * arch-x86_32.h + * + * Guest OS interface to x86 32-bit Xen. + * + * Copyright (c) 2004, K A Fraser + */ + +#ifndef __XEN_PUBLIC_ARCH_X86_32_H__ +#define __XEN_PUBLIC_ARCH_X86_32_H__ + +#ifdef __XEN__ +#define __DEFINE_GUEST_HANDLE(name, type) \ + typedef struct { type *p; } __guest_handle_ ## name +#else +#define __DEFINE_GUEST_HANDLE(name, type) \ + typedef type * __guest_handle_ ## name +#endif + +#define DEFINE_GUEST_HANDLE_STRUCT(name) \ + __DEFINE_GUEST_HANDLE(name, struct name) +#define DEFINE_GUEST_HANDLE(name) __DEFINE_GUEST_HANDLE(name, name) +#define GUEST_HANDLE(name) __guest_handle_ ## name + +#ifndef __ASSEMBLY__ +/* Guest handles for primitive C types. */ +__DEFINE_GUEST_HANDLE(uchar, unsigned char); +__DEFINE_GUEST_HANDLE(uint, unsigned int); +__DEFINE_GUEST_HANDLE(ulong, unsigned long); +DEFINE_GUEST_HANDLE(char); +DEFINE_GUEST_HANDLE(int); +DEFINE_GUEST_HANDLE(long); +DEFINE_GUEST_HANDLE(void); +#endif + +/* + * SEGMENT DESCRIPTOR TABLES + */ +/* + * A number of GDT entries are reserved by Xen. These are not situated at the + * start of the GDT because some stupid OSes export hard-coded selector values + * in their ABI. These hard-coded values are always near the start of the GDT, + * so Xen places itself out of the way, at the far end of the GDT. + */ +#define FIRST_RESERVED_GDT_PAGE 14 +#define FIRST_RESERVED_GDT_BYTE (FIRST_RESERVED_GDT_PAGE * 4096) +#define FIRST_RESERVED_GDT_ENTRY (FIRST_RESERVED_GDT_BYTE / 8) + +/* + * These flat segments are in the Xen-private section of every GDT. Since these + * are also present in the initial GDT, many OSes will be able to avoid + * installing their own GDT. + */ +#define FLAT_RING1_CS 0xe019 /* GDT index 259 */ +#define FLAT_RING1_DS 0xe021 /* GDT index 260 */ +#define FLAT_RING1_SS 0xe021 /* GDT index 260 */ +#define FLAT_RING3_CS 0xe02b /* GDT index 261 */ +#define FLAT_RING3_DS 0xe033 /* GDT index 262 */ +#define FLAT_RING3_SS 0xe033 /* GDT index 262 */ + +#define FLAT_KERNEL_CS FLAT_RING1_CS +#define FLAT_KERNEL_DS FLAT_RING1_DS +#define FLAT_KERNEL_SS FLAT_RING1_SS +#define FLAT_USER_CS FLAT_RING3_CS +#define FLAT_USER_DS FLAT_RING3_DS +#define FLAT_USER_SS FLAT_RING3_SS + +/* And the trap vector is... */ +#define TRAP_INSTR "int $0x82" + +/* + * Virtual addresses beyond this are not modifiable by guest OSes. The + * machine->physical mapping table starts at this address, read-only. + */ +#ifdef CONFIG_X86_PAE +#define __HYPERVISOR_VIRT_START 0xF5800000 +#else +#define __HYPERVISOR_VIRT_START 0xFC000000 +#endif + +#ifndef HYPERVISOR_VIRT_START +#define HYPERVISOR_VIRT_START mk_unsigned_long(__HYPERVISOR_VIRT_START) +#endif + +#ifndef machine_to_phys_mapping +#define machine_to_phys_mapping ((unsigned long *)HYPERVISOR_VIRT_START) +#endif + +/* Maximum number of virtual CPUs in multi-processor guests. */ +#define MAX_VIRT_CPUS 32 + +#ifndef __ASSEMBLY__ + +/* + * Send an array of these to HYPERVISOR_set_trap_table() + */ +#define TI_GET_DPL(_ti) ((_ti)->flags & 3) +#define TI_GET_IF(_ti) ((_ti)->flags & 4) +#define TI_SET_DPL(_ti, _dpl) ((_ti)->flags |= (_dpl)) +#define TI_SET_IF(_ti, _if) ((_ti)->flags |= ((!!(_if))<<2)) + +struct trap_info { + uint8_t vector; /* exception vector */ + uint8_t flags; /* 0-3: privilege level; 4: clear event enable? */ + uint16_t cs; /* code selector */ + unsigned long address; /* code offset */ +}; +DEFINE_GUEST_HANDLE_STRUCT(trap_info); + +struct cpu_user_regs { + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t esi; + uint32_t edi; + uint32_t ebp; + uint32_t eax; + uint16_t error_code; /* private */ + uint16_t entry_vector; /* private */ + uint32_t eip; + uint16_t cs; + uint8_t saved_upcall_mask; + uint8_t _pad0; + uint32_t eflags; /* eflags.IF == !saved_upcall_mask */ + uint32_t esp; + uint16_t ss, _pad1; + uint16_t es, _pad2; + uint16_t ds, _pad3; + uint16_t fs, _pad4; + uint16_t gs, _pad5; +}; +DEFINE_GUEST_HANDLE_STRUCT(cpu_user_regs); + +typedef uint64_t tsc_timestamp_t; /* RDTSC timestamp */ + +/* + * The following is all CPU context. Note that the fpu_ctxt block is filled + * in by FXSAVE if the CPU has feature FXSR; otherwise FSAVE is used. + */ +struct vcpu_guest_context { + /* FPU registers come first so they can be aligned for FXSAVE/FXRSTOR. */ + struct { char x[512]; } fpu_ctxt; /* User-level FPU registers */ +#define VGCF_I387_VALID (1<<0) +#define VGCF_HVM_GUEST (1<<1) +#define VGCF_IN_KERNEL (1<<2) + unsigned long flags; /* VGCF_* flags */ + struct cpu_user_regs user_regs; /* User-level CPU registers */ + struct trap_info trap_ctxt[256]; /* Virtual IDT */ + unsigned long ldt_base, ldt_ents; /* LDT (linear address, # ents) */ + unsigned long gdt_frames[16], gdt_ents; /* GDT (machine frames, # ents) */ + unsigned long kernel_ss, kernel_sp; /* Virtual TSS (only SS1/SP1) */ + unsigned long ctrlreg[8]; /* CR0-CR7 (control registers) */ + unsigned long debugreg[8]; /* DB0-DB7 (debug registers) */ + unsigned long event_callback_cs; /* CS:EIP of event callback */ + unsigned long event_callback_eip; + unsigned long failsafe_callback_cs; /* CS:EIP of failsafe callback */ + unsigned long failsafe_callback_eip; + unsigned long vm_assist; /* VMASST_TYPE_* bitmap */ +}; +DEFINE_GUEST_HANDLE_STRUCT(vcpu_guest_context); + +struct arch_shared_info { + unsigned long max_pfn; /* max pfn that appears in table */ + /* Frame containing list of mfns containing list of mfns containing p2m. */ + unsigned long pfn_to_mfn_frame_list_list; + unsigned long nmi_reason; +}; + +struct arch_vcpu_info { + unsigned long cr2; + unsigned long pad[5]; /* sizeof(struct vcpu_info) == 64 */ +}; + +#endif /* !__ASSEMBLY__ */ + +/* + * Prefix forces emulation of some non-trapping instructions. + * Currently only CPUID. + */ +#ifdef __ASSEMBLY__ +#define XEN_EMULATE_PREFIX .byte 0x0f,0x0b,0x78,0x65,0x6e ; +#define XEN_CPUID XEN_EMULATE_PREFIX cpuid +#else +#define XEN_EMULATE_PREFIX ".byte 0x0f,0x0b,0x78,0x65,0x6e ; " +#define XEN_CPUID XEN_EMULATE_PREFIX "cpuid" +#endif + +#endif diff --git a/include/asm-powerpc/systbl.h b/include/asm-powerpc/systbl.h index 1cc3f9c..cc6d872 100644 --- a/include/asm-powerpc/systbl.h +++ b/include/asm-powerpc/systbl.h @@ -308,6 +308,7 @@ COMPAT_SYS_SPU(move_pages) SYSCALL_SPU(getcpu) COMPAT_SYS(epoll_pwait) COMPAT_SYS_SPU(utimensat) +COMPAT_SYS(fallocate) COMPAT_SYS_SPU(signalfd) COMPAT_SYS_SPU(timerfd) SYSCALL_SPU(eventfd) diff --git a/include/asm-powerpc/unistd.h b/include/asm-powerpc/unistd.h index f71c606..97d82b6 100644 --- a/include/asm-powerpc/unistd.h +++ b/include/asm-powerpc/unistd.h @@ -331,10 +331,11 @@ #define __NR_timerfd 306 #define __NR_eventfd 307 #define __NR_sync_file_range2 308 +#define __NR_fallocate 309 #ifdef __KERNEL__ -#define __NR_syscalls 309 +#define __NR_syscalls 310 #define __NR__exit __NR_exit #define NR_syscalls __NR_syscalls diff --git a/include/asm-sparc64/io.h b/include/asm-sparc64/io.h index ad595b6..9565a89 100644 --- a/include/asm-sparc64/io.h +++ b/include/asm-sparc64/io.h @@ -14,11 +14,6 @@ #define __SLOW_DOWN_IO do { } while (0) #define SLOW_DOWN_IO do { } while (0) -extern unsigned long virt_to_bus_not_defined_use_pci_map(volatile void *addr); -#define virt_to_bus virt_to_bus_not_defined_use_pci_map -extern unsigned long bus_to_virt_not_defined_use_pci_map(volatile void *addr); -#define bus_to_virt bus_to_virt_not_defined_use_pci_map - /* BIO layer definitions. */ extern unsigned long kern_base, kern_size; #define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) diff --git a/include/asm-sparc64/mdesc.h b/include/asm-sparc64/mdesc.h index e97c431..1acc727 100644 --- a/include/asm-sparc64/mdesc.h +++ b/include/asm-sparc64/mdesc.h @@ -61,6 +61,16 @@ extern u64 mdesc_arc_target(struct mdesc_handle *hp, u64 arc); extern void mdesc_update(void); +struct mdesc_notifier_client { + void (*add)(struct mdesc_handle *handle, u64 node); + void (*remove)(struct mdesc_handle *handle, u64 node); + + const char *node_name; + struct mdesc_notifier_client *next; +}; + +extern void mdesc_register_notifier(struct mdesc_notifier_client *client); + extern void mdesc_fill_in_cpu_data(cpumask_t mask); extern void sun4v_mdesc_init(void); diff --git a/include/asm-sparc64/vio.h b/include/asm-sparc64/vio.h index 83c9642..c0a8d4e 100644 --- a/include/asm-sparc64/vio.h +++ b/include/asm-sparc64/vio.h @@ -264,7 +264,7 @@ static inline u32 vio_dring_avail(struct vio_dring_state *dr, ((dr->prod - dr->cons) & (ring_size - 1))); } -#define VIO_MAX_TYPE_LEN 64 +#define VIO_MAX_TYPE_LEN 32 #define VIO_MAX_COMPAT_LEN 64 struct vio_dev { diff --git a/include/asm-x86_64/unistd.h b/include/asm-x86_64/unistd.h index 8696f8a..fc4e73f 100644 --- a/include/asm-x86_64/unistd.h +++ b/include/asm-x86_64/unistd.h @@ -630,6 +630,8 @@ __SYSCALL(__NR_signalfd, sys_signalfd) __SYSCALL(__NR_timerfd, sys_timerfd) #define __NR_eventfd 284 __SYSCALL(__NR_eventfd, sys_eventfd) +#define __NR_fallocate 285 +__SYSCALL(__NR_fallocate, sys_fallocate) #ifndef __NO_STUBS #define __ARCH_WANT_OLD_READDIR diff --git a/include/linux/elfnote.h b/include/linux/elfnote.h index 9a1e067..e831759 100644 --- a/include/linux/elfnote.h +++ b/include/linux/elfnote.h @@ -38,17 +38,25 @@ * e.g. ELFNOTE(XYZCo, 42, .asciz, "forty-two") * ELFNOTE(XYZCo, 12, .long, 0xdeadbeef) */ -#define ELFNOTE(name, type, desctype, descdata) \ -.pushsection .note.name, "",@note ; \ - .align 4 ; \ +#define ELFNOTE_START(name, type, flags) \ +.pushsection .note.name, flags,@note ; \ + .balign 4 ; \ .long 2f - 1f /* namesz */ ; \ - .long 4f - 3f /* descsz */ ; \ + .long 4484f - 3f /* descsz */ ; \ .long type ; \ 1:.asciz #name ; \ -2:.align 4 ; \ -3:desctype descdata ; \ -4:.align 4 ; \ +2:.balign 4 ; \ +3: + +#define ELFNOTE_END \ +4484:.balign 4 ; \ .popsection ; + +#define ELFNOTE(name, type, desc) \ + ELFNOTE_START(name, type, "") \ + desc ; \ + ELFNOTE_END + #else /* !__ASSEMBLER__ */ #include <linux/elf.h> /* diff --git a/include/linux/ext4_fs.h b/include/linux/ext4_fs.h index de1f9f7..cdee7aa 100644 --- a/include/linux/ext4_fs.h +++ b/include/linux/ext4_fs.h @@ -71,7 +71,7 @@ /* * Maximal count of links to a file */ -#define EXT4_LINK_MAX 32000 +#define EXT4_LINK_MAX 65000 /* * Macro-instructions used to manage several block sizes @@ -102,6 +102,7 @@ EXT4_GOOD_OLD_FIRST_INO : \ (s)->s_first_ino) #endif +#define EXT4_BLOCK_ALIGN(size, blkbits) ALIGN((size), (1 << (blkbits))) /* * Macro-instructions used to manage fragments @@ -201,6 +202,7 @@ struct ext4_group_desc #define EXT4_STATE_JDATA 0x00000001 /* journaled data exists */ #define EXT4_STATE_NEW 0x00000002 /* inode is newly created */ #define EXT4_STATE_XATTR 0x00000004 /* has in-inode xattrs */ +#define EXT4_STATE_NO_EXPAND 0x00000008 /* No space for expansion */ /* Used to pass group descriptor data when online resize is done */ struct ext4_new_group_input { @@ -225,6 +227,11 @@ struct ext4_new_group_data { __u32 free_blocks_count; }; +/* + * Following is used by preallocation code to tell get_blocks() that we + * want uninitialzed extents. + */ +#define EXT4_CREATE_UNINITIALIZED_EXT 2 /* * ioctl commands @@ -237,7 +244,7 @@ struct ext4_new_group_data { #define EXT4_IOC_GROUP_ADD _IOW('f', 8,struct ext4_new_group_input) #define EXT4_IOC_GETVERSION_OLD FS_IOC_GETVERSION #define EXT4_IOC_SETVERSION_OLD FS_IOC_SETVERSION -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG #define EXT4_IOC_WAIT_FOR_READONLY _IOR('f', 99, long) #endif #define EXT4_IOC_GETRSVSZ _IOR('f', 5, long) @@ -253,7 +260,7 @@ struct ext4_new_group_data { #define EXT4_IOC32_GETRSVSZ _IOR('f', 5, int) #define EXT4_IOC32_SETRSVSZ _IOW('f', 6, int) #define EXT4_IOC32_GROUP_EXTEND _IOW('f', 7, unsigned int) -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG #define EXT4_IOC32_WAIT_FOR_READONLY _IOR('f', 99, int) #endif #define EXT4_IOC32_GETVERSION_OLD FS_IOC32_GETVERSION @@ -282,7 +289,7 @@ struct ext4_inode { __le16 i_uid; /* Low 16 bits of Owner Uid */ __le32 i_size; /* Size in bytes */ __le32 i_atime; /* Access time */ - __le32 i_ctime; /* Creation time */ + __le32 i_ctime; /* Inode Change time */ __le32 i_mtime; /* Modification time */ __le32 i_dtime; /* Deletion Time */ __le16 i_gid; /* Low 16 bits of Group Id */ @@ -331,10 +338,85 @@ struct ext4_inode { } osd2; /* OS dependent 2 */ __le16 i_extra_isize; __le16 i_pad1; + __le32 i_ctime_extra; /* extra Change time (nsec << 2 | epoch) */ + __le32 i_mtime_extra; /* extra Modification time(nsec << 2 | epoch) */ + __le32 i_atime_extra; /* extra Access time (nsec << 2 | epoch) */ + __le32 i_crtime; /* File Creation time */ + __le32 i_crtime_extra; /* extra FileCreationtime (nsec << 2 | epoch) */ }; #define i_size_high i_dir_acl +#define EXT4_EPOCH_BITS 2 +#define EXT4_EPOCH_MASK ((1 << EXT4_EPOCH_BITS) - 1) +#define EXT4_NSEC_MASK (~0UL << EXT4_EPOCH_BITS) + +/* + * Extended fields will fit into an inode if the filesystem was formatted + * with large inodes (-I 256 or larger) and there are not currently any EAs + * consuming all of the available space. For new inodes we always reserve + * enough space for the kernel's known extended fields, but for inodes + * created with an old kernel this might not have been the case. None of + * the extended inode fields is critical for correct filesystem operation. + * This macro checks if a certain field fits in the inode. Note that + * inode-size = GOOD_OLD_INODE_SIZE + i_extra_isize + */ +#define EXT4_FITS_IN_INODE(ext4_inode, einode, field) \ + ((offsetof(typeof(*ext4_inode), field) + \ + sizeof((ext4_inode)->field)) \ + <= (EXT4_GOOD_OLD_INODE_SIZE + \ + (einode)->i_extra_isize)) \ + +static inline __le32 ext4_encode_extra_time(struct timespec *time) +{ + return cpu_to_le32((sizeof(time->tv_sec) > 4 ? + time->tv_sec >> 32 : 0) | + ((time->tv_nsec << 2) & EXT4_NSEC_MASK)); +} + +static inline void ext4_decode_extra_time(struct timespec *time, __le32 extra) +{ + if (sizeof(time->tv_sec) > 4) + time->tv_sec |= (__u64)(le32_to_cpu(extra) & EXT4_EPOCH_MASK) + << 32; + time->tv_nsec = (le32_to_cpu(extra) & EXT4_NSEC_MASK) >> 2; +} + +#define EXT4_INODE_SET_XTIME(xtime, inode, raw_inode) \ +do { \ + (raw_inode)->xtime = cpu_to_le32((inode)->xtime.tv_sec); \ + if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra)) \ + (raw_inode)->xtime ## _extra = \ + ext4_encode_extra_time(&(inode)->xtime); \ +} while (0) + +#define EXT4_EINODE_SET_XTIME(xtime, einode, raw_inode) \ +do { \ + if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime)) \ + (raw_inode)->xtime = cpu_to_le32((einode)->xtime.tv_sec); \ + if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime ## _extra)) \ + (raw_inode)->xtime ## _extra = \ + ext4_encode_extra_time(&(einode)->xtime); \ +} while (0) + +#define EXT4_INODE_GET_XTIME(xtime, inode, raw_inode) \ +do { \ + (inode)->xtime.tv_sec = (signed)le32_to_cpu((raw_inode)->xtime); \ + if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra)) \ + ext4_decode_extra_time(&(inode)->xtime, \ + raw_inode->xtime ## _extra); \ +} while (0) + +#define EXT4_EINODE_GET_XTIME(xtime, einode, raw_inode) \ +do { \ + if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime)) \ + (einode)->xtime.tv_sec = \ + (signed)le32_to_cpu((raw_inode)->xtime); \ + if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime ## _extra)) \ + ext4_decode_extra_time(&(einode)->xtime, \ + raw_inode->xtime ## _extra); \ +} while (0) + #if defined(__KERNEL__) || defined(__linux__) #define i_reserved1 osd1.linux1.l_i_reserved1 #define i_frag osd2.linux2.l_i_frag @@ -533,6 +615,13 @@ static inline struct ext4_inode_info *EXT4_I(struct inode *inode) return container_of(inode, struct ext4_inode_info, vfs_inode); } +static inline struct timespec ext4_current_time(struct inode *inode) +{ + return (inode->i_sb->s_time_gran < NSEC_PER_SEC) ? + current_fs_time(inode->i_sb) : CURRENT_TIME_SEC; +} + + static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino) { return ino == EXT4_ROOT_INO || @@ -603,6 +692,8 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino) #define EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 #define EXT4_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 #define EXT4_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 +#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020 +#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040 #define EXT4_FEATURE_INCOMPAT_COMPRESSION 0x0001 #define EXT4_FEATURE_INCOMPAT_FILETYPE 0x0002 @@ -620,6 +711,8 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino) EXT4_FEATURE_INCOMPAT_64BIT) #define EXT4_FEATURE_RO_COMPAT_SUPP (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \ EXT4_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT4_FEATURE_RO_COMPAT_DIR_NLINK | \ + EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE | \ EXT4_FEATURE_RO_COMPAT_BTREE_DIR) /* @@ -862,6 +955,7 @@ extern int ext4_change_inode_journal_flag(struct inode *, int); extern int ext4_get_inode_loc(struct inode *, struct ext4_iloc *); extern void ext4_truncate (struct inode *); extern void ext4_set_inode_flags(struct inode *); +extern void ext4_get_inode_flags(struct ext4_inode_info *); extern void ext4_set_aops(struct inode *inode); extern int ext4_writepage_trans_blocks(struct inode *); extern int ext4_block_truncate_page(handle_t *handle, struct page *page, @@ -983,6 +1077,8 @@ extern int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, extern void ext4_ext_truncate(struct inode *, struct page *); extern void ext4_ext_init(struct super_block *); extern void ext4_ext_release(struct super_block *); +extern long ext4_fallocate(struct inode *inode, int mode, loff_t offset, + loff_t len); static inline int ext4_get_blocks_wrap(handle_t *handle, struct inode *inode, sector_t block, unsigned long max_blocks, struct buffer_head *bh, diff --git a/include/linux/ext4_fs_extents.h b/include/linux/ext4_fs_extents.h index acfe5974..81406f3 100644 --- a/include/linux/ext4_fs_extents.h +++ b/include/linux/ext4_fs_extents.h @@ -141,7 +141,25 @@ typedef int (*ext_prepare_callback)(struct inode *, struct ext4_ext_path *, #define EXT_MAX_BLOCK 0xffffffff -#define EXT_MAX_LEN ((1UL << 15) - 1) +/* + * EXT_INIT_MAX_LEN is the maximum number of blocks we can have in an + * initialized extent. This is 2^15 and not (2^16 - 1), since we use the + * MSB of ee_len field in the extent datastructure to signify if this + * particular extent is an initialized extent or an uninitialized (i.e. + * preallocated). + * EXT_UNINIT_MAX_LEN is the maximum number of blocks we can have in an + * uninitialized extent. + * If ee_len is <= 0x8000, it is an initialized extent. Otherwise, it is an + * uninitialized one. In other words, if MSB of ee_len is set, it is an + * uninitialized extent with only one special scenario when ee_len = 0x8000. + * In this case we can not have an uninitialized extent of zero length and + * thus we make it as a special case of initialized extent with 0x8000 length. + * This way we get better extent-to-group alignment for initialized extents. + * Hence, the maximum number of blocks we can have in an *initialized* + * extent is 2^15 (32768) and in an *uninitialized* extent is 2^15-1 (32767). + */ +#define EXT_INIT_MAX_LEN (1UL << 15) +#define EXT_UNINIT_MAX_LEN (EXT_INIT_MAX_LEN - 1) #define EXT_FIRST_EXTENT(__hdr__) \ @@ -188,8 +206,31 @@ ext4_ext_invalidate_cache(struct inode *inode) EXT4_I(inode)->i_cached_extent.ec_type = EXT4_EXT_CACHE_NO; } +static inline void ext4_ext_mark_uninitialized(struct ext4_extent *ext) +{ + /* We can not have an uninitialized extent of zero length! */ + BUG_ON((le16_to_cpu(ext->ee_len) & ~EXT_INIT_MAX_LEN) == 0); + ext->ee_len |= cpu_to_le16(EXT_INIT_MAX_LEN); +} + +static inline int ext4_ext_is_uninitialized(struct ext4_extent *ext) +{ + /* Extent with ee_len of 0x8000 is treated as an initialized extent */ + return (le16_to_cpu(ext->ee_len) > EXT_INIT_MAX_LEN); +} + +static inline int ext4_ext_get_actual_len(struct ext4_extent *ext) +{ + return (le16_to_cpu(ext->ee_len) <= EXT_INIT_MAX_LEN ? + le16_to_cpu(ext->ee_len) : + (le16_to_cpu(ext->ee_len) - EXT_INIT_MAX_LEN)); +} + extern int ext4_extent_tree_init(handle_t *, struct inode *); extern int ext4_ext_calc_credits_for_insert(struct inode *, struct ext4_ext_path *); +extern int ext4_ext_try_to_merge(struct inode *inode, + struct ext4_ext_path *path, + struct ext4_extent *); extern unsigned int ext4_ext_check_overlap(struct inode *, struct ext4_extent *, struct ext4_ext_path *); extern int ext4_ext_insert_extent(handle_t *, struct inode *, struct ext4_ext_path *, struct ext4_extent *); extern int ext4_ext_walk_space(struct inode *, unsigned long, unsigned long, ext_prepare_callback, void *); diff --git a/include/linux/ext4_fs_i.h b/include/linux/ext4_fs_i.h index 9de4944..1a511e99 100644 --- a/include/linux/ext4_fs_i.h +++ b/include/linux/ext4_fs_i.h @@ -153,6 +153,11 @@ struct ext4_inode_info { unsigned long i_ext_generation; struct ext4_ext_cache i_cached_extent; + /* + * File creation time. Its function is same as that of + * struct timespec i_{a,c,m}time in the generic inode. + */ + struct timespec i_crtime; }; #endif /* _LINUX_EXT4_FS_I */ diff --git a/include/linux/ext4_fs_sb.h b/include/linux/ext4_fs_sb.h index 2347557..1b2ffee 100644 --- a/include/linux/ext4_fs_sb.h +++ b/include/linux/ext4_fs_sb.h @@ -73,7 +73,7 @@ struct ext4_sb_info { struct list_head s_orphan; unsigned long s_commit_interval; struct block_device *journal_bdev; -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG struct timer_list turn_ro_timer; /* For turning read-only (crash simulation) */ wait_queue_head_t ro_wait_queue; /* For people waiting for the fs to go read-only */ #endif @@ -81,6 +81,7 @@ struct ext4_sb_info { char *s_qf_names[MAXQUOTAS]; /* Names of quota files with journalled quota */ int s_jquota_fmt; /* Format of quota to use */ #endif + unsigned int s_want_extra_isize; /* New inodes should reserve # bytes */ #ifdef EXTENTS_STATS /* ext4 extents stats */ diff --git a/include/linux/falloc.h b/include/linux/falloc.h new file mode 100644 index 0000000..8e912ab --- /dev/null +++ b/include/linux/falloc.h @@ -0,0 +1,6 @@ +#ifndef _FALLOC_H_ +#define _FALLOC_H_ + +#define FALLOC_FL_KEEP_SIZE 0x01 /* default is extend size */ + +#endif /* _FALLOC_H_ */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 98205f6..0b806c5 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1147,6 +1147,8 @@ struct inode_operations { ssize_t (*listxattr) (struct dentry *, char *, size_t); int (*removexattr) (struct dentry *, const char *); void (*truncate_range)(struct inode *, loff_t, loff_t); + long (*fallocate)(struct inode *inode, int mode, loff_t offset, + loff_t len); }; struct seq_file; diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index 0e0fedd..260d6d7 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -50,14 +50,14 @@ */ #define JBD_DEFAULT_MAX_COMMIT_AGE 5 -#ifdef CONFIG_JBD_DEBUG +#ifdef CONFIG_JBD2_DEBUG /* * Define JBD_EXPENSIVE_CHECKING to enable more expensive internal * consistency checks. By default we don't do this unless - * CONFIG_JBD_DEBUG is on. + * CONFIG_JBD2_DEBUG is on. */ #define JBD_EXPENSIVE_CHECKING -extern int jbd2_journal_enable_debug; +extern u8 jbd2_journal_enable_debug; #define jbd_debug(n, f, a...) \ do { \ diff --git a/include/linux/kmod.h b/include/linux/kmod.h index 10f505c..5dc1384 100644 --- a/include/linux/kmod.h +++ b/include/linux/kmod.h @@ -36,13 +36,57 @@ static inline int request_module(const char * name, ...) { return -ENOSYS; } #define try_then_request_module(x, mod...) ((x) ?: (request_module(mod), (x))) struct key; -extern int call_usermodehelper_keys(char *path, char *argv[], char *envp[], - struct key *session_keyring, int wait); +struct file; +struct subprocess_info; + +/* Allocate a subprocess_info structure */ +struct subprocess_info *call_usermodehelper_setup(char *path, + char **argv, char **envp); + +/* Set various pieces of state into the subprocess_info structure */ +void call_usermodehelper_setkeys(struct subprocess_info *info, + struct key *session_keyring); +int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info, + struct file **filp); +void call_usermodehelper_setcleanup(struct subprocess_info *info, + void (*cleanup)(char **argv, char **envp)); + +enum umh_wait { + UMH_NO_WAIT = -1, /* don't wait at all */ + UMH_WAIT_EXEC = 0, /* wait for the exec, but not the process */ + UMH_WAIT_PROC = 1, /* wait for the process to complete */ +}; + +/* Actually execute the sub-process */ +int call_usermodehelper_exec(struct subprocess_info *info, enum umh_wait wait); + +/* Free the subprocess_info. This is only needed if you're not going + to call call_usermodehelper_exec */ +void call_usermodehelper_freeinfo(struct subprocess_info *info); static inline int -call_usermodehelper(char *path, char **argv, char **envp, int wait) +call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait) { - return call_usermodehelper_keys(path, argv, envp, NULL, wait); + struct subprocess_info *info; + + info = call_usermodehelper_setup(path, argv, envp); + if (info == NULL) + return -ENOMEM; + return call_usermodehelper_exec(info, wait); +} + +static inline int +call_usermodehelper_keys(char *path, char **argv, char **envp, + struct key *session_keyring, enum umh_wait wait) +{ + struct subprocess_info *info; + + info = call_usermodehelper_setup(path, argv, envp); + if (info == NULL) + return -ENOMEM; + + call_usermodehelper_setkeys(info, session_keyring); + return call_usermodehelper_exec(info, wait); } extern void usermodehelper_init(void); diff --git a/include/linux/major.h b/include/linux/major.h index 7e7c909..0cb9805 100644 --- a/include/linux/major.h +++ b/include/linux/major.h @@ -158,6 +158,8 @@ #define VXSPEC_MAJOR 200 /* VERITAS volume config driver */ #define VXDMP_MAJOR 201 /* VERITAS volume multipath driver */ +#define XENVBD_MAJOR 202 /* Xen virtual block device */ + #define MSR_MAJOR 202 #define CPUID_MAJOR 203 diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index da7a13c..9820ca1 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1098,10 +1098,8 @@ extern int dev_mc_delete(struct net_device *dev, void *addr, int alen, int all extern int dev_mc_add(struct net_device *dev, void *addr, int alen, int newonly); extern int dev_mc_sync(struct net_device *to, struct net_device *from); extern void dev_mc_unsync(struct net_device *to, struct net_device *from); -extern void dev_mc_discard(struct net_device *dev); extern int __dev_addr_delete(struct dev_addr_list **list, int *count, void *addr, int alen, int all); extern int __dev_addr_add(struct dev_addr_list **list, int *count, void *addr, int alen, int newonly); -extern void __dev_addr_discard(struct dev_addr_list **list); extern void dev_set_promiscuity(struct net_device *dev, int inc); extern void dev_set_allmulti(struct net_device *dev, int inc); extern void netdev_state_change(struct net_device *dev); diff --git a/include/linux/netfilter_ipv4/ipt_iprange.h b/include/linux/netfilter_ipv4/ipt_iprange.h index 34ab0fb..a92fefc 100644 --- a/include/linux/netfilter_ipv4/ipt_iprange.h +++ b/include/linux/netfilter_ipv4/ipt_iprange.h @@ -1,6 +1,8 @@ #ifndef _IPT_IPRANGE_H #define _IPT_IPRANGE_H +#include <linux/types.h> + #define IPRANGE_SRC 0x01 /* Match source IP address */ #define IPRANGE_DST 0x02 /* Match destination IP address */ #define IPRANGE_SRC_INV 0x10 /* Negate the condition */ diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h index ae2d79f..731cd2a 100644 --- a/include/linux/page-flags.h +++ b/include/linux/page-flags.h @@ -92,6 +92,7 @@ /* PG_owner_priv_1 users should have descriptive aliases */ #define PG_checked PG_owner_priv_1 /* Used by some filesystems */ +#define PG_pinned PG_owner_priv_1 /* Xen pinned pagetable */ #if (BITS_PER_LONG > 32) /* @@ -170,6 +171,10 @@ static inline void SetPageUptodate(struct page *page) #define SetPageChecked(page) set_bit(PG_checked, &(page)->flags) #define ClearPageChecked(page) clear_bit(PG_checked, &(page)->flags) +#define PagePinned(page) test_bit(PG_pinned, &(page)->flags) +#define SetPagePinned(page) set_bit(PG_pinned, &(page)->flags) +#define ClearPagePinned(page) clear_bit(PG_pinned, &(page)->flags) + #define PageReserved(page) test_bit(PG_reserved, &(page)->flags) #define SetPageReserved(page) set_bit(PG_reserved, &(page)->flags) #define ClearPageReserved(page) clear_bit(PG_reserved, &(page)->flags) diff --git a/include/linux/reboot.h b/include/linux/reboot.h index 1dd1c70..85ea63f 100644 --- a/include/linux/reboot.h +++ b/include/linux/reboot.h @@ -67,6 +67,11 @@ extern void kernel_power_off(void); void ctrl_alt_del(void); +#define POWEROFF_CMD_PATH_LEN 256 +extern char poweroff_cmd[POWEROFF_CMD_PATH_LEN]; + +extern int orderly_poweroff(bool force); + /* * Emergency restart, callable from an interrupt handler. */ diff --git a/include/linux/string.h b/include/linux/string.h index 7f2eb6a..836062b 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -105,8 +105,12 @@ extern void * memchr(const void *,int,__kernel_size_t); #endif extern char *kstrdup(const char *s, gfp_t gfp); +extern char *kstrndup(const char *s, size_t len, gfp_t gfp); extern void *kmemdup(const void *src, size_t len, gfp_t gfp); +extern char **argv_split(gfp_t gfp, const char *str, int *argcp); +extern void argv_free(char **argv); + #ifdef __cplusplus } #endif diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 83d0ec1..7a8b1e3 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -610,6 +610,7 @@ asmlinkage long sys_signalfd(int ufd, sigset_t __user *user_mask, size_t sizemas asmlinkage long sys_timerfd(int ufd, int clockid, int flags, const struct itimerspec __user *utmr); asmlinkage long sys_eventfd(unsigned int count); +asmlinkage long sys_fallocate(int fd, int mode, loff_t offset, loff_t len); int kernel_execve(const char *filename, char *const argv[], char *const envp[]); diff --git a/include/linux/vmalloc.h b/include/linux/vmalloc.h index 132b260..c2b10ca 100644 --- a/include/linux/vmalloc.h +++ b/include/linux/vmalloc.h @@ -70,6 +70,10 @@ extern int map_vm_area(struct vm_struct *area, pgprot_t prot, struct page ***pages); extern void unmap_kernel_range(unsigned long addr, unsigned long size); +/* Allocate/destroy a 'vmalloc' VM area. */ +extern struct vm_struct *alloc_vm_area(size_t size); +extern void free_vm_area(struct vm_struct *area); + /* * Internals. Dont't use.. */ diff --git a/include/media/saa7146.h b/include/media/saa7146.h index d3f4f5a..6770324 100644 --- a/include/media/saa7146.h +++ b/include/media/saa7146.h @@ -114,7 +114,7 @@ struct saa7146_dev struct mutex lock; unsigned char __iomem *mem; /* pointer to mapped IO memory */ - int revision; /* chip revision; needed for bug-workarounds*/ + u32 revision; /* chip revision; needed for bug-workarounds*/ /* pci-device & irq stuff*/ char name[32]; @@ -157,8 +157,8 @@ struct saa7146_format* format_by_fourcc(struct saa7146_dev *dev, int fourcc); int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt); 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_vfree_destroy_pgtable(struct pci_dev *pci, char *mem, struct saa7146_pgtable *pt); +void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt); +void saa7146_vfree_destroy_pgtable(struct pci_dev *pci, void *mem, 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 nobusyloop); diff --git a/include/media/tuner.h b/include/media/tuner.h index 6dcf3c4..160381c 100644 --- a/include/media/tuner.h +++ b/include/media/tuner.h @@ -23,8 +23,6 @@ #define _TUNER_H #include <linux/videodev2.h> -#include <linux/i2c.h> -#include <media/tuner-types.h> extern int tuner_debug; @@ -124,6 +122,7 @@ extern int tuner_debug; #define TUNER_THOMSON_FE6600 72 /* DViCO FusionHDTV DVB-T Hybrid */ #define TUNER_SAMSUNG_TCPG_6121P30A 73 /* Hauppauge PVR-500 PAL */ #define TUNER_TDA9887 74 /* This tuner should be used only internally */ +#define TUNER_TEA5761 75 /* Only FM Radio Tuner */ /* tv card specific */ #define TDA9887_PRESENT (1<<0) @@ -182,74 +181,6 @@ struct tuner_setup { int (*tuner_callback) (void *dev, int command,int arg); }; -struct tuner { - /* device */ - struct i2c_client i2c; - - unsigned int type; /* chip type */ - - unsigned int mode; - unsigned int mode_mask; /* Combination of allowable modes */ - - unsigned int tv_freq; /* keep track of the current settings */ - unsigned int radio_freq; - u16 last_div; - unsigned int audmode; - v4l2_std_id std; - - int using_v4l2; - - /* used by tda9887 */ - unsigned int tda9887_config; - unsigned char tda9887_data[4]; - - /* used by MT2032 */ - unsigned int xogc; - unsigned int radio_if2; - - /* used by tda8290 */ - unsigned char tda8290_easy_mode; - unsigned char tda827x_lpsel; - unsigned char tda827x_addr; - unsigned char tda827x_ver; - unsigned int sgIF; - - unsigned int config; - int (*tuner_callback) (void *dev, int command,int arg); - - /* function ptrs */ - void (*set_tv_freq)(struct i2c_client *c, unsigned int freq); - void (*set_radio_freq)(struct i2c_client *c, unsigned int freq); - int (*has_signal)(struct i2c_client *c); - int (*is_stereo)(struct i2c_client *c); - int (*get_afc)(struct i2c_client *c); - void (*tuner_status)(struct i2c_client *c); - void (*standby)(struct i2c_client *c); -}; - -extern unsigned const int tuner_count; - -extern int microtune_init(struct i2c_client *c); -extern int xc3028_init(struct i2c_client *c); -extern int tda8290_init(struct i2c_client *c); -extern int tda8290_probe(struct i2c_client *c); -extern int tea5767_tuner_init(struct i2c_client *c); -extern int default_tuner_init(struct i2c_client *c); -extern int tea5767_autodetection(struct i2c_client *c); -extern int tda9887_tuner_init(struct i2c_client *c); - -#define tuner_warn(fmt, arg...) do {\ - printk(KERN_WARNING "%s %d-%04x: " fmt, t->i2c.driver->driver.name, \ - i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0) -#define tuner_info(fmt, arg...) do {\ - printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.driver->driver.name, \ - i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0) -#define tuner_dbg(fmt, arg...) do {\ - extern int tuner_debug; \ - if (tuner_debug) \ - printk(KERN_DEBUG "%s %d-%04x: " fmt, t->i2c.driver->driver.name, \ - i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0) - #endif /* __KERNEL__ */ #endif /* _TUNER_H */ diff --git a/include/mtd/ubi-header.h b/include/mtd/ubi-header.h index fa479c7..74efa77 100644 --- a/include/mtd/ubi-header.h +++ b/include/mtd/ubi-header.h @@ -74,42 +74,13 @@ enum { UBI_COMPAT_REJECT = 5 }; -/* - * ubi16_t/ubi32_t/ubi64_t - 16, 32, and 64-bit integers used in UBI on-flash - * data structures. - */ -typedef struct { - uint16_t int16; -} __attribute__ ((packed)) ubi16_t; - -typedef struct { - uint32_t int32; -} __attribute__ ((packed)) ubi32_t; - -typedef struct { - uint64_t int64; -} __attribute__ ((packed)) ubi64_t; - -/* - * In this implementation of UBI uses the big-endian format for on-flash - * integers. The below are the corresponding conversion macros. - */ -#define cpu_to_ubi16(x) ((ubi16_t){__cpu_to_be16(x)}) -#define ubi16_to_cpu(x) ((uint16_t)__be16_to_cpu((x).int16)) - -#define cpu_to_ubi32(x) ((ubi32_t){__cpu_to_be32(x)}) -#define ubi32_to_cpu(x) ((uint32_t)__be32_to_cpu((x).int32)) - -#define cpu_to_ubi64(x) ((ubi64_t){__cpu_to_be64(x)}) -#define ubi64_to_cpu(x) ((uint64_t)__be64_to_cpu((x).int64)) - /* Sizes of UBI headers */ #define UBI_EC_HDR_SIZE sizeof(struct ubi_ec_hdr) #define UBI_VID_HDR_SIZE sizeof(struct ubi_vid_hdr) /* Sizes of UBI headers without the ending CRC */ -#define UBI_EC_HDR_SIZE_CRC (UBI_EC_HDR_SIZE - sizeof(ubi32_t)) -#define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(ubi32_t)) +#define UBI_EC_HDR_SIZE_CRC (UBI_EC_HDR_SIZE - sizeof(__be32)) +#define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(__be32)) /** * struct ubi_ec_hdr - UBI erase counter header. @@ -137,14 +108,14 @@ typedef struct { * eraseblocks. */ struct ubi_ec_hdr { - ubi32_t magic; - uint8_t version; - uint8_t padding1[3]; - ubi64_t ec; /* Warning: the current limit is 31-bit anyway! */ - ubi32_t vid_hdr_offset; - ubi32_t data_offset; - uint8_t padding2[36]; - ubi32_t hdr_crc; + __be32 magic; + __u8 version; + __u8 padding1[3]; + __be64 ec; /* Warning: the current limit is 31-bit anyway! */ + __be32 vid_hdr_offset; + __be32 data_offset; + __u8 padding2[36]; + __be32 hdr_crc; } __attribute__ ((packed)); /** @@ -262,22 +233,22 @@ struct ubi_ec_hdr { * software (say, cramfs) on top of the UBI volume. */ struct ubi_vid_hdr { - ubi32_t magic; - uint8_t version; - uint8_t vol_type; - uint8_t copy_flag; - uint8_t compat; - ubi32_t vol_id; - ubi32_t lnum; - ubi32_t leb_ver; /* obsolete, to be removed, don't use */ - ubi32_t data_size; - ubi32_t used_ebs; - ubi32_t data_pad; - ubi32_t data_crc; - uint8_t padding1[4]; - ubi64_t sqnum; - uint8_t padding2[12]; - ubi32_t hdr_crc; + __be32 magic; + __u8 version; + __u8 vol_type; + __u8 copy_flag; + __u8 compat; + __be32 vol_id; + __be32 lnum; + __be32 leb_ver; /* obsolete, to be removed, don't use */ + __be32 data_size; + __be32 used_ebs; + __be32 data_pad; + __be32 data_crc; + __u8 padding1[4]; + __be64 sqnum; + __u8 padding2[12]; + __be32 hdr_crc; } __attribute__ ((packed)); /* Internal UBI volumes count */ @@ -306,7 +277,7 @@ struct ubi_vid_hdr { #define UBI_VTBL_RECORD_SIZE sizeof(struct ubi_vtbl_record) /* Size of the volume table record without the ending CRC */ -#define UBI_VTBL_RECORD_SIZE_CRC (UBI_VTBL_RECORD_SIZE - sizeof(ubi32_t)) +#define UBI_VTBL_RECORD_SIZE_CRC (UBI_VTBL_RECORD_SIZE - sizeof(__be32)) /** * struct ubi_vtbl_record - a record in the volume table. @@ -346,15 +317,15 @@ struct ubi_vid_hdr { * Empty records contain all zeroes and the CRC checksum of those zeroes. */ struct ubi_vtbl_record { - ubi32_t reserved_pebs; - ubi32_t alignment; - ubi32_t data_pad; - uint8_t vol_type; - uint8_t upd_marker; - ubi16_t name_len; - uint8_t name[UBI_VOL_NAME_MAX+1]; - uint8_t padding2[24]; - ubi32_t crc; + __be32 reserved_pebs; + __be32 alignment; + __be32 data_pad; + __u8 vol_type; + __u8 upd_marker; + __be16 name_len; + __u8 name[UBI_VOL_NAME_MAX+1]; + __u8 padding2[24]; + __be32 crc; } __attribute__ ((packed)); #endif /* !__UBI_HEADER_H__ */ diff --git a/include/net/tcp.h b/include/net/tcp.h index a8af9ae..8b404b1 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -652,8 +652,7 @@ struct tcp_congestion_ops { /* lower bound for congestion window (optional) */ u32 (*min_cwnd)(const struct sock *sk); /* do new cwnd calculation (required) */ - void (*cong_avoid)(struct sock *sk, u32 ack, - u32 rtt, u32 in_flight, int good_ack); + void (*cong_avoid)(struct sock *sk, u32 ack, u32 in_flight, int good_ack); /* call before changing ca_state (optional) */ void (*set_state)(struct sock *sk, u8 new_state); /* call when cwnd event occurs (optional) */ @@ -684,8 +683,7 @@ extern void tcp_slow_start(struct tcp_sock *tp); extern struct tcp_congestion_ops tcp_init_congestion_ops; extern u32 tcp_reno_ssthresh(struct sock *sk); -extern void tcp_reno_cong_avoid(struct sock *sk, u32 ack, - u32 rtt, u32 in_flight, int flag); +extern void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 in_flight, int flag); extern u32 tcp_reno_min_cwnd(const struct sock *sk); extern struct tcp_congestion_ops tcp_reno; diff --git a/include/net/xfrm.h b/include/net/xfrm.h index ae959e9..a5f80bf 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -585,7 +585,6 @@ static inline int xfrm_sec_ctx_match(struct xfrm_sec_ctx *s1, struct xfrm_sec_ct struct xfrm_dst { union { - struct xfrm_dst *next; struct dst_entry dst; struct rtable rt; struct rt6_info rt6; diff --git a/include/xen/events.h b/include/xen/events.h new file mode 100644 index 0000000..2bde54d --- /dev/null +++ b/include/xen/events.h @@ -0,0 +1,48 @@ +#ifndef _XEN_EVENTS_H +#define _XEN_EVENTS_H + +#include <linux/interrupt.h> + +#include <xen/interface/event_channel.h> +#include <asm/xen/hypercall.h> + +enum ipi_vector { + XEN_RESCHEDULE_VECTOR, + XEN_CALL_FUNCTION_VECTOR, + + XEN_NR_IPIS, +}; + +int bind_evtchn_to_irq(unsigned int evtchn); +int bind_evtchn_to_irqhandler(unsigned int evtchn, + irq_handler_t handler, + unsigned long irqflags, const char *devname, + void *dev_id); +int bind_virq_to_irqhandler(unsigned int virq, unsigned int cpu, + irq_handler_t handler, + unsigned long irqflags, const char *devname, + void *dev_id); +int bind_ipi_to_irqhandler(enum ipi_vector ipi, + unsigned int cpu, + irq_handler_t handler, + unsigned long irqflags, + const char *devname, + void *dev_id); + +/* + * Common unbind function for all event sources. Takes IRQ to unbind from. + * Automatically closes the underlying event channel (even for bindings + * made with bind_evtchn_to_irqhandler()). + */ +void unbind_from_irqhandler(unsigned int irq, void *dev_id); + +void xen_send_IPI_one(unsigned int cpu, enum ipi_vector vector); + +static inline void notify_remote_via_evtchn(int port) +{ + struct evtchn_send send = { .port = port }; + (void)HYPERVISOR_event_channel_op(EVTCHNOP_send, &send); +} + +extern void notify_remote_via_irq(int irq); +#endif /* _XEN_EVENTS_H */ diff --git a/include/xen/features.h b/include/xen/features.h new file mode 100644 index 0000000..27292d4 --- /dev/null +++ b/include/xen/features.h @@ -0,0 +1,23 @@ +/****************************************************************************** + * features.h + * + * Query the features reported by Xen. + * + * Copyright (c) 2006, Ian Campbell + */ + +#ifndef __XEN_FEATURES_H__ +#define __XEN_FEATURES_H__ + +#include <xen/interface/features.h> + +void xen_setup_features(void); + +extern u8 xen_features[XENFEAT_NR_SUBMAPS * 32]; + +static inline int xen_feature(int flag) +{ + return xen_features[flag]; +} + +#endif /* __ASM_XEN_FEATURES_H__ */ diff --git a/include/xen/grant_table.h b/include/xen/grant_table.h new file mode 100644 index 0000000..761c834 --- /dev/null +++ b/include/xen/grant_table.h @@ -0,0 +1,107 @@ +/****************************************************************************** + * grant_table.h + * + * Two sets of functionality: + * 1. Granting foreign access to our memory reservation. + * 2. Accessing others' memory reservations via grant references. + * (i.e., mechanisms for both sender and recipient of grant references) + * + * Copyright (c) 2004-2005, K A Fraser + * Copyright (c) 2005, Christopher Clark + * + * 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; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef __ASM_GNTTAB_H__ +#define __ASM_GNTTAB_H__ + +#include <asm/xen/hypervisor.h> +#include <xen/interface/grant_table.h> + +/* NR_GRANT_FRAMES must be less than or equal to that configured in Xen */ +#define NR_GRANT_FRAMES 4 + +struct gnttab_free_callback { + struct gnttab_free_callback *next; + void (*fn)(void *); + void *arg; + u16 count; +}; + +int gnttab_grant_foreign_access(domid_t domid, unsigned long frame, + int readonly); + +/* + * End access through the given grant reference, iff the grant entry is no + * longer in use. Return 1 if the grant entry was freed, 0 if it is still in + * use. + */ +int gnttab_end_foreign_access_ref(grant_ref_t ref, int readonly); + +/* + * Eventually end access through the given grant reference, and once that + * access has been ended, free the given page too. Access will be ended + * immediately iff the grant entry is not in use, otherwise it will happen + * some time later. page may be 0, in which case no freeing will occur. + */ +void gnttab_end_foreign_access(grant_ref_t ref, int readonly, + unsigned long page); + +int gnttab_grant_foreign_transfer(domid_t domid, unsigned long pfn); + +unsigned long gnttab_end_foreign_transfer_ref(grant_ref_t ref); +unsigned long gnttab_end_foreign_transfer(grant_ref_t ref); + +int gnttab_query_foreign_access(grant_ref_t ref); + +/* + * operations on reserved batches of grant references + */ +int gnttab_alloc_grant_references(u16 count, grant_ref_t *pprivate_head); + +void gnttab_free_grant_reference(grant_ref_t ref); + +void gnttab_free_grant_references(grant_ref_t head); + +int gnttab_empty_grant_references(const grant_ref_t *pprivate_head); + +int gnttab_claim_grant_reference(grant_ref_t *pprivate_head); + +void gnttab_release_grant_reference(grant_ref_t *private_head, + grant_ref_t release); + +void gnttab_request_free_callback(struct gnttab_free_callback *callback, + void (*fn)(void *), void *arg, u16 count); +void gnttab_cancel_free_callback(struct gnttab_free_callback *callback); + +void gnttab_grant_foreign_access_ref(grant_ref_t ref, domid_t domid, + unsigned long frame, int readonly); + +void gnttab_grant_foreign_transfer_ref(grant_ref_t, domid_t domid, + unsigned long pfn); + +#define gnttab_map_vaddr(map) ((void *)(map.host_virt_addr)) + +#endif /* __ASM_GNTTAB_H__ */ diff --git a/include/xen/hvc-console.h b/include/xen/hvc-console.h new file mode 100644 index 0000000..21c0ecf --- /dev/null +++ b/include/xen/hvc-console.h @@ -0,0 +1,6 @@ +#ifndef XEN_HVC_CONSOLE_H +#define XEN_HVC_CONSOLE_H + +extern struct console xenboot_console; + +#endif /* XEN_HVC_CONSOLE_H */ diff --git a/include/xen/interface/elfnote.h b/include/xen/interface/elfnote.h new file mode 100644 index 0000000..a64d3df --- /dev/null +++ b/include/xen/interface/elfnote.h @@ -0,0 +1,133 @@ +/****************************************************************************** + * elfnote.h + * + * Definitions used for the Xen ELF notes. + * + * Copyright (c) 2006, Ian Campbell, XenSource Ltd. + */ + +#ifndef __XEN_PUBLIC_ELFNOTE_H__ +#define __XEN_PUBLIC_ELFNOTE_H__ + +/* + * The notes should live in a SHT_NOTE segment and have "Xen" in the + * name field. + * + * Numeric types are either 4 or 8 bytes depending on the content of + * the desc field. + * + * LEGACY indicated the fields in the legacy __xen_guest string which + * this a note type replaces. + */ + +/* + * NAME=VALUE pair (string). + * + * LEGACY: FEATURES and PAE + */ +#define XEN_ELFNOTE_INFO 0 + +/* + * The virtual address of the entry point (numeric). + * + * LEGACY: VIRT_ENTRY + */ +#define XEN_ELFNOTE_ENTRY 1 + +/* The virtual address of the hypercall transfer page (numeric). + * + * LEGACY: HYPERCALL_PAGE. (n.b. legacy value is a physical page + * number not a virtual address) + */ +#define XEN_ELFNOTE_HYPERCALL_PAGE 2 + +/* The virtual address where the kernel image should be mapped (numeric). + * + * Defaults to 0. + * + * LEGACY: VIRT_BASE + */ +#define XEN_ELFNOTE_VIRT_BASE 3 + +/* + * The offset of the ELF paddr field from the acutal required + * psuedo-physical address (numeric). + * + * This is used to maintain backwards compatibility with older kernels + * which wrote __PAGE_OFFSET into that field. This field defaults to 0 + * if not present. + * + * LEGACY: ELF_PADDR_OFFSET. (n.b. legacy default is VIRT_BASE) + */ +#define XEN_ELFNOTE_PADDR_OFFSET 4 + +/* + * The version of Xen that we work with (string). + * + * LEGACY: XEN_VER + */ +#define XEN_ELFNOTE_XEN_VERSION 5 + +/* + * The name of the guest operating system (string). + * + * LEGACY: GUEST_OS + */ +#define XEN_ELFNOTE_GUEST_OS 6 + +/* + * The version of the guest operating system (string). + * + * LEGACY: GUEST_VER + */ +#define XEN_ELFNOTE_GUEST_VERSION 7 + +/* + * The loader type (string). + * + * LEGACY: LOADER + */ +#define XEN_ELFNOTE_LOADER 8 + +/* + * The kernel supports PAE (x86/32 only, string = "yes" or "no"). + * + * LEGACY: PAE (n.b. The legacy interface included a provision to + * indicate 'extended-cr3' support allowing L3 page tables to be + * placed above 4G. It is assumed that any kernel new enough to use + * these ELF notes will include this and therefore "yes" here is + * equivalent to "yes[entended-cr3]" in the __xen_guest interface. + */ +#define XEN_ELFNOTE_PAE_MODE 9 + +/* + * The features supported/required by this kernel (string). + * + * The string must consist of a list of feature names (as given in + * features.h, without the "XENFEAT_" prefix) separated by '|' + * characters. If a feature is required for the kernel to function + * then the feature name must be preceded by a '!' character. + * + * LEGACY: FEATURES + */ +#define XEN_ELFNOTE_FEATURES 10 + +/* + * The kernel requires the symbol table to be loaded (string = "yes" or "no") + * LEGACY: BSD_SYMTAB (n.b. The legacy treated the presence or absence + * of this string as a boolean flag rather than requiring "yes" or + * "no". + */ +#define XEN_ELFNOTE_BSD_SYMTAB 11 + +#endif /* __XEN_PUBLIC_ELFNOTE_H__ */ + +/* + * Local variables: + * mode: C + * c-set-style: "BSD" + * c-basic-offset: 4 + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/include/xen/interface/event_channel.h b/include/xen/interface/event_channel.h new file mode 100644 index 0000000..919b5bd --- /dev/null +++ b/include/xen/interface/event_channel.h @@ -0,0 +1,195 @@ +/****************************************************************************** + * event_channel.h + * + * Event channels between domains. + * + * Copyright (c) 2003-2004, K A Fraser. + */ + +#ifndef __XEN_PUBLIC_EVENT_CHANNEL_H__ +#define __XEN_PUBLIC_EVENT_CHANNEL_H__ + +typedef uint32_t evtchn_port_t; +DEFINE_GUEST_HANDLE(evtchn_port_t); + +/* + * EVTCHNOP_alloc_unbound: Allocate a port in domain <dom> and mark as + * accepting interdomain bindings from domain <remote_dom>. A fresh port + * is allocated in <dom> and returned as <port>. + * NOTES: + * 1. If the caller is unprivileged then <dom> must be DOMID_SELF. + * 2. <rdom> may be DOMID_SELF, allowing loopback connections. + */ +#define EVTCHNOP_alloc_unbound 6 +struct evtchn_alloc_unbound { + /* IN parameters */ + domid_t dom, remote_dom; + /* OUT parameters */ + evtchn_port_t port; +}; + +/* + * EVTCHNOP_bind_interdomain: Construct an interdomain event channel between + * the calling domain and <remote_dom>. <remote_dom,remote_port> must identify + * a port that is unbound and marked as accepting bindings from the calling + * domain. A fresh port is allocated in the calling domain and returned as + * <local_port>. + * NOTES: + * 2. <remote_dom> may be DOMID_SELF, allowing loopback connections. + */ +#define EVTCHNOP_bind_interdomain 0 +struct evtchn_bind_interdomain { + /* IN parameters. */ + domid_t remote_dom; + evtchn_port_t remote_port; + /* OUT parameters. */ + evtchn_port_t local_port; +}; + +/* + * EVTCHNOP_bind_virq: Bind a local event channel to VIRQ <irq> on specified + * vcpu. + * NOTES: + * 1. A virtual IRQ may be bound to at most one event channel per vcpu. + * 2. The allocated event channel is bound to the specified vcpu. The binding + * may not be changed. + */ +#define EVTCHNOP_bind_virq 1 +struct evtchn_bind_virq { + /* IN parameters. */ + uint32_t virq; + uint32_t vcpu; + /* OUT parameters. */ + evtchn_port_t port; +}; + +/* + * EVTCHNOP_bind_pirq: Bind a local event channel to PIRQ <irq>. + * NOTES: + * 1. A physical IRQ may be bound to at most one event channel per domain. + * 2. Only a sufficiently-privileged domain may bind to a physical IRQ. + */ +#define EVTCHNOP_bind_pirq 2 +struct evtchn_bind_pirq { + /* IN parameters. */ + uint32_t pirq; +#define BIND_PIRQ__WILL_SHARE 1 + uint32_t flags; /* BIND_PIRQ__* */ + /* OUT parameters. */ + evtchn_port_t port; +}; + +/* + * EVTCHNOP_bind_ipi: Bind a local event channel to receive events. + * NOTES: + * 1. The allocated event channel is bound to the specified vcpu. The binding + * may not be changed. + */ +#define EVTCHNOP_bind_ipi 7 +struct evtchn_bind_ipi { + uint32_t vcpu; + /* OUT parameters. */ + evtchn_port_t port; +}; + +/* + * EVTCHNOP_close: Close a local event channel <port>. If the channel is + * interdomain then the remote end is placed in the unbound state + * (EVTCHNSTAT_unbound), awaiting a new connection. + */ +#define EVTCHNOP_close 3 +struct evtchn_close { + /* IN parameters. */ + evtchn_port_t port; +}; + +/* + * EVTCHNOP_send: Send an event to the remote end of the channel whose local + * endpoint is <port>. + */ +#define EVTCHNOP_send 4 +struct evtchn_send { + /* IN parameters. */ + evtchn_port_t port; +}; + +/* + * EVTCHNOP_status: Get the current status of the communication channel which + * has an endpoint at <dom, port>. + * NOTES: + * 1. <dom> may be specified as DOMID_SELF. + * 2. Only a sufficiently-privileged domain may obtain the status of an event + * channel for which <dom> is not DOMID_SELF. + */ +#define EVTCHNOP_status 5 +struct evtchn_status { + /* IN parameters */ + domid_t dom; + evtchn_port_t port; + /* OUT parameters */ +#define EVTCHNSTAT_closed 0 /* Channel is not in use. */ +#define EVTCHNSTAT_unbound 1 /* Channel is waiting interdom connection.*/ +#define EVTCHNSTAT_interdomain 2 /* Channel is connected to remote domain. */ +#define EVTCHNSTAT_pirq 3 /* Channel is bound to a phys IRQ line. */ +#define EVTCHNSTAT_virq 4 /* Channel is bound to a virtual IRQ line */ +#define EVTCHNSTAT_ipi 5 /* Channel is bound to a virtual IPI line */ + uint32_t status; + uint32_t vcpu; /* VCPU to which this channel is bound. */ + union { + struct { + domid_t dom; + } unbound; /* EVTCHNSTAT_unbound */ + struct { + domid_t dom; + evtchn_port_t port; + } interdomain; /* EVTCHNSTAT_interdomain */ + uint32_t pirq; /* EVTCHNSTAT_pirq */ + uint32_t virq; /* EVTCHNSTAT_virq */ + } u; +}; + +/* + * EVTCHNOP_bind_vcpu: Specify which vcpu a channel should notify when an + * event is pending. + * NOTES: + * 1. IPI- and VIRQ-bound channels always notify the vcpu that initialised + * the binding. This binding cannot be changed. + * 2. All other channels notify vcpu0 by default. This default is set when + * the channel is allocated (a port that is freed and subsequently reused + * has its binding reset to vcpu0). + */ +#define EVTCHNOP_bind_vcpu 8 +struct evtchn_bind_vcpu { + /* IN parameters. */ + evtchn_port_t port; + uint32_t vcpu; +}; + +/* + * EVTCHNOP_unmask: Unmask the specified local event-channel port and deliver + * a notification to the appropriate VCPU if an event is pending. + */ +#define EVTCHNOP_unmask 9 +struct evtchn_unmask { + /* IN parameters. */ + evtchn_port_t port; +}; + +struct evtchn_op { + uint32_t cmd; /* EVTCHNOP_* */ + union { + struct evtchn_alloc_unbound alloc_unbound; + struct evtchn_bind_interdomain bind_interdomain; + struct evtchn_bind_virq bind_virq; + struct evtchn_bind_pirq bind_pirq; + struct evtchn_bind_ipi bind_ipi; + struct evtchn_close close; + struct evtchn_send send; + struct evtchn_status status; + struct evtchn_bind_vcpu bind_vcpu; + struct evtchn_unmask unmask; + } u; +}; +DEFINE_GUEST_HANDLE_STRUCT(evtchn_op); + +#endif /* __XEN_PUBLIC_EVENT_CHANNEL_H__ */ diff --git a/include/xen/interface/features.h b/include/xen/interface/features.h new file mode 100644 index 0000000..d73228d --- /dev/null +++ b/include/xen/interface/features.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * features.h + * + * Feature flags, reported by XENVER_get_features. + * + * Copyright (c) 2006, Keir Fraser <keir@xensource.com> + */ + +#ifndef __XEN_PUBLIC_FEATURES_H__ +#define __XEN_PUBLIC_FEATURES_H__ + +/* + * If set, the guest does not need to write-protect its pagetables, and can + * update them via direct writes. + */ +#define XENFEAT_writable_page_tables 0 + +/* + * If set, the guest does not need to write-protect its segment descriptor + * tables, and can update them via direct writes. + */ +#define XENFEAT_writable_descriptor_tables 1 + +/* + * If set, translation between the guest's 'pseudo-physical' address space + * and the host's machine address space are handled by the hypervisor. In this + * mode the guest does not need to perform phys-to/from-machine translations + * when performing page table operations. + */ +#define XENFEAT_auto_translated_physmap 2 + +/* If set, the guest is running in supervisor mode (e.g., x86 ring 0). */ +#define XENFEAT_supervisor_mode_kernel 3 + +/* + * If set, the guest does not need to allocate x86 PAE page directories + * below 4GB. This flag is usually implied by auto_translated_physmap. + */ +#define XENFEAT_pae_pgdir_above_4gb 4 + +#define XENFEAT_NR_SUBMAPS 1 + +#endif /* __XEN_PUBLIC_FEATURES_H__ */ diff --git a/include/xen/interface/grant_table.h b/include/xen/interface/grant_table.h new file mode 100644 index 0000000..2190498 --- /dev/null +++ b/include/xen/interface/grant_table.h @@ -0,0 +1,375 @@ +/****************************************************************************** + * grant_table.h + * + * Interface for granting foreign access to page frames, and receiving + * page-ownership transfers. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2004, K A Fraser + */ + +#ifndef __XEN_PUBLIC_GRANT_TABLE_H__ +#define __XEN_PUBLIC_GRANT_TABLE_H__ + + +/*********************************** + * GRANT TABLE REPRESENTATION + */ + +/* Some rough guidelines on accessing and updating grant-table entries + * in a concurrency-safe manner. For more information, Linux contains a + * reference implementation for guest OSes (arch/xen/kernel/grant_table.c). + * + * NB. WMB is a no-op on current-generation x86 processors. However, a + * compiler barrier will still be required. + * + * Introducing a valid entry into the grant table: + * 1. Write ent->domid. + * 2. Write ent->frame: + * GTF_permit_access: Frame to which access is permitted. + * GTF_accept_transfer: Pseudo-phys frame slot being filled by new + * frame, or zero if none. + * 3. Write memory barrier (WMB). + * 4. Write ent->flags, inc. valid type. + * + * Invalidating an unused GTF_permit_access entry: + * 1. flags = ent->flags. + * 2. Observe that !(flags & (GTF_reading|GTF_writing)). + * 3. Check result of SMP-safe CMPXCHG(&ent->flags, flags, 0). + * NB. No need for WMB as reuse of entry is control-dependent on success of + * step 3, and all architectures guarantee ordering of ctrl-dep writes. + * + * Invalidating an in-use GTF_permit_access entry: + * This cannot be done directly. Request assistance from the domain controller + * which can set a timeout on the use of a grant entry and take necessary + * action. (NB. This is not yet implemented!). + * + * Invalidating an unused GTF_accept_transfer entry: + * 1. flags = ent->flags. + * 2. Observe that !(flags & GTF_transfer_committed). [*] + * 3. Check result of SMP-safe CMPXCHG(&ent->flags, flags, 0). + * NB. No need for WMB as reuse of entry is control-dependent on success of + * step 3, and all architectures guarantee ordering of ctrl-dep writes. + * [*] If GTF_transfer_committed is set then the grant entry is 'committed'. + * The guest must /not/ modify the grant entry until the address of the + * transferred frame is written. It is safe for the guest to spin waiting + * for this to occur (detect by observing GTF_transfer_completed in + * ent->flags). + * + * Invalidating a committed GTF_accept_transfer entry: + * 1. Wait for (ent->flags & GTF_transfer_completed). + * + * Changing a GTF_permit_access from writable to read-only: + * Use SMP-safe CMPXCHG to set GTF_readonly, while checking !GTF_writing. + * + * Changing a GTF_permit_access from read-only to writable: + * Use SMP-safe bit-setting instruction. + */ + +/* + * A grant table comprises a packed array of grant entries in one or more + * page frames shared between Xen and a guest. + * [XEN]: This field is written by Xen and read by the sharing guest. + * [GST]: This field is written by the guest and read by Xen. + */ +struct grant_entry { + /* GTF_xxx: various type and flag information. [XEN,GST] */ + uint16_t flags; + /* The domain being granted foreign privileges. [GST] */ + domid_t domid; + /* + * GTF_permit_access: Frame that @domid is allowed to map and access. [GST] + * GTF_accept_transfer: Frame whose ownership transferred by @domid. [XEN] + */ + uint32_t frame; +}; + +/* + * Type of grant entry. + * GTF_invalid: This grant entry grants no privileges. + * GTF_permit_access: Allow @domid to map/access @frame. + * GTF_accept_transfer: Allow @domid to transfer ownership of one page frame + * to this guest. Xen writes the page number to @frame. + */ +#define GTF_invalid (0U<<0) +#define GTF_permit_access (1U<<0) +#define GTF_accept_transfer (2U<<0) +#define GTF_type_mask (3U<<0) + +/* + * Subflags for GTF_permit_access. + * GTF_readonly: Restrict @domid to read-only mappings and accesses. [GST] + * GTF_reading: Grant entry is currently mapped for reading by @domid. [XEN] + * GTF_writing: Grant entry is currently mapped for writing by @domid. [XEN] + */ +#define _GTF_readonly (2) +#define GTF_readonly (1U<<_GTF_readonly) +#define _GTF_reading (3) +#define GTF_reading (1U<<_GTF_reading) +#define _GTF_writing (4) +#define GTF_writing (1U<<_GTF_writing) + +/* + * Subflags for GTF_accept_transfer: + * GTF_transfer_committed: Xen sets this flag to indicate that it is committed + * to transferring ownership of a page frame. When a guest sees this flag + * it must /not/ modify the grant entry until GTF_transfer_completed is + * set by Xen. + * GTF_transfer_completed: It is safe for the guest to spin-wait on this flag + * after reading GTF_transfer_committed. Xen will always write the frame + * address, followed by ORing this flag, in a timely manner. + */ +#define _GTF_transfer_committed (2) +#define GTF_transfer_committed (1U<<_GTF_transfer_committed) +#define _GTF_transfer_completed (3) +#define GTF_transfer_completed (1U<<_GTF_transfer_completed) + + +/*********************************** + * GRANT TABLE QUERIES AND USES + */ + +/* + * Reference to a grant entry in a specified domain's grant table. + */ +typedef uint32_t grant_ref_t; + +/* + * Handle to track a mapping created via a grant reference. + */ +typedef uint32_t grant_handle_t; + +/* + * GNTTABOP_map_grant_ref: Map the grant entry (<dom>,<ref>) for access + * by devices and/or host CPUs. If successful, <handle> is a tracking number + * that must be presented later to destroy the mapping(s). On error, <handle> + * is a negative status code. + * NOTES: + * 1. If GNTMAP_device_map is specified then <dev_bus_addr> is the address + * via which I/O devices may access the granted frame. + * 2. If GNTMAP_host_map is specified then a mapping will be added at + * either a host virtual address in the current address space, or at + * a PTE at the specified machine address. The type of mapping to + * perform is selected through the GNTMAP_contains_pte flag, and the + * address is specified in <host_addr>. + * 3. Mappings should only be destroyed via GNTTABOP_unmap_grant_ref. If a + * host mapping is destroyed by other means then it is *NOT* guaranteed + * to be accounted to the correct grant reference! + */ +#define GNTTABOP_map_grant_ref 0 +struct gnttab_map_grant_ref { + /* IN parameters. */ + uint64_t host_addr; + uint32_t flags; /* GNTMAP_* */ + grant_ref_t ref; + domid_t dom; + /* OUT parameters. */ + int16_t status; /* GNTST_* */ + grant_handle_t handle; + uint64_t dev_bus_addr; +}; + +/* + * GNTTABOP_unmap_grant_ref: Destroy one or more grant-reference mappings + * tracked by <handle>. If <host_addr> or <dev_bus_addr> is zero, that + * field is ignored. If non-zero, they must refer to a device/host mapping + * that is tracked by <handle> + * NOTES: + * 1. The call may fail in an undefined manner if either mapping is not + * tracked by <handle>. + * 3. After executing a batch of unmaps, it is guaranteed that no stale + * mappings will remain in the device or host TLBs. + */ +#define GNTTABOP_unmap_grant_ref 1 +struct gnttab_unmap_grant_ref { + /* IN parameters. */ + uint64_t host_addr; + uint64_t dev_bus_addr; + grant_handle_t handle; + /* OUT parameters. */ + int16_t status; /* GNTST_* */ +}; + +/* + * GNTTABOP_setup_table: Set up a grant table for <dom> comprising at least + * <nr_frames> pages. The frame addresses are written to the <frame_list>. + * Only <nr_frames> addresses are written, even if the table is larger. + * NOTES: + * 1. <dom> may be specified as DOMID_SELF. + * 2. Only a sufficiently-privileged domain may specify <dom> != DOMID_SELF. + * 3. Xen may not support more than a single grant-table page per domain. + */ +#define GNTTABOP_setup_table 2 +struct gnttab_setup_table { + /* IN parameters. */ + domid_t dom; + uint32_t nr_frames; + /* OUT parameters. */ + int16_t status; /* GNTST_* */ + ulong *frame_list; +}; + +/* + * GNTTABOP_dump_table: Dump the contents of the grant table to the + * xen console. Debugging use only. + */ +#define GNTTABOP_dump_table 3 +struct gnttab_dump_table { + /* IN parameters. */ + domid_t dom; + /* OUT parameters. */ + int16_t status; /* GNTST_* */ +}; + +/* + * GNTTABOP_transfer_grant_ref: Transfer <frame> to a foreign domain. The + * foreign domain has previously registered its interest in the transfer via + * <domid, ref>. + * + * Note that, even if the transfer fails, the specified page no longer belongs + * to the calling domain *unless* the error is GNTST_bad_page. + */ +#define GNTTABOP_transfer 4 +struct gnttab_transfer { + /* IN parameters. */ + unsigned long mfn; + domid_t domid; + grant_ref_t ref; + /* OUT parameters. */ + int16_t status; +}; + + +/* + * GNTTABOP_copy: Hypervisor based copy + * source and destinations can be eithers MFNs or, for foreign domains, + * grant references. the foreign domain has to grant read/write access + * in its grant table. + * + * The flags specify what type source and destinations are (either MFN + * or grant reference). + * + * Note that this can also be used to copy data between two domains + * via a third party if the source and destination domains had previously + * grant appropriate access to their pages to the third party. + * + * source_offset specifies an offset in the source frame, dest_offset + * the offset in the target frame and len specifies the number of + * bytes to be copied. + */ + +#define _GNTCOPY_source_gref (0) +#define GNTCOPY_source_gref (1<<_GNTCOPY_source_gref) +#define _GNTCOPY_dest_gref (1) +#define GNTCOPY_dest_gref (1<<_GNTCOPY_dest_gref) + +#define GNTTABOP_copy 5 +struct gnttab_copy { + /* IN parameters. */ + struct { + union { + grant_ref_t ref; + unsigned long gmfn; + } u; + domid_t domid; + uint16_t offset; + } source, dest; + uint16_t len; + uint16_t flags; /* GNTCOPY_* */ + /* OUT parameters. */ + int16_t status; +}; + +/* + * GNTTABOP_query_size: Query the current and maximum sizes of the shared + * grant table. + * NOTES: + * 1. <dom> may be specified as DOMID_SELF. + * 2. Only a sufficiently-privileged domain may specify <dom> != DOMID_SELF. + */ +#define GNTTABOP_query_size 6 +struct gnttab_query_size { + /* IN parameters. */ + domid_t dom; + /* OUT parameters. */ + uint32_t nr_frames; + uint32_t max_nr_frames; + int16_t status; /* GNTST_* */ +}; + + +/* + * Bitfield values for update_pin_status.flags. + */ + /* Map the grant entry for access by I/O devices. */ +#define _GNTMAP_device_map (0) +#define GNTMAP_device_map (1<<_GNTMAP_device_map) + /* Map the grant entry for access by host CPUs. */ +#define _GNTMAP_host_map (1) +#define GNTMAP_host_map (1<<_GNTMAP_host_map) + /* Accesses to the granted frame will be restricted to read-only access. */ +#define _GNTMAP_readonly (2) +#define GNTMAP_readonly (1<<_GNTMAP_readonly) + /* + * GNTMAP_host_map subflag: + * 0 => The host mapping is usable only by the guest OS. + * 1 => The host mapping is usable by guest OS + current application. + */ +#define _GNTMAP_application_map (3) +#define GNTMAP_application_map (1<<_GNTMAP_application_map) + + /* + * GNTMAP_contains_pte subflag: + * 0 => This map request contains a host virtual address. + * 1 => This map request contains the machine addess of the PTE to update. + */ +#define _GNTMAP_contains_pte (4) +#define GNTMAP_contains_pte (1<<_GNTMAP_contains_pte) + +/* + * Values for error status returns. All errors are -ve. + */ +#define GNTST_okay (0) /* Normal return. */ +#define GNTST_general_error (-1) /* General undefined error. */ +#define GNTST_bad_domain (-2) /* Unrecognsed domain id. */ +#define GNTST_bad_gntref (-3) /* Unrecognised or inappropriate gntref. */ +#define GNTST_bad_handle (-4) /* Unrecognised or inappropriate handle. */ +#define GNTST_bad_virt_addr (-5) /* Inappropriate virtual address to map. */ +#define GNTST_bad_dev_addr (-6) /* Inappropriate device address to unmap.*/ +#define GNTST_no_device_space (-7) /* Out of space in I/O MMU. */ +#define GNTST_permission_denied (-8) /* Not enough privilege for operation. */ +#define GNTST_bad_page (-9) /* Specified page was invalid for op. */ +#define GNTST_bad_copy_arg (-10) /* copy arguments cross page boundary */ + +#define GNTTABOP_error_msgs { \ + "okay", \ + "undefined error", \ + "unrecognised domain id", \ + "invalid grant reference", \ + "invalid mapping handle", \ + "invalid virtual address", \ + "invalid device address", \ + "no spare translation slot in the I/O MMU", \ + "permission denied", \ + "bad page", \ + "copy arguments cross page boundary" \ +} + +#endif /* __XEN_PUBLIC_GRANT_TABLE_H__ */ diff --git a/include/xen/interface/io/blkif.h b/include/xen/interface/io/blkif.h new file mode 100644 index 0000000..c2d1fa4 --- /dev/null +++ b/include/xen/interface/io/blkif.h @@ -0,0 +1,94 @@ +/****************************************************************************** + * blkif.h + * + * Unified block-device I/O interface for Xen guest OSes. + * + * Copyright (c) 2003-2004, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_IO_BLKIF_H__ +#define __XEN_PUBLIC_IO_BLKIF_H__ + +#include "ring.h" +#include "../grant_table.h" + +/* + * Front->back notifications: When enqueuing a new request, sending a + * notification can be made conditional on req_event (i.e., the generic + * hold-off mechanism provided by the ring macros). Backends must set + * req_event appropriately (e.g., using RING_FINAL_CHECK_FOR_REQUESTS()). + * + * Back->front notifications: When enqueuing a new response, sending a + * notification can be made conditional on rsp_event (i.e., the generic + * hold-off mechanism provided by the ring macros). Frontends must set + * rsp_event appropriately (e.g., using RING_FINAL_CHECK_FOR_RESPONSES()). + */ + +typedef uint16_t blkif_vdev_t; +typedef uint64_t blkif_sector_t; + +/* + * REQUEST CODES. + */ +#define BLKIF_OP_READ 0 +#define BLKIF_OP_WRITE 1 +/* + * Recognised only if "feature-barrier" is present in backend xenbus info. + * The "feature_barrier" node contains a boolean indicating whether barrier + * requests are likely to succeed or fail. Either way, a barrier request + * may fail at any time with BLKIF_RSP_EOPNOTSUPP if it is unsupported by + * the underlying block-device hardware. The boolean simply indicates whether + * or not it is worthwhile for the frontend to attempt barrier requests. + * If a backend does not recognise BLKIF_OP_WRITE_BARRIER, it should *not* + * create the "feature-barrier" node! + */ +#define BLKIF_OP_WRITE_BARRIER 2 + +/* + * Maximum scatter/gather segments per request. + * This is carefully chosen so that sizeof(struct blkif_ring) <= PAGE_SIZE. + * NB. This could be 12 if the ring indexes weren't stored in the same page. + */ +#define BLKIF_MAX_SEGMENTS_PER_REQUEST 11 + +struct blkif_request { + uint8_t operation; /* BLKIF_OP_??? */ + uint8_t nr_segments; /* number of segments */ + blkif_vdev_t handle; /* only for read/write requests */ + uint64_t id; /* private guest value, echoed in resp */ + blkif_sector_t sector_number;/* start sector idx on disk (r/w only) */ + struct blkif_request_segment { + grant_ref_t gref; /* reference to I/O buffer frame */ + /* @first_sect: first sector in frame to transfer (inclusive). */ + /* @last_sect: last sector in frame to transfer (inclusive). */ + uint8_t first_sect, last_sect; + } seg[BLKIF_MAX_SEGMENTS_PER_REQUEST]; +}; + +struct blkif_response { + uint64_t id; /* copied from request */ + uint8_t operation; /* copied from request */ + int16_t status; /* BLKIF_RSP_??? */ +}; + +/* + * STATUS RETURN CODES. + */ + /* Operation not supported (only happens on barrier writes). */ +#define BLKIF_RSP_EOPNOTSUPP -2 + /* Operation failed for some unspecified reason (-EIO). */ +#define BLKIF_RSP_ERROR -1 + /* Operation completed successfully. */ +#define BLKIF_RSP_OKAY 0 + +/* + * Generate blkif ring structures and types. + */ + +DEFINE_RING_TYPES(blkif, struct blkif_request, struct blkif_response); + +#define VDISK_CDROM 0x1 +#define VDISK_REMOVABLE 0x2 +#define VDISK_READONLY 0x4 + +#endif /* __XEN_PUBLIC_IO_BLKIF_H__ */ diff --git a/include/xen/interface/io/console.h b/include/xen/interface/io/console.h new file mode 100644 index 0000000..e563de7 --- /dev/null +++ b/include/xen/interface/io/console.h @@ -0,0 +1,23 @@ +/****************************************************************************** + * console.h + * + * Console I/O interface for Xen guest OSes. + * + * Copyright (c) 2005, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_IO_CONSOLE_H__ +#define __XEN_PUBLIC_IO_CONSOLE_H__ + +typedef uint32_t XENCONS_RING_IDX; + +#define MASK_XENCONS_IDX(idx, ring) ((idx) & (sizeof(ring)-1)) + +struct xencons_interface { + char in[1024]; + char out[2048]; + XENCONS_RING_IDX in_cons, in_prod; + XENCONS_RING_IDX out_cons, out_prod; +}; + +#endif /* __XEN_PUBLIC_IO_CONSOLE_H__ */ diff --git a/include/xen/interface/io/netif.h b/include/xen/interface/io/netif.h new file mode 100644 index 0000000..518481c --- /dev/null +++ b/include/xen/interface/io/netif.h @@ -0,0 +1,158 @@ +/****************************************************************************** + * netif.h + * + * Unified network-device I/O interface for Xen guest OSes. + * + * Copyright (c) 2003-2004, Keir Fraser + */ + +#ifndef __XEN_PUBLIC_IO_NETIF_H__ +#define __XEN_PUBLIC_IO_NETIF_H__ + +#include "ring.h" +#include "../grant_table.h" + +/* + * Notifications after enqueuing any type of message should be conditional on + * the appropriate req_event or rsp_event field in the shared ring. + * If the client sends notification for rx requests then it should specify + * feature 'feature-rx-notify' via xenbus. Otherwise the backend will assume + * that it cannot safely queue packets (as it may not be kicked to send them). + */ + +/* + * This is the 'wire' format for packets: + * Request 1: netif_tx_request -- NETTXF_* (any flags) + * [Request 2: netif_tx_extra] (only if request 1 has NETTXF_extra_info) + * [Request 3: netif_tx_extra] (only if request 2 has XEN_NETIF_EXTRA_MORE) + * Request 4: netif_tx_request -- NETTXF_more_data + * Request 5: netif_tx_request -- NETTXF_more_data + * ... + * Request N: netif_tx_request -- 0 + */ + +/* Protocol checksum field is blank in the packet (hardware offload)? */ +#define _NETTXF_csum_blank (0) +#define NETTXF_csum_blank (1U<<_NETTXF_csum_blank) + +/* Packet data has been validated against protocol checksum. */ +#define _NETTXF_data_validated (1) +#define NETTXF_data_validated (1U<<_NETTXF_data_validated) + +/* Packet continues in the next request descriptor. */ +#define _NETTXF_more_data (2) +#define NETTXF_more_data (1U<<_NETTXF_more_data) + +/* Packet to be followed by extra descriptor(s). */ +#define _NETTXF_extra_info (3) +#define NETTXF_extra_info (1U<<_NETTXF_extra_info) + +struct xen_netif_tx_request { + grant_ref_t gref; /* Reference to buffer page */ + uint16_t offset; /* Offset within buffer page */ + uint16_t flags; /* NETTXF_* */ + uint16_t id; /* Echoed in response message. */ + uint16_t size; /* Packet size in bytes. */ +}; + +/* Types of netif_extra_info descriptors. */ +#define XEN_NETIF_EXTRA_TYPE_NONE (0) /* Never used - invalid */ +#define XEN_NETIF_EXTRA_TYPE_GSO (1) /* u.gso */ +#define XEN_NETIF_EXTRA_TYPE_MAX (2) + +/* netif_extra_info flags. */ +#define _XEN_NETIF_EXTRA_FLAG_MORE (0) +#define XEN_NETIF_EXTRA_FLAG_MORE (1U<<_XEN_NETIF_EXTRA_FLAG_MORE) + +/* GSO types - only TCPv4 currently supported. */ +#define XEN_NETIF_GSO_TYPE_TCPV4 (1) + +/* + * This structure needs to fit within both netif_tx_request and + * netif_rx_response for compatibility. + */ +struct xen_netif_extra_info { + uint8_t type; /* XEN_NETIF_EXTRA_TYPE_* */ + uint8_t flags; /* XEN_NETIF_EXTRA_FLAG_* */ + + union { + struct { + /* + * Maximum payload size of each segment. For + * example, for TCP this is just the path MSS. + */ + uint16_t size; + + /* + * GSO type. This determines the protocol of + * the packet and any extra features required + * to segment the packet properly. + */ + uint8_t type; /* XEN_NETIF_GSO_TYPE_* */ + + /* Future expansion. */ + uint8_t pad; + + /* + * GSO features. This specifies any extra GSO + * features required to process this packet, + * such as ECN support for TCPv4. + */ + uint16_t features; /* XEN_NETIF_GSO_FEAT_* */ + } gso; + + uint16_t pad[3]; + } u; +}; + +struct xen_netif_tx_response { + uint16_t id; + int16_t status; /* NETIF_RSP_* */ +}; + +struct xen_netif_rx_request { + uint16_t id; /* Echoed in response message. */ + grant_ref_t gref; /* Reference to incoming granted frame */ +}; + +/* Packet data has been validated against protocol checksum. */ +#define _NETRXF_data_validated (0) +#define NETRXF_data_validated (1U<<_NETRXF_data_validated) + +/* Protocol checksum field is blank in the packet (hardware offload)? */ +#define _NETRXF_csum_blank (1) +#define NETRXF_csum_blank (1U<<_NETRXF_csum_blank) + +/* Packet continues in the next request descriptor. */ +#define _NETRXF_more_data (2) +#define NETRXF_more_data (1U<<_NETRXF_more_data) + +/* Packet to be followed by extra descriptor(s). */ +#define _NETRXF_extra_info (3) +#define NETRXF_extra_info (1U<<_NETRXF_extra_info) + +struct xen_netif_rx_response { + uint16_t id; + uint16_t offset; /* Offset in page of start of received packet */ + uint16_t flags; /* NETRXF_* */ + int16_t status; /* -ve: BLKIF_RSP_* ; +ve: Rx'ed pkt size. */ +}; + +/* + * Generate netif ring structures and types. + */ + +DEFINE_RING_TYPES(xen_netif_tx, + struct xen_netif_tx_request, + struct xen_netif_tx_response); +DEFINE_RING_TYPES(xen_netif_rx, + struct xen_netif_rx_request, + struct xen_netif_rx_response); + +#define NETIF_RSP_DROPPED -2 +#define NETIF_RSP_ERROR -1 +#define NETIF_RSP_OKAY 0 +/* No response: used for auxiliary requests (e.g., netif_tx_extra). */ +#define NETIF_RSP_NULL 1 + +#endif diff --git a/include/xen/interface/io/ring.h b/include/xen/interface/io/ring.h new file mode 100644 index 0000000..e8cbf43 --- /dev/null +++ b/include/xen/interface/io/ring.h @@ -0,0 +1,260 @@ +/****************************************************************************** + * ring.h + * + * Shared producer-consumer ring macros. + * + * Tim Deegan and Andrew Warfield November 2004. + */ + +#ifndef __XEN_PUBLIC_IO_RING_H__ +#define __XEN_PUBLIC_IO_RING_H__ + +typedef unsigned int RING_IDX; + +/* Round a 32-bit unsigned constant down to the nearest power of two. */ +#define __RD2(_x) (((_x) & 0x00000002) ? 0x2 : ((_x) & 0x1)) +#define __RD4(_x) (((_x) & 0x0000000c) ? __RD2((_x)>>2)<<2 : __RD2(_x)) +#define __RD8(_x) (((_x) & 0x000000f0) ? __RD4((_x)>>4)<<4 : __RD4(_x)) +#define __RD16(_x) (((_x) & 0x0000ff00) ? __RD8((_x)>>8)<<8 : __RD8(_x)) +#define __RD32(_x) (((_x) & 0xffff0000) ? __RD16((_x)>>16)<<16 : __RD16(_x)) + +/* + * Calculate size of a shared ring, given the total available space for the + * ring and indexes (_sz), and the name tag of the request/response structure. + * A ring contains as many entries as will fit, rounded down to the nearest + * power of two (so we can mask with (size-1) to loop around). + */ +#define __RING_SIZE(_s, _sz) \ + (__RD32(((_sz) - (long)&(_s)->ring + (long)(_s)) / sizeof((_s)->ring[0]))) + +/* + * Macros to make the correct C datatypes for a new kind of ring. + * + * To make a new ring datatype, you need to have two message structures, + * let's say struct request, and struct response already defined. + * + * In a header where you want the ring datatype declared, you then do: + * + * DEFINE_RING_TYPES(mytag, struct request, struct response); + * + * These expand out to give you a set of types, as you can see below. + * The most important of these are: + * + * struct mytag_sring - The shared ring. + * struct mytag_front_ring - The 'front' half of the ring. + * struct mytag_back_ring - The 'back' half of the ring. + * + * To initialize a ring in your code you need to know the location and size + * of the shared memory area (PAGE_SIZE, for instance). To initialise + * the front half: + * + * struct mytag_front_ring front_ring; + * SHARED_RING_INIT((struct mytag_sring *)shared_page); + * FRONT_RING_INIT(&front_ring, (struct mytag_sring *)shared_page, + * PAGE_SIZE); + * + * Initializing the back follows similarly (note that only the front + * initializes the shared ring): + * + * struct mytag_back_ring back_ring; + * BACK_RING_INIT(&back_ring, (struct mytag_sring *)shared_page, + * PAGE_SIZE); + */ + +#define DEFINE_RING_TYPES(__name, __req_t, __rsp_t) \ + \ +/* Shared ring entry */ \ +union __name##_sring_entry { \ + __req_t req; \ + __rsp_t rsp; \ +}; \ + \ +/* Shared ring page */ \ +struct __name##_sring { \ + RING_IDX req_prod, req_event; \ + RING_IDX rsp_prod, rsp_event; \ + uint8_t pad[48]; \ + union __name##_sring_entry ring[1]; /* variable-length */ \ +}; \ + \ +/* "Front" end's private variables */ \ +struct __name##_front_ring { \ + RING_IDX req_prod_pvt; \ + RING_IDX rsp_cons; \ + unsigned int nr_ents; \ + struct __name##_sring *sring; \ +}; \ + \ +/* "Back" end's private variables */ \ +struct __name##_back_ring { \ + RING_IDX rsp_prod_pvt; \ + RING_IDX req_cons; \ + unsigned int nr_ents; \ + struct __name##_sring *sring; \ +}; + +/* + * Macros for manipulating rings. + * + * FRONT_RING_whatever works on the "front end" of a ring: here + * requests are pushed on to the ring and responses taken off it. + * + * BACK_RING_whatever works on the "back end" of a ring: here + * requests are taken off the ring and responses put on. + * + * N.B. these macros do NO INTERLOCKS OR FLOW CONTROL. + * This is OK in 1-for-1 request-response situations where the + * requestor (front end) never has more than RING_SIZE()-1 + * outstanding requests. + */ + +/* Initialising empty rings */ +#define SHARED_RING_INIT(_s) do { \ + (_s)->req_prod = (_s)->rsp_prod = 0; \ + (_s)->req_event = (_s)->rsp_event = 1; \ + memset((_s)->pad, 0, sizeof((_s)->pad)); \ +} while(0) + +#define FRONT_RING_INIT(_r, _s, __size) do { \ + (_r)->req_prod_pvt = 0; \ + (_r)->rsp_cons = 0; \ + (_r)->nr_ents = __RING_SIZE(_s, __size); \ + (_r)->sring = (_s); \ +} while (0) + +#define BACK_RING_INIT(_r, _s, __size) do { \ + (_r)->rsp_prod_pvt = 0; \ + (_r)->req_cons = 0; \ + (_r)->nr_ents = __RING_SIZE(_s, __size); \ + (_r)->sring = (_s); \ +} while (0) + +/* Initialize to existing shared indexes -- for recovery */ +#define FRONT_RING_ATTACH(_r, _s, __size) do { \ + (_r)->sring = (_s); \ + (_r)->req_prod_pvt = (_s)->req_prod; \ + (_r)->rsp_cons = (_s)->rsp_prod; \ + (_r)->nr_ents = __RING_SIZE(_s, __size); \ +} while (0) + +#define BACK_RING_ATTACH(_r, _s, __size) do { \ + (_r)->sring = (_s); \ + (_r)->rsp_prod_pvt = (_s)->rsp_prod; \ + (_r)->req_cons = (_s)->req_prod; \ + (_r)->nr_ents = __RING_SIZE(_s, __size); \ +} while (0) + +/* How big is this ring? */ +#define RING_SIZE(_r) \ + ((_r)->nr_ents) + +/* Number of free requests (for use on front side only). */ +#define RING_FREE_REQUESTS(_r) \ + (RING_SIZE(_r) - ((_r)->req_prod_pvt - (_r)->rsp_cons)) + +/* Test if there is an empty slot available on the front ring. + * (This is only meaningful from the front. ) + */ +#define RING_FULL(_r) \ + (RING_FREE_REQUESTS(_r) == 0) + +/* Test if there are outstanding messages to be processed on a ring. */ +#define RING_HAS_UNCONSUMED_RESPONSES(_r) \ + ((_r)->sring->rsp_prod - (_r)->rsp_cons) + +#define RING_HAS_UNCONSUMED_REQUESTS(_r) \ + ({ \ + unsigned int req = (_r)->sring->req_prod - (_r)->req_cons; \ + unsigned int rsp = RING_SIZE(_r) - \ + ((_r)->req_cons - (_r)->rsp_prod_pvt); \ + req < rsp ? req : rsp; \ + }) + +/* Direct access to individual ring elements, by index. */ +#define RING_GET_REQUEST(_r, _idx) \ + (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req)) + +#define RING_GET_RESPONSE(_r, _idx) \ + (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].rsp)) + +/* Loop termination condition: Would the specified index overflow the ring? */ +#define RING_REQUEST_CONS_OVERFLOW(_r, _cons) \ + (((_cons) - (_r)->rsp_prod_pvt) >= RING_SIZE(_r)) + +#define RING_PUSH_REQUESTS(_r) do { \ + wmb(); /* back sees requests /before/ updated producer index */ \ + (_r)->sring->req_prod = (_r)->req_prod_pvt; \ +} while (0) + +#define RING_PUSH_RESPONSES(_r) do { \ + wmb(); /* front sees responses /before/ updated producer index */ \ + (_r)->sring->rsp_prod = (_r)->rsp_prod_pvt; \ +} while (0) + +/* + * Notification hold-off (req_event and rsp_event): + * + * When queueing requests or responses on a shared ring, it may not always be + * necessary to notify the remote end. For example, if requests are in flight + * in a backend, the front may be able to queue further requests without + * notifying the back (if the back checks for new requests when it queues + * responses). + * + * When enqueuing requests or responses: + * + * Use RING_PUSH_{REQUESTS,RESPONSES}_AND_CHECK_NOTIFY(). The second argument + * is a boolean return value. True indicates that the receiver requires an + * asynchronous notification. + * + * After dequeuing requests or responses (before sleeping the connection): + * + * Use RING_FINAL_CHECK_FOR_REQUESTS() or RING_FINAL_CHECK_FOR_RESPONSES(). + * The second argument is a boolean return value. True indicates that there + * are pending messages on the ring (i.e., the connection should not be put + * to sleep). + * + * These macros will set the req_event/rsp_event field to trigger a + * notification on the very next message that is enqueued. If you want to + * create batches of work (i.e., only receive a notification after several + * messages have been enqueued) then you will need to create a customised + * version of the FINAL_CHECK macro in your own code, which sets the event + * field appropriately. + */ + +#define RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(_r, _notify) do { \ + RING_IDX __old = (_r)->sring->req_prod; \ + RING_IDX __new = (_r)->req_prod_pvt; \ + wmb(); /* back sees requests /before/ updated producer index */ \ + (_r)->sring->req_prod = __new; \ + mb(); /* back sees new requests /before/ we check req_event */ \ + (_notify) = ((RING_IDX)(__new - (_r)->sring->req_event) < \ + (RING_IDX)(__new - __old)); \ +} while (0) + +#define RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(_r, _notify) do { \ + RING_IDX __old = (_r)->sring->rsp_prod; \ + RING_IDX __new = (_r)->rsp_prod_pvt; \ + wmb(); /* front sees responses /before/ updated producer index */ \ + (_r)->sring->rsp_prod = __new; \ + mb(); /* front sees new responses /before/ we check rsp_event */ \ + (_notify) = ((RING_IDX)(__new - (_r)->sring->rsp_event) < \ + (RING_IDX)(__new - __old)); \ +} while (0) + +#define RING_FINAL_CHECK_FOR_REQUESTS(_r, _work_to_do) do { \ + (_work_to_do) = RING_HAS_UNCONSUMED_REQUESTS(_r); \ + if (_work_to_do) break; \ + (_r)->sring->req_event = (_r)->req_cons + 1; \ + mb(); \ + (_work_to_do) = RING_HAS_UNCONSUMED_REQUESTS(_r); \ +} while (0) + +#define RING_FINAL_CHECK_FOR_RESPONSES(_r, _work_to_do) do { \ + (_work_to_do) = RING_HAS_UNCONSUMED_RESPONSES(_r); \ + if (_work_to_do) break; \ + (_r)->sring->rsp_event = (_r)->rsp_cons + 1; \ + mb(); \ + (_work_to_do) = RING_HAS_UNCONSUMED_RESPONSES(_r); \ +} while (0) + +#endif /* __XEN_PUBLIC_IO_RING_H__ */ diff --git a/include/xen/interface/io/xenbus.h b/include/xen/interface/io/xenbus.h new file mode 100644 index 0000000..46508c7 --- /dev/null +++ b/include/xen/interface/io/xenbus.h @@ -0,0 +1,44 @@ +/***************************************************************************** + * xenbus.h + * + * Xenbus protocol details. + * + * Copyright (C) 2005 XenSource Ltd. + */ + +#ifndef _XEN_PUBLIC_IO_XENBUS_H +#define _XEN_PUBLIC_IO_XENBUS_H + +/* The state of either end of the Xenbus, i.e. the current communication + status of initialisation across the bus. States here imply nothing about + the state of the connection between the driver and the kernel's device + layers. */ +enum xenbus_state +{ + XenbusStateUnknown = 0, + XenbusStateInitialising = 1, + XenbusStateInitWait = 2, /* Finished early + initialisation, but waiting + for information from the peer + or hotplug scripts. */ + XenbusStateInitialised = 3, /* Initialised and waiting for a + connection from the peer. */ + XenbusStateConnected = 4, + XenbusStateClosing = 5, /* The device is being closed + due to an error or an unplug + event. */ + XenbusStateClosed = 6 + +}; + +#endif /* _XEN_PUBLIC_IO_XENBUS_H */ + +/* + * Local variables: + * c-file-style: "linux" + * indent-tabs-mode: t + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * End: + */ diff --git a/include/xen/interface/io/xs_wire.h b/include/xen/interface/io/xs_wire.h new file mode 100644 index 0000000..99fcffb --- /dev/null +++ b/include/xen/interface/io/xs_wire.h @@ -0,0 +1,87 @@ +/* + * Details of the "wire" protocol between Xen Store Daemon and client + * library or guest kernel. + * Copyright (C) 2005 Rusty Russell IBM Corporation + */ + +#ifndef _XS_WIRE_H +#define _XS_WIRE_H + +enum xsd_sockmsg_type +{ + XS_DEBUG, + XS_DIRECTORY, + XS_READ, + XS_GET_PERMS, + XS_WATCH, + XS_UNWATCH, + XS_TRANSACTION_START, + XS_TRANSACTION_END, + XS_INTRODUCE, + XS_RELEASE, + XS_GET_DOMAIN_PATH, + XS_WRITE, + XS_MKDIR, + XS_RM, + XS_SET_PERMS, + XS_WATCH_EVENT, + XS_ERROR, + XS_IS_DOMAIN_INTRODUCED +}; + +#define XS_WRITE_NONE "NONE" +#define XS_WRITE_CREATE "CREATE" +#define XS_WRITE_CREATE_EXCL "CREATE|EXCL" + +/* We hand errors as strings, for portability. */ +struct xsd_errors +{ + int errnum; + const char *errstring; +}; +#define XSD_ERROR(x) { x, #x } +static struct xsd_errors xsd_errors[] __attribute__((unused)) = { + XSD_ERROR(EINVAL), + XSD_ERROR(EACCES), + XSD_ERROR(EEXIST), + XSD_ERROR(EISDIR), + XSD_ERROR(ENOENT), + XSD_ERROR(ENOMEM), + XSD_ERROR(ENOSPC), + XSD_ERROR(EIO), + XSD_ERROR(ENOTEMPTY), + XSD_ERROR(ENOSYS), + XSD_ERROR(EROFS), + XSD_ERROR(EBUSY), + XSD_ERROR(EAGAIN), + XSD_ERROR(EISCONN) +}; + +struct xsd_sockmsg +{ + uint32_t type; /* XS_??? */ + uint32_t req_id;/* Request identifier, echoed in daemon's response. */ + uint32_t tx_id; /* Transaction id (0 if not related to a transaction). */ + uint32_t len; /* Length of data following this. */ + + /* Generally followed by nul-terminated string(s). */ +}; + +enum xs_watch_type +{ + XS_WATCH_PATH = 0, + XS_WATCH_TOKEN +}; + +/* Inter-domain shared memory communications. */ +#define XENSTORE_RING_SIZE 1024 +typedef uint32_t XENSTORE_RING_IDX; +#define MASK_XENSTORE_IDX(idx) ((idx) & (XENSTORE_RING_SIZE-1)) +struct xenstore_domain_interface { + char req[XENSTORE_RING_SIZE]; /* Requests to xenstore daemon. */ + char rsp[XENSTORE_RING_SIZE]; /* Replies and async watch events. */ + XENSTORE_RING_IDX req_cons, req_prod; + XENSTORE_RING_IDX rsp_cons, rsp_prod; +}; + +#endif /* _XS_WIRE_H */ diff --git a/include/xen/interface/memory.h b/include/xen/interface/memory.h new file mode 100644 index 0000000..af36ead --- /dev/null +++ b/include/xen/interface/memory.h @@ -0,0 +1,145 @@ +/****************************************************************************** + * memory.h + * + * Memory reservation and information. + * + * Copyright (c) 2005, Keir Fraser <keir@xensource.com> + */ + +#ifndef __XEN_PUBLIC_MEMORY_H__ +#define __XEN_PUBLIC_MEMORY_H__ + +/* + * Increase or decrease the specified domain's memory reservation. Returns a + * -ve errcode on failure, or the # extents successfully allocated or freed. + * arg == addr of struct xen_memory_reservation. + */ +#define XENMEM_increase_reservation 0 +#define XENMEM_decrease_reservation 1 +#define XENMEM_populate_physmap 6 +struct xen_memory_reservation { + + /* + * XENMEM_increase_reservation: + * OUT: MFN (*not* GMFN) bases of extents that were allocated + * XENMEM_decrease_reservation: + * IN: GMFN bases of extents to free + * XENMEM_populate_physmap: + * IN: GPFN bases of extents to populate with memory + * OUT: GMFN bases of extents that were allocated + * (NB. This command also updates the mach_to_phys translation table) + */ + GUEST_HANDLE(ulong) extent_start; + + /* Number of extents, and size/alignment of each (2^extent_order pages). */ + unsigned long nr_extents; + unsigned int extent_order; + + /* + * Maximum # bits addressable by the user of the allocated region (e.g., + * I/O devices often have a 32-bit limitation even in 64-bit systems). If + * zero then the user has no addressing restriction. + * This field is not used by XENMEM_decrease_reservation. + */ + unsigned int address_bits; + + /* + * Domain whose reservation is being changed. + * Unprivileged domains can specify only DOMID_SELF. + */ + domid_t domid; + +}; +DEFINE_GUEST_HANDLE_STRUCT(xen_memory_reservation); + +/* + * Returns the maximum machine frame number of mapped RAM in this system. + * This command always succeeds (it never returns an error code). + * arg == NULL. + */ +#define XENMEM_maximum_ram_page 2 + +/* + * Returns the current or maximum memory reservation, in pages, of the + * specified domain (may be DOMID_SELF). Returns -ve errcode on failure. + * arg == addr of domid_t. + */ +#define XENMEM_current_reservation 3 +#define XENMEM_maximum_reservation 4 + +/* + * Returns a list of MFN bases of 2MB extents comprising the machine_to_phys + * mapping table. Architectures which do not have a m2p table do not implement + * this command. + * arg == addr of xen_machphys_mfn_list_t. + */ +#define XENMEM_machphys_mfn_list 5 +struct xen_machphys_mfn_list { + /* + * Size of the 'extent_start' array. Fewer entries will be filled if the + * machphys table is smaller than max_extents * 2MB. + */ + unsigned int max_extents; + + /* + * Pointer to buffer to fill with list of extent starts. If there are + * any large discontiguities in the machine address space, 2MB gaps in + * the machphys table will be represented by an MFN base of zero. + */ + GUEST_HANDLE(ulong) extent_start; + + /* + * Number of extents written to the above array. This will be smaller + * than 'max_extents' if the machphys table is smaller than max_e * 2MB. + */ + unsigned int nr_extents; +}; +DEFINE_GUEST_HANDLE_STRUCT(xen_machphys_mfn_list); + +/* + * Sets the GPFN at which a particular page appears in the specified guest's + * pseudophysical address space. + * arg == addr of xen_add_to_physmap_t. + */ +#define XENMEM_add_to_physmap 7 +struct xen_add_to_physmap { + /* Which domain to change the mapping for. */ + domid_t domid; + + /* Source mapping space. */ +#define XENMAPSPACE_shared_info 0 /* shared info page */ +#define XENMAPSPACE_grant_table 1 /* grant table page */ + unsigned int space; + + /* Index into source mapping space. */ + unsigned long idx; + + /* GPFN where the source mapping page should appear. */ + unsigned long gpfn; +}; +DEFINE_GUEST_HANDLE_STRUCT(xen_add_to_physmap); + +/* + * Translates a list of domain-specific GPFNs into MFNs. Returns a -ve error + * code on failure. This call only works for auto-translated guests. + */ +#define XENMEM_translate_gpfn_list 8 +struct xen_translate_gpfn_list { + /* Which domain to translate for? */ + domid_t domid; + + /* Length of list. */ + unsigned long nr_gpfns; + + /* List of GPFNs to translate. */ + GUEST_HANDLE(ulong) gpfn_list; + + /* + * Output list to contain MFN translations. May be the same as the input + * list (in which case each input GPFN is overwritten with the output MFN). + */ + GUEST_HANDLE(ulong) mfn_list; +}; +DEFINE_GUEST_HANDLE_STRUCT(xen_translate_gpfn_list); + +#endif /* __XEN_PUBLIC_MEMORY_H__ */ diff --git a/include/xen/interface/physdev.h b/include/xen/interface/physdev.h new file mode 100644 index 0000000..cd69391 --- /dev/null +++ b/include/xen/interface/physdev.h @@ -0,0 +1,145 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef __XEN_PUBLIC_PHYSDEV_H__ +#define __XEN_PUBLIC_PHYSDEV_H__ + +/* + * Prototype for this hypercall is: + * int physdev_op(int cmd, void *args) + * @cmd == PHYSDEVOP_??? (physdev operation). + * @args == Operation-specific extra arguments (NULL if none). + */ + +/* + * Notify end-of-interrupt (EOI) for the specified IRQ. + * @arg == pointer to physdev_eoi structure. + */ +#define PHYSDEVOP_eoi 12 +struct physdev_eoi { + /* IN */ + uint32_t irq; +}; + +/* + * Query the status of an IRQ line. + * @arg == pointer to physdev_irq_status_query structure. + */ +#define PHYSDEVOP_irq_status_query 5 +struct physdev_irq_status_query { + /* IN */ + uint32_t irq; + /* OUT */ + uint32_t flags; /* XENIRQSTAT_* */ +}; + +/* Need to call PHYSDEVOP_eoi when the IRQ has been serviced? */ +#define _XENIRQSTAT_needs_eoi (0) +#define XENIRQSTAT_needs_eoi (1U<<_XENIRQSTAT_needs_eoi) + +/* IRQ shared by multiple guests? */ +#define _XENIRQSTAT_shared (1) +#define XENIRQSTAT_shared (1U<<_XENIRQSTAT_shared) + +/* + * Set the current VCPU's I/O privilege level. + * @arg == pointer to physdev_set_iopl structure. + */ +#define PHYSDEVOP_set_iopl 6 +struct physdev_set_iopl { + /* IN */ + uint32_t iopl; +}; + +/* + * Set the current VCPU's I/O-port permissions bitmap. + * @arg == pointer to physdev_set_iobitmap structure. + */ +#define PHYSDEVOP_set_iobitmap 7 +struct physdev_set_iobitmap { + /* IN */ + uint8_t * bitmap; + uint32_t nr_ports; +}; + +/* + * Read or write an IO-APIC register. + * @arg == pointer to physdev_apic structure. + */ +#define PHYSDEVOP_apic_read 8 +#define PHYSDEVOP_apic_write 9 +struct physdev_apic { + /* IN */ + unsigned long apic_physbase; + uint32_t reg; + /* IN or OUT */ + uint32_t value; +}; + +/* + * Allocate or free a physical upcall vector for the specified IRQ line. + * @arg == pointer to physdev_irq structure. + */ +#define PHYSDEVOP_alloc_irq_vector 10 +#define PHYSDEVOP_free_irq_vector 11 +struct physdev_irq { + /* IN */ + uint32_t irq; + /* IN or OUT */ + uint32_t vector; +}; + +/* + * Argument to physdev_op_compat() hypercall. Superceded by new physdev_op() + * hypercall since 0x00030202. + */ +struct physdev_op { + uint32_t cmd; + union { + struct physdev_irq_status_query irq_status_query; + struct physdev_set_iopl set_iopl; + struct physdev_set_iobitmap set_iobitmap; + struct physdev_apic apic_op; + struct physdev_irq irq_op; + } u; +}; + +/* + * Notify that some PIRQ-bound event channels have been unmasked. + * ** This command is obsolete since interface version 0x00030202 and is ** + * ** unsupported by newer versions of Xen. ** + */ +#define PHYSDEVOP_IRQ_UNMASK_NOTIFY 4 + +/* + * These all-capitals physdev operation names are superceded by the new names + * (defined above) since interface version 0x00030202. + */ +#define PHYSDEVOP_IRQ_STATUS_QUERY PHYSDEVOP_irq_status_query +#define PHYSDEVOP_SET_IOPL PHYSDEVOP_set_iopl +#define PHYSDEVOP_SET_IOBITMAP PHYSDEVOP_set_iobitmap +#define PHYSDEVOP_APIC_READ PHYSDEVOP_apic_read +#define PHYSDEVOP_APIC_WRITE PHYSDEVOP_apic_write +#define PHYSDEVOP_ASSIGN_VECTOR PHYSDEVOP_alloc_irq_vector +#define PHYSDEVOP_FREE_VECTOR PHYSDEVOP_free_irq_vector +#define PHYSDEVOP_IRQ_NEEDS_UNMASK_NOTIFY XENIRQSTAT_needs_eoi +#define PHYSDEVOP_IRQ_SHARED XENIRQSTAT_shared + +#endif /* __XEN_PUBLIC_PHYSDEV_H__ */ diff --git a/include/xen/interface/sched.h b/include/xen/interface/sched.h new file mode 100644 index 0000000..5fec575 --- /dev/null +++ b/include/xen/interface/sched.h @@ -0,0 +1,77 @@ +/****************************************************************************** + * sched.h + * + * Scheduler state interactions + * + * Copyright (c) 2005, Keir Fraser <keir@xensource.com> + */ + +#ifndef __XEN_PUBLIC_SCHED_H__ +#define __XEN_PUBLIC_SCHED_H__ + +#include "event_channel.h" + +/* + * The prototype for this hypercall is: + * long sched_op_new(int cmd, void *arg) + * @cmd == SCHEDOP_??? (scheduler operation). + * @arg == Operation-specific extra argument(s), as described below. + * + * **NOTE**: + * Versions of Xen prior to 3.0.2 provide only the following legacy version + * of this hypercall, supporting only the commands yield, block and shutdown: + * long sched_op(int cmd, unsigned long arg) + * @cmd == SCHEDOP_??? (scheduler operation). + * @arg == 0 (SCHEDOP_yield and SCHEDOP_block) + * == SHUTDOWN_* code (SCHEDOP_shutdown) + */ + +/* + * Voluntarily yield the CPU. + * @arg == NULL. + */ +#define SCHEDOP_yield 0 + +/* + * Block execution of this VCPU until an event is received for processing. + * If called with event upcalls masked, this operation will atomically + * reenable event delivery and check for pending events before blocking the + * VCPU. This avoids a "wakeup waiting" race. + * @arg == NULL. + */ +#define SCHEDOP_block 1 + +/* + * Halt execution of this domain (all VCPUs) and notify the system controller. + * @arg == pointer to sched_shutdown structure. + */ +#define SCHEDOP_shutdown 2 +struct sched_shutdown { + unsigned int reason; /* SHUTDOWN_* */ +}; +DEFINE_GUEST_HANDLE_STRUCT(sched_shutdown); + +/* + * Poll a set of event-channel ports. Return when one or more are pending. An + * optional timeout may be specified. + * @arg == pointer to sched_poll structure. + */ +#define SCHEDOP_poll 3 +struct sched_poll { + GUEST_HANDLE(evtchn_port_t) ports; + unsigned int nr_ports; + uint64_t timeout; +}; +DEFINE_GUEST_HANDLE_STRUCT(sched_poll); + +/* + * Reason codes for SCHEDOP_shutdown. These may be interpreted by control + * software to determine the appropriate action. For the most part, Xen does + * not care about the shutdown code. + */ +#define SHUTDOWN_poweroff 0 /* Domain exited normally. Clean up and kill. */ +#define SHUTDOWN_reboot 1 /* Clean up, kill, and then restart. */ +#define SHUTDOWN_suspend 2 /* Clean up, save suspend info, kill. */ +#define SHUTDOWN_crash 3 /* Tell controller we've crashed. */ + +#endif /* __XEN_PUBLIC_SCHED_H__ */ diff --git a/include/xen/interface/vcpu.h b/include/xen/interface/vcpu.h new file mode 100644 index 0000000..ff61ea3 --- /dev/null +++ b/include/xen/interface/vcpu.h @@ -0,0 +1,167 @@ +/****************************************************************************** + * vcpu.h + * + * VCPU initialisation, query, and hotplug. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright (c) 2005, Keir Fraser <keir@xensource.com> + */ + +#ifndef __XEN_PUBLIC_VCPU_H__ +#define __XEN_PUBLIC_VCPU_H__ + +/* + * Prototype for this hypercall is: + * int vcpu_op(int cmd, int vcpuid, void *extra_args) + * @cmd == VCPUOP_??? (VCPU operation). + * @vcpuid == VCPU to operate on. + * @extra_args == Operation-specific extra arguments (NULL if none). + */ + +/* + * Initialise a VCPU. Each VCPU can be initialised only once. A + * newly-initialised VCPU will not run until it is brought up by VCPUOP_up. + * + * @extra_arg == pointer to vcpu_guest_context structure containing initial + * state for the VCPU. + */ +#define VCPUOP_initialise 0 + +/* + * Bring up a VCPU. This makes the VCPU runnable. This operation will fail + * if the VCPU has not been initialised (VCPUOP_initialise). + */ +#define VCPUOP_up 1 + +/* + * Bring down a VCPU (i.e., make it non-runnable). + * There are a few caveats that callers should observe: + * 1. This operation may return, and VCPU_is_up may return false, before the + * VCPU stops running (i.e., the command is asynchronous). It is a good + * idea to ensure that the VCPU has entered a non-critical loop before + * bringing it down. Alternatively, this operation is guaranteed + * synchronous if invoked by the VCPU itself. + * 2. After a VCPU is initialised, there is currently no way to drop all its + * references to domain memory. Even a VCPU that is down still holds + * memory references via its pagetable base pointer and GDT. It is good + * practise to move a VCPU onto an 'idle' or default page table, LDT and + * GDT before bringing it down. + */ +#define VCPUOP_down 2 + +/* Returns 1 if the given VCPU is up. */ +#define VCPUOP_is_up 3 + +/* + * Return information about the state and running time of a VCPU. + * @extra_arg == pointer to vcpu_runstate_info structure. + */ +#define VCPUOP_get_runstate_info 4 +struct vcpu_runstate_info { + /* VCPU's current state (RUNSTATE_*). */ + int state; + /* When was current state entered (system time, ns)? */ + uint64_t state_entry_time; + /* + * Time spent in each RUNSTATE_* (ns). The sum of these times is + * guaranteed not to drift from system time. + */ + uint64_t time[4]; +}; + +/* VCPU is currently running on a physical CPU. */ +#define RUNSTATE_running 0 + +/* VCPU is runnable, but not currently scheduled on any physical CPU. */ +#define RUNSTATE_runnable 1 + +/* VCPU is blocked (a.k.a. idle). It is therefore not runnable. */ +#define RUNSTATE_blocked 2 + +/* + * VCPU is not runnable, but it is not blocked. + * This is a 'catch all' state for things like hotplug and pauses by the + * system administrator (or for critical sections in the hypervisor). + * RUNSTATE_blocked dominates this state (it is the preferred state). + */ +#define RUNSTATE_offline 3 + +/* + * Register a shared memory area from which the guest may obtain its own + * runstate information without needing to execute a hypercall. + * Notes: + * 1. The registered address may be virtual or physical, depending on the + * platform. The virtual address should be registered on x86 systems. + * 2. Only one shared area may be registered per VCPU. The shared area is + * updated by the hypervisor each time the VCPU is scheduled. Thus + * runstate.state will always be RUNSTATE_running and + * runstate.state_entry_time will indicate the system time at which the + * VCPU was last scheduled to run. + * @extra_arg == pointer to vcpu_register_runstate_memory_area structure. + */ +#define VCPUOP_register_runstate_memory_area 5 +struct vcpu_register_runstate_memory_area { + union { + struct vcpu_runstate_info *v; + uint64_t p; + } addr; +}; + +/* + * Set or stop a VCPU's periodic timer. Every VCPU has one periodic timer + * which can be set via these commands. Periods smaller than one millisecond + * may not be supported. + */ +#define VCPUOP_set_periodic_timer 6 /* arg == vcpu_set_periodic_timer_t */ +#define VCPUOP_stop_periodic_timer 7 /* arg == NULL */ +struct vcpu_set_periodic_timer { + uint64_t period_ns; +}; + +/* + * Set or stop a VCPU's single-shot timer. Every VCPU has one single-shot + * timer which can be set via these commands. + */ +#define VCPUOP_set_singleshot_timer 8 /* arg == vcpu_set_singleshot_timer_t */ +#define VCPUOP_stop_singleshot_timer 9 /* arg == NULL */ +struct vcpu_set_singleshot_timer { + uint64_t timeout_abs_ns; + uint32_t flags; /* VCPU_SSHOTTMR_??? */ +}; + +/* Flags to VCPUOP_set_singleshot_timer. */ + /* Require the timeout to be in the future (return -ETIME if it's passed). */ +#define _VCPU_SSHOTTMR_future (0) +#define VCPU_SSHOTTMR_future (1U << _VCPU_SSHOTTMR_future) + +/* + * Register a memory location in the guest address space for the + * vcpu_info structure. This allows the guest to place the vcpu_info + * structure in a convenient place, such as in a per-cpu data area. + * The pointer need not be page aligned, but the structure must not + * cross a page boundary. + */ +#define VCPUOP_register_vcpu_info 10 /* arg == struct vcpu_info */ +struct vcpu_register_vcpu_info { + uint32_t mfn; /* mfn of page to place vcpu_info */ + uint32_t offset; /* offset within page */ +}; + +#endif /* __XEN_PUBLIC_VCPU_H__ */ diff --git a/include/xen/interface/version.h b/include/xen/interface/version.h new file mode 100644 index 0000000..453235e --- /dev/null +++ b/include/xen/interface/version.h @@ -0,0 +1,60 @@ +/****************************************************************************** + * version.h + * + * Xen version, type, and compile information. + * + * Copyright (c) 2005, Nguyen Anh Quynh <aquynh@gmail.com> + * Copyright (c) 2005, Keir Fraser <keir@xensource.com> + */ + +#ifndef __XEN_PUBLIC_VERSION_H__ +#define __XEN_PUBLIC_VERSION_H__ + +/* NB. All ops return zero on success, except XENVER_version. */ + +/* arg == NULL; returns major:minor (16:16). */ +#define XENVER_version 0 + +/* arg == xen_extraversion_t. */ +#define XENVER_extraversion 1 +struct xen_extraversion { + char extraversion[16]; +}; +#define XEN_EXTRAVERSION_LEN (sizeof(struct xen_extraversion)) + +/* arg == xen_compile_info_t. */ +#define XENVER_compile_info 2 +struct xen_compile_info { + char compiler[64]; + char compile_by[16]; + char compile_domain[32]; + char compile_date[32]; +}; + +#define XENVER_capabilities 3 +struct xen_capabilities_info { + char info[1024]; +}; +#define XEN_CAPABILITIES_INFO_LEN (sizeof(struct xen_capabilities_info)) + +#define XENVER_changeset 4 +struct xen_changeset_info { + char info[64]; +}; +#define XEN_CHANGESET_INFO_LEN (sizeof(struct xen_changeset_info)) + +#define XENVER_platform_parameters 5 +struct xen_platform_parameters { + unsigned long virt_start; +}; + +#define XENVER_get_features 6 +struct xen_feature_info { + unsigned int submap_idx; /* IN: which 32-bit submap to return */ + uint32_t submap; /* OUT: 32-bit submap */ +}; + +/* Declares the features reported by XENVER_get_features. */ +#include "features.h" + +#endif /* __XEN_PUBLIC_VERSION_H__ */ diff --git a/include/xen/interface/xen.h b/include/xen/interface/xen.h new file mode 100644 index 0000000..518a5bf --- /dev/null +++ b/include/xen/interface/xen.h @@ -0,0 +1,447 @@ +/****************************************************************************** + * xen.h + * + * Guest OS interface to Xen. + * + * Copyright (c) 2004, K A Fraser + */ + +#ifndef __XEN_PUBLIC_XEN_H__ +#define __XEN_PUBLIC_XEN_H__ + +#include <asm/xen/interface.h> + +/* + * XEN "SYSTEM CALLS" (a.k.a. HYPERCALLS). + */ + +/* + * x86_32: EAX = vector; EBX, ECX, EDX, ESI, EDI = args 1, 2, 3, 4, 5. + * EAX = return value + * (argument registers may be clobbered on return) + * x86_64: RAX = vector; RDI, RSI, RDX, R10, R8, R9 = args 1, 2, 3, 4, 5, 6. + * RAX = return value + * (argument registers not clobbered on return; RCX, R11 are) + */ +#define __HYPERVISOR_set_trap_table 0 +#define __HYPERVISOR_mmu_update 1 +#define __HYPERVISOR_set_gdt 2 +#define __HYPERVISOR_stack_switch 3 +#define __HYPERVISOR_set_callbacks 4 +#define __HYPERVISOR_fpu_taskswitch 5 +#define __HYPERVISOR_sched_op 6 +#define __HYPERVISOR_dom0_op 7 +#define __HYPERVISOR_set_debugreg 8 +#define __HYPERVISOR_get_debugreg 9 +#define __HYPERVISOR_update_descriptor 10 +#define __HYPERVISOR_memory_op 12 +#define __HYPERVISOR_multicall 13 +#define __HYPERVISOR_update_va_mapping 14 +#define __HYPERVISOR_set_timer_op 15 +#define __HYPERVISOR_event_channel_op_compat 16 +#define __HYPERVISOR_xen_version 17 +#define __HYPERVISOR_console_io 18 +#define __HYPERVISOR_physdev_op_compat 19 +#define __HYPERVISOR_grant_table_op 20 +#define __HYPERVISOR_vm_assist 21 +#define __HYPERVISOR_update_va_mapping_otherdomain 22 +#define __HYPERVISOR_iret 23 /* x86 only */ +#define __HYPERVISOR_vcpu_op 24 +#define __HYPERVISOR_set_segment_base 25 /* x86/64 only */ +#define __HYPERVISOR_mmuext_op 26 +#define __HYPERVISOR_acm_op 27 +#define __HYPERVISOR_nmi_op 28 +#define __HYPERVISOR_sched_op_new 29 +#define __HYPERVISOR_callback_op 30 +#define __HYPERVISOR_xenoprof_op 31 +#define __HYPERVISOR_event_channel_op 32 +#define __HYPERVISOR_physdev_op 33 +#define __HYPERVISOR_hvm_op 34 + +/* + * VIRTUAL INTERRUPTS + * + * Virtual interrupts that a guest OS may receive from Xen. + */ +#define VIRQ_TIMER 0 /* Timebase update, and/or requested timeout. */ +#define VIRQ_DEBUG 1 /* Request guest to dump debug info. */ +#define VIRQ_CONSOLE 2 /* (DOM0) Bytes received on emergency console. */ +#define VIRQ_DOM_EXC 3 /* (DOM0) Exceptional event for some domain. */ +#define VIRQ_DEBUGGER 6 /* (DOM0) A domain has paused for debugging. */ +#define NR_VIRQS 8 + +/* + * MMU-UPDATE REQUESTS + * + * HYPERVISOR_mmu_update() accepts a list of (ptr, val) pairs. + * A foreigndom (FD) can be specified (or DOMID_SELF for none). + * Where the FD has some effect, it is described below. + * ptr[1:0] specifies the appropriate MMU_* command. + * + * ptr[1:0] == MMU_NORMAL_PT_UPDATE: + * Updates an entry in a page table. If updating an L1 table, and the new + * table entry is valid/present, the mapped frame must belong to the FD, if + * an FD has been specified. If attempting to map an I/O page then the + * caller assumes the privilege of the FD. + * FD == DOMID_IO: Permit /only/ I/O mappings, at the priv level of the caller. + * FD == DOMID_XEN: Map restricted areas of Xen's heap space. + * ptr[:2] -- Machine address of the page-table entry to modify. + * val -- Value to write. + * + * ptr[1:0] == MMU_MACHPHYS_UPDATE: + * Updates an entry in the machine->pseudo-physical mapping table. + * ptr[:2] -- Machine address within the frame whose mapping to modify. + * The frame must belong to the FD, if one is specified. + * val -- Value to write into the mapping entry. + */ +#define MMU_NORMAL_PT_UPDATE 0 /* checked '*ptr = val'. ptr is MA. */ +#define MMU_MACHPHYS_UPDATE 1 /* ptr = MA of frame to modify entry for */ + +/* + * MMU EXTENDED OPERATIONS + * + * HYPERVISOR_mmuext_op() accepts a list of mmuext_op structures. + * A foreigndom (FD) can be specified (or DOMID_SELF for none). + * Where the FD has some effect, it is described below. + * + * cmd: MMUEXT_(UN)PIN_*_TABLE + * mfn: Machine frame number to be (un)pinned as a p.t. page. + * The frame must belong to the FD, if one is specified. + * + * cmd: MMUEXT_NEW_BASEPTR + * mfn: Machine frame number of new page-table base to install in MMU. + * + * cmd: MMUEXT_NEW_USER_BASEPTR [x86/64 only] + * mfn: Machine frame number of new page-table base to install in MMU + * when in user space. + * + * cmd: MMUEXT_TLB_FLUSH_LOCAL + * No additional arguments. Flushes local TLB. + * + * cmd: MMUEXT_INVLPG_LOCAL + * linear_addr: Linear address to be flushed from the local TLB. + * + * cmd: MMUEXT_TLB_FLUSH_MULTI + * vcpumask: Pointer to bitmap of VCPUs to be flushed. + * + * cmd: MMUEXT_INVLPG_MULTI + * linear_addr: Linear address to be flushed. + * vcpumask: Pointer to bitmap of VCPUs to be flushed. + * + * cmd: MMUEXT_TLB_FLUSH_ALL + * No additional arguments. Flushes all VCPUs' TLBs. + * + * cmd: MMUEXT_INVLPG_ALL + * linear_addr: Linear address to be flushed from all VCPUs' TLBs. + * + * cmd: MMUEXT_FLUSH_CACHE + * No additional arguments. Writes back and flushes cache contents. + * + * cmd: MMUEXT_SET_LDT + * linear_addr: Linear address of LDT base (NB. must be page-aligned). + * nr_ents: Number of entries in LDT. + */ +#define MMUEXT_PIN_L1_TABLE 0 +#define MMUEXT_PIN_L2_TABLE 1 +#define MMUEXT_PIN_L3_TABLE 2 +#define MMUEXT_PIN_L4_TABLE 3 +#define MMUEXT_UNPIN_TABLE 4 +#define MMUEXT_NEW_BASEPTR 5 +#define MMUEXT_TLB_FLUSH_LOCAL 6 +#define MMUEXT_INVLPG_LOCAL 7 +#define MMUEXT_TLB_FLUSH_MULTI 8 +#define MMUEXT_INVLPG_MULTI 9 +#define MMUEXT_TLB_FLUSH_ALL 10 +#define MMUEXT_INVLPG_ALL 11 +#define MMUEXT_FLUSH_CACHE 12 +#define MMUEXT_SET_LDT 13 +#define MMUEXT_NEW_USER_BASEPTR 15 + +#ifndef __ASSEMBLY__ +struct mmuext_op { + unsigned int cmd; + union { + /* [UN]PIN_TABLE, NEW_BASEPTR, NEW_USER_BASEPTR */ + unsigned long mfn; + /* INVLPG_LOCAL, INVLPG_ALL, SET_LDT */ + unsigned long linear_addr; + } arg1; + union { + /* SET_LDT */ + unsigned int nr_ents; + /* TLB_FLUSH_MULTI, INVLPG_MULTI */ + void *vcpumask; + } arg2; +}; +DEFINE_GUEST_HANDLE_STRUCT(mmuext_op); +#endif + +/* These are passed as 'flags' to update_va_mapping. They can be ORed. */ +/* When specifying UVMF_MULTI, also OR in a pointer to a CPU bitmap. */ +/* UVMF_LOCAL is merely UVMF_MULTI with a NULL bitmap pointer. */ +#define UVMF_NONE (0UL<<0) /* No flushing at all. */ +#define UVMF_TLB_FLUSH (1UL<<0) /* Flush entire TLB(s). */ +#define UVMF_INVLPG (2UL<<0) /* Flush only one entry. */ +#define UVMF_FLUSHTYPE_MASK (3UL<<0) +#define UVMF_MULTI (0UL<<2) /* Flush subset of TLBs. */ +#define UVMF_LOCAL (0UL<<2) /* Flush local TLB. */ +#define UVMF_ALL (1UL<<2) /* Flush all TLBs. */ + +/* + * Commands to HYPERVISOR_console_io(). + */ +#define CONSOLEIO_write 0 +#define CONSOLEIO_read 1 + +/* + * Commands to HYPERVISOR_vm_assist(). + */ +#define VMASST_CMD_enable 0 +#define VMASST_CMD_disable 1 +#define VMASST_TYPE_4gb_segments 0 +#define VMASST_TYPE_4gb_segments_notify 1 +#define VMASST_TYPE_writable_pagetables 2 +#define VMASST_TYPE_pae_extended_cr3 3 +#define MAX_VMASST_TYPE 3 + +#ifndef __ASSEMBLY__ + +typedef uint16_t domid_t; + +/* Domain ids >= DOMID_FIRST_RESERVED cannot be used for ordinary domains. */ +#define DOMID_FIRST_RESERVED (0x7FF0U) + +/* DOMID_SELF is used in certain contexts to refer to oneself. */ +#define DOMID_SELF (0x7FF0U) + +/* + * DOMID_IO is used to restrict page-table updates to mapping I/O memory. + * Although no Foreign Domain need be specified to map I/O pages, DOMID_IO + * is useful to ensure that no mappings to the OS's own heap are accidentally + * installed. (e.g., in Linux this could cause havoc as reference counts + * aren't adjusted on the I/O-mapping code path). + * This only makes sense in MMUEXT_SET_FOREIGNDOM, but in that context can + * be specified by any calling domain. + */ +#define DOMID_IO (0x7FF1U) + +/* + * DOMID_XEN is used to allow privileged domains to map restricted parts of + * Xen's heap space (e.g., the machine_to_phys table). + * This only makes sense in MMUEXT_SET_FOREIGNDOM, and is only permitted if + * the caller is privileged. + */ +#define DOMID_XEN (0x7FF2U) + +/* + * Send an array of these to HYPERVISOR_mmu_update(). + * NB. The fields are natural pointer/address size for this architecture. + */ +struct mmu_update { + uint64_t ptr; /* Machine address of PTE. */ + uint64_t val; /* New contents of PTE. */ +}; +DEFINE_GUEST_HANDLE_STRUCT(mmu_update); + +/* + * Send an array of these to HYPERVISOR_multicall(). + * NB. The fields are natural register size for this architecture. + */ +struct multicall_entry { + unsigned long op; + long result; + unsigned long args[6]; +}; +DEFINE_GUEST_HANDLE_STRUCT(multicall_entry); + +/* + * Event channel endpoints per domain: + * 1024 if a long is 32 bits; 4096 if a long is 64 bits. + */ +#define NR_EVENT_CHANNELS (sizeof(unsigned long) * sizeof(unsigned long) * 64) + +struct vcpu_time_info { + /* + * Updates to the following values are preceded and followed + * by an increment of 'version'. The guest can therefore + * detect updates by looking for changes to 'version'. If the + * least-significant bit of the version number is set then an + * update is in progress and the guest must wait to read a + * consistent set of values. The correct way to interact with + * the version number is similar to Linux's seqlock: see the + * implementations of read_seqbegin/read_seqretry. + */ + uint32_t version; + uint32_t pad0; + uint64_t tsc_timestamp; /* TSC at last update of time vals. */ + uint64_t system_time; /* Time, in nanosecs, since boot. */ + /* + * Current system time: + * system_time + ((tsc - tsc_timestamp) << tsc_shift) * tsc_to_system_mul + * CPU frequency (Hz): + * ((10^9 << 32) / tsc_to_system_mul) >> tsc_shift + */ + uint32_t tsc_to_system_mul; + int8_t tsc_shift; + int8_t pad1[3]; +}; /* 32 bytes */ + +struct vcpu_info { + /* + * 'evtchn_upcall_pending' is written non-zero by Xen to indicate + * a pending notification for a particular VCPU. It is then cleared + * by the guest OS /before/ checking for pending work, thus avoiding + * a set-and-check race. Note that the mask is only accessed by Xen + * on the CPU that is currently hosting the VCPU. This means that the + * pending and mask flags can be updated by the guest without special + * synchronisation (i.e., no need for the x86 LOCK prefix). + * This may seem suboptimal because if the pending flag is set by + * a different CPU then an IPI may be scheduled even when the mask + * is set. However, note: + * 1. The task of 'interrupt holdoff' is covered by the per-event- + * channel mask bits. A 'noisy' event that is continually being + * triggered can be masked at source at this very precise + * granularity. + * 2. The main purpose of the per-VCPU mask is therefore to restrict + * reentrant execution: whether for concurrency control, or to + * prevent unbounded stack usage. Whatever the purpose, we expect + * that the mask will be asserted only for short periods at a time, + * and so the likelihood of a 'spurious' IPI is suitably small. + * The mask is read before making an event upcall to the guest: a + * non-zero mask therefore guarantees that the VCPU will not receive + * an upcall activation. The mask is cleared when the VCPU requests + * to block: this avoids wakeup-waiting races. + */ + uint8_t evtchn_upcall_pending; + uint8_t evtchn_upcall_mask; + unsigned long evtchn_pending_sel; + struct arch_vcpu_info arch; + struct vcpu_time_info time; +}; /* 64 bytes (x86) */ + +/* + * Xen/kernel shared data -- pointer provided in start_info. + * NB. We expect that this struct is smaller than a page. + */ +struct shared_info { + struct vcpu_info vcpu_info[MAX_VIRT_CPUS]; + + /* + * A domain can create "event channels" on which it can send and receive + * asynchronous event notifications. There are three classes of event that + * are delivered by this mechanism: + * 1. Bi-directional inter- and intra-domain connections. Domains must + * arrange out-of-band to set up a connection (usually by allocating + * an unbound 'listener' port and avertising that via a storage service + * such as xenstore). + * 2. Physical interrupts. A domain with suitable hardware-access + * privileges can bind an event-channel port to a physical interrupt + * source. + * 3. Virtual interrupts ('events'). A domain can bind an event-channel + * port to a virtual interrupt source, such as the virtual-timer + * device or the emergency console. + * + * Event channels are addressed by a "port index". Each channel is + * associated with two bits of information: + * 1. PENDING -- notifies the domain that there is a pending notification + * to be processed. This bit is cleared by the guest. + * 2. MASK -- if this bit is clear then a 0->1 transition of PENDING + * will cause an asynchronous upcall to be scheduled. This bit is only + * updated by the guest. It is read-only within Xen. If a channel + * becomes pending while the channel is masked then the 'edge' is lost + * (i.e., when the channel is unmasked, the guest must manually handle + * pending notifications as no upcall will be scheduled by Xen). + * + * To expedite scanning of pending notifications, any 0->1 pending + * transition on an unmasked channel causes a corresponding bit in a + * per-vcpu selector word to be set. Each bit in the selector covers a + * 'C long' in the PENDING bitfield array. + */ + unsigned long evtchn_pending[sizeof(unsigned long) * 8]; + unsigned long evtchn_mask[sizeof(unsigned long) * 8]; + + /* + * Wallclock time: updated only by control software. Guests should base + * their gettimeofday() syscall on this wallclock-base value. + */ + uint32_t wc_version; /* Version counter: see vcpu_time_info_t. */ + uint32_t wc_sec; /* Secs 00:00:00 UTC, Jan 1, 1970. */ + uint32_t wc_nsec; /* Nsecs 00:00:00 UTC, Jan 1, 1970. */ + + struct arch_shared_info arch; + +}; + +/* + * Start-of-day memory layout for the initial domain (DOM0): + * 1. The domain is started within contiguous virtual-memory region. + * 2. The contiguous region begins and ends on an aligned 4MB boundary. + * 3. The region start corresponds to the load address of the OS image. + * If the load address is not 4MB aligned then the address is rounded down. + * 4. This the order of bootstrap elements in the initial virtual region: + * a. relocated kernel image + * b. initial ram disk [mod_start, mod_len] + * c. list of allocated page frames [mfn_list, nr_pages] + * d. start_info_t structure [register ESI (x86)] + * e. bootstrap page tables [pt_base, CR3 (x86)] + * f. bootstrap stack [register ESP (x86)] + * 5. Bootstrap elements are packed together, but each is 4kB-aligned. + * 6. The initial ram disk may be omitted. + * 7. The list of page frames forms a contiguous 'pseudo-physical' memory + * layout for the domain. In particular, the bootstrap virtual-memory + * region is a 1:1 mapping to the first section of the pseudo-physical map. + * 8. All bootstrap elements are mapped read-writable for the guest OS. The + * only exception is the bootstrap page table, which is mapped read-only. + * 9. There is guaranteed to be at least 512kB padding after the final + * bootstrap element. If necessary, the bootstrap virtual region is + * extended by an extra 4MB to ensure this. + */ + +#define MAX_GUEST_CMDLINE 1024 +struct start_info { + /* THE FOLLOWING ARE FILLED IN BOTH ON INITIAL BOOT AND ON RESUME. */ + char magic[32]; /* "xen-<version>-<platform>". */ + unsigned long nr_pages; /* Total pages allocated to this domain. */ + unsigned long shared_info; /* MACHINE address of shared info struct. */ + uint32_t flags; /* SIF_xxx flags. */ + unsigned long store_mfn; /* MACHINE page number of shared page. */ + uint32_t store_evtchn; /* Event channel for store communication. */ + union { + struct { + unsigned long mfn; /* MACHINE page number of console page. */ + uint32_t evtchn; /* Event channel for console page. */ + } domU; + struct { + uint32_t info_off; /* Offset of console_info struct. */ + uint32_t info_size; /* Size of console_info struct from start.*/ + } dom0; + } console; + /* THE FOLLOWING ARE ONLY FILLED IN ON INITIAL BOOT (NOT RESUME). */ + unsigned long pt_base; /* VIRTUAL address of page directory. */ + unsigned long nr_pt_frames; /* Number of bootstrap p.t. frames. */ + unsigned long mfn_list; /* VIRTUAL address of page-frame list. */ + unsigned long mod_start; /* VIRTUAL address of pre-loaded module. */ + unsigned long mod_len; /* Size (bytes) of pre-loaded module. */ + int8_t cmd_line[MAX_GUEST_CMDLINE]; +}; + +/* These flags are passed in the 'flags' field of start_info_t. */ +#define SIF_PRIVILEGED (1<<0) /* Is the domain privileged? */ +#define SIF_INITDOMAIN (1<<1) /* Is this the initial control domain? */ + +typedef uint64_t cpumap_t; + +typedef uint8_t xen_domain_handle_t[16]; + +/* Turn a plain number into a C unsigned long constant. */ +#define __mk_unsigned_long(x) x ## UL +#define mk_unsigned_long(x) __mk_unsigned_long(x) + +#else /* __ASSEMBLY__ */ + +/* In assembly code we cannot use C numeric constant suffixes. */ +#define mk_unsigned_long(x) x + +#endif /* !__ASSEMBLY__ */ + +#endif /* __XEN_PUBLIC_XEN_H__ */ diff --git a/include/xen/page.h b/include/xen/page.h new file mode 100644 index 0000000..1df6c19 --- /dev/null +++ b/include/xen/page.h @@ -0,0 +1,179 @@ +#ifndef __XEN_PAGE_H +#define __XEN_PAGE_H + +#include <linux/pfn.h> + +#include <asm/uaccess.h> + +#include <xen/features.h> + +#ifdef CONFIG_X86_PAE +/* Xen machine address */ +typedef struct xmaddr { + unsigned long long maddr; +} xmaddr_t; + +/* Xen pseudo-physical address */ +typedef struct xpaddr { + unsigned long long paddr; +} xpaddr_t; +#else +/* Xen machine address */ +typedef struct xmaddr { + unsigned long maddr; +} xmaddr_t; + +/* Xen pseudo-physical address */ +typedef struct xpaddr { + unsigned long paddr; +} xpaddr_t; +#endif + +#define XMADDR(x) ((xmaddr_t) { .maddr = (x) }) +#define XPADDR(x) ((xpaddr_t) { .paddr = (x) }) + +/**** MACHINE <-> PHYSICAL CONVERSION MACROS ****/ +#define INVALID_P2M_ENTRY (~0UL) +#define FOREIGN_FRAME_BIT (1UL<<31) +#define FOREIGN_FRAME(m) ((m) | FOREIGN_FRAME_BIT) + +extern unsigned long *phys_to_machine_mapping; + +static inline unsigned long pfn_to_mfn(unsigned long pfn) +{ + if (xen_feature(XENFEAT_auto_translated_physmap)) + return pfn; + + return phys_to_machine_mapping[(unsigned int)(pfn)] & + ~FOREIGN_FRAME_BIT; +} + +static inline int phys_to_machine_mapping_valid(unsigned long pfn) +{ + if (xen_feature(XENFEAT_auto_translated_physmap)) + return 1; + + return (phys_to_machine_mapping[pfn] != INVALID_P2M_ENTRY); +} + +static inline unsigned long mfn_to_pfn(unsigned long mfn) +{ + unsigned long pfn; + + if (xen_feature(XENFEAT_auto_translated_physmap)) + return mfn; + +#if 0 + if (unlikely((mfn >> machine_to_phys_order) != 0)) + return max_mapnr; +#endif + + pfn = 0; + /* + * The array access can fail (e.g., device space beyond end of RAM). + * In such cases it doesn't matter what we return (we return garbage), + * but we must handle the fault without crashing! + */ + __get_user(pfn, &machine_to_phys_mapping[mfn]); + + return pfn; +} + +static inline xmaddr_t phys_to_machine(xpaddr_t phys) +{ + unsigned offset = phys.paddr & ~PAGE_MASK; + return XMADDR(PFN_PHYS((u64)pfn_to_mfn(PFN_DOWN(phys.paddr))) | offset); +} + +static inline xpaddr_t machine_to_phys(xmaddr_t machine) +{ + unsigned offset = machine.maddr & ~PAGE_MASK; + return XPADDR(PFN_PHYS((u64)mfn_to_pfn(PFN_DOWN(machine.maddr))) | offset); +} + +/* + * We detect special mappings in one of two ways: + * 1. If the MFN is an I/O page then Xen will set the m2p entry + * to be outside our maximum possible pseudophys range. + * 2. If the MFN belongs to a different domain then we will certainly + * not have MFN in our p2m table. Conversely, if the page is ours, + * then we'll have p2m(m2p(MFN))==MFN. + * If we detect a special mapping then it doesn't have a 'struct page'. + * We force !pfn_valid() by returning an out-of-range pointer. + * + * NB. These checks require that, for any MFN that is not in our reservation, + * there is no PFN such that p2m(PFN) == MFN. Otherwise we can get confused if + * we are foreign-mapping the MFN, and the other domain as m2p(MFN) == PFN. + * Yikes! Various places must poke in INVALID_P2M_ENTRY for safety. + * + * NB2. When deliberately mapping foreign pages into the p2m table, you *must* + * use FOREIGN_FRAME(). This will cause pte_pfn() to choke on it, as we + * require. In all the cases we care about, the FOREIGN_FRAME bit is + * masked (e.g., pfn_to_mfn()) so behaviour there is correct. + */ +static inline unsigned long mfn_to_local_pfn(unsigned long mfn) +{ + extern unsigned long max_mapnr; + unsigned long pfn = mfn_to_pfn(mfn); + if ((pfn < max_mapnr) + && !xen_feature(XENFEAT_auto_translated_physmap) + && (phys_to_machine_mapping[pfn] != mfn)) + return max_mapnr; /* force !pfn_valid() */ + return pfn; +} + +static inline void set_phys_to_machine(unsigned long pfn, unsigned long mfn) +{ + if (xen_feature(XENFEAT_auto_translated_physmap)) { + BUG_ON(pfn != mfn && mfn != INVALID_P2M_ENTRY); + return; + } + phys_to_machine_mapping[pfn] = mfn; +} + +/* VIRT <-> MACHINE conversion */ +#define virt_to_machine(v) (phys_to_machine(XPADDR(__pa(v)))) +#define virt_to_mfn(v) (pfn_to_mfn(PFN_DOWN(__pa(v)))) +#define mfn_to_virt(m) (__va(mfn_to_pfn(m) << PAGE_SHIFT)) + +#ifdef CONFIG_X86_PAE +#define pte_mfn(_pte) (((_pte).pte_low >> PAGE_SHIFT) | \ + (((_pte).pte_high & 0xfff) << (32-PAGE_SHIFT))) + +static inline pte_t mfn_pte(unsigned long page_nr, pgprot_t pgprot) +{ + pte_t pte; + + pte.pte_high = (page_nr >> (32 - PAGE_SHIFT)) | + (pgprot_val(pgprot) >> 32); + pte.pte_high &= (__supported_pte_mask >> 32); + pte.pte_low = ((page_nr << PAGE_SHIFT) | pgprot_val(pgprot)); + pte.pte_low &= __supported_pte_mask; + + return pte; +} + +static inline unsigned long long pte_val_ma(pte_t x) +{ + return ((unsigned long long)x.pte_high << 32) | x.pte_low; +} +#define pmd_val_ma(v) ((v).pmd) +#define pud_val_ma(v) ((v).pgd.pgd) +#define __pte_ma(x) ((pte_t) { .pte_low = (x), .pte_high = (x)>>32 } ) +#define __pmd_ma(x) ((pmd_t) { (x) } ) +#else /* !X86_PAE */ +#define pte_mfn(_pte) ((_pte).pte_low >> PAGE_SHIFT) +#define mfn_pte(pfn, prot) __pte_ma(((pfn) << PAGE_SHIFT) | pgprot_val(prot)) +#define pte_val_ma(x) ((x).pte_low) +#define pmd_val_ma(v) ((v).pud.pgd.pgd) +#define __pte_ma(x) ((pte_t) { (x) } ) +#endif /* CONFIG_X86_PAE */ + +#define pgd_val_ma(x) ((x).pgd) + + +xmaddr_t arbitrary_virt_to_machine(unsigned long address); +void make_lowmem_page_readonly(void *vaddr); +void make_lowmem_page_readwrite(void *vaddr); + +#endif /* __XEN_PAGE_H */ diff --git a/include/xen/xenbus.h b/include/xen/xenbus.h new file mode 100644 index 0000000..6f7c290 --- /dev/null +++ b/include/xen/xenbus.h @@ -0,0 +1,234 @@ +/****************************************************************************** + * xenbus.h + * + * Talks to Xen Store to figure out what devices we have. + * + * Copyright (C) 2005 Rusty Russell, IBM Corporation + * Copyright (C) 2005 XenSource Ltd. + * + * 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; or, when distributed + * separately from the Linux kernel or incorporated into other + * software packages, subject to the following license: + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this source file (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef _XEN_XENBUS_H +#define _XEN_XENBUS_H + +#include <linux/device.h> +#include <linux/notifier.h> +#include <linux/mutex.h> +#include <linux/completion.h> +#include <linux/init.h> +#include <xen/interface/xen.h> +#include <xen/interface/grant_table.h> +#include <xen/interface/io/xenbus.h> +#include <xen/interface/io/xs_wire.h> + +/* Register callback to watch this node. */ +struct xenbus_watch +{ + struct list_head list; + + /* Path being watched. */ + const char *node; + + /* Callback (executed in a process context with no locks held). */ + void (*callback)(struct xenbus_watch *, + const char **vec, unsigned int len); +}; + + +/* A xenbus device. */ +struct xenbus_device { + const char *devicetype; + const char *nodename; + const char *otherend; + int otherend_id; + struct xenbus_watch otherend_watch; + struct device dev; + enum xenbus_state state; + struct completion down; +}; + +static inline struct xenbus_device *to_xenbus_device(struct device *dev) +{ + return container_of(dev, struct xenbus_device, dev); +} + +struct xenbus_device_id +{ + /* .../device/<device_type>/<identifier> */ + char devicetype[32]; /* General class of device. */ +}; + +/* A xenbus driver. */ +struct xenbus_driver { + char *name; + struct module *owner; + const struct xenbus_device_id *ids; + int (*probe)(struct xenbus_device *dev, + const struct xenbus_device_id *id); + void (*otherend_changed)(struct xenbus_device *dev, + enum xenbus_state backend_state); + int (*remove)(struct xenbus_device *dev); + int (*suspend)(struct xenbus_device *dev); + int (*suspend_cancel)(struct xenbus_device *dev); + int (*resume)(struct xenbus_device *dev); + int (*uevent)(struct xenbus_device *, char **, int, char *, int); + struct device_driver driver; + int (*read_otherend_details)(struct xenbus_device *dev); +}; + +static inline struct xenbus_driver *to_xenbus_driver(struct device_driver *drv) +{ + return container_of(drv, struct xenbus_driver, driver); +} + +int __must_check __xenbus_register_frontend(struct xenbus_driver *drv, + struct module *owner, + const char *mod_name); + +static inline int __must_check +xenbus_register_frontend(struct xenbus_driver *drv) +{ + WARN_ON(drv->owner != THIS_MODULE); + return __xenbus_register_frontend(drv, THIS_MODULE, KBUILD_MODNAME); +} + +int __must_check __xenbus_register_backend(struct xenbus_driver *drv, + struct module *owner, + const char *mod_name); +static inline int __must_check +xenbus_register_backend(struct xenbus_driver *drv) +{ + WARN_ON(drv->owner != THIS_MODULE); + return __xenbus_register_backend(drv, THIS_MODULE, KBUILD_MODNAME); +} + +void xenbus_unregister_driver(struct xenbus_driver *drv); + +struct xenbus_transaction +{ + u32 id; +}; + +/* Nil transaction ID. */ +#define XBT_NIL ((struct xenbus_transaction) { 0 }) + +int __init xenbus_dev_init(void); + +char **xenbus_directory(struct xenbus_transaction t, + const char *dir, const char *node, unsigned int *num); +void *xenbus_read(struct xenbus_transaction t, + const char *dir, const char *node, unsigned int *len); +int xenbus_write(struct xenbus_transaction t, + const char *dir, const char *node, const char *string); +int xenbus_mkdir(struct xenbus_transaction t, + const char *dir, const char *node); +int xenbus_exists(struct xenbus_transaction t, + const char *dir, const char *node); +int xenbus_rm(struct xenbus_transaction t, const char *dir, const char *node); +int xenbus_transaction_start(struct xenbus_transaction *t); +int xenbus_transaction_end(struct xenbus_transaction t, int abort); + +/* Single read and scanf: returns -errno or num scanned if > 0. */ +int xenbus_scanf(struct xenbus_transaction t, + const char *dir, const char *node, const char *fmt, ...) + __attribute__((format(scanf, 4, 5))); + +/* Single printf and write: returns -errno or 0. */ +int xenbus_printf(struct xenbus_transaction t, + const char *dir, const char *node, const char *fmt, ...) + __attribute__((format(printf, 4, 5))); + +/* Generic read function: NULL-terminated triples of name, + * sprintf-style type string, and pointer. Returns 0 or errno.*/ +int xenbus_gather(struct xenbus_transaction t, const char *dir, ...); + +/* notifer routines for when the xenstore comes up */ +extern int xenstored_ready; +int register_xenstore_notifier(struct notifier_block *nb); +void unregister_xenstore_notifier(struct notifier_block *nb); + +int register_xenbus_watch(struct xenbus_watch *watch); +void unregister_xenbus_watch(struct xenbus_watch *watch); +void xs_suspend(void); +void xs_resume(void); +void xs_suspend_cancel(void); + +/* Used by xenbus_dev to borrow kernel's store connection. */ +void *xenbus_dev_request_and_reply(struct xsd_sockmsg *msg); + +struct work_struct; + +/* Prepare for domain suspend: then resume or cancel the suspend. */ +void xenbus_suspend(void); +void xenbus_resume(void); +void xenbus_probe(struct work_struct *); +void xenbus_suspend_cancel(void); + +#define XENBUS_IS_ERR_READ(str) ({ \ + if (!IS_ERR(str) && strlen(str) == 0) { \ + kfree(str); \ + str = ERR_PTR(-ERANGE); \ + } \ + IS_ERR(str); \ +}) + +#define XENBUS_EXIST_ERR(err) ((err) == -ENOENT || (err) == -ERANGE) + +int xenbus_watch_path(struct xenbus_device *dev, const char *path, + struct xenbus_watch *watch, + void (*callback)(struct xenbus_watch *, + const char **, unsigned int)); +int xenbus_watch_pathfmt(struct xenbus_device *dev, struct xenbus_watch *watch, + void (*callback)(struct xenbus_watch *, + const char **, unsigned int), + const char *pathfmt, ...) + __attribute__ ((format (printf, 4, 5))); + +int xenbus_switch_state(struct xenbus_device *dev, enum xenbus_state new_state); +int xenbus_grant_ring(struct xenbus_device *dev, unsigned long ring_mfn); +int xenbus_map_ring_valloc(struct xenbus_device *dev, + int gnt_ref, void **vaddr); +int xenbus_map_ring(struct xenbus_device *dev, int gnt_ref, + grant_handle_t *handle, void *vaddr); + +int xenbus_unmap_ring_vfree(struct xenbus_device *dev, void *vaddr); +int xenbus_unmap_ring(struct xenbus_device *dev, + grant_handle_t handle, void *vaddr); + +int xenbus_alloc_evtchn(struct xenbus_device *dev, int *port); +int xenbus_bind_evtchn(struct xenbus_device *dev, int remote_port, int *port); +int xenbus_free_evtchn(struct xenbus_device *dev, int port); + +enum xenbus_state xenbus_read_driver_state(const char *path); + +void xenbus_dev_error(struct xenbus_device *dev, int err, const char *fmt, ...); +void xenbus_dev_fatal(struct xenbus_device *dev, int err, const char *fmt, ...); + +const char *xenbus_strstate(enum xenbus_state state); +int xenbus_dev_is_online(struct xenbus_device *dev); +int xenbus_frontend_closed(struct xenbus_device *dev); + +#endif /* _XEN_XENBUS_H */ diff --git a/kernel/cpuset.c b/kernel/cpuset.c index b4796d8..57e6448 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -516,7 +516,7 @@ static void cpuset_release_agent(const char *pathbuf) envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; envp[i] = NULL; - call_usermodehelper(argv[0], argv, envp, 0); + call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC); kfree(pathbuf); } diff --git a/kernel/kmod.c b/kernel/kmod.c index 4d32eb0..78d365c 100644 --- a/kernel/kmod.c +++ b/kernel/kmod.c @@ -119,9 +119,10 @@ struct subprocess_info { char **argv; char **envp; struct key *ring; - int wait; + enum umh_wait wait; int retval; struct file *stdin; + void (*cleanup)(char **argv, char **envp); }; /* @@ -180,6 +181,14 @@ static int ____call_usermodehelper(void *data) do_exit(0); } +void call_usermodehelper_freeinfo(struct subprocess_info *info) +{ + if (info->cleanup) + (*info->cleanup)(info->argv, info->envp); + kfree(info); +} +EXPORT_SYMBOL(call_usermodehelper_freeinfo); + /* Keventd can't block, but this (a child) can. */ static int wait_for_helper(void *data) { @@ -216,8 +225,8 @@ static int wait_for_helper(void *data) sub_info->retval = ret; } - if (sub_info->wait < 0) - kfree(sub_info); + if (sub_info->wait == UMH_NO_WAIT) + call_usermodehelper_freeinfo(sub_info); else complete(sub_info->complete); return 0; @@ -229,34 +238,122 @@ static void __call_usermodehelper(struct work_struct *work) struct subprocess_info *sub_info = container_of(work, struct subprocess_info, work); pid_t pid; - int wait = sub_info->wait; + enum umh_wait wait = sub_info->wait; /* CLONE_VFORK: wait until the usermode helper has execve'd * successfully We need the data structures to stay around * until that is done. */ - if (wait) + if (wait == UMH_WAIT_PROC || wait == UMH_NO_WAIT) pid = kernel_thread(wait_for_helper, sub_info, CLONE_FS | CLONE_FILES | SIGCHLD); else pid = kernel_thread(____call_usermodehelper, sub_info, CLONE_VFORK | SIGCHLD); - if (wait < 0) - return; + switch (wait) { + case UMH_NO_WAIT: + break; - if (pid < 0) { + case UMH_WAIT_PROC: + if (pid > 0) + break; sub_info->retval = pid; + /* FALLTHROUGH */ + + case UMH_WAIT_EXEC: complete(sub_info->complete); - } else if (!wait) - complete(sub_info->complete); + } +} + +/** + * call_usermodehelper_setup - prepare to call a usermode helper + * @path - path to usermode executable + * @argv - arg vector for process + * @envp - environment for process + * + * Returns either NULL on allocation failure, or a subprocess_info + * structure. This should be passed to call_usermodehelper_exec to + * exec the process and free the structure. + */ +struct subprocess_info *call_usermodehelper_setup(char *path, + char **argv, char **envp) +{ + struct subprocess_info *sub_info; + sub_info = kzalloc(sizeof(struct subprocess_info), GFP_ATOMIC); + if (!sub_info) + goto out; + + INIT_WORK(&sub_info->work, __call_usermodehelper); + sub_info->path = path; + sub_info->argv = argv; + sub_info->envp = envp; + + out: + return sub_info; } +EXPORT_SYMBOL(call_usermodehelper_setup); /** - * call_usermodehelper_keys - start a usermode application - * @path: pathname for the application - * @argv: null-terminated argument list - * @envp: null-terminated environment list - * @session_keyring: session keyring for process (NULL for an empty keyring) + * call_usermodehelper_setkeys - set the session keys for usermode helper + * @info: a subprocess_info returned by call_usermodehelper_setup + * @session_keyring: the session keyring for the process + */ +void call_usermodehelper_setkeys(struct subprocess_info *info, + struct key *session_keyring) +{ + info->ring = session_keyring; +} +EXPORT_SYMBOL(call_usermodehelper_setkeys); + +/** + * call_usermodehelper_setcleanup - set a cleanup function + * @info: a subprocess_info returned by call_usermodehelper_setup + * @cleanup: a cleanup function + * + * The cleanup function is just befor ethe subprocess_info is about to + * be freed. This can be used for freeing the argv and envp. The + * Function must be runnable in either a process context or the + * context in which call_usermodehelper_exec is called. + */ +void call_usermodehelper_setcleanup(struct subprocess_info *info, + void (*cleanup)(char **argv, char **envp)) +{ + info->cleanup = cleanup; +} +EXPORT_SYMBOL(call_usermodehelper_setcleanup); + +/** + * call_usermodehelper_stdinpipe - set up a pipe to be used for stdin + * @sub_info: a subprocess_info returned by call_usermodehelper_setup + * @filp: set to the write-end of a pipe + * + * This constructs a pipe, and sets the read end to be the stdin of the + * subprocess, and returns the write-end in *@filp. + */ +int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info, + struct file **filp) +{ + struct file *f; + + f = create_write_pipe(); + if (IS_ERR(f)) + return PTR_ERR(f); + *filp = f; + + f = create_read_pipe(f); + if (IS_ERR(f)) { + free_write_pipe(*filp); + return PTR_ERR(f); + } + sub_info->stdin = f; + + return 0; +} +EXPORT_SYMBOL(call_usermodehelper_stdinpipe); + +/** + * call_usermodehelper_exec - start a usermode application + * @sub_info: information about the subprocessa * @wait: wait for the application to finish and return status. * when -1 don't wait at all, but you get no useful error back when * the program couldn't be exec'ed. This makes it safe to call @@ -265,81 +362,68 @@ static void __call_usermodehelper(struct work_struct *work) * Runs a user-space application. The application is started * asynchronously if wait is not set, and runs as a child of keventd. * (ie. it runs with full root capabilities). - * - * Must be called from process context. Returns a negative error code - * if program was not execed successfully, or 0. */ -int call_usermodehelper_keys(char *path, char **argv, char **envp, - struct key *session_keyring, int wait) +int call_usermodehelper_exec(struct subprocess_info *sub_info, + enum umh_wait wait) { DECLARE_COMPLETION_ONSTACK(done); - struct subprocess_info *sub_info; int retval; - if (!khelper_wq) - return -EBUSY; - - if (path[0] == '\0') - return 0; + if (sub_info->path[0] == '\0') { + retval = 0; + goto out; + } - sub_info = kzalloc(sizeof(struct subprocess_info), GFP_ATOMIC); - if (!sub_info) - return -ENOMEM; + if (!khelper_wq) { + retval = -EBUSY; + goto out; + } - INIT_WORK(&sub_info->work, __call_usermodehelper); sub_info->complete = &done; - sub_info->path = path; - sub_info->argv = argv; - sub_info->envp = envp; - sub_info->ring = session_keyring; sub_info->wait = wait; queue_work(khelper_wq, &sub_info->work); - if (wait < 0) /* task has freed sub_info */ + if (wait == UMH_NO_WAIT) /* task has freed sub_info */ return 0; wait_for_completion(&done); retval = sub_info->retval; - kfree(sub_info); + + out: + call_usermodehelper_freeinfo(sub_info); return retval; } -EXPORT_SYMBOL(call_usermodehelper_keys); +EXPORT_SYMBOL(call_usermodehelper_exec); +/** + * call_usermodehelper_pipe - call a usermode helper process with a pipe stdin + * @path: path to usermode executable + * @argv: arg vector for process + * @envp: environment for process + * @filp: set to the write-end of a pipe + * + * This is a simple wrapper which executes a usermode-helper function + * with a pipe as stdin. It is implemented entirely in terms of + * lower-level call_usermodehelper_* functions. + */ int call_usermodehelper_pipe(char *path, char **argv, char **envp, struct file **filp) { - DECLARE_COMPLETION(done); - struct subprocess_info sub_info = { - .work = __WORK_INITIALIZER(sub_info.work, - __call_usermodehelper), - .complete = &done, - .path = path, - .argv = argv, - .envp = envp, - .retval = 0, - }; - struct file *f; + struct subprocess_info *sub_info; + int ret; - if (!khelper_wq) - return -EBUSY; + sub_info = call_usermodehelper_setup(path, argv, envp); + if (sub_info == NULL) + return -ENOMEM; - if (path[0] == '\0') - return 0; + ret = call_usermodehelper_stdinpipe(sub_info, filp); + if (ret < 0) + goto out; - f = create_write_pipe(); - if (IS_ERR(f)) - return PTR_ERR(f); - *filp = f; - - f = create_read_pipe(f); - if (IS_ERR(f)) { - free_write_pipe(*filp); - return PTR_ERR(f); - } - sub_info.stdin = f; + return call_usermodehelper_exec(sub_info, 1); - queue_work(khelper_wq, &sub_info.work); - wait_for_completion(&done); - return sub_info.retval; + out: + call_usermodehelper_freeinfo(sub_info); + return ret; } EXPORT_SYMBOL(call_usermodehelper_pipe); diff --git a/kernel/sys.c b/kernel/sys.c index 4d141ae..18987c7 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -2286,3 +2286,61 @@ asmlinkage long sys_getcpu(unsigned __user *cpup, unsigned __user *nodep, } return err ? -EFAULT : 0; } + +char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff"; + +static void argv_cleanup(char **argv, char **envp) +{ + argv_free(argv); +} + +/** + * orderly_poweroff - Trigger an orderly system poweroff + * @force: force poweroff if command execution fails + * + * This may be called from any context to trigger a system shutdown. + * If the orderly shutdown fails, it will force an immediate shutdown. + */ +int orderly_poweroff(bool force) +{ + int argc; + char **argv = argv_split(GFP_ATOMIC, poweroff_cmd, &argc); + static char *envp[] = { + "HOME=/", + "PATH=/sbin:/bin:/usr/sbin:/usr/bin", + NULL + }; + int ret = -ENOMEM; + struct subprocess_info *info; + + if (argv == NULL) { + printk(KERN_WARNING "%s failed to allocate memory for \"%s\"\n", + __func__, poweroff_cmd); + goto out; + } + + info = call_usermodehelper_setup(argv[0], argv, envp); + if (info == NULL) { + argv_free(argv); + goto out; + } + + call_usermodehelper_setcleanup(info, argv_cleanup); + + ret = call_usermodehelper_exec(info, UMH_NO_WAIT); + + out: + if (ret && force) { + printk(KERN_WARNING "Failed to start orderly shutdown: " + "forcing the issue\n"); + + /* I guess this should try to kick off some daemon to + sync and poweroff asap. Or not even bother syncing + if we're doing an emergency shutdown? */ + emergency_sync(); + kernel_power_off(); + } + + return ret; +} +EXPORT_SYMBOL_GPL(orderly_poweroff); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 7063ebc..44a1d69 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -46,6 +46,7 @@ #include <linux/syscalls.h> #include <linux/nfs_fs.h> #include <linux/acpi.h> +#include <linux/reboot.h> #include <asm/uaccess.h> #include <asm/processor.h> @@ -705,6 +706,15 @@ static ctl_table kern_table[] = { .proc_handler = &proc_dointvec, }, #endif + { + .ctl_name = CTL_UNNUMBERED, + .procname = "poweroff_cmd", + .data = &poweroff_cmd, + .maxlen = POWEROFF_CMD_PATH_LEN, + .mode = 0644, + .proc_handler = &proc_dostring, + .strategy = &sysctl_string, + }, { .ctl_name = 0 } }; diff --git a/lib/Makefile b/lib/Makefile index da68b2c..6149663 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -5,7 +5,7 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \ rbtree.o radix-tree.o dump_stack.o \ idr.o int_sqrt.o bitmap.o extable.o prio_tree.o \ - sha1.o irq_regs.o reciprocal_div.o + sha1.o irq_regs.o reciprocal_div.o argv_split.o lib-$(CONFIG_MMU) += ioremap.o lib-$(CONFIG_SMP) += cpumask.o diff --git a/lib/argv_split.c b/lib/argv_split.c new file mode 100644 index 0000000..4096ed4 --- /dev/null +++ b/lib/argv_split.c @@ -0,0 +1,105 @@ +/* + * Helper function for splitting a string into an argv-like array. + */ + +#include <linux/kernel.h> +#include <linux/ctype.h> +#include <linux/bug.h> + +static const char *skip_sep(const char *cp) +{ + while (*cp && isspace(*cp)) + cp++; + + return cp; +} + +static const char *skip_arg(const char *cp) +{ + while (*cp && !isspace(*cp)) + cp++; + + return cp; +} + +static int count_argc(const char *str) +{ + int count = 0; + + while (*str) { + str = skip_sep(str); + if (*str) { + count++; + str = skip_arg(str); + } + } + + return count; +} + +/** + * argv_free - free an argv + * @argv - the argument vector to be freed + * + * Frees an argv and the strings it points to. + */ +void argv_free(char **argv) +{ + char **p; + for (p = argv; *p; p++) + kfree(*p); + + kfree(argv); +} +EXPORT_SYMBOL(argv_free); + +/** + * argv_split - split a string at whitespace, returning an argv + * @gfp: the GFP mask used to allocate memory + * @str: the string to be split + * @argcp: returned argument count + * + * Returns an array of pointers to strings which are split out from + * @str. This is performed by strictly splitting on white-space; no + * quote processing is performed. Multiple whitespace characters are + * considered to be a single argument separator. The returned array + * is always NULL-terminated. Returns NULL on memory allocation + * failure. + */ +char **argv_split(gfp_t gfp, const char *str, int *argcp) +{ + int argc = count_argc(str); + char **argv = kzalloc(sizeof(*argv) * (argc+1), gfp); + char **argvp; + + if (argv == NULL) + goto out; + + *argcp = argc; + argvp = argv; + + while (*str) { + str = skip_sep(str); + + if (*str) { + const char *p = str; + char *t; + + str = skip_arg(str); + + t = kstrndup(p, str-p, gfp); + if (t == NULL) + goto fail; + *argvp++ = t; + } + } + *argvp = NULL; + + out: + return argv; + + fail: + argv_free(argv); + return NULL; +} +EXPORT_SYMBOL(argv_split); diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index 12e311d..bd5ecbb 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -208,7 +208,7 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, argv [0] = uevent_helper; argv [1] = (char *)subsystem; argv [2] = NULL; - call_usermodehelper (argv[0], argv, envp, 0); + call_usermodehelper (argv[0], argv, envp, UMH_WAIT_EXEC); } exit: @@ -6,7 +6,6 @@ /** * kstrdup - allocate space for and copy an existing string - * * @s: the string to duplicate * @gfp: the GFP mask used in the kmalloc() call when allocating memory */ @@ -27,6 +26,30 @@ char *kstrdup(const char *s, gfp_t gfp) EXPORT_SYMBOL(kstrdup); /** + * kstrndup - allocate space for and copy an existing string + * @s: the string to duplicate + * @max: read at most @max chars from @s + * @gfp: the GFP mask used in the kmalloc() call when allocating memory + */ +char *kstrndup(const char *s, size_t max, gfp_t gfp) +{ + size_t len; + char *buf; + + if (!s) + return NULL; + + len = strnlen(s, max); + buf = kmalloc_track_caller(len+1, gfp); + if (buf) { + memcpy(buf, s, len); + buf[len] = '\0'; + } + return buf; +} +EXPORT_SYMBOL(kstrndup); + +/** * kmemdup - duplicate region of memory * * @src: memory region to duplicate @@ -80,7 +103,6 @@ EXPORT_SYMBOL(krealloc); /* * strndup_user - duplicate an existing string from user space - * * @s: The string to duplicate * @n: Maximum number of bytes to copy, including the trailing NUL. */ diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 8e05a11..3130c34 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c @@ -767,3 +767,56 @@ EXPORT_SYMBOL(remap_vmalloc_range); void __attribute__((weak)) vmalloc_sync_all(void) { } + + +static int f(pte_t *pte, struct page *pmd_page, unsigned long addr, void *data) +{ + /* apply_to_page_range() does all the hard work. */ + return 0; +} + +/** + * alloc_vm_area - allocate a range of kernel address space + * @size: size of the area + * @returns: NULL on failure, vm_struct on success + * + * This function reserves a range of kernel address space, and + * allocates pagetables to map that range. No actual mappings + * are created. If the kernel address space is not shared + * between processes, it syncs the pagetable across all + * processes. + */ +struct vm_struct *alloc_vm_area(size_t size) +{ + struct vm_struct *area; + + area = get_vm_area(size, VM_IOREMAP); + if (area == NULL) + return NULL; + + /* + * This ensures that page tables are constructed for this region + * of kernel virtual address space and mapped into init_mm. + */ + if (apply_to_page_range(&init_mm, (unsigned long)area->addr, + area->size, f, NULL)) { + free_vm_area(area); + return NULL; + } + + /* Make sure the pagetables are constructed in process kernel + mappings */ + vmalloc_sync_all(); + + return area; +} +EXPORT_SYMBOL_GPL(alloc_vm_area); + +void free_vm_area(struct vm_struct *area) +{ + struct vm_struct *ret; + ret = remove_vm_area(area->addr); + BUG_ON(ret != area); + kfree(area); +} +EXPORT_SYMBOL_GPL(free_vm_area); diff --git a/net/atm/br2684.c b/net/atm/br2684.c index faa6aaf..c0f6861 100644 --- a/net/atm/br2684.c +++ b/net/atm/br2684.c @@ -460,11 +460,7 @@ static void br2684_push(struct atm_vcc *atmvcc, struct sk_buff *skb) skb_pull(skb, plen); skb_set_mac_header(skb, -ETH_HLEN); skb->pkt_type = PACKET_HOST; -#ifdef CONFIG_BR2684_FAST_TRANS - skb->protocol = ((u16 *) skb->data)[-1]; -#else /* some protocols might require this: */ skb->protocol = br_type_trans(skb, net_dev); -#endif /* CONFIG_BR2684_FAST_TRANS */ #else skb_pull(skb, plen - ETH_HLEN); skb->protocol = eth_type_trans(skb, net_dev); diff --git a/net/bridge/br_stp_if.c b/net/bridge/br_stp_if.c index a786e78..1ea2f86 100644 --- a/net/bridge/br_stp_if.c +++ b/net/bridge/br_stp_if.c @@ -125,7 +125,7 @@ static void br_stp_start(struct net_bridge *br) char *argv[] = { BR_STP_PROG, br->dev->name, "start", NULL }; char *envp[] = { NULL }; - r = call_usermodehelper(BR_STP_PROG, argv, envp, 1); + r = call_usermodehelper(BR_STP_PROG, argv, envp, UMH_WAIT_PROC); if (r == 0) { br->stp_enabled = BR_USER_STP; printk(KERN_INFO "%s: userspace STP started\n", br->dev->name); diff --git a/net/core/dev.c b/net/core/dev.c index 13a0d9f..6357f54c 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2715,20 +2715,6 @@ int __dev_addr_add(struct dev_addr_list **list, int *count, return 0; } -void __dev_addr_discard(struct dev_addr_list **list) -{ - struct dev_addr_list *tmp; - - while (*list != NULL) { - tmp = *list; - *list = tmp->next; - if (tmp->da_users > tmp->da_gusers) - printk("__dev_addr_discard: address leakage! " - "da_users=%d\n", tmp->da_users); - kfree(tmp); - } -} - /** * dev_unicast_delete - Release secondary unicast address. * @dev: device @@ -2777,11 +2763,30 @@ int dev_unicast_add(struct net_device *dev, void *addr, int alen) } EXPORT_SYMBOL(dev_unicast_add); -static void dev_unicast_discard(struct net_device *dev) +static void __dev_addr_discard(struct dev_addr_list **list) +{ + struct dev_addr_list *tmp; + + while (*list != NULL) { + tmp = *list; + *list = tmp->next; + if (tmp->da_users > tmp->da_gusers) + printk("__dev_addr_discard: address leakage! " + "da_users=%d\n", tmp->da_users); + kfree(tmp); + } +} + +static void dev_addr_discard(struct net_device *dev) { netif_tx_lock_bh(dev); + __dev_addr_discard(&dev->uc_list); dev->uc_count = 0; + + __dev_addr_discard(&dev->mc_list); + dev->mc_count = 0; + netif_tx_unlock_bh(dev); } @@ -3739,8 +3744,7 @@ void unregister_netdevice(struct net_device *dev) /* * Flush the unicast and multicast chains */ - dev_unicast_discard(dev); - dev_mc_discard(dev); + dev_addr_discard(dev); if (dev->uninit) dev->uninit(dev); diff --git a/net/core/dev_mcast.c b/net/core/dev_mcast.c index 235a2a8..99aece1 100644 --- a/net/core/dev_mcast.c +++ b/net/core/dev_mcast.c @@ -177,18 +177,6 @@ void dev_mc_unsync(struct net_device *to, struct net_device *from) } EXPORT_SYMBOL(dev_mc_unsync); -/* - * Discard multicast list when a device is downed - */ - -void dev_mc_discard(struct net_device *dev) -{ - netif_tx_lock_bh(dev); - __dev_addr_discard(&dev->mc_list); - dev->mc_count = 0; - netif_tx_unlock_bh(dev); -} - #ifdef CONFIG_PROC_FS static void *dev_mc_seq_start(struct seq_file *seq, loff_t *pos) { diff --git a/net/core/gen_estimator.c b/net/core/gen_estimator.c index cc84d8d..590a767 100644 --- a/net/core/gen_estimator.c +++ b/net/core/gen_estimator.c @@ -79,27 +79,27 @@ struct gen_estimator { - struct gen_estimator *next; + struct list_head list; struct gnet_stats_basic *bstats; struct gnet_stats_rate_est *rate_est; spinlock_t *stats_lock; - unsigned interval; int ewma_log; u64 last_bytes; u32 last_packets; u32 avpps; u32 avbps; + struct rcu_head e_rcu; }; struct gen_estimator_head { struct timer_list timer; - struct gen_estimator *list; + struct list_head list; }; static struct gen_estimator_head elist[EST_MAX_INTERVAL+1]; -/* Estimator array lock */ +/* Protects against NULL dereference */ static DEFINE_RWLOCK(est_lock); static void est_timer(unsigned long arg) @@ -107,13 +107,17 @@ static void est_timer(unsigned long arg) int idx = (int)arg; struct gen_estimator *e; - read_lock(&est_lock); - for (e = elist[idx].list; e; e = e->next) { + rcu_read_lock(); + list_for_each_entry_rcu(e, &elist[idx].list, list) { u64 nbytes; u32 npackets; u32 rate; spin_lock(e->stats_lock); + read_lock(&est_lock); + if (e->bstats == NULL) + goto skip; + nbytes = e->bstats->bytes; npackets = e->bstats->packets; rate = (nbytes - e->last_bytes)<<(7 - idx); @@ -125,12 +129,14 @@ static void est_timer(unsigned long arg) e->last_packets = npackets; e->avpps += ((long)rate - (long)e->avpps) >> e->ewma_log; e->rate_est->pps = (e->avpps+0x1FF)>>10; +skip: + read_unlock(&est_lock); spin_unlock(e->stats_lock); } - if (elist[idx].list != NULL) + if (!list_empty(&elist[idx].list)) mod_timer(&elist[idx].timer, jiffies + ((HZ<<idx)/4)); - read_unlock(&est_lock); + rcu_read_unlock(); } /** @@ -147,12 +153,17 @@ static void est_timer(unsigned long arg) * &rate_est with the statistics lock grabed during this period. * * Returns 0 on success or a negative error code. + * + * NOTE: Called under rtnl_mutex */ int gen_new_estimator(struct gnet_stats_basic *bstats, - struct gnet_stats_rate_est *rate_est, spinlock_t *stats_lock, struct rtattr *opt) + struct gnet_stats_rate_est *rate_est, + spinlock_t *stats_lock, + struct rtattr *opt) { struct gen_estimator *est; struct gnet_estimator *parm = RTA_DATA(opt); + int idx; if (RTA_PAYLOAD(opt) < sizeof(*parm)) return -EINVAL; @@ -164,7 +175,7 @@ int gen_new_estimator(struct gnet_stats_basic *bstats, if (est == NULL) return -ENOBUFS; - est->interval = parm->interval + 2; + idx = parm->interval + 2; est->bstats = bstats; est->rate_est = rate_est; est->stats_lock = stats_lock; @@ -174,20 +185,25 @@ int gen_new_estimator(struct gnet_stats_basic *bstats, est->last_packets = bstats->packets; est->avpps = rate_est->pps<<10; - est->next = elist[est->interval].list; - if (est->next == NULL) { - init_timer(&elist[est->interval].timer); - elist[est->interval].timer.data = est->interval; - elist[est->interval].timer.expires = jiffies + ((HZ<<est->interval)/4); - elist[est->interval].timer.function = est_timer; - add_timer(&elist[est->interval].timer); + if (!elist[idx].timer.function) { + INIT_LIST_HEAD(&elist[idx].list); + setup_timer(&elist[idx].timer, est_timer, idx); } - write_lock_bh(&est_lock); - elist[est->interval].list = est; - write_unlock_bh(&est_lock); + + if (list_empty(&elist[idx].list)) + mod_timer(&elist[idx].timer, jiffies + ((HZ<<idx)/4)); + + list_add_rcu(&est->list, &elist[idx].list); return 0; } +static void __gen_kill_estimator(struct rcu_head *head) +{ + struct gen_estimator *e = container_of(head, + struct gen_estimator, e_rcu); + kfree(e); +} + /** * gen_kill_estimator - remove a rate estimator * @bstats: basic statistics @@ -195,31 +211,32 @@ int gen_new_estimator(struct gnet_stats_basic *bstats, * * Removes the rate estimator specified by &bstats and &rate_est * and deletes the timer. + * + * NOTE: Called under rtnl_mutex */ void gen_kill_estimator(struct gnet_stats_basic *bstats, struct gnet_stats_rate_est *rate_est) { int idx; - struct gen_estimator *est, **pest; + struct gen_estimator *e, *n; for (idx=0; idx <= EST_MAX_INTERVAL; idx++) { - int killed = 0; - pest = &elist[idx].list; - while ((est=*pest) != NULL) { - if (est->rate_est != rate_est || est->bstats != bstats) { - pest = &est->next; + + /* Skip non initialized indexes */ + if (!elist[idx].timer.function) + continue; + + list_for_each_entry_safe(e, n, &elist[idx].list, list) { + if (e->rate_est != rate_est || e->bstats != bstats) continue; - } write_lock_bh(&est_lock); - *pest = est->next; + e->bstats = NULL; write_unlock_bh(&est_lock); - kfree(est); - killed++; + list_del_rcu(&e->list); + call_rcu(&e->e_rcu, __gen_kill_estimator); } - if (killed && elist[idx].list == NULL) - del_timer(&elist[idx].timer); } } diff --git a/net/ipv4/tcp_bic.c b/net/ipv4/tcp_bic.c index dd9ef65..519de09 100644 --- a/net/ipv4/tcp_bic.c +++ b/net/ipv4/tcp_bic.c @@ -137,7 +137,7 @@ static inline void bictcp_update(struct bictcp *ca, u32 cwnd) } static void bictcp_cong_avoid(struct sock *sk, u32 ack, - u32 seq_rtt, u32 in_flight, int data_acked) + u32 in_flight, int data_acked) { struct tcp_sock *tp = tcp_sk(sk); struct bictcp *ca = inet_csk_ca(sk); diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index 1260e52..55fca18 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -324,8 +324,7 @@ EXPORT_SYMBOL_GPL(tcp_slow_start); /* This is Jacobson's slow start and congestion avoidance. * SIGCOMM '88, p. 328. */ -void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 rtt, u32 in_flight, - int flag) +void tcp_reno_cong_avoid(struct sock *sk, u32 ack, u32 in_flight, int flag) { struct tcp_sock *tp = tcp_sk(sk); diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index ebfaac2..d17da30 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -270,7 +270,7 @@ static inline void measure_delay(struct sock *sk) } static void bictcp_cong_avoid(struct sock *sk, u32 ack, - u32 seq_rtt, u32 in_flight, int data_acked) + u32 in_flight, int data_acked) { struct tcp_sock *tp = tcp_sk(sk); struct bictcp *ca = inet_csk_ca(sk); diff --git a/net/ipv4/tcp_highspeed.c b/net/ipv4/tcp_highspeed.c index 43d624e5..14a073d 100644 --- a/net/ipv4/tcp_highspeed.c +++ b/net/ipv4/tcp_highspeed.c @@ -109,7 +109,7 @@ static void hstcp_init(struct sock *sk) tp->snd_cwnd_clamp = min_t(u32, tp->snd_cwnd_clamp, 0xffffffff/128); } -static void hstcp_cong_avoid(struct sock *sk, u32 adk, u32 rtt, +static void hstcp_cong_avoid(struct sock *sk, u32 adk, u32 in_flight, int data_acked) { struct tcp_sock *tp = tcp_sk(sk); diff --git a/net/ipv4/tcp_htcp.c b/net/ipv4/tcp_htcp.c index 4ba4a7a..632c05a 100644 --- a/net/ipv4/tcp_htcp.c +++ b/net/ipv4/tcp_htcp.c @@ -225,7 +225,7 @@ static u32 htcp_recalc_ssthresh(struct sock *sk) return max((tp->snd_cwnd * ca->beta) >> 7, 2U); } -static void htcp_cong_avoid(struct sock *sk, u32 ack, u32 rtt, +static void htcp_cong_avoid(struct sock *sk, u32 ack, s32 rtt, u32 in_flight, int data_acked) { struct tcp_sock *tp = tcp_sk(sk); diff --git a/net/ipv4/tcp_hybla.c b/net/ipv4/tcp_hybla.c index e5be351..b3e55cf 100644 --- a/net/ipv4/tcp_hybla.c +++ b/net/ipv4/tcp_hybla.c @@ -85,7 +85,7 @@ static inline u32 hybla_fraction(u32 odds) * o Give cwnd a new value based on the model proposed * o remember increments <1 */ -static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 rtt, +static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 in_flight, int flag) { struct tcp_sock *tp = tcp_sk(sk); @@ -103,7 +103,7 @@ static void hybla_cong_avoid(struct sock *sk, u32 ack, u32 rtt, return; if (!ca->hybla_en) - return tcp_reno_cong_avoid(sk, ack, rtt, in_flight, flag); + return tcp_reno_cong_avoid(sk, ack, in_flight, flag); if (ca->rho == 0) hybla_recalc_param(sk); diff --git a/net/ipv4/tcp_illinois.c b/net/ipv4/tcp_illinois.c index b2b2256..cc5de6f 100644 --- a/net/ipv4/tcp_illinois.c +++ b/net/ipv4/tcp_illinois.c @@ -258,7 +258,7 @@ static void tcp_illinois_state(struct sock *sk, u8 new_state) /* * Increase window in response to successful acknowledgment. */ -static void tcp_illinois_cong_avoid(struct sock *sk, u32 ack, u32 rtt, +static void tcp_illinois_cong_avoid(struct sock *sk, u32 ack, u32 in_flight, int flag) { struct tcp_sock *tp = tcp_sk(sk); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 4e5884a..fec8a7a 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2323,11 +2323,11 @@ static inline void tcp_ack_update_rtt(struct sock *sk, const int flag, tcp_ack_no_tstamp(sk, seq_rtt, flag); } -static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 rtt, +static void tcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight, int good) { const struct inet_connection_sock *icsk = inet_csk(sk); - icsk->icsk_ca_ops->cong_avoid(sk, ack, rtt, in_flight, good); + icsk->icsk_ca_ops->cong_avoid(sk, ack, in_flight, good); tcp_sk(sk)->snd_cwnd_stamp = tcp_time_stamp; } @@ -2826,11 +2826,11 @@ static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag) /* Advance CWND, if state allows this. */ if ((flag & FLAG_DATA_ACKED) && !frto_cwnd && tcp_may_raise_cwnd(sk, flag)) - tcp_cong_avoid(sk, ack, seq_rtt, prior_in_flight, 0); + tcp_cong_avoid(sk, ack, prior_in_flight, 0); tcp_fastretrans_alert(sk, prior_snd_una, prior_packets, flag); } else { if ((flag & FLAG_DATA_ACKED) && !frto_cwnd) - tcp_cong_avoid(sk, ack, seq_rtt, prior_in_flight, 1); + tcp_cong_avoid(sk, ack, prior_in_flight, 1); } if ((flag & FLAG_FORWARD_PROGRESS) || !(flag&FLAG_NOT_DUP)) diff --git a/net/ipv4/tcp_lp.c b/net/ipv4/tcp_lp.c index e49836c..80e140e 100644 --- a/net/ipv4/tcp_lp.c +++ b/net/ipv4/tcp_lp.c @@ -115,13 +115,12 @@ static void tcp_lp_init(struct sock *sk) * Will only call newReno CA when away from inference. * From TCP-LP's paper, this will be handled in additive increasement. */ -static void tcp_lp_cong_avoid(struct sock *sk, u32 ack, u32 rtt, u32 in_flight, - int flag) +static void tcp_lp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight, int flag) { struct lp *lp = inet_csk_ca(sk); if (!(lp->flag & LP_WITHIN_INF)) - tcp_reno_cong_avoid(sk, ack, rtt, in_flight, flag); + tcp_reno_cong_avoid(sk, ack, in_flight, flag); } /** diff --git a/net/ipv4/tcp_scalable.c b/net/ipv4/tcp_scalable.c index 4624501..be27a33 100644 --- a/net/ipv4/tcp_scalable.c +++ b/net/ipv4/tcp_scalable.c @@ -15,7 +15,7 @@ #define TCP_SCALABLE_AI_CNT 50U #define TCP_SCALABLE_MD_SCALE 3 -static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 rtt, +static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 in_flight, int flag) { struct tcp_sock *tp = tcp_sk(sk); diff --git a/net/ipv4/tcp_vegas.c b/net/ipv4/tcp_vegas.c index e218a51..914e030 100644 --- a/net/ipv4/tcp_vegas.c +++ b/net/ipv4/tcp_vegas.c @@ -163,13 +163,13 @@ void tcp_vegas_cwnd_event(struct sock *sk, enum tcp_ca_event event) EXPORT_SYMBOL_GPL(tcp_vegas_cwnd_event); static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, - u32 seq_rtt, u32 in_flight, int flag) + u32 in_flight, int flag) { struct tcp_sock *tp = tcp_sk(sk); struct vegas *vegas = inet_csk_ca(sk); if (!vegas->doing_vegas_now) - return tcp_reno_cong_avoid(sk, ack, seq_rtt, in_flight, flag); + return tcp_reno_cong_avoid(sk, ack, in_flight, flag); /* The key players are v_beg_snd_una and v_beg_snd_nxt. * @@ -228,7 +228,7 @@ static void tcp_vegas_cong_avoid(struct sock *sk, u32 ack, /* We don't have enough RTT samples to do the Vegas * calculation, so we'll behave like Reno. */ - tcp_reno_cong_avoid(sk, ack, seq_rtt, in_flight, flag); + tcp_reno_cong_avoid(sk, ack, in_flight, flag); } else { u32 rtt, target_cwnd, diff; diff --git a/net/ipv4/tcp_veno.c b/net/ipv4/tcp_veno.c index ec854cc..7a55ddf 100644 --- a/net/ipv4/tcp_veno.c +++ b/net/ipv4/tcp_veno.c @@ -115,13 +115,13 @@ static void tcp_veno_cwnd_event(struct sock *sk, enum tcp_ca_event event) } static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, - u32 seq_rtt, u32 in_flight, int flag) + u32 in_flight, int flag) { struct tcp_sock *tp = tcp_sk(sk); struct veno *veno = inet_csk_ca(sk); if (!veno->doing_veno_now) - return tcp_reno_cong_avoid(sk, ack, seq_rtt, in_flight, flag); + return tcp_reno_cong_avoid(sk, ack, in_flight, flag); /* limited by applications */ if (!tcp_is_cwnd_limited(sk, in_flight)) @@ -132,7 +132,7 @@ static void tcp_veno_cong_avoid(struct sock *sk, u32 ack, /* We don't have enough rtt samples to do the Veno * calculation, so we'll behave like Reno. */ - tcp_reno_cong_avoid(sk, ack, seq_rtt, in_flight, flag); + tcp_reno_cong_avoid(sk, ack, in_flight, flag); } else { u32 rtt, target_cwnd; diff --git a/net/ipv4/tcp_yeah.c b/net/ipv4/tcp_yeah.c index 545ed23..c04b7c6 100644 --- a/net/ipv4/tcp_yeah.c +++ b/net/ipv4/tcp_yeah.c @@ -70,7 +70,7 @@ static void tcp_yeah_pkts_acked(struct sock *sk, u32 pkts_acked, ktime_t last) } static void tcp_yeah_cong_avoid(struct sock *sk, u32 ack, - u32 seq_rtt, u32 in_flight, int flag) + u32 in_flight, int flag) { struct tcp_sock *tp = tcp_sk(sk); struct yeah *yeah = inet_csk_ca(sk); diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c index dcd7e32..4c670cf 100644 --- a/net/irda/af_irda.c +++ b/net/irda/af_irda.c @@ -2567,7 +2567,7 @@ int __init irsock_init(void) * Remove IrDA protocol * */ -void __exit irsock_cleanup(void) +void irsock_cleanup(void) { sock_unregister(PF_IRDA); proto_unregister(&irda_proto); diff --git a/net/irda/irda_device.c b/net/irda/irda_device.c index 7b5def1..435b563 100644 --- a/net/irda/irda_device.c +++ b/net/irda/irda_device.c @@ -95,14 +95,14 @@ int __init irda_device_init( void) return 0; } -static void __exit leftover_dongle(void *arg) +static void leftover_dongle(void *arg) { struct dongle_reg *reg = arg; IRDA_WARNING("IrDA: Dongle type %x not unregistered\n", reg->type); } -void __exit irda_device_cleanup(void) +void irda_device_cleanup(void) { IRDA_DEBUG(4, "%s()\n", __FUNCTION__); diff --git a/net/irda/iriap.c b/net/irda/iriap.c index 774eb70..ee3889f 100644 --- a/net/irda/iriap.c +++ b/net/irda/iriap.c @@ -153,7 +153,7 @@ int __init iriap_init(void) * Initializes the IrIAP layer, called by the module cleanup code in * irmod.c */ -void __exit iriap_cleanup(void) +void iriap_cleanup(void) { irlmp_unregister_service(service_handle); diff --git a/net/irda/irias_object.c b/net/irda/irias_object.c index 4adaae2..cf30245 100644 --- a/net/irda/irias_object.c +++ b/net/irda/irias_object.c @@ -36,39 +36,6 @@ hashbin_t *irias_objects; */ struct ias_value irias_missing = { IAS_MISSING, 0, 0, 0, {0}}; -/* - * Function strndup (str, max) - * - * My own kernel version of strndup! - * - * Faster, check boundary... Jean II - */ -static char *strndup(char *str, size_t max) -{ - char *new_str; - int len; - - /* Check string */ - if (str == NULL) - return NULL; - /* Check length, truncate */ - len = strlen(str); - if(len > max) - len = max; - - /* Allocate new string */ - new_str = kmalloc(len + 1, GFP_ATOMIC); - if (new_str == NULL) { - IRDA_WARNING("%s: Unable to kmalloc!\n", __FUNCTION__); - return NULL; - } - - /* Copy and truncate */ - memcpy(new_str, str, len); - new_str[len] = '\0'; - - return new_str; -} /* * Function ias_new_object (name, id) @@ -90,7 +57,7 @@ struct ias_object *irias_new_object( char *name, int id) } obj->magic = IAS_OBJECT_MAGIC; - obj->name = strndup(name, IAS_MAX_CLASSNAME); + obj->name = kstrndup(name, IAS_MAX_CLASSNAME, GFP_ATOMIC); if (!obj->name) { IRDA_WARNING("%s(), Unable to allocate name!\n", __FUNCTION__); @@ -360,7 +327,7 @@ void irias_add_integer_attrib(struct ias_object *obj, char *name, int value, } attrib->magic = IAS_ATTRIB_MAGIC; - attrib->name = strndup(name, IAS_MAX_ATTRIBNAME); + attrib->name = kstrndup(name, IAS_MAX_ATTRIBNAME, GFP_ATOMIC); /* Insert value */ attrib->value = irias_new_integer_value(value); @@ -404,7 +371,7 @@ void irias_add_octseq_attrib(struct ias_object *obj, char *name, __u8 *octets, } attrib->magic = IAS_ATTRIB_MAGIC; - attrib->name = strndup(name, IAS_MAX_ATTRIBNAME); + attrib->name = kstrndup(name, IAS_MAX_ATTRIBNAME, GFP_ATOMIC); attrib->value = irias_new_octseq_value( octets, len); if (!attrib->name || !attrib->value) { @@ -446,7 +413,7 @@ void irias_add_string_attrib(struct ias_object *obj, char *name, char *value, } attrib->magic = IAS_ATTRIB_MAGIC; - attrib->name = strndup(name, IAS_MAX_ATTRIBNAME); + attrib->name = kstrndup(name, IAS_MAX_ATTRIBNAME, GFP_ATOMIC); attrib->value = irias_new_string_value(value); if (!attrib->name || !attrib->value) { @@ -506,7 +473,7 @@ struct ias_value *irias_new_string_value(char *string) value->type = IAS_STRING; value->charset = CS_ASCII; - value->t.string = strndup(string, IAS_MAX_STRING); + value->t.string = kstrndup(string, IAS_MAX_STRING, GFP_ATOMIC); if (!value->t.string) { IRDA_WARNING("%s: Unable to kmalloc!\n", __FUNCTION__); kfree(value); diff --git a/net/irda/irlap.c b/net/irda/irlap.c index 2fc9f51..3d76aaf 100644 --- a/net/irda/irlap.c +++ b/net/irda/irlap.c @@ -95,7 +95,7 @@ int __init irlap_init(void) return 0; } -void __exit irlap_cleanup(void) +void irlap_cleanup(void) { IRDA_ASSERT(irlap != NULL, return;); diff --git a/net/irda/irlmp.c b/net/irda/irlmp.c index 24a5e3f..7efa930 100644 --- a/net/irda/irlmp.c +++ b/net/irda/irlmp.c @@ -116,7 +116,7 @@ int __init irlmp_init(void) * Remove IrLMP layer * */ -void __exit irlmp_cleanup(void) +void irlmp_cleanup(void) { /* Check for main structure */ IRDA_ASSERT(irlmp != NULL, return;); diff --git a/net/irda/irproc.c b/net/irda/irproc.c index d6f9aba..181cb51 100644 --- a/net/irda/irproc.c +++ b/net/irda/irproc.c @@ -84,7 +84,7 @@ void __init irda_proc_register(void) * Unregister irda entry in /proc file system * */ -void __exit irda_proc_unregister(void) +void irda_proc_unregister(void) { int i; diff --git a/net/irda/irsysctl.c b/net/irda/irsysctl.c index 2e968e7..957e04f 100644 --- a/net/irda/irsysctl.c +++ b/net/irda/irsysctl.c @@ -287,7 +287,7 @@ int __init irda_sysctl_register(void) * Unregister our sysctl interface * */ -void __exit irda_sysctl_unregister(void) +void irda_sysctl_unregister(void) { unregister_sysctl_table(irda_table_header); } diff --git a/net/irda/irttp.c b/net/irda/irttp.c index 7f50832a..3d7ab03 100644 --- a/net/irda/irttp.c +++ b/net/irda/irttp.c @@ -109,7 +109,7 @@ int __init irttp_init(void) * Called by module destruction/cleanup code * */ -void __exit irttp_cleanup(void) +void irttp_cleanup(void) { /* Check for main structure */ IRDA_ASSERT(irttp->magic == TTP_MAGIC, return;); diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 3ac39f1..3599770 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -436,6 +436,7 @@ config NETFILTER_XT_MATCH_CONNBYTES config NETFILTER_XT_MATCH_CONNLIMIT tristate '"connlimit" match support"' depends on NETFILTER_XTABLES + depends on NF_CONNTRACK ---help--- This match allows you to match against the number of parallel connections to a server per client IP address (or address block). diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index a3c8e69..641cfbc 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1012,13 +1012,14 @@ static int netlink_setsockopt(struct socket *sock, int level, int optname, { struct sock *sk = sock->sk; struct netlink_sock *nlk = nlk_sk(sk); - int val = 0, err; + unsigned int val = 0; + int err; if (level != SOL_NETLINK) return -ENOPROTOOPT; if (optlen >= sizeof(int) && - get_user(val, (int __user *)optval)) + get_user(val, (unsigned int __user *)optval)) return -EFAULT; switch (optname) { diff --git a/net/sched/Kconfig b/net/sched/Kconfig index d3f7c3f..8a74cac 100644 --- a/net/sched/Kconfig +++ b/net/sched/Kconfig @@ -97,7 +97,7 @@ config NET_SCH_ATM select classes of this queuing discipline. Each class maps the flow(s) it is handling to a given virtual circuit. - See the top of <file:net/sched/sch_atm.c>) for more details. + See the top of <file:net/sched/sch_atm.c> for more details. To compile this code as a module, choose M here: the module will be called sch_atm. @@ -137,7 +137,7 @@ config NET_SCH_SFQ tristate "Stochastic Fairness Queueing (SFQ)" ---help--- Say Y here if you want to use the Stochastic Fairness Queueing (SFQ) - packet scheduling algorithm . + packet scheduling algorithm. See the top of <file:net/sched/sch_sfq.c> for more details. @@ -306,7 +306,7 @@ config NET_CLS_RSVP6 is important for real time data such as streaming sound or video. Say Y here if you want to be able to classify outgoing packets based - on their RSVP requests and you are using the IPv6. + on their RSVP requests and you are using the IPv6 protocol. To compile this code as a module, choose M here: the module will be called cls_rsvp6. diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c index 417ec8f..ddc4f2c 100644 --- a/net/sched/sch_atm.c +++ b/net/sched/sch_atm.c @@ -292,13 +292,12 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent, } } DPRINTK("atm_tc_change: new id %x\n", classid); - flow = kmalloc(sizeof(struct atm_flow_data) + hdr_len, GFP_KERNEL); + flow = kzalloc(sizeof(struct atm_flow_data) + hdr_len, GFP_KERNEL); DPRINTK("atm_tc_change: flow %p\n", flow); if (!flow) { error = -ENOBUFS; goto err_out; } - memset(flow, 0, sizeof(*flow)); flow->filter_list = NULL; if (!(flow->q = qdisc_create_dflt(sch->dev, &pfifo_qdisc_ops, classid))) flow->q = &noop_qdisc; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 157bfbd..b48f06f 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -2141,7 +2141,7 @@ int xfrm_bundle_ok(struct xfrm_policy *pol, struct xfrm_dst *first, if (last == first) break; - last = last->u.next; + last = (struct xfrm_dst *)last->u.dst.next; last->child_mtu_cached = mtu; } diff --git a/security/keys/request_key.c b/security/keys/request_key.c index f573ac1..5575001 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -108,7 +108,8 @@ static int call_sbin_request_key(struct key *key, argv[i] = NULL; /* do it */ - ret = call_usermodehelper_keys(argv[0], argv, envp, keyring, 1); + ret = call_usermodehelper_keys(argv[0], argv, envp, keyring, + UMH_WAIT_PROC); error_link: key_put(keyring); |