summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/00-INDEX2
-rw-r--r--Documentation/ABI/testing/sysfs-bus-css35
-rw-r--r--Documentation/ABI/testing/sysfs-dev20
-rw-r--r--Documentation/DocBook/gadget.tmpl38
-rw-r--r--Documentation/DocBook/uio-howto.tmpl40
-rw-r--r--Documentation/IRQ-affinity.txt37
-rw-r--r--Documentation/RCU/NMI-RCU.txt3
-rw-r--r--Documentation/RCU/RTFP.txt108
-rw-r--r--Documentation/RCU/checklist.txt89
-rw-r--r--Documentation/RCU/whatisRCU.txt58
-rw-r--r--Documentation/cpusets.txt2
-rw-r--r--Documentation/cputopology.txt26
-rw-r--r--Documentation/early-userspace/00-INDEX8
-rw-r--r--Documentation/early-userspace/initrd.txt (renamed from Documentation/initrd.txt)6
-rw-r--r--Documentation/feature-removal-schedule.txt7
-rw-r--r--Documentation/filesystems/nfs-rdma.txt103
-rw-r--r--Documentation/filesystems/proc.txt29
-rw-r--r--Documentation/filesystems/sysfs-pci.txt1
-rw-r--r--Documentation/filesystems/sysfs.txt6
-rw-r--r--Documentation/hwmon/dme17374
-rw-r--r--Documentation/hwmon/sysfs-interface33
-rw-r--r--Documentation/i2c/busses/i2c-i81047
-rw-r--r--Documentation/i2c/busses/i2c-prosavage23
-rw-r--r--Documentation/i2c/busses/i2c-savage426
-rw-r--r--Documentation/i2c/fault-codes127
-rw-r--r--Documentation/i2c/smbus-protocol4
-rw-r--r--Documentation/i2c/writing-clients40
-rw-r--r--Documentation/ia64/paravirt_ops.txt137
-rw-r--r--Documentation/kernel-parameters.txt38
-rw-r--r--Documentation/kobject.txt6
-rw-r--r--Documentation/laptops/thinkpad-acpi.txt2
-rw-r--r--Documentation/scheduler/sched-domains.txt7
-rw-r--r--Documentation/scsi/aacraid.txt24
-rw-r--r--Documentation/tracers/mmiotrace.txt164
-rw-r--r--Documentation/usb/WUSB-Design-overview.txt448
-rw-r--r--Documentation/usb/gadget_serial.txt35
-rw-r--r--Documentation/usb/uhci.txt165
-rw-r--r--Documentation/usb/wusb-cbaf133
-rw-r--r--Documentation/video4linux/CARDLIST.cx238851
-rw-r--r--Documentation/video4linux/CARDLIST.em28xx4
-rw-r--r--Documentation/video4linux/CARDLIST.saa71344
-rw-r--r--Documentation/x86/i386/IO-APIC.txt (renamed from Documentation/i386/IO-APIC.txt)0
-rw-r--r--Documentation/x86/i386/boot.txt (renamed from Documentation/i386/boot.txt)79
-rw-r--r--Documentation/x86/i386/usb-legacy-support.txt (renamed from Documentation/i386/usb-legacy-support.txt)0
-rw-r--r--Documentation/x86/i386/zero-page.txt (renamed from Documentation/i386/zero-page.txt)0
-rw-r--r--Documentation/x86/x86_64/00-INDEX (renamed from Documentation/x86_64/00-INDEX)0
-rw-r--r--Documentation/x86/x86_64/boot-options.txt (renamed from Documentation/x86_64/boot-options.txt)0
-rw-r--r--Documentation/x86/x86_64/cpu-hotplug-spec (renamed from Documentation/x86_64/cpu-hotplug-spec)0
-rw-r--r--Documentation/x86/x86_64/fake-numa-for-cpusets (renamed from Documentation/x86_64/fake-numa-for-cpusets)0
-rw-r--r--Documentation/x86/x86_64/kernel-stacks (renamed from Documentation/x86_64/kernel-stacks)0
-rw-r--r--Documentation/x86/x86_64/machinecheck (renamed from Documentation/x86_64/machinecheck)0
-rw-r--r--Documentation/x86/x86_64/mm.txt (renamed from Documentation/x86_64/mm.txt)5
-rw-r--r--Documentation/x86/x86_64/uefi.txt (renamed from Documentation/x86_64/uefi.txt)0
-rw-r--r--MAINTAINERS47
-rw-r--r--Makefile9
-rw-r--r--arch/alpha/kernel/irq.c5
-rw-r--r--arch/arm/Kconfig2
-rw-r--r--arch/arm/boot/compressed/Makefile6
-rw-r--r--arch/arm/common/dmabounce.c22
-rw-r--r--arch/arm/common/locomo.c2
-rw-r--r--arch/arm/common/sa1111.c10
-rw-r--r--arch/arm/common/scoop.c2
-rw-r--r--arch/arm/kernel/Makefile5
-rw-r--r--arch/arm/kernel/armksyms.c5
-rw-r--r--arch/arm/kernel/ecard.c5
-rw-r--r--arch/arm/kernel/entry-common.S47
-rw-r--r--arch/arm/kernel/ftrace.c118
-rw-r--r--arch/arm/kernel/kprobes.c2
-rw-r--r--arch/arm/mach-integrator/impd1.c7
-rw-r--r--arch/arm/mach-integrator/lm.c6
-rw-r--r--arch/arm/mach-pxa/ssp.c2
-rw-r--r--arch/arm/plat-omap/mailbox.c2
-rw-r--r--arch/avr32/boards/atngw100/setup.c29
-rw-r--r--arch/avr32/boards/atstk1000/atstk1002.c8
-rw-r--r--arch/avr32/boards/atstk1000/atstk1003.c7
-rw-r--r--arch/avr32/boards/atstk1000/atstk1004.c9
-rw-r--r--arch/avr32/kernel/signal.c3
-rw-r--r--arch/avr32/kernel/vmlinux.lds.S2
-rw-r--r--arch/avr32/lib/io-readsb.S2
-rw-r--r--arch/avr32/mach-at32ap/at32ap700x.c191
-rw-r--r--arch/avr32/mach-at32ap/pio.c2
-rw-r--r--arch/avr32/mm/init.c2
-rw-r--r--arch/ia64/Makefile6
-rw-r--r--arch/ia64/kernel/Makefile44
-rw-r--r--arch/ia64/kernel/acpi.c9
-rw-r--r--arch/ia64/kernel/entry.S115
-rw-r--r--arch/ia64/kernel/head.S41
-rw-r--r--arch/ia64/kernel/iosapic.c45
-rw-r--r--arch/ia64/kernel/irq_ia64.c19
-rw-r--r--arch/ia64/kernel/ivt.S462
-rw-r--r--arch/ia64/kernel/minstate.h13
-rw-r--r--arch/ia64/kernel/nr-irqs.c24
-rw-r--r--arch/ia64/kernel/paravirt.c369
-rw-r--r--arch/ia64/kernel/paravirt_inst.h29
-rw-r--r--arch/ia64/kernel/paravirtentry.S60
-rw-r--r--arch/ia64/kernel/setup.c10
-rw-r--r--arch/ia64/kernel/smpboot.c2
-rw-r--r--arch/ia64/kernel/time.c23
-rw-r--r--arch/ia64/kernel/vmlinux.lds.S1
-rw-r--r--arch/ia64/kvm/Makefile3
-rw-r--r--arch/ia64/kvm/kvm-ia64.c17
-rw-r--r--arch/ia64/kvm/mmio.c3
-rw-r--r--arch/ia64/sn/kernel/irq.c1
-rw-r--r--arch/mips/kernel/rtlx.c4
-rw-r--r--arch/mips/sibyte/common/sb_tbprof.c3
-rw-r--r--arch/parisc/kernel/vmlinux.lds.S1
-rw-r--r--arch/powerpc/Kconfig4
-rw-r--r--arch/powerpc/kernel/Makefile14
-rw-r--r--arch/powerpc/kernel/entry_32.S123
-rw-r--r--arch/powerpc/kernel/entry_64.S62
-rw-r--r--arch/powerpc/kernel/ftrace.c161
-rw-r--r--arch/powerpc/kernel/io.c3
-rw-r--r--arch/powerpc/kernel/irq.c6
-rw-r--r--arch/powerpc/kernel/ppc_ksyms.c5
-rw-r--r--arch/powerpc/kernel/setup_32.c6
-rw-r--r--arch/powerpc/kernel/vmlinux.lds.S2
-rw-r--r--arch/powerpc/kvm/44x_tlb.c9
-rw-r--r--arch/powerpc/kvm/Makefile2
-rw-r--r--arch/powerpc/kvm/booke_guest.c33
-rw-r--r--arch/powerpc/kvm/emulate.c12
-rw-r--r--arch/powerpc/kvm/powerpc.c7
-rw-r--r--arch/powerpc/platforms/chrp/pci.c2
-rw-r--r--arch/powerpc/platforms/powermac/Makefile5
-rw-r--r--arch/s390/Kconfig2
-rw-r--r--arch/s390/kernel/Makefile7
-rw-r--r--arch/s390/kernel/binfmt_elf32.c214
-rw-r--r--arch/s390/kernel/compat_ptrace.h4
-rw-r--r--arch/s390/kernel/early.c18
-rw-r--r--arch/s390/kernel/process.c32
-rw-r--r--arch/s390/kernel/ptrace.c363
-rw-r--r--arch/s390/kernel/smp.c2
-rw-r--r--arch/s390/kernel/time.c634
-rw-r--r--arch/s390/kernel/vmlinux.lds.S1
-rw-r--r--arch/s390/kernel/vtime.c25
-rw-r--r--arch/s390/kvm/diag.c2
-rw-r--r--arch/s390/kvm/interrupt.c7
-rw-r--r--arch/s390/kvm/kvm-s390.c17
-rw-r--r--arch/s390/mm/pgtable.c44
-rw-r--r--arch/s390/mm/vmem.c2
-rw-r--r--arch/sh/Kconfig18
-rw-r--r--arch/sh/Makefile3
-rw-r--r--arch/sh/boards/renesas/ap325rxa/Makefile1
-rw-r--r--arch/sh/boards/renesas/ap325rxa/setup.c108
-rw-r--r--arch/sh/boards/renesas/rsk7203/Makefile1
-rw-r--r--arch/sh/boards/renesas/rsk7203/setup.c126
-rw-r--r--arch/sh/boards/renesas/sh7763rdp/Makefile1
-rw-r--r--arch/sh/boards/renesas/sh7763rdp/irq.c45
-rw-r--r--arch/sh/boards/renesas/sh7763rdp/setup.c128
-rw-r--r--arch/sh/configs/ap325rxa_defconfig947
-rw-r--r--arch/sh/configs/sh7763rdp_defconfig1052
-rw-r--r--arch/sh/kernel/Makefile_322
-rw-r--r--arch/sh/kernel/cpu/sh4a/setup-sh7763.c10
-rw-r--r--arch/sh/kernel/ptrace_32.c23
-rw-r--r--arch/sh/kernel/signal_32.c25
-rw-r--r--arch/sh/kernel/vmlinux_32.lds.S1
-rw-r--r--arch/sh/kernel/vmlinux_64.lds.S1
-rw-r--r--arch/sh/mm/Kconfig12
-rw-r--r--arch/sh/tools/mach-types3
-rw-r--r--arch/sparc64/Kconfig2
-rw-r--r--arch/sparc64/Kconfig.debug2
-rw-r--r--arch/sparc64/kernel/Makefile1
-rw-r--r--arch/sparc64/kernel/ebus.c4
-rw-r--r--arch/sparc64/kernel/ftrace.c92
-rw-r--r--arch/sparc64/kernel/of_device.c4
-rw-r--r--arch/sparc64/kernel/pci.c2
-rw-r--r--arch/sparc64/kernel/vio.c18
-rw-r--r--arch/sparc64/lib/mcount.S58
-rw-r--r--arch/x86/Kconfig218
-rw-r--r--arch/x86/Kconfig.cpu4
-rw-r--r--arch/x86/Kconfig.debug33
-rw-r--r--arch/x86/Makefile25
-rw-r--r--arch/x86/boot/a20.c3
-rw-r--r--arch/x86/boot/compressed/head_64.S5
-rw-r--r--arch/x86/boot/compressed/misc.c43
-rw-r--r--arch/x86/boot/cpu.c2
-rw-r--r--arch/x86/boot/main.c4
-rw-r--r--arch/x86/boot/memory.c3
-rw-r--r--arch/x86/boot/video-vga.c3
-rw-r--r--arch/x86/configs/i386_defconfig1711
-rw-r--r--arch/x86/configs/x86_64_defconfig1735
-rw-r--r--arch/x86/kernel/Makefile18
-rw-r--r--arch/x86/kernel/acpi/boot.c334
-rw-r--r--arch/x86/kernel/acpi/sleep.c2
-rw-r--r--arch/x86/kernel/alternative.c22
-rw-r--r--arch/x86/kernel/aperture_64.c309
-rw-r--r--arch/x86/kernel/apic_32.c51
-rw-r--r--arch/x86/kernel/apic_64.c53
-rw-r--r--arch/x86/kernel/apm_32.c33
-rw-r--r--arch/x86/kernel/cpu/Makefile4
-rw-r--r--arch/x86/kernel/cpu/addon_cpuid_features.c3
-rw-r--r--arch/x86/kernel/cpu/amd.c45
-rw-r--r--arch/x86/kernel/cpu/amd_64.c206
-rw-r--r--arch/x86/kernel/cpu/bugs.c27
-rw-r--r--arch/x86/kernel/cpu/bugs_64.c (renamed from arch/x86/kernel/bugs_64.c)0
-rw-r--r--arch/x86/kernel/cpu/centaur_64.c31
-rw-r--r--arch/x86/kernel/cpu/common.c2
-rw-r--r--arch/x86/kernel/cpu/cpu.h5
-rw-r--r--arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c6
-rw-r--r--arch/x86/kernel/cpu/cpufreq/p4-clockmod.c6
-rw-r--r--arch/x86/kernel/cpu/cpufreq/powernow-k8.c8
-rw-r--r--arch/x86/kernel/cpu/cpufreq/speedstep-centrino.c10
-rw-r--r--arch/x86/kernel/cpu/cpufreq/speedstep-ich.c4
-rw-r--r--arch/x86/kernel/cpu/intel_64.c92
-rw-r--r--arch/x86/kernel/cpu/intel_cacheinfo.c3
-rw-r--r--arch/x86/kernel/cpu/mcheck/mce_64.c20
-rw-r--r--arch/x86/kernel/cpu/mcheck/mce_amd_64.c4
-rw-r--r--arch/x86/kernel/cpu/mtrr/generic.c38
-rw-r--r--arch/x86/kernel/cpu/mtrr/main.c901
-rw-r--r--arch/x86/kernel/cpu/mtrr/mtrr.h3
-rw-r--r--arch/x86/kernel/cpu/perfctr-watchdog.c4
-rw-r--r--arch/x86/kernel/cpuid.c4
-rw-r--r--arch/x86/kernel/e820.c892
-rw-r--r--arch/x86/kernel/e820_32.c536
-rw-r--r--arch/x86/kernel/e820_64.c787
-rw-r--r--arch/x86/kernel/early-quirks.c13
-rw-r--r--arch/x86/kernel/efi.c59
-rw-r--r--arch/x86/kernel/efi_64.c8
-rw-r--r--arch/x86/kernel/entry_32.S71
-rw-r--r--arch/x86/kernel/entry_64.S106
-rw-r--r--arch/x86/kernel/ftrace.c149
-rw-r--r--arch/x86/kernel/genapic_64.c2
-rw-r--r--arch/x86/kernel/genx2apic_uv_x.c141
-rw-r--r--arch/x86/kernel/head.c73
-rw-r--r--arch/x86/kernel/head32.c27
-rw-r--r--arch/x86/kernel/head64.c90
-rw-r--r--arch/x86/kernel/head_32.S8
-rw-r--r--arch/x86/kernel/head_64.S27
-rw-r--r--arch/x86/kernel/hpet.c43
-rw-r--r--arch/x86/kernel/i386_ksyms_32.c9
-rw-r--r--arch/x86/kernel/i8259.c (renamed from arch/x86/kernel/i8259_32.c)136
-rw-r--r--arch/x86/kernel/i8259_64.c512
-rw-r--r--arch/x86/kernel/io_apic_32.c600
-rw-r--r--arch/x86/kernel/io_apic_64.c203
-rw-r--r--arch/x86/kernel/ipi.c1
-rw-r--r--arch/x86/kernel/irq_32.c254
-rw-r--r--arch/x86/kernel/irq_64.c28
-rw-r--r--arch/x86/kernel/irqinit_32.c114
-rw-r--r--arch/x86/kernel/irqinit_64.c217
-rw-r--r--arch/x86/kernel/ldt.c6
-rw-r--r--arch/x86/kernel/machine_kexec_32.c4
-rw-r--r--arch/x86/kernel/machine_kexec_64.c4
-rw-r--r--arch/x86/kernel/mfgpt_32.c2
-rw-r--r--arch/x86/kernel/microcode.c4
-rw-r--r--arch/x86/kernel/mmconf-fam10h_64.c1
-rw-r--r--arch/x86/kernel/mpparse.c808
-rw-r--r--arch/x86/kernel/msr.c4
-rw-r--r--arch/x86/kernel/nmi.c (renamed from arch/x86/kernel/nmi_64.c)218
-rw-r--r--arch/x86/kernel/nmi_32.c472
-rw-r--r--arch/x86/kernel/numaq_32.c25
-rw-r--r--arch/x86/kernel/paravirt.c1
-rw-r--r--arch/x86/kernel/pci-dma.c33
-rw-r--r--arch/x86/kernel/pci-gart_64.c27
-rw-r--r--arch/x86/kernel/process.c178
-rw-r--r--arch/x86/kernel/process_32.c64
-rw-r--r--arch/x86/kernel/process_64.c51
-rw-r--r--arch/x86/kernel/quirks.c58
-rw-r--r--arch/x86/kernel/reboot.c18
-rw-r--r--arch/x86/kernel/reboot_fixups_32.c4
-rw-r--r--arch/x86/kernel/setup.c335
-rw-r--r--arch/x86/kernel/setup64.c8
-rw-r--r--arch/x86/kernel/setup_32.c297
-rw-r--r--arch/x86/kernel/setup_64.c426
-rw-r--r--arch/x86/kernel/smpboot.c113
-rw-r--r--arch/x86/kernel/srat_32.c43
-rw-r--r--arch/x86/kernel/summit_32.c2
-rw-r--r--arch/x86/kernel/sys_i386_32.c64
-rw-r--r--arch/x86/kernel/time_32.c3
-rw-r--r--arch/x86/kernel/trampoline.c2
-rw-r--r--arch/x86/kernel/traps_32.c10
-rw-r--r--arch/x86/kernel/traps_64.c32
-rw-r--r--arch/x86/kernel/tsc_32.c6
-rw-r--r--arch/x86/kernel/tsc_64.c2
-rw-r--r--arch/x86/kernel/vmiclock_32.c3
-rw-r--r--arch/x86/kernel/vmlinux_32.lds.S16
-rw-r--r--arch/x86/kernel/vmlinux_64.lds.S17
-rw-r--r--arch/x86/kernel/vsmp_64.c3
-rw-r--r--arch/x86/kernel/vsyscall_64.c3
-rw-r--r--arch/x86/kernel/x8664_ksyms_64.c11
-rw-r--r--arch/x86/kvm/Makefile3
-rw-r--r--arch/x86/kvm/i8254.c31
-rw-r--r--arch/x86/kvm/i8259.c3
-rw-r--r--arch/x86/kvm/irq.c6
-rw-r--r--arch/x86/kvm/irq.h2
-rw-r--r--arch/x86/kvm/lapic.c13
-rw-r--r--arch/x86/kvm/lapic.h1
-rw-r--r--arch/x86/kvm/mmu.c64
-rw-r--r--arch/x86/kvm/mmu.h3
-rw-r--r--arch/x86/kvm/paging_tmpl.h30
-rw-r--r--arch/x86/kvm/svm.c87
-rw-r--r--arch/x86/kvm/vmx.c190
-rw-r--r--arch/x86/kvm/vmx.h12
-rw-r--r--arch/x86/kvm/x86.c255
-rw-r--r--arch/x86/kvm/x86_emulate.c83
-rw-r--r--arch/x86/lguest/boot.c5
-rw-r--r--arch/x86/lib/Makefile1
-rw-r--r--arch/x86/lib/thunk_32.S47
-rw-r--r--arch/x86/lib/thunk_64.S19
-rw-r--r--arch/x86/mach-default/setup.c7
-rw-r--r--arch/x86/mach-es7000/Makefile1
-rw-r--r--arch/x86/mach-es7000/es7000plat.c49
-rw-r--r--arch/x86/mach-generic/Makefile10
-rw-r--r--arch/x86/mach-generic/bigsmp.c4
-rw-r--r--arch/x86/mach-generic/numaq.c41
-rw-r--r--arch/x86/mach-generic/probe.c15
-rw-r--r--arch/x86/mach-visws/mpparse.c7
-rw-r--r--arch/x86/mach-visws/visws_apic.c3
-rw-r--r--arch/x86/mach-voyager/setup.c7
-rw-r--r--arch/x86/mach-voyager/voyager_smp.c14
-rw-r--r--arch/x86/mm/Makefile5
-rw-r--r--arch/x86/mm/discontig_32.c210
-rw-r--r--arch/x86/mm/dump_pagetables.c2
-rw-r--r--arch/x86/mm/fault.c25
-rw-r--r--arch/x86/mm/init_32.c25
-rw-r--r--arch/x86/mm/init_64.c40
-rw-r--r--arch/x86/mm/ioremap.c21
-rw-r--r--arch/x86/mm/k8topology_64.c13
-rw-r--r--arch/x86/mm/kmmio.c510
-rw-r--r--arch/x86/mm/mmio-mod.c515
-rw-r--r--arch/x86/mm/numa_64.c89
-rw-r--r--arch/x86/mm/pageattr.c40
-rw-r--r--arch/x86/mm/pat.c35
-rw-r--r--arch/x86/mm/pf_in.c489
-rw-r--r--arch/x86/mm/pf_in.h39
-rw-r--r--arch/x86/mm/pgtable.c2
-rw-r--r--arch/x86/mm/srat_64.c32
-rw-r--r--arch/x86/mm/testmmiotrace.c71
-rw-r--r--arch/x86/pci/Makefile_325
-rw-r--r--arch/x86/pci/acpi.c18
-rw-r--r--arch/x86/pci/common.c34
-rw-r--r--arch/x86/pci/direct.c21
-rw-r--r--arch/x86/pci/early.c60
-rw-r--r--arch/x86/pci/irq.c511
-rw-r--r--arch/x86/pci/k8-bus_64.c4
-rw-r--r--arch/x86/pci/mmconfig-shared.c2
-rw-r--r--arch/x86/pci/numa.c29
-rw-r--r--arch/x86/pci/pci.h1
-rw-r--r--arch/x86/vdso/vclock_gettime.c15
-rw-r--r--arch/x86/vdso/vgetcpu.c3
-rw-r--r--arch/x86/vdso/vma.c2
-rw-r--r--arch/x86/xen/Kconfig12
-rw-r--r--arch/x86/xen/Makefile2
-rw-r--r--arch/x86/xen/enlighten.c166
-rw-r--r--arch/x86/xen/manage.c143
-rw-r--r--arch/x86/xen/mmu.c227
-rw-r--r--arch/x86/xen/mmu.h32
-rw-r--r--arch/x86/xen/setup.c5
-rw-r--r--arch/x86/xen/smp.c12
-rw-r--r--arch/x86/xen/suspend.c45
-rw-r--r--arch/x86/xen/time.c26
-rw-r--r--arch/x86/xen/xen-head.S9
-rw-r--r--arch/x86/xen/xen-ops.h11
-rw-r--r--block/blk-core.c14
-rw-r--r--block/blk-exec.c6
-rw-r--r--block/blktrace.c10
-rw-r--r--block/bsg.c12
-rw-r--r--block/genhd.c205
-rw-r--r--crypto/async_tx/async_tx.c1
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/acpi/Kconfig10
-rw-r--r--drivers/acpi/Makefile1
-rw-r--r--drivers/acpi/bay.c16
-rw-r--r--drivers/acpi/dispatcher/dsfield.c5
-rw-r--r--drivers/acpi/dock.c2
-rw-r--r--drivers/acpi/ec.c2
-rw-r--r--drivers/acpi/executer/exconfig.c10
-rw-r--r--drivers/acpi/fan.c10
-rw-r--r--drivers/acpi/glue.c12
-rw-r--r--drivers/acpi/hardware/hwsleep.c8
-rw-r--r--drivers/acpi/numa.c31
-rw-r--r--drivers/acpi/parser/psargs.c4
-rw-r--r--drivers/acpi/pci_irq.c5
-rw-r--r--drivers/acpi/pci_slot.c368
-rw-r--r--drivers/acpi/processor_core.c40
-rw-r--r--drivers/acpi/processor_idle.c21
-rw-r--r--drivers/acpi/processor_perflib.c18
-rw-r--r--drivers/acpi/processor_throttling.c20
-rw-r--r--drivers/acpi/scan.c58
-rw-r--r--drivers/acpi/sleep/main.c303
-rw-r--r--drivers/acpi/sleep/proc.c6
-rw-r--r--drivers/acpi/system.c15
-rw-r--r--drivers/acpi/tables/tbinstal.c25
-rw-r--r--drivers/acpi/tables/tbxface.c2
-rw-r--r--drivers/acpi/thermal.c15
-rw-r--r--drivers/acpi/utilities/utmisc.c2
-rw-r--r--drivers/acpi/video.c5
-rw-r--r--drivers/atm/eni.h1
-rw-r--r--drivers/base/base.h27
-rw-r--r--drivers/base/class.c153
-rw-r--r--drivers/base/core.c189
-rw-r--r--drivers/base/cpu.c4
-rw-r--r--drivers/base/firmware_class.c2
-rw-r--r--drivers/base/platform.c296
-rw-r--r--drivers/base/power/main.c675
-rw-r--r--drivers/base/power/power.h2
-rw-r--r--drivers/base/power/trace.c8
-rw-r--r--drivers/base/topology.c63
-rw-r--r--drivers/block/aoe/aoechr.c5
-rw-r--r--drivers/block/paride/pd.c20
-rw-r--r--drivers/block/paride/pg.c5
-rw-r--r--drivers/block/paride/pt.c10
-rw-r--r--drivers/block/pktcdvd.c4
-rw-r--r--drivers/bluetooth/bfusb.c3
-rw-r--r--drivers/bluetooth/bt3c_cs.c3
-rw-r--r--drivers/char/agp/amd64-agp.c85
-rw-r--r--drivers/char/cyclades.c10
-rw-r--r--drivers/char/dsp56k.c3
-rw-r--r--drivers/char/hpet.c63
-rw-r--r--drivers/char/hvc_xen.c61
-rw-r--r--drivers/char/ip2/ip2main.c12
-rw-r--r--drivers/char/ipmi/ipmi_devintf.c2
-rw-r--r--drivers/char/istallion.c5
-rw-r--r--drivers/char/lp.c3
-rw-r--r--drivers/char/mem.c6
-rw-r--r--drivers/char/misc.c4
-rw-r--r--drivers/char/pcmcia/cm4000_cs.c2
-rw-r--r--drivers/char/pcmcia/cm4040_cs.c3
-rw-r--r--drivers/char/ppdev.c5
-rw-r--r--drivers/char/raw.c7
-rw-r--r--drivers/char/snsc.c3
-rw-r--r--drivers/char/stallion.c4
-rw-r--r--drivers/char/tty_io.c12
-rw-r--r--drivers/char/vc_screen.c12
-rw-r--r--drivers/char/viotape.c8
-rw-r--r--drivers/char/vt.c14
-rw-r--r--drivers/char/xilinx_hwicap/xilinx_hwicap.c5
-rw-r--r--drivers/cpufreq/cpufreq.c14
-rw-r--r--drivers/cpufreq/cpufreq_conservative.c2
-rw-r--r--drivers/cpufreq/cpufreq_ondemand.c4
-rw-r--r--drivers/cpuidle/cpuidle.c40
-rw-r--r--drivers/dca/dca-sysfs.c8
-rw-r--r--drivers/eisa/Makefile2
-rw-r--r--drivers/eisa/eisa-bus.c4
-rw-r--r--drivers/firewire/fw-card.c2
-rw-r--r--drivers/firewire/fw-device.h1
-rw-r--r--drivers/firewire/fw-ohci.c10
-rw-r--r--drivers/firewire/fw-sbp2.c25
-rw-r--r--drivers/firewire/fw-transaction.c35
-rw-r--r--drivers/firewire/fw-transaction.h14
-rw-r--r--drivers/firmware/dell_rbu.c2
-rw-r--r--drivers/firmware/dmi_scan.c5
-rw-r--r--drivers/hid/hid-core.c10
-rw-r--r--drivers/hid/hidraw.c52
-rw-r--r--drivers/hid/usbhid/hid-quirks.c14
-rw-r--r--drivers/hid/usbhid/hiddev.c14
-rw-r--r--drivers/hwmon/Kconfig25
-rw-r--r--drivers/hwmon/abituguru3.c18
-rw-r--r--drivers/hwmon/adt7473.c19
-rw-r--r--drivers/hwmon/dme1737.c297
-rw-r--r--drivers/hwmon/hwmon.c3
-rw-r--r--drivers/hwmon/lm75.c302
-rw-r--r--drivers/hwmon/lm85.c25
-rw-r--r--drivers/i2c/algos/i2c-algo-bit.c4
-rw-r--r--drivers/i2c/algos/i2c-algo-pca.c2
-rw-r--r--drivers/i2c/busses/Kconfig524
-rw-r--r--drivers/i2c/busses/Makefile43
-rw-r--r--drivers/i2c/busses/i2c-ali1535.c36
-rw-r--r--drivers/i2c/busses/i2c-ali1563.c38
-rw-r--r--drivers/i2c/busses/i2c-ali15x3.c30
-rw-r--r--drivers/i2c/busses/i2c-amd756-s4882.c4
-rw-r--r--drivers/i2c/busses/i2c-amd756.c32
-rw-r--r--drivers/i2c/busses/i2c-amd8111.c54
-rw-r--r--drivers/i2c/busses/i2c-cpm.c745
-rw-r--r--drivers/i2c/busses/i2c-davinci.c89
-rw-r--r--drivers/i2c/busses/i2c-elektor.c2
-rw-r--r--drivers/i2c/busses/i2c-gpio.c2
-rw-r--r--drivers/i2c/busses/i2c-i801.c52
-rw-r--r--drivers/i2c/busses/i2c-i810.c260
-rw-r--r--drivers/i2c/busses/i2c-ibm_iic.c183
-rw-r--r--drivers/i2c/busses/i2c-iop3xx.c2
-rw-r--r--drivers/i2c/busses/i2c-isch.c339
-rw-r--r--drivers/i2c/busses/i2c-mpc.c2
-rw-r--r--drivers/i2c/busses/i2c-mv64xxx.c2
-rw-r--r--drivers/i2c/busses/i2c-nforce2-s4985.c257
-rw-r--r--drivers/i2c/busses/i2c-nforce2.c49
-rw-r--r--drivers/i2c/busses/i2c-ocores.c2
-rw-r--r--drivers/i2c/busses/i2c-pasemi.c2
-rw-r--r--drivers/i2c/busses/i2c-pca-platform.c2
-rw-r--r--drivers/i2c/busses/i2c-piix4.c71
-rw-r--r--drivers/i2c/busses/i2c-pmcmsp.c2
-rw-r--r--drivers/i2c/busses/i2c-prosavage.c325
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c2
-rw-r--r--drivers/i2c/busses/i2c-savage4.c185
-rw-r--r--drivers/i2c/busses/i2c-sibyte.c4
-rw-r--r--drivers/i2c/busses/i2c-sis5595.c27
-rw-r--r--drivers/i2c/busses/i2c-sis630.c56
-rw-r--r--drivers/i2c/busses/i2c-sis96x.c34
-rw-r--r--drivers/i2c/busses/i2c-stub.c6
-rw-r--r--drivers/i2c/busses/i2c-taos-evm.c5
-rw-r--r--drivers/i2c/busses/i2c-via.c2
-rw-r--r--drivers/i2c/busses/i2c-viapro.c29
-rw-r--r--drivers/i2c/busses/scx200_acb.c2
-rw-r--r--drivers/i2c/chips/eeprom.c47
-rw-r--r--drivers/i2c/i2c-core.c359
-rw-r--r--drivers/i2c/i2c-dev.c11
-rw-r--r--drivers/ide/Kconfig33
-rw-r--r--drivers/ide/Makefile5
-rw-r--r--drivers/ide/arm/bast-ide.c2
-rw-r--r--drivers/ide/arm/icside.c105
-rw-r--r--drivers/ide/arm/ide_arm.c1
-rw-r--r--drivers/ide/arm/palm_bk3710.c7
-rw-r--r--drivers/ide/arm/rapide.c33
-rw-r--r--drivers/ide/h8300/ide-h8300.c25
-rw-r--r--drivers/ide/ide-acpi.c6
-rw-r--r--drivers/ide/ide-atapi.c296
-rw-r--r--drivers/ide/ide-cd.c122
-rw-r--r--drivers/ide/ide-cd.h4
-rw-r--r--drivers/ide/ide-cd_ioctl.c113
-rw-r--r--drivers/ide/ide-disk.c13
-rw-r--r--drivers/ide/ide-dma.c4
-rw-r--r--drivers/ide/ide-floppy.c357
-rw-r--r--drivers/ide/ide-generic.c1
-rw-r--r--drivers/ide/ide-io.c114
-rw-r--r--drivers/ide/ide-iops.c36
-rw-r--r--drivers/ide/ide-lib.c151
-rw-r--r--drivers/ide/ide-pio-blacklist.c94
-rw-r--r--drivers/ide/ide-pnp.c5
-rw-r--r--drivers/ide/ide-probe.c37
-rw-r--r--drivers/ide/ide-proc.c1
-rw-r--r--drivers/ide/ide-tape.c571
-rw-r--r--drivers/ide/ide-taskfile.c72
-rw-r--r--drivers/ide/ide-timings.c (renamed from drivers/ide/ide-timing.h)205
-rw-r--r--drivers/ide/ide.c393
-rw-r--r--drivers/ide/legacy/ali14xx.c5
-rw-r--r--drivers/ide/legacy/buddha.c3
-rw-r--r--drivers/ide/legacy/falconide.c3
-rw-r--r--drivers/ide/legacy/gayle.c7
-rw-r--r--drivers/ide/legacy/ht6560b.c17
-rw-r--r--drivers/ide/legacy/ide-4drives.c25
-rw-r--r--drivers/ide/legacy/ide-cs.c46
-rw-r--r--drivers/ide/legacy/ide_platform.c9
-rw-r--r--drivers/ide/legacy/macide.c3
-rw-r--r--drivers/ide/legacy/q40ide.c3
-rw-r--r--drivers/ide/legacy/qd65xx.c23
-rw-r--r--drivers/ide/mips/au1xxx-ide.c13
-rw-r--r--drivers/ide/mips/swarm.c28
-rw-r--r--drivers/ide/pci/aec62xx.c2
-rw-r--r--drivers/ide/pci/alim15x3.c8
-rw-r--r--drivers/ide/pci/amd74xx.c18
-rw-r--r--drivers/ide/pci/cmd640.c141
-rw-r--r--drivers/ide/pci/cmd64x.c10
-rw-r--r--drivers/ide/pci/cs5535.c6
-rw-r--r--drivers/ide/pci/cy82c693.c11
-rw-r--r--drivers/ide/pci/delkin_cb.c29
-rw-r--r--drivers/ide/pci/hpt366.c3
-rw-r--r--drivers/ide/pci/ns87415.c4
-rw-r--r--drivers/ide/pci/opti621.c19
-rw-r--r--drivers/ide/pci/scc_pata.c19
-rw-r--r--drivers/ide/pci/sgiioc4.c30
-rw-r--r--drivers/ide/pci/siimage.c25
-rw-r--r--drivers/ide/pci/sis5513.c8
-rw-r--r--drivers/ide/pci/sl82c105.c3
-rw-r--r--drivers/ide/pci/via82cxxx.c18
-rw-r--r--drivers/ide/ppc/Makefile1
-rw-r--r--drivers/ide/ppc/mpc8xx.c847
-rw-r--r--drivers/ide/ppc/pmac.c87
-rw-r--r--drivers/ide/setup-pci.c30
-rw-r--r--drivers/ieee1394/csr1212.c32
-rw-r--r--drivers/ieee1394/dv1394.c7
-rw-r--r--drivers/ieee1394/highlevel.c4
-rw-r--r--drivers/ieee1394/highlevel.h13
-rw-r--r--drivers/ieee1394/nodemgr.c23
-rw-r--r--drivers/ieee1394/raw1394.c24
-rw-r--r--drivers/ieee1394/sbp2.c22
-rw-r--r--drivers/ieee1394/sbp2.h1
-rw-r--r--drivers/ieee1394/video1394.c8
-rw-r--r--drivers/infiniband/core/addr.c41
-rw-r--r--drivers/infiniband/core/agent.h2
-rw-r--r--drivers/infiniband/core/cache.c2
-rw-r--r--drivers/infiniband/core/cm.c74
-rw-r--r--drivers/infiniband/core/cma.c42
-rw-r--r--drivers/infiniband/core/core_priv.h2
-rw-r--r--drivers/infiniband/core/device.c2
-rw-r--r--drivers/infiniband/core/fmr_pool.c2
-rw-r--r--drivers/infiniband/core/mad_priv.h2
-rw-r--r--drivers/infiniband/core/mad_rmpp.c2
-rw-r--r--drivers/infiniband/core/mad_rmpp.h2
-rw-r--r--drivers/infiniband/core/packer.c2
-rw-r--r--drivers/infiniband/core/sa_query.c24
-rw-r--r--drivers/infiniband/core/sysfs.c2
-rw-r--r--drivers/infiniband/core/ucm.c2
-rw-r--r--drivers/infiniband/core/ud_header.c2
-rw-r--r--drivers/infiniband/core/umem.c2
-rw-r--r--drivers/infiniband/core/user_mad.c2
-rw-r--r--drivers/infiniband/core/uverbs.h2
-rw-r--r--drivers/infiniband/core/uverbs_cmd.c4
-rw-r--r--drivers/infiniband/core/uverbs_main.c2
-rw-r--r--drivers/infiniband/core/verbs.c48
-rw-r--r--drivers/infiniband/hw/ehca/ehca_irq.c4
-rw-r--r--drivers/infiniband/hw/ehca/ehca_reqs.c2
-rw-r--r--drivers/infiniband/hw/ipath/ipath_cq.c2
-rw-r--r--drivers/infiniband/hw/ipath/ipath_file_ops.c2
-rw-r--r--drivers/infiniband/hw/ipath/ipath_iba7220.c4
-rw-r--r--drivers/infiniband/hw/ipath/ipath_rc.c4
-rw-r--r--drivers/infiniband/hw/ipath/ipath_ruc.c4
-rw-r--r--drivers/infiniband/hw/ipath/ipath_uc.c8
-rw-r--r--drivers/infiniband/hw/ipath/ipath_ud.c8
-rw-r--r--drivers/infiniband/hw/ipath/ipath_verbs.c1
-rw-r--r--drivers/infiniband/hw/ipath/ipath_verbs_mcast.c3
-rw-r--r--drivers/infiniband/hw/mlx4/cq.c12
-rw-r--r--drivers/infiniband/hw/mlx4/qp.c8
-rw-r--r--drivers/infiniband/hw/mthca/mthca_allocator.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_av.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_catas.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_cmd.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_cmd.h2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_config_reg.h2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_cq.c6
-rw-r--r--drivers/infiniband/hw/mthca/mthca_dev.h2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_doorbell.h2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_eq.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_mad.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_main.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_mcg.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_memfree.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_memfree.h2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_mr.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_pd.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_profile.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_profile.h2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_provider.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_provider.h2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_qp.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_reset.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_srq.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_uar.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_user.h1
-rw-r--r--drivers/infiniband/hw/mthca/mthca_wqe.h2
-rw-r--r--drivers/infiniband/hw/nes/nes_cm.c1
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib.h3
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_cm.c21
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_fs.c2
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_ib.c2
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c8
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_multicast.c2
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_verbs.c2
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_vlan.c2
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.c358
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.h46
-rw-r--r--drivers/infiniband/ulp/iser/iser_initiator.c211
-rw-r--r--drivers/infiniband/ulp/iser/iser_memory.c79
-rw-r--r--drivers/infiniband/ulp/iser/iser_verbs.c30
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.c15
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.h2
-rw-r--r--drivers/input/misc/Kconfig1
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h7
-rw-r--r--drivers/input/xen-kbdfront.c20
-rw-r--r--drivers/isdn/capi/capi.c3
-rw-r--r--drivers/isdn/hardware/eicon/divasmain.c1
-rw-r--r--drivers/isdn/hardware/eicon/divasproc.c4
-rw-r--r--drivers/isdn/hysdn/hysdn_procconf.c29
-rw-r--r--drivers/lguest/lg.h1
-rw-r--r--drivers/macintosh/adb.c3
-rw-r--r--drivers/macintosh/mediabay.c3
-rw-r--r--drivers/mca/mca-bus.c2
-rw-r--r--drivers/md/Kconfig19
-rw-r--r--drivers/md/Makefile7
-rw-r--r--drivers/md/dm-emc.c345
-rw-r--r--drivers/md/dm-hw-handler.c213
-rw-r--r--drivers/md/dm-hw-handler.h63
-rw-r--r--drivers/md/dm-mpath-hp-sw.c247
-rw-r--r--drivers/md/dm-mpath-rdac.c700
-rw-r--r--drivers/md/dm-mpath.c161
-rw-r--r--drivers/md/dm-mpath.h1
-rw-r--r--drivers/media/common/tuners/tda18271-maps.c2
-rw-r--r--drivers/media/common/tuners/tuner-i2c.h8
-rw-r--r--drivers/media/common/tuners/tuner-xc2028.c2
-rw-r--r--drivers/media/common/tuners/xc5000.c2
-rw-r--r--drivers/media/dvb/dvb-core/dvbdev.c4
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig9
-rw-r--r--drivers/media/dvb/dvb-usb/Makefile3
-rw-r--r--drivers/media/dvb/dvb-usb/anysee.c555
-rw-r--r--drivers/media/dvb/dvb-usb/anysee.h304
-rw-r--r--drivers/media/dvb/dvb-usb/au6610.c83
-rw-r--r--drivers/media/dvb/dvb-usb/au6610.h22
-rw-r--r--drivers/media/dvb/dvb-usb/cxusb.c21
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-ids.h2
-rw-r--r--drivers/media/dvb/dvb-usb/gl861.c65
-rw-r--r--drivers/media/dvb/dvb-usb/gl861.h2
-rw-r--r--drivers/media/dvb/dvb-usb/gp8psk.c3
-rw-r--r--drivers/media/dvb/frontends/Kconfig14
-rw-r--r--drivers/media/dvb/frontends/Makefile1
-rw-r--r--drivers/media/dvb/frontends/au8522.c1
-rw-r--r--drivers/media/dvb/frontends/bcm3510.c5
-rw-r--r--drivers/media/dvb/frontends/drx397xD.c1504
-rw-r--r--drivers/media/dvb/frontends/drx397xD.h130
-rw-r--r--drivers/media/dvb/frontends/drx397xD_fw.h40
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.c47
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.h1
-rw-r--r--drivers/media/dvb/frontends/nxt200x.c3
-rw-r--r--drivers/media/dvb/frontends/or51211.c4
-rw-r--r--drivers/media/dvb/frontends/s5h1409.c1
-rw-r--r--drivers/media/dvb/frontends/s5h1411.c1
-rw-r--r--drivers/media/dvb/frontends/sp8870.c2
-rw-r--r--drivers/media/dvb/frontends/sp887x.c2
-rw-r--r--drivers/media/dvb/frontends/tda10023.c193
-rw-r--r--drivers/media/dvb/frontends/tda1002x.h31
-rw-r--r--drivers/media/dvb/frontends/tda10048.c2
-rw-r--r--drivers/media/dvb/frontends/tda1004x.c2
-rw-r--r--drivers/media/dvb/ttpci/Kconfig1
-rw-r--r--drivers/media/dvb/ttpci/budget-av.c12
-rw-r--r--drivers/media/dvb/ttusb-dec/ttusb_dec.c2
-rw-r--r--drivers/media/radio/radio-si470x.c483
-rw-r--r--drivers/media/video/bt8xx/bttv-cards.c3
-rw-r--r--drivers/media/video/bt8xx/bttv-driver.c44
-rw-r--r--drivers/media/video/bt8xx/bttv-vbi.c6
-rw-r--r--drivers/media/video/bt8xx/bttvp.h6
-rw-r--r--drivers/media/video/cafe_ccic.c18
-rw-r--r--drivers/media/video/compat_ioctl32.c1
-rw-r--r--drivers/media/video/cx18/cx18-av-core.c2
-rw-r--r--drivers/media/video/cx18/cx18-av-core.h6
-rw-r--r--drivers/media/video/cx18/cx18-cards.c61
-rw-r--r--drivers/media/video/cx18/cx18-cards.h40
-rw-r--r--drivers/media/video/cx18/cx18-driver.c2
-rw-r--r--drivers/media/video/cx23885/cx23885-cards.c13
-rw-r--r--drivers/media/video/cx23885/cx23885-dvb.c27
-rw-r--r--drivers/media/video/cx23885/cx23885-video.c24
-rw-r--r--drivers/media/video/cx23885/cx23885.h1
-rw-r--r--drivers/media/video/cx25840/cx25840-core.c2
-rw-r--r--drivers/media/video/cx25840/cx25840-firmware.c27
-rw-r--r--drivers/media/video/cx88/cx88-blackbird.c16
-rw-r--r--drivers/media/video/cx88/cx88-video.c24
-rw-r--r--drivers/media/video/em28xx/em28xx-audio.c12
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c60
-rw-r--r--drivers/media/video/em28xx/em28xx-dvb.c33
-rw-r--r--drivers/media/video/em28xx/em28xx-reg.h1
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c54
-rw-r--r--drivers/media/video/em28xx/em28xx.h2
-rw-r--r--drivers/media/video/ivtv/ivtv-cards.c41
-rw-r--r--drivers/media/video/ivtv/ivtv-cards.h3
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.c3
-rw-r--r--drivers/media/video/ivtv/ivtv-gpio.c11
-rw-r--r--drivers/media/video/ivtv/ivtvfb.c82
-rw-r--r--drivers/media/video/meye.c18
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-audio.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-audio.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-context.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-context.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-ctrl.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-ctrl.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-debug.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-debugifc.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-debugifc.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-devattr.c11
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-devattr.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-eeprom.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-eeprom.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-encoder.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-encoder.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.c46
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-i2c-core.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-i2c-core.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-io.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-io.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-ioread.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-ioread.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-main.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-std.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-std.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-sysfs.c461
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-sysfs.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-tuner.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-tuner.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-util.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-video-v4l.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-video-v4l.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-wm8775.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-wm8775.h1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2.h1
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c95
-rw-r--r--drivers/media/video/saa7134/saa7134-dvb.c3
-rw-r--r--drivers/media/video/saa7134/saa7134-empress.c45
-rw-r--r--drivers/media/video/saa7134/saa7134-input.c2
-rw-r--r--drivers/media/video/saa7134/saa7134-reg.h1
-rw-r--r--drivers/media/video/saa7134/saa7134-tvaudio.c33
-rw-r--r--drivers/media/video/saa7134/saa7134-video.c52
-rw-r--r--drivers/media/video/saa7134/saa7134.h3
-rw-r--r--drivers/media/video/soc_camera.c18
-rw-r--r--drivers/media/video/stk-webcam.c18
-rw-r--r--drivers/media/video/tcm825x.c6
-rw-r--r--drivers/media/video/tcm825x.h1
-rw-r--r--drivers/media/video/usbvision/usbvision-video.c18
-rw-r--r--drivers/media/video/videodev.c293
-rw-r--r--drivers/media/video/vivi.c37
-rw-r--r--drivers/media/video/zr364xx.c16
-rw-r--r--drivers/memstick/host/jmb38x_ms.c4
-rw-r--r--drivers/message/fusion/lsi/mpi.h2
-rw-r--r--drivers/message/fusion/lsi/mpi_cnfg.h2
-rw-r--r--drivers/message/fusion/mptbase.c100
-rw-r--r--drivers/message/fusion/mptbase.h19
-rw-r--r--drivers/message/fusion/mptctl.c4
-rw-r--r--drivers/message/fusion/mptctl.h2
-rw-r--r--drivers/message/fusion/mptdebug.h2
-rw-r--r--drivers/message/fusion/mptfc.c6
-rw-r--r--drivers/message/fusion/mptlan.c2
-rw-r--r--drivers/message/fusion/mptlan.h2
-rw-r--r--drivers/message/fusion/mptsas.c2
-rw-r--r--drivers/message/fusion/mptsas.h2
-rw-r--r--drivers/message/fusion/mptscsih.c2
-rw-r--r--drivers/message/fusion/mptscsih.h2
-rw-r--r--drivers/message/fusion/mptspi.c3
-rw-r--r--drivers/misc/Kconfig17
-rw-r--r--drivers/misc/Makefile6
-rw-r--r--drivers/misc/compal-laptop.c437
-rw-r--r--drivers/misc/phantom.c5
-rw-r--r--drivers/misc/thinkpad_acpi.c494
-rw-r--r--drivers/mtd/mtdchar.c9
-rw-r--r--drivers/mtd/ubi/build.c72
-rw-r--r--drivers/mtd/ubi/cdev.c6
-rw-r--r--drivers/mtd/ubi/eba.c19
-rw-r--r--drivers/mtd/ubi/kapi.c6
-rw-r--r--drivers/mtd/ubi/misc.c2
-rw-r--r--drivers/mtd/ubi/ubi.h1
-rw-r--r--drivers/mtd/ubi/upd.c6
-rw-r--r--drivers/mtd/ubi/vmt.c20
-rw-r--r--drivers/mtd/ubi/vtbl.c55
-rw-r--r--drivers/mtd/ubi/wl.c3
-rw-r--r--drivers/net/7990.c6
-rw-r--r--drivers/net/arm/at91_ether.c2
-rw-r--r--drivers/net/arm/ep93xx_eth.c2
-rw-r--r--drivers/net/arm/etherh.c6
-rw-r--r--drivers/net/atlx/atl1.c17
-rw-r--r--drivers/net/cxgb3/common.h5
-rw-r--r--drivers/net/cxgb3/t3_hw.c7
-rw-r--r--drivers/net/ehea/ehea_main.c16
-rw-r--r--drivers/net/fec_mpc52xx.c2
-rw-r--r--drivers/net/forcedeth.c20
-rw-r--r--drivers/net/ipg.c4
-rw-r--r--drivers/net/irda/Kconfig1
-rw-r--r--drivers/net/irda/irda-usb.c12
-rw-r--r--drivers/net/ixgbe/ixgbe_82598.c4
-rw-r--r--drivers/net/macvlan.c2
-rw-r--r--drivers/net/myri10ge/myri10ge.c11
-rw-r--r--drivers/net/phy/phy_device.c8
-rw-r--r--drivers/net/ppp_generic.c3
-rw-r--r--drivers/net/pppoe.c37
-rw-r--r--drivers/net/pppol2tp.c20
-rw-r--r--drivers/net/r6040.c2
-rw-r--r--drivers/net/sfc/falcon.c4
-rw-r--r--drivers/net/sky2.c2
-rw-r--r--drivers/net/smc91x.h8
-rw-r--r--drivers/net/tg3.c33
-rw-r--r--drivers/net/virtio_net.c52
-rw-r--r--drivers/net/wan/cosa.c6
-rw-r--r--drivers/net/wireless/atmel.c6
-rw-r--r--drivers/net/wireless/ipw2200.c176
-rw-r--r--drivers/net/wireless/ipw2200.h6
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945-led.c5
-rw-r--r--drivers/net/wireless/libertas/cmd.c5
-rw-r--r--drivers/net/wireless/libertas/if_cs.c2
-rw-r--r--drivers/net/wireless/libertas/if_sdio.c4
-rw-r--r--drivers/net/wireless/libertas/if_usb.c4
-rw-r--r--drivers/net/wireless/libertas/main.c2
-rw-r--r--drivers/net/wireless/p54/p54usb.c5
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00.h4
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00pci.h2
-rw-r--r--drivers/net/wireless/rt2x00/rt61pci.c4
-rw-r--r--drivers/net/wireless/rt2x00/rt73usb.c6
-rw-r--r--drivers/net/wireless/zd1201.c2
-rw-r--r--drivers/net/wireless/zd1211rw/zd_mac.c2
-rw-r--r--drivers/pci/Makefile2
-rw-r--r--drivers/pci/hotplug/acpi_pcihp.c85
-rw-r--r--drivers/pci/hotplug/acpiphp.h5
-rw-r--r--drivers/pci/hotplug/acpiphp_core.c25
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c23
-rw-r--r--drivers/pci/hotplug/acpiphp_ibm.c6
-rw-r--r--drivers/pci/hotplug/cpci_hotplug_core.c2
-rw-r--r--drivers/pci/hotplug/cpqphp_core.c4
-rw-r--r--drivers/pci/hotplug/fakephp.c86
-rw-r--r--drivers/pci/hotplug/ibmphp_ebda.c3
-rw-r--r--drivers/pci/hotplug/pci_hotplug_core.c274
-rw-r--r--drivers/pci/hotplug/pciehp.h9
-rw-r--r--drivers/pci/hotplug/pciehp_core.c45
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c104
-rw-r--r--drivers/pci/hotplug/rpadlpar_sysfs.c5
-rw-r--r--drivers/pci/hotplug/rpaphp_slot.c44
-rw-r--r--drivers/pci/hotplug/sgi_hotplug.c12
-rw-r--r--drivers/pci/hotplug/shpchp.h14
-rw-r--r--drivers/pci/hotplug/shpchp_core.c37
-rw-r--r--drivers/pci/hotplug/shpchp_hpc.c1
-rw-r--r--drivers/pci/intel-iommu.c1
-rw-r--r--drivers/pci/msi.c12
-rw-r--r--drivers/pci/pci-acpi.c222
-rw-r--r--drivers/pci/pci-driver.c390
-rw-r--r--drivers/pci/pci-sysfs.c85
-rw-r--r--drivers/pci/pci.c6
-rw-r--r--drivers/pci/pci.h16
-rw-r--r--drivers/pci/pcie/aer/aerdrv.c1
-rw-r--r--drivers/pci/pcie/portdrv_bus.c1
-rw-r--r--drivers/pci/probe.c45
-rw-r--r--drivers/pci/proc.c4
-rw-r--r--drivers/pci/quirks.c134
-rw-r--r--drivers/pci/slot.c233
-rw-r--r--drivers/pnp/pnpacpi/core.c4
-rw-r--r--drivers/pnp/pnpacpi/rsparser.c46
-rw-r--r--drivers/power/apm_power.c2
-rw-r--r--drivers/power/power_supply_core.c4
-rw-r--r--drivers/rtc/interface.c2
-rw-r--r--drivers/s390/block/dasd.c48
-rw-r--r--drivers/s390/block/dasd_3990_erp.c17
-rw-r--r--drivers/s390/block/dasd_devmap.c28
-rw-r--r--drivers/s390/block/dasd_eckd.c18
-rw-r--r--drivers/s390/block/dasd_eer.c4
-rw-r--r--drivers/s390/block/dasd_fba.c16
-rw-r--r--drivers/s390/block/dasd_int.h4
-rw-r--r--drivers/s390/block/dasd_proc.c2
-rw-r--r--drivers/s390/block/dcssblk.c7
-rw-r--r--drivers/s390/char/con3215.c6
-rw-r--r--drivers/s390/char/con3270.c6
-rw-r--r--drivers/s390/char/fs3270.c6
-rw-r--r--drivers/s390/char/raw3270.c30
-rw-r--r--drivers/s390/char/sclp_vt220.c1
-rw-r--r--drivers/s390/char/tape_34xx.c12
-rw-r--r--drivers/s390/char/tape_3590.c152
-rw-r--r--drivers/s390/char/tape_block.c2
-rw-r--r--drivers/s390/char/tape_class.c7
-rw-r--r--drivers/s390/char/tape_core.c35
-rw-r--r--drivers/s390/char/tape_proc.c2
-rw-r--r--drivers/s390/char/tape_std.c12
-rw-r--r--drivers/s390/char/tty3270.c6
-rw-r--r--drivers/s390/char/vmlogrdr.c5
-rw-r--r--drivers/s390/char/vmur.c16
-rw-r--r--drivers/s390/cio/Makefile3
-rw-r--r--drivers/s390/cio/airq.c45
-rw-r--r--drivers/s390/cio/blacklist.c6
-rw-r--r--drivers/s390/cio/ccwgroup.c3
-rw-r--r--drivers/s390/cio/chp.c76
-rw-r--r--drivers/s390/cio/chp.h15
-rw-r--r--drivers/s390/cio/chsc.c292
-rw-r--r--drivers/s390/cio/chsc.h7
-rw-r--r--drivers/s390/cio/cio.c297
-rw-r--r--drivers/s390/cio/cio.h16
-rw-r--r--drivers/s390/cio/cmf.c11
-rw-r--r--drivers/s390/cio/css.c269
-rw-r--r--drivers/s390/cio/css.h43
-rw-r--r--drivers/s390/cio/device.c471
-rw-r--r--drivers/s390/cio/device.h7
-rw-r--r--drivers/s390/cio/device_fsm.c216
-rw-r--r--drivers/s390/cio/device_id.c16
-rw-r--r--drivers/s390/cio/device_ops.c136
-rw-r--r--drivers/s390/cio/device_pgid.c26
-rw-r--r--drivers/s390/cio/device_status.c133
-rw-r--r--drivers/s390/cio/fcx.c350
-rw-r--r--drivers/s390/cio/io_sch.h46
-rw-r--r--drivers/s390/cio/isc.c68
-rw-r--r--drivers/s390/cio/itcw.c327
-rw-r--r--drivers/s390/cio/qdio.c39
-rw-r--r--drivers/s390/cio/qdio.h3
-rw-r--r--drivers/s390/cio/scsw.c843
-rw-r--r--drivers/s390/crypto/ap_bus.c4
-rw-r--r--drivers/s390/kvm/kvm_virtio.c15
-rw-r--r--drivers/s390/net/claw.c125
-rw-r--r--drivers/s390/net/claw.h2
-rw-r--r--drivers/s390/net/ctcm_fsms.c12
-rw-r--r--drivers/s390/net/ctcm_main.c48
-rw-r--r--drivers/s390/net/ctcm_main.h2
-rw-r--r--drivers/s390/net/lcs.c67
-rw-r--r--drivers/s390/net/netiucv.c4
-rw-r--r--drivers/s390/net/qeth_core.h10
-rw-r--r--drivers/s390/net/qeth_core_main.c71
-rw-r--r--drivers/s390/net/qeth_core_offl.c6
-rw-r--r--drivers/s390/net/qeth_core_sys.c12
-rw-r--r--drivers/s390/net/qeth_l2_main.c41
-rw-r--r--drivers/s390/net/qeth_l3_main.c75
-rw-r--r--drivers/s390/net/qeth_l3_sys.c24
-rw-r--r--drivers/s390/s390_rdev.c2
-rw-r--r--drivers/s390/s390mach.c107
-rw-r--r--drivers/s390/s390mach.h10
-rw-r--r--drivers/s390/scsi/zfcp_aux.c63
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c33
-rw-r--r--drivers/s390/scsi/zfcp_dbf.h12
-rw-r--r--drivers/s390/scsi/zfcp_def.h34
-rw-r--r--drivers/s390/scsi/zfcp_erp.c37
-rw-r--r--drivers/s390/scsi/zfcp_ext.h8
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c105
-rw-r--r--drivers/s390/scsi/zfcp_fsf.h11
-rw-r--r--drivers/s390/scsi/zfcp_qdio.c27
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c65
-rw-r--r--drivers/sbus/sbus.c6
-rw-r--r--drivers/scsi/Kconfig2
-rw-r--r--drivers/scsi/Makefile1
-rw-r--r--drivers/scsi/aacraid/commctrl.c33
-rw-r--r--drivers/scsi/aacraid/linit.c2
-rw-r--r--drivers/scsi/aic94xx/aic94xx_sds.c12
-rw-r--r--drivers/scsi/aic94xx/aic94xx_sds.h4
-rw-r--r--drivers/scsi/aic94xx/aic94xx_seq.c7
-rw-r--r--drivers/scsi/device_handler/Kconfig32
-rw-r--r--drivers/scsi/device_handler/Makefile7
-rw-r--r--drivers/scsi/device_handler/scsi_dh.c162
-rw-r--r--drivers/scsi/device_handler/scsi_dh_emc.c499
-rw-r--r--drivers/scsi/device_handler/scsi_dh_hp_sw.c202
-rw-r--r--drivers/scsi/device_handler/scsi_dh_rdac.c691
-rw-r--r--drivers/scsi/dpt_i2o.c4
-rw-r--r--drivers/scsi/hosts.c7
-rw-r--r--drivers/scsi/ide-scsi.c302
-rw-r--r--drivers/scsi/iscsi_tcp.c515
-rw-r--r--drivers/scsi/iscsi_tcp.h7
-rw-r--r--drivers/scsi/libiscsi.c1353
-rw-r--r--drivers/scsi/mesh.c8
-rw-r--r--drivers/scsi/qla4xxx/ql4_os.c8
-rw-r--r--drivers/scsi/scsi.c9
-rw-r--r--drivers/scsi/scsi_debug.c98
-rw-r--r--drivers/scsi/scsi_error.c11
-rw-r--r--drivers/scsi/scsi_lib.c8
-rw-r--r--drivers/scsi/scsi_sysfs.c1
-rw-r--r--drivers/scsi/scsi_transport_fc.c9
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c388
-rw-r--r--drivers/scsi/sd.c5
-rw-r--r--drivers/scsi/sr.c3
-rw-r--r--drivers/serial/sh-sci.c17
-rw-r--r--drivers/serial/sh-sci.h38
-rw-r--r--drivers/spi/spi.c2
-rw-r--r--drivers/spi/spidev.c4
-rw-r--r--drivers/uio/Kconfig10
-rw-r--r--drivers/uio/Makefile1
-rw-r--r--drivers/uio/uio.c26
-rw-r--r--drivers/uio/uio_pdrv.c118
-rw-r--r--drivers/usb/Kconfig2
-rw-r--r--drivers/usb/Makefile3
-rw-r--r--drivers/usb/atm/cxacru.c2
-rw-r--r--drivers/usb/atm/speedtch.c1
-rw-r--r--drivers/usb/atm/ueagle-atm.c20
-rw-r--r--drivers/usb/core/devices.c2
-rw-r--r--drivers/usb/core/devio.c17
-rw-r--r--drivers/usb/core/driver.c37
-rw-r--r--drivers/usb/core/endpoint.c4
-rw-r--r--drivers/usb/core/file.c15
-rw-r--r--drivers/usb/core/hcd.c6
-rw-r--r--drivers/usb/core/hcd.h2
-rw-r--r--drivers/usb/core/hub.c506
-rw-r--r--drivers/usb/core/message.c8
-rw-r--r--drivers/usb/core/usb.c7
-rw-r--r--drivers/usb/gadget/Makefile6
-rw-r--r--drivers/usb/gadget/amd5536udc.c2
-rw-r--r--drivers/usb/gadget/at91_udc.c13
-rw-r--r--drivers/usb/gadget/composite.c1041
-rw-r--r--drivers/usb/gadget/config.c75
-rw-r--r--drivers/usb/gadget/dummy_hcd.c4
-rw-r--r--drivers/usb/gadget/epautoconf.c1
-rw-r--r--drivers/usb/gadget/ether.c2
-rw-r--r--drivers/usb/gadget/f_acm.c587
-rw-r--r--drivers/usb/gadget/f_loopback.c383
-rw-r--r--drivers/usb/gadget/f_serial.c298
-rw-r--r--drivers/usb/gadget/f_sourcesink.c589
-rw-r--r--drivers/usb/gadget/file_storage.c4
-rw-r--r--drivers/usb/gadget/fsl_usb2_udc.c2
-rw-r--r--drivers/usb/gadget/g_zero.h25
-rw-r--r--drivers/usb/gadget/goku_udc.c2
-rw-r--r--drivers/usb/gadget/inode.c23
-rw-r--r--drivers/usb/gadget/lh7a40x_udc.c2
-rw-r--r--drivers/usb/gadget/m66592-udc.c2
-rw-r--r--drivers/usb/gadget/net2280.c2
-rw-r--r--drivers/usb/gadget/omap_udc.c2
-rw-r--r--drivers/usb/gadget/printer.c11
-rw-r--r--drivers/usb/gadget/pxa2xx_udc.c2
-rw-r--r--drivers/usb/gadget/rndis.c55
-rw-r--r--drivers/usb/gadget/rndis.h2
-rw-r--r--drivers/usb/gadget/serial.c2313
-rw-r--r--drivers/usb/gadget/u_serial.c1270
-rw-r--r--drivers/usb/gadget/u_serial.h47
-rw-r--r--drivers/usb/gadget/zero.c1158
-rw-r--r--drivers/usb/host/Kconfig26
-rw-r--r--drivers/usb/host/Makefile3
-rw-r--r--drivers/usb/host/ehci-dbg.c4
-rw-r--r--drivers/usb/host/ehci-fsl.c12
-rw-r--r--drivers/usb/host/ehci-ixp4xx.c8
-rw-r--r--drivers/usb/host/ehci-orion.c8
-rw-r--r--drivers/usb/host/ehci-ps3.c2
-rw-r--r--drivers/usb/host/hwa-hc.c935
-rw-r--r--drivers/usb/host/isp116x-hcd.c2
-rw-r--r--drivers/usb/host/isp1760-hcd.c18
-rw-r--r--drivers/usb/host/isp1760-if.c6
-rw-r--r--drivers/usb/host/ohci-at91.c9
-rw-r--r--drivers/usb/host/ohci-dbg.c2
-rw-r--r--drivers/usb/host/ohci-hcd.c2
-rw-r--r--drivers/usb/host/ohci-omap.c2
-rw-r--r--drivers/usb/host/ohci-pnx4008.c2
-rw-r--r--drivers/usb/host/ohci-ppc-of.c2
-rw-r--r--drivers/usb/host/ohci-ps3.c2
-rw-r--r--drivers/usb/host/ohci-q.c3
-rw-r--r--drivers/usb/host/ohci-sm501.c2
-rw-r--r--drivers/usb/host/ohci-ssb.c2
-rw-r--r--drivers/usb/host/sl811-hcd.c2
-rw-r--r--drivers/usb/host/u132-hcd.c2
-rw-r--r--drivers/usb/host/whci/Kbuild11
-rw-r--r--drivers/usb/host/whci/asl.c366
-rw-r--r--drivers/usb/host/whci/hcd.c339
-rw-r--r--drivers/usb/host/whci/hw.c80
-rw-r--r--drivers/usb/host/whci/init.c181
-rw-r--r--drivers/usb/host/whci/int.c95
-rw-r--r--drivers/usb/host/whci/pzl.c398
-rw-r--r--drivers/usb/host/whci/qset.c563
-rw-r--r--drivers/usb/host/whci/whcd.h195
-rw-r--r--drivers/usb/host/whci/whci-hc.h413
-rw-r--r--drivers/usb/host/whci/wusb.c241
-rw-r--r--drivers/usb/misc/Kconfig11
-rw-r--r--drivers/usb/misc/Makefile1
-rw-r--r--drivers/usb/misc/auerswald.c10
-rw-r--r--drivers/usb/misc/emi62.c2
-rw-r--r--drivers/usb/misc/ftdi-elan.c24
-rw-r--r--drivers/usb/misc/gotemp.c301
-rw-r--r--drivers/usb/misc/iowarrior.c8
-rw-r--r--drivers/usb/misc/isight_firmware.c23
-rw-r--r--drivers/usb/misc/rio500.c8
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb.c13
-rw-r--r--drivers/usb/misc/usblcd.c6
-rw-r--r--drivers/usb/mon/mon_bin.c5
-rw-r--r--drivers/usb/serial/Kconfig8
-rw-r--r--drivers/usb/serial/cp2101.c13
-rw-r--r--drivers/usb/serial/digi_acceleport.c3
-rw-r--r--drivers/usb/serial/io_fw_down3.h11
-rw-r--r--drivers/usb/serial/io_ti.c1888
-rw-r--r--drivers/usb/serial/keyspan_pda.S2
-rw-r--r--drivers/usb/serial/kl5kusb105.c2
-rw-r--r--drivers/usb/serial/ti_fw_3410.h4
-rw-r--r--drivers/usb/serial/ti_fw_5052.h5
-rw-r--r--drivers/usb/serial/ti_usb_3410_5052.c106
-rw-r--r--drivers/usb/serial/usb-serial.c6
-rw-r--r--drivers/usb/serial/xircom_pgs.S2
-rw-r--r--drivers/usb/storage/datafab.c2
-rw-r--r--drivers/usb/storage/debug.c2
-rw-r--r--drivers/usb/storage/debug.h2
-rw-r--r--drivers/usb/storage/dpcm.c2
-rw-r--r--drivers/usb/storage/dpcm.h2
-rw-r--r--drivers/usb/storage/freecom.c2
-rw-r--r--drivers/usb/storage/freecom.h2
-rw-r--r--drivers/usb/storage/initializers.c2
-rw-r--r--drivers/usb/storage/initializers.h2
-rw-r--r--drivers/usb/storage/isd200.c8
-rw-r--r--drivers/usb/storage/jumpshot.c2
-rw-r--r--drivers/usb/storage/protocol.c2
-rw-r--r--drivers/usb/storage/protocol.h2
-rw-r--r--drivers/usb/storage/scsiglue.c28
-rw-r--r--drivers/usb/storage/scsiglue.h2
-rw-r--r--drivers/usb/storage/sddr09.c1
-rw-r--r--drivers/usb/storage/sddr09.h2
-rw-r--r--drivers/usb/storage/sddr55.c2
-rw-r--r--drivers/usb/storage/sddr55.h2
-rw-r--r--drivers/usb/storage/shuttle_usbat.c2
-rw-r--r--drivers/usb/storage/shuttle_usbat.h2
-rw-r--r--drivers/usb/storage/transport.c78
-rw-r--r--drivers/usb/storage/transport.h2
-rw-r--r--drivers/usb/storage/unusual_devs.h2
-rw-r--r--drivers/usb/storage/usb.c111
-rw-r--r--drivers/usb/storage/usb.h25
-rw-r--r--drivers/usb/wusbcore/Kconfig17
-rw-r--r--drivers/usb/wusbcore/Makefile20
-rw-r--r--drivers/usb/wusbcore/cbaf.c620
-rw-r--r--drivers/usb/wusbcore/crypto.c538
-rw-r--r--drivers/usb/wusbcore/dev-sysfs.c199
-rw-r--r--drivers/usb/wusbcore/devconnect.c1319
-rw-r--r--drivers/usb/wusbcore/mmc.c329
-rw-r--r--drivers/usb/wusbcore/pal.c39
-rw-r--r--drivers/usb/wusbcore/reservation.c115
-rw-r--r--drivers/usb/wusbcore/rh.c477
-rw-r--r--drivers/usb/wusbcore/security.c644
-rw-r--r--drivers/usb/wusbcore/wa-hc.c83
-rw-r--r--drivers/usb/wusbcore/wa-hc.h416
-rw-r--r--drivers/usb/wusbcore/wa-nep.c313
-rw-r--r--drivers/usb/wusbcore/wa-rpipe.c562
-rw-r--r--drivers/usb/wusbcore/wa-xfer.c1709
-rw-r--r--drivers/usb/wusbcore/wusbhc.c392
-rw-r--r--drivers/usb/wusbcore/wusbhc.h525
-rw-r--r--drivers/uwb/Kconfig89
-rw-r--r--drivers/uwb/Makefile29
-rw-r--r--drivers/uwb/address.c374
-rw-r--r--drivers/uwb/beacon.c707
-rw-r--r--drivers/uwb/driver.c142
-rw-r--r--drivers/uwb/drp-avail.c288
-rw-r--r--drivers/uwb/drp-ie.c233
-rw-r--r--drivers/uwb/drp.c451
-rw-r--r--drivers/uwb/est.c414
-rw-r--r--drivers/uwb/hwa-rc.c912
-rw-r--r--drivers/uwb/i1480/Makefile2
-rw-r--r--drivers/uwb/i1480/dfu/Makefile9
-rw-r--r--drivers/uwb/i1480/dfu/dfu.c281
-rw-r--r--drivers/uwb/i1480/dfu/i1480-dfu.h263
-rw-r--r--drivers/uwb/i1480/dfu/mac.c529
-rw-r--r--drivers/uwb/i1480/dfu/phy.c203
-rw-r--r--drivers/uwb/i1480/dfu/usb.c501
-rw-r--r--drivers/uwb/i1480/i1480-est.c99
-rw-r--r--drivers/uwb/i1480/i1480-wlp.h155
-rw-r--r--drivers/uwb/i1480/i1480u-wlp/Makefile8
-rw-r--r--drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h269
-rw-r--r--drivers/uwb/i1480/i1480u-wlp/lc.c421
-rw-r--r--drivers/uwb/i1480/i1480u-wlp/netdev.c367
-rw-r--r--drivers/uwb/i1480/i1480u-wlp/rx.c491
-rw-r--r--drivers/uwb/i1480/i1480u-wlp/sysfs.c446
-rw-r--r--drivers/uwb/i1480/i1480u-wlp/tx.c638
-rw-r--r--drivers/uwb/ie.c570
-rw-r--r--drivers/uwb/lc-dev.c517
-rw-r--r--drivers/uwb/lc-rc.c485
-rw-r--r--drivers/uwb/neh.c615
-rw-r--r--drivers/uwb/pal.c71
-rw-r--r--drivers/uwb/reset.c319
-rw-r--r--drivers/uwb/rsv.c677
-rw-r--r--drivers/uwb/scan.c159
-rw-r--r--drivers/uwb/umc-bus.c185
-rw-r--r--drivers/uwb/umc-dev.c103
-rw-r--r--drivers/uwb/umc-drv.c31
-rw-r--r--drivers/uwb/uwb-debug.c367
-rw-r--r--drivers/uwb/uwb-internal.h283
-rw-r--r--drivers/uwb/uwbd.c425
-rw-r--r--drivers/uwb/whc-rc.c511
-rw-r--r--drivers/uwb/whci.c274
-rw-r--r--drivers/uwb/wlp/Makefile10
-rw-r--r--drivers/uwb/wlp/driver.c43
-rw-r--r--drivers/uwb/wlp/eda.c449
-rw-r--r--drivers/uwb/wlp/messages.c1946
-rw-r--r--drivers/uwb/wlp/sysfs.c715
-rw-r--r--drivers/uwb/wlp/txrx.c374
-rw-r--r--drivers/uwb/wlp/wlp-internal.h228
-rw-r--r--drivers/uwb/wlp/wlp-lc.c572
-rw-r--r--drivers/uwb/wlp/wss-lc.c1056
-rw-r--r--drivers/video/aty/aty128fb.c2
-rw-r--r--drivers/video/aty/radeonfb.h2
-rw-r--r--drivers/video/console/fbcon.c3
-rw-r--r--drivers/video/fb_ddc.c1
-rw-r--r--drivers/video/fbmem.c5
-rw-r--r--drivers/video/fsl-diu-fb.c4
-rw-r--r--drivers/video/intelfb/intelfb_i2c.c12
-rw-r--r--drivers/video/matrox/i2c-matroxfb.c20
-rw-r--r--drivers/video/pxafb.c6
-rw-r--r--drivers/video/xen-fbfront.c211
-rw-r--r--drivers/xen/Makefile2
-rw-r--r--drivers/xen/balloon.c10
-rw-r--r--drivers/xen/events.c114
-rw-r--r--drivers/xen/grant-table.c4
-rw-r--r--drivers/xen/manage.c252
-rw-r--r--drivers/xen/xenbus/xenbus_comms.c23
-rw-r--r--fs/Kconfig8
-rw-r--r--fs/Kconfig.binfmt2
-rw-r--r--fs/binfmt_elf_fdpic.c45
-rw-r--r--fs/cifs/CHANGES5
-rw-r--r--fs/cifs/cifsfs.c21
-rw-r--r--fs/cifs/cifsglob.h3
-rw-r--r--fs/cifs/cifspdu.h23
-rw-r--r--fs/cifs/cifssmb.c6
-rw-r--r--fs/cifs/connect.c5
-rw-r--r--fs/cifs/dir.c4
-rw-r--r--fs/cifs/file.c7
-rw-r--r--fs/cifs/inode.c148
-rw-r--r--fs/cifs/misc.c3
-rw-r--r--fs/cifs/readdir.c77
-rw-r--r--fs/coda/psdev.c5
-rw-r--r--fs/dcache.c102
-rw-r--r--fs/dlm/lock.c3
-rw-r--r--fs/dlm/user.c2
-rw-r--r--fs/jfs/jfs_debug.c62
-rw-r--r--fs/jfs/jfs_debug.h10
-rw-r--r--fs/jfs/jfs_dtree.h3
-rw-r--r--fs/jfs/jfs_imap.c2
-rw-r--r--fs/jfs/jfs_logmgr.c35
-rw-r--r--fs/jfs/jfs_metapage.c36
-rw-r--r--fs/jfs/jfs_txnmgr.c68
-rw-r--r--fs/jfs/jfs_xtree.c36
-rw-r--r--fs/jfs/namei.c2
-rw-r--r--fs/jfs/super.c7
-rw-r--r--fs/nfsd/nfs4proc.c1
-rw-r--r--fs/nfsd/nfs4state.c21
-rw-r--r--fs/nfsd/nfs4xdr.c2
-rw-r--r--fs/nfsd/nfsctl.c103
-rw-r--r--fs/nfsd/nfssvc.c124
-rw-r--r--fs/ocfs2/cluster/netdebug.c8
-rw-r--r--fs/ocfs2/cluster/nodemanager.c74
-rw-r--r--fs/ocfs2/cluster/nodemanager.h4
-rw-r--r--fs/ocfs2/dlmglue.c122
-rw-r--r--fs/ocfs2/file.c2
-rw-r--r--fs/ocfs2/ocfs2.h12
-rw-r--r--fs/ocfs2/stack_o2cb.c41
-rw-r--r--fs/ocfs2/stack_user.c22
-rw-r--r--fs/ocfs2/stackglue.c119
-rw-r--r--fs/ocfs2/stackglue.h19
-rw-r--r--fs/ocfs2/super.c6
-rw-r--r--fs/partitions/check.c2
-rw-r--r--fs/proc/proc_misc.c16
-rw-r--r--fs/sysfs/file.c13
-rw-r--r--fs/sysfs/mount.c2
-rw-r--r--fs/sysfs/sysfs.h1
-rw-r--r--fs/xfs/linux-2.6/kmem.c6
-rw-r--r--fs/xfs/linux-2.6/kmem.h4
-rw-r--r--fs/xfs/linux-2.6/xfs_aops.c5
-rw-r--r--fs/xfs/linux-2.6/xfs_buf.c14
-rw-r--r--fs/xfs/linux-2.6/xfs_buf.h2
-rw-r--r--fs/xfs/linux-2.6/xfs_export.c2
-rw-r--r--fs/xfs/linux-2.6/xfs_iops.c74
-rw-r--r--fs/xfs/linux-2.6/xfs_iops.h1
-rw-r--r--fs/xfs/linux-2.6/xfs_linux.h1
-rw-r--r--fs/xfs/linux-2.6/xfs_super.c566
-rw-r--r--fs/xfs/linux-2.6/xfs_super.h3
-rw-r--r--fs/xfs/quota/xfs_dquot.c3
-rw-r--r--fs/xfs/quota/xfs_dquot.h2
-rw-r--r--fs/xfs/quota/xfs_dquot_item.c4
-rw-r--r--fs/xfs/quota/xfs_qm.c14
-rw-r--r--fs/xfs/quota/xfs_qm_syscalls.c8
-rw-r--r--fs/xfs/support/ktrace.c4
-rw-r--r--fs/xfs/xfs_attr.c158
-rw-r--r--fs/xfs/xfs_attr.h2
-rw-r--r--fs/xfs/xfs_attr_leaf.c38
-rw-r--r--fs/xfs/xfs_attr_sf.h10
-rw-r--r--fs/xfs/xfs_bmap.c2
-rw-r--r--fs/xfs/xfs_buf_item.c8
-rw-r--r--fs/xfs/xfs_clnt.h1
-rw-r--r--fs/xfs/xfs_da_btree.c46
-rw-r--r--fs/xfs/xfs_da_btree.h36
-rw-r--r--fs/xfs/xfs_dfrag.c4
-rw-r--r--fs/xfs/xfs_dir2.c125
-rw-r--r--fs/xfs/xfs_dir2.h6
-rw-r--r--fs/xfs/xfs_dir2_block.c56
-rw-r--r--fs/xfs/xfs_dir2_data.c5
-rw-r--r--fs/xfs/xfs_dir2_leaf.c80
-rw-r--r--fs/xfs/xfs_dir2_node.c387
-rw-r--r--fs/xfs/xfs_dir2_sf.c83
-rw-r--r--fs/xfs/xfs_dir2_trace.c20
-rw-r--r--fs/xfs/xfs_error.c5
-rw-r--r--fs/xfs/xfs_extfree_item.c6
-rw-r--r--fs/xfs/xfs_fs.h1
-rw-r--r--fs/xfs/xfs_fsops.c4
-rw-r--r--fs/xfs/xfs_inode.c95
-rw-r--r--fs/xfs/xfs_inode.h3
-rw-r--r--fs/xfs/xfs_inode_item.c7
-rw-r--r--fs/xfs/xfs_itable.c6
-rw-r--r--fs/xfs/xfs_log.c49
-rw-r--r--fs/xfs/xfs_log_priv.h6
-rw-r--r--fs/xfs/xfs_log_recover.c21
-rw-r--r--fs/xfs/xfs_mount.c92
-rw-r--r--fs/xfs/xfs_mount.h15
-rw-r--r--fs/xfs/xfs_mru_cache.c8
-rw-r--r--fs/xfs/xfs_rtalloc.c2
-rw-r--r--fs/xfs/xfs_sb.h17
-rw-r--r--fs/xfs/xfs_trans.c4
-rw-r--r--fs/xfs/xfs_trans_inode.c2
-rw-r--r--fs/xfs/xfs_trans_item.c8
-rw-r--r--fs/xfs/xfs_vfsops.c479
-rw-r--r--fs/xfs/xfs_vfsops.h5
-rw-r--r--fs/xfs/xfs_vnodeops.c34
-rw-r--r--fs/xfs/xfs_vnodeops.h2
-rw-r--r--include/acpi/acpi_bus.h4
-rw-r--r--include/acpi/processor.h3
-rw-r--r--include/asm-arm/arch-pxa/regs-lcd.h5
-rw-r--r--include/asm-arm/kprobes.h1
-rw-r--r--include/asm-avr32/arch-at32ap/board.h10
-rw-r--r--include/asm-avr32/pci.h2
-rw-r--r--include/asm-generic/percpu.h9
-rw-r--r--include/asm-generic/sections.h10
-rw-r--r--include/asm-generic/vmlinux.lds.h43
-rw-r--r--include/asm-ia64/Kbuild2
-rw-r--r--include/asm-ia64/gcc_intrin.h24
-rw-r--r--include/asm-ia64/hw_irq.h23
-rw-r--r--include/asm-ia64/intel_intrin.h41
-rw-r--r--include/asm-ia64/intrinsics.h55
-rw-r--r--include/asm-ia64/iosapic.h18
-rw-r--r--include/asm-ia64/irq.h9
-rw-r--r--include/asm-ia64/kvm_host.h3
-rw-r--r--include/asm-ia64/mmu_context.h6
-rw-r--r--include/asm-ia64/native/inst.h175
-rw-r--r--include/asm-ia64/native/irq.h35
-rw-r--r--include/asm-ia64/paravirt.h255
-rw-r--r--include/asm-ia64/paravirt_privop.h114
-rw-r--r--include/asm-ia64/smp.h2
-rw-r--r--include/asm-ia64/system.h11
-rw-r--r--include/asm-powerpc/ftrace.h6
-rw-r--r--include/asm-powerpc/hw_irq.h10
-rw-r--r--include/asm-powerpc/kvm_host.h2
-rw-r--r--include/asm-powerpc/kvm_ppc.h1
-rw-r--r--include/asm-s390/airq.h4
-rw-r--r--include/asm-s390/ccwdev.h12
-rw-r--r--include/asm-s390/cio.h114
-rw-r--r--include/asm-s390/elf.h51
-rw-r--r--include/asm-s390/etr.h45
-rw-r--r--include/asm-s390/fcx.h311
-rw-r--r--include/asm-s390/isc.h24
-rw-r--r--include/asm-s390/itcw.h30
-rw-r--r--include/asm-s390/processor.h24
-rw-r--r--include/asm-s390/ptrace.h15
-rw-r--r--include/asm-s390/system.h8
-rw-r--r--include/asm-s390/timer.h12
-rw-r--r--include/asm-sh/elf.h53
-rw-r--r--include/asm-sh/mmu.h4
-rw-r--r--include/asm-sh/page.h2
-rw-r--r--include/asm-sh/pgtable_32.h4
-rw-r--r--include/asm-sh/processor.h1
-rw-r--r--include/asm-sh/processor_32.h4
-rw-r--r--include/asm-sh/processor_64.h4
-rw-r--r--include/asm-sh/ptrace.h5
-rw-r--r--include/asm-sh/segment.h30
-rw-r--r--include/asm-sh/sh7763rdp.h54
-rw-r--r--include/asm-sh/thread_info.h2
-rw-r--r--include/asm-sh/uaccess.h222
-rw-r--r--include/asm-sh/uaccess_32.h341
-rw-r--r--include/asm-sh/uaccess_64.h206
-rw-r--r--include/asm-x86/acpi.h1
-rw-r--r--include/asm-x86/alternative.h2
-rw-r--r--include/asm-x86/apic.h4
-rw-r--r--include/asm-x86/atomic_64.h6
-rw-r--r--include/asm-x86/bios_ebda.h2
-rw-r--r--include/asm-x86/bitops.h34
-rw-r--r--include/asm-x86/bootparam.h4
-rw-r--r--include/asm-x86/cpufeature.h6
-rw-r--r--include/asm-x86/current.h42
-rw-r--r--include/asm-x86/current_32.h17
-rw-r--r--include/asm-x86/current_64.h27
-rw-r--r--include/asm-x86/desc.h22
-rw-r--r--include/asm-x86/e820.h80
-rw-r--r--include/asm-x86/e820_32.h24
-rw-r--r--include/asm-x86/e820_64.h27
-rw-r--r--include/asm-x86/efi.h2
-rw-r--r--include/asm-x86/fixmap_32.h8
-rw-r--r--include/asm-x86/gart.h62
-rw-r--r--include/asm-x86/genapic_64.h2
-rw-r--r--include/asm-x86/hardirq.h6
-rw-r--r--include/asm-x86/hw_irq.h105
-rw-r--r--include/asm-x86/hw_irq_32.h66
-rw-r--r--include/asm-x86/hw_irq_64.h173
-rw-r--r--include/asm-x86/i8259.h2
-rw-r--r--include/asm-x86/io.h56
-rw-r--r--include/asm-x86/io_32.h49
-rw-r--r--include/asm-x86/io_64.h71
-rw-r--r--include/asm-x86/io_apic.h38
-rw-r--r--include/asm-x86/ipi.h3
-rw-r--r--include/asm-x86/irq.h51
-rw-r--r--include/asm-x86/irq_32.h51
-rw-r--r--include/asm-x86/irq_64.h51
-rw-r--r--include/asm-x86/irq_vectors.h169
-rw-r--r--include/asm-x86/irqflags.h24
-rw-r--r--include/asm-x86/kvm.h1
-rw-r--r--include/asm-x86/kvm_host.h43
-rw-r--r--include/asm-x86/mach-default/irq_vectors.h96
-rw-r--r--include/asm-x86/mach-default/irq_vectors_limits.h16
-rw-r--r--include/asm-x86/mach-default/smpboot_hooks.h4
-rw-r--r--include/asm-x86/mach-generic/mach_mpparse.h7
-rw-r--r--include/asm-x86/mach-numaq/mach_apic.h39
-rw-r--r--include/asm-x86/mach-numaq/mach_mpparse.h11
-rw-r--r--include/asm-x86/mach-visws/irq_vectors.h62
-rw-r--r--include/asm-x86/mach-voyager/irq_vectors.h79
-rw-r--r--include/asm-x86/mmconfig.h12
-rw-r--r--include/asm-x86/mmzone_32.h26
-rw-r--r--include/asm-x86/mpspec.h30
-rw-r--r--include/asm-x86/msr-index.h4
-rw-r--r--include/asm-x86/nmi.h12
-rw-r--r--include/asm-x86/numa_64.h19
-rw-r--r--include/asm-x86/numaq.h6
-rw-r--r--include/asm-x86/page.h11
-rw-r--r--include/asm-x86/page_32.h11
-rw-r--r--include/asm-x86/paravirt.h15
-rw-r--r--include/asm-x86/pci-direct.h4
-rw-r--r--include/asm-x86/pci.h2
-rw-r--r--include/asm-x86/pda.h7
-rw-r--r--include/asm-x86/percpu.h129
-rw-r--r--include/asm-x86/pgtable.h72
-rw-r--r--include/asm-x86/processor-flags.h6
-rw-r--r--include/asm-x86/proto.h2
-rw-r--r--include/asm-x86/ptrace.h8
-rw-r--r--include/asm-x86/reboot.h2
-rw-r--r--include/asm-x86/required-features.h6
-rw-r--r--include/asm-x86/resume-trace.h2
-rw-r--r--include/asm-x86/seccomp_32.h1
-rw-r--r--include/asm-x86/setup.h11
-rw-r--r--include/asm-x86/smp.h16
-rw-r--r--include/asm-x86/srat.h12
-rw-r--r--include/asm-x86/stackprotector.h38
-rw-r--r--include/asm-x86/string_32.h323
-rw-r--r--include/asm-x86/system.h9
-rw-r--r--include/asm-x86/thread_info.h248
-rw-r--r--include/asm-x86/thread_info_32.h205
-rw-r--r--include/asm-x86/thread_info_64.h195
-rw-r--r--include/asm-x86/topology.h152
-rw-r--r--include/asm-x86/uv/uv_hub.h188
-rw-r--r--include/asm-x86/uv/uv_mmrs.h509
-rw-r--r--include/asm-x86/vm86.h11
-rw-r--r--include/asm-x86/vsyscall.h3
-rw-r--r--include/asm-x86/xen/hypercall.h11
-rw-r--r--include/asm-x86/xen/page.h29
-rw-r--r--include/linux/acpi.h3
-rw-r--r--include/linux/bitmap.h1
-rw-r--r--include/linux/blkdev.h1
-rw-r--r--include/linux/bootmem.h2
-rw-r--r--include/linux/byteorder/bitfields.h114
-rw-r--r--include/linux/console.h2
-rw-r--r--include/linux/cpuidle.h1
-rw-r--r--include/linux/cpumask.h98
-rw-r--r--include/linux/dcache.h2
-rw-r--r--include/linux/device.h60
-rw-r--r--include/linux/efi.h4
-rw-r--r--include/linux/eisa.h2
-rw-r--r--include/linux/firmware.h2
-rw-r--r--include/linux/ftrace.h140
-rw-r--r--include/linux/hpet.h3
-rw-r--r--include/linux/i2c-id.h1
-rw-r--r--include/linux/i2c.h8
-rw-r--r--include/linux/ide.h118
-rw-r--r--include/linux/init.h1
-rw-r--r--include/linux/interrupt.h8
-rw-r--r--include/linux/ioport.h6
-rw-r--r--include/linux/ipv6.h4
-rw-r--r--include/linux/irq.h9
-rw-r--r--include/linux/irqflags.h13
-rw-r--r--include/linux/kernel.h6
-rw-r--r--include/linux/kernel_stat.h2
-rw-r--r--include/linux/kobject.h3
-rw-r--r--include/linux/kvm.h33
-rw-r--r--include/linux/kvm_host.h13
-rw-r--r--include/linux/linkage.h2
-rw-r--r--include/linux/list.h367
-rw-r--r--include/linux/magic.h1
-rw-r--r--include/linux/marker.h40
-rw-r--r--include/linux/math64.h21
-rw-r--r--include/linux/mm.h4
-rw-r--r--include/linux/mmiotrace.h85
-rw-r--r--include/linux/mod_devicetable.h9
-rw-r--r--include/linux/nfs4.h3
-rw-r--r--include/linux/nfsd/nfsd.h1
-rw-r--r--include/linux/nfsd/state.h2
-rw-r--r--include/linux/page-flags.h1
-rw-r--r--include/linux/pageblock-flags.h8
-rw-r--r--include/linux/pci.h41
-rw-r--r--include/linux/pci_hotplug.h14
-rw-r--r--include/linux/percpu.h24
-rw-r--r--include/linux/platform_device.h1
-rw-r--r--include/linux/pm.h314
-rw-r--r--include/linux/poison.h10
-rw-r--r--include/linux/preempt.h34
-rw-r--r--include/linux/rcuclassic.h3
-rw-r--r--include/linux/rculist.h373
-rw-r--r--include/linux/rcupdate.h26
-rw-r--r--include/linux/rcupreempt.h42
-rw-r--r--include/linux/resume-trace.h2
-rw-r--r--include/linux/rtnetlink.h1
-rw-r--r--include/linux/sched.h87
-rw-r--r--include/linux/spi/spi.h2
-rw-r--r--include/linux/ssb/ssb_driver_gige.h2
-rw-r--r--include/linux/stackprotector.h16
-rw-r--r--include/linux/sunrpc/gss_krb5.h28
-rw-r--r--include/linux/sunrpc/svc.h7
-rw-r--r--include/linux/sunrpc/svc_rdma.h36
-rw-r--r--include/linux/suspend.h14
-rw-r--r--include/linux/sysfs.h9
-rw-r--r--include/linux/time.h16
-rw-r--r--include/linux/topology.h13
-rw-r--r--include/linux/uio_driver.h2
-rw-r--r--include/linux/usb.h8
-rw-r--r--include/linux/usb/composite.h338
-rw-r--r--include/linux/usb/gadget.h19
-rw-r--r--include/linux/usb/wusb-wa.h281
-rw-r--r--include/linux/usb/wusb.h419
-rw-r--r--include/linux/usbdevice_fs.h2
-rw-r--r--include/linux/uwb.h759
-rw-r--r--include/linux/uwb/debug-cmd.h57
-rw-r--r--include/linux/uwb/debug.h82
-rw-r--r--include/linux/uwb/spec.h625
-rw-r--r--include/linux/uwb/umc.h194
-rw-r--r--include/linux/uwb/whci.h117
-rw-r--r--include/linux/videodev2.h10
-rw-r--r--include/linux/virtio_net.h2
-rw-r--r--include/linux/wlp.h728
-rw-r--r--include/linux/writeback.h2
-rw-r--r--include/media/cx25840.h6
-rw-r--r--include/media/v4l2-dev.h73
-rw-r--r--include/net/inet_sock.h10
-rw-r--r--include/net/tcp.h1
-rw-r--r--include/rdma/ib_addr.h42
-rw-r--r--include/rdma/ib_cache.h2
-rw-r--r--include/rdma/ib_cm.h2
-rw-r--r--include/rdma/ib_fmr_pool.h4
-rw-r--r--include/rdma/ib_mad.h17
-rw-r--r--include/rdma/ib_pack.h2
-rw-r--r--include/rdma/ib_sa.h2
-rw-r--r--include/rdma/ib_smi.h4
-rw-r--r--include/rdma/ib_user_cm.h2
-rw-r--r--include/rdma/ib_user_mad.h2
-rw-r--r--include/rdma/ib_user_verbs.h7
-rw-r--r--include/rdma/ib_verbs.h90
-rw-r--r--include/rdma/iw_cm.h2
-rw-r--r--include/rdma/rdma_cm.h52
-rw-r--r--include/rdma/rdma_cm_ib.h50
-rw-r--r--include/scsi/iscsi_if.h93
-rw-r--r--include/scsi/iscsi_proto.h3
-rw-r--r--include/scsi/libiscsi.h107
-rw-r--r--include/scsi/scsi.h1
-rw-r--r--include/scsi/scsi_device.h23
-rw-r--r--include/scsi/scsi_dh.h59
-rw-r--r--include/scsi/scsi_host.h2
-rw-r--r--include/scsi/scsi_transport_fc.h4
-rw-r--r--include/scsi/scsi_transport_iscsi.h93
-rw-r--r--include/xen/events.h4
-rw-r--r--include/xen/grant_table.h3
-rw-r--r--include/xen/hvc-console.h9
-rw-r--r--include/xen/interface/elfnote.h20
-rw-r--r--include/xen/interface/io/fbif.h29
-rw-r--r--include/xen/interface/io/kbdif.h2
-rw-r--r--include/xen/interface/memory.h12
-rw-r--r--include/xen/xen-ops.h6
-rw-r--r--init/main.c17
-rw-r--r--ipc/shm.c2
-rw-r--r--kernel/Makefile23
-rw-r--r--kernel/cpu.c27
-rw-r--r--kernel/cpuset.c24
-rw-r--r--kernel/exit.c5
-rw-r--r--kernel/fork.c7
-rw-r--r--kernel/hrtimer.c9
-rw-r--r--kernel/irq/manage.c28
-rw-r--r--kernel/irq/proc.c59
-rw-r--r--kernel/kthread.c1
-rw-r--r--kernel/lockdep.c37
-rw-r--r--kernel/marker.c30
-rw-r--r--kernel/module.c7
-rw-r--r--kernel/mutex-debug.c2
-rw-r--r--kernel/mutex.c5
-rw-r--r--kernel/panic.c77
-rw-r--r--kernel/pid.c1
-rw-r--r--kernel/posix-cpu-timers.c3
-rw-r--r--kernel/power/disk.c50
-rw-r--r--kernel/power/main.c16
-rw-r--r--kernel/printk.c110
-rw-r--r--kernel/rcuclassic.c34
-rw-r--r--kernel/rcupdate.c71
-rw-r--r--kernel/rcupreempt.c431
-rw-r--r--kernel/rcupreempt_trace.c1
-rw-r--r--kernel/sched.c412
-rw-r--r--kernel/sched_cpupri.c174
-rw-r--r--kernel/sched_cpupri.h36
-rw-r--r--kernel/sched_fair.c21
-rw-r--r--kernel/sched_features.h2
-rw-r--r--kernel/sched_rt.c311
-rw-r--r--kernel/sched_trace.h41
-rw-r--r--kernel/semaphore.c2
-rw-r--r--kernel/softirq.c7
-rw-r--r--kernel/softlockup.c48
-rw-r--r--kernel/spinlock.c2
-rw-r--r--kernel/stacktrace.c3
-rw-r--r--kernel/sysctl.c31
-rw-r--r--kernel/taskstats.c4
-rw-r--r--kernel/time/clocksource.c4
-rw-r--r--kernel/time/tick-broadcast.c9
-rw-r--r--kernel/time/tick-sched.c11
-rw-r--r--kernel/timer.c2
-rw-r--r--kernel/trace/Kconfig135
-rw-r--r--kernel/trace/Makefile24
-rw-r--r--kernel/trace/ftrace.c1599
-rw-r--r--kernel/trace/trace.c3143
-rw-r--r--kernel/trace/trace.h333
-rw-r--r--kernel/trace/trace_functions.c78
-rw-r--r--kernel/trace/trace_irqsoff.c486
-rw-r--r--kernel/trace/trace_mmiotrace.c295
-rw-r--r--kernel/trace/trace_sched_switch.c286
-rw-r--r--kernel/trace/trace_sched_wakeup.c447
-rw-r--r--kernel/trace/trace_selftest.c563
-rw-r--r--kernel/trace/trace_selftest_dynamic.c7
-rw-r--r--kernel/trace/trace_sysprof.c363
-rw-r--r--kernel/workqueue.c6
-rw-r--r--lib/Kconfig.debug104
-rw-r--r--lib/Makefile10
-rw-r--r--lib/bitmap.c22
-rw-r--r--lib/cpumask.c9
-rw-r--r--lib/div64.c10
-rw-r--r--lib/kobject.c81
-rw-r--r--lib/smp_processor_id.c6
-rw-r--r--lib/textsearch.c1
-rw-r--r--mm/allocpercpu.c4
-rw-r--r--mm/bootmem.c6
-rw-r--r--mm/filemap.c15
-rw-r--r--mm/nommu.c21
-rw-r--r--mm/page-writeback.c10
-rw-r--r--mm/page_alloc.c49
-rw-r--r--mm/vmstat.c2
-rw-r--r--net/802/psnap.c1
-rw-r--r--net/8021q/vlan.c1
-rw-r--r--net/bluetooth/hci_sysfs.c7
-rw-r--r--net/bridge/br_fdb.c1
-rw-r--r--net/bridge/br_stp.c1
-rw-r--r--net/core/dev.c8
-rw-r--r--net/dccp/ackvec.c29
-rw-r--r--net/dccp/ccids/ccid3.c14
-rw-r--r--net/dccp/ccids/lib/tfrc.c8
-rw-r--r--net/dccp/ccids/lib/tfrc.h25
-rw-r--r--net/dccp/ccids/lib/tfrc_equation.c8
-rw-r--r--net/dccp/ipv4.c3
-rw-r--r--net/dccp/ipv6.c1
-rw-r--r--net/dccp/minisocks.c8
-rw-r--r--net/dccp/options.c4
-rw-r--r--net/dccp/output.c2
-rw-r--r--net/dccp/probe.c2
-rw-r--r--net/ipv4/fib_semantics.c5
-rw-r--r--net/ipv4/syncookies.c3
-rw-r--r--net/ipv4/tcp_ipv4.c2
-rw-r--r--net/ipv6/af_inet6.c2
-rw-r--r--net/ipv6/datagram.c5
-rw-r--r--net/ipv6/ipv6_sockglue.c12
-rw-r--r--net/ipv6/route.c8
-rw-r--r--net/ipv6/syncookies.c1
-rw-r--r--net/ipv6/tcp_ipv6.c1
-rw-r--r--net/iucv/iucv.c2
-rw-r--r--net/key/af_key.c3
-rw-r--r--net/mac80211/ieee80211_i.h2
-rw-r--r--net/mac80211/main.c1
-rw-r--r--net/mac80211/mlme.c18
-rw-r--r--net/mac80211/wext.c15
-rw-r--r--net/netfilter/nf_conntrack_core.c3
-rw-r--r--net/netfilter/nf_conntrack_helper.c1
-rw-r--r--net/netfilter/nf_conntrack_netlink.c1
-rw-r--r--net/netfilter/nf_log.c4
-rw-r--r--net/netlabel/netlabel_domainhash.c3
-rw-r--r--net/sunrpc/auth_gss/Makefile4
-rw-r--r--net/sunrpc/auth_gss/auth_gss.c14
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_crypto.c64
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_seal.c26
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_unseal.c16
-rw-r--r--net/sunrpc/auth_gss/gss_krb5_wrap.c79
-rw-r--r--net/sunrpc/auth_gss/gss_mech_switch.c14
-rw-r--r--net/sunrpc/auth_gss/svcauth_gss.c15
-rw-r--r--net/sunrpc/svc.c110
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma.c40
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_recvfrom.c84
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_sendto.c125
-rw-r--r--net/sunrpc/xprtrdma/svc_rdma_transport.c195
-rw-r--r--samples/firmware_class/firmware_sample_firmware_class.c2
-rw-r--r--scripts/Makefile.lib3
-rw-r--r--scripts/mod/file2alias.c36
-rw-r--r--scripts/mod/modpost.c25
-rw-r--r--sound/core/init.c5
-rw-r--r--sound/drivers/vx/vx_core.c4
-rw-r--r--sound/oss/soundcard.c14
-rw-r--r--sound/pci/maestro3.c29
-rw-r--r--sound/pci/pcxhr/pcxhr_core.c4
-rw-r--r--sound/pci/riptide/riptide.c10
-rw-r--r--sound/pci/vx222/vx222_ops.c2
-rw-r--r--sound/pci/ymfpci/ymfpci_main.c59
-rw-r--r--sound/sound_core.c5
-rw-r--r--tests/Kconfig80
-rw-r--r--tests/Makefile10
-rw-r--r--tests/backtracetest.c (renamed from kernel/backtracetest.c)0
-rw-r--r--tests/lkdtm.c (renamed from drivers/misc/lkdtm.c)12
-rw-r--r--tests/locking-selftest-hardirq.h (renamed from lib/locking-selftest-hardirq.h)0
-rw-r--r--tests/locking-selftest-mutex.h (renamed from lib/locking-selftest-mutex.h)0
-rw-r--r--tests/locking-selftest-rlock-hardirq.h (renamed from lib/locking-selftest-rlock-hardirq.h)0
-rw-r--r--tests/locking-selftest-rlock-softirq.h (renamed from lib/locking-selftest-rlock-softirq.h)0
-rw-r--r--tests/locking-selftest-rlock.h (renamed from lib/locking-selftest-rlock.h)0
-rw-r--r--tests/locking-selftest-rsem.h (renamed from lib/locking-selftest-rsem.h)0
-rw-r--r--tests/locking-selftest-softirq.h (renamed from lib/locking-selftest-softirq.h)0
-rw-r--r--tests/locking-selftest-spin-hardirq.h (renamed from lib/locking-selftest-spin-hardirq.h)0
-rw-r--r--tests/locking-selftest-spin-softirq.h (renamed from lib/locking-selftest-spin-softirq.h)0
-rw-r--r--tests/locking-selftest-spin.h (renamed from lib/locking-selftest-spin.h)0
-rw-r--r--tests/locking-selftest-wlock-hardirq.h (renamed from lib/locking-selftest-wlock-hardirq.h)0
-rw-r--r--tests/locking-selftest-wlock-softirq.h (renamed from lib/locking-selftest-wlock-softirq.h)0
-rw-r--r--tests/locking-selftest-wlock.h (renamed from lib/locking-selftest-wlock.h)0
-rw-r--r--tests/locking-selftest-wsem.h (renamed from lib/locking-selftest-wsem.h)0
-rw-r--r--tests/locking-selftest.c (renamed from lib/locking-selftest.c)0
-rw-r--r--tests/rcutorture.c (renamed from kernel/rcutorture.c)34
-rw-r--r--tests/rtmutex-tester.c (renamed from kernel/rtmutex-tester.c)2
-rw-r--r--tests/test_kprobes.c (renamed from kernel/test_kprobes.c)0
-rw-r--r--virt/kvm/coalesced_mmio.c156
-rw-r--r--virt/kvm/coalesced_mmio.h23
-rw-r--r--virt/kvm/ioapic.c44
-rw-r--r--virt/kvm/iodev.h8
-rw-r--r--virt/kvm/kvm_main.c130
-rw-r--r--virt/kvm/kvm_trace.c18
1766 files changed, 101881 insertions, 33163 deletions
diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX
index 1977fab38656..acba8305fafd 100644
--- a/Documentation/00-INDEX
+++ b/Documentation/00-INDEX
@@ -179,8 +179,6 @@ ia64/
- directory with info about Linux on Intel 64 bit architecture.
infiniband/
- directory with documents concerning Linux InfiniBand support.
-initrd.txt
- - how to use the RAM disk as an initial/temporary root filesystem.
input/
- info on Linux input device support.
io_ordering.txt
diff --git a/Documentation/ABI/testing/sysfs-bus-css b/Documentation/ABI/testing/sysfs-bus-css
new file mode 100644
index 000000000000..b585ec258a08
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-css
@@ -0,0 +1,35 @@
+What: /sys/bus/css/devices/.../type
+Date: March 2008
+Contact: Cornelia Huck <cornelia.huck@de.ibm.com>
+ linux-s390@vger.kernel.org
+Description: Contains the subchannel type, as reported by the hardware.
+ This attribute is present for all subchannel types.
+
+What: /sys/bus/css/devices/.../modalias
+Date: March 2008
+Contact: Cornelia Huck <cornelia.huck@de.ibm.com>
+ linux-s390@vger.kernel.org
+Description: Contains the module alias as reported with uevents.
+ It is of the format css:t<type> and present for all
+ subchannel types.
+
+What: /sys/bus/css/drivers/io_subchannel/.../chpids
+Date: December 2002
+Contact: Cornelia Huck <cornelia.huck@de.ibm.com>
+ linux-s390@vger.kernel.org
+Description: Contains the ids of the channel paths used by this
+ subchannel, as reported by the channel subsystem
+ during subchannel recognition.
+ Note: This is an I/O-subchannel specific attribute.
+Users: s390-tools, HAL
+
+What: /sys/bus/css/drivers/io_subchannel/.../pimpampom
+Date: December 2002
+Contact: Cornelia Huck <cornelia.huck@de.ibm.com>
+ linux-s390@vger.kernel.org
+Description: Contains the PIM/PAM/POM values, as reported by the
+ channel subsystem when last queried by the common I/O
+ layer (this implies that this attribute is not neccessarily
+ in sync with the values current in the channel subsystem).
+ Note: This is an I/O-subchannel specific attribute.
+Users: s390-tools, HAL
diff --git a/Documentation/ABI/testing/sysfs-dev b/Documentation/ABI/testing/sysfs-dev
new file mode 100644
index 000000000000..a9f2b8b0530f
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-dev
@@ -0,0 +1,20 @@
+What: /sys/dev
+Date: April 2008
+KernelVersion: 2.6.26
+Contact: Dan Williams <dan.j.williams@intel.com>
+Description: The /sys/dev tree provides a method to look up the sysfs
+ path for a device using the information returned from
+ stat(2). There are two directories, 'block' and 'char',
+ beneath /sys/dev containing symbolic links with names of
+ the form "<major>:<minor>". These links point to the
+ corresponding sysfs path for the given device.
+
+ Example:
+ $ readlink /sys/dev/block/8:32
+ ../../block/sdc
+
+ Entries in /sys/dev/char and /sys/dev/block will be
+ dynamically created and destroyed as devices enter and
+ leave the system.
+
+Users: mdadm <linux-raid@vger.kernel.org>
diff --git a/Documentation/DocBook/gadget.tmpl b/Documentation/DocBook/gadget.tmpl
index 5a8ffa761e09..ea3bc9565e6a 100644
--- a/Documentation/DocBook/gadget.tmpl
+++ b/Documentation/DocBook/gadget.tmpl
@@ -524,6 +524,44 @@ These utilities include endpoint autoconfiguration.
<!-- !Edrivers/usb/gadget/epautoconf.c -->
</sect1>
+<sect1 id="composite"><title>Composite Device Framework</title>
+
+<para>The core API is sufficient for writing drivers for composite
+USB devices (with more than one function in a given configuration),
+and also multi-configuration devices (also more than one function,
+but not necessarily sharing a given configuration).
+There is however an optional framework which makes it easier to
+reuse and combine functions.
+</para>
+
+<para>Devices using this framework provide a <emphasis>struct
+usb_composite_driver</emphasis>, which in turn provides one or
+more <emphasis>struct usb_configuration</emphasis> instances.
+Each such configuration includes at least one
+<emphasis>struct usb_function</emphasis>, which packages a user
+visible role such as "network link" or "mass storage device".
+Management functions may also exist, such as "Device Firmware
+Upgrade".
+</para>
+
+!Iinclude/linux/usb/composite.h
+!Edrivers/usb/gadget/composite.c
+
+</sect1>
+
+<sect1 id="functions"><title>Composite Device Functions</title>
+
+<para>At this writing, a few of the current gadget drivers have
+been converted to this framework.
+Near-term plans include converting all of them, except for "gadgetfs".
+</para>
+
+!Edrivers/usb/gadget/f_acm.c
+!Edrivers/usb/gadget/f_serial.c
+
+</sect1>
+
+
</chapter>
<chapter id="controllers"><title>Peripheral Controller Drivers</title>
diff --git a/Documentation/DocBook/uio-howto.tmpl b/Documentation/DocBook/uio-howto.tmpl
index fdd7f4f887b7..c4d187313963 100644
--- a/Documentation/DocBook/uio-howto.tmpl
+++ b/Documentation/DocBook/uio-howto.tmpl
@@ -30,6 +30,12 @@
<revhistory>
<revision>
+ <revnumber>0.5</revnumber>
+ <date>2008-05-22</date>
+ <authorinitials>hjk</authorinitials>
+ <revremark>Added description of write() function.</revremark>
+ </revision>
+ <revision>
<revnumber>0.4</revnumber>
<date>2007-11-26</date>
<authorinitials>hjk</authorinitials>
@@ -64,7 +70,7 @@
<?dbhtml filename="copyright.html"?>
<title>Copyright and License</title>
<para>
- Copyright (c) 2006 by Hans-Jürgen Koch.</para>
+ Copyright (c) 2006-2008 by Hans-Jürgen Koch.</para>
<para>
This documentation is Free Software licensed under the terms of the
GPL version 2.
@@ -189,6 +195,30 @@ interested in translating it, please email me
represents the total interrupt count. You can use this number
to figure out if you missed some interrupts.
</para>
+ <para>
+ For some hardware that has more than one interrupt source internally,
+ but not separate IRQ mask and status registers, there might be
+ situations where userspace cannot determine what the interrupt source
+ was if the kernel handler disables them by writing to the chip's IRQ
+ register. In such a case, the kernel has to disable the IRQ completely
+ to leave the chip's register untouched. Now the userspace part can
+ determine the cause of the interrupt, but it cannot re-enable
+ interrupts. Another cornercase is chips where re-enabling interrupts
+ is a read-modify-write operation to a combined IRQ status/acknowledge
+ register. This would be racy if a new interrupt occurred
+ simultaneously.
+ </para>
+ <para>
+ To address these problems, UIO also implements a write() function. It
+ is normally not used and can be ignored for hardware that has only a
+ single interrupt source or has separate IRQ mask and status registers.
+ If you need it, however, a write to <filename>/dev/uioX</filename>
+ will call the <function>irqcontrol()</function> function implemented
+ by the driver. You have to write a 32-bit value that is usually either
+ 0 or 1 to disable or enable interrupts. If a driver does not implement
+ <function>irqcontrol()</function>, <function>write()</function> will
+ return with <varname>-ENOSYS</varname>.
+ </para>
<para>
To handle interrupts properly, your custom kernel module can
@@ -362,6 +392,14 @@ device is actually used.
<function>open()</function>, you will probably also want a custom
<function>release()</function> function.
</para></listitem>
+
+<listitem><para>
+<varname>int (*irqcontrol)(struct uio_info *info, s32 irq_on)
+</varname>: Optional. If you need to be able to enable or disable
+interrupts from userspace by writing to <filename>/dev/uioX</filename>,
+you can implement this function. The parameter <varname>irq_on</varname>
+will be 0 to disable interrupts and 1 to enable them.
+</para></listitem>
</itemizedlist>
<para>
diff --git a/Documentation/IRQ-affinity.txt b/Documentation/IRQ-affinity.txt
index 938d7dd05490..b4a615b78403 100644
--- a/Documentation/IRQ-affinity.txt
+++ b/Documentation/IRQ-affinity.txt
@@ -1,17 +1,26 @@
+ChangeLog:
+ Started by Ingo Molnar <mingo@redhat.com>
+ Update by Max Krasnyansky <maxk@qualcomm.com>
-SMP IRQ affinity, started by Ingo Molnar <mingo@redhat.com>
-
+SMP IRQ affinity
/proc/irq/IRQ#/smp_affinity specifies which target CPUs are permitted
for a given IRQ source. It's a bitmask of allowed CPUs. It's not allowed
to turn off all CPUs, and if an IRQ controller does not support IRQ
affinity then the value will not change from the default 0xffffffff.
+/proc/irq/default_smp_affinity specifies default affinity mask that applies
+to all non-active IRQs. Once IRQ is allocated/activated its affinity bitmask
+will be set to the default mask. It can then be changed as described above.
+Default mask is 0xffffffff.
+
Here is an example of restricting IRQ44 (eth1) to CPU0-3 then restricting
-the IRQ to CPU4-7 (this is an 8-CPU SMP box):
+it to CPU4-7 (this is an 8-CPU SMP box):
+[root@moon 44]# cd /proc/irq/44
[root@moon 44]# cat smp_affinity
ffffffff
+
[root@moon 44]# echo 0f > smp_affinity
[root@moon 44]# cat smp_affinity
0000000f
@@ -21,17 +30,27 @@ PING hell (195.4.7.3): 56 data bytes
--- hell ping statistics ---
6029 packets transmitted, 6027 packets received, 0% packet loss
round-trip min/avg/max = 0.1/0.1/0.4 ms
-[root@moon 44]# cat /proc/interrupts | grep 44:
- 44: 0 1785 1785 1783 1783 1
-1 0 IO-APIC-level eth1
+[root@moon 44]# cat /proc/interrupts | grep 'CPU\|44:'
+ CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7
+ 44: 1068 1785 1785 1783 0 0 0 0 IO-APIC-level eth1
+
+As can be seen from the line above IRQ44 was delivered only to the first four
+processors (0-3).
+Now lets restrict that IRQ to CPU(4-7).
+
[root@moon 44]# echo f0 > smp_affinity
+[root@moon 44]# cat smp_affinity
+000000f0
[root@moon 44]# ping -f h
PING hell (195.4.7.3): 56 data bytes
..
--- hell ping statistics ---
2779 packets transmitted, 2777 packets received, 0% packet loss
round-trip min/avg/max = 0.1/0.5/585.4 ms
-[root@moon 44]# cat /proc/interrupts | grep 44:
- 44: 1068 1785 1785 1784 1784 1069 1070 1069 IO-APIC-level eth1
-[root@moon 44]#
+[root@moon 44]# cat /proc/interrupts | 'CPU\|44:'
+ CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7
+ 44: 1068 1785 1785 1783 1784 1069 1070 1069 IO-APIC-level eth1
+
+This time around IRQ44 was delivered only to the last four processors.
+i.e counters for the CPU0-3 did not change.
diff --git a/Documentation/RCU/NMI-RCU.txt b/Documentation/RCU/NMI-RCU.txt
index c64158ecde43..a6d32e65d222 100644
--- a/Documentation/RCU/NMI-RCU.txt
+++ b/Documentation/RCU/NMI-RCU.txt
@@ -93,6 +93,9 @@ Since NMI handlers disable preemption, synchronize_sched() is guaranteed
not to return until all ongoing NMI handlers exit. It is therefore safe
to free up the handler's data as soon as synchronize_sched() returns.
+Important note: for this to work, the architecture in question must
+invoke irq_enter() and irq_exit() on NMI entry and exit, respectively.
+
Answer to Quick Quiz
diff --git a/Documentation/RCU/RTFP.txt b/Documentation/RCU/RTFP.txt
index 39ad8f56783a..9f711d2df91b 100644
--- a/Documentation/RCU/RTFP.txt
+++ b/Documentation/RCU/RTFP.txt
@@ -52,6 +52,10 @@ of each iteration. Unfortunately, chaotic relaxation requires highly
structured data, such as the matrices used in scientific programs, and
is thus inapplicable to most data structures in operating-system kernels.
+In 1992, Henry (now Alexia) Massalin completed a dissertation advising
+parallel programmers to defer processing when feasible to simplify
+synchronization. RCU makes extremely heavy use of this advice.
+
In 1993, Jacobson [Jacobson93] verbally described what is perhaps the
simplest deferred-free technique: simply waiting a fixed amount of time
before freeing blocks awaiting deferred free. Jacobson did not describe
@@ -138,6 +142,13 @@ blocking in read-side critical sections appeared [PaulEMcKenney2006c],
Robert Olsson described an RCU-protected trie-hash combination
[RobertOlsson2006a].
+2007 saw the journal version of the award-winning RCU paper from 2006
+[ThomasEHart2007a], as well as a paper demonstrating use of Promela
+and Spin to mechanically verify an optimization to Oleg Nesterov's
+QRCU [PaulEMcKenney2007QRCUspin], a design document describing
+preemptible RCU [PaulEMcKenney2007PreemptibleRCU], and the three-part
+LWN "What is RCU?" series [PaulEMcKenney2007WhatIsRCUFundamentally,
+PaulEMcKenney2008WhatIsRCUUsage, and PaulEMcKenney2008WhatIsRCUAPI].
Bibtex Entries
@@ -202,6 +213,20 @@ Bibtex Entries
,Year="1991"
}
+@phdthesis{HMassalinPhD
+,author="H. Massalin"
+,title="Synthesis: An Efficient Implementation of Fundamental Operating
+System Services"
+,school="Columbia University"
+,address="New York, NY"
+,year="1992"
+,annotation="
+ Mondo optimizing compiler.
+ Wait-free stuff.
+ Good advice: defer work to avoid synchronization.
+"
+}
+
@unpublished{Jacobson93
,author="Van Jacobson"
,title="Avoid Read-Side Locking Via Delayed Free"
@@ -635,3 +660,86 @@ Revised:
"
}
+@unpublished{PaulEMcKenney2007PreemptibleRCU
+,Author="Paul E. McKenney"
+,Title="The design of preemptible read-copy-update"
+,month="October"
+,day="8"
+,year="2007"
+,note="Available:
+\url{http://lwn.net/Articles/253651/}
+[Viewed October 25, 2007]"
+,annotation="
+ LWN article describing the design of preemptible RCU.
+"
+}
+
+########################################################################
+#
+# "What is RCU?" LWN series.
+#
+
+@unpublished{PaulEMcKenney2007WhatIsRCUFundamentally
+,Author="Paul E. McKenney and Jonathan Walpole"
+,Title="What is {RCU}, Fundamentally?"
+,month="December"
+,day="17"
+,year="2007"
+,note="Available:
+\url{http://lwn.net/Articles/262464/}
+[Viewed December 27, 2007]"
+,annotation="
+ Lays out the three basic components of RCU: (1) publish-subscribe,
+ (2) wait for pre-existing readers to complete, and (2) maintain
+ multiple versions.
+"
+}
+
+@unpublished{PaulEMcKenney2008WhatIsRCUUsage
+,Author="Paul E. McKenney"
+,Title="What is {RCU}? Part 2: Usage"
+,month="January"
+,day="4"
+,year="2008"
+,note="Available:
+\url{http://lwn.net/Articles/263130/}
+[Viewed January 4, 2008]"
+,annotation="
+ Lays out six uses of RCU:
+ 1. RCU is a Reader-Writer Lock Replacement
+ 2. RCU is a Restricted Reference-Counting Mechanism
+ 3. RCU is a Bulk Reference-Counting Mechanism
+ 4. RCU is a Poor Man's Garbage Collector
+ 5. RCU is a Way of Providing Existence Guarantees
+ 6. RCU is a Way of Waiting for Things to Finish
+"
+}
+
+@unpublished{PaulEMcKenney2008WhatIsRCUAPI
+,Author="Paul E. McKenney"
+,Title="{RCU} part 3: the {RCU} {API}"
+,month="January"
+,day="17"
+,year="2008"
+,note="Available:
+\url{http://lwn.net/Articles/264090/}
+[Viewed January 10, 2008]"
+,annotation="
+ Gives an overview of the Linux-kernel RCU API and a brief annotated RCU
+ bibliography.
+"
+}
+
+@article{DinakarGuniguntala2008IBMSysJ
+,author="D. Guniguntala and P. E. McKenney and J. Triplett and J. Walpole"
+,title="The read-copy-update mechanism for supporting real-time applications on shared-memory multiprocessor systems with {Linux}"
+,Year="2008"
+,Month="April"
+,journal="IBM Systems Journal"
+,volume="47"
+,number="2"
+,pages="@@-@@"
+,annotation="
+ RCU, realtime RCU, sleepable RCU, performance.
+"
+}
diff --git a/Documentation/RCU/checklist.txt b/Documentation/RCU/checklist.txt
index 42b01bc2e1b4..cf5562cbe356 100644
--- a/Documentation/RCU/checklist.txt
+++ b/Documentation/RCU/checklist.txt
@@ -13,10 +13,13 @@ over a rather long period of time, but improvements are always welcome!
detailed performance measurements show that RCU is nonetheless
the right tool for the job.
- The other exception would be where performance is not an issue,
- and RCU provides a simpler implementation. An example of this
- situation is the dynamic NMI code in the Linux 2.6 kernel,
- at least on architectures where NMIs are rare.
+ Another exception is where performance is not an issue, and RCU
+ provides a simpler implementation. An example of this situation
+ is the dynamic NMI code in the Linux 2.6 kernel, at least on
+ architectures where NMIs are rare.
+
+ Yet another exception is where the low real-time latency of RCU's
+ read-side primitives is critically important.
1. Does the update code have proper mutual exclusion?
@@ -39,9 +42,10 @@ over a rather long period of time, but improvements are always welcome!
2. Do the RCU read-side critical sections make proper use of
rcu_read_lock() and friends? These primitives are needed
- to suppress preemption (or bottom halves, in the case of
- rcu_read_lock_bh()) in the read-side critical sections,
- and are also an excellent aid to readability.
+ to prevent grace periods from ending prematurely, which
+ could result in data being unceremoniously freed out from
+ under your read-side code, which can greatly increase the
+ actuarial risk of your kernel.
As a rough rule of thumb, any dereference of an RCU-protected
pointer must be covered by rcu_read_lock() or rcu_read_lock_bh()
@@ -54,15 +58,30 @@ over a rather long period of time, but improvements are always welcome!
be running while updates are in progress. There are a number
of ways to handle this concurrency, depending on the situation:
- a. Make updates appear atomic to readers. For example,
+ a. Use the RCU variants of the list and hlist update
+ primitives to add, remove, and replace elements on an
+ RCU-protected list. Alternatively, use the RCU-protected
+ trees that have been added to the Linux kernel.
+
+ This is almost always the best approach.
+
+ b. Proceed as in (a) above, but also maintain per-element
+ locks (that are acquired by both readers and writers)
+ that guard per-element state. Of course, fields that
+ the readers refrain from accessing can be guarded by the
+ update-side lock.
+
+ This works quite well, also.
+
+ c. Make updates appear atomic to readers. For example,
pointer updates to properly aligned fields will appear
atomic, as will individual atomic primitives. Operations
performed under a lock and sequences of multiple atomic
primitives will -not- appear to be atomic.
- This is almost always the best approach.
+ This can work, but is starting to get a bit tricky.
- b. Carefully order the updates and the reads so that
+ d. Carefully order the updates and the reads so that
readers see valid data at all phases of the update.
This is often more difficult than it sounds, especially
given modern CPUs' tendency to reorder memory references.
@@ -123,18 +142,22 @@ over a rather long period of time, but improvements are always welcome!
when publicizing a pointer to a structure that can
be traversed by an RCU read-side critical section.
-5. If call_rcu(), or a related primitive such as call_rcu_bh(),
- is used, the callback function must be written to be called
- from softirq context. In particular, it cannot block.
+5. If call_rcu(), or a related primitive such as call_rcu_bh() or
+ call_rcu_sched(), is used, the callback function must be
+ written to be called from softirq context. In particular,
+ it cannot block.
6. Since synchronize_rcu() can block, it cannot be called from
- any sort of irq context.
+ any sort of irq context. Ditto for synchronize_sched() and
+ synchronize_srcu().
7. If the updater uses call_rcu(), then the corresponding readers
must use rcu_read_lock() and rcu_read_unlock(). If the updater
uses call_rcu_bh(), then the corresponding readers must use
- rcu_read_lock_bh() and rcu_read_unlock_bh(). Mixing things up
- will result in confusion and broken kernels.
+ rcu_read_lock_bh() and rcu_read_unlock_bh(). If the updater
+ uses call_rcu_sched(), then the corresponding readers must
+ disable preemption. Mixing things up will result in confusion
+ and broken kernels.
One exception to this rule: rcu_read_lock() and rcu_read_unlock()
may be substituted for rcu_read_lock_bh() and rcu_read_unlock_bh()
@@ -143,9 +166,9 @@ over a rather long period of time, but improvements are always welcome!
such cases is a must, of course! And the jury is still out on
whether the increased speed is worth it.
-8. Although synchronize_rcu() is a bit slower than is call_rcu(),
- it usually results in simpler code. So, unless update
- performance is critically important or the updaters cannot block,
+8. Although synchronize_rcu() is slower than is call_rcu(), it
+ usually results in simpler code. So, unless update performance
+ is critically important or the updaters cannot block,
synchronize_rcu() should be used in preference to call_rcu().
An especially important property of the synchronize_rcu()
@@ -187,23 +210,23 @@ over a rather long period of time, but improvements are always welcome!
number of updates per grace period.
9. All RCU list-traversal primitives, which include
- list_for_each_rcu(), list_for_each_entry_rcu(),
+ rcu_dereference(), list_for_each_rcu(), list_for_each_entry_rcu(),
list_for_each_continue_rcu(), and list_for_each_safe_rcu(),
- must be within an RCU read-side critical section. RCU
+ must be either within an RCU read-side critical section or
+ must be protected by appropriate update-side locks. RCU
read-side critical sections are delimited by rcu_read_lock()
and rcu_read_unlock(), or by similar primitives such as
rcu_read_lock_bh() and rcu_read_unlock_bh().
- Use of the _rcu() list-traversal primitives outside of an
- RCU read-side critical section causes no harm other than
- a slight performance degradation on Alpha CPUs. It can
- also be quite helpful in reducing code bloat when common
- code is shared between readers and updaters.
+ The reason that it is permissible to use RCU list-traversal
+ primitives when the update-side lock is held is that doing so
+ can be quite helpful in reducing code bloat when common code is
+ shared between readers and updaters.
10. Conversely, if you are in an RCU read-side critical section,
- you -must- use the "_rcu()" variants of the list macros.
- Failing to do so will break Alpha and confuse people reading
- your code.
+ and you don't hold the appropriate update-side lock, you -must-
+ use the "_rcu()" variants of the list macros. Failing to do so
+ will break Alpha and confuse people reading your code.
11. Note that synchronize_rcu() -only- guarantees to wait until
all currently executing rcu_read_lock()-protected RCU read-side
@@ -230,6 +253,14 @@ over a rather long period of time, but improvements are always welcome!
must use whatever locking or other synchronization is required
to safely access and/or modify that data structure.
+ RCU callbacks are -usually- executed on the same CPU that executed
+ the corresponding call_rcu(), call_rcu_bh(), or call_rcu_sched(),
+ but are by -no- means guaranteed to be. For example, if a given
+ CPU goes offline while having an RCU callback pending, then that
+ RCU callback will execute on some surviving CPU. (If this was
+ not the case, a self-spawning RCU callback would prevent the
+ victim CPU from ever going offline.)
+
14. SRCU (srcu_read_lock(), srcu_read_unlock(), and synchronize_srcu())
may only be invoked from process context. Unlike other forms of
RCU, it -is- permissible to block in an SRCU read-side critical
diff --git a/Documentation/RCU/whatisRCU.txt b/Documentation/RCU/whatisRCU.txt
index e0d6d99b8f9b..e04d643a9f57 100644
--- a/Documentation/RCU/whatisRCU.txt
+++ b/Documentation/RCU/whatisRCU.txt
@@ -1,3 +1,11 @@
+Please note that the "What is RCU?" LWN series is an excellent place
+to start learning about RCU:
+
+1. What is RCU, Fundamentally? http://lwn.net/Articles/262464/
+2. What is RCU? Part 2: Usage http://lwn.net/Articles/263130/
+3. RCU part 3: the RCU API http://lwn.net/Articles/264090/
+
+
What is RCU?
RCU is a synchronization mechanism that was added to the Linux kernel
@@ -772,26 +780,18 @@ Linux-kernel source code, but it helps to have a full list of the
APIs, since there does not appear to be a way to categorize them
in docbook. Here is the list, by category.
-Markers for RCU read-side critical sections:
-
- rcu_read_lock
- rcu_read_unlock
- rcu_read_lock_bh
- rcu_read_unlock_bh
- srcu_read_lock
- srcu_read_unlock
-
RCU pointer/list traversal:
rcu_dereference
+ list_for_each_entry_rcu
+ hlist_for_each_entry_rcu
+
list_for_each_rcu (to be deprecated in favor of
list_for_each_entry_rcu)
- list_for_each_entry_rcu
list_for_each_continue_rcu (to be deprecated in favor of new
list_for_each_entry_continue_rcu)
- hlist_for_each_entry_rcu
-RCU pointer update:
+RCU pointer/list update:
rcu_assign_pointer
list_add_rcu
@@ -799,16 +799,36 @@ RCU pointer update:
list_del_rcu
list_replace_rcu
hlist_del_rcu
+ hlist_add_after_rcu
+ hlist_add_before_rcu
hlist_add_head_rcu
+ hlist_replace_rcu
+ list_splice_init_rcu()
-RCU grace period:
+RCU: Critical sections Grace period Barrier
+
+ rcu_read_lock synchronize_net rcu_barrier
+ rcu_read_unlock synchronize_rcu
+ call_rcu
+
+
+bh: Critical sections Grace period Barrier
+
+ rcu_read_lock_bh call_rcu_bh rcu_barrier_bh
+ rcu_read_unlock_bh
+
+
+sched: Critical sections Grace period Barrier
+
+ [preempt_disable] synchronize_sched rcu_barrier_sched
+ [and friends] call_rcu_sched
+
+
+SRCU: Critical sections Grace period Barrier
+
+ srcu_read_lock synchronize_srcu N/A
+ srcu_read_unlock
- synchronize_net
- synchronize_sched
- synchronize_rcu
- synchronize_srcu
- call_rcu
- call_rcu_bh
See the comment headers in the source code (or the docbook generated
from them) for more information.
diff --git a/Documentation/cpusets.txt b/Documentation/cpusets.txt
index d803c5c68ab5..353504de3084 100644
--- a/Documentation/cpusets.txt
+++ b/Documentation/cpusets.txt
@@ -542,7 +542,7 @@ otherwise initial value -1 that indicates the cpuset has no request.
2 : search cores in a package.
3 : search cpus in a node [= system wide on non-NUMA system]
( 4 : search nodes in a chunk of node [on NUMA system] )
- ( 5~ : search system wide [on NUMA system])
+ ( 5 : search system wide [on NUMA system] )
This file is per-cpuset and affect the sched domain where the cpuset
belongs to. Therefore if the flag 'sched_load_balance' of a cpuset
diff --git a/Documentation/cputopology.txt b/Documentation/cputopology.txt
index b61cb9564023..bd699da24666 100644
--- a/Documentation/cputopology.txt
+++ b/Documentation/cputopology.txt
@@ -14,9 +14,8 @@ represent the thread siblings to cpu X in the same physical package;
To implement it in an architecture-neutral way, a new source file,
drivers/base/topology.c, is to export the 4 attributes.
-If one architecture wants to support this feature, it just needs to
-implement 4 defines, typically in file include/asm-XXX/topology.h.
-The 4 defines are:
+For an architecture to support this feature, it must define some of
+these macros in include/asm-XXX/topology.h:
#define topology_physical_package_id(cpu)
#define topology_core_id(cpu)
#define topology_thread_siblings(cpu)
@@ -25,17 +24,10 @@ The 4 defines are:
The type of **_id is int.
The type of siblings is cpumask_t.
-To be consistent on all architectures, the 4 attributes should have
-default values if their values are unavailable. Below is the rule.
-1) physical_package_id: If cpu has no physical package id, -1 is the
-default value.
-2) core_id: If cpu doesn't support multi-core, its core id is 0.
-3) thread_siblings: Just include itself, if the cpu doesn't support
-HT/multi-thread.
-4) core_siblings: Just include itself, if the cpu doesn't support
-multi-core and HT/Multi-thread.
-
-So be careful when declaring the 4 defines in include/asm-XXX/topology.h.
-
-If an attribute isn't defined on an architecture, it won't be exported.
-
+To be consistent on all architectures, include/linux/topology.h
+provides default definitions for any of the above macros that are
+not defined by include/asm-XXX/topology.h:
+1) physical_package_id: -1
+2) core_id: 0
+3) thread_siblings: just the given CPU
+4) core_siblings: just the given CPU
diff --git a/Documentation/early-userspace/00-INDEX b/Documentation/early-userspace/00-INDEX
new file mode 100644
index 000000000000..9deb666c4fce
--- /dev/null
+++ b/Documentation/early-userspace/00-INDEX
@@ -0,0 +1,8 @@
+00-INDEX
+ - this file
+README
+ - explains what early userspace is
+buffer_format.txt
+ - documents the initramfs buffer format
+initrd.txt
+ - how to use the RAM disk as an initial/temporary root filesystem.
diff --git a/Documentation/initrd.txt b/Documentation/early-userspace/initrd.txt
index 1ba84f3584e3..772d6a307345 100644
--- a/Documentation/initrd.txt
+++ b/Documentation/early-userspace/initrd.txt
@@ -111,7 +111,7 @@ procedure should create the /initrd directory.
If initrd will not be mounted in some cases, its content is still
accessible if the following device has been created:
-# mknod /dev/initrd b 1 250
+# mknod /dev/initrd b 1 250
# chmod 400 /dev/initrd
Second, the kernel has to be compiled with RAM disk support and with
@@ -303,7 +303,7 @@ Last not least, CD-ROM distributors may use it for better installation
from CD, e.g. by using a boot floppy and bootstrapping a bigger RAM disk
via initrd from CD; or by booting via a loader like LOADLIN or directly
from the CD-ROM, and loading the RAM disk from CD without need of
-floppies.
+floppies.
Obsolete root change mechanism
@@ -322,7 +322,7 @@ such a directory exists on the new root file system.
In order to use this mechanism, you do not have to specify the boot
command options root, init, or rw. (If specified, they will affect
the real root file system, not the initrd environment.)
-
+
If /proc is mounted, the "real" root device can be changed from within
linuxrc by writing the number of the new root FS device to the special
file /proc/sys/kernel/real-root-dev, e.g.
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt
index 5b3f31faed56..41013d4c35e2 100644
--- a/Documentation/feature-removal-schedule.txt
+++ b/Documentation/feature-removal-schedule.txt
@@ -222,13 +222,6 @@ Who: Thomas Gleixner <tglx@linutronix.de>
---------------------------
-What: i2c-i810, i2c-prosavage and i2c-savage4
-When: May 2008
-Why: These drivers are superseded by i810fb, intelfb and savagefb.
-Who: Jean Delvare <khali@linux-fr.org>
-
----------------------------
-
What (Why):
- include/linux/netfilter_ipv4/ipt_TOS.h ipt_tos.h header files
(superseded by xt_TOS/xt_tos target & match)
diff --git a/Documentation/filesystems/nfs-rdma.txt b/Documentation/filesystems/nfs-rdma.txt
index d0ec45ae4e7d..44bd766f2e5d 100644
--- a/Documentation/filesystems/nfs-rdma.txt
+++ b/Documentation/filesystems/nfs-rdma.txt
@@ -5,7 +5,7 @@
################################################################################
Author: NetApp and Open Grid Computing
- Date: April 15, 2008
+ Date: May 29, 2008
Table of Contents
~~~~~~~~~~~~~~~~~
@@ -60,16 +60,18 @@ Installation
The procedures described in this document have been tested with
distributions from Red Hat's Fedora Project (http://fedora.redhat.com/).
- - Install nfs-utils-1.1.1 or greater on the client
+ - Install nfs-utils-1.1.2 or greater on the client
- An NFS/RDMA mount point can only be obtained by using the mount.nfs
- command in nfs-utils-1.1.1 or greater. To see which version of mount.nfs
- you are using, type:
+ An NFS/RDMA mount point can be obtained by using the mount.nfs command in
+ nfs-utils-1.1.2 or greater (nfs-utils-1.1.1 was the first nfs-utils
+ version with support for NFS/RDMA mounts, but for various reasons we
+ recommend using nfs-utils-1.1.2 or greater). To see which version of
+ mount.nfs you are using, type:
- > /sbin/mount.nfs -V
+ $ /sbin/mount.nfs -V
- If the version is less than 1.1.1 or the command does not exist,
- then you will need to install the latest version of nfs-utils.
+ If the version is less than 1.1.2 or the command does not exist,
+ you should install the latest version of nfs-utils.
Download the latest package from:
@@ -77,22 +79,33 @@ Installation
Uncompress the package and follow the installation instructions.
- If you will not be using GSS and NFSv4, the installation process
- can be simplified by disabling these features when running configure:
+ If you will not need the idmapper and gssd executables (you do not need
+ these to create an NFS/RDMA enabled mount command), the installation
+ process can be simplified by disabling these features when running
+ configure:
- > ./configure --disable-gss --disable-nfsv4
+ $ ./configure --disable-gss --disable-nfsv4
- For more information on this see the package's README and INSTALL files.
+ To build nfs-utils you will need the tcp_wrappers package installed. For
+ more information on this see the package's README and INSTALL files.
After building the nfs-utils package, there will be a mount.nfs binary in
the utils/mount directory. This binary can be used to initiate NFS v2, v3,
- or v4 mounts. To initiate a v4 mount, the binary must be called mount.nfs4.
- The standard technique is to create a symlink called mount.nfs4 to mount.nfs.
+ or v4 mounts. To initiate a v4 mount, the binary must be called
+ mount.nfs4. The standard technique is to create a symlink called
+ mount.nfs4 to mount.nfs.
- NOTE: mount.nfs and therefore nfs-utils-1.1.1 or greater is only needed
+ This mount.nfs binary should be installed at /sbin/mount.nfs as follows:
+
+ $ sudo cp utils/mount/mount.nfs /sbin/mount.nfs
+
+ In this location, mount.nfs will be invoked automatically for NFS mounts
+ by the system mount commmand.
+
+ NOTE: mount.nfs and therefore nfs-utils-1.1.2 or greater is only needed
on the NFS client machine. You do not need this specific version of
nfs-utils on the server. Furthermore, only the mount.nfs command from
- nfs-utils-1.1.1 is needed on the client.
+ nfs-utils-1.1.2 is needed on the client.
- Install a Linux kernel with NFS/RDMA
@@ -156,8 +169,8 @@ Check RDMA and NFS Setup
this time. For example, if you are using a Mellanox Tavor/Sinai/Arbel
card:
- > modprobe ib_mthca
- > modprobe ib_ipoib
+ $ modprobe ib_mthca
+ $ modprobe ib_ipoib
If you are using InfiniBand, make sure there is a Subnet Manager (SM)
running on the network. If your IB switch has an embedded SM, you can
@@ -166,7 +179,7 @@ Check RDMA and NFS Setup
If an SM is running on your network, you should see the following:
- > cat /sys/class/infiniband/driverX/ports/1/state
+ $ cat /sys/class/infiniband/driverX/ports/1/state
4: ACTIVE
where driverX is mthca0, ipath5, ehca3, etc.
@@ -174,10 +187,10 @@ Check RDMA and NFS Setup
To further test the InfiniBand software stack, use IPoIB (this
assumes you have two IB hosts named host1 and host2):
- host1> ifconfig ib0 a.b.c.x
- host2> ifconfig ib0 a.b.c.y
- host1> ping a.b.c.y
- host2> ping a.b.c.x
+ host1$ ifconfig ib0 a.b.c.x
+ host2$ ifconfig ib0 a.b.c.y
+ host1$ ping a.b.c.y
+ host2$ ping a.b.c.x
For other device types, follow the appropriate procedures.
@@ -202,11 +215,11 @@ NFS/RDMA Setup
/vol0 192.168.0.47(fsid=0,rw,async,insecure,no_root_squash)
/vol0 192.168.0.0/255.255.255.0(fsid=0,rw,async,insecure,no_root_squash)
- The IP address(es) is(are) the client's IPoIB address for an InfiniBand HCA or the
- cleint's iWARP address(es) for an RNIC.
+ The IP address(es) is(are) the client's IPoIB address for an InfiniBand
+ HCA or the cleint's iWARP address(es) for an RNIC.
- NOTE: The "insecure" option must be used because the NFS/RDMA client does not
- use a reserved port.
+ NOTE: The "insecure" option must be used because the NFS/RDMA client does
+ not use a reserved port.
Each time a machine boots:
@@ -214,43 +227,45 @@ NFS/RDMA Setup
For InfiniBand using a Mellanox adapter:
- > modprobe ib_mthca
- > modprobe ib_ipoib
- > ifconfig ib0 a.b.c.d
+ $ modprobe ib_mthca
+ $ modprobe ib_ipoib
+ $ ifconfig ib0 a.b.c.d
NOTE: use unique addresses for the client and server
- Start the NFS server
- If the NFS/RDMA server was built as a module (CONFIG_SUNRPC_XPRT_RDMA=m in kernel config),
- load the RDMA transport module:
+ If the NFS/RDMA server was built as a module (CONFIG_SUNRPC_XPRT_RDMA=m in
+ kernel config), load the RDMA transport module:
- > modprobe svcrdma
+ $ modprobe svcrdma
- Regardless of how the server was built (module or built-in), start the server:
+ Regardless of how the server was built (module or built-in), start the
+ server:
- > /etc/init.d/nfs start
+ $ /etc/init.d/nfs start
or
- > service nfs start
+ $ service nfs start
Instruct the server to listen on the RDMA transport:
- > echo rdma 2050 > /proc/fs/nfsd/portlist
+ $ echo rdma 2050 > /proc/fs/nfsd/portlist
- On the client system
- If the NFS/RDMA client was built as a module (CONFIG_SUNRPC_XPRT_RDMA=m in kernel config),
- load the RDMA client module:
+ If the NFS/RDMA client was built as a module (CONFIG_SUNRPC_XPRT_RDMA=m in
+ kernel config), load the RDMA client module:
- > modprobe xprtrdma.ko
+ $ modprobe xprtrdma.ko
- Regardless of how the client was built (module or built-in), issue the mount.nfs command:
+ Regardless of how the client was built (module or built-in), use this
+ command to mount the NFS/RDMA server:
- > /path/to/your/mount.nfs <IPoIB-server-name-or-address>:/<export> /mnt -i -o rdma,port=2050
+ $ mount -o rdma,port=2050 <IPoIB-server-name-or-address>:/<export> /mnt
- To verify that the mount is using RDMA, run "cat /proc/mounts" and check the
- "proto" field for the given mount.
+ To verify that the mount is using RDMA, run "cat /proc/mounts" and check
+ the "proto" field for the given mount.
Congratulations! You're using NFS/RDMA!
diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt
index dbc3c6a3650f..7f268f327d75 100644
--- a/Documentation/filesystems/proc.txt
+++ b/Documentation/filesystems/proc.txt
@@ -380,28 +380,35 @@ i386 and x86_64 platforms support the new IRQ vector displays.
Of some interest is the introduction of the /proc/irq directory to 2.4.
It could be used to set IRQ to CPU affinity, this means that you can "hook" an
IRQ to only one CPU, or to exclude a CPU of handling IRQs. The contents of the
-irq subdir is one subdir for each IRQ, and one file; prof_cpu_mask
+irq subdir is one subdir for each IRQ, and two files; default_smp_affinity and
+prof_cpu_mask.
For example
> ls /proc/irq/
0 10 12 14 16 18 2 4 6 8 prof_cpu_mask
- 1 11 13 15 17 19 3 5 7 9
+ 1 11 13 15 17 19 3 5 7 9 default_smp_affinity
> ls /proc/irq/0/
smp_affinity
-The contents of the prof_cpu_mask file and each smp_affinity file for each IRQ
-is the same by default:
+smp_affinity is a bitmask, in which you can specify which CPUs can handle the
+IRQ, you can set it by doing:
- > cat /proc/irq/0/smp_affinity
- ffffffff
+ > echo 1 > /proc/irq/10/smp_affinity
+
+This means that only the first CPU will handle the IRQ, but you can also echo
+5 which means that only the first and fourth CPU can handle the IRQ.
-It's a bitmask, in which you can specify which CPUs can handle the IRQ, you can
-set it by doing:
+The contents of each smp_affinity file is the same by default:
+
+ > cat /proc/irq/0/smp_affinity
+ ffffffff
- > echo 1 > /proc/irq/prof_cpu_mask
+The default_smp_affinity mask applies to all non-active IRQs, which are the
+IRQs which have not yet been allocated/activated, and hence which lack a
+/proc/irq/[0-9]* directory.
-This means that only the first CPU will handle the IRQ, but you can also echo 5
-which means that only the first and fourth CPU can handle the IRQ.
+prof_cpu_mask specifies which CPUs are to be profiled by the system wide
+profiler. Default value is ffffffff (all cpus).
The way IRQs are routed is handled by the IO-APIC, and it's Round Robin
between all the CPUs which are allowed to handle it. As usual the kernel has
diff --git a/Documentation/filesystems/sysfs-pci.txt b/Documentation/filesystems/sysfs-pci.txt
index 5daa2aaec2c5..68ef48839c04 100644
--- a/Documentation/filesystems/sysfs-pci.txt
+++ b/Documentation/filesystems/sysfs-pci.txt
@@ -36,6 +36,7 @@ files, each with their own function.
local_cpus nearby CPU mask (cpumask, ro)
resource PCI resource host addresses (ascii, ro)
resource0..N PCI resource N, if present (binary, mmap)
+ resource0_wc..N_wc PCI WC map resource N, if prefetchable (binary, mmap)
rom PCI ROM resource, if present (binary, ro)
subsystem_device PCI subsystem device (ascii, ro)
subsystem_vendor PCI subsystem vendor (ascii, ro)
diff --git a/Documentation/filesystems/sysfs.txt b/Documentation/filesystems/sysfs.txt
index 7f27b8f840d0..9e9c348275a9 100644
--- a/Documentation/filesystems/sysfs.txt
+++ b/Documentation/filesystems/sysfs.txt
@@ -248,6 +248,7 @@ The top level sysfs directory looks like:
block/
bus/
class/
+dev/
devices/
firmware/
net/
@@ -274,6 +275,11 @@ fs/ contains a directory for some filesystems. Currently each
filesystem wanting to export attributes must create its own hierarchy
below fs/ (see ./fuse.txt for an example).
+dev/ contains two directories char/ and block/. Inside these two
+directories there are symlinks named <major>:<minor>. These symlinks
+point to the sysfs directory for the given device. /sys/dev provides a
+quick way to lookup the sysfs interface for a device from the result of
+a stat(2) operation.
More information can driver-model specific features can be found in
Documentation/driver-model/.
diff --git a/Documentation/hwmon/dme1737 b/Documentation/hwmon/dme1737
index 8f446070e64a..b1fe00999439 100644
--- a/Documentation/hwmon/dme1737
+++ b/Documentation/hwmon/dme1737
@@ -22,6 +22,10 @@ Module Parameters
and PWM output control functions. Using this parameter
shouldn't be required since the BIOS usually takes care
of this.
+* probe_all_addr: bool Include non-standard LPC addresses 0x162e and 0x164e
+ when probing for ISA devices. This is required for the
+ following boards:
+ - VIA EPIA SN18000
Note that there is no need to use this parameter if the driver loads without
complaining. The driver will say so if it is necessary.
diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface
index f4a8ebc1ef1a..2d845730d4e0 100644
--- a/Documentation/hwmon/sysfs-interface
+++ b/Documentation/hwmon/sysfs-interface
@@ -2,17 +2,12 @@ Naming and data format standards for sysfs files
------------------------------------------------
The libsensors library offers an interface to the raw sensors data
-through the sysfs interface. See libsensors documentation and source for
-further information. As of writing this document, libsensors
-(from lm_sensors 2.8.3) is heavily chip-dependent. Adding or updating
-support for any given chip requires modifying the library's code.
-This is because libsensors was written for the procfs interface
-older kernel modules were using, which wasn't standardized enough.
-Recent versions of libsensors (from lm_sensors 2.8.2 and later) have
-support for the sysfs interface, though.
-
-The new sysfs interface was designed to be as chip-independent as
-possible.
+through the sysfs interface. Since lm-sensors 3.0.0, libsensors is
+completely chip-independent. It assumes that all the kernel drivers
+implement the standard sysfs interface described in this document.
+This makes adding or updating support for any given chip very easy, as
+libsensors, and applications using it, do not need to be modified.
+This is a major improvement compared to lm-sensors 2.
Note that motherboards vary widely in the connections to sensor chips.
There is no standard that ensures, for example, that the second
@@ -35,19 +30,17 @@ access this data in a simple and consistent way. That said, such programs
will have to implement conversion, labeling and hiding of inputs. For
this reason, it is still not recommended to bypass the library.
-If you are developing a userspace application please send us feedback on
-this standard.
-
-Note that this standard isn't completely established yet, so it is subject
-to changes. If you are writing a new hardware monitoring driver those
-features can't seem to fit in this interface, please contact us with your
-extension proposal. Keep in mind that backward compatibility must be
-preserved.
-
Each chip gets its own directory in the sysfs /sys/devices tree. To
find all sensor chips, it is easier to follow the device symlinks from
/sys/class/hwmon/hwmon*.
+Up to lm-sensors 3.0.0, libsensors looks for hardware monitoring attributes
+in the "physical" device directory. Since lm-sensors 3.0.1, attributes found
+in the hwmon "class" device directory are also supported. Complex drivers
+(e.g. drivers for multifunction chips) may want to use this possibility to
+avoid namespace pollution. The only drawback will be that older versions of
+libsensors won't support the driver in question.
+
All sysfs values are fixed point numbers.
There is only one value per file, unlike the older /proc specification.
diff --git a/Documentation/i2c/busses/i2c-i810 b/Documentation/i2c/busses/i2c-i810
deleted file mode 100644
index 778210ee1583..000000000000
--- a/Documentation/i2c/busses/i2c-i810
+++ /dev/null
@@ -1,47 +0,0 @@
-Kernel driver i2c-i810
-
-Supported adapters:
- * Intel 82810, 82810-DC100, 82810E, and 82815 (GMCH)
- * Intel 82845G (GMCH)
-
-Authors:
- Frodo Looijaard <frodol@dds.nl>,
- Philip Edelbrock <phil@netroedge.com>,
- Kyösti Mälkki <kmalkki@cc.hut.fi>,
- Ralph Metzler <rjkm@thp.uni-koeln.de>,
- Mark D. Studebaker <mdsxyz123@yahoo.com>
-
-Main contact: Mark Studebaker <mdsxyz123@yahoo.com>
-
-Description
------------
-
-WARNING: If you have an '810' or '815' motherboard, your standard I2C
-temperature sensors are most likely on the 801's I2C bus. You want the
-i2c-i801 driver for those, not this driver.
-
-Now for the i2c-i810...
-
-The GMCH chip contains two I2C interfaces.
-
-The first interface is used for DDC (Data Display Channel) which is a
-serial channel through the VGA monitor connector to a DDC-compliant
-monitor. This interface is defined by the Video Electronics Standards
-Association (VESA). The standards are available for purchase at
-http://www.vesa.org .
-
-The second interface is a general-purpose I2C bus. It may be connected to a
-TV-out chip such as the BT869 or possibly to a digital flat-panel display.
-
-Features
---------
-
-Both busses use the i2c-algo-bit driver for 'bit banging'
-and support for specific transactions is provided by i2c-algo-bit.
-
-Issues
-------
-
-If you enable bus testing in i2c-algo-bit (insmod i2c-algo-bit bit_test=1),
-the test may fail; if so, the i2c-i810 driver won't be inserted. However,
-we think this has been fixed.
diff --git a/Documentation/i2c/busses/i2c-prosavage b/Documentation/i2c/busses/i2c-prosavage
deleted file mode 100644
index 703687902511..000000000000
--- a/Documentation/i2c/busses/i2c-prosavage
+++ /dev/null
@@ -1,23 +0,0 @@
-Kernel driver i2c-prosavage
-
-Supported adapters:
-
- S3/VIA KM266/VT8375 aka ProSavage8
- S3/VIA KM133/VT8365 aka Savage4
-
-Author: Henk Vergonet <henk@god.dyndns.org>
-
-Description
------------
-
-The Savage4 chips contain two I2C interfaces (aka a I2C 'master' or
-'host').
-
-The first interface is used for DDC (Data Display Channel) which is a
-serial channel through the VGA monitor connector to a DDC-compliant
-monitor. This interface is defined by the Video Electronics Standards
-Association (VESA). The standards are available for purchase at
-http://www.vesa.org . The second interface is a general-purpose I2C bus.
-
-Usefull for gaining access to the TV Encoder chips.
-
diff --git a/Documentation/i2c/busses/i2c-savage4 b/Documentation/i2c/busses/i2c-savage4
deleted file mode 100644
index 6ecceab618d3..000000000000
--- a/Documentation/i2c/busses/i2c-savage4
+++ /dev/null
@@ -1,26 +0,0 @@
-Kernel driver i2c-savage4
-
-Supported adapters:
- * Savage4
- * Savage2000
-
-Authors:
- Alexander Wold <awold@bigfoot.com>,
- Mark D. Studebaker <mdsxyz123@yahoo.com>
-
-Description
------------
-
-The Savage4 chips contain two I2C interfaces (aka a I2C 'master'
-or 'host').
-
-The first interface is used for DDC (Data Display Channel) which is a
-serial channel through the VGA monitor connector to a DDC-compliant
-monitor. This interface is defined by the Video Electronics Standards
-Association (VESA). The standards are available for purchase at
-http://www.vesa.org . The DDC bus is not yet supported because its register
-is not directly memory-mapped.
-
-The second interface is a general-purpose I2C bus. This is the only
-interface supported by the driver at the moment.
-
diff --git a/Documentation/i2c/fault-codes b/Documentation/i2c/fault-codes
new file mode 100644
index 000000000000..045765c0b9b5
--- /dev/null
+++ b/Documentation/i2c/fault-codes
@@ -0,0 +1,127 @@
+This is a summary of the most important conventions for use of fault
+codes in the I2C/SMBus stack.
+
+
+A "Fault" is not always an "Error"
+----------------------------------
+Not all fault reports imply errors; "page faults" should be a familiar
+example. Software often retries idempotent operations after transient
+faults. There may be fancier recovery schemes that are appropriate in
+some cases, such as re-initializing (and maybe resetting). After such
+recovery, triggered by a fault report, there is no error.
+
+In a similar way, sometimes a "fault" code just reports one defined
+result for an operation ... it doesn't indicate that anything is wrong
+at all, just that the outcome wasn't on the "golden path".
+
+In short, your I2C driver code may need to know these codes in order
+to respond correctly. Other code may need to rely on YOUR code reporting
+the right fault code, so that it can (in turn) behave correctly.
+
+
+I2C and SMBus fault codes
+-------------------------
+These are returned as negative numbers from most calls, with zero or
+some positive number indicating a non-fault return. The specific
+numbers associated with these symbols differ between architectures,
+though most Linux systems use <asm-generic/errno*.h> numbering.
+
+Note that the descriptions here are not exhaustive. There are other
+codes that may be returned, and other cases where these codes should
+be returned. However, drivers should not return other codes for these
+cases (unless the hardware doesn't provide unique fault reports).
+
+Also, codes returned by adapter probe methods follow rules which are
+specific to their host bus (such as PCI, or the platform bus).
+
+
+EAGAIN
+ Returned by I2C adapters when they lose arbitration in master
+ transmit mode: some other master was transmitting different
+ data at the same time.
+
+ Also returned when trying to invoke an I2C operation in an
+ atomic context, when some task is already using that I2C bus
+ to execute some other operation.
+
+EBADMSG
+ Returned by SMBus logic when an invalid Packet Error Code byte
+ is received. This code is a CRC covering all bytes in the
+ transaction, and is sent before the terminating STOP. This
+ fault is only reported on read transactions; the SMBus slave
+ may have a way to report PEC mismatches on writes from the
+ host. Note that even if PECs are in use, you should not rely
+ on these as the only way to detect incorrect data transfers.
+
+EBUSY
+ Returned by SMBus adapters when the bus was busy for longer
+ than allowed. This usually indicates some device (maybe the
+ SMBus adapter) needs some fault recovery (such as resetting),
+ or that the reset was attempted but failed.
+
+EINVAL
+ This rather vague error means an invalid parameter has been
+ detected before any I/O operation was started. Use a more
+ specific fault code when you can.
+
+ One example would be a driver trying an SMBus Block Write
+ with block size outside the range of 1-32 bytes.
+
+EIO
+ This rather vague error means something went wrong when
+ performing an I/O operation. Use a more specific fault
+ code when you can.
+
+ENODEV
+ Returned by driver probe() methods. This is a bit more
+ specific than ENXIO, implying the problem isn't with the
+ address, but with the device found there. Driver probes
+ may verify the device returns *correct* responses, and
+ return this as appropriate. (The driver core will warn
+ about probe faults other than ENXIO and ENODEV.)
+
+ENOMEM
+ Returned by any component that can't allocate memory when
+ it needs to do so.
+
+ENXIO
+ Returned by I2C adapters to indicate that the address phase
+ of a transfer didn't get an ACK. While it might just mean
+ an I2C device was temporarily not responding, usually it
+ means there's nothing listening at that address.
+
+ Returned by driver probe() methods to indicate that they
+ found no device to bind to. (ENODEV may also be used.)
+
+EOPNOTSUPP
+ Returned by an adapter when asked to perform an operation
+ that it doesn't, or can't, support.
+
+ For example, this would be returned when an adapter that
+ doesn't support SMBus block transfers is asked to execute
+ one. In that case, the driver making that request should
+ have verified that functionality was supported before it
+ made that block transfer request.
+
+ Similarly, if an I2C adapter can't execute all legal I2C
+ messages, it should return this when asked to perform a
+ transaction it can't. (These limitations can't be seen in
+ the adapter's functionality mask, since the assumption is
+ that if an adapter supports I2C it supports all of I2C.)
+
+EPROTO
+ Returned when slave does not conform to the relevant I2C
+ or SMBus (or chip-specific) protocol specifications. One
+ case is when the length of an SMBus block data response
+ (from the SMBus slave) is outside the range 1-32 bytes.
+
+ETIMEDOUT
+ This is returned by drivers when an operation took too much
+ time, and was aborted before it completed.
+
+ SMBus adapters may return it when an operation took more
+ time than allowed by the SMBus specification; for example,
+ when a slave stretches clocks too far. I2C has no such
+ timeouts, but it's normal for I2C adapters to impose some
+ arbitrary limits (much longer than SMBus!) too.
+
diff --git a/Documentation/i2c/smbus-protocol b/Documentation/i2c/smbus-protocol
index 03f08fb491cc..24bfb65da17d 100644
--- a/Documentation/i2c/smbus-protocol
+++ b/Documentation/i2c/smbus-protocol
@@ -42,8 +42,8 @@ Count (8 bits): A data byte containing the length of a block operation.
[..]: Data sent by I2C device, as opposed to data sent by the host adapter.
-SMBus Quick Command: i2c_smbus_write_quick()
-=============================================
+SMBus Quick Command
+===================
This sends a single bit to the device, at the place of the Rd/Wr bit.
diff --git a/Documentation/i2c/writing-clients b/Documentation/i2c/writing-clients
index ee75cbace28d..63722d3c9cdf 100644
--- a/Documentation/i2c/writing-clients
+++ b/Documentation/i2c/writing-clients
@@ -25,12 +25,23 @@ routines, and should be zero-initialized except for fields with data you
provide. A client structure holds device-specific information like the
driver model device node, and its I2C address.
+/* iff driver uses driver model ("new style") binding model: */
+
+static struct i2c_device_id foo_idtable[] = {
+ { "foo", my_id_for_foo },
+ { "bar", my_id_for_bar },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, foo_idtable);
+
static struct i2c_driver foo_driver = {
.driver = {
.name = "foo",
},
/* iff driver uses driver model ("new style") binding model: */
+ .id_table = foo_ids,
.probe = foo_probe,
.remove = foo_remove,
@@ -173,10 +184,9 @@ handle may be used during foo_probe(). If foo_probe() reports success
(zero not a negative status code) it may save the handle and use it until
foo_remove() returns. That binding model is used by most Linux drivers.
-Drivers match devices when i2c_client.driver_name and the driver name are
-the same; this approach is used in several other busses that don't have
-device typing support in the hardware. The driver and module name should
-match, so hotplug/coldplug mechanisms will modprobe the driver.
+The probe function is called when an entry in the id_table name field
+matches the device's name. It is passed the entry that was matched so
+the driver knows which one in the table matched.
Device Creation (Standard driver model)
@@ -559,7 +569,6 @@ SMBus communication
in terms of it. Never use this function directly!
- extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value);
extern s32 i2c_smbus_read_byte(struct i2c_client * client);
extern s32 i2c_smbus_write_byte(struct i2c_client * client, u8 value);
extern s32 i2c_smbus_read_byte_data(struct i2c_client * client, u8 command);
@@ -568,30 +577,31 @@ SMBus communication
extern s32 i2c_smbus_read_word_data(struct i2c_client * client, u8 command);
extern s32 i2c_smbus_write_word_data(struct i2c_client * client,
u8 command, u16 value);
+ extern s32 i2c_smbus_read_block_data(struct i2c_client * client,
+ u8 command, u8 *values);
extern s32 i2c_smbus_write_block_data(struct i2c_client * client,
u8 command, u8 length,
u8 *values);
extern s32 i2c_smbus_read_i2c_block_data(struct i2c_client * client,
u8 command, u8 length, u8 *values);
-
-These ones were removed in Linux 2.6.10 because they had no users, but could
-be added back later if needed:
-
- extern s32 i2c_smbus_read_block_data(struct i2c_client * client,
- u8 command, u8 *values);
extern s32 i2c_smbus_write_i2c_block_data(struct i2c_client * client,
u8 command, u8 length,
u8 *values);
+
+These ones were removed from i2c-core because they had no users, but could
+be added back later if needed:
+
+ extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value);
extern s32 i2c_smbus_process_call(struct i2c_client * client,
u8 command, u16 value);
extern s32 i2c_smbus_block_process_call(struct i2c_client *client,
u8 command, u8 length,
u8 *values)
-All these transactions return -1 on failure. The 'write' transactions
-return 0 on success; the 'read' transactions return the read value, except
-for read_block, which returns the number of values read. The block buffers
-need not be longer than 32 bytes.
+All these transactions return a negative errno value on failure. The 'write'
+transactions return 0 on success; the 'read' transactions return the read
+value, except for block transactions, which return the number of values
+read. The block buffers need not be longer than 32 bytes.
You can read the file `smbus-protocol' for more information about the
actual SMBus protocol.
diff --git a/Documentation/ia64/paravirt_ops.txt b/Documentation/ia64/paravirt_ops.txt
new file mode 100644
index 000000000000..39ded02ec33f
--- /dev/null
+++ b/Documentation/ia64/paravirt_ops.txt
@@ -0,0 +1,137 @@
+Paravirt_ops on IA64
+====================
+ 21 May 2008, Isaku Yamahata <yamahata@valinux.co.jp>
+
+
+Introduction
+------------
+The aim of this documentation is to help with maintainability and/or to
+encourage people to use paravirt_ops/IA64.
+
+paravirt_ops (pv_ops in short) is a way for virtualization support of
+Linux kernel on x86. Several ways for virtualization support were
+proposed, paravirt_ops is the winner.
+On the other hand, now there are also several IA64 virtualization
+technologies like kvm/IA64, xen/IA64 and many other academic IA64
+hypervisors so that it is good to add generic virtualization
+infrastructure on Linux/IA64.
+
+
+What is paravirt_ops?
+---------------------
+It has been developed on x86 as virtualization support via API, not ABI.
+It allows each hypervisor to override operations which are important for
+hypervisors at API level. And it allows a single kernel binary to run on
+all supported execution environments including native machine.
+Essentially paravirt_ops is a set of function pointers which represent
+operations corresponding to low level sensitive instructions and high
+level functionalities in various area. But one significant difference
+from usual function pointer table is that it allows optimization with
+binary patch. It is because some of these operations are very
+performance sensitive and indirect call overhead is not negligible.
+With binary patch, indirect C function call can be transformed into
+direct C function call or in-place execution to eliminate the overhead.
+
+Thus, operations of paravirt_ops are classified into three categories.
+- simple indirect call
+ These operations correspond to high level functionality so that the
+ overhead of indirect call isn't very important.
+
+- indirect call which allows optimization with binary patch
+ Usually these operations correspond to low level instructions. They
+ are called frequently and performance critical. So the overhead is
+ very important.
+
+- a set of macros for hand written assembly code
+ Hand written assembly codes (.S files) also need paravirtualization
+ because they include sensitive instructions or some of code paths in
+ them are very performance critical.
+
+
+The relation to the IA64 machine vector
+---------------------------------------
+Linux/IA64 has the IA64 machine vector functionality which allows the
+kernel to switch implementations (e.g. initialization, ipi, dma api...)
+depending on executing platform.
+We can replace some implementations very easily defining a new machine
+vector. Thus another approach for virtualization support would be
+enhancing the machine vector functionality.
+But paravirt_ops approach was taken because
+- virtualization support needs wider support than machine vector does.
+ e.g. low level instruction paravirtualization. It must be
+ initialized very early before platform detection.
+
+- virtualization support needs more functionality like binary patch.
+ Probably the calling overhead might not be very large compared to the
+ emulation overhead of virtualization. However in the native case, the
+ overhead should be eliminated completely.
+ A single kernel binary should run on each environment including native,
+ and the overhead of paravirt_ops on native environment should be as
+ small as possible.
+
+- for full virtualization technology, e.g. KVM/IA64 or
+ Xen/IA64 HVM domain, the result would be
+ (the emulated platform machine vector. probably dig) + (pv_ops).
+ This means that the virtualization support layer should be under
+ the machine vector layer.
+
+Possibly it might be better to move some function pointers from
+paravirt_ops to machine vector. In fact, Xen domU case utilizes both
+pv_ops and machine vector.
+
+
+IA64 paravirt_ops
+-----------------
+In this section, the concrete paravirt_ops will be discussed.
+Because of the architecture difference between ia64 and x86, the
+resulting set of functions is very different from x86 pv_ops.
+
+- C function pointer tables
+They are not very performance critical so that simple C indirect
+function call is acceptable. The following structures are defined at
+this moment. For details see linux/include/asm-ia64/paravirt.h
+ - struct pv_info
+ This structure describes the execution environment.
+ - struct pv_init_ops
+ This structure describes the various initialization hooks.
+ - struct pv_iosapic_ops
+ This structure describes hooks to iosapic operations.
+ - struct pv_irq_ops
+ This structure describes hooks to irq related operations
+ - struct pv_time_op
+ This structure describes hooks to steal time accounting.
+
+- a set of indirect calls which need optimization
+Currently this class of functions correspond to a subset of IA64
+intrinsics. At this moment the optimization with binary patch isn't
+implemented yet.
+struct pv_cpu_op is defined. For details see
+linux/include/asm-ia64/paravirt_privop.h
+Mostly they correspond to ia64 intrinsics 1-to-1.
+Caveat: Now they are defined as C indirect function pointers, but in
+order to support binary patch optimization, they will be changed
+using GCC extended inline assembly code.
+
+- a set of macros for hand written assembly code (.S files)
+For maintenance purpose, the taken approach for .S files is single
+source code and compile multiple times with different macros definitions.
+Each pv_ops instance must define those macros to compile.
+The important thing here is that sensitive, but non-privileged
+instructions must be paravirtualized and that some privileged
+instructions also need paravirtualization for reasonable performance.
+Developers who modify .S files must be aware of that. At this moment
+an easy checker is implemented to detect paravirtualization breakage.
+But it doesn't cover all the cases.
+
+Sometimes this set of macros is called pv_cpu_asm_op. But there is no
+corresponding structure in the source code.
+Those macros mostly 1:1 correspond to a subset of privileged
+instructions. See linux/include/asm-ia64/native/inst.h.
+And some functions written in assembly also need to be overrided so
+that each pv_ops instance have to define some macros. Again see
+linux/include/asm-ia64/native/inst.h.
+
+
+Those structures must be initialized very early before start_kernel.
+Probably initialized in head.S using multi entry point or some other trick.
+For native case implementation see linux/arch/ia64/kernel/paravirt.c.
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index e07c432c731f..5134d63a84a4 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -147,10 +147,14 @@ and is between 256 and 4096 characters. It is defined in the file
default: 0
acpi_sleep= [HW,ACPI] Sleep options
- Format: { s3_bios, s3_mode, s3_beep }
+ Format: { s3_bios, s3_mode, s3_beep, old_ordering }
See Documentation/power/video.txt for s3_bios and s3_mode.
s3_beep is for debugging; it makes the PC's speaker beep
as soon as the kernel's real-mode entry point is called.
+ old_ordering causes the ACPI 1.0 ordering of the _PTS
+ control method, wrt putting devices into low power
+ states, to be enforced (the ACPI 2.0 ordering of _PTS is
+ used by default).
acpi_sci= [HW,ACPI] ACPI System Control Interrupt trigger mode
Format: { level | edge | high | low }
@@ -599,6 +603,29 @@ and is between 256 and 4096 characters. It is defined in the file
See drivers/char/README.epca and
Documentation/digiepca.txt.
+ disable_mtrr_cleanup [X86]
+ enable_mtrr_cleanup [X86]
+ The kernel tries to adjust MTRR layout from continuous
+ to discrete, to make X server driver able to add WB
+ entry later. This parameter enables/disables that.
+
+ mtrr_chunk_size=nn[KMG] [X86]
+ used for mtrr cleanup. It is largest continous chunk
+ that could hold holes aka. UC entries.
+
+ mtrr_gran_size=nn[KMG] [X86]
+ Used for mtrr cleanup. It is granularity of mtrr block.
+ Default is 1.
+ Large value could prevent small alignment from
+ using up MTRRs.
+
+ mtrr_spare_reg_nr=n [X86]
+ Format: <integer>
+ Range: 0,7 : spare reg number
+ Default : 1
+ Used for mtrr cleanup. It is spare mtrr entries number.
+ Set to 2 or more if your graphical card needs more.
+
disable_mtrr_trim [X86, Intel and AMD only]
By default the kernel will trim any uncacheable
memory out of your available memory pool based on
@@ -722,9 +749,6 @@ and is between 256 and 4096 characters. It is defined in the file
hd= [EIDE] (E)IDE hard drive subsystem geometry
Format: <cyl>,<head>,<sect>
- hd?= [HW] (E)IDE subsystem
- hd?lun= See Documentation/ide/ide.txt.
-
highmem=nn[KMG] [KNL,BOOT] forces the highmem zone to have an exact
size of <nn>. This works even on boxes that have no
highmem otherwise. This also works to reduce highmem
@@ -1496,6 +1520,9 @@ and is between 256 and 4096 characters. It is defined in the file
Use with caution as certain devices share
address decoders between ROMs and other
resources.
+ norom [X86-32,X86_64] Do not assign address space to
+ expansion ROMs that do not already have
+ BIOS assigned address ranges.
irqmask=0xMMMM [X86-32] Set a bit mask of IRQs allowed to be
assigned automatically to PCI devices. You can
make the kernel exclude IRQs of your ISA cards
@@ -1971,6 +1998,9 @@ and is between 256 and 4096 characters. It is defined in the file
snd-ymfpci= [HW,ALSA]
+ softlockup_panic=
+ [KNL] Should the soft-lockup detector generate panics.
+
sonypi.*= [HW] Sony Programmable I/O Control Device driver
See Documentation/sonypi.txt
diff --git a/Documentation/kobject.txt b/Documentation/kobject.txt
index bf3256e04027..f5d2aad65a67 100644
--- a/Documentation/kobject.txt
+++ b/Documentation/kobject.txt
@@ -118,6 +118,10 @@ the name of the kobject, call kobject_rename():
int kobject_rename(struct kobject *kobj, const char *new_name);
+Note kobject_rename does perform any locking or have a solid notion of
+what names are valid so the provide must provide their own sanity checking
+and serialization.
+
There is a function called kobject_set_name() but that is legacy cruft and
is being removed. If your code needs to call this function, it is
incorrect and needs to be fixed.
@@ -305,7 +309,7 @@ should not be manipulated by any other user.
A kset keeps its children in a standard kernel linked list. Kobjects point
back to their containing kset via their kset field. In almost all cases,
-the kobjects belonging to a ket have that kset (or, strictly, its embedded
+the kobjects belonging to a kset have that kset (or, strictly, its embedded
kobject) in their parent.
As a kset contains a kobject within it, it should always be dynamically
diff --git a/Documentation/laptops/thinkpad-acpi.txt b/Documentation/laptops/thinkpad-acpi.txt
index 01c6c3d8a7e3..64b3f146e4b0 100644
--- a/Documentation/laptops/thinkpad-acpi.txt
+++ b/Documentation/laptops/thinkpad-acpi.txt
@@ -503,7 +503,7 @@ generate input device EV_KEY events.
In addition to the EV_KEY events, thinkpad-acpi may also issue EV_SW
events for switches:
-SW_RADIO T60 and later hardare rfkill rocker switch
+SW_RFKILL_ALL T60 and later hardare rfkill rocker switch
SW_TABLET_MODE Tablet ThinkPads HKEY events 0x5009 and 0x500A
Non hot-key ACPI HKEY event map:
diff --git a/Documentation/scheduler/sched-domains.txt b/Documentation/scheduler/sched-domains.txt
index a9e990ab980f..373ceacc367e 100644
--- a/Documentation/scheduler/sched-domains.txt
+++ b/Documentation/scheduler/sched-domains.txt
@@ -61,10 +61,7 @@ builder by #define'ing ARCH_HASH_SCHED_DOMAIN, and exporting your
arch_init_sched_domains function. This function will attach domains to all
CPUs using cpu_attach_domain.
-Implementors should change the line
-#undef SCHED_DOMAIN_DEBUG
-to
-#define SCHED_DOMAIN_DEBUG
-in kernel/sched.c as this enables an error checking parse of the sched domains
+The sched-domains debugging infrastructure can be enabled by enabling
+CONFIG_SCHED_DEBUG. This enables an error checking parse of the sched domains
which should catch most possible errors (described above). It also prints out
the domain structure in a visual format.
diff --git a/Documentation/scsi/aacraid.txt b/Documentation/scsi/aacraid.txt
index d16011a8618e..709ca991a451 100644
--- a/Documentation/scsi/aacraid.txt
+++ b/Documentation/scsi/aacraid.txt
@@ -56,19 +56,33 @@ Supported Cards/Chipsets
9005:0285:9005:02d1 Adaptec 5405 (Voodoo40)
9005:0285:15d9:02d2 SMC AOC-USAS-S8i-LP
9005:0285:15d9:02d3 SMC AOC-USAS-S8iR-LP
- 9005:0285:9005:02d4 Adaptec 2045 (Voodoo04 Lite)
- 9005:0285:9005:02d5 Adaptec 2405 (Voodoo40 Lite)
- 9005:0285:9005:02d6 Adaptec 2445 (Voodoo44 Lite)
- 9005:0285:9005:02d7 Adaptec 2805 (Voodoo80 Lite)
+ 9005:0285:9005:02d4 Adaptec ASR-2045 (Voodoo04 Lite)
+ 9005:0285:9005:02d5 Adaptec ASR-2405 (Voodoo40 Lite)
+ 9005:0285:9005:02d6 Adaptec ASR-2445 (Voodoo44 Lite)
+ 9005:0285:9005:02d7 Adaptec ASR-2805 (Voodoo80 Lite)
+ 9005:0285:9005:02d8 Adaptec 5405G (Voodoo40 PM)
+ 9005:0285:9005:02d9 Adaptec 5445G (Voodoo44 PM)
+ 9005:0285:9005:02da Adaptec 5805G (Voodoo80 PM)
+ 9005:0285:9005:02db Adaptec 5085G (Voodoo08 PM)
+ 9005:0285:9005:02dc Adaptec 51245G (Voodoo124 PM)
+ 9005:0285:9005:02dd Adaptec 51645G (Voodoo164 PM)
+ 9005:0285:9005:02de Adaptec 52445G (Voodoo244 PM)
+ 9005:0285:9005:02df Adaptec ASR-2045G (Voodoo04 Lite PM)
+ 9005:0285:9005:02e0 Adaptec ASR-2405G (Voodoo40 Lite PM)
+ 9005:0285:9005:02e1 Adaptec ASR-2445G (Voodoo44 Lite PM)
+ 9005:0285:9005:02e2 Adaptec ASR-2805G (Voodoo80 Lite PM)
1011:0046:9005:0364 Adaptec 5400S (Mustang)
+ 1011:0046:9005:0365 Adaptec 5400S (Mustang)
9005:0287:9005:0800 Adaptec Themisto (Jupiter)
9005:0200:9005:0200 Adaptec Themisto (Jupiter)
9005:0286:9005:0800 Adaptec Callisto (Jupiter)
1011:0046:9005:1364 Dell PERC 2/QC (Quad Channel, Mustang)
+ 1011:0046:9005:1365 Dell PERC 2/QC (Quad Channel, Mustang)
1028:0001:1028:0001 Dell PERC 2/Si (Iguana)
1028:0003:1028:0003 Dell PERC 3/Si (SlimFast)
1028:0002:1028:0002 Dell PERC 3/Di (Opal)
- 1028:0004:1028:0004 Dell PERC 3/DiF (Iguana)
+ 1028:0004:1028:0004 Dell PERC 3/SiF (Iguana)
+ 1028:0004:1028:00d0 Dell PERC 3/DiF (Iguana)
1028:0002:1028:00d1 Dell PERC 3/DiV (Viper)
1028:0002:1028:00d9 Dell PERC 3/DiL (Lexus)
1028:000a:1028:0106 Dell PERC 3/DiJ (Jaguar)
diff --git a/Documentation/tracers/mmiotrace.txt b/Documentation/tracers/mmiotrace.txt
new file mode 100644
index 000000000000..a4afb560a45b
--- /dev/null
+++ b/Documentation/tracers/mmiotrace.txt
@@ -0,0 +1,164 @@
+ In-kernel memory-mapped I/O tracing
+
+
+Home page and links to optional user space tools:
+
+ http://nouveau.freedesktop.org/wiki/MmioTrace
+
+MMIO tracing was originally developed by Intel around 2003 for their Fault
+Injection Test Harness. In Dec 2006 - Jan 2007, using the code from Intel,
+Jeff Muizelaar created a tool for tracing MMIO accesses with the Nouveau
+project in mind. Since then many people have contributed.
+
+Mmiotrace was built for reverse engineering any memory-mapped IO device with
+the Nouveau project as the first real user. Only x86 and x86_64 architectures
+are supported.
+
+Out-of-tree mmiotrace was originally modified for mainline inclusion and
+ftrace framework by Pekka Paalanen <pq@iki.fi>.
+
+
+Preparation
+-----------
+
+Mmiotrace feature is compiled in by the CONFIG_MMIOTRACE option. Tracing is
+disabled by default, so it is safe to have this set to yes. SMP systems are
+supported, but tracing is unreliable and may miss events if more than one CPU
+is on-line, therefore mmiotrace takes all but one CPU off-line during run-time
+activation. You can re-enable CPUs by hand, but you have been warned, there
+is no way to automatically detect if you are losing events due to CPUs racing.
+
+
+Usage Quick Reference
+---------------------
+
+$ mount -t debugfs debugfs /debug
+$ echo mmiotrace > /debug/tracing/current_tracer
+$ cat /debug/tracing/trace_pipe > mydump.txt &
+Start X or whatever.
+$ echo "X is up" > /debug/tracing/marker
+$ echo none > /debug/tracing/current_tracer
+Check for lost events.
+
+
+Usage
+-----
+
+Make sure debugfs is mounted to /debug. If not, (requires root privileges)
+$ mount -t debugfs debugfs /debug
+
+Check that the driver you are about to trace is not loaded.
+
+Activate mmiotrace (requires root privileges):
+$ echo mmiotrace > /debug/tracing/current_tracer
+
+Start storing the trace:
+$ cat /debug/tracing/trace_pipe > mydump.txt &
+The 'cat' process should stay running (sleeping) in the background.
+
+Load the driver you want to trace and use it. Mmiotrace will only catch MMIO
+accesses to areas that are ioremapped while mmiotrace is active.
+
+[Unimplemented feature:]
+During tracing you can place comments (markers) into the trace by
+$ echo "X is up" > /debug/tracing/marker
+This makes it easier to see which part of the (huge) trace corresponds to
+which action. It is recommended to place descriptive markers about what you
+do.
+
+Shut down mmiotrace (requires root privileges):
+$ echo none > /debug/tracing/current_tracer
+The 'cat' process exits. If it does not, kill it by issuing 'fg' command and
+pressing ctrl+c.
+
+Check that mmiotrace did not lose events due to a buffer filling up. Either
+$ grep -i lost mydump.txt
+which tells you exactly how many events were lost, or use
+$ dmesg
+to view your kernel log and look for "mmiotrace has lost events" warning. If
+events were lost, the trace is incomplete. You should enlarge the buffers and
+try again. Buffers are enlarged by first seeing how large the current buffers
+are:
+$ cat /debug/tracing/trace_entries
+gives you a number. Approximately double this number and write it back, for
+instance:
+$ echo 128000 > /debug/tracing/trace_entries
+Then start again from the top.
+
+If you are doing a trace for a driver project, e.g. Nouveau, you should also
+do the following before sending your results:
+$ lspci -vvv > lspci.txt
+$ dmesg > dmesg.txt
+$ tar zcf pciid-nick-mmiotrace.tar.gz mydump.txt lspci.txt dmesg.txt
+and then send the .tar.gz file. The trace compresses considerably. Replace
+"pciid" and "nick" with the PCI ID or model name of your piece of hardware
+under investigation and your nick name.
+
+
+How Mmiotrace Works
+-------------------
+
+Access to hardware IO-memory is gained by mapping addresses from PCI bus by
+calling one of the ioremap_*() functions. Mmiotrace is hooked into the
+__ioremap() function and gets called whenever a mapping is created. Mapping is
+an event that is recorded into the trace log. Note, that ISA range mappings
+are not caught, since the mapping always exists and is returned directly.
+
+MMIO accesses are recorded via page faults. Just before __ioremap() returns,
+the mapped pages are marked as not present. Any access to the pages causes a
+fault. The page fault handler calls mmiotrace to handle the fault. Mmiotrace
+marks the page present, sets TF flag to achieve single stepping and exits the
+fault handler. The instruction that faulted is executed and debug trap is
+entered. Here mmiotrace again marks the page as not present. The instruction
+is decoded to get the type of operation (read/write), data width and the value
+read or written. These are stored to the trace log.
+
+Setting the page present in the page fault handler has a race condition on SMP
+machines. During the single stepping other CPUs may run freely on that page
+and events can be missed without a notice. Re-enabling other CPUs during
+tracing is discouraged.
+
+
+Trace Log Format
+----------------
+
+The raw log is text and easily filtered with e.g. grep and awk. One record is
+one line in the log. A record starts with a keyword, followed by keyword
+dependant arguments. Arguments are separated by a space, or continue until the
+end of line. The format for version 20070824 is as follows:
+
+Explanation Keyword Space separated arguments
+---------------------------------------------------------------------------
+
+read event R width, timestamp, map id, physical, value, PC, PID
+write event W width, timestamp, map id, physical, value, PC, PID
+ioremap event MAP timestamp, map id, physical, virtual, length, PC, PID
+iounmap event UNMAP timestamp, map id, PC, PID
+marker MARK timestamp, text
+version VERSION the string "20070824"
+info for reader LSPCI one line from lspci -v
+PCI address map PCIDEV space separated /proc/bus/pci/devices data
+unk. opcode UNKNOWN timestamp, map id, physical, data, PC, PID
+
+Timestamp is in seconds with decimals. Physical is a PCI bus address, virtual
+is a kernel virtual address. Width is the data width in bytes and value is the
+data value. Map id is an arbitrary id number identifying the mapping that was
+used in an operation. PC is the program counter and PID is process id. PC is
+zero if it is not recorded. PID is always zero as tracing MMIO accesses
+originating in user space memory is not yet supported.
+
+For instance, the following awk filter will pass all 32-bit writes that target
+physical addresses in the range [0xfb73ce40, 0xfb800000[
+
+$ awk '/W 4 / { adr=strtonum($5); if (adr >= 0xfb73ce40 &&
+adr < 0xfb800000) print; }'
+
+
+Tools for Developers
+--------------------
+
+The user space tools include utilities for:
+- replacing numeric addresses and values with hardware register names
+- replaying MMIO logs, i.e., re-executing the recorded writes
+
+
diff --git a/Documentation/usb/WUSB-Design-overview.txt b/Documentation/usb/WUSB-Design-overview.txt
new file mode 100644
index 000000000000..4c3d62c7843a
--- /dev/null
+++ b/Documentation/usb/WUSB-Design-overview.txt
@@ -0,0 +1,448 @@
+
+Linux UWB + Wireless USB + WiNET
+
+ (C) 2005-2006 Intel Corporation
+ Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+
+ 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.
+
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+
+Please visit http://bughost.org/thewiki/Design-overview.txt-1.8 for
+updated content.
+
+ * Design-overview.txt-1.8
+
+This code implements a Ultra Wide Band stack for Linux, as well as
+drivers for the the USB based UWB radio controllers defined in the
+Wireless USB 1.0 specification (including Wireless USB host controller
+and an Intel WiNET controller).
+
+ 1. Introduction
+ 1. HWA: Host Wire adapters, your Wireless USB dongle
+
+ 2. DWA: Device Wired Adaptor, a Wireless USB hub for wired
+ devices
+ 3. WHCI: Wireless Host Controller Interface, the PCI WUSB host
+ adapter
+ 2. The UWB stack
+ 1. Devices and hosts: the basic structure
+
+ 2. Host Controller life cycle
+
+ 3. On the air: beacons and enumerating the radio neighborhood
+
+ 4. Device lists
+ 5. Bandwidth allocation
+
+ 3. Wireless USB Host Controller drivers
+
+ 4. Glossary
+
+
+ Introduction
+
+UWB is a wide-band communication protocol that is to serve also as the
+low-level protocol for others (much like TCP sits on IP). Currently
+these others are Wireless USB and TCP/IP, but seems Bluetooth and
+Firewire/1394 are coming along.
+
+UWB uses a band from roughly 3 to 10 GHz, transmitting at a max of
+~-41dB (or 0.074 uW/MHz--geography specific data is still being
+negotiated w/ regulators, so watch for changes). That band is divided in
+a bunch of ~1.5 GHz wide channels (or band groups) composed of three
+subbands/subchannels (528 MHz each). Each channel is independent of each
+other, so you could consider them different "busses". Initially this
+driver considers them all a single one.
+
+Radio time is divided in 65536 us long /superframes/, each one divided
+in 256 256us long /MASs/ (Media Allocation Slots), which are the basic
+time/media allocation units for transferring data. At the beginning of
+each superframe there is a Beacon Period (BP), where every device
+transmit its beacon on a single MAS. The length of the BP depends on how
+many devices are present and the length of their beacons.
+
+Devices have a MAC (fixed, 48 bit address) and a device (changeable, 16
+bit address) and send periodic beacons to advertise themselves and pass
+info on what they are and do. They advertise their capabilities and a
+bunch of other stuff.
+
+The different logical parts of this driver are:
+
+ *
+
+ *UWB*: the Ultra-Wide-Band stack -- manages the radio and
+ associated spectrum to allow for devices sharing it. Allows to
+ control bandwidth assingment, beaconing, scanning, etc
+
+ *
+
+ *WUSB*: the layer that sits on top of UWB to provide Wireless USB.
+ The Wireless USB spec defines means to control a UWB radio and to
+ do the actual WUSB.
+
+
+ HWA: Host Wire adapters, your Wireless USB dongle
+
+WUSB also defines a device called a Host Wire Adaptor (HWA), which in
+mere terms is a USB dongle that enables your PC to have UWB and Wireless
+USB. The Wireless USB Host Controller in a HWA looks to the host like a
+[Wireless] USB controller connected via USB (!)
+
+The HWA itself is broken in two or three main interfaces:
+
+ *
+
+ *RC*: Radio control -- this implements an interface to the
+ Ultra-Wide-Band radio controller. The driver for this implements a
+ USB-based UWB Radio Controller to the UWB stack.
+
+ *
+
+ *HC*: the wireless USB host controller. It looks like a USB host
+ whose root port is the radio and the WUSB devices connect to it.
+ To the system it looks like a separate USB host. The driver (will)
+ implement a USB host controller (similar to UHCI, OHCI or EHCI)
+ for which the root hub is the radio...To reiterate: it is a USB
+ controller that is connected via USB instead of PCI.
+
+ *
+
+ *WINET*: some HW provide a WiNET interface (IP over UWB). This
+ package provides a driver for it (it looks like a network
+ interface, winetX). The driver detects when there is a link up for
+ their type and kick into gear.
+
+
+ DWA: Device Wired Adaptor, a Wireless USB hub for wired devices
+
+These are the complement to HWAs. They are a USB host for connecting
+wired devices, but it is connected to your PC connected via Wireless
+USB. To the system it looks like yet another USB host. To the untrained
+eye, it looks like a hub that connects upstream wirelessly.
+
+We still offer no support for this; however, it should share a lot of
+code with the HWA-RC driver; there is a bunch of factorization work that
+has been done to support that in upcoming releases.
+
+
+ WHCI: Wireless Host Controller Interface, the PCI WUSB host adapter
+
+This is your usual PCI device that implements WHCI. Similar in concept
+to EHCI, it allows your wireless USB devices (including DWAs) to connect
+to your host via a PCI interface. As in the case of the HWA, it has a
+Radio Control interface and the WUSB Host Controller interface per se.
+
+There is still no driver support for this, but will be in upcoming
+releases.
+
+
+ The UWB stack
+
+The main mission of the UWB stack is to keep a tally of which devices
+are in radio proximity to allow drivers to connect to them. As well, it
+provides an API for controlling the local radio controllers (RCs from
+now on), such as to start/stop beaconing, scan, allocate bandwidth, etc.
+
+
+ Devices and hosts: the basic structure
+
+The main building block here is the UWB device (struct uwb_dev). For
+each device that pops up in radio presence (ie: the UWB host receives a
+beacon from it) you get a struct uwb_dev that will show up in
+/sys/class/uwb and in /sys/bus/uwb/devices.
+
+For each RC that is detected, a new struct uwb_rc is created. In turn, a
+RC is also a device, so they also show in /sys/class/uwb and
+/sys/bus/uwb/devices, but at the same time, only radio controllers show
+up in /sys/class/uwb_rc.
+
+ *
+
+ [*] The reason for RCs being also devices is that not only we can
+ see them while enumerating the system device tree, but also on the
+ radio (their beacons and stuff), so the handling has to be
+ likewise to that of a device.
+
+Each RC driver is implemented by a separate driver that plugs into the
+interface that the UWB stack provides through a struct uwb_rc_ops. The
+spec creators have been nice enough to make the message format the same
+for HWA and WHCI RCs, so the driver is really a very thin transport that
+moves the requests from the UWB API to the device [/uwb_rc_ops->cmd()/]
+and sends the replies and notifications back to the API
+[/uwb_rc_neh_grok()/]. Notifications are handled to the UWB daemon, that
+is chartered, among other things, to keep the tab of how the UWB radio
+neighborhood looks, creating and destroying devices as they show up or
+dissapear.
+
+Command execution is very simple: a command block is sent and a event
+block or reply is expected back. For sending/receiving command/events, a
+handle called /neh/ (Notification/Event Handle) is opened with
+/uwb_rc_neh_open()/.
+
+The HWA-RC (USB dongle) driver (drivers/uwb/hwa-rc.c) does this job for
+the USB connected HWA. Eventually, drivers/whci-rc.c will do the same
+for the PCI connected WHCI controller.
+
+
+ Host Controller life cycle
+
+So let's say we connect a dongle to the system: it is detected and
+firmware uploaded if needed [for Intel's i1480
+/drivers/uwb/ptc/usb.c:ptc_usb_probe()/] and then it is reenumerated.
+Now we have a real HWA device connected and
+/drivers/uwb/hwa-rc.c:hwarc_probe()/ picks it up, that will set up the
+Wire-Adaptor environment and then suck it into the UWB stack's vision of
+the world [/drivers/uwb/lc-rc.c:uwb_rc_add()/].
+
+ *
+
+ [*] The stack should put a new RC to scan for devices
+ [/uwb_rc_scan()/] so it finds what's available around and tries to
+ connect to them, but this is policy stuff and should be driven
+ from user space. As of now, the operator is expected to do it
+ manually; see the release notes for documentation on the procedure.
+
+When a dongle is disconnected, /drivers/uwb/hwa-rc.c:hwarc_disconnect()/
+takes time of tearing everything down safely (or not...).
+
+
+ On the air: beacons and enumerating the radio neighborhood
+
+So assuming we have devices and we have agreed for a channel to connect
+on (let's say 9), we put the new RC to beacon:
+
+ *
+
+ $ echo 9 0 > /sys/class/uwb_rc/uwb0/beacon
+
+Now it is visible. If there were other devices in the same radio channel
+and beacon group (that's what the zero is for), the dongle's radio
+control interface will send beacon notifications on its
+notification/event endpoint (NEEP). The beacon notifications are part of
+the event stream that is funneled into the API with
+/drivers/uwb/neh.c:uwb_rc_neh_grok()/ and delivered to the UWBD, the UWB
+daemon through a notification list.
+
+UWBD wakes up and scans the event list; finds a beacon and adds it to
+the BEACON CACHE (/uwb_beca/). If he receives a number of beacons from
+the same device, he considers it to be 'onair' and creates a new device
+[/drivers/uwb/lc-dev.c:uwbd_dev_onair()/]. Similarly, when no beacons
+are received in some time, the device is considered gone and wiped out
+[uwbd calls periodically /uwb/beacon.c:uwb_beca_purge()/ that will purge
+the beacon cache of dead devices].
+
+
+ Device lists
+
+All UWB devices are kept in the list of the struct bus_type uwb_bus.
+
+
+ Bandwidth allocation
+
+The UWB stack maintains a local copy of DRP availability through
+processing of incoming *DRP Availability Change* notifications. This
+local copy is currently used to present the current bandwidth
+availability to the user through the sysfs file
+/sys/class/uwb_rc/uwbx/bw_avail. In the future the bandwidth
+availability information will be used by the bandwidth reservation
+routines.
+
+The bandwidth reservation routines are in progress and are thus not
+present in the current release. When completed they will enable a user
+to initiate DRP reservation requests through interaction with sysfs. DRP
+reservation requests from remote UWB devices will also be handled. The
+bandwidth management done by the UWB stack will include callbacks to the
+higher layers will enable the higher layers to use the reservations upon
+completion. [Note: The bandwidth reservation work is in progress and
+subject to change.]
+
+
+ Wireless USB Host Controller drivers
+
+*WARNING* This section needs a lot of work!
+
+As explained above, there are three different types of HCs in the WUSB
+world: HWA-HC, DWA-HC and WHCI-HC.
+
+HWA-HC and DWA-HC share that they are Wire-Adapters (USB or WUSB
+connected controllers), and their transfer management system is almost
+identical. So is their notification delivery system.
+
+HWA-HC and WHCI-HC share that they are both WUSB host controllers, so
+they have to deal with WUSB device life cycle and maintenance, wireless
+root-hub
+
+HWA exposes a Host Controller interface (HWA-HC 0xe0/02/02). This has
+three endpoints (Notifications, Data Transfer In and Data Transfer
+Out--known as NEP, DTI and DTO in the code).
+
+We reserve UWB bandwidth for our Wireless USB Cluster, create a Cluster
+ID and tell the HC to use all that. Then we start it. This means the HC
+starts sending MMCs.
+
+ *
+
+ The MMCs are blocks of data defined somewhere in the WUSB1.0 spec
+ that define a stream in the UWB channel time allocated for sending
+ WUSB IEs (host to device commands/notifications) and Device
+ Notifications (device initiated to host). Each host defines a
+ unique Wireless USB cluster through MMCs. Devices can connect to a
+ single cluster at the time. The IEs are Information Elements, and
+ among them are the bandwidth allocations that tell each device
+ when can they transmit or receive.
+
+Now it all depends on external stimuli.
+
+*New device connection*
+
+A new device pops up, it scans the radio looking for MMCs that give out
+the existence of Wireless USB channels. Once one (or more) are found,
+selects which one to connect to. Sends a /DN_Connect/ (device
+notification connect) during the DNTS (Device Notification Time
+Slot--announced in the MMCs
+
+HC picks the /DN_Connect/ out (nep module sends to notif.c for delivery
+into /devconnect/). This process starts the authentication process for
+the device. First we allocate a /fake port/ and assign an
+unauthenticated address (128 to 255--what we really do is
+0x80 | fake_port_idx). We fiddle with the fake port status and /khubd/
+sees a new connection, so he moves on to enable the fake port with a reset.
+
+So now we are in the reset path -- we know we have a non-yet enumerated
+device with an unauthorized address; we ask user space to authenticate
+(FIXME: not yet done, similar to bluetooth pairing), then we do the key
+exchange (FIXME: not yet done) and issue a /set address 0/ to bring the
+device to the default state. Device is authenticated.
+
+From here, the USB stack takes control through the usb_hcd ops. khubd
+has seen the port status changes, as we have been toggling them. It will
+start enumerating and doing transfers through usb_hcd->urb_enqueue() to
+read descriptors and move our data.
+
+*Device life cycle and keep alives*
+
+Everytime there is a succesful transfer to/from a device, we update a
+per-device activity timestamp. If not, every now and then we check and
+if the activity timestamp gets old, we ping the device by sending it a
+Keep Alive IE; it responds with a /DN_Alive/ pong during the DNTS (this
+arrives to us as a notification through
+devconnect.c:wusb_handle_dn_alive(). If a device times out, we
+disconnect it from the system (cleaning up internal information and
+toggling the bits in the fake hub port, which kicks khubd into removing
+the rest of the stuff).
+
+This is done through devconnect:__wusb_check_devs(), which will scan the
+device list looking for whom needs refreshing.
+
+If the device wants to disconnect, it will either die (ugly) or send a
+/DN_Disconnect/ that will prompt a disconnection from the system.
+
+*Sending and receiving data*
+
+Data is sent and received through /Remote Pipes/ (rpipes). An rpipe is
+/aimed/ at an endpoint in a WUSB device. This is the same for HWAs and
+DWAs.
+
+Each HC has a number of rpipes and buffers that can be assigned to them;
+when doing a data transfer (xfer), first the rpipe has to be aimed and
+prepared (buffers assigned), then we can start queueing requests for
+data in or out.
+
+Data buffers have to be segmented out before sending--so we send first a
+header (segment request) and then if there is any data, a data buffer
+immediately after to the DTI interface (yep, even the request). If our
+buffer is bigger than the max segment size, then we just do multiple
+requests.
+
+[This sucks, because doing USB scatter gatter in Linux is resource
+intensive, if any...not that the current approach is not. It just has to
+be cleaned up a lot :)].
+
+If reading, we don't send data buffers, just the segment headers saying
+we want to read segments.
+
+When the xfer is executed, we receive a notification that says data is
+ready in the DTI endpoint (handled through
+xfer.c:wa_handle_notif_xfer()). In there we read from the DTI endpoint a
+descriptor that gives us the status of the transfer, its identification
+(given when we issued it) and the segment number. If it was a data read,
+we issue another URB to read into the destination buffer the chunk of
+data coming out of the remote endpoint. Done, wait for the next guy. The
+callbacks for the URBs issued from here are the ones that will declare
+the xfer complete at some point and call it's callback.
+
+Seems simple, but the implementation is not trivial.
+
+ *
+
+ *WARNING* Old!!
+
+The main xfer descriptor, wa_xfer (equivalent to a URB) contains an
+array of segments, tallys on segments and buffers and callback
+information. Buried in there is a lot of URBs for executing the segments
+and buffer transfers.
+
+For OUT xfers, there is an array of segments, one URB for each, another
+one of buffer URB. When submitting, we submit URBs for segment request
+1, buffer 1, segment 2, buffer 2...etc. Then we wait on the DTI for xfer
+result data; when all the segments are complete, we call the callback to
+finalize the transfer.
+
+For IN xfers, we only issue URBs for the segments we want to read and
+then wait for the xfer result data.
+
+*URB mapping into xfers*
+
+This is done by hwahc_op_urb_[en|de]queue(). In enqueue() we aim an
+rpipe to the endpoint where we have to transmit, create a transfer
+context (wa_xfer) and submit it. When the xfer is done, our callback is
+called and we assign the status bits and release the xfer resources.
+
+In dequeue() we are basically cancelling/aborting the transfer. We issue
+a xfer abort request to the HC, cancell all the URBs we had submitted
+and not yet done and when all that is done, the xfer callback will be
+called--this will call the URB callback.
+
+
+ Glossary
+
+*DWA* -- Device Wire Adapter
+
+USB host, wired for downstream devices, upstream connects wirelessly
+with Wireless USB.
+
+*EVENT* -- Response to a command on the NEEP
+
+*HWA* -- Host Wire Adapter / USB dongle for UWB and Wireless USB
+
+*NEH* -- Notification/Event Handle
+
+Handle/file descriptor for receiving notifications or events. The WA
+code requires you to get one of this to listen for notifications or
+events on the NEEP.
+
+*NEEP* -- Notification/Event EndPoint
+
+Stuff related to the management of the first endpoint of a HWA USB
+dongle that is used to deliver an stream of events and notifications to
+the host.
+
+*NOTIFICATION* -- Message coming in the NEEP as response to something.
+
+*RC* -- Radio Control
+
+Design-overview.txt-1.8 (last edited 2006-11-04 12:22:24 by
+InakyPerezGonzalez)
+
diff --git a/Documentation/usb/gadget_serial.txt b/Documentation/usb/gadget_serial.txt
index 815f5c2301ff..9b22bd14c348 100644
--- a/Documentation/usb/gadget_serial.txt
+++ b/Documentation/usb/gadget_serial.txt
@@ -1,6 +1,7 @@
Linux Gadget Serial Driver v2.0
11/20/2004
+ (updated 8-May-2008 for v2.3)
License and Disclaimer
@@ -31,7 +32,7 @@ Prerequisites
-------------
Versions of the gadget serial driver are available for the
2.4 Linux kernels, but this document assumes you are using
-version 2.0 or later of the gadget serial driver in a 2.6
+version 2.3 or later of the gadget serial driver in a 2.6
Linux kernel.
This document assumes that you are familiar with Linux and
@@ -40,6 +41,12 @@ standard utilities, use minicom and HyperTerminal, and work with
USB and serial devices. It also assumes you configure the Linux
gadget and usb drivers as modules.
+With version 2.3 of the driver, major and minor device nodes are
+no longer statically defined. Your Linux based system should mount
+sysfs in /sys, and use "mdev" (in Busybox) or "udev" to make the
+/dev nodes matching the sysfs /sys/class/tty files.
+
+
Overview
--------
@@ -104,15 +111,8 @@ driver. All this are listed under "USB Gadget Support" when
configuring the kernel. Then rebuild and install the kernel or
modules.
-The gadget serial driver uses major number 127, for now. So you
-will need to create a device node for it, like this:
-
- mknod /dev/ttygserial c 127 0
-
-You only need to do this once.
-
Then you must load the gadget serial driver. To load it as an
-ACM device, do this:
+ACM device (recommended for interoperability), do this:
modprobe g_serial use_acm=1
@@ -125,6 +125,23 @@ controller driver. This must be done each time you reboot the gadget
side Linux system. You can add this to the start up scripts, if
desired.
+Your system should use mdev (from busybox) or udev to make the
+device nodes. After this gadget driver has been set up you should
+then see a /dev/ttyGS0 node:
+
+ # ls -l /dev/ttyGS0 | cat
+ crw-rw---- 1 root root 253, 0 May 8 14:10 /dev/ttyGS0
+ #
+
+Note that the major number (253, above) is system-specific. If
+you need to create /dev nodes by hand, the right numbers to use
+will be in the /sys/class/tty/ttyGS0/dev file.
+
+When you link this gadget driver early, perhaps even statically,
+you may want to set up an /etc/inittab entry to run "getty" on it.
+The /dev/ttyGS0 line should work like most any other serial port.
+
+
If gadget serial is loaded as an ACM device you will want to use
either the Windows or Linux ACM driver on the host side. If gadget
serial is loaded as a bulk in/out device, you will want to use the
diff --git a/Documentation/usb/uhci.txt b/Documentation/usb/uhci.txt
deleted file mode 100644
index 2f25952c86c6..000000000000
--- a/Documentation/usb/uhci.txt
+++ /dev/null
@@ -1,165 +0,0 @@
-Specification and Internals for the New UHCI Driver (Whitepaper...)
-
- brought to you by
-
- Georg Acher, acher@in.tum.de (executive slave) (base guitar)
- Deti Fliegl, deti@fliegl.de (executive slave) (lead voice)
- Thomas Sailer, sailer@ife.ee.ethz.ch (chief consultant) (cheer leader)
-
- $Id: README.uhci,v 1.1 1999/12/14 14:03:02 fliegl Exp $
-
-This document and the new uhci sources can be found on
- http://hotswap.in.tum.de/usb
-
-1. General issues
-
-1.1 Why a new UHCI driver, we already have one?!?
-
-Correct, but its internal structure got more and more mixed up by the (still
-ongoing) efforts to get isochronous transfers (ISO) to work.
-Since there is an increasing need for reliable ISO-transfers (especially
-for USB-audio needed by TS and for a DAB-USB-Receiver build by GA and DF),
-this state was a bit unsatisfying in our opinion, so we've decided (based
-on knowledge and experiences with the old UHCI driver) to start
-from scratch with a new approach, much simpler but at the same time more
-powerful.
-It is inspired by the way Win98/Win2000 handles USB requests via URBs,
-but it's definitely 100% free of MS-code and doesn't crash while
-unplugging an used ISO-device like Win98 ;-)
-Some code for HW setup and root hub management was taken from the
-original UHCI driver, but heavily modified to fit into the new code.
-The invention of the basic concept, and major coding were completed in two
-days (and nights) on the 16th and 17th of October 1999, now known as the
-great USB-October-Revolution started by GA, DF, and TS ;-)
-
-Since the concept is in no way UHCI dependent, we hope that it will also be
-transferred to the OHCI-driver, so both drivers share a common API.
-
-1.2. Advantages and disadvantages
-
-+ All USB transfer types work now!
-+ Asynchronous operation
-+ Simple, but powerful interface (only two calls for start and cancel)
-+ Easy migration to the new API, simplified by a compatibility API
-+ Simple usage of ISO transfers
-+ Automatic linking of requests
-+ ISO transfers allow variable length for each frame and striping
-+ No CPU dependent and non-portable atomic memory access, no asm()-inlines
-+ Tested on x86 and Alpha
-
-- Rewriting for ISO transfers needed
-
-1.3. Is there some compatibility to the old API?
-
-Yes, but only for control, bulk and interrupt transfers. We've implemented
-some wrapper calls for these transfer types. The usbcore works fine with
-these wrappers. For ISO there's no compatibility, because the old ISO-API
-and its semantics were unnecessary complicated in our opinion.
-
-1.4. What's really working?
-
-As said above, CTRL and BULK already work fine even with the wrappers,
-so legacy code wouldn't notice the change.
-Regarding to Thomas, ISO transfers now run stable with USB audio.
-INT transfers (e.g. mouse driver) work fine, too.
-
-1.5. Are there any bugs?
-
-No ;-)
-Hm...
-Well, of course this implementation needs extensive testing on all available
-hardware, but we believe that any fixes shouldn't harm the overall concept.
-
-1.6. What should be done next?
-
-A large part of the request handling seems to be identical for UHCI and
-OHCI, so it would be a good idea to extract the common parts and have only
-the HW specific stuff in uhci.c. Furthermore, all other USB device drivers
-should need URBification, if they use isochronous or interrupt transfers.
-One thing missing in the current implementation (and the old UHCI driver)
-is fair queueing for BULK transfers. Since this would need (in principle)
-the alteration of already constructed TD chains (to switch from depth to
-breadth execution), another way has to be found. Maybe some simple
-heuristics work with the same effect.
-
----------------------------------------------------------------------------
-
-2. Internal structure and mechanisms
-
-To get quickly familiar with the internal structures, here's a short
-description how the new UHCI driver works. However, the ultimate source of
-truth is only uhci.c!
-
-2.1. Descriptor structure (QHs and TDs)
-
-During initialization, the following skeleton is allocated in init_skel:
-
- framespecific | common chain
-
-framelist[]
-[ 0 ]-----> TD --> TD -------\
-[ 1 ]-----> TD --> TD --------> TD ----> QH -------> QH -------> QH ---> NULL
- ... TD --> TD -------/
-[1023]-----> TD --> TD ------/
-
- ^^ ^^ ^^ ^^ ^^ ^^
- 1024 TDs for 7 TDs for 1 TD for Start of Start of End Chain
- ISO INT (2-128ms) 1ms-INT CTRL Chain BULK Chain
-
-For each CTRL or BULK transfer a new QH is allocated and the containing data
-transfers are appended as (vertical) TDs. After building the whole QH with its
-dangling TDs, the QH is inserted before the BULK Chain QH (for CTRL) or
-before the End Chain QH (for BULK). Since only the QH->next pointers are
-affected, no atomic memory operation is required. The three QHs in the
-common chain are never equipped with TDs!
-
-For ISO or INT, the TD for each frame is simply inserted into the appropriate
-ISO/INT-TD-chain for the desired frame. The 7 skeleton INT-TDs are scattered
-among the 1024 frames similar to the old UHCI driver.
-
-For CTRL/BULK/ISO, the last TD in the transfer has the IOC-bit set. For INT,
-every TD (there is only one...) has the IOC-bit set.
-
-Besides the data for the UHCI controller (2 or 4 32bit words), the descriptors
-are double-linked through the .vertical and .horizontal elements in the
-SW data of the descriptor (using the double-linked list structures and
-operations), but SW-linking occurs only in closed domains, i.e. for each of
-the 1024 ISO-chains and the 8 INT-chains there is a closed cycle. This
-simplifies all insertions and unlinking operations and avoids costly
-bus_to_virt()-calls.
-
-2.2. URB structure and linking to QH/TDs
-
-During assembly of the QH and TDs of the requested action, these descriptors
-are stored in urb->urb_list, so the allocated QH/TD descriptors are bound to
-this URB.
-If the assembly was successful and the descriptors were added to the HW chain,
-the corresponding URB is inserted into a global URB list for this controller.
-This list stores all pending URBs.
-
-2.3. Interrupt processing
-
-Since UHCI provides no means to directly detect completed transactions, the
-following is done in each UHCI interrupt (uhci_interrupt()):
-
-For each URB in the pending queue (process_urb()), the ACTIVE-flag of the
-associated TDs are processed (depending on the transfer type
-process_{transfer|interrupt|iso}()). If the TDs are not active anymore,
-they indicate the completion of the transaction and the status is calculated.
-Inactive QH/TDs are removed from the HW chain (since the host controller
-already removed the TDs from the QH, no atomic access is needed) and
-eventually the URB is marked as completed (OK or errors) and removed from the
-pending queue. Then the next linked URB is submitted. After (or immediately
-before) that, the completion handler is called.
-
-2.4. Unlinking URBs
-
-First, all QH/TDs stored in the URB are unlinked from the HW chain.
-To ensure that the host controller really left a vertical TD chain, we
-wait for one frame. After that, the TDs are physically destroyed.
-
-2.5. URB linking and the consequences
-
-Since URBs can be linked and the corresponding submit_urb is called in
-the UHCI-interrupt, all work associated with URB/QH/TD assembly has to be
-interrupt save. This forces kmalloc to use GFP_ATOMIC in the interrupt.
diff --git a/Documentation/usb/wusb-cbaf b/Documentation/usb/wusb-cbaf
new file mode 100644
index 000000000000..a385478ba12e
--- /dev/null
+++ b/Documentation/usb/wusb-cbaf
@@ -0,0 +1,133 @@
+#! /bin/bash
+#
+
+set -e
+
+progname=$(basename $0)
+function help
+{
+ cat <<EOF
+Usage: $progname COMMAND DEVICEs [ARGS]
+
+Command for manipulating the pairing/authentication credentials of a
+Wireless USB device that supports wired-mode Cable-Based-Association.
+
+Works in conjunction with the wusb-cba.ko driver from http://linuxuwb.org.
+
+
+DEVICE
+
+ sysfs path to the device to authenticate; for example, both this
+ guys are the same:
+
+ /sys/devices/pci0000:00/0000:00:1d.7/usb1/1-4/1-4.4/1-4.4:1.1
+ /sys/bus/usb/drivers/wusb-cbaf/1-4.4:1.1
+
+COMMAND/ARGS are
+
+ start
+
+ Start a WUSB host controller (by setting up a CHID)
+
+ set-chid DEVICE HOST-CHID HOST-BANDGROUP HOST-NAME
+
+ Sets host information in the device; after this you can call the
+ get-cdid to see how does this device report itself to us.
+
+ get-cdid DEVICE
+
+ Get the device ID associated to the HOST-CHDI we sent with
+ 'set-chid'. We might not know about it.
+
+ set-cc DEVICE
+
+ If we allow the device to connect, set a random new CDID and CK
+ (connection key). Device saves them for the next time it wants to
+ connect wireless. We save them for that next time also so we can
+ authenticate the device (when we see the CDID he uses to id
+ itself) and the CK to crypto talk to it.
+
+CHID is always 16 hex bytes in 'XX YY ZZ...' form
+BANDGROUP is almost always 0001
+
+Examples:
+
+ You can default most arguments to '' to get a sane value:
+
+ $ $progname set-chid '' '' '' "My host name"
+
+ A full sequence:
+
+ $ $progname set-chid '' '' '' "My host name"
+ $ $progname get-cdid ''
+ $ $progname set-cc ''
+
+EOF
+}
+
+
+# Defaults
+# FIXME: CHID should come from a database :), band group from the host
+host_CHID="00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff"
+host_band_group="0001"
+host_name="Linux-WUSB"
+
+devs="$(echo /sys/bus/usb/drivers/wusb-cbaf/[0-9]*)"
+hdevs="$(find /sys -name wusb_chid -printf "%h\n")"
+
+result=0
+case $1 in
+ start)
+ for dev in ${2:-$hdevs}
+ do
+ uwb_rc=$(find $(dirname $(dirname $dev)) -iname uwb_rc:uwb*)
+ if cat $uwb_rc/uwb_rc/beacon | grep -q "channel: -1"
+ then
+ echo 13 0 | cat > $uwb_rc/uwb_rc/beacon
+ echo I: started beaconing on ch 13 in host $(basename $uwb_rc)
+ fi
+ echo $host_CHID | cat > $dev/wusb_chid
+ echo I: started host $(basename $dev)
+ done
+ ;;
+ set-chid)
+ shift
+ for dev in ${2:-$devs}
+ do
+ echo "${2:-$host_CHID}" "${3:-$host_band_group}" "${4:-$host_name}" \
+ | cat > $dev/wusb_host_info
+ done
+ ;;
+ get-cdid)
+ for dev in ${2:-$devs}
+ do
+ cat $dev/wusb_cdid
+ done
+ ;;
+ set-cc)
+ for dev in ${2:-$devs}
+ do
+ shift
+ CDID="$(head --bytes=16 /dev/urandom | od -tx1 -An)"
+ CK="$(head --bytes=16 /dev/urandom | od -tx1 -An)"
+ cat > $dev/wusb_cc <<EOF
+CDID:$CDID
+CK:$CK
+EOF
+ cat <<EOF
+I: CC set
+CHID: $host_CHID
+CDID:$CDID
+CK: $CK
+EOF
+ done
+ ;;
+ help|h|--help|-h)
+ help
+ ;;
+ *)
+ echo "E: Unknown usage" 1>&2
+ help 1>&2
+ result=1
+esac
+exit $result
diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885
index 191194ea1e25..f0e613ba55b8 100644
--- a/Documentation/video4linux/CARDLIST.cx23885
+++ b/Documentation/video4linux/CARDLIST.cx23885
@@ -8,3 +8,4 @@
7 -> Hauppauge WinTV-HVR1200 [0070:71d1,0070:71d3]
8 -> Hauppauge WinTV-HVR1700 [0070:8101]
9 -> Hauppauge WinTV-HVR1400 [0070:8010]
+ 10 -> DViCO FusionHDTV7 Dual Express [18ac:d618]
diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx
index 1d6a245c828f..c7e23942c1dc 100644
--- a/Documentation/video4linux/CARDLIST.em28xx
+++ b/Documentation/video4linux/CARDLIST.em28xx
@@ -8,10 +8,12 @@
7 -> Leadtek Winfast USB II (em2800)
8 -> Kworld USB2800 (em2800)
9 -> Pinnacle Dazzle DVC 90/DVC 100 (em2820/em2840) [2304:0207,2304:021a]
- 10 -> Hauppauge WinTV HVR 900 (em2880) [2040:6500,2040:6502]
+ 10 -> Hauppauge WinTV HVR 900 (em2880) [2040:6500]
11 -> Terratec Hybrid XS (em2880) [0ccd:0042]
12 -> Kworld PVR TV 2800 RF (em2820/em2840)
13 -> Terratec Prodigy XS (em2880) [0ccd:0047]
14 -> Pixelview Prolink PlayTV USB 2.0 (em2820/em2840)
15 -> V-Gear PocketTV (em2800)
16 -> Hauppauge WinTV HVR 950 (em2880) [2040:6513,2040:6517,2040:651b,2040:651f]
+ 17 -> Pinnacle PCTV HD Pro Stick (em2880) [2304:0227]
+ 18 -> Hauppauge WinTV HVR 900 (R2) (em2880) [2040:6502]
diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134
index 67937df1e974..4c8a86f24dbe 100644
--- a/Documentation/video4linux/CARDLIST.saa7134
+++ b/Documentation/video4linux/CARDLIST.saa7134
@@ -128,7 +128,7 @@
127 -> Beholder BeholdTV 507 FM/RDS / BeholdTV 509 FM [0000:5071,0000:507B,5ace:5070,5ace:5090]
128 -> Beholder BeholdTV Columbus TVFM [0000:5201]
129 -> Beholder BeholdTV 607 / BeholdTV 609 [5ace:6070,5ace:6071,5ace:6072,5ace:6073,5ace:6090,5ace:6091,5ace:6092,5ace:6093]
-130 -> Beholder BeholdTV M6 / BeholdTV M6 Extra [5ace:6190,5ace:6193,5ace:6191]
+130 -> Beholder BeholdTV M6 [5ace:6190]
131 -> Twinhan Hybrid DTV-DVB 3056 PCI [1822:0022]
132 -> Genius TVGO AM11MCE
133 -> NXP Snake DVB-S reference design
@@ -141,3 +141,5 @@
140 -> Avermedia DVB-S Pro A700 [1461:a7a1]
141 -> Avermedia DVB-S Hybrid+FM A700 [1461:a7a2]
142 -> Beholder BeholdTV H6 [5ace:6290]
+143 -> Beholder BeholdTV M63 [5ace:6191]
+144 -> Beholder BeholdTV M6 Extra [5ace:6193]
diff --git a/Documentation/i386/IO-APIC.txt b/Documentation/x86/i386/IO-APIC.txt
index 30b4c714fbe1..30b4c714fbe1 100644
--- a/Documentation/i386/IO-APIC.txt
+++ b/Documentation/x86/i386/IO-APIC.txt
diff --git a/Documentation/i386/boot.txt b/Documentation/x86/i386/boot.txt
index 95ad15c3b01f..147bfe511cdd 100644
--- a/Documentation/i386/boot.txt
+++ b/Documentation/x86/i386/boot.txt
@@ -1,17 +1,14 @@
- THE LINUX/I386 BOOT PROTOCOL
- ----------------------------
+ THE LINUX/x86 BOOT PROTOCOL
+ ---------------------------
- H. Peter Anvin <hpa@zytor.com>
- Last update 2007-05-23
-
-On the i386 platform, the Linux kernel uses a rather complicated boot
+On the x86 platform, the Linux kernel uses a rather complicated boot
convention. This has evolved partially due to historical aspects, as
well as the desire in the early days to have the kernel itself be a
bootable image, the complicated PC memory model and due to changed
expectations in the PC industry caused by the effective demise of
real-mode DOS as a mainstream operating system.
-Currently, the following versions of the Linux/i386 boot protocol exist.
+Currently, the following versions of the Linux/x86 boot protocol exist.
Old kernels: zImage/Image support only. Some very early kernels
may not even support a command line.
@@ -372,10 +369,17 @@ Protocol: 2.00+
- If 0, the protected-mode code is loaded at 0x10000.
- If 1, the protected-mode code is loaded at 0x100000.
+ Bit 5 (write): QUIET_FLAG
+ - If 0, print early messages.
+ - If 1, suppress early messages.
+ This requests to the kernel (decompressor and early
+ kernel) to not write early messages that require
+ accessing the display hardware directly.
+
Bit 6 (write): KEEP_SEGMENTS
Protocol: 2.07+
- - if 0, reload the segment registers in the 32bit entry point.
- - if 1, do not reload the segment registers in the 32bit entry point.
+ - If 0, reload the segment registers in the 32bit entry point.
+ - If 1, do not reload the segment registers in the 32bit entry point.
Assume that %cs %ds %ss %es are all set to flat segments with
a base of 0 (or the equivalent for their environment).
@@ -504,7 +508,7 @@ Protocol: 2.06+
maximum size was 255.
Field name: hardware_subarch
-Type: write
+Type: write (optional, defaults to x86/PC)
Offset/size: 0x23c/4
Protocol: 2.07+
@@ -520,11 +524,13 @@ Protocol: 2.07+
0x00000002 Xen
Field name: hardware_subarch_data
-Type: write
+Type: write (subarch-dependent)
Offset/size: 0x240/8
Protocol: 2.07+
A pointer to data that is specific to hardware subarch
+ This field is currently unused for the default x86/PC environment,
+ do not modify.
Field name: payload_offset
Type: read
@@ -545,6 +551,34 @@ Protocol: 2.08+
The length of the payload.
+Field name: setup_data
+Type: write (special)
+Offset/size: 0x250/8
+Protocol: 2.09+
+
+ The 64-bit physical pointer to NULL terminated single linked list of
+ struct setup_data. This is used to define a more extensible boot
+ parameters passing mechanism. The definition of struct setup_data is
+ as follow:
+
+ struct setup_data {
+ u64 next;
+ u32 type;
+ u32 len;
+ u8 data[0];
+ };
+
+ Where, the next is a 64-bit physical pointer to the next node of
+ linked list, the next field of the last node is 0; the type is used
+ to identify the contents of data; the len is the length of data
+ field; the data holds the real payload.
+
+ This list may be modified at a number of points during the bootup
+ process. Therefore, when modifying this list one should always make
+ sure to consider the case where the linked list already contains
+ entries.
+
+
**** THE IMAGE CHECKSUM
From boot protocol version 2.08 onwards the CRC-32 is calculated over
@@ -553,6 +587,7 @@ initial remainder of 0xffffffff. The checksum is appended to the
file; therefore the CRC of the file up to the limit specified in the
syssize field of the header is always 0.
+
**** THE KERNEL COMMAND LINE
The kernel command line has become an important way for the boot
@@ -584,28 +619,6 @@ command line is entered using the following protocol:
covered by setup_move_size, so you may need to adjust this
field.
-Field name: setup_data
-Type: write (obligatory)
-Offset/size: 0x250/8
-Protocol: 2.09+
-
- The 64-bit physical pointer to NULL terminated single linked list of
- struct setup_data. This is used to define a more extensible boot
- parameters passing mechanism. The definition of struct setup_data is
- as follow:
-
- struct setup_data {
- u64 next;
- u32 type;
- u32 len;
- u8 data[0];
- };
-
- Where, the next is a 64-bit physical pointer to the next node of
- linked list, the next field of the last node is 0; the type is used
- to identify the contents of data; the len is the length of data
- field; the data holds the real payload.
-
**** MEMORY LAYOUT OF THE REAL-MODE CODE
diff --git a/Documentation/i386/usb-legacy-support.txt b/Documentation/x86/i386/usb-legacy-support.txt
index 1894cdfc69d9..1894cdfc69d9 100644
--- a/Documentation/i386/usb-legacy-support.txt
+++ b/Documentation/x86/i386/usb-legacy-support.txt
diff --git a/Documentation/i386/zero-page.txt b/Documentation/x86/i386/zero-page.txt
index 169ad423a3d1..169ad423a3d1 100644
--- a/Documentation/i386/zero-page.txt
+++ b/Documentation/x86/i386/zero-page.txt
diff --git a/Documentation/x86_64/00-INDEX b/Documentation/x86/x86_64/00-INDEX
index 92fc20ab5f0e..92fc20ab5f0e 100644
--- a/Documentation/x86_64/00-INDEX
+++ b/Documentation/x86/x86_64/00-INDEX
diff --git a/Documentation/x86_64/boot-options.txt b/Documentation/x86/x86_64/boot-options.txt
index b0c7b6c4abda..b0c7b6c4abda 100644
--- a/Documentation/x86_64/boot-options.txt
+++ b/Documentation/x86/x86_64/boot-options.txt
diff --git a/Documentation/x86_64/cpu-hotplug-spec b/Documentation/x86/x86_64/cpu-hotplug-spec
index 3c23e0587db3..3c23e0587db3 100644
--- a/Documentation/x86_64/cpu-hotplug-spec
+++ b/Documentation/x86/x86_64/cpu-hotplug-spec
diff --git a/Documentation/x86_64/fake-numa-for-cpusets b/Documentation/x86/x86_64/fake-numa-for-cpusets
index d1a985c5b00a..d1a985c5b00a 100644
--- a/Documentation/x86_64/fake-numa-for-cpusets
+++ b/Documentation/x86/x86_64/fake-numa-for-cpusets
diff --git a/Documentation/x86_64/kernel-stacks b/Documentation/x86/x86_64/kernel-stacks
index 5ad65d51fb95..5ad65d51fb95 100644
--- a/Documentation/x86_64/kernel-stacks
+++ b/Documentation/x86/x86_64/kernel-stacks
diff --git a/Documentation/x86_64/machinecheck b/Documentation/x86/x86_64/machinecheck
index a05e58e7b159..a05e58e7b159 100644
--- a/Documentation/x86_64/machinecheck
+++ b/Documentation/x86/x86_64/machinecheck
diff --git a/Documentation/x86_64/mm.txt b/Documentation/x86/x86_64/mm.txt
index b89b6d2bebfa..efce75097369 100644
--- a/Documentation/x86_64/mm.txt
+++ b/Documentation/x86/x86_64/mm.txt
@@ -11,9 +11,8 @@ ffffc10000000000 - ffffc1ffffffffff (=40 bits) hole
ffffc20000000000 - ffffe1ffffffffff (=45 bits) vmalloc/ioremap space
ffffe20000000000 - ffffe2ffffffffff (=40 bits) virtual memory map (1TB)
... unused hole ...
-ffffffff80000000 - ffffffff82800000 (=40 MB) kernel text mapping, from phys 0
-... unused hole ...
-ffffffff88000000 - fffffffffff00000 (=1919 MB) module mapping space
+ffffffff80000000 - ffffffffa0000000 (=512 MB) kernel text mapping, from phys 0
+ffffffffa0000000 - fffffffffff00000 (=1536 MB) module mapping space
The direct mapping covers all memory in the system up to the highest
memory address (this means in some cases it can also include PCI memory
diff --git a/Documentation/x86_64/uefi.txt b/Documentation/x86/x86_64/uefi.txt
index 7d77120a5184..7d77120a5184 100644
--- a/Documentation/x86_64/uefi.txt
+++ b/Documentation/x86/x86_64/uefi.txt
diff --git a/MAINTAINERS b/MAINTAINERS
index cb71eb47c331..3952511c0d01 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -228,41 +228,41 @@ ACPI BATTERY DRIVERS
P: Alexey Starikovskiy
M: astarikovskiy@suse.de
L: linux-acpi@vger.kernel.org
-W: http://acpi.sourceforge.net/
+W: http://www.lesswatts.org/projects/acpi/
S: Supported
ACPI EC DRIVER
P: Alexey Starikovskiy
M: astarikovskiy@suse.de
L: linux-acpi@vger.kernel.org
-W: http://acpi.sourceforge.net/
+W: http://www.lesswatts.org/projects/acpi/
S: Supported
ACPI FAN DRIVER
P: Len Brown
M: len.brown@intel.com
L: linux-acpi@vger.kernel.org
-W: http://acpi.sourceforge.net/
+W: http://www.lesswatts.org/projects/acpi/
S: Supported
ACPI PCI HOTPLUG DRIVER
P: Kristen Carlson Accardi
M: kristen.c.accardi@intel.com
-L: pcihpd-discuss@lists.sourceforge.net
+L: linux-pci@vger.kernel.org
S: Supported
ACPI THERMAL DRIVER
P: Len Brown
M: len.brown@intel.com
L: linux-acpi@vger.kernel.org
-W: http://acpi.sourceforge.net/
+W: http://www.lesswatts.org/projects/acpi/
S: Supported
ACPI VIDEO DRIVER
P: Rui Zhang
M: rui.zhang@intel.com
L: linux-acpi@vger.kernel.org
-W: http://acpi.sourceforge.net/
+W: http://www.lesswatts.org/projects/acpi/
S: Supported
ACPI WMI DRIVER
@@ -1134,23 +1134,28 @@ COMPACTPCI HOTPLUG CORE
P: Scott Murray
M: scottm@somanetworks.com
M: scott@spiteful.org
-L: pcihpd-discuss@lists.sourceforge.net
+L: linux-pci@vger.kernel.org
S: Supported
COMPACTPCI HOTPLUG ZIATECH ZT5550 DRIVER
P: Scott Murray
M: scottm@somanetworks.com
M: scott@spiteful.org
-L: pcihpd-discuss@lists.sourceforge.net
+L: linux-pci@vger.kernel.org
S: Supported
COMPACTPCI HOTPLUG GENERIC DRIVER
P: Scott Murray
M: scottm@somanetworks.com
M: scott@spiteful.org
-L: pcihpd-discuss@lists.sourceforge.net
+L: linux-pci@vger.kernel.org
S: Supported
+COMPAL LAPTOP SUPPORT
+P: Cezary Jackiewicz
+M: cezary.jackiewicz@gmail.com
+S: Maintained
+
COMPUTONE INTELLIPORT MULTIPORT CARD
P: Michael H. Warfield
M: mhw@wittsend.com
@@ -1668,6 +1673,13 @@ L: linuxppc-embedded@ozlabs.org
L: linux-kernel@vger.kernel.org
S: Maintained
+FREESCALE I2C CPM DRIVER
+P: Jochen Friedrich
+M: jochen@scram.de
+L: linuxppc-dev@ozlabs.org
+L: i2c@lm-sensors.org
+S: Maintained
+
FREESCALE SOC FS_ENET DRIVER
P: Pantelis Antoniou
M: pantelis.antoniou@gmail.com
@@ -3177,7 +3189,7 @@ S: Supported
PCIE HOTPLUG DRIVER
P: Kristen Carlson Accardi
M: kristen.c.accardi@intel.com
-L: pcihpd-discuss@lists.sourceforge.net
+L: linux-pci@vger.kernel.org
S: Supported
PCMCIA SUBSYSTEM
@@ -3658,13 +3670,6 @@ M: romieu@fr.zoreil.com
L: netdev@vger.kernel.org
S: Maintained
-SIS 5513 IDE CONTROLLER DRIVER
-P: Lionel Bouton
-M: Lionel.Bouton@inet6.fr
-W: http://inet6.dyn.dhs.org/sponsoring/sis5513/index.html
-W: http://gyver.homeip.net/sis5513/index.html
-S: Maintained
-
SIS 900/7016 FAST ETHERNET DRIVER
P: Daniele Venzano
M: venza@brownhat.org
@@ -3815,7 +3820,7 @@ S: Maintained
SHPC HOTPLUG DRIVER
P: Kristen Carlson Accardi
M: kristen.c.accardi@intel.com
-L: pcihpd-discuss@lists.sourceforge.net
+L: linux-pci@vger.kernel.org
S: Supported
SECURE DIGITAL HOST CONTROLLER INTERFACE DRIVER
@@ -4435,10 +4440,10 @@ M: johnpol@2ka.mipt.ru
S: Maintained
W83791D HARDWARE MONITORING DRIVER
-P: Charles Spirakis
-M: bezaur@gmail.com
+P: Marc Hulsman
+M: m.hulsman@tudelft.nl
L: lm-sensors@lm-sensors.org
-S: Odd Fixes
+S: Maintained
W83793 HARDWARE MONITORING DRIVER
P: Rudolf Marek
diff --git a/Makefile b/Makefile
index 2b951205317d..5e48507c4f41 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,8 @@
VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 26
-EXTRAVERSION = -rc5
-NAME = Funky Weasel is Jiggy wit it
+EXTRAVERSION = -rc6
+NAME = Rotary Wombat
# *DOCUMENTATION*
# To see a list of typical targets execute "make help"
@@ -528,6 +528,10 @@ KBUILD_CFLAGS += -g
KBUILD_AFLAGS += -gdwarf-2
endif
+ifdef CONFIG_FTRACE
+KBUILD_CFLAGS += -pg
+endif
+
# We trigger additional mismatches with less inlining
ifdef CONFIG_DEBUG_SECTION_MISMATCH
KBUILD_CFLAGS += $(call cc-option, -fno-inline-functions-called-once)
@@ -608,6 +612,7 @@ export mod_strip_cmd
ifeq ($(KBUILD_EXTMOD),)
core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
+core-$(CONFIG_KERNEL_TESTS) += tests/
vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \
$(core-y) $(core-m) $(drivers-y) $(drivers-m) \
diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c
index facf82a5499a..c626a821cdcb 100644
--- a/arch/alpha/kernel/irq.c
+++ b/arch/alpha/kernel/irq.c
@@ -42,8 +42,7 @@ void ack_bad_irq(unsigned int irq)
#ifdef CONFIG_SMP
static char irq_user_affinity[NR_IRQS];
-int
-select_smp_affinity(unsigned int irq)
+int irq_select_affinity(unsigned int irq)
{
static int last_cpu;
int cpu = last_cpu + 1;
@@ -51,7 +50,7 @@ select_smp_affinity(unsigned int irq)
if (!irq_desc[irq].chip->set_affinity || irq_user_affinity[irq])
return 1;
- while (!cpu_possible(cpu))
+ while (!cpu_possible(cpu) || !cpu_isset(cpu, irq_default_affinity))
cpu = (cpu < (NR_CPUS-1) ? cpu + 1 : 0);
last_cpu = cpu;
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index b786e68914d4..3845e5c8a34f 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -14,6 +14,8 @@ config ARM
select HAVE_OPROFILE
select HAVE_KPROBES if (!XIP_KERNEL)
select HAVE_KRETPROBES if (HAVE_KPROBES)
+ select HAVE_FTRACE if (!XIP_KERNEL)
+ select HAVE_DYNAMIC_FTRACE if (HAVE_FTRACE)
help
The ARM series is a line of low-power-consumption RISC chip designs
licensed by ARM Ltd and targeted at embedded applications and
diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile
index de9d9ee50958..95baac4939e0 100644
--- a/arch/arm/boot/compressed/Makefile
+++ b/arch/arm/boot/compressed/Makefile
@@ -69,6 +69,12 @@ SEDFLAGS = s/TEXT_START/$(ZTEXTADDR)/;s/BSS_START/$(ZBSSADDR)/
targets := vmlinux vmlinux.lds piggy.gz piggy.o font.o font.c \
head.o misc.o $(OBJS)
+
+ifeq ($(CONFIG_FTRACE),y)
+ORIG_CFLAGS := $(KBUILD_CFLAGS)
+KBUILD_CFLAGS = $(subst -pg, , $(ORIG_CFLAGS))
+endif
+
EXTRA_CFLAGS := -fpic -fno-builtin
EXTRA_AFLAGS :=
diff --git a/arch/arm/common/dmabounce.c b/arch/arm/common/dmabounce.c
index 52fc6a883281..59800a534058 100644
--- a/arch/arm/common/dmabounce.c
+++ b/arch/arm/common/dmabounce.c
@@ -554,9 +554,8 @@ dmabounce_register_dev(struct device *dev, unsigned long small_buffer_size,
device_info = kmalloc(sizeof(struct dmabounce_device_info), GFP_ATOMIC);
if (!device_info) {
- printk(KERN_ERR
- "Could not allocated dmabounce_device_info for %s",
- dev->bus_id);
+ dev_err(dev,
+ "Could not allocated dmabounce_device_info\n");
return -ENOMEM;
}
@@ -594,8 +593,7 @@ dmabounce_register_dev(struct device *dev, unsigned long small_buffer_size,
dev->archdata.dmabounce = device_info;
- printk(KERN_INFO "dmabounce: registered device %s on %s bus\n",
- dev->bus_id, dev->bus->name);
+ dev_info(dev, "dmabounce: registered device\n");
return 0;
@@ -614,16 +612,15 @@ dmabounce_unregister_dev(struct device *dev)
dev->archdata.dmabounce = NULL;
if (!device_info) {
- printk(KERN_WARNING
- "%s: Never registered with dmabounce but attempting" \
- "to unregister!\n", dev->bus_id);
+ dev_warn(dev,
+ "Never registered with dmabounce but attempting"
+ "to unregister!\n");
return;
}
if (!list_empty(&device_info->safe_buffers)) {
- printk(KERN_ERR
- "%s: Removing from dmabounce with pending buffers!\n",
- dev->bus_id);
+ dev_err(dev,
+ "Removing from dmabounce with pending buffers!\n");
BUG();
}
@@ -639,8 +636,7 @@ dmabounce_unregister_dev(struct device *dev)
kfree(device_info);
- printk(KERN_INFO "dmabounce: device %s on %s bus unregistered\n",
- dev->bus_id, dev->bus->name);
+ dev_info(dev, "dmabounce: device unregistered\n");
}
diff --git a/arch/arm/common/locomo.c b/arch/arm/common/locomo.c
index d973c986f721..c3c3a3339049 100644
--- a/arch/arm/common/locomo.c
+++ b/arch/arm/common/locomo.c
@@ -543,7 +543,6 @@ locomo_init_one_child(struct locomo *lchip, struct locomo_dev_info *info)
goto out;
}
- strncpy(dev->dev.bus_id, info->name, sizeof(dev->dev.bus_id));
/*
* If the parent device has a DMA mask associated with it,
* propagate it down to the children.
@@ -553,6 +552,7 @@ locomo_init_one_child(struct locomo *lchip, struct locomo_dev_info *info)
dev->dev.dma_mask = &dev->dma_mask;
}
+ dev_set_name(&dev->dev, "%s", info->name);
dev->devid = info->devid;
dev->dev.parent = lchip->dev;
dev->dev.bus = &locomo_bus_type;
diff --git a/arch/arm/common/sa1111.c b/arch/arm/common/sa1111.c
index eb06d0b2cb74..c8e8f0ea59e1 100644
--- a/arch/arm/common/sa1111.c
+++ b/arch/arm/common/sa1111.c
@@ -550,9 +550,7 @@ sa1111_init_one_child(struct sa1111 *sachip, struct resource *parent,
goto out;
}
- snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id),
- "%4.4lx", info->offset);
-
+ dev_set_name(&dev->dev, "%4.4lx", info->offset);
dev->devid = info->devid;
dev->dev.parent = sachip->dev;
dev->dev.bus = &sa1111_bus_type;
@@ -560,7 +558,7 @@ sa1111_init_one_child(struct sa1111 *sachip, struct resource *parent,
dev->dev.coherent_dma_mask = sachip->dev->coherent_dma_mask;
dev->res.start = sachip->phys + info->offset;
dev->res.end = dev->res.start + 511;
- dev->res.name = dev->dev.bus_id;
+ dev->res.name = dev_name(&dev->dev);
dev->res.flags = IORESOURCE_MEM;
dev->mapbase = sachip->base + info->offset;
dev->skpcr_mask = info->skpcr_mask;
@@ -570,6 +568,7 @@ sa1111_init_one_child(struct sa1111 *sachip, struct resource *parent,
if (ret) {
printk("SA1111: failed to allocate resource for %s\n",
dev->res.name);
+ dev_set_name(&dev->dev, NULL);
kfree(dev);
goto out;
}
@@ -593,7 +592,8 @@ sa1111_init_one_child(struct sa1111 *sachip, struct resource *parent,
if (dev->dma_mask != 0xffffffffUL) {
ret = dmabounce_register_dev(&dev->dev, 1024, 4096);
if (ret) {
- printk("SA1111: Failed to register %s with dmabounce", dev->dev.bus_id);
+ dev_err(&dev->dev, "SA1111: Failed to register"
+ " with dmabounce\n");
device_unregister(&dev->dev);
}
}
diff --git a/arch/arm/common/scoop.c b/arch/arm/common/scoop.c
index bc299b07a6fa..ae39553589dd 100644
--- a/arch/arm/common/scoop.c
+++ b/arch/arm/common/scoop.c
@@ -247,7 +247,7 @@ static int __devinit scoop_probe(struct platform_device *pdev)
devptr->gpio.base = -1;
if (inf->gpio_base != 0) {
- devptr->gpio.label = pdev->dev.bus_id;
+ devptr->gpio.label = dev_name(&pdev->dev);
devptr->gpio.base = inf->gpio_base;
devptr->gpio.ngpio = 12; /* PA11 = 0, PA12 = 1, etc. up to PA22 = 11 */
devptr->gpio.set = scoop_gpio_set;
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index ad455ff5aebe..eb9092ca8008 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -4,6 +4,10 @@
AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
+ifdef CONFIG_DYNAMIC_FTRACE
+CFLAGS_REMOVE_ftrace.o = -pg
+endif
+
# Object file lists.
obj-y := compat.o entry-armv.o entry-common.o irq.o \
@@ -18,6 +22,7 @@ obj-$(CONFIG_ARTHUR) += arthur.o
obj-$(CONFIG_ISA_DMA) += dma-isa.o
obj-$(CONFIG_PCI) += bios32.o isa.o
obj-$(CONFIG_SMP) += smp.o
+obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
obj-$(CONFIG_KPROBES) += kprobes.o kprobes-decode.o
obj-$(CONFIG_ATAGS_PROC) += atags.o
diff --git a/arch/arm/kernel/armksyms.c b/arch/arm/kernel/armksyms.c
index 688b7b1ee416..3b132215cbf8 100644
--- a/arch/arm/kernel/armksyms.c
+++ b/arch/arm/kernel/armksyms.c
@@ -48,6 +48,11 @@ extern void __aeabi_ulcmp(void);
extern void fpundefinstr(void);
extern void fp_enter(void);
+#ifdef CONFIG_FTRACE
+extern void mcount(void);
+EXPORT_SYMBOL(mcount);
+#endif
+
/*
* This has a special calling convention; it doesn't
* modify any of the usual registers, except for LR.
diff --git a/arch/arm/kernel/ecard.c b/arch/arm/kernel/ecard.c
index a53c0aba5c14..2ee615f77992 100644
--- a/arch/arm/kernel/ecard.c
+++ b/arch/arm/kernel/ecard.c
@@ -783,7 +783,7 @@ static void ecard_proc_init(void)
#define ec_set_resource(ec,nr,st,sz) \
do { \
- (ec)->resource[nr].name = ec->dev.bus_id; \
+ (ec)->resource[nr].name = dev_name(&ec->dev); \
(ec)->resource[nr].start = st; \
(ec)->resource[nr].end = (st) + (sz) - 1; \
(ec)->resource[nr].flags = IORESOURCE_MEM; \
@@ -853,8 +853,7 @@ static struct expansion_card *__init ecard_alloc_card(int type, int slot)
for (i = 0; i < ECARD_NUM_RESOURCES; i++) {
if (ec->resource[i].flags &&
request_resource(&iomem_resource, &ec->resource[i])) {
- printk(KERN_ERR "%s: resource(s) not available\n",
- ec->dev.bus_id);
+ dev_err(&ec->dev, "resource(s) not available\n");
ec->resource[i].end -= ec->resource[i].start;
ec->resource[i].start = 0;
ec->resource[i].flags = 0;
diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
index 597ed00a08d8..8f79a4789ed4 100644
--- a/arch/arm/kernel/entry-common.S
+++ b/arch/arm/kernel/entry-common.S
@@ -99,6 +99,53 @@ ENTRY(ret_from_fork)
#undef CALL
#define CALL(x) .long x
+#ifdef CONFIG_FTRACE
+#ifdef CONFIG_DYNAMIC_FTRACE
+ENTRY(mcount)
+ stmdb sp!, {r0-r3, lr}
+ mov r0, lr
+
+ .globl mcount_call
+mcount_call:
+ bl ftrace_stub
+ ldmia sp!, {r0-r3, pc}
+
+ENTRY(ftrace_caller)
+ stmdb sp!, {r0-r3, lr}
+ ldr r1, [fp, #-4]
+ mov r0, lr
+
+ .globl ftrace_call
+ftrace_call:
+ bl ftrace_stub
+ ldmia sp!, {r0-r3, pc}
+
+#else
+
+ENTRY(mcount)
+ stmdb sp!, {r0-r3, lr}
+ ldr r0, =ftrace_trace_function
+ ldr r2, [r0]
+ adr r0, ftrace_stub
+ cmp r0, r2
+ bne trace
+ ldmia sp!, {r0-r3, pc}
+
+trace:
+ ldr r1, [fp, #-4]
+ mov r0, lr
+ mov lr, pc
+ mov pc, r2
+ ldmia sp!, {r0-r3, pc}
+
+#endif /* CONFIG_DYNAMIC_FTRACE */
+
+ .globl ftrace_stub
+ftrace_stub:
+ mov pc, lr
+
+#endif /* CONFIG_FTRACE */
+
/*=============================================================================
* SWI handler
*-----------------------------------------------------------------------------
diff --git a/arch/arm/kernel/ftrace.c b/arch/arm/kernel/ftrace.c
new file mode 100644
index 000000000000..22f3d6e309f9
--- /dev/null
+++ b/arch/arm/kernel/ftrace.c
@@ -0,0 +1,118 @@
+/*
+ * Dynamic function tracing support.
+ *
+ * Copyright (C) 2008 Abhishek Sagar <sagar.abhishek@gmail.com>
+ *
+ * For licencing details, see COPYING.
+ *
+ * Defines low-level handling of mcount calls when the kernel
+ * is compiled with the -pg flag. When using dynamic ftrace, the
+ * mcount call-sites get patched lazily with NOP till they are
+ * enabled. All code mutation routines here take effect atomically.
+ */
+
+#include <linux/ftrace.h>
+#include <asm/cacheflush.h>
+
+#define INSN_SIZE 4
+#define PC_OFFSET 8
+#define BL_OPCODE 0xeb000000
+#define BL_OFFSET_MASK 0x00ffffff
+
+static unsigned long bl_insn;
+static const unsigned long NOP = 0xe1a00000; /* mov r0, r0 */
+
+unsigned char *ftrace_nop_replace(void)
+{
+ return (char *)&NOP;
+}
+
+/* construct a branch (BL) instruction to addr */
+unsigned char *ftrace_call_replace(unsigned long pc, unsigned long addr)
+{
+ long offset;
+
+ offset = (long)addr - (long)(pc - INSN_SIZE + PC_OFFSET);
+ if (unlikely(offset < -33554432 || offset > 33554428)) {
+ /* Can't generate branches that far (from ARM ARM). Ftrace
+ * doesn't generate branches outside of core kernel text.
+ */
+ WARN_ON_ONCE(1);
+ return NULL;
+ }
+ offset = (offset >> 2) & BL_OFFSET_MASK;
+ bl_insn = BL_OPCODE | offset;
+ return (unsigned char *)&bl_insn;
+}
+
+int ftrace_modify_code(unsigned long pc, unsigned char *old_code,
+ unsigned char *new_code)
+{
+ unsigned long err = 0, replaced = 0, old, new;
+
+ old = *(unsigned long *)old_code;
+ new = *(unsigned long *)new_code;
+ pc -= INSN_SIZE;
+
+ __asm__ __volatile__ (
+ "1: ldr %1, [%2] \n"
+ " cmp %1, %4 \n"
+ "2: streq %3, [%2] \n"
+ " cmpne %1, %3 \n"
+ " movne %0, #2 \n"
+ "3:\n"
+
+ ".section .fixup, \"ax\"\n"
+ "4: mov %0, #1 \n"
+ " b 3b \n"
+ ".previous\n"
+
+ ".section __ex_table, \"a\"\n"
+ " .long 1b, 4b \n"
+ " .long 2b, 4b \n"
+ ".previous\n"
+
+ : "=r"(err), "=r"(replaced)
+ : "r"(pc), "r"(new), "r"(old), "0"(err), "1"(replaced)
+ : "memory");
+
+ if (!err && (replaced == old))
+ flush_icache_range(pc, pc + INSN_SIZE);
+
+ return err;
+}
+
+int ftrace_update_ftrace_func(ftrace_func_t func)
+{
+ int ret;
+ unsigned long pc, old;
+ unsigned char *new;
+
+ pc = (unsigned long)&ftrace_call;
+ pc += INSN_SIZE;
+ memcpy(&old, &ftrace_call, INSN_SIZE);
+ new = ftrace_call_replace(pc, (unsigned long)func);
+ ret = ftrace_modify_code(pc, (unsigned char *)&old, new);
+ return ret;
+}
+
+int ftrace_mcount_set(unsigned long *data)
+{
+ unsigned long pc, old;
+ unsigned long *addr = data;
+ unsigned char *new;
+
+ pc = (unsigned long)&mcount_call;
+ pc += INSN_SIZE;
+ memcpy(&old, &mcount_call, INSN_SIZE);
+ new = ftrace_call_replace(pc, *addr);
+ *addr = ftrace_modify_code(pc, (unsigned char *)&old, new);
+ return 0;
+}
+
+/* run from kstop_machine */
+int __init ftrace_dyn_arch_init(void *data)
+{
+ ftrace_mcount_set(data);
+ return 0;
+}
diff --git a/arch/arm/kernel/kprobes.c b/arch/arm/kernel/kprobes.c
index 5593dd207216..5ee39e10c8d1 100644
--- a/arch/arm/kernel/kprobes.c
+++ b/arch/arm/kernel/kprobes.c
@@ -274,7 +274,7 @@ int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
* for kretprobe handlers which should normally be interested in r0 only
* anyway.
*/
-static void __attribute__((naked)) __kprobes kretprobe_trampoline(void)
+void __naked __kprobes kretprobe_trampoline(void)
{
__asm__ __volatile__ (
"stmdb sp!, {r0 - r11} \n\t"
diff --git a/arch/arm/mach-integrator/impd1.c b/arch/arm/mach-integrator/impd1.c
index 62e653a3ea1a..5a1588cf8242 100644
--- a/arch/arm/mach-integrator/impd1.c
+++ b/arch/arm/mach-integrator/impd1.c
@@ -393,9 +393,7 @@ static int impd1_probe(struct lm_device *dev)
if (!d)
continue;
- snprintf(d->dev.bus_id, sizeof(d->dev.bus_id),
- "lm%x:%5.5lx", dev->id, idev->offset >> 12);
-
+ dev_set_name(&d->dev, "lm%x:%5.5lx", dev->id, idev->offset >> 12);
d->dev.parent = &dev->dev;
d->res.start = dev->resource.start + idev->offset;
d->res.end = d->res.start + SZ_4K - 1;
@@ -407,8 +405,7 @@ static int impd1_probe(struct lm_device *dev)
ret = amba_device_register(d, &dev->resource);
if (ret) {
- printk("unable to register device %s: %d\n",
- d->dev.bus_id, ret);
+ dev_err(&d->dev, "unable to register device: %d\n");
kfree(d);
}
}
diff --git a/arch/arm/mach-integrator/lm.c b/arch/arm/mach-integrator/lm.c
index 622cdc4212dd..f939c5091405 100644
--- a/arch/arm/mach-integrator/lm.c
+++ b/arch/arm/mach-integrator/lm.c
@@ -81,8 +81,10 @@ int lm_device_register(struct lm_device *dev)
dev->dev.release = lm_device_release;
dev->dev.bus = &lm_bustype;
- snprintf(dev->dev.bus_id, sizeof(dev->dev.bus_id), "lm%d", dev->id);
- dev->resource.name = dev->dev.bus_id;
+ ret = dev_set_name(&dev->dev, "lm%d", dev->id);
+ if (ret)
+ return ret;
+ dev->resource.name = dev_name(&dev->dev);
ret = request_resource(&iomem_resource, &dev->resource);
if (ret == 0) {
diff --git a/arch/arm/mach-pxa/ssp.c b/arch/arm/mach-pxa/ssp.c
index 00af7f2fed66..0bb31982fb6f 100644
--- a/arch/arm/mach-pxa/ssp.c
+++ b/arch/arm/mach-pxa/ssp.c
@@ -330,7 +330,7 @@ struct ssp_device *ssp_request(int port, const char *label)
mutex_unlock(&ssp_lock);
- if (ssp->port_id != port)
+ if (&ssp->node == &ssp_list)
return NULL;
return ssp;
diff --git a/arch/arm/plat-omap/mailbox.c b/arch/arm/plat-omap/mailbox.c
index 6f33f58bca45..ff1413eae0b8 100644
--- a/arch/arm/plat-omap/mailbox.c
+++ b/arch/arm/plat-omap/mailbox.c
@@ -334,7 +334,7 @@ static int omap_mbox_init(struct omap_mbox *mbox)
}
mbox->dev.class = &omap_mbox_class;
- strlcpy(mbox->dev.bus_id, mbox->name, KOBJ_NAME_LEN);
+ dev_set_name(&mbox->dev, "%s", mbox->name);
dev_set_drvdata(&mbox->dev, mbox);
ret = device_register(&mbox->dev);
diff --git a/arch/avr32/boards/atngw100/setup.c b/arch/avr32/boards/atngw100/setup.c
index a398be284966..a51bb9fb3c89 100644
--- a/arch/avr32/boards/atngw100/setup.c
+++ b/arch/avr32/boards/atngw100/setup.c
@@ -9,6 +9,8 @@
*/
#include <linux/clk.h>
#include <linux/etherdevice.h>
+#include <linux/irq.h>
+#include <linux/i2c.h>
#include <linux/i2c-gpio.h>
#include <linux/init.h>
#include <linux/linkage.h>
@@ -25,6 +27,13 @@
#include <asm/arch/init.h>
#include <asm/arch/portmux.h>
+/* Oscillator frequencies. These are board-specific */
+unsigned long at32_board_osc_rates[3] = {
+ [0] = 32768, /* 32.768 kHz on RTC osc */
+ [1] = 20000000, /* 20 MHz on osc0 */
+ [2] = 12000000, /* 12 MHz on osc1 */
+};
+
/* Initialized by bootloader-specific startup code. */
struct tag *bootloader_tags __initdata;
@@ -140,6 +149,10 @@ static struct platform_device i2c_gpio_device = {
},
};
+static struct i2c_board_info __initdata i2c_info[] = {
+ /* NOTE: original ATtiny24 firmware is at address 0x0b */
+};
+
static int __init atngw100_init(void)
{
unsigned i;
@@ -165,12 +178,28 @@ static int __init atngw100_init(void)
}
platform_device_register(&ngw_gpio_leds);
+ /* all these i2c/smbus pins should have external pullups for
+ * open-drain sharing among all I2C devices. SDA and SCL do;
+ * PB28/EXTINT3 doesn't; it should be SMBALERT# (for PMBus),
+ * but it's not available off-board.
+ */
+ at32_select_periph(GPIO_PIN_PB(28), 0, AT32_GPIOF_PULLUP);
at32_select_gpio(i2c_gpio_data.sda_pin,
AT32_GPIOF_MULTIDRV | AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH);
at32_select_gpio(i2c_gpio_data.scl_pin,
AT32_GPIOF_MULTIDRV | AT32_GPIOF_OUTPUT | AT32_GPIOF_HIGH);
platform_device_register(&i2c_gpio_device);
+ i2c_register_board_info(0, i2c_info, ARRAY_SIZE(i2c_info));
return 0;
}
postcore_initcall(atngw100_init);
+
+static int __init atngw100_arch_init(void)
+{
+ /* set_irq_type() after the arch_initcall for EIC has run, and
+ * before the I2C subsystem could try using this IRQ.
+ */
+ return set_irq_type(AT32_EXTINT(3), IRQ_TYPE_EDGE_FALLING);
+}
+arch_initcall(atngw100_arch_init);
diff --git a/arch/avr32/boards/atstk1000/atstk1002.c b/arch/avr32/boards/atstk1000/atstk1002.c
index 000eb4220a12..86b363c1c25b 100644
--- a/arch/avr32/boards/atstk1000/atstk1002.c
+++ b/arch/avr32/boards/atstk1000/atstk1002.c
@@ -28,6 +28,12 @@
#include "atstk1000.h"
+/* Oscillator frequencies. These are board specific */
+unsigned long at32_board_osc_rates[3] = {
+ [0] = 32768, /* 32.768 kHz on RTC osc */
+ [1] = 20000000, /* 20 MHz on osc0 */
+ [2] = 12000000, /* 12 MHz on osc1 */
+};
struct eth_addr {
u8 addr[6];
@@ -232,7 +238,7 @@ static int __init atstk1002_init(void)
set_hw_addr(at32_add_device_eth(1, &eth_data[1]));
#else
at32_add_device_lcdc(0, &atstk1000_lcdc_data,
- fbmem_start, fbmem_size);
+ fbmem_start, fbmem_size, 0);
#endif
at32_add_device_usba(0, NULL);
#ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM
diff --git a/arch/avr32/boards/atstk1000/atstk1003.c b/arch/avr32/boards/atstk1000/atstk1003.c
index a0b223df35a2..ea109f435a83 100644
--- a/arch/avr32/boards/atstk1000/atstk1003.c
+++ b/arch/avr32/boards/atstk1000/atstk1003.c
@@ -27,6 +27,13 @@
#include "atstk1000.h"
+/* Oscillator frequencies. These are board specific */
+unsigned long at32_board_osc_rates[3] = {
+ [0] = 32768, /* 32.768 kHz on RTC osc */
+ [1] = 20000000, /* 20 MHz on osc0 */
+ [2] = 12000000, /* 12 MHz on osc1 */
+};
+
#ifdef CONFIG_BOARD_ATSTK1000_EXTDAC
static struct at73c213_board_info at73c213_data = {
.ssc_id = 0,
diff --git a/arch/avr32/boards/atstk1000/atstk1004.c b/arch/avr32/boards/atstk1000/atstk1004.c
index e765a8652b3e..c7236df74d74 100644
--- a/arch/avr32/boards/atstk1000/atstk1004.c
+++ b/arch/avr32/boards/atstk1000/atstk1004.c
@@ -29,6 +29,13 @@
#include "atstk1000.h"
+/* Oscillator frequencies. These are board specific */
+unsigned long at32_board_osc_rates[3] = {
+ [0] = 32768, /* 32.768 kHz on RTC osc */
+ [1] = 20000000, /* 20 MHz on osc0 */
+ [2] = 12000000, /* 12 MHz on osc1 */
+};
+
#ifdef CONFIG_BOARD_ATSTK1000_EXTDAC
static struct at73c213_board_info at73c213_data = {
.ssc_id = 0,
@@ -133,7 +140,7 @@ static int __init atstk1004_init(void)
at32_add_device_mci(0);
#endif
at32_add_device_lcdc(0, &atstk1000_lcdc_data,
- fbmem_start, fbmem_size);
+ fbmem_start, fbmem_size, 0);
at32_add_device_usba(0, NULL);
#ifndef CONFIG_BOARD_ATSTK100X_SW3_CUSTOM
at32_add_device_ssc(0, ATMEL_SSC_TX);
diff --git a/arch/avr32/kernel/signal.c b/arch/avr32/kernel/signal.c
index 5616a00c10ba..c5b11f9067f1 100644
--- a/arch/avr32/kernel/signal.c
+++ b/arch/avr32/kernel/signal.c
@@ -93,6 +93,9 @@ asmlinkage int sys_rt_sigreturn(struct pt_regs *regs)
if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
goto badframe;
+ if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->sp) == -EFAULT)
+ goto badframe;
+
pr_debug("Context restored: pc = %08lx, lr = %08lx, sp = %08lx\n",
regs->pc, regs->lr, regs->sp);
diff --git a/arch/avr32/kernel/vmlinux.lds.S b/arch/avr32/kernel/vmlinux.lds.S
index 481cfd40c053..bc932c9b4272 100644
--- a/arch/avr32/kernel/vmlinux.lds.S
+++ b/arch/avr32/kernel/vmlinux.lds.S
@@ -93,8 +93,6 @@ SECTIONS
__stop___ex_table = .;
}
- BUG_TABLE
-
RODATA
. = ALIGN(THREAD_SIZE);
diff --git a/arch/avr32/lib/io-readsb.S b/arch/avr32/lib/io-readsb.S
index 2be5da7ed26b..cb2d86945559 100644
--- a/arch/avr32/lib/io-readsb.S
+++ b/arch/avr32/lib/io-readsb.S
@@ -41,7 +41,7 @@ __raw_readsb:
2: sub r10, -4
reteq r12
-3: ld.uh r8, r12[0]
+3: ld.ub r8, r12[0]
sub r10, 1
st.b r11++, r8
brne 3b
diff --git a/arch/avr32/mach-at32ap/at32ap700x.c b/arch/avr32/mach-at32ap/at32ap700x.c
index 0f24b4f85c17..a9ae7bb108b2 100644
--- a/arch/avr32/mach-at32ap/at32ap700x.c
+++ b/arch/avr32/mach-at32ap/at32ap700x.c
@@ -93,19 +93,12 @@ static struct clk devname##_##_name = { \
static DEFINE_SPINLOCK(pm_lock);
-unsigned long at32ap7000_osc_rates[3] = {
- [0] = 32768,
- /* FIXME: these are ATSTK1002-specific */
- [1] = 20000000,
- [2] = 12000000,
-};
-
static struct clk osc0;
static struct clk osc1;
static unsigned long osc_get_rate(struct clk *clk)
{
- return at32ap7000_osc_rates[clk->index];
+ return at32_board_osc_rates[clk->index];
}
static unsigned long pll_get_rate(struct clk *clk, unsigned long control)
@@ -841,6 +834,81 @@ void __init at32_add_system_devices(void)
}
/* --------------------------------------------------------------------
+ * PSIF
+ * -------------------------------------------------------------------- */
+static struct resource atmel_psif0_resource[] __initdata = {
+ {
+ .start = 0xffe03c00,
+ .end = 0xffe03cff,
+ .flags = IORESOURCE_MEM,
+ },
+ IRQ(18),
+};
+static struct clk atmel_psif0_pclk = {
+ .name = "pclk",
+ .parent = &pba_clk,
+ .mode = pba_clk_mode,
+ .get_rate = pba_clk_get_rate,
+ .index = 15,
+};
+
+static struct resource atmel_psif1_resource[] __initdata = {
+ {
+ .start = 0xffe03d00,
+ .end = 0xffe03dff,
+ .flags = IORESOURCE_MEM,
+ },
+ IRQ(18),
+};
+static struct clk atmel_psif1_pclk = {
+ .name = "pclk",
+ .parent = &pba_clk,
+ .mode = pba_clk_mode,
+ .get_rate = pba_clk_get_rate,
+ .index = 15,
+};
+
+struct platform_device *__init at32_add_device_psif(unsigned int id)
+{
+ struct platform_device *pdev;
+
+ if (!(id == 0 || id == 1))
+ return NULL;
+
+ pdev = platform_device_alloc("atmel_psif", id);
+ if (!pdev)
+ return NULL;
+
+ switch (id) {
+ case 0:
+ if (platform_device_add_resources(pdev, atmel_psif0_resource,
+ ARRAY_SIZE(atmel_psif0_resource)))
+ goto err_add_resources;
+ atmel_psif0_pclk.dev = &pdev->dev;
+ select_peripheral(PA(8), PERIPH_A, 0); /* CLOCK */
+ select_peripheral(PA(9), PERIPH_A, 0); /* DATA */
+ break;
+ case 1:
+ if (platform_device_add_resources(pdev, atmel_psif1_resource,
+ ARRAY_SIZE(atmel_psif1_resource)))
+ goto err_add_resources;
+ atmel_psif1_pclk.dev = &pdev->dev;
+ select_peripheral(PB(11), PERIPH_A, 0); /* CLOCK */
+ select_peripheral(PB(12), PERIPH_A, 0); /* DATA */
+ break;
+ default:
+ return NULL;
+ }
+
+ platform_device_add(pdev);
+ return pdev;
+
+err_add_resources:
+ platform_device_put(pdev);
+ return NULL;
+}
+
+/* --------------------------------------------------------------------
* USART
* -------------------------------------------------------------------- */
@@ -1264,7 +1332,8 @@ static struct clk atmel_lcdfb0_pixclk = {
struct platform_device *__init
at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data,
- unsigned long fbmem_start, unsigned long fbmem_len)
+ unsigned long fbmem_start, unsigned long fbmem_len,
+ unsigned int pin_config)
{
struct platform_device *pdev;
struct atmel_lcdfb_info *info;
@@ -1291,37 +1360,77 @@ at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data,
switch (id) {
case 0:
pdev = &atmel_lcdfb0_device;
- select_peripheral(PC(19), PERIPH_A, 0); /* CC */
- select_peripheral(PC(20), PERIPH_A, 0); /* HSYNC */
- select_peripheral(PC(21), PERIPH_A, 0); /* PCLK */
- select_peripheral(PC(22), PERIPH_A, 0); /* VSYNC */
- select_peripheral(PC(23), PERIPH_A, 0); /* DVAL */
- select_peripheral(PC(24), PERIPH_A, 0); /* MODE */
- select_peripheral(PC(25), PERIPH_A, 0); /* PWR */
- select_peripheral(PC(26), PERIPH_A, 0); /* DATA0 */
- select_peripheral(PC(27), PERIPH_A, 0); /* DATA1 */
- select_peripheral(PC(28), PERIPH_A, 0); /* DATA2 */
- select_peripheral(PC(29), PERIPH_A, 0); /* DATA3 */
- select_peripheral(PC(30), PERIPH_A, 0); /* DATA4 */
- select_peripheral(PC(31), PERIPH_A, 0); /* DATA5 */
- select_peripheral(PD(0), PERIPH_A, 0); /* DATA6 */
- select_peripheral(PD(1), PERIPH_A, 0); /* DATA7 */
- select_peripheral(PD(2), PERIPH_A, 0); /* DATA8 */
- select_peripheral(PD(3), PERIPH_A, 0); /* DATA9 */
- select_peripheral(PD(4), PERIPH_A, 0); /* DATA10 */
- select_peripheral(PD(5), PERIPH_A, 0); /* DATA11 */
- select_peripheral(PD(6), PERIPH_A, 0); /* DATA12 */
- select_peripheral(PD(7), PERIPH_A, 0); /* DATA13 */
- select_peripheral(PD(8), PERIPH_A, 0); /* DATA14 */
- select_peripheral(PD(9), PERIPH_A, 0); /* DATA15 */
- select_peripheral(PD(10), PERIPH_A, 0); /* DATA16 */
- select_peripheral(PD(11), PERIPH_A, 0); /* DATA17 */
- select_peripheral(PD(12), PERIPH_A, 0); /* DATA18 */
- select_peripheral(PD(13), PERIPH_A, 0); /* DATA19 */
- select_peripheral(PD(14), PERIPH_A, 0); /* DATA20 */
- select_peripheral(PD(15), PERIPH_A, 0); /* DATA21 */
- select_peripheral(PD(16), PERIPH_A, 0); /* DATA22 */
- select_peripheral(PD(17), PERIPH_A, 0); /* DATA23 */
+
+ switch (pin_config) {
+ case 0:
+ select_peripheral(PC(19), PERIPH_A, 0); /* CC */
+ select_peripheral(PC(20), PERIPH_A, 0); /* HSYNC */
+ select_peripheral(PC(21), PERIPH_A, 0); /* PCLK */
+ select_peripheral(PC(22), PERIPH_A, 0); /* VSYNC */
+ select_peripheral(PC(23), PERIPH_A, 0); /* DVAL */
+ select_peripheral(PC(24), PERIPH_A, 0); /* MODE */
+ select_peripheral(PC(25), PERIPH_A, 0); /* PWR */
+ select_peripheral(PC(26), PERIPH_A, 0); /* DATA0 */
+ select_peripheral(PC(27), PERIPH_A, 0); /* DATA1 */
+ select_peripheral(PC(28), PERIPH_A, 0); /* DATA2 */
+ select_peripheral(PC(29), PERIPH_A, 0); /* DATA3 */
+ select_peripheral(PC(30), PERIPH_A, 0); /* DATA4 */
+ select_peripheral(PC(31), PERIPH_A, 0); /* DATA5 */
+ select_peripheral(PD(0), PERIPH_A, 0); /* DATA6 */
+ select_peripheral(PD(1), PERIPH_A, 0); /* DATA7 */
+ select_peripheral(PD(2), PERIPH_A, 0); /* DATA8 */
+ select_peripheral(PD(3), PERIPH_A, 0); /* DATA9 */
+ select_peripheral(PD(4), PERIPH_A, 0); /* DATA10 */
+ select_peripheral(PD(5), PERIPH_A, 0); /* DATA11 */
+ select_peripheral(PD(6), PERIPH_A, 0); /* DATA12 */
+ select_peripheral(PD(7), PERIPH_A, 0); /* DATA13 */
+ select_peripheral(PD(8), PERIPH_A, 0); /* DATA14 */
+ select_peripheral(PD(9), PERIPH_A, 0); /* DATA15 */
+ select_peripheral(PD(10), PERIPH_A, 0); /* DATA16 */
+ select_peripheral(PD(11), PERIPH_A, 0); /* DATA17 */
+ select_peripheral(PD(12), PERIPH_A, 0); /* DATA18 */
+ select_peripheral(PD(13), PERIPH_A, 0); /* DATA19 */
+ select_peripheral(PD(14), PERIPH_A, 0); /* DATA20 */
+ select_peripheral(PD(15), PERIPH_A, 0); /* DATA21 */
+ select_peripheral(PD(16), PERIPH_A, 0); /* DATA22 */
+ select_peripheral(PD(17), PERIPH_A, 0); /* DATA23 */
+ break;
+ case 1:
+ select_peripheral(PE(0), PERIPH_B, 0); /* CC */
+ select_peripheral(PC(20), PERIPH_A, 0); /* HSYNC */
+ select_peripheral(PC(21), PERIPH_A, 0); /* PCLK */
+ select_peripheral(PC(22), PERIPH_A, 0); /* VSYNC */
+ select_peripheral(PE(1), PERIPH_B, 0); /* DVAL */
+ select_peripheral(PE(2), PERIPH_B, 0); /* MODE */
+ select_peripheral(PC(25), PERIPH_A, 0); /* PWR */
+ select_peripheral(PE(3), PERIPH_B, 0); /* DATA0 */
+ select_peripheral(PE(4), PERIPH_B, 0); /* DATA1 */
+ select_peripheral(PE(5), PERIPH_B, 0); /* DATA2 */
+ select_peripheral(PE(6), PERIPH_B, 0); /* DATA3 */
+ select_peripheral(PE(7), PERIPH_B, 0); /* DATA4 */
+ select_peripheral(PC(31), PERIPH_A, 0); /* DATA5 */
+ select_peripheral(PD(0), PERIPH_A, 0); /* DATA6 */
+ select_peripheral(PD(1), PERIPH_A, 0); /* DATA7 */
+ select_peripheral(PE(8), PERIPH_B, 0); /* DATA8 */
+ select_peripheral(PE(9), PERIPH_B, 0); /* DATA9 */
+ select_peripheral(PE(10), PERIPH_B, 0); /* DATA10 */
+ select_peripheral(PE(11), PERIPH_B, 0); /* DATA11 */
+ select_peripheral(PE(12), PERIPH_B, 0); /* DATA12 */
+ select_peripheral(PD(7), PERIPH_A, 0); /* DATA13 */
+ select_peripheral(PD(8), PERIPH_A, 0); /* DATA14 */
+ select_peripheral(PD(9), PERIPH_A, 0); /* DATA15 */
+ select_peripheral(PE(13), PERIPH_B, 0); /* DATA16 */
+ select_peripheral(PE(14), PERIPH_B, 0); /* DATA17 */
+ select_peripheral(PE(15), PERIPH_B, 0); /* DATA18 */
+ select_peripheral(PE(16), PERIPH_B, 0); /* DATA19 */
+ select_peripheral(PE(17), PERIPH_B, 0); /* DATA20 */
+ select_peripheral(PE(18), PERIPH_B, 0); /* DATA21 */
+ select_peripheral(PD(16), PERIPH_A, 0); /* DATA22 */
+ select_peripheral(PD(17), PERIPH_A, 0); /* DATA23 */
+ break;
+ default:
+ goto err_invalid_id;
+ }
clk_set_parent(&atmel_lcdfb0_pixclk, &pll0);
clk_set_rate(&atmel_lcdfb0_pixclk, clk_get_rate(&pll0));
@@ -1900,6 +2009,8 @@ struct clk *at32_clock_list[] = {
&pio4_mck,
&at32_tcb0_t0_clk,
&at32_tcb1_t0_clk,
+ &atmel_psif0_pclk,
+ &atmel_psif1_pclk,
&atmel_usart0_usart,
&atmel_usart1_usart,
&atmel_usart2_usart,
diff --git a/arch/avr32/mach-at32ap/pio.c b/arch/avr32/mach-at32ap/pio.c
index 38a8fa31c0b5..60da03ba7117 100644
--- a/arch/avr32/mach-at32ap/pio.c
+++ b/arch/avr32/mach-at32ap/pio.c
@@ -318,6 +318,8 @@ static void pio_bank_show(struct seq_file *s, struct gpio_chip *chip)
const char *label;
label = gpiochip_is_requested(chip, i);
+ if (!label && (imr & mask))
+ label = "[irq]";
if (!label)
continue;
diff --git a/arch/avr32/mm/init.c b/arch/avr32/mm/init.c
index 0e64ddc45e37..5ee1e407dcf7 100644
--- a/arch/avr32/mm/init.c
+++ b/arch/avr32/mm/init.c
@@ -11,6 +11,7 @@
#include <linux/swap.h>
#include <linux/init.h>
#include <linux/mmzone.h>
+#include <linux/module.h>
#include <linux/bootmem.h>
#include <linux/pagemap.h>
#include <linux/nodemask.h>
@@ -28,6 +29,7 @@ DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
pgd_t swapper_pg_dir[PTRS_PER_PGD];
struct page *empty_zero_page;
+EXPORT_SYMBOL(empty_zero_page);
/*
* Cache of MMU context last used.
diff --git a/arch/ia64/Makefile b/arch/ia64/Makefile
index 88f1a55c6c94..3b9c8cadfd34 100644
--- a/arch/ia64/Makefile
+++ b/arch/ia64/Makefile
@@ -99,3 +99,9 @@ define archhelp
echo ' boot - Build vmlinux and bootloader for Ski simulator'
echo '* unwcheck - Check vmlinux for invalid unwind info'
endef
+
+archprepare: make_nr_irqs_h FORCE
+PHONY += make_nr_irqs_h FORCE
+
+make_nr_irqs_h: FORCE
+ $(Q)$(MAKE) $(build)=arch/ia64/kernel include/asm-ia64/nr-irqs.h
diff --git a/arch/ia64/kernel/Makefile b/arch/ia64/kernel/Makefile
index 13fd10e8699e..87fea11aecb7 100644
--- a/arch/ia64/kernel/Makefile
+++ b/arch/ia64/kernel/Makefile
@@ -36,6 +36,8 @@ obj-$(CONFIG_PCI_MSI) += msi_ia64.o
mca_recovery-y += mca_drv.o mca_drv_asm.o
obj-$(CONFIG_IA64_MC_ERR_INJECT)+= err_inject.o
+obj-$(CONFIG_PARAVIRT) += paravirt.o paravirtentry.o
+
obj-$(CONFIG_IA64_ESI) += esi.o
ifneq ($(CONFIG_IA64_ESI),)
obj-y += esi_stub.o # must be in kernel proper
@@ -70,3 +72,45 @@ $(obj)/gate-syms.o: $(obj)/gate.lds $(obj)/gate.o FORCE
# We must build gate.so before we can assemble it.
# Note: kbuild does not track this dependency due to usage of .incbin
$(obj)/gate-data.o: $(obj)/gate.so
+
+# Calculate NR_IRQ = max(IA64_NATIVE_NR_IRQS, XEN_NR_IRQS, ...) based on config
+define sed-y
+ "/^->/{s:^->\([^ ]*\) [\$$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:; s:->::; p;}"
+endef
+quiet_cmd_nr_irqs = GEN $@
+define cmd_nr_irqs
+ (set -e; \
+ echo "#ifndef __ASM_NR_IRQS_H__"; \
+ echo "#define __ASM_NR_IRQS_H__"; \
+ echo "/*"; \
+ echo " * DO NOT MODIFY."; \
+ echo " *"; \
+ echo " * This file was generated by Kbuild"; \
+ echo " *"; \
+ echo " */"; \
+ echo ""; \
+ sed -ne $(sed-y) $<; \
+ echo ""; \
+ echo "#endif" ) > $@
+endef
+
+# We use internal kbuild rules to avoid the "is up to date" message from make
+arch/$(SRCARCH)/kernel/nr-irqs.s: $(srctree)/arch/$(SRCARCH)/kernel/nr-irqs.c \
+ $(wildcard $(srctree)/include/asm-ia64/*/irq.h)
+ $(Q)mkdir -p $(dir $@)
+ $(call if_changed_dep,cc_s_c)
+
+include/asm-ia64/nr-irqs.h: arch/$(SRCARCH)/kernel/nr-irqs.s
+ $(Q)mkdir -p $(dir $@)
+ $(call cmd,nr_irqs)
+
+clean-files += $(objtree)/include/asm-ia64/nr-irqs.h
+
+#
+# native ivt.S and entry.S
+#
+ASM_PARAVIRT_OBJS = ivt.o entry.o
+define paravirtualized_native
+AFLAGS_$(1) += -D__IA64_ASM_PARAVIRTUALIZED_NATIVE
+endef
+$(foreach obj,$(ASM_PARAVIRT_OBJS),$(eval $(call paravirtualized_native,$(obj))))
diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c
index 853d1f11be00..43687cc60dfb 100644
--- a/arch/ia64/kernel/acpi.c
+++ b/arch/ia64/kernel/acpi.c
@@ -465,7 +465,6 @@ void __init acpi_numa_slit_init(struct acpi_table_slit *slit)
printk(KERN_ERR
"ACPI 2.0 SLIT: size mismatch: %d expected, %d actual\n",
len, slit->header.length);
- memset(numa_slit, 10, sizeof(numa_slit));
return;
}
slit_table = slit;
@@ -574,8 +573,14 @@ void __init acpi_numa_arch_fixup(void)
printk(KERN_INFO "Number of memory chunks in system = %d\n",
num_node_memblks);
- if (!slit_table)
+ if (!slit_table) {
+ for (i = 0; i < MAX_NUMNODES; i++)
+ for (j = 0; j < MAX_NUMNODES; j++)
+ node_distance(i, j) = i == j ? LOCAL_DISTANCE :
+ REMOTE_DISTANCE;
return;
+ }
+
memset(numa_slit, -1, sizeof(numa_slit));
for (i = 0; i < slit_table->locality_count; i++) {
if (!pxm_bit_test(i))
diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S
index ca2bb95726de..56ab156c48ae 100644
--- a/arch/ia64/kernel/entry.S
+++ b/arch/ia64/kernel/entry.S
@@ -23,6 +23,11 @@
* 11/07/2000
*/
/*
+ * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ * pv_ops.
+ */
+/*
* Global (preserved) predicate usage on syscall entry/exit path:
*
* pKStk: See entry.h.
@@ -45,6 +50,7 @@
#include "minstate.h"
+#ifdef __IA64_ASM_PARAVIRTUALIZED_NATIVE
/*
* execve() is special because in case of success, we need to
* setup a null register window frame.
@@ -173,6 +179,7 @@ GLOBAL_ENTRY(sys_clone)
mov rp=loc0
br.ret.sptk.many rp
END(sys_clone)
+#endif /* __IA64_ASM_PARAVIRTUALIZED_NATIVE */
/*
* prev_task <- ia64_switch_to(struct task_struct *next)
@@ -180,7 +187,7 @@ END(sys_clone)
* called. The code starting at .map relies on this. The rest of the code
* doesn't care about the interrupt masking status.
*/
-GLOBAL_ENTRY(ia64_switch_to)
+GLOBAL_ENTRY(__paravirt_switch_to)
.prologue
alloc r16=ar.pfs,1,0,0,0
DO_SAVE_SWITCH_STACK
@@ -204,7 +211,7 @@ GLOBAL_ENTRY(ia64_switch_to)
;;
.done:
ld8 sp=[r21] // load kernel stack pointer of new task
- mov IA64_KR(CURRENT)=in0 // update "current" application register
+ MOV_TO_KR(CURRENT, in0, r8, r9) // update "current" application register
mov r8=r13 // return pointer to previously running task
mov r13=in0 // set "current" pointer
;;
@@ -216,26 +223,25 @@ GLOBAL_ENTRY(ia64_switch_to)
br.ret.sptk.many rp // boogie on out in new context
.map:
- rsm psr.ic // interrupts (psr.i) are already disabled here
+ RSM_PSR_IC(r25) // interrupts (psr.i) are already disabled here
movl r25=PAGE_KERNEL
;;
srlz.d
or r23=r25,r20 // construct PA | page properties
mov r25=IA64_GRANULE_SHIFT<<2
;;
- mov cr.itir=r25
- mov cr.ifa=in0 // VA of next task...
+ MOV_TO_ITIR(p0, r25, r8)
+ MOV_TO_IFA(in0, r8) // VA of next task...
;;
mov r25=IA64_TR_CURRENT_STACK
- mov IA64_KR(CURRENT_STACK)=r26 // remember last page we mapped...
+ MOV_TO_KR(CURRENT_STACK, r26, r8, r9) // remember last page we mapped...
;;
itr.d dtr[r25]=r23 // wire in new mapping...
- ssm psr.ic // reenable the psr.ic bit
- ;;
- srlz.d
+ SSM_PSR_IC_AND_SRLZ_D(r8, r9) // reenable the psr.ic bit
br.cond.sptk .done
-END(ia64_switch_to)
+END(__paravirt_switch_to)
+#ifdef __IA64_ASM_PARAVIRTUALIZED_NATIVE
/*
* Note that interrupts are enabled during save_switch_stack and load_switch_stack. This
* means that we may get an interrupt with "sp" pointing to the new kernel stack while
@@ -375,7 +381,7 @@ END(save_switch_stack)
* - b7 holds address to return to
* - must not touch r8-r11
*/
-ENTRY(load_switch_stack)
+GLOBAL_ENTRY(load_switch_stack)
.prologue
.altrp b7
@@ -571,7 +577,7 @@ GLOBAL_ENTRY(ia64_trace_syscall)
.ret3:
(pUStk) cmp.eq.unc p6,p0=r0,r0 // p6 <- pUStk
(pUStk) rsm psr.i // disable interrupts
- br.cond.sptk .work_pending_syscall_end
+ br.cond.sptk ia64_work_pending_syscall_end
strace_error:
ld8 r3=[r2] // load pt_regs.r8
@@ -636,8 +642,17 @@ GLOBAL_ENTRY(ia64_ret_from_syscall)
adds r2=PT(R8)+16,sp // r2 = &pt_regs.r8
mov r10=r0 // clear error indication in r10
(p7) br.cond.spnt handle_syscall_error // handle potential syscall failure
+#ifdef CONFIG_PARAVIRT
+ ;;
+ br.cond.sptk.few ia64_leave_syscall
+ ;;
+#endif /* CONFIG_PARAVIRT */
END(ia64_ret_from_syscall)
+#ifndef CONFIG_PARAVIRT
// fall through
+#endif
+#endif /* __IA64_ASM_PARAVIRTUALIZED_NATIVE */
+
/*
* ia64_leave_syscall(): Same as ia64_leave_kernel, except that it doesn't
* need to switch to bank 0 and doesn't restore the scratch registers.
@@ -682,7 +697,7 @@ END(ia64_ret_from_syscall)
* ar.csd: cleared
* ar.ssd: cleared
*/
-ENTRY(ia64_leave_syscall)
+GLOBAL_ENTRY(__paravirt_leave_syscall)
PT_REGS_UNWIND_INFO(0)
/*
* work.need_resched etc. mustn't get changed by this CPU before it returns to
@@ -692,11 +707,11 @@ ENTRY(ia64_leave_syscall)
* extra work. We always check for extra work when returning to user-level.
* With CONFIG_PREEMPT, we also check for extra work when the preempt_count
* is 0. After extra work processing has been completed, execution
- * resumes at .work_processed_syscall with p6 set to 1 if the extra-work-check
+ * resumes at ia64_work_processed_syscall with p6 set to 1 if the extra-work-check
* needs to be redone.
*/
#ifdef CONFIG_PREEMPT
- rsm psr.i // disable interrupts
+ RSM_PSR_I(p0, r2, r18) // disable interrupts
cmp.eq pLvSys,p0=r0,r0 // pLvSys=1: leave from syscall
(pKStk) adds r20=TI_PRE_COUNT+IA64_TASK_SIZE,r13
;;
@@ -706,11 +721,12 @@ ENTRY(ia64_leave_syscall)
;;
cmp.eq p6,p0=r21,r0 // p6 <- pUStk || (preempt_count == 0)
#else /* !CONFIG_PREEMPT */
-(pUStk) rsm psr.i
+ RSM_PSR_I(pUStk, r2, r18)
cmp.eq pLvSys,p0=r0,r0 // pLvSys=1: leave from syscall
(pUStk) cmp.eq.unc p6,p0=r0,r0 // p6 <- pUStk
#endif
-.work_processed_syscall:
+.global __paravirt_work_processed_syscall;
+__paravirt_work_processed_syscall:
#ifdef CONFIG_VIRT_CPU_ACCOUNTING
adds r2=PT(LOADRS)+16,r12
(pUStk) mov.m r22=ar.itc // fetch time at leave
@@ -744,7 +760,7 @@ ENTRY(ia64_leave_syscall)
(pNonSys) break 0 // bug check: we shouldn't be here if pNonSys is TRUE!
;;
invala // M0|1 invalidate ALAT
- rsm psr.i | psr.ic // M2 turn off interrupts and interruption collection
+ RSM_PSR_I_IC(r28, r29, r30) // M2 turn off interrupts and interruption collection
cmp.eq p9,p0=r0,r0 // A set p9 to indicate that we should restore cr.ifs
ld8 r29=[r2],16 // M0|1 load cr.ipsr
@@ -765,7 +781,7 @@ ENTRY(ia64_leave_syscall)
;;
#endif
ld8 r26=[r2],PT(B0)-PT(AR_PFS) // M0|1 load ar.pfs
-(pKStk) mov r22=psr // M2 read PSR now that interrupts are disabled
+ MOV_FROM_PSR(pKStk, r22, r21) // M2 read PSR now that interrupts are disabled
nop 0
;;
ld8 r21=[r2],PT(AR_RNAT)-PT(B0) // M0|1 load b0
@@ -798,7 +814,7 @@ ENTRY(ia64_leave_syscall)
srlz.d // M0 ensure interruption collection is off (for cover)
shr.u r18=r19,16 // I0|1 get byte size of existing "dirty" partition
- cover // B add current frame into dirty partition & set cr.ifs
+ COVER // B add current frame into dirty partition & set cr.ifs
;;
#ifdef CONFIG_VIRT_CPU_ACCOUNTING
mov r19=ar.bsp // M2 get new backing store pointer
@@ -823,8 +839,9 @@ ENTRY(ia64_leave_syscall)
mov.m ar.ssd=r0 // M2 clear ar.ssd
mov f11=f0 // F clear f11
br.cond.sptk.many rbs_switch // B
-END(ia64_leave_syscall)
+END(__paravirt_leave_syscall)
+#ifdef __IA64_ASM_PARAVIRTUALIZED_NATIVE
#ifdef CONFIG_IA32_SUPPORT
GLOBAL_ENTRY(ia64_ret_from_ia32_execve)
PT_REGS_UNWIND_INFO(0)
@@ -835,10 +852,20 @@ GLOBAL_ENTRY(ia64_ret_from_ia32_execve)
st8.spill [r2]=r8 // store return value in slot for r8 and set unat bit
.mem.offset 8,0
st8.spill [r3]=r0 // clear error indication in slot for r10 and set unat bit
+#ifdef CONFIG_PARAVIRT
+ ;;
+ // don't fall through, ia64_leave_kernel may be #define'd
+ br.cond.sptk.few ia64_leave_kernel
+ ;;
+#endif /* CONFIG_PARAVIRT */
END(ia64_ret_from_ia32_execve)
+#ifndef CONFIG_PARAVIRT
// fall through
+#endif
#endif /* CONFIG_IA32_SUPPORT */
-GLOBAL_ENTRY(ia64_leave_kernel)
+#endif /* __IA64_ASM_PARAVIRTUALIZED_NATIVE */
+
+GLOBAL_ENTRY(__paravirt_leave_kernel)
PT_REGS_UNWIND_INFO(0)
/*
* work.need_resched etc. mustn't get changed by this CPU before it returns to
@@ -852,7 +879,7 @@ GLOBAL_ENTRY(ia64_leave_kernel)
* needs to be redone.
*/
#ifdef CONFIG_PREEMPT
- rsm psr.i // disable interrupts
+ RSM_PSR_I(p0, r17, r31) // disable interrupts
cmp.eq p0,pLvSys=r0,r0 // pLvSys=0: leave from kernel
(pKStk) adds r20=TI_PRE_COUNT+IA64_TASK_SIZE,r13
;;
@@ -862,7 +889,7 @@ GLOBAL_ENTRY(ia64_leave_kernel)
;;
cmp.eq p6,p0=r21,r0 // p6 <- pUStk || (preempt_count == 0)
#else
-(pUStk) rsm psr.i
+ RSM_PSR_I(pUStk, r17, r31)
cmp.eq p0,pLvSys=r0,r0 // pLvSys=0: leave from kernel
(pUStk) cmp.eq.unc p6,p0=r0,r0 // p6 <- pUStk
#endif
@@ -910,7 +937,7 @@ GLOBAL_ENTRY(ia64_leave_kernel)
mov ar.csd=r30
mov ar.ssd=r31
;;
- rsm psr.i | psr.ic // initiate turning off of interrupt and interruption collection
+ RSM_PSR_I_IC(r23, r22, r25) // initiate turning off of interrupt and interruption collection
invala // invalidate ALAT
;;
ld8.fill r22=[r2],24
@@ -942,7 +969,7 @@ GLOBAL_ENTRY(ia64_leave_kernel)
mov ar.ccv=r15
;;
ldf.fill f11=[r2]
- bsw.0 // switch back to bank 0 (no stop bit required beforehand...)
+ BSW_0(r2, r3, r15) // switch back to bank 0 (no stop bit required beforehand...)
;;
(pUStk) mov r18=IA64_KR(CURRENT)// M2 (12 cycle read latency)
adds r16=PT(CR_IPSR)+16,r12
@@ -950,12 +977,12 @@ GLOBAL_ENTRY(ia64_leave_kernel)
#ifdef CONFIG_VIRT_CPU_ACCOUNTING
.pred.rel.mutex pUStk,pKStk
-(pKStk) mov r22=psr // M2 read PSR now that interrupts are disabled
+ MOV_FROM_PSR(pKStk, r22, r29) // M2 read PSR now that interrupts are disabled
(pUStk) mov.m r22=ar.itc // M fetch time at leave
nop.i 0
;;
#else
-(pKStk) mov r22=psr // M2 read PSR now that interrupts are disabled
+ MOV_FROM_PSR(pKStk, r22, r29) // M2 read PSR now that interrupts are disabled
nop.i 0
nop.i 0
;;
@@ -1027,7 +1054,7 @@ GLOBAL_ENTRY(ia64_leave_kernel)
* NOTE: alloc, loadrs, and cover can't be predicated.
*/
(pNonSys) br.cond.dpnt dont_preserve_current_frame
- cover // add current frame into dirty partition and set cr.ifs
+ COVER // add current frame into dirty partition and set cr.ifs
;;
mov r19=ar.bsp // get new backing store pointer
rbs_switch:
@@ -1130,16 +1157,16 @@ skip_rbs_switch:
(pKStk) dep r29=r22,r29,21,1 // I0 update ipsr.pp with psr.pp
(pLvSys)mov r16=r0 // A clear r16 for leave_syscall, no-op otherwise
;;
- mov cr.ipsr=r29 // M2
+ MOV_TO_IPSR(p0, r29, r25) // M2
mov ar.pfs=r26 // I0
(pLvSys)mov r17=r0 // A clear r17 for leave_syscall, no-op otherwise
-(p9) mov cr.ifs=r30 // M2
+ MOV_TO_IFS(p9, r30, r25)// M2
mov b0=r21 // I0
(pLvSys)mov r18=r0 // A clear r18 for leave_syscall, no-op otherwise
mov ar.fpsr=r20 // M2
- mov cr.iip=r28 // M2
+ MOV_TO_IIP(r28, r25) // M2
nop 0
;;
(pUStk) mov ar.rnat=r24 // M2 must happen with RSE in lazy mode
@@ -1148,7 +1175,7 @@ skip_rbs_switch:
mov ar.rsc=r27 // M2
mov pr=r31,-1 // I0
- rfi // B
+ RFI // B
/*
* On entry:
@@ -1174,35 +1201,36 @@ skip_rbs_switch:
;;
(pKStk) st4 [r20]=r21
#endif
- ssm psr.i // enable interrupts
+ SSM_PSR_I(p0, p6, r2) // enable interrupts
br.call.spnt.many rp=schedule
.ret9: cmp.eq p6,p0=r0,r0 // p6 <- 1 (re-check)
- rsm psr.i // disable interrupts
+ RSM_PSR_I(p0, r2, r20) // disable interrupts
;;
#ifdef CONFIG_PREEMPT
(pKStk) adds r20=TI_PRE_COUNT+IA64_TASK_SIZE,r13
;;
(pKStk) st4 [r20]=r0 // preempt_count() <- 0
#endif
-(pLvSys)br.cond.sptk.few .work_pending_syscall_end
+(pLvSys)br.cond.sptk.few __paravirt_pending_syscall_end
br.cond.sptk.many .work_processed_kernel
.notify:
(pUStk) br.call.spnt.many rp=notify_resume_user
.ret10: cmp.ne p6,p0=r0,r0 // p6 <- 0 (don't re-check)
-(pLvSys)br.cond.sptk.few .work_pending_syscall_end
+(pLvSys)br.cond.sptk.few __paravirt_pending_syscall_end
br.cond.sptk.many .work_processed_kernel
-.work_pending_syscall_end:
+.global __paravirt_pending_syscall_end;
+__paravirt_pending_syscall_end:
adds r2=PT(R8)+16,r12
adds r3=PT(R10)+16,r12
;;
ld8 r8=[r2]
ld8 r10=[r3]
- br.cond.sptk.many .work_processed_syscall
-
-END(ia64_leave_kernel)
+ br.cond.sptk.many __paravirt_work_processed_syscall_target
+END(__paravirt_leave_kernel)
+#ifdef __IA64_ASM_PARAVIRTUALIZED_NATIVE
ENTRY(handle_syscall_error)
/*
* Some system calls (e.g., ptrace, mmap) can return arbitrary values which could
@@ -1244,7 +1272,7 @@ END(ia64_invoke_schedule_tail)
* We declare 8 input registers so the system call args get preserved,
* in case we need to restart a system call.
*/
-ENTRY(notify_resume_user)
+GLOBAL_ENTRY(notify_resume_user)
.prologue ASM_UNW_PRLG_RP|ASM_UNW_PRLG_PFS, ASM_UNW_PRLG_GRSAVE(8)
alloc loc1=ar.pfs,8,2,3,0 // preserve all eight input regs in case of syscall restart!
mov r9=ar.unat
@@ -1306,7 +1334,7 @@ ENTRY(sys_rt_sigreturn)
adds sp=16,sp
;;
ld8 r9=[sp] // load new ar.unat
- mov.sptk b7=r8,ia64_leave_kernel
+ mov.sptk b7=r8,ia64_native_leave_kernel
;;
mov ar.unat=r9
br.many b7
@@ -1665,3 +1693,4 @@ sys_call_table:
data8 sys_timerfd_gettime
.org sys_call_table + 8*NR_syscalls // guard against failures to increase NR_syscalls
+#endif /* __IA64_ASM_PARAVIRTUALIZED_NATIVE */
diff --git a/arch/ia64/kernel/head.S b/arch/ia64/kernel/head.S
index ddeab4e36fd5..db540e58c783 100644
--- a/arch/ia64/kernel/head.S
+++ b/arch/ia64/kernel/head.S
@@ -26,11 +26,14 @@
#include <asm/mmu_context.h>
#include <asm/asm-offsets.h>
#include <asm/pal.h>
+#include <asm/paravirt.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <asm/ptrace.h>
#include <asm/system.h>
#include <asm/mca_asm.h>
+#include <linux/init.h>
+#include <linux/linkage.h>
#ifdef CONFIG_HOTPLUG_CPU
#define SAL_PSR_BITS_TO_SET \
@@ -367,6 +370,44 @@ start_ap:
;;
(isBP) st8 [r2]=r28 // save the address of the boot param area passed by the bootloader
+#ifdef CONFIG_PARAVIRT
+
+ movl r14=hypervisor_setup_hooks
+ movl r15=hypervisor_type
+ mov r16=num_hypervisor_hooks
+ ;;
+ ld8 r2=[r15]
+ ;;
+ cmp.ltu p7,p0=r2,r16 // array size check
+ shladd r8=r2,3,r14
+ ;;
+(p7) ld8 r9=[r8]
+ ;;
+(p7) mov b1=r9
+(p7) cmp.ne.unc p7,p0=r9,r0 // no actual branch to NULL
+ ;;
+(p7) br.call.sptk.many rp=b1
+
+ __INITDATA
+
+default_setup_hook = 0 // Currently nothing needs to be done.
+
+ .weak xen_setup_hook
+
+ .global hypervisor_type
+hypervisor_type:
+ data8 PARAVIRT_HYPERVISOR_TYPE_DEFAULT
+
+ // must have the same order with PARAVIRT_HYPERVISOR_TYPE_xxx
+
+hypervisor_setup_hooks:
+ data8 default_setup_hook
+ data8 xen_setup_hook
+num_hypervisor_hooks = (. - hypervisor_setup_hooks) / 8
+ .previous
+
+#endif
+
#ifdef CONFIG_SMP
(isAP) br.call.sptk.many rp=start_secondary
.ret0:
diff --git a/arch/ia64/kernel/iosapic.c b/arch/ia64/kernel/iosapic.c
index 082c31dcfd99..587196dd84fd 100644
--- a/arch/ia64/kernel/iosapic.c
+++ b/arch/ia64/kernel/iosapic.c
@@ -587,6 +587,15 @@ static inline int irq_is_shared (int irq)
return (iosapic_intr_info[irq].count > 1);
}
+struct irq_chip*
+ia64_native_iosapic_get_irq_chip(unsigned long trigger)
+{
+ if (trigger == IOSAPIC_EDGE)
+ return &irq_type_iosapic_edge;
+ else
+ return &irq_type_iosapic_level;
+}
+
static int
register_intr (unsigned int gsi, int irq, unsigned char delivery,
unsigned long polarity, unsigned long trigger)
@@ -637,13 +646,10 @@ register_intr (unsigned int gsi, int irq, unsigned char delivery,
iosapic_intr_info[irq].dmode = delivery;
iosapic_intr_info[irq].trigger = trigger;
- if (trigger == IOSAPIC_EDGE)
- irq_type = &irq_type_iosapic_edge;
- else
- irq_type = &irq_type_iosapic_level;
+ irq_type = iosapic_get_irq_chip(trigger);
idesc = irq_desc + irq;
- if (idesc->chip != irq_type) {
+ if (irq_type != NULL && idesc->chip != irq_type) {
if (idesc->chip != &no_irq_type)
printk(KERN_WARNING
"%s: changing vector %d from %s to %s\n",
@@ -976,6 +982,22 @@ iosapic_override_isa_irq (unsigned int isa_irq, unsigned int gsi,
}
void __init
+ia64_native_iosapic_pcat_compat_init(void)
+{
+ if (pcat_compat) {
+ /*
+ * Disable the compatibility mode interrupts (8259 style),
+ * needs IN/OUT support enabled.
+ */
+ printk(KERN_INFO
+ "%s: Disabling PC-AT compatible 8259 interrupts\n",
+ __func__);
+ outb(0xff, 0xA1);
+ outb(0xff, 0x21);
+ }
+}
+
+void __init
iosapic_system_init (int system_pcat_compat)
{
int irq;
@@ -989,17 +1011,8 @@ iosapic_system_init (int system_pcat_compat)
}
pcat_compat = system_pcat_compat;
- if (pcat_compat) {
- /*
- * Disable the compatibility mode interrupts (8259 style),
- * needs IN/OUT support enabled.
- */
- printk(KERN_INFO
- "%s: Disabling PC-AT compatible 8259 interrupts\n",
- __func__);
- outb(0xff, 0xA1);
- outb(0xff, 0x21);
- }
+ if (pcat_compat)
+ iosapic_pcat_compat_init();
}
static inline int
diff --git a/arch/ia64/kernel/irq_ia64.c b/arch/ia64/kernel/irq_ia64.c
index 5538471e8d68..28d3d483db92 100644
--- a/arch/ia64/kernel/irq_ia64.c
+++ b/arch/ia64/kernel/irq_ia64.c
@@ -196,7 +196,7 @@ static void clear_irq_vector(int irq)
}
int
-assign_irq_vector (int irq)
+ia64_native_assign_irq_vector (int irq)
{
unsigned long flags;
int vector, cpu;
@@ -222,7 +222,7 @@ assign_irq_vector (int irq)
}
void
-free_irq_vector (int vector)
+ia64_native_free_irq_vector (int vector)
{
if (vector < IA64_FIRST_DEVICE_VECTOR ||
vector > IA64_LAST_DEVICE_VECTOR)
@@ -600,7 +600,6 @@ static irqreturn_t dummy_handler (int irq, void *dev_id)
{
BUG();
}
-extern irqreturn_t handle_IPI (int irq, void *dev_id);
static struct irqaction ipi_irqaction = {
.handler = handle_IPI,
@@ -623,7 +622,7 @@ static struct irqaction tlb_irqaction = {
#endif
void
-register_percpu_irq (ia64_vector vec, struct irqaction *action)
+ia64_native_register_percpu_irq (ia64_vector vec, struct irqaction *action)
{
irq_desc_t *desc;
unsigned int irq;
@@ -638,13 +637,21 @@ register_percpu_irq (ia64_vector vec, struct irqaction *action)
}
void __init
-init_IRQ (void)
+ia64_native_register_ipi(void)
{
- register_percpu_irq(IA64_SPURIOUS_INT_VECTOR, NULL);
#ifdef CONFIG_SMP
register_percpu_irq(IA64_IPI_VECTOR, &ipi_irqaction);
register_percpu_irq(IA64_IPI_RESCHEDULE, &resched_irqaction);
register_percpu_irq(IA64_IPI_LOCAL_TLB_FLUSH, &tlb_irqaction);
+#endif
+}
+
+void __init
+init_IRQ (void)
+{
+ ia64_register_ipi();
+ register_percpu_irq(IA64_SPURIOUS_INT_VECTOR, NULL);
+#ifdef CONFIG_SMP
#if defined(CONFIG_IA64_GENERIC) || defined(CONFIG_IA64_DIG)
if (vector_domain_type != VECTOR_DOMAIN_NONE) {
BUG_ON(IA64_FIRST_DEVICE_VECTOR != IA64_IRQ_MOVE_VECTOR);
diff --git a/arch/ia64/kernel/ivt.S b/arch/ia64/kernel/ivt.S
index 80b44ea052d7..c39627df3cde 100644
--- a/arch/ia64/kernel/ivt.S
+++ b/arch/ia64/kernel/ivt.S
@@ -12,6 +12,14 @@
*
* 00/08/23 Asit Mallick <asit.k.mallick@intel.com> TLB handling for SMP
* 00/12/20 David Mosberger-Tang <davidm@hpl.hp.com> DTLB/ITLB handler now uses virtual PT.
+ *
+ * Copyright (C) 2005 Hewlett-Packard Co
+ * Dan Magenheimer <dan.magenheimer@hp.com>
+ * Xen paravirtualization
+ * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ * pv_ops.
+ * Yaozu (Eddie) Dong <eddie.dong@intel.com>
*/
/*
* This file defines the interruption vector table used by the CPU.
@@ -102,13 +110,13 @@ ENTRY(vhpt_miss)
* - the faulting virtual address uses unimplemented address bits
* - the faulting virtual address has no valid page table mapping
*/
- mov r16=cr.ifa // get address that caused the TLB miss
+ MOV_FROM_IFA(r16) // get address that caused the TLB miss
#ifdef CONFIG_HUGETLB_PAGE
movl r18=PAGE_SHIFT
- mov r25=cr.itir
+ MOV_FROM_ITIR(r25)
#endif
;;
- rsm psr.dt // use physical addressing for data
+ RSM_PSR_DT // use physical addressing for data
mov r31=pr // save the predicate registers
mov r19=IA64_KR(PT_BASE) // get page table base address
shl r21=r16,3 // shift bit 60 into sign bit
@@ -168,21 +176,21 @@ ENTRY(vhpt_miss)
dep r21=r19,r20,3,(PAGE_SHIFT-3) // r21=pte_offset(pmd,addr)
;;
(p7) ld8 r18=[r21] // read *pte
- mov r19=cr.isr // cr.isr bit 32 tells us if this is an insn miss
+ MOV_FROM_ISR(r19) // cr.isr bit 32 tells us if this is an insn miss
;;
(p7) tbit.z p6,p7=r18,_PAGE_P_BIT // page present bit cleared?
- mov r22=cr.iha // get the VHPT address that caused the TLB miss
+ MOV_FROM_IHA(r22) // get the VHPT address that caused the TLB miss
;; // avoid RAW on p7
(p7) tbit.nz.unc p10,p11=r19,32 // is it an instruction TLB miss?
dep r23=0,r20,0,PAGE_SHIFT // clear low bits to get page address
;;
-(p10) itc.i r18 // insert the instruction TLB entry
-(p11) itc.d r18 // insert the data TLB entry
+ ITC_I_AND_D(p10, p11, r18, r24) // insert the instruction TLB entry and
+ // insert the data TLB entry
(p6) br.cond.spnt.many page_fault // handle bad address/page not present (page fault)
- mov cr.ifa=r22
+ MOV_TO_IFA(r22, r24)
#ifdef CONFIG_HUGETLB_PAGE
-(p8) mov cr.itir=r25 // change to default page-size for VHPT
+ MOV_TO_ITIR(p8, r25, r24) // change to default page-size for VHPT
#endif
/*
@@ -192,7 +200,7 @@ ENTRY(vhpt_miss)
*/
adds r24=__DIRTY_BITS_NO_ED|_PAGE_PL_0|_PAGE_AR_RW,r23
;;
-(p7) itc.d r24
+ ITC_D(p7, r24, r25)
;;
#ifdef CONFIG_SMP
/*
@@ -234,7 +242,7 @@ ENTRY(vhpt_miss)
#endif
mov pr=r31,-1 // restore predicate registers
- rfi
+ RFI
END(vhpt_miss)
.org ia64_ivt+0x400
@@ -248,11 +256,11 @@ ENTRY(itlb_miss)
* mode, walk the page table, and then re-execute the PTE read and
* go on normally after that.
*/
- mov r16=cr.ifa // get virtual address
+ MOV_FROM_IFA(r16) // get virtual address
mov r29=b0 // save b0
mov r31=pr // save predicates
.itlb_fault:
- mov r17=cr.iha // get virtual address of PTE
+ MOV_FROM_IHA(r17) // get virtual address of PTE
movl r30=1f // load nested fault continuation point
;;
1: ld8 r18=[r17] // read *pte
@@ -261,7 +269,7 @@ ENTRY(itlb_miss)
tbit.z p6,p0=r18,_PAGE_P_BIT // page present bit cleared?
(p6) br.cond.spnt page_fault
;;
- itc.i r18
+ ITC_I(p0, r18, r19)
;;
#ifdef CONFIG_SMP
/*
@@ -278,7 +286,7 @@ ENTRY(itlb_miss)
(p7) ptc.l r16,r20
#endif
mov pr=r31,-1
- rfi
+ RFI
END(itlb_miss)
.org ia64_ivt+0x0800
@@ -292,11 +300,11 @@ ENTRY(dtlb_miss)
* mode, walk the page table, and then re-execute the PTE read and
* go on normally after that.
*/
- mov r16=cr.ifa // get virtual address
+ MOV_FROM_IFA(r16) // get virtual address
mov r29=b0 // save b0
mov r31=pr // save predicates
dtlb_fault:
- mov r17=cr.iha // get virtual address of PTE
+ MOV_FROM_IHA(r17) // get virtual address of PTE
movl r30=1f // load nested fault continuation point
;;
1: ld8 r18=[r17] // read *pte
@@ -305,7 +313,7 @@ dtlb_fault:
tbit.z p6,p0=r18,_PAGE_P_BIT // page present bit cleared?
(p6) br.cond.spnt page_fault
;;
- itc.d r18
+ ITC_D(p0, r18, r19)
;;
#ifdef CONFIG_SMP
/*
@@ -322,7 +330,7 @@ dtlb_fault:
(p7) ptc.l r16,r20
#endif
mov pr=r31,-1
- rfi
+ RFI
END(dtlb_miss)
.org ia64_ivt+0x0c00
@@ -330,9 +338,9 @@ END(dtlb_miss)
// 0x0c00 Entry 3 (size 64 bundles) Alt ITLB (19)
ENTRY(alt_itlb_miss)
DBG_FAULT(3)
- mov r16=cr.ifa // get address that caused the TLB miss
+ MOV_FROM_IFA(r16) // get address that caused the TLB miss
movl r17=PAGE_KERNEL
- mov r21=cr.ipsr
+ MOV_FROM_IPSR(p0, r21)
movl r19=(((1 << IA64_MAX_PHYS_BITS) - 1) & ~0xfff)
mov r31=pr
;;
@@ -341,9 +349,9 @@ ENTRY(alt_itlb_miss)
;;
cmp.gt p8,p0=6,r22 // user mode
;;
-(p8) thash r17=r16
+ THASH(p8, r17, r16, r23)
;;
-(p8) mov cr.iha=r17
+ MOV_TO_IHA(p8, r17, r23)
(p8) mov r29=b0 // save b0
(p8) br.cond.dptk .itlb_fault
#endif
@@ -358,9 +366,9 @@ ENTRY(alt_itlb_miss)
or r19=r19,r18 // set bit 4 (uncached) if the access was to region 6
(p8) br.cond.spnt page_fault
;;
- itc.i r19 // insert the TLB entry
+ ITC_I(p0, r19, r18) // insert the TLB entry
mov pr=r31,-1
- rfi
+ RFI
END(alt_itlb_miss)
.org ia64_ivt+0x1000
@@ -368,11 +376,11 @@ END(alt_itlb_miss)
// 0x1000 Entry 4 (size 64 bundles) Alt DTLB (7,46)
ENTRY(alt_dtlb_miss)
DBG_FAULT(4)
- mov r16=cr.ifa // get address that caused the TLB miss
+ MOV_FROM_IFA(r16) // get address that caused the TLB miss
movl r17=PAGE_KERNEL
- mov r20=cr.isr
+ MOV_FROM_ISR(r20)
movl r19=(((1 << IA64_MAX_PHYS_BITS) - 1) & ~0xfff)
- mov r21=cr.ipsr
+ MOV_FROM_IPSR(p0, r21)
mov r31=pr
mov r24=PERCPU_ADDR
;;
@@ -381,9 +389,9 @@ ENTRY(alt_dtlb_miss)
;;
cmp.gt p8,p0=6,r22 // access to region 0-5
;;
-(p8) thash r17=r16
+ THASH(p8, r17, r16, r25)
;;
-(p8) mov cr.iha=r17
+ MOV_TO_IHA(p8, r17, r25)
(p8) mov r29=b0 // save b0
(p8) br.cond.dptk dtlb_fault
#endif
@@ -402,7 +410,7 @@ ENTRY(alt_dtlb_miss)
tbit.nz p9,p0=r20,IA64_ISR_NA_BIT // is non-access bit on?
;;
(p10) sub r19=r19,r26
-(p10) mov cr.itir=r25
+ MOV_TO_ITIR(p10, r25, r24)
cmp.ne p8,p0=r0,r23
(p9) cmp.eq.or.andcm p6,p7=IA64_ISR_CODE_LFETCH,r22 // check isr.code field
(p12) dep r17=-1,r17,4,1 // set ma=UC for region 6 addr
@@ -411,11 +419,11 @@ ENTRY(alt_dtlb_miss)
dep r21=-1,r21,IA64_PSR_ED_BIT,1
;;
or r19=r19,r17 // insert PTE control bits into r19
-(p6) mov cr.ipsr=r21
+ MOV_TO_IPSR(p6, r21, r24)
;;
-(p7) itc.d r19 // insert the TLB entry
+ ITC_D(p7, r19, r18) // insert the TLB entry
mov pr=r31,-1
- rfi
+ RFI
END(alt_dtlb_miss)
.org ia64_ivt+0x1400
@@ -444,10 +452,10 @@ ENTRY(nested_dtlb_miss)
*
* Clobbered: b0, r18, r19, r21, r22, psr.dt (cleared)
*/
- rsm psr.dt // switch to using physical data addressing
+ RSM_PSR_DT // switch to using physical data addressing
mov r19=IA64_KR(PT_BASE) // get the page table base address
shl r21=r16,3 // shift bit 60 into sign bit
- mov r18=cr.itir
+ MOV_FROM_ITIR(r18)
;;
shr.u r17=r16,61 // get the region number into r17
extr.u r18=r18,2,6 // get the faulting page size
@@ -507,33 +515,6 @@ ENTRY(ikey_miss)
FAULT(6)
END(ikey_miss)
- //-----------------------------------------------------------------------------------
- // call do_page_fault (predicates are in r31, psr.dt may be off, r16 is faulting address)
-ENTRY(page_fault)
- ssm psr.dt
- ;;
- srlz.i
- ;;
- SAVE_MIN_WITH_COVER
- alloc r15=ar.pfs,0,0,3,0
- mov out0=cr.ifa
- mov out1=cr.isr
- adds r3=8,r2 // set up second base pointer
- ;;
- ssm psr.ic | PSR_DEFAULT_BITS
- ;;
- srlz.i // guarantee that interruption collectin is on
- ;;
-(p15) ssm psr.i // restore psr.i
- movl r14=ia64_leave_kernel
- ;;
- SAVE_REST
- mov rp=r14
- ;;
- adds out2=16,r12 // out2 = pointer to pt_regs
- br.call.sptk.many b6=ia64_do_page_fault // ignore return address
-END(page_fault)
-
.org ia64_ivt+0x1c00
/////////////////////////////////////////////////////////////////////////////////////////
// 0x1c00 Entry 7 (size 64 bundles) Data Key Miss (12,51)
@@ -556,10 +537,10 @@ ENTRY(dirty_bit)
* page table TLB entry isn't present, we take a nested TLB miss hit where we look
* up the physical address of the L3 PTE and then continue at label 1 below.
*/
- mov r16=cr.ifa // get the address that caused the fault
+ MOV_FROM_IFA(r16) // get the address that caused the fault
movl r30=1f // load continuation point in case of nested fault
;;
- thash r17=r16 // compute virtual address of L3 PTE
+ THASH(p0, r17, r16, r18) // compute virtual address of L3 PTE
mov r29=b0 // save b0 in case of nested fault
mov r31=pr // save pr
#ifdef CONFIG_SMP
@@ -576,7 +557,7 @@ ENTRY(dirty_bit)
;;
(p6) cmp.eq p6,p7=r26,r18 // Only compare if page is present
;;
-(p6) itc.d r25 // install updated PTE
+ ITC_D(p6, r25, r18) // install updated PTE
;;
/*
* Tell the assemblers dependency-violation checker that the above "itc" instructions
@@ -602,7 +583,7 @@ ENTRY(dirty_bit)
itc.d r18 // install updated PTE
#endif
mov pr=r31,-1 // restore pr
- rfi
+ RFI
END(dirty_bit)
.org ia64_ivt+0x2400
@@ -611,22 +592,22 @@ END(dirty_bit)
ENTRY(iaccess_bit)
DBG_FAULT(9)
// Like Entry 8, except for instruction access
- mov r16=cr.ifa // get the address that caused the fault
+ MOV_FROM_IFA(r16) // get the address that caused the fault
movl r30=1f // load continuation point in case of nested fault
mov r31=pr // save predicates
#ifdef CONFIG_ITANIUM
/*
* Erratum 10 (IFA may contain incorrect address) has "NoFix" status.
*/
- mov r17=cr.ipsr
+ MOV_FROM_IPSR(p0, r17)
;;
- mov r18=cr.iip
+ MOV_FROM_IIP(r18)
tbit.z p6,p0=r17,IA64_PSR_IS_BIT // IA64 instruction set?
;;
(p6) mov r16=r18 // if so, use cr.iip instead of cr.ifa
#endif /* CONFIG_ITANIUM */
;;
- thash r17=r16 // compute virtual address of L3 PTE
+ THASH(p0, r17, r16, r18) // compute virtual address of L3 PTE
mov r29=b0 // save b0 in case of nested fault)
#ifdef CONFIG_SMP
mov r28=ar.ccv // save ar.ccv
@@ -642,7 +623,7 @@ ENTRY(iaccess_bit)
;;
(p6) cmp.eq p6,p7=r26,r18 // Only if page present
;;
-(p6) itc.i r25 // install updated PTE
+ ITC_I(p6, r25, r26) // install updated PTE
;;
/*
* Tell the assemblers dependency-violation checker that the above "itc" instructions
@@ -668,7 +649,7 @@ ENTRY(iaccess_bit)
itc.i r18 // install updated PTE
#endif /* !CONFIG_SMP */
mov pr=r31,-1
- rfi
+ RFI
END(iaccess_bit)
.org ia64_ivt+0x2800
@@ -677,10 +658,10 @@ END(iaccess_bit)
ENTRY(daccess_bit)
DBG_FAULT(10)
// Like Entry 8, except for data access
- mov r16=cr.ifa // get the address that caused the fault
+ MOV_FROM_IFA(r16) // get the address that caused the fault
movl r30=1f // load continuation point in case of nested fault
;;
- thash r17=r16 // compute virtual address of L3 PTE
+ THASH(p0, r17, r16, r18) // compute virtual address of L3 PTE
mov r31=pr
mov r29=b0 // save b0 in case of nested fault)
#ifdef CONFIG_SMP
@@ -697,7 +678,7 @@ ENTRY(daccess_bit)
;;
(p6) cmp.eq p6,p7=r26,r18 // Only if page is present
;;
-(p6) itc.d r25 // install updated PTE
+ ITC_D(p6, r25, r26) // install updated PTE
/*
* Tell the assemblers dependency-violation checker that the above "itc" instructions
* cannot possibly affect the following loads:
@@ -721,7 +702,7 @@ ENTRY(daccess_bit)
#endif
mov b0=r29 // restore b0
mov pr=r31,-1
- rfi
+ RFI
END(daccess_bit)
.org ia64_ivt+0x2c00
@@ -745,10 +726,10 @@ ENTRY(break_fault)
*/
DBG_FAULT(11)
mov.m r16=IA64_KR(CURRENT) // M2 r16 <- current task (12 cyc)
- mov r29=cr.ipsr // M2 (12 cyc)
+ MOV_FROM_IPSR(p0, r29) // M2 (12 cyc)
mov r31=pr // I0 (2 cyc)
- mov r17=cr.iim // M2 (2 cyc)
+ MOV_FROM_IIM(r17) // M2 (2 cyc)
mov.m r27=ar.rsc // M2 (12 cyc)
mov r18=__IA64_BREAK_SYSCALL // A
@@ -767,7 +748,7 @@ ENTRY(break_fault)
nop.m 0
movl r30=sys_call_table // X
- mov r28=cr.iip // M2 (2 cyc)
+ MOV_FROM_IIP(r28) // M2 (2 cyc)
cmp.eq p0,p7=r18,r17 // I0 is this a system call?
(p7) br.cond.spnt non_syscall // B no ->
//
@@ -864,18 +845,17 @@ ENTRY(break_fault)
#endif
mov ar.rsc=0x3 // M2 set eager mode, pl 0, LE, loadrs=0
nop 0
- bsw.1 // B (6 cyc) regs are saved, switch to bank 1
+ BSW_1(r2, r14) // B (6 cyc) regs are saved, switch to bank 1
;;
- ssm psr.ic | PSR_DEFAULT_BITS // M2 now it's safe to re-enable intr.-collection
+ SSM_PSR_IC_AND_DEFAULT_BITS_AND_SRLZ_I(r3, r16) // M2 now it's safe to re-enable intr.-collection
+ // M0 ensure interruption collection is on
movl r3=ia64_ret_from_syscall // X
;;
-
- srlz.i // M0 ensure interruption collection is on
mov rp=r3 // I0 set the real return addr
(p10) br.cond.spnt.many ia64_ret_from_syscall // B return if bad call-frame or r15 is a NaT
-(p15) ssm psr.i // M2 restore psr.i
+ SSM_PSR_I(p15, p15, r16) // M2 restore psr.i
(p14) br.call.sptk.many b6=b6 // B invoke syscall-handker (ignore return addr)
br.cond.spnt.many ia64_trace_syscall // B do syscall-tracing thingamagic
// NOT REACHED
@@ -895,27 +875,8 @@ END(break_fault)
/////////////////////////////////////////////////////////////////////////////////////////
// 0x3000 Entry 12 (size 64 bundles) External Interrupt (4)
ENTRY(interrupt)
- DBG_FAULT(12)
- mov r31=pr // prepare to save predicates
- ;;
- SAVE_MIN_WITH_COVER // uses r31; defines r2 and r3
- ssm psr.ic | PSR_DEFAULT_BITS
- ;;
- adds r3=8,r2 // set up second base pointer for SAVE_REST
- srlz.i // ensure everybody knows psr.ic is back on
- ;;
- SAVE_REST
- ;;
- MCA_RECOVER_RANGE(interrupt)
- alloc r14=ar.pfs,0,0,2,0 // must be first in an insn group
- mov out0=cr.ivr // pass cr.ivr as first arg
- add out1=16,sp // pass pointer to pt_regs as second arg
- ;;
- srlz.d // make sure we see the effect of cr.ivr
- movl r14=ia64_leave_kernel
- ;;
- mov rp=r14
- br.call.sptk.many b6=ia64_handle_irq
+ /* interrupt handler has become too big to fit this area. */
+ br.sptk.many __interrupt
END(interrupt)
.org ia64_ivt+0x3400
@@ -978,6 +939,7 @@ END(interrupt)
* - ar.fpsr: set to kernel settings
* - b6: preserved (same as on entry)
*/
+#ifdef __IA64_ASM_PARAVIRTUALIZED_NATIVE
GLOBAL_ENTRY(ia64_syscall_setup)
#if PT(B6) != 0
# error This code assumes that b6 is the first field in pt_regs.
@@ -1069,6 +1031,7 @@ GLOBAL_ENTRY(ia64_syscall_setup)
(p10) mov r8=-EINVAL
br.ret.sptk.many b7
END(ia64_syscall_setup)
+#endif /* __IA64_ASM_PARAVIRTUALIZED_NATIVE */
.org ia64_ivt+0x3c00
/////////////////////////////////////////////////////////////////////////////////////////
@@ -1082,7 +1045,7 @@ END(ia64_syscall_setup)
DBG_FAULT(16)
FAULT(16)
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
+#if defined(CONFIG_VIRT_CPU_ACCOUNTING) && defined(__IA64_ASM_PARAVIRTUALIZED_NATIVE)
/*
* There is no particular reason for this code to be here, other than
* that there happens to be space here that would go unused otherwise.
@@ -1092,7 +1055,7 @@ END(ia64_syscall_setup)
* account_sys_enter is called from SAVE_MIN* macros if accounting is
* enabled and if the macro is entered from user mode.
*/
-ENTRY(account_sys_enter)
+GLOBAL_ENTRY(account_sys_enter)
// mov.m r20=ar.itc is called in advance, and r13 is current
add r16=TI_AC_STAMP+IA64_TASK_SIZE,r13
add r17=TI_AC_LEAVE+IA64_TASK_SIZE,r13
@@ -1123,110 +1086,18 @@ END(account_sys_enter)
DBG_FAULT(17)
FAULT(17)
-ENTRY(non_syscall)
- mov ar.rsc=r27 // restore ar.rsc before SAVE_MIN_WITH_COVER
- ;;
- SAVE_MIN_WITH_COVER
-
- // There is no particular reason for this code to be here, other than that
- // there happens to be space here that would go unused otherwise. If this
- // fault ever gets "unreserved", simply moved the following code to a more
- // suitable spot...
-
- alloc r14=ar.pfs,0,0,2,0
- mov out0=cr.iim
- add out1=16,sp
- adds r3=8,r2 // set up second base pointer for SAVE_REST
-
- ssm psr.ic | PSR_DEFAULT_BITS
- ;;
- srlz.i // guarantee that interruption collection is on
- ;;
-(p15) ssm psr.i // restore psr.i
- movl r15=ia64_leave_kernel
- ;;
- SAVE_REST
- mov rp=r15
- ;;
- br.call.sptk.many b6=ia64_bad_break // avoid WAW on CFM and ignore return addr
-END(non_syscall)
-
.org ia64_ivt+0x4800
/////////////////////////////////////////////////////////////////////////////////////////
// 0x4800 Entry 18 (size 64 bundles) Reserved
DBG_FAULT(18)
FAULT(18)
- /*
- * There is no particular reason for this code to be here, other than that
- * there happens to be space here that would go unused otherwise. If this
- * fault ever gets "unreserved", simply moved the following code to a more
- * suitable spot...
- */
-
-ENTRY(dispatch_unaligned_handler)
- SAVE_MIN_WITH_COVER
- ;;
- alloc r14=ar.pfs,0,0,2,0 // now it's safe (must be first in insn group!)
- mov out0=cr.ifa
- adds out1=16,sp
-
- ssm psr.ic | PSR_DEFAULT_BITS
- ;;
- srlz.i // guarantee that interruption collection is on
- ;;
-(p15) ssm psr.i // restore psr.i
- adds r3=8,r2 // set up second base pointer
- ;;
- SAVE_REST
- movl r14=ia64_leave_kernel
- ;;
- mov rp=r14
- br.sptk.many ia64_prepare_handle_unaligned
-END(dispatch_unaligned_handler)
-
.org ia64_ivt+0x4c00
/////////////////////////////////////////////////////////////////////////////////////////
// 0x4c00 Entry 19 (size 64 bundles) Reserved
DBG_FAULT(19)
FAULT(19)
- /*
- * There is no particular reason for this code to be here, other than that
- * there happens to be space here that would go unused otherwise. If this
- * fault ever gets "unreserved", simply moved the following code to a more
- * suitable spot...
- */
-
-ENTRY(dispatch_to_fault_handler)
- /*
- * Input:
- * psr.ic: off
- * r19: fault vector number (e.g., 24 for General Exception)
- * r31: contains saved predicates (pr)
- */
- SAVE_MIN_WITH_COVER_R19
- alloc r14=ar.pfs,0,0,5,0
- mov out0=r15
- mov out1=cr.isr
- mov out2=cr.ifa
- mov out3=cr.iim
- mov out4=cr.itir
- ;;
- ssm psr.ic | PSR_DEFAULT_BITS
- ;;
- srlz.i // guarantee that interruption collection is on
- ;;
-(p15) ssm psr.i // restore psr.i
- adds r3=8,r2 // set up second base pointer for SAVE_REST
- ;;
- SAVE_REST
- movl r14=ia64_leave_kernel
- ;;
- mov rp=r14
- br.call.sptk.many b6=ia64_fault
-END(dispatch_to_fault_handler)
-
//
// --- End of long entries, Beginning of short entries
//
@@ -1236,8 +1107,8 @@ END(dispatch_to_fault_handler)
// 0x5000 Entry 20 (size 16 bundles) Page Not Present (10,22,49)
ENTRY(page_not_present)
DBG_FAULT(20)
- mov r16=cr.ifa
- rsm psr.dt
+ MOV_FROM_IFA(r16)
+ RSM_PSR_DT
/*
* The Linux page fault handler doesn't expect non-present pages to be in
* the TLB. Flush the existing entry now, so we meet that expectation.
@@ -1256,8 +1127,8 @@ END(page_not_present)
// 0x5100 Entry 21 (size 16 bundles) Key Permission (13,25,52)
ENTRY(key_permission)
DBG_FAULT(21)
- mov r16=cr.ifa
- rsm psr.dt
+ MOV_FROM_IFA(r16)
+ RSM_PSR_DT
mov r31=pr
;;
srlz.d
@@ -1269,8 +1140,8 @@ END(key_permission)
// 0x5200 Entry 22 (size 16 bundles) Instruction Access Rights (26)
ENTRY(iaccess_rights)
DBG_FAULT(22)
- mov r16=cr.ifa
- rsm psr.dt
+ MOV_FROM_IFA(r16)
+ RSM_PSR_DT
mov r31=pr
;;
srlz.d
@@ -1282,8 +1153,8 @@ END(iaccess_rights)
// 0x5300 Entry 23 (size 16 bundles) Data Access Rights (14,53)
ENTRY(daccess_rights)
DBG_FAULT(23)
- mov r16=cr.ifa
- rsm psr.dt
+ MOV_FROM_IFA(r16)
+ RSM_PSR_DT
mov r31=pr
;;
srlz.d
@@ -1295,7 +1166,7 @@ END(daccess_rights)
// 0x5400 Entry 24 (size 16 bundles) General Exception (5,32,34,36,38,39)
ENTRY(general_exception)
DBG_FAULT(24)
- mov r16=cr.isr
+ MOV_FROM_ISR(r16)
mov r31=pr
;;
cmp4.eq p6,p0=0,r16
@@ -1324,8 +1195,8 @@ END(disabled_fp_reg)
ENTRY(nat_consumption)
DBG_FAULT(26)
- mov r16=cr.ipsr
- mov r17=cr.isr
+ MOV_FROM_IPSR(p0, r16)
+ MOV_FROM_ISR(r17)
mov r31=pr // save PR
;;
and r18=0xf,r17 // r18 = cr.ipsr.code{3:0}
@@ -1335,10 +1206,10 @@ ENTRY(nat_consumption)
dep r16=-1,r16,IA64_PSR_ED_BIT,1
(p6) br.cond.spnt 1f // branch if (cr.ispr.na == 0 || cr.ipsr.code{3:0} != LFETCH)
;;
- mov cr.ipsr=r16 // set cr.ipsr.na
+ MOV_TO_IPSR(p0, r16, r18)
mov pr=r31,-1
;;
- rfi
+ RFI
1: mov pr=r31,-1
;;
@@ -1360,26 +1231,26 @@ ENTRY(speculation_vector)
*
* cr.imm contains zero_ext(imm21)
*/
- mov r18=cr.iim
+ MOV_FROM_IIM(r18)
;;
- mov r17=cr.iip
+ MOV_FROM_IIP(r17)
shl r18=r18,43 // put sign bit in position (43=64-21)
;;
- mov r16=cr.ipsr
+ MOV_FROM_IPSR(p0, r16)
shr r18=r18,39 // sign extend (39=43-4)
;;
add r17=r17,r18 // now add the offset
;;
- mov cr.iip=r17
+ MOV_FROM_IIP(r17)
dep r16=0,r16,41,2 // clear EI
;;
- mov cr.ipsr=r16
+ MOV_FROM_IPSR(p0, r16)
;;
- rfi // and go back
+ RFI
END(speculation_vector)
.org ia64_ivt+0x5800
@@ -1517,11 +1388,11 @@ ENTRY(ia32_intercept)
DBG_FAULT(46)
#ifdef CONFIG_IA32_SUPPORT
mov r31=pr
- mov r16=cr.isr
+ MOV_FROM_ISR(r16)
;;
extr.u r17=r16,16,8 // get ISR.code
mov r18=ar.eflag
- mov r19=cr.iim // old eflag value
+ MOV_FROM_IIM(r19) // old eflag value
;;
cmp.ne p6,p0=2,r17
(p6) br.cond.spnt 1f // not a system flag fault
@@ -1533,7 +1404,7 @@ ENTRY(ia32_intercept)
(p6) br.cond.spnt 1f // eflags.ac bit didn't change
;;
mov pr=r31,-1 // restore predicate registers
- rfi
+ RFI
1:
#endif // CONFIG_IA32_SUPPORT
@@ -1673,6 +1544,137 @@ END(ia32_interrupt)
DBG_FAULT(67)
FAULT(67)
+ //-----------------------------------------------------------------------------------
+ // call do_page_fault (predicates are in r31, psr.dt may be off, r16 is faulting address)
+ENTRY(page_fault)
+ SSM_PSR_DT_AND_SRLZ_I
+ ;;
+ SAVE_MIN_WITH_COVER
+ alloc r15=ar.pfs,0,0,3,0
+ MOV_FROM_IFA(out0)
+ MOV_FROM_ISR(out1)
+ SSM_PSR_IC_AND_DEFAULT_BITS_AND_SRLZ_I(r14, r3)
+ adds r3=8,r2 // set up second base pointer
+ SSM_PSR_I(p15, p15, r14) // restore psr.i
+ movl r14=ia64_leave_kernel
+ ;;
+ SAVE_REST
+ mov rp=r14
+ ;;
+ adds out2=16,r12 // out2 = pointer to pt_regs
+ br.call.sptk.many b6=ia64_do_page_fault // ignore return address
+END(page_fault)
+
+ENTRY(non_syscall)
+ mov ar.rsc=r27 // restore ar.rsc before SAVE_MIN_WITH_COVER
+ ;;
+ SAVE_MIN_WITH_COVER
+
+ // There is no particular reason for this code to be here, other than that
+ // there happens to be space here that would go unused otherwise. If this
+ // fault ever gets "unreserved", simply moved the following code to a more
+ // suitable spot...
+
+ alloc r14=ar.pfs,0,0,2,0
+ MOV_FROM_IIM(out0)
+ add out1=16,sp
+ adds r3=8,r2 // set up second base pointer for SAVE_REST
+
+ SSM_PSR_IC_AND_DEFAULT_BITS_AND_SRLZ_I(r15, r24)
+ // guarantee that interruption collection is on
+ SSM_PSR_I(p15, p15, r15) // restore psr.i
+ movl r15=ia64_leave_kernel
+ ;;
+ SAVE_REST
+ mov rp=r15
+ ;;
+ br.call.sptk.many b6=ia64_bad_break // avoid WAW on CFM and ignore return addr
+END(non_syscall)
+
+ENTRY(__interrupt)
+ DBG_FAULT(12)
+ mov r31=pr // prepare to save predicates
+ ;;
+ SAVE_MIN_WITH_COVER // uses r31; defines r2 and r3
+ SSM_PSR_IC_AND_DEFAULT_BITS_AND_SRLZ_I(r3, r14)
+ // ensure everybody knows psr.ic is back on
+ adds r3=8,r2 // set up second base pointer for SAVE_REST
+ ;;
+ SAVE_REST
+ ;;
+ MCA_RECOVER_RANGE(interrupt)
+ alloc r14=ar.pfs,0,0,2,0 // must be first in an insn group
+ MOV_FROM_IVR(out0, r8) // pass cr.ivr as first arg
+ add out1=16,sp // pass pointer to pt_regs as second arg
+ ;;
+ srlz.d // make sure we see the effect of cr.ivr
+ movl r14=ia64_leave_kernel
+ ;;
+ mov rp=r14
+ br.call.sptk.many b6=ia64_handle_irq
+END(__interrupt)
+
+ /*
+ * There is no particular reason for this code to be here, other than that
+ * there happens to be space here that would go unused otherwise. If this
+ * fault ever gets "unreserved", simply moved the following code to a more
+ * suitable spot...
+ */
+
+ENTRY(dispatch_unaligned_handler)
+ SAVE_MIN_WITH_COVER
+ ;;
+ alloc r14=ar.pfs,0,0,2,0 // now it's safe (must be first in insn group!)
+ MOV_FROM_IFA(out0)
+ adds out1=16,sp
+
+ SSM_PSR_IC_AND_DEFAULT_BITS_AND_SRLZ_I(r3, r24)
+ // guarantee that interruption collection is on
+ SSM_PSR_I(p15, p15, r3) // restore psr.i
+ adds r3=8,r2 // set up second base pointer
+ ;;
+ SAVE_REST
+ movl r14=ia64_leave_kernel
+ ;;
+ mov rp=r14
+ br.sptk.many ia64_prepare_handle_unaligned
+END(dispatch_unaligned_handler)
+
+ /*
+ * There is no particular reason for this code to be here, other than that
+ * there happens to be space here that would go unused otherwise. If this
+ * fault ever gets "unreserved", simply moved the following code to a more
+ * suitable spot...
+ */
+
+ENTRY(dispatch_to_fault_handler)
+ /*
+ * Input:
+ * psr.ic: off
+ * r19: fault vector number (e.g., 24 for General Exception)
+ * r31: contains saved predicates (pr)
+ */
+ SAVE_MIN_WITH_COVER_R19
+ alloc r14=ar.pfs,0,0,5,0
+ MOV_FROM_ISR(out1)
+ MOV_FROM_IFA(out2)
+ MOV_FROM_IIM(out3)
+ MOV_FROM_ITIR(out4)
+ ;;
+ SSM_PSR_IC_AND_DEFAULT_BITS_AND_SRLZ_I(r3, out0)
+ // guarantee that interruption collection is on
+ mov out0=r15
+ ;;
+ SSM_PSR_I(p15, p15, r3) // restore psr.i
+ adds r3=8,r2 // set up second base pointer for SAVE_REST
+ ;;
+ SAVE_REST
+ movl r14=ia64_leave_kernel
+ ;;
+ mov rp=r14
+ br.call.sptk.many b6=ia64_fault
+END(dispatch_to_fault_handler)
+
/*
* Squatting in this space ...
*
@@ -1686,11 +1688,10 @@ ENTRY(dispatch_illegal_op_fault)
.prologue
.body
SAVE_MIN_WITH_COVER
- ssm psr.ic | PSR_DEFAULT_BITS
- ;;
- srlz.i // guarantee that interruption collection is on
+ SSM_PSR_IC_AND_DEFAULT_BITS_AND_SRLZ_I(r3, r24)
+ // guarantee that interruption collection is on
;;
-(p15) ssm psr.i // restore psr.i
+ SSM_PSR_I(p15, p15, r3) // restore psr.i
adds r3=8,r2 // set up second base pointer for SAVE_REST
;;
alloc r14=ar.pfs,0,0,1,0 // must be first in insn group
@@ -1729,12 +1730,11 @@ END(dispatch_illegal_op_fault)
ENTRY(dispatch_to_ia32_handler)
SAVE_MIN
;;
- mov r14=cr.isr
- ssm psr.ic | PSR_DEFAULT_BITS
- ;;
- srlz.i // guarantee that interruption collection is on
+ MOV_FROM_ISR(r14)
+ SSM_PSR_IC_AND_DEFAULT_BITS_AND_SRLZ_I(r3, r24)
+ // guarantee that interruption collection is on
;;
-(p15) ssm psr.i
+ SSM_PSR_I(p15, p15, r3)
adds r3=8,r2 // Base pointer for SAVE_REST
;;
SAVE_REST
diff --git a/arch/ia64/kernel/minstate.h b/arch/ia64/kernel/minstate.h
index 74b6d670aaef..292e214a3b84 100644
--- a/arch/ia64/kernel/minstate.h
+++ b/arch/ia64/kernel/minstate.h
@@ -2,6 +2,7 @@
#include <asm/cache.h>
#include "entry.h"
+#include "paravirt_inst.h"
#ifdef CONFIG_VIRT_CPU_ACCOUNTING
/* read ar.itc in advance, and use it before leaving bank 0 */
@@ -43,16 +44,16 @@
* Note that psr.ic is NOT turned on by this macro. This is so that
* we can pass interruption state as arguments to a handler.
*/
-#define DO_SAVE_MIN(COVER,SAVE_IFS,EXTRA,WORKAROUND) \
+#define IA64_NATIVE_DO_SAVE_MIN(__COVER,SAVE_IFS,EXTRA,WORKAROUND) \
mov r16=IA64_KR(CURRENT); /* M */ \
mov r27=ar.rsc; /* M */ \
mov r20=r1; /* A */ \
mov r25=ar.unat; /* M */ \
- mov r29=cr.ipsr; /* M */ \
+ MOV_FROM_IPSR(p0,r29); /* M */ \
mov r26=ar.pfs; /* I */ \
- mov r28=cr.iip; /* M */ \
+ MOV_FROM_IIP(r28); /* M */ \
mov r21=ar.fpsr; /* M */ \
- COVER; /* B;; (or nothing) */ \
+ __COVER; /* B;; (or nothing) */ \
;; \
adds r16=IA64_TASK_THREAD_ON_USTACK_OFFSET,r16; \
;; \
@@ -244,6 +245,6 @@
1: \
.pred.rel "mutex", pKStk, pUStk
-#define SAVE_MIN_WITH_COVER DO_SAVE_MIN(cover, mov r30=cr.ifs, , RSE_WORKAROUND)
-#define SAVE_MIN_WITH_COVER_R19 DO_SAVE_MIN(cover, mov r30=cr.ifs, mov r15=r19, RSE_WORKAROUND)
+#define SAVE_MIN_WITH_COVER DO_SAVE_MIN(COVER, mov r30=cr.ifs, , RSE_WORKAROUND)
+#define SAVE_MIN_WITH_COVER_R19 DO_SAVE_MIN(COVER, mov r30=cr.ifs, mov r15=r19, RSE_WORKAROUND)
#define SAVE_MIN DO_SAVE_MIN( , mov r30=r0, , )
diff --git a/arch/ia64/kernel/nr-irqs.c b/arch/ia64/kernel/nr-irqs.c
new file mode 100644
index 000000000000..1ae049181e83
--- /dev/null
+++ b/arch/ia64/kernel/nr-irqs.c
@@ -0,0 +1,24 @@
+/*
+ * calculate
+ * NR_IRQS = max(IA64_NATIVE_NR_IRQS, XEN_NR_IRQS, FOO_NR_IRQS...)
+ * depending on config.
+ * This must be calculated before processing asm-offset.c.
+ */
+
+#define ASM_OFFSETS_C 1
+
+#include <linux/kbuild.h>
+#include <linux/threads.h>
+#include <asm-ia64/native/irq.h>
+
+void foo(void)
+{
+ union paravirt_nr_irqs_max {
+ char ia64_native_nr_irqs[IA64_NATIVE_NR_IRQS];
+#ifdef CONFIG_XEN
+ char xen_nr_irqs[XEN_NR_IRQS];
+#endif
+ };
+
+ DEFINE(NR_IRQS, sizeof (union paravirt_nr_irqs_max));
+}
diff --git a/arch/ia64/kernel/paravirt.c b/arch/ia64/kernel/paravirt.c
new file mode 100644
index 000000000000..afaf5b9a2cf0
--- /dev/null
+++ b/arch/ia64/kernel/paravirt.c
@@ -0,0 +1,369 @@
+/******************************************************************************
+ * arch/ia64/kernel/paravirt.c
+ *
+ * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ * Yaozu (Eddie) Dong <eddie.dong@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/init.h>
+
+#include <linux/compiler.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include <asm/iosapic.h>
+#include <asm/paravirt.h>
+
+/***************************************************************************
+ * general info
+ */
+struct pv_info pv_info = {
+ .kernel_rpl = 0,
+ .paravirt_enabled = 0,
+ .name = "bare hardware"
+};
+
+/***************************************************************************
+ * pv_init_ops
+ * initialization hooks.
+ */
+
+struct pv_init_ops pv_init_ops;
+
+/***************************************************************************
+ * pv_cpu_ops
+ * intrinsics hooks.
+ */
+
+/* ia64_native_xxx are macros so that we have to make them real functions */
+
+#define DEFINE_VOID_FUNC1(name) \
+ static void \
+ ia64_native_ ## name ## _func(unsigned long arg) \
+ { \
+ ia64_native_ ## name(arg); \
+ } \
+
+#define DEFINE_VOID_FUNC2(name) \
+ static void \
+ ia64_native_ ## name ## _func(unsigned long arg0, \
+ unsigned long arg1) \
+ { \
+ ia64_native_ ## name(arg0, arg1); \
+ } \
+
+#define DEFINE_FUNC0(name) \
+ static unsigned long \
+ ia64_native_ ## name ## _func(void) \
+ { \
+ return ia64_native_ ## name(); \
+ }
+
+#define DEFINE_FUNC1(name, type) \
+ static unsigned long \
+ ia64_native_ ## name ## _func(type arg) \
+ { \
+ return ia64_native_ ## name(arg); \
+ } \
+
+DEFINE_VOID_FUNC1(fc);
+DEFINE_VOID_FUNC1(intrin_local_irq_restore);
+
+DEFINE_VOID_FUNC2(ptcga);
+DEFINE_VOID_FUNC2(set_rr);
+
+DEFINE_FUNC0(get_psr_i);
+
+DEFINE_FUNC1(thash, unsigned long);
+DEFINE_FUNC1(get_cpuid, int);
+DEFINE_FUNC1(get_pmd, int);
+DEFINE_FUNC1(get_rr, unsigned long);
+
+static void
+ia64_native_ssm_i_func(void)
+{
+ ia64_native_ssm(IA64_PSR_I);
+}
+
+static void
+ia64_native_rsm_i_func(void)
+{
+ ia64_native_rsm(IA64_PSR_I);
+}
+
+static void
+ia64_native_set_rr0_to_rr4_func(unsigned long val0, unsigned long val1,
+ unsigned long val2, unsigned long val3,
+ unsigned long val4)
+{
+ ia64_native_set_rr0_to_rr4(val0, val1, val2, val3, val4);
+}
+
+#define CASE_GET_REG(id) \
+ case _IA64_REG_ ## id: \
+ res = ia64_native_getreg(_IA64_REG_ ## id); \
+ break;
+#define CASE_GET_AR(id) CASE_GET_REG(AR_ ## id)
+#define CASE_GET_CR(id) CASE_GET_REG(CR_ ## id)
+
+unsigned long
+ia64_native_getreg_func(int regnum)
+{
+ unsigned long res = -1;
+ switch (regnum) {
+ CASE_GET_REG(GP);
+ CASE_GET_REG(IP);
+ CASE_GET_REG(PSR);
+ CASE_GET_REG(TP);
+ CASE_GET_REG(SP);
+
+ CASE_GET_AR(KR0);
+ CASE_GET_AR(KR1);
+ CASE_GET_AR(KR2);
+ CASE_GET_AR(KR3);
+ CASE_GET_AR(KR4);
+ CASE_GET_AR(KR5);
+ CASE_GET_AR(KR6);
+ CASE_GET_AR(KR7);
+ CASE_GET_AR(RSC);
+ CASE_GET_AR(BSP);
+ CASE_GET_AR(BSPSTORE);
+ CASE_GET_AR(RNAT);
+ CASE_GET_AR(FCR);
+ CASE_GET_AR(EFLAG);
+ CASE_GET_AR(CSD);
+ CASE_GET_AR(SSD);
+ CASE_GET_AR(CFLAG);
+ CASE_GET_AR(FSR);
+ CASE_GET_AR(FIR);
+ CASE_GET_AR(FDR);
+ CASE_GET_AR(CCV);
+ CASE_GET_AR(UNAT);
+ CASE_GET_AR(FPSR);
+ CASE_GET_AR(ITC);
+ CASE_GET_AR(PFS);
+ CASE_GET_AR(LC);
+ CASE_GET_AR(EC);
+
+ CASE_GET_CR(DCR);
+ CASE_GET_CR(ITM);
+ CASE_GET_CR(IVA);
+ CASE_GET_CR(PTA);
+ CASE_GET_CR(IPSR);
+ CASE_GET_CR(ISR);
+ CASE_GET_CR(IIP);
+ CASE_GET_CR(IFA);
+ CASE_GET_CR(ITIR);
+ CASE_GET_CR(IIPA);
+ CASE_GET_CR(IFS);
+ CASE_GET_CR(IIM);
+ CASE_GET_CR(IHA);
+ CASE_GET_CR(LID);
+ CASE_GET_CR(IVR);
+ CASE_GET_CR(TPR);
+ CASE_GET_CR(EOI);
+ CASE_GET_CR(IRR0);
+ CASE_GET_CR(IRR1);
+ CASE_GET_CR(IRR2);
+ CASE_GET_CR(IRR3);
+ CASE_GET_CR(ITV);
+ CASE_GET_CR(PMV);
+ CASE_GET_CR(CMCV);
+ CASE_GET_CR(LRR0);
+ CASE_GET_CR(LRR1);
+
+ default:
+ printk(KERN_CRIT "wrong_getreg %d\n", regnum);
+ break;
+ }
+ return res;
+}
+
+#define CASE_SET_REG(id) \
+ case _IA64_REG_ ## id: \
+ ia64_native_setreg(_IA64_REG_ ## id, val); \
+ break;
+#define CASE_SET_AR(id) CASE_SET_REG(AR_ ## id)
+#define CASE_SET_CR(id) CASE_SET_REG(CR_ ## id)
+
+void
+ia64_native_setreg_func(int regnum, unsigned long val)
+{
+ switch (regnum) {
+ case _IA64_REG_PSR_L:
+ ia64_native_setreg(_IA64_REG_PSR_L, val);
+ ia64_dv_serialize_data();
+ break;
+ CASE_SET_REG(SP);
+ CASE_SET_REG(GP);
+
+ CASE_SET_AR(KR0);
+ CASE_SET_AR(KR1);
+ CASE_SET_AR(KR2);
+ CASE_SET_AR(KR3);
+ CASE_SET_AR(KR4);
+ CASE_SET_AR(KR5);
+ CASE_SET_AR(KR6);
+ CASE_SET_AR(KR7);
+ CASE_SET_AR(RSC);
+ CASE_SET_AR(BSP);
+ CASE_SET_AR(BSPSTORE);
+ CASE_SET_AR(RNAT);
+ CASE_SET_AR(FCR);
+ CASE_SET_AR(EFLAG);
+ CASE_SET_AR(CSD);
+ CASE_SET_AR(SSD);
+ CASE_SET_AR(CFLAG);
+ CASE_SET_AR(FSR);
+ CASE_SET_AR(FIR);
+ CASE_SET_AR(FDR);
+ CASE_SET_AR(CCV);
+ CASE_SET_AR(UNAT);
+ CASE_SET_AR(FPSR);
+ CASE_SET_AR(ITC);
+ CASE_SET_AR(PFS);
+ CASE_SET_AR(LC);
+ CASE_SET_AR(EC);
+
+ CASE_SET_CR(DCR);
+ CASE_SET_CR(ITM);
+ CASE_SET_CR(IVA);
+ CASE_SET_CR(PTA);
+ CASE_SET_CR(IPSR);
+ CASE_SET_CR(ISR);
+ CASE_SET_CR(IIP);
+ CASE_SET_CR(IFA);
+ CASE_SET_CR(ITIR);
+ CASE_SET_CR(IIPA);
+ CASE_SET_CR(IFS);
+ CASE_SET_CR(IIM);
+ CASE_SET_CR(IHA);
+ CASE_SET_CR(LID);
+ CASE_SET_CR(IVR);
+ CASE_SET_CR(TPR);
+ CASE_SET_CR(EOI);
+ CASE_SET_CR(IRR0);
+ CASE_SET_CR(IRR1);
+ CASE_SET_CR(IRR2);
+ CASE_SET_CR(IRR3);
+ CASE_SET_CR(ITV);
+ CASE_SET_CR(PMV);
+ CASE_SET_CR(CMCV);
+ CASE_SET_CR(LRR0);
+ CASE_SET_CR(LRR1);
+ default:
+ printk(KERN_CRIT "wrong setreg %d\n", regnum);
+ break;
+ }
+}
+
+struct pv_cpu_ops pv_cpu_ops = {
+ .fc = ia64_native_fc_func,
+ .thash = ia64_native_thash_func,
+ .get_cpuid = ia64_native_get_cpuid_func,
+ .get_pmd = ia64_native_get_pmd_func,
+ .ptcga = ia64_native_ptcga_func,
+ .get_rr = ia64_native_get_rr_func,
+ .set_rr = ia64_native_set_rr_func,
+ .set_rr0_to_rr4 = ia64_native_set_rr0_to_rr4_func,
+ .ssm_i = ia64_native_ssm_i_func,
+ .getreg = ia64_native_getreg_func,
+ .setreg = ia64_native_setreg_func,
+ .rsm_i = ia64_native_rsm_i_func,
+ .get_psr_i = ia64_native_get_psr_i_func,
+ .intrin_local_irq_restore
+ = ia64_native_intrin_local_irq_restore_func,
+};
+EXPORT_SYMBOL(pv_cpu_ops);
+
+/******************************************************************************
+ * replacement of hand written assembly codes.
+ */
+
+void
+paravirt_cpu_asm_init(const struct pv_cpu_asm_switch *cpu_asm_switch)
+{
+ extern unsigned long paravirt_switch_to_targ;
+ extern unsigned long paravirt_leave_syscall_targ;
+ extern unsigned long paravirt_work_processed_syscall_targ;
+ extern unsigned long paravirt_leave_kernel_targ;
+
+ paravirt_switch_to_targ = cpu_asm_switch->switch_to;
+ paravirt_leave_syscall_targ = cpu_asm_switch->leave_syscall;
+ paravirt_work_processed_syscall_targ =
+ cpu_asm_switch->work_processed_syscall;
+ paravirt_leave_kernel_targ = cpu_asm_switch->leave_kernel;
+}
+
+/***************************************************************************
+ * pv_iosapic_ops
+ * iosapic read/write hooks.
+ */
+
+static unsigned int
+ia64_native_iosapic_read(char __iomem *iosapic, unsigned int reg)
+{
+ return __ia64_native_iosapic_read(iosapic, reg);
+}
+
+static void
+ia64_native_iosapic_write(char __iomem *iosapic, unsigned int reg, u32 val)
+{
+ __ia64_native_iosapic_write(iosapic, reg, val);
+}
+
+struct pv_iosapic_ops pv_iosapic_ops = {
+ .pcat_compat_init = ia64_native_iosapic_pcat_compat_init,
+ .get_irq_chip = ia64_native_iosapic_get_irq_chip,
+
+ .__read = ia64_native_iosapic_read,
+ .__write = ia64_native_iosapic_write,
+};
+
+/***************************************************************************
+ * pv_irq_ops
+ * irq operations
+ */
+
+struct pv_irq_ops pv_irq_ops = {
+ .register_ipi = ia64_native_register_ipi,
+
+ .assign_irq_vector = ia64_native_assign_irq_vector,
+ .free_irq_vector = ia64_native_free_irq_vector,
+ .register_percpu_irq = ia64_native_register_percpu_irq,
+
+ .resend_irq = ia64_native_resend_irq,
+};
+
+/***************************************************************************
+ * pv_time_ops
+ * time operations
+ */
+
+static int
+ia64_native_do_steal_accounting(unsigned long *new_itm)
+{
+ return 0;
+}
+
+struct pv_time_ops pv_time_ops = {
+ .do_steal_accounting = ia64_native_do_steal_accounting,
+};
diff --git a/arch/ia64/kernel/paravirt_inst.h b/arch/ia64/kernel/paravirt_inst.h
new file mode 100644
index 000000000000..5cad6fb2ed19
--- /dev/null
+++ b/arch/ia64/kernel/paravirt_inst.h
@@ -0,0 +1,29 @@
+/******************************************************************************
+ * linux/arch/ia64/xen/paravirt_inst.h
+ *
+ * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * 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
+ *
+ */
+
+#ifdef __IA64_ASM_PARAVIRTUALIZED_XEN
+#include <asm/xen/inst.h>
+#include <asm/xen/minstate.h>
+#else
+#include <asm/native/inst.h>
+#endif
+
diff --git a/arch/ia64/kernel/paravirtentry.S b/arch/ia64/kernel/paravirtentry.S
new file mode 100644
index 000000000000..2f42fcb9776a
--- /dev/null
+++ b/arch/ia64/kernel/paravirtentry.S
@@ -0,0 +1,60 @@
+/******************************************************************************
+ * linux/arch/ia64/xen/paravirtentry.S
+ *
+ * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * 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 <asm/asmmacro.h>
+#include <asm/asm-offsets.h>
+#include "entry.h"
+
+#define DATA8(sym, init_value) \
+ .pushsection .data.read_mostly ; \
+ .align 8 ; \
+ .global sym ; \
+ sym: ; \
+ data8 init_value ; \
+ .popsection
+
+#define BRANCH(targ, reg, breg) \
+ movl reg=targ ; \
+ ;; \
+ ld8 reg=[reg] ; \
+ ;; \
+ mov breg=reg ; \
+ br.cond.sptk.many breg
+
+#define BRANCH_PROC(sym, reg, breg) \
+ DATA8(paravirt_ ## sym ## _targ, ia64_native_ ## sym) ; \
+ GLOBAL_ENTRY(paravirt_ ## sym) ; \
+ BRANCH(paravirt_ ## sym ## _targ, reg, breg) ; \
+ END(paravirt_ ## sym)
+
+#define BRANCH_PROC_UNWINFO(sym, reg, breg) \
+ DATA8(paravirt_ ## sym ## _targ, ia64_native_ ## sym) ; \
+ GLOBAL_ENTRY(paravirt_ ## sym) ; \
+ PT_REGS_UNWIND_INFO(0) ; \
+ BRANCH(paravirt_ ## sym ## _targ, reg, breg) ; \
+ END(paravirt_ ## sym)
+
+
+BRANCH_PROC(switch_to, r22, b7)
+BRANCH_PROC_UNWINFO(leave_syscall, r22, b7)
+BRANCH_PROC(work_processed_syscall, r2, b7)
+BRANCH_PROC_UNWINFO(leave_kernel, r22, b7)
diff --git a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c
index f48a809c686d..750749551e86 100644
--- a/arch/ia64/kernel/setup.c
+++ b/arch/ia64/kernel/setup.c
@@ -51,6 +51,7 @@
#include <asm/mca.h>
#include <asm/meminit.h>
#include <asm/page.h>
+#include <asm/paravirt.h>
#include <asm/patch.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
@@ -341,6 +342,8 @@ reserve_memory (void)
rsvd_region[n].end = (unsigned long) ia64_imva(_end);
n++;
+ n += paravirt_reserve_memory(&rsvd_region[n]);
+
#ifdef CONFIG_BLK_DEV_INITRD
if (ia64_boot_param->initrd_start) {
rsvd_region[n].start = (unsigned long)__va(ia64_boot_param->initrd_start);
@@ -519,6 +522,8 @@ setup_arch (char **cmdline_p)
{
unw_init();
+ paravirt_arch_setup_early();
+
ia64_patch_vtop((u64) __start___vtop_patchlist, (u64) __end___vtop_patchlist);
*cmdline_p = __va(ia64_boot_param->command_line);
@@ -584,6 +589,9 @@ setup_arch (char **cmdline_p)
acpi_boot_init();
#endif
+ paravirt_banner();
+ paravirt_arch_setup_console(cmdline_p);
+
#ifdef CONFIG_VT
if (!conswitchp) {
# if defined(CONFIG_DUMMY_CONSOLE)
@@ -603,6 +611,8 @@ setup_arch (char **cmdline_p)
#endif
/* enable IA-64 Machine Check Abort Handling unless disabled */
+ if (paravirt_arch_setup_nomca())
+ nomca = 1;
if (!nomca)
ia64_mca_init();
diff --git a/arch/ia64/kernel/smpboot.c b/arch/ia64/kernel/smpboot.c
index d7ad42b77d41..933f38811528 100644
--- a/arch/ia64/kernel/smpboot.c
+++ b/arch/ia64/kernel/smpboot.c
@@ -50,6 +50,7 @@
#include <asm/machvec.h>
#include <asm/mca.h>
#include <asm/page.h>
+#include <asm/paravirt.h>
#include <asm/pgalloc.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
@@ -642,6 +643,7 @@ void __devinit smp_prepare_boot_cpu(void)
cpu_set(smp_processor_id(), cpu_online_map);
cpu_set(smp_processor_id(), cpu_callin_map);
per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE;
+ paravirt_post_smp_prepare_boot_cpu();
}
#ifdef CONFIG_HOTPLUG_CPU
diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
index 8c73643f2d66..046ca89efc05 100644
--- a/arch/ia64/kernel/time.c
+++ b/arch/ia64/kernel/time.c
@@ -24,6 +24,7 @@
#include <asm/machvec.h>
#include <asm/delay.h>
#include <asm/hw_irq.h>
+#include <asm/paravirt.h>
#include <asm/ptrace.h>
#include <asm/sal.h>
#include <asm/sections.h>
@@ -48,6 +49,15 @@ EXPORT_SYMBOL(last_cli_ip);
#endif
+#ifdef CONFIG_PARAVIRT
+static void
+paravirt_clocksource_resume(void)
+{
+ if (pv_time_ops.clocksource_resume)
+ pv_time_ops.clocksource_resume();
+}
+#endif
+
static struct clocksource clocksource_itc = {
.name = "itc",
.rating = 350,
@@ -56,6 +66,9 @@ static struct clocksource clocksource_itc = {
.mult = 0, /*to be calculated*/
.shift = 16,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
+#ifdef CONFIG_PARAVIRT
+ .resume = paravirt_clocksource_resume,
+#endif
};
static struct clocksource *itc_clocksource;
@@ -156,6 +169,9 @@ timer_interrupt (int irq, void *dev_id)
profile_tick(CPU_PROFILING);
+ if (paravirt_do_steal_accounting(&new_itm))
+ goto skip_process_time_accounting;
+
while (1) {
update_process_times(user_mode(get_irq_regs()));
@@ -185,6 +201,8 @@ timer_interrupt (int irq, void *dev_id)
local_irq_disable();
}
+skip_process_time_accounting:
+
do {
/*
* If we're too close to the next clock tick for
@@ -334,6 +352,11 @@ ia64_init_itm (void)
*/
clocksource_itc.rating = 50;
+ paravirt_init_missing_ticks_accounting(smp_processor_id());
+
+ /* avoid softlock up message when cpu is unplug and plugged again. */
+ touch_softlockup_watchdog();
+
/* Setup the CPU local timer tick */
ia64_cpu_local_tick();
diff --git a/arch/ia64/kernel/vmlinux.lds.S b/arch/ia64/kernel/vmlinux.lds.S
index 5929ab10a289..5a77206c2492 100644
--- a/arch/ia64/kernel/vmlinux.lds.S
+++ b/arch/ia64/kernel/vmlinux.lds.S
@@ -4,7 +4,6 @@
#include <asm/system.h>
#include <asm/pgtable.h>
-#define LOAD_OFFSET (KERNEL_START - KERNEL_TR_PAGE_SIZE)
#include <asm-generic/vmlinux.lds.h>
#define IVT_TEXT \
diff --git a/arch/ia64/kvm/Makefile b/arch/ia64/kvm/Makefile
index 112791dd2542..bf22fb9e6dcf 100644
--- a/arch/ia64/kvm/Makefile
+++ b/arch/ia64/kvm/Makefile
@@ -43,7 +43,8 @@ $(obj)/$(offsets-file): arch/ia64/kvm/asm-offsets.s
EXTRA_CFLAGS += -Ivirt/kvm -Iarch/ia64/kvm/
EXTRA_AFLAGS += -Ivirt/kvm -Iarch/ia64/kvm/
-common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o ioapic.o)
+common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o ioapic.o \
+ coalesced_mmio.o)
kvm-objs := $(common-objs) kvm-ia64.o kvm_fw.o
obj-$(CONFIG_KVM) += kvm.o
diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c
index 318b81100623..a4cf4a25d0e8 100644
--- a/arch/ia64/kvm/kvm-ia64.c
+++ b/arch/ia64/kvm/kvm-ia64.c
@@ -187,6 +187,9 @@ int kvm_dev_ioctl_check_extension(long ext)
r = 1;
break;
+ case KVM_CAP_COALESCED_MMIO:
+ r = KVM_COALESCED_MMIO_PAGE_OFFSET;
+ break;
default:
r = 0;
}
@@ -195,11 +198,11 @@ int kvm_dev_ioctl_check_extension(long ext)
}
static struct kvm_io_device *vcpu_find_mmio_dev(struct kvm_vcpu *vcpu,
- gpa_t addr)
+ gpa_t addr, int len, int is_write)
{
struct kvm_io_device *dev;
- dev = kvm_io_bus_find_dev(&vcpu->kvm->mmio_bus, addr);
+ dev = kvm_io_bus_find_dev(&vcpu->kvm->mmio_bus, addr, len, is_write);
return dev;
}
@@ -231,7 +234,7 @@ static int handle_mmio(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
kvm_run->exit_reason = KVM_EXIT_MMIO;
return 0;
mmio:
- mmio_dev = vcpu_find_mmio_dev(vcpu, p->addr);
+ mmio_dev = vcpu_find_mmio_dev(vcpu, p->addr, p->size, !p->dir);
if (mmio_dev) {
if (!p->dir)
kvm_iodevice_write(mmio_dev, p->addr, p->size,
@@ -1035,14 +1038,6 @@ static void kvm_free_vmm_area(void)
}
}
-/*
- * Make sure that a cpu that is being hot-unplugged does not have any vcpus
- * cached on it. Leave it as blank for IA64.
- */
-void decache_vcpus_on_cpu(int cpu)
-{
-}
-
static void vti_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
{
}
diff --git a/arch/ia64/kvm/mmio.c b/arch/ia64/kvm/mmio.c
index 351bf70da463..7f1a858bc69f 100644
--- a/arch/ia64/kvm/mmio.c
+++ b/arch/ia64/kvm/mmio.c
@@ -159,7 +159,8 @@ static void mmio_access(struct kvm_vcpu *vcpu, u64 src_pa, u64 *dest,
if (p->u.ioreq.state == STATE_IORESP_READY) {
if (dir == IOREQ_READ)
- *dest = p->u.ioreq.data;
+ /* it's necessary to ensure zero extending */
+ *dest = p->u.ioreq.data & (~0UL >> (64-(s*8)));
} else
panic_vm(vcpu);
out:
diff --git a/arch/ia64/sn/kernel/irq.c b/arch/ia64/sn/kernel/irq.c
index 53351c3cd7b1..96c31b4180c3 100644
--- a/arch/ia64/sn/kernel/irq.c
+++ b/arch/ia64/sn/kernel/irq.c
@@ -11,6 +11,7 @@
#include <linux/irq.h>
#include <linux/spinlock.h>
#include <linux/init.h>
+#include <linux/rculist.h>
#include <asm/sn/addrs.h>
#include <asm/sn/arch.h>
#include <asm/sn/intr.h>
diff --git a/arch/mips/kernel/rtlx.c b/arch/mips/kernel/rtlx.c
index b88f1c18ff4d..598048a7c9e7 100644
--- a/arch/mips/kernel/rtlx.c
+++ b/arch/mips/kernel/rtlx.c
@@ -517,8 +517,8 @@ static int __init rtlx_module_init(void)
atomic_set(&channel_wqs[i].in_open, 0);
mutex_init(&channel_wqs[i].mutex);
- dev = device_create(mt_class, NULL, MKDEV(major, i),
- "%s%d", module_name, i);
+ dev = device_create_drvdata(mt_class, NULL, MKDEV(major, i),
+ NULL, "%s%d", module_name, i);
if (IS_ERR(dev)) {
err = PTR_ERR(dev);
goto out_chrdev;
diff --git a/arch/mips/sibyte/common/sb_tbprof.c b/arch/mips/sibyte/common/sb_tbprof.c
index 63b444eaf01e..edeb77e17bdd 100644
--- a/arch/mips/sibyte/common/sb_tbprof.c
+++ b/arch/mips/sibyte/common/sb_tbprof.c
@@ -565,7 +565,8 @@ static int __init sbprof_tb_init(void)
tb_class = tbc;
- dev = device_create(tbc, NULL, MKDEV(SBPROF_TB_MAJOR, 0), "tb");
+ dev = device_create_drvdata(tbc, NULL, MKDEV(SBPROF_TB_MAJOR, 0),
+ NULL, "tb");
if (IS_ERR(dev)) {
err = PTR_ERR(dev);
goto out_class;
diff --git a/arch/parisc/kernel/vmlinux.lds.S b/arch/parisc/kernel/vmlinux.lds.S
index 50b4a3a25d0a..ff7d4ff4675a 100644
--- a/arch/parisc/kernel/vmlinux.lds.S
+++ b/arch/parisc/kernel/vmlinux.lds.S
@@ -66,7 +66,6 @@ SECTIONS
_etext = .;
RODATA
- BUG_TABLE
/* writeable */
/* Make sure this is page aligned so
diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 3934e2659407..a5e9912e2d37 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -105,11 +105,13 @@ config ARCH_NO_VIRT_TO_BUS
config PPC
bool
default y
+ select HAVE_DYNAMIC_FTRACE
+ select HAVE_FTRACE
select HAVE_IDE
- select HAVE_OPROFILE
select HAVE_KPROBES
select HAVE_KRETPROBES
select HAVE_LMB
+ select HAVE_OPROFILE
config EARLY_PRINTK
bool
diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 2346d271fbfd..f3f5e2641432 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -12,6 +12,18 @@ CFLAGS_prom_init.o += -fPIC
CFLAGS_btext.o += -fPIC
endif
+ifdef CONFIG_FTRACE
+# Do not trace early boot code
+CFLAGS_REMOVE_cputable.o = -pg
+CFLAGS_REMOVE_prom_init.o = -pg
+
+ifdef CONFIG_DYNAMIC_FTRACE
+# dynamic ftrace setup.
+CFLAGS_REMOVE_ftrace.o = -pg
+endif
+
+endif
+
obj-y := cputable.o ptrace.o syscalls.o \
irq.o align.o signal_32.o pmc.o vdso.o \
init_task.o process.o systbl.o idle.o \
@@ -78,6 +90,8 @@ obj-$(CONFIG_KEXEC) += machine_kexec.o crash.o \
obj-$(CONFIG_AUDIT) += audit.o
obj64-$(CONFIG_AUDIT) += compat_audit.o
+obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
+
obj-$(CONFIG_8XX_MINIMAL_FPEMU) += softemu8xx.o
ifneq ($(CONFIG_PPC_INDIRECT_IO),y)
diff --git a/arch/powerpc/kernel/entry_32.S b/arch/powerpc/kernel/entry_32.S
index 0c8614d9875c..3b1dd29d9f91 100644
--- a/arch/powerpc/kernel/entry_32.S
+++ b/arch/powerpc/kernel/entry_32.S
@@ -1035,3 +1035,126 @@ machine_check_in_rtas:
/* XXX load up BATs and panic */
#endif /* CONFIG_PPC_RTAS */
+
+#ifdef CONFIG_FTRACE
+#ifdef CONFIG_DYNAMIC_FTRACE
+_GLOBAL(mcount)
+_GLOBAL(_mcount)
+ stwu r1,-48(r1)
+ stw r3, 12(r1)
+ stw r4, 16(r1)
+ stw r5, 20(r1)
+ stw r6, 24(r1)
+ mflr r3
+ stw r7, 28(r1)
+ mfcr r5
+ stw r8, 32(r1)
+ stw r9, 36(r1)
+ stw r10,40(r1)
+ stw r3, 44(r1)
+ stw r5, 8(r1)
+ .globl mcount_call
+mcount_call:
+ bl ftrace_stub
+ nop
+ lwz r6, 8(r1)
+ lwz r0, 44(r1)
+ lwz r3, 12(r1)
+ mtctr r0
+ lwz r4, 16(r1)
+ mtcr r6
+ lwz r5, 20(r1)
+ lwz r6, 24(r1)
+ lwz r0, 52(r1)
+ lwz r7, 28(r1)
+ lwz r8, 32(r1)
+ mtlr r0
+ lwz r9, 36(r1)
+ lwz r10,40(r1)
+ addi r1, r1, 48
+ bctr
+
+_GLOBAL(ftrace_caller)
+ /* Based off of objdump optput from glibc */
+ stwu r1,-48(r1)
+ stw r3, 12(r1)
+ stw r4, 16(r1)
+ stw r5, 20(r1)
+ stw r6, 24(r1)
+ mflr r3
+ lwz r4, 52(r1)
+ mfcr r5
+ stw r7, 28(r1)
+ stw r8, 32(r1)
+ stw r9, 36(r1)
+ stw r10,40(r1)
+ stw r3, 44(r1)
+ stw r5, 8(r1)
+.globl ftrace_call
+ftrace_call:
+ bl ftrace_stub
+ nop
+ lwz r6, 8(r1)
+ lwz r0, 44(r1)
+ lwz r3, 12(r1)
+ mtctr r0
+ lwz r4, 16(r1)
+ mtcr r6
+ lwz r5, 20(r1)
+ lwz r6, 24(r1)
+ lwz r0, 52(r1)
+ lwz r7, 28(r1)
+ lwz r8, 32(r1)
+ mtlr r0
+ lwz r9, 36(r1)
+ lwz r10,40(r1)
+ addi r1, r1, 48
+ bctr
+#else
+_GLOBAL(mcount)
+_GLOBAL(_mcount)
+ stwu r1,-48(r1)
+ stw r3, 12(r1)
+ stw r4, 16(r1)
+ stw r5, 20(r1)
+ stw r6, 24(r1)
+ mflr r3
+ lwz r4, 52(r1)
+ mfcr r5
+ stw r7, 28(r1)
+ stw r8, 32(r1)
+ stw r9, 36(r1)
+ stw r10,40(r1)
+ stw r3, 44(r1)
+ stw r5, 8(r1)
+
+ LOAD_REG_ADDR(r5, ftrace_trace_function)
+ lwz r5,0(r5)
+
+ mtctr r5
+ bctrl
+
+ nop
+
+ lwz r6, 8(r1)
+ lwz r0, 44(r1)
+ lwz r3, 12(r1)
+ mtctr r0
+ lwz r4, 16(r1)
+ mtcr r6
+ lwz r5, 20(r1)
+ lwz r6, 24(r1)
+ lwz r0, 52(r1)
+ lwz r7, 28(r1)
+ lwz r8, 32(r1)
+ mtlr r0
+ lwz r9, 36(r1)
+ lwz r10,40(r1)
+ addi r1, r1, 48
+ bctr
+#endif
+
+_GLOBAL(ftrace_stub)
+ blr
+
+#endif /* CONFIG_MCOUNT */
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
index c0db5b769e55..2c4d9e056ead 100644
--- a/arch/powerpc/kernel/entry_64.S
+++ b/arch/powerpc/kernel/entry_64.S
@@ -870,3 +870,65 @@ _GLOBAL(enter_prom)
ld r0,16(r1)
mtlr r0
blr
+
+#ifdef CONFIG_FTRACE
+#ifdef CONFIG_DYNAMIC_FTRACE
+_GLOBAL(mcount)
+_GLOBAL(_mcount)
+ /* Taken from output of objdump from lib64/glibc */
+ mflr r3
+ stdu r1, -112(r1)
+ std r3, 128(r1)
+ .globl mcount_call
+mcount_call:
+ bl ftrace_stub
+ nop
+ ld r0, 128(r1)
+ mtlr r0
+ addi r1, r1, 112
+ blr
+
+_GLOBAL(ftrace_caller)
+ /* Taken from output of objdump from lib64/glibc */
+ mflr r3
+ ld r11, 0(r1)
+ stdu r1, -112(r1)
+ std r3, 128(r1)
+ ld r4, 16(r11)
+.globl ftrace_call
+ftrace_call:
+ bl ftrace_stub
+ nop
+ ld r0, 128(r1)
+ mtlr r0
+ addi r1, r1, 112
+_GLOBAL(ftrace_stub)
+ blr
+#else
+_GLOBAL(mcount)
+ blr
+
+_GLOBAL(_mcount)
+ /* Taken from output of objdump from lib64/glibc */
+ mflr r3
+ ld r11, 0(r1)
+ stdu r1, -112(r1)
+ std r3, 128(r1)
+ ld r4, 16(r11)
+
+
+ LOAD_REG_ADDR(r5,ftrace_trace_function)
+ ld r5,0(r5)
+ ld r5,0(r5)
+ mtctr r5
+ bctrl
+
+ nop
+ ld r0, 128(r1)
+ mtlr r0
+ addi r1, r1, 112
+_GLOBAL(ftrace_stub)
+ blr
+
+#endif
+#endif
diff --git a/arch/powerpc/kernel/ftrace.c b/arch/powerpc/kernel/ftrace.c
new file mode 100644
index 000000000000..e12c593ab9ca
--- /dev/null
+++ b/arch/powerpc/kernel/ftrace.c
@@ -0,0 +1,161 @@
+/*
+ * Code for replacing ftrace calls with jumps.
+ *
+ * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
+ *
+ * Thanks goes out to P.A. Semi, Inc for supplying me with a PPC64 box.
+ *
+ */
+
+#include <linux/spinlock.h>
+#include <linux/hardirq.h>
+#include <linux/ftrace.h>
+#include <linux/percpu.h>
+#include <linux/init.h>
+#include <linux/list.h>
+
+#include <asm/cacheflush.h>
+
+#define CALL_BACK 4
+
+static unsigned int ftrace_nop = 0x60000000;
+
+#ifdef CONFIG_PPC32
+# define GET_ADDR(addr) addr
+#else
+/* PowerPC64's functions are data that points to the functions */
+# define GET_ADDR(addr) *(unsigned long *)addr
+#endif
+
+static unsigned int notrace ftrace_calc_offset(long ip, long addr)
+{
+ return (int)((addr + CALL_BACK) - ip);
+}
+
+notrace unsigned char *ftrace_nop_replace(void)
+{
+ return (char *)&ftrace_nop;
+}
+
+notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
+{
+ static unsigned int op;
+
+ /*
+ * It would be nice to just use create_function_call, but that will
+ * update the code itself. Here we need to just return the
+ * instruction that is going to be modified, without modifying the
+ * code.
+ */
+ addr = GET_ADDR(addr);
+
+ /* Set to "bl addr" */
+ op = 0x48000001 | (ftrace_calc_offset(ip, addr) & 0x03fffffc);
+
+ /*
+ * No locking needed, this must be called via kstop_machine
+ * which in essence is like running on a uniprocessor machine.
+ */
+ return (unsigned char *)&op;
+}
+
+#ifdef CONFIG_PPC64
+# define _ASM_ALIGN " .align 3 "
+# define _ASM_PTR " .llong "
+#else
+# define _ASM_ALIGN " .align 2 "
+# define _ASM_PTR " .long "
+#endif
+
+notrace int
+ftrace_modify_code(unsigned long ip, unsigned char *old_code,
+ unsigned char *new_code)
+{
+ unsigned replaced;
+ unsigned old = *(unsigned *)old_code;
+ unsigned new = *(unsigned *)new_code;
+ int faulted = 0;
+
+ /* move the IP back to the start of the call */
+ ip -= CALL_BACK;
+
+ /*
+ * Note: Due to modules and __init, code can
+ * disappear and change, we need to protect against faulting
+ * as well as code changing.
+ *
+ * No real locking needed, this code is run through
+ * kstop_machine.
+ */
+ asm volatile (
+ "1: lwz %1, 0(%2)\n"
+ " cmpw %1, %5\n"
+ " bne 2f\n"
+ " stwu %3, 0(%2)\n"
+ "2:\n"
+ ".section .fixup, \"ax\"\n"
+ "3: li %0, 1\n"
+ " b 2b\n"
+ ".previous\n"
+ ".section __ex_table,\"a\"\n"
+ _ASM_ALIGN "\n"
+ _ASM_PTR "1b, 3b\n"
+ ".previous"
+ : "=r"(faulted), "=r"(replaced)
+ : "r"(ip), "r"(new),
+ "0"(faulted), "r"(old)
+ : "memory");
+
+ if (replaced != old && replaced != new)
+ faulted = 2;
+
+ if (!faulted)
+ flush_icache_range(ip, ip + 8);
+
+ return faulted;
+}
+
+notrace int ftrace_update_ftrace_func(ftrace_func_t func)
+{
+ unsigned long ip = (unsigned long)(&ftrace_call);
+ unsigned char old[4], *new;
+ int ret;
+
+ ip += CALL_BACK;
+
+ memcpy(old, &ftrace_call, 4);
+ new = ftrace_call_replace(ip, (unsigned long)func);
+ ret = ftrace_modify_code(ip, old, new);
+
+ return ret;
+}
+
+notrace int ftrace_mcount_set(unsigned long *data)
+{
+ unsigned long ip = (long)(&mcount_call);
+ unsigned long *addr = data;
+ unsigned char old[4], *new;
+
+ /* ip is at the location, but modify code will subtact this */
+ ip += CALL_BACK;
+
+ /*
+ * Replace the mcount stub with a pointer to the
+ * ip recorder function.
+ */
+ memcpy(old, &mcount_call, 4);
+ new = ftrace_call_replace(ip, *addr);
+ *addr = ftrace_modify_code(ip, old, new);
+
+ return 0;
+}
+
+int __init ftrace_dyn_arch_init(void *data)
+{
+ /* This is running in kstop_machine */
+
+ ftrace_mcount_set(data);
+
+ return 0;
+}
+
diff --git a/arch/powerpc/kernel/io.c b/arch/powerpc/kernel/io.c
index e31aca9208eb..1882bf419fa6 100644
--- a/arch/powerpc/kernel/io.c
+++ b/arch/powerpc/kernel/io.c
@@ -120,7 +120,8 @@ EXPORT_SYMBOL(_outsl_ns);
#define IO_CHECK_ALIGN(v,a) ((((unsigned long)(v)) & ((a) - 1)) == 0)
-void _memset_io(volatile void __iomem *addr, int c, unsigned long n)
+notrace void
+_memset_io(volatile void __iomem *addr, int c, unsigned long n)
{
void *p = (void __force *)addr;
u32 lc = c;
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index 2f73f705d564..6e01eb0a3315 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c
@@ -98,7 +98,7 @@ EXPORT_SYMBOL(irq_desc);
int distribute_irqs = 1;
-static inline unsigned long get_hard_enabled(void)
+static inline notrace unsigned long get_hard_enabled(void)
{
unsigned long enabled;
@@ -108,13 +108,13 @@ static inline unsigned long get_hard_enabled(void)
return enabled;
}
-static inline void set_soft_enabled(unsigned long enable)
+static inline notrace void set_soft_enabled(unsigned long enable)
{
__asm__ __volatile__("stb %0,%1(13)"
: : "r" (enable), "i" (offsetof(struct paca_struct, soft_enabled)));
}
-void raw_local_irq_restore(unsigned long en)
+notrace void raw_local_irq_restore(unsigned long en)
{
/*
* get_paca()->soft_enabled = en;
diff --git a/arch/powerpc/kernel/ppc_ksyms.c b/arch/powerpc/kernel/ppc_ksyms.c
index cf6b5a7d8b3f..4300db52662a 100644
--- a/arch/powerpc/kernel/ppc_ksyms.c
+++ b/arch/powerpc/kernel/ppc_ksyms.c
@@ -43,6 +43,7 @@
#include <asm/div64.h>
#include <asm/signal.h>
#include <asm/dcr.h>
+#include <asm/ftrace.h>
#ifdef CONFIG_PPC32
extern void transfer_to_handler(void);
@@ -68,6 +69,10 @@ EXPORT_SYMBOL(single_step_exception);
EXPORT_SYMBOL(sys_sigreturn);
#endif
+#ifdef CONFIG_FTRACE
+EXPORT_SYMBOL(_mcount);
+#endif
+
EXPORT_SYMBOL(strcpy);
EXPORT_SYMBOL(strncpy);
EXPORT_SYMBOL(strcat);
diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c
index 5112a4aa801d..19e8fcb9cea8 100644
--- a/arch/powerpc/kernel/setup_32.c
+++ b/arch/powerpc/kernel/setup_32.c
@@ -81,7 +81,7 @@ int ucache_bsize;
* from the address that it was linked at, so we must use RELOC/PTRRELOC
* to access static data (including strings). -- paulus
*/
-unsigned long __init early_init(unsigned long dt_ptr)
+notrace unsigned long __init early_init(unsigned long dt_ptr)
{
unsigned long offset = reloc_offset();
struct cpu_spec *spec;
@@ -111,7 +111,7 @@ unsigned long __init early_init(unsigned long dt_ptr)
* This is called very early on the boot process, after a minimal
* MMU environment has been set up but before MMU_init is called.
*/
-void __init machine_init(unsigned long dt_ptr, unsigned long phys)
+notrace void __init machine_init(unsigned long dt_ptr, unsigned long phys)
{
/* Enable early debugging if any specified (see udbg.h) */
udbg_early_init();
@@ -133,7 +133,7 @@ void __init machine_init(unsigned long dt_ptr, unsigned long phys)
#ifdef CONFIG_BOOKE_WDT
/* Checks wdt=x and wdt_period=xx command-line option */
-int __init early_parse_wdt(char *p)
+notrace int __init early_parse_wdt(char *p)
{
if (p && strncmp(p, "0", 1) != 0)
booke_wdt_enabled = 1;
diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S
index 0c3000bf8d75..53d57d17a894 100644
--- a/arch/powerpc/kernel/vmlinux.lds.S
+++ b/arch/powerpc/kernel/vmlinux.lds.S
@@ -64,8 +64,6 @@ SECTIONS
NOTES
- BUG_TABLE
-
/*
* Init sections discarded at runtime
*/
diff --git a/arch/powerpc/kvm/44x_tlb.c b/arch/powerpc/kvm/44x_tlb.c
index f5d7a5eab96e..75dff7cfa814 100644
--- a/arch/powerpc/kvm/44x_tlb.c
+++ b/arch/powerpc/kvm/44x_tlb.c
@@ -116,8 +116,6 @@ static void kvmppc_44x_shadow_release(struct kvm_vcpu *vcpu,
struct tlbe *stlbe = &vcpu->arch.shadow_tlb[index];
struct page *page = vcpu->arch.shadow_pages[index];
- kunmap(vcpu->arch.shadow_pages[index]);
-
if (get_tlb_v(stlbe)) {
if (kvmppc_44x_tlbe_is_writable(stlbe))
kvm_release_page_dirty(page);
@@ -144,18 +142,19 @@ void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gfn_t gfn, u64 asid,
stlbe = &vcpu->arch.shadow_tlb[victim];
/* Get reference to new page. */
- down_write(&current->mm->mmap_sem);
+ down_read(&current->mm->mmap_sem);
new_page = gfn_to_page(vcpu->kvm, gfn);
if (is_error_page(new_page)) {
- printk(KERN_ERR "Couldn't get guest page!\n");
+ printk(KERN_ERR "Couldn't get guest page for gfn %lx!\n", gfn);
kvm_release_page_clean(new_page);
+ up_read(&current->mm->mmap_sem);
return;
}
hpaddr = page_to_phys(new_page);
/* Drop reference to old page. */
kvmppc_44x_shadow_release(vcpu, victim);
- up_write(&current->mm->mmap_sem);
+ up_read(&current->mm->mmap_sem);
vcpu->arch.shadow_pages[victim] = new_page;
diff --git a/arch/powerpc/kvm/Makefile b/arch/powerpc/kvm/Makefile
index d0d358d367ec..04e3449e1f42 100644
--- a/arch/powerpc/kvm/Makefile
+++ b/arch/powerpc/kvm/Makefile
@@ -4,7 +4,7 @@
EXTRA_CFLAGS += -Ivirt/kvm -Iarch/powerpc/kvm
-common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o)
+common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o coalesced_mmio.o)
kvm-objs := $(common-objs) powerpc.o emulate.o booke_guest.o
obj-$(CONFIG_KVM) += kvm.o
diff --git a/arch/powerpc/kvm/booke_guest.c b/arch/powerpc/kvm/booke_guest.c
index 712d89a28c46..9c8ad850c6e3 100644
--- a/arch/powerpc/kvm/booke_guest.c
+++ b/arch/powerpc/kvm/booke_guest.c
@@ -227,39 +227,6 @@ void kvmppc_check_and_deliver_interrupts(struct kvm_vcpu *vcpu)
}
}
-static int kvmppc_emulate_mmio(struct kvm_run *run, struct kvm_vcpu *vcpu)
-{
- enum emulation_result er;
- int r;
-
- er = kvmppc_emulate_instruction(run, vcpu);
- switch (er) {
- case EMULATE_DONE:
- /* Future optimization: only reload non-volatiles if they were
- * actually modified. */
- r = RESUME_GUEST_NV;
- break;
- case EMULATE_DO_MMIO:
- run->exit_reason = KVM_EXIT_MMIO;
- /* We must reload nonvolatiles because "update" load/store
- * instructions modify register state. */
- /* Future optimization: only reload non-volatiles if they were
- * actually modified. */
- r = RESUME_HOST_NV;
- break;
- case EMULATE_FAIL:
- /* XXX Deliver Program interrupt to guest. */
- printk(KERN_EMERG "%s: emulation failed (%08x)\n", __func__,
- vcpu->arch.last_inst);
- r = RESUME_HOST;
- break;
- default:
- BUG();
- }
-
- return r;
-}
-
/**
* kvmppc_handle_exit
*
diff --git a/arch/powerpc/kvm/emulate.c b/arch/powerpc/kvm/emulate.c
index a03fe0c80698..000097461283 100644
--- a/arch/powerpc/kvm/emulate.c
+++ b/arch/powerpc/kvm/emulate.c
@@ -246,6 +246,11 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
case 31:
switch (get_xop(inst)) {
+ case 23: /* lwzx */
+ rt = get_rt(inst);
+ emulated = kvmppc_handle_load(run, vcpu, rt, 4, 1);
+ break;
+
case 83: /* mfmsr */
rt = get_rt(inst);
vcpu->arch.gpr[rt] = vcpu->arch.msr;
@@ -267,6 +272,13 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu)
kvmppc_set_msr(vcpu, vcpu->arch.gpr[rs]);
break;
+ case 151: /* stwx */
+ rs = get_rs(inst);
+ emulated = kvmppc_handle_store(run, vcpu,
+ vcpu->arch.gpr[rs],
+ 4, 1);
+ break;
+
case 163: /* wrteei */
vcpu->arch.msr = (vcpu->arch.msr & ~MSR_EE)
| (inst & MSR_EE);
diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c
index 777e0f34e0ea..b850d2497027 100644
--- a/arch/powerpc/kvm/powerpc.c
+++ b/arch/powerpc/kvm/powerpc.c
@@ -145,6 +145,9 @@ int kvm_dev_ioctl_check_extension(long ext)
case KVM_CAP_USER_MEMORY:
r = 1;
break;
+ case KVM_CAP_COALESCED_MMIO:
+ r = KVM_COALESCED_MMIO_PAGE_OFFSET;
+ break;
default:
r = 0;
break;
@@ -240,10 +243,6 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
{
}
-void decache_vcpus_on_cpu(int cpu)
-{
-}
-
int kvm_arch_vcpu_ioctl_debug_guest(struct kvm_vcpu *vcpu,
struct kvm_debug_guest *dbg)
{
diff --git a/arch/powerpc/platforms/chrp/pci.c b/arch/powerpc/platforms/chrp/pci.c
index 609c46db4a1b..768c262b9368 100644
--- a/arch/powerpc/platforms/chrp/pci.c
+++ b/arch/powerpc/platforms/chrp/pci.c
@@ -367,7 +367,7 @@ static void chrp_pci_fixup_vt8231_ata(struct pci_dev *viaide)
viaisa = pci_get_device(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8231, NULL);
if (!viaisa)
return;
- printk("Fixing VIA IDE, force legacy mode on '%s'\n", viaide->dev.bus_id);
+ dev_info(&viaide->dev, "Fixing VIA IDE, force legacy mode on\n");
pci_read_config_byte(viaide, PCI_CLASS_PROG, &progif);
pci_write_config_byte(viaide, PCI_CLASS_PROG, progif & ~0x5);
diff --git a/arch/powerpc/platforms/powermac/Makefile b/arch/powerpc/platforms/powermac/Makefile
index 4d72c8f72159..89774177b209 100644
--- a/arch/powerpc/platforms/powermac/Makefile
+++ b/arch/powerpc/platforms/powermac/Makefile
@@ -1,5 +1,10 @@
CFLAGS_bootx_init.o += -fPIC
+ifdef CONFIG_FTRACE
+# Do not trace early boot code
+CFLAGS_REMOVE_bootx_init.o = -pg
+endif
+
obj-y += pic.o setup.o time.o feature.o pci.o \
sleep.o low_i2c.o cache.o pfunc_core.o \
pfunc_base.o
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 93acb3c1859d..a79820c3ab08 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -146,6 +146,7 @@ config MATHEMU
config COMPAT
bool "Kernel support for 31 bit emulation"
depends on 64BIT
+ select COMPAT_BINFMT_ELF
help
Select this option if you want to enable your system kernel to
handle system-calls from ELF binaries for 31 bit ESA. This option
@@ -304,6 +305,7 @@ config ARCH_SPARSEMEM_ENABLE
def_bool y
select SPARSEMEM_VMEMMAP_ENABLE
select SPARSEMEM_VMEMMAP
+ select SPARSEMEM_STATIC if !64BIT
config ARCH_SPARSEMEM_DEFAULT
def_bool y
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile
index 6302f5082588..0fed81d91e03 100644
--- a/arch/s390/kernel/Makefile
+++ b/arch/s390/kernel/Makefile
@@ -7,6 +7,11 @@
#
CFLAGS_smp.o := -Wno-nonnull
+#
+# Pass UTS_MACHINE for user_regset definition
+#
+CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"'
+
obj-y := bitmap.o traps.o time.o process.o base.o early.o \
setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \
s390_ext.o debug.o irq.o ipl.o dis.o diag.o
@@ -23,7 +28,7 @@ obj-$(CONFIG_AUDIT) += audit.o
compat-obj-$(CONFIG_AUDIT) += compat_audit.o
obj-$(CONFIG_COMPAT) += compat_linux.o compat_signal.o \
compat_wrapper.o compat_exec_domain.o \
- binfmt_elf32.o $(compat-obj-y)
+ $(compat-obj-y)
obj-$(CONFIG_VIRT_TIMER) += vtime.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
diff --git a/arch/s390/kernel/binfmt_elf32.c b/arch/s390/kernel/binfmt_elf32.c
deleted file mode 100644
index 3e1c315b736d..000000000000
--- a/arch/s390/kernel/binfmt_elf32.c
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Support for 32-bit Linux for S390 ELF binaries.
- *
- * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
- * Author(s): Gerhard Tonn (ton@de.ibm.com)
- *
- * Heavily inspired by the 32-bit Sparc compat code which is
- * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller (davem@redhat.com)
- * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek (jj@ultra.linux.cz)
- */
-
-#define __ASMS390_ELF_H
-
-#include <linux/time.h>
-
-/*
- * These are used to set parameters in the core dumps.
- */
-#define ELF_CLASS ELFCLASS32
-#define ELF_DATA ELFDATA2MSB
-#define ELF_ARCH EM_S390
-
-/*
- * This is used to ensure we don't load something for the wrong architecture.
- */
-#define elf_check_arch(x) \
- (((x)->e_machine == EM_S390 || (x)->e_machine == EM_S390_OLD) \
- && (x)->e_ident[EI_CLASS] == ELF_CLASS)
-
-/* ELF register definitions */
-#define NUM_GPRS 16
-#define NUM_FPRS 16
-#define NUM_ACRS 16
-
-/* For SVR4/S390 the function pointer to be registered with `atexit` is
- passed in R14. */
-#define ELF_PLAT_INIT(_r, load_addr) \
- do { \
- _r->gprs[14] = 0; \
- } while(0)
-
-#define USE_ELF_CORE_DUMP
-#define ELF_EXEC_PAGESIZE 4096
-
-/* This is the location that an ET_DYN program is loaded if exec'ed. Typical
- use of this is to invoke "./ld.so someprog" to test out a new version of
- the loader. We need to make sure that it is out of the way of the program
- that it will "exec", and that there is sufficient room for the brk. */
-
-#define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2)
-
-/* Wow, the "main" arch needs arch dependent functions too.. :) */
-
-/* regs is struct pt_regs, pr_reg is elf_gregset_t (which is
- now struct_user_regs, they are different) */
-
-#define ELF_CORE_COPY_REGS(pr_reg, regs) dump_regs32(regs, &pr_reg);
-
-#define ELF_CORE_COPY_TASK_REGS(tsk, regs) dump_task_regs32(tsk, regs)
-
-#define ELF_CORE_COPY_FPREGS(tsk, fpregs) dump_task_fpu(tsk, fpregs)
-
-/* This yields a mask that user programs can use to figure out what
- instruction set this CPU supports. */
-
-#define ELF_HWCAP (0)
-
-/* This yields a string that ld.so will use to load implementation
- specific libraries for optimization. This is more specific in
- intent than poking at uname or /proc/cpuinfo.
-
- For the moment, we have only optimizations for the Intel generations,
- but that could change... */
-
-#define ELF_PLATFORM (NULL)
-
-#define SET_PERSONALITY(ex, ibcs2) \
-do { \
- if (ibcs2) \
- set_personality(PER_SVR4); \
- else if (current->personality != PER_LINUX32) \
- set_personality(PER_LINUX); \
- set_thread_flag(TIF_31BIT); \
-} while (0)
-
-#include "compat_linux.h"
-
-typedef _s390_fp_regs32 elf_fpregset_t;
-
-typedef struct
-{
-
- _psw_t32 psw;
- __u32 gprs[__NUM_GPRS];
- __u32 acrs[__NUM_ACRS];
- __u32 orig_gpr2;
-} s390_regs32;
-typedef s390_regs32 elf_gregset_t;
-
-static inline int dump_regs32(struct pt_regs *ptregs, elf_gregset_t *regs)
-{
- int i;
-
- memcpy(&regs->psw.mask, &ptregs->psw.mask, 4);
- memcpy(&regs->psw.addr, (char *)&ptregs->psw.addr + 4, 4);
- for (i = 0; i < NUM_GPRS; i++)
- regs->gprs[i] = ptregs->gprs[i];
- save_access_regs(regs->acrs);
- regs->orig_gpr2 = ptregs->orig_gpr2;
- return 1;
-}
-
-static inline int dump_task_regs32(struct task_struct *tsk, elf_gregset_t *regs)
-{
- struct pt_regs *ptregs = task_pt_regs(tsk);
- int i;
-
- memcpy(&regs->psw.mask, &ptregs->psw.mask, 4);
- memcpy(&regs->psw.addr, (char *)&ptregs->psw.addr + 4, 4);
- for (i = 0; i < NUM_GPRS; i++)
- regs->gprs[i] = ptregs->gprs[i];
- memcpy(regs->acrs, tsk->thread.acrs, sizeof(regs->acrs));
- regs->orig_gpr2 = ptregs->orig_gpr2;
- return 1;
-}
-
-static inline int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpregs)
-{
- if (tsk == current)
- save_fp_regs((s390_fp_regs *) fpregs);
- else
- memcpy(fpregs, &tsk->thread.fp_regs, sizeof(elf_fpregset_t));
- return 1;
-}
-
-#include <asm/processor.h>
-#include <asm/pgalloc.h>
-#include <linux/module.h>
-#include <linux/elfcore.h>
-#include <linux/binfmts.h>
-#include <linux/compat.h>
-
-#define elf_prstatus elf_prstatus32
-struct elf_prstatus32
-{
- struct elf_siginfo pr_info; /* Info associated with signal */
- short pr_cursig; /* Current signal */
- u32 pr_sigpend; /* Set of pending signals */
- u32 pr_sighold; /* Set of held signals */
- pid_t pr_pid;
- pid_t pr_ppid;
- pid_t pr_pgrp;
- pid_t pr_sid;
- struct compat_timeval pr_utime; /* User time */
- struct compat_timeval pr_stime; /* System time */
- struct compat_timeval pr_cutime; /* Cumulative user time */
- struct compat_timeval pr_cstime; /* Cumulative system time */
- elf_gregset_t pr_reg; /* GP registers */
- int pr_fpvalid; /* True if math co-processor being used. */
-};
-
-#define elf_prpsinfo elf_prpsinfo32
-struct elf_prpsinfo32
-{
- char pr_state; /* numeric process state */
- char pr_sname; /* char for pr_state */
- char pr_zomb; /* zombie */
- char pr_nice; /* nice val */
- u32 pr_flag; /* flags */
- u16 pr_uid;
- u16 pr_gid;
- pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid;
- /* Lots missing */
- char pr_fname[16]; /* filename of executable */
- char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */
-};
-
-#include <linux/highuid.h>
-
-/*
-#define init_elf_binfmt init_elf32_binfmt
-*/
-
-#undef start_thread
-#define start_thread start_thread31
-
-static inline void start_thread31(struct pt_regs *regs, unsigned long new_psw,
- unsigned long new_stackp)
-{
- set_fs(USER_DS);
- regs->psw.mask = psw_user32_bits;
- regs->psw.addr = new_psw;
- regs->gprs[15] = new_stackp;
- crst_table_downgrade(current->mm, 1UL << 31);
-}
-
-MODULE_DESCRIPTION("Binary format loader for compatibility with 32bit Linux for S390 binaries,"
- " Copyright 2000 IBM Corporation");
-MODULE_AUTHOR("Gerhard Tonn <ton@de.ibm.com>");
-
-#undef MODULE_DESCRIPTION
-#undef MODULE_AUTHOR
-
-#undef cputime_to_timeval
-#define cputime_to_timeval cputime_to_compat_timeval
-static inline void
-cputime_to_compat_timeval(const cputime_t cputime, struct compat_timeval *value)
-{
- value->tv_usec = cputime % 1000000;
- value->tv_sec = cputime / 1000000;
-}
-
-#include "../../../fs/binfmt_elf.c"
-
diff --git a/arch/s390/kernel/compat_ptrace.h b/arch/s390/kernel/compat_ptrace.h
index 419aef913ee1..cde81fa64f89 100644
--- a/arch/s390/kernel/compat_ptrace.h
+++ b/arch/s390/kernel/compat_ptrace.h
@@ -1,7 +1,7 @@
#ifndef _PTRACE32_H
#define _PTRACE32_H
-#include "compat_linux.h" /* needed for _psw_t32 */
+#include "compat_linux.h" /* needed for psw_compat_t */
typedef struct {
__u32 cr[3];
@@ -38,7 +38,7 @@ typedef struct {
struct user_regs_struct32
{
- _psw_t32 psw;
+ psw_compat_t psw;
u32 gprs[NUM_GPRS];
u32 acrs[NUM_ACRS];
u32 orig_gpr2;
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index d0e09684b9ce..b2226e41f067 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -380,6 +380,23 @@ static __init void detect_machine_facilities(void)
#endif
}
+static __init void rescue_initrd(void)
+{
+#ifdef CONFIG_BLK_DEV_INITRD
+ /*
+ * Move the initrd right behind the bss section in case it starts
+ * within the bss section. So we don't overwrite it when the bss
+ * section gets cleared.
+ */
+ if (!INITRD_START || !INITRD_SIZE)
+ return;
+ if (INITRD_START >= (unsigned long) __bss_stop)
+ return;
+ memmove(__bss_stop, (void *) INITRD_START, INITRD_SIZE);
+ INITRD_START = (unsigned long) __bss_stop;
+#endif
+}
+
/*
* Save ipl parameters, clear bss memory, initialize storage keys
* and create a kernel NSS at startup if the SAVESYS= parm is defined
@@ -389,6 +406,7 @@ void __init startup_init(void)
unsigned long long memsize;
ipl_save_parameters();
+ rescue_initrd();
clear_bss_section();
init_kernel_storage_key();
lockdep_init();
diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c
index 7920861109d2..85defd01d293 100644
--- a/arch/s390/kernel/process.c
+++ b/arch/s390/kernel/process.c
@@ -75,46 +75,19 @@ unsigned long thread_saved_pc(struct task_struct *tsk)
return sf->gprs[8];
}
-/*
- * Need to know about CPUs going idle?
- */
-static ATOMIC_NOTIFIER_HEAD(idle_chain);
DEFINE_PER_CPU(struct s390_idle_data, s390_idle);
-int register_idle_notifier(struct notifier_block *nb)
-{
- return atomic_notifier_chain_register(&idle_chain, nb);
-}
-EXPORT_SYMBOL(register_idle_notifier);
-
-int unregister_idle_notifier(struct notifier_block *nb)
-{
- return atomic_notifier_chain_unregister(&idle_chain, nb);
-}
-EXPORT_SYMBOL(unregister_idle_notifier);
-
static int s390_idle_enter(void)
{
struct s390_idle_data *idle;
- int nr_calls = 0;
- void *hcpu;
- int rc;
- hcpu = (void *)(long)smp_processor_id();
- rc = __atomic_notifier_call_chain(&idle_chain, S390_CPU_IDLE, hcpu, -1,
- &nr_calls);
- if (rc == NOTIFY_BAD) {
- nr_calls--;
- __atomic_notifier_call_chain(&idle_chain, S390_CPU_NOT_IDLE,
- hcpu, nr_calls, NULL);
- return rc;
- }
idle = &__get_cpu_var(s390_idle);
spin_lock(&idle->lock);
idle->idle_count++;
idle->in_idle = 1;
idle->idle_enter = get_clock();
spin_unlock(&idle->lock);
+ vtime_stop_cpu_timer();
return NOTIFY_OK;
}
@@ -122,13 +95,12 @@ void s390_idle_leave(void)
{
struct s390_idle_data *idle;
+ vtime_start_cpu_timer();
idle = &__get_cpu_var(s390_idle);
spin_lock(&idle->lock);
idle->idle_time += get_clock() - idle->idle_enter;
idle->in_idle = 0;
spin_unlock(&idle->lock);
- atomic_notifier_call_chain(&idle_chain, S390_CPU_NOT_IDLE,
- (void *)(long) smp_processor_id());
}
extern void s390_handle_mcck(void);
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
index 35827b9bd4d1..2815bfe348a6 100644
--- a/arch/s390/kernel/ptrace.c
+++ b/arch/s390/kernel/ptrace.c
@@ -33,6 +33,8 @@
#include <linux/security.h>
#include <linux/audit.h>
#include <linux/signal.h>
+#include <linux/elf.h>
+#include <linux/regset.h>
#include <asm/segment.h>
#include <asm/page.h>
@@ -47,6 +49,11 @@
#include "compat_ptrace.h"
#endif
+enum s390_regset {
+ REGSET_GENERAL,
+ REGSET_FP,
+};
+
static void
FixPerRegisters(struct task_struct *task)
{
@@ -126,24 +133,10 @@ ptrace_disable(struct task_struct *child)
* struct user contain pad bytes that should be read as zeroes.
* Lovely...
*/
-static int
-peek_user(struct task_struct *child, addr_t addr, addr_t data)
+static unsigned long __peek_user(struct task_struct *child, addr_t addr)
{
struct user *dummy = NULL;
- addr_t offset, tmp, mask;
-
- /*
- * Stupid gdb peeks/pokes the access registers in 64 bit with
- * an alignment of 4. Programmers from hell...
- */
- mask = __ADDR_MASK;
-#ifdef CONFIG_64BIT
- if (addr >= (addr_t) &dummy->regs.acrs &&
- addr < (addr_t) &dummy->regs.orig_gpr2)
- mask = 3;
-#endif
- if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK)
- return -EIO;
+ addr_t offset, tmp;
if (addr < (addr_t) &dummy->regs.acrs) {
/*
@@ -197,24 +190,18 @@ peek_user(struct task_struct *child, addr_t addr, addr_t data)
} else
tmp = 0;
- return put_user(tmp, (addr_t __user *) data);
+ return tmp;
}
-/*
- * Write a word to the user area of a process at location addr. This
- * operation does have an additional problem compared to peek_user.
- * Stores to the program status word and on the floating point
- * control register needs to get checked for validity.
- */
static int
-poke_user(struct task_struct *child, addr_t addr, addr_t data)
+peek_user(struct task_struct *child, addr_t addr, addr_t data)
{
struct user *dummy = NULL;
- addr_t offset, mask;
+ addr_t tmp, mask;
/*
* Stupid gdb peeks/pokes the access registers in 64 bit with
- * an alignment of 4. Programmers from hell indeed...
+ * an alignment of 4. Programmers from hell...
*/
mask = __ADDR_MASK;
#ifdef CONFIG_64BIT
@@ -225,6 +212,21 @@ poke_user(struct task_struct *child, addr_t addr, addr_t data)
if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK)
return -EIO;
+ tmp = __peek_user(child, addr);
+ return put_user(tmp, (addr_t __user *) data);
+}
+
+/*
+ * Write a word to the user area of a process at location addr. This
+ * operation does have an additional problem compared to peek_user.
+ * Stores to the program status word and on the floating point
+ * control register needs to get checked for validity.
+ */
+static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
+{
+ struct user *dummy = NULL;
+ addr_t offset;
+
if (addr < (addr_t) &dummy->regs.acrs) {
/*
* psw and gprs are stored on the stack
@@ -292,6 +294,28 @@ poke_user(struct task_struct *child, addr_t addr, addr_t data)
return 0;
}
+static int
+poke_user(struct task_struct *child, addr_t addr, addr_t data)
+{
+ struct user *dummy = NULL;
+ addr_t mask;
+
+ /*
+ * Stupid gdb peeks/pokes the access registers in 64 bit with
+ * an alignment of 4. Programmers from hell indeed...
+ */
+ mask = __ADDR_MASK;
+#ifdef CONFIG_64BIT
+ if (addr >= (addr_t) &dummy->regs.acrs &&
+ addr < (addr_t) &dummy->regs.orig_gpr2)
+ mask = 3;
+#endif
+ if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK)
+ return -EIO;
+
+ return __poke_user(child, addr, data);
+}
+
long arch_ptrace(struct task_struct *child, long request, long addr, long data)
{
ptrace_area parea;
@@ -367,18 +391,13 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
/*
* Same as peek_user but for a 31 bit program.
*/
-static int
-peek_user_emu31(struct task_struct *child, addr_t addr, addr_t data)
+static u32 __peek_user_compat(struct task_struct *child, addr_t addr)
{
struct user32 *dummy32 = NULL;
per_struct32 *dummy_per32 = NULL;
addr_t offset;
__u32 tmp;
- if (!test_thread_flag(TIF_31BIT) ||
- (addr & 3) || addr > sizeof(struct user) - 3)
- return -EIO;
-
if (addr < (addr_t) &dummy32->regs.acrs) {
/*
* psw and gprs are stored on the stack
@@ -435,25 +454,32 @@ peek_user_emu31(struct task_struct *child, addr_t addr, addr_t data)
} else
tmp = 0;
+ return tmp;
+}
+
+static int peek_user_compat(struct task_struct *child,
+ addr_t addr, addr_t data)
+{
+ __u32 tmp;
+
+ if (!test_thread_flag(TIF_31BIT) ||
+ (addr & 3) || addr > sizeof(struct user) - 3)
+ return -EIO;
+
+ tmp = __peek_user_compat(child, addr);
return put_user(tmp, (__u32 __user *) data);
}
/*
* Same as poke_user but for a 31 bit program.
*/
-static int
-poke_user_emu31(struct task_struct *child, addr_t addr, addr_t data)
+static int __poke_user_compat(struct task_struct *child,
+ addr_t addr, addr_t data)
{
struct user32 *dummy32 = NULL;
per_struct32 *dummy_per32 = NULL;
+ __u32 tmp = (__u32) data;
addr_t offset;
- __u32 tmp;
-
- if (!test_thread_flag(TIF_31BIT) ||
- (addr & 3) || addr > sizeof(struct user32) - 3)
- return -EIO;
-
- tmp = (__u32) data;
if (addr < (addr_t) &dummy32->regs.acrs) {
/*
@@ -528,6 +554,16 @@ poke_user_emu31(struct task_struct *child, addr_t addr, addr_t data)
return 0;
}
+static int poke_user_compat(struct task_struct *child,
+ addr_t addr, addr_t data)
+{
+ if (!test_thread_flag(TIF_31BIT) ||
+ (addr & 3) || addr > sizeof(struct user32) - 3)
+ return -EIO;
+
+ return __poke_user_compat(child, addr, data);
+}
+
long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
compat_ulong_t caddr, compat_ulong_t cdata)
{
@@ -539,11 +575,11 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
switch (request) {
case PTRACE_PEEKUSR:
/* read the word at location addr in the USER area. */
- return peek_user_emu31(child, addr, data);
+ return peek_user_compat(child, addr, data);
case PTRACE_POKEUSR:
/* write the word at location addr in the USER area */
- return poke_user_emu31(child, addr, data);
+ return poke_user_compat(child, addr, data);
case PTRACE_PEEKUSR_AREA:
case PTRACE_POKEUSR_AREA:
@@ -555,13 +591,13 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
copied = 0;
while (copied < parea.len) {
if (request == PTRACE_PEEKUSR_AREA)
- ret = peek_user_emu31(child, addr, data);
+ ret = peek_user_compat(child, addr, data);
else {
__u32 utmp;
if (get_user(utmp,
(__u32 __force __user *) data))
return -EFAULT;
- ret = poke_user_emu31(child, addr, utmp);
+ ret = poke_user_compat(child, addr, utmp);
}
if (ret)
return ret;
@@ -610,3 +646,240 @@ syscall_trace(struct pt_regs *regs, int entryexit)
regs->gprs[2], regs->orig_gpr2, regs->gprs[3],
regs->gprs[4], regs->gprs[5]);
}
+
+/*
+ * user_regset definitions.
+ */
+
+static int s390_regs_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ if (target == current)
+ save_access_regs(target->thread.acrs);
+
+ if (kbuf) {
+ unsigned long *k = kbuf;
+ while (count > 0) {
+ *k++ = __peek_user(target, pos);
+ count -= sizeof(*k);
+ pos += sizeof(*k);
+ }
+ } else {
+ unsigned long __user *u = ubuf;
+ while (count > 0) {
+ if (__put_user(__peek_user(target, pos), u++))
+ return -EFAULT;
+ count -= sizeof(*u);
+ pos += sizeof(*u);
+ }
+ }
+ return 0;
+}
+
+static int s390_regs_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int rc = 0;
+
+ if (target == current)
+ save_access_regs(target->thread.acrs);
+
+ if (kbuf) {
+ const unsigned long *k = kbuf;
+ while (count > 0 && !rc) {
+ rc = __poke_user(target, pos, *k++);
+ count -= sizeof(*k);
+ pos += sizeof(*k);
+ }
+ } else {
+ const unsigned long __user *u = ubuf;
+ while (count > 0 && !rc) {
+ unsigned long word;
+ rc = __get_user(word, u++);
+ if (rc)
+ break;
+ rc = __poke_user(target, pos, word);
+ count -= sizeof(*u);
+ pos += sizeof(*u);
+ }
+ }
+
+ if (rc == 0 && target == current)
+ restore_access_regs(target->thread.acrs);
+
+ return rc;
+}
+
+static int s390_fpregs_get(struct task_struct *target,
+ const struct user_regset *regset, unsigned int pos,
+ unsigned int count, void *kbuf, void __user *ubuf)
+{
+ if (target == current)
+ save_fp_regs(&target->thread.fp_regs);
+
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.fp_regs, 0, -1);
+}
+
+static int s390_fpregs_set(struct task_struct *target,
+ const struct user_regset *regset, unsigned int pos,
+ unsigned int count, const void *kbuf,
+ const void __user *ubuf)
+{
+ int rc = 0;
+
+ if (target == current)
+ save_fp_regs(&target->thread.fp_regs);
+
+ /* If setting FPC, must validate it first. */
+ if (count > 0 && pos < offsetof(s390_fp_regs, fprs)) {
+ u32 fpc[2] = { target->thread.fp_regs.fpc, 0 };
+ rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &fpc,
+ 0, offsetof(s390_fp_regs, fprs));
+ if (rc)
+ return rc;
+ if ((fpc[0] & ~FPC_VALID_MASK) != 0 || fpc[1] != 0)
+ return -EINVAL;
+ target->thread.fp_regs.fpc = fpc[0];
+ }
+
+ if (rc == 0 && count > 0)
+ rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ target->thread.fp_regs.fprs,
+ offsetof(s390_fp_regs, fprs), -1);
+
+ if (rc == 0 && target == current)
+ restore_fp_regs(&target->thread.fp_regs);
+
+ return rc;
+}
+
+static const struct user_regset s390_regsets[] = {
+ [REGSET_GENERAL] = {
+ .core_note_type = NT_PRSTATUS,
+ .n = sizeof(s390_regs) / sizeof(long),
+ .size = sizeof(long),
+ .align = sizeof(long),
+ .get = s390_regs_get,
+ .set = s390_regs_set,
+ },
+ [REGSET_FP] = {
+ .core_note_type = NT_PRFPREG,
+ .n = sizeof(s390_fp_regs) / sizeof(long),
+ .size = sizeof(long),
+ .align = sizeof(long),
+ .get = s390_fpregs_get,
+ .set = s390_fpregs_set,
+ },
+};
+
+static const struct user_regset_view user_s390_view = {
+ .name = UTS_MACHINE,
+ .e_machine = EM_S390,
+ .regsets = s390_regsets,
+ .n = ARRAY_SIZE(s390_regsets)
+};
+
+#ifdef CONFIG_COMPAT
+static int s390_compat_regs_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ if (target == current)
+ save_access_regs(target->thread.acrs);
+
+ if (kbuf) {
+ compat_ulong_t *k = kbuf;
+ while (count > 0) {
+ *k++ = __peek_user_compat(target, pos);
+ count -= sizeof(*k);
+ pos += sizeof(*k);
+ }
+ } else {
+ compat_ulong_t __user *u = ubuf;
+ while (count > 0) {
+ if (__put_user(__peek_user_compat(target, pos), u++))
+ return -EFAULT;
+ count -= sizeof(*u);
+ pos += sizeof(*u);
+ }
+ }
+ return 0;
+}
+
+static int s390_compat_regs_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int rc = 0;
+
+ if (target == current)
+ save_access_regs(target->thread.acrs);
+
+ if (kbuf) {
+ const compat_ulong_t *k = kbuf;
+ while (count > 0 && !rc) {
+ rc = __poke_user_compat(target, pos, *k++);
+ count -= sizeof(*k);
+ pos += sizeof(*k);
+ }
+ } else {
+ const compat_ulong_t __user *u = ubuf;
+ while (count > 0 && !rc) {
+ compat_ulong_t word;
+ rc = __get_user(word, u++);
+ if (rc)
+ break;
+ rc = __poke_user_compat(target, pos, word);
+ count -= sizeof(*u);
+ pos += sizeof(*u);
+ }
+ }
+
+ if (rc == 0 && target == current)
+ restore_access_regs(target->thread.acrs);
+
+ return rc;
+}
+
+static const struct user_regset s390_compat_regsets[] = {
+ [REGSET_GENERAL] = {
+ .core_note_type = NT_PRSTATUS,
+ .n = sizeof(s390_compat_regs) / sizeof(compat_long_t),
+ .size = sizeof(compat_long_t),
+ .align = sizeof(compat_long_t),
+ .get = s390_compat_regs_get,
+ .set = s390_compat_regs_set,
+ },
+ [REGSET_FP] = {
+ .core_note_type = NT_PRFPREG,
+ .n = sizeof(s390_fp_regs) / sizeof(compat_long_t),
+ .size = sizeof(compat_long_t),
+ .align = sizeof(compat_long_t),
+ .get = s390_fpregs_get,
+ .set = s390_fpregs_set,
+ },
+};
+
+static const struct user_regset_view user_s390_compat_view = {
+ .name = "s390",
+ .e_machine = EM_S390,
+ .regsets = s390_compat_regsets,
+ .n = ARRAY_SIZE(s390_compat_regsets)
+};
+#endif
+
+const struct user_regset_view *task_user_regset_view(struct task_struct *task)
+{
+#ifdef CONFIG_COMPAT
+ if (test_tsk_thread_flag(task, TIF_31BIT))
+ return &user_s390_compat_view;
+#endif
+ return &user_s390_view;
+}
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index 42b1d12ebb10..5d4fa4b1c74c 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -711,7 +711,7 @@ int __cpuinit __cpu_up(unsigned int cpu)
memset(sf, 0, sizeof(struct stack_frame));
sf->gprs[9] = (unsigned long) sf;
cpu_lowcore->save_area[15] = (unsigned long) sf;
- __ctl_store(cpu_lowcore->cregs_save_area[0], 0, 15);
+ __ctl_store(cpu_lowcore->cregs_save_area, 0, 15);
asm volatile(
" stam 0,15,0(%0)"
: : "a" (&cpu_lowcore->access_regs_save_area) : "memory");
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c
index 7aec676fefd5..7418bebb547f 100644
--- a/arch/s390/kernel/time.c
+++ b/arch/s390/kernel/time.c
@@ -3,7 +3,7 @@
* Time of day based timer functions.
*
* S390 version
- * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Copyright IBM Corp. 1999, 2008
* Author(s): Hartmut Penner (hp@de.ibm.com),
* Martin Schwidefsky (schwidefsky@de.ibm.com),
* Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
@@ -31,6 +31,7 @@
#include <linux/notifier.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
+#include <linux/bootmem.h>
#include <asm/uaccess.h>
#include <asm/delay.h>
#include <asm/s390_ext.h>
@@ -162,7 +163,7 @@ void init_cpu_timer(void)
/* Enable clock comparator timer interrupt. */
__ctl_set_bit(0,11);
- /* Always allow ETR external interrupts, even without an ETR. */
+ /* Always allow the timing alert external interrupt. */
__ctl_set_bit(0, 4);
}
@@ -170,8 +171,21 @@ static void clock_comparator_interrupt(__u16 code)
{
}
+static void etr_timing_alert(struct etr_irq_parm *);
+static void stp_timing_alert(struct stp_irq_parm *);
+
+static void timing_alert_interrupt(__u16 code)
+{
+ if (S390_lowcore.ext_params & 0x00c40000)
+ etr_timing_alert((struct etr_irq_parm *)
+ &S390_lowcore.ext_params);
+ if (S390_lowcore.ext_params & 0x00038000)
+ stp_timing_alert((struct stp_irq_parm *)
+ &S390_lowcore.ext_params);
+}
+
static void etr_reset(void);
-static void etr_ext_handler(__u16);
+static void stp_reset(void);
/*
* Get the TOD clock running.
@@ -181,6 +195,7 @@ static u64 __init reset_tod_clock(void)
u64 time;
etr_reset();
+ stp_reset();
if (store_clock(&time) == 0)
return time;
/* TOD clock not running. Set the clock to Unix Epoch. */
@@ -231,8 +246,9 @@ void __init time_init(void)
if (clocksource_register(&clocksource_tod) != 0)
panic("Could not register TOD clock source");
- /* request the etr external interrupt */
- if (register_early_external_interrupt(0x1406, etr_ext_handler,
+ /* request the timing alert external interrupt */
+ if (register_early_external_interrupt(0x1406,
+ timing_alert_interrupt,
&ext_int_etr_cc) != 0)
panic("Couldn't request external interrupt 0x1406");
@@ -245,10 +261,112 @@ void __init time_init(void)
}
/*
+ * The time is "clock". old is what we think the time is.
+ * Adjust the value by a multiple of jiffies and add the delta to ntp.
+ * "delay" is an approximation how long the synchronization took. If
+ * the time correction is positive, then "delay" is subtracted from
+ * the time difference and only the remaining part is passed to ntp.
+ */
+static unsigned long long adjust_time(unsigned long long old,
+ unsigned long long clock,
+ unsigned long long delay)
+{
+ unsigned long long delta, ticks;
+ struct timex adjust;
+
+ if (clock > old) {
+ /* It is later than we thought. */
+ delta = ticks = clock - old;
+ delta = ticks = (delta < delay) ? 0 : delta - delay;
+ delta -= do_div(ticks, CLK_TICKS_PER_JIFFY);
+ adjust.offset = ticks * (1000000 / HZ);
+ } else {
+ /* It is earlier than we thought. */
+ delta = ticks = old - clock;
+ delta -= do_div(ticks, CLK_TICKS_PER_JIFFY);
+ delta = -delta;
+ adjust.offset = -ticks * (1000000 / HZ);
+ }
+ jiffies_timer_cc += delta;
+ if (adjust.offset != 0) {
+ printk(KERN_NOTICE "etr: time adjusted by %li micro-seconds\n",
+ adjust.offset);
+ adjust.modes = ADJ_OFFSET_SINGLESHOT;
+ do_adjtimex(&adjust);
+ }
+ return delta;
+}
+
+static DEFINE_PER_CPU(atomic_t, clock_sync_word);
+static unsigned long clock_sync_flags;
+
+#define CLOCK_SYNC_HAS_ETR 0
+#define CLOCK_SYNC_HAS_STP 1
+#define CLOCK_SYNC_ETR 2
+#define CLOCK_SYNC_STP 3
+
+/*
+ * The synchronous get_clock function. It will write the current clock
+ * value to the clock pointer and return 0 if the clock is in sync with
+ * the external time source. If the clock mode is local it will return
+ * -ENOSYS and -EAGAIN if the clock is not in sync with the external
+ * reference.
+ */
+int get_sync_clock(unsigned long long *clock)
+{
+ atomic_t *sw_ptr;
+ unsigned int sw0, sw1;
+
+ sw_ptr = &get_cpu_var(clock_sync_word);
+ sw0 = atomic_read(sw_ptr);
+ *clock = get_clock();
+ sw1 = atomic_read(sw_ptr);
+ put_cpu_var(clock_sync_sync);
+ if (sw0 == sw1 && (sw0 & 0x80000000U))
+ /* Success: time is in sync. */
+ return 0;
+ if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags) &&
+ !test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags))
+ return -ENOSYS;
+ if (!test_bit(CLOCK_SYNC_ETR, &clock_sync_flags) &&
+ !test_bit(CLOCK_SYNC_STP, &clock_sync_flags))
+ return -EACCES;
+ return -EAGAIN;
+}
+EXPORT_SYMBOL(get_sync_clock);
+
+/*
+ * Make get_sync_clock return -EAGAIN.
+ */
+static void disable_sync_clock(void *dummy)
+{
+ atomic_t *sw_ptr = &__get_cpu_var(clock_sync_word);
+ /*
+ * Clear the in-sync bit 2^31. All get_sync_clock calls will
+ * fail until the sync bit is turned back on. In addition
+ * increase the "sequence" counter to avoid the race of an
+ * etr event and the complete recovery against get_sync_clock.
+ */
+ atomic_clear_mask(0x80000000, sw_ptr);
+ atomic_inc(sw_ptr);
+}
+
+/*
+ * Make get_sync_clock return 0 again.
+ * Needs to be called from a context disabled for preemption.
+ */
+static void enable_sync_clock(void)
+{
+ atomic_t *sw_ptr = &__get_cpu_var(clock_sync_word);
+ atomic_set_mask(0x80000000, sw_ptr);
+}
+
+/*
* External Time Reference (ETR) code.
*/
static int etr_port0_online;
static int etr_port1_online;
+static int etr_steai_available;
static int __init early_parse_etr(char *p)
{
@@ -273,12 +391,6 @@ enum etr_event {
ETR_EVENT_UPDATE,
};
-enum etr_flags {
- ETR_FLAG_ENOSYS,
- ETR_FLAG_EACCES,
- ETR_FLAG_STEAI,
-};
-
/*
* Valid bit combinations of the eacr register are (x = don't care):
* e0 e1 dp p0 p1 ea es sl
@@ -305,74 +417,18 @@ enum etr_flags {
*/
static struct etr_eacr etr_eacr;
static u64 etr_tolec; /* time of last eacr update */
-static unsigned long etr_flags;
static struct etr_aib etr_port0;
static int etr_port0_uptodate;
static struct etr_aib etr_port1;
static int etr_port1_uptodate;
static unsigned long etr_events;
static struct timer_list etr_timer;
-static DEFINE_PER_CPU(atomic_t, etr_sync_word);
static void etr_timeout(unsigned long dummy);
static void etr_work_fn(struct work_struct *work);
static DECLARE_WORK(etr_work, etr_work_fn);
/*
- * The etr get_clock function. It will write the current clock value
- * to the clock pointer and return 0 if the clock is in sync with the
- * external time source. If the clock mode is local it will return
- * -ENOSYS and -EAGAIN if the clock is not in sync with the external
- * reference. This function is what ETR is all about..
- */
-int get_sync_clock(unsigned long long *clock)
-{
- atomic_t *sw_ptr;
- unsigned int sw0, sw1;
-
- sw_ptr = &get_cpu_var(etr_sync_word);
- sw0 = atomic_read(sw_ptr);
- *clock = get_clock();
- sw1 = atomic_read(sw_ptr);
- put_cpu_var(etr_sync_sync);
- if (sw0 == sw1 && (sw0 & 0x80000000U))
- /* Success: time is in sync. */
- return 0;
- if (test_bit(ETR_FLAG_ENOSYS, &etr_flags))
- return -ENOSYS;
- if (test_bit(ETR_FLAG_EACCES, &etr_flags))
- return -EACCES;
- return -EAGAIN;
-}
-EXPORT_SYMBOL(get_sync_clock);
-
-/*
- * Make get_sync_clock return -EAGAIN.
- */
-static void etr_disable_sync_clock(void *dummy)
-{
- atomic_t *sw_ptr = &__get_cpu_var(etr_sync_word);
- /*
- * Clear the in-sync bit 2^31. All get_sync_clock calls will
- * fail until the sync bit is turned back on. In addition
- * increase the "sequence" counter to avoid the race of an
- * etr event and the complete recovery against get_sync_clock.
- */
- atomic_clear_mask(0x80000000, sw_ptr);
- atomic_inc(sw_ptr);
-}
-
-/*
- * Make get_sync_clock return 0 again.
- * Needs to be called from a context disabled for preemption.
- */
-static void etr_enable_sync_clock(void)
-{
- atomic_t *sw_ptr = &__get_cpu_var(etr_sync_word);
- atomic_set_mask(0x80000000, sw_ptr);
-}
-
-/*
* Reset ETR attachment.
*/
static void etr_reset(void)
@@ -381,15 +437,13 @@ static void etr_reset(void)
.e0 = 0, .e1 = 0, ._pad0 = 4, .dp = 0,
.p0 = 0, .p1 = 0, ._pad1 = 0, .ea = 0,
.es = 0, .sl = 0 };
- if (etr_setr(&etr_eacr) == 0)
+ if (etr_setr(&etr_eacr) == 0) {
etr_tolec = get_clock();
- else {
- set_bit(ETR_FLAG_ENOSYS, &etr_flags);
- if (etr_port0_online || etr_port1_online) {
- printk(KERN_WARNING "Running on non ETR capable "
- "machine, only local mode available.\n");
- etr_port0_online = etr_port1_online = 0;
- }
+ set_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags);
+ } else if (etr_port0_online || etr_port1_online) {
+ printk(KERN_WARNING "Running on non ETR capable "
+ "machine, only local mode available.\n");
+ etr_port0_online = etr_port1_online = 0;
}
}
@@ -397,14 +451,12 @@ static int __init etr_init(void)
{
struct etr_aib aib;
- if (test_bit(ETR_FLAG_ENOSYS, &etr_flags))
+ if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags))
return 0;
/* Check if this machine has the steai instruction. */
if (etr_steai(&aib, ETR_STEAI_STEPPING_PORT) == 0)
- set_bit(ETR_FLAG_STEAI, &etr_flags);
+ etr_steai_available = 1;
setup_timer(&etr_timer, etr_timeout, 0UL);
- if (!etr_port0_online && !etr_port1_online)
- set_bit(ETR_FLAG_EACCES, &etr_flags);
if (etr_port0_online) {
set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events);
schedule_work(&etr_work);
@@ -435,7 +487,8 @@ void etr_switch_to_local(void)
{
if (!etr_eacr.sl)
return;
- etr_disable_sync_clock(NULL);
+ if (test_bit(CLOCK_SYNC_ETR, &clock_sync_flags))
+ disable_sync_clock(NULL);
set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events);
schedule_work(&etr_work);
}
@@ -450,23 +503,21 @@ void etr_sync_check(void)
{
if (!etr_eacr.es)
return;
- etr_disable_sync_clock(NULL);
+ if (test_bit(CLOCK_SYNC_ETR, &clock_sync_flags))
+ disable_sync_clock(NULL);
set_bit(ETR_EVENT_SYNC_CHECK, &etr_events);
schedule_work(&etr_work);
}
/*
- * ETR external interrupt. There are two causes:
+ * ETR timing alert. There are two causes:
* 1) port state change, check the usability of the port
* 2) port alert, one of the ETR-data-validity bits (v1-v2 bits of the
* sldr-status word) or ETR-data word 1 (edf1) or ETR-data word 3 (edf3)
* or ETR-data word 4 (edf4) has changed.
*/
-static void etr_ext_handler(__u16 code)
+static void etr_timing_alert(struct etr_irq_parm *intparm)
{
- struct etr_interruption_parameter *intparm =
- (struct etr_interruption_parameter *) &S390_lowcore.ext_params;
-
if (intparm->pc0)
/* ETR port 0 state change. */
set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events);
@@ -591,58 +642,23 @@ static int etr_aib_follows(struct etr_aib *a1, struct etr_aib *a2, int p)
return 1;
}
-/*
- * The time is "clock". old is what we think the time is.
- * Adjust the value by a multiple of jiffies and add the delta to ntp.
- * "delay" is an approximation how long the synchronization took. If
- * the time correction is positive, then "delay" is subtracted from
- * the time difference and only the remaining part is passed to ntp.
- */
-static unsigned long long etr_adjust_time(unsigned long long old,
- unsigned long long clock,
- unsigned long long delay)
-{
- unsigned long long delta, ticks;
- struct timex adjust;
-
- if (clock > old) {
- /* It is later than we thought. */
- delta = ticks = clock - old;
- delta = ticks = (delta < delay) ? 0 : delta - delay;
- delta -= do_div(ticks, CLK_TICKS_PER_JIFFY);
- adjust.offset = ticks * (1000000 / HZ);
- } else {
- /* It is earlier than we thought. */
- delta = ticks = old - clock;
- delta -= do_div(ticks, CLK_TICKS_PER_JIFFY);
- delta = -delta;
- adjust.offset = -ticks * (1000000 / HZ);
- }
- jiffies_timer_cc += delta;
- if (adjust.offset != 0) {
- printk(KERN_NOTICE "etr: time adjusted by %li micro-seconds\n",
- adjust.offset);
- adjust.modes = ADJ_OFFSET_SINGLESHOT;
- do_adjtimex(&adjust);
- }
- return delta;
-}
-
-static struct {
+struct clock_sync_data {
int in_sync;
unsigned long long fixup_cc;
-} etr_sync;
+};
-static void etr_sync_cpu_start(void *dummy)
+static void clock_sync_cpu_start(void *dummy)
{
- etr_enable_sync_clock();
+ struct clock_sync_data *sync = dummy;
+
+ enable_sync_clock();
/*
* This looks like a busy wait loop but it isn't. etr_sync_cpus
* is called on all other cpus while the TOD clocks is stopped.
* __udelay will stop the cpu on an enabled wait psw until the
* TOD is running again.
*/
- while (etr_sync.in_sync == 0) {
+ while (sync->in_sync == 0) {
__udelay(1);
/*
* A different cpu changes *in_sync. Therefore use
@@ -650,17 +666,17 @@ static void etr_sync_cpu_start(void *dummy)
*/
barrier();
}
- if (etr_sync.in_sync != 1)
+ if (sync->in_sync != 1)
/* Didn't work. Clear per-cpu in sync bit again. */
- etr_disable_sync_clock(NULL);
+ disable_sync_clock(NULL);
/*
* This round of TOD syncing is done. Set the clock comparator
* to the next tick and let the processor continue.
*/
- fixup_clock_comparator(etr_sync.fixup_cc);
+ fixup_clock_comparator(sync->fixup_cc);
}
-static void etr_sync_cpu_end(void *dummy)
+static void clock_sync_cpu_end(void *dummy)
{
}
@@ -672,6 +688,7 @@ static void etr_sync_cpu_end(void *dummy)
static int etr_sync_clock(struct etr_aib *aib, int port)
{
struct etr_aib *sync_port;
+ struct clock_sync_data etr_sync;
unsigned long long clock, old_clock, delay, delta;
int follows;
int rc;
@@ -690,9 +707,9 @@ static int etr_sync_clock(struct etr_aib *aib, int port)
*/
memset(&etr_sync, 0, sizeof(etr_sync));
preempt_disable();
- smp_call_function(etr_sync_cpu_start, NULL, 0, 0);
+ smp_call_function(clock_sync_cpu_start, &etr_sync, 0, 0);
local_irq_disable();
- etr_enable_sync_clock();
+ enable_sync_clock();
/* Set clock to next OTE. */
__ctl_set_bit(14, 21);
@@ -707,13 +724,13 @@ static int etr_sync_clock(struct etr_aib *aib, int port)
/* Adjust Linux timing variables. */
delay = (unsigned long long)
(aib->edf2.etv - sync_port->edf2.etv) << 32;
- delta = etr_adjust_time(old_clock, clock, delay);
+ delta = adjust_time(old_clock, clock, delay);
etr_sync.fixup_cc = delta;
fixup_clock_comparator(delta);
/* Verify that the clock is properly set. */
if (!etr_aib_follows(sync_port, aib, port)) {
/* Didn't work. */
- etr_disable_sync_clock(NULL);
+ disable_sync_clock(NULL);
etr_sync.in_sync = -EAGAIN;
rc = -EAGAIN;
} else {
@@ -724,12 +741,12 @@ static int etr_sync_clock(struct etr_aib *aib, int port)
/* Could not set the clock ?!? */
__ctl_clear_bit(0, 29);
__ctl_clear_bit(14, 21);
- etr_disable_sync_clock(NULL);
+ disable_sync_clock(NULL);
etr_sync.in_sync = -EAGAIN;
rc = -EAGAIN;
}
local_irq_enable();
- smp_call_function(etr_sync_cpu_end,NULL,0,0);
+ smp_call_function(clock_sync_cpu_end, NULL, 0, 0);
preempt_enable();
return rc;
}
@@ -832,7 +849,7 @@ static struct etr_eacr etr_handle_update(struct etr_aib *aib,
* Do not try to get the alternate port aib if the clock
* is not in sync yet.
*/
- if (!eacr.es)
+ if (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags) && !eacr.es)
return eacr;
/*
@@ -840,7 +857,7 @@ static struct etr_eacr etr_handle_update(struct etr_aib *aib,
* the other port immediately. If only stetr is available the
* data-port bit toggle has to be used.
*/
- if (test_bit(ETR_FLAG_STEAI, &etr_flags)) {
+ if (etr_steai_available) {
if (eacr.p0 && !etr_port0_uptodate) {
etr_steai_cv(&etr_port0, ETR_STEAI_PORT_0);
etr_port0_uptodate = 1;
@@ -909,10 +926,10 @@ static void etr_work_fn(struct work_struct *work)
if (!eacr.ea) {
/* Both ports offline. Reset everything. */
eacr.dp = eacr.es = eacr.sl = 0;
- on_each_cpu(etr_disable_sync_clock, NULL, 0, 1);
+ on_each_cpu(disable_sync_clock, NULL, 0, 1);
del_timer_sync(&etr_timer);
etr_update_eacr(eacr);
- set_bit(ETR_FLAG_EACCES, &etr_flags);
+ clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
return;
}
@@ -953,7 +970,6 @@ static void etr_work_fn(struct work_struct *work)
eacr.e1 = 1;
sync_port = (etr_port0_uptodate &&
etr_port_valid(&etr_port0, 0)) ? 0 : -1;
- clear_bit(ETR_FLAG_EACCES, &etr_flags);
} else if (eacr.p1 && aib.esw.psc1 == etr_lpsc_pps_mode) {
eacr.sl = 0;
eacr.e0 = 0;
@@ -962,7 +978,6 @@ static void etr_work_fn(struct work_struct *work)
eacr.es = 0;
sync_port = (etr_port1_uptodate &&
etr_port_valid(&etr_port1, 1)) ? 1 : -1;
- clear_bit(ETR_FLAG_EACCES, &etr_flags);
} else if (eacr.p0 && aib.esw.psc0 == etr_lpsc_operational_step) {
eacr.sl = 1;
eacr.e0 = 1;
@@ -976,7 +991,6 @@ static void etr_work_fn(struct work_struct *work)
eacr.e1 = 1;
sync_port = (etr_port0_uptodate &&
etr_port_valid(&etr_port0, 0)) ? 0 : -1;
- clear_bit(ETR_FLAG_EACCES, &etr_flags);
} else if (eacr.p1 && aib.esw.psc1 == etr_lpsc_operational_step) {
eacr.sl = 1;
eacr.e0 = 0;
@@ -985,19 +999,22 @@ static void etr_work_fn(struct work_struct *work)
eacr.es = 0;
sync_port = (etr_port1_uptodate &&
etr_port_valid(&etr_port1, 1)) ? 1 : -1;
- clear_bit(ETR_FLAG_EACCES, &etr_flags);
} else {
/* Both ports not usable. */
eacr.es = eacr.sl = 0;
sync_port = -1;
- set_bit(ETR_FLAG_EACCES, &etr_flags);
+ clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
}
+ if (!test_bit(CLOCK_SYNC_ETR, &clock_sync_flags))
+ eacr.es = 0;
+
/*
* If the clock is in sync just update the eacr and return.
* If there is no valid sync port wait for a port update.
*/
- if (eacr.es || sync_port < 0) {
+ if (test_bit(CLOCK_SYNC_STP, &clock_sync_flags) ||
+ eacr.es || sync_port < 0) {
etr_update_eacr(eacr);
etr_set_tolec_timeout(now);
return;
@@ -1018,11 +1035,13 @@ static void etr_work_fn(struct work_struct *work)
* and set up a timer to try again after 0.5 seconds
*/
etr_update_eacr(eacr);
+ set_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
if (now < etr_tolec + (1600000 << 12) ||
etr_sync_clock(&aib, sync_port) != 0) {
/* Sync failed. Try again in 1/2 second. */
eacr.es = 0;
etr_update_eacr(eacr);
+ clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
etr_set_sync_timeout();
} else
etr_set_tolec_timeout(now);
@@ -1097,8 +1116,8 @@ static ssize_t etr_online_store(struct sys_device *dev,
value = simple_strtoul(buf, NULL, 0);
if (value != 0 && value != 1)
return -EINVAL;
- if (test_bit(ETR_FLAG_ENOSYS, &etr_flags))
- return -ENOSYS;
+ if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags))
+ return -EOPNOTSUPP;
if (dev == &etr_port0_dev) {
if (etr_port0_online == value)
return count; /* Nothing to do. */
@@ -1292,3 +1311,318 @@ out:
}
device_initcall(etr_init_sysfs);
+
+/*
+ * Server Time Protocol (STP) code.
+ */
+static int stp_online;
+static struct stp_sstpi stp_info;
+static void *stp_page;
+
+static void stp_work_fn(struct work_struct *work);
+static DECLARE_WORK(stp_work, stp_work_fn);
+
+static int __init early_parse_stp(char *p)
+{
+ if (strncmp(p, "off", 3) == 0)
+ stp_online = 0;
+ else if (strncmp(p, "on", 2) == 0)
+ stp_online = 1;
+ return 0;
+}
+early_param("stp", early_parse_stp);
+
+/*
+ * Reset STP attachment.
+ */
+static void stp_reset(void)
+{
+ int rc;
+
+ stp_page = alloc_bootmem_pages(PAGE_SIZE);
+ rc = chsc_sstpc(stp_page, STP_OP_CTRL, 0x0000);
+ if (rc == 1)
+ set_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags);
+ else if (stp_online) {
+ printk(KERN_WARNING "Running on non STP capable machine.\n");
+ free_bootmem((unsigned long) stp_page, PAGE_SIZE);
+ stp_page = NULL;
+ stp_online = 0;
+ }
+}
+
+static int __init stp_init(void)
+{
+ if (test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags) && stp_online)
+ schedule_work(&stp_work);
+ return 0;
+}
+
+arch_initcall(stp_init);
+
+/*
+ * STP timing alert. There are three causes:
+ * 1) timing status change
+ * 2) link availability change
+ * 3) time control parameter change
+ * In all three cases we are only interested in the clock source state.
+ * If a STP clock source is now available use it.
+ */
+static void stp_timing_alert(struct stp_irq_parm *intparm)
+{
+ if (intparm->tsc || intparm->lac || intparm->tcpc)
+ schedule_work(&stp_work);
+}
+
+/*
+ * STP sync check machine check. This is called when the timing state
+ * changes from the synchronized state to the unsynchronized state.
+ * After a STP sync check the clock is not in sync. The machine check
+ * is broadcasted to all cpus at the same time.
+ */
+void stp_sync_check(void)
+{
+ if (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags))
+ return;
+ disable_sync_clock(NULL);
+ schedule_work(&stp_work);
+}
+
+/*
+ * STP island condition machine check. This is called when an attached
+ * server attempts to communicate over an STP link and the servers
+ * have matching CTN ids and have a valid stratum-1 configuration
+ * but the configurations do not match.
+ */
+void stp_island_check(void)
+{
+ if (!test_bit(CLOCK_SYNC_STP, &clock_sync_flags))
+ return;
+ disable_sync_clock(NULL);
+ schedule_work(&stp_work);
+}
+
+/*
+ * STP tasklet. Check for the STP state and take over the clock
+ * synchronization if the STP clock source is usable.
+ */
+static void stp_work_fn(struct work_struct *work)
+{
+ struct clock_sync_data stp_sync;
+ unsigned long long old_clock, delta;
+ int rc;
+
+ if (!stp_online) {
+ chsc_sstpc(stp_page, STP_OP_CTRL, 0x0000);
+ return;
+ }
+
+ rc = chsc_sstpc(stp_page, STP_OP_CTRL, 0xb0e0);
+ if (rc)
+ return;
+
+ rc = chsc_sstpi(stp_page, &stp_info, sizeof(struct stp_sstpi));
+ if (rc || stp_info.c == 0)
+ return;
+
+ /*
+ * Catch all other cpus and make them wait until we have
+ * successfully synced the clock. smp_call_function will
+ * return after all other cpus are in clock_sync_cpu_start.
+ */
+ memset(&stp_sync, 0, sizeof(stp_sync));
+ preempt_disable();
+ smp_call_function(clock_sync_cpu_start, &stp_sync, 0, 0);
+ local_irq_disable();
+ enable_sync_clock();
+
+ set_bit(CLOCK_SYNC_STP, &clock_sync_flags);
+ if (test_and_clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags))
+ schedule_work(&etr_work);
+
+ rc = 0;
+ if (stp_info.todoff[0] || stp_info.todoff[1] ||
+ stp_info.todoff[2] || stp_info.todoff[3] ||
+ stp_info.tmd != 2) {
+ old_clock = get_clock();
+ rc = chsc_sstpc(stp_page, STP_OP_SYNC, 0);
+ if (rc == 0) {
+ delta = adjust_time(old_clock, get_clock(), 0);
+ fixup_clock_comparator(delta);
+ rc = chsc_sstpi(stp_page, &stp_info,
+ sizeof(struct stp_sstpi));
+ if (rc == 0 && stp_info.tmd != 2)
+ rc = -EAGAIN;
+ }
+ }
+ if (rc) {
+ disable_sync_clock(NULL);
+ stp_sync.in_sync = -EAGAIN;
+ clear_bit(CLOCK_SYNC_STP, &clock_sync_flags);
+ if (etr_port0_online || etr_port1_online)
+ schedule_work(&etr_work);
+ } else
+ stp_sync.in_sync = 1;
+
+ local_irq_enable();
+ smp_call_function(clock_sync_cpu_end, NULL, 0, 0);
+ preempt_enable();
+}
+
+/*
+ * STP class sysfs interface functions
+ */
+static struct sysdev_class stp_sysclass = {
+ .name = "stp",
+};
+
+static ssize_t stp_ctn_id_show(struct sysdev_class *class, char *buf)
+{
+ if (!stp_online)
+ return -ENODATA;
+ return sprintf(buf, "%016llx\n",
+ *(unsigned long long *) stp_info.ctnid);
+}
+
+static SYSDEV_CLASS_ATTR(ctn_id, 0400, stp_ctn_id_show, NULL);
+
+static ssize_t stp_ctn_type_show(struct sysdev_class *class, char *buf)
+{
+ if (!stp_online)
+ return -ENODATA;
+ return sprintf(buf, "%i\n", stp_info.ctn);
+}
+
+static SYSDEV_CLASS_ATTR(ctn_type, 0400, stp_ctn_type_show, NULL);
+
+static ssize_t stp_dst_offset_show(struct sysdev_class *class, char *buf)
+{
+ if (!stp_online || !(stp_info.vbits & 0x2000))
+ return -ENODATA;
+ return sprintf(buf, "%i\n", (int)(s16) stp_info.dsto);
+}
+
+static SYSDEV_CLASS_ATTR(dst_offset, 0400, stp_dst_offset_show, NULL);
+
+static ssize_t stp_leap_seconds_show(struct sysdev_class *class, char *buf)
+{
+ if (!stp_online || !(stp_info.vbits & 0x8000))
+ return -ENODATA;
+ return sprintf(buf, "%i\n", (int)(s16) stp_info.leaps);
+}
+
+static SYSDEV_CLASS_ATTR(leap_seconds, 0400, stp_leap_seconds_show, NULL);
+
+static ssize_t stp_stratum_show(struct sysdev_class *class, char *buf)
+{
+ if (!stp_online)
+ return -ENODATA;
+ return sprintf(buf, "%i\n", (int)(s16) stp_info.stratum);
+}
+
+static SYSDEV_CLASS_ATTR(stratum, 0400, stp_stratum_show, NULL);
+
+static ssize_t stp_time_offset_show(struct sysdev_class *class, char *buf)
+{
+ if (!stp_online || !(stp_info.vbits & 0x0800))
+ return -ENODATA;
+ return sprintf(buf, "%i\n", (int) stp_info.tto);
+}
+
+static SYSDEV_CLASS_ATTR(time_offset, 0400, stp_time_offset_show, NULL);
+
+static ssize_t stp_time_zone_offset_show(struct sysdev_class *class, char *buf)
+{
+ if (!stp_online || !(stp_info.vbits & 0x4000))
+ return -ENODATA;
+ return sprintf(buf, "%i\n", (int)(s16) stp_info.tzo);
+}
+
+static SYSDEV_CLASS_ATTR(time_zone_offset, 0400,
+ stp_time_zone_offset_show, NULL);
+
+static ssize_t stp_timing_mode_show(struct sysdev_class *class, char *buf)
+{
+ if (!stp_online)
+ return -ENODATA;
+ return sprintf(buf, "%i\n", stp_info.tmd);
+}
+
+static SYSDEV_CLASS_ATTR(timing_mode, 0400, stp_timing_mode_show, NULL);
+
+static ssize_t stp_timing_state_show(struct sysdev_class *class, char *buf)
+{
+ if (!stp_online)
+ return -ENODATA;
+ return sprintf(buf, "%i\n", stp_info.tst);
+}
+
+static SYSDEV_CLASS_ATTR(timing_state, 0400, stp_timing_state_show, NULL);
+
+static ssize_t stp_online_show(struct sysdev_class *class, char *buf)
+{
+ return sprintf(buf, "%i\n", stp_online);
+}
+
+static ssize_t stp_online_store(struct sysdev_class *class,
+ const char *buf, size_t count)
+{
+ unsigned int value;
+
+ value = simple_strtoul(buf, NULL, 0);
+ if (value != 0 && value != 1)
+ return -EINVAL;
+ if (!test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags))
+ return -EOPNOTSUPP;
+ stp_online = value;
+ schedule_work(&stp_work);
+ return count;
+}
+
+/*
+ * Can't use SYSDEV_CLASS_ATTR because the attribute should be named
+ * stp/online but attr_online already exists in this file ..
+ */
+static struct sysdev_class_attribute attr_stp_online = {
+ .attr = { .name = "online", .mode = 0600 },
+ .show = stp_online_show,
+ .store = stp_online_store,
+};
+
+static struct sysdev_class_attribute *stp_attributes[] = {
+ &attr_ctn_id,
+ &attr_ctn_type,
+ &attr_dst_offset,
+ &attr_leap_seconds,
+ &attr_stp_online,
+ &attr_stratum,
+ &attr_time_offset,
+ &attr_time_zone_offset,
+ &attr_timing_mode,
+ &attr_timing_state,
+ NULL
+};
+
+static int __init stp_init_sysfs(void)
+{
+ struct sysdev_class_attribute **attr;
+ int rc;
+
+ rc = sysdev_class_register(&stp_sysclass);
+ if (rc)
+ goto out;
+ for (attr = stp_attributes; *attr; attr++) {
+ rc = sysdev_class_create_file(&stp_sysclass, *attr);
+ if (rc)
+ goto out_unreg;
+ }
+ return 0;
+out_unreg:
+ for (; attr >= stp_attributes; attr--)
+ sysdev_class_remove_file(&stp_sysclass, *attr);
+ sysdev_class_unregister(&stp_sysclass);
+out:
+ return rc;
+}
+
+device_initcall(stp_init_sysfs);
diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S
index b4607155e8d0..76c1e60c92f3 100644
--- a/arch/s390/kernel/vmlinux.lds.S
+++ b/arch/s390/kernel/vmlinux.lds.S
@@ -40,7 +40,6 @@ SECTIONS
_etext = .; /* End of text section */
NOTES :text :note
- BUG_TABLE :text
RODATA
diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c
index ca90ee3f930e..eb08d04bc1ec 100644
--- a/arch/s390/kernel/vtime.c
+++ b/arch/s390/kernel/vtime.c
@@ -136,7 +136,7 @@ static inline void set_vtimer(__u64 expires)
}
#endif
-static void start_cpu_timer(void)
+void vtime_start_cpu_timer(void)
{
struct vtimer_queue *vt_list;
@@ -150,7 +150,7 @@ static void start_cpu_timer(void)
set_vtimer(vt_list->idle);
}
-static void stop_cpu_timer(void)
+void vtime_stop_cpu_timer(void)
{
struct vtimer_queue *vt_list;
@@ -536,24 +536,6 @@ void init_cpu_vtimer(void)
}
-static int vtimer_idle_notify(struct notifier_block *self,
- unsigned long action, void *hcpu)
-{
- switch (action) {
- case S390_CPU_IDLE:
- stop_cpu_timer();
- break;
- case S390_CPU_NOT_IDLE:
- start_cpu_timer();
- break;
- }
- return NOTIFY_OK;
-}
-
-static struct notifier_block vtimer_idle_nb = {
- .notifier_call = vtimer_idle_notify,
-};
-
void __init vtime_init(void)
{
/* request the cpu timer external interrupt */
@@ -561,9 +543,6 @@ void __init vtime_init(void)
&ext_int_info_timer) != 0)
panic("Couldn't request external interrupt 0x1005");
- if (register_idle_notifier(&vtimer_idle_nb))
- panic("Couldn't register idle notifier");
-
/* Enable cpu timer interrupts on the boot cpu. */
init_cpu_vtimer();
}
diff --git a/arch/s390/kvm/diag.c b/arch/s390/kvm/diag.c
index f639a152869f..a0775e1f08df 100644
--- a/arch/s390/kvm/diag.c
+++ b/arch/s390/kvm/diag.c
@@ -20,7 +20,7 @@ static int __diag_time_slice_end(struct kvm_vcpu *vcpu)
VCPU_EVENT(vcpu, 5, "%s", "diag time slice end");
vcpu->stat.diagnose_44++;
vcpu_put(vcpu);
- schedule();
+ yield();
vcpu_load(vcpu);
return 0;
}
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index fcd1ed8015c1..84a7fed4cd4e 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -339,6 +339,11 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu)
if (kvm_cpu_has_interrupt(vcpu))
return 0;
+ __set_cpu_idle(vcpu);
+ spin_lock_bh(&vcpu->arch.local_int.lock);
+ vcpu->arch.local_int.timer_due = 0;
+ spin_unlock_bh(&vcpu->arch.local_int.lock);
+
if (psw_interrupts_disabled(vcpu)) {
VCPU_EVENT(vcpu, 3, "%s", "disabled wait");
__unset_cpu_idle(vcpu);
@@ -366,8 +371,6 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu)
no_timer:
spin_lock_bh(&vcpu->arch.local_int.float_int->lock);
spin_lock_bh(&vcpu->arch.local_int.lock);
- __set_cpu_idle(vcpu);
- vcpu->arch.local_int.timer_due = 0;
add_wait_queue(&vcpu->arch.local_int.wq, &wait);
while (list_empty(&vcpu->arch.local_int.list) &&
list_empty(&vcpu->arch.local_int.float_int->list) &&
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 0ac36a649eba..4585c8ac2b0c 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -79,10 +79,6 @@ void kvm_arch_hardware_disable(void *garbage)
{
}
-void decache_vcpus_on_cpu(int cpu)
-{
-}
-
int kvm_arch_hardware_setup(void)
{
return 0;
@@ -423,6 +419,8 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
return -EINVAL; /* not implemented yet */
}
+extern void s390_handle_mcck(void);
+
static void __vcpu_run(struct kvm_vcpu *vcpu)
{
memcpy(&vcpu->arch.sie_block->gg14, &vcpu->arch.guest_gprs[14], 16);
@@ -430,13 +428,21 @@ static void __vcpu_run(struct kvm_vcpu *vcpu)
if (need_resched())
schedule();
+ if (test_thread_flag(TIF_MCCK_PENDING))
+ s390_handle_mcck();
+
+ kvm_s390_deliver_pending_interrupts(vcpu);
+
vcpu->arch.sie_block->icptcode = 0;
local_irq_disable();
kvm_guest_enter();
local_irq_enable();
VCPU_EVENT(vcpu, 6, "entering sie flags %x",
atomic_read(&vcpu->arch.sie_block->cpuflags));
- sie64a(vcpu->arch.sie_block, vcpu->arch.guest_gprs);
+ if (sie64a(vcpu->arch.sie_block, vcpu->arch.guest_gprs)) {
+ VCPU_EVENT(vcpu, 3, "%s", "fault in sie instruction");
+ kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+ }
VCPU_EVENT(vcpu, 6, "exit sie icptcode %d",
vcpu->arch.sie_block->icptcode);
local_irq_disable();
@@ -475,7 +481,6 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
might_sleep();
do {
- kvm_s390_deliver_pending_interrupts(vcpu);
__vcpu_run(vcpu);
rc = kvm_handle_sie_intercept(vcpu);
} while (!signal_pending(current) && !rc);
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c
index 5c1aea97cd12..3d98ba82ea67 100644
--- a/arch/s390/mm/pgtable.c
+++ b/arch/s390/mm/pgtable.c
@@ -254,36 +254,46 @@ void disable_noexec(struct mm_struct *mm, struct task_struct *tsk)
int s390_enable_sie(void)
{
struct task_struct *tsk = current;
- struct mm_struct *mm;
- int rc;
+ struct mm_struct *mm, *old_mm;
- task_lock(tsk);
-
- rc = 0;
+ /* Do we have pgstes? if yes, we are done */
if (tsk->mm->context.pgstes)
- goto unlock;
+ return 0;
- rc = -EINVAL;
+ /* lets check if we are allowed to replace the mm */
+ task_lock(tsk);
if (!tsk->mm || atomic_read(&tsk->mm->mm_users) > 1 ||
- tsk->mm != tsk->active_mm || tsk->mm->ioctx_list)
- goto unlock;
+ tsk->mm != tsk->active_mm || tsk->mm->ioctx_list) {
+ task_unlock(tsk);
+ return -EINVAL;
+ }
+ task_unlock(tsk);
- tsk->mm->context.pgstes = 1; /* dirty little tricks .. */
+ /* we copy the mm with pgstes enabled */
+ tsk->mm->context.pgstes = 1;
mm = dup_mm(tsk);
tsk->mm->context.pgstes = 0;
-
- rc = -ENOMEM;
if (!mm)
- goto unlock;
- mmput(tsk->mm);
+ return -ENOMEM;
+
+ /* Now lets check again if somebody attached ptrace etc */
+ task_lock(tsk);
+ if (!tsk->mm || atomic_read(&tsk->mm->mm_users) > 1 ||
+ tsk->mm != tsk->active_mm || tsk->mm->ioctx_list) {
+ mmput(mm);
+ task_unlock(tsk);
+ return -EINVAL;
+ }
+
+ /* ok, we are alone. No ptrace, no threads, etc. */
+ old_mm = tsk->mm;
tsk->mm = tsk->active_mm = mm;
preempt_disable();
update_mm(mm, tsk);
cpu_set(smp_processor_id(), mm->cpu_vm_mask);
preempt_enable();
- rc = 0;
-unlock:
task_unlock(tsk);
- return rc;
+ mmput(old_mm);
+ return 0;
}
EXPORT_SYMBOL_GPL(s390_enable_sie);
diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c
index f591188fa2c0..e4868bfc672f 100644
--- a/arch/s390/mm/vmem.c
+++ b/arch/s390/mm/vmem.c
@@ -236,7 +236,7 @@ static int insert_memory_segment(struct memory_segment *seg)
{
struct memory_segment *tmp;
- if (seg->start + seg->size >= VMEM_MAX_PHYS ||
+ if (seg->start + seg->size > VMEM_MAX_PHYS ||
seg->start + seg->size < seg->start)
return -ERANGE;
diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig
index 9a854c8e5274..10a0c184444e 100644
--- a/arch/sh/Kconfig
+++ b/arch/sh/Kconfig
@@ -476,6 +476,10 @@ config SH_RTS7751R2D
Select RTS7751R2D if configuring for a Renesas Technology
Sales SH-Graphics board.
+config SH_RSK7203
+ bool "RSK7203"
+ depends on CPU_SUBTYPE_SH7203
+
config SH_SDK7780
bool "SDK7780R3"
depends on CPU_SUBTYPE_SH7780
@@ -497,6 +501,20 @@ config SH_MIGOR
Select Migo-R if configuring for the SH7722 Migo-R platform
by Renesas System Solutions Asia Pte. Ltd.
+config SH_AP325RXA
+ bool "AP-325RXA"
+ select CPU_SUBTYPE_SH7723
+ help
+ Renesas "AP-325RXA" support.
+ Compatible with ALGO SYSTEM CO.,LTD. "AP-320A"
+
+config SH_SH7763RDP
+ bool "SH7763RDP"
+ depends on CPU_SUBTYPE_SH7763
+ help
+ Select SH7763RDP if configuring for a Renesas SH7763
+ evaluation board.
+
config SH_EDOSK7705
bool "EDOSK7705"
depends on CPU_SUBTYPE_SH7705
diff --git a/arch/sh/Makefile b/arch/sh/Makefile
index fb7b1b15e392..1452c4df7dea 100644
--- a/arch/sh/Makefile
+++ b/arch/sh/Makefile
@@ -121,6 +121,9 @@ machdir-$(CONFIG_SH_HIGHLANDER) += renesas/r7780rp
machdir-$(CONFIG_SH_MIGOR) += renesas/migor
machdir-$(CONFIG_SH_SDK7780) += renesas/sdk7780
machdir-$(CONFIG_SH_X3PROTO) += renesas/x3proto
+machdir-$(CONFIG_SH_RSK7203) += renesas/rsk7203
+machdir-$(CONFIG_SH_AP325RXA) += renesas/ap325rxa
+machdir-$(CONFIG_SH_SH7763RDP) += renesas/sh7763rdp
machdir-$(CONFIG_SH_SH4202_MICRODEV) += superh/microdev
machdir-$(CONFIG_SH_LANDISK) += landisk
machdir-$(CONFIG_SH_TITAN) += titan
diff --git a/arch/sh/boards/renesas/ap325rxa/Makefile b/arch/sh/boards/renesas/ap325rxa/Makefile
new file mode 100644
index 000000000000..f663768429f0
--- /dev/null
+++ b/arch/sh/boards/renesas/ap325rxa/Makefile
@@ -0,0 +1 @@
+obj-y := setup.o
diff --git a/arch/sh/boards/renesas/ap325rxa/setup.c b/arch/sh/boards/renesas/ap325rxa/setup.c
new file mode 100644
index 000000000000..e33340cafa01
--- /dev/null
+++ b/arch/sh/boards/renesas/ap325rxa/setup.c
@@ -0,0 +1,108 @@
+/*
+ * Renesas - AP-325RXA
+ * (Compatible with Algo System ., LTD. - AP-320A)
+ *
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Author : Yusuke Goda <goda.yuske@renesas.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/physmap.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+static struct resource smc9118_resources[] = {
+ [0] = {
+ .start = 0xb6080000,
+ .end = 0xb60fffff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = 35,
+ .end = 35,
+ .flags = IORESOURCE_IRQ,
+ }
+};
+
+static struct platform_device smc9118_device = {
+ .name = "smc911x",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(smc9118_resources),
+ .resource = smc9118_resources,
+};
+
+static struct mtd_partition ap325rxa_nor_flash_partitions[] = {
+ {
+ .name = "uboot",
+ .offset = 0,
+ .size = (1 * 1024 * 1024),
+ .mask_flags = MTD_WRITEABLE, /* Read-only */
+ }, {
+ .name = "kernel",
+ .offset = MTDPART_OFS_APPEND,
+ .size = (2 * 1024 * 1024),
+ }, {
+ .name = "other",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL,
+ },
+};
+
+static struct physmap_flash_data ap325rxa_nor_flash_data = {
+ .width = 2,
+ .parts = ap325rxa_nor_flash_partitions,
+ .nr_parts = ARRAY_SIZE(ap325rxa_nor_flash_partitions),
+};
+
+static struct resource ap325rxa_nor_flash_resources[] = {
+ [0] = {
+ .name = "NOR Flash",
+ .start = 0x00000000,
+ .end = 0x00ffffff,
+ .flags = IORESOURCE_MEM,
+ }
+};
+
+static struct platform_device ap325rxa_nor_flash_device = {
+ .name = "physmap-flash",
+ .resource = ap325rxa_nor_flash_resources,
+ .num_resources = ARRAY_SIZE(ap325rxa_nor_flash_resources),
+ .dev = {
+ .platform_data = &ap325rxa_nor_flash_data,
+ },
+};
+
+static struct platform_device *ap325rxa_devices[] __initdata = {
+ &smc9118_device,
+ &ap325rxa_nor_flash_device
+};
+
+static int __init ap325rxa_devices_setup(void)
+{
+ return platform_add_devices(ap325rxa_devices,
+ ARRAY_SIZE(ap325rxa_devices));
+}
+device_initcall(ap325rxa_devices_setup);
+
+#define MSTPCR0 (0xA4150030)
+#define MSTPCR2 (0xA4150038)
+
+static void __init ap325rxa_setup(char **cmdline_p)
+{
+ /* enable VEU0 + VEU1 */
+ ctrl_outl(ctrl_inl(MSTPCR2) & ~0x00000044, MSTPCR2); /* bit 2 + 6 */
+
+ /* enable MERAM */
+ ctrl_outl(ctrl_inl(MSTPCR0) & ~0x00000001, MSTPCR0); /* bit 0 */
+}
+
+static struct sh_machine_vector mv_ap325rxa __initmv = {
+ .mv_name = "AP-325RXA",
+ .mv_setup = ap325rxa_setup,
+};
diff --git a/arch/sh/boards/renesas/rsk7203/Makefile b/arch/sh/boards/renesas/rsk7203/Makefile
new file mode 100644
index 000000000000..f663768429f0
--- /dev/null
+++ b/arch/sh/boards/renesas/rsk7203/Makefile
@@ -0,0 +1 @@
+obj-y := setup.o
diff --git a/arch/sh/boards/renesas/rsk7203/setup.c b/arch/sh/boards/renesas/rsk7203/setup.c
new file mode 100644
index 000000000000..0bbda04b03b9
--- /dev/null
+++ b/arch/sh/boards/renesas/rsk7203/setup.c
@@ -0,0 +1,126 @@
+/*
+ * Renesas Technology Europe RSK+ 7203 Support.
+ *
+ * Copyright (C) 2008 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/physmap.h>
+#include <linux/mtd/map.h>
+#include <asm/machvec.h>
+#include <asm/io.h>
+
+static struct resource smc911x_resources[] = {
+ [0] = {
+ .start = 0x24000000,
+ .end = 0x24000000 + 0x100,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = 64,
+ .end = 64,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device smc911x_device = {
+ .name = "smc911x",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(smc911x_resources),
+ .resource = smc911x_resources,
+};
+
+static const char *probes[] = { "cmdlinepart", NULL };
+
+static struct mtd_partition *parsed_partitions;
+
+static struct mtd_partition rsk7203_partitions[] = {
+ {
+ .name = "Bootloader",
+ .offset = 0x00000000,
+ .size = 0x00040000,
+ .mask_flags = MTD_WRITEABLE,
+ }, {
+ .name = "Kernel",
+ .offset = MTDPART_OFS_NXTBLK,
+ .size = 0x001c0000,
+ }, {
+ .name = "Flash_FS",
+ .offset = MTDPART_OFS_NXTBLK,
+ .size = MTDPART_SIZ_FULL,
+ }
+};
+
+static struct physmap_flash_data flash_data = {
+ .width = 2,
+};
+
+static struct resource flash_resource = {
+ .start = 0x20000000,
+ .end = 0x20400000,
+ .flags = IORESOURCE_MEM,
+};
+
+static struct platform_device flash_device = {
+ .name = "physmap-flash",
+ .id = -1,
+ .resource = &flash_resource,
+ .num_resources = 1,
+ .dev = {
+ .platform_data = &flash_data,
+ },
+};
+
+static struct mtd_info *flash_mtd;
+
+static struct map_info rsk7203_flash_map = {
+ .name = "RSK+ Flash",
+ .size = 0x400000,
+ .bankwidth = 2,
+};
+
+static void __init set_mtd_partitions(void)
+{
+ int nr_parts = 0;
+
+ simple_map_init(&rsk7203_flash_map);
+ flash_mtd = do_map_probe("cfi_probe", &rsk7203_flash_map);
+ nr_parts = parse_mtd_partitions(flash_mtd, probes,
+ &parsed_partitions, 0);
+ /* If there is no partition table, used the hard coded table */
+ if (nr_parts <= 0) {
+ flash_data.parts = rsk7203_partitions;
+ flash_data.nr_parts = ARRAY_SIZE(rsk7203_partitions);
+ } else {
+ flash_data.nr_parts = nr_parts;
+ flash_data.parts = parsed_partitions;
+ }
+}
+
+
+static struct platform_device *rsk7203_devices[] __initdata = {
+ &smc911x_device,
+ &flash_device,
+};
+
+static int __init rsk7203_devices_setup(void)
+{
+ set_mtd_partitions();
+ return platform_add_devices(rsk7203_devices,
+ ARRAY_SIZE(rsk7203_devices));
+}
+device_initcall(rsk7203_devices_setup);
+
+/*
+ * The Machine Vector
+ */
+static struct sh_machine_vector mv_rsk7203 __initmv = {
+ .mv_name = "RSK+7203",
+};
diff --git a/arch/sh/boards/renesas/sh7763rdp/Makefile b/arch/sh/boards/renesas/sh7763rdp/Makefile
new file mode 100644
index 000000000000..f6c0b55516d2
--- /dev/null
+++ b/arch/sh/boards/renesas/sh7763rdp/Makefile
@@ -0,0 +1 @@
+obj-y := setup.o irq.o
diff --git a/arch/sh/boards/renesas/sh7763rdp/irq.c b/arch/sh/boards/renesas/sh7763rdp/irq.c
new file mode 100644
index 000000000000..fd850bad2dec
--- /dev/null
+++ b/arch/sh/boards/renesas/sh7763rdp/irq.c
@@ -0,0 +1,45 @@
+/*
+ * linux/arch/sh/boards/renesas/sh7763rdp/irq.c
+ *
+ * Renesas Solutions SH7763RDP Support.
+ *
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Copyright (C) 2008 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/sh7763rdp.h>
+
+#define INTC_BASE (0xFFD00000)
+#define INTC_INT2PRI7 (INTC_BASE+0x4001C)
+#define INTC_INT2MSKCR (INTC_BASE+0x4003C)
+#define INTC_INT2MSKCR1 (INTC_BASE+0x400D4)
+
+/*
+ * Initialize IRQ setting
+ */
+void __init init_sh7763rdp_IRQ(void)
+{
+ /* GPIO enabled */
+ ctrl_outl(1 << 25, INTC_INT2MSKCR);
+
+ /* enable GPIO interrupts */
+ ctrl_outl((ctrl_inl(INTC_INT2PRI7) & 0xFF00FFFF) | 0x000F0000,
+ INTC_INT2PRI7);
+
+ /* USBH enabled */
+ ctrl_outl(1 << 17, INTC_INT2MSKCR1);
+
+ /* GETHER enabled */
+ ctrl_outl(1 << 16, INTC_INT2MSKCR1);
+
+ /* DMAC enabled */
+ ctrl_outl(1 << 8, INTC_INT2MSKCR);
+}
diff --git a/arch/sh/boards/renesas/sh7763rdp/setup.c b/arch/sh/boards/renesas/sh7763rdp/setup.c
new file mode 100644
index 000000000000..925f16af7121
--- /dev/null
+++ b/arch/sh/boards/renesas/sh7763rdp/setup.c
@@ -0,0 +1,128 @@
+/*
+ * linux/arch/sh/boards/renesas/sh7763rdp/setup.c
+ *
+ * Renesas Solutions sh7763rdp board
+ *
+ * Copyright (C) 2008 Renesas Solutions Corp.
+ * Copyright (C) 2008 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/mtd/physmap.h>
+#include <asm/io.h>
+#include <asm/sh7763rdp.h>
+
+/* NOR Flash */
+static struct mtd_partition sh7763rdp_nor_flash_partitions[] = {
+ {
+ .name = "U-Boot",
+ .offset = 0,
+ .size = (2 * 128 * 1024),
+ .mask_flags = MTD_WRITEABLE, /* Read-only */
+ }, {
+ .name = "Linux-Kernel",
+ .offset = MTDPART_OFS_APPEND,
+ .size = (20 * 128 * 1024),
+ }, {
+ .name = "Root Filesystem",
+ .offset = MTDPART_OFS_APPEND,
+ .size = MTDPART_SIZ_FULL,
+ },
+};
+
+static struct physmap_flash_data sh7763rdp_nor_flash_data = {
+ .width = 2,
+ .parts = sh7763rdp_nor_flash_partitions,
+ .nr_parts = ARRAY_SIZE(sh7763rdp_nor_flash_partitions),
+};
+
+static struct resource sh7763rdp_nor_flash_resources[] = {
+ [0] = {
+ .name = "NOR Flash",
+ .start = 0,
+ .end = (64 * 1024 * 1024),
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device sh7763rdp_nor_flash_device = {
+ .name = "physmap-flash",
+ .resource = sh7763rdp_nor_flash_resources,
+ .num_resources = ARRAY_SIZE(sh7763rdp_nor_flash_resources),
+ .dev = {
+ .platform_data = &sh7763rdp_nor_flash_data,
+ },
+};
+
+static struct platform_device *sh7763rdp_devices[] __initdata = {
+ &sh7763rdp_nor_flash_device,
+};
+
+static int __init sh7763rdp_devices_setup(void)
+{
+ return platform_add_devices(sh7763rdp_devices,
+ ARRAY_SIZE(sh7763rdp_devices));
+}
+__initcall(sh7763rdp_devices_setup);
+
+static void __init sh7763rdp_setup(char **cmdline_p)
+{
+ /* Board version check */
+ if (ctrl_inw(CPLD_BOARD_ID_ERV_REG) == 0xECB1)
+ printk(KERN_INFO "RTE Standard Configuration\n");
+ else
+ printk(KERN_INFO "RTA Standard Configuration\n");
+
+ /* USB pin select bits (clear bit 5-2 to 0) */
+ ctrl_outw((ctrl_inw(PORT_PSEL2) & 0xFFC3), PORT_PSEL2);
+ /* USBH setup port I controls to other (clear bits 4-9 to 0) */
+ ctrl_outw(ctrl_inw(PORT_PICR) & 0xFC0F, PORT_PICR);
+
+ /* Select USB Host controller */
+ ctrl_outw(0x00, USB_USBHSC);
+
+ /* For LCD */
+ /* set PTJ7-1, bits 15-2 of PJCR to 0 */
+ ctrl_outw(ctrl_inw(PORT_PJCR) & 0x0003, PORT_PJCR);
+ /* set PTI5, bits 11-10 of PICR to 0 */
+ ctrl_outw(ctrl_inw(PORT_PICR) & 0xF3FF, PORT_PICR);
+ ctrl_outw(0, PORT_PKCR);
+ ctrl_outw(0, PORT_PLCR);
+ /* set PSEL2 bits 14-8, 5-4, of PSEL2 to 0 */
+ ctrl_outw((ctrl_inw(PORT_PSEL2) & 0x00C0), PORT_PSEL2);
+ /* set PSEL3 bits 14-12, 6-4, 2-0 of PSEL3 to 0 */
+ ctrl_outw((ctrl_inw(PORT_PSEL3) & 0x0700), PORT_PSEL3);
+
+ /* For HAC */
+ /* bit3-0 0100:HAC & SSI1 enable */
+ ctrl_outw((ctrl_inw(PORT_PSEL1) & 0xFFF0) | 0x0004, PORT_PSEL1);
+ /* bit14 1:SSI_HAC_CLK enable */
+ ctrl_outw(ctrl_inw(PORT_PSEL4) | 0x4000, PORT_PSEL4);
+
+ /* SH-Ether */
+ ctrl_outw((ctrl_inw(PORT_PSEL1) & ~0xff00) | 0x2400, PORT_PSEL1);
+ ctrl_outw(0x0, PORT_PFCR);
+ ctrl_outw(0x0, PORT_PFCR);
+ ctrl_outw(0x0, PORT_PFCR);
+
+ /* MMC */
+ /*selects SCIF and MMC other functions */
+ ctrl_outw(0x0001, PORT_PSEL0);
+ /* MMC clock operates */
+ ctrl_outl(ctrl_inl(MSTPCR1) & ~0x8, MSTPCR1);
+ ctrl_outw(ctrl_inw(PORT_PACR) & ~0x3000, PORT_PACR);
+ ctrl_outw(ctrl_inw(PORT_PCCR) & ~0xCFC3, PORT_PCCR);
+}
+
+static struct sh_machine_vector mv_sh7763rdp __initmv = {
+ .mv_name = "sh7763drp",
+ .mv_setup = sh7763rdp_setup,
+ .mv_nr_irqs = 112,
+ .mv_init_irq = init_sh7763rdp_IRQ,
+};
diff --git a/arch/sh/configs/ap325rxa_defconfig b/arch/sh/configs/ap325rxa_defconfig
new file mode 100644
index 000000000000..5471df53753c
--- /dev/null
+++ b/arch/sh/configs/ap325rxa_defconfig
@@ -0,0 +1,947 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.26-rc4
+# Wed Jun 4 17:30:00 2008
+#
+CONFIG_SUPERH=y
+CONFIG_SUPERH32=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_FIND_NEXT_BIT=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_ARCH_NO_VIRT_TO_BUS=y
+CONFIG_ARCH_SUPPORTS_AOUT=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+CONFIG_GROUP_SCHED=y
+CONFIG_FAIR_GROUP_SCHED=y
+# CONFIG_RT_GROUP_SCHED is not set
+CONFIG_USER_SCHED=y
+# CONFIG_CGROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+# CONFIG_NAMESPACES is not set
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+# CONFIG_KALLSYMS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_HAVE_KPROBES is not set
+# CONFIG_HAVE_KRETPROBES is not set
+# CONFIG_HAVE_DMA_ATTRS is not set
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+# CONFIG_DEFAULT_AS is not set
+# CONFIG_DEFAULT_DEADLINE is not set
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+CONFIG_CLASSIC_RCU=y
+
+#
+# System type
+#
+CONFIG_CPU_SH4=y
+CONFIG_CPU_SH4A=y
+CONFIG_CPU_SHX2=y
+# CONFIG_CPU_SUBTYPE_SH7619 is not set
+# CONFIG_CPU_SUBTYPE_SH7203 is not set
+# CONFIG_CPU_SUBTYPE_SH7206 is not set
+# CONFIG_CPU_SUBTYPE_SH7263 is not set
+# CONFIG_CPU_SUBTYPE_MXG is not set
+# CONFIG_CPU_SUBTYPE_SH7705 is not set
+# CONFIG_CPU_SUBTYPE_SH7706 is not set
+# CONFIG_CPU_SUBTYPE_SH7707 is not set
+# CONFIG_CPU_SUBTYPE_SH7708 is not set
+# CONFIG_CPU_SUBTYPE_SH7709 is not set
+# CONFIG_CPU_SUBTYPE_SH7710 is not set
+# CONFIG_CPU_SUBTYPE_SH7712 is not set
+# CONFIG_CPU_SUBTYPE_SH7720 is not set
+# CONFIG_CPU_SUBTYPE_SH7721 is not set
+# CONFIG_CPU_SUBTYPE_SH7750 is not set
+# CONFIG_CPU_SUBTYPE_SH7091 is not set
+# CONFIG_CPU_SUBTYPE_SH7750R is not set
+# CONFIG_CPU_SUBTYPE_SH7750S is not set
+# CONFIG_CPU_SUBTYPE_SH7751 is not set
+# CONFIG_CPU_SUBTYPE_SH7751R is not set
+# CONFIG_CPU_SUBTYPE_SH7760 is not set
+# CONFIG_CPU_SUBTYPE_SH4_202 is not set
+CONFIG_CPU_SUBTYPE_SH7723=y
+# CONFIG_CPU_SUBTYPE_SH7763 is not set
+# CONFIG_CPU_SUBTYPE_SH7770 is not set
+# CONFIG_CPU_SUBTYPE_SH7780 is not set
+# CONFIG_CPU_SUBTYPE_SH7785 is not set
+# CONFIG_CPU_SUBTYPE_SHX3 is not set
+# CONFIG_CPU_SUBTYPE_SH7343 is not set
+# CONFIG_CPU_SUBTYPE_SH7722 is not set
+# CONFIG_CPU_SUBTYPE_SH7366 is not set
+# CONFIG_CPU_SUBTYPE_SH5_101 is not set
+# CONFIG_CPU_SUBTYPE_SH5_103 is not set
+
+#
+# Memory management options
+#
+CONFIG_QUICKLIST=y
+CONFIG_MMU=y
+CONFIG_PAGE_OFFSET=0x80000000
+CONFIG_MEMORY_START=0x08000000
+CONFIG_MEMORY_SIZE=0x08000000
+CONFIG_29BIT=y
+# CONFIG_X2TLB is not set
+CONFIG_VSYSCALL=y
+CONFIG_ARCH_FLATMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_DEFAULT=y
+CONFIG_MAX_ACTIVE_REGIONS=1
+CONFIG_ARCH_POPULATES_NODE_MAP=y
+CONFIG_ARCH_SELECT_MEMORY_MODEL=y
+CONFIG_PAGE_SIZE_4KB=y
+# CONFIG_PAGE_SIZE_8KB is not set
+# CONFIG_PAGE_SIZE_16KB is not set
+# CONFIG_PAGE_SIZE_64KB is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_SPARSEMEM_STATIC=y
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_NR_QUICK=2
+
+#
+# Cache configuration
+#
+# CONFIG_SH_DIRECT_MAPPED is not set
+CONFIG_CACHE_WRITEBACK=y
+# CONFIG_CACHE_WRITETHROUGH is not set
+# CONFIG_CACHE_OFF is not set
+
+#
+# Processor features
+#
+CONFIG_CPU_LITTLE_ENDIAN=y
+# CONFIG_CPU_BIG_ENDIAN is not set
+CONFIG_SH_FPU=y
+# CONFIG_SH_STORE_QUEUES is not set
+CONFIG_CPU_HAS_INTEVT=y
+CONFIG_CPU_HAS_SR_RB=y
+CONFIG_CPU_HAS_PTEA=y
+CONFIG_CPU_HAS_FPU=y
+
+#
+# Board support
+#
+CONFIG_SH_AP325RXA=y
+
+#
+# Timer and clock configuration
+#
+CONFIG_SH_TMU=y
+CONFIG_SH_TIMER_IRQ=16
+CONFIG_SH_PCLK_FREQ=33333333
+CONFIG_TICK_ONESHOT=y
+# CONFIG_NO_HZ is not set
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# DMA support
+#
+# CONFIG_SH_DMA is not set
+
+#
+# Companion Chips
+#
+
+#
+# Additional SuperH Device Drivers
+#
+# CONFIG_HEARTBEAT is not set
+# CONFIG_PUSH_SWITCH is not set
+
+#
+# Kernel features
+#
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_250=y
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=250
+# CONFIG_SCHED_HRTICK is not set
+# CONFIG_KEXEC is not set
+# CONFIG_CRASH_DUMP is not set
+# CONFIG_PREEMPT_NONE is not set
+# CONFIG_PREEMPT_VOLUNTARY is not set
+CONFIG_PREEMPT=y
+# CONFIG_PREEMPT_RCU is not set
+CONFIG_GUSA=y
+
+#
+# Boot options
+#
+CONFIG_ZERO_PAGE_OFFSET=0x00001000
+CONFIG_BOOT_LINK_OFFSET=0x00800000
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=tty1 console=ttySC5,38400 root=/dev/nfs ip=dhcp"
+
+#
+# Bus options
+#
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_ASK_IP_FIB_HASH=y
+# CONFIG_IP_FIB_TRIE is not set
+CONFIG_IP_FIB_HASH=y
+# CONFIG_IP_MULTIPLE_TABLES is not set
+# CONFIG_IP_ROUTE_MULTIPATH is not set
+# CONFIG_IP_ROUTE_VERBOSE is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+
+#
+# Wireless
+#
+# CONFIG_CFG80211 is not set
+# CONFIG_WIRELESS_EXT is not set
+# CONFIG_MAC80211 is not set
+# CONFIG_IEEE80211 is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+CONFIG_MTD_CONCAT=y
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_CFI_INTELEXT is not set
+CONFIG_MTD_CFI_AMDSTD=y
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_START=0xffffffff
+CONFIG_MTD_PHYSMAP_LEN=0
+CONFIG_MTD_PHYSMAP_BANKWIDTH=0
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=4
+CONFIG_BLK_DEV_RAM_SIZE=4096
+# CONFIG_BLK_DEV_XIP is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_BLK_DEV_SR is not set
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+CONFIG_SCSI_LOWLEVEL=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NETDEVICES_MULTIQUEUE is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+# CONFIG_PHYLIB is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_AX88796 is not set
+# CONFIG_STNIC is not set
+# CONFIG_SMC91X is not set
+CONFIG_SMC911X=y
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_B44 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+# CONFIG_IWLWIFI_LEDS is not set
+# CONFIG_WAN is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_SH_SCI=y
+CONFIG_SERIAL_SH_SCI_NR_UARTS=6
+CONFIG_SERIAL_SH_SCI_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_I2C is not set
+# CONFIG_SPI is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DVB_CORE is not set
+# CONFIG_VIDEO_MEDIA is not set
+
+#
+# Multimedia drivers
+#
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+CONFIG_DUMMY_CONSOLE=y
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+# CONFIG_HID_SUPPORT is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_NEW_LEDS is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_RTC_CLASS is not set
+# CONFIG_UIO is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_XATTR=y
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+# CONFIG_EXT4DEV_FS is not set
+CONFIG_JBD=y
+CONFIG_FS_MBCACHE=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+# CONFIG_MSDOS_FS is not set
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLBFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS2_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+# CONFIG_NFS_V4 is not set
+CONFIG_NFSD=y
+CONFIG_NFSD_V3=y
+# CONFIG_NFSD_V3_ACL is not set
+# CONFIG_NFSD_V4 is not set
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+# CONFIG_SUNRPC_BIND34 is not set
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+CONFIG_NLS_CODEPAGE_932=y
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_SAMPLES is not set
+# CONFIG_SH_STANDARD_BIOS is not set
+# CONFIG_EARLY_SCIF_CONSOLE is not set
+# CONFIG_SH_KGDB is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_MANAGER=y
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+CONFIG_CRYPTO_CBC=y
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_MD4 is not set
+# CONFIG_CRYPTO_MD5 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_DES is not set
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+
+#
+# Compression
+#
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_LZO is not set
+CONFIG_CRYPTO_HW=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_GENERIC_FIND_FIRST_BIT is not set
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/sh/configs/sh7763rdp_defconfig b/arch/sh/configs/sh7763rdp_defconfig
new file mode 100644
index 000000000000..83f3fe5db3e5
--- /dev/null
+++ b/arch/sh/configs/sh7763rdp_defconfig
@@ -0,0 +1,1052 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.26-rc4
+# Fri Jun 6 12:20:17 2008
+#
+CONFIG_SUPERH=y
+CONFIG_SUPERH32=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_FIND_NEXT_BIT=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_ARCH_NO_VIRT_TO_BUS=y
+CONFIG_ARCH_SUPPORTS_AOUT=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+CONFIG_GROUP_SCHED=y
+CONFIG_FAIR_GROUP_SCHED=y
+# CONFIG_RT_GROUP_SCHED is not set
+CONFIG_USER_SCHED=y
+# CONFIG_CGROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+CONFIG_NAMESPACES=y
+CONFIG_UTS_NS=y
+CONFIG_IPC_NS=y
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+# CONFIG_SYSCTL_SYSCALL is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+CONFIG_PROFILING=y
+# CONFIG_MARKERS is not set
+CONFIG_OPROFILE=y
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_HAVE_KPROBES is not set
+# CONFIG_HAVE_KRETPROBES is not set
+# CONFIG_HAVE_DMA_ATTRS is not set
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+# CONFIG_MODULE_UNLOAD is not set
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+# CONFIG_KMOD is not set
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+CONFIG_DEFAULT_AS=y
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="anticipatory"
+CONFIG_CLASSIC_RCU=y
+
+#
+# System type
+#
+CONFIG_CPU_SH4=y
+CONFIG_CPU_SH4A=y
+# CONFIG_CPU_SUBTYPE_SH7619 is not set
+# CONFIG_CPU_SUBTYPE_SH7203 is not set
+# CONFIG_CPU_SUBTYPE_SH7206 is not set
+# CONFIG_CPU_SUBTYPE_SH7263 is not set
+# CONFIG_CPU_SUBTYPE_MXG is not set
+# CONFIG_CPU_SUBTYPE_SH7705 is not set
+# CONFIG_CPU_SUBTYPE_SH7706 is not set
+# CONFIG_CPU_SUBTYPE_SH7707 is not set
+# CONFIG_CPU_SUBTYPE_SH7708 is not set
+# CONFIG_CPU_SUBTYPE_SH7709 is not set
+# CONFIG_CPU_SUBTYPE_SH7710 is not set
+# CONFIG_CPU_SUBTYPE_SH7712 is not set
+# CONFIG_CPU_SUBTYPE_SH7720 is not set
+# CONFIG_CPU_SUBTYPE_SH7721 is not set
+# CONFIG_CPU_SUBTYPE_SH7750 is not set
+# CONFIG_CPU_SUBTYPE_SH7091 is not set
+# CONFIG_CPU_SUBTYPE_SH7750R is not set
+# CONFIG_CPU_SUBTYPE_SH7750S is not set
+# CONFIG_CPU_SUBTYPE_SH7751 is not set
+# CONFIG_CPU_SUBTYPE_SH7751R is not set
+# CONFIG_CPU_SUBTYPE_SH7760 is not set
+# CONFIG_CPU_SUBTYPE_SH4_202 is not set
+# CONFIG_CPU_SUBTYPE_SH7723 is not set
+CONFIG_CPU_SUBTYPE_SH7763=y
+# CONFIG_CPU_SUBTYPE_SH7770 is not set
+# CONFIG_CPU_SUBTYPE_SH7780 is not set
+# CONFIG_CPU_SUBTYPE_SH7785 is not set
+# CONFIG_CPU_SUBTYPE_SHX3 is not set
+# CONFIG_CPU_SUBTYPE_SH7343 is not set
+# CONFIG_CPU_SUBTYPE_SH7722 is not set
+# CONFIG_CPU_SUBTYPE_SH7366 is not set
+# CONFIG_CPU_SUBTYPE_SH5_101 is not set
+# CONFIG_CPU_SUBTYPE_SH5_103 is not set
+
+#
+# Memory management options
+#
+CONFIG_QUICKLIST=y
+CONFIG_MMU=y
+CONFIG_PAGE_OFFSET=0x80000000
+CONFIG_MEMORY_START=0x0c000000
+CONFIG_MEMORY_SIZE=0x04000000
+CONFIG_29BIT=y
+CONFIG_VSYSCALL=y
+CONFIG_ARCH_FLATMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_DEFAULT=y
+CONFIG_MAX_ACTIVE_REGIONS=1
+CONFIG_ARCH_POPULATES_NODE_MAP=y
+CONFIG_ARCH_SELECT_MEMORY_MODEL=y
+CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
+CONFIG_PAGE_SIZE_4KB=y
+# CONFIG_PAGE_SIZE_8KB is not set
+# CONFIG_PAGE_SIZE_16KB is not set
+# CONFIG_PAGE_SIZE_64KB is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+# CONFIG_FLATMEM_MANUAL is not set
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+CONFIG_SPARSEMEM_MANUAL=y
+CONFIG_SPARSEMEM=y
+CONFIG_HAVE_MEMORY_PRESENT=y
+CONFIG_SPARSEMEM_STATIC=y
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+# CONFIG_MEMORY_HOTPLUG is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+CONFIG_NR_QUICK=2
+
+#
+# Cache configuration
+#
+# CONFIG_SH_DIRECT_MAPPED is not set
+CONFIG_CACHE_WRITEBACK=y
+# CONFIG_CACHE_WRITETHROUGH is not set
+# CONFIG_CACHE_OFF is not set
+
+#
+# Processor features
+#
+CONFIG_CPU_LITTLE_ENDIAN=y
+# CONFIG_CPU_BIG_ENDIAN is not set
+CONFIG_SH_FPU=y
+# CONFIG_SH_STORE_QUEUES is not set
+CONFIG_CPU_HAS_INTEVT=y
+CONFIG_CPU_HAS_SR_RB=y
+CONFIG_CPU_HAS_FPU=y
+
+#
+# Board support
+#
+CONFIG_SH_SH7763RDP=y
+
+#
+# Timer and clock configuration
+#
+CONFIG_SH_TMU=y
+CONFIG_SH_TIMER_IRQ=28
+CONFIG_SH_PCLK_FREQ=66666666
+# CONFIG_TICK_ONESHOT is not set
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# DMA support
+#
+# CONFIG_SH_DMA is not set
+
+#
+# Companion Chips
+#
+
+#
+# Additional SuperH Device Drivers
+#
+# CONFIG_HEARTBEAT is not set
+# CONFIG_PUSH_SWITCH is not set
+
+#
+# Kernel features
+#
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_250=y
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=250
+# CONFIG_SCHED_HRTICK is not set
+# CONFIG_KEXEC is not set
+# CONFIG_CRASH_DUMP is not set
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+CONFIG_GUSA=y
+
+#
+# Boot options
+#
+CONFIG_ZERO_PAGE_OFFSET=0x00001000
+CONFIG_BOOT_LINK_OFFSET=0x00800000
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="console=ttySC2,115200 root=/dev/sda1 rootdelay=10"
+
+#
+# Bus options
+#
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=y
+CONFIG_INET_TCP_DIAG=y
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IPV6 is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+
+#
+# Wireless
+#
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_EXT=y
+# CONFIG_MAC80211 is not set
+# CONFIG_IEEE80211 is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+# CONFIG_MTD_CHAR is not set
+CONFIG_MTD_BLKDEVS=y
+# CONFIG_MTD_BLOCK is not set
+# CONFIG_MTD_BLOCK_RO is not set
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_GEN_PROBE=y
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+CONFIG_MTD_CFI_NOSWAP=y
+# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set
+# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set
+CONFIG_MTD_CFI_GEOMETRY=y
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+# CONFIG_MTD_MAP_BANK_WIDTH_4 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_OTP is not set
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_CFI_STAA=y
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+CONFIG_MTD_PHYSMAP=y
+CONFIG_MTD_PHYSMAP_START=0x8000000
+CONFIG_MTD_PHYSMAP_LEN=0
+CONFIG_MTD_PHYSMAP_BANKWIDTH=2
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_UB is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_MISC_DEVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_BLK_DEV_SR is not set
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+# CONFIG_SCSI_MULTI_LUN is not set
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+CONFIG_SCSI_LOWLEVEL=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NETDEVICES_MULTIQUEUE is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+CONFIG_PHYLIB=y
+
+#
+# MII PHY device drivers
+#
+# CONFIG_MARVELL_PHY is not set
+# CONFIG_DAVICOM_PHY is not set
+# CONFIG_QSEMI_PHY is not set
+# CONFIG_LXT_PHY is not set
+# CONFIG_CICADA_PHY is not set
+# CONFIG_VITESSE_PHY is not set
+# CONFIG_SMSC_PHY is not set
+# CONFIG_BROADCOM_PHY is not set
+# CONFIG_ICPLUS_PHY is not set
+# CONFIG_REALTEK_PHY is not set
+# CONFIG_FIXED_PHY is not set
+CONFIG_MDIO_BITBANG=y
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_AX88796 is not set
+# CONFIG_STNIC is not set
+# CONFIG_SMC91X is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_B44 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+# CONFIG_IWLWIFI_LEDS is not set
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_WAN is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_SH_SCI=y
+CONFIG_SERIAL_SH_SCI_NR_UARTS=3
+CONFIG_SERIAL_SH_SCI_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=256
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_I2C is not set
+# CONFIG_SPI is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DVB_CORE is not set
+# CONFIG_VIDEO_MEDIA is not set
+
+#
+# Multimedia drivers
+#
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+# CONFIG_FB is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+# CONFIG_HID_SUPPORT is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+
+#
+# Miscellaneous USB options
+#
+# CONFIG_USB_DEVICEFS is not set
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
+CONFIG_USB_OHCI_HCD=y
+# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
+# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
+# CONFIG_USB_WDM is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# may also be needed; see USB_STORAGE Help for more information
+#
+CONFIG_USB_STORAGE=y
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_DPCM is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_ONETOUCH is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_MICROTEK is not set
+CONFIG_USB_MON=y
+
+#
+# USB port drivers
+#
+# CONFIG_USB_SERIAL is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_AUERSWALD is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_BERRY_CHARGE is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_PHIDGET is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_GADGET is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_NEW_LEDS is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_RTC_CLASS is not set
+# CONFIG_UIO is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_XATTR=y
+# CONFIG_EXT3_FS_POSIX_ACL is not set
+# CONFIG_EXT3_FS_SECURITY is not set
+# CONFIG_EXT4DEV_FS is not set
+CONFIG_JBD=y
+CONFIG_FS_MBCACHE=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+CONFIG_AUTOFS_FS=y
+CONFIG_AUTOFS4_FS=y
+# CONFIG_FUSE_FS is not set
+CONFIG_GENERIC_ACL=y
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+# CONFIG_HUGETLBFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS2_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+# CONFIG_NFS_V3 is not set
+# CONFIG_NFS_V4 is not set
+# CONFIG_NFSD is not set
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+# CONFIG_SUNRPC_BIND34 is not set
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=y
+CONFIG_NLS_CODEPAGE_775=y
+CONFIG_NLS_CODEPAGE_850=y
+CONFIG_NLS_CODEPAGE_852=y
+CONFIG_NLS_CODEPAGE_855=y
+CONFIG_NLS_CODEPAGE_857=y
+CONFIG_NLS_CODEPAGE_860=y
+CONFIG_NLS_CODEPAGE_861=y
+CONFIG_NLS_CODEPAGE_862=y
+CONFIG_NLS_CODEPAGE_863=y
+CONFIG_NLS_CODEPAGE_864=y
+CONFIG_NLS_CODEPAGE_865=y
+CONFIG_NLS_CODEPAGE_866=y
+CONFIG_NLS_CODEPAGE_869=y
+CONFIG_NLS_CODEPAGE_936=y
+CONFIG_NLS_CODEPAGE_950=y
+CONFIG_NLS_CODEPAGE_932=y
+CONFIG_NLS_CODEPAGE_949=y
+CONFIG_NLS_CODEPAGE_874=y
+CONFIG_NLS_ISO8859_8=y
+CONFIG_NLS_CODEPAGE_1250=y
+CONFIG_NLS_CODEPAGE_1251=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=y
+CONFIG_NLS_ISO8859_3=y
+CONFIG_NLS_ISO8859_4=y
+CONFIG_NLS_ISO8859_5=y
+CONFIG_NLS_ISO8859_6=y
+CONFIG_NLS_ISO8859_7=y
+CONFIG_NLS_ISO8859_9=y
+CONFIG_NLS_ISO8859_13=y
+CONFIG_NLS_ISO8859_14=y
+CONFIG_NLS_ISO8859_15=y
+CONFIG_NLS_KOI8_R=y
+CONFIG_NLS_KOI8_U=y
+CONFIG_NLS_UTF8=y
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+# CONFIG_PRINTK_TIME is not set
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_FRAME_WARN=1024
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_SAMPLES is not set
+# CONFIG_SH_STANDARD_BIOS is not set
+# CONFIG_EARLY_SCIF_CONSOLE is not set
+# CONFIG_SH_KGDB is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+# CONFIG_CRYPTO_MANAGER is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+# CONFIG_CRYPTO_CBC is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_MD4 is not set
+# CONFIG_CRYPTO_MD5 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_ARC4 is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_DES is not set
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+
+#
+# Compression
+#
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_LZO is not set
+CONFIG_CRYPTO_HW=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_GENERIC_FIND_FIRST_BIT is not set
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/arch/sh/kernel/Makefile_32 b/arch/sh/kernel/Makefile_32
index 4bbdce36b92b..0e6905fe9fec 100644
--- a/arch/sh/kernel/Makefile_32
+++ b/arch/sh/kernel/Makefile_32
@@ -21,7 +21,7 @@ obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
obj-$(CONFIG_PM) += pm.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
-obj-$(CONFIG_BINFMT_ELF) += dump_task.o
+obj-$(CONFIG_ELF_CORE) += dump_task.o
obj-$(CONFIG_IO_TRAPPED) += io_trapped.o
EXTRA_CFLAGS += -Werror
diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7763.c b/arch/sh/kernel/cpu/sh4a/setup-sh7763.c
index f189a559462b..3b278df8a53b 100644
--- a/arch/sh/kernel/cpu/sh4a/setup-sh7763.c
+++ b/arch/sh/kernel/cpu/sh4a/setup-sh7763.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2006 Paul Mundt
* Copyright (C) 2007 Yoshihiro Shimoda
+ * Copyright (C) 2008 Nobuhiro Iwamatsu
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
@@ -56,6 +57,11 @@ static struct plat_sci_port sci_platform_data[] = {
.type = PORT_SCIF,
.irqs = { 76, 77, 79, 78 },
}, {
+ .mapbase = 0xffe10000,
+ .flags = UPF_BOOT_AUTOCONF,
+ .type = PORT_SCIF,
+ .irqs = { 104, 105, 107, 106 },
+ }, {
.flags = 0,
}
};
@@ -208,8 +214,8 @@ static struct intc_vect vectors[] __initdata = {
INTC_VECT(TMU5, 0xe40), INTC_VECT(ADC, 0xe60),
INTC_VECT(SSI0, 0xe80), INTC_VECT(SSI1, 0xea0),
INTC_VECT(SSI2, 0xec0), INTC_VECT(SSI3, 0xee0),
- INTC_VECT(SCIF1_ERI, 0xf00), INTC_VECT(SCIF1_RXI, 0xf20),
- INTC_VECT(SCIF1_BRI, 0xf40), INTC_VECT(SCIF1_TXI, 0xf60),
+ INTC_VECT(SCIF2_ERI, 0xf00), INTC_VECT(SCIF2_RXI, 0xf20),
+ INTC_VECT(SCIF2_BRI, 0xf40), INTC_VECT(SCIF2_TXI, 0xf60),
INTC_VECT(GPIO_CH0, 0xf80), INTC_VECT(GPIO_CH1, 0xfa0),
INTC_VECT(GPIO_CH2, 0xfc0), INTC_VECT(GPIO_CH3, 0xfe0),
};
diff --git a/arch/sh/kernel/ptrace_32.c b/arch/sh/kernel/ptrace_32.c
index fddb547f3c2b..2bc72def5cf8 100644
--- a/arch/sh/kernel/ptrace_32.c
+++ b/arch/sh/kernel/ptrace_32.c
@@ -241,6 +241,29 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data)
break;
}
#endif
+#ifdef CONFIG_BINFMT_ELF_FDPIC
+ case PTRACE_GETFDPIC: {
+ unsigned long tmp = 0;
+
+ switch (addr) {
+ case PTRACE_GETFDPIC_EXEC:
+ tmp = child->mm->context.exec_fdpic_loadmap;
+ break;
+ case PTRACE_GETFDPIC_INTERP:
+ tmp = child->mm->context.interp_fdpic_loadmap;
+ break;
+ default:
+ break;
+ }
+
+ ret = 0;
+ if (put_user(tmp, (unsigned long *) data)) {
+ ret = -EFAULT;
+ break;
+ }
+ break;
+ }
+#endif
default:
ret = ptrace_request(child, request, addr, data);
break;
diff --git a/arch/sh/kernel/signal_32.c b/arch/sh/kernel/signal_32.c
index f311551d9a05..46170a9a7221 100644
--- a/arch/sh/kernel/signal_32.c
+++ b/arch/sh/kernel/signal_32.c
@@ -33,6 +33,11 @@
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+struct fdpic_func_descriptor {
+ unsigned long text;
+ unsigned long GOT;
+};
+
/*
* Atomically swap in the new signal mask, and wait for a signal.
*/
@@ -378,7 +383,15 @@ static int setup_frame(int sig, struct k_sigaction *ka,
regs->regs[4] = signal; /* Arg for signal handler */
regs->regs[5] = 0;
regs->regs[6] = (unsigned long) &frame->sc;
- regs->pc = (unsigned long) ka->sa.sa_handler;
+
+ if (current->personality & FDPIC_FUNCPTRS) {
+ struct fdpic_func_descriptor __user *funcptr =
+ (struct fdpic_func_descriptor __user *)ka->sa.sa_handler;
+
+ __get_user(regs->pc, &funcptr->text);
+ __get_user(regs->regs[12], &funcptr->GOT);
+ } else
+ regs->pc = (unsigned long)ka->sa.sa_handler;
set_fs(USER_DS);
@@ -458,7 +471,15 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
regs->regs[4] = signal; /* Arg for signal handler */
regs->regs[5] = (unsigned long) &frame->info;
regs->regs[6] = (unsigned long) &frame->uc;
- regs->pc = (unsigned long) ka->sa.sa_handler;
+
+ if (current->personality & FDPIC_FUNCPTRS) {
+ struct fdpic_func_descriptor __user *funcptr =
+ (struct fdpic_func_descriptor __user *)ka->sa.sa_handler;
+
+ __get_user(regs->pc, &funcptr->text);
+ __get_user(regs->regs[12], &funcptr->GOT);
+ } else
+ regs->pc = (unsigned long)ka->sa.sa_handler;
set_fs(USER_DS);
diff --git a/arch/sh/kernel/vmlinux_32.lds.S b/arch/sh/kernel/vmlinux_32.lds.S
index c7113786ecd4..7b4b82bd1156 100644
--- a/arch/sh/kernel/vmlinux_32.lds.S
+++ b/arch/sh/kernel/vmlinux_32.lds.S
@@ -44,7 +44,6 @@ SECTIONS
_etext = .; /* End of text section */
- BUG_TABLE
NOTES
RO_DATA(PAGE_SIZE)
diff --git a/arch/sh/kernel/vmlinux_64.lds.S b/arch/sh/kernel/vmlinux_64.lds.S
index d1e177009a41..33fa46451406 100644
--- a/arch/sh/kernel/vmlinux_64.lds.S
+++ b/arch/sh/kernel/vmlinux_64.lds.S
@@ -65,7 +65,6 @@ SECTIONS
_etext = .; /* End of text section */
- BUG_TABLE
NOTES
RO_DATA(PAGE_SIZE)
diff --git a/arch/sh/mm/Kconfig b/arch/sh/mm/Kconfig
index 5fd218430b19..29d8e3c58b34 100644
--- a/arch/sh/mm/Kconfig
+++ b/arch/sh/mm/Kconfig
@@ -145,19 +145,25 @@ choice
config PAGE_SIZE_4KB
bool "4kB"
- depends on !X2TLB
+ depends on !MMU || !X2TLB
help
This is the default page size used by all SuperH CPUs.
config PAGE_SIZE_8KB
bool "8kB"
- depends on X2TLB
+ depends on !MMU || X2TLB
help
This enables 8kB pages as supported by SH-X2 and later MMUs.
+config PAGE_SIZE_16KB
+ bool "16kB"
+ depends on !MMU
+ help
+ This enables 16kB pages on MMU-less SH systems.
+
config PAGE_SIZE_64KB
bool "64kB"
- depends on CPU_SH4 || CPU_SH5
+ depends on !MMU || CPU_SH4 || CPU_SH5
help
This enables support for 64kB pages, possible on all SH-4
CPUs and later.
diff --git a/arch/sh/tools/mach-types b/arch/sh/tools/mach-types
index 1bba7d36be90..4473723685ca 100644
--- a/arch/sh/tools/mach-types
+++ b/arch/sh/tools/mach-types
@@ -46,3 +46,6 @@ R2D_1 RTS7751R2D_1
CAYMAN SH_CAYMAN
SDK7780 SH_SDK7780
MIGOR SH_MIGOR
+RSK7203 SH_RSK7203
+AP325RXA SH_AP325RXA
+SH7763RDP SH_SH7763RDP
diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig
index eb36f3b746b8..fca9246470b1 100644
--- a/arch/sparc64/Kconfig
+++ b/arch/sparc64/Kconfig
@@ -11,6 +11,8 @@ config SPARC
config SPARC64
bool
default y
+ select HAVE_DYNAMIC_FTRACE
+ select HAVE_FTRACE
select HAVE_IDE
select HAVE_LMB
select HAVE_ARCH_KGDB
diff --git a/arch/sparc64/Kconfig.debug b/arch/sparc64/Kconfig.debug
index 6a4d28a4076d..d6d32d178fc8 100644
--- a/arch/sparc64/Kconfig.debug
+++ b/arch/sparc64/Kconfig.debug
@@ -33,7 +33,7 @@ config DEBUG_PAGEALLOC
config MCOUNT
bool
- depends on STACK_DEBUG
+ depends on STACK_DEBUG || FTRACE
default y
config FRAME_POINTER
diff --git a/arch/sparc64/kernel/Makefile b/arch/sparc64/kernel/Makefile
index ec4f5ebb1ca6..418b5782096e 100644
--- a/arch/sparc64/kernel/Makefile
+++ b/arch/sparc64/kernel/Makefile
@@ -14,6 +14,7 @@ obj-y := process.o setup.o cpu.o idprom.o \
power.o sbus.o sparc64_ksyms.o chmc.o \
visemul.o prom.o of_device.o hvapi.o sstate.o mdesc.o
+obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-$(CONFIG_PCI) += ebus.o pci_common.o \
pci_psycho.o pci_sabre.o pci_schizo.o \
diff --git a/arch/sparc64/kernel/ebus.c b/arch/sparc64/kernel/ebus.c
index c49d0388b793..4d58d7ce708d 100644
--- a/arch/sparc64/kernel/ebus.c
+++ b/arch/sparc64/kernel/ebus.c
@@ -401,7 +401,7 @@ static void __init fill_ebus_device(struct device_node *dp, struct linux_ebus_de
dev->ofdev.node = dp;
dev->ofdev.dev.parent = &dev->bus->ofdev.dev;
dev->ofdev.dev.bus = &ebus_bus_type;
- sprintf(dev->ofdev.dev.bus_id, "ebus[%08x]", dp->node);
+ dev_set_name(&dev->ofdev.dev, "ebus[%08x]", dp->node);
/* Register with core */
if (of_device_register(&dev->ofdev) != 0)
@@ -501,7 +501,7 @@ void __init ebus_init(void)
ebus->ofdev.node = dp;
ebus->ofdev.dev.parent = &pdev->dev;
ebus->ofdev.dev.bus = &ebus_bus_type;
- sprintf(ebus->ofdev.dev.bus_id, "ebus%d", num_ebus);
+ dev_set_name(&ebus->ofdev.dev, "ebus%d", num_ebus);
/* Register with core */
if (of_device_register(&ebus->ofdev) != 0)
diff --git a/arch/sparc64/kernel/ftrace.c b/arch/sparc64/kernel/ftrace.c
new file mode 100644
index 000000000000..c17373195b1e
--- /dev/null
+++ b/arch/sparc64/kernel/ftrace.c
@@ -0,0 +1,92 @@
+#include <linux/spinlock.h>
+#include <linux/hardirq.h>
+#include <linux/ftrace.h>
+#include <linux/percpu.h>
+#include <linux/init.h>
+#include <linux/list.h>
+
+static const u32 ftrace_nop = 0x01000000;
+
+notrace unsigned char *ftrace_nop_replace(void)
+{
+ return (char *)&ftrace_nop;
+}
+
+notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
+{
+ static u32 call;
+ s32 off;
+
+ off = ((s32)addr - (s32)ip);
+ call = 0x40000000 | ((u32)off >> 2);
+
+ return (unsigned char *) &call;
+}
+
+notrace int
+ftrace_modify_code(unsigned long ip, unsigned char *old_code,
+ unsigned char *new_code)
+{
+ u32 old = *(u32 *)old_code;
+ u32 new = *(u32 *)new_code;
+ u32 replaced;
+ int faulted;
+
+ __asm__ __volatile__(
+ "1: cas [%[ip]], %[old], %[new]\n"
+ " flush %[ip]\n"
+ " mov 0, %[faulted]\n"
+ "2:\n"
+ " .section .fixup,#alloc,#execinstr\n"
+ " .align 4\n"
+ "3: sethi %%hi(2b), %[faulted]\n"
+ " jmpl %[faulted] + %%lo(2b), %%g0\n"
+ " mov 1, %[faulted]\n"
+ " .previous\n"
+ " .section __ex_table,\"a\"\n"
+ " .align 4\n"
+ " .word 1b, 3b\n"
+ " .previous\n"
+ : "=r" (replaced), [faulted] "=r" (faulted)
+ : [new] "0" (new), [old] "r" (old), [ip] "r" (ip)
+ : "memory");
+
+ if (replaced != old && replaced != new)
+ faulted = 2;
+
+ return faulted;
+}
+
+notrace int ftrace_update_ftrace_func(ftrace_func_t func)
+{
+ unsigned long ip = (unsigned long)(&ftrace_call);
+ unsigned char old[4], *new;
+
+ memcpy(old, &ftrace_call, 4);
+ new = ftrace_call_replace(ip, (unsigned long)func);
+ return ftrace_modify_code(ip, old, new);
+}
+
+notrace int ftrace_mcount_set(unsigned long *data)
+{
+ unsigned long ip = (long)(&mcount_call);
+ unsigned long *addr = data;
+ unsigned char old[4], *new;
+
+ /*
+ * Replace the mcount stub with a pointer to the
+ * ip recorder function.
+ */
+ memcpy(old, &mcount_call, 4);
+ new = ftrace_call_replace(ip, *addr);
+ *addr = ftrace_modify_code(ip, old, new);
+
+ return 0;
+}
+
+
+int __init ftrace_dyn_arch_init(void *data)
+{
+ ftrace_mcount_set(data);
+ return 0;
+}
diff --git a/arch/sparc64/kernel/of_device.c b/arch/sparc64/kernel/of_device.c
index d569f60c24b8..4fd48ab7dda4 100644
--- a/arch/sparc64/kernel/of_device.c
+++ b/arch/sparc64/kernel/of_device.c
@@ -797,9 +797,9 @@ static struct of_device * __init scan_one_device(struct device_node *dp,
op->dev.parent = parent;
op->dev.bus = &of_platform_bus_type;
if (!parent)
- strcpy(op->dev.bus_id, "root");
+ dev_set_name(&op->dev, "root");
else
- sprintf(op->dev.bus_id, "%08x", dp->node);
+ dev_set_name(&op->dev, "%08x", dp->node);
if (of_device_register(op)) {
printk("%s: Could not register of device.\n",
diff --git a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c
index 112b09f16f36..55096195458f 100644
--- a/arch/sparc64/kernel/pci.c
+++ b/arch/sparc64/kernel/pci.c
@@ -408,7 +408,7 @@ struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,
dev->class = class >> 8;
dev->revision = class & 0xff;
- sprintf(pci_name(dev), "%04x:%02x:%02x.%d", pci_domain_nr(bus),
+ dev_set_name(&dev->dev, "%04x:%02x:%02x.%d", pci_domain_nr(bus),
dev->bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn));
if (ofpci_verbose)
diff --git a/arch/sparc64/kernel/vio.c b/arch/sparc64/kernel/vio.c
index e78b3517940b..a490077891a4 100644
--- a/arch/sparc64/kernel/vio.c
+++ b/arch/sparc64/kernel/vio.c
@@ -224,7 +224,7 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
if (!strcmp(type, "domain-services-port"))
bus_id_name = "ds";
- if (strlen(bus_id_name) >= KOBJ_NAME_LEN - 4) {
+ if (strlen(bus_id_name) >= BUS_ID_SIZE - 4) {
printk(KERN_ERR "VIO: bus_id_name [%s] is too long.\n",
bus_id_name);
return NULL;
@@ -260,16 +260,14 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
vio_fill_channel_info(hp, mp, vdev);
if (!id) {
- snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%s",
- bus_id_name);
+ dev_set_name(&vdev->dev, "%s", bus_id_name);
vdev->dev_no = ~(u64)0;
} else if (!cfg_handle) {
- snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%s-%lu",
- bus_id_name, *id);
+ dev_set_name(&vdev->dev, "%s-%lu", bus_id_name, *id);
vdev->dev_no = *id;
} else {
- snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%s-%lu-%lu",
- bus_id_name, *cfg_handle, *id);
+ dev_set_name(&vdev->dev, "%s-%lu-%lu", bus_id_name,
+ *cfg_handle, *id);
vdev->dev_no = *cfg_handle;
}
@@ -292,12 +290,12 @@ static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
}
vdev->dp = dp;
- printk(KERN_INFO "VIO: Adding device %s\n", vdev->dev.bus_id);
+ printk(KERN_INFO "VIO: Adding device %s\n", dev_name(&vdev->dev));
err = device_register(&vdev->dev);
if (err) {
printk(KERN_ERR "VIO: Could not register device %s, err=%d\n",
- vdev->dev.bus_id, err);
+ dev_name(&vdev->dev), err);
kfree(vdev);
return NULL;
}
@@ -330,7 +328,7 @@ static void vio_remove(struct mdesc_handle *hp, u64 node)
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);
+ printk(KERN_INFO "VIO: Removing device %s\n", dev_name(dev));
device_unregister(dev);
}
diff --git a/arch/sparc64/lib/mcount.S b/arch/sparc64/lib/mcount.S
index 9e4534b485c7..7735a7a60533 100644
--- a/arch/sparc64/lib/mcount.S
+++ b/arch/sparc64/lib/mcount.S
@@ -28,10 +28,13 @@ ovstack:
.skip OVSTACKSIZE
#endif
.text
- .align 32
- .globl mcount, _mcount
-mcount:
+ .align 32
+ .globl _mcount
+ .type _mcount,#function
+ .globl mcount
+ .type mcount,#function
_mcount:
+mcount:
#ifdef CONFIG_STACK_DEBUG
/*
* Check whether %sp is dangerously low.
@@ -55,6 +58,53 @@ _mcount:
or %g3, %lo(panicstring), %o0
call prom_halt
nop
+1:
+#endif
+#ifdef CONFIG_FTRACE
+#ifdef CONFIG_DYNAMIC_FTRACE
+ mov %o7, %o0
+ .globl mcount_call
+mcount_call:
+ call ftrace_stub
+ mov %o0, %o7
+#else
+ sethi %hi(ftrace_trace_function), %g1
+ sethi %hi(ftrace_stub), %g2
+ ldx [%g1 + %lo(ftrace_trace_function)], %g1
+ or %g2, %lo(ftrace_stub), %g2
+ cmp %g1, %g2
+ be,pn %icc, 1f
+ mov %i7, %o1
+ jmpl %g1, %g0
+ mov %o7, %o0
+ /* not reached */
+1:
#endif
-1: retl
+#endif
+ retl
nop
+ .size _mcount,.-_mcount
+ .size mcount,.-mcount
+
+#ifdef CONFIG_FTRACE
+ .globl ftrace_stub
+ .type ftrace_stub,#function
+ftrace_stub:
+ retl
+ nop
+ .size ftrace_stub,.-ftrace_stub
+#ifdef CONFIG_DYNAMIC_FTRACE
+ .globl ftrace_caller
+ .type ftrace_caller,#function
+ftrace_caller:
+ mov %i7, %o1
+ mov %o7, %o0
+ .globl ftrace_call
+ftrace_call:
+ call ftrace_stub
+ mov %o0, %o7
+ retl
+ nop
+ .size ftrace_caller,.-ftrace_caller
+#endif
+#endif
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 52e18e6d2ba0..9e3ee5431e31 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -23,6 +23,8 @@ config X86
select HAVE_OPROFILE
select HAVE_KPROBES
select HAVE_KRETPROBES
+ select HAVE_DYNAMIC_FTRACE
+ select HAVE_FTRACE
select HAVE_KVM if ((X86_32 && !X86_VOYAGER && !X86_VISWS && !X86_NUMAQ) || X86_64)
select HAVE_ARCH_KGDB if !X86_VOYAGER
@@ -121,7 +123,7 @@ config ARCH_HAS_CACHE_LINE_SIZE
def_bool y
config HAVE_SETUP_PER_CPU_AREA
- def_bool X86_64 || (X86_SMP && !X86_VOYAGER)
+ def_bool X86_64_SMP || (X86_SMP && !X86_VOYAGER)
config HAVE_CPUMASK_OF_CPU_MAP
def_bool X86_64_SMP
@@ -251,7 +253,7 @@ config X86_ELAN
config X86_VOYAGER
bool "Voyager (NCR)"
- depends on X86_32 && (SMP || BROKEN)
+ depends on X86_32 && (SMP || BROKEN) && !PCI
help
Voyager is an MCA-based 32-way capable SMP architecture proprietary
to NCR Corp. Machine classes 345x/35xx/4100/51xx are Voyager-based.
@@ -261,39 +263,9 @@ config X86_VOYAGER
If you do not specifically know you have a Voyager based machine,
say N here, otherwise the kernel you build will not be bootable.
-config X86_NUMAQ
- bool "NUMAQ (IBM/Sequent)"
- depends on SMP && X86_32
- select NUMA
- help
- This option is used for getting Linux to run on a (IBM/Sequent) NUMA
- multiquad box. This changes the way that processors are bootstrapped,
- and uses Clustered Logical APIC addressing mode instead of Flat Logical.
- You will need a new lynxer.elf file to flash your firmware with - send
- email to <Martin.Bligh@us.ibm.com>.
-
-config X86_SUMMIT
- bool "Summit/EXA (IBM x440)"
- depends on X86_32 && SMP
- help
- This option is needed for IBM systems that use the Summit/EXA chipset.
- In particular, it is needed for the x440.
-
- If you don't have one of these computers, you should say N here.
- If you want to build a NUMA kernel, you must select ACPI.
-
-config X86_BIGSMP
- bool "Support for other sub-arch SMP systems with more than 8 CPUs"
- depends on X86_32 && SMP
- help
- This option is needed for the systems that have more than 8 CPUs
- and if the system is not of any sub-arch type above.
-
- If you don't have such a system, you should say N here.
-
config X86_VISWS
bool "SGI 320/540 (Visual Workstation)"
- depends on X86_32
+ depends on X86_32 && !PCI
help
The SGI Visual Workstation series is an IA32-based workstation
based on SGI systems chips with some legacy PC hardware attached.
@@ -304,12 +276,33 @@ config X86_VISWS
and vice versa. See <file:Documentation/sgi-visws.txt> for details.
config X86_GENERICARCH
- bool "Generic architecture (Summit, bigsmp, ES7000, default)"
+ bool "Generic architecture"
depends on X86_32
help
- This option compiles in the Summit, bigsmp, ES7000, default subarchitectures.
- It is intended for a generic binary kernel.
- If you want a NUMA kernel, select ACPI. We need SRAT for NUMA.
+ This option compiles in the NUMAQ, Summit, bigsmp, ES7000, default
+ subarchitectures. It is intended for a generic binary kernel.
+ if you select them all, kernel will probe it one by one. and will
+ fallback to default.
+
+if X86_GENERICARCH
+
+config X86_NUMAQ
+ bool "NUMAQ (IBM/Sequent)"
+ depends on SMP && X86_32 && PCI
+ select NUMA
+ help
+ This option is used for getting Linux to run on a NUMAQ (IBM/Sequent)
+ NUMA multiquad box. This changes the way that processors are
+ bootstrapped, and uses Clustered Logical APIC addressing mode instead
+ of Flat Logical. You will need a new lynxer.elf file to flash your
+ firmware with - send email to <Martin.Bligh@us.ibm.com>.
+
+config X86_SUMMIT
+ bool "Summit/EXA (IBM x440)"
+ depends on X86_32 && SMP
+ help
+ This option is needed for IBM systems that use the Summit/EXA chipset.
+ In particular, it is needed for the x440.
config X86_ES7000
bool "Support for Unisys ES7000 IA32 series"
@@ -317,8 +310,15 @@ config X86_ES7000
help
Support for Unisys ES7000 systems. Say 'Y' here if this kernel is
supposed to run on an IA32-based Unisys ES7000 system.
- Only choose this option if you have such a system, otherwise you
- should say N here.
+
+config X86_BIGSMP
+ bool "Support for big SMP systems with more than 8 CPUs"
+ depends on X86_32 && SMP
+ help
+ This option is needed for the systems that have more than 8 CPUs
+ and if the system is not of any sub-arch type above.
+
+endif
config X86_RDC321X
bool "RDC R-321x SoC"
@@ -337,7 +337,7 @@ config X86_RDC321X
config X86_VSMP
bool "Support for ScaleMP vSMP"
select PARAVIRT
- depends on X86_64
+ depends on X86_64 && !PCI
help
Support for ScaleMP vSMP systems. Say 'Y' here if this kernel is
supposed to run on these EM64T-based machines. Only choose this option
@@ -412,35 +412,19 @@ config PARAVIRT
endif
-config MEMTEST_BOOTPARAM
- bool "Memtest boot parameter"
+config MEMTEST
+ bool "Memtest"
depends on X86_64
default y
help
This option adds a kernel parameter 'memtest', which allows memtest
- to be disabled at boot. If this option is selected, memtest
- functionality can be disabled with memtest=0 on the kernel
- command line. The purpose of this option is to allow a single
- kernel image to be distributed with memtest built in, but not
- necessarily enabled.
-
+ to be set.
+ memtest=0, mean disabled; -- default
+ memtest=1, mean do 1 test pattern;
+ ...
+ memtest=4, mean do 4 test patterns.
If you are unsure how to answer this question, answer Y.
-config MEMTEST_BOOTPARAM_VALUE
- int "Memtest boot parameter default value (0-4)"
- depends on MEMTEST_BOOTPARAM
- range 0 4
- default 0
- help
- This option sets the default value for the kernel parameter
- 'memtest', which allows memtest to be disabled at boot. If this
- option is set to 0 (zero), the memtest kernel parameter will
- default to 0, disabling memtest at bootup. If this option is
- set to 4, the memtest kernel parameter will default to 4,
- enabling memtest at bootup, and use that as pattern number.
-
- If you are unsure how to answer this question, answer 0.
-
config ACPI_SRAT
def_bool y
depends on X86_32 && ACPI && NUMA && (X86_SUMMIT || X86_GENERICARCH)
@@ -556,20 +540,35 @@ config SWIOTLB
config IOMMU_HELPER
def_bool (CALGARY_IOMMU || GART_IOMMU || SWIOTLB)
+config MAXSMP
+ bool "Configure Maximum number of SMP Processors and NUMA Nodes"
+ depends on X86_64 && SMP
+ default n
+ help
+ Configure maximum number of CPUS and NUMA Nodes for this architecture.
+ If unsure, say N.
+if MAXSMP
config NR_CPUS
- int "Maximum number of CPUs (2-255)"
- range 2 255
+ int
+ default "4096"
+endif
+
+if !MAXSMP
+config NR_CPUS
+ int "Maximum number of CPUs (2-4096)"
+ range 2 4096
depends on SMP
default "32" if X86_NUMAQ || X86_SUMMIT || X86_BIGSMP || X86_ES7000
default "8"
help
This allows you to specify the maximum number of CPUs which this
- kernel will support. The maximum supported value is 255 and the
+ kernel will support. The maximum supported value is 4096 and the
minimum value which makes sense is 2.
This is purely to save memory - each supported CPU adds
- approximately eight kilobytes to the kernel image.
+ approximately one kilobyte to the kernel image.
+endif
config SCHED_SMT
bool "SMT (Hyperthreading) scheduler support"
@@ -906,9 +905,9 @@ config X86_PAE
config NUMA
bool "Numa Memory Allocation and Scheduler Support (EXPERIMENTAL)"
depends on SMP
- depends on X86_64 || (X86_32 && HIGHMEM64G && (X86_NUMAQ || (X86_SUMMIT || X86_GENERICARCH) && ACPI) && EXPERIMENTAL)
+ depends on X86_64 || (X86_32 && HIGHMEM64G && (X86_NUMAQ || X86_GENERICARCH || X86_SUMMIT && ACPI) && EXPERIMENTAL)
default n if X86_PC
- default y if (X86_NUMAQ || X86_SUMMIT)
+ default y if (X86_NUMAQ || X86_SUMMIT || X86_GENERICARCH)
help
Enable NUMA (Non Uniform Memory Access) support.
The kernel will try to allocate memory used by a CPU on the
@@ -960,13 +959,25 @@ config NUMA_EMU
into virtual nodes when booted with "numa=fake=N", where N is the
number of nodes. This is only useful for debugging.
+if MAXSMP
+
+config NODES_SHIFT
+ int
+ default "9"
+endif
+
+if !MAXSMP
config NODES_SHIFT
- int "Max num nodes shift(1-15)"
- range 1 15 if X86_64
+ int "Maximum NUMA Nodes (as a power of 2)"
+ range 1 9 if X86_64
default "6" if X86_64
default "4" if X86_NUMAQ
default "3"
depends on NEED_MULTIPLE_NODES
+ help
+ Specify the maximum number of NUMA Nodes available on the target
+ system. Increases memory reserved to accomodate various tables.
+endif
config HAVE_ARCH_BOOTMEM_NODE
def_bool y
@@ -1014,6 +1025,11 @@ config ARCH_MEMORY_PROBE
def_bool X86_64
depends on MEMORY_HOTPLUG
+config ILLEGAL_POINTER_VALUE
+ hex
+ default 0 if X86_32
+ default 0xffffc10000000000 if X86_64
+
source "mm/Kconfig"
config HIGHPTE
@@ -1085,6 +1101,40 @@ config MTRR
See <file:Documentation/mtrr.txt> for more information.
+config MTRR_SANITIZER
+ def_bool y
+ prompt "MTRR cleanup support"
+ depends on MTRR
+ help
+ Convert MTRR layout from continuous to discrete, so some X driver
+ could add WB entries.
+
+ Say N here if you see bootup problems (boot crash, boot hang,
+ spontaneous reboots).
+
+ Could be disabled with disable_mtrr_cleanup. Also mtrr_chunk_size
+ could be used to send largest mtrr entry size for continuous block
+ to hold holes (aka. UC entries)
+
+ If unsure, say Y.
+
+config MTRR_SANITIZER_ENABLE_DEFAULT
+ int "MTRR cleanup enable value (0-1)"
+ range 0 1
+ default "0"
+ depends on MTRR_SANITIZER
+ help
+ Enable mtrr cleanup default value
+
+config MTRR_SANITIZER_SPARE_REG_NR_DEFAULT
+ int "MTRR cleanup spare reg num (0-7)"
+ range 0 7
+ default "1"
+ depends on MTRR_SANITIZER
+ help
+ mtrr cleanup spare entries default, it can be changed via
+ mtrr_spare_reg_nr=
+
config X86_PAT
bool
prompt "x86 PAT support"
@@ -1140,13 +1190,17 @@ config SECCOMP
If unsure, say Y. Only embedded should say N here.
+config CC_STACKPROTECTOR_ALL
+ bool
+
config CC_STACKPROTECTOR
bool "Enable -fstack-protector buffer overflow detection (EXPERIMENTAL)"
- depends on X86_64 && EXPERIMENTAL && BROKEN
+ depends on X86_64
+ select CC_STACKPROTECTOR_ALL
help
- This option turns on the -fstack-protector GCC feature. This
- feature puts, at the beginning of critical functions, a canary
- value on the stack just before the return address, and validates
+ This option turns on the -fstack-protector GCC feature. This
+ feature puts, at the beginning of functions, a canary value on
+ the stack just before the return address, and validates
the value just before actually returning. Stack based buffer
overflows (that need to overwrite this return address) now also
overwrite the canary, which gets detected and the attack is then
@@ -1154,15 +1208,8 @@ config CC_STACKPROTECTOR
This feature requires gcc version 4.2 or above, or a distribution
gcc with the feature backported. Older versions are automatically
- detected and for those versions, this configuration option is ignored.
-
-config CC_STACKPROTECTOR_ALL
- bool "Use stack-protector for all functions"
- depends on CC_STACKPROTECTOR
- help
- Normally, GCC only inserts the canary value protection for
- functions that use large-ish on-stack buffers. By enabling
- this option, GCC will be asked to do this for ALL functions.
+ detected and for those versions, this configuration option is
+ ignored. (and a warning is printed during bootup)
source kernel/Kconfig.hz
@@ -1470,8 +1517,7 @@ endmenu
menu "Bus options (PCI etc.)"
config PCI
- bool "PCI support" if !X86_VISWS && !X86_VSMP
- depends on !X86_VOYAGER
+ bool "PCI support"
default y
select ARCH_SUPPORTS_MSI if (X86_LOCAL_APIC && X86_IO_APIC)
help
diff --git a/arch/x86/Kconfig.cpu b/arch/x86/Kconfig.cpu
index 2ad6301849a1..3d22bb8175b4 100644
--- a/arch/x86/Kconfig.cpu
+++ b/arch/x86/Kconfig.cpu
@@ -399,6 +399,10 @@ config X86_TSC
def_bool y
depends on ((MWINCHIP3D || MWINCHIP2 || MCRUSOE || MEFFICEON || MCYRIXIII || MK7 || MK6 || MPENTIUM4 || MPENTIUMM || MPENTIUMIII || MPENTIUMII || M686 || M586MMX || M586TSC || MK8 || MVIAC3_2 || MVIAC7 || MGEODEGX1 || MGEODE_LX || MCORE2) && !X86_NUMAQ) || X86_64
+config X86_CMPXCHG64
+ def_bool y
+ depends on X86_PAE || X86_64
+
# this should be set for all -march=.. options where the compiler
# generates cmov.
config X86_CMOV
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug
index 18363374d51a..c72a6eeadeb6 100644
--- a/arch/x86/Kconfig.debug
+++ b/arch/x86/Kconfig.debug
@@ -60,7 +60,7 @@ config DEBUG_PAGEALLOC
config DEBUG_PER_CPU_MAPS
bool "Debug access to per_cpu maps"
depends on DEBUG_KERNEL
- depends on X86_64_SMP
+ depends on X86_SMP
default n
help
Say Y to verify that the per_cpu map being accessed has
@@ -106,6 +106,7 @@ config DIRECT_GBPAGES
config DEBUG_RODATA_TEST
bool "Testcase for the DEBUG_RODATA feature"
depends on DEBUG_RODATA
+ default y
help
This option enables a testcase for the DEBUG_RODATA
feature as well as for the change_page_attr() infrastructure.
@@ -131,7 +132,7 @@ config 4KSTACKS
config X86_FIND_SMP_CONFIG
def_bool y
- depends on X86_LOCAL_APIC || X86_VOYAGER
+ depends on X86_MPPARSE || X86_VOYAGER || X86_VISWS
depends on X86_32
config X86_MPPARSE
@@ -172,6 +173,34 @@ config IOMMU_LEAK
Add a simple leak tracer to the IOMMU code. This is useful when you
are debugging a buggy device driver that leaks IOMMU mappings.
+config MMIOTRACE_HOOKS
+ bool
+
+config MMIOTRACE
+ bool "Memory mapped IO tracing"
+ depends on DEBUG_KERNEL && PCI
+ select TRACING
+ select MMIOTRACE_HOOKS
+ default y
+ help
+ Mmiotrace traces Memory Mapped I/O access and is meant for
+ debugging and reverse engineering. It is called from the ioremap
+ implementation and works via page faults. Tracing is disabled by
+ default and can be enabled at run-time.
+
+ See Documentation/tracers/mmiotrace.txt.
+ If you are not helping to develop drivers, say N.
+
+config MMIOTRACE_TEST
+ tristate "Test module for mmiotrace"
+ depends on MMIOTRACE && m
+ help
+ This is a dumb module for testing mmiotrace. It is very dangerous
+ as it will write garbage to IO memory starting at a given address.
+ However, it should be safe to use on e.g. unused portion of VRAM.
+
+ Say N, unless you absolutely know what you are doing.
+
#
# IO delay types:
#
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index 3cff3c894cf3..fc698f2cdf3c 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -73,7 +73,7 @@ else
stackp := $(CONFIG_SHELL) $(srctree)/scripts/gcc-x86_64-has-stack-protector.sh
stackp-$(CONFIG_CC_STACKPROTECTOR) := $(shell $(stackp) \
- "$(CC)" -fstack-protector )
+ "$(CC)" "-fstack-protector -DGCC_HAS_SP" )
stackp-$(CONFIG_CC_STACKPROTECTOR_ALL) += $(shell $(stackp) \
"$(CC)" -fstack-protector-all )
@@ -117,29 +117,11 @@ mcore-$(CONFIG_X86_VOYAGER) := arch/x86/mach-voyager/
mflags-$(CONFIG_X86_VISWS) := -Iinclude/asm-x86/mach-visws
mcore-$(CONFIG_X86_VISWS) := arch/x86/mach-visws/
-# NUMAQ subarch support
-mflags-$(CONFIG_X86_NUMAQ) := -Iinclude/asm-x86/mach-numaq
-mcore-$(CONFIG_X86_NUMAQ) := arch/x86/mach-default/
-
-# BIGSMP subarch support
-mflags-$(CONFIG_X86_BIGSMP) := -Iinclude/asm-x86/mach-bigsmp
-mcore-$(CONFIG_X86_BIGSMP) := arch/x86/mach-default/
-
-#Summit subarch support
-mflags-$(CONFIG_X86_SUMMIT) := -Iinclude/asm-x86/mach-summit
-mcore-$(CONFIG_X86_SUMMIT) := arch/x86/mach-default/
-
# generic subarchitecture
mflags-$(CONFIG_X86_GENERICARCH):= -Iinclude/asm-x86/mach-generic
fcore-$(CONFIG_X86_GENERICARCH) += arch/x86/mach-generic/
mcore-$(CONFIG_X86_GENERICARCH) := arch/x86/mach-default/
-
-# ES7000 subarch support
-mflags-$(CONFIG_X86_ES7000) := -Iinclude/asm-x86/mach-es7000
-fcore-$(CONFIG_X86_ES7000) := arch/x86/mach-es7000/
-mcore-$(CONFIG_X86_ES7000) := arch/x86/mach-default/
-
# RDC R-321x subarch support
mflags-$(CONFIG_X86_RDC321X) := -Iinclude/asm-x86/mach-rdc321x
mcore-$(CONFIG_X86_RDC321X) := arch/x86/mach-default/
@@ -160,6 +142,7 @@ KBUILD_AFLAGS += $(mflags-y)
head-y := arch/x86/kernel/head_$(BITS).o
head-y += arch/x86/kernel/head$(BITS).o
+head-y += arch/x86/kernel/head.o
head-y += arch/x86/kernel/init_task.o
libs-y += arch/x86/lib/
@@ -210,12 +193,12 @@ all: bzImage
# KBUILD_IMAGE specify target image being built
KBUILD_IMAGE := $(boot)/bzImage
-zImage zlilo zdisk: KBUILD_IMAGE := arch/x86/boot/zImage
+zImage zlilo zdisk: KBUILD_IMAGE := $(boot)/zImage
zImage bzImage: vmlinux
$(Q)$(MAKE) $(build)=$(boot) $(KBUILD_IMAGE)
$(Q)mkdir -p $(objtree)/arch/$(UTS_MACHINE)/boot
- $(Q)ln -fsn ../../x86/boot/bzImage $(objtree)/arch/$(UTS_MACHINE)/boot/bzImage
+ $(Q)ln -fsn ../../x86/boot/bzImage $(objtree)/arch/$(UTS_MACHINE)/boot/$@
compressed: zImage
diff --git a/arch/x86/boot/a20.c b/arch/x86/boot/a20.c
index 90943f83e84d..e01aafd03bde 100644
--- a/arch/x86/boot/a20.c
+++ b/arch/x86/boot/a20.c
@@ -115,8 +115,6 @@ static void enable_a20_fast(void)
int enable_a20(void)
{
- int loops = A20_ENABLE_LOOPS;
-
#if defined(CONFIG_X86_ELAN)
/* Elan croaks if we try to touch the KBC */
enable_a20_fast();
@@ -128,6 +126,7 @@ int enable_a20(void)
enable_a20_kbc();
return 0;
#else
+ int loops = A20_ENABLE_LOOPS;
while (loops--) {
/* First, check to see if A20 is already enabled
(legacy free, etc.) */
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index d8819efac81d..1d5dff4123e1 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -30,6 +30,7 @@
#include <asm/page.h>
#include <asm/boot.h>
#include <asm/msr.h>
+#include <asm/processor-flags.h>
#include <asm/asm-offsets.h>
.section ".text.head"
@@ -109,7 +110,7 @@ startup_32:
/* Enable PAE mode */
xorl %eax, %eax
- orl $(1 << 5), %eax
+ orl $(X86_CR4_PAE), %eax
movl %eax, %cr4
/*
@@ -170,7 +171,7 @@ startup_32:
pushl %eax
/* Enter paged protected Mode, activating Long Mode */
- movl $0x80000001, %eax /* Enable Paging and Protected mode */
+ movl $(X86_CR0_PG | X86_CR0_PE), %eax /* Enable Paging and Protected mode */
movl %eax, %cr0
/* Jump from 32bit compatibility mode into 64bit mode. */
diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c
index 90456cee47c3..8984d38688f8 100644
--- a/arch/x86/boot/compressed/misc.c
+++ b/arch/x86/boot/compressed/misc.c
@@ -30,6 +30,7 @@
#include <asm/io.h>
#include <asm/page.h>
#include <asm/boot.h>
+#include <asm/bootparam.h>
/* WARNING!!
* This code is compiled with -fPIC and it is relocated dynamically
@@ -187,13 +188,8 @@ static void gzip_release(void **);
/*
* This is set up by the setup-routine at boot-time
*/
-static unsigned char *real_mode; /* Pointer to real-mode data */
-
-#define RM_EXT_MEM_K (*(unsigned short *)(real_mode + 0x2))
-#ifndef STANDARD_MEMORY_BIOS_CALL
-#define RM_ALT_MEM_K (*(unsigned long *)(real_mode + 0x1e0))
-#endif
-#define RM_SCREEN_INFO (*(struct screen_info *)(real_mode+0))
+static struct boot_params *real_mode; /* Pointer to real-mode data */
+static int quiet;
extern unsigned char input_data[];
extern int input_len;
@@ -221,10 +217,6 @@ static char *vidmem;
static int vidport;
static int lines, cols;
-#ifdef CONFIG_X86_NUMAQ
-void *xquad_portio;
-#endif
-
#include "../../../../lib/inflate.c"
static void *malloc(int size)
@@ -276,12 +268,13 @@ static void putstr(const char *s)
char c;
#ifdef CONFIG_X86_32
- if (RM_SCREEN_INFO.orig_video_mode == 0 && lines == 0 && cols == 0)
+ if (real_mode->screen_info.orig_video_mode == 0 &&
+ lines == 0 && cols == 0)
return;
#endif
- x = RM_SCREEN_INFO.orig_x;
- y = RM_SCREEN_INFO.orig_y;
+ x = real_mode->screen_info.orig_x;
+ y = real_mode->screen_info.orig_y;
while ((c = *s++) != '\0') {
if (c == '\n') {
@@ -302,8 +295,8 @@ static void putstr(const char *s)
}
}
- RM_SCREEN_INFO.orig_x = x;
- RM_SCREEN_INFO.orig_y = y;
+ real_mode->screen_info.orig_x = x;
+ real_mode->screen_info.orig_y = y;
pos = (x + cols * y) * 2; /* Update cursor position */
outb(14, vidport);
@@ -395,7 +388,8 @@ static void parse_elf(void *output)
return;
}
- putstr("Parsing ELF... ");
+ if (!quiet)
+ putstr("Parsing ELF... ");
phdrs = malloc(sizeof(*phdrs) * ehdr.e_phnum);
if (!phdrs)
@@ -430,7 +424,10 @@ asmlinkage void decompress_kernel(void *rmode, memptr heap,
{
real_mode = rmode;
- if (RM_SCREEN_INFO.orig_video_mode == 7) {
+ if (real_mode->hdr.loadflags & QUIET_FLAG)
+ quiet = 1;
+
+ if (real_mode->screen_info.orig_video_mode == 7) {
vidmem = (char *) 0xb0000;
vidport = 0x3b4;
} else {
@@ -438,8 +435,8 @@ asmlinkage void decompress_kernel(void *rmode, memptr heap,
vidport = 0x3d4;
}
- lines = RM_SCREEN_INFO.orig_video_lines;
- cols = RM_SCREEN_INFO.orig_video_cols;
+ lines = real_mode->screen_info.orig_video_lines;
+ cols = real_mode->screen_info.orig_video_cols;
window = output; /* Output buffer (Normally at 1M) */
free_mem_ptr = heap; /* Heap */
@@ -465,9 +462,11 @@ asmlinkage void decompress_kernel(void *rmode, memptr heap,
#endif
makecrc();
- putstr("\nDecompressing Linux... ");
+ if (!quiet)
+ putstr("\nDecompressing Linux... ");
gunzip();
parse_elf(output);
- putstr("done.\nBooting the kernel.\n");
+ if (!quiet)
+ putstr("done.\nBooting the kernel.\n");
return;
}
diff --git a/arch/x86/boot/cpu.c b/arch/x86/boot/cpu.c
index 00e19edd852c..92d6fd73dc7d 100644
--- a/arch/x86/boot/cpu.c
+++ b/arch/x86/boot/cpu.c
@@ -28,6 +28,8 @@ static char *cpu_name(int level)
if (level == 64) {
return "x86-64";
} else {
+ if (level == 15)
+ level = 6;
sprintf(buf, "i%d86", level);
return buf;
}
diff --git a/arch/x86/boot/main.c b/arch/x86/boot/main.c
index 77569a4a3be1..2296164b54d2 100644
--- a/arch/x86/boot/main.c
+++ b/arch/x86/boot/main.c
@@ -165,6 +165,10 @@ void main(void)
/* Set the video mode */
set_video();
+ /* Parse command line for 'quiet' and pass it to decompressor. */
+ if (cmdline_find_option_bool("quiet"))
+ boot_params.hdr.loadflags |= QUIET_FLAG;
+
/* Do the last things and invoke protected mode */
go_to_protected_mode();
}
diff --git a/arch/x86/boot/memory.c b/arch/x86/boot/memory.c
index acad32eb4290..53165c97336b 100644
--- a/arch/x86/boot/memory.c
+++ b/arch/x86/boot/memory.c
@@ -13,6 +13,7 @@
*/
#include "boot.h"
+#include <linux/kernel.h>
#define SMAP 0x534d4150 /* ASCII "SMAP" */
@@ -53,7 +54,7 @@ static int detect_memory_e820(void)
count++;
desc++;
- } while (next && count < E820MAX);
+ } while (next && count < ARRAY_SIZE(boot_params.e820_map));
return boot_params.e820_entries = count;
}
diff --git a/arch/x86/boot/video-vga.c b/arch/x86/boot/video-vga.c
index 40ecb8d7688c..b939cb476dec 100644
--- a/arch/x86/boot/video-vga.c
+++ b/arch/x86/boot/video-vga.c
@@ -259,8 +259,7 @@ static int vga_probe(void)
return mode_count[adapter];
}
-__videocard video_vga =
-{
+__videocard video_vga = {
.card_name = "VGA",
.probe = vga_probe,
.set_mode = vga_set_mode,
diff --git a/arch/x86/configs/i386_defconfig b/arch/x86/configs/i386_defconfig
index ad7ddaaff588..9bc34e2033ec 100644
--- a/arch/x86/configs/i386_defconfig
+++ b/arch/x86/configs/i386_defconfig
@@ -1,54 +1,103 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.22-git14
-# Fri Jul 20 09:53:15 2007
+# Linux kernel version: 2.6.26-rc1
+# Sun May 4 19:59:02 2008
#
+# CONFIG_64BIT is not set
CONFIG_X86_32=y
+# CONFIG_X86_64 is not set
+CONFIG_X86=y
+CONFIG_DEFCONFIG_LIST="arch/x86/configs/i386_defconfig"
+# CONFIG_GENERIC_LOCKBREAK is not set
CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CMOS_UPDATE=y
CONFIG_CLOCKSOURCE_WATCHDOG=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
CONFIG_LOCKDEP_SUPPORT=y
CONFIG_STACKTRACE_SUPPORT=y
-CONFIG_SEMAPHORE_SLEEPERS=y
-CONFIG_X86=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_FAST_CMPXCHG_LOCAL=y
CONFIG_MMU=y
CONFIG_ZONE_DMA=y
-CONFIG_QUICKLIST=y
CONFIG_GENERIC_ISA_DMA=y
CONFIG_GENERIC_IOMAP=y
CONFIG_GENERIC_BUG=y
CONFIG_GENERIC_HWEIGHT=y
+# CONFIG_GENERIC_GPIO is not set
CONFIG_ARCH_MAY_HAVE_PC_FDC=y
-CONFIG_DMI=y
-CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+# CONFIG_RWSEM_GENERIC_SPINLOCK is not set
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+# CONFIG_GENERIC_TIME_VSYSCALL is not set
+CONFIG_ARCH_HAS_CPU_RELAX=y
+CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y
+CONFIG_HAVE_SETUP_PER_CPU_AREA=y
+# CONFIG_HAVE_CPUMASK_OF_CPU_MAP is not set
+CONFIG_ARCH_HIBERNATION_POSSIBLE=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+# CONFIG_ZONE_DMA32 is not set
+CONFIG_ARCH_POPULATES_NODE_MAP=y
+# CONFIG_AUDIT_ARCH is not set
+CONFIG_ARCH_SUPPORTS_AOUT=y
+CONFIG_ARCH_SUPPORTS_OPTIMIZED_INLINING=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_PENDING_IRQ=y
+CONFIG_X86_SMP=y
+CONFIG_X86_32_SMP=y
+CONFIG_X86_HT=y
+CONFIG_X86_BIOS_REBOOT=y
+CONFIG_X86_TRAMPOLINE=y
+CONFIG_KTIME_SCALAR=y
#
-# Code maturity level options
+# General setup
#
CONFIG_EXPERIMENTAL=y
CONFIG_LOCK_KERNEL=y
CONFIG_INIT_ENV_ARG_LIMIT=32
-
-#
-# General setup
-#
CONFIG_LOCALVERSION=""
-CONFIG_LOCALVERSION_AUTO=y
+# CONFIG_LOCALVERSION_AUTO is not set
CONFIG_SWAP=y
CONFIG_SYSVIPC=y
CONFIG_SYSVIPC_SYSCTL=y
CONFIG_POSIX_MQUEUE=y
-# CONFIG_BSD_PROCESS_ACCT is not set
-# CONFIG_TASKSTATS is not set
-# CONFIG_USER_NS is not set
-# CONFIG_AUDIT is not set
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_LOG_BUF_SHIFT=18
-# CONFIG_CPUSETS is not set
-CONFIG_SYSFS_DEPRECATED=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_AUDIT=y
+CONFIG_AUDITSYSCALL=y
+CONFIG_AUDIT_TREE=y
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=17
+CONFIG_CGROUPS=y
+# CONFIG_CGROUP_DEBUG is not set
+CONFIG_CGROUP_NS=y
+# CONFIG_CGROUP_DEVICE is not set
+CONFIG_CPUSETS=y
+CONFIG_GROUP_SCHED=y
+CONFIG_FAIR_GROUP_SCHED=y
+# CONFIG_RT_GROUP_SCHED is not set
+# CONFIG_USER_SCHED is not set
+CONFIG_CGROUP_SCHED=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+# CONFIG_CGROUP_MEM_RES_CTLR is not set
+# CONFIG_SYSFS_DEPRECATED_V2 is not set
+CONFIG_PROC_PID_CPUSET=y
CONFIG_RELAY=y
+CONFIG_NAMESPACES=y
+CONFIG_UTS_NS=y
+CONFIG_IPC_NS=y
+CONFIG_USER_NS=y
+CONFIG_PID_NS=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_INITRAMFS_SOURCE=""
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
@@ -56,13 +105,15 @@ CONFIG_SYSCTL=y
# CONFIG_EMBEDDED is not set
CONFIG_UID16=y
CONFIG_SYSCTL_SYSCALL=y
+CONFIG_SYSCTL_SYSCALL_CHECK=y
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
-# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_KALLSYMS_EXTRA_PASS=y
CONFIG_HOTPLUG=y
CONFIG_PRINTK=y
CONFIG_BUG=y
CONFIG_ELF_CORE=y
+# CONFIG_COMPAT_BRK is not set
CONFIG_BASE_FULL=y
CONFIG_FUTEX=y
CONFIG_ANON_INODES=y
@@ -76,6 +127,17 @@ CONFIG_SLUB_DEBUG=y
# CONFIG_SLAB is not set
CONFIG_SLUB=y
# CONFIG_SLOB is not set
+CONFIG_PROFILING=y
+CONFIG_MARKERS=y
+# CONFIG_OPROFILE is not set
+CONFIG_HAVE_OPROFILE=y
+CONFIG_KPROBES=y
+CONFIG_KRETPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+# CONFIG_HAVE_DMA_ATTRS is not set
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SLABINFO=y
CONFIG_RT_MUTEXES=y
# CONFIG_TINY_SHMEM is not set
CONFIG_BASE_SMALL=0
@@ -87,10 +149,10 @@ CONFIG_MODULE_FORCE_UNLOAD=y
# CONFIG_KMOD is not set
CONFIG_STOP_MACHINE=y
CONFIG_BLOCK=y
-CONFIG_LBD=y
-# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LBD is not set
+CONFIG_BLK_DEV_IO_TRACE=y
# CONFIG_LSF is not set
-# CONFIG_BLK_DEV_BSG is not set
+CONFIG_BLK_DEV_BSG=y
#
# IO Schedulers
@@ -103,7 +165,8 @@ CONFIG_IOSCHED_CFQ=y
# CONFIG_DEFAULT_DEADLINE is not set
CONFIG_DEFAULT_CFQ=y
# CONFIG_DEFAULT_NOOP is not set
-CONFIG_DEFAULT_IOSCHED="anticipatory"
+CONFIG_DEFAULT_IOSCHED="cfq"
+CONFIG_CLASSIC_RCU=y
#
# Processor type and features
@@ -111,18 +174,21 @@ CONFIG_DEFAULT_IOSCHED="anticipatory"
CONFIG_TICK_ONESHOT=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
CONFIG_SMP=y
-# CONFIG_X86_PC is not set
+CONFIG_X86_PC=y
# CONFIG_X86_ELAN is not set
# CONFIG_X86_VOYAGER is not set
# CONFIG_X86_NUMAQ is not set
# CONFIG_X86_SUMMIT is not set
# CONFIG_X86_BIGSMP is not set
# CONFIG_X86_VISWS is not set
-CONFIG_X86_GENERICARCH=y
+# CONFIG_X86_GENERICARCH is not set
# CONFIG_X86_ES7000 is not set
-# CONFIG_PARAVIRT is not set
-CONFIG_X86_CYCLONE_TIMER=y
+# CONFIG_X86_RDC321X is not set
+# CONFIG_X86_VSMP is not set
+CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y
+# CONFIG_PARAVIRT_GUEST is not set
# CONFIG_M386 is not set
# CONFIG_M486 is not set
# CONFIG_M586 is not set
@@ -130,9 +196,8 @@ CONFIG_X86_CYCLONE_TIMER=y
# CONFIG_M586MMX is not set
# CONFIG_M686 is not set
# CONFIG_MPENTIUMII is not set
-CONFIG_MPENTIUMIII=y
+# CONFIG_MPENTIUMIII is not set
# CONFIG_MPENTIUMM is not set
-# CONFIG_MCORE2 is not set
# CONFIG_MPENTIUM4 is not set
# CONFIG_MK6 is not set
# CONFIG_MK7 is not set
@@ -147,14 +212,14 @@ CONFIG_MPENTIUMIII=y
# CONFIG_MCYRIXIII is not set
# CONFIG_MVIAC3_2 is not set
# CONFIG_MVIAC7 is not set
-CONFIG_X86_GENERIC=y
+# CONFIG_MPSC is not set
+CONFIG_MCORE2=y
+# CONFIG_GENERIC_CPU is not set
+# CONFIG_X86_GENERIC is not set
+CONFIG_X86_CPU=y
CONFIG_X86_CMPXCHG=y
-CONFIG_X86_L1_CACHE_SHIFT=7
+CONFIG_X86_L1_CACHE_SHIFT=6
CONFIG_X86_XADD=y
-CONFIG_RWSEM_XCHGADD_ALGORITHM=y
-# CONFIG_ARCH_HAS_ILOG2_U32 is not set
-# CONFIG_ARCH_HAS_ILOG2_U64 is not set
-CONFIG_GENERIC_CALIBRATE_DELAY=y
CONFIG_X86_WP_WORKS_OK=y
CONFIG_X86_INVLPG=y
CONFIG_X86_BSWAP=y
@@ -162,106 +227,120 @@ CONFIG_X86_POPAD_OK=y
CONFIG_X86_GOOD_APIC=y
CONFIG_X86_INTEL_USERCOPY=y
CONFIG_X86_USE_PPRO_CHECKSUM=y
+CONFIG_X86_P6_NOP=y
CONFIG_X86_TSC=y
-CONFIG_X86_CMOV=y
-CONFIG_X86_MINIMUM_CPU_FAMILY=4
+CONFIG_X86_MINIMUM_CPU_FAMILY=6
+CONFIG_X86_DEBUGCTLMSR=y
CONFIG_HPET_TIMER=y
CONFIG_HPET_EMULATE_RTC=y
-CONFIG_NR_CPUS=32
-CONFIG_SCHED_SMT=y
+CONFIG_DMI=y
+# CONFIG_IOMMU_HELPER is not set
+CONFIG_NR_CPUS=4
+# CONFIG_SCHED_SMT is not set
CONFIG_SCHED_MC=y
# CONFIG_PREEMPT_NONE is not set
CONFIG_PREEMPT_VOLUNTARY=y
# CONFIG_PREEMPT is not set
-CONFIG_PREEMPT_BKL=y
CONFIG_X86_LOCAL_APIC=y
CONFIG_X86_IO_APIC=y
-CONFIG_X86_MCE=y
-CONFIG_X86_MCE_NONFATAL=y
-CONFIG_X86_MCE_P4THERMAL=y
+# CONFIG_X86_MCE is not set
CONFIG_VM86=y
# CONFIG_TOSHIBA is not set
# CONFIG_I8K is not set
# CONFIG_X86_REBOOTFIXUPS is not set
-CONFIG_MICROCODE=y
-CONFIG_MICROCODE_OLD_INTERFACE=y
+# CONFIG_MICROCODE is not set
CONFIG_X86_MSR=y
CONFIG_X86_CPUID=y
-
-#
-# Firmware Drivers
-#
-# CONFIG_EDD is not set
-# CONFIG_DELL_RBU is not set
-# CONFIG_DCDBAS is not set
-CONFIG_DMIID=y
# CONFIG_NOHIGHMEM is not set
CONFIG_HIGHMEM4G=y
# CONFIG_HIGHMEM64G is not set
CONFIG_PAGE_OFFSET=0xC0000000
CONFIG_HIGHMEM=y
-CONFIG_ARCH_POPULATES_NODE_MAP=y
+CONFIG_NEED_NODE_MEMMAP_SIZE=y
+CONFIG_ARCH_FLATMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ARCH_SELECT_MEMORY_MODEL=y
CONFIG_SELECT_MEMORY_MODEL=y
-CONFIG_FLATMEM_MANUAL=y
+# CONFIG_FLATMEM_MANUAL is not set
# CONFIG_DISCONTIGMEM_MANUAL is not set
-# CONFIG_SPARSEMEM_MANUAL is not set
-CONFIG_FLATMEM=y
-CONFIG_FLAT_NODE_MEM_MAP=y
-# CONFIG_SPARSEMEM_STATIC is not set
+CONFIG_SPARSEMEM_MANUAL=y
+CONFIG_SPARSEMEM=y
+CONFIG_HAVE_MEMORY_PRESENT=y
+CONFIG_SPARSEMEM_STATIC=y
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+
+#
+# Memory hotplug is currently incompatible with Software Suspend
+#
+CONFIG_PAGEFLAGS_EXTENDED=y
CONFIG_SPLIT_PTLOCK_CPUS=4
CONFIG_RESOURCES_64BIT=y
CONFIG_ZONE_DMA_FLAG=1
CONFIG_BOUNCE=y
-CONFIG_NR_QUICK=1
CONFIG_VIRT_TO_BUS=y
# CONFIG_HIGHPTE is not set
# CONFIG_MATH_EMULATION is not set
CONFIG_MTRR=y
-# CONFIG_EFI is not set
+# CONFIG_X86_PAT is not set
+CONFIG_EFI=y
# CONFIG_IRQBALANCE is not set
CONFIG_SECCOMP=y
# CONFIG_HZ_100 is not set
-CONFIG_HZ_250=y
+# CONFIG_HZ_250 is not set
# CONFIG_HZ_300 is not set
-# CONFIG_HZ_1000 is not set
-CONFIG_HZ=250
-# CONFIG_KEXEC is not set
-# CONFIG_CRASH_DUMP is not set
-CONFIG_PHYSICAL_START=0x100000
-# CONFIG_RELOCATABLE is not set
-CONFIG_PHYSICAL_ALIGN=0x100000
-# CONFIG_HOTPLUG_CPU is not set
-CONFIG_COMPAT_VDSO=y
+CONFIG_HZ_1000=y
+CONFIG_HZ=1000
+CONFIG_SCHED_HRTICK=y
+CONFIG_KEXEC=y
+CONFIG_CRASH_DUMP=y
+CONFIG_PHYSICAL_START=0x1000000
+CONFIG_RELOCATABLE=y
+CONFIG_PHYSICAL_ALIGN=0x200000
+CONFIG_HOTPLUG_CPU=y
+# CONFIG_COMPAT_VDSO is not set
CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
#
-# Power management options (ACPI, APM)
+# Power management options
#
CONFIG_PM=y
-CONFIG_PM_LEGACY=y
-# CONFIG_PM_DEBUG is not set
-
-#
-# ACPI (Advanced Configuration and Power Interface) Support
-#
+CONFIG_PM_DEBUG=y
+# CONFIG_PM_VERBOSE is not set
+CONFIG_CAN_PM_TRACE=y
+CONFIG_PM_TRACE=y
+CONFIG_PM_TRACE_RTC=y
+CONFIG_PM_SLEEP_SMP=y
+CONFIG_PM_SLEEP=y
+CONFIG_SUSPEND=y
+CONFIG_SUSPEND_FREEZER=y
+CONFIG_HIBERNATION=y
+CONFIG_PM_STD_PARTITION=""
CONFIG_ACPI=y
+CONFIG_ACPI_SLEEP=y
CONFIG_ACPI_PROCFS=y
+CONFIG_ACPI_PROCFS_POWER=y
+CONFIG_ACPI_SYSFS_POWER=y
+CONFIG_ACPI_PROC_EVENT=y
CONFIG_ACPI_AC=y
CONFIG_ACPI_BATTERY=y
CONFIG_ACPI_BUTTON=y
CONFIG_ACPI_FAN=y
-# CONFIG_ACPI_DOCK is not set
+CONFIG_ACPI_DOCK=y
+# CONFIG_ACPI_BAY is not set
CONFIG_ACPI_PROCESSOR=y
+CONFIG_ACPI_HOTPLUG_CPU=y
CONFIG_ACPI_THERMAL=y
+# CONFIG_ACPI_WMI is not set
# CONFIG_ACPI_ASUS is not set
# CONFIG_ACPI_TOSHIBA is not set
-CONFIG_ACPI_BLACKLIST_YEAR=2001
-CONFIG_ACPI_DEBUG=y
+# CONFIG_ACPI_CUSTOM_DSDT is not set
+CONFIG_ACPI_BLACKLIST_YEAR=0
+# CONFIG_ACPI_DEBUG is not set
CONFIG_ACPI_EC=y
CONFIG_ACPI_POWER=y
CONFIG_ACPI_SYSTEM=y
CONFIG_X86_PM_TIMER=y
-# CONFIG_ACPI_CONTAINER is not set
+CONFIG_ACPI_CONTAINER=y
# CONFIG_ACPI_SBS is not set
# CONFIG_APM is not set
@@ -271,15 +350,17 @@ CONFIG_X86_PM_TIMER=y
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_TABLE=y
CONFIG_CPU_FREQ_DEBUG=y
-CONFIG_CPU_FREQ_STAT=y
-# CONFIG_CPU_FREQ_STAT_DETAILS is not set
-CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
-# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
+# CONFIG_CPU_FREQ_STAT is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
-CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
#
# CPUFreq processor drivers
@@ -287,8 +368,7 @@ CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
CONFIG_X86_ACPI_CPUFREQ=y
# CONFIG_X86_POWERNOW_K6 is not set
# CONFIG_X86_POWERNOW_K7 is not set
-CONFIG_X86_POWERNOW_K8=y
-CONFIG_X86_POWERNOW_K8_ACPI=y
+# CONFIG_X86_POWERNOW_K8 is not set
# CONFIG_X86_GX_SUSPMOD is not set
# CONFIG_X86_SPEEDSTEP_CENTRINO is not set
# CONFIG_X86_SPEEDSTEP_ICH is not set
@@ -302,43 +382,72 @@ CONFIG_X86_POWERNOW_K8_ACPI=y
#
# shared options
#
-CONFIG_X86_ACPI_CPUFREQ_PROC_INTF=y
+# CONFIG_X86_ACPI_CPUFREQ_PROC_INTF is not set
# CONFIG_X86_SPEEDSTEP_LIB is not set
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_IDLE_GOV_LADDER=y
+CONFIG_CPU_IDLE_GOV_MENU=y
#
-# Bus options (PCI, PCMCIA, EISA, MCA, ISA)
+# Bus options (PCI etc.)
#
CONFIG_PCI=y
# CONFIG_PCI_GOBIOS is not set
# CONFIG_PCI_GOMMCONFIG is not set
# CONFIG_PCI_GODIRECT is not set
CONFIG_PCI_GOANY=y
+# CONFIG_PCI_GOOLPC is not set
CONFIG_PCI_BIOS=y
CONFIG_PCI_DIRECT=y
CONFIG_PCI_MMCONFIG=y
-# CONFIG_PCIEPORTBUS is not set
+CONFIG_PCI_DOMAINS=y
+CONFIG_PCIEPORTBUS=y
+# CONFIG_HOTPLUG_PCI_PCIE is not set
+CONFIG_PCIEAER=y
+# CONFIG_PCIEASPM is not set
CONFIG_ARCH_SUPPORTS_MSI=y
CONFIG_PCI_MSI=y
+# CONFIG_PCI_LEGACY is not set
# CONFIG_PCI_DEBUG is not set
-# CONFIG_HT_IRQ is not set
+CONFIG_HT_IRQ=y
CONFIG_ISA_DMA_API=y
# CONFIG_ISA is not set
# CONFIG_MCA is not set
# CONFIG_SCx200 is not set
+# CONFIG_OLPC is not set
CONFIG_K8_NB=y
-
-#
-# PCCARD (PCMCIA/CardBus) support
-#
-# CONFIG_PCCARD is not set
-# CONFIG_HOTPLUG_PCI is not set
-
-#
-# Executable file formats
+CONFIG_PCCARD=y
+# CONFIG_PCMCIA_DEBUG is not set
+CONFIG_PCMCIA=y
+CONFIG_PCMCIA_LOAD_CIS=y
+CONFIG_PCMCIA_IOCTL=y
+CONFIG_CARDBUS=y
+
+#
+# PC-card bridges
+#
+CONFIG_YENTA=y
+CONFIG_YENTA_O2=y
+CONFIG_YENTA_RICOH=y
+CONFIG_YENTA_TI=y
+CONFIG_YENTA_ENE_TUNE=y
+CONFIG_YENTA_TOSHIBA=y
+# CONFIG_PD6729 is not set
+# CONFIG_I82092 is not set
+CONFIG_PCCARD_NONSTATIC=y
+CONFIG_HOTPLUG_PCI=y
+# CONFIG_HOTPLUG_PCI_FAKE is not set
+# CONFIG_HOTPLUG_PCI_IBM is not set
+# CONFIG_HOTPLUG_PCI_ACPI is not set
+# CONFIG_HOTPLUG_PCI_CPCI is not set
+# CONFIG_HOTPLUG_PCI_SHPC is not set
+
+#
+# Executable file formats / Emulations
#
CONFIG_BINFMT_ELF=y
# CONFIG_BINFMT_AOUT is not set
-# CONFIG_BINFMT_MISC is not set
+CONFIG_BINFMT_MISC=y
#
# Networking
@@ -349,59 +458,142 @@ CONFIG_NET=y
# Networking options
#
CONFIG_PACKET=y
-# CONFIG_PACKET_MMAP is not set
+CONFIG_PACKET_MMAP=y
CONFIG_UNIX=y
CONFIG_XFRM=y
-# CONFIG_XFRM_USER is not set
+CONFIG_XFRM_USER=y
# CONFIG_XFRM_SUB_POLICY is not set
# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
# CONFIG_NET_KEY is not set
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
-# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_ASK_IP_FIB_HASH=y
+# CONFIG_IP_FIB_TRIE is not set
CONFIG_IP_FIB_HASH=y
-CONFIG_IP_PNP=y
-CONFIG_IP_PNP_DHCP=y
-# CONFIG_IP_PNP_BOOTP is not set
-# CONFIG_IP_PNP_RARP is not set
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+# CONFIG_IP_PNP is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
-# CONFIG_IP_MROUTE is not set
+CONFIG_IP_MROUTE=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
# CONFIG_ARPD is not set
-# CONFIG_SYN_COOKIES is not set
+CONFIG_SYN_COOKIES=y
# CONFIG_INET_AH is not set
# CONFIG_INET_ESP is not set
# CONFIG_INET_IPCOMP is not set
# CONFIG_INET_XFRM_TUNNEL is not set
CONFIG_INET_TUNNEL=y
-CONFIG_INET_XFRM_MODE_TRANSPORT=y
-CONFIG_INET_XFRM_MODE_TUNNEL=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
# CONFIG_INET_XFRM_MODE_BEET is not set
-CONFIG_INET_DIAG=y
-CONFIG_INET_TCP_DIAG=y
-# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_INET_LRO=y
+# CONFIG_INET_DIAG is not set
+CONFIG_TCP_CONG_ADVANCED=y
+# CONFIG_TCP_CONG_BIC is not set
CONFIG_TCP_CONG_CUBIC=y
+# CONFIG_TCP_CONG_WESTWOOD is not set
+# CONFIG_TCP_CONG_HTCP is not set
+# CONFIG_TCP_CONG_HSTCP is not set
+# CONFIG_TCP_CONG_HYBLA is not set
+# CONFIG_TCP_CONG_VEGAS is not set
+# CONFIG_TCP_CONG_SCALABLE is not set
+# CONFIG_TCP_CONG_LP is not set
+# CONFIG_TCP_CONG_VENO is not set
+# CONFIG_TCP_CONG_YEAH is not set
+# CONFIG_TCP_CONG_ILLINOIS is not set
+# CONFIG_DEFAULT_BIC is not set
+CONFIG_DEFAULT_CUBIC=y
+# CONFIG_DEFAULT_HTCP is not set
+# CONFIG_DEFAULT_VEGAS is not set
+# CONFIG_DEFAULT_WESTWOOD is not set
+# CONFIG_DEFAULT_RENO is not set
CONFIG_DEFAULT_TCP_CONG="cubic"
-# CONFIG_TCP_MD5SIG is not set
+CONFIG_TCP_MD5SIG=y
+# CONFIG_IP_VS is not set
CONFIG_IPV6=y
# CONFIG_IPV6_PRIVACY is not set
# CONFIG_IPV6_ROUTER_PREF is not set
# CONFIG_IPV6_OPTIMISTIC_DAD is not set
-# CONFIG_INET6_AH is not set
-# CONFIG_INET6_ESP is not set
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
# CONFIG_INET6_IPCOMP is not set
# CONFIG_IPV6_MIP6 is not set
# CONFIG_INET6_XFRM_TUNNEL is not set
# CONFIG_INET6_TUNNEL is not set
CONFIG_INET6_XFRM_MODE_TRANSPORT=y
CONFIG_INET6_XFRM_MODE_TUNNEL=y
-# CONFIG_INET6_XFRM_MODE_BEET is not set
+CONFIG_INET6_XFRM_MODE_BEET=y
# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
CONFIG_IPV6_SIT=y
+CONFIG_IPV6_NDISC_NODETYPE=y
# CONFIG_IPV6_TUNNEL is not set
# CONFIG_IPV6_MULTIPLE_TABLES is not set
-# CONFIG_NETWORK_SECMARK is not set
-# CONFIG_NETFILTER is not set
+# CONFIG_IPV6_MROUTE is not set
+CONFIG_NETLABEL=y
+CONFIG_NETWORK_SECMARK=y
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+# CONFIG_NETFILTER_ADVANCED is not set
+
+#
+# Core Netfilter Configuration
+#
+CONFIG_NETFILTER_NETLINK=y
+CONFIG_NETFILTER_NETLINK_LOG=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_SECMARK=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_SIP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_XTABLES=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_SECMARK=y
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_NF_CONNTRACK_PROC_COMPAT=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_TARGET_LOG=y
+CONFIG_IP_NF_TARGET_ULOG=y
+CONFIG_NF_NAT=y
+CONFIG_NF_NAT_NEEDED=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_NF_NAT_FTP=y
+CONFIG_NF_NAT_IRC=y
+# CONFIG_NF_NAT_TFTP is not set
+# CONFIG_NF_NAT_AMANDA is not set
+# CONFIG_NF_NAT_PPTP is not set
+# CONFIG_NF_NAT_H323 is not set
+CONFIG_NF_NAT_SIP=y
+CONFIG_IP_NF_MANGLE=y
+
+#
+# IPv6: Netfilter Configuration
+#
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_MATCH_IPV6HEADER=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_LOG=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_MANGLE=y
# CONFIG_IP_DCCP is not set
# CONFIG_IP_SCTP is not set
# CONFIG_TIPC is not set
@@ -409,6 +601,7 @@ CONFIG_IPV6_SIT=y
# CONFIG_BRIDGE is not set
# CONFIG_VLAN_8021Q is not set
# CONFIG_DECNET is not set
+CONFIG_LLC=y
# CONFIG_LLC2 is not set
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
@@ -416,28 +609,99 @@ CONFIG_IPV6_SIT=y
# CONFIG_LAPB is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-
-#
-# QoS and/or fair queueing
-#
-# CONFIG_NET_SCHED is not set
+CONFIG_NET_SCHED=y
+
+#
+# Queueing/Scheduling
+#
+# CONFIG_NET_SCH_CBQ is not set
+# CONFIG_NET_SCH_HTB is not set
+# CONFIG_NET_SCH_HFSC is not set
+# CONFIG_NET_SCH_PRIO is not set
+# CONFIG_NET_SCH_RR is not set
+# CONFIG_NET_SCH_RED is not set
+# CONFIG_NET_SCH_SFQ is not set
+# CONFIG_NET_SCH_TEQL is not set
+# CONFIG_NET_SCH_TBF is not set
+# CONFIG_NET_SCH_GRED is not set
+# CONFIG_NET_SCH_DSMARK is not set
+# CONFIG_NET_SCH_NETEM is not set
+# CONFIG_NET_SCH_INGRESS is not set
+
+#
+# Classification
+#
+CONFIG_NET_CLS=y
+# CONFIG_NET_CLS_BASIC is not set
+# CONFIG_NET_CLS_TCINDEX is not set
+# CONFIG_NET_CLS_ROUTE4 is not set
+# CONFIG_NET_CLS_FW is not set
+# CONFIG_NET_CLS_U32 is not set
+# CONFIG_NET_CLS_RSVP is not set
+# CONFIG_NET_CLS_RSVP6 is not set
+# CONFIG_NET_CLS_FLOW is not set
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_STACK=32
+# CONFIG_NET_EMATCH_CMP is not set
+# CONFIG_NET_EMATCH_NBYTE is not set
+# CONFIG_NET_EMATCH_U32 is not set
+# CONFIG_NET_EMATCH_META is not set
+# CONFIG_NET_EMATCH_TEXT is not set
+CONFIG_NET_CLS_ACT=y
+# CONFIG_NET_ACT_POLICE is not set
+# CONFIG_NET_ACT_GACT is not set
+# CONFIG_NET_ACT_MIRRED is not set
+# CONFIG_NET_ACT_IPT is not set
+# CONFIG_NET_ACT_NAT is not set
+# CONFIG_NET_ACT_PEDIT is not set
+# CONFIG_NET_ACT_SIMP is not set
+CONFIG_NET_SCH_FIFO=y
#
# Network testing
#
# CONFIG_NET_PKTGEN is not set
# CONFIG_NET_TCPPROBE is not set
-# CONFIG_HAMRADIO is not set
+CONFIG_HAMRADIO=y
+
+#
+# Packet Radio protocols
+#
+# CONFIG_AX25 is not set
+# CONFIG_CAN is not set
# CONFIG_IRDA is not set
# CONFIG_BT is not set
# CONFIG_AF_RXRPC is not set
+CONFIG_FIB_RULES=y
#
# Wireless
#
-# CONFIG_CFG80211 is not set
-# CONFIG_WIRELESS_EXT is not set
-# CONFIG_MAC80211 is not set
+CONFIG_CFG80211=y
+CONFIG_NL80211=y
+CONFIG_WIRELESS_EXT=y
+CONFIG_MAC80211=y
+
+#
+# Rate control algorithm selection
+#
+CONFIG_MAC80211_RC_DEFAULT_PID=y
+# CONFIG_MAC80211_RC_DEFAULT_NONE is not set
+
+#
+# Selecting 'y' for an algorithm will
+#
+
+#
+# build the algorithm into mac80211.
+#
+CONFIG_MAC80211_RC_DEFAULT="pid"
+CONFIG_MAC80211_RC_PID=y
+# CONFIG_MAC80211_MESH is not set
+CONFIG_MAC80211_LEDS=y
+# CONFIG_MAC80211_DEBUGFS is not set
+# CONFIG_MAC80211_DEBUG_PACKET_ALIGNMENT is not set
+# CONFIG_MAC80211_DEBUG is not set
# CONFIG_IEEE80211 is not set
# CONFIG_RFKILL is not set
# CONFIG_NET_9P is not set
@@ -449,13 +713,15 @@ CONFIG_IPV6_SIT=y
#
# Generic Driver Options
#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_STANDALONE=y
CONFIG_PREVENT_FIRMWARE_BUILD=y
CONFIG_FW_LOADER=y
# CONFIG_DEBUG_DRIVER is not set
-# CONFIG_DEBUG_DEVRES is not set
+CONFIG_DEBUG_DEVRES=y
# CONFIG_SYS_HYPERVISOR is not set
-# CONFIG_CONNECTOR is not set
+CONFIG_CONNECTOR=y
+CONFIG_PROC_EVENTS=y
# CONFIG_MTD is not set
# CONFIG_PARPORT is not set
CONFIG_PNP=y
@@ -466,7 +732,7 @@ CONFIG_PNP=y
#
CONFIG_PNPACPI=y
CONFIG_BLK_DEV=y
-CONFIG_BLK_DEV_FD=y
+# CONFIG_BLK_DEV_FD is not set
# CONFIG_BLK_CPQ_DA is not set
# CONFIG_BLK_CPQ_CISS_DA is not set
# CONFIG_BLK_DEV_DAC960 is not set
@@ -479,8 +745,8 @@ CONFIG_BLK_DEV_LOOP=y
# CONFIG_BLK_DEV_UB is not set
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_COUNT=16
-CONFIG_BLK_DEV_RAM_SIZE=4096
-CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
+CONFIG_BLK_DEV_RAM_SIZE=16384
+# CONFIG_BLK_DEV_XIP is not set
# CONFIG_CDROM_PKTCDVD is not set
# CONFIG_ATA_OVER_ETH is not set
CONFIG_MISC_DEVICES=y
@@ -489,73 +755,17 @@ CONFIG_MISC_DEVICES=y
# CONFIG_EEPROM_93CX6 is not set
# CONFIG_SGI_IOC4 is not set
# CONFIG_TIFM_CORE is not set
+# CONFIG_ACER_WMI is not set
+# CONFIG_ASUS_LAPTOP is not set
+# CONFIG_FUJITSU_LAPTOP is not set
+# CONFIG_TC1100_WMI is not set
+# CONFIG_MSI_LAPTOP is not set
# CONFIG_SONY_LAPTOP is not set
# CONFIG_THINKPAD_ACPI is not set
-CONFIG_IDE=y
-CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
-# CONFIG_BLK_DEV_IDE_SATA is not set
-# CONFIG_BLK_DEV_HD_IDE is not set
-CONFIG_BLK_DEV_IDEDISK=y
-CONFIG_IDEDISK_MULTI_MODE=y
-CONFIG_BLK_DEV_IDECD=y
-# CONFIG_BLK_DEV_IDETAPE is not set
-# CONFIG_BLK_DEV_IDEFLOPPY is not set
-# CONFIG_BLK_DEV_IDESCSI is not set
-CONFIG_BLK_DEV_IDEACPI=y
-# CONFIG_IDE_TASK_IOCTL is not set
-CONFIG_IDE_PROC_FS=y
-
-#
-# IDE chipset support/bugfixes
-#
-CONFIG_IDE_GENERIC=y
-# CONFIG_BLK_DEV_CMD640 is not set
-# CONFIG_BLK_DEV_IDEPNP is not set
-CONFIG_BLK_DEV_IDEPCI=y
-# CONFIG_IDEPCI_SHARE_IRQ is not set
-CONFIG_IDEPCI_PCIBUS_ORDER=y
-# CONFIG_BLK_DEV_OFFBOARD is not set
-# CONFIG_BLK_DEV_GENERIC is not set
-# CONFIG_BLK_DEV_OPTI621 is not set
-# CONFIG_BLK_DEV_RZ1000 is not set
-CONFIG_BLK_DEV_IDEDMA_PCI=y
-# CONFIG_BLK_DEV_IDEDMA_FORCED is not set
-# CONFIG_IDEDMA_ONLYDISK is not set
-# CONFIG_BLK_DEV_AEC62XX is not set
-# CONFIG_BLK_DEV_ALI15X3 is not set
-CONFIG_BLK_DEV_AMD74XX=y
-# CONFIG_BLK_DEV_ATIIXP is not set
-# CONFIG_BLK_DEV_CMD64X is not set
-# CONFIG_BLK_DEV_TRIFLEX is not set
-# CONFIG_BLK_DEV_CY82C693 is not set
-# CONFIG_BLK_DEV_CS5520 is not set
-# CONFIG_BLK_DEV_CS5530 is not set
-# CONFIG_BLK_DEV_CS5535 is not set
-# CONFIG_BLK_DEV_HPT34X is not set
-# CONFIG_BLK_DEV_HPT366 is not set
-# CONFIG_BLK_DEV_JMICRON is not set
-# CONFIG_BLK_DEV_SC1200 is not set
-CONFIG_BLK_DEV_PIIX=y
-# CONFIG_BLK_DEV_IT8213 is not set
-# CONFIG_BLK_DEV_IT821X is not set
-# CONFIG_BLK_DEV_NS87415 is not set
-# CONFIG_BLK_DEV_PDC202XX_OLD is not set
-# CONFIG_BLK_DEV_PDC202XX_NEW is not set
-# CONFIG_BLK_DEV_SVWKS is not set
-# CONFIG_BLK_DEV_SIIMAGE is not set
-# CONFIG_BLK_DEV_SIS5513 is not set
-# CONFIG_BLK_DEV_SLC90E66 is not set
-# CONFIG_BLK_DEV_TRM290 is not set
-# CONFIG_BLK_DEV_VIA82CXXX is not set
-# CONFIG_BLK_DEV_TC86C001 is not set
-# CONFIG_IDE_ARM is not set
-CONFIG_BLK_DEV_IDEDMA=y
-# CONFIG_IDEDMA_IVB is not set
-# CONFIG_BLK_DEV_HD is not set
+# CONFIG_INTEL_MENLOW is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
#
# SCSI device support
@@ -564,8 +774,8 @@ CONFIG_BLK_DEV_IDEDMA=y
CONFIG_SCSI=y
CONFIG_SCSI_DMA=y
# CONFIG_SCSI_TGT is not set
-CONFIG_SCSI_NETLINK=y
-# CONFIG_SCSI_PROC_FS is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
#
# SCSI support type (disk, tape, CD-ROM)
@@ -574,7 +784,7 @@ CONFIG_BLK_DEV_SD=y
# CONFIG_CHR_DEV_ST is not set
# CONFIG_CHR_DEV_OSST is not set
CONFIG_BLK_DEV_SR=y
-# CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_BLK_DEV_SR_VENDOR=y
CONFIG_CHR_DEV_SG=y
# CONFIG_CHR_DEV_SCH is not set
@@ -582,7 +792,7 @@ CONFIG_CHR_DEV_SG=y
# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
#
# CONFIG_SCSI_MULTI_LUN is not set
-# CONFIG_SCSI_CONSTANTS is not set
+CONFIG_SCSI_CONSTANTS=y
# CONFIG_SCSI_LOGGING is not set
# CONFIG_SCSI_SCAN_ASYNC is not set
CONFIG_SCSI_WAIT_SCAN=m
@@ -591,81 +801,37 @@ CONFIG_SCSI_WAIT_SCAN=m
# SCSI Transports
#
CONFIG_SCSI_SPI_ATTRS=y
-CONFIG_SCSI_FC_ATTRS=y
+# CONFIG_SCSI_FC_ATTRS is not set
# CONFIG_SCSI_ISCSI_ATTRS is not set
# CONFIG_SCSI_SAS_ATTRS is not set
# CONFIG_SCSI_SAS_LIBSAS is not set
-
-#
-# SCSI low-level drivers
-#
-# CONFIG_ISCSI_TCP is not set
-CONFIG_BLK_DEV_3W_XXXX_RAID=y
-# CONFIG_SCSI_3W_9XXX is not set
-# CONFIG_SCSI_ACARD is not set
-# CONFIG_SCSI_AACRAID is not set
-CONFIG_SCSI_AIC7XXX=y
-CONFIG_AIC7XXX_CMDS_PER_DEVICE=32
-CONFIG_AIC7XXX_RESET_DELAY_MS=5000
-CONFIG_AIC7XXX_DEBUG_ENABLE=y
-CONFIG_AIC7XXX_DEBUG_MASK=0
-CONFIG_AIC7XXX_REG_PRETTY_PRINT=y
-# CONFIG_SCSI_AIC7XXX_OLD is not set
-CONFIG_SCSI_AIC79XX=y
-CONFIG_AIC79XX_CMDS_PER_DEVICE=32
-CONFIG_AIC79XX_RESET_DELAY_MS=4000
-# CONFIG_AIC79XX_DEBUG_ENABLE is not set
-CONFIG_AIC79XX_DEBUG_MASK=0
-# CONFIG_AIC79XX_REG_PRETTY_PRINT is not set
-# CONFIG_SCSI_AIC94XX is not set
-# CONFIG_SCSI_DPT_I2O is not set
-# CONFIG_SCSI_ADVANSYS is not set
-# CONFIG_SCSI_ARCMSR is not set
-# CONFIG_MEGARAID_NEWGEN is not set
-# CONFIG_MEGARAID_LEGACY is not set
-# CONFIG_MEGARAID_SAS is not set
-# CONFIG_SCSI_HPTIOP is not set
-# CONFIG_SCSI_BUSLOGIC is not set
-# CONFIG_SCSI_DMX3191D is not set
-# CONFIG_SCSI_EATA is not set
-# CONFIG_SCSI_FUTURE_DOMAIN is not set
-# CONFIG_SCSI_GDTH is not set
-# CONFIG_SCSI_IPS is not set
-# CONFIG_SCSI_INITIO is not set
-# CONFIG_SCSI_INIA100 is not set
-# CONFIG_SCSI_STEX is not set
-# CONFIG_SCSI_SYM53C8XX_2 is not set
-# CONFIG_SCSI_IPR is not set
-# CONFIG_SCSI_QLOGIC_1280 is not set
-# CONFIG_SCSI_QLA_FC is not set
-# CONFIG_SCSI_QLA_ISCSI is not set
-# CONFIG_SCSI_LPFC is not set
-# CONFIG_SCSI_DC395x is not set
-# CONFIG_SCSI_DC390T is not set
-# CONFIG_SCSI_NSP32 is not set
-# CONFIG_SCSI_DEBUG is not set
-# CONFIG_SCSI_SRP is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+# CONFIG_SCSI_LOWLEVEL is not set
+# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set
CONFIG_ATA=y
# CONFIG_ATA_NONSTANDARD is not set
CONFIG_ATA_ACPI=y
+CONFIG_SATA_PMP=y
CONFIG_SATA_AHCI=y
-CONFIG_SATA_SVW=y
+# CONFIG_SATA_SIL24 is not set
+CONFIG_ATA_SFF=y
+# CONFIG_SATA_SVW is not set
CONFIG_ATA_PIIX=y
# CONFIG_SATA_MV is not set
-CONFIG_SATA_NV=y
+# CONFIG_SATA_NV is not set
# CONFIG_PDC_ADMA is not set
# CONFIG_SATA_QSTOR is not set
# CONFIG_SATA_PROMISE is not set
# CONFIG_SATA_SX4 is not set
-CONFIG_SATA_SIL=y
-# CONFIG_SATA_SIL24 is not set
+# CONFIG_SATA_SIL is not set
# CONFIG_SATA_SIS is not set
# CONFIG_SATA_ULI is not set
-CONFIG_SATA_VIA=y
+# CONFIG_SATA_VIA is not set
# CONFIG_SATA_VITESSE is not set
# CONFIG_SATA_INIC162X is not set
+# CONFIG_PATA_ACPI is not set
# CONFIG_PATA_ALI is not set
-# CONFIG_PATA_AMD is not set
+CONFIG_PATA_AMD=y
# CONFIG_PATA_ARTOP is not set
# CONFIG_PATA_ATIIXP is not set
# CONFIG_PATA_CMD640_PCI is not set
@@ -673,6 +839,7 @@ CONFIG_SATA_VIA=y
# CONFIG_PATA_CS5520 is not set
# CONFIG_PATA_CS5530 is not set
# CONFIG_PATA_CS5535 is not set
+# CONFIG_PATA_CS5536 is not set
# CONFIG_PATA_CYPRESS is not set
# CONFIG_PATA_EFAR is not set
# CONFIG_ATA_GENERIC is not set
@@ -686,11 +853,14 @@ CONFIG_SATA_VIA=y
# CONFIG_PATA_TRIFLEX is not set
# CONFIG_PATA_MARVELL is not set
# CONFIG_PATA_MPIIX is not set
-# CONFIG_PATA_OLDPIIX is not set
+CONFIG_PATA_OLDPIIX=y
# CONFIG_PATA_NETCELL is not set
+# CONFIG_PATA_NINJA32 is not set
# CONFIG_PATA_NS87410 is not set
+# CONFIG_PATA_NS87415 is not set
# CONFIG_PATA_OPTI is not set
# CONFIG_PATA_OPTIDMA is not set
+# CONFIG_PATA_PCMCIA is not set
# CONFIG_PATA_PDC_OLD is not set
# CONFIG_PATA_RADISYS is not set
# CONFIG_PATA_RZ1000 is not set
@@ -702,65 +872,42 @@ CONFIG_SATA_VIA=y
# CONFIG_PATA_VIA is not set
# CONFIG_PATA_WINBOND is not set
CONFIG_MD=y
-# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_MD=y
+# CONFIG_MD_LINEAR is not set
+# CONFIG_MD_RAID0 is not set
+# CONFIG_MD_RAID1 is not set
+# CONFIG_MD_RAID10 is not set
+# CONFIG_MD_RAID456 is not set
+# CONFIG_MD_MULTIPATH is not set
+# CONFIG_MD_FAULTY is not set
CONFIG_BLK_DEV_DM=y
# CONFIG_DM_DEBUG is not set
# CONFIG_DM_CRYPT is not set
# CONFIG_DM_SNAPSHOT is not set
-# CONFIG_DM_MIRROR is not set
-# CONFIG_DM_ZERO is not set
+CONFIG_DM_MIRROR=y
+CONFIG_DM_ZERO=y
# CONFIG_DM_MULTIPATH is not set
# CONFIG_DM_DELAY is not set
-
-#
-# Fusion MPT device support
-#
-CONFIG_FUSION=y
-CONFIG_FUSION_SPI=y
-# CONFIG_FUSION_FC is not set
-# CONFIG_FUSION_SAS is not set
-CONFIG_FUSION_MAX_SGE=128
-# CONFIG_FUSION_CTL is not set
+# CONFIG_DM_UEVENT is not set
+# CONFIG_FUSION is not set
#
# IEEE 1394 (FireWire) support
#
# CONFIG_FIREWIRE is not set
-CONFIG_IEEE1394=y
-
-#
-# Subsystem Options
-#
-# CONFIG_IEEE1394_VERBOSEDEBUG is not set
-
-#
-# Controllers
-#
-
-#
-# Texas Instruments PCILynx requires I2C
-#
-CONFIG_IEEE1394_OHCI1394=y
-
-#
-# Protocols
-#
-# CONFIG_IEEE1394_VIDEO1394 is not set
-# CONFIG_IEEE1394_SBP2 is not set
-# CONFIG_IEEE1394_ETH1394_ROM_ENTRY is not set
-# CONFIG_IEEE1394_ETH1394 is not set
-# CONFIG_IEEE1394_DV1394 is not set
-CONFIG_IEEE1394_RAWIO=y
+# CONFIG_IEEE1394 is not set
# CONFIG_I2O is not set
CONFIG_MACINTOSH_DRIVERS=y
-# CONFIG_MAC_EMUMOUSEBTN is not set
+CONFIG_MAC_EMUMOUSEBTN=y
CONFIG_NETDEVICES=y
-CONFIG_NETDEVICES_MULTIQUEUE=y
+# CONFIG_NETDEVICES_MULTIQUEUE is not set
+# CONFIG_IFB is not set
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_MACVLAN is not set
# CONFIG_EQUALIZER is not set
# CONFIG_TUN is not set
+# CONFIG_VETH is not set
# CONFIG_NET_SB1000 is not set
# CONFIG_ARCNET is not set
# CONFIG_PHYLIB is not set
@@ -770,38 +917,40 @@ CONFIG_MII=y
# CONFIG_SUNGEM is not set
# CONFIG_CASSINI is not set
CONFIG_NET_VENDOR_3COM=y
-CONFIG_VORTEX=y
+# CONFIG_VORTEX is not set
# CONFIG_TYPHOON is not set
CONFIG_NET_TULIP=y
# CONFIG_DE2104X is not set
-CONFIG_TULIP=y
-# CONFIG_TULIP_MWI is not set
-# CONFIG_TULIP_MMIO is not set
-# CONFIG_TULIP_NAPI is not set
+# CONFIG_TULIP is not set
# CONFIG_DE4X5 is not set
# CONFIG_WINBOND_840 is not set
# CONFIG_DM9102 is not set
# CONFIG_ULI526X is not set
+# CONFIG_PCMCIA_XIRCOM is not set
# CONFIG_HP100 is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
CONFIG_NET_PCI=y
# CONFIG_PCNET32 is not set
# CONFIG_AMD8111_ETH is not set
# CONFIG_ADAPTEC_STARFIRE is not set
-CONFIG_B44=y
+# CONFIG_B44 is not set
CONFIG_FORCEDETH=y
# CONFIG_FORCEDETH_NAPI is not set
-# CONFIG_DGRS is not set
# CONFIG_EEPRO100 is not set
CONFIG_E100=y
# CONFIG_FEALNX is not set
# CONFIG_NATSEMI is not set
# CONFIG_NE2K_PCI is not set
-CONFIG_8139CP=y
+# CONFIG_8139CP is not set
CONFIG_8139TOO=y
-# CONFIG_8139TOO_PIO is not set
+CONFIG_8139TOO_PIO=y
# CONFIG_8139TOO_TUNE_TWISTER is not set
# CONFIG_8139TOO_8129 is not set
# CONFIG_8139_OLD_RX_RESET is not set
+# CONFIG_R6040 is not set
# CONFIG_SIS900 is not set
# CONFIG_EPIC100 is not set
# CONFIG_SUNDANCE is not set
@@ -814,34 +963,75 @@ CONFIG_NETDEV_1000=y
CONFIG_E1000=y
# CONFIG_E1000_NAPI is not set
# CONFIG_E1000_DISABLE_PACKET_SPLIT is not set
+# CONFIG_E1000E is not set
+# CONFIG_E1000E_ENABLED is not set
+# CONFIG_IP1000 is not set
+# CONFIG_IGB is not set
# CONFIG_NS83820 is not set
# CONFIG_HAMACHI is not set
# CONFIG_YELLOWFIN is not set
-CONFIG_R8169=y
-# CONFIG_R8169_NAPI is not set
+# CONFIG_R8169 is not set
# CONFIG_SIS190 is not set
# CONFIG_SKGE is not set
CONFIG_SKY2=y
+# CONFIG_SKY2_DEBUG is not set
# CONFIG_VIA_VELOCITY is not set
CONFIG_TIGON3=y
-CONFIG_BNX2=y
+# CONFIG_BNX2 is not set
# CONFIG_QLA3XXX is not set
# CONFIG_ATL1 is not set
CONFIG_NETDEV_10000=y
# CONFIG_CHELSIO_T1 is not set
# CONFIG_CHELSIO_T3 is not set
+# CONFIG_IXGBE is not set
# CONFIG_IXGB is not set
# CONFIG_S2IO is not set
# CONFIG_MYRI10GE is not set
# CONFIG_NETXEN_NIC is not set
+# CONFIG_NIU is not set
# CONFIG_MLX4_CORE is not set
-# CONFIG_TR is not set
+# CONFIG_TEHUTI is not set
+# CONFIG_BNX2X is not set
+# CONFIG_SFC is not set
+CONFIG_TR=y
+# CONFIG_IBMOL is not set
+# CONFIG_IBMLS is not set
+# CONFIG_3C359 is not set
+# CONFIG_TMS380TR is not set
#
# Wireless LAN
#
# CONFIG_WLAN_PRE80211 is not set
-# CONFIG_WLAN_80211 is not set
+CONFIG_WLAN_80211=y
+# CONFIG_PCMCIA_RAYCS is not set
+# CONFIG_IPW2100 is not set
+# CONFIG_IPW2200 is not set
+# CONFIG_LIBERTAS is not set
+# CONFIG_AIRO is not set
+# CONFIG_HERMES is not set
+# CONFIG_ATMEL is not set
+# CONFIG_AIRO_CS is not set
+# CONFIG_PCMCIA_WL3501 is not set
+# CONFIG_PRISM54 is not set
+# CONFIG_USB_ZD1201 is not set
+# CONFIG_USB_NET_RNDIS_WLAN is not set
+# CONFIG_RTL8180 is not set
+# CONFIG_RTL8187 is not set
+# CONFIG_ADM8211 is not set
+# CONFIG_P54_COMMON is not set
+CONFIG_ATH5K=y
+# CONFIG_ATH5K_DEBUG is not set
+# CONFIG_IWLWIFI is not set
+# CONFIG_IWLCORE is not set
+# CONFIG_IWLWIFI_LEDS is not set
+# CONFIG_IWL4965 is not set
+# CONFIG_IWL3945 is not set
+# CONFIG_HOSTAP is not set
+# CONFIG_B43 is not set
+# CONFIG_B43LEGACY is not set
+# CONFIG_ZD1211RW is not set
+# CONFIG_RT2X00 is not set
#
# USB Network Adapters
@@ -850,16 +1040,27 @@ CONFIG_NETDEV_10000=y
# CONFIG_USB_KAWETH is not set
# CONFIG_USB_PEGASUS is not set
# CONFIG_USB_RTL8150 is not set
-# CONFIG_USB_USBNET_MII is not set
# CONFIG_USB_USBNET is not set
+CONFIG_NET_PCMCIA=y
+# CONFIG_PCMCIA_3C589 is not set
+# CONFIG_PCMCIA_3C574 is not set
+# CONFIG_PCMCIA_FMVJ18X is not set
+# CONFIG_PCMCIA_PCNET is not set
+# CONFIG_PCMCIA_NMCLAN is not set
+# CONFIG_PCMCIA_SMC91C92 is not set
+# CONFIG_PCMCIA_XIRC2PS is not set
+# CONFIG_PCMCIA_AXNET is not set
+# CONFIG_PCMCIA_IBMTR is not set
# CONFIG_WAN is not set
-# CONFIG_FDDI is not set
+CONFIG_FDDI=y
+# CONFIG_DEFXX is not set
+# CONFIG_SKFP is not set
# CONFIG_HIPPI is not set
# CONFIG_PPP is not set
# CONFIG_SLIP is not set
# CONFIG_NET_FC is not set
-# CONFIG_SHAPER is not set
CONFIG_NETCONSOLE=y
+# CONFIG_NETCONSOLE_DYNAMIC is not set
CONFIG_NETPOLL=y
# CONFIG_NETPOLL_TRAP is not set
CONFIG_NET_POLL_CONTROLLER=y
@@ -870,18 +1071,17 @@ CONFIG_NET_POLL_CONTROLLER=y
# Input device support
#
CONFIG_INPUT=y
-# CONFIG_INPUT_FF_MEMLESS is not set
-# CONFIG_INPUT_POLLDEV is not set
+CONFIG_INPUT_FF_MEMLESS=y
+CONFIG_INPUT_POLLDEV=y
#
# Userland interfaces
#
CONFIG_INPUT_MOUSEDEV=y
-CONFIG_INPUT_MOUSEDEV_PSAUX=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
# CONFIG_INPUT_JOYDEV is not set
-# CONFIG_INPUT_TSDEV is not set
CONFIG_INPUT_EVDEV=y
# CONFIG_INPUT_EVBUG is not set
@@ -906,17 +1106,63 @@ CONFIG_MOUSE_PS2_TRACKPOINT=y
# CONFIG_MOUSE_SERIAL is not set
# CONFIG_MOUSE_APPLETOUCH is not set
# CONFIG_MOUSE_VSXXXAA is not set
-# CONFIG_INPUT_JOYSTICK is not set
-# CONFIG_INPUT_TABLET is not set
-# CONFIG_INPUT_TOUCHSCREEN is not set
-# CONFIG_INPUT_MISC is not set
+CONFIG_INPUT_JOYSTICK=y
+# CONFIG_JOYSTICK_ANALOG is not set
+# CONFIG_JOYSTICK_A3D is not set
+# CONFIG_JOYSTICK_ADI is not set
+# CONFIG_JOYSTICK_COBRA is not set
+# CONFIG_JOYSTICK_GF2K is not set
+# CONFIG_JOYSTICK_GRIP is not set
+# CONFIG_JOYSTICK_GRIP_MP is not set
+# CONFIG_JOYSTICK_GUILLEMOT is not set
+# CONFIG_JOYSTICK_INTERACT is not set
+# CONFIG_JOYSTICK_SIDEWINDER is not set
+# CONFIG_JOYSTICK_TMDC is not set
+# CONFIG_JOYSTICK_IFORCE is not set
+# CONFIG_JOYSTICK_WARRIOR is not set
+# CONFIG_JOYSTICK_MAGELLAN is not set
+# CONFIG_JOYSTICK_SPACEORB is not set
+# CONFIG_JOYSTICK_SPACEBALL is not set
+# CONFIG_JOYSTICK_STINGER is not set
+# CONFIG_JOYSTICK_TWIDJOY is not set
+# CONFIG_JOYSTICK_ZHENHUA is not set
+# CONFIG_JOYSTICK_JOYDUMP is not set
+# CONFIG_JOYSTICK_XPAD is not set
+CONFIG_INPUT_TABLET=y
+# CONFIG_TABLET_USB_ACECAD is not set
+# CONFIG_TABLET_USB_AIPTEK is not set
+# CONFIG_TABLET_USB_GTCO is not set
+# CONFIG_TABLET_USB_KBTAB is not set
+# CONFIG_TABLET_USB_WACOM is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_UCB1400 is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_PCSPKR is not set
+# CONFIG_INPUT_APANEL is not set
+# CONFIG_INPUT_WISTRON_BTNS is not set
+# CONFIG_INPUT_ATLAS_BTNS is not set
+# CONFIG_INPUT_ATI_REMOTE is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+# CONFIG_INPUT_UINPUT is not set
#
# Hardware I/O ports
#
CONFIG_SERIO=y
CONFIG_SERIO_I8042=y
-# CONFIG_SERIO_SERPORT is not set
+CONFIG_SERIO_SERPORT=y
# CONFIG_SERIO_CT82C710 is not set
# CONFIG_SERIO_PCIPS2 is not set
CONFIG_SERIO_LIBPS2=y
@@ -929,8 +1175,26 @@ CONFIG_SERIO_LIBPS2=y
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
CONFIG_HW_CONSOLE=y
-# CONFIG_VT_HW_CONSOLE_BINDING is not set
-# CONFIG_SERIAL_NONSTANDARD is not set
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_DEVKMEM=y
+CONFIG_SERIAL_NONSTANDARD=y
+# CONFIG_COMPUTONE is not set
+# CONFIG_ROCKETPORT is not set
+# CONFIG_CYCLADES is not set
+# CONFIG_DIGIEPCA is not set
+# CONFIG_MOXA_INTELLIO is not set
+# CONFIG_MOXA_SMARTIO is not set
+# CONFIG_ISI is not set
+# CONFIG_SYNCLINK is not set
+# CONFIG_SYNCLINKMP is not set
+# CONFIG_SYNCLINK_GT is not set
+# CONFIG_N_HDLC is not set
+# CONFIG_RISCOM8 is not set
+# CONFIG_SPECIALIX is not set
+# CONFIG_SX is not set
+# CONFIG_RIO is not set
+# CONFIG_STALDRV is not set
+# CONFIG_NOZOMI is not set
#
# Serial drivers
@@ -940,9 +1204,14 @@ CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_FIX_EARLYCON_MEM=y
CONFIG_SERIAL_8250_PCI=y
CONFIG_SERIAL_8250_PNP=y
-CONFIG_SERIAL_8250_NR_UARTS=4
+# CONFIG_SERIAL_8250_CS is not set
+CONFIG_SERIAL_8250_NR_UARTS=32
CONFIG_SERIAL_8250_RUNTIME_UARTS=4
-# CONFIG_SERIAL_8250_EXTENDED is not set
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250_DETECT_IRQ=y
+CONFIG_SERIAL_8250_RSA=y
#
# Non-8250 serial port support
@@ -951,89 +1220,275 @@ CONFIG_SERIAL_CORE=y
CONFIG_SERIAL_CORE_CONSOLE=y
# CONFIG_SERIAL_JSM is not set
CONFIG_UNIX98_PTYS=y
-CONFIG_LEGACY_PTYS=y
-CONFIG_LEGACY_PTY_COUNT=256
+# CONFIG_LEGACY_PTYS is not set
# CONFIG_IPMI_HANDLER is not set
-# CONFIG_WATCHDOG is not set
CONFIG_HW_RANDOM=y
-CONFIG_HW_RANDOM_INTEL=y
-CONFIG_HW_RANDOM_AMD=y
+# CONFIG_HW_RANDOM_INTEL is not set
+# CONFIG_HW_RANDOM_AMD is not set
CONFIG_HW_RANDOM_GEODE=y
CONFIG_HW_RANDOM_VIA=y
-# CONFIG_NVRAM is not set
-CONFIG_RTC=y
+CONFIG_NVRAM=y
# CONFIG_R3964 is not set
# CONFIG_APPLICOM is not set
# CONFIG_SONYPI is not set
-CONFIG_AGP=y
-# CONFIG_AGP_ALI is not set
-# CONFIG_AGP_ATI is not set
-# CONFIG_AGP_AMD is not set
-CONFIG_AGP_AMD64=y
-CONFIG_AGP_INTEL=y
-# CONFIG_AGP_NVIDIA is not set
-# CONFIG_AGP_SIS is not set
-# CONFIG_AGP_SWORKS is not set
-# CONFIG_AGP_VIA is not set
-# CONFIG_AGP_EFFICEON is not set
-# CONFIG_DRM is not set
+
+#
+# PCMCIA character devices
+#
+# CONFIG_SYNCLINK_CS is not set
+# CONFIG_CARDMAN_4000 is not set
+# CONFIG_CARDMAN_4040 is not set
+# CONFIG_IPWIRELESS is not set
# CONFIG_MWAVE is not set
# CONFIG_PC8736x_GPIO is not set
# CONFIG_NSC_GPIO is not set
# CONFIG_CS5535_GPIO is not set
-CONFIG_RAW_DRIVER=y
-CONFIG_MAX_RAW_DEVS=256
+# CONFIG_RAW_DRIVER is not set
CONFIG_HPET=y
# CONFIG_HPET_RTC_IRQ is not set
-CONFIG_HPET_MMAP=y
+# CONFIG_HPET_MMAP is not set
# CONFIG_HANGCHECK_TIMER is not set
# CONFIG_TCG_TPM is not set
# CONFIG_TELCLOCK is not set
CONFIG_DEVPORT=y
-# CONFIG_I2C is not set
-
-#
-# SPI support
-#
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_CHARDEV is not set
+
+#
+# I2C Hardware Bus support
+#
+# CONFIG_I2C_ALI1535 is not set
+# CONFIG_I2C_ALI1563 is not set
+# CONFIG_I2C_ALI15X3 is not set
+# CONFIG_I2C_AMD756 is not set
+# CONFIG_I2C_AMD8111 is not set
+CONFIG_I2C_I801=y
+# CONFIG_I2C_I810 is not set
+# CONFIG_I2C_PIIX4 is not set
+# CONFIG_I2C_NFORCE2 is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_PROSAVAGE is not set
+# CONFIG_I2C_SAVAGE4 is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_SCx200_ACB is not set
+# CONFIG_I2C_SIS5595 is not set
+# CONFIG_I2C_SIS630 is not set
+# CONFIG_I2C_SIS96X is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_TINY_USB is not set
+# CONFIG_I2C_VIA is not set
+# CONFIG_I2C_VIAPRO is not set
+# CONFIG_I2C_VOODOO3 is not set
+# CONFIG_I2C_PCA_PLATFORM is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
# CONFIG_SPI is not set
-# CONFIG_SPI_MASTER is not set
# CONFIG_W1 is not set
-# CONFIG_POWER_SUPPLY is not set
+CONFIG_POWER_SUPPLY=y
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+# CONFIG_PDA_POWER is not set
+# CONFIG_BATTERY_DS2760 is not set
# CONFIG_HWMON is not set
+CONFIG_THERMAL=y
+CONFIG_WATCHDOG=y
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+
+#
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+# CONFIG_ACQUIRE_WDT is not set
+# CONFIG_ADVANTECH_WDT is not set
+# CONFIG_ALIM1535_WDT is not set
+# CONFIG_ALIM7101_WDT is not set
+# CONFIG_SC520_WDT is not set
+# CONFIG_EUROTECH_WDT is not set
+# CONFIG_IB700_WDT is not set
+# CONFIG_IBMASR is not set
+# CONFIG_WAFER_WDT is not set
+# CONFIG_I6300ESB_WDT is not set
+# CONFIG_ITCO_WDT is not set
+# CONFIG_IT8712F_WDT is not set
+# CONFIG_HP_WATCHDOG is not set
+# CONFIG_SC1200_WDT is not set
+# CONFIG_PC87413_WDT is not set
+# CONFIG_60XX_WDT is not set
+# CONFIG_SBC8360_WDT is not set
+# CONFIG_SBC7240_WDT is not set
+# CONFIG_CPU5_WDT is not set
+# CONFIG_SMSC37B787_WDT is not set
+# CONFIG_W83627HF_WDT is not set
+# CONFIG_W83697HF_WDT is not set
+# CONFIG_W83877F_WDT is not set
+# CONFIG_W83977F_WDT is not set
+# CONFIG_MACHZ_WDT is not set
+# CONFIG_SBC_EPX_C3_WATCHDOG is not set
+
+#
+# PCI-based Watchdog Cards
+#
+# CONFIG_PCIPCWATCHDOG is not set
+# CONFIG_WDTPCI is not set
+
+#
+# USB-based Watchdog Cards
+#
+# CONFIG_USBPCWATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
#
# Multifunction device drivers
#
# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
#
# Multimedia devices
#
+
+#
+# Multimedia core support
+#
# CONFIG_VIDEO_DEV is not set
# CONFIG_DVB_CORE is not set
+
+#
+# Multimedia drivers
+#
CONFIG_DAB=y
# CONFIG_USB_DABUSB is not set
#
# Graphics support
#
-# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+CONFIG_AGP=y
+# CONFIG_AGP_ALI is not set
+# CONFIG_AGP_ATI is not set
+# CONFIG_AGP_AMD is not set
+CONFIG_AGP_AMD64=y
+CONFIG_AGP_INTEL=y
+# CONFIG_AGP_NVIDIA is not set
+# CONFIG_AGP_SIS is not set
+# CONFIG_AGP_SWORKS is not set
+# CONFIG_AGP_VIA is not set
+# CONFIG_AGP_EFFICEON is not set
+CONFIG_DRM=y
+# CONFIG_DRM_TDFX is not set
+# CONFIG_DRM_R128 is not set
+# CONFIG_DRM_RADEON is not set
+# CONFIG_DRM_I810 is not set
+# CONFIG_DRM_I830 is not set
+CONFIG_DRM_I915=y
+# CONFIG_DRM_MGA is not set
+# CONFIG_DRM_SIS is not set
+# CONFIG_DRM_VIA is not set
+# CONFIG_DRM_SAVAGE is not set
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+CONFIG_FB_DEFERRED_IO=y
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+CONFIG_FB_MODE_HELPERS=y
+CONFIG_FB_TILEBLITTING=y
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_CIRRUS is not set
+# CONFIG_FB_PM2 is not set
+# CONFIG_FB_CYBER2000 is not set
+# CONFIG_FB_ARC is not set
+# CONFIG_FB_ASILIANT is not set
+# CONFIG_FB_IMSTT is not set
+# CONFIG_FB_VGA16 is not set
+# CONFIG_FB_UVESA is not set
+# CONFIG_FB_VESA is not set
+CONFIG_FB_EFI=y
+# CONFIG_FB_IMAC is not set
+# CONFIG_FB_N411 is not set
+# CONFIG_FB_HGA is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_NVIDIA is not set
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_I810 is not set
+# CONFIG_FB_LE80578 is not set
+# CONFIG_FB_INTEL is not set
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_RADEON is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_S3 is not set
+# CONFIG_FB_SAVAGE is not set
+# CONFIG_FB_SIS is not set
+# CONFIG_FB_NEOMAGIC is not set
+# CONFIG_FB_KYRO is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_VT8623 is not set
+# CONFIG_FB_CYBLA is not set
+# CONFIG_FB_TRIDENT is not set
+# CONFIG_FB_ARK is not set
+# CONFIG_FB_PM3 is not set
+# CONFIG_FB_GEODE is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CORGI is not set
+# CONFIG_BACKLIGHT_PROGEAR is not set
#
# Display device support
#
# CONFIG_DISPLAY_SUPPORT is not set
-# CONFIG_VGASTATE is not set
-# CONFIG_FB is not set
#
# Console display driver support
#
CONFIG_VGA_CONSOLE=y
CONFIG_VGACON_SOFT_SCROLLBACK=y
-CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=128
+CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=64
CONFIG_VIDEO_SELECT=y
CONFIG_DUMMY_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE is not set
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_LOGO_LINUX_CLUT224=y
#
# Sound
@@ -1043,33 +1498,167 @@ CONFIG_SOUND=y
#
# Advanced Linux Sound Architecture
#
-# CONFIG_SND is not set
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+CONFIG_SND_HWDEP=y
+CONFIG_SND_SEQUENCER=y
+CONFIG_SND_SEQ_DUMMY=y
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=y
+CONFIG_SND_PCM_OSS=y
+CONFIG_SND_PCM_OSS_PLUGINS=y
+CONFIG_SND_SEQUENCER_OSS=y
+CONFIG_SND_DYNAMIC_MINORS=y
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+CONFIG_SND_VMASTER=y
+
+#
+# Generic devices
+#
+# CONFIG_SND_PCSP is not set
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_VIRMIDI is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+
+#
+# PCI devices
+#
+# CONFIG_SND_AD1889 is not set
+# CONFIG_SND_ALS300 is not set
+# CONFIG_SND_ALS4000 is not set
+# CONFIG_SND_ALI5451 is not set
+# CONFIG_SND_ATIIXP is not set
+# CONFIG_SND_ATIIXP_MODEM is not set
+# CONFIG_SND_AU8810 is not set
+# CONFIG_SND_AU8820 is not set
+# CONFIG_SND_AU8830 is not set
+# CONFIG_SND_AW2 is not set
+# CONFIG_SND_AZT3328 is not set
+# CONFIG_SND_BT87X is not set
+# CONFIG_SND_CA0106 is not set
+# CONFIG_SND_CMIPCI is not set
+# CONFIG_SND_OXYGEN is not set
+# CONFIG_SND_CS4281 is not set
+# CONFIG_SND_CS46XX is not set
+# CONFIG_SND_CS5530 is not set
+# CONFIG_SND_CS5535AUDIO is not set
+# CONFIG_SND_DARLA20 is not set
+# CONFIG_SND_GINA20 is not set
+# CONFIG_SND_LAYLA20 is not set
+# CONFIG_SND_DARLA24 is not set
+# CONFIG_SND_GINA24 is not set
+# CONFIG_SND_LAYLA24 is not set
+# CONFIG_SND_MONA is not set
+# CONFIG_SND_MIA is not set
+# CONFIG_SND_ECHO3G is not set
+# CONFIG_SND_INDIGO is not set
+# CONFIG_SND_INDIGOIO is not set
+# CONFIG_SND_INDIGODJ is not set
+# CONFIG_SND_EMU10K1 is not set
+# CONFIG_SND_EMU10K1X is not set
+# CONFIG_SND_ENS1370 is not set
+# CONFIG_SND_ENS1371 is not set
+# CONFIG_SND_ES1938 is not set
+# CONFIG_SND_ES1968 is not set
+# CONFIG_SND_FM801 is not set
+CONFIG_SND_HDA_INTEL=y
+CONFIG_SND_HDA_HWDEP=y
+CONFIG_SND_HDA_CODEC_REALTEK=y
+CONFIG_SND_HDA_CODEC_ANALOG=y
+CONFIG_SND_HDA_CODEC_SIGMATEL=y
+CONFIG_SND_HDA_CODEC_VIA=y
+CONFIG_SND_HDA_CODEC_ATIHDMI=y
+CONFIG_SND_HDA_CODEC_CONEXANT=y
+CONFIG_SND_HDA_CODEC_CMEDIA=y
+CONFIG_SND_HDA_CODEC_SI3054=y
+CONFIG_SND_HDA_GENERIC=y
+# CONFIG_SND_HDA_POWER_SAVE is not set
+# CONFIG_SND_HDSP is not set
+# CONFIG_SND_HDSPM is not set
+# CONFIG_SND_HIFIER is not set
+# CONFIG_SND_ICE1712 is not set
+# CONFIG_SND_ICE1724 is not set
+# CONFIG_SND_INTEL8X0 is not set
+# CONFIG_SND_INTEL8X0M is not set
+# CONFIG_SND_KORG1212 is not set
+# CONFIG_SND_MAESTRO3 is not set
+# CONFIG_SND_MIXART is not set
+# CONFIG_SND_NM256 is not set
+# CONFIG_SND_PCXHR is not set
+# CONFIG_SND_RIPTIDE is not set
+# CONFIG_SND_RME32 is not set
+# CONFIG_SND_RME96 is not set
+# CONFIG_SND_RME9652 is not set
+# CONFIG_SND_SIS7019 is not set
+# CONFIG_SND_SONICVIBES is not set
+# CONFIG_SND_TRIDENT is not set
+# CONFIG_SND_VIA82XX is not set
+# CONFIG_SND_VIA82XX_MODEM is not set
+# CONFIG_SND_VIRTUOSO is not set
+# CONFIG_SND_VX222 is not set
+# CONFIG_SND_YMFPCI is not set
+
+#
+# USB devices
+#
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_USX2Y is not set
+# CONFIG_SND_USB_CAIAQ is not set
+
+#
+# PCMCIA devices
+#
+# CONFIG_SND_VXPOCKET is not set
+# CONFIG_SND_PDAUDIOCF is not set
+
+#
+# System on Chip audio support
+#
+# CONFIG_SND_SOC is not set
+
+#
+# ALSA SoC audio for Freescale SOCs
+#
+
+#
+# SoC Audio for the Texas Instruments OMAP
+#
#
# Open Sound System
#
-CONFIG_SOUND_PRIME=y
-# CONFIG_SOUND_TRIDENT is not set
-# CONFIG_SOUND_MSNDCLAS is not set
-# CONFIG_SOUND_MSNDPIN is not set
-# CONFIG_SOUND_OSS is not set
+# CONFIG_SOUND_PRIME is not set
CONFIG_HID_SUPPORT=y
CONFIG_HID=y
-# CONFIG_HID_DEBUG is not set
+CONFIG_HID_DEBUG=y
+CONFIG_HIDRAW=y
#
# USB Input Devices
#
CONFIG_USB_HID=y
-# CONFIG_USB_HIDINPUT_POWERBOOK is not set
-# CONFIG_HID_FF is not set
-# CONFIG_USB_HIDDEV is not set
+CONFIG_USB_HIDINPUT_POWERBOOK=y
+CONFIG_HID_FF=y
+CONFIG_HID_PID=y
+CONFIG_LOGITECH_FF=y
+# CONFIG_LOGIRUMBLEPAD2_FF is not set
+CONFIG_PANTHERLORD_FF=y
+CONFIG_THRUSTMASTER_FF=y
+CONFIG_ZEROPLUS_FF=y
+CONFIG_USB_HIDDEV=y
CONFIG_USB_SUPPORT=y
CONFIG_USB_ARCH_HAS_HCD=y
CONFIG_USB_ARCH_HAS_OHCI=y
CONFIG_USB_ARCH_HAS_EHCI=y
CONFIG_USB=y
-# CONFIG_USB_DEBUG is not set
+CONFIG_USB_DEBUG=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
#
# Miscellaneous USB options
@@ -1077,18 +1666,18 @@ CONFIG_USB=y
CONFIG_USB_DEVICEFS=y
# CONFIG_USB_DEVICE_CLASS is not set
# CONFIG_USB_DYNAMIC_MINORS is not set
-# CONFIG_USB_SUSPEND is not set
-# CONFIG_USB_PERSIST is not set
+CONFIG_USB_SUSPEND=y
# CONFIG_USB_OTG is not set
#
# USB Host Controller Drivers
#
+# CONFIG_USB_C67X00_HCD is not set
CONFIG_USB_EHCI_HCD=y
-# CONFIG_USB_EHCI_SPLIT_ISO is not set
# CONFIG_USB_EHCI_ROOT_HUB_TT is not set
# CONFIG_USB_EHCI_TT_NEWSCHED is not set
# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
CONFIG_USB_OHCI_HCD=y
# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
@@ -1121,8 +1710,10 @@ CONFIG_USB_STORAGE=y
# CONFIG_USB_STORAGE_SDDR55 is not set
# CONFIG_USB_STORAGE_JUMPSHOT is not set
# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_ONETOUCH is not set
# CONFIG_USB_STORAGE_KARMA is not set
-# CONFIG_USB_LIBUSUAL is not set
+# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set
+CONFIG_USB_LIBUSUAL=y
#
# USB Imaging devices
@@ -1134,10 +1725,6 @@ CONFIG_USB_MON=y
#
# USB port drivers
#
-
-#
-# USB Serial Converter support
-#
# CONFIG_USB_SERIAL is not set
#
@@ -1163,90 +1750,125 @@ CONFIG_USB_MON=y
# CONFIG_USB_TRANCEVIBRATOR is not set
# CONFIG_USB_IOWARRIOR is not set
# CONFIG_USB_TEST is not set
+# CONFIG_USB_GADGET is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
#
-# USB DSL modem support
+# LED drivers
#
+# CONFIG_LEDS_CLEVO_MAIL is not set
#
-# USB Gadget Support
+# LED Triggers
#
-# CONFIG_USB_GADGET is not set
-# CONFIG_MMC is not set
+CONFIG_LEDS_TRIGGERS=y
+# CONFIG_LEDS_TRIGGER_TIMER is not set
+# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set
+# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_INFINIBAND is not set
+CONFIG_EDAC=y
#
-# LED devices
+# Reporting subsystems
#
-# CONFIG_NEW_LEDS is not set
+# CONFIG_EDAC_DEBUG is not set
+# CONFIG_EDAC_MM_EDAC is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_HCTOSYS is not set
+# CONFIG_RTC_DEBUG is not set
#
-# LED drivers
+# RTC interfaces
#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
#
-# LED Triggers
+# I2C RTC drivers
#
-# CONFIG_INFINIBAND is not set
-# CONFIG_EDAC is not set
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_S35390A is not set
#
-# Real Time Clock
+# SPI RTC drivers
#
-# CONFIG_RTC_CLASS is not set
#
-# DMA Engine support
+# Platform RTC drivers
#
-# CONFIG_DMA_ENGINE is not set
+CONFIG_RTC_DRV_CMOS=y
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_V3020 is not set
#
-# DMA Clients
+# on-CPU RTC drivers
#
+CONFIG_DMADEVICES=y
#
# DMA Devices
#
-CONFIG_VIRTUALIZATION=y
-# CONFIG_KVM is not set
+# CONFIG_INTEL_IOATDMA is not set
+# CONFIG_UIO is not set
#
-# Userspace I/O
+# Firmware Drivers
#
-# CONFIG_UIO is not set
+# CONFIG_EDD is not set
+CONFIG_EFI_VARS=y
+# CONFIG_DELL_RBU is not set
+# CONFIG_DCDBAS is not set
+CONFIG_DMIID=y
+# CONFIG_ISCSI_IBFT_FIND is not set
#
# File systems
#
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT2_FS_POSIX_ACL=y
-# CONFIG_EXT2_FS_SECURITY is not set
-# CONFIG_EXT2_FS_XIP is not set
+# CONFIG_EXT2_FS is not set
CONFIG_EXT3_FS=y
CONFIG_EXT3_FS_XATTR=y
CONFIG_EXT3_FS_POSIX_ACL=y
-# CONFIG_EXT3_FS_SECURITY is not set
+CONFIG_EXT3_FS_SECURITY=y
# CONFIG_EXT4DEV_FS is not set
CONFIG_JBD=y
# CONFIG_JBD_DEBUG is not set
CONFIG_FS_MBCACHE=y
-CONFIG_REISERFS_FS=y
-# CONFIG_REISERFS_CHECK is not set
-# CONFIG_REISERFS_PROC_INFO is not set
-CONFIG_REISERFS_FS_XATTR=y
-CONFIG_REISERFS_FS_POSIX_ACL=y
-# CONFIG_REISERFS_FS_SECURITY is not set
+# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
CONFIG_FS_POSIX_ACL=y
# CONFIG_XFS_FS is not set
-# CONFIG_GFS2_FS is not set
# CONFIG_OCFS2_FS is not set
-# CONFIG_MINIX_FS is not set
-# CONFIG_ROMFS_FS is not set
+CONFIG_DNOTIFY=y
CONFIG_INOTIFY=y
CONFIG_INOTIFY_USER=y
-# CONFIG_QUOTA is not set
-CONFIG_DNOTIFY=y
+CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+# CONFIG_PRINT_QUOTA_WARNING is not set
+# CONFIG_QFMT_V1 is not set
+CONFIG_QFMT_V2=y
+CONFIG_QUOTACTL=y
# CONFIG_AUTOFS_FS is not set
CONFIG_AUTOFS4_FS=y
# CONFIG_FUSE_FS is not set
@@ -1256,8 +1878,8 @@ CONFIG_GENERIC_ACL=y
# CD-ROM/DVD Filesystems
#
CONFIG_ISO9660_FS=y
-# CONFIG_JOLIET is not set
-# CONFIG_ZISOFS is not set
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
# CONFIG_UDF_FS is not set
#
@@ -1275,13 +1897,13 @@ CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
#
CONFIG_PROC_FS=y
CONFIG_PROC_KCORE=y
+CONFIG_PROC_VMCORE=y
CONFIG_PROC_SYSCTL=y
CONFIG_SYSFS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_HUGETLBFS=y
CONFIG_HUGETLB_PAGE=y
-CONFIG_RAMFS=y
# CONFIG_CONFIGFS_FS is not set
#
@@ -1289,6 +1911,7 @@ CONFIG_RAMFS=y
#
# CONFIG_ADFS_FS is not set
# CONFIG_AFFS_FS is not set
+# CONFIG_ECRYPT_FS is not set
# CONFIG_HFS_FS is not set
# CONFIG_HFSPLUS_FS is not set
# CONFIG_BEFS_FS is not set
@@ -1296,33 +1919,15 @@ CONFIG_RAMFS=y
# CONFIG_EFS_FS is not set
# CONFIG_CRAMFS is not set
# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
# CONFIG_HPFS_FS is not set
# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
# CONFIG_SYSV_FS is not set
# CONFIG_UFS_FS is not set
-
-#
-# Network File Systems
-#
-CONFIG_NFS_FS=y
-CONFIG_NFS_V3=y
-# CONFIG_NFS_V3_ACL is not set
-# CONFIG_NFS_V4 is not set
-# CONFIG_NFS_DIRECTIO is not set
-CONFIG_NFSD=y
-CONFIG_NFSD_V3=y
-# CONFIG_NFSD_V3_ACL is not set
-# CONFIG_NFSD_V4 is not set
-CONFIG_NFSD_TCP=y
-CONFIG_ROOT_NFS=y
-CONFIG_LOCKD=y
-CONFIG_LOCKD_V4=y
-CONFIG_EXPORTFS=y
-CONFIG_NFS_COMMON=y
-CONFIG_SUNRPC=y
-# CONFIG_SUNRPC_BIND34 is not set
-# CONFIG_RPCSEC_GSS_KRB5 is not set
-# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+# CONFIG_NFS_FS is not set
+# CONFIG_NFSD is not set
# CONFIG_SMB_FS is not set
# CONFIG_CIFS is not set
# CONFIG_NCP_FS is not set
@@ -1332,14 +1937,26 @@ CONFIG_SUNRPC=y
#
# Partition Types
#
-# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+CONFIG_OSF_PARTITION=y
+CONFIG_AMIGA_PARTITION=y
+# CONFIG_ATARI_PARTITION is not set
+CONFIG_MAC_PARTITION=y
CONFIG_MSDOS_PARTITION=y
-
-#
-# Native Language Support
-#
+CONFIG_BSD_DISKLABEL=y
+CONFIG_MINIX_SUBPARTITION=y
+CONFIG_SOLARIS_X86_PARTITION=y
+CONFIG_UNIXWARE_DISKLABEL=y
+# CONFIG_LDM_PARTITION is not set
+CONFIG_SGI_PARTITION=y
+# CONFIG_ULTRIX_PARTITION is not set
+CONFIG_SUN_PARTITION=y
+CONFIG_KARMA_PARTITION=y
+CONFIG_EFI_PARTITION=y
+# CONFIG_SYSV68_PARTITION is not set
CONFIG_NLS=y
-CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_DEFAULT="utf8"
CONFIG_NLS_CODEPAGE_437=y
# CONFIG_NLS_CODEPAGE_737 is not set
# CONFIG_NLS_CODEPAGE_775 is not set
@@ -1374,37 +1991,33 @@ CONFIG_NLS_ISO8859_1=y
# CONFIG_NLS_ISO8859_9 is not set
# CONFIG_NLS_ISO8859_13 is not set
# CONFIG_NLS_ISO8859_14 is not set
-CONFIG_NLS_ISO8859_15=y
+# CONFIG_NLS_ISO8859_15 is not set
# CONFIG_NLS_KOI8_R is not set
# CONFIG_NLS_KOI8_U is not set
CONFIG_NLS_UTF8=y
-
-#
-# Distributed Lock Manager
-#
# CONFIG_DLM is not set
-CONFIG_INSTRUMENTATION=y
-CONFIG_PROFILING=y
-CONFIG_OPROFILE=y
-CONFIG_KPROBES=y
#
# Kernel hacking
#
CONFIG_TRACE_IRQFLAGS_SUPPORT=y
# CONFIG_PRINTK_TIME is not set
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_FRAME_WARN=2048
CONFIG_MAGIC_SYSRQ=y
-CONFIG_UNUSED_SYMBOLS=y
-# CONFIG_DEBUG_FS is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+CONFIG_DEBUG_FS=y
# CONFIG_HEADERS_CHECK is not set
CONFIG_DEBUG_KERNEL=y
# CONFIG_DEBUG_SHIRQ is not set
-CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_DETECT_SOFTLOCKUP is not set
# CONFIG_SCHED_DEBUG is not set
-# CONFIG_SCHEDSTATS is not set
+CONFIG_SCHEDSTATS=y
CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_OBJECTS is not set
# CONFIG_SLUB_DEBUG_ON is not set
+# CONFIG_SLUB_STATS is not set
# CONFIG_DEBUG_RT_MUTEXES is not set
# CONFIG_RT_MUTEX_TESTER is not set
# CONFIG_DEBUG_SPINLOCK is not set
@@ -1419,48 +2032,174 @@ CONFIG_TIMER_STATS=y
CONFIG_DEBUG_BUGVERBOSE=y
# CONFIG_DEBUG_INFO is not set
# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
# CONFIG_DEBUG_LIST is not set
-# CONFIG_FRAME_POINTER is not set
-CONFIG_OPTIMIZE_INLINING=y
+# CONFIG_DEBUG_SG is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_BOOT_PRINTK_DELAY is not set
# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_KPROBES_SANITY_TEST is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
# CONFIG_LKDTM is not set
# CONFIG_FAULT_INJECTION is not set
+# CONFIG_LATENCYTOP is not set
+CONFIG_PROVIDE_OHCI1394_DMA_INIT=y
+# CONFIG_SAMPLES is not set
+# CONFIG_KGDB is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_NONPROMISC_DEVMEM is not set
CONFIG_EARLY_PRINTK=y
CONFIG_DEBUG_STACKOVERFLOW=y
-# CONFIG_DEBUG_STACK_USAGE is not set
-# CONFIG_DEBUG_RODATA is not set
+CONFIG_DEBUG_STACK_USAGE=y
+# CONFIG_DEBUG_PAGEALLOC is not set
+# CONFIG_X86_PTDUMP is not set
+CONFIG_DEBUG_RODATA=y
+# CONFIG_DEBUG_RODATA_TEST is not set
+CONFIG_DEBUG_NX_TEST=m
# CONFIG_4KSTACKS is not set
CONFIG_X86_FIND_SMP_CONFIG=y
CONFIG_X86_MPPARSE=y
CONFIG_DOUBLEFAULT=y
+CONFIG_IO_DELAY_TYPE_0X80=0
+CONFIG_IO_DELAY_TYPE_0XED=1
+CONFIG_IO_DELAY_TYPE_UDELAY=2
+CONFIG_IO_DELAY_TYPE_NONE=3
+CONFIG_IO_DELAY_0X80=y
+# CONFIG_IO_DELAY_0XED is not set
+# CONFIG_IO_DELAY_UDELAY is not set
+# CONFIG_IO_DELAY_NONE is not set
+CONFIG_DEFAULT_IO_DELAY_TYPE=0
+CONFIG_DEBUG_BOOT_PARAMS=y
+# CONFIG_CPA_DEBUG is not set
#
# Security options
#
-# CONFIG_KEYS is not set
-# CONFIG_SECURITY is not set
-# CONFIG_CRYPTO is not set
+CONFIG_KEYS=y
+CONFIG_KEYS_DEBUG_PROC_KEYS=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+# CONFIG_SECURITY_NETWORK_XFRM is not set
+CONFIG_SECURITY_CAPABILITIES=y
+CONFIG_SECURITY_FILE_CAPABILITIES=y
+# CONFIG_SECURITY_ROOTPLUG is not set
+CONFIG_SECURITY_DEFAULT_MMAP_MIN_ADDR=65536
+CONFIG_SECURITY_SELINUX=y
+CONFIG_SECURITY_SELINUX_BOOTPARAM=y
+CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1
+CONFIG_SECURITY_SELINUX_DISABLE=y
+CONFIG_SECURITY_SELINUX_DEVELOP=y
+CONFIG_SECURITY_SELINUX_AVC_STATS=y
+CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1
+# CONFIG_SECURITY_SELINUX_ENABLE_SECMARK_DEFAULT is not set
+# CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX is not set
+# CONFIG_SECURITY_SMACK is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_AEAD=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_MANAGER=y
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_AUTHENC=y
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+CONFIG_CRYPTO_CBC=y
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+CONFIG_CRYPTO_ECB=y
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+CONFIG_CRYPTO_HMAC=y
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+CONFIG_CRYPTO_SHA1=y
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+CONFIG_CRYPTO_AES=y
+# CONFIG_CRYPTO_AES_586 is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+CONFIG_CRYPTO_ARC4=y
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SALSA20_586 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+# CONFIG_CRYPTO_TWOFISH_586 is not set
+
+#
+# Compression
+#
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_LZO is not set
+CONFIG_CRYPTO_HW=y
+# CONFIG_CRYPTO_DEV_PADLOCK is not set
+# CONFIG_CRYPTO_DEV_GEODE is not set
+# CONFIG_CRYPTO_DEV_HIFN_795X is not set
+CONFIG_HAVE_KVM=y
+CONFIG_VIRTUALIZATION=y
+# CONFIG_KVM is not set
+# CONFIG_LGUEST is not set
+# CONFIG_VIRTIO_PCI is not set
+# CONFIG_VIRTIO_BALLOON is not set
#
# Library routines
#
CONFIG_BITREVERSE=y
+CONFIG_GENERIC_FIND_FIRST_BIT=y
+CONFIG_GENERIC_FIND_NEXT_BIT=y
# CONFIG_CRC_CCITT is not set
# CONFIG_CRC16 is not set
# CONFIG_CRC_ITU_T is not set
CONFIG_CRC32=y
# CONFIG_CRC7 is not set
# CONFIG_LIBCRC32C is not set
+CONFIG_AUDIT_GENERIC=y
CONFIG_ZLIB_INFLATE=y
CONFIG_PLIST=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_HAS_DMA=y
-CONFIG_GENERIC_HARDIRQS=y
-CONFIG_GENERIC_IRQ_PROBE=y
-CONFIG_GENERIC_PENDING_IRQ=y
-CONFIG_X86_SMP=y
-CONFIG_X86_HT=y
-CONFIG_X86_BIOS_REBOOT=y
-CONFIG_X86_TRAMPOLINE=y
-CONFIG_KTIME_SCALAR=y
diff --git a/arch/x86/configs/x86_64_defconfig b/arch/x86/configs/x86_64_defconfig
index 2d6f5b2809d2..ae5124e064d4 100644
--- a/arch/x86/configs/x86_64_defconfig
+++ b/arch/x86/configs/x86_64_defconfig
@@ -1,64 +1,103 @@
#
# Automatically generated make config: don't edit
-# Linux kernel version: 2.6.22-git14
-# Fri Jul 20 09:53:15 2007
+# Linux kernel version: 2.6.26-rc1
+# Sun May 4 19:59:57 2008
#
-CONFIG_X86_64=y
CONFIG_64BIT=y
+# CONFIG_X86_32 is not set
+CONFIG_X86_64=y
CONFIG_X86=y
+CONFIG_DEFCONFIG_LIST="arch/x86/configs/x86_64_defconfig"
+# CONFIG_GENERIC_LOCKBREAK is not set
CONFIG_GENERIC_TIME=y
-CONFIG_GENERIC_TIME_VSYSCALL=y
CONFIG_GENERIC_CMOS_UPDATE=y
-CONFIG_ZONE_DMA32=y
+CONFIG_CLOCKSOURCE_WATCHDOG=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
CONFIG_LOCKDEP_SUPPORT=y
CONFIG_STACKTRACE_SUPPORT=y
-CONFIG_SEMAPHORE_SLEEPERS=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_FAST_CMPXCHG_LOCAL=y
CONFIG_MMU=y
CONFIG_ZONE_DMA=y
-CONFIG_QUICKLIST=y
-CONFIG_NR_QUICK=2
-CONFIG_RWSEM_GENERIC_SPINLOCK=y
-CONFIG_GENERIC_HWEIGHT=y
-CONFIG_GENERIC_CALIBRATE_DELAY=y
-CONFIG_X86_CMPXCHG=y
-CONFIG_EARLY_PRINTK=y
CONFIG_GENERIC_ISA_DMA=y
CONFIG_GENERIC_IOMAP=y
-CONFIG_ARCH_MAY_HAVE_PC_FDC=y
-CONFIG_ARCH_POPULATES_NODE_MAP=y
-CONFIG_DMI=y
-CONFIG_AUDIT_ARCH=y
CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_HWEIGHT=y
+# CONFIG_GENERIC_GPIO is not set
+CONFIG_ARCH_MAY_HAVE_PC_FDC=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
# CONFIG_ARCH_HAS_ILOG2_U32 is not set
# CONFIG_ARCH_HAS_ILOG2_U64 is not set
-CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_GENERIC_TIME_VSYSCALL=y
+CONFIG_ARCH_HAS_CPU_RELAX=y
+CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y
+CONFIG_HAVE_SETUP_PER_CPU_AREA=y
+CONFIG_HAVE_CPUMASK_OF_CPU_MAP=y
+CONFIG_ARCH_HIBERNATION_POSSIBLE=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_ZONE_DMA32=y
+CONFIG_ARCH_POPULATES_NODE_MAP=y
+CONFIG_AUDIT_ARCH=y
+CONFIG_ARCH_SUPPORTS_AOUT=y
+CONFIG_ARCH_SUPPORTS_OPTIMIZED_INLINING=y
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_PENDING_IRQ=y
+CONFIG_X86_SMP=y
+CONFIG_X86_64_SMP=y
+CONFIG_X86_HT=y
+CONFIG_X86_BIOS_REBOOT=y
+CONFIG_X86_TRAMPOLINE=y
+# CONFIG_KTIME_SCALAR is not set
#
-# Code maturity level options
+# General setup
#
CONFIG_EXPERIMENTAL=y
CONFIG_LOCK_KERNEL=y
CONFIG_INIT_ENV_ARG_LIMIT=32
-
-#
-# General setup
-#
CONFIG_LOCALVERSION=""
-CONFIG_LOCALVERSION_AUTO=y
+# CONFIG_LOCALVERSION_AUTO is not set
CONFIG_SWAP=y
CONFIG_SYSVIPC=y
CONFIG_SYSVIPC_SYSCTL=y
CONFIG_POSIX_MQUEUE=y
-# CONFIG_BSD_PROCESS_ACCT is not set
-# CONFIG_TASKSTATS is not set
-# CONFIG_USER_NS is not set
-# CONFIG_AUDIT is not set
-CONFIG_IKCONFIG=y
-CONFIG_IKCONFIG_PROC=y
-CONFIG_LOG_BUF_SHIFT=18
-# CONFIG_CPUSETS is not set
-CONFIG_SYSFS_DEPRECATED=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_AUDIT=y
+CONFIG_AUDITSYSCALL=y
+CONFIG_AUDIT_TREE=y
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=17
+CONFIG_CGROUPS=y
+# CONFIG_CGROUP_DEBUG is not set
+CONFIG_CGROUP_NS=y
+# CONFIG_CGROUP_DEVICE is not set
+CONFIG_CPUSETS=y
+CONFIG_GROUP_SCHED=y
+CONFIG_FAIR_GROUP_SCHED=y
+# CONFIG_RT_GROUP_SCHED is not set
+# CONFIG_USER_SCHED is not set
+CONFIG_CGROUP_SCHED=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+# CONFIG_CGROUP_MEM_RES_CTLR is not set
+# CONFIG_SYSFS_DEPRECATED_V2 is not set
+CONFIG_PROC_PID_CPUSET=y
CONFIG_RELAY=y
+CONFIG_NAMESPACES=y
+CONFIG_UTS_NS=y
+CONFIG_IPC_NS=y
+CONFIG_USER_NS=y
+CONFIG_PID_NS=y
CONFIG_BLK_DEV_INITRD=y
CONFIG_INITRAMFS_SOURCE=""
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
@@ -66,13 +105,15 @@ CONFIG_SYSCTL=y
# CONFIG_EMBEDDED is not set
CONFIG_UID16=y
CONFIG_SYSCTL_SYSCALL=y
+CONFIG_SYSCTL_SYSCALL_CHECK=y
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
-# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_KALLSYMS_EXTRA_PASS=y
CONFIG_HOTPLUG=y
CONFIG_PRINTK=y
CONFIG_BUG=y
CONFIG_ELF_CORE=y
+# CONFIG_COMPAT_BRK is not set
CONFIG_BASE_FULL=y
CONFIG_FUTEX=y
CONFIG_ANON_INODES=y
@@ -82,9 +123,21 @@ CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
CONFIG_SHMEM=y
CONFIG_VM_EVENT_COUNTERS=y
-CONFIG_SLAB=y
-# CONFIG_SLUB is not set
+CONFIG_SLUB_DEBUG=y
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
# CONFIG_SLOB is not set
+CONFIG_PROFILING=y
+CONFIG_MARKERS=y
+# CONFIG_OPROFILE is not set
+CONFIG_HAVE_OPROFILE=y
+CONFIG_KPROBES=y
+CONFIG_KRETPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+# CONFIG_HAVE_DMA_ATTRS is not set
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SLABINFO=y
CONFIG_RT_MUTEXES=y
# CONFIG_TINY_SHMEM is not set
CONFIG_BASE_SMALL=0
@@ -96,14 +149,15 @@ CONFIG_MODULE_FORCE_UNLOAD=y
# CONFIG_KMOD is not set
CONFIG_STOP_MACHINE=y
CONFIG_BLOCK=y
-# CONFIG_BLK_DEV_IO_TRACE is not set
-# CONFIG_BLK_DEV_BSG is not set
+CONFIG_BLK_DEV_IO_TRACE=y
+CONFIG_BLK_DEV_BSG=y
+CONFIG_BLOCK_COMPAT=y
#
# IO Schedulers
#
CONFIG_IOSCHED_NOOP=y
-# CONFIG_IOSCHED_AS is not set
+CONFIG_IOSCHED_AS=y
CONFIG_IOSCHED_DEADLINE=y
CONFIG_IOSCHED_CFQ=y
# CONFIG_DEFAULT_AS is not set
@@ -111,107 +165,177 @@ CONFIG_IOSCHED_CFQ=y
CONFIG_DEFAULT_CFQ=y
# CONFIG_DEFAULT_NOOP is not set
CONFIG_DEFAULT_IOSCHED="cfq"
+CONFIG_CLASSIC_RCU=y
#
# Processor type and features
#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_SMP=y
CONFIG_X86_PC=y
+# CONFIG_X86_ELAN is not set
+# CONFIG_X86_VOYAGER is not set
+# CONFIG_X86_NUMAQ is not set
+# CONFIG_X86_SUMMIT is not set
+# CONFIG_X86_BIGSMP is not set
+# CONFIG_X86_VISWS is not set
+# CONFIG_X86_GENERICARCH is not set
+# CONFIG_X86_ES7000 is not set
+# CONFIG_X86_RDC321X is not set
# CONFIG_X86_VSMP is not set
+# CONFIG_PARAVIRT_GUEST is not set
+CONFIG_MEMTEST_BOOTPARAM=y
+CONFIG_MEMTEST_BOOTPARAM_VALUE=0
+# CONFIG_M386 is not set
+# CONFIG_M486 is not set
+# CONFIG_M586 is not set
+# CONFIG_M586TSC is not set
+# CONFIG_M586MMX is not set
+# CONFIG_M686 is not set
+# CONFIG_MPENTIUMII is not set
+# CONFIG_MPENTIUMIII is not set
+# CONFIG_MPENTIUMM is not set
+# CONFIG_MPENTIUM4 is not set
+# CONFIG_MK6 is not set
+# CONFIG_MK7 is not set
# CONFIG_MK8 is not set
+# CONFIG_MCRUSOE is not set
+# CONFIG_MEFFICEON is not set
+# CONFIG_MWINCHIPC6 is not set
+# CONFIG_MWINCHIP2 is not set
+# CONFIG_MWINCHIP3D is not set
+# CONFIG_MGEODEGX1 is not set
+# CONFIG_MGEODE_LX is not set
+# CONFIG_MCYRIXIII is not set
+# CONFIG_MVIAC3_2 is not set
+# CONFIG_MVIAC7 is not set
# CONFIG_MPSC is not set
-# CONFIG_MCORE2 is not set
-CONFIG_GENERIC_CPU=y
-CONFIG_X86_L1_CACHE_BYTES=128
-CONFIG_X86_L1_CACHE_SHIFT=7
-CONFIG_X86_INTERNODE_CACHE_BYTES=128
-CONFIG_X86_TSC=y
+CONFIG_MCORE2=y
+# CONFIG_GENERIC_CPU is not set
+CONFIG_X86_CPU=y
+CONFIG_X86_L1_CACHE_BYTES=64
+CONFIG_X86_INTERNODE_CACHE_BYTES=64
+CONFIG_X86_CMPXCHG=y
+CONFIG_X86_L1_CACHE_SHIFT=6
CONFIG_X86_GOOD_APIC=y
-# CONFIG_MICROCODE is not set
-CONFIG_X86_MSR=y
-CONFIG_X86_CPUID=y
-CONFIG_X86_HT=y
-CONFIG_X86_IO_APIC=y
-CONFIG_X86_LOCAL_APIC=y
-CONFIG_MTRR=y
-CONFIG_SMP=y
-CONFIG_SCHED_SMT=y
+CONFIG_X86_INTEL_USERCOPY=y
+CONFIG_X86_USE_PPRO_CHECKSUM=y
+CONFIG_X86_P6_NOP=y
+CONFIG_X86_TSC=y
+CONFIG_X86_CMOV=y
+CONFIG_X86_MINIMUM_CPU_FAMILY=64
+CONFIG_X86_DEBUGCTLMSR=y
+CONFIG_HPET_TIMER=y
+CONFIG_HPET_EMULATE_RTC=y
+CONFIG_DMI=y
+CONFIG_GART_IOMMU=y
+CONFIG_CALGARY_IOMMU=y
+CONFIG_CALGARY_IOMMU_ENABLED_BY_DEFAULT=y
+CONFIG_SWIOTLB=y
+CONFIG_IOMMU_HELPER=y
+CONFIG_NR_CPUS=4
+# CONFIG_SCHED_SMT is not set
CONFIG_SCHED_MC=y
# CONFIG_PREEMPT_NONE is not set
CONFIG_PREEMPT_VOLUNTARY=y
# CONFIG_PREEMPT is not set
-CONFIG_PREEMPT_BKL=y
+CONFIG_X86_LOCAL_APIC=y
+CONFIG_X86_IO_APIC=y
+# CONFIG_X86_MCE is not set
+# CONFIG_I8K is not set
+# CONFIG_MICROCODE is not set
+CONFIG_X86_MSR=y
+CONFIG_X86_CPUID=y
CONFIG_NUMA=y
CONFIG_K8_NUMA=y
-CONFIG_NODES_SHIFT=6
CONFIG_X86_64_ACPI_NUMA=y
-CONFIG_NUMA_EMU=y
+CONFIG_NODES_SPAN_OTHER_NODES=y
+# CONFIG_NUMA_EMU is not set
+CONFIG_NODES_SHIFT=6
+CONFIG_ARCH_SPARSEMEM_DEFAULT=y
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ARCH_SELECT_MEMORY_MODEL=y
+CONFIG_SELECT_MEMORY_MODEL=y
+# CONFIG_FLATMEM_MANUAL is not set
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+CONFIG_SPARSEMEM_MANUAL=y
+CONFIG_SPARSEMEM=y
CONFIG_NEED_MULTIPLE_NODES=y
+CONFIG_HAVE_MEMORY_PRESENT=y
# CONFIG_SPARSEMEM_STATIC is not set
+CONFIG_SPARSEMEM_EXTREME=y
+CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
+CONFIG_SPARSEMEM_VMEMMAP=y
+
+#
+# Memory hotplug is currently incompatible with Software Suspend
+#
+CONFIG_PAGEFLAGS_EXTENDED=y
CONFIG_SPLIT_PTLOCK_CPUS=4
CONFIG_MIGRATION=y
CONFIG_RESOURCES_64BIT=y
CONFIG_ZONE_DMA_FLAG=1
CONFIG_BOUNCE=y
CONFIG_VIRT_TO_BUS=y
-CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID=y
-CONFIG_OUT_OF_LINE_PFN_TO_PAGE=y
-CONFIG_NR_CPUS=32
-CONFIG_PHYSICAL_ALIGN=0x200000
-CONFIG_HOTPLUG_CPU=y
-CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
-CONFIG_HPET_TIMER=y
-CONFIG_HPET_EMULATE_RTC=y
-CONFIG_GART_IOMMU=y
-# CONFIG_CALGARY_IOMMU is not set
-CONFIG_SWIOTLB=y
-CONFIG_X86_MCE=y
-CONFIG_X86_MCE_INTEL=y
-CONFIG_X86_MCE_AMD=y
-# CONFIG_KEXEC is not set
-# CONFIG_CRASH_DUMP is not set
-# CONFIG_RELOCATABLE is not set
-CONFIG_PHYSICAL_START=0x200000
+CONFIG_MTRR=y
+# CONFIG_X86_PAT is not set
+CONFIG_EFI=y
CONFIG_SECCOMP=y
-# CONFIG_CC_STACKPROTECTOR is not set
# CONFIG_HZ_100 is not set
-CONFIG_HZ_250=y
+# CONFIG_HZ_250 is not set
# CONFIG_HZ_300 is not set
-# CONFIG_HZ_1000 is not set
-CONFIG_HZ=250
-CONFIG_K8_NB=y
-CONFIG_GENERIC_HARDIRQS=y
-CONFIG_GENERIC_IRQ_PROBE=y
-CONFIG_ISA_DMA_API=y
-CONFIG_GENERIC_PENDING_IRQ=y
+CONFIG_HZ_1000=y
+CONFIG_HZ=1000
+CONFIG_SCHED_HRTICK=y
+CONFIG_KEXEC=y
+CONFIG_CRASH_DUMP=y
+CONFIG_PHYSICAL_START=0x1000000
+CONFIG_RELOCATABLE=y
+CONFIG_PHYSICAL_ALIGN=0x200000
+CONFIG_HOTPLUG_CPU=y
+# CONFIG_COMPAT_VDSO is not set
+CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
+CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID=y
#
# Power management options
#
+CONFIG_ARCH_HIBERNATION_HEADER=y
CONFIG_PM=y
-# CONFIG_PM_LEGACY is not set
-# CONFIG_PM_DEBUG is not set
+CONFIG_PM_DEBUG=y
+# CONFIG_PM_VERBOSE is not set
+CONFIG_CAN_PM_TRACE=y
+CONFIG_PM_TRACE=y
+CONFIG_PM_TRACE_RTC=y
+CONFIG_PM_SLEEP_SMP=y
+CONFIG_PM_SLEEP=y
+CONFIG_SUSPEND=y
+CONFIG_SUSPEND_FREEZER=y
CONFIG_HIBERNATION=y
CONFIG_PM_STD_PARTITION=""
-
-#
-# ACPI (Advanced Configuration and Power Interface) Support
-#
CONFIG_ACPI=y
CONFIG_ACPI_SLEEP=y
-CONFIG_ACPI_SLEEP_PROC_FS=y
-CONFIG_ACPI_SLEEP_PROC_SLEEP=y
CONFIG_ACPI_PROCFS=y
+CONFIG_ACPI_PROCFS_POWER=y
+CONFIG_ACPI_SYSFS_POWER=y
+CONFIG_ACPI_PROC_EVENT=y
CONFIG_ACPI_AC=y
CONFIG_ACPI_BATTERY=y
CONFIG_ACPI_BUTTON=y
CONFIG_ACPI_FAN=y
-# CONFIG_ACPI_DOCK is not set
+CONFIG_ACPI_DOCK=y
+# CONFIG_ACPI_BAY is not set
CONFIG_ACPI_PROCESSOR=y
CONFIG_ACPI_HOTPLUG_CPU=y
CONFIG_ACPI_THERMAL=y
CONFIG_ACPI_NUMA=y
+# CONFIG_ACPI_WMI is not set
# CONFIG_ACPI_ASUS is not set
# CONFIG_ACPI_TOSHIBA is not set
+# CONFIG_ACPI_CUSTOM_DSDT is not set
CONFIG_ACPI_BLACKLIST_YEAR=0
# CONFIG_ACPI_DEBUG is not set
CONFIG_ACPI_EC=y
@@ -227,29 +351,34 @@ CONFIG_ACPI_CONTAINER=y
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_TABLE=y
CONFIG_CPU_FREQ_DEBUG=y
-CONFIG_CPU_FREQ_STAT=y
-# CONFIG_CPU_FREQ_STAT_DETAILS is not set
-CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
-# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
+# CONFIG_CPU_FREQ_STAT is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
CONFIG_CPU_FREQ_GOV_USERSPACE=y
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
-CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
#
# CPUFreq processor drivers
#
-CONFIG_X86_POWERNOW_K8=y
-CONFIG_X86_POWERNOW_K8_ACPI=y
-# CONFIG_X86_SPEEDSTEP_CENTRINO is not set
CONFIG_X86_ACPI_CPUFREQ=y
+# CONFIG_X86_POWERNOW_K8 is not set
+# CONFIG_X86_SPEEDSTEP_CENTRINO is not set
+# CONFIG_X86_P4_CLOCKMOD is not set
#
# shared options
#
-CONFIG_X86_ACPI_CPUFREQ_PROC_INTF=y
+# CONFIG_X86_ACPI_CPUFREQ_PROC_INTF is not set
# CONFIG_X86_SPEEDSTEP_LIB is not set
+CONFIG_CPU_IDLE=y
+CONFIG_CPU_IDLE_GOV_LADDER=y
+CONFIG_CPU_IDLE_GOV_MENU=y
#
# Bus options (PCI etc.)
@@ -257,27 +386,56 @@ CONFIG_X86_ACPI_CPUFREQ_PROC_INTF=y
CONFIG_PCI=y
CONFIG_PCI_DIRECT=y
CONFIG_PCI_MMCONFIG=y
+CONFIG_PCI_DOMAINS=y
+CONFIG_DMAR=y
+CONFIG_DMAR_GFX_WA=y
+CONFIG_DMAR_FLOPPY_WA=y
CONFIG_PCIEPORTBUS=y
+# CONFIG_HOTPLUG_PCI_PCIE is not set
CONFIG_PCIEAER=y
+# CONFIG_PCIEASPM is not set
CONFIG_ARCH_SUPPORTS_MSI=y
CONFIG_PCI_MSI=y
+# CONFIG_PCI_LEGACY is not set
# CONFIG_PCI_DEBUG is not set
-# CONFIG_HT_IRQ is not set
-
-#
-# PCCARD (PCMCIA/CardBus) support
-#
-# CONFIG_PCCARD is not set
-# CONFIG_HOTPLUG_PCI is not set
+CONFIG_HT_IRQ=y
+CONFIG_ISA_DMA_API=y
+CONFIG_K8_NB=y
+CONFIG_PCCARD=y
+# CONFIG_PCMCIA_DEBUG is not set
+CONFIG_PCMCIA=y
+CONFIG_PCMCIA_LOAD_CIS=y
+CONFIG_PCMCIA_IOCTL=y
+CONFIG_CARDBUS=y
+
+#
+# PC-card bridges
+#
+CONFIG_YENTA=y
+CONFIG_YENTA_O2=y
+CONFIG_YENTA_RICOH=y
+CONFIG_YENTA_TI=y
+CONFIG_YENTA_ENE_TUNE=y
+CONFIG_YENTA_TOSHIBA=y
+# CONFIG_PD6729 is not set
+# CONFIG_I82092 is not set
+CONFIG_PCCARD_NONSTATIC=y
+CONFIG_HOTPLUG_PCI=y
+# CONFIG_HOTPLUG_PCI_FAKE is not set
+# CONFIG_HOTPLUG_PCI_ACPI is not set
+# CONFIG_HOTPLUG_PCI_CPCI is not set
+# CONFIG_HOTPLUG_PCI_SHPC is not set
#
# Executable file formats / Emulations
#
CONFIG_BINFMT_ELF=y
-# CONFIG_BINFMT_MISC is not set
+CONFIG_COMPAT_BINFMT_ELF=y
+CONFIG_BINFMT_MISC=y
CONFIG_IA32_EMULATION=y
-CONFIG_IA32_AOUT=y
+# CONFIG_IA32_AOUT is not set
CONFIG_COMPAT=y
+CONFIG_COMPAT_FOR_U64_ALIGNMENT=y
CONFIG_SYSVIPC_COMPAT=y
#
@@ -289,22 +447,31 @@ CONFIG_NET=y
# Networking options
#
CONFIG_PACKET=y
-# CONFIG_PACKET_MMAP is not set
+CONFIG_PACKET_MMAP=y
CONFIG_UNIX=y
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=y
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
# CONFIG_NET_KEY is not set
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
-# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_ASK_IP_FIB_HASH=y
+# CONFIG_IP_FIB_TRIE is not set
CONFIG_IP_FIB_HASH=y
-CONFIG_IP_PNP=y
-CONFIG_IP_PNP_DHCP=y
-# CONFIG_IP_PNP_BOOTP is not set
-# CONFIG_IP_PNP_RARP is not set
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+# CONFIG_IP_PNP is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
-# CONFIG_IP_MROUTE is not set
+CONFIG_IP_MROUTE=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
# CONFIG_ARPD is not set
-# CONFIG_SYN_COOKIES is not set
+CONFIG_SYN_COOKIES=y
# CONFIG_INET_AH is not set
# CONFIG_INET_ESP is not set
# CONFIG_INET_IPCOMP is not set
@@ -313,31 +480,109 @@ CONFIG_INET_TUNNEL=y
# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
# CONFIG_INET_XFRM_MODE_TUNNEL is not set
# CONFIG_INET_XFRM_MODE_BEET is not set
-CONFIG_INET_DIAG=y
-CONFIG_INET_TCP_DIAG=y
-# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_INET_LRO=y
+# CONFIG_INET_DIAG is not set
+CONFIG_TCP_CONG_ADVANCED=y
+# CONFIG_TCP_CONG_BIC is not set
CONFIG_TCP_CONG_CUBIC=y
+# CONFIG_TCP_CONG_WESTWOOD is not set
+# CONFIG_TCP_CONG_HTCP is not set
+# CONFIG_TCP_CONG_HSTCP is not set
+# CONFIG_TCP_CONG_HYBLA is not set
+# CONFIG_TCP_CONG_VEGAS is not set
+# CONFIG_TCP_CONG_SCALABLE is not set
+# CONFIG_TCP_CONG_LP is not set
+# CONFIG_TCP_CONG_VENO is not set
+# CONFIG_TCP_CONG_YEAH is not set
+# CONFIG_TCP_CONG_ILLINOIS is not set
+# CONFIG_DEFAULT_BIC is not set
+CONFIG_DEFAULT_CUBIC=y
+# CONFIG_DEFAULT_HTCP is not set
+# CONFIG_DEFAULT_VEGAS is not set
+# CONFIG_DEFAULT_WESTWOOD is not set
+# CONFIG_DEFAULT_RENO is not set
CONFIG_DEFAULT_TCP_CONG="cubic"
-# CONFIG_TCP_MD5SIG is not set
+CONFIG_TCP_MD5SIG=y
+# CONFIG_IP_VS is not set
CONFIG_IPV6=y
# CONFIG_IPV6_PRIVACY is not set
# CONFIG_IPV6_ROUTER_PREF is not set
# CONFIG_IPV6_OPTIMISTIC_DAD is not set
-# CONFIG_INET6_AH is not set
-# CONFIG_INET6_ESP is not set
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
# CONFIG_INET6_IPCOMP is not set
# CONFIG_IPV6_MIP6 is not set
# CONFIG_INET6_XFRM_TUNNEL is not set
# CONFIG_INET6_TUNNEL is not set
-# CONFIG_INET6_XFRM_MODE_TRANSPORT is not set
-# CONFIG_INET6_XFRM_MODE_TUNNEL is not set
-# CONFIG_INET6_XFRM_MODE_BEET is not set
+CONFIG_INET6_XFRM_MODE_TRANSPORT=y
+CONFIG_INET6_XFRM_MODE_TUNNEL=y
+CONFIG_INET6_XFRM_MODE_BEET=y
# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
CONFIG_IPV6_SIT=y
+CONFIG_IPV6_NDISC_NODETYPE=y
# CONFIG_IPV6_TUNNEL is not set
# CONFIG_IPV6_MULTIPLE_TABLES is not set
-# CONFIG_NETWORK_SECMARK is not set
-# CONFIG_NETFILTER is not set
+# CONFIG_IPV6_MROUTE is not set
+CONFIG_NETLABEL=y
+CONFIG_NETWORK_SECMARK=y
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+# CONFIG_NETFILTER_ADVANCED is not set
+
+#
+# Core Netfilter Configuration
+#
+CONFIG_NETFILTER_NETLINK=y
+CONFIG_NETFILTER_NETLINK_LOG=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_SECMARK=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_SIP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_XTABLES=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_SECMARK=y
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=y
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_NF_CONNTRACK_PROC_COMPAT=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_TARGET_LOG=y
+CONFIG_IP_NF_TARGET_ULOG=y
+CONFIG_NF_NAT=y
+CONFIG_NF_NAT_NEEDED=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_NF_NAT_FTP=y
+CONFIG_NF_NAT_IRC=y
+# CONFIG_NF_NAT_TFTP is not set
+# CONFIG_NF_NAT_AMANDA is not set
+# CONFIG_NF_NAT_PPTP is not set
+# CONFIG_NF_NAT_H323 is not set
+CONFIG_NF_NAT_SIP=y
+CONFIG_IP_NF_MANGLE=y
+
+#
+# IPv6: Netfilter Configuration
+#
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_MATCH_IPV6HEADER=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_LOG=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_MANGLE=y
# CONFIG_IP_DCCP is not set
# CONFIG_IP_SCTP is not set
# CONFIG_TIPC is not set
@@ -345,6 +590,7 @@ CONFIG_IPV6_SIT=y
# CONFIG_BRIDGE is not set
# CONFIG_VLAN_8021Q is not set
# CONFIG_DECNET is not set
+CONFIG_LLC=y
# CONFIG_LLC2 is not set
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
@@ -352,28 +598,99 @@ CONFIG_IPV6_SIT=y
# CONFIG_LAPB is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
-
-#
-# QoS and/or fair queueing
-#
-# CONFIG_NET_SCHED is not set
+CONFIG_NET_SCHED=y
+
+#
+# Queueing/Scheduling
+#
+# CONFIG_NET_SCH_CBQ is not set
+# CONFIG_NET_SCH_HTB is not set
+# CONFIG_NET_SCH_HFSC is not set
+# CONFIG_NET_SCH_PRIO is not set
+# CONFIG_NET_SCH_RR is not set
+# CONFIG_NET_SCH_RED is not set
+# CONFIG_NET_SCH_SFQ is not set
+# CONFIG_NET_SCH_TEQL is not set
+# CONFIG_NET_SCH_TBF is not set
+# CONFIG_NET_SCH_GRED is not set
+# CONFIG_NET_SCH_DSMARK is not set
+# CONFIG_NET_SCH_NETEM is not set
+# CONFIG_NET_SCH_INGRESS is not set
+
+#
+# Classification
+#
+CONFIG_NET_CLS=y
+# CONFIG_NET_CLS_BASIC is not set
+# CONFIG_NET_CLS_TCINDEX is not set
+# CONFIG_NET_CLS_ROUTE4 is not set
+# CONFIG_NET_CLS_FW is not set
+# CONFIG_NET_CLS_U32 is not set
+# CONFIG_NET_CLS_RSVP is not set
+# CONFIG_NET_CLS_RSVP6 is not set
+# CONFIG_NET_CLS_FLOW is not set
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_STACK=32
+# CONFIG_NET_EMATCH_CMP is not set
+# CONFIG_NET_EMATCH_NBYTE is not set
+# CONFIG_NET_EMATCH_U32 is not set
+# CONFIG_NET_EMATCH_META is not set
+# CONFIG_NET_EMATCH_TEXT is not set
+CONFIG_NET_CLS_ACT=y
+# CONFIG_NET_ACT_POLICE is not set
+# CONFIG_NET_ACT_GACT is not set
+# CONFIG_NET_ACT_MIRRED is not set
+# CONFIG_NET_ACT_IPT is not set
+# CONFIG_NET_ACT_NAT is not set
+# CONFIG_NET_ACT_PEDIT is not set
+# CONFIG_NET_ACT_SIMP is not set
+CONFIG_NET_SCH_FIFO=y
#
# Network testing
#
# CONFIG_NET_PKTGEN is not set
# CONFIG_NET_TCPPROBE is not set
-# CONFIG_HAMRADIO is not set
+CONFIG_HAMRADIO=y
+
+#
+# Packet Radio protocols
+#
+# CONFIG_AX25 is not set
+# CONFIG_CAN is not set
# CONFIG_IRDA is not set
# CONFIG_BT is not set
# CONFIG_AF_RXRPC is not set
+CONFIG_FIB_RULES=y
#
# Wireless
#
-# CONFIG_CFG80211 is not set
-# CONFIG_WIRELESS_EXT is not set
-# CONFIG_MAC80211 is not set
+CONFIG_CFG80211=y
+CONFIG_NL80211=y
+CONFIG_WIRELESS_EXT=y
+CONFIG_MAC80211=y
+
+#
+# Rate control algorithm selection
+#
+CONFIG_MAC80211_RC_DEFAULT_PID=y
+# CONFIG_MAC80211_RC_DEFAULT_NONE is not set
+
+#
+# Selecting 'y' for an algorithm will
+#
+
+#
+# build the algorithm into mac80211.
+#
+CONFIG_MAC80211_RC_DEFAULT="pid"
+CONFIG_MAC80211_RC_PID=y
+# CONFIG_MAC80211_MESH is not set
+CONFIG_MAC80211_LEDS=y
+# CONFIG_MAC80211_DEBUGFS is not set
+# CONFIG_MAC80211_DEBUG_PACKET_ALIGNMENT is not set
+# CONFIG_MAC80211_DEBUG is not set
# CONFIG_IEEE80211 is not set
# CONFIG_RFKILL is not set
# CONFIG_NET_9P is not set
@@ -385,13 +702,15 @@ CONFIG_IPV6_SIT=y
#
# Generic Driver Options
#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_STANDALONE=y
CONFIG_PREVENT_FIRMWARE_BUILD=y
CONFIG_FW_LOADER=y
# CONFIG_DEBUG_DRIVER is not set
-# CONFIG_DEBUG_DEVRES is not set
+CONFIG_DEBUG_DEVRES=y
# CONFIG_SYS_HYPERVISOR is not set
-# CONFIG_CONNECTOR is not set
+CONFIG_CONNECTOR=y
+CONFIG_PROC_EVENTS=y
# CONFIG_MTD is not set
# CONFIG_PARPORT is not set
CONFIG_PNP=y
@@ -402,7 +721,7 @@ CONFIG_PNP=y
#
CONFIG_PNPACPI=y
CONFIG_BLK_DEV=y
-CONFIG_BLK_DEV_FD=y
+# CONFIG_BLK_DEV_FD is not set
# CONFIG_BLK_CPQ_DA is not set
# CONFIG_BLK_CPQ_CISS_DA is not set
# CONFIG_BLK_DEV_DAC960 is not set
@@ -415,8 +734,8 @@ CONFIG_BLK_DEV_LOOP=y
# CONFIG_BLK_DEV_UB is not set
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_COUNT=16
-CONFIG_BLK_DEV_RAM_SIZE=4096
-CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
+CONFIG_BLK_DEV_RAM_SIZE=16384
+# CONFIG_BLK_DEV_XIP is not set
# CONFIG_CDROM_PKTCDVD is not set
# CONFIG_ATA_OVER_ETH is not set
CONFIG_MISC_DEVICES=y
@@ -425,72 +744,16 @@ CONFIG_MISC_DEVICES=y
# CONFIG_EEPROM_93CX6 is not set
# CONFIG_SGI_IOC4 is not set
# CONFIG_TIFM_CORE is not set
+# CONFIG_ACER_WMI is not set
+# CONFIG_ASUS_LAPTOP is not set
+# CONFIG_FUJITSU_LAPTOP is not set
+# CONFIG_MSI_LAPTOP is not set
# CONFIG_SONY_LAPTOP is not set
# CONFIG_THINKPAD_ACPI is not set
-CONFIG_IDE=y
-CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
-# CONFIG_BLK_DEV_IDE_SATA is not set
-# CONFIG_BLK_DEV_HD_IDE is not set
-CONFIG_BLK_DEV_IDEDISK=y
-CONFIG_IDEDISK_MULTI_MODE=y
-CONFIG_BLK_DEV_IDECD=y
-# CONFIG_BLK_DEV_IDETAPE is not set
-# CONFIG_BLK_DEV_IDEFLOPPY is not set
-# CONFIG_BLK_DEV_IDESCSI is not set
-CONFIG_BLK_DEV_IDEACPI=y
-# CONFIG_IDE_TASK_IOCTL is not set
-CONFIG_IDE_PROC_FS=y
-
-#
-# IDE chipset support/bugfixes
-#
-CONFIG_IDE_GENERIC=y
-# CONFIG_BLK_DEV_CMD640 is not set
-# CONFIG_BLK_DEV_IDEPNP is not set
-CONFIG_BLK_DEV_IDEPCI=y
-# CONFIG_IDEPCI_SHARE_IRQ is not set
-CONFIG_IDEPCI_PCIBUS_ORDER=y
-# CONFIG_BLK_DEV_OFFBOARD is not set
-# CONFIG_BLK_DEV_GENERIC is not set
-# CONFIG_BLK_DEV_OPTI621 is not set
-# CONFIG_BLK_DEV_RZ1000 is not set
-CONFIG_BLK_DEV_IDEDMA_PCI=y
-# CONFIG_BLK_DEV_IDEDMA_FORCED is not set
-# CONFIG_IDEDMA_ONLYDISK is not set
-# CONFIG_BLK_DEV_AEC62XX is not set
-# CONFIG_BLK_DEV_ALI15X3 is not set
-CONFIG_BLK_DEV_AMD74XX=y
-CONFIG_BLK_DEV_ATIIXP=y
-# CONFIG_BLK_DEV_CMD64X is not set
-# CONFIG_BLK_DEV_TRIFLEX is not set
-# CONFIG_BLK_DEV_CY82C693 is not set
-# CONFIG_BLK_DEV_CS5520 is not set
-# CONFIG_BLK_DEV_CS5530 is not set
-# CONFIG_BLK_DEV_HPT34X is not set
-# CONFIG_BLK_DEV_HPT366 is not set
-# CONFIG_BLK_DEV_JMICRON is not set
-# CONFIG_BLK_DEV_SC1200 is not set
-CONFIG_BLK_DEV_PIIX=y
-# CONFIG_BLK_DEV_IT8213 is not set
-# CONFIG_BLK_DEV_IT821X is not set
-# CONFIG_BLK_DEV_NS87415 is not set
-# CONFIG_BLK_DEV_PDC202XX_OLD is not set
-CONFIG_BLK_DEV_PDC202XX_NEW=y
-# CONFIG_BLK_DEV_SVWKS is not set
-# CONFIG_BLK_DEV_SIIMAGE is not set
-# CONFIG_BLK_DEV_SIS5513 is not set
-# CONFIG_BLK_DEV_SLC90E66 is not set
-# CONFIG_BLK_DEV_TRM290 is not set
-# CONFIG_BLK_DEV_VIA82CXXX is not set
-# CONFIG_BLK_DEV_TC86C001 is not set
-# CONFIG_IDE_ARM is not set
-CONFIG_BLK_DEV_IDEDMA=y
-# CONFIG_IDEDMA_IVB is not set
-# CONFIG_BLK_DEV_HD is not set
+# CONFIG_INTEL_MENLOW is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
#
# SCSI device support
@@ -499,8 +762,8 @@ CONFIG_BLK_DEV_IDEDMA=y
CONFIG_SCSI=y
CONFIG_SCSI_DMA=y
# CONFIG_SCSI_TGT is not set
-CONFIG_SCSI_NETLINK=y
-# CONFIG_SCSI_PROC_FS is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
#
# SCSI support type (disk, tape, CD-ROM)
@@ -509,7 +772,7 @@ CONFIG_BLK_DEV_SD=y
# CONFIG_CHR_DEV_ST is not set
# CONFIG_CHR_DEV_OSST is not set
CONFIG_BLK_DEV_SR=y
-# CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_BLK_DEV_SR_VENDOR=y
CONFIG_CHR_DEV_SG=y
# CONFIG_CHR_DEV_SCH is not set
@@ -526,73 +789,37 @@ CONFIG_SCSI_WAIT_SCAN=m
# SCSI Transports
#
CONFIG_SCSI_SPI_ATTRS=y
-CONFIG_SCSI_FC_ATTRS=y
+# CONFIG_SCSI_FC_ATTRS is not set
# CONFIG_SCSI_ISCSI_ATTRS is not set
-CONFIG_SCSI_SAS_ATTRS=y
+# CONFIG_SCSI_SAS_ATTRS is not set
# CONFIG_SCSI_SAS_LIBSAS is not set
-
-#
-# SCSI low-level drivers
-#
-# CONFIG_ISCSI_TCP is not set
-# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
-# CONFIG_SCSI_3W_9XXX is not set
-# CONFIG_SCSI_ACARD is not set
-# CONFIG_SCSI_AACRAID is not set
-# CONFIG_SCSI_AIC7XXX is not set
-# CONFIG_SCSI_AIC7XXX_OLD is not set
-CONFIG_SCSI_AIC79XX=y
-CONFIG_AIC79XX_CMDS_PER_DEVICE=32
-CONFIG_AIC79XX_RESET_DELAY_MS=4000
-# CONFIG_AIC79XX_DEBUG_ENABLE is not set
-CONFIG_AIC79XX_DEBUG_MASK=0
-# CONFIG_AIC79XX_REG_PRETTY_PRINT is not set
-# CONFIG_SCSI_AIC94XX is not set
-# CONFIG_SCSI_ARCMSR is not set
-# CONFIG_MEGARAID_NEWGEN is not set
-# CONFIG_MEGARAID_LEGACY is not set
-# CONFIG_MEGARAID_SAS is not set
-# CONFIG_SCSI_HPTIOP is not set
-# CONFIG_SCSI_BUSLOGIC is not set
-# CONFIG_SCSI_DMX3191D is not set
-# CONFIG_SCSI_EATA is not set
-# CONFIG_SCSI_FUTURE_DOMAIN is not set
-# CONFIG_SCSI_GDTH is not set
-# CONFIG_SCSI_IPS is not set
-# CONFIG_SCSI_INITIO is not set
-# CONFIG_SCSI_INIA100 is not set
-# CONFIG_SCSI_STEX is not set
-# CONFIG_SCSI_SYM53C8XX_2 is not set
-# CONFIG_SCSI_IPR is not set
-# CONFIG_SCSI_QLOGIC_1280 is not set
-# CONFIG_SCSI_QLA_FC is not set
-# CONFIG_SCSI_QLA_ISCSI is not set
-# CONFIG_SCSI_LPFC is not set
-# CONFIG_SCSI_DC395x is not set
-# CONFIG_SCSI_DC390T is not set
-# CONFIG_SCSI_DEBUG is not set
-# CONFIG_SCSI_SRP is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+# CONFIG_SCSI_LOWLEVEL is not set
+# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set
CONFIG_ATA=y
# CONFIG_ATA_NONSTANDARD is not set
CONFIG_ATA_ACPI=y
+CONFIG_SATA_PMP=y
CONFIG_SATA_AHCI=y
-CONFIG_SATA_SVW=y
+# CONFIG_SATA_SIL24 is not set
+CONFIG_ATA_SFF=y
+# CONFIG_SATA_SVW is not set
CONFIG_ATA_PIIX=y
# CONFIG_SATA_MV is not set
-CONFIG_SATA_NV=y
+# CONFIG_SATA_NV is not set
# CONFIG_PDC_ADMA is not set
# CONFIG_SATA_QSTOR is not set
# CONFIG_SATA_PROMISE is not set
# CONFIG_SATA_SX4 is not set
-CONFIG_SATA_SIL=y
-# CONFIG_SATA_SIL24 is not set
+# CONFIG_SATA_SIL is not set
# CONFIG_SATA_SIS is not set
# CONFIG_SATA_ULI is not set
-CONFIG_SATA_VIA=y
+# CONFIG_SATA_VIA is not set
# CONFIG_SATA_VITESSE is not set
# CONFIG_SATA_INIC162X is not set
+# CONFIG_PATA_ACPI is not set
# CONFIG_PATA_ALI is not set
-# CONFIG_PATA_AMD is not set
+CONFIG_PATA_AMD=y
# CONFIG_PATA_ARTOP is not set
# CONFIG_PATA_ATIIXP is not set
# CONFIG_PATA_CMD640_PCI is not set
@@ -612,11 +839,14 @@ CONFIG_SATA_VIA=y
# CONFIG_PATA_TRIFLEX is not set
# CONFIG_PATA_MARVELL is not set
# CONFIG_PATA_MPIIX is not set
-# CONFIG_PATA_OLDPIIX is not set
+CONFIG_PATA_OLDPIIX=y
# CONFIG_PATA_NETCELL is not set
+# CONFIG_PATA_NINJA32 is not set
# CONFIG_PATA_NS87410 is not set
+# CONFIG_PATA_NS87415 is not set
# CONFIG_PATA_OPTI is not set
# CONFIG_PATA_OPTIDMA is not set
+# CONFIG_PATA_PCMCIA is not set
# CONFIG_PATA_PDC_OLD is not set
# CONFIG_PATA_RADISYS is not set
# CONFIG_PATA_RZ1000 is not set
@@ -628,65 +858,42 @@ CONFIG_SATA_VIA=y
# CONFIG_PATA_VIA is not set
# CONFIG_PATA_WINBOND is not set
CONFIG_MD=y
-# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_MD=y
+# CONFIG_MD_LINEAR is not set
+# CONFIG_MD_RAID0 is not set
+# CONFIG_MD_RAID1 is not set
+# CONFIG_MD_RAID10 is not set
+# CONFIG_MD_RAID456 is not set
+# CONFIG_MD_MULTIPATH is not set
+# CONFIG_MD_FAULTY is not set
CONFIG_BLK_DEV_DM=y
# CONFIG_DM_DEBUG is not set
# CONFIG_DM_CRYPT is not set
# CONFIG_DM_SNAPSHOT is not set
-# CONFIG_DM_MIRROR is not set
-# CONFIG_DM_ZERO is not set
+CONFIG_DM_MIRROR=y
+CONFIG_DM_ZERO=y
# CONFIG_DM_MULTIPATH is not set
# CONFIG_DM_DELAY is not set
-
-#
-# Fusion MPT device support
-#
-CONFIG_FUSION=y
-CONFIG_FUSION_SPI=y
-# CONFIG_FUSION_FC is not set
-# CONFIG_FUSION_SAS is not set
-CONFIG_FUSION_MAX_SGE=128
-# CONFIG_FUSION_CTL is not set
+# CONFIG_DM_UEVENT is not set
+# CONFIG_FUSION is not set
#
# IEEE 1394 (FireWire) support
#
# CONFIG_FIREWIRE is not set
-CONFIG_IEEE1394=y
-
-#
-# Subsystem Options
-#
-# CONFIG_IEEE1394_VERBOSEDEBUG is not set
-
-#
-# Controllers
-#
-
-#
-# Texas Instruments PCILynx requires I2C
-#
-CONFIG_IEEE1394_OHCI1394=y
-
-#
-# Protocols
-#
-# CONFIG_IEEE1394_VIDEO1394 is not set
-# CONFIG_IEEE1394_SBP2 is not set
-# CONFIG_IEEE1394_ETH1394_ROM_ENTRY is not set
-# CONFIG_IEEE1394_ETH1394 is not set
-# CONFIG_IEEE1394_DV1394 is not set
-CONFIG_IEEE1394_RAWIO=y
+# CONFIG_IEEE1394 is not set
# CONFIG_I2O is not set
CONFIG_MACINTOSH_DRIVERS=y
-# CONFIG_MAC_EMUMOUSEBTN is not set
+CONFIG_MAC_EMUMOUSEBTN=y
CONFIG_NETDEVICES=y
-CONFIG_NETDEVICES_MULTIQUEUE=y
+# CONFIG_NETDEVICES_MULTIQUEUE is not set
+# CONFIG_IFB is not set
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_MACVLAN is not set
# CONFIG_EQUALIZER is not set
-CONFIG_TUN=y
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
# CONFIG_NET_SB1000 is not set
# CONFIG_ARCNET is not set
# CONFIG_PHYLIB is not set
@@ -696,39 +903,40 @@ CONFIG_MII=y
# CONFIG_SUNGEM is not set
# CONFIG_CASSINI is not set
CONFIG_NET_VENDOR_3COM=y
-CONFIG_VORTEX=y
+# CONFIG_VORTEX is not set
# CONFIG_TYPHOON is not set
CONFIG_NET_TULIP=y
# CONFIG_DE2104X is not set
-CONFIG_TULIP=y
-# CONFIG_TULIP_MWI is not set
-# CONFIG_TULIP_MMIO is not set
-# CONFIG_TULIP_NAPI is not set
+# CONFIG_TULIP is not set
# CONFIG_DE4X5 is not set
# CONFIG_WINBOND_840 is not set
# CONFIG_DM9102 is not set
# CONFIG_ULI526X is not set
+# CONFIG_PCMCIA_XIRCOM is not set
# CONFIG_HP100 is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
CONFIG_NET_PCI=y
# CONFIG_PCNET32 is not set
-CONFIG_AMD8111_ETH=y
-# CONFIG_AMD8111E_NAPI is not set
+# CONFIG_AMD8111_ETH is not set
# CONFIG_ADAPTEC_STARFIRE is not set
-CONFIG_B44=y
+# CONFIG_B44 is not set
CONFIG_FORCEDETH=y
# CONFIG_FORCEDETH_NAPI is not set
-# CONFIG_DGRS is not set
# CONFIG_EEPRO100 is not set
CONFIG_E100=y
# CONFIG_FEALNX is not set
# CONFIG_NATSEMI is not set
# CONFIG_NE2K_PCI is not set
-CONFIG_8139CP=y
+# CONFIG_8139CP is not set
CONFIG_8139TOO=y
-# CONFIG_8139TOO_PIO is not set
+CONFIG_8139TOO_PIO=y
# CONFIG_8139TOO_TUNE_TWISTER is not set
# CONFIG_8139TOO_8129 is not set
# CONFIG_8139_OLD_RX_RESET is not set
+# CONFIG_R6040 is not set
# CONFIG_SIS900 is not set
# CONFIG_EPIC100 is not set
# CONFIG_SUNDANCE is not set
@@ -740,34 +948,74 @@ CONFIG_NETDEV_1000=y
CONFIG_E1000=y
# CONFIG_E1000_NAPI is not set
# CONFIG_E1000_DISABLE_PACKET_SPLIT is not set
+# CONFIG_E1000E is not set
+# CONFIG_E1000E_ENABLED is not set
+# CONFIG_IP1000 is not set
+# CONFIG_IGB is not set
# CONFIG_NS83820 is not set
# CONFIG_HAMACHI is not set
# CONFIG_YELLOWFIN is not set
# CONFIG_R8169 is not set
# CONFIG_SIS190 is not set
# CONFIG_SKGE is not set
-# CONFIG_SKY2 is not set
+CONFIG_SKY2=y
+# CONFIG_SKY2_DEBUG is not set
# CONFIG_VIA_VELOCITY is not set
CONFIG_TIGON3=y
-CONFIG_BNX2=y
+# CONFIG_BNX2 is not set
# CONFIG_QLA3XXX is not set
# CONFIG_ATL1 is not set
CONFIG_NETDEV_10000=y
# CONFIG_CHELSIO_T1 is not set
# CONFIG_CHELSIO_T3 is not set
+# CONFIG_IXGBE is not set
# CONFIG_IXGB is not set
-CONFIG_S2IO=m
-# CONFIG_S2IO_NAPI is not set
+# CONFIG_S2IO is not set
# CONFIG_MYRI10GE is not set
# CONFIG_NETXEN_NIC is not set
+# CONFIG_NIU is not set
# CONFIG_MLX4_CORE is not set
-# CONFIG_TR is not set
+# CONFIG_TEHUTI is not set
+# CONFIG_BNX2X is not set
+# CONFIG_SFC is not set
+CONFIG_TR=y
+# CONFIG_IBMOL is not set
+# CONFIG_3C359 is not set
+# CONFIG_TMS380TR is not set
#
# Wireless LAN
#
# CONFIG_WLAN_PRE80211 is not set
-# CONFIG_WLAN_80211 is not set
+CONFIG_WLAN_80211=y
+# CONFIG_PCMCIA_RAYCS is not set
+# CONFIG_IPW2100 is not set
+# CONFIG_IPW2200 is not set
+# CONFIG_LIBERTAS is not set
+# CONFIG_AIRO is not set
+# CONFIG_HERMES is not set
+# CONFIG_ATMEL is not set
+# CONFIG_AIRO_CS is not set
+# CONFIG_PCMCIA_WL3501 is not set
+# CONFIG_PRISM54 is not set
+# CONFIG_USB_ZD1201 is not set
+# CONFIG_USB_NET_RNDIS_WLAN is not set
+# CONFIG_RTL8180 is not set
+# CONFIG_RTL8187 is not set
+# CONFIG_ADM8211 is not set
+# CONFIG_P54_COMMON is not set
+CONFIG_ATH5K=y
+# CONFIG_ATH5K_DEBUG is not set
+# CONFIG_IWLWIFI is not set
+# CONFIG_IWLCORE is not set
+# CONFIG_IWLWIFI_LEDS is not set
+# CONFIG_IWL4965 is not set
+# CONFIG_IWL3945 is not set
+# CONFIG_HOSTAP is not set
+# CONFIG_B43 is not set
+# CONFIG_B43LEGACY is not set
+# CONFIG_ZD1211RW is not set
+# CONFIG_RT2X00 is not set
#
# USB Network Adapters
@@ -776,16 +1024,26 @@ CONFIG_S2IO=m
# CONFIG_USB_KAWETH is not set
# CONFIG_USB_PEGASUS is not set
# CONFIG_USB_RTL8150 is not set
-# CONFIG_USB_USBNET_MII is not set
# CONFIG_USB_USBNET is not set
+CONFIG_NET_PCMCIA=y
+# CONFIG_PCMCIA_3C589 is not set
+# CONFIG_PCMCIA_3C574 is not set
+# CONFIG_PCMCIA_FMVJ18X is not set
+# CONFIG_PCMCIA_PCNET is not set
+# CONFIG_PCMCIA_NMCLAN is not set
+# CONFIG_PCMCIA_SMC91C92 is not set
+# CONFIG_PCMCIA_XIRC2PS is not set
+# CONFIG_PCMCIA_AXNET is not set
# CONFIG_WAN is not set
-# CONFIG_FDDI is not set
+CONFIG_FDDI=y
+# CONFIG_DEFXX is not set
+# CONFIG_SKFP is not set
# CONFIG_HIPPI is not set
# CONFIG_PPP is not set
# CONFIG_SLIP is not set
# CONFIG_NET_FC is not set
-# CONFIG_SHAPER is not set
CONFIG_NETCONSOLE=y
+# CONFIG_NETCONSOLE_DYNAMIC is not set
CONFIG_NETPOLL=y
# CONFIG_NETPOLL_TRAP is not set
CONFIG_NET_POLL_CONTROLLER=y
@@ -796,18 +1054,17 @@ CONFIG_NET_POLL_CONTROLLER=y
# Input device support
#
CONFIG_INPUT=y
-# CONFIG_INPUT_FF_MEMLESS is not set
-# CONFIG_INPUT_POLLDEV is not set
+CONFIG_INPUT_FF_MEMLESS=y
+CONFIG_INPUT_POLLDEV=y
#
# Userland interfaces
#
CONFIG_INPUT_MOUSEDEV=y
-CONFIG_INPUT_MOUSEDEV_PSAUX=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
# CONFIG_INPUT_JOYDEV is not set
-# CONFIG_INPUT_TSDEV is not set
CONFIG_INPUT_EVDEV=y
# CONFIG_INPUT_EVBUG is not set
@@ -832,17 +1089,62 @@ CONFIG_MOUSE_PS2_TRACKPOINT=y
# CONFIG_MOUSE_SERIAL is not set
# CONFIG_MOUSE_APPLETOUCH is not set
# CONFIG_MOUSE_VSXXXAA is not set
-# CONFIG_INPUT_JOYSTICK is not set
-# CONFIG_INPUT_TABLET is not set
-# CONFIG_INPUT_TOUCHSCREEN is not set
-# CONFIG_INPUT_MISC is not set
+CONFIG_INPUT_JOYSTICK=y
+# CONFIG_JOYSTICK_ANALOG is not set
+# CONFIG_JOYSTICK_A3D is not set
+# CONFIG_JOYSTICK_ADI is not set
+# CONFIG_JOYSTICK_COBRA is not set
+# CONFIG_JOYSTICK_GF2K is not set
+# CONFIG_JOYSTICK_GRIP is not set
+# CONFIG_JOYSTICK_GRIP_MP is not set
+# CONFIG_JOYSTICK_GUILLEMOT is not set
+# CONFIG_JOYSTICK_INTERACT is not set
+# CONFIG_JOYSTICK_SIDEWINDER is not set
+# CONFIG_JOYSTICK_TMDC is not set
+# CONFIG_JOYSTICK_IFORCE is not set
+# CONFIG_JOYSTICK_WARRIOR is not set
+# CONFIG_JOYSTICK_MAGELLAN is not set
+# CONFIG_JOYSTICK_SPACEORB is not set
+# CONFIG_JOYSTICK_SPACEBALL is not set
+# CONFIG_JOYSTICK_STINGER is not set
+# CONFIG_JOYSTICK_TWIDJOY is not set
+# CONFIG_JOYSTICK_ZHENHUA is not set
+# CONFIG_JOYSTICK_JOYDUMP is not set
+# CONFIG_JOYSTICK_XPAD is not set
+CONFIG_INPUT_TABLET=y
+# CONFIG_TABLET_USB_ACECAD is not set
+# CONFIG_TABLET_USB_AIPTEK is not set
+# CONFIG_TABLET_USB_GTCO is not set
+# CONFIG_TABLET_USB_KBTAB is not set
+# CONFIG_TABLET_USB_WACOM is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_UCB1400 is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_PCSPKR is not set
+# CONFIG_INPUT_APANEL is not set
+# CONFIG_INPUT_ATLAS_BTNS is not set
+# CONFIG_INPUT_ATI_REMOTE is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+# CONFIG_INPUT_UINPUT is not set
#
# Hardware I/O ports
#
CONFIG_SERIO=y
CONFIG_SERIO_I8042=y
-# CONFIG_SERIO_SERPORT is not set
+CONFIG_SERIO_SERPORT=y
# CONFIG_SERIO_CT82C710 is not set
# CONFIG_SERIO_PCIPS2 is not set
CONFIG_SERIO_LIBPS2=y
@@ -855,8 +1157,26 @@ CONFIG_SERIO_LIBPS2=y
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
CONFIG_HW_CONSOLE=y
-# CONFIG_VT_HW_CONSOLE_BINDING is not set
-# CONFIG_SERIAL_NONSTANDARD is not set
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_DEVKMEM=y
+CONFIG_SERIAL_NONSTANDARD=y
+# CONFIG_COMPUTONE is not set
+# CONFIG_ROCKETPORT is not set
+# CONFIG_CYCLADES is not set
+# CONFIG_DIGIEPCA is not set
+# CONFIG_MOXA_INTELLIO is not set
+# CONFIG_MOXA_SMARTIO is not set
+# CONFIG_ISI is not set
+# CONFIG_SYNCLINK is not set
+# CONFIG_SYNCLINKMP is not set
+# CONFIG_SYNCLINK_GT is not set
+# CONFIG_N_HDLC is not set
+# CONFIG_RISCOM8 is not set
+# CONFIG_SPECIALIX is not set
+# CONFIG_SX is not set
+# CONFIG_RIO is not set
+# CONFIG_STALDRV is not set
+# CONFIG_NOZOMI is not set
#
# Serial drivers
@@ -866,9 +1186,14 @@ CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_FIX_EARLYCON_MEM=y
CONFIG_SERIAL_8250_PCI=y
CONFIG_SERIAL_8250_PNP=y
-CONFIG_SERIAL_8250_NR_UARTS=4
+# CONFIG_SERIAL_8250_CS is not set
+CONFIG_SERIAL_8250_NR_UARTS=32
CONFIG_SERIAL_8250_RUNTIME_UARTS=4
-# CONFIG_SERIAL_8250_EXTENDED is not set
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250_DETECT_IRQ=y
+CONFIG_SERIAL_8250_RSA=y
#
# Non-8250 serial port support
@@ -877,78 +1202,260 @@ CONFIG_SERIAL_CORE=y
CONFIG_SERIAL_CORE_CONSOLE=y
# CONFIG_SERIAL_JSM is not set
CONFIG_UNIX98_PTYS=y
-CONFIG_LEGACY_PTYS=y
-CONFIG_LEGACY_PTY_COUNT=256
+# CONFIG_LEGACY_PTYS is not set
# CONFIG_IPMI_HANDLER is not set
-# CONFIG_WATCHDOG is not set
CONFIG_HW_RANDOM=y
-CONFIG_HW_RANDOM_INTEL=y
-CONFIG_HW_RANDOM_AMD=y
-# CONFIG_NVRAM is not set
-CONFIG_RTC=y
+# CONFIG_HW_RANDOM_INTEL is not set
+# CONFIG_HW_RANDOM_AMD is not set
+CONFIG_NVRAM=y
# CONFIG_R3964 is not set
# CONFIG_APPLICOM is not set
-CONFIG_AGP=y
-CONFIG_AGP_AMD64=y
-CONFIG_AGP_INTEL=y
-# CONFIG_AGP_SIS is not set
-# CONFIG_AGP_VIA is not set
-# CONFIG_DRM is not set
+
+#
+# PCMCIA character devices
+#
+# CONFIG_SYNCLINK_CS is not set
+# CONFIG_CARDMAN_4000 is not set
+# CONFIG_CARDMAN_4040 is not set
+# CONFIG_IPWIRELESS is not set
# CONFIG_MWAVE is not set
# CONFIG_PC8736x_GPIO is not set
-CONFIG_RAW_DRIVER=y
-CONFIG_MAX_RAW_DEVS=256
+# CONFIG_RAW_DRIVER is not set
CONFIG_HPET=y
# CONFIG_HPET_RTC_IRQ is not set
-CONFIG_HPET_MMAP=y
+# CONFIG_HPET_MMAP is not set
# CONFIG_HANGCHECK_TIMER is not set
# CONFIG_TCG_TPM is not set
# CONFIG_TELCLOCK is not set
CONFIG_DEVPORT=y
-# CONFIG_I2C is not set
-
-#
-# SPI support
-#
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_CHARDEV is not set
+
+#
+# I2C Hardware Bus support
+#
+# CONFIG_I2C_ALI1535 is not set
+# CONFIG_I2C_ALI1563 is not set
+# CONFIG_I2C_ALI15X3 is not set
+# CONFIG_I2C_AMD756 is not set
+# CONFIG_I2C_AMD8111 is not set
+CONFIG_I2C_I801=y
+# CONFIG_I2C_I810 is not set
+# CONFIG_I2C_PIIX4 is not set
+# CONFIG_I2C_NFORCE2 is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_PROSAVAGE is not set
+# CONFIG_I2C_SAVAGE4 is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_SIS5595 is not set
+# CONFIG_I2C_SIS630 is not set
+# CONFIG_I2C_SIS96X is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_TINY_USB is not set
+# CONFIG_I2C_VIA is not set
+# CONFIG_I2C_VIAPRO is not set
+# CONFIG_I2C_VOODOO3 is not set
+# CONFIG_I2C_PCA_PLATFORM is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
# CONFIG_SPI is not set
-# CONFIG_SPI_MASTER is not set
# CONFIG_W1 is not set
-# CONFIG_POWER_SUPPLY is not set
+CONFIG_POWER_SUPPLY=y
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+# CONFIG_PDA_POWER is not set
+# CONFIG_BATTERY_DS2760 is not set
# CONFIG_HWMON is not set
+CONFIG_THERMAL=y
+CONFIG_WATCHDOG=y
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+
+#
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+# CONFIG_ACQUIRE_WDT is not set
+# CONFIG_ADVANTECH_WDT is not set
+# CONFIG_ALIM1535_WDT is not set
+# CONFIG_ALIM7101_WDT is not set
+# CONFIG_SC520_WDT is not set
+# CONFIG_EUROTECH_WDT is not set
+# CONFIG_IB700_WDT is not set
+# CONFIG_IBMASR is not set
+# CONFIG_WAFER_WDT is not set
+# CONFIG_I6300ESB_WDT is not set
+# CONFIG_ITCO_WDT is not set
+# CONFIG_IT8712F_WDT is not set
+# CONFIG_HP_WATCHDOG is not set
+# CONFIG_SC1200_WDT is not set
+# CONFIG_PC87413_WDT is not set
+# CONFIG_60XX_WDT is not set
+# CONFIG_SBC8360_WDT is not set
+# CONFIG_CPU5_WDT is not set
+# CONFIG_SMSC37B787_WDT is not set
+# CONFIG_W83627HF_WDT is not set
+# CONFIG_W83697HF_WDT is not set
+# CONFIG_W83877F_WDT is not set
+# CONFIG_W83977F_WDT is not set
+# CONFIG_MACHZ_WDT is not set
+# CONFIG_SBC_EPX_C3_WATCHDOG is not set
+
+#
+# PCI-based Watchdog Cards
+#
+# CONFIG_PCIPCWATCHDOG is not set
+# CONFIG_WDTPCI is not set
+
+#
+# USB-based Watchdog Cards
+#
+# CONFIG_USBPCWATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
#
# Multifunction device drivers
#
# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_PASIC3 is not set
#
# Multimedia devices
#
+
+#
+# Multimedia core support
+#
# CONFIG_VIDEO_DEV is not set
# CONFIG_DVB_CORE is not set
+
+#
+# Multimedia drivers
+#
CONFIG_DAB=y
# CONFIG_USB_DABUSB is not set
#
# Graphics support
#
-# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+CONFIG_AGP=y
+CONFIG_AGP_AMD64=y
+CONFIG_AGP_INTEL=y
+# CONFIG_AGP_SIS is not set
+# CONFIG_AGP_VIA is not set
+CONFIG_DRM=y
+# CONFIG_DRM_TDFX is not set
+# CONFIG_DRM_R128 is not set
+# CONFIG_DRM_RADEON is not set
+# CONFIG_DRM_I810 is not set
+# CONFIG_DRM_I830 is not set
+CONFIG_DRM_I915=y
+# CONFIG_DRM_MGA is not set
+# CONFIG_DRM_SIS is not set
+# CONFIG_DRM_VIA is not set
+# CONFIG_DRM_SAVAGE is not set
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+CONFIG_FB_DEFERRED_IO=y
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+CONFIG_FB_MODE_HELPERS=y
+CONFIG_FB_TILEBLITTING=y
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_CIRRUS is not set
+# CONFIG_FB_PM2 is not set
+# CONFIG_FB_CYBER2000 is not set
+# CONFIG_FB_ARC is not set
+# CONFIG_FB_ASILIANT is not set
+# CONFIG_FB_IMSTT is not set
+# CONFIG_FB_VGA16 is not set
+# CONFIG_FB_UVESA is not set
+# CONFIG_FB_VESA is not set
+CONFIG_FB_EFI=y
+# CONFIG_FB_IMAC is not set
+# CONFIG_FB_N411 is not set
+# CONFIG_FB_HGA is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_NVIDIA is not set
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_LE80578 is not set
+# CONFIG_FB_INTEL is not set
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_RADEON is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_S3 is not set
+# CONFIG_FB_SAVAGE is not set
+# CONFIG_FB_SIS is not set
+# CONFIG_FB_NEOMAGIC is not set
+# CONFIG_FB_KYRO is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_VT8623 is not set
+# CONFIG_FB_TRIDENT is not set
+# CONFIG_FB_ARK is not set
+# CONFIG_FB_PM3 is not set
+# CONFIG_FB_GEODE is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CORGI is not set
+# CONFIG_BACKLIGHT_PROGEAR is not set
#
# Display device support
#
# CONFIG_DISPLAY_SUPPORT is not set
-# CONFIG_VGASTATE is not set
-# CONFIG_FB is not set
#
# Console display driver support
#
CONFIG_VGA_CONSOLE=y
CONFIG_VGACON_SOFT_SCROLLBACK=y
-CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=256
+CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=64
CONFIG_VIDEO_SELECT=y
CONFIG_DUMMY_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE is not set
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_LOGO_LINUX_CLUT224=y
#
# Sound
@@ -958,33 +1465,165 @@ CONFIG_SOUND=y
#
# Advanced Linux Sound Architecture
#
-# CONFIG_SND is not set
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+CONFIG_SND_HWDEP=y
+CONFIG_SND_SEQUENCER=y
+CONFIG_SND_SEQ_DUMMY=y
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=y
+CONFIG_SND_PCM_OSS=y
+CONFIG_SND_PCM_OSS_PLUGINS=y
+CONFIG_SND_SEQUENCER_OSS=y
+CONFIG_SND_DYNAMIC_MINORS=y
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+CONFIG_SND_VMASTER=y
+
+#
+# Generic devices
+#
+# CONFIG_SND_PCSP is not set
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_VIRMIDI is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+
+#
+# PCI devices
+#
+# CONFIG_SND_AD1889 is not set
+# CONFIG_SND_ALS300 is not set
+# CONFIG_SND_ALS4000 is not set
+# CONFIG_SND_ALI5451 is not set
+# CONFIG_SND_ATIIXP is not set
+# CONFIG_SND_ATIIXP_MODEM is not set
+# CONFIG_SND_AU8810 is not set
+# CONFIG_SND_AU8820 is not set
+# CONFIG_SND_AU8830 is not set
+# CONFIG_SND_AW2 is not set
+# CONFIG_SND_AZT3328 is not set
+# CONFIG_SND_BT87X is not set
+# CONFIG_SND_CA0106 is not set
+# CONFIG_SND_CMIPCI is not set
+# CONFIG_SND_OXYGEN is not set
+# CONFIG_SND_CS4281 is not set
+# CONFIG_SND_CS46XX is not set
+# CONFIG_SND_CS5530 is not set
+# CONFIG_SND_DARLA20 is not set
+# CONFIG_SND_GINA20 is not set
+# CONFIG_SND_LAYLA20 is not set
+# CONFIG_SND_DARLA24 is not set
+# CONFIG_SND_GINA24 is not set
+# CONFIG_SND_LAYLA24 is not set
+# CONFIG_SND_MONA is not set
+# CONFIG_SND_MIA is not set
+# CONFIG_SND_ECHO3G is not set
+# CONFIG_SND_INDIGO is not set
+# CONFIG_SND_INDIGOIO is not set
+# CONFIG_SND_INDIGODJ is not set
+# CONFIG_SND_EMU10K1 is not set
+# CONFIG_SND_EMU10K1X is not set
+# CONFIG_SND_ENS1370 is not set
+# CONFIG_SND_ENS1371 is not set
+# CONFIG_SND_ES1938 is not set
+# CONFIG_SND_ES1968 is not set
+# CONFIG_SND_FM801 is not set
+CONFIG_SND_HDA_INTEL=y
+CONFIG_SND_HDA_HWDEP=y
+CONFIG_SND_HDA_CODEC_REALTEK=y
+CONFIG_SND_HDA_CODEC_ANALOG=y
+CONFIG_SND_HDA_CODEC_SIGMATEL=y
+CONFIG_SND_HDA_CODEC_VIA=y
+CONFIG_SND_HDA_CODEC_ATIHDMI=y
+CONFIG_SND_HDA_CODEC_CONEXANT=y
+CONFIG_SND_HDA_CODEC_CMEDIA=y
+CONFIG_SND_HDA_CODEC_SI3054=y
+CONFIG_SND_HDA_GENERIC=y
+# CONFIG_SND_HDA_POWER_SAVE is not set
+# CONFIG_SND_HDSP is not set
+# CONFIG_SND_HDSPM is not set
+# CONFIG_SND_HIFIER is not set
+# CONFIG_SND_ICE1712 is not set
+# CONFIG_SND_ICE1724 is not set
+# CONFIG_SND_INTEL8X0 is not set
+# CONFIG_SND_INTEL8X0M is not set
+# CONFIG_SND_KORG1212 is not set
+# CONFIG_SND_MAESTRO3 is not set
+# CONFIG_SND_MIXART is not set
+# CONFIG_SND_NM256 is not set
+# CONFIG_SND_PCXHR is not set
+# CONFIG_SND_RIPTIDE is not set
+# CONFIG_SND_RME32 is not set
+# CONFIG_SND_RME96 is not set
+# CONFIG_SND_RME9652 is not set
+# CONFIG_SND_SONICVIBES is not set
+# CONFIG_SND_TRIDENT is not set
+# CONFIG_SND_VIA82XX is not set
+# CONFIG_SND_VIA82XX_MODEM is not set
+# CONFIG_SND_VIRTUOSO is not set
+# CONFIG_SND_VX222 is not set
+# CONFIG_SND_YMFPCI is not set
+
+#
+# USB devices
+#
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_USX2Y is not set
+# CONFIG_SND_USB_CAIAQ is not set
+
+#
+# PCMCIA devices
+#
+# CONFIG_SND_VXPOCKET is not set
+# CONFIG_SND_PDAUDIOCF is not set
+
+#
+# System on Chip audio support
+#
+# CONFIG_SND_SOC is not set
+
+#
+# ALSA SoC audio for Freescale SOCs
+#
+
+#
+# SoC Audio for the Texas Instruments OMAP
+#
#
# Open Sound System
#
-CONFIG_SOUND_PRIME=y
-# CONFIG_SOUND_TRIDENT is not set
-# CONFIG_SOUND_MSNDCLAS is not set
-# CONFIG_SOUND_MSNDPIN is not set
-# CONFIG_SOUND_OSS is not set
+# CONFIG_SOUND_PRIME is not set
CONFIG_HID_SUPPORT=y
CONFIG_HID=y
-# CONFIG_HID_DEBUG is not set
+CONFIG_HID_DEBUG=y
+CONFIG_HIDRAW=y
#
# USB Input Devices
#
CONFIG_USB_HID=y
-# CONFIG_USB_HIDINPUT_POWERBOOK is not set
-# CONFIG_HID_FF is not set
-# CONFIG_USB_HIDDEV is not set
+CONFIG_USB_HIDINPUT_POWERBOOK=y
+CONFIG_HID_FF=y
+CONFIG_HID_PID=y
+CONFIG_LOGITECH_FF=y
+# CONFIG_LOGIRUMBLEPAD2_FF is not set
+CONFIG_PANTHERLORD_FF=y
+CONFIG_THRUSTMASTER_FF=y
+CONFIG_ZEROPLUS_FF=y
+CONFIG_USB_HIDDEV=y
CONFIG_USB_SUPPORT=y
CONFIG_USB_ARCH_HAS_HCD=y
CONFIG_USB_ARCH_HAS_OHCI=y
CONFIG_USB_ARCH_HAS_EHCI=y
CONFIG_USB=y
-# CONFIG_USB_DEBUG is not set
+CONFIG_USB_DEBUG=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
#
# Miscellaneous USB options
@@ -992,18 +1631,18 @@ CONFIG_USB=y
CONFIG_USB_DEVICEFS=y
# CONFIG_USB_DEVICE_CLASS is not set
# CONFIG_USB_DYNAMIC_MINORS is not set
-# CONFIG_USB_SUSPEND is not set
-# CONFIG_USB_PERSIST is not set
+CONFIG_USB_SUSPEND=y
# CONFIG_USB_OTG is not set
#
# USB Host Controller Drivers
#
+# CONFIG_USB_C67X00_HCD is not set
CONFIG_USB_EHCI_HCD=y
-# CONFIG_USB_EHCI_SPLIT_ISO is not set
# CONFIG_USB_EHCI_ROOT_HUB_TT is not set
# CONFIG_USB_EHCI_TT_NEWSCHED is not set
# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
CONFIG_USB_OHCI_HCD=y
# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
@@ -1036,8 +1675,10 @@ CONFIG_USB_STORAGE=y
# CONFIG_USB_STORAGE_SDDR55 is not set
# CONFIG_USB_STORAGE_JUMPSHOT is not set
# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_ONETOUCH is not set
# CONFIG_USB_STORAGE_KARMA is not set
-# CONFIG_USB_LIBUSUAL is not set
+# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set
+CONFIG_USB_LIBUSUAL=y
#
# USB Imaging devices
@@ -1049,10 +1690,6 @@ CONFIG_USB_MON=y
#
# USB port drivers
#
-
-#
-# USB Serial Converter support
-#
# CONFIG_USB_SERIAL is not set
#
@@ -1078,98 +1715,126 @@ CONFIG_USB_MON=y
# CONFIG_USB_TRANCEVIBRATOR is not set
# CONFIG_USB_IOWARRIOR is not set
# CONFIG_USB_TEST is not set
+# CONFIG_USB_GADGET is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
#
-# USB DSL modem support
+# LED drivers
#
+# CONFIG_LEDS_CLEVO_MAIL is not set
#
-# USB Gadget Support
+# LED Triggers
#
-# CONFIG_USB_GADGET is not set
-# CONFIG_MMC is not set
+CONFIG_LEDS_TRIGGERS=y
+# CONFIG_LEDS_TRIGGER_TIMER is not set
+# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set
+# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_INFINIBAND is not set
+CONFIG_EDAC=y
#
-# LED devices
+# Reporting subsystems
#
-# CONFIG_NEW_LEDS is not set
+# CONFIG_EDAC_DEBUG is not set
+# CONFIG_EDAC_MM_EDAC is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+# CONFIG_RTC_HCTOSYS is not set
+# CONFIG_RTC_DEBUG is not set
#
-# LED drivers
+# RTC interfaces
#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
#
-# LED Triggers
+# I2C RTC drivers
#
-# CONFIG_INFINIBAND is not set
-# CONFIG_EDAC is not set
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_S35390A is not set
#
-# Real Time Clock
+# SPI RTC drivers
#
-# CONFIG_RTC_CLASS is not set
#
-# DMA Engine support
+# Platform RTC drivers
#
-# CONFIG_DMA_ENGINE is not set
+CONFIG_RTC_DRV_CMOS=y
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_V3020 is not set
#
-# DMA Clients
+# on-CPU RTC drivers
#
+CONFIG_DMADEVICES=y
#
# DMA Devices
#
-CONFIG_VIRTUALIZATION=y
-# CONFIG_KVM is not set
-
-#
-# Userspace I/O
-#
+# CONFIG_INTEL_IOATDMA is not set
# CONFIG_UIO is not set
#
# Firmware Drivers
#
# CONFIG_EDD is not set
+CONFIG_EFI_VARS=y
# CONFIG_DELL_RBU is not set
# CONFIG_DCDBAS is not set
CONFIG_DMIID=y
+# CONFIG_ISCSI_IBFT_FIND is not set
#
# File systems
#
-CONFIG_EXT2_FS=y
-CONFIG_EXT2_FS_XATTR=y
-CONFIG_EXT2_FS_POSIX_ACL=y
-# CONFIG_EXT2_FS_SECURITY is not set
-# CONFIG_EXT2_FS_XIP is not set
+# CONFIG_EXT2_FS is not set
CONFIG_EXT3_FS=y
CONFIG_EXT3_FS_XATTR=y
CONFIG_EXT3_FS_POSIX_ACL=y
-# CONFIG_EXT3_FS_SECURITY is not set
+CONFIG_EXT3_FS_SECURITY=y
# CONFIG_EXT4DEV_FS is not set
CONFIG_JBD=y
# CONFIG_JBD_DEBUG is not set
CONFIG_FS_MBCACHE=y
-CONFIG_REISERFS_FS=y
-# CONFIG_REISERFS_CHECK is not set
-# CONFIG_REISERFS_PROC_INFO is not set
-CONFIG_REISERFS_FS_XATTR=y
-CONFIG_REISERFS_FS_POSIX_ACL=y
-# CONFIG_REISERFS_FS_SECURITY is not set
+# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
CONFIG_FS_POSIX_ACL=y
# CONFIG_XFS_FS is not set
# CONFIG_GFS2_FS is not set
# CONFIG_OCFS2_FS is not set
-# CONFIG_MINIX_FS is not set
-# CONFIG_ROMFS_FS is not set
+CONFIG_DNOTIFY=y
CONFIG_INOTIFY=y
CONFIG_INOTIFY_USER=y
-# CONFIG_QUOTA is not set
-CONFIG_DNOTIFY=y
+CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+# CONFIG_PRINT_QUOTA_WARNING is not set
+# CONFIG_QFMT_V1 is not set
+CONFIG_QFMT_V2=y
+CONFIG_QUOTACTL=y
# CONFIG_AUTOFS_FS is not set
CONFIG_AUTOFS4_FS=y
# CONFIG_FUSE_FS is not set
@@ -1180,7 +1845,7 @@ CONFIG_GENERIC_ACL=y
#
CONFIG_ISO9660_FS=y
CONFIG_JOLIET=y
-# CONFIG_ZISOFS is not set
+CONFIG_ZISOFS=y
# CONFIG_UDF_FS is not set
#
@@ -1198,13 +1863,13 @@ CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
#
CONFIG_PROC_FS=y
CONFIG_PROC_KCORE=y
+CONFIG_PROC_VMCORE=y
CONFIG_PROC_SYSCTL=y
CONFIG_SYSFS=y
CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_HUGETLBFS=y
CONFIG_HUGETLB_PAGE=y
-CONFIG_RAMFS=y
# CONFIG_CONFIGFS_FS is not set
#
@@ -1212,6 +1877,7 @@ CONFIG_RAMFS=y
#
# CONFIG_ADFS_FS is not set
# CONFIG_AFFS_FS is not set
+# CONFIG_ECRYPT_FS is not set
# CONFIG_HFS_FS is not set
# CONFIG_HFSPLUS_FS is not set
# CONFIG_BEFS_FS is not set
@@ -1219,33 +1885,15 @@ CONFIG_RAMFS=y
# CONFIG_EFS_FS is not set
# CONFIG_CRAMFS is not set
# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
# CONFIG_HPFS_FS is not set
# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
# CONFIG_SYSV_FS is not set
# CONFIG_UFS_FS is not set
-
-#
-# Network File Systems
-#
-CONFIG_NFS_FS=y
-CONFIG_NFS_V3=y
-# CONFIG_NFS_V3_ACL is not set
-# CONFIG_NFS_V4 is not set
-# CONFIG_NFS_DIRECTIO is not set
-CONFIG_NFSD=y
-CONFIG_NFSD_V3=y
-# CONFIG_NFSD_V3_ACL is not set
-# CONFIG_NFSD_V4 is not set
-CONFIG_NFSD_TCP=y
-CONFIG_ROOT_NFS=y
-CONFIG_LOCKD=y
-CONFIG_LOCKD_V4=y
-CONFIG_EXPORTFS=y
-CONFIG_NFS_COMMON=y
-CONFIG_SUNRPC=y
-# CONFIG_SUNRPC_BIND34 is not set
-# CONFIG_RPCSEC_GSS_KRB5 is not set
-# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+# CONFIG_NFS_FS is not set
+# CONFIG_NFSD is not set
# CONFIG_SMB_FS is not set
# CONFIG_CIFS is not set
# CONFIG_NCP_FS is not set
@@ -1255,14 +1903,26 @@ CONFIG_SUNRPC=y
#
# Partition Types
#
-# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+CONFIG_OSF_PARTITION=y
+CONFIG_AMIGA_PARTITION=y
+# CONFIG_ATARI_PARTITION is not set
+CONFIG_MAC_PARTITION=y
CONFIG_MSDOS_PARTITION=y
-
-#
-# Native Language Support
-#
+CONFIG_BSD_DISKLABEL=y
+CONFIG_MINIX_SUBPARTITION=y
+CONFIG_SOLARIS_X86_PARTITION=y
+CONFIG_UNIXWARE_DISKLABEL=y
+# CONFIG_LDM_PARTITION is not set
+CONFIG_SGI_PARTITION=y
+# CONFIG_ULTRIX_PARTITION is not set
+CONFIG_SUN_PARTITION=y
+CONFIG_KARMA_PARTITION=y
+CONFIG_EFI_PARTITION=y
+# CONFIG_SYSV68_PARTITION is not set
CONFIG_NLS=y
-CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_DEFAULT="utf8"
CONFIG_NLS_CODEPAGE_437=y
# CONFIG_NLS_CODEPAGE_737 is not set
# CONFIG_NLS_CODEPAGE_775 is not set
@@ -1297,40 +1957,33 @@ CONFIG_NLS_ISO8859_1=y
# CONFIG_NLS_ISO8859_9 is not set
# CONFIG_NLS_ISO8859_13 is not set
# CONFIG_NLS_ISO8859_14 is not set
-CONFIG_NLS_ISO8859_15=y
+# CONFIG_NLS_ISO8859_15 is not set
# CONFIG_NLS_KOI8_R is not set
# CONFIG_NLS_KOI8_U is not set
CONFIG_NLS_UTF8=y
-
-#
-# Distributed Lock Manager
-#
# CONFIG_DLM is not set
#
-# Instrumentation Support
-#
-CONFIG_PROFILING=y
-CONFIG_OPROFILE=y
-CONFIG_KPROBES=y
-
-#
# Kernel hacking
#
CONFIG_TRACE_IRQFLAGS_SUPPORT=y
# CONFIG_PRINTK_TIME is not set
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
# CONFIG_ENABLE_MUST_CHECK is not set
+CONFIG_FRAME_WARN=2048
CONFIG_MAGIC_SYSRQ=y
-CONFIG_UNUSED_SYMBOLS=y
+# CONFIG_UNUSED_SYMBOLS is not set
CONFIG_DEBUG_FS=y
# CONFIG_HEADERS_CHECK is not set
CONFIG_DEBUG_KERNEL=y
# CONFIG_DEBUG_SHIRQ is not set
-CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_DETECT_SOFTLOCKUP is not set
# CONFIG_SCHED_DEBUG is not set
-# CONFIG_SCHEDSTATS is not set
+CONFIG_SCHEDSTATS=y
CONFIG_TIMER_STATS=y
-# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_SLUB_DEBUG_ON is not set
+# CONFIG_SLUB_STATS is not set
# CONFIG_DEBUG_RT_MUTEXES is not set
# CONFIG_RT_MUTEX_TESTER is not set
# CONFIG_DEBUG_SPINLOCK is not set
@@ -1344,28 +1997,162 @@ CONFIG_TIMER_STATS=y
CONFIG_DEBUG_BUGVERBOSE=y
# CONFIG_DEBUG_INFO is not set
# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
# CONFIG_DEBUG_LIST is not set
-# CONFIG_FRAME_POINTER is not set
-CONFIG_OPTIMIZE_INLINING=y
+# CONFIG_DEBUG_SG is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_BOOT_PRINTK_DELAY is not set
# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_KPROBES_SANITY_TEST is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
# CONFIG_LKDTM is not set
# CONFIG_FAULT_INJECTION is not set
-# CONFIG_DEBUG_RODATA is not set
-# CONFIG_IOMMU_DEBUG is not set
+# CONFIG_LATENCYTOP is not set
+CONFIG_PROVIDE_OHCI1394_DMA_INIT=y
+# CONFIG_SAMPLES is not set
+# CONFIG_KGDB is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_NONPROMISC_DEVMEM is not set
+CONFIG_EARLY_PRINTK=y
CONFIG_DEBUG_STACKOVERFLOW=y
-# CONFIG_DEBUG_STACK_USAGE is not set
+CONFIG_DEBUG_STACK_USAGE=y
+# CONFIG_DEBUG_PAGEALLOC is not set
+# CONFIG_DEBUG_PER_CPU_MAPS is not set
+# CONFIG_X86_PTDUMP is not set
+CONFIG_DEBUG_RODATA=y
+# CONFIG_DIRECT_GBPAGES is not set
+# CONFIG_DEBUG_RODATA_TEST is not set
+CONFIG_DEBUG_NX_TEST=m
+CONFIG_X86_MPPARSE=y
+# CONFIG_IOMMU_DEBUG is not set
+CONFIG_IO_DELAY_TYPE_0X80=0
+CONFIG_IO_DELAY_TYPE_0XED=1
+CONFIG_IO_DELAY_TYPE_UDELAY=2
+CONFIG_IO_DELAY_TYPE_NONE=3
+CONFIG_IO_DELAY_0X80=y
+# CONFIG_IO_DELAY_0XED is not set
+# CONFIG_IO_DELAY_UDELAY is not set
+# CONFIG_IO_DELAY_NONE is not set
+CONFIG_DEFAULT_IO_DELAY_TYPE=0
+CONFIG_DEBUG_BOOT_PARAMS=y
+# CONFIG_CPA_DEBUG is not set
#
# Security options
#
-# CONFIG_KEYS is not set
-# CONFIG_SECURITY is not set
-# CONFIG_CRYPTO is not set
+CONFIG_KEYS=y
+CONFIG_KEYS_DEBUG_PROC_KEYS=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+# CONFIG_SECURITY_NETWORK_XFRM is not set
+CONFIG_SECURITY_CAPABILITIES=y
+CONFIG_SECURITY_FILE_CAPABILITIES=y
+# CONFIG_SECURITY_ROOTPLUG is not set
+CONFIG_SECURITY_DEFAULT_MMAP_MIN_ADDR=65536
+CONFIG_SECURITY_SELINUX=y
+CONFIG_SECURITY_SELINUX_BOOTPARAM=y
+CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1
+CONFIG_SECURITY_SELINUX_DISABLE=y
+CONFIG_SECURITY_SELINUX_DEVELOP=y
+CONFIG_SECURITY_SELINUX_AVC_STATS=y
+CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1
+# CONFIG_SECURITY_SELINUX_ENABLE_SECMARK_DEFAULT is not set
+# CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX is not set
+# CONFIG_SECURITY_SMACK is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_AEAD=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_MANAGER=y
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_AUTHENC=y
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+CONFIG_CRYPTO_CBC=y
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+CONFIG_CRYPTO_ECB=y
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+CONFIG_CRYPTO_HMAC=y
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+CONFIG_CRYPTO_SHA1=y
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+CONFIG_CRYPTO_AES=y
+# CONFIG_CRYPTO_AES_X86_64 is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+CONFIG_CRYPTO_ARC4=y
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SALSA20_X86_64 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+# CONFIG_CRYPTO_TWOFISH_X86_64 is not set
+
+#
+# Compression
+#
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_LZO is not set
+CONFIG_CRYPTO_HW=y
+# CONFIG_CRYPTO_DEV_HIFN_795X is not set
+CONFIG_HAVE_KVM=y
+CONFIG_VIRTUALIZATION=y
+# CONFIG_KVM is not set
+# CONFIG_VIRTIO_PCI is not set
+# CONFIG_VIRTIO_BALLOON is not set
#
# Library routines
#
CONFIG_BITREVERSE=y
+CONFIG_GENERIC_FIND_FIRST_BIT=y
+CONFIG_GENERIC_FIND_NEXT_BIT=y
# CONFIG_CRC_CCITT is not set
# CONFIG_CRC16 is not set
# CONFIG_CRC_ITU_T is not set
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 5e618c3b4720..e1928384c686 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -2,10 +2,17 @@
# Makefile for the linux kernel.
#
-extra-y := head_$(BITS).o head$(BITS).o init_task.o vmlinux.lds
+extra-y := head_$(BITS).o head$(BITS).o head.o init_task.o vmlinux.lds
CPPFLAGS_vmlinux.lds += -U$(UTS_MACHINE)
+ifdef CONFIG_FTRACE
+# Do not profile debug utilities
+CFLAGS_REMOVE_tsc_64.o = -pg
+CFLAGS_REMOVE_tsc_32.o = -pg
+CFLAGS_REMOVE_rtc.o = -pg
+endif
+
#
# vsyscalls (which work on the user stack) should have
# no stack-protector checks:
@@ -14,18 +21,18 @@ nostackp := $(call cc-option, -fno-stack-protector)
CFLAGS_vsyscall_64.o := $(PROFILING) -g0 $(nostackp)
CFLAGS_hpet.o := $(nostackp)
CFLAGS_tsc_64.o := $(nostackp)
+CFLAGS_paravirt.o := $(nostackp)
obj-y := process_$(BITS).o signal_$(BITS).o entry_$(BITS).o
obj-y += traps_$(BITS).o irq_$(BITS).o
obj-y += time_$(BITS).o ioport.o ldt.o
-obj-y += setup_$(BITS).o i8259_$(BITS).o setup.o
+obj-y += setup_$(BITS).o i8259.o irqinit_$(BITS).o setup.o
obj-$(CONFIG_X86_32) += sys_i386_32.o i386_ksyms_32.o
obj-$(CONFIG_X86_64) += sys_x86_64.o x8664_ksyms_64.o
obj-$(CONFIG_X86_64) += syscall_64.o vsyscall_64.o setup64.o
-obj-y += bootflag.o e820_$(BITS).o
+obj-y += bootflag.o e820_$(BITS).o e820.o
obj-y += pci-dma.o quirks.o i8237.o topology.o kdebugfs.o
obj-y += alternative.o i8253.o pci-nommu.o
-obj-$(CONFIG_X86_64) += bugs_64.o
obj-y += tsc_$(BITS).o io_delay.o rtc.o
obj-$(CONFIG_X86_TRAMPOLINE) += trampoline.o
@@ -53,9 +60,10 @@ obj-$(CONFIG_X86_32_SMP) += smpcommon.o
obj-$(CONFIG_X86_64_SMP) += tsc_sync.o smpcommon.o
obj-$(CONFIG_X86_TRAMPOLINE) += trampoline_$(BITS).o
obj-$(CONFIG_X86_MPPARSE) += mpparse.o
-obj-$(CONFIG_X86_LOCAL_APIC) += apic_$(BITS).o nmi_$(BITS).o
+obj-$(CONFIG_X86_LOCAL_APIC) += apic_$(BITS).o nmi.o
obj-$(CONFIG_X86_IO_APIC) += io_apic_$(BITS).o
obj-$(CONFIG_X86_REBOOTFIXUPS) += reboot_fixups_32.o
+obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
obj-$(CONFIG_KEXEC) += machine_kexec_$(BITS).o
obj-$(CONFIG_KEXEC) += relocate_kernel_$(BITS).o crash.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump_$(BITS).o
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index 33c5216fd3e1..f1d24bda02b8 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -338,8 +338,6 @@ acpi_parse_lapic_nmi(struct acpi_subtable_header * header, const unsigned long e
#ifdef CONFIG_X86_IO_APIC
-struct mp_ioapic_routing mp_ioapic_routing[MAX_IO_APICS];
-
static int __init
acpi_parse_ioapic(struct acpi_subtable_header * header, const unsigned long end)
{
@@ -514,8 +512,6 @@ int acpi_register_gsi(u32 gsi, int triggering, int polarity)
* Make sure all (legacy) PCI IRQs are set as level-triggered.
*/
if (acpi_irq_model == ACPI_IRQ_MODEL_PIC) {
- extern void eisa_set_level_irq(unsigned int irq);
-
if (triggering == ACPI_LEVEL_SENSITIVE)
eisa_set_level_irq(gsi);
}
@@ -860,6 +856,336 @@ static int __init acpi_parse_madt_lapic_entries(void)
#endif /* CONFIG_X86_LOCAL_APIC */
#ifdef CONFIG_X86_IO_APIC
+#define MP_ISA_BUS 0
+
+#ifdef CONFIG_X86_ES7000
+extern int es7000_plat;
+#endif
+
+static struct {
+ int apic_id;
+ int gsi_base;
+ int gsi_end;
+ DECLARE_BITMAP(pin_programmed, MP_MAX_IOAPIC_PIN + 1);
+} mp_ioapic_routing[MAX_IO_APICS];
+
+static int mp_find_ioapic(int gsi)
+{
+ int i = 0;
+
+ /* Find the IOAPIC that manages this GSI. */
+ for (i = 0; i < nr_ioapics; i++) {
+ if ((gsi >= mp_ioapic_routing[i].gsi_base)
+ && (gsi <= mp_ioapic_routing[i].gsi_end))
+ return i;
+ }
+
+ printk(KERN_ERR "ERROR: Unable to locate IOAPIC for GSI %d\n", gsi);
+ return -1;
+}
+
+static u8 __init uniq_ioapic_id(u8 id)
+{
+#ifdef CONFIG_X86_32
+ if ((boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) &&
+ !APIC_XAPIC(apic_version[boot_cpu_physical_apicid]))
+ return io_apic_get_unique_id(nr_ioapics, id);
+ else
+ return id;
+#else
+ int i;
+ DECLARE_BITMAP(used, 256);
+ bitmap_zero(used, 256);
+ for (i = 0; i < nr_ioapics; i++) {
+ struct mp_config_ioapic *ia = &mp_ioapics[i];
+ __set_bit(ia->mp_apicid, used);
+ }
+ if (!test_bit(id, used))
+ return id;
+ return find_first_zero_bit(used, 256);
+#endif
+}
+
+static int bad_ioapic(unsigned long address)
+{
+ if (nr_ioapics >= MAX_IO_APICS) {
+ printk(KERN_ERR "ERROR: Max # of I/O APICs (%d) exceeded "
+ "(found %d)\n", MAX_IO_APICS, nr_ioapics);
+ panic("Recompile kernel with bigger MAX_IO_APICS!\n");
+ }
+ if (!address) {
+ printk(KERN_ERR "WARNING: Bogus (zero) I/O APIC address"
+ " found in table, skipping!\n");
+ return 1;
+ }
+ return 0;
+}
+
+void __init mp_register_ioapic(int id, u32 address, u32 gsi_base)
+{
+ int idx = 0;
+
+ if (bad_ioapic(address))
+ return;
+
+ idx = nr_ioapics;
+
+ mp_ioapics[idx].mp_type = MP_IOAPIC;
+ mp_ioapics[idx].mp_flags = MPC_APIC_USABLE;
+ mp_ioapics[idx].mp_apicaddr = address;
+
+ set_fixmap_nocache(FIX_IO_APIC_BASE_0 + idx, address);
+ mp_ioapics[idx].mp_apicid = uniq_ioapic_id(id);
+#ifdef CONFIG_X86_32
+ mp_ioapics[idx].mp_apicver = io_apic_get_version(idx);
+#else
+ mp_ioapics[idx].mp_apicver = 0;
+#endif
+ /*
+ * Build basic GSI lookup table to facilitate gsi->io_apic lookups
+ * and to prevent reprogramming of IOAPIC pins (PCI GSIs).
+ */
+ mp_ioapic_routing[idx].apic_id = mp_ioapics[idx].mp_apicid;
+ mp_ioapic_routing[idx].gsi_base = gsi_base;
+ mp_ioapic_routing[idx].gsi_end = gsi_base +
+ io_apic_get_redir_entries(idx);
+
+ printk(KERN_INFO "IOAPIC[%d]: apic_id %d, version %d, address 0x%lx, "
+ "GSI %d-%d\n", idx, mp_ioapics[idx].mp_apicid,
+ mp_ioapics[idx].mp_apicver, mp_ioapics[idx].mp_apicaddr,
+ mp_ioapic_routing[idx].gsi_base, mp_ioapic_routing[idx].gsi_end);
+
+ nr_ioapics++;
+}
+
+void __init mp_override_legacy_irq(u8 bus_irq, u8 polarity, u8 trigger, u32 gsi)
+{
+ int ioapic = -1;
+ int pin = -1;
+
+ /*
+ * Convert 'gsi' to 'ioapic.pin'.
+ */
+ ioapic = mp_find_ioapic(gsi);
+ if (ioapic < 0)
+ return;
+ pin = gsi - mp_ioapic_routing[ioapic].gsi_base;
+
+ /*
+ * TBD: This check is for faulty timer entries, where the override
+ * erroneously sets the trigger to level, resulting in a HUGE
+ * increase of timer interrupts!
+ */
+ if ((bus_irq == 0) && (trigger == 3))
+ trigger = 1;
+
+ mp_irqs[mp_irq_entries].mp_type = MP_INTSRC;
+ mp_irqs[mp_irq_entries].mp_irqtype = mp_INT;
+ mp_irqs[mp_irq_entries].mp_irqflag = (trigger << 2) | polarity;
+ mp_irqs[mp_irq_entries].mp_srcbus = MP_ISA_BUS;
+ mp_irqs[mp_irq_entries].mp_srcbusirq = bus_irq; /* IRQ */
+ mp_irqs[mp_irq_entries].mp_dstapic =
+ mp_ioapics[ioapic].mp_apicid; /* APIC ID */
+ mp_irqs[mp_irq_entries].mp_dstirq = pin; /* INTIN# */
+
+ if (++mp_irq_entries == MAX_IRQ_SOURCES)
+ panic("Max # of irq sources exceeded!!\n");
+
+}
+
+void __init mp_config_acpi_legacy_irqs(void)
+{
+ int i = 0;
+ int ioapic = -1;
+
+#if defined (CONFIG_MCA) || defined (CONFIG_EISA)
+ /*
+ * Fabricate the legacy ISA bus (bus #31).
+ */
+ mp_bus_id_to_type[MP_ISA_BUS] = MP_BUS_ISA;
+#endif
+ set_bit(MP_ISA_BUS, mp_bus_not_pci);
+ Dprintk("Bus #%d is ISA\n", MP_ISA_BUS);
+
+#ifdef CONFIG_X86_ES7000
+ /*
+ * Older generations of ES7000 have no legacy identity mappings
+ */
+ if (es7000_plat == 1)
+ return;
+#endif
+
+ /*
+ * Locate the IOAPIC that manages the ISA IRQs (0-15).
+ */
+ ioapic = mp_find_ioapic(0);
+ if (ioapic < 0)
+ return;
+
+ /*
+ * Use the default configuration for the IRQs 0-15. Unless
+ * overridden by (MADT) interrupt source override entries.
+ */
+ for (i = 0; i < 16; i++) {
+ int idx;
+
+ mp_irqs[mp_irq_entries].mp_type = MP_INTSRC;
+ mp_irqs[mp_irq_entries].mp_irqflag = 0; /* Conforming */
+ mp_irqs[mp_irq_entries].mp_srcbus = MP_ISA_BUS;
+ mp_irqs[mp_irq_entries].mp_dstapic = mp_ioapics[ioapic].mp_apicid;
+
+ for (idx = 0; idx < mp_irq_entries; idx++) {
+ struct mp_config_intsrc *irq = mp_irqs + idx;
+
+ /* Do we already have a mapping for this ISA IRQ? */
+ if (irq->mp_srcbus == MP_ISA_BUS
+ && irq->mp_srcbusirq == i)
+ break;
+
+ /* Do we already have a mapping for this IOAPIC pin */
+ if ((irq->mp_dstapic ==
+ mp_irqs[mp_irq_entries].mp_dstapic) &&
+ (irq->mp_dstirq == i))
+ break;
+ }
+
+ if (idx != mp_irq_entries) {
+ printk(KERN_DEBUG "ACPI: IRQ%d used by override.\n", i);
+ continue; /* IRQ already used */
+ }
+
+ mp_irqs[mp_irq_entries].mp_irqtype = mp_INT;
+ mp_irqs[mp_irq_entries].mp_srcbusirq = i; /* Identity mapped */
+ mp_irqs[mp_irq_entries].mp_dstirq = i;
+
+ if (++mp_irq_entries == MAX_IRQ_SOURCES)
+ panic("Max # of irq sources exceeded!!\n");
+ }
+}
+
+int mp_register_gsi(u32 gsi, int triggering, int polarity)
+{
+ int ioapic;
+ int ioapic_pin;
+#ifdef CONFIG_X86_32
+#define MAX_GSI_NUM 4096
+#define IRQ_COMPRESSION_START 64
+
+ static int pci_irq = IRQ_COMPRESSION_START;
+ /*
+ * Mapping between Global System Interrupts, which
+ * represent all possible interrupts, and IRQs
+ * assigned to actual devices.
+ */
+ static int gsi_to_irq[MAX_GSI_NUM];
+#else
+
+ if (acpi_irq_model != ACPI_IRQ_MODEL_IOAPIC)
+ return gsi;
+#endif
+
+ /* Don't set up the ACPI SCI because it's already set up */
+ if (acpi_gbl_FADT.sci_interrupt == gsi)
+ return gsi;
+
+ ioapic = mp_find_ioapic(gsi);
+ if (ioapic < 0) {
+ printk(KERN_WARNING "No IOAPIC for GSI %u\n", gsi);
+ return gsi;
+ }
+
+ ioapic_pin = gsi - mp_ioapic_routing[ioapic].gsi_base;
+
+#ifdef CONFIG_X86_32
+ if (ioapic_renumber_irq)
+ gsi = ioapic_renumber_irq(ioapic, gsi);
+#endif
+
+ /*
+ * Avoid pin reprogramming. PRTs typically include entries
+ * with redundant pin->gsi mappings (but unique PCI devices);
+ * we only program the IOAPIC on the first.
+ */
+ if (ioapic_pin > MP_MAX_IOAPIC_PIN) {
+ printk(KERN_ERR "Invalid reference to IOAPIC pin "
+ "%d-%d\n", mp_ioapic_routing[ioapic].apic_id,
+ ioapic_pin);
+ return gsi;
+ }
+ if (test_bit(ioapic_pin, mp_ioapic_routing[ioapic].pin_programmed)) {
+ Dprintk(KERN_DEBUG "Pin %d-%d already programmed\n",
+ mp_ioapic_routing[ioapic].apic_id, ioapic_pin);
+#ifdef CONFIG_X86_32
+ return (gsi < IRQ_COMPRESSION_START ? gsi : gsi_to_irq[gsi]);
+#else
+ return gsi;
+#endif
+ }
+
+ set_bit(ioapic_pin, mp_ioapic_routing[ioapic].pin_programmed);
+#ifdef CONFIG_X86_32
+ /*
+ * For GSI >= 64, use IRQ compression
+ */
+ if ((gsi >= IRQ_COMPRESSION_START)
+ && (triggering == ACPI_LEVEL_SENSITIVE)) {
+ /*
+ * For PCI devices assign IRQs in order, avoiding gaps
+ * due to unused I/O APIC pins.
+ */
+ int irq = gsi;
+ if (gsi < MAX_GSI_NUM) {
+ /*
+ * Retain the VIA chipset work-around (gsi > 15), but
+ * avoid a problem where the 8254 timer (IRQ0) is setup
+ * via an override (so it's not on pin 0 of the ioapic),
+ * and at the same time, the pin 0 interrupt is a PCI
+ * type. The gsi > 15 test could cause these two pins
+ * to be shared as IRQ0, and they are not shareable.
+ * So test for this condition, and if necessary, avoid
+ * the pin collision.
+ */
+ gsi = pci_irq++;
+ /*
+ * Don't assign IRQ used by ACPI SCI
+ */
+ if (gsi == acpi_gbl_FADT.sci_interrupt)
+ gsi = pci_irq++;
+ gsi_to_irq[irq] = gsi;
+ } else {
+ printk(KERN_ERR "GSI %u is too high\n", gsi);
+ return gsi;
+ }
+ }
+#endif
+ io_apic_set_pci_routing(ioapic, ioapic_pin, gsi,
+ triggering == ACPI_EDGE_SENSITIVE ? 0 : 1,
+ polarity == ACPI_ACTIVE_HIGH ? 0 : 1);
+ return gsi;
+}
+
+int mp_config_acpi_gsi(unsigned char number, unsigned int devfn, u8 pin,
+ u32 gsi, int triggering, int polarity)
+{
+ struct mpc_config_intsrc intsrc;
+ int ioapic;
+
+ /* print the entry should happen on mptable identically */
+ intsrc.mpc_type = MP_INTSRC;
+ intsrc.mpc_irqtype = mp_INT;
+ intsrc.mpc_irqflag = (triggering == ACPI_EDGE_SENSITIVE ? 4 : 0x0c) |
+ (polarity == ACPI_ACTIVE_HIGH ? 1 : 3);
+ intsrc.mpc_srcbus = number;
+ intsrc.mpc_srcbusirq = (((devfn >> 3) & 0x1f) << 2) | ((pin - 1) & 3);
+ ioapic = mp_find_ioapic(gsi);
+ intsrc.mpc_dstapic = mp_ioapic_routing[ioapic].apic_id;
+ intsrc.mpc_dstirq = gsi - mp_ioapic_routing[ioapic].gsi_base;
+
+ MP_intsrc_info(&intsrc);
+
+ return 0;
+}
+
/*
* Parse IOAPIC related entries in MADT
* returns 0 on success, < 0 on error
diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c
index afc25ee9964b..882e970032d5 100644
--- a/arch/x86/kernel/acpi/sleep.c
+++ b/arch/x86/kernel/acpi/sleep.c
@@ -124,6 +124,8 @@ static int __init acpi_sleep_setup(char *str)
acpi_realmode_flags |= 2;
if (strncmp(str, "s3_beep", 7) == 0)
acpi_realmode_flags |= 4;
+ if (strncmp(str, "old_ordering", 12) == 0)
+ acpi_old_suspend_ordering();
str = strchr(str, ',');
if (str != NULL)
str += strspn(str, ", \t");
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index 65c7857a90dd..2763cb37b553 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -1,6 +1,6 @@
#include <linux/module.h>
#include <linux/sched.h>
-#include <linux/spinlock.h>
+#include <linux/mutex.h>
#include <linux/list.h>
#include <linux/kprobes.h>
#include <linux/mm.h>
@@ -143,7 +143,7 @@ static const unsigned char *const p6_nops[ASM_NOP_MAX+1] = {
#ifdef CONFIG_X86_64
extern char __vsyscall_0;
-static inline const unsigned char*const * find_nop_table(void)
+const unsigned char *const *find_nop_table(void)
{
return boot_cpu_data.x86_vendor != X86_VENDOR_INTEL ||
boot_cpu_data.x86 < 6 ? k8_nops : p6_nops;
@@ -162,7 +162,7 @@ static const struct nop {
{ -1, NULL }
};
-static const unsigned char*const * find_nop_table(void)
+const unsigned char *const *find_nop_table(void)
{
const unsigned char *const *noptable = intel_nops;
int i;
@@ -279,7 +279,7 @@ struct smp_alt_module {
struct list_head next;
};
static LIST_HEAD(smp_alt_modules);
-static DEFINE_SPINLOCK(smp_alt);
+static DEFINE_MUTEX(smp_alt);
static int smp_mode = 1; /* protected by smp_alt */
void alternatives_smp_module_add(struct module *mod, char *name,
@@ -312,12 +312,12 @@ void alternatives_smp_module_add(struct module *mod, char *name,
__func__, smp->locks, smp->locks_end,
smp->text, smp->text_end, smp->name);
- spin_lock(&smp_alt);
+ mutex_lock(&smp_alt);
list_add_tail(&smp->next, &smp_alt_modules);
if (boot_cpu_has(X86_FEATURE_UP))
alternatives_smp_unlock(smp->locks, smp->locks_end,
smp->text, smp->text_end);
- spin_unlock(&smp_alt);
+ mutex_unlock(&smp_alt);
}
void alternatives_smp_module_del(struct module *mod)
@@ -327,17 +327,17 @@ void alternatives_smp_module_del(struct module *mod)
if (smp_alt_once || noreplace_smp)
return;
- spin_lock(&smp_alt);
+ mutex_lock(&smp_alt);
list_for_each_entry(item, &smp_alt_modules, next) {
if (mod != item->mod)
continue;
list_del(&item->next);
- spin_unlock(&smp_alt);
+ mutex_unlock(&smp_alt);
DPRINTK("%s: %s\n", __func__, item->name);
kfree(item);
return;
}
- spin_unlock(&smp_alt);
+ mutex_unlock(&smp_alt);
}
void alternatives_smp_switch(int smp)
@@ -359,7 +359,7 @@ void alternatives_smp_switch(int smp)
return;
BUG_ON(!smp && (num_online_cpus() > 1));
- spin_lock(&smp_alt);
+ mutex_lock(&smp_alt);
/*
* Avoid unnecessary switches because it forces JIT based VMs to
@@ -383,7 +383,7 @@ void alternatives_smp_switch(int smp)
mod->text, mod->text_end);
}
smp_mode = smp;
- spin_unlock(&smp_alt);
+ mutex_unlock(&smp_alt);
}
#endif
diff --git a/arch/x86/kernel/aperture_64.c b/arch/x86/kernel/aperture_64.c
index 479926d9e004..eb20f168c0fd 100644
--- a/arch/x86/kernel/aperture_64.c
+++ b/arch/x86/kernel/aperture_64.c
@@ -35,6 +35,18 @@ int fallback_aper_force __initdata;
int fix_aperture __initdata = 1;
+struct bus_dev_range {
+ int bus;
+ int dev_base;
+ int dev_limit;
+};
+
+static struct bus_dev_range bus_dev_ranges[] __initdata = {
+ { 0x00, 0x18, 0x20},
+ { 0xff, 0x00, 0x20},
+ { 0xfe, 0x00, 0x20}
+};
+
static struct resource gart_resource = {
.name = "GART",
.flags = IORESOURCE_MEM,
@@ -55,8 +67,9 @@ static u32 __init allocate_aperture(void)
u32 aper_size;
void *p;
- if (fallback_aper_order > 7)
- fallback_aper_order = 7;
+ /* aper_size should <= 1G */
+ if (fallback_aper_order > 5)
+ fallback_aper_order = 5;
aper_size = (32 * 1024 * 1024) << fallback_aper_order;
/*
@@ -65,7 +78,20 @@ static u32 __init allocate_aperture(void)
* memory. Unfortunately we cannot move it up because that would
* make the IOMMU useless.
*/
- p = __alloc_bootmem_nopanic(aper_size, aper_size, 0);
+ /*
+ * using 512M as goal, in case kexec will load kernel_big
+ * that will do the on position decompress, and could overlap with
+ * that positon with gart that is used.
+ * sequende:
+ * kernel_small
+ * ==> kexec (with kdump trigger path or previous doesn't shutdown gart)
+ * ==> kernel_small(gart area become e820_reserved)
+ * ==> kexec (with kdump trigger path or previous doesn't shutdown gart)
+ * ==> kerne_big (uncompressed size will be big than 64M or 128M)
+ * so don't use 512M below as gart iommu, leave the space for kernel
+ * code for safe
+ */
+ p = __alloc_bootmem_nopanic(aper_size, aper_size, 512ULL<<20);
if (!p || __pa(p)+aper_size > 0xffffffff) {
printk(KERN_ERR
"Cannot allocate aperture memory hole (%p,%uK)\n",
@@ -83,69 +109,53 @@ static u32 __init allocate_aperture(void)
return (u32)__pa(p);
}
-static int __init aperture_valid(u64 aper_base, u32 aper_size)
-{
- if (!aper_base)
- return 0;
-
- if (aper_base + aper_size > 0x100000000UL) {
- printk(KERN_ERR "Aperture beyond 4GB. Ignoring.\n");
- return 0;
- }
- if (e820_any_mapped(aper_base, aper_base + aper_size, E820_RAM)) {
- printk(KERN_ERR "Aperture pointing to e820 RAM. Ignoring.\n");
- return 0;
- }
- if (aper_size < 64*1024*1024) {
- printk(KERN_ERR "Aperture too small (%d MB)\n", aper_size>>20);
- return 0;
- }
-
- return 1;
-}
/* Find a PCI capability */
-static __u32 __init find_cap(int num, int slot, int func, int cap)
+static u32 __init find_cap(int bus, int slot, int func, int cap)
{
int bytes;
u8 pos;
- if (!(read_pci_config_16(num, slot, func, PCI_STATUS) &
+ if (!(read_pci_config_16(bus, slot, func, PCI_STATUS) &
PCI_STATUS_CAP_LIST))
return 0;
- pos = read_pci_config_byte(num, slot, func, PCI_CAPABILITY_LIST);
+ pos = read_pci_config_byte(bus, slot, func, PCI_CAPABILITY_LIST);
for (bytes = 0; bytes < 48 && pos >= 0x40; bytes++) {
u8 id;
pos &= ~3;
- id = read_pci_config_byte(num, slot, func, pos+PCI_CAP_LIST_ID);
+ id = read_pci_config_byte(bus, slot, func, pos+PCI_CAP_LIST_ID);
if (id == 0xff)
break;
if (id == cap)
return pos;
- pos = read_pci_config_byte(num, slot, func,
+ pos = read_pci_config_byte(bus, slot, func,
pos+PCI_CAP_LIST_NEXT);
}
return 0;
}
/* Read a standard AGPv3 bridge header */
-static __u32 __init read_agp(int num, int slot, int func, int cap, u32 *order)
+static u32 __init read_agp(int bus, int slot, int func, int cap, u32 *order)
{
u32 apsize;
u32 apsizereg;
int nbits;
u32 aper_low, aper_hi;
u64 aper;
+ u32 old_order;
- printk(KERN_INFO "AGP bridge at %02x:%02x:%02x\n", num, slot, func);
- apsizereg = read_pci_config_16(num, slot, func, cap + 0x14);
+ printk(KERN_INFO "AGP bridge at %02x:%02x:%02x\n", bus, slot, func);
+ apsizereg = read_pci_config_16(bus, slot, func, cap + 0x14);
if (apsizereg == 0xffffffff) {
printk(KERN_ERR "APSIZE in AGP bridge unreadable\n");
return 0;
}
+ /* old_order could be the value from NB gart setting */
+ old_order = *order;
+
apsize = apsizereg & 0xfff;
/* Some BIOS use weird encodings not in the AGPv3 table. */
if (apsize & 0xff)
@@ -155,14 +165,26 @@ static __u32 __init read_agp(int num, int slot, int func, int cap, u32 *order)
if ((int)*order < 0) /* < 32MB */
*order = 0;
- aper_low = read_pci_config(num, slot, func, 0x10);
- aper_hi = read_pci_config(num, slot, func, 0x14);
+ aper_low = read_pci_config(bus, slot, func, 0x10);
+ aper_hi = read_pci_config(bus, slot, func, 0x14);
aper = (aper_low & ~((1<<22)-1)) | ((u64)aper_hi << 32);
+ /*
+ * On some sick chips, APSIZE is 0. It means it wants 4G
+ * so let double check that order, and lets trust AMD NB settings:
+ */
+ printk(KERN_INFO "Aperture from AGP @ %Lx old size %u MB\n",
+ aper, 32 << old_order);
+ if (aper + (32ULL<<(20 + *order)) > 0x100000000ULL) {
+ printk(KERN_INFO "Aperture size %u MB (APSIZE %x) is not right, using settings from NB\n",
+ 32 << *order, apsizereg);
+ *order = old_order;
+ }
+
printk(KERN_INFO "Aperture from AGP @ %Lx size %u MB (APSIZE %x)\n",
aper, 32 << *order, apsizereg);
- if (!aperture_valid(aper, (32*1024*1024) << *order))
+ if (!aperture_valid(aper, (32*1024*1024) << *order, 32<<20))
return 0;
return (u32)aper;
}
@@ -180,17 +202,17 @@ static __u32 __init read_agp(int num, int slot, int func, int cap, u32 *order)
* the AGP bridges should be always an own bus on the HT hierarchy,
* but do it here for future safety.
*/
-static __u32 __init search_agp_bridge(u32 *order, int *valid_agp)
+static u32 __init search_agp_bridge(u32 *order, int *valid_agp)
{
- int num, slot, func;
+ int bus, slot, func;
/* Poor man's PCI discovery */
- for (num = 0; num < 256; num++) {
+ for (bus = 0; bus < 256; bus++) {
for (slot = 0; slot < 32; slot++) {
for (func = 0; func < 8; func++) {
u32 class, cap;
u8 type;
- class = read_pci_config(num, slot, func,
+ class = read_pci_config(bus, slot, func,
PCI_CLASS_REVISION);
if (class == 0xffffffff)
break;
@@ -199,17 +221,17 @@ static __u32 __init search_agp_bridge(u32 *order, int *valid_agp)
case PCI_CLASS_BRIDGE_HOST:
case PCI_CLASS_BRIDGE_OTHER: /* needed? */
/* AGP bridge? */
- cap = find_cap(num, slot, func,
+ cap = find_cap(bus, slot, func,
PCI_CAP_ID_AGP);
if (!cap)
break;
*valid_agp = 1;
- return read_agp(num, slot, func, cap,
+ return read_agp(bus, slot, func, cap,
order);
}
/* No multi-function device? */
- type = read_pci_config_byte(num, slot, func,
+ type = read_pci_config_byte(bus, slot, func,
PCI_HEADER_TYPE);
if (!(type & 0x80))
break;
@@ -249,36 +271,50 @@ void __init early_gart_iommu_check(void)
* or BIOS forget to put that in reserved.
* try to update e820 to make that region as reserved.
*/
- int fix, num;
+ int i, fix, slot;
u32 ctl;
u32 aper_size = 0, aper_order = 0, last_aper_order = 0;
u64 aper_base = 0, last_aper_base = 0;
- int aper_enabled = 0, last_aper_enabled = 0;
+ int aper_enabled = 0, last_aper_enabled = 0, last_valid = 0;
if (!early_pci_allowed())
return;
+ /* This is mostly duplicate of iommu_hole_init */
fix = 0;
- for (num = 24; num < 32; num++) {
- if (!early_is_k8_nb(read_pci_config(0, num, 3, 0x00)))
- continue;
-
- ctl = read_pci_config(0, num, 3, 0x90);
- aper_enabled = ctl & 1;
- aper_order = (ctl >> 1) & 7;
- aper_size = (32 * 1024 * 1024) << aper_order;
- aper_base = read_pci_config(0, num, 3, 0x94) & 0x7fff;
- aper_base <<= 25;
-
- if ((last_aper_order && aper_order != last_aper_order) ||
- (last_aper_base && aper_base != last_aper_base) ||
- (last_aper_enabled && aper_enabled != last_aper_enabled)) {
- fix = 1;
- break;
+ for (i = 0; i < ARRAY_SIZE(bus_dev_ranges); i++) {
+ int bus;
+ int dev_base, dev_limit;
+
+ bus = bus_dev_ranges[i].bus;
+ dev_base = bus_dev_ranges[i].dev_base;
+ dev_limit = bus_dev_ranges[i].dev_limit;
+
+ for (slot = dev_base; slot < dev_limit; slot++) {
+ if (!early_is_k8_nb(read_pci_config(bus, slot, 3, 0x00)))
+ continue;
+
+ ctl = read_pci_config(bus, slot, 3, AMD64_GARTAPERTURECTL);
+ aper_enabled = ctl & AMD64_GARTEN;
+ aper_order = (ctl >> 1) & 7;
+ aper_size = (32 * 1024 * 1024) << aper_order;
+ aper_base = read_pci_config(bus, slot, 3, AMD64_GARTAPERTUREBASE) & 0x7fff;
+ aper_base <<= 25;
+
+ if (last_valid) {
+ if ((aper_order != last_aper_order) ||
+ (aper_base != last_aper_base) ||
+ (aper_enabled != last_aper_enabled)) {
+ fix = 1;
+ break;
+ }
+ }
+
+ last_aper_order = aper_order;
+ last_aper_base = aper_base;
+ last_aper_enabled = aper_enabled;
+ last_valid = 1;
}
- last_aper_order = aper_order;
- last_aper_base = aper_base;
- last_aper_enabled = aper_enabled;
}
if (!fix && !aper_enabled)
@@ -288,34 +324,48 @@ void __init early_gart_iommu_check(void)
fix = 1;
if (gart_fix_e820 && !fix && aper_enabled) {
- if (e820_any_mapped(aper_base, aper_base + aper_size,
- E820_RAM)) {
- /* reserved it, so we can resuse it in second kernel */
+ if (!e820_all_mapped(aper_base, aper_base + aper_size,
+ E820_RESERVED)) {
+ /* reserve it, so we can reuse it in second kernel */
printk(KERN_INFO "update e820 for GART\n");
add_memory_region(aper_base, aper_size, E820_RESERVED);
update_e820();
}
- return;
}
+ if (!fix)
+ return;
+
/* different nodes have different setting, disable them all at first*/
- for (num = 24; num < 32; num++) {
- if (!early_is_k8_nb(read_pci_config(0, num, 3, 0x00)))
- continue;
+ for (i = 0; i < ARRAY_SIZE(bus_dev_ranges); i++) {
+ int bus;
+ int dev_base, dev_limit;
+
+ bus = bus_dev_ranges[i].bus;
+ dev_base = bus_dev_ranges[i].dev_base;
+ dev_limit = bus_dev_ranges[i].dev_limit;
- ctl = read_pci_config(0, num, 3, 0x90);
- ctl &= ~1;
- write_pci_config(0, num, 3, 0x90, ctl);
+ for (slot = dev_base; slot < dev_limit; slot++) {
+ if (!early_is_k8_nb(read_pci_config(bus, slot, 3, 0x00)))
+ continue;
+
+ ctl = read_pci_config(bus, slot, 3, AMD64_GARTAPERTURECTL);
+ ctl &= ~AMD64_GARTEN;
+ write_pci_config(bus, slot, 3, AMD64_GARTAPERTURECTL, ctl);
+ }
}
}
+static int __initdata printed_gart_size_msg;
+
void __init gart_iommu_hole_init(void)
{
+ u32 agp_aper_base = 0, agp_aper_order = 0;
u32 aper_size, aper_alloc = 0, aper_order = 0, last_aper_order = 0;
u64 aper_base, last_aper_base = 0;
- int fix, num, valid_agp = 0;
- int node;
+ int fix, slot, valid_agp = 0;
+ int i, node;
if (gart_iommu_aperture_disabled || !fix_aperture ||
!early_pci_allowed())
@@ -323,38 +373,63 @@ void __init gart_iommu_hole_init(void)
printk(KERN_INFO "Checking aperture...\n");
+ if (!fallback_aper_force)
+ agp_aper_base = search_agp_bridge(&agp_aper_order, &valid_agp);
+
fix = 0;
node = 0;
- for (num = 24; num < 32; num++) {
- if (!early_is_k8_nb(read_pci_config(0, num, 3, 0x00)))
- continue;
-
- iommu_detected = 1;
- gart_iommu_aperture = 1;
-
- aper_order = (read_pci_config(0, num, 3, 0x90) >> 1) & 7;
- aper_size = (32 * 1024 * 1024) << aper_order;
- aper_base = read_pci_config(0, num, 3, 0x94) & 0x7fff;
- aper_base <<= 25;
-
- printk(KERN_INFO "Node %d: aperture @ %Lx size %u MB\n",
- node, aper_base, aper_size >> 20);
- node++;
-
- if (!aperture_valid(aper_base, aper_size)) {
- fix = 1;
- break;
- }
+ for (i = 0; i < ARRAY_SIZE(bus_dev_ranges); i++) {
+ int bus;
+ int dev_base, dev_limit;
+
+ bus = bus_dev_ranges[i].bus;
+ dev_base = bus_dev_ranges[i].dev_base;
+ dev_limit = bus_dev_ranges[i].dev_limit;
+
+ for (slot = dev_base; slot < dev_limit; slot++) {
+ if (!early_is_k8_nb(read_pci_config(bus, slot, 3, 0x00)))
+ continue;
+
+ iommu_detected = 1;
+ gart_iommu_aperture = 1;
+
+ aper_order = (read_pci_config(bus, slot, 3, AMD64_GARTAPERTURECTL) >> 1) & 7;
+ aper_size = (32 * 1024 * 1024) << aper_order;
+ aper_base = read_pci_config(bus, slot, 3, AMD64_GARTAPERTUREBASE) & 0x7fff;
+ aper_base <<= 25;
+
+ printk(KERN_INFO "Node %d: aperture @ %Lx size %u MB\n",
+ node, aper_base, aper_size >> 20);
+ node++;
+
+ if (!aperture_valid(aper_base, aper_size, 64<<20)) {
+ if (valid_agp && agp_aper_base &&
+ agp_aper_base == aper_base &&
+ agp_aper_order == aper_order) {
+ /* the same between two setting from NB and agp */
+ if (!no_iommu && end_pfn > MAX_DMA32_PFN && !printed_gart_size_msg) {
+ printk(KERN_ERR "you are using iommu with agp, but GART size is less than 64M\n");
+ printk(KERN_ERR "please increase GART size in your BIOS setup\n");
+ printk(KERN_ERR "if BIOS doesn't have that option, contact your HW vendor!\n");
+ printed_gart_size_msg = 1;
+ }
+ } else {
+ fix = 1;
+ goto out;
+ }
+ }
- if ((last_aper_order && aper_order != last_aper_order) ||
- (last_aper_base && aper_base != last_aper_base)) {
- fix = 1;
- break;
+ if ((last_aper_order && aper_order != last_aper_order) ||
+ (last_aper_base && aper_base != last_aper_base)) {
+ fix = 1;
+ goto out;
+ }
+ last_aper_order = aper_order;
+ last_aper_base = aper_base;
}
- last_aper_order = aper_order;
- last_aper_base = aper_base;
}
+out:
if (!fix && !fallback_aper_force) {
if (last_aper_base) {
unsigned long n = (32 * 1024 * 1024) << last_aper_order;
@@ -364,8 +439,10 @@ void __init gart_iommu_hole_init(void)
return;
}
- if (!fallback_aper_force)
- aper_alloc = search_agp_bridge(&aper_order, &valid_agp);
+ if (!fallback_aper_force) {
+ aper_alloc = agp_aper_base;
+ aper_order = agp_aper_order;
+ }
if (aper_alloc) {
/* Got the aperture from the AGP bridge */
@@ -401,16 +478,22 @@ void __init gart_iommu_hole_init(void)
}
/* Fix up the north bridges */
- for (num = 24; num < 32; num++) {
- if (!early_is_k8_nb(read_pci_config(0, num, 3, 0x00)))
- continue;
-
- /*
- * Don't enable translation yet. That is done later.
- * Assume this BIOS didn't initialise the GART so
- * just overwrite all previous bits
- */
- write_pci_config(0, num, 3, 0x90, aper_order<<1);
- write_pci_config(0, num, 3, 0x94, aper_alloc>>25);
+ for (i = 0; i < ARRAY_SIZE(bus_dev_ranges); i++) {
+ int bus;
+ int dev_base, dev_limit;
+
+ bus = bus_dev_ranges[i].bus;
+ dev_base = bus_dev_ranges[i].dev_base;
+ dev_limit = bus_dev_ranges[i].dev_limit;
+ for (slot = dev_base; slot < dev_limit; slot++) {
+ if (!early_is_k8_nb(read_pci_config(bus, slot, 3, 0x00)))
+ continue;
+
+ /* Don't enable translation yet. That is done later.
+ Assume this BIOS didn't initialise the GART so
+ just overwrite all previous bits */
+ write_pci_config(bus, slot, 3, AMD64_GARTAPERTURECTL, aper_order << 1);
+ write_pci_config(bus, slot, 3, AMD64_GARTAPERTUREBASE, aper_alloc >> 25);
+ }
}
}
diff --git a/arch/x86/kernel/apic_32.c b/arch/x86/kernel/apic_32.c
index 4b99b1bdeb6c..84ce106b33c8 100644
--- a/arch/x86/kernel/apic_32.c
+++ b/arch/x86/kernel/apic_32.c
@@ -52,9 +52,6 @@
unsigned long mp_lapic_addr;
-DEFINE_PER_CPU(u16, x86_bios_cpu_apicid) = BAD_APICID;
-EXPORT_PER_CPU_SYMBOL(x86_bios_cpu_apicid);
-
/*
* Knob to control our willingness to enable the local APIC.
*
@@ -64,18 +61,26 @@ static int enable_local_apic __initdata;
/* Local APIC timer verification ok */
static int local_apic_timer_verify_ok;
-/* Disable local APIC timer from the kernel commandline or via dmi quirk
- or using CPU MSR check */
-int local_apic_timer_disabled;
+/* Disable local APIC timer from the kernel commandline or via dmi quirk */
+static int local_apic_timer_disabled;
/* Local APIC timer works in C2 */
int local_apic_timer_c2_ok;
EXPORT_SYMBOL_GPL(local_apic_timer_c2_ok);
+int first_system_vector = 0xfe;
+
+char system_vectors[NR_VECTORS] = { [0 ... NR_VECTORS-1] = SYS_VECTOR_FREE};
+
/*
* Debug level, exported for io_apic.c
*/
int apic_verbosity;
+int pic_mode;
+
+/* Have we found an MP table */
+int smp_found_config;
+
static unsigned int calibration_result;
static int lapic_next_event(unsigned long delta,
@@ -1154,9 +1159,6 @@ static int __init detect_init_APIC(void)
if (l & MSR_IA32_APICBASE_ENABLE)
mp_lapic_addr = l & MSR_IA32_APICBASE_BASE;
- if (nmi_watchdog != NMI_NONE && nmi_watchdog != NMI_DISABLED)
- nmi_watchdog = NMI_LOCAL_APIC;
-
printk(KERN_INFO "Found and enabled local APIC!\n");
apic_pm_activate();
@@ -1202,7 +1204,7 @@ void __init init_apic_mappings(void)
for (i = 0; i < nr_ioapics; i++) {
if (smp_found_config) {
- ioapic_phys = mp_ioapics[i].mpc_apicaddr;
+ ioapic_phys = mp_ioapics[i].mp_apicaddr;
if (!ioapic_phys) {
printk(KERN_ERR
"WARNING: bogus zero IO-APIC "
@@ -1269,6 +1271,10 @@ int __init APIC_init_uniprocessor(void)
setup_local_APIC();
+#ifdef CONFIG_X86_IO_APIC
+ if (!smp_found_config || skip_ioapic_setup || !nr_ioapics)
+#endif
+ localise_nmi_watchdog();
end_local_APIC_setup();
#ifdef CONFIG_X86_IO_APIC
if (smp_found_config)
@@ -1351,13 +1357,13 @@ void __init smp_intr_init(void)
* The reschedule interrupt is a CPU-to-CPU reschedule-helper
* IPI, driven by wakeup.
*/
- set_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt);
+ alloc_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt);
/* IPI for invalidation */
- set_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt);
+ alloc_intr_gate(INVALIDATE_TLB_VECTOR, invalidate_interrupt);
/* IPI for generic function call */
- set_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt);
+ alloc_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt);
}
#endif
@@ -1370,15 +1376,15 @@ void __init apic_intr_init(void)
smp_intr_init();
#endif
/* self generated IPI for local APIC timer */
- set_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt);
+ alloc_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt);
/* IPI vectors for APIC spurious and error interrupts */
- set_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt);
- set_intr_gate(ERROR_APIC_VECTOR, error_interrupt);
+ alloc_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt);
+ alloc_intr_gate(ERROR_APIC_VECTOR, error_interrupt);
/* thermal monitor LVT interrupt */
#ifdef CONFIG_X86_MCE_P4THERMAL
- set_intr_gate(THERMAL_APIC_VECTOR, thermal_interrupt);
+ alloc_intr_gate(THERMAL_APIC_VECTOR, thermal_interrupt);
#endif
}
@@ -1513,6 +1519,9 @@ void __cpuinit generic_processor_info(int apicid, int version)
*/
cpu = 0;
+ if (apicid > max_physical_apicid)
+ max_physical_apicid = apicid;
+
/*
* Would be preferable to switch to bigsmp when CONFIG_HOTPLUG_CPU=y
* but we need to work other dependencies like SMP_SUSPEND etc
@@ -1520,7 +1529,7 @@ void __cpuinit generic_processor_info(int apicid, int version)
* if (CPU_HOTPLUG_ENABLED || num_processors > 8)
* - Ashok Raj <ashok.raj@intel.com>
*/
- if (num_processors > 8) {
+ if (max_physical_apicid >= 8) {
switch (boot_cpu_data.x86_vendor) {
case X86_VENDOR_INTEL:
if (!APIC_XAPIC(version)) {
@@ -1534,9 +1543,9 @@ void __cpuinit generic_processor_info(int apicid, int version)
}
#ifdef CONFIG_SMP
/* are we being called early in kernel startup? */
- if (x86_cpu_to_apicid_early_ptr) {
- u16 *cpu_to_apicid = x86_cpu_to_apicid_early_ptr;
- u16 *bios_cpu_apicid = x86_bios_cpu_apicid_early_ptr;
+ if (early_per_cpu_ptr(x86_cpu_to_apicid)) {
+ u16 *cpu_to_apicid = early_per_cpu_ptr(x86_cpu_to_apicid);
+ u16 *bios_cpu_apicid = early_per_cpu_ptr(x86_bios_cpu_apicid);
cpu_to_apicid[cpu] = apicid;
bios_cpu_apicid[cpu] = apicid;
diff --git a/arch/x86/kernel/apic_64.c b/arch/x86/kernel/apic_64.c
index 0633cfd0dc29..e494809fc508 100644
--- a/arch/x86/kernel/apic_64.c
+++ b/arch/x86/kernel/apic_64.c
@@ -43,7 +43,7 @@
#include <mach_ipi.h>
#include <mach_apic.h>
-int disable_apic_timer __cpuinitdata;
+static int disable_apic_timer __cpuinitdata;
static int apic_calibrate_pmtmr __initdata;
int disable_apic;
@@ -56,6 +56,9 @@ EXPORT_SYMBOL_GPL(local_apic_timer_c2_ok);
*/
int apic_verbosity;
+/* Have we found an MP table */
+int smp_found_config;
+
static struct resource lapic_resource = {
.name = "Local APIC",
.flags = IORESOURCE_MEM | IORESOURCE_BUSY,
@@ -87,9 +90,6 @@ static unsigned long apic_phys;
unsigned long mp_lapic_addr;
-DEFINE_PER_CPU(u16, x86_bios_cpu_apicid) = BAD_APICID;
-EXPORT_PER_CPU_SYMBOL(x86_bios_cpu_apicid);
-
unsigned int __cpuinitdata maxcpus = NR_CPUS;
/*
* Get the LAPIC version
@@ -422,32 +422,8 @@ void __init setup_boot_APIC_clock(void)
setup_APIC_timer();
}
-/*
- * AMD C1E enabled CPUs have a real nasty problem: Some BIOSes set the
- * C1E flag only in the secondary CPU, so when we detect the wreckage
- * we already have enabled the boot CPU local apic timer. Check, if
- * disable_apic_timer is set and the DUMMY flag is cleared. If yes,
- * set the DUMMY flag again and force the broadcast mode in the
- * clockevents layer.
- */
-static void __cpuinit check_boot_apic_timer_broadcast(void)
-{
- if (!disable_apic_timer ||
- (lapic_clockevent.features & CLOCK_EVT_FEAT_DUMMY))
- return;
-
- printk(KERN_INFO "AMD C1E detected late. Force timer broadcast.\n");
- lapic_clockevent.features |= CLOCK_EVT_FEAT_DUMMY;
-
- local_irq_enable();
- clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_FORCE,
- &boot_cpu_physical_apicid);
- local_irq_disable();
-}
-
void __cpuinit setup_secondary_APIC_clock(void)
{
- check_boot_apic_timer_broadcast();
setup_APIC_timer();
}
@@ -875,7 +851,7 @@ static int __init detect_init_APIC(void)
void __init early_init_lapic_mapping(void)
{
- unsigned long apic_phys;
+ unsigned long phys_addr;
/*
* If no local APIC can be found then go out
@@ -884,11 +860,11 @@ void __init early_init_lapic_mapping(void)
if (!smp_found_config)
return;
- apic_phys = mp_lapic_addr;
+ phys_addr = mp_lapic_addr;
- set_fixmap_nocache(FIX_APIC_BASE, apic_phys);
+ set_fixmap_nocache(FIX_APIC_BASE, phys_addr);
apic_printk(APIC_VERBOSE, "mapped APIC to %16lx (%16lx)\n",
- APIC_BASE, apic_phys);
+ APIC_BASE, phys_addr);
/*
* Fetch the APIC ID of the BSP in case we have a
@@ -954,6 +930,8 @@ int __init APIC_init_uniprocessor(void)
if (!skip_ioapic_setup && nr_ioapics)
enable_IO_APIC();
+ if (!smp_found_config || skip_ioapic_setup || !nr_ioapics)
+ localise_nmi_watchdog();
end_local_APIC_setup();
if (smp_found_config && !skip_ioapic_setup && nr_ioapics)
@@ -1090,10 +1068,13 @@ void __cpuinit generic_processor_info(int apicid, int version)
*/
cpu = 0;
}
+ if (apicid > max_physical_apicid)
+ max_physical_apicid = apicid;
+
/* are we being called early in kernel startup? */
- if (x86_cpu_to_apicid_early_ptr) {
- u16 *cpu_to_apicid = x86_cpu_to_apicid_early_ptr;
- u16 *bios_cpu_apicid = x86_bios_cpu_apicid_early_ptr;
+ if (early_per_cpu_ptr(x86_cpu_to_apicid)) {
+ u16 *cpu_to_apicid = early_per_cpu_ptr(x86_cpu_to_apicid);
+ u16 *bios_cpu_apicid = early_per_cpu_ptr(x86_bios_cpu_apicid);
cpu_to_apicid[cpu] = apicid;
bios_cpu_apicid[cpu] = apicid;
@@ -1269,7 +1250,7 @@ __cpuinit int apic_is_clustered_box(void)
if ((boot_cpu_data.x86_vendor == X86_VENDOR_AMD) && !is_vsmp_box())
return 0;
- bios_cpu_apicid = x86_bios_cpu_apicid_early_ptr;
+ bios_cpu_apicid = early_per_cpu_ptr(x86_bios_cpu_apicid);
bitmap_zero(clustermap, NUM_APIC_CLUSTERS);
for (i = 0; i < NR_CPUS; i++) {
diff --git a/arch/x86/kernel/apm_32.c b/arch/x86/kernel/apm_32.c
index bf9290e29013..45e1a08c5aa0 100644
--- a/arch/x86/kernel/apm_32.c
+++ b/arch/x86/kernel/apm_32.c
@@ -228,6 +228,7 @@
#include <linux/suspend.h>
#include <linux/kthread.h>
#include <linux/jiffies.h>
+#include <linux/smp_lock.h>
#include <asm/system.h>
#include <asm/uaccess.h>
@@ -1149,7 +1150,7 @@ static void queue_event(apm_event_t event, struct apm_user *sender)
as->event_tail = 0;
}
as->events[as->event_head] = event;
- if ((!as->suser) || (!as->writer))
+ if (!as->suser || !as->writer)
continue;
switch (event) {
case APM_SYS_SUSPEND:
@@ -1211,9 +1212,9 @@ static int suspend(int vetoable)
if (err != APM_SUCCESS)
apm_error("suspend", err);
err = (err == APM_SUCCESS) ? 0 : -EIO;
- device_power_up();
+ device_power_up(PMSG_RESUME);
local_irq_enable();
- device_resume();
+ device_resume(PMSG_RESUME);
queue_event(APM_NORMAL_RESUME, NULL);
spin_lock(&user_list_lock);
for (as = user_list; as != NULL; as = as->next) {
@@ -1238,7 +1239,7 @@ static void standby(void)
apm_error("standby", err);
local_irq_disable();
- device_power_up();
+ device_power_up(PMSG_RESUME);
local_irq_enable();
}
@@ -1324,7 +1325,7 @@ static void check_events(void)
ignore_bounce = 1;
if ((event != APM_NORMAL_RESUME)
|| (ignore_normal_resume == 0)) {
- device_resume();
+ device_resume(PMSG_RESUME);
queue_event(event, NULL);
}
ignore_normal_resume = 0;
@@ -1396,7 +1397,7 @@ static void apm_mainloop(void)
static int check_apm_user(struct apm_user *as, const char *func)
{
- if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
+ if (as == NULL || as->magic != APM_BIOS_MAGIC) {
printk(KERN_ERR "apm: %s passed bad filp\n", func);
return 1;
}
@@ -1459,18 +1460,19 @@ static unsigned int do_poll(struct file *fp, poll_table *wait)
return 0;
}
-static int do_ioctl(struct inode *inode, struct file *filp,
- u_int cmd, u_long arg)
+static long do_ioctl(struct file *filp, u_int cmd, u_long arg)
{
struct apm_user *as;
+ int ret;
as = filp->private_data;
if (check_apm_user(as, "ioctl"))
return -EIO;
- if ((!as->suser) || (!as->writer))
+ if (!as->suser || !as->writer)
return -EPERM;
switch (cmd) {
case APM_IOC_STANDBY:
+ lock_kernel();
if (as->standbys_read > 0) {
as->standbys_read--;
as->standbys_pending--;
@@ -1479,8 +1481,10 @@ static int do_ioctl(struct inode *inode, struct file *filp,
queue_event(APM_USER_STANDBY, as);
if (standbys_pending <= 0)
standby();
+ unlock_kernel();
break;
case APM_IOC_SUSPEND:
+ lock_kernel();
if (as->suspends_read > 0) {
as->suspends_read--;
as->suspends_pending--;
@@ -1488,16 +1492,17 @@ static int do_ioctl(struct inode *inode, struct file *filp,
} else
queue_event(APM_USER_SUSPEND, as);
if (suspends_pending <= 0) {
- return suspend(1);
+ ret = suspend(1);
} else {
as->suspend_wait = 1;
wait_event_interruptible(apm_suspend_waitqueue,
as->suspend_wait == 0);
- return as->suspend_result;
+ ret = as->suspend_result;
}
- break;
+ unlock_kernel();
+ return ret;
default:
- return -EINVAL;
+ return -ENOTTY;
}
return 0;
}
@@ -1860,7 +1865,7 @@ static const struct file_operations apm_bios_fops = {
.owner = THIS_MODULE,
.read = do_read,
.poll = do_poll,
- .ioctl = do_ioctl,
+ .unlocked_ioctl = do_ioctl,
.open = do_open,
.release = do_release,
};
diff --git a/arch/x86/kernel/cpu/Makefile b/arch/x86/kernel/cpu/Makefile
index a0c6f8190887..65b1be5fe9ce 100644
--- a/arch/x86/kernel/cpu/Makefile
+++ b/arch/x86/kernel/cpu/Makefile
@@ -6,11 +6,15 @@ obj-y := intel_cacheinfo.o addon_cpuid_features.o
obj-y += proc.o feature_names.o
obj-$(CONFIG_X86_32) += common.o bugs.o
+obj-$(CONFIG_X86_64) += bugs_64.o
obj-$(CONFIG_X86_32) += amd.o
+obj-$(CONFIG_X86_64) += amd_64.o
obj-$(CONFIG_X86_32) += cyrix.o
obj-$(CONFIG_X86_32) += centaur.o
+obj-$(CONFIG_X86_64) += centaur_64.o
obj-$(CONFIG_X86_32) += transmeta.o
obj-$(CONFIG_X86_32) += intel.o
+obj-$(CONFIG_X86_64) += intel_64.o
obj-$(CONFIG_X86_32) += umc.o
obj-$(CONFIG_X86_MCE) += mcheck/
diff --git a/arch/x86/kernel/cpu/addon_cpuid_features.c b/arch/x86/kernel/cpu/addon_cpuid_features.c
index c2e1ce33c7cb..d8b3e4a9d663 100644
--- a/arch/x86/kernel/cpu/addon_cpuid_features.c
+++ b/arch/x86/kernel/cpu/addon_cpuid_features.c
@@ -62,6 +62,9 @@ void __cpuinit validate_pat_support(struct cpuinfo_x86 *c)
if (c->x86 == 0xF || (c->x86 == 6 && c->x86_model >= 15))
return;
break;
+ case X86_VENDOR_CENTAUR:
+ case X86_VENDOR_TRANSMETA:
+ return;
}
pat_disable(cpu_has_pat ?
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index 245866828294..acc891ae5901 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -24,43 +24,6 @@
extern void vide(void);
__asm__(".align 4\nvide: ret");
-#ifdef CONFIG_X86_LOCAL_APIC
-#define ENABLE_C1E_MASK 0x18000000
-#define CPUID_PROCESSOR_SIGNATURE 1
-#define CPUID_XFAM 0x0ff00000
-#define CPUID_XFAM_K8 0x00000000
-#define CPUID_XFAM_10H 0x00100000
-#define CPUID_XFAM_11H 0x00200000
-#define CPUID_XMOD 0x000f0000
-#define CPUID_XMOD_REV_F 0x00040000
-
-/* AMD systems with C1E don't have a working lAPIC timer. Check for that. */
-static __cpuinit int amd_apic_timer_broken(void)
-{
- u32 lo, hi;
- u32 eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
- switch (eax & CPUID_XFAM) {
- case CPUID_XFAM_K8:
- if ((eax & CPUID_XMOD) < CPUID_XMOD_REV_F)
- break;
- case CPUID_XFAM_10H:
- case CPUID_XFAM_11H:
- rdmsr(MSR_K8_ENABLE_C1E, lo, hi);
- if (lo & ENABLE_C1E_MASK) {
- if (smp_processor_id() != boot_cpu_physical_apicid)
- printk(KERN_INFO "AMD C1E detected late. "
- " Force timer broadcast.\n");
- return 1;
- }
- break;
- default:
- /* err on the side of caution */
- return 1;
- }
- return 0;
-}
-#endif
-
int force_mwait __cpuinitdata;
static void __cpuinit early_init_amd(struct cpuinfo_x86 *c)
@@ -297,17 +260,15 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c)
num_cache_leaves = 3;
}
-#ifdef CONFIG_X86_LOCAL_APIC
- if (amd_apic_timer_broken())
- local_apic_timer_disabled = 1;
-#endif
-
/* K6s reports MCEs but don't actually have all the MSRs */
if (c->x86 < 6)
clear_cpu_cap(c, X86_FEATURE_MCE);
if (cpu_has_xmm2)
set_cpu_cap(c, X86_FEATURE_MFENCE_RDTSC);
+
+ if (c->x86 == 0x10)
+ amd_enable_pci_ext_cfg(c);
}
static unsigned int __cpuinit amd_size_cache(struct cpuinfo_x86 *c, unsigned int size)
diff --git a/arch/x86/kernel/cpu/amd_64.c b/arch/x86/kernel/cpu/amd_64.c
new file mode 100644
index 000000000000..f8d20588bde9
--- /dev/null
+++ b/arch/x86/kernel/cpu/amd_64.c
@@ -0,0 +1,206 @@
+#include <linux/init.h>
+#include <linux/mm.h>
+
+#include <asm/numa_64.h>
+#include <asm/mmconfig.h>
+#include <asm/cacheflush.h>
+
+#include <mach_apic.h>
+#include "cpu.h"
+
+extern int __cpuinit get_model_name(struct cpuinfo_x86 *c);
+extern void __cpuinit display_cacheinfo(struct cpuinfo_x86 *c);
+
+int force_mwait __cpuinitdata;
+
+#ifdef CONFIG_NUMA
+static int __cpuinit nearby_node(int apicid)
+{
+ int i, node;
+
+ for (i = apicid - 1; i >= 0; i--) {
+ node = apicid_to_node[i];
+ if (node != NUMA_NO_NODE && node_online(node))
+ return node;
+ }
+ for (i = apicid + 1; i < MAX_LOCAL_APIC; i++) {
+ node = apicid_to_node[i];
+ if (node != NUMA_NO_NODE && node_online(node))
+ return node;
+ }
+ return first_node(node_online_map); /* Shouldn't happen */
+}
+#endif
+
+/*
+ * On a AMD dual core setup the lower bits of the APIC id distingush the cores.
+ * Assumes number of cores is a power of two.
+ */
+static void __cpuinit amd_detect_cmp(struct cpuinfo_x86 *c)
+{
+#ifdef CONFIG_SMP
+ unsigned bits;
+#ifdef CONFIG_NUMA
+ int cpu = smp_processor_id();
+ int node = 0;
+ unsigned apicid = hard_smp_processor_id();
+#endif
+ bits = c->x86_coreid_bits;
+
+ /* Low order bits define the core id (index of core in socket) */
+ c->cpu_core_id = c->initial_apicid & ((1 << bits)-1);
+ /* Convert the initial APIC ID into the socket ID */
+ c->phys_proc_id = c->initial_apicid >> bits;
+
+#ifdef CONFIG_NUMA
+ node = c->phys_proc_id;
+ if (apicid_to_node[apicid] != NUMA_NO_NODE)
+ node = apicid_to_node[apicid];
+ if (!node_online(node)) {
+ /* Two possibilities here:
+ - The CPU is missing memory and no node was created.
+ In that case try picking one from a nearby CPU
+ - The APIC IDs differ from the HyperTransport node IDs
+ which the K8 northbridge parsing fills in.
+ Assume they are all increased by a constant offset,
+ but in the same order as the HT nodeids.
+ If that doesn't result in a usable node fall back to the
+ path for the previous case. */
+
+ int ht_nodeid = c->initial_apicid;
+
+ if (ht_nodeid >= 0 &&
+ apicid_to_node[ht_nodeid] != NUMA_NO_NODE)
+ node = apicid_to_node[ht_nodeid];
+ /* Pick a nearby node */
+ if (!node_online(node))
+ node = nearby_node(apicid);
+ }
+ numa_set_node(cpu, node);
+
+ printk(KERN_INFO "CPU %d/%x -> Node %d\n", cpu, apicid, node);
+#endif
+#endif
+}
+
+static void __cpuinit early_init_amd_mc(struct cpuinfo_x86 *c)
+{
+#ifdef CONFIG_SMP
+ unsigned bits, ecx;
+
+ /* Multi core CPU? */
+ if (c->extended_cpuid_level < 0x80000008)
+ return;
+
+ ecx = cpuid_ecx(0x80000008);
+
+ c->x86_max_cores = (ecx & 0xff) + 1;
+
+ /* CPU telling us the core id bits shift? */
+ bits = (ecx >> 12) & 0xF;
+
+ /* Otherwise recompute */
+ if (bits == 0) {
+ while ((1 << bits) < c->x86_max_cores)
+ bits++;
+ }
+
+ c->x86_coreid_bits = bits;
+
+#endif
+}
+
+void __cpuinit early_init_amd(struct cpuinfo_x86 *c)
+{
+ early_init_amd_mc(c);
+
+ /* c->x86_power is 8000_0007 edx. Bit 8 is constant TSC */
+ if (c->x86_power & (1<<8))
+ set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC);
+}
+
+void __cpuinit init_amd(struct cpuinfo_x86 *c)
+{
+ unsigned level;
+
+#ifdef CONFIG_SMP
+ unsigned long value;
+
+ /*
+ * Disable TLB flush filter by setting HWCR.FFDIS on K8
+ * bit 6 of msr C001_0015
+ *
+ * Errata 63 for SH-B3 steppings
+ * Errata 122 for all steppings (F+ have it disabled by default)
+ */
+ if (c->x86 == 15) {
+ rdmsrl(MSR_K8_HWCR, value);
+ value |= 1 << 6;
+ wrmsrl(MSR_K8_HWCR, value);
+ }
+#endif
+
+ /* Bit 31 in normal CPUID used for nonstandard 3DNow ID;
+ 3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway */
+ clear_cpu_cap(c, 0*32+31);
+
+ /* On C+ stepping K8 rep microcode works well for copy/memset */
+ level = cpuid_eax(1);
+ if (c->x86 == 15 && ((level >= 0x0f48 && level < 0x0f50) ||
+ level >= 0x0f58))
+ set_cpu_cap(c, X86_FEATURE_REP_GOOD);
+ if (c->x86 == 0x10 || c->x86 == 0x11)
+ set_cpu_cap(c, X86_FEATURE_REP_GOOD);
+
+ /* Enable workaround for FXSAVE leak */
+ if (c->x86 >= 6)
+ set_cpu_cap(c, X86_FEATURE_FXSAVE_LEAK);
+
+ level = get_model_name(c);
+ if (!level) {
+ switch (c->x86) {
+ case 15:
+ /* Should distinguish Models here, but this is only
+ a fallback anyways. */
+ strcpy(c->x86_model_id, "Hammer");
+ break;
+ }
+ }
+ display_cacheinfo(c);
+
+ /* Multi core CPU? */
+ if (c->extended_cpuid_level >= 0x80000008)
+ amd_detect_cmp(c);
+
+ if (c->extended_cpuid_level >= 0x80000006 &&
+ (cpuid_edx(0x80000006) & 0xf000))
+ num_cache_leaves = 4;
+ else
+ num_cache_leaves = 3;
+
+ if (c->x86 == 0xf || c->x86 == 0x10 || c->x86 == 0x11)
+ set_cpu_cap(c, X86_FEATURE_K8);
+
+ /* MFENCE stops RDTSC speculation */
+ set_cpu_cap(c, X86_FEATURE_MFENCE_RDTSC);
+
+ if (c->x86 == 0x10)
+ fam10h_check_enable_mmcfg();
+
+ if (c->x86 == 0x10)
+ amd_enable_pci_ext_cfg(c);
+
+ if (c == &boot_cpu_data && c->x86 >= 0xf && c->x86 <= 0x11) {
+ unsigned long long tseg;
+
+ /*
+ * Split up direct mapping around the TSEG SMM area.
+ * Don't do it for gbpages because there seems very little
+ * benefit in doing so.
+ */
+ if (!rdmsrl_safe(MSR_K8_TSEG_ADDR, &tseg) &&
+ (tseg >> PMD_SHIFT) <
+ (max_pfn_mapped >> (PMD_SHIFT-PAGE_SHIFT)))
+ set_memory_4k((unsigned long)__va(tseg), 1);
+ }
+}
diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
index 170d2f5523b2..1b1c56bb338f 100644
--- a/arch/x86/kernel/cpu/bugs.c
+++ b/arch/x86/kernel/cpu/bugs.c
@@ -59,8 +59,12 @@ static void __init check_fpu(void)
return;
}
-/* trap_init() enabled FXSR and company _before_ testing for FP problems here. */
- /* Test for the divl bug.. */
+ /*
+ * trap_init() enabled FXSR and company _before_ testing for FP
+ * problems here.
+ *
+ * Test for the divl bug..
+ */
__asm__("fninit\n\t"
"fldl %1\n\t"
"fdivl %2\n\t"
@@ -108,10 +112,15 @@ static void __init check_popad(void)
"movl $12345678,%%eax; movl $0,%%edi; pusha; popa; movl (%%edx,%%edi),%%ecx "
: "=&a" (res)
: "d" (inp)
- : "ecx", "edi" );
- /* If this fails, it means that any user program may lock the CPU hard. Too bad. */
- if (res != 12345678) printk( "Buggy.\n" );
- else printk( "OK.\n" );
+ : "ecx", "edi");
+ /*
+ * If this fails, it means that any user program may lock the
+ * CPU hard. Too bad.
+ */
+ if (res != 12345678)
+ printk("Buggy.\n");
+ else
+ printk("OK.\n");
#endif
}
@@ -137,7 +146,8 @@ static void __init check_config(void)
* i486+ only features! (WP works in supervisor mode and the
* new "invlpg" and "bswap" instructions)
*/
-#if defined(CONFIG_X86_WP_WORKS_OK) || defined(CONFIG_X86_INVLPG) || defined(CONFIG_X86_BSWAP)
+#if defined(CONFIG_X86_WP_WORKS_OK) || defined(CONFIG_X86_INVLPG) || \
+ defined(CONFIG_X86_BSWAP)
if (boot_cpu_data.x86 == 3)
panic("Kernel requires i486+ for 'invlpg' and other features");
#endif
@@ -170,6 +180,7 @@ void __init check_bugs(void)
check_fpu();
check_hlt();
check_popad();
- init_utsname()->machine[1] = '0' + (boot_cpu_data.x86 > 6 ? 6 : boot_cpu_data.x86);
+ init_utsname()->machine[1] =
+ '0' + (boot_cpu_data.x86 > 6 ? 6 : boot_cpu_data.x86);
alternative_instructions();
}
diff --git a/arch/x86/kernel/bugs_64.c b/arch/x86/kernel/cpu/bugs_64.c
index 9a3ed0649d4e..9a3ed0649d4e 100644
--- a/arch/x86/kernel/bugs_64.c
+++ b/arch/x86/kernel/cpu/bugs_64.c
diff --git a/arch/x86/kernel/cpu/centaur_64.c b/arch/x86/kernel/cpu/centaur_64.c
new file mode 100644
index 000000000000..bac96d187d05
--- /dev/null
+++ b/arch/x86/kernel/cpu/centaur_64.c
@@ -0,0 +1,31 @@
+#include <linux/init.h>
+#include <linux/smp.h>
+
+#include <asm/cpufeature.h>
+#include <asm/processor.h>
+
+void __cpuinit early_init_centaur(struct cpuinfo_x86 *c)
+{
+ if (c->x86 == 0x6 && c->x86_model >= 0xf)
+ set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC);
+}
+
+void __cpuinit init_centaur(struct cpuinfo_x86 *c)
+{
+ /* Cache sizes */
+ unsigned n;
+
+ n = c->extended_cpuid_level;
+ if (n >= 0x80000008) {
+ unsigned eax = cpuid_eax(0x80000008);
+ c->x86_virt_bits = (eax >> 8) & 0xff;
+ c->x86_phys_bits = eax & 0xff;
+ }
+
+ if (c->x86 == 0x6 && c->x86_model >= 0xf) {
+ c->x86_cache_alignment = c->x86_clflush_size * 2;
+ set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC);
+ set_cpu_cap(c, X86_FEATURE_REP_GOOD);
+ }
+ set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC);
+}
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index d0463a946247..b2f54fafb8bc 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -21,7 +21,7 @@
#include "cpu.h"
-DEFINE_PER_CPU(struct gdt_page, gdt_page) = { .gdt = {
+DEFINE_PER_CPU_PAGE_ALIGNED(struct gdt_page, gdt_page) = { .gdt = {
[GDT_ENTRY_KERNEL_CS] = { { { 0x0000ffff, 0x00cf9a00 } } },
[GDT_ENTRY_KERNEL_DS] = { { { 0x0000ffff, 0x00cf9200 } } },
[GDT_ENTRY_DEFAULT_USER_CS] = { { { 0x0000ffff, 0x00cffa00 } } },
diff --git a/arch/x86/kernel/cpu/cpu.h b/arch/x86/kernel/cpu/cpu.h
index 783691b2a738..f5d5bb1b5541 100644
--- a/arch/x86/kernel/cpu/cpu.h
+++ b/arch/x86/kernel/cpu/cpu.h
@@ -1,3 +1,4 @@
+#ifdef CONFIG_X86_32
struct cpu_model_info {
int vendor;
@@ -36,3 +37,7 @@ extern struct cpu_vendor_dev __x86cpuvendor_start[], __x86cpuvendor_end[];
extern int get_model_name(struct cpuinfo_x86 *c);
extern void display_cacheinfo(struct cpuinfo_x86 *c);
+
+#endif /* CONFIG_X86_32 */
+
+extern void __cpuinit amd_enable_pci_ext_cfg(struct cpuinfo_x86 *c);
diff --git a/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c b/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c
index b0c8208df9fa..dd097b835839 100644
--- a/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c
+++ b/arch/x86/kernel/cpu/cpufreq/acpi-cpufreq.c
@@ -202,7 +202,7 @@ static void drv_write(struct drv_cmd *cmd)
cpumask_t saved_mask = current->cpus_allowed;
unsigned int i;
- for_each_cpu_mask(i, cmd->mask) {
+ for_each_cpu_mask_nr(i, cmd->mask) {
set_cpus_allowed_ptr(current, &cpumask_of_cpu(i));
do_drv_write(cmd);
}
@@ -451,7 +451,7 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy,
freqs.old = perf->states[perf->state].core_frequency * 1000;
freqs.new = data->freq_table[next_state].frequency;
- for_each_cpu_mask(i, cmd.mask) {
+ for_each_cpu_mask_nr(i, cmd.mask) {
freqs.cpu = i;
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
}
@@ -466,7 +466,7 @@ static int acpi_cpufreq_target(struct cpufreq_policy *policy,
}
}
- for_each_cpu_mask(i, cmd.mask) {
+ for_each_cpu_mask_nr(i, cmd.mask) {
freqs.cpu = i;
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
}
diff --git a/arch/x86/kernel/cpu/cpufreq/p4-clockmod.c b/arch/x86/kernel/cpu/cpufreq/p4-clockmod.c
index 199e4e05e5dc..f1685fb91fbd 100644
--- a/arch/x86/kernel/cpu/cpufreq/p4-clockmod.c
+++ b/arch/x86/kernel/cpu/cpufreq/p4-clockmod.c
@@ -122,7 +122,7 @@ static int cpufreq_p4_target(struct cpufreq_policy *policy,
return 0;
/* notifiers */
- for_each_cpu_mask(i, policy->cpus) {
+ for_each_cpu_mask_nr(i, policy->cpus) {
freqs.cpu = i;
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
}
@@ -130,11 +130,11 @@ static int cpufreq_p4_target(struct cpufreq_policy *policy,
/* run on each logical CPU, see section 13.15.3 of IA32 Intel Architecture Software
* Developer's Manual, Volume 3
*/
- for_each_cpu_mask(i, policy->cpus)
+ for_each_cpu_mask_nr(i, policy->cpus)
cpufreq_p4_setdc(i, p4clockmod_table[newstate].index);
/* notifiers */
- for_each_cpu_mask(i, policy->cpus) {
+ for_each_cpu_mask_nr(i, policy->cpus) {
freqs.cpu = i;
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
}
diff --git a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c
index 206791eb46e3..c45ca6d4dce1 100644
--- a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c
+++ b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c
@@ -966,7 +966,7 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data, unsigned i
freqs.old = find_khz_freq_from_fid(data->currfid);
freqs.new = find_khz_freq_from_fid(fid);
- for_each_cpu_mask(i, *(data->available_cores)) {
+ for_each_cpu_mask_nr(i, *(data->available_cores)) {
freqs.cpu = i;
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
}
@@ -974,7 +974,7 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data, unsigned i
res = transition_fid_vid(data, fid, vid);
freqs.new = find_khz_freq_from_fid(data->currfid);
- for_each_cpu_mask(i, *(data->available_cores)) {
+ for_each_cpu_mask_nr(i, *(data->available_cores)) {
freqs.cpu = i;
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
}
@@ -997,7 +997,7 @@ static int transition_frequency_pstate(struct powernow_k8_data *data, unsigned i
freqs.old = find_khz_freq_from_pstate(data->powernow_table, data->currpstate);
freqs.new = find_khz_freq_from_pstate(data->powernow_table, pstate);
- for_each_cpu_mask(i, *(data->available_cores)) {
+ for_each_cpu_mask_nr(i, *(data->available_cores)) {
freqs.cpu = i;
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
}
@@ -1005,7 +1005,7 @@ static int transition_frequency_pstate(struct powernow_k8_data *data, unsigned i
res = transition_pstate(data, pstate);
freqs.new = find_khz_freq_from_pstate(data->powernow_table, pstate);
- for_each_cpu_mask(i, *(data->available_cores)) {
+ for_each_cpu_mask_nr(i, *(data->available_cores)) {
freqs.cpu = i;
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
}
diff --git a/arch/x86/kernel/cpu/cpufreq/speedstep-centrino.c b/arch/x86/kernel/cpu/cpufreq/speedstep-centrino.c
index 908dd347c67e..8b0dd6f2a1ac 100644
--- a/arch/x86/kernel/cpu/cpufreq/speedstep-centrino.c
+++ b/arch/x86/kernel/cpu/cpufreq/speedstep-centrino.c
@@ -476,7 +476,7 @@ static int centrino_target (struct cpufreq_policy *policy,
saved_mask = current->cpus_allowed;
first_cpu = 1;
cpus_clear(covered_cpus);
- for_each_cpu_mask(j, online_policy_cpus) {
+ for_each_cpu_mask_nr(j, online_policy_cpus) {
/*
* Support for SMP systems.
* Make sure we are running on CPU that wants to change freq
@@ -517,7 +517,7 @@ static int centrino_target (struct cpufreq_policy *policy,
dprintk("target=%dkHz old=%d new=%d msr=%04x\n",
target_freq, freqs.old, freqs.new, msr);
- for_each_cpu_mask(k, online_policy_cpus) {
+ for_each_cpu_mask_nr(k, online_policy_cpus) {
freqs.cpu = k;
cpufreq_notify_transition(&freqs,
CPUFREQ_PRECHANGE);
@@ -540,7 +540,7 @@ static int centrino_target (struct cpufreq_policy *policy,
preempt_enable();
}
- for_each_cpu_mask(k, online_policy_cpus) {
+ for_each_cpu_mask_nr(k, online_policy_cpus) {
freqs.cpu = k;
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
}
@@ -554,7 +554,7 @@ static int centrino_target (struct cpufreq_policy *policy,
*/
if (!cpus_empty(covered_cpus)) {
- for_each_cpu_mask(j, covered_cpus) {
+ for_each_cpu_mask_nr(j, covered_cpus) {
set_cpus_allowed_ptr(current,
&cpumask_of_cpu(j));
wrmsr(MSR_IA32_PERF_CTL, oldmsr, h);
@@ -564,7 +564,7 @@ static int centrino_target (struct cpufreq_policy *policy,
tmp = freqs.new;
freqs.new = freqs.old;
freqs.old = tmp;
- for_each_cpu_mask(j, online_policy_cpus) {
+ for_each_cpu_mask_nr(j, online_policy_cpus) {
freqs.cpu = j;
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
diff --git a/arch/x86/kernel/cpu/cpufreq/speedstep-ich.c b/arch/x86/kernel/cpu/cpufreq/speedstep-ich.c
index 1b50244b1fdf..191f7263c61d 100644
--- a/arch/x86/kernel/cpu/cpufreq/speedstep-ich.c
+++ b/arch/x86/kernel/cpu/cpufreq/speedstep-ich.c
@@ -279,7 +279,7 @@ static int speedstep_target (struct cpufreq_policy *policy,
cpus_allowed = current->cpus_allowed;
- for_each_cpu_mask(i, policy->cpus) {
+ for_each_cpu_mask_nr(i, policy->cpus) {
freqs.cpu = i;
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
}
@@ -292,7 +292,7 @@ static int speedstep_target (struct cpufreq_policy *policy,
/* allow to be run on all CPUs */
set_cpus_allowed_ptr(current, &cpus_allowed);
- for_each_cpu_mask(i, policy->cpus) {
+ for_each_cpu_mask_nr(i, policy->cpus) {
freqs.cpu = i;
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
}
diff --git a/arch/x86/kernel/cpu/intel_64.c b/arch/x86/kernel/cpu/intel_64.c
new file mode 100644
index 000000000000..b33912199484
--- /dev/null
+++ b/arch/x86/kernel/cpu/intel_64.c
@@ -0,0 +1,92 @@
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <asm/processor.h>
+#include <asm/ptrace.h>
+#include <asm/topology.h>
+#include <asm/numa_64.h>
+
+void __cpuinit early_init_intel(struct cpuinfo_x86 *c)
+{
+ if ((c->x86 == 0xf && c->x86_model >= 0x03) ||
+ (c->x86 == 0x6 && c->x86_model >= 0x0e))
+ set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC);
+}
+
+/*
+ * find out the number of processor cores on the die
+ */
+static int __cpuinit intel_num_cpu_cores(struct cpuinfo_x86 *c)
+{
+ unsigned int eax, t;
+
+ if (c->cpuid_level < 4)
+ return 1;
+
+ cpuid_count(4, 0, &eax, &t, &t, &t);
+
+ if (eax & 0x1f)
+ return ((eax >> 26) + 1);
+ else
+ return 1;
+}
+
+static void __cpuinit srat_detect_node(void)
+{
+#ifdef CONFIG_NUMA
+ unsigned node;
+ int cpu = smp_processor_id();
+ int apicid = hard_smp_processor_id();
+
+ /* Don't do the funky fallback heuristics the AMD version employs
+ for now. */
+ node = apicid_to_node[apicid];
+ if (node == NUMA_NO_NODE || !node_online(node))
+ node = first_node(node_online_map);
+ numa_set_node(cpu, node);
+
+ printk(KERN_INFO "CPU %d/%x -> Node %d\n", cpu, apicid, node);
+#endif
+}
+
+void __cpuinit init_intel(struct cpuinfo_x86 *c)
+{
+ /* Cache sizes */
+ unsigned n;
+
+ init_intel_cacheinfo(c);
+ if (c->cpuid_level > 9) {
+ unsigned eax = cpuid_eax(10);
+ /* Check for version and the number of counters */
+ if ((eax & 0xff) && (((eax>>8) & 0xff) > 1))
+ set_cpu_cap(c, X86_FEATURE_ARCH_PERFMON);
+ }
+
+ if (cpu_has_ds) {
+ unsigned int l1, l2;
+ rdmsr(MSR_IA32_MISC_ENABLE, l1, l2);
+ if (!(l1 & (1<<11)))
+ set_cpu_cap(c, X86_FEATURE_BTS);
+ if (!(l1 & (1<<12)))
+ set_cpu_cap(c, X86_FEATURE_PEBS);
+ }
+
+
+ if (cpu_has_bts)
+ ds_init_intel(c);
+
+ n = c->extended_cpuid_level;
+ if (n >= 0x80000008) {
+ unsigned eax = cpuid_eax(0x80000008);
+ c->x86_virt_bits = (eax >> 8) & 0xff;
+ c->x86_phys_bits = eax & 0xff;
+ }
+
+ if (c->x86 == 15)
+ c->x86_cache_alignment = c->x86_clflush_size * 2;
+ if (c->x86 == 6)
+ set_cpu_cap(c, X86_FEATURE_REP_GOOD);
+ set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC);
+ c->x86_max_cores = intel_num_cpu_cores(c);
+
+ srat_detect_node();
+}
diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c
index 26d615dcb149..a7b0f8f1736b 100644
--- a/arch/x86/kernel/cpu/intel_cacheinfo.c
+++ b/arch/x86/kernel/cpu/intel_cacheinfo.c
@@ -62,6 +62,7 @@ static struct _cache_table cache_table[] __cpuinitdata =
{ 0x4b, LVL_3, 8192 }, /* 16-way set assoc, 64 byte line size */
{ 0x4c, LVL_3, 12288 }, /* 12-way set assoc, 64 byte line size */
{ 0x4d, LVL_3, 16384 }, /* 16-way set assoc, 64 byte line size */
+ { 0x4e, LVL_2, 6144 }, /* 24-way set assoc, 64 byte line size */
{ 0x60, LVL_1_DATA, 16 }, /* 8-way set assoc, sectored cache, 64 byte line size */
{ 0x66, LVL_1_DATA, 8 }, /* 4-way set assoc, sectored cache, 64 byte line size */
{ 0x67, LVL_1_DATA, 16 }, /* 4-way set assoc, sectored cache, 64 byte line size */
@@ -488,7 +489,7 @@ static void __cpuinit cache_remove_shared_cpu_map(unsigned int cpu, int index)
int sibling;
this_leaf = CPUID4_INFO_IDX(cpu, index);
- for_each_cpu_mask(sibling, this_leaf->shared_cpu_map) {
+ for_each_cpu_mask_nr(sibling, this_leaf->shared_cpu_map) {
sibling_leaf = CPUID4_INFO_IDX(sibling, index);
cpu_clear(cpu, sibling_leaf->shared_cpu_map);
}
diff --git a/arch/x86/kernel/cpu/mcheck/mce_64.c b/arch/x86/kernel/cpu/mcheck/mce_64.c
index e07e8c068ae0..f1f3f5e163b7 100644
--- a/arch/x86/kernel/cpu/mcheck/mce_64.c
+++ b/arch/x86/kernel/cpu/mcheck/mce_64.c
@@ -31,7 +31,7 @@
#include <asm/idle.h>
#define MISC_MCELOG_MINOR 227
-#define NR_BANKS 6
+#define NR_SYSFS_BANKS 6
atomic_t mce_entry;
@@ -46,7 +46,7 @@ static int mce_dont_init;
*/
static int tolerant = 1;
static int banks;
-static unsigned long bank[NR_BANKS] = { [0 ... NR_BANKS-1] = ~0UL };
+static unsigned long bank[NR_SYSFS_BANKS] = { [0 ... NR_SYSFS_BANKS-1] = ~0UL };
static unsigned long notify_user;
static int rip_msr;
static int mce_bootlog = -1;
@@ -209,7 +209,7 @@ void do_machine_check(struct pt_regs * regs, long error_code)
barrier();
for (i = 0; i < banks; i++) {
- if (!bank[i])
+ if (i < NR_SYSFS_BANKS && !bank[i])
continue;
m.misc = 0;
@@ -444,9 +444,10 @@ static void mce_init(void *dummy)
rdmsrl(MSR_IA32_MCG_CAP, cap);
banks = cap & 0xff;
- if (banks > NR_BANKS) {
- printk(KERN_INFO "MCE: warning: using only %d banks\n", banks);
- banks = NR_BANKS;
+ if (banks > MCE_EXTENDED_BANK) {
+ printk(KERN_INFO "MCE: warning: using only %d banks\n",
+ MCE_EXTENDED_BANK);
+ banks = MCE_EXTENDED_BANK;
}
/* Use accurate RIP reporting if available. */
if ((cap & (1<<9)) && ((cap >> 16) & 0xff) >= 9)
@@ -462,7 +463,7 @@ static void mce_init(void *dummy)
wrmsr(MSR_IA32_MCG_CTL, 0xffffffff, 0xffffffff);
for (i = 0; i < banks; i++) {
- wrmsrl(MSR_IA32_MC0_CTL+4*i, bank[i]);
+ wrmsrl(MSR_IA32_MC0_CTL+4*i, ~0UL);
wrmsrl(MSR_IA32_MC0_STATUS+4*i, 0);
}
}
@@ -766,7 +767,10 @@ DEFINE_PER_CPU(struct sys_device, device_mce);
} \
static SYSDEV_ATTR(name, 0644, show_ ## name, set_ ## name);
-/* TBD should generate these dynamically based on number of available banks */
+/*
+ * TBD should generate these dynamically based on number of available banks.
+ * Have only 6 contol banks in /sysfs until then.
+ */
ACCESSOR(bank0ctl,bank[0],mce_restart())
ACCESSOR(bank1ctl,bank[1],mce_restart())
ACCESSOR(bank2ctl,bank[2],mce_restart())
diff --git a/arch/x86/kernel/cpu/mcheck/mce_amd_64.c b/arch/x86/kernel/cpu/mcheck/mce_amd_64.c
index 7c9a813e1193..88736cadbaa6 100644
--- a/arch/x86/kernel/cpu/mcheck/mce_amd_64.c
+++ b/arch/x86/kernel/cpu/mcheck/mce_amd_64.c
@@ -527,7 +527,7 @@ static __cpuinit int threshold_create_bank(unsigned int cpu, unsigned int bank)
if (err)
goto out_free;
- for_each_cpu_mask(i, b->cpus) {
+ for_each_cpu_mask_nr(i, b->cpus) {
if (i == cpu)
continue;
@@ -617,7 +617,7 @@ static void threshold_remove_bank(unsigned int cpu, int bank)
#endif
/* remove all sibling symlinks before unregistering */
- for_each_cpu_mask(i, b->cpus) {
+ for_each_cpu_mask_nr(i, b->cpus) {
if (i == cpu)
continue;
diff --git a/arch/x86/kernel/cpu/mtrr/generic.c b/arch/x86/kernel/cpu/mtrr/generic.c
index 5d241ce94a44..509bd3d9eacd 100644
--- a/arch/x86/kernel/cpu/mtrr/generic.c
+++ b/arch/x86/kernel/cpu/mtrr/generic.c
@@ -37,7 +37,7 @@ static struct fixed_range_block fixed_range_blocks[] = {
static unsigned long smp_changes_mask;
static struct mtrr_state mtrr_state = {};
static int mtrr_state_set;
-static u64 tom2;
+u64 mtrr_tom2;
#undef MODULE_PARAM_PREFIX
#define MODULE_PARAM_PREFIX "mtrr."
@@ -139,8 +139,8 @@ u8 mtrr_type_lookup(u64 start, u64 end)
}
}
- if (tom2) {
- if (start >= (1ULL<<32) && (end < tom2))
+ if (mtrr_tom2) {
+ if (start >= (1ULL<<32) && (end < mtrr_tom2))
return MTRR_TYPE_WRBACK;
}
@@ -158,6 +158,20 @@ get_mtrr_var_range(unsigned int index, struct mtrr_var_range *vr)
rdmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi);
}
+/* fill the MSR pair relating to a var range */
+void fill_mtrr_var_range(unsigned int index,
+ u32 base_lo, u32 base_hi, u32 mask_lo, u32 mask_hi)
+{
+ struct mtrr_var_range *vr;
+
+ vr = mtrr_state.var_ranges;
+
+ vr[index].base_lo = base_lo;
+ vr[index].base_hi = base_hi;
+ vr[index].mask_lo = mask_lo;
+ vr[index].mask_hi = mask_hi;
+}
+
static void
get_fixed_ranges(mtrr_type * frs)
{
@@ -213,13 +227,13 @@ void __init get_mtrr_state(void)
mtrr_state.enabled = (lo & 0xc00) >> 10;
if (amd_special_default_mtrr()) {
- unsigned lo, hi;
+ unsigned low, high;
/* TOP_MEM2 */
- rdmsr(MSR_K8_TOP_MEM2, lo, hi);
- tom2 = hi;
- tom2 <<= 32;
- tom2 |= lo;
- tom2 &= 0xffffff8000000ULL;
+ rdmsr(MSR_K8_TOP_MEM2, low, high);
+ mtrr_tom2 = high;
+ mtrr_tom2 <<= 32;
+ mtrr_tom2 |= low;
+ mtrr_tom2 &= 0xffffff800000ULL;
}
if (mtrr_show) {
int high_width;
@@ -251,9 +265,9 @@ void __init get_mtrr_state(void)
else
printk(KERN_INFO "MTRR %u disabled\n", i);
}
- if (tom2) {
+ if (mtrr_tom2) {
printk(KERN_INFO "TOM2: %016llx aka %lldM\n",
- tom2, tom2>>20);
+ mtrr_tom2, mtrr_tom2>>20);
}
}
mtrr_state_set = 1;
@@ -328,7 +342,7 @@ static void set_fixed_range(int msr, bool *changed, unsigned int *msrwords)
if (lo != msrwords[0] || hi != msrwords[1]) {
if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
- boot_cpu_data.x86 == 15 &&
+ (boot_cpu_data.x86 >= 0x0f && boot_cpu_data.x86 <= 0x11) &&
((msrwords[0] | msrwords[1]) & K8_MTRR_RDMEM_WRMEM_MASK))
k8_enable_fixed_iorrs();
mtrr_wrmsr(msr, msrwords[0], msrwords[1]);
diff --git a/arch/x86/kernel/cpu/mtrr/main.c b/arch/x86/kernel/cpu/mtrr/main.c
index 6a1e278d9323..0642201784e0 100644
--- a/arch/x86/kernel/cpu/mtrr/main.c
+++ b/arch/x86/kernel/cpu/mtrr/main.c
@@ -37,6 +37,7 @@
#include <linux/smp.h>
#include <linux/cpu.h>
#include <linux/mutex.h>
+#include <linux/sort.h>
#include <asm/e820.h>
#include <asm/mtrr.h>
@@ -609,6 +610,787 @@ static struct sysdev_driver mtrr_sysdev_driver = {
.resume = mtrr_restore,
};
+/* should be related to MTRR_VAR_RANGES nums */
+#define RANGE_NUM 256
+
+struct res_range {
+ unsigned long start;
+ unsigned long end;
+};
+
+static int __init
+add_range(struct res_range *range, int nr_range, unsigned long start,
+ unsigned long end)
+{
+ /* out of slots */
+ if (nr_range >= RANGE_NUM)
+ return nr_range;
+
+ range[nr_range].start = start;
+ range[nr_range].end = end;
+
+ nr_range++;
+
+ return nr_range;
+}
+
+static int __init
+add_range_with_merge(struct res_range *range, int nr_range, unsigned long start,
+ unsigned long end)
+{
+ int i;
+
+ /* try to merge it with old one */
+ for (i = 0; i < nr_range; i++) {
+ unsigned long final_start, final_end;
+ unsigned long common_start, common_end;
+
+ if (!range[i].end)
+ continue;
+
+ common_start = max(range[i].start, start);
+ common_end = min(range[i].end, end);
+ if (common_start > common_end + 1)
+ continue;
+
+ final_start = min(range[i].start, start);
+ final_end = max(range[i].end, end);
+
+ range[i].start = final_start;
+ range[i].end = final_end;
+ return nr_range;
+ }
+
+ /* need to add that */
+ return add_range(range, nr_range, start, end);
+}
+
+static void __init
+subtract_range(struct res_range *range, unsigned long start, unsigned long end)
+{
+ int i, j;
+
+ for (j = 0; j < RANGE_NUM; j++) {
+ if (!range[j].end)
+ continue;
+
+ if (start <= range[j].start && end >= range[j].end) {
+ range[j].start = 0;
+ range[j].end = 0;
+ continue;
+ }
+
+ if (start <= range[j].start && end < range[j].end &&
+ range[j].start < end + 1) {
+ range[j].start = end + 1;
+ continue;
+ }
+
+
+ if (start > range[j].start && end >= range[j].end &&
+ range[j].end > start - 1) {
+ range[j].end = start - 1;
+ continue;
+ }
+
+ if (start > range[j].start && end < range[j].end) {
+ /* find the new spare */
+ for (i = 0; i < RANGE_NUM; i++) {
+ if (range[i].end == 0)
+ break;
+ }
+ if (i < RANGE_NUM) {
+ range[i].end = range[j].end;
+ range[i].start = end + 1;
+ } else {
+ printk(KERN_ERR "run of slot in ranges\n");
+ }
+ range[j].end = start - 1;
+ continue;
+ }
+ }
+}
+
+static int __init cmp_range(const void *x1, const void *x2)
+{
+ const struct res_range *r1 = x1;
+ const struct res_range *r2 = x2;
+ long start1, start2;
+
+ start1 = r1->start;
+ start2 = r2->start;
+
+ return start1 - start2;
+}
+
+struct var_mtrr_range_state {
+ unsigned long base_pfn;
+ unsigned long size_pfn;
+ mtrr_type type;
+};
+
+struct var_mtrr_range_state __initdata range_state[RANGE_NUM];
+static int __initdata debug_print;
+
+static int __init
+x86_get_mtrr_mem_range(struct res_range *range, int nr_range,
+ unsigned long extra_remove_base,
+ unsigned long extra_remove_size)
+{
+ unsigned long i, base, size;
+ mtrr_type type;
+
+ for (i = 0; i < num_var_ranges; i++) {
+ type = range_state[i].type;
+ if (type != MTRR_TYPE_WRBACK)
+ continue;
+ base = range_state[i].base_pfn;
+ size = range_state[i].size_pfn;
+ nr_range = add_range_with_merge(range, nr_range, base,
+ base + size - 1);
+ }
+ if (debug_print) {
+ printk(KERN_DEBUG "After WB checking\n");
+ for (i = 0; i < nr_range; i++)
+ printk(KERN_DEBUG "MTRR MAP PFN: %016lx - %016lx\n",
+ range[i].start, range[i].end + 1);
+ }
+
+ /* take out UC ranges */
+ for (i = 0; i < num_var_ranges; i++) {
+ type = range_state[i].type;
+ if (type != MTRR_TYPE_UNCACHABLE)
+ continue;
+ size = range_state[i].size_pfn;
+ if (!size)
+ continue;
+ base = range_state[i].base_pfn;
+ subtract_range(range, base, base + size - 1);
+ }
+ if (extra_remove_size)
+ subtract_range(range, extra_remove_base,
+ extra_remove_base + extra_remove_size - 1);
+
+ /* get new range num */
+ nr_range = 0;
+ for (i = 0; i < RANGE_NUM; i++) {
+ if (!range[i].end)
+ continue;
+ nr_range++;
+ }
+ if (debug_print) {
+ printk(KERN_DEBUG "After UC checking\n");
+ for (i = 0; i < nr_range; i++)
+ printk(KERN_DEBUG "MTRR MAP PFN: %016lx - %016lx\n",
+ range[i].start, range[i].end + 1);
+ }
+
+ /* sort the ranges */
+ sort(range, nr_range, sizeof(struct res_range), cmp_range, NULL);
+ if (debug_print) {
+ printk(KERN_DEBUG "After sorting\n");
+ for (i = 0; i < nr_range; i++)
+ printk(KERN_DEBUG "MTRR MAP PFN: %016lx - %016lx\n",
+ range[i].start, range[i].end + 1);
+ }
+
+ /* clear those is not used */
+ for (i = nr_range; i < RANGE_NUM; i++)
+ memset(&range[i], 0, sizeof(range[i]));
+
+ return nr_range;
+}
+
+static struct res_range __initdata range[RANGE_NUM];
+
+#ifdef CONFIG_MTRR_SANITIZER
+
+static unsigned long __init sum_ranges(struct res_range *range, int nr_range)
+{
+ unsigned long sum;
+ int i;
+
+ sum = 0;
+ for (i = 0; i < nr_range; i++)
+ sum += range[i].end + 1 - range[i].start;
+
+ return sum;
+}
+
+static int enable_mtrr_cleanup __initdata =
+ CONFIG_MTRR_SANITIZER_ENABLE_DEFAULT;
+
+static int __init disable_mtrr_cleanup_setup(char *str)
+{
+ if (enable_mtrr_cleanup != -1)
+ enable_mtrr_cleanup = 0;
+ return 0;
+}
+early_param("disable_mtrr_cleanup", disable_mtrr_cleanup_setup);
+
+static int __init enable_mtrr_cleanup_setup(char *str)
+{
+ if (enable_mtrr_cleanup != -1)
+ enable_mtrr_cleanup = 1;
+ return 0;
+}
+early_param("enble_mtrr_cleanup", enable_mtrr_cleanup_setup);
+
+struct var_mtrr_state {
+ unsigned long range_startk;
+ unsigned long range_sizek;
+ unsigned long chunk_sizek;
+ unsigned long gran_sizek;
+ unsigned int reg;
+};
+
+static void __init
+set_var_mtrr(unsigned int reg, unsigned long basek, unsigned long sizek,
+ unsigned char type, unsigned int address_bits)
+{
+ u32 base_lo, base_hi, mask_lo, mask_hi;
+ u64 base, mask;
+
+ if (!sizek) {
+ fill_mtrr_var_range(reg, 0, 0, 0, 0);
+ return;
+ }
+
+ mask = (1ULL << address_bits) - 1;
+ mask &= ~((((u64)sizek) << 10) - 1);
+
+ base = ((u64)basek) << 10;
+
+ base |= type;
+ mask |= 0x800;
+
+ base_lo = base & ((1ULL<<32) - 1);
+ base_hi = base >> 32;
+
+ mask_lo = mask & ((1ULL<<32) - 1);
+ mask_hi = mask >> 32;
+
+ fill_mtrr_var_range(reg, base_lo, base_hi, mask_lo, mask_hi);
+}
+
+static void __init
+save_var_mtrr(unsigned int reg, unsigned long basek, unsigned long sizek,
+ unsigned char type)
+{
+ range_state[reg].base_pfn = basek >> (PAGE_SHIFT - 10);
+ range_state[reg].size_pfn = sizek >> (PAGE_SHIFT - 10);
+ range_state[reg].type = type;
+}
+
+static void __init
+set_var_mtrr_all(unsigned int address_bits)
+{
+ unsigned long basek, sizek;
+ unsigned char type;
+ unsigned int reg;
+
+ for (reg = 0; reg < num_var_ranges; reg++) {
+ basek = range_state[reg].base_pfn << (PAGE_SHIFT - 10);
+ sizek = range_state[reg].size_pfn << (PAGE_SHIFT - 10);
+ type = range_state[reg].type;
+
+ set_var_mtrr(reg, basek, sizek, type, address_bits);
+ }
+}
+
+static unsigned int __init
+range_to_mtrr(unsigned int reg, unsigned long range_startk,
+ unsigned long range_sizek, unsigned char type)
+{
+ if (!range_sizek || (reg >= num_var_ranges))
+ return reg;
+
+ while (range_sizek) {
+ unsigned long max_align, align;
+ unsigned long sizek;
+
+ /* Compute the maximum size I can make a range */
+ if (range_startk)
+ max_align = ffs(range_startk) - 1;
+ else
+ max_align = 32;
+ align = fls(range_sizek) - 1;
+ if (align > max_align)
+ align = max_align;
+
+ sizek = 1 << align;
+ if (debug_print)
+ printk(KERN_DEBUG "Setting variable MTRR %d, "
+ "base: %ldMB, range: %ldMB, type %s\n",
+ reg, range_startk >> 10, sizek >> 10,
+ (type == MTRR_TYPE_UNCACHABLE)?"UC":
+ ((type == MTRR_TYPE_WRBACK)?"WB":"Other")
+ );
+ save_var_mtrr(reg++, range_startk, sizek, type);
+ range_startk += sizek;
+ range_sizek -= sizek;
+ if (reg >= num_var_ranges)
+ break;
+ }
+ return reg;
+}
+
+static unsigned __init
+range_to_mtrr_with_hole(struct var_mtrr_state *state, unsigned long basek,
+ unsigned long sizek)
+{
+ unsigned long hole_basek, hole_sizek;
+ unsigned long second_basek, second_sizek;
+ unsigned long range0_basek, range0_sizek;
+ unsigned long range_basek, range_sizek;
+ unsigned long chunk_sizek;
+ unsigned long gran_sizek;
+
+ hole_basek = 0;
+ hole_sizek = 0;
+ second_basek = 0;
+ second_sizek = 0;
+ chunk_sizek = state->chunk_sizek;
+ gran_sizek = state->gran_sizek;
+
+ /* align with gran size, prevent small block used up MTRRs */
+ range_basek = ALIGN(state->range_startk, gran_sizek);
+ if ((range_basek > basek) && basek)
+ return second_sizek;
+ state->range_sizek -= (range_basek - state->range_startk);
+ range_sizek = ALIGN(state->range_sizek, gran_sizek);
+
+ while (range_sizek > state->range_sizek) {
+ range_sizek -= gran_sizek;
+ if (!range_sizek)
+ return 0;
+ }
+ state->range_sizek = range_sizek;
+
+ /* try to append some small hole */
+ range0_basek = state->range_startk;
+ range0_sizek = ALIGN(state->range_sizek, chunk_sizek);
+ if (range0_sizek == state->range_sizek) {
+ if (debug_print)
+ printk(KERN_DEBUG "rangeX: %016lx - %016lx\n",
+ range0_basek<<10,
+ (range0_basek + state->range_sizek)<<10);
+ state->reg = range_to_mtrr(state->reg, range0_basek,
+ state->range_sizek, MTRR_TYPE_WRBACK);
+ return 0;
+ }
+
+ range0_sizek -= chunk_sizek;
+ if (range0_sizek && sizek) {
+ while (range0_basek + range0_sizek > (basek + sizek)) {
+ range0_sizek -= chunk_sizek;
+ if (!range0_sizek)
+ break;
+ }
+ }
+
+ if (range0_sizek) {
+ if (debug_print)
+ printk(KERN_DEBUG "range0: %016lx - %016lx\n",
+ range0_basek<<10,
+ (range0_basek + range0_sizek)<<10);
+ state->reg = range_to_mtrr(state->reg, range0_basek,
+ range0_sizek, MTRR_TYPE_WRBACK);
+
+ }
+
+ range_basek = range0_basek + range0_sizek;
+ range_sizek = chunk_sizek;
+
+ if (range_basek + range_sizek > basek &&
+ range_basek + range_sizek <= (basek + sizek)) {
+ /* one hole */
+ second_basek = basek;
+ second_sizek = range_basek + range_sizek - basek;
+ }
+
+ /* if last piece, only could one hole near end */
+ if ((second_basek || !basek) &&
+ range_sizek - (state->range_sizek - range0_sizek) - second_sizek <
+ (chunk_sizek >> 1)) {
+ /*
+ * one hole in middle (second_sizek is 0) or at end
+ * (second_sizek is 0 )
+ */
+ hole_sizek = range_sizek - (state->range_sizek - range0_sizek)
+ - second_sizek;
+ hole_basek = range_basek + range_sizek - hole_sizek
+ - second_sizek;
+ } else {
+ /* fallback for big hole, or several holes */
+ range_sizek = state->range_sizek - range0_sizek;
+ second_basek = 0;
+ second_sizek = 0;
+ }
+
+ if (debug_print)
+ printk(KERN_DEBUG "range: %016lx - %016lx\n", range_basek<<10,
+ (range_basek + range_sizek)<<10);
+ state->reg = range_to_mtrr(state->reg, range_basek, range_sizek,
+ MTRR_TYPE_WRBACK);
+ if (hole_sizek) {
+ if (debug_print)
+ printk(KERN_DEBUG "hole: %016lx - %016lx\n",
+ hole_basek<<10, (hole_basek + hole_sizek)<<10);
+ state->reg = range_to_mtrr(state->reg, hole_basek, hole_sizek,
+ MTRR_TYPE_UNCACHABLE);
+
+ }
+
+ return second_sizek;
+}
+
+static void __init
+set_var_mtrr_range(struct var_mtrr_state *state, unsigned long base_pfn,
+ unsigned long size_pfn)
+{
+ unsigned long basek, sizek;
+ unsigned long second_sizek = 0;
+
+ if (state->reg >= num_var_ranges)
+ return;
+
+ basek = base_pfn << (PAGE_SHIFT - 10);
+ sizek = size_pfn << (PAGE_SHIFT - 10);
+
+ /* See if I can merge with the last range */
+ if ((basek <= 1024) ||
+ (state->range_startk + state->range_sizek == basek)) {
+ unsigned long endk = basek + sizek;
+ state->range_sizek = endk - state->range_startk;
+ return;
+ }
+ /* Write the range mtrrs */
+ if (state->range_sizek != 0)
+ second_sizek = range_to_mtrr_with_hole(state, basek, sizek);
+
+ /* Allocate an msr */
+ state->range_startk = basek + second_sizek;
+ state->range_sizek = sizek - second_sizek;
+}
+
+/* mininum size of mtrr block that can take hole */
+static u64 mtrr_chunk_size __initdata = (256ULL<<20);
+
+static int __init parse_mtrr_chunk_size_opt(char *p)
+{
+ if (!p)
+ return -EINVAL;
+ mtrr_chunk_size = memparse(p, &p);
+ return 0;
+}
+early_param("mtrr_chunk_size", parse_mtrr_chunk_size_opt);
+
+/* granity of mtrr of block */
+static u64 mtrr_gran_size __initdata;
+
+static int __init parse_mtrr_gran_size_opt(char *p)
+{
+ if (!p)
+ return -EINVAL;
+ mtrr_gran_size = memparse(p, &p);
+ return 0;
+}
+early_param("mtrr_gran_size", parse_mtrr_gran_size_opt);
+
+static int nr_mtrr_spare_reg __initdata =
+ CONFIG_MTRR_SANITIZER_SPARE_REG_NR_DEFAULT;
+
+static int __init parse_mtrr_spare_reg(char *arg)
+{
+ if (arg)
+ nr_mtrr_spare_reg = simple_strtoul(arg, NULL, 0);
+ return 0;
+}
+
+early_param("mtrr_spare_reg_nr", parse_mtrr_spare_reg);
+
+static int __init
+x86_setup_var_mtrrs(struct res_range *range, int nr_range,
+ u64 chunk_size, u64 gran_size)
+{
+ struct var_mtrr_state var_state;
+ int i;
+ int num_reg;
+
+ var_state.range_startk = 0;
+ var_state.range_sizek = 0;
+ var_state.reg = 0;
+ var_state.chunk_sizek = chunk_size >> 10;
+ var_state.gran_sizek = gran_size >> 10;
+
+ memset(range_state, 0, sizeof(range_state));
+
+ /* Write the range etc */
+ for (i = 0; i < nr_range; i++)
+ set_var_mtrr_range(&var_state, range[i].start,
+ range[i].end - range[i].start + 1);
+
+ /* Write the last range */
+ if (var_state.range_sizek != 0)
+ range_to_mtrr_with_hole(&var_state, 0, 0);
+
+ num_reg = var_state.reg;
+ /* Clear out the extra MTRR's */
+ while (var_state.reg < num_var_ranges) {
+ save_var_mtrr(var_state.reg, 0, 0, 0);
+ var_state.reg++;
+ }
+
+ return num_reg;
+}
+
+struct mtrr_cleanup_result {
+ unsigned long gran_sizek;
+ unsigned long chunk_sizek;
+ unsigned long lose_cover_sizek;
+ unsigned int num_reg;
+ int bad;
+};
+
+/*
+ * gran_size: 1M, 2M, ..., 2G
+ * chunk size: gran_size, ..., 4G
+ * so we need (2+13)*6
+ */
+#define NUM_RESULT 90
+#define PSHIFT (PAGE_SHIFT - 10)
+
+static struct mtrr_cleanup_result __initdata result[NUM_RESULT];
+static struct res_range __initdata range_new[RANGE_NUM];
+static unsigned long __initdata min_loss_pfn[RANGE_NUM];
+
+static int __init mtrr_cleanup(unsigned address_bits)
+{
+ unsigned long extra_remove_base, extra_remove_size;
+ unsigned long i, base, size, def, dummy;
+ mtrr_type type;
+ int nr_range, nr_range_new;
+ u64 chunk_size, gran_size;
+ unsigned long range_sums, range_sums_new;
+ int index_good;
+ int num_reg_good;
+
+ /* extra one for all 0 */
+ int num[MTRR_NUM_TYPES + 1];
+
+ if (!is_cpu(INTEL) || enable_mtrr_cleanup < 1)
+ return 0;
+ rdmsr(MTRRdefType_MSR, def, dummy);
+ def &= 0xff;
+ if (def != MTRR_TYPE_UNCACHABLE)
+ return 0;
+
+ /* get it and store it aside */
+ memset(range_state, 0, sizeof(range_state));
+ for (i = 0; i < num_var_ranges; i++) {
+ mtrr_if->get(i, &base, &size, &type);
+ range_state[i].base_pfn = base;
+ range_state[i].size_pfn = size;
+ range_state[i].type = type;
+ }
+
+ /* check entries number */
+ memset(num, 0, sizeof(num));
+ for (i = 0; i < num_var_ranges; i++) {
+ type = range_state[i].type;
+ size = range_state[i].size_pfn;
+ if (type >= MTRR_NUM_TYPES)
+ continue;
+ if (!size)
+ type = MTRR_NUM_TYPES;
+ num[type]++;
+ }
+
+ /* check if we got UC entries */
+ if (!num[MTRR_TYPE_UNCACHABLE])
+ return 0;
+
+ /* check if we only had WB and UC */
+ if (num[MTRR_TYPE_WRBACK] + num[MTRR_TYPE_UNCACHABLE] !=
+ num_var_ranges - num[MTRR_NUM_TYPES])
+ return 0;
+
+ memset(range, 0, sizeof(range));
+ extra_remove_size = 0;
+ if (mtrr_tom2) {
+ extra_remove_base = 1 << (32 - PAGE_SHIFT);
+ extra_remove_size =
+ (mtrr_tom2 >> PAGE_SHIFT) - extra_remove_base;
+ }
+ nr_range = x86_get_mtrr_mem_range(range, 0, extra_remove_base,
+ extra_remove_size);
+ range_sums = sum_ranges(range, nr_range);
+ printk(KERN_INFO "total RAM coverred: %ldM\n",
+ range_sums >> (20 - PAGE_SHIFT));
+
+ if (mtrr_chunk_size && mtrr_gran_size) {
+ int num_reg;
+
+ debug_print = 1;
+ /* convert ranges to var ranges state */
+ num_reg = x86_setup_var_mtrrs(range, nr_range, mtrr_chunk_size,
+ mtrr_gran_size);
+
+ /* we got new setting in range_state, check it */
+ memset(range_new, 0, sizeof(range_new));
+ nr_range_new = x86_get_mtrr_mem_range(range_new, 0,
+ extra_remove_base,
+ extra_remove_size);
+ range_sums_new = sum_ranges(range_new, nr_range_new);
+
+ i = 0;
+ result[i].chunk_sizek = mtrr_chunk_size >> 10;
+ result[i].gran_sizek = mtrr_gran_size >> 10;
+ result[i].num_reg = num_reg;
+ if (range_sums < range_sums_new) {
+ result[i].lose_cover_sizek =
+ (range_sums_new - range_sums) << PSHIFT;
+ result[i].bad = 1;
+ } else
+ result[i].lose_cover_sizek =
+ (range_sums - range_sums_new) << PSHIFT;
+
+ printk(KERN_INFO "%sgran_size: %ldM \tchunk_size: %ldM \t",
+ result[i].bad?"*BAD*":" ", result[i].gran_sizek >> 10,
+ result[i].chunk_sizek >> 10);
+ printk(KERN_CONT "num_reg: %d \tlose cover RAM: %s%ldM \n",
+ result[i].num_reg, result[i].bad?"-":"",
+ result[i].lose_cover_sizek >> 10);
+ if (!result[i].bad) {
+ set_var_mtrr_all(address_bits);
+ return 1;
+ }
+ printk(KERN_INFO "invalid mtrr_gran_size or mtrr_chunk_size, "
+ "will find optimal one\n");
+ debug_print = 0;
+ memset(result, 0, sizeof(result[0]));
+ }
+
+ i = 0;
+ memset(min_loss_pfn, 0xff, sizeof(min_loss_pfn));
+ memset(result, 0, sizeof(result));
+ for (gran_size = (1ULL<<20); gran_size < (1ULL<<32); gran_size <<= 1) {
+ for (chunk_size = gran_size; chunk_size < (1ULL<<33);
+ chunk_size <<= 1) {
+ int num_reg;
+
+ if (debug_print)
+ printk(KERN_INFO
+ "\ngran_size: %lldM chunk_size_size: %lldM\n",
+ gran_size >> 20, chunk_size >> 20);
+ if (i >= NUM_RESULT)
+ continue;
+
+ /* convert ranges to var ranges state */
+ num_reg = x86_setup_var_mtrrs(range, nr_range,
+ chunk_size, gran_size);
+
+ /* we got new setting in range_state, check it */
+ memset(range_new, 0, sizeof(range_new));
+ nr_range_new = x86_get_mtrr_mem_range(range_new, 0,
+ extra_remove_base, extra_remove_size);
+ range_sums_new = sum_ranges(range_new, nr_range_new);
+
+ result[i].chunk_sizek = chunk_size >> 10;
+ result[i].gran_sizek = gran_size >> 10;
+ result[i].num_reg = num_reg;
+ if (range_sums < range_sums_new) {
+ result[i].lose_cover_sizek =
+ (range_sums_new - range_sums) << PSHIFT;
+ result[i].bad = 1;
+ } else
+ result[i].lose_cover_sizek =
+ (range_sums - range_sums_new) << PSHIFT;
+
+ /* double check it */
+ if (!result[i].bad && !result[i].lose_cover_sizek) {
+ if (nr_range_new != nr_range ||
+ memcmp(range, range_new, sizeof(range)))
+ result[i].bad = 1;
+ }
+
+ if (!result[i].bad && (range_sums - range_sums_new <
+ min_loss_pfn[num_reg])) {
+ min_loss_pfn[num_reg] =
+ range_sums - range_sums_new;
+ }
+ i++;
+ }
+ }
+
+ /* print out all */
+ for (i = 0; i < NUM_RESULT; i++) {
+ printk(KERN_INFO "%sgran_size: %ldM \tchunk_size: %ldM \t",
+ result[i].bad?"*BAD* ":" ", result[i].gran_sizek >> 10,
+ result[i].chunk_sizek >> 10);
+ printk(KERN_CONT "num_reg: %d \tlose RAM: %s%ldM\n",
+ result[i].num_reg, result[i].bad?"-":"",
+ result[i].lose_cover_sizek >> 10);
+ }
+
+ /* try to find the optimal index */
+ if (nr_mtrr_spare_reg >= num_var_ranges)
+ nr_mtrr_spare_reg = num_var_ranges - 1;
+ num_reg_good = -1;
+ for (i = num_var_ranges - nr_mtrr_spare_reg; i > 0; i--) {
+ if (!min_loss_pfn[i]) {
+ num_reg_good = i;
+ break;
+ }
+ }
+
+ index_good = -1;
+ if (num_reg_good != -1) {
+ for (i = 0; i < NUM_RESULT; i++) {
+ if (!result[i].bad &&
+ result[i].num_reg == num_reg_good &&
+ !result[i].lose_cover_sizek) {
+ index_good = i;
+ break;
+ }
+ }
+ }
+
+ if (index_good != -1) {
+ printk(KERN_INFO "Found optimal setting for mtrr clean up\n");
+ i = index_good;
+ printk(KERN_INFO "gran_size: %ldM \tchunk_size: %ldM \t",
+ result[i].gran_sizek >> 10,
+ result[i].chunk_sizek >> 10);
+ printk(KERN_CONT "num_reg: %d \tlose RAM: %ldM\n",
+ result[i].num_reg,
+ result[i].lose_cover_sizek >> 10);
+ /* convert ranges to var ranges state */
+ chunk_size = result[i].chunk_sizek;
+ chunk_size <<= 10;
+ gran_size = result[i].gran_sizek;
+ gran_size <<= 10;
+ debug_print = 1;
+ x86_setup_var_mtrrs(range, nr_range, chunk_size, gran_size);
+ set_var_mtrr_all(address_bits);
+ return 1;
+ }
+
+ printk(KERN_INFO "mtrr_cleanup: can not find optimal value\n");
+ printk(KERN_INFO "please specify mtrr_gran_size/mtrr_chunk_size\n");
+
+ return 0;
+}
+#else
+static int __init mtrr_cleanup(unsigned address_bits)
+{
+ return 0;
+}
+#endif
+
+static int __initdata changed_by_mtrr_cleanup;
+
static int disable_mtrr_trim;
static int __init disable_mtrr_trim_setup(char *str)
@@ -648,6 +1430,19 @@ int __init amd_special_default_mtrr(void)
return 0;
}
+static u64 __init real_trim_memory(unsigned long start_pfn,
+ unsigned long limit_pfn)
+{
+ u64 trim_start, trim_size;
+ trim_start = start_pfn;
+ trim_start <<= PAGE_SHIFT;
+ trim_size = limit_pfn;
+ trim_size <<= PAGE_SHIFT;
+ trim_size -= trim_start;
+
+ return update_memory_range(trim_start, trim_size, E820_RAM,
+ E820_RESERVED);
+}
/**
* mtrr_trim_uncached_memory - trim RAM not covered by MTRRs
* @end_pfn: ending page frame number
@@ -663,8 +1458,11 @@ int __init mtrr_trim_uncached_memory(unsigned long end_pfn)
{
unsigned long i, base, size, highest_pfn = 0, def, dummy;
mtrr_type type;
- u64 trim_start, trim_size;
+ int nr_range;
+ u64 total_trim_size;
+ /* extra one for all 0 */
+ int num[MTRR_NUM_TYPES + 1];
/*
* Make sure we only trim uncachable memory on machines that
* support the Intel MTRR architecture:
@@ -676,14 +1474,22 @@ int __init mtrr_trim_uncached_memory(unsigned long end_pfn)
if (def != MTRR_TYPE_UNCACHABLE)
return 0;
- if (amd_special_default_mtrr())
- return 0;
+ /* get it and store it aside */
+ memset(range_state, 0, sizeof(range_state));
+ for (i = 0; i < num_var_ranges; i++) {
+ mtrr_if->get(i, &base, &size, &type);
+ range_state[i].base_pfn = base;
+ range_state[i].size_pfn = size;
+ range_state[i].type = type;
+ }
/* Find highest cached pfn */
for (i = 0; i < num_var_ranges; i++) {
- mtrr_if->get(i, &base, &size, &type);
+ type = range_state[i].type;
if (type != MTRR_TYPE_WRBACK)
continue;
+ base = range_state[i].base_pfn;
+ size = range_state[i].size_pfn;
if (highest_pfn < base + size)
highest_pfn = base + size;
}
@@ -698,22 +1504,65 @@ int __init mtrr_trim_uncached_memory(unsigned long end_pfn)
return 0;
}
- if (highest_pfn < end_pfn) {
+ /* check entries number */
+ memset(num, 0, sizeof(num));
+ for (i = 0; i < num_var_ranges; i++) {
+ type = range_state[i].type;
+ if (type >= MTRR_NUM_TYPES)
+ continue;
+ size = range_state[i].size_pfn;
+ if (!size)
+ type = MTRR_NUM_TYPES;
+ num[type]++;
+ }
+
+ /* no entry for WB? */
+ if (!num[MTRR_TYPE_WRBACK])
+ return 0;
+
+ /* check if we only had WB and UC */
+ if (num[MTRR_TYPE_WRBACK] + num[MTRR_TYPE_UNCACHABLE] !=
+ num_var_ranges - num[MTRR_NUM_TYPES])
+ return 0;
+
+ memset(range, 0, sizeof(range));
+ nr_range = 0;
+ if (mtrr_tom2) {
+ range[nr_range].start = (1ULL<<(32 - PAGE_SHIFT));
+ range[nr_range].end = (mtrr_tom2 >> PAGE_SHIFT) - 1;
+ if (highest_pfn < range[nr_range].end + 1)
+ highest_pfn = range[nr_range].end + 1;
+ nr_range++;
+ }
+ nr_range = x86_get_mtrr_mem_range(range, nr_range, 0, 0);
+
+ total_trim_size = 0;
+ /* check the head */
+ if (range[0].start)
+ total_trim_size += real_trim_memory(0, range[0].start);
+ /* check the holes */
+ for (i = 0; i < nr_range - 1; i++) {
+ if (range[i].end + 1 < range[i+1].start)
+ total_trim_size += real_trim_memory(range[i].end + 1,
+ range[i+1].start);
+ }
+ /* check the top */
+ i = nr_range - 1;
+ if (range[i].end + 1 < end_pfn)
+ total_trim_size += real_trim_memory(range[i].end + 1,
+ end_pfn);
+
+ if (total_trim_size) {
printk(KERN_WARNING "WARNING: BIOS bug: CPU MTRRs don't cover"
- " all of memory, losing %luMB of RAM.\n",
- (end_pfn - highest_pfn) >> (20 - PAGE_SHIFT));
+ " all of memory, losing %lluMB of RAM.\n",
+ total_trim_size >> 20);
- WARN_ON(1);
+ if (!changed_by_mtrr_cleanup)
+ WARN_ON(1);
printk(KERN_INFO "update e820 for mtrr\n");
- trim_start = highest_pfn;
- trim_start <<= PAGE_SHIFT;
- trim_size = end_pfn;
- trim_size <<= PAGE_SHIFT;
- trim_size -= trim_start;
- update_memory_range(trim_start, trim_size, E820_RAM,
- E820_RESERVED);
update_e820();
+
return 1;
}
@@ -729,18 +1578,21 @@ int __init mtrr_trim_uncached_memory(unsigned long end_pfn)
*/
void __init mtrr_bp_init(void)
{
+ u32 phys_addr;
init_ifs();
+ phys_addr = 32;
+
if (cpu_has_mtrr) {
mtrr_if = &generic_mtrr_ops;
size_or_mask = 0xff000000; /* 36 bits */
size_and_mask = 0x00f00000;
+ phys_addr = 36;
/* This is an AMD specific MSR, but we assume(hope?) that
Intel will implement it to when they extend the address
bus of the Xeon. */
if (cpuid_eax(0x80000000) >= 0x80000008) {
- u32 phys_addr;
phys_addr = cpuid_eax(0x80000008) & 0xff;
/* CPUID workaround for Intel 0F33/0F34 CPU */
if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL &&
@@ -758,6 +1610,7 @@ void __init mtrr_bp_init(void)
don't support PAE */
size_or_mask = 0xfff00000; /* 32 bits */
size_and_mask = 0;
+ phys_addr = 32;
}
} else {
switch (boot_cpu_data.x86_vendor) {
@@ -791,8 +1644,15 @@ void __init mtrr_bp_init(void)
if (mtrr_if) {
set_num_var_ranges();
init_table();
- if (use_intel())
+ if (use_intel()) {
get_mtrr_state();
+
+ if (mtrr_cleanup(phys_addr)) {
+ changed_by_mtrr_cleanup = 1;
+ mtrr_if->set_all();
+ }
+
+ }
}
}
@@ -829,9 +1689,10 @@ static int __init mtrr_init_finialize(void)
{
if (!mtrr_if)
return 0;
- if (use_intel())
- mtrr_state_warn();
- else {
+ if (use_intel()) {
+ if (!changed_by_mtrr_cleanup)
+ mtrr_state_warn();
+ } else {
/* The CPUs haven't MTRR and seem to not support SMP. They have
* specific drivers, we use a tricky method to support
* suspend/resume for them.
diff --git a/arch/x86/kernel/cpu/mtrr/mtrr.h b/arch/x86/kernel/cpu/mtrr/mtrr.h
index 2cc77eb6fea3..2dc4ec656b23 100644
--- a/arch/x86/kernel/cpu/mtrr/mtrr.h
+++ b/arch/x86/kernel/cpu/mtrr/mtrr.h
@@ -81,6 +81,8 @@ void set_mtrr_done(struct set_mtrr_context *ctxt);
void set_mtrr_cache_disable(struct set_mtrr_context *ctxt);
void set_mtrr_prepare_save(struct set_mtrr_context *ctxt);
+void fill_mtrr_var_range(unsigned int index,
+ u32 base_lo, u32 base_hi, u32 mask_lo, u32 mask_hi);
void get_mtrr_state(void);
extern void set_mtrr_ops(struct mtrr_ops * ops);
@@ -92,6 +94,7 @@ extern struct mtrr_ops * mtrr_if;
#define use_intel() (mtrr_if && mtrr_if->use_intel_if == 1)
extern unsigned int num_var_ranges;
+extern u64 mtrr_tom2;
void mtrr_state_warn(void);
const char *mtrr_attrib_to_str(int x);
diff --git a/arch/x86/kernel/cpu/perfctr-watchdog.c b/arch/x86/kernel/cpu/perfctr-watchdog.c
index f9ae93adffe5..ddda4b64f545 100644
--- a/arch/x86/kernel/cpu/perfctr-watchdog.c
+++ b/arch/x86/kernel/cpu/perfctr-watchdog.c
@@ -181,7 +181,9 @@ void disable_lapic_nmi_watchdog(void)
return;
on_each_cpu(stop_apic_nmi_watchdog, NULL, 0, 1);
- wd_ops->unreserve();
+
+ if (wd_ops)
+ wd_ops->unreserve();
BUG_ON(atomic_read(&nmi_active) != 0);
}
diff --git a/arch/x86/kernel/cpuid.c b/arch/x86/kernel/cpuid.c
index daff52a62248..3d8b3588afbe 100644
--- a/arch/x86/kernel/cpuid.c
+++ b/arch/x86/kernel/cpuid.c
@@ -132,8 +132,8 @@ static __cpuinit int cpuid_device_create(int cpu)
{
struct device *dev;
- dev = device_create(cpuid_class, NULL, MKDEV(CPUID_MAJOR, cpu),
- "cpu%d", cpu);
+ dev = device_create_drvdata(cpuid_class, NULL, MKDEV(CPUID_MAJOR, cpu),
+ NULL, "cpu%d", cpu);
return IS_ERR(dev) ? PTR_ERR(dev) : 0;
}
diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c
new file mode 100644
index 000000000000..68aba413d403
--- /dev/null
+++ b/arch/x86/kernel/e820.c
@@ -0,0 +1,892 @@
+/*
+ * Handle the memory map.
+ * The functions here do the job until bootmem takes over.
+ *
+ * Getting sanitize_e820_map() in sync with i386 version by applying change:
+ * - Provisions for empty E820 memory regions (reported by certain BIOSes).
+ * Alex Achenbach <xela@slit.de>, December 2002.
+ * Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <linux/kexec.h>
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/pfn.h>
+#include <linux/suspend.h>
+
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/e820.h>
+#include <asm/proto.h>
+#include <asm/setup.h>
+#include <asm/trampoline.h>
+
+struct e820map e820;
+
+/* For PCI or other memory-mapped resources */
+unsigned long pci_mem_start = 0xaeedbabe;
+#ifdef CONFIG_PCI
+EXPORT_SYMBOL(pci_mem_start);
+#endif
+
+/*
+ * This function checks if any part of the range <start,end> is mapped
+ * with type.
+ */
+int
+e820_any_mapped(u64 start, u64 end, unsigned type)
+{
+ int i;
+
+ for (i = 0; i < e820.nr_map; i++) {
+ struct e820entry *ei = &e820.map[i];
+
+ if (type && ei->type != type)
+ continue;
+ if (ei->addr >= end || ei->addr + ei->size <= start)
+ continue;
+ return 1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(e820_any_mapped);
+
+/*
+ * This function checks if the entire range <start,end> is mapped with type.
+ *
+ * Note: this function only works correct if the e820 table is sorted and
+ * not-overlapping, which is the case
+ */
+int __init e820_all_mapped(u64 start, u64 end, unsigned type)
+{
+ int i;
+
+ for (i = 0; i < e820.nr_map; i++) {
+ struct e820entry *ei = &e820.map[i];
+
+ if (type && ei->type != type)
+ continue;
+ /* is the region (part) in overlap with the current region ?*/
+ if (ei->addr >= end || ei->addr + ei->size <= start)
+ continue;
+
+ /* if the region is at the beginning of <start,end> we move
+ * start to the end of the region since it's ok until there
+ */
+ if (ei->addr <= start)
+ start = ei->addr + ei->size;
+ /*
+ * if start is now at or beyond end, we're done, full
+ * coverage
+ */
+ if (start >= end)
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Add a memory region to the kernel e820 map.
+ */
+void __init add_memory_region(u64 start, u64 size, int type)
+{
+ int x = e820.nr_map;
+
+ if (x == ARRAY_SIZE(e820.map)) {
+ printk(KERN_ERR "Ooops! Too many entries in the memory map!\n");
+ return;
+ }
+
+ e820.map[x].addr = start;
+ e820.map[x].size = size;
+ e820.map[x].type = type;
+ e820.nr_map++;
+}
+
+void __init e820_print_map(char *who)
+{
+ int i;
+
+ for (i = 0; i < e820.nr_map; i++) {
+ printk(KERN_INFO " %s: %016Lx - %016Lx ", who,
+ (unsigned long long) e820.map[i].addr,
+ (unsigned long long)
+ (e820.map[i].addr + e820.map[i].size));
+ switch (e820.map[i].type) {
+ case E820_RAM:
+ printk(KERN_CONT "(usable)\n");
+ break;
+ case E820_RESERVED:
+ printk(KERN_CONT "(reserved)\n");
+ break;
+ case E820_ACPI:
+ printk(KERN_CONT "(ACPI data)\n");
+ break;
+ case E820_NVS:
+ printk(KERN_CONT "(ACPI NVS)\n");
+ break;
+ default:
+ printk(KERN_CONT "type %u\n", e820.map[i].type);
+ break;
+ }
+ }
+}
+
+/*
+ * Sanitize the BIOS e820 map.
+ *
+ * Some e820 responses include overlapping entries. The following
+ * replaces the original e820 map with a new one, removing overlaps,
+ * and resolving conflicting memory types in favor of highest
+ * numbered type.
+ *
+ * The input parameter biosmap points to an array of 'struct
+ * e820entry' which on entry has elements in the range [0, *pnr_map)
+ * valid, and which has space for up to max_nr_map entries.
+ * On return, the resulting sanitized e820 map entries will be in
+ * overwritten in the same location, starting at biosmap.
+ *
+ * The integer pointed to by pnr_map must be valid on entry (the
+ * current number of valid entries located at biosmap) and will
+ * be updated on return, with the new number of valid entries
+ * (something no more than max_nr_map.)
+ *
+ * The return value from sanitize_e820_map() is zero if it
+ * successfully 'sanitized' the map entries passed in, and is -1
+ * if it did nothing, which can happen if either of (1) it was
+ * only passed one map entry, or (2) any of the input map entries
+ * were invalid (start + size < start, meaning that the size was
+ * so big the described memory range wrapped around through zero.)
+ *
+ * Visually we're performing the following
+ * (1,2,3,4 = memory types)...
+ *
+ * Sample memory map (w/overlaps):
+ * ____22__________________
+ * ______________________4_
+ * ____1111________________
+ * _44_____________________
+ * 11111111________________
+ * ____________________33__
+ * ___________44___________
+ * __________33333_________
+ * ______________22________
+ * ___________________2222_
+ * _________111111111______
+ * _____________________11_
+ * _________________4______
+ *
+ * Sanitized equivalent (no overlap):
+ * 1_______________________
+ * _44_____________________
+ * ___1____________________
+ * ____22__________________
+ * ______11________________
+ * _________1______________
+ * __________3_____________
+ * ___________44___________
+ * _____________33_________
+ * _______________2________
+ * ________________1_______
+ * _________________4______
+ * ___________________2____
+ * ____________________33__
+ * ______________________4_
+ */
+
+int __init sanitize_e820_map(struct e820entry *biosmap, int max_nr_map,
+ int *pnr_map)
+{
+ struct change_member {
+ struct e820entry *pbios; /* pointer to original bios entry */
+ unsigned long long addr; /* address for this change point */
+ };
+static struct change_member change_point_list[2*E820_X_MAX] __initdata;
+static struct change_member *change_point[2*E820_X_MAX] __initdata;
+static struct e820entry *overlap_list[E820_X_MAX] __initdata;
+static struct e820entry new_bios[E820_X_MAX] __initdata;
+ struct change_member *change_tmp;
+ unsigned long current_type, last_type;
+ unsigned long long last_addr;
+ int chgidx, still_changing;
+ int overlap_entries;
+ int new_bios_entry;
+ int old_nr, new_nr, chg_nr;
+ int i;
+
+ /* if there's only one memory region, don't bother */
+ if (*pnr_map < 2)
+ return -1;
+
+ old_nr = *pnr_map;
+ BUG_ON(old_nr > max_nr_map);
+
+ /* bail out if we find any unreasonable addresses in bios map */
+ for (i = 0; i < old_nr; i++)
+ if (biosmap[i].addr + biosmap[i].size < biosmap[i].addr)
+ return -1;
+
+ /* create pointers for initial change-point information (for sorting) */
+ for (i = 0; i < 2 * old_nr; i++)
+ change_point[i] = &change_point_list[i];
+
+ /* record all known change-points (starting and ending addresses),
+ omitting those that are for empty memory regions */
+ chgidx = 0;
+ for (i = 0; i < old_nr; i++) {
+ if (biosmap[i].size != 0) {
+ change_point[chgidx]->addr = biosmap[i].addr;
+ change_point[chgidx++]->pbios = &biosmap[i];
+ change_point[chgidx]->addr = biosmap[i].addr +
+ biosmap[i].size;
+ change_point[chgidx++]->pbios = &biosmap[i];
+ }
+ }
+ chg_nr = chgidx;
+
+ /* sort change-point list by memory addresses (low -> high) */
+ still_changing = 1;
+ while (still_changing) {
+ still_changing = 0;
+ for (i = 1; i < chg_nr; i++) {
+ unsigned long long curaddr, lastaddr;
+ unsigned long long curpbaddr, lastpbaddr;
+
+ curaddr = change_point[i]->addr;
+ lastaddr = change_point[i - 1]->addr;
+ curpbaddr = change_point[i]->pbios->addr;
+ lastpbaddr = change_point[i - 1]->pbios->addr;
+
+ /*
+ * swap entries, when:
+ *
+ * curaddr > lastaddr or
+ * curaddr == lastaddr and curaddr == curpbaddr and
+ * lastaddr != lastpbaddr
+ */
+ if (curaddr < lastaddr ||
+ (curaddr == lastaddr && curaddr == curpbaddr &&
+ lastaddr != lastpbaddr)) {
+ change_tmp = change_point[i];
+ change_point[i] = change_point[i-1];
+ change_point[i-1] = change_tmp;
+ still_changing = 1;
+ }
+ }
+ }
+
+ /* create a new bios memory map, removing overlaps */
+ overlap_entries = 0; /* number of entries in the overlap table */
+ new_bios_entry = 0; /* index for creating new bios map entries */
+ last_type = 0; /* start with undefined memory type */
+ last_addr = 0; /* start with 0 as last starting address */
+
+ /* loop through change-points, determining affect on the new bios map */
+ for (chgidx = 0; chgidx < chg_nr; chgidx++) {
+ /* keep track of all overlapping bios entries */
+ if (change_point[chgidx]->addr ==
+ change_point[chgidx]->pbios->addr) {
+ /*
+ * add map entry to overlap list (> 1 entry
+ * implies an overlap)
+ */
+ overlap_list[overlap_entries++] =
+ change_point[chgidx]->pbios;
+ } else {
+ /*
+ * remove entry from list (order independent,
+ * so swap with last)
+ */
+ for (i = 0; i < overlap_entries; i++) {
+ if (overlap_list[i] ==
+ change_point[chgidx]->pbios)
+ overlap_list[i] =
+ overlap_list[overlap_entries-1];
+ }
+ overlap_entries--;
+ }
+ /*
+ * if there are overlapping entries, decide which
+ * "type" to use (larger value takes precedence --
+ * 1=usable, 2,3,4,4+=unusable)
+ */
+ current_type = 0;
+ for (i = 0; i < overlap_entries; i++)
+ if (overlap_list[i]->type > current_type)
+ current_type = overlap_list[i]->type;
+ /*
+ * continue building up new bios map based on this
+ * information
+ */
+ if (current_type != last_type) {
+ if (last_type != 0) {
+ new_bios[new_bios_entry].size =
+ change_point[chgidx]->addr - last_addr;
+ /*
+ * move forward only if the new size
+ * was non-zero
+ */
+ if (new_bios[new_bios_entry].size != 0)
+ /*
+ * no more space left for new
+ * bios entries ?
+ */
+ if (++new_bios_entry >= max_nr_map)
+ break;
+ }
+ if (current_type != 0) {
+ new_bios[new_bios_entry].addr =
+ change_point[chgidx]->addr;
+ new_bios[new_bios_entry].type = current_type;
+ last_addr = change_point[chgidx]->addr;
+ }
+ last_type = current_type;
+ }
+ }
+ /* retain count for new bios entries */
+ new_nr = new_bios_entry;
+
+ /* copy new bios mapping into original location */
+ memcpy(biosmap, new_bios, new_nr * sizeof(struct e820entry));
+ *pnr_map = new_nr;
+
+ return 0;
+}
+
+/*
+ * Copy the BIOS e820 map into a safe place.
+ *
+ * Sanity-check it while we're at it..
+ *
+ * If we're lucky and live on a modern system, the setup code
+ * will have given us a memory map that we can use to properly
+ * set up memory. If we aren't, we'll fake a memory map.
+ */
+int __init copy_e820_map(struct e820entry *biosmap, int nr_map)
+{
+ /* Only one memory region (or negative)? Ignore it */
+ if (nr_map < 2)
+ return -1;
+
+ do {
+ u64 start = biosmap->addr;
+ u64 size = biosmap->size;
+ u64 end = start + size;
+ u32 type = biosmap->type;
+
+ /* Overflow in 64 bits? Ignore the memory map. */
+ if (start > end)
+ return -1;
+
+ add_memory_region(start, size, type);
+ } while (biosmap++, --nr_map);
+ return 0;
+}
+
+u64 __init update_memory_range(u64 start, u64 size, unsigned old_type,
+ unsigned new_type)
+{
+ int i;
+ u64 real_updated_size = 0;
+
+ BUG_ON(old_type == new_type);
+
+ for (i = 0; i < e820.nr_map; i++) {
+ struct e820entry *ei = &e820.map[i];
+ u64 final_start, final_end;
+ if (ei->type != old_type)
+ continue;
+ /* totally covered? */
+ if (ei->addr >= start &&
+ (ei->addr + ei->size) <= (start + size)) {
+ ei->type = new_type;
+ real_updated_size += ei->size;
+ continue;
+ }
+ /* partially covered */
+ final_start = max(start, ei->addr);
+ final_end = min(start + size, ei->addr + ei->size);
+ if (final_start >= final_end)
+ continue;
+ add_memory_region(final_start, final_end - final_start,
+ new_type);
+ real_updated_size += final_end - final_start;
+ }
+ return real_updated_size;
+}
+
+void __init update_e820(void)
+{
+ int nr_map;
+
+ nr_map = e820.nr_map;
+ if (sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &nr_map))
+ return;
+ e820.nr_map = nr_map;
+ printk(KERN_INFO "modified physical RAM map:\n");
+ e820_print_map("modified");
+}
+
+/*
+ * Search for the biggest gap in the low 32 bits of the e820
+ * memory space. We pass this space to PCI to assign MMIO resources
+ * for hotplug or unconfigured devices in.
+ * Hopefully the BIOS let enough space left.
+ */
+__init void e820_setup_gap(void)
+{
+ unsigned long gapstart, gapsize, round;
+ unsigned long long last;
+ int i;
+ int found = 0;
+
+ last = 0x100000000ull;
+ gapstart = 0x10000000;
+ gapsize = 0x400000;
+ i = e820.nr_map;
+ while (--i >= 0) {
+ unsigned long long start = e820.map[i].addr;
+ unsigned long long end = start + e820.map[i].size;
+
+ /*
+ * Since "last" is at most 4GB, we know we'll
+ * fit in 32 bits if this condition is true
+ */
+ if (last > end) {
+ unsigned long gap = last - end;
+
+ if (gap > gapsize) {
+ gapsize = gap;
+ gapstart = end;
+ found = 1;
+ }
+ }
+ if (start < last)
+ last = start;
+ }
+
+#ifdef CONFIG_X86_64
+ if (!found) {
+ gapstart = (end_pfn << PAGE_SHIFT) + 1024*1024;
+ printk(KERN_ERR "PCI: Warning: Cannot find a gap in the 32bit "
+ "address range\n"
+ KERN_ERR "PCI: Unassigned devices with 32bit resource "
+ "registers may break!\n");
+ }
+#endif
+
+ /*
+ * See how much we want to round up: start off with
+ * rounding to the next 1MB area.
+ */
+ round = 0x100000;
+ while ((gapsize >> 4) > round)
+ round += round;
+ /* Fun with two's complement */
+ pci_mem_start = (gapstart + round) & -round;
+
+ printk(KERN_INFO
+ "Allocating PCI resources starting at %lx (gap: %lx:%lx)\n",
+ pci_mem_start, gapstart, gapsize);
+}
+
+#if defined(CONFIG_X86_64) || \
+ (defined(CONFIG_X86_32) && defined(CONFIG_HIBERNATION))
+/**
+ * Find the ranges of physical addresses that do not correspond to
+ * e820 RAM areas and mark the corresponding pages as nosave for
+ * hibernation (32 bit) or software suspend and suspend to RAM (64 bit).
+ *
+ * This function requires the e820 map to be sorted and without any
+ * overlapping entries and assumes the first e820 area to be RAM.
+ */
+void __init e820_mark_nosave_regions(unsigned long limit_pfn)
+{
+ int i;
+ unsigned long pfn;
+
+ pfn = PFN_DOWN(e820.map[0].addr + e820.map[0].size);
+ for (i = 1; i < e820.nr_map; i++) {
+ struct e820entry *ei = &e820.map[i];
+
+ if (pfn < PFN_UP(ei->addr))
+ register_nosave_region(pfn, PFN_UP(ei->addr));
+
+ pfn = PFN_DOWN(ei->addr + ei->size);
+ if (ei->type != E820_RAM)
+ register_nosave_region(PFN_UP(ei->addr), pfn);
+
+ if (pfn >= limit_pfn)
+ break;
+ }
+}
+#endif
+
+/*
+ * Early reserved memory areas.
+ */
+#define MAX_EARLY_RES 20
+
+struct early_res {
+ u64 start, end;
+ char name[16];
+};
+static struct early_res early_res[MAX_EARLY_RES] __initdata = {
+ { 0, PAGE_SIZE, "BIOS data page" }, /* BIOS data page */
+#if defined(CONFIG_X86_64) && defined(CONFIG_X86_TRAMPOLINE)
+ { TRAMPOLINE_BASE, TRAMPOLINE_BASE + 2 * PAGE_SIZE, "TRAMPOLINE" },
+#endif
+#if defined(CONFIG_X86_32) && defined(CONFIG_SMP)
+ /*
+ * But first pinch a few for the stack/trampoline stuff
+ * FIXME: Don't need the extra page at 4K, but need to fix
+ * trampoline before removing it. (see the GDT stuff)
+ */
+ { PAGE_SIZE, PAGE_SIZE + PAGE_SIZE, "EX TRAMPOLINE" },
+ /*
+ * Has to be in very low memory so we can execute
+ * real-mode AP code.
+ */
+ { TRAMPOLINE_BASE, TRAMPOLINE_BASE + PAGE_SIZE, "TRAMPOLINE" },
+#endif
+ {}
+};
+
+static int __init find_overlapped_early(u64 start, u64 end)
+{
+ int i;
+ struct early_res *r;
+
+ for (i = 0; i < MAX_EARLY_RES && early_res[i].end; i++) {
+ r = &early_res[i];
+ if (end > r->start && start < r->end)
+ break;
+ }
+
+ return i;
+}
+
+void __init reserve_early(u64 start, u64 end, char *name)
+{
+ int i;
+ struct early_res *r;
+
+ i = find_overlapped_early(start, end);
+ if (i >= MAX_EARLY_RES)
+ panic("Too many early reservations");
+ r = &early_res[i];
+ if (r->end)
+ panic("Overlapping early reservations "
+ "%llx-%llx %s to %llx-%llx %s\n",
+ start, end - 1, name?name:"", r->start,
+ r->end - 1, r->name);
+ r->start = start;
+ r->end = end;
+ if (name)
+ strncpy(r->name, name, sizeof(r->name) - 1);
+}
+
+void __init free_early(u64 start, u64 end)
+{
+ struct early_res *r;
+ int i, j;
+
+ i = find_overlapped_early(start, end);
+ r = &early_res[i];
+ if (i >= MAX_EARLY_RES || r->end != end || r->start != start)
+ panic("free_early on not reserved area: %llx-%llx!",
+ start, end - 1);
+
+ for (j = i + 1; j < MAX_EARLY_RES && early_res[j].end; j++)
+ ;
+
+ memmove(&early_res[i], &early_res[i + 1],
+ (j - 1 - i) * sizeof(struct early_res));
+
+ early_res[j - 1].end = 0;
+}
+
+int __init page_is_reserved_early(unsigned long pagenr)
+{
+ u64 start = (u64)pagenr << PAGE_SHIFT;
+ int i;
+ struct early_res *r;
+
+ i = find_overlapped_early(start, start + PAGE_SIZE);
+ r = &early_res[i];
+ return (i < MAX_EARLY_RES && r->end);
+}
+
+void __init early_res_to_bootmem(u64 start, u64 end)
+{
+ int i;
+ u64 final_start, final_end;
+ for (i = 0; i < MAX_EARLY_RES && early_res[i].end; i++) {
+ struct early_res *r = &early_res[i];
+ final_start = max(start, r->start);
+ final_end = min(end, r->end);
+ if (final_start >= final_end)
+ continue;
+ printk(KERN_INFO " early res: %d [%llx-%llx] %s\n", i,
+ final_start, final_end - 1, r->name);
+ reserve_bootmem(final_start, final_end - final_start,
+ BOOTMEM_DEFAULT);
+ }
+}
+
+/* Check for already reserved areas */
+static inline int __init bad_addr(u64 *addrp, u64 size, u64 align)
+{
+ int i;
+ u64 addr = *addrp;
+ int changed = 0;
+ struct early_res *r;
+again:
+ i = find_overlapped_early(addr, addr + size);
+ r = &early_res[i];
+ if (i < MAX_EARLY_RES && r->end) {
+ *addrp = addr = round_up(r->end, align);
+ changed = 1;
+ goto again;
+ }
+ return changed;
+}
+
+/* Check for already reserved areas */
+static inline int __init bad_addr_size(u64 *addrp, u64 *sizep, u64 align)
+{
+ int i;
+ u64 addr = *addrp, last;
+ u64 size = *sizep;
+ int changed = 0;
+again:
+ last = addr + size;
+ for (i = 0; i < MAX_EARLY_RES && early_res[i].end; i++) {
+ struct early_res *r = &early_res[i];
+ if (last > r->start && addr < r->start) {
+ size = r->start - addr;
+ changed = 1;
+ goto again;
+ }
+ if (last > r->end && addr < r->end) {
+ addr = round_up(r->end, align);
+ size = last - addr;
+ changed = 1;
+ goto again;
+ }
+ if (last <= r->end && addr >= r->start) {
+ (*sizep)++;
+ return 0;
+ }
+ }
+ if (changed) {
+ *addrp = addr;
+ *sizep = size;
+ }
+ return changed;
+}
+
+/*
+ * Find a free area with specified alignment in a specific range.
+ */
+u64 __init find_e820_area(u64 start, u64 end, u64 size, u64 align)
+{
+ int i;
+
+ for (i = 0; i < e820.nr_map; i++) {
+ struct e820entry *ei = &e820.map[i];
+ u64 addr, last;
+ u64 ei_last;
+
+ if (ei->type != E820_RAM)
+ continue;
+ addr = round_up(ei->addr, align);
+ ei_last = ei->addr + ei->size;
+ if (addr < start)
+ addr = round_up(start, align);
+ if (addr >= ei_last)
+ continue;
+ while (bad_addr(&addr, size, align) && addr+size <= ei_last)
+ ;
+ last = addr + size;
+ if (last > ei_last)
+ continue;
+ if (last > end)
+ continue;
+ return addr;
+ }
+ return -1ULL;
+}
+
+/*
+ * Find next free range after *start
+ */
+u64 __init find_e820_area_size(u64 start, u64 *sizep, u64 align)
+{
+ int i;
+
+ for (i = 0; i < e820.nr_map; i++) {
+ struct e820entry *ei = &e820.map[i];
+ u64 addr, last;
+ u64 ei_last;
+
+ if (ei->type != E820_RAM)
+ continue;
+ addr = round_up(ei->addr, align);
+ ei_last = ei->addr + ei->size;
+ if (addr < start)
+ addr = round_up(start, align);
+ if (addr >= ei_last)
+ continue;
+ *sizep = ei_last - addr;
+ while (bad_addr_size(&addr, sizep, align) &&
+ addr + *sizep <= ei_last)
+ ;
+ last = addr + *sizep;
+ if (last > ei_last)
+ continue;
+ return addr;
+ }
+ return -1UL;
+
+}
+
+/*
+ * pre allocated 4k and reserved it in e820
+ */
+u64 __init early_reserve_e820(u64 startt, u64 sizet, u64 align)
+{
+ u64 size = 0;
+ u64 addr;
+ u64 start;
+
+ start = startt;
+ while (size < sizet)
+ start = find_e820_area_size(start, &size, align);
+
+ if (size < sizet)
+ return 0;
+
+ addr = round_down(start + size - sizet, align);
+ update_memory_range(addr, sizet, E820_RAM, E820_RESERVED);
+ printk(KERN_INFO "update e820 for early_reserve_e820\n");
+ update_e820();
+
+ return addr;
+}
+
+#ifdef CONFIG_X86_32
+# ifdef CONFIG_X86_PAE
+# define MAX_ARCH_PFN (1ULL<<(36-PAGE_SHIFT))
+# else
+# define MAX_ARCH_PFN (1ULL<<(32-PAGE_SHIFT))
+# endif
+#else /* CONFIG_X86_32 */
+# define MAX_ARCH_PFN MAXMEM>>PAGE_SHIFT
+#endif
+
+/*
+ * Last pfn which the user wants to use.
+ */
+unsigned long __initdata end_user_pfn = MAX_ARCH_PFN;
+
+/*
+ * Find the highest page frame number we have available
+ */
+unsigned long __init e820_end_of_ram(void)
+{
+ unsigned long last_pfn;
+ unsigned long max_arch_pfn = MAX_ARCH_PFN;
+
+ last_pfn = find_max_pfn_with_active_regions();
+
+ if (last_pfn > max_arch_pfn)
+ last_pfn = max_arch_pfn;
+ if (last_pfn > end_user_pfn)
+ last_pfn = end_user_pfn;
+
+ printk(KERN_INFO "last_pfn = %lu max_arch_pfn = %lu\n",
+ last_pfn, max_arch_pfn);
+ return last_pfn;
+}
+
+/*
+ * Finds an active region in the address range from start_pfn to last_pfn and
+ * returns its range in ei_startpfn and ei_endpfn for the e820 entry.
+ */
+int __init e820_find_active_region(const struct e820entry *ei,
+ unsigned long start_pfn,
+ unsigned long last_pfn,
+ unsigned long *ei_startpfn,
+ unsigned long *ei_endpfn)
+{
+ u64 align = PAGE_SIZE;
+
+ *ei_startpfn = round_up(ei->addr, align) >> PAGE_SHIFT;
+ *ei_endpfn = round_down(ei->addr + ei->size, align) >> PAGE_SHIFT;
+
+ /* Skip map entries smaller than a page */
+ if (*ei_startpfn >= *ei_endpfn)
+ return 0;
+
+ /* Skip if map is outside the node */
+ if (ei->type != E820_RAM || *ei_endpfn <= start_pfn ||
+ *ei_startpfn >= last_pfn)
+ return 0;
+
+ /* Check for overlaps */
+ if (*ei_startpfn < start_pfn)
+ *ei_startpfn = start_pfn;
+ if (*ei_endpfn > last_pfn)
+ *ei_endpfn = last_pfn;
+
+ /* Obey end_user_pfn to save on memmap */
+ if (*ei_startpfn >= end_user_pfn)
+ return 0;
+ if (*ei_endpfn > end_user_pfn)
+ *ei_endpfn = end_user_pfn;
+
+ return 1;
+}
+
+/* Walk the e820 map and register active regions within a node */
+void __init e820_register_active_regions(int nid, unsigned long start_pfn,
+ unsigned long last_pfn)
+{
+ unsigned long ei_startpfn;
+ unsigned long ei_endpfn;
+ int i;
+
+ for (i = 0; i < e820.nr_map; i++)
+ if (e820_find_active_region(&e820.map[i],
+ start_pfn, last_pfn,
+ &ei_startpfn, &ei_endpfn))
+ add_active_range(nid, ei_startpfn, ei_endpfn);
+}
+
+/*
+ * Find the hole size (in bytes) in the memory range.
+ * @start: starting address of the memory range to scan
+ * @end: ending address of the memory range to scan
+ */
+u64 __init e820_hole_size(u64 start, u64 end)
+{
+ unsigned long start_pfn = start >> PAGE_SHIFT;
+ unsigned long last_pfn = end >> PAGE_SHIFT;
+ unsigned long ei_startpfn, ei_endpfn, ram = 0;
+ int i;
+
+ for (i = 0; i < e820.nr_map; i++) {
+ if (e820_find_active_region(&e820.map[i],
+ start_pfn, last_pfn,
+ &ei_startpfn, &ei_endpfn))
+ ram += ei_endpfn - ei_startpfn;
+ }
+ return end - start - ((u64)ram << PAGE_SHIFT);
+}
diff --git a/arch/x86/kernel/e820_32.c b/arch/x86/kernel/e820_32.c
index ed733e7cf4e6..e8a3b968c9fa 100644
--- a/arch/x86/kernel/e820_32.c
+++ b/arch/x86/kernel/e820_32.c
@@ -9,29 +9,12 @@
#include <linux/mm.h>
#include <linux/pfn.h>
#include <linux/uaccess.h>
-#include <linux/suspend.h>
#include <asm/pgtable.h>
#include <asm/page.h>
#include <asm/e820.h>
#include <asm/setup.h>
-struct e820map e820;
-struct change_member {
- struct e820entry *pbios; /* pointer to original bios entry */
- unsigned long long addr; /* address for this change point */
-};
-static struct change_member change_point_list[2*E820MAX] __initdata;
-static struct change_member *change_point[2*E820MAX] __initdata;
-static struct e820entry *overlap_list[E820MAX] __initdata;
-static struct e820entry new_bios[E820MAX] __initdata;
-/* For PCI or other memory-mapped resources */
-unsigned long pci_mem_start = 0x10000000;
-#ifdef CONFIG_PCI
-EXPORT_SYMBOL(pci_mem_start);
-#endif
-extern int user_defined_memmap;
-
static struct resource system_rom_resource = {
.name = "System ROM",
.start = 0xf0000,
@@ -224,398 +207,12 @@ void __init init_iomem_resources(struct resource *code_resource,
}
}
-#if defined(CONFIG_PM) && defined(CONFIG_HIBERNATION)
-/**
- * e820_mark_nosave_regions - Find the ranges of physical addresses that do not
- * correspond to e820 RAM areas and mark the corresponding pages as nosave for
- * hibernation.
- *
- * This function requires the e820 map to be sorted and without any
- * overlapping entries and assumes the first e820 area to be RAM.
- */
-void __init e820_mark_nosave_regions(void)
-{
- int i;
- unsigned long pfn;
-
- pfn = PFN_DOWN(e820.map[0].addr + e820.map[0].size);
- for (i = 1; i < e820.nr_map; i++) {
- struct e820entry *ei = &e820.map[i];
-
- if (pfn < PFN_UP(ei->addr))
- register_nosave_region(pfn, PFN_UP(ei->addr));
-
- pfn = PFN_DOWN(ei->addr + ei->size);
- if (ei->type != E820_RAM)
- register_nosave_region(PFN_UP(ei->addr), pfn);
-
- if (pfn >= max_low_pfn)
- break;
- }
-}
-#endif
-
-void __init add_memory_region(unsigned long long start,
- unsigned long long size, int type)
-{
- int x;
-
- x = e820.nr_map;
-
- if (x == E820MAX) {
- printk(KERN_ERR "Ooops! Too many entries in the memory map!\n");
- return;
- }
-
- e820.map[x].addr = start;
- e820.map[x].size = size;
- e820.map[x].type = type;
- e820.nr_map++;
-} /* add_memory_region */
-
-/*
- * Sanitize the BIOS e820 map.
- *
- * Some e820 responses include overlapping entries. The following
- * replaces the original e820 map with a new one, removing overlaps.
- *
- */
-int __init sanitize_e820_map(struct e820entry * biosmap, char * pnr_map)
-{
- struct change_member *change_tmp;
- unsigned long current_type, last_type;
- unsigned long long last_addr;
- int chgidx, still_changing;
- int overlap_entries;
- int new_bios_entry;
- int old_nr, new_nr, chg_nr;
- int i;
-
- /*
- Visually we're performing the following (1,2,3,4 = memory types)...
-
- Sample memory map (w/overlaps):
- ____22__________________
- ______________________4_
- ____1111________________
- _44_____________________
- 11111111________________
- ____________________33__
- ___________44___________
- __________33333_________
- ______________22________
- ___________________2222_
- _________111111111______
- _____________________11_
- _________________4______
-
- Sanitized equivalent (no overlap):
- 1_______________________
- _44_____________________
- ___1____________________
- ____22__________________
- ______11________________
- _________1______________
- __________3_____________
- ___________44___________
- _____________33_________
- _______________2________
- ________________1_______
- _________________4______
- ___________________2____
- ____________________33__
- ______________________4_
- */
- /* if there's only one memory region, don't bother */
- if (*pnr_map < 2) {
- return -1;
- }
-
- old_nr = *pnr_map;
-
- /* bail out if we find any unreasonable addresses in bios map */
- for (i=0; i<old_nr; i++)
- if (biosmap[i].addr + biosmap[i].size < biosmap[i].addr) {
- return -1;
- }
-
- /* create pointers for initial change-point information (for sorting) */
- for (i=0; i < 2*old_nr; i++)
- change_point[i] = &change_point_list[i];
-
- /* record all known change-points (starting and ending addresses),
- omitting those that are for empty memory regions */
- chgidx = 0;
- for (i=0; i < old_nr; i++) {
- if (biosmap[i].size != 0) {
- change_point[chgidx]->addr = biosmap[i].addr;
- change_point[chgidx++]->pbios = &biosmap[i];
- change_point[chgidx]->addr = biosmap[i].addr + biosmap[i].size;
- change_point[chgidx++]->pbios = &biosmap[i];
- }
- }
- chg_nr = chgidx; /* true number of change-points */
-
- /* sort change-point list by memory addresses (low -> high) */
- still_changing = 1;
- while (still_changing) {
- still_changing = 0;
- for (i=1; i < chg_nr; i++) {
- /* if <current_addr> > <last_addr>, swap */
- /* or, if current=<start_addr> & last=<end_addr>, swap */
- if ((change_point[i]->addr < change_point[i-1]->addr) ||
- ((change_point[i]->addr == change_point[i-1]->addr) &&
- (change_point[i]->addr == change_point[i]->pbios->addr) &&
- (change_point[i-1]->addr != change_point[i-1]->pbios->addr))
- )
- {
- change_tmp = change_point[i];
- change_point[i] = change_point[i-1];
- change_point[i-1] = change_tmp;
- still_changing=1;
- }
- }
- }
-
- /* create a new bios memory map, removing overlaps */
- overlap_entries=0; /* number of entries in the overlap table */
- new_bios_entry=0; /* index for creating new bios map entries */
- last_type = 0; /* start with undefined memory type */
- last_addr = 0; /* start with 0 as last starting address */
- /* loop through change-points, determining affect on the new bios map */
- for (chgidx=0; chgidx < chg_nr; chgidx++)
- {
- /* keep track of all overlapping bios entries */
- if (change_point[chgidx]->addr == change_point[chgidx]->pbios->addr)
- {
- /* add map entry to overlap list (> 1 entry implies an overlap) */
- overlap_list[overlap_entries++]=change_point[chgidx]->pbios;
- }
- else
- {
- /* remove entry from list (order independent, so swap with last) */
- for (i=0; i<overlap_entries; i++)
- {
- if (overlap_list[i] == change_point[chgidx]->pbios)
- overlap_list[i] = overlap_list[overlap_entries-1];
- }
- overlap_entries--;
- }
- /* if there are overlapping entries, decide which "type" to use */
- /* (larger value takes precedence -- 1=usable, 2,3,4,4+=unusable) */
- current_type = 0;
- for (i=0; i<overlap_entries; i++)
- if (overlap_list[i]->type > current_type)
- current_type = overlap_list[i]->type;
- /* continue building up new bios map based on this information */
- if (current_type != last_type) {
- if (last_type != 0) {
- new_bios[new_bios_entry].size =
- change_point[chgidx]->addr - last_addr;
- /* move forward only if the new size was non-zero */
- if (new_bios[new_bios_entry].size != 0)
- if (++new_bios_entry >= E820MAX)
- break; /* no more space left for new bios entries */
- }
- if (current_type != 0) {
- new_bios[new_bios_entry].addr = change_point[chgidx]->addr;
- new_bios[new_bios_entry].type = current_type;
- last_addr=change_point[chgidx]->addr;
- }
- last_type = current_type;
- }
- }
- new_nr = new_bios_entry; /* retain count for new bios entries */
-
- /* copy new bios mapping into original location */
- memcpy(biosmap, new_bios, new_nr*sizeof(struct e820entry));
- *pnr_map = new_nr;
-
- return 0;
-}
-
-/*
- * Copy the BIOS e820 map into a safe place.
- *
- * Sanity-check it while we're at it..
- *
- * If we're lucky and live on a modern system, the setup code
- * will have given us a memory map that we can use to properly
- * set up memory. If we aren't, we'll fake a memory map.
- *
- * We check to see that the memory map contains at least 2 elements
- * before we'll use it, because the detection code in setup.S may
- * not be perfect and most every PC known to man has two memory
- * regions: one from 0 to 640k, and one from 1mb up. (The IBM
- * thinkpad 560x, for example, does not cooperate with the memory
- * detection code.)
- */
-int __init copy_e820_map(struct e820entry *biosmap, int nr_map)
-{
- /* Only one memory region (or negative)? Ignore it */
- if (nr_map < 2)
- return -1;
-
- do {
- u64 start = biosmap->addr;
- u64 size = biosmap->size;
- u64 end = start + size;
- u32 type = biosmap->type;
-
- /* Overflow in 64 bits? Ignore the memory map. */
- if (start > end)
- return -1;
-
- add_memory_region(start, size, type);
- } while (biosmap++, --nr_map);
-
- return 0;
-}
-
-/*
- * Find the highest page frame number we have available
- */
-void __init propagate_e820_map(void)
-{
- int i;
-
- max_pfn = 0;
-
- for (i = 0; i < e820.nr_map; i++) {
- unsigned long start, end;
- /* RAM? */
- if (e820.map[i].type != E820_RAM)
- continue;
- start = PFN_UP(e820.map[i].addr);
- end = PFN_DOWN(e820.map[i].addr + e820.map[i].size);
- if (start >= end)
- continue;
- if (end > max_pfn)
- max_pfn = end;
- memory_present(0, start, end);
- }
-}
-
-/*
- * Register fully available low RAM pages with the bootmem allocator.
- */
-void __init register_bootmem_low_pages(unsigned long max_low_pfn)
-{
- int i;
-
- for (i = 0; i < e820.nr_map; i++) {
- unsigned long curr_pfn, last_pfn, size;
- /*
- * Reserve usable low memory
- */
- if (e820.map[i].type != E820_RAM)
- continue;
- /*
- * We are rounding up the start address of usable memory:
- */
- curr_pfn = PFN_UP(e820.map[i].addr);
- if (curr_pfn >= max_low_pfn)
- continue;
- /*
- * ... and at the end of the usable range downwards:
- */
- last_pfn = PFN_DOWN(e820.map[i].addr + e820.map[i].size);
-
- if (last_pfn > max_low_pfn)
- last_pfn = max_low_pfn;
-
- /*
- * .. finally, did all the rounding and playing
- * around just make the area go away?
- */
- if (last_pfn <= curr_pfn)
- continue;
-
- size = last_pfn - curr_pfn;
- free_bootmem(PFN_PHYS(curr_pfn), PFN_PHYS(size));
- }
-}
-
-void __init e820_register_memory(void)
-{
- unsigned long gapstart, gapsize, round;
- unsigned long long last;
- int i;
-
- /*
- * Search for the biggest gap in the low 32 bits of the e820
- * memory space.
- */
- last = 0x100000000ull;
- gapstart = 0x10000000;
- gapsize = 0x400000;
- i = e820.nr_map;
- while (--i >= 0) {
- unsigned long long start = e820.map[i].addr;
- unsigned long long end = start + e820.map[i].size;
-
- /*
- * Since "last" is at most 4GB, we know we'll
- * fit in 32 bits if this condition is true
- */
- if (last > end) {
- unsigned long gap = last - end;
-
- if (gap > gapsize) {
- gapsize = gap;
- gapstart = end;
- }
- }
- if (start < last)
- last = start;
- }
-
- /*
- * See how much we want to round up: start off with
- * rounding to the next 1MB area.
- */
- round = 0x100000;
- while ((gapsize >> 4) > round)
- round += round;
- /* Fun with two's complement */
- pci_mem_start = (gapstart + round) & -round;
-
- printk("Allocating PCI resources starting at %08lx (gap: %08lx:%08lx)\n",
- pci_mem_start, gapstart, gapsize);
-}
-
-void __init print_memory_map(char *who)
-{
- int i;
-
- for (i = 0; i < e820.nr_map; i++) {
- printk(" %s: %016Lx - %016Lx ", who,
- e820.map[i].addr,
- e820.map[i].addr + e820.map[i].size);
- switch (e820.map[i].type) {
- case E820_RAM: printk("(usable)\n");
- break;
- case E820_RESERVED:
- printk("(reserved)\n");
- break;
- case E820_ACPI:
- printk("(ACPI data)\n");
- break;
- case E820_NVS:
- printk("(ACPI NVS)\n");
- break;
- default: printk("type %u\n", e820.map[i].type);
- break;
- }
- }
-}
-
void __init limit_regions(unsigned long long size)
{
unsigned long long current_addr;
int i;
- print_memory_map("limit_regions start");
+ e820_print_map("limit_regions start");
for (i = 0; i < e820.nr_map; i++) {
current_addr = e820.map[i].addr + e820.map[i].size;
if (current_addr < size)
@@ -634,63 +231,59 @@ void __init limit_regions(unsigned long long size)
e820.nr_map = i + 1;
e820.map[i].size -= current_addr - size;
}
- print_memory_map("limit_regions endfor");
+ e820_print_map("limit_regions endfor");
return;
}
- print_memory_map("limit_regions endfunc");
+ e820_print_map("limit_regions endfunc");
}
-/*
- * This function checks if any part of the range <start,end> is mapped
- * with type.
- */
-int
-e820_any_mapped(u64 start, u64 end, unsigned type)
+/* Overridden in paravirt.c if CONFIG_PARAVIRT */
+char * __init __attribute__((weak)) memory_setup(void)
{
- int i;
- for (i = 0; i < e820.nr_map; i++) {
- const struct e820entry *ei = &e820.map[i];
- if (type && ei->type != type)
- continue;
- if (ei->addr >= end || ei->addr + ei->size <= start)
- continue;
- return 1;
- }
- return 0;
+ return machine_specific_memory_setup();
}
-EXPORT_SYMBOL_GPL(e820_any_mapped);
-
- /*
- * This function checks if the entire range <start,end> is mapped with type.
- *
- * Note: this function only works correct if the e820 table is sorted and
- * not-overlapping, which is the case
- */
-int __init
-e820_all_mapped(unsigned long s, unsigned long e, unsigned type)
+
+void __init setup_memory_map(void)
{
- u64 start = s;
- u64 end = e;
- int i;
- for (i = 0; i < e820.nr_map; i++) {
- struct e820entry *ei = &e820.map[i];
- if (type && ei->type != type)
- continue;
- /* is the region (part) in overlap with the current region ?*/
- if (ei->addr >= end || ei->addr + ei->size <= start)
- continue;
- /* if the region is at the beginning of <start,end> we move
- * start to the end of the region since it's ok until there
+ printk(KERN_INFO "BIOS-provided physical RAM map:\n");
+ e820_print_map(memory_setup());
+}
+
+static int __initdata user_defined_memmap;
+
+/*
+ * "mem=nopentium" disables the 4MB page tables.
+ * "mem=XXX[kKmM]" defines a memory region from HIGH_MEM
+ * to <mem>, overriding the bios size.
+ * "memmap=XXX[KkmM]@XXX[KkmM]" defines a memory region from
+ * <start> to <start>+<mem>, overriding the bios size.
+ *
+ * HPA tells me bootloaders need to parse mem=, so no new
+ * option should be mem= [also see Documentation/i386/boot.txt]
+ */
+static int __init parse_mem(char *arg)
+{
+ if (!arg)
+ return -EINVAL;
+
+ if (strcmp(arg, "nopentium") == 0) {
+ setup_clear_cpu_cap(X86_FEATURE_PSE);
+ } else {
+ /* If the user specifies memory size, we
+ * limit the BIOS-provided memory map to
+ * that size. exactmap can be used to specify
+ * the exact map. mem=number can be used to
+ * trim the existing memory map.
*/
- if (ei->addr <= start)
- start = ei->addr + ei->size;
- /* if start is now at or beyond end, we're done, full
- * coverage */
- if (start >= end)
- return 1; /* we're done */
+ unsigned long long mem_size;
+
+ mem_size = memparse(arg, &arg);
+ limit_regions(mem_size);
+ user_defined_memmap = 1;
}
return 0;
}
+early_param("mem", parse_mem);
static int __init parse_memmap(char *arg)
{
@@ -704,8 +297,9 @@ static int __init parse_memmap(char *arg)
* size before original memory map is
* reset.
*/
- propagate_e820_map();
- saved_max_pfn = max_pfn;
+ e820_register_active_regions(0, 0, -1UL);
+ saved_max_pfn = e820_end_of_ram();
+ remove_all_active_ranges();
#endif
e820.nr_map = 0;
user_defined_memmap = 1;
@@ -736,40 +330,12 @@ static int __init parse_memmap(char *arg)
return 0;
}
early_param("memmap", parse_memmap);
-void __init update_memory_range(u64 start, u64 size, unsigned old_type,
- unsigned new_type)
-{
- int i;
-
- BUG_ON(old_type == new_type);
- for (i = 0; i < e820.nr_map; i++) {
- struct e820entry *ei = &e820.map[i];
- u64 final_start, final_end;
- if (ei->type != old_type)
- continue;
- /* totally covered? */
- if (ei->addr >= start && ei->size <= size) {
- ei->type = new_type;
- continue;
- }
- /* partially covered */
- final_start = max(start, ei->addr);
- final_end = min(start + size, ei->addr + ei->size);
- if (final_start >= final_end)
- continue;
- add_memory_region(final_start, final_end - final_start,
- new_type);
+void __init finish_e820_parsing(void)
+{
+ if (user_defined_memmap) {
+ printk(KERN_INFO "user-defined physical RAM map:\n");
+ e820_print_map("user");
}
}
-void __init update_e820(void)
-{
- u8 nr_map;
- nr_map = e820.nr_map;
- if (sanitize_e820_map(e820.map, &nr_map))
- return;
- e820.nr_map = nr_map;
- printk(KERN_INFO "modified physical RAM map:\n");
- print_memory_map("modified");
-}
diff --git a/arch/x86/kernel/e820_64.c b/arch/x86/kernel/e820_64.c
index 124480c0008d..0afee2ca0bf8 100644
--- a/arch/x86/kernel/e820_64.c
+++ b/arch/x86/kernel/e820_64.c
@@ -17,8 +17,8 @@
#include <linux/kexec.h>
#include <linux/module.h>
#include <linux/mm.h>
-#include <linux/suspend.h>
#include <linux/pfn.h>
+#include <linux/pci.h>
#include <asm/pgtable.h>
#include <asm/page.h>
@@ -29,8 +29,6 @@
#include <asm/kdebug.h>
#include <asm/trampoline.h>
-struct e820map e820;
-
/*
* PFN of last memory page.
*/
@@ -44,285 +42,6 @@ unsigned long end_pfn;
unsigned long max_pfn_mapped;
/*
- * Last pfn which the user wants to use.
- */
-static unsigned long __initdata end_user_pfn = MAXMEM>>PAGE_SHIFT;
-
-/*
- * Early reserved memory areas.
- */
-#define MAX_EARLY_RES 20
-
-struct early_res {
- unsigned long start, end;
- char name[16];
-};
-static struct early_res early_res[MAX_EARLY_RES] __initdata = {
- { 0, PAGE_SIZE, "BIOS data page" }, /* BIOS data page */
-#ifdef CONFIG_X86_TRAMPOLINE
- { TRAMPOLINE_BASE, TRAMPOLINE_BASE + 2 * PAGE_SIZE, "TRAMPOLINE" },
-#endif
- {}
-};
-
-void __init reserve_early(unsigned long start, unsigned long end, char *name)
-{
- int i;
- struct early_res *r;
- for (i = 0; i < MAX_EARLY_RES && early_res[i].end; i++) {
- r = &early_res[i];
- if (end > r->start && start < r->end)
- panic("Overlapping early reservations %lx-%lx %s to %lx-%lx %s\n",
- start, end - 1, name?name:"", r->start, r->end - 1, r->name);
- }
- if (i >= MAX_EARLY_RES)
- panic("Too many early reservations");
- r = &early_res[i];
- r->start = start;
- r->end = end;
- if (name)
- strncpy(r->name, name, sizeof(r->name) - 1);
-}
-
-void __init free_early(unsigned long start, unsigned long end)
-{
- struct early_res *r;
- int i, j;
-
- for (i = 0; i < MAX_EARLY_RES && early_res[i].end; i++) {
- r = &early_res[i];
- if (start == r->start && end == r->end)
- break;
- }
- if (i >= MAX_EARLY_RES || !early_res[i].end)
- panic("free_early on not reserved area: %lx-%lx!", start, end);
-
- for (j = i + 1; j < MAX_EARLY_RES && early_res[j].end; j++)
- ;
-
- memmove(&early_res[i], &early_res[i + 1],
- (j - 1 - i) * sizeof(struct early_res));
-
- early_res[j - 1].end = 0;
-}
-
-void __init early_res_to_bootmem(unsigned long start, unsigned long end)
-{
- int i;
- unsigned long final_start, final_end;
- for (i = 0; i < MAX_EARLY_RES && early_res[i].end; i++) {
- struct early_res *r = &early_res[i];
- final_start = max(start, r->start);
- final_end = min(end, r->end);
- if (final_start >= final_end)
- continue;
- printk(KERN_INFO " early res: %d [%lx-%lx] %s\n", i,
- final_start, final_end - 1, r->name);
- reserve_bootmem_generic(final_start, final_end - final_start);
- }
-}
-
-/* Check for already reserved areas */
-static inline int __init
-bad_addr(unsigned long *addrp, unsigned long size, unsigned long align)
-{
- int i;
- unsigned long addr = *addrp, last;
- int changed = 0;
-again:
- last = addr + size;
- for (i = 0; i < MAX_EARLY_RES && early_res[i].end; i++) {
- struct early_res *r = &early_res[i];
- if (last >= r->start && addr < r->end) {
- *addrp = addr = round_up(r->end, align);
- changed = 1;
- goto again;
- }
- }
- return changed;
-}
-
-/* Check for already reserved areas */
-static inline int __init
-bad_addr_size(unsigned long *addrp, unsigned long *sizep, unsigned long align)
-{
- int i;
- unsigned long addr = *addrp, last;
- unsigned long size = *sizep;
- int changed = 0;
-again:
- last = addr + size;
- for (i = 0; i < MAX_EARLY_RES && early_res[i].end; i++) {
- struct early_res *r = &early_res[i];
- if (last > r->start && addr < r->start) {
- size = r->start - addr;
- changed = 1;
- goto again;
- }
- if (last > r->end && addr < r->end) {
- addr = round_up(r->end, align);
- size = last - addr;
- changed = 1;
- goto again;
- }
- if (last <= r->end && addr >= r->start) {
- (*sizep)++;
- return 0;
- }
- }
- if (changed) {
- *addrp = addr;
- *sizep = size;
- }
- return changed;
-}
-/*
- * This function checks if any part of the range <start,end> is mapped
- * with type.
- */
-int
-e820_any_mapped(unsigned long start, unsigned long end, unsigned type)
-{
- int i;
-
- for (i = 0; i < e820.nr_map; i++) {
- struct e820entry *ei = &e820.map[i];
-
- if (type && ei->type != type)
- continue;
- if (ei->addr >= end || ei->addr + ei->size <= start)
- continue;
- return 1;
- }
- return 0;
-}
-EXPORT_SYMBOL_GPL(e820_any_mapped);
-
-/*
- * This function checks if the entire range <start,end> is mapped with type.
- *
- * Note: this function only works correct if the e820 table is sorted and
- * not-overlapping, which is the case
- */
-int __init e820_all_mapped(unsigned long start, unsigned long end,
- unsigned type)
-{
- int i;
-
- for (i = 0; i < e820.nr_map; i++) {
- struct e820entry *ei = &e820.map[i];
-
- if (type && ei->type != type)
- continue;
- /* is the region (part) in overlap with the current region ?*/
- if (ei->addr >= end || ei->addr + ei->size <= start)
- continue;
-
- /* if the region is at the beginning of <start,end> we move
- * start to the end of the region since it's ok until there
- */
- if (ei->addr <= start)
- start = ei->addr + ei->size;
- /*
- * if start is now at or beyond end, we're done, full
- * coverage
- */
- if (start >= end)
- return 1;
- }
- return 0;
-}
-
-/*
- * Find a free area with specified alignment in a specific range.
- */
-unsigned long __init find_e820_area(unsigned long start, unsigned long end,
- unsigned long size, unsigned long align)
-{
- int i;
-
- for (i = 0; i < e820.nr_map; i++) {
- struct e820entry *ei = &e820.map[i];
- unsigned long addr, last;
- unsigned long ei_last;
-
- if (ei->type != E820_RAM)
- continue;
- addr = round_up(ei->addr, align);
- ei_last = ei->addr + ei->size;
- if (addr < start)
- addr = round_up(start, align);
- if (addr >= ei_last)
- continue;
- while (bad_addr(&addr, size, align) && addr+size <= ei_last)
- ;
- last = addr + size;
- if (last > ei_last)
- continue;
- if (last > end)
- continue;
- return addr;
- }
- return -1UL;
-}
-
-/*
- * Find next free range after *start
- */
-unsigned long __init find_e820_area_size(unsigned long start,
- unsigned long *sizep,
- unsigned long align)
-{
- int i;
-
- for (i = 0; i < e820.nr_map; i++) {
- struct e820entry *ei = &e820.map[i];
- unsigned long addr, last;
- unsigned long ei_last;
-
- if (ei->type != E820_RAM)
- continue;
- addr = round_up(ei->addr, align);
- ei_last = ei->addr + ei->size;
- if (addr < start)
- addr = round_up(start, align);
- if (addr >= ei_last)
- continue;
- *sizep = ei_last - addr;
- while (bad_addr_size(&addr, sizep, align) &&
- addr + *sizep <= ei_last)
- ;
- last = addr + *sizep;
- if (last > ei_last)
- continue;
- return addr;
- }
- return -1UL;
-
-}
-/*
- * Find the highest page frame number we have available
- */
-unsigned long __init e820_end_of_ram(void)
-{
- unsigned long end_pfn;
-
- end_pfn = find_max_pfn_with_active_regions();
-
- if (end_pfn > max_pfn_mapped)
- max_pfn_mapped = end_pfn;
- if (max_pfn_mapped > MAXMEM>>PAGE_SHIFT)
- max_pfn_mapped = MAXMEM>>PAGE_SHIFT;
- if (end_pfn > end_user_pfn)
- end_pfn = end_user_pfn;
- if (end_pfn > max_pfn_mapped)
- end_pfn = max_pfn_mapped;
-
- printk(KERN_INFO "max_pfn_mapped = %lu\n", max_pfn_mapped);
- return end_pfn;
-}
-
-/*
* Mark e820 reserved areas as busy for the resource manager.
*/
void __init e820_reserve_resources(void)
@@ -346,393 +65,6 @@ void __init e820_reserve_resources(void)
}
}
-/*
- * Find the ranges of physical addresses that do not correspond to
- * e820 RAM areas and mark the corresponding pages as nosave for software
- * suspend and suspend to RAM.
- *
- * This function requires the e820 map to be sorted and without any
- * overlapping entries and assumes the first e820 area to be RAM.
- */
-void __init e820_mark_nosave_regions(void)
-{
- int i;
- unsigned long paddr;
-
- paddr = round_down(e820.map[0].addr + e820.map[0].size, PAGE_SIZE);
- for (i = 1; i < e820.nr_map; i++) {
- struct e820entry *ei = &e820.map[i];
-
- if (paddr < ei->addr)
- register_nosave_region(PFN_DOWN(paddr),
- PFN_UP(ei->addr));
-
- paddr = round_down(ei->addr + ei->size, PAGE_SIZE);
- if (ei->type != E820_RAM)
- register_nosave_region(PFN_UP(ei->addr),
- PFN_DOWN(paddr));
-
- if (paddr >= (end_pfn << PAGE_SHIFT))
- break;
- }
-}
-
-/*
- * Finds an active region in the address range from start_pfn to end_pfn and
- * returns its range in ei_startpfn and ei_endpfn for the e820 entry.
- */
-static int __init e820_find_active_region(const struct e820entry *ei,
- unsigned long start_pfn,
- unsigned long end_pfn,
- unsigned long *ei_startpfn,
- unsigned long *ei_endpfn)
-{
- *ei_startpfn = round_up(ei->addr, PAGE_SIZE) >> PAGE_SHIFT;
- *ei_endpfn = round_down(ei->addr + ei->size, PAGE_SIZE) >> PAGE_SHIFT;
-
- /* Skip map entries smaller than a page */
- if (*ei_startpfn >= *ei_endpfn)
- return 0;
-
- /* Check if max_pfn_mapped should be updated */
- if (ei->type != E820_RAM && *ei_endpfn > max_pfn_mapped)
- max_pfn_mapped = *ei_endpfn;
-
- /* Skip if map is outside the node */
- if (ei->type != E820_RAM || *ei_endpfn <= start_pfn ||
- *ei_startpfn >= end_pfn)
- return 0;
-
- /* Check for overlaps */
- if (*ei_startpfn < start_pfn)
- *ei_startpfn = start_pfn;
- if (*ei_endpfn > end_pfn)
- *ei_endpfn = end_pfn;
-
- /* Obey end_user_pfn to save on memmap */
- if (*ei_startpfn >= end_user_pfn)
- return 0;
- if (*ei_endpfn > end_user_pfn)
- *ei_endpfn = end_user_pfn;
-
- return 1;
-}
-
-/* Walk the e820 map and register active regions within a node */
-void __init
-e820_register_active_regions(int nid, unsigned long start_pfn,
- unsigned long end_pfn)
-{
- unsigned long ei_startpfn;
- unsigned long ei_endpfn;
- int i;
-
- for (i = 0; i < e820.nr_map; i++)
- if (e820_find_active_region(&e820.map[i],
- start_pfn, end_pfn,
- &ei_startpfn, &ei_endpfn))
- add_active_range(nid, ei_startpfn, ei_endpfn);
-}
-
-/*
- * Add a memory region to the kernel e820 map.
- */
-void __init add_memory_region(unsigned long start, unsigned long size, int type)
-{
- int x = e820.nr_map;
-
- if (x == E820MAX) {
- printk(KERN_ERR "Ooops! Too many entries in the memory map!\n");
- return;
- }
-
- e820.map[x].addr = start;
- e820.map[x].size = size;
- e820.map[x].type = type;
- e820.nr_map++;
-}
-
-/*
- * Find the hole size (in bytes) in the memory range.
- * @start: starting address of the memory range to scan
- * @end: ending address of the memory range to scan
- */
-unsigned long __init e820_hole_size(unsigned long start, unsigned long end)
-{
- unsigned long start_pfn = start >> PAGE_SHIFT;
- unsigned long end_pfn = end >> PAGE_SHIFT;
- unsigned long ei_startpfn, ei_endpfn, ram = 0;
- int i;
-
- for (i = 0; i < e820.nr_map; i++) {
- if (e820_find_active_region(&e820.map[i],
- start_pfn, end_pfn,
- &ei_startpfn, &ei_endpfn))
- ram += ei_endpfn - ei_startpfn;
- }
- return end - start - (ram << PAGE_SHIFT);
-}
-
-static void __init e820_print_map(char *who)
-{
- int i;
-
- for (i = 0; i < e820.nr_map; i++) {
- printk(KERN_INFO " %s: %016Lx - %016Lx ", who,
- (unsigned long long) e820.map[i].addr,
- (unsigned long long)
- (e820.map[i].addr + e820.map[i].size));
- switch (e820.map[i].type) {
- case E820_RAM:
- printk(KERN_CONT "(usable)\n");
- break;
- case E820_RESERVED:
- printk(KERN_CONT "(reserved)\n");
- break;
- case E820_ACPI:
- printk(KERN_CONT "(ACPI data)\n");
- break;
- case E820_NVS:
- printk(KERN_CONT "(ACPI NVS)\n");
- break;
- default:
- printk(KERN_CONT "type %u\n", e820.map[i].type);
- break;
- }
- }
-}
-
-/*
- * Sanitize the BIOS e820 map.
- *
- * Some e820 responses include overlapping entries. The following
- * replaces the original e820 map with a new one, removing overlaps.
- *
- */
-static int __init sanitize_e820_map(struct e820entry *biosmap, char *pnr_map)
-{
- struct change_member {
- struct e820entry *pbios; /* pointer to original bios entry */
- unsigned long long addr; /* address for this change point */
- };
- static struct change_member change_point_list[2*E820MAX] __initdata;
- static struct change_member *change_point[2*E820MAX] __initdata;
- static struct e820entry *overlap_list[E820MAX] __initdata;
- static struct e820entry new_bios[E820MAX] __initdata;
- struct change_member *change_tmp;
- unsigned long current_type, last_type;
- unsigned long long last_addr;
- int chgidx, still_changing;
- int overlap_entries;
- int new_bios_entry;
- int old_nr, new_nr, chg_nr;
- int i;
-
- /*
- Visually we're performing the following
- (1,2,3,4 = memory types)...
-
- Sample memory map (w/overlaps):
- ____22__________________
- ______________________4_
- ____1111________________
- _44_____________________
- 11111111________________
- ____________________33__
- ___________44___________
- __________33333_________
- ______________22________
- ___________________2222_
- _________111111111______
- _____________________11_
- _________________4______
-
- Sanitized equivalent (no overlap):
- 1_______________________
- _44_____________________
- ___1____________________
- ____22__________________
- ______11________________
- _________1______________
- __________3_____________
- ___________44___________
- _____________33_________
- _______________2________
- ________________1_______
- _________________4______
- ___________________2____
- ____________________33__
- ______________________4_
- */
-
- /* if there's only one memory region, don't bother */
- if (*pnr_map < 2)
- return -1;
-
- old_nr = *pnr_map;
-
- /* bail out if we find any unreasonable addresses in bios map */
- for (i = 0; i < old_nr; i++)
- if (biosmap[i].addr + biosmap[i].size < biosmap[i].addr)
- return -1;
-
- /* create pointers for initial change-point information (for sorting) */
- for (i = 0; i < 2 * old_nr; i++)
- change_point[i] = &change_point_list[i];
-
- /* record all known change-points (starting and ending addresses),
- omitting those that are for empty memory regions */
- chgidx = 0;
- for (i = 0; i < old_nr; i++) {
- if (biosmap[i].size != 0) {
- change_point[chgidx]->addr = biosmap[i].addr;
- change_point[chgidx++]->pbios = &biosmap[i];
- change_point[chgidx]->addr = biosmap[i].addr +
- biosmap[i].size;
- change_point[chgidx++]->pbios = &biosmap[i];
- }
- }
- chg_nr = chgidx;
-
- /* sort change-point list by memory addresses (low -> high) */
- still_changing = 1;
- while (still_changing) {
- still_changing = 0;
- for (i = 1; i < chg_nr; i++) {
- unsigned long long curaddr, lastaddr;
- unsigned long long curpbaddr, lastpbaddr;
-
- curaddr = change_point[i]->addr;
- lastaddr = change_point[i - 1]->addr;
- curpbaddr = change_point[i]->pbios->addr;
- lastpbaddr = change_point[i - 1]->pbios->addr;
-
- /*
- * swap entries, when:
- *
- * curaddr > lastaddr or
- * curaddr == lastaddr and curaddr == curpbaddr and
- * lastaddr != lastpbaddr
- */
- if (curaddr < lastaddr ||
- (curaddr == lastaddr && curaddr == curpbaddr &&
- lastaddr != lastpbaddr)) {
- change_tmp = change_point[i];
- change_point[i] = change_point[i-1];
- change_point[i-1] = change_tmp;
- still_changing = 1;
- }
- }
- }
-
- /* create a new bios memory map, removing overlaps */
- overlap_entries = 0; /* number of entries in the overlap table */
- new_bios_entry = 0; /* index for creating new bios map entries */
- last_type = 0; /* start with undefined memory type */
- last_addr = 0; /* start with 0 as last starting address */
-
- /* loop through change-points, determining affect on the new bios map */
- for (chgidx = 0; chgidx < chg_nr; chgidx++) {
- /* keep track of all overlapping bios entries */
- if (change_point[chgidx]->addr ==
- change_point[chgidx]->pbios->addr) {
- /*
- * add map entry to overlap list (> 1 entry
- * implies an overlap)
- */
- overlap_list[overlap_entries++] =
- change_point[chgidx]->pbios;
- } else {
- /*
- * remove entry from list (order independent,
- * so swap with last)
- */
- for (i = 0; i < overlap_entries; i++) {
- if (overlap_list[i] ==
- change_point[chgidx]->pbios)
- overlap_list[i] =
- overlap_list[overlap_entries-1];
- }
- overlap_entries--;
- }
- /*
- * if there are overlapping entries, decide which
- * "type" to use (larger value takes precedence --
- * 1=usable, 2,3,4,4+=unusable)
- */
- current_type = 0;
- for (i = 0; i < overlap_entries; i++)
- if (overlap_list[i]->type > current_type)
- current_type = overlap_list[i]->type;
- /*
- * continue building up new bios map based on this
- * information
- */
- if (current_type != last_type) {
- if (last_type != 0) {
- new_bios[new_bios_entry].size =
- change_point[chgidx]->addr - last_addr;
- /*
- * move forward only if the new size
- * was non-zero
- */
- if (new_bios[new_bios_entry].size != 0)
- /*
- * no more space left for new
- * bios entries ?
- */
- if (++new_bios_entry >= E820MAX)
- break;
- }
- if (current_type != 0) {
- new_bios[new_bios_entry].addr =
- change_point[chgidx]->addr;
- new_bios[new_bios_entry].type = current_type;
- last_addr = change_point[chgidx]->addr;
- }
- last_type = current_type;
- }
- }
- /* retain count for new bios entries */
- new_nr = new_bios_entry;
-
- /* copy new bios mapping into original location */
- memcpy(biosmap, new_bios, new_nr * sizeof(struct e820entry));
- *pnr_map = new_nr;
-
- return 0;
-}
-
-/*
- * Copy the BIOS e820 map into a safe place.
- *
- * Sanity-check it while we're at it..
- *
- * If we're lucky and live on a modern system, the setup code
- * will have given us a memory map that we can use to properly
- * set up memory. If we aren't, we'll fake a memory map.
- */
-static int __init copy_e820_map(struct e820entry *biosmap, int nr_map)
-{
- /* Only one memory region (or negative)? Ignore it */
- if (nr_map < 2)
- return -1;
-
- do {
- u64 start = biosmap->addr;
- u64 size = biosmap->size;
- u64 end = start + size;
- u32 type = biosmap->type;
-
- /* Overflow in 64 bits? Ignore the memory map. */
- if (start > end)
- return -1;
-
- add_memory_region(start, size, type);
- } while (biosmap++, --nr_map);
- return 0;
-}
-
static void early_panic(char *msg)
{
early_printk(msg);
@@ -740,16 +72,21 @@ static void early_panic(char *msg)
}
/* We're not void only for x86 32-bit compat */
-char * __init machine_specific_memory_setup(void)
+char *__init machine_specific_memory_setup(void)
{
char *who = "BIOS-e820";
+ int new_nr;
/*
* Try to copy the BIOS-supplied E820-map.
*
* Otherwise fake a memory map; one section from 0k->640k,
* the next section from 1mb->appropriate_mem_k
*/
- sanitize_e820_map(boot_params.e820_map, &boot_params.e820_entries);
+ new_nr = boot_params.e820_entries;
+ sanitize_e820_map(boot_params.e820_map,
+ ARRAY_SIZE(boot_params.e820_map),
+ &new_nr);
+ boot_params.e820_entries = new_nr;
if (copy_e820_map(boot_params.e820_map, boot_params.e820_entries) < 0)
early_panic("Cannot find a valid memory map");
printk(KERN_INFO "BIOS-provided physical RAM map:\n");
@@ -787,7 +124,6 @@ static int __init parse_memmap_opt(char *p)
saved_max_pfn = e820_end_of_ram();
remove_all_active_ranges();
#endif
- max_pfn_mapped = 0;
e820.nr_map = 0;
userdef = 1;
return 0;
@@ -818,9 +154,9 @@ early_param("memmap", parse_memmap_opt);
void __init finish_e820_parsing(void)
{
if (userdef) {
- char nr = e820.nr_map;
+ int nr = e820.nr_map;
- if (sanitize_e820_map(e820.map, &nr) < 0)
+ if (sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &nr) < 0)
early_panic("Invalid user supplied memory map");
e820.nr_map = nr;
@@ -829,109 +165,6 @@ void __init finish_e820_parsing(void)
}
}
-void __init update_memory_range(u64 start, u64 size, unsigned old_type,
- unsigned new_type)
-{
- int i;
-
- BUG_ON(old_type == new_type);
-
- for (i = 0; i < e820.nr_map; i++) {
- struct e820entry *ei = &e820.map[i];
- u64 final_start, final_end;
- if (ei->type != old_type)
- continue;
- /* totally covered? */
- if (ei->addr >= start && ei->size <= size) {
- ei->type = new_type;
- continue;
- }
- /* partially covered */
- final_start = max(start, ei->addr);
- final_end = min(start + size, ei->addr + ei->size);
- if (final_start >= final_end)
- continue;
- add_memory_region(final_start, final_end - final_start,
- new_type);
- }
-}
-
-void __init update_e820(void)
-{
- u8 nr_map;
-
- nr_map = e820.nr_map;
- if (sanitize_e820_map(e820.map, &nr_map))
- return;
- e820.nr_map = nr_map;
- printk(KERN_INFO "modified physical RAM map:\n");
- e820_print_map("modified");
-}
-
-unsigned long pci_mem_start = 0xaeedbabe;
-EXPORT_SYMBOL(pci_mem_start);
-
-/*
- * Search for the biggest gap in the low 32 bits of the e820
- * memory space. We pass this space to PCI to assign MMIO resources
- * for hotplug or unconfigured devices in.
- * Hopefully the BIOS let enough space left.
- */
-__init void e820_setup_gap(void)
-{
- unsigned long gapstart, gapsize, round;
- unsigned long last;
- int i;
- int found = 0;
-
- last = 0x100000000ull;
- gapstart = 0x10000000;
- gapsize = 0x400000;
- i = e820.nr_map;
- while (--i >= 0) {
- unsigned long long start = e820.map[i].addr;
- unsigned long long end = start + e820.map[i].size;
-
- /*
- * Since "last" is at most 4GB, we know we'll
- * fit in 32 bits if this condition is true
- */
- if (last > end) {
- unsigned long gap = last - end;
-
- if (gap > gapsize) {
- gapsize = gap;
- gapstart = end;
- found = 1;
- }
- }
- if (start < last)
- last = start;
- }
-
- if (!found) {
- gapstart = (end_pfn << PAGE_SHIFT) + 1024*1024;
- printk(KERN_ERR "PCI: Warning: Cannot find a gap in the 32bit "
- "address range\n"
- KERN_ERR "PCI: Unassigned devices with 32bit resource "
- "registers may break!\n");
- }
-
- /*
- * See how much we want to round up: start off with
- * rounding to the next 1MB area.
- */
- round = 0x100000;
- while ((gapsize >> 4) > round)
- round += round;
- /* Fun with two's complement */
- pci_mem_start = (gapstart + round) & -round;
-
- printk(KERN_INFO
- "Allocating PCI resources starting at %lx (gap: %lx:%lx)\n",
- pci_mem_start, gapstart, gapsize);
-}
-
int __init arch_get_ram_range(int slot, u64 *addr, u64 *size)
{
int i;
diff --git a/arch/x86/kernel/early-quirks.c b/arch/x86/kernel/early-quirks.c
index 9f51e1ea9e82..84fd9f2a28ff 100644
--- a/arch/x86/kernel/early-quirks.c
+++ b/arch/x86/kernel/early-quirks.c
@@ -98,17 +98,6 @@ static void __init nvidia_bugs(int num, int slot, int func)
}
-static void __init ati_bugs(int num, int slot, int func)
-{
-#ifdef CONFIG_X86_IO_APIC
- if (timer_over_8254 == 1) {
- timer_over_8254 = 0;
- printk(KERN_INFO
- "ATI board detected. Disabling timer routing over 8254.\n");
- }
-#endif
-}
-
#define QFLAG_APPLY_ONCE 0x1
#define QFLAG_APPLIED 0x2
#define QFLAG_DONE (QFLAG_APPLY_ONCE|QFLAG_APPLIED)
@@ -126,8 +115,6 @@ static struct chipset early_qrk[] __initdata = {
PCI_CLASS_BRIDGE_PCI, PCI_ANY_ID, QFLAG_APPLY_ONCE, nvidia_bugs },
{ PCI_VENDOR_ID_VIA, PCI_ANY_ID,
PCI_CLASS_BRIDGE_PCI, PCI_ANY_ID, QFLAG_APPLY_ONCE, via_bugs },
- { PCI_VENDOR_ID_ATI, PCI_ANY_ID,
- PCI_CLASS_BRIDGE_PCI, PCI_ANY_ID, QFLAG_APPLY_ONCE, ati_bugs },
{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB,
PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, fix_hypertransport_config },
{}
diff --git a/arch/x86/kernel/efi.c b/arch/x86/kernel/efi.c
index 77d424cf68b3..d5c7fcdd1861 100644
--- a/arch/x86/kernel/efi.c
+++ b/arch/x86/kernel/efi.c
@@ -213,6 +213,48 @@ unsigned long efi_get_time(void)
eft.minute, eft.second);
}
+/*
+ * Tell the kernel about the EFI memory map. This might include
+ * more than the max 128 entries that can fit in the e820 legacy
+ * (zeropage) memory map.
+ */
+
+static void __init add_efi_memmap(void)
+{
+ void *p;
+
+ for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
+ efi_memory_desc_t *md = p;
+ unsigned long long start = md->phys_addr;
+ unsigned long long size = md->num_pages << EFI_PAGE_SHIFT;
+ int e820_type;
+
+ if (md->attribute & EFI_MEMORY_WB)
+ e820_type = E820_RAM;
+ else
+ e820_type = E820_RESERVED;
+ add_memory_region(start, size, e820_type);
+ }
+ sanitize_e820_map(e820.map, ARRAY_SIZE(e820.map), &e820.nr_map);
+}
+
+void __init efi_reserve_early(void)
+{
+ unsigned long pmap;
+
+ pmap = boot_params.efi_info.efi_memmap;
+#ifdef CONFIG_X86_64
+ pmap += (__u64)boot_params.efi_info.efi_memmap_hi << 32;
+#endif
+ memmap.phys_map = (void *)pmap;
+ memmap.nr_map = boot_params.efi_info.efi_memmap_size /
+ boot_params.efi_info.efi_memdesc_size;
+ memmap.desc_version = boot_params.efi_info.efi_memdesc_version;
+ memmap.desc_size = boot_params.efi_info.efi_memdesc_size;
+ reserve_early(pmap, pmap + memmap.nr_map * memmap.desc_size,
+ "EFI memmap");
+}
+
#if EFI_DEBUG
static void __init print_efi_memmap(void)
{
@@ -242,21 +284,11 @@ void __init efi_init(void)
int i = 0;
void *tmp;
-#ifdef CONFIG_X86_32
efi_phys.systab = (efi_system_table_t *)boot_params.efi_info.efi_systab;
- memmap.phys_map = (void *)boot_params.efi_info.efi_memmap;
-#else
- efi_phys.systab = (efi_system_table_t *)
- (boot_params.efi_info.efi_systab |
- ((__u64)boot_params.efi_info.efi_systab_hi<<32));
- memmap.phys_map = (void *)
- (boot_params.efi_info.efi_memmap |
- ((__u64)boot_params.efi_info.efi_memmap_hi<<32));
+#ifdef CONFIG_X86_64
+ efi_phys.systab = (void *)efi_phys.systab +
+ ((__u64)boot_params.efi_info.efi_systab_hi<<32);
#endif
- memmap.nr_map = boot_params.efi_info.efi_memmap_size /
- boot_params.efi_info.efi_memdesc_size;
- memmap.desc_version = boot_params.efi_info.efi_memdesc_version;
- memmap.desc_size = boot_params.efi_info.efi_memdesc_size;
efi.systab = early_ioremap((unsigned long)efi_phys.systab,
sizeof(efi_system_table_t));
@@ -370,6 +402,7 @@ void __init efi_init(void)
if (memmap.desc_size != sizeof(efi_memory_desc_t))
printk(KERN_WARNING "Kernel-defined memdesc"
"doesn't match the one from EFI!\n");
+ add_efi_memmap();
/* Setup for EFI runtime service */
reboot_type = BOOT_EFI;
diff --git a/arch/x86/kernel/efi_64.c b/arch/x86/kernel/efi_64.c
index d0060fdcccac..652c5287215f 100644
--- a/arch/x86/kernel/efi_64.c
+++ b/arch/x86/kernel/efi_64.c
@@ -97,13 +97,7 @@ void __init efi_call_phys_epilog(void)
early_runtime_code_mapping_set_exec(0);
}
-void __init efi_reserve_bootmem(void)
-{
- reserve_bootmem_generic((unsigned long)memmap.phys_map,
- memmap.nr_map * memmap.desc_size);
-}
-
-void __iomem * __init efi_ioremap(unsigned long phys_addr, unsigned long size)
+void __iomem *__init efi_ioremap(unsigned long phys_addr, unsigned long size)
{
static unsigned pages_mapped __initdata;
unsigned i, pages;
diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S
index 2a609dc3271c..235ea9b7716e 100644
--- a/arch/x86/kernel/entry_32.S
+++ b/arch/x86/kernel/entry_32.S
@@ -51,7 +51,7 @@
#include <asm/percpu.h>
#include <asm/dwarf2.h>
#include <asm/processor-flags.h>
-#include "irq_vectors.h"
+#include <asm/irq_vectors.h>
/*
* We use macros for low-level operations which need to be overridden
@@ -248,6 +248,7 @@ ENTRY(resume_userspace)
DISABLE_INTERRUPTS(CLBR_ANY) # make sure we don't miss an interrupt
# setting need_resched or sigpending
# between sampling and the iret
+ TRACE_IRQS_OFF
movl TI_flags(%ebp), %ecx
andl $_TIF_WORK_MASK, %ecx # is there any work to be done on
# int/exception return?
@@ -1109,6 +1110,74 @@ ENDPROC(xen_failsafe_callback)
#endif /* CONFIG_XEN */
+#ifdef CONFIG_FTRACE
+#ifdef CONFIG_DYNAMIC_FTRACE
+
+ENTRY(mcount)
+ pushl %eax
+ pushl %ecx
+ pushl %edx
+ movl 0xc(%esp), %eax
+
+.globl mcount_call
+mcount_call:
+ call ftrace_stub
+
+ popl %edx
+ popl %ecx
+ popl %eax
+
+ ret
+END(mcount)
+
+ENTRY(ftrace_caller)
+ pushl %eax
+ pushl %ecx
+ pushl %edx
+ movl 0xc(%esp), %eax
+ movl 0x4(%ebp), %edx
+
+.globl ftrace_call
+ftrace_call:
+ call ftrace_stub
+
+ popl %edx
+ popl %ecx
+ popl %eax
+
+.globl ftrace_stub
+ftrace_stub:
+ ret
+END(ftrace_caller)
+
+#else /* ! CONFIG_DYNAMIC_FTRACE */
+
+ENTRY(mcount)
+ cmpl $ftrace_stub, ftrace_trace_function
+ jnz trace
+.globl ftrace_stub
+ftrace_stub:
+ ret
+
+ /* taken from glibc */
+trace:
+ pushl %eax
+ pushl %ecx
+ pushl %edx
+ movl 0xc(%esp), %eax
+ movl 0x4(%ebp), %edx
+
+ call *ftrace_trace_function
+
+ popl %edx
+ popl %ecx
+ popl %eax
+
+ jmp ftrace_stub
+END(mcount)
+#endif /* CONFIG_DYNAMIC_FTRACE */
+#endif /* CONFIG_FTRACE */
+
.section .rodata,"a"
#include "syscall_table_32.S"
diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S
index 556a8df522a7..4e354fe90f9a 100644
--- a/arch/x86/kernel/entry_64.S
+++ b/arch/x86/kernel/entry_64.S
@@ -54,6 +54,108 @@
.code64
+#ifdef CONFIG_FTRACE
+#ifdef CONFIG_DYNAMIC_FTRACE
+ENTRY(mcount)
+
+ subq $0x38, %rsp
+ movq %rax, (%rsp)
+ movq %rcx, 8(%rsp)
+ movq %rdx, 16(%rsp)
+ movq %rsi, 24(%rsp)
+ movq %rdi, 32(%rsp)
+ movq %r8, 40(%rsp)
+ movq %r9, 48(%rsp)
+
+ movq 0x38(%rsp), %rdi
+
+.globl mcount_call
+mcount_call:
+ call ftrace_stub
+
+ movq 48(%rsp), %r9
+ movq 40(%rsp), %r8
+ movq 32(%rsp), %rdi
+ movq 24(%rsp), %rsi
+ movq 16(%rsp), %rdx
+ movq 8(%rsp), %rcx
+ movq (%rsp), %rax
+ addq $0x38, %rsp
+
+ retq
+END(mcount)
+
+ENTRY(ftrace_caller)
+
+ /* taken from glibc */
+ subq $0x38, %rsp
+ movq %rax, (%rsp)
+ movq %rcx, 8(%rsp)
+ movq %rdx, 16(%rsp)
+ movq %rsi, 24(%rsp)
+ movq %rdi, 32(%rsp)
+ movq %r8, 40(%rsp)
+ movq %r9, 48(%rsp)
+
+ movq 0x38(%rsp), %rdi
+ movq 8(%rbp), %rsi
+
+.globl ftrace_call
+ftrace_call:
+ call ftrace_stub
+
+ movq 48(%rsp), %r9
+ movq 40(%rsp), %r8
+ movq 32(%rsp), %rdi
+ movq 24(%rsp), %rsi
+ movq 16(%rsp), %rdx
+ movq 8(%rsp), %rcx
+ movq (%rsp), %rax
+ addq $0x38, %rsp
+
+.globl ftrace_stub
+ftrace_stub:
+ retq
+END(ftrace_caller)
+
+#else /* ! CONFIG_DYNAMIC_FTRACE */
+ENTRY(mcount)
+ cmpq $ftrace_stub, ftrace_trace_function
+ jnz trace
+.globl ftrace_stub
+ftrace_stub:
+ retq
+
+trace:
+ /* taken from glibc */
+ subq $0x38, %rsp
+ movq %rax, (%rsp)
+ movq %rcx, 8(%rsp)
+ movq %rdx, 16(%rsp)
+ movq %rsi, 24(%rsp)
+ movq %rdi, 32(%rsp)
+ movq %r8, 40(%rsp)
+ movq %r9, 48(%rsp)
+
+ movq 0x38(%rsp), %rdi
+ movq 8(%rbp), %rsi
+
+ call *ftrace_trace_function
+
+ movq 48(%rsp), %r9
+ movq 40(%rsp), %r8
+ movq 32(%rsp), %rdi
+ movq 24(%rsp), %rsi
+ movq 16(%rsp), %rdx
+ movq 8(%rsp), %rcx
+ movq (%rsp), %rax
+ addq $0x38, %rsp
+
+ jmp ftrace_stub
+END(mcount)
+#endif /* CONFIG_DYNAMIC_FTRACE */
+#endif /* CONFIG_FTRACE */
+
#ifndef CONFIG_PREEMPT
#define retint_kernel retint_restore_args
#endif
@@ -1120,10 +1222,6 @@ ENTRY(coprocessor_segment_overrun)
zeroentry do_coprocessor_segment_overrun
END(coprocessor_segment_overrun)
-ENTRY(reserved)
- zeroentry do_reserved
-END(reserved)
-
/* runs on exception stack */
ENTRY(double_fault)
XCPT_FRAME
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
new file mode 100644
index 000000000000..bc5cf8d46742
--- /dev/null
+++ b/arch/x86/kernel/ftrace.c
@@ -0,0 +1,149 @@
+/*
+ * Code for replacing ftrace calls with jumps.
+ *
+ * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
+ *
+ * Thanks goes to Ingo Molnar, for suggesting the idea.
+ * Mathieu Desnoyers, for suggesting postponing the modifications.
+ * Arjan van de Ven, for keeping me straight, and explaining to me
+ * the dangers of modifying code on the run.
+ */
+
+#include <linux/spinlock.h>
+#include <linux/hardirq.h>
+#include <linux/ftrace.h>
+#include <linux/percpu.h>
+#include <linux/init.h>
+#include <linux/list.h>
+
+#include <asm/alternative.h>
+
+#define CALL_BACK 5
+
+/* Long is fine, even if it is only 4 bytes ;-) */
+static long *ftrace_nop;
+
+union ftrace_code_union {
+ char code[5];
+ struct {
+ char e8;
+ int offset;
+ } __attribute__((packed));
+};
+
+static int notrace ftrace_calc_offset(long ip, long addr)
+{
+ return (int)(addr - ip);
+}
+
+notrace unsigned char *ftrace_nop_replace(void)
+{
+ return (char *)ftrace_nop;
+}
+
+notrace unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
+{
+ static union ftrace_code_union calc;
+
+ calc.e8 = 0xe8;
+ calc.offset = ftrace_calc_offset(ip, addr);
+
+ /*
+ * No locking needed, this must be called via kstop_machine
+ * which in essence is like running on a uniprocessor machine.
+ */
+ return calc.code;
+}
+
+notrace int
+ftrace_modify_code(unsigned long ip, unsigned char *old_code,
+ unsigned char *new_code)
+{
+ unsigned replaced;
+ unsigned old = *(unsigned *)old_code; /* 4 bytes */
+ unsigned new = *(unsigned *)new_code; /* 4 bytes */
+ unsigned char newch = new_code[4];
+ int faulted = 0;
+
+ /* move the IP back to the start of the call */
+ ip -= CALL_BACK;
+
+ /*
+ * Note: Due to modules and __init, code can
+ * disappear and change, we need to protect against faulting
+ * as well as code changing.
+ *
+ * No real locking needed, this code is run through
+ * kstop_machine.
+ */
+ asm volatile (
+ "1: lock\n"
+ " cmpxchg %3, (%2)\n"
+ " jnz 2f\n"
+ " movb %b4, 4(%2)\n"
+ "2:\n"
+ ".section .fixup, \"ax\"\n"
+ "3: movl $1, %0\n"
+ " jmp 2b\n"
+ ".previous\n"
+ _ASM_EXTABLE(1b, 3b)
+ : "=r"(faulted), "=a"(replaced)
+ : "r"(ip), "r"(new), "r"(newch),
+ "0"(faulted), "a"(old)
+ : "memory");
+ sync_core();
+
+ if (replaced != old && replaced != new)
+ faulted = 2;
+
+ return faulted;
+}
+
+notrace int ftrace_update_ftrace_func(ftrace_func_t func)
+{
+ unsigned long ip = (unsigned long)(&ftrace_call);
+ unsigned char old[5], *new;
+ int ret;
+
+ ip += CALL_BACK;
+
+ memcpy(old, &ftrace_call, 5);
+ new = ftrace_call_replace(ip, (unsigned long)func);
+ ret = ftrace_modify_code(ip, old, new);
+
+ return ret;
+}
+
+notrace int ftrace_mcount_set(unsigned long *data)
+{
+ unsigned long ip = (long)(&mcount_call);
+ unsigned long *addr = data;
+ unsigned char old[5], *new;
+
+ /* ip is at the location, but modify code will subtact this */
+ ip += CALL_BACK;
+
+ /*
+ * Replace the mcount stub with a pointer to the
+ * ip recorder function.
+ */
+ memcpy(old, &mcount_call, 5);
+ new = ftrace_call_replace(ip, *addr);
+ *addr = ftrace_modify_code(ip, old, new);
+
+ return 0;
+}
+
+int __init ftrace_dyn_arch_init(void *data)
+{
+ const unsigned char *const *noptable = find_nop_table();
+
+ /* This is running in kstop_machine */
+
+ ftrace_mcount_set(data);
+
+ ftrace_nop = (unsigned long *)noptable[CALL_BACK];
+
+ return 0;
+}
+
diff --git a/arch/x86/kernel/genapic_64.c b/arch/x86/kernel/genapic_64.c
index cbaaf69bedb2..1fa8be5bd217 100644
--- a/arch/x86/kernel/genapic_64.c
+++ b/arch/x86/kernel/genapic_64.c
@@ -51,7 +51,7 @@ void __init setup_apic_routing(void)
else
#endif
- if (num_possible_cpus() <= 8)
+ if (max_physical_apicid < 8)
genapic = &apic_flat;
else
genapic = &apic_physflat;
diff --git a/arch/x86/kernel/genx2apic_uv_x.c b/arch/x86/kernel/genx2apic_uv_x.c
index ebf13908a743..45e84acca8a9 100644
--- a/arch/x86/kernel/genx2apic_uv_x.c
+++ b/arch/x86/kernel/genx2apic_uv_x.c
@@ -5,7 +5,7 @@
*
* SGI UV APIC functions (note: not an Intel compatible APIC)
*
- * Copyright (C) 2007 Silicon Graphics, Inc. All rights reserved.
+ * Copyright (C) 2007-2008 Silicon Graphics, Inc. All rights reserved.
*/
#include <linux/threads.h>
@@ -55,37 +55,37 @@ static cpumask_t uv_vector_allocation_domain(int cpu)
int uv_wakeup_secondary(int phys_apicid, unsigned int start_rip)
{
unsigned long val;
- int nasid;
+ int pnode;
- nasid = uv_apicid_to_nasid(phys_apicid);
+ pnode = uv_apicid_to_pnode(phys_apicid);
val = (1UL << UVH_IPI_INT_SEND_SHFT) |
(phys_apicid << UVH_IPI_INT_APIC_ID_SHFT) |
(((long)start_rip << UVH_IPI_INT_VECTOR_SHFT) >> 12) |
APIC_DM_INIT;
- uv_write_global_mmr64(nasid, UVH_IPI_INT, val);
+ uv_write_global_mmr64(pnode, UVH_IPI_INT, val);
mdelay(10);
val = (1UL << UVH_IPI_INT_SEND_SHFT) |
(phys_apicid << UVH_IPI_INT_APIC_ID_SHFT) |
(((long)start_rip << UVH_IPI_INT_VECTOR_SHFT) >> 12) |
APIC_DM_STARTUP;
- uv_write_global_mmr64(nasid, UVH_IPI_INT, val);
+ uv_write_global_mmr64(pnode, UVH_IPI_INT, val);
return 0;
}
static void uv_send_IPI_one(int cpu, int vector)
{
unsigned long val, apicid, lapicid;
- int nasid;
+ int pnode;
apicid = per_cpu(x86_cpu_to_apicid, cpu); /* ZZZ - cache node-local ? */
lapicid = apicid & 0x3f; /* ZZZ macro needed */
- nasid = uv_apicid_to_nasid(apicid);
+ pnode = uv_apicid_to_pnode(apicid);
val =
(1UL << UVH_IPI_INT_SEND_SHFT) | (lapicid <<
UVH_IPI_INT_APIC_ID_SHFT) |
(vector << UVH_IPI_INT_VECTOR_SHFT);
- uv_write_global_mmr64(nasid, UVH_IPI_INT, val);
+ uv_write_global_mmr64(pnode, UVH_IPI_INT, val);
}
static void uv_send_IPI_mask(cpumask_t mask, int vector)
@@ -159,39 +159,81 @@ struct genapic apic_x2apic_uv_x = {
.phys_pkg_id = phys_pkg_id, /* Fixme ZZZ */
};
-static __cpuinit void set_x2apic_extra_bits(int nasid)
+static __cpuinit void set_x2apic_extra_bits(int pnode)
{
- __get_cpu_var(x2apic_extra_bits) = ((nasid >> 1) << 6);
+ __get_cpu_var(x2apic_extra_bits) = (pnode << 6);
}
/*
* Called on boot cpu.
*/
+static __init int boot_pnode_to_blade(int pnode)
+{
+ int blade;
+
+ for (blade = 0; blade < uv_num_possible_blades(); blade++)
+ if (pnode == uv_blade_info[blade].pnode)
+ return blade;
+ BUG();
+}
+
+struct redir_addr {
+ unsigned long redirect;
+ unsigned long alias;
+};
+
+#define DEST_SHIFT UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_0_MMR_DEST_BASE_SHFT
+
+static __initdata struct redir_addr redir_addrs[] = {
+ {UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_0_MMR, UVH_SI_ALIAS0_OVERLAY_CONFIG},
+ {UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_1_MMR, UVH_SI_ALIAS1_OVERLAY_CONFIG},
+ {UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_2_MMR, UVH_SI_ALIAS2_OVERLAY_CONFIG},
+};
+
+static __init void get_lowmem_redirect(unsigned long *base, unsigned long *size)
+{
+ union uvh_si_alias0_overlay_config_u alias;
+ union uvh_rh_gam_alias210_redirect_config_2_mmr_u redirect;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(redir_addrs); i++) {
+ alias.v = uv_read_local_mmr(redir_addrs[i].alias);
+ if (alias.s.base == 0) {
+ *size = (1UL << alias.s.m_alias);
+ redirect.v = uv_read_local_mmr(redir_addrs[i].redirect);
+ *base = (unsigned long)redirect.s.dest_base << DEST_SHIFT;
+ return;
+ }
+ }
+ BUG();
+}
+
static __init void uv_system_init(void)
{
union uvh_si_addr_map_config_u m_n_config;
- int bytes, nid, cpu, lcpu, nasid, last_nasid, blade;
- unsigned long mmr_base;
+ union uvh_node_id_u node_id;
+ unsigned long gnode_upper, lowmem_redir_base, lowmem_redir_size;
+ int bytes, nid, cpu, lcpu, pnode, blade, i, j, m_val, n_val;
+ unsigned long mmr_base, present;
m_n_config.v = uv_read_local_mmr(UVH_SI_ADDR_MAP_CONFIG);
+ m_val = m_n_config.s.m_skt;
+ n_val = m_n_config.s.n_skt;
mmr_base =
uv_read_local_mmr(UVH_RH_GAM_MMR_OVERLAY_CONFIG_MMR) &
~UV_MMR_ENABLE;
printk(KERN_DEBUG "UV: global MMR base 0x%lx\n", mmr_base);
- last_nasid = -1;
- for_each_possible_cpu(cpu) {
- nid = cpu_to_node(cpu);
- nasid = uv_apicid_to_nasid(per_cpu(x86_cpu_to_apicid, cpu));
- if (nasid != last_nasid)
- uv_possible_blades++;
- last_nasid = nasid;
- }
+ for(i = 0; i < UVH_NODE_PRESENT_TABLE_DEPTH; i++)
+ uv_possible_blades +=
+ hweight64(uv_read_local_mmr( UVH_NODE_PRESENT_TABLE + i * 8));
printk(KERN_DEBUG "UV: Found %d blades\n", uv_num_possible_blades());
bytes = sizeof(struct uv_blade_info) * uv_num_possible_blades();
uv_blade_info = alloc_bootmem_pages(bytes);
+ get_lowmem_redirect(&lowmem_redir_base, &lowmem_redir_size);
+
bytes = sizeof(uv_node_to_blade[0]) * num_possible_nodes();
uv_node_to_blade = alloc_bootmem_pages(bytes);
memset(uv_node_to_blade, 255, bytes);
@@ -200,43 +242,56 @@ static __init void uv_system_init(void)
uv_cpu_to_blade = alloc_bootmem_pages(bytes);
memset(uv_cpu_to_blade, 255, bytes);
- last_nasid = -1;
- blade = -1;
- lcpu = -1;
- for_each_possible_cpu(cpu) {
- nid = cpu_to_node(cpu);
- nasid = uv_apicid_to_nasid(per_cpu(x86_cpu_to_apicid, cpu));
- if (nasid != last_nasid) {
- blade++;
- lcpu = -1;
- uv_blade_info[blade].nr_posible_cpus = 0;
+ blade = 0;
+ for (i = 0; i < UVH_NODE_PRESENT_TABLE_DEPTH; i++) {
+ present = uv_read_local_mmr(UVH_NODE_PRESENT_TABLE + i * 8);
+ for (j = 0; j < 64; j++) {
+ if (!test_bit(j, &present))
+ continue;
+ uv_blade_info[blade].pnode = (i * 64 + j);
+ uv_blade_info[blade].nr_possible_cpus = 0;
uv_blade_info[blade].nr_online_cpus = 0;
+ blade++;
}
- last_nasid = nasid;
- lcpu++;
+ }
- uv_cpu_hub_info(cpu)->m_val = m_n_config.s.m_skt;
- uv_cpu_hub_info(cpu)->n_val = m_n_config.s.n_skt;
+ node_id.v = uv_read_local_mmr(UVH_NODE_ID);
+ gnode_upper = (((unsigned long)node_id.s.node_id) &
+ ~((1 << n_val) - 1)) << m_val;
+
+ for_each_present_cpu(cpu) {
+ nid = cpu_to_node(cpu);
+ pnode = uv_apicid_to_pnode(per_cpu(x86_cpu_to_apicid, cpu));
+ blade = boot_pnode_to_blade(pnode);
+ lcpu = uv_blade_info[blade].nr_possible_cpus;
+ uv_blade_info[blade].nr_possible_cpus++;
+
+ uv_cpu_hub_info(cpu)->lowmem_remap_base = lowmem_redir_base;
+ uv_cpu_hub_info(cpu)->lowmem_remap_top =
+ lowmem_redir_base + lowmem_redir_size;
+ uv_cpu_hub_info(cpu)->m_val = m_val;
+ uv_cpu_hub_info(cpu)->n_val = m_val;
uv_cpu_hub_info(cpu)->numa_blade_id = blade;
uv_cpu_hub_info(cpu)->blade_processor_id = lcpu;
- uv_cpu_hub_info(cpu)->local_nasid = nasid;
- uv_cpu_hub_info(cpu)->gnode_upper =
- nasid & ~((1 << uv_hub_info->n_val) - 1);
+ uv_cpu_hub_info(cpu)->pnode = pnode;
+ uv_cpu_hub_info(cpu)->pnode_mask = (1 << n_val) - 1;
+ uv_cpu_hub_info(cpu)->gpa_mask = (1 << (m_val + n_val)) - 1;
+ uv_cpu_hub_info(cpu)->gnode_upper = gnode_upper;
uv_cpu_hub_info(cpu)->global_mmr_base = mmr_base;
uv_cpu_hub_info(cpu)->coherency_domain_number = 0;/* ZZZ */
- uv_blade_info[blade].nasid = nasid;
- uv_blade_info[blade].nr_posible_cpus++;
uv_node_to_blade[nid] = blade;
uv_cpu_to_blade[cpu] = blade;
- printk(KERN_DEBUG "UV cpu %d, apicid 0x%x, nasid %d, nid %d\n",
- cpu, per_cpu(x86_cpu_to_apicid, cpu), nasid, nid);
- printk(KERN_DEBUG "UV lcpu %d, blade %d\n", lcpu, blade);
+ printk(KERN_DEBUG "UV cpu %d, apicid 0x%x, pnode %d, nid %d, "
+ "lcpu %d, blade %d\n",
+ cpu, per_cpu(x86_cpu_to_apicid, cpu), pnode, nid,
+ lcpu, blade);
}
}
/*
* Called on each cpu to initialize the per_cpu UV data area.
+ * ZZZ hotplug not supported yet
*/
void __cpuinit uv_cpu_init(void)
{
@@ -246,5 +301,5 @@ void __cpuinit uv_cpu_init(void)
uv_blade_info[uv_numa_blade_id()].nr_online_cpus++;
if (get_uv_system_type() == UV_NON_UNIQUE_APIC)
- set_x2apic_extra_bits(uv_hub_info->local_nasid);
+ set_x2apic_extra_bits(uv_hub_info->pnode);
}
diff --git a/arch/x86/kernel/head.c b/arch/x86/kernel/head.c
new file mode 100644
index 000000000000..a727c0b9819c
--- /dev/null
+++ b/arch/x86/kernel/head.c
@@ -0,0 +1,73 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include <asm/setup.h>
+#include <asm/bios_ebda.h>
+
+#define BIOS_LOWMEM_KILOBYTES 0x413
+
+/*
+ * The BIOS places the EBDA/XBDA at the top of conventional
+ * memory, and usually decreases the reported amount of
+ * conventional memory (int 0x12) too. This also contains a
+ * workaround for Dell systems that neglect to reserve EBDA.
+ * The same workaround also avoids a problem with the AMD768MPX
+ * chipset: reserve a page before VGA to prevent PCI prefetch
+ * into it (errata #56). Usually the page is reserved anyways,
+ * unless you have no PS/2 mouse plugged in.
+ */
+void __init reserve_ebda_region(void)
+{
+ unsigned int lowmem, ebda_addr;
+
+ /* To determine the position of the EBDA and the */
+ /* end of conventional memory, we need to look at */
+ /* the BIOS data area. In a paravirtual environment */
+ /* that area is absent. We'll just have to assume */
+ /* that the paravirt case can handle memory setup */
+ /* correctly, without our help. */
+ if (paravirt_enabled())
+ return;
+
+ /* end of low (conventional) memory */
+ lowmem = *(unsigned short *)__va(BIOS_LOWMEM_KILOBYTES);
+ lowmem <<= 10;
+
+ /* start of EBDA area */
+ ebda_addr = get_bios_ebda();
+
+ /* Fixup: bios puts an EBDA in the top 64K segment */
+ /* of conventional memory, but does not adjust lowmem. */
+ if ((lowmem - ebda_addr) <= 0x10000)
+ lowmem = ebda_addr;
+
+ /* Fixup: bios does not report an EBDA at all. */
+ /* Some old Dells seem to need 4k anyhow (bugzilla 2990) */
+ if ((ebda_addr == 0) && (lowmem >= 0x9f000))
+ lowmem = 0x9f000;
+
+ /* Paranoia: should never happen, but... */
+ if ((lowmem == 0) || (lowmem >= 0x100000))
+ lowmem = 0x9f000;
+
+ /* reserve all memory between lowmem and the 1MB mark */
+ reserve_early(lowmem, 0x100000, "BIOS reserved");
+}
+
+void __init reserve_setup_data(void)
+{
+ struct setup_data *data;
+ u64 pa_data;
+ char buf[32];
+
+ if (boot_params.hdr.version < 0x0209)
+ return;
+ pa_data = boot_params.hdr.setup_data;
+ while (pa_data) {
+ data = early_ioremap(pa_data, sizeof(*data));
+ sprintf(buf, "setup data %x", data->type);
+ reserve_early(pa_data, pa_data+sizeof(*data)+data->len, buf);
+ pa_data = data->next;
+ early_iounmap(data, sizeof(*data));
+ }
+}
diff --git a/arch/x86/kernel/head32.c b/arch/x86/kernel/head32.c
index 3db059058927..fa1d25dd83e3 100644
--- a/arch/x86/kernel/head32.c
+++ b/arch/x86/kernel/head32.c
@@ -8,7 +8,34 @@
#include <linux/init.h>
#include <linux/start_kernel.h>
+#include <asm/setup.h>
+#include <asm/sections.h>
+#include <asm/e820.h>
+#include <asm/bios_ebda.h>
+
void __init i386_start_kernel(void)
{
+ reserve_early(__pa_symbol(&_text), __pa_symbol(&_end), "TEXT DATA BSS");
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ /* Reserve INITRD */
+ if (boot_params.hdr.type_of_loader && boot_params.hdr.ramdisk_image) {
+ u64 ramdisk_image = boot_params.hdr.ramdisk_image;
+ u64 ramdisk_size = boot_params.hdr.ramdisk_size;
+ u64 ramdisk_end = ramdisk_image + ramdisk_size;
+ reserve_early(ramdisk_image, ramdisk_end, "RAMDISK");
+ }
+#endif
+ reserve_early(init_pg_tables_start, init_pg_tables_end,
+ "INIT_PG_TABLE");
+
+ reserve_ebda_region();
+
+ /*
+ * At this point everything still needed from the boot loader
+ * or BIOS or kernel text should be early reserved or marked not
+ * RAM in e820. All other memory is free game.
+ */
+
start_kernel();
}
diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c
index e25c57b8aa84..c970929bb15d 100644
--- a/arch/x86/kernel/head64.c
+++ b/arch/x86/kernel/head64.c
@@ -25,6 +25,20 @@
#include <asm/e820.h>
#include <asm/bios_ebda.h>
+/* boot cpu pda */
+static struct x8664_pda _boot_cpu_pda __read_mostly;
+
+#ifdef CONFIG_SMP
+/*
+ * We install an empty cpu_pda pointer table to indicate to early users
+ * (numa_set_node) that the cpu_pda pointer table for cpus other than
+ * the boot cpu is not yet setup.
+ */
+static struct x8664_pda *__cpu_pda[NR_CPUS] __initdata;
+#else
+static struct x8664_pda *__cpu_pda[NR_CPUS] __read_mostly;
+#endif
+
static void __init zap_identity_mappings(void)
{
pgd_t *pgd = pgd_offset_k(0UL);
@@ -51,74 +65,6 @@ static void __init copy_bootdata(char *real_mode_data)
}
}
-#define BIOS_LOWMEM_KILOBYTES 0x413
-
-/*
- * The BIOS places the EBDA/XBDA at the top of conventional
- * memory, and usually decreases the reported amount of
- * conventional memory (int 0x12) too. This also contains a
- * workaround for Dell systems that neglect to reserve EBDA.
- * The same workaround also avoids a problem with the AMD768MPX
- * chipset: reserve a page before VGA to prevent PCI prefetch
- * into it (errata #56). Usually the page is reserved anyways,
- * unless you have no PS/2 mouse plugged in.
- */
-static void __init reserve_ebda_region(void)
-{
- unsigned int lowmem, ebda_addr;
-
- /* To determine the position of the EBDA and the */
- /* end of conventional memory, we need to look at */
- /* the BIOS data area. In a paravirtual environment */
- /* that area is absent. We'll just have to assume */
- /* that the paravirt case can handle memory setup */
- /* correctly, without our help. */
- if (paravirt_enabled())
- return;
-
- /* end of low (conventional) memory */
- lowmem = *(unsigned short *)__va(BIOS_LOWMEM_KILOBYTES);
- lowmem <<= 10;
-
- /* start of EBDA area */
- ebda_addr = get_bios_ebda();
-
- /* Fixup: bios puts an EBDA in the top 64K segment */
- /* of conventional memory, but does not adjust lowmem. */
- if ((lowmem - ebda_addr) <= 0x10000)
- lowmem = ebda_addr;
-
- /* Fixup: bios does not report an EBDA at all. */
- /* Some old Dells seem to need 4k anyhow (bugzilla 2990) */
- if ((ebda_addr == 0) && (lowmem >= 0x9f000))
- lowmem = 0x9f000;
-
- /* Paranoia: should never happen, but... */
- if ((lowmem == 0) || (lowmem >= 0x100000))
- lowmem = 0x9f000;
-
- /* reserve all memory between lowmem and the 1MB mark */
- reserve_early(lowmem, 0x100000, "BIOS reserved");
-}
-
-static void __init reserve_setup_data(void)
-{
- struct setup_data *data;
- unsigned long pa_data;
- char buf[32];
-
- if (boot_params.hdr.version < 0x0209)
- return;
- pa_data = boot_params.hdr.setup_data;
- while (pa_data) {
- data = early_ioremap(pa_data, sizeof(*data));
- sprintf(buf, "setup data %x", data->type);
- reserve_early(pa_data, pa_data+sizeof(*data)+data->len, buf);
- pa_data = data->next;
- early_iounmap(data, sizeof(*data));
- }
-}
-
void __init x86_64_start_kernel(char * real_mode_data)
{
int i;
@@ -156,10 +102,12 @@ void __init x86_64_start_kernel(char * real_mode_data)
early_printk("Kernel alive\n");
- for (i = 0; i < NR_CPUS; i++)
- cpu_pda(i) = &boot_cpu_pda[i];
-
+ _cpu_pda = __cpu_pda;
+ cpu_pda(0) = &_boot_cpu_pda;
pda_init(0);
+
+ early_printk("Kernel really alive\n");
+
copy_bootdata(__va(real_mode_data));
reserve_early(__pa_symbol(&_text), __pa_symbol(&_end), "TEXT DATA BSS");
diff --git a/arch/x86/kernel/head_32.S b/arch/x86/kernel/head_32.S
index b2cc73768a9d..b98b338aae1a 100644
--- a/arch/x86/kernel/head_32.S
+++ b/arch/x86/kernel/head_32.S
@@ -189,11 +189,12 @@ default_entry:
* this stage.
*/
-#define KPMDS ((0x100000000-__PAGE_OFFSET) >> 30) /* Number of kernel PMDs */
+#define KPMDS (((-__PAGE_OFFSET) >> 30) & 3) /* Number of kernel PMDs */
xorl %ebx,%ebx /* %ebx is kept at zero */
movl $pa(pg0), %edi
+ movl %edi, pa(init_pg_tables_start)
movl $pa(swapper_pg_pmd), %edx
movl $PTE_ATTR, %eax
10:
@@ -219,6 +220,8 @@ default_entry:
jb 10b
1:
movl %edi,pa(init_pg_tables_end)
+ shrl $12, %eax
+ movl %eax, pa(max_pfn_mapped)
/* Do early initialization of the fixmap area */
movl $pa(swapper_pg_fixmap)+PDE_ATTR,%eax
@@ -228,6 +231,7 @@ default_entry:
page_pde_offset = (__PAGE_OFFSET >> 20);
movl $pa(pg0), %edi
+ movl %edi, pa(init_pg_tables_start)
movl $pa(swapper_pg_dir), %edx
movl $PTE_ATTR, %eax
10:
@@ -249,6 +253,8 @@ page_pde_offset = (__PAGE_OFFSET >> 20);
cmpl %ebp,%eax
jb 10b
movl %edi,pa(init_pg_tables_end)
+ shrl $12, %eax
+ movl %eax, pa(max_pfn_mapped)
/* Do early initialization of the fixmap area */
movl $pa(swapper_pg_fixmap)+PDE_ATTR,%eax
diff --git a/arch/x86/kernel/head_64.S b/arch/x86/kernel/head_64.S
index 10a1955bb1d1..d8ed3259e6e4 100644
--- a/arch/x86/kernel/head_64.S
+++ b/arch/x86/kernel/head_64.S
@@ -18,6 +18,7 @@
#include <asm/page.h>
#include <asm/msr.h>
#include <asm/cache.h>
+#include <asm/processor-flags.h>
#ifdef CONFIG_PARAVIRT
#include <asm/asm-offsets.h>
@@ -154,9 +155,7 @@ ENTRY(secondary_startup_64)
*/
/* Enable PAE mode and PGE */
- xorq %rax, %rax
- btsq $5, %rax
- btsq $7, %rax
+ movl $(X86_CR4_PAE | X86_CR4_PGE), %eax
movq %rax, %cr4
/* Setup early boot stage 4 level pagetables. */
@@ -184,14 +183,10 @@ ENTRY(secondary_startup_64)
1: wrmsr /* Make changes effective */
/* Setup cr0 */
-#define CR0_PM 1 /* protected mode */
-#define CR0_MP (1<<1)
-#define CR0_ET (1<<4)
-#define CR0_NE (1<<5)
-#define CR0_WP (1<<16)
-#define CR0_AM (1<<18)
-#define CR0_PAGING (1<<31)
- movl $CR0_PM|CR0_MP|CR0_ET|CR0_NE|CR0_WP|CR0_AM|CR0_PAGING,%eax
+#define CR0_STATE (X86_CR0_PE | X86_CR0_MP | X86_CR0_ET | \
+ X86_CR0_NE | X86_CR0_WP | X86_CR0_AM | \
+ X86_CR0_PG)
+ movl $CR0_STATE, %eax
/* Make changes effective */
movq %rax, %cr0
@@ -327,11 +322,11 @@ early_idt_ripmsg:
ENTRY(name)
/* Automate the creation of 1 to 1 mapping pmd entries */
-#define PMDS(START, PERM, COUNT) \
- i = 0 ; \
- .rept (COUNT) ; \
- .quad (START) + (i << 21) + (PERM) ; \
- i = i + 1 ; \
+#define PMDS(START, PERM, COUNT) \
+ i = 0 ; \
+ .rept (COUNT) ; \
+ .quad (START) + (i << PMD_SHIFT) + (PERM) ; \
+ i = i + 1 ; \
.endr
/*
diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c
index 9b5cfcdfc426..ea230ec69057 100644
--- a/arch/x86/kernel/hpet.c
+++ b/arch/x86/kernel/hpet.c
@@ -17,7 +17,7 @@
/* FSEC = 10^-15
NSEC = 10^-9 */
-#define FSEC_PER_NSEC 1000000
+#define FSEC_PER_NSEC 1000000L
/*
* HPET address is set in acpi/boot.c, when an ACPI entry exists
@@ -206,20 +206,19 @@ static void hpet_enable_legacy_int(void)
static void hpet_legacy_clockevent_register(void)
{
- uint64_t hpet_freq;
-
/* Start HPET legacy interrupts */
hpet_enable_legacy_int();
/*
- * The period is a femto seconds value. We need to calculate the
- * scaled math multiplication factor for nanosecond to hpet tick
- * conversion.
+ * The mult factor is defined as (include/linux/clockchips.h)
+ * mult/2^shift = cyc/ns (in contrast to ns/cyc in clocksource.h)
+ * hpet_period is in units of femtoseconds (per cycle), so
+ * mult/2^shift = cyc/ns = 10^6/hpet_period
+ * mult = (10^6 * 2^shift)/hpet_period
+ * mult = (FSEC_PER_NSEC << hpet_clockevent.shift)/hpet_period
*/
- hpet_freq = 1000000000000000ULL;
- do_div(hpet_freq, hpet_period);
- hpet_clockevent.mult = div_sc((unsigned long) hpet_freq,
- NSEC_PER_SEC, hpet_clockevent.shift);
+ hpet_clockevent.mult = div_sc((unsigned long) FSEC_PER_NSEC,
+ hpet_period, hpet_clockevent.shift);
/* Calculate the min / max delta */
hpet_clockevent.max_delta_ns = clockevent_delta2ns(0x7FFFFFFF,
&hpet_clockevent);
@@ -324,7 +323,7 @@ static struct clocksource clocksource_hpet = {
static int hpet_clocksource_register(void)
{
- u64 tmp, start, now;
+ u64 start, now;
cycle_t t1;
/* Start the counter */
@@ -351,21 +350,15 @@ static int hpet_clocksource_register(void)
return -ENODEV;
}
- /* Initialize and register HPET clocksource
- *
- * hpet period is in femto seconds per cycle
- * so we need to convert this to ns/cyc units
- * approximated by mult/2^shift
- *
- * fsec/cyc * 1nsec/1000000fsec = nsec/cyc = mult/2^shift
- * fsec/cyc * 1ns/1000000fsec * 2^shift = mult
- * fsec/cyc * 2^shift * 1nsec/1000000fsec = mult
- * (fsec/cyc << shift)/1000000 = mult
- * (hpet_period << shift)/FSEC_PER_NSEC = mult
+ /*
+ * The definition of mult is (include/linux/clocksource.h)
+ * mult/2^shift = ns/cyc and hpet_period is in units of fsec/cyc
+ * so we first need to convert hpet_period to ns/cyc units:
+ * mult/2^shift = ns/cyc = hpet_period/10^6
+ * mult = (hpet_period * 2^shift)/10^6
+ * mult = (hpet_period << shift)/FSEC_PER_NSEC
*/
- tmp = (u64)hpet_period << HPET_SHIFT;
- do_div(tmp, FSEC_PER_NSEC);
- clocksource_hpet.mult = (u32)tmp;
+ clocksource_hpet.mult = div_sc(hpet_period, FSEC_PER_NSEC, HPET_SHIFT);
clocksource_register(&clocksource_hpet);
diff --git a/arch/x86/kernel/i386_ksyms_32.c b/arch/x86/kernel/i386_ksyms_32.c
index deb43785e923..29999dbb754c 100644
--- a/arch/x86/kernel/i386_ksyms_32.c
+++ b/arch/x86/kernel/i386_ksyms_32.c
@@ -1,7 +1,14 @@
+#include <linux/ftrace.h>
#include <linux/module.h>
+
#include <asm/checksum.h>
-#include <asm/desc.h>
#include <asm/pgtable.h>
+#include <asm/desc.h>
+
+#ifdef CONFIG_FTRACE
+/* mcount is defined in assembly */
+EXPORT_SYMBOL(mcount);
+#endif
/* Networking helper routines. */
EXPORT_SYMBOL(csum_partial_copy_generic);
diff --git a/arch/x86/kernel/i8259_32.c b/arch/x86/kernel/i8259.c
index fe631967d625..dc92b49d9204 100644
--- a/arch/x86/kernel/i8259_32.c
+++ b/arch/x86/kernel/i8259.c
@@ -1,8 +1,10 @@
+#include <linux/linkage.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
+#include <linux/timex.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/init.h>
@@ -10,10 +12,12 @@
#include <linux/sysdev.h>
#include <linux/bitops.h>
+#include <asm/acpi.h>
#include <asm/atomic.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/timer.h>
+#include <asm/hw_irq.h>
#include <asm/pgtable.h>
#include <asm/delay.h>
#include <asm/desc.h>
@@ -32,7 +36,7 @@ static int i8259A_auto_eoi;
DEFINE_SPINLOCK(i8259A_lock);
static void mask_and_ack_8259A(unsigned int);
-static struct irq_chip i8259A_chip = {
+struct irq_chip i8259A_chip = {
.name = "XT-PIC",
.mask = disable_8259A_irq,
.disable = disable_8259A_irq,
@@ -125,14 +129,14 @@ static inline int i8259A_irq_real(unsigned int irq)
int irqmask = 1<<irq;
if (irq < 8) {
- outb(0x0B,PIC_MASTER_CMD); /* ISR register */
+ outb(0x0B, PIC_MASTER_CMD); /* ISR register */
value = inb(PIC_MASTER_CMD) & irqmask;
- outb(0x0A,PIC_MASTER_CMD); /* back to the IRR register */
+ outb(0x0A, PIC_MASTER_CMD); /* back to the IRR register */
return value;
}
- outb(0x0B,PIC_SLAVE_CMD); /* ISR register */
+ outb(0x0B, PIC_SLAVE_CMD); /* ISR register */
value = inb(PIC_SLAVE_CMD) & (irqmask >> 8);
- outb(0x0A,PIC_SLAVE_CMD); /* back to the IRR register */
+ outb(0x0A, PIC_SLAVE_CMD); /* back to the IRR register */
return value;
}
@@ -171,12 +175,14 @@ handle_real_irq:
if (irq & 8) {
inb(PIC_SLAVE_IMR); /* DUMMY - (do we need this?) */
outb(cached_slave_mask, PIC_SLAVE_IMR);
- outb(0x60+(irq&7),PIC_SLAVE_CMD);/* 'Specific EOI' to slave */
- outb(0x60+PIC_CASCADE_IR,PIC_MASTER_CMD); /* 'Specific EOI' to master-IRQ2 */
+ /* 'Specific EOI' to slave */
+ outb(0x60+(irq&7), PIC_SLAVE_CMD);
+ /* 'Specific EOI' to master-IRQ2 */
+ outb(0x60+PIC_CASCADE_IR, PIC_MASTER_CMD);
} else {
inb(PIC_MASTER_IMR); /* DUMMY - (do we need this?) */
outb(cached_master_mask, PIC_MASTER_IMR);
- outb(0x60+irq,PIC_MASTER_CMD); /* 'Specific EOI to master */
+ outb(0x60+irq, PIC_MASTER_CMD); /* 'Specific EOI to master */
}
spin_unlock_irqrestore(&i8259A_lock, flags);
return;
@@ -199,7 +205,8 @@ spurious_8259A_irq:
* lets ACK and report it. [once per IRQ]
*/
if (!(spurious_irq_mask & irqmask)) {
- printk(KERN_DEBUG "spurious 8259A interrupt: IRQ%d.\n", irq);
+ printk(KERN_DEBUG
+ "spurious 8259A interrupt: IRQ%d.\n", irq);
spurious_irq_mask |= irqmask;
}
atomic_inc(&irq_err_count);
@@ -290,17 +297,28 @@ void init_8259A(int auto_eoi)
* outb_pic - this has to work on a wide range of PC hardware.
*/
outb_pic(0x11, PIC_MASTER_CMD); /* ICW1: select 8259A-1 init */
- outb_pic(0x20 + 0, PIC_MASTER_IMR); /* ICW2: 8259A-1 IR0-7 mapped to 0x20-0x27 */
- outb_pic(1U << PIC_CASCADE_IR, PIC_MASTER_IMR); /* 8259A-1 (the master) has a slave on IR2 */
+
+ /* ICW2: 8259A-1 IR0-7 mapped to 0x30-0x37 on x86-64,
+ to 0x20-0x27 on i386 */
+ outb_pic(IRQ0_VECTOR, PIC_MASTER_IMR);
+
+ /* 8259A-1 (the master) has a slave on IR2 */
+ outb_pic(1U << PIC_CASCADE_IR, PIC_MASTER_IMR);
+
if (auto_eoi) /* master does Auto EOI */
outb_pic(MASTER_ICW4_DEFAULT | PIC_ICW4_AEOI, PIC_MASTER_IMR);
else /* master expects normal EOI */
outb_pic(MASTER_ICW4_DEFAULT, PIC_MASTER_IMR);
outb_pic(0x11, PIC_SLAVE_CMD); /* ICW1: select 8259A-2 init */
- outb_pic(0x20 + 8, PIC_SLAVE_IMR); /* ICW2: 8259A-2 IR0-7 mapped to 0x28-0x2f */
- outb_pic(PIC_CASCADE_IR, PIC_SLAVE_IMR); /* 8259A-2 is a slave on master's IR2 */
- outb_pic(SLAVE_ICW4_DEFAULT, PIC_SLAVE_IMR); /* (slave's support for AEOI in flat mode is to be investigated) */
+
+ /* ICW2: 8259A-2 IR0-7 mapped to IRQ8_VECTOR */
+ outb_pic(IRQ8_VECTOR, PIC_SLAVE_IMR);
+ /* 8259A-2 is a slave on master's IR2 */
+ outb_pic(PIC_CASCADE_IR, PIC_SLAVE_IMR);
+ /* (slave's support for AEOI in flat mode is to be investigated) */
+ outb_pic(SLAVE_ICW4_DEFAULT, PIC_SLAVE_IMR);
+
if (auto_eoi)
/*
* In AEOI mode we just have to mask the interrupt
@@ -317,93 +335,3 @@ void init_8259A(int auto_eoi)
spin_unlock_irqrestore(&i8259A_lock, flags);
}
-
-/*
- * Note that on a 486, we don't want to do a SIGFPE on an irq13
- * as the irq is unreliable, and exception 16 works correctly
- * (ie as explained in the intel literature). On a 386, you
- * can't use exception 16 due to bad IBM design, so we have to
- * rely on the less exact irq13.
- *
- * Careful.. Not only is IRQ13 unreliable, but it is also
- * leads to races. IBM designers who came up with it should
- * be shot.
- */
-
-
-static irqreturn_t math_error_irq(int cpl, void *dev_id)
-{
- extern void math_error(void __user *);
- outb(0,0xF0);
- if (ignore_fpu_irq || !boot_cpu_data.hard_math)
- return IRQ_NONE;
- math_error((void __user *)get_irq_regs()->ip);
- return IRQ_HANDLED;
-}
-
-/*
- * New motherboards sometimes make IRQ 13 be a PCI interrupt,
- * so allow interrupt sharing.
- */
-static struct irqaction fpu_irq = {
- .handler = math_error_irq,
- .mask = CPU_MASK_NONE,
- .name = "fpu",
-};
-
-void __init init_ISA_irqs (void)
-{
- int i;
-
-#ifdef CONFIG_X86_LOCAL_APIC
- init_bsp_APIC();
-#endif
- init_8259A(0);
-
- /*
- * 16 old-style INTA-cycle interrupts:
- */
- for (i = 0; i < 16; i++) {
- set_irq_chip_and_handler_name(i, &i8259A_chip,
- handle_level_irq, "XT");
- }
-}
-
-/* Overridden in paravirt.c */
-void init_IRQ(void) __attribute__((weak, alias("native_init_IRQ")));
-
-void __init native_init_IRQ(void)
-{
- int i;
-
- /* all the set up before the call gates are initialised */
- pre_intr_init_hook();
-
- /*
- * Cover the whole vector space, no vector can escape
- * us. (some of these will be overridden and become
- * 'special' SMP interrupts)
- */
- for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) {
- int vector = FIRST_EXTERNAL_VECTOR + i;
- if (i >= NR_IRQS)
- break;
- /* SYSCALL_VECTOR was reserved in trap_init. */
- if (!test_bit(vector, used_vectors))
- set_intr_gate(vector, interrupt[i]);
- }
-
- /* setup after call gates are initialised (usually add in
- * the architecture specific gates)
- */
- intr_init_hook();
-
- /*
- * External FPU? Set up irq13 if so, for
- * original braindamaged IBM FERR coupling.
- */
- if (boot_cpu_data.hard_math && !cpu_has_fpu)
- setup_irq(FPU_IRQ, &fpu_irq);
-
- irq_ctx_init(smp_processor_id());
-}
diff --git a/arch/x86/kernel/i8259_64.c b/arch/x86/kernel/i8259_64.c
deleted file mode 100644
index fa57a1568508..000000000000
--- a/arch/x86/kernel/i8259_64.c
+++ /dev/null
@@ -1,512 +0,0 @@
-#include <linux/linkage.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/timex.h>
-#include <linux/slab.h>
-#include <linux/random.h>
-#include <linux/init.h>
-#include <linux/kernel_stat.h>
-#include <linux/sysdev.h>
-#include <linux/bitops.h>
-
-#include <asm/acpi.h>
-#include <asm/atomic.h>
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/hw_irq.h>
-#include <asm/pgtable.h>
-#include <asm/delay.h>
-#include <asm/desc.h>
-#include <asm/apic.h>
-#include <asm/i8259.h>
-
-/*
- * Common place to define all x86 IRQ vectors
- *
- * This builds up the IRQ handler stubs using some ugly macros in irq.h
- *
- * These macros create the low-level assembly IRQ routines that save
- * register context and call do_IRQ(). do_IRQ() then does all the
- * operations that are needed to keep the AT (or SMP IOAPIC)
- * interrupt-controller happy.
- */
-
-#define BI(x,y) \
- BUILD_IRQ(x##y)
-
-#define BUILD_16_IRQS(x) \
- BI(x,0) BI(x,1) BI(x,2) BI(x,3) \
- BI(x,4) BI(x,5) BI(x,6) BI(x,7) \
- BI(x,8) BI(x,9) BI(x,a) BI(x,b) \
- BI(x,c) BI(x,d) BI(x,e) BI(x,f)
-
-/*
- * ISA PIC or low IO-APIC triggered (INTA-cycle or APIC) interrupts:
- * (these are usually mapped to vectors 0x30-0x3f)
- */
-
-/*
- * The IO-APIC gives us many more interrupt sources. Most of these
- * are unused but an SMP system is supposed to have enough memory ...
- * sometimes (mostly wrt. hw bugs) we get corrupted vectors all
- * across the spectrum, so we really want to be prepared to get all
- * of these. Plus, more powerful systems might have more than 64
- * IO-APIC registers.
- *
- * (these are usually mapped into the 0x30-0xff vector range)
- */
- BUILD_16_IRQS(0x2) BUILD_16_IRQS(0x3)
-BUILD_16_IRQS(0x4) BUILD_16_IRQS(0x5) BUILD_16_IRQS(0x6) BUILD_16_IRQS(0x7)
-BUILD_16_IRQS(0x8) BUILD_16_IRQS(0x9) BUILD_16_IRQS(0xa) BUILD_16_IRQS(0xb)
-BUILD_16_IRQS(0xc) BUILD_16_IRQS(0xd) BUILD_16_IRQS(0xe) BUILD_16_IRQS(0xf)
-
-#undef BUILD_16_IRQS
-#undef BI
-
-
-#define IRQ(x,y) \
- IRQ##x##y##_interrupt
-
-#define IRQLIST_16(x) \
- IRQ(x,0), IRQ(x,1), IRQ(x,2), IRQ(x,3), \
- IRQ(x,4), IRQ(x,5), IRQ(x,6), IRQ(x,7), \
- IRQ(x,8), IRQ(x,9), IRQ(x,a), IRQ(x,b), \
- IRQ(x,c), IRQ(x,d), IRQ(x,e), IRQ(x,f)
-
-/* for the irq vectors */
-static void (*__initdata interrupt[NR_VECTORS - FIRST_EXTERNAL_VECTOR])(void) = {
- IRQLIST_16(0x2), IRQLIST_16(0x3),
- IRQLIST_16(0x4), IRQLIST_16(0x5), IRQLIST_16(0x6), IRQLIST_16(0x7),
- IRQLIST_16(0x8), IRQLIST_16(0x9), IRQLIST_16(0xa), IRQLIST_16(0xb),
- IRQLIST_16(0xc), IRQLIST_16(0xd), IRQLIST_16(0xe), IRQLIST_16(0xf)
-};
-
-#undef IRQ
-#undef IRQLIST_16
-
-/*
- * This is the 'legacy' 8259A Programmable Interrupt Controller,
- * present in the majority of PC/AT boxes.
- * plus some generic x86 specific things if generic specifics makes
- * any sense at all.
- * this file should become arch/i386/kernel/irq.c when the old irq.c
- * moves to arch independent land
- */
-
-static int i8259A_auto_eoi;
-DEFINE_SPINLOCK(i8259A_lock);
-static void mask_and_ack_8259A(unsigned int);
-
-static struct irq_chip i8259A_chip = {
- .name = "XT-PIC",
- .mask = disable_8259A_irq,
- .disable = disable_8259A_irq,
- .unmask = enable_8259A_irq,
- .mask_ack = mask_and_ack_8259A,
-};
-
-/*
- * 8259A PIC functions to handle ISA devices:
- */
-
-/*
- * This contains the irq mask for both 8259A irq controllers,
- */
-unsigned int cached_irq_mask = 0xffff;
-
-/*
- * Not all IRQs can be routed through the IO-APIC, eg. on certain (older)
- * boards the timer interrupt is not really connected to any IO-APIC pin,
- * it's fed to the master 8259A's IR0 line only.
- *
- * Any '1' bit in this mask means the IRQ is routed through the IO-APIC.
- * this 'mixed mode' IRQ handling costs nothing because it's only used
- * at IRQ setup time.
- */
-unsigned long io_apic_irqs;
-
-void disable_8259A_irq(unsigned int irq)
-{
- unsigned int mask = 1 << irq;
- unsigned long flags;
-
- spin_lock_irqsave(&i8259A_lock, flags);
- cached_irq_mask |= mask;
- if (irq & 8)
- outb(cached_slave_mask, PIC_SLAVE_IMR);
- else
- outb(cached_master_mask, PIC_MASTER_IMR);
- spin_unlock_irqrestore(&i8259A_lock, flags);
-}
-
-void enable_8259A_irq(unsigned int irq)
-{
- unsigned int mask = ~(1 << irq);
- unsigned long flags;
-
- spin_lock_irqsave(&i8259A_lock, flags);
- cached_irq_mask &= mask;
- if (irq & 8)
- outb(cached_slave_mask, PIC_SLAVE_IMR);
- else
- outb(cached_master_mask, PIC_MASTER_IMR);
- spin_unlock_irqrestore(&i8259A_lock, flags);
-}
-
-int i8259A_irq_pending(unsigned int irq)
-{
- unsigned int mask = 1<<irq;
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&i8259A_lock, flags);
- if (irq < 8)
- ret = inb(PIC_MASTER_CMD) & mask;
- else
- ret = inb(PIC_SLAVE_CMD) & (mask >> 8);
- spin_unlock_irqrestore(&i8259A_lock, flags);
-
- return ret;
-}
-
-void make_8259A_irq(unsigned int irq)
-{
- disable_irq_nosync(irq);
- io_apic_irqs &= ~(1<<irq);
- set_irq_chip_and_handler_name(irq, &i8259A_chip, handle_level_irq,
- "XT");
- enable_irq(irq);
-}
-
-/*
- * This function assumes to be called rarely. Switching between
- * 8259A registers is slow.
- * This has to be protected by the irq controller spinlock
- * before being called.
- */
-static inline int i8259A_irq_real(unsigned int irq)
-{
- int value;
- int irqmask = 1<<irq;
-
- if (irq < 8) {
- outb(0x0B,PIC_MASTER_CMD); /* ISR register */
- value = inb(PIC_MASTER_CMD) & irqmask;
- outb(0x0A,PIC_MASTER_CMD); /* back to the IRR register */
- return value;
- }
- outb(0x0B,PIC_SLAVE_CMD); /* ISR register */
- value = inb(PIC_SLAVE_CMD) & (irqmask >> 8);
- outb(0x0A,PIC_SLAVE_CMD); /* back to the IRR register */
- return value;
-}
-
-/*
- * Careful! The 8259A is a fragile beast, it pretty
- * much _has_ to be done exactly like this (mask it
- * first, _then_ send the EOI, and the order of EOI
- * to the two 8259s is important!
- */
-static void mask_and_ack_8259A(unsigned int irq)
-{
- unsigned int irqmask = 1 << irq;
- unsigned long flags;
-
- spin_lock_irqsave(&i8259A_lock, flags);
- /*
- * Lightweight spurious IRQ detection. We do not want
- * to overdo spurious IRQ handling - it's usually a sign
- * of hardware problems, so we only do the checks we can
- * do without slowing down good hardware unnecessarily.
- *
- * Note that IRQ7 and IRQ15 (the two spurious IRQs
- * usually resulting from the 8259A-1|2 PICs) occur
- * even if the IRQ is masked in the 8259A. Thus we
- * can check spurious 8259A IRQs without doing the
- * quite slow i8259A_irq_real() call for every IRQ.
- * This does not cover 100% of spurious interrupts,
- * but should be enough to warn the user that there
- * is something bad going on ...
- */
- if (cached_irq_mask & irqmask)
- goto spurious_8259A_irq;
- cached_irq_mask |= irqmask;
-
-handle_real_irq:
- if (irq & 8) {
- inb(PIC_SLAVE_IMR); /* DUMMY - (do we need this?) */
- outb(cached_slave_mask, PIC_SLAVE_IMR);
- /* 'Specific EOI' to slave */
- outb(0x60+(irq&7),PIC_SLAVE_CMD);
- /* 'Specific EOI' to master-IRQ2 */
- outb(0x60+PIC_CASCADE_IR,PIC_MASTER_CMD);
- } else {
- inb(PIC_MASTER_IMR); /* DUMMY - (do we need this?) */
- outb(cached_master_mask, PIC_MASTER_IMR);
- /* 'Specific EOI' to master */
- outb(0x60+irq,PIC_MASTER_CMD);
- }
- spin_unlock_irqrestore(&i8259A_lock, flags);
- return;
-
-spurious_8259A_irq:
- /*
- * this is the slow path - should happen rarely.
- */
- if (i8259A_irq_real(irq))
- /*
- * oops, the IRQ _is_ in service according to the
- * 8259A - not spurious, go handle it.
- */
- goto handle_real_irq;
-
- {
- static int spurious_irq_mask;
- /*
- * At this point we can be sure the IRQ is spurious,
- * lets ACK and report it. [once per IRQ]
- */
- if (!(spurious_irq_mask & irqmask)) {
- printk(KERN_DEBUG
- "spurious 8259A interrupt: IRQ%d.\n", irq);
- spurious_irq_mask |= irqmask;
- }
- atomic_inc(&irq_err_count);
- /*
- * Theoretically we do not have to handle this IRQ,
- * but in Linux this does not cause problems and is
- * simpler for us.
- */
- goto handle_real_irq;
- }
-}
-
-static char irq_trigger[2];
-/**
- * ELCR registers (0x4d0, 0x4d1) control edge/level of IRQ
- */
-static void restore_ELCR(char *trigger)
-{
- outb(trigger[0], 0x4d0);
- outb(trigger[1], 0x4d1);
-}
-
-static void save_ELCR(char *trigger)
-{
- /* IRQ 0,1,2,8,13 are marked as reserved */
- trigger[0] = inb(0x4d0) & 0xF8;
- trigger[1] = inb(0x4d1) & 0xDE;
-}
-
-static int i8259A_resume(struct sys_device *dev)
-{
- init_8259A(i8259A_auto_eoi);
- restore_ELCR(irq_trigger);
- return 0;
-}
-
-static int i8259A_suspend(struct sys_device *dev, pm_message_t state)
-{
- save_ELCR(irq_trigger);
- return 0;
-}
-
-static int i8259A_shutdown(struct sys_device *dev)
-{
- /* Put the i8259A into a quiescent state that
- * the kernel initialization code can get it
- * out of.
- */
- outb(0xff, PIC_MASTER_IMR); /* mask all of 8259A-1 */
- outb(0xff, PIC_SLAVE_IMR); /* mask all of 8259A-1 */
- return 0;
-}
-
-static struct sysdev_class i8259_sysdev_class = {
- .name = "i8259",
- .suspend = i8259A_suspend,
- .resume = i8259A_resume,
- .shutdown = i8259A_shutdown,
-};
-
-static struct sys_device device_i8259A = {
- .id = 0,
- .cls = &i8259_sysdev_class,
-};
-
-static int __init i8259A_init_sysfs(void)
-{
- int error = sysdev_class_register(&i8259_sysdev_class);
- if (!error)
- error = sysdev_register(&device_i8259A);
- return error;
-}
-
-device_initcall(i8259A_init_sysfs);
-
-void init_8259A(int auto_eoi)
-{
- unsigned long flags;
-
- i8259A_auto_eoi = auto_eoi;
-
- spin_lock_irqsave(&i8259A_lock, flags);
-
- outb(0xff, PIC_MASTER_IMR); /* mask all of 8259A-1 */
- outb(0xff, PIC_SLAVE_IMR); /* mask all of 8259A-2 */
-
- /*
- * outb_pic - this has to work on a wide range of PC hardware.
- */
- outb_pic(0x11, PIC_MASTER_CMD); /* ICW1: select 8259A-1 init */
- /* ICW2: 8259A-1 IR0-7 mapped to 0x30-0x37 */
- outb_pic(IRQ0_VECTOR, PIC_MASTER_IMR);
- /* 8259A-1 (the master) has a slave on IR2 */
- outb_pic(0x04, PIC_MASTER_IMR);
- if (auto_eoi) /* master does Auto EOI */
- outb_pic(MASTER_ICW4_DEFAULT | PIC_ICW4_AEOI, PIC_MASTER_IMR);
- else /* master expects normal EOI */
- outb_pic(MASTER_ICW4_DEFAULT, PIC_MASTER_IMR);
-
- outb_pic(0x11, PIC_SLAVE_CMD); /* ICW1: select 8259A-2 init */
- /* ICW2: 8259A-2 IR0-7 mapped to 0x38-0x3f */
- outb_pic(IRQ8_VECTOR, PIC_SLAVE_IMR);
- /* 8259A-2 is a slave on master's IR2 */
- outb_pic(PIC_CASCADE_IR, PIC_SLAVE_IMR);
- /* (slave's support for AEOI in flat mode is to be investigated) */
- outb_pic(SLAVE_ICW4_DEFAULT, PIC_SLAVE_IMR);
-
- if (auto_eoi)
- /*
- * In AEOI mode we just have to mask the interrupt
- * when acking.
- */
- i8259A_chip.mask_ack = disable_8259A_irq;
- else
- i8259A_chip.mask_ack = mask_and_ack_8259A;
-
- udelay(100); /* wait for 8259A to initialize */
-
- outb(cached_master_mask, PIC_MASTER_IMR); /* restore master IRQ mask */
- outb(cached_slave_mask, PIC_SLAVE_IMR); /* restore slave IRQ mask */
-
- spin_unlock_irqrestore(&i8259A_lock, flags);
-}
-
-
-
-
-/*
- * IRQ2 is cascade interrupt to second interrupt controller
- */
-
-static struct irqaction irq2 = {
- .handler = no_action,
- .mask = CPU_MASK_NONE,
- .name = "cascade",
-};
-DEFINE_PER_CPU(vector_irq_t, vector_irq) = {
- [0 ... IRQ0_VECTOR - 1] = -1,
- [IRQ0_VECTOR] = 0,
- [IRQ1_VECTOR] = 1,
- [IRQ2_VECTOR] = 2,
- [IRQ3_VECTOR] = 3,
- [IRQ4_VECTOR] = 4,
- [IRQ5_VECTOR] = 5,
- [IRQ6_VECTOR] = 6,
- [IRQ7_VECTOR] = 7,
- [IRQ8_VECTOR] = 8,
- [IRQ9_VECTOR] = 9,
- [IRQ10_VECTOR] = 10,
- [IRQ11_VECTOR] = 11,
- [IRQ12_VECTOR] = 12,
- [IRQ13_VECTOR] = 13,
- [IRQ14_VECTOR] = 14,
- [IRQ15_VECTOR] = 15,
- [IRQ15_VECTOR + 1 ... NR_VECTORS - 1] = -1
-};
-
-void __init init_ISA_irqs (void)
-{
- int i;
-
- init_bsp_APIC();
- init_8259A(0);
-
- for (i = 0; i < NR_IRQS; i++) {
- irq_desc[i].status = IRQ_DISABLED;
- irq_desc[i].action = NULL;
- irq_desc[i].depth = 1;
-
- if (i < 16) {
- /*
- * 16 old-style INTA-cycle interrupts:
- */
- set_irq_chip_and_handler_name(i, &i8259A_chip,
- handle_level_irq, "XT");
- } else {
- /*
- * 'high' PCI IRQs filled in on demand
- */
- irq_desc[i].chip = &no_irq_chip;
- }
- }
-}
-
-void init_IRQ(void) __attribute__((weak, alias("native_init_IRQ")));
-
-void __init native_init_IRQ(void)
-{
- int i;
-
- init_ISA_irqs();
- /*
- * Cover the whole vector space, no vector can escape
- * us. (some of these will be overridden and become
- * 'special' SMP interrupts)
- */
- for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) {
- int vector = FIRST_EXTERNAL_VECTOR + i;
- if (vector != IA32_SYSCALL_VECTOR)
- set_intr_gate(vector, interrupt[i]);
- }
-
-#ifdef CONFIG_SMP
- /*
- * The reschedule interrupt is a CPU-to-CPU reschedule-helper
- * IPI, driven by wakeup.
- */
- set_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt);
-
- /* IPIs for invalidation */
- set_intr_gate(INVALIDATE_TLB_VECTOR_START+0, invalidate_interrupt0);
- set_intr_gate(INVALIDATE_TLB_VECTOR_START+1, invalidate_interrupt1);
- set_intr_gate(INVALIDATE_TLB_VECTOR_START+2, invalidate_interrupt2);
- set_intr_gate(INVALIDATE_TLB_VECTOR_START+3, invalidate_interrupt3);
- set_intr_gate(INVALIDATE_TLB_VECTOR_START+4, invalidate_interrupt4);
- set_intr_gate(INVALIDATE_TLB_VECTOR_START+5, invalidate_interrupt5);
- set_intr_gate(INVALIDATE_TLB_VECTOR_START+6, invalidate_interrupt6);
- set_intr_gate(INVALIDATE_TLB_VECTOR_START+7, invalidate_interrupt7);
-
- /* IPI for generic function call */
- set_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt);
-
- /* Low priority IPI to cleanup after moving an irq */
- set_intr_gate(IRQ_MOVE_CLEANUP_VECTOR, irq_move_cleanup_interrupt);
-#endif
- set_intr_gate(THERMAL_APIC_VECTOR, thermal_interrupt);
- set_intr_gate(THRESHOLD_APIC_VECTOR, threshold_interrupt);
-
- /* self generated IPI for local APIC timer */
- set_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt);
-
- /* IPI vectors for APIC spurious and error interrupts */
- set_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt);
- set_intr_gate(ERROR_APIC_VECTOR, error_interrupt);
-
- if (!acpi_ioapic)
- setup_irq(2, &irq2);
-}
diff --git a/arch/x86/kernel/io_apic_32.c b/arch/x86/kernel/io_apic_32.c
index a40d54fc1fdd..e71f352bfb95 100644
--- a/arch/x86/kernel/io_apic_32.c
+++ b/arch/x86/kernel/io_apic_32.c
@@ -37,12 +37,14 @@
#include <linux/kthread.h>
#include <linux/jiffies.h> /* time_after() */
-#include <asm/io.h>
+#include <linux/io.h>
+#include <linux/smp.h>
+
#include <asm/smp.h>
#include <asm/desc.h>
#include <asm/timer.h>
#include <asm/i8259.h>
-#include <asm/nmi.h>
+#include <linux/nmi.h>
#include <asm/msidef.h>
#include <asm/hypertransport.h>
@@ -58,7 +60,7 @@ static struct { int pin, apic; } ioapic_i8259 = { -1, -1 };
static DEFINE_SPINLOCK(ioapic_lock);
static DEFINE_SPINLOCK(vector_lock);
-int timer_over_8254 __initdata = 1;
+int timer_through_8259 __initdata;
/*
* Is the SiS APIC rmw bug present ?
@@ -72,15 +74,21 @@ int sis_apic_bug = -1;
int nr_ioapic_registers[MAX_IO_APICS];
/* I/O APIC entries */
-struct mpc_config_ioapic mp_ioapics[MAX_IO_APICS];
+struct mp_config_ioapic mp_ioapics[MAX_IO_APICS];
int nr_ioapics;
/* MP IRQ source entries */
-struct mpc_config_intsrc mp_irqs[MAX_IRQ_SOURCES];
+struct mp_config_intsrc mp_irqs[MAX_IRQ_SOURCES];
/* # of MP IRQ source entries */
int mp_irq_entries;
+#if defined(CONFIG_MCA) || defined(CONFIG_EISA)
+int mp_bus_id_to_type[MAX_MP_BUSSES];
+#endif
+
+DECLARE_BITMAP(mp_bus_not_pci, MAX_MP_BUSSES);
+
static int disable_timer_pin_1 __initdata;
/*
@@ -110,7 +118,7 @@ struct io_apic {
static __attribute_const__ struct io_apic __iomem *io_apic_base(int idx)
{
return (void __iomem *) __fix_to_virt(FIX_IO_APIC_BASE_0 + idx)
- + (mp_ioapics[idx].mpc_apicaddr & ~PAGE_MASK);
+ + (mp_ioapics[idx].mp_apicaddr & ~PAGE_MASK);
}
static inline unsigned int io_apic_read(unsigned int apic, unsigned int reg)
@@ -239,7 +247,7 @@ static void __init replace_pin_at_irq(unsigned int irq,
}
}
-static void __modify_IO_APIC_irq (unsigned int irq, unsigned long enable, unsigned long disable)
+static void __modify_IO_APIC_irq(unsigned int irq, unsigned long enable, unsigned long disable)
{
struct irq_pin_list *entry = irq_2_pin + irq;
unsigned int pin, reg;
@@ -259,30 +267,32 @@ static void __modify_IO_APIC_irq (unsigned int irq, unsigned long enable, unsign
}
/* mask = 1 */
-static void __mask_IO_APIC_irq (unsigned int irq)
+static void __mask_IO_APIC_irq(unsigned int irq)
{
- __modify_IO_APIC_irq(irq, 0x00010000, 0);
+ __modify_IO_APIC_irq(irq, IO_APIC_REDIR_MASKED, 0);
}
/* mask = 0 */
-static void __unmask_IO_APIC_irq (unsigned int irq)
+static void __unmask_IO_APIC_irq(unsigned int irq)
{
- __modify_IO_APIC_irq(irq, 0, 0x00010000);
+ __modify_IO_APIC_irq(irq, 0, IO_APIC_REDIR_MASKED);
}
/* mask = 1, trigger = 0 */
-static void __mask_and_edge_IO_APIC_irq (unsigned int irq)
+static void __mask_and_edge_IO_APIC_irq(unsigned int irq)
{
- __modify_IO_APIC_irq(irq, 0x00010000, 0x00008000);
+ __modify_IO_APIC_irq(irq, IO_APIC_REDIR_MASKED,
+ IO_APIC_REDIR_LEVEL_TRIGGER);
}
/* mask = 0, trigger = 1 */
-static void __unmask_and_level_IO_APIC_irq (unsigned int irq)
+static void __unmask_and_level_IO_APIC_irq(unsigned int irq)
{
- __modify_IO_APIC_irq(irq, 0x00008000, 0x00010000);
+ __modify_IO_APIC_irq(irq, IO_APIC_REDIR_LEVEL_TRIGGER,
+ IO_APIC_REDIR_MASKED);
}
-static void mask_IO_APIC_irq (unsigned int irq)
+static void mask_IO_APIC_irq(unsigned int irq)
{
unsigned long flags;
@@ -291,7 +301,7 @@ static void mask_IO_APIC_irq (unsigned int irq)
spin_unlock_irqrestore(&ioapic_lock, flags);
}
-static void unmask_IO_APIC_irq (unsigned int irq)
+static void unmask_IO_APIC_irq(unsigned int irq)
{
unsigned long flags;
@@ -303,7 +313,7 @@ static void unmask_IO_APIC_irq (unsigned int irq)
static void clear_IO_APIC_pin(unsigned int apic, unsigned int pin)
{
struct IO_APIC_route_entry entry;
-
+
/* Check delivery_mode to be sure we're not clearing an SMI pin */
entry = ioapic_read_entry(apic, pin);
if (entry.delivery_mode == dest_SMI)
@@ -315,7 +325,7 @@ static void clear_IO_APIC_pin(unsigned int apic, unsigned int pin)
ioapic_mask_entry(apic, pin);
}
-static void clear_IO_APIC (void)
+static void clear_IO_APIC(void)
{
int apic, pin;
@@ -332,7 +342,7 @@ static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t cpumask)
struct irq_pin_list *entry = irq_2_pin + irq;
unsigned int apicid_value;
cpumask_t tmp;
-
+
cpus_and(tmp, cpumask, cpu_online_map);
if (cpus_empty(tmp))
tmp = TARGET_CPUS;
@@ -361,7 +371,7 @@ static void set_ioapic_affinity_irq(unsigned int irq, cpumask_t cpumask)
# include <linux/kernel_stat.h> /* kstat */
# include <linux/slab.h> /* kmalloc() */
# include <linux/timer.h>
-
+
#define IRQBALANCE_CHECK_ARCH -999
#define MAX_BALANCED_IRQ_INTERVAL (5*HZ)
#define MIN_BALANCED_IRQ_INTERVAL (HZ/2)
@@ -373,14 +383,14 @@ static int physical_balance __read_mostly;
static long balanced_irq_interval __read_mostly = MAX_BALANCED_IRQ_INTERVAL;
static struct irq_cpu_info {
- unsigned long * last_irq;
- unsigned long * irq_delta;
+ unsigned long *last_irq;
+ unsigned long *irq_delta;
unsigned long irq;
} irq_cpu_data[NR_CPUS];
#define CPU_IRQ(cpu) (irq_cpu_data[cpu].irq)
-#define LAST_CPU_IRQ(cpu,irq) (irq_cpu_data[cpu].last_irq[irq])
-#define IRQ_DELTA(cpu,irq) (irq_cpu_data[cpu].irq_delta[irq])
+#define LAST_CPU_IRQ(cpu, irq) (irq_cpu_data[cpu].last_irq[irq])
+#define IRQ_DELTA(cpu, irq) (irq_cpu_data[cpu].irq_delta[irq])
#define IDLE_ENOUGH(cpu,now) \
(idle_cpu(cpu) && ((now) - per_cpu(irq_stat, (cpu)).idle_timestamp > 1))
@@ -419,8 +429,8 @@ inside:
if (cpu == -1)
cpu = NR_CPUS-1;
}
- } while (!cpu_online(cpu) || !IRQ_ALLOWED(cpu,allowed_mask) ||
- (search_idle && !IDLE_ENOUGH(cpu,now)));
+ } while (!cpu_online(cpu) || !IRQ_ALLOWED(cpu, allowed_mask) ||
+ (search_idle && !IDLE_ENOUGH(cpu, now)));
return cpu;
}
@@ -430,15 +440,14 @@ static inline void balance_irq(int cpu, int irq)
unsigned long now = jiffies;
cpumask_t allowed_mask;
unsigned int new_cpu;
-
+
if (irqbalance_disabled)
- return;
+ return;
cpus_and(allowed_mask, cpu_online_map, balance_irq_affinity[irq]);
new_cpu = move(cpu, allowed_mask, now, 1);
- if (cpu != new_cpu) {
+ if (cpu != new_cpu)
set_pending_irq(irq, cpumask_of_cpu(new_cpu));
- }
}
static inline void rotate_irqs_among_cpus(unsigned long useful_load_threshold)
@@ -450,14 +459,14 @@ static inline void rotate_irqs_among_cpus(unsigned long useful_load_threshold)
if (!irq_desc[j].action)
continue;
/* Is it a significant load ? */
- if (IRQ_DELTA(CPU_TO_PACKAGEINDEX(i),j) <
+ if (IRQ_DELTA(CPU_TO_PACKAGEINDEX(i), j) <
useful_load_threshold)
continue;
balance_irq(i, j);
}
}
balanced_irq_interval = max((long)MIN_BALANCED_IRQ_INTERVAL,
- balanced_irq_interval - BALANCED_IRQ_LESS_DELTA);
+ balanced_irq_interval - BALANCED_IRQ_LESS_DELTA);
return;
}
@@ -486,22 +495,22 @@ static void do_irq_balance(void)
/* Is this an active IRQ or balancing disabled ? */
if (!irq_desc[j].action || irq_balancing_disabled(j))
continue;
- if ( package_index == i )
- IRQ_DELTA(package_index,j) = 0;
+ if (package_index == i)
+ IRQ_DELTA(package_index, j) = 0;
/* Determine the total count per processor per IRQ */
value_now = (unsigned long) kstat_cpu(i).irqs[j];
/* Determine the activity per processor per IRQ */
- delta = value_now - LAST_CPU_IRQ(i,j);
+ delta = value_now - LAST_CPU_IRQ(i, j);
/* Update last_cpu_irq[][] for the next time */
- LAST_CPU_IRQ(i,j) = value_now;
+ LAST_CPU_IRQ(i, j) = value_now;
/* Ignore IRQs whose rate is less than the clock */
if (delta < useful_load_threshold)
continue;
/* update the load for the processor or package total */
- IRQ_DELTA(package_index,j) += delta;
+ IRQ_DELTA(package_index, j) += delta;
/* Keep track of the higher numbered sibling as well */
if (i != package_index)
@@ -527,7 +536,8 @@ static void do_irq_balance(void)
max_cpu_irq = ULONG_MAX;
tryanothercpu:
- /* Look for heaviest loaded processor.
+ /*
+ * Look for heaviest loaded processor.
* We may come back to get the next heaviest loaded processor.
* Skip processors with trivial loads.
*/
@@ -536,7 +546,7 @@ tryanothercpu:
for_each_online_cpu(i) {
if (i != CPU_TO_PACKAGEINDEX(i))
continue;
- if (max_cpu_irq <= CPU_IRQ(i))
+ if (max_cpu_irq <= CPU_IRQ(i))
continue;
if (tmp_cpu_irq < CPU_IRQ(i)) {
tmp_cpu_irq = CPU_IRQ(i);
@@ -545,8 +555,9 @@ tryanothercpu:
}
if (tmp_loaded == -1) {
- /* In the case of small number of heavy interrupt sources,
- * loading some of the cpus too much. We use Ingo's original
+ /*
+ * In the case of small number of heavy interrupt sources,
+ * loading some of the cpus too much. We use Ingo's original
* approach to rotate them around.
*/
if (!first_attempt && imbalance >= useful_load_threshold) {
@@ -555,13 +566,14 @@ tryanothercpu:
}
goto not_worth_the_effort;
}
-
+
first_attempt = 0; /* heaviest search */
max_cpu_irq = tmp_cpu_irq; /* load */
max_loaded = tmp_loaded; /* processor */
imbalance = (max_cpu_irq - min_cpu_irq) / 2;
-
- /* if imbalance is less than approx 10% of max load, then
+
+ /*
+ * if imbalance is less than approx 10% of max load, then
* observe diminishing returns action. - quit
*/
if (imbalance < (max_cpu_irq >> 3))
@@ -577,26 +589,25 @@ tryanotherirq:
/* Is this an active IRQ? */
if (!irq_desc[j].action)
continue;
- if (imbalance <= IRQ_DELTA(max_loaded,j))
+ if (imbalance <= IRQ_DELTA(max_loaded, j))
continue;
/* Try to find the IRQ that is closest to the imbalance
* without going over.
*/
- if (move_this_load < IRQ_DELTA(max_loaded,j)) {
- move_this_load = IRQ_DELTA(max_loaded,j);
+ if (move_this_load < IRQ_DELTA(max_loaded, j)) {
+ move_this_load = IRQ_DELTA(max_loaded, j);
selected_irq = j;
}
}
- if (selected_irq == -1) {
+ if (selected_irq == -1)
goto tryanothercpu;
- }
imbalance = move_this_load;
-
+
/* For physical_balance case, we accumulated both load
* values in the one of the siblings cpu_irq[],
* to use the same code for physical and logical processors
- * as much as possible.
+ * as much as possible.
*
* NOTE: the cpu_irq[] array holds the sum of the load for
* sibling A and sibling B in the slot for the lowest numbered
@@ -625,11 +636,11 @@ tryanotherirq:
/* mark for change destination */
set_pending_irq(selected_irq, cpumask_of_cpu(min_loaded));
- /* Since we made a change, come back sooner to
+ /* Since we made a change, come back sooner to
* check for more variation.
*/
balanced_irq_interval = max((long)MIN_BALANCED_IRQ_INTERVAL,
- balanced_irq_interval - BALANCED_IRQ_LESS_DELTA);
+ balanced_irq_interval - BALANCED_IRQ_LESS_DELTA);
return;
}
goto tryanotherirq;
@@ -640,7 +651,7 @@ not_worth_the_effort:
* upward
*/
balanced_irq_interval = min((long)MAX_BALANCED_IRQ_INTERVAL,
- balanced_irq_interval + BALANCED_IRQ_MORE_DELTA);
+ balanced_irq_interval + BALANCED_IRQ_MORE_DELTA);
return;
}
@@ -679,13 +690,13 @@ static int __init balanced_irq_init(void)
cpumask_t tmp;
cpus_shift_right(tmp, cpu_online_map, 2);
- c = &boot_cpu_data;
+ c = &boot_cpu_data;
/* When not overwritten by the command line ask subarchitecture. */
if (irqbalance_disabled == IRQBALANCE_CHECK_ARCH)
irqbalance_disabled = NO_BALANCE_IRQ;
if (irqbalance_disabled)
return 0;
-
+
/* disable irqbalance completely if there is only one processor online */
if (num_online_cpus() < 2) {
irqbalance_disabled = 1;
@@ -705,10 +716,10 @@ static int __init balanced_irq_init(void)
printk(KERN_ERR "balanced_irq_init: out of memory");
goto failed;
}
- memset(irq_cpu_data[i].irq_delta,0,sizeof(unsigned long) * NR_IRQS);
- memset(irq_cpu_data[i].last_irq,0,sizeof(unsigned long) * NR_IRQS);
+ memset(irq_cpu_data[i].irq_delta, 0, sizeof(unsigned long) * NR_IRQS);
+ memset(irq_cpu_data[i].last_irq, 0, sizeof(unsigned long) * NR_IRQS);
}
-
+
printk(KERN_INFO "Starting balanced_irq\n");
if (!IS_ERR(kthread_run(balanced_irq, NULL, "kirqd")))
return 0;
@@ -801,10 +812,10 @@ static int find_irq_entry(int apic, int pin, int type)
int i;
for (i = 0; i < mp_irq_entries; i++)
- if (mp_irqs[i].mpc_irqtype == type &&
- (mp_irqs[i].mpc_dstapic == mp_ioapics[apic].mpc_apicid ||
- mp_irqs[i].mpc_dstapic == MP_APIC_ALL) &&
- mp_irqs[i].mpc_dstirq == pin)
+ if (mp_irqs[i].mp_irqtype == type &&
+ (mp_irqs[i].mp_dstapic == mp_ioapics[apic].mp_apicid ||
+ mp_irqs[i].mp_dstapic == MP_APIC_ALL) &&
+ mp_irqs[i].mp_dstirq == pin)
return i;
return -1;
@@ -818,13 +829,13 @@ static int __init find_isa_irq_pin(int irq, int type)
int i;
for (i = 0; i < mp_irq_entries; i++) {
- int lbus = mp_irqs[i].mpc_srcbus;
+ int lbus = mp_irqs[i].mp_srcbus;
if (test_bit(lbus, mp_bus_not_pci) &&
- (mp_irqs[i].mpc_irqtype == type) &&
- (mp_irqs[i].mpc_srcbusirq == irq))
+ (mp_irqs[i].mp_irqtype == type) &&
+ (mp_irqs[i].mp_srcbusirq == irq))
- return mp_irqs[i].mpc_dstirq;
+ return mp_irqs[i].mp_dstirq;
}
return -1;
}
@@ -834,17 +845,17 @@ static int __init find_isa_irq_apic(int irq, int type)
int i;
for (i = 0; i < mp_irq_entries; i++) {
- int lbus = mp_irqs[i].mpc_srcbus;
+ int lbus = mp_irqs[i].mp_srcbus;
if (test_bit(lbus, mp_bus_not_pci) &&
- (mp_irqs[i].mpc_irqtype == type) &&
- (mp_irqs[i].mpc_srcbusirq == irq))
+ (mp_irqs[i].mp_irqtype == type) &&
+ (mp_irqs[i].mp_srcbusirq == irq))
break;
}
if (i < mp_irq_entries) {
int apic;
- for(apic = 0; apic < nr_ioapics; apic++) {
- if (mp_ioapics[apic].mpc_apicid == mp_irqs[i].mpc_dstapic)
+ for (apic = 0; apic < nr_ioapics; apic++) {
+ if (mp_ioapics[apic].mp_apicid == mp_irqs[i].mp_dstapic)
return apic;
}
}
@@ -864,28 +875,28 @@ int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pin)
apic_printk(APIC_DEBUG, "querying PCI -> IRQ mapping bus:%d, "
"slot:%d, pin:%d.\n", bus, slot, pin);
- if (mp_bus_id_to_pci_bus[bus] == -1) {
+ if (test_bit(bus, mp_bus_not_pci)) {
printk(KERN_WARNING "PCI BIOS passed nonexistent PCI bus %d!\n", bus);
return -1;
}
for (i = 0; i < mp_irq_entries; i++) {
- int lbus = mp_irqs[i].mpc_srcbus;
+ int lbus = mp_irqs[i].mp_srcbus;
for (apic = 0; apic < nr_ioapics; apic++)
- if (mp_ioapics[apic].mpc_apicid == mp_irqs[i].mpc_dstapic ||
- mp_irqs[i].mpc_dstapic == MP_APIC_ALL)
+ if (mp_ioapics[apic].mp_apicid == mp_irqs[i].mp_dstapic ||
+ mp_irqs[i].mp_dstapic == MP_APIC_ALL)
break;
if (!test_bit(lbus, mp_bus_not_pci) &&
- !mp_irqs[i].mpc_irqtype &&
+ !mp_irqs[i].mp_irqtype &&
(bus == lbus) &&
- (slot == ((mp_irqs[i].mpc_srcbusirq >> 2) & 0x1f))) {
- int irq = pin_2_irq(i,apic,mp_irqs[i].mpc_dstirq);
+ (slot == ((mp_irqs[i].mp_srcbusirq >> 2) & 0x1f))) {
+ int irq = pin_2_irq(i, apic, mp_irqs[i].mp_dstirq);
if (!(apic || IO_APIC_IRQ(irq)))
continue;
- if (pin == (mp_irqs[i].mpc_srcbusirq & 3))
+ if (pin == (mp_irqs[i].mp_srcbusirq & 3))
return irq;
/*
* Use the first all-but-pin matching entry as a
@@ -900,7 +911,7 @@ int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pin)
EXPORT_SYMBOL(IO_APIC_get_PCI_irq_vector);
/*
- * This function currently is only a helper for the i386 smp boot process where
+ * This function currently is only a helper for the i386 smp boot process where
* we need to reprogram the ioredtbls to cater for the cpus which have come online
* so mask in all cases should simply be TARGET_CPUS
*/
@@ -952,7 +963,7 @@ static int EISA_ELCR(unsigned int irq)
* EISA conforming in the MP table, that means its trigger type must
* be read in from the ELCR */
-#define default_EISA_trigger(idx) (EISA_ELCR(mp_irqs[idx].mpc_srcbusirq))
+#define default_EISA_trigger(idx) (EISA_ELCR(mp_irqs[idx].mp_srcbusirq))
#define default_EISA_polarity(idx) default_ISA_polarity(idx)
/* PCI interrupts are always polarity one level triggered,
@@ -969,118 +980,115 @@ static int EISA_ELCR(unsigned int irq)
static int MPBIOS_polarity(int idx)
{
- int bus = mp_irqs[idx].mpc_srcbus;
+ int bus = mp_irqs[idx].mp_srcbus;
int polarity;
/*
* Determine IRQ line polarity (high active or low active):
*/
- switch (mp_irqs[idx].mpc_irqflag & 3)
+ switch (mp_irqs[idx].mp_irqflag & 3) {
+ case 0: /* conforms, ie. bus-type dependent polarity */
{
- case 0: /* conforms, ie. bus-type dependent polarity */
- {
- polarity = test_bit(bus, mp_bus_not_pci)?
- default_ISA_polarity(idx):
- default_PCI_polarity(idx);
- break;
- }
- case 1: /* high active */
- {
- polarity = 0;
- break;
- }
- case 2: /* reserved */
- {
- printk(KERN_WARNING "broken BIOS!!\n");
- polarity = 1;
- break;
- }
- case 3: /* low active */
- {
- polarity = 1;
- break;
- }
- default: /* invalid */
- {
- printk(KERN_WARNING "broken BIOS!!\n");
- polarity = 1;
- break;
- }
+ polarity = test_bit(bus, mp_bus_not_pci)?
+ default_ISA_polarity(idx):
+ default_PCI_polarity(idx);
+ break;
+ }
+ case 1: /* high active */
+ {
+ polarity = 0;
+ break;
+ }
+ case 2: /* reserved */
+ {
+ printk(KERN_WARNING "broken BIOS!!\n");
+ polarity = 1;
+ break;
+ }
+ case 3: /* low active */
+ {
+ polarity = 1;
+ break;
+ }
+ default: /* invalid */
+ {
+ printk(KERN_WARNING "broken BIOS!!\n");
+ polarity = 1;
+ break;
+ }
}
return polarity;
}
static int MPBIOS_trigger(int idx)
{
- int bus = mp_irqs[idx].mpc_srcbus;
+ int bus = mp_irqs[idx].mp_srcbus;
int trigger;
/*
* Determine IRQ trigger mode (edge or level sensitive):
*/
- switch ((mp_irqs[idx].mpc_irqflag>>2) & 3)
+ switch ((mp_irqs[idx].mp_irqflag>>2) & 3) {
+ case 0: /* conforms, ie. bus-type dependent */
{
- case 0: /* conforms, ie. bus-type dependent */
- {
- trigger = test_bit(bus, mp_bus_not_pci)?
- default_ISA_trigger(idx):
- default_PCI_trigger(idx);
+ trigger = test_bit(bus, mp_bus_not_pci)?
+ default_ISA_trigger(idx):
+ default_PCI_trigger(idx);
#if defined(CONFIG_EISA) || defined(CONFIG_MCA)
- switch (mp_bus_id_to_type[bus])
- {
- case MP_BUS_ISA: /* ISA pin */
- {
- /* set before the switch */
- break;
- }
- case MP_BUS_EISA: /* EISA pin */
- {
- trigger = default_EISA_trigger(idx);
- break;
- }
- case MP_BUS_PCI: /* PCI pin */
- {
- /* set before the switch */
- break;
- }
- case MP_BUS_MCA: /* MCA pin */
- {
- trigger = default_MCA_trigger(idx);
- break;
- }
- default:
- {
- printk(KERN_WARNING "broken BIOS!!\n");
- trigger = 1;
- break;
- }
- }
-#endif
+ switch (mp_bus_id_to_type[bus]) {
+ case MP_BUS_ISA: /* ISA pin */
+ {
+ /* set before the switch */
break;
}
- case 1: /* edge */
+ case MP_BUS_EISA: /* EISA pin */
{
- trigger = 0;
+ trigger = default_EISA_trigger(idx);
break;
}
- case 2: /* reserved */
+ case MP_BUS_PCI: /* PCI pin */
{
- printk(KERN_WARNING "broken BIOS!!\n");
- trigger = 1;
+ /* set before the switch */
break;
}
- case 3: /* level */
+ case MP_BUS_MCA: /* MCA pin */
{
- trigger = 1;
+ trigger = default_MCA_trigger(idx);
break;
}
- default: /* invalid */
+ default:
{
printk(KERN_WARNING "broken BIOS!!\n");
- trigger = 0;
+ trigger = 1;
break;
}
}
+#endif
+ break;
+ }
+ case 1: /* edge */
+ {
+ trigger = 0;
+ break;
+ }
+ case 2: /* reserved */
+ {
+ printk(KERN_WARNING "broken BIOS!!\n");
+ trigger = 1;
+ break;
+ }
+ case 3: /* level */
+ {
+ trigger = 1;
+ break;
+ }
+ default: /* invalid */
+ {
+ printk(KERN_WARNING "broken BIOS!!\n");
+ trigger = 0;
+ break;
+ }
+ }
return trigger;
}
@@ -1097,16 +1105,16 @@ static inline int irq_trigger(int idx)
static int pin_2_irq(int idx, int apic, int pin)
{
int irq, i;
- int bus = mp_irqs[idx].mpc_srcbus;
+ int bus = mp_irqs[idx].mp_srcbus;
/*
* Debugging check, we are in big trouble if this message pops up!
*/
- if (mp_irqs[idx].mpc_dstirq != pin)
+ if (mp_irqs[idx].mp_dstirq != pin)
printk(KERN_ERR "broken BIOS or MPTABLE parser, ayiee!!\n");
if (test_bit(bus, mp_bus_not_pci))
- irq = mp_irqs[idx].mpc_srcbusirq;
+ irq = mp_irqs[idx].mp_srcbusirq;
else {
/*
* PCI IRQs are mapped in order
@@ -1148,8 +1156,8 @@ static inline int IO_APIC_irq_trigger(int irq)
for (apic = 0; apic < nr_ioapics; apic++) {
for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) {
- idx = find_irq_entry(apic,pin,mp_INT);
- if ((idx != -1) && (irq == pin_2_irq(idx,apic,pin)))
+ idx = find_irq_entry(apic, pin, mp_INT);
+ if ((idx != -1) && (irq == pin_2_irq(idx, apic, pin)))
return irq_trigger(idx);
}
}
@@ -1164,7 +1172,7 @@ static u8 irq_vector[NR_IRQ_VECTORS] __read_mostly = { FIRST_DEVICE_VECTOR , 0 }
static int __assign_irq_vector(int irq)
{
- static int current_vector = FIRST_DEVICE_VECTOR, current_offset = 0;
+ static int current_vector = FIRST_DEVICE_VECTOR, current_offset;
int vector, offset;
BUG_ON((unsigned)irq >= NR_IRQ_VECTORS);
@@ -1176,7 +1184,7 @@ static int __assign_irq_vector(int irq)
offset = current_offset;
next:
vector += 8;
- if (vector >= FIRST_SYSTEM_VECTOR) {
+ if (vector >= first_system_vector) {
offset = (offset + 1) % 8;
vector = FIRST_DEVICE_VECTOR + offset;
}
@@ -1237,25 +1245,25 @@ static void __init setup_IO_APIC_irqs(void)
/*
* add it to the IO-APIC irq-routing table:
*/
- memset(&entry,0,sizeof(entry));
+ memset(&entry, 0, sizeof(entry));
entry.delivery_mode = INT_DELIVERY_MODE;
entry.dest_mode = INT_DEST_MODE;
entry.mask = 0; /* enable IRQ */
- entry.dest.logical.logical_dest =
+ entry.dest.logical.logical_dest =
cpu_mask_to_apicid(TARGET_CPUS);
- idx = find_irq_entry(apic,pin,mp_INT);
+ idx = find_irq_entry(apic, pin, mp_INT);
if (idx == -1) {
if (first_notcon) {
apic_printk(APIC_VERBOSE, KERN_DEBUG
" IO-APIC (apicid-pin) %d-%d",
- mp_ioapics[apic].mpc_apicid,
+ mp_ioapics[apic].mp_apicid,
pin);
first_notcon = 0;
} else
apic_printk(APIC_VERBOSE, ", %d-%d",
- mp_ioapics[apic].mpc_apicid, pin);
+ mp_ioapics[apic].mp_apicid, pin);
continue;
}
@@ -1289,7 +1297,7 @@ static void __init setup_IO_APIC_irqs(void)
vector = assign_irq_vector(irq);
entry.vector = vector;
ioapic_register_intr(irq, vector, IOAPIC_AUTO);
-
+
if (!apic && (irq < 16))
disable_8259A_irq(irq);
}
@@ -1302,25 +1310,21 @@ static void __init setup_IO_APIC_irqs(void)
}
/*
- * Set up the 8259A-master output pin:
+ * Set up the timer pin, possibly with the 8259A-master behind.
*/
-static void __init setup_ExtINT_IRQ0_pin(unsigned int apic, unsigned int pin, int vector)
+static void __init setup_timer_IRQ0_pin(unsigned int apic, unsigned int pin,
+ int vector)
{
struct IO_APIC_route_entry entry;
- memset(&entry,0,sizeof(entry));
-
- disable_8259A_irq(0);
-
- /* mask LVT0 */
- apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT);
+ memset(&entry, 0, sizeof(entry));
/*
* We use logical delivery to get the timer IRQ
* to the first CPU.
*/
entry.dest_mode = INT_DEST_MODE;
- entry.mask = 0; /* unmask IRQ now */
+ entry.mask = 1; /* mask IRQ now */
entry.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS);
entry.delivery_mode = INT_DELIVERY_MODE;
entry.polarity = 0;
@@ -1329,17 +1333,14 @@ static void __init setup_ExtINT_IRQ0_pin(unsigned int apic, unsigned int pin, in
/*
* The timer IRQ doesn't have to know that behind the
- * scene we have a 8259A-master in AEOI mode ...
+ * scene we may have a 8259A-master in AEOI mode ...
*/
- irq_desc[0].chip = &ioapic_chip;
- set_irq_handler(0, handle_edge_irq);
+ ioapic_register_intr(0, vector, IOAPIC_EDGE);
/*
* Add it to the IO-APIC irq-routing table:
*/
ioapic_write_entry(apic, pin, entry);
-
- enable_8259A_irq(0);
}
void __init print_IO_APIC(void)
@@ -1354,10 +1355,10 @@ void __init print_IO_APIC(void)
if (apic_verbosity == APIC_QUIET)
return;
- printk(KERN_DEBUG "number of MP IRQ sources: %d.\n", mp_irq_entries);
+ printk(KERN_DEBUG "number of MP IRQ sources: %d.\n", mp_irq_entries);
for (i = 0; i < nr_ioapics; i++)
printk(KERN_DEBUG "number of IO-APIC #%d registers: %d.\n",
- mp_ioapics[i].mpc_apicid, nr_ioapic_registers[i]);
+ mp_ioapics[i].mp_apicid, nr_ioapic_registers[i]);
/*
* We are a bit conservative about what we expect. We have to
@@ -1376,7 +1377,7 @@ void __init print_IO_APIC(void)
reg_03.raw = io_apic_read(apic, 3);
spin_unlock_irqrestore(&ioapic_lock, flags);
- printk(KERN_DEBUG "IO APIC #%d......\n", mp_ioapics[apic].mpc_apicid);
+ printk(KERN_DEBUG "IO APIC #%d......\n", mp_ioapics[apic].mp_apicid);
printk(KERN_DEBUG ".... register #00: %08X\n", reg_00.raw);
printk(KERN_DEBUG "....... : physical APIC id: %02X\n", reg_00.bits.ID);
printk(KERN_DEBUG "....... : Delivery Type: %X\n", reg_00.bits.delivery_type);
@@ -1459,7 +1460,7 @@ void __init print_IO_APIC(void)
#if 0
-static void print_APIC_bitfield (int base)
+static void print_APIC_bitfield(int base)
{
unsigned int v;
int i, j;
@@ -1480,7 +1481,7 @@ static void print_APIC_bitfield (int base)
}
}
-void /*__init*/ print_local_APIC(void * dummy)
+void /*__init*/ print_local_APIC(void *dummy)
{
unsigned int v, ver, maxlvt;
@@ -1489,6 +1490,7 @@ void /*__init*/ print_local_APIC(void * dummy)
printk("\n" KERN_DEBUG "printing local APIC contents on CPU#%d/%d:\n",
smp_processor_id(), hard_smp_processor_id());
+ v = apic_read(APIC_ID);
printk(KERN_INFO "... APIC ID: %08x (%01x)\n", v,
GET_APIC_ID(read_apic_id()));
v = apic_read(APIC_LVR);
@@ -1563,7 +1565,7 @@ void /*__init*/ print_local_APIC(void * dummy)
printk("\n");
}
-void print_all_local_APICs (void)
+void print_all_local_APICs(void)
{
on_each_cpu(print_local_APIC, NULL, 1, 1);
}
@@ -1586,11 +1588,11 @@ void /*__init*/ print_PIC(void)
v = inb(0xa0) << 8 | inb(0x20);
printk(KERN_DEBUG "... PIC IRR: %04x\n", v);
- outb(0x0b,0xa0);
- outb(0x0b,0x20);
+ outb(0x0b, 0xa0);
+ outb(0x0b, 0x20);
v = inb(0xa0) << 8 | inb(0x20);
- outb(0x0a,0xa0);
- outb(0x0a,0x20);
+ outb(0x0a, 0xa0);
+ outb(0x0a, 0x20);
spin_unlock_irqrestore(&i8259A_lock, flags);
@@ -1626,7 +1628,7 @@ static void __init enable_IO_APIC(void)
spin_unlock_irqrestore(&ioapic_lock, flags);
nr_ioapic_registers[apic] = reg_01.bits.entries+1;
}
- for(apic = 0; apic < nr_ioapics; apic++) {
+ for (apic = 0; apic < nr_ioapics; apic++) {
int pin;
/* See if any of the pins is in ExtINT mode */
for (pin = 0; pin < nr_ioapic_registers[apic]; pin++) {
@@ -1716,7 +1718,6 @@ void disable_IO_APIC(void)
* by Matt Domsch <Matt_Domsch@dell.com> Tue Dec 21 12:25:05 CST 1999
*/
-#ifndef CONFIG_X86_NUMAQ
static void __init setup_ioapic_ids_from_mpc(void)
{
union IO_APIC_reg_00 reg_00;
@@ -1726,6 +1727,11 @@ static void __init setup_ioapic_ids_from_mpc(void)
unsigned char old_id;
unsigned long flags;
+#ifdef CONFIG_X86_NUMAQ
+ if (found_numaq)
+ return;
+#endif
+
/*
* Don't check I/O APIC IDs for xAPIC systems. They have
* no meaning without the serial APIC bus.
@@ -1748,15 +1754,15 @@ static void __init setup_ioapic_ids_from_mpc(void)
spin_lock_irqsave(&ioapic_lock, flags);
reg_00.raw = io_apic_read(apic, 0);
spin_unlock_irqrestore(&ioapic_lock, flags);
-
- old_id = mp_ioapics[apic].mpc_apicid;
- if (mp_ioapics[apic].mpc_apicid >= get_physical_broadcast()) {
+ old_id = mp_ioapics[apic].mp_apicid;
+
+ if (mp_ioapics[apic].mp_apicid >= get_physical_broadcast()) {
printk(KERN_ERR "BIOS bug, IO-APIC#%d ID is %d in the MPC table!...\n",
- apic, mp_ioapics[apic].mpc_apicid);
+ apic, mp_ioapics[apic].mp_apicid);
printk(KERN_ERR "... fixing up to %d. (tell your hw vendor)\n",
reg_00.bits.ID);
- mp_ioapics[apic].mpc_apicid = reg_00.bits.ID;
+ mp_ioapics[apic].mp_apicid = reg_00.bits.ID;
}
/*
@@ -1765,9 +1771,9 @@ static void __init setup_ioapic_ids_from_mpc(void)
* 'stuck on smp_invalidate_needed IPI wait' messages.
*/
if (check_apicid_used(phys_id_present_map,
- mp_ioapics[apic].mpc_apicid)) {
+ mp_ioapics[apic].mp_apicid)) {
printk(KERN_ERR "BIOS bug, IO-APIC#%d ID %d is already used!...\n",
- apic, mp_ioapics[apic].mpc_apicid);
+ apic, mp_ioapics[apic].mp_apicid);
for (i = 0; i < get_physical_broadcast(); i++)
if (!physid_isset(i, phys_id_present_map))
break;
@@ -1776,13 +1782,13 @@ static void __init setup_ioapic_ids_from_mpc(void)
printk(KERN_ERR "... fixing up to %d. (tell your hw vendor)\n",
i);
physid_set(i, phys_id_present_map);
- mp_ioapics[apic].mpc_apicid = i;
+ mp_ioapics[apic].mp_apicid = i;
} else {
physid_mask_t tmp;
- tmp = apicid_to_cpu_present(mp_ioapics[apic].mpc_apicid);
+ tmp = apicid_to_cpu_present(mp_ioapics[apic].mp_apicid);
apic_printk(APIC_VERBOSE, "Setting %d in the "
"phys_id_present_map\n",
- mp_ioapics[apic].mpc_apicid);
+ mp_ioapics[apic].mp_apicid);
physids_or(phys_id_present_map, phys_id_present_map, tmp);
}
@@ -1791,21 +1797,21 @@ static void __init setup_ioapic_ids_from_mpc(void)
* We need to adjust the IRQ routing table
* if the ID changed.
*/
- if (old_id != mp_ioapics[apic].mpc_apicid)
+ if (old_id != mp_ioapics[apic].mp_apicid)
for (i = 0; i < mp_irq_entries; i++)
- if (mp_irqs[i].mpc_dstapic == old_id)
- mp_irqs[i].mpc_dstapic
- = mp_ioapics[apic].mpc_apicid;
+ if (mp_irqs[i].mp_dstapic == old_id)
+ mp_irqs[i].mp_dstapic
+ = mp_ioapics[apic].mp_apicid;
/*
* Read the right value from the MPC table and
* write it into the ID register.
- */
+ */
apic_printk(APIC_VERBOSE, KERN_INFO
"...changing IO-APIC physical APIC ID to %d ...",
- mp_ioapics[apic].mpc_apicid);
+ mp_ioapics[apic].mp_apicid);
- reg_00.bits.ID = mp_ioapics[apic].mpc_apicid;
+ reg_00.bits.ID = mp_ioapics[apic].mp_apicid;
spin_lock_irqsave(&ioapic_lock, flags);
io_apic_write(apic, 0, reg_00.raw);
spin_unlock_irqrestore(&ioapic_lock, flags);
@@ -1816,15 +1822,12 @@ static void __init setup_ioapic_ids_from_mpc(void)
spin_lock_irqsave(&ioapic_lock, flags);
reg_00.raw = io_apic_read(apic, 0);
spin_unlock_irqrestore(&ioapic_lock, flags);
- if (reg_00.bits.ID != mp_ioapics[apic].mpc_apicid)
+ if (reg_00.bits.ID != mp_ioapics[apic].mp_apicid)
printk("could not set ID!\n");
else
apic_printk(APIC_VERBOSE, " ok.\n");
}
}
-#else
-static void __init setup_ioapic_ids_from_mpc(void) { }
-#endif
int no_timer_check __initdata;
@@ -2020,7 +2023,7 @@ static void ack_apic(unsigned int irq)
ack_APIC_irq();
}
-static void mask_lapic_irq (unsigned int irq)
+static void mask_lapic_irq(unsigned int irq)
{
unsigned long v;
@@ -2028,7 +2031,7 @@ static void mask_lapic_irq (unsigned int irq)
apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED);
}
-static void unmask_lapic_irq (unsigned int irq)
+static void unmask_lapic_irq(unsigned int irq)
{
unsigned long v;
@@ -2037,7 +2040,7 @@ static void unmask_lapic_irq (unsigned int irq)
}
static struct irq_chip lapic_chip __read_mostly = {
- .name = "local-APIC-edge",
+ .name = "local-APIC",
.mask = mask_lapic_irq,
.unmask = unmask_lapic_irq,
.eoi = ack_apic,
@@ -2046,14 +2049,14 @@ static struct irq_chip lapic_chip __read_mostly = {
static void __init setup_nmi(void)
{
/*
- * Dirty trick to enable the NMI watchdog ...
+ * Dirty trick to enable the NMI watchdog ...
* We put the 8259A master into AEOI mode and
* unmask on all local APICs LVT0 as NMI.
*
* The idea to use the 8259A in AEOI mode ('8259A Virtual Wire')
* is from Maciej W. Rozycki - so we do not have to EOI from
* the NMI handler or the timer interrupt.
- */
+ */
apic_printk(APIC_VERBOSE, KERN_INFO "activating NMI Watchdog ...");
enable_NMI_through_LVT0();
@@ -2129,6 +2132,7 @@ static inline void __init unlock_ExtINT_logic(void)
static inline void __init check_timer(void)
{
int apic1, pin1, apic2, pin2;
+ int no_pin1 = 0;
int vector;
unsigned int ver;
unsigned long flags;
@@ -2146,21 +2150,17 @@ static inline void __init check_timer(void)
set_intr_gate(vector, interrupt[0]);
/*
- * Subtle, code in do_timer_interrupt() expects an AEOI
- * mode for the 8259A whenever interrupts are routed
- * through I/O APICs. Also IRQ0 has to be enabled in
- * the 8259A which implies the virtual wire has to be
- * disabled in the local APIC. Finally timer interrupts
- * need to be acknowledged manually in the 8259A for
- * timer_interrupt() and for the i82489DX when using
- * the NMI watchdog.
+ * As IRQ0 is to be enabled in the 8259A, the virtual
+ * wire has to be disabled in the local APIC. Also
+ * timer interrupts need to be acknowledged manually in
+ * the 8259A for the i82489DX when using the NMI
+ * watchdog as that APIC treats NMIs as level-triggered.
+ * The AEOI mode will finish them in the 8259A
+ * automatically.
*/
apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT);
init_8259A(1);
- timer_ack = !cpu_has_tsc;
- timer_ack |= (nmi_watchdog == NMI_IO_APIC && !APIC_INTEGRATED(ver));
- if (timer_over_8254 > 0)
- enable_8259A_irq(0);
+ timer_ack = (nmi_watchdog == NMI_IO_APIC && !APIC_INTEGRATED(ver));
pin1 = find_isa_irq_pin(0, mp_INT);
apic1 = find_isa_irq_apic(0, mp_INT);
@@ -2170,14 +2170,33 @@ static inline void __init check_timer(void)
printk(KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n",
vector, apic1, pin1, apic2, pin2);
+ /*
+ * Some BIOS writers are clueless and report the ExtINTA
+ * I/O APIC input from the cascaded 8259A as the timer
+ * interrupt input. So just in case, if only one pin
+ * was found above, try it both directly and through the
+ * 8259A.
+ */
+ if (pin1 == -1) {
+ pin1 = pin2;
+ apic1 = apic2;
+ no_pin1 = 1;
+ } else if (pin2 == -1) {
+ pin2 = pin1;
+ apic2 = apic1;
+ }
+
if (pin1 != -1) {
/*
* Ok, does IRQ0 through the IOAPIC work?
*/
+ if (no_pin1) {
+ add_pin_to_irq(0, apic1, pin1);
+ setup_timer_IRQ0_pin(apic1, pin1, vector);
+ }
unmask_IO_APIC_irq(0);
if (timer_irq_works()) {
if (nmi_watchdog == NMI_IO_APIC) {
- disable_8259A_irq(0);
setup_nmi();
enable_8259A_irq(0);
}
@@ -2186,43 +2205,46 @@ static inline void __init check_timer(void)
goto out;
}
clear_IO_APIC_pin(apic1, pin1);
- printk(KERN_ERR "..MP-BIOS bug: 8254 timer not connected to "
- "IO-APIC\n");
- }
+ if (!no_pin1)
+ printk(KERN_ERR "..MP-BIOS bug: "
+ "8254 timer not connected to IO-APIC\n");
- printk(KERN_INFO "...trying to set up timer (IRQ0) through the 8259A ... ");
- if (pin2 != -1) {
+ printk(KERN_INFO "...trying to set up timer (IRQ0) "
+ "through the 8259A ... ");
printk("\n..... (found pin %d) ...", pin2);
/*
* legacy devices should be connected to IO APIC #0
*/
- setup_ExtINT_IRQ0_pin(apic2, pin2, vector);
+ replace_pin_at_irq(0, apic1, pin1, apic2, pin2);
+ setup_timer_IRQ0_pin(apic2, pin2, vector);
+ unmask_IO_APIC_irq(0);
+ enable_8259A_irq(0);
if (timer_irq_works()) {
printk("works.\n");
- if (pin1 != -1)
- replace_pin_at_irq(0, apic1, pin1, apic2, pin2);
- else
- add_pin_to_irq(0, apic2, pin2);
+ timer_through_8259 = 1;
if (nmi_watchdog == NMI_IO_APIC) {
+ disable_8259A_irq(0);
setup_nmi();
+ enable_8259A_irq(0);
}
goto out;
}
/*
* Cleanup, just in case ...
*/
+ disable_8259A_irq(0);
clear_IO_APIC_pin(apic2, pin2);
+ printk(" failed.\n");
}
- printk(" failed.\n");
if (nmi_watchdog == NMI_IO_APIC) {
printk(KERN_WARNING "timer doesn't work through the IO-APIC - disabling NMI Watchdog!\n");
- nmi_watchdog = 0;
+ nmi_watchdog = NMI_NONE;
}
+ timer_ack = 0;
printk(KERN_INFO "...trying to set up timer as Virtual Wire IRQ...");
- disable_8259A_irq(0);
set_irq_chip_and_handler_name(0, &lapic_chip, handle_fasteoi_irq,
"fasteoi");
apic_write_around(APIC_LVT0, APIC_DM_FIXED | vector); /* Fixed mode */
@@ -2232,12 +2254,12 @@ static inline void __init check_timer(void)
printk(" works.\n");
goto out;
}
+ disable_8259A_irq(0);
apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | vector);
printk(" failed.\n");
printk(KERN_INFO "...trying to set up timer as ExtINT IRQ...");
- timer_ack = 0;
init_8259A(0);
make_8259A_irq(0);
apic_write_around(APIC_LVT0, APIC_DM_EXTINT);
@@ -2269,7 +2291,7 @@ void __init setup_IO_APIC(void)
int i;
/* Reserve all the system vectors. */
- for (i = FIRST_SYSTEM_VECTOR; i < NR_VECTORS; i++)
+ for (i = first_system_vector; i < NR_VECTORS; i++)
set_bit(i, used_vectors);
enable_IO_APIC();
@@ -2294,28 +2316,14 @@ void __init setup_IO_APIC(void)
print_IO_APIC();
}
-static int __init setup_disable_8254_timer(char *s)
-{
- timer_over_8254 = -1;
- return 1;
-}
-static int __init setup_enable_8254_timer(char *s)
-{
- timer_over_8254 = 2;
- return 1;
-}
-
-__setup("disable_8254_timer", setup_disable_8254_timer);
-__setup("enable_8254_timer", setup_enable_8254_timer);
-
/*
* Called after all the initialization is done. If we didnt find any
* APIC bugs then we can allow the modify fast path
*/
-
+
static int __init io_apic_bug_finalize(void)
{
- if(sis_apic_bug == -1)
+ if (sis_apic_bug == -1)
sis_apic_bug = 0;
return 0;
}
@@ -2326,17 +2334,17 @@ struct sysfs_ioapic_data {
struct sys_device dev;
struct IO_APIC_route_entry entry[0];
};
-static struct sysfs_ioapic_data * mp_ioapic_data[MAX_IO_APICS];
+static struct sysfs_ioapic_data *mp_ioapic_data[MAX_IO_APICS];
static int ioapic_suspend(struct sys_device *dev, pm_message_t state)
{
struct IO_APIC_route_entry *entry;
struct sysfs_ioapic_data *data;
int i;
-
+
data = container_of(dev, struct sysfs_ioapic_data, dev);
entry = data->entry;
- for (i = 0; i < nr_ioapic_registers[dev->id]; i ++)
+ for (i = 0; i < nr_ioapic_registers[dev->id]; i++)
entry[i] = ioapic_read_entry(dev->id, i);
return 0;
@@ -2349,18 +2357,18 @@ static int ioapic_resume(struct sys_device *dev)
unsigned long flags;
union IO_APIC_reg_00 reg_00;
int i;
-
+
data = container_of(dev, struct sysfs_ioapic_data, dev);
entry = data->entry;
spin_lock_irqsave(&ioapic_lock, flags);
reg_00.raw = io_apic_read(dev->id, 0);
- if (reg_00.bits.ID != mp_ioapics[dev->id].mpc_apicid) {
- reg_00.bits.ID = mp_ioapics[dev->id].mpc_apicid;
+ if (reg_00.bits.ID != mp_ioapics[dev->id].mp_apicid) {
+ reg_00.bits.ID = mp_ioapics[dev->id].mp_apicid;
io_apic_write(dev->id, 0, reg_00.raw);
}
spin_unlock_irqrestore(&ioapic_lock, flags);
- for (i = 0; i < nr_ioapic_registers[dev->id]; i ++)
+ for (i = 0; i < nr_ioapic_registers[dev->id]; i++)
ioapic_write_entry(dev->id, i, entry[i]);
return 0;
@@ -2374,15 +2382,15 @@ static struct sysdev_class ioapic_sysdev_class = {
static int __init ioapic_init_sysfs(void)
{
- struct sys_device * dev;
+ struct sys_device *dev;
int i, size, error = 0;
error = sysdev_class_register(&ioapic_sysdev_class);
if (error)
return error;
- for (i = 0; i < nr_ioapics; i++ ) {
- size = sizeof(struct sys_device) + nr_ioapic_registers[i]
+ for (i = 0; i < nr_ioapics; i++) {
+ size = sizeof(struct sys_device) + nr_ioapic_registers[i]
* sizeof(struct IO_APIC_route_entry);
mp_ioapic_data[i] = kmalloc(size, GFP_KERNEL);
if (!mp_ioapic_data[i]) {
@@ -2391,7 +2399,7 @@ static int __init ioapic_init_sysfs(void)
}
memset(mp_ioapic_data[i], 0, size);
dev = &mp_ioapic_data[i]->dev;
- dev->id = i;
+ dev->id = i;
dev->cls = &ioapic_sysdev_class;
error = sysdev_register(dev);
if (error) {
@@ -2466,7 +2474,7 @@ static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_ms
msg->address_lo =
MSI_ADDR_BASE_LO |
((INT_DEST_MODE == 0) ?
- MSI_ADDR_DEST_MODE_PHYSICAL:
+MSI_ADDR_DEST_MODE_PHYSICAL:
MSI_ADDR_DEST_MODE_LOGICAL) |
((INT_DELIVERY_MODE != dest_LowestPrio) ?
MSI_ADDR_REDIRECTION_CPU:
@@ -2477,7 +2485,7 @@ static int msi_compose_msg(struct pci_dev *pdev, unsigned int irq, struct msi_ms
MSI_DATA_TRIGGER_EDGE |
MSI_DATA_LEVEL_ASSERT |
((INT_DELIVERY_MODE != dest_LowestPrio) ?
- MSI_DATA_DELIVERY_FIXED:
+MSI_DATA_DELIVERY_FIXED:
MSI_DATA_DELIVERY_LOWPRI) |
MSI_DATA_VECTOR(vector);
}
@@ -2648,12 +2656,12 @@ int arch_setup_ht_irq(unsigned int irq, struct pci_dev *dev)
#endif /* CONFIG_HT_IRQ */
/* --------------------------------------------------------------------------
- ACPI-based IOAPIC Configuration
+ ACPI-based IOAPIC Configuration
-------------------------------------------------------------------------- */
#ifdef CONFIG_ACPI
-int __init io_apic_get_unique_id (int ioapic, int apic_id)
+int __init io_apic_get_unique_id(int ioapic, int apic_id)
{
union IO_APIC_reg_00 reg_00;
static physid_mask_t apic_id_map = PHYSID_MASK_NONE;
@@ -2662,10 +2670,10 @@ int __init io_apic_get_unique_id (int ioapic, int apic_id)
int i = 0;
/*
- * The P4 platform supports up to 256 APIC IDs on two separate APIC
- * buses (one for LAPICs, one for IOAPICs), where predecessors only
+ * The P4 platform supports up to 256 APIC IDs on two separate APIC
+ * buses (one for LAPICs, one for IOAPICs), where predecessors only
* supports up to 16 on one shared APIC bus.
- *
+ *
* TBD: Expand LAPIC/IOAPIC support on P4-class systems to take full
* advantage of new APIC bus architecture.
*/
@@ -2684,7 +2692,7 @@ int __init io_apic_get_unique_id (int ioapic, int apic_id)
}
/*
- * Every APIC in a system must have a unique ID or we get lots of nice
+ * Every APIC in a system must have a unique ID or we get lots of nice
* 'stuck on smp_invalidate_needed IPI wait' messages.
*/
if (check_apicid_used(apic_id_map, apic_id)) {
@@ -2701,7 +2709,7 @@ int __init io_apic_get_unique_id (int ioapic, int apic_id)
"trying %d\n", ioapic, apic_id, i);
apic_id = i;
- }
+ }
tmp = apicid_to_cpu_present(apic_id);
physids_or(apic_id_map, apic_id_map, tmp);
@@ -2728,7 +2736,7 @@ int __init io_apic_get_unique_id (int ioapic, int apic_id)
}
-int __init io_apic_get_version (int ioapic)
+int __init io_apic_get_version(int ioapic)
{
union IO_APIC_reg_01 reg_01;
unsigned long flags;
@@ -2741,7 +2749,7 @@ int __init io_apic_get_version (int ioapic)
}
-int __init io_apic_get_redir_entries (int ioapic)
+int __init io_apic_get_redir_entries(int ioapic)
{
union IO_APIC_reg_01 reg_01;
unsigned long flags;
@@ -2754,7 +2762,7 @@ int __init io_apic_get_redir_entries (int ioapic)
}
-int io_apic_set_pci_routing (int ioapic, int pin, int irq, int edge_level, int active_high_low)
+int io_apic_set_pci_routing(int ioapic, int pin, int irq, int edge_level, int active_high_low)
{
struct IO_APIC_route_entry entry;
@@ -2770,7 +2778,7 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int edge_level, int a
* corresponding device driver registers for this IRQ.
*/
- memset(&entry,0,sizeof(entry));
+ memset(&entry, 0, sizeof(entry));
entry.delivery_mode = INT_DELIVERY_MODE;
entry.dest_mode = INT_DEST_MODE;
@@ -2789,7 +2797,7 @@ int io_apic_set_pci_routing (int ioapic, int pin, int irq, int edge_level, int a
apic_printk(APIC_DEBUG, KERN_DEBUG "IOAPIC[%d]: Set PCI routing entry "
"(%d-%d -> 0x%x -> IRQ %d Mode:%i Active:%i)\n", ioapic,
- mp_ioapics[ioapic].mpc_apicid, pin, entry.vector, irq,
+ mp_ioapics[ioapic].mp_apicid, pin, entry.vector, irq,
edge_level, active_high_low);
ioapic_register_intr(irq, entry.vector, edge_level);
@@ -2810,8 +2818,8 @@ int acpi_get_override_irq(int bus_irq, int *trigger, int *polarity)
return -1;
for (i = 0; i < mp_irq_entries; i++)
- if (mp_irqs[i].mpc_irqtype == mp_INT &&
- mp_irqs[i].mpc_srcbusirq == bus_irq)
+ if (mp_irqs[i].mp_irqtype == mp_INT &&
+ mp_irqs[i].mp_srcbusirq == bus_irq)
break;
if (i >= mp_irq_entries)
return -1;
diff --git a/arch/x86/kernel/io_apic_64.c b/arch/x86/kernel/io_apic_64.c
index ef1a8dfcc529..84f233fb6fd0 100644
--- a/arch/x86/kernel/io_apic_64.c
+++ b/arch/x86/kernel/io_apic_64.c
@@ -61,7 +61,7 @@ struct irq_cfg {
};
/* irq_cfg is indexed by the sum of all RTEs in all I/O APICs. */
-struct irq_cfg irq_cfg[NR_IRQS] __read_mostly = {
+static struct irq_cfg irq_cfg[NR_IRQS] __read_mostly = {
[0] = { .domain = CPU_MASK_ALL, .vector = IRQ0_VECTOR, },
[1] = { .domain = CPU_MASK_ALL, .vector = IRQ1_VECTOR, },
[2] = { .domain = CPU_MASK_ALL, .vector = IRQ2_VECTOR, },
@@ -82,6 +82,10 @@ struct irq_cfg irq_cfg[NR_IRQS] __read_mostly = {
static int assign_irq_vector(int irq, cpumask_t mask);
+int first_system_vector = 0xfe;
+
+char system_vectors[NR_VECTORS] = { [0 ... NR_VECTORS-1] = SYS_VECTOR_FREE};
+
#define __apicdebuginit __init
int sis_apic_bug; /* not actually supported, dummy for compile */
@@ -90,7 +94,7 @@ static int no_timer_check;
static int disable_timer_pin_1 __initdata;
-int timer_over_8254 __initdata = 1;
+int timer_through_8259 __initdata;
/* Where if anywhere is the i8259 connect in external int mode */
static struct { int pin, apic; } ioapic_i8259 = { -1, -1 };
@@ -104,15 +108,17 @@ DEFINE_SPINLOCK(vector_lock);
int nr_ioapic_registers[MAX_IO_APICS];
/* I/O APIC entries */
-struct mpc_config_ioapic mp_ioapics[MAX_IO_APICS];
+struct mp_config_ioapic mp_ioapics[MAX_IO_APICS];
int nr_ioapics;
/* MP IRQ source entries */
-struct mpc_config_intsrc mp_irqs[MAX_IRQ_SOURCES];
+struct mp_config_intsrc mp_irqs[MAX_IRQ_SOURCES];
/* # of MP IRQ source entries */
int mp_irq_entries;
+DECLARE_BITMAP(mp_bus_not_pci, MAX_MP_BUSSES);
+
/*
* Rough estimation of how many shared IRQs there are, can
* be changed anytime.
@@ -140,7 +146,7 @@ struct io_apic {
static __attribute_const__ struct io_apic __iomem *io_apic_base(int idx)
{
return (void __iomem *) __fix_to_virt(FIX_IO_APIC_BASE_0 + idx)
- + (mp_ioapics[idx].mpc_apicaddr & ~PAGE_MASK);
+ + (mp_ioapics[idx].mp_apicaddr & ~PAGE_MASK);
}
static inline unsigned int io_apic_read(unsigned int apic, unsigned int reg)
@@ -183,7 +189,7 @@ static bool io_apic_level_ack_pending(unsigned int irq)
break;
reg = io_apic_read(entry->apic, 0x10 + pin*2);
/* Is the remote IRR bit set? */
- if ((reg >> 14) & 1) {
+ if (reg & IO_APIC_REDIR_REMOTE_IRR) {
spin_unlock_irqrestore(&ioapic_lock, flags);
return true;
}
@@ -298,7 +304,7 @@ static void __target_IO_APIC_irq(unsigned int irq, unsigned int dest, u8 vector)
break;
io_apic_write(apic, 0x11 + pin*2, dest);
reg = io_apic_read(apic, 0x10 + pin*2);
- reg &= ~0x000000ff;
+ reg &= ~IO_APIC_REDIR_VECTOR_MASK;
reg |= vector;
io_apic_modify(apic, reg);
if (!entry->next)
@@ -366,10 +372,11 @@ static void add_pin_to_irq(unsigned int irq, int apic, int pin)
static void name##_IO_APIC_irq (unsigned int irq) \
__DO_ACTION(R, ACTION, FINAL)
-DO_ACTION( __mask, 0, |= 0x00010000, io_apic_sync(entry->apic) )
- /* mask = 1 */
-DO_ACTION( __unmask, 0, &= 0xfffeffff, )
- /* mask = 0 */
+/* mask = 1 */
+DO_ACTION(__mask, 0, |= IO_APIC_REDIR_MASKED, io_apic_sync(entry->apic))
+
+/* mask = 0 */
+DO_ACTION(__unmask, 0, &= ~IO_APIC_REDIR_MASKED, )
static void mask_IO_APIC_irq (unsigned int irq)
{
@@ -430,20 +437,6 @@ static int __init disable_timer_pin_setup(char *arg)
}
__setup("disable_timer_pin_1", disable_timer_pin_setup);
-static int __init setup_disable_8254_timer(char *s)
-{
- timer_over_8254 = -1;
- return 1;
-}
-static int __init setup_enable_8254_timer(char *s)
-{
- timer_over_8254 = 2;
- return 1;
-}
-
-__setup("disable_8254_timer", setup_disable_8254_timer);
-__setup("enable_8254_timer", setup_enable_8254_timer);
-
/*
* Find the IRQ entry number of a certain pin.
@@ -453,10 +446,10 @@ static int find_irq_entry(int apic, int pin, int type)
int i;
for (i = 0; i < mp_irq_entries; i++)
- if (mp_irqs[i].mpc_irqtype == type &&
- (mp_irqs[i].mpc_dstapic == mp_ioapics[apic].mpc_apicid ||
- mp_irqs[i].mpc_dstapic == MP_APIC_ALL) &&
- mp_irqs[i].mpc_dstirq == pin)
+ if (mp_irqs[i].mp_irqtype == type &&
+ (mp_irqs[i].mp_dstapic == mp_ioapics[apic].mp_apicid ||
+ mp_irqs[i].mp_dstapic == MP_APIC_ALL) &&
+ mp_irqs[i].mp_dstirq == pin)
return i;
return -1;
@@ -470,13 +463,13 @@ static int __init find_isa_irq_pin(int irq, int type)
int i;
for (i = 0; i < mp_irq_entries; i++) {
- int lbus = mp_irqs[i].mpc_srcbus;
+ int lbus = mp_irqs[i].mp_srcbus;
if (test_bit(lbus, mp_bus_not_pci) &&
- (mp_irqs[i].mpc_irqtype == type) &&
- (mp_irqs[i].mpc_srcbusirq == irq))
+ (mp_irqs[i].mp_irqtype == type) &&
+ (mp_irqs[i].mp_srcbusirq == irq))
- return mp_irqs[i].mpc_dstirq;
+ return mp_irqs[i].mp_dstirq;
}
return -1;
}
@@ -486,17 +479,17 @@ static int __init find_isa_irq_apic(int irq, int type)
int i;
for (i = 0; i < mp_irq_entries; i++) {
- int lbus = mp_irqs[i].mpc_srcbus;
+ int lbus = mp_irqs[i].mp_srcbus;
if (test_bit(lbus, mp_bus_not_pci) &&
- (mp_irqs[i].mpc_irqtype == type) &&
- (mp_irqs[i].mpc_srcbusirq == irq))
+ (mp_irqs[i].mp_irqtype == type) &&
+ (mp_irqs[i].mp_srcbusirq == irq))
break;
}
if (i < mp_irq_entries) {
int apic;
for(apic = 0; apic < nr_ioapics; apic++) {
- if (mp_ioapics[apic].mpc_apicid == mp_irqs[i].mpc_dstapic)
+ if (mp_ioapics[apic].mp_apicid == mp_irqs[i].mp_dstapic)
return apic;
}
}
@@ -516,28 +509,28 @@ int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pin)
apic_printk(APIC_DEBUG, "querying PCI -> IRQ mapping bus:%d, slot:%d, pin:%d.\n",
bus, slot, pin);
- if (mp_bus_id_to_pci_bus[bus] == -1) {
+ if (test_bit(bus, mp_bus_not_pci)) {
apic_printk(APIC_VERBOSE, "PCI BIOS passed nonexistent PCI bus %d!\n", bus);
return -1;
}
for (i = 0; i < mp_irq_entries; i++) {
- int lbus = mp_irqs[i].mpc_srcbus;
+ int lbus = mp_irqs[i].mp_srcbus;
for (apic = 0; apic < nr_ioapics; apic++)
- if (mp_ioapics[apic].mpc_apicid == mp_irqs[i].mpc_dstapic ||
- mp_irqs[i].mpc_dstapic == MP_APIC_ALL)
+ if (mp_ioapics[apic].mp_apicid == mp_irqs[i].mp_dstapic ||
+ mp_irqs[i].mp_dstapic == MP_APIC_ALL)
break;
if (!test_bit(lbus, mp_bus_not_pci) &&
- !mp_irqs[i].mpc_irqtype &&
+ !mp_irqs[i].mp_irqtype &&
(bus == lbus) &&
- (slot == ((mp_irqs[i].mpc_srcbusirq >> 2) & 0x1f))) {
- int irq = pin_2_irq(i,apic,mp_irqs[i].mpc_dstirq);
+ (slot == ((mp_irqs[i].mp_srcbusirq >> 2) & 0x1f))) {
+ int irq = pin_2_irq(i,apic,mp_irqs[i].mp_dstirq);
if (!(apic || IO_APIC_IRQ(irq)))
continue;
- if (pin == (mp_irqs[i].mpc_srcbusirq & 3))
+ if (pin == (mp_irqs[i].mp_srcbusirq & 3))
return irq;
/*
* Use the first all-but-pin matching entry as a
@@ -565,13 +558,13 @@ int IO_APIC_get_PCI_irq_vector(int bus, int slot, int pin)
static int MPBIOS_polarity(int idx)
{
- int bus = mp_irqs[idx].mpc_srcbus;
+ int bus = mp_irqs[idx].mp_srcbus;
int polarity;
/*
* Determine IRQ line polarity (high active or low active):
*/
- switch (mp_irqs[idx].mpc_irqflag & 3)
+ switch (mp_irqs[idx].mp_irqflag & 3)
{
case 0: /* conforms, ie. bus-type dependent polarity */
if (test_bit(bus, mp_bus_not_pci))
@@ -607,13 +600,13 @@ static int MPBIOS_polarity(int idx)
static int MPBIOS_trigger(int idx)
{
- int bus = mp_irqs[idx].mpc_srcbus;
+ int bus = mp_irqs[idx].mp_srcbus;
int trigger;
/*
* Determine IRQ trigger mode (edge or level sensitive):
*/
- switch ((mp_irqs[idx].mpc_irqflag>>2) & 3)
+ switch ((mp_irqs[idx].mp_irqflag>>2) & 3)
{
case 0: /* conforms, ie. bus-type dependent */
if (test_bit(bus, mp_bus_not_pci))
@@ -660,16 +653,16 @@ static inline int irq_trigger(int idx)
static int pin_2_irq(int idx, int apic, int pin)
{
int irq, i;
- int bus = mp_irqs[idx].mpc_srcbus;
+ int bus = mp_irqs[idx].mp_srcbus;
/*
* Debugging check, we are in big trouble if this message pops up!
*/
- if (mp_irqs[idx].mpc_dstirq != pin)
+ if (mp_irqs[idx].mp_dstirq != pin)
printk(KERN_ERR "broken BIOS or MPTABLE parser, ayiee!!\n");
if (test_bit(bus, mp_bus_not_pci)) {
- irq = mp_irqs[idx].mpc_srcbusirq;
+ irq = mp_irqs[idx].mp_srcbusirq;
} else {
/*
* PCI IRQs are mapped in order
@@ -718,7 +711,7 @@ static int __assign_irq_vector(int irq, cpumask_t mask)
return 0;
}
- for_each_cpu_mask(cpu, mask) {
+ for_each_cpu_mask_nr(cpu, mask) {
cpumask_t domain, new_mask;
int new_cpu;
int vector, offset;
@@ -730,7 +723,7 @@ static int __assign_irq_vector(int irq, cpumask_t mask)
offset = current_offset;
next:
vector += 8;
- if (vector >= FIRST_SYSTEM_VECTOR) {
+ if (vector >= first_system_vector) {
/* If we run out of vectors on large boxen, must share them. */
offset = (offset + 1) % 8;
vector = FIRST_DEVICE_VECTOR + offset;
@@ -739,7 +732,7 @@ next:
continue;
if (vector == IA32_SYSCALL_VECTOR)
goto next;
- for_each_cpu_mask(new_cpu, new_mask)
+ for_each_cpu_mask_nr(new_cpu, new_mask)
if (per_cpu(vector_irq, new_cpu)[vector] != -1)
goto next;
/* Found one! */
@@ -749,7 +742,7 @@ next:
cfg->move_in_progress = 1;
cfg->old_domain = cfg->domain;
}
- for_each_cpu_mask(new_cpu, new_mask)
+ for_each_cpu_mask_nr(new_cpu, new_mask)
per_cpu(vector_irq, new_cpu)[vector] = irq;
cfg->vector = vector;
cfg->domain = domain;
@@ -781,7 +774,7 @@ static void __clear_irq_vector(int irq)
vector = cfg->vector;
cpus_and(mask, cfg->domain, cpu_online_map);
- for_each_cpu_mask(cpu, mask)
+ for_each_cpu_mask_nr(cpu, mask)
per_cpu(vector_irq, cpu)[vector] = -1;
cfg->vector = 0;
@@ -846,7 +839,7 @@ static void setup_IO_APIC_irq(int apic, int pin, unsigned int irq,
apic_printk(APIC_VERBOSE,KERN_DEBUG
"IOAPIC[%d]: Set routing entry (%d-%d -> 0x%x -> "
"IRQ %d Mode:%i Active:%i)\n",
- apic, mp_ioapics[apic].mpc_apicid, pin, cfg->vector,
+ apic, mp_ioapics[apic].mp_apicid, pin, cfg->vector,
irq, trigger, polarity);
/*
@@ -887,10 +880,10 @@ static void __init setup_IO_APIC_irqs(void)
idx = find_irq_entry(apic,pin,mp_INT);
if (idx == -1) {
if (first_notcon) {
- apic_printk(APIC_VERBOSE, KERN_DEBUG " IO-APIC (apicid-pin) %d-%d", mp_ioapics[apic].mpc_apicid, pin);
+ apic_printk(APIC_VERBOSE, KERN_DEBUG " IO-APIC (apicid-pin) %d-%d", mp_ioapics[apic].mp_apicid, pin);
first_notcon = 0;
} else
- apic_printk(APIC_VERBOSE, ", %d-%d", mp_ioapics[apic].mpc_apicid, pin);
+ apic_printk(APIC_VERBOSE, ", %d-%d", mp_ioapics[apic].mp_apicid, pin);
continue;
}
if (!first_notcon) {
@@ -911,26 +904,21 @@ static void __init setup_IO_APIC_irqs(void)
}
/*
- * Set up the 8259A-master output pin as broadcast to all
- * CPUs.
+ * Set up the timer pin, possibly with the 8259A-master behind.
*/
-static void __init setup_ExtINT_IRQ0_pin(unsigned int apic, unsigned int pin, int vector)
+static void __init setup_timer_IRQ0_pin(unsigned int apic, unsigned int pin,
+ int vector)
{
struct IO_APIC_route_entry entry;
memset(&entry, 0, sizeof(entry));
- disable_8259A_irq(0);
-
- /* mask LVT0 */
- apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT);
-
/*
* We use logical delivery to get the timer IRQ
* to the first CPU.
*/
entry.dest_mode = INT_DEST_MODE;
- entry.mask = 0; /* unmask IRQ now */
+ entry.mask = 1; /* mask IRQ now */
entry.dest = cpu_mask_to_apicid(TARGET_CPUS);
entry.delivery_mode = INT_DELIVERY_MODE;
entry.polarity = 0;
@@ -939,7 +927,7 @@ static void __init setup_ExtINT_IRQ0_pin(unsigned int apic, unsigned int pin, in
/*
* The timer IRQ doesn't have to know that behind the
- * scene we have a 8259A-master in AEOI mode ...
+ * scene we may have a 8259A-master in AEOI mode ...
*/
set_irq_chip_and_handler_name(0, &ioapic_chip, handle_edge_irq, "edge");
@@ -947,8 +935,6 @@ static void __init setup_ExtINT_IRQ0_pin(unsigned int apic, unsigned int pin, in
* Add it to the IO-APIC irq-routing table:
*/
ioapic_write_entry(apic, pin, entry);
-
- enable_8259A_irq(0);
}
void __apicdebuginit print_IO_APIC(void)
@@ -965,7 +951,7 @@ void __apicdebuginit print_IO_APIC(void)
printk(KERN_DEBUG "number of MP IRQ sources: %d.\n", mp_irq_entries);
for (i = 0; i < nr_ioapics; i++)
printk(KERN_DEBUG "number of IO-APIC #%d registers: %d.\n",
- mp_ioapics[i].mpc_apicid, nr_ioapic_registers[i]);
+ mp_ioapics[i].mp_apicid, nr_ioapic_registers[i]);
/*
* We are a bit conservative about what we expect. We have to
@@ -983,7 +969,7 @@ void __apicdebuginit print_IO_APIC(void)
spin_unlock_irqrestore(&ioapic_lock, flags);
printk("\n");
- printk(KERN_DEBUG "IO APIC #%d......\n", mp_ioapics[apic].mpc_apicid);
+ printk(KERN_DEBUG "IO APIC #%d......\n", mp_ioapics[apic].mp_apicid);
printk(KERN_DEBUG ".... register #00: %08X\n", reg_00.raw);
printk(KERN_DEBUG "....... : physical APIC id: %02X\n", reg_00.bits.ID);
@@ -1077,6 +1063,7 @@ void __apicdebuginit print_local_APIC(void * dummy)
printk("\n" KERN_DEBUG "printing local APIC contents on CPU#%d/%d:\n",
smp_processor_id(), hard_smp_processor_id());
+ v = apic_read(APIC_ID);
printk(KERN_INFO "... APIC ID: %08x (%01x)\n", v, GET_APIC_ID(read_apic_id()));
v = apic_read(APIC_LVR);
printk(KERN_INFO "... APIC VERSION: %08x\n", v);
@@ -1659,6 +1646,7 @@ static inline void __init check_timer(void)
struct irq_cfg *cfg = irq_cfg + 0;
int apic1, pin1, apic2, pin2;
unsigned long flags;
+ int no_pin1 = 0;
local_irq_save(flags);
@@ -1669,16 +1657,11 @@ static inline void __init check_timer(void)
assign_irq_vector(0, TARGET_CPUS);
/*
- * Subtle, code in do_timer_interrupt() expects an AEOI
- * mode for the 8259A whenever interrupts are routed
- * through I/O APICs. Also IRQ0 has to be enabled in
- * the 8259A which implies the virtual wire has to be
- * disabled in the local APIC.
+ * As IRQ0 is to be enabled in the 8259A, the virtual
+ * wire has to be disabled in the local APIC.
*/
apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT);
init_8259A(1);
- if (timer_over_8254 > 0)
- enable_8259A_irq(0);
pin1 = find_isa_irq_pin(0, mp_INT);
apic1 = find_isa_irq_apic(0, mp_INT);
@@ -1688,15 +1671,34 @@ static inline void __init check_timer(void)
apic_printk(APIC_VERBOSE,KERN_INFO "..TIMER: vector=0x%02X apic1=%d pin1=%d apic2=%d pin2=%d\n",
cfg->vector, apic1, pin1, apic2, pin2);
+ /*
+ * Some BIOS writers are clueless and report the ExtINTA
+ * I/O APIC input from the cascaded 8259A as the timer
+ * interrupt input. So just in case, if only one pin
+ * was found above, try it both directly and through the
+ * 8259A.
+ */
+ if (pin1 == -1) {
+ pin1 = pin2;
+ apic1 = apic2;
+ no_pin1 = 1;
+ } else if (pin2 == -1) {
+ pin2 = pin1;
+ apic2 = apic1;
+ }
+
if (pin1 != -1) {
/*
* Ok, does IRQ0 through the IOAPIC work?
*/
+ if (no_pin1) {
+ add_pin_to_irq(0, apic1, pin1);
+ setup_timer_IRQ0_pin(apic1, pin1, cfg->vector);
+ }
unmask_IO_APIC_irq(0);
if (!no_timer_check && timer_irq_works()) {
nmi_watchdog_default();
if (nmi_watchdog == NMI_IO_APIC) {
- disable_8259A_irq(0);
setup_nmi();
enable_8259A_irq(0);
}
@@ -1705,42 +1707,48 @@ static inline void __init check_timer(void)
goto out;
}
clear_IO_APIC_pin(apic1, pin1);
- apic_printk(APIC_QUIET,KERN_ERR "..MP-BIOS bug: 8254 timer not "
- "connected to IO-APIC\n");
- }
+ if (!no_pin1)
+ apic_printk(APIC_QUIET,KERN_ERR "..MP-BIOS bug: "
+ "8254 timer not connected to IO-APIC\n");
- apic_printk(APIC_VERBOSE,KERN_INFO "...trying to set up timer (IRQ0) "
- "through the 8259A ... ");
- if (pin2 != -1) {
+ apic_printk(APIC_VERBOSE,KERN_INFO
+ "...trying to set up timer (IRQ0) "
+ "through the 8259A ... ");
apic_printk(APIC_VERBOSE,"\n..... (found apic %d pin %d) ...",
apic2, pin2);
/*
* legacy devices should be connected to IO APIC #0
*/
- setup_ExtINT_IRQ0_pin(apic2, pin2, cfg->vector);
+ /* replace_pin_at_irq(0, apic1, pin1, apic2, pin2); */
+ setup_timer_IRQ0_pin(apic2, pin2, cfg->vector);
+ unmask_IO_APIC_irq(0);
+ enable_8259A_irq(0);
if (timer_irq_works()) {
apic_printk(APIC_VERBOSE," works.\n");
+ timer_through_8259 = 1;
nmi_watchdog_default();
if (nmi_watchdog == NMI_IO_APIC) {
+ disable_8259A_irq(0);
setup_nmi();
+ enable_8259A_irq(0);
}
goto out;
}
/*
* Cleanup, just in case ...
*/
+ disable_8259A_irq(0);
clear_IO_APIC_pin(apic2, pin2);
+ apic_printk(APIC_VERBOSE," failed.\n");
}
- apic_printk(APIC_VERBOSE," failed.\n");
if (nmi_watchdog == NMI_IO_APIC) {
printk(KERN_WARNING "timer doesn't work through the IO-APIC - disabling NMI Watchdog!\n");
- nmi_watchdog = 0;
+ nmi_watchdog = NMI_NONE;
}
apic_printk(APIC_VERBOSE, KERN_INFO "...trying to set up timer as Virtual Wire IRQ...");
- disable_8259A_irq(0);
irq_desc[0].chip = &lapic_irq_type;
apic_write(APIC_LVT0, APIC_DM_FIXED | cfg->vector); /* Fixed mode */
enable_8259A_irq(0);
@@ -1749,6 +1757,7 @@ static inline void __init check_timer(void)
apic_printk(APIC_VERBOSE," works.\n");
goto out;
}
+ disable_8259A_irq(0);
apic_write(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | cfg->vector);
apic_printk(APIC_VERBOSE," failed.\n");
@@ -1841,8 +1850,8 @@ static int ioapic_resume(struct sys_device *dev)
spin_lock_irqsave(&ioapic_lock, flags);
reg_00.raw = io_apic_read(dev->id, 0);
- if (reg_00.bits.ID != mp_ioapics[dev->id].mpc_apicid) {
- reg_00.bits.ID = mp_ioapics[dev->id].mpc_apicid;
+ if (reg_00.bits.ID != mp_ioapics[dev->id].mp_apicid) {
+ reg_00.bits.ID = mp_ioapics[dev->id].mp_apicid;
io_apic_write(dev->id, 0, reg_00.raw);
}
spin_unlock_irqrestore(&ioapic_lock, flags);
@@ -2242,8 +2251,8 @@ int acpi_get_override_irq(int bus_irq, int *trigger, int *polarity)
return -1;
for (i = 0; i < mp_irq_entries; i++)
- if (mp_irqs[i].mpc_irqtype == mp_INT &&
- mp_irqs[i].mpc_srcbusirq == bus_irq)
+ if (mp_irqs[i].mp_irqtype == mp_INT &&
+ mp_irqs[i].mp_srcbusirq == bus_irq)
break;
if (i >= mp_irq_entries)
return -1;
@@ -2336,7 +2345,7 @@ void __init ioapic_init_mappings(void)
ioapic_res = ioapic_setup_resources();
for (i = 0; i < nr_ioapics; i++) {
if (smp_found_config) {
- ioapic_phys = mp_ioapics[i].mpc_apicaddr;
+ ioapic_phys = mp_ioapics[i].mp_apicaddr;
} else {
ioapic_phys = (unsigned long)
alloc_bootmem_pages(PAGE_SIZE);
diff --git a/arch/x86/kernel/ipi.c b/arch/x86/kernel/ipi.c
index c0df7b89ca23..9d98cda39ad9 100644
--- a/arch/x86/kernel/ipi.c
+++ b/arch/x86/kernel/ipi.c
@@ -8,7 +8,6 @@
#include <linux/kernel_stat.h>
#include <linux/mc146818rtc.h>
#include <linux/cache.h>
-#include <linux/interrupt.h>
#include <linux/cpu.h>
#include <linux/module.h>
diff --git a/arch/x86/kernel/irq_32.c b/arch/x86/kernel/irq_32.c
index 147352df28b9..47a6f6f12478 100644
--- a/arch/x86/kernel/irq_32.c
+++ b/arch/x86/kernel/irq_32.c
@@ -48,6 +48,29 @@ void ack_bad_irq(unsigned int irq)
#endif
}
+#ifdef CONFIG_DEBUG_STACKOVERFLOW
+/* Debugging check for stack overflow: is there less than 1KB free? */
+static int check_stack_overflow(void)
+{
+ long sp;
+
+ __asm__ __volatile__("andl %%esp,%0" :
+ "=r" (sp) : "0" (THREAD_SIZE - 1));
+
+ return sp < (sizeof(struct thread_info) + STACK_WARN);
+}
+
+static void print_stack_overflow(void)
+{
+ printk(KERN_WARNING "low stack detected by irq handler\n");
+ dump_stack();
+}
+
+#else
+static inline int check_stack_overflow(void) { return 0; }
+static inline void print_stack_overflow(void) { }
+#endif
+
#ifdef CONFIG_4KSTACKS
/*
* per-CPU IRQ handling contexts (thread information and stack)
@@ -59,48 +82,29 @@ union irq_ctx {
static union irq_ctx *hardirq_ctx[NR_CPUS] __read_mostly;
static union irq_ctx *softirq_ctx[NR_CPUS] __read_mostly;
-#endif
-/*
- * do_IRQ handles all normal device IRQ's (the special
- * SMP cross-CPU interrupts have their own specific
- * handlers).
- */
-unsigned int do_IRQ(struct pt_regs *regs)
-{
- struct pt_regs *old_regs;
- /* high bit used in ret_from_ code */
- int irq = ~regs->orig_ax;
- struct irq_desc *desc = irq_desc + irq;
-#ifdef CONFIG_4KSTACKS
- union irq_ctx *curctx, *irqctx;
- u32 *isp;
-#endif
+static char softirq_stack[NR_CPUS * THREAD_SIZE]
+ __attribute__((__section__(".bss.page_aligned")));
- if (unlikely((unsigned)irq >= NR_IRQS)) {
- printk(KERN_EMERG "%s: cannot handle IRQ %d\n",
- __func__, irq);
- BUG();
- }
+static char hardirq_stack[NR_CPUS * THREAD_SIZE]
+ __attribute__((__section__(".bss.page_aligned")));
- old_regs = set_irq_regs(regs);
- irq_enter();
-#ifdef CONFIG_DEBUG_STACKOVERFLOW
- /* Debugging check for stack overflow: is there less than 1KB free? */
- {
- long sp;
-
- __asm__ __volatile__("andl %%esp,%0" :
- "=r" (sp) : "0" (THREAD_SIZE - 1));
- if (unlikely(sp < (sizeof(struct thread_info) + STACK_WARN))) {
- printk("do_IRQ: stack overflow: %ld\n",
- sp - sizeof(struct thread_info));
- dump_stack();
- }
- }
-#endif
+static void call_on_stack(void *func, void *stack)
+{
+ asm volatile("xchgl %%ebx,%%esp \n"
+ "call *%%edi \n"
+ "movl %%ebx,%%esp \n"
+ : "=b" (stack)
+ : "0" (stack),
+ "D"(func)
+ : "memory", "cc", "edx", "ecx", "eax");
+}
-#ifdef CONFIG_4KSTACKS
+static inline int
+execute_on_irq_stack(int overflow, struct irq_desc *desc, int irq)
+{
+ union irq_ctx *curctx, *irqctx;
+ u32 *isp, arg1, arg2;
curctx = (union irq_ctx *) current_thread_info();
irqctx = hardirq_ctx[smp_processor_id()];
@@ -111,52 +115,39 @@ unsigned int do_IRQ(struct pt_regs *regs)
* handler) we can't do that and just have to keep using the
* current stack (which is the irq stack already after all)
*/
- if (curctx != irqctx) {
- int arg1, arg2, bx;
-
- /* build the stack frame on the IRQ stack */
- isp = (u32*) ((char*)irqctx + sizeof(*irqctx));
- irqctx->tinfo.task = curctx->tinfo.task;
- irqctx->tinfo.previous_esp = current_stack_pointer;
+ if (unlikely(curctx == irqctx))
+ return 0;
- /*
- * Copy the softirq bits in preempt_count so that the
- * softirq checks work in the hardirq context.
- */
- irqctx->tinfo.preempt_count =
- (irqctx->tinfo.preempt_count & ~SOFTIRQ_MASK) |
- (curctx->tinfo.preempt_count & SOFTIRQ_MASK);
-
- asm volatile(
- " xchgl %%ebx,%%esp \n"
- " call *%%edi \n"
- " movl %%ebx,%%esp \n"
- : "=a" (arg1), "=d" (arg2), "=b" (bx)
- : "0" (irq), "1" (desc), "2" (isp),
- "D" (desc->handle_irq)
- : "memory", "cc", "ecx"
- );
- } else
-#endif
- desc->handle_irq(irq, desc);
+ /* build the stack frame on the IRQ stack */
+ isp = (u32 *) ((char*)irqctx + sizeof(*irqctx));
+ irqctx->tinfo.task = curctx->tinfo.task;
+ irqctx->tinfo.previous_esp = current_stack_pointer;
- irq_exit();
- set_irq_regs(old_regs);
+ /*
+ * Copy the softirq bits in preempt_count so that the
+ * softirq checks work in the hardirq context.
+ */
+ irqctx->tinfo.preempt_count =
+ (irqctx->tinfo.preempt_count & ~SOFTIRQ_MASK) |
+ (curctx->tinfo.preempt_count & SOFTIRQ_MASK);
+
+ if (unlikely(overflow))
+ call_on_stack(print_stack_overflow, isp);
+
+ asm volatile("xchgl %%ebx,%%esp \n"
+ "call *%%edi \n"
+ "movl %%ebx,%%esp \n"
+ : "=a" (arg1), "=d" (arg2), "=b" (isp)
+ : "0" (irq), "1" (desc), "2" (isp),
+ "D" (desc->handle_irq)
+ : "memory", "cc", "ecx");
return 1;
}
-#ifdef CONFIG_4KSTACKS
-
-static char softirq_stack[NR_CPUS * THREAD_SIZE]
- __attribute__((__section__(".bss.page_aligned")));
-
-static char hardirq_stack[NR_CPUS * THREAD_SIZE]
- __attribute__((__section__(".bss.page_aligned")));
-
/*
* allocate per-cpu stacks for hardirq and for softirq processing
*/
-void irq_ctx_init(int cpu)
+void __cpuinit irq_ctx_init(int cpu)
{
union irq_ctx *irqctx;
@@ -164,25 +155,25 @@ void irq_ctx_init(int cpu)
return;
irqctx = (union irq_ctx*) &hardirq_stack[cpu*THREAD_SIZE];
- irqctx->tinfo.task = NULL;
- irqctx->tinfo.exec_domain = NULL;
- irqctx->tinfo.cpu = cpu;
- irqctx->tinfo.preempt_count = HARDIRQ_OFFSET;
- irqctx->tinfo.addr_limit = MAKE_MM_SEG(0);
+ irqctx->tinfo.task = NULL;
+ irqctx->tinfo.exec_domain = NULL;
+ irqctx->tinfo.cpu = cpu;
+ irqctx->tinfo.preempt_count = HARDIRQ_OFFSET;
+ irqctx->tinfo.addr_limit = MAKE_MM_SEG(0);
hardirq_ctx[cpu] = irqctx;
irqctx = (union irq_ctx*) &softirq_stack[cpu*THREAD_SIZE];
- irqctx->tinfo.task = NULL;
- irqctx->tinfo.exec_domain = NULL;
- irqctx->tinfo.cpu = cpu;
- irqctx->tinfo.preempt_count = 0;
- irqctx->tinfo.addr_limit = MAKE_MM_SEG(0);
+ irqctx->tinfo.task = NULL;
+ irqctx->tinfo.exec_domain = NULL;
+ irqctx->tinfo.cpu = cpu;
+ irqctx->tinfo.preempt_count = 0;
+ irqctx->tinfo.addr_limit = MAKE_MM_SEG(0);
softirq_ctx[cpu] = irqctx;
- printk("CPU %u irqstacks, hard=%p soft=%p\n",
- cpu,hardirq_ctx[cpu],softirq_ctx[cpu]);
+ printk(KERN_DEBUG "CPU %u irqstacks, hard=%p soft=%p\n",
+ cpu,hardirq_ctx[cpu],softirq_ctx[cpu]);
}
void irq_ctx_exit(int cpu)
@@ -211,25 +202,56 @@ asmlinkage void do_softirq(void)
/* build the stack frame on the softirq stack */
isp = (u32*) ((char*)irqctx + sizeof(*irqctx));
- asm volatile(
- " xchgl %%ebx,%%esp \n"
- " call __do_softirq \n"
- " movl %%ebx,%%esp \n"
- : "=b"(isp)
- : "0"(isp)
- : "memory", "cc", "edx", "ecx", "eax"
- );
+ call_on_stack(__do_softirq, isp);
/*
* Shouldnt happen, we returned above if in_interrupt():
- */
+ */
WARN_ON_ONCE(softirq_count());
}
local_irq_restore(flags);
}
+
+#else
+static inline int
+execute_on_irq_stack(int overflow, struct irq_desc *desc, int irq) { return 0; }
#endif
/*
+ * do_IRQ handles all normal device IRQ's (the special
+ * SMP cross-CPU interrupts have their own specific
+ * handlers).
+ */
+unsigned int do_IRQ(struct pt_regs *regs)
+{
+ struct pt_regs *old_regs;
+ /* high bit used in ret_from_ code */
+ int overflow, irq = ~regs->orig_ax;
+ struct irq_desc *desc = irq_desc + irq;
+
+ if (unlikely((unsigned)irq >= NR_IRQS)) {
+ printk(KERN_EMERG "%s: cannot handle IRQ %d\n",
+ __func__, irq);
+ BUG();
+ }
+
+ old_regs = set_irq_regs(regs);
+ irq_enter();
+
+ overflow = check_stack_overflow();
+
+ if (!execute_on_irq_stack(overflow, desc, irq)) {
+ if (unlikely(overflow))
+ print_stack_overflow();
+ desc->handle_irq(irq, desc);
+ }
+
+ irq_exit();
+ set_irq_regs(old_regs);
+ return 1;
+}
+
+/*
* Interrupt statistics:
*/
@@ -313,16 +335,20 @@ skip:
per_cpu(irq_stat,j).irq_tlb_count);
seq_printf(p, " TLB shootdowns\n");
#endif
+#ifdef CONFIG_X86_MCE
seq_printf(p, "TRM: ");
for_each_online_cpu(j)
seq_printf(p, "%10u ",
per_cpu(irq_stat,j).irq_thermal_count);
seq_printf(p, " Thermal event interrupts\n");
+#endif
+#ifdef CONFIG_X86_LOCAL_APIC
seq_printf(p, "SPU: ");
for_each_online_cpu(j)
seq_printf(p, "%10u ",
per_cpu(irq_stat,j).irq_spurious_count);
seq_printf(p, " Spurious interrupts\n");
+#endif
seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count));
#if defined(CONFIG_X86_IO_APIC)
seq_printf(p, "MIS: %10u\n", atomic_read(&irq_mis_count));
@@ -331,6 +357,40 @@ skip:
return 0;
}
+/*
+ * /proc/stat helpers
+ */
+u64 arch_irq_stat_cpu(unsigned int cpu)
+{
+ u64 sum = nmi_count(cpu);
+
+#ifdef CONFIG_X86_LOCAL_APIC
+ sum += per_cpu(irq_stat, cpu).apic_timer_irqs;
+#endif
+#ifdef CONFIG_SMP
+ sum += per_cpu(irq_stat, cpu).irq_resched_count;
+ sum += per_cpu(irq_stat, cpu).irq_call_count;
+ sum += per_cpu(irq_stat, cpu).irq_tlb_count;
+#endif
+#ifdef CONFIG_X86_MCE
+ sum += per_cpu(irq_stat, cpu).irq_thermal_count;
+#endif
+#ifdef CONFIG_X86_LOCAL_APIC
+ sum += per_cpu(irq_stat, cpu).irq_spurious_count;
+#endif
+ return sum;
+}
+
+u64 arch_irq_stat(void)
+{
+ u64 sum = atomic_read(&irq_err_count);
+
+#ifdef CONFIG_X86_IO_APIC
+ sum += atomic_read(&irq_mis_count);
+#endif
+ return sum;
+}
+
#ifdef CONFIG_HOTPLUG_CPU
#include <mach_apic.h>
diff --git a/arch/x86/kernel/irq_64.c b/arch/x86/kernel/irq_64.c
index 3aac15466a91..1f78b238d8d2 100644
--- a/arch/x86/kernel/irq_64.c
+++ b/arch/x86/kernel/irq_64.c
@@ -135,6 +135,7 @@ skip:
seq_printf(p, "%10u ", cpu_pda(j)->irq_tlb_count);
seq_printf(p, " TLB shootdowns\n");
#endif
+#ifdef CONFIG_X86_MCE
seq_printf(p, "TRM: ");
for_each_online_cpu(j)
seq_printf(p, "%10u ", cpu_pda(j)->irq_thermal_count);
@@ -143,6 +144,7 @@ skip:
for_each_online_cpu(j)
seq_printf(p, "%10u ", cpu_pda(j)->irq_threshold_count);
seq_printf(p, " Threshold APIC interrupts\n");
+#endif
seq_printf(p, "SPU: ");
for_each_online_cpu(j)
seq_printf(p, "%10u ", cpu_pda(j)->irq_spurious_count);
@@ -153,6 +155,32 @@ skip:
}
/*
+ * /proc/stat helpers
+ */
+u64 arch_irq_stat_cpu(unsigned int cpu)
+{
+ u64 sum = cpu_pda(cpu)->__nmi_count;
+
+ sum += cpu_pda(cpu)->apic_timer_irqs;
+#ifdef CONFIG_SMP
+ sum += cpu_pda(cpu)->irq_resched_count;
+ sum += cpu_pda(cpu)->irq_call_count;
+ sum += cpu_pda(cpu)->irq_tlb_count;
+#endif
+#ifdef CONFIG_X86_MCE
+ sum += cpu_pda(cpu)->irq_thermal_count;
+ sum += cpu_pda(cpu)->irq_threshold_count;
+#endif
+ sum += cpu_pda(cpu)->irq_spurious_count;
+ return sum;
+}
+
+u64 arch_irq_stat(void)
+{
+ return atomic_read(&irq_err_count);
+}
+
+/*
* do_IRQ handles all normal device IRQ's (the special
* SMP cross-CPU interrupts have their own specific
* handlers).
diff --git a/arch/x86/kernel/irqinit_32.c b/arch/x86/kernel/irqinit_32.c
new file mode 100644
index 000000000000..d66914287ee1
--- /dev/null
+++ b/arch/x86/kernel/irqinit_32.c
@@ -0,0 +1,114 @@
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/init.h>
+#include <linux/kernel_stat.h>
+#include <linux/sysdev.h>
+#include <linux/bitops.h>
+
+#include <asm/atomic.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/timer.h>
+#include <asm/pgtable.h>
+#include <asm/delay.h>
+#include <asm/desc.h>
+#include <asm/apic.h>
+#include <asm/arch_hooks.h>
+#include <asm/i8259.h>
+
+
+
+/*
+ * Note that on a 486, we don't want to do a SIGFPE on an irq13
+ * as the irq is unreliable, and exception 16 works correctly
+ * (ie as explained in the intel literature). On a 386, you
+ * can't use exception 16 due to bad IBM design, so we have to
+ * rely on the less exact irq13.
+ *
+ * Careful.. Not only is IRQ13 unreliable, but it is also
+ * leads to races. IBM designers who came up with it should
+ * be shot.
+ */
+
+
+static irqreturn_t math_error_irq(int cpl, void *dev_id)
+{
+ extern void math_error(void __user *);
+ outb(0,0xF0);
+ if (ignore_fpu_irq || !boot_cpu_data.hard_math)
+ return IRQ_NONE;
+ math_error((void __user *)get_irq_regs()->ip);
+ return IRQ_HANDLED;
+}
+
+/*
+ * New motherboards sometimes make IRQ 13 be a PCI interrupt,
+ * so allow interrupt sharing.
+ */
+static struct irqaction fpu_irq = {
+ .handler = math_error_irq,
+ .mask = CPU_MASK_NONE,
+ .name = "fpu",
+};
+
+void __init init_ISA_irqs (void)
+{
+ int i;
+
+#ifdef CONFIG_X86_LOCAL_APIC
+ init_bsp_APIC();
+#endif
+ init_8259A(0);
+
+ /*
+ * 16 old-style INTA-cycle interrupts:
+ */
+ for (i = 0; i < 16; i++) {
+ set_irq_chip_and_handler_name(i, &i8259A_chip,
+ handle_level_irq, "XT");
+ }
+}
+
+/* Overridden in paravirt.c */
+void init_IRQ(void) __attribute__((weak, alias("native_init_IRQ")));
+
+void __init native_init_IRQ(void)
+{
+ int i;
+
+ /* all the set up before the call gates are initialised */
+ pre_intr_init_hook();
+
+ /*
+ * Cover the whole vector space, no vector can escape
+ * us. (some of these will be overridden and become
+ * 'special' SMP interrupts)
+ */
+ for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) {
+ int vector = FIRST_EXTERNAL_VECTOR + i;
+ if (i >= NR_IRQS)
+ break;
+ /* SYSCALL_VECTOR was reserved in trap_init. */
+ if (!test_bit(vector, used_vectors))
+ set_intr_gate(vector, interrupt[i]);
+ }
+
+ /* setup after call gates are initialised (usually add in
+ * the architecture specific gates)
+ */
+ intr_init_hook();
+
+ /*
+ * External FPU? Set up irq13 if so, for
+ * original braindamaged IBM FERR coupling.
+ */
+ if (boot_cpu_data.hard_math && !cpu_has_fpu)
+ setup_irq(FPU_IRQ, &fpu_irq);
+
+ irq_ctx_init(smp_processor_id());
+}
diff --git a/arch/x86/kernel/irqinit_64.c b/arch/x86/kernel/irqinit_64.c
new file mode 100644
index 000000000000..31f49e8f46a7
--- /dev/null
+++ b/arch/x86/kernel/irqinit_64.c
@@ -0,0 +1,217 @@
+#include <linux/linkage.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/init.h>
+#include <linux/kernel_stat.h>
+#include <linux/sysdev.h>
+#include <linux/bitops.h>
+
+#include <asm/acpi.h>
+#include <asm/atomic.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/hw_irq.h>
+#include <asm/pgtable.h>
+#include <asm/delay.h>
+#include <asm/desc.h>
+#include <asm/apic.h>
+#include <asm/i8259.h>
+
+/*
+ * Common place to define all x86 IRQ vectors
+ *
+ * This builds up the IRQ handler stubs using some ugly macros in irq.h
+ *
+ * These macros create the low-level assembly IRQ routines that save
+ * register context and call do_IRQ(). do_IRQ() then does all the
+ * operations that are needed to keep the AT (or SMP IOAPIC)
+ * interrupt-controller happy.
+ */
+
+#define IRQ_NAME2(nr) nr##_interrupt(void)
+#define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr)
+
+/*
+ * SMP has a few special interrupts for IPI messages
+ */
+
+#define BUILD_IRQ(nr) \
+ asmlinkage void IRQ_NAME(nr); \
+ asm("\n.p2align\n" \
+ "IRQ" #nr "_interrupt:\n\t" \
+ "push $~(" #nr ") ; " \
+ "jmp common_interrupt");
+
+#define BI(x,y) \
+ BUILD_IRQ(x##y)
+
+#define BUILD_16_IRQS(x) \
+ BI(x,0) BI(x,1) BI(x,2) BI(x,3) \
+ BI(x,4) BI(x,5) BI(x,6) BI(x,7) \
+ BI(x,8) BI(x,9) BI(x,a) BI(x,b) \
+ BI(x,c) BI(x,d) BI(x,e) BI(x,f)
+
+/*
+ * ISA PIC or low IO-APIC triggered (INTA-cycle or APIC) interrupts:
+ * (these are usually mapped to vectors 0x30-0x3f)
+ */
+
+/*
+ * The IO-APIC gives us many more interrupt sources. Most of these
+ * are unused but an SMP system is supposed to have enough memory ...
+ * sometimes (mostly wrt. hw bugs) we get corrupted vectors all
+ * across the spectrum, so we really want to be prepared to get all
+ * of these. Plus, more powerful systems might have more than 64
+ * IO-APIC registers.
+ *
+ * (these are usually mapped into the 0x30-0xff vector range)
+ */
+ BUILD_16_IRQS(0x2) BUILD_16_IRQS(0x3)
+BUILD_16_IRQS(0x4) BUILD_16_IRQS(0x5) BUILD_16_IRQS(0x6) BUILD_16_IRQS(0x7)
+BUILD_16_IRQS(0x8) BUILD_16_IRQS(0x9) BUILD_16_IRQS(0xa) BUILD_16_IRQS(0xb)
+BUILD_16_IRQS(0xc) BUILD_16_IRQS(0xd) BUILD_16_IRQS(0xe) BUILD_16_IRQS(0xf)
+
+#undef BUILD_16_IRQS
+#undef BI
+
+
+#define IRQ(x,y) \
+ IRQ##x##y##_interrupt
+
+#define IRQLIST_16(x) \
+ IRQ(x,0), IRQ(x,1), IRQ(x,2), IRQ(x,3), \
+ IRQ(x,4), IRQ(x,5), IRQ(x,6), IRQ(x,7), \
+ IRQ(x,8), IRQ(x,9), IRQ(x,a), IRQ(x,b), \
+ IRQ(x,c), IRQ(x,d), IRQ(x,e), IRQ(x,f)
+
+/* for the irq vectors */
+static void (*__initdata interrupt[NR_VECTORS - FIRST_EXTERNAL_VECTOR])(void) = {
+ IRQLIST_16(0x2), IRQLIST_16(0x3),
+ IRQLIST_16(0x4), IRQLIST_16(0x5), IRQLIST_16(0x6), IRQLIST_16(0x7),
+ IRQLIST_16(0x8), IRQLIST_16(0x9), IRQLIST_16(0xa), IRQLIST_16(0xb),
+ IRQLIST_16(0xc), IRQLIST_16(0xd), IRQLIST_16(0xe), IRQLIST_16(0xf)
+};
+
+#undef IRQ
+#undef IRQLIST_16
+
+
+
+
+/*
+ * IRQ2 is cascade interrupt to second interrupt controller
+ */
+
+static struct irqaction irq2 = {
+ .handler = no_action,
+ .mask = CPU_MASK_NONE,
+ .name = "cascade",
+};
+DEFINE_PER_CPU(vector_irq_t, vector_irq) = {
+ [0 ... IRQ0_VECTOR - 1] = -1,
+ [IRQ0_VECTOR] = 0,
+ [IRQ1_VECTOR] = 1,
+ [IRQ2_VECTOR] = 2,
+ [IRQ3_VECTOR] = 3,
+ [IRQ4_VECTOR] = 4,
+ [IRQ5_VECTOR] = 5,
+ [IRQ6_VECTOR] = 6,
+ [IRQ7_VECTOR] = 7,
+ [IRQ8_VECTOR] = 8,
+ [IRQ9_VECTOR] = 9,
+ [IRQ10_VECTOR] = 10,
+ [IRQ11_VECTOR] = 11,
+ [IRQ12_VECTOR] = 12,
+ [IRQ13_VECTOR] = 13,
+ [IRQ14_VECTOR] = 14,
+ [IRQ15_VECTOR] = 15,
+ [IRQ15_VECTOR + 1 ... NR_VECTORS - 1] = -1
+};
+
+static void __init init_ISA_irqs (void)
+{
+ int i;
+
+ init_bsp_APIC();
+ init_8259A(0);
+
+ for (i = 0; i < NR_IRQS; i++) {
+ irq_desc[i].status = IRQ_DISABLED;
+ irq_desc[i].action = NULL;
+ irq_desc[i].depth = 1;
+
+ if (i < 16) {
+ /*
+ * 16 old-style INTA-cycle interrupts:
+ */
+ set_irq_chip_and_handler_name(i, &i8259A_chip,
+ handle_level_irq, "XT");
+ } else {
+ /*
+ * 'high' PCI IRQs filled in on demand
+ */
+ irq_desc[i].chip = &no_irq_chip;
+ }
+ }
+}
+
+void init_IRQ(void) __attribute__((weak, alias("native_init_IRQ")));
+
+void __init native_init_IRQ(void)
+{
+ int i;
+
+ init_ISA_irqs();
+ /*
+ * Cover the whole vector space, no vector can escape
+ * us. (some of these will be overridden and become
+ * 'special' SMP interrupts)
+ */
+ for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) {
+ int vector = FIRST_EXTERNAL_VECTOR + i;
+ if (vector != IA32_SYSCALL_VECTOR)
+ set_intr_gate(vector, interrupt[i]);
+ }
+
+#ifdef CONFIG_SMP
+ /*
+ * The reschedule interrupt is a CPU-to-CPU reschedule-helper
+ * IPI, driven by wakeup.
+ */
+ alloc_intr_gate(RESCHEDULE_VECTOR, reschedule_interrupt);
+
+ /* IPIs for invalidation */
+ alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+0, invalidate_interrupt0);
+ alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+1, invalidate_interrupt1);
+ alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+2, invalidate_interrupt2);
+ alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+3, invalidate_interrupt3);
+ alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+4, invalidate_interrupt4);
+ alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+5, invalidate_interrupt5);
+ alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+6, invalidate_interrupt6);
+ alloc_intr_gate(INVALIDATE_TLB_VECTOR_START+7, invalidate_interrupt7);
+
+ /* IPI for generic function call */
+ alloc_intr_gate(CALL_FUNCTION_VECTOR, call_function_interrupt);
+
+ /* Low priority IPI to cleanup after moving an irq */
+ set_intr_gate(IRQ_MOVE_CLEANUP_VECTOR, irq_move_cleanup_interrupt);
+#endif
+ alloc_intr_gate(THERMAL_APIC_VECTOR, thermal_interrupt);
+ alloc_intr_gate(THRESHOLD_APIC_VECTOR, threshold_interrupt);
+
+ /* self generated IPI for local APIC timer */
+ alloc_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt);
+
+ /* IPI vectors for APIC spurious and error interrupts */
+ alloc_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt);
+ alloc_intr_gate(ERROR_APIC_VECTOR, error_interrupt);
+
+ if (!acpi_ioapic)
+ setup_irq(2, &irq2);
+}
diff --git a/arch/x86/kernel/ldt.c b/arch/x86/kernel/ldt.c
index 0224c3637c73..21f2bae98c15 100644
--- a/arch/x86/kernel/ldt.c
+++ b/arch/x86/kernel/ldt.c
@@ -20,9 +20,9 @@
#include <asm/mmu_context.h>
#ifdef CONFIG_SMP
-static void flush_ldt(void *null)
+static void flush_ldt(void *current_mm)
{
- if (current->active_mm)
+ if (current->active_mm == current_mm)
load_LDT(&current->active_mm->context);
}
#endif
@@ -68,7 +68,7 @@ static int alloc_ldt(mm_context_t *pc, int mincount, int reload)
load_LDT(pc);
mask = cpumask_of_cpu(smp_processor_id());
if (!cpus_equal(current->mm->cpu_vm_mask, mask))
- smp_call_function(flush_ldt, NULL, 1, 1);
+ smp_call_function(flush_ldt, current->mm, 1, 1);
preempt_enable();
#else
load_LDT(pc);
diff --git a/arch/x86/kernel/machine_kexec_32.c b/arch/x86/kernel/machine_kexec_32.c
index d0b234c9fc31..88923fd7a6fc 100644
--- a/arch/x86/kernel/machine_kexec_32.c
+++ b/arch/x86/kernel/machine_kexec_32.c
@@ -11,6 +11,8 @@
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/numa.h>
+#include <linux/ftrace.h>
+
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
#include <asm/tlbflush.h>
@@ -107,6 +109,8 @@ NORET_TYPE void machine_kexec(struct kimage *image)
unsigned long page_list[PAGES_NR];
void *control_page;
+ tracer_disable();
+
/* Interrupts aren't acceptable while we reboot */
local_irq_disable();
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c
index 576a03db4511..1558fdc174f9 100644
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -11,6 +11,8 @@
#include <linux/string.h>
#include <linux/reboot.h>
#include <linux/numa.h>
+#include <linux/ftrace.h>
+
#include <asm/pgtable.h>
#include <asm/tlbflush.h>
#include <asm/mmu_context.h>
@@ -184,6 +186,8 @@ NORET_TYPE void machine_kexec(struct kimage *image)
unsigned long page_list[PAGES_NR];
void *control_page;
+ tracer_disable();
+
/* Interrupts aren't acceptable while we reboot */
local_irq_disable();
diff --git a/arch/x86/kernel/mfgpt_32.c b/arch/x86/kernel/mfgpt_32.c
index 3cad17fe026b..07c0f828f488 100644
--- a/arch/x86/kernel/mfgpt_32.c
+++ b/arch/x86/kernel/mfgpt_32.c
@@ -155,6 +155,7 @@ int geode_mfgpt_toggle_event(int timer, int cmp, int event, int enable)
wrmsr(msr, value, dummy);
return 0;
}
+EXPORT_SYMBOL_GPL(geode_mfgpt_toggle_event);
int geode_mfgpt_set_irq(int timer, int cmp, int irq, int enable)
{
@@ -222,6 +223,7 @@ int geode_mfgpt_alloc_timer(int timer, int domain)
/* No timers available - too bad */
return -1;
}
+EXPORT_SYMBOL_GPL(geode_mfgpt_alloc_timer);
#ifdef CONFIG_GEODE_MFGPT_TIMER
diff --git a/arch/x86/kernel/microcode.c b/arch/x86/kernel/microcode.c
index 69729e38b78a..649dfd761f23 100644
--- a/arch/x86/kernel/microcode.c
+++ b/arch/x86/kernel/microcode.c
@@ -488,7 +488,7 @@ MODULE_ALIAS_MISCDEV(MICROCODE_MINOR);
#define microcode_dev_exit() do { } while(0)
#endif
-static long get_next_ucode_from_buffer(void **mc, void *buf,
+static long get_next_ucode_from_buffer(void **mc, const u8 *buf,
unsigned long size, long offset)
{
microcode_header_t *mc_header;
@@ -522,7 +522,7 @@ static int cpu_request_microcode(int cpu)
char name[30];
struct cpuinfo_x86 *c = &cpu_data(cpu);
const struct firmware *firmware;
- void *buf;
+ const u8 *buf;
unsigned long size;
long offset = 0;
int error;
diff --git a/arch/x86/kernel/mmconf-fam10h_64.c b/arch/x86/kernel/mmconf-fam10h_64.c
index edc5fbfe85c0..fdfdc550b366 100644
--- a/arch/x86/kernel/mmconf-fam10h_64.c
+++ b/arch/x86/kernel/mmconf-fam10h_64.c
@@ -12,6 +12,7 @@
#include <asm/io.h>
#include <asm/msr.h>
#include <asm/acpi.h>
+#include <asm/mmconfig.h>
#include "../pci/pci.h"
diff --git a/arch/x86/kernel/mpparse.c b/arch/x86/kernel/mpparse.c
index 404683b94e79..6ae60909b601 100644
--- a/arch/x86/kernel/mpparse.c
+++ b/arch/x86/kernel/mpparse.c
@@ -25,6 +25,8 @@
#include <asm/proto.h>
#include <asm/acpi.h>
#include <asm/bios_ebda.h>
+#include <asm/e820.h>
+#include <asm/trampoline.h>
#include <mach_apic.h>
#ifdef CONFIG_X86_32
@@ -32,28 +34,6 @@
#include <mach_mpparse.h>
#endif
-/* Have we found an MP table */
-int smp_found_config;
-
-/*
- * Various Linux-internal data structures created from the
- * MP-table.
- */
-#if defined (CONFIG_MCA) || defined (CONFIG_EISA)
-int mp_bus_id_to_type[MAX_MP_BUSSES];
-#endif
-
-DECLARE_BITMAP(mp_bus_not_pci, MAX_MP_BUSSES);
-int mp_bus_id_to_pci_bus[MAX_MP_BUSSES] = {[0 ... MAX_MP_BUSSES - 1] = -1 };
-
-static int mp_current_pci_id;
-
-int pic_mode;
-
-/*
- * Intel MP BIOS table parsing routines:
- */
-
/*
* Checksum an MP configuration block.
*/
@@ -69,15 +49,73 @@ static int __init mpf_checksum(unsigned char *mp, int len)
}
#ifdef CONFIG_X86_NUMAQ
+int found_numaq;
/*
* Have to match translation table entries to main table entries by counter
* hence the mpc_record variable .... can't see a less disgusting way of
* doing this ....
*/
+struct mpc_config_translation {
+ unsigned char mpc_type;
+ unsigned char trans_len;
+ unsigned char trans_type;
+ unsigned char trans_quad;
+ unsigned char trans_global;
+ unsigned char trans_local;
+ unsigned short trans_reserved;
+};
+
static int mpc_record;
static struct mpc_config_translation *translation_table[MAX_MPC_ENTRY]
__cpuinitdata;
+
+static inline int generate_logical_apicid(int quad, int phys_apicid)
+{
+ return (quad << 4) + (phys_apicid ? phys_apicid << 1 : 1);
+}
+
+
+static inline int mpc_apic_id(struct mpc_config_processor *m,
+ struct mpc_config_translation *translation_record)
+{
+ int quad = translation_record->trans_quad;
+ int logical_apicid = generate_logical_apicid(quad, m->mpc_apicid);
+
+ printk(KERN_DEBUG "Processor #%d %u:%u APIC version %d (quad %d, apic %d)\n",
+ m->mpc_apicid,
+ (m->mpc_cpufeature & CPU_FAMILY_MASK) >> 8,
+ (m->mpc_cpufeature & CPU_MODEL_MASK) >> 4,
+ m->mpc_apicver, quad, logical_apicid);
+ return logical_apicid;
+}
+
+int mp_bus_id_to_node[MAX_MP_BUSSES];
+
+int mp_bus_id_to_local[MAX_MP_BUSSES];
+
+static void mpc_oem_bus_info(struct mpc_config_bus *m, char *name,
+ struct mpc_config_translation *translation)
+{
+ int quad = translation->trans_quad;
+ int local = translation->trans_local;
+
+ mp_bus_id_to_node[m->mpc_busid] = quad;
+ mp_bus_id_to_local[m->mpc_busid] = local;
+ printk(KERN_INFO "Bus #%d is %s (node %d)\n",
+ m->mpc_busid, name, quad);
+}
+
+int quad_local_to_mp_bus_id [NR_CPUS/4][4];
+static void mpc_oem_pci_bus(struct mpc_config_bus *m,
+ struct mpc_config_translation *translation)
+{
+ int quad = translation->trans_quad;
+ int local = translation->trans_local;
+
+ quad_local_to_mp_bus_id[quad][local] = m->mpc_busid;
+}
+
#endif
static void __cpuinit MP_processor_info(struct mpc_config_processor *m)
@@ -90,7 +128,10 @@ static void __cpuinit MP_processor_info(struct mpc_config_processor *m)
return;
}
#ifdef CONFIG_X86_NUMAQ
- apicid = mpc_apic_id(m, translation_table[mpc_record]);
+ if (found_numaq)
+ apicid = mpc_apic_id(m, translation_table[mpc_record]);
+ else
+ apicid = m->mpc_apicid;
#else
apicid = m->mpc_apicid;
#endif
@@ -103,17 +144,18 @@ static void __cpuinit MP_processor_info(struct mpc_config_processor *m)
generic_processor_info(apicid, m->mpc_apicver);
}
+#ifdef CONFIG_X86_IO_APIC
static void __init MP_bus_info(struct mpc_config_bus *m)
{
char str[7];
-
memcpy(str, m->mpc_bustype, 6);
str[6] = 0;
#ifdef CONFIG_X86_NUMAQ
- mpc_oem_bus_info(m, str, translation_table[mpc_record]);
+ if (found_numaq)
+ mpc_oem_bus_info(m, str, translation_table[mpc_record]);
#else
- Dprintk("Bus #%d is %s\n", m->mpc_busid, str);
+ printk(KERN_INFO "Bus #%d is %s\n", m->mpc_busid, str);
#endif
#if MAX_MP_BUSSES < 256
@@ -132,11 +174,10 @@ static void __init MP_bus_info(struct mpc_config_bus *m)
#endif
} else if (strncmp(str, BUSTYPE_PCI, sizeof(BUSTYPE_PCI) - 1) == 0) {
#ifdef CONFIG_X86_NUMAQ
- mpc_oem_pci_bus(m, translation_table[mpc_record]);
+ if (found_numaq)
+ mpc_oem_pci_bus(m, translation_table[mpc_record]);
#endif
clear_bit(m->mpc_busid, mp_bus_not_pci);
- mp_bus_id_to_pci_bus[m->mpc_busid] = mp_current_pci_id;
- mp_current_pci_id++;
#if defined(CONFIG_EISA) || defined (CONFIG_MCA)
mp_bus_id_to_type[m->mpc_busid] = MP_BUS_PCI;
} else if (strncmp(str, BUSTYPE_EISA, sizeof(BUSTYPE_EISA) - 1) == 0) {
@@ -147,6 +188,7 @@ static void __init MP_bus_info(struct mpc_config_bus *m)
} else
printk(KERN_WARNING "Unknown bustype %s - ignoring\n", str);
}
+#endif
#ifdef CONFIG_X86_IO_APIC
@@ -176,18 +218,89 @@ static void __init MP_ioapic_info(struct mpc_config_ioapic *m)
if (bad_ioapic(m->mpc_apicaddr))
return;
- mp_ioapics[nr_ioapics] = *m;
+ mp_ioapics[nr_ioapics].mp_apicaddr = m->mpc_apicaddr;
+ mp_ioapics[nr_ioapics].mp_apicid = m->mpc_apicid;
+ mp_ioapics[nr_ioapics].mp_type = m->mpc_type;
+ mp_ioapics[nr_ioapics].mp_apicver = m->mpc_apicver;
+ mp_ioapics[nr_ioapics].mp_flags = m->mpc_flags;
nr_ioapics++;
}
-static void __init MP_intsrc_info(struct mpc_config_intsrc *m)
+static void print_MP_intsrc_info(struct mpc_config_intsrc *m)
{
- mp_irqs[mp_irq_entries] = *m;
- Dprintk("Int: type %d, pol %d, trig %d, bus %d,"
+ printk(KERN_CONT "Int: type %d, pol %d, trig %d, bus %02x,"
" IRQ %02x, APIC ID %x, APIC INT %02x\n",
m->mpc_irqtype, m->mpc_irqflag & 3,
(m->mpc_irqflag >> 2) & 3, m->mpc_srcbus,
m->mpc_srcbusirq, m->mpc_dstapic, m->mpc_dstirq);
+}
+
+static void __init print_mp_irq_info(struct mp_config_intsrc *mp_irq)
+{
+ printk(KERN_CONT "Int: type %d, pol %d, trig %d, bus %02x,"
+ " IRQ %02x, APIC ID %x, APIC INT %02x\n",
+ mp_irq->mp_irqtype, mp_irq->mp_irqflag & 3,
+ (mp_irq->mp_irqflag >> 2) & 3, mp_irq->mp_srcbus,
+ mp_irq->mp_srcbusirq, mp_irq->mp_dstapic, mp_irq->mp_dstirq);
+}
+
+static void assign_to_mp_irq(struct mpc_config_intsrc *m,
+ struct mp_config_intsrc *mp_irq)
+{
+ mp_irq->mp_dstapic = m->mpc_dstapic;
+ mp_irq->mp_type = m->mpc_type;
+ mp_irq->mp_irqtype = m->mpc_irqtype;
+ mp_irq->mp_irqflag = m->mpc_irqflag;
+ mp_irq->mp_srcbus = m->mpc_srcbus;
+ mp_irq->mp_srcbusirq = m->mpc_srcbusirq;
+ mp_irq->mp_dstirq = m->mpc_dstirq;
+}
+
+static void __init assign_to_mpc_intsrc(struct mp_config_intsrc *mp_irq,
+ struct mpc_config_intsrc *m)
+{
+ m->mpc_dstapic = mp_irq->mp_dstapic;
+ m->mpc_type = mp_irq->mp_type;
+ m->mpc_irqtype = mp_irq->mp_irqtype;
+ m->mpc_irqflag = mp_irq->mp_irqflag;
+ m->mpc_srcbus = mp_irq->mp_srcbus;
+ m->mpc_srcbusirq = mp_irq->mp_srcbusirq;
+ m->mpc_dstirq = mp_irq->mp_dstirq;
+}
+
+static int mp_irq_mpc_intsrc_cmp(struct mp_config_intsrc *mp_irq,
+ struct mpc_config_intsrc *m)
+{
+ if (mp_irq->mp_dstapic != m->mpc_dstapic)
+ return 1;
+ if (mp_irq->mp_type != m->mpc_type)
+ return 2;
+ if (mp_irq->mp_irqtype != m->mpc_irqtype)
+ return 3;
+ if (mp_irq->mp_irqflag != m->mpc_irqflag)
+ return 4;
+ if (mp_irq->mp_srcbus != m->mpc_srcbus)
+ return 5;
+ if (mp_irq->mp_srcbusirq != m->mpc_srcbusirq)
+ return 6;
+ if (mp_irq->mp_dstirq != m->mpc_dstirq)
+ return 7;
+
+ return 0;
+}
+
+void MP_intsrc_info(struct mpc_config_intsrc *m)
+{
+ int i;
+
+ print_MP_intsrc_info(m);
+
+ for (i = 0; i < mp_irq_entries; i++) {
+ if (!mp_irq_mpc_intsrc_cmp(&mp_irqs[i], m))
+ return;
+ }
+
+ assign_to_mp_irq(m, &mp_irqs[mp_irq_entries]);
if (++mp_irq_entries == MAX_IRQ_SOURCES)
panic("Max # of irq sources exceeded!!\n");
}
@@ -196,7 +309,7 @@ static void __init MP_intsrc_info(struct mpc_config_intsrc *m)
static void __init MP_lintsrc_info(struct mpc_config_lintsrc *m)
{
- Dprintk("Lint: type %d, pol %d, trig %d, bus %d,"
+ printk(KERN_INFO "Lint: type %d, pol %d, trig %d, bus %02x,"
" IRQ %02x, APIC ID %x, APIC LINT %02x\n",
m->mpc_irqtype, m->mpc_irqflag & 3,
(m->mpc_irqflag >> 2) & 3, m->mpc_srcbusid,
@@ -266,11 +379,14 @@ static void __init smp_read_mpc_oem(struct mp_config_oemtable *oemtable,
}
}
-static inline void mps_oem_check(struct mp_config_table *mpc, char *oem,
+void numaq_mps_oem_check(struct mp_config_table *mpc, char *oem,
char *productid)
{
if (strncmp(oem, "IBM NUMA", 8))
- printk("Warning! May not be a NUMA-Q system!\n");
+ printk("Warning! Not a NUMA-Q system!\n");
+ else
+ found_numaq = 1;
+
if (mpc->mpc_oemptr)
smp_read_mpc_oem((struct mp_config_oemtable *)mpc->mpc_oemptr,
mpc->mpc_oemsize);
@@ -281,12 +397,9 @@ static inline void mps_oem_check(struct mp_config_table *mpc, char *oem,
* Read/parse the MPC
*/
-static int __init smp_read_mpc(struct mp_config_table *mpc, unsigned early)
+static int __init smp_check_mpc(struct mp_config_table *mpc, char *oem,
+ char *str)
{
- char str[16];
- char oem[10];
- int count = sizeof(*mpc);
- unsigned char *mpt = ((unsigned char *)mpc) + count;
if (memcmp(mpc->mpc_signature, MPC_SIGNATURE, 4)) {
printk(KERN_ERR "MPTABLE: bad signature [%c%c%c%c]!\n",
@@ -309,19 +422,42 @@ static int __init smp_read_mpc(struct mp_config_table *mpc, unsigned early)
}
memcpy(oem, mpc->mpc_oem, 8);
oem[8] = 0;
- printk(KERN_INFO "MPTABLE: OEM ID: %s ", oem);
+ printk(KERN_INFO "MPTABLE: OEM ID: %s\n", oem);
memcpy(str, mpc->mpc_productid, 12);
str[12] = 0;
- printk("Product ID: %s ", str);
-#ifdef CONFIG_X86_32
- mps_oem_check(mpc, oem, str);
-#endif
- printk(KERN_INFO "MPTABLE: Product ID: %s ", str);
+ printk(KERN_INFO "MPTABLE: Product ID: %s\n", str);
printk(KERN_INFO "MPTABLE: APIC at: 0x%X\n", mpc->mpc_lapic);
+ return 1;
+}
+
+static int __init smp_read_mpc(struct mp_config_table *mpc, unsigned early)
+{
+ char str[16];
+ char oem[10];
+
+ int count = sizeof(*mpc);
+ unsigned char *mpt = ((unsigned char *)mpc) + count;
+
+ if (!smp_check_mpc(mpc, oem, str))
+ return 0;
+
+#ifdef CONFIG_X86_32
+ /*
+ * need to make sure summit and es7000's mps_oem_check is safe to be
+ * called early via genericarch 's mps_oem_check
+ */
+ if (early) {
+#ifdef CONFIG_X86_NUMAQ
+ numaq_mps_oem_check(mpc, oem, str);
+#endif
+ } else
+ mps_oem_check(mpc, oem, str);
+#endif
+
/* save the local APIC address, it might be non-default */
if (!acpi_lapic)
mp_lapic_addr = mpc->mpc_lapic;
@@ -352,7 +488,9 @@ static int __init smp_read_mpc(struct mp_config_table *mpc, unsigned early)
{
struct mpc_config_bus *m =
(struct mpc_config_bus *)mpt;
+#ifdef CONFIG_X86_IO_APIC
MP_bus_info(m);
+#endif
mpt += sizeof(*m);
count += sizeof(*m);
break;
@@ -402,6 +540,11 @@ static int __init smp_read_mpc(struct mp_config_table *mpc, unsigned early)
++mpc_record;
#endif
}
+
+#ifdef CONFIG_X86_GENERICARCH
+ generic_bigsmp_probe();
+#endif
+
setup_apic_routing();
if (!num_processors)
printk(KERN_ERR "MPTABLE: no processors registered!\n");
@@ -427,7 +570,7 @@ static void __init construct_default_ioirq_mptable(int mpc_default_type)
intsrc.mpc_type = MP_INTSRC;
intsrc.mpc_irqflag = 0; /* conforming */
intsrc.mpc_srcbus = 0;
- intsrc.mpc_dstapic = mp_ioapics[0].mpc_apicid;
+ intsrc.mpc_dstapic = mp_ioapics[0].mp_apicid;
intsrc.mpc_irqtype = mp_INT;
@@ -488,40 +631,11 @@ static void __init construct_default_ioirq_mptable(int mpc_default_type)
MP_intsrc_info(&intsrc);
}
-#endif
-static inline void __init construct_default_ISA_mptable(int mpc_default_type)
+static void construct_ioapic_table(int mpc_default_type)
{
- struct mpc_config_processor processor;
- struct mpc_config_bus bus;
-#ifdef CONFIG_X86_IO_APIC
struct mpc_config_ioapic ioapic;
-#endif
- struct mpc_config_lintsrc lintsrc;
- int linttypes[2] = { mp_ExtINT, mp_NMI };
- int i;
-
- /*
- * local APIC has default address
- */
- mp_lapic_addr = APIC_DEFAULT_PHYS_BASE;
-
- /*
- * 2 CPUs, numbered 0 & 1.
- */
- processor.mpc_type = MP_PROCESSOR;
- /* Either an integrated APIC or a discrete 82489DX. */
- processor.mpc_apicver = mpc_default_type > 4 ? 0x10 : 0x01;
- processor.mpc_cpuflag = CPU_ENABLED;
- processor.mpc_cpufeature = (boot_cpu_data.x86 << 8) |
- (boot_cpu_data.x86_model << 4) | boot_cpu_data.x86_mask;
- processor.mpc_featureflag = boot_cpu_data.x86_capability[0];
- processor.mpc_reserved[0] = 0;
- processor.mpc_reserved[1] = 0;
- for (i = 0; i < 2; i++) {
- processor.mpc_apicid = i;
- MP_processor_info(&processor);
- }
+ struct mpc_config_bus bus;
bus.mpc_type = MP_BUS;
bus.mpc_busid = 0;
@@ -550,7 +664,6 @@ static inline void __init construct_default_ISA_mptable(int mpc_default_type)
MP_bus_info(&bus);
}
-#ifdef CONFIG_X86_IO_APIC
ioapic.mpc_type = MP_IOAPIC;
ioapic.mpc_apicid = 2;
ioapic.mpc_apicver = mpc_default_type > 4 ? 0x10 : 0x01;
@@ -562,7 +675,42 @@ static inline void __init construct_default_ISA_mptable(int mpc_default_type)
* We set up most of the low 16 IO-APIC pins according to MPS rules.
*/
construct_default_ioirq_mptable(mpc_default_type);
+}
+#else
+static inline void construct_ioapic_table(int mpc_default_type) { }
#endif
+
+static inline void __init construct_default_ISA_mptable(int mpc_default_type)
+{
+ struct mpc_config_processor processor;
+ struct mpc_config_lintsrc lintsrc;
+ int linttypes[2] = { mp_ExtINT, mp_NMI };
+ int i;
+
+ /*
+ * local APIC has default address
+ */
+ mp_lapic_addr = APIC_DEFAULT_PHYS_BASE;
+
+ /*
+ * 2 CPUs, numbered 0 & 1.
+ */
+ processor.mpc_type = MP_PROCESSOR;
+ /* Either an integrated APIC or a discrete 82489DX. */
+ processor.mpc_apicver = mpc_default_type > 4 ? 0x10 : 0x01;
+ processor.mpc_cpuflag = CPU_ENABLED;
+ processor.mpc_cpufeature = (boot_cpu_data.x86 << 8) |
+ (boot_cpu_data.x86_model << 4) | boot_cpu_data.x86_mask;
+ processor.mpc_featureflag = boot_cpu_data.x86_capability[0];
+ processor.mpc_reserved[0] = 0;
+ processor.mpc_reserved[1] = 0;
+ for (i = 0; i < 2; i++) {
+ processor.mpc_apicid = i;
+ MP_processor_info(&processor);
+ }
+
+ construct_ioapic_table(mpc_default_type);
+
lintsrc.mpc_type = MP_LINTSRC;
lintsrc.mpc_irqflag = 0; /* conforming */
lintsrc.mpc_srcbusid = 0;
@@ -600,7 +748,7 @@ static void __init __get_smp_config(unsigned early)
printk(KERN_INFO "Intel MultiProcessor Specification v1.%d\n",
mpf->mpf_specification);
-#ifdef CONFIG_X86_32
+#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_X86_32)
if (mpf->mpf_feature2 & (1 << 7)) {
printk(KERN_INFO " IMCR and PIC compatibility mode.\n");
pic_mode = 1;
@@ -632,7 +780,9 @@ static void __init __get_smp_config(unsigned early)
* override the defaults.
*/
if (!smp_read_mpc(phys_to_virt(mpf->mpf_physptr), early)) {
+#ifdef CONFIG_X86_LOCAL_APIC
smp_found_config = 0;
+#endif
printk(KERN_ERR
"BIOS bug, MP table errors detected!...\n");
printk(KERN_ERR "... disabling SMP support. "
@@ -689,7 +839,7 @@ static int __init smp_scan_config(unsigned long base, unsigned long length,
unsigned int *bp = phys_to_virt(base);
struct intel_mp_floating *mpf;
- Dprintk("Scan SMP from %p for %ld bytes.\n", bp, length);
+ printk(KERN_DEBUG "Scan SMP from %p for %ld bytes.\n", bp, length);
BUILD_BUG_ON(sizeof(*mpf) != 16);
while (length > 0) {
@@ -699,8 +849,9 @@ static int __init smp_scan_config(unsigned long base, unsigned long length,
!mpf_checksum((unsigned char *)bp, 16) &&
((mpf->mpf_specification == 1)
|| (mpf->mpf_specification == 4))) {
-
+#ifdef CONFIG_X86_LOCAL_APIC
smp_found_config = 1;
+#endif
mpf_found = mpf;
#ifdef CONFIG_X86_32
printk(KERN_INFO "found SMP MP-table at [%p] %08lx\n",
@@ -729,10 +880,11 @@ static int __init smp_scan_config(unsigned long base, unsigned long length,
if (!reserve)
return 1;
- reserve_bootmem_generic(virt_to_phys(mpf), PAGE_SIZE);
+ reserve_bootmem_generic(virt_to_phys(mpf), PAGE_SIZE,
+ BOOTMEM_DEFAULT);
if (mpf->mpf_physptr)
reserve_bootmem_generic(mpf->mpf_physptr,
- PAGE_SIZE);
+ PAGE_SIZE, BOOTMEM_DEFAULT);
#endif
return 1;
}
@@ -790,298 +942,294 @@ void __init find_smp_config(void)
__find_smp_config(1);
}
-/* --------------------------------------------------------------------------
- ACPI-based MP Configuration
- -------------------------------------------------------------------------- */
+#ifdef CONFIG_X86_IO_APIC
+static u8 __initdata irq_used[MAX_IRQ_SOURCES];
-/*
- * Keep this outside and initialized to 0, for !CONFIG_ACPI builds:
- */
-int es7000_plat;
+static int __init get_MP_intsrc_index(struct mpc_config_intsrc *m)
+{
+ int i;
-#ifdef CONFIG_ACPI
+ if (m->mpc_irqtype != mp_INT)
+ return 0;
-#ifdef CONFIG_X86_IO_APIC
+ if (m->mpc_irqflag != 0x0f)
+ return 0;
-#define MP_ISA_BUS 0
+ /* not legacy */
-extern struct mp_ioapic_routing mp_ioapic_routing[MAX_IO_APICS];
+ for (i = 0; i < mp_irq_entries; i++) {
+ if (mp_irqs[i].mp_irqtype != mp_INT)
+ continue;
-static int mp_find_ioapic(int gsi)
-{
- int i = 0;
+ if (mp_irqs[i].mp_irqflag != 0x0f)
+ continue;
- /* Find the IOAPIC that manages this GSI. */
- for (i = 0; i < nr_ioapics; i++) {
- if ((gsi >= mp_ioapic_routing[i].gsi_base)
- && (gsi <= mp_ioapic_routing[i].gsi_end))
- return i;
+ if (mp_irqs[i].mp_srcbus != m->mpc_srcbus)
+ continue;
+ if (mp_irqs[i].mp_srcbusirq != m->mpc_srcbusirq)
+ continue;
+ if (irq_used[i]) {
+ /* already claimed */
+ return -2;
+ }
+ irq_used[i] = 1;
+ return i;
}
- printk(KERN_ERR "ERROR: Unable to locate IOAPIC for GSI %d\n", gsi);
+ /* not found */
return -1;
}
-static u8 __init uniq_ioapic_id(u8 id)
-{
-#ifdef CONFIG_X86_32
- if ((boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) &&
- !APIC_XAPIC(apic_version[boot_cpu_physical_apicid]))
- return io_apic_get_unique_id(nr_ioapics, id);
- else
- return id;
-#else
- int i;
- DECLARE_BITMAP(used, 256);
- bitmap_zero(used, 256);
- for (i = 0; i < nr_ioapics; i++) {
- struct mpc_config_ioapic *ia = &mp_ioapics[i];
- __set_bit(ia->mpc_apicid, used);
- }
- if (!test_bit(id, used))
- return id;
- return find_first_zero_bit(used, 256);
+#define SPARE_SLOT_NUM 20
+
+static struct mpc_config_intsrc __initdata *m_spare[SPARE_SLOT_NUM];
#endif
-}
-void __init mp_register_ioapic(int id, u32 address, u32 gsi_base)
+static int __init replace_intsrc_all(struct mp_config_table *mpc,
+ unsigned long mpc_new_phys,
+ unsigned long mpc_new_length)
{
- int idx = 0;
-
- if (bad_ioapic(address))
- return;
+#ifdef CONFIG_X86_IO_APIC
+ int i;
+ int nr_m_spare = 0;
+#endif
- idx = nr_ioapics;
+ int count = sizeof(*mpc);
+ unsigned char *mpt = ((unsigned char *)mpc) + count;
- mp_ioapics[idx].mpc_type = MP_IOAPIC;
- mp_ioapics[idx].mpc_flags = MPC_APIC_USABLE;
- mp_ioapics[idx].mpc_apicaddr = address;
+ printk(KERN_INFO "mpc_length %x\n", mpc->mpc_length);
+ while (count < mpc->mpc_length) {
+ switch (*mpt) {
+ case MP_PROCESSOR:
+ {
+ struct mpc_config_processor *m =
+ (struct mpc_config_processor *)mpt;
+ mpt += sizeof(*m);
+ count += sizeof(*m);
+ break;
+ }
+ case MP_BUS:
+ {
+ struct mpc_config_bus *m =
+ (struct mpc_config_bus *)mpt;
+ mpt += sizeof(*m);
+ count += sizeof(*m);
+ break;
+ }
+ case MP_IOAPIC:
+ {
+ mpt += sizeof(struct mpc_config_ioapic);
+ count += sizeof(struct mpc_config_ioapic);
+ break;
+ }
+ case MP_INTSRC:
+ {
+#ifdef CONFIG_X86_IO_APIC
+ struct mpc_config_intsrc *m =
+ (struct mpc_config_intsrc *)mpt;
- set_fixmap_nocache(FIX_IO_APIC_BASE_0 + idx, address);
- mp_ioapics[idx].mpc_apicid = uniq_ioapic_id(id);
-#ifdef CONFIG_X86_32
- mp_ioapics[idx].mpc_apicver = io_apic_get_version(idx);
-#else
- mp_ioapics[idx].mpc_apicver = 0;
+ printk(KERN_INFO "OLD ");
+ print_MP_intsrc_info(m);
+ i = get_MP_intsrc_index(m);
+ if (i > 0) {
+ assign_to_mpc_intsrc(&mp_irqs[i], m);
+ printk(KERN_INFO "NEW ");
+ print_mp_irq_info(&mp_irqs[i]);
+ } else if (!i) {
+ /* legacy, do nothing */
+ } else if (nr_m_spare < SPARE_SLOT_NUM) {
+ /*
+ * not found (-1), or duplicated (-2)
+ * are invalid entries,
+ * we need to use the slot later
+ */
+ m_spare[nr_m_spare] = m;
+ nr_m_spare++;
+ }
#endif
- /*
- * Build basic GSI lookup table to facilitate gsi->io_apic lookups
- * and to prevent reprogramming of IOAPIC pins (PCI GSIs).
- */
- mp_ioapic_routing[idx].apic_id = mp_ioapics[idx].mpc_apicid;
- mp_ioapic_routing[idx].gsi_base = gsi_base;
- mp_ioapic_routing[idx].gsi_end = gsi_base +
- io_apic_get_redir_entries(idx);
-
- printk(KERN_INFO "IOAPIC[%d]: apic_id %d, version %d, address 0x%x, "
- "GSI %d-%d\n", idx, mp_ioapics[idx].mpc_apicid,
- mp_ioapics[idx].mpc_apicver, mp_ioapics[idx].mpc_apicaddr,
- mp_ioapic_routing[idx].gsi_base, mp_ioapic_routing[idx].gsi_end);
-
- nr_ioapics++;
-}
+ mpt += sizeof(struct mpc_config_intsrc);
+ count += sizeof(struct mpc_config_intsrc);
+ break;
+ }
+ case MP_LINTSRC:
+ {
+ struct mpc_config_lintsrc *m =
+ (struct mpc_config_lintsrc *)mpt;
+ mpt += sizeof(*m);
+ count += sizeof(*m);
+ break;
+ }
+ default:
+ /* wrong mptable */
+ printk(KERN_ERR "Your mptable is wrong, contact your HW vendor!\n");
+ printk(KERN_ERR "type %x\n", *mpt);
+ print_hex_dump(KERN_ERR, " ", DUMP_PREFIX_ADDRESS, 16,
+ 1, mpc, mpc->mpc_length, 1);
+ goto out;
+ }
+ }
-void __init mp_override_legacy_irq(u8 bus_irq, u8 polarity, u8 trigger, u32 gsi)
-{
- struct mpc_config_intsrc intsrc;
- int ioapic = -1;
- int pin = -1;
+#ifdef CONFIG_X86_IO_APIC
+ for (i = 0; i < mp_irq_entries; i++) {
+ if (irq_used[i])
+ continue;
- /*
- * Convert 'gsi' to 'ioapic.pin'.
- */
- ioapic = mp_find_ioapic(gsi);
- if (ioapic < 0)
- return;
- pin = gsi - mp_ioapic_routing[ioapic].gsi_base;
+ if (mp_irqs[i].mp_irqtype != mp_INT)
+ continue;
- /*
- * TBD: This check is for faulty timer entries, where the override
- * erroneously sets the trigger to level, resulting in a HUGE
- * increase of timer interrupts!
- */
- if ((bus_irq == 0) && (trigger == 3))
- trigger = 1;
+ if (mp_irqs[i].mp_irqflag != 0x0f)
+ continue;
- intsrc.mpc_type = MP_INTSRC;
- intsrc.mpc_irqtype = mp_INT;
- intsrc.mpc_irqflag = (trigger << 2) | polarity;
- intsrc.mpc_srcbus = MP_ISA_BUS;
- intsrc.mpc_srcbusirq = bus_irq; /* IRQ */
- intsrc.mpc_dstapic = mp_ioapics[ioapic].mpc_apicid; /* APIC ID */
- intsrc.mpc_dstirq = pin; /* INTIN# */
+ if (nr_m_spare > 0) {
+ printk(KERN_INFO "*NEW* found ");
+ nr_m_spare--;
+ assign_to_mpc_intsrc(&mp_irqs[i], m_spare[nr_m_spare]);
+ m_spare[nr_m_spare] = NULL;
+ } else {
+ struct mpc_config_intsrc *m =
+ (struct mpc_config_intsrc *)mpt;
+ count += sizeof(struct mpc_config_intsrc);
+ if (!mpc_new_phys) {
+ printk(KERN_INFO "No spare slots, try to append...take your risk, new mpc_length %x\n", count);
+ } else {
+ if (count <= mpc_new_length)
+ printk(KERN_INFO "No spare slots, try to append..., new mpc_length %x\n", count);
+ else {
+ printk(KERN_ERR "mpc_new_length %lx is too small\n", mpc_new_length);
+ goto out;
+ }
+ }
+ assign_to_mpc_intsrc(&mp_irqs[i], m);
+ mpc->mpc_length = count;
+ mpt += sizeof(struct mpc_config_intsrc);
+ }
+ print_mp_irq_info(&mp_irqs[i]);
+ }
+#endif
+out:
+ /* update checksum */
+ mpc->mpc_checksum = 0;
+ mpc->mpc_checksum -= mpf_checksum((unsigned char *)mpc,
+ mpc->mpc_length);
- MP_intsrc_info(&intsrc);
+ return 0;
}
-void __init mp_config_acpi_legacy_irqs(void)
-{
- struct mpc_config_intsrc intsrc;
- int i = 0;
- int ioapic = -1;
+int __initdata enable_update_mptable;
-#if defined (CONFIG_MCA) || defined (CONFIG_EISA)
- /*
- * Fabricate the legacy ISA bus (bus #31).
- */
- mp_bus_id_to_type[MP_ISA_BUS] = MP_BUS_ISA;
-#endif
- set_bit(MP_ISA_BUS, mp_bus_not_pci);
- Dprintk("Bus #%d is ISA\n", MP_ISA_BUS);
+static int __init update_mptable_setup(char *str)
+{
+ enable_update_mptable = 1;
+ return 0;
+}
+early_param("update_mptable", update_mptable_setup);
- /*
- * Older generations of ES7000 have no legacy identity mappings
- */
- if (es7000_plat == 1)
- return;
+static unsigned long __initdata mpc_new_phys;
+static unsigned long mpc_new_length __initdata = 4096;
- /*
- * Locate the IOAPIC that manages the ISA IRQs (0-15).
- */
- ioapic = mp_find_ioapic(0);
- if (ioapic < 0)
- return;
+/* alloc_mptable or alloc_mptable=4k */
+static int __initdata alloc_mptable;
+static int __init parse_alloc_mptable_opt(char *p)
+{
+ enable_update_mptable = 1;
+ alloc_mptable = 1;
+ if (!p)
+ return 0;
+ mpc_new_length = memparse(p, &p);
+ return 0;
+}
+early_param("alloc_mptable", parse_alloc_mptable_opt);
- intsrc.mpc_type = MP_INTSRC;
- intsrc.mpc_irqflag = 0; /* Conforming */
- intsrc.mpc_srcbus = MP_ISA_BUS;
-#ifdef CONFIG_X86_IO_APIC
- intsrc.mpc_dstapic = mp_ioapics[ioapic].mpc_apicid;
+void __init early_reserve_e820_mpc_new(void)
+{
+ if (enable_update_mptable && alloc_mptable) {
+ u64 startt = 0;
+#ifdef CONFIG_X86_TRAMPOLINE
+ startt = TRAMPOLINE_BASE;
#endif
- /*
- * Use the default configuration for the IRQs 0-15. Unless
- * overridden by (MADT) interrupt source override entries.
- */
- for (i = 0; i < 16; i++) {
- int idx;
-
- for (idx = 0; idx < mp_irq_entries; idx++) {
- struct mpc_config_intsrc *irq = mp_irqs + idx;
-
- /* Do we already have a mapping for this ISA IRQ? */
- if (irq->mpc_srcbus == MP_ISA_BUS
- && irq->mpc_srcbusirq == i)
- break;
-
- /* Do we already have a mapping for this IOAPIC pin */
- if ((irq->mpc_dstapic == intsrc.mpc_dstapic) &&
- (irq->mpc_dstirq == i))
- break;
- }
-
- if (idx != mp_irq_entries) {
- printk(KERN_DEBUG "ACPI: IRQ%d used by override.\n", i);
- continue; /* IRQ already used */
- }
-
- intsrc.mpc_irqtype = mp_INT;
- intsrc.mpc_srcbusirq = i; /* Identity mapped */
- intsrc.mpc_dstirq = i;
-
- MP_intsrc_info(&intsrc);
+ mpc_new_phys = early_reserve_e820(startt, mpc_new_length, 4);
}
}
-int mp_register_gsi(u32 gsi, int triggering, int polarity)
+static int __init update_mp_table(void)
{
- int ioapic;
- int ioapic_pin;
-#ifdef CONFIG_X86_32
-#define MAX_GSI_NUM 4096
-#define IRQ_COMPRESSION_START 64
+ char str[16];
+ char oem[10];
+ struct intel_mp_floating *mpf;
+ struct mp_config_table *mpc;
+ struct mp_config_table *mpc_new;
+
+ if (!enable_update_mptable)
+ return 0;
+
+ mpf = mpf_found;
+ if (!mpf)
+ return 0;
- static int pci_irq = IRQ_COMPRESSION_START;
/*
- * Mapping between Global System Interrupts, which
- * represent all possible interrupts, and IRQs
- * assigned to actual devices.
+ * Now see if we need to go further.
*/
- static int gsi_to_irq[MAX_GSI_NUM];
-#else
-
- if (acpi_irq_model != ACPI_IRQ_MODEL_IOAPIC)
- return gsi;
-#endif
+ if (mpf->mpf_feature1 != 0)
+ return 0;
- /* Don't set up the ACPI SCI because it's already set up */
- if (acpi_gbl_FADT.sci_interrupt == gsi)
- return gsi;
+ if (!mpf->mpf_physptr)
+ return 0;
- ioapic = mp_find_ioapic(gsi);
- if (ioapic < 0) {
- printk(KERN_WARNING "No IOAPIC for GSI %u\n", gsi);
- return gsi;
- }
+ mpc = phys_to_virt(mpf->mpf_physptr);
- ioapic_pin = gsi - mp_ioapic_routing[ioapic].gsi_base;
+ if (!smp_check_mpc(mpc, oem, str))
+ return 0;
-#ifdef CONFIG_X86_32
- if (ioapic_renumber_irq)
- gsi = ioapic_renumber_irq(ioapic, gsi);
-#endif
+ printk(KERN_INFO "mpf: %lx\n", virt_to_phys(mpf));
+ printk(KERN_INFO "mpf_physptr: %x\n", mpf->mpf_physptr);
- /*
- * Avoid pin reprogramming. PRTs typically include entries
- * with redundant pin->gsi mappings (but unique PCI devices);
- * we only program the IOAPIC on the first.
- */
- if (ioapic_pin > MP_MAX_IOAPIC_PIN) {
- printk(KERN_ERR "Invalid reference to IOAPIC pin "
- "%d-%d\n", mp_ioapic_routing[ioapic].apic_id,
- ioapic_pin);
- return gsi;
+ if (mpc_new_phys && mpc->mpc_length > mpc_new_length) {
+ mpc_new_phys = 0;
+ printk(KERN_INFO "mpc_new_length is %ld, please use alloc_mptable=8k\n",
+ mpc_new_length);
}
- if (test_bit(ioapic_pin, mp_ioapic_routing[ioapic].pin_programmed)) {
- Dprintk(KERN_DEBUG "Pin %d-%d already programmed\n",
- mp_ioapic_routing[ioapic].apic_id, ioapic_pin);
-#ifdef CONFIG_X86_32
- return (gsi < IRQ_COMPRESSION_START ? gsi : gsi_to_irq[gsi]);
-#else
- return gsi;
-#endif
+
+ if (!mpc_new_phys) {
+ unsigned char old, new;
+ /* check if we can change the postion */
+ mpc->mpc_checksum = 0;
+ old = mpf_checksum((unsigned char *)mpc, mpc->mpc_length);
+ mpc->mpc_checksum = 0xff;
+ new = mpf_checksum((unsigned char *)mpc, mpc->mpc_length);
+ if (old == new) {
+ printk(KERN_INFO "mpc is readonly, please try alloc_mptable instead\n");
+ return 0;
+ }
+ printk(KERN_INFO "use in-positon replacing\n");
+ } else {
+ mpf->mpf_physptr = mpc_new_phys;
+ mpc_new = phys_to_virt(mpc_new_phys);
+ memcpy(mpc_new, mpc, mpc->mpc_length);
+ mpc = mpc_new;
+ /* check if we can modify that */
+ if (mpc_new_phys - mpf->mpf_physptr) {
+ struct intel_mp_floating *mpf_new;
+ /* steal 16 bytes from [0, 1k) */
+ printk(KERN_INFO "mpf new: %x\n", 0x400 - 16);
+ mpf_new = phys_to_virt(0x400 - 16);
+ memcpy(mpf_new, mpf, 16);
+ mpf = mpf_new;
+ mpf->mpf_physptr = mpc_new_phys;
+ }
+ mpf->mpf_checksum = 0;
+ mpf->mpf_checksum -= mpf_checksum((unsigned char *)mpf, 16);
+ printk(KERN_INFO "mpf_physptr new: %x\n", mpf->mpf_physptr);
}
- set_bit(ioapic_pin, mp_ioapic_routing[ioapic].pin_programmed);
-#ifdef CONFIG_X86_32
/*
- * For GSI >= 64, use IRQ compression
+ * only replace the one with mp_INT and
+ * MP_IRQ_TRIGGER_LEVEL|MP_IRQ_POLARITY_LOW,
+ * already in mp_irqs , stored by ... and mp_config_acpi_gsi,
+ * may need pci=routeirq for all coverage
*/
- if ((gsi >= IRQ_COMPRESSION_START)
- && (triggering == ACPI_LEVEL_SENSITIVE)) {
- /*
- * For PCI devices assign IRQs in order, avoiding gaps
- * due to unused I/O APIC pins.
- */
- int irq = gsi;
- if (gsi < MAX_GSI_NUM) {
- /*
- * Retain the VIA chipset work-around (gsi > 15), but
- * avoid a problem where the 8254 timer (IRQ0) is setup
- * via an override (so it's not on pin 0 of the ioapic),
- * and at the same time, the pin 0 interrupt is a PCI
- * type. The gsi > 15 test could cause these two pins
- * to be shared as IRQ0, and they are not shareable.
- * So test for this condition, and if necessary, avoid
- * the pin collision.
- */
- gsi = pci_irq++;
- /*
- * Don't assign IRQ used by ACPI SCI
- */
- if (gsi == acpi_gbl_FADT.sci_interrupt)
- gsi = pci_irq++;
- gsi_to_irq[irq] = gsi;
- } else {
- printk(KERN_ERR "GSI %u is too high\n", gsi);
- return gsi;
- }
- }
-#endif
- io_apic_set_pci_routing(ioapic, ioapic_pin, gsi,
- triggering == ACPI_EDGE_SENSITIVE ? 0 : 1,
- polarity == ACPI_ACTIVE_HIGH ? 0 : 1);
- return gsi;
+ replace_intsrc_all(mpc, mpc_new_phys, mpc_new_length);
+
+ return 0;
}
-#endif /* CONFIG_X86_IO_APIC */
-#endif /* CONFIG_ACPI */
+late_initcall(update_mp_table);
diff --git a/arch/x86/kernel/msr.c b/arch/x86/kernel/msr.c
index 1f3abe048e93..be288ba706bf 100644
--- a/arch/x86/kernel/msr.c
+++ b/arch/x86/kernel/msr.c
@@ -141,8 +141,8 @@ static int __cpuinit msr_device_create(int cpu)
{
struct device *dev;
- dev = device_create(msr_class, NULL, MKDEV(MSR_MAJOR, cpu),
- "msr%d", cpu);
+ dev = device_create_drvdata(msr_class, NULL, MKDEV(MSR_MAJOR, cpu),
+ NULL, "msr%d", cpu);
return IS_ERR(dev) ? PTR_ERR(dev) : 0;
}
diff --git a/arch/x86/kernel/nmi_64.c b/arch/x86/kernel/nmi.c
index 5a29ded994fa..0231845a8e4e 100644
--- a/arch/x86/kernel/nmi_64.c
+++ b/arch/x86/kernel/nmi.c
@@ -6,6 +6,7 @@
* Fixes:
* Mikael Pettersson : AMD K7 support for local APIC NMI watchdog.
* Mikael Pettersson : Power Management for local APIC NMI watchdog.
+ * Mikael Pettersson : Pentium 4 support for local APIC NMI watchdog.
* Pavel Machek and
* Mikael Pettersson : PM converted to driver model. Disable/enable API.
*/
@@ -17,20 +18,25 @@
#include <linux/module.h>
#include <linux/sysdev.h>
#include <linux/sysctl.h>
+#include <linux/percpu.h>
#include <linux/kprobes.h>
#include <linux/cpumask.h>
+#include <linux/kernel_stat.h>
#include <linux/kdebug.h>
+#include <linux/smp.h>
-#include <asm/smp.h>
-#include <asm/nmi.h>
+#include <asm/apic.h>
+#include <asm/i8259.h>
+#include <asm/io_apic.h>
#include <asm/proto.h>
+#include <asm/timer.h>
+
#include <asm/mce.h>
#include <mach_traps.h>
int unknown_nmi_panic;
int nmi_watchdog_enabled;
-int panic_on_unrecovered_nmi;
static cpumask_t backtrace_mask = CPU_MASK_NONE;
@@ -41,12 +47,47 @@ static cpumask_t backtrace_mask = CPU_MASK_NONE;
* 0: the lapic NMI watchdog is disabled, but can be enabled
*/
atomic_t nmi_active = ATOMIC_INIT(0); /* oprofile uses this */
-static int panic_on_timeout;
+EXPORT_SYMBOL(nmi_active);
unsigned int nmi_watchdog = NMI_DEFAULT;
-static unsigned int nmi_hz = HZ;
+EXPORT_SYMBOL(nmi_watchdog);
+
+static int panic_on_timeout;
+static unsigned int nmi_hz = HZ;
static DEFINE_PER_CPU(short, wd_enabled);
+static int endflag __initdata;
+
+static inline unsigned int get_nmi_count(int cpu)
+{
+#ifdef CONFIG_X86_64
+ return cpu_pda(cpu)->__nmi_count;
+#else
+ return nmi_count(cpu);
+#endif
+}
+
+static inline int mce_in_progress(void)
+{
+#if defined(CONFIX_X86_64) && defined(CONFIG_X86_MCE)
+ return atomic_read(&mce_entry) > 0;
+#endif
+ return 0;
+}
+
+/*
+ * Take the local apic timer and PIT/HPET into account. We don't
+ * know which one is active, when we have highres/dyntick on
+ */
+static inline unsigned int get_timer_irqs(int cpu)
+{
+#ifdef CONFIG_X86_64
+ return read_pda(apic_timer_irqs) + read_pda(irq0_irqs);
+#else
+ return per_cpu(irq_stat, cpu).apic_timer_irqs +
+ per_cpu(irq_stat, cpu).irq0_irqs;
+#endif
+}
/* Run after command line and cpu_init init, but before all other checks */
void nmi_watchdog_default(void)
@@ -56,22 +97,23 @@ void nmi_watchdog_default(void)
nmi_watchdog = NMI_NONE;
}
-static int endflag __initdata = 0;
-
#ifdef CONFIG_SMP
-/* The performance counters used by NMI_LOCAL_APIC don't trigger when
+/*
+ * The performance counters used by NMI_LOCAL_APIC don't trigger when
* the CPU is idle. To make sure the NMI watchdog really ticks on all
* CPUs during the test make them busy.
*/
static __init void nmi_cpu_busy(void *data)
{
local_irq_enable_in_hardirq();
- /* Intentionally don't use cpu_relax here. This is
- to make sure that the performance counter really ticks,
- even if there is a simulator or similar that catches the
- pause instruction. On a real HT machine this is fine because
- all other CPUs are busy with "useless" delay loops and don't
- care if they get somewhat less cycles. */
+ /*
+ * Intentionally don't use cpu_relax here. This is
+ * to make sure that the performance counter really ticks,
+ * even if there is a simulator or similar that catches the
+ * pause instruction. On a real HT machine this is fine because
+ * all other CPUs are busy with "useless" delay loops and don't
+ * care if they get somewhat less cycles.
+ */
while (endflag == 0)
mb();
}
@@ -79,18 +121,18 @@ static __init void nmi_cpu_busy(void *data)
int __init check_nmi_watchdog(void)
{
- int *prev_nmi_count;
+ unsigned int *prev_nmi_count;
int cpu;
- if ((nmi_watchdog == NMI_NONE) || (nmi_watchdog == NMI_DISABLED))
+ if (nmi_watchdog == NMI_NONE || nmi_watchdog == NMI_DISABLED)
return 0;
if (!atomic_read(&nmi_active))
return 0;
- prev_nmi_count = kmalloc(NR_CPUS * sizeof(int), GFP_KERNEL);
+ prev_nmi_count = kmalloc(nr_cpu_ids * sizeof(int), GFP_KERNEL);
if (!prev_nmi_count)
- return -1;
+ goto error;
printk(KERN_INFO "Testing NMI watchdog ... ");
@@ -99,20 +141,21 @@ int __init check_nmi_watchdog(void)
smp_call_function(nmi_cpu_busy, (void *)&endflag, 0, 0);
#endif
- for (cpu = 0; cpu < NR_CPUS; cpu++)
- prev_nmi_count[cpu] = cpu_pda(cpu)->__nmi_count;
+ for_each_possible_cpu(cpu)
+ prev_nmi_count[cpu] = get_nmi_count(cpu);
+
local_irq_enable();
- mdelay((20*1000)/nmi_hz); // wait 20 ticks
+ mdelay((20 * 1000) / nmi_hz); /* wait 20 ticks */
for_each_online_cpu(cpu) {
if (!per_cpu(wd_enabled, cpu))
continue;
- if (cpu_pda(cpu)->__nmi_count - prev_nmi_count[cpu] <= 5) {
+ if (get_nmi_count(cpu) - prev_nmi_count[cpu] <= 5) {
printk(KERN_WARNING "WARNING: CPU#%d: NMI "
- "appears to be stuck (%d->%d)!\n",
+ "appears to be stuck (%d->%d)!\n",
cpu,
prev_nmi_count[cpu],
- cpu_pda(cpu)->__nmi_count);
+ get_nmi_count(cpu));
per_cpu(wd_enabled, cpu) = 0;
atomic_dec(&nmi_active);
}
@@ -121,24 +164,31 @@ int __init check_nmi_watchdog(void)
if (!atomic_read(&nmi_active)) {
kfree(prev_nmi_count);
atomic_set(&nmi_active, -1);
- return -1;
+ goto error;
}
printk("OK.\n");
- /* now that we know it works we can reduce NMI frequency to
- something more reasonable; makes a difference in some configs */
+ /*
+ * now that we know it works we can reduce NMI frequency to
+ * something more reasonable; makes a difference in some configs
+ */
if (nmi_watchdog == NMI_LOCAL_APIC)
nmi_hz = lapic_adjust_nmi_hz(1);
kfree(prev_nmi_count);
return 0;
+error:
+ if (nmi_watchdog == NMI_IO_APIC && !timer_through_8259)
+ disable_8259A_irq(0);
+
+ return -1;
}
static int __init setup_nmi_watchdog(char *str)
{
int nmi;
- if (!strncmp(str,"panic",5)) {
+ if (!strncmp(str, "panic", 5)) {
panic_on_timeout = 1;
str = strchr(str, ',');
if (!str)
@@ -148,15 +198,17 @@ static int __init setup_nmi_watchdog(char *str)
get_option(&str, &nmi);
- if ((nmi >= NMI_INVALID) || (nmi < NMI_NONE))
+ if (nmi >= NMI_INVALID || nmi < NMI_NONE)
return 0;
nmi_watchdog = nmi;
return 1;
}
-
__setup("nmi_watchdog=", setup_nmi_watchdog);
+/*
+ * Suspend/resume support
+ */
#ifdef CONFIG_PM
static int nmi_pm_active; /* nmi_active before suspend */
@@ -195,7 +247,8 @@ static int __init init_lapic_nmi_sysfs(void)
{
int error;
- /* should really be a BUG_ON but b/c this is an
+ /*
+ * should really be a BUG_ON but b/c this is an
* init call, it just doesn't work. -dcz
*/
if (nmi_watchdog != NMI_LOCAL_APIC)
@@ -209,6 +262,7 @@ static int __init init_lapic_nmi_sysfs(void)
error = sysdev_register(&device_lapic_nmi);
return error;
}
+
/* must come after the local APIC's device_initcall() */
late_initcall(init_lapic_nmi_sysfs);
@@ -216,7 +270,7 @@ late_initcall(init_lapic_nmi_sysfs);
static void __acpi_nmi_enable(void *__unused)
{
- apic_write(APIC_LVT0, APIC_DM_NMI);
+ apic_write_around(APIC_LVT0, APIC_DM_NMI);
}
/*
@@ -230,7 +284,7 @@ void acpi_nmi_enable(void)
static void __acpi_nmi_disable(void *__unused)
{
- apic_write(APIC_LVT0, APIC_DM_NMI | APIC_LVT_MASKED);
+ apic_write_around(APIC_LVT0, APIC_DM_NMI | APIC_LVT_MASKED);
}
/*
@@ -249,11 +303,12 @@ void setup_apic_nmi_watchdog(void *unused)
/* cheap hack to support suspend/resume */
/* if cpu0 is not active neither should the other cpus */
- if ((smp_processor_id() != 0) && (atomic_read(&nmi_active) <= 0))
+ if (smp_processor_id() != 0 && atomic_read(&nmi_active) <= 0)
return;
switch (nmi_watchdog) {
case NMI_LOCAL_APIC:
+ /* enable it before to avoid race with handler */
__get_cpu_var(wd_enabled) = 1;
if (lapic_watchdog_init(nmi_hz) < 0) {
__get_cpu_var(wd_enabled) = 0;
@@ -269,9 +324,9 @@ void setup_apic_nmi_watchdog(void *unused)
void stop_apic_nmi_watchdog(void *unused)
{
/* only support LOCAL and IO APICs for now */
- if ((nmi_watchdog != NMI_LOCAL_APIC) &&
- (nmi_watchdog != NMI_IO_APIC))
- return;
+ if (nmi_watchdog != NMI_LOCAL_APIC &&
+ nmi_watchdog != NMI_IO_APIC)
+ return;
if (__get_cpu_var(wd_enabled) == 0)
return;
if (nmi_watchdog == NMI_LOCAL_APIC)
@@ -287,6 +342,11 @@ void stop_apic_nmi_watchdog(void *unused)
*
* as these watchdog NMI IRQs are generated on every CPU, we only
* have to check the current processor.
+ *
+ * since NMIs don't listen to _any_ locks, we have to be extremely
+ * careful not to rely on unsafe variables. The printk might lock
+ * up though, so we have to break up any console locks first ...
+ * [when there will be more tty-related locks, break them up here too!]
*/
static DEFINE_PER_CPU(unsigned, last_irq_sum);
@@ -299,7 +359,7 @@ void touch_nmi_watchdog(void)
unsigned cpu;
/*
- * Tell other CPUs to reset their alert counters. We cannot
+ * Tell other CPUs to reset their alert counters. We cannot
* do it ourselves because the alert count increase is not
* atomic.
*/
@@ -309,6 +369,9 @@ void touch_nmi_watchdog(void)
}
}
+ /*
+ * Tickle the softlockup detector too:
+ */
touch_softlockup_watchdog();
}
EXPORT_SYMBOL(touch_nmi_watchdog);
@@ -316,7 +379,12 @@ EXPORT_SYMBOL(touch_nmi_watchdog);
notrace __kprobes int
nmi_watchdog_tick(struct pt_regs *regs, unsigned reason)
{
- int sum;
+ /*
+ * Since current_thread_info()-> is always on the stack, and we
+ * always switch the stack NMI-atomically, it's safe to use
+ * smp_processor_id().
+ */
+ unsigned int sum;
int touched = 0;
int cpu = smp_processor_id();
int rc = 0;
@@ -328,7 +396,8 @@ nmi_watchdog_tick(struct pt_regs *regs, unsigned reason)
touched = 1;
}
- sum = read_pda(apic_timer_irqs) + read_pda(irq0_irqs);
+ sum = get_timer_irqs(cpu);
+
if (__get_cpu_var(nmi_touch)) {
__get_cpu_var(nmi_touch) = 0;
touched = 1;
@@ -338,28 +407,29 @@ nmi_watchdog_tick(struct pt_regs *regs, unsigned reason)
static DEFINE_SPINLOCK(lock); /* Serialise the printks */
spin_lock(&lock);
- printk("NMI backtrace for cpu %d\n", cpu);
+ printk(KERN_WARNING "NMI backtrace for cpu %d\n", cpu);
dump_stack();
spin_unlock(&lock);
cpu_clear(cpu, backtrace_mask);
}
-#ifdef CONFIG_X86_MCE
- /* Could check oops_in_progress here too, but it's safer
- not too */
- if (atomic_read(&mce_entry) > 0)
+ /* Could check oops_in_progress here too, but it's safer not to */
+ if (mce_in_progress())
touched = 1;
-#endif
- /* if the apic timer isn't firing, this cpu isn't doing much */
+
+ /* if the none of the timers isn't firing, this cpu isn't doing much */
if (!touched && __get_cpu_var(last_irq_sum) == sum) {
/*
* Ayiee, looks like this CPU is stuck ...
* wait a few IRQs (5 seconds) before doing the oops ...
*/
local_inc(&__get_cpu_var(alert_counter));
- if (local_read(&__get_cpu_var(alert_counter)) == 5*nmi_hz)
- die_nmi("NMI Watchdog detected LOCKUP on CPU %d\n", regs,
- panic_on_timeout);
+ if (local_read(&__get_cpu_var(alert_counter)) == 5 * nmi_hz)
+ /*
+ * die_nmi will return ONLY if NOTIFY_STOP happens..
+ */
+ die_nmi("BUG: NMI Watchdog detected LOCKUP",
+ regs, panic_on_timeout);
} else {
__get_cpu_var(last_irq_sum) = sum;
local_set(&__get_cpu_var(alert_counter), 0);
@@ -373,7 +443,8 @@ nmi_watchdog_tick(struct pt_regs *regs, unsigned reason)
rc |= lapic_wd_event(nmi_hz);
break;
case NMI_IO_APIC:
- /* don't know how to accurately check for this.
+ /*
+ * don't know how to accurately check for this.
* just assume it was a watchdog timer interrupt
* This matches the old behaviour.
*/
@@ -383,30 +454,6 @@ nmi_watchdog_tick(struct pt_regs *regs, unsigned reason)
return rc;
}
-static unsigned ignore_nmis;
-
-asmlinkage notrace __kprobes void
-do_nmi(struct pt_regs *regs, long error_code)
-{
- nmi_enter();
- add_pda(__nmi_count,1);
- if (!ignore_nmis)
- default_do_nmi(regs);
- nmi_exit();
-}
-
-void stop_nmi(void)
-{
- acpi_nmi_disable();
- ignore_nmis++;
-}
-
-void restart_nmi(void)
-{
- ignore_nmis--;
- acpi_nmi_enable();
-}
-
#ifdef CONFIG_SYSCTL
static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu)
@@ -415,7 +462,7 @@ static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu)
char buf[64];
sprintf(buf, "NMI received for unknown reason %02x\n", reason);
- die_nmi(buf, regs, 1); /* Always panic here */
+ die_nmi(buf, regs, 1); /* Always panic here */
return 0;
}
@@ -434,27 +481,37 @@ int proc_nmi_enabled(struct ctl_table *table, int write, struct file *file,
return 0;
if (atomic_read(&nmi_active) < 0 || nmi_watchdog == NMI_DISABLED) {
- printk( KERN_WARNING "NMI watchdog is permanently disabled\n");
+ printk(KERN_WARNING
+ "NMI watchdog is permanently disabled\n");
return -EIO;
}
/* if nmi_watchdog is not set yet, then set it */
nmi_watchdog_default();
+#ifdef CONFIG_X86_32
+ if (nmi_watchdog == NMI_NONE) {
+ if (lapic_watchdog_ok())
+ nmi_watchdog = NMI_LOCAL_APIC;
+ else
+ nmi_watchdog = NMI_IO_APIC;
+ }
+#endif
+
if (nmi_watchdog == NMI_LOCAL_APIC) {
if (nmi_watchdog_enabled)
enable_lapic_nmi_watchdog();
else
disable_lapic_nmi_watchdog();
} else {
- printk( KERN_WARNING
+ printk(KERN_WARNING
"NMI watchdog doesn't know what hardware to touch\n");
return -EIO;
}
return 0;
}
-#endif
+#endif /* CONFIG_SYSCTL */
int do_nmi_callback(struct pt_regs *regs, int cpu)
{
@@ -477,6 +534,3 @@ void __trigger_all_cpu_backtrace(void)
mdelay(1);
}
}
-
-EXPORT_SYMBOL(nmi_active);
-EXPORT_SYMBOL(nmi_watchdog);
diff --git a/arch/x86/kernel/nmi_32.c b/arch/x86/kernel/nmi_32.c
deleted file mode 100644
index 11b14bbaa61e..000000000000
--- a/arch/x86/kernel/nmi_32.c
+++ /dev/null
@@ -1,472 +0,0 @@
-/*
- * NMI watchdog support on APIC systems
- *
- * Started by Ingo Molnar <mingo@redhat.com>
- *
- * Fixes:
- * Mikael Pettersson : AMD K7 support for local APIC NMI watchdog.
- * Mikael Pettersson : Power Management for local APIC NMI watchdog.
- * Mikael Pettersson : Pentium 4 support for local APIC NMI watchdog.
- * Pavel Machek and
- * Mikael Pettersson : PM converted to driver model. Disable/enable API.
- */
-
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/nmi.h>
-#include <linux/sysdev.h>
-#include <linux/sysctl.h>
-#include <linux/percpu.h>
-#include <linux/kprobes.h>
-#include <linux/cpumask.h>
-#include <linux/kernel_stat.h>
-#include <linux/kdebug.h>
-#include <linux/slab.h>
-
-#include <asm/smp.h>
-#include <asm/nmi.h>
-#include <asm/timer.h>
-
-#include "mach_traps.h"
-
-int unknown_nmi_panic;
-int nmi_watchdog_enabled;
-
-static cpumask_t backtrace_mask = CPU_MASK_NONE;
-
-/* nmi_active:
- * >0: the lapic NMI watchdog is active, but can be disabled
- * <0: the lapic NMI watchdog has not been set up, and cannot
- * be enabled
- * 0: the lapic NMI watchdog is disabled, but can be enabled
- */
-atomic_t nmi_active = ATOMIC_INIT(0); /* oprofile uses this */
-
-unsigned int nmi_watchdog = NMI_DEFAULT;
-static unsigned int nmi_hz = HZ;
-
-static DEFINE_PER_CPU(short, wd_enabled);
-
-static int endflag __initdata = 0;
-
-#ifdef CONFIG_SMP
-/* The performance counters used by NMI_LOCAL_APIC don't trigger when
- * the CPU is idle. To make sure the NMI watchdog really ticks on all
- * CPUs during the test make them busy.
- */
-static __init void nmi_cpu_busy(void *data)
-{
- local_irq_enable_in_hardirq();
- /* Intentionally don't use cpu_relax here. This is
- to make sure that the performance counter really ticks,
- even if there is a simulator or similar that catches the
- pause instruction. On a real HT machine this is fine because
- all other CPUs are busy with "useless" delay loops and don't
- care if they get somewhat less cycles. */
- while (endflag == 0)
- mb();
-}
-#endif
-
-int __init check_nmi_watchdog(void)
-{
- unsigned int *prev_nmi_count;
- int cpu;
-
- if ((nmi_watchdog == NMI_NONE) || (nmi_watchdog == NMI_DISABLED))
- return 0;
-
- if (!atomic_read(&nmi_active))
- return 0;
-
- prev_nmi_count = kmalloc(NR_CPUS * sizeof(int), GFP_KERNEL);
- if (!prev_nmi_count)
- goto error;
-
- printk(KERN_INFO "Testing NMI watchdog ... ");
-
-#ifdef CONFIG_SMP
- if (nmi_watchdog == NMI_LOCAL_APIC)
- smp_call_function(nmi_cpu_busy, (void *)&endflag, 0, 0);
-#endif
-
- for_each_possible_cpu(cpu)
- prev_nmi_count[cpu] = nmi_count(cpu);
- local_irq_enable();
- mdelay((20*1000)/nmi_hz); // wait 20 ticks
-
- for_each_possible_cpu(cpu) {
-#ifdef CONFIG_SMP
- /* Check cpu_callin_map here because that is set
- after the timer is started. */
- if (!cpu_isset(cpu, cpu_callin_map))
- continue;
-#endif
- if (!per_cpu(wd_enabled, cpu))
- continue;
- if (nmi_count(cpu) - prev_nmi_count[cpu] <= 5) {
- printk(KERN_WARNING "WARNING: CPU#%d: NMI "
- "appears to be stuck (%d->%d)!\n",
- cpu,
- prev_nmi_count[cpu],
- nmi_count(cpu));
- per_cpu(wd_enabled, cpu) = 0;
- atomic_dec(&nmi_active);
- }
- }
- endflag = 1;
- if (!atomic_read(&nmi_active)) {
- kfree(prev_nmi_count);
- atomic_set(&nmi_active, -1);
- goto error;
- }
- printk("OK.\n");
-
- /* now that we know it works we can reduce NMI frequency to
- something more reasonable; makes a difference in some configs */
- if (nmi_watchdog == NMI_LOCAL_APIC)
- nmi_hz = lapic_adjust_nmi_hz(1);
-
- kfree(prev_nmi_count);
- return 0;
-error:
- timer_ack = !cpu_has_tsc;
-
- return -1;
-}
-
-static int __init setup_nmi_watchdog(char *str)
-{
- int nmi;
-
- get_option(&str, &nmi);
-
- if ((nmi >= NMI_INVALID) || (nmi < NMI_NONE))
- return 0;
-
- nmi_watchdog = nmi;
- return 1;
-}
-
-__setup("nmi_watchdog=", setup_nmi_watchdog);
-
-
-/* Suspend/resume support */
-
-#ifdef CONFIG_PM
-
-static int nmi_pm_active; /* nmi_active before suspend */
-
-static int lapic_nmi_suspend(struct sys_device *dev, pm_message_t state)
-{
- /* only CPU0 goes here, other CPUs should be offline */
- nmi_pm_active = atomic_read(&nmi_active);
- stop_apic_nmi_watchdog(NULL);
- BUG_ON(atomic_read(&nmi_active) != 0);
- return 0;
-}
-
-static int lapic_nmi_resume(struct sys_device *dev)
-{
- /* only CPU0 goes here, other CPUs should be offline */
- if (nmi_pm_active > 0) {
- setup_apic_nmi_watchdog(NULL);
- touch_nmi_watchdog();
- }
- return 0;
-}
-
-
-static struct sysdev_class nmi_sysclass = {
- .name = "lapic_nmi",
- .resume = lapic_nmi_resume,
- .suspend = lapic_nmi_suspend,
-};
-
-static struct sys_device device_lapic_nmi = {
- .id = 0,
- .cls = &nmi_sysclass,
-};
-
-static int __init init_lapic_nmi_sysfs(void)
-{
- int error;
-
- /* should really be a BUG_ON but b/c this is an
- * init call, it just doesn't work. -dcz
- */
- if (nmi_watchdog != NMI_LOCAL_APIC)
- return 0;
-
- if (atomic_read(&nmi_active) < 0)
- return 0;
-
- error = sysdev_class_register(&nmi_sysclass);
- if (!error)
- error = sysdev_register(&device_lapic_nmi);
- return error;
-}
-/* must come after the local APIC's device_initcall() */
-late_initcall(init_lapic_nmi_sysfs);
-
-#endif /* CONFIG_PM */
-
-static void __acpi_nmi_enable(void *__unused)
-{
- apic_write_around(APIC_LVT0, APIC_DM_NMI);
-}
-
-/*
- * Enable timer based NMIs on all CPUs:
- */
-void acpi_nmi_enable(void)
-{
- if (atomic_read(&nmi_active) && nmi_watchdog == NMI_IO_APIC)
- on_each_cpu(__acpi_nmi_enable, NULL, 0, 1);
-}
-
-static void __acpi_nmi_disable(void *__unused)
-{
- apic_write(APIC_LVT0, APIC_DM_NMI | APIC_LVT_MASKED);
-}
-
-/*
- * Disable timer based NMIs on all CPUs:
- */
-void acpi_nmi_disable(void)
-{
- if (atomic_read(&nmi_active) && nmi_watchdog == NMI_IO_APIC)
- on_each_cpu(__acpi_nmi_disable, NULL, 0, 1);
-}
-
-void setup_apic_nmi_watchdog(void *unused)
-{
- if (__get_cpu_var(wd_enabled))
- return;
-
- /* cheap hack to support suspend/resume */
- /* if cpu0 is not active neither should the other cpus */
- if ((smp_processor_id() != 0) && (atomic_read(&nmi_active) <= 0))
- return;
-
- switch (nmi_watchdog) {
- case NMI_LOCAL_APIC:
- __get_cpu_var(wd_enabled) = 1; /* enable it before to avoid race with handler */
- if (lapic_watchdog_init(nmi_hz) < 0) {
- __get_cpu_var(wd_enabled) = 0;
- return;
- }
- /* FALL THROUGH */
- case NMI_IO_APIC:
- __get_cpu_var(wd_enabled) = 1;
- atomic_inc(&nmi_active);
- }
-}
-
-void stop_apic_nmi_watchdog(void *unused)
-{
- /* only support LOCAL and IO APICs for now */
- if ((nmi_watchdog != NMI_LOCAL_APIC) &&
- (nmi_watchdog != NMI_IO_APIC))
- return;
- if (__get_cpu_var(wd_enabled) == 0)
- return;
- if (nmi_watchdog == NMI_LOCAL_APIC)
- lapic_watchdog_stop();
- __get_cpu_var(wd_enabled) = 0;
- atomic_dec(&nmi_active);
-}
-
-/*
- * the best way to detect whether a CPU has a 'hard lockup' problem
- * is to check it's local APIC timer IRQ counts. If they are not
- * changing then that CPU has some problem.
- *
- * as these watchdog NMI IRQs are generated on every CPU, we only
- * have to check the current processor.
- *
- * since NMIs don't listen to _any_ locks, we have to be extremely
- * careful not to rely on unsafe variables. The printk might lock
- * up though, so we have to break up any console locks first ...
- * [when there will be more tty-related locks, break them up
- * here too!]
- */
-
-static unsigned int
- last_irq_sums [NR_CPUS],
- alert_counter [NR_CPUS];
-
-void touch_nmi_watchdog(void)
-{
- if (nmi_watchdog > 0) {
- unsigned cpu;
-
- /*
- * Just reset the alert counters, (other CPUs might be
- * spinning on locks we hold):
- */
- for_each_present_cpu(cpu) {
- if (alert_counter[cpu])
- alert_counter[cpu] = 0;
- }
- }
-
- /*
- * Tickle the softlockup detector too:
- */
- touch_softlockup_watchdog();
-}
-EXPORT_SYMBOL(touch_nmi_watchdog);
-
-extern void die_nmi(struct pt_regs *, const char *msg);
-
-notrace __kprobes int
-nmi_watchdog_tick(struct pt_regs *regs, unsigned reason)
-{
-
- /*
- * Since current_thread_info()-> is always on the stack, and we
- * always switch the stack NMI-atomically, it's safe to use
- * smp_processor_id().
- */
- unsigned int sum;
- int touched = 0;
- int cpu = smp_processor_id();
- int rc = 0;
-
- /* check for other users first */
- if (notify_die(DIE_NMI, "nmi", regs, reason, 2, SIGINT)
- == NOTIFY_STOP) {
- rc = 1;
- touched = 1;
- }
-
- if (cpu_isset(cpu, backtrace_mask)) {
- static DEFINE_SPINLOCK(lock); /* Serialise the printks */
-
- spin_lock(&lock);
- printk("NMI backtrace for cpu %d\n", cpu);
- dump_stack();
- spin_unlock(&lock);
- cpu_clear(cpu, backtrace_mask);
- }
-
- /*
- * Take the local apic timer and PIT/HPET into account. We don't
- * know which one is active, when we have highres/dyntick on
- */
- sum = per_cpu(irq_stat, cpu).apic_timer_irqs +
- per_cpu(irq_stat, cpu).irq0_irqs;
-
- /* if the none of the timers isn't firing, this cpu isn't doing much */
- if (!touched && last_irq_sums[cpu] == sum) {
- /*
- * Ayiee, looks like this CPU is stuck ...
- * wait a few IRQs (5 seconds) before doing the oops ...
- */
- alert_counter[cpu]++;
- if (alert_counter[cpu] == 5*nmi_hz)
- /*
- * die_nmi will return ONLY if NOTIFY_STOP happens..
- */
- die_nmi(regs, "BUG: NMI Watchdog detected LOCKUP");
- } else {
- last_irq_sums[cpu] = sum;
- alert_counter[cpu] = 0;
- }
- /* see if the nmi watchdog went off */
- if (!__get_cpu_var(wd_enabled))
- return rc;
- switch (nmi_watchdog) {
- case NMI_LOCAL_APIC:
- rc |= lapic_wd_event(nmi_hz);
- break;
- case NMI_IO_APIC:
- /* don't know how to accurately check for this.
- * just assume it was a watchdog timer interrupt
- * This matches the old behaviour.
- */
- rc = 1;
- break;
- }
- return rc;
-}
-
-#ifdef CONFIG_SYSCTL
-
-static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu)
-{
- unsigned char reason = get_nmi_reason();
- char buf[64];
-
- sprintf(buf, "NMI received for unknown reason %02x\n", reason);
- die_nmi(regs, buf);
- return 0;
-}
-
-/*
- * proc handler for /proc/sys/kernel/nmi
- */
-int proc_nmi_enabled(struct ctl_table *table, int write, struct file *file,
- void __user *buffer, size_t *length, loff_t *ppos)
-{
- int old_state;
-
- nmi_watchdog_enabled = (atomic_read(&nmi_active) > 0) ? 1 : 0;
- old_state = nmi_watchdog_enabled;
- proc_dointvec(table, write, file, buffer, length, ppos);
- if (!!old_state == !!nmi_watchdog_enabled)
- return 0;
-
- if (atomic_read(&nmi_active) < 0 || nmi_watchdog == NMI_DISABLED) {
- printk( KERN_WARNING "NMI watchdog is permanently disabled\n");
- return -EIO;
- }
-
- if (nmi_watchdog == NMI_DEFAULT) {
- if (lapic_watchdog_ok())
- nmi_watchdog = NMI_LOCAL_APIC;
- else
- nmi_watchdog = NMI_IO_APIC;
- }
-
- if (nmi_watchdog == NMI_LOCAL_APIC) {
- if (nmi_watchdog_enabled)
- enable_lapic_nmi_watchdog();
- else
- disable_lapic_nmi_watchdog();
- } else {
- printk( KERN_WARNING
- "NMI watchdog doesn't know what hardware to touch\n");
- return -EIO;
- }
- return 0;
-}
-
-#endif
-
-int do_nmi_callback(struct pt_regs *regs, int cpu)
-{
-#ifdef CONFIG_SYSCTL
- if (unknown_nmi_panic)
- return unknown_nmi_panic_callback(regs, cpu);
-#endif
- return 0;
-}
-
-void __trigger_all_cpu_backtrace(void)
-{
- int i;
-
- backtrace_mask = cpu_online_map;
- /* Wait for up to 10 seconds for all CPUs to do the backtrace */
- for (i = 0; i < 10 * 1000; i++) {
- if (cpus_empty(backtrace_mask))
- break;
- mdelay(1);
- }
-}
-
-EXPORT_SYMBOL(nmi_active);
-EXPORT_SYMBOL(nmi_watchdog);
diff --git a/arch/x86/kernel/numaq_32.c b/arch/x86/kernel/numaq_32.c
index e65281b1634b..f0f1de1c4a1d 100644
--- a/arch/x86/kernel/numaq_32.c
+++ b/arch/x86/kernel/numaq_32.c
@@ -31,6 +31,8 @@
#include <asm/numaq.h>
#include <asm/topology.h>
#include <asm/processor.h>
+#include <asm/mpspec.h>
+#include <asm/e820.h>
#define MB_TO_PAGES(addr) ((addr) << (20 - PAGE_SHIFT))
@@ -58,6 +60,8 @@ static void __init smp_dump_qct(void)
node_end_pfn[node] = MB_TO_PAGES(
eq->hi_shrd_mem_start + eq->hi_shrd_mem_size);
+ e820_register_active_regions(node, node_start_pfn[node],
+ node_end_pfn[node]);
memory_present(node,
node_start_pfn[node], node_end_pfn[node]);
node_remap_size[node] = node_memmap_size_bytes(node,
@@ -67,13 +71,24 @@ static void __init smp_dump_qct(void)
}
}
-/*
- * Unlike Summit, we don't really care to let the NUMA-Q
- * fall back to flat mode. Don't compile for NUMA-Q
- * unless you really need it!
- */
+static __init void early_check_numaq(void)
+{
+ /*
+ * Find possible boot-time SMP configuration:
+ */
+ early_find_smp_config();
+ /*
+ * get boot-time SMP configuration:
+ */
+ if (smp_found_config)
+ early_get_smp_config();
+}
+
int __init get_memcfg_numaq(void)
{
+ early_check_numaq();
+ if (!found_numaq)
+ return 0;
smp_dump_qct();
return 1;
}
diff --git a/arch/x86/kernel/paravirt.c b/arch/x86/kernel/paravirt.c
index 74f0c5ea2a03..c98d54688180 100644
--- a/arch/x86/kernel/paravirt.c
+++ b/arch/x86/kernel/paravirt.c
@@ -403,6 +403,7 @@ struct pv_mmu_ops pv_mmu_ops = {
#endif /* PAGETABLE_LEVELS >= 3 */
.pte_val = native_pte_val,
+ .pte_flags = native_pte_val,
.pgd_val = native_pgd_val,
.make_pte = native_make_pte,
diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c
index c5ef1af8e79d..b2f38b8c2c30 100644
--- a/arch/x86/kernel/pci-dma.c
+++ b/arch/x86/kernel/pci-dma.c
@@ -77,10 +77,14 @@ void __init dma32_reserve_bootmem(void)
if (end_pfn <= MAX_DMA32_PFN)
return;
+ /*
+ * check aperture_64.c allocate_aperture() for reason about
+ * using 512M as goal
+ */
align = 64ULL<<20;
size = round_up(dma32_bootmem_size, align);
dma32_bootmem_ptr = __alloc_bootmem_nopanic(size, align,
- __pa(MAX_DMA_ADDRESS));
+ 512ULL<<20);
if (dma32_bootmem_ptr)
dma32_bootmem_size = size;
else
@@ -88,7 +92,6 @@ void __init dma32_reserve_bootmem(void)
}
static void __init dma32_free_bootmem(void)
{
- int node;
if (end_pfn <= MAX_DMA32_PFN)
return;
@@ -96,9 +99,7 @@ static void __init dma32_free_bootmem(void)
if (!dma32_bootmem_ptr)
return;
- for_each_online_node(node)
- free_bootmem_node(NODE_DATA(node), __pa(dma32_bootmem_ptr),
- dma32_bootmem_size);
+ free_bootmem(__pa(dma32_bootmem_ptr), dma32_bootmem_size);
dma32_bootmem_ptr = NULL;
dma32_bootmem_size = 0;
@@ -319,8 +320,7 @@ int dma_supported(struct device *dev, u64 mask)
{
#ifdef CONFIG_PCI
if (mask > 0xffffffff && forbid_dac > 0) {
- printk(KERN_INFO "PCI: Disallowing DAC for device %s\n",
- dev->bus_id);
+ dev_info(dev, "PCI: Disallowing DAC for device\n");
return 0;
}
#endif
@@ -347,8 +347,7 @@ int dma_supported(struct device *dev, u64 mask)
type. Normally this doesn't make any difference, but gives
more gentle handling of IOMMU overflow. */
if (iommu_sac_force && (mask >= DMA_40BIT_MASK)) {
- printk(KERN_INFO "%s: Force SAC with mask %Lx\n",
- dev->bus_id, mask);
+ dev_info(dev, "Force SAC with mask %Lx\n", mask);
return 0;
}
@@ -357,7 +356,7 @@ int dma_supported(struct device *dev, u64 mask)
EXPORT_SYMBOL(dma_supported);
/* Allocate DMA memory on node near device */
-noinline struct page *
+static noinline struct page *
dma_alloc_pages(struct device *dev, gfp_t gfp, unsigned order)
{
int node;
@@ -378,6 +377,7 @@ dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
struct page *page;
unsigned long dma_mask = 0;
dma_addr_t bus;
+ int noretry = 0;
/* ignore region specifiers */
gfp &= ~(__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32);
@@ -397,20 +397,25 @@ dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
if (dev->dma_mask == NULL)
return NULL;
- /* Don't invoke OOM killer */
- gfp |= __GFP_NORETRY;
+ /* Don't invoke OOM killer or retry in lower 16MB DMA zone */
+ if (gfp & __GFP_DMA)
+ noretry = 1;
#ifdef CONFIG_X86_64
/* Why <=? Even when the mask is smaller than 4GB it is often
larger than 16MB and in this case we have a chance of
finding fitting memory in the next higher zone first. If
not retry with true GFP_DMA. -AK */
- if (dma_mask <= DMA_32BIT_MASK && !(gfp & GFP_DMA))
+ if (dma_mask <= DMA_32BIT_MASK && !(gfp & GFP_DMA)) {
gfp |= GFP_DMA32;
+ if (dma_mask < DMA_32BIT_MASK)
+ noretry = 1;
+ }
#endif
again:
- page = dma_alloc_pages(dev, gfp, get_order(size));
+ page = dma_alloc_pages(dev,
+ noretry ? gfp | __GFP_NORETRY : gfp, get_order(size));
if (page == NULL)
return NULL;
diff --git a/arch/x86/kernel/pci-gart_64.c b/arch/x86/kernel/pci-gart_64.c
index aa8ec928caa8..3c27f6e4783f 100644
--- a/arch/x86/kernel/pci-gart_64.c
+++ b/arch/x86/kernel/pci-gart_64.c
@@ -198,9 +198,7 @@ static void iommu_full(struct device *dev, size_t size, int dir)
* out. Hopefully no network devices use single mappings that big.
*/
- printk(KERN_ERR
- "PCI-DMA: Out of IOMMU space for %lu bytes at device %s\n",
- size, dev->bus_id);
+ dev_err(dev, "PCI-DMA: Out of IOMMU space for %lu bytes\n", size);
if (size > PAGE_SIZE*EMERGENCY_PAGES) {
if (dir == PCI_DMA_FROMDEVICE || dir == PCI_DMA_BIDIRECTIONAL)
@@ -534,8 +532,8 @@ static __init unsigned read_aperture(struct pci_dev *dev, u32 *size)
unsigned aper_size = 0, aper_base_32, aper_order;
u64 aper_base;
- pci_read_config_dword(dev, 0x94, &aper_base_32);
- pci_read_config_dword(dev, 0x90, &aper_order);
+ pci_read_config_dword(dev, AMD64_GARTAPERTUREBASE, &aper_base_32);
+ pci_read_config_dword(dev, AMD64_GARTAPERTURECTL, &aper_order);
aper_order = (aper_order >> 1) & 7;
aper_base = aper_base_32 & 0x7fff;
@@ -615,19 +613,8 @@ static __init int init_k8_gatt(struct agp_kern_info *info)
agp_gatt_table = gatt;
for (i = 0; i < num_k8_northbridges; i++) {
- u32 gatt_reg;
- u32 ctl;
-
dev = k8_northbridges[i];
- gatt_reg = __pa(gatt) >> 12;
- gatt_reg <<= 4;
- pci_write_config_dword(dev, 0x98, gatt_reg);
- pci_read_config_dword(dev, 0x90, &ctl);
-
- ctl |= 1;
- ctl &= ~((1<<4) | (1<<5));
-
- pci_write_config_dword(dev, 0x90, ctl);
+ enable_gart_translation(dev, __pa(gatt));
}
error = sysdev_class_register(&gart_sysdev_class);
@@ -677,11 +664,11 @@ void gart_iommu_shutdown(void)
u32 ctl;
dev = k8_northbridges[i];
- pci_read_config_dword(dev, 0x90, &ctl);
+ pci_read_config_dword(dev, AMD64_GARTAPERTURECTL, &ctl);
- ctl &= ~1;
+ ctl &= ~GARTEN;
- pci_write_config_dword(dev, 0x90, ctl);
+ pci_write_config_dword(dev, AMD64_GARTAPERTURECTL, ctl);
}
}
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index ba370dc8685b..68ad3539b143 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -6,6 +6,7 @@
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/pm.h>
+#include <linux/clockchips.h>
struct kmem_cache *task_xstate_cachep;
@@ -45,6 +46,76 @@ void arch_task_cache_init(void)
SLAB_PANIC, NULL);
}
+/*
+ * Idle related variables and functions
+ */
+unsigned long boot_option_idle_override = 0;
+EXPORT_SYMBOL(boot_option_idle_override);
+
+/*
+ * Powermanagement idle function, if any..
+ */
+void (*pm_idle)(void);
+EXPORT_SYMBOL(pm_idle);
+
+#ifdef CONFIG_X86_32
+/*
+ * This halt magic was a workaround for ancient floppy DMA
+ * wreckage. It should be safe to remove.
+ */
+static int hlt_counter;
+void disable_hlt(void)
+{
+ hlt_counter++;
+}
+EXPORT_SYMBOL(disable_hlt);
+
+void enable_hlt(void)
+{
+ hlt_counter--;
+}
+EXPORT_SYMBOL(enable_hlt);
+
+static inline int hlt_use_halt(void)
+{
+ return (!hlt_counter && boot_cpu_data.hlt_works_ok);
+}
+#else
+static inline int hlt_use_halt(void)
+{
+ return 1;
+}
+#endif
+
+/*
+ * We use this if we don't have any better
+ * idle routine..
+ */
+void default_idle(void)
+{
+ if (hlt_use_halt()) {
+ current_thread_info()->status &= ~TS_POLLING;
+ /*
+ * TS_POLLING-cleared state must be visible before we
+ * test NEED_RESCHED:
+ */
+ smp_mb();
+
+ if (!need_resched())
+ safe_halt(); /* enables interrupts racelessly */
+ else
+ local_irq_enable();
+ current_thread_info()->status |= TS_POLLING;
+ } else {
+ local_irq_enable();
+ /* loop is done by the caller */
+ cpu_relax();
+ }
+}
+#ifdef CONFIG_APM_MODULE
+EXPORT_SYMBOL(default_idle);
+#endif
+
static void do_nothing(void *unused)
{
}
@@ -122,44 +193,117 @@ static void poll_idle(void)
*
* idle=mwait overrides this decision and forces the usage of mwait.
*/
+
+#define MWAIT_INFO 0x05
+#define MWAIT_ECX_EXTENDED_INFO 0x01
+#define MWAIT_EDX_C1 0xf0
+
static int __cpuinit mwait_usable(const struct cpuinfo_x86 *c)
{
+ u32 eax, ebx, ecx, edx;
+
if (force_mwait)
return 1;
- if (c->x86_vendor == X86_VENDOR_AMD) {
- switch(c->x86) {
- case 0x10:
- case 0x11:
- return 0;
- }
- }
+ if (c->cpuid_level < MWAIT_INFO)
+ return 0;
+
+ cpuid(MWAIT_INFO, &eax, &ebx, &ecx, &edx);
+ /* Check, whether EDX has extended info about MWAIT */
+ if (!(ecx & MWAIT_ECX_EXTENDED_INFO))
+ return 1;
+
+ /*
+ * edx enumeratios MONITOR/MWAIT extensions. Check, whether
+ * C1 supports MWAIT
+ */
+ return (edx & MWAIT_EDX_C1);
+}
+
+/*
+ * Check for AMD CPUs, which have potentially C1E support
+ */
+static int __cpuinit check_c1e_idle(const struct cpuinfo_x86 *c)
+{
+ if (c->x86_vendor != X86_VENDOR_AMD)
+ return 0;
+
+ if (c->x86 < 0x0F)
+ return 0;
+
+ /* Family 0x0f models < rev F do not have C1E */
+ if (c->x86 == 0x0f && c->x86_model < 0x40)
+ return 0;
+
return 1;
}
-void __cpuinit select_idle_routine(const struct cpuinfo_x86 *c)
+/*
+ * C1E aware idle routine. We check for C1E active in the interrupt
+ * pending message MSR. If we detect C1E, then we handle it the same
+ * way as C3 power states (local apic timer and TSC stop)
+ */
+static void c1e_idle(void)
{
- static int selected;
+ static cpumask_t c1e_mask = CPU_MASK_NONE;
+ static int c1e_detected;
- if (selected)
+ if (need_resched())
return;
+
+ if (!c1e_detected) {
+ u32 lo, hi;
+
+ rdmsr(MSR_K8_INT_PENDING_MSG, lo, hi);
+ if (lo & K8_INTP_C1E_ACTIVE_MASK) {
+ c1e_detected = 1;
+ mark_tsc_unstable("TSC halt in C1E");
+ printk(KERN_INFO "System has C1E enabled\n");
+ }
+ }
+
+ if (c1e_detected) {
+ int cpu = smp_processor_id();
+
+ if (!cpu_isset(cpu, c1e_mask)) {
+ cpu_set(cpu, c1e_mask);
+ /* Force broadcast so ACPI can not interfere */
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_FORCE,
+ &cpu);
+ printk(KERN_INFO "Switch to broadcast mode on CPU%d\n",
+ cpu);
+ }
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &cpu);
+ default_idle();
+ local_irq_disable();
+ clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu);
+ local_irq_enable();
+ } else
+ default_idle();
+}
+
+void __cpuinit select_idle_routine(const struct cpuinfo_x86 *c)
+{
#ifdef CONFIG_X86_SMP
if (pm_idle == poll_idle && smp_num_siblings > 1) {
printk(KERN_WARNING "WARNING: polling idle and HT enabled,"
" performance may degrade.\n");
}
#endif
+ if (pm_idle)
+ return;
+
if (cpu_has(c, X86_FEATURE_MWAIT) && mwait_usable(c)) {
/*
- * Skip, if setup has overridden idle.
* One CPU supports mwait => All CPUs supports mwait
*/
- if (!pm_idle) {
- printk(KERN_INFO "using mwait in idle threads.\n");
- pm_idle = mwait_idle;
- }
- }
- selected = 1;
+ printk(KERN_INFO "using mwait in idle threads.\n");
+ pm_idle = mwait_idle;
+ } else if (check_c1e_idle(c)) {
+ printk(KERN_INFO "using C1E aware idle routine\n");
+ pm_idle = c1e_idle;
+ } else
+ pm_idle = default_idle;
}
static int __init idle_setup(char *str)
diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c
index 6d5483356e74..83374de77a6f 100644
--- a/arch/x86/kernel/process_32.c
+++ b/arch/x86/kernel/process_32.c
@@ -58,11 +58,6 @@
asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
-static int hlt_counter;
-
-unsigned long boot_option_idle_override = 0;
-EXPORT_SYMBOL(boot_option_idle_override);
-
DEFINE_PER_CPU(struct task_struct *, current_task) = &init_task;
EXPORT_PER_CPU_SYMBOL(current_task);
@@ -77,55 +72,6 @@ unsigned long thread_saved_pc(struct task_struct *tsk)
return ((unsigned long *)tsk->thread.sp)[3];
}
-/*
- * Powermanagement idle function, if any..
- */
-void (*pm_idle)(void);
-EXPORT_SYMBOL(pm_idle);
-
-void disable_hlt(void)
-{
- hlt_counter++;
-}
-
-EXPORT_SYMBOL(disable_hlt);
-
-void enable_hlt(void)
-{
- hlt_counter--;
-}
-
-EXPORT_SYMBOL(enable_hlt);
-
-/*
- * We use this if we don't have any better
- * idle routine..
- */
-void default_idle(void)
-{
- if (!hlt_counter && boot_cpu_data.hlt_works_ok) {
- current_thread_info()->status &= ~TS_POLLING;
- /*
- * TS_POLLING-cleared state must be visible before we
- * test NEED_RESCHED:
- */
- smp_mb();
-
- if (!need_resched())
- safe_halt(); /* enables interrupts racelessly */
- else
- local_irq_enable();
- current_thread_info()->status |= TS_POLLING;
- } else {
- local_irq_enable();
- /* loop is done by the caller */
- cpu_relax();
- }
-}
-#ifdef CONFIG_APM_MODULE
-EXPORT_SYMBOL(default_idle);
-#endif
-
#ifdef CONFIG_HOTPLUG_CPU
#include <asm/nmi.h>
/* We don't actually take CPU down, just spin without interrupts. */
@@ -168,24 +114,22 @@ void cpu_idle(void)
while (1) {
tick_nohz_stop_sched_tick();
while (!need_resched()) {
- void (*idle)(void);
check_pgt_cache();
rmb();
- idle = pm_idle;
if (rcu_pending(cpu))
rcu_check_callbacks(cpu, 0);
- if (!idle)
- idle = default_idle;
-
if (cpu_is_offline(cpu))
play_dead();
local_irq_disable();
__get_cpu_var(irq_stat).idle_timestamp = jiffies;
- idle();
+ /* Don't trace irqs off for idle */
+ stop_critical_timings();
+ pm_idle();
+ start_critical_timings();
}
tick_nohz_restart_sched_tick();
preempt_enable_no_resched();
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index ac54ff56df80..f67a463d181b 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -16,6 +16,7 @@
#include <stdarg.h>
+#include <linux/stackprotector.h>
#include <linux/cpu.h>
#include <linux/errno.h>
#include <linux/sched.h>
@@ -56,15 +57,6 @@ asmlinkage extern void ret_from_fork(void);
unsigned long kernel_thread_flags = CLONE_VM | CLONE_UNTRACED;
-unsigned long boot_option_idle_override = 0;
-EXPORT_SYMBOL(boot_option_idle_override);
-
-/*
- * Powermanagement idle function, if any..
- */
-void (*pm_idle)(void);
-EXPORT_SYMBOL(pm_idle);
-
static ATOMIC_NOTIFIER_HEAD(idle_notifier);
void idle_notifier_register(struct notifier_block *n)
@@ -94,25 +86,6 @@ void exit_idle(void)
__exit_idle();
}
-/*
- * We use this if we don't have any better
- * idle routine..
- */
-void default_idle(void)
-{
- current_thread_info()->status &= ~TS_POLLING;
- /*
- * TS_POLLING-cleared state must be visible before we
- * test NEED_RESCHED:
- */
- smp_mb();
- if (!need_resched())
- safe_halt(); /* enables interrupts racelessly */
- else
- local_irq_enable();
- current_thread_info()->status |= TS_POLLING;
-}
-
#ifdef CONFIG_HOTPLUG_CPU
DECLARE_PER_CPU(int, cpu_state);
@@ -146,16 +119,24 @@ static inline void play_dead(void)
void cpu_idle(void)
{
current_thread_info()->status |= TS_POLLING;
+
+ /*
+ * If we're the non-boot CPU, nothing set the PDA stack
+ * canary up for us - and if we are the boot CPU we have
+ * a 0 stack canary. This is a good place for updating
+ * it, as we wont ever return from this function (so the
+ * invalid canaries already on the stack wont ever
+ * trigger):
+ */
+ boot_init_stack_canary();
+
/* endless idle loop with no priority at all */
while (1) {
tick_nohz_stop_sched_tick();
while (!need_resched()) {
- void (*idle)(void);
rmb();
- idle = pm_idle;
- if (!idle)
- idle = default_idle;
+
if (cpu_is_offline(smp_processor_id()))
play_dead();
/*
@@ -165,7 +146,10 @@ void cpu_idle(void)
*/
local_irq_disable();
enter_idle();
- idle();
+ /* Don't trace irqs off for idle */
+ stop_critical_timings();
+ pm_idle();
+ start_critical_timings();
/* In many cases the interrupt that ended idle
has already called exit_idle. But some idle
loops can be woken up without interrupt. */
@@ -640,7 +624,6 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
write_pda(kernelstack,
(unsigned long)task_stack_page(next_p) + THREAD_SIZE - PDA_STACKOFFSET);
#ifdef CONFIG_CC_STACKPROTECTOR
- write_pda(stack_canary, next_p->stack_canary);
/*
* Build time only check to make sure the stack_canary is at
* offset 40 in the pda; this is a gcc ABI requirement
diff --git a/arch/x86/kernel/quirks.c b/arch/x86/kernel/quirks.c
index d89a648fe710..79bdcd11c66e 100644
--- a/arch/x86/kernel/quirks.c
+++ b/arch/x86/kernel/quirks.c
@@ -65,6 +65,7 @@ static enum {
ICH_FORCE_HPET_RESUME,
VT8237_FORCE_HPET_RESUME,
NVIDIA_FORCE_HPET_RESUME,
+ ATI_FORCE_HPET_RESUME,
} force_hpet_resume_type;
static void __iomem *rcba_base;
@@ -158,6 +159,8 @@ static void ich_force_enable_hpet(struct pci_dev *dev)
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0,
ich_force_enable_hpet);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_0,
+ ich_force_enable_hpet);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1,
ich_force_enable_hpet);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0,
@@ -174,6 +177,12 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_7,
static struct pci_dev *cached_dev;
+static void hpet_print_force_info(void)
+{
+ printk(KERN_INFO "HPET not enabled in BIOS. "
+ "You might try hpet=force boot option\n");
+}
+
static void old_ich_force_hpet_resume(void)
{
u32 val;
@@ -253,6 +262,8 @@ static void old_ich_force_enable_hpet_user(struct pci_dev *dev)
{
if (hpet_force_user)
old_ich_force_enable_hpet(dev);
+ else
+ hpet_print_force_info();
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0,
@@ -290,8 +301,13 @@ static void vt8237_force_enable_hpet(struct pci_dev *dev)
{
u32 uninitialized_var(val);
- if (!hpet_force_user || hpet_address || force_hpet_address)
+ if (hpet_address || force_hpet_address)
+ return;
+
+ if (!hpet_force_user) {
+ hpet_print_force_info();
return;
+ }
pci_read_config_dword(dev, 0x68, &val);
/*
@@ -330,6 +346,36 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8235,
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237,
vt8237_force_enable_hpet);
+static void ati_force_hpet_resume(void)
+{
+ pci_write_config_dword(cached_dev, 0x14, 0xfed00000);
+ printk(KERN_DEBUG "Force enabled HPET at resume\n");
+}
+
+static void ati_force_enable_hpet(struct pci_dev *dev)
+{
+ u32 uninitialized_var(val);
+
+ if (hpet_address || force_hpet_address)
+ return;
+
+ if (!hpet_force_user) {
+ hpet_print_force_info();
+ return;
+ }
+
+ pci_write_config_dword(dev, 0x14, 0xfed00000);
+ pci_read_config_dword(dev, 0x14, &val);
+ force_hpet_address = val;
+ force_hpet_resume_type = ATI_FORCE_HPET_RESUME;
+ dev_printk(KERN_DEBUG, &dev->dev, "Force enabled HPET at 0x%lx\n",
+ force_hpet_address);
+ cached_dev = dev;
+ return;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS,
+ ati_force_enable_hpet);
+
/*
* Undocumented chipset feature taken from LinuxBIOS.
*/
@@ -343,8 +389,13 @@ static void nvidia_force_enable_hpet(struct pci_dev *dev)
{
u32 uninitialized_var(val);
- if (!hpet_force_user || hpet_address || force_hpet_address)
+ if (hpet_address || force_hpet_address)
+ return;
+
+ if (!hpet_force_user) {
+ hpet_print_force_info();
return;
+ }
pci_write_config_dword(dev, 0x44, 0xfed00001);
pci_read_config_dword(dev, 0x44, &val);
@@ -397,6 +448,9 @@ void force_hpet_resume(void)
case NVIDIA_FORCE_HPET_RESUME:
nvidia_force_hpet_resume();
return;
+ case ATI_FORCE_HPET_RESUME:
+ ati_force_hpet_resume();
+ return;
default:
break;
}
diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c
index f6be7d5f82f8..f8a62160e151 100644
--- a/arch/x86/kernel/reboot.c
+++ b/arch/x86/kernel/reboot.c
@@ -27,7 +27,7 @@
void (*pm_power_off)(void);
EXPORT_SYMBOL(pm_power_off);
-static long no_idt[3];
+static const struct desc_ptr no_idt = {};
static int reboot_mode;
enum reboot_type reboot_type = BOOT_KBD;
int reboot_force;
@@ -201,15 +201,15 @@ core_initcall(reboot_init);
controller to pulse the CPU reset line, which is more thorough, but
doesn't work with at least one type of 486 motherboard. It is easy
to stop this code working; hence the copious comments. */
-static unsigned long long
+static const unsigned long long
real_mode_gdt_entries [3] =
{
0x0000000000000000ULL, /* Null descriptor */
- 0x00009a000000ffffULL, /* 16-bit real-mode 64k code at 0x00000000 */
- 0x000092000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */
+ 0x00009b000000ffffULL, /* 16-bit real-mode 64k code at 0x00000000 */
+ 0x000093000100ffffULL /* 16-bit real-mode 64k data at 0x00000100 */
};
-static struct desc_ptr
+static const struct desc_ptr
real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, (long)real_mode_gdt_entries },
real_mode_idt = { 0x3ff, 0 };
@@ -231,7 +231,7 @@ real_mode_idt = { 0x3ff, 0 };
More could be done here to set up the registers as if a CPU reset had
occurred; hopefully real BIOSs don't assume much. */
-static unsigned char real_mode_switch [] =
+static const unsigned char real_mode_switch [] =
{
0x66, 0x0f, 0x20, 0xc0, /* movl %cr0,%eax */
0x66, 0x83, 0xe0, 0x11, /* andl $0x00000011,%eax */
@@ -245,7 +245,7 @@ static unsigned char real_mode_switch [] =
0x24, 0x10, /* f: andb $0x10,al */
0x66, 0x0f, 0x22, 0xc0 /* movl %eax,%cr0 */
};
-static unsigned char jump_to_bios [] =
+static const unsigned char jump_to_bios [] =
{
0xea, 0x00, 0x00, 0xff, 0xff /* ljmp $0xffff,$0x0000 */
};
@@ -255,7 +255,7 @@ static unsigned char jump_to_bios [] =
* specified by the code and length parameters.
* We assume that length will aways be less that 100!
*/
-void machine_real_restart(unsigned char *code, int length)
+void machine_real_restart(const unsigned char *code, int length)
{
local_irq_disable();
@@ -368,7 +368,7 @@ static void native_machine_emergency_restart(void)
}
case BOOT_TRIPLE:
- load_idt((const struct desc_ptr *)&no_idt);
+ load_idt(&no_idt);
__asm__ __volatile__("int3");
reboot_type = BOOT_KBD;
diff --git a/arch/x86/kernel/reboot_fixups_32.c b/arch/x86/kernel/reboot_fixups_32.c
index dec0b5ec25c2..61a837743fe5 100644
--- a/arch/x86/kernel/reboot_fixups_32.c
+++ b/arch/x86/kernel/reboot_fixups_32.c
@@ -49,7 +49,7 @@ struct device_fixup {
void (*reboot_fixup)(struct pci_dev *);
};
-static struct device_fixup fixups_table[] = {
+static const struct device_fixup fixups_table[] = {
{ PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY, cs5530a_warm_reset },
{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA, cs5536_warm_reset },
{ PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE, cs5530a_warm_reset },
@@ -64,7 +64,7 @@ static struct device_fixup fixups_table[] = {
*/
void mach_reboot_fixups(void)
{
- struct device_fixup *cur;
+ const struct device_fixup *cur;
struct pci_dev *dev;
int i;
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index 6f80b852a196..cff27d30d65f 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -17,16 +17,38 @@ unsigned int num_processors;
unsigned disabled_cpus __cpuinitdata;
/* Processor that is doing the boot up */
unsigned int boot_cpu_physical_apicid = -1U;
+unsigned int max_physical_apicid;
EXPORT_SYMBOL(boot_cpu_physical_apicid);
-DEFINE_PER_CPU(u16, x86_cpu_to_apicid) = BAD_APICID;
-EXPORT_PER_CPU_SYMBOL(x86_cpu_to_apicid);
-
/* Bitmask of physically existing CPUs */
physid_mask_t phys_cpu_present_map;
#endif
-#if defined(CONFIG_HAVE_SETUP_PER_CPU_AREA) && defined(CONFIG_X86_SMP)
+/* map cpu index to physical APIC ID */
+DEFINE_EARLY_PER_CPU(u16, x86_cpu_to_apicid, BAD_APICID);
+DEFINE_EARLY_PER_CPU(u16, x86_bios_cpu_apicid, BAD_APICID);
+EXPORT_EARLY_PER_CPU_SYMBOL(x86_cpu_to_apicid);
+EXPORT_EARLY_PER_CPU_SYMBOL(x86_bios_cpu_apicid);
+
+#if defined(CONFIG_NUMA) && defined(CONFIG_X86_64)
+#define X86_64_NUMA 1
+
+/* map cpu index to node index */
+DEFINE_EARLY_PER_CPU(int, x86_cpu_to_node_map, NUMA_NO_NODE);
+EXPORT_EARLY_PER_CPU_SYMBOL(x86_cpu_to_node_map);
+
+/* which logical CPUs are on which nodes */
+cpumask_t *node_to_cpumask_map;
+EXPORT_SYMBOL(node_to_cpumask_map);
+
+/* setup node_to_cpumask_map */
+static void __init setup_node_to_cpumask_map(void);
+
+#else
+static inline void setup_node_to_cpumask_map(void) { }
+#endif
+
+#if defined(CONFIG_HAVE_SETUP_PER_CPU_AREA) && defined(CONFIG_SMP)
/*
* Copy data used in early init routines from the initial arrays to the
* per cpu data areas. These arrays then become expendable and the
@@ -37,20 +59,21 @@ static void __init setup_per_cpu_maps(void)
int cpu;
for_each_possible_cpu(cpu) {
- per_cpu(x86_cpu_to_apicid, cpu) = x86_cpu_to_apicid_init[cpu];
+ per_cpu(x86_cpu_to_apicid, cpu) =
+ early_per_cpu_map(x86_cpu_to_apicid, cpu);
per_cpu(x86_bios_cpu_apicid, cpu) =
- x86_bios_cpu_apicid_init[cpu];
-#ifdef CONFIG_NUMA
+ early_per_cpu_map(x86_bios_cpu_apicid, cpu);
+#ifdef X86_64_NUMA
per_cpu(x86_cpu_to_node_map, cpu) =
- x86_cpu_to_node_map_init[cpu];
+ early_per_cpu_map(x86_cpu_to_node_map, cpu);
#endif
}
/* indicate the early static arrays will soon be gone */
- x86_cpu_to_apicid_early_ptr = NULL;
- x86_bios_cpu_apicid_early_ptr = NULL;
-#ifdef CONFIG_NUMA
- x86_cpu_to_node_map_early_ptr = NULL;
+ early_per_cpu_ptr(x86_cpu_to_apicid) = NULL;
+ early_per_cpu_ptr(x86_bios_cpu_apicid) = NULL;
+#ifdef X86_64_NUMA
+ early_per_cpu_ptr(x86_cpu_to_node_map) = NULL;
#endif
}
@@ -79,6 +102,50 @@ static inline void setup_cpumask_of_cpu(void) { }
*/
unsigned long __per_cpu_offset[NR_CPUS] __read_mostly;
EXPORT_SYMBOL(__per_cpu_offset);
+static inline void setup_cpu_pda_map(void) { }
+
+#elif !defined(CONFIG_SMP)
+static inline void setup_cpu_pda_map(void) { }
+
+#else /* CONFIG_SMP && CONFIG_X86_64 */
+
+/*
+ * Allocate cpu_pda pointer table and array via alloc_bootmem.
+ */
+static void __init setup_cpu_pda_map(void)
+{
+ char *pda;
+ struct x8664_pda **new_cpu_pda;
+ unsigned long size;
+ int cpu;
+
+ size = roundup(sizeof(struct x8664_pda), cache_line_size());
+
+ /* allocate cpu_pda array and pointer table */
+ {
+ unsigned long tsize = nr_cpu_ids * sizeof(void *);
+ unsigned long asize = size * (nr_cpu_ids - 1);
+
+ tsize = roundup(tsize, cache_line_size());
+ new_cpu_pda = alloc_bootmem(tsize + asize);
+ pda = (char *)new_cpu_pda + tsize;
+ }
+
+ /* initialize pointer table to static pda's */
+ for_each_possible_cpu(cpu) {
+ if (cpu == 0) {
+ /* leave boot cpu pda in place */
+ new_cpu_pda[0] = cpu_pda(0);
+ continue;
+ }
+ new_cpu_pda[cpu] = (struct x8664_pda *)pda;
+ new_cpu_pda[cpu]->in_bootmem = 1;
+ pda += size;
+ }
+
+ /* point to new pointer table */
+ _cpu_pda = new_cpu_pda;
+}
#endif
/*
@@ -88,52 +155,262 @@ EXPORT_SYMBOL(__per_cpu_offset);
*/
void __init setup_per_cpu_areas(void)
{
- int i, highest_cpu = 0;
- unsigned long size;
+ ssize_t size = PERCPU_ENOUGH_ROOM;
+ char *ptr;
+ int cpu;
#ifdef CONFIG_HOTPLUG_CPU
prefill_possible_map();
+#else
+ nr_cpu_ids = num_processors;
#endif
+ /* Setup cpu_pda map */
+ setup_cpu_pda_map();
+
/* Copy section for each CPU (we discard the original) */
size = PERCPU_ENOUGH_ROOM;
- printk(KERN_INFO "PERCPU: Allocating %lu bytes of per cpu data\n",
+ printk(KERN_INFO "PERCPU: Allocating %zd bytes of per cpu data\n",
size);
- for_each_possible_cpu(i) {
- char *ptr;
+ for_each_possible_cpu(cpu) {
#ifndef CONFIG_NEED_MULTIPLE_NODES
ptr = alloc_bootmem_pages(size);
#else
- int node = early_cpu_to_node(i);
+ int node = early_cpu_to_node(cpu);
if (!node_online(node) || !NODE_DATA(node)) {
ptr = alloc_bootmem_pages(size);
printk(KERN_INFO
- "cpu %d has no node or node-local memory\n", i);
+ "cpu %d has no node %d or node-local memory\n",
+ cpu, node);
}
else
ptr = alloc_bootmem_pages_node(NODE_DATA(node), size);
#endif
- if (!ptr)
- panic("Cannot allocate cpu data for CPU %d\n", i);
-#ifdef CONFIG_X86_64
- cpu_pda(i)->data_offset = ptr - __per_cpu_start;
-#else
- __per_cpu_offset[i] = ptr - __per_cpu_start;
-#endif
+ per_cpu_offset(cpu) = ptr - __per_cpu_start;
memcpy(ptr, __per_cpu_start, __per_cpu_end - __per_cpu_start);
- highest_cpu = i;
}
- nr_cpu_ids = highest_cpu + 1;
- printk(KERN_DEBUG "NR_CPUS: %d, nr_cpu_ids: %d\n", NR_CPUS, nr_cpu_ids);
+ printk(KERN_DEBUG "NR_CPUS: %d, nr_cpu_ids: %d, nr_node_ids %d\n",
+ NR_CPUS, nr_cpu_ids, nr_node_ids);
/* Setup percpu data maps */
setup_per_cpu_maps();
+ /* Setup node to cpumask map */
+ setup_node_to_cpumask_map();
+
/* Setup cpumask_of_cpu map */
setup_cpumask_of_cpu();
}
#endif
+#define ENABLE_CF8_EXT_CFG (1ULL << 46)
+
+void __cpuinit amd_enable_pci_ext_cfg(struct cpuinfo_x86 *c)
+{
+ u64 reg;
+ rdmsrl(MSR_AMD64_NB_CFG, reg);
+ if (!(reg & ENABLE_CF8_EXT_CFG)) {
+ reg |= ENABLE_CF8_EXT_CFG;
+ wrmsrl(MSR_AMD64_NB_CFG, reg);
+ }
+ set_cpu_cap(c, X86_FEATURE_PCI_EXT_CFG);
+}
+
+
+void __init parse_setup_data(void)
+{
+ struct setup_data *data;
+ u64 pa_data;
+
+ if (boot_params.hdr.version < 0x0209)
+ return;
+ pa_data = boot_params.hdr.setup_data;
+ while (pa_data) {
+ data = early_ioremap(pa_data, PAGE_SIZE);
+ switch (data->type) {
+ default:
+ break;
+ }
+#ifndef CONFIG_DEBUG_BOOT_PARAMS
+ free_early(pa_data, pa_data+sizeof(*data)+data->len);
+#endif
+ pa_data = data->next;
+ early_iounmap(data, PAGE_SIZE);
+ }
+}
+
+#ifdef X86_64_NUMA
+
+/*
+ * Allocate node_to_cpumask_map based on number of available nodes
+ * Requires node_possible_map to be valid.
+ *
+ * Note: node_to_cpumask() is not valid until after this is done.
+ */
+static void __init setup_node_to_cpumask_map(void)
+{
+ unsigned int node, num = 0;
+ cpumask_t *map;
+
+ /* setup nr_node_ids if not done yet */
+ if (nr_node_ids == MAX_NUMNODES) {
+ for_each_node_mask(node, node_possible_map)
+ num = node;
+ nr_node_ids = num + 1;
+ }
+
+ /* allocate the map */
+ map = alloc_bootmem_low(nr_node_ids * sizeof(cpumask_t));
+
+ Dprintk(KERN_DEBUG "Node to cpumask map at %p for %d nodes\n",
+ map, nr_node_ids);
+
+ /* node_to_cpumask() will now work */
+ node_to_cpumask_map = map;
+}
+
+void __cpuinit numa_set_node(int cpu, int node)
+{
+ int *cpu_to_node_map = early_per_cpu_ptr(x86_cpu_to_node_map);
+
+ if (cpu_pda(cpu) && node != NUMA_NO_NODE)
+ cpu_pda(cpu)->nodenumber = node;
+
+ if (cpu_to_node_map)
+ cpu_to_node_map[cpu] = node;
+
+ else if (per_cpu_offset(cpu))
+ per_cpu(x86_cpu_to_node_map, cpu) = node;
+
+ else
+ Dprintk(KERN_INFO "Setting node for non-present cpu %d\n", cpu);
+}
+
+void __cpuinit numa_clear_node(int cpu)
+{
+ numa_set_node(cpu, NUMA_NO_NODE);
+}
+
+#ifndef CONFIG_DEBUG_PER_CPU_MAPS
+
+void __cpuinit numa_add_cpu(int cpu)
+{
+ cpu_set(cpu, node_to_cpumask_map[early_cpu_to_node(cpu)]);
+}
+
+void __cpuinit numa_remove_cpu(int cpu)
+{
+ cpu_clear(cpu, node_to_cpumask_map[cpu_to_node(cpu)]);
+}
+
+#else /* CONFIG_DEBUG_PER_CPU_MAPS */
+
+/*
+ * --------- debug versions of the numa functions ---------
+ */
+static void __cpuinit numa_set_cpumask(int cpu, int enable)
+{
+ int node = cpu_to_node(cpu);
+ cpumask_t *mask;
+ char buf[64];
+
+ if (node_to_cpumask_map == NULL) {
+ printk(KERN_ERR "node_to_cpumask_map NULL\n");
+ dump_stack();
+ return;
+ }
+
+ mask = &node_to_cpumask_map[node];
+ if (enable)
+ cpu_set(cpu, *mask);
+ else
+ cpu_clear(cpu, *mask);
+
+ cpulist_scnprintf(buf, sizeof(buf), *mask);
+ printk(KERN_DEBUG "%s cpu %d node %d: mask now %s\n",
+ enable? "numa_add_cpu":"numa_remove_cpu", cpu, node, buf);
+ }
+
+void __cpuinit numa_add_cpu(int cpu)
+{
+ numa_set_cpumask(cpu, 1);
+}
+
+void __cpuinit numa_remove_cpu(int cpu)
+{
+ numa_set_cpumask(cpu, 0);
+}
+
+int cpu_to_node(int cpu)
+{
+ if (early_per_cpu_ptr(x86_cpu_to_node_map)) {
+ printk(KERN_WARNING
+ "cpu_to_node(%d): usage too early!\n", cpu);
+ dump_stack();
+ return early_per_cpu_ptr(x86_cpu_to_node_map)[cpu];
+ }
+ return per_cpu(x86_cpu_to_node_map, cpu);
+}
+EXPORT_SYMBOL(cpu_to_node);
+
+/*
+ * Same function as cpu_to_node() but used if called before the
+ * per_cpu areas are setup.
+ */
+int early_cpu_to_node(int cpu)
+{
+ if (early_per_cpu_ptr(x86_cpu_to_node_map))
+ return early_per_cpu_ptr(x86_cpu_to_node_map)[cpu];
+
+ if (!per_cpu_offset(cpu)) {
+ printk(KERN_WARNING
+ "early_cpu_to_node(%d): no per_cpu area!\n", cpu);
+ dump_stack();
+ return NUMA_NO_NODE;
+ }
+ return per_cpu(x86_cpu_to_node_map, cpu);
+}
+
+/*
+ * Returns a pointer to the bitmask of CPUs on Node 'node'.
+ */
+cpumask_t *_node_to_cpumask_ptr(int node)
+{
+ if (node_to_cpumask_map == NULL) {
+ printk(KERN_WARNING
+ "_node_to_cpumask_ptr(%d): no node_to_cpumask_map!\n",
+ node);
+ dump_stack();
+ return &cpu_online_map;
+ }
+ BUG_ON(node >= nr_node_ids);
+ return &node_to_cpumask_map[node];
+}
+EXPORT_SYMBOL(_node_to_cpumask_ptr);
+
+/*
+ * Returns a bitmask of CPUs on Node 'node'.
+ */
+cpumask_t node_to_cpumask(int node)
+{
+ if (node_to_cpumask_map == NULL) {
+ printk(KERN_WARNING
+ "node_to_cpumask(%d): no node_to_cpumask_map!\n", node);
+ dump_stack();
+ return cpu_online_map;
+ }
+ BUG_ON(node >= nr_node_ids);
+ return node_to_cpumask_map[node];
+}
+EXPORT_SYMBOL(node_to_cpumask);
+
+/*
+ * --------- end of debug versions of the numa functions ---------
+ */
+
+#endif /* CONFIG_DEBUG_PER_CPU_MAPS */
+
+#endif /* X86_64_NUMA */
diff --git a/arch/x86/kernel/setup64.c b/arch/x86/kernel/setup64.c
index aee0e8200777..631ea6cc01d8 100644
--- a/arch/x86/kernel/setup64.c
+++ b/arch/x86/kernel/setup64.c
@@ -12,6 +12,7 @@
#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/kgdb.h>
+#include <linux/topology.h>
#include <asm/pda.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
@@ -34,9 +35,8 @@ struct boot_params boot_params;
cpumask_t cpu_initialized __cpuinitdata = CPU_MASK_NONE;
-struct x8664_pda *_cpu_pda[NR_CPUS] __read_mostly;
+struct x8664_pda **_cpu_pda __read_mostly;
EXPORT_SYMBOL(_cpu_pda);
-struct x8664_pda boot_cpu_pda[NR_CPUS] __cacheline_aligned;
struct desc_ptr idt_descr = { 256 * 16 - 1, (unsigned long) idt_table };
@@ -114,8 +114,10 @@ void pda_init(int cpu)
__get_free_pages(GFP_ATOMIC, IRQSTACK_ORDER);
if (!pda->irqstackptr)
panic("cannot allocate irqstack for cpu %d", cpu);
- }
+ if (pda->nodenumber == 0 && cpu_to_node(cpu) != NUMA_NO_NODE)
+ pda->nodenumber = cpu_to_node(cpu);
+ }
pda->irqstackptr += IRQSTACKSIZE-64;
}
diff --git a/arch/x86/kernel/setup_32.c b/arch/x86/kernel/setup_32.c
index 2c5f8b213e86..67fb542de750 100644
--- a/arch/x86/kernel/setup_32.c
+++ b/arch/x86/kernel/setup_32.c
@@ -67,10 +67,12 @@
#include <asm/bios_ebda.h>
#include <asm/cacheflush.h>
#include <asm/processor.h>
+#include <asm/efi.h>
/* This value is set up by the early boot code to point to the value
immediately after the boot time page tables. It contains a *physical*
address, and must not be in the .bss segment! */
+unsigned long init_pg_tables_start __initdata = ~0UL;
unsigned long init_pg_tables_end __initdata = ~0UL;
/*
@@ -237,42 +239,6 @@ static inline void copy_edd(void)
}
#endif
-int __initdata user_defined_memmap;
-
-/*
- * "mem=nopentium" disables the 4MB page tables.
- * "mem=XXX[kKmM]" defines a memory region from HIGH_MEM
- * to <mem>, overriding the bios size.
- * "memmap=XXX[KkmM]@XXX[KkmM]" defines a memory region from
- * <start> to <start>+<mem>, overriding the bios size.
- *
- * HPA tells me bootloaders need to parse mem=, so no new
- * option should be mem= [also see Documentation/i386/boot.txt]
- */
-static int __init parse_mem(char *arg)
-{
- if (!arg)
- return -EINVAL;
-
- if (strcmp(arg, "nopentium") == 0) {
- setup_clear_cpu_cap(X86_FEATURE_PSE);
- } else {
- /* If the user specifies memory size, we
- * limit the BIOS-provided memory map to
- * that size. exactmap can be used to specify
- * the exact map. mem=number can be used to
- * trim the existing memory map.
- */
- unsigned long long mem_size;
-
- mem_size = memparse(arg, &arg);
- limit_regions(mem_size);
- user_defined_memmap = 1;
- }
- return 0;
-}
-early_param("mem", parse_mem);
-
#ifdef CONFIG_PROC_VMCORE
/* elfcorehdr= specifies the location of elf core header
* stored by the crashed kernel.
@@ -395,56 +361,6 @@ unsigned long __init find_max_low_pfn(void)
return max_low_pfn;
}
-#define BIOS_LOWMEM_KILOBYTES 0x413
-
-/*
- * The BIOS places the EBDA/XBDA at the top of conventional
- * memory, and usually decreases the reported amount of
- * conventional memory (int 0x12) too. This also contains a
- * workaround for Dell systems that neglect to reserve EBDA.
- * The same workaround also avoids a problem with the AMD768MPX
- * chipset: reserve a page before VGA to prevent PCI prefetch
- * into it (errata #56). Usually the page is reserved anyways,
- * unless you have no PS/2 mouse plugged in.
- */
-static void __init reserve_ebda_region(void)
-{
- unsigned int lowmem, ebda_addr;
-
- /* To determine the position of the EBDA and the */
- /* end of conventional memory, we need to look at */
- /* the BIOS data area. In a paravirtual environment */
- /* that area is absent. We'll just have to assume */
- /* that the paravirt case can handle memory setup */
- /* correctly, without our help. */
- if (paravirt_enabled())
- return;
-
- /* end of low (conventional) memory */
- lowmem = *(unsigned short *)__va(BIOS_LOWMEM_KILOBYTES);
- lowmem <<= 10;
-
- /* start of EBDA area */
- ebda_addr = get_bios_ebda();
-
- /* Fixup: bios puts an EBDA in the top 64K segment */
- /* of conventional memory, but does not adjust lowmem. */
- if ((lowmem - ebda_addr) <= 0x10000)
- lowmem = ebda_addr;
-
- /* Fixup: bios does not report an EBDA at all. */
- /* Some old Dells seem to need 4k anyhow (bugzilla 2990) */
- if ((ebda_addr == 0) && (lowmem >= 0x9f000))
- lowmem = 0x9f000;
-
- /* Paranoia: should never happen, but... */
- if ((lowmem == 0) || (lowmem >= 0x100000))
- lowmem = 0x9f000;
-
- /* reserve all memory between lowmem and the 1MB mark */
- reserve_bootmem(lowmem, 0x100000 - lowmem, BOOTMEM_DEFAULT);
-}
-
#ifndef CONFIG_NEED_MULTIPLE_NODES
static void __init setup_bootmem_allocator(void);
static unsigned long __init setup_memory(void)
@@ -462,11 +378,13 @@ static unsigned long __init setup_memory(void)
if (max_pfn > max_low_pfn) {
highstart_pfn = max_low_pfn;
}
+ memory_present(0, 0, highend_pfn);
printk(KERN_NOTICE "%ldMB HIGHMEM available.\n",
pages_to_mb(highend_pfn - highstart_pfn));
num_physpages = highend_pfn;
high_memory = (void *) __va(highstart_pfn * PAGE_SIZE - 1) + 1;
#else
+ memory_present(0, 0, max_low_pfn);
num_physpages = max_low_pfn;
high_memory = (void *) __va(max_low_pfn * PAGE_SIZE - 1) + 1;
#endif
@@ -488,11 +406,12 @@ static void __init zone_sizes_init(void)
max_zone_pfns[ZONE_DMA] =
virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT;
max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
+ remove_all_active_ranges();
#ifdef CONFIG_HIGHMEM
max_zone_pfns[ZONE_HIGHMEM] = highend_pfn;
- add_active_range(0, 0, highend_pfn);
+ e820_register_active_regions(0, 0, highend_pfn);
#else
- add_active_range(0, 0, max_low_pfn);
+ e820_register_active_regions(0, 0, max_low_pfn);
#endif
free_area_init_nodes(max_zone_pfns);
@@ -532,10 +451,16 @@ static void __init reserve_crashkernel(void)
(unsigned long)(crash_size >> 20),
(unsigned long)(crash_base >> 20),
(unsigned long)(total_mem >> 20));
+
+ if (reserve_bootmem(crash_base, crash_size,
+ BOOTMEM_EXCLUSIVE) < 0) {
+ printk(KERN_INFO "crashkernel reservation "
+ "failed - memory is in use\n");
+ return;
+ }
+
crashk_res.start = crash_base;
crashk_res.end = crash_base + crash_size - 1;
- reserve_bootmem(crash_base, crash_size,
- BOOTMEM_DEFAULT);
} else
printk(KERN_INFO "crashkernel reservation failed - "
"you have to specify a base address\n");
@@ -552,44 +477,57 @@ static bool do_relocate_initrd = false;
static void __init reserve_initrd(void)
{
- unsigned long ramdisk_image = boot_params.hdr.ramdisk_image;
- unsigned long ramdisk_size = boot_params.hdr.ramdisk_size;
- unsigned long ramdisk_end = ramdisk_image + ramdisk_size;
- unsigned long end_of_lowmem = max_low_pfn << PAGE_SHIFT;
- unsigned long ramdisk_here;
-
- initrd_start = 0;
+ u64 ramdisk_image = boot_params.hdr.ramdisk_image;
+ u64 ramdisk_size = boot_params.hdr.ramdisk_size;
+ u64 ramdisk_end = ramdisk_image + ramdisk_size;
+ u64 end_of_lowmem = max_low_pfn << PAGE_SHIFT;
+ u64 ramdisk_here;
if (!boot_params.hdr.type_of_loader ||
!ramdisk_image || !ramdisk_size)
return; /* No initrd provided by bootloader */
- if (ramdisk_end < ramdisk_image) {
- printk(KERN_ERR "initrd wraps around end of memory, "
- "disabling initrd\n");
- return;
- }
+ initrd_start = 0;
+
if (ramdisk_size >= end_of_lowmem/2) {
+ free_early(ramdisk_image, ramdisk_end);
printk(KERN_ERR "initrd too large to handle, "
"disabling initrd\n");
return;
}
+
+ printk(KERN_INFO "old RAMDISK: %08llx - %08llx\n", ramdisk_image,
+ ramdisk_end);
+
+
if (ramdisk_end <= end_of_lowmem) {
/* All in lowmem, easy case */
- reserve_bootmem(ramdisk_image, ramdisk_size, BOOTMEM_DEFAULT);
+ /*
+ * don't need to reserve again, already reserved early
+ * in i386_start_kernel
+ */
initrd_start = ramdisk_image + PAGE_OFFSET;
initrd_end = initrd_start+ramdisk_size;
return;
}
/* We need to move the initrd down into lowmem */
- ramdisk_here = (end_of_lowmem - ramdisk_size) & PAGE_MASK;
+ ramdisk_here = find_e820_area(min_low_pfn<<PAGE_SHIFT,
+ end_of_lowmem, ramdisk_size,
+ PAGE_SIZE);
+
+ if (ramdisk_here == -1ULL)
+ panic("Cannot find place for new RAMDISK of size %lld\n",
+ ramdisk_size);
/* Note: this includes all the lowmem currently occupied by
the initrd, we rely on that fact to keep the data intact. */
- reserve_bootmem(ramdisk_here, ramdisk_size, BOOTMEM_DEFAULT);
+ reserve_early(ramdisk_here, ramdisk_here + ramdisk_size,
+ "NEW RAMDISK");
initrd_start = ramdisk_here + PAGE_OFFSET;
initrd_end = initrd_start + ramdisk_size;
+ printk(KERN_INFO "Allocated new RAMDISK: %08llx - %08llx\n",
+ ramdisk_here, ramdisk_here + ramdisk_size);
do_relocate_initrd = true;
}
@@ -598,10 +536,10 @@ static void __init reserve_initrd(void)
static void __init relocate_initrd(void)
{
- unsigned long ramdisk_image = boot_params.hdr.ramdisk_image;
- unsigned long ramdisk_size = boot_params.hdr.ramdisk_size;
- unsigned long end_of_lowmem = max_low_pfn << PAGE_SHIFT;
- unsigned long ramdisk_here;
+ u64 ramdisk_image = boot_params.hdr.ramdisk_image;
+ u64 ramdisk_size = boot_params.hdr.ramdisk_size;
+ u64 end_of_lowmem = max_low_pfn << PAGE_SHIFT;
+ u64 ramdisk_here;
unsigned long slop, clen, mapaddr;
char *p, *q;
@@ -618,6 +556,10 @@ static void __init relocate_initrd(void)
p = (char *)__va(ramdisk_image);
memcpy(q, p, clen);
q += clen;
+ /* need to free these low pages...*/
+ printk(KERN_INFO "Freeing old partial RAMDISK %08llx-%08llx\n",
+ ramdisk_image, ramdisk_image + clen - 1);
+ free_bootmem(ramdisk_image, clen);
ramdisk_image += clen;
ramdisk_size -= clen;
}
@@ -636,47 +578,44 @@ static void __init relocate_initrd(void)
ramdisk_image += clen;
ramdisk_size -= clen;
}
+ /* high pages is not converted by early_res_to_bootmem */
+ ramdisk_image = boot_params.hdr.ramdisk_image;
+ ramdisk_size = boot_params.hdr.ramdisk_size;
+ printk(KERN_INFO "Copied RAMDISK from %016llx - %016llx to %08llx - %08llx\n",
+ ramdisk_image, ramdisk_image + ramdisk_size - 1,
+ ramdisk_here, ramdisk_here + ramdisk_size - 1);
}
#endif /* CONFIG_BLK_DEV_INITRD */
void __init setup_bootmem_allocator(void)
{
- unsigned long bootmap_size;
+ int i;
+ unsigned long bootmap_size, bootmap;
/*
* Initialize the boot-time allocator (with low memory only):
*/
- bootmap_size = init_bootmem(min_low_pfn, max_low_pfn);
-
- register_bootmem_low_pages(max_low_pfn);
-
- /*
- * Reserve the bootmem bitmap itself as well. We do this in two
- * steps (first step was init_bootmem()) because this catches
- * the (very unlikely) case of us accidentally initializing the
- * bootmem allocator with an invalid RAM area.
- */
- reserve_bootmem(__pa_symbol(_text), (PFN_PHYS(min_low_pfn) +
- bootmap_size + PAGE_SIZE-1) - __pa_symbol(_text),
- BOOTMEM_DEFAULT);
-
- /*
- * reserve physical page 0 - it's a special BIOS page on many boxes,
- * enabling clean reboots, SMP operation, laptop functions.
- */
- reserve_bootmem(0, PAGE_SIZE, BOOTMEM_DEFAULT);
-
- /* reserve EBDA region */
- reserve_ebda_region();
-
-#ifdef CONFIG_SMP
- /*
- * But first pinch a few for the stack/trampoline stuff
- * FIXME: Don't need the extra page at 4K, but need to fix
- * trampoline before removing it. (see the GDT stuff)
- */
- reserve_bootmem(PAGE_SIZE, PAGE_SIZE, BOOTMEM_DEFAULT);
+ bootmap_size = bootmem_bootmap_pages(max_low_pfn)<<PAGE_SHIFT;
+ bootmap = find_e820_area(min_low_pfn<<PAGE_SHIFT,
+ max_pfn_mapped<<PAGE_SHIFT, bootmap_size,
+ PAGE_SIZE);
+ if (bootmap == -1L)
+ panic("Cannot find bootmem map of size %ld\n", bootmap_size);
+ reserve_early(bootmap, bootmap + bootmap_size, "BOOTMAP");
+#ifdef CONFIG_BLK_DEV_INITRD
+ reserve_initrd();
#endif
+ bootmap_size = init_bootmem(bootmap >> PAGE_SHIFT, max_low_pfn);
+ printk(KERN_INFO " mapped low ram: 0 - %08lx\n",
+ max_pfn_mapped<<PAGE_SHIFT);
+ printk(KERN_INFO " low ram: %08lx - %08lx\n",
+ min_low_pfn<<PAGE_SHIFT, max_low_pfn<<PAGE_SHIFT);
+ printk(KERN_INFO " bootmap %08lx - %08lx\n",
+ bootmap, bootmap + bootmap_size);
+ for_each_online_node(i)
+ free_bootmem_with_active_regions(i, max_low_pfn);
+ early_res_to_bootmem(0, max_low_pfn<<PAGE_SHIFT);
+
#ifdef CONFIG_ACPI_SLEEP
/*
* Reserve low memory region for sleep support.
@@ -689,10 +628,6 @@ void __init setup_bootmem_allocator(void)
*/
find_smp_config();
#endif
-#ifdef CONFIG_BLK_DEV_INITRD
- reserve_initrd();
-#endif
- numa_kva_reserve();
reserve_crashkernel();
reserve_ibft_region();
@@ -725,12 +660,6 @@ static void set_mca_bus(int x)
static void set_mca_bus(int x) { }
#endif
-/* Overridden in paravirt.c if CONFIG_PARAVIRT */
-char * __init __attribute__((weak)) memory_setup(void)
-{
- return machine_specific_memory_setup();
-}
-
#ifdef CONFIG_NUMA
/*
* In the golden day, when everything among i386 and x86_64 will be
@@ -743,6 +672,12 @@ int x86_cpu_to_node_map_init[NR_CPUS] = {
DEFINE_PER_CPU(int, x86_cpu_to_node_map) = NUMA_NO_NODE;
#endif
+/* Overridden in paravirt.c if CONFIG_PARAVIRT */
+char * __init __attribute__((weak)) memory_setup(void)
+{
+ return machine_specific_memory_setup();
+}
+
/*
* Determine if we were loaded by an EFI loader. If so, then we have also been
* passed the efi memmap, systab, etc., so we should use these data structures
@@ -758,11 +693,14 @@ void __init setup_arch(char **cmdline_p)
pre_setup_arch_hook();
early_cpu_init();
early_ioremap_init();
+ reserve_setup_data();
#ifdef CONFIG_EFI
if (!strncmp((char *)&boot_params.efi_info.efi_loader_signature,
- "EL32", 4))
+ "EL32", 4)) {
efi_enabled = 1;
+ efi_reserve_early();
+ }
#endif
ROOT_DEV = old_decode_dev(boot_params.hdr.root_dev);
@@ -786,8 +724,7 @@ void __init setup_arch(char **cmdline_p)
#endif
ARCH_SETUP
- printk(KERN_INFO "BIOS-provided physical RAM map:\n");
- print_memory_map(memory_setup());
+ setup_memory_map();
copy_edd();
@@ -805,12 +742,11 @@ void __init setup_arch(char **cmdline_p)
bss_resource.start = virt_to_phys(&__bss_start);
bss_resource.end = virt_to_phys(&__bss_stop)-1;
+ parse_setup_data();
+
parse_early_param();
- if (user_defined_memmap) {
- printk(KERN_INFO "user-defined physical RAM map:\n");
- print_memory_map("user");
- }
+ finish_e820_parsing();
strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE);
*cmdline_p = command_line;
@@ -818,11 +754,22 @@ void __init setup_arch(char **cmdline_p)
if (efi_enabled)
efi_init();
+ e820_register_active_regions(0, 0, -1UL);
+ /*
+ * partially used pages are not usable - thus
+ * we are rounding upwards:
+ */
+ max_pfn = e820_end_of_ram();
+
+ /* preallocate 4k for mptable mpc */
+ early_reserve_e820_mpc_new();
/* update e820 for memory not covered by WB MTRRs */
- propagate_e820_map();
mtrr_bp_init();
- if (mtrr_trim_uncached_memory(max_pfn))
- propagate_e820_map();
+ if (mtrr_trim_uncached_memory(max_pfn)) {
+ remove_all_active_ranges();
+ e820_register_active_regions(0, 0, -1UL);
+ max_pfn = e820_end_of_ram();
+ }
max_low_pfn = setup_memory();
@@ -849,9 +796,6 @@ void __init setup_arch(char **cmdline_p)
* not to exceed the 8Mb limit.
*/
-#ifdef CONFIG_SMP
- smp_alloc_memory(); /* AP processor realmode stacks in low memory*/
-#endif
paging_init();
/*
@@ -881,18 +825,6 @@ void __init setup_arch(char **cmdline_p)
io_delay_init();
-#ifdef CONFIG_X86_SMP
- /*
- * setup to use the early static init tables during kernel startup
- * X86_SMP will exclude sub-arches that don't deal well with it.
- */
- x86_cpu_to_apicid_early_ptr = (void *)x86_cpu_to_apicid_init;
- x86_bios_cpu_apicid_early_ptr = (void *)x86_bios_cpu_apicid_init;
-#ifdef CONFIG_NUMA
- x86_cpu_to_node_map_early_ptr = (void *)x86_cpu_to_node_map_init;
-#endif
-#endif
-
#ifdef CONFIG_X86_GENERICARCH
generic_apic_probe();
#endif
@@ -908,21 +840,20 @@ void __init setup_arch(char **cmdline_p)
#ifdef CONFIG_ACPI
acpi_boot_init();
-
+#endif
+#if defined(CONFIG_X86_MPPARSE) || defined(CONFIG_X86_VISWS)
+ if (smp_found_config)
+ get_smp_config();
+#endif
#if defined(CONFIG_SMP) && defined(CONFIG_X86_PC)
if (def_to_bigsmp)
printk(KERN_WARNING "More than 8 CPUs detected and "
"CONFIG_X86_PC cannot handle it.\nUse "
"CONFIG_X86_GENERICARCH or CONFIG_X86_BIGSMP.\n");
#endif
-#endif
-#ifdef CONFIG_X86_LOCAL_APIC
- if (smp_found_config)
- get_smp_config();
-#endif
- e820_register_memory();
- e820_mark_nosave_regions();
+ e820_setup_gap();
+ e820_mark_nosave_regions(max_low_pfn);
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
diff --git a/arch/x86/kernel/setup_64.c b/arch/x86/kernel/setup_64.c
index 6dff1286ad8a..a006bfc91c20 100644
--- a/arch/x86/kernel/setup_64.c
+++ b/arch/x86/kernel/setup_64.c
@@ -56,6 +56,7 @@
#include <asm/desc.h>
#include <video/edid.h>
#include <asm/e820.h>
+#include <asm/mpspec.h>
#include <asm/dma.h>
#include <asm/gart.h>
#include <asm/mpspec.h>
@@ -71,6 +72,7 @@
#include <asm/topology.h>
#include <asm/trampoline.h>
#include <asm/pat.h>
+#include <asm/mmconfig.h>
#include <mach_apic.h>
#ifdef CONFIG_PARAVIRT
@@ -95,8 +97,6 @@ int bootloader_type;
unsigned long saved_video_mode;
-int force_mwait __cpuinitdata;
-
/*
* Early DMI memory
*/
@@ -118,7 +118,7 @@ EXPORT_SYMBOL_GPL(edid_info);
extern int root_mountflags;
-char __initdata command_line[COMMAND_LINE_SIZE];
+static char __initdata command_line[COMMAND_LINE_SIZE];
static struct resource standard_io_resources[] = {
{ .name = "dma1", .start = 0x00, .end = 0x1f,
@@ -243,7 +243,7 @@ static void __init reserve_crashkernel(void)
return;
}
- if (reserve_bootmem(crash_base, crash_size,
+ if (reserve_bootmem_generic(crash_base, crash_size,
BOOTMEM_EXCLUSIVE) < 0) {
printk(KERN_INFO "crashkernel reservation failed - "
"memory is in use\n");
@@ -271,40 +271,6 @@ void __attribute__((weak)) __init memory_setup(void)
machine_specific_memory_setup();
}
-static void __init parse_setup_data(void)
-{
- struct setup_data *data;
- unsigned long pa_data;
-
- if (boot_params.hdr.version < 0x0209)
- return;
- pa_data = boot_params.hdr.setup_data;
- while (pa_data) {
- data = early_ioremap(pa_data, PAGE_SIZE);
- switch (data->type) {
- default:
- break;
- }
-#ifndef CONFIG_DEBUG_BOOT_PARAMS
- free_early(pa_data, pa_data+sizeof(*data)+data->len);
-#endif
- pa_data = data->next;
- early_iounmap(data, PAGE_SIZE);
- }
-}
-
-#ifdef CONFIG_PCI_MMCONFIG
-extern void __cpuinit fam10h_check_enable_mmcfg(void);
-extern void __init check_enable_amd_mmconf_dmi(void);
-#else
-void __cpuinit fam10h_check_enable_mmcfg(void)
-{
-}
-void __init check_enable_amd_mmconf_dmi(void)
-{
-}
-#endif
-
/*
* setup_arch - architecture-specific boot-time initializations
*
@@ -329,8 +295,10 @@ void __init setup_arch(char **cmdline_p)
#endif
#ifdef CONFIG_EFI
if (!strncmp((char *)&boot_params.efi_info.efi_loader_signature,
- "EL64", 4))
+ "EL64", 4)) {
efi_enabled = 1;
+ efi_reserve_early();
+ }
#endif
ARCH_SETUP
@@ -361,6 +329,11 @@ void __init setup_arch(char **cmdline_p)
parse_early_param();
+#ifdef CONFIG_PCI
+ if (pci_early_dump_regs)
+ early_dump_pci_devices();
+#endif
+
#ifdef CONFIG_PROVIDE_OHCI1394_DMA_INIT
if (init_ohci1394_dma_early)
init_ohci1394_dma_on_all_controllers();
@@ -381,9 +354,13 @@ void __init setup_arch(char **cmdline_p)
* we are rounding upwards:
*/
end_pfn = e820_end_of_ram();
+
+ /* pre allocte 4k for mptable mpc */
+ early_reserve_e820_mpc_new();
/* update e820 for memory not covered by WB MTRRs */
mtrr_bp_init();
if (mtrr_trim_uncached_memory(end_pfn)) {
+ remove_all_active_ranges();
e820_register_active_regions(0, 0, -1UL);
end_pfn = e820_end_of_ram();
}
@@ -392,7 +369,7 @@ void __init setup_arch(char **cmdline_p)
check_efer();
- max_pfn_mapped = init_memory_mapping(0, (max_pfn_mapped << PAGE_SHIFT));
+ max_pfn_mapped = init_memory_mapping(0, (end_pfn << PAGE_SHIFT));
if (efi_enabled)
efi_init();
@@ -406,15 +383,6 @@ void __init setup_arch(char **cmdline_p)
kvmclock_init();
#endif
-#ifdef CONFIG_SMP
- /* setup to use the early static init tables during kernel startup */
- x86_cpu_to_apicid_early_ptr = (void *)x86_cpu_to_apicid_init;
- x86_bios_cpu_apicid_early_ptr = (void *)x86_bios_cpu_apicid_init;
-#ifdef CONFIG_NUMA
- x86_cpu_to_node_map_early_ptr = (void *)x86_cpu_to_node_map_init;
-#endif
-#endif
-
#ifdef CONFIG_ACPI
/*
* Initialize the ACPI boot-time table parser (gets the RSDP and SDT).
@@ -453,13 +421,12 @@ void __init setup_arch(char **cmdline_p)
acpi_reserve_bootmem();
#endif
- if (efi_enabled)
- efi_reserve_bootmem();
-
+#ifdef CONFIG_X86_MPPARSE
/*
* Find and reserve possible boot-time SMP configuration:
*/
find_smp_config();
+#endif
#ifdef CONFIG_BLK_DEV_INITRD
if (boot_params.hdr.type_of_loader && boot_params.hdr.ramdisk_image) {
unsigned long ramdisk_image = boot_params.hdr.ramdisk_image;
@@ -502,11 +469,13 @@ void __init setup_arch(char **cmdline_p)
init_cpu_to_node();
+#ifdef CONFIG_X86_MPPARSE
/*
* get boot-time SMP configuration:
*/
if (smp_found_config)
get_smp_config();
+#endif
init_apic_mappings();
ioapic_init_mappings();
@@ -516,7 +485,7 @@ void __init setup_arch(char **cmdline_p)
* We trust e820 completely. No explicit ROM probing in memory.
*/
e820_reserve_resources();
- e820_mark_nosave_regions();
+ e820_mark_nosave_regions(end_pfn);
/* request I/O space for devices used on all i[345]86 PCs */
for (i = 0; i < ARRAY_SIZE(standard_io_resources); i++)
@@ -537,7 +506,7 @@ void __init setup_arch(char **cmdline_p)
check_enable_amd_mmconf_dmi();
}
-static int __cpuinit get_model_name(struct cpuinfo_x86 *c)
+int __cpuinit get_model_name(struct cpuinfo_x86 *c)
{
unsigned int *v;
@@ -553,7 +522,7 @@ static int __cpuinit get_model_name(struct cpuinfo_x86 *c)
}
-static void __cpuinit display_cacheinfo(struct cpuinfo_x86 *c)
+void __cpuinit display_cacheinfo(struct cpuinfo_x86 *c)
{
unsigned int n, dummy, eax, ebx, ecx, edx;
@@ -585,228 +554,6 @@ static void __cpuinit display_cacheinfo(struct cpuinfo_x86 *c)
}
}
-#ifdef CONFIG_NUMA
-static int __cpuinit nearby_node(int apicid)
-{
- int i, node;
-
- for (i = apicid - 1; i >= 0; i--) {
- node = apicid_to_node[i];
- if (node != NUMA_NO_NODE && node_online(node))
- return node;
- }
- for (i = apicid + 1; i < MAX_LOCAL_APIC; i++) {
- node = apicid_to_node[i];
- if (node != NUMA_NO_NODE && node_online(node))
- return node;
- }
- return first_node(node_online_map); /* Shouldn't happen */
-}
-#endif
-
-/*
- * On a AMD dual core setup the lower bits of the APIC id distingush the cores.
- * Assumes number of cores is a power of two.
- */
-static void __cpuinit amd_detect_cmp(struct cpuinfo_x86 *c)
-{
-#ifdef CONFIG_SMP
- unsigned bits;
-#ifdef CONFIG_NUMA
- int cpu = smp_processor_id();
- int node = 0;
- unsigned apicid = hard_smp_processor_id();
-#endif
- bits = c->x86_coreid_bits;
-
- /* Low order bits define the core id (index of core in socket) */
- c->cpu_core_id = c->initial_apicid & ((1 << bits)-1);
- /* Convert the initial APIC ID into the socket ID */
- c->phys_proc_id = c->initial_apicid >> bits;
-
-#ifdef CONFIG_NUMA
- node = c->phys_proc_id;
- if (apicid_to_node[apicid] != NUMA_NO_NODE)
- node = apicid_to_node[apicid];
- if (!node_online(node)) {
- /* Two possibilities here:
- - The CPU is missing memory and no node was created.
- In that case try picking one from a nearby CPU
- - The APIC IDs differ from the HyperTransport node IDs
- which the K8 northbridge parsing fills in.
- Assume they are all increased by a constant offset,
- but in the same order as the HT nodeids.
- If that doesn't result in a usable node fall back to the
- path for the previous case. */
-
- int ht_nodeid = c->initial_apicid;
-
- if (ht_nodeid >= 0 &&
- apicid_to_node[ht_nodeid] != NUMA_NO_NODE)
- node = apicid_to_node[ht_nodeid];
- /* Pick a nearby node */
- if (!node_online(node))
- node = nearby_node(apicid);
- }
- numa_set_node(cpu, node);
-
- printk(KERN_INFO "CPU %d/%x -> Node %d\n", cpu, apicid, node);
-#endif
-#endif
-}
-
-static void __cpuinit early_init_amd_mc(struct cpuinfo_x86 *c)
-{
-#ifdef CONFIG_SMP
- unsigned bits, ecx;
-
- /* Multi core CPU? */
- if (c->extended_cpuid_level < 0x80000008)
- return;
-
- ecx = cpuid_ecx(0x80000008);
-
- c->x86_max_cores = (ecx & 0xff) + 1;
-
- /* CPU telling us the core id bits shift? */
- bits = (ecx >> 12) & 0xF;
-
- /* Otherwise recompute */
- if (bits == 0) {
- while ((1 << bits) < c->x86_max_cores)
- bits++;
- }
-
- c->x86_coreid_bits = bits;
-
-#endif
-}
-
-#define ENABLE_C1E_MASK 0x18000000
-#define CPUID_PROCESSOR_SIGNATURE 1
-#define CPUID_XFAM 0x0ff00000
-#define CPUID_XFAM_K8 0x00000000
-#define CPUID_XFAM_10H 0x00100000
-#define CPUID_XFAM_11H 0x00200000
-#define CPUID_XMOD 0x000f0000
-#define CPUID_XMOD_REV_F 0x00040000
-
-/* AMD systems with C1E don't have a working lAPIC timer. Check for that. */
-static __cpuinit int amd_apic_timer_broken(void)
-{
- u32 lo, hi, eax = cpuid_eax(CPUID_PROCESSOR_SIGNATURE);
-
- switch (eax & CPUID_XFAM) {
- case CPUID_XFAM_K8:
- if ((eax & CPUID_XMOD) < CPUID_XMOD_REV_F)
- break;
- case CPUID_XFAM_10H:
- case CPUID_XFAM_11H:
- rdmsr(MSR_K8_ENABLE_C1E, lo, hi);
- if (lo & ENABLE_C1E_MASK)
- return 1;
- break;
- default:
- /* err on the side of caution */
- return 1;
- }
- return 0;
-}
-
-static void __cpuinit early_init_amd(struct cpuinfo_x86 *c)
-{
- early_init_amd_mc(c);
-
- /* c->x86_power is 8000_0007 edx. Bit 8 is constant TSC */
- if (c->x86_power & (1<<8))
- set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC);
-}
-
-static void __cpuinit init_amd(struct cpuinfo_x86 *c)
-{
- unsigned level;
-
-#ifdef CONFIG_SMP
- unsigned long value;
-
- /*
- * Disable TLB flush filter by setting HWCR.FFDIS on K8
- * bit 6 of msr C001_0015
- *
- * Errata 63 for SH-B3 steppings
- * Errata 122 for all steppings (F+ have it disabled by default)
- */
- if (c->x86 == 15) {
- rdmsrl(MSR_K8_HWCR, value);
- value |= 1 << 6;
- wrmsrl(MSR_K8_HWCR, value);
- }
-#endif
-
- /* Bit 31 in normal CPUID used for nonstandard 3DNow ID;
- 3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway */
- clear_cpu_cap(c, 0*32+31);
-
- /* On C+ stepping K8 rep microcode works well for copy/memset */
- level = cpuid_eax(1);
- if (c->x86 == 15 && ((level >= 0x0f48 && level < 0x0f50) ||
- level >= 0x0f58))
- set_cpu_cap(c, X86_FEATURE_REP_GOOD);
- if (c->x86 == 0x10 || c->x86 == 0x11)
- set_cpu_cap(c, X86_FEATURE_REP_GOOD);
-
- /* Enable workaround for FXSAVE leak */
- if (c->x86 >= 6)
- set_cpu_cap(c, X86_FEATURE_FXSAVE_LEAK);
-
- level = get_model_name(c);
- if (!level) {
- switch (c->x86) {
- case 15:
- /* Should distinguish Models here, but this is only
- a fallback anyways. */
- strcpy(c->x86_model_id, "Hammer");
- break;
- }
- }
- display_cacheinfo(c);
-
- /* Multi core CPU? */
- if (c->extended_cpuid_level >= 0x80000008)
- amd_detect_cmp(c);
-
- if (c->extended_cpuid_level >= 0x80000006 &&
- (cpuid_edx(0x80000006) & 0xf000))
- num_cache_leaves = 4;
- else
- num_cache_leaves = 3;
-
- if (c->x86 == 0xf || c->x86 == 0x10 || c->x86 == 0x11)
- set_cpu_cap(c, X86_FEATURE_K8);
-
- /* MFENCE stops RDTSC speculation */
- set_cpu_cap(c, X86_FEATURE_MFENCE_RDTSC);
-
- if (c->x86 == 0x10)
- fam10h_check_enable_mmcfg();
-
- if (amd_apic_timer_broken())
- disable_apic_timer = 1;
-
- if (c == &boot_cpu_data && c->x86 >= 0xf && c->x86 <= 0x11) {
- unsigned long long tseg;
-
- /*
- * Split up direct mapping around the TSEG SMM area.
- * Don't do it for gbpages because there seems very little
- * benefit in doing so.
- */
- if (!rdmsrl_safe(MSR_K8_TSEG_ADDR, &tseg) &&
- (tseg >> PMD_SHIFT) < (max_pfn_mapped >> (PMD_SHIFT-PAGE_SHIFT)))
- set_memory_4k((unsigned long)__va(tseg), 1);
- }
-}
-
void __cpuinit detect_ht(struct cpuinfo_x86 *c)
{
#ifdef CONFIG_SMP
@@ -857,123 +604,6 @@ out:
#endif
}
-/*
- * find out the number of processor cores on the die
- */
-static int __cpuinit intel_num_cpu_cores(struct cpuinfo_x86 *c)
-{
- unsigned int eax, t;
-
- if (c->cpuid_level < 4)
- return 1;
-
- cpuid_count(4, 0, &eax, &t, &t, &t);
-
- if (eax & 0x1f)
- return ((eax >> 26) + 1);
- else
- return 1;
-}
-
-static void __cpuinit srat_detect_node(void)
-{
-#ifdef CONFIG_NUMA
- unsigned node;
- int cpu = smp_processor_id();
- int apicid = hard_smp_processor_id();
-
- /* Don't do the funky fallback heuristics the AMD version employs
- for now. */
- node = apicid_to_node[apicid];
- if (node == NUMA_NO_NODE || !node_online(node))
- node = first_node(node_online_map);
- numa_set_node(cpu, node);
-
- printk(KERN_INFO "CPU %d/%x -> Node %d\n", cpu, apicid, node);
-#endif
-}
-
-static void __cpuinit early_init_intel(struct cpuinfo_x86 *c)
-{
- if ((c->x86 == 0xf && c->x86_model >= 0x03) ||
- (c->x86 == 0x6 && c->x86_model >= 0x0e))
- set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC);
-}
-
-static void __cpuinit init_intel(struct cpuinfo_x86 *c)
-{
- /* Cache sizes */
- unsigned n;
-
- init_intel_cacheinfo(c);
- if (c->cpuid_level > 9) {
- unsigned eax = cpuid_eax(10);
- /* Check for version and the number of counters */
- if ((eax & 0xff) && (((eax>>8) & 0xff) > 1))
- set_cpu_cap(c, X86_FEATURE_ARCH_PERFMON);
- }
-
- if (cpu_has_ds) {
- unsigned int l1, l2;
- rdmsr(MSR_IA32_MISC_ENABLE, l1, l2);
- if (!(l1 & (1<<11)))
- set_cpu_cap(c, X86_FEATURE_BTS);
- if (!(l1 & (1<<12)))
- set_cpu_cap(c, X86_FEATURE_PEBS);
- }
-
-
- if (cpu_has_bts)
- ds_init_intel(c);
-
- n = c->extended_cpuid_level;
- if (n >= 0x80000008) {
- unsigned eax = cpuid_eax(0x80000008);
- c->x86_virt_bits = (eax >> 8) & 0xff;
- c->x86_phys_bits = eax & 0xff;
- /* CPUID workaround for Intel 0F34 CPU */
- if (c->x86_vendor == X86_VENDOR_INTEL &&
- c->x86 == 0xF && c->x86_model == 0x3 &&
- c->x86_mask == 0x4)
- c->x86_phys_bits = 36;
- }
-
- if (c->x86 == 15)
- c->x86_cache_alignment = c->x86_clflush_size * 2;
- if (c->x86 == 6)
- set_cpu_cap(c, X86_FEATURE_REP_GOOD);
- set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC);
- c->x86_max_cores = intel_num_cpu_cores(c);
-
- srat_detect_node();
-}
-
-static void __cpuinit early_init_centaur(struct cpuinfo_x86 *c)
-{
- if (c->x86 == 0x6 && c->x86_model >= 0xf)
- set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC);
-}
-
-static void __cpuinit init_centaur(struct cpuinfo_x86 *c)
-{
- /* Cache sizes */
- unsigned n;
-
- n = c->extended_cpuid_level;
- if (n >= 0x80000008) {
- unsigned eax = cpuid_eax(0x80000008);
- c->x86_virt_bits = (eax >> 8) & 0xff;
- c->x86_phys_bits = eax & 0xff;
- }
-
- if (c->x86 == 0x6 && c->x86_model >= 0xf) {
- c->x86_cache_alignment = c->x86_clflush_size * 2;
- set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC);
- set_cpu_cap(c, X86_FEATURE_REP_GOOD);
- }
- set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC);
-}
-
static void __cpuinit get_cpu_vendor(struct cpuinfo_x86 *c)
{
char *v = c->x86_vendor_id;
@@ -988,6 +618,14 @@ static void __cpuinit get_cpu_vendor(struct cpuinfo_x86 *c)
c->x86_vendor = X86_VENDOR_UNKNOWN;
}
+// FIXME: Needs to use cpu_vendor_dev_register
+extern void __cpuinit early_init_amd(struct cpuinfo_x86 *c);
+extern void __cpuinit init_amd(struct cpuinfo_x86 *c);
+extern void __cpuinit early_init_intel(struct cpuinfo_x86 *c);
+extern void __cpuinit init_intel(struct cpuinfo_x86 *c);
+extern void __cpuinit early_init_centaur(struct cpuinfo_x86 *c);
+extern void __cpuinit init_centaur(struct cpuinfo_x86 *c);
+
/* Do some early cpuid on the boot CPU to get some parameter that are
needed before check_bugs. Everything advanced is in identify_cpu
below. */
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 56078d61c793..722b24b710c4 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -59,7 +59,6 @@
#include <asm/pgtable.h>
#include <asm/tlbflush.h>
#include <asm/mtrr.h>
-#include <asm/nmi.h>
#include <asm/vmi.h>
#include <asm/genapic.h>
#include <linux/mc146818rtc.h>
@@ -68,22 +67,6 @@
#include <mach_wakecpu.h>
#include <smpboot_hooks.h>
-/*
- * FIXME: For x86_64, those are defined in other files. But moving them here,
- * would make the setup areas dependent on smp, which is a loss. When we
- * integrate apic between arches, we can probably do a better job, but
- * right now, they'll stay here -- glommer
- */
-
-/* which logical CPU number maps to which CPU (physical APIC ID) */
-u16 x86_cpu_to_apicid_init[NR_CPUS] __initdata =
- { [0 ... NR_CPUS-1] = BAD_APICID };
-void *x86_cpu_to_apicid_early_ptr;
-
-u16 x86_bios_cpu_apicid_init[NR_CPUS] __initdata
- = { [0 ... NR_CPUS-1] = BAD_APICID };
-void *x86_bios_cpu_apicid_early_ptr;
-
#ifdef CONFIG_X86_32
u8 apicid_2_node[MAX_APICID];
static int low_mappings;
@@ -487,7 +470,7 @@ void __cpuinit set_cpu_sibling_map(int cpu)
cpu_set(cpu, cpu_sibling_setup_map);
if (smp_num_siblings > 1) {
- for_each_cpu_mask(i, cpu_sibling_setup_map) {
+ for_each_cpu_mask_nr(i, cpu_sibling_setup_map) {
if (c->phys_proc_id == cpu_data(i).phys_proc_id &&
c->cpu_core_id == cpu_data(i).cpu_core_id) {
cpu_set(i, per_cpu(cpu_sibling_map, cpu));
@@ -510,7 +493,7 @@ void __cpuinit set_cpu_sibling_map(int cpu)
return;
}
- for_each_cpu_mask(i, cpu_sibling_setup_map) {
+ for_each_cpu_mask_nr(i, cpu_sibling_setup_map) {
if (per_cpu(cpu_llc_id, cpu) != BAD_APICID &&
per_cpu(cpu_llc_id, cpu) == per_cpu(cpu_llc_id, i)) {
cpu_set(i, c->llc_shared_map);
@@ -555,23 +538,6 @@ cpumask_t cpu_coregroup_map(int cpu)
return c->llc_shared_map;
}
-#ifdef CONFIG_X86_32
-/*
- * We are called very early to get the low memory for the
- * SMP bootup trampoline page.
- */
-void __init smp_alloc_memory(void)
-{
- trampoline_base = alloc_bootmem_low_pages(PAGE_SIZE);
- /*
- * Has to be in very low memory so we can execute
- * real-mode AP code.
- */
- if (__pa(trampoline_base) >= 0x9F000)
- BUG();
-}
-#endif
-
static void impress_friends(void)
{
int cpu;
@@ -832,6 +798,45 @@ static void __cpuinit do_fork_idle(struct work_struct *work)
complete(&c_idle->done);
}
+#ifdef CONFIG_X86_64
+/*
+ * Allocate node local memory for the AP pda.
+ *
+ * Must be called after the _cpu_pda pointer table is initialized.
+ */
+static int __cpuinit get_local_pda(int cpu)
+{
+ struct x8664_pda *oldpda, *newpda;
+ unsigned long size = sizeof(struct x8664_pda);
+ int node = cpu_to_node(cpu);
+
+ if (cpu_pda(cpu) && !cpu_pda(cpu)->in_bootmem)
+ return 0;
+
+ oldpda = cpu_pda(cpu);
+ newpda = kmalloc_node(size, GFP_ATOMIC, node);
+ if (!newpda) {
+ printk(KERN_ERR "Could not allocate node local PDA "
+ "for CPU %d on node %d\n", cpu, node);
+
+ if (oldpda)
+ return 0; /* have a usable pda */
+ else
+ return -1;
+ }
+
+ if (oldpda) {
+ memcpy(newpda, oldpda, size);
+ if (!after_bootmem)
+ free_bootmem((unsigned long)oldpda, size);
+ }
+
+ newpda->in_bootmem = 0;
+ cpu_pda(cpu) = newpda;
+ return 0;
+}
+#endif /* CONFIG_X86_64 */
+
static int __cpuinit do_boot_cpu(int apicid, int cpu)
/*
* NOTE - on most systems this is a PHYSICAL apic ID, but on multiquad
@@ -857,19 +862,11 @@ static int __cpuinit do_boot_cpu(int apicid, int cpu)
}
/* Allocate node local memory for AP pdas */
- if (cpu_pda(cpu) == &boot_cpu_pda[cpu]) {
- struct x8664_pda *newpda, *pda;
- int node = cpu_to_node(cpu);
- pda = cpu_pda(cpu);
- newpda = kmalloc_node(sizeof(struct x8664_pda), GFP_ATOMIC,
- node);
- if (newpda) {
- memcpy(newpda, pda, sizeof(struct x8664_pda));
- cpu_pda(cpu) = newpda;
- } else
- printk(KERN_ERR
- "Could not allocate node local PDA for CPU %d on node %d\n",
- cpu, node);
+ if (cpu > 0) {
+ boot_error = get_local_pda(cpu);
+ if (boot_error)
+ goto restore_state;
+ /* if can't get pda memory, can't start cpu */
}
#endif
@@ -988,11 +985,13 @@ do_rest:
}
}
+restore_state:
+
if (boot_error) {
/* Try to put things back the way they were before ... */
unmap_cpu_to_logical_apicid(cpu);
#ifdef CONFIG_X86_64
- clear_node_cpumask(cpu); /* was set by numa_add_cpu */
+ numa_remove_cpu(cpu); /* was set by numa_add_cpu */
#endif
cpu_clear(cpu, cpu_callout_map); /* was set by do_boot_cpu() */
cpu_clear(cpu, cpu_initialized); /* was set by cpu_init() */
@@ -1158,9 +1157,11 @@ static int __init smp_sanity_check(unsigned max_cpus)
* If SMP should be disabled, then really disable it!
*/
if (!max_cpus) {
- printk(KERN_INFO "SMP mode deactivated,"
- "forcing use of dummy APIC emulation.\n");
+ printk(KERN_INFO "SMP mode deactivated.\n");
smpboot_clear_io_apic();
+
+ localise_nmi_watchdog();
+
#ifdef CONFIG_X86_32
connect_bsp_APIC();
#endif
@@ -1301,7 +1302,7 @@ static void remove_siblinginfo(int cpu)
int sibling;
struct cpuinfo_x86 *c = &cpu_data(cpu);
- for_each_cpu_mask(sibling, per_cpu(cpu_core_map, cpu)) {
+ for_each_cpu_mask_nr(sibling, per_cpu(cpu_core_map, cpu)) {
cpu_clear(cpu, per_cpu(cpu_core_map, sibling));
/*/
* last thread sibling in this cpu core going down
@@ -1310,7 +1311,7 @@ static void remove_siblinginfo(int cpu)
cpu_data(sibling).booted_cores--;
}
- for_each_cpu_mask(sibling, per_cpu(cpu_sibling_map, cpu))
+ for_each_cpu_mask_nr(sibling, per_cpu(cpu_sibling_map, cpu))
cpu_clear(cpu, per_cpu(cpu_sibling_map, sibling));
cpus_clear(per_cpu(cpu_sibling_map, cpu));
cpus_clear(per_cpu(cpu_core_map, cpu));
@@ -1364,6 +1365,8 @@ __init void prefill_possible_map(void)
for (i = 0; i < possible; i++)
cpu_set(i, cpu_possible_map);
+
+ nr_cpu_ids = possible;
}
static void __ref remove_cpu_from_maps(int cpu)
@@ -1374,7 +1377,7 @@ static void __ref remove_cpu_from_maps(int cpu)
cpu_clear(cpu, cpu_callin_map);
/* was set by cpu_init() */
clear_bit(cpu, (unsigned long *)&cpu_initialized);
- clear_node_cpumask(cpu);
+ numa_remove_cpu(cpu);
#endif
}
diff --git a/arch/x86/kernel/srat_32.c b/arch/x86/kernel/srat_32.c
index 70e4a374b4e8..e9d91720a40f 100644
--- a/arch/x86/kernel/srat_32.c
+++ b/arch/x86/kernel/srat_32.c
@@ -31,6 +31,7 @@
#include <asm/srat.h>
#include <asm/topology.h>
#include <asm/smp.h>
+#include <asm/e820.h>
/*
* proximity macros and definitions
@@ -244,12 +245,13 @@ static int __init acpi20_parse_srat(struct acpi_table_srat *sratp)
printk("chunk %d nid %d start_pfn %08lx end_pfn %08lx\n",
j, chunk->nid, chunk->start_pfn, chunk->end_pfn);
node_read_chunk(chunk->nid, chunk);
- add_active_range(chunk->nid, chunk->start_pfn, chunk->end_pfn);
+ e820_register_active_regions(chunk->nid, chunk->start_pfn,
+ min(chunk->end_pfn, max_pfn));
}
for_each_online_node(nid) {
unsigned long start = node_start_pfn[nid];
- unsigned long end = node_end_pfn[nid];
+ unsigned long end = min(node_end_pfn[nid], max_pfn);
memory_present(nid, start, end);
node_remap_size[nid] = node_memmap_size_bytes(nid, start, end);
@@ -261,7 +263,7 @@ out_fail:
struct acpi_static_rsdt {
struct acpi_table_rsdt table;
- u32 padding[7]; /* Allow for 7 more table entries */
+ u32 padding[32]; /* Allow for 32 more table entries */
};
int __init get_memcfg_from_srat(void)
@@ -297,7 +299,7 @@ int __init get_memcfg_from_srat(void)
}
rsdt = (struct acpi_table_rsdt *)
- early_ioremap(rsdp->rsdt_physical_address, sizeof(struct acpi_table_rsdt));
+ early_ioremap(rsdp->rsdt_physical_address, sizeof(saved_rsdt));
if (!rsdt) {
printk(KERN_WARNING
@@ -310,6 +312,7 @@ int __init get_memcfg_from_srat(void)
if (strncmp(header->signature, ACPI_SIG_RSDT, strlen(ACPI_SIG_RSDT))) {
printk(KERN_WARNING "ACPI: RSDT signature incorrect\n");
+ early_iounmap(rsdt, sizeof(saved_rsdt));
goto out_err;
}
@@ -319,37 +322,51 @@ int __init get_memcfg_from_srat(void)
* size of RSDT) divided by the size of each entry
* (4-byte table pointers).
*/
- tables = (header->length - sizeof(struct acpi_table_header)) / 4;
+ tables = (header->length - sizeof(struct acpi_table_header)) / sizeof(u32);
if (!tables)
goto out_err;
memcpy(&saved_rsdt, rsdt, sizeof(saved_rsdt));
-
+ early_iounmap(rsdt, sizeof(saved_rsdt));
if (saved_rsdt.table.header.length > sizeof(saved_rsdt)) {
printk(KERN_WARNING "ACPI: Too big length in RSDT: %d\n",
saved_rsdt.table.header.length);
goto out_err;
}
- printk("Begin SRAT table scan....\n");
+ printk("Begin SRAT table scan....%d\n", tables);
- for (i = 0; i < tables; i++) {
+ for (i = 0; i < tables; i++){
+ int result;
+ u32 length;
/* Map in header, then map in full table length. */
header = (struct acpi_table_header *)
early_ioremap(saved_rsdt.table.table_offset_entry[i], sizeof(struct acpi_table_header));
if (!header)
break;
+
+ printk(KERN_INFO "ACPI: %4.4s %08lX, %04X\n",
+ header->signature,
+ (unsigned long)saved_rsdt.table.table_offset_entry[i],
+ header->length);
+
+ if (strncmp((char *) &header->signature, ACPI_SIG_SRAT, 4)) {
+ early_iounmap(header, sizeof(struct acpi_table_header));
+ continue;
+ }
+
+ length = header->length;
+ early_iounmap(header, sizeof(struct acpi_table_header));
header = (struct acpi_table_header *)
- early_ioremap(saved_rsdt.table.table_offset_entry[i], header->length);
+ early_ioremap(saved_rsdt.table.table_offset_entry[i], length);
if (!header)
break;
- if (strncmp((char *) &header->signature, ACPI_SIG_SRAT, 4))
- continue;
-
/* we've found the srat table. don't need to look at any more tables */
- return acpi20_parse_srat((struct acpi_table_srat *)header);
+ result = acpi20_parse_srat((struct acpi_table_srat *)header);
+ early_iounmap(header, length);
+ return result;
}
out_err:
remove_all_active_ranges();
diff --git a/arch/x86/kernel/summit_32.c b/arch/x86/kernel/summit_32.c
index ae751094eba9..d67ce5f044ba 100644
--- a/arch/x86/kernel/summit_32.c
+++ b/arch/x86/kernel/summit_32.c
@@ -36,7 +36,9 @@ static struct rio_table_hdr *rio_table_hdr __initdata;
static struct scal_detail *scal_devs[MAX_NUMNODES] __initdata;
static struct rio_detail *rio_devs[MAX_NUMNODES*4] __initdata;
+#ifndef CONFIG_X86_NUMAQ
static int mp_bus_id_to_node[MAX_MP_BUSSES] __initdata;
+#endif
static int __init setup_pci_node_map_for_wpeg(int wpeg_num, int last_bus)
{
diff --git a/arch/x86/kernel/sys_i386_32.c b/arch/x86/kernel/sys_i386_32.c
index d2ab52cc1d6b..7066cb855a60 100644
--- a/arch/x86/kernel/sys_i386_32.c
+++ b/arch/x86/kernel/sys_i386_32.c
@@ -19,8 +19,8 @@
#include <linux/utsname.h>
#include <linux/ipc.h>
-#include <asm/uaccess.h>
-#include <asm/unistd.h>
+#include <linux/uaccess.h>
+#include <linux/unistd.h>
asmlinkage long sys_mmap2(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags,
@@ -103,7 +103,7 @@ asmlinkage int old_select(struct sel_arg_struct __user *arg)
*
* This is really horribly ugly.
*/
-asmlinkage int sys_ipc (uint call, int first, int second,
+asmlinkage int sys_ipc(uint call, int first, int second,
int third, void __user *ptr, long fifth)
{
int version, ret;
@@ -113,24 +113,24 @@ asmlinkage int sys_ipc (uint call, int first, int second,
switch (call) {
case SEMOP:
- return sys_semtimedop (first, (struct sembuf __user *)ptr, second, NULL);
+ return sys_semtimedop(first, (struct sembuf __user *)ptr, second, NULL);
case SEMTIMEDOP:
return sys_semtimedop(first, (struct sembuf __user *)ptr, second,
(const struct timespec __user *)fifth);
case SEMGET:
- return sys_semget (first, second, third);
+ return sys_semget(first, second, third);
case SEMCTL: {
union semun fourth;
if (!ptr)
return -EINVAL;
if (get_user(fourth.__pad, (void __user * __user *) ptr))
return -EFAULT;
- return sys_semctl (first, second, third, fourth);
+ return sys_semctl(first, second, third, fourth);
}
case MSGSND:
- return sys_msgsnd (first, (struct msgbuf __user *) ptr,
+ return sys_msgsnd(first, (struct msgbuf __user *) ptr,
second, third);
case MSGRCV:
switch (version) {
@@ -138,45 +138,45 @@ asmlinkage int sys_ipc (uint call, int first, int second,
struct ipc_kludge tmp;
if (!ptr)
return -EINVAL;
-
+
if (copy_from_user(&tmp,
- (struct ipc_kludge __user *) ptr,
- sizeof (tmp)))
+ (struct ipc_kludge __user *) ptr,
+ sizeof(tmp)))
return -EFAULT;
- return sys_msgrcv (first, tmp.msgp, second,
+ return sys_msgrcv(first, tmp.msgp, second,
tmp.msgtyp, third);
}
default:
- return sys_msgrcv (first,
+ return sys_msgrcv(first,
(struct msgbuf __user *) ptr,
second, fifth, third);
}
case MSGGET:
- return sys_msgget ((key_t) first, second);
+ return sys_msgget((key_t) first, second);
case MSGCTL:
- return sys_msgctl (first, second, (struct msqid_ds __user *) ptr);
+ return sys_msgctl(first, second, (struct msqid_ds __user *) ptr);
case SHMAT:
switch (version) {
default: {
ulong raddr;
- ret = do_shmat (first, (char __user *) ptr, second, &raddr);
+ ret = do_shmat(first, (char __user *) ptr, second, &raddr);
if (ret)
return ret;
- return put_user (raddr, (ulong __user *) third);
+ return put_user(raddr, (ulong __user *) third);
}
case 1: /* iBCS2 emulator entry point */
if (!segment_eq(get_fs(), get_ds()))
return -EINVAL;
/* The "(ulong *) third" is valid _only_ because of the kernel segment thing */
- return do_shmat (first, (char __user *) ptr, second, (ulong *) third);
+ return do_shmat(first, (char __user *) ptr, second, (ulong *) third);
}
- case SHMDT:
- return sys_shmdt ((char __user *)ptr);
+ case SHMDT:
+ return sys_shmdt((char __user *)ptr);
case SHMGET:
- return sys_shmget (first, second, third);
+ return sys_shmget(first, second, third);
case SHMCTL:
- return sys_shmctl (first, second,
+ return sys_shmctl(first, second,
(struct shmid_ds __user *) ptr);
default:
return -ENOSYS;
@@ -186,28 +186,28 @@ asmlinkage int sys_ipc (uint call, int first, int second,
/*
* Old cruft
*/
-asmlinkage int sys_uname(struct old_utsname __user * name)
+asmlinkage int sys_uname(struct old_utsname __user *name)
{
int err;
if (!name)
return -EFAULT;
down_read(&uts_sem);
- err = copy_to_user(name, utsname(), sizeof (*name));
+ err = copy_to_user(name, utsname(), sizeof(*name));
up_read(&uts_sem);
- return err?-EFAULT:0;
+ return err? -EFAULT:0;
}
-asmlinkage int sys_olduname(struct oldold_utsname __user * name)
+asmlinkage int sys_olduname(struct oldold_utsname __user *name)
{
int error;
if (!name)
return -EFAULT;
- if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname)))
+ if (!access_ok(VERIFY_WRITE, name, sizeof(struct oldold_utsname)))
return -EFAULT;
-
- down_read(&uts_sem);
-
+
+ down_read(&uts_sem);
+
error = __copy_to_user(&name->sysname, &utsname()->sysname,
__OLD_UTS_LEN);
error |= __put_user(0, name->sysname + __OLD_UTS_LEN);
@@ -223,9 +223,9 @@ asmlinkage int sys_olduname(struct oldold_utsname __user * name)
error |= __copy_to_user(&name->machine, &utsname()->machine,
__OLD_UTS_LEN);
error |= __put_user(0, name->machine + __OLD_UTS_LEN);
-
+
up_read(&uts_sem);
-
+
error = error ? -EFAULT : 0;
return error;
@@ -241,6 +241,6 @@ int kernel_execve(const char *filename, char *const argv[], char *const envp[])
long __res;
asm volatile ("push %%ebx ; movl %2,%%ebx ; int $0x80 ; pop %%ebx"
: "=a" (__res)
- : "0" (__NR_execve),"ri" (filename),"c" (argv), "d" (envp) : "memory");
+ : "0" (__NR_execve), "ri" (filename), "c" (argv), "d" (envp) : "memory");
return __res;
}
diff --git a/arch/x86/kernel/time_32.c b/arch/x86/kernel/time_32.c
index 2ff21f398934..5f29f12da50c 100644
--- a/arch/x86/kernel/time_32.c
+++ b/arch/x86/kernel/time_32.c
@@ -84,8 +84,7 @@ irqreturn_t timer_interrupt(int irq, void *dev_id)
if (timer_ack) {
/*
* Subtle, when I/O APICs are used we have to ack timer IRQ
- * manually to reset the IRR bit for do_slow_gettimeoffset().
- * This will also deassert NMI lines for the watchdog if run
+ * manually to deassert NMI lines for the watchdog if run
* on an 82489DX-based system.
*/
spin_lock(&i8259A_lock);
diff --git a/arch/x86/kernel/trampoline.c b/arch/x86/kernel/trampoline.c
index abbf199adebb..1106fac6024d 100644
--- a/arch/x86/kernel/trampoline.c
+++ b/arch/x86/kernel/trampoline.c
@@ -2,7 +2,7 @@
#include <asm/trampoline.h>
-/* ready for x86_64, no harm for x86, since it will overwrite after alloc */
+/* ready for x86_64 and x86 */
unsigned char *trampoline_base = __va(TRAMPOLINE_BASE);
/*
diff --git a/arch/x86/kernel/traps_32.c b/arch/x86/kernel/traps_32.c
index bde6f63e15d5..94c76073d902 100644
--- a/arch/x86/kernel/traps_32.c
+++ b/arch/x86/kernel/traps_32.c
@@ -403,6 +403,7 @@ int __kprobes __die(const char *str, struct pt_regs *regs, long err)
#endif
printk("\n");
+ sysfs_printk_last_file();
if (notify_die(DIE_OOPS, str, regs, err,
current->thread.trap_no, SIGSEGV) != NOTIFY_STOP) {
@@ -544,6 +545,7 @@ vm86_trap:
#define DO_ERROR(trapnr, signr, str, name) \
void do_##name(struct pt_regs *regs, long error_code) \
{ \
+ trace_hardirqs_fixup(); \
if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
== NOTIFY_STOP) \
return; \
@@ -755,9 +757,9 @@ unknown_nmi_error(unsigned char reason, struct pt_regs *regs)
static DEFINE_SPINLOCK(nmi_print_lock);
-void notrace __kprobes die_nmi(struct pt_regs *regs, const char *msg)
+void notrace __kprobes die_nmi(char *str, struct pt_regs *regs, int do_panic)
{
- if (notify_die(DIE_NMIWATCHDOG, msg, regs, 0, 2, SIGINT) == NOTIFY_STOP)
+ if (notify_die(DIE_NMIWATCHDOG, str, regs, 0, 2, SIGINT) == NOTIFY_STOP)
return;
spin_lock(&nmi_print_lock);
@@ -766,10 +768,12 @@ void notrace __kprobes die_nmi(struct pt_regs *regs, const char *msg)
* to get a message out:
*/
bust_spinlocks(1);
- printk(KERN_EMERG "%s", msg);
+ printk(KERN_EMERG "%s", str);
printk(" on CPU%d, ip %08lx, registers:\n",
smp_processor_id(), regs->ip);
show_registers(regs);
+ if (do_panic)
+ panic("Non maskable interrupt");
console_silent();
spin_unlock(&nmi_print_lock);
bust_spinlocks(0);
diff --git a/arch/x86/kernel/traps_64.c b/arch/x86/kernel/traps_64.c
index adff76ea97c4..5cf522c93e48 100644
--- a/arch/x86/kernel/traps_64.c
+++ b/arch/x86/kernel/traps_64.c
@@ -71,12 +71,13 @@ asmlinkage void general_protection(void);
asmlinkage void page_fault(void);
asmlinkage void coprocessor_error(void);
asmlinkage void simd_coprocessor_error(void);
-asmlinkage void reserved(void);
asmlinkage void alignment_check(void);
asmlinkage void machine_check(void);
asmlinkage void spurious_interrupt_bug(void);
+int panic_on_unrecovered_nmi;
static unsigned int code_bytes = 64;
+static unsigned ignore_nmis;
static inline void conditional_sti(struct pt_regs *regs)
{
@@ -575,6 +576,7 @@ int __kprobes __die(const char * str, struct pt_regs * regs, long err)
printk("DEBUG_PAGEALLOC");
#endif
printk("\n");
+ sysfs_printk_last_file();
if (notify_die(DIE_OOPS, str, regs, err, current->thread.trap_no, SIGSEGV) == NOTIFY_STOP)
return 1;
show_registers(regs);
@@ -614,7 +616,9 @@ die_nmi(char *str, struct pt_regs *regs, int do_panic)
* We are in trouble anyway, lets at least try
* to get a message out.
*/
- printk(str, smp_processor_id());
+ printk(KERN_EMERG "%s", str);
+ printk(" on CPU%d, ip %08lx, registers:\n",
+ smp_processor_id(), regs->ip);
show_registers(regs);
if (kexec_should_crash(current))
crash_kexec(regs);
@@ -702,12 +706,10 @@ DO_ERROR_INFO( 0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->ip)
DO_ERROR( 4, SIGSEGV, "overflow", overflow)
DO_ERROR( 5, SIGSEGV, "bounds", bounds)
DO_ERROR_INFO( 6, SIGILL, "invalid opcode", invalid_op, ILL_ILLOPN, regs->ip)
-DO_ERROR( 7, SIGSEGV, "device not available", device_not_available)
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_INFO(17, SIGBUS, "alignment check", alignment_check, BUS_ADRALN, 0)
-DO_ERROR(18, SIGSEGV, "reserved", reserved)
/* Runs on IST stack */
asmlinkage void do_stack_segment(struct pt_regs *regs, long error_code)
@@ -865,6 +867,28 @@ asmlinkage notrace __kprobes void default_do_nmi(struct pt_regs *regs)
io_check_error(reason, regs);
}
+asmlinkage notrace __kprobes void
+do_nmi(struct pt_regs *regs, long error_code)
+{
+ nmi_enter();
+ add_pda(__nmi_count, 1);
+ if (!ignore_nmis)
+ default_do_nmi(regs);
+ nmi_exit();
+}
+
+void stop_nmi(void)
+{
+ acpi_nmi_disable();
+ ignore_nmis++;
+}
+
+void restart_nmi(void)
+{
+ ignore_nmis--;
+ acpi_nmi_enable();
+}
+
/* runs on IST stack. */
asmlinkage void __kprobes do_int3(struct pt_regs * regs, long error_code)
{
diff --git a/arch/x86/kernel/tsc_32.c b/arch/x86/kernel/tsc_32.c
index 068759db63dd..ac8b85e18939 100644
--- a/arch/x86/kernel/tsc_32.c
+++ b/arch/x86/kernel/tsc_32.c
@@ -283,7 +283,6 @@ core_initcall(cpufreq_tsc);
/* clock source code */
-static unsigned long current_tsc_khz;
static struct clocksource clocksource_tsc;
/*
@@ -441,9 +440,8 @@ void __init tsc_init(void)
unsynchronized_tsc();
check_geode_tsc_reliable();
- current_tsc_khz = tsc_khz;
- clocksource_tsc.mult = clocksource_khz2mult(current_tsc_khz,
- clocksource_tsc.shift);
+ clocksource_tsc.mult = clocksource_khz2mult(tsc_khz,
+ clocksource_tsc.shift);
/* lower the rating if we already know its unstable: */
if (check_tsc_unstable()) {
clocksource_tsc.rating = 0;
diff --git a/arch/x86/kernel/tsc_64.c b/arch/x86/kernel/tsc_64.c
index 1784b8077a12..9898fb01edfd 100644
--- a/arch/x86/kernel/tsc_64.c
+++ b/arch/x86/kernel/tsc_64.c
@@ -242,7 +242,7 @@ void __init tsc_calibrate(void)
if (hpet) {
printk(KERN_INFO "TSC calibrated against HPET\n");
if (hpet2 < hpet1)
- hpet2 += 0x100000000;
+ hpet2 += 0x100000000UL;
hpet2 -= hpet1;
tsc1 = (hpet2 * hpet_readl(HPET_PERIOD)) / 1000000;
} else {
diff --git a/arch/x86/kernel/vmiclock_32.c b/arch/x86/kernel/vmiclock_32.c
index a2b030780aa9..ba7d19e102b1 100644
--- a/arch/x86/kernel/vmiclock_32.c
+++ b/arch/x86/kernel/vmiclock_32.c
@@ -33,8 +33,7 @@
#include <asm/apic.h>
#include <asm/timer.h>
#include <asm/i8253.h>
-
-#include <irq_vectors.h>
+#include <asm/irq_vectors.h>
#define VMI_ONESHOT (VMI_ALARM_IS_ONESHOT | VMI_CYCLES_REAL | vmi_get_alarm_wiring())
#define VMI_PERIODIC (VMI_ALARM_IS_PERIODIC | VMI_CYCLES_REAL | vmi_get_alarm_wiring())
diff --git a/arch/x86/kernel/vmlinux_32.lds.S b/arch/x86/kernel/vmlinux_32.lds.S
index ce5ed083a1e9..248a575da05f 100644
--- a/arch/x86/kernel/vmlinux_32.lds.S
+++ b/arch/x86/kernel/vmlinux_32.lds.S
@@ -49,23 +49,14 @@ SECTIONS
_etext = .; /* End of text section */
} :text = 0x9090
+ NOTES :text :note
+
. = ALIGN(16); /* Exception table */
__ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) {
__start___ex_table = .;
*(__ex_table)
__stop___ex_table = .;
- }
-
- NOTES :text :note
-
- BUG_TABLE :text
-
- . = ALIGN(4);
- .tracedata : AT(ADDR(.tracedata) - LOAD_OFFSET) {
- __tracedata_start = .;
- *(.tracedata)
- __tracedata_end = .;
- }
+ } :text = 0x9090
RODATA
@@ -189,6 +180,7 @@ SECTIONS
. = ALIGN(PAGE_SIZE);
.data.percpu : AT(ADDR(.data.percpu) - LOAD_OFFSET) {
__per_cpu_start = .;
+ *(.data.percpu.page_aligned)
*(.data.percpu)
*(.data.percpu.shared_aligned)
__per_cpu_end = .;
diff --git a/arch/x86/kernel/vmlinux_64.lds.S b/arch/x86/kernel/vmlinux_64.lds.S
index fad3674b06a5..2f8cd2987271 100644
--- a/arch/x86/kernel/vmlinux_64.lds.S
+++ b/arch/x86/kernel/vmlinux_64.lds.S
@@ -19,7 +19,7 @@ PHDRS {
data PT_LOAD FLAGS(7); /* RWE */
user PT_LOAD FLAGS(7); /* RWE */
data.init PT_LOAD FLAGS(7); /* RWE */
- note PT_NOTE FLAGS(4); /* R__ */
+ note PT_NOTE FLAGS(0); /* ___ */
}
SECTIONS
{
@@ -40,26 +40,17 @@ SECTIONS
_etext = .; /* End of text section */
} :text = 0x9090
+ NOTES :text :note
+
. = ALIGN(16); /* Exception table */
__ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) {
__start___ex_table = .;
*(__ex_table)
__stop___ex_table = .;
- }
-
- NOTES :text :note
-
- BUG_TABLE :text
+ } :text = 0x9090
RODATA
- . = ALIGN(4);
- .tracedata : AT(ADDR(.tracedata) - LOAD_OFFSET) {
- __tracedata_start = .;
- *(.tracedata)
- __tracedata_end = .;
- }
-
. = ALIGN(PAGE_SIZE); /* Align data segment to page size boundary */
/* Data */
.data : AT(ADDR(.data) - LOAD_OFFSET) {
diff --git a/arch/x86/kernel/vsmp_64.c b/arch/x86/kernel/vsmp_64.c
index ba8c0b75ab0a..0c029e8959c7 100644
--- a/arch/x86/kernel/vsmp_64.c
+++ b/arch/x86/kernel/vsmp_64.c
@@ -15,9 +15,12 @@
#include <linux/init.h>
#include <linux/pci_ids.h>
#include <linux/pci_regs.h>
+
+#include <asm/apic.h>
#include <asm/pci-direct.h>
#include <asm/io.h>
#include <asm/paravirt.h>
+#include <asm/setup.h>
#if defined CONFIG_PCI && defined CONFIG_PARAVIRT
/*
diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c
index 61efa2f7d564..4063dfa2a02d 100644
--- a/arch/x86/kernel/vsyscall_64.c
+++ b/arch/x86/kernel/vsyscall_64.c
@@ -42,7 +42,8 @@
#include <asm/topology.h>
#include <asm/vgtod.h>
-#define __vsyscall(nr) __attribute__ ((unused,__section__(".vsyscall_" #nr)))
+#define __vsyscall(nr) \
+ __attribute__ ((unused, __section__(".vsyscall_" #nr))) notrace
#define __syscall_clobber "r11","cx","memory"
/*
diff --git a/arch/x86/kernel/x8664_ksyms_64.c b/arch/x86/kernel/x8664_ksyms_64.c
index f6c05d0410fb..122885bc5f3b 100644
--- a/arch/x86/kernel/x8664_ksyms_64.c
+++ b/arch/x86/kernel/x8664_ksyms_64.c
@@ -1,15 +1,22 @@
/* Exports for assembly files.
All C exports should go in the respective C files. */
+#include <linux/ftrace.h>
#include <linux/module.h>
-#include <net/checksum.h>
#include <linux/smp.h>
+#include <net/checksum.h>
+
#include <asm/processor.h>
-#include <asm/uaccess.h>
#include <asm/pgtable.h>
+#include <asm/uaccess.h>
#include <asm/desc.h>
+#ifdef CONFIG_FTRACE
+/* mcount is defined in assembly */
+EXPORT_SYMBOL(mcount);
+#endif
+
EXPORT_SYMBOL(kernel_thread);
EXPORT_SYMBOL(__get_user_1);
diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile
index c97d35c218db..d0e940bb6f40 100644
--- a/arch/x86/kvm/Makefile
+++ b/arch/x86/kvm/Makefile
@@ -2,7 +2,8 @@
# Makefile for Kernel-based Virtual Machine module
#
-common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o ioapic.o)
+common-objs = $(addprefix ../../../virt/kvm/, kvm_main.o ioapic.o \
+ coalesced_mmio.o)
ifeq ($(CONFIG_KVM_TRACE),y)
common-objs += $(addprefix ../../../virt/kvm/, kvm_trace.o)
endif
diff --git a/arch/x86/kvm/i8254.c b/arch/x86/kvm/i8254.c
index 7c077a9d9777..c0f7872a9124 100644
--- a/arch/x86/kvm/i8254.c
+++ b/arch/x86/kvm/i8254.c
@@ -91,7 +91,7 @@ static void pit_set_gate(struct kvm *kvm, int channel, u32 val)
c->gate = val;
}
-int pit_get_gate(struct kvm *kvm, int channel)
+static int pit_get_gate(struct kvm *kvm, int channel)
{
WARN_ON(!mutex_is_locked(&kvm->arch.vpit->pit_state.lock));
@@ -193,14 +193,13 @@ static void pit_latch_status(struct kvm *kvm, int channel)
}
}
-int __pit_timer_fn(struct kvm_kpit_state *ps)
+static int __pit_timer_fn(struct kvm_kpit_state *ps)
{
struct kvm_vcpu *vcpu0 = ps->pit->kvm->vcpus[0];
struct kvm_kpit_timer *pt = &ps->pit_timer;
- atomic_inc(&pt->pending);
- smp_mb__after_atomic_inc();
- /* FIXME: handle case where the guest is in guest mode */
+ if (!atomic_inc_and_test(&pt->pending))
+ set_bit(KVM_REQ_PENDING_TIMER, &vcpu0->requests);
if (vcpu0 && waitqueue_active(&vcpu0->wq)) {
vcpu0->arch.mp_state = KVM_MP_STATE_RUNNABLE;
wake_up_interruptible(&vcpu0->wq);
@@ -237,6 +236,19 @@ static enum hrtimer_restart pit_timer_fn(struct hrtimer *data)
return HRTIMER_NORESTART;
}
+void __kvm_migrate_pit_timer(struct kvm_vcpu *vcpu)
+{
+ struct kvm_pit *pit = vcpu->kvm->arch.vpit;
+ struct hrtimer *timer;
+
+ if (vcpu->vcpu_id != 0 || !pit)
+ return;
+
+ timer = &pit->pit_state.pit_timer.timer;
+ if (hrtimer_cancel(timer))
+ hrtimer_start(timer, timer->expires, HRTIMER_MODE_ABS);
+}
+
static void destroy_pit_timer(struct kvm_kpit_timer *pt)
{
pr_debug("pit: execute del timer!\n");
@@ -293,6 +305,7 @@ static void pit_load_count(struct kvm *kvm, int channel, u32 val)
create_pit_timer(&ps->pit_timer, val, 0);
break;
case 2:
+ case 3:
create_pit_timer(&ps->pit_timer, val, 1);
break;
default:
@@ -444,7 +457,8 @@ static void pit_ioport_read(struct kvm_io_device *this,
mutex_unlock(&pit_state->lock);
}
-static int pit_in_range(struct kvm_io_device *this, gpa_t addr)
+static int pit_in_range(struct kvm_io_device *this, gpa_t addr,
+ int len, int is_write)
{
return ((addr >= KVM_PIT_BASE_ADDRESS) &&
(addr < KVM_PIT_BASE_ADDRESS + KVM_PIT_MEM_LENGTH));
@@ -485,7 +499,8 @@ static void speaker_ioport_read(struct kvm_io_device *this,
mutex_unlock(&pit_state->lock);
}
-static int speaker_in_range(struct kvm_io_device *this, gpa_t addr)
+static int speaker_in_range(struct kvm_io_device *this, gpa_t addr,
+ int len, int is_write)
{
return (addr == KVM_SPEAKER_BASE_ADDRESS);
}
@@ -560,7 +575,7 @@ void kvm_free_pit(struct kvm *kvm)
}
}
-void __inject_pit_timer_intr(struct kvm *kvm)
+static void __inject_pit_timer_intr(struct kvm *kvm)
{
mutex_lock(&kvm->lock);
kvm_ioapic_set_irq(kvm->arch.vioapic, 0, 1);
diff --git a/arch/x86/kvm/i8259.c b/arch/x86/kvm/i8259.c
index ab29cf2def47..5857f59ad4aa 100644
--- a/arch/x86/kvm/i8259.c
+++ b/arch/x86/kvm/i8259.c
@@ -346,7 +346,8 @@ static u32 elcr_ioport_read(void *opaque, u32 addr1)
return s->elcr;
}
-static int picdev_in_range(struct kvm_io_device *this, gpa_t addr)
+static int picdev_in_range(struct kvm_io_device *this, gpa_t addr,
+ int len, int is_write)
{
switch (addr) {
case 0x20:
diff --git a/arch/x86/kvm/irq.c b/arch/x86/kvm/irq.c
index ce1f583459b1..76d736b5f664 100644
--- a/arch/x86/kvm/irq.c
+++ b/arch/x86/kvm/irq.c
@@ -94,3 +94,9 @@ void kvm_timer_intr_post(struct kvm_vcpu *vcpu, int vec)
/* TODO: PIT, RTC etc. */
}
EXPORT_SYMBOL_GPL(kvm_timer_intr_post);
+
+void __kvm_migrate_timers(struct kvm_vcpu *vcpu)
+{
+ __kvm_migrate_apic_timer(vcpu);
+ __kvm_migrate_pit_timer(vcpu);
+}
diff --git a/arch/x86/kvm/irq.h b/arch/x86/kvm/irq.h
index 1802134b836f..2a15be2275c0 100644
--- a/arch/x86/kvm/irq.h
+++ b/arch/x86/kvm/irq.h
@@ -84,6 +84,8 @@ void kvm_timer_intr_post(struct kvm_vcpu *vcpu, int vec);
void kvm_inject_pending_timer_irqs(struct kvm_vcpu *vcpu);
void kvm_inject_apic_timer_irqs(struct kvm_vcpu *vcpu);
void __kvm_migrate_apic_timer(struct kvm_vcpu *vcpu);
+void __kvm_migrate_pit_timer(struct kvm_vcpu *vcpu);
+void __kvm_migrate_timers(struct kvm_vcpu *vcpu);
int pit_has_pending_timer(struct kvm_vcpu *vcpu);
int apic_has_pending_timer(struct kvm_vcpu *vcpu);
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index c297c50eba63..73f43de69f67 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -356,8 +356,9 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode,
case APIC_DM_SMI:
printk(KERN_DEBUG "Ignoring guest SMI\n");
break;
+
case APIC_DM_NMI:
- printk(KERN_DEBUG "Ignoring guest NMI\n");
+ kvm_inject_nmi(vcpu);
break;
case APIC_DM_INIT:
@@ -572,6 +573,8 @@ static u32 __apic_read(struct kvm_lapic *apic, unsigned int offset)
{
u32 val = 0;
+ KVMTRACE_1D(APIC_ACCESS, apic->vcpu, (u32)offset, handler);
+
if (offset >= LAPIC_MMIO_LENGTH)
return 0;
@@ -695,6 +698,8 @@ static void apic_mmio_write(struct kvm_io_device *this,
offset &= 0xff0;
+ KVMTRACE_1D(APIC_ACCESS, apic->vcpu, (u32)offset, handler);
+
switch (offset) {
case APIC_ID: /* Local APIC ID */
apic_set_reg(apic, APIC_ID, val);
@@ -780,7 +785,8 @@ static void apic_mmio_write(struct kvm_io_device *this,
}
-static int apic_mmio_range(struct kvm_io_device *this, gpa_t addr)
+static int apic_mmio_range(struct kvm_io_device *this, gpa_t addr,
+ int len, int size)
{
struct kvm_lapic *apic = (struct kvm_lapic *)this->private;
int ret = 0;
@@ -939,7 +945,8 @@ static int __apic_timer_fn(struct kvm_lapic *apic)
int result = 0;
wait_queue_head_t *q = &apic->vcpu->wq;
- atomic_inc(&apic->timer.pending);
+ if(!atomic_inc_and_test(&apic->timer.pending))
+ set_bit(KVM_REQ_PENDING_TIMER, &apic->vcpu->requests);
if (waitqueue_active(q)) {
apic->vcpu->arch.mp_state = KVM_MP_STATE_RUNNABLE;
wake_up_interruptible(q);
diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h
index 676c396c9cee..81858881287e 100644
--- a/arch/x86/kvm/lapic.h
+++ b/arch/x86/kvm/lapic.h
@@ -31,6 +31,7 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu);
u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu);
void kvm_lapic_set_tpr(struct kvm_vcpu *vcpu, unsigned long cr8);
void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value);
+u64 kvm_lapic_get_base(struct kvm_vcpu *vcpu);
int kvm_apic_match_physical_addr(struct kvm_lapic *apic, u16 dest);
int kvm_apic_match_logical_addr(struct kvm_lapic *apic, u8 mda);
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
index 7246b60afb96..5ebb2788bd73 100644
--- a/arch/x86/kvm/mmu.c
+++ b/arch/x86/kvm/mmu.c
@@ -640,6 +640,7 @@ static void rmap_write_protect(struct kvm *kvm, u64 gfn)
rmap_remove(kvm, spte);
--kvm->stat.lpages;
set_shadow_pte(spte, shadow_trap_nonpresent_pte);
+ spte = NULL;
write_protected = 1;
}
spte = rmap_next(kvm, rmapp, spte);
@@ -658,7 +659,7 @@ static int is_empty_shadow_page(u64 *spt)
u64 *end;
for (pos = spt, end = pos + PAGE_SIZE / sizeof(u64); pos != end; pos++)
- if (*pos != shadow_trap_nonpresent_pte) {
+ if (is_shadow_present_pte(*pos)) {
printk(KERN_ERR "%s: %p %llx\n", __func__,
pos, *pos);
return 0;
@@ -775,6 +776,15 @@ static void mmu_page_remove_parent_pte(struct kvm_mmu_page *sp,
BUG();
}
+static void nonpaging_prefetch_page(struct kvm_vcpu *vcpu,
+ struct kvm_mmu_page *sp)
+{
+ int i;
+
+ for (i = 0; i < PT64_ENT_PER_PAGE; ++i)
+ sp->spt[i] = shadow_trap_nonpresent_pte;
+}
+
static struct kvm_mmu_page *kvm_mmu_lookup_page(struct kvm *kvm, gfn_t gfn)
{
unsigned index;
@@ -840,7 +850,10 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu,
hlist_add_head(&sp->hash_link, bucket);
if (!metaphysical)
rmap_write_protect(vcpu->kvm, gfn);
- vcpu->arch.mmu.prefetch_page(vcpu, sp);
+ if (shadow_trap_nonpresent_pte != shadow_notrap_nonpresent_pte)
+ vcpu->arch.mmu.prefetch_page(vcpu, sp);
+ else
+ nonpaging_prefetch_page(vcpu, sp);
return sp;
}
@@ -1082,10 +1095,6 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *shadow_pte,
struct kvm_mmu_page *shadow;
spte |= PT_WRITABLE_MASK;
- if (user_fault) {
- mmu_unshadow(vcpu->kvm, gfn);
- goto unshadowed;
- }
shadow = kvm_mmu_lookup_page(vcpu->kvm, gfn);
if (shadow ||
@@ -1102,8 +1111,6 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *shadow_pte,
}
}
-unshadowed:
-
if (pte_access & ACC_WRITE_MASK)
mark_page_dirty(vcpu->kvm, gfn);
@@ -1127,8 +1134,10 @@ unshadowed:
else
kvm_release_pfn_clean(pfn);
}
- if (!ptwrite || !*ptwrite)
+ if (speculative) {
vcpu->arch.last_pte_updated = shadow_pte;
+ vcpu->arch.last_pte_gfn = gfn;
+ }
}
static void nonpaging_new_cr3(struct kvm_vcpu *vcpu)
@@ -1216,15 +1225,6 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn)
}
-static void nonpaging_prefetch_page(struct kvm_vcpu *vcpu,
- struct kvm_mmu_page *sp)
-{
- int i;
-
- for (i = 0; i < PT64_ENT_PER_PAGE; ++i)
- sp->spt[i] = shadow_trap_nonpresent_pte;
-}
-
static void mmu_free_roots(struct kvm_vcpu *vcpu)
{
int i;
@@ -1580,11 +1580,13 @@ static void mmu_pte_write_new_pte(struct kvm_vcpu *vcpu,
u64 *spte,
const void *new)
{
- if ((sp->role.level != PT_PAGE_TABLE_LEVEL)
- && !vcpu->arch.update_pte.largepage) {
- ++vcpu->kvm->stat.mmu_pde_zapped;
- return;
- }
+ if (sp->role.level != PT_PAGE_TABLE_LEVEL) {
+ if (!vcpu->arch.update_pte.largepage ||
+ sp->role.glevels == PT32_ROOT_LEVEL) {
+ ++vcpu->kvm->stat.mmu_pde_zapped;
+ return;
+ }
+ }
++vcpu->kvm->stat.mmu_pte_updated;
if (sp->role.glevels == PT32_ROOT_LEVEL)
@@ -1674,6 +1676,18 @@ static void mmu_guess_page_from_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa,
vcpu->arch.update_pte.pfn = pfn;
}
+static void kvm_mmu_access_page(struct kvm_vcpu *vcpu, gfn_t gfn)
+{
+ u64 *spte = vcpu->arch.last_pte_updated;
+
+ if (spte
+ && vcpu->arch.last_pte_gfn == gfn
+ && shadow_accessed_mask
+ && !(*spte & shadow_accessed_mask)
+ && is_shadow_present_pte(*spte))
+ set_bit(PT_ACCESSED_SHIFT, (unsigned long *)spte);
+}
+
void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa,
const u8 *new, int bytes)
{
@@ -1697,6 +1711,7 @@ void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa,
pgprintk("%s: gpa %llx bytes %d\n", __func__, gpa, bytes);
mmu_guess_page_from_pte_write(vcpu, gpa, new, bytes);
spin_lock(&vcpu->kvm->mmu_lock);
+ kvm_mmu_access_page(vcpu, gfn);
kvm_mmu_free_some_pages(vcpu);
++vcpu->kvm->stat.mmu_pte_write;
kvm_mmu_audit(vcpu, "pre pte write");
@@ -1858,6 +1873,7 @@ static void free_mmu_pages(struct kvm_vcpu *vcpu)
sp = container_of(vcpu->kvm->arch.active_mmu_pages.next,
struct kvm_mmu_page, link);
kvm_mmu_zap_page(vcpu->kvm, sp);
+ cond_resched();
}
free_page((unsigned long)vcpu->arch.mmu.pae_root);
}
@@ -1950,7 +1966,7 @@ void kvm_mmu_zap_all(struct kvm *kvm)
kvm_flush_remote_tlbs(kvm);
}
-void kvm_mmu_remove_one_alloc_mmu_page(struct kvm *kvm)
+static void kvm_mmu_remove_one_alloc_mmu_page(struct kvm *kvm)
{
struct kvm_mmu_page *page;
diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h
index 1730757bbc7a..258e5d56298e 100644
--- a/arch/x86/kvm/mmu.h
+++ b/arch/x86/kvm/mmu.h
@@ -15,7 +15,8 @@
#define PT_USER_MASK (1ULL << 2)
#define PT_PWT_MASK (1ULL << 3)
#define PT_PCD_MASK (1ULL << 4)
-#define PT_ACCESSED_MASK (1ULL << 5)
+#define PT_ACCESSED_SHIFT 5
+#define PT_ACCESSED_MASK (1ULL << PT_ACCESSED_SHIFT)
#define PT_DIRTY_MASK (1ULL << 6)
#define PT_PAGE_SIZE_MASK (1ULL << 7)
#define PT_PAT_MASK (1ULL << 7)
diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h
index 156fe10288ae..4d918220baeb 100644
--- a/arch/x86/kvm/paging_tmpl.h
+++ b/arch/x86/kvm/paging_tmpl.h
@@ -418,7 +418,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr,
/* mmio */
if (is_error_pfn(pfn)) {
- pgprintk("gfn %x is mmio\n", walker.gfn);
+ pgprintk("gfn %lx is mmio\n", walker.gfn);
kvm_release_pfn_clean(pfn);
return 1;
}
@@ -460,8 +460,9 @@ static gpa_t FNAME(gva_to_gpa)(struct kvm_vcpu *vcpu, gva_t vaddr)
static void FNAME(prefetch_page)(struct kvm_vcpu *vcpu,
struct kvm_mmu_page *sp)
{
- int i, offset = 0, r = 0;
- pt_element_t pt;
+ int i, j, offset, r;
+ pt_element_t pt[256 / sizeof(pt_element_t)];
+ gpa_t pte_gpa;
if (sp->role.metaphysical
|| (PTTYPE == 32 && sp->role.level > PT_PAGE_TABLE_LEVEL)) {
@@ -469,19 +470,20 @@ static void FNAME(prefetch_page)(struct kvm_vcpu *vcpu,
return;
}
- if (PTTYPE == 32)
+ pte_gpa = gfn_to_gpa(sp->gfn);
+ if (PTTYPE == 32) {
offset = sp->role.quadrant << PT64_LEVEL_BITS;
+ pte_gpa += offset * sizeof(pt_element_t);
+ }
- for (i = 0; i < PT64_ENT_PER_PAGE; ++i) {
- gpa_t pte_gpa = gfn_to_gpa(sp->gfn);
- pte_gpa += (i+offset) * sizeof(pt_element_t);
-
- r = kvm_read_guest_atomic(vcpu->kvm, pte_gpa, &pt,
- sizeof(pt_element_t));
- if (r || is_present_pte(pt))
- sp->spt[i] = shadow_trap_nonpresent_pte;
- else
- sp->spt[i] = shadow_notrap_nonpresent_pte;
+ for (i = 0; i < PT64_ENT_PER_PAGE; i += ARRAY_SIZE(pt)) {
+ r = kvm_read_guest_atomic(vcpu->kvm, pte_gpa, pt, sizeof pt);
+ pte_gpa += ARRAY_SIZE(pt) * sizeof(pt_element_t);
+ for (j = 0; j < ARRAY_SIZE(pt); ++j)
+ if (r || is_present_pte(pt[j]))
+ sp->spt[i+j] = shadow_trap_nonpresent_pte;
+ else
+ sp->spt[i+j] = shadow_notrap_nonpresent_pte;
}
}
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index ab22615eee89..238e8f3afaf4 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -27,6 +27,8 @@
#include <asm/desc.h>
+#define __ex(x) __kvm_handle_fault_on_reboot(x)
+
MODULE_AUTHOR("Qumranet");
MODULE_LICENSE("GPL");
@@ -129,17 +131,17 @@ static inline void push_irq(struct kvm_vcpu *vcpu, u8 irq)
static inline void clgi(void)
{
- asm volatile (SVM_CLGI);
+ asm volatile (__ex(SVM_CLGI));
}
static inline void stgi(void)
{
- asm volatile (SVM_STGI);
+ asm volatile (__ex(SVM_STGI));
}
static inline void invlpga(unsigned long addr, u32 asid)
{
- asm volatile (SVM_INVLPGA :: "a"(addr), "c"(asid));
+ asm volatile (__ex(SVM_INVLPGA) :: "a"(addr), "c"(asid));
}
static inline unsigned long kvm_read_cr2(void)
@@ -688,7 +690,7 @@ static void svm_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
delta = vcpu->arch.host_tsc - tsc_this;
svm->vmcb->control.tsc_offset += delta;
vcpu->cpu = cpu;
- kvm_migrate_apic_timer(vcpu);
+ kvm_migrate_timers(vcpu);
}
for (i = 0; i < NR_HOST_SAVE_USER_MSRS; i++)
@@ -707,10 +709,6 @@ static void svm_vcpu_put(struct kvm_vcpu *vcpu)
rdtscll(vcpu->arch.host_tsc);
}
-static void svm_vcpu_decache(struct kvm_vcpu *vcpu)
-{
-}
-
static void svm_cache_regs(struct kvm_vcpu *vcpu)
{
struct vcpu_svm *svm = to_svm(vcpu);
@@ -949,7 +947,9 @@ static void new_asid(struct vcpu_svm *svm, struct svm_cpu_data *svm_data)
static unsigned long svm_get_dr(struct kvm_vcpu *vcpu, int dr)
{
- return to_svm(vcpu)->db_regs[dr];
+ unsigned long val = to_svm(vcpu)->db_regs[dr];
+ KVMTRACE_2D(DR_READ, vcpu, (u32)dr, (u32)val, handler);
+ return val;
}
static void svm_set_dr(struct kvm_vcpu *vcpu, int dr, unsigned long value,
@@ -1004,6 +1004,16 @@ static int pf_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
fault_address = svm->vmcb->control.exit_info_2;
error_code = svm->vmcb->control.exit_info_1;
+
+ if (!npt_enabled)
+ KVMTRACE_3D(PAGE_FAULT, &svm->vcpu, error_code,
+ (u32)fault_address, (u32)(fault_address >> 32),
+ handler);
+ else
+ KVMTRACE_3D(TDP_FAULT, &svm->vcpu, error_code,
+ (u32)fault_address, (u32)(fault_address >> 32),
+ handler);
+
return kvm_mmu_page_fault(&svm->vcpu, fault_address, error_code);
}
@@ -1081,6 +1091,19 @@ static int io_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
return kvm_emulate_pio(&svm->vcpu, kvm_run, in, size, port);
}
+static int nmi_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
+{
+ KVMTRACE_0D(NMI, &svm->vcpu, handler);
+ return 1;
+}
+
+static int intr_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
+{
+ ++svm->vcpu.stat.irq_exits;
+ KVMTRACE_0D(INTR, &svm->vcpu, handler);
+ return 1;
+}
+
static int nop_on_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
{
return 1;
@@ -1219,6 +1242,9 @@ static int rdmsr_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
if (svm_get_msr(&svm->vcpu, ecx, &data))
kvm_inject_gp(&svm->vcpu, 0);
else {
+ KVMTRACE_3D(MSR_READ, &svm->vcpu, ecx, (u32)data,
+ (u32)(data >> 32), handler);
+
svm->vmcb->save.rax = data & 0xffffffff;
svm->vcpu.arch.regs[VCPU_REGS_RDX] = data >> 32;
svm->next_rip = svm->vmcb->save.rip + 2;
@@ -1284,16 +1310,19 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, unsigned ecx, u64 data)
case MSR_K7_EVNTSEL1:
case MSR_K7_EVNTSEL2:
case MSR_K7_EVNTSEL3:
+ case MSR_K7_PERFCTR0:
+ case MSR_K7_PERFCTR1:
+ case MSR_K7_PERFCTR2:
+ case MSR_K7_PERFCTR3:
/*
- * only support writing 0 to the performance counters for now
- * to make Windows happy. Should be replaced by a real
- * performance counter emulation later.
+ * Just discard all writes to the performance counters; this
+ * should keep both older linux and windows 64-bit guests
+ * happy
*/
- if (data != 0)
- goto unhandled;
+ pr_unimpl(vcpu, "unimplemented perfctr wrmsr: 0x%x data 0x%llx\n", ecx, data);
+
break;
default:
- unhandled:
return kvm_set_msr_common(vcpu, ecx, data);
}
return 0;
@@ -1304,6 +1333,10 @@ static int wrmsr_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
u32 ecx = svm->vcpu.arch.regs[VCPU_REGS_RCX];
u64 data = (svm->vmcb->save.rax & -1u)
| ((u64)(svm->vcpu.arch.regs[VCPU_REGS_RDX] & -1u) << 32);
+
+ KVMTRACE_3D(MSR_WRITE, &svm->vcpu, ecx, (u32)data, (u32)(data >> 32),
+ handler);
+
svm->next_rip = svm->vmcb->save.rip + 2;
if (svm_set_msr(&svm->vcpu, ecx, data))
kvm_inject_gp(&svm->vcpu, 0);
@@ -1323,6 +1356,8 @@ static int msr_interception(struct vcpu_svm *svm, struct kvm_run *kvm_run)
static int interrupt_window_interception(struct vcpu_svm *svm,
struct kvm_run *kvm_run)
{
+ KVMTRACE_0D(PEND_INTR, &svm->vcpu, handler);
+
svm->vmcb->control.intercept &= ~(1ULL << INTERCEPT_VINTR);
svm->vmcb->control.int_ctl &= ~V_IRQ_MASK;
/*
@@ -1364,8 +1399,8 @@ static int (*svm_exit_handlers[])(struct vcpu_svm *svm,
[SVM_EXIT_EXCP_BASE + PF_VECTOR] = pf_interception,
[SVM_EXIT_EXCP_BASE + NM_VECTOR] = nm_interception,
[SVM_EXIT_EXCP_BASE + MC_VECTOR] = mc_interception,
- [SVM_EXIT_INTR] = nop_on_interception,
- [SVM_EXIT_NMI] = nop_on_interception,
+ [SVM_EXIT_INTR] = intr_interception,
+ [SVM_EXIT_NMI] = nmi_interception,
[SVM_EXIT_SMI] = nop_on_interception,
[SVM_EXIT_INIT] = nop_on_interception,
[SVM_EXIT_VINTR] = interrupt_window_interception,
@@ -1397,6 +1432,9 @@ static int handle_exit(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
struct vcpu_svm *svm = to_svm(vcpu);
u32 exit_code = svm->vmcb->control.exit_code;
+ KVMTRACE_3D(VMEXIT, vcpu, exit_code, (u32)svm->vmcb->save.rip,
+ (u32)((u64)svm->vmcb->save.rip >> 32), entryexit);
+
if (npt_enabled) {
int mmu_reload = 0;
if ((vcpu->arch.cr0 ^ svm->vmcb->save.cr0) & X86_CR0_PG) {
@@ -1470,6 +1508,8 @@ static inline void svm_inject_irq(struct vcpu_svm *svm, int irq)
{
struct vmcb_control_area *control;
+ KVMTRACE_1D(INJ_VIRQ, &svm->vcpu, (u32)irq, handler);
+
control = &svm->vmcb->control;
control->int_vector = irq;
control->int_ctl &= ~V_INTR_PRIO_MASK;
@@ -1716,17 +1756,17 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
/* Enter guest mode */
"push %%rax \n\t"
"mov %c[vmcb](%[svm]), %%rax \n\t"
- SVM_VMLOAD "\n\t"
- SVM_VMRUN "\n\t"
- SVM_VMSAVE "\n\t"
+ __ex(SVM_VMLOAD) "\n\t"
+ __ex(SVM_VMRUN) "\n\t"
+ __ex(SVM_VMSAVE) "\n\t"
"pop %%rax \n\t"
#else
/* Enter guest mode */
"push %%eax \n\t"
"mov %c[vmcb](%[svm]), %%eax \n\t"
- SVM_VMLOAD "\n\t"
- SVM_VMRUN "\n\t"
- SVM_VMSAVE "\n\t"
+ __ex(SVM_VMLOAD) "\n\t"
+ __ex(SVM_VMRUN) "\n\t"
+ __ex(SVM_VMSAVE) "\n\t"
"pop %%eax \n\t"
#endif
@@ -1889,7 +1929,6 @@ static struct kvm_x86_ops svm_x86_ops = {
.prepare_guest_switch = svm_prepare_guest_switch,
.vcpu_load = svm_vcpu_load,
.vcpu_put = svm_vcpu_put,
- .vcpu_decache = svm_vcpu_decache,
.set_guest_debug = svm_guest_debug,
.get_msr = svm_get_msr,
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index bfe4db11989c..6e4278d51fa3 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -30,6 +30,8 @@
#include <asm/io.h>
#include <asm/desc.h>
+#define __ex(x) __kvm_handle_fault_on_reboot(x)
+
MODULE_AUTHOR("Qumranet");
MODULE_LICENSE("GPL");
@@ -53,6 +55,7 @@ struct vmcs {
struct vcpu_vmx {
struct kvm_vcpu vcpu;
+ struct list_head local_vcpus_link;
int launched;
u8 fail;
u32 idt_vectoring_info;
@@ -91,6 +94,7 @@ static int init_rmode(struct kvm *kvm);
static DEFINE_PER_CPU(struct vmcs *, vmxarea);
static DEFINE_PER_CPU(struct vmcs *, current_vmcs);
+static DEFINE_PER_CPU(struct list_head, vcpus_on_cpu);
static struct page *vmx_io_bitmap_a;
static struct page *vmx_io_bitmap_b;
@@ -260,6 +264,11 @@ static inline int cpu_has_vmx_vpid(void)
SECONDARY_EXEC_ENABLE_VPID);
}
+static inline int cpu_has_virtual_nmis(void)
+{
+ return vmcs_config.pin_based_exec_ctrl & PIN_BASED_VIRTUAL_NMIS;
+}
+
static int __find_msr_index(struct vcpu_vmx *vmx, u32 msr)
{
int i;
@@ -278,7 +287,7 @@ static inline void __invvpid(int ext, u16 vpid, gva_t gva)
u64 gva;
} operand = { vpid, 0, gva };
- asm volatile (ASM_VMX_INVVPID
+ asm volatile (__ex(ASM_VMX_INVVPID)
/* CF==1 or ZF==1 --> rc = -1 */
"; ja 1f ; ud2 ; 1:"
: : "a"(&operand), "c"(ext) : "cc", "memory");
@@ -290,7 +299,7 @@ static inline void __invept(int ext, u64 eptp, gpa_t gpa)
u64 eptp, gpa;
} operand = {eptp, gpa};
- asm volatile (ASM_VMX_INVEPT
+ asm volatile (__ex(ASM_VMX_INVEPT)
/* CF==1 or ZF==1 --> rc = -1 */
"; ja 1f ; ud2 ; 1:\n"
: : "a" (&operand), "c" (ext) : "cc", "memory");
@@ -311,7 +320,7 @@ static void vmcs_clear(struct vmcs *vmcs)
u64 phys_addr = __pa(vmcs);
u8 error;
- asm volatile (ASM_VMX_VMCLEAR_RAX "; setna %0"
+ asm volatile (__ex(ASM_VMX_VMCLEAR_RAX) "; setna %0"
: "=g"(error) : "a"(&phys_addr), "m"(phys_addr)
: "cc", "memory");
if (error)
@@ -329,6 +338,9 @@ static void __vcpu_clear(void *arg)
if (per_cpu(current_vmcs, cpu) == vmx->vmcs)
per_cpu(current_vmcs, cpu) = NULL;
rdtscll(vmx->vcpu.arch.host_tsc);
+ list_del(&vmx->local_vcpus_link);
+ vmx->vcpu.cpu = -1;
+ vmx->launched = 0;
}
static void vcpu_clear(struct vcpu_vmx *vmx)
@@ -336,7 +348,6 @@ static void vcpu_clear(struct vcpu_vmx *vmx)
if (vmx->vcpu.cpu == -1)
return;
smp_call_function_single(vmx->vcpu.cpu, __vcpu_clear, vmx, 0, 1);
- vmx->launched = 0;
}
static inline void vpid_sync_vcpu_all(struct vcpu_vmx *vmx)
@@ -378,7 +389,7 @@ static unsigned long vmcs_readl(unsigned long field)
{
unsigned long value;
- asm volatile (ASM_VMX_VMREAD_RDX_RAX
+ asm volatile (__ex(ASM_VMX_VMREAD_RDX_RAX)
: "=a"(value) : "d"(field) : "cc");
return value;
}
@@ -413,7 +424,7 @@ static void vmcs_writel(unsigned long field, unsigned long value)
{
u8 error;
- asm volatile (ASM_VMX_VMWRITE_RAX_RDX "; setna %0"
+ asm volatile (__ex(ASM_VMX_VMWRITE_RAX_RDX) "; setna %0"
: "=q"(error) : "a"(value), "d"(field) : "cc");
if (unlikely(error))
vmwrite_error(field, value);
@@ -431,10 +442,8 @@ static void vmcs_write32(unsigned long field, u32 value)
static void vmcs_write64(unsigned long field, u64 value)
{
-#ifdef CONFIG_X86_64
- vmcs_writel(field, value);
-#else
vmcs_writel(field, value);
+#ifndef CONFIG_X86_64
asm volatile ("");
vmcs_writel(field+1, value >> 32);
#endif
@@ -608,15 +617,19 @@ static void vmx_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
if (vcpu->cpu != cpu) {
vcpu_clear(vmx);
- kvm_migrate_apic_timer(vcpu);
+ kvm_migrate_timers(vcpu);
vpid_sync_vcpu_all(vmx);
+ local_irq_disable();
+ list_add(&vmx->local_vcpus_link,
+ &per_cpu(vcpus_on_cpu, cpu));
+ local_irq_enable();
}
if (per_cpu(current_vmcs, cpu) != vmx->vmcs) {
u8 error;
per_cpu(current_vmcs, cpu) = vmx->vmcs;
- asm volatile (ASM_VMX_VMPTRLD_RAX "; setna %0"
+ asm volatile (__ex(ASM_VMX_VMPTRLD_RAX) "; setna %0"
: "=g"(error) : "a"(&phys_addr), "m"(phys_addr)
: "cc");
if (error)
@@ -677,11 +690,6 @@ static void vmx_fpu_deactivate(struct kvm_vcpu *vcpu)
update_exception_bitmap(vcpu);
}
-static void vmx_vcpu_decache(struct kvm_vcpu *vcpu)
-{
- vcpu_clear(to_vmx(vcpu));
-}
-
static unsigned long vmx_get_rflags(struct kvm_vcpu *vcpu)
{
return vmcs_readl(GUEST_RFLAGS);
@@ -1019,6 +1027,7 @@ static void hardware_enable(void *garbage)
u64 phys_addr = __pa(per_cpu(vmxarea, cpu));
u64 old;
+ INIT_LIST_HEAD(&per_cpu(vcpus_on_cpu, cpu));
rdmsrl(MSR_IA32_FEATURE_CONTROL, old);
if ((old & (MSR_IA32_FEATURE_CONTROL_LOCKED |
MSR_IA32_FEATURE_CONTROL_VMXON_ENABLED))
@@ -1029,13 +1038,26 @@ static void hardware_enable(void *garbage)
MSR_IA32_FEATURE_CONTROL_LOCKED |
MSR_IA32_FEATURE_CONTROL_VMXON_ENABLED);
write_cr4(read_cr4() | X86_CR4_VMXE); /* FIXME: not cpu hotplug safe */
- asm volatile (ASM_VMX_VMXON_RAX : : "a"(&phys_addr), "m"(phys_addr)
+ asm volatile (ASM_VMX_VMXON_RAX
+ : : "a"(&phys_addr), "m"(phys_addr)
: "memory", "cc");
}
+static void vmclear_local_vcpus(void)
+{
+ int cpu = raw_smp_processor_id();
+ struct vcpu_vmx *vmx, *n;
+
+ list_for_each_entry_safe(vmx, n, &per_cpu(vcpus_on_cpu, cpu),
+ local_vcpus_link)
+ __vcpu_clear(vmx);
+}
+
static void hardware_disable(void *garbage)
{
- asm volatile (ASM_VMX_VMXOFF : : : "cc");
+ vmclear_local_vcpus();
+ asm volatile (__ex(ASM_VMX_VMXOFF) : : : "cc");
+ write_cr4(read_cr4() & ~X86_CR4_VMXE);
}
static __init int adjust_vmx_controls(u32 ctl_min, u32 ctl_opt,
@@ -1068,7 +1090,7 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf)
u32 _vmentry_control = 0;
min = PIN_BASED_EXT_INTR_MASK | PIN_BASED_NMI_EXITING;
- opt = 0;
+ opt = PIN_BASED_VIRTUAL_NMIS;
if (adjust_vmx_controls(min, opt, MSR_IA32_VMX_PINBASED_CTLS,
&_pin_based_exec_control) < 0)
return -EIO;
@@ -1817,7 +1839,7 @@ static void allocate_vpid(struct vcpu_vmx *vmx)
spin_unlock(&vmx_vpid_lock);
}
-void vmx_disable_intercept_for_msr(struct page *msr_bitmap, u32 msr)
+static void vmx_disable_intercept_for_msr(struct page *msr_bitmap, u32 msr)
{
void *va;
@@ -2110,6 +2132,13 @@ static void vmx_inject_irq(struct kvm_vcpu *vcpu, int irq)
irq | INTR_TYPE_EXT_INTR | INTR_INFO_VALID_MASK);
}
+static void vmx_inject_nmi(struct kvm_vcpu *vcpu)
+{
+ vmcs_write32(VM_ENTRY_INTR_INFO_FIELD,
+ INTR_TYPE_NMI_INTR | INTR_INFO_VALID_MASK | NMI_VECTOR);
+ vcpu->arch.nmi_pending = 0;
+}
+
static void kvm_do_inject_irq(struct kvm_vcpu *vcpu)
{
int word_index = __ffs(vcpu->arch.irq_summary);
@@ -2550,8 +2579,6 @@ static int handle_apic_access(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
exit_qualification = vmcs_read64(EXIT_QUALIFICATION);
offset = exit_qualification & 0xffful;
- KVMTRACE_1D(APIC_ACCESS, vcpu, (u32)offset, handler);
-
er = emulate_instruction(vcpu, kvm_run, 0, 0, 0);
if (er != EMULATE_DONE) {
@@ -2635,6 +2662,19 @@ static int handle_ept_violation(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
return 1;
}
+static int handle_nmi_window(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
+{
+ u32 cpu_based_vm_exec_control;
+
+ /* clear pending NMI */
+ cpu_based_vm_exec_control = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL);
+ cpu_based_vm_exec_control &= ~CPU_BASED_VIRTUAL_NMI_PENDING;
+ vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control);
+ ++vcpu->stat.nmi_window_exits;
+
+ return 1;
+}
+
/*
* The exit handlers return 1 if the exit was handled fully and guest execution
* may resume. Otherwise they set the kvm_run parameter to indicate what needs
@@ -2645,6 +2685,7 @@ static int (*kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu,
[EXIT_REASON_EXCEPTION_NMI] = handle_exception,
[EXIT_REASON_EXTERNAL_INTERRUPT] = handle_external_interrupt,
[EXIT_REASON_TRIPLE_FAULT] = handle_triple_fault,
+ [EXIT_REASON_NMI_WINDOW] = handle_nmi_window,
[EXIT_REASON_IO_INSTRUCTION] = handle_io,
[EXIT_REASON_CR_ACCESS] = handle_cr,
[EXIT_REASON_DR_ACCESS] = handle_dr,
@@ -2732,17 +2773,52 @@ static void enable_irq_window(struct kvm_vcpu *vcpu)
vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control);
}
+static void enable_nmi_window(struct kvm_vcpu *vcpu)
+{
+ u32 cpu_based_vm_exec_control;
+
+ if (!cpu_has_virtual_nmis())
+ return;
+
+ cpu_based_vm_exec_control = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL);
+ cpu_based_vm_exec_control |= CPU_BASED_VIRTUAL_NMI_PENDING;
+ vmcs_write32(CPU_BASED_VM_EXEC_CONTROL, cpu_based_vm_exec_control);
+}
+
+static int vmx_nmi_enabled(struct kvm_vcpu *vcpu)
+{
+ u32 guest_intr = vmcs_read32(GUEST_INTERRUPTIBILITY_INFO);
+ return !(guest_intr & (GUEST_INTR_STATE_NMI |
+ GUEST_INTR_STATE_MOV_SS |
+ GUEST_INTR_STATE_STI));
+}
+
+static int vmx_irq_enabled(struct kvm_vcpu *vcpu)
+{
+ u32 guest_intr = vmcs_read32(GUEST_INTERRUPTIBILITY_INFO);
+ return (!(guest_intr & (GUEST_INTR_STATE_MOV_SS |
+ GUEST_INTR_STATE_STI)) &&
+ (vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF));
+}
+
+static void enable_intr_window(struct kvm_vcpu *vcpu)
+{
+ if (vcpu->arch.nmi_pending)
+ enable_nmi_window(vcpu);
+ else if (kvm_cpu_has_interrupt(vcpu))
+ enable_irq_window(vcpu);
+}
+
static void vmx_intr_assist(struct kvm_vcpu *vcpu)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);
- u32 idtv_info_field, intr_info_field;
- int has_ext_irq, interrupt_window_open;
+ u32 idtv_info_field, intr_info_field, exit_intr_info_field;
int vector;
update_tpr_threshold(vcpu);
- has_ext_irq = kvm_cpu_has_interrupt(vcpu);
intr_info_field = vmcs_read32(VM_ENTRY_INTR_INFO_FIELD);
+ exit_intr_info_field = vmcs_read32(VM_EXIT_INTR_INFO);
idtv_info_field = vmx->idt_vectoring_info;
if (intr_info_field & INTR_INFO_VALID_MASK) {
if (idtv_info_field & INTR_INFO_VALID_MASK) {
@@ -2750,8 +2826,7 @@ static void vmx_intr_assist(struct kvm_vcpu *vcpu)
if (printk_ratelimit())
printk(KERN_ERR "Fault when IDT_Vectoring\n");
}
- if (has_ext_irq)
- enable_irq_window(vcpu);
+ enable_intr_window(vcpu);
return;
}
if (unlikely(idtv_info_field & INTR_INFO_VALID_MASK)) {
@@ -2761,30 +2836,56 @@ static void vmx_intr_assist(struct kvm_vcpu *vcpu)
u8 vect = idtv_info_field & VECTORING_INFO_VECTOR_MASK;
vmx_inject_irq(vcpu, vect);
- if (unlikely(has_ext_irq))
- enable_irq_window(vcpu);
+ enable_intr_window(vcpu);
return;
}
KVMTRACE_1D(REDELIVER_EVT, vcpu, idtv_info_field, handler);
- vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, idtv_info_field);
+ /*
+ * SDM 3: 25.7.1.2
+ * Clear bit "block by NMI" before VM entry if a NMI delivery
+ * faulted.
+ */
+ if ((idtv_info_field & VECTORING_INFO_TYPE_MASK)
+ == INTR_TYPE_NMI_INTR && cpu_has_virtual_nmis())
+ vmcs_write32(GUEST_INTERRUPTIBILITY_INFO,
+ vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) &
+ ~GUEST_INTR_STATE_NMI);
+
+ vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, idtv_info_field
+ & ~INTR_INFO_RESVD_BITS_MASK);
vmcs_write32(VM_ENTRY_INSTRUCTION_LEN,
vmcs_read32(VM_EXIT_INSTRUCTION_LEN));
if (unlikely(idtv_info_field & INTR_INFO_DELIVER_CODE_MASK))
vmcs_write32(VM_ENTRY_EXCEPTION_ERROR_CODE,
vmcs_read32(IDT_VECTORING_ERROR_CODE));
- if (unlikely(has_ext_irq))
- enable_irq_window(vcpu);
+ enable_intr_window(vcpu);
return;
}
- if (!has_ext_irq)
+ if (cpu_has_virtual_nmis()) {
+ /*
+ * SDM 3: 25.7.1.2
+ * Re-set bit "block by NMI" before VM entry if vmexit caused by
+ * a guest IRET fault.
+ */
+ if ((exit_intr_info_field & INTR_INFO_UNBLOCK_NMI) &&
+ (exit_intr_info_field & INTR_INFO_VECTOR_MASK) != 8)
+ vmcs_write32(GUEST_INTERRUPTIBILITY_INFO,
+ vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) |
+ GUEST_INTR_STATE_NMI);
+ else if (vcpu->arch.nmi_pending) {
+ if (vmx_nmi_enabled(vcpu))
+ vmx_inject_nmi(vcpu);
+ enable_intr_window(vcpu);
+ return;
+ }
+
+ }
+ if (!kvm_cpu_has_interrupt(vcpu))
return;
- interrupt_window_open =
- ((vmcs_readl(GUEST_RFLAGS) & X86_EFLAGS_IF) &&
- (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & 3) == 0);
- if (interrupt_window_open) {
+ if (vmx_irq_enabled(vcpu)) {
vector = kvm_cpu_get_interrupt(vcpu);
vmx_inject_irq(vcpu, vector);
kvm_timer_intr_post(vcpu, vector);
@@ -2834,7 +2935,7 @@ static void vmx_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
"push %%edx; push %%ebp;"
"push %%ecx \n\t"
#endif
- ASM_VMX_VMWRITE_RSP_RDX "\n\t"
+ __ex(ASM_VMX_VMWRITE_RSP_RDX) "\n\t"
/* Check if vmlaunch of vmresume is needed */
"cmpl $0, %c[launched](%0) \n\t"
/* Load guest registers. Don't clobber flags. */
@@ -2869,9 +2970,9 @@ static void vmx_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
#endif
/* Enter guest mode */
"jne .Llaunched \n\t"
- ASM_VMX_VMLAUNCH "\n\t"
+ __ex(ASM_VMX_VMLAUNCH) "\n\t"
"jmp .Lkvm_vmx_return \n\t"
- ".Llaunched: " ASM_VMX_VMRESUME "\n\t"
+ ".Llaunched: " __ex(ASM_VMX_VMRESUME) "\n\t"
".Lkvm_vmx_return: "
/* Save guest registers, load host registers, keep flags */
#ifdef CONFIG_X86_64
@@ -2945,7 +3046,8 @@ static void vmx_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
fixup_rmode_irq(vmx);
vcpu->arch.interrupt_window_open =
- (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) & 3) == 0;
+ (vmcs_read32(GUEST_INTERRUPTIBILITY_INFO) &
+ (GUEST_INTR_STATE_STI | GUEST_INTR_STATE_MOV_SS)) == 0;
asm("mov %0, %%ds; mov %0, %%es" : : "r"(__USER_DS));
vmx->launched = 1;
@@ -2953,7 +3055,8 @@ static void vmx_vcpu_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
intr_info = vmcs_read32(VM_EXIT_INTR_INFO);
/* We need to handle NMIs before interrupts are enabled */
- if ((intr_info & INTR_INFO_INTR_TYPE_MASK) == 0x200) { /* nmi */
+ if ((intr_info & INTR_INFO_INTR_TYPE_MASK) == 0x200 &&
+ (intr_info & INTR_INFO_VALID_MASK)) {
KVMTRACE_0D(NMI, vcpu, handler);
asm("int $2");
}
@@ -2964,7 +3067,7 @@ static void vmx_free_vmcs(struct kvm_vcpu *vcpu)
struct vcpu_vmx *vmx = to_vmx(vcpu);
if (vmx->vmcs) {
- on_each_cpu(__vcpu_clear, vmx, 0, 1);
+ vcpu_clear(vmx);
free_vmcs(vmx->vmcs);
vmx->vmcs = NULL;
}
@@ -3091,7 +3194,6 @@ static struct kvm_x86_ops vmx_x86_ops = {
.prepare_guest_switch = vmx_save_host_state,
.vcpu_load = vmx_vcpu_load,
.vcpu_put = vmx_vcpu_put,
- .vcpu_decache = vmx_vcpu_decache,
.set_guest_debug = set_guest_debug,
.guest_debug_pre = kvm_guest_debug_pre,
diff --git a/arch/x86/kvm/vmx.h b/arch/x86/kvm/vmx.h
index 79d94c610dfe..425a13436b3f 100644
--- a/arch/x86/kvm/vmx.h
+++ b/arch/x86/kvm/vmx.h
@@ -40,6 +40,7 @@
#define CPU_BASED_CR8_LOAD_EXITING 0x00080000
#define CPU_BASED_CR8_STORE_EXITING 0x00100000
#define CPU_BASED_TPR_SHADOW 0x00200000
+#define CPU_BASED_VIRTUAL_NMI_PENDING 0x00400000
#define CPU_BASED_MOV_DR_EXITING 0x00800000
#define CPU_BASED_UNCOND_IO_EXITING 0x01000000
#define CPU_BASED_USE_IO_BITMAPS 0x02000000
@@ -216,7 +217,7 @@ enum vmcs_field {
#define EXIT_REASON_TRIPLE_FAULT 2
#define EXIT_REASON_PENDING_INTERRUPT 7
-
+#define EXIT_REASON_NMI_WINDOW 8
#define EXIT_REASON_TASK_SWITCH 9
#define EXIT_REASON_CPUID 10
#define EXIT_REASON_HLT 12
@@ -251,7 +252,9 @@ enum vmcs_field {
#define INTR_INFO_VECTOR_MASK 0xff /* 7:0 */
#define INTR_INFO_INTR_TYPE_MASK 0x700 /* 10:8 */
#define INTR_INFO_DELIVER_CODE_MASK 0x800 /* 11 */
+#define INTR_INFO_UNBLOCK_NMI 0x1000 /* 12 */
#define INTR_INFO_VALID_MASK 0x80000000 /* 31 */
+#define INTR_INFO_RESVD_BITS_MASK 0x7ffff000
#define VECTORING_INFO_VECTOR_MASK INTR_INFO_VECTOR_MASK
#define VECTORING_INFO_TYPE_MASK INTR_INFO_INTR_TYPE_MASK
@@ -259,9 +262,16 @@ enum vmcs_field {
#define VECTORING_INFO_VALID_MASK INTR_INFO_VALID_MASK
#define INTR_TYPE_EXT_INTR (0 << 8) /* external interrupt */
+#define INTR_TYPE_NMI_INTR (2 << 8) /* NMI */
#define INTR_TYPE_EXCEPTION (3 << 8) /* processor exception */
#define INTR_TYPE_SOFT_INTR (4 << 8) /* software interrupt */
+/* GUEST_INTERRUPTIBILITY_INFO flags. */
+#define GUEST_INTR_STATE_STI 0x00000001
+#define GUEST_INTR_STATE_MOV_SS 0x00000002
+#define GUEST_INTR_STATE_SMI 0x00000004
+#define GUEST_INTR_STATE_NMI 0x00000008
+
/*
* Exit Qualifications for MOV for Control Register Access
*/
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 21338bdb28ff..369892265a08 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -72,6 +72,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
{ "mmio_exits", VCPU_STAT(mmio_exits) },
{ "signal_exits", VCPU_STAT(signal_exits) },
{ "irq_window", VCPU_STAT(irq_window_exits) },
+ { "nmi_window", VCPU_STAT(nmi_window_exits) },
{ "halt_exits", VCPU_STAT(halt_exits) },
{ "halt_wakeup", VCPU_STAT(halt_wakeup) },
{ "hypercalls", VCPU_STAT(hypercalls) },
@@ -173,6 +174,12 @@ void kvm_inject_page_fault(struct kvm_vcpu *vcpu, unsigned long addr,
kvm_queue_exception_e(vcpu, PF_VECTOR, error_code);
}
+void kvm_inject_nmi(struct kvm_vcpu *vcpu)
+{
+ vcpu->arch.nmi_pending = 1;
+}
+EXPORT_SYMBOL_GPL(kvm_inject_nmi);
+
void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code)
{
WARN_ON(vcpu->arch.exception.pending);
@@ -551,6 +558,38 @@ static void kvm_write_guest_time(struct kvm_vcpu *v)
mark_page_dirty(v->kvm, vcpu->time >> PAGE_SHIFT);
}
+static bool msr_mtrr_valid(unsigned msr)
+{
+ switch (msr) {
+ case 0x200 ... 0x200 + 2 * KVM_NR_VAR_MTRR - 1:
+ case MSR_MTRRfix64K_00000:
+ case MSR_MTRRfix16K_80000:
+ case MSR_MTRRfix16K_A0000:
+ case MSR_MTRRfix4K_C0000:
+ case MSR_MTRRfix4K_C8000:
+ case MSR_MTRRfix4K_D0000:
+ case MSR_MTRRfix4K_D8000:
+ case MSR_MTRRfix4K_E0000:
+ case MSR_MTRRfix4K_E8000:
+ case MSR_MTRRfix4K_F0000:
+ case MSR_MTRRfix4K_F8000:
+ case MSR_MTRRdefType:
+ case MSR_IA32_CR_PAT:
+ return true;
+ case 0x2f8:
+ return true;
+ }
+ return false;
+}
+
+static int set_msr_mtrr(struct kvm_vcpu *vcpu, u32 msr, u64 data)
+{
+ if (!msr_mtrr_valid(msr))
+ return 1;
+
+ vcpu->arch.mtrr[msr - 0x200] = data;
+ return 0;
+}
int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data)
{
@@ -572,8 +611,9 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data)
break;
case MSR_IA32_UCODE_REV:
case MSR_IA32_UCODE_WRITE:
- case 0x200 ... 0x2ff: /* MTRRs */
break;
+ case 0x200 ... 0x2ff:
+ return set_msr_mtrr(vcpu, msr, data);
case MSR_IA32_APICBASE:
kvm_set_apic_base(vcpu, data);
break;
@@ -635,6 +675,15 @@ int kvm_get_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 *pdata)
return kvm_x86_ops->get_msr(vcpu, msr_index, pdata);
}
+static int get_msr_mtrr(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
+{
+ if (!msr_mtrr_valid(msr))
+ return 1;
+
+ *pdata = vcpu->arch.mtrr[msr - 0x200];
+ return 0;
+}
+
int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
{
u64 data;
@@ -656,11 +705,13 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata)
case MSR_IA32_MC0_MISC+16:
case MSR_IA32_UCODE_REV:
case MSR_IA32_EBL_CR_POWERON:
- /* MTRR registers */
- case 0xfe:
- case 0x200 ... 0x2ff:
data = 0;
break;
+ case MSR_MTRRcap:
+ data = 0x500 | KVM_NR_VAR_MTRR;
+ break;
+ case 0x200 ... 0x2ff:
+ return get_msr_mtrr(vcpu, msr, pdata);
case 0xcd: /* fsb frequency */
data = 3;
break;
@@ -768,41 +819,6 @@ out:
return r;
}
-/*
- * Make sure that a cpu that is being hot-unplugged does not have any vcpus
- * cached on it.
- */
-void decache_vcpus_on_cpu(int cpu)
-{
- struct kvm *vm;
- struct kvm_vcpu *vcpu;
- int i;
-
- spin_lock(&kvm_lock);
- list_for_each_entry(vm, &vm_list, vm_list)
- for (i = 0; i < KVM_MAX_VCPUS; ++i) {
- vcpu = vm->vcpus[i];
- if (!vcpu)
- continue;
- /*
- * If the vcpu is locked, then it is running on some
- * other cpu and therefore it is not cached on the
- * cpu in question.
- *
- * If it's not locked, check the last cpu it executed
- * on.
- */
- if (mutex_trylock(&vcpu->mutex)) {
- if (vcpu->cpu == cpu) {
- kvm_x86_ops->vcpu_decache(vcpu);
- vcpu->cpu = -1;
- }
- mutex_unlock(&vcpu->mutex);
- }
- }
- spin_unlock(&kvm_lock);
-}
-
int kvm_dev_ioctl_check_extension(long ext)
{
int r;
@@ -820,6 +836,9 @@ int kvm_dev_ioctl_check_extension(long ext)
case KVM_CAP_MP_STATE:
r = 1;
break;
+ case KVM_CAP_COALESCED_MMIO:
+ r = KVM_COALESCED_MMIO_PAGE_OFFSET;
+ break;
case KVM_CAP_VAPIC:
r = !kvm_x86_ops->cpu_has_accelerated_tpr();
break;
@@ -1732,13 +1751,14 @@ static void kvm_init_msr_list(void)
* Only apic need an MMIO device hook, so shortcut now..
*/
static struct kvm_io_device *vcpu_find_pervcpu_dev(struct kvm_vcpu *vcpu,
- gpa_t addr)
+ gpa_t addr, int len,
+ int is_write)
{
struct kvm_io_device *dev;
if (vcpu->arch.apic) {
dev = &vcpu->arch.apic->dev;
- if (dev->in_range(dev, addr))
+ if (dev->in_range(dev, addr, len, is_write))
return dev;
}
return NULL;
@@ -1746,13 +1766,15 @@ static struct kvm_io_device *vcpu_find_pervcpu_dev(struct kvm_vcpu *vcpu,
static struct kvm_io_device *vcpu_find_mmio_dev(struct kvm_vcpu *vcpu,
- gpa_t addr)
+ gpa_t addr, int len,
+ int is_write)
{
struct kvm_io_device *dev;
- dev = vcpu_find_pervcpu_dev(vcpu, addr);
+ dev = vcpu_find_pervcpu_dev(vcpu, addr, len, is_write);
if (dev == NULL)
- dev = kvm_io_bus_find_dev(&vcpu->kvm->mmio_bus, addr);
+ dev = kvm_io_bus_find_dev(&vcpu->kvm->mmio_bus, addr, len,
+ is_write);
return dev;
}
@@ -1820,7 +1842,7 @@ mmio:
* Is this MMIO handled locally?
*/
mutex_lock(&vcpu->kvm->lock);
- mmio_dev = vcpu_find_mmio_dev(vcpu, gpa);
+ mmio_dev = vcpu_find_mmio_dev(vcpu, gpa, bytes, 0);
if (mmio_dev) {
kvm_iodevice_read(mmio_dev, gpa, bytes, val);
mutex_unlock(&vcpu->kvm->lock);
@@ -1875,7 +1897,7 @@ mmio:
* Is this MMIO handled locally?
*/
mutex_lock(&vcpu->kvm->lock);
- mmio_dev = vcpu_find_mmio_dev(vcpu, gpa);
+ mmio_dev = vcpu_find_mmio_dev(vcpu, gpa, bytes, 1);
if (mmio_dev) {
kvm_iodevice_write(mmio_dev, gpa, bytes, val);
mutex_unlock(&vcpu->kvm->lock);
@@ -1971,6 +1993,7 @@ int emulate_invlpg(struct kvm_vcpu *vcpu, gva_t address)
int emulate_clts(struct kvm_vcpu *vcpu)
{
+ KVMTRACE_0D(CLTS, vcpu, handler);
kvm_x86_ops->set_cr0(vcpu, vcpu->arch.cr0 & ~X86_CR0_TS);
return X86EMUL_CONTINUE;
}
@@ -2009,11 +2032,11 @@ void kvm_report_emulation_failure(struct kvm_vcpu *vcpu, const char *context)
unsigned long rip = vcpu->arch.rip;
unsigned long rip_linear;
- rip_linear = rip + get_segment_base(vcpu, VCPU_SREG_CS);
-
if (reported)
return;
+ rip_linear = rip + get_segment_base(vcpu, VCPU_SREG_CS);
+
emulator_read_std(rip_linear, (void *)opcodes, 4, vcpu);
printk(KERN_ERR "emulation failed (%s) rip %lx %02x %02x %02x %02x\n",
@@ -2251,9 +2274,10 @@ static void pio_string_write(struct kvm_io_device *pio_dev,
}
static struct kvm_io_device *vcpu_find_pio_dev(struct kvm_vcpu *vcpu,
- gpa_t addr)
+ gpa_t addr, int len,
+ int is_write)
{
- return kvm_io_bus_find_dev(&vcpu->kvm->pio_bus, addr);
+ return kvm_io_bus_find_dev(&vcpu->kvm->pio_bus, addr, len, is_write);
}
int kvm_emulate_pio(struct kvm_vcpu *vcpu, struct kvm_run *run, int in,
@@ -2282,11 +2306,10 @@ int kvm_emulate_pio(struct kvm_vcpu *vcpu, struct kvm_run *run, int in,
kvm_x86_ops->cache_regs(vcpu);
memcpy(vcpu->arch.pio_data, &vcpu->arch.regs[VCPU_REGS_RAX], 4);
- kvm_x86_ops->decache_regs(vcpu);
kvm_x86_ops->skip_emulated_instruction(vcpu);
- pio_dev = vcpu_find_pio_dev(vcpu, port);
+ pio_dev = vcpu_find_pio_dev(vcpu, port, size, !in);
if (pio_dev) {
kernel_pio(pio_dev, vcpu, vcpu->arch.pio_data);
complete_pio(vcpu);
@@ -2368,7 +2391,9 @@ int kvm_emulate_pio_string(struct kvm_vcpu *vcpu, struct kvm_run *run, int in,
}
}
- pio_dev = vcpu_find_pio_dev(vcpu, port);
+ pio_dev = vcpu_find_pio_dev(vcpu, port,
+ vcpu->arch.pio.cur_count,
+ !vcpu->arch.pio.in);
if (!vcpu->arch.pio.in) {
/* string PIO write */
ret = pio_copy_data(vcpu);
@@ -2551,27 +2576,41 @@ void realmode_lmsw(struct kvm_vcpu *vcpu, unsigned long msw,
unsigned long realmode_get_cr(struct kvm_vcpu *vcpu, int cr)
{
+ unsigned long value;
+
kvm_x86_ops->decache_cr4_guest_bits(vcpu);
switch (cr) {
case 0:
- return vcpu->arch.cr0;
+ value = vcpu->arch.cr0;
+ break;
case 2:
- return vcpu->arch.cr2;
+ value = vcpu->arch.cr2;
+ break;
case 3:
- return vcpu->arch.cr3;
+ value = vcpu->arch.cr3;
+ break;
case 4:
- return vcpu->arch.cr4;
+ value = vcpu->arch.cr4;
+ break;
case 8:
- return kvm_get_cr8(vcpu);
+ value = kvm_get_cr8(vcpu);
+ break;
default:
vcpu_printf(vcpu, "%s: unexpected cr %u\n", __func__, cr);
return 0;
}
+ KVMTRACE_3D(CR_READ, vcpu, (u32)cr, (u32)value,
+ (u32)((u64)value >> 32), handler);
+
+ return value;
}
void realmode_set_cr(struct kvm_vcpu *vcpu, int cr, unsigned long val,
unsigned long *rflags)
{
+ KVMTRACE_3D(CR_WRITE, vcpu, (u32)cr, (u32)val,
+ (u32)((u64)val >> 32), handler);
+
switch (cr) {
case 0:
kvm_set_cr0(vcpu, mk_cr_64(vcpu->arch.cr0, val));
@@ -2758,7 +2797,9 @@ again:
if (vcpu->requests) {
if (test_and_clear_bit(KVM_REQ_MIGRATE_TIMER, &vcpu->requests))
- __kvm_migrate_apic_timer(vcpu);
+ __kvm_migrate_timers(vcpu);
+ if (test_and_clear_bit(KVM_REQ_TLB_FLUSH, &vcpu->requests))
+ kvm_x86_ops->tlb_flush(vcpu);
if (test_and_clear_bit(KVM_REQ_REPORT_TPR_ACCESS,
&vcpu->requests)) {
kvm_run->exit_reason = KVM_EXIT_TPR_ACCESS;
@@ -2772,6 +2813,7 @@ again:
}
}
+ clear_bit(KVM_REQ_PENDING_TIMER, &vcpu->requests);
kvm_inject_pending_timer_irqs(vcpu);
preempt_disable();
@@ -2781,21 +2823,13 @@ again:
local_irq_disable();
- if (need_resched()) {
+ if (vcpu->requests || need_resched()) {
local_irq_enable();
preempt_enable();
r = 1;
goto out;
}
- if (vcpu->requests)
- if (test_bit(KVM_REQ_MMU_RELOAD, &vcpu->requests)) {
- local_irq_enable();
- preempt_enable();
- r = 1;
- goto out;
- }
-
if (signal_pending(current)) {
local_irq_enable();
preempt_enable();
@@ -2825,9 +2859,6 @@ again:
kvm_guest_enter();
- if (vcpu->requests)
- if (test_and_clear_bit(KVM_REQ_TLB_FLUSH, &vcpu->requests))
- kvm_x86_ops->tlb_flush(vcpu);
KVMTRACE_0D(VMENTRY, vcpu, entryexit);
kvm_x86_ops->run(vcpu, kvm_run);
@@ -3029,8 +3060,8 @@ int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs)
return 0;
}
-static void get_segment(struct kvm_vcpu *vcpu,
- struct kvm_segment *var, int seg)
+void kvm_get_segment(struct kvm_vcpu *vcpu,
+ struct kvm_segment *var, int seg)
{
kvm_x86_ops->get_segment(vcpu, var, seg);
}
@@ -3039,7 +3070,7 @@ void kvm_get_cs_db_l_bits(struct kvm_vcpu *vcpu, int *db, int *l)
{
struct kvm_segment cs;
- get_segment(vcpu, &cs, VCPU_SREG_CS);
+ kvm_get_segment(vcpu, &cs, VCPU_SREG_CS);
*db = cs.db;
*l = cs.l;
}
@@ -3053,15 +3084,15 @@ int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu,
vcpu_load(vcpu);
- get_segment(vcpu, &sregs->cs, VCPU_SREG_CS);
- get_segment(vcpu, &sregs->ds, VCPU_SREG_DS);
- get_segment(vcpu, &sregs->es, VCPU_SREG_ES);
- get_segment(vcpu, &sregs->fs, VCPU_SREG_FS);
- get_segment(vcpu, &sregs->gs, VCPU_SREG_GS);
- get_segment(vcpu, &sregs->ss, VCPU_SREG_SS);
+ kvm_get_segment(vcpu, &sregs->cs, VCPU_SREG_CS);
+ kvm_get_segment(vcpu, &sregs->ds, VCPU_SREG_DS);
+ kvm_get_segment(vcpu, &sregs->es, VCPU_SREG_ES);
+ kvm_get_segment(vcpu, &sregs->fs, VCPU_SREG_FS);
+ kvm_get_segment(vcpu, &sregs->gs, VCPU_SREG_GS);
+ kvm_get_segment(vcpu, &sregs->ss, VCPU_SREG_SS);
- get_segment(vcpu, &sregs->tr, VCPU_SREG_TR);
- get_segment(vcpu, &sregs->ldt, VCPU_SREG_LDTR);
+ kvm_get_segment(vcpu, &sregs->tr, VCPU_SREG_TR);
+ kvm_get_segment(vcpu, &sregs->ldt, VCPU_SREG_LDTR);
kvm_x86_ops->get_idt(vcpu, &dt);
sregs->idt.limit = dt.limit;
@@ -3113,7 +3144,7 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu,
return 0;
}
-static void set_segment(struct kvm_vcpu *vcpu,
+static void kvm_set_segment(struct kvm_vcpu *vcpu,
struct kvm_segment *var, int seg)
{
kvm_x86_ops->set_segment(vcpu, var, seg);
@@ -3150,7 +3181,7 @@ static void get_segment_descritptor_dtable(struct kvm_vcpu *vcpu,
if (selector & 1 << 2) {
struct kvm_segment kvm_seg;
- get_segment(vcpu, &kvm_seg, VCPU_SREG_LDTR);
+ kvm_get_segment(vcpu, &kvm_seg, VCPU_SREG_LDTR);
if (kvm_seg.unusable)
dtable->limit = 0;
@@ -3256,7 +3287,7 @@ static u16 get_segment_selector(struct kvm_vcpu *vcpu, int seg)
{
struct kvm_segment kvm_seg;
- get_segment(vcpu, &kvm_seg, seg);
+ kvm_get_segment(vcpu, &kvm_seg, seg);
return kvm_seg.selector;
}
@@ -3272,8 +3303,8 @@ static int load_segment_descriptor_to_kvm_desct(struct kvm_vcpu *vcpu,
return 0;
}
-static int load_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector,
- int type_bits, int seg)
+int kvm_load_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector,
+ int type_bits, int seg)
{
struct kvm_segment kvm_seg;
@@ -3286,7 +3317,7 @@ static int load_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector,
if (!kvm_seg.s)
kvm_seg.unusable = 1;
- set_segment(vcpu, &kvm_seg, seg);
+ kvm_set_segment(vcpu, &kvm_seg, seg);
return 0;
}
@@ -3332,25 +3363,25 @@ static int load_state_from_tss32(struct kvm_vcpu *vcpu,
vcpu->arch.regs[VCPU_REGS_RSI] = tss->esi;
vcpu->arch.regs[VCPU_REGS_RDI] = tss->edi;
- if (load_segment_descriptor(vcpu, tss->ldt_selector, 0, VCPU_SREG_LDTR))
+ if (kvm_load_segment_descriptor(vcpu, tss->ldt_selector, 0, VCPU_SREG_LDTR))
return 1;
- if (load_segment_descriptor(vcpu, tss->es, 1, VCPU_SREG_ES))
+ if (kvm_load_segment_descriptor(vcpu, tss->es, 1, VCPU_SREG_ES))
return 1;
- if (load_segment_descriptor(vcpu, tss->cs, 9, VCPU_SREG_CS))
+ if (kvm_load_segment_descriptor(vcpu, tss->cs, 9, VCPU_SREG_CS))
return 1;
- if (load_segment_descriptor(vcpu, tss->ss, 1, VCPU_SREG_SS))
+ if (kvm_load_segment_descriptor(vcpu, tss->ss, 1, VCPU_SREG_SS))
return 1;
- if (load_segment_descriptor(vcpu, tss->ds, 1, VCPU_SREG_DS))
+ if (kvm_load_segment_descriptor(vcpu, tss->ds, 1, VCPU_SREG_DS))
return 1;
- if (load_segment_descriptor(vcpu, tss->fs, 1, VCPU_SREG_FS))
+ if (kvm_load_segment_descriptor(vcpu, tss->fs, 1, VCPU_SREG_FS))
return 1;
- if (load_segment_descriptor(vcpu, tss->gs, 1, VCPU_SREG_GS))
+ if (kvm_load_segment_descriptor(vcpu, tss->gs, 1, VCPU_SREG_GS))
return 1;
return 0;
}
@@ -3391,24 +3422,24 @@ static int load_state_from_tss16(struct kvm_vcpu *vcpu,
vcpu->arch.regs[VCPU_REGS_RSI] = tss->si;
vcpu->arch.regs[VCPU_REGS_RDI] = tss->di;
- if (load_segment_descriptor(vcpu, tss->ldt, 0, VCPU_SREG_LDTR))
+ if (kvm_load_segment_descriptor(vcpu, tss->ldt, 0, VCPU_SREG_LDTR))
return 1;
- if (load_segment_descriptor(vcpu, tss->es, 1, VCPU_SREG_ES))
+ if (kvm_load_segment_descriptor(vcpu, tss->es, 1, VCPU_SREG_ES))
return 1;
- if (load_segment_descriptor(vcpu, tss->cs, 9, VCPU_SREG_CS))
+ if (kvm_load_segment_descriptor(vcpu, tss->cs, 9, VCPU_SREG_CS))
return 1;
- if (load_segment_descriptor(vcpu, tss->ss, 1, VCPU_SREG_SS))
+ if (kvm_load_segment_descriptor(vcpu, tss->ss, 1, VCPU_SREG_SS))
return 1;
- if (load_segment_descriptor(vcpu, tss->ds, 1, VCPU_SREG_DS))
+ if (kvm_load_segment_descriptor(vcpu, tss->ds, 1, VCPU_SREG_DS))
return 1;
return 0;
}
-int kvm_task_switch_16(struct kvm_vcpu *vcpu, u16 tss_selector,
+static int kvm_task_switch_16(struct kvm_vcpu *vcpu, u16 tss_selector,
struct desc_struct *cseg_desc,
struct desc_struct *nseg_desc)
{
@@ -3431,7 +3462,7 @@ out:
return ret;
}
-int kvm_task_switch_32(struct kvm_vcpu *vcpu, u16 tss_selector,
+static int kvm_task_switch_32(struct kvm_vcpu *vcpu, u16 tss_selector,
struct desc_struct *cseg_desc,
struct desc_struct *nseg_desc)
{
@@ -3461,7 +3492,7 @@ int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason)
struct desc_struct nseg_desc;
int ret = 0;
- get_segment(vcpu, &tr_seg, VCPU_SREG_TR);
+ kvm_get_segment(vcpu, &tr_seg, VCPU_SREG_TR);
if (load_guest_segment_descriptor(vcpu, tss_selector, &nseg_desc))
goto out;
@@ -3520,7 +3551,7 @@ int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason)
kvm_x86_ops->set_cr0(vcpu, vcpu->arch.cr0 | X86_CR0_TS);
seg_desct_to_kvm_desct(&nseg_desc, tss_selector, &tr_seg);
tr_seg.type = 11;
- set_segment(vcpu, &tr_seg, VCPU_SREG_TR);
+ kvm_set_segment(vcpu, &tr_seg, VCPU_SREG_TR);
out:
kvm_x86_ops->decache_regs(vcpu);
return ret;
@@ -3587,15 +3618,15 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu,
}
}
- set_segment(vcpu, &sregs->cs, VCPU_SREG_CS);
- set_segment(vcpu, &sregs->ds, VCPU_SREG_DS);
- set_segment(vcpu, &sregs->es, VCPU_SREG_ES);
- set_segment(vcpu, &sregs->fs, VCPU_SREG_FS);
- set_segment(vcpu, &sregs->gs, VCPU_SREG_GS);
- set_segment(vcpu, &sregs->ss, VCPU_SREG_SS);
+ kvm_set_segment(vcpu, &sregs->cs, VCPU_SREG_CS);
+ kvm_set_segment(vcpu, &sregs->ds, VCPU_SREG_DS);
+ kvm_set_segment(vcpu, &sregs->es, VCPU_SREG_ES);
+ kvm_set_segment(vcpu, &sregs->fs, VCPU_SREG_FS);
+ kvm_set_segment(vcpu, &sregs->gs, VCPU_SREG_GS);
+ kvm_set_segment(vcpu, &sregs->ss, VCPU_SREG_SS);
- set_segment(vcpu, &sregs->tr, VCPU_SREG_TR);
- set_segment(vcpu, &sregs->ldt, VCPU_SREG_LDTR);
+ kvm_set_segment(vcpu, &sregs->tr, VCPU_SREG_TR);
+ kvm_set_segment(vcpu, &sregs->ldt, VCPU_SREG_LDTR);
vcpu_put(vcpu);
diff --git a/arch/x86/kvm/x86_emulate.c b/arch/x86/kvm/x86_emulate.c
index 8a96320ab071..b90857c76569 100644
--- a/arch/x86/kvm/x86_emulate.c
+++ b/arch/x86/kvm/x86_emulate.c
@@ -121,7 +121,7 @@ static u16 opcode_table[256] = {
0, 0, 0, DstReg | SrcMem32 | ModRM | Mov /* movsxd (x86/64) */ ,
0, 0, 0, 0,
/* 0x68 - 0x6F */
- 0, 0, ImplicitOps | Mov | Stack, 0,
+ SrcImm | Mov | Stack, 0, SrcImmByte | Mov | Stack, 0,
SrcNone | ByteOp | ImplicitOps, SrcNone | ImplicitOps, /* insb, insw/insd */
SrcNone | ByteOp | ImplicitOps, SrcNone | ImplicitOps, /* outsb, outsw/outsd */
/* 0x70 - 0x77 */
@@ -138,7 +138,8 @@ static u16 opcode_table[256] = {
/* 0x88 - 0x8F */
ByteOp | DstMem | SrcReg | ModRM | Mov, DstMem | SrcReg | ModRM | Mov,
ByteOp | DstReg | SrcMem | ModRM | Mov, DstReg | SrcMem | ModRM | Mov,
- 0, ModRM | DstReg, 0, Group | Group1A,
+ DstMem | SrcReg | ModRM | Mov, ModRM | DstReg,
+ DstReg | SrcMem | ModRM | Mov, Group | Group1A,
/* 0x90 - 0x9F */
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, ImplicitOps | Stack, ImplicitOps | Stack, 0, 0,
@@ -152,7 +153,8 @@ static u16 opcode_table[256] = {
ByteOp | ImplicitOps | Mov | String, ImplicitOps | Mov | String,
ByteOp | ImplicitOps | String, ImplicitOps | String,
/* 0xB0 - 0xBF */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ DstReg | SrcImm | Mov, 0, 0, 0, 0, 0, 0, 0,
/* 0xC0 - 0xC7 */
ByteOp | DstMem | SrcImm | ModRM, DstMem | SrcImmByte | ModRM,
0, ImplicitOps | Stack, 0, 0,
@@ -168,7 +170,8 @@ static u16 opcode_table[256] = {
/* 0xE0 - 0xE7 */
0, 0, 0, 0, 0, 0, 0, 0,
/* 0xE8 - 0xEF */
- ImplicitOps | Stack, SrcImm|ImplicitOps, 0, SrcImmByte|ImplicitOps,
+ ImplicitOps | Stack, SrcImm | ImplicitOps,
+ ImplicitOps, SrcImmByte | ImplicitOps,
0, 0, 0, 0,
/* 0xF0 - 0xF7 */
0, 0, 0, 0,
@@ -1049,6 +1052,7 @@ done_prefixes:
break;
case DstMem:
if ((c->d & ModRM) && c->modrm_mod == 3) {
+ c->dst.bytes = (c->d & ByteOp) ? 1 : c->op_bytes;
c->dst.type = OP_REG;
c->dst.val = c->dst.orig_val = c->modrm_val;
c->dst.ptr = c->modrm_ptr;
@@ -1420,9 +1424,8 @@ special_insn:
goto cannot_emulate;
c->dst.val = (s32) c->src.val;
break;
+ case 0x68: /* push imm */
case 0x6a: /* push imm8 */
- c->src.val = 0L;
- c->src.val = insn_fetch(s8, 1, c->eip);
emulate_push(ctxt);
break;
case 0x6c: /* insb */
@@ -1514,9 +1517,44 @@ special_insn:
break;
case 0x88 ... 0x8b: /* mov */
goto mov;
+ case 0x8c: { /* mov r/m, sreg */
+ struct kvm_segment segreg;
+
+ if (c->modrm_reg <= 5)
+ kvm_get_segment(ctxt->vcpu, &segreg, c->modrm_reg);
+ else {
+ printk(KERN_INFO "0x8c: Invalid segreg in modrm byte 0x%02x\n",
+ c->modrm);
+ goto cannot_emulate;
+ }
+ c->dst.val = segreg.selector;
+ break;
+ }
case 0x8d: /* lea r16/r32, m */
c->dst.val = c->modrm_ea;
break;
+ case 0x8e: { /* mov seg, r/m16 */
+ uint16_t sel;
+ int type_bits;
+ int err;
+
+ sel = c->src.val;
+ if (c->modrm_reg <= 5) {
+ type_bits = (c->modrm_reg == 1) ? 9 : 1;
+ err = kvm_load_segment_descriptor(ctxt->vcpu, sel,
+ type_bits, c->modrm_reg);
+ } else {
+ printk(KERN_INFO "Invalid segreg in modrm byte 0x%02x\n",
+ c->modrm);
+ goto cannot_emulate;
+ }
+
+ if (err < 0)
+ goto cannot_emulate;
+
+ c->dst.type = OP_NONE; /* Disable writeback. */
+ break;
+ }
case 0x8f: /* pop (sole member of Grp1a) */
rc = emulate_grp1a(ctxt, ops);
if (rc != 0)
@@ -1622,6 +1660,8 @@ special_insn:
case 0xae ... 0xaf: /* scas */
DPRINTF("Urk! I don't handle SCAS.\n");
goto cannot_emulate;
+ case 0xb8: /* mov r, imm */
+ goto mov;
case 0xc0 ... 0xc1:
emulate_grp2(ctxt);
break;
@@ -1660,7 +1700,33 @@ special_insn:
break;
}
case 0xe9: /* jmp rel */
- case 0xeb: /* jmp rel short */
+ goto jmp;
+ case 0xea: /* jmp far */ {
+ uint32_t eip;
+ uint16_t sel;
+
+ switch (c->op_bytes) {
+ case 2:
+ eip = insn_fetch(u16, 2, c->eip);
+ break;
+ case 4:
+ eip = insn_fetch(u32, 4, c->eip);
+ break;
+ default:
+ DPRINTF("jmp far: Invalid op_bytes\n");
+ goto cannot_emulate;
+ }
+ sel = insn_fetch(u16, 2, c->eip);
+ if (kvm_load_segment_descriptor(ctxt->vcpu, sel, 9, VCPU_SREG_CS) < 0) {
+ DPRINTF("jmp far: Failed to load CS descriptor\n");
+ goto cannot_emulate;
+ }
+
+ c->eip = eip;
+ break;
+ }
+ case 0xeb:
+ jmp: /* jmp rel short */
jmp_rel(c, c->src.val);
c->dst.type = OP_NONE; /* Disable writeback. */
break;
@@ -1727,7 +1793,8 @@ twobyte_insn:
if (rc)
goto done;
- kvm_emulate_hypercall(ctxt->vcpu);
+ /* Let the processor re-execute the fixed hypercall */
+ c->eip = ctxt->vcpu->arch.rip;
/* Disable writeback. */
c->dst.type = OP_NONE;
break;
diff --git a/arch/x86/lguest/boot.c b/arch/x86/lguest/boot.c
index 5c7e2fd52075..5e4772907c6e 100644
--- a/arch/x86/lguest/boot.c
+++ b/arch/x86/lguest/boot.c
@@ -1012,6 +1012,7 @@ __init void lguest_init(void)
* clobbered. The Launcher places our initial pagetables somewhere at
* the top of our physical memory, so we don't need extra space: set
* init_pg_tables_end to the end of the kernel. */
+ init_pg_tables_start = __pa(pg0);
init_pg_tables_end = __pa(pg0);
/* Load the %fs segment register (the per-cpu segment register) with
@@ -1065,9 +1066,9 @@ __init void lguest_init(void)
pm_power_off = lguest_power_off;
machine_ops.restart = lguest_restart;
- /* Now we're set up, call start_kernel() in init/main.c and we proceed
+ /* Now we're set up, call i386_start_kernel() in head32.c and we proceed
* to boot as normal. It never returns. */
- start_kernel();
+ i386_start_kernel();
}
/*
* This marks the end of stage II of our journey, The Guest.
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
index 76f60f52a885..84aa2883fe15 100644
--- a/arch/x86/lib/Makefile
+++ b/arch/x86/lib/Makefile
@@ -5,6 +5,7 @@
obj-$(CONFIG_SMP) := msr-on-cpu.o
lib-y := delay_$(BITS).o
+lib-y += thunk_$(BITS).o
lib-y += usercopy_$(BITS).o getuser_$(BITS).o putuser_$(BITS).o
lib-y += memcpy_$(BITS).o
diff --git a/arch/x86/lib/thunk_32.S b/arch/x86/lib/thunk_32.S
new file mode 100644
index 000000000000..650b11e00ecc
--- /dev/null
+++ b/arch/x86/lib/thunk_32.S
@@ -0,0 +1,47 @@
+/*
+ * Trampoline to trace irqs off. (otherwise CALLER_ADDR1 might crash)
+ * Copyright 2008 by Steven Rostedt, Red Hat, Inc
+ * (inspired by Andi Kleen's thunk_64.S)
+ * Subject to the GNU public license, v.2. No warranty of any kind.
+ */
+
+ #include <linux/linkage.h>
+
+#define ARCH_TRACE_IRQS_ON \
+ pushl %eax; \
+ pushl %ecx; \
+ pushl %edx; \
+ call trace_hardirqs_on; \
+ popl %edx; \
+ popl %ecx; \
+ popl %eax;
+
+#define ARCH_TRACE_IRQS_OFF \
+ pushl %eax; \
+ pushl %ecx; \
+ pushl %edx; \
+ call trace_hardirqs_off; \
+ popl %edx; \
+ popl %ecx; \
+ popl %eax;
+
+#ifdef CONFIG_TRACE_IRQFLAGS
+ /* put return address in eax (arg1) */
+ .macro thunk_ra name,func
+ .globl \name
+\name:
+ pushl %eax
+ pushl %ecx
+ pushl %edx
+ /* Place EIP in the arg1 */
+ movl 3*4(%esp), %eax
+ call \func
+ popl %edx
+ popl %ecx
+ popl %eax
+ ret
+ .endm
+
+ thunk_ra trace_hardirqs_on_thunk,trace_hardirqs_on_caller
+ thunk_ra trace_hardirqs_off_thunk,trace_hardirqs_off_caller
+#endif
diff --git a/arch/x86/lib/thunk_64.S b/arch/x86/lib/thunk_64.S
index e009251d4e9f..bf9a7d5a5428 100644
--- a/arch/x86/lib/thunk_64.S
+++ b/arch/x86/lib/thunk_64.S
@@ -2,6 +2,7 @@
* Save registers before calling assembly functions. This avoids
* disturbance of register allocation in some inline assembly constructs.
* Copyright 2001,2002 by Andi Kleen, SuSE Labs.
+ * Added trace_hardirqs callers - Copyright 2007 Steven Rostedt, Red Hat, Inc.
* Subject to the GNU public license, v.2. No warranty of any kind.
*/
@@ -42,8 +43,22 @@
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
- thunk trace_hardirqs_on_thunk,trace_hardirqs_on
- thunk trace_hardirqs_off_thunk,trace_hardirqs_off
+ /* put return address in rdi (arg1) */
+ .macro thunk_ra name,func
+ .globl \name
+\name:
+ CFI_STARTPROC
+ SAVE_ARGS
+ /* SAVE_ARGS pushs 9 elements */
+ /* the next element would be the rip */
+ movq 9*8(%rsp), %rdi
+ call \func
+ jmp restore
+ CFI_ENDPROC
+ .endm
+
+ thunk_ra trace_hardirqs_on_thunk,trace_hardirqs_on_caller
+ thunk_ra trace_hardirqs_off_thunk,trace_hardirqs_off_caller
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
diff --git a/arch/x86/mach-default/setup.c b/arch/x86/mach-default/setup.c
index 0c28a071824c..56b4c39cb7fa 100644
--- a/arch/x86/mach-default/setup.c
+++ b/arch/x86/mach-default/setup.c
@@ -153,6 +153,7 @@ late_initcall(print_ipi_mode);
char * __init machine_specific_memory_setup(void)
{
char *who;
+ int new_nr;
who = "BIOS-e820";
@@ -163,7 +164,11 @@ char * __init machine_specific_memory_setup(void)
* Otherwise fake a memory map; one section from 0k->640k,
* the next section from 1mb->appropriate_mem_k
*/
- sanitize_e820_map(boot_params.e820_map, &boot_params.e820_entries);
+ new_nr = boot_params.e820_entries;
+ sanitize_e820_map(boot_params.e820_map,
+ ARRAY_SIZE(boot_params.e820_map),
+ &new_nr);
+ boot_params.e820_entries = new_nr;
if (copy_e820_map(boot_params.e820_map, boot_params.e820_entries)
< 0) {
unsigned long mem_size;
diff --git a/arch/x86/mach-es7000/Makefile b/arch/x86/mach-es7000/Makefile
index 69dd4da218dc..3ef8b43b62fc 100644
--- a/arch/x86/mach-es7000/Makefile
+++ b/arch/x86/mach-es7000/Makefile
@@ -3,4 +3,3 @@
#
obj-$(CONFIG_X86_ES7000) := es7000plat.o
-obj-$(CONFIG_X86_GENERICARCH) := es7000plat.o
diff --git a/arch/x86/mach-es7000/es7000plat.c b/arch/x86/mach-es7000/es7000plat.c
index f5d6f7d8b86e..4354ce804889 100644
--- a/arch/x86/mach-es7000/es7000plat.c
+++ b/arch/x86/mach-es7000/es7000plat.c
@@ -52,6 +52,8 @@ static struct mip_reg *host_reg;
static int mip_port;
static unsigned long mip_addr, host_addr;
+int es7000_plat;
+
/*
* GSI override for ES7000 platforms.
*/
@@ -175,53 +177,6 @@ find_unisys_acpi_oem_table(unsigned long *oem_addr)
}
#endif
-/*
- * This file also gets compiled if CONFIG_X86_GENERICARCH is set. Generic
- * arch already has got following function definitions (asm-generic/es7000.c)
- * hence no need to define these for that case.
- */
-#ifndef CONFIG_X86_GENERICARCH
-void es7000_sw_apic(void);
-void __init enable_apic_mode(void)
-{
- es7000_sw_apic();
- return;
-}
-
-__init int mps_oem_check(struct mp_config_table *mpc, char *oem,
- char *productid)
-{
- if (mpc->mpc_oemptr) {
- struct mp_config_oemtable *oem_table =
- (struct mp_config_oemtable *)mpc->mpc_oemptr;
- if (!strncmp(oem, "UNISYS", 6))
- return parse_unisys_oem((char *)oem_table);
- }
- return 0;
-}
-#ifdef CONFIG_ACPI
-/* Hook from generic ACPI tables.c */
-int __init acpi_madt_oem_check(char *oem_id, char *oem_table_id)
-{
- unsigned long oem_addr;
- if (!find_unisys_acpi_oem_table(&oem_addr)) {
- if (es7000_check_dsdt())
- return parse_unisys_oem((char *)oem_addr);
- else {
- setup_unisys();
- return 1;
- }
- }
- return 0;
-}
-#else
-int __init acpi_madt_oem_check(char *oem_id, char *oem_table_id)
-{
- return 0;
-}
-#endif
-#endif /* COFIG_X86_GENERICARCH */
-
static void
es7000_spin(int n)
{
diff --git a/arch/x86/mach-generic/Makefile b/arch/x86/mach-generic/Makefile
index 19d6d407737b..0dbd7803a1d5 100644
--- a/arch/x86/mach-generic/Makefile
+++ b/arch/x86/mach-generic/Makefile
@@ -2,7 +2,11 @@
# Makefile for the generic architecture
#
-EXTRA_CFLAGS := -Iarch/x86/kernel
+EXTRA_CFLAGS := -Iarch/x86/kernel
-obj-y := probe.o summit.o bigsmp.o es7000.o default.o
-obj-y += ../../x86/mach-es7000/
+obj-y := probe.o default.o
+obj-$(CONFIG_X86_NUMAQ) += numaq.o
+obj-$(CONFIG_X86_SUMMIT) += summit.o
+obj-$(CONFIG_X86_BIGSMP) += bigsmp.o
+obj-$(CONFIG_X86_ES7000) += es7000.o
+obj-$(CONFIG_X86_ES7000) += ../../x86/mach-es7000/
diff --git a/arch/x86/mach-generic/bigsmp.c b/arch/x86/mach-generic/bigsmp.c
index 95fc463056d0..59d771714559 100644
--- a/arch/x86/mach-generic/bigsmp.c
+++ b/arch/x86/mach-generic/bigsmp.c
@@ -23,10 +23,8 @@ static int dmi_bigsmp; /* can be set by dmi scanners */
static int hp_ht_bigsmp(const struct dmi_system_id *d)
{
-#ifdef CONFIG_X86_GENERICARCH
printk(KERN_NOTICE "%s detected: force use of apic=bigsmp\n", d->ident);
dmi_bigsmp = 1;
-#endif
return 0;
}
@@ -48,7 +46,7 @@ static const struct dmi_system_id bigsmp_dmi_table[] = {
static int probe_bigsmp(void)
{
if (def_to_bigsmp)
- dmi_bigsmp = 1;
+ dmi_bigsmp = 1;
else
dmi_check_system(bigsmp_dmi_table);
return dmi_bigsmp;
diff --git a/arch/x86/mach-generic/numaq.c b/arch/x86/mach-generic/numaq.c
new file mode 100644
index 000000000000..8091e68764c4
--- /dev/null
+++ b/arch/x86/mach-generic/numaq.c
@@ -0,0 +1,41 @@
+/*
+ * APIC driver for the IBM NUMAQ chipset.
+ */
+#define APIC_DEFINITION 1
+#include <linux/threads.h>
+#include <linux/cpumask.h>
+#include <linux/smp.h>
+#include <asm/mpspec.h>
+#include <asm/genapic.h>
+#include <asm/fixmap.h>
+#include <asm/apicdef.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <asm/mach-numaq/mach_apic.h>
+#include <asm/mach-numaq/mach_apicdef.h>
+#include <asm/mach-numaq/mach_ipi.h>
+#include <asm/mach-numaq/mach_mpparse.h>
+#include <asm/mach-numaq/mach_wakecpu.h>
+#include <asm/numaq.h>
+
+static int mps_oem_check(struct mp_config_table *mpc, char *oem,
+ char *productid)
+{
+ numaq_mps_oem_check(mpc, oem, productid);
+ return found_numaq;
+}
+
+static int probe_numaq(void)
+{
+ /* already know from get_memcfg_numaq() */
+ return found_numaq;
+}
+
+/* Hook from generic ACPI tables.c */
+static int acpi_madt_oem_check(char *oem_id, char *oem_table_id)
+{
+ return 0;
+}
+
+struct genapic apic_numaq = APIC_INIT("NUMAQ", probe_numaq);
diff --git a/arch/x86/mach-generic/probe.c b/arch/x86/mach-generic/probe.c
index c5ae751b994a..ba18dec48555 100644
--- a/arch/x86/mach-generic/probe.c
+++ b/arch/x86/mach-generic/probe.c
@@ -16,6 +16,7 @@
#include <asm/apicdef.h>
#include <asm/genapic.h>
+extern struct genapic apic_numaq;
extern struct genapic apic_summit;
extern struct genapic apic_bigsmp;
extern struct genapic apic_es7000;
@@ -24,9 +25,18 @@ extern struct genapic apic_default;
struct genapic *genapic = &apic_default;
static struct genapic *apic_probe[] __initdata = {
+#ifdef CONFIG_X86_NUMAQ
+ &apic_numaq,
+#endif
+#ifdef CONFIG_X86_SUMMIT
&apic_summit,
+#endif
+#ifdef CONFIG_X86_BIGSMP
&apic_bigsmp,
+#endif
+#ifdef CONFIG_X86_ES7000
&apic_es7000,
+#endif
&apic_default, /* must be last */
NULL,
};
@@ -54,6 +64,7 @@ early_param("apic", parse_apic);
void __init generic_bigsmp_probe(void)
{
+#if CONFIG_X86_BIGSMP
/*
* This routine is used to switch to bigsmp mode when
* - There is no apic= option specified by the user
@@ -67,6 +78,7 @@ void __init generic_bigsmp_probe(void)
printk(KERN_INFO "Overriding APIC driver with %s\n",
genapic->name);
}
+#endif
}
void __init generic_apic_probe(void)
@@ -88,7 +100,8 @@ void __init generic_apic_probe(void)
/* These functions can switch the APIC even after the initial ->probe() */
-int __init mps_oem_check(struct mp_config_table *mpc, char *oem, char *productid)
+int __init mps_oem_check(struct mp_config_table *mpc, char *oem,
+ char *productid)
{
int i;
for (i = 0; apic_probe[i]; ++i) {
diff --git a/arch/x86/mach-visws/mpparse.c b/arch/x86/mach-visws/mpparse.c
index 57484e91ab90..a2fb78c0d154 100644
--- a/arch/x86/mach-visws/mpparse.c
+++ b/arch/x86/mach-visws/mpparse.c
@@ -8,11 +8,6 @@
#include "cobalt.h"
#include "mach_apic.h"
-/* Have we found an MP table */
-int smp_found_config;
-
-int pic_mode;
-
extern unsigned int __cpuinitdata maxcpus;
/*
@@ -76,7 +71,9 @@ void __init find_smp_config(void)
if (ncpus > maxcpus)
ncpus = maxcpus;
+#ifdef CONFIG_X86_LOCAL_APIC
smp_found_config = 1;
+#endif
while (ncpus--)
MP_processor_info(mp++);
diff --git a/arch/x86/mach-visws/visws_apic.c b/arch/x86/mach-visws/visws_apic.c
index cef9cb1d15ac..d8b2cfd85d92 100644
--- a/arch/x86/mach-visws/visws_apic.c
+++ b/arch/x86/mach-visws/visws_apic.c
@@ -21,10 +21,9 @@
#include <asm/io.h>
#include <asm/apic.h>
#include <asm/i8259.h>
+#include <asm/irq_vectors.h>
#include "cobalt.h"
-#include "irq_vectors.h"
-
static DEFINE_SPINLOCK(cobalt_lock);
diff --git a/arch/x86/mach-voyager/setup.c b/arch/x86/mach-voyager/setup.c
index 5ae5466b9eb9..f4aca9fa9546 100644
--- a/arch/x86/mach-voyager/setup.c
+++ b/arch/x86/mach-voyager/setup.c
@@ -62,6 +62,7 @@ void __init time_init_hook(void)
char *__init machine_specific_memory_setup(void)
{
char *who;
+ int new_nr;
who = "NOT VOYAGER";
@@ -111,7 +112,11 @@ char *__init machine_specific_memory_setup(void)
* Otherwise fake a memory map; one section from 0k->640k,
* the next section from 1mb->appropriate_mem_k
*/
- sanitize_e820_map(boot_params.e820_map, &boot_params.e820_entries);
+ new_nr = boot_params.e820_entries;
+ sanitize_e820_map(boot_params.e820_map,
+ ARRAY_SIZE(boot_params.e820_map),
+ &new_nr);
+ boot_params.e820_entries = new_nr;
if (copy_e820_map(boot_params.e820_map, boot_params.e820_entries)
< 0) {
unsigned long mem_size;
diff --git a/arch/x86/mach-voyager/voyager_smp.c b/arch/x86/mach-voyager/voyager_smp.c
index 8acbf0cdf1a5..8dedd01e909f 100644
--- a/arch/x86/mach-voyager/voyager_smp.c
+++ b/arch/x86/mach-voyager/voyager_smp.c
@@ -59,11 +59,6 @@ __u32 voyager_quad_processors = 0;
* activity count. Finally exported by i386_ksyms.c */
static int voyager_extended_cpus = 1;
-/* Have we found an SMP box - used by time.c to do the profiling
- interrupt for timeslicing; do not set to 1 until the per CPU timer
- interrupt is active */
-int smp_found_config = 0;
-
/* Used for the invalidate map that's also checked in the spinlock */
static volatile unsigned long smp_invalidate_needed;
@@ -1137,15 +1132,6 @@ void flush_tlb_all(void)
on_each_cpu(do_flush_tlb_all, 0, 1, 1);
}
-/* used to set up the trampoline for other CPUs when the memory manager
- * is sorted out */
-void __init smp_alloc_memory(void)
-{
- trampoline_base = alloc_bootmem_low_pages(PAGE_SIZE);
- if (__pa(trampoline_base) >= 0x93000)
- BUG();
-}
-
/* send a reschedule CPI to one CPU by physical CPU number*/
static void voyager_smp_send_reschedule(int cpu)
{
diff --git a/arch/x86/mm/Makefile b/arch/x86/mm/Makefile
index b7b3e4c7cfc9..07dab503c9e3 100644
--- a/arch/x86/mm/Makefile
+++ b/arch/x86/mm/Makefile
@@ -8,6 +8,11 @@ obj-$(CONFIG_X86_PTDUMP) += dump_pagetables.o
obj-$(CONFIG_HIGHMEM) += highmem_32.o
+obj-$(CONFIG_MMIOTRACE_HOOKS) += kmmio.o
+obj-$(CONFIG_MMIOTRACE) += mmiotrace.o
+mmiotrace-y := pf_in.o mmio-mod.o
+obj-$(CONFIG_MMIOTRACE_TEST) += testmmiotrace.o
+
ifeq ($(CONFIG_X86_32),y)
obj-$(CONFIG_NUMA) += discontig_32.o
else
diff --git a/arch/x86/mm/discontig_32.c b/arch/x86/mm/discontig_32.c
index 914ccf983687..c6ce6a5e183f 100644
--- a/arch/x86/mm/discontig_32.c
+++ b/arch/x86/mm/discontig_32.c
@@ -38,6 +38,7 @@
#include <asm/setup.h>
#include <asm/mmzone.h>
#include <asm/bios_ebda.h>
+#include <asm/proto.h>
struct pglist_data *node_data[MAX_NUMNODES] __read_mostly;
EXPORT_SYMBOL(node_data);
@@ -59,14 +60,14 @@ unsigned long node_end_pfn[MAX_NUMNODES] __read_mostly;
/*
* 4) physnode_map - the mapping between a pfn and owning node
* physnode_map keeps track of the physical memory layout of a generic
- * numa node on a 256Mb break (each element of the array will
- * represent 256Mb of memory and will be marked by the node id. so,
+ * numa node on a 64Mb break (each element of the array will
+ * represent 64Mb of memory and will be marked by the node id. so,
* if the first gig is on node 0, and the second gig is on node 1
* physnode_map will contain:
*
- * physnode_map[0-3] = 0;
- * physnode_map[4-7] = 1;
- * physnode_map[8- ] = -1;
+ * physnode_map[0-15] = 0;
+ * physnode_map[16-31] = 1;
+ * physnode_map[32- ] = -1;
*/
s8 physnode_map[MAX_ELEMENTS] __read_mostly = { [0 ... (MAX_ELEMENTS - 1)] = -1};
EXPORT_SYMBOL(physnode_map);
@@ -81,9 +82,9 @@ void memory_present(int nid, unsigned long start, unsigned long end)
printk(KERN_DEBUG " ");
for (pfn = start; pfn < end; pfn += PAGES_PER_ELEMENT) {
physnode_map[pfn / PAGES_PER_ELEMENT] = nid;
- printk("%ld ", pfn);
+ printk(KERN_CONT "%ld ", pfn);
}
- printk("\n");
+ printk(KERN_CONT "\n");
}
unsigned long node_memmap_size_bytes(int nid, unsigned long start_pfn,
@@ -119,11 +120,11 @@ int __init get_memcfg_numa_flat(void)
{
printk("NUMA - single node, flat memory mode\n");
- /* Run the memory configuration and find the top of memory. */
- propagate_e820_map();
node_start_pfn[0] = 0;
node_end_pfn[0] = max_pfn;
+ e820_register_active_regions(0, 0, max_pfn);
memory_present(0, 0, max_pfn);
+ node_remap_size[0] = node_memmap_size_bytes(0, 0, max_pfn);
/* Indicate there is one node available. */
nodes_clear(node_online_map);
@@ -156,24 +157,29 @@ static void __init propagate_e820_map_node(int nid)
*/
static void __init allocate_pgdat(int nid)
{
- if (nid && node_has_online_mem(nid))
+ if (nid && node_has_online_mem(nid) && node_remap_start_vaddr[nid])
NODE_DATA(nid) = (pg_data_t *)node_remap_start_vaddr[nid];
else {
- NODE_DATA(nid) = (pg_data_t *)(pfn_to_kaddr(min_low_pfn));
- min_low_pfn += PFN_UP(sizeof(pg_data_t));
+ unsigned long pgdat_phys;
+ pgdat_phys = find_e820_area(min_low_pfn<<PAGE_SHIFT,
+ (nid ? max_low_pfn:max_pfn_mapped)<<PAGE_SHIFT,
+ sizeof(pg_data_t),
+ PAGE_SIZE);
+ NODE_DATA(nid) = (pg_data_t *)(pfn_to_kaddr(pgdat_phys>>PAGE_SHIFT));
+ reserve_early(pgdat_phys, pgdat_phys + sizeof(pg_data_t),
+ "NODE_DATA");
}
+ printk(KERN_DEBUG "allocate_pgdat: node %d NODE_DATA %08lx\n",
+ nid, (unsigned long)NODE_DATA(nid));
}
-#ifdef CONFIG_DISCONTIGMEM
/*
- * In the discontig memory model, a portion of the kernel virtual area (KVA)
- * is reserved and portions of nodes are mapped using it. This is to allow
- * node-local memory to be allocated for structures that would normally require
- * ZONE_NORMAL. The memory is allocated with alloc_remap() and callers
- * should be prepared to allocate from the bootmem allocator instead. This KVA
- * mechanism is incompatible with SPARSEMEM as it makes assumptions about the
- * layout of memory that are broken if alloc_remap() succeeds for some of the
- * map and fails for others
+ * In the DISCONTIGMEM and SPARSEMEM memory model, a portion of the kernel
+ * virtual address space (KVA) is reserved and portions of nodes are mapped
+ * using it. This is to allow node-local memory to be allocated for
+ * structures that would normally require ZONE_NORMAL. The memory is
+ * allocated with alloc_remap() and callers should be prepared to allocate
+ * from the bootmem allocator instead.
*/
static unsigned long node_remap_start_pfn[MAX_NUMNODES];
static void *node_remap_end_vaddr[MAX_NUMNODES];
@@ -202,8 +208,12 @@ void __init remap_numa_kva(void)
int node;
for_each_online_node(node) {
+ printk(KERN_DEBUG "remap_numa_kva: node %d\n", node);
for (pfn=0; pfn < node_remap_size[node]; pfn += PTRS_PER_PTE) {
vaddr = node_remap_start_vaddr[node]+(pfn<<PAGE_SHIFT);
+ printk(KERN_DEBUG "remap_numa_kva: %08lx to pfn %08lx\n",
+ (unsigned long)vaddr,
+ node_remap_start_pfn[node] + pfn);
set_pmd_pfn((ulong) vaddr,
node_remap_start_pfn[node] + pfn,
PAGE_KERNEL_LARGE);
@@ -215,17 +225,21 @@ static unsigned long calculate_numa_remap_pages(void)
{
int nid;
unsigned long size, reserve_pages = 0;
- unsigned long pfn;
for_each_online_node(nid) {
- unsigned old_end_pfn = node_end_pfn[nid];
+ u64 node_end_target;
+ u64 node_end_final;
/*
* The acpi/srat node info can show hot-add memroy zones
* where memory could be added but not currently present.
*/
+ printk("node %d pfn: [%lx - %lx]\n",
+ nid, node_start_pfn[nid], node_end_pfn[nid]);
if (node_start_pfn[nid] > max_pfn)
continue;
+ if (!node_end_pfn[nid])
+ continue;
if (node_end_pfn[nid] > max_pfn)
node_end_pfn[nid] = max_pfn;
@@ -237,39 +251,42 @@ static unsigned long calculate_numa_remap_pages(void)
/* now the roundup is correct, convert to PAGE_SIZE pages */
size = size * PTRS_PER_PTE;
- /*
- * Validate the region we are allocating only contains valid
- * pages.
- */
- for (pfn = node_end_pfn[nid] - size;
- pfn < node_end_pfn[nid]; pfn++)
- if (!page_is_ram(pfn))
- break;
-
- if (pfn != node_end_pfn[nid])
- size = 0;
+ node_end_target = round_down(node_end_pfn[nid] - size,
+ PTRS_PER_PTE);
+ node_end_target <<= PAGE_SHIFT;
+ do {
+ node_end_final = find_e820_area(node_end_target,
+ ((u64)node_end_pfn[nid])<<PAGE_SHIFT,
+ ((u64)size)<<PAGE_SHIFT,
+ LARGE_PAGE_BYTES);
+ node_end_target -= LARGE_PAGE_BYTES;
+ } while (node_end_final == -1ULL &&
+ (node_end_target>>PAGE_SHIFT) > (node_start_pfn[nid]));
+
+ if (node_end_final == -1ULL)
+ panic("Can not get kva ram\n");
printk("Reserving %ld pages of KVA for lmem_map of node %d\n",
size, nid);
node_remap_size[nid] = size;
node_remap_offset[nid] = reserve_pages;
reserve_pages += size;
- printk("Shrinking node %d from %ld pages to %ld pages\n",
- nid, node_end_pfn[nid], node_end_pfn[nid] - size);
-
- if (node_end_pfn[nid] & (PTRS_PER_PTE-1)) {
- /*
- * Align node_end_pfn[] and node_remap_start_pfn[] to
- * pmd boundary. remap_numa_kva will barf otherwise.
- */
- printk("Shrinking node %d further by %ld pages for proper alignment\n",
- nid, node_end_pfn[nid] & (PTRS_PER_PTE-1));
- size += node_end_pfn[nid] & (PTRS_PER_PTE-1);
- }
+ printk("Shrinking node %d from %ld pages to %lld pages\n",
+ nid, node_end_pfn[nid], node_end_final>>PAGE_SHIFT);
- node_end_pfn[nid] -= size;
+ /*
+ * prevent kva address below max_low_pfn want it on system
+ * with less memory later.
+ * layout will be: KVA address , KVA RAM
+ */
+ if ((node_end_final>>PAGE_SHIFT) < max_low_pfn)
+ reserve_early(node_end_final,
+ node_end_final+(((u64)size)<<PAGE_SHIFT),
+ "KVA RAM");
+
+ node_end_pfn[nid] = node_end_final>>PAGE_SHIFT;
node_remap_start_pfn[nid] = node_end_pfn[nid];
- shrink_active_range(nid, old_end_pfn, node_end_pfn[nid]);
+ shrink_active_range(nid, node_end_pfn[nid]);
}
printk("Reserving total of %ld pages for numa KVA remap\n",
reserve_pages);
@@ -287,35 +304,15 @@ static void init_remap_allocator(int nid)
printk ("node %d will remap to vaddr %08lx - %08lx\n", nid,
(ulong) node_remap_start_vaddr[nid],
- (ulong) pfn_to_kaddr(highstart_pfn
- + node_remap_offset[nid] + node_remap_size[nid]));
-}
-#else
-void *alloc_remap(int nid, unsigned long size)
-{
- return NULL;
+ (ulong) node_remap_end_vaddr[nid]);
}
-static unsigned long calculate_numa_remap_pages(void)
-{
- return 0;
-}
-
-static void init_remap_allocator(int nid)
-{
-}
-
-void __init remap_numa_kva(void)
-{
-}
-#endif /* CONFIG_DISCONTIGMEM */
-
extern void setup_bootmem_allocator(void);
unsigned long __init setup_memory(void)
{
int nid;
unsigned long system_start_pfn, system_max_low_pfn;
- unsigned long wasted_pages;
+ long kva_target_pfn;
/*
* When mapping a NUMA machine we allocate the node_mem_map arrays
@@ -324,34 +321,38 @@ unsigned long __init setup_memory(void)
* this space and use it to adjust the boundary between ZONE_NORMAL
* and ZONE_HIGHMEM.
*/
+
+ /* call find_max_low_pfn at first, it could update max_pfn */
+ system_max_low_pfn = max_low_pfn = find_max_low_pfn();
+
+ remove_all_active_ranges();
get_memcfg_numa();
- kva_pages = calculate_numa_remap_pages();
+ kva_pages = round_up(calculate_numa_remap_pages(), PTRS_PER_PTE);
/* partially used pages are not usable - thus round upwards */
system_start_pfn = min_low_pfn = PFN_UP(init_pg_tables_end);
- kva_start_pfn = find_max_low_pfn() - kva_pages;
+ kva_target_pfn = round_down(max_low_pfn - kva_pages, PTRS_PER_PTE);
+ do {
+ kva_start_pfn = find_e820_area(kva_target_pfn<<PAGE_SHIFT,
+ max_low_pfn<<PAGE_SHIFT,
+ kva_pages<<PAGE_SHIFT,
+ PTRS_PER_PTE<<PAGE_SHIFT) >> PAGE_SHIFT;
+ kva_target_pfn -= PTRS_PER_PTE;
+ } while (kva_start_pfn == -1UL && kva_target_pfn > min_low_pfn);
-#ifdef CONFIG_BLK_DEV_INITRD
- /* Numa kva area is below the initrd */
- if (initrd_start)
- kva_start_pfn = PFN_DOWN(initrd_start - PAGE_OFFSET)
- - kva_pages;
-#endif
+ if (kva_start_pfn == -1UL)
+ panic("Can not get kva space\n");
- /*
- * We waste pages past at the end of the KVA for no good reason other
- * than how it is located. This is bad.
- */
- wasted_pages = kva_start_pfn & (PTRS_PER_PTE-1);
- kva_start_pfn -= wasted_pages;
- kva_pages += wasted_pages;
-
- system_max_low_pfn = max_low_pfn = find_max_low_pfn();
printk("kva_start_pfn ~ %ld find_max_low_pfn() ~ %ld\n",
kva_start_pfn, max_low_pfn);
printk("max_pfn = %ld\n", max_pfn);
+
+ /* avoid clash with initrd */
+ reserve_early(kva_start_pfn<<PAGE_SHIFT,
+ (kva_start_pfn + kva_pages)<<PAGE_SHIFT,
+ "KVA PG");
#ifdef CONFIG_HIGHMEM
highstart_pfn = highend_pfn = max_pfn;
if (max_pfn > system_max_low_pfn)
@@ -387,16 +388,8 @@ unsigned long __init setup_memory(void)
return max_low_pfn;
}
-void __init numa_kva_reserve(void)
-{
- if (kva_pages)
- reserve_bootmem(PFN_PHYS(kva_start_pfn), PFN_PHYS(kva_pages),
- BOOTMEM_DEFAULT);
-}
-
void __init zone_sizes_init(void)
{
- int nid;
unsigned long max_zone_pfns[MAX_NR_ZONES];
memset(max_zone_pfns, 0, sizeof(max_zone_pfns));
max_zone_pfns[ZONE_DMA] =
@@ -406,15 +399,6 @@ void __init zone_sizes_init(void)
max_zone_pfns[ZONE_HIGHMEM] = highend_pfn;
#endif
- /* If SRAT has not registered memory, register it now */
- if (find_max_pfn_with_active_regions() == 0) {
- for_each_online_node(nid) {
- if (node_has_online_mem(nid))
- add_active_range(nid, node_start_pfn[nid],
- node_end_pfn[nid]);
- }
- }
-
free_area_init_nodes(max_zone_pfns);
return;
}
@@ -476,3 +460,21 @@ int memory_add_physaddr_to_nid(u64 addr)
EXPORT_SYMBOL_GPL(memory_add_physaddr_to_nid);
#endif
+
+#if defined(CONFIG_ACPI_NUMA) && !defined(CONFIG_HAVE_ARCH_PARSE_SRAT)
+/*
+ * Dummy on 32-bit, for now:
+ */
+void __init acpi_numa_slit_init(struct acpi_table_slit *slit)
+{
+}
+
+void __init
+acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa)
+{
+}
+
+void __init acpi_numa_arch_fixup(void)
+{
+}
+#endif
diff --git a/arch/x86/mm/dump_pagetables.c b/arch/x86/mm/dump_pagetables.c
index 2c24bea92c66..0bb0caed8971 100644
--- a/arch/x86/mm/dump_pagetables.c
+++ b/arch/x86/mm/dump_pagetables.c
@@ -42,7 +42,7 @@ static struct addr_marker address_markers[] = {
{ 0, "User Space" },
#ifdef CONFIG_X86_64
{ 0x8000000000000000UL, "Kernel Space" },
- { 0xffff810000000000UL, "Low Kernel Mapping" },
+ { PAGE_OFFSET, "Low Kernel Mapping" },
{ VMALLOC_START, "vmalloc() Area" },
{ VMEMMAP_START, "Vmemmap" },
{ __START_KERNEL_map, "High Kernel Mapping" },
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index fd7e1798c75a..e0907882a2d4 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -10,6 +10,7 @@
#include <linux/string.h>
#include <linux/types.h>
#include <linux/ptrace.h>
+#include <linux/mmiotrace.h>
#include <linux/mman.h>
#include <linux/mm.h>
#include <linux/smp.h>
@@ -25,6 +26,7 @@
#include <linux/kprobes.h>
#include <linux/uaccess.h>
#include <linux/kdebug.h>
+#include <linux/magic.h>
#include <asm/system.h>
#include <asm/desc.h>
@@ -49,6 +51,16 @@
#define PF_RSVD (1<<3)
#define PF_INSTR (1<<4)
+static inline int kmmio_fault(struct pt_regs *regs, unsigned long addr)
+{
+#ifdef CONFIG_MMIOTRACE_HOOKS
+ if (unlikely(is_kmmio_active()))
+ if (kmmio_handler(regs, addr) == 1)
+ return -1;
+#endif
+ return 0;
+}
+
static inline int notify_page_fault(struct pt_regs *regs)
{
#ifdef CONFIG_KPROBES
@@ -497,6 +509,11 @@ static int vmalloc_fault(unsigned long address)
unsigned long pgd_paddr;
pmd_t *pmd_k;
pte_t *pte_k;
+
+ /* Make sure we are in vmalloc area */
+ if (!(address >= VMALLOC_START && address < VMALLOC_END))
+ return -1;
+
/*
* Synchronize this task's top level page-table
* with the 'reference' page table.
@@ -581,6 +598,8 @@ void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code)
unsigned long address;
int write, si_code;
int fault;
+ unsigned long *stackend;
+
#ifdef CONFIG_X86_64
unsigned long flags;
#endif
@@ -601,6 +620,8 @@ void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code)
if (notify_page_fault(regs))
return;
+ if (unlikely(kmmio_fault(regs, address)))
+ return;
/*
* We fault-in kernel-space virtual memory on-demand. The
@@ -850,6 +871,10 @@ no_context:
show_fault_oops(regs, error_code, address);
+ stackend = end_of_stack(tsk);
+ if (*stackend != STACK_END_MAGIC)
+ printk(KERN_ALERT "Thread overran stack, or stack corrupted\n");
+
tsk->thread.cr2 = address;
tsk->thread.trap_no = 14;
tsk->thread.error_code = error_code;
diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c
index ec30d10154b6..4f308fadc568 100644
--- a/arch/x86/mm/init_32.c
+++ b/arch/x86/mm/init_32.c
@@ -162,6 +162,7 @@ static void __init kernel_physical_mapping_init(pgd_t *pgd_base)
pgd_t *pgd;
pmd_t *pmd;
pte_t *pte;
+ unsigned pages_2m = 0, pages_4k = 0;
pgd_idx = pgd_index(PAGE_OFFSET);
pgd = pgd_base + pgd_idx;
@@ -197,6 +198,7 @@ static void __init kernel_physical_mapping_init(pgd_t *pgd_base)
is_kernel_text(addr2))
prot = PAGE_KERNEL_LARGE_EXEC;
+ pages_2m++;
set_pmd(pmd, pfn_pmd(pfn, prot));
pfn += PTRS_PER_PTE;
@@ -213,11 +215,14 @@ static void __init kernel_physical_mapping_init(pgd_t *pgd_base)
if (is_kernel_text(addr))
prot = PAGE_KERNEL_EXEC;
+ pages_4k++;
set_pte(pte, pfn_pte(pfn, prot));
}
max_pfn_mapped = pfn;
}
}
+ update_page_count(PG_LEVEL_2M, pages_2m);
+ update_page_count(PG_LEVEL_4K, pages_4k);
}
static inline int page_kills_ppro(unsigned long pagenr)
@@ -289,7 +294,8 @@ static void __init permanent_kmaps_init(pgd_t *pgd_base)
void __init add_one_highpage_init(struct page *page, int pfn, int bad_ppro)
{
- if (page_is_ram(pfn) && !(bad_ppro && page_kills_ppro(pfn))) {
+ if (page_is_ram(pfn) && !(bad_ppro && page_kills_ppro(pfn)) &&
+ !page_is_reserved_early(pfn)) {
ClearPageReserved(page);
init_page_count(page);
__free_page(page);
@@ -571,17 +577,6 @@ void __init mem_init(void)
#endif
bad_ppro = ppro_with_ram_bug();
-#ifdef CONFIG_HIGHMEM
- /* check that fixmap and pkmap do not overlap */
- if (PKMAP_BASE + LAST_PKMAP*PAGE_SIZE >= FIXADDR_START) {
- printk(KERN_ERR
- "fixmap and kmap areas overlap - this will crash\n");
- printk(KERN_ERR "pkstart: %lxh pkend: %lxh fixstart %lxh\n",
- PKMAP_BASE, PKMAP_BASE + LAST_PKMAP*PAGE_SIZE,
- FIXADDR_START);
- BUG();
- }
-#endif
/* this will put all low memory onto the freelists */
totalram_pages += free_all_bootmem();
@@ -614,7 +609,6 @@ void __init mem_init(void)
(unsigned long) (totalhigh_pages << (PAGE_SHIFT-10))
);
-#if 1 /* double-sanity-check paranoia */
printk(KERN_INFO "virtual kernel memory layout:\n"
" fixmap : 0x%08lx - 0x%08lx (%4ld kB)\n"
#ifdef CONFIG_HIGHMEM
@@ -655,7 +649,6 @@ void __init mem_init(void)
#endif
BUG_ON(VMALLOC_START > VMALLOC_END);
BUG_ON((unsigned long)high_memory > VMALLOC_START);
-#endif /* double-sanity-check paranoia */
if (boot_cpu_data.wp_works_ok < 0)
test_wp_bit();
@@ -710,6 +703,8 @@ void mark_rodata_ro(void)
unsigned long start = PFN_ALIGN(_text);
unsigned long size = PFN_ALIGN(_etext) - start;
+#ifndef CONFIG_DYNAMIC_FTRACE
+ /* Dynamic tracing modifies the kernel text section */
set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT);
printk(KERN_INFO "Write protecting the kernel text: %luk\n",
size >> 10);
@@ -722,6 +717,8 @@ void mark_rodata_ro(void)
printk(KERN_INFO "Testing CPA: write protecting again\n");
set_pages_ro(virt_to_page(start), size>>PAGE_SHIFT);
#endif
+#endif /* CONFIG_DYNAMIC_FTRACE */
+
start += size;
size = (unsigned long)__end_rodata - start;
set_pages_ro(virt_to_page(start), size >> PAGE_SHIFT);
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index 156e6d7b0e32..c8d8f1a3bffd 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -18,6 +18,7 @@
#include <linux/swap.h>
#include <linux/smp.h>
#include <linux/init.h>
+#include <linux/initrd.h>
#include <linux/pagemap.h>
#include <linux/bootmem.h>
#include <linux/proc_fs.h>
@@ -312,6 +313,8 @@ __meminit void early_iounmap(void *addr, unsigned long size)
static unsigned long __meminit
phys_pmd_init(pmd_t *pmd_page, unsigned long address, unsigned long end)
{
+ unsigned long pages = 0;
+
int i = pmd_index(address);
for (; i < PTRS_PER_PMD; i++, address += PMD_SIZE) {
@@ -328,9 +331,11 @@ phys_pmd_init(pmd_t *pmd_page, unsigned long address, unsigned long end)
if (pmd_val(*pmd))
continue;
+ pages++;
set_pte((pte_t *)pmd,
pfn_pte(address >> PAGE_SHIFT, PAGE_KERNEL_LARGE));
}
+ update_page_count(PG_LEVEL_2M, pages);
return address;
}
@@ -350,6 +355,7 @@ phys_pmd_update(pud_t *pud, unsigned long address, unsigned long end)
static unsigned long __meminit
phys_pud_init(pud_t *pud_page, unsigned long addr, unsigned long end)
{
+ unsigned long pages = 0;
unsigned long last_map_addr = end;
int i = pud_index(addr);
@@ -374,6 +380,7 @@ phys_pud_init(pud_t *pud_page, unsigned long addr, unsigned long end)
}
if (direct_gbpages) {
+ pages++;
set_pte((pte_t *)pud,
pfn_pte(addr >> PAGE_SHIFT, PAGE_KERNEL_LARGE));
last_map_addr = (addr & PUD_MASK) + PUD_SIZE;
@@ -390,6 +397,7 @@ phys_pud_init(pud_t *pud_page, unsigned long addr, unsigned long end)
unmap_low_page(pmd);
}
__flush_tlb_all();
+ update_page_count(PG_LEVEL_1G, pages);
return last_map_addr >> PAGE_SHIFT;
}
@@ -431,7 +439,7 @@ static void __init init_gbpages(void)
direct_gbpages = 0;
}
-#ifdef CONFIG_MEMTEST_BOOTPARAM
+#ifdef CONFIG_MEMTEST
static void __init memtest(unsigned long start_phys, unsigned long size,
unsigned pattern)
@@ -493,7 +501,8 @@ static void __init memtest(unsigned long start_phys, unsigned long size,
}
-static int memtest_pattern __initdata = CONFIG_MEMTEST_BOOTPARAM_VALUE;
+/* default is disabled */
+static int memtest_pattern __initdata;
static int __init parse_memtest(char *arg)
{
@@ -766,6 +775,13 @@ EXPORT_SYMBOL_GPL(rodata_test_data);
void mark_rodata_ro(void)
{
unsigned long start = PFN_ALIGN(_stext), end = PFN_ALIGN(__end_rodata);
+ unsigned long rodata_start =
+ ((unsigned long)__start_rodata + PAGE_SIZE - 1) & PAGE_MASK;
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+ /* Dynamic tracing modifies the kernel text section */
+ start = rodata_start;
+#endif
printk(KERN_INFO "Write protecting the kernel read-only data: %luk\n",
(end - start) >> 10);
@@ -775,8 +791,7 @@ void mark_rodata_ro(void)
* The rodata section (but not the kernel text!) should also be
* not-executable.
*/
- start = ((unsigned long)__start_rodata + PAGE_SIZE - 1) & PAGE_MASK;
- set_memory_nx(start, (end - start) >> PAGE_SHIFT);
+ set_memory_nx(rodata_start, (end - rodata_start) >> PAGE_SHIFT);
rodata_test();
@@ -798,12 +813,13 @@ void free_initrd_mem(unsigned long start, unsigned long end)
}
#endif
-void __init reserve_bootmem_generic(unsigned long phys, unsigned len)
+int __init reserve_bootmem_generic(unsigned long phys, unsigned len, int flags)
{
#ifdef CONFIG_NUMA
int nid, next_nid;
#endif
unsigned long pfn = phys >> PAGE_SHIFT;
+ int ret;
if (pfn >= end_pfn) {
/*
@@ -811,11 +827,11 @@ void __init reserve_bootmem_generic(unsigned long phys, unsigned len)
* firmware tables:
*/
if (pfn < max_pfn_mapped)
- return;
+ return -EFAULT;
printk(KERN_ERR "reserve_bootmem: illegal reserve %lx %u\n",
phys, len);
- return;
+ return -EFAULT;
}
/* Should check here against the e820 map to avoid double free */
@@ -823,9 +839,13 @@ void __init reserve_bootmem_generic(unsigned long phys, unsigned len)
nid = phys_to_nid(phys);
next_nid = phys_to_nid(phys + len - 1);
if (nid == next_nid)
- reserve_bootmem_node(NODE_DATA(nid), phys, len, BOOTMEM_DEFAULT);
+ ret = reserve_bootmem_node(NODE_DATA(nid), phys, len, flags);
else
- reserve_bootmem(phys, len, BOOTMEM_DEFAULT);
+ ret = reserve_bootmem(phys, len, flags);
+
+ if (ret != 0)
+ return ret;
+
#else
reserve_bootmem(phys, len, BOOTMEM_DEFAULT);
#endif
@@ -834,6 +854,8 @@ void __init reserve_bootmem_generic(unsigned long phys, unsigned len)
dma_reserve += len / PAGE_SIZE;
set_dma_reserve(dma_reserve);
}
+
+ return 0;
}
int kern_addr_valid(unsigned long addr)
diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c
index 2b2bb3f9b683..fc33b7ace8a3 100644
--- a/arch/x86/mm/ioremap.c
+++ b/arch/x86/mm/ioremap.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
+#include <linux/mmiotrace.h>
#include <asm/cacheflush.h>
#include <asm/e820.h>
@@ -122,10 +123,13 @@ static void __iomem *__ioremap_caller(resource_size_t phys_addr,
{
unsigned long pfn, offset, vaddr;
resource_size_t last_addr;
+ const resource_size_t unaligned_phys_addr = phys_addr;
+ const unsigned long unaligned_size = size;
struct vm_struct *area;
unsigned long new_prot_val;
pgprot_t prot;
int retval;
+ void __iomem *ret_addr;
/* Don't allow wraparound or zero size */
last_addr = phys_addr + size - 1;
@@ -233,7 +237,10 @@ static void __iomem *__ioremap_caller(resource_size_t phys_addr,
return NULL;
}
- return (void __iomem *) (vaddr + offset);
+ ret_addr = (void __iomem *) (vaddr + offset);
+ mmiotrace_ioremap(unaligned_phys_addr, unaligned_size, ret_addr);
+
+ return ret_addr;
}
/**
@@ -318,13 +325,15 @@ void iounmap(volatile void __iomem *addr)
* vm_area and by simply returning an address into the kernel mapping
* of ISA space. So handle that here.
*/
- if (addr >= phys_to_virt(ISA_START_ADDRESS) &&
- addr < phys_to_virt(ISA_END_ADDRESS))
+ if ((void __force *)addr >= phys_to_virt(ISA_START_ADDRESS) &&
+ (void __force *)addr < phys_to_virt(ISA_END_ADDRESS))
return;
addr = (volatile void __iomem *)
(PAGE_MASK & (unsigned long __force)addr);
+ mmiotrace_iounmap(addr);
+
/* Use the vm area unlocked, assuming the caller
ensures there isn't another iounmap for the same address
in parallel. Reuse of the virtual address is prevented by
@@ -332,7 +341,7 @@ void iounmap(volatile void __iomem *addr)
cpa takes care of the direct mappings. */
read_lock(&vmlist_lock);
for (p = vmlist; p; p = p->next) {
- if (p->addr == addr)
+ if (p->addr == (void __force *)addr)
break;
}
read_unlock(&vmlist_lock);
@@ -346,7 +355,7 @@ void iounmap(volatile void __iomem *addr)
free_memtype(p->phys_addr, p->phys_addr + get_vm_area_size(p));
/* Finally remove it */
- o = remove_vm_area((void *)addr);
+ o = remove_vm_area((void __force *)addr);
BUG_ON(p != o || o == NULL);
kfree(p);
}
@@ -365,7 +374,7 @@ void *xlate_dev_mem_ptr(unsigned long phys)
if (page_is_ram(start >> PAGE_SHIFT))
return __va(phys);
- addr = (void *)ioremap(start, PAGE_SIZE);
+ addr = (void __force *)ioremap(start, PAGE_SIZE);
if (addr)
addr = (void *)((unsigned long)addr | (phys & ~PAGE_MASK));
diff --git a/arch/x86/mm/k8topology_64.c b/arch/x86/mm/k8topology_64.c
index 1f476e477844..0ea66b532c35 100644
--- a/arch/x86/mm/k8topology_64.c
+++ b/arch/x86/mm/k8topology_64.c
@@ -22,6 +22,7 @@
#include <asm/numa.h>
#include <asm/mpspec.h>
#include <asm/apic.h>
+#include <asm/k8.h>
static __init int find_northbridge(void)
{
@@ -73,17 +74,12 @@ static __init void early_get_boot_cpu_id(void)
int __init k8_scan_nodes(unsigned long start, unsigned long end)
{
+ unsigned numnodes, cores, bits, apicid_base;
unsigned long prevbase;
struct bootnode nodes[8];
- int nodeid, i, nb;
unsigned char nodeids[8];
- int found = 0;
- u32 reg;
- unsigned numnodes;
- unsigned cores;
- unsigned bits;
- int j;
- unsigned apicid_base;
+ int i, j, nb, found = 0;
+ u32 nodeid, reg;
if (!early_pci_allowed())
return -1;
@@ -105,7 +101,6 @@ int __init k8_scan_nodes(unsigned long start, unsigned long end)
prevbase = 0;
for (i = 0; i < 8; i++) {
unsigned long base, limit;
- u32 nodeid;
base = read_pci_config(0, nb, 1, 0x40 + i*8);
limit = read_pci_config(0, nb, 1, 0x44 + i*8);
diff --git a/arch/x86/mm/kmmio.c b/arch/x86/mm/kmmio.c
new file mode 100644
index 000000000000..7bfdad7589fa
--- /dev/null
+++ b/arch/x86/mm/kmmio.c
@@ -0,0 +1,510 @@
+/* Support for MMIO probes.
+ * Benfit many code from kprobes
+ * (C) 2002 Louis Zhuang <louis.zhuang@intel.com>.
+ * 2007 Alexander Eichner
+ * 2008 Pekka Paalanen <pq@iki.fi>
+ */
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/hash.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/uaccess.h>
+#include <linux/ptrace.h>
+#include <linux/preempt.h>
+#include <linux/percpu.h>
+#include <linux/kdebug.h>
+#include <linux/mutex.h>
+#include <linux/io.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+#include <linux/errno.h>
+#include <asm/debugreg.h>
+#include <linux/mmiotrace.h>
+#include <linux/rculist.h>
+
+#define KMMIO_PAGE_HASH_BITS 4
+#define KMMIO_PAGE_TABLE_SIZE (1 << KMMIO_PAGE_HASH_BITS)
+
+struct kmmio_fault_page {
+ struct list_head list;
+ struct kmmio_fault_page *release_next;
+ unsigned long page; /* location of the fault page */
+
+ /*
+ * Number of times this page has been registered as a part
+ * of a probe. If zero, page is disarmed and this may be freed.
+ * Used only by writers (RCU).
+ */
+ int count;
+};
+
+struct kmmio_delayed_release {
+ struct rcu_head rcu;
+ struct kmmio_fault_page *release_list;
+};
+
+struct kmmio_context {
+ struct kmmio_fault_page *fpage;
+ struct kmmio_probe *probe;
+ unsigned long saved_flags;
+ unsigned long addr;
+ int active;
+};
+
+static DEFINE_SPINLOCK(kmmio_lock);
+
+/* Protected by kmmio_lock */
+unsigned int kmmio_count;
+
+/* Read-protected by RCU, write-protected by kmmio_lock. */
+static struct list_head kmmio_page_table[KMMIO_PAGE_TABLE_SIZE];
+static LIST_HEAD(kmmio_probes);
+
+static struct list_head *kmmio_page_list(unsigned long page)
+{
+ return &kmmio_page_table[hash_long(page, KMMIO_PAGE_HASH_BITS)];
+}
+
+/* Accessed per-cpu */
+static DEFINE_PER_CPU(struct kmmio_context, kmmio_ctx);
+
+/*
+ * this is basically a dynamic stabbing problem:
+ * Could use the existing prio tree code or
+ * Possible better implementations:
+ * The Interval Skip List: A Data Structure for Finding All Intervals That
+ * Overlap a Point (might be simple)
+ * Space Efficient Dynamic Stabbing with Fast Queries - Mikkel Thorup
+ */
+/* Get the kmmio at this addr (if any). You must be holding RCU read lock. */
+static struct kmmio_probe *get_kmmio_probe(unsigned long addr)
+{
+ struct kmmio_probe *p;
+ list_for_each_entry_rcu(p, &kmmio_probes, list) {
+ if (addr >= p->addr && addr <= (p->addr + p->len))
+ return p;
+ }
+ return NULL;
+}
+
+/* You must be holding RCU read lock. */
+static struct kmmio_fault_page *get_kmmio_fault_page(unsigned long page)
+{
+ struct list_head *head;
+ struct kmmio_fault_page *p;
+
+ page &= PAGE_MASK;
+ head = kmmio_page_list(page);
+ list_for_each_entry_rcu(p, head, list) {
+ if (p->page == page)
+ return p;
+ }
+ return NULL;
+}
+
+static void set_page_present(unsigned long addr, bool present,
+ unsigned int *pglevel)
+{
+ pteval_t pteval;
+ pmdval_t pmdval;
+ unsigned int level;
+ pmd_t *pmd;
+ pte_t *pte = lookup_address(addr, &level);
+
+ if (!pte) {
+ pr_err("kmmio: no pte for page 0x%08lx\n", addr);
+ return;
+ }
+
+ if (pglevel)
+ *pglevel = level;
+
+ switch (level) {
+ case PG_LEVEL_2M:
+ pmd = (pmd_t *)pte;
+ pmdval = pmd_val(*pmd) & ~_PAGE_PRESENT;
+ if (present)
+ pmdval |= _PAGE_PRESENT;
+ set_pmd(pmd, __pmd(pmdval));
+ break;
+
+ case PG_LEVEL_4K:
+ pteval = pte_val(*pte) & ~_PAGE_PRESENT;
+ if (present)
+ pteval |= _PAGE_PRESENT;
+ set_pte_atomic(pte, __pte(pteval));
+ break;
+
+ default:
+ pr_err("kmmio: unexpected page level 0x%x.\n", level);
+ return;
+ }
+
+ __flush_tlb_one(addr);
+}
+
+/** Mark the given page as not present. Access to it will trigger a fault. */
+static void arm_kmmio_fault_page(unsigned long page, unsigned int *pglevel)
+{
+ set_page_present(page & PAGE_MASK, false, pglevel);
+}
+
+/** Mark the given page as present. */
+static void disarm_kmmio_fault_page(unsigned long page, unsigned int *pglevel)
+{
+ set_page_present(page & PAGE_MASK, true, pglevel);
+}
+
+/*
+ * This is being called from do_page_fault().
+ *
+ * We may be in an interrupt or a critical section. Also prefecthing may
+ * trigger a page fault. We may be in the middle of process switch.
+ * We cannot take any locks, because we could be executing especially
+ * within a kmmio critical section.
+ *
+ * Local interrupts are disabled, so preemption cannot happen.
+ * Do not enable interrupts, do not sleep, and watch out for other CPUs.
+ */
+/*
+ * Interrupts are disabled on entry as trap3 is an interrupt gate
+ * and they remain disabled thorough out this function.
+ */
+int kmmio_handler(struct pt_regs *regs, unsigned long addr)
+{
+ struct kmmio_context *ctx;
+ struct kmmio_fault_page *faultpage;
+ int ret = 0; /* default to fault not handled */
+
+ /*
+ * Preemption is now disabled to prevent process switch during
+ * single stepping. We can only handle one active kmmio trace
+ * per cpu, so ensure that we finish it before something else
+ * gets to run. We also hold the RCU read lock over single
+ * stepping to avoid looking up the probe and kmmio_fault_page
+ * again.
+ */
+ preempt_disable();
+ rcu_read_lock();
+
+ faultpage = get_kmmio_fault_page(addr);
+ if (!faultpage) {
+ /*
+ * Either this page fault is not caused by kmmio, or
+ * another CPU just pulled the kmmio probe from under
+ * our feet. The latter case should not be possible.
+ */
+ goto no_kmmio;
+ }
+
+ ctx = &get_cpu_var(kmmio_ctx);
+ if (ctx->active) {
+ disarm_kmmio_fault_page(faultpage->page, NULL);
+ if (addr == ctx->addr) {
+ /*
+ * On SMP we sometimes get recursive probe hits on the
+ * same address. Context is already saved, fall out.
+ */
+ pr_debug("kmmio: duplicate probe hit on CPU %d, for "
+ "address 0x%08lx.\n",
+ smp_processor_id(), addr);
+ ret = 1;
+ goto no_kmmio_ctx;
+ }
+ /*
+ * Prevent overwriting already in-flight context.
+ * This should not happen, let's hope disarming at least
+ * prevents a panic.
+ */
+ pr_emerg("kmmio: recursive probe hit on CPU %d, "
+ "for address 0x%08lx. Ignoring.\n",
+ smp_processor_id(), addr);
+ pr_emerg("kmmio: previous hit was at 0x%08lx.\n",
+ ctx->addr);
+ goto no_kmmio_ctx;
+ }
+ ctx->active++;
+
+ ctx->fpage = faultpage;
+ ctx->probe = get_kmmio_probe(addr);
+ ctx->saved_flags = (regs->flags & (X86_EFLAGS_TF | X86_EFLAGS_IF));
+ ctx->addr = addr;
+
+ if (ctx->probe && ctx->probe->pre_handler)
+ ctx->probe->pre_handler(ctx->probe, regs, addr);
+
+ /*
+ * Enable single-stepping and disable interrupts for the faulting
+ * context. Local interrupts must not get enabled during stepping.
+ */
+ regs->flags |= X86_EFLAGS_TF;
+ regs->flags &= ~X86_EFLAGS_IF;
+
+ /* Now we set present bit in PTE and single step. */
+ disarm_kmmio_fault_page(ctx->fpage->page, NULL);
+
+ /*
+ * If another cpu accesses the same page while we are stepping,
+ * the access will not be caught. It will simply succeed and the
+ * only downside is we lose the event. If this becomes a problem,
+ * the user should drop to single cpu before tracing.
+ */
+
+ put_cpu_var(kmmio_ctx);
+ return 1; /* fault handled */
+
+no_kmmio_ctx:
+ put_cpu_var(kmmio_ctx);
+no_kmmio:
+ rcu_read_unlock();
+ preempt_enable_no_resched();
+ return ret;
+}
+
+/*
+ * Interrupts are disabled on entry as trap1 is an interrupt gate
+ * and they remain disabled thorough out this function.
+ * This must always get called as the pair to kmmio_handler().
+ */
+static int post_kmmio_handler(unsigned long condition, struct pt_regs *regs)
+{
+ int ret = 0;
+ struct kmmio_context *ctx = &get_cpu_var(kmmio_ctx);
+
+ if (!ctx->active) {
+ pr_debug("kmmio: spurious debug trap on CPU %d.\n",
+ smp_processor_id());
+ goto out;
+ }
+
+ if (ctx->probe && ctx->probe->post_handler)
+ ctx->probe->post_handler(ctx->probe, condition, regs);
+
+ arm_kmmio_fault_page(ctx->fpage->page, NULL);
+
+ regs->flags &= ~X86_EFLAGS_TF;
+ regs->flags |= ctx->saved_flags;
+
+ /* These were acquired in kmmio_handler(). */
+ ctx->active--;
+ BUG_ON(ctx->active);
+ rcu_read_unlock();
+ preempt_enable_no_resched();
+
+ /*
+ * if somebody else is singlestepping across a probe point, flags
+ * will have TF set, in which case, continue the remaining processing
+ * of do_debug, as if this is not a probe hit.
+ */
+ if (!(regs->flags & X86_EFLAGS_TF))
+ ret = 1;
+out:
+ put_cpu_var(kmmio_ctx);
+ return ret;
+}
+
+/* You must be holding kmmio_lock. */
+static int add_kmmio_fault_page(unsigned long page)
+{
+ struct kmmio_fault_page *f;
+
+ page &= PAGE_MASK;
+ f = get_kmmio_fault_page(page);
+ if (f) {
+ if (!f->count)
+ arm_kmmio_fault_page(f->page, NULL);
+ f->count++;
+ return 0;
+ }
+
+ f = kmalloc(sizeof(*f), GFP_ATOMIC);
+ if (!f)
+ return -1;
+
+ f->count = 1;
+ f->page = page;
+ list_add_rcu(&f->list, kmmio_page_list(f->page));
+
+ arm_kmmio_fault_page(f->page, NULL);
+
+ return 0;
+}
+
+/* You must be holding kmmio_lock. */
+static void release_kmmio_fault_page(unsigned long page,
+ struct kmmio_fault_page **release_list)
+{
+ struct kmmio_fault_page *f;
+
+ page &= PAGE_MASK;
+ f = get_kmmio_fault_page(page);
+ if (!f)
+ return;
+
+ f->count--;
+ BUG_ON(f->count < 0);
+ if (!f->count) {
+ disarm_kmmio_fault_page(f->page, NULL);
+ f->release_next = *release_list;
+ *release_list = f;
+ }
+}
+
+/*
+ * With page-unaligned ioremaps, one or two armed pages may contain
+ * addresses from outside the intended mapping. Events for these addresses
+ * are currently silently dropped. The events may result only from programming
+ * mistakes by accessing addresses before the beginning or past the end of a
+ * mapping.
+ */
+int register_kmmio_probe(struct kmmio_probe *p)
+{
+ unsigned long flags;
+ int ret = 0;
+ unsigned long size = 0;
+ const unsigned long size_lim = p->len + (p->addr & ~PAGE_MASK);
+
+ spin_lock_irqsave(&kmmio_lock, flags);
+ if (get_kmmio_probe(p->addr)) {
+ ret = -EEXIST;
+ goto out;
+ }
+ kmmio_count++;
+ list_add_rcu(&p->list, &kmmio_probes);
+ while (size < size_lim) {
+ if (add_kmmio_fault_page(p->addr + size))
+ pr_err("kmmio: Unable to set page fault.\n");
+ size += PAGE_SIZE;
+ }
+out:
+ spin_unlock_irqrestore(&kmmio_lock, flags);
+ /*
+ * XXX: What should I do here?
+ * Here was a call to global_flush_tlb(), but it does not exist
+ * anymore. It seems it's not needed after all.
+ */
+ return ret;
+}
+EXPORT_SYMBOL(register_kmmio_probe);
+
+static void rcu_free_kmmio_fault_pages(struct rcu_head *head)
+{
+ struct kmmio_delayed_release *dr = container_of(
+ head,
+ struct kmmio_delayed_release,
+ rcu);
+ struct kmmio_fault_page *p = dr->release_list;
+ while (p) {
+ struct kmmio_fault_page *next = p->release_next;
+ BUG_ON(p->count);
+ kfree(p);
+ p = next;
+ }
+ kfree(dr);
+}
+
+static void remove_kmmio_fault_pages(struct rcu_head *head)
+{
+ struct kmmio_delayed_release *dr = container_of(
+ head,
+ struct kmmio_delayed_release,
+ rcu);
+ struct kmmio_fault_page *p = dr->release_list;
+ struct kmmio_fault_page **prevp = &dr->release_list;
+ unsigned long flags;
+ spin_lock_irqsave(&kmmio_lock, flags);
+ while (p) {
+ if (!p->count)
+ list_del_rcu(&p->list);
+ else
+ *prevp = p->release_next;
+ prevp = &p->release_next;
+ p = p->release_next;
+ }
+ spin_unlock_irqrestore(&kmmio_lock, flags);
+ /* This is the real RCU destroy call. */
+ call_rcu(&dr->rcu, rcu_free_kmmio_fault_pages);
+}
+
+/*
+ * Remove a kmmio probe. You have to synchronize_rcu() before you can be
+ * sure that the callbacks will not be called anymore. Only after that
+ * you may actually release your struct kmmio_probe.
+ *
+ * Unregistering a kmmio fault page has three steps:
+ * 1. release_kmmio_fault_page()
+ * Disarm the page, wait a grace period to let all faults finish.
+ * 2. remove_kmmio_fault_pages()
+ * Remove the pages from kmmio_page_table.
+ * 3. rcu_free_kmmio_fault_pages()
+ * Actally free the kmmio_fault_page structs as with RCU.
+ */
+void unregister_kmmio_probe(struct kmmio_probe *p)
+{
+ unsigned long flags;
+ unsigned long size = 0;
+ const unsigned long size_lim = p->len + (p->addr & ~PAGE_MASK);
+ struct kmmio_fault_page *release_list = NULL;
+ struct kmmio_delayed_release *drelease;
+
+ spin_lock_irqsave(&kmmio_lock, flags);
+ while (size < size_lim) {
+ release_kmmio_fault_page(p->addr + size, &release_list);
+ size += PAGE_SIZE;
+ }
+ list_del_rcu(&p->list);
+ kmmio_count--;
+ spin_unlock_irqrestore(&kmmio_lock, flags);
+
+ drelease = kmalloc(sizeof(*drelease), GFP_ATOMIC);
+ if (!drelease) {
+ pr_crit("kmmio: leaking kmmio_fault_page objects.\n");
+ return;
+ }
+ drelease->release_list = release_list;
+
+ /*
+ * This is not really RCU here. We have just disarmed a set of
+ * pages so that they cannot trigger page faults anymore. However,
+ * we cannot remove the pages from kmmio_page_table,
+ * because a probe hit might be in flight on another CPU. The
+ * pages are collected into a list, and they will be removed from
+ * kmmio_page_table when it is certain that no probe hit related to
+ * these pages can be in flight. RCU grace period sounds like a
+ * good choice.
+ *
+ * If we removed the pages too early, kmmio page fault handler might
+ * not find the respective kmmio_fault_page and determine it's not
+ * a kmmio fault, when it actually is. This would lead to madness.
+ */
+ call_rcu(&drelease->rcu, remove_kmmio_fault_pages);
+}
+EXPORT_SYMBOL(unregister_kmmio_probe);
+
+static int kmmio_die_notifier(struct notifier_block *nb, unsigned long val,
+ void *args)
+{
+ struct die_args *arg = args;
+
+ if (val == DIE_DEBUG && (arg->err & DR_STEP))
+ if (post_kmmio_handler(arg->err, arg->regs) == 1)
+ return NOTIFY_STOP;
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block nb_die = {
+ .notifier_call = kmmio_die_notifier
+};
+
+static int __init init_kmmio(void)
+{
+ int i;
+ for (i = 0; i < KMMIO_PAGE_TABLE_SIZE; i++)
+ INIT_LIST_HEAD(&kmmio_page_table[i]);
+ return register_die_notifier(&nb_die);
+}
+fs_initcall(init_kmmio); /* should be before device_initcall() */
diff --git a/arch/x86/mm/mmio-mod.c b/arch/x86/mm/mmio-mod.c
new file mode 100644
index 000000000000..e7397e108beb
--- /dev/null
+++ b/arch/x86/mm/mmio-mod.c
@@ -0,0 +1,515 @@
+/*
+ * 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.
+ *
+ * Copyright (C) IBM Corporation, 2005
+ * Jeff Muizelaar, 2006, 2007
+ * Pekka Paalanen, 2008 <pq@iki.fi>
+ *
+ * Derived from the read-mod example from relay-examples by Tom Zanussi.
+ */
+#define DEBUG 1
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/version.h>
+#include <linux/kallsyms.h>
+#include <asm/pgtable.h>
+#include <linux/mmiotrace.h>
+#include <asm/e820.h> /* for ISA_START_ADDRESS */
+#include <asm/atomic.h>
+#include <linux/percpu.h>
+#include <linux/cpu.h>
+
+#include "pf_in.h"
+
+#define NAME "mmiotrace: "
+
+struct trap_reason {
+ unsigned long addr;
+ unsigned long ip;
+ enum reason_type type;
+ int active_traces;
+};
+
+struct remap_trace {
+ struct list_head list;
+ struct kmmio_probe probe;
+ resource_size_t phys;
+ unsigned long id;
+};
+
+/* Accessed per-cpu. */
+static DEFINE_PER_CPU(struct trap_reason, pf_reason);
+static DEFINE_PER_CPU(struct mmiotrace_rw, cpu_trace);
+
+#if 0 /* XXX: no way gather this info anymore */
+/* Access to this is not per-cpu. */
+static DEFINE_PER_CPU(atomic_t, dropped);
+#endif
+
+static struct dentry *marker_file;
+
+static DEFINE_MUTEX(mmiotrace_mutex);
+static DEFINE_SPINLOCK(trace_lock);
+static atomic_t mmiotrace_enabled;
+static LIST_HEAD(trace_list); /* struct remap_trace */
+
+/*
+ * Locking in this file:
+ * - mmiotrace_mutex enforces enable/disable_mmiotrace() critical sections.
+ * - mmiotrace_enabled may be modified only when holding mmiotrace_mutex
+ * and trace_lock.
+ * - Routines depending on is_enabled() must take trace_lock.
+ * - trace_list users must hold trace_lock.
+ * - is_enabled() guarantees that mmio_trace_record is allowed.
+ * - pre/post callbacks assume the effect of is_enabled() being true.
+ */
+
+/* module parameters */
+static unsigned long filter_offset;
+static int nommiotrace;
+static int trace_pc;
+
+module_param(filter_offset, ulong, 0);
+module_param(nommiotrace, bool, 0);
+module_param(trace_pc, bool, 0);
+
+MODULE_PARM_DESC(filter_offset, "Start address of traced mappings.");
+MODULE_PARM_DESC(nommiotrace, "Disable actual MMIO tracing.");
+MODULE_PARM_DESC(trace_pc, "Record address of faulting instructions.");
+
+static bool is_enabled(void)
+{
+ return atomic_read(&mmiotrace_enabled);
+}
+
+#if 0 /* XXX: needs rewrite */
+/*
+ * Write callback for the debugfs entry:
+ * Read a marker and write it to the mmio trace log
+ */
+static ssize_t write_marker(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ char *event = NULL;
+ struct mm_io_header *headp;
+ ssize_t len = (count > 65535) ? 65535 : count;
+
+ event = kzalloc(sizeof(*headp) + len, GFP_KERNEL);
+ if (!event)
+ return -ENOMEM;
+
+ headp = (struct mm_io_header *)event;
+ headp->type = MMIO_MAGIC | (MMIO_MARKER << MMIO_OPCODE_SHIFT);
+ headp->data_len = len;
+
+ if (copy_from_user(event + sizeof(*headp), buffer, len)) {
+ kfree(event);
+ return -EFAULT;
+ }
+
+ spin_lock_irq(&trace_lock);
+#if 0 /* XXX: convert this to use tracing */
+ if (is_enabled())
+ relay_write(chan, event, sizeof(*headp) + len);
+ else
+#endif
+ len = -EINVAL;
+ spin_unlock_irq(&trace_lock);
+ kfree(event);
+ return len;
+}
+#endif
+
+static void print_pte(unsigned long address)
+{
+ unsigned int level;
+ pte_t *pte = lookup_address(address, &level);
+
+ if (!pte) {
+ pr_err(NAME "Error in %s: no pte for page 0x%08lx\n",
+ __func__, address);
+ return;
+ }
+
+ if (level == PG_LEVEL_2M) {
+ pr_emerg(NAME "4MB pages are not currently supported: "
+ "0x%08lx\n", address);
+ BUG();
+ }
+ pr_info(NAME "pte for 0x%lx: 0x%llx 0x%llx\n", address,
+ (unsigned long long)pte_val(*pte),
+ (unsigned long long)pte_val(*pte) & _PAGE_PRESENT);
+}
+
+/*
+ * For some reason the pre/post pairs have been called in an
+ * unmatched order. Report and die.
+ */
+static void die_kmmio_nesting_error(struct pt_regs *regs, unsigned long addr)
+{
+ const struct trap_reason *my_reason = &get_cpu_var(pf_reason);
+ pr_emerg(NAME "unexpected fault for address: 0x%08lx, "
+ "last fault for address: 0x%08lx\n",
+ addr, my_reason->addr);
+ print_pte(addr);
+ print_symbol(KERN_EMERG "faulting IP is at %s\n", regs->ip);
+ print_symbol(KERN_EMERG "last faulting IP was at %s\n", my_reason->ip);
+#ifdef __i386__
+ pr_emerg("eax: %08lx ebx: %08lx ecx: %08lx edx: %08lx\n",
+ regs->ax, regs->bx, regs->cx, regs->dx);
+ pr_emerg("esi: %08lx edi: %08lx ebp: %08lx esp: %08lx\n",
+ regs->si, regs->di, regs->bp, regs->sp);
+#else
+ pr_emerg("rax: %016lx rcx: %016lx rdx: %016lx\n",
+ regs->ax, regs->cx, regs->dx);
+ pr_emerg("rsi: %016lx rdi: %016lx rbp: %016lx rsp: %016lx\n",
+ regs->si, regs->di, regs->bp, regs->sp);
+#endif
+ put_cpu_var(pf_reason);
+ BUG();
+}
+
+static void pre(struct kmmio_probe *p, struct pt_regs *regs,
+ unsigned long addr)
+{
+ struct trap_reason *my_reason = &get_cpu_var(pf_reason);
+ struct mmiotrace_rw *my_trace = &get_cpu_var(cpu_trace);
+ const unsigned long instptr = instruction_pointer(regs);
+ const enum reason_type type = get_ins_type(instptr);
+ struct remap_trace *trace = p->private;
+
+ /* it doesn't make sense to have more than one active trace per cpu */
+ if (my_reason->active_traces)
+ die_kmmio_nesting_error(regs, addr);
+ else
+ my_reason->active_traces++;
+
+ my_reason->type = type;
+ my_reason->addr = addr;
+ my_reason->ip = instptr;
+
+ my_trace->phys = addr - trace->probe.addr + trace->phys;
+ my_trace->map_id = trace->id;
+
+ /*
+ * Only record the program counter when requested.
+ * It may taint clean-room reverse engineering.
+ */
+ if (trace_pc)
+ my_trace->pc = instptr;
+ else
+ my_trace->pc = 0;
+
+ /*
+ * XXX: the timestamp recorded will be *after* the tracing has been
+ * done, not at the time we hit the instruction. SMP implications
+ * on event ordering?
+ */
+
+ switch (type) {
+ case REG_READ:
+ my_trace->opcode = MMIO_READ;
+ my_trace->width = get_ins_mem_width(instptr);
+ break;
+ case REG_WRITE:
+ my_trace->opcode = MMIO_WRITE;
+ my_trace->width = get_ins_mem_width(instptr);
+ my_trace->value = get_ins_reg_val(instptr, regs);
+ break;
+ case IMM_WRITE:
+ my_trace->opcode = MMIO_WRITE;
+ my_trace->width = get_ins_mem_width(instptr);
+ my_trace->value = get_ins_imm_val(instptr);
+ break;
+ default:
+ {
+ unsigned char *ip = (unsigned char *)instptr;
+ my_trace->opcode = MMIO_UNKNOWN_OP;
+ my_trace->width = 0;
+ my_trace->value = (*ip) << 16 | *(ip + 1) << 8 |
+ *(ip + 2);
+ }
+ }
+ put_cpu_var(cpu_trace);
+ put_cpu_var(pf_reason);
+}
+
+static void post(struct kmmio_probe *p, unsigned long condition,
+ struct pt_regs *regs)
+{
+ struct trap_reason *my_reason = &get_cpu_var(pf_reason);
+ struct mmiotrace_rw *my_trace = &get_cpu_var(cpu_trace);
+
+ /* this should always return the active_trace count to 0 */
+ my_reason->active_traces--;
+ if (my_reason->active_traces) {
+ pr_emerg(NAME "unexpected post handler");
+ BUG();
+ }
+
+ switch (my_reason->type) {
+ case REG_READ:
+ my_trace->value = get_ins_reg_val(my_reason->ip, regs);
+ break;
+ default:
+ break;
+ }
+
+ mmio_trace_rw(my_trace);
+ put_cpu_var(cpu_trace);
+ put_cpu_var(pf_reason);
+}
+
+static void ioremap_trace_core(resource_size_t offset, unsigned long size,
+ void __iomem *addr)
+{
+ static atomic_t next_id;
+ struct remap_trace *trace = kmalloc(sizeof(*trace), GFP_KERNEL);
+ /* These are page-unaligned. */
+ struct mmiotrace_map map = {
+ .phys = offset,
+ .virt = (unsigned long)addr,
+ .len = size,
+ .opcode = MMIO_PROBE
+ };
+
+ if (!trace) {
+ pr_err(NAME "kmalloc failed in ioremap\n");
+ return;
+ }
+
+ *trace = (struct remap_trace) {
+ .probe = {
+ .addr = (unsigned long)addr,
+ .len = size,
+ .pre_handler = pre,
+ .post_handler = post,
+ .private = trace
+ },
+ .phys = offset,
+ .id = atomic_inc_return(&next_id)
+ };
+ map.map_id = trace->id;
+
+ spin_lock_irq(&trace_lock);
+ if (!is_enabled())
+ goto not_enabled;
+
+ mmio_trace_mapping(&map);
+ list_add_tail(&trace->list, &trace_list);
+ if (!nommiotrace)
+ register_kmmio_probe(&trace->probe);
+
+not_enabled:
+ spin_unlock_irq(&trace_lock);
+}
+
+void mmiotrace_ioremap(resource_size_t offset, unsigned long size,
+ void __iomem *addr)
+{
+ if (!is_enabled()) /* recheck and proper locking in *_core() */
+ return;
+
+ pr_debug(NAME "ioremap_*(0x%llx, 0x%lx) = %p\n",
+ (unsigned long long)offset, size, addr);
+ if ((filter_offset) && (offset != filter_offset))
+ return;
+ ioremap_trace_core(offset, size, addr);
+}
+
+static void iounmap_trace_core(volatile void __iomem *addr)
+{
+ struct mmiotrace_map map = {
+ .phys = 0,
+ .virt = (unsigned long)addr,
+ .len = 0,
+ .opcode = MMIO_UNPROBE
+ };
+ struct remap_trace *trace;
+ struct remap_trace *tmp;
+ struct remap_trace *found_trace = NULL;
+
+ pr_debug(NAME "Unmapping %p.\n", addr);
+
+ spin_lock_irq(&trace_lock);
+ if (!is_enabled())
+ goto not_enabled;
+
+ list_for_each_entry_safe(trace, tmp, &trace_list, list) {
+ if ((unsigned long)addr == trace->probe.addr) {
+ if (!nommiotrace)
+ unregister_kmmio_probe(&trace->probe);
+ list_del(&trace->list);
+ found_trace = trace;
+ break;
+ }
+ }
+ map.map_id = (found_trace) ? found_trace->id : -1;
+ mmio_trace_mapping(&map);
+
+not_enabled:
+ spin_unlock_irq(&trace_lock);
+ if (found_trace) {
+ synchronize_rcu(); /* unregister_kmmio_probe() requirement */
+ kfree(found_trace);
+ }
+}
+
+void mmiotrace_iounmap(volatile void __iomem *addr)
+{
+ might_sleep();
+ if (is_enabled()) /* recheck and proper locking in *_core() */
+ iounmap_trace_core(addr);
+}
+
+static void clear_trace_list(void)
+{
+ struct remap_trace *trace;
+ struct remap_trace *tmp;
+
+ /*
+ * No locking required, because the caller ensures we are in a
+ * critical section via mutex, and is_enabled() is false,
+ * i.e. nothing can traverse or modify this list.
+ * Caller also ensures is_enabled() cannot change.
+ */
+ list_for_each_entry(trace, &trace_list, list) {
+ pr_notice(NAME "purging non-iounmapped "
+ "trace @0x%08lx, size 0x%lx.\n",
+ trace->probe.addr, trace->probe.len);
+ if (!nommiotrace)
+ unregister_kmmio_probe(&trace->probe);
+ }
+ synchronize_rcu(); /* unregister_kmmio_probe() requirement */
+
+ list_for_each_entry_safe(trace, tmp, &trace_list, list) {
+ list_del(&trace->list);
+ kfree(trace);
+ }
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+static cpumask_t downed_cpus;
+
+static void enter_uniprocessor(void)
+{
+ int cpu;
+ int err;
+
+ get_online_cpus();
+ downed_cpus = cpu_online_map;
+ cpu_clear(first_cpu(cpu_online_map), downed_cpus);
+ if (num_online_cpus() > 1)
+ pr_notice(NAME "Disabling non-boot CPUs...\n");
+ put_online_cpus();
+
+ for_each_cpu_mask(cpu, downed_cpus) {
+ err = cpu_down(cpu);
+ if (!err)
+ pr_info(NAME "CPU%d is down.\n", cpu);
+ else
+ pr_err(NAME "Error taking CPU%d down: %d\n", cpu, err);
+ }
+ if (num_online_cpus() > 1)
+ pr_warning(NAME "multiple CPUs still online, "
+ "may miss events.\n");
+}
+
+static void leave_uniprocessor(void)
+{
+ int cpu;
+ int err;
+
+ if (cpus_weight(downed_cpus) == 0)
+ return;
+ pr_notice(NAME "Re-enabling CPUs...\n");
+ for_each_cpu_mask(cpu, downed_cpus) {
+ err = cpu_up(cpu);
+ if (!err)
+ pr_info(NAME "enabled CPU%d.\n", cpu);
+ else
+ pr_err(NAME "cannot re-enable CPU%d: %d\n", cpu, err);
+ }
+}
+
+#else /* !CONFIG_HOTPLUG_CPU */
+static void enter_uniprocessor(void)
+{
+ if (num_online_cpus() > 1)
+ pr_warning(NAME "multiple CPUs are online, may miss events. "
+ "Suggest booting with maxcpus=1 kernel argument.\n");
+}
+
+static void leave_uniprocessor(void)
+{
+}
+#endif
+
+#if 0 /* XXX: out of order */
+static struct file_operations fops_marker = {
+ .owner = THIS_MODULE,
+ .write = write_marker
+};
+#endif
+
+void enable_mmiotrace(void)
+{
+ mutex_lock(&mmiotrace_mutex);
+ if (is_enabled())
+ goto out;
+
+#if 0 /* XXX: tracing does not support text entries */
+ marker_file = debugfs_create_file("marker", 0660, dir, NULL,
+ &fops_marker);
+ if (!marker_file)
+ pr_err(NAME "marker file creation failed.\n");
+#endif
+
+ if (nommiotrace)
+ pr_info(NAME "MMIO tracing disabled.\n");
+ enter_uniprocessor();
+ spin_lock_irq(&trace_lock);
+ atomic_inc(&mmiotrace_enabled);
+ spin_unlock_irq(&trace_lock);
+ pr_info(NAME "enabled.\n");
+out:
+ mutex_unlock(&mmiotrace_mutex);
+}
+
+void disable_mmiotrace(void)
+{
+ mutex_lock(&mmiotrace_mutex);
+ if (!is_enabled())
+ goto out;
+
+ spin_lock_irq(&trace_lock);
+ atomic_dec(&mmiotrace_enabled);
+ BUG_ON(is_enabled());
+ spin_unlock_irq(&trace_lock);
+
+ clear_trace_list(); /* guarantees: no more kmmio callbacks */
+ leave_uniprocessor();
+ if (marker_file) {
+ debugfs_remove(marker_file);
+ marker_file = NULL;
+ }
+
+ pr_info(NAME "disabled.\n");
+out:
+ mutex_unlock(&mmiotrace_mutex);
+}
diff --git a/arch/x86/mm/numa_64.c b/arch/x86/mm/numa_64.c
index c5066d519e5d..c4557e25f60c 100644
--- a/arch/x86/mm/numa_64.c
+++ b/arch/x86/mm/numa_64.c
@@ -27,30 +27,17 @@
struct pglist_data *node_data[MAX_NUMNODES] __read_mostly;
EXPORT_SYMBOL(node_data);
-bootmem_data_t plat_node_bdata[MAX_NUMNODES];
+static bootmem_data_t plat_node_bdata[MAX_NUMNODES];
struct memnode memnode;
-#ifdef CONFIG_SMP
-int x86_cpu_to_node_map_init[NR_CPUS] = {
- [0 ... NR_CPUS-1] = NUMA_NO_NODE
-};
-void *x86_cpu_to_node_map_early_ptr;
-EXPORT_SYMBOL(x86_cpu_to_node_map_early_ptr);
-#endif
-DEFINE_PER_CPU(int, x86_cpu_to_node_map) = NUMA_NO_NODE;
-EXPORT_PER_CPU_SYMBOL(x86_cpu_to_node_map);
-
s16 apicid_to_node[MAX_LOCAL_APIC] __cpuinitdata = {
[0 ... MAX_LOCAL_APIC-1] = NUMA_NO_NODE
};
-cpumask_t node_to_cpumask_map[MAX_NUMNODES] __read_mostly;
-EXPORT_SYMBOL(node_to_cpumask_map);
-
int numa_off __initdata;
-unsigned long __initdata nodemap_addr;
-unsigned long __initdata nodemap_size;
+static unsigned long __initdata nodemap_addr;
+static unsigned long __initdata nodemap_size;
/*
* Given a shift value, try to populate memnodemap[]
@@ -192,7 +179,7 @@ static void * __init early_node_mem(int nodeid, unsigned long start,
void __init setup_node_bootmem(int nodeid, unsigned long start,
unsigned long end)
{
- unsigned long start_pfn, end_pfn, bootmap_pages, bootmap_size;
+ unsigned long start_pfn, last_pfn, bootmap_pages, bootmap_size;
unsigned long bootmap_start, nodedata_phys;
void *bootmap;
const int pgdat_size = round_up(sizeof(pg_data_t), PAGE_SIZE);
@@ -204,7 +191,7 @@ void __init setup_node_bootmem(int nodeid, unsigned long start,
start, end);
start_pfn = start >> PAGE_SHIFT;
- end_pfn = end >> PAGE_SHIFT;
+ last_pfn = end >> PAGE_SHIFT;
node_data[nodeid] = early_node_mem(nodeid, start, end, pgdat_size,
SMP_CACHE_BYTES);
@@ -217,7 +204,7 @@ void __init setup_node_bootmem(int nodeid, unsigned long start,
memset(NODE_DATA(nodeid), 0, sizeof(pg_data_t));
NODE_DATA(nodeid)->bdata = &plat_node_bdata[nodeid];
NODE_DATA(nodeid)->node_start_pfn = start_pfn;
- NODE_DATA(nodeid)->node_spanned_pages = end_pfn - start_pfn;
+ NODE_DATA(nodeid)->node_spanned_pages = last_pfn - start_pfn;
/*
* Find a place for the bootmem map
@@ -226,14 +213,14 @@ void __init setup_node_bootmem(int nodeid, unsigned long start,
* early_node_mem will get that with find_e820_area instead
* of alloc_bootmem, that could clash with reserved range
*/
- bootmap_pages = bootmem_bootmap_pages(end_pfn - start_pfn);
+ bootmap_pages = bootmem_bootmap_pages(last_pfn - start_pfn);
nid = phys_to_nid(nodedata_phys);
if (nid == nodeid)
bootmap_start = round_up(nodedata_phys + pgdat_size, PAGE_SIZE);
else
bootmap_start = round_up(start, PAGE_SIZE);
/*
- * SMP_CAHCE_BYTES could be enough, but init_bootmem_node like
+ * SMP_CACHE_BYTES could be enough, but init_bootmem_node like
* to use that to align to PAGE_SIZE
*/
bootmap = early_node_mem(nodeid, bootmap_start, end,
@@ -248,7 +235,7 @@ void __init setup_node_bootmem(int nodeid, unsigned long start,
bootmap_size = init_bootmem_node(NODE_DATA(nodeid),
bootmap_start >> PAGE_SHIFT,
- start_pfn, end_pfn);
+ start_pfn, last_pfn);
printk(KERN_INFO " bootmap [%016lx - %016lx] pages %lx\n",
bootmap_start, bootmap_start + bootmap_size - 1,
@@ -309,7 +296,7 @@ void __init numa_init_array(void)
#ifdef CONFIG_NUMA_EMU
/* Numa emulation */
-char *cmdline __initdata;
+static char *cmdline __initdata;
/*
* Setups up nid to range from addr to addr + size. If the end
@@ -413,15 +400,15 @@ static int __init split_nodes_by_size(struct bootnode *nodes, u64 *addr,
}
/*
- * Sets up the system RAM area from start_pfn to end_pfn according to the
+ * Sets up the system RAM area from start_pfn to last_pfn according to the
* numa=fake command-line option.
*/
static struct bootnode nodes[MAX_NUMNODES] __initdata;
-static int __init numa_emulation(unsigned long start_pfn, unsigned long end_pfn)
+static int __init numa_emulation(unsigned long start_pfn, unsigned long last_pfn)
{
u64 size, addr = start_pfn << PAGE_SHIFT;
- u64 max_addr = end_pfn << PAGE_SHIFT;
+ u64 max_addr = last_pfn << PAGE_SHIFT;
int num_nodes = 0, num = 0, coeff_flag, coeff = -1, i;
memset(&nodes, 0, sizeof(nodes));
@@ -527,7 +514,7 @@ out:
}
#endif /* CONFIG_NUMA_EMU */
-void __init numa_initmem_init(unsigned long start_pfn, unsigned long end_pfn)
+void __init numa_initmem_init(unsigned long start_pfn, unsigned long last_pfn)
{
int i;
@@ -535,7 +522,7 @@ void __init numa_initmem_init(unsigned long start_pfn, unsigned long end_pfn)
nodes_clear(node_online_map);
#ifdef CONFIG_NUMA_EMU
- if (cmdline && !numa_emulation(start_pfn, end_pfn))
+ if (cmdline && !numa_emulation(start_pfn, last_pfn))
return;
nodes_clear(node_possible_map);
nodes_clear(node_online_map);
@@ -543,7 +530,7 @@ void __init numa_initmem_init(unsigned long start_pfn, unsigned long end_pfn)
#ifdef CONFIG_ACPI_NUMA
if (!numa_off && !acpi_scan_nodes(start_pfn << PAGE_SHIFT,
- end_pfn << PAGE_SHIFT))
+ last_pfn << PAGE_SHIFT))
return;
nodes_clear(node_possible_map);
nodes_clear(node_online_map);
@@ -551,7 +538,7 @@ void __init numa_initmem_init(unsigned long start_pfn, unsigned long end_pfn)
#ifdef CONFIG_K8_NUMA
if (!numa_off && !k8_scan_nodes(start_pfn<<PAGE_SHIFT,
- end_pfn<<PAGE_SHIFT))
+ last_pfn<<PAGE_SHIFT))
return;
nodes_clear(node_possible_map);
nodes_clear(node_online_map);
@@ -561,7 +548,7 @@ void __init numa_initmem_init(unsigned long start_pfn, unsigned long end_pfn)
printk(KERN_INFO "Faking a node at %016lx-%016lx\n",
start_pfn << PAGE_SHIFT,
- end_pfn << PAGE_SHIFT);
+ last_pfn << PAGE_SHIFT);
/* setup dummy node covering all memory */
memnode_shift = 63;
memnodemap = memnode.embedded_map;
@@ -570,29 +557,8 @@ void __init numa_initmem_init(unsigned long start_pfn, unsigned long end_pfn)
node_set(0, node_possible_map);
for (i = 0; i < NR_CPUS; i++)
numa_set_node(i, 0);
- /* cpumask_of_cpu() may not be available during early startup */
- memset(&node_to_cpumask_map[0], 0, sizeof(node_to_cpumask_map[0]));
- cpu_set(0, node_to_cpumask_map[0]);
- e820_register_active_regions(0, start_pfn, end_pfn);
- setup_node_bootmem(0, start_pfn << PAGE_SHIFT, end_pfn << PAGE_SHIFT);
-}
-
-__cpuinit void numa_add_cpu(int cpu)
-{
- set_bit(cpu,
- (unsigned long *)&node_to_cpumask_map[early_cpu_to_node(cpu)]);
-}
-
-void __cpuinit numa_set_node(int cpu, int node)
-{
- int *cpu_to_node_map = x86_cpu_to_node_map_early_ptr;
-
- if(cpu_to_node_map)
- cpu_to_node_map[cpu] = node;
- else if(per_cpu_offset(cpu))
- per_cpu(x86_cpu_to_node_map, cpu) = node;
- else
- Dprintk(KERN_INFO "Setting node for non-present cpu %d\n", cpu);
+ e820_register_active_regions(0, start_pfn, last_pfn);
+ setup_node_bootmem(0, start_pfn << PAGE_SHIFT, last_pfn << PAGE_SHIFT);
}
unsigned long __init numa_free_all_bootmem(void)
@@ -641,6 +607,7 @@ static __init int numa_setup(char *opt)
}
early_param("numa", numa_setup);
+#ifdef CONFIG_NUMA
/*
* Setup early cpu_to_node.
*
@@ -652,14 +619,19 @@ early_param("numa", numa_setup);
* is already initialized in a round robin manner at numa_init_array,
* prior to this call, and this initialization is good enough
* for the fake NUMA cases.
+ *
+ * Called before the per_cpu areas are setup.
*/
void __init init_cpu_to_node(void)
{
- int i;
+ int cpu;
+ u16 *cpu_to_apicid = early_per_cpu_ptr(x86_cpu_to_apicid);
- for (i = 0; i < NR_CPUS; i++) {
+ BUG_ON(cpu_to_apicid == NULL);
+
+ for_each_possible_cpu(cpu) {
int node;
- u16 apicid = x86_cpu_to_apicid_init[i];
+ u16 apicid = cpu_to_apicid[cpu];
if (apicid == BAD_APICID)
continue;
@@ -668,8 +640,9 @@ void __init init_cpu_to_node(void)
continue;
if (!node_online(node))
continue;
- numa_set_node(i, node);
+ numa_set_node(cpu, node);
}
}
+#endif
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c
index 60bcb5b6a37e..105e8611b9a0 100644
--- a/arch/x86/mm/pageattr.c
+++ b/arch/x86/mm/pageattr.c
@@ -34,6 +34,41 @@ struct cpa_data {
unsigned force_split : 1;
};
+#ifdef CONFIG_PROC_FS
+static unsigned long direct_pages_count[PG_LEVEL_NUM];
+
+void update_page_count(int level, unsigned long pages)
+{
+ unsigned long flags;
+
+ /* Protect against CPA */
+ spin_lock_irqsave(&pgd_lock, flags);
+ direct_pages_count[level] += pages;
+ spin_unlock_irqrestore(&pgd_lock, flags);
+}
+
+static void split_page_count(int level)
+{
+ direct_pages_count[level]--;
+ direct_pages_count[level - 1] += PTRS_PER_PTE;
+}
+
+int arch_report_meminfo(char *page)
+{
+ int n = sprintf(page, "DirectMap4k: %8lu\n"
+ "DirectMap2M: %8lu\n",
+ direct_pages_count[PG_LEVEL_4K],
+ direct_pages_count[PG_LEVEL_2M]);
+#ifdef CONFIG_X86_64
+ n += sprintf(page + n, "DirectMap1G: %8lu\n",
+ direct_pages_count[PG_LEVEL_1G]);
+#endif
+ return n;
+}
+#else
+static inline void split_page_count(int level) { }
+#endif
+
#ifdef CONFIG_X86_64
static inline unsigned long highmap_start_pfn(void)
@@ -227,6 +262,7 @@ pte_t *lookup_address(unsigned long address, unsigned int *level)
return pte_offset_kernel(pmd, address);
}
+EXPORT_SYMBOL_GPL(lookup_address);
/*
* Set the new pmd in all the pgds we know about:
@@ -500,6 +536,10 @@ static int split_large_page(pte_t *kpte, unsigned long address)
for (i = 0; i < PTRS_PER_PTE; i++, pfn += pfninc)
set_pte(&pbase[i], pfn_pte(pfn, ref_prot));
+ if (address >= (unsigned long)__va(0) &&
+ address < (unsigned long)__va(max_pfn_mapped << PAGE_SHIFT))
+ split_page_count(level);
+
/*
* Install the new, split up pagetable. Important details here:
*
diff --git a/arch/x86/mm/pat.c b/arch/x86/mm/pat.c
index 06b7a1c90fb8..f8e1b9128cca 100644
--- a/arch/x86/mm/pat.c
+++ b/arch/x86/mm/pat.c
@@ -42,6 +42,19 @@ static int __init nopat(char *str)
early_param("nopat", nopat);
#endif
+
+static int debug_enable;
+static int __init pat_debug_setup(char *str)
+{
+ debug_enable = 1;
+ return 0;
+}
+__setup("debugpat", pat_debug_setup);
+
+#define dprintk(fmt, arg...) \
+ do { if (debug_enable) printk(KERN_INFO fmt, ##arg); } while (0)
+
+
static u64 __read_mostly boot_pat_state;
enum {
@@ -278,7 +291,7 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type,
struct memtype *saved_ptr;
if (parse->start >= end) {
- pr_debug("New Entry\n");
+ dprintk("New Entry\n");
list_add(&new_entry->nd, parse->nd.prev);
new_entry = NULL;
break;
@@ -328,7 +341,7 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type,
break;
}
- pr_debug("Overlap at 0x%Lx-0x%Lx\n",
+ dprintk("Overlap at 0x%Lx-0x%Lx\n",
saved_ptr->start, saved_ptr->end);
/* No conflict. Go ahead and add this new entry */
list_add(&new_entry->nd, saved_ptr->nd.prev);
@@ -380,7 +393,7 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type,
break;
}
- pr_debug(KERN_INFO "Overlap at 0x%Lx-0x%Lx\n",
+ dprintk("Overlap at 0x%Lx-0x%Lx\n",
saved_ptr->start, saved_ptr->end);
/* No conflict. Go ahead and add this new entry */
list_add(&new_entry->nd, &saved_ptr->nd);
@@ -402,16 +415,16 @@ int reserve_memtype(u64 start, u64 end, unsigned long req_type,
if (new_entry) {
/* No conflict. Not yet added to the list. Add to the tail */
list_add_tail(&new_entry->nd, &memtype_list);
- pr_debug("New Entry\n");
+ dprintk("New Entry\n");
}
if (ret_type) {
- pr_debug(
+ dprintk(
"reserve_memtype added 0x%Lx-0x%Lx, track %s, req %s, ret %s\n",
start, end, cattr_name(actual_type),
cattr_name(req_type), cattr_name(*ret_type));
} else {
- pr_debug(
+ dprintk(
"reserve_memtype added 0x%Lx-0x%Lx, track %s, req %s\n",
start, end, cattr_name(actual_type),
cattr_name(req_type));
@@ -452,7 +465,7 @@ int free_memtype(u64 start, u64 end)
current->comm, current->pid, start, end);
}
- pr_debug("free_memtype request 0x%Lx-0x%Lx\n", start, end);
+ dprintk("free_memtype request 0x%Lx-0x%Lx\n", start, end);
return err;
}
@@ -522,10 +535,10 @@ int phys_mem_access_prot_allowed(struct file *file, unsigned long pfn,
* we maintain the tradition of paranoia in this code.
*/
if (!pat_wc_enabled &&
- ! ( test_bit(X86_FEATURE_MTRR, boot_cpu_data.x86_capability) ||
- test_bit(X86_FEATURE_K6_MTRR, boot_cpu_data.x86_capability) ||
- test_bit(X86_FEATURE_CYRIX_ARR, boot_cpu_data.x86_capability) ||
- test_bit(X86_FEATURE_CENTAUR_MCR, boot_cpu_data.x86_capability)) &&
+ ! ( boot_cpu_has(X86_FEATURE_MTRR) ||
+ boot_cpu_has(X86_FEATURE_K6_MTRR) ||
+ boot_cpu_has(X86_FEATURE_CYRIX_ARR) ||
+ boot_cpu_has(X86_FEATURE_CENTAUR_MCR)) &&
(pfn << PAGE_SHIFT) >= __pa(high_memory)) {
flags = _PAGE_CACHE_UC;
}
diff --git a/arch/x86/mm/pf_in.c b/arch/x86/mm/pf_in.c
new file mode 100644
index 000000000000..efa1911e20ca
--- /dev/null
+++ b/arch/x86/mm/pf_in.c
@@ -0,0 +1,489 @@
+/*
+ * Fault Injection Test harness (FI)
+ * Copyright (C) Intel Crop.
+ *
+ * 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.
+ *
+ */
+
+/* Id: pf_in.c,v 1.1.1.1 2002/11/12 05:56:32 brlock Exp
+ * Copyright by Intel Crop., 2002
+ * Louis Zhuang (louis.zhuang@intel.com)
+ *
+ * Bjorn Steinbrink (B.Steinbrink@gmx.de), 2007
+ */
+
+#include <linux/module.h>
+#include <linux/ptrace.h> /* struct pt_regs */
+#include "pf_in.h"
+
+#ifdef __i386__
+/* IA32 Manual 3, 2-1 */
+static unsigned char prefix_codes[] = {
+ 0xF0, 0xF2, 0xF3, 0x2E, 0x36, 0x3E, 0x26, 0x64,
+ 0x65, 0x2E, 0x3E, 0x66, 0x67
+};
+/* IA32 Manual 3, 3-432*/
+static unsigned int reg_rop[] = {
+ 0x8A, 0x8B, 0xB60F, 0xB70F, 0xBE0F, 0xBF0F
+};
+static unsigned int reg_wop[] = { 0x88, 0x89 };
+static unsigned int imm_wop[] = { 0xC6, 0xC7 };
+/* IA32 Manual 3, 3-432*/
+static unsigned int rw8[] = { 0x88, 0x8A, 0xC6 };
+static unsigned int rw32[] = {
+ 0x89, 0x8B, 0xC7, 0xB60F, 0xB70F, 0xBE0F, 0xBF0F
+};
+static unsigned int mw8[] = { 0x88, 0x8A, 0xC6, 0xB60F, 0xBE0F };
+static unsigned int mw16[] = { 0xB70F, 0xBF0F };
+static unsigned int mw32[] = { 0x89, 0x8B, 0xC7 };
+static unsigned int mw64[] = {};
+#else /* not __i386__ */
+static unsigned char prefix_codes[] = {
+ 0x66, 0x67, 0x2E, 0x3E, 0x26, 0x64, 0x65, 0x36,
+ 0xF0, 0xF3, 0xF2,
+ /* REX Prefixes */
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f
+};
+/* AMD64 Manual 3, Appendix A*/
+static unsigned int reg_rop[] = {
+ 0x8A, 0x8B, 0xB60F, 0xB70F, 0xBE0F, 0xBF0F
+};
+static unsigned int reg_wop[] = { 0x88, 0x89 };
+static unsigned int imm_wop[] = { 0xC6, 0xC7 };
+static unsigned int rw8[] = { 0xC6, 0x88, 0x8A };
+static unsigned int rw32[] = {
+ 0xC7, 0x89, 0x8B, 0xB60F, 0xB70F, 0xBE0F, 0xBF0F
+};
+/* 8 bit only */
+static unsigned int mw8[] = { 0xC6, 0x88, 0x8A, 0xB60F, 0xBE0F };
+/* 16 bit only */
+static unsigned int mw16[] = { 0xB70F, 0xBF0F };
+/* 16 or 32 bit */
+static unsigned int mw32[] = { 0xC7 };
+/* 16, 32 or 64 bit */
+static unsigned int mw64[] = { 0x89, 0x8B };
+#endif /* not __i386__ */
+
+static int skip_prefix(unsigned char *addr, int *shorted, int *enlarged,
+ int *rexr)
+{
+ int i;
+ unsigned char *p = addr;
+ *shorted = 0;
+ *enlarged = 0;
+ *rexr = 0;
+
+restart:
+ for (i = 0; i < ARRAY_SIZE(prefix_codes); i++) {
+ if (*p == prefix_codes[i]) {
+ if (*p == 0x66)
+ *shorted = 1;
+#ifdef __amd64__
+ if ((*p & 0xf8) == 0x48)
+ *enlarged = 1;
+ if ((*p & 0xf4) == 0x44)
+ *rexr = 1;
+#endif
+ p++;
+ goto restart;
+ }
+ }
+
+ return (p - addr);
+}
+
+static int get_opcode(unsigned char *addr, unsigned int *opcode)
+{
+ int len;
+
+ if (*addr == 0x0F) {
+ /* 0x0F is extension instruction */
+ *opcode = *(unsigned short *)addr;
+ len = 2;
+ } else {
+ *opcode = *addr;
+ len = 1;
+ }
+
+ return len;
+}
+
+#define CHECK_OP_TYPE(opcode, array, type) \
+ for (i = 0; i < ARRAY_SIZE(array); i++) { \
+ if (array[i] == opcode) { \
+ rv = type; \
+ goto exit; \
+ } \
+ }
+
+enum reason_type get_ins_type(unsigned long ins_addr)
+{
+ unsigned int opcode;
+ unsigned char *p;
+ int shorted, enlarged, rexr;
+ int i;
+ enum reason_type rv = OTHERS;
+
+ p = (unsigned char *)ins_addr;
+ p += skip_prefix(p, &shorted, &enlarged, &rexr);
+ p += get_opcode(p, &opcode);
+
+ CHECK_OP_TYPE(opcode, reg_rop, REG_READ);
+ CHECK_OP_TYPE(opcode, reg_wop, REG_WRITE);
+ CHECK_OP_TYPE(opcode, imm_wop, IMM_WRITE);
+
+exit:
+ return rv;
+}
+#undef CHECK_OP_TYPE
+
+static unsigned int get_ins_reg_width(unsigned long ins_addr)
+{
+ unsigned int opcode;
+ unsigned char *p;
+ int i, shorted, enlarged, rexr;
+
+ p = (unsigned char *)ins_addr;
+ p += skip_prefix(p, &shorted, &enlarged, &rexr);
+ p += get_opcode(p, &opcode);
+
+ for (i = 0; i < ARRAY_SIZE(rw8); i++)
+ if (rw8[i] == opcode)
+ return 1;
+
+ for (i = 0; i < ARRAY_SIZE(rw32); i++)
+ if (rw32[i] == opcode)
+ return (shorted ? 2 : (enlarged ? 8 : 4));
+
+ printk(KERN_ERR "mmiotrace: Unknown opcode 0x%02x\n", opcode);
+ return 0;
+}
+
+unsigned int get_ins_mem_width(unsigned long ins_addr)
+{
+ unsigned int opcode;
+ unsigned char *p;
+ int i, shorted, enlarged, rexr;
+
+ p = (unsigned char *)ins_addr;
+ p += skip_prefix(p, &shorted, &enlarged, &rexr);
+ p += get_opcode(p, &opcode);
+
+ for (i = 0; i < ARRAY_SIZE(mw8); i++)
+ if (mw8[i] == opcode)
+ return 1;
+
+ for (i = 0; i < ARRAY_SIZE(mw16); i++)
+ if (mw16[i] == opcode)
+ return 2;
+
+ for (i = 0; i < ARRAY_SIZE(mw32); i++)
+ if (mw32[i] == opcode)
+ return shorted ? 2 : 4;
+
+ for (i = 0; i < ARRAY_SIZE(mw64); i++)
+ if (mw64[i] == opcode)
+ return shorted ? 2 : (enlarged ? 8 : 4);
+
+ printk(KERN_ERR "mmiotrace: Unknown opcode 0x%02x\n", opcode);
+ return 0;
+}
+
+/*
+ * Define register ident in mod/rm byte.
+ * Note: these are NOT the same as in ptrace-abi.h.
+ */
+enum {
+ arg_AL = 0,
+ arg_CL = 1,
+ arg_DL = 2,
+ arg_BL = 3,
+ arg_AH = 4,
+ arg_CH = 5,
+ arg_DH = 6,
+ arg_BH = 7,
+
+ arg_AX = 0,
+ arg_CX = 1,
+ arg_DX = 2,
+ arg_BX = 3,
+ arg_SP = 4,
+ arg_BP = 5,
+ arg_SI = 6,
+ arg_DI = 7,
+#ifdef __amd64__
+ arg_R8 = 8,
+ arg_R9 = 9,
+ arg_R10 = 10,
+ arg_R11 = 11,
+ arg_R12 = 12,
+ arg_R13 = 13,
+ arg_R14 = 14,
+ arg_R15 = 15
+#endif
+};
+
+static unsigned char *get_reg_w8(int no, struct pt_regs *regs)
+{
+ unsigned char *rv = NULL;
+
+ switch (no) {
+ case arg_AL:
+ rv = (unsigned char *)&regs->ax;
+ break;
+ case arg_BL:
+ rv = (unsigned char *)&regs->bx;
+ break;
+ case arg_CL:
+ rv = (unsigned char *)&regs->cx;
+ break;
+ case arg_DL:
+ rv = (unsigned char *)&regs->dx;
+ break;
+ case arg_AH:
+ rv = 1 + (unsigned char *)&regs->ax;
+ break;
+ case arg_BH:
+ rv = 1 + (unsigned char *)&regs->bx;
+ break;
+ case arg_CH:
+ rv = 1 + (unsigned char *)&regs->cx;
+ break;
+ case arg_DH:
+ rv = 1 + (unsigned char *)&regs->dx;
+ break;
+#ifdef __amd64__
+ case arg_R8:
+ rv = (unsigned char *)&regs->r8;
+ break;
+ case arg_R9:
+ rv = (unsigned char *)&regs->r9;
+ break;
+ case arg_R10:
+ rv = (unsigned char *)&regs->r10;
+ break;
+ case arg_R11:
+ rv = (unsigned char *)&regs->r11;
+ break;
+ case arg_R12:
+ rv = (unsigned char *)&regs->r12;
+ break;
+ case arg_R13:
+ rv = (unsigned char *)&regs->r13;
+ break;
+ case arg_R14:
+ rv = (unsigned char *)&regs->r14;
+ break;
+ case arg_R15:
+ rv = (unsigned char *)&regs->r15;
+ break;
+#endif
+ default:
+ printk(KERN_ERR "mmiotrace: Error reg no# %d\n", no);
+ break;
+ }
+ return rv;
+}
+
+static unsigned long *get_reg_w32(int no, struct pt_regs *regs)
+{
+ unsigned long *rv = NULL;
+
+ switch (no) {
+ case arg_AX:
+ rv = &regs->ax;
+ break;
+ case arg_BX:
+ rv = &regs->bx;
+ break;
+ case arg_CX:
+ rv = &regs->cx;
+ break;
+ case arg_DX:
+ rv = &regs->dx;
+ break;
+ case arg_SP:
+ rv = &regs->sp;
+ break;
+ case arg_BP:
+ rv = &regs->bp;
+ break;
+ case arg_SI:
+ rv = &regs->si;
+ break;
+ case arg_DI:
+ rv = &regs->di;
+ break;
+#ifdef __amd64__
+ case arg_R8:
+ rv = &regs->r8;
+ break;
+ case arg_R9:
+ rv = &regs->r9;
+ break;
+ case arg_R10:
+ rv = &regs->r10;
+ break;
+ case arg_R11:
+ rv = &regs->r11;
+ break;
+ case arg_R12:
+ rv = &regs->r12;
+ break;
+ case arg_R13:
+ rv = &regs->r13;
+ break;
+ case arg_R14:
+ rv = &regs->r14;
+ break;
+ case arg_R15:
+ rv = &regs->r15;
+ break;
+#endif
+ default:
+ printk(KERN_ERR "mmiotrace: Error reg no# %d\n", no);
+ }
+
+ return rv;
+}
+
+unsigned long get_ins_reg_val(unsigned long ins_addr, struct pt_regs *regs)
+{
+ unsigned int opcode;
+ unsigned char mod_rm;
+ int reg;
+ unsigned char *p;
+ int i, shorted, enlarged, rexr;
+ unsigned long rv;
+
+ p = (unsigned char *)ins_addr;
+ p += skip_prefix(p, &shorted, &enlarged, &rexr);
+ p += get_opcode(p, &opcode);
+ for (i = 0; i < ARRAY_SIZE(reg_rop); i++)
+ if (reg_rop[i] == opcode) {
+ rv = REG_READ;
+ goto do_work;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(reg_wop); i++)
+ if (reg_wop[i] == opcode) {
+ rv = REG_WRITE;
+ goto do_work;
+ }
+
+ printk(KERN_ERR "mmiotrace: Not a register instruction, opcode "
+ "0x%02x\n", opcode);
+ goto err;
+
+do_work:
+ mod_rm = *p;
+ reg = ((mod_rm >> 3) & 0x7) | (rexr << 3);
+ switch (get_ins_reg_width(ins_addr)) {
+ case 1:
+ return *get_reg_w8(reg, regs);
+
+ case 2:
+ return *(unsigned short *)get_reg_w32(reg, regs);
+
+ case 4:
+ return *(unsigned int *)get_reg_w32(reg, regs);
+
+#ifdef __amd64__
+ case 8:
+ return *(unsigned long *)get_reg_w32(reg, regs);
+#endif
+
+ default:
+ printk(KERN_ERR "mmiotrace: Error width# %d\n", reg);
+ }
+
+err:
+ return 0;
+}
+
+unsigned long get_ins_imm_val(unsigned long ins_addr)
+{
+ unsigned int opcode;
+ unsigned char mod_rm;
+ unsigned char mod;
+ unsigned char *p;
+ int i, shorted, enlarged, rexr;
+ unsigned long rv;
+
+ p = (unsigned char *)ins_addr;
+ p += skip_prefix(p, &shorted, &enlarged, &rexr);
+ p += get_opcode(p, &opcode);
+ for (i = 0; i < ARRAY_SIZE(imm_wop); i++)
+ if (imm_wop[i] == opcode) {
+ rv = IMM_WRITE;
+ goto do_work;
+ }
+
+ printk(KERN_ERR "mmiotrace: Not an immediate instruction, opcode "
+ "0x%02x\n", opcode);
+ goto err;
+
+do_work:
+ mod_rm = *p;
+ mod = mod_rm >> 6;
+ p++;
+ switch (mod) {
+ case 0:
+ /* if r/m is 5 we have a 32 disp (IA32 Manual 3, Table 2-2) */
+ /* AMD64: XXX Check for address size prefix? */
+ if ((mod_rm & 0x7) == 0x5)
+ p += 4;
+ break;
+
+ case 1:
+ p += 1;
+ break;
+
+ case 2:
+ p += 4;
+ break;
+
+ case 3:
+ default:
+ printk(KERN_ERR "mmiotrace: not a memory access instruction "
+ "at 0x%lx, rm_mod=0x%02x\n",
+ ins_addr, mod_rm);
+ }
+
+ switch (get_ins_reg_width(ins_addr)) {
+ case 1:
+ return *(unsigned char *)p;
+
+ case 2:
+ return *(unsigned short *)p;
+
+ case 4:
+ return *(unsigned int *)p;
+
+#ifdef __amd64__
+ case 8:
+ return *(unsigned long *)p;
+#endif
+
+ default:
+ printk(KERN_ERR "mmiotrace: Error: width.\n");
+ }
+
+err:
+ return 0;
+}
diff --git a/arch/x86/mm/pf_in.h b/arch/x86/mm/pf_in.h
new file mode 100644
index 000000000000..e05341a51a27
--- /dev/null
+++ b/arch/x86/mm/pf_in.h
@@ -0,0 +1,39 @@
+/*
+ * Fault Injection Test harness (FI)
+ * Copyright (C) Intel Crop.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __PF_H_
+#define __PF_H_
+
+enum reason_type {
+ NOT_ME, /* page fault is not in regions */
+ NOTHING, /* access others point in regions */
+ REG_READ, /* read from addr to reg */
+ REG_WRITE, /* write from reg to addr */
+ IMM_WRITE, /* write from imm to addr */
+ OTHERS /* Other instructions can not intercept */
+};
+
+enum reason_type get_ins_type(unsigned long ins_addr);
+unsigned int get_ins_mem_width(unsigned long ins_addr);
+unsigned long get_ins_reg_val(unsigned long ins_addr, struct pt_regs *regs);
+unsigned long get_ins_imm_val(unsigned long ins_addr);
+
+#endif /* __PF_H_ */
diff --git a/arch/x86/mm/pgtable.c b/arch/x86/mm/pgtable.c
index 50159764f694..ee1d6d39edd4 100644
--- a/arch/x86/mm/pgtable.c
+++ b/arch/x86/mm/pgtable.c
@@ -255,7 +255,7 @@ int ptep_test_and_clear_young(struct vm_area_struct *vma,
if (pte_young(*ptep))
ret = test_and_clear_bit(_PAGE_BIT_ACCESSED,
- &ptep->pte);
+ (unsigned long *) &ptep->pte);
if (ret)
pte_update(vma->vm_mm, addr, ptep);
diff --git a/arch/x86/mm/srat_64.c b/arch/x86/mm/srat_64.c
index 3890234e5b26..b67f5a16755f 100644
--- a/arch/x86/mm/srat_64.c
+++ b/arch/x86/mm/srat_64.c
@@ -97,36 +97,9 @@ static __init inline int srat_disabled(void)
return numa_off || acpi_numa < 0;
}
-/*
- * A lot of BIOS fill in 10 (= no distance) everywhere. This messes
- * up the NUMA heuristics which wants the local node to have a smaller
- * distance than the others.
- * Do some quick checks here and only use the SLIT if it passes.
- */
-static __init int slit_valid(struct acpi_table_slit *slit)
-{
- int i, j;
- int d = slit->locality_count;
- for (i = 0; i < d; i++) {
- for (j = 0; j < d; j++) {
- u8 val = slit->entry[d*i + j];
- if (i == j) {
- if (val != LOCAL_DISTANCE)
- return 0;
- } else if (val <= LOCAL_DISTANCE)
- return 0;
- }
- }
- return 1;
-}
-
/* Callback for SLIT parsing */
void __init acpi_numa_slit_init(struct acpi_table_slit *slit)
{
- if (!slit_valid(slit)) {
- printk(KERN_INFO "ACPI: SLIT table looks invalid. Not used.\n");
- return;
- }
acpi_slit = slit;
}
@@ -403,7 +376,7 @@ int __init acpi_scan_nodes(unsigned long start, unsigned long end)
if (node == NUMA_NO_NODE)
continue;
if (!node_isset(node, node_possible_map))
- numa_set_node(i, NUMA_NO_NODE);
+ numa_clear_node(i);
}
numa_init_array();
return 0;
@@ -522,6 +495,7 @@ int __node_distance(int a, int b)
EXPORT_SYMBOL(__node_distance);
+#if defined(CONFIG_MEMORY_HOTPLUG_SPARSE) || defined(CONFIG_ACPI_HOTPLUG_MEMORY)
int memory_add_physaddr_to_nid(u64 start)
{
int i, ret = 0;
@@ -533,4 +507,4 @@ int memory_add_physaddr_to_nid(u64 start)
return ret;
}
EXPORT_SYMBOL_GPL(memory_add_physaddr_to_nid);
-
+#endif
diff --git a/arch/x86/mm/testmmiotrace.c b/arch/x86/mm/testmmiotrace.c
new file mode 100644
index 000000000000..d877c5b423ef
--- /dev/null
+++ b/arch/x86/mm/testmmiotrace.c
@@ -0,0 +1,71 @@
+/*
+ * Written by Pekka Paalanen, 2008 <pq@iki.fi>
+ */
+#include <linux/module.h>
+#include <linux/io.h>
+
+#define MODULE_NAME "testmmiotrace"
+
+static unsigned long mmio_address;
+module_param(mmio_address, ulong, 0);
+MODULE_PARM_DESC(mmio_address, "Start address of the mapping of 16 kB.");
+
+static void do_write_test(void __iomem *p)
+{
+ unsigned int i;
+ for (i = 0; i < 256; i++)
+ iowrite8(i, p + i);
+ for (i = 1024; i < (5 * 1024); i += 2)
+ iowrite16(i * 12 + 7, p + i);
+ for (i = (5 * 1024); i < (16 * 1024); i += 4)
+ iowrite32(i * 212371 + 13, p + i);
+}
+
+static void do_read_test(void __iomem *p)
+{
+ unsigned int i;
+ for (i = 0; i < 256; i++)
+ ioread8(p + i);
+ for (i = 1024; i < (5 * 1024); i += 2)
+ ioread16(p + i);
+ for (i = (5 * 1024); i < (16 * 1024); i += 4)
+ ioread32(p + i);
+}
+
+static void do_test(void)
+{
+ void __iomem *p = ioremap_nocache(mmio_address, 0x4000);
+ if (!p) {
+ pr_err(MODULE_NAME ": could not ioremap, aborting.\n");
+ return;
+ }
+ do_write_test(p);
+ do_read_test(p);
+ iounmap(p);
+}
+
+static int __init init(void)
+{
+ if (mmio_address == 0) {
+ pr_err(MODULE_NAME ": you have to use the module argument "
+ "mmio_address.\n");
+ pr_err(MODULE_NAME ": DO NOT LOAD THIS MODULE UNLESS"
+ " YOU REALLY KNOW WHAT YOU ARE DOING!\n");
+ return -ENXIO;
+ }
+
+ pr_warning(MODULE_NAME ": WARNING: mapping 16 kB @ 0x%08lx "
+ "in PCI address space, and writing "
+ "rubbish in there.\n", mmio_address);
+ do_test();
+ return 0;
+}
+
+static void __exit cleanup(void)
+{
+ pr_debug(MODULE_NAME ": unloaded.\n");
+}
+
+module_init(init);
+module_exit(cleanup);
+MODULE_LICENSE("GPL");
diff --git a/arch/x86/pci/Makefile_32 b/arch/x86/pci/Makefile_32
index 89ec35d00efd..962d96c0495a 100644
--- a/arch/x86/pci/Makefile_32
+++ b/arch/x86/pci/Makefile_32
@@ -13,10 +13,11 @@ pci-y := fixup.o
pci-$(CONFIG_ACPI) += acpi.o
pci-y += legacy.o irq.o
-# Careful: VISWS and NUMAQ overrule the pci-y above. The colons are
+# Careful: VISWS overrule the pci-y above. The colons are
# therefor correct. This needs a proper fix by distangling the code.
pci-$(CONFIG_X86_VISWS) := visws.o fixup.o
-pci-$(CONFIG_X86_NUMAQ) := numa.o irq.o
+
+pci-$(CONFIG_X86_NUMAQ) += numa.o
# Necessary for NUMAQ as well
pci-$(CONFIG_NUMA) += mp_bus_to_node.o
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c
index d95de2f199cd..4fa52d3dc848 100644
--- a/arch/x86/pci/acpi.c
+++ b/arch/x86/pci/acpi.c
@@ -171,8 +171,11 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_device *device, int do
if (node != -1)
set_mp_bus_to_node(busnum, node);
else
- node = get_mp_bus_to_node(busnum);
#endif
+ node = get_mp_bus_to_node(busnum);
+
+ if (node != -1 && !node_online(node))
+ node = -1;
/* Allocate per-root-bus (not per bus) arch-specific data.
* TODO: leak; this memory is never freed.
@@ -204,21 +207,22 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_device *device, int do
if (!bus)
kfree(sd);
+ if (bus && node != -1) {
#ifdef CONFIG_ACPI_NUMA
- if (bus) {
- if (pxm >= 0) {
+ if (pxm >= 0)
printk(KERN_DEBUG "bus %02x -> pxm %d -> node %d\n",
- busnum, pxm, pxm_to_node(pxm));
- }
- }
+ busnum, pxm, node);
+#else
+ printk(KERN_DEBUG "bus %02x -> node %d\n",
+ busnum, node);
#endif
+ }
if (bus && (pci_probe & PCI_USE__CRS))
get_current_resources(device, busnum, domain, bus);
return bus;
}
-extern int pci_routeirq;
static int __init pci_acpi_init(void)
{
struct pci_dev *dev = NULL;
diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c
index 6e64aaf00d1d..e1986d266869 100644
--- a/arch/x86/pci/common.c
+++ b/arch/x86/pci/common.c
@@ -20,6 +20,7 @@
unsigned int pci_probe = PCI_PROBE_BIOS | PCI_PROBE_CONF1 | PCI_PROBE_CONF2 |
PCI_PROBE_MMCONF;
+unsigned int pci_early_dump_regs;
static int pci_bf_sort;
int pci_routeirq;
int pcibios_last_bus = -1;
@@ -121,6 +122,21 @@ void __init dmi_check_skip_isa_align(void)
dmi_check_system(can_skip_pciprobe_dmi_table);
}
+static void __devinit pcibios_fixup_device_resources(struct pci_dev *dev)
+{
+ struct resource *rom_r = &dev->resource[PCI_ROM_RESOURCE];
+
+ if (pci_probe & PCI_NOASSIGN_ROMS) {
+ if (rom_r->parent)
+ return;
+ if (rom_r->start) {
+ /* we deal with BIOS assigned ROM later */
+ return;
+ }
+ rom_r->start = rom_r->end = rom_r->flags = 0;
+ }
+}
+
/*
* Called after each bus is probed, but before its children
* are examined.
@@ -128,7 +144,11 @@ void __init dmi_check_skip_isa_align(void)
void __devinit pcibios_fixup_bus(struct pci_bus *b)
{
+ struct pci_dev *dev;
+
pci_read_bridge_bases(b);
+ list_for_each_entry(dev, &b->devices, bus_list)
+ pcibios_fixup_device_resources(dev);
}
/*
@@ -328,18 +348,18 @@ static struct dmi_system_id __devinitdata pciprobe_dmi_table[] = {
#endif
{
.callback = set_bf_sort,
- .ident = "HP ProLiant DL385 G2",
+ .ident = "HP ProLiant DL360",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "HP"),
- DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant DL385 G2"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant DL360"),
},
},
{
.callback = set_bf_sort,
- .ident = "HP ProLiant DL585 G2",
+ .ident = "HP ProLiant DL380",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "HP"),
- DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant DL585 G2"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant DL380"),
},
},
{}
@@ -483,12 +503,18 @@ char * __devinit pcibios_setup(char *str)
else if (!strcmp(str, "rom")) {
pci_probe |= PCI_ASSIGN_ROMS;
return NULL;
+ } else if (!strcmp(str, "norom")) {
+ pci_probe |= PCI_NOASSIGN_ROMS;
+ return NULL;
} else if (!strcmp(str, "assign-busses")) {
pci_probe |= PCI_ASSIGN_ALL_BUSSES;
return NULL;
} else if (!strcmp(str, "use_crs")) {
pci_probe |= PCI_USE__CRS;
return NULL;
+ } else if (!strcmp(str, "earlydump")) {
+ pci_early_dump_regs = 1;
+ return NULL;
} else if (!strcmp(str, "routeirq")) {
pci_routeirq = 1;
return NULL;
diff --git a/arch/x86/pci/direct.c b/arch/x86/pci/direct.c
index 21d1e0e0d535..27d61b63def6 100644
--- a/arch/x86/pci/direct.c
+++ b/arch/x86/pci/direct.c
@@ -8,18 +8,21 @@
#include "pci.h"
/*
- * Functions for accessing PCI configuration space with type 1 accesses
+ * Functions for accessing PCI base (first 256 bytes) and extended
+ * (4096 bytes per PCI function) configuration space with type 1
+ * accesses.
*/
#define PCI_CONF1_ADDRESS(bus, devfn, reg) \
- (0x80000000 | (bus << 16) | (devfn << 8) | (reg & ~3))
+ (0x80000000 | ((reg & 0xF00) << 16) | (bus << 16) \
+ | (devfn << 8) | (reg & 0xFC))
static int pci_conf1_read(unsigned int seg, unsigned int bus,
unsigned int devfn, int reg, int len, u32 *value)
{
unsigned long flags;
- if ((bus > 255) || (devfn > 255) || (reg > 255)) {
+ if ((bus > 255) || (devfn > 255) || (reg > 4095)) {
*value = -1;
return -EINVAL;
}
@@ -50,7 +53,7 @@ static int pci_conf1_write(unsigned int seg, unsigned int bus,
{
unsigned long flags;
- if ((bus > 255) || (devfn > 255) || (reg > 255))
+ if ((bus > 255) || (devfn > 255) || (reg > 4095))
return -EINVAL;
spin_lock_irqsave(&pci_config_lock, flags);
@@ -260,10 +263,16 @@ void __init pci_direct_init(int type)
return;
printk(KERN_INFO "PCI: Using configuration type %d for base access\n",
type);
- if (type == 1)
+ if (type == 1) {
raw_pci_ops = &pci_direct_conf1;
- else
+ if (!raw_pci_ext_ops && cpu_has_pci_ext_cfg) {
+ printk(KERN_INFO "PCI: Using configuration type 1 "
+ "for extended access\n");
+ raw_pci_ext_ops = &pci_direct_conf1;
+ }
+ } else {
raw_pci_ops = &pci_direct_conf2;
+ }
}
int __init pci_direct_probe(void)
diff --git a/arch/x86/pci/early.c b/arch/x86/pci/early.c
index 42df4b6606df..858dbe3399f9 100644
--- a/arch/x86/pci/early.c
+++ b/arch/x86/pci/early.c
@@ -49,7 +49,14 @@ void write_pci_config_byte(u8 bus, u8 slot, u8 func, u8 offset, u8 val)
{
PDprintk("%x writing to %x: %x\n", slot, offset, val);
outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
- outb(val, 0xcfc);
+ outb(val, 0xcfc + (offset&3));
+}
+
+void write_pci_config_16(u8 bus, u8 slot, u8 func, u8 offset, u16 val)
+{
+ PDprintk("%x writing to %x: %x\n", slot, offset, val);
+ outl(0x80000000 | (bus<<16) | (slot<<11) | (func<<8) | offset, 0xcf8);
+ outw(val, 0xcfc + (offset&2));
}
int early_pci_allowed(void)
@@ -57,3 +64,54 @@ int early_pci_allowed(void)
return (pci_probe & (PCI_PROBE_CONF1|PCI_PROBE_NOEARLY)) ==
PCI_PROBE_CONF1;
}
+
+void early_dump_pci_device(u8 bus, u8 slot, u8 func)
+{
+ int i;
+ int j;
+ u32 val;
+
+ printk("PCI: %02x:%02x:%02x", bus, slot, func);
+
+ for (i = 0; i < 256; i += 4) {
+ if (!(i & 0x0f))
+ printk("\n%04x:",i);
+
+ val = read_pci_config(bus, slot, func, i);
+ for (j = 0; j < 4; j++) {
+ printk(" %02x", val & 0xff);
+ val >>= 8;
+ }
+ }
+ printk("\n");
+}
+
+void early_dump_pci_devices(void)
+{
+ unsigned bus, slot, func;
+
+ if (!early_pci_allowed())
+ return;
+
+ for (bus = 0; bus < 256; bus++) {
+ for (slot = 0; slot < 32; slot++) {
+ for (func = 0; func < 8; func++) {
+ u32 class;
+ u8 type;
+ class = read_pci_config(bus, slot, func,
+ PCI_CLASS_REVISION);
+ if (class == 0xffffffff)
+ break;
+
+ early_dump_pci_device(bus, slot, func);
+
+ /* No multi-function device? */
+ type = read_pci_config_byte(bus, slot, func,
+ PCI_HEADER_TYPE);
+ if (!(type & 0x80))
+ break;
+ }
+ }
+ }
+}
+
diff --git a/arch/x86/pci/irq.c b/arch/x86/pci/irq.c
index ca8df9c260bc..c422e10337be 100644
--- a/arch/x86/pci/irq.c
+++ b/arch/x86/pci/irq.c
@@ -11,8 +11,8 @@
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/dmi.h>
-#include <asm/io.h>
-#include <asm/smp.h>
+#include <linux/io.h>
+#include <linux/smp.h>
#include <asm/io_apic.h>
#include <linux/irq.h>
#include <linux/acpi.h>
@@ -45,7 +45,8 @@ struct irq_router {
char *name;
u16 vendor, device;
int (*get)(struct pci_dev *router, struct pci_dev *dev, int pirq);
- int (*set)(struct pci_dev *router, struct pci_dev *dev, int pirq, int new);
+ int (*set)(struct pci_dev *router, struct pci_dev *dev, int pirq,
+ int new);
};
struct irq_router_handler {
@@ -61,7 +62,7 @@ void (*pcibios_disable_irq)(struct pci_dev *dev) = NULL;
* and perform checksum verification.
*/
-static inline struct irq_routing_table * pirq_check_routing_table(u8 *addr)
+static inline struct irq_routing_table *pirq_check_routing_table(u8 *addr)
{
struct irq_routing_table *rt;
int i;
@@ -74,10 +75,11 @@ static inline struct irq_routing_table * pirq_check_routing_table(u8 *addr)
rt->size < sizeof(struct irq_routing_table))
return NULL;
sum = 0;
- for (i=0; i < rt->size; i++)
+ for (i = 0; i < rt->size; i++)
sum += addr[i];
if (!sum) {
- DBG(KERN_DEBUG "PCI: Interrupt Routing Table found at 0x%p\n", rt);
+ DBG(KERN_DEBUG "PCI: Interrupt Routing Table found at 0x%p\n",
+ rt);
return rt;
}
return NULL;
@@ -100,7 +102,8 @@ static struct irq_routing_table * __init pirq_find_routing_table(void)
return rt;
printk(KERN_WARNING "PCI: PIRQ table NOT found at pirqaddr\n");
}
- for(addr = (u8 *) __va(0xf0000); addr < (u8 *) __va(0x100000); addr += 16) {
+ for (addr = (u8 *) __va(0xf0000); addr < (u8 *) __va(0x100000);
+ addr += 16) {
rt = pirq_check_routing_table(addr);
if (rt)
return rt;
@@ -122,20 +125,23 @@ static void __init pirq_peer_trick(void)
struct irq_info *e;
memset(busmap, 0, sizeof(busmap));
- for(i=0; i < (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); i++) {
+ for (i = 0; i < (rt->size - sizeof(struct irq_routing_table)) /
+ sizeof(struct irq_info); i++) {
e = &rt->slots[i];
#ifdef DEBUG
{
int j;
- DBG(KERN_DEBUG "%02x:%02x slot=%02x", e->bus, e->devfn/8, e->slot);
- for(j=0; j<4; j++)
- DBG(" %d:%02x/%04x", j, e->irq[j].link, e->irq[j].bitmap);
+ DBG(KERN_DEBUG "%02x:%02x slot=%02x", e->bus,
+ e->devfn/8, e->slot);
+ for (j = 0; j < 4; j++)
+ DBG(" %d:%02x/%04x", j, e->irq[j].link,
+ e->irq[j].bitmap);
DBG("\n");
}
#endif
busmap[e->bus] = 1;
}
- for(i = 1; i < 256; i++) {
+ for (i = 1; i < 256; i++) {
int node;
if (!busmap[i] || pci_find_bus(0, i))
continue;
@@ -174,7 +180,8 @@ void eisa_set_level_irq(unsigned int irq)
* Common IRQ routing practice: nibbles in config space,
* offset by some magic constant.
*/
-static unsigned int read_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr)
+static unsigned int
+read_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr)
{
u8 x;
unsigned reg = offset + (nr >> 1);
@@ -183,7 +190,8 @@ static unsigned int read_config_nybble(struct pci_dev *router, unsigned offset,
return (nr & 1) ? (x >> 4) : (x & 0xf);
}
-static void write_config_nybble(struct pci_dev *router, unsigned offset, unsigned nr, unsigned int val)
+static void write_config_nybble(struct pci_dev *router, unsigned offset,
+ unsigned nr, unsigned int val)
{
u8 x;
unsigned reg = offset + (nr >> 1);
@@ -200,15 +208,18 @@ static void write_config_nybble(struct pci_dev *router, unsigned offset, unsigne
*/
static int pirq_ali_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
{
- static const unsigned char irqmap[16] = { 0, 9, 3, 10, 4, 5, 7, 6, 1, 11, 0, 12, 0, 14, 0, 15 };
+ static const unsigned char irqmap[16] = { 0, 9, 3, 10, 4, 5, 7, 6, 1,
+ 11, 0, 12, 0, 14, 0, 15 };
WARN_ON_ONCE(pirq > 16);
return irqmap[read_config_nybble(router, 0x48, pirq-1)];
}
-static int pirq_ali_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+static int
+pirq_ali_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
- static const unsigned char irqmap[16] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1, 3, 9, 11, 0, 13, 15 };
+ static const unsigned char irqmap[16] = { 0, 8, 0, 2, 4, 5, 7, 6, 0, 1,
+ 3, 9, 11, 0, 13, 15 };
unsigned int val = irqmap[irq];
WARN_ON_ONCE(pirq > 16);
@@ -231,7 +242,8 @@ static int pirq_piix_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
return (x < 16) ? x : 0;
}
-static int pirq_piix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+static int
+pirq_piix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
pci_write_config_byte(router, pirq, irq);
return 1;
@@ -247,7 +259,8 @@ static int pirq_via_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
return read_config_nybble(router, 0x55, pirq == 4 ? 5 : pirq);
}
-static int pirq_via_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+static int
+pirq_via_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
write_config_nybble(router, 0x55, pirq == 4 ? 5 : pirq, irq);
return 1;
@@ -258,7 +271,8 @@ static int pirq_via_set(struct pci_dev *router, struct pci_dev *dev, int pirq, i
* but without the ugly irq number munging.
* However, for 82C586, nibble map is different .
*/
-static int pirq_via586_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
+static int
+pirq_via586_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
{
static const unsigned int pirqmap[5] = { 3, 2, 5, 1, 1 };
@@ -266,7 +280,8 @@ static int pirq_via586_get(struct pci_dev *router, struct pci_dev *dev, int pirq
return read_config_nybble(router, 0x55, pirqmap[pirq-1]);
}
-static int pirq_via586_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+static int
+pirq_via586_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
static const unsigned int pirqmap[5] = { 3, 2, 5, 1, 1 };
@@ -285,10 +300,11 @@ static int pirq_ite_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
static const unsigned char pirqmap[4] = { 1, 0, 2, 3 };
WARN_ON_ONCE(pirq > 4);
- return read_config_nybble(router,0x43, pirqmap[pirq-1]);
+ return read_config_nybble(router, 0x43, pirqmap[pirq-1]);
}
-static int pirq_ite_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+static int
+pirq_ite_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
static const unsigned char pirqmap[4] = { 1, 0, 2, 3 };
@@ -306,7 +322,8 @@ static int pirq_opti_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
return read_config_nybble(router, 0xb8, pirq >> 4);
}
-static int pirq_opti_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+static int
+pirq_opti_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
write_config_nybble(router, 0xb8, pirq >> 4, irq);
return 1;
@@ -314,7 +331,7 @@ static int pirq_opti_set(struct pci_dev *router, struct pci_dev *dev, int pirq,
/*
* Cyrix: nibble offset 0x5C
- * 0x5C bits 7:4 is INTB bits 3:0 is INTA
+ * 0x5C bits 7:4 is INTB bits 3:0 is INTA
* 0x5D bits 7:4 is INTD bits 3:0 is INTC
*/
static int pirq_cyrix_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
@@ -322,7 +339,8 @@ static int pirq_cyrix_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
return read_config_nybble(router, 0x5C, (pirq-1)^1);
}
-static int pirq_cyrix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+static int
+pirq_cyrix_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
write_config_nybble(router, 0x5C, (pirq-1)^1, irq);
return 1;
@@ -350,7 +368,7 @@ static int pirq_cyrix_set(struct pci_dev *router, struct pci_dev *dev, int pirq,
* Apparently there are systems implementing PCI routing table using
* link values 0x01-0x04 and others using 0x41-0x44 for PCI INTA..D.
* We try our best to handle both link mappings.
- *
+ *
* Currently (2003-05-21) it appears most SiS chipsets follow the
* definition of routing registers from the SiS-5595 southbridge.
* According to the SiS 5595 datasheets the revision id's of the
@@ -370,7 +388,7 @@ static int pirq_cyrix_set(struct pci_dev *router, struct pci_dev *dev, int pirq,
*
* 0x62: USBIRQ:
* bit 6 OHCI function disabled (0), enabled (1)
- *
+ *
* 0x6a: ACPI/SCI IRQ: bits 4-6 reserved
*
* 0x7e: Data Acq. Module IRQ - bits 4-6 reserved
@@ -405,7 +423,8 @@ static int pirq_sis_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
return (x & PIRQ_SIS_IRQ_DISABLE) ? 0 : (x & PIRQ_SIS_IRQ_MASK);
}
-static int pirq_sis_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+static int
+pirq_sis_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
u8 x;
int reg;
@@ -439,7 +458,8 @@ static int pirq_vlsi_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
return read_config_nybble(router, 0x74, pirq-1);
}
-static int pirq_vlsi_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+static int
+pirq_vlsi_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
WARN_ON_ONCE(pirq >= 9);
if (pirq > 8) {
@@ -461,13 +481,15 @@ static int pirq_vlsi_set(struct pci_dev *router, struct pci_dev *dev, int pirq,
* 0x00 for ACPI (SCI), 0x01 for USB, 0x02 for IDE0, 0x04 for IDE1,
* and 0x03 for SMBus.
*/
-static int pirq_serverworks_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
+static int
+pirq_serverworks_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
{
outb(pirq, 0xc00);
return inb(0xc01) & 0xf;
}
-static int pirq_serverworks_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+static int pirq_serverworks_set(struct pci_dev *router, struct pci_dev *dev,
+ int pirq, int irq)
{
outb(pirq, 0xc00);
outb(irq, 0xc01);
@@ -482,27 +504,27 @@ static int pirq_serverworks_set(struct pci_dev *router, struct pci_dev *dev, int
* offset 0x56 0-3 PIRQA 4-7 PIRQB
* offset 0x57 0-3 PIRQC 4-7 PIRQD
*/
-static int pirq_amd756_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
+static int
+pirq_amd756_get(struct pci_dev *router, struct pci_dev *dev, int pirq)
{
u8 irq;
irq = 0;
if (pirq <= 4)
- {
irq = read_config_nybble(router, 0x56, pirq - 1);
- }
- printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d get irq : %2d\n",
+ printk(KERN_INFO
+ "AMD756: dev %04x:%04x, router pirq : %d get irq : %2d\n",
dev->vendor, dev->device, pirq, irq);
return irq;
}
-static int pirq_amd756_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+static int
+pirq_amd756_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
- printk(KERN_INFO "AMD756: dev %04x:%04x, router pirq : %d SET irq : %2d\n",
+ printk(KERN_INFO
+ "AMD756: dev %04x:%04x, router pirq : %d SET irq : %2d\n",
dev->vendor, dev->device, pirq, irq);
if (pirq <= 4)
- {
write_config_nybble(router, 0x56, pirq - 1, irq);
- }
return 1;
}
@@ -528,7 +550,8 @@ static int pirq_pico_set(struct pci_dev *router, struct pci_dev *dev, int pirq,
#ifdef CONFIG_PCI_BIOS
-static int pirq_bios_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
+static int
+pirq_bios_set(struct pci_dev *router, struct pci_dev *dev, int pirq, int irq)
{
struct pci_dev *bridge;
int pin = pci_get_interrupt_pin(dev, &bridge);
@@ -537,11 +560,14 @@ static int pirq_bios_set(struct pci_dev *router, struct pci_dev *dev, int pirq,
#endif
-static __init int intel_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
+static __init int
+intel_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
{
static struct pci_device_id __initdata pirq_440gx[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443GX_0) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443GX_2) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_82443GX_0) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL,
+ PCI_DEVICE_ID_INTEL_82443GX_2) },
{ },
};
@@ -549,50 +575,49 @@ static __init int intel_router_probe(struct irq_router *r, struct pci_dev *route
if (pci_dev_present(pirq_440gx))
return 0;
- switch(device)
- {
- case PCI_DEVICE_ID_INTEL_82371FB_0:
- case PCI_DEVICE_ID_INTEL_82371SB_0:
- case PCI_DEVICE_ID_INTEL_82371AB_0:
- case PCI_DEVICE_ID_INTEL_82371MX:
- case PCI_DEVICE_ID_INTEL_82443MX_0:
- case PCI_DEVICE_ID_INTEL_82801AA_0:
- case PCI_DEVICE_ID_INTEL_82801AB_0:
- case PCI_DEVICE_ID_INTEL_82801BA_0:
- case PCI_DEVICE_ID_INTEL_82801BA_10:
- case PCI_DEVICE_ID_INTEL_82801CA_0:
- case PCI_DEVICE_ID_INTEL_82801CA_12:
- case PCI_DEVICE_ID_INTEL_82801DB_0:
- case PCI_DEVICE_ID_INTEL_82801E_0:
- case PCI_DEVICE_ID_INTEL_82801EB_0:
- case PCI_DEVICE_ID_INTEL_ESB_1:
- case PCI_DEVICE_ID_INTEL_ICH6_0:
- case PCI_DEVICE_ID_INTEL_ICH6_1:
- case PCI_DEVICE_ID_INTEL_ICH7_0:
- case PCI_DEVICE_ID_INTEL_ICH7_1:
- case PCI_DEVICE_ID_INTEL_ICH7_30:
- case PCI_DEVICE_ID_INTEL_ICH7_31:
- case PCI_DEVICE_ID_INTEL_ESB2_0:
- case PCI_DEVICE_ID_INTEL_ICH8_0:
- case PCI_DEVICE_ID_INTEL_ICH8_1:
- case PCI_DEVICE_ID_INTEL_ICH8_2:
- case PCI_DEVICE_ID_INTEL_ICH8_3:
- case PCI_DEVICE_ID_INTEL_ICH8_4:
- case PCI_DEVICE_ID_INTEL_ICH9_0:
- case PCI_DEVICE_ID_INTEL_ICH9_1:
- case PCI_DEVICE_ID_INTEL_ICH9_2:
- case PCI_DEVICE_ID_INTEL_ICH9_3:
- case PCI_DEVICE_ID_INTEL_ICH9_4:
- case PCI_DEVICE_ID_INTEL_ICH9_5:
- case PCI_DEVICE_ID_INTEL_TOLAPAI_0:
- case PCI_DEVICE_ID_INTEL_ICH10_0:
- case PCI_DEVICE_ID_INTEL_ICH10_1:
- case PCI_DEVICE_ID_INTEL_ICH10_2:
- case PCI_DEVICE_ID_INTEL_ICH10_3:
- r->name = "PIIX/ICH";
- r->get = pirq_piix_get;
- r->set = pirq_piix_set;
- return 1;
+ switch (device) {
+ case PCI_DEVICE_ID_INTEL_82371FB_0:
+ case PCI_DEVICE_ID_INTEL_82371SB_0:
+ case PCI_DEVICE_ID_INTEL_82371AB_0:
+ case PCI_DEVICE_ID_INTEL_82371MX:
+ case PCI_DEVICE_ID_INTEL_82443MX_0:
+ case PCI_DEVICE_ID_INTEL_82801AA_0:
+ case PCI_DEVICE_ID_INTEL_82801AB_0:
+ case PCI_DEVICE_ID_INTEL_82801BA_0:
+ case PCI_DEVICE_ID_INTEL_82801BA_10:
+ case PCI_DEVICE_ID_INTEL_82801CA_0:
+ case PCI_DEVICE_ID_INTEL_82801CA_12:
+ case PCI_DEVICE_ID_INTEL_82801DB_0:
+ case PCI_DEVICE_ID_INTEL_82801E_0:
+ case PCI_DEVICE_ID_INTEL_82801EB_0:
+ case PCI_DEVICE_ID_INTEL_ESB_1:
+ case PCI_DEVICE_ID_INTEL_ICH6_0:
+ case PCI_DEVICE_ID_INTEL_ICH6_1:
+ case PCI_DEVICE_ID_INTEL_ICH7_0:
+ case PCI_DEVICE_ID_INTEL_ICH7_1:
+ case PCI_DEVICE_ID_INTEL_ICH7_30:
+ case PCI_DEVICE_ID_INTEL_ICH7_31:
+ case PCI_DEVICE_ID_INTEL_ESB2_0:
+ case PCI_DEVICE_ID_INTEL_ICH8_0:
+ case PCI_DEVICE_ID_INTEL_ICH8_1:
+ case PCI_DEVICE_ID_INTEL_ICH8_2:
+ case PCI_DEVICE_ID_INTEL_ICH8_3:
+ case PCI_DEVICE_ID_INTEL_ICH8_4:
+ case PCI_DEVICE_ID_INTEL_ICH9_0:
+ case PCI_DEVICE_ID_INTEL_ICH9_1:
+ case PCI_DEVICE_ID_INTEL_ICH9_2:
+ case PCI_DEVICE_ID_INTEL_ICH9_3:
+ case PCI_DEVICE_ID_INTEL_ICH9_4:
+ case PCI_DEVICE_ID_INTEL_ICH9_5:
+ case PCI_DEVICE_ID_INTEL_TOLAPAI_0:
+ case PCI_DEVICE_ID_INTEL_ICH10_0:
+ case PCI_DEVICE_ID_INTEL_ICH10_1:
+ case PCI_DEVICE_ID_INTEL_ICH10_2:
+ case PCI_DEVICE_ID_INTEL_ICH10_3:
+ r->name = "PIIX/ICH";
+ r->get = pirq_piix_get;
+ r->set = pirq_piix_set;
+ return 1;
}
return 0;
}
@@ -606,7 +631,7 @@ static __init int via_router_probe(struct irq_router *r,
* workarounds for some buggy BIOSes
*/
if (device == PCI_DEVICE_ID_VIA_82C586_0) {
- switch(router->device) {
+ switch (router->device) {
case PCI_DEVICE_ID_VIA_82C686:
/*
* Asus k7m bios wrongly reports 82C686A
@@ -631,7 +656,7 @@ static __init int via_router_probe(struct irq_router *r,
}
}
- switch(device) {
+ switch (device) {
case PCI_DEVICE_ID_VIA_82C586_0:
r->name = "VIA";
r->get = pirq_via586_get;
@@ -652,88 +677,89 @@ static __init int via_router_probe(struct irq_router *r,
return 0;
}
-static __init int vlsi_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
+static __init int
+vlsi_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
{
- switch(device)
- {
- case PCI_DEVICE_ID_VLSI_82C534:
- r->name = "VLSI 82C534";
- r->get = pirq_vlsi_get;
- r->set = pirq_vlsi_set;
- return 1;
+ switch (device) {
+ case PCI_DEVICE_ID_VLSI_82C534:
+ r->name = "VLSI 82C534";
+ r->get = pirq_vlsi_get;
+ r->set = pirq_vlsi_set;
+ return 1;
}
return 0;
}
-static __init int serverworks_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
+static __init int serverworks_router_probe(struct irq_router *r,
+ struct pci_dev *router, u16 device)
{
- switch(device)
- {
- case PCI_DEVICE_ID_SERVERWORKS_OSB4:
- case PCI_DEVICE_ID_SERVERWORKS_CSB5:
- r->name = "ServerWorks";
- r->get = pirq_serverworks_get;
- r->set = pirq_serverworks_set;
- return 1;
+ switch (device) {
+ case PCI_DEVICE_ID_SERVERWORKS_OSB4:
+ case PCI_DEVICE_ID_SERVERWORKS_CSB5:
+ r->name = "ServerWorks";
+ r->get = pirq_serverworks_get;
+ r->set = pirq_serverworks_set;
+ return 1;
}
return 0;
}
-static __init int sis_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
+static __init int
+sis_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
{
if (device != PCI_DEVICE_ID_SI_503)
return 0;
-
+
r->name = "SIS";
r->get = pirq_sis_get;
r->set = pirq_sis_set;
return 1;
}
-static __init int cyrix_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
+static __init int
+cyrix_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
{
- switch(device)
- {
- case PCI_DEVICE_ID_CYRIX_5520:
- r->name = "NatSemi";
- r->get = pirq_cyrix_get;
- r->set = pirq_cyrix_set;
- return 1;
+ switch (device) {
+ case PCI_DEVICE_ID_CYRIX_5520:
+ r->name = "NatSemi";
+ r->get = pirq_cyrix_get;
+ r->set = pirq_cyrix_set;
+ return 1;
}
return 0;
}
-static __init int opti_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
+static __init int
+opti_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
{
- switch(device)
- {
- case PCI_DEVICE_ID_OPTI_82C700:
- r->name = "OPTI";
- r->get = pirq_opti_get;
- r->set = pirq_opti_set;
- return 1;
+ switch (device) {
+ case PCI_DEVICE_ID_OPTI_82C700:
+ r->name = "OPTI";
+ r->get = pirq_opti_get;
+ r->set = pirq_opti_set;
+ return 1;
}
return 0;
}
-static __init int ite_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
+static __init int
+ite_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
{
- switch(device)
- {
- case PCI_DEVICE_ID_ITE_IT8330G_0:
- r->name = "ITE";
- r->get = pirq_ite_get;
- r->set = pirq_ite_set;
- return 1;
+ switch (device) {
+ case PCI_DEVICE_ID_ITE_IT8330G_0:
+ r->name = "ITE";
+ r->get = pirq_ite_get;
+ r->set = pirq_ite_set;
+ return 1;
}
return 0;
}
-static __init int ali_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
+static __init int
+ali_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
{
- switch(device)
- {
+ switch (device) {
case PCI_DEVICE_ID_AL_M1533:
case PCI_DEVICE_ID_AL_M1563:
printk(KERN_DEBUG "PCI: Using ALI IRQ Router\n");
@@ -745,28 +771,29 @@ static __init int ali_router_probe(struct irq_router *r, struct pci_dev *router,
return 0;
}
-static __init int amd_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
+static __init int
+amd_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
{
- switch(device)
- {
- case PCI_DEVICE_ID_AMD_VIPER_740B:
- r->name = "AMD756";
- break;
- case PCI_DEVICE_ID_AMD_VIPER_7413:
- r->name = "AMD766";
- break;
- case PCI_DEVICE_ID_AMD_VIPER_7443:
- r->name = "AMD768";
- break;
- default:
- return 0;
+ switch (device) {
+ case PCI_DEVICE_ID_AMD_VIPER_740B:
+ r->name = "AMD756";
+ break;
+ case PCI_DEVICE_ID_AMD_VIPER_7413:
+ r->name = "AMD766";
+ break;
+ case PCI_DEVICE_ID_AMD_VIPER_7443:
+ r->name = "AMD768";
+ break;
+ default:
+ return 0;
}
r->get = pirq_amd756_get;
r->set = pirq_amd756_set;
return 1;
}
-
-static __init int pico_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
+
+static __init int
+pico_router_probe(struct irq_router *r, struct pci_dev *router, u16 device)
{
switch (device) {
case PCI_DEVICE_ID_PICOPOWER_PT86C523:
@@ -807,7 +834,7 @@ static struct pci_dev *pirq_router_dev;
* FIXME: should we have an option to say "generic for
* chipset" ?
*/
-
+
static void __init pirq_find_router(struct irq_router *r)
{
struct irq_routing_table *rt = pirq_table;
@@ -826,7 +853,7 @@ static void __init pirq_find_router(struct irq_router *r)
r->name = "default";
r->get = NULL;
r->set = NULL;
-
+
DBG(KERN_DEBUG "PCI: Attempting to find IRQ router for %04x:%04x\n",
rt->rtr_vendor, rt->rtr_device);
@@ -837,12 +864,14 @@ static void __init pirq_find_router(struct irq_router *r)
return;
}
- for( h = pirq_routers; h->vendor; h++) {
+ for (h = pirq_routers; h->vendor; h++) {
/* First look for a router match */
- if (rt->rtr_vendor == h->vendor && h->probe(r, pirq_router_dev, rt->rtr_device))
+ if (rt->rtr_vendor == h->vendor &&
+ h->probe(r, pirq_router_dev, rt->rtr_device))
break;
/* Fall back to a device match */
- if (pirq_router_dev->vendor == h->vendor && h->probe(r, pirq_router_dev, pirq_router_dev->device))
+ if (pirq_router_dev->vendor == h->vendor &&
+ h->probe(r, pirq_router_dev, pirq_router_dev->device))
break;
}
printk(KERN_INFO "PCI: Using IRQ router %s [%04x/%04x] at %s\n",
@@ -857,11 +886,13 @@ static void __init pirq_find_router(struct irq_router *r)
static struct irq_info *pirq_get_info(struct pci_dev *dev)
{
struct irq_routing_table *rt = pirq_table;
- int entries = (rt->size - sizeof(struct irq_routing_table)) / sizeof(struct irq_info);
+ int entries = (rt->size - sizeof(struct irq_routing_table)) /
+ sizeof(struct irq_info);
struct irq_info *info;
for (info = rt->slots; entries--; info++)
- if (info->bus == dev->bus->number && PCI_SLOT(info->devfn) == PCI_SLOT(dev->devfn))
+ if (info->bus == dev->bus->number &&
+ PCI_SLOT(info->devfn) == PCI_SLOT(dev->devfn))
return info;
return NULL;
}
@@ -889,7 +920,7 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
if (!pirq_table)
return 0;
-
+
DBG(KERN_DEBUG "IRQ for %s[%c]", pci_name(dev), 'A' + pin);
info = pirq_get_info(dev);
if (!info) {
@@ -902,7 +933,8 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
DBG(" -> not routed\n" KERN_DEBUG);
return 0;
}
- DBG(" -> PIRQ %02x, mask %04x, excl %04x", pirq, mask, pirq_table->exclusive_irqs);
+ DBG(" -> PIRQ %02x, mask %04x, excl %04x", pirq, mask,
+ pirq_table->exclusive_irqs);
mask &= pcibios_irq_mask;
/* Work around broken HP Pavilion Notebooks which assign USB to
@@ -915,7 +947,8 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
}
/* same for Acer Travelmate 360, but with CB and irq 11 -> 10 */
- if (acer_tm360_irqrouting && dev->irq == 11 && dev->vendor == PCI_VENDOR_ID_O2) {
+ if (acer_tm360_irqrouting && dev->irq == 11 &&
+ dev->vendor == PCI_VENDOR_ID_O2) {
pirq = 0x68;
mask = 0x400;
dev->irq = r->get(pirq_router_dev, dev, pirq);
@@ -928,17 +961,20 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
*/
newirq = dev->irq;
if (newirq && !((1 << newirq) & mask)) {
- if ( pci_probe & PCI_USE_PIRQ_MASK) newirq = 0;
- else printk("\n" KERN_WARNING
- "PCI: IRQ %i for device %s doesn't match PIRQ mask "
- "- try pci=usepirqmask\n" KERN_DEBUG, newirq,
- pci_name(dev));
+ if (pci_probe & PCI_USE_PIRQ_MASK)
+ newirq = 0;
+ else
+ printk("\n" KERN_WARNING
+ "PCI: IRQ %i for device %s doesn't match PIRQ mask - try pci=usepirqmask\n"
+ KERN_DEBUG, newirq,
+ pci_name(dev));
}
if (!newirq && assign) {
for (i = 0; i < 16; i++) {
if (!(mask & (1 << i)))
continue;
- if (pirq_penalty[i] < pirq_penalty[newirq] && can_request_irq(i, IRQF_SHARED))
+ if (pirq_penalty[i] < pirq_penalty[newirq] &&
+ can_request_irq(i, IRQF_SHARED))
newirq = i;
}
}
@@ -949,12 +985,13 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
irq = pirq & 0xf;
DBG(" -> hardcoded IRQ %d\n", irq);
msg = "Hardcoded";
- } else if ( r->get && (irq = r->get(pirq_router_dev, dev, pirq)) && \
- ((!(pci_probe & PCI_USE_PIRQ_MASK)) || ((1 << irq) & mask)) ) {
+ } else if (r->get && (irq = r->get(pirq_router_dev, dev, pirq)) && \
+ ((!(pci_probe & PCI_USE_PIRQ_MASK)) || ((1 << irq) & mask))) {
DBG(" -> got IRQ %d\n", irq);
msg = "Found";
eisa_set_level_irq(irq);
- } else if (newirq && r->set && (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) {
+ } else if (newirq && r->set &&
+ (dev->class >> 8) != PCI_CLASS_DISPLAY_VGA) {
DBG(" -> assigning IRQ %d", newirq);
if (r->set(pirq_router_dev, dev, pirq, newirq)) {
eisa_set_level_irq(newirq);
@@ -972,7 +1009,8 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
} else
return 0;
}
- printk(KERN_INFO "PCI: %s IRQ %d for device %s\n", msg, irq, pci_name(dev));
+ printk(KERN_INFO "PCI: %s IRQ %d for device %s\n", msg, irq,
+ pci_name(dev));
/* Update IRQ for all devices with the same pirq value */
while ((dev2 = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev2)) != NULL) {
@@ -984,20 +1022,26 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign)
if (!info)
continue;
if (info->irq[pin].link == pirq) {
- /* We refuse to override the dev->irq information. Give a warning! */
- if ( dev2->irq && dev2->irq != irq && \
+ /*
+ * We refuse to override the dev->irq
+ * information. Give a warning!
+ */
+ if (dev2->irq && dev2->irq != irq && \
(!(pci_probe & PCI_USE_PIRQ_MASK) || \
- ((1 << dev2->irq) & mask)) ) {
+ ((1 << dev2->irq) & mask))) {
#ifndef CONFIG_PCI_MSI
- printk(KERN_INFO "IRQ routing conflict for %s, have irq %d, want irq %d\n",
- pci_name(dev2), dev2->irq, irq);
+ printk(KERN_INFO
+ "IRQ routing conflict for %s, have irq %d, want irq %d\n",
+ pci_name(dev2), dev2->irq, irq);
#endif
- continue;
- }
+ continue;
+ }
dev2->irq = irq;
pirq_penalty[irq]++;
if (dev != dev2)
- printk(KERN_INFO "PCI: Sharing IRQ %d with %s\n", irq, pci_name(dev2));
+ printk(KERN_INFO
+ "PCI: Sharing IRQ %d with %s\n",
+ irq, pci_name(dev2));
}
}
return 1;
@@ -1011,15 +1055,21 @@ static void __init pcibios_fixup_irqs(void)
DBG(KERN_DEBUG "PCI: IRQ fixup\n");
while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
/*
- * If the BIOS has set an out of range IRQ number, just ignore it.
- * Also keep track of which IRQ's are already in use.
+ * If the BIOS has set an out of range IRQ number, just
+ * ignore it. Also keep track of which IRQ's are
+ * already in use.
*/
if (dev->irq >= 16) {
- DBG(KERN_DEBUG "%s: ignoring bogus IRQ %d\n", pci_name(dev), dev->irq);
+ DBG(KERN_DEBUG "%s: ignoring bogus IRQ %d\n",
+ pci_name(dev), dev->irq);
dev->irq = 0;
}
- /* If the IRQ is already assigned to a PCI device, ignore its ISA use penalty */
- if (pirq_penalty[dev->irq] >= 100 && pirq_penalty[dev->irq] < 100000)
+ /*
+ * If the IRQ is already assigned to a PCI device,
+ * ignore its ISA use penalty
+ */
+ if (pirq_penalty[dev->irq] >= 100 &&
+ pirq_penalty[dev->irq] < 100000)
pirq_penalty[dev->irq] = 0;
pirq_penalty[dev->irq]++;
}
@@ -1031,31 +1081,40 @@ static void __init pcibios_fixup_irqs(void)
/*
* Recalculate IRQ numbers if we use the I/O APIC.
*/
- if (io_apic_assign_pci_irqs)
- {
+ if (io_apic_assign_pci_irqs) {
int irq;
if (pin) {
- pin--; /* interrupt pins are numbered starting from 1 */
- irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin);
+ /*
+ * interrupt pins are numbered starting
+ * from 1
+ */
+ pin--;
+ irq = IO_APIC_get_PCI_irq_vector(dev->bus->number,
+ PCI_SLOT(dev->devfn), pin);
/*
* Busses behind bridges are typically not listed in the MP-table.
* In this case we have to look up the IRQ based on the parent bus,
* parent slot, and pin number. The SMP code detects such bridged
* busses itself so we should get into this branch reliably.
*/
- if (irq < 0 && dev->bus->parent) { /* go back to the bridge */
- struct pci_dev * bridge = dev->bus->self;
+ if (irq < 0 && dev->bus->parent) {
+ /* go back to the bridge */
+ struct pci_dev *bridge = dev->bus->self;
pin = (pin + PCI_SLOT(dev->devfn)) % 4;
- irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number,
- PCI_SLOT(bridge->devfn), pin);
+ irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number,
+ PCI_SLOT(bridge->devfn),
+ pin);
if (irq >= 0)
- printk(KERN_WARNING "PCI: using PPB %s[%c] to get irq %d\n",
- pci_name(bridge), 'A' + pin, irq);
+ printk(KERN_WARNING
+ "PCI: using PPB %s[%c] to get irq %d\n",
+ pci_name(bridge),
+ 'A' + pin, irq);
}
if (irq >= 0) {
- printk(KERN_INFO "PCI->APIC IRQ transform: %s[%c] -> IRQ %d\n",
+ printk(KERN_INFO
+ "PCI->APIC IRQ transform: %s[%c] -> IRQ %d\n",
pci_name(dev), 'A' + pin, irq);
dev->irq = irq;
}
@@ -1078,7 +1137,8 @@ static int __init fix_broken_hp_bios_irq9(const struct dmi_system_id *d)
{
if (!broken_hp_bios_irq9) {
broken_hp_bios_irq9 = 1;
- printk(KERN_INFO "%s detected - fixing broken IRQ routing\n", d->ident);
+ printk(KERN_INFO "%s detected - fixing broken IRQ routing\n",
+ d->ident);
}
return 0;
}
@@ -1091,7 +1151,8 @@ static int __init fix_acer_tm360_irqrouting(const struct dmi_system_id *d)
{
if (!acer_tm360_irqrouting) {
acer_tm360_irqrouting = 1;
- printk(KERN_INFO "%s detected - fixing broken IRQ routing\n", d->ident);
+ printk(KERN_INFO "%s detected - fixing broken IRQ routing\n",
+ d->ident);
}
return 0;
}
@@ -1103,7 +1164,8 @@ static struct dmi_system_id __initdata pciirq_dmi_table[] = {
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_BIOS_VERSION, "GE.M1.03"),
- DMI_MATCH(DMI_PRODUCT_VERSION, "HP Pavilion Notebook Model GE"),
+ DMI_MATCH(DMI_PRODUCT_VERSION,
+ "HP Pavilion Notebook Model GE"),
DMI_MATCH(DMI_BOARD_VERSION, "OmniBook N32N-736"),
},
},
@@ -1138,11 +1200,14 @@ static int __init pcibios_irq_init(void)
pirq_find_router(&pirq_router);
if (pirq_table->exclusive_irqs) {
int i;
- for (i=0; i<16; i++)
+ for (i = 0; i < 16; i++)
if (!(pirq_table->exclusive_irqs & (1 << i)))
pirq_penalty[i] += 100;
}
- /* If we're using the I/O APIC, avoid using the PCI IRQ routing table */
+ /*
+ * If we're using the I/O APIC, avoid using the PCI IRQ
+ * routing table
+ */
if (io_apic_assign_pci_irqs)
pirq_table = NULL;
}
@@ -1189,33 +1254,40 @@ static int pirq_enable_irq(struct pci_dev *dev)
if (pin && !pcibios_lookup_irq(dev, 1) && !dev->irq) {
char *msg = "";
- pin--; /* interrupt pins are numbered starting from 1 */
+ pin--; /* interrupt pins are numbered starting from 1 */
if (io_apic_assign_pci_irqs) {
int irq;
- irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin);
+ irq = IO_APIC_get_PCI_irq_vector(dev->bus->number,
+ PCI_SLOT(dev->devfn), pin);
/*
- * Busses behind bridges are typically not listed in the MP-table.
- * In this case we have to look up the IRQ based on the parent bus,
- * parent slot, and pin number. The SMP code detects such bridged
- * busses itself so we should get into this branch reliably.
+ * Busses behind bridges are typically not
+ * listed in the MP-table. In this case we have
+ * to look up the IRQ based on the parent bus,
+ * parent slot, and pin number. The SMP code
+ * detects such bridged busses itself so we
+ * should get into this branch reliably.
*/
temp_dev = dev;
- while (irq < 0 && dev->bus->parent) { /* go back to the bridge */
- struct pci_dev * bridge = dev->bus->self;
+ while (irq < 0 && dev->bus->parent) {
+ /* go back to the bridge */
+ struct pci_dev *bridge = dev->bus->self;
pin = (pin + PCI_SLOT(dev->devfn)) % 4;
- irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number,
+ irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number,
PCI_SLOT(bridge->devfn), pin);
if (irq >= 0)
- printk(KERN_WARNING "PCI: using PPB %s[%c] to get irq %d\n",
- pci_name(bridge), 'A' + pin, irq);
+ printk(KERN_WARNING
+ "PCI: using PPB %s[%c] to get irq %d\n",
+ pci_name(bridge),
+ 'A' + pin, irq);
dev = bridge;
}
dev = temp_dev;
if (irq >= 0) {
- printk(KERN_INFO "PCI->APIC IRQ transform: %s[%c] -> IRQ %d\n",
+ printk(KERN_INFO
+ "PCI->APIC IRQ transform: %s[%c] -> IRQ %d\n",
pci_name(dev), 'A' + pin, irq);
dev->irq = irq;
return 0;
@@ -1226,12 +1298,17 @@ static int pirq_enable_irq(struct pci_dev *dev)
else
msg = " Please try using pci=biosirq.";
- /* With IDE legacy devices the IRQ lookup failure is not a problem.. */
- if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE && !(dev->class & 0x5))
+ /*
+ * With IDE legacy devices the IRQ lookup failure is not
+ * a problem..
+ */
+ if (dev->class >> 8 == PCI_CLASS_STORAGE_IDE &&
+ !(dev->class & 0x5))
return 0;
- printk(KERN_WARNING "PCI: No IRQ known for interrupt pin %c of device %s.%s\n",
- 'A' + pin, pci_name(dev), msg);
+ printk(KERN_WARNING
+ "PCI: No IRQ known for interrupt pin %c of device %s.%s\n",
+ 'A' + pin, pci_name(dev), msg);
}
return 0;
}
diff --git a/arch/x86/pci/k8-bus_64.c b/arch/x86/pci/k8-bus_64.c
index 5c2799c20e47..bfefdf0f40d4 100644
--- a/arch/x86/pci/k8-bus_64.c
+++ b/arch/x86/pci/k8-bus_64.c
@@ -384,7 +384,7 @@ static int __init early_fill_mp_bus_info(void)
/* need to take out [0, TOM) for RAM*/
address = MSR_K8_TOP_MEM1;
rdmsrl(address, val);
- end = (val & 0xffffff8000000ULL);
+ end = (val & 0xffffff800000ULL);
printk(KERN_INFO "TOM: %016lx aka %ldM\n", end, end>>20);
if (end < (1ULL<<32))
update_range(range, 0, end - 1);
@@ -478,7 +478,7 @@ static int __init early_fill_mp_bus_info(void)
/* TOP_MEM2 */
address = MSR_K8_TOP_MEM2;
rdmsrl(address, val);
- end = (val & 0xffffff8000000ULL);
+ end = (val & 0xffffff800000ULL);
printk(KERN_INFO "TOM2: %016lx aka %ldM\n", end, end>>20);
update_range(range, 1ULL<<32, end - 1);
}
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c
index 0cfebecf2a8f..23faaa890ffc 100644
--- a/arch/x86/pci/mmconfig-shared.c
+++ b/arch/x86/pci/mmconfig-shared.c
@@ -374,7 +374,7 @@ reject:
static int __initdata known_bridge;
-void __init __pci_mmcfg_init(int early)
+static void __init __pci_mmcfg_init(int early)
{
/* MMCONFIG disabled */
if ((pci_probe & PCI_PROBE_MMCONF) == 0)
diff --git a/arch/x86/pci/numa.c b/arch/x86/pci/numa.c
index d9afbae5092b..99f1ecd485b5 100644
--- a/arch/x86/pci/numa.c
+++ b/arch/x86/pci/numa.c
@@ -6,45 +6,21 @@
#include <linux/init.h>
#include <linux/nodemask.h>
#include <mach_apic.h>
+#include <asm/mpspec.h>
#include "pci.h"
#define XQUAD_PORTIO_BASE 0xfe400000
#define XQUAD_PORTIO_QUAD 0x40000 /* 256k per quad. */
-int mp_bus_id_to_node[MAX_MP_BUSSES];
#define BUS2QUAD(global) (mp_bus_id_to_node[global])
-int mp_bus_id_to_local[MAX_MP_BUSSES];
#define BUS2LOCAL(global) (mp_bus_id_to_local[global])
-void mpc_oem_bus_info(struct mpc_config_bus *m, char *name,
- struct mpc_config_translation *translation)
-{
- int quad = translation->trans_quad;
- int local = translation->trans_local;
-
- mp_bus_id_to_node[m->mpc_busid] = quad;
- mp_bus_id_to_local[m->mpc_busid] = local;
- printk(KERN_INFO "Bus #%d is %s (node %d)\n",
- m->mpc_busid, name, quad);
-}
-
-int quad_local_to_mp_bus_id [NR_CPUS/4][4];
#define QUADLOCAL2BUS(quad,local) (quad_local_to_mp_bus_id[quad][local])
-void mpc_oem_pci_bus(struct mpc_config_bus *m,
- struct mpc_config_translation *translation)
-{
- int quad = translation->trans_quad;
- int local = translation->trans_local;
-
- quad_local_to_mp_bus_id[quad][local] = m->mpc_busid;
-}
/* Where the IO area was mapped on multiquad, always 0 otherwise */
void *xquad_portio;
-#ifdef CONFIG_X86_NUMAQ
EXPORT_SYMBOL(xquad_portio);
-#endif
#define XQUAD_PORT_ADDR(port, quad) (xquad_portio + (XQUAD_PORTIO_QUAD*quad) + port)
@@ -179,6 +155,9 @@ static int __init pci_numa_init(void)
{
int quad;
+ if (!found_numaq)
+ return 0;
+
raw_pci_ops = &pci_direct_conf1_mq;
if (pcibios_scanned++)
diff --git a/arch/x86/pci/pci.h b/arch/x86/pci/pci.h
index 720c4c554534..291dafec07b7 100644
--- a/arch/x86/pci/pci.h
+++ b/arch/x86/pci/pci.h
@@ -27,6 +27,7 @@
#define PCI_CAN_SKIP_ISA_ALIGN 0x8000
#define PCI_USE__CRS 0x10000
#define PCI_CHECK_ENABLE_AMD_MMCONF 0x20000
+#define PCI_NOASSIGN_ROMS 0x40000
extern unsigned int pci_probe;
extern unsigned long pirq_table_addr;
diff --git a/arch/x86/vdso/vclock_gettime.c b/arch/x86/vdso/vclock_gettime.c
index efa2ba7c6005..1ef0f90813d6 100644
--- a/arch/x86/vdso/vclock_gettime.c
+++ b/arch/x86/vdso/vclock_gettime.c
@@ -23,7 +23,7 @@
#define gtod vdso_vsyscall_gtod_data
-static long vdso_fallback_gettime(long clock, struct timespec *ts)
+notrace static long vdso_fallback_gettime(long clock, struct timespec *ts)
{
long ret;
asm("syscall" : "=a" (ret) :
@@ -31,7 +31,7 @@ static long vdso_fallback_gettime(long clock, struct timespec *ts)
return ret;
}
-static inline long vgetns(void)
+notrace static inline long vgetns(void)
{
long v;
cycles_t (*vread)(void);
@@ -40,7 +40,7 @@ static inline long vgetns(void)
return (v * gtod->clock.mult) >> gtod->clock.shift;
}
-static noinline int do_realtime(struct timespec *ts)
+notrace static noinline int do_realtime(struct timespec *ts)
{
unsigned long seq, ns;
do {
@@ -54,7 +54,8 @@ static noinline int do_realtime(struct timespec *ts)
}
/* Copy of the version in kernel/time.c which we cannot directly access */
-static void vset_normalized_timespec(struct timespec *ts, long sec, long nsec)
+notrace static void
+vset_normalized_timespec(struct timespec *ts, long sec, long nsec)
{
while (nsec >= NSEC_PER_SEC) {
nsec -= NSEC_PER_SEC;
@@ -68,7 +69,7 @@ static void vset_normalized_timespec(struct timespec *ts, long sec, long nsec)
ts->tv_nsec = nsec;
}
-static noinline int do_monotonic(struct timespec *ts)
+notrace static noinline int do_monotonic(struct timespec *ts)
{
unsigned long seq, ns, secs;
do {
@@ -82,7 +83,7 @@ static noinline int do_monotonic(struct timespec *ts)
return 0;
}
-int __vdso_clock_gettime(clockid_t clock, struct timespec *ts)
+notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts)
{
if (likely(gtod->sysctl_enabled && gtod->clock.vread))
switch (clock) {
@@ -96,7 +97,7 @@ int __vdso_clock_gettime(clockid_t clock, struct timespec *ts)
int clock_gettime(clockid_t, struct timespec *)
__attribute__((weak, alias("__vdso_clock_gettime")));
-int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
+notrace int __vdso_gettimeofday(struct timeval *tv, struct timezone *tz)
{
long ret;
if (likely(gtod->sysctl_enabled && gtod->clock.vread)) {
diff --git a/arch/x86/vdso/vgetcpu.c b/arch/x86/vdso/vgetcpu.c
index c8097f17f8a9..9fbc6b20026b 100644
--- a/arch/x86/vdso/vgetcpu.c
+++ b/arch/x86/vdso/vgetcpu.c
@@ -13,7 +13,8 @@
#include <asm/vgtod.h>
#include "vextern.h"
-long __vdso_getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *unused)
+notrace long
+__vdso_getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *unused)
{
unsigned int p;
diff --git a/arch/x86/vdso/vma.c b/arch/x86/vdso/vma.c
index 3fdd51497a83..19a6cfaf5db9 100644
--- a/arch/x86/vdso/vma.c
+++ b/arch/x86/vdso/vma.c
@@ -16,7 +16,7 @@
#include "vextern.h" /* Just for VMAGIC. */
#undef VEXTERN
-int vdso_enabled = 1;
+unsigned int __read_mostly vdso_enabled = 1;
extern char vdso_start[], vdso_end[];
extern unsigned short vdso_sync_cpuid;
diff --git a/arch/x86/xen/Kconfig b/arch/x86/xen/Kconfig
index 2e641be2737e..d0f1c7c5dc3d 100644
--- a/arch/x86/xen/Kconfig
+++ b/arch/x86/xen/Kconfig
@@ -6,8 +6,18 @@ config XEN
bool "Xen guest support"
select PARAVIRT
depends on X86_32
- depends on X86_CMPXCHG && X86_TSC && !(X86_VISWS || X86_VOYAGER)
+ depends on X86_CMPXCHG && X86_TSC && X86_PAE && !(X86_VISWS || X86_VOYAGER)
help
This is the Linux Xen port. Enabling this will allow the
kernel to boot in a paravirtualized environment under the
Xen hypervisor.
+
+config XEN_MAX_DOMAIN_MEMORY
+ int "Maximum allowed size of a domain in gigabytes"
+ default 8
+ depends on XEN
+ help
+ The pseudo-physical to machine address array is sized
+ according to the maximum possible memory size of a Xen
+ domain. This array uses 1 page per gigabyte, so there's no
+ need to be too stingy here. \ No newline at end of file
diff --git a/arch/x86/xen/Makefile b/arch/x86/xen/Makefile
index 3d8df981d5fd..2ba2d1649131 100644
--- a/arch/x86/xen/Makefile
+++ b/arch/x86/xen/Makefile
@@ -1,4 +1,4 @@
obj-y := enlighten.o setup.o multicalls.o mmu.o \
- time.o manage.o xen-asm.o grant-table.o
+ time.o xen-asm.o grant-table.o suspend.o
obj-$(CONFIG_SMP) += smp.o
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
index c8a56e457d61..6e03b94c69d5 100644
--- a/arch/x86/xen/enlighten.c
+++ b/arch/x86/xen/enlighten.c
@@ -75,13 +75,13 @@ DEFINE_PER_CPU(unsigned long, xen_current_cr3); /* actual vcpu cr3 */
struct start_info *xen_start_info;
EXPORT_SYMBOL_GPL(xen_start_info);
-static /* __initdata */ struct shared_info dummy_shared_info;
+struct shared_info xen_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;
+struct shared_info *HYPERVISOR_shared_info = (void *)&xen_dummy_shared_info;
/*
* Flag to determine whether vcpu info placement is available on all
@@ -98,13 +98,13 @@ struct shared_info *HYPERVISOR_shared_info = (void *)&dummy_shared_info;
*/
static int have_vcpu_info_placement = 1;
-static void __init xen_vcpu_setup(int cpu)
+static void xen_vcpu_setup(int cpu)
{
struct vcpu_register_vcpu_info info;
int err;
struct vcpu_info *vcpup;
- BUG_ON(HYPERVISOR_shared_info == &dummy_shared_info);
+ BUG_ON(HYPERVISOR_shared_info == &xen_dummy_shared_info);
per_cpu(xen_vcpu, cpu) = &HYPERVISOR_shared_info->vcpu_info[cpu];
if (!have_vcpu_info_placement)
@@ -136,6 +136,34 @@ static void __init xen_vcpu_setup(int cpu)
}
}
+/*
+ * On restore, set the vcpu placement up again.
+ * If it fails, then we're in a bad state, since
+ * we can't back out from using it...
+ */
+void xen_vcpu_restore(void)
+{
+ if (have_vcpu_info_placement) {
+ int cpu;
+
+ for_each_online_cpu(cpu) {
+ bool other_cpu = (cpu != smp_processor_id());
+
+ if (other_cpu &&
+ HYPERVISOR_vcpu_op(VCPUOP_down, cpu, NULL))
+ BUG();
+
+ xen_vcpu_setup(cpu);
+
+ if (other_cpu &&
+ HYPERVISOR_vcpu_op(VCPUOP_up, cpu, NULL))
+ BUG();
+ }
+
+ BUG_ON(!have_vcpu_info_placement);
+ }
+}
+
static void __init xen_banner(void)
{
printk(KERN_INFO "Booting paravirtualized kernel on %s\n",
@@ -235,13 +263,13 @@ 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();
+ /* We don't need to worry about being preempted here, since
+ either a) interrupts are disabled, so no preemption, or b)
+ the caller is confused and is trying to re-enable interrupts
+ on an indeterminate processor. */
+
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. */
@@ -254,7 +282,7 @@ static void xen_irq_enable(void)
static void xen_safe_halt(void)
{
/* Blocking includes an implicit local_irq_enable(). */
- if (HYPERVISOR_sched_op(SCHEDOP_block, 0) != 0)
+ if (HYPERVISOR_sched_op(SCHEDOP_block, NULL) != 0)
BUG();
}
@@ -607,6 +635,30 @@ static void xen_flush_tlb_others(const cpumask_t *cpus, struct mm_struct *mm,
xen_mc_issue(PARAVIRT_LAZY_MMU);
}
+static void xen_clts(void)
+{
+ struct multicall_space mcs;
+
+ mcs = xen_mc_entry(0);
+
+ MULTI_fpu_taskswitch(mcs.mc, 0);
+
+ xen_mc_issue(PARAVIRT_LAZY_CPU);
+}
+
+static void xen_write_cr0(unsigned long cr0)
+{
+ struct multicall_space mcs;
+
+ /* Only pay attention to cr0.TS; everything else is
+ ignored. */
+ mcs = xen_mc_entry(0);
+
+ MULTI_fpu_taskswitch(mcs.mc, (cr0 & X86_CR0_TS) != 0);
+
+ xen_mc_issue(PARAVIRT_LAZY_CPU);
+}
+
static void xen_write_cr2(unsigned long cr2)
{
x86_read_percpu(xen_vcpu)->arch.cr2 = cr2;
@@ -624,8 +676,10 @@ static unsigned long xen_read_cr2_direct(void)
static void xen_write_cr4(unsigned long cr4)
{
- /* Just ignore cr4 changes; Xen doesn't allow us to do
- anything anyway. */
+ cr4 &= ~X86_CR4_PGE;
+ cr4 &= ~X86_CR4_PSE;
+
+ native_write_cr4(cr4);
}
static unsigned long xen_read_cr3(void)
@@ -785,38 +839,35 @@ static __init void xen_set_pte_init(pte_t *ptep, pte_t pte)
static __init void xen_pagetable_setup_start(pgd_t *base)
{
pgd_t *xen_pgd = (pgd_t *)xen_start_info->pt_base;
+ int i;
/* special set_pte for pagetable initialization */
pv_mmu_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.
+ * copy top-level of Xen-supplied pagetable into place. This
+ * 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);
+ /*
+ * 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);
+ memcpy(pmd, (void *)pgd_page_vaddr(xen_pgd[i]),
+ PAGE_SIZE);
- make_lowmem_page_readonly(pmd);
+ make_lowmem_page_readonly(pmd);
- set_pgd(&base[i], __pgd(1 + __pa(pmd)));
- } else
- pgd_clear(&base[i]);
- }
+ 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 */
@@ -834,7 +885,7 @@ static __init void xen_pagetable_setup_start(pgd_t *base)
PFN_DOWN(__pa(xen_start_info->pt_base)));
}
-static __init void setup_shared_info(void)
+void xen_setup_shared_info(void)
{
if (!xen_feature(XENFEAT_auto_translated_physmap)) {
unsigned long addr = fix_to_virt(FIX_PARAVIRT_BOOTMAP);
@@ -857,6 +908,8 @@ static __init void setup_shared_info(void)
/* In UP this is as good a place as any to set up shared info */
xen_setup_vcpu_info_placement();
#endif
+
+ xen_setup_mfn_list_list();
}
static __init void xen_pagetable_setup_done(pgd_t *base)
@@ -869,25 +922,23 @@ static __init void xen_pagetable_setup_done(pgd_t *base)
pv_mmu_ops.release_pmd = xen_release_pmd;
pv_mmu_ops.set_pte = xen_set_pte;
- setup_shared_info();
+ xen_setup_shared_info();
/* Actually pin the pagetable down, but we can't set PG_pinned
yet because the page structures don't exist yet. */
- {
- unsigned level;
+ pin_pagetable_pfn(MMUEXT_PIN_L3_TABLE, PFN_DOWN(__pa(base)));
+}
-#ifdef CONFIG_X86_PAE
- level = MMUEXT_PIN_L3_TABLE;
-#else
- level = MMUEXT_PIN_L2_TABLE;
-#endif
+static __init void xen_post_allocator_init(void)
+{
+ pv_mmu_ops.set_pmd = xen_set_pmd;
+ pv_mmu_ops.set_pud = xen_set_pud;
- pin_pagetable_pfn(level, PFN_DOWN(__pa(base)));
- }
+ xen_mark_init_mm_pinned();
}
/* This is called once we have the cpu_possible_map */
-void __init xen_setup_vcpu_info_placement(void)
+void xen_setup_vcpu_info_placement(void)
{
int cpu;
@@ -973,7 +1024,7 @@ static const struct pv_init_ops xen_init_ops __initdata = {
.banner = xen_banner,
.memory_setup = xen_memory_setup,
.arch_setup = xen_arch_setup,
- .post_allocator_init = xen_mark_init_mm_pinned,
+ .post_allocator_init = xen_post_allocator_init,
};
static const struct pv_time_ops xen_time_ops __initdata = {
@@ -991,10 +1042,10 @@ static const struct pv_cpu_ops xen_cpu_ops __initdata = {
.set_debugreg = xen_set_debugreg,
.get_debugreg = xen_get_debugreg,
- .clts = native_clts,
+ .clts = xen_clts,
.read_cr0 = native_read_cr0,
- .write_cr0 = native_write_cr0,
+ .write_cr0 = xen_write_cr0,
.read_cr4 = native_read_cr4,
.read_cr4_safe = native_read_cr4_safe,
@@ -1085,24 +1136,23 @@ static const struct pv_mmu_ops xen_mmu_ops __initdata = {
.set_pte = NULL, /* see xen_pagetable_setup_* */
.set_pte_at = xen_set_pte_at,
- .set_pmd = xen_set_pmd,
+ .set_pmd = xen_set_pmd_hyper,
.pte_val = xen_pte_val,
+ .pte_flags = native_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,
+ .set_pud = xen_set_pud_hyper,
.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,
@@ -1129,11 +1179,13 @@ static const struct smp_ops xen_smp_ops __initdata = {
static void xen_reboot(int reason)
{
+ struct sched_shutdown r = { .reason = reason };
+
#ifdef CONFIG_SMP
smp_send_stop();
#endif
- if (HYPERVISOR_sched_op(SCHEDOP_shutdown, reason))
+ if (HYPERVISOR_sched_op(SCHEDOP_shutdown, &r))
BUG();
}
@@ -1207,10 +1259,11 @@ asmlinkage void __init xen_start_kernel(void)
/* Get mfn list */
if (!xen_feature(XENFEAT_auto_translated_physmap))
- phys_to_machine_mapping = (unsigned long *)xen_start_info->mfn_list;
+ xen_build_dynamic_phys_to_machine();
pgd = (pgd_t *)xen_start_info->pt_base;
+ init_pg_tables_start = __pa(pgd);
init_pg_tables_end = __pa(pgd) + xen_start_info->nr_pt_frames*PAGE_SIZE;
init_mm.pgd = pgd; /* use the Xen pagetables to start */
@@ -1242,9 +1295,12 @@ asmlinkage void __init xen_start_kernel(void)
? __pa(xen_start_info->mod_start) : 0;
boot_params.hdr.ramdisk_size = xen_start_info->mod_len;
- if (!is_initial_xendomain())
+ if (!is_initial_xendomain()) {
+ add_preferred_console("xenboot", 0, NULL);
+ add_preferred_console("tty", 0, NULL);
add_preferred_console("hvc", 0, NULL);
+ }
/* Start the world */
- start_kernel();
+ i386_start_kernel();
}
diff --git a/arch/x86/xen/manage.c b/arch/x86/xen/manage.c
deleted file mode 100644
index aa7af9e6abc0..000000000000
--- a/arch/x86/xen/manage.c
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * 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/x86/xen/mmu.c b/arch/x86/xen/mmu.c
index 3525ef523a74..7c9935858f92 100644
--- a/arch/x86/xen/mmu.c
+++ b/arch/x86/xen/mmu.c
@@ -56,6 +56,131 @@
#include "multicalls.h"
#include "mmu.h"
+#define P2M_ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(unsigned long))
+#define TOP_ENTRIES (MAX_DOMAIN_PAGES / P2M_ENTRIES_PER_PAGE)
+
+/* Placeholder for holes in the address space */
+static unsigned long p2m_missing[P2M_ENTRIES_PER_PAGE]
+ __attribute__((section(".data.page_aligned"))) =
+ { [ 0 ... P2M_ENTRIES_PER_PAGE-1 ] = ~0UL };
+
+ /* Array of pointers to pages containing p2m entries */
+static unsigned long *p2m_top[TOP_ENTRIES]
+ __attribute__((section(".data.page_aligned"))) =
+ { [ 0 ... TOP_ENTRIES - 1] = &p2m_missing[0] };
+
+/* Arrays of p2m arrays expressed in mfns used for save/restore */
+static unsigned long p2m_top_mfn[TOP_ENTRIES]
+ __attribute__((section(".bss.page_aligned")));
+
+static unsigned long p2m_top_mfn_list[
+ PAGE_ALIGN(TOP_ENTRIES / P2M_ENTRIES_PER_PAGE)]
+ __attribute__((section(".bss.page_aligned")));
+
+static inline unsigned p2m_top_index(unsigned long pfn)
+{
+ BUG_ON(pfn >= MAX_DOMAIN_PAGES);
+ return pfn / P2M_ENTRIES_PER_PAGE;
+}
+
+static inline unsigned p2m_index(unsigned long pfn)
+{
+ return pfn % P2M_ENTRIES_PER_PAGE;
+}
+
+/* Build the parallel p2m_top_mfn structures */
+void xen_setup_mfn_list_list(void)
+{
+ unsigned pfn, idx;
+
+ for(pfn = 0; pfn < MAX_DOMAIN_PAGES; pfn += P2M_ENTRIES_PER_PAGE) {
+ unsigned topidx = p2m_top_index(pfn);
+
+ p2m_top_mfn[topidx] = virt_to_mfn(p2m_top[topidx]);
+ }
+
+ for(idx = 0; idx < ARRAY_SIZE(p2m_top_mfn_list); idx++) {
+ unsigned topidx = idx * P2M_ENTRIES_PER_PAGE;
+ p2m_top_mfn_list[idx] = virt_to_mfn(&p2m_top_mfn[topidx]);
+ }
+
+ BUG_ON(HYPERVISOR_shared_info == &xen_dummy_shared_info);
+
+ HYPERVISOR_shared_info->arch.pfn_to_mfn_frame_list_list =
+ virt_to_mfn(p2m_top_mfn_list);
+ HYPERVISOR_shared_info->arch.max_pfn = xen_start_info->nr_pages;
+}
+
+/* Set up p2m_top to point to the domain-builder provided p2m pages */
+void __init xen_build_dynamic_phys_to_machine(void)
+{
+ unsigned long *mfn_list = (unsigned long *)xen_start_info->mfn_list;
+ unsigned long max_pfn = min(MAX_DOMAIN_PAGES, xen_start_info->nr_pages);
+ unsigned pfn;
+
+ for(pfn = 0; pfn < max_pfn; pfn += P2M_ENTRIES_PER_PAGE) {
+ unsigned topidx = p2m_top_index(pfn);
+
+ p2m_top[topidx] = &mfn_list[pfn];
+ }
+}
+
+unsigned long get_phys_to_machine(unsigned long pfn)
+{
+ unsigned topidx, idx;
+
+ if (unlikely(pfn >= MAX_DOMAIN_PAGES))
+ return INVALID_P2M_ENTRY;
+
+ topidx = p2m_top_index(pfn);
+ idx = p2m_index(pfn);
+ return p2m_top[topidx][idx];
+}
+EXPORT_SYMBOL_GPL(get_phys_to_machine);
+
+static void alloc_p2m(unsigned long **pp, unsigned long *mfnp)
+{
+ unsigned long *p;
+ unsigned i;
+
+ p = (void *)__get_free_page(GFP_KERNEL | __GFP_NOFAIL);
+ BUG_ON(p == NULL);
+
+ for(i = 0; i < P2M_ENTRIES_PER_PAGE; i++)
+ p[i] = INVALID_P2M_ENTRY;
+
+ if (cmpxchg(pp, p2m_missing, p) != p2m_missing)
+ free_page((unsigned long)p);
+ else
+ *mfnp = virt_to_mfn(p);
+}
+
+void set_phys_to_machine(unsigned long pfn, unsigned long mfn)
+{
+ unsigned topidx, idx;
+
+ if (unlikely(xen_feature(XENFEAT_auto_translated_physmap))) {
+ BUG_ON(pfn != mfn && mfn != INVALID_P2M_ENTRY);
+ return;
+ }
+
+ if (unlikely(pfn >= MAX_DOMAIN_PAGES)) {
+ BUG_ON(mfn != INVALID_P2M_ENTRY);
+ return;
+ }
+
+ topidx = p2m_top_index(pfn);
+ if (p2m_top[topidx] == p2m_missing) {
+ /* no need to allocate a page to store an invalid entry */
+ if (mfn == INVALID_P2M_ENTRY)
+ return;
+ alloc_p2m(&p2m_top[topidx], &p2m_top_mfn[topidx]);
+ }
+
+ idx = p2m_index(pfn);
+ p2m_top[topidx][idx] = mfn;
+}
+
xmaddr_t arbitrary_virt_to_machine(unsigned long address)
{
unsigned int level;
@@ -98,7 +223,14 @@ void make_lowmem_page_readwrite(void *vaddr)
}
-void xen_set_pmd(pmd_t *ptr, pmd_t val)
+static bool page_pinned(void *ptr)
+{
+ struct page *page = virt_to_page(ptr);
+
+ return PagePinned(page);
+}
+
+void xen_set_pmd_hyper(pmd_t *ptr, pmd_t val)
{
struct multicall_space mcs;
struct mmu_update *u;
@@ -116,6 +248,18 @@ void xen_set_pmd(pmd_t *ptr, pmd_t val)
preempt_enable();
}
+void xen_set_pmd(pmd_t *ptr, pmd_t val)
+{
+ /* If page is not pinned, we can just update the entry
+ directly */
+ if (!page_pinned(ptr)) {
+ *ptr = val;
+ return;
+ }
+
+ xen_set_pmd_hyper(ptr, val);
+}
+
/*
* Associate a virtual page frame with a given physical page frame
* and protection flags for that frame.
@@ -222,8 +366,8 @@ pmdval_t xen_pmd_val(pmd_t pmd)
ret = machine_to_phys(XMADDR(ret)).paddr | _PAGE_PRESENT;
return ret;
}
-#ifdef CONFIG_X86_PAE
-void xen_set_pud(pud_t *ptr, pud_t val)
+
+void xen_set_pud_hyper(pud_t *ptr, pud_t val)
{
struct multicall_space mcs;
struct mmu_update *u;
@@ -241,6 +385,18 @@ void xen_set_pud(pud_t *ptr, pud_t val)
preempt_enable();
}
+void xen_set_pud(pud_t *ptr, pud_t val)
+{
+ /* If page is not pinned, we can just update the entry
+ directly */
+ if (!page_pinned(ptr)) {
+ *ptr = val;
+ return;
+ }
+
+ xen_set_pud_hyper(ptr, val);
+}
+
void xen_set_pte(pte_t *ptep, pte_t pte)
{
ptep->pte_high = pte.pte_high;
@@ -262,7 +418,7 @@ void xen_pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
void xen_pmd_clear(pmd_t *pmdp)
{
- xen_set_pmd(pmdp, __pmd(0));
+ set_pmd(pmdp, __pmd(0));
}
pmd_t xen_make_pmd(pmdval_t pmd)
@@ -272,12 +428,6 @@ pmd_t xen_make_pmd(pmdval_t pmd)
return native_make_pmd(pmd);
}
-#else /* !PAE */
-void xen_set_pte(pte_t *ptep, pte_t pte)
-{
- *ptep = pte;
-}
-#endif /* CONFIG_X86_PAE */
/*
(Yet another) pagetable walker. This one is intended for pinning a
@@ -430,8 +580,6 @@ static int pin_page(struct page *page, enum pt_level level)
read-only, and can be pinned. */
void xen_pgd_pin(pgd_t *pgd)
{
- unsigned level;
-
xen_mc_batch();
if (pgd_walk(pgd, pin_page, TASK_SIZE)) {
@@ -441,15 +589,31 @@ void xen_pgd_pin(pgd_t *pgd)
xen_mc_batch();
}
-#ifdef CONFIG_X86_PAE
- level = MMUEXT_PIN_L3_TABLE;
-#else
- level = MMUEXT_PIN_L2_TABLE;
-#endif
+ xen_do_pin(MMUEXT_PIN_L3_TABLE, PFN_DOWN(__pa(pgd)));
+ xen_mc_issue(0);
+}
- xen_do_pin(level, PFN_DOWN(__pa(pgd)));
+/*
+ * On save, we need to pin all pagetables to make sure they get their
+ * mfns turned into pfns. Search the list for any unpinned pgds and pin
+ * them (unpinned pgds are not currently in use, probably because the
+ * process is under construction or destruction).
+ */
+void xen_mm_pin_all(void)
+{
+ unsigned long flags;
+ struct page *page;
- xen_mc_issue(0);
+ spin_lock_irqsave(&pgd_lock, flags);
+
+ list_for_each_entry(page, &pgd_list, lru) {
+ if (!PagePinned(page)) {
+ xen_pgd_pin((pgd_t *)page_address(page));
+ SetPageSavePinned(page);
+ }
+ }
+
+ spin_unlock_irqrestore(&pgd_lock, flags);
}
/* The init_mm pagetable is really pinned as soon as its created, but
@@ -509,6 +673,29 @@ static void xen_pgd_unpin(pgd_t *pgd)
xen_mc_issue(0);
}
+/*
+ * On resume, undo any pinning done at save, so that the rest of the
+ * kernel doesn't see any unexpected pinned pagetables.
+ */
+void xen_mm_unpin_all(void)
+{
+ unsigned long flags;
+ struct page *page;
+
+ spin_lock_irqsave(&pgd_lock, flags);
+
+ list_for_each_entry(page, &pgd_list, lru) {
+ if (PageSavePinned(page)) {
+ BUG_ON(!PagePinned(page));
+ printk("unpinning pinned %p\n", page_address(page));
+ xen_pgd_unpin((pgd_t *)page_address(page));
+ ClearPageSavePinned(page);
+ }
+ }
+
+ spin_unlock_irqrestore(&pgd_lock, flags);
+}
+
void xen_activate_mm(struct mm_struct *prev, struct mm_struct *next)
{
spin_lock(&next->page_table_lock);
@@ -602,7 +789,7 @@ void xen_exit_mmap(struct mm_struct *mm)
spin_lock(&mm->page_table_lock);
/* pgd may not be pinned in the error exit path of execve */
- if (PagePinned(virt_to_page(mm->pgd)))
+ if (page_pinned(mm->pgd))
xen_pgd_unpin(mm->pgd);
spin_unlock(&mm->page_table_lock);
diff --git a/arch/x86/xen/mmu.h b/arch/x86/xen/mmu.h
index b5e189b1519d..e3dd09e25c63 100644
--- a/arch/x86/xen/mmu.h
+++ b/arch/x86/xen/mmu.h
@@ -25,10 +25,6 @@ enum pt_level {
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);
@@ -37,31 +33,23 @@ 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);
+pteval_t xen_pte_val(pte_t);
+pmdval_t xen_pmd_val(pmd_t);
+pgdval_t 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);
+pte_t xen_make_pte(pteval_t);
+pmd_t xen_make_pmd(pmdval_t);
+pgd_t xen_make_pgd(pgdval_t);
+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_pte_atomic(pte_t *ptep, pte_t pte);
+void xen_set_pmd(pmd_t *pmdp, pmd_t pmdval);
void xen_set_pud(pud_t *ptr, pud_t val);
+void xen_set_pmd_hyper(pmd_t *pmdp, pmd_t pmdval);
+void xen_set_pud_hyper(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/x86/xen/setup.c b/arch/x86/xen/setup.c
index 82517e4a752a..488447878a9d 100644
--- a/arch/x86/xen/setup.c
+++ b/arch/x86/xen/setup.c
@@ -16,6 +16,7 @@
#include <asm/xen/hypervisor.h>
#include <asm/xen/hypercall.h>
+#include <xen/page.h>
#include <xen/interface/callback.h>
#include <xen/interface/physdev.h>
#include <xen/features.h>
@@ -27,8 +28,6 @@
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.
@@ -38,6 +37,8 @@ char * __init xen_memory_setup(void)
{
unsigned long max_pfn = xen_start_info->nr_pages;
+ max_pfn = min(MAX_DOMAIN_PAGES, max_pfn);
+
e820.nr_map = 0;
add_memory_region(0, LOWMEMSIZE(), E820_RAM);
add_memory_region(HIGH_MEMORY, PFN_PHYS(max_pfn)-HIGH_MEMORY, E820_RAM);
diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c
index 94e69000f982..c5b5e39ef287 100644
--- a/arch/x86/xen/smp.c
+++ b/arch/x86/xen/smp.c
@@ -35,7 +35,7 @@
#include "xen-ops.h"
#include "mmu.h"
-static cpumask_t xen_cpu_initialized_map;
+cpumask_t xen_cpu_initialized_map;
static DEFINE_PER_CPU(int, resched_irq) = -1;
static DEFINE_PER_CPU(int, callfunc_irq) = -1;
static DEFINE_PER_CPU(int, debug_irq) = -1;
@@ -65,6 +65,12 @@ static struct call_data_struct *call_data;
*/
static irqreturn_t xen_reschedule_interrupt(int irq, void *dev_id)
{
+#ifdef CONFIG_X86_32
+ __get_cpu_var(irq_stat).irq_resched_count++;
+#else
+ add_pda(irq_resched_count, 1);
+#endif
+
return IRQ_HANDLED;
}
@@ -345,7 +351,7 @@ static void xen_send_IPI_mask(cpumask_t mask, enum ipi_vector vector)
cpus_and(mask, mask, cpu_online_map);
- for_each_cpu_mask(cpu, mask)
+ for_each_cpu_mask_nr(cpu, mask)
xen_send_IPI_one(cpu, vector);
}
@@ -413,7 +419,7 @@ int xen_smp_call_function_mask(cpumask_t mask, void (*func)(void *),
/* Make sure other vcpus get a chance to run if they need to. */
yield = false;
- for_each_cpu_mask(cpu, mask)
+ for_each_cpu_mask_nr(cpu, mask)
if (xen_vcpu_stolen(cpu))
yield = true;
diff --git a/arch/x86/xen/suspend.c b/arch/x86/xen/suspend.c
new file mode 100644
index 000000000000..251669a932d4
--- /dev/null
+++ b/arch/x86/xen/suspend.c
@@ -0,0 +1,45 @@
+#include <linux/types.h>
+
+#include <xen/interface/xen.h>
+#include <xen/grant_table.h>
+#include <xen/events.h>
+
+#include <asm/xen/hypercall.h>
+#include <asm/xen/page.h>
+
+#include "xen-ops.h"
+#include "mmu.h"
+
+void xen_pre_suspend(void)
+{
+ xen_start_info->store_mfn = mfn_to_pfn(xen_start_info->store_mfn);
+ xen_start_info->console.domU.mfn =
+ mfn_to_pfn(xen_start_info->console.domU.mfn);
+
+ BUG_ON(!irqs_disabled());
+
+ HYPERVISOR_shared_info = &xen_dummy_shared_info;
+ if (HYPERVISOR_update_va_mapping(fix_to_virt(FIX_PARAVIRT_BOOTMAP),
+ __pte_ma(0), 0))
+ BUG();
+}
+
+void xen_post_suspend(int suspend_cancelled)
+{
+ xen_setup_shared_info();
+
+ if (suspend_cancelled) {
+ xen_start_info->store_mfn =
+ pfn_to_mfn(xen_start_info->store_mfn);
+ xen_start_info->console.domU.mfn =
+ pfn_to_mfn(xen_start_info->console.domU.mfn);
+ } else {
+#ifdef CONFIG_SMP
+ xen_cpu_initialized_map = cpu_online_map;
+#endif
+ xen_vcpu_restore();
+ xen_timer_resume();
+ }
+
+}
+
diff --git a/arch/x86/xen/time.c b/arch/x86/xen/time.c
index c39e1a5aa241..0da249dbdc49 100644
--- a/arch/x86/xen/time.c
+++ b/arch/x86/xen/time.c
@@ -12,6 +12,7 @@
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/kernel_stat.h>
+#include <linux/math64.h>
#include <asm/xen/hypervisor.h>
#include <asm/xen/hypercall.h>
@@ -150,11 +151,7 @@ static void do_stolen_accounting(void)
if (stolen < 0)
stolen = 0;
- ticks = 0;
- while (stolen >= NS_PER_TICK) {
- ticks++;
- stolen -= NS_PER_TICK;
- }
+ ticks = iter_div_u64_rem(stolen, NS_PER_TICK, &stolen);
__get_cpu_var(residual_stolen) = stolen;
account_steal_time(NULL, ticks);
@@ -166,11 +163,7 @@ static void do_stolen_accounting(void)
if (blocked < 0)
blocked = 0;
- ticks = 0;
- while (blocked >= NS_PER_TICK) {
- ticks++;
- blocked -= NS_PER_TICK;
- }
+ ticks = iter_div_u64_rem(blocked, NS_PER_TICK, &blocked);
__get_cpu_var(residual_blocked) = blocked;
account_steal_time(idle_task(smp_processor_id()), ticks);
}
@@ -572,6 +565,19 @@ void xen_setup_cpu_clockevents(void)
clockevents_register_device(&__get_cpu_var(xen_clock_events));
}
+void xen_timer_resume(void)
+{
+ int cpu;
+
+ if (xen_clockevent != &xen_vcpuop_clockevent)
+ return;
+
+ for_each_online_cpu(cpu) {
+ if (HYPERVISOR_vcpu_op(VCPUOP_stop_periodic_timer, cpu, NULL))
+ BUG();
+ }
+}
+
__init void xen_time_init(void)
{
int cpu = smp_processor_id();
diff --git a/arch/x86/xen/xen-head.S b/arch/x86/xen/xen-head.S
index 288d587ce73c..ef6c9e005f90 100644
--- a/arch/x86/xen/xen-head.S
+++ b/arch/x86/xen/xen-head.S
@@ -7,6 +7,7 @@
#include <linux/init.h>
#include <asm/boot.h>
#include <xen/interface/elfnote.h>
+#include <asm/xen/interface.h>
__INIT
ENTRY(startup_xen)
@@ -30,11 +31,11 @@ ENTRY(hypercall_page)
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")
+ ELFNOTE(Xen, XEN_ELFNOTE_L1_MFN_VALID,
+ .quad _PAGE_PRESENT; .quad _PAGE_PRESENT)
+ ELFNOTE(Xen, XEN_ELFNOTE_SUSPEND_CANCEL, .long 1)
+ ELFNOTE(Xen, XEN_ELFNOTE_HV_START_LOW, .long __HYPERVISOR_VIRT_START)
#endif /*CONFIG_XEN */
diff --git a/arch/x86/xen/xen-ops.h b/arch/x86/xen/xen-ops.h
index f1063ae08037..9a055592a307 100644
--- a/arch/x86/xen/xen-ops.h
+++ b/arch/x86/xen/xen-ops.h
@@ -9,18 +9,26 @@
extern const char xen_hypervisor_callback[];
extern const char xen_failsafe_callback[];
+struct trap_info;
void xen_copy_trap_info(struct trap_info *traps);
DECLARE_PER_CPU(unsigned long, xen_cr3);
DECLARE_PER_CPU(unsigned long, xen_current_cr3);
extern struct start_info *xen_start_info;
+extern struct shared_info xen_dummy_shared_info;
extern struct shared_info *HYPERVISOR_shared_info;
+void xen_setup_mfn_list_list(void);
+void xen_setup_shared_info(void);
+
char * __init xen_memory_setup(void);
void __init xen_arch_setup(void);
void __init xen_init_IRQ(void);
void xen_enable_sysenter(void);
+void xen_vcpu_restore(void);
+
+void __init xen_build_dynamic_phys_to_machine(void);
void xen_setup_timer(int cpu);
void xen_setup_cpu_clockevents(void);
@@ -29,6 +37,7 @@ 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_timer_resume(void);
irqreturn_t xen_debug_interrupt(int irq, void *dev_id);
@@ -54,6 +63,8 @@ int xen_smp_call_function_single(int cpu, void (*func) (void *info), void *info,
int xen_smp_call_function_mask(cpumask_t mask, void (*func)(void *),
void *info, int wait);
+extern cpumask_t xen_cpu_initialized_map;
+
/* Declare an asm function, along with symbols needed to make it
inlineable */
diff --git a/block/blk-core.c b/block/blk-core.c
index 1905aaba49fb..61b8471e014a 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -1042,15 +1042,9 @@ void blk_put_request(struct request *req)
unsigned long flags;
struct request_queue *q = req->q;
- /*
- * Gee, IDE calls in w/ NULL q. Fix IDE and remove the
- * following if (q) test.
- */
- if (q) {
- spin_lock_irqsave(q->queue_lock, flags);
- __blk_put_request(q, req);
- spin_unlock_irqrestore(q->queue_lock, flags);
- }
+ spin_lock_irqsave(q->queue_lock, flags);
+ __blk_put_request(q, req);
+ spin_unlock_irqrestore(q->queue_lock, flags);
}
EXPORT_SYMBOL(blk_put_request);
@@ -2045,7 +2039,7 @@ int __init blk_dev_init(void)
for_each_possible_cpu(i)
INIT_LIST_HEAD(&per_cpu(blk_cpu_done, i));
- open_softirq(BLOCK_SOFTIRQ, blk_done_softirq, NULL);
+ open_softirq(BLOCK_SOFTIRQ, blk_done_softirq);
register_hotcpu_notifier(&blk_cpu_notifier);
return 0;
diff --git a/block/blk-exec.c b/block/blk-exec.c
index 391dd6224890..9bceff7674f2 100644
--- a/block/blk-exec.c
+++ b/block/blk-exec.c
@@ -18,7 +18,7 @@
* @rq: request to complete
* @error: end io status of the request
*/
-void blk_end_sync_rq(struct request *rq, int error)
+static void blk_end_sync_rq(struct request *rq, int error)
{
struct completion *waiting = rq->end_io_data;
@@ -31,7 +31,6 @@ void blk_end_sync_rq(struct request *rq, int error)
*/
complete(waiting);
}
-EXPORT_SYMBOL(blk_end_sync_rq);
/**
* blk_execute_rq_nowait - insert a request into queue for execution
@@ -58,6 +57,9 @@ void blk_execute_rq_nowait(struct request_queue *q, struct gendisk *bd_disk,
spin_lock_irq(q->queue_lock);
__elv_add_request(q, rq, where, 1);
__generic_unplug_device(q);
+ /* the queue is stopped so it won't be plugged+unplugged */
+ if (blk_pm_resume_request(rq))
+ q->request_fn(q);
spin_unlock_irq(q->queue_lock);
}
EXPORT_SYMBOL_GPL(blk_execute_rq_nowait);
diff --git a/block/blktrace.c b/block/blktrace.c
index 7ae87cc4a163..8d3a27780260 100644
--- a/block/blktrace.c
+++ b/block/blktrace.c
@@ -79,16 +79,17 @@ void __trace_note_message(struct blk_trace *bt, const char *fmt, ...)
{
int n;
va_list args;
+ unsigned long flags;
char *buf;
- preempt_disable();
+ local_irq_save(flags);
buf = per_cpu_ptr(bt->msg_data, smp_processor_id());
va_start(args, fmt);
n = vscnprintf(buf, BLK_TN_MAX_MSG, fmt, args);
va_end(args);
trace_note(bt, 0, BLK_TN_MESSAGE, buf, n);
- preempt_enable();
+ local_irq_restore(flags);
}
EXPORT_SYMBOL_GPL(__trace_note_message);
@@ -158,10 +159,7 @@ void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes,
/*
* A word about the locking here - we disable interrupts to reserve
* some space in the relay per-cpu buffer, to prevent an irq
- * from coming in and stepping on our toes. Once reserved, it's
- * enough to get preemption disabled to prevent read of this data
- * before we are through filling it. get_cpu()/put_cpu() does this
- * for us
+ * from coming in and stepping on our toes.
*/
local_irq_save(flags);
diff --git a/block/bsg.c b/block/bsg.c
index f0b7cd343216..cfcf73706dcb 100644
--- a/block/bsg.c
+++ b/block/bsg.c
@@ -724,8 +724,13 @@ static int bsg_put_device(struct bsg_device *bd)
mutex_lock(&bsg_mutex);
do_free = atomic_dec_and_test(&bd->ref_count);
- if (!do_free)
+ if (!do_free) {
+ mutex_unlock(&bsg_mutex);
goto out;
+ }
+
+ hlist_del(&bd->dev_list);
+ mutex_unlock(&bsg_mutex);
dprintk("%s: tearing down\n", bd->name);
@@ -741,10 +746,8 @@ static int bsg_put_device(struct bsg_device *bd)
*/
ret = bsg_complete_all_commands(bd);
- hlist_del(&bd->dev_list);
kfree(bd);
out:
- mutex_unlock(&bsg_mutex);
kref_put(&q->bsg_dev.ref, bsg_kref_release_function);
if (do_free)
blk_put_queue(q);
@@ -1019,7 +1022,8 @@ int bsg_register_queue(struct request_queue *q, struct device *parent,
bcd->release = release;
kref_init(&bcd->ref);
dev = MKDEV(bsg_major, bcd->minor);
- class_dev = device_create(bsg_class, parent, dev, "%s", devname);
+ class_dev = device_create_drvdata(bsg_class, parent, dev, NULL,
+ "%s", devname);
if (IS_ERR(class_dev)) {
ret = PTR_ERR(class_dev);
goto put_dev;
diff --git a/block/genhd.c b/block/genhd.c
index b922d4801c87..b4b5f4f594bf 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -183,6 +183,7 @@ static int exact_lock(dev_t devt, void *data)
void add_disk(struct gendisk *disk)
{
struct backing_dev_info *bdi;
+ int retval;
disk->flags |= GENHD_FL_UP;
blk_register_region(MKDEV(disk->major, disk->first_minor),
@@ -192,7 +193,8 @@ void add_disk(struct gendisk *disk)
bdi = &disk->queue->backing_dev_info;
bdi_register_dev(bdi, MKDEV(disk->major, disk->first_minor));
- sysfs_create_link(&disk->dev.kobj, &bdi->dev->kobj, "bdi");
+ retval = sysfs_create_link(&disk->dev.kobj, &bdi->dev->kobj, "bdi");
+ WARN_ON(retval);
}
EXPORT_SYMBOL(add_disk);
@@ -223,89 +225,111 @@ struct gendisk *get_gendisk(dev_t devt, int *part)
}
/*
- * print a full list of all partitions - intended for places where the root
- * filesystem can't be mounted and thus to give the victim some idea of what
- * went wrong
+ * print a partitions - intended for places where the root filesystem can't be
+ * mounted and thus to give the victim some idea of what went wrong
*/
-void __init printk_all_partitions(void)
+static int printk_partition(struct device *dev, void *data)
{
- struct device *dev;
struct gendisk *sgp;
char buf[BDEVNAME_SIZE];
int n;
- mutex_lock(&block_class_lock);
- /* For each block device... */
- list_for_each_entry(dev, &block_class.devices, node) {
- if (dev->type != &disk_type)
- continue;
- sgp = dev_to_disk(dev);
- /*
- * Don't show empty devices or things that have been surpressed
- */
- if (get_capacity(sgp) == 0 ||
- (sgp->flags & GENHD_FL_SUPPRESS_PARTITION_INFO))
- continue;
+ if (dev->type != &disk_type)
+ goto exit;
- /*
- * Note, unlike /proc/partitions, I am showing the numbers in
- * hex - the same format as the root= option takes.
- */
- printk("%02x%02x %10llu %s",
- sgp->major, sgp->first_minor,
- (unsigned long long)get_capacity(sgp) >> 1,
- disk_name(sgp, 0, buf));
- if (sgp->driverfs_dev != NULL &&
- sgp->driverfs_dev->driver != NULL)
- printk(" driver: %s\n",
- sgp->driverfs_dev->driver->name);
- else
- printk(" (driver?)\n");
-
- /* now show the partitions */
- for (n = 0; n < sgp->minors - 1; ++n) {
- if (sgp->part[n] == NULL)
- continue;
- if (sgp->part[n]->nr_sects == 0)
- continue;
- printk(" %02x%02x %10llu %s\n",
- sgp->major, n + 1 + sgp->first_minor,
- (unsigned long long)sgp->part[n]->nr_sects >> 1,
- disk_name(sgp, n + 1, buf));
- }
+ sgp = dev_to_disk(dev);
+ /*
+ * Don't show empty devices or things that have been surpressed
+ */
+ if (get_capacity(sgp) == 0 ||
+ (sgp->flags & GENHD_FL_SUPPRESS_PARTITION_INFO))
+ goto exit;
+
+ /*
+ * Note, unlike /proc/partitions, I am showing the numbers in
+ * hex - the same format as the root= option takes.
+ */
+ printk("%02x%02x %10llu %s",
+ sgp->major, sgp->first_minor,
+ (unsigned long long)get_capacity(sgp) >> 1,
+ disk_name(sgp, 0, buf));
+ if (sgp->driverfs_dev != NULL &&
+ sgp->driverfs_dev->driver != NULL)
+ printk(" driver: %s\n",
+ sgp->driverfs_dev->driver->name);
+ else
+ printk(" (driver?)\n");
+
+ /* now show the partitions */
+ for (n = 0; n < sgp->minors - 1; ++n) {
+ if (sgp->part[n] == NULL)
+ goto exit;
+ if (sgp->part[n]->nr_sects == 0)
+ goto exit;
+ printk(" %02x%02x %10llu %s\n",
+ sgp->major, n + 1 + sgp->first_minor,
+ (unsigned long long)sgp->part[n]->nr_sects >> 1,
+ disk_name(sgp, n + 1, buf));
}
+exit:
+ return 0;
+}
+/*
+ * print a full list of all partitions - intended for places where the root
+ * filesystem can't be mounted and thus to give the victim some idea of what
+ * went wrong
+ */
+void __init printk_all_partitions(void)
+{
+ mutex_lock(&block_class_lock);
+ class_for_each_device(&block_class, NULL, NULL, printk_partition);
mutex_unlock(&block_class_lock);
}
#ifdef CONFIG_PROC_FS
/* iterator */
+static int find_start(struct device *dev, void *data)
+{
+ loff_t k = *(loff_t *)data;
+
+ if (dev->type != &disk_type)
+ return 0;
+ if (!k--)
+ return 1;
+ return 0;
+}
+
static void *part_start(struct seq_file *part, loff_t *pos)
{
- loff_t k = *pos;
struct device *dev;
+ loff_t n = *pos;
+
+ if (!n)
+ seq_puts(part, "major minor #blocks name\n\n");
mutex_lock(&block_class_lock);
- list_for_each_entry(dev, &block_class.devices, node) {
- if (dev->type != &disk_type)
- continue;
- if (!k--)
- return dev_to_disk(dev);
- }
+ dev = class_find_device(&block_class, NULL, (void *)pos, find_start);
+ if (dev)
+ return dev_to_disk(dev);
return NULL;
}
+static int find_next(struct device *dev, void *data)
+{
+ if (dev->type == &disk_type)
+ return 1;
+ return 0;
+}
+
static void *part_next(struct seq_file *part, void *v, loff_t *pos)
{
struct gendisk *gp = v;
struct device *dev;
++*pos;
- list_for_each_entry(dev, &gp->dev.node, node) {
- if (&dev->node == &block_class.devices)
- return NULL;
- if (dev->type == &disk_type)
- return dev_to_disk(dev);
- }
+ dev = class_find_device(&block_class, &gp->dev, NULL, find_next);
+ if (dev)
+ return dev_to_disk(dev);
return NULL;
}
@@ -320,9 +344,6 @@ static int show_partition(struct seq_file *part, void *v)
int n;
char buf[BDEVNAME_SIZE];
- if (&sgp->dev.node == block_class.devices.next)
- seq_puts(part, "major minor #blocks name\n\n");
-
/* Don't show non-partitionable removeable devices or empty devices */
if (!get_capacity(sgp) ||
(sgp->minors == 1 && (sgp->flags & GENHD_FL_REMOVABLE)))
@@ -368,7 +389,10 @@ static struct kobject *base_probe(dev_t devt, int *part, void *data)
static int __init genhd_device_init(void)
{
- int error = class_register(&block_class);
+ int error;
+
+ block_class.dev_kobj = sysfs_dev_block_kobj;
+ error = class_register(&block_class);
if (unlikely(error))
return error;
bdev_map = kobj_map_init(base_probe, &block_class_lock);
@@ -520,6 +544,7 @@ static struct device_type disk_type = {
.release = disk_release,
};
+#ifdef CONFIG_PROC_FS
/*
* aggregate disk stat collector. Uses the same stats that the sysfs
* entries do, above, but makes them available through one seq_file.
@@ -530,16 +555,12 @@ static struct device_type disk_type = {
static void *diskstats_start(struct seq_file *part, loff_t *pos)
{
- loff_t k = *pos;
struct device *dev;
mutex_lock(&block_class_lock);
- list_for_each_entry(dev, &block_class.devices, node) {
- if (dev->type != &disk_type)
- continue;
- if (!k--)
- return dev_to_disk(dev);
- }
+ dev = class_find_device(&block_class, NULL, (void *)pos, find_start);
+ if (dev)
+ return dev_to_disk(dev);
return NULL;
}
@@ -549,12 +570,9 @@ static void *diskstats_next(struct seq_file *part, void *v, loff_t *pos)
struct device *dev;
++*pos;
- list_for_each_entry(dev, &gp->dev.node, node) {
- if (&dev->node == &block_class.devices)
- return NULL;
- if (dev->type == &disk_type)
- return dev_to_disk(dev);
- }
+ dev = class_find_device(&block_class, &gp->dev, NULL, find_next);
+ if (dev)
+ return dev_to_disk(dev);
return NULL;
}
@@ -629,6 +647,7 @@ const struct seq_operations diskstats_op = {
.stop = diskstats_stop,
.show = diskstats_show
};
+#endif /* CONFIG_PROC_FS */
static void media_change_notify_thread(struct work_struct *work)
{
@@ -653,24 +672,38 @@ void genhd_media_change_notify(struct gendisk *disk)
EXPORT_SYMBOL_GPL(genhd_media_change_notify);
#endif /* 0 */
+struct find_block {
+ const char *name;
+ int part;
+};
+
+static int match_id(struct device *dev, void *data)
+{
+ struct find_block *find = data;
+
+ if (dev->type != &disk_type)
+ return 0;
+ if (strcmp(dev->bus_id, find->name) == 0) {
+ struct gendisk *disk = dev_to_disk(dev);
+ if (find->part < disk->minors)
+ return 1;
+ }
+ return 0;
+}
+
dev_t blk_lookup_devt(const char *name, int part)
{
struct device *dev;
dev_t devt = MKDEV(0, 0);
+ struct find_block find;
mutex_lock(&block_class_lock);
- list_for_each_entry(dev, &block_class.devices, node) {
- if (dev->type != &disk_type)
- continue;
- if (strcmp(dev->bus_id, name) == 0) {
- struct gendisk *disk = dev_to_disk(dev);
-
- if (part < disk->minors)
- devt = MKDEV(MAJOR(dev->devt),
- MINOR(dev->devt) + part);
- break;
- }
- }
+ find.name = name;
+ find.part = part;
+ dev = class_find_device(&block_class, NULL, (void *)&find, match_id);
+ if (dev)
+ devt = MKDEV(MAJOR(dev->devt),
+ MINOR(dev->devt) + part);
mutex_unlock(&block_class_lock);
return devt;
diff --git a/crypto/async_tx/async_tx.c b/crypto/async_tx/async_tx.c
index c6e772fc5ccd..095c798d3170 100644
--- a/crypto/async_tx/async_tx.c
+++ b/crypto/async_tx/async_tx.c
@@ -23,6 +23,7 @@
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
+#include <linux/rculist.h>
#include <linux/kernel.h>
#include <linux/async_tx.h>
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 59f33fa6af3e..9b399a1bae8b 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -78,6 +78,8 @@ source "drivers/hid/Kconfig"
source "drivers/usb/Kconfig"
+source "drivers/uwb/Kconfig"
+
source "drivers/mmc/Kconfig"
source "drivers/memstick/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index f65deda72d61..81eb33d616ae 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/
obj-$(CONFIG_PARIDE) += block/paride/
obj-$(CONFIG_TC) += tc/
obj-$(CONFIG_USB) += usb/
+obj-$(CONFIG_UWB) += uwb/
obj-$(CONFIG_PCI) += usb/
obj-$(CONFIG_USB_GADGET) += usb/gadget/
obj-$(CONFIG_SERIO) += input/serio/
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index c52fca833268..6807c2acd977 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -4,7 +4,6 @@
menuconfig ACPI
bool "ACPI (Advanced Configuration and Power Interface) Support"
- depends on !X86_NUMAQ
depends on !X86_VISWS
depends on !IA64_HP_SIM
depends on IA64 || X86
@@ -338,6 +337,15 @@ config ACPI_EC
the battery and thermal drivers. If you are compiling for a
mobile system, say Y.
+config ACPI_PCI_SLOT
+ tristate "PCI slot detection driver"
+ default n
+ help
+ This driver will attempt to discover all PCI slots in your system,
+ and creates entries in /sys/bus/pci/slots/. This feature can
+ help you correlate PCI bus addresses with the physical geography
+ of your slots. If you are unsure, say N.
+
config ACPI_POWER
bool
default y
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 40b0fcae4c78..579c29c0665f 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_ACPI_DOCK) += dock.o
obj-$(CONFIG_ACPI_BAY) += bay.o
obj-$(CONFIG_ACPI_VIDEO) += video.o
obj-y += pci_root.o pci_link.o pci_irq.o pci_bind.o
+obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o
obj-$(CONFIG_ACPI_POWER) += power.o
obj-$(CONFIG_ACPI_PROCESSOR) += processor.o
obj-$(CONFIG_ACPI_CONTAINER) += container.o
diff --git a/drivers/acpi/bay.c b/drivers/acpi/bay.c
index d2fc94161848..26038c2a2a71 100644
--- a/drivers/acpi/bay.c
+++ b/drivers/acpi/bay.c
@@ -301,16 +301,20 @@ static int bay_add(acpi_handle handle, int id)
*/
pdev->dev.uevent_suppress = 0;
- if (acpi_bay_add_fs(new_bay)) {
- platform_device_unregister(new_bay->pdev);
- goto bay_add_err;
- }
-
/* register for events on this device */
status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
bay_notify, new_bay);
if (ACPI_FAILURE(status)) {
- printk(KERN_ERR PREFIX "Error installing bay notify handler\n");
+ printk(KERN_INFO PREFIX "Error installing bay notify handler\n");
+ platform_device_unregister(new_bay->pdev);
+ goto bay_add_err;
+ }
+
+ if (acpi_bay_add_fs(new_bay)) {
+ acpi_remove_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
+ bay_notify);
+ platform_device_unregister(new_bay->pdev);
+ goto bay_add_err;
}
/* if we are on a dock station, we should register for dock
diff --git a/drivers/acpi/dispatcher/dsfield.c b/drivers/acpi/dispatcher/dsfield.c
index c78078315be9..f988a5e7d2b4 100644
--- a/drivers/acpi/dispatcher/dsfield.c
+++ b/drivers/acpi/dispatcher/dsfield.c
@@ -450,10 +450,6 @@ acpi_ds_init_field_objects(union acpi_parse_object *op,
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
- if (!arg) {
- return_ACPI_STATUS(AE_AML_NO_OPERAND);
- }
-
/* Creating new namespace node(s), should not already exist */
flags = ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE |
@@ -467,6 +463,7 @@ acpi_ds_init_field_objects(union acpi_parse_object *op,
/*
* Walk the list of entries in the field_list
+ * Note: field_list can be of zero length. In this case, Arg will be NULL.
*/
while (arg) {
/*
diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c
index fa44fb96fc34..96c542f7fded 100644
--- a/drivers/acpi/dock.c
+++ b/drivers/acpi/dock.c
@@ -834,7 +834,7 @@ static int dock_add(acpi_handle handle)
goto dock_add_err;
}
- printk(KERN_INFO PREFIX "%s \n", ACPI_DOCK_DRIVER_DESCRIPTION);
+ printk(KERN_INFO PREFIX "%s\n", ACPI_DOCK_DRIVER_DESCRIPTION);
return 0;
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 0924992187e8..5622aee996b2 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -194,7 +194,7 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll)
while (time_before(jiffies, delay)) {
if (acpi_ec_check_status(ec, event))
return 0;
- udelay(ACPI_EC_UDELAY);
+ msleep(1);
}
}
pr_err(PREFIX "acpi_ec_wait timeout, status = 0x%2.2x, event = %s\n",
diff --git a/drivers/acpi/executer/exconfig.c b/drivers/acpi/executer/exconfig.c
index 24da921d13e3..39d742190584 100644
--- a/drivers/acpi/executer/exconfig.c
+++ b/drivers/acpi/executer/exconfig.c
@@ -375,9 +375,15 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
goto cleanup;
}
+ /*
+ * Add the table to the namespace.
+ *
+ * Note: We load the table objects relative to the root of the namespace.
+ * This appears to go against the ACPI specification, but we do it for
+ * compatibility with other ACPI implementations.
+ */
status =
- acpi_ex_add_table(table_index, walk_state->scope_info->scope.node,
- &ddb_handle);
+ acpi_ex_add_table(table_index, acpi_gbl_root_node, &ddb_handle);
if (ACPI_FAILURE(status)) {
/* On error, table_ptr was deallocated above */
diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
index 6cf10cbc1eee..f1112362eaa9 100644
--- a/drivers/acpi/fan.c
+++ b/drivers/acpi/fan.c
@@ -257,22 +257,22 @@ static int acpi_fan_add(struct acpi_device *device)
goto end;
}
- printk(KERN_INFO PREFIX
- "%s is registered as cooling_device%d\n",
- device->dev.bus_id, cdev->id);
+ dev_info(&device->dev, "registered as cooling_device%d\n", cdev->id);
acpi_driver_data(device) = cdev;
result = sysfs_create_link(&device->dev.kobj,
&cdev->device.kobj,
"thermal_cooling");
if (result)
- printk(KERN_ERR PREFIX "Create sysfs link\n");
+ dev_err(&device->dev, "Failed to create sysfs link "
+ "'thermal_cooling'\n");
result = sysfs_create_link(&cdev->device.kobj,
&device->dev.kobj,
"device");
if (result)
- printk(KERN_ERR PREFIX "Create sysfs link\n");
+ dev_err(&device->dev, "Failed to create sysfs link "
+ "'device'\n");
result = acpi_fan_add_fs(device);
if (result)
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c
index 06f8634fe58b..3c0443f5949a 100644
--- a/drivers/acpi/glue.c
+++ b/drivers/acpi/glue.c
@@ -146,8 +146,7 @@ static int acpi_bind_one(struct device *dev, acpi_handle handle)
acpi_status status;
if (dev->archdata.acpi_handle) {
- printk(KERN_WARNING PREFIX
- "Drivers changed 'acpi_handle' for %s\n", dev->bus_id);
+ dev_warn(dev, "Drivers changed 'acpi_handle'\n");
return -EINVAL;
}
get_device(dev);
@@ -193,8 +192,7 @@ static int acpi_unbind_one(struct device *dev)
/* acpi_bind_one increase refcnt by one */
put_device(dev);
} else {
- printk(KERN_ERR PREFIX
- "Oops, 'acpi_handle' corrupt for %s\n", dev->bus_id);
+ dev_err(dev, "Oops, 'acpi_handle' corrupt\n");
}
return 0;
}
@@ -272,6 +270,12 @@ static u32 rtc_handler(void *context)
static inline void rtc_wake_setup(void)
{
acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL);
+ /*
+ * After the RTC handler is installed, the Fixed_RTC event should
+ * be disabled. Only when the RTC alarm is set will it be enabled.
+ */
+ acpi_clear_event(ACPI_EVENT_RTC);
+ acpi_disable_event(ACPI_EVENT_RTC, 0);
}
static void rtc_wake_on(struct device *dev)
diff --git a/drivers/acpi/hardware/hwsleep.c b/drivers/acpi/hardware/hwsleep.c
index d9937e05ec6a..dba3cfbe8cba 100644
--- a/drivers/acpi/hardware/hwsleep.c
+++ b/drivers/acpi/hardware/hwsleep.c
@@ -223,15 +223,17 @@ acpi_status acpi_enter_sleep_state_prep(u8 sleep_state)
break;
}
- /* Set the system indicators to show the desired sleep state. */
-
+ /*
+ * Set the system indicators to show the desired sleep state.
+ * _SST is an optional method (return no error if not found)
+ */
status = acpi_evaluate_object(NULL, METHOD_NAME__SST, &arg_list, NULL);
if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) {
ACPI_EXCEPTION((AE_INFO, status,
"While executing method _SST"));
}
- return_ACPI_STATUS(status);
+ return_ACPI_STATUS(AE_OK);
}
ACPI_EXPORT_SYMBOL(acpi_enter_sleep_state_prep)
diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c
index 5d59cb33b1a5..658e5f3abae0 100644
--- a/drivers/acpi/numa.c
+++ b/drivers/acpi/numa.c
@@ -140,19 +140,42 @@ acpi_table_print_srat_entry(struct acpi_subtable_header *header)
}
}
+/*
+ * A lot of BIOS fill in 10 (= no distance) everywhere. This messes
+ * up the NUMA heuristics which wants the local node to have a smaller
+ * distance than the others.
+ * Do some quick checks here and only use the SLIT if it passes.
+ */
+static __init int slit_valid(struct acpi_table_slit *slit)
+{
+ int i, j;
+ int d = slit->locality_count;
+ for (i = 0; i < d; i++) {
+ for (j = 0; j < d; j++) {
+ u8 val = slit->entry[d*i + j];
+ if (i == j) {
+ if (val != LOCAL_DISTANCE)
+ return 0;
+ } else if (val <= LOCAL_DISTANCE)
+ return 0;
+ }
+ }
+ return 1;
+}
+
static int __init acpi_parse_slit(struct acpi_table_header *table)
{
struct acpi_table_slit *slit;
- u32 localities;
if (!table)
return -EINVAL;
slit = (struct acpi_table_slit *)table;
- /* downcast just for %llu vs %lu for i386/ia64 */
- localities = (u32) slit->locality_count;
-
+ if (!slit_valid(slit)) {
+ printk(KERN_INFO "ACPI: SLIT table looks invalid. Not used.\n");
+ return -EINVAL;
+ }
acpi_numa_slit_init(slit);
return 0;
diff --git a/drivers/acpi/parser/psargs.c b/drivers/acpi/parser/psargs.c
index f1e8bf65e24e..e94463778845 100644
--- a/drivers/acpi/parser/psargs.c
+++ b/drivers/acpi/parser/psargs.c
@@ -268,7 +268,7 @@ acpi_ps_get_next_namepath(struct acpi_walk_state *walk_state,
*/
if (ACPI_SUCCESS(status) &&
possible_method_call && (node->type == ACPI_TYPE_METHOD)) {
- if (walk_state->op->common.aml_opcode == AML_UNLOAD_OP) {
+ if (walk_state->opcode == AML_UNLOAD_OP) {
/*
* acpi_ps_get_next_namestring has increased the AML pointer,
* so we need to restore the saved AML pointer for method call.
@@ -691,7 +691,7 @@ acpi_ps_get_next_arg(struct acpi_walk_state *walk_state,
/* To support super_name arg of Unload */
- if (walk_state->op->common.aml_opcode == AML_UNLOAD_OP) {
+ if (walk_state->opcode == AML_UNLOAD_OP) {
status =
acpi_ps_get_next_namepath(walk_state,
parser_state, arg,
diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c
index 89022a74faee..e556f30c7c16 100644
--- a/drivers/acpi/pci_irq.c
+++ b/drivers/acpi/pci_irq.c
@@ -570,6 +570,11 @@ int acpi_pci_irq_enable(struct pci_dev *dev)
(triggering == ACPI_LEVEL_SENSITIVE) ? "level" : "edge",
(polarity == ACPI_ACTIVE_LOW) ? "low" : "high", dev->irq);
+#ifdef CONFIG_X86
+ mp_config_acpi_gsi(dev->bus->number, dev->devfn, dev->pin, irq,
+ triggering, polarity);
+#endif
+
return 0;
}
diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c
new file mode 100644
index 000000000000..b9ab030a52d5
--- /dev/null
+++ b/drivers/acpi/pci_slot.c
@@ -0,0 +1,368 @@
+/*
+ * pci_slot.c - ACPI PCI Slot Driver
+ *
+ * The code here is heavily leveraged from the acpiphp module.
+ * Thanks to Matthew Wilcox <matthew@wil.cx> for much guidance.
+ * Thanks to Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com> for code
+ * review and fixes.
+ *
+ * Copyright (C) 2007 Alex Chiang <achiang@hp.com>
+ * Copyright (C) 2007 Hewlett-Packard Development Company, L.P.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * 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.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+
+static int debug;
+static int check_sta_before_sun;
+
+#define DRIVER_VERSION "0.1"
+#define DRIVER_AUTHOR "Alex Chiang <achiang@hp.com>"
+#define DRIVER_DESC "ACPI PCI Slot Detection Driver"
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
+module_param(debug, bool, 0644);
+
+#define _COMPONENT ACPI_PCI_COMPONENT
+ACPI_MODULE_NAME("pci_slot");
+
+#define MY_NAME "pci_slot"
+#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
+#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
+#define dbg(format, arg...) \
+ do { \
+ if (debug) \
+ printk(KERN_DEBUG "%s: " format, \
+ MY_NAME , ## arg); \
+ } while (0)
+
+#define SLOT_NAME_SIZE 20 /* Inspired by #define in acpiphp.h */
+
+struct acpi_pci_slot {
+ acpi_handle root_handle; /* handle of the root bridge */
+ struct pci_slot *pci_slot; /* corresponding pci_slot */
+ struct list_head list; /* node in the list of slots */
+};
+
+static int acpi_pci_slot_add(acpi_handle handle);
+static void acpi_pci_slot_remove(acpi_handle handle);
+
+static LIST_HEAD(slot_list);
+static DEFINE_MUTEX(slot_list_lock);
+static struct acpi_pci_driver acpi_pci_slot_driver = {
+ .add = acpi_pci_slot_add,
+ .remove = acpi_pci_slot_remove,
+};
+
+static int
+check_slot(acpi_handle handle, int *device, unsigned long *sun)
+{
+ int retval = 0;
+ unsigned long adr, sta;
+ acpi_status status;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+
+ acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
+ dbg("Checking slot on path: %s\n", (char *)buffer.pointer);
+
+ if (check_sta_before_sun) {
+ /* If SxFy doesn't have _STA, we just assume it's there */
+ status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+ if (ACPI_SUCCESS(status) && !(sta & ACPI_STA_DEVICE_PRESENT)) {
+ retval = -1;
+ goto out;
+ }
+ }
+
+ status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
+ if (ACPI_FAILURE(status)) {
+ dbg("_ADR returned %d on %s\n", status, (char *)buffer.pointer);
+ retval = -1;
+ goto out;
+ }
+
+ *device = (adr >> 16) & 0xffff;
+
+ /* No _SUN == not a slot == bail */
+ status = acpi_evaluate_integer(handle, "_SUN", NULL, sun);
+ if (ACPI_FAILURE(status)) {
+ dbg("_SUN returned %d on %s\n", status, (char *)buffer.pointer);
+ retval = -1;
+ goto out;
+ }
+
+out:
+ kfree(buffer.pointer);
+ return retval;
+}
+
+struct callback_args {
+ acpi_walk_callback user_function; /* only for walk_p2p_bridge */
+ struct pci_bus *pci_bus;
+ acpi_handle root_handle;
+};
+
+/*
+ * register_slot
+ *
+ * Called once for each SxFy object in the namespace. Don't worry about
+ * calling pci_create_slot multiple times for the same pci_bus:device,
+ * since each subsequent call simply bumps the refcount on the pci_slot.
+ *
+ * The number of calls to pci_destroy_slot from unregister_slot is
+ * symmetrical.
+ */
+static acpi_status
+register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ int device;
+ unsigned long sun;
+ char name[SLOT_NAME_SIZE];
+ struct acpi_pci_slot *slot;
+ struct pci_slot *pci_slot;
+ struct callback_args *parent_context = context;
+ struct pci_bus *pci_bus = parent_context->pci_bus;
+
+ if (check_slot(handle, &device, &sun))
+ return AE_OK;
+
+ slot = kmalloc(sizeof(*slot), GFP_KERNEL);
+ if (!slot) {
+ err("%s: cannot allocate memory\n", __func__);
+ return AE_OK;
+ }
+
+ snprintf(name, sizeof(name), "%u", (u32)sun);
+ pci_slot = pci_create_slot(pci_bus, device, name);
+ if (IS_ERR(pci_slot)) {
+ err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot));
+ kfree(slot);
+ }
+
+ slot->root_handle = parent_context->root_handle;
+ slot->pci_slot = pci_slot;
+ INIT_LIST_HEAD(&slot->list);
+ mutex_lock(&slot_list_lock);
+ list_add(&slot->list, &slot_list);
+ mutex_unlock(&slot_list_lock);
+
+ dbg("pci_slot: %p, pci_bus: %x, device: %d, name: %s\n",
+ pci_slot, pci_bus->number, device, name);
+
+ return AE_OK;
+}
+
+/*
+ * walk_p2p_bridge - discover and walk p2p bridges
+ * @handle: points to an acpi_pci_root
+ * @context: p2p_bridge_context pointer
+ *
+ * Note that when we call ourselves recursively, we pass a different
+ * value of pci_bus in the child_context.
+ */
+static acpi_status
+walk_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ int device, function;
+ unsigned long adr;
+ acpi_status status;
+ acpi_handle dummy_handle;
+ acpi_walk_callback user_function;
+
+ struct pci_dev *dev;
+ struct pci_bus *pci_bus;
+ struct callback_args child_context;
+ struct callback_args *parent_context = context;
+
+ pci_bus = parent_context->pci_bus;
+ user_function = parent_context->user_function;
+
+ status = acpi_get_handle(handle, "_ADR", &dummy_handle);
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+
+ status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+
+ device = (adr >> 16) & 0xffff;
+ function = adr & 0xffff;
+
+ dev = pci_get_slot(pci_bus, PCI_DEVFN(device, function));
+ if (!dev || !dev->subordinate)
+ goto out;
+
+ child_context.pci_bus = dev->subordinate;
+ child_context.user_function = user_function;
+ child_context.root_handle = parent_context->root_handle;
+
+ dbg("p2p bridge walk, pci_bus = %x\n", dev->subordinate->number);
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
+ user_function, &child_context, NULL);
+ if (ACPI_FAILURE(status))
+ goto out;
+
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
+ walk_p2p_bridge, &child_context, NULL);
+out:
+ pci_dev_put(dev);
+ return AE_OK;
+}
+
+/*
+ * walk_root_bridge - generic root bridge walker
+ * @handle: points to an acpi_pci_root
+ * @user_function: user callback for slot objects
+ *
+ * Call user_function for all objects underneath this root bridge.
+ * Walk p2p bridges underneath us and call user_function on those too.
+ */
+static int
+walk_root_bridge(acpi_handle handle, acpi_walk_callback user_function)
+{
+ int seg, bus;
+ unsigned long tmp;
+ acpi_status status;
+ acpi_handle dummy_handle;
+ struct pci_bus *pci_bus;
+ struct callback_args context;
+
+ /* If the bridge doesn't have _STA, we assume it is always there */
+ status = acpi_get_handle(handle, "_STA", &dummy_handle);
+ if (ACPI_SUCCESS(status)) {
+ status = acpi_evaluate_integer(handle, "_STA", NULL, &tmp);
+ if (ACPI_FAILURE(status)) {
+ info("%s: _STA evaluation failure\n", __func__);
+ return 0;
+ }
+ if ((tmp & ACPI_STA_DEVICE_FUNCTIONING) == 0)
+ /* don't register this object */
+ return 0;
+ }
+
+ status = acpi_evaluate_integer(handle, "_SEG", NULL, &tmp);
+ seg = ACPI_SUCCESS(status) ? tmp : 0;
+
+ status = acpi_evaluate_integer(handle, "_BBN", NULL, &tmp);
+ bus = ACPI_SUCCESS(status) ? tmp : 0;
+
+ pci_bus = pci_find_bus(seg, bus);
+ if (!pci_bus)
+ return 0;
+
+ context.pci_bus = pci_bus;
+ context.user_function = user_function;
+ context.root_handle = handle;
+
+ dbg("root bridge walk, pci_bus = %x\n", pci_bus->number);
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
+ user_function, &context, NULL);
+ if (ACPI_FAILURE(status))
+ return status;
+
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1,
+ walk_p2p_bridge, &context, NULL);
+ if (ACPI_FAILURE(status))
+ err("%s: walk_p2p_bridge failure - %d\n", __func__, status);
+
+ return status;
+}
+
+/*
+ * acpi_pci_slot_add
+ * @handle: points to an acpi_pci_root
+ */
+static int
+acpi_pci_slot_add(acpi_handle handle)
+{
+ acpi_status status;
+
+ status = walk_root_bridge(handle, register_slot);
+ if (ACPI_FAILURE(status))
+ err("%s: register_slot failure - %d\n", __func__, status);
+
+ return status;
+}
+
+/*
+ * acpi_pci_slot_remove
+ * @handle: points to an acpi_pci_root
+ */
+static void
+acpi_pci_slot_remove(acpi_handle handle)
+{
+ struct acpi_pci_slot *slot, *tmp;
+
+ mutex_lock(&slot_list_lock);
+ list_for_each_entry_safe(slot, tmp, &slot_list, list) {
+ if (slot->root_handle == handle) {
+ list_del(&slot->list);
+ pci_destroy_slot(slot->pci_slot);
+ kfree(slot);
+ }
+ }
+ mutex_unlock(&slot_list_lock);
+}
+
+static int do_sta_before_sun(const struct dmi_system_id *d)
+{
+ info("%s detected: will evaluate _STA before calling _SUN\n", d->ident);
+ check_sta_before_sun = 1;
+ return 0;
+}
+
+static struct dmi_system_id acpi_pci_slot_dmi_table[] __initdata = {
+ /*
+ * Fujitsu Primequest machines will return 1023 to indicate an
+ * error if the _SUN method is evaluated on SxFy objects that
+ * are not present (as indicated by _STA), so for those machines,
+ * we want to check _STA before evaluating _SUN.
+ */
+ {
+ .callback = do_sta_before_sun,
+ .ident = "Fujitsu PRIMEQUEST",
+ .matches = {
+ DMI_MATCH(DMI_BIOS_VENDOR, "FUJITSU LIMITED"),
+ DMI_MATCH(DMI_BIOS_VERSION, "PRIMEQUEST"),
+ },
+ },
+ {}
+};
+
+static int __init
+acpi_pci_slot_init(void)
+{
+ dmi_check_system(acpi_pci_slot_dmi_table);
+ acpi_pci_register_driver(&acpi_pci_slot_driver);
+ return 0;
+}
+
+static void __exit
+acpi_pci_slot_exit(void)
+{
+ acpi_pci_unregister_driver(&acpi_pci_slot_driver);
+}
+
+module_init(acpi_pci_slot_init);
+module_exit(acpi_pci_slot_exit);
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
index 386e5aa48834..a12fa118667d 100644
--- a/drivers/acpi/processor_core.c
+++ b/drivers/acpi/processor_core.c
@@ -86,7 +86,6 @@ static int acpi_processor_info_open_fs(struct inode *inode, struct file *file);
static void acpi_processor_notify(acpi_handle handle, u32 event, void *data);
static acpi_status acpi_processor_hotadd_init(acpi_handle handle, int *p_cpu);
static int acpi_processor_handle_eject(struct acpi_processor *pr);
-extern int acpi_processor_tstate_has_changed(struct acpi_processor *pr);
static const struct acpi_device_id processor_device_ids[] = {
@@ -119,7 +118,7 @@ static const struct file_operations acpi_processor_info_fops = {
.release = single_release,
};
-struct acpi_processor *processors[NR_CPUS];
+DEFINE_PER_CPU(struct acpi_processor *, processors);
struct acpi_processor_errata errata __read_mostly;
/* --------------------------------------------------------------------------
@@ -615,14 +614,14 @@ static int acpi_processor_get_info(struct acpi_processor *pr, unsigned has_uid)
return 0;
}
-static void *processor_device_array[NR_CPUS];
+static DEFINE_PER_CPU(void *, processor_device_array);
static int __cpuinit acpi_processor_start(struct acpi_device *device)
{
int result = 0;
acpi_status status = AE_OK;
struct acpi_processor *pr;
-
+ struct sys_device *sysdev;
pr = acpi_driver_data(device);
@@ -639,20 +638,24 @@ static int __cpuinit acpi_processor_start(struct acpi_device *device)
* ACPI id of processors can be reported wrongly by the BIOS.
* Don't trust it blindly
*/
- if (processor_device_array[pr->id] != NULL &&
- processor_device_array[pr->id] != device) {
+ if (per_cpu(processor_device_array, pr->id) != NULL &&
+ per_cpu(processor_device_array, pr->id) != device) {
printk(KERN_WARNING "BIOS reported wrong ACPI id "
"for the processor\n");
return -ENODEV;
}
- processor_device_array[pr->id] = device;
+ per_cpu(processor_device_array, pr->id) = device;
- processors[pr->id] = pr;
+ per_cpu(processors, pr->id) = pr;
result = acpi_processor_add_fs(device);
if (result)
goto end;
+ sysdev = get_cpu_sysdev(pr->id);
+ if (sysfs_create_link(&device->dev.kobj, &sysdev->kobj, "sysdev"))
+ return -EFAULT;
+
status = acpi_install_notify_handler(pr->handle, ACPI_DEVICE_NOTIFY,
acpi_processor_notify, pr);
@@ -675,9 +678,8 @@ static int __cpuinit acpi_processor_start(struct acpi_device *device)
goto end;
}
- printk(KERN_INFO PREFIX
- "%s is registered as cooling_device%d\n",
- device->dev.bus_id, pr->cdev->id);
+ dev_info(&device->dev, "registered as cooling_device%d\n",
+ pr->cdev->id);
result = sysfs_create_link(&device->dev.kobj,
&pr->cdev->device.kobj,
@@ -750,7 +752,7 @@ static int acpi_cpu_soft_notify(struct notifier_block *nfb,
unsigned long action, void *hcpu)
{
unsigned int cpu = (unsigned long)hcpu;
- struct acpi_processor *pr = processors[cpu];
+ struct acpi_processor *pr = per_cpu(processors, cpu);
if (action == CPU_ONLINE && pr) {
acpi_processor_ppc_has_changed(pr);
@@ -811,6 +813,8 @@ static int acpi_processor_remove(struct acpi_device *device, int type)
status = acpi_remove_notify_handler(pr->handle, ACPI_DEVICE_NOTIFY,
acpi_processor_notify);
+ sysfs_remove_link(&device->dev.kobj, "sysdev");
+
acpi_processor_remove_fs(device);
if (pr->cdev) {
@@ -820,8 +824,8 @@ static int acpi_processor_remove(struct acpi_device *device, int type)
pr->cdev = NULL;
}
- processors[pr->id] = NULL;
- processor_device_array[pr->id] = NULL;
+ per_cpu(processors, pr->id) = NULL;
+ per_cpu(processor_device_array, pr->id) = NULL;
kfree(pr);
return 0;
@@ -1015,9 +1019,9 @@ static acpi_status acpi_processor_hotadd_init(acpi_handle handle, int *p_cpu)
static int acpi_processor_handle_eject(struct acpi_processor *pr)
{
- if (cpu_online(pr->id)) {
- return (-EINVAL);
- }
+ if (cpu_online(pr->id))
+ cpu_down(pr->id);
+
arch_unregister_cpu(pr->id);
acpi_unmap_lsapic(pr->id);
return (0);
@@ -1069,8 +1073,6 @@ static int __init acpi_processor_init(void)
{
int result = 0;
-
- memset(&processors, 0, sizeof(processors));
memset(&errata, 0, sizeof(errata));
#ifdef CONFIG_SMP
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 2dd2c1f3a01c..dbf93610e5ee 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -401,7 +401,7 @@ static void acpi_processor_idle(void)
*/
local_irq_disable();
- pr = processors[smp_processor_id()];
+ pr = __get_cpu_var(processors);
if (!pr) {
local_irq_enable();
return;
@@ -1431,7 +1431,7 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev,
struct acpi_processor *pr;
struct acpi_processor_cx *cx = cpuidle_get_statedata(state);
- pr = processors[smp_processor_id()];
+ pr = __get_cpu_var(processors);
if (unlikely(!pr))
return 0;
@@ -1471,7 +1471,7 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
u32 t1, t2;
int sleep_ticks = 0;
- pr = processors[smp_processor_id()];
+ pr = __get_cpu_var(processors);
if (unlikely(!pr))
return 0;
@@ -1549,7 +1549,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
u32 t1, t2;
int sleep_ticks = 0;
- pr = processors[smp_processor_id()];
+ pr = __get_cpu_var(processors);
if (unlikely(!pr))
return 0;
@@ -1669,6 +1669,7 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
return -EINVAL;
}
+ dev->cpu = pr->id;
for (i = 0; i < CPUIDLE_STATE_MAX; i++) {
dev->states[i].name[0] = '\0';
dev->states[i].desc[0] = '\0';
@@ -1738,7 +1739,7 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
int acpi_processor_cst_has_changed(struct acpi_processor *pr)
{
- int ret;
+ int ret = 0;
if (boot_option_idle_override)
return 0;
@@ -1756,8 +1757,10 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr)
cpuidle_pause_and_lock();
cpuidle_disable_device(&pr->power.dev);
acpi_processor_get_power_info(pr);
- acpi_processor_setup_cpuidle(pr);
- ret = cpuidle_enable_device(&pr->power.dev);
+ if (pr->flags.power) {
+ acpi_processor_setup_cpuidle(pr);
+ ret = cpuidle_enable_device(&pr->power.dev);
+ }
cpuidle_resume_and_unlock();
return ret;
@@ -1813,7 +1816,6 @@ int __cpuinit acpi_processor_power_init(struct acpi_processor *pr,
if (pr->flags.power) {
#ifdef CONFIG_CPU_IDLE
acpi_processor_setup_cpuidle(pr);
- pr->power.dev.cpu = pr->id;
if (cpuidle_register_device(&pr->power.dev))
return -EIO;
#endif
@@ -1850,8 +1852,7 @@ int acpi_processor_power_exit(struct acpi_processor *pr,
return 0;
#ifdef CONFIG_CPU_IDLE
- if (pr->flags.power)
- cpuidle_unregister_device(&pr->power.dev);
+ cpuidle_unregister_device(&pr->power.dev);
#endif
pr->flags.power_setup_done = 0;
diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c
index d80b2d1441af..b4749969c6b4 100644
--- a/drivers/acpi/processor_perflib.c
+++ b/drivers/acpi/processor_perflib.c
@@ -89,7 +89,7 @@ static int acpi_processor_ppc_notifier(struct notifier_block *nb,
if (event != CPUFREQ_INCOMPATIBLE)
goto out;
- pr = processors[policy->cpu];
+ pr = per_cpu(processors, policy->cpu);
if (!pr || !pr->performance)
goto out;
@@ -572,7 +572,7 @@ int acpi_processor_preregister_performance(
/* Call _PSD for all CPUs */
for_each_possible_cpu(i) {
- pr = processors[i];
+ pr = per_cpu(processors, i);
if (!pr) {
/* Look only at processors in ACPI namespace */
continue;
@@ -603,7 +603,7 @@ int acpi_processor_preregister_performance(
* domain info.
*/
for_each_possible_cpu(i) {
- pr = processors[i];
+ pr = per_cpu(processors, i);
if (!pr)
continue;
@@ -624,7 +624,7 @@ int acpi_processor_preregister_performance(
cpus_clear(covered_cpus);
for_each_possible_cpu(i) {
- pr = processors[i];
+ pr = per_cpu(processors, i);
if (!pr)
continue;
@@ -651,7 +651,7 @@ int acpi_processor_preregister_performance(
if (i == j)
continue;
- match_pr = processors[j];
+ match_pr = per_cpu(processors, j);
if (!match_pr)
continue;
@@ -680,7 +680,7 @@ int acpi_processor_preregister_performance(
if (i == j)
continue;
- match_pr = processors[j];
+ match_pr = per_cpu(processors, j);
if (!match_pr)
continue;
@@ -697,7 +697,7 @@ int acpi_processor_preregister_performance(
err_ret:
for_each_possible_cpu(i) {
- pr = processors[i];
+ pr = per_cpu(processors, i);
if (!pr || !pr->performance)
continue;
@@ -728,7 +728,7 @@ acpi_processor_register_performance(struct acpi_processor_performance
mutex_lock(&performance_mutex);
- pr = processors[cpu];
+ pr = per_cpu(processors, cpu);
if (!pr) {
mutex_unlock(&performance_mutex);
return -ENODEV;
@@ -766,7 +766,7 @@ acpi_processor_unregister_performance(struct acpi_processor_performance
mutex_lock(&performance_mutex);
- pr = processors[cpu];
+ pr = per_cpu(processors, cpu);
if (!pr) {
mutex_unlock(&performance_mutex);
return;
diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c
index bb06738860c4..74b26085dd28 100644
--- a/drivers/acpi/processor_throttling.c
+++ b/drivers/acpi/processor_throttling.c
@@ -71,7 +71,7 @@ static int acpi_processor_update_tsd_coord(void)
* coordination between all CPUs.
*/
for_each_possible_cpu(i) {
- pr = processors[i];
+ pr = per_cpu(processors, i);
if (!pr)
continue;
@@ -93,7 +93,7 @@ static int acpi_processor_update_tsd_coord(void)
cpus_clear(covered_cpus);
for_each_possible_cpu(i) {
- pr = processors[i];
+ pr = per_cpu(processors, i);
if (!pr)
continue;
@@ -119,7 +119,7 @@ static int acpi_processor_update_tsd_coord(void)
if (i == j)
continue;
- match_pr = processors[j];
+ match_pr = per_cpu(processors, j);
if (!match_pr)
continue;
@@ -152,7 +152,7 @@ static int acpi_processor_update_tsd_coord(void)
if (i == j)
continue;
- match_pr = processors[j];
+ match_pr = per_cpu(processors, j);
if (!match_pr)
continue;
@@ -172,7 +172,7 @@ static int acpi_processor_update_tsd_coord(void)
err_ret:
for_each_possible_cpu(i) {
- pr = processors[i];
+ pr = per_cpu(processors, i);
if (!pr)
continue;
@@ -214,7 +214,7 @@ static int acpi_processor_throttling_notifier(unsigned long event, void *data)
struct acpi_processor_throttling *p_throttling;
cpu = p_tstate->cpu;
- pr = processors[cpu];
+ pr = per_cpu(processors, cpu);
if (!pr) {
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Invalid pr pointer\n"));
return 0;
@@ -1013,7 +1013,7 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state)
* affected cpu in order to get one proper T-state.
* The notifier event is THROTTLING_PRECHANGE.
*/
- for_each_cpu_mask(i, online_throttling_cpus) {
+ for_each_cpu_mask_nr(i, online_throttling_cpus) {
t_state.cpu = i;
acpi_processor_throttling_notifier(THROTTLING_PRECHANGE,
&t_state);
@@ -1034,8 +1034,8 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state)
* it is necessary to set T-state for every affected
* cpus.
*/
- for_each_cpu_mask(i, online_throttling_cpus) {
- match_pr = processors[i];
+ for_each_cpu_mask_nr(i, online_throttling_cpus) {
+ match_pr = per_cpu(processors, i);
/*
* If the pointer is invalid, we will report the
* error message and continue.
@@ -1068,7 +1068,7 @@ int acpi_processor_set_throttling(struct acpi_processor *pr, int state)
* affected cpu to update the T-states.
* The notifier event is THROTTLING_POSTCHANGE
*/
- for_each_cpu_mask(i, online_throttling_cpus) {
+ for_each_cpu_mask_nr(i, online_throttling_cpus) {
t_state.cpu = i;
acpi_processor_throttling_notifier(THROTTLING_POSTCHANGE,
&t_state);
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 6d85289f1c12..9c50c39902d5 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -6,6 +6,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/acpi.h>
+#include <asm/signal.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acinterp.h> /* for acpi_ex_eisa_id_to_string() */
@@ -92,17 +93,37 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha
}
static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);
-static int acpi_eject_operation(acpi_handle handle, int lockable)
+static int acpi_bus_hot_remove_device(void *context)
{
+ struct acpi_device *device;
+ acpi_handle handle = context;
struct acpi_object_list arg_list;
union acpi_object arg;
acpi_status status = AE_OK;
- /*
- * TBD: evaluate _PS3?
- */
+ if (acpi_bus_get_device(handle, &device))
+ return 0;
- if (lockable) {
+ if (!device)
+ return 0;
+
+ ACPI_DEBUG_PRINT((ACPI_DB_INFO,
+ "Hot-removing device %s...\n", device->dev.bus_id));
+
+
+ if (acpi_bus_trim(device, 1)) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "Removing device failed\n"));
+ return -1;
+ }
+
+ /* power off device */
+ status = acpi_evaluate_object(handle, "_PS3", NULL, NULL);
+ if (ACPI_FAILURE(status) && status != AE_NOT_FOUND)
+ ACPI_DEBUG_PRINT((ACPI_DB_WARN,
+ "Power-off device failed\n"));
+
+ if (device->flags.lockable) {
arg_list.count = 1;
arg_list.pointer = &arg;
arg.type = ACPI_TYPE_INTEGER;
@@ -118,24 +139,19 @@ static int acpi_eject_operation(acpi_handle handle, int lockable)
/*
* TBD: _EJD support.
*/
-
status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL);
- if (ACPI_FAILURE(status)) {
- return (-ENODEV);
- }
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
- return (0);
+ return 0;
}
static ssize_t
acpi_eject_store(struct device *d, struct device_attribute *attr,
const char *buf, size_t count)
{
- int result;
int ret = count;
- int islockable;
acpi_status status;
- acpi_handle handle;
acpi_object_type type = 0;
struct acpi_device *acpi_device = to_acpi_device(d);
@@ -154,17 +170,9 @@ acpi_eject_store(struct device *d, struct device_attribute *attr,
goto err;
}
- islockable = acpi_device->flags.lockable;
- handle = acpi_device->handle;
-
- result = acpi_bus_trim(acpi_device, 1);
-
- if (!result)
- result = acpi_eject_operation(handle, islockable);
-
- if (result) {
- ret = -EBUSY;
- }
+ /* remove the device in another thread to fix the deadlock issue */
+ ret = kernel_thread(acpi_bus_hot_remove_device,
+ acpi_device->handle, SIGCHLD);
err:
return ret;
}
@@ -459,7 +467,7 @@ static int acpi_device_register(struct acpi_device *device,
device->dev.release = &acpi_device_release;
result = device_add(&device->dev);
if(result) {
- printk(KERN_ERR PREFIX "Error adding device %s", device->dev.bus_id);
+ dev_err(&device->dev, "Error adding device\n");
goto end;
}
diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c
index c3b0cd88d09f..4addf8ad50ae 100644
--- a/drivers/acpi/sleep/main.c
+++ b/drivers/acpi/sleep/main.c
@@ -24,10 +24,6 @@
u8 sleep_states[ACPI_S_STATE_COUNT];
-#ifdef CONFIG_PM_SLEEP
-static u32 acpi_target_sleep_state = ACPI_STATE_S0;
-#endif
-
static int acpi_sleep_prepare(u32 acpi_state)
{
#ifdef CONFIG_ACPI_SLEEP
@@ -50,9 +46,96 @@ static int acpi_sleep_prepare(u32 acpi_state)
return 0;
}
-#ifdef CONFIG_SUSPEND
-static struct platform_suspend_ops acpi_suspend_ops;
+#ifdef CONFIG_PM_SLEEP
+static u32 acpi_target_sleep_state = ACPI_STATE_S0;
+
+/*
+ * ACPI 1.0 wants us to execute _PTS before suspending devices, so we allow the
+ * user to request that behavior by using the 'acpi_old_suspend_ordering'
+ * kernel command line option that causes the following variable to be set.
+ */
+static bool old_suspend_ordering;
+
+void __init acpi_old_suspend_ordering(void)
+{
+ old_suspend_ordering = true;
+}
+
+/**
+ * acpi_pm_disable_gpes - Disable the GPEs.
+ */
+static int acpi_pm_disable_gpes(void)
+{
+ acpi_hw_disable_all_gpes();
+ return 0;
+}
+
+/**
+ * __acpi_pm_prepare - Prepare the platform to enter the target state.
+ *
+ * If necessary, set the firmware waking vector and do arch-specific
+ * nastiness to get the wakeup code to the waking vector.
+ */
+static int __acpi_pm_prepare(void)
+{
+ int error = acpi_sleep_prepare(acpi_target_sleep_state);
+
+ if (error)
+ acpi_target_sleep_state = ACPI_STATE_S0;
+ return error;
+}
+
+/**
+ * acpi_pm_prepare - Prepare the platform to enter the target sleep
+ * state and disable the GPEs.
+ */
+static int acpi_pm_prepare(void)
+{
+ int error = __acpi_pm_prepare();
+
+ if (!error)
+ acpi_hw_disable_all_gpes();
+ return error;
+}
+
+/**
+ * acpi_pm_finish - Instruct the platform to leave a sleep state.
+ *
+ * This is called after we wake back up (or if entering the sleep state
+ * failed).
+ */
+static void acpi_pm_finish(void)
+{
+ u32 acpi_state = acpi_target_sleep_state;
+
+ if (acpi_state == ACPI_STATE_S0)
+ return;
+ printk(KERN_INFO PREFIX "Waking up from system sleep state S%d\n",
+ acpi_state);
+ acpi_disable_wakeup_device(acpi_state);
+ acpi_leave_sleep_state(acpi_state);
+
+ /* reset firmware waking vector */
+ acpi_set_firmware_waking_vector((acpi_physical_address) 0);
+
+ acpi_target_sleep_state = ACPI_STATE_S0;
+}
+
+/**
+ * acpi_pm_end - Finish up suspend sequence.
+ */
+static void acpi_pm_end(void)
+{
+ /*
+ * This is necessary in case acpi_pm_finish() is not called during a
+ * failing transition to a sleep state.
+ */
+ acpi_target_sleep_state = ACPI_STATE_S0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+#ifdef CONFIG_SUSPEND
extern void do_suspend_lowlevel(void);
static u32 acpi_suspend_states[] = {
@@ -62,13 +145,10 @@ static u32 acpi_suspend_states[] = {
[PM_SUSPEND_MAX] = ACPI_STATE_S5
};
-static int init_8259A_after_S1;
-
/**
* acpi_suspend_begin - Set the target system sleep state to the state
* associated with given @pm_state, if supported.
*/
-
static int acpi_suspend_begin(suspend_state_t pm_state)
{
u32 acpi_state = acpi_suspend_states[pm_state];
@@ -85,25 +165,6 @@ static int acpi_suspend_begin(suspend_state_t pm_state)
}
/**
- * acpi_suspend_prepare - Do preliminary suspend work.
- *
- * If necessary, set the firmware waking vector and do arch-specific
- * nastiness to get the wakeup code to the waking vector.
- */
-
-static int acpi_suspend_prepare(void)
-{
- int error = acpi_sleep_prepare(acpi_target_sleep_state);
-
- if (error) {
- acpi_target_sleep_state = ACPI_STATE_S0;
- return error;
- }
-
- return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT;
-}
-
-/**
* acpi_suspend_enter - Actually enter a sleep state.
* @pm_state: ignored
*
@@ -111,7 +172,6 @@ static int acpi_suspend_prepare(void)
* assembly, which in turn call acpi_enter_sleep_state().
* It's unfortunate, but it works. Please fix if you're feeling frisky.
*/
-
static int acpi_suspend_enter(suspend_state_t pm_state)
{
acpi_status status = AE_OK;
@@ -168,46 +228,6 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
return ACPI_SUCCESS(status) ? 0 : -EFAULT;
}
-/**
- * acpi_suspend_finish - Instruct the platform to leave a sleep state.
- *
- * This is called after we wake back up (or if entering the sleep state
- * failed).
- */
-
-static void acpi_suspend_finish(void)
-{
- u32 acpi_state = acpi_target_sleep_state;
-
- acpi_disable_wakeup_device(acpi_state);
- acpi_leave_sleep_state(acpi_state);
-
- /* reset firmware waking vector */
- acpi_set_firmware_waking_vector((acpi_physical_address) 0);
-
- acpi_target_sleep_state = ACPI_STATE_S0;
-
-#ifdef CONFIG_X86
- if (init_8259A_after_S1) {
- printk("Broken toshiba laptop -> kicking interrupts\n");
- init_8259A(0);
- }
-#endif
-}
-
-/**
- * acpi_suspend_end - Finish up suspend sequence.
- */
-
-static void acpi_suspend_end(void)
-{
- /*
- * This is necessary in case acpi_suspend_finish() is not called during a
- * failing transition to a sleep state.
- */
- acpi_target_sleep_state = ACPI_STATE_S0;
-}
-
static int acpi_suspend_state_valid(suspend_state_t pm_state)
{
u32 acpi_state;
@@ -227,30 +247,39 @@ static int acpi_suspend_state_valid(suspend_state_t pm_state)
static struct platform_suspend_ops acpi_suspend_ops = {
.valid = acpi_suspend_state_valid,
.begin = acpi_suspend_begin,
- .prepare = acpi_suspend_prepare,
+ .prepare = acpi_pm_prepare,
.enter = acpi_suspend_enter,
- .finish = acpi_suspend_finish,
- .end = acpi_suspend_end,
+ .finish = acpi_pm_finish,
+ .end = acpi_pm_end,
};
-/*
- * Toshiba fails to preserve interrupts over S1, reinitialization
- * of 8259 is needed after S1 resume.
+/**
+ * acpi_suspend_begin_old - Set the target system sleep state to the
+ * state associated with given @pm_state, if supported, and
+ * execute the _PTS control method. This function is used if the
+ * pre-ACPI 2.0 suspend ordering has been requested.
*/
-static int __init init_ints_after_s1(const struct dmi_system_id *d)
+static int acpi_suspend_begin_old(suspend_state_t pm_state)
{
- printk(KERN_WARNING "%s with broken S1 detected.\n", d->ident);
- init_8259A_after_S1 = 1;
- return 0;
+ int error = acpi_suspend_begin(pm_state);
+
+ if (!error)
+ error = __acpi_pm_prepare();
+ return error;
}
-static struct dmi_system_id __initdata acpisleep_dmi_table[] = {
- {
- .callback = init_ints_after_s1,
- .ident = "Toshiba Satellite 4030cdt",
- .matches = {DMI_MATCH(DMI_PRODUCT_NAME, "S4030CDT/4.3"),},
- },
- {},
+/*
+ * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has
+ * been requested.
+ */
+static struct platform_suspend_ops acpi_suspend_ops_old = {
+ .valid = acpi_suspend_state_valid,
+ .begin = acpi_suspend_begin_old,
+ .prepare = acpi_pm_disable_gpes,
+ .enter = acpi_suspend_enter,
+ .finish = acpi_pm_finish,
+ .end = acpi_pm_end,
+ .recover = acpi_pm_finish,
};
#endif /* CONFIG_SUSPEND */
@@ -258,22 +287,9 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = {
static int acpi_hibernation_begin(void)
{
acpi_target_sleep_state = ACPI_STATE_S4;
-
return 0;
}
-static int acpi_hibernation_prepare(void)
-{
- int error = acpi_sleep_prepare(ACPI_STATE_S4);
-
- if (error) {
- acpi_target_sleep_state = ACPI_STATE_S0;
- return error;
- }
-
- return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT;
-}
-
static int acpi_hibernation_enter(void)
{
acpi_status status = AE_OK;
@@ -303,52 +319,55 @@ static void acpi_hibernation_leave(void)
acpi_leave_sleep_state_prep(ACPI_STATE_S4);
}
-static void acpi_hibernation_finish(void)
+static void acpi_pm_enable_gpes(void)
{
- acpi_disable_wakeup_device(ACPI_STATE_S4);
- acpi_leave_sleep_state(ACPI_STATE_S4);
-
- /* reset firmware waking vector */
- acpi_set_firmware_waking_vector((acpi_physical_address) 0);
-
- acpi_target_sleep_state = ACPI_STATE_S0;
+ acpi_hw_enable_all_runtime_gpes();
}
-static void acpi_hibernation_end(void)
-{
- /*
- * This is necessary in case acpi_hibernation_finish() is not called
- * during a failing transition to the sleep state.
- */
- acpi_target_sleep_state = ACPI_STATE_S0;
-}
+static struct platform_hibernation_ops acpi_hibernation_ops = {
+ .begin = acpi_hibernation_begin,
+ .end = acpi_pm_end,
+ .pre_snapshot = acpi_pm_prepare,
+ .finish = acpi_pm_finish,
+ .prepare = acpi_pm_prepare,
+ .enter = acpi_hibernation_enter,
+ .leave = acpi_hibernation_leave,
+ .pre_restore = acpi_pm_disable_gpes,
+ .restore_cleanup = acpi_pm_enable_gpes,
+};
-static int acpi_hibernation_pre_restore(void)
+/**
+ * acpi_hibernation_begin_old - Set the target system sleep state to
+ * ACPI_STATE_S4 and execute the _PTS control method. This
+ * function is used if the pre-ACPI 2.0 suspend ordering has been
+ * requested.
+ */
+static int acpi_hibernation_begin_old(void)
{
- acpi_status status;
-
- status = acpi_hw_disable_all_gpes();
-
- return ACPI_SUCCESS(status) ? 0 : -EFAULT;
-}
+ int error = acpi_sleep_prepare(ACPI_STATE_S4);
-static void acpi_hibernation_restore_cleanup(void)
-{
- acpi_hw_enable_all_runtime_gpes();
+ if (!error)
+ acpi_target_sleep_state = ACPI_STATE_S4;
+ return error;
}
-static struct platform_hibernation_ops acpi_hibernation_ops = {
- .begin = acpi_hibernation_begin,
- .end = acpi_hibernation_end,
- .pre_snapshot = acpi_hibernation_prepare,
- .finish = acpi_hibernation_finish,
- .prepare = acpi_hibernation_prepare,
+/*
+ * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has
+ * been requested.
+ */
+static struct platform_hibernation_ops acpi_hibernation_ops_old = {
+ .begin = acpi_hibernation_begin_old,
+ .end = acpi_pm_end,
+ .pre_snapshot = acpi_pm_disable_gpes,
+ .finish = acpi_pm_finish,
+ .prepare = acpi_pm_disable_gpes,
.enter = acpi_hibernation_enter,
.leave = acpi_hibernation_leave,
- .pre_restore = acpi_hibernation_pre_restore,
- .restore_cleanup = acpi_hibernation_restore_cleanup,
+ .pre_restore = acpi_pm_disable_gpes,
+ .restore_cleanup = acpi_pm_enable_gpes,
+ .recover = acpi_pm_finish,
};
-#endif /* CONFIG_HIBERNATION */
+#endif /* CONFIG_HIBERNATION */
int acpi_suspend(u32 acpi_state)
{
@@ -369,8 +388,8 @@ int acpi_suspend(u32 acpi_state)
/**
* acpi_pm_device_sleep_state - return preferred power state of ACPI device
* in the system sleep state given by %acpi_target_sleep_state
- * @dev: device to examine
- * @wake: if set, the device should be able to wake up the system
+ * @dev: device to examine; its driver model wakeup flags control
+ * whether it should be able to wake up the system
* @d_min_p: used to store the upper limit of allowed states range
* Return value: preferred power state of the device on success, -ENODEV on
* failure (ie. if there's no 'struct acpi_device' for @dev)
@@ -388,7 +407,7 @@ int acpi_suspend(u32 acpi_state)
* via @wake.
*/
-int acpi_pm_device_sleep_state(struct device *dev, int wake, int *d_min_p)
+int acpi_pm_device_sleep_state(struct device *dev, int *d_min_p)
{
acpi_handle handle = DEVICE_ACPI_HANDLE(dev);
struct acpi_device *adev;
@@ -427,7 +446,7 @@ int acpi_pm_device_sleep_state(struct device *dev, int wake, int *d_min_p)
* can wake the system. _S0W may be valid, too.
*/
if (acpi_target_sleep_state == ACPI_STATE_S0 ||
- (wake && adev->wakeup.state.enabled &&
+ (device_may_wakeup(dev) && adev->wakeup.state.enabled &&
adev->wakeup.sleep_state <= acpi_target_sleep_state)) {
acpi_status status;
@@ -473,8 +492,6 @@ int __init acpi_sleep_init(void)
u8 type_a, type_b;
#ifdef CONFIG_SUSPEND
int i = 0;
-
- dmi_check_system(acpisleep_dmi_table);
#endif
if (acpi_disabled)
@@ -492,13 +509,15 @@ int __init acpi_sleep_init(void)
}
}
- suspend_set_ops(&acpi_suspend_ops);
+ suspend_set_ops(old_suspend_ordering ?
+ &acpi_suspend_ops_old : &acpi_suspend_ops);
#endif
#ifdef CONFIG_HIBERNATION
status = acpi_get_sleep_type_data(ACPI_STATE_S4, &type_a, &type_b);
if (ACPI_SUCCESS(status)) {
- hibernation_set_ops(&acpi_hibernation_ops);
+ hibernation_set_ops(old_suspend_ordering ?
+ &acpi_hibernation_ops_old : &acpi_hibernation_ops);
sleep_states[ACPI_STATE_S4] = 1;
printk(" S4");
}
diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c
index 8a5fe8710513..224c57c03381 100644
--- a/drivers/acpi/sleep/proc.c
+++ b/drivers/acpi/sleep/proc.c
@@ -495,6 +495,12 @@ static int __init acpi_sleep_proc_init(void)
acpi_root_dir, &acpi_system_alarm_fops);
acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL);
+ /*
+ * Disable the RTC event after installing RTC handler.
+ * Only when RTC alarm is set will it be enabled.
+ */
+ acpi_clear_event(ACPI_EVENT_RTC);
+ acpi_disable_event(ACPI_EVENT_RTC, 0);
#endif /* HAVE_ACPI_LEGACY_ALARM */
/* 'wakeup device' [R/W] */
diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c
index 769f24855eb6..5bd2dec9a7ac 100644
--- a/drivers/acpi/system.c
+++ b/drivers/acpi/system.c
@@ -77,7 +77,6 @@ static ssize_t acpi_table_show(struct kobject *kobj,
container_of(bin_attr, struct acpi_table_attr, attr);
struct acpi_table_header *table_header = NULL;
acpi_status status;
- ssize_t ret_count = count;
status =
acpi_get_table(table_attr->name, table_attr->instance,
@@ -85,18 +84,8 @@ static ssize_t acpi_table_show(struct kobject *kobj,
if (ACPI_FAILURE(status))
return -ENODEV;
- if (offset >= table_header->length) {
- ret_count = 0;
- goto end;
- }
-
- if (offset + ret_count > table_header->length)
- ret_count = table_header->length - offset;
-
- memcpy(buf, ((char *)table_header) + offset, ret_count);
-
- end:
- return ret_count;
+ return memory_read_from_buffer(buf, count, &offset,
+ table_header, table_header->length);
}
static void acpi_table_attr_init(struct acpi_table_attr *table_attr,
diff --git a/drivers/acpi/tables/tbinstal.c b/drivers/acpi/tables/tbinstal.c
index 402f93e1ff20..5336ce88f89f 100644
--- a/drivers/acpi/tables/tbinstal.c
+++ b/drivers/acpi/tables/tbinstal.c
@@ -123,24 +123,13 @@ acpi_tb_add_table(struct acpi_table_desc *table_desc,
}
}
- /* The table must be either an SSDT or a PSDT or an OEMx */
-
- if (!ACPI_COMPARE_NAME(table_desc->pointer->signature, ACPI_SIG_PSDT)&&
- !ACPI_COMPARE_NAME(table_desc->pointer->signature, ACPI_SIG_SSDT)&&
- strncmp(table_desc->pointer->signature, "OEM", 3)) {
- /* Check for a printable name */
- if (acpi_ut_valid_acpi_name(
- *(u32 *) table_desc->pointer->signature)) {
- ACPI_ERROR((AE_INFO, "Table has invalid signature "
- "[%4.4s], must be SSDT or PSDT",
- table_desc->pointer->signature));
- } else {
- ACPI_ERROR((AE_INFO, "Table has invalid signature "
- "(0x%8.8X), must be SSDT or PSDT",
- *(u32 *) table_desc->pointer->signature));
- }
- return_ACPI_STATUS(AE_BAD_SIGNATURE);
- }
+ /*
+ * Originally, we checked the table signature for "SSDT" or "PSDT" here.
+ * Next, we added support for OEMx tables, signature "OEM".
+ * Valid tables were encountered with a null signature, so we've just
+ * given up on validating the signature, since it seems to be a waste
+ * of code. The original code was removed (05/2008).
+ */
(void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES);
diff --git a/drivers/acpi/tables/tbxface.c b/drivers/acpi/tables/tbxface.c
index fb57b93c2495..0e319604d3e7 100644
--- a/drivers/acpi/tables/tbxface.c
+++ b/drivers/acpi/tables/tbxface.c
@@ -540,7 +540,7 @@ static acpi_status acpi_tb_load_namespace(void)
acpi_tb_print_table_header(0, table);
if (no_auto_ssdt == 0) {
- printk(KERN_WARNING "ACPI: DSDT override uses original SSDTs unless \"acpi_no_auto_ssdt\"");
+ printk(KERN_WARNING "ACPI: DSDT override uses original SSDTs unless \"acpi_no_auto_ssdt\"\n");
}
}
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 504385b1f211..30a341337933 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -364,10 +364,17 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag)
if (flag & ACPI_TRIPS_CRITICAL) {
status = acpi_evaluate_integer(tz->device->handle,
"_CRT", NULL, &tz->trips.critical.temperature);
- if (ACPI_FAILURE(status)) {
+ /*
+ * Treat freezing temperatures as invalid as well; some
+ * BIOSes return really low values and cause reboots at startup.
+ * Below zero (Celcius) values clearly aren't right for sure..
+ * ... so lets discard those as invalid.
+ */
+ if (ACPI_FAILURE(status) ||
+ tz->trips.critical.temperature <= 2732) {
tz->trips.critical.flags.valid = 0;
ACPI_EXCEPTION((AE_INFO, status,
- "No critical threshold"));
+ "No or invalid critical threshold"));
return -ENODEV;
} else {
tz->trips.critical.flags.valid = 1;
@@ -1172,8 +1179,8 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz)
tz->tz_enabled = 1;
- printk(KERN_INFO PREFIX "%s is registered as thermal_zone%d\n",
- tz->device->dev.bus_id, tz->thermal_zone->id);
+ dev_info(&tz->device->dev, "registered as thermal_zone%d\n",
+ tz->thermal_zone->id);
return 0;
}
diff --git a/drivers/acpi/utilities/utmisc.c b/drivers/acpi/utilities/utmisc.c
index e4ba7192cd15..1f057b71db1a 100644
--- a/drivers/acpi/utilities/utmisc.c
+++ b/drivers/acpi/utilities/utmisc.c
@@ -1048,6 +1048,7 @@ acpi_ut_exception(char *module_name,
va_start(args, format);
acpi_os_vprintf(format, args);
acpi_os_printf(" [%X]\n", ACPI_CA_VERSION);
+ va_end(args);
}
EXPORT_SYMBOL(acpi_ut_exception);
@@ -1063,7 +1064,6 @@ acpi_ut_warning(char *module_name, u32 line_number, char *format, ...)
acpi_os_vprintf(format, args);
acpi_os_printf(" [%X]\n", ACPI_CA_VERSION);
va_end(args);
- va_end(args);
}
void ACPI_INTERNAL_VAR_XFACE
diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
index 5e5dda3a3027..03b79faca380 100644
--- a/drivers/acpi/video.c
+++ b/drivers/acpi/video.c
@@ -741,9 +741,8 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
if (IS_ERR(device->cdev))
return;
- printk(KERN_INFO PREFIX
- "%s is registered as cooling_device%d\n",
- device->dev->dev.bus_id, device->cdev->id);
+ dev_info(&device->dev->dev, "registered as cooling_device%d\n",
+ device->cdev->id);
result = sysfs_create_link(&device->dev->dev.kobj,
&device->cdev->device.kobj,
"thermal_cooling");
diff --git a/drivers/atm/eni.h b/drivers/atm/eni.h
index d04fefb0841f..e4c9525e60b3 100644
--- a/drivers/atm/eni.h
+++ b/drivers/atm/eni.h
@@ -18,7 +18,6 @@
#include "midway.h"
-#define KERNEL_OFFSET 0xC0000000 /* kernel 0x0 is at phys 0xC0000000 */
#define DEV_LABEL "eni"
#define UBR_BUFFER (128*1024) /* UBR buffer size */
diff --git a/drivers/base/base.h b/drivers/base/base.h
index 2c9ae43e2219..31dc0cd84afa 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -36,6 +36,33 @@ struct driver_private {
};
#define to_driver(obj) container_of(obj, struct driver_private, kobj)
+
+/**
+ * struct class_private - structure to hold the private to the driver core portions of the class structure.
+ *
+ * @class_subsys - the struct kset that defines this class. This is the main kobject
+ * @class_devices - list of devices associated with this class
+ * @class_interfaces - list of class_interfaces associated with this class
+ * @class_dirs - "glue" directory for virtual devices associated with this class
+ * @class_mutex - mutex to protect the children, devices, and interfaces lists.
+ * @class - pointer back to the struct class that this structure is associated
+ * with.
+ *
+ * This structure is the one that is the actual kobject allowing struct
+ * class to be statically allocated safely. Nothing outside of the driver
+ * core should ever touch these fields.
+ */
+struct class_private {
+ struct kset class_subsys;
+ struct list_head class_devices;
+ struct list_head class_interfaces;
+ struct kset class_dirs;
+ struct mutex class_mutex;
+ struct class *class;
+};
+#define to_class(obj) \
+ container_of(obj, struct class_private, class_subsys.kobj)
+
/* initialisation functions */
extern int devices_init(void);
extern int buses_init(void);
diff --git a/drivers/base/class.c b/drivers/base/class.c
index e085af0ff94f..839d27cecb36 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -18,20 +18,20 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/genhd.h>
+#include <linux/mutex.h>
#include "base.h"
#define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)
-#define to_class(obj) container_of(obj, struct class, subsys.kobj)
static ssize_t class_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct class_attribute *class_attr = to_class_attr(attr);
- struct class *dc = to_class(kobj);
+ struct class_private *cp = to_class(kobj);
ssize_t ret = -EIO;
if (class_attr->show)
- ret = class_attr->show(dc, buf);
+ ret = class_attr->show(cp->class, buf);
return ret;
}
@@ -39,17 +39,18 @@ static ssize_t class_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
struct class_attribute *class_attr = to_class_attr(attr);
- struct class *dc = to_class(kobj);
+ struct class_private *cp = to_class(kobj);
ssize_t ret = -EIO;
if (class_attr->store)
- ret = class_attr->store(dc, buf, count);
+ ret = class_attr->store(cp->class, buf, count);
return ret;
}
static void class_release(struct kobject *kobj)
{
- struct class *class = to_class(kobj);
+ struct class_private *cp = to_class(kobj);
+ struct class *class = cp->class;
pr_debug("class '%s': release.\n", class->name);
@@ -70,7 +71,7 @@ static struct kobj_type class_ktype = {
.release = class_release,
};
-/* Hotplug events for classes go to the class_obj subsys */
+/* Hotplug events for classes go to the class class_subsys */
static struct kset *class_kset;
@@ -78,7 +79,8 @@ int class_create_file(struct class *cls, const struct class_attribute *attr)
{
int error;
if (cls)
- error = sysfs_create_file(&cls->subsys.kobj, &attr->attr);
+ error = sysfs_create_file(&cls->p->class_subsys.kobj,
+ &attr->attr);
else
error = -EINVAL;
return error;
@@ -87,21 +89,20 @@ int class_create_file(struct class *cls, const struct class_attribute *attr)
void class_remove_file(struct class *cls, const struct class_attribute *attr)
{
if (cls)
- sysfs_remove_file(&cls->subsys.kobj, &attr->attr);
+ sysfs_remove_file(&cls->p->class_subsys.kobj, &attr->attr);
}
static struct class *class_get(struct class *cls)
{
if (cls)
- return container_of(kset_get(&cls->subsys),
- struct class, subsys);
- return NULL;
+ kset_get(&cls->p->class_subsys);
+ return cls;
}
static void class_put(struct class *cls)
{
if (cls)
- kset_put(&cls->subsys);
+ kset_put(&cls->p->class_subsys);
}
static int add_class_attrs(struct class *cls)
@@ -134,42 +135,57 @@ static void remove_class_attrs(struct class *cls)
}
}
-int class_register(struct class *cls)
+int __class_register(struct class *cls, struct lock_class_key *key)
{
+ struct class_private *cp;
int error;
pr_debug("device class '%s': registering\n", cls->name);
- INIT_LIST_HEAD(&cls->devices);
- INIT_LIST_HEAD(&cls->interfaces);
- kset_init(&cls->class_dirs);
- init_MUTEX(&cls->sem);
- error = kobject_set_name(&cls->subsys.kobj, "%s", cls->name);
- if (error)
+ cp = kzalloc(sizeof(*cp), GFP_KERNEL);
+ if (!cp)
+ return -ENOMEM;
+ INIT_LIST_HEAD(&cp->class_devices);
+ INIT_LIST_HEAD(&cp->class_interfaces);
+ kset_init(&cp->class_dirs);
+ __mutex_init(&cp->class_mutex, "struct class mutex", key);
+ error = kobject_set_name(&cp->class_subsys.kobj, "%s", cls->name);
+ if (error) {
+ kfree(cp);
return error;
+ }
+
+ /* set the default /sys/dev directory for devices of this class */
+ if (!cls->dev_kobj)
+ cls->dev_kobj = sysfs_dev_char_kobj;
#if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK)
/* let the block class directory show up in the root of sysfs */
if (cls != &block_class)
- cls->subsys.kobj.kset = class_kset;
+ cp->class_subsys.kobj.kset = class_kset;
#else
- cls->subsys.kobj.kset = class_kset;
+ cp->class_subsys.kobj.kset = class_kset;
#endif
- cls->subsys.kobj.ktype = &class_ktype;
+ cp->class_subsys.kobj.ktype = &class_ktype;
+ cp->class = cls;
+ cls->p = cp;
- error = kset_register(&cls->subsys);
- if (!error) {
- error = add_class_attrs(class_get(cls));
- class_put(cls);
+ error = kset_register(&cp->class_subsys);
+ if (error) {
+ kfree(cp);
+ return error;
}
+ error = add_class_attrs(class_get(cls));
+ class_put(cls);
return error;
}
+EXPORT_SYMBOL_GPL(__class_register);
void class_unregister(struct class *cls)
{
pr_debug("device class '%s': unregistering\n", cls->name);
remove_class_attrs(cls);
- kset_unregister(&cls->subsys);
+ kset_unregister(&cls->p->class_subsys);
}
static void class_create_release(struct class *cls)
@@ -189,7 +205,8 @@ static void class_create_release(struct class *cls)
* Note, the pointer created here is to be destroyed when finished by
* making a call to class_destroy().
*/
-struct class *class_create(struct module *owner, const char *name)
+struct class *__class_create(struct module *owner, const char *name,
+ struct lock_class_key *key)
{
struct class *cls;
int retval;
@@ -204,7 +221,7 @@ struct class *class_create(struct module *owner, const char *name)
cls->owner = owner;
cls->class_release = class_create_release;
- retval = class_register(cls);
+ retval = __class_register(cls, key);
if (retval)
goto error;
@@ -214,6 +231,7 @@ error:
kfree(cls);
return ERR_PTR(retval);
}
+EXPORT_SYMBOL_GPL(__class_create);
/**
* class_destroy - destroys a struct class structure
@@ -252,39 +270,44 @@ char *make_class_name(const char *name, struct kobject *kobj)
/**
* class_for_each_device - device iterator
* @class: the class we're iterating
+ * @start: the device to start with in the list, if any.
* @data: data for the callback
* @fn: function to be called for each device
*
* Iterate over @class's list of devices, and call @fn for each,
- * passing it @data.
+ * passing it @data. If @start is set, the list iteration will start
+ * there, otherwise if it is NULL, the iteration starts at the
+ * beginning of the list.
*
* We check the return of @fn each time. If it returns anything
* other than 0, we break out and return that value.
*
- * Note, we hold class->sem in this function, so it can not be
+ * Note, we hold class->class_mutex in this function, so it can not be
* re-acquired in @fn, otherwise it will self-deadlocking. For
* example, calls to add or remove class members would be verboten.
*/
-int class_for_each_device(struct class *class, void *data,
- int (*fn)(struct device *, void *))
+int class_for_each_device(struct class *class, struct device *start,
+ void *data, int (*fn)(struct device *, void *))
{
struct device *dev;
int error = 0;
if (!class)
return -EINVAL;
- down(&class->sem);
- list_for_each_entry(dev, &class->devices, node) {
+ mutex_lock(&class->p->class_mutex);
+ list_for_each_entry(dev, &class->p->class_devices, node) {
+ if (start) {
+ if (start == dev)
+ start = NULL;
+ continue;
+ }
dev = get_device(dev);
- if (dev) {
- error = fn(dev, data);
- put_device(dev);
- } else
- error = -ENODEV;
+ error = fn(dev, data);
+ put_device(dev);
if (error)
break;
}
- up(&class->sem);
+ mutex_unlock(&class->p->class_mutex);
return error;
}
@@ -293,6 +316,7 @@ EXPORT_SYMBOL_GPL(class_for_each_device);
/**
* class_find_device - device iterator for locating a particular device
* @class: the class we're iterating
+ * @start: Device to begin with
* @data: data for the match function
* @match: function to check device
*
@@ -306,12 +330,13 @@ EXPORT_SYMBOL_GPL(class_for_each_device);
*
* Note, you will need to drop the reference with put_device() after use.
*
- * We hold class->sem in this function, so it can not be
+ * We hold class->class_mutex in this function, so it can not be
* re-acquired in @match, otherwise it will self-deadlocking. For
* example, calls to add or remove class members would be verboten.
*/
-struct device *class_find_device(struct class *class, void *data,
- int (*match)(struct device *, void *))
+struct device *class_find_device(struct class *class, struct device *start,
+ void *data,
+ int (*match)(struct device *, void *))
{
struct device *dev;
int found = 0;
@@ -319,19 +344,21 @@ struct device *class_find_device(struct class *class, void *data,
if (!class)
return NULL;
- down(&class->sem);
- list_for_each_entry(dev, &class->devices, node) {
+ mutex_lock(&class->p->class_mutex);
+ list_for_each_entry(dev, &class->p->class_devices, node) {
+ if (start) {
+ if (start == dev)
+ start = NULL;
+ continue;
+ }
dev = get_device(dev);
- if (dev) {
- if (match(dev, data)) {
- found = 1;
- break;
- } else
- put_device(dev);
- } else
+ if (match(dev, data)) {
+ found = 1;
break;
+ } else
+ put_device(dev);
}
- up(&class->sem);
+ mutex_unlock(&class->p->class_mutex);
return found ? dev : NULL;
}
@@ -349,13 +376,13 @@ int class_interface_register(struct class_interface *class_intf)
if (!parent)
return -EINVAL;
- down(&parent->sem);
- list_add_tail(&class_intf->node, &parent->interfaces);
+ mutex_lock(&parent->p->class_mutex);
+ list_add_tail(&class_intf->node, &parent->p->class_interfaces);
if (class_intf->add_dev) {
- list_for_each_entry(dev, &parent->devices, node)
+ list_for_each_entry(dev, &parent->p->class_devices, node)
class_intf->add_dev(dev, class_intf);
}
- up(&parent->sem);
+ mutex_unlock(&parent->p->class_mutex);
return 0;
}
@@ -368,13 +395,13 @@ void class_interface_unregister(struct class_interface *class_intf)
if (!parent)
return;
- down(&parent->sem);
+ mutex_lock(&parent->p->class_mutex);
list_del_init(&class_intf->node);
if (class_intf->remove_dev) {
- list_for_each_entry(dev, &parent->devices, node)
+ list_for_each_entry(dev, &parent->p->class_devices, node)
class_intf->remove_dev(dev, class_intf);
}
- up(&parent->sem);
+ mutex_unlock(&parent->p->class_mutex);
class_put(parent);
}
@@ -389,9 +416,7 @@ int __init classes_init(void)
EXPORT_SYMBOL_GPL(class_create_file);
EXPORT_SYMBOL_GPL(class_remove_file);
-EXPORT_SYMBOL_GPL(class_register);
EXPORT_SYMBOL_GPL(class_unregister);
-EXPORT_SYMBOL_GPL(class_create);
EXPORT_SYMBOL_GPL(class_destroy);
EXPORT_SYMBOL_GPL(class_interface_register);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 422cfcad486d..90621a470abc 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -21,12 +21,16 @@
#include <linux/genhd.h>
#include <linux/kallsyms.h>
#include <linux/semaphore.h>
+#include <linux/mutex.h>
#include "base.h"
#include "power/power.h"
int (*platform_notify)(struct device *dev) = NULL;
int (*platform_notify_remove)(struct device *dev) = NULL;
+static struct kobject *dev_kobj;
+struct kobject *sysfs_dev_char_kobj;
+struct kobject *sysfs_dev_block_kobj;
#ifdef CONFIG_BLOCK
static inline int device_is_not_partition(struct device *dev)
@@ -465,6 +469,7 @@ EXPORT_SYMBOL_GPL(device_create_bin_file);
*/
void device_remove_bin_file(struct device *dev, struct bin_attribute *attr)
{
+ /* might_sleep(); */
if (dev)
sysfs_remove_bin_file(&dev->kobj, attr);
}
@@ -548,7 +553,7 @@ static struct kobject *get_device_parent(struct device *dev,
{
/* class devices without a parent live in /sys/class/<classname>/ */
if (dev->class && (!parent || parent->class != dev->class))
- return &dev->class->subsys.kobj;
+ return &dev->class->p->class_subsys.kobj;
/* all other devices keep their parent */
else if (parent)
return &parent->kobj;
@@ -594,13 +599,13 @@ static struct kobject *get_device_parent(struct device *dev,
parent_kobj = &parent->kobj;
/* find our class-directory at the parent and reference it */
- spin_lock(&dev->class->class_dirs.list_lock);
- list_for_each_entry(k, &dev->class->class_dirs.list, entry)
+ spin_lock(&dev->class->p->class_dirs.list_lock);
+ list_for_each_entry(k, &dev->class->p->class_dirs.list, entry)
if (k->parent == parent_kobj) {
kobj = kobject_get(k);
break;
}
- spin_unlock(&dev->class->class_dirs.list_lock);
+ spin_unlock(&dev->class->p->class_dirs.list_lock);
if (kobj)
return kobj;
@@ -608,7 +613,7 @@ static struct kobject *get_device_parent(struct device *dev,
k = kobject_create();
if (!k)
return NULL;
- k->kset = &dev->class->class_dirs;
+ k->kset = &dev->class->p->class_dirs;
retval = kobject_add(k, parent_kobj, "%s", dev->class->name);
if (retval < 0) {
kobject_put(k);
@@ -627,7 +632,7 @@ static void cleanup_glue_dir(struct device *dev, struct kobject *glue_dir)
{
/* see if we live in a "glue" directory */
if (!glue_dir || !dev->class ||
- glue_dir->kset != &dev->class->class_dirs)
+ glue_dir->kset != &dev->class->p->class_dirs)
return;
kobject_put(glue_dir);
@@ -654,17 +659,18 @@ static int device_add_class_symlinks(struct device *dev)
if (!dev->class)
return 0;
- error = sysfs_create_link(&dev->kobj, &dev->class->subsys.kobj,
+ error = sysfs_create_link(&dev->kobj,
+ &dev->class->p->class_subsys.kobj,
"subsystem");
if (error)
goto out;
#ifdef CONFIG_SYSFS_DEPRECATED
/* stacked class devices need a symlink in the class directory */
- if (dev->kobj.parent != &dev->class->subsys.kobj &&
+ if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&
device_is_not_partition(dev)) {
- error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj,
- dev->bus_id);
+ error = sysfs_create_link(&dev->class->p->class_subsys.kobj,
+ &dev->kobj, dev->bus_id);
if (error)
goto out_subsys;
}
@@ -701,13 +707,14 @@ out_device:
if (dev->parent && device_is_not_partition(dev))
sysfs_remove_link(&dev->kobj, "device");
out_busid:
- if (dev->kobj.parent != &dev->class->subsys.kobj &&
+ if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&
device_is_not_partition(dev))
- sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id);
+ sysfs_remove_link(&dev->class->p->class_subsys.kobj,
+ dev->bus_id);
#else
/* link in the class directory pointing to the device */
- error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj,
- dev->bus_id);
+ error = sysfs_create_link(&dev->class->p->class_subsys.kobj,
+ &dev->kobj, dev->bus_id);
if (error)
goto out_subsys;
@@ -720,7 +727,7 @@ out_busid:
return 0;
out_busid:
- sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id);
+ sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev->bus_id);
#endif
out_subsys:
@@ -746,14 +753,15 @@ static void device_remove_class_symlinks(struct device *dev)
sysfs_remove_link(&dev->kobj, "device");
}
- if (dev->kobj.parent != &dev->class->subsys.kobj &&
+ if (dev->kobj.parent != &dev->class->p->class_subsys.kobj &&
device_is_not_partition(dev))
- sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id);
+ sysfs_remove_link(&dev->class->p->class_subsys.kobj,
+ dev->bus_id);
#else
if (dev->parent && device_is_not_partition(dev))
sysfs_remove_link(&dev->kobj, "device");
- sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id);
+ sysfs_remove_link(&dev->class->p->class_subsys.kobj, dev->bus_id);
#endif
sysfs_remove_link(&dev->kobj, "subsystem");
@@ -762,6 +770,7 @@ static void device_remove_class_symlinks(struct device *dev)
/**
* dev_set_name - set a device name
* @dev: device
+ * @fmt: format string for the device's name
*/
int dev_set_name(struct device *dev, const char *fmt, ...)
{
@@ -775,6 +784,54 @@ int dev_set_name(struct device *dev, const char *fmt, ...)
EXPORT_SYMBOL_GPL(dev_set_name);
/**
+ * device_to_dev_kobj - select a /sys/dev/ directory for the device
+ * @dev: device
+ *
+ * By default we select char/ for new entries. Setting class->dev_obj
+ * to NULL prevents an entry from being created. class->dev_kobj must
+ * be set (or cleared) before any devices are registered to the class
+ * otherwise device_create_sys_dev_entry() and
+ * device_remove_sys_dev_entry() will disagree about the the presence
+ * of the link.
+ */
+static struct kobject *device_to_dev_kobj(struct device *dev)
+{
+ struct kobject *kobj;
+
+ if (dev->class)
+ kobj = dev->class->dev_kobj;
+ else
+ kobj = sysfs_dev_char_kobj;
+
+ return kobj;
+}
+
+static int device_create_sys_dev_entry(struct device *dev)
+{
+ struct kobject *kobj = device_to_dev_kobj(dev);
+ int error = 0;
+ char devt_str[15];
+
+ if (kobj) {
+ format_dev_t(devt_str, dev->devt);
+ error = sysfs_create_link(kobj, &dev->kobj, devt_str);
+ }
+
+ return error;
+}
+
+static void device_remove_sys_dev_entry(struct device *dev)
+{
+ struct kobject *kobj = device_to_dev_kobj(dev);
+ char devt_str[15];
+
+ if (kobj) {
+ format_dev_t(devt_str, dev->devt);
+ sysfs_remove_link(kobj, devt_str);
+ }
+}
+
+/**
* device_add - add device to device hierarchy.
* @dev: device.
*
@@ -828,6 +885,10 @@ int device_add(struct device *dev)
error = device_create_file(dev, &devt_attr);
if (error)
goto ueventattrError;
+
+ error = device_create_sys_dev_entry(dev);
+ if (error)
+ goto devtattrError;
}
error = device_add_class_symlinks(dev);
@@ -848,15 +909,16 @@ int device_add(struct device *dev)
klist_add_tail(&dev->knode_parent, &parent->klist_children);
if (dev->class) {
- down(&dev->class->sem);
+ mutex_lock(&dev->class->p->class_mutex);
/* tie the class to the device */
- list_add_tail(&dev->node, &dev->class->devices);
+ list_add_tail(&dev->node, &dev->class->p->class_devices);
/* notify any interfaces that the device is here */
- list_for_each_entry(class_intf, &dev->class->interfaces, node)
+ list_for_each_entry(class_intf,
+ &dev->class->p->class_interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
- up(&dev->class->sem);
+ mutex_unlock(&dev->class->p->class_mutex);
}
Done:
put_device(dev);
@@ -872,6 +934,9 @@ int device_add(struct device *dev)
device_remove_class_symlinks(dev);
SymlinkError:
if (MAJOR(dev->devt))
+ device_remove_sys_dev_entry(dev);
+ devtattrError:
+ if (MAJOR(dev->devt))
device_remove_file(dev, &devt_attr);
ueventattrError:
device_remove_file(dev, &uevent_attr);
@@ -947,19 +1012,22 @@ void device_del(struct device *dev)
device_pm_remove(dev);
if (parent)
klist_del(&dev->knode_parent);
- if (MAJOR(dev->devt))
+ if (MAJOR(dev->devt)) {
+ device_remove_sys_dev_entry(dev);
device_remove_file(dev, &devt_attr);
+ }
if (dev->class) {
device_remove_class_symlinks(dev);
- down(&dev->class->sem);
+ mutex_lock(&dev->class->p->class_mutex);
/* notify any interfaces that the device is now gone */
- list_for_each_entry(class_intf, &dev->class->interfaces, node)
+ list_for_each_entry(class_intf,
+ &dev->class->p->class_interfaces, node)
if (class_intf->remove_dev)
class_intf->remove_dev(dev, class_intf);
/* remove the device from the class list */
list_del_init(&dev->node);
- up(&dev->class->sem);
+ mutex_unlock(&dev->class->p->class_mutex);
}
device_remove_file(dev, &uevent_attr);
device_remove_attrs(dev);
@@ -1073,7 +1141,25 @@ int __init devices_init(void)
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
if (!devices_kset)
return -ENOMEM;
+ dev_kobj = kobject_create_and_add("dev", NULL);
+ if (!dev_kobj)
+ goto dev_kobj_err;
+ sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
+ if (!sysfs_dev_block_kobj)
+ goto block_kobj_err;
+ sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
+ if (!sysfs_dev_char_kobj)
+ goto char_kobj_err;
+
return 0;
+
+ char_kobj_err:
+ kobject_put(sysfs_dev_block_kobj);
+ block_kobj_err:
+ kobject_put(dev_kobj);
+ dev_kobj_err:
+ kset_unregister(devices_kset);
+ return -ENOMEM;
}
EXPORT_SYMBOL_GPL(device_for_each_child);
@@ -1194,40 +1280,6 @@ struct device *device_create_drvdata(struct class *class,
}
EXPORT_SYMBOL_GPL(device_create_drvdata);
-/**
- * device_create - creates a device and registers it with sysfs
- * @class: pointer to the struct class that this device should be registered to
- * @parent: pointer to the parent struct device of this new device, if any
- * @devt: the dev_t for the char device to be added
- * @fmt: string for the device's name
- *
- * This function can be used by char device classes. A struct device
- * will be created in sysfs, registered to the specified class.
- *
- * A "dev" file will be created, showing the dev_t for the device, if
- * the dev_t is not 0,0.
- * If a pointer to a parent struct device is passed in, the newly created
- * struct device will be a child of that device in sysfs.
- * The pointer to the struct device will be returned from the call.
- * Any further sysfs files that might be required can be created using this
- * pointer.
- *
- * Note: the struct class passed to this function must have previously
- * been created with a call to class_create().
- */
-struct device *device_create(struct class *class, struct device *parent,
- dev_t devt, const char *fmt, ...)
-{
- va_list vargs;
- struct device *dev;
-
- va_start(vargs, fmt);
- dev = device_create_vargs(class, parent, devt, NULL, fmt, vargs);
- va_end(vargs);
- return dev;
-}
-EXPORT_SYMBOL_GPL(device_create);
-
static int __match_devt(struct device *dev, void *data)
{
dev_t *devt = data;
@@ -1247,7 +1299,7 @@ void device_destroy(struct class *class, dev_t devt)
{
struct device *dev;
- dev = class_find_device(class, &devt, __match_devt);
+ dev = class_find_device(class, NULL, &devt, __match_devt);
if (dev) {
put_device(dev);
device_unregister(dev);
@@ -1259,6 +1311,11 @@ EXPORT_SYMBOL_GPL(device_destroy);
* device_rename - renames a device
* @dev: the pointer to the struct device to be renamed
* @new_name: the new name of the device
+ *
+ * It is the responsibility of the caller to provide mutual
+ * exclusion between two different calls of device_rename
+ * on the same device to ensure that new_name is valid and
+ * won't conflict with other devices.
*/
int device_rename(struct device *dev, char *new_name)
{
@@ -1306,11 +1363,12 @@ int device_rename(struct device *dev, char *new_name)
}
#else
if (dev->class) {
- error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj,
- dev->bus_id);
+ error = sysfs_create_link(&dev->class->p->class_subsys.kobj,
+ &dev->kobj, dev->bus_id);
if (error)
goto out;
- sysfs_remove_link(&dev->class->subsys.kobj, old_device_name);
+ sysfs_remove_link(&dev->class->p->class_subsys.kobj,
+ old_device_name);
}
#endif
@@ -1446,4 +1504,7 @@ void device_shutdown(void)
dev->driver->shutdown(dev);
}
}
+ kobject_put(sysfs_dev_char_kobj);
+ kobject_put(sysfs_dev_block_kobj);
+ kobject_put(dev_kobj);
}
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index e38dfed41d80..5000402ae092 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -119,14 +119,14 @@ static ssize_t print_cpus_##type(struct sysdev_class *class, char *buf) \
{ \
return print_cpus_map(buf, &cpu_##type##_map); \
} \
-struct sysdev_class_attribute attr_##type##_map = \
+static struct sysdev_class_attribute attr_##type##_map = \
_SYSDEV_CLASS_ATTR(type, 0444, print_cpus_##type, NULL)
print_cpus_func(online);
print_cpus_func(possible);
print_cpus_func(present);
-struct sysdev_class_attribute *cpu_state_attr[] = {
+static struct sysdev_class_attribute *cpu_state_attr[] = {
&attr_online_map,
&attr_possible_map,
&attr_present_map,
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 9fd4a8534146..264b3a2cd860 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -257,7 +257,7 @@ firmware_data_write(struct kobject *kobj, struct bin_attribute *bin_attr,
if (retval)
goto out;
- memcpy(fw->data + offset, buffer, count);
+ memcpy((u8 *)fw->data + offset, buffer, count);
fw->size = max_t(size_t, offset + count, fw->size);
retval = count;
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 911ec600fe71..3f940393d6c7 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -453,6 +453,8 @@ int platform_driver_register(struct platform_driver *drv)
drv->driver.suspend = platform_drv_suspend;
if (drv->resume)
drv->driver.resume = platform_drv_resume;
+ if (drv->pm)
+ drv->driver.pm = &drv->pm->base;
return driver_register(&drv->driver);
}
EXPORT_SYMBOL_GPL(platform_driver_register);
@@ -560,7 +562,9 @@ static int platform_match(struct device *dev, struct device_driver *drv)
return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
}
-static int platform_suspend(struct device *dev, pm_message_t mesg)
+#ifdef CONFIG_PM_SLEEP
+
+static int platform_legacy_suspend(struct device *dev, pm_message_t mesg)
{
int ret = 0;
@@ -570,7 +574,7 @@ static int platform_suspend(struct device *dev, pm_message_t mesg)
return ret;
}
-static int platform_suspend_late(struct device *dev, pm_message_t mesg)
+static int platform_legacy_suspend_late(struct device *dev, pm_message_t mesg)
{
struct platform_driver *drv = to_platform_driver(dev->driver);
struct platform_device *pdev;
@@ -583,7 +587,7 @@ static int platform_suspend_late(struct device *dev, pm_message_t mesg)
return ret;
}
-static int platform_resume_early(struct device *dev)
+static int platform_legacy_resume_early(struct device *dev)
{
struct platform_driver *drv = to_platform_driver(dev->driver);
struct platform_device *pdev;
@@ -596,7 +600,7 @@ static int platform_resume_early(struct device *dev)
return ret;
}
-static int platform_resume(struct device *dev)
+static int platform_legacy_resume(struct device *dev)
{
int ret = 0;
@@ -606,15 +610,291 @@ static int platform_resume(struct device *dev)
return ret;
}
+static int platform_pm_prepare(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (drv && drv->pm && drv->pm->prepare)
+ ret = drv->pm->prepare(dev);
+
+ return ret;
+}
+
+static void platform_pm_complete(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+
+ if (drv && drv->pm && drv->pm->complete)
+ drv->pm->complete(dev);
+}
+
+#ifdef CONFIG_SUSPEND
+
+static int platform_pm_suspend(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->suspend)
+ ret = drv->pm->suspend(dev);
+ } else {
+ ret = platform_legacy_suspend(dev, PMSG_SUSPEND);
+ }
+
+ return ret;
+}
+
+static int platform_pm_suspend_noirq(struct device *dev)
+{
+ struct platform_driver *pdrv;
+ int ret = 0;
+
+ if (!dev->driver)
+ return 0;
+
+ pdrv = to_platform_driver(dev->driver);
+ if (pdrv->pm) {
+ if (pdrv->pm->suspend_noirq)
+ ret = pdrv->pm->suspend_noirq(dev);
+ } else {
+ ret = platform_legacy_suspend_late(dev, PMSG_SUSPEND);
+ }
+
+ return ret;
+}
+
+static int platform_pm_resume(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->resume)
+ ret = drv->pm->resume(dev);
+ } else {
+ ret = platform_legacy_resume(dev);
+ }
+
+ return ret;
+}
+
+static int platform_pm_resume_noirq(struct device *dev)
+{
+ struct platform_driver *pdrv;
+ int ret = 0;
+
+ if (!dev->driver)
+ return 0;
+
+ pdrv = to_platform_driver(dev->driver);
+ if (pdrv->pm) {
+ if (pdrv->pm->resume_noirq)
+ ret = pdrv->pm->resume_noirq(dev);
+ } else {
+ ret = platform_legacy_resume_early(dev);
+ }
+
+ return ret;
+}
+
+#else /* !CONFIG_SUSPEND */
+
+#define platform_pm_suspend NULL
+#define platform_pm_resume NULL
+#define platform_pm_suspend_noirq NULL
+#define platform_pm_resume_noirq NULL
+
+#endif /* !CONFIG_SUSPEND */
+
+#ifdef CONFIG_HIBERNATION
+
+static int platform_pm_freeze(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (!drv)
+ return 0;
+
+ if (drv->pm) {
+ if (drv->pm->freeze)
+ ret = drv->pm->freeze(dev);
+ } else {
+ ret = platform_legacy_suspend(dev, PMSG_FREEZE);
+ }
+
+ return ret;
+}
+
+static int platform_pm_freeze_noirq(struct device *dev)
+{
+ struct platform_driver *pdrv;
+ int ret = 0;
+
+ if (!dev->driver)
+ return 0;
+
+ pdrv = to_platform_driver(dev->driver);
+ if (pdrv->pm) {
+ if (pdrv->pm->freeze_noirq)
+ ret = pdrv->pm->freeze_noirq(dev);
+ } else {
+ ret = platform_legacy_suspend_late(dev, PMSG_FREEZE);
+ }
+
+ return ret;
+}
+
+static int platform_pm_thaw(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->thaw)
+ ret = drv->pm->thaw(dev);
+ } else {
+ ret = platform_legacy_resume(dev);
+ }
+
+ return ret;
+}
+
+static int platform_pm_thaw_noirq(struct device *dev)
+{
+ struct platform_driver *pdrv;
+ int ret = 0;
+
+ if (!dev->driver)
+ return 0;
+
+ pdrv = to_platform_driver(dev->driver);
+ if (pdrv->pm) {
+ if (pdrv->pm->thaw_noirq)
+ ret = pdrv->pm->thaw_noirq(dev);
+ } else {
+ ret = platform_legacy_resume_early(dev);
+ }
+
+ return ret;
+}
+
+static int platform_pm_poweroff(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->poweroff)
+ ret = drv->pm->poweroff(dev);
+ } else {
+ ret = platform_legacy_suspend(dev, PMSG_HIBERNATE);
+ }
+
+ return ret;
+}
+
+static int platform_pm_poweroff_noirq(struct device *dev)
+{
+ struct platform_driver *pdrv;
+ int ret = 0;
+
+ if (!dev->driver)
+ return 0;
+
+ pdrv = to_platform_driver(dev->driver);
+ if (pdrv->pm) {
+ if (pdrv->pm->poweroff_noirq)
+ ret = pdrv->pm->poweroff_noirq(dev);
+ } else {
+ ret = platform_legacy_suspend_late(dev, PMSG_HIBERNATE);
+ }
+
+ return ret;
+}
+
+static int platform_pm_restore(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int ret = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->restore)
+ ret = drv->pm->restore(dev);
+ } else {
+ ret = platform_legacy_resume(dev);
+ }
+
+ return ret;
+}
+
+static int platform_pm_restore_noirq(struct device *dev)
+{
+ struct platform_driver *pdrv;
+ int ret = 0;
+
+ if (!dev->driver)
+ return 0;
+
+ pdrv = to_platform_driver(dev->driver);
+ if (pdrv->pm) {
+ if (pdrv->pm->restore_noirq)
+ ret = pdrv->pm->restore_noirq(dev);
+ } else {
+ ret = platform_legacy_resume_early(dev);
+ }
+
+ return ret;
+}
+
+#else /* !CONFIG_HIBERNATION */
+
+#define platform_pm_freeze NULL
+#define platform_pm_thaw NULL
+#define platform_pm_poweroff NULL
+#define platform_pm_restore NULL
+#define platform_pm_freeze_noirq NULL
+#define platform_pm_thaw_noirq NULL
+#define platform_pm_poweroff_noirq NULL
+#define platform_pm_restore_noirq NULL
+
+#endif /* !CONFIG_HIBERNATION */
+
+struct pm_ext_ops platform_pm_ops = {
+ .base = {
+ .prepare = platform_pm_prepare,
+ .complete = platform_pm_complete,
+ .suspend = platform_pm_suspend,
+ .resume = platform_pm_resume,
+ .freeze = platform_pm_freeze,
+ .thaw = platform_pm_thaw,
+ .poweroff = platform_pm_poweroff,
+ .restore = platform_pm_restore,
+ },
+ .suspend_noirq = platform_pm_suspend_noirq,
+ .resume_noirq = platform_pm_resume_noirq,
+ .freeze_noirq = platform_pm_freeze_noirq,
+ .thaw_noirq = platform_pm_thaw_noirq,
+ .poweroff_noirq = platform_pm_poweroff_noirq,
+ .restore_noirq = platform_pm_restore_noirq,
+};
+
+#define PLATFORM_PM_OPS_PTR &platform_pm_ops
+
+#else /* !CONFIG_PM_SLEEP */
+
+#define PLATFORM_PM_OPS_PTR NULL
+
+#endif /* !CONFIG_PM_SLEEP */
+
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
- .suspend = platform_suspend,
- .suspend_late = platform_suspend_late,
- .resume_early = platform_resume_early,
- .resume = platform_resume,
+ .pm = PLATFORM_PM_OPS_PTR,
};
EXPORT_SYMBOL_GPL(platform_bus_type);
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 45cc3d9eacb8..3250c5257b74 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -12,11 +12,9 @@
* and add it to the list of power-controlled devices. sysfs entries for
* controlling device power management will also be added.
*
- * A different set of lists than the global subsystem list are used to
- * keep track of power info because we use different lists to hold
- * devices based on what stage of the power management process they
- * are in. The power domain dependencies may also differ from the
- * ancestral dependencies that the subsystem list maintains.
+ * A separate list is used for keeping track of power info, because the power
+ * domain dependencies may differ from the ancestral dependencies that the
+ * subsystem list maintains.
*/
#include <linux/device.h>
@@ -30,31 +28,40 @@
#include "power.h"
/*
- * The entries in the dpm_active list are in a depth first order, simply
+ * The entries in the dpm_list list are in a depth first order, simply
* because children are guaranteed to be discovered after parents, and
* are inserted at the back of the list on discovery.
*
- * All the other lists are kept in the same order, for consistency.
- * However the lists aren't always traversed in the same order.
- * Semaphores must be acquired from the top (i.e., front) down
- * and released in the opposite order. Devices must be suspended
- * from the bottom (i.e., end) up and resumed in the opposite order.
- * That way no parent will be suspended while it still has an active
- * child.
- *
* Since device_pm_add() may be called with a device semaphore held,
* we must never try to acquire a device semaphore while holding
* dpm_list_mutex.
*/
-LIST_HEAD(dpm_active);
-static LIST_HEAD(dpm_off);
-static LIST_HEAD(dpm_off_irq);
+LIST_HEAD(dpm_list);
static DEFINE_MUTEX(dpm_list_mtx);
-/* 'true' if all devices have been suspended, protected by dpm_list_mtx */
-static bool all_sleeping;
+/*
+ * Set once the preparation of devices for a PM transition has started, reset
+ * before starting to resume devices. Protected by dpm_list_mtx.
+ */
+static bool transition_started;
+
+/**
+ * device_pm_lock - lock the list of active devices used by the PM core
+ */
+void device_pm_lock(void)
+{
+ mutex_lock(&dpm_list_mtx);
+}
+
+/**
+ * device_pm_unlock - unlock the list of active devices used by the PM core
+ */
+void device_pm_unlock(void)
+{
+ mutex_unlock(&dpm_list_mtx);
+}
/**
* device_pm_add - add a device to the list of active devices
@@ -68,17 +75,25 @@ int device_pm_add(struct device *dev)
dev->bus ? dev->bus->name : "No Bus",
kobject_name(&dev->kobj));
mutex_lock(&dpm_list_mtx);
- if ((dev->parent && dev->parent->power.sleeping) || all_sleeping) {
- if (dev->parent->power.sleeping)
- dev_warn(dev, "parent %s is sleeping\n",
+ if (dev->parent) {
+ if (dev->parent->power.status >= DPM_SUSPENDING) {
+ dev_warn(dev, "parent %s is sleeping, will not add\n",
dev->parent->bus_id);
- else
- dev_warn(dev, "all devices are sleeping\n");
+ WARN_ON(true);
+ }
+ } else if (transition_started) {
+ /*
+ * We refuse to register parentless devices while a PM
+ * transition is in progress in order to avoid leaving them
+ * unhandled down the road
+ */
WARN_ON(true);
}
error = dpm_sysfs_add(dev);
- if (!error)
- list_add_tail(&dev->power.entry, &dpm_active);
+ if (!error) {
+ dev->power.status = DPM_ON;
+ list_add_tail(&dev->power.entry, &dpm_list);
+ }
mutex_unlock(&dpm_list_mtx);
return error;
}
@@ -100,73 +115,243 @@ void device_pm_remove(struct device *dev)
mutex_unlock(&dpm_list_mtx);
}
+/**
+ * pm_op - execute the PM operation appropiate for given PM event
+ * @dev: Device.
+ * @ops: PM operations to choose from.
+ * @state: PM transition of the system being carried out.
+ */
+static int pm_op(struct device *dev, struct pm_ops *ops, pm_message_t state)
+{
+ int error = 0;
+
+ switch (state.event) {
+#ifdef CONFIG_SUSPEND
+ case PM_EVENT_SUSPEND:
+ if (ops->suspend) {
+ error = ops->suspend(dev);
+ suspend_report_result(ops->suspend, error);
+ }
+ break;
+ case PM_EVENT_RESUME:
+ if (ops->resume) {
+ error = ops->resume(dev);
+ suspend_report_result(ops->resume, error);
+ }
+ break;
+#endif /* CONFIG_SUSPEND */
+#ifdef CONFIG_HIBERNATION
+ case PM_EVENT_FREEZE:
+ case PM_EVENT_QUIESCE:
+ if (ops->freeze) {
+ error = ops->freeze(dev);
+ suspend_report_result(ops->freeze, error);
+ }
+ break;
+ case PM_EVENT_HIBERNATE:
+ if (ops->poweroff) {
+ error = ops->poweroff(dev);
+ suspend_report_result(ops->poweroff, error);
+ }
+ break;
+ case PM_EVENT_THAW:
+ case PM_EVENT_RECOVER:
+ if (ops->thaw) {
+ error = ops->thaw(dev);
+ suspend_report_result(ops->thaw, error);
+ }
+ break;
+ case PM_EVENT_RESTORE:
+ if (ops->restore) {
+ error = ops->restore(dev);
+ suspend_report_result(ops->restore, error);
+ }
+ break;
+#endif /* CONFIG_HIBERNATION */
+ default:
+ error = -EINVAL;
+ }
+ return error;
+}
+
+/**
+ * pm_noirq_op - execute the PM operation appropiate for given PM event
+ * @dev: Device.
+ * @ops: PM operations to choose from.
+ * @state: PM transition of the system being carried out.
+ *
+ * The operation is executed with interrupts disabled by the only remaining
+ * functional CPU in the system.
+ */
+static int pm_noirq_op(struct device *dev, struct pm_ext_ops *ops,
+ pm_message_t state)
+{
+ int error = 0;
+
+ switch (state.event) {
+#ifdef CONFIG_SUSPEND
+ case PM_EVENT_SUSPEND:
+ if (ops->suspend_noirq) {
+ error = ops->suspend_noirq(dev);
+ suspend_report_result(ops->suspend_noirq, error);
+ }
+ break;
+ case PM_EVENT_RESUME:
+ if (ops->resume_noirq) {
+ error = ops->resume_noirq(dev);
+ suspend_report_result(ops->resume_noirq, error);
+ }
+ break;
+#endif /* CONFIG_SUSPEND */
+#ifdef CONFIG_HIBERNATION
+ case PM_EVENT_FREEZE:
+ case PM_EVENT_QUIESCE:
+ if (ops->freeze_noirq) {
+ error = ops->freeze_noirq(dev);
+ suspend_report_result(ops->freeze_noirq, error);
+ }
+ break;
+ case PM_EVENT_HIBERNATE:
+ if (ops->poweroff_noirq) {
+ error = ops->poweroff_noirq(dev);
+ suspend_report_result(ops->poweroff_noirq, error);
+ }
+ break;
+ case PM_EVENT_THAW:
+ case PM_EVENT_RECOVER:
+ if (ops->thaw_noirq) {
+ error = ops->thaw_noirq(dev);
+ suspend_report_result(ops->thaw_noirq, error);
+ }
+ break;
+ case PM_EVENT_RESTORE:
+ if (ops->restore_noirq) {
+ error = ops->restore_noirq(dev);
+ suspend_report_result(ops->restore_noirq, error);
+ }
+ break;
+#endif /* CONFIG_HIBERNATION */
+ default:
+ error = -EINVAL;
+ }
+ return error;
+}
+
+static char *pm_verb(int event)
+{
+ switch (event) {
+ case PM_EVENT_SUSPEND:
+ return "suspend";
+ case PM_EVENT_RESUME:
+ return "resume";
+ case PM_EVENT_FREEZE:
+ return "freeze";
+ case PM_EVENT_QUIESCE:
+ return "quiesce";
+ case PM_EVENT_HIBERNATE:
+ return "hibernate";
+ case PM_EVENT_THAW:
+ return "thaw";
+ case PM_EVENT_RESTORE:
+ return "restore";
+ case PM_EVENT_RECOVER:
+ return "recover";
+ default:
+ return "(unknown PM event)";
+ }
+}
+
+static void pm_dev_dbg(struct device *dev, pm_message_t state, char *info)
+{
+ dev_dbg(dev, "%s%s%s\n", info, pm_verb(state.event),
+ ((state.event & PM_EVENT_SLEEP) && device_may_wakeup(dev)) ?
+ ", may wakeup" : "");
+}
+
+static void pm_dev_err(struct device *dev, pm_message_t state, char *info,
+ int error)
+{
+ printk(KERN_ERR "PM: Device %s failed to %s%s: error %d\n",
+ kobject_name(&dev->kobj), pm_verb(state.event), info, error);
+}
+
/*------------------------- Resume routines -------------------------*/
/**
- * resume_device_early - Power on one device (early resume).
+ * resume_device_noirq - Power on one device (early resume).
* @dev: Device.
+ * @state: PM transition of the system being carried out.
*
* Must be called with interrupts disabled.
*/
-static int resume_device_early(struct device *dev)
+static int resume_device_noirq(struct device *dev, pm_message_t state)
{
int error = 0;
TRACE_DEVICE(dev);
TRACE_RESUME(0);
- if (dev->bus && dev->bus->resume_early) {
- dev_dbg(dev, "EARLY resume\n");
+ if (!dev->bus)
+ goto End;
+
+ if (dev->bus->pm) {
+ pm_dev_dbg(dev, state, "EARLY ");
+ error = pm_noirq_op(dev, dev->bus->pm, state);
+ } else if (dev->bus->resume_early) {
+ pm_dev_dbg(dev, state, "legacy EARLY ");
error = dev->bus->resume_early(dev);
}
-
+ End:
TRACE_RESUME(error);
return error;
}
/**
* dpm_power_up - Power on all regular (non-sysdev) devices.
+ * @state: PM transition of the system being carried out.
*
- * Walk the dpm_off_irq list and power each device up. This
- * is used for devices that required they be powered down with
- * interrupts disabled. As devices are powered on, they are moved
- * to the dpm_off list.
+ * Execute the appropriate "noirq resume" callback for all devices marked
+ * as DPM_OFF_IRQ.
*
* Must be called with interrupts disabled and only one CPU running.
*/
-static void dpm_power_up(void)
+static void dpm_power_up(pm_message_t state)
{
+ struct device *dev;
- while (!list_empty(&dpm_off_irq)) {
- struct list_head *entry = dpm_off_irq.next;
- struct device *dev = to_device(entry);
+ list_for_each_entry(dev, &dpm_list, power.entry)
+ if (dev->power.status > DPM_OFF) {
+ int error;
- list_move_tail(entry, &dpm_off);
- resume_device_early(dev);
- }
+ dev->power.status = DPM_OFF;
+ error = resume_device_noirq(dev, state);
+ if (error)
+ pm_dev_err(dev, state, " early", error);
+ }
}
/**
* device_power_up - Turn on all devices that need special attention.
+ * @state: PM transition of the system being carried out.
*
* Power on system devices, then devices that required we shut them down
* with interrupts disabled.
*
* Must be called with interrupts disabled.
*/
-void device_power_up(void)
+void device_power_up(pm_message_t state)
{
sysdev_resume();
- dpm_power_up();
+ dpm_power_up(state);
}
EXPORT_SYMBOL_GPL(device_power_up);
/**
* resume_device - Restore state for one device.
* @dev: Device.
- *
+ * @state: PM transition of the system being carried out.
*/
-static int resume_device(struct device *dev)
+static int resume_device(struct device *dev, pm_message_t state)
{
int error = 0;
@@ -175,21 +360,40 @@ static int resume_device(struct device *dev)
down(&dev->sem);
- if (dev->bus && dev->bus->resume) {
- dev_dbg(dev,"resuming\n");
- error = dev->bus->resume(dev);
+ if (dev->bus) {
+ if (dev->bus->pm) {
+ pm_dev_dbg(dev, state, "");
+ error = pm_op(dev, &dev->bus->pm->base, state);
+ } else if (dev->bus->resume) {
+ pm_dev_dbg(dev, state, "legacy ");
+ error = dev->bus->resume(dev);
+ }
+ if (error)
+ goto End;
}
- if (!error && dev->type && dev->type->resume) {
- dev_dbg(dev,"resuming\n");
- error = dev->type->resume(dev);
+ if (dev->type) {
+ if (dev->type->pm) {
+ pm_dev_dbg(dev, state, "type ");
+ error = pm_op(dev, dev->type->pm, state);
+ } else if (dev->type->resume) {
+ pm_dev_dbg(dev, state, "legacy type ");
+ error = dev->type->resume(dev);
+ }
+ if (error)
+ goto End;
}
- if (!error && dev->class && dev->class->resume) {
- dev_dbg(dev,"class resume\n");
- error = dev->class->resume(dev);
+ if (dev->class) {
+ if (dev->class->pm) {
+ pm_dev_dbg(dev, state, "class ");
+ error = pm_op(dev, dev->class->pm, state);
+ } else if (dev->class->resume) {
+ pm_dev_dbg(dev, state, "legacy class ");
+ error = dev->class->resume(dev);
+ }
}
-
+ End:
up(&dev->sem);
TRACE_RESUME(error);
@@ -198,78 +402,161 @@ static int resume_device(struct device *dev)
/**
* dpm_resume - Resume every device.
+ * @state: PM transition of the system being carried out.
*
- * Resume the devices that have either not gone through
- * the late suspend, or that did go through it but also
- * went through the early resume.
+ * Execute the appropriate "resume" callback for all devices the status of
+ * which indicates that they are inactive.
+ */
+static void dpm_resume(pm_message_t state)
+{
+ struct list_head list;
+
+ INIT_LIST_HEAD(&list);
+ mutex_lock(&dpm_list_mtx);
+ transition_started = false;
+ while (!list_empty(&dpm_list)) {
+ struct device *dev = to_device(dpm_list.next);
+
+ get_device(dev);
+ if (dev->power.status >= DPM_OFF) {
+ int error;
+
+ dev->power.status = DPM_RESUMING;
+ mutex_unlock(&dpm_list_mtx);
+
+ error = resume_device(dev, state);
+
+ mutex_lock(&dpm_list_mtx);
+ if (error)
+ pm_dev_err(dev, state, "", error);
+ } else if (dev->power.status == DPM_SUSPENDING) {
+ /* Allow new children of the device to be registered */
+ dev->power.status = DPM_RESUMING;
+ }
+ if (!list_empty(&dev->power.entry))
+ list_move_tail(&dev->power.entry, &list);
+ put_device(dev);
+ }
+ list_splice(&list, &dpm_list);
+ mutex_unlock(&dpm_list_mtx);
+}
+
+/**
+ * complete_device - Complete a PM transition for given device
+ * @dev: Device.
+ * @state: PM transition of the system being carried out.
+ */
+static void complete_device(struct device *dev, pm_message_t state)
+{
+ down(&dev->sem);
+
+ if (dev->class && dev->class->pm && dev->class->pm->complete) {
+ pm_dev_dbg(dev, state, "completing class ");
+ dev->class->pm->complete(dev);
+ }
+
+ if (dev->type && dev->type->pm && dev->type->pm->complete) {
+ pm_dev_dbg(dev, state, "completing type ");
+ dev->type->pm->complete(dev);
+ }
+
+ if (dev->bus && dev->bus->pm && dev->bus->pm->base.complete) {
+ pm_dev_dbg(dev, state, "completing ");
+ dev->bus->pm->base.complete(dev);
+ }
+
+ up(&dev->sem);
+}
+
+/**
+ * dpm_complete - Complete a PM transition for all devices.
+ * @state: PM transition of the system being carried out.
*
- * Take devices from the dpm_off_list, resume them,
- * and put them on the dpm_locked list.
+ * Execute the ->complete() callbacks for all devices that are not marked
+ * as DPM_ON.
*/
-static void dpm_resume(void)
+static void dpm_complete(pm_message_t state)
{
+ struct list_head list;
+
+ INIT_LIST_HEAD(&list);
mutex_lock(&dpm_list_mtx);
- all_sleeping = false;
- while(!list_empty(&dpm_off)) {
- struct list_head *entry = dpm_off.next;
- struct device *dev = to_device(entry);
+ while (!list_empty(&dpm_list)) {
+ struct device *dev = to_device(dpm_list.prev);
- list_move_tail(entry, &dpm_active);
- dev->power.sleeping = false;
- mutex_unlock(&dpm_list_mtx);
- resume_device(dev);
- mutex_lock(&dpm_list_mtx);
+ get_device(dev);
+ if (dev->power.status > DPM_ON) {
+ dev->power.status = DPM_ON;
+ mutex_unlock(&dpm_list_mtx);
+
+ complete_device(dev, state);
+
+ mutex_lock(&dpm_list_mtx);
+ }
+ if (!list_empty(&dev->power.entry))
+ list_move(&dev->power.entry, &list);
+ put_device(dev);
}
+ list_splice(&list, &dpm_list);
mutex_unlock(&dpm_list_mtx);
}
/**
* device_resume - Restore state of each device in system.
+ * @state: PM transition of the system being carried out.
*
* Resume all the devices, unlock them all, and allow new
* devices to be registered once again.
*/
-void device_resume(void)
+void device_resume(pm_message_t state)
{
might_sleep();
- dpm_resume();
+ dpm_resume(state);
+ dpm_complete(state);
}
EXPORT_SYMBOL_GPL(device_resume);
/*------------------------- Suspend routines -------------------------*/
-static inline char *suspend_verb(u32 event)
+/**
+ * resume_event - return a PM message representing the resume event
+ * corresponding to given sleep state.
+ * @sleep_state: PM message representing a sleep state.
+ */
+static pm_message_t resume_event(pm_message_t sleep_state)
{
- switch (event) {
- case PM_EVENT_SUSPEND: return "suspend";
- case PM_EVENT_FREEZE: return "freeze";
- case PM_EVENT_PRETHAW: return "prethaw";
- default: return "(unknown suspend event)";
+ switch (sleep_state.event) {
+ case PM_EVENT_SUSPEND:
+ return PMSG_RESUME;
+ case PM_EVENT_FREEZE:
+ case PM_EVENT_QUIESCE:
+ return PMSG_RECOVER;
+ case PM_EVENT_HIBERNATE:
+ return PMSG_RESTORE;
}
-}
-
-static void
-suspend_device_dbg(struct device *dev, pm_message_t state, char *info)
-{
- dev_dbg(dev, "%s%s%s\n", info, suspend_verb(state.event),
- ((state.event == PM_EVENT_SUSPEND) && device_may_wakeup(dev)) ?
- ", may wakeup" : "");
+ return PMSG_ON;
}
/**
- * suspend_device_late - Shut down one device (late suspend).
+ * suspend_device_noirq - Shut down one device (late suspend).
* @dev: Device.
- * @state: Power state device is entering.
+ * @state: PM transition of the system being carried out.
*
* This is called with interrupts off and only a single CPU running.
*/
-static int suspend_device_late(struct device *dev, pm_message_t state)
+static int suspend_device_noirq(struct device *dev, pm_message_t state)
{
int error = 0;
- if (dev->bus && dev->bus->suspend_late) {
- suspend_device_dbg(dev, state, "LATE ");
+ if (!dev->bus)
+ return 0;
+
+ if (dev->bus->pm) {
+ pm_dev_dbg(dev, state, "LATE ");
+ error = pm_noirq_op(dev, dev->bus->pm, state);
+ } else if (dev->bus->suspend_late) {
+ pm_dev_dbg(dev, state, "legacy LATE ");
error = dev->bus->suspend_late(dev, state);
suspend_report_result(dev->bus->suspend_late, error);
}
@@ -278,37 +565,30 @@ static int suspend_device_late(struct device *dev, pm_message_t state)
/**
* device_power_down - Shut down special devices.
- * @state: Power state to enter.
+ * @state: PM transition of the system being carried out.
*
- * Power down devices that require interrupts to be disabled
- * and move them from the dpm_off list to the dpm_off_irq list.
+ * Power down devices that require interrupts to be disabled.
* Then power down system devices.
*
* Must be called with interrupts disabled and only one CPU running.
*/
int device_power_down(pm_message_t state)
{
+ struct device *dev;
int error = 0;
- while (!list_empty(&dpm_off)) {
- struct list_head *entry = dpm_off.prev;
- struct device *dev = to_device(entry);
-
- error = suspend_device_late(dev, state);
+ list_for_each_entry_reverse(dev, &dpm_list, power.entry) {
+ error = suspend_device_noirq(dev, state);
if (error) {
- printk(KERN_ERR "Could not power down device %s: "
- "error %d\n",
- kobject_name(&dev->kobj), error);
+ pm_dev_err(dev, state, " late", error);
break;
}
- if (!list_empty(&dev->power.entry))
- list_move(&dev->power.entry, &dpm_off_irq);
+ dev->power.status = DPM_OFF_IRQ;
}
-
if (!error)
error = sysdev_suspend(state);
if (error)
- dpm_power_up();
+ dpm_power_up(resume_event(state));
return error;
}
EXPORT_SYMBOL_GPL(device_power_down);
@@ -316,7 +596,7 @@ EXPORT_SYMBOL_GPL(device_power_down);
/**
* suspend_device - Save state of one device.
* @dev: Device.
- * @state: Power state device is entering.
+ * @state: PM transition of the system being carried out.
*/
static int suspend_device(struct device *dev, pm_message_t state)
{
@@ -324,24 +604,43 @@ static int suspend_device(struct device *dev, pm_message_t state)
down(&dev->sem);
- if (dev->class && dev->class->suspend) {
- suspend_device_dbg(dev, state, "class ");
- error = dev->class->suspend(dev, state);
- suspend_report_result(dev->class->suspend, error);
+ if (dev->class) {
+ if (dev->class->pm) {
+ pm_dev_dbg(dev, state, "class ");
+ error = pm_op(dev, dev->class->pm, state);
+ } else if (dev->class->suspend) {
+ pm_dev_dbg(dev, state, "legacy class ");
+ error = dev->class->suspend(dev, state);
+ suspend_report_result(dev->class->suspend, error);
+ }
+ if (error)
+ goto End;
}
- if (!error && dev->type && dev->type->suspend) {
- suspend_device_dbg(dev, state, "type ");
- error = dev->type->suspend(dev, state);
- suspend_report_result(dev->type->suspend, error);
+ if (dev->type) {
+ if (dev->type->pm) {
+ pm_dev_dbg(dev, state, "type ");
+ error = pm_op(dev, dev->type->pm, state);
+ } else if (dev->type->suspend) {
+ pm_dev_dbg(dev, state, "legacy type ");
+ error = dev->type->suspend(dev, state);
+ suspend_report_result(dev->type->suspend, error);
+ }
+ if (error)
+ goto End;
}
- if (!error && dev->bus && dev->bus->suspend) {
- suspend_device_dbg(dev, state, "");
- error = dev->bus->suspend(dev, state);
- suspend_report_result(dev->bus->suspend, error);
+ if (dev->bus) {
+ if (dev->bus->pm) {
+ pm_dev_dbg(dev, state, "");
+ error = pm_op(dev, &dev->bus->pm->base, state);
+ } else if (dev->bus->suspend) {
+ pm_dev_dbg(dev, state, "legacy ");
+ error = dev->bus->suspend(dev, state);
+ suspend_report_result(dev->bus->suspend, error);
+ }
}
-
+ End:
up(&dev->sem);
return error;
@@ -349,67 +648,139 @@ static int suspend_device(struct device *dev, pm_message_t state)
/**
* dpm_suspend - Suspend every device.
- * @state: Power state to put each device in.
- *
- * Walk the dpm_locked list. Suspend each device and move it
- * to the dpm_off list.
+ * @state: PM transition of the system being carried out.
*
- * (For historical reasons, if it returns -EAGAIN, that used to mean
- * that the device would be called again with interrupts disabled.
- * These days, we use the "suspend_late()" callback for that, so we
- * print a warning and consider it an error).
+ * Execute the appropriate "suspend" callbacks for all devices.
*/
static int dpm_suspend(pm_message_t state)
{
+ struct list_head list;
int error = 0;
+ INIT_LIST_HEAD(&list);
mutex_lock(&dpm_list_mtx);
- while (!list_empty(&dpm_active)) {
- struct list_head *entry = dpm_active.prev;
- struct device *dev = to_device(entry);
+ while (!list_empty(&dpm_list)) {
+ struct device *dev = to_device(dpm_list.prev);
- WARN_ON(dev->parent && dev->parent->power.sleeping);
-
- dev->power.sleeping = true;
+ get_device(dev);
mutex_unlock(&dpm_list_mtx);
+
error = suspend_device(dev, state);
+
mutex_lock(&dpm_list_mtx);
if (error) {
- printk(KERN_ERR "Could not suspend device %s: "
- "error %d%s\n",
- kobject_name(&dev->kobj),
- error,
- (error == -EAGAIN ?
- " (please convert to suspend_late)" :
- ""));
- dev->power.sleeping = false;
+ pm_dev_err(dev, state, "", error);
+ put_device(dev);
break;
}
+ dev->power.status = DPM_OFF;
if (!list_empty(&dev->power.entry))
- list_move(&dev->power.entry, &dpm_off);
+ list_move(&dev->power.entry, &list);
+ put_device(dev);
}
- if (!error)
- all_sleeping = true;
+ list_splice(&list, dpm_list.prev);
mutex_unlock(&dpm_list_mtx);
+ return error;
+}
+
+/**
+ * prepare_device - Execute the ->prepare() callback(s) for given device.
+ * @dev: Device.
+ * @state: PM transition of the system being carried out.
+ */
+static int prepare_device(struct device *dev, pm_message_t state)
+{
+ int error = 0;
+
+ down(&dev->sem);
+
+ if (dev->bus && dev->bus->pm && dev->bus->pm->base.prepare) {
+ pm_dev_dbg(dev, state, "preparing ");
+ error = dev->bus->pm->base.prepare(dev);
+ suspend_report_result(dev->bus->pm->base.prepare, error);
+ if (error)
+ goto End;
+ }
+
+ if (dev->type && dev->type->pm && dev->type->pm->prepare) {
+ pm_dev_dbg(dev, state, "preparing type ");
+ error = dev->type->pm->prepare(dev);
+ suspend_report_result(dev->type->pm->prepare, error);
+ if (error)
+ goto End;
+ }
+
+ if (dev->class && dev->class->pm && dev->class->pm->prepare) {
+ pm_dev_dbg(dev, state, "preparing class ");
+ error = dev->class->pm->prepare(dev);
+ suspend_report_result(dev->class->pm->prepare, error);
+ }
+ End:
+ up(&dev->sem);
+
+ return error;
+}
+
+/**
+ * dpm_prepare - Prepare all devices for a PM transition.
+ * @state: PM transition of the system being carried out.
+ *
+ * Execute the ->prepare() callback for all devices.
+ */
+static int dpm_prepare(pm_message_t state)
+{
+ struct list_head list;
+ int error = 0;
+
+ INIT_LIST_HEAD(&list);
+ mutex_lock(&dpm_list_mtx);
+ transition_started = true;
+ while (!list_empty(&dpm_list)) {
+ struct device *dev = to_device(dpm_list.next);
+
+ get_device(dev);
+ dev->power.status = DPM_PREPARING;
+ mutex_unlock(&dpm_list_mtx);
+ error = prepare_device(dev, state);
+
+ mutex_lock(&dpm_list_mtx);
+ if (error) {
+ dev->power.status = DPM_ON;
+ if (error == -EAGAIN) {
+ put_device(dev);
+ continue;
+ }
+ printk(KERN_ERR "PM: Failed to prepare device %s "
+ "for power transition: error %d\n",
+ kobject_name(&dev->kobj), error);
+ put_device(dev);
+ break;
+ }
+ dev->power.status = DPM_SUSPENDING;
+ if (!list_empty(&dev->power.entry))
+ list_move_tail(&dev->power.entry, &list);
+ put_device(dev);
+ }
+ list_splice(&list, &dpm_list);
+ mutex_unlock(&dpm_list_mtx);
return error;
}
/**
* device_suspend - Save state and stop all devices in system.
- * @state: new power management state
+ * @state: PM transition of the system being carried out.
*
- * Prevent new devices from being registered, then lock all devices
- * and suspend them.
+ * Prepare and suspend all devices.
*/
int device_suspend(pm_message_t state)
{
int error;
might_sleep();
- error = dpm_suspend(state);
- if (error)
- device_resume();
+ error = dpm_prepare(state);
+ if (!error)
+ error = dpm_suspend(state);
return error;
}
EXPORT_SYMBOL_GPL(device_suspend);
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index a6894f2a4b99..a3252c0e2887 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -4,7 +4,7 @@
* main.c
*/
-extern struct list_head dpm_active; /* The active device list */
+extern struct list_head dpm_list; /* The active device list */
static inline struct device *to_device(struct list_head *entry)
{
diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c
index 2b4b392dcbc1..2aa6e8fc4def 100644
--- a/drivers/base/power/trace.c
+++ b/drivers/base/power/trace.c
@@ -153,7 +153,7 @@ EXPORT_SYMBOL(set_trace_device);
* it's not any guarantee, but it's a high _likelihood_ that
* the match is valid).
*/
-void generate_resume_trace(void *tracedata, unsigned int user)
+void generate_resume_trace(const void *tracedata, unsigned int user)
{
unsigned short lineno = *(unsigned short *)tracedata;
const char *file = *(const char **)(tracedata + 2);
@@ -188,13 +188,13 @@ static int show_file_hash(unsigned int value)
static int show_dev_hash(unsigned int value)
{
int match = 0;
- struct list_head * entry = dpm_active.prev;
+ struct list_head *entry = dpm_list.prev;
- while (entry != &dpm_active) {
+ while (entry != &dpm_list) {
struct device * dev = to_device(entry);
unsigned int hash = hash_string(DEVSEED, dev->bus_id, DEVHASH);
if (hash == value) {
- printk(" hash matches device %s\n", dev->bus_id);
+ dev_info(dev, "hash matches\n");
match++;
}
entry = entry->prev;
diff --git a/drivers/base/topology.c b/drivers/base/topology.c
index fdf4044d2e74..899982855776 100644
--- a/drivers/base/topology.c
+++ b/drivers/base/topology.c
@@ -40,6 +40,7 @@ static ssize_t show_##name(struct sys_device *dev, char *buf) \
return sprintf(buf, "%d\n", topology_##name(cpu)); \
}
+#if defined(topology_thread_siblings) || defined(topology_core_siblings)
static ssize_t show_cpumap(int type, cpumask_t *mask, char *buf)
{
ptrdiff_t len = PTR_ALIGN(buf + PAGE_SIZE - 1, PAGE_SIZE) - buf;
@@ -54,65 +55,67 @@ static ssize_t show_cpumap(int type, cpumask_t *mask, char *buf)
}
return n;
}
+#endif
+#ifdef arch_provides_topology_pointers
#define define_siblings_show_map(name) \
-static inline ssize_t show_##name(struct sys_device *dev, char *buf) \
+static ssize_t show_##name(struct sys_device *dev, char *buf) \
{ \
unsigned int cpu = dev->id; \
- return show_cpumap(0, &(topology_##name(cpu)), buf); \
+ cpumask_t siblings = topology_##name(cpu); \
+ return show_cpumap(0, &siblings, buf); \
}
#define define_siblings_show_list(name) \
-static inline ssize_t show_##name##_list(struct sys_device *dev, char *buf) \
+static ssize_t show_##name##_list(struct sys_device *dev, char *buf) \
+{ \
+ unsigned int cpu = dev->id; \
+ cpumask_t siblings = topology_##name(cpu); \
+ return show_cpumap(1, &siblings, buf); \
+}
+
+#else
+#define define_siblings_show_map(name) \
+static ssize_t show_##name(struct sys_device *dev, char *buf) \
{ \
unsigned int cpu = dev->id; \
- return show_cpumap(1, &(topology_##name(cpu)), buf); \
+ cpumask_t mask = topology_##name(cpu); \
+ return show_cpumap(0, &mask, buf); \
}
+#define define_siblings_show_list(name) \
+static ssize_t show_##name##_list(struct sys_device *dev, char *buf) \
+{ \
+ unsigned int cpu = dev->id; \
+ cpumask_t mask = topology_##name(cpu); \
+ return show_cpumap(1, &mask, buf); \
+}
+#endif
+
#define define_siblings_show_func(name) \
define_siblings_show_map(name); define_siblings_show_list(name)
-#ifdef topology_physical_package_id
define_id_show_func(physical_package_id);
define_one_ro(physical_package_id);
-#define ref_physical_package_id_attr &attr_physical_package_id.attr,
-#else
-#define ref_physical_package_id_attr
-#endif
-#ifdef topology_core_id
define_id_show_func(core_id);
define_one_ro(core_id);
-#define ref_core_id_attr &attr_core_id.attr,
-#else
-#define ref_core_id_attr
-#endif
-#ifdef topology_thread_siblings
define_siblings_show_func(thread_siblings);
define_one_ro(thread_siblings);
define_one_ro(thread_siblings_list);
-#define ref_thread_siblings_attr \
- &attr_thread_siblings.attr, &attr_thread_siblings_list.attr,
-#else
-#define ref_thread_siblings_attr
-#endif
-#ifdef topology_core_siblings
define_siblings_show_func(core_siblings);
define_one_ro(core_siblings);
define_one_ro(core_siblings_list);
-#define ref_core_siblings_attr \
- &attr_core_siblings.attr, &attr_core_siblings_list.attr,
-#else
-#define ref_core_siblings_attr
-#endif
static struct attribute *default_attrs[] = {
- ref_physical_package_id_attr
- ref_core_id_attr
- ref_thread_siblings_attr
- ref_core_siblings_attr
+ &attr_physical_package_id.attr,
+ &attr_core_id.attr,
+ &attr_thread_siblings.attr,
+ &attr_thread_siblings_list.attr,
+ &attr_core_siblings.attr,
+ &attr_core_siblings_list.attr,
NULL
};
diff --git a/drivers/block/aoe/aoechr.c b/drivers/block/aoe/aoechr.c
index e8e60e7a2e70..e5b2f2d2fb90 100644
--- a/drivers/block/aoe/aoechr.c
+++ b/drivers/block/aoe/aoechr.c
@@ -272,8 +272,9 @@ aoechr_init(void)
return PTR_ERR(aoe_class);
}
for (i = 0; i < ARRAY_SIZE(chardevs); ++i)
- device_create(aoe_class, NULL,
- MKDEV(AOE_MAJOR, chardevs[i].minor), chardevs[i].name);
+ device_create_drvdata(aoe_class, NULL,
+ MKDEV(AOE_MAJOR, chardevs[i].minor),
+ NULL, chardevs[i].name);
return 0;
}
diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c
index 570f3b70dce7..5fdfa7c888ce 100644
--- a/drivers/block/paride/pd.c
+++ b/drivers/block/paride/pd.c
@@ -712,19 +712,17 @@ static void do_pd_request(struct request_queue * q)
static int pd_special_command(struct pd_unit *disk,
enum action (*func)(struct pd_unit *disk))
{
- DECLARE_COMPLETION_ONSTACK(wait);
- struct request rq;
+ struct request *rq;
int err = 0;
- blk_rq_init(NULL, &rq);
- rq.rq_disk = disk->gd;
- rq.end_io_data = &wait;
- rq.end_io = blk_end_sync_rq;
- blk_insert_request(disk->gd->queue, &rq, 0, func);
- wait_for_completion(&wait);
- if (rq.errors)
- err = -EIO;
- blk_put_request(&rq);
+ rq = blk_get_request(disk->gd->queue, READ, __GFP_WAIT);
+
+ rq->cmd_type = REQ_TYPE_SPECIAL;
+ rq->special = func;
+
+ err = blk_execute_rq(disk->gd->queue, disk->gd, rq, 0);
+
+ blk_put_request(rq);
return err;
}
diff --git a/drivers/block/paride/pg.c b/drivers/block/paride/pg.c
index ab86e23ddc69..0267d5fc0dde 100644
--- a/drivers/block/paride/pg.c
+++ b/drivers/block/paride/pg.c
@@ -676,8 +676,9 @@ static int __init pg_init(void)
for (unit = 0; unit < PG_UNITS; unit++) {
struct pg *dev = &devices[unit];
if (dev->present)
- device_create(pg_class, NULL, MKDEV(major, unit),
- "pg%u", unit);
+ device_create_drvdata(pg_class, NULL,
+ MKDEV(major, unit), NULL,
+ "pg%u", unit);
}
err = 0;
goto out;
diff --git a/drivers/block/paride/pt.c b/drivers/block/paride/pt.c
index 8b9549ab4a4e..defab6164d28 100644
--- a/drivers/block/paride/pt.c
+++ b/drivers/block/paride/pt.c
@@ -972,10 +972,12 @@ static int __init pt_init(void)
for (unit = 0; unit < PT_UNITS; unit++)
if (pt[unit].present) {
- device_create(pt_class, NULL, MKDEV(major, unit),
- "pt%d", unit);
- device_create(pt_class, NULL, MKDEV(major, unit + 128),
- "pt%dn", unit);
+ device_create_drvdata(pt_class, NULL,
+ MKDEV(major, unit), NULL,
+ "pt%d", unit);
+ device_create_drvdata(pt_class, NULL,
+ MKDEV(major, unit + 128), NULL,
+ "pt%dn", unit);
}
goto out;
diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c
index 3ba1df93e9e3..12f2cfdce74f 100644
--- a/drivers/block/pktcdvd.c
+++ b/drivers/block/pktcdvd.c
@@ -302,7 +302,9 @@ static struct kobj_type kobj_pkt_type_wqueue = {
static void pkt_sysfs_dev_new(struct pktcdvd_device *pd)
{
if (class_pktcdvd) {
- pd->dev = device_create(class_pktcdvd, NULL, pd->pkt_dev, "%s", pd->name);
+ pd->dev = device_create_drvdata(class_pktcdvd, NULL,
+ pd->pkt_dev, NULL,
+ "%s", pd->name);
if (IS_ERR(pd->dev))
pd->dev = NULL;
}
diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c
index b990805806af..0c211adbc063 100644
--- a/drivers/bluetooth/bfusb.c
+++ b/drivers/bluetooth/bfusb.c
@@ -566,7 +566,8 @@ static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg
return -ENOIOCTLCMD;
}
-static int bfusb_load_firmware(struct bfusb_data *data, unsigned char *firmware, int count)
+static int bfusb_load_firmware(struct bfusb_data *data,
+ const unsigned char *firmware, int count)
{
unsigned char *buf;
int err, pipe, len, size, sent = 0;
diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
index 7703d6e06fd9..593b7c595038 100644
--- a/drivers/bluetooth/bt3c_cs.c
+++ b/drivers/bluetooth/bt3c_cs.c
@@ -470,7 +470,8 @@ static int bt3c_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long
/* ======================== Card services HCI interaction ======================== */
-static int bt3c_load_firmware(bt3c_info_t *info, unsigned char *firmware, int count)
+static int bt3c_load_firmware(bt3c_info_t *info, const unsigned char *firmware,
+ int count)
{
char *ptr = (char *) firmware;
char b[9];
diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c
index d8200ac8f8cb..f5af65ac8c78 100644
--- a/drivers/char/agp/amd64-agp.c
+++ b/drivers/char/agp/amd64-agp.c
@@ -16,28 +16,9 @@
#include <asm/page.h> /* PAGE_SIZE */
#include <asm/e820.h>
#include <asm/k8.h>
+#include <asm/gart.h>
#include "agp.h"
-/* PTE bits. */
-#define GPTE_VALID 1
-#define GPTE_COHERENT 2
-
-/* Aperture control register bits. */
-#define GARTEN (1<<0)
-#define DISGARTCPU (1<<4)
-#define DISGARTIO (1<<5)
-
-/* GART cache control register bits. */
-#define INVGART (1<<0)
-#define GARTPTEERR (1<<1)
-
-/* K8 On-cpu GART registers */
-#define AMD64_GARTAPERTURECTL 0x90
-#define AMD64_GARTAPERTUREBASE 0x94
-#define AMD64_GARTTABLEBASE 0x98
-#define AMD64_GARTCACHECTL 0x9c
-#define AMD64_GARTEN (1<<0)
-
/* NVIDIA K8 registers */
#define NVIDIA_X86_64_0_APBASE 0x10
#define NVIDIA_X86_64_1_APBASE1 0x50
@@ -165,29 +146,18 @@ static int amd64_fetch_size(void)
* In a multiprocessor x86-64 system, this function gets
* called once for each CPU.
*/
-static u64 amd64_configure (struct pci_dev *hammer, u64 gatt_table)
+static u64 amd64_configure(struct pci_dev *hammer, u64 gatt_table)
{
u64 aperturebase;
u32 tmp;
- u64 addr, aper_base;
+ u64 aper_base;
/* Address to map to */
- pci_read_config_dword (hammer, AMD64_GARTAPERTUREBASE, &tmp);
+ pci_read_config_dword(hammer, AMD64_GARTAPERTUREBASE, &tmp);
aperturebase = tmp << 25;
aper_base = (aperturebase & PCI_BASE_ADDRESS_MEM_MASK);
- /* address of the mappings table */
- addr = (u64) gatt_table;
- addr >>= 12;
- tmp = (u32) addr<<4;
- tmp &= ~0xf;
- pci_write_config_dword (hammer, AMD64_GARTTABLEBASE, tmp);
-
- /* Enable GART translation for this hammer. */
- pci_read_config_dword(hammer, AMD64_GARTAPERTURECTL, &tmp);
- tmp |= GARTEN;
- tmp &= ~(DISGARTCPU | DISGARTIO);
- pci_write_config_dword(hammer, AMD64_GARTAPERTURECTL, tmp);
+ enable_gart_translation(hammer, gatt_table);
return aper_base;
}
@@ -226,9 +196,9 @@ static void amd64_cleanup(void)
for (i = 0; i < num_k8_northbridges; i++) {
struct pci_dev *dev = k8_northbridges[i];
/* disable gart translation */
- pci_read_config_dword (dev, AMD64_GARTAPERTURECTL, &tmp);
+ pci_read_config_dword(dev, AMD64_GARTAPERTURECTL, &tmp);
tmp &= ~AMD64_GARTEN;
- pci_write_config_dword (dev, AMD64_GARTAPERTURECTL, tmp);
+ pci_write_config_dword(dev, AMD64_GARTAPERTURECTL, tmp);
}
}
@@ -258,24 +228,10 @@ static const struct agp_bridge_driver amd_8151_driver = {
};
/* Some basic sanity checks for the aperture. */
-static int __devinit aperture_valid(u64 aper, u32 size)
+static int __devinit agp_aperture_valid(u64 aper, u32 size)
{
- if (aper == 0) {
- printk(KERN_ERR PFX "No aperture\n");
- return 0;
- }
- if (size < 32*1024*1024) {
- printk(KERN_ERR PFX "Aperture too small (%d MB)\n", size>>20);
- return 0;
- }
- if ((u64)aper + size > 0x100000000ULL) {
- printk(KERN_ERR PFX "Aperture out of bounds\n");
+ if (!aperture_valid(aper, size, 32*1024*1024))
return 0;
- }
- if (e820_any_mapped(aper, aper + size, E820_RAM)) {
- printk(KERN_ERR PFX "Aperture pointing to RAM\n");
- return 0;
- }
/* Request the Aperture. This catches cases when someone else
already put a mapping in there - happens with some very broken BIOS
@@ -308,11 +264,11 @@ static __devinit int fix_northbridge(struct pci_dev *nb, struct pci_dev *agp,
u32 nb_order, nb_base;
u16 apsize;
- pci_read_config_dword(nb, 0x90, &nb_order);
+ pci_read_config_dword(nb, AMD64_GARTAPERTURECTL, &nb_order);
nb_order = (nb_order >> 1) & 7;
- pci_read_config_dword(nb, 0x94, &nb_base);
+ pci_read_config_dword(nb, AMD64_GARTAPERTUREBASE, &nb_base);
nb_aper = nb_base << 25;
- if (aperture_valid(nb_aper, (32*1024*1024)<<nb_order)) {
+ if (agp_aperture_valid(nb_aper, (32*1024*1024)<<nb_order)) {
return 0;
}
@@ -331,12 +287,23 @@ static __devinit int fix_northbridge(struct pci_dev *nb, struct pci_dev *agp,
pci_read_config_dword(agp, 0x10, &aper_low);
pci_read_config_dword(agp, 0x14, &aper_hi);
aper = (aper_low & ~((1<<22)-1)) | ((u64)aper_hi << 32);
+
+ /*
+ * On some sick chips APSIZE is 0. This means it wants 4G
+ * so let double check that order, and lets trust the AMD NB settings
+ */
+ if (order >=0 && aper + (32ULL<<(20 + order)) > 0x100000000ULL) {
+ printk(KERN_INFO "Aperture size %u MB is not right, using settings from NB\n",
+ 32 << order);
+ order = nb_order;
+ }
+
printk(KERN_INFO PFX "Aperture from AGP @ %Lx size %u MB\n", aper, 32 << order);
- if (order < 0 || !aperture_valid(aper, (32*1024*1024)<<order))
+ if (order < 0 || !agp_aperture_valid(aper, (32*1024*1024)<<order))
return -1;
- pci_write_config_dword(nb, 0x90, order << 1);
- pci_write_config_dword(nb, 0x94, aper >> 25);
+ pci_write_config_dword(nb, AMD64_GARTAPERTURECTL, order << 1);
+ pci_write_config_dword(nb, AMD64_GARTAPERTUREBASE, aper >> 25);
return 0;
}
diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c
index ef73e72daedc..6bff9d87dc57 100644
--- a/drivers/char/cyclades.c
+++ b/drivers/char/cyclades.c
@@ -4668,7 +4668,7 @@ static inline int __devinit cyc_isfwstr(const char *str, unsigned int size)
return 0;
}
-static inline void __devinit cyz_fpga_copy(void __iomem *fpga, u8 *data,
+static inline void __devinit cyz_fpga_copy(void __iomem *fpga, const u8 *data,
unsigned int size)
{
for (; size > 0; size--) {
@@ -4701,10 +4701,10 @@ static int __devinit __cyz_load_fw(const struct firmware *fw,
const char *name, const u32 mailbox, void __iomem *base,
void __iomem *fpga)
{
- void *ptr = fw->data;
- struct zfile_header *h = ptr;
- struct zfile_config *c, *cs;
- struct zfile_block *b, *bs;
+ const void *ptr = fw->data;
+ const struct zfile_header *h = ptr;
+ const struct zfile_config *c, *cs;
+ const struct zfile_block *b, *bs;
unsigned int a, tmp, len = fw->size;
#define BAD_FW KERN_ERR "Bad firmware: "
if (len < sizeof(*h)) {
diff --git a/drivers/char/dsp56k.c b/drivers/char/dsp56k.c
index a69c65283260..85373b33db56 100644
--- a/drivers/char/dsp56k.c
+++ b/drivers/char/dsp56k.c
@@ -513,7 +513,8 @@ static int __init dsp56k_init_driver(void)
err = PTR_ERR(dsp56k_class);
goto out_chrdev;
}
- device_create(dsp56k_class, NULL, MKDEV(DSP56K_MAJOR, 0), "dsp56k");
+ device_create_drvdata(dsp56k_class, NULL, MKDEV(DSP56K_MAJOR, 0),
+ NULL, "dsp56k");
printk(banner);
goto out;
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index e7fb0bca3667..c9bf5d44402d 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -184,6 +184,67 @@ static irqreturn_t hpet_interrupt(int irq, void *data)
return IRQ_HANDLED;
}
+static void hpet_timer_set_irq(struct hpet_dev *devp)
+{
+ unsigned long v;
+ int irq, gsi;
+ struct hpet_timer __iomem *timer;
+
+ spin_lock_irq(&hpet_lock);
+ if (devp->hd_hdwirq) {
+ spin_unlock_irq(&hpet_lock);
+ return;
+ }
+
+ timer = devp->hd_timer;
+
+ /* we prefer level triggered mode */
+ v = readl(&timer->hpet_config);
+ if (!(v & Tn_INT_TYPE_CNF_MASK)) {
+ v |= Tn_INT_TYPE_CNF_MASK;
+ writel(v, &timer->hpet_config);
+ }
+ spin_unlock_irq(&hpet_lock);
+
+ v = (readq(&timer->hpet_config) & Tn_INT_ROUTE_CAP_MASK) >>
+ Tn_INT_ROUTE_CAP_SHIFT;
+
+ /*
+ * In PIC mode, skip IRQ0-4, IRQ6-9, IRQ12-15 which is always used by
+ * legacy device. In IO APIC mode, we skip all the legacy IRQS.
+ */
+ if (acpi_irq_model == ACPI_IRQ_MODEL_PIC)
+ v &= ~0xf3df;
+ else
+ v &= ~0xffff;
+
+ for (irq = find_first_bit(&v, HPET_MAX_IRQ); irq < HPET_MAX_IRQ;
+ irq = find_next_bit(&v, HPET_MAX_IRQ, 1 + irq)) {
+
+ if (irq >= NR_IRQS) {
+ irq = HPET_MAX_IRQ;
+ break;
+ }
+
+ gsi = acpi_register_gsi(irq, ACPI_LEVEL_SENSITIVE,
+ ACPI_ACTIVE_LOW);
+ if (gsi > 0)
+ break;
+
+ /* FIXME: Setup interrupt source table */
+ }
+
+ if (irq < HPET_MAX_IRQ) {
+ spin_lock_irq(&hpet_lock);
+ v = readl(&timer->hpet_config);
+ v |= irq << Tn_INT_ROUTE_CNF_SHIFT;
+ writel(v, &timer->hpet_config);
+ devp->hd_hdwirq = gsi;
+ spin_unlock_irq(&hpet_lock);
+ }
+ return;
+}
+
static int hpet_open(struct inode *inode, struct file *file)
{
struct hpet_dev *devp;
@@ -215,6 +276,8 @@ static int hpet_open(struct inode *inode, struct file *file)
devp->hd_flags |= HPET_OPEN;
spin_unlock_irq(&hpet_lock);
+ hpet_timer_set_irq(devp);
+
return 0;
}
diff --git a/drivers/char/hvc_xen.c b/drivers/char/hvc_xen.c
index dd68f8541c2d..db2ae4216279 100644
--- a/drivers/char/hvc_xen.c
+++ b/drivers/char/hvc_xen.c
@@ -39,9 +39,14 @@ static int xencons_irq;
/* ------------------------------------------------------------------ */
+static unsigned long console_pfn = ~0ul;
+
static inline struct xencons_interface *xencons_interface(void)
{
- return mfn_to_virt(xen_start_info->console.domU.mfn);
+ if (console_pfn == ~0ul)
+ return mfn_to_virt(xen_start_info->console.domU.mfn);
+ else
+ return __va(console_pfn << PAGE_SHIFT);
}
static inline void notify_daemon(void)
@@ -101,20 +106,32 @@ static int __init xen_init(void)
{
struct hvc_struct *hp;
- if (!is_running_on_xen())
- return 0;
+ if (!is_running_on_xen() ||
+ is_initial_xendomain() ||
+ !xen_start_info->console.domU.evtchn)
+ return -ENODEV;
xencons_irq = bind_evtchn_to_irq(xen_start_info->console.domU.evtchn);
if (xencons_irq < 0)
- xencons_irq = 0 /* NO_IRQ */;
+ 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;
+
+ console_pfn = mfn_to_pfn(xen_start_info->console.domU.mfn);
+
return 0;
}
+void xen_console_resume(void)
+{
+ if (xencons_irq)
+ rebind_evtchn_irq(xen_start_info->console.domU.evtchn, xencons_irq);
+}
+
static void __exit xen_fini(void)
{
if (hvc)
@@ -134,12 +151,28 @@ module_init(xen_init);
module_exit(xen_fini);
console_initcall(xen_cons_init);
+static void raw_console_write(const char *str, int len)
+{
+ while(len > 0) {
+ int rc = HYPERVISOR_console_io(CONSOLEIO_write, len, (char *)str);
+ if (rc <= 0)
+ break;
+
+ str += rc;
+ len -= rc;
+ }
+}
+
+#ifdef CONFIG_EARLY_PRINTK
static void xenboot_write_console(struct console *console, const char *string,
unsigned len)
{
unsigned int linelen, off = 0;
const char *pos;
+ raw_console_write(string, len);
+
+ write_console(0, "(early) ", 8);
while (off < len && NULL != (pos = strchr(string+off, '\n'))) {
linelen = pos-string+off;
if (off + linelen > len)
@@ -155,5 +188,23 @@ static void xenboot_write_console(struct console *console, const char *string,
struct console xenboot_console = {
.name = "xenboot",
.write = xenboot_write_console,
- .flags = CON_PRINTBUFFER | CON_BOOT,
+ .flags = CON_PRINTBUFFER | CON_BOOT | CON_ANYTIME,
};
+#endif /* CONFIG_EARLY_PRINTK */
+
+void xen_raw_console_write(const char *str)
+{
+ raw_console_write(str, strlen(str));
+}
+
+void xen_raw_printk(const char *fmt, ...)
+{
+ static char buf[512];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ xen_raw_console_write(buf);
+}
diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c
index c12cf8fc4be0..3966694a44a0 100644
--- a/drivers/char/ip2/ip2main.c
+++ b/drivers/char/ip2/ip2main.c
@@ -692,12 +692,12 @@ ip2_loadmain(int *iop, int *irqp, unsigned char *firmware, int firmsize)
}
if ( NULL != ( pB = i2BoardPtrTable[i] ) ) {
- device_create(ip2_class, NULL,
- MKDEV(IP2_IPL_MAJOR, 4 * i),
- "ipl%d", i);
- device_create(ip2_class, NULL,
- MKDEV(IP2_IPL_MAJOR, 4 * i + 1),
- "stat%d", i);
+ device_create_drvdata(ip2_class, NULL,
+ MKDEV(IP2_IPL_MAJOR, 4 * i),
+ NULL, "ipl%d", i);
+ device_create_drvdata(ip2_class, NULL,
+ MKDEV(IP2_IPL_MAJOR, 4 * i + 1),
+ NULL, "stat%d", i);
for ( box = 0; box < ABS_MAX_BOXES; ++box )
{
diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c
index 0246a2b8ce48..fee6781ffbe6 100644
--- a/drivers/char/ipmi/ipmi_devintf.c
+++ b/drivers/char/ipmi/ipmi_devintf.c
@@ -865,7 +865,7 @@ static void ipmi_new_smi(int if_num, struct device *device)
entry->dev = dev;
mutex_lock(&reg_list_mutex);
- device_create(ipmi_class, device, dev, "ipmi%d", if_num);
+ device_create_drvdata(ipmi_class, device, dev, NULL, "ipmi%d", if_num);
list_add(&entry->link, &reg_list);
mutex_unlock(&reg_list_mutex);
}
diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c
index 7c8b62f162bf..3e1895fe1d9b 100644
--- a/drivers/char/istallion.c
+++ b/drivers/char/istallion.c
@@ -4599,8 +4599,9 @@ static int __init istallion_module_init(void)
istallion_class = class_create(THIS_MODULE, "staliomem");
for (i = 0; i < 4; i++)
- device_create(istallion_class, NULL, MKDEV(STL_SIOMEMMAJOR, i),
- "staliomem%d", i);
+ device_create_drvdata(istallion_class, NULL,
+ MKDEV(STL_SIOMEMMAJOR, i),
+ NULL, "staliomem%d", i);
return 0;
err_deinit:
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index 60ac642752be..0418a4af420e 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -799,7 +799,8 @@ static int lp_register(int nr, struct parport *port)
if (reset)
lp_reset(nr);
- device_create(lp_class, port->dev, MKDEV(LP_MAJOR, nr), "lp%d", nr);
+ device_create_drvdata(lp_class, port->dev, MKDEV(LP_MAJOR, nr), NULL,
+ "lp%d", nr);
printk(KERN_INFO "lp%d: using %s (%s).\n", nr, port->name,
(port->irq == PARPORT_IRQ_NONE)?"polling":"interrupt-driven");
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 934ffafedaea..832025f6d532 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -983,9 +983,9 @@ static int __init chr_dev_init(void)
mem_class = class_create(THIS_MODULE, "mem");
for (i = 0; i < ARRAY_SIZE(devlist); i++)
- device_create(mem_class, NULL,
- MKDEV(MEM_MAJOR, devlist[i].minor),
- devlist[i].name);
+ device_create_drvdata(mem_class, NULL,
+ MKDEV(MEM_MAJOR, devlist[i].minor),
+ NULL, devlist[i].name);
return 0;
}
diff --git a/drivers/char/misc.c b/drivers/char/misc.c
index eaace0db0ff4..639b2b41d53e 100644
--- a/drivers/char/misc.c
+++ b/drivers/char/misc.c
@@ -214,8 +214,8 @@ int misc_register(struct miscdevice * misc)
misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);
dev = MKDEV(MISC_MAJOR, misc->minor);
- misc->this_device = device_create(misc_class, misc->parent, dev,
- "%s", misc->name);
+ misc->this_device = device_create_drvdata(misc_class, misc->parent,
+ dev, NULL, "%s", misc->name);
if (IS_ERR(misc->this_device)) {
err = PTR_ERR(misc->this_device);
goto out;
diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c
index 4a933d413423..586c1961b6fc 100644
--- a/drivers/char/pcmcia/cm4000_cs.c
+++ b/drivers/char/pcmcia/cm4000_cs.c
@@ -1864,7 +1864,7 @@ static int cm4000_probe(struct pcmcia_device *link)
return ret;
}
- device_create(cmm_class, NULL, MKDEV(major, i), "cmm%d", i);
+ device_create_drvdata(cmm_class, NULL, MKDEV(major, i), NULL, "cmm%d", i);
return 0;
}
diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c
index 035084c07329..aaac106b8db5 100644
--- a/drivers/char/pcmcia/cm4040_cs.c
+++ b/drivers/char/pcmcia/cm4040_cs.c
@@ -642,7 +642,8 @@ static int reader_probe(struct pcmcia_device *link)
return ret;
}
- device_create(cmx_class, NULL, MKDEV(major, i), "cmx%d", i);
+ device_create_drvdata(cmx_class, NULL, MKDEV(major, i), NULL,
+ "cmx%d", i);
return 0;
}
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c
index 3aab837d9480..d0a0c783f630 100644
--- a/drivers/char/ppdev.c
+++ b/drivers/char/ppdev.c
@@ -750,8 +750,9 @@ static const struct file_operations pp_fops = {
static void pp_attach(struct parport *port)
{
- device_create(ppdev_class, port->dev, MKDEV(PP_MAJOR, port->number),
- "parport%d", port->number);
+ device_create_drvdata(ppdev_class, port->dev,
+ MKDEV(PP_MAJOR, port->number),
+ NULL, "parport%d", port->number);
}
static void pp_detach(struct parport *port)
diff --git a/drivers/char/raw.c b/drivers/char/raw.c
index bbfa0e241cba..213f05d71f89 100644
--- a/drivers/char/raw.c
+++ b/drivers/char/raw.c
@@ -128,8 +128,8 @@ raw_ioctl(struct inode *inode, struct file *filp,
static void bind_device(struct raw_config_request *rq)
{
device_destroy(raw_class, MKDEV(RAW_MAJOR, rq->raw_minor));
- device_create(raw_class, NULL, MKDEV(RAW_MAJOR, rq->raw_minor),
- "raw%d", rq->raw_minor);
+ device_create_drvdata(raw_class, NULL, MKDEV(RAW_MAJOR, rq->raw_minor),
+ NULL, "raw%d", rq->raw_minor);
}
/*
@@ -280,7 +280,8 @@ static int __init raw_init(void)
ret = PTR_ERR(raw_class);
goto error_region;
}
- device_create(raw_class, NULL, MKDEV(RAW_MAJOR, 0), "rawctl");
+ device_create_drvdata(raw_class, NULL, MKDEV(RAW_MAJOR, 0), NULL,
+ "rawctl");
return 0;
diff --git a/drivers/char/snsc.c b/drivers/char/snsc.c
index 8fe099a41065..864080c257ab 100644
--- a/drivers/char/snsc.c
+++ b/drivers/char/snsc.c
@@ -441,7 +441,8 @@ scdrv_init(void)
continue;
}
- device_create(snsc_class, NULL, dev, "%s", devname);
+ device_create_drvdata(snsc_class, NULL, dev, NULL,
+ "%s", devname);
ia64_sn_irtr_intr_enable(scd->scd_nasid,
0 /*ignored */ ,
diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c
index d17be10c5d21..c0c03fd1c691 100644
--- a/drivers/char/stallion.c
+++ b/drivers/char/stallion.c
@@ -4753,8 +4753,8 @@ static int __init stallion_module_init(void)
if (IS_ERR(stallion_class))
printk("STALLION: failed to create class\n");
for (i = 0; i < 4; i++)
- device_create(stallion_class, NULL, MKDEV(STL_SIOMEMMAJOR, i),
- "staliomem%d", i);
+ device_create_drvdata(stallion_class, NULL, MKDEV(STL_SIOMEMMAJOR, i),
+ NULL, "staliomem%d", i);
return 0;
err_unrtty:
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index e94bee032314..59dc116b8eb9 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -3896,7 +3896,7 @@ struct device *tty_register_device(struct tty_driver *driver, unsigned index,
else
tty_line_name(driver, index, name);
- return device_create(tty_class, device, dev, name);
+ return device_create_drvdata(tty_class, device, dev, NULL, name);
}
/**
@@ -4174,20 +4174,22 @@ static int __init tty_init(void)
if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
panic("Couldn't register /dev/tty driver\n");
- device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), "tty");
+ device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL,
+ "tty");
cdev_init(&console_cdev, &console_fops);
if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
panic("Couldn't register /dev/console driver\n");
- device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), "console");
+ device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL,
+ "console");
#ifdef CONFIG_UNIX98_PTYS
cdev_init(&ptmx_cdev, &ptmx_fops);
if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0)
panic("Couldn't register /dev/ptmx driver\n");
- device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), "ptmx");
+ device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx");
#endif
#ifdef CONFIG_VT
@@ -4195,7 +4197,7 @@ static int __init tty_init(void)
if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||
register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0)
panic("Couldn't register /dev/tty0 driver\n");
- device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), "tty0");
+ device_create_drvdata(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0");
vty_init();
#endif
diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c
index 83aeedda200c..03f9dc309dbe 100644
--- a/drivers/char/vc_screen.c
+++ b/drivers/char/vc_screen.c
@@ -476,10 +476,10 @@ static struct class *vc_class;
void vcs_make_sysfs(struct tty_struct *tty)
{
- device_create(vc_class, NULL, MKDEV(VCS_MAJOR, tty->index + 1),
- "vcs%u", tty->index + 1);
- device_create(vc_class, NULL, MKDEV(VCS_MAJOR, tty->index + 129),
- "vcsa%u", tty->index + 1);
+ device_create_drvdata(vc_class, NULL, MKDEV(VCS_MAJOR, tty->index + 1),
+ NULL, "vcs%u", tty->index + 1);
+ device_create_drvdata(vc_class, NULL, MKDEV(VCS_MAJOR, tty->index + 129),
+ NULL, "vcsa%u", tty->index + 1);
}
void vcs_remove_sysfs(struct tty_struct *tty)
@@ -494,7 +494,7 @@ int __init vcs_init(void)
panic("unable to get major %d for vcs device", VCS_MAJOR);
vc_class = class_create(THIS_MODULE, "vc");
- device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 0), "vcs");
- device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 128), "vcsa");
+ device_create_drvdata(vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs");
+ device_create_drvdata(vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa");
return 0;
}
diff --git a/drivers/char/viotape.c b/drivers/char/viotape.c
index c39ddaff5e8f..e508ad99d11e 100644
--- a/drivers/char/viotape.c
+++ b/drivers/char/viotape.c
@@ -872,10 +872,10 @@ static int viotape_probe(struct vio_dev *vdev, const struct vio_device_id *id)
state[i].cur_part = 0;
for (j = 0; j < MAX_PARTITIONS; ++j)
state[i].part_stat_rwi[j] = VIOT_IDLE;
- device_create(tape_class, NULL, MKDEV(VIOTAPE_MAJOR, i),
- "iseries!vt%d", i);
- device_create(tape_class, NULL, MKDEV(VIOTAPE_MAJOR, i | 0x80),
- "iseries!nvt%d", i);
+ device_create_drvdata(tape_class, NULL, MKDEV(VIOTAPE_MAJOR, i),
+ NULL, "iseries!vt%d", i);
+ device_create_drvdata(tape_class, NULL, MKDEV(VIOTAPE_MAJOR, i | 0x80),
+ NULL, "iseries!nvt%d", i);
printk(VIOTAPE_KERN_INFO "tape iseries/vt%d is iSeries "
"resource %10.10s type %4.4s, model %3.3s\n",
i, viotape_unitinfo[i].rsrcname,
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index 935f1c207a1f..e32a076d5f1f 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -3425,9 +3425,10 @@ int register_con_driver(const struct consw *csw, int first, int last)
if (retval)
goto err;
- con_driver->dev = device_create(vtconsole_class, NULL,
- MKDEV(0, con_driver->node),
- "vtcon%i", con_driver->node);
+ con_driver->dev = device_create_drvdata(vtconsole_class, NULL,
+ MKDEV(0, con_driver->node),
+ NULL, "vtcon%i",
+ con_driver->node);
if (IS_ERR(con_driver->dev)) {
printk(KERN_WARNING "Unable to create device for %s; "
@@ -3535,9 +3536,10 @@ static int __init vtconsole_class_init(void)
struct con_driver *con = &registered_con_driver[i];
if (con->con && !con->dev) {
- con->dev = device_create(vtconsole_class, NULL,
- MKDEV(0, con->node),
- "vtcon%i", con->node);
+ con->dev = device_create_drvdata(vtconsole_class, NULL,
+ MKDEV(0, con->node),
+ NULL, "vtcon%i",
+ con->node);
if (IS_ERR(con->dev)) {
printk(KERN_WARNING "Unable to create "
diff --git a/drivers/char/xilinx_hwicap/xilinx_hwicap.c b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
index 3edf1fc12963..ba104631a976 100644
--- a/drivers/char/xilinx_hwicap/xilinx_hwicap.c
+++ b/drivers/char/xilinx_hwicap/xilinx_hwicap.c
@@ -654,8 +654,9 @@ static int __devinit hwicap_setup(struct device *dev, int id,
dev_err(dev, "cdev_add() failed\n");
goto failed3;
}
- /* devfs_mk_cdev(devt, S_IFCHR|S_IRUGO|S_IWUGO, DRIVER_NAME); */
- device_create(icap_class, dev, devt, "%s%d", DRIVER_NAME, id);
+
+ device_create_drvdata(icap_class, dev, devt, NULL,
+ "%s%d", DRIVER_NAME, id);
return 0; /* success */
failed3:
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 1d41496ed2f8..d8f8b1e0edde 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -589,7 +589,7 @@ static ssize_t show_cpus(cpumask_t mask, char *buf)
ssize_t i = 0;
unsigned int cpu;
- for_each_cpu_mask(cpu, mask) {
+ for_each_cpu_mask_nr(cpu, mask) {
if (i)
i += scnprintf(&buf[i], (PAGE_SIZE - i - 2), " ");
i += scnprintf(&buf[i], (PAGE_SIZE - i - 2), "%u", cpu);
@@ -835,7 +835,7 @@ static int cpufreq_add_dev(struct sys_device *sys_dev)
}
#endif
- for_each_cpu_mask(j, policy->cpus) {
+ for_each_cpu_mask_nr(j, policy->cpus) {
if (cpu == j)
continue;
@@ -898,14 +898,14 @@ static int cpufreq_add_dev(struct sys_device *sys_dev)
}
spin_lock_irqsave(&cpufreq_driver_lock, flags);
- for_each_cpu_mask(j, policy->cpus) {
+ for_each_cpu_mask_nr(j, policy->cpus) {
cpufreq_cpu_data[j] = policy;
per_cpu(policy_cpu, j) = policy->cpu;
}
spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
/* symlink affected CPUs */
- for_each_cpu_mask(j, policy->cpus) {
+ for_each_cpu_mask_nr(j, policy->cpus) {
if (j == cpu)
continue;
if (!cpu_online(j))
@@ -945,7 +945,7 @@ static int cpufreq_add_dev(struct sys_device *sys_dev)
err_out_unregister:
spin_lock_irqsave(&cpufreq_driver_lock, flags);
- for_each_cpu_mask(j, policy->cpus)
+ for_each_cpu_mask_nr(j, policy->cpus)
cpufreq_cpu_data[j] = NULL;
spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
@@ -1028,7 +1028,7 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev)
* links afterwards.
*/
if (unlikely(cpus_weight(data->cpus) > 1)) {
- for_each_cpu_mask(j, data->cpus) {
+ for_each_cpu_mask_nr(j, data->cpus) {
if (j == cpu)
continue;
cpufreq_cpu_data[j] = NULL;
@@ -1038,7 +1038,7 @@ static int __cpufreq_remove_dev(struct sys_device *sys_dev)
spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
if (unlikely(cpus_weight(data->cpus) > 1)) {
- for_each_cpu_mask(j, data->cpus) {
+ for_each_cpu_mask_nr(j, data->cpus) {
if (j == cpu)
continue;
dprintk("removing link for cpu %u\n", j);
diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c
index 5d3a04ba6ad2..fe565ee43757 100644
--- a/drivers/cpufreq/cpufreq_conservative.c
+++ b/drivers/cpufreq/cpufreq_conservative.c
@@ -497,7 +497,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
return rc;
}
- for_each_cpu_mask(j, policy->cpus) {
+ for_each_cpu_mask_nr(j, policy->cpus) {
struct cpu_dbs_info_s *j_dbs_info;
j_dbs_info = &per_cpu(cpu_dbs_info, j);
j_dbs_info->cur_policy = policy;
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index d2af20dda382..33855cb3cf16 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -367,7 +367,7 @@ static void dbs_check_cpu(struct cpu_dbs_info_s *this_dbs_info)
/* Get Idle Time */
idle_ticks = UINT_MAX;
- for_each_cpu_mask(j, policy->cpus) {
+ for_each_cpu_mask_nr(j, policy->cpus) {
cputime64_t total_idle_ticks;
unsigned int tmp_idle_ticks;
struct cpu_dbs_info_s *j_dbs_info;
@@ -521,7 +521,7 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy,
return rc;
}
- for_each_cpu_mask(j, policy->cpus) {
+ for_each_cpu_mask_nr(j, policy->cpus) {
struct cpu_dbs_info_s *j_dbs_info;
j_dbs_info = &per_cpu(cpu_dbs_info, j);
j_dbs_info->cur_policy = policy;
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index fc555a90bb21..23554b676d6e 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -38,6 +38,8 @@ static void cpuidle_kick_cpus(void)
static void cpuidle_kick_cpus(void) {}
#endif
+static int __cpuidle_register_device(struct cpuidle_device *dev);
+
/**
* cpuidle_idle_call - the main idle loop
*
@@ -138,6 +140,12 @@ int cpuidle_enable_device(struct cpuidle_device *dev)
if (!dev->state_count)
return -EINVAL;
+ if (dev->registered == 0) {
+ ret = __cpuidle_register_device(dev);
+ if (ret)
+ return ret;
+ }
+
if ((ret = cpuidle_add_state_sysfs(dev)))
return ret;
@@ -232,10 +240,13 @@ static void poll_idle_init(struct cpuidle_device *dev) {}
#endif /* CONFIG_ARCH_HAS_CPU_RELAX */
/**
- * cpuidle_register_device - registers a CPU's idle PM feature
+ * __cpuidle_register_device - internal register function called before register
+ * and enable routines
* @dev: the cpu
+ *
+ * cpuidle_lock mutex must be held before this is called
*/
-int cpuidle_register_device(struct cpuidle_device *dev)
+static int __cpuidle_register_device(struct cpuidle_device *dev)
{
int ret;
struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu);
@@ -247,18 +258,34 @@ int cpuidle_register_device(struct cpuidle_device *dev)
init_completion(&dev->kobj_unregister);
- mutex_lock(&cpuidle_lock);
-
poll_idle_init(dev);
per_cpu(cpuidle_devices, dev->cpu) = dev;
list_add(&dev->device_list, &cpuidle_detected_devices);
if ((ret = cpuidle_add_sysfs(sys_dev))) {
- mutex_unlock(&cpuidle_lock);
module_put(cpuidle_curr_driver->owner);
return ret;
}
+ dev->registered = 1;
+ return 0;
+}
+
+/**
+ * cpuidle_register_device - registers a CPU's idle PM feature
+ * @dev: the cpu
+ */
+int cpuidle_register_device(struct cpuidle_device *dev)
+{
+ int ret;
+
+ mutex_lock(&cpuidle_lock);
+
+ if ((ret = __cpuidle_register_device(dev))) {
+ mutex_unlock(&cpuidle_lock);
+ return ret;
+ }
+
cpuidle_enable_device(dev);
cpuidle_install_idle_handler();
@@ -278,6 +305,9 @@ void cpuidle_unregister_device(struct cpuidle_device *dev)
{
struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu);
+ if (dev->registered == 0)
+ return;
+
cpuidle_pause_and_lock();
cpuidle_disable_device(dev);
diff --git a/drivers/dca/dca-sysfs.c b/drivers/dca/dca-sysfs.c
index 011328faa5f2..9a70377bfb34 100644
--- a/drivers/dca/dca-sysfs.c
+++ b/drivers/dca/dca-sysfs.c
@@ -14,8 +14,9 @@ int dca_sysfs_add_req(struct dca_provider *dca, struct device *dev, int slot)
{
struct device *cd;
- cd = device_create(dca_class, dca->cd, MKDEV(0, slot + 1),
- "requester%d", slot);
+ cd = device_create_drvdata(dca_class, dca->cd,
+ MKDEV(0, slot + 1), NULL,
+ "requester%d", slot);
if (IS_ERR(cd))
return PTR_ERR(cd);
return 0;
@@ -46,7 +47,8 @@ idr_try_again:
return err;
}
- cd = device_create(dca_class, dev, MKDEV(0, 0), "dca%d", dca->id);
+ cd = device_create_drvdata(dca_class, dev, MKDEV(0, 0), NULL,
+ "dca%d", dca->id);
if (IS_ERR(cd)) {
spin_lock(&dca_idr_lock);
idr_remove(&dca_idr, dca->id);
diff --git a/drivers/eisa/Makefile b/drivers/eisa/Makefile
index 70abf93fe6b0..5369ce957c6d 100644
--- a/drivers/eisa/Makefile
+++ b/drivers/eisa/Makefile
@@ -9,7 +9,7 @@ obj-${CONFIG_EISA_VIRTUAL_ROOT} += virtual_root.o
# Ugly hack to get DEVICE_NAME_SIZE value...
-DEVICE_NAME_SIZE =$(shell awk '$$1=="\#define" && $$2=="DEVICE_NAME_SIZE" {print $$3-1}' $(srctree)/include/linux/device.h)
+DEVICE_NAME_SIZE = 50
$(obj)/eisa-bus.o: $(obj)/devlist.h
diff --git a/drivers/eisa/eisa-bus.c b/drivers/eisa/eisa-bus.c
index 65dcf0432653..c950bf8606d9 100644
--- a/drivers/eisa/eisa-bus.c
+++ b/drivers/eisa/eisa-bus.c
@@ -22,7 +22,7 @@
struct eisa_device_info {
struct eisa_device_id id;
- char name[DEVICE_NAME_SIZE];
+ char name[50];
};
#ifdef CONFIG_EISA_NAMES
@@ -63,7 +63,7 @@ static void __init eisa_name_device (struct eisa_device *edev)
if (!strcmp (edev->id.sig, eisa_table[i].id.sig)) {
strlcpy (edev->pretty_name,
eisa_table[i].name,
- DEVICE_NAME_SIZE);
+ sizeof(edev->pretty_name));
return;
}
}
diff --git a/drivers/firewire/fw-card.c b/drivers/firewire/fw-card.c
index 5b4c0d9f5173..1332b66ae0b3 100644
--- a/drivers/firewire/fw-card.c
+++ b/drivers/firewire/fw-card.c
@@ -403,6 +403,7 @@ fw_card_initialize(struct fw_card *card, const struct fw_card_driver *driver,
card->current_tlabel = 0;
card->tlabel_mask = 0;
card->color = 0;
+ card->broadcast_channel = BROADCAST_CHANNEL_INITIAL;
INIT_LIST_HEAD(&card->transaction_list);
spin_lock_init(&card->lock);
@@ -496,7 +497,6 @@ dummy_enable_phys_dma(struct fw_card *card,
}
static struct fw_card_driver dummy_driver = {
- .name = "dummy",
.enable = dummy_enable,
.update_phy_reg = dummy_update_phy_reg,
.set_config_rom = dummy_set_config_rom,
diff --git a/drivers/firewire/fw-device.h b/drivers/firewire/fw-device.h
index 5f131f5129da..42305bbac72f 100644
--- a/drivers/firewire/fw-device.h
+++ b/drivers/firewire/fw-device.h
@@ -62,7 +62,6 @@ struct fw_device {
bool cmc;
struct fw_card *card;
struct device device;
- struct list_head link;
struct list_head client_list;
u32 *config_rom;
size_t config_rom_length;
diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c
index 4f02c55f13e1..a6c1fa234510 100644
--- a/drivers/firewire/fw-ohci.c
+++ b/drivers/firewire/fw-ohci.c
@@ -548,6 +548,11 @@ static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer)
p.header_length = 12;
p.payload_length = 0;
break;
+
+ default:
+ /* FIXME: Stop context, discard everything, and restart? */
+ p.header_length = 0;
+ p.payload_length = 0;
}
p.payload = (void *) buffer + p.header_length;
@@ -1468,6 +1473,9 @@ static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length)
reg_write(ohci, OHCI1394_HCControlClear,
OHCI1394_HCControl_noByteSwapData);
+ reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->self_id_bus);
+ reg_write(ohci, OHCI1394_LinkControlClear,
+ OHCI1394_LinkControl_rcvPhyPkt);
reg_write(ohci, OHCI1394_LinkControlSet,
OHCI1394_LinkControl_rcvSelfID |
OHCI1394_LinkControl_cycleTimerEnable |
@@ -1481,7 +1489,6 @@ static int ohci_enable(struct fw_card *card, u32 *config_rom, size_t length)
ar_context_run(&ohci->ar_request_ctx);
ar_context_run(&ohci->ar_response_ctx);
- reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->self_id_bus);
reg_write(ohci, OHCI1394_PhyUpperBound, 0x00010000);
reg_write(ohci, OHCI1394_IntEventClear, ~0);
reg_write(ohci, OHCI1394_IntMaskClear, ~0);
@@ -2290,7 +2297,6 @@ ohci_queue_iso(struct fw_iso_context *base,
}
static const struct fw_card_driver ohci_driver = {
- .name = ohci_driver_name,
.enable = ohci_enable,
.update_phy_reg = ohci_update_phy_reg,
.set_config_rom = ohci_set_config_rom,
diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c
index b2458bb8e9ca..efa7eb216346 100644
--- a/drivers/firewire/fw-sbp2.c
+++ b/drivers/firewire/fw-sbp2.c
@@ -86,6 +86,11 @@ MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device "
* - delay inquiry
* Wait extra SBP2_INQUIRY_DELAY seconds after login before SCSI inquiry.
*
+ * - power condition
+ * Set the power condition field in the START STOP UNIT commands sent by
+ * sd_mod on suspend, resume, and shutdown (if manage_start_stop is on).
+ * Some disks need this to spin down or to resume properly.
+ *
* - override internal blacklist
* Instead of adding to the built-in blacklist, use only the workarounds
* specified in the module load parameter.
@@ -97,6 +102,7 @@ MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device "
#define SBP2_WORKAROUND_FIX_CAPACITY 0x8
#define SBP2_WORKAROUND_DELAY_INQUIRY 0x10
#define SBP2_INQUIRY_DELAY 12
+#define SBP2_WORKAROUND_POWER_CONDITION 0x20
#define SBP2_WORKAROUND_OVERRIDE 0x100
static int sbp2_param_workarounds;
@@ -107,6 +113,8 @@ MODULE_PARM_DESC(workarounds, "Work around device bugs (default = 0"
", skip mode page 8 = " __stringify(SBP2_WORKAROUND_MODE_SENSE_8)
", fix capacity = " __stringify(SBP2_WORKAROUND_FIX_CAPACITY)
", delay inquiry = " __stringify(SBP2_WORKAROUND_DELAY_INQUIRY)
+ ", set power condition in start stop unit = "
+ __stringify(SBP2_WORKAROUND_POWER_CONDITION)
", override internal blacklist = " __stringify(SBP2_WORKAROUND_OVERRIDE)
", or a combination)");
@@ -310,18 +318,25 @@ static const struct {
.firmware_revision = 0x002800,
.model = 0x001010,
.workarounds = SBP2_WORKAROUND_INQUIRY_36 |
- SBP2_WORKAROUND_MODE_SENSE_8,
+ SBP2_WORKAROUND_MODE_SENSE_8 |
+ SBP2_WORKAROUND_POWER_CONDITION,
},
/* DViCO Momobay FX-3A with TSB42AA9A bridge */ {
.firmware_revision = 0x002800,
.model = 0x000000,
- .workarounds = SBP2_WORKAROUND_DELAY_INQUIRY,
+ .workarounds = SBP2_WORKAROUND_DELAY_INQUIRY |
+ SBP2_WORKAROUND_POWER_CONDITION,
},
/* Initio bridges, actually only needed for some older ones */ {
.firmware_revision = 0x000200,
.model = ~0,
.workarounds = SBP2_WORKAROUND_INQUIRY_36,
},
+ /* PL-3507 bridge with Prolific firmware */ {
+ .firmware_revision = 0x012800,
+ .model = ~0,
+ .workarounds = SBP2_WORKAROUND_POWER_CONDITION,
+ },
/* Symbios bridge */ {
.firmware_revision = 0xa0b800,
.model = ~0,
@@ -1529,6 +1544,9 @@ static int sbp2_scsi_slave_configure(struct scsi_device *sdev)
sdev->use_10_for_rw = 1;
+ if (sbp2_param_exclusive_login)
+ sdev->manage_start_stop = 1;
+
if (sdev->type == TYPE_ROM)
sdev->use_10_for_ms = 1;
@@ -1539,6 +1557,9 @@ static int sbp2_scsi_slave_configure(struct scsi_device *sdev)
if (lu->tgt->workarounds & SBP2_WORKAROUND_FIX_CAPACITY)
sdev->fix_capacity = 1;
+ if (lu->tgt->workarounds & SBP2_WORKAROUND_POWER_CONDITION)
+ sdev->start_stop_pwr_cond = 1;
+
if (lu->tgt->workarounds & SBP2_WORKAROUND_128K_MAX_TRANS)
blk_queue_max_sectors(sdev->request_queue, 128 * 1024 / 512);
diff --git a/drivers/firewire/fw-transaction.c b/drivers/firewire/fw-transaction.c
index ccf0e4cf108f..efcf0d48f098 100644
--- a/drivers/firewire/fw-transaction.c
+++ b/drivers/firewire/fw-transaction.c
@@ -54,6 +54,9 @@
#define HEADER_GET_DATA_LENGTH(q) (((q) >> 16) & 0xffff)
#define HEADER_GET_EXTENDED_TCODE(q) (((q) >> 0) & 0xffff)
+#define HEADER_DESTINATION_IS_BROADCAST(q) \
+ (((q) & HEADER_DESTINATION(0x3f)) == HEADER_DESTINATION(0x3f))
+
#define PHY_CONFIG_GAP_COUNT(gap_count) (((gap_count) << 16) | (1 << 22))
#define PHY_CONFIG_ROOT_ID(node_id) ((((node_id) & 0x3f) << 24) | (1 << 23))
#define PHY_IDENTIFIER(id) ((id) << 30)
@@ -572,7 +575,8 @@ allocate_request(struct fw_packet *p)
break;
default:
- BUG();
+ fw_error("ERROR - corrupt request received - %08x %08x %08x\n",
+ p->header[0], p->header[1], p->header[2]);
return NULL;
}
@@ -604,12 +608,9 @@ allocate_request(struct fw_packet *p)
void
fw_send_response(struct fw_card *card, struct fw_request *request, int rcode)
{
- /*
- * Broadcast packets are reported as ACK_COMPLETE, so this
- * check is sufficient to ensure we don't send response to
- * broadcast packets or posted writes.
- */
- if (request->ack != ACK_PENDING) {
+ /* unified transaction or broadcast transaction: don't respond */
+ if (request->ack != ACK_PENDING ||
+ HEADER_DESTINATION_IS_BROADCAST(request->request_header[0])) {
kfree(request);
return;
}
@@ -797,12 +798,13 @@ handle_registers(struct fw_card *card, struct fw_request *request,
int reg = offset & ~CSR_REGISTER_BASE;
unsigned long long bus_time;
__be32 *data = payload;
+ int rcode = RCODE_COMPLETE;
switch (reg) {
case CSR_CYCLE_TIME:
case CSR_BUS_TIME:
if (!TCODE_IS_READ_REQUEST(tcode) || length != 4) {
- fw_send_response(card, request, RCODE_TYPE_ERROR);
+ rcode = RCODE_TYPE_ERROR;
break;
}
@@ -811,7 +813,17 @@ handle_registers(struct fw_card *card, struct fw_request *request,
*data = cpu_to_be32(bus_time);
else
*data = cpu_to_be32(bus_time >> 25);
- fw_send_response(card, request, RCODE_COMPLETE);
+ break;
+
+ case CSR_BROADCAST_CHANNEL:
+ if (tcode == TCODE_READ_QUADLET_REQUEST)
+ *data = cpu_to_be32(card->broadcast_channel);
+ else if (tcode == TCODE_WRITE_QUADLET_REQUEST)
+ card->broadcast_channel =
+ (be32_to_cpu(*data) & BROADCAST_CHANNEL_VALID) |
+ BROADCAST_CHANNEL_INITIAL;
+ else
+ rcode = RCODE_TYPE_ERROR;
break;
case CSR_BUS_MANAGER_ID:
@@ -830,10 +842,13 @@ handle_registers(struct fw_card *card, struct fw_request *request,
case CSR_BUSY_TIMEOUT:
/* FIXME: Implement this. */
+
default:
- fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+ rcode = RCODE_ADDRESS_ERROR;
break;
}
+
+ fw_send_response(card, request, rcode);
}
static struct fw_address_handler registers = {
diff --git a/drivers/firewire/fw-transaction.h b/drivers/firewire/fw-transaction.h
index 04d3854f6560..219094e38542 100644
--- a/drivers/firewire/fw-transaction.h
+++ b/drivers/firewire/fw-transaction.h
@@ -20,12 +20,12 @@
#define __fw_transaction_h
#include <linux/device.h>
-#include <linux/timer.h>
-#include <linux/interrupt.h>
-#include <linux/list.h>
-#include <linux/fs.h>
#include <linux/dma-mapping.h>
#include <linux/firewire-constants.h>
+#include <linux/list.h>
+#include <linux/spinlock_types.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
#include <asm/atomic.h>
#define TCODE_IS_READ_REQUEST(tcode) (((tcode) & ~1) == 4)
@@ -80,6 +80,9 @@
#define CSR_SPEED_MAP 0x2000
#define CSR_SPEED_MAP_END 0x3000
+#define BROADCAST_CHANNEL_INITIAL (1 << 31 | 31)
+#define BROADCAST_CHANNEL_VALID (1 << 30)
+
#define fw_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, ## args)
#define fw_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args)
@@ -236,6 +239,7 @@ struct fw_card {
*/
int self_id_count;
u32 topology_map[252 + 3];
+ u32 broadcast_channel;
spinlock_t lock; /* Take this lock when handling the lists in
* this struct. */
@@ -348,8 +352,6 @@ int
fw_iso_context_stop(struct fw_iso_context *ctx);
struct fw_card_driver {
- const char *name;
-
/*
* Enable the given card with the given initial config rom.
* This function is expected to activate the card, and either
diff --git a/drivers/firmware/dell_rbu.c b/drivers/firmware/dell_rbu.c
index 6a8b1e037e07..7430e218cda6 100644
--- a/drivers/firmware/dell_rbu.c
+++ b/drivers/firmware/dell_rbu.c
@@ -220,7 +220,7 @@ out_noalloc:
return retval;
}
-static int packetize_data(void *data, size_t length)
+static int packetize_data(const u8 *data, size_t length)
{
int rc = 0;
int done = 0;
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index c5e3ed7e903b..455575be3560 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -8,6 +8,11 @@
#include <linux/slab.h>
#include <asm/dmi.h>
+/*
+ * DMI stands for "Desktop Management Interface". It is part
+ * of and an antecedent to, SMBIOS, which stands for System
+ * Management BIOS. See further: http://www.dmtf.org/standards
+ */
static char dmi_empty_string[] = " ";
static const char * __init dmi_string_nosave(const struct dmi_header *dm, u8 s)
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index f43d6d3cf2fa..426ac5add585 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -780,7 +780,7 @@ static __inline__ __u32 extract(__u8 *report, unsigned offset, unsigned n)
*/
static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u32 value)
{
- __le64 x;
+ u64 x;
u64 m = (1ULL << n) - 1;
if (n > 32)
@@ -796,10 +796,10 @@ static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u3
report += offset >> 3;
offset &= 7;
- x = get_unaligned((__le64 *)report);
- x &= cpu_to_le64(~(m << offset));
- x |= cpu_to_le64(((u64) value) << offset);
- put_unaligned(x, (__le64 *) report);
+ x = get_unaligned_le64(report);
+ x &= ~(m << offset);
+ x |= ((u64)value) << offset;
+ put_unaligned_le64(x, report);
}
/*
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index 1ca6f4635eeb..26b96d0f86fa 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -30,6 +30,7 @@
#include <linux/major.h>
#include <linux/hid.h>
#include <linux/mutex.h>
+#include <linux/smp_lock.h>
#include <linux/hidraw.h>
@@ -104,6 +105,7 @@ out:
static ssize_t hidraw_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
unsigned int minor = iminor(file->f_path.dentry->d_inode);
+ /* FIXME: What stops hidraw_table going NULL */
struct hid_device *dev = hidraw_table[minor]->hid;
__u8 *buf;
int ret = 0;
@@ -211,35 +213,38 @@ static int hidraw_release(struct inode * inode, struct file * file)
return 0;
}
-static int hidraw_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+static long hidraw_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
+ struct inode *inode = file->f_path.dentry->d_inode;
unsigned int minor = iminor(inode);
+ long ret = 0;
+ /* FIXME: What stops hidraw_table going NULL */
struct hidraw *dev = hidraw_table[minor];
void __user *user_arg = (void __user*) arg;
+ lock_kernel();
switch (cmd) {
case HIDIOCGRDESCSIZE:
if (put_user(dev->hid->rsize, (int __user *)arg))
- return -EFAULT;
- return 0;
+ ret = -EFAULT;
+ break;
case HIDIOCGRDESC:
{
__u32 len;
if (get_user(len, (int __user *)arg))
- return -EFAULT;
-
- if (len > HID_MAX_DESCRIPTOR_SIZE - 1)
- return -EINVAL;
-
- if (copy_to_user(user_arg + offsetof(
- struct hidraw_report_descriptor,
- value[0]),
- dev->hid->rdesc,
- min(dev->hid->rsize, len)))
- return -EFAULT;
- return 0;
+ ret = -EFAULT;
+ else if (len > HID_MAX_DESCRIPTOR_SIZE - 1)
+ ret = -EINVAL;
+ else if (copy_to_user(user_arg + offsetof(
+ struct hidraw_report_descriptor,
+ value[0]),
+ dev->hid->rdesc,
+ min(dev->hid->rsize, len)))
+ ret = -EFAULT;
+ break;
}
case HIDIOCGRAWINFO:
{
@@ -249,15 +254,13 @@ static int hidraw_ioctl(struct inode *inode, struct file *file, unsigned int cmd
dinfo.vendor = dev->hid->vendor;
dinfo.product = dev->hid->product;
if (copy_to_user(user_arg, &dinfo, sizeof(dinfo)))
- return -EFAULT;
-
- return 0;
+ ret = -EFAULT;
+ break;
}
default:
- printk(KERN_EMERG "hidraw: unsupported ioctl() %x\n",
- cmd);
+ ret = -ENOTTY;
}
- return -EINVAL;
+ return ret;
}
static const struct file_operations hidraw_ops = {
@@ -267,7 +270,7 @@ static const struct file_operations hidraw_ops = {
.poll = hidraw_poll,
.open = hidraw_open,
.release = hidraw_release,
- .ioctl = hidraw_ioctl,
+ .unlocked_ioctl = hidraw_ioctl,
};
void hidraw_report_event(struct hid_device *hid, u8 *data, int len)
@@ -319,8 +322,9 @@ int hidraw_connect(struct hid_device *hid)
goto out;
}
- dev->dev = device_create(hidraw_class, NULL, MKDEV(hidraw_major, minor),
- "%s%d", "hidraw", minor);
+ dev->dev = device_create_drvdata(hidraw_class, NULL,
+ MKDEV(hidraw_major, minor), NULL,
+ "%s%d", "hidraw", minor);
if (IS_ERR(dev->dev)) {
spin_lock(&minors_lock);
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 1df832a8fcbc..ba22eec200cd 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -69,12 +69,18 @@
#define USB_DEVICE_ID_APPLE_ALU_ANSI 0x0220
#define USB_DEVICE_ID_APPLE_ALU_ISO 0x0221
#define USB_DEVICE_ID_APPLE_ALU_JIS 0x0222
+#define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI 0x0223
+#define USB_DEVICE_ID_APPLE_WELLSPRING_ISO 0x0224
+#define USB_DEVICE_ID_APPLE_WELLSPRING_JIS 0x0225
#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ANSI 0x0229
#define USB_DEVICE_ID_APPLE_GEYSER4_HF_ISO 0x022a
#define USB_DEVICE_ID_APPLE_GEYSER4_HF_JIS 0x022b
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI 0x022c
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO 0x022d
#define USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS 0x022e
+#define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI 0x0230
+#define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO 0x0231
+#define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS 0x0232
#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
@@ -642,6 +648,12 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN },
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD },
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN },
+ { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI, HID_QUIRK_APPLE_HAS_FN },
+ { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD },
+ { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS, HID_QUIRK_APPLE_HAS_FN },
+ { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI, HID_QUIRK_APPLE_HAS_FN },
+ { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD },
+ { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS, HID_QUIRK_APPLE_HAS_FN },
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
{ USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
@@ -1128,7 +1140,7 @@ static void usbhid_fixup_microsoft_descriptor(unsigned char *rdesc, int rsize)
&& rdesc[557] == 0x19
&& rdesc[559] == 0x29) {
printk(KERN_INFO "Fixing up Microsoft Wireless Receiver Model 1028 report descriptor\n");
- rdesc[284] = rdesc[304] = rdesc[558] = 0x35;
+ rdesc[284] = rdesc[304] = rdesc[557] = 0x35;
rdesc[352] = 0x36;
rdesc[286] = rdesc[355] = 0x46;
rdesc[306] = rdesc[559] = 0x45;
diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c
index 95cc192bc7af..842e9edb888e 100644
--- a/drivers/hid/usbhid/hiddev.c
+++ b/drivers/hid/usbhid/hiddev.c
@@ -406,6 +406,7 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd,
uref_multi = kmalloc(sizeof(struct hiddev_usage_ref_multi), GFP_KERNEL);
if (!uref_multi)
return -ENOMEM;
+ lock_kernel();
uref = &uref_multi->uref;
if (cmd == HIDIOCGUSAGES || cmd == HIDIOCSUSAGES) {
if (copy_from_user(uref_multi, user_arg,
@@ -501,12 +502,15 @@ static noinline int hiddev_ioctl_usage(struct hiddev *hiddev, unsigned int cmd,
}
goodreturn:
+ unlock_kernel();
kfree(uref_multi);
return 0;
fault:
+ unlock_kernel();
kfree(uref_multi);
return -EFAULT;
inval:
+ unlock_kernel();
kfree(uref_multi);
return -EINVAL;
}
@@ -540,7 +544,7 @@ static noinline int hiddev_ioctl_string(struct hiddev *hiddev, unsigned int cmd,
return len;
}
-static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct hiddev_list *list = file->private_data;
struct hiddev *hiddev = list->hiddev;
@@ -555,7 +559,10 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
struct usbhid_device *usbhid = hid->driver_data;
void __user *user_arg = (void __user *)arg;
int i;
+
+ /* Called without BKL by compat methods so no BKL taken */
+ /* FIXME: Who or what stop this racing with a disconnect ?? */
if (!hiddev->exist)
return -EIO;
@@ -756,8 +763,7 @@ static int hiddev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
#ifdef CONFIG_COMPAT
static long hiddev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- struct inode *inode = file->f_path.dentry->d_inode;
- return hiddev_ioctl(inode, file, cmd, (unsigned long)compat_ptr(arg));
+ return hiddev_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
}
#endif
@@ -768,7 +774,7 @@ static const struct file_operations hiddev_fops = {
.poll = hiddev_poll,
.open = hiddev_open,
.release = hiddev_release,
- .ioctl = hiddev_ioctl,
+ .unlocked_ioctl = hiddev_ioctl,
.fasync = hiddev_fasync,
#ifdef CONFIG_COMPAT
.compat_ioctl = hiddev_compat_ioctl,
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 00ff53348491..c882fd05cf29 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -394,13 +394,24 @@ config SENSORS_LM75
tristate "National Semiconductor LM75 and compatibles"
depends on I2C
help
- If you say yes here you get support for National Semiconductor LM75
- sensor chips and clones: Dallas Semiconductor DS75 and DS1775 (in
- 9-bit precision mode), and TelCom (now Microchip) TCN75.
-
- The DS75 and DS1775 in 10- to 12-bit precision modes will require
- a force module parameter. The driver will not handle the extra
- precision anyhow.
+ If you say yes here you get support for one common type of
+ temperature sensor chip, with models including:
+
+ - Dallas Semiconductor DS75 and DS1775
+ - Maxim MAX6625 and MAX6626
+ - Microchip MCP980x
+ - National Semiconductor LM75
+ - NXP's LM75A
+ - ST Microelectronics STDS75
+ - TelCom (now Microchip) TCN75
+ - Texas Instruments TMP100, TMP101, TMP75, TMP175, TMP275
+
+ This driver supports driver model based binding through board
+ specific I2C device tables.
+
+ It also supports the "legacy" style of driver binding. To use
+ that with some chips which don't replicate LM75 quirks exactly,
+ you may need the "force" module parameter.
This driver can also be built as a module. If so, the module
will be called lm75.
diff --git a/drivers/hwmon/abituguru3.c b/drivers/hwmon/abituguru3.c
index ed33fddc4dee..f00f497b9ca9 100644
--- a/drivers/hwmon/abituguru3.c
+++ b/drivers/hwmon/abituguru3.c
@@ -30,6 +30,7 @@
#include <linux/platform_device.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
+#include <linux/dmi.h>
#include <asm/io.h>
/* uGuru3 bank addresses */
@@ -323,7 +324,7 @@ static const struct abituguru3_motherboard_info abituguru3_motherboards[] = {
{ "AUX1 Fan", 36, 2, 60, 1, 0 },
{ NULL, 0, 0, 0, 0, 0 } }
},
- { 0x0013, "unknown", {
+ { 0x0013, "Abit AW8D", {
{ "CPU Core", 0, 0, 10, 1, 0 },
{ "DDR", 1, 0, 10, 1, 0 },
{ "DDR VTT", 2, 0, 10, 1, 0 },
@@ -349,6 +350,7 @@ static const struct abituguru3_motherboard_info abituguru3_motherboards[] = {
{ "AUX2 Fan", 36, 2, 60, 1, 0 },
{ "AUX3 Fan", 37, 2, 60, 1, 0 },
{ "AUX4 Fan", 38, 2, 60, 1, 0 },
+ { "AUX5 Fan", 39, 2, 60, 1, 0 },
{ NULL, 0, 0, 0, 0, 0 } }
},
{ 0x0014, "Abit AB9 Pro", {
@@ -1111,11 +1113,12 @@ static int __init abituguru3_detect(void)
{
/* See if there is an uguru3 there. An idle uGuru3 will hold 0x00 or
0x08 at DATA and 0xAC at CMD. Sometimes the uGuru3 will hold 0x05
- at CMD instead, why is unknown. So we test for 0x05 too. */
+ or 0x55 at CMD instead, why is unknown. */
u8 data_val = inb_p(ABIT_UGURU3_BASE + ABIT_UGURU3_DATA);
u8 cmd_val = inb_p(ABIT_UGURU3_BASE + ABIT_UGURU3_CMD);
if (((data_val == 0x00) || (data_val == 0x08)) &&
- ((cmd_val == 0xAC) || (cmd_val == 0x05)))
+ ((cmd_val == 0xAC) || (cmd_val == 0x05) ||
+ (cmd_val == 0x55)))
return ABIT_UGURU3_BASE;
ABIT_UGURU3_DEBUG("no Abit uGuru3 found, data = 0x%02X, cmd = "
@@ -1138,6 +1141,15 @@ static int __init abituguru3_init(void)
int address, err;
struct resource res = { .flags = IORESOURCE_IO };
+#ifdef CONFIG_DMI
+ const char *board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
+
+ /* safety check, refuse to load on non Abit motherboards */
+ if (!force && (!board_vendor ||
+ strcmp(board_vendor, "http://www.abit.com.tw/")))
+ return -ENODEV;
+#endif
+
address = abituguru3_detect();
if (address < 0)
return address;
diff --git a/drivers/hwmon/adt7473.c b/drivers/hwmon/adt7473.c
index c1009d6f9796..0cd6c720a854 100644
--- a/drivers/hwmon/adt7473.c
+++ b/drivers/hwmon/adt7473.c
@@ -39,32 +39,20 @@ I2C_CLIENT_INSMOD_1(adt7473);
#define ADT7473_REG_BASE_ADDR 0x20
#define ADT7473_REG_VOLT_BASE_ADDR 0x21
-#define ADT7473_REG_VOLT_MAX_ADDR 0x22
#define ADT7473_REG_VOLT_MIN_BASE_ADDR 0x46
-#define ADT7473_REG_VOLT_MIN_MAX_ADDR 0x49
#define ADT7473_REG_TEMP_BASE_ADDR 0x25
-#define ADT7473_REG_TEMP_MAX_ADDR 0x27
#define ADT7473_REG_TEMP_LIMITS_BASE_ADDR 0x4E
-#define ADT7473_REG_TEMP_LIMITS_MAX_ADDR 0x53
#define ADT7473_REG_TEMP_TMIN_BASE_ADDR 0x67
-#define ADT7473_REG_TEMP_TMIN_MAX_ADDR 0x69
#define ADT7473_REG_TEMP_TMAX_BASE_ADDR 0x6A
-#define ADT7473_REG_TEMP_TMAX_MAX_ADDR 0x6C
#define ADT7473_REG_FAN_BASE_ADDR 0x28
-#define ADT7473_REG_FAN_MAX_ADDR 0x2F
#define ADT7473_REG_FAN_MIN_BASE_ADDR 0x54
-#define ADT7473_REG_FAN_MIN_MAX_ADDR 0x5B
#define ADT7473_REG_PWM_BASE_ADDR 0x30
-#define ADT7473_REG_PWM_MAX_ADDR 0x32
#define ADT7473_REG_PWM_MIN_BASE_ADDR 0x64
-#define ADT7473_REG_PWM_MIN_MAX_ADDR 0x66
#define ADT7473_REG_PWM_MAX_BASE_ADDR 0x38
-#define ADT7473_REG_PWM_MAX_MAX_ADDR 0x3A
#define ADT7473_REG_PWM_BHVR_BASE_ADDR 0x5C
-#define ADT7473_REG_PWM_BHVR_MAX_ADDR 0x5E
#define ADT7473_PWM_BHVR_MASK 0xE0
#define ADT7473_PWM_BHVR_SHIFT 5
@@ -102,7 +90,6 @@ I2C_CLIENT_INSMOD_1(adt7473);
#define ADT7473_FAN4_ALARM 0x20
#define ADT7473_R1T_SHORT 0x40
#define ADT7473_R2T_SHORT 0x80
-#define ADT7473_REG_MAX_ADDR 0x80
#define ALARM2(x) ((x) << 8)
@@ -309,6 +296,9 @@ no_sensor_update:
ADT7473_REG_PWM_BHVR(i));
}
+ i = i2c_smbus_read_byte_data(client, ADT7473_REG_CFG4);
+ data->max_duty_at_overheat = !!(i & ADT7473_CFG4_MAX_DUTY_AT_OVT);
+
data->limits_last_updated = local_jiffies;
data->limits_valid = 1;
@@ -569,10 +559,9 @@ static ssize_t set_max_duty_at_crit(struct device *dev,
struct i2c_client *client = to_i2c_client(dev);
struct adt7473_data *data = i2c_get_clientdata(client);
int temp = simple_strtol(buf, NULL, 10);
- temp = temp && 0xFF;
mutex_lock(&data->lock);
- data->max_duty_at_overheat = temp;
+ data->max_duty_at_overheat = !!temp;
reg = i2c_smbus_read_byte_data(client, ADT7473_REG_CFG4);
if (temp)
reg |= ADT7473_CFG4_MAX_DUTY_AT_OVT;
diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c
index 7673f65877e1..5e2cf0aef480 100644
--- a/drivers/hwmon/dme1737.c
+++ b/drivers/hwmon/dme1737.c
@@ -48,6 +48,11 @@ static unsigned short force_id;
module_param(force_id, ushort, 0);
MODULE_PARM_DESC(force_id, "Override the detected device ID");
+static int probe_all_addr;
+module_param(probe_all_addr, bool, 0);
+MODULE_PARM_DESC(probe_all_addr, "Include probing of non-standard LPC "
+ "addresses");
+
/* Addresses to scan */
static const unsigned short normal_i2c[] = {0x2c, 0x2d, 0x2e, I2C_CLIENT_END};
@@ -176,6 +181,7 @@ struct dme1737_data {
int valid; /* !=0 if following fields are valid */
unsigned long last_update; /* in jiffies */
unsigned long last_vbat; /* in jiffies */
+ enum chips type;
u8 vid;
u8 pwm_rr_en;
@@ -210,20 +216,27 @@ struct dme1737_data {
};
/* Nominal voltage values */
-static const int IN_NOMINAL[] = {5000, 2250, 3300, 5000, 12000, 3300, 3300};
+static const int IN_NOMINAL_DME1737[] = {5000, 2250, 3300, 5000, 12000, 3300,
+ 3300};
+static const int IN_NOMINAL_SCH311x[] = {2500, 1500, 3300, 5000, 12000, 3300,
+ 3300};
+#define IN_NOMINAL(ix, type) (((type) == dme1737) ? \
+ IN_NOMINAL_DME1737[(ix)] : \
+ IN_NOMINAL_SCH311x[(ix)])
/* Voltage input
* Voltage inputs have 16 bits resolution, limit values have 8 bits
* resolution. */
-static inline int IN_FROM_REG(int reg, int ix, int res)
+static inline int IN_FROM_REG(int reg, int ix, int res, int type)
{
- return (reg * IN_NOMINAL[ix] + (3 << (res - 3))) / (3 << (res - 2));
+ return (reg * IN_NOMINAL(ix, type) + (3 << (res - 3))) /
+ (3 << (res - 2));
}
-static inline int IN_TO_REG(int val, int ix)
+static inline int IN_TO_REG(int val, int ix, int type)
{
- return SENSORS_LIMIT((val * 192 + IN_NOMINAL[ix] / 2) /
- IN_NOMINAL[ix], 0, 255);
+ return SENSORS_LIMIT((val * 192 + IN_NOMINAL(ix, type) / 2) /
+ IN_NOMINAL(ix, type), 0, 255);
}
/* Temperature input
@@ -722,13 +735,13 @@ static ssize_t show_in(struct device *dev, struct device_attribute *attr,
switch (fn) {
case SYS_IN_INPUT:
- res = IN_FROM_REG(data->in[ix], ix, 16);
+ res = IN_FROM_REG(data->in[ix], ix, 16, data->type);
break;
case SYS_IN_MIN:
- res = IN_FROM_REG(data->in_min[ix], ix, 8);
+ res = IN_FROM_REG(data->in_min[ix], ix, 8, data->type);
break;
case SYS_IN_MAX:
- res = IN_FROM_REG(data->in_max[ix], ix, 8);
+ res = IN_FROM_REG(data->in_max[ix], ix, 8, data->type);
break;
case SYS_IN_ALARM:
res = (data->alarms >> DME1737_BIT_ALARM_IN[ix]) & 0x01;
@@ -755,12 +768,12 @@ static ssize_t set_in(struct device *dev, struct device_attribute *attr,
mutex_lock(&data->update_lock);
switch (fn) {
case SYS_IN_MIN:
- data->in_min[ix] = IN_TO_REG(val, ix);
+ data->in_min[ix] = IN_TO_REG(val, ix, data->type);
dme1737_write(client, DME1737_REG_IN_MIN(ix),
data->in_min[ix]);
break;
case SYS_IN_MAX:
- data->in_max[ix] = IN_TO_REG(val, ix);
+ data->in_max[ix] = IN_TO_REG(val, ix, data->type);
dme1737_write(client, DME1737_REG_IN_MAX(ix),
data->in_max[ix]);
break;
@@ -1501,9 +1514,9 @@ SENSOR_DEVICE_ATTR_PWM_1TO3(3);
/* PWMs 5-6 */
#define SENSOR_DEVICE_ATTR_PWM_5TO6(ix) \
-static SENSOR_DEVICE_ATTR_2(pwm##ix, S_IRUGO | S_IWUSR, \
+static SENSOR_DEVICE_ATTR_2(pwm##ix, S_IRUGO, \
show_pwm, set_pwm, SYS_PWM, ix-1); \
-static SENSOR_DEVICE_ATTR_2(pwm##ix##_freq, S_IRUGO | S_IWUSR, \
+static SENSOR_DEVICE_ATTR_2(pwm##ix##_freq, S_IRUGO, \
show_pwm, set_pwm, SYS_PWM_FREQ, ix-1); \
static SENSOR_DEVICE_ATTR_2(pwm##ix##_enable, S_IRUGO, \
show_pwm, NULL, SYS_PWM_ENABLE, ix-1)
@@ -1517,91 +1530,75 @@ static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); /* for ISA devices */
-#define SENSOR_DEV_ATTR_IN(ix) \
-&sensor_dev_attr_in##ix##_input.dev_attr.attr, \
-&sensor_dev_attr_in##ix##_min.dev_attr.attr, \
-&sensor_dev_attr_in##ix##_max.dev_attr.attr, \
-&sensor_dev_attr_in##ix##_alarm.dev_attr.attr
-
-/* These attributes are read-writeable only if the chip is *not* locked */
-#define SENSOR_DEV_ATTR_TEMP_LOCK(ix) \
-&sensor_dev_attr_temp##ix##_offset.dev_attr.attr
-
-#define SENSOR_DEV_ATTR_TEMP(ix) \
-SENSOR_DEV_ATTR_TEMP_LOCK(ix), \
-&sensor_dev_attr_temp##ix##_input.dev_attr.attr, \
-&sensor_dev_attr_temp##ix##_min.dev_attr.attr, \
-&sensor_dev_attr_temp##ix##_max.dev_attr.attr, \
-&sensor_dev_attr_temp##ix##_alarm.dev_attr.attr, \
-&sensor_dev_attr_temp##ix##_fault.dev_attr.attr
-
-/* These attributes are read-writeable only if the chip is *not* locked */
-#define SENSOR_DEV_ATTR_ZONE_LOCK(ix) \
-&sensor_dev_attr_zone##ix##_auto_point1_temp_hyst.dev_attr.attr, \
-&sensor_dev_attr_zone##ix##_auto_point1_temp.dev_attr.attr, \
-&sensor_dev_attr_zone##ix##_auto_point2_temp.dev_attr.attr, \
-&sensor_dev_attr_zone##ix##_auto_point3_temp.dev_attr.attr
-
-#define SENSOR_DEV_ATTR_ZONE(ix) \
-SENSOR_DEV_ATTR_ZONE_LOCK(ix), \
-&sensor_dev_attr_zone##ix##_auto_channels_temp.dev_attr.attr
-
-#define SENSOR_DEV_ATTR_FAN_1TO4(ix) \
-&sensor_dev_attr_fan##ix##_input.dev_attr.attr, \
-&sensor_dev_attr_fan##ix##_min.dev_attr.attr, \
-&sensor_dev_attr_fan##ix##_alarm.dev_attr.attr, \
-&sensor_dev_attr_fan##ix##_type.dev_attr.attr
-
-#define SENSOR_DEV_ATTR_FAN_5TO6(ix) \
-&sensor_dev_attr_fan##ix##_input.dev_attr.attr, \
-&sensor_dev_attr_fan##ix##_min.dev_attr.attr, \
-&sensor_dev_attr_fan##ix##_alarm.dev_attr.attr, \
-&sensor_dev_attr_fan##ix##_max.dev_attr.attr
-
-/* These attributes are read-writeable only if the chip is *not* locked */
-#define SENSOR_DEV_ATTR_PWM_1TO3_LOCK(ix) \
-&sensor_dev_attr_pwm##ix##_freq.dev_attr.attr, \
-&sensor_dev_attr_pwm##ix##_enable.dev_attr.attr, \
-&sensor_dev_attr_pwm##ix##_ramp_rate.dev_attr.attr, \
-&sensor_dev_attr_pwm##ix##_auto_channels_zone.dev_attr.attr, \
-&sensor_dev_attr_pwm##ix##_auto_pwm_min.dev_attr.attr, \
-&sensor_dev_attr_pwm##ix##_auto_point1_pwm.dev_attr.attr
-
-#define SENSOR_DEV_ATTR_PWM_1TO3(ix) \
-SENSOR_DEV_ATTR_PWM_1TO3_LOCK(ix), \
-&sensor_dev_attr_pwm##ix.dev_attr.attr, \
-&sensor_dev_attr_pwm##ix##_auto_point2_pwm.dev_attr.attr
-
-/* These attributes are read-writeable only if the chip is *not* locked */
-#define SENSOR_DEV_ATTR_PWM_5TO6_LOCK(ix) \
-&sensor_dev_attr_pwm##ix.dev_attr.attr, \
-&sensor_dev_attr_pwm##ix##_freq.dev_attr.attr
-
-#define SENSOR_DEV_ATTR_PWM_5TO6(ix) \
-SENSOR_DEV_ATTR_PWM_5TO6_LOCK(ix), \
-&sensor_dev_attr_pwm##ix##_enable.dev_attr.attr
-
/* This struct holds all the attributes that are always present and need to be
* created unconditionally. The attributes that need modification of their
* permissions are created read-only and write permissions are added or removed
* on the fly when required */
static struct attribute *dme1737_attr[] ={
/* Voltages */
- SENSOR_DEV_ATTR_IN(0),
- SENSOR_DEV_ATTR_IN(1),
- SENSOR_DEV_ATTR_IN(2),
- SENSOR_DEV_ATTR_IN(3),
- SENSOR_DEV_ATTR_IN(4),
- SENSOR_DEV_ATTR_IN(5),
- SENSOR_DEV_ATTR_IN(6),
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in5_alarm.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in6_alarm.dev_attr.attr,
/* Temperatures */
- SENSOR_DEV_ATTR_TEMP(1),
- SENSOR_DEV_ATTR_TEMP(2),
- SENSOR_DEV_ATTR_TEMP(3),
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_fault.dev_attr.attr,
+ &sensor_dev_attr_temp1_offset.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &sensor_dev_attr_temp2_offset.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_fault.dev_attr.attr,
+ &sensor_dev_attr_temp3_offset.dev_attr.attr,
/* Zones */
- SENSOR_DEV_ATTR_ZONE(1),
- SENSOR_DEV_ATTR_ZONE(2),
- SENSOR_DEV_ATTR_ZONE(3),
+ &sensor_dev_attr_zone1_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_zone1_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_zone1_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_zone1_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_zone1_auto_channels_temp.dev_attr.attr,
+ &sensor_dev_attr_zone2_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_zone2_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_zone2_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_zone2_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_zone2_auto_channels_temp.dev_attr.attr,
+ &sensor_dev_attr_zone3_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_zone3_auto_channels_temp.dev_attr.attr,
/* Misc */
&dev_attr_vrm.attr,
&dev_attr_cpu0_vid.attr,
@@ -1616,23 +1613,48 @@ static const struct attribute_group dme1737_group = {
* Their creation depends on the chip configuration which is determined during
* module load. */
static struct attribute *dme1737_attr_pwm1[] = {
- SENSOR_DEV_ATTR_PWM_1TO3(1),
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm1_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1_ramp_rate.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_channels_zone.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_pwm_min.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
NULL
};
static struct attribute *dme1737_attr_pwm2[] = {
- SENSOR_DEV_ATTR_PWM_1TO3(2),
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm2_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2_ramp_rate.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_channels_zone.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_pwm_min.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr,
NULL
};
static struct attribute *dme1737_attr_pwm3[] = {
- SENSOR_DEV_ATTR_PWM_1TO3(3),
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+ &sensor_dev_attr_pwm3_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm3_ramp_rate.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_channels_zone.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_pwm_min.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr,
NULL
};
static struct attribute *dme1737_attr_pwm5[] = {
- SENSOR_DEV_ATTR_PWM_5TO6(5),
+ &sensor_dev_attr_pwm5.dev_attr.attr,
+ &sensor_dev_attr_pwm5_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm5_enable.dev_attr.attr,
NULL
};
static struct attribute *dme1737_attr_pwm6[] = {
- SENSOR_DEV_ATTR_PWM_5TO6(6),
+ &sensor_dev_attr_pwm6.dev_attr.attr,
+ &sensor_dev_attr_pwm6_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm6_enable.dev_attr.attr,
NULL
};
@@ -1649,27 +1671,45 @@ static const struct attribute_group dme1737_pwm_group[] = {
* Their creation depends on the chip configuration which is determined during
* module load. */
static struct attribute *dme1737_attr_fan1[] = {
- SENSOR_DEV_ATTR_FAN_1TO4(1),
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan1_type.dev_attr.attr,
NULL
};
static struct attribute *dme1737_attr_fan2[] = {
- SENSOR_DEV_ATTR_FAN_1TO4(2),
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan2_type.dev_attr.attr,
NULL
};
static struct attribute *dme1737_attr_fan3[] = {
- SENSOR_DEV_ATTR_FAN_1TO4(3),
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan3_type.dev_attr.attr,
NULL
};
static struct attribute *dme1737_attr_fan4[] = {
- SENSOR_DEV_ATTR_FAN_1TO4(4),
+ &sensor_dev_attr_fan4_input.dev_attr.attr,
+ &sensor_dev_attr_fan4_min.dev_attr.attr,
+ &sensor_dev_attr_fan4_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan4_type.dev_attr.attr,
NULL
};
static struct attribute *dme1737_attr_fan5[] = {
- SENSOR_DEV_ATTR_FAN_5TO6(5),
+ &sensor_dev_attr_fan5_input.dev_attr.attr,
+ &sensor_dev_attr_fan5_min.dev_attr.attr,
+ &sensor_dev_attr_fan5_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan5_max.dev_attr.attr,
NULL
};
static struct attribute *dme1737_attr_fan6[] = {
- SENSOR_DEV_ATTR_FAN_5TO6(6),
+ &sensor_dev_attr_fan6_input.dev_attr.attr,
+ &sensor_dev_attr_fan6_min.dev_attr.attr,
+ &sensor_dev_attr_fan6_alarm.dev_attr.attr,
+ &sensor_dev_attr_fan6_max.dev_attr.attr,
NULL
};
@@ -1686,13 +1726,22 @@ static const struct attribute_group dme1737_fan_group[] = {
* writeable if the chip is *not* locked. Otherwise they stay read-only. */
static struct attribute *dme1737_attr_lock[] = {
/* Temperatures */
- SENSOR_DEV_ATTR_TEMP_LOCK(1),
- SENSOR_DEV_ATTR_TEMP_LOCK(2),
- SENSOR_DEV_ATTR_TEMP_LOCK(3),
+ &sensor_dev_attr_temp1_offset.dev_attr.attr,
+ &sensor_dev_attr_temp2_offset.dev_attr.attr,
+ &sensor_dev_attr_temp3_offset.dev_attr.attr,
/* Zones */
- SENSOR_DEV_ATTR_ZONE_LOCK(1),
- SENSOR_DEV_ATTR_ZONE_LOCK(2),
- SENSOR_DEV_ATTR_ZONE_LOCK(3),
+ &sensor_dev_attr_zone1_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_zone1_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_zone1_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_zone1_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_zone2_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_zone2_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_zone2_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_zone2_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_zone3_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_zone3_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_zone3_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_zone3_auto_point3_temp.dev_attr.attr,
NULL
};
@@ -1704,23 +1753,40 @@ static const struct attribute_group dme1737_lock_group = {
* writeable if the chip is *not* locked and the respective PWM is available.
* Otherwise they stay read-only. */
static struct attribute *dme1737_attr_pwm1_lock[] = {
- SENSOR_DEV_ATTR_PWM_1TO3_LOCK(1),
+ &sensor_dev_attr_pwm1_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1_ramp_rate.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_channels_zone.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_pwm_min.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
NULL
};
static struct attribute *dme1737_attr_pwm2_lock[] = {
- SENSOR_DEV_ATTR_PWM_1TO3_LOCK(2),
+ &sensor_dev_attr_pwm2_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2_ramp_rate.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_channels_zone.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_pwm_min.dev_attr.attr,
+ &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr,
NULL
};
static struct attribute *dme1737_attr_pwm3_lock[] = {
- SENSOR_DEV_ATTR_PWM_1TO3_LOCK(3),
+ &sensor_dev_attr_pwm3_freq.dev_attr.attr,
+ &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm3_ramp_rate.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_channels_zone.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_pwm_min.dev_attr.attr,
+ &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr,
NULL
};
static struct attribute *dme1737_attr_pwm5_lock[] = {
- SENSOR_DEV_ATTR_PWM_5TO6_LOCK(5),
+ &sensor_dev_attr_pwm5.dev_attr.attr,
+ &sensor_dev_attr_pwm5_freq.dev_attr.attr,
NULL
};
static struct attribute *dme1737_attr_pwm6_lock[] = {
- SENSOR_DEV_ATTR_PWM_5TO6_LOCK(6),
+ &sensor_dev_attr_pwm6.dev_attr.attr,
+ &sensor_dev_attr_pwm6_freq.dev_attr.attr,
NULL
};
@@ -2109,6 +2175,7 @@ static int dme1737_i2c_detect(struct i2c_adapter *adapter, int address,
kind = dme1737;
name = "dme1737";
+ data->type = kind;
/* Fill in the remaining client fields and put it into the global
* list */
@@ -2301,6 +2368,7 @@ static int __devinit dme1737_isa_probe(struct platform_device *pdev)
err = -ENODEV;
goto exit_kfree;
}
+ data->type = -1;
/* Fill in the remaining client fields and initialize the mutex */
strlcpy(client->name, "sch311x", I2C_NAME_SIZE);
@@ -2377,7 +2445,10 @@ static int __init dme1737_init(void)
}
if (dme1737_isa_detect(0x2e, &addr) &&
- dme1737_isa_detect(0x4e, &addr)) {
+ dme1737_isa_detect(0x4e, &addr) &&
+ (!probe_all_addr ||
+ (dme1737_isa_detect(0x162e, &addr) &&
+ dme1737_isa_detect(0x164e, &addr)))) {
/* Return 0 if we didn't find an ISA device */
return 0;
}
diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
index 3db28450a3b3..7321a88a5112 100644
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -55,7 +55,8 @@ again:
return ERR_PTR(err);
id = id & MAX_ID_MASK;
- hwdev = device_create(hwmon_class, dev, MKDEV(0,0), HWMON_ID_FORMAT, id);
+ hwdev = device_create_drvdata(hwmon_class, dev, MKDEV(0, 0), NULL,
+ HWMON_ID_FORMAT, id);
if (IS_ERR(hwdev)) {
spin_lock(&idr_lock);
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
index fa7696905154..7880c273c2c5 100644
--- a/drivers/hwmon/lm75.c
+++ b/drivers/hwmon/lm75.c
@@ -30,14 +30,37 @@
#include "lm75.h"
-/* Addresses to scan */
+/*
+ * This driver handles the LM75 and compatible digital temperature sensors.
+ * Only types which are _not_ listed in I2C_CLIENT_INSMOD_*() need to be
+ * listed here. We start at 9 since I2C_CLIENT_INSMOD_*() currently allow
+ * definition of up to 8 chip types (plus zero).
+ */
+
+enum lm75_type { /* keep sorted in alphabetical order */
+ ds1775 = 9,
+ ds75,
+ /* lm75 -- in I2C_CLIENT_INSMOD_1() */
+ lm75a,
+ max6625,
+ max6626,
+ mcp980x,
+ stds75,
+ tcn75,
+ tmp100,
+ tmp101,
+ tmp175,
+ tmp275,
+ tmp75,
+};
+
+/* Addresses scanned by legacy style driver binding */
static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c,
0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
-/* Insmod parameters */
+/* Insmod parameters (only for legacy style driver binding) */
I2C_CLIENT_INSMOD_1(lm75);
-/* Many LM75 constants specified below */
/* The LM75 registers */
#define LM75_REG_CONF 0x01
@@ -49,10 +72,11 @@ static const u8 LM75_REG_TEMP[3] = {
/* Each client has this additional data */
struct lm75_data {
- struct i2c_client client;
- struct device *hwmon_dev;
+ struct i2c_client *client;
+ struct device *hwmon_dev;
struct mutex update_lock;
- char valid; /* !=0 if following fields are valid */
+ u8 orig_conf;
+ char valid; /* !=0 if registers are valid */
unsigned long last_updated; /* In jiffies */
u16 temp[3]; /* Register values,
0 = input
@@ -60,23 +84,14 @@ struct lm75_data {
2 = hyst */
};
-static int lm75_attach_adapter(struct i2c_adapter *adapter);
-static int lm75_detect(struct i2c_adapter *adapter, int address, int kind);
-static void lm75_init_client(struct i2c_client *client);
-static int lm75_detach_client(struct i2c_client *client);
static int lm75_read_value(struct i2c_client *client, u8 reg);
static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value);
static struct lm75_data *lm75_update_device(struct device *dev);
-/* This is the driver that will be inserted */
-static struct i2c_driver lm75_driver = {
- .driver = {
- .name = "lm75",
- },
- .attach_adapter = lm75_attach_adapter,
- .detach_client = lm75_detach_client,
-};
+/*-----------------------------------------------------------------------*/
+
+/* sysfs attributes for hwmon */
static ssize_t show_temp(struct device *dev, struct device_attribute *da,
char *buf)
@@ -109,13 +124,6 @@ static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
show_temp, set_temp, 2);
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
-static int lm75_attach_adapter(struct i2c_adapter *adapter)
-{
- if (!(adapter->class & I2C_CLASS_HWMON))
- return 0;
- return i2c_probe(adapter, &addr_data, lm75_detect);
-}
-
static struct attribute *lm75_attributes[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
@@ -128,32 +136,144 @@ static const struct attribute_group lm75_group = {
.attrs = lm75_attributes,
};
+/*-----------------------------------------------------------------------*/
+
+/* "New style" I2C driver binding -- following the driver model */
+
+static int
+lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct lm75_data *data;
+ int status;
+ u8 set_mask, clr_mask;
+ int new;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
+ return -EIO;
+
+ data = kzalloc(sizeof(struct lm75_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+
+ data->client = client;
+ mutex_init(&data->update_lock);
+
+ /* Set to LM75 resolution (9 bits, 1/2 degree C) and range.
+ * Then tweak to be more precise when appropriate.
+ */
+ set_mask = 0;
+ clr_mask = (1 << 0) /* continuous conversions */
+ | (1 << 6) | (1 << 5); /* 9-bit mode */
+
+ /* configure as specified */
+ status = lm75_read_value(client, LM75_REG_CONF);
+ if (status < 0) {
+ dev_dbg(&client->dev, "Can't read config? %d\n", status);
+ goto exit_free;
+ }
+ data->orig_conf = status;
+ new = status & ~clr_mask;
+ new |= set_mask;
+ if (status != new)
+ lm75_write_value(client, LM75_REG_CONF, new);
+ dev_dbg(&client->dev, "Config %02x\n", new);
+
+ /* Register sysfs hooks */
+ status = sysfs_create_group(&client->dev.kobj, &lm75_group);
+ if (status)
+ goto exit_free;
+
+ data->hwmon_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ status = PTR_ERR(data->hwmon_dev);
+ goto exit_remove;
+ }
+
+ dev_info(&client->dev, "%s: sensor '%s'\n",
+ data->hwmon_dev->bus_id, client->name);
+
+ return 0;
+
+exit_remove:
+ sysfs_remove_group(&client->dev.kobj, &lm75_group);
+exit_free:
+ i2c_set_clientdata(client, NULL);
+ kfree(data);
+ return status;
+}
+
+static int lm75_remove(struct i2c_client *client)
+{
+ struct lm75_data *data = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm75_group);
+ lm75_write_value(client, LM75_REG_CONF, data->orig_conf);
+ i2c_set_clientdata(client, NULL);
+ kfree(data);
+ return 0;
+}
+
+static const struct i2c_device_id lm75_ids[] = {
+ { "ds1775", ds1775, },
+ { "ds75", ds75, },
+ { "lm75", lm75, },
+ { "lm75a", lm75a, },
+ { "max6625", max6625, },
+ { "max6626", max6626, },
+ { "mcp980x", mcp980x, },
+ { "stds75", stds75, },
+ { "tcn75", tcn75, },
+ { "tmp100", tmp100, },
+ { "tmp101", tmp101, },
+ { "tmp175", tmp175, },
+ { "tmp275", tmp275, },
+ { "tmp75", tmp75, },
+ { /* LIST END */ }
+};
+MODULE_DEVICE_TABLE(i2c, lm75_ids);
+
+static struct i2c_driver lm75_driver = {
+ .driver = {
+ .name = "lm75",
+ },
+ .probe = lm75_probe,
+ .remove = lm75_remove,
+ .id_table = lm75_ids,
+};
+
+/*-----------------------------------------------------------------------*/
+
+/* "Legacy" I2C driver binding */
+
+static struct i2c_driver lm75_legacy_driver;
+
/* This function is called by i2c_probe */
static int lm75_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i;
struct i2c_client *new_client;
- struct lm75_data *data;
int err = 0;
- const char *name = "";
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
goto exit;
- /* OK. For now, we presume we have a valid client. We now create the
- client structure, even though we cannot fill it completely yet.
- But it allows us to access lm75_{read,write}_value. */
- if (!(data = kzalloc(sizeof(struct lm75_data), GFP_KERNEL))) {
+ /* OK. For now, we presume we have a valid address. We create the
+ client structure, even though there may be no sensor present.
+ But it allows us to use i2c_smbus_read_*_data() calls. */
+ new_client = kzalloc(sizeof *new_client, GFP_KERNEL);
+ if (!new_client) {
err = -ENOMEM;
goto exit;
}
- new_client = &data->client;
- i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
- new_client->driver = &lm75_driver;
+ new_client->driver = &lm75_legacy_driver;
new_client->flags = 0;
/* Now, we do the remaining detection. There is no identification-
@@ -174,17 +294,17 @@ static int lm75_detect(struct i2c_adapter *adapter, int address, int kind)
|| i2c_smbus_read_word_data(new_client, 5) != hyst
|| i2c_smbus_read_word_data(new_client, 6) != hyst
|| i2c_smbus_read_word_data(new_client, 7) != hyst)
- goto exit_free;
+ goto exit_free;
os = i2c_smbus_read_word_data(new_client, 3);
if (i2c_smbus_read_word_data(new_client, 4) != os
|| i2c_smbus_read_word_data(new_client, 5) != os
|| i2c_smbus_read_word_data(new_client, 6) != os
|| i2c_smbus_read_word_data(new_client, 7) != os)
- goto exit_free;
+ goto exit_free;
/* Unused bits */
if (conf & 0xe0)
- goto exit_free;
+ goto exit_free;
/* Addresses cycling */
for (i = 8; i < 0xff; i += 8)
@@ -194,67 +314,69 @@ static int lm75_detect(struct i2c_adapter *adapter, int address, int kind)
goto exit_free;
}
- /* Determine the chip type - only one kind supported! */
- if (kind <= 0)
- kind = lm75;
-
- if (kind == lm75) {
- name = "lm75";
- }
-
- /* Fill in the remaining client fields and put it into the global list */
- strlcpy(new_client->name, name, I2C_NAME_SIZE);
- data->valid = 0;
- mutex_init(&data->update_lock);
+ /* NOTE: we treat "force=..." and "force_lm75=..." the same.
+ * Only new-style driver binding distinguishes chip types.
+ */
+ strlcpy(new_client->name, "lm75", I2C_NAME_SIZE);
/* Tell the I2C layer a new client has arrived */
- if ((err = i2c_attach_client(new_client)))
+ err = i2c_attach_client(new_client);
+ if (err)
goto exit_free;
- /* Initialize the LM75 chip */
- lm75_init_client(new_client);
-
- /* Register sysfs hooks */
- if ((err = sysfs_create_group(&new_client->dev.kobj, &lm75_group)))
+ err = lm75_probe(new_client, NULL);
+ if (err < 0)
goto exit_detach;
- data->hwmon_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->hwmon_dev)) {
- err = PTR_ERR(data->hwmon_dev);
- goto exit_remove;
- }
-
return 0;
-exit_remove:
- sysfs_remove_group(&new_client->dev.kobj, &lm75_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
- kfree(data);
+ kfree(new_client);
exit:
return err;
}
+static int lm75_attach_adapter(struct i2c_adapter *adapter)
+{
+ if (!(adapter->class & I2C_CLASS_HWMON))
+ return 0;
+ return i2c_probe(adapter, &addr_data, lm75_detect);
+}
+
static int lm75_detach_client(struct i2c_client *client)
{
- struct lm75_data *data = i2c_get_clientdata(client);
- hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&client->dev.kobj, &lm75_group);
+ lm75_remove(client);
i2c_detach_client(client);
- kfree(data);
+ kfree(client);
return 0;
}
+static struct i2c_driver lm75_legacy_driver = {
+ .driver = {
+ .name = "lm75_legacy",
+ },
+ .attach_adapter = lm75_attach_adapter,
+ .detach_client = lm75_detach_client,
+};
+
+/*-----------------------------------------------------------------------*/
+
+/* register access */
+
/* All registers are word-sized, except for the configuration register.
LM75 uses a high-byte first convention, which is exactly opposite to
the SMBus standard. */
static int lm75_read_value(struct i2c_client *client, u8 reg)
{
+ int value;
+
if (reg == LM75_REG_CONF)
return i2c_smbus_read_byte_data(client, reg);
- else
- return swab16(i2c_smbus_read_word_data(client, reg));
+
+ value = i2c_smbus_read_word_data(client, reg);
+ return (value < 0) ? value : swab16(value);
}
static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value)
@@ -265,16 +387,6 @@ static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value)
return i2c_smbus_write_word_data(client, reg, swab16(value));
}
-static void lm75_init_client(struct i2c_client *client)
-{
- int reg;
-
- /* Enable if in shutdown mode */
- reg = lm75_read_value(client, LM75_REG_CONF);
- if (reg >= 0 && (reg & 0x01))
- lm75_write_value(client, LM75_REG_CONF, reg & 0xfe);
-}
-
static struct lm75_data *lm75_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -287,9 +399,16 @@ static struct lm75_data *lm75_update_device(struct device *dev)
int i;
dev_dbg(&client->dev, "Starting lm75 update\n");
- for (i = 0; i < ARRAY_SIZE(data->temp); i++)
- data->temp[i] = lm75_read_value(client,
- LM75_REG_TEMP[i]);
+ for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
+ int status;
+
+ status = lm75_read_value(client, LM75_REG_TEMP[i]);
+ if (status < 0)
+ dev_dbg(&client->dev, "reg %d, err %d\n",
+ LM75_REG_TEMP[i], status);
+ else
+ data->temp[i] = status;
+ }
data->last_updated = jiffies;
data->valid = 1;
}
@@ -299,13 +418,28 @@ static struct lm75_data *lm75_update_device(struct device *dev)
return data;
}
+/*-----------------------------------------------------------------------*/
+
+/* module glue */
+
static int __init sensors_lm75_init(void)
{
- return i2c_add_driver(&lm75_driver);
+ int status;
+
+ status = i2c_add_driver(&lm75_driver);
+ if (status < 0)
+ return status;
+
+ status = i2c_add_driver(&lm75_legacy_driver);
+ if (status < 0)
+ i2c_del_driver(&lm75_driver);
+
+ return status;
}
static void __exit sensors_lm75_exit(void)
{
+ i2c_del_driver(&lm75_legacy_driver);
i2c_del_driver(&lm75_driver);
}
diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c
index 182fe6a5605f..ee5eca1c1921 100644
--- a/drivers/hwmon/lm85.c
+++ b/drivers/hwmon/lm85.c
@@ -192,23 +192,20 @@ static int RANGE_TO_REG( int range )
{
int i;
- if ( range < lm85_range_map[0] ) {
- return 0 ;
- } else if ( range > lm85_range_map[15] ) {
+ if (range >= lm85_range_map[15])
return 15 ;
- } else { /* find closest match */
- for ( i = 14 ; i >= 0 ; --i ) {
- if ( range > lm85_range_map[i] ) { /* range bracketed */
- if ((lm85_range_map[i+1] - range) <
- (range - lm85_range_map[i])) {
- i++;
- break;
- }
- break;
- }
+
+ /* Find the closest match */
+ for (i = 14; i >= 0; --i) {
+ if (range >= lm85_range_map[i]) {
+ if ((lm85_range_map[i + 1] - range) <
+ (range - lm85_range_map[i]))
+ return i + 1;
+ return i;
}
}
- return( i & 0x0f );
+
+ return 0;
}
#define RANGE_FROM_REG(val) (lm85_range_map[(val)&0x0f])
diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c
index 35812823787b..eb8f72ca02f4 100644
--- a/drivers/i2c/algos/i2c-algo-bit.c
+++ b/drivers/i2c/algos/i2c-algo-bit.c
@@ -320,7 +320,7 @@ static int try_address(struct i2c_adapter *i2c_adap,
unsigned char addr, int retries)
{
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
- int i, ret = -1;
+ int i, ret = 0;
for (i = 0; i <= retries; i++) {
ret = i2c_outb(i2c_adap, addr);
@@ -508,7 +508,7 @@ static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
addr ^= 1;
ret = try_address(i2c_adap, addr, retries);
if ((ret != 1) && !nak_ok)
- return -EREMOTEIO;
+ return -ENXIO;
}
return 0;
diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c
index e954a20b97a6..d50b329a3c94 100644
--- a/drivers/i2c/algos/i2c-algo-pca.c
+++ b/drivers/i2c/algos/i2c-algo-pca.c
@@ -182,7 +182,7 @@ static int pca_xfer(struct i2c_adapter *i2c_adap,
}
if (state != 0xf8) {
dev_dbg(&i2c_adap->dev, "bus is not idle. status is %#04x\n", state);
- return -EIO;
+ return -EAGAIN;
}
DEB1("{{{ XFER %d messages\n", num);
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 48438cc5d0ca..4aeefa6d5ac7 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -4,6 +4,8 @@
menu "I2C Hardware Bus support"
+comment "PC SMBus host controller drivers"
+
config I2C_ALI1535
tristate "ALI 1535"
depends on PCI
@@ -73,6 +75,238 @@ config I2C_AMD8111
This driver can also be built as a module. If so, the module
will be called i2c-amd8111.
+config I2C_I801
+ tristate "Intel 82801 (ICH)"
+ depends on PCI
+ help
+ If you say yes to this option, support will be included for the Intel
+ 801 family of mainboard I2C interfaces. Specifically, the following
+ versions of the chipset are supported:
+ 82801AA
+ 82801AB
+ 82801BA
+ 82801CA/CAM
+ 82801DB
+ 82801EB/ER (ICH5/ICH5R)
+ 6300ESB
+ ICH6
+ ICH7
+ ESB2
+ ICH8
+ ICH9
+ Tolapai
+ ICH10
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-i801.
+
+config I2C_PIIX4
+ tristate "Intel PIIX4 and compatible (ATI/Serverworks/Broadcom/SMSC)"
+ depends on PCI
+ help
+ If you say yes to this option, support will be included for the Intel
+ PIIX4 family of mainboard I2C interfaces. Specifically, the following
+ versions of the chipset are supported (note that Serverworks is part
+ of Broadcom):
+ Intel PIIX4
+ Intel 440MX
+ ATI IXP200
+ ATI IXP300
+ ATI IXP400
+ ATI SB600
+ ATI SB700
+ ATI SB800
+ Serverworks OSB4
+ Serverworks CSB5
+ Serverworks CSB6
+ Serverworks HT-1000
+ SMSC Victory66
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-piix4.
+
+config I2C_NFORCE2
+ tristate "Nvidia nForce2, nForce3 and nForce4"
+ depends on PCI
+ help
+ If you say yes to this option, support will be included for the Nvidia
+ nForce2, nForce3 and nForce4 families of mainboard I2C interfaces.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-nforce2.
+
+config I2C_NFORCE2_S4985
+ tristate "SMBus multiplexing on the Tyan S4985"
+ depends on I2C_NFORCE2 && EXPERIMENTAL
+ help
+ Enabling this option will add specific SMBus support for the Tyan
+ S4985 motherboard. On this 4-CPU board, the SMBus is multiplexed
+ over 4 different channels, where the various memory module EEPROMs
+ live. Saying yes here will give you access to these in addition
+ to the trunk.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-nforce2-s4985.
+
+config I2C_SIS5595
+ tristate "SiS 5595"
+ depends on PCI
+ help
+ If you say yes to this option, support will be included for the
+ SiS5595 SMBus (a subset of I2C) interface.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-sis5595.
+
+config I2C_SIS630
+ tristate "SiS 630/730"
+ depends on PCI
+ help
+ If you say yes to this option, support will be included for the
+ SiS630 and SiS730 SMBus (a subset of I2C) interface.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-sis630.
+
+config I2C_SIS96X
+ tristate "SiS 96x"
+ depends on PCI
+ help
+ If you say yes to this option, support will be included for the SiS
+ 96x SMBus (a subset of I2C) interfaces. Specifically, the following
+ chipsets are supported:
+ 645/961
+ 645DX/961
+ 645DX/962
+ 648/961
+ 650/961
+ 735
+ 745
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-sis96x.
+
+config I2C_VIA
+ tristate "VIA VT82C586B"
+ depends on PCI && EXPERIMENTAL
+ select I2C_ALGOBIT
+ help
+ If you say yes to this option, support will be included for the VIA
+ 82C586B I2C interface
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-via.
+
+config I2C_VIAPRO
+ tristate "VIA VT82C596/82C686/82xx and CX700"
+ depends on PCI
+ help
+ If you say yes to this option, support will be included for the VIA
+ VT82C596 and later SMBus interface. Specifically, the following
+ chipsets are supported:
+ VT82C596A/B
+ VT82C686A/B
+ VT8231
+ VT8233/A
+ VT8235
+ VT8237R/A/S
+ VT8251
+ CX700
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-viapro.
+
+comment "Graphics adapter I2C/DDC channel drivers"
+
+config I2C_VOODOO3
+ tristate "Voodoo 3"
+ depends on PCI
+ select I2C_ALGOBIT
+ help
+ If you say yes to this option, support will be included for the
+ Voodoo 3 I2C interface.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-voodoo3.
+
+comment "External I2C/SMBus adapter drivers"
+
+config I2C_PARPORT
+ tristate "Parallel port adapter"
+ depends on PARPORT
+ select I2C_ALGOBIT
+ help
+ This supports parallel port I2C adapters such as the ones made by
+ Philips or Velleman, Analog Devices evaluation boards, and more.
+ Basically any adapter using the parallel port as an I2C bus with
+ no extra chipset is supported by this driver, or could be.
+
+ This driver is a replacement for (and was inspired by) an older
+ driver named i2c-philips-par. The new driver supports more devices,
+ and makes it easier to add support for new devices.
+
+ An adapter type parameter is now mandatory. Please read the file
+ Documentation/i2c/busses/i2c-parport for details.
+
+ Another driver exists, named i2c-parport-light, which doesn't depend
+ on the parport driver. This is meant for embedded systems. Don't say
+ Y here if you intend to say Y or M there.
+
+ This support is also available as a module. If so, the module
+ will be called i2c-parport.
+
+config I2C_PARPORT_LIGHT
+ tristate "Parallel port adapter (light)"
+ select I2C_ALGOBIT
+ help
+ This supports parallel port I2C adapters such as the ones made by
+ Philips or Velleman, Analog Devices evaluation boards, and more.
+ Basically any adapter using the parallel port as an I2C bus with
+ no extra chipset is supported by this driver, or could be.
+
+ This driver is a light version of i2c-parport. It doesn't depend
+ on the parport driver, and uses direct I/O access instead. This
+ might be preferred on embedded systems where wasting memory for
+ the clean but heavy parport handling is not an option. The
+ drawback is a reduced portability and the impossibility to
+ daisy-chain other parallel port devices.
+
+ Don't say Y here if you said Y or M to i2c-parport. Saying M to
+ both is possible but both modules should not be loaded at the same
+ time.
+
+ This support is also available as a module. If so, the module
+ will be called i2c-parport-light.
+
+config I2C_TAOS_EVM
+ tristate "TAOS evaluation module"
+ depends on EXPERIMENTAL
+ select SERIO
+ select SERIO_SERPORT
+ default n
+ help
+ This supports TAOS evaluation modules on serial port. In order to
+ use this driver, you will need the inputattach tool, which is part
+ of the input-utils package.
+
+ If unsure, say N.
+
+ This support is also available as a module. If so, the module
+ will be called i2c-taos-evm.
+
+config I2C_TINY_USB
+ tristate "Tiny-USB adapter"
+ depends on USB
+ help
+ If you say yes to this option, support will be included for the
+ i2c-tiny-usb, a simple do-it-yourself USB to I2C interface. See
+ http://www.harbaum.org/till/i2c_tiny_usb for hardware details.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-tiny-usb.
+
+comment "Other I2C/SMBus bus drivers"
+
config I2C_AT91
tristate "Atmel AT91 I2C Two-Wire interface (TWI)"
depends on ARCH_AT91 && EXPERIMENTAL && BROKEN
@@ -101,10 +335,9 @@ config I2C_AU1550
config I2C_BLACKFIN_TWI
tristate "Blackfin TWI I2C support"
depends on BLACKFIN
+ depends on !BF561 && !BF531 && !BF532 && !BF533
help
- This is the TWI I2C device driver for Blackfin BF522, BF525,
- BF527, BF534, BF536, BF537 and BF54x. For other Blackfin processors,
- please don't use this driver.
+ This is the I2C bus driver for Blackfin on-chip TWI interface.
This driver can also be built as a module. If so, the module
will be called i2c-bfin-twi.
@@ -117,6 +350,16 @@ config I2C_BLACKFIN_TWI_CLK_KHZ
help
The unit of the TWI clock is kHz.
+config I2C_CPM
+ tristate "Freescale CPM1 or CPM2 (MPC8xx/826x)"
+ depends on (CPM1 || CPM2) && OF_I2C
+ help
+ This supports the use of the I2C interface on Freescale
+ processors with CPM1 or CPM2.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-cpm.
+
config I2C_DAVINCI
tristate "DaVinci I2C driver"
depends on ARCH_DAVINCI
@@ -161,51 +404,6 @@ config I2C_HYDRA
This support is also available as a module. If so, the module
will be called i2c-hydra.
-config I2C_I801
- tristate "Intel 82801 (ICH)"
- depends on PCI
- help
- If you say yes to this option, support will be included for the Intel
- 801 family of mainboard I2C interfaces. Specifically, the following
- versions of the chipset are supported:
- 82801AA
- 82801AB
- 82801BA
- 82801CA/CAM
- 82801DB
- 82801EB/ER (ICH5/ICH5R)
- 6300ESB
- ICH6
- ICH7
- ESB2
- ICH8
- ICH9
- Tolapai
- ICH10
-
- This driver can also be built as a module. If so, the module
- will be called i2c-i801.
-
-config I2C_I810
- tristate "Intel 810/815 (DEPRECATED)"
- default n
- depends on PCI
- select I2C_ALGOBIT
- help
- If you say yes to this option, support will be included for the Intel
- 810/815 family of mainboard I2C interfaces. Specifically, the
- following versions of the chipset are supported:
- i810AA
- i810AB
- i810E
- i815
- i845G
-
- This driver is deprecated in favor of the i810fb and intelfb drivers.
-
- This driver can also be built as a module. If so, the module
- will be called i2c-i810.
-
config I2C_PXA
tristate "Intel PXA2XX I2C adapter (EXPERIMENTAL)"
depends on EXPERIMENTAL && ARCH_PXA
@@ -222,31 +420,6 @@ config I2C_PXA_SLAVE
is necessary for systems where the PXA may be a target on the
I2C bus.
-config I2C_PIIX4
- tristate "Intel PIIX4 and compatible (ATI/Serverworks/Broadcom/SMSC)"
- depends on PCI
- help
- If you say yes to this option, support will be included for the Intel
- PIIX4 family of mainboard I2C interfaces. Specifically, the following
- versions of the chipset are supported (note that Serverworks is part
- of Broadcom):
- Intel PIIX4
- Intel 440MX
- ATI IXP200
- ATI IXP300
- ATI IXP400
- ATI SB600
- ATI SB700
- ATI SB800
- Serverworks OSB4
- Serverworks CSB5
- Serverworks CSB6
- Serverworks HT-1000
- SMSC Victory66
-
- This driver can also be built as a module. If so, the module
- will be called i2c-piix4.
-
config I2C_IBM_IIC
tristate "IBM PPC 4xx on-chip I2C interface"
depends on 4xx
@@ -267,6 +440,16 @@ config I2C_IOP3XX
This driver can also be built as a module. If so, the module
will be called i2c-iop3xx.
+config I2C_ISCH
+ tristate "Intel SCH SMBus 1.0"
+ depends on PCI
+ help
+ Say Y here if you want to use SMBus controller on the Intel SCH
+ based systems.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-isch.
+
config I2C_IXP2000
tristate "IXP2000 GPIO-Based I2C Interface (DEPRECATED)"
depends on ARCH_IXP2000
@@ -305,16 +488,6 @@ config I2C_MPC
This driver can also be built as a module. If so, the module
will be called i2c-mpc.
-config I2C_NFORCE2
- tristate "Nvidia nForce2, nForce3 and nForce4"
- depends on PCI
- help
- If you say yes to this option, support will be included for the Nvidia
- nForce2, nForce3 and nForce4 families of mainboard I2C interfaces.
-
- This driver can also be built as a module. If so, the module
- will be called i2c-nforce2.
-
config I2C_OCORES
tristate "OpenCores I2C Controller"
depends on EXPERIMENTAL
@@ -336,77 +509,12 @@ config I2C_OMAP
Like OMAP1510/1610/1710/5912 and OMAP242x.
For details see http://www.ti.com/omap.
-config I2C_PARPORT
- tristate "Parallel port adapter"
- depends on PARPORT
- select I2C_ALGOBIT
- help
- This supports parallel port I2C adapters such as the ones made by
- Philips or Velleman, Analog Devices evaluation boards, and more.
- Basically any adapter using the parallel port as an I2C bus with
- no extra chipset is supported by this driver, or could be.
-
- This driver is a replacement for (and was inspired by) an older
- driver named i2c-philips-par. The new driver supports more devices,
- and makes it easier to add support for new devices.
-
- An adapter type parameter is now mandatory. Please read the file
- Documentation/i2c/busses/i2c-parport for details.
-
- Another driver exists, named i2c-parport-light, which doesn't depend
- on the parport driver. This is meant for embedded systems. Don't say
- Y here if you intend to say Y or M there.
-
- This support is also available as a module. If so, the module
- will be called i2c-parport.
-
-config I2C_PARPORT_LIGHT
- tristate "Parallel port adapter (light)"
- select I2C_ALGOBIT
- help
- This supports parallel port I2C adapters such as the ones made by
- Philips or Velleman, Analog Devices evaluation boards, and more.
- Basically any adapter using the parallel port as an I2C bus with
- no extra chipset is supported by this driver, or could be.
-
- This driver is a light version of i2c-parport. It doesn't depend
- on the parport driver, and uses direct I/O access instead. This
- might be preferred on embedded systems where wasting memory for
- the clean but heavy parport handling is not an option. The
- drawback is a reduced portability and the impossibility to
- daisy-chain other parallel port devices.
-
- Don't say Y here if you said Y or M to i2c-parport. Saying M to
- both is possible but both modules should not be loaded at the same
- time.
-
- This support is also available as a module. If so, the module
- will be called i2c-parport-light.
-
config I2C_PASEMI
tristate "PA Semi SMBus interface"
depends on PPC_PASEMI && PCI
help
Supports the PA Semi PWRficient on-chip SMBus interfaces.
-config I2C_PROSAVAGE
- tristate "S3/VIA (Pro)Savage (DEPRECATED)"
- default n
- depends on PCI
- select I2C_ALGOBIT
- help
- If you say yes to this option, support will be included for the
- I2C bus and DDC bus of the S3VIA embedded Savage4 and ProSavage8
- graphics processors.
- chipsets supported:
- S3/VIA KM266/VT8375 aka ProSavage8
- S3/VIA KM133/VT8365 aka Savage4
-
- This driver is deprecated in favor of the savagefb driver.
-
- This support is also available as a module. If so, the module
- will be called i2c-prosavage.
-
config I2C_S3C2410
tristate "S3C2410 I2C Driver"
depends on ARCH_S3C2410
@@ -414,20 +522,6 @@ config I2C_S3C2410
Say Y here to include support for I2C controller in the
Samsung S3C2410 based System-on-Chip devices.
-config I2C_SAVAGE4
- tristate "S3 Savage 4 (DEPRECATED)"
- default n
- depends on PCI
- select I2C_ALGOBIT
- help
- If you say yes to this option, support will be included for the
- S3 Savage 4 I2C interface.
-
- This driver is deprecated in favor of the savagefb driver.
-
- This driver can also be built as a module. If so, the module
- will be called i2c-savage4.
-
config I2C_SIBYTE
tristate "SiByte SMBus interface"
depends on SIBYTE_SB1xxx_SOC
@@ -489,60 +583,6 @@ config SCx200_ACB
This support is also available as a module. If so, the module
will be called scx200_acb.
-config I2C_SIS5595
- tristate "SiS 5595"
- depends on PCI
- help
- If you say yes to this option, support will be included for the
- SiS5595 SMBus (a subset of I2C) interface.
-
- This driver can also be built as a module. If so, the module
- will be called i2c-sis5595.
-
-config I2C_SIS630
- tristate "SiS 630/730"
- depends on PCI
- help
- If you say yes to this option, support will be included for the
- SiS630 and SiS730 SMBus (a subset of I2C) interface.
-
- This driver can also be built as a module. If so, the module
- will be called i2c-sis630.
-
-config I2C_SIS96X
- tristate "SiS 96x"
- depends on PCI
- help
- If you say yes to this option, support will be included for the SiS
- 96x SMBus (a subset of I2C) interfaces. Specifically, the following
- chipsets are supported:
- 645/961
- 645DX/961
- 645DX/962
- 648/961
- 650/961
- 735
- 745
-
- This driver can also be built as a module. If so, the module
- will be called i2c-sis96x.
-
-config I2C_TAOS_EVM
- tristate "TAOS evaluation module"
- depends on EXPERIMENTAL
- select SERIO
- select SERIO_SERPORT
- default n
- help
- This supports TAOS evaluation modules on serial port. In order to
- use this driver, you will need the inputattach tool, which is part
- of the input-utils package.
-
- If unsure, say N.
-
- This support is also available as a module. If so, the module
- will be called i2c-taos-evm.
-
config I2C_STUB
tristate "I2C/SMBus Test Stub"
depends on EXPERIMENTAL && m
@@ -556,17 +596,6 @@ config I2C_STUB
If you don't know what to do here, definitely say N.
-config I2C_TINY_USB
- tristate "I2C-Tiny-USB"
- depends on USB
- help
- If you say yes to this option, support will be included for the
- i2c-tiny-usb, a simple do-it-yourself USB to I2C interface. See
- http://www.harbaum.org/till/i2c_tiny_usb for hardware details.
-
- This driver can also be built as a module. If so, the module
- will be called i2c-tiny-usb.
-
config I2C_VERSATILE
tristate "ARM Versatile/Realview I2C bus support"
depends on ARCH_VERSATILE || ARCH_REALVIEW
@@ -588,47 +617,6 @@ config I2C_ACORN
If you don't know, say Y.
-config I2C_VIA
- tristate "VIA 82C586B"
- depends on PCI && EXPERIMENTAL
- select I2C_ALGOBIT
- help
- If you say yes to this option, support will be included for the VIA
- 82C586B I2C interface
-
- This driver can also be built as a module. If so, the module
- will be called i2c-via.
-
-config I2C_VIAPRO
- tristate "VIA VT82C596/82C686/82xx and CX700"
- depends on PCI
- help
- If you say yes to this option, support will be included for the VIA
- VT82C596 and later SMBus interface. Specifically, the following
- chipsets are supported:
- VT82C596A/B
- VT82C686A/B
- VT8231
- VT8233/A
- VT8235
- VT8237R/A/S
- VT8251
- CX700
-
- This driver can also be built as a module. If so, the module
- will be called i2c-viapro.
-
-config I2C_VOODOO3
- tristate "Voodoo 3"
- depends on PCI
- select I2C_ALGOBIT
- help
- If you say yes to this option, support will be included for the
- Voodoo 3 I2C interface.
-
- This driver can also be built as a module. If so, the module
- will be called i2c-voodoo3.
-
config I2C_PCA_ISA
tristate "PCA9564 on an ISA bus"
depends on ISA
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index e8c882a5ea66..bc6fc364459d 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -2,57 +2,64 @@
# Makefile for the i2c bus drivers.
#
+# PC SMBus host controller drivers
obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o
obj-$(CONFIG_I2C_ALI1563) += i2c-ali1563.o
obj-$(CONFIG_I2C_ALI15X3) += i2c-ali15x3.o
obj-$(CONFIG_I2C_AMD756) += i2c-amd756.o
obj-$(CONFIG_I2C_AMD756_S4882) += i2c-amd756-s4882.o
obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o
+obj-$(CONFIG_I2C_I801) += i2c-i801.o
+obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o
+obj-$(CONFIG_I2C_NFORCE2_S4985) += i2c-nforce2-s4985.o
+obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o
+obj-$(CONFIG_I2C_SIS5595) += i2c-sis5595.o
+obj-$(CONFIG_I2C_SIS630) += i2c-sis630.o
+obj-$(CONFIG_I2C_SIS96X) += i2c-sis96x.o
+obj-$(CONFIG_I2C_VIA) += i2c-via.o
+obj-$(CONFIG_I2C_VIAPRO) += i2c-viapro.o
+
+# Graphics adapter I2C/DDC channel drivers
+obj-$(CONFIG_I2C_VOODOO3) += i2c-voodoo3.o
+
+# External I2C/SMBus adapter drivers
+obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
+obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
+obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o
+obj-$(CONFIG_I2C_TINY_USB) += i2c-tiny-usb.o
+
+# Other I2C/SMBus bus drivers
+obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o
obj-$(CONFIG_I2C_AT91) += i2c-at91.o
obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o
obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o
+obj-$(CONFIG_I2C_CPM) += i2c-cpm.o
obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o
obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o
-obj-$(CONFIG_I2C_I801) += i2c-i801.o
-obj-$(CONFIG_I2C_I810) += i2c-i810.o
obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o
obj-$(CONFIG_I2C_IOP3XX) += i2c-iop3xx.o
+obj-$(CONFIG_I2C_ISCH) += i2c-isch.o
obj-$(CONFIG_I2C_IXP2000) += i2c-ixp2000.o
-obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o
obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
-obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o
obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o
obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
-obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
-obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
obj-$(CONFIG_I2C_PASEMI) += i2c-pasemi.o
obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
obj-$(CONFIG_I2C_PCA_PLATFORM) += i2c-pca-platform.o
-obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o
obj-$(CONFIG_I2C_PMCMSP) += i2c-pmcmsp.o
obj-$(CONFIG_I2C_PNX) += i2c-pnx.o
-obj-$(CONFIG_I2C_PROSAVAGE) += i2c-prosavage.o
+obj-$(CONFIG_I2C_POWERMAC) += i2c-powermac.o
obj-$(CONFIG_I2C_PXA) += i2c-pxa.o
obj-$(CONFIG_I2C_S3C2410) += i2c-s3c2410.o
-obj-$(CONFIG_I2C_SAVAGE4) += i2c-savage4.o
obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
obj-$(CONFIG_I2C_SIBYTE) += i2c-sibyte.o
obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
-obj-$(CONFIG_I2C_SIS5595) += i2c-sis5595.o
-obj-$(CONFIG_I2C_SIS630) += i2c-sis630.o
-obj-$(CONFIG_I2C_SIS96X) += i2c-sis96x.o
obj-$(CONFIG_I2C_STUB) += i2c-stub.o
-obj-$(CONFIG_I2C_TAOS_EVM) += i2c-taos-evm.o
-obj-$(CONFIG_I2C_TINY_USB) += i2c-tiny-usb.o
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
-obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o
-obj-$(CONFIG_I2C_VIA) += i2c-via.o
-obj-$(CONFIG_I2C_VIAPRO) += i2c-viapro.o
-obj-$(CONFIG_I2C_VOODOO3) += i2c-voodoo3.o
obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o
diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c
index f14372ac2fc5..442d559b1aa9 100644
--- a/drivers/i2c/busses/i2c-ali1535.c
+++ b/drivers/i2c/busses/i2c-ali1535.c
@@ -61,6 +61,7 @@
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/init.h>
+#include <linux/acpi.h>
#include <asm/io.h>
@@ -159,6 +160,11 @@ static int ali1535_setup(struct pci_dev *dev)
goto exit;
}
+ retval = acpi_check_region(ali1535_smba, ALI1535_SMB_IOSIZE,
+ ali1535_driver.name);
+ if (retval)
+ goto exit;
+
if (!request_region(ali1535_smba, ALI1535_SMB_IOSIZE,
ali1535_driver.name)) {
dev_err(&dev->dev, "ALI1535_smb region 0x%x already in use!\n",
@@ -259,7 +265,7 @@ static int ali1535_transaction(struct i2c_adapter *adap)
dev_err(&adap->dev,
"SMBus reset failed! (0x%02x) - controller or "
"device on bus is probably hung\n", temp);
- return -1;
+ return -EBUSY;
}
} else {
/* check and clear done bit */
@@ -281,12 +287,12 @@ static int ali1535_transaction(struct i2c_adapter *adap)
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
- result = -1;
+ result = -ETIMEDOUT;
dev_err(&adap->dev, "SMBus Timeout!\n");
}
if (temp & ALI1535_STS_FAIL) {
- result = -1;
+ result = -EIO;
dev_dbg(&adap->dev, "Error: Failed bus transaction\n");
}
@@ -295,7 +301,7 @@ static int ali1535_transaction(struct i2c_adapter *adap)
* do a printk. This means that bus collisions go unreported.
*/
if (temp & ALI1535_STS_BUSERR) {
- result = -1;
+ result = -ENXIO;
dev_dbg(&adap->dev,
"Error: no response or bus collision ADD=%02x\n",
inb_p(SMBHSTADD));
@@ -303,13 +309,13 @@ static int ali1535_transaction(struct i2c_adapter *adap)
/* haven't ever seen this */
if (temp & ALI1535_STS_DEV) {
- result = -1;
+ result = -EIO;
dev_err(&adap->dev, "Error: device error\n");
}
/* check to see if the "command complete" indication is set */
if (!(temp & ALI1535_STS_DONE)) {
- result = -1;
+ result = -ETIMEDOUT;
dev_err(&adap->dev, "Error: command never completed\n");
}
@@ -332,7 +338,7 @@ static int ali1535_transaction(struct i2c_adapter *adap)
return result;
}
-/* Return -1 on error. */
+/* Return negative errno on error. */
static s32 ali1535_access(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write, u8 command,
int size, union i2c_smbus_data *data)
@@ -357,10 +363,6 @@ static s32 ali1535_access(struct i2c_adapter *adap, u16 addr,
outb_p(0xFF, SMBHSTSTS);
switch (size) {
- case I2C_SMBUS_PROC_CALL:
- dev_err(&adap->dev, "I2C_SMBUS_PROC_CALL not supported!\n");
- result = -1;
- goto EXIT;
case I2C_SMBUS_QUICK:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
@@ -418,13 +420,15 @@ static s32 ali1535_access(struct i2c_adapter *adap, u16 addr,
outb_p(data->block[i], SMBBLKDAT);
}
break;
+ default:
+ dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
+ result = -EOPNOTSUPP;
+ goto EXIT;
}
- if (ali1535_transaction(adap)) {
- /* Error in transaction */
- result = -1;
+ result = ali1535_transaction(adap);
+ if (result)
goto EXIT;
- }
if ((read_write == I2C_SMBUS_WRITE) || (size == ALI1535_QUICK)) {
result = 0;
@@ -475,7 +479,7 @@ static const struct i2c_algorithm smbus_algorithm = {
static struct i2c_adapter ali1535_adapter = {
.owner = THIS_MODULE,
.id = I2C_HW_SMBUS_ALI1535,
- .class = I2C_CLASS_HWMON,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
.algo = &smbus_algorithm,
};
diff --git a/drivers/i2c/busses/i2c-ali1563.c b/drivers/i2c/busses/i2c-ali1563.c
index 6b68074e518a..fc3e5b026423 100644
--- a/drivers/i2c/busses/i2c-ali1563.c
+++ b/drivers/i2c/busses/i2c-ali1563.c
@@ -21,6 +21,7 @@
#include <linux/i2c.h>
#include <linux/pci.h>
#include <linux/init.h>
+#include <linux/acpi.h>
#define ALI1563_MAX_TIMEOUT 500
#define ALI1563_SMBBA 0x80
@@ -67,6 +68,7 @@ static int ali1563_transaction(struct i2c_adapter * a, int size)
{
u32 data;
int timeout;
+ int status = -EIO;
dev_dbg(&a->dev, "Transaction (pre): STS=%02x, CNTL1=%02x, "
"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
@@ -103,13 +105,15 @@ static int ali1563_transaction(struct i2c_adapter * a, int size)
/* Issue 'kill' to host controller */
outb_p(HST_CNTL2_KILL,SMB_HST_CNTL2);
data = inb_p(SMB_HST_STS);
+ status = -ETIMEDOUT;
}
/* device error - no response, ignore the autodetection case */
- if ((data & HST_STS_DEVERR) && (size != HST_CNTL2_QUICK)) {
- dev_err(&a->dev, "Device error!\n");
+ if (data & HST_STS_DEVERR) {
+ if (size != HST_CNTL2_QUICK)
+ dev_err(&a->dev, "Device error!\n");
+ status = -ENXIO;
}
-
/* bus collision */
if (data & HST_STS_BUSERR) {
dev_err(&a->dev, "Bus collision!\n");
@@ -122,13 +126,14 @@ static int ali1563_transaction(struct i2c_adapter * a, int size)
outb_p(0x0,SMB_HST_CNTL2);
}
- return -1;
+ return status;
}
static int ali1563_block_start(struct i2c_adapter * a)
{
u32 data;
int timeout;
+ int status = -EIO;
dev_dbg(&a->dev, "Block (pre): STS=%02x, CNTL1=%02x, "
"CNTL2=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, DAT1=%02x\n",
@@ -164,13 +169,20 @@ static int ali1563_block_start(struct i2c_adapter * a)
if (timeout && !(data & HST_STS_BAD))
return 0;
+
+ if (timeout == 0)
+ status = -ETIMEDOUT;
+
+ if (data & HST_STS_DEVERR)
+ status = -ENXIO;
+
dev_err(&a->dev, "SMBus Error: %s%s%s%s%s\n",
- timeout ? "Timeout " : "",
+ timeout ? "" : "Timeout ",
data & HST_STS_FAIL ? "Transaction Failed " : "",
data & HST_STS_BUSERR ? "No response or Bus Collision " : "",
data & HST_STS_DEVERR ? "Device Error " : "",
!(data & HST_STS_DONE) ? "Transaction Never Finished " : "");
- return -1;
+ return status;
}
static int ali1563_block(struct i2c_adapter * a, union i2c_smbus_data * data, u8 rw)
@@ -235,10 +247,6 @@ static s32 ali1563_access(struct i2c_adapter * a, u16 addr,
/* Map the size to what the chip understands */
switch (size) {
- case I2C_SMBUS_PROC_CALL:
- dev_err(&a->dev, "I2C_SMBUS_PROC_CALL not supported!\n");
- error = -EINVAL;
- break;
case I2C_SMBUS_QUICK:
size = HST_CNTL2_QUICK;
break;
@@ -254,6 +262,10 @@ static s32 ali1563_access(struct i2c_adapter * a, u16 addr,
case I2C_SMBUS_BLOCK_DATA:
size = HST_CNTL2_BLOCK;
break;
+ default:
+ dev_warn(&a->dev, "Unsupported transaction %d\n", size);
+ error = -EOPNOTSUPP;
+ goto Done;
}
outb_p(((addr & 0x7f) << 1) | (rw & 0x01), SMB_HST_ADD);
@@ -345,6 +357,10 @@ static int __devinit ali1563_setup(struct pci_dev * dev)
}
}
+ if (acpi_check_region(ali1563_smba, ALI1563_SMB_IOSIZE,
+ ali1563_pci_driver.name))
+ goto Err;
+
if (!request_region(ali1563_smba, ALI1563_SMB_IOSIZE,
ali1563_pci_driver.name)) {
dev_err(&dev->dev, "Could not allocate I/O space at 0x%04x\n",
@@ -371,7 +387,7 @@ static const struct i2c_algorithm ali1563_algorithm = {
static struct i2c_adapter ali1563_adapter = {
.owner = THIS_MODULE,
.id = I2C_HW_SMBUS_ALI1563,
- .class = I2C_CLASS_HWMON,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
.algo = &ali1563_algorithm,
};
diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c
index 93bf87d70961..a030abd3b32b 100644
--- a/drivers/i2c/busses/i2c-ali15x3.c
+++ b/drivers/i2c/busses/i2c-ali15x3.c
@@ -68,6 +68,7 @@
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/init.h>
+#include <linux/acpi.h>
#include <asm/io.h>
/* ALI15X3 SMBus address offsets */
@@ -166,6 +167,10 @@ static int ali15x3_setup(struct pci_dev *ALI15X3_dev)
if(force_addr)
ali15x3_smba = force_addr & ~(ALI15X3_SMB_IOSIZE - 1);
+ if (acpi_check_region(ali15x3_smba, ALI15X3_SMB_IOSIZE,
+ ali15x3_driver.name))
+ return -EBUSY;
+
if (!request_region(ali15x3_smba, ALI15X3_SMB_IOSIZE,
ali15x3_driver.name)) {
dev_err(&ALI15X3_dev->dev,
@@ -282,7 +287,7 @@ static int ali15x3_transaction(struct i2c_adapter *adap)
dev_err(&adap->dev, "SMBus reset failed! (0x%02x) - "
"controller or device on bus is probably hung\n",
temp);
- return -1;
+ return -EBUSY;
}
} else {
/* check and clear done bit */
@@ -304,12 +309,12 @@ static int ali15x3_transaction(struct i2c_adapter *adap)
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
- result = -1;
+ result = -ETIMEDOUT;
dev_err(&adap->dev, "SMBus Timeout!\n");
}
if (temp & ALI15X3_STS_TERM) {
- result = -1;
+ result = -EIO;
dev_dbg(&adap->dev, "Error: Failed bus transaction\n");
}
@@ -320,7 +325,7 @@ static int ali15x3_transaction(struct i2c_adapter *adap)
This means that bus collisions go unreported.
*/
if (temp & ALI15X3_STS_COLL) {
- result = -1;
+ result = -ENXIO;
dev_dbg(&adap->dev,
"Error: no response or bus collision ADD=%02x\n",
inb_p(SMBHSTADD));
@@ -328,7 +333,7 @@ static int ali15x3_transaction(struct i2c_adapter *adap)
/* haven't ever seen this */
if (temp & ALI15X3_STS_DEV) {
- result = -1;
+ result = -EIO;
dev_err(&adap->dev, "Error: device error\n");
}
dev_dbg(&adap->dev, "Transaction (post): STS=%02x, CNT=%02x, CMD=%02x, "
@@ -338,7 +343,7 @@ static int ali15x3_transaction(struct i2c_adapter *adap)
return result;
}
-/* Return -1 on error. */
+/* Return negative errno on error. */
static s32 ali15x3_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write, u8 command,
int size, union i2c_smbus_data * data)
@@ -362,9 +367,6 @@ static s32 ali15x3_access(struct i2c_adapter * adap, u16 addr,
}
switch (size) {
- case I2C_SMBUS_PROC_CALL:
- dev_err(&adap->dev, "I2C_SMBUS_PROC_CALL not supported!\n");
- return -1;
case I2C_SMBUS_QUICK:
outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMBHSTADD);
@@ -417,12 +419,16 @@ static s32 ali15x3_access(struct i2c_adapter * adap, u16 addr,
}
size = ALI15X3_BLOCK_DATA;
break;
+ default:
+ dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
+ return -EOPNOTSUPP;
}
outb_p(size, SMBHSTCNT); /* output command */
- if (ali15x3_transaction(adap)) /* Error in transaction */
- return -1;
+ temp = ali15x3_transaction(adap);
+ if (temp)
+ return temp;
if ((read_write == I2C_SMBUS_WRITE) || (size == ALI15X3_QUICK))
return 0;
@@ -470,7 +476,7 @@ static const struct i2c_algorithm smbus_algorithm = {
static struct i2c_adapter ali15x3_adapter = {
.owner = THIS_MODULE,
.id = I2C_HW_SMBUS_ALI15X3,
- .class = I2C_CLASS_HWMON,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
.algo = &smbus_algorithm,
};
diff --git a/drivers/i2c/busses/i2c-amd756-s4882.c b/drivers/i2c/busses/i2c-amd756-s4882.c
index c38a0a112208..2f150e33c74c 100644
--- a/drivers/i2c/busses/i2c-amd756-s4882.c
+++ b/drivers/i2c/busses/i2c-amd756-s4882.c
@@ -58,7 +58,7 @@ static s32 amd756_access_virt0(struct i2c_adapter * adap, u16 addr,
/* We exclude the multiplexed addresses */
if (addr == 0x4c || (addr & 0xfc) == 0x50 || (addr & 0xfc) == 0x30
|| addr == 0x18)
- return -1;
+ return -ENXIO;
mutex_lock(&amd756_lock);
@@ -86,7 +86,7 @@ static inline s32 amd756_access_channel(struct i2c_adapter * adap, u16 addr,
/* We exclude the non-multiplexed addresses */
if (addr != 0x4c && (addr & 0xfc) != 0x50 && (addr & 0xfc) != 0x30)
- return -1;
+ return -ENXIO;
mutex_lock(&amd756_lock);
diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c
index 43508d61eb7c..26acc657e5c9 100644
--- a/drivers/i2c/busses/i2c-amd756.c
+++ b/drivers/i2c/busses/i2c-amd756.c
@@ -45,6 +45,7 @@
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/init.h>
+#include <linux/acpi.h>
#include <asm/io.h>
/* AMD756 SMBus address offsets */
@@ -151,17 +152,17 @@ static int amd756_transaction(struct i2c_adapter *adap)
}
if (temp & GS_PRERR_STS) {
- result = -1;
+ result = -ENXIO;
dev_dbg(&adap->dev, "SMBus Protocol error (no response)!\n");
}
if (temp & GS_COL_STS) {
- result = -1;
+ result = -EIO;
dev_warn(&adap->dev, "SMBus collision!\n");
}
if (temp & GS_TO_STS) {
- result = -1;
+ result = -ETIMEDOUT;
dev_dbg(&adap->dev, "SMBus protocol timeout!\n");
}
@@ -189,22 +190,18 @@ static int amd756_transaction(struct i2c_adapter *adap)
outw_p(inw(SMB_GLOBAL_ENABLE) | GE_ABORT, SMB_GLOBAL_ENABLE);
msleep(100);
outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS);
- return -1;
+ return -EIO;
}
-/* Return -1 on error. */
+/* Return negative errno on error. */
static s32 amd756_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data)
{
int i, len;
+ int status;
- /** TODO: Should I supporte the 10-bit transfers? */
switch (size) {
- case I2C_SMBUS_PROC_CALL:
- dev_dbg(&adap->dev, "I2C_SMBUS_PROC_CALL not supported!\n");
- /* TODO: Well... It is supported, I'm just not sure what to do here... */
- return -1;
case I2C_SMBUS_QUICK:
outw_p(((addr & 0x7f) << 1) | (read_write & 0x01),
SMB_HOST_ADDRESS);
@@ -251,13 +248,17 @@ static s32 amd756_access(struct i2c_adapter * adap, u16 addr,
}
size = AMD756_BLOCK_DATA;
break;
+ default:
+ dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
+ return -EOPNOTSUPP;
}
/* How about enabling interrupts... */
outw_p(size & GE_CYC_TYPE_MASK, SMB_GLOBAL_ENABLE);
- if (amd756_transaction(adap)) /* Error in transaction */
- return -1;
+ status = amd756_transaction(adap);
+ if (status)
+ return status;
if ((read_write == I2C_SMBUS_WRITE) || (size == AMD756_QUICK))
return 0;
@@ -301,7 +302,7 @@ static const struct i2c_algorithm smbus_algorithm = {
struct i2c_adapter amd756_smbus = {
.owner = THIS_MODULE,
.id = I2C_HW_SMBUS_AMD756,
- .class = I2C_CLASS_HWMON,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
.algo = &smbus_algorithm,
};
@@ -368,6 +369,11 @@ static int __devinit amd756_probe(struct pci_dev *pdev,
amd756_ioport += SMB_ADDR_OFFSET;
}
+ error = acpi_check_region(amd756_ioport, SMB_IOSIZE,
+ amd756_driver.name);
+ if (error)
+ return error;
+
if (!request_region(amd756_ioport, SMB_IOSIZE, amd756_driver.name)) {
dev_err(&pdev->dev, "SMB region 0x%x already in use!\n",
amd756_ioport);
diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c
index 5d1a27ef2504..3972208876b3 100644
--- a/drivers/i2c/busses/i2c-amd8111.c
+++ b/drivers/i2c/busses/i2c-amd8111.c
@@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/delay.h>
+#include <linux/acpi.h>
#include <asm/io.h>
MODULE_LICENSE("GPL");
@@ -77,7 +78,7 @@ static unsigned int amd_ec_wait_write(struct amd_smbus *smbus)
if (!timeout) {
dev_warn(&smbus->dev->dev,
"Timeout while waiting for IBF to clear\n");
- return -1;
+ return -ETIMEDOUT;
}
return 0;
@@ -93,7 +94,7 @@ static unsigned int amd_ec_wait_read(struct amd_smbus *smbus)
if (!timeout) {
dev_warn(&smbus->dev->dev,
"Timeout while waiting for OBF to set\n");
- return -1;
+ return -ETIMEDOUT;
}
return 0;
@@ -102,16 +103,21 @@ static unsigned int amd_ec_wait_read(struct amd_smbus *smbus)
static unsigned int amd_ec_read(struct amd_smbus *smbus, unsigned char address,
unsigned char *data)
{
- if (amd_ec_wait_write(smbus))
- return -1;
+ int status;
+
+ status = amd_ec_wait_write(smbus);
+ if (status)
+ return status;
outb(AMD_EC_CMD_RD, smbus->base + AMD_EC_CMD);
- if (amd_ec_wait_write(smbus))
- return -1;
+ status = amd_ec_wait_write(smbus);
+ if (status)
+ return status;
outb(address, smbus->base + AMD_EC_DATA);
- if (amd_ec_wait_read(smbus))
- return -1;
+ status = amd_ec_wait_read(smbus);
+ if (status)
+ return status;
*data = inb(smbus->base + AMD_EC_DATA);
return 0;
@@ -120,16 +126,21 @@ static unsigned int amd_ec_read(struct amd_smbus *smbus, unsigned char address,
static unsigned int amd_ec_write(struct amd_smbus *smbus, unsigned char address,
unsigned char data)
{
- if (amd_ec_wait_write(smbus))
- return -1;
+ int status;
+
+ status = amd_ec_wait_write(smbus);
+ if (status)
+ return status;
outb(AMD_EC_CMD_WR, smbus->base + AMD_EC_CMD);
- if (amd_ec_wait_write(smbus))
- return -1;
+ status = amd_ec_wait_write(smbus);
+ if (status)
+ return status;
outb(address, smbus->base + AMD_EC_DATA);
- if (amd_ec_wait_write(smbus))
- return -1;
+ status = amd_ec_wait_write(smbus);
+ if (status)
+ return status;
outb(data, smbus->base + AMD_EC_DATA);
return 0;
@@ -267,12 +278,17 @@ static s32 amd8111_access(struct i2c_adapter * adap, u16 addr,
default:
dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
- return -1;
+ return -EOPNOTSUPP;
}
amd_ec_write(smbus, AMD_SMB_ADDR, addr << 1);
amd_ec_write(smbus, AMD_SMB_PRTCL, protocol);
+ /* FIXME this discards status from ec_read(); so temp[0] will
+ * hold stack garbage ... the rest of this routine will act
+ * nonsensically. Ignored ec_write() status might explain
+ * some such failures...
+ */
amd_ec_read(smbus, AMD_SMB_STS, temp + 0);
if (~temp[0] & AMD_SMB_STS_DONE) {
@@ -286,7 +302,7 @@ static s32 amd8111_access(struct i2c_adapter * adap, u16 addr,
}
if ((~temp[0] & AMD_SMB_STS_DONE) || (temp[0] & AMD_SMB_STS_STATUS))
- return -1;
+ return -EIO;
if (read_write == I2C_SMBUS_WRITE)
return 0;
@@ -359,6 +375,10 @@ static int __devinit amd8111_probe(struct pci_dev *dev,
smbus->base = pci_resource_start(dev, 0);
smbus->size = pci_resource_len(dev, 0);
+ error = acpi_check_resource_conflict(&dev->resource[0]);
+ if (error)
+ goto out_kfree;
+
if (!request_region(smbus->base, smbus->size, amd8111_driver.name)) {
error = -EBUSY;
goto out_kfree;
@@ -368,7 +388,7 @@ static int __devinit amd8111_probe(struct pci_dev *dev,
snprintf(smbus->adapter.name, sizeof(smbus->adapter.name),
"SMBus2 AMD8111 adapter at %04x", smbus->base);
smbus->adapter.id = I2C_HW_SMBUS_AMD8111;
- smbus->adapter.class = I2C_CLASS_HWMON;
+ smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
smbus->adapter.algo = &smbus_algorithm;
smbus->adapter.algo_data = smbus;
diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c
new file mode 100644
index 000000000000..652acd7b5943
--- /dev/null
+++ b/drivers/i2c/busses/i2c-cpm.c
@@ -0,0 +1,745 @@
+/*
+ * Freescale CPM1/CPM2 I2C interface.
+ * Copyright (c) 1999 Dan Malek (dmalek@jlc.net).
+ *
+ * moved into proper i2c interface;
+ * Brad Parker (brad@heeltoe.com)
+ *
+ * Parts from dbox2_i2c.c (cvs.tuxbox.org)
+ * (C) 2000-2001 Felix Domke (tmbinc@gmx.net), Gillem (htoa@gmx.net)
+ *
+ * (C) 2007 Montavista Software, Inc.
+ * Vitaly Bordug <vitb@kernel.crashing.org>
+ *
+ * Converted to of_platform_device. Renamed to i2c-cpm.c.
+ * (C) 2007,2008 Jochen Friedrich <jochen@scram.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/stddef.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_i2c.h>
+#include <sysdev/fsl_soc.h>
+#include <asm/cpm.h>
+
+/* Try to define this if you have an older CPU (earlier than rev D4) */
+/* However, better use a GPIO based bitbang driver in this case :/ */
+#undef I2C_CHIP_ERRATA
+
+#define CPM_MAX_READ 513
+#define CPM_MAXBD 4
+
+#define I2C_EB (0x10) /* Big endian mode */
+#define I2C_EB_CPM2 (0x30) /* Big endian mode, memory snoop */
+
+#define DPRAM_BASE ((u8 __iomem __force *)cpm_muram_addr(0))
+
+/* I2C parameter RAM. */
+struct i2c_ram {
+ ushort rbase; /* Rx Buffer descriptor base address */
+ ushort tbase; /* Tx Buffer descriptor base address */
+ u_char rfcr; /* Rx function code */
+ u_char tfcr; /* Tx function code */
+ ushort mrblr; /* Max receive buffer length */
+ uint rstate; /* Internal */
+ uint rdp; /* Internal */
+ ushort rbptr; /* Rx Buffer descriptor pointer */
+ ushort rbc; /* Internal */
+ uint rxtmp; /* Internal */
+ uint tstate; /* Internal */
+ uint tdp; /* Internal */
+ ushort tbptr; /* Tx Buffer descriptor pointer */
+ ushort tbc; /* Internal */
+ uint txtmp; /* Internal */
+ char res1[4]; /* Reserved */
+ ushort rpbase; /* Relocation pointer */
+ char res2[2]; /* Reserved */
+};
+
+#define I2COM_START 0x80
+#define I2COM_MASTER 0x01
+#define I2CER_TXE 0x10
+#define I2CER_BUSY 0x04
+#define I2CER_TXB 0x02
+#define I2CER_RXB 0x01
+#define I2MOD_EN 0x01
+
+/* I2C Registers */
+struct i2c_reg {
+ u8 i2mod;
+ u8 res1[3];
+ u8 i2add;
+ u8 res2[3];
+ u8 i2brg;
+ u8 res3[3];
+ u8 i2com;
+ u8 res4[3];
+ u8 i2cer;
+ u8 res5[3];
+ u8 i2cmr;
+};
+
+struct cpm_i2c {
+ char *base;
+ struct of_device *ofdev;
+ struct i2c_adapter adap;
+ uint dp_addr;
+ int version; /* CPM1=1, CPM2=2 */
+ int irq;
+ int cp_command;
+ int freq;
+ struct i2c_reg __iomem *i2c_reg;
+ struct i2c_ram __iomem *i2c_ram;
+ u16 i2c_addr;
+ wait_queue_head_t i2c_wait;
+ cbd_t __iomem *tbase;
+ cbd_t __iomem *rbase;
+ u_char *txbuf[CPM_MAXBD];
+ u_char *rxbuf[CPM_MAXBD];
+ u32 txdma[CPM_MAXBD];
+ u32 rxdma[CPM_MAXBD];
+};
+
+static irqreturn_t cpm_i2c_interrupt(int irq, void *dev_id)
+{
+ struct cpm_i2c *cpm;
+ struct i2c_reg __iomem *i2c_reg;
+ struct i2c_adapter *adap = dev_id;
+ int i;
+
+ cpm = i2c_get_adapdata(dev_id);
+ i2c_reg = cpm->i2c_reg;
+
+ /* Clear interrupt. */
+ i = in_8(&i2c_reg->i2cer);
+ out_8(&i2c_reg->i2cer, i);
+
+ dev_dbg(&adap->dev, "Interrupt: %x\n", i);
+
+ wake_up_interruptible(&cpm->i2c_wait);
+
+ return i ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static void cpm_reset_i2c_params(struct cpm_i2c *cpm)
+{
+ struct i2c_ram __iomem *i2c_ram = cpm->i2c_ram;
+
+ /* Set up the I2C parameters in the parameter ram. */
+ out_be16(&i2c_ram->tbase, (u8 __iomem *)cpm->tbase - DPRAM_BASE);
+ out_be16(&i2c_ram->rbase, (u8 __iomem *)cpm->rbase - DPRAM_BASE);
+
+ if (cpm->version == 1) {
+ out_8(&i2c_ram->tfcr, I2C_EB);
+ out_8(&i2c_ram->rfcr, I2C_EB);
+ } else {
+ out_8(&i2c_ram->tfcr, I2C_EB_CPM2);
+ out_8(&i2c_ram->rfcr, I2C_EB_CPM2);
+ }
+
+ out_be16(&i2c_ram->mrblr, CPM_MAX_READ);
+
+ out_be32(&i2c_ram->rstate, 0);
+ out_be32(&i2c_ram->rdp, 0);
+ out_be16(&i2c_ram->rbptr, 0);
+ out_be16(&i2c_ram->rbc, 0);
+ out_be32(&i2c_ram->rxtmp, 0);
+ out_be32(&i2c_ram->tstate, 0);
+ out_be32(&i2c_ram->tdp, 0);
+ out_be16(&i2c_ram->tbptr, 0);
+ out_be16(&i2c_ram->tbc, 0);
+ out_be32(&i2c_ram->txtmp, 0);
+}
+
+static void cpm_i2c_force_close(struct i2c_adapter *adap)
+{
+ struct cpm_i2c *cpm = i2c_get_adapdata(adap);
+ struct i2c_reg __iomem *i2c_reg = cpm->i2c_reg;
+
+ dev_dbg(&adap->dev, "cpm_i2c_force_close()\n");
+
+ cpm_command(cpm->cp_command, CPM_CR_CLOSE_RX_BD);
+
+ out_8(&i2c_reg->i2cmr, 0x00); /* Disable all interrupts */
+ out_8(&i2c_reg->i2cer, 0xff);
+}
+
+static void cpm_i2c_parse_message(struct i2c_adapter *adap,
+ struct i2c_msg *pmsg, int num, int tx, int rx)
+{
+ cbd_t __iomem *tbdf;
+ cbd_t __iomem *rbdf;
+ u_char addr;
+ u_char *tb;
+ u_char *rb;
+ struct cpm_i2c *cpm = i2c_get_adapdata(adap);
+
+ tbdf = cpm->tbase + tx;
+ rbdf = cpm->rbase + rx;
+
+ addr = pmsg->addr << 1;
+ if (pmsg->flags & I2C_M_RD)
+ addr |= 1;
+
+ tb = cpm->txbuf[tx];
+ rb = cpm->rxbuf[rx];
+
+ /* Align read buffer */
+ rb = (u_char *) (((ulong) rb + 1) & ~1);
+
+ tb[0] = addr; /* Device address byte w/rw flag */
+
+ out_be16(&tbdf->cbd_datlen, pmsg->len + 1);
+ out_be16(&tbdf->cbd_sc, 0);
+
+ if (!(pmsg->flags & I2C_M_NOSTART))
+ setbits16(&tbdf->cbd_sc, BD_I2C_START);
+
+ if (tx + 1 == num)
+ setbits16(&tbdf->cbd_sc, BD_SC_LAST | BD_SC_WRAP);
+
+ if (pmsg->flags & I2C_M_RD) {
+ /*
+ * To read, we need an empty buffer of the proper length.
+ * All that is used is the first byte for address, the remainder
+ * is just used for timing (and doesn't really have to exist).
+ */
+
+ dev_dbg(&adap->dev, "cpm_i2c_read(abyte=0x%x)\n", addr);
+
+ out_be16(&rbdf->cbd_datlen, 0);
+ out_be16(&rbdf->cbd_sc, BD_SC_EMPTY | BD_SC_INTRPT);
+
+ if (rx + 1 == CPM_MAXBD)
+ setbits16(&rbdf->cbd_sc, BD_SC_WRAP);
+
+ eieio();
+ setbits16(&tbdf->cbd_sc, BD_SC_READY);
+ } else {
+ dev_dbg(&adap->dev, "cpm_iic_write(abyte=0x%x)\n", addr);
+
+ memcpy(tb+1, pmsg->buf, pmsg->len);
+
+ eieio();
+ setbits16(&tbdf->cbd_sc, BD_SC_READY | BD_SC_INTRPT);
+ }
+}
+
+static int cpm_i2c_check_message(struct i2c_adapter *adap,
+ struct i2c_msg *pmsg, int tx, int rx)
+{
+ cbd_t __iomem *tbdf;
+ cbd_t __iomem *rbdf;
+ u_char *tb;
+ u_char *rb;
+ struct cpm_i2c *cpm = i2c_get_adapdata(adap);
+
+ tbdf = cpm->tbase + tx;
+ rbdf = cpm->rbase + rx;
+
+ tb = cpm->txbuf[tx];
+ rb = cpm->rxbuf[rx];
+
+ /* Align read buffer */
+ rb = (u_char *) (((uint) rb + 1) & ~1);
+
+ eieio();
+ if (pmsg->flags & I2C_M_RD) {
+ dev_dbg(&adap->dev, "tx sc 0x%04x, rx sc 0x%04x\n",
+ in_be16(&tbdf->cbd_sc), in_be16(&rbdf->cbd_sc));
+
+ if (in_be16(&tbdf->cbd_sc) & BD_SC_NAK) {
+ dev_dbg(&adap->dev, "I2C read; No ack\n");
+ return -ENXIO;
+ }
+ if (in_be16(&rbdf->cbd_sc) & BD_SC_EMPTY) {
+ dev_err(&adap->dev,
+ "I2C read; complete but rbuf empty\n");
+ return -EREMOTEIO;
+ }
+ if (in_be16(&rbdf->cbd_sc) & BD_SC_OV) {
+ dev_err(&adap->dev, "I2C read; Overrun\n");
+ return -EREMOTEIO;
+ }
+ memcpy(pmsg->buf, rb, pmsg->len);
+ } else {
+ dev_dbg(&adap->dev, "tx sc %d 0x%04x\n", tx,
+ in_be16(&tbdf->cbd_sc));
+
+ if (in_be16(&tbdf->cbd_sc) & BD_SC_NAK) {
+ dev_dbg(&adap->dev, "I2C write; No ack\n");
+ return -ENXIO;
+ }
+ if (in_be16(&tbdf->cbd_sc) & BD_SC_UN) {
+ dev_err(&adap->dev, "I2C write; Underrun\n");
+ return -EIO;
+ }
+ if (in_be16(&tbdf->cbd_sc) & BD_SC_CL) {
+ dev_err(&adap->dev, "I2C write; Collision\n");
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+static int cpm_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
+{
+ struct cpm_i2c *cpm = i2c_get_adapdata(adap);
+ struct i2c_reg __iomem *i2c_reg = cpm->i2c_reg;
+ struct i2c_ram __iomem *i2c_ram = cpm->i2c_ram;
+ struct i2c_msg *pmsg;
+ int ret, i;
+ int tptr;
+ int rptr;
+ cbd_t __iomem *tbdf;
+ cbd_t __iomem *rbdf;
+
+ if (num > CPM_MAXBD)
+ return -EINVAL;
+
+ /* Check if we have any oversized READ requests */
+ for (i = 0; i < num; i++) {
+ pmsg = &msgs[i];
+ if (pmsg->len >= CPM_MAX_READ)
+ return -EINVAL;
+ }
+
+ /* Reset to use first buffer */
+ out_be16(&i2c_ram->rbptr, in_be16(&i2c_ram->rbase));
+ out_be16(&i2c_ram->tbptr, in_be16(&i2c_ram->tbase));
+
+ tbdf = cpm->tbase;
+ rbdf = cpm->rbase;
+
+ tptr = 0;
+ rptr = 0;
+
+ while (tptr < num) {
+ pmsg = &msgs[tptr];
+ dev_dbg(&adap->dev, "R: %d T: %d\n", rptr, tptr);
+
+ cpm_i2c_parse_message(adap, pmsg, num, tptr, rptr);
+ if (pmsg->flags & I2C_M_RD)
+ rptr++;
+ tptr++;
+ }
+ /* Start transfer now */
+ /* Enable RX/TX/Error interupts */
+ out_8(&i2c_reg->i2cmr, I2CER_BUSY | I2CER_TXB | I2CER_RXB);
+ out_8(&i2c_reg->i2cer, 0xff); /* Clear interrupt status */
+ /* Chip bug, set enable here */
+ setbits8(&i2c_reg->i2mod, I2MOD_EN); /* Enable */
+ /* Begin transmission */
+ setbits8(&i2c_reg->i2com, I2COM_START);
+
+ tptr = 0;
+ rptr = 0;
+
+ while (tptr < num) {
+ /* Check for outstanding messages */
+ dev_dbg(&adap->dev, "test ready.\n");
+ pmsg = &msgs[tptr];
+ if (pmsg->flags & I2C_M_RD)
+ ret = wait_event_interruptible_timeout(cpm->i2c_wait,
+ !(in_be16(&rbdf[rptr].cbd_sc) & BD_SC_EMPTY),
+ 1 * HZ);
+ else
+ ret = wait_event_interruptible_timeout(cpm->i2c_wait,
+ !(in_be16(&tbdf[tptr].cbd_sc) & BD_SC_READY),
+ 1 * HZ);
+ if (ret == 0) {
+ ret = -EREMOTEIO;
+ dev_err(&adap->dev, "I2C transfer: timeout\n");
+ goto out_err;
+ }
+ if (ret > 0) {
+ dev_dbg(&adap->dev, "ready.\n");
+ ret = cpm_i2c_check_message(adap, pmsg, tptr, rptr);
+ tptr++;
+ if (pmsg->flags & I2C_M_RD)
+ rptr++;
+ if (ret)
+ goto out_err;
+ }
+ }
+#ifdef I2C_CHIP_ERRATA
+ /*
+ * Chip errata, clear enable. This is not needed on rev D4 CPUs.
+ * Disabling I2C too early may cause too short stop condition
+ */
+ udelay(4);
+ clrbits8(&i2c_reg->i2mod, I2MOD_EN);
+#endif
+ return (num);
+
+out_err:
+ cpm_i2c_force_close(adap);
+#ifdef I2C_CHIP_ERRATA
+ /*
+ * Chip errata, clear enable. This is not needed on rev D4 CPUs.
+ */
+ clrbits8(&i2c_reg->i2mod, I2MOD_EN);
+#endif
+ return ret;
+}
+
+static u32 cpm_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+/* -----exported algorithm data: ------------------------------------- */
+
+static const struct i2c_algorithm cpm_i2c_algo = {
+ .master_xfer = cpm_i2c_xfer,
+ .functionality = cpm_i2c_func,
+};
+
+static const struct i2c_adapter cpm_ops = {
+ .owner = THIS_MODULE,
+ .name = "i2c-cpm",
+ .algo = &cpm_i2c_algo,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+};
+
+static int __devinit cpm_i2c_setup(struct cpm_i2c *cpm)
+{
+ struct of_device *ofdev = cpm->ofdev;
+ const u32 *data;
+ int len, ret, i;
+ void __iomem *i2c_base;
+ cbd_t __iomem *tbdf;
+ cbd_t __iomem *rbdf;
+ unsigned char brg;
+
+ dev_dbg(&cpm->ofdev->dev, "cpm_i2c_setup()\n");
+
+ init_waitqueue_head(&cpm->i2c_wait);
+
+ cpm->irq = of_irq_to_resource(ofdev->node, 0, NULL);
+ if (cpm->irq == NO_IRQ)
+ return -EINVAL;
+
+ /* Install interrupt handler. */
+ ret = request_irq(cpm->irq, cpm_i2c_interrupt, 0, "cpm_i2c",
+ &cpm->adap);
+ if (ret)
+ return ret;
+
+ /* I2C parameter RAM */
+ i2c_base = of_iomap(ofdev->node, 1);
+ if (i2c_base == NULL) {
+ ret = -EINVAL;
+ goto out_irq;
+ }
+
+ if (of_device_is_compatible(ofdev->node, "fsl,cpm1-i2c")) {
+
+ /* Check for and use a microcode relocation patch. */
+ cpm->i2c_ram = i2c_base;
+ cpm->i2c_addr = in_be16(&cpm->i2c_ram->rpbase);
+
+ /*
+ * Maybe should use cpm_muram_alloc instead of hardcoding
+ * this in micropatch.c
+ */
+ if (cpm->i2c_addr) {
+ cpm->i2c_ram = cpm_muram_addr(cpm->i2c_addr);
+ iounmap(i2c_base);
+ }
+
+ cpm->version = 1;
+
+ } else if (of_device_is_compatible(ofdev->node, "fsl,cpm2-i2c")) {
+ cpm->i2c_addr = cpm_muram_alloc(sizeof(struct i2c_ram), 64);
+ cpm->i2c_ram = cpm_muram_addr(cpm->i2c_addr);
+ out_be16(i2c_base, cpm->i2c_addr);
+ iounmap(i2c_base);
+
+ cpm->version = 2;
+
+ } else {
+ iounmap(i2c_base);
+ ret = -EINVAL;
+ goto out_irq;
+ }
+
+ /* I2C control/status registers */
+ cpm->i2c_reg = of_iomap(ofdev->node, 0);
+ if (cpm->i2c_reg == NULL) {
+ ret = -EINVAL;
+ goto out_ram;
+ }
+
+ data = of_get_property(ofdev->node, "fsl,cpm-command", &len);
+ if (!data || len != 4) {
+ ret = -EINVAL;
+ goto out_reg;
+ }
+ cpm->cp_command = *data;
+
+ data = of_get_property(ofdev->node, "linux,i2c-class", &len);
+ if (data && len == 4)
+ cpm->adap.class = *data;
+
+ data = of_get_property(ofdev->node, "clock-frequency", &len);
+ if (data && len == 4)
+ cpm->freq = *data;
+ else
+ cpm->freq = 60000; /* use 60kHz i2c clock by default */
+
+ /*
+ * Allocate space for CPM_MAXBD transmit and receive buffer
+ * descriptors in the DP ram.
+ */
+ cpm->dp_addr = cpm_muram_alloc(sizeof(cbd_t) * 2 * CPM_MAXBD, 8);
+ if (!cpm->dp_addr) {
+ ret = -ENOMEM;
+ goto out_reg;
+ }
+
+ cpm->tbase = cpm_muram_addr(cpm->dp_addr);
+ cpm->rbase = cpm_muram_addr(cpm->dp_addr + sizeof(cbd_t) * CPM_MAXBD);
+
+ /* Allocate TX and RX buffers */
+
+ tbdf = cpm->tbase;
+ rbdf = cpm->rbase;
+
+ for (i = 0; i < CPM_MAXBD; i++) {
+ cpm->rxbuf[i] = dma_alloc_coherent(
+ NULL, CPM_MAX_READ + 1, &cpm->rxdma[i], GFP_KERNEL);
+ if (!cpm->rxbuf[i]) {
+ ret = -ENOMEM;
+ goto out_muram;
+ }
+ out_be32(&rbdf[i].cbd_bufaddr, ((cpm->rxdma[i] + 1) & ~1));
+
+ cpm->txbuf[i] = (unsigned char *)dma_alloc_coherent(
+ NULL, CPM_MAX_READ + 1, &cpm->txdma[i], GFP_KERNEL);
+ if (!cpm->txbuf[i]) {
+ ret = -ENOMEM;
+ goto out_muram;
+ }
+ out_be32(&tbdf[i].cbd_bufaddr, cpm->txdma[i]);
+ }
+
+ /* Initialize Tx/Rx parameters. */
+
+ cpm_reset_i2c_params(cpm);
+
+ dev_dbg(&cpm->ofdev->dev, "i2c_ram %p, i2c_addr 0x%04x\n",
+ cpm->i2c_ram, cpm->i2c_addr);
+ dev_dbg(&cpm->ofdev->dev, "tbase 0x%04x, rbase 0x%04x\n",
+ (u8 __iomem *)cpm->tbase - DPRAM_BASE,
+ (u8 __iomem *)cpm->rbase - DPRAM_BASE);
+
+ cpm_command(cpm->cp_command, CPM_CR_INIT_TRX);
+
+ /*
+ * Select an invalid address. Just make sure we don't use loopback mode
+ */
+ out_8(&cpm->i2c_reg->i2add, 0x7f << 1);
+
+ /*
+ * PDIV is set to 00 in i2mod, so brgclk/32 is used as input to the
+ * i2c baud rate generator. This is divided by 2 x (DIV + 3) to get
+ * the actual i2c bus frequency.
+ */
+ brg = get_brgfreq() / (32 * 2 * cpm->freq) - 3;
+ out_8(&cpm->i2c_reg->i2brg, brg);
+
+ out_8(&cpm->i2c_reg->i2mod, 0x00);
+ out_8(&cpm->i2c_reg->i2com, I2COM_MASTER); /* Master mode */
+
+ /* Disable interrupts. */
+ out_8(&cpm->i2c_reg->i2cmr, 0);
+ out_8(&cpm->i2c_reg->i2cer, 0xff);
+
+ return 0;
+
+out_muram:
+ for (i = 0; i < CPM_MAXBD; i++) {
+ if (cpm->rxbuf[i])
+ dma_free_coherent(NULL, CPM_MAX_READ + 1,
+ cpm->rxbuf[i], cpm->rxdma[i]);
+ if (cpm->txbuf[i])
+ dma_free_coherent(NULL, CPM_MAX_READ + 1,
+ cpm->txbuf[i], cpm->txdma[i]);
+ }
+ cpm_muram_free(cpm->dp_addr);
+out_reg:
+ iounmap(cpm->i2c_reg);
+out_ram:
+ if ((cpm->version == 1) && (!cpm->i2c_addr))
+ iounmap(cpm->i2c_ram);
+ if (cpm->version == 2)
+ cpm_muram_free(cpm->i2c_addr);
+out_irq:
+ free_irq(cpm->irq, &cpm->adap);
+ return ret;
+}
+
+static void cpm_i2c_shutdown(struct cpm_i2c *cpm)
+{
+ int i;
+
+ /* Shut down I2C. */
+ clrbits8(&cpm->i2c_reg->i2mod, I2MOD_EN);
+
+ /* Disable interrupts */
+ out_8(&cpm->i2c_reg->i2cmr, 0);
+ out_8(&cpm->i2c_reg->i2cer, 0xff);
+
+ free_irq(cpm->irq, &cpm->adap);
+
+ /* Free all memory */
+ for (i = 0; i < CPM_MAXBD; i++) {
+ dma_free_coherent(NULL, CPM_MAX_READ + 1,
+ cpm->rxbuf[i], cpm->rxdma[i]);
+ dma_free_coherent(NULL, CPM_MAX_READ + 1,
+ cpm->txbuf[i], cpm->txdma[i]);
+ }
+
+ cpm_muram_free(cpm->dp_addr);
+ iounmap(cpm->i2c_reg);
+
+ if ((cpm->version == 1) && (!cpm->i2c_addr))
+ iounmap(cpm->i2c_ram);
+ if (cpm->version == 2)
+ cpm_muram_free(cpm->i2c_addr);
+}
+
+static int __devinit cpm_i2c_probe(struct of_device *ofdev,
+ const struct of_device_id *match)
+{
+ int result, len;
+ struct cpm_i2c *cpm;
+ const u32 *data;
+
+ cpm = kzalloc(sizeof(struct cpm_i2c), GFP_KERNEL);
+ if (!cpm)
+ return -ENOMEM;
+
+ cpm->ofdev = ofdev;
+
+ dev_set_drvdata(&ofdev->dev, cpm);
+
+ cpm->adap = cpm_ops;
+ i2c_set_adapdata(&cpm->adap, cpm);
+ cpm->adap.dev.parent = &ofdev->dev;
+
+ result = cpm_i2c_setup(cpm);
+ if (result) {
+ dev_err(&ofdev->dev, "Unable to init hardware\n");
+ goto out_free;
+ }
+
+ /* register new adapter to i2c module... */
+
+ data = of_get_property(ofdev->node, "linux,i2c-index", &len);
+ if (data && len == 4) {
+ cpm->adap.nr = *data;
+ result = i2c_add_numbered_adapter(&cpm->adap);
+ } else
+ result = i2c_add_adapter(&cpm->adap);
+
+ if (result < 0) {
+ dev_err(&ofdev->dev, "Unable to register with I2C\n");
+ goto out_shut;
+ }
+
+ dev_dbg(&ofdev->dev, "hw routines for %s registered.\n",
+ cpm->adap.name);
+
+ /*
+ * register OF I2C devices
+ */
+ of_register_i2c_devices(&cpm->adap, ofdev->node);
+
+ return 0;
+out_shut:
+ cpm_i2c_shutdown(cpm);
+out_free:
+ dev_set_drvdata(&ofdev->dev, NULL);
+ kfree(cpm);
+
+ return result;
+}
+
+static int __devexit cpm_i2c_remove(struct of_device *ofdev)
+{
+ struct cpm_i2c *cpm = dev_get_drvdata(&ofdev->dev);
+
+ i2c_del_adapter(&cpm->adap);
+
+ cpm_i2c_shutdown(cpm);
+
+ dev_set_drvdata(&ofdev->dev, NULL);
+ kfree(cpm);
+
+ return 0;
+}
+
+static const struct of_device_id cpm_i2c_match[] = {
+ {
+ .compatible = "fsl,cpm1-i2c",
+ },
+ {
+ .compatible = "fsl,cpm2-i2c",
+ },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, cpm_i2c_match);
+
+static struct of_platform_driver cpm_i2c_driver = {
+ .match_table = cpm_i2c_match,
+ .probe = cpm_i2c_probe,
+ .remove = __devexit_p(cpm_i2c_remove),
+ .driver = {
+ .name = "fsl-i2c-cpm",
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init cpm_i2c_init(void)
+{
+ return of_register_platform_driver(&cpm_i2c_driver);
+}
+
+static void __exit cpm_i2c_exit(void)
+{
+ of_unregister_platform_driver(&cpm_i2c_driver);
+}
+
+module_init(cpm_i2c_init);
+module_exit(cpm_i2c_exit);
+
+MODULE_AUTHOR("Jochen Friedrich <jochen@scram.de>");
+MODULE_DESCRIPTION("I2C-Bus adapter routines for CPM boards");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c
index 7ecbfc429b19..af3846eda985 100644
--- a/drivers/i2c/busses/i2c-davinci.c
+++ b/drivers/i2c/busses/i2c-davinci.c
@@ -85,6 +85,7 @@
#define DAVINCI_I2C_MDR_MST (1 << 10)
#define DAVINCI_I2C_MDR_TRX (1 << 9)
#define DAVINCI_I2C_MDR_XA (1 << 8)
+#define DAVINCI_I2C_MDR_RM (1 << 7)
#define DAVINCI_I2C_MDR_IRS (1 << 5)
#define DAVINCI_I2C_IMR_AAS (1 << 6)
@@ -112,6 +113,7 @@ struct davinci_i2c_dev {
u8 *buf;
size_t buf_len;
int irq;
+ u8 terminate;
struct i2c_adapter adapter;
};
@@ -142,6 +144,7 @@ static int i2c_davinci_init(struct davinci_i2c_dev *dev)
struct davinci_i2c_platform_data *pdata = dev->dev->platform_data;
u16 psc;
u32 clk;
+ u32 d;
u32 clkh;
u32 clkl;
u32 input_clock = clk_get_rate(dev->clk);
@@ -171,23 +174,29 @@ static int i2c_davinci_init(struct davinci_i2c_dev *dev)
* if PSC > 1 , d = 5
*/
- psc = 26; /* To get 1MHz clock */
+ /* get minimum of 7 MHz clock, but max of 12 MHz */
+ psc = (input_clock / 7000000) - 1;
+ if ((input_clock / (psc + 1)) > 12000000)
+ psc++; /* better to run under spec than over */
+ d = (psc >= 2) ? 5 : 7 - psc;
- clk = ((input_clock / (psc + 1)) / (pdata->bus_freq * 1000)) - 10;
- clkh = (50 * clk) / 100;
+ clk = ((input_clock / (psc + 1)) / (pdata->bus_freq * 1000)) - (d << 1);
+ clkh = clk >> 1;
clkl = clk - clkh;
davinci_i2c_write_reg(dev, DAVINCI_I2C_PSC_REG, psc);
davinci_i2c_write_reg(dev, DAVINCI_I2C_CLKH_REG, clkh);
davinci_i2c_write_reg(dev, DAVINCI_I2C_CLKL_REG, clkl);
- dev_dbg(dev->dev, "CLK = %d\n", clk);
+ dev_dbg(dev->dev, "input_clock = %d, CLK = %d\n", input_clock, clk);
dev_dbg(dev->dev, "PSC = %d\n",
davinci_i2c_read_reg(dev, DAVINCI_I2C_PSC_REG));
dev_dbg(dev->dev, "CLKL = %d\n",
davinci_i2c_read_reg(dev, DAVINCI_I2C_CLKL_REG));
dev_dbg(dev->dev, "CLKH = %d\n",
davinci_i2c_read_reg(dev, DAVINCI_I2C_CLKH_REG));
+ dev_dbg(dev->dev, "bus_freq = %dkHz, bus_delay = %d\n",
+ pdata->bus_freq, pdata->bus_delay);
/* Take the I2C module out of reset: */
w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG);
@@ -233,7 +242,6 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
struct davinci_i2c_dev *dev = i2c_get_adapdata(adap);
struct davinci_i2c_platform_data *pdata = dev->dev->platform_data;
u32 flag;
- u32 stat;
u16 w;
int r;
@@ -254,12 +262,9 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
davinci_i2c_write_reg(dev, DAVINCI_I2C_CNT_REG, dev->buf_len);
- init_completion(&dev->cmd_complete);
+ INIT_COMPLETION(dev->cmd_complete);
dev->cmd_err = 0;
- /* Clear any pending interrupts by reading the IVR */
- stat = davinci_i2c_read_reg(dev, DAVINCI_I2C_IVR_REG);
-
/* Take I2C out of reset, configure it as master and set the
* start bit */
flag = DAVINCI_I2C_MDR_IRS | DAVINCI_I2C_MDR_MST | DAVINCI_I2C_MDR_STT;
@@ -280,20 +285,34 @@ i2c_davinci_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg, int stop)
MOD_REG_BIT(w, DAVINCI_I2C_IMR_XRDY, 1);
davinci_i2c_write_reg(dev, DAVINCI_I2C_IMR_REG, w);
+ dev->terminate = 0;
/* write the data into mode register */
davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, flag);
r = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
DAVINCI_I2C_TIMEOUT);
- dev->buf_len = 0;
- if (r < 0)
- return r;
-
if (r == 0) {
dev_err(dev->dev, "controller timed out\n");
i2c_davinci_init(dev);
+ dev->buf_len = 0;
return -ETIMEDOUT;
}
+ if (dev->buf_len) {
+ /* This should be 0 if all bytes were transferred
+ * or dev->cmd_err denotes an error.
+ * A signal may have aborted the transfer.
+ */
+ if (r >= 0) {
+ dev_err(dev->dev, "abnormal termination buf_len=%i\n",
+ dev->buf_len);
+ r = -EREMOTEIO;
+ }
+ dev->terminate = 1;
+ wmb();
+ dev->buf_len = 0;
+ }
+ if (r < 0)
+ return r;
/* no error */
if (likely(!dev->cmd_err))
@@ -338,12 +357,11 @@ i2c_davinci_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
for (i = 0; i < num; i++) {
ret = i2c_davinci_xfer_msg(adap, &msgs[i], (i == (num - 1)));
+ dev_dbg(dev->dev, "%s [%d/%d] ret: %d\n", __func__, i + 1, num,
+ ret);
if (ret < 0)
return ret;
}
-
- dev_dbg(dev->dev, "%s:%d ret: %d\n", __func__, __LINE__, ret);
-
return num;
}
@@ -352,6 +370,27 @@ static u32 i2c_davinci_func(struct i2c_adapter *adap)
return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
}
+static void terminate_read(struct davinci_i2c_dev *dev)
+{
+ u16 w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG);
+ w |= DAVINCI_I2C_MDR_NACK;
+ davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w);
+
+ /* Throw away data */
+ davinci_i2c_read_reg(dev, DAVINCI_I2C_DRR_REG);
+ if (!dev->terminate)
+ dev_err(dev->dev, "RDR IRQ while no data requested\n");
+}
+static void terminate_write(struct davinci_i2c_dev *dev)
+{
+ u16 w = davinci_i2c_read_reg(dev, DAVINCI_I2C_MDR_REG);
+ w |= DAVINCI_I2C_MDR_RM | DAVINCI_I2C_MDR_STP;
+ davinci_i2c_write_reg(dev, DAVINCI_I2C_MDR_REG, w);
+
+ if (!dev->terminate)
+ dev_err(dev->dev, "TDR IRQ while no data to send\n");
+}
+
/*
* Interrupt service routine. This gets called whenever an I2C interrupt
* occurs.
@@ -372,12 +411,15 @@ static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id)
switch (stat) {
case DAVINCI_I2C_IVR_AL:
+ /* Arbitration lost, must retry */
dev->cmd_err |= DAVINCI_I2C_STR_AL;
+ dev->buf_len = 0;
complete(&dev->cmd_complete);
break;
case DAVINCI_I2C_IVR_NACK:
dev->cmd_err |= DAVINCI_I2C_STR_NACK;
+ dev->buf_len = 0;
complete(&dev->cmd_complete);
break;
@@ -399,9 +441,10 @@ static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id)
davinci_i2c_write_reg(dev,
DAVINCI_I2C_STR_REG,
DAVINCI_I2C_IMR_RRDY);
- } else
- dev_err(dev->dev, "RDR IRQ while no "
- "data requested\n");
+ } else {
+ /* signal can terminate transfer */
+ terminate_read(dev);
+ }
break;
case DAVINCI_I2C_IVR_XRDY:
@@ -418,9 +461,10 @@ static irqreturn_t i2c_davinci_isr(int this_irq, void *dev_id)
davinci_i2c_write_reg(dev,
DAVINCI_I2C_IMR_REG,
w);
- } else
- dev_err(dev->dev, "TDR IRQ while no data to "
- "send\n");
+ } else {
+ /* signal can terminate transfer */
+ terminate_write(dev);
+ }
break;
case DAVINCI_I2C_IVR_SCD:
@@ -475,6 +519,7 @@ static int davinci_i2c_probe(struct platform_device *pdev)
goto err_release_region;
}
+ init_completion(&dev->cmd_complete);
dev->dev = get_device(&pdev->dev);
dev->irq = irq->start;
platform_set_drvdata(pdev, dev);
diff --git a/drivers/i2c/busses/i2c-elektor.c b/drivers/i2c/busses/i2c-elektor.c
index b7a9977b025f..c251cf21a62b 100644
--- a/drivers/i2c/busses/i2c-elektor.c
+++ b/drivers/i2c/busses/i2c-elektor.c
@@ -202,7 +202,7 @@ static struct i2c_algo_pcf_data pcf_isa_data = {
static struct i2c_adapter pcf_isa_ops = {
.owner = THIS_MODULE,
- .class = I2C_CLASS_HWMON,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
.id = I2C_HW_P_ELEK,
.algo_data = &pcf_isa_data,
.name = "i2c-elektor",
diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c
index 7c1b762aa681..79b455a1f090 100644
--- a/drivers/i2c/busses/i2c-gpio.c
+++ b/drivers/i2c/busses/i2c-gpio.c
@@ -140,7 +140,7 @@ static int __init i2c_gpio_probe(struct platform_device *pdev)
adap->owner = THIS_MODULE;
snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id);
adap->algo_data = bit_data;
- adap->class = I2C_CLASS_HWMON;
+ adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
adap->dev.parent = &pdev->dev;
/*
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index b0f771fe4326..338834fde436 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -64,6 +64,7 @@
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/i2c.h>
+#include <linux/acpi.h>
#include <asm/io.h>
/* I801 SMBus address offsets */
@@ -151,7 +152,7 @@ static int i801_transaction(int xact)
outb_p(temp, SMBHSTSTS);
if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) {
dev_dbg(&I801_dev->dev, "Failed! (%02x)\n", temp);
- return -1;
+ return -EBUSY;
} else {
dev_dbg(&I801_dev->dev, "Successful!\n");
}
@@ -170,7 +171,7 @@ static int i801_transaction(int xact)
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
- result = -1;
+ result = -ETIMEDOUT;
/* try to stop the current command */
dev_dbg(&I801_dev->dev, "Terminating the current operation\n");
outb_p(inb_p(SMBHSTCNT) | SMBHSTCNT_KILL, SMBHSTCNT);
@@ -179,19 +180,19 @@ static int i801_transaction(int xact)
}
if (temp & SMBHSTSTS_FAILED) {
- result = -1;
+ result = -EIO;
dev_dbg(&I801_dev->dev, "Error: Failed bus transaction\n");
}
if (temp & SMBHSTSTS_BUS_ERR) {
- result = -1;
+ result = -EIO;
dev_err(&I801_dev->dev, "Bus collision! SMBus may be locked "
"until next hard reset. (sorry!)\n");
/* Clock stops and slave is stuck in mid-transmission */
}
if (temp & SMBHSTSTS_DEV_ERR) {
- result = -1;
+ result = -ENXIO;
dev_dbg(&I801_dev->dev, "Error: no response!\n");
}
@@ -231,6 +232,7 @@ static int i801_block_transaction_by_block(union i2c_smbus_data *data,
char read_write, int hwpec)
{
int i, len;
+ int status;
inb_p(SMBHSTCNT); /* reset the data buffer index */
@@ -242,14 +244,15 @@ static int i801_block_transaction_by_block(union i2c_smbus_data *data,
outb_p(data->block[i+1], SMBBLKDAT);
}
- if (i801_transaction(I801_BLOCK_DATA | ENABLE_INT9 |
- I801_PEC_EN * hwpec))
- return -1;
+ status = i801_transaction(I801_BLOCK_DATA | ENABLE_INT9 |
+ I801_PEC_EN * hwpec);
+ if (status)
+ return status;
if (read_write == I2C_SMBUS_READ) {
len = inb_p(SMBHSTDAT0);
if (len < 1 || len > I2C_SMBUS_BLOCK_MAX)
- return -1;
+ return -EPROTO;
data->block[0] = len;
for (i = 0; i < len; i++)
@@ -314,11 +317,11 @@ static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) {
dev_err(&I801_dev->dev,
"Reset failed! (%02x)\n", temp);
- return -1;
+ return -EBUSY;
}
if (i != 1)
/* if die in middle of block transaction, fail */
- return -1;
+ return -EIO;
}
if (i == 1)
@@ -342,19 +345,19 @@ static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
msleep(1);
outb_p(inb_p(SMBHSTCNT) & (~SMBHSTCNT_KILL),
SMBHSTCNT);
- result = -1;
+ result = -ETIMEDOUT;
dev_dbg(&I801_dev->dev, "SMBus Timeout!\n");
}
if (temp & SMBHSTSTS_FAILED) {
- result = -1;
+ result = -EIO;
dev_dbg(&I801_dev->dev,
"Error: Failed bus transaction\n");
} else if (temp & SMBHSTSTS_BUS_ERR) {
- result = -1;
+ result = -EIO;
dev_err(&I801_dev->dev, "Bus collision!\n");
} else if (temp & SMBHSTSTS_DEV_ERR) {
- result = -1;
+ result = -ENXIO;
dev_dbg(&I801_dev->dev, "Error: no response!\n");
}
@@ -362,7 +365,7 @@ static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
&& command != I2C_SMBUS_I2C_BLOCK_DATA) {
len = inb_p(SMBHSTDAT0);
if (len < 1 || len > I2C_SMBUS_BLOCK_MAX)
- return -1;
+ return -EPROTO;
data->block[0] = len;
}
@@ -394,7 +397,7 @@ static int i801_set_block_buffer_mode(void)
{
outb_p(inb_p(SMBAUXCTL) | SMBAUXCTL_E32B, SMBAUXCTL);
if ((inb_p(SMBAUXCTL) & SMBAUXCTL_E32B) == 0)
- return -1;
+ return -EIO;
return 0;
}
@@ -414,7 +417,7 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
} else if (!(i801_features & FEATURE_I2C_BLOCK_READ)) {
dev_err(&I801_dev->dev,
"I2C block read is unsupported!\n");
- return -1;
+ return -EOPNOTSUPP;
}
}
@@ -449,7 +452,7 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
return result;
}
-/* Return -1 on error. */
+/* Return negative errno on error. */
static s32 i801_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write, u8 command,
int size, union i2c_smbus_data * data)
@@ -511,10 +514,9 @@ static s32 i801_access(struct i2c_adapter * adap, u16 addr,
outb_p(command, SMBHSTCMD);
block = 1;
break;
- case I2C_SMBUS_PROC_CALL:
default:
dev_err(&I801_dev->dev, "Unsupported transaction %d\n", size);
- return -1;
+ return -EOPNOTSUPP;
}
if (hwpec) /* enable/disable hardware PEC */
@@ -537,7 +539,7 @@ static s32 i801_access(struct i2c_adapter * adap, u16 addr,
if(block)
return ret;
if(ret)
- return -1;
+ return ret;
if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
return 0;
@@ -572,7 +574,7 @@ static const struct i2c_algorithm smbus_algorithm = {
static struct i2c_adapter i801_adapter = {
.owner = THIS_MODULE,
.id = I2C_HW_SMBUS_I801,
- .class = I2C_CLASS_HWMON,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
.algo = &smbus_algorithm,
};
@@ -639,6 +641,10 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id
goto exit;
}
+ err = acpi_check_resource_conflict(&dev->resource[SMBBAR]);
+ if (err)
+ goto exit;
+
err = pci_request_region(dev, SMBBAR, i801_driver.name);
if (err) {
dev_err(&dev->dev, "Failed to request SMBus region "
diff --git a/drivers/i2c/busses/i2c-i810.c b/drivers/i2c/busses/i2c-i810.c
deleted file mode 100644
index 42e8d94c276f..000000000000
--- a/drivers/i2c/busses/i2c-i810.c
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- i2c-i810.c - Part of lm_sensors, Linux kernel modules for hardware
- monitoring
- Copyright (c) 1998, 1999, 2000 Frodo Looijaard <frodol@dds.nl>,
- Philip Edelbrock <phil@netroedge.com>,
- Ralph Metzler <rjkm@thp.uni-koeln.de>, and
- Mark D. Studebaker <mdsxyz123@yahoo.com>
-
- Based on code written by Ralph Metzler <rjkm@thp.uni-koeln.de> and
- Simon Vogl
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-/*
- This interfaces to the I810/I815 to provide access to
- the DDC Bus and the I2C Bus.
-
- SUPPORTED DEVICES PCI ID
- i810AA 7121
- i810AB 7123
- i810E 7125
- i815 1132
- i845G 2562
-*/
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-#include <asm/io.h>
-
-/* GPIO register locations */
-#define I810_IOCONTROL_OFFSET 0x5000
-#define I810_HVSYNC 0x00 /* not used */
-#define I810_GPIOA 0x10
-#define I810_GPIOB 0x14
-
-/* bit locations in the registers */
-#define SCL_DIR_MASK 0x0001
-#define SCL_DIR 0x0002
-#define SCL_VAL_MASK 0x0004
-#define SCL_VAL_OUT 0x0008
-#define SCL_VAL_IN 0x0010
-#define SDA_DIR_MASK 0x0100
-#define SDA_DIR 0x0200
-#define SDA_VAL_MASK 0x0400
-#define SDA_VAL_OUT 0x0800
-#define SDA_VAL_IN 0x1000
-
-/* initialization states */
-#define INIT1 0x1
-#define INIT2 0x2
-#define INIT3 0x4
-
-/* delays */
-#define CYCLE_DELAY 10
-#define TIMEOUT (HZ / 2)
-
-static void __iomem *ioaddr;
-
-/* The i810 GPIO registers have individual masks for each bit
- so we never have to read before writing. Nice. */
-
-static void bit_i810i2c_setscl(void *data, int val)
-{
- writel((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK,
- ioaddr + I810_GPIOB);
- readl(ioaddr + I810_GPIOB); /* flush posted write */
-}
-
-static void bit_i810i2c_setsda(void *data, int val)
-{
- writel((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK,
- ioaddr + I810_GPIOB);
- readl(ioaddr + I810_GPIOB); /* flush posted write */
-}
-
-/* The GPIO pins are open drain, so the pins could always remain outputs.
- However, some chip versions don't latch the inputs unless they
- are set as inputs.
- We rely on the i2c-algo-bit routines to set the pins high before
- reading the input from other chips. Following guidance in the 815
- prog. ref. guide, we do a "dummy write" of 0 to the register before
- reading which forces the input value to be latched. We presume this
- applies to the 810 as well; shouldn't hurt anyway. This is necessary to get
- i2c_algo_bit bit_test=1 to pass. */
-
-static int bit_i810i2c_getscl(void *data)
-{
- writel(SCL_DIR_MASK, ioaddr + I810_GPIOB);
- writel(0, ioaddr + I810_GPIOB);
- return (0 != (readl(ioaddr + I810_GPIOB) & SCL_VAL_IN));
-}
-
-static int bit_i810i2c_getsda(void *data)
-{
- writel(SDA_DIR_MASK, ioaddr + I810_GPIOB);
- writel(0, ioaddr + I810_GPIOB);
- return (0 != (readl(ioaddr + I810_GPIOB) & SDA_VAL_IN));
-}
-
-static void bit_i810ddc_setscl(void *data, int val)
-{
- writel((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK,
- ioaddr + I810_GPIOA);
- readl(ioaddr + I810_GPIOA); /* flush posted write */
-}
-
-static void bit_i810ddc_setsda(void *data, int val)
-{
- writel((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK,
- ioaddr + I810_GPIOA);
- readl(ioaddr + I810_GPIOA); /* flush posted write */
-}
-
-static int bit_i810ddc_getscl(void *data)
-{
- writel(SCL_DIR_MASK, ioaddr + I810_GPIOA);
- writel(0, ioaddr + I810_GPIOA);
- return (0 != (readl(ioaddr + I810_GPIOA) & SCL_VAL_IN));
-}
-
-static int bit_i810ddc_getsda(void *data)
-{
- writel(SDA_DIR_MASK, ioaddr + I810_GPIOA);
- writel(0, ioaddr + I810_GPIOA);
- return (0 != (readl(ioaddr + I810_GPIOA) & SDA_VAL_IN));
-}
-
-static int config_i810(struct pci_dev *dev)
-{
- unsigned long cadr;
-
- /* map I810 memory */
- cadr = dev->resource[1].start;
- cadr += I810_IOCONTROL_OFFSET;
- cadr &= PCI_BASE_ADDRESS_MEM_MASK;
- ioaddr = ioremap_nocache(cadr, 0x1000);
- if (ioaddr) {
- bit_i810i2c_setscl(NULL, 1);
- bit_i810i2c_setsda(NULL, 1);
- bit_i810ddc_setscl(NULL, 1);
- bit_i810ddc_setsda(NULL, 1);
- return 0;
- }
- return -ENODEV;
-}
-
-static struct i2c_algo_bit_data i810_i2c_bit_data = {
- .setsda = bit_i810i2c_setsda,
- .setscl = bit_i810i2c_setscl,
- .getsda = bit_i810i2c_getsda,
- .getscl = bit_i810i2c_getscl,
- .udelay = CYCLE_DELAY,
- .timeout = TIMEOUT,
-};
-
-static struct i2c_adapter i810_i2c_adapter = {
- .owner = THIS_MODULE,
- .id = I2C_HW_B_I810,
- .name = "I810/I815 I2C Adapter",
- .algo_data = &i810_i2c_bit_data,
-};
-
-static struct i2c_algo_bit_data i810_ddc_bit_data = {
- .setsda = bit_i810ddc_setsda,
- .setscl = bit_i810ddc_setscl,
- .getsda = bit_i810ddc_getsda,
- .getscl = bit_i810ddc_getscl,
- .udelay = CYCLE_DELAY,
- .timeout = TIMEOUT,
-};
-
-static struct i2c_adapter i810_ddc_adapter = {
- .owner = THIS_MODULE,
- .id = I2C_HW_B_I810,
- .name = "I810/I815 DDC Adapter",
- .algo_data = &i810_ddc_bit_data,
-};
-
-static struct pci_device_id i810_ids[] __devinitdata = {
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810_IG1) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810_IG3) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810E_IG) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82815_CGC) },
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82845G_IG) },
- { 0, },
-};
-
-MODULE_DEVICE_TABLE (pci, i810_ids);
-
-static int __devinit i810_probe(struct pci_dev *dev, const struct pci_device_id *id)
-{
- int retval;
-
- retval = config_i810(dev);
- if (retval)
- return retval;
- dev_info(&dev->dev, "i810/i815 i2c device found.\n");
-
- /* set up the sysfs linkage to our parent device */
- i810_i2c_adapter.dev.parent = &dev->dev;
- i810_ddc_adapter.dev.parent = &dev->dev;
-
- retval = i2c_bit_add_bus(&i810_i2c_adapter);
- if (retval)
- return retval;
- retval = i2c_bit_add_bus(&i810_ddc_adapter);
- if (retval)
- i2c_del_adapter(&i810_i2c_adapter);
- return retval;
-}
-
-static void __devexit i810_remove(struct pci_dev *dev)
-{
- i2c_del_adapter(&i810_ddc_adapter);
- i2c_del_adapter(&i810_i2c_adapter);
- iounmap(ioaddr);
-}
-
-static struct pci_driver i810_driver = {
- .name = "i810_smbus",
- .id_table = i810_ids,
- .probe = i810_probe,
- .remove = __devexit_p(i810_remove),
-};
-
-static int __init i2c_i810_init(void)
-{
- return pci_register_driver(&i810_driver);
-}
-
-static void __exit i2c_i810_exit(void)
-{
- pci_unregister_driver(&i810_driver);
-}
-
-MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, "
- "Philip Edelbrock <phil@netroedge.com>, "
- "Ralph Metzler <rjkm@thp.uni-koeln.de>, "
- "and Mark D. Studebaker <mdsxyz123@yahoo.com>");
-MODULE_DESCRIPTION("I810/I815 I2C/DDC driver");
-MODULE_LICENSE("GPL");
-
-module_init(i2c_i810_init);
-module_exit(i2c_i810_exit);
diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c
index 85dbf34382e1..d8a19c110564 100644
--- a/drivers/i2c/busses/i2c-ibm_iic.c
+++ b/drivers/i2c/busses/i2c-ibm_iic.c
@@ -42,13 +42,7 @@
#include <asm/io.h>
#include <linux/i2c.h>
#include <linux/i2c-id.h>
-
-#ifdef CONFIG_IBM_OCP
-#include <asm/ocp.h>
-#include <asm/ibm4xx.h>
-#else
#include <linux/of_platform.h>
-#endif
#include "i2c-ibm_iic.h"
@@ -665,180 +659,6 @@ static inline u8 iic_clckdiv(unsigned int opb)
return (u8)((opb + 9) / 10 - 1);
}
-#ifdef CONFIG_IBM_OCP
-/*
- * Register single IIC interface
- */
-static int __devinit iic_probe(struct ocp_device *ocp){
-
- struct ibm_iic_private* dev;
- struct i2c_adapter* adap;
- struct ocp_func_iic_data* iic_data = ocp->def->additions;
- int ret;
-
- if (!iic_data)
- printk(KERN_WARNING"ibm-iic%d: missing additional data!\n",
- ocp->def->index);
-
- if (!(dev = kzalloc(sizeof(*dev), GFP_KERNEL))) {
- printk(KERN_ERR "ibm-iic%d: failed to allocate device data\n",
- ocp->def->index);
- return -ENOMEM;
- }
-
- dev->idx = ocp->def->index;
- ocp_set_drvdata(ocp, dev);
-
- if (!request_mem_region(ocp->def->paddr, sizeof(struct iic_regs),
- "ibm_iic")) {
- ret = -EBUSY;
- goto fail1;
- }
-
- if (!(dev->vaddr = ioremap(ocp->def->paddr, sizeof(struct iic_regs)))){
- printk(KERN_ERR "ibm-iic%d: failed to ioremap device registers\n",
- dev->idx);
- ret = -ENXIO;
- goto fail2;
- }
-
- init_waitqueue_head(&dev->wq);
-
- dev->irq = iic_force_poll ? -1 : ocp->def->irq;
- if (dev->irq >= 0){
- /* Disable interrupts until we finish initialization,
- assumes level-sensitive IRQ setup...
- */
- iic_interrupt_mode(dev, 0);
- if (request_irq(dev->irq, iic_handler, 0, "IBM IIC", dev)){
- printk(KERN_ERR "ibm-iic%d: request_irq %d failed\n",
- dev->idx, dev->irq);
- /* Fallback to the polling mode */
- dev->irq = -1;
- }
- }
-
- if (dev->irq < 0)
- printk(KERN_WARNING "ibm-iic%d: using polling mode\n",
- dev->idx);
-
- /* Board specific settings */
- dev->fast_mode = iic_force_fast ? 1 : (iic_data ? iic_data->fast_mode : 0);
-
- /* clckdiv is the same for *all* IIC interfaces,
- * but I'd rather make a copy than introduce another global. --ebs
- */
- dev->clckdiv = iic_clckdiv(ocp_sys_info.opb_bus_freq);
- DBG("%d: clckdiv = %d\n", dev->idx, dev->clckdiv);
-
- /* Initialize IIC interface */
- iic_dev_init(dev);
-
- /* Register it with i2c layer */
- adap = &dev->adap;
- adap->dev.parent = &ocp->dev;
- strcpy(adap->name, "IBM IIC");
- i2c_set_adapdata(adap, dev);
- adap->id = I2C_HW_OCP;
- adap->class = I2C_CLASS_HWMON;
- adap->algo = &iic_algo;
- adap->client_register = NULL;
- adap->client_unregister = NULL;
- adap->timeout = 1;
-
- /*
- * If "dev->idx" is negative we consider it as zero.
- * The reason to do so is to avoid sysfs names that only make
- * sense when there are multiple adapters.
- */
- adap->nr = dev->idx >= 0 ? dev->idx : 0;
-
- if ((ret = i2c_add_numbered_adapter(adap)) < 0) {
- printk(KERN_ERR "ibm-iic%d: failed to register i2c adapter\n",
- dev->idx);
- goto fail;
- }
-
- printk(KERN_INFO "ibm-iic%d: using %s mode\n", dev->idx,
- dev->fast_mode ? "fast (400 kHz)" : "standard (100 kHz)");
-
- return 0;
-
-fail:
- if (dev->irq >= 0){
- iic_interrupt_mode(dev, 0);
- free_irq(dev->irq, dev);
- }
-
- iounmap(dev->vaddr);
-fail2:
- release_mem_region(ocp->def->paddr, sizeof(struct iic_regs));
-fail1:
- ocp_set_drvdata(ocp, NULL);
- kfree(dev);
- return ret;
-}
-
-/*
- * Cleanup initialized IIC interface
- */
-static void __devexit iic_remove(struct ocp_device *ocp)
-{
- struct ibm_iic_private* dev = (struct ibm_iic_private*)ocp_get_drvdata(ocp);
- BUG_ON(dev == NULL);
- if (i2c_del_adapter(&dev->adap)){
- printk(KERN_ERR "ibm-iic%d: failed to delete i2c adapter :(\n",
- dev->idx);
- /* That's *very* bad, just shutdown IRQ ... */
- if (dev->irq >= 0){
- iic_interrupt_mode(dev, 0);
- free_irq(dev->irq, dev);
- dev->irq = -1;
- }
- } else {
- if (dev->irq >= 0){
- iic_interrupt_mode(dev, 0);
- free_irq(dev->irq, dev);
- }
- iounmap(dev->vaddr);
- release_mem_region(ocp->def->paddr, sizeof(struct iic_regs));
- kfree(dev);
- }
-}
-
-static struct ocp_device_id ibm_iic_ids[] __devinitdata =
-{
- { .vendor = OCP_VENDOR_IBM, .function = OCP_FUNC_IIC },
- { .vendor = OCP_VENDOR_INVALID }
-};
-
-MODULE_DEVICE_TABLE(ocp, ibm_iic_ids);
-
-static struct ocp_driver ibm_iic_driver =
-{
- .name = "iic",
- .id_table = ibm_iic_ids,
- .probe = iic_probe,
- .remove = __devexit_p(iic_remove),
-#if defined(CONFIG_PM)
- .suspend = NULL,
- .resume = NULL,
-#endif
-};
-
-static int __init iic_init(void)
-{
- printk(KERN_INFO "IBM IIC driver v" DRIVER_VERSION "\n");
- return ocp_register_driver(&ibm_iic_driver);
-}
-
-static void __exit iic_exit(void)
-{
- ocp_unregister_driver(&ibm_iic_driver);
-}
-
-#else /* !CONFIG_IBM_OCP */
-
static int __devinit iic_request_irq(struct of_device *ofdev,
struct ibm_iic_private *dev)
{
@@ -934,7 +754,7 @@ static int __devinit iic_probe(struct of_device *ofdev,
strlcpy(adap->name, "IBM IIC", sizeof(adap->name));
i2c_set_adapdata(adap, dev);
adap->id = I2C_HW_OCP;
- adap->class = I2C_CLASS_HWMON;
+ adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
adap->algo = &iic_algo;
adap->timeout = 1;
adap->nr = dev->idx;
@@ -1011,7 +831,6 @@ static void __exit iic_exit(void)
{
of_unregister_platform_driver(&ibm_iic_driver);
}
-#endif /* CONFIG_IBM_OCP */
module_init(iic_init);
module_exit(iic_exit);
diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c
index 39884e797594..fc2714ac0c0f 100644
--- a/drivers/i2c/busses/i2c-iop3xx.c
+++ b/drivers/i2c/busses/i2c-iop3xx.c
@@ -482,7 +482,7 @@ iop3xx_i2c_probe(struct platform_device *pdev)
memcpy(new_adapter->name, pdev->name, strlen(pdev->name));
new_adapter->id = I2C_HW_IOP3XX;
new_adapter->owner = THIS_MODULE;
- new_adapter->class = I2C_CLASS_HWMON;
+ new_adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
new_adapter->dev.parent = &pdev->dev;
new_adapter->nr = pdev->id;
diff --git a/drivers/i2c/busses/i2c-isch.c b/drivers/i2c/busses/i2c-isch.c
new file mode 100644
index 000000000000..b9c01aa90036
--- /dev/null
+++ b/drivers/i2c/busses/i2c-isch.c
@@ -0,0 +1,339 @@
+/*
+ i2c-isch.c - Linux kernel driver for Intel SCH chipset SMBus
+ - Based on i2c-piix4.c
+ Copyright (c) 1998 - 2002 Frodo Looijaard <frodol@dds.nl> and
+ Philip Edelbrock <phil@netroedge.com>
+ - Intel SCH support
+ Copyright (c) 2007 - 2008 Jacob Jun Pan <jacob.jun.pan@intel.com>
+
+ 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.
+
+ 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.
+*/
+
+/*
+ Supports:
+ Intel SCH chipsets (AF82US15W, AF82US15L, AF82UL11L)
+ Note: we assume there can only be one device, with one SMBus interface.
+*/
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/stddef.h>
+#include <linux/ioport.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/acpi.h>
+
+/* SCH SMBus address offsets */
+#define SMBHSTCNT (0 + sch_smba)
+#define SMBHSTSTS (1 + sch_smba)
+#define SMBHSTADD (4 + sch_smba) /* TSA */
+#define SMBHSTCMD (5 + sch_smba)
+#define SMBHSTDAT0 (6 + sch_smba)
+#define SMBHSTDAT1 (7 + sch_smba)
+#define SMBBLKDAT (0x20 + sch_smba)
+
+/* count for request_region */
+#define SMBIOSIZE 64
+
+/* PCI Address Constants */
+#define SMBBA_SCH 0x40
+
+/* Other settings */
+#define MAX_TIMEOUT 500
+
+/* I2C constants */
+#define SCH_QUICK 0x00
+#define SCH_BYTE 0x01
+#define SCH_BYTE_DATA 0x02
+#define SCH_WORD_DATA 0x03
+#define SCH_BLOCK_DATA 0x05
+
+static unsigned short sch_smba;
+static struct pci_driver sch_driver;
+static struct i2c_adapter sch_adapter;
+
+/*
+ * Start the i2c transaction -- the i2c_access will prepare the transaction
+ * and this function will execute it.
+ * return 0 for success and others for failure.
+ */
+static int sch_transaction(void)
+{
+ int temp;
+ int result = 0;
+ int timeout = 0;
+
+ dev_dbg(&sch_adapter.dev, "Transaction (pre): CNT=%02x, CMD=%02x, "
+ "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb(SMBHSTCNT),
+ inb(SMBHSTCMD), inb(SMBHSTADD), inb(SMBHSTDAT0),
+ inb(SMBHSTDAT1));
+
+ /* Make sure the SMBus host is ready to start transmitting */
+ temp = inb(SMBHSTSTS) & 0x0f;
+ if (temp) {
+ /* Can not be busy since we checked it in sch_access */
+ if (temp & 0x01) {
+ dev_dbg(&sch_adapter.dev, "Completion (%02x). "
+ "Clear...\n", temp);
+ }
+ if (temp & 0x06) {
+ dev_dbg(&sch_adapter.dev, "SMBus error (%02x). "
+ "Resetting...\n", temp);
+ }
+ outb(temp, SMBHSTSTS);
+ temp = inb(SMBHSTSTS) & 0x0f;
+ if (temp) {
+ dev_err(&sch_adapter.dev,
+ "SMBus is not ready: (%02x)\n", temp);
+ return -EAGAIN;
+ }
+ }
+
+ /* start the transaction by setting bit 4 */
+ outb(inb(SMBHSTCNT) | 0x10, SMBHSTCNT);
+
+ do {
+ msleep(1);
+ temp = inb(SMBHSTSTS) & 0x0f;
+ } while ((temp & 0x08) && (timeout++ < MAX_TIMEOUT));
+
+ /* If the SMBus is still busy, we give up */
+ if (timeout >= MAX_TIMEOUT) {
+ dev_err(&sch_adapter.dev, "SMBus Timeout!\n");
+ result = -ETIMEDOUT;
+ }
+ if (temp & 0x04) {
+ result = -EIO;
+ dev_dbg(&sch_adapter.dev, "Bus collision! SMBus may be "
+ "locked until next hard reset. (sorry!)\n");
+ /* Clock stops and slave is stuck in mid-transmission */
+ } else if (temp & 0x02) {
+ result = -EIO;
+ dev_err(&sch_adapter.dev, "Error: no response!\n");
+ } else if (temp & 0x01) {
+ dev_dbg(&sch_adapter.dev, "Post complete!\n");
+ outb(temp, SMBHSTSTS);
+ temp = inb(SMBHSTSTS) & 0x07;
+ if (temp & 0x06) {
+ /* Completion clear failed */
+ dev_dbg(&sch_adapter.dev, "Failed reset at end of "
+ "transaction (%02x), Bus error!\n", temp);
+ }
+ } else {
+ result = -ENXIO;
+ dev_dbg(&sch_adapter.dev, "No such address.\n");
+ }
+ dev_dbg(&sch_adapter.dev, "Transaction (post): CNT=%02x, CMD=%02x, "
+ "ADD=%02x, DAT0=%02x, DAT1=%02x\n", inb(SMBHSTCNT),
+ inb(SMBHSTCMD), inb(SMBHSTADD), inb(SMBHSTDAT0),
+ inb(SMBHSTDAT1));
+ return result;
+}
+
+/*
+ * This is the main access entry for i2c-sch access
+ * adap is i2c_adapter pointer, addr is the i2c device bus address, read_write
+ * (0 for read and 1 for write), size is i2c transaction type and data is the
+ * union of transaction for data to be transfered or data read from bus.
+ * return 0 for success and others for failure.
+ */
+static s32 sch_access(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size, union i2c_smbus_data *data)
+{
+ int i, len, temp, rc;
+
+ /* Make sure the SMBus host is not busy */
+ temp = inb(SMBHSTSTS) & 0x0f;
+ if (temp & 0x08) {
+ dev_dbg(&sch_adapter.dev, "SMBus busy (%02x)\n", temp);
+ return -EAGAIN;
+ }
+ dev_dbg(&sch_adapter.dev, "access size: %d %s\n", size,
+ (read_write)?"READ":"WRITE");
+ switch (size) {
+ case I2C_SMBUS_QUICK:
+ outb((addr << 1) | read_write, SMBHSTADD);
+ size = SCH_QUICK;
+ break;
+ case I2C_SMBUS_BYTE:
+ outb((addr << 1) | read_write, SMBHSTADD);
+ if (read_write == I2C_SMBUS_WRITE)
+ outb(command, SMBHSTCMD);
+ size = SCH_BYTE;
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ outb((addr << 1) | read_write, SMBHSTADD);
+ outb(command, SMBHSTCMD);
+ if (read_write == I2C_SMBUS_WRITE)
+ outb(data->byte, SMBHSTDAT0);
+ size = SCH_BYTE_DATA;
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ outb((addr << 1) | read_write, SMBHSTADD);
+ outb(command, SMBHSTCMD);
+ if (read_write == I2C_SMBUS_WRITE) {
+ outb(data->word & 0xff, SMBHSTDAT0);
+ outb((data->word & 0xff00) >> 8, SMBHSTDAT1);
+ }
+ size = SCH_WORD_DATA;
+ break;
+ case I2C_SMBUS_BLOCK_DATA:
+ outb((addr << 1) | read_write, SMBHSTADD);
+ outb(command, SMBHSTCMD);
+ if (read_write == I2C_SMBUS_WRITE) {
+ len = data->block[0];
+ if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
+ return -EINVAL;
+ outb(len, SMBHSTDAT0);
+ for (i = 1; i <= len; i++)
+ outb(data->block[i], SMBBLKDAT+i-1);
+ }
+ size = SCH_BLOCK_DATA;
+ break;
+ default:
+ dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
+ return -EOPNOTSUPP;
+ }
+ dev_dbg(&sch_adapter.dev, "write size %d to 0x%04x\n", size, SMBHSTCNT);
+ outb((inb(SMBHSTCNT) & 0xb0) | (size & 0x7), SMBHSTCNT);
+
+ rc = sch_transaction();
+ if (rc) /* Error in transaction */
+ return rc;
+
+ if ((read_write == I2C_SMBUS_WRITE) || (size == SCH_QUICK))
+ return 0;
+
+ switch (size) {
+ case SCH_BYTE:
+ case SCH_BYTE_DATA:
+ data->byte = inb(SMBHSTDAT0);
+ break;
+ case SCH_WORD_DATA:
+ data->word = inb(SMBHSTDAT0) + (inb(SMBHSTDAT1) << 8);
+ break;
+ case SCH_BLOCK_DATA:
+ data->block[0] = inb(SMBHSTDAT0);
+ if (data->block[0] == 0 || data->block[0] > I2C_SMBUS_BLOCK_MAX)
+ return -EPROTO;
+ for (i = 1; i <= data->block[0]; i++)
+ data->block[i] = inb(SMBBLKDAT+i-1);
+ break;
+ }
+ return 0;
+}
+
+static u32 sch_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static const struct i2c_algorithm smbus_algorithm = {
+ .smbus_xfer = sch_access,
+ .functionality = sch_func,
+};
+
+static struct i2c_adapter sch_adapter = {
+ .owner = THIS_MODULE,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
+ .algo = &smbus_algorithm,
+};
+
+static struct pci_device_id sch_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, sch_ids);
+
+static int __devinit sch_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ int retval;
+ unsigned int smba;
+
+ pci_read_config_dword(dev, SMBBA_SCH, &smba);
+ if (!(smba & (1 << 31))) {
+ dev_err(&dev->dev, "SMBus I/O space disabled!\n");
+ return -ENODEV;
+ }
+
+ sch_smba = (unsigned short)smba;
+ if (sch_smba == 0) {
+ dev_err(&dev->dev, "SMBus base address uninitialized!\n");
+ return -ENODEV;
+ }
+ if (acpi_check_region(sch_smba, SMBIOSIZE, sch_driver.name))
+ return -EBUSY;
+ if (!request_region(sch_smba, SMBIOSIZE, sch_driver.name)) {
+ dev_err(&dev->dev, "SMBus region 0x%x already in use!\n",
+ sch_smba);
+ return -EBUSY;
+ }
+ dev_dbg(&dev->dev, "SMBA = 0x%X\n", sch_smba);
+
+ /* set up the sysfs linkage to our parent device */
+ sch_adapter.dev.parent = &dev->dev;
+
+ snprintf(sch_adapter.name, sizeof(sch_adapter.name),
+ "SMBus SCH adapter at %04x", sch_smba);
+
+ retval = i2c_add_adapter(&sch_adapter);
+ if (retval) {
+ dev_err(&dev->dev, "Couldn't register adapter!\n");
+ release_region(sch_smba, SMBIOSIZE);
+ sch_smba = 0;
+ }
+
+ return retval;
+}
+
+static void __devexit sch_remove(struct pci_dev *dev)
+{
+ if (sch_smba) {
+ i2c_del_adapter(&sch_adapter);
+ release_region(sch_smba, SMBIOSIZE);
+ sch_smba = 0;
+ }
+}
+
+static struct pci_driver sch_driver = {
+ .name = "isch_smbus",
+ .id_table = sch_ids,
+ .probe = sch_probe,
+ .remove = __devexit_p(sch_remove),
+};
+
+static int __init i2c_sch_init(void)
+{
+ return pci_register_driver(&sch_driver);
+}
+
+static void __exit i2c_sch_exit(void)
+{
+ pci_unregister_driver(&sch_driver);
+}
+
+MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@intel.com>");
+MODULE_DESCRIPTION("Intel SCH SMBus driver");
+MODULE_LICENSE("GPL");
+
+module_init(i2c_sch_init);
+module_exit(i2c_sch_exit);
diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
index a076129de7e8..10b9342a36c2 100644
--- a/drivers/i2c/busses/i2c-mpc.c
+++ b/drivers/i2c/busses/i2c-mpc.c
@@ -311,7 +311,7 @@ static struct i2c_adapter mpc_ops = {
.name = "MPC adapter",
.id = I2C_HW_MPC107,
.algo = &mpc_algo,
- .class = I2C_CLASS_HWMON,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
.timeout = 1,
};
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index 036e6a883e67..9e8118d2fe64 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -530,7 +530,7 @@ mv64xxx_i2c_probe(struct platform_device *pd)
drv_data->adapter.id = I2C_HW_MV64XXX;
drv_data->adapter.algo = &mv64xxx_i2c_algo;
drv_data->adapter.owner = THIS_MODULE;
- drv_data->adapter.class = I2C_CLASS_HWMON;
+ drv_data->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
drv_data->adapter.timeout = pdata->timeout;
drv_data->adapter.nr = pd->id;
platform_set_drvdata(pd, drv_data);
diff --git a/drivers/i2c/busses/i2c-nforce2-s4985.c b/drivers/i2c/busses/i2c-nforce2-s4985.c
new file mode 100644
index 000000000000..6a8995dfd0bb
--- /dev/null
+++ b/drivers/i2c/busses/i2c-nforce2-s4985.c
@@ -0,0 +1,257 @@
+/*
+ * i2c-nforce2-s4985.c - i2c-nforce2 extras for the Tyan S4985 motherboard
+ *
+ * Copyright (C) 2008 Jean Delvare <khali@linux-fr.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * We select the channels by sending commands to the Philips
+ * PCA9556 chip at I2C address 0x18. The main adapter is used for
+ * the non-multiplexed part of the bus, and 4 virtual adapters
+ * are defined for the multiplexed addresses: 0x50-0x53 (memory
+ * module EEPROM) located on channels 1-4. We define one virtual
+ * adapter per CPU, which corresponds to one multiplexed channel:
+ * CPU0: virtual adapter 1, channel 1
+ * CPU1: virtual adapter 2, channel 2
+ * CPU2: virtual adapter 3, channel 3
+ * CPU3: virtual adapter 4, channel 4
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+
+extern struct i2c_adapter *nforce2_smbus;
+
+static struct i2c_adapter *s4985_adapter;
+static struct i2c_algorithm *s4985_algo;
+
+/* Wrapper access functions for multiplexed SMBus */
+static DEFINE_MUTEX(nforce2_lock);
+
+static s32 nforce2_access_virt0(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size,
+ union i2c_smbus_data *data)
+{
+ int error;
+
+ /* We exclude the multiplexed addresses */
+ if ((addr & 0xfc) == 0x50 || (addr & 0xfc) == 0x30
+ || addr == 0x18)
+ return -ENXIO;
+
+ mutex_lock(&nforce2_lock);
+ error = nforce2_smbus->algo->smbus_xfer(adap, addr, flags, read_write,
+ command, size, data);
+ mutex_unlock(&nforce2_lock);
+
+ return error;
+}
+
+/* We remember the last used channels combination so as to only switch
+ channels when it is really needed. This greatly reduces the SMBus
+ overhead, but also assumes that nobody will be writing to the PCA9556
+ in our back. */
+static u8 last_channels;
+
+static inline s32 nforce2_access_channel(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size,
+ union i2c_smbus_data *data,
+ u8 channels)
+{
+ int error;
+
+ /* We exclude the non-multiplexed addresses */
+ if ((addr & 0xfc) != 0x50 && (addr & 0xfc) != 0x30)
+ return -ENXIO;
+
+ mutex_lock(&nforce2_lock);
+ if (last_channels != channels) {
+ union i2c_smbus_data mplxdata;
+ mplxdata.byte = channels;
+
+ error = nforce2_smbus->algo->smbus_xfer(adap, 0x18, 0,
+ I2C_SMBUS_WRITE, 0x01,
+ I2C_SMBUS_BYTE_DATA,
+ &mplxdata);
+ if (error)
+ goto UNLOCK;
+ last_channels = channels;
+ }
+ error = nforce2_smbus->algo->smbus_xfer(adap, addr, flags, read_write,
+ command, size, data);
+
+UNLOCK:
+ mutex_unlock(&nforce2_lock);
+ return error;
+}
+
+static s32 nforce2_access_virt1(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size,
+ union i2c_smbus_data *data)
+{
+ /* CPU0: channel 1 enabled */
+ return nforce2_access_channel(adap, addr, flags, read_write, command,
+ size, data, 0x02);
+}
+
+static s32 nforce2_access_virt2(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size,
+ union i2c_smbus_data *data)
+{
+ /* CPU1: channel 2 enabled */
+ return nforce2_access_channel(adap, addr, flags, read_write, command,
+ size, data, 0x04);
+}
+
+static s32 nforce2_access_virt3(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size,
+ union i2c_smbus_data *data)
+{
+ /* CPU2: channel 3 enabled */
+ return nforce2_access_channel(adap, addr, flags, read_write, command,
+ size, data, 0x08);
+}
+
+static s32 nforce2_access_virt4(struct i2c_adapter *adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size,
+ union i2c_smbus_data *data)
+{
+ /* CPU3: channel 4 enabled */
+ return nforce2_access_channel(adap, addr, flags, read_write, command,
+ size, data, 0x10);
+}
+
+static int __init nforce2_s4985_init(void)
+{
+ int i, error;
+ union i2c_smbus_data ioconfig;
+
+ /* Unregister physical bus */
+ if (!nforce2_smbus)
+ return -ENODEV;
+ error = i2c_del_adapter(nforce2_smbus);
+ if (error) {
+ dev_err(&nforce2_smbus->dev, "Physical bus removal failed\n");
+ goto ERROR0;
+ }
+
+ printk(KERN_INFO "Enabling SMBus multiplexing for Tyan S4985\n");
+ /* Define the 5 virtual adapters and algorithms structures */
+ s4985_adapter = kzalloc(5 * sizeof(struct i2c_adapter), GFP_KERNEL);
+ if (!s4985_adapter) {
+ error = -ENOMEM;
+ goto ERROR1;
+ }
+ s4985_algo = kzalloc(5 * sizeof(struct i2c_algorithm), GFP_KERNEL);
+ if (!s4985_algo) {
+ error = -ENOMEM;
+ goto ERROR2;
+ }
+
+ /* Fill in the new structures */
+ s4985_algo[0] = *(nforce2_smbus->algo);
+ s4985_algo[0].smbus_xfer = nforce2_access_virt0;
+ s4985_adapter[0] = *nforce2_smbus;
+ s4985_adapter[0].algo = s4985_algo;
+ s4985_adapter[0].dev.parent = nforce2_smbus->dev.parent;
+ for (i = 1; i < 5; i++) {
+ s4985_algo[i] = *(nforce2_smbus->algo);
+ s4985_adapter[i] = *nforce2_smbus;
+ snprintf(s4985_adapter[i].name, sizeof(s4985_adapter[i].name),
+ "SMBus nForce2 adapter (CPU%d)", i - 1);
+ s4985_adapter[i].algo = s4985_algo + i;
+ s4985_adapter[i].dev.parent = nforce2_smbus->dev.parent;
+ }
+ s4985_algo[1].smbus_xfer = nforce2_access_virt1;
+ s4985_algo[2].smbus_xfer = nforce2_access_virt2;
+ s4985_algo[3].smbus_xfer = nforce2_access_virt3;
+ s4985_algo[4].smbus_xfer = nforce2_access_virt4;
+
+ /* Configure the PCA9556 multiplexer */
+ ioconfig.byte = 0x00; /* All I/O to output mode */
+ error = nforce2_smbus->algo->smbus_xfer(nforce2_smbus, 0x18, 0,
+ I2C_SMBUS_WRITE, 0x03,
+ I2C_SMBUS_BYTE_DATA, &ioconfig);
+ if (error) {
+ dev_err(&nforce2_smbus->dev, "PCA9556 configuration failed\n");
+ error = -EIO;
+ goto ERROR3;
+ }
+
+ /* Register virtual adapters */
+ for (i = 0; i < 5; i++) {
+ error = i2c_add_adapter(s4985_adapter + i);
+ if (error) {
+ dev_err(&nforce2_smbus->dev,
+ "Virtual adapter %d registration "
+ "failed, module not inserted\n", i);
+ for (i--; i >= 0; i--)
+ i2c_del_adapter(s4985_adapter + i);
+ goto ERROR3;
+ }
+ }
+
+ return 0;
+
+ERROR3:
+ kfree(s4985_algo);
+ s4985_algo = NULL;
+ERROR2:
+ kfree(s4985_adapter);
+ s4985_adapter = NULL;
+ERROR1:
+ /* Restore physical bus */
+ i2c_add_adapter(nforce2_smbus);
+ERROR0:
+ return error;
+}
+
+static void __exit nforce2_s4985_exit(void)
+{
+ if (s4985_adapter) {
+ int i;
+
+ for (i = 0; i < 5; i++)
+ i2c_del_adapter(s4985_adapter+i);
+ kfree(s4985_adapter);
+ s4985_adapter = NULL;
+ }
+ kfree(s4985_algo);
+ s4985_algo = NULL;
+
+ /* Restore physical bus */
+ if (i2c_add_adapter(nforce2_smbus))
+ dev_err(&nforce2_smbus->dev, "Physical bus restoration "
+ "failed\n");
+}
+
+MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("S4985 SMBus multiplexing");
+MODULE_LICENSE("GPL");
+
+module_init(nforce2_s4985_init);
+module_exit(nforce2_s4985_exit);
diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c
index 43c9f8df9509..3b19bc41a60b 100644
--- a/drivers/i2c/busses/i2c-nforce2.c
+++ b/drivers/i2c/busses/i2c-nforce2.c
@@ -51,6 +51,7 @@
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/dmi.h>
+#include <linux/acpi.h>
#include <asm/io.h>
MODULE_LICENSE("GPL");
@@ -124,6 +125,20 @@ static struct dmi_system_id __devinitdata nforce2_dmi_blacklist2[] = {
static struct pci_driver nforce2_driver;
+/* For multiplexing support, we need a global reference to the 1st
+ SMBus channel */
+#if defined CONFIG_I2C_NFORCE2_S4985 || defined CONFIG_I2C_NFORCE2_S4985_MODULE
+struct i2c_adapter *nforce2_smbus;
+EXPORT_SYMBOL_GPL(nforce2_smbus);
+
+static void nforce2_set_reference(struct i2c_adapter *adap)
+{
+ nforce2_smbus = adap;
+}
+#else
+static inline void nforce2_set_reference(struct i2c_adapter *adap) { }
+#endif
+
static void nforce2_abort(struct i2c_adapter *adap)
{
struct nforce2_smbus *smbus = adap->algo_data;
@@ -158,16 +173,16 @@ static int nforce2_check_status(struct i2c_adapter *adap)
dev_dbg(&adap->dev, "SMBus Timeout!\n");
if (smbus->can_abort)
nforce2_abort(adap);
- return -1;
+ return -ETIMEDOUT;
}
if (!(temp & NVIDIA_SMB_STS_DONE) || (temp & NVIDIA_SMB_STS_STATUS)) {
dev_dbg(&adap->dev, "Transaction failed (0x%02x)!\n", temp);
- return -1;
+ return -EIO;
}
return 0;
}
-/* Return -1 on error */
+/* Return negative errno on error */
static s32 nforce2_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data)
@@ -175,7 +190,7 @@ static s32 nforce2_access(struct i2c_adapter * adap, u16 addr,
struct nforce2_smbus *smbus = adap->algo_data;
unsigned char protocol, pec;
u8 len;
- int i;
+ int i, status;
protocol = (read_write == I2C_SMBUS_READ) ? NVIDIA_SMB_PRTCL_READ :
NVIDIA_SMB_PRTCL_WRITE;
@@ -219,7 +234,7 @@ static s32 nforce2_access(struct i2c_adapter * adap, u16 addr,
"Transaction failed "
"(requested block size: %d)\n",
len);
- return -1;
+ return -EINVAL;
}
outb_p(len, NVIDIA_SMB_BCNT);
for (i = 0; i < I2C_SMBUS_BLOCK_MAX; i++)
@@ -231,14 +246,15 @@ static s32 nforce2_access(struct i2c_adapter * adap, u16 addr,
default:
dev_err(&adap->dev, "Unsupported transaction %d\n", size);
- return -1;
+ return -EOPNOTSUPP;
}
outb_p((addr & 0x7f) << 1, NVIDIA_SMB_ADDR);
outb_p(protocol, NVIDIA_SMB_PRTCL);
- if (nforce2_check_status(adap))
- return -1;
+ status = nforce2_check_status(adap);
+ if (status)
+ return status;
if (read_write == I2C_SMBUS_WRITE)
return 0;
@@ -260,7 +276,7 @@ static s32 nforce2_access(struct i2c_adapter * adap, u16 addr,
dev_err(&adap->dev, "Transaction failed "
"(received block size: 0x%02x)\n",
len);
- return -1;
+ return -EPROTO;
}
for (i = 0; i < len; i++)
data->block[i+1] = inb_p(NVIDIA_SMB_DATA + i);
@@ -321,21 +337,26 @@ static int __devinit nforce2_probe_smb (struct pci_dev *dev, int bar,
!= PCIBIOS_SUCCESSFUL) {
dev_err(&dev->dev, "Error reading PCI config for %s\n",
name);
- return -1;
+ return -EIO;
}
smbus->base = iobase & PCI_BASE_ADDRESS_IO_MASK;
smbus->size = 64;
}
+ error = acpi_check_region(smbus->base, smbus->size,
+ nforce2_driver.name);
+ if (error)
+ return -1;
+
if (!request_region(smbus->base, smbus->size, nforce2_driver.name)) {
dev_err(&smbus->adapter.dev, "Error requesting region %02x .. %02X for %s\n",
smbus->base, smbus->base+smbus->size-1, name);
- return -1;
+ return -EBUSY;
}
smbus->adapter.owner = THIS_MODULE;
smbus->adapter.id = I2C_HW_SMBUS_NFORCE2;
- smbus->adapter.class = I2C_CLASS_HWMON;
+ smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
smbus->adapter.algo = &smbus_algorithm;
smbus->adapter.algo_data = smbus;
smbus->adapter.dev.parent = &dev->dev;
@@ -346,7 +367,7 @@ static int __devinit nforce2_probe_smb (struct pci_dev *dev, int bar,
if (error) {
dev_err(&smbus->adapter.dev, "Failed to register adapter.\n");
release_region(smbus->base, smbus->size);
- return -1;
+ return error;
}
dev_info(&smbus->adapter.dev, "nForce2 SMBus adapter at %#x\n", smbus->base);
return 0;
@@ -398,6 +419,7 @@ static int __devinit nforce2_probe(struct pci_dev *dev, const struct pci_device_
return -ENODEV;
}
+ nforce2_set_reference(&smbuses[0].adapter);
return 0;
}
@@ -406,6 +428,7 @@ static void __devexit nforce2_remove(struct pci_dev *dev)
{
struct nforce2_smbus *smbuses = (void*) pci_get_drvdata(dev);
+ nforce2_set_reference(NULL);
if (smbuses[0].base) {
i2c_del_adapter(&smbuses[0].adapter);
release_region(smbuses[0].base, smbuses[0].size);
diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c
index f145692cbb76..51ca79bf6480 100644
--- a/drivers/i2c/busses/i2c-ocores.c
+++ b/drivers/i2c/busses/i2c-ocores.c
@@ -205,7 +205,7 @@ static const struct i2c_algorithm ocores_algorithm = {
static struct i2c_adapter ocores_adapter = {
.owner = THIS_MODULE,
.name = "i2c-ocores",
- .class = I2C_CLASS_HWMON,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
.algo = &ocores_algorithm,
};
diff --git a/drivers/i2c/busses/i2c-pasemi.c b/drivers/i2c/busses/i2c-pasemi.c
index 1603c81e39d4..adf0fbb902f0 100644
--- a/drivers/i2c/busses/i2c-pasemi.c
+++ b/drivers/i2c/busses/i2c-pasemi.c
@@ -365,7 +365,7 @@ static int __devinit pasemi_smb_probe(struct pci_dev *dev,
smbus->adapter.owner = THIS_MODULE;
snprintf(smbus->adapter.name, sizeof(smbus->adapter.name),
"PA Semi SMBus adapter at 0x%lx", smbus->base);
- smbus->adapter.class = I2C_CLASS_HWMON;
+ smbus->adapter.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
smbus->adapter.algo = &smbus_algorithm;
smbus->adapter.algo_data = smbus;
smbus->adapter.nr = PCI_FUNC(dev->devfn);
diff --git a/drivers/i2c/busses/i2c-pca-platform.c b/drivers/i2c/busses/i2c-pca-platform.c
index 9d75f51e8f0e..6bb15ad0a6b6 100644
--- a/drivers/i2c/busses/i2c-pca-platform.c
+++ b/drivers/i2c/busses/i2c-pca-platform.c
@@ -163,7 +163,7 @@ static int __devinit i2c_pca_pf_probe(struct platform_device *pdev)
i2c->reg_base = ioremap(res->start, res_len(res));
if (!i2c->reg_base) {
- ret = -EIO;
+ ret = -ENOMEM;
goto e_remap;
}
i2c->io_base = res->start;
diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
index ac9165968587..924e84a53488 100644
--- a/drivers/i2c/busses/i2c-piix4.c
+++ b/drivers/i2c/busses/i2c-piix4.c
@@ -39,16 +39,10 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/dmi.h>
+#include <linux/acpi.h>
#include <asm/io.h>
-struct sd {
- const unsigned short mfr;
- const unsigned short dev;
- const unsigned char fn;
- const char *name;
-};
-
/* PIIX4 SMBus address offsets */
#define SMBHSTSTS (0 + piix4_smba)
#define SMBHSLVSTS (1 + piix4_smba)
@@ -101,8 +95,6 @@ MODULE_PARM_DESC(force_addr,
"Forcibly enable the PIIX4 at the given address. "
"EXTREMELY DANGEROUS!");
-static int piix4_transaction(void);
-
static unsigned short piix4_smba;
static int srvrworks_csb5_delay;
static struct pci_driver piix4_driver;
@@ -141,8 +133,6 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev,
{
unsigned char temp;
- dev_info(&PIIX4_dev->dev, "Found %s device\n", pci_name(PIIX4_dev));
-
if ((PIIX4_dev->vendor == PCI_VENDOR_ID_SERVERWORKS) &&
(PIIX4_dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5))
srvrworks_csb5_delay = 1;
@@ -172,17 +162,20 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev,
pci_read_config_word(PIIX4_dev, SMBBA, &piix4_smba);
piix4_smba &= 0xfff0;
if(piix4_smba == 0) {
- dev_err(&PIIX4_dev->dev, "SMB base address "
+ dev_err(&PIIX4_dev->dev, "SMBus base address "
"uninitialized - upgrade BIOS or use "
"force_addr=0xaddr\n");
return -ENODEV;
}
}
+ if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name))
+ return -EBUSY;
+
if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) {
- dev_err(&PIIX4_dev->dev, "SMB region 0x%x already in use!\n",
+ dev_err(&PIIX4_dev->dev, "SMBus region 0x%x already in use!\n",
piix4_smba);
- return -ENODEV;
+ return -EBUSY;
}
pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp);
@@ -228,13 +221,13 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev,
"(or code out of date)!\n");
pci_read_config_byte(PIIX4_dev, SMBREV, &temp);
- dev_dbg(&PIIX4_dev->dev, "SMBREV = 0x%X\n", temp);
- dev_dbg(&PIIX4_dev->dev, "SMBA = 0x%X\n", piix4_smba);
+ dev_info(&PIIX4_dev->dev,
+ "SMBus Host Controller at 0x%x, revision %d\n",
+ piix4_smba, temp);
return 0;
}
-/* Another internally used function */
static int piix4_transaction(void)
{
int temp;
@@ -253,7 +246,7 @@ static int piix4_transaction(void)
outb_p(temp, SMBHSTSTS);
if ((temp = inb_p(SMBHSTSTS)) != 0x00) {
dev_err(&piix4_adapter.dev, "Failed! (%02x)\n", temp);
- return -1;
+ return -EBUSY;
} else {
dev_dbg(&piix4_adapter.dev, "Successful!\n");
}
@@ -275,23 +268,23 @@ static int piix4_transaction(void)
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
dev_err(&piix4_adapter.dev, "SMBus Timeout!\n");
- result = -1;
+ result = -ETIMEDOUT;
}
if (temp & 0x10) {
- result = -1;
+ result = -EIO;
dev_err(&piix4_adapter.dev, "Error: Failed bus transaction\n");
}
if (temp & 0x08) {
- result = -1;
+ result = -EIO;
dev_dbg(&piix4_adapter.dev, "Bus collision! SMBus may be "
"locked until next hard reset. (sorry!)\n");
/* Clock stops and slave is stuck in mid-transmission */
}
if (temp & 0x04) {
- result = -1;
+ result = -ENXIO;
dev_dbg(&piix4_adapter.dev, "Error: no response!\n");
}
@@ -309,31 +302,29 @@ static int piix4_transaction(void)
return result;
}
-/* Return -1 on error. */
+/* Return negative errno on error. */
static s32 piix4_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data)
{
int i, len;
+ int status;
switch (size) {
- case I2C_SMBUS_PROC_CALL:
- dev_err(&adap->dev, "I2C_SMBUS_PROC_CALL not supported!\n");
- return -1;
case I2C_SMBUS_QUICK:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p((addr << 1) | read_write,
SMBHSTADD);
size = PIIX4_QUICK;
break;
case I2C_SMBUS_BYTE:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p((addr << 1) | read_write,
SMBHSTADD);
if (read_write == I2C_SMBUS_WRITE)
outb_p(command, SMBHSTCMD);
size = PIIX4_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p((addr << 1) | read_write,
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE)
@@ -341,7 +332,7 @@ static s32 piix4_access(struct i2c_adapter * adap, u16 addr,
size = PIIX4_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p((addr << 1) | read_write,
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE) {
@@ -351,15 +342,13 @@ static s32 piix4_access(struct i2c_adapter * adap, u16 addr,
size = PIIX4_WORD_DATA;
break;
case I2C_SMBUS_BLOCK_DATA:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
+ outb_p((addr << 1) | read_write,
SMBHSTADD);
outb_p(command, SMBHSTCMD);
if (read_write == I2C_SMBUS_WRITE) {
len = data->block[0];
- if (len < 0)
- len = 0;
- if (len > 32)
- len = 32;
+ if (len == 0 || len > I2C_SMBUS_BLOCK_MAX)
+ return -EINVAL;
outb_p(len, SMBHSTDAT0);
i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */
for (i = 1; i <= len; i++)
@@ -367,12 +356,16 @@ static s32 piix4_access(struct i2c_adapter * adap, u16 addr,
}
size = PIIX4_BLOCK_DATA;
break;
+ default:
+ dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
+ return -EOPNOTSUPP;
}
outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT);
- if (piix4_transaction()) /* Error in transaction */
- return -1;
+ status = piix4_transaction();
+ if (status)
+ return status;
if ((read_write == I2C_SMBUS_WRITE) || (size == PIIX4_QUICK))
return 0;
@@ -388,6 +381,8 @@ static s32 piix4_access(struct i2c_adapter * adap, u16 addr,
break;
case PIIX4_BLOCK_DATA:
data->block[0] = inb_p(SMBHSTDAT0);
+ if (data->block[0] == 0 || data->block[0] > I2C_SMBUS_BLOCK_MAX)
+ return -EPROTO;
i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */
for (i = 1; i <= data->block[0]; i++)
data->block[i] = inb_p(SMBBLKDAT);
@@ -411,7 +406,7 @@ static const struct i2c_algorithm smbus_algorithm = {
static struct i2c_adapter piix4_adapter = {
.owner = THIS_MODULE,
.id = I2C_HW_SMBUS_PIIX4,
- .class = I2C_CLASS_HWMON,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
.algo = &smbus_algorithm,
};
diff --git a/drivers/i2c/busses/i2c-pmcmsp.c b/drivers/i2c/busses/i2c-pmcmsp.c
index 63b3e2c11cff..dcf2045b5222 100644
--- a/drivers/i2c/busses/i2c-pmcmsp.c
+++ b/drivers/i2c/busses/i2c-pmcmsp.c
@@ -622,7 +622,7 @@ static struct i2c_algorithm pmcmsptwi_algo = {
static struct i2c_adapter pmcmsptwi_adapter = {
.owner = THIS_MODULE,
- .class = I2C_CLASS_HWMON,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
.algo = &pmcmsptwi_algo,
.name = DRV_NAME,
};
diff --git a/drivers/i2c/busses/i2c-prosavage.c b/drivers/i2c/busses/i2c-prosavage.c
deleted file mode 100644
index 07c1f1e27df1..000000000000
--- a/drivers/i2c/busses/i2c-prosavage.c
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * kernel/busses/i2c-prosavage.c
- *
- * i2c bus driver for S3/VIA 8365/8375 graphics processor.
- * Copyright (c) 2003 Henk Vergonet <henk@god.dyndns.org>
- * Based on code written by:
- * Frodo Looijaard <frodol@dds.nl>,
- * Philip Edelbrock <phil@netroedge.com>,
- * Ralph Metzler <rjkm@thp.uni-koeln.de>, and
- * Mark D. Studebaker <mdsxyz123@yahoo.com>
- * Simon Vogl
- * and others
- *
- * Please read the lm_sensors documentation for details on use.
- *
- * 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.
- *
- */
-/* 18-05-2003 HVE - created
- * 14-06-2003 HVE - adapted for lm_sensors2
- * 17-06-2003 HVE - linux 2.5.xx compatible
- * 18-06-2003 HVE - codingstyle
- * 21-06-2003 HVE - compatibility lm_sensors2 and linux 2.5.xx
- * codingstyle, mmio enabled
- *
- * This driver interfaces to the I2C bus of the VIA north bridge embedded
- * ProSavage4/8 devices. Usefull for gaining access to the TV Encoder chips.
- *
- * Graphics cores:
- * S3/VIA KM266/VT8375 aka ProSavage8
- * S3/VIA KM133/VT8365 aka Savage4
- *
- * Two serial busses are implemented:
- * SERIAL1 - I2C serial communications interface
- * SERIAL2 - DDC2 monitor communications interface
- *
- * Tested on a FX41 mainboard, see http://www.shuttle.com
- *
- *
- * TODO:
- * - integration with prosavage framebuffer device
- * (Additional documentation needed :(
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-#include <asm/io.h>
-
-/*
- * driver configuration
- */
-#define MAX_BUSSES 2
-
-struct s_i2c_bus {
- void __iomem *mmvga;
- int i2c_reg;
- int adap_ok;
- struct i2c_adapter adap;
- struct i2c_algo_bit_data algo;
-};
-
-struct s_i2c_chip {
- void __iomem *mmio;
- struct s_i2c_bus i2c_bus[MAX_BUSSES];
-};
-
-
-/*
- * i2c configuration
- */
-#define CYCLE_DELAY 10
-#define TIMEOUT (HZ / 2)
-
-
-/*
- * S3/VIA 8365/8375 registers
- */
-#define VGA_CR_IX 0x3d4
-#define VGA_CR_DATA 0x3d5
-
-#define CR_SERIAL1 0xa0 /* I2C serial communications interface */
-#define MM_SERIAL1 0xff20
-#define CR_SERIAL2 0xb1 /* DDC2 monitor communications interface */
-
-/* based on vt8365 documentation */
-#define I2C_ENAB 0x10
-#define I2C_SCL_OUT 0x01
-#define I2C_SDA_OUT 0x02
-#define I2C_SCL_IN 0x04
-#define I2C_SDA_IN 0x08
-
-#define SET_CR_IX(p, val) writeb((val), (p)->mmvga + VGA_CR_IX)
-#define SET_CR_DATA(p, val) writeb((val), (p)->mmvga + VGA_CR_DATA)
-#define GET_CR_DATA(p) readb((p)->mmvga + VGA_CR_DATA)
-
-
-/*
- * Serial bus line handling
- *
- * serial communications register as parameter in private data
- *
- * TODO: locks with other code sections accessing video registers?
- */
-static void bit_s3via_setscl(void *bus, int val)
-{
- struct s_i2c_bus *p = (struct s_i2c_bus *)bus;
- unsigned int r;
-
- SET_CR_IX(p, p->i2c_reg);
- r = GET_CR_DATA(p);
- r |= I2C_ENAB;
- if (val) {
- r |= I2C_SCL_OUT;
- } else {
- r &= ~I2C_SCL_OUT;
- }
- SET_CR_DATA(p, r);
-}
-
-static void bit_s3via_setsda(void *bus, int val)
-{
- struct s_i2c_bus *p = (struct s_i2c_bus *)bus;
- unsigned int r;
-
- SET_CR_IX(p, p->i2c_reg);
- r = GET_CR_DATA(p);
- r |= I2C_ENAB;
- if (val) {
- r |= I2C_SDA_OUT;
- } else {
- r &= ~I2C_SDA_OUT;
- }
- SET_CR_DATA(p, r);
-}
-
-static int bit_s3via_getscl(void *bus)
-{
- struct s_i2c_bus *p = (struct s_i2c_bus *)bus;
-
- SET_CR_IX(p, p->i2c_reg);
- return (0 != (GET_CR_DATA(p) & I2C_SCL_IN));
-}
-
-static int bit_s3via_getsda(void *bus)
-{
- struct s_i2c_bus *p = (struct s_i2c_bus *)bus;
-
- SET_CR_IX(p, p->i2c_reg);
- return (0 != (GET_CR_DATA(p) & I2C_SDA_IN));
-}
-
-
-/*
- * adapter initialisation
- */
-static int i2c_register_bus(struct pci_dev *dev, struct s_i2c_bus *p, void __iomem *mmvga, u32 i2c_reg)
-{
- int ret;
- p->adap.owner = THIS_MODULE;
- p->adap.id = I2C_HW_B_S3VIA;
- p->adap.algo_data = &p->algo;
- p->adap.dev.parent = &dev->dev;
- p->algo.setsda = bit_s3via_setsda;
- p->algo.setscl = bit_s3via_setscl;
- p->algo.getsda = bit_s3via_getsda;
- p->algo.getscl = bit_s3via_getscl;
- p->algo.udelay = CYCLE_DELAY;
- p->algo.timeout = TIMEOUT;
- p->algo.data = p;
- p->mmvga = mmvga;
- p->i2c_reg = i2c_reg;
-
- ret = i2c_bit_add_bus(&p->adap);
- if (ret) {
- return ret;
- }
-
- p->adap_ok = 1;
- return 0;
-}
-
-
-/*
- * Cleanup stuff
- */
-static void prosavage_remove(struct pci_dev *dev)
-{
- struct s_i2c_chip *chip;
- int i, ret;
-
- chip = (struct s_i2c_chip *)pci_get_drvdata(dev);
-
- if (!chip) {
- return;
- }
- for (i = MAX_BUSSES - 1; i >= 0; i--) {
- if (chip->i2c_bus[i].adap_ok == 0)
- continue;
-
- ret = i2c_del_adapter(&chip->i2c_bus[i].adap);
- if (ret) {
- dev_err(&dev->dev, "%s not removed\n",
- chip->i2c_bus[i].adap.name);
- }
- }
- if (chip->mmio) {
- iounmap(chip->mmio);
- }
- kfree(chip);
-}
-
-
-/*
- * Detect chip and initialize it
- */
-static int __devinit prosavage_probe(struct pci_dev *dev, const struct pci_device_id *id)
-{
- int ret;
- unsigned long base, len;
- struct s_i2c_chip *chip;
- struct s_i2c_bus *bus;
-
- pci_set_drvdata(dev, kzalloc(sizeof(struct s_i2c_chip), GFP_KERNEL));
- chip = (struct s_i2c_chip *)pci_get_drvdata(dev);
- if (chip == NULL) {
- return -ENOMEM;
- }
-
- base = dev->resource[0].start & PCI_BASE_ADDRESS_MEM_MASK;
- len = dev->resource[0].end - base + 1;
- chip->mmio = ioremap_nocache(base, len);
-
- if (chip->mmio == NULL) {
- dev_err(&dev->dev, "ioremap failed\n");
- prosavage_remove(dev);
- return -ENODEV;
- }
-
-
- /*
- * Chip initialisation
- */
- /* Unlock Extended IO Space ??? */
-
-
- /*
- * i2c bus registration
- */
- bus = &chip->i2c_bus[0];
- snprintf(bus->adap.name, sizeof(bus->adap.name),
- "ProSavage I2C bus at %02x:%02x.%x",
- dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
- ret = i2c_register_bus(dev, bus, chip->mmio + 0x8000, CR_SERIAL1);
- if (ret) {
- goto err_adap;
- }
- /*
- * ddc bus registration
- */
- bus = &chip->i2c_bus[1];
- snprintf(bus->adap.name, sizeof(bus->adap.name),
- "ProSavage DDC bus at %02x:%02x.%x",
- dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
- ret = i2c_register_bus(dev, bus, chip->mmio + 0x8000, CR_SERIAL2);
- if (ret) {
- goto err_adap;
- }
- return 0;
-err_adap:
- dev_err(&dev->dev, "%s failed\n", bus->adap.name);
- prosavage_remove(dev);
- return ret;
-}
-
-
-/*
- * Data for PCI driver interface
- */
-static struct pci_device_id prosavage_pci_tbl[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_SAVAGE4) },
- { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_DEVICE_ID_S3_PROSAVAGE8) },
- { 0, },
-};
-
-MODULE_DEVICE_TABLE (pci, prosavage_pci_tbl);
-
-static struct pci_driver prosavage_driver = {
- .name = "prosavage_smbus",
- .id_table = prosavage_pci_tbl,
- .probe = prosavage_probe,
- .remove = prosavage_remove,
-};
-
-static int __init i2c_prosavage_init(void)
-{
- return pci_register_driver(&prosavage_driver);
-}
-
-static void __exit i2c_prosavage_exit(void)
-{
- pci_unregister_driver(&prosavage_driver);
-}
-
-MODULE_DEVICE_TABLE(pci, prosavage_pci_tbl);
-MODULE_AUTHOR("Henk Vergonet");
-MODULE_DESCRIPTION("ProSavage VIA 8365/8375 smbus driver");
-MODULE_LICENSE("GPL");
-
-module_init (i2c_prosavage_init);
-module_exit (i2c_prosavage_exit);
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index 1305ef190fc1..fd7b2308a0b3 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -591,7 +591,7 @@ static struct s3c24xx_i2c s3c24xx_i2c = {
.owner = THIS_MODULE,
.algo = &s3c24xx_i2c_algorithm,
.retries = 2,
- .class = I2C_CLASS_HWMON,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
},
};
diff --git a/drivers/i2c/busses/i2c-savage4.c b/drivers/i2c/busses/i2c-savage4.c
deleted file mode 100644
index 8adf4abaa035..000000000000
--- a/drivers/i2c/busses/i2c-savage4.c
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- i2c-savage4.c - Part of lm_sensors, Linux kernel modules for hardware
- monitoring
- Copyright (C) 1998-2003 The LM Sensors Team
- Alexander Wold <awold@bigfoot.com>
- Mark D. Studebaker <mdsxyz123@yahoo.com>
-
- Based on i2c-voodoo3.c.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-/* This interfaces to the I2C bus of the Savage4 to gain access to
- the BT869 and possibly other I2C devices. The DDC bus is not
- yet supported because its register is not memory-mapped.
-*/
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/i2c.h>
-#include <linux/i2c-algo-bit.h>
-#include <asm/io.h>
-
-/* device IDs */
-#define PCI_CHIP_SAVAGE4 0x8A22
-#define PCI_CHIP_SAVAGE2000 0x9102
-
-#define REG 0xff20 /* Serial Port 1 Register */
-
-/* bit locations in the register */
-#define I2C_ENAB 0x00000020
-#define I2C_SCL_OUT 0x00000001
-#define I2C_SDA_OUT 0x00000002
-#define I2C_SCL_IN 0x00000008
-#define I2C_SDA_IN 0x00000010
-
-/* delays */
-#define CYCLE_DELAY 10
-#define TIMEOUT (HZ / 2)
-
-
-static void __iomem *ioaddr;
-
-/* The sav GPIO registers don't have individual masks for each bit
- so we always have to read before writing. */
-
-static void bit_savi2c_setscl(void *data, int val)
-{
- unsigned int r;
- r = readl(ioaddr + REG);
- if(val)
- r |= I2C_SCL_OUT;
- else
- r &= ~I2C_SCL_OUT;
- writel(r, ioaddr + REG);
- readl(ioaddr + REG); /* flush posted write */
-}
-
-static void bit_savi2c_setsda(void *data, int val)
-{
- unsigned int r;
- r = readl(ioaddr + REG);
- if(val)
- r |= I2C_SDA_OUT;
- else
- r &= ~I2C_SDA_OUT;
- writel(r, ioaddr + REG);
- readl(ioaddr + REG); /* flush posted write */
-}
-
-/* The GPIO pins are open drain, so the pins always remain outputs.
- We rely on the i2c-algo-bit routines to set the pins high before
- reading the input from other chips. */
-
-static int bit_savi2c_getscl(void *data)
-{
- return (0 != (readl(ioaddr + REG) & I2C_SCL_IN));
-}
-
-static int bit_savi2c_getsda(void *data)
-{
- return (0 != (readl(ioaddr + REG) & I2C_SDA_IN));
-}
-
-/* Configures the chip */
-
-static int config_s4(struct pci_dev *dev)
-{
- unsigned long cadr;
-
- /* map memory */
- cadr = dev->resource[0].start;
- cadr &= PCI_BASE_ADDRESS_MEM_MASK;
- ioaddr = ioremap_nocache(cadr, 0x0080000);
- if (ioaddr) {
- /* writel(0x8160, ioaddr + REG2); */
- writel(0x00000020, ioaddr + REG);
- dev_info(&dev->dev, "Using Savage4 at %p\n", ioaddr);
- return 0;
- }
- return -ENODEV;
-}
-
-static struct i2c_algo_bit_data sav_i2c_bit_data = {
- .setsda = bit_savi2c_setsda,
- .setscl = bit_savi2c_setscl,
- .getsda = bit_savi2c_getsda,
- .getscl = bit_savi2c_getscl,
- .udelay = CYCLE_DELAY,
- .timeout = TIMEOUT
-};
-
-static struct i2c_adapter savage4_i2c_adapter = {
- .owner = THIS_MODULE,
- .id = I2C_HW_B_SAVAGE,
- .name = "I2C Savage4 adapter",
- .algo_data = &sav_i2c_bit_data,
-};
-
-static struct pci_device_id savage4_ids[] __devinitdata = {
- { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE4) },
- { PCI_DEVICE(PCI_VENDOR_ID_S3, PCI_CHIP_SAVAGE2000) },
- { 0, }
-};
-
-MODULE_DEVICE_TABLE (pci, savage4_ids);
-
-static int __devinit savage4_probe(struct pci_dev *dev, const struct pci_device_id *id)
-{
- int retval;
-
- retval = config_s4(dev);
- if (retval)
- return retval;
-
- /* set up the sysfs linkage to our parent device */
- savage4_i2c_adapter.dev.parent = &dev->dev;
-
- return i2c_bit_add_bus(&savage4_i2c_adapter);
-}
-
-static void __devexit savage4_remove(struct pci_dev *dev)
-{
- i2c_del_adapter(&savage4_i2c_adapter);
- iounmap(ioaddr);
-}
-
-static struct pci_driver savage4_driver = {
- .name = "savage4_smbus",
- .id_table = savage4_ids,
- .probe = savage4_probe,
- .remove = __devexit_p(savage4_remove),
-};
-
-static int __init i2c_savage4_init(void)
-{
- return pci_register_driver(&savage4_driver);
-}
-
-static void __exit i2c_savage4_exit(void)
-{
- pci_unregister_driver(&savage4_driver);
-}
-
-MODULE_AUTHOR("Alexander Wold <awold@bigfoot.com> "
- "and Mark D. Studebaker <mdsxyz123@yahoo.com>");
-MODULE_DESCRIPTION("Savage4 I2C/SMBus driver");
-MODULE_LICENSE("GPL");
-
-module_init(i2c_savage4_init);
-module_exit(i2c_savage4_exit);
diff --git a/drivers/i2c/busses/i2c-sibyte.c b/drivers/i2c/busses/i2c-sibyte.c
index 114634da6c6e..ac8822e7a5b4 100644
--- a/drivers/i2c/busses/i2c-sibyte.c
+++ b/drivers/i2c/busses/i2c-sibyte.c
@@ -156,7 +156,7 @@ static struct i2c_adapter sibyte_board_adapter[2] = {
{
.owner = THIS_MODULE,
.id = I2C_HW_SIBYTE,
- .class = I2C_CLASS_HWMON,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
.algo = NULL,
.algo_data = &sibyte_board_data[0],
.name = "SiByte SMBus 0",
@@ -164,7 +164,7 @@ static struct i2c_adapter sibyte_board_adapter[2] = {
{
.owner = THIS_MODULE,
.id = I2C_HW_SIBYTE,
- .class = I2C_CLASS_HWMON,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
.algo = NULL,
.algo_data = &sibyte_board_data[1],
.name = "SiByte SMBus 1",
diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c
index 9ca8f9155f95..3c8e0e1bd24f 100644
--- a/drivers/i2c/busses/i2c-sis5595.c
+++ b/drivers/i2c/busses/i2c-sis5595.c
@@ -62,6 +62,7 @@
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/i2c.h>
+#include <linux/acpi.h>
#include <asm/io.h>
static int blacklist[] = {
@@ -174,6 +175,11 @@ static int sis5595_setup(struct pci_dev *SIS5595_dev)
/* NB: We grab just the two SMBus registers here, but this may still
* interfere with ACPI :-( */
+ retval = acpi_check_region(sis5595_base + SMB_INDEX, 2,
+ sis5595_driver.name);
+ if (retval)
+ return retval;
+
if (!request_region(sis5595_base + SMB_INDEX, 2,
sis5595_driver.name)) {
dev_err(&SIS5595_dev->dev, "SMBus registers 0x%04x-0x%04x already in use!\n",
@@ -236,7 +242,7 @@ static int sis5595_transaction(struct i2c_adapter *adap)
sis5595_write(SMB_STS_HI, temp >> 8);
if ((temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) != 0x00) {
dev_dbg(&adap->dev, "Failed! (%02x)\n", temp);
- return -1;
+ return -EBUSY;
} else {
dev_dbg(&adap->dev, "Successful!\n");
}
@@ -254,19 +260,19 @@ static int sis5595_transaction(struct i2c_adapter *adap)
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
dev_dbg(&adap->dev, "SMBus Timeout!\n");
- result = -1;
+ result = -ETIMEDOUT;
}
if (temp & 0x10) {
dev_dbg(&adap->dev, "Error: Failed bus transaction\n");
- result = -1;
+ result = -ENXIO;
}
if (temp & 0x20) {
dev_err(&adap->dev, "Bus collision! SMBus may be locked until "
"next hard reset (or not...)\n");
/* Clock stops and slave is stuck in mid-transmission */
- result = -1;
+ result = -EIO;
}
temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8);
@@ -282,11 +288,13 @@ static int sis5595_transaction(struct i2c_adapter *adap)
return result;
}
-/* Return -1 on error. */
+/* Return negative errno on error. */
static s32 sis5595_access(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data)
{
+ int status;
+
switch (size) {
case I2C_SMBUS_QUICK:
sis5595_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
@@ -318,13 +326,14 @@ static s32 sis5595_access(struct i2c_adapter *adap, u16 addr,
break;
default:
dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
- return -1;
+ return -EOPNOTSUPP;
}
sis5595_write(SMB_CTL_LO, ((size & 0x0E)));
- if (sis5595_transaction(adap))
- return -1;
+ status = sis5595_transaction(adap);
+ if (status)
+ return status;
if ((size != SIS5595_PROC_CALL) &&
((read_write == I2C_SMBUS_WRITE) || (size == SIS5595_QUICK)))
@@ -359,7 +368,7 @@ static const struct i2c_algorithm smbus_algorithm = {
static struct i2c_adapter sis5595_adapter = {
.owner = THIS_MODULE,
.id = I2C_HW_SMBUS_SIS5595,
- .class = I2C_CLASS_HWMON,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
.algo = &smbus_algorithm,
};
diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c
index 3765dd7f450f..72c29a650b22 100644
--- a/drivers/i2c/busses/i2c-sis630.c
+++ b/drivers/i2c/busses/i2c-sis630.c
@@ -55,6 +55,7 @@
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/i2c.h>
+#include <linux/acpi.h>
#include <asm/io.h>
/* SIS630 SMBus registers */
@@ -134,7 +135,7 @@ static int sis630_transaction_start(struct i2c_adapter *adap, int size, u8 *oldc
if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) {
dev_dbg(&adap->dev, "Failed! (%02x)\n", temp);
- return -1;
+ return -EBUSY;
} else {
dev_dbg(&adap->dev, "Successful!\n");
}
@@ -177,17 +178,17 @@ static int sis630_transaction_wait(struct i2c_adapter *adap, int size)
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
dev_dbg(&adap->dev, "SMBus Timeout!\n");
- result = -1;
+ result = -ETIMEDOUT;
}
if (temp & 0x02) {
dev_dbg(&adap->dev, "Error: Failed bus transaction\n");
- result = -1;
+ result = -ENXIO;
}
if (temp & 0x04) {
dev_err(&adap->dev, "Bus collision!\n");
- result = -1;
+ result = -EIO;
/*
TBD: Datasheet say:
the software should clear this bit and restart SMBUS operation.
@@ -250,8 +251,10 @@ static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *dat
if (i==8 || (len<8 && i==len)) {
dev_dbg(&adap->dev, "start trans len=%d i=%d\n",len ,i);
/* first transaction */
- if (sis630_transaction_start(adap, SIS630_BLOCK_DATA, &oldclock))
- return -1;
+ rc = sis630_transaction_start(adap,
+ SIS630_BLOCK_DATA, &oldclock);
+ if (rc)
+ return rc;
}
else if ((i-1)%8 == 7 || i==len) {
dev_dbg(&adap->dev, "trans_wait len=%d i=%d\n",len,i);
@@ -264,9 +267,10 @@ static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *dat
*/
sis630_write(SMB_STS,0x10);
}
- if (sis630_transaction_wait(adap, SIS630_BLOCK_DATA)) {
+ rc = sis630_transaction_wait(adap,
+ SIS630_BLOCK_DATA);
+ if (rc) {
dev_dbg(&adap->dev, "trans_wait failed\n");
- rc = -1;
break;
}
}
@@ -275,13 +279,14 @@ static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *dat
else {
/* read request */
data->block[0] = len = 0;
- if (sis630_transaction_start(adap, SIS630_BLOCK_DATA, &oldclock)) {
- return -1;
- }
+ rc = sis630_transaction_start(adap,
+ SIS630_BLOCK_DATA, &oldclock);
+ if (rc)
+ return rc;
do {
- if (sis630_transaction_wait(adap, SIS630_BLOCK_DATA)) {
+ rc = sis630_transaction_wait(adap, SIS630_BLOCK_DATA);
+ if (rc) {
dev_dbg(&adap->dev, "trans_wait failed\n");
- rc = -1;
break;
}
/* if this first transaction then read byte count */
@@ -311,11 +316,13 @@ static int sis630_block_data(struct i2c_adapter *adap, union i2c_smbus_data *dat
return rc;
}
-/* Return -1 on error. */
+/* Return negative errno on error. */
static s32 sis630_access(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data)
{
+ int status;
+
switch (size) {
case I2C_SMBUS_QUICK:
sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01));
@@ -350,13 +357,14 @@ static s32 sis630_access(struct i2c_adapter *adap, u16 addr,
size = SIS630_BLOCK_DATA;
return sis630_block_data(adap, data, read_write);
default:
- printk("Unsupported I2C size\n");
- return -1;
- break;
+ dev_warn(&adap->dev, "Unsupported transaction %d\n",
+ size);
+ return -EOPNOTSUPP;
}
- if (sis630_transaction(adap, size))
- return -1;
+ status = sis630_transaction(adap, size);
+ if (status)
+ return status;
if ((size != SIS630_PCALL) &&
((read_write == I2C_SMBUS_WRITE) || (size == SIS630_QUICK))) {
@@ -372,9 +380,6 @@ static s32 sis630_access(struct i2c_adapter *adap, u16 addr,
case SIS630_WORD_DATA:
data->word = sis630_read(SMB_BYTE) + (sis630_read(SMB_BYTE + 1) << 8);
break;
- default:
- return -1;
- break;
}
return 0;
@@ -433,6 +438,11 @@ static int sis630_setup(struct pci_dev *sis630_dev)
dev_dbg(&sis630_dev->dev, "ACPI base at 0x%04x\n", acpi_base);
+ retval = acpi_check_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION,
+ sis630_driver.name);
+ if (retval)
+ goto exit;
+
/* Everything is happy, let's grab the memory and set things up. */
if (!request_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION,
sis630_driver.name)) {
@@ -458,7 +468,7 @@ static const struct i2c_algorithm smbus_algorithm = {
static struct i2c_adapter sis630_adapter = {
.owner = THIS_MODULE,
.id = I2C_HW_SMBUS_SIS630,
- .class = I2C_CLASS_HWMON,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
.algo = &smbus_algorithm,
};
diff --git a/drivers/i2c/busses/i2c-sis96x.c b/drivers/i2c/busses/i2c-sis96x.c
index dc235bb8e24d..848b37c97f7f 100644
--- a/drivers/i2c/busses/i2c-sis96x.c
+++ b/drivers/i2c/busses/i2c-sis96x.c
@@ -40,6 +40,7 @@
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/init.h>
+#include <linux/acpi.h>
#include <asm/io.h>
/* base address register in PCI config space */
@@ -111,7 +112,7 @@ static int sis96x_transaction(int size)
/* check it again */
if (((temp = sis96x_read(SMB_CNT)) & 0x03) != 0x00) {
dev_dbg(&sis96x_adapter.dev, "Failed (0x%02x)\n", temp);
- return -1;
+ return -EBUSY;
} else {
dev_dbg(&sis96x_adapter.dev, "Successful\n");
}
@@ -136,19 +137,19 @@ static int sis96x_transaction(int size)
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
dev_dbg(&sis96x_adapter.dev, "SMBus Timeout! (0x%02x)\n", temp);
- result = -1;
+ result = -ETIMEDOUT;
}
/* device error - probably missing ACK */
if (temp & 0x02) {
dev_dbg(&sis96x_adapter.dev, "Failed bus transaction!\n");
- result = -1;
+ result = -ENXIO;
}
/* bus collision */
if (temp & 0x04) {
dev_dbg(&sis96x_adapter.dev, "Bus collision!\n");
- result = -1;
+ result = -EIO;
}
/* Finish up by resetting the bus */
@@ -161,11 +162,12 @@ static int sis96x_transaction(int size)
return result;
}
-/* Return -1 on error. */
+/* Return negative errno on error. */
static s32 sis96x_access(struct i2c_adapter * adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data)
{
+ int status;
switch (size) {
case I2C_SMBUS_QUICK:
@@ -200,20 +202,14 @@ static s32 sis96x_access(struct i2c_adapter * adap, u16 addr,
SIS96x_PROC_CALL : SIS96x_WORD_DATA);
break;
- case I2C_SMBUS_BLOCK_DATA:
- /* TO DO: */
- dev_info(&adap->dev, "SMBus block not implemented!\n");
- return -1;
- break;
-
default:
- dev_info(&adap->dev, "Unsupported I2C size\n");
- return -1;
- break;
+ dev_warn(&adap->dev, "Unsupported transaction %d\n", size);
+ return -EOPNOTSUPP;
}
- if (sis96x_transaction(size))
- return -1;
+ status = sis96x_transaction(size);
+ if (status)
+ return status;
if ((size != SIS96x_PROC_CALL) &&
((read_write == I2C_SMBUS_WRITE) || (size == SIS96x_QUICK)))
@@ -249,7 +245,7 @@ static const struct i2c_algorithm smbus_algorithm = {
static struct i2c_adapter sis96x_adapter = {
.owner = THIS_MODULE,
.id = I2C_HW_SMBUS_SIS96X,
- .class = I2C_CLASS_HWMON,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
.algo = &smbus_algorithm,
};
@@ -286,6 +282,10 @@ static int __devinit sis96x_probe(struct pci_dev *dev,
dev_info(&dev->dev, "SiS96x SMBus base address: 0x%04x\n",
sis96x_smbus_base);
+ retval = acpi_check_resource_conflict(&dev->resource[SIS96x_BAR]);
+ if (retval)
+ return retval;
+
/* Everything is happy, let's grab the memory and set things up. */
if (!request_region(sis96x_smbus_base, SMB_IOSIZE,
sis96x_driver.name)) {
diff --git a/drivers/i2c/busses/i2c-stub.c b/drivers/i2c/busses/i2c-stub.c
index d08eeec53913..1b7b2af94036 100644
--- a/drivers/i2c/busses/i2c-stub.c
+++ b/drivers/i2c/busses/i2c-stub.c
@@ -43,7 +43,7 @@ struct stub_chip {
static struct stub_chip *stub_chips;
-/* Return -1 on error. */
+/* Return negative errno on error. */
static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags,
char read_write, u8 command, int size, union i2c_smbus_data * data)
{
@@ -120,7 +120,7 @@ static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags,
default:
dev_dbg(&adap->dev, "Unsupported I2C/SMBus command\n");
- ret = -1;
+ ret = -EOPNOTSUPP;
break;
} /* switch (size) */
@@ -140,7 +140,7 @@ static const struct i2c_algorithm smbus_algorithm = {
static struct i2c_adapter stub_adapter = {
.owner = THIS_MODULE,
- .class = I2C_CLASS_HWMON,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
.algo = &smbus_algorithm,
.name = "SMBus stub driver",
};
diff --git a/drivers/i2c/busses/i2c-taos-evm.c b/drivers/i2c/busses/i2c-taos-evm.c
index de9db49e54d9..224aa12ee7c8 100644
--- a/drivers/i2c/busses/i2c-taos-evm.c
+++ b/drivers/i2c/busses/i2c-taos-evm.c
@@ -96,9 +96,8 @@ static int taos_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
sprintf(p, "$%02X", command);
break;
default:
- dev_dbg(&adapter->dev, "Unsupported transaction size %d\n",
- size);
- return -EINVAL;
+ dev_warn(&adapter->dev, "Unsupported transaction %d\n", size);
+ return -EOPNOTSUPP;
}
/* Send the transaction to the TAOS EVM */
diff --git a/drivers/i2c/busses/i2c-via.c b/drivers/i2c/busses/i2c-via.c
index 61716f6b14dc..6517f8a6d911 100644
--- a/drivers/i2c/busses/i2c-via.c
+++ b/drivers/i2c/busses/i2c-via.c
@@ -87,7 +87,7 @@ static struct i2c_algo_bit_data bit_data = {
static struct i2c_adapter vt586b_adapter = {
.owner = THIS_MODULE,
.id = I2C_HW_B_VIA,
- .class = I2C_CLASS_HWMON,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
.name = "VIA i2c",
.algo_data = &bit_data,
};
diff --git a/drivers/i2c/busses/i2c-viapro.c b/drivers/i2c/busses/i2c-viapro.c
index 77b13d027f86..faef99560f23 100644
--- a/drivers/i2c/busses/i2c-viapro.c
+++ b/drivers/i2c/busses/i2c-viapro.c
@@ -50,6 +50,7 @@
#include <linux/ioport.h>
#include <linux/i2c.h>
#include <linux/init.h>
+#include <linux/acpi.h>
#include <asm/io.h>
static struct pci_dev *vt596_pdev;
@@ -152,7 +153,7 @@ static int vt596_transaction(u8 size)
if ((temp = inb_p(SMBHSTSTS)) & 0x1F) {
dev_err(&vt596_adapter.dev, "SMBus reset failed! "
"(0x%02x)\n", temp);
- return -1;
+ return -EBUSY;
}
}
@@ -167,24 +168,24 @@ static int vt596_transaction(u8 size)
/* If the SMBus is still busy, we give up */
if (timeout >= MAX_TIMEOUT) {
- result = -1;
+ result = -ETIMEDOUT;
dev_err(&vt596_adapter.dev, "SMBus timeout!\n");
}
if (temp & 0x10) {
- result = -1;
+ result = -EIO;
dev_err(&vt596_adapter.dev, "Transaction failed (0x%02x)\n",
size);
}
if (temp & 0x08) {
- result = -1;
+ result = -EIO;
dev_err(&vt596_adapter.dev, "SMBus collision!\n");
}
if (temp & 0x04) {
int read = inb_p(SMBHSTADD) & 0x01;
- result = -1;
+ result = -ENXIO;
/* The quick and receive byte commands are used to probe
for chips, so errors are expected, and we don't want
to frighten the user. */
@@ -202,12 +203,13 @@ static int vt596_transaction(u8 size)
return result;
}
-/* Return -1 on error, 0 on success */
+/* Return negative errno on error, 0 on success */
static s32 vt596_access(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write, u8 command,
int size, union i2c_smbus_data *data)
{
int i;
+ int status;
switch (size) {
case I2C_SMBUS_QUICK:
@@ -258,8 +260,9 @@ static s32 vt596_access(struct i2c_adapter *adap, u16 addr,
outb_p(((addr & 0x7f) << 1) | read_write, SMBHSTADD);
- if (vt596_transaction(size)) /* Error in transaction */
- return -1;
+ status = vt596_transaction(size);
+ if (status)
+ return status;
if ((read_write == I2C_SMBUS_WRITE) || (size == VT596_QUICK))
return 0;
@@ -285,9 +288,9 @@ static s32 vt596_access(struct i2c_adapter *adap, u16 addr,
return 0;
exit_unsupported:
- dev_warn(&vt596_adapter.dev, "Unsupported command invoked! (0x%02x)\n",
+ dev_warn(&vt596_adapter.dev, "Unsupported transaction %d\n",
size);
- return -1;
+ return -EOPNOTSUPP;
}
static u32 vt596_func(struct i2c_adapter *adapter)
@@ -309,7 +312,7 @@ static const struct i2c_algorithm smbus_algorithm = {
static struct i2c_adapter vt596_adapter = {
.owner = THIS_MODULE,
.id = I2C_HW_SMBUS_VIA2,
- .class = I2C_CLASS_HWMON,
+ .class = I2C_CLASS_HWMON | I2C_CLASS_SPD,
.algo = &smbus_algorithm,
};
@@ -354,6 +357,10 @@ static int __devinit vt596_probe(struct pci_dev *pdev,
}
found:
+ error = acpi_check_region(vt596_smba, 8, vt596_driver.name);
+ if (error)
+ return error;
+
if (!request_region(vt596_smba, 8, vt596_driver.name)) {
dev_err(&pdev->dev, "SMBus region 0x%x already in use!\n",
vt596_smba);
diff --git a/drivers/i2c/busses/scx200_acb.c b/drivers/i2c/busses/scx200_acb.c
index 61abe0f33255..ed794b145a11 100644
--- a/drivers/i2c/busses/scx200_acb.c
+++ b/drivers/i2c/busses/scx200_acb.c
@@ -442,7 +442,7 @@ static __init struct scx200_acb_iface *scx200_create_iface(const char *text,
adapter->owner = THIS_MODULE;
adapter->id = I2C_HW_SMBUS_SCX200;
adapter->algo = &scx200_acb_algorithm;
- adapter->class = I2C_CLASS_HWMON;
+ adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
adapter->dev.parent = dev;
mutex_init(&iface->mutex);
diff --git a/drivers/i2c/chips/eeprom.c b/drivers/i2c/chips/eeprom.c
index 7dee001e5133..9a81252a7218 100644
--- a/drivers/i2c/chips/eeprom.c
+++ b/drivers/i2c/chips/eeprom.c
@@ -78,7 +78,7 @@ static struct i2c_driver eeprom_driver = {
static void eeprom_update_client(struct i2c_client *client, u8 slice)
{
struct eeprom_data *data = i2c_get_clientdata(client);
- int i, j;
+ int i;
mutex_lock(&data->update_lock);
@@ -93,15 +93,12 @@ static void eeprom_update_client(struct i2c_client *client, u8 slice)
!= 32)
goto exit;
} else {
- if (i2c_smbus_write_byte(client, slice << 5)) {
- dev_dbg(&client->dev, "eeprom read start has failed!\n");
- goto exit;
- }
- for (i = slice << 5; i < (slice + 1) << 5; i++) {
- j = i2c_smbus_read_byte(client);
- if (j < 0)
+ for (i = slice << 5; i < (slice + 1) << 5; i += 2) {
+ int word = i2c_smbus_read_word_data(client, i);
+ if (word < 0)
goto exit;
- data->data[i] = (u8) j;
+ data->data[i] = word & 0xff;
+ data->data[i + 1] = word >> 8;
}
}
data->last_updated[slice] = jiffies;
@@ -159,6 +156,8 @@ static struct bin_attribute eeprom_attr = {
static int eeprom_attach_adapter(struct i2c_adapter *adapter)
{
+ if (!(adapter->class & (I2C_CLASS_DDC | I2C_CLASS_SPD)))
+ return 0;
return i2c_probe(adapter, &addr_data, eeprom_detect);
}
@@ -169,14 +168,21 @@ static int eeprom_detect(struct i2c_adapter *adapter, int address, int kind)
struct eeprom_data *data;
int err = 0;
- /* There are three ways we can read the EEPROM data:
+ /* EDID EEPROMs are often 24C00 EEPROMs, which answer to all
+ addresses 0x50-0x57, but we only care about 0x50. So decline
+ attaching to addresses >= 0x51 on DDC buses */
+ if (!(adapter->class & I2C_CLASS_SPD) && address >= 0x51)
+ goto exit;
+
+ /* There are four ways we can read the EEPROM data:
(1) I2C block reads (faster, but unsupported by most adapters)
- (2) Consecutive byte reads (100% overhead)
- (3) Regular byte data reads (200% overhead)
- The third method is not implemented by this driver because all
- known adapters support at least the second. */
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA
- | I2C_FUNC_SMBUS_BYTE))
+ (2) Word reads (128% overhead)
+ (3) Consecutive byte reads (88% overhead, unsafe)
+ (4) Regular byte data reads (265% overhead)
+ The third and fourth methods are not implemented by this driver
+ because all known adapters support one of the first two. */
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)
+ && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK))
goto exit;
if (!(data = kzalloc(sizeof(struct eeprom_data), GFP_KERNEL))) {
@@ -204,13 +210,14 @@ static int eeprom_detect(struct i2c_adapter *adapter, int address, int kind)
/* Detect the Vaio nature of EEPROMs.
We use the "PCG-" or "VGN-" prefix as the signature. */
- if (address == 0x57) {
+ if (address == 0x57
+ && i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
char name[4];
name[0] = i2c_smbus_read_byte_data(new_client, 0x80);
- name[1] = i2c_smbus_read_byte(new_client);
- name[2] = i2c_smbus_read_byte(new_client);
- name[3] = i2c_smbus_read_byte(new_client);
+ name[1] = i2c_smbus_read_byte_data(new_client, 0x81);
+ name[2] = i2c_smbus_read_byte_data(new_client, 0x82);
+ name[3] = i2c_smbus_read_byte_data(new_client, 0x83);
if (!memcmp(name, "PCG-", 4) || !memcmp(name, "VGN-", 4)) {
dev_info(&new_client->dev, "Vaio EEPROM detected, "
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index d0175f4f8fc6..70d12ccc8925 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -29,13 +29,11 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/idr.h>
-#include <linux/seq_file.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/completion.h>
#include <linux/hardirq.h>
#include <linux/irqflags.h>
-#include <linux/semaphore.h>
#include <asm/uaccess.h>
#include "i2c-core.h"
@@ -103,19 +101,14 @@ static int i2c_device_probe(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct i2c_driver *driver = to_i2c_driver(dev->driver);
- const struct i2c_device_id *id;
int status;
- if (!driver->probe)
+ if (!driver->probe || !driver->id_table)
return -ENODEV;
client->driver = driver;
dev_dbg(dev, "probe\n");
- if (driver->id_table)
- id = i2c_match_id(driver->id_table, client);
- else
- id = NULL;
- status = driver->probe(client, id);
+ status = driver->probe(client, i2c_match_id(driver->id_table, client));
if (status)
client->driver = NULL;
return status;
@@ -582,8 +575,7 @@ static int i2c_do_del_adapter(struct device_driver *d, void *data)
*/
int i2c_del_adapter(struct i2c_adapter *adap)
{
- struct list_head *item, *_n;
- struct i2c_client *client;
+ struct i2c_client *client, *_n;
int res = 0;
mutex_lock(&core_lock);
@@ -604,10 +596,9 @@ int i2c_del_adapter(struct i2c_adapter *adap)
/* detach any active clients. This must be done first, because
* it can fail; in which case we give up. */
- list_for_each_safe(item, _n, &adap->clients) {
+ list_for_each_entry_safe(client, _n, &adap->clients, list) {
struct i2c_driver *driver;
- client = list_entry(item, struct i2c_client, list);
driver = client->driver;
/* new style, follow standard driver model */
@@ -646,6 +637,16 @@ EXPORT_SYMBOL(i2c_del_adapter);
/* ------------------------------------------------------------------------- */
+static int __attach_adapter(struct device *dev, void *data)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(dev);
+ struct i2c_driver *driver = data;
+
+ driver->attach_adapter(adapter);
+
+ return 0;
+}
+
/*
* An i2c_driver is used with one or more i2c_client (device) nodes to access
* i2c slave chips, on a bus instance associated with some i2c_adapter. There
@@ -686,21 +687,47 @@ int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
/* legacy drivers scan i2c busses directly */
- if (driver->attach_adapter) {
- struct i2c_adapter *adapter;
+ if (driver->attach_adapter)
+ class_for_each_device(&i2c_adapter_class, NULL, driver,
+ __attach_adapter);
+
+ mutex_unlock(&core_lock);
+ return 0;
+}
+EXPORT_SYMBOL(i2c_register_driver);
- down(&i2c_adapter_class.sem);
- list_for_each_entry(adapter, &i2c_adapter_class.devices,
- dev.node) {
- driver->attach_adapter(adapter);
+static int __detach_adapter(struct device *dev, void *data)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(dev);
+ struct i2c_driver *driver = data;
+
+ /* Have a look at each adapter, if clients of this driver are still
+ * attached. If so, detach them to be able to kill the driver
+ * afterwards.
+ */
+ if (driver->detach_adapter) {
+ if (driver->detach_adapter(adapter))
+ dev_err(&adapter->dev,
+ "detach_adapter failed for driver [%s]\n",
+ driver->driver.name);
+ } else {
+ struct i2c_client *client, *_n;
+
+ list_for_each_entry_safe(client, _n, &adapter->clients, list) {
+ if (client->driver != driver)
+ continue;
+ dev_dbg(&adapter->dev,
+ "detaching client [%s] at 0x%02x\n",
+ client->name, client->addr);
+ if (driver->detach_client(client))
+ dev_err(&adapter->dev, "detach_client "
+ "failed for client [%s] at 0x%02x\n",
+ client->name, client->addr);
}
- up(&i2c_adapter_class.sem);
}
- mutex_unlock(&core_lock);
return 0;
}
-EXPORT_SYMBOL(i2c_register_driver);
/**
* i2c_del_driver - unregister I2C driver
@@ -709,48 +736,13 @@ EXPORT_SYMBOL(i2c_register_driver);
*/
void i2c_del_driver(struct i2c_driver *driver)
{
- struct list_head *item2, *_n;
- struct i2c_client *client;
- struct i2c_adapter *adap;
-
mutex_lock(&core_lock);
- /* new-style driver? */
- if (is_newstyle_driver(driver))
- goto unregister;
-
- /* Have a look at each adapter, if clients of this driver are still
- * attached. If so, detach them to be able to kill the driver
- * afterwards.
- */
- down(&i2c_adapter_class.sem);
- list_for_each_entry(adap, &i2c_adapter_class.devices, dev.node) {
- if (driver->detach_adapter) {
- if (driver->detach_adapter(adap)) {
- dev_err(&adap->dev, "detach_adapter failed "
- "for driver [%s]\n",
- driver->driver.name);
- }
- } else {
- list_for_each_safe(item2, _n, &adap->clients) {
- client = list_entry(item2, struct i2c_client, list);
- if (client->driver != driver)
- continue;
- dev_dbg(&adap->dev, "detaching client [%s] "
- "at 0x%02x\n", client->name,
- client->addr);
- if (driver->detach_client(client)) {
- dev_err(&adap->dev, "detach_client "
- "failed for client [%s] at "
- "0x%02x\n", client->name,
- client->addr);
- }
- }
- }
- }
- up(&i2c_adapter_class.sem);
+ /* legacy driver? */
+ if (!is_newstyle_driver(driver))
+ class_for_each_device(&i2c_adapter_class, NULL, driver,
+ __detach_adapter);
- unregister:
driver_unregister(&driver->driver);
pr_debug("i2c-core: driver [%s] unregistered\n", driver->driver.name);
@@ -863,8 +855,9 @@ EXPORT_SYMBOL(i2c_detach_client);
*/
struct i2c_client *i2c_use_client(struct i2c_client *client)
{
- get_device(&client->dev);
- return client;
+ if (client && get_device(&client->dev))
+ return client;
+ return NULL;
}
EXPORT_SYMBOL(i2c_use_client);
@@ -876,7 +869,8 @@ EXPORT_SYMBOL(i2c_use_client);
*/
void i2c_release_client(struct i2c_client *client)
{
- put_device(&client->dev);
+ if (client)
+ put_device(&client->dev);
}
EXPORT_SYMBOL(i2c_release_client);
@@ -942,10 +936,39 @@ module_exit(i2c_exit);
* ----------------------------------------------------
*/
+/**
+ * i2c_transfer - execute a single or combined I2C message
+ * @adap: Handle to I2C bus
+ * @msgs: One or more messages to execute before STOP is issued to
+ * terminate the operation; each message begins with a START.
+ * @num: Number of messages to be executed.
+ *
+ * Returns negative errno, else the number of messages executed.
+ *
+ * Note that there is no requirement that each message be sent to
+ * the same slave address, although that is the most common model.
+ */
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
{
int ret;
+ /* REVISIT the fault reporting model here is weak:
+ *
+ * - When we get an error after receiving N bytes from a slave,
+ * there is no way to report "N".
+ *
+ * - When we get a NAK after transmitting N bytes to a slave,
+ * there is no way to report "N" ... or to let the master
+ * continue executing the rest of this combined message, if
+ * that's the appropriate response.
+ *
+ * - When for example "num" is two and we successfully complete
+ * the first message but get an error part way through the
+ * second, it's unclear whether that should be reported as
+ * one (discarding status on the second message) or errno
+ * (discarding status on the first one).
+ */
+
if (adap->algo->master_xfer) {
#ifdef DEBUG
for (ret = 0; ret < num; ret++) {
@@ -971,11 +994,19 @@ int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
- return -ENOSYS;
+ return -EOPNOTSUPP;
}
}
EXPORT_SYMBOL(i2c_transfer);
+/**
+ * i2c_master_send - issue a single I2C message in master transmit mode
+ * @client: Handle to slave device
+ * @buf: Data that will be written to the slave
+ * @count: How many bytes to write
+ *
+ * Returns negative errno, or else the number of bytes written.
+ */
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
{
int ret;
@@ -995,6 +1026,14 @@ int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
}
EXPORT_SYMBOL(i2c_master_send);
+/**
+ * i2c_master_recv - issue a single I2C message in master receive mode
+ * @client: Handle to slave device
+ * @buf: Where to store data read from slave
+ * @count: How many bytes to read
+ *
+ * Returns negative errno, or else the number of bytes read.
+ */
int i2c_master_recv(struct i2c_client *client, char *buf ,int count)
{
struct i2c_adapter *adap=client->adapter;
@@ -1103,7 +1142,7 @@ int i2c_probe(struct i2c_adapter *adapter,
dev_warn(&adapter->dev, "SMBus Quick command not supported, "
"can't probe for chips\n");
- return -1;
+ return -EOPNOTSUPP;
}
/* Probe entries are done second, and are not affected by ignore
@@ -1295,29 +1334,38 @@ static int i2c_smbus_check_pec(u8 cpec, struct i2c_msg *msg)
if (rpec != cpec) {
pr_debug("i2c-core: Bad PEC 0x%02x vs. 0x%02x\n",
rpec, cpec);
- return -1;
+ return -EBADMSG;
}
return 0;
}
-s32 i2c_smbus_write_quick(struct i2c_client *client, u8 value)
-{
- return i2c_smbus_xfer(client->adapter,client->addr,client->flags,
- value,0,I2C_SMBUS_QUICK,NULL);
-}
-EXPORT_SYMBOL(i2c_smbus_write_quick);
-
+/**
+ * i2c_smbus_read_byte - SMBus "receive byte" protocol
+ * @client: Handle to slave device
+ *
+ * This executes the SMBus "receive byte" protocol, returning negative errno
+ * else the byte received from the device.
+ */
s32 i2c_smbus_read_byte(struct i2c_client *client)
{
union i2c_smbus_data data;
- if (i2c_smbus_xfer(client->adapter,client->addr,client->flags,
- I2C_SMBUS_READ,0,I2C_SMBUS_BYTE, &data))
- return -1;
- else
- return data.byte;
+ int status;
+
+ status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
+ I2C_SMBUS_READ, 0,
+ I2C_SMBUS_BYTE, &data);
+ return (status < 0) ? status : data.byte;
}
EXPORT_SYMBOL(i2c_smbus_read_byte);
+/**
+ * i2c_smbus_write_byte - SMBus "send byte" protocol
+ * @client: Handle to slave device
+ * @value: Byte to be sent
+ *
+ * This executes the SMBus "send byte" protocol, returning negative errno
+ * else zero on success.
+ */
s32 i2c_smbus_write_byte(struct i2c_client *client, u8 value)
{
return i2c_smbus_xfer(client->adapter,client->addr,client->flags,
@@ -1325,17 +1373,35 @@ s32 i2c_smbus_write_byte(struct i2c_client *client, u8 value)
}
EXPORT_SYMBOL(i2c_smbus_write_byte);
+/**
+ * i2c_smbus_read_byte_data - SMBus "read byte" protocol
+ * @client: Handle to slave device
+ * @command: Byte interpreted by slave
+ *
+ * This executes the SMBus "read byte" protocol, returning negative errno
+ * else a data byte received from the device.
+ */
s32 i2c_smbus_read_byte_data(struct i2c_client *client, u8 command)
{
union i2c_smbus_data data;
- if (i2c_smbus_xfer(client->adapter,client->addr,client->flags,
- I2C_SMBUS_READ,command, I2C_SMBUS_BYTE_DATA,&data))
- return -1;
- else
- return data.byte;
+ int status;
+
+ status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
+ I2C_SMBUS_READ, command,
+ I2C_SMBUS_BYTE_DATA, &data);
+ return (status < 0) ? status : data.byte;
}
EXPORT_SYMBOL(i2c_smbus_read_byte_data);
+/**
+ * i2c_smbus_write_byte_data - SMBus "write byte" protocol
+ * @client: Handle to slave device
+ * @command: Byte interpreted by slave
+ * @value: Byte being written
+ *
+ * This executes the SMBus "write byte" protocol, returning negative errno
+ * else zero on success.
+ */
s32 i2c_smbus_write_byte_data(struct i2c_client *client, u8 command, u8 value)
{
union i2c_smbus_data data;
@@ -1346,17 +1412,35 @@ s32 i2c_smbus_write_byte_data(struct i2c_client *client, u8 command, u8 value)
}
EXPORT_SYMBOL(i2c_smbus_write_byte_data);
+/**
+ * i2c_smbus_read_word_data - SMBus "read word" protocol
+ * @client: Handle to slave device
+ * @command: Byte interpreted by slave
+ *
+ * This executes the SMBus "read word" protocol, returning negative errno
+ * else a 16-bit unsigned "word" received from the device.
+ */
s32 i2c_smbus_read_word_data(struct i2c_client *client, u8 command)
{
union i2c_smbus_data data;
- if (i2c_smbus_xfer(client->adapter,client->addr,client->flags,
- I2C_SMBUS_READ,command, I2C_SMBUS_WORD_DATA, &data))
- return -1;
- else
- return data.word;
+ int status;
+
+ status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
+ I2C_SMBUS_READ, command,
+ I2C_SMBUS_WORD_DATA, &data);
+ return (status < 0) ? status : data.word;
}
EXPORT_SYMBOL(i2c_smbus_read_word_data);
+/**
+ * i2c_smbus_write_word_data - SMBus "write word" protocol
+ * @client: Handle to slave device
+ * @command: Byte interpreted by slave
+ * @value: 16-bit "word" being written
+ *
+ * This executes the SMBus "write word" protocol, returning negative errno
+ * else zero on success.
+ */
s32 i2c_smbus_write_word_data(struct i2c_client *client, u8 command, u16 value)
{
union i2c_smbus_data data;
@@ -1368,15 +1452,14 @@ s32 i2c_smbus_write_word_data(struct i2c_client *client, u8 command, u16 value)
EXPORT_SYMBOL(i2c_smbus_write_word_data);
/**
- * i2c_smbus_read_block_data - SMBus block read request
+ * i2c_smbus_read_block_data - SMBus "block read" protocol
* @client: Handle to slave device
- * @command: Command byte issued to let the slave know what data should
- * be returned
+ * @command: Byte interpreted by slave
* @values: Byte array into which data will be read; big enough to hold
* the data returned by the slave. SMBus allows at most 32 bytes.
*
- * Returns the number of bytes read in the slave's response, else a
- * negative number to indicate some kind of error.
+ * This executes the SMBus "block read" protocol, returning negative errno
+ * else the number of data bytes in the slave's response.
*
* Note that using this function requires that the client's adapter support
* the I2C_FUNC_SMBUS_READ_BLOCK_DATA functionality. Not all adapter drivers
@@ -1387,17 +1470,29 @@ s32 i2c_smbus_read_block_data(struct i2c_client *client, u8 command,
u8 *values)
{
union i2c_smbus_data data;
+ int status;
- if (i2c_smbus_xfer(client->adapter, client->addr, client->flags,
- I2C_SMBUS_READ, command,
- I2C_SMBUS_BLOCK_DATA, &data))
- return -1;
+ status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
+ I2C_SMBUS_READ, command,
+ I2C_SMBUS_BLOCK_DATA, &data);
+ if (status)
+ return status;
memcpy(values, &data.block[1], data.block[0]);
return data.block[0];
}
EXPORT_SYMBOL(i2c_smbus_read_block_data);
+/**
+ * i2c_smbus_write_block_data - SMBus "block write" protocol
+ * @client: Handle to slave device
+ * @command: Byte interpreted by slave
+ * @length: Size of data block; SMBus allows at most 32 bytes
+ * @values: Byte array which will be written.
+ *
+ * This executes the SMBus "block write" protocol, returning negative errno
+ * else zero on success.
+ */
s32 i2c_smbus_write_block_data(struct i2c_client *client, u8 command,
u8 length, const u8 *values)
{
@@ -1418,14 +1513,16 @@ s32 i2c_smbus_read_i2c_block_data(struct i2c_client *client, u8 command,
u8 length, u8 *values)
{
union i2c_smbus_data data;
+ int status;
if (length > I2C_SMBUS_BLOCK_MAX)
length = I2C_SMBUS_BLOCK_MAX;
data.block[0] = length;
- if (i2c_smbus_xfer(client->adapter,client->addr,client->flags,
- I2C_SMBUS_READ,command,
- I2C_SMBUS_I2C_BLOCK_DATA,&data))
- return -1;
+ status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
+ I2C_SMBUS_READ, command,
+ I2C_SMBUS_I2C_BLOCK_DATA, &data);
+ if (status < 0)
+ return status;
memcpy(values, &data.block[1], data.block[0]);
return data.block[0];
@@ -1466,6 +1563,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
};
int i;
u8 partial_pec = 0;
+ int status;
msgbuf0[0] = command;
switch(size) {
@@ -1515,10 +1613,10 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
} else {
msg[0].len = data->block[0] + 2;
if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {
- dev_err(&adapter->dev, "smbus_access called with "
- "invalid block write size (%d)\n",
- data->block[0]);
- return -1;
+ dev_err(&adapter->dev,
+ "Invalid block write size %d\n",
+ data->block[0]);
+ return -EINVAL;
}
for (i = 1; i < msg[0].len; i++)
msgbuf0[i] = data->block[i-1];
@@ -1528,10 +1626,10 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
num = 2; /* Another special case */
read_write = I2C_SMBUS_READ;
if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {
- dev_err(&adapter->dev, "%s called with invalid "
- "block proc call size (%d)\n", __func__,
+ dev_err(&adapter->dev,
+ "Invalid block write size %d\n",
data->block[0]);
- return -1;
+ return -EINVAL;
}
msg[0].len = data->block[0] + 2;
for (i = 1; i < msg[0].len; i++)
@@ -1546,19 +1644,18 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
} else {
msg[0].len = data->block[0] + 1;
if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 1) {
- dev_err(&adapter->dev, "i2c_smbus_xfer_emulated called with "
- "invalid block write size (%d)\n",
- data->block[0]);
- return -1;
+ dev_err(&adapter->dev,
+ "Invalid block write size %d\n",
+ data->block[0]);
+ return -EINVAL;
}
for (i = 1; i <= data->block[0]; i++)
msgbuf0[i] = data->block[i];
}
break;
default:
- dev_err(&adapter->dev, "smbus_access called with invalid size (%d)\n",
- size);
- return -1;
+ dev_err(&adapter->dev, "Unsupported transaction %d\n", size);
+ return -EOPNOTSUPP;
}
i = ((flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK
@@ -1576,13 +1673,15 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
msg[num-1].len++;
}
- if (i2c_transfer(adapter, msg, num) < 0)
- return -1;
+ status = i2c_transfer(adapter, msg, num);
+ if (status < 0)
+ return status;
/* Check PEC if last message is a read */
if (i && (msg[num-1].flags & I2C_M_RD)) {
- if (i2c_smbus_check_pec(partial_pec, &msg[num-1]) < 0)
- return -1;
+ status = i2c_smbus_check_pec(partial_pec, &msg[num-1]);
+ if (status < 0)
+ return status;
}
if (read_write == I2C_SMBUS_READ)
@@ -1610,9 +1709,21 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
return 0;
}
-
+/**
+ * i2c_smbus_xfer - execute SMBus protocol operations
+ * @adapter: Handle to I2C bus
+ * @addr: Address of SMBus slave on that bus
+ * @flags: I2C_CLIENT_* flags (usually zero or I2C_CLIENT_PEC)
+ * @read_write: I2C_SMBUS_READ or I2C_SMBUS_WRITE
+ * @command: Byte interpreted by slave, for protocols which use such bytes
+ * @protocol: SMBus protocol operation to execute, such as I2C_SMBUS_PROC_CALL
+ * @data: Data to be read or written
+ *
+ * This executes an SMBus protocol operation, and returns a negative
+ * errno code else zero on success.
+ */
s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,
- char read_write, u8 command, int size,
+ char read_write, u8 command, int protocol,
union i2c_smbus_data * data)
{
s32 res;
@@ -1622,11 +1733,11 @@ s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,
if (adapter->algo->smbus_xfer) {
mutex_lock(&adapter->bus_lock);
res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write,
- command,size,data);
+ command, protocol, data);
mutex_unlock(&adapter->bus_lock);
} else
res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,
- command,size,data);
+ command, protocol, data);
return res;
}
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
index d34c14c81c29..30ef44768818 100644
--- a/drivers/i2c/i2c-dev.c
+++ b/drivers/i2c/i2c-dev.c
@@ -366,8 +366,7 @@ static noinline int i2cdev_ioctl_smbus(struct i2c_client *client,
return res;
}
-static int i2cdev_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct i2c_client *client = (struct i2c_client *)file->private_data;
unsigned long funcs;
@@ -487,7 +486,7 @@ static const struct file_operations i2cdev_fops = {
.llseek = no_llseek,
.read = i2cdev_read,
.write = i2cdev_write,
- .ioctl = i2cdev_ioctl,
+ .unlocked_ioctl = i2cdev_ioctl,
.open = i2cdev_open,
.release = i2cdev_release,
};
@@ -512,9 +511,9 @@ static int i2cdev_attach_adapter(struct i2c_adapter *adap)
return PTR_ERR(i2c_dev);
/* register this i2c device with the driver core */
- i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
- MKDEV(I2C_MAJOR, adap->nr),
- "i2c-%d", adap->nr);
+ i2c_dev->dev = device_create_drvdata(i2c_dev_class, &adap->dev,
+ MKDEV(I2C_MAJOR, adap->nr),
+ NULL, "i2c-%d", adap->nr);
if (IS_ERR(i2c_dev->dev)) {
res = PTR_ERR(i2c_dev->dev);
goto error;
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
index b4f3aefa12b6..ff8da2283ff9 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -98,6 +98,12 @@ if BLK_DEV_IDE
comment "Please see Documentation/ide/ide.txt for help/info on IDE drives"
+config IDE_TIMINGS
+ bool
+
+config IDE_ATAPI
+ bool
+
config BLK_DEV_IDE_SATA
bool "Support for SATA (deprecated; conflicts with libata SATA driver)"
default n
@@ -201,6 +207,7 @@ config BLK_DEV_IDECD_VERBOSE_ERRORS
config BLK_DEV_IDETAPE
tristate "Include IDE/ATAPI TAPE support"
+ select IDE_ATAPI
help
If you have an IDE tape drive using the ATAPI protocol, say Y.
ATAPI is a newer protocol used by IDE tape and CD-ROM drives,
@@ -223,6 +230,7 @@ config BLK_DEV_IDETAPE
config BLK_DEV_IDEFLOPPY
tristate "Include IDE/ATAPI FLOPPY support"
+ select IDE_ATAPI
---help---
If you have an IDE floppy drive which uses the ATAPI protocol,
answer Y. ATAPI is a newer protocol used by IDE CD-ROM/tape/floppy
@@ -246,6 +254,7 @@ config BLK_DEV_IDEFLOPPY
config BLK_DEV_IDESCSI
tristate "SCSI emulation support"
depends on SCSI
+ select IDE_ATAPI
---help---
WARNING: ide-scsi is no longer needed for cd writing applications!
The 2.6 kernel supports direct writing to ide-cd, which eliminates
@@ -320,6 +329,7 @@ config BLK_DEV_PLATFORM
config BLK_DEV_CMD640
tristate "CMD640 chipset bugfix/support"
depends on X86
+ select IDE_TIMINGS
---help---
The CMD-Technologies CMD640 IDE chip is used on many common 486 and
Pentium motherboards, usually in combination with a "Neptune" or
@@ -416,6 +426,7 @@ config BLK_DEV_GENERIC
config BLK_DEV_OPTI621
tristate "OPTi 82C621 chipset enhanced support (EXPERIMENTAL)"
depends on EXPERIMENTAL
+ select IDE_TIMINGS
select BLK_DEV_IDEPCI
help
This is a driver for the OPTi 82C621 EIDE controller.
@@ -449,6 +460,7 @@ config BLK_DEV_AEC62XX
config BLK_DEV_ALI15X3
tristate "ALI M15x3 chipset support"
+ select IDE_TIMINGS
select BLK_DEV_IDEDMA_PCI
help
This driver ensures (U)DMA support for ALI 1533, 1543 and 1543C
@@ -463,6 +475,7 @@ config BLK_DEV_ALI15X3
config BLK_DEV_AMD74XX
tristate "AMD and nVidia IDE support"
depends on !ARM
+ select IDE_TIMINGS
select BLK_DEV_IDEDMA_PCI
help
This driver adds explicit support for AMD-7xx and AMD-8111 chips
@@ -483,6 +496,7 @@ config BLK_DEV_ATIIXP
config BLK_DEV_CMD64X
tristate "CMD64{3|6|8|9} chipset support"
+ select IDE_TIMINGS
select BLK_DEV_IDEDMA_PCI
help
Say Y here if you have an IDE controller which uses any of these
@@ -497,6 +511,7 @@ config BLK_DEV_TRIFLEX
config BLK_DEV_CY82C693
tristate "CY82C693 chipset support"
+ select IDE_TIMINGS
select BLK_DEV_IDEDMA_PCI
help
This driver adds detection and support for the CY82C693 chipset
@@ -689,6 +704,7 @@ config BLK_DEV_SIS5513
config BLK_DEV_SL82C105
tristate "Winbond SL82c105 support"
depends on (PPC || ARM)
+ select IDE_TIMINGS
select BLK_DEV_IDEDMA_PCI
help
If you have a Winbond SL82c105 IDE controller, say Y here to enable
@@ -719,6 +735,7 @@ config BLK_DEV_TRM290
config BLK_DEV_VIA82CXXX
tristate "VIA82CXXX chipset support"
+ select IDE_TIMINGS
select BLK_DEV_IDEDMA_PCI
help
This driver adds explicit support for VIA BusMastering IDE chips.
@@ -745,6 +762,7 @@ endif
config BLK_DEV_IDE_PMAC
tristate "PowerMac on-board IDE support"
depends on PPC_PMAC && IDE=y && BLK_DEV_IDE=y
+ select IDE_TIMINGS
help
This driver provides support for the on-board IDE controller on
most of the recent Apple Power Macintoshes and PowerBooks.
@@ -913,21 +931,12 @@ config BLK_DEV_Q40IDE
config BLK_DEV_PALMCHIP_BK3710
tristate "Palmchip bk3710 IDE controller support"
depends on ARCH_DAVINCI
+ select IDE_TIMINGS
select BLK_DEV_IDEDMA_SFF
help
Say Y here if you want to support the onchip IDE controller on the
TI DaVinci SoC
-
-config BLK_DEV_MPC8xx_IDE
- tristate "MPC8xx IDE support"
- depends on 8xx && (LWMON || IVMS8 || IVML24 || TQM8xxL) && IDE=y && BLK_DEV_IDE=y && !PPC_MERGE
- help
- This option provides support for IDE on Motorola MPC8xx Systems.
- Please see 'Type of MPC8xx IDE interface' for details.
-
- If unsure, say N.
-
choice
prompt "Type of MPC8xx IDE interface"
depends on BLK_DEV_MPC8xx_IDE
@@ -975,6 +984,7 @@ config BLK_DEV_4DRIVES
config BLK_DEV_ALI14XX
tristate "ALI M14xx support"
+ select IDE_TIMINGS
help
This driver is enabled at runtime using the "ali14xx.probe" kernel
boot parameter. It enables support for the secondary IDE interface
@@ -994,6 +1004,7 @@ config BLK_DEV_DTC2278
config BLK_DEV_HT6560B
tristate "Holtek HT6560B support"
+ select IDE_TIMINGS
help
This driver is enabled at runtime using the "ht6560b.probe" kernel
boot parameter. It enables support for the secondary IDE interface
@@ -1003,6 +1014,7 @@ config BLK_DEV_HT6560B
config BLK_DEV_QD65XX
tristate "QDI QD65xx support"
+ select IDE_TIMINGS
help
This driver is enabled at runtime using the "qd65xx.probe" kernel
boot parameter. It permits faster I/O speeds to be set. See the
@@ -1028,6 +1040,7 @@ endif
config BLK_DEV_HD_ONLY
bool "Old hard disk (MFM/RLL/IDE) driver"
+ depends on !ARM || ARCH_RPC || ARCH_SHARK || BROKEN
help
There are two drivers for MFM/RLL/IDE hard disks. Most people use
the newer enhanced driver, but this old one is still around for two
diff --git a/drivers/ide/Makefile b/drivers/ide/Makefile
index f94b679b611e..8605536ea18f 100644
--- a/drivers/ide/Makefile
+++ b/drivers/ide/Makefile
@@ -11,9 +11,12 @@
EXTRA_CFLAGS += -Idrivers/ide
-ide-core-y += ide.o ide-io.o ide-iops.o ide-lib.o ide-probe.o ide-taskfile.o
+ide-core-y += ide.o ide-io.o ide-iops.o ide-lib.o ide-probe.o ide-taskfile.o \
+ ide-pio-blacklist.o
# core IDE code
+ide-core-$(CONFIG_IDE_TIMINGS) += ide-timings.o
+ide-core-$(CONFIG_IDE_ATAPI) += ide-atapi.o
ide-core-$(CONFIG_BLK_DEV_IDEPCI) += setup-pci.o
ide-core-$(CONFIG_BLK_DEV_IDEDMA) += ide-dma.o
ide-core-$(CONFIG_IDE_PROC_FS) += ide-proc.o
diff --git a/drivers/ide/arm/bast-ide.c b/drivers/ide/arm/bast-ide.c
index 713cef20622e..2752c551325a 100644
--- a/drivers/ide/arm/bast-ide.c
+++ b/drivers/ide/arm/bast-ide.c
@@ -42,6 +42,7 @@ static int __init bastide_register(unsigned int base, unsigned int aux, int irq)
hw.io_ports.ctl_addr = aux + (6 * 0x20);
hw.irq = irq;
+ hw.chipset = ide_generic;
hwif = ide_find_port();
if (hwif == NULL)
@@ -49,7 +50,6 @@ static int __init bastide_register(unsigned int base, unsigned int aux, int irq)
i = hwif->index;
- ide_init_port_data(hwif, i);
ide_init_port_hw(hwif, &hw);
hwif->port_ops = NULL;
diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c
index 061456914ca3..52f58c885783 100644
--- a/drivers/ide/arm/icside.c
+++ b/drivers/ide/arm/icside.c
@@ -21,6 +21,8 @@
#include <asm/dma.h>
#include <asm/ecard.h>
+#define DRV_NAME "icside"
+
#define ICS_IDENT_OFFSET 0x2280
#define ICS_ARCIN_V5_INTRSTAT 0x0000
@@ -68,6 +70,7 @@ struct icside_state {
unsigned int enabled;
void __iomem *irq_port;
void __iomem *ioc_base;
+ unsigned int sel;
unsigned int type;
ide_hwif_t *hwif[2];
};
@@ -165,7 +168,8 @@ static const expansioncard_ops_t icside_ops_arcin_v6 = {
static void icside_maskproc(ide_drive_t *drive, int mask)
{
ide_hwif_t *hwif = HWIF(drive);
- struct icside_state *state = hwif->hwif_data;
+ struct expansion_card *ec = ECARD_DEV(hwif->dev);
+ struct icside_state *state = ecard_get_drvdata(ec);
unsigned long flags;
local_irq_save(flags);
@@ -308,6 +312,7 @@ static int icside_dma_setup(ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
struct expansion_card *ec = ECARD_DEV(hwif->dev);
+ struct icside_state *state = ecard_get_drvdata(ec);
struct request *rq = hwif->hwgroup->rq;
unsigned int dma_mode;
@@ -331,7 +336,7 @@ static int icside_dma_setup(ide_drive_t *drive)
/*
* Route the DMA signals to the correct interface.
*/
- writeb(hwif->select_data, hwif->config_data);
+ writeb(state->sel | hwif->channel, state->ioc_base);
/*
* Select the correct timing for this drive.
@@ -359,7 +364,8 @@ static void icside_dma_exec_cmd(ide_drive_t *drive, u8 cmd)
static int icside_dma_test_irq(ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
- struct icside_state *state = hwif->hwif_data;
+ struct expansion_card *ec = ECARD_DEV(hwif->dev);
+ struct icside_state *state = ecard_get_drvdata(ec);
return readb(state->irq_port +
(hwif->channel ?
@@ -411,36 +417,24 @@ static int icside_dma_off_init(ide_hwif_t *hwif, const struct ide_port_info *d)
return -EOPNOTSUPP;
}
-static ide_hwif_t *
-icside_setup(void __iomem *base, struct cardinfo *info, struct expansion_card *ec)
+static void icside_setup_ports(hw_regs_t *hw, void __iomem *base,
+ struct cardinfo *info, struct expansion_card *ec)
{
unsigned long port = (unsigned long)base + info->dataoffset;
- ide_hwif_t *hwif;
- hwif = ide_find_port();
- if (hwif) {
- /*
- * Ensure we're using MMIO
- */
- default_hwif_mmiops(hwif);
-
- hwif->io_ports.data_addr = port;
- hwif->io_ports.error_addr = port + (1 << info->stepping);
- hwif->io_ports.nsect_addr = port + (2 << info->stepping);
- hwif->io_ports.lbal_addr = port + (3 << info->stepping);
- hwif->io_ports.lbam_addr = port + (4 << info->stepping);
- hwif->io_ports.lbah_addr = port + (5 << info->stepping);
- hwif->io_ports.device_addr = port + (6 << info->stepping);
- hwif->io_ports.status_addr = port + (7 << info->stepping);
- hwif->io_ports.ctl_addr =
- (unsigned long)base + info->ctrloffset;
- hwif->irq = ec->irq;
- hwif->chipset = ide_acorn;
- hwif->gendev.parent = &ec->dev;
- hwif->dev = &ec->dev;
- }
-
- return hwif;
+ hw->io_ports.data_addr = port;
+ hw->io_ports.error_addr = port + (1 << info->stepping);
+ hw->io_ports.nsect_addr = port + (2 << info->stepping);
+ hw->io_ports.lbal_addr = port + (3 << info->stepping);
+ hw->io_ports.lbam_addr = port + (4 << info->stepping);
+ hw->io_ports.lbah_addr = port + (5 << info->stepping);
+ hw->io_ports.device_addr = port + (6 << info->stepping);
+ hw->io_ports.status_addr = port + (7 << info->stepping);
+ hw->io_ports.ctl_addr = (unsigned long)base + info->ctrloffset;
+
+ hw->irq = ec->irq;
+ hw->dev = &ec->dev;
+ hw->chipset = ide_acorn;
}
static int __init
@@ -449,6 +443,7 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec)
ide_hwif_t *hwif;
void __iomem *base;
u8 idx[4] = { 0xff, 0xff, 0xff, 0xff };
+ hw_regs_t hw;
base = ecardm_iomap(ec, ECARD_RES_MEMC, 0, 0);
if (!base)
@@ -466,12 +461,19 @@ icside_register_v5(struct icside_state *state, struct expansion_card *ec)
*/
icside_irqdisable_arcin_v5(ec, 0);
- hwif = icside_setup(base, &icside_cardinfo_v5, ec);
+ icside_setup_ports(&hw, base, &icside_cardinfo_v5, ec);
+
+ hwif = ide_find_port();
if (!hwif)
return -ENODEV;
+ ide_init_port_hw(hwif, &hw);
+ default_hwif_mmiops(hwif);
+
state->hwif[0] = hwif;
+ ecard_set_drvdata(ec, state);
+
idx[0] = hwif->index;
ide_device_add(idx, NULL);
@@ -497,6 +499,7 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec)
int ret;
u8 idx[4] = { 0xff, 0xff, 0xff, 0xff };
struct ide_port_info d = icside_v6_port_info;
+ hw_regs_t hw[2];
ioc_base = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, 0);
if (!ioc_base) {
@@ -525,43 +528,47 @@ icside_register_v6(struct icside_state *state, struct expansion_card *ec)
state->irq_port = easi_base;
state->ioc_base = ioc_base;
+ state->sel = sel;
/*
* Be on the safe side - disable interrupts
*/
icside_irqdisable_arcin_v6(ec, 0);
+ icside_setup_ports(&hw[0], easi_base, &icside_cardinfo_v6_1, ec);
+ icside_setup_ports(&hw[1], easi_base, &icside_cardinfo_v6_2, ec);
+
/*
* Find and register the interfaces.
*/
- hwif = icside_setup(easi_base, &icside_cardinfo_v6_1, ec);
- mate = icside_setup(easi_base, &icside_cardinfo_v6_2, ec);
+ hwif = ide_find_port();
+ if (hwif == NULL)
+ return -ENODEV;
- if (!hwif || !mate) {
- ret = -ENODEV;
- goto out;
+ ide_init_port_hw(hwif, &hw[0]);
+ default_hwif_mmiops(hwif);
+
+ idx[0] = hwif->index;
+
+ mate = ide_find_port();
+ if (mate) {
+ ide_init_port_hw(mate, &hw[1]);
+ default_hwif_mmiops(mate);
+
+ idx[1] = mate->index;
}
state->hwif[0] = hwif;
state->hwif[1] = mate;
- hwif->hwif_data = state;
- hwif->config_data = (unsigned long)ioc_base;
- hwif->select_data = sel;
-
- mate->hwif_data = state;
- mate->config_data = (unsigned long)ioc_base;
- mate->select_data = sel | 1;
+ ecard_set_drvdata(ec, state);
- if (ec->dma != NO_DMA && !request_dma(ec->dma, hwif->name)) {
+ if (ec->dma != NO_DMA && !request_dma(ec->dma, DRV_NAME)) {
d.init_dma = icside_dma_init;
d.port_ops = &icside_v6_port_ops;
d.dma_ops = NULL;
}
- idx[0] = hwif->index;
- idx[1] = mate->index;
-
ide_device_add(idx, &d);
return 0;
@@ -627,10 +634,8 @@ icside_probe(struct expansion_card *ec, const struct ecard_id *id)
break;
}
- if (ret == 0) {
- ecard_set_drvdata(ec, state);
+ if (ret == 0)
goto out;
- }
kfree(state);
release:
diff --git a/drivers/ide/arm/ide_arm.c b/drivers/ide/arm/ide_arm.c
index 4263ffd4ab20..2f311da4c963 100644
--- a/drivers/ide/arm/ide_arm.c
+++ b/drivers/ide/arm/ide_arm.c
@@ -49,6 +49,7 @@ static int __init ide_arm_init(void)
memset(&hw, 0, sizeof(hw));
ide_std_init_ports(&hw, base, ctl);
hw.irq = IDE_ARM_IRQ;
+ hw.chipset = ide_generic;
hwif = ide_find_port();
if (hwif) {
diff --git a/drivers/ide/arm/palm_bk3710.c b/drivers/ide/arm/palm_bk3710.c
index 96378ebfb31f..d00a009ba4d6 100644
--- a/drivers/ide/arm/palm_bk3710.c
+++ b/drivers/ide/arm/palm_bk3710.c
@@ -74,8 +74,6 @@ struct palm_bk3710_udmatiming {
#define BK3710_IORDYTMP 0x78
#define BK3710_IORDYTMS 0x7C
-#include "../ide-timing.h"
-
static long ide_palm_clk;
static const struct palm_bk3710_udmatiming palm_bk3710_udmatimings[6] = {
@@ -399,19 +397,14 @@ static int __devinit palm_bk3710_probe(struct platform_device *pdev)
i = hwif->index;
- ide_init_port_data(hwif, i);
ide_init_port_hw(hwif, &hw);
- hwif->mmio = 1;
default_hwif_mmiops(hwif);
idx[0] = i;
ide_device_add(idx, &palm_bk3710_port_info);
- if (!hwif->present)
- goto out;
-
return 0;
out:
printk(KERN_WARNING "Palm Chip BK3710 IDE Register Fail\n");
diff --git a/drivers/ide/arm/rapide.c b/drivers/ide/arm/rapide.c
index 1747b2358775..43057e0303c8 100644
--- a/drivers/ide/arm/rapide.c
+++ b/drivers/ide/arm/rapide.c
@@ -11,6 +11,10 @@
#include <asm/ecard.h>
+static struct const ide_port_info rapide_port_info = {
+ .host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA,
+};
+
static void rapide_setup_ports(hw_regs_t *hw, void __iomem *base,
void __iomem *ctrl, unsigned int sz, int irq)
{
@@ -44,25 +48,26 @@ rapide_probe(struct expansion_card *ec, const struct ecard_id *id)
goto release;
}
- hwif = ide_find_port();
- if (hwif) {
- memset(&hw, 0, sizeof(hw));
- rapide_setup_ports(&hw, base, base + 0x818, 1 << 6, ec->irq);
- hw.chipset = ide_generic;
- hw.dev = &ec->dev;
+ memset(&hw, 0, sizeof(hw));
+ rapide_setup_ports(&hw, base, base + 0x818, 1 << 6, ec->irq);
+ hw.chipset = ide_generic;
+ hw.dev = &ec->dev;
- ide_init_port_hw(hwif, &hw);
+ hwif = ide_find_port();
+ if (hwif == NULL) {
+ ret = -ENOENT;
+ goto release;
+ }
- hwif->host_flags = IDE_HFLAG_MMIO;
- default_hwif_mmiops(hwif);
+ ide_init_port_hw(hwif, &hw);
+ default_hwif_mmiops(hwif);
- idx[0] = hwif->index;
+ idx[0] = hwif->index;
- ide_device_add(idx, NULL);
+ ide_device_add(idx, &rapide_port_info);
- ecard_set_drvdata(ec, hwif);
- goto out;
- }
+ ecard_set_drvdata(ec, hwif);
+ goto out;
release:
ecard_release_resources(ec);
diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c
index ecf53bb0d2aa..20fad6d542cc 100644
--- a/drivers/ide/h8300/ide-h8300.c
+++ b/drivers/ide/h8300/ide-h8300.c
@@ -8,6 +8,8 @@
#include <asm/io.h>
#include <asm/irq.h>
+#define DRV_NAME "ide-h8300"
+
#define bswap(d) \
({ \
u16 r; \
@@ -52,8 +54,6 @@ static void h8300_tf_load(ide_drive_t *drive, ide_task_t *task)
if (task->tf_flags & IDE_TFLAG_FLAGGED)
HIHI = 0xFF;
- ide_set_irq(drive, 1);
-
if (task->tf_flags & IDE_TFLAG_OUT_DATA)
mm_outw((tf->hob_data << 8) | tf->data, io_ports->data_addr);
@@ -98,7 +98,7 @@ static void h8300_tf_read(ide_drive_t *drive, ide_task_t *task)
}
/* be sure we're looking at the low order bits */
- outb(drive->ctl & ~0x80, io_ports->ctl_addr);
+ outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr);
if (task->tf_flags & IDE_TFLAG_IN_NSECT)
tf->nsect = inb(io_ports->nsect_addr);
@@ -112,7 +112,7 @@ static void h8300_tf_read(ide_drive_t *drive, ide_task_t *task)
tf->device = inb(io_ports->device_addr);
if (task->tf_flags & IDE_TFLAG_LBA48) {
- outb(drive->ctl | 0x80, io_ports->ctl_addr);
+ outb(ATA_DEVCTL_OBS | 0x80, io_ports->ctl_addr);
if (task->tf_flags & IDE_TFLAG_IN_HOB_FEATURE)
tf->hob_feature = inb(io_ports->feature_addr);
@@ -178,6 +178,10 @@ static inline void hwif_setup(ide_hwif_t *hwif)
hwif->output_data = h8300_output_data;
}
+static const struct ide_port_info h8300_port_info = {
+ .host_flags = IDE_HFLAG_NO_IO_32BIT | IDE_HFLAG_NO_DMA,
+};
+
static int __init h8300_ide_init(void)
{
hw_regs_t hw;
@@ -185,6 +189,8 @@ static int __init h8300_ide_init(void)
int index;
u8 idx[4] = { 0xff, 0xff, 0xff, 0xff };
+ printk(KERN_INFO DRV_NAME ": H8/300 generic IDE interface\n");
+
if (!request_region(CONFIG_H8300_IDE_BASE, H8300_IDE_GAP*8, "ide-h8300"))
goto out_busy;
if (!request_region(CONFIG_H8300_IDE_ALT, H8300_IDE_GAP, "ide-h8300")) {
@@ -194,22 +200,17 @@ static int __init h8300_ide_init(void)
hw_setup(&hw);
- hwif = ide_find_port();
- if (hwif == NULL) {
- printk(KERN_ERR "ide-h8300: IDE I/F register failed\n");
+ hwif = ide_find_port_slot(&h8300_port_info);
+ if (hwif == NULL)
return -ENOENT;
- }
index = hwif->index;
- ide_init_port_data(hwif, index);
ide_init_port_hw(hwif, &hw);
hwif_setup(hwif);
- hwif->host_flags = IDE_HFLAG_NO_IO_32BIT;
- printk(KERN_INFO "ide%d: H8/300 generic IDE interface\n", index);
idx[0] = index;
- ide_device_add(idx, NULL);
+ ide_device_add(idx, &h8300_port_info);
return 0;
diff --git a/drivers/ide/ide-acpi.c b/drivers/ide/ide-acpi.c
index 9d3601fa5680..6f704628c27d 100644
--- a/drivers/ide/ide-acpi.c
+++ b/drivers/ide/ide-acpi.c
@@ -60,15 +60,15 @@ struct ide_acpi_hwif_link {
#define DEBPRINT(fmt, args...) do {} while (0)
#endif /* DEBUGGING */
-int ide_noacpi;
+static int ide_noacpi;
module_param_named(noacpi, ide_noacpi, bool, 0);
MODULE_PARM_DESC(noacpi, "disable IDE ACPI support");
-int ide_acpigtf;
+static int ide_acpigtf;
module_param_named(acpigtf, ide_acpigtf, bool, 0);
MODULE_PARM_DESC(acpigtf, "enable IDE ACPI _GTF support");
-int ide_acpionboot;
+static int ide_acpionboot;
module_param_named(acpionboot, ide_acpionboot, bool, 0);
MODULE_PARM_DESC(acpionboot, "call IDE ACPI methods on boot");
diff --git a/drivers/ide/ide-atapi.c b/drivers/ide/ide-atapi.c
new file mode 100644
index 000000000000..2802031de670
--- /dev/null
+++ b/drivers/ide/ide-atapi.c
@@ -0,0 +1,296 @@
+/*
+ * ATAPI support.
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+#include <scsi/scsi.h>
+
+#ifdef DEBUG
+#define debug_log(fmt, args...) \
+ printk(KERN_INFO "ide: " fmt, ## args)
+#else
+#define debug_log(fmt, args...) do {} while (0)
+#endif
+
+/* TODO: unify the code thus making some arguments go away */
+ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc,
+ ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry,
+ void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *),
+ void (*retry_pc)(ide_drive_t *), void (*dsc_handle)(ide_drive_t *),
+ void (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned, int))
+{
+ ide_hwif_t *hwif = drive->hwif;
+ xfer_func_t *xferfunc;
+ unsigned int temp;
+ u16 bcount;
+ u8 stat, ireason, scsi = drive->scsi;
+
+ debug_log("Enter %s - interrupt handler\n", __func__);
+
+ if (pc->flags & PC_FLAG_TIMEDOUT) {
+ pc->callback(drive);
+ return ide_stopped;
+ }
+
+ /* Clear the interrupt */
+ stat = ide_read_status(drive);
+
+ if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) {
+ if (hwif->dma_ops->dma_end(drive) ||
+ (drive->media == ide_tape && !scsi && (stat & ERR_STAT))) {
+ if (drive->media == ide_floppy && !scsi)
+ printk(KERN_ERR "%s: DMA %s error\n",
+ drive->name, rq_data_dir(pc->rq)
+ ? "write" : "read");
+ pc->flags |= PC_FLAG_DMA_ERROR;
+ } else {
+ pc->xferred = pc->req_xfer;
+ if (update_buffers)
+ update_buffers(drive, pc);
+ }
+ debug_log("%s: DMA finished\n", drive->name);
+ }
+
+ /* No more interrupts */
+ if ((stat & DRQ_STAT) == 0) {
+ debug_log("Packet command completed, %d bytes transferred\n",
+ pc->xferred);
+
+ pc->flags &= ~PC_FLAG_DMA_IN_PROGRESS;
+
+ local_irq_enable_in_hardirq();
+
+ if (drive->media == ide_tape && !scsi &&
+ (stat & ERR_STAT) && pc->c[0] == REQUEST_SENSE)
+ stat &= ~ERR_STAT;
+ if ((stat & ERR_STAT) || (pc->flags & PC_FLAG_DMA_ERROR)) {
+ /* Error detected */
+ debug_log("%s: I/O error\n", drive->name);
+
+ if (drive->media != ide_tape || scsi) {
+ pc->rq->errors++;
+ if (scsi)
+ goto cmd_finished;
+ }
+
+ if (pc->c[0] == REQUEST_SENSE) {
+ printk(KERN_ERR "%s: I/O error in request sense"
+ " command\n", drive->name);
+ return ide_do_reset(drive);
+ }
+
+ debug_log("[cmd %x]: check condition\n", pc->c[0]);
+
+ /* Retry operation */
+ retry_pc(drive);
+ /* queued, but not started */
+ return ide_stopped;
+ }
+cmd_finished:
+ pc->error = 0;
+ if ((pc->flags & PC_FLAG_WAIT_FOR_DSC) &&
+ (stat & SEEK_STAT) == 0) {
+ dsc_handle(drive);
+ return ide_stopped;
+ }
+ /* Command finished - Call the callback function */
+ pc->callback(drive);
+ return ide_stopped;
+ }
+
+ if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) {
+ pc->flags &= ~PC_FLAG_DMA_IN_PROGRESS;
+ printk(KERN_ERR "%s: The device wants to issue more interrupts "
+ "in DMA mode\n", drive->name);
+ ide_dma_off(drive);
+ return ide_do_reset(drive);
+ }
+ /* Get the number of bytes to transfer on this interrupt. */
+ bcount = (hwif->INB(hwif->io_ports.lbah_addr) << 8) |
+ hwif->INB(hwif->io_ports.lbam_addr);
+
+ ireason = hwif->INB(hwif->io_ports.nsect_addr);
+
+ if (ireason & CD) {
+ printk(KERN_ERR "%s: CoD != 0 in %s\n", drive->name, __func__);
+ return ide_do_reset(drive);
+ }
+ if (((ireason & IO) == IO) == !!(pc->flags & PC_FLAG_WRITING)) {
+ /* Hopefully, we will never get here */
+ printk(KERN_ERR "%s: We wanted to %s, but the device wants us "
+ "to %s!\n", drive->name,
+ (ireason & IO) ? "Write" : "Read",
+ (ireason & IO) ? "Read" : "Write");
+ return ide_do_reset(drive);
+ }
+ if (!(pc->flags & PC_FLAG_WRITING)) {
+ /* Reading - Check that we have enough space */
+ temp = pc->xferred + bcount;
+ if (temp > pc->req_xfer) {
+ if (temp > pc->buf_size) {
+ printk(KERN_ERR "%s: The device wants to send "
+ "us more data than expected - "
+ "discarding data\n",
+ drive->name);
+ if (scsi)
+ temp = pc->buf_size - pc->xferred;
+ else
+ temp = 0;
+ if (temp) {
+ if (pc->sg)
+ io_buffers(drive, pc, temp, 0);
+ else
+ hwif->input_data(drive, NULL,
+ pc->cur_pos, temp);
+ printk(KERN_ERR "%s: transferred %d of "
+ "%d bytes\n",
+ drive->name,
+ temp, bcount);
+ }
+ pc->xferred += temp;
+ pc->cur_pos += temp;
+ ide_pad_transfer(drive, 0, bcount - temp);
+ ide_set_handler(drive, handler, timeout,
+ expiry);
+ return ide_started;
+ }
+ debug_log("The device wants to send us more data than "
+ "expected - allowing transfer\n");
+ }
+ xferfunc = hwif->input_data;
+ } else
+ xferfunc = hwif->output_data;
+
+ if ((drive->media == ide_floppy && !scsi && !pc->buf) ||
+ (drive->media == ide_tape && !scsi && pc->bh) ||
+ (scsi && pc->sg))
+ io_buffers(drive, pc, bcount, !!(pc->flags & PC_FLAG_WRITING));
+ else
+ xferfunc(drive, NULL, pc->cur_pos, bcount);
+
+ /* Update the current position */
+ pc->xferred += bcount;
+ pc->cur_pos += bcount;
+
+ debug_log("[cmd %x] transferred %d bytes on that intr.\n",
+ pc->c[0], bcount);
+
+ /* And set the interrupt handler again */
+ ide_set_handler(drive, handler, timeout, expiry);
+ return ide_started;
+}
+EXPORT_SYMBOL_GPL(ide_pc_intr);
+
+static u8 ide_wait_ireason(ide_drive_t *drive, u8 ireason)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ int retries = 100;
+
+ while (retries-- && ((ireason & CD) == 0 || (ireason & IO))) {
+ printk(KERN_ERR "%s: (IO,CoD != (0,1) while issuing "
+ "a packet command, retrying\n", drive->name);
+ udelay(100);
+ ireason = hwif->INB(hwif->io_ports.nsect_addr);
+ if (retries == 0) {
+ printk(KERN_ERR "%s: (IO,CoD != (0,1) while issuing "
+ "a packet command, ignoring\n",
+ drive->name);
+ ireason |= CD;
+ ireason &= ~IO;
+ }
+ }
+
+ return ireason;
+}
+
+ide_startstop_t ide_transfer_pc(ide_drive_t *drive, struct ide_atapi_pc *pc,
+ ide_handler_t *handler, unsigned int timeout,
+ ide_expiry_t *expiry)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ ide_startstop_t startstop;
+ u8 ireason;
+
+ if (ide_wait_stat(&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) {
+ printk(KERN_ERR "%s: Strange, packet command initiated yet "
+ "DRQ isn't asserted\n", drive->name);
+ return startstop;
+ }
+
+ ireason = hwif->INB(hwif->io_ports.nsect_addr);
+ if (drive->media == ide_tape && !drive->scsi)
+ ireason = ide_wait_ireason(drive, ireason);
+
+ if ((ireason & CD) == 0 || (ireason & IO)) {
+ printk(KERN_ERR "%s: (IO,CoD) != (0,1) while issuing "
+ "a packet command\n", drive->name);
+ return ide_do_reset(drive);
+ }
+
+ /* Set the interrupt routine */
+ ide_set_handler(drive, handler, timeout, expiry);
+
+ /* Begin DMA, if necessary */
+ if (pc->flags & PC_FLAG_DMA_OK) {
+ pc->flags |= PC_FLAG_DMA_IN_PROGRESS;
+ hwif->dma_ops->dma_start(drive);
+ }
+
+ /* Send the actual packet */
+ if ((pc->flags & PC_FLAG_ZIP_DRIVE) == 0)
+ hwif->output_data(drive, NULL, pc->c, 12);
+
+ return ide_started;
+}
+EXPORT_SYMBOL_GPL(ide_transfer_pc);
+
+ide_startstop_t ide_issue_pc(ide_drive_t *drive, struct ide_atapi_pc *pc,
+ ide_handler_t *handler, unsigned int timeout,
+ ide_expiry_t *expiry)
+{
+ ide_hwif_t *hwif = drive->hwif;
+ u16 bcount;
+ u8 dma = 0;
+
+ /* We haven't transferred any data yet */
+ pc->xferred = 0;
+ pc->cur_pos = pc->buf;
+
+ /* Request to transfer the entire buffer at once */
+ if (drive->media == ide_tape && !drive->scsi)
+ bcount = pc->req_xfer;
+ else
+ bcount = min(pc->req_xfer, 63 * 1024);
+
+ if (pc->flags & PC_FLAG_DMA_ERROR) {
+ pc->flags &= ~PC_FLAG_DMA_ERROR;
+ ide_dma_off(drive);
+ }
+
+ if ((pc->flags & PC_FLAG_DMA_OK) && drive->using_dma) {
+ if (drive->scsi)
+ hwif->sg_mapped = 1;
+ dma = !hwif->dma_ops->dma_setup(drive);
+ if (drive->scsi)
+ hwif->sg_mapped = 0;
+ }
+
+ if (!dma)
+ pc->flags &= ~PC_FLAG_DMA_OK;
+
+ ide_pktcmd_tf_load(drive, drive->scsi ? 0 : IDE_TFLAG_OUT_DEVICE,
+ bcount, dma);
+
+ /* Issue the packet command */
+ if (pc->flags & PC_FLAG_DRQ_INTERRUPT) {
+ ide_execute_command(drive, WIN_PACKETCMD, handler,
+ timeout, NULL);
+ return ide_started;
+ } else {
+ ide_execute_pkt_cmd(drive);
+ return (*handler)(drive);
+ }
+}
+EXPORT_SYMBOL_GPL(ide_issue_pc);
diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
index 68e7f19dc036..d99847157186 100644
--- a/drivers/ide/ide-cd.c
+++ b/drivers/ide/ide-cd.c
@@ -188,16 +188,6 @@ static void cdrom_analyze_sense_data(ide_drive_t *drive,
ide_cd_log_error(drive->name, failed_command, sense);
}
-/* Initialize a ide-cd packet command request */
-void ide_cd_init_rq(ide_drive_t *drive, struct request *rq)
-{
- struct cdrom_info *cd = drive->driver_data;
-
- ide_init_drive_cmd(rq);
- rq->cmd_type = REQ_TYPE_ATA_PC;
- rq->rq_disk = cd->disk;
-}
-
static void cdrom_queue_request_sense(ide_drive_t *drive, void *sense,
struct request *failed_command)
{
@@ -208,7 +198,9 @@ static void cdrom_queue_request_sense(ide_drive_t *drive, void *sense,
sense = &info->sense_data;
/* stuff the sense request in front of our current request */
- ide_cd_init_rq(drive, rq);
+ blk_rq_init(NULL, rq);
+ rq->cmd_type = REQ_TYPE_ATA_PC;
+ rq->rq_disk = info->disk;
rq->data = sense;
rq->cmd[0] = GPCMD_REQUEST_SENSE;
@@ -216,11 +208,12 @@ static void cdrom_queue_request_sense(ide_drive_t *drive, void *sense,
rq->data_len = 18;
rq->cmd_type = REQ_TYPE_SENSE;
+ rq->cmd_flags |= REQ_PREEMPT;
/* NOTE! Save the failed command in "rq->buffer" */
rq->buffer = (void *) failed_command;
- (void) ide_do_drive_cmd(drive, rq, ide_preempt);
+ ide_do_drive_cmd(drive, rq);
}
static void cdrom_end_request(ide_drive_t *drive, int uptodate)
@@ -537,8 +530,8 @@ static ide_startstop_t cdrom_start_packet_command(ide_drive_t *drive,
info->dma = !hwif->dma_ops->dma_setup(drive);
/* set up the controller registers */
- ide_pktcmd_tf_load(drive, IDE_TFLAG_OUT_NSECT | IDE_TFLAG_OUT_LBAL |
- IDE_TFLAG_NO_SELECT_MASK, xferlen, info->dma);
+ ide_pktcmd_tf_load(drive, IDE_TFLAG_OUT_NSECT | IDE_TFLAG_OUT_LBAL,
+ xferlen, info->dma);
if (info->cd_flags & IDE_CD_FLAG_DRQ_INTERRUPT) {
/* waiting for CDB interrupt, not DMA yet. */
@@ -838,34 +831,54 @@ static void ide_cd_request_sense_fixup(struct request *rq)
}
}
-int ide_cd_queue_pc(ide_drive_t *drive, struct request *rq)
+int ide_cd_queue_pc(ide_drive_t *drive, const unsigned char *cmd,
+ int write, void *buffer, unsigned *bufflen,
+ struct request_sense *sense, int timeout,
+ unsigned int cmd_flags)
{
- struct request_sense sense;
+ struct cdrom_info *info = drive->driver_data;
+ struct request_sense local_sense;
int retries = 10;
- unsigned int flags = rq->cmd_flags;
+ unsigned int flags = 0;
- if (rq->sense == NULL)
- rq->sense = &sense;
+ if (!sense)
+ sense = &local_sense;
/* start of retry loop */
do {
+ struct request *rq;
int error;
- unsigned long time = jiffies;
- rq->cmd_flags = flags;
- error = ide_do_drive_cmd(drive, rq, ide_wait);
- time = jiffies - time;
+ rq = blk_get_request(drive->queue, write, __GFP_WAIT);
+
+ memcpy(rq->cmd, cmd, BLK_MAX_CDB);
+ rq->cmd_type = REQ_TYPE_ATA_PC;
+ rq->sense = sense;
+ rq->cmd_flags |= cmd_flags;
+ rq->timeout = timeout;
+ if (buffer) {
+ rq->data = buffer;
+ rq->data_len = *bufflen;
+ }
+
+ error = blk_execute_rq(drive->queue, info->disk, rq, 0);
+
+ if (buffer)
+ *bufflen = rq->data_len;
+
+ flags = rq->cmd_flags;
+ blk_put_request(rq);
/*
* FIXME: we should probably abort/retry or something in case of
* failure.
*/
- if (rq->cmd_flags & REQ_FAILED) {
+ if (flags & REQ_FAILED) {
/*
* The request failed. Retry if it was due to a unit
* attention status (usually means media was changed).
*/
- struct request_sense *reqbuf = rq->sense;
+ struct request_sense *reqbuf = sense;
if (reqbuf->sense_key == UNIT_ATTENTION)
cdrom_saw_media_change(drive);
@@ -885,10 +898,10 @@ int ide_cd_queue_pc(ide_drive_t *drive, struct request *rq)
}
/* end of retry loop */
- } while ((rq->cmd_flags & REQ_FAILED) && retries >= 0);
+ } while ((flags & REQ_FAILED) && retries >= 0);
/* return an error if the command failed */
- return (rq->cmd_flags & REQ_FAILED) ? -EIO : 0;
+ return (flags & REQ_FAILED) ? -EIO : 0;
}
/*
@@ -1268,23 +1281,20 @@ static void msf_from_bcd(struct atapi_msf *msf)
int cdrom_check_status(ide_drive_t *drive, struct request_sense *sense)
{
- struct request req;
struct cdrom_info *info = drive->driver_data;
struct cdrom_device_info *cdi = &info->devinfo;
+ unsigned char cmd[BLK_MAX_CDB];
- ide_cd_init_rq(drive, &req);
-
- req.sense = sense;
- req.cmd[0] = GPCMD_TEST_UNIT_READY;
- req.cmd_flags |= REQ_QUIET;
+ memset(cmd, 0, BLK_MAX_CDB);
+ cmd[0] = GPCMD_TEST_UNIT_READY;
/*
* Sanyo 3 CD changer uses byte 7 of TEST_UNIT_READY to switch CDs
* instead of supporting the LOAD_UNLOAD opcode.
*/
- req.cmd[7] = cdi->sanyo_slot % 3;
+ cmd[7] = cdi->sanyo_slot % 3;
- return ide_cd_queue_pc(drive, &req);
+ return ide_cd_queue_pc(drive, cmd, 0, NULL, 0, sense, 0, REQ_QUIET);
}
static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity,
@@ -1297,17 +1307,14 @@ static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity,
} capbuf;
int stat;
- struct request req;
-
- ide_cd_init_rq(drive, &req);
+ unsigned char cmd[BLK_MAX_CDB];
+ unsigned len = sizeof(capbuf);
- req.sense = sense;
- req.cmd[0] = GPCMD_READ_CDVD_CAPACITY;
- req.data = (char *)&capbuf;
- req.data_len = sizeof(capbuf);
- req.cmd_flags |= REQ_QUIET;
+ memset(cmd, 0, BLK_MAX_CDB);
+ cmd[0] = GPCMD_READ_CDVD_CAPACITY;
- stat = ide_cd_queue_pc(drive, &req);
+ stat = ide_cd_queue_pc(drive, cmd, 0, &capbuf, &len, sense, 0,
+ REQ_QUIET);
if (stat == 0) {
*capacity = 1 + be32_to_cpu(capbuf.lba);
*sectors_per_frame =
@@ -1321,24 +1328,20 @@ static int cdrom_read_tocentry(ide_drive_t *drive, int trackno, int msf_flag,
int format, char *buf, int buflen,
struct request_sense *sense)
{
- struct request req;
+ unsigned char cmd[BLK_MAX_CDB];
- ide_cd_init_rq(drive, &req);
+ memset(cmd, 0, BLK_MAX_CDB);
- req.sense = sense;
- req.data = buf;
- req.data_len = buflen;
- req.cmd_flags |= REQ_QUIET;
- req.cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
- req.cmd[6] = trackno;
- req.cmd[7] = (buflen >> 8);
- req.cmd[8] = (buflen & 0xff);
- req.cmd[9] = (format << 6);
+ cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
+ cmd[6] = trackno;
+ cmd[7] = (buflen >> 8);
+ cmd[8] = (buflen & 0xff);
+ cmd[9] = (format << 6);
if (msf_flag)
- req.cmd[1] = 2;
+ cmd[1] = 2;
- return ide_cd_queue_pc(drive, &req);
+ return ide_cd_queue_pc(drive, cmd, 0, buf, &buflen, sense, 0, REQ_QUIET);
}
/* Try to read the entire TOC for the disk into our internal buffer. */
@@ -2103,11 +2106,6 @@ static int ide_cd_probe(ide_drive_t *drive)
goto failed;
}
}
- if (drive->scsi) {
- printk(KERN_INFO "ide-cd: passing drive %s to ide-scsi "
- "emulation.\n", drive->name);
- goto failed;
- }
info = kzalloc(sizeof(struct cdrom_info), GFP_KERNEL);
if (info == NULL) {
printk(KERN_ERR "%s: Can't allocate a cdrom structure\n",
diff --git a/drivers/ide/ide-cd.h b/drivers/ide/ide-cd.h
index a58801c4484d..fe0ea36e4124 100644
--- a/drivers/ide/ide-cd.h
+++ b/drivers/ide/ide-cd.h
@@ -143,8 +143,8 @@ struct cdrom_info {
void ide_cd_log_error(const char *, struct request *, struct request_sense *);
/* ide-cd.c functions used by ide-cd_ioctl.c */
-void ide_cd_init_rq(ide_drive_t *, struct request *);
-int ide_cd_queue_pc(ide_drive_t *, struct request *);
+int ide_cd_queue_pc(ide_drive_t *, const unsigned char *, int, void *,
+ unsigned *, struct request_sense *, int, unsigned int);
int ide_cd_read_toc(ide_drive_t *, struct request_sense *);
int ide_cdrom_get_capabilities(ide_drive_t *, u8 *);
void ide_cdrom_update_speed(ide_drive_t *, u8 *);
diff --git a/drivers/ide/ide-cd_ioctl.c b/drivers/ide/ide-cd_ioctl.c
index 6d147ce6782f..24d002addf73 100644
--- a/drivers/ide/ide-cd_ioctl.c
+++ b/drivers/ide/ide-cd_ioctl.c
@@ -104,8 +104,8 @@ int cdrom_eject(ide_drive_t *drive, int ejectflag,
{
struct cdrom_info *cd = drive->driver_data;
struct cdrom_device_info *cdi = &cd->devinfo;
- struct request req;
char loej = 0x02;
+ unsigned char cmd[BLK_MAX_CDB];
if ((cd->cd_flags & IDE_CD_FLAG_NO_EJECT) && !ejectflag)
return -EDRIVE_CANT_DO_THIS;
@@ -114,17 +114,16 @@ int cdrom_eject(ide_drive_t *drive, int ejectflag,
if ((cd->cd_flags & IDE_CD_FLAG_DOOR_LOCKED) && ejectflag)
return 0;
- ide_cd_init_rq(drive, &req);
-
/* only tell drive to close tray if open, if it can do that */
if (ejectflag && (cdi->mask & CDC_CLOSE_TRAY))
loej = 0;
- req.sense = sense;
- req.cmd[0] = GPCMD_START_STOP_UNIT;
- req.cmd[4] = loej | (ejectflag != 0);
+ memset(cmd, 0, BLK_MAX_CDB);
+
+ cmd[0] = GPCMD_START_STOP_UNIT;
+ cmd[4] = loej | (ejectflag != 0);
- return ide_cd_queue_pc(drive, &req);
+ return ide_cd_queue_pc(drive, cmd, 0, NULL, 0, sense, 0, 0);
}
/* Lock the door if LOCKFLAG is nonzero; unlock it otherwise. */
@@ -134,7 +133,6 @@ int ide_cd_lockdoor(ide_drive_t *drive, int lockflag,
{
struct cdrom_info *cd = drive->driver_data;
struct request_sense my_sense;
- struct request req;
int stat;
if (sense == NULL)
@@ -144,11 +142,15 @@ int ide_cd_lockdoor(ide_drive_t *drive, int lockflag,
if (cd->cd_flags & IDE_CD_FLAG_NO_DOORLOCK) {
stat = 0;
} else {
- ide_cd_init_rq(drive, &req);
- req.sense = sense;
- req.cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL;
- req.cmd[4] = lockflag ? 1 : 0;
- stat = ide_cd_queue_pc(drive, &req);
+ unsigned char cmd[BLK_MAX_CDB];
+
+ memset(cmd, 0, BLK_MAX_CDB);
+
+ cmd[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL;
+ cmd[4] = lockflag ? 1 : 0;
+
+ stat = ide_cd_queue_pc(drive, cmd, 0, NULL, 0,
+ sense, 0, 0);
}
/* If we got an illegal field error, the drive
@@ -206,32 +208,30 @@ int ide_cdrom_select_speed(struct cdrom_device_info *cdi, int speed)
{
ide_drive_t *drive = cdi->handle;
struct cdrom_info *cd = drive->driver_data;
- struct request rq;
struct request_sense sense;
u8 buf[ATAPI_CAPABILITIES_PAGE_SIZE];
int stat;
-
- ide_cd_init_rq(drive, &rq);
-
- rq.sense = &sense;
+ unsigned char cmd[BLK_MAX_CDB];
if (speed == 0)
speed = 0xffff; /* set to max */
else
speed *= 177; /* Nx to kbytes/s */
- rq.cmd[0] = GPCMD_SET_SPEED;
+ memset(cmd, 0, BLK_MAX_CDB);
+
+ cmd[0] = GPCMD_SET_SPEED;
/* Read Drive speed in kbytes/second MSB/LSB */
- rq.cmd[2] = (speed >> 8) & 0xff;
- rq.cmd[3] = speed & 0xff;
+ cmd[2] = (speed >> 8) & 0xff;
+ cmd[3] = speed & 0xff;
if ((cdi->mask & (CDC_CD_R | CDC_CD_RW | CDC_DVD_R)) !=
(CDC_CD_R | CDC_CD_RW | CDC_DVD_R)) {
/* Write Drive speed in kbytes/second MSB/LSB */
- rq.cmd[4] = (speed >> 8) & 0xff;
- rq.cmd[5] = speed & 0xff;
+ cmd[4] = (speed >> 8) & 0xff;
+ cmd[5] = speed & 0xff;
}
- stat = ide_cd_queue_pc(drive, &rq);
+ stat = ide_cd_queue_pc(drive, cmd, 0, NULL, 0, &sense, 0, 0);
if (!ide_cdrom_get_capabilities(drive, buf)) {
ide_cdrom_update_speed(drive, buf);
@@ -268,21 +268,19 @@ int ide_cdrom_get_mcn(struct cdrom_device_info *cdi,
{
ide_drive_t *drive = cdi->handle;
int stat, mcnlen;
- struct request rq;
char buf[24];
+ unsigned char cmd[BLK_MAX_CDB];
+ unsigned len = sizeof(buf);
- ide_cd_init_rq(drive, &rq);
+ memset(cmd, 0, BLK_MAX_CDB);
- rq.data = buf;
- rq.data_len = sizeof(buf);
+ cmd[0] = GPCMD_READ_SUBCHANNEL;
+ cmd[1] = 2; /* MSF addressing */
+ cmd[2] = 0x40; /* request subQ data */
+ cmd[3] = 2; /* format */
+ cmd[8] = len;
- rq.cmd[0] = GPCMD_READ_SUBCHANNEL;
- rq.cmd[1] = 2; /* MSF addressing */
- rq.cmd[2] = 0x40; /* request subQ data */
- rq.cmd[3] = 2; /* format */
- rq.cmd[8] = sizeof(buf);
-
- stat = ide_cd_queue_pc(drive, &rq);
+ stat = ide_cd_queue_pc(drive, cmd, 0, buf, &len, NULL, 0, 0);
if (stat)
return stat;
@@ -298,14 +296,14 @@ int ide_cdrom_reset(struct cdrom_device_info *cdi)
ide_drive_t *drive = cdi->handle;
struct cdrom_info *cd = drive->driver_data;
struct request_sense sense;
- struct request req;
+ struct request *rq;
int ret;
- ide_cd_init_rq(drive, &req);
- req.cmd_type = REQ_TYPE_SPECIAL;
- req.cmd_flags = REQ_QUIET;
- ret = ide_do_drive_cmd(drive, &req, ide_wait);
-
+ rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
+ rq->cmd_type = REQ_TYPE_SPECIAL;
+ rq->cmd_flags = REQ_QUIET;
+ ret = blk_execute_rq(drive->queue, cd->disk, rq, 0);
+ blk_put_request(rq);
/*
* A reset will unlock the door. If it was previously locked,
* lock it again.
@@ -351,8 +349,8 @@ static int ide_cd_fake_play_trkind(ide_drive_t *drive, void *arg)
struct atapi_toc_entry *first_toc, *last_toc;
unsigned long lba_start, lba_end;
int stat;
- struct request rq;
struct request_sense sense;
+ unsigned char cmd[BLK_MAX_CDB];
stat = ide_cd_get_toc_entry(drive, ti->cdti_trk0, &first_toc);
if (stat)
@@ -370,14 +368,13 @@ static int ide_cd_fake_play_trkind(ide_drive_t *drive, void *arg)
if (lba_end <= lba_start)
return -EINVAL;
- ide_cd_init_rq(drive, &rq);
+ memset(cmd, 0, BLK_MAX_CDB);
- rq.sense = &sense;
- rq.cmd[0] = GPCMD_PLAY_AUDIO_MSF;
- lba_to_msf(lba_start, &rq.cmd[3], &rq.cmd[4], &rq.cmd[5]);
- lba_to_msf(lba_end - 1, &rq.cmd[6], &rq.cmd[7], &rq.cmd[8]);
+ cmd[0] = GPCMD_PLAY_AUDIO_MSF;
+ lba_to_msf(lba_start, &cmd[3], &cmd[4], &cmd[5]);
+ lba_to_msf(lba_end - 1, &cmd[6], &cmd[7], &cmd[8]);
- return ide_cd_queue_pc(drive, &rq);
+ return ide_cd_queue_pc(drive, cmd, 0, NULL, 0, &sense, 0, 0);
}
static int ide_cd_read_tochdr(ide_drive_t *drive, void *arg)
@@ -447,8 +444,9 @@ int ide_cdrom_audio_ioctl(struct cdrom_device_info *cdi,
int ide_cdrom_packet(struct cdrom_device_info *cdi,
struct packet_command *cgc)
{
- struct request req;
ide_drive_t *drive = cdi->handle;
+ unsigned int flags = 0;
+ unsigned len = cgc->buflen;
if (cgc->timeout <= 0)
cgc->timeout = ATAPI_WAIT_PC;
@@ -456,24 +454,21 @@ int ide_cdrom_packet(struct cdrom_device_info *cdi,
/* here we queue the commands from the uniform CD-ROM
layer. the packet must be complete, as we do not
touch it at all. */
- ide_cd_init_rq(drive, &req);
if (cgc->data_direction == CGC_DATA_WRITE)
- req.cmd_flags |= REQ_RW;
+ flags |= REQ_RW;
- memcpy(req.cmd, cgc->cmd, CDROM_PACKET_SIZE);
if (cgc->sense)
memset(cgc->sense, 0, sizeof(struct request_sense));
- req.data = cgc->buffer;
- req.data_len = cgc->buflen;
- req.timeout = cgc->timeout;
if (cgc->quiet)
- req.cmd_flags |= REQ_QUIET;
+ flags |= REQ_QUIET;
- req.sense = cgc->sense;
- cgc->stat = ide_cd_queue_pc(drive, &req);
+ cgc->stat = ide_cd_queue_pc(drive, cgc->cmd,
+ cgc->data_direction == CGC_DATA_WRITE,
+ cgc->buffer, &len,
+ cgc->sense, cgc->timeout, flags);
if (!cgc->stat)
- cgc->buflen -= req.data_len;
+ cgc->buflen -= len;
return cgc->stat;
}
diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c
index 8e08d083fce9..5f49a4ae9dd8 100644
--- a/drivers/ide/ide-disk.c
+++ b/drivers/ide/ide-disk.c
@@ -198,8 +198,7 @@ static ide_startstop_t __ide_do_rw_disk(ide_drive_t *drive, struct request *rq,
}
memset(&task, 0, sizeof(task));
- task.tf_flags = IDE_TFLAG_NO_SELECT_MASK; /* FIXME? */
- task.tf_flags |= (IDE_TFLAG_TF | IDE_TFLAG_DEVICE);
+ task.tf_flags = IDE_TFLAG_TF | IDE_TFLAG_DEVICE;
if (drive->select.b.lba) {
if (lba48) {
@@ -617,7 +616,8 @@ static void idedisk_prepare_flush(struct request_queue *q, struct request *rq)
*/
static int set_multcount(ide_drive_t *drive, int arg)
{
- struct request rq;
+ struct request *rq;
+ int error;
if (arg < 0 || arg > drive->id->max_multsect)
return -EINVAL;
@@ -625,12 +625,13 @@ static int set_multcount(ide_drive_t *drive, int arg)
if (drive->special.b.set_multmode)
return -EBUSY;
- ide_init_drive_cmd(&rq);
- rq.cmd_type = REQ_TYPE_ATA_TASKFILE;
+ rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
+ rq->cmd_type = REQ_TYPE_ATA_TASKFILE;
drive->mult_req = arg;
drive->special.b.set_multmode = 1;
- (void)ide_do_drive_cmd(drive, &rq, ide_wait);
+ error = blk_execute_rq(drive->queue, NULL, rq, 0);
+ blk_put_request(rq);
return (drive->mult_count == arg) ? 0 : -EIO;
}
diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c
index 653b1ade13d3..7ee44f86bc54 100644
--- a/drivers/ide/ide-dma.c
+++ b/drivers/ide/ide-dma.c
@@ -463,7 +463,7 @@ int ide_dma_setup(ide_drive_t *drive)
}
/* PRD table */
- if (hwif->mmio)
+ if (hwif->host_flags & IDE_HFLAG_MMIO)
writel(hwif->dmatable_dma,
(void __iomem *)(hwif->dma_base + ATA_DMA_TABLE_OFS));
else
@@ -692,7 +692,7 @@ static int ide_tune_dma(ide_drive_t *drive)
ide_hwif_t *hwif = drive->hwif;
u8 speed;
- if (noautodma || drive->nodma || (drive->id->capability & 1) == 0)
+ if (drive->nodma || (drive->id->capability & 1) == 0)
return 0;
/* consult the list of known "bad" drives */
diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c
index f05fbc2bd7a8..b3689437269f 100644
--- a/drivers/ide/ide-floppy.c
+++ b/drivers/ide/ide-floppy.c
@@ -286,11 +286,12 @@ static void idefloppy_queue_pc_head(ide_drive_t *drive, struct ide_atapi_pc *pc,
{
struct ide_floppy_obj *floppy = drive->driver_data;
- ide_init_drive_cmd(rq);
+ blk_rq_init(NULL, rq);
rq->buffer = (char *) pc;
rq->cmd_type = REQ_TYPE_SPECIAL;
+ rq->cmd_flags |= REQ_PREEMPT;
rq->rq_disk = floppy->disk;
- (void) ide_do_drive_cmd(drive, rq, ide_preempt);
+ ide_do_drive_cmd(drive, rq);
}
static struct ide_atapi_pc *idefloppy_next_pc_storage(ide_drive_t *drive)
@@ -311,50 +312,41 @@ static struct request *idefloppy_next_rq_storage(ide_drive_t *drive)
return (&floppy->rq_stack[floppy->rq_stack_index++]);
}
-static void idefloppy_request_sense_callback(ide_drive_t *drive)
+static void ide_floppy_callback(ide_drive_t *drive)
{
idefloppy_floppy_t *floppy = drive->driver_data;
- u8 *buf = floppy->pc->buf;
+ struct ide_atapi_pc *pc = floppy->pc;
+ int uptodate = pc->error ? 0 : 1;
debug_log("Reached %s\n", __func__);
- if (!floppy->pc->error) {
- floppy->sense_key = buf[2] & 0x0F;
- floppy->asc = buf[12];
- floppy->ascq = buf[13];
- floppy->progress_indication = buf[15] & 0x80 ?
- (u16)get_unaligned((u16 *)&buf[16]) : 0x10000;
+ if (floppy->failed_pc == pc)
+ floppy->failed_pc = NULL;
- if (floppy->failed_pc)
- debug_log("pc = %x, sense key = %x, asc = %x,"
- " ascq = %x\n",
- floppy->failed_pc->c[0],
- floppy->sense_key,
- floppy->asc,
- floppy->ascq);
- else
- debug_log("sense key = %x, asc = %x, ascq = %x\n",
- floppy->sense_key,
- floppy->asc,
- floppy->ascq);
+ if (pc->c[0] == GPCMD_READ_10 || pc->c[0] == GPCMD_WRITE_10 ||
+ (pc->rq && blk_pc_request(pc->rq)))
+ uptodate = 1; /* FIXME */
+ else if (pc->c[0] == GPCMD_REQUEST_SENSE) {
+ u8 *buf = floppy->pc->buf;
+ if (!pc->error) {
+ floppy->sense_key = buf[2] & 0x0F;
+ floppy->asc = buf[12];
+ floppy->ascq = buf[13];
+ floppy->progress_indication = buf[15] & 0x80 ?
+ (u16)get_unaligned((u16 *)&buf[16]) : 0x10000;
- idefloppy_end_request(drive, 1, 0);
- } else {
- printk(KERN_ERR "Error in REQUEST SENSE itself - Aborting"
- " request!\n");
- idefloppy_end_request(drive, 0, 0);
- }
-}
+ if (floppy->failed_pc)
+ debug_log("pc = %x, ", floppy->failed_pc->c[0]);
-/* General packet command callback function. */
-static void idefloppy_pc_callback(ide_drive_t *drive)
-{
- idefloppy_floppy_t *floppy = drive->driver_data;
-
- debug_log("Reached %s\n", __func__);
+ debug_log("sense key = %x, asc = %x, ascq = %x\n",
+ floppy->sense_key, floppy->asc, floppy->ascq);
+ } else
+ printk(KERN_ERR "Error in REQUEST SENSE itself - "
+ "Aborting request!\n");
+ }
- idefloppy_end_request(drive, floppy->pc->error ? 0 : 1, 0);
+ idefloppy_end_request(drive, uptodate, 0);
}
static void idefloppy_init_pc(struct ide_atapi_pc *pc)
@@ -365,7 +357,7 @@ static void idefloppy_init_pc(struct ide_atapi_pc *pc)
pc->req_xfer = 0;
pc->buf = pc->pc_buf;
pc->buf_size = IDEFLOPPY_PC_BUFFER_SIZE;
- pc->idefloppy_callback = &idefloppy_pc_callback;
+ pc->callback = ide_floppy_callback;
}
static void idefloppy_create_request_sense_cmd(struct ide_atapi_pc *pc)
@@ -374,7 +366,6 @@ static void idefloppy_create_request_sense_cmd(struct ide_atapi_pc *pc)
pc->c[0] = GPCMD_REQUEST_SENSE;
pc->c[4] = 255;
pc->req_xfer = 18;
- pc->idefloppy_callback = &idefloppy_request_sense_callback;
}
/*
@@ -397,174 +388,19 @@ static void idefloppy_retry_pc(ide_drive_t *drive)
static ide_startstop_t idefloppy_pc_intr(ide_drive_t *drive)
{
idefloppy_floppy_t *floppy = drive->driver_data;
- ide_hwif_t *hwif = drive->hwif;
- struct ide_atapi_pc *pc = floppy->pc;
- struct request *rq = pc->rq;
- xfer_func_t *xferfunc;
- unsigned int temp;
- int dma_error = 0;
- u16 bcount;
- u8 stat, ireason;
-
- debug_log("Reached %s interrupt handler\n", __func__);
-
- if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) {
- dma_error = hwif->dma_ops->dma_end(drive);
- if (dma_error) {
- printk(KERN_ERR "%s: DMA %s error\n", drive->name,
- rq_data_dir(rq) ? "write" : "read");
- pc->flags |= PC_FLAG_DMA_ERROR;
- } else {
- pc->xferred = pc->req_xfer;
- idefloppy_update_buffers(drive, pc);
- }
- debug_log("DMA finished\n");
- }
-
- /* Clear the interrupt */
- stat = ide_read_status(drive);
-
- /* No more interrupts */
- if ((stat & DRQ_STAT) == 0) {
- debug_log("Packet command completed, %d bytes transferred\n",
- pc->xferred);
- pc->flags &= ~PC_FLAG_DMA_IN_PROGRESS;
-
- local_irq_enable_in_hardirq();
-
- if ((stat & ERR_STAT) || (pc->flags & PC_FLAG_DMA_ERROR)) {
- /* Error detected */
- debug_log("%s: I/O error\n", drive->name);
- rq->errors++;
- if (pc->c[0] == GPCMD_REQUEST_SENSE) {
- printk(KERN_ERR "ide-floppy: I/O error in "
- "request sense command\n");
- return ide_do_reset(drive);
- }
- /* Retry operation */
- idefloppy_retry_pc(drive);
- /* queued, but not started */
- return ide_stopped;
- }
- pc->error = 0;
- if (floppy->failed_pc == pc)
- floppy->failed_pc = NULL;
- /* Command finished - Call the callback function */
- pc->idefloppy_callback(drive);
- return ide_stopped;
- }
-
- if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) {
- pc->flags &= ~PC_FLAG_DMA_IN_PROGRESS;
- printk(KERN_ERR "ide-floppy: The floppy wants to issue "
- "more interrupts in DMA mode\n");
- ide_dma_off(drive);
- return ide_do_reset(drive);
- }
-
- /* Get the number of bytes to transfer */
- bcount = (hwif->INB(hwif->io_ports.lbah_addr) << 8) |
- hwif->INB(hwif->io_ports.lbam_addr);
- /* on this interrupt */
- ireason = hwif->INB(hwif->io_ports.nsect_addr);
-
- if (ireason & CD) {
- printk(KERN_ERR "ide-floppy: CoD != 0 in %s\n", __func__);
- return ide_do_reset(drive);
- }
- if (((ireason & IO) == IO) == !!(pc->flags & PC_FLAG_WRITING)) {
- /* Hopefully, we will never get here */
- printk(KERN_ERR "ide-floppy: We wanted to %s, ",
- (ireason & IO) ? "Write" : "Read");
- printk(KERN_ERR "but the floppy wants us to %s !\n",
- (ireason & IO) ? "Read" : "Write");
- return ide_do_reset(drive);
- }
- if (!(pc->flags & PC_FLAG_WRITING)) {
- /* Reading - Check that we have enough space */
- temp = pc->xferred + bcount;
- if (temp > pc->req_xfer) {
- if (temp > pc->buf_size) {
- printk(KERN_ERR "ide-floppy: The floppy wants "
- "to send us more data than expected "
- "- discarding data\n");
- ide_pad_transfer(drive, 0, bcount);
-
- ide_set_handler(drive,
- &idefloppy_pc_intr,
- IDEFLOPPY_WAIT_CMD,
- NULL);
- return ide_started;
- }
- debug_log("The floppy wants to send us more data than"
- " expected - allowing transfer\n");
- }
- }
- if (pc->flags & PC_FLAG_WRITING)
- xferfunc = hwif->output_data;
- else
- xferfunc = hwif->input_data;
-
- if (pc->buf)
- xferfunc(drive, NULL, pc->cur_pos, bcount);
- else
- ide_floppy_io_buffers(drive, pc, bcount,
- !!(pc->flags & PC_FLAG_WRITING));
-
- /* Update the current position */
- pc->xferred += bcount;
- pc->cur_pos += bcount;
-
- /* And set the interrupt handler again */
- ide_set_handler(drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD, NULL);
- return ide_started;
-}
-
-/*
- * This is the original routine that did the packet transfer.
- * It fails at high speeds on the Iomega ZIP drive, so there's a slower version
- * for that drive below. The algorithm is chosen based on drive type
- */
-static ide_startstop_t idefloppy_transfer_pc(ide_drive_t *drive)
-{
- ide_hwif_t *hwif = drive->hwif;
- ide_startstop_t startstop;
- idefloppy_floppy_t *floppy = drive->driver_data;
- u8 ireason;
-
- if (ide_wait_stat(&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) {
- printk(KERN_ERR "ide-floppy: Strange, packet command "
- "initiated yet DRQ isn't asserted\n");
- return startstop;
- }
- ireason = hwif->INB(hwif->io_ports.nsect_addr);
- if ((ireason & CD) == 0 || (ireason & IO)) {
- printk(KERN_ERR "ide-floppy: (IO,CoD) != (0,1) while "
- "issuing a packet command\n");
- return ide_do_reset(drive);
- }
- /* Set the interrupt routine */
- ide_set_handler(drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD, NULL);
-
- /* Send the actual packet */
- hwif->output_data(drive, NULL, floppy->pc->c, 12);
-
- return ide_started;
+ return ide_pc_intr(drive, floppy->pc, idefloppy_pc_intr,
+ IDEFLOPPY_WAIT_CMD, NULL, idefloppy_update_buffers,
+ idefloppy_retry_pc, NULL, ide_floppy_io_buffers);
}
-
/*
* What we have here is a classic case of a top half / bottom half interrupt
* service routine. In interrupt mode, the device sends an interrupt to signal
* that it is ready to receive a packet. However, we need to delay about 2-3
* ticks before issuing the packet or we gets in trouble.
- *
- * So, follow carefully. transfer_pc1 is called as an interrupt (or directly).
- * In either case, when the device says it's ready for a packet, we schedule
- * the packet transfer to occur about 2-3 ticks later in transfer_pc2.
*/
-static int idefloppy_transfer_pc2(ide_drive_t *drive)
+static int idefloppy_transfer_pc(ide_drive_t *drive)
{
idefloppy_floppy_t *floppy = drive->driver_data;
@@ -575,24 +411,19 @@ static int idefloppy_transfer_pc2(ide_drive_t *drive)
return IDEFLOPPY_WAIT_CMD;
}
-static ide_startstop_t idefloppy_transfer_pc1(ide_drive_t *drive)
+
+/*
+ * Called as an interrupt (or directly). When the device says it's ready for a
+ * packet, we schedule the packet transfer to occur about 2-3 ticks later in
+ * transfer_pc.
+ */
+static ide_startstop_t idefloppy_start_pc_transfer(ide_drive_t *drive)
{
- ide_hwif_t *hwif = drive->hwif;
idefloppy_floppy_t *floppy = drive->driver_data;
- ide_startstop_t startstop;
- u8 ireason;
+ struct ide_atapi_pc *pc = floppy->pc;
+ ide_expiry_t *expiry;
+ unsigned int timeout;
- if (ide_wait_stat(&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) {
- printk(KERN_ERR "ide-floppy: Strange, packet command "
- "initiated yet DRQ isn't asserted\n");
- return startstop;
- }
- ireason = hwif->INB(hwif->io_ports.nsect_addr);
- if ((ireason & CD) == 0 || (ireason & IO)) {
- printk(KERN_ERR "ide-floppy: (IO,CoD) != (0,1) "
- "while issuing a packet command\n");
- return ide_do_reset(drive);
- }
/*
* The following delay solves a problem with ATAPI Zip 100 drives
* where the Busy flag was apparently being deasserted before the
@@ -601,10 +432,15 @@ static ide_startstop_t idefloppy_transfer_pc1(ide_drive_t *drive)
* 40 and 50msec work well. idefloppy_pc_intr will not be actually
* used until after the packet is moved in about 50 msec.
*/
+ if (pc->flags & PC_FLAG_ZIP_DRIVE) {
+ timeout = floppy->ticks;
+ expiry = &idefloppy_transfer_pc;
+ } else {
+ timeout = IDEFLOPPY_WAIT_CMD;
+ expiry = NULL;
+ }
- ide_set_handler(drive, &idefloppy_pc_intr, floppy->ticks,
- &idefloppy_transfer_pc2);
- return ide_started;
+ return ide_transfer_pc(drive, pc, idefloppy_pc_intr, timeout, expiry);
}
static void ide_floppy_report_error(idefloppy_floppy_t *floppy,
@@ -627,10 +463,6 @@ static ide_startstop_t idefloppy_issue_pc(ide_drive_t *drive,
struct ide_atapi_pc *pc)
{
idefloppy_floppy_t *floppy = drive->driver_data;
- ide_hwif_t *hwif = drive->hwif;
- ide_handler_t *pkt_xfer_routine;
- u16 bcount;
- u8 dma;
if (floppy->failed_pc == NULL &&
pc->c[0] != GPCMD_REQUEST_SENSE)
@@ -645,65 +477,16 @@ static ide_startstop_t idefloppy_issue_pc(ide_drive_t *drive,
pc->error = IDEFLOPPY_ERROR_GENERAL;
floppy->failed_pc = NULL;
- pc->idefloppy_callback(drive);
+ pc->callback(drive);
return ide_stopped;
}
debug_log("Retry number - %d\n", pc->retries);
pc->retries++;
- /* We haven't transferred any data yet */
- pc->xferred = 0;
- pc->cur_pos = pc->buf;
- bcount = min(pc->req_xfer, 63 * 1024);
-
- if (pc->flags & PC_FLAG_DMA_ERROR) {
- pc->flags &= ~PC_FLAG_DMA_ERROR;
- ide_dma_off(drive);
- }
- dma = 0;
-
- if ((pc->flags & PC_FLAG_DMA_RECOMMENDED) && drive->using_dma)
- dma = !hwif->dma_ops->dma_setup(drive);
- ide_pktcmd_tf_load(drive, IDE_TFLAG_NO_SELECT_MASK |
- IDE_TFLAG_OUT_DEVICE, bcount, dma);
-
- if (dma) {
- /* Begin DMA, if necessary */
- pc->flags |= PC_FLAG_DMA_IN_PROGRESS;
- hwif->dma_ops->dma_start(drive);
- }
-
- /* Can we transfer the packet when we get the interrupt or wait? */
- if (floppy->flags & IDEFLOPPY_FLAG_ZIP_DRIVE) {
- /* wait */
- pkt_xfer_routine = &idefloppy_transfer_pc1;
- } else {
- /* immediate */
- pkt_xfer_routine = &idefloppy_transfer_pc;
- }
-
- if (floppy->flags & IDEFLOPPY_FLAG_DRQ_INTERRUPT) {
- /* Issue the packet command */
- ide_execute_command(drive, WIN_PACKETCMD,
- pkt_xfer_routine,
- IDEFLOPPY_WAIT_CMD,
- NULL);
- return ide_started;
- } else {
- /* Issue the packet command */
- ide_execute_pkt_cmd(drive);
- return (*pkt_xfer_routine) (drive);
- }
-}
-
-static void idefloppy_rw_callback(ide_drive_t *drive)
-{
- debug_log("Reached %s\n", __func__);
-
- idefloppy_end_request(drive, 1, 0);
- return;
+ return ide_issue_pc(drive, pc, idefloppy_start_pc_transfer,
+ IDEFLOPPY_WAIT_CMD, NULL);
}
static void idefloppy_create_prevent_cmd(struct ide_atapi_pc *pc, int prevent)
@@ -800,21 +583,19 @@ static void idefloppy_create_rw_cmd(idefloppy_floppy_t *floppy,
put_unaligned(cpu_to_be16(blocks), (unsigned short *)&pc->c[7]);
put_unaligned(cpu_to_be32(block), (unsigned int *) &pc->c[2]);
- pc->idefloppy_callback = &idefloppy_rw_callback;
pc->rq = rq;
pc->b_count = cmd == READ ? 0 : rq->bio->bi_size;
if (rq->cmd_flags & REQ_RW)
pc->flags |= PC_FLAG_WRITING;
pc->buf = NULL;
pc->req_xfer = pc->buf_size = blocks * floppy->block_size;
- pc->flags |= PC_FLAG_DMA_RECOMMENDED;
+ pc->flags |= PC_FLAG_DMA_OK;
}
static void idefloppy_blockpc_cmd(idefloppy_floppy_t *floppy,
struct ide_atapi_pc *pc, struct request *rq)
{
idefloppy_init_pc(pc);
- pc->idefloppy_callback = &idefloppy_rw_callback;
memcpy(pc->c, rq->cmd, sizeof(pc->c));
pc->rq = rq;
pc->b_count = rq->data_len;
@@ -822,7 +603,7 @@ static void idefloppy_blockpc_cmd(idefloppy_floppy_t *floppy,
pc->flags |= PC_FLAG_WRITING;
pc->buf = rq->data;
if (rq->bio)
- pc->flags |= PC_FLAG_DMA_RECOMMENDED;
+ pc->flags |= PC_FLAG_DMA_OK;
/*
* possibly problematic, doesn't look like ide-floppy correctly
* handled scattered requests if dma fails...
@@ -875,7 +656,14 @@ static ide_startstop_t idefloppy_do_request(ide_drive_t *drive,
return ide_stopped;
}
+ if (floppy->flags & IDEFLOPPY_FLAG_DRQ_INTERRUPT)
+ pc->flags |= PC_FLAG_DRQ_INTERRUPT;
+
+ if (floppy->flags & IDEFLOPPY_FLAG_ZIP_DRIVE)
+ pc->flags |= PC_FLAG_ZIP_DRIVE;
+
pc->rq = rq;
+
return idefloppy_issue_pc(drive, pc);
}
@@ -886,14 +674,16 @@ static ide_startstop_t idefloppy_do_request(ide_drive_t *drive,
static int idefloppy_queue_pc_tail(ide_drive_t *drive, struct ide_atapi_pc *pc)
{
struct ide_floppy_obj *floppy = drive->driver_data;
- struct request rq;
+ struct request *rq;
+ int error;
- ide_init_drive_cmd(&rq);
- rq.buffer = (char *) pc;
- rq.cmd_type = REQ_TYPE_SPECIAL;
- rq.rq_disk = floppy->disk;
+ rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
+ rq->buffer = (char *) pc;
+ rq->cmd_type = REQ_TYPE_SPECIAL;
+ error = blk_execute_rq(drive->queue, floppy->disk, rq, 0);
+ blk_put_request(rq);
- return ide_do_drive_cmd(drive, &rq, ide_wait);
+ return error;
}
/*
@@ -1622,11 +1412,6 @@ static int ide_floppy_probe(ide_drive_t *drive)
" of ide-floppy\n", drive->name);
goto failed;
}
- if (drive->scsi) {
- printk(KERN_INFO "ide-floppy: passing drive %s to ide-scsi"
- " emulation.\n", drive->name);
- goto failed;
- }
floppy = kzalloc(sizeof(idefloppy_floppy_t), GFP_KERNEL);
if (!floppy) {
printk(KERN_ERR "ide-floppy: %s: Can't allocate a floppy"
diff --git a/drivers/ide/ide-generic.c b/drivers/ide/ide-generic.c
index a6073e248f45..9134488ac043 100644
--- a/drivers/ide/ide-generic.c
+++ b/drivers/ide/ide-generic.c
@@ -125,6 +125,7 @@ static int __init ide_generic_init(void)
memset(&hw, 0, sizeof(hw));
ide_std_init_ports(&hw, io_addr, io_addr + 0x206);
hw.irq = ide_default_irq(io_addr);
+ hw.chipset = ide_generic;
ide_init_port_hw(hwif, &hw);
idx[i] = i;
diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c
index 696525342e9a..28057747c1f8 100644
--- a/drivers/ide/ide-io.c
+++ b/drivers/ide/ide-io.c
@@ -358,31 +358,6 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err)
EXPORT_SYMBOL(ide_end_drive_cmd);
-/**
- * try_to_flush_leftover_data - flush junk
- * @drive: drive to flush
- *
- * try_to_flush_leftover_data() is invoked in response to a drive
- * unexpectedly having its DRQ_STAT bit set. As an alternative to
- * resetting the drive, this routine tries to clear the condition
- * by read a sector's worth of data from the drive. Of course,
- * this may not help if the drive is *waiting* for data from *us*.
- */
-static void try_to_flush_leftover_data (ide_drive_t *drive)
-{
- int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS;
-
- if (drive->media != ide_disk)
- return;
- while (i > 0) {
- u32 buffer[16];
- u32 wcount = (i > 16) ? 16 : i;
-
- i -= wcount;
- drive->hwif->input_data(drive, NULL, buffer, wcount * 4);
- }
-}
-
static void ide_kill_rq(ide_drive_t *drive, struct request *rq)
{
if (rq->rq_disk) {
@@ -422,8 +397,11 @@ static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8
}
if ((stat & DRQ_STAT) && rq_data_dir(rq) == READ &&
- (hwif->host_flags & IDE_HFLAG_ERROR_STOPS_FIFO) == 0)
- try_to_flush_leftover_data(drive);
+ (hwif->host_flags & IDE_HFLAG_ERROR_STOPS_FIFO) == 0) {
+ int nsect = drive->mult_count ? drive->mult_count : 1;
+
+ ide_pad_transfer(drive, READ, nsect * SECTOR_SIZE);
+ }
if (rq->errors >= ERROR_MAX || blk_noretry_request(rq)) {
ide_kill_rq(drive, rq);
@@ -459,7 +437,7 @@ static ide_startstop_t ide_atapi_error(ide_drive_t *drive, struct request *rq, u
if (ide_read_status(drive) & (BUSY_STAT | DRQ_STAT))
/* force an abort */
- hwif->OUTBSYNC(drive, WIN_IDLEIMMEDIATE,
+ hwif->OUTBSYNC(hwif, WIN_IDLEIMMEDIATE,
hwif->io_ports.command_addr);
if (rq->errors >= ERROR_MAX) {
@@ -1539,88 +1517,30 @@ irqreturn_t ide_intr (int irq, void *dev_id)
}
/**
- * ide_init_drive_cmd - initialize a drive command request
- * @rq: request object
- *
- * Initialize a request before we fill it in and send it down to
- * ide_do_drive_cmd. Commands must be set up by this function. Right
- * now it doesn't do a lot, but if that changes abusers will have a
- * nasty surprise.
- */
-
-void ide_init_drive_cmd (struct request *rq)
-{
- blk_rq_init(NULL, rq);
-}
-
-EXPORT_SYMBOL(ide_init_drive_cmd);
-
-/**
* ide_do_drive_cmd - issue IDE special command
* @drive: device to issue command
* @rq: request to issue
- * @action: action for processing
*
* This function issues a special IDE device request
* onto the request queue.
*
- * If action is ide_wait, then the rq is queued at the end of the
- * request queue, and the function sleeps until it has been processed.
- * This is for use when invoked from an ioctl handler.
- *
- * If action is ide_preempt, then the rq is queued at the head of
- * the request queue, displacing the currently-being-processed
- * request and this function returns immediately without waiting
- * for the new rq to be completed. This is VERY DANGEROUS, and is
- * intended for careful use by the ATAPI tape/cdrom driver code.
- *
- * If action is ide_end, then the rq is queued at the end of the
- * request queue, and the function returns immediately without waiting
- * for the new rq to be completed. This is again intended for careful
- * use by the ATAPI tape/cdrom driver code.
+ * the rq is queued at the head of the request queue, displacing
+ * the currently-being-processed request and this function
+ * returns immediately without waiting for the new rq to be
+ * completed. This is VERY DANGEROUS, and is intended for
+ * careful use by the ATAPI tape/cdrom driver code.
*/
-
-int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action)
+
+void ide_do_drive_cmd(ide_drive_t *drive, struct request *rq)
{
unsigned long flags;
ide_hwgroup_t *hwgroup = HWGROUP(drive);
- DECLARE_COMPLETION_ONSTACK(wait);
- int where = ELEVATOR_INSERT_BACK, err;
- int must_wait = (action == ide_wait || action == ide_head_wait);
-
- rq->errors = 0;
-
- /*
- * we need to hold an extra reference to request for safe inspection
- * after completion
- */
- if (must_wait) {
- rq->ref_count++;
- rq->end_io_data = &wait;
- rq->end_io = blk_end_sync_rq;
- }
spin_lock_irqsave(&ide_lock, flags);
- if (action == ide_preempt)
- hwgroup->rq = NULL;
- if (action == ide_preempt || action == ide_head_wait) {
- where = ELEVATOR_INSERT_FRONT;
- rq->cmd_flags |= REQ_PREEMPT;
- }
- __elv_add_request(drive->queue, rq, where, 0);
- ide_do_request(hwgroup, IDE_NO_IRQ);
+ hwgroup->rq = NULL;
+ __elv_add_request(drive->queue, rq, ELEVATOR_INSERT_FRONT, 1);
+ __generic_unplug_device(drive->queue);
spin_unlock_irqrestore(&ide_lock, flags);
-
- err = 0;
- if (must_wait) {
- wait_for_completion(&wait);
- if (rq->errors)
- err = -EIO;
-
- blk_put_request(rq);
- }
-
- return err;
}
EXPORT_SYMBOL(ide_do_drive_cmd);
@@ -1637,6 +1557,8 @@ void ide_pktcmd_tf_load(ide_drive_t *drive, u32 tf_flags, u16 bcount, u8 dma)
task.tf.lbah = (bcount >> 8) & 0xff;
ide_tf_dump(drive->name, &task.tf);
+ ide_set_irq(drive, 1);
+ SELECT_MASK(drive, 0);
drive->hwif->tf_load(drive, &task);
}
diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c
index 0daf923541ff..66e39e56a3fb 100644
--- a/drivers/ide/ide-iops.c
+++ b/drivers/ide/ide-iops.c
@@ -42,7 +42,7 @@ static void ide_outb (u8 val, unsigned long port)
outb(val, port);
}
-static void ide_outbsync (ide_drive_t *drive, u8 addr, unsigned long port)
+static void ide_outbsync(ide_hwif_t *hwif, u8 addr, unsigned long port)
{
outb(addr, port);
}
@@ -68,7 +68,7 @@ static void ide_mm_outb (u8 value, unsigned long port)
writeb(value, (void __iomem *) port);
}
-static void ide_mm_outbsync (ide_drive_t *drive, u8 value, unsigned long port)
+static void ide_mm_outbsync(ide_hwif_t *hwif, u8 value, unsigned long port)
{
writeb(value, (void __iomem *) port);
}
@@ -95,7 +95,7 @@ void SELECT_DRIVE (ide_drive_t *drive)
hwif->OUTB(drive->select.all, hwif->io_ports.device_addr);
}
-static void SELECT_MASK(ide_drive_t *drive, int mask)
+void SELECT_MASK(ide_drive_t *drive, int mask)
{
const struct ide_port_ops *port_ops = drive->hwif->port_ops;
@@ -120,11 +120,6 @@ static void ide_tf_load(ide_drive_t *drive, ide_task_t *task)
if (task->tf_flags & IDE_TFLAG_FLAGGED)
HIHI = 0xFF;
- ide_set_irq(drive, 1);
-
- if ((task->tf_flags & IDE_TFLAG_NO_SELECT_MASK) == 0)
- SELECT_MASK(drive, 0);
-
if (task->tf_flags & IDE_TFLAG_OUT_DATA) {
u16 data = (tf->hob_data << 8) | tf->data;
@@ -191,7 +186,7 @@ static void ide_tf_read(ide_drive_t *drive, ide_task_t *task)
}
/* be sure we're looking at the low order bits */
- tf_outb(drive->ctl & ~0x80, io_ports->ctl_addr);
+ tf_outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr);
if (task->tf_flags & IDE_TFLAG_IN_NSECT)
tf->nsect = tf_inb(io_ports->nsect_addr);
@@ -205,7 +200,7 @@ static void ide_tf_read(ide_drive_t *drive, ide_task_t *task)
tf->device = tf_inb(io_ports->device_addr);
if (task->tf_flags & IDE_TFLAG_LBA48) {
- tf_outb(drive->ctl | 0x80, io_ports->ctl_addr);
+ tf_outb(ATA_DEVCTL_OBS | 0x80, io_ports->ctl_addr);
if (task->tf_flags & IDE_TFLAG_IN_HOB_FEATURE)
tf->hob_feature = tf_inb(io_ports->feature_addr);
@@ -691,7 +686,7 @@ int ide_driveid_update(ide_drive_t *drive)
SELECT_MASK(drive, 1);
ide_set_irq(drive, 1);
msleep(50);
- hwif->OUTBSYNC(drive, WIN_IDENTIFY, hwif->io_ports.command_addr);
+ hwif->OUTBSYNC(hwif, WIN_IDENTIFY, hwif->io_ports.command_addr);
timeout = jiffies + WAIT_WORSTCASE;
do {
if (time_after(jiffies, timeout)) {
@@ -744,9 +739,6 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed)
int error = 0;
u8 stat;
-// while (HWGROUP(drive)->busy)
-// msleep(50);
-
#ifdef CONFIG_BLK_DEV_IDEDMA
if (hwif->dma_ops) /* check if host supports DMA */
hwif->dma_ops->dma_host_set(drive, 0);
@@ -781,7 +773,7 @@ int ide_config_drive_speed(ide_drive_t *drive, u8 speed)
ide_set_irq(drive, 0);
hwif->OUTB(speed, io_ports->nsect_addr);
hwif->OUTB(SETFEATURES_XFER, io_ports->feature_addr);
- hwif->OUTBSYNC(drive, WIN_SETFEATURES, io_ports->command_addr);
+ hwif->OUTBSYNC(hwif, WIN_SETFEATURES, io_ports->command_addr);
if (drive->quirk_list == 2)
ide_set_irq(drive, 1);
@@ -889,7 +881,7 @@ void ide_execute_command(ide_drive_t *drive, u8 cmd, ide_handler_t *handler,
spin_lock_irqsave(&ide_lock, flags);
__ide_set_handler(drive, handler, timeout, expiry);
- hwif->OUTBSYNC(drive, cmd, hwif->io_ports.command_addr);
+ hwif->OUTBSYNC(hwif, cmd, hwif->io_ports.command_addr);
/*
* Drive takes 400nS to respond, we must avoid the IRQ being
* serviced before that.
@@ -907,7 +899,7 @@ void ide_execute_pkt_cmd(ide_drive_t *drive)
unsigned long flags;
spin_lock_irqsave(&ide_lock, flags);
- hwif->OUTBSYNC(drive, WIN_PACKETCMD, hwif->io_ports.command_addr);
+ hwif->OUTBSYNC(hwif, WIN_PACKETCMD, hwif->io_ports.command_addr);
ndelay(400);
spin_unlock_irqrestore(&ide_lock, flags);
}
@@ -1102,7 +1094,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
pre_reset(drive);
SELECT_DRIVE(drive);
udelay (20);
- hwif->OUTBSYNC(drive, WIN_SRST, io_ports->command_addr);
+ hwif->OUTBSYNC(hwif, WIN_SRST, io_ports->command_addr);
ndelay(400);
hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
hwgroup->polling = 1;
@@ -1133,14 +1125,14 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
* recover from reset very quickly, saving us the first 50ms wait time.
*/
/* set SRST and nIEN */
- hwif->OUTBSYNC(drive, drive->ctl|6, io_ports->ctl_addr);
+ hwif->OUTBSYNC(hwif, ATA_DEVCTL_OBS | 6, io_ports->ctl_addr);
/* more than enough time */
udelay(10);
if (drive->quirk_list == 2)
- ctl = drive->ctl; /* clear SRST and nIEN */
+ ctl = ATA_DEVCTL_OBS; /* clear SRST and nIEN */
else
- ctl = drive->ctl | 2; /* clear SRST, leave nIEN */
- hwif->OUTBSYNC(drive, ctl, io_ports->ctl_addr);
+ ctl = ATA_DEVCTL_OBS | 2; /* clear SRST, leave nIEN */
+ hwif->OUTBSYNC(hwif, ctl, io_ports->ctl_addr);
/* more than enough time */
udelay(10);
hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c
index 47af80df6872..13af72f09ec4 100644
--- a/drivers/ide/ide-lib.c
+++ b/drivers/ide/ide-lib.c
@@ -1,26 +1,11 @@
-#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
#include <linux/interrupt.h>
-#include <linux/major.h>
-#include <linux/errno.h>
-#include <linux/genhd.h>
-#include <linux/blkpg.h>
-#include <linux/slab.h>
-#include <linux/pci.h>
-#include <linux/delay.h>
#include <linux/hdreg.h>
#include <linux/ide.h>
#include <linux/bitops.h>
-#include <asm/byteorder.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
-
static const char *udma_str[] =
{ "UDMA/16", "UDMA/25", "UDMA/33", "UDMA/44",
"UDMA/66", "UDMA/100", "UDMA/133", "UDMA7" };
@@ -90,142 +75,6 @@ static u8 ide_rate_filter(ide_drive_t *drive, u8 speed)
return min(speed, mode);
}
-/*
- * Standard (generic) timings for PIO modes, from ATA2 specification.
- * These timings are for access to the IDE data port register *only*.
- * Some drives may specify a mode, while also specifying a different
- * value for cycle_time (from drive identification data).
- */
-const ide_pio_timings_t ide_pio_timings[6] = {
- { 70, 165, 600 }, /* PIO Mode 0 */
- { 50, 125, 383 }, /* PIO Mode 1 */
- { 30, 100, 240 }, /* PIO Mode 2 */
- { 30, 80, 180 }, /* PIO Mode 3 with IORDY */
- { 25, 70, 120 }, /* PIO Mode 4 with IORDY */
- { 20, 50, 100 } /* PIO Mode 5 with IORDY (nonstandard) */
-};
-
-EXPORT_SYMBOL_GPL(ide_pio_timings);
-
-/*
- * Shared data/functions for determining best PIO mode for an IDE drive.
- * Most of this stuff originally lived in cmd640.c, and changes to the
- * ide_pio_blacklist[] table should be made with EXTREME CAUTION to avoid
- * breaking the fragile cmd640.c support.
- */
-
-/*
- * Black list. Some drives incorrectly report their maximal PIO mode,
- * at least in respect to CMD640. Here we keep info on some known drives.
- */
-static struct ide_pio_info {
- const char *name;
- int pio;
-} ide_pio_blacklist [] = {
- { "Conner Peripherals 540MB - CFS540A", 3 },
-
- { "WDC AC2700", 3 },
- { "WDC AC2540", 3 },
- { "WDC AC2420", 3 },
- { "WDC AC2340", 3 },
- { "WDC AC2250", 0 },
- { "WDC AC2200", 0 },
- { "WDC AC21200", 4 },
- { "WDC AC2120", 0 },
- { "WDC AC2850", 3 },
- { "WDC AC1270", 3 },
- { "WDC AC1170", 1 },
- { "WDC AC1210", 1 },
- { "WDC AC280", 0 },
- { "WDC AC31000", 3 },
- { "WDC AC31200", 3 },
-
- { "Maxtor 7131 AT", 1 },
- { "Maxtor 7171 AT", 1 },
- { "Maxtor 7213 AT", 1 },
- { "Maxtor 7245 AT", 1 },
- { "Maxtor 7345 AT", 1 },
- { "Maxtor 7546 AT", 3 },
- { "Maxtor 7540 AV", 3 },
-
- { "SAMSUNG SHD-3121A", 1 },
- { "SAMSUNG SHD-3122A", 1 },
- { "SAMSUNG SHD-3172A", 1 },
-
- { "ST5660A", 3 },
- { "ST3660A", 3 },
- { "ST3630A", 3 },
- { "ST3655A", 3 },
- { "ST3391A", 3 },
- { "ST3390A", 1 },
- { "ST3600A", 1 },
- { "ST3290A", 0 },
- { "ST3144A", 0 },
- { "ST3491A", 1 }, /* reports 3, should be 1 or 2 (depending on */
- /* drive) according to Seagates FIND-ATA program */
-
- { "QUANTUM ELS127A", 0 },
- { "QUANTUM ELS170A", 0 },
- { "QUANTUM LPS240A", 0 },
- { "QUANTUM LPS210A", 3 },
- { "QUANTUM LPS270A", 3 },
- { "QUANTUM LPS365A", 3 },
- { "QUANTUM LPS540A", 3 },
- { "QUANTUM LIGHTNING 540A", 3 },
- { "QUANTUM LIGHTNING 730A", 3 },
-
- { "QUANTUM FIREBALL_540", 3 }, /* Older Quantum Fireballs don't work */
- { "QUANTUM FIREBALL_640", 3 },
- { "QUANTUM FIREBALL_1080", 3 },
- { "QUANTUM FIREBALL_1280", 3 },
- { NULL, 0 }
-};
-
-/**
- * ide_scan_pio_blacklist - check for a blacklisted drive
- * @model: Drive model string
- *
- * This routine searches the ide_pio_blacklist for an entry
- * matching the start/whole of the supplied model name.
- *
- * Returns -1 if no match found.
- * Otherwise returns the recommended PIO mode from ide_pio_blacklist[].
- */
-
-static int ide_scan_pio_blacklist (char *model)
-{
- struct ide_pio_info *p;
-
- for (p = ide_pio_blacklist; p->name != NULL; p++) {
- if (strncmp(p->name, model, strlen(p->name)) == 0)
- return p->pio;
- }
- return -1;
-}
-
-unsigned int ide_pio_cycle_time(ide_drive_t *drive, u8 pio)
-{
- struct hd_driveid *id = drive->id;
- int cycle_time = 0;
-
- if (id->field_valid & 2) {
- if (id->capability & 8)
- cycle_time = id->eide_pio_iordy;
- else
- cycle_time = id->eide_pio;
- }
-
- /* conservative "downgrade" for all pre-ATA2 drives */
- if (pio < 3) {
- if (cycle_time && cycle_time < ide_pio_timings[pio].cycle_time)
- cycle_time = 0; /* use standard timing */
- }
-
- return cycle_time ? cycle_time : ide_pio_timings[pio].cycle_time;
-}
-
-EXPORT_SYMBOL_GPL(ide_pio_cycle_time);
-
/**
* ide_get_best_pio_mode - get PIO mode from drive
* @drive: drive to consider
diff --git a/drivers/ide/ide-pio-blacklist.c b/drivers/ide/ide-pio-blacklist.c
new file mode 100644
index 000000000000..a8c2c8f8660a
--- /dev/null
+++ b/drivers/ide/ide-pio-blacklist.c
@@ -0,0 +1,94 @@
+/*
+ * PIO blacklist. Some drives incorrectly report their maximal PIO mode,
+ * at least in respect to CMD640. Here we keep info on some known drives.
+ *
+ * Changes to the ide_pio_blacklist[] should be made with EXTREME CAUTION
+ * to avoid breaking the fragile cmd640.c support.
+ */
+
+#include <linux/string.h>
+
+static struct ide_pio_info {
+ const char *name;
+ int pio;
+} ide_pio_blacklist [] = {
+ { "Conner Peripherals 540MB - CFS540A", 3 },
+
+ { "WDC AC2700", 3 },
+ { "WDC AC2540", 3 },
+ { "WDC AC2420", 3 },
+ { "WDC AC2340", 3 },
+ { "WDC AC2250", 0 },
+ { "WDC AC2200", 0 },
+ { "WDC AC21200", 4 },
+ { "WDC AC2120", 0 },
+ { "WDC AC2850", 3 },
+ { "WDC AC1270", 3 },
+ { "WDC AC1170", 1 },
+ { "WDC AC1210", 1 },
+ { "WDC AC280", 0 },
+ { "WDC AC31000", 3 },
+ { "WDC AC31200", 3 },
+
+ { "Maxtor 7131 AT", 1 },
+ { "Maxtor 7171 AT", 1 },
+ { "Maxtor 7213 AT", 1 },
+ { "Maxtor 7245 AT", 1 },
+ { "Maxtor 7345 AT", 1 },
+ { "Maxtor 7546 AT", 3 },
+ { "Maxtor 7540 AV", 3 },
+
+ { "SAMSUNG SHD-3121A", 1 },
+ { "SAMSUNG SHD-3122A", 1 },
+ { "SAMSUNG SHD-3172A", 1 },
+
+ { "ST5660A", 3 },
+ { "ST3660A", 3 },
+ { "ST3630A", 3 },
+ { "ST3655A", 3 },
+ { "ST3391A", 3 },
+ { "ST3390A", 1 },
+ { "ST3600A", 1 },
+ { "ST3290A", 0 },
+ { "ST3144A", 0 },
+ { "ST3491A", 1 }, /* reports 3, should be 1 or 2 (depending on drive)
+ according to Seagate's FIND-ATA program */
+
+ { "QUANTUM ELS127A", 0 },
+ { "QUANTUM ELS170A", 0 },
+ { "QUANTUM LPS240A", 0 },
+ { "QUANTUM LPS210A", 3 },
+ { "QUANTUM LPS270A", 3 },
+ { "QUANTUM LPS365A", 3 },
+ { "QUANTUM LPS540A", 3 },
+ { "QUANTUM LIGHTNING 540A", 3 },
+ { "QUANTUM LIGHTNING 730A", 3 },
+
+ { "QUANTUM FIREBALL_540", 3 }, /* Older Quantum Fireballs don't work */
+ { "QUANTUM FIREBALL_640", 3 },
+ { "QUANTUM FIREBALL_1080", 3 },
+ { "QUANTUM FIREBALL_1280", 3 },
+ { NULL, 0 }
+};
+
+/**
+ * ide_scan_pio_blacklist - check for a blacklisted drive
+ * @model: Drive model string
+ *
+ * This routine searches the ide_pio_blacklist for an entry
+ * matching the start/whole of the supplied model name.
+ *
+ * Returns -1 if no match found.
+ * Otherwise returns the recommended PIO mode from ide_pio_blacklist[].
+ */
+
+int ide_scan_pio_blacklist(char *model)
+{
+ struct ide_pio_info *p;
+
+ for (p = ide_pio_blacklist; p->name != NULL; p++) {
+ if (strncmp(p->name, model, strlen(p->name)) == 0)
+ return p->pio;
+ }
+ return -1;
+}
diff --git a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c
index 6a8953f68e9f..03f2ef5470a3 100644
--- a/drivers/ide/ide-pnp.c
+++ b/drivers/ide/ide-pnp.c
@@ -33,6 +33,8 @@ static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
ide_hwif_t *hwif;
unsigned long base, ctl;
+ printk(KERN_INFO DRV_NAME ": generic PnP IDE interface\n");
+
if (!(pnp_port_valid(dev, 0) && pnp_port_valid(dev, 1) && pnp_irq_valid(dev, 0)))
return -1;
@@ -55,16 +57,15 @@ static int idepnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
memset(&hw, 0, sizeof(hw));
ide_std_init_ports(&hw, base, ctl);
hw.irq = pnp_irq(dev, 0);
+ hw.chipset = ide_generic;
hwif = ide_find_port();
if (hwif) {
u8 index = hwif->index;
u8 idx[4] = { index, 0xff, 0xff, 0xff };
- ide_init_port_data(hwif, index);
ide_init_port_hw(hwif, &hw);
- printk(KERN_INFO "ide%d: generic PnP IDE interface\n", index);
pnp_set_drvdata(dev, hwif);
ide_device_add(idx, NULL);
diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
index 655ec7ef568a..08a3531ec088 100644
--- a/drivers/ide/ide-probe.c
+++ b/drivers/ide/ide-probe.c
@@ -39,6 +39,8 @@
#include <asm/uaccess.h>
#include <asm/io.h>
+static ide_hwif_t ide_hwifs[MAX_HWIFS]; /* master data repository */
+
/**
* generic_id - add a generic drive id
* @drive: drive to make an ID block for
@@ -293,7 +295,7 @@ static int actual_try_to_identify (ide_drive_t *drive, u8 cmd)
hwif->OUTB(0, io_ports->feature_addr);
/* ask drive for ID */
- hwif->OUTBSYNC(drive, cmd, io_ports->command_addr);
+ hwif->OUTBSYNC(hwif, cmd, hwif->io_ports.command_addr);
timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2;
timeout += jiffies;
@@ -478,9 +480,9 @@ static int do_probe (ide_drive_t *drive, u8 cmd)
printk(KERN_ERR "%s: no response (status = 0x%02x), "
"resetting drive\n", drive->name, stat);
msleep(50);
- hwif->OUTB(drive->select.all, io_ports->device_addr);
+ SELECT_DRIVE(drive);
msleep(50);
- hwif->OUTBSYNC(drive, WIN_SRST, io_ports->command_addr);
+ hwif->OUTBSYNC(hwif, WIN_SRST, io_ports->command_addr);
(void)ide_busy_sleep(hwif);
rc = try_to_identify(drive, cmd);
}
@@ -516,7 +518,7 @@ static void enable_nest (ide_drive_t *drive)
printk("%s: enabling %s -- ", hwif->name, drive->id->model);
SELECT_DRIVE(drive);
msleep(50);
- hwif->OUTBSYNC(drive, EXABYTE_ENABLE_NEST, hwif->io_ports.command_addr);
+ hwif->OUTBSYNC(hwif, EXABYTE_ENABLE_NEST, hwif->io_ports.command_addr);
if (ide_busy_sleep(hwif)) {
printk(KERN_CONT "failed (timeout)\n");
@@ -1067,7 +1069,7 @@ static int init_irq (ide_hwif_t *hwif)
if (io_ports->ctl_addr)
/* clear nIEN */
- hwif->OUTB(0x08, io_ports->ctl_addr);
+ hwif->OUTBSYNC(hwif, ATA_DEVCTL_OBS, io_ports->ctl_addr);
if (request_irq(hwif->irq,&ide_intr,sa,hwif->name,hwgroup))
goto out_unlink;
@@ -1324,17 +1326,16 @@ static void ide_port_init_devices(ide_hwif_t *hwif)
drive->unmask = 1;
if (hwif->host_flags & IDE_HFLAG_NO_UNMASK_IRQS)
drive->no_unmask = 1;
- }
- if (port_ops && port_ops->port_init_devs)
- port_ops->port_init_devs(hwif);
+ if (port_ops && port_ops->init_dev)
+ port_ops->init_dev(drive);
+ }
}
static void ide_init_port(ide_hwif_t *hwif, unsigned int port,
const struct ide_port_info *d)
{
- if (d->chipset != ide_etrax100)
- hwif->channel = port;
+ hwif->channel = port;
if (d->chipset)
hwif->chipset = d->chipset;
@@ -1480,22 +1481,29 @@ ide_hwif_t *ide_find_port_slot(const struct ide_port_info *d)
for (; i < MAX_HWIFS; i++) {
hwif = &ide_hwifs[i];
if (hwif->chipset == ide_unknown)
- return hwif;
+ goto out_found;
}
} else {
for (i = 2; i < MAX_HWIFS; i++) {
hwif = &ide_hwifs[i];
if (hwif->chipset == ide_unknown)
- return hwif;
+ goto out_found;
}
for (i = 0; i < 2 && i < MAX_HWIFS; i++) {
hwif = &ide_hwifs[i];
if (hwif->chipset == ide_unknown)
- return hwif;
+ goto out_found;
}
}
+ printk(KERN_ERR "%s: no free slot for interface\n",
+ d ? d->name : "ide");
+
return NULL;
+
+out_found:
+ ide_init_port_data(hwif, i);
+ return hwif;
}
EXPORT_SYMBOL_GPL(ide_find_port_slot);
@@ -1519,7 +1527,7 @@ int ide_device_add_all(u8 *idx, const struct ide_port_info *d)
continue;
}
- if (d->chipset != ide_etrax100 && (i & 1) && mate) {
+ if ((i & 1) && mate) {
hwif->mate = mate;
mate->mate = hwif;
}
@@ -1665,6 +1673,7 @@ static void ide_legacy_init_one(u8 *idx, hw_regs_t *hw, u8 port_no,
ide_std_init_ports(hw, base, ctl);
hw->irq = irq;
+ hw->chipset = d->chipset;
hwif = ide_find_port_slot(d);
if (hwif) {
diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c
index 8d6ad812a014..55ec7f798772 100644
--- a/drivers/ide/ide-proc.c
+++ b/drivers/ide/ide-proc.c
@@ -63,7 +63,6 @@ static int proc_ide_read_imodel
case ide_pmac: name = "mac-io"; break;
case ide_au1xxx: name = "au1xxx"; break;
case ide_palm3710: name = "palm3710"; break;
- case ide_etrax100: name = "etrax100"; break;
case ide_acorn: name = "acorn"; break;
default: name = "(unknown)"; break;
}
diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c
index 1e1f26331a24..6b3a1ce60e55 100644
--- a/drivers/ide/ide-tape.c
+++ b/drivers/ide/ide-tape.c
@@ -144,9 +144,6 @@ enum {
/*************************** End of tunable parameters ***********************/
-/* Read/Write error simulation */
-#define SIMULATE_ERRORS 0
-
/* tape directions */
enum {
IDETAPE_DIR_NONE = (1 << 0),
@@ -442,7 +439,7 @@ static void idetape_output_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc,
}
}
-static void idetape_update_buffers(struct ide_atapi_pc *pc)
+static void idetape_update_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc)
{
struct idetape_bh *bh = pc->bh;
int count;
@@ -506,18 +503,6 @@ static struct request *idetape_next_rq_storage(ide_drive_t *drive)
return (&tape->rq_stack[tape->rq_stack_index++]);
}
-static void idetape_init_pc(struct ide_atapi_pc *pc)
-{
- memset(pc->c, 0, 12);
- pc->retries = 0;
- pc->flags = 0;
- pc->req_xfer = 0;
- pc->buf = pc->pc_buf;
- pc->buf_size = IDETAPE_PC_BUFFER_SIZE;
- pc->bh = NULL;
- pc->b_data = NULL;
-}
-
/*
* called on each failed packet command retry to analyze the request sense. We
* currently do not utilize this information.
@@ -538,8 +523,8 @@ static void idetape_analyze_error(ide_drive_t *drive, u8 *sense)
if (pc->flags & PC_FLAG_DMA_ERROR) {
pc->xferred = pc->req_xfer -
tape->blk_size *
- be32_to_cpu(get_unaligned((u32 *)&sense[3]));
- idetape_update_buffers(pc);
+ get_unaligned_be32(&sense[3]);
+ idetape_update_buffers(drive, pc);
}
/*
@@ -634,21 +619,78 @@ static int idetape_end_request(ide_drive_t *drive, int uptodate, int nr_sects)
return 0;
}
-static ide_startstop_t idetape_request_sense_callback(ide_drive_t *drive)
+static void ide_tape_callback(ide_drive_t *drive)
{
idetape_tape_t *tape = drive->driver_data;
+ struct ide_atapi_pc *pc = tape->pc;
+ int uptodate = pc->error ? 0 : 1;
debug_log(DBG_PROCS, "Enter %s\n", __func__);
- if (!tape->pc->error) {
- idetape_analyze_error(drive, tape->pc->buf);
- idetape_end_request(drive, 1, 0);
- } else {
- printk(KERN_ERR "ide-tape: Error in REQUEST SENSE itself - "
- "Aborting request!\n");
- idetape_end_request(drive, 0, 0);
+ if (tape->failed_pc == pc)
+ tape->failed_pc = NULL;
+
+ if (pc->c[0] == REQUEST_SENSE) {
+ if (uptodate)
+ idetape_analyze_error(drive, pc->buf);
+ else
+ printk(KERN_ERR "ide-tape: Error in REQUEST SENSE "
+ "itself - Aborting request!\n");
+ } else if (pc->c[0] == READ_6 || pc->c[0] == WRITE_6) {
+ struct request *rq = drive->hwif->hwgroup->rq;
+ int blocks = pc->xferred / tape->blk_size;
+
+ tape->avg_size += blocks * tape->blk_size;
+
+ if (time_after_eq(jiffies, tape->avg_time + HZ)) {
+ tape->avg_speed = tape->avg_size * HZ /
+ (jiffies - tape->avg_time) / 1024;
+ tape->avg_size = 0;
+ tape->avg_time = jiffies;
+ }
+
+ tape->first_frame += blocks;
+ rq->current_nr_sectors -= blocks;
+
+ if (pc->error)
+ uptodate = pc->error;
+ } else if (pc->c[0] == READ_POSITION && uptodate) {
+ u8 *readpos = tape->pc->buf;
+
+ debug_log(DBG_SENSE, "BOP - %s\n",
+ (readpos[0] & 0x80) ? "Yes" : "No");
+ debug_log(DBG_SENSE, "EOP - %s\n",
+ (readpos[0] & 0x40) ? "Yes" : "No");
+
+ if (readpos[0] & 0x4) {
+ printk(KERN_INFO "ide-tape: Block location is unknown"
+ "to the tape\n");
+ clear_bit(IDETAPE_FLAG_ADDRESS_VALID, &tape->flags);
+ uptodate = 0;
+ } else {
+ debug_log(DBG_SENSE, "Block Location - %u\n",
+ be32_to_cpu(*(u32 *)&readpos[4]));
+
+ tape->partition = readpos[1];
+ tape->first_frame = be32_to_cpu(*(u32 *)&readpos[4]);
+ set_bit(IDETAPE_FLAG_ADDRESS_VALID, &tape->flags);
+ }
}
- return ide_stopped;
+
+ idetape_end_request(drive, uptodate, 0);
+}
+
+static void idetape_init_pc(struct ide_atapi_pc *pc)
+{
+ memset(pc->c, 0, 12);
+ pc->retries = 0;
+ pc->flags = 0;
+ pc->req_xfer = 0;
+ pc->buf = pc->pc_buf;
+ pc->buf_size = IDETAPE_PC_BUFFER_SIZE;
+ pc->bh = NULL;
+ pc->b_data = NULL;
+ pc->callback = ide_tape_callback;
}
static void idetape_create_request_sense_cmd(struct ide_atapi_pc *pc)
@@ -657,7 +699,6 @@ static void idetape_create_request_sense_cmd(struct ide_atapi_pc *pc)
pc->c[0] = REQUEST_SENSE;
pc->c[4] = 20;
pc->req_xfer = 20;
- pc->idetape_callback = &idetape_request_sense_callback;
}
static void idetape_init_rq(struct request *rq, u8 cmd)
@@ -688,9 +729,10 @@ static void idetape_queue_pc_head(ide_drive_t *drive, struct ide_atapi_pc *pc,
struct ide_tape_obj *tape = drive->driver_data;
idetape_init_rq(rq, REQ_IDETAPE_PC1);
+ rq->cmd_flags |= REQ_PREEMPT;
rq->buffer = (char *) pc;
rq->rq_disk = tape->disk;
- (void) ide_do_drive_cmd(drive, rq, ide_preempt);
+ ide_do_drive_cmd(drive, rq);
}
/*
@@ -698,7 +740,7 @@ static void idetape_queue_pc_head(ide_drive_t *drive, struct ide_atapi_pc *pc,
* last packet command. We queue a request sense packet command in
* the head of the request list.
*/
-static ide_startstop_t idetape_retry_pc (ide_drive_t *drive)
+static void idetape_retry_pc(ide_drive_t *drive)
{
idetape_tape_t *tape = drive->driver_data;
struct ide_atapi_pc *pc;
@@ -710,7 +752,6 @@ static ide_startstop_t idetape_retry_pc (ide_drive_t *drive)
idetape_create_request_sense_cmd(pc);
set_bit(IDETAPE_FLAG_IGNORE_DSC, &tape->flags);
idetape_queue_pc_head(drive, pc, rq);
- return ide_stopped;
}
/*
@@ -727,7 +768,26 @@ static void idetape_postpone_request(ide_drive_t *drive)
ide_stall_queue(drive, tape->dsc_poll_freq);
}
-typedef void idetape_io_buf(ide_drive_t *, struct ide_atapi_pc *, unsigned int);
+static void ide_tape_handle_dsc(ide_drive_t *drive)
+{
+ idetape_tape_t *tape = drive->driver_data;
+
+ /* Media access command */
+ tape->dsc_polling_start = jiffies;
+ tape->dsc_poll_freq = IDETAPE_DSC_MA_FAST;
+ tape->dsc_timeout = jiffies + IDETAPE_DSC_MA_TIMEOUT;
+ /* Allow ide.c to handle other requests */
+ idetape_postpone_request(drive);
+}
+
+static void ide_tape_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc,
+ unsigned int bcount, int write)
+{
+ if (write)
+ idetape_output_buffers(drive, pc, bcount);
+ else
+ idetape_input_buffers(drive, pc, bcount);
+}
/*
* This is the usual interrupt handler which will be called during a packet
@@ -738,169 +798,11 @@ typedef void idetape_io_buf(ide_drive_t *, struct ide_atapi_pc *, unsigned int);
*/
static ide_startstop_t idetape_pc_intr(ide_drive_t *drive)
{
- ide_hwif_t *hwif = drive->hwif;
idetape_tape_t *tape = drive->driver_data;
- struct ide_atapi_pc *pc = tape->pc;
- xfer_func_t *xferfunc;
- idetape_io_buf *iobuf;
- unsigned int temp;
-#if SIMULATE_ERRORS
- static int error_sim_count;
-#endif
- u16 bcount;
- u8 stat, ireason;
-
- debug_log(DBG_PROCS, "Enter %s - interrupt handler\n", __func__);
-
- /* Clear the interrupt */
- stat = ide_read_status(drive);
-
- if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) {
- if (hwif->dma_ops->dma_end(drive) || (stat & ERR_STAT)) {
- /*
- * A DMA error is sometimes expected. For example,
- * if the tape is crossing a filemark during a
- * READ command, it will issue an irq and position
- * itself before the filemark, so that only a partial
- * data transfer will occur (which causes the DMA
- * error). In that case, we will later ask the tape
- * how much bytes of the original request were
- * actually transferred (we can't receive that
- * information from the DMA engine on most chipsets).
- */
-
- /*
- * On the contrary, a DMA error is never expected;
- * it usually indicates a hardware error or abort.
- * If the tape crosses a filemark during a READ
- * command, it will issue an irq and position itself
- * after the filemark (not before). Only a partial
- * data transfer will occur, but no DMA error.
- * (AS, 19 Apr 2001)
- */
- pc->flags |= PC_FLAG_DMA_ERROR;
- } else {
- pc->xferred = pc->req_xfer;
- idetape_update_buffers(pc);
- }
- debug_log(DBG_PROCS, "DMA finished\n");
-
- }
-
- /* No more interrupts */
- if ((stat & DRQ_STAT) == 0) {
- debug_log(DBG_SENSE, "Packet command completed, %d bytes"
- " transferred\n", pc->xferred);
-
- pc->flags &= ~PC_FLAG_DMA_IN_PROGRESS;
- local_irq_enable();
-
-#if SIMULATE_ERRORS
- if ((pc->c[0] == WRITE_6 || pc->c[0] == READ_6) &&
- (++error_sim_count % 100) == 0) {
- printk(KERN_INFO "ide-tape: %s: simulating error\n",
- tape->name);
- stat |= ERR_STAT;
- }
-#endif
- if ((stat & ERR_STAT) && pc->c[0] == REQUEST_SENSE)
- stat &= ~ERR_STAT;
- if ((stat & ERR_STAT) || (pc->flags & PC_FLAG_DMA_ERROR)) {
- /* Error detected */
- debug_log(DBG_ERR, "%s: I/O error\n", tape->name);
-
- if (pc->c[0] == REQUEST_SENSE) {
- printk(KERN_ERR "ide-tape: I/O error in request"
- " sense command\n");
- return ide_do_reset(drive);
- }
- debug_log(DBG_ERR, "[cmd %x]: check condition\n",
- pc->c[0]);
-
- /* Retry operation */
- return idetape_retry_pc(drive);
- }
- pc->error = 0;
- if ((pc->flags & PC_FLAG_WAIT_FOR_DSC) &&
- (stat & SEEK_STAT) == 0) {
- /* Media access command */
- tape->dsc_polling_start = jiffies;
- tape->dsc_poll_freq = IDETAPE_DSC_MA_FAST;
- tape->dsc_timeout = jiffies + IDETAPE_DSC_MA_TIMEOUT;
- /* Allow ide.c to handle other requests */
- idetape_postpone_request(drive);
- return ide_stopped;
- }
- if (tape->failed_pc == pc)
- tape->failed_pc = NULL;
- /* Command finished - Call the callback function */
- return pc->idetape_callback(drive);
- }
-
- if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) {
- pc->flags &= ~PC_FLAG_DMA_IN_PROGRESS;
- printk(KERN_ERR "ide-tape: The tape wants to issue more "
- "interrupts in DMA mode\n");
- printk(KERN_ERR "ide-tape: DMA disabled, reverting to PIO\n");
- ide_dma_off(drive);
- return ide_do_reset(drive);
- }
- /* Get the number of bytes to transfer on this interrupt. */
- bcount = (hwif->INB(hwif->io_ports.lbah_addr) << 8) |
- hwif->INB(hwif->io_ports.lbam_addr);
-
- ireason = hwif->INB(hwif->io_ports.nsect_addr);
-
- if (ireason & CD) {
- printk(KERN_ERR "ide-tape: CoD != 0 in %s\n", __func__);
- return ide_do_reset(drive);
- }
- if (((ireason & IO) == IO) == !!(pc->flags & PC_FLAG_WRITING)) {
- /* Hopefully, we will never get here */
- printk(KERN_ERR "ide-tape: We wanted to %s, ",
- (ireason & IO) ? "Write" : "Read");
- printk(KERN_ERR "ide-tape: but the tape wants us to %s !\n",
- (ireason & IO) ? "Read" : "Write");
- return ide_do_reset(drive);
- }
- if (!(pc->flags & PC_FLAG_WRITING)) {
- /* Reading - Check that we have enough space */
- temp = pc->xferred + bcount;
- if (temp > pc->req_xfer) {
- if (temp > pc->buf_size) {
- printk(KERN_ERR "ide-tape: The tape wants to "
- "send us more data than expected "
- "- discarding data\n");
- ide_pad_transfer(drive, 0, bcount);
- ide_set_handler(drive, &idetape_pc_intr,
- IDETAPE_WAIT_CMD, NULL);
- return ide_started;
- }
- debug_log(DBG_SENSE, "The tape wants to send us more "
- "data than expected - allowing transfer\n");
- }
- iobuf = &idetape_input_buffers;
- xferfunc = hwif->input_data;
- } else {
- iobuf = &idetape_output_buffers;
- xferfunc = hwif->output_data;
- }
-
- if (pc->bh)
- iobuf(drive, pc, bcount);
- else
- xferfunc(drive, NULL, pc->cur_pos, bcount);
-
- /* Update the current position */
- pc->xferred += bcount;
- pc->cur_pos += bcount;
-
- debug_log(DBG_SENSE, "[cmd %x] transferred %d bytes on that intr.\n",
- pc->c[0], bcount);
- /* And set the interrupt handler again */
- ide_set_handler(drive, &idetape_pc_intr, IDETAPE_WAIT_CMD, NULL);
- return ide_started;
+ return ide_pc_intr(drive, tape->pc, idetape_pc_intr, IDETAPE_WAIT_CMD,
+ NULL, idetape_update_buffers, idetape_retry_pc,
+ ide_tape_handle_dsc, ide_tape_io_buffers);
}
/*
@@ -941,56 +843,16 @@ static ide_startstop_t idetape_pc_intr(ide_drive_t *drive)
*/
static ide_startstop_t idetape_transfer_pc(ide_drive_t *drive)
{
- ide_hwif_t *hwif = drive->hwif;
idetape_tape_t *tape = drive->driver_data;
- struct ide_atapi_pc *pc = tape->pc;
- int retries = 100;
- ide_startstop_t startstop;
- u8 ireason;
-
- if (ide_wait_stat(&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) {
- printk(KERN_ERR "ide-tape: Strange, packet command initiated "
- "yet DRQ isn't asserted\n");
- return startstop;
- }
- ireason = hwif->INB(hwif->io_ports.nsect_addr);
- while (retries-- && ((ireason & CD) == 0 || (ireason & IO))) {
- printk(KERN_ERR "ide-tape: (IO,CoD != (0,1) while issuing "
- "a packet command, retrying\n");
- udelay(100);
- ireason = hwif->INB(hwif->io_ports.nsect_addr);
- if (retries == 0) {
- printk(KERN_ERR "ide-tape: (IO,CoD != (0,1) while "
- "issuing a packet command, ignoring\n");
- ireason |= CD;
- ireason &= ~IO;
- }
- }
- if ((ireason & CD) == 0 || (ireason & IO)) {
- printk(KERN_ERR "ide-tape: (IO,CoD) != (0,1) while issuing "
- "a packet command\n");
- return ide_do_reset(drive);
- }
- /* Set the interrupt routine */
- ide_set_handler(drive, &idetape_pc_intr, IDETAPE_WAIT_CMD, NULL);
-#ifdef CONFIG_BLK_DEV_IDEDMA
- /* Begin DMA, if necessary */
- if (pc->flags & PC_FLAG_DMA_IN_PROGRESS)
- hwif->dma_ops->dma_start(drive);
-#endif
- /* Send the actual packet */
- hwif->output_data(drive, NULL, pc->c, 12);
- return ide_started;
+ return ide_transfer_pc(drive, tape->pc, idetape_pc_intr,
+ IDETAPE_WAIT_CMD, NULL);
}
static ide_startstop_t idetape_issue_pc(ide_drive_t *drive,
struct ide_atapi_pc *pc)
{
- ide_hwif_t *hwif = drive->hwif;
idetape_tape_t *tape = drive->driver_data;
- int dma_ok = 0;
- u16 bcount;
if (tape->pc->c[0] == REQUEST_SENSE &&
pc->c[0] == REQUEST_SENSE) {
@@ -1025,50 +887,15 @@ static ide_startstop_t idetape_issue_pc(ide_drive_t *drive,
pc->error = IDETAPE_ERROR_GENERAL;
}
tape->failed_pc = NULL;
- return pc->idetape_callback(drive);
+ pc->callback(drive);
+ return ide_stopped;
}
debug_log(DBG_SENSE, "Retry #%d, cmd = %02X\n", pc->retries, pc->c[0]);
pc->retries++;
- /* We haven't transferred any data yet */
- pc->xferred = 0;
- pc->cur_pos = pc->buf;
- /* Request to transfer the entire buffer at once */
- bcount = pc->req_xfer;
- if (pc->flags & PC_FLAG_DMA_ERROR) {
- pc->flags &= ~PC_FLAG_DMA_ERROR;
- printk(KERN_WARNING "ide-tape: DMA disabled, "
- "reverting to PIO\n");
- ide_dma_off(drive);
- }
- if ((pc->flags & PC_FLAG_DMA_RECOMMENDED) && drive->using_dma)
- dma_ok = !hwif->dma_ops->dma_setup(drive);
-
- ide_pktcmd_tf_load(drive, IDE_TFLAG_NO_SELECT_MASK |
- IDE_TFLAG_OUT_DEVICE, bcount, dma_ok);
-
- if (dma_ok)
- /* Will begin DMA later */
- pc->flags |= PC_FLAG_DMA_IN_PROGRESS;
- if (test_bit(IDETAPE_FLAG_DRQ_INTERRUPT, &tape->flags)) {
- ide_execute_command(drive, WIN_PACKETCMD, &idetape_transfer_pc,
- IDETAPE_WAIT_CMD, NULL);
- return ide_started;
- } else {
- ide_execute_pkt_cmd(drive);
- return idetape_transfer_pc(drive);
- }
-}
-
-static ide_startstop_t idetape_pc_callback(ide_drive_t *drive)
-{
- idetape_tape_t *tape = drive->driver_data;
-
- debug_log(DBG_PROCS, "Enter %s\n", __func__);
-
- idetape_end_request(drive, tape->pc->error ? 0 : 1, 0);
- return ide_stopped;
+ return ide_issue_pc(drive, pc, idetape_transfer_pc,
+ IDETAPE_WAIT_CMD, NULL);
}
/* A mode sense command is used to "sense" tape parameters. */
@@ -1096,7 +923,6 @@ static void idetape_create_mode_sense_cmd(struct ide_atapi_pc *pc, u8 page_code)
pc->req_xfer = 24;
else
pc->req_xfer = 50;
- pc->idetape_callback = &idetape_pc_callback;
}
static ide_startstop_t idetape_media_access_finished(ide_drive_t *drive)
@@ -1114,80 +940,41 @@ static ide_startstop_t idetape_media_access_finished(ide_drive_t *drive)
printk(KERN_ERR "ide-tape: %s: I/O error, ",
tape->name);
/* Retry operation */
- return idetape_retry_pc(drive);
+ idetape_retry_pc(drive);
+ return ide_stopped;
}
pc->error = 0;
- if (tape->failed_pc == pc)
- tape->failed_pc = NULL;
} else {
pc->error = IDETAPE_ERROR_GENERAL;
tape->failed_pc = NULL;
}
- return pc->idetape_callback(drive);
-}
-
-static ide_startstop_t idetape_rw_callback(ide_drive_t *drive)
-{
- idetape_tape_t *tape = drive->driver_data;
- struct request *rq = HWGROUP(drive)->rq;
- int blocks = tape->pc->xferred / tape->blk_size;
-
- tape->avg_size += blocks * tape->blk_size;
-
- if (time_after_eq(jiffies, tape->avg_time + HZ)) {
- tape->avg_speed = tape->avg_size * HZ /
- (jiffies - tape->avg_time) / 1024;
- tape->avg_size = 0;
- tape->avg_time = jiffies;
- }
- debug_log(DBG_PROCS, "Enter %s\n", __func__);
-
- tape->first_frame += blocks;
- rq->current_nr_sectors -= blocks;
-
- if (!tape->pc->error)
- idetape_end_request(drive, 1, 0);
- else
- idetape_end_request(drive, tape->pc->error, 0);
+ pc->callback(drive);
return ide_stopped;
}
-static void idetape_create_read_cmd(idetape_tape_t *tape,
- struct ide_atapi_pc *pc,
- unsigned int length, struct idetape_bh *bh)
+static void ide_tape_create_rw_cmd(idetape_tape_t *tape,
+ struct ide_atapi_pc *pc, unsigned int length,
+ struct idetape_bh *bh, u8 opcode)
{
idetape_init_pc(pc);
- pc->c[0] = READ_6;
put_unaligned(cpu_to_be32(length), (unsigned int *) &pc->c[1]);
pc->c[1] = 1;
- pc->idetape_callback = &idetape_rw_callback;
pc->bh = bh;
- atomic_set(&bh->b_count, 0);
pc->buf = NULL;
pc->buf_size = length * tape->blk_size;
pc->req_xfer = pc->buf_size;
if (pc->req_xfer == tape->buffer_size)
- pc->flags |= PC_FLAG_DMA_RECOMMENDED;
-}
+ pc->flags |= PC_FLAG_DMA_OK;
-static void idetape_create_write_cmd(idetape_tape_t *tape,
- struct ide_atapi_pc *pc,
- unsigned int length, struct idetape_bh *bh)
-{
- idetape_init_pc(pc);
- pc->c[0] = WRITE_6;
- put_unaligned(cpu_to_be32(length), (unsigned int *) &pc->c[1]);
- pc->c[1] = 1;
- pc->idetape_callback = &idetape_rw_callback;
- pc->flags |= PC_FLAG_WRITING;
- pc->bh = bh;
- pc->b_data = bh->b_data;
- pc->b_count = atomic_read(&bh->b_count);
- pc->buf = NULL;
- pc->buf_size = length * tape->blk_size;
- pc->req_xfer = pc->buf_size;
- if (pc->req_xfer == tape->buffer_size)
- pc->flags |= PC_FLAG_DMA_RECOMMENDED;
+ if (opcode == READ_6) {
+ pc->c[0] = READ_6;
+ atomic_set(&bh->b_count, 0);
+ } else if (opcode == WRITE_6) {
+ pc->c[0] = WRITE_6;
+ pc->flags |= PC_FLAG_WRITING;
+ pc->b_data = bh->b_data;
+ pc->b_count = atomic_read(&bh->b_count);
+ }
}
static ide_startstop_t idetape_do_request(ide_drive_t *drive,
@@ -1211,8 +998,10 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive,
}
/* Retry a failed packet command */
- if (tape->failed_pc && tape->pc->c[0] == REQUEST_SENSE)
- return idetape_issue_pc(drive, tape->failed_pc);
+ if (tape->failed_pc && tape->pc->c[0] == REQUEST_SENSE) {
+ pc = tape->failed_pc;
+ goto out;
+ }
if (postponed_rq != NULL)
if (rq != postponed_rq) {
@@ -1262,14 +1051,16 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive,
}
if (rq->cmd[0] & REQ_IDETAPE_READ) {
pc = idetape_next_pc_storage(drive);
- idetape_create_read_cmd(tape, pc, rq->current_nr_sectors,
- (struct idetape_bh *)rq->special);
+ ide_tape_create_rw_cmd(tape, pc, rq->current_nr_sectors,
+ (struct idetape_bh *)rq->special,
+ READ_6);
goto out;
}
if (rq->cmd[0] & REQ_IDETAPE_WRITE) {
pc = idetape_next_pc_storage(drive);
- idetape_create_write_cmd(tape, pc, rq->current_nr_sectors,
- (struct idetape_bh *)rq->special);
+ ide_tape_create_rw_cmd(tape, pc, rq->current_nr_sectors,
+ (struct idetape_bh *)rq->special,
+ WRITE_6);
goto out;
}
if (rq->cmd[0] & REQ_IDETAPE_PC1) {
@@ -1284,6 +1075,9 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive,
}
BUG();
out:
+ if (test_bit(IDETAPE_FLAG_DRQ_INTERRUPT, &tape->flags))
+ pc->flags |= PC_FLAG_DRQ_INTERRUPT;
+
return idetape_issue_pc(drive, pc);
}
@@ -1447,40 +1241,6 @@ static void idetape_init_merge_buffer(idetape_tape_t *tape)
}
}
-static ide_startstop_t idetape_read_position_callback(ide_drive_t *drive)
-{
- idetape_tape_t *tape = drive->driver_data;
- u8 *readpos = tape->pc->buf;
-
- debug_log(DBG_PROCS, "Enter %s\n", __func__);
-
- if (!tape->pc->error) {
- debug_log(DBG_SENSE, "BOP - %s\n",
- (readpos[0] & 0x80) ? "Yes" : "No");
- debug_log(DBG_SENSE, "EOP - %s\n",
- (readpos[0] & 0x40) ? "Yes" : "No");
-
- if (readpos[0] & 0x4) {
- printk(KERN_INFO "ide-tape: Block location is unknown"
- "to the tape\n");
- clear_bit(IDETAPE_FLAG_ADDRESS_VALID, &tape->flags);
- idetape_end_request(drive, 0, 0);
- } else {
- debug_log(DBG_SENSE, "Block Location - %u\n",
- be32_to_cpu(*(u32 *)&readpos[4]));
-
- tape->partition = readpos[1];
- tape->first_frame =
- be32_to_cpu(*(u32 *)&readpos[4]);
- set_bit(IDETAPE_FLAG_ADDRESS_VALID, &tape->flags);
- idetape_end_request(drive, 1, 0);
- }
- } else {
- idetape_end_request(drive, 0, 0);
- }
- return ide_stopped;
-}
-
/*
* Write a filemark if write_filemark=1. Flush the device buffers without
* writing a filemark otherwise.
@@ -1492,14 +1252,12 @@ static void idetape_create_write_filemark_cmd(ide_drive_t *drive,
pc->c[0] = WRITE_FILEMARKS;
pc->c[4] = write_filemark;
pc->flags |= PC_FLAG_WAIT_FOR_DSC;
- pc->idetape_callback = &idetape_pc_callback;
}
static void idetape_create_test_unit_ready_cmd(struct ide_atapi_pc *pc)
{
idetape_init_pc(pc);
pc->c[0] = TEST_UNIT_READY;
- pc->idetape_callback = &idetape_pc_callback;
}
/*
@@ -1518,12 +1276,16 @@ static void idetape_create_test_unit_ready_cmd(struct ide_atapi_pc *pc)
static int idetape_queue_pc_tail(ide_drive_t *drive, struct ide_atapi_pc *pc)
{
struct ide_tape_obj *tape = drive->driver_data;
- struct request rq;
+ struct request *rq;
+ int error;
- idetape_init_rq(&rq, REQ_IDETAPE_PC1);
- rq.buffer = (char *) pc;
- rq.rq_disk = tape->disk;
- return ide_do_drive_cmd(drive, &rq, ide_wait);
+ rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
+ rq->cmd_type = REQ_TYPE_SPECIAL;
+ rq->cmd[0] = REQ_IDETAPE_PC1;
+ rq->buffer = (char *)pc;
+ error = blk_execute_rq(drive->queue, tape->disk, rq, 0);
+ blk_put_request(rq);
+ return error;
}
static void idetape_create_load_unload_cmd(ide_drive_t *drive,
@@ -1533,7 +1295,6 @@ static void idetape_create_load_unload_cmd(ide_drive_t *drive,
pc->c[0] = START_STOP;
pc->c[4] = cmd;
pc->flags |= PC_FLAG_WAIT_FOR_DSC;
- pc->idetape_callback = &idetape_pc_callback;
}
static int idetape_wait_ready(ide_drive_t *drive, unsigned long timeout)
@@ -1585,7 +1346,6 @@ static void idetape_create_read_position_cmd(struct ide_atapi_pc *pc)
idetape_init_pc(pc);
pc->c[0] = READ_POSITION;
pc->req_xfer = 20;
- pc->idetape_callback = &idetape_read_position_callback;
}
static int idetape_read_position(ide_drive_t *drive)
@@ -1613,7 +1373,6 @@ static void idetape_create_locate_cmd(ide_drive_t *drive,
put_unaligned(cpu_to_be32(block), (unsigned int *) &pc->c[3]);
pc->c[8] = partition;
pc->flags |= PC_FLAG_WAIT_FOR_DSC;
- pc->idetape_callback = &idetape_pc_callback;
}
static int idetape_create_prevent_cmd(ide_drive_t *drive,
@@ -1628,7 +1387,6 @@ static int idetape_create_prevent_cmd(ide_drive_t *drive,
idetape_init_pc(pc);
pc->c[0] = ALLOW_MEDIUM_REMOVAL;
pc->c[4] = prevent;
- pc->idetape_callback = &idetape_pc_callback;
return 1;
}
@@ -1700,26 +1458,33 @@ static int idetape_queue_rw_tail(ide_drive_t *drive, int cmd, int blocks,
struct idetape_bh *bh)
{
idetape_tape_t *tape = drive->driver_data;
- struct request rq;
+ struct request *rq;
+ int ret, errors;
debug_log(DBG_SENSE, "%s: cmd=%d\n", __func__, cmd);
- idetape_init_rq(&rq, cmd);
- rq.rq_disk = tape->disk;
- rq.special = (void *)bh;
- rq.sector = tape->first_frame;
- rq.nr_sectors = blocks;
- rq.current_nr_sectors = blocks;
- (void) ide_do_drive_cmd(drive, &rq, ide_wait);
+ rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
+ rq->cmd_type = REQ_TYPE_SPECIAL;
+ rq->cmd[0] = cmd;
+ rq->rq_disk = tape->disk;
+ rq->special = (void *)bh;
+ rq->sector = tape->first_frame;
+ rq->nr_sectors = blocks;
+ rq->current_nr_sectors = blocks;
+ blk_execute_rq(drive->queue, tape->disk, rq, 0);
+
+ errors = rq->errors;
+ ret = tape->blk_size * (blocks - rq->current_nr_sectors);
+ blk_put_request(rq);
if ((cmd & (REQ_IDETAPE_READ | REQ_IDETAPE_WRITE)) == 0)
return 0;
if (tape->merge_bh)
idetape_init_merge_buffer(tape);
- if (rq.errors == IDETAPE_ERROR_GENERAL)
+ if (errors == IDETAPE_ERROR_GENERAL)
return -EIO;
- return (tape->blk_size * (blocks-rq.current_nr_sectors));
+ return ret;
}
static void idetape_create_inquiry_cmd(struct ide_atapi_pc *pc)
@@ -1728,7 +1493,6 @@ static void idetape_create_inquiry_cmd(struct ide_atapi_pc *pc)
pc->c[0] = INQUIRY;
pc->c[4] = 254;
pc->req_xfer = 254;
- pc->idetape_callback = &idetape_pc_callback;
}
static void idetape_create_rewind_cmd(ide_drive_t *drive,
@@ -1737,7 +1501,6 @@ static void idetape_create_rewind_cmd(ide_drive_t *drive,
idetape_init_pc(pc);
pc->c[0] = REZERO_UNIT;
pc->flags |= PC_FLAG_WAIT_FOR_DSC;
- pc->idetape_callback = &idetape_pc_callback;
}
static void idetape_create_erase_cmd(struct ide_atapi_pc *pc)
@@ -1746,7 +1509,6 @@ static void idetape_create_erase_cmd(struct ide_atapi_pc *pc)
pc->c[0] = ERASE;
pc->c[1] = 1;
pc->flags |= PC_FLAG_WAIT_FOR_DSC;
- pc->idetape_callback = &idetape_pc_callback;
}
static void idetape_create_space_cmd(struct ide_atapi_pc *pc, int count, u8 cmd)
@@ -1756,7 +1518,6 @@ static void idetape_create_space_cmd(struct ide_atapi_pc *pc, int count, u8 cmd)
put_unaligned(cpu_to_be32(count), (unsigned int *) &pc->c[1]);
pc->c[1] = cmd;
pc->flags |= PC_FLAG_WAIT_FOR_DSC;
- pc->idetape_callback = &idetape_pc_callback;
}
/* Queue up a character device originated write request. */
@@ -2746,9 +2507,8 @@ static void idetape_setup(ide_drive_t *drive, idetape_tape_t *tape, int minor)
* Ensure that the number we got makes sense; limit it within
* IDETAPE_DSC_RW_MIN and IDETAPE_DSC_RW_MAX.
*/
- tape->best_dsc_rw_freq = max_t(unsigned long,
- min_t(unsigned long, t, IDETAPE_DSC_RW_MAX),
- IDETAPE_DSC_RW_MIN);
+ tape->best_dsc_rw_freq = clamp_t(unsigned long, t, IDETAPE_DSC_RW_MIN,
+ IDETAPE_DSC_RW_MAX);
printk(KERN_INFO "ide-tape: %s <-> %s: %dKBps, %d*%dkB buffer, "
"%lums tDSC%s\n",
drive->name, tape->name, *(u16 *)&tape->caps[14],
@@ -2900,11 +2660,6 @@ static int ide_tape_probe(ide_drive_t *drive)
" the driver\n", drive->name);
goto failed;
}
- if (drive->scsi) {
- printk(KERN_INFO "ide-tape: passing drive %s to ide-scsi"
- " emulation.\n", drive->name);
- goto failed;
- }
tape = kzalloc(sizeof(idetape_tape_t), GFP_KERNEL);
if (tape == NULL) {
printk(KERN_ERR "ide-tape: %s: Can't allocate a tape struct\n",
@@ -2938,10 +2693,12 @@ static int ide_tape_probe(ide_drive_t *drive)
idetape_setup(drive, tape, minor);
- device_create(idetape_sysfs_class, &drive->gendev,
- MKDEV(IDETAPE_MAJOR, minor), "%s", tape->name);
- device_create(idetape_sysfs_class, &drive->gendev,
- MKDEV(IDETAPE_MAJOR, minor + 128), "n%s", tape->name);
+ device_create_drvdata(idetape_sysfs_class, &drive->gendev,
+ MKDEV(IDETAPE_MAJOR, minor), NULL,
+ "%s", tape->name);
+ device_create_drvdata(idetape_sysfs_class, &drive->gendev,
+ MKDEV(IDETAPE_MAJOR, minor + 128), NULL,
+ "n%s", tape->name);
g->fops = &idetape_block_ops;
ide_register_region(g);
diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c
index 0c908ca3ff79..3675b5d4fe1a 100644
--- a/drivers/ide/ide-taskfile.c
+++ b/drivers/ide/ide-taskfile.c
@@ -8,28 +8,17 @@
* The big the bad and the ugly.
*/
-#include <linux/module.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
-#include <linux/major.h>
#include <linux/errno.h>
-#include <linux/genhd.h>
-#include <linux/blkpg.h>
#include <linux/slab.h>
-#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/hdreg.h>
#include <linux/ide.h>
-#include <linux/bitops.h>
-#include <linux/scatterlist.h>
-#include <asm/byteorder.h>
-#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/io.h>
@@ -62,25 +51,6 @@ int taskfile_lib_get_identify (ide_drive_t *drive, u8 *buf)
return ide_raw_taskfile(drive, &args, buf, 1);
}
-static int inline task_dma_ok(ide_task_t *task)
-{
- if (blk_fs_request(task->rq) || (task->tf_flags & IDE_TFLAG_FLAGGED))
- return 1;
-
- switch (task->tf.command) {
- case WIN_WRITEDMA_ONCE:
- case WIN_WRITEDMA:
- case WIN_WRITEDMA_EXT:
- case WIN_READDMA_ONCE:
- case WIN_READDMA:
- case WIN_READDMA_EXT:
- case WIN_IDENTIFY_DMA:
- return 1;
- }
-
- return 0;
-}
-
static ide_startstop_t task_no_data_intr(ide_drive_t *);
static ide_startstop_t set_geometry_intr(ide_drive_t *);
static ide_startstop_t recal_intr(ide_drive_t *);
@@ -109,13 +79,15 @@ ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task)
if ((task->tf_flags & IDE_TFLAG_DMA_PIO_FALLBACK) == 0) {
ide_tf_dump(drive->name, tf);
+ ide_set_irq(drive, 1);
+ SELECT_MASK(drive, 0);
hwif->tf_load(drive, task);
}
switch (task->data_phase) {
case TASKFILE_MULTI_OUT:
case TASKFILE_OUT:
- hwif->OUTBSYNC(drive, tf->command, hwif->io_ports.command_addr);
+ hwif->OUTBSYNC(hwif, tf->command, hwif->io_ports.command_addr);
ndelay(400); /* FIXME */
return pre_task_out_intr(drive, task->rq);
case TASKFILE_MULTI_IN:
@@ -137,8 +109,7 @@ ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task)
WAIT_WORSTCASE, NULL);
return ide_started;
default:
- if (task_dma_ok(task) == 0 || drive->using_dma == 0 ||
- dma_ops->dma_setup(drive))
+ if (drive->using_dma == 0 || dma_ops->dma_setup(drive))
return ide_stopped;
dma_ops->dma_exec_cmd(drive, tf->command);
dma_ops->dma_start(drive);
@@ -181,7 +152,6 @@ static ide_startstop_t set_geometry_intr(ide_drive_t *drive)
if (stat & (ERR_STAT|DRQ_STAT))
return ide_error(drive, "set_geometry_intr", stat);
- BUG_ON(HWGROUP(drive)->handler != NULL);
ide_set_handler(drive, &set_geometry_intr, WAIT_WORSTCASE, NULL);
return ide_started;
}
@@ -492,11 +462,12 @@ static ide_startstop_t pre_task_out_intr(ide_drive_t *drive, struct request *rq)
int ide_raw_taskfile(ide_drive_t *drive, ide_task_t *task, u8 *buf, u16 nsect)
{
- struct request rq;
+ struct request *rq;
+ int error;
- blk_rq_init(NULL, &rq);
- rq.cmd_type = REQ_TYPE_ATA_TASKFILE;
- rq.buffer = buf;
+ rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
+ rq->cmd_type = REQ_TYPE_ATA_TASKFILE;
+ rq->buffer = buf;
/*
* (ks) We transfer currently only whole sectors.
@@ -504,16 +475,19 @@ int ide_raw_taskfile(ide_drive_t *drive, ide_task_t *task, u8 *buf, u16 nsect)
* if we would find a solution to transfer any size.
* To support special commands like READ LONG.
*/
- rq.hard_nr_sectors = rq.nr_sectors = nsect;
- rq.hard_cur_sectors = rq.current_nr_sectors = nsect;
+ rq->hard_nr_sectors = rq->nr_sectors = nsect;
+ rq->hard_cur_sectors = rq->current_nr_sectors = nsect;
if (task->tf_flags & IDE_TFLAG_WRITE)
- rq.cmd_flags |= REQ_RW;
+ rq->cmd_flags |= REQ_RW;
+
+ rq->special = task;
+ task->rq = rq;
- rq.special = task;
- task->rq = &rq;
+ error = blk_execute_rq(drive->queue, NULL, rq, 0);
+ blk_put_request(rq);
- return ide_do_drive_cmd(drive, &rq, ide_wait);
+ return error;
}
EXPORT_SYMBOL(ide_raw_taskfile);
@@ -739,12 +713,14 @@ int ide_cmd_ioctl (ide_drive_t *drive, unsigned int cmd, unsigned long arg)
struct hd_driveid *id = drive->id;
if (NULL == (void *) arg) {
- struct request rq;
+ struct request *rq;
- ide_init_drive_cmd(&rq);
- rq.cmd_type = REQ_TYPE_ATA_TASKFILE;
+ rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
+ rq->cmd_type = REQ_TYPE_ATA_TASKFILE;
+ err = blk_execute_rq(drive->queue, NULL, rq, 0);
+ blk_put_request(rq);
- return ide_do_drive_cmd(drive, &rq, ide_wait);
+ return err;
}
if (copy_from_user(args, (void __user *)arg, 4))
diff --git a/drivers/ide/ide-timing.h b/drivers/ide/ide-timings.c
index 3b12ffe77071..8c2f8327f487 100644
--- a/drivers/ide/ide-timing.h
+++ b/drivers/ide/ide-timings.c
@@ -1,11 +1,7 @@
-#ifndef _IDE_TIMING_H
-#define _IDE_TIMING_H
-
/*
* Copyright (c) 1999-2001 Vojtech Pavlik
- */
-
-/*
+ * Copyright (c) 2007-2008 Bartlomiej Zolnierkiewicz
+ *
* 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
@@ -27,27 +23,14 @@
#include <linux/kernel.h>
#include <linux/hdreg.h>
-
-#define XFER_PIO_5 0x0d
-#define XFER_UDMA_SLOW 0x4f
-
-struct ide_timing {
- short mode;
- short setup; /* t1 */
- short act8b; /* t2 for 8-bit io */
- short rec8b; /* t2i for 8-bit io */
- short cyc8b; /* t0 for 8-bit io */
- short active; /* t2 or tD */
- short recover; /* t2i or tK */
- short cycle; /* t0 */
- short udma; /* t2CYCTYP/2 */
-};
+#include <linux/ide.h>
+#include <linux/module.h>
/*
* PIO 0-5, MWDMA 0-2 and UDMA 0-6 timings (in nanoseconds).
* These were taken from ATA/ATAPI-6 standard, rev 0a, except
* for PIO 5, which is a nonstandard extension and UDMA6, which
- * is currently supported only by Maxtor drives.
+ * is currently supported only by Maxtor drives.
*/
static struct ide_timing ide_timing[] = {
@@ -61,12 +44,10 @@ static struct ide_timing ide_timing[] = {
{ XFER_UDMA_1, 0, 0, 0, 0, 0, 0, 0, 80 },
{ XFER_UDMA_0, 0, 0, 0, 0, 0, 0, 0, 120 },
- { XFER_UDMA_SLOW, 0, 0, 0, 0, 0, 0, 0, 150 },
-
{ XFER_MW_DMA_2, 25, 0, 0, 0, 70, 25, 120, 0 },
{ XFER_MW_DMA_1, 45, 0, 0, 0, 80, 50, 150, 0 },
{ XFER_MW_DMA_0, 60, 0, 0, 0, 215, 215, 480, 0 },
-
+
{ XFER_SW_DMA_2, 60, 0, 0, 0, 120, 120, 240, 0 },
{ XFER_SW_DMA_1, 90, 0, 0, 0, 240, 240, 480, 0 },
{ XFER_SW_DMA_0, 120, 0, 0, 0, 480, 480, 960, 0 },
@@ -81,30 +62,46 @@ static struct ide_timing ide_timing[] = {
{ XFER_PIO_SLOW, 120, 290, 240, 960, 290, 240, 960, 0 },
- { -1 }
+ { 0xff }
};
-#define IDE_TIMING_SETUP 0x01
-#define IDE_TIMING_ACT8B 0x02
-#define IDE_TIMING_REC8B 0x04
-#define IDE_TIMING_CYC8B 0x08
-#define IDE_TIMING_8BIT 0x0e
-#define IDE_TIMING_ACTIVE 0x10
-#define IDE_TIMING_RECOVER 0x20
-#define IDE_TIMING_CYCLE 0x40
-#define IDE_TIMING_UDMA 0x80
-#define IDE_TIMING_ALL 0xff
-
-#define FIT(v,vmin,vmax) max_t(short,min_t(short,v,vmax),vmin)
-#define ENOUGH(v,unit) (((v)-1)/(unit)+1)
-#define EZ(v,unit) ((v)?ENOUGH(v,unit):0)
-
-#define XFER_MODE 0xf0
-#define XFER_MWDMA 0x20
-#define XFER_EPIO 0x01
-#define XFER_PIO 0x00
-
-static void ide_timing_quantize(struct ide_timing *t, struct ide_timing *q, int T, int UT)
+struct ide_timing *ide_timing_find_mode(u8 speed)
+{
+ struct ide_timing *t;
+
+ for (t = ide_timing; t->mode != speed; t++)
+ if (t->mode == 0xff)
+ return NULL;
+ return t;
+}
+EXPORT_SYMBOL_GPL(ide_timing_find_mode);
+
+u16 ide_pio_cycle_time(ide_drive_t *drive, u8 pio)
+{
+ struct hd_driveid *id = drive->id;
+ struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
+ u16 cycle = 0;
+
+ if (id->field_valid & 2) {
+ if (id->capability & 8)
+ cycle = id->eide_pio_iordy;
+ else
+ cycle = id->eide_pio;
+
+ /* conservative "downgrade" for all pre-ATA2 drives */
+ if (pio < 3 && cycle < t->cycle)
+ cycle = 0; /* use standard timing */
+ }
+
+ return cycle ? cycle : t->cycle;
+}
+EXPORT_SYMBOL_GPL(ide_pio_cycle_time);
+
+#define ENOUGH(v, unit) (((v) - 1) / (unit) + 1)
+#define EZ(v, unit) ((v) ? ENOUGH(v, unit) : 0)
+
+static void ide_timing_quantize(struct ide_timing *t, struct ide_timing *q,
+ int T, int UT)
{
q->setup = EZ(t->setup * 1000, T);
q->act8b = EZ(t->act8b * 1000, T);
@@ -116,92 +113,83 @@ static void ide_timing_quantize(struct ide_timing *t, struct ide_timing *q, int
q->udma = EZ(t->udma * 1000, UT);
}
-static void ide_timing_merge(struct ide_timing *a, struct ide_timing *b, struct ide_timing *m, unsigned int what)
-{
- if (what & IDE_TIMING_SETUP ) m->setup = max(a->setup, b->setup);
- if (what & IDE_TIMING_ACT8B ) m->act8b = max(a->act8b, b->act8b);
- if (what & IDE_TIMING_REC8B ) m->rec8b = max(a->rec8b, b->rec8b);
- if (what & IDE_TIMING_CYC8B ) m->cyc8b = max(a->cyc8b, b->cyc8b);
- if (what & IDE_TIMING_ACTIVE ) m->active = max(a->active, b->active);
- if (what & IDE_TIMING_RECOVER) m->recover = max(a->recover, b->recover);
- if (what & IDE_TIMING_CYCLE ) m->cycle = max(a->cycle, b->cycle);
- if (what & IDE_TIMING_UDMA ) m->udma = max(a->udma, b->udma);
-}
-
-static struct ide_timing* ide_timing_find_mode(short speed)
+void ide_timing_merge(struct ide_timing *a, struct ide_timing *b,
+ struct ide_timing *m, unsigned int what)
{
- struct ide_timing *t;
-
- for (t = ide_timing; t->mode != speed; t++)
- if (t->mode < 0)
- return NULL;
- return t;
+ if (what & IDE_TIMING_SETUP)
+ m->setup = max(a->setup, b->setup);
+ if (what & IDE_TIMING_ACT8B)
+ m->act8b = max(a->act8b, b->act8b);
+ if (what & IDE_TIMING_REC8B)
+ m->rec8b = max(a->rec8b, b->rec8b);
+ if (what & IDE_TIMING_CYC8B)
+ m->cyc8b = max(a->cyc8b, b->cyc8b);
+ if (what & IDE_TIMING_ACTIVE)
+ m->active = max(a->active, b->active);
+ if (what & IDE_TIMING_RECOVER)
+ m->recover = max(a->recover, b->recover);
+ if (what & IDE_TIMING_CYCLE)
+ m->cycle = max(a->cycle, b->cycle);
+ if (what & IDE_TIMING_UDMA)
+ m->udma = max(a->udma, b->udma);
}
+EXPORT_SYMBOL_GPL(ide_timing_merge);
-static int ide_timing_compute(ide_drive_t *drive, short speed, struct ide_timing *t, int T, int UT)
+int ide_timing_compute(ide_drive_t *drive, u8 speed,
+ struct ide_timing *t, int T, int UT)
{
struct hd_driveid *id = drive->id;
struct ide_timing *s, p;
-/*
- * Find the mode.
- */
-
- if (!(s = ide_timing_find_mode(speed)))
+ /*
+ * Find the mode.
+ */
+ s = ide_timing_find_mode(speed);
+ if (s == NULL)
return -EINVAL;
-/*
- * Copy the timing from the table.
- */
-
+ /*
+ * Copy the timing from the table.
+ */
*t = *s;
-/*
- * If the drive is an EIDE drive, it can tell us it needs extended
- * PIO/MWDMA cycle timing.
- */
-
+ /*
+ * If the drive is an EIDE drive, it can tell us it needs extended
+ * PIO/MWDMA cycle timing.
+ */
if (id && id->field_valid & 2) { /* EIDE drive */
memset(&p, 0, sizeof(p));
- switch (speed & XFER_MODE) {
-
- case XFER_PIO:
- if (speed <= XFER_PIO_2) p.cycle = p.cyc8b = id->eide_pio;
- else p.cycle = p.cyc8b = id->eide_pio_iordy;
- break;
-
- case XFER_MWDMA:
- p.cycle = id->eide_dma_min;
- break;
- }
+ if (speed <= XFER_PIO_2)
+ p.cycle = p.cyc8b = id->eide_pio;
+ else if (speed <= XFER_PIO_5)
+ p.cycle = p.cyc8b = id->eide_pio_iordy;
+ else if (speed >= XFER_MW_DMA_0 && speed <= XFER_MW_DMA_2)
+ p.cycle = id->eide_dma_min;
ide_timing_merge(&p, t, t, IDE_TIMING_CYCLE | IDE_TIMING_CYC8B);
}
-/*
- * Convert the timing to bus clock counts.
- */
-
+ /*
+ * Convert the timing to bus clock counts.
+ */
ide_timing_quantize(t, t, T, UT);
-/*
- * Even in DMA/UDMA modes we still use PIO access for IDENTIFY, S.M.A.R.T
- * and some other commands. We have to ensure that the DMA cycle timing is
- * slower/equal than the fastest PIO timing.
- */
-
- if ((speed & XFER_MODE) != XFER_PIO) {
+ /*
+ * Even in DMA/UDMA modes we still use PIO access for IDENTIFY,
+ * S.M.A.R.T and some other commands. We have to ensure that the
+ * DMA cycle timing is slower/equal than the fastest PIO timing.
+ */
+ if (speed >= XFER_SW_DMA_0) {
u8 pio = ide_get_best_pio_mode(drive, 255, 5);
ide_timing_compute(drive, XFER_PIO_0 + pio, &p, T, UT);
ide_timing_merge(&p, t, t, IDE_TIMING_ALL);
}
-/*
- * Lengthen active & recovery time so that cycle time is correct.
- */
-
+ /*
+ * Lengthen active & recovery time so that cycle time is correct.
+ */
if (t->act8b + t->rec8b < t->cyc8b) {
t->act8b += (t->cyc8b - (t->act8b + t->rec8b)) / 2;
t->rec8b = t->cyc8b - t->act8b;
@@ -214,5 +202,4 @@ static int ide_timing_compute(ide_drive_t *drive, short speed, struct ide_timing
return 0;
}
-
-#endif
+EXPORT_SYMBOL_GPL(ide_timing_compute);
diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c
index c758dcb13b14..b0739c98a06e 100644
--- a/drivers/ide/ide.c
+++ b/drivers/ide/ide.c
@@ -50,29 +50,16 @@
#include <linux/types.h>
#include <linux/string.h>
#include <linux/kernel.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/major.h>
#include <linux/errno.h>
#include <linux/genhd.h>
-#include <linux/blkpg.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/pci.h>
-#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/completion.h>
-#include <linux/reboot.h>
-#include <linux/cdrom.h>
-#include <linux/seq_file.h>
#include <linux/device.h>
-#include <linux/bitops.h>
-
-#include <asm/byteorder.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
/* default maximum number of failures */
@@ -86,15 +73,10 @@ static const u8 ide_hwif_to_major[] = { IDE0_MAJOR, IDE1_MAJOR,
IDE6_MAJOR, IDE7_MAJOR,
IDE8_MAJOR, IDE9_MAJOR };
-static int idebus_parameter; /* holds the "idebus=" parameter */
-static int system_bus_speed; /* holds what we think is VESA/PCI bus speed */
-
DEFINE_MUTEX(ide_cfg_mtx);
- __cacheline_aligned_in_smp DEFINE_SPINLOCK(ide_lock);
-int noautodma = 0;
-
-ide_hwif_t ide_hwifs[MAX_HWIFS]; /* master data repository */
+__cacheline_aligned_in_smp DEFINE_SPINLOCK(ide_lock);
+EXPORT_SYMBOL(ide_lock);
static void ide_port_init_devices_data(ide_hwif_t *);
@@ -124,7 +106,6 @@ void ide_init_port_data(ide_hwif_t *hwif, unsigned int index)
ide_port_init_devices_data(hwif);
}
-EXPORT_SYMBOL_GPL(ide_init_port_data);
static void ide_port_init_devices_data(ide_hwif_t *hwif)
{
@@ -139,7 +120,6 @@ static void ide_port_init_devices_data(ide_hwif_t *hwif)
drive->media = ide_disk;
drive->select.all = (unit<<4)|0xa0;
drive->hwif = hwif;
- drive->ctl = 0x08;
drive->ready_stat = READY_STAT;
drive->bad_wstat = BAD_W_STAT;
drive->special.b.recalibrate = 1;
@@ -154,73 +134,6 @@ static void ide_port_init_devices_data(ide_hwif_t *hwif)
}
}
-/*
- * init_ide_data() sets reasonable default values into all fields
- * of all instances of the hwifs and drives, but only on the first call.
- * Subsequent calls have no effect (they don't wipe out anything).
- *
- * This routine is normally called at driver initialization time,
- * but may also be called MUCH earlier during kernel "command-line"
- * parameter processing. As such, we cannot depend on any other parts
- * of the kernel (such as memory allocation) to be functioning yet.
- *
- * This is too bad, as otherwise we could dynamically allocate the
- * ide_drive_t structs as needed, rather than always consuming memory
- * for the max possible number (MAX_HWIFS * MAX_DRIVES) of them.
- *
- * FIXME: We should stuff the setup data into __init and copy the
- * relevant hwifs/allocate them properly during boot.
- */
-#define MAGIC_COOKIE 0x12345678
-static void __init init_ide_data (void)
-{
- unsigned int index;
- static unsigned long magic_cookie = MAGIC_COOKIE;
-
- if (magic_cookie != MAGIC_COOKIE)
- return; /* already initialized */
- magic_cookie = 0;
-
- /* Initialise all interface structures */
- for (index = 0; index < MAX_HWIFS; ++index) {
- ide_hwif_t *hwif = &ide_hwifs[index];
-
- ide_init_port_data(hwif, index);
- }
-}
-
-/**
- * ide_system_bus_speed - guess bus speed
- *
- * ide_system_bus_speed() returns what we think is the system VESA/PCI
- * bus speed (in MHz). This is used for calculating interface PIO timings.
- * The default is 40 for known PCI systems, 50 otherwise.
- * The "idebus=xx" parameter can be used to override this value.
- * The actual value to be used is computed/displayed the first time
- * through. Drivers should only use this as a last resort.
- *
- * Returns a guessed speed in MHz.
- */
-
-static int ide_system_bus_speed(void)
-{
-#ifdef CONFIG_PCI
- static struct pci_device_id pci_default[] = {
- { PCI_DEVICE(PCI_ANY_ID, PCI_ANY_ID) },
- { }
- };
-#else
-#define pci_default 0
-#endif /* CONFIG_PCI */
-
- /* user supplied value */
- if (idebus_parameter)
- return idebus_parameter;
-
- /* safe default value for PCI or VESA and PCI*/
- return pci_dev_present(pci_default) ? 33 : 50;
-}
-
void ide_remove_port_from_hwgroup(ide_hwif_t *hwif)
{
ide_hwgroup_t *hwgroup = hwif->hwgroup;
@@ -371,7 +284,8 @@ void ide_init_port_hw(ide_hwif_t *hwif, hw_regs_t *hw)
memcpy(&hwif->io_ports, &hw->io_ports, sizeof(hwif->io_ports));
hwif->irq = hw->irq;
hwif->chipset = hw->chipset;
- hwif->gendev.parent = hw->dev;
+ hwif->dev = hw->dev;
+ hwif->gendev.parent = hw->parent ? hw->parent : hw->dev;
hwif->ack_intr = hw->ack_intr;
}
EXPORT_SYMBOL_GPL(ide_init_port_hw);
@@ -498,7 +412,7 @@ out:
int set_pio_mode(ide_drive_t *drive, int arg)
{
- struct request rq;
+ struct request *rq;
ide_hwif_t *hwif = drive->hwif;
const struct ide_port_ops *port_ops = hwif->port_ops;
@@ -512,12 +426,15 @@ int set_pio_mode(ide_drive_t *drive, int arg)
if (drive->special.b.set_tune)
return -EBUSY;
- ide_init_drive_cmd(&rq);
- rq.cmd_type = REQ_TYPE_ATA_TASKFILE;
+ rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
+ rq->cmd_type = REQ_TYPE_ATA_TASKFILE;
drive->tune_req = (u8) arg;
drive->special.b.set_tune = 1;
- (void) ide_do_drive_cmd(drive, &rq, ide_wait);
+
+ blk_execute_rq(drive->queue, NULL, rq, 0);
+ blk_put_request(rq);
+
return 0;
}
@@ -537,25 +454,11 @@ static int set_unmaskirq(ide_drive_t *drive, int arg)
return 0;
}
-/**
- * system_bus_clock - clock guess
- *
- * External version of the bus clock guess used by very old IDE drivers
- * for things like VLB timings. Should not be used.
- */
-
-int system_bus_clock (void)
-{
- return system_bus_speed;
-}
-
-EXPORT_SYMBOL(system_bus_clock);
-
static int generic_ide_suspend(struct device *dev, pm_message_t mesg)
{
ide_drive_t *drive = dev->driver_data;
ide_hwif_t *hwif = HWIF(drive);
- struct request rq;
+ struct request *rq;
struct request_pm_state rqpm;
ide_task_t args;
int ret;
@@ -564,18 +467,19 @@ static int generic_ide_suspend(struct device *dev, pm_message_t mesg)
if (!(drive->dn % 2))
ide_acpi_get_timing(hwif);
- blk_rq_init(NULL, &rq);
memset(&rqpm, 0, sizeof(rqpm));
memset(&args, 0, sizeof(args));
- rq.cmd_type = REQ_TYPE_PM_SUSPEND;
- rq.special = &args;
- rq.data = &rqpm;
+ rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
+ rq->cmd_type = REQ_TYPE_PM_SUSPEND;
+ rq->special = &args;
+ rq->data = &rqpm;
rqpm.pm_step = ide_pm_state_start_suspend;
if (mesg.event == PM_EVENT_PRETHAW)
mesg.event = PM_EVENT_FREEZE;
rqpm.pm_state = mesg.event;
- ret = ide_do_drive_cmd(drive, &rq, ide_wait);
+ ret = blk_execute_rq(drive->queue, NULL, rq, 0);
+ blk_put_request(rq);
/* only call ACPI _PS3 after both drivers are suspended */
if (!ret && (((drive->dn % 2) && hwif->drives[0].present
&& hwif->drives[1].present)
@@ -589,7 +493,7 @@ static int generic_ide_resume(struct device *dev)
{
ide_drive_t *drive = dev->driver_data;
ide_hwif_t *hwif = HWIF(drive);
- struct request rq;
+ struct request *rq;
struct request_pm_state rqpm;
ide_task_t args;
int err;
@@ -602,16 +506,18 @@ static int generic_ide_resume(struct device *dev)
ide_acpi_exec_tfs(drive);
- blk_rq_init(NULL, &rq);
memset(&rqpm, 0, sizeof(rqpm));
memset(&args, 0, sizeof(args));
- rq.cmd_type = REQ_TYPE_PM_RESUME;
- rq.special = &args;
- rq.data = &rqpm;
+ rq = blk_get_request(drive->queue, READ, __GFP_WAIT);
+ rq->cmd_type = REQ_TYPE_PM_RESUME;
+ rq->cmd_flags |= REQ_PREEMPT;
+ rq->special = &args;
+ rq->data = &rqpm;
rqpm.pm_step = ide_pm_state_start_resume;
rqpm.pm_state = PM_EVENT_ON;
- err = ide_do_drive_cmd(drive, &rq, ide_head_wait);
+ err = blk_execute_rq(drive->queue, NULL, rq, 1);
+ blk_put_request(rq);
if (err == 0 && dev->driver) {
ide_driver_t *drv = to_ide_driver(dev->driver);
@@ -764,212 +670,6 @@ set_val:
EXPORT_SYMBOL(generic_ide_ioctl);
-/*
- * stridx() returns the offset of c within s,
- * or -1 if c is '\0' or not found within s.
- */
-static int __init stridx (const char *s, char c)
-{
- char *i = strchr(s, c);
- return (i && c) ? i - s : -1;
-}
-
-/*
- * match_parm() does parsing for ide_setup():
- *
- * 1. the first char of s must be '='.
- * 2. if the remainder matches one of the supplied keywords,
- * the index (1 based) of the keyword is negated and returned.
- * 3. if the remainder is a series of no more than max_vals numbers
- * separated by commas, the numbers are saved in vals[] and a
- * count of how many were saved is returned. Base10 is assumed,
- * and base16 is allowed when prefixed with "0x".
- * 4. otherwise, zero is returned.
- */
-static int __init match_parm (char *s, const char *keywords[], int vals[], int max_vals)
-{
- static const char *decimal = "0123456789";
- static const char *hex = "0123456789abcdef";
- int i, n;
-
- if (*s++ == '=') {
- /*
- * Try matching against the supplied keywords,
- * and return -(index+1) if we match one
- */
- if (keywords != NULL) {
- for (i = 0; *keywords != NULL; ++i) {
- if (!strcmp(s, *keywords++))
- return -(i+1);
- }
- }
- /*
- * Look for a series of no more than "max_vals"
- * numeric values separated by commas, in base10,
- * or base16 when prefixed with "0x".
- * Return a count of how many were found.
- */
- for (n = 0; (i = stridx(decimal, *s)) >= 0;) {
- vals[n] = i;
- while ((i = stridx(decimal, *++s)) >= 0)
- vals[n] = (vals[n] * 10) + i;
- if (*s == 'x' && !vals[n]) {
- while ((i = stridx(hex, *++s)) >= 0)
- vals[n] = (vals[n] * 0x10) + i;
- }
- if (++n == max_vals)
- break;
- if (*s == ',' || *s == ';')
- ++s;
- }
- if (!*s)
- return n;
- }
- return 0; /* zero = nothing matched */
-}
-
-/*
- * ide_setup() gets called VERY EARLY during initialization,
- * to handle kernel "command line" strings beginning with "hdx=" or "ide".
- *
- * Remember to update Documentation/ide/ide.txt if you change something here.
- */
-static int __init ide_setup(char *s)
-{
- ide_hwif_t *hwif;
- ide_drive_t *drive;
- unsigned int hw, unit;
- int vals[3];
- const char max_drive = 'a' + ((MAX_HWIFS * MAX_DRIVES) - 1);
-
- if (strncmp(s,"hd",2) == 0 && s[2] == '=') /* hd= is for hd.c */
- return 0; /* driver and not us */
-
- if (strncmp(s,"ide",3) && strncmp(s,"idebus",6) && strncmp(s,"hd",2))
- return 0;
-
- printk(KERN_INFO "ide_setup: %s", s);
- init_ide_data ();
-
-#ifdef CONFIG_BLK_DEV_IDEDOUBLER
- if (!strcmp(s, "ide=doubler")) {
- extern int ide_doubler;
-
- printk(" : Enabled support for IDE doublers\n");
- ide_doubler = 1;
- goto obsolete_option;
- }
-#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
-
- if (!strcmp(s, "ide=nodma")) {
- printk(" : Prevented DMA\n");
- noautodma = 1;
- goto obsolete_option;
- }
-
-#ifdef CONFIG_BLK_DEV_IDEACPI
- if (!strcmp(s, "ide=noacpi")) {
- //printk(" : Disable IDE ACPI support.\n");
- ide_noacpi = 1;
- goto obsolete_option;
- }
- if (!strcmp(s, "ide=acpigtf")) {
- //printk(" : Enable IDE ACPI _GTF support.\n");
- ide_acpigtf = 1;
- goto obsolete_option;
- }
- if (!strcmp(s, "ide=acpionboot")) {
- //printk(" : Call IDE ACPI methods on boot.\n");
- ide_acpionboot = 1;
- goto obsolete_option;
- }
-#endif /* CONFIG_BLK_DEV_IDEACPI */
-
- /*
- * Look for drive options: "hdx="
- */
- if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) {
- const char *hd_words[] = {
- "none", "noprobe", "nowerr", "cdrom", "nodma",
- "-6", "-7", "-8", "-9", "-10",
- "noflush", "remap", "remap63", "scsi", NULL };
- unit = s[2] - 'a';
- hw = unit / MAX_DRIVES;
- unit = unit % MAX_DRIVES;
- hwif = &ide_hwifs[hw];
- drive = &hwif->drives[unit];
- if (strncmp(s + 4, "ide-", 4) == 0) {
- strlcpy(drive->driver_req, s + 4, sizeof(drive->driver_req));
- goto obsolete_option;
- }
- switch (match_parm(&s[3], hd_words, vals, 3)) {
- case -1: /* "none" */
- case -2: /* "noprobe" */
- drive->noprobe = 1;
- goto obsolete_option;
- case -3: /* "nowerr" */
- drive->bad_wstat = BAD_R_STAT;
- goto obsolete_option;
- case -4: /* "cdrom" */
- drive->present = 1;
- drive->media = ide_cdrom;
- /* an ATAPI device ignores DRDY */
- drive->ready_stat = 0;
- goto obsolete_option;
- case -5: /* nodma */
- drive->nodma = 1;
- goto obsolete_option;
- case -11: /* noflush */
- drive->noflush = 1;
- goto obsolete_option;
- case -12: /* "remap" */
- drive->remap_0_to_1 = 1;
- goto obsolete_option;
- case -13: /* "remap63" */
- drive->sect0 = 63;
- goto obsolete_option;
- case -14: /* "scsi" */
- drive->scsi = 1;
- goto obsolete_option;
- case 3: /* cyl,head,sect */
- drive->media = ide_disk;
- drive->ready_stat = READY_STAT;
- drive->cyl = drive->bios_cyl = vals[0];
- drive->head = drive->bios_head = vals[1];
- drive->sect = drive->bios_sect = vals[2];
- drive->present = 1;
- drive->forced_geom = 1;
- goto obsolete_option;
- default:
- goto bad_option;
- }
- }
-
- if (s[0] != 'i' || s[1] != 'd' || s[2] != 'e')
- goto bad_option;
- /*
- * Look for bus speed option: "idebus="
- */
- if (s[3] == 'b' && s[4] == 'u' && s[5] == 's') {
- if (match_parm(&s[6], NULL, vals, 1) != 1)
- goto bad_option;
- if (vals[0] >= 20 && vals[0] <= 66) {
- idebus_parameter = vals[0];
- } else
- printk(" -- BAD BUS SPEED! Expected value from 20 to 66");
- goto obsolete_option;
- }
-
-bad_option:
- printk(" -- BAD OPTION\n");
- return 1;
-obsolete_option:
- printk(" -- OBSOLETE OPTION, WILL BE REMOVED SOON!\n");
- return 1;
-}
-
-EXPORT_SYMBOL(ide_lock);
-
static int ide_bus_match(struct device *dev, struct device_driver *drv)
{
return 1;
@@ -1288,11 +988,6 @@ static int __init ide_init(void)
int ret;
printk(KERN_INFO "Uniform Multi-Platform E-IDE driver\n");
- system_bus_speed = ide_system_bus_speed();
-
- printk(KERN_INFO "ide: Assuming %dMHz system bus speed "
- "for PIO modes%s\n", system_bus_speed,
- idebus_parameter ? "" : "; override with idebus=xx");
ret = bus_register(&ide_bus_type);
if (ret < 0) {
@@ -1307,8 +1002,6 @@ static int __init ide_init(void)
}
ide_port_class->dev_release = ide_port_class_release;
- init_ide_data();
-
proc_ide_create();
return 0;
@@ -1319,32 +1012,7 @@ out_port_class:
return ret;
}
-#ifdef MODULE
-static char *options = NULL;
-module_param(options, charp, 0);
-MODULE_LICENSE("GPL");
-
-static void __init parse_options (char *line)
-{
- char *next = line;
-
- if (line == NULL || !*line)
- return;
- while ((line = next) != NULL) {
- if ((next = strchr(line,' ')) != NULL)
- *next++ = 0;
- if (!ide_setup(line))
- printk (KERN_INFO "Unknown option '%s'\n", line);
- }
-}
-
-int __init init_module (void)
-{
- parse_options(options);
- return ide_init();
-}
-
-void __exit cleanup_module (void)
+static void __exit ide_exit(void)
{
proc_ide_destroy();
@@ -1353,10 +1021,7 @@ void __exit cleanup_module (void)
bus_unregister(&ide_bus_type);
}
-#else /* !MODULE */
-
-__setup("", ide_setup);
-
module_init(ide_init);
+module_exit(ide_exit);
-#endif /* MODULE */
+MODULE_LICENSE("GPL");
diff --git a/drivers/ide/legacy/ali14xx.c b/drivers/ide/legacy/ali14xx.c
index 90c65cf97448..4ec19737f3c5 100644
--- a/drivers/ide/legacy/ali14xx.c
+++ b/drivers/ide/legacy/ali14xx.c
@@ -116,11 +116,12 @@ static void ali14xx_set_pio_mode(ide_drive_t *drive, const u8 pio)
int time1, time2;
u8 param1, param2, param3, param4;
unsigned long flags;
- int bus_speed = ide_vlb_clk ? ide_vlb_clk : system_bus_clock();
+ int bus_speed = ide_vlb_clk ? ide_vlb_clk : 50;
+ struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
/* calculate timing, according to PIO mode */
time1 = ide_pio_cycle_time(drive, pio);
- time2 = ide_pio_timings[pio].active_time;
+ time2 = t->active;
param3 = param1 = (time2 * bus_speed + 999) / 1000;
param4 = param2 = (time1 * bus_speed + 999) / 1000 - param1;
if (pio < 3) {
diff --git a/drivers/ide/legacy/buddha.c b/drivers/ide/legacy/buddha.c
index 5c730e4dd735..0497e7f85b09 100644
--- a/drivers/ide/legacy/buddha.c
+++ b/drivers/ide/legacy/buddha.c
@@ -138,6 +138,8 @@ static void __init buddha_setup_ports(hw_regs_t *hw, unsigned long base,
hw->irq = IRQ_AMIGA_PORTS;
hw->ack_intr = ack_intr;
+
+ hw->chipset = ide_generic;
}
/*
@@ -225,7 +227,6 @@ fail_base2:
if (hwif) {
u8 index = hwif->index;
- ide_init_port_data(hwif, index);
ide_init_port_hw(hwif, &hw);
idx[i] = index;
diff --git a/drivers/ide/legacy/falconide.c b/drivers/ide/legacy/falconide.c
index 9e449a0c623f..129a812bb57f 100644
--- a/drivers/ide/legacy/falconide.c
+++ b/drivers/ide/legacy/falconide.c
@@ -81,6 +81,8 @@ static void __init falconide_setup_ports(hw_regs_t *hw)
hw->irq = IRQ_MFP_IDE;
hw->ack_intr = NULL;
+
+ hw->chipset = ide_generic;
}
/*
@@ -109,7 +111,6 @@ static int __init falconide_init(void)
u8 index = hwif->index;
u8 idx[4] = { index, 0xff, 0xff, 0xff };
- ide_init_port_data(hwif, index);
ide_init_port_hw(hwif, &hw);
/* Atari has a byte-swapped IDE interface */
diff --git a/drivers/ide/legacy/gayle.c b/drivers/ide/legacy/gayle.c
index a9c2593a898c..7e74b20202df 100644
--- a/drivers/ide/legacy/gayle.c
+++ b/drivers/ide/legacy/gayle.c
@@ -16,6 +16,7 @@
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/zorro.h>
+#include <linux/module.h>
#include <asm/setup.h>
#include <asm/amigahw.h>
@@ -62,7 +63,8 @@
GAYLE_NUM_HWIFS-1)
#define GAYLE_HAS_CONTROL_REG (!ide_doubler)
#define GAYLE_IDEREG_SIZE (ide_doubler ? 0x1000 : 0x2000)
-int ide_doubler = 0; /* support IDE doublers? */
+
+static int ide_doubler;
module_param_named(doubler, ide_doubler, bool, 0);
MODULE_PARM_DESC(doubler, "enable support for IDE doublers");
#endif /* CONFIG_BLK_DEV_IDEDOUBLER */
@@ -112,6 +114,8 @@ static void __init gayle_setup_ports(hw_regs_t *hw, unsigned long base,
hw->irq = IRQ_AMIGA_PORTS;
hw->ack_intr = ack_intr;
+
+ hw->chipset = ide_generic;
}
/*
@@ -181,7 +185,6 @@ found:
if (hwif) {
u8 index = hwif->index;
- ide_init_port_data(hwif, index);
ide_init_port_hw(hwif, &hw);
idx[i] = index;
diff --git a/drivers/ide/legacy/ht6560b.c b/drivers/ide/legacy/ht6560b.c
index 4fe516df9f74..7bc8fd59ea9e 100644
--- a/drivers/ide/legacy/ht6560b.c
+++ b/drivers/ide/legacy/ht6560b.c
@@ -212,10 +212,11 @@ static u8 ht_pio2timings(ide_drive_t *drive, const u8 pio)
{
int active_time, recovery_time;
int active_cycles, recovery_cycles;
- int bus_speed = ide_vlb_clk ? ide_vlb_clk : system_bus_clock();
+ int bus_speed = ide_vlb_clk ? ide_vlb_clk : 50;
if (pio) {
unsigned int cycle_time;
+ struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
cycle_time = ide_pio_cycle_time(drive, pio);
@@ -224,10 +225,8 @@ static u8 ht_pio2timings(ide_drive_t *drive, const u8 pio)
* actual cycle time for recovery and activity
* according system bus speed.
*/
- active_time = ide_pio_timings[pio].active_time;
- recovery_time = cycle_time
- - active_time
- - ide_pio_timings[pio].setup_time;
+ active_time = t->active;
+ recovery_time = cycle_time - active_time - t->setup;
/*
* Cycle times should be Vesa bus cycles
*/
@@ -311,16 +310,16 @@ static void ht6560b_set_pio_mode(ide_drive_t *drive, const u8 pio)
#endif
}
-static void __init ht6560b_port_init_devs(ide_hwif_t *hwif)
+static void __init ht6560b_init_dev(ide_drive_t *drive)
{
+ ide_hwif_t *hwif = drive->hwif;
/* Setting default configurations for drives. */
int t = (HT_CONFIG_DEFAULT << 8) | HT_TIMING_DEFAULT;
if (hwif->channel)
t |= (HT_SECONDARY_IF << 8);
- hwif->drives[0].drive_data = t;
- hwif->drives[1].drive_data = t;
+ drive->drive_data = t;
}
static int probe_ht6560b;
@@ -329,7 +328,7 @@ module_param_named(probe, probe_ht6560b, bool, 0);
MODULE_PARM_DESC(probe, "probe for HT6560B chipset");
static const struct ide_port_ops ht6560b_port_ops = {
- .port_init_devs = ht6560b_port_init_devs,
+ .init_dev = ht6560b_init_dev,
.set_pio_mode = ht6560b_set_pio_mode,
.selectproc = ht6560b_selectproc,
};
diff --git a/drivers/ide/legacy/ide-4drives.c b/drivers/ide/legacy/ide-4drives.c
index ecae916a3385..89c8ff0a4d08 100644
--- a/drivers/ide/legacy/ide-4drives.c
+++ b/drivers/ide/legacy/ide-4drives.c
@@ -11,6 +11,21 @@ static int probe_4drives;
module_param_named(probe, probe_4drives, bool, 0);
MODULE_PARM_DESC(probe, "probe for generic IDE chipset with 4 drives/port");
+static void ide_4drives_init_dev(ide_drive_t *drive)
+{
+ if (drive->hwif->channel)
+ drive->select.all ^= 0x20;
+}
+
+static const struct ide_port_ops ide_4drives_port_ops = {
+ .init_dev = ide_4drives_init_dev,
+};
+
+static const struct ide_port_info ide_4drives_port_info = {
+ .port_ops = &ide_4drives_port_ops,
+ .host_flags = IDE_HFLAG_SERIALIZE | IDE_HFLAG_NO_DMA,
+};
+
static int __init ide_4drives_init(void)
{
ide_hwif_t *hwif, *mate;
@@ -49,18 +64,10 @@ static int __init ide_4drives_init(void)
mate = ide_find_port();
if (mate) {
ide_init_port_hw(mate, &hw);
- mate->drives[0].select.all ^= 0x20;
- mate->drives[1].select.all ^= 0x20;
idx[1] = mate->index;
-
- if (hwif) {
- hwif->mate = mate;
- mate->mate = hwif;
- hwif->serialized = mate->serialized = 1;
- }
}
- ide_device_add(idx, NULL);
+ ide_device_add(idx, &ide_4drives_port_info);
return 0;
}
diff --git a/drivers/ide/legacy/ide-cs.c b/drivers/ide/legacy/ide-cs.c
index aa2ea3deac85..a0d8606b88c5 100644
--- a/drivers/ide/legacy/ide-cs.c
+++ b/drivers/ide/legacy/ide-cs.c
@@ -150,6 +150,11 @@ static const struct ide_port_ops idecs_port_ops = {
.quirkproc = ide_undecoded_slave,
};
+static const struct ide_port_info idecs_port_info = {
+ .port_ops = &idecs_port_ops,
+ .host_flags = IDE_HFLAG_NO_DMA,
+};
+
static ide_hwif_t *idecs_register(unsigned long io, unsigned long ctl,
unsigned long irq, struct pcmcia_device *handle)
{
@@ -181,19 +186,25 @@ static ide_hwif_t *idecs_register(unsigned long io, unsigned long ctl,
if (hwif == NULL)
goto out_release;
- i = hwif->index;
-
- ide_init_port_data(hwif, i);
ide_init_port_hw(hwif, &hw);
- hwif->port_ops = &idecs_port_ops;
- idx[0] = i;
+ idx[0] = hwif->index;
- ide_device_add(idx, NULL);
+ ide_device_add(idx, &idecs_port_info);
if (hwif->present)
return hwif;
+ /* retry registration in case device is still spinning up */
+ for (i = 0; i < 10; i++) {
+ msleep(100);
+ ide_port_scan(hwif);
+ if (hwif->present)
+ return hwif;
+ }
+
+ return hwif;
+
out_release:
release_region(ctl, 1);
release_region(io, 8);
@@ -222,7 +233,7 @@ static int ide_config(struct pcmcia_device *link)
cistpl_cftable_entry_t dflt;
} *stk = NULL;
cistpl_cftable_entry_t *cfg;
- int i, pass, last_ret = 0, last_fn = 0, is_kme = 0;
+ int pass, last_ret = 0, last_fn = 0, is_kme = 0;
unsigned long io_base, ctl_base;
ide_hwif_t *hwif;
@@ -319,30 +330,15 @@ static int ide_config(struct pcmcia_device *link)
if (is_kme)
outb(0x81, ctl_base+1);
- /* retry registration in case device is still spinning up */
- for (i = 0; i < 10; i++) {
- hwif = idecs_register(io_base, ctl_base, link->irq.AssignedIRQ, link);
- if (hwif)
- break;
- if (link->io.NumPorts1 == 0x20) {
+ hwif = idecs_register(io_base, ctl_base, link->irq.AssignedIRQ, link);
+ if (hwif == NULL && link->io.NumPorts1 == 0x20) {
outb(0x02, ctl_base + 0x10);
hwif = idecs_register(io_base + 0x10, ctl_base + 0x10,
link->irq.AssignedIRQ, link);
- if (hwif) {
- io_base += 0x10;
- ctl_base += 0x10;
- break;
- }
- }
- msleep(100);
}
- if (hwif == NULL) {
- printk(KERN_NOTICE "ide-cs: ide_register() at 0x%3lx & 0x%3lx"
- ", irq %u failed\n", io_base, ctl_base,
- link->irq.AssignedIRQ);
+ if (hwif == NULL)
goto failed;
- }
info->ndev = 1;
sprintf(info->node.dev_name, "hd%c", 'a' + hwif->index * 2);
diff --git a/drivers/ide/legacy/ide_platform.c b/drivers/ide/legacy/ide_platform.c
index d3bc3f24e05d..a249562b34b5 100644
--- a/drivers/ide/legacy/ide_platform.c
+++ b/drivers/ide/legacy/ide_platform.c
@@ -44,6 +44,10 @@ static void __devinit plat_ide_setup_ports(hw_regs_t *hw,
hw->chipset = ide_generic;
}
+static const struct ide_port_info platform_ide_port_info = {
+ .host_flags = IDE_HFLAG_NO_DMA,
+};
+
static int __devinit plat_ide_probe(struct platform_device *pdev)
{
struct resource *res_base, *res_alt, *res_irq;
@@ -54,6 +58,7 @@ static int __devinit plat_ide_probe(struct platform_device *pdev)
int ret = 0;
int mmio = 0;
hw_regs_t hw;
+ struct ide_port_info d = platform_ide_port_info;
pdata = pdev->dev.platform_data;
@@ -102,13 +107,13 @@ static int __devinit plat_ide_probe(struct platform_device *pdev)
ide_init_port_hw(hwif, &hw);
if (mmio) {
- hwif->host_flags = IDE_HFLAG_MMIO;
+ d.host_flags |= IDE_HFLAG_MMIO;
default_hwif_mmiops(hwif);
}
idx[0] = hwif->index;
- ide_device_add(idx, NULL);
+ ide_device_add(idx, &d);
platform_set_drvdata(pdev, hwif);
diff --git a/drivers/ide/legacy/macide.c b/drivers/ide/legacy/macide.c
index caa2632dd08e..0a6195bcfeda 100644
--- a/drivers/ide/legacy/macide.c
+++ b/drivers/ide/legacy/macide.c
@@ -78,6 +78,8 @@ static void __init macide_setup_ports(hw_regs_t *hw, unsigned long base,
hw->irq = irq;
hw->ack_intr = ack_intr;
+
+ hw->chipset = ide_generic;
}
static const char *mac_ide_name[] =
@@ -128,7 +130,6 @@ static int __init macide_init(void)
u8 index = hwif->index;
u8 idx[4] = { index, 0xff, 0xff, 0xff };
- ide_init_port_data(hwif, index);
ide_init_port_hw(hwif, &hw);
ide_device_add(idx, NULL);
diff --git a/drivers/ide/legacy/q40ide.c b/drivers/ide/legacy/q40ide.c
index 6f535d00e638..9c2b9d078f69 100644
--- a/drivers/ide/legacy/q40ide.c
+++ b/drivers/ide/legacy/q40ide.c
@@ -70,6 +70,8 @@ static void q40_ide_setup_ports(hw_regs_t *hw, unsigned long base,
hw->irq = irq;
hw->ack_intr = ack_intr;
+
+ hw->chipset = ide_generic;
}
static void q40ide_input_data(ide_drive_t *drive, struct request *rq,
@@ -140,7 +142,6 @@ static int __init q40ide_init(void)
hwif = ide_find_port();
if (hwif) {
- ide_init_port_data(hwif, hwif->index);
ide_init_port_hw(hwif, &hw);
/* Q40 has a byte-swapped IDE interface */
diff --git a/drivers/ide/legacy/qd65xx.c b/drivers/ide/legacy/qd65xx.c
index 6424af154325..2338f344ea24 100644
--- a/drivers/ide/legacy/qd65xx.c
+++ b/drivers/ide/legacy/qd65xx.c
@@ -110,7 +110,7 @@ static void qd65xx_select(ide_drive_t *drive)
static u8 qd6500_compute_timing (ide_hwif_t *hwif, int active_time, int recovery_time)
{
- int clk = ide_vlb_clk ? ide_vlb_clk : system_bus_clock();
+ int clk = ide_vlb_clk ? ide_vlb_clk : 50;
u8 act_cyc, rec_cyc;
if (clk <= 33) {
@@ -132,7 +132,7 @@ static u8 qd6500_compute_timing (ide_hwif_t *hwif, int active_time, int recovery
static u8 qd6580_compute_timing (int active_time, int recovery_time)
{
- int clk = ide_vlb_clk ? ide_vlb_clk : system_bus_clock();
+ int clk = ide_vlb_clk ? ide_vlb_clk : 50;
u8 act_cyc, rec_cyc;
act_cyc = 17 - IDE_IN(active_time * clk / 1000 + 1, 2, 17);
@@ -207,6 +207,7 @@ static void qd6500_set_pio_mode(ide_drive_t *drive, const u8 pio)
static void qd6580_set_pio_mode(ide_drive_t *drive, const u8 pio)
{
ide_hwif_t *hwif = drive->hwif;
+ struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
unsigned int cycle_time;
int active_time = 175;
int recovery_time = 415; /* worst case values from the dos driver */
@@ -236,7 +237,7 @@ static void qd6580_set_pio_mode(ide_drive_t *drive, const u8 pio)
active_time = 110;
recovery_time = cycle_time - 120;
} else {
- active_time = ide_pio_timings[pio].active_time;
+ active_time = t->active;
recovery_time = cycle_time - active_time;
}
}
@@ -281,17 +282,18 @@ static int __init qd_testreg(int port)
return (readreg != QD_TESTVAL);
}
-static void __init qd6500_port_init_devs(ide_hwif_t *hwif)
+static void __init qd6500_init_dev(ide_drive_t *drive)
{
+ ide_hwif_t *hwif = drive->hwif;
u8 base = (hwif->config_data & 0xff00) >> 8;
u8 config = QD_CONFIG(hwif);
- hwif->drives[0].drive_data = QD6500_DEF_DATA;
- hwif->drives[1].drive_data = QD6500_DEF_DATA;
+ drive->drive_data = QD6500_DEF_DATA;
}
-static void __init qd6580_port_init_devs(ide_hwif_t *hwif)
+static void __init qd6580_init_dev(ide_drive_t *drive)
{
+ ide_hwif_t *hwif = drive->hwif;
u16 t1, t2;
u8 base = (hwif->config_data & 0xff00) >> 8;
u8 config = QD_CONFIG(hwif);
@@ -302,18 +304,17 @@ static void __init qd6580_port_init_devs(ide_hwif_t *hwif)
} else
t2 = t1 = hwif->channel ? QD6580_DEF_DATA2 : QD6580_DEF_DATA;
- hwif->drives[0].drive_data = t1;
- hwif->drives[1].drive_data = t2;
+ drive->drive_data = drive->select.b.unit ? t2 : t1;
}
static const struct ide_port_ops qd6500_port_ops = {
- .port_init_devs = qd6500_port_init_devs,
+ .init_dev = qd6500_init_dev,
.set_pio_mode = qd6500_set_pio_mode,
.selectproc = qd65xx_select,
};
static const struct ide_port_ops qd6580_port_ops = {
- .port_init_devs = qd6580_port_init_devs,
+ .init_dev = qd6580_init_dev,
.set_pio_mode = qd6580_set_pio_mode,
.selectproc = qd65xx_select,
};
diff --git a/drivers/ide/mips/au1xxx-ide.c b/drivers/ide/mips/au1xxx-ide.c
index 1a6c27b32498..48d57cae63c6 100644
--- a/drivers/ide/mips/au1xxx-ide.c
+++ b/drivers/ide/mips/au1xxx-ide.c
@@ -213,10 +213,8 @@ static int auide_build_dmatable(ide_drive_t *drive)
{
int i, iswrite, count = 0;
ide_hwif_t *hwif = HWIF(drive);
-
struct request *rq = HWGROUP(drive)->rq;
-
- _auide_hwif *ahwif = (_auide_hwif*)hwif->hwif_data;
+ _auide_hwif *ahwif = &auide_hwif;
struct scatterlist *sg;
iswrite = (rq_data_dir(rq) == WRITE);
@@ -402,7 +400,7 @@ static const struct ide_dma_ops au1xxx_dma_ops = {
static int auide_ddma_init(ide_hwif_t *hwif, const struct ide_port_info *d)
{
- _auide_hwif *auide = (_auide_hwif *)hwif->hwif_data;
+ _auide_hwif *auide = &auide_hwif;
dbdev_tab_t source_dev_tab, target_dev_tab;
u32 dev_id, tsize, devwidth, flags;
@@ -463,7 +461,7 @@ static int auide_ddma_init(ide_hwif_t *hwif, const struct ide_port_info *d)
#else
static int auide_ddma_init(ide_hwif_t *hwif, const struct ide_port_info *d)
{
- _auide_hwif *auide = (_auide_hwif *)hwif->hwif_data;
+ _auide_hwif *auide = &auide_hwif;
dbdev_tab_t source_dev_tab;
int flags;
@@ -600,8 +598,6 @@ static int au_ide_probe(struct device *dev)
ide_init_port_hw(hwif, &hw);
- hwif->dev = dev;
-
/* If the user has selected DDMA assisted copies,
then set up a few local I/O function entry points
*/
@@ -610,11 +606,8 @@ static int au_ide_probe(struct device *dev)
hwif->input_data = au1xxx_input_data;
hwif->output_data = au1xxx_output_data;
#endif
- hwif->select_data = 0; /* no chipset-specific code */
- hwif->config_data = 0; /* no chipset-specific code */
auide_hwif.hwif = hwif;
- hwif->hwif_data = &auide_hwif;
idx[0] = hwif->index;
diff --git a/drivers/ide/mips/swarm.c b/drivers/ide/mips/swarm.c
index 52fee3d2771a..9f1212cc4aed 100644
--- a/drivers/ide/mips/swarm.c
+++ b/drivers/ide/mips/swarm.c
@@ -61,6 +61,11 @@ static struct resource swarm_ide_resource = {
static struct platform_device *swarm_ide_dev;
+static const struct ide_port_info swarm_port_info = {
+ .name = DRV_NAME,
+ .host_flags = IDE_HFLAG_MMIO | IDE_HFLAG_NO_DMA,
+};
+
/*
* swarm_ide_probe - if the board header indicates the existence of
* Generic Bus IDE, allocate a HWIF for it.
@@ -77,12 +82,6 @@ static int __devinit swarm_ide_probe(struct device *dev)
if (!SIBYTE_HAVE_IDE)
return -ENODEV;
- hwif = ide_find_port();
- if (hwif == NULL) {
- printk(KERN_ERR DRV_NAME ": no free slot for interface\n");
- return -ENOMEM;
- }
-
base = ioremap(A_IO_EXT_BASE, 0x800);
offset = __raw_readq(base + R_IO_EXT_REG(R_IO_EXT_START_ADDR, IDE_CS));
size = __raw_readq(base + R_IO_EXT_REG(R_IO_EXT_MULT_SIZE, IDE_CS));
@@ -109,10 +108,6 @@ static int __devinit swarm_ide_probe(struct device *dev)
base = ioremap(offset, size);
- /* Setup MMIO ops. */
- hwif->host_flags = IDE_HFLAG_MMIO;
- default_hwif_mmiops(hwif);
-
for (i = 0; i <= 7; i++)
hw.io_ports_array[i] =
(unsigned long)(base + ((0x1f0 + i) << 5));
@@ -121,15 +116,26 @@ static int __devinit swarm_ide_probe(struct device *dev)
hw.irq = K_INT_GB_IDE;
hw.chipset = ide_generic;
+ hwif = ide_find_port_slot(&swarm_port_info);
+ if (hwif == NULL)
+ goto err;
+
ide_init_port_hw(hwif, &hw);
+ /* Setup MMIO ops. */
+ default_hwif_mmiops(hwif);
+
idx[0] = hwif->index;
- ide_device_add(idx, NULL);
+ ide_device_add(idx, &swarm_port_info);
dev_set_drvdata(dev, hwif);
return 0;
+err:
+ release_resource(&swarm_ide_resource);
+ iounmap(base);
+ return -ENOMEM;
}
static struct device_driver swarm_ide_driver = {
diff --git a/drivers/ide/pci/aec62xx.c b/drivers/ide/pci/aec62xx.c
index 7f46c224b7c4..ae7a4329a581 100644
--- a/drivers/ide/pci/aec62xx.c
+++ b/drivers/ide/pci/aec62xx.c
@@ -140,7 +140,7 @@ static void aec_set_pio_mode(ide_drive_t *drive, const u8 pio)
static unsigned int __devinit init_chipset_aec62xx(struct pci_dev *dev, const char *name)
{
- int bus_speed = ide_pci_clk ? ide_pci_clk : system_bus_clock();
+ int bus_speed = ide_pci_clk ? ide_pci_clk : 33;
if (bus_speed <= 33)
pci_set_drvdata(dev, (void *) aec6xxx_33_base);
diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c
index f2129d5e07f2..80d19c0eb780 100644
--- a/drivers/ide/pci/alim15x3.c
+++ b/drivers/ide/pci/alim15x3.c
@@ -69,22 +69,20 @@ static void ali_set_pio_mode(ide_drive_t *drive, const u8 pio)
{
ide_hwif_t *hwif = HWIF(drive);
struct pci_dev *dev = to_pci_dev(hwif->dev);
- int s_time, a_time, c_time;
+ struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
+ int s_time = t->setup, a_time = t->active, c_time = t->cycle;
u8 s_clc, a_clc, r_clc;
unsigned long flags;
- int bus_speed = ide_pci_clk ? ide_pci_clk : system_bus_clock();
+ int bus_speed = ide_pci_clk ? ide_pci_clk : 33;
int port = hwif->channel ? 0x5c : 0x58;
int portFIFO = hwif->channel ? 0x55 : 0x54;
u8 cd_dma_fifo = 0;
int unit = drive->select.b.unit & 1;
- s_time = ide_pio_timings[pio].setup_time;
- a_time = ide_pio_timings[pio].active_time;
if ((s_clc = (s_time * bus_speed + 999) / 1000) >= 8)
s_clc = 0;
if ((a_clc = (a_time * bus_speed + 999) / 1000) >= 8)
a_clc = 0;
- c_time = ide_pio_timings[pio].cycle_time;
if (!(r_clc = (c_time * bus_speed + 999) / 1000 - a_clc - s_clc)) {
r_clc = 1;
diff --git a/drivers/ide/pci/amd74xx.c b/drivers/ide/pci/amd74xx.c
index efcf54338be7..0bfcdd0e77b3 100644
--- a/drivers/ide/pci/amd74xx.c
+++ b/drivers/ide/pci/amd74xx.c
@@ -21,8 +21,6 @@
#include <linux/init.h>
#include <linux/ide.h>
-#include "ide-timing.h"
-
enum {
AMD_IDE_CONFIG = 0x41,
AMD_CABLE_DETECT = 0x42,
@@ -53,20 +51,20 @@ static void amd_set_speed(struct pci_dev *dev, u8 dn, u8 udma_mask,
u8 t = 0, offset = amd_offset(dev);
pci_read_config_byte(dev, AMD_ADDRESS_SETUP + offset, &t);
- t = (t & ~(3 << ((3 - dn) << 1))) | ((FIT(timing->setup, 1, 4) - 1) << ((3 - dn) << 1));
+ t = (t & ~(3 << ((3 - dn) << 1))) | ((clamp_val(timing->setup, 1, 4) - 1) << ((3 - dn) << 1));
pci_write_config_byte(dev, AMD_ADDRESS_SETUP + offset, t);
pci_write_config_byte(dev, AMD_8BIT_TIMING + offset + (1 - (dn >> 1)),
- ((FIT(timing->act8b, 1, 16) - 1) << 4) | (FIT(timing->rec8b, 1, 16) - 1));
+ ((clamp_val(timing->act8b, 1, 16) - 1) << 4) | (clamp_val(timing->rec8b, 1, 16) - 1));
pci_write_config_byte(dev, AMD_DRIVE_TIMING + offset + (3 - dn),
- ((FIT(timing->active, 1, 16) - 1) << 4) | (FIT(timing->recover, 1, 16) - 1));
+ ((clamp_val(timing->active, 1, 16) - 1) << 4) | (clamp_val(timing->recover, 1, 16) - 1));
switch (udma_mask) {
- case ATA_UDMA2: t = timing->udma ? (0xc0 | (FIT(timing->udma, 2, 5) - 2)) : 0x03; break;
- case ATA_UDMA4: t = timing->udma ? (0xc0 | amd_cyc2udma[FIT(timing->udma, 2, 10)]) : 0x03; break;
- case ATA_UDMA5: t = timing->udma ? (0xc0 | amd_cyc2udma[FIT(timing->udma, 1, 10)]) : 0x03; break;
- case ATA_UDMA6: t = timing->udma ? (0xc0 | amd_cyc2udma[FIT(timing->udma, 1, 15)]) : 0x03; break;
+ case ATA_UDMA2: t = timing->udma ? (0xc0 | (clamp_val(timing->udma, 2, 5) - 2)) : 0x03; break;
+ case ATA_UDMA4: t = timing->udma ? (0xc0 | amd_cyc2udma[clamp_val(timing->udma, 2, 10)]) : 0x03; break;
+ case ATA_UDMA5: t = timing->udma ? (0xc0 | amd_cyc2udma[clamp_val(timing->udma, 1, 10)]) : 0x03; break;
+ case ATA_UDMA6: t = timing->udma ? (0xc0 | amd_cyc2udma[clamp_val(timing->udma, 1, 15)]) : 0x03; break;
default: return;
}
@@ -179,7 +177,7 @@ static unsigned int __devinit init_chipset_amd74xx(struct pci_dev *dev,
* Determine the system bus clock.
*/
- amd_clock = (ide_pci_clk ? ide_pci_clk : system_bus_clock()) * 1000;
+ amd_clock = (ide_pci_clk ? ide_pci_clk : 33) * 1000;
switch (amd_clock) {
case 33000: amd_clock = 33333; break;
diff --git a/drivers/ide/pci/cmd640.c b/drivers/ide/pci/cmd640.c
index aaf38109eaec..1ad1e23e3105 100644
--- a/drivers/ide/pci/cmd640.c
+++ b/drivers/ide/pci/cmd640.c
@@ -521,21 +521,23 @@ static void program_drive_counts(ide_drive_t *drive, unsigned int index)
static void cmd640_set_mode(ide_drive_t *drive, unsigned int index,
u8 pio_mode, unsigned int cycle_time)
{
+ struct ide_timing *t;
int setup_time, active_time, recovery_time, clock_time;
u8 setup_count, active_count, recovery_count, recovery_count2, cycle_count;
int bus_speed;
- if (cmd640_vlb && ide_vlb_clk)
- bus_speed = ide_vlb_clk;
- else if (!cmd640_vlb && ide_pci_clk)
- bus_speed = ide_pci_clk;
+ if (cmd640_vlb)
+ bus_speed = ide_vlb_clk ? ide_vlb_clk : 50;
else
- bus_speed = system_bus_clock();
+ bus_speed = ide_pci_clk ? ide_pci_clk : 33;
if (pio_mode > 5)
pio_mode = 5;
- setup_time = ide_pio_timings[pio_mode].setup_time;
- active_time = ide_pio_timings[pio_mode].active_time;
+
+ t = ide_timing_find_mode(XFER_PIO_0 + pio_mode);
+ setup_time = t->setup;
+ active_time = t->active;
+
recovery_time = cycle_time - (setup_time + active_time);
clock_time = 1000 / bus_speed;
cycle_count = DIV_ROUND_UP(cycle_time, clock_time);
@@ -609,11 +611,40 @@ static void cmd640_set_pio_mode(ide_drive_t *drive, const u8 pio)
display_clocks(index);
}
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+
+static void cmd640_init_dev(ide_drive_t *drive)
+{
+ unsigned int i = drive->hwif->channel * 2 + drive->select.b.unit;
+
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
+ /*
+ * Reset timing to the slowest speed and turn off prefetch.
+ * This way, the drive identify code has a better chance.
+ */
+ setup_counts[i] = 4; /* max possible */
+ active_counts[i] = 16; /* max possible */
+ recovery_counts[i] = 16; /* max possible */
+ program_drive_counts(drive, i);
+ set_prefetch_mode(drive, i, 0);
+ printk(KERN_INFO DRV_NAME ": drive%d timings/prefetch cleared\n", i);
+#else
+ /*
+ * Set the drive unmask flags to match the prefetch setting.
+ */
+ check_prefetch(drive, i);
+ printk(KERN_INFO DRV_NAME ": drive%d timings/prefetch(%s) preserved\n",
+ i, drive->no_io_32bit ? "off" : "on");
+#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
+}
+
static const struct ide_port_ops cmd640_port_ops = {
+ .init_dev = cmd640_init_dev,
+#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
.set_pio_mode = cmd640_set_pio_mode,
+#endif
};
-#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
static int pci_conf1(void)
{
@@ -656,10 +687,8 @@ static const struct ide_port_info cmd640_port_info __initdata = {
IDE_HFLAG_NO_DMA |
IDE_HFLAG_ABUSE_PREFETCH |
IDE_HFLAG_ABUSE_FAST_DEVSEL,
-#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
.port_ops = &cmd640_port_ops,
.pio_mask = ATA_PIO5,
-#endif
};
static int cmd640x_init_one(unsigned long base, unsigned long ctl)
@@ -685,12 +714,8 @@ static int cmd640x_init_one(unsigned long base, unsigned long ctl)
*/
static int __init cmd640x_init(void)
{
-#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
- int second_port_toggled = 0;
-#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
int second_port_cmd640 = 0, rc;
const char *bus_type, *port2;
- unsigned int index;
u8 b, cfr;
u8 idx[4] = { 0xff, 0xff, 0xff, 0xff };
hw_regs_t hw[2];
@@ -747,9 +772,11 @@ static int __init cmd640x_init(void)
ide_std_init_ports(&hw[0], 0x1f0, 0x3f6);
hw[0].irq = 14;
+ hw[0].chipset = ide_cmd640;
ide_std_init_ports(&hw[1], 0x170, 0x376);
hw[1].irq = 15;
+ hw[1].chipset = ide_cmd640;
printk(KERN_INFO "cmd640: buggy cmd640%c interface on %s, config=0x%02x"
"\n", 'a' + cmd640_chip_version - 1, bus_type, cfr);
@@ -774,88 +801,44 @@ static int __init cmd640x_init(void)
put_cmd640_reg(CMDTIM, 0);
put_cmd640_reg(BRST, 0x40);
- cmd_hwif1 = ide_find_port();
+ b = get_cmd640_reg(CNTRL);
/*
* Try to enable the secondary interface, if not already enabled
*/
- if (cmd_hwif1 &&
- cmd_hwif1->drives[0].noprobe && cmd_hwif1->drives[1].noprobe) {
- port2 = "not probed";
+ if (secondary_port_responding()) {
+ if ((b & CNTRL_ENA_2ND)) {
+ second_port_cmd640 = 1;
+ port2 = "okay";
+ } else if (cmd640_vlb) {
+ second_port_cmd640 = 1;
+ port2 = "alive";
+ } else
+ port2 = "not cmd640";
} else {
- b = get_cmd640_reg(CNTRL);
+ put_cmd640_reg(CNTRL, b ^ CNTRL_ENA_2ND); /* toggle the bit */
if (secondary_port_responding()) {
- if ((b & CNTRL_ENA_2ND)) {
- second_port_cmd640 = 1;
- port2 = "okay";
- } else if (cmd640_vlb) {
- second_port_cmd640 = 1;
- port2 = "alive";
- } else
- port2 = "not cmd640";
+ second_port_cmd640 = 1;
+ port2 = "enabled";
} else {
- put_cmd640_reg(CNTRL, b ^ CNTRL_ENA_2ND); /* toggle the bit */
- if (secondary_port_responding()) {
- second_port_cmd640 = 1;
-#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
- second_port_toggled = 1;
-#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
- port2 = "enabled";
- } else {
- put_cmd640_reg(CNTRL, b); /* restore original setting */
- port2 = "not responding";
- }
+ put_cmd640_reg(CNTRL, b); /* restore original setting */
+ port2 = "not responding";
}
}
/*
* Initialize data for secondary cmd640 port, if enabled
*/
- if (second_port_cmd640 && cmd_hwif1) {
- ide_init_port_hw(cmd_hwif1, &hw[1]);
- idx[1] = cmd_hwif1->index;
+ if (second_port_cmd640) {
+ cmd_hwif1 = ide_find_port();
+ if (cmd_hwif1) {
+ ide_init_port_hw(cmd_hwif1, &hw[1]);
+ idx[1] = cmd_hwif1->index;
+ }
}
printk(KERN_INFO "cmd640: %sserialized, secondary interface %s\n",
second_port_cmd640 ? "" : "not ", port2);
- /*
- * Establish initial timings/prefetch for all drives.
- * Do not unnecessarily disturb any prior BIOS setup of these.
- */
- for (index = 0; index < (2 + (second_port_cmd640 << 1)); index++) {
- ide_drive_t *drive;
-
- if (index > 1) {
- if (cmd_hwif1 == NULL)
- continue;
- drive = &cmd_hwif1->drives[index & 1];
- } else {
- if (cmd_hwif0 == NULL)
- continue;
- drive = &cmd_hwif0->drives[index & 1];
- }
-
-#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED
- /*
- * Reset timing to the slowest speed and turn off prefetch.
- * This way, the drive identify code has a better chance.
- */
- setup_counts [index] = 4; /* max possible */
- active_counts [index] = 16; /* max possible */
- recovery_counts [index] = 16; /* max possible */
- program_drive_counts(drive, index);
- set_prefetch_mode(drive, index, 0);
- printk("cmd640: drive%d timings/prefetch cleared\n", index);
-#else
- /*
- * Set the drive unmask flags to match the prefetch setting
- */
- check_prefetch(drive, index);
- printk("cmd640: drive%d timings/prefetch(%s) preserved\n",
- index, drive->no_io_32bit ? "off" : "on");
-#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */
- }
-
#ifdef CMD640_DUMP_REGS
cmd640_dump_regs();
#endif
diff --git a/drivers/ide/pci/cmd64x.c b/drivers/ide/pci/cmd64x.c
index 08674711d089..cfa784bacf48 100644
--- a/drivers/ide/pci/cmd64x.c
+++ b/drivers/ide/pci/cmd64x.c
@@ -69,7 +69,7 @@ static u8 quantize_timing(int timing, int quant)
static void program_cycle_times (ide_drive_t *drive, int cycle_time, int active_time)
{
struct pci_dev *dev = to_pci_dev(drive->hwif->dev);
- int clock_time = 1000 / (ide_pci_clk ? ide_pci_clk : system_bus_clock());
+ int clock_time = 1000 / (ide_pci_clk ? ide_pci_clk : 33);
u8 cycle_count, active_count, recovery_count, drwtim;
static const u8 recovery_values[] =
{15, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0};
@@ -116,6 +116,7 @@ static void cmd64x_tune_pio(ide_drive_t *drive, const u8 pio)
{
ide_hwif_t *hwif = HWIF(drive);
struct pci_dev *dev = to_pci_dev(hwif->dev);
+ struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
unsigned int cycle_time;
u8 setup_count, arttim = 0;
@@ -124,11 +125,10 @@ static void cmd64x_tune_pio(ide_drive_t *drive, const u8 pio)
cycle_time = ide_pio_cycle_time(drive, pio);
- program_cycle_times(drive, cycle_time,
- ide_pio_timings[pio].active_time);
+ program_cycle_times(drive, cycle_time, t->active);
- setup_count = quantize_timing(ide_pio_timings[pio].setup_time,
- 1000 / (ide_pci_clk ? ide_pci_clk : system_bus_clock()));
+ setup_count = quantize_timing(t->setup,
+ 1000 / (ide_pci_clk ? ide_pci_clk : 33));
/*
* The primary channel has individual address setup timing registers
diff --git a/drivers/ide/pci/cs5535.c b/drivers/ide/pci/cs5535.c
index 99fe91a191b8..dc97c48623f3 100644
--- a/drivers/ide/pci/cs5535.c
+++ b/drivers/ide/pci/cs5535.c
@@ -26,8 +26,6 @@
#include <linux/pci.h>
#include <linux/ide.h>
-#include "ide-timing.h"
-
#define MSR_ATAC_BASE 0x51300000
#define ATAC_GLD_MSR_CAP (MSR_ATAC_BASE+0)
#define ATAC_GLD_MSR_CONFIG (MSR_ATAC_BASE+0x01)
@@ -75,13 +73,11 @@ static unsigned int cs5535_udma_timings[5] =
*/
static void cs5535_set_speed(ide_drive_t *drive, const u8 speed)
{
-
u32 reg = 0, dummy;
int unit = drive->select.b.unit;
-
/* Set the PIO timings */
- if ((speed & XFER_MODE) == XFER_PIO) {
+ if (speed < XFER_SW_DMA_0) {
ide_drive_t *pair = ide_get_paired_drive(drive);
u8 cmd, pioa;
diff --git a/drivers/ide/pci/cy82c693.c b/drivers/ide/pci/cy82c693.c
index 77cc22c2ad45..e14ad5530fa4 100644
--- a/drivers/ide/pci/cy82c693.c
+++ b/drivers/ide/pci/cy82c693.c
@@ -133,23 +133,22 @@ static int calc_clk(int time, int bus_speed)
*/
static void compute_clocks(u8 pio, pio_clocks_t *p_pclk)
{
+ struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
int clk1, clk2;
- int bus_speed = ide_pci_clk ? ide_pci_clk : system_bus_clock();
+ int bus_speed = ide_pci_clk ? ide_pci_clk : 33;
/* we don't check against CY82C693's min and max speed,
* so you can play with the idebus=xx parameter
*/
/* let's calc the address setup time clocks */
- p_pclk->address_time = (u8)calc_clk(ide_pio_timings[pio].setup_time, bus_speed);
+ p_pclk->address_time = (u8)calc_clk(t->setup, bus_speed);
/* let's calc the active and recovery time clocks */
- clk1 = calc_clk(ide_pio_timings[pio].active_time, bus_speed);
+ clk1 = calc_clk(t->active, bus_speed);
/* calc recovery timing */
- clk2 = ide_pio_timings[pio].cycle_time -
- ide_pio_timings[pio].active_time -
- ide_pio_timings[pio].setup_time;
+ clk2 = t->cycle - t->active - t->setup;
clk2 = calc_clk(clk2, bus_speed);
diff --git a/drivers/ide/pci/delkin_cb.c b/drivers/ide/pci/delkin_cb.c
index b9e457996d0e..0106e2a2df77 100644
--- a/drivers/ide/pci/delkin_cb.c
+++ b/drivers/ide/pci/delkin_cb.c
@@ -47,13 +47,18 @@ static const struct ide_port_ops delkin_cb_port_ops = {
.quirkproc = ide_undecoded_slave,
};
+static const struct ide_port_info delkin_cb_port_info = {
+ .port_ops = &delkin_cb_port_ops,
+ .host_flags = IDE_HFLAG_IO_32BIT | IDE_HFLAG_UNMASK_IRQS |
+ IDE_HFLAG_NO_DMA,
+};
+
static int __devinit
delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id)
{
unsigned long base;
hw_regs_t hw;
ide_hwif_t *hwif = NULL;
- ide_drive_t *drive;
int i, rc;
u8 idx[4] = { 0xff, 0xff, 0xff, 0xff };
@@ -79,6 +84,7 @@ delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id)
memset(&hw, 0, sizeof(hw));
ide_std_init_ports(&hw, base + 0x10, base + 0x1e);
hw.irq = dev->irq;
+ hw.dev = &dev->dev;
hw.chipset = ide_pci; /* this enables IRQ sharing */
hwif = ide_find_port();
@@ -87,28 +93,17 @@ delkin_cb_probe (struct pci_dev *dev, const struct pci_device_id *id)
i = hwif->index;
- ide_init_port_data(hwif, i);
ide_init_port_hw(hwif, &hw);
- hwif->port_ops = &delkin_cb_port_ops;
idx[0] = i;
- ide_device_add(idx, NULL);
-
- if (!hwif->present)
- goto out_disable;
+ ide_device_add(idx, &delkin_cb_port_info);
pci_set_drvdata(dev, hwif);
- hwif->dev = &dev->dev;
- drive = &hwif->drives[0];
- if (drive->present) {
- drive->io_32bit = 1;
- drive->unmask = 1;
- }
+
return 0;
out_disable:
- printk(KERN_ERR "delkin_cb: no IDE devices found\n");
pci_release_regions(dev);
pci_disable_device(dev);
return -ENODEV;
@@ -139,14 +134,12 @@ static struct pci_driver driver = {
.remove = delkin_cb_remove,
};
-static int
-delkin_cb_init (void)
+static int __init delkin_cb_init(void)
{
return pci_register_driver(&driver);
}
-static void
-delkin_cb_exit (void)
+static void __exit delkin_cb_exit(void)
{
pci_unregister_driver(&driver);
}
diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c
index c929dadaaaff..397c6cbe953c 100644
--- a/drivers/ide/pci/hpt366.c
+++ b/drivers/ide/pci/hpt366.c
@@ -759,8 +759,7 @@ static void hpt3xx_maskproc(ide_drive_t *drive, int mask)
enable_irq (hwif->irq);
}
} else
- outb(mask ? (drive->ctl | 2) : (drive->ctl & ~2),
- hwif->io_ports.ctl_addr);
+ outb(ATA_DEVCTL_OBS | (mask ? 2 : 0), hwif->io_ports.ctl_addr);
}
/*
diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c
index fec4955f449b..e427080e36a5 100644
--- a/drivers/ide/pci/ns87415.c
+++ b/drivers/ide/pci/ns87415.c
@@ -76,7 +76,7 @@ static void superio_tf_read(ide_drive_t *drive, ide_task_t *task)
}
/* be sure we're looking at the low order bits */
- outb(drive->ctl & ~0x80, io_ports->ctl_addr);
+ outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr);
if (task->tf_flags & IDE_TFLAG_IN_NSECT)
tf->nsect = inb(io_ports->nsect_addr);
@@ -90,7 +90,7 @@ static void superio_tf_read(ide_drive_t *drive, ide_task_t *task)
tf->device = superio_ide_inb(io_ports->device_addr);
if (task->tf_flags & IDE_TFLAG_LBA48) {
- outb(drive->ctl | 0x80, io_ports->ctl_addr);
+ outb(ATA_DEVCTL_OBS | 0x80, io_ports->ctl_addr);
if (task->tf_flags & IDE_TFLAG_IN_HOB_FEATURE)
tf->hob_feature = inb(io_ports->feature_addr);
diff --git a/drivers/ide/pci/opti621.c b/drivers/ide/pci/opti621.c
index 6e99080497bf..ac2829b91f07 100644
--- a/drivers/ide/pci/opti621.c
+++ b/drivers/ide/pci/opti621.c
@@ -208,15 +208,15 @@ typedef struct pio_clocks_s {
static void compute_clocks(int pio, pio_clocks_t *clks)
{
if (pio != PIO_NOT_EXIST) {
- int adr_setup, data_pls;
- int bus_speed = ide_pci_clk ? ide_pci_clk : system_bus_clock();
+ struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
+ int adr_setup = t->setup, data_pls = t->active;
+ int bus_speed = ide_pci_clk ? ide_pci_clk : 33;
- adr_setup = ide_pio_timings[pio].setup_time;
- data_pls = ide_pio_timings[pio].active_time;
clks->address_time = cmpt_clk(adr_setup, bus_speed);
clks->data_time = cmpt_clk(data_pls, bus_speed);
- clks->recovery_time = cmpt_clk(ide_pio_timings[pio].cycle_time
- - adr_setup-data_pls, bus_speed);
+ clks->recovery_time = cmpt_clk(t->cycle - adr_setup - data_pls,
+ bus_speed);
+
if (clks->address_time < 1)
clks->address_time = 1;
if (clks->address_time > 4)
@@ -319,14 +319,13 @@ static void opti621_set_pio_mode(ide_drive_t *drive, const u8 pio)
spin_unlock_irqrestore(&opti621_lock, flags);
}
-static void __devinit opti621_port_init_devs(ide_hwif_t *hwif)
+static void __devinit opti621_init_dev(ide_drive_t *drive)
{
- hwif->drives[0].drive_data = PIO_DONT_KNOW;
- hwif->drives[1].drive_data = PIO_DONT_KNOW;
+ drive->drive_data = PIO_DONT_KNOW;
}
static const struct ide_port_ops opti621_port_ops = {
- .port_init_devs = opti621_port_init_devs,
+ .init_dev = opti621_init_dev,
.set_pio_mode = opti621_set_pio_mode,
};
diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c
index 910fb00deb71..25c32923bebc 100644
--- a/drivers/ide/pci/scc_pata.c
+++ b/drivers/ide/pci/scc_pata.c
@@ -148,11 +148,8 @@ static void scc_ide_outb(u8 addr, unsigned long port)
out_be32((void*)port, addr);
}
-static void
-scc_ide_outbsync(ide_drive_t * drive, u8 addr, unsigned long port)
+static void scc_ide_outbsync(ide_hwif_t *hwif, u8 addr, unsigned long port)
{
- ide_hwif_t *hwif = HWIF(drive);
-
out_be32((void*)port, addr);
eieio();
in_be32((void*)(hwif->dma_base + 0x01c));
@@ -561,12 +558,9 @@ static int scc_ide_setup_pci_device(struct pci_dev *dev,
u8 idx[4] = { 0xff, 0xff, 0xff, 0xff };
int i;
- hwif = ide_find_port();
- if (hwif == NULL) {
- printk(KERN_ERR "%s: too many IDE interfaces, "
- "no room in table\n", SCC_PATA_NAME);
+ hwif = ide_find_port_slot(d);
+ if (hwif == NULL)
return -ENOMEM;
- }
memset(&hw, 0, sizeof(hw));
for (i = 0; i <= 8; i++)
@@ -662,8 +656,6 @@ static void scc_tf_load(ide_drive_t *drive, ide_task_t *task)
if (task->tf_flags & IDE_TFLAG_FLAGGED)
HIHI = 0xFF;
- ide_set_irq(drive, 1);
-
if (task->tf_flags & IDE_TFLAG_OUT_DATA)
out_be32((void *)io_ports->data_addr,
(tf->hob_data << 8) | tf->data);
@@ -708,7 +700,7 @@ static void scc_tf_read(ide_drive_t *drive, ide_task_t *task)
}
/* be sure we're looking at the low order bits */
- scc_ide_outb(drive->ctl & ~0x80, io_ports->ctl_addr);
+ scc_ide_outb(ATA_DEVCTL_OBS & ~0x80, io_ports->ctl_addr);
if (task->tf_flags & IDE_TFLAG_IN_NSECT)
tf->nsect = scc_ide_inb(io_ports->nsect_addr);
@@ -722,7 +714,7 @@ static void scc_tf_read(ide_drive_t *drive, ide_task_t *task)
tf->device = scc_ide_inb(io_ports->device_addr);
if (task->tf_flags & IDE_TFLAG_LBA48) {
- scc_ide_outb(drive->ctl | 0x80, io_ports->ctl_addr);
+ scc_ide_outb(ATA_DEVCTL_OBS | 0x80, io_ports->ctl_addr);
if (task->tf_flags & IDE_TFLAG_IN_HOB_FEATURE)
tf->hob_feature = scc_ide_inb(io_ports->feature_addr);
@@ -795,7 +787,6 @@ static void __devinit init_mmio_iops_scc(ide_hwif_t *hwif)
hwif->dma_base = dma_base;
hwif->config_data = ports->ctl;
- hwif->mmio = 1;
}
/**
diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c
index 16a0bce17d69..c79ff5b41088 100644
--- a/drivers/ide/pci/sgiioc4.c
+++ b/drivers/ide/pci/sgiioc4.c
@@ -111,7 +111,7 @@ sgiioc4_init_hwif_ports(hw_regs_t * hw, unsigned long data_port,
static void
sgiioc4_maskproc(ide_drive_t * drive, int mask)
{
- writeb(mask ? (drive->ctl | 2) : (drive->ctl & ~2),
+ writeb(ATA_DEVCTL_OBS | (mask ? 2 : 0),
(void __iomem *)drive->hwif->io_ports.ctl_addr);
}
@@ -369,8 +369,7 @@ ide_dma_sgiioc4(ide_hwif_t *hwif, const struct ide_port_info *d)
hwif->sg_max_nents = IOC4_PRD_ENTRIES;
pad = pci_alloc_consistent(dev, IOC4_IDE_CACHELINE_SIZE,
- (dma_addr_t *) &(hwif->dma_status));
-
+ (dma_addr_t *)&hwif->extra_base);
if (pad) {
ide_set_hwifdata(hwif, pad);
return 0;
@@ -439,7 +438,7 @@ sgiioc4_configure_for_dma(int dma_direction, ide_drive_t * drive)
/* Address of the Ending DMA */
memset(ide_get_hwifdata(hwif), 0, IOC4_IDE_CACHELINE_SIZE);
- ending_dma_addr = cpu_to_le32(hwif->dma_status);
+ ending_dma_addr = cpu_to_le32(hwif->extra_base);
writel(ending_dma_addr, (void __iomem *)(dma_base + IOC4_DMA_END_ADDR * 4));
writel(dma_direction, (void __iomem *)ioc4_dma_addr);
@@ -569,6 +568,7 @@ static const struct ide_dma_ops sgiioc4_dma_ops = {
};
static const struct ide_port_info sgiioc4_port_info __devinitdata = {
+ .name = DRV_NAME,
.chipset = ide_pci,
.init_dma = ide_dma_sgiioc4,
.port_ops = &sgiioc4_port_ops,
@@ -588,13 +588,6 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev)
hw_regs_t hw;
struct ide_port_info d = sgiioc4_port_info;
- hwif = ide_find_port();
- if (hwif == NULL) {
- printk(KERN_ERR "%s: too many IDE interfaces, no room in table\n",
- DRV_NAME);
- return -ENOMEM;
- }
-
/* Get the CmdBlk and CtrlBlk Base Registers */
bar0 = pci_resource_start(dev, 0);
virt_base = ioremap(bar0, pci_resource_len(dev, 0));
@@ -609,11 +602,11 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev)
cmd_phys_base = bar0 + IOC4_CMD_OFFSET;
if (!request_mem_region(cmd_phys_base, IOC4_CMD_CTL_BLK_SIZE,
- hwif->name)) {
+ DRV_NAME)) {
printk(KERN_ERR
"%s : %s -- ERROR, Addresses "
"0x%p to 0x%p ALREADY in use\n",
- __func__, hwif->name, (void *) cmd_phys_base,
+ __func__, DRV_NAME, (void *) cmd_phys_base,
(void *) cmd_phys_base + IOC4_CMD_CTL_BLK_SIZE);
return -ENOMEM;
}
@@ -624,9 +617,12 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev)
hw.irq = dev->irq;
hw.chipset = ide_pci;
hw.dev = &dev->dev;
- ide_init_port_hw(hwif, &hw);
- hwif->dev = &dev->dev;
+ hwif = ide_find_port_slot(&d);
+ if (hwif == NULL)
+ goto err;
+
+ ide_init_port_hw(hwif, &hw);
/* The IOC4 uses MMIO rather than Port IO. */
default_hwif_mmiops(hwif);
@@ -642,6 +638,10 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev)
return -EIO;
return 0;
+err:
+ release_mem_region(cmd_phys_base, IOC4_CMD_CTL_BLK_SIZE);
+ iounmap(virt_base);
+ return -ENOMEM;
}
static unsigned int __devinit
diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c
index 0006b9e58567..b75e9bb390a7 100644
--- a/drivers/ide/pci/siimage.c
+++ b/drivers/ide/pci/siimage.c
@@ -94,7 +94,7 @@ static unsigned long siimage_selreg(ide_hwif_t *hwif, int r)
unsigned long base = (unsigned long)hwif->hwif_data;
base += 0xA0 + r;
- if (hwif->mmio)
+ if (hwif->host_flags & IDE_HFLAG_MMIO)
base += hwif->channel << 6;
else
base += hwif->channel << 4;
@@ -117,7 +117,7 @@ static inline unsigned long siimage_seldev(ide_drive_t *drive, int r)
unsigned long base = (unsigned long)hwif->hwif_data;
base += 0xA0 + r;
- if (hwif->mmio)
+ if (hwif->host_flags & IDE_HFLAG_MMIO)
base += hwif->channel << 6;
else
base += hwif->channel << 4;
@@ -190,7 +190,9 @@ static u8 sil_pata_udma_filter(ide_drive_t *drive)
unsigned long base = (unsigned long)hwif->hwif_data;
u8 scsc, mask = 0;
- scsc = sil_ioread8(dev, base + (hwif->mmio ? 0x4A : 0x8A));
+ base += (hwif->host_flags & IDE_HFLAG_MMIO) ? 0x4A : 0x8A;
+
+ scsc = sil_ioread8(dev, base);
switch (scsc & 0x30) {
case 0x10: /* 133 */
@@ -238,8 +240,9 @@ static void sil_set_pio_mode(ide_drive_t *drive, u8 pio)
unsigned long tfaddr = siimage_selreg(hwif, 0x02);
unsigned long base = (unsigned long)hwif->hwif_data;
u8 tf_pio = pio;
- u8 addr_mask = hwif->channel ? (hwif->mmio ? 0xF4 : 0x84)
- : (hwif->mmio ? 0xB4 : 0x80);
+ u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
+ u8 addr_mask = hwif->channel ? (mmio ? 0xF4 : 0x84)
+ : (mmio ? 0xB4 : 0x80);
u8 mode = 0;
u8 unit = drive->select.b.unit;
@@ -290,13 +293,13 @@ static void sil_set_dma_mode(ide_drive_t *drive, const u8 speed)
u16 ultra = 0, multi = 0;
u8 mode = 0, unit = drive->select.b.unit;
unsigned long base = (unsigned long)hwif->hwif_data;
- u8 scsc = 0, addr_mask = hwif->channel ?
- (hwif->mmio ? 0xF4 : 0x84) :
- (hwif->mmio ? 0xB4 : 0x80);
+ u8 mmio = (hwif->host_flags & IDE_HFLAG_MMIO) ? 1 : 0;
+ u8 scsc = 0, addr_mask = hwif->channel ? (mmio ? 0xF4 : 0x84)
+ : (mmio ? 0xB4 : 0x80);
unsigned long ma = siimage_seldev(drive, 0x08);
unsigned long ua = siimage_seldev(drive, 0x0C);
- scsc = sil_ioread8 (dev, base + (hwif->mmio ? 0x4A : 0x8A));
+ scsc = sil_ioread8 (dev, base + (mmio ? 0x4A : 0x8A));
mode = sil_ioread8 (dev, base + addr_mask);
multi = sil_ioread16(dev, ma);
ultra = sil_ioread16(dev, ua);
@@ -391,7 +394,7 @@ static int siimage_mmio_dma_test_irq(ide_drive_t *drive)
static int siimage_dma_test_irq(ide_drive_t *drive)
{
- if (drive->hwif->mmio)
+ if (drive->hwif->host_flags & IDE_HFLAG_MMIO)
return siimage_mmio_dma_test_irq(drive);
else
return siimage_io_dma_test_irq(drive);
@@ -640,8 +643,6 @@ static void __devinit init_mmio_iops_siimage(ide_hwif_t *hwif)
hwif->irq = dev->irq;
hwif->dma_base = (unsigned long)addr + (ch ? 0x08 : 0x00);
-
- hwif->mmio = 1;
}
static int is_dev_seagate_sata(ide_drive_t *drive)
diff --git a/drivers/ide/pci/sis5513.c b/drivers/ide/pci/sis5513.c
index 4b0b85d8faf5..2389945ca95d 100644
--- a/drivers/ide/pci/sis5513.c
+++ b/drivers/ide/pci/sis5513.c
@@ -52,8 +52,6 @@
#include <linux/init.h>
#include <linux/ide.h>
-#include "ide-timing.h"
-
/* registers layout and init values are chipset family dependant */
#define ATA_16 0x01
@@ -569,6 +567,11 @@ static int __devinit sis5513_init_one(struct pci_dev *dev, const struct pci_devi
{
struct ide_port_info d = sis5513_chipset;
u8 udma_rates[] = { 0x00, 0x00, 0x07, 0x1f, 0x3f, 0x3f, 0x7f, 0x7f };
+ int rc;
+
+ rc = pci_enable_device(dev);
+ if (rc)
+ return rc;
if (sis_find_family(dev) == 0)
return -ENOTSUPP;
@@ -611,7 +614,6 @@ MODULE_LICENSE("GPL");
/*
* TODO:
* - CLEANUP
- * - Use drivers/ide/ide-timing.h !
* - More checks in the config registers (force values instead of
* relying on the BIOS setting them correctly).
* - Further optimisations ?
diff --git a/drivers/ide/pci/sl82c105.c b/drivers/ide/pci/sl82c105.c
index ce84fa045d39..6efbde297174 100644
--- a/drivers/ide/pci/sl82c105.c
+++ b/drivers/ide/pci/sl82c105.c
@@ -47,10 +47,11 @@
*/
static unsigned int get_pio_timings(ide_drive_t *drive, u8 pio)
{
+ struct ide_timing *t = ide_timing_find_mode(XFER_PIO_0 + pio);
unsigned int cmd_on, cmd_off;
u8 iordy = 0;
- cmd_on = (ide_pio_timings[pio].active_time + 29) / 30;
+ cmd_on = (t->active + 29) / 30;
cmd_off = (ide_pio_cycle_time(drive, pio) - 30 * cmd_on + 29) / 30;
if (cmd_on == 0)
diff --git a/drivers/ide/pci/via82cxxx.c b/drivers/ide/pci/via82cxxx.c
index 566e0ecb8db1..e47384c70c40 100644
--- a/drivers/ide/pci/via82cxxx.c
+++ b/drivers/ide/pci/via82cxxx.c
@@ -35,8 +35,6 @@
#include <asm/processor.h>
#endif
-#include "ide-timing.h"
-
#define VIA_IDE_ENABLE 0x40
#define VIA_IDE_CONFIG 0x41
#define VIA_FIFO_CONFIG 0x43
@@ -120,21 +118,21 @@ static void via_set_speed(ide_hwif_t *hwif, u8 dn, struct ide_timing *timing)
if (~vdev->via_config->flags & VIA_BAD_AST) {
pci_read_config_byte(dev, VIA_ADDRESS_SETUP, &t);
- t = (t & ~(3 << ((3 - dn) << 1))) | ((FIT(timing->setup, 1, 4) - 1) << ((3 - dn) << 1));
+ t = (t & ~(3 << ((3 - dn) << 1))) | ((clamp_val(timing->setup, 1, 4) - 1) << ((3 - dn) << 1));
pci_write_config_byte(dev, VIA_ADDRESS_SETUP, t);
}
pci_write_config_byte(dev, VIA_8BIT_TIMING + (1 - (dn >> 1)),
- ((FIT(timing->act8b, 1, 16) - 1) << 4) | (FIT(timing->rec8b, 1, 16) - 1));
+ ((clamp_val(timing->act8b, 1, 16) - 1) << 4) | (clamp_val(timing->rec8b, 1, 16) - 1));
pci_write_config_byte(dev, VIA_DRIVE_TIMING + (3 - dn),
- ((FIT(timing->active, 1, 16) - 1) << 4) | (FIT(timing->recover, 1, 16) - 1));
+ ((clamp_val(timing->active, 1, 16) - 1) << 4) | (clamp_val(timing->recover, 1, 16) - 1));
switch (vdev->via_config->udma_mask) {
- case ATA_UDMA2: t = timing->udma ? (0xe0 | (FIT(timing->udma, 2, 5) - 2)) : 0x03; break;
- case ATA_UDMA4: t = timing->udma ? (0xe8 | (FIT(timing->udma, 2, 9) - 2)) : 0x0f; break;
- case ATA_UDMA5: t = timing->udma ? (0xe0 | (FIT(timing->udma, 2, 9) - 2)) : 0x07; break;
- case ATA_UDMA6: t = timing->udma ? (0xe0 | (FIT(timing->udma, 2, 9) - 2)) : 0x07; break;
+ case ATA_UDMA2: t = timing->udma ? (0xe0 | (clamp_val(timing->udma, 2, 5) - 2)) : 0x03; break;
+ case ATA_UDMA4: t = timing->udma ? (0xe8 | (clamp_val(timing->udma, 2, 9) - 2)) : 0x0f; break;
+ case ATA_UDMA5: t = timing->udma ? (0xe0 | (clamp_val(timing->udma, 2, 9) - 2)) : 0x07; break;
+ case ATA_UDMA6: t = timing->udma ? (0xe0 | (clamp_val(timing->udma, 2, 9) - 2)) : 0x07; break;
default: return;
}
@@ -340,7 +338,7 @@ static unsigned int __devinit init_chipset_via82cxxx(struct pci_dev *dev, const
* Determine system bus clock.
*/
- via_clock = (ide_pci_clk ? ide_pci_clk : system_bus_clock()) * 1000;
+ via_clock = (ide_pci_clk ? ide_pci_clk : 33) * 1000;
switch (via_clock) {
case 33000: via_clock = 33333; break;
diff --git a/drivers/ide/ppc/Makefile b/drivers/ide/ppc/Makefile
index 65af5848b28c..74e52adcdf4b 100644
--- a/drivers/ide/ppc/Makefile
+++ b/drivers/ide/ppc/Makefile
@@ -1,3 +1,2 @@
obj-$(CONFIG_BLK_DEV_IDE_PMAC) += pmac.o
-obj-$(CONFIG_BLK_DEV_MPC8xx_IDE) += mpc8xx.o
diff --git a/drivers/ide/ppc/mpc8xx.c b/drivers/ide/ppc/mpc8xx.c
deleted file mode 100644
index f0e638dcc3ab..000000000000
--- a/drivers/ide/ppc/mpc8xx.c
+++ /dev/null
@@ -1,847 +0,0 @@
-/*
- * Copyright (C) 2000, 2001 Wolfgang Denk, wd@denx.de
- * Modified for direct IDE interface
- * by Thomas Lange, thomas@corelatus.com
- * Modified for direct IDE interface on 8xx without using the PCMCIA
- * controller
- * by Steven.Scholz@imc-berlin.de
- * Moved out of arch/ppc/kernel/m8xx_setup.c, other minor cleanups
- * by Mathew Locke <mattl@mvista.com>
- */
-
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/mm.h>
-#include <linux/stddef.h>
-#include <linux/unistd.h>
-#include <linux/ptrace.h>
-#include <linux/slab.h>
-#include <linux/user.h>
-#include <linux/tty.h>
-#include <linux/major.h>
-#include <linux/interrupt.h>
-#include <linux/reboot.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/ide.h>
-#include <linux/bootmem.h>
-
-#include <asm/mpc8xx.h>
-#include <asm/mmu.h>
-#include <asm/processor.h>
-#include <asm/io.h>
-#include <asm/pgtable.h>
-#include <asm/ide.h>
-#include <asm/8xx_immap.h>
-#include <asm/machdep.h>
-#include <asm/irq.h>
-
-#define DRV_NAME "ide-mpc8xx"
-
-static int identify (volatile u8 *p);
-static void print_fixed (volatile u8 *p);
-static void print_funcid (int func);
-static int check_ide_device (unsigned long base);
-
-static void ide_interrupt_ack (void *dev);
-static void m8xx_ide_set_pio_mode(ide_drive_t *drive, const u8 pio);
-
-typedef struct ide_ioport_desc {
- unsigned long base_off; /* Offset to PCMCIA memory */
- unsigned long reg_off[IDE_NR_PORTS]; /* controller register offsets */
- int irq; /* IRQ */
-} ide_ioport_desc_t;
-
-ide_ioport_desc_t ioport_dsc[MAX_HWIFS] = {
-#ifdef IDE0_BASE_OFFSET
- { IDE0_BASE_OFFSET,
- {
- IDE0_DATA_REG_OFFSET,
- IDE0_ERROR_REG_OFFSET,
- IDE0_NSECTOR_REG_OFFSET,
- IDE0_SECTOR_REG_OFFSET,
- IDE0_LCYL_REG_OFFSET,
- IDE0_HCYL_REG_OFFSET,
- IDE0_SELECT_REG_OFFSET,
- IDE0_STATUS_REG_OFFSET,
- IDE0_CONTROL_REG_OFFSET,
- IDE0_IRQ_REG_OFFSET,
- },
- IDE0_INTERRUPT,
- },
-#ifdef IDE1_BASE_OFFSET
- { IDE1_BASE_OFFSET,
- {
- IDE1_DATA_REG_OFFSET,
- IDE1_ERROR_REG_OFFSET,
- IDE1_NSECTOR_REG_OFFSET,
- IDE1_SECTOR_REG_OFFSET,
- IDE1_LCYL_REG_OFFSET,
- IDE1_HCYL_REG_OFFSET,
- IDE1_SELECT_REG_OFFSET,
- IDE1_STATUS_REG_OFFSET,
- IDE1_CONTROL_REG_OFFSET,
- IDE1_IRQ_REG_OFFSET,
- },
- IDE1_INTERRUPT,
- },
-#endif /* IDE1_BASE_OFFSET */
-#endif /* IDE0_BASE_OFFSET */
-};
-
-ide_pio_timings_t ide_pio_clocks[6];
-int hold_time[6] = {30, 20, 15, 10, 10, 10 }; /* PIO Mode 5 with IORDY (nonstandard) */
-
-/*
- * Warning: only 1 (ONE) PCMCIA slot supported here,
- * which must be correctly initialized by the firmware (PPCBoot).
- */
-static int _slot_ = -1; /* will be read from PCMCIA registers */
-
-/* Make clock cycles and always round up */
-#define PCMCIA_MK_CLKS( t, T ) (( (t) * ((T)/1000000) + 999U ) / 1000U )
-
-#define M8XX_PCMCIA_CD2(slot) (0x10000000 >> (slot << 4))
-#define M8XX_PCMCIA_CD1(slot) (0x08000000 >> (slot << 4))
-
-/*
- * The TQM850L hardware has two pins swapped! Grrrrgh!
- */
-#ifdef CONFIG_TQM850L
-#define __MY_PCMCIA_GCRX_CXRESET PCMCIA_GCRX_CXOE
-#define __MY_PCMCIA_GCRX_CXOE PCMCIA_GCRX_CXRESET
-#else
-#define __MY_PCMCIA_GCRX_CXRESET PCMCIA_GCRX_CXRESET
-#define __MY_PCMCIA_GCRX_CXOE PCMCIA_GCRX_CXOE
-#endif
-
-#if defined(CONFIG_BLK_DEV_MPC8xx_IDE) && defined(CONFIG_IDE_8xx_PCCARD)
-#define PCMCIA_SCHLVL IDE0_INTERRUPT /* Status Change Interrupt Level */
-static int pcmcia_schlvl = PCMCIA_SCHLVL;
-#endif
-
-/*
- * See include/linux/ide.h for definition of hw_regs_t (p, base)
- */
-
-/*
- * m8xx_ide_init_ports() for a direct IDE interface _using_
- * MPC8xx's internal PCMCIA interface
- */
-#if defined(CONFIG_IDE_8xx_PCCARD) || defined(CONFIG_IDE_8xx_DIRECT)
-static int __init m8xx_ide_init_ports(hw_regs_t *hw, unsigned long data_port)
-{
- unsigned long *p = hw->io_ports_array;
- int i;
-
- typedef struct {
- ulong br;
- ulong or;
- } pcmcia_win_t;
- volatile pcmcia_win_t *win;
- volatile pcmconf8xx_t *pcmp;
-
- uint *pgcrx;
- u32 pcmcia_phy_base;
- u32 pcmcia_phy_end;
- static unsigned long pcmcia_base = 0;
- unsigned long base;
-
- *p = 0;
-
- pcmp = (pcmconf8xx_t *)(&(((immap_t *)IMAP_ADDR)->im_pcmcia));
-
- if (!pcmcia_base) {
- /*
- * Read out PCMCIA registers. Since the reset values
- * are undefined, we sure hope that they have been
- * set up by firmware
- */
-
- /* Scan all registers for valid settings */
- pcmcia_phy_base = 0xFFFFFFFF;
- pcmcia_phy_end = 0;
- /* br0 is start of brX and orX regs */
- win = (pcmcia_win_t *) \
- (&(((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pbr0));
- for (i = 0; i < 8; i++) {
- if (win->or & 1) { /* This bank is marked as valid */
- if (win->br < pcmcia_phy_base) {
- pcmcia_phy_base = win->br;
- }
- if ((win->br + PCMCIA_MEM_SIZE) > pcmcia_phy_end) {
- pcmcia_phy_end = win->br + PCMCIA_MEM_SIZE;
- }
- /* Check which slot that has been defined */
- _slot_ = (win->or >> 2) & 1;
-
- } /* Valid bank */
- win++;
- } /* for */
-
- printk ("PCMCIA slot %c: phys mem %08x...%08x (size %08x)\n",
- 'A' + _slot_,
- pcmcia_phy_base, pcmcia_phy_end,
- pcmcia_phy_end - pcmcia_phy_base);
-
- if (!request_mem_region(pcmcia_phy_base,
- pcmcia_phy_end - pcmcia_phy_base,
- DRV_NAME)) {
- printk(KERN_ERR "%s: resources busy\n", DRV_NAME);
- return -EBUSY;
- }
-
- pcmcia_base=(unsigned long)ioremap(pcmcia_phy_base,
- pcmcia_phy_end-pcmcia_phy_base);
-
-#ifdef DEBUG
- printk ("PCMCIA virt base: %08lx\n", pcmcia_base);
-#endif
- /* Compute clock cycles for PIO timings */
- for (i=0; i<6; ++i) {
- bd_t *binfo = (bd_t *)__res;
-
- hold_time[i] =
- PCMCIA_MK_CLKS (hold_time[i],
- binfo->bi_busfreq);
- ide_pio_clocks[i].setup_time =
- PCMCIA_MK_CLKS (ide_pio_timings[i].setup_time,
- binfo->bi_busfreq);
- ide_pio_clocks[i].active_time =
- PCMCIA_MK_CLKS (ide_pio_timings[i].active_time,
- binfo->bi_busfreq);
- ide_pio_clocks[i].cycle_time =
- PCMCIA_MK_CLKS (ide_pio_timings[i].cycle_time,
- binfo->bi_busfreq);
-#if 0
- printk ("PIO mode %d timings: %d/%d/%d => %d/%d/%d\n",
- i,
- ide_pio_clocks[i].setup_time,
- ide_pio_clocks[i].active_time,
- ide_pio_clocks[i].hold_time,
- ide_pio_clocks[i].cycle_time,
- ide_pio_timings[i].setup_time,
- ide_pio_timings[i].active_time,
- ide_pio_timings[i].hold_time,
- ide_pio_timings[i].cycle_time);
-#endif
- }
- }
-
- if (_slot_ == -1) {
- printk ("PCMCIA slot has not been defined! Using A as default\n");
- _slot_ = 0;
- }
-
-#ifdef CONFIG_IDE_8xx_PCCARD
-
-#ifdef DEBUG
- printk ("PIPR = 0x%08X slot %c ==> mask = 0x%X\n",
- pcmp->pcmc_pipr,
- 'A' + _slot_,
- M8XX_PCMCIA_CD1(_slot_) | M8XX_PCMCIA_CD2(_slot_) );
-#endif /* DEBUG */
-
- if (pcmp->pcmc_pipr & (M8XX_PCMCIA_CD1(_slot_)|M8XX_PCMCIA_CD2(_slot_))) {
- printk ("No card in slot %c: PIPR=%08x\n",
- 'A' + _slot_, (u32) pcmp->pcmc_pipr);
- return -ENODEV; /* No card in slot */
- }
-
- check_ide_device (pcmcia_base);
-
-#endif /* CONFIG_IDE_8xx_PCCARD */
-
- base = pcmcia_base + ioport_dsc[data_port].base_off;
-#ifdef DEBUG
- printk ("base: %08x + %08x = %08x\n",
- pcmcia_base, ioport_dsc[data_port].base_off, base);
-#endif
-
- for (i = 0; i < IDE_NR_PORTS; ++i) {
-#ifdef DEBUG
- printk ("port[%d]: %08x + %08x = %08x\n",
- i,
- base,
- ioport_dsc[data_port].reg_off[i],
- i, base + ioport_dsc[data_port].reg_off[i]);
-#endif
- *p++ = base + ioport_dsc[data_port].reg_off[i];
- }
-
- hw->irq = ioport_dsc[data_port].irq;
- hw->ack_intr = (ide_ack_intr_t *)ide_interrupt_ack;
-
-#ifdef CONFIG_IDE_8xx_PCCARD
- {
- unsigned int reg;
-
- if (_slot_)
- pgcrx = &((immap_t *) IMAP_ADDR)->im_pcmcia.pcmc_pgcrb;
- else
- pgcrx = &((immap_t *) IMAP_ADDR)->im_pcmcia.pcmc_pgcra;
-
- reg = *pgcrx;
- reg |= mk_int_int_mask (pcmcia_schlvl) << 24;
- reg |= mk_int_int_mask (pcmcia_schlvl) << 16;
- *pgcrx = reg;
- }
-#endif /* CONFIG_IDE_8xx_PCCARD */
-
- /* Enable Harddisk Interrupt,
- * and make it edge sensitive
- */
- /* (11-18) Set edge detect for irq, no wakeup from low power mode */
- ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_siel |=
- (0x80000000 >> ioport_dsc[data_port].irq);
-
-#ifdef CONFIG_IDE_8xx_PCCARD
- /* Make sure we don't get garbage irq */
- ((immap_t *) IMAP_ADDR)->im_pcmcia.pcmc_pscr = 0xFFFF;
-
- /* Enable falling edge irq */
- pcmp->pcmc_per = 0x100000 >> (16 * _slot_);
-#endif /* CONFIG_IDE_8xx_PCCARD */
-
- return 0;
-}
-#endif /* CONFIG_IDE_8xx_PCCARD || CONFIG_IDE_8xx_DIRECT */
-
-/*
- * m8xx_ide_init_ports() for a direct IDE interface _not_ using
- * MPC8xx's internal PCMCIA interface
- */
-#if defined(CONFIG_IDE_EXT_DIRECT)
-static int __init m8xx_ide_init_ports(hw_regs_t *hw, unsigned long data_port)
-{
- unsigned long *p = hw->io_ports_array;
- int i;
-
- u32 ide_phy_base;
- u32 ide_phy_end;
- static unsigned long ide_base = 0;
- unsigned long base;
-
- *p = 0;
-
- if (!ide_base) {
-
- /* TODO:
- * - add code to read ORx, BRx
- */
- ide_phy_base = CFG_ATA_BASE_ADDR;
- ide_phy_end = CFG_ATA_BASE_ADDR + 0x200;
-
- printk ("IDE phys mem : %08x...%08x (size %08x)\n",
- ide_phy_base, ide_phy_end,
- ide_phy_end - ide_phy_base);
-
- if (!request_mem_region(ide_phy_base, 0x200, DRV_NAME)) {
- printk(KERN_ERR "%s: resources busy\n", DRV_NAME);
- return -EBUSY;
- }
-
- ide_base=(unsigned long)ioremap(ide_phy_base,
- ide_phy_end-ide_phy_base);
-
-#ifdef DEBUG
- printk ("IDE virt base: %08lx\n", ide_base);
-#endif
- }
-
- base = ide_base + ioport_dsc[data_port].base_off;
-#ifdef DEBUG
- printk ("base: %08x + %08x = %08x\n",
- ide_base, ioport_dsc[data_port].base_off, base);
-#endif
-
- for (i = 0; i < IDE_NR_PORTS; ++i) {
-#ifdef DEBUG
- printk ("port[%d]: %08x + %08x = %08x\n",
- i,
- base,
- ioport_dsc[data_port].reg_off[i],
- i, base + ioport_dsc[data_port].reg_off[i]);
-#endif
- *p++ = base + ioport_dsc[data_port].reg_off[i];
- }
-
- /* direct connected IDE drive, i.e. external IRQ */
- hw->irq = ioport_dsc[data_port].irq;
- hw->ack_intr = (ide_ack_intr_t *)ide_interrupt_ack;
-
- /* Enable Harddisk Interrupt,
- * and make it edge sensitive
- */
- /* (11-18) Set edge detect for irq, no wakeup from low power mode */
- ((immap_t *) IMAP_ADDR)->im_siu_conf.sc_siel |=
- (0x80000000 >> ioport_dsc[data_port].irq);
-
- return 0;
-}
-#endif /* CONFIG_IDE_8xx_DIRECT */
-
-
-/* -------------------------------------------------------------------- */
-
-
-/* PCMCIA Timing */
-#ifndef PCMCIA_SHT
-#define PCMCIA_SHT(t) ((t & 0x0F)<<16) /* Strobe Hold Time */
-#define PCMCIA_SST(t) ((t & 0x0F)<<12) /* Strobe Setup Time */
-#define PCMCIA_SL(t) ((t==32) ? 0 : ((t & 0x1F)<<7)) /* Strobe Length */
-#endif
-
-/* Calculate PIO timings */
-static void m8xx_ide_set_pio_mode(ide_drive_t *drive, const u8 pio)
-{
-#if defined(CONFIG_IDE_8xx_PCCARD) || defined(CONFIG_IDE_8xx_DIRECT)
- volatile pcmconf8xx_t *pcmp;
- ulong timing, mask, reg;
-
- pcmp = (pcmconf8xx_t *)(&(((immap_t *)IMAP_ADDR)->im_pcmcia));
-
- mask = ~(PCMCIA_SHT(0xFF) | PCMCIA_SST(0xFF) | PCMCIA_SL(0xFF));
-
- timing = PCMCIA_SHT(hold_time[pio] )
- | PCMCIA_SST(ide_pio_clocks[pio].setup_time )
- | PCMCIA_SL (ide_pio_clocks[pio].active_time)
- ;
-
-#if 1
- printk ("Setting timing bits 0x%08lx in PCMCIA controller\n", timing);
-#endif
- if ((reg = pcmp->pcmc_por0 & mask) != 0)
- pcmp->pcmc_por0 = reg | timing;
-
- if ((reg = pcmp->pcmc_por1 & mask) != 0)
- pcmp->pcmc_por1 = reg | timing;
-
- if ((reg = pcmp->pcmc_por2 & mask) != 0)
- pcmp->pcmc_por2 = reg | timing;
-
- if ((reg = pcmp->pcmc_por3 & mask) != 0)
- pcmp->pcmc_por3 = reg | timing;
-
- if ((reg = pcmp->pcmc_por4 & mask) != 0)
- pcmp->pcmc_por4 = reg | timing;
-
- if ((reg = pcmp->pcmc_por5 & mask) != 0)
- pcmp->pcmc_por5 = reg | timing;
-
- if ((reg = pcmp->pcmc_por6 & mask) != 0)
- pcmp->pcmc_por6 = reg | timing;
-
- if ((reg = pcmp->pcmc_por7 & mask) != 0)
- pcmp->pcmc_por7 = reg | timing;
-
-#elif defined(CONFIG_IDE_EXT_DIRECT)
-
- printk("%s[%d] %s: not implemented yet!\n",
- __FILE__, __LINE__, __func__);
-#endif /* defined(CONFIG_IDE_8xx_PCCARD) || defined(CONFIG_IDE_8xx_PCMCIA */
-}
-
-static const struct ide_port_ops m8xx_port_ops = {
- .set_pio_mode = m8xx_ide_set_pio_mode,
-};
-
-static void
-ide_interrupt_ack (void *dev)
-{
-#ifdef CONFIG_IDE_8xx_PCCARD
- u_int pscr, pipr;
-
-#if (PCMCIA_SOCKETS_NO == 2)
- u_int _slot_;
-#endif
-
- /* get interrupt sources */
-
- pscr = ((volatile immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr;
- pipr = ((volatile immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pipr;
-
- /*
- * report only if both card detect signals are the same
- * not too nice done,
- * we depend on that CD2 is the bit to the left of CD1...
- */
-
- if(_slot_==-1){
- printk("PCMCIA slot has not been defined! Using A as default\n");
- _slot_=0;
- }
-
- if(((pipr & M8XX_PCMCIA_CD2(_slot_)) >> 1) ^
- (pipr & M8XX_PCMCIA_CD1(_slot_)) ) {
- printk ("card detect interrupt\n");
- }
- /* clear the interrupt sources */
- ((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr = pscr;
-
-#else /* ! CONFIG_IDE_8xx_PCCARD */
- /*
- * Only CONFIG_IDE_8xx_PCCARD is using the interrupt of the
- * MPC8xx's PCMCIA controller, so there is nothing to be done here
- * for CONFIG_IDE_8xx_DIRECT and CONFIG_IDE_EXT_DIRECT.
- * The interrupt is handled somewhere else. -- Steven
- */
-#endif /* CONFIG_IDE_8xx_PCCARD */
-}
-
-
-
-/*
- * CIS Tupel codes
- */
-#define CISTPL_NULL 0x00
-#define CISTPL_DEVICE 0x01
-#define CISTPL_LONGLINK_CB 0x02
-#define CISTPL_INDIRECT 0x03
-#define CISTPL_CONFIG_CB 0x04
-#define CISTPL_CFTABLE_ENTRY_CB 0x05
-#define CISTPL_LONGLINK_MFC 0x06
-#define CISTPL_BAR 0x07
-#define CISTPL_PWR_MGMNT 0x08
-#define CISTPL_EXTDEVICE 0x09
-#define CISTPL_CHECKSUM 0x10
-#define CISTPL_LONGLINK_A 0x11
-#define CISTPL_LONGLINK_C 0x12
-#define CISTPL_LINKTARGET 0x13
-#define CISTPL_NO_LINK 0x14
-#define CISTPL_VERS_1 0x15
-#define CISTPL_ALTSTR 0x16
-#define CISTPL_DEVICE_A 0x17
-#define CISTPL_JEDEC_C 0x18
-#define CISTPL_JEDEC_A 0x19
-#define CISTPL_CONFIG 0x1a
-#define CISTPL_CFTABLE_ENTRY 0x1b
-#define CISTPL_DEVICE_OC 0x1c
-#define CISTPL_DEVICE_OA 0x1d
-#define CISTPL_DEVICE_GEO 0x1e
-#define CISTPL_DEVICE_GEO_A 0x1f
-#define CISTPL_MANFID 0x20
-#define CISTPL_FUNCID 0x21
-#define CISTPL_FUNCE 0x22
-#define CISTPL_SWIL 0x23
-#define CISTPL_END 0xff
-
-/*
- * CIS Function ID codes
- */
-#define CISTPL_FUNCID_MULTI 0x00
-#define CISTPL_FUNCID_MEMORY 0x01
-#define CISTPL_FUNCID_SERIAL 0x02
-#define CISTPL_FUNCID_PARALLEL 0x03
-#define CISTPL_FUNCID_FIXED 0x04
-#define CISTPL_FUNCID_VIDEO 0x05
-#define CISTPL_FUNCID_NETWORK 0x06
-#define CISTPL_FUNCID_AIMS 0x07
-#define CISTPL_FUNCID_SCSI 0x08
-
-/*
- * Fixed Disk FUNCE codes
- */
-#define CISTPL_IDE_INTERFACE 0x01
-
-#define CISTPL_FUNCE_IDE_IFACE 0x01
-#define CISTPL_FUNCE_IDE_MASTER 0x02
-#define CISTPL_FUNCE_IDE_SLAVE 0x03
-
-/* First feature byte */
-#define CISTPL_IDE_SILICON 0x04
-#define CISTPL_IDE_UNIQUE 0x08
-#define CISTPL_IDE_DUAL 0x10
-
-/* Second feature byte */
-#define CISTPL_IDE_HAS_SLEEP 0x01
-#define CISTPL_IDE_HAS_STANDBY 0x02
-#define CISTPL_IDE_HAS_IDLE 0x04
-#define CISTPL_IDE_LOW_POWER 0x08
-#define CISTPL_IDE_REG_INHIBIT 0x10
-#define CISTPL_IDE_HAS_INDEX 0x20
-#define CISTPL_IDE_IOIS16 0x40
-
-
-/* -------------------------------------------------------------------- */
-
-
-#define MAX_TUPEL_SZ 512
-#define MAX_FEATURES 4
-
-static int check_ide_device (unsigned long base)
-{
- volatile u8 *ident = NULL;
- volatile u8 *feature_p[MAX_FEATURES];
- volatile u8 *p, *start;
- int n_features = 0;
- u8 func_id = ~0;
- u8 code, len;
- unsigned short config_base = 0;
- int found = 0;
- int i;
-
-#ifdef DEBUG
- printk ("PCMCIA MEM: %08lX\n", base);
-#endif
- start = p = (volatile u8 *) base;
-
- while ((p - start) < MAX_TUPEL_SZ) {
-
- code = *p; p += 2;
-
- if (code == 0xFF) { /* End of chain */
- break;
- }
-
- len = *p; p += 2;
-#ifdef DEBUG_PCMCIA
- { volatile u8 *q = p;
- printk ("\nTuple code %02x length %d\n\tData:",
- code, len);
-
- for (i = 0; i < len; ++i) {
- printk (" %02x", *q);
- q+= 2;
- }
- }
-#endif /* DEBUG_PCMCIA */
- switch (code) {
- case CISTPL_VERS_1:
- ident = p + 4;
- break;
- case CISTPL_FUNCID:
- func_id = *p;
- break;
- case CISTPL_FUNCE:
- if (n_features < MAX_FEATURES)
- feature_p[n_features++] = p;
- break;
- case CISTPL_CONFIG:
- config_base = (*(p+6) << 8) + (*(p+4));
- default:
- break;
- }
- p += 2 * len;
- }
-
- found = identify (ident);
-
- if (func_id != ((u8)~0)) {
- print_funcid (func_id);
-
- if (func_id == CISTPL_FUNCID_FIXED)
- found = 1;
- else
- return (1); /* no disk drive */
- }
-
- for (i=0; i<n_features; ++i) {
- print_fixed (feature_p[i]);
- }
-
- if (!found) {
- printk ("unknown card type\n");
- return (1);
- }
-
- /* set level mode irq and I/O mapped device in config reg*/
- *((u8 *)(base + config_base)) = 0x41;
-
- return (0);
-}
-
-/* ------------------------------------------------------------------------- */
-
-static void print_funcid (int func)
-{
- switch (func) {
- case CISTPL_FUNCID_MULTI:
- printk (" Multi-Function");
- break;
- case CISTPL_FUNCID_MEMORY:
- printk (" Memory");
- break;
- case CISTPL_FUNCID_SERIAL:
- printk (" Serial Port");
- break;
- case CISTPL_FUNCID_PARALLEL:
- printk (" Parallel Port");
- break;
- case CISTPL_FUNCID_FIXED:
- printk (" Fixed Disk");
- break;
- case CISTPL_FUNCID_VIDEO:
- printk (" Video Adapter");
- break;
- case CISTPL_FUNCID_NETWORK:
- printk (" Network Adapter");
- break;
- case CISTPL_FUNCID_AIMS:
- printk (" AIMS Card");
- break;
- case CISTPL_FUNCID_SCSI:
- printk (" SCSI Adapter");
- break;
- default:
- printk (" Unknown");
- break;
- }
- printk (" Card\n");
-}
-
-/* ------------------------------------------------------------------------- */
-
-static void print_fixed (volatile u8 *p)
-{
- if (p == NULL)
- return;
-
- switch (*p) {
- case CISTPL_FUNCE_IDE_IFACE:
- { u8 iface = *(p+2);
-
- printk ((iface == CISTPL_IDE_INTERFACE) ? " IDE" : " unknown");
- printk (" interface ");
- break;
- }
- case CISTPL_FUNCE_IDE_MASTER:
- case CISTPL_FUNCE_IDE_SLAVE:
- { u8 f1 = *(p+2);
- u8 f2 = *(p+4);
-
- printk ((f1 & CISTPL_IDE_SILICON) ? " [silicon]" : " [rotating]");
-
- if (f1 & CISTPL_IDE_UNIQUE)
- printk (" [unique]");
-
- printk ((f1 & CISTPL_IDE_DUAL) ? " [dual]" : " [single]");
-
- if (f2 & CISTPL_IDE_HAS_SLEEP)
- printk (" [sleep]");
-
- if (f2 & CISTPL_IDE_HAS_STANDBY)
- printk (" [standby]");
-
- if (f2 & CISTPL_IDE_HAS_IDLE)
- printk (" [idle]");
-
- if (f2 & CISTPL_IDE_LOW_POWER)
- printk (" [low power]");
-
- if (f2 & CISTPL_IDE_REG_INHIBIT)
- printk (" [reg inhibit]");
-
- if (f2 & CISTPL_IDE_HAS_INDEX)
- printk (" [index]");
-
- if (f2 & CISTPL_IDE_IOIS16)
- printk (" [IOis16]");
-
- break;
- }
- }
- printk ("\n");
-}
-
-/* ------------------------------------------------------------------------- */
-
-
-#define MAX_IDENT_CHARS 64
-#define MAX_IDENT_FIELDS 4
-
-static u8 *known_cards[] = {
- "ARGOSY PnPIDE D5",
- NULL
-};
-
-static int identify (volatile u8 *p)
-{
- u8 id_str[MAX_IDENT_CHARS];
- u8 data;
- u8 *t;
- u8 **card;
- int i, done;
-
- if (p == NULL)
- return (0); /* Don't know */
-
- t = id_str;
- done =0;
-
- for (i=0; i<=4 && !done; ++i, p+=2) {
- while ((data = *p) != '\0') {
- if (data == 0xFF) {
- done = 1;
- break;
- }
- *t++ = data;
- if (t == &id_str[MAX_IDENT_CHARS-1]) {
- done = 1;
- break;
- }
- p += 2;
- }
- if (!done)
- *t++ = ' ';
- }
- *t = '\0';
- while (--t > id_str) {
- if (*t == ' ')
- *t = '\0';
- else
- break;
- }
- printk ("Card ID: %s\n", id_str);
-
- for (card=known_cards; *card; ++card) {
- if (strcmp(*card, id_str) == 0) { /* found! */
- return (1);
- }
- }
-
- return (0); /* don't know */
-}
-
-static int __init mpc8xx_ide_probe(void)
-{
- hw_regs_t hw;
- u8 idx[4] = { 0xff, 0xff, 0xff, 0xff };
-
-#ifdef IDE0_BASE_OFFSET
- memset(&hw, 0, sizeof(hw));
- if (!m8xx_ide_init_ports(&hw, 0)) {
- ide_hwif_t *hwif = ide_find_port();
-
- if (hwif) {
- ide_init_port_hw(hwif, &hw);
- hwif->pio_mask = ATA_PIO4;
- hwif->port_ops = &m8xx_port_ops;
-
- idx[0] = hwif->index;
- }
- }
-#ifdef IDE1_BASE_OFFSET
- memset(&hw, 0, sizeof(hw));
- if (!m8xx_ide_init_ports(&hw, 1)) {
- ide_hwif_t *mate = ide_find_port();
-
- if (mate) {
- ide_init_port_hw(mate, &hw);
- mate->pio_mask = ATA_PIO4;
- mate->port_ops = &m8xx_port_ops;
-
- idx[1] = mate->index;
- }
- }
-#endif
-#endif
-
- ide_device_add(idx, NULL);
-
- return 0;
-}
-
-module_init(mpc8xx_ide_probe);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c
index 48aa019127bc..93fb9067c043 100644
--- a/drivers/ide/ppc/pmac.c
+++ b/drivers/ide/ppc/pmac.c
@@ -5,7 +5,7 @@
* for doing DMA.
*
* Copyright (C) 1998-2003 Paul Mackerras & Ben. Herrenschmidt
- * Copyright (C) 2007 Bartlomiej Zolnierkiewicz
+ * Copyright (C) 2007-2008 Bartlomiej Zolnierkiewicz
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -48,8 +48,6 @@
#include <asm/mediabay.h>
#endif
-#include "../ide-timing.h"
-
#undef IDE_PMAC_DEBUG
#define DMA_WAIT_TIMEOUT 50
@@ -59,7 +57,6 @@ typedef struct pmac_ide_hwif {
int irq;
int kind;
int aapl_bus_id;
- unsigned cable_80 : 1;
unsigned mediabay : 1;
unsigned broken_dma : 1;
unsigned broken_dma_warn : 1;
@@ -481,13 +478,13 @@ pmac_ide_do_update_timings(ide_drive_t *drive)
pmac_ide_selectproc(drive);
}
-static void
-pmac_outbsync(ide_drive_t *drive, u8 value, unsigned long port)
+static void pmac_outbsync(ide_hwif_t *hwif, u8 value, unsigned long port)
{
u32 tmp;
writeb(value, (void __iomem *) port);
- tmp = readl(PMAC_IDE_REG(IDE_TIMING_CONFIG));
+ tmp = readl((void __iomem *)(hwif->io_ports.data_addr
+ + IDE_TIMING_CONFIG));
}
/*
@@ -496,6 +493,7 @@ pmac_outbsync(ide_drive_t *drive, u8 value, unsigned long port)
static void
pmac_ide_set_pio_mode(ide_drive_t *drive, const u8 pio)
{
+ struct ide_timing *tim = ide_timing_find_mode(XFER_PIO_0 + pio);
u32 *timings, t;
unsigned accessTicks, recTicks;
unsigned accessTime, recTime;
@@ -527,10 +525,9 @@ pmac_ide_set_pio_mode(ide_drive_t *drive, const u8 pio)
}
case controller_kl_ata4:
/* 66Mhz cell */
- recTime = cycle_time - ide_pio_timings[pio].active_time
- - ide_pio_timings[pio].setup_time;
+ recTime = cycle_time - tim->active - tim->setup;
recTime = max(recTime, 150U);
- accessTime = ide_pio_timings[pio].active_time;
+ accessTime = tim->active;
accessTime = max(accessTime, 150U);
accessTicks = SYSCLK_TICKS_66(accessTime);
accessTicks = min(accessTicks, 0x1fU);
@@ -543,10 +540,9 @@ pmac_ide_set_pio_mode(ide_drive_t *drive, const u8 pio)
default: {
/* 33Mhz cell */
int ebit = 0;
- recTime = cycle_time - ide_pio_timings[pio].active_time
- - ide_pio_timings[pio].setup_time;
+ recTime = cycle_time - tim->active - tim->setup;
recTime = max(recTime, 150U);
- accessTime = ide_pio_timings[pio].active_time;
+ accessTime = tim->active;
accessTime = max(accessTime, 150U);
accessTicks = SYSCLK_TICKS(accessTime);
accessTicks = min(accessTicks, 0x1fU);
@@ -918,10 +914,40 @@ pmac_ide_do_resume(ide_hwif_t *hwif)
return 0;
}
+static u8 pmac_ide_cable_detect(ide_hwif_t *hwif)
+{
+ pmac_ide_hwif_t *pmif = (pmac_ide_hwif_t *)ide_get_hwifdata(hwif);
+ struct device_node *np = pmif->node;
+ const char *cable = of_get_property(np, "cable-type", NULL);
+
+ /* Get cable type from device-tree. */
+ if (cable && !strncmp(cable, "80-", 3))
+ return ATA_CBL_PATA80;
+
+ /*
+ * G5's seem to have incorrect cable type in device-tree.
+ * Let's assume they have a 80 conductor cable, this seem
+ * to be always the case unless the user mucked around.
+ */
+ if (of_device_is_compatible(np, "K2-UATA") ||
+ of_device_is_compatible(np, "shasta-ata"))
+ return ATA_CBL_PATA80;
+
+ return ATA_CBL_PATA40;
+}
+
static const struct ide_port_ops pmac_ide_ata6_port_ops = {
.set_pio_mode = pmac_ide_set_pio_mode,
.set_dma_mode = pmac_ide_set_dma_mode,
.selectproc = pmac_ide_kauai_selectproc,
+ .cable_detect = pmac_ide_cable_detect,
+};
+
+static const struct ide_port_ops pmac_ide_ata4_port_ops = {
+ .set_pio_mode = pmac_ide_set_pio_mode,
+ .set_dma_mode = pmac_ide_set_dma_mode,
+ .selectproc = pmac_ide_selectproc,
+ .cable_detect = pmac_ide_cable_detect,
};
static const struct ide_port_ops pmac_ide_port_ops = {
@@ -949,10 +975,7 @@ static const struct ide_port_info pmac_port_info = {
/*
* Setup, register & probe an IDE channel driven by this driver, this is
- * called by one of the 2 probe functions (macio or PCI). Note that a channel
- * that ends up beeing free of any device is not kept around by this driver
- * (it is kept in 2.4). This introduce an interface numbering change on some
- * rare machines unfortunately, but it's better this way.
+ * called by one of the 2 probe functions (macio or PCI).
*/
static int __devinit
pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif, hw_regs_t *hw)
@@ -962,7 +985,6 @@ pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif, hw_regs_t *hw)
u8 idx[4] = { 0xff, 0xff, 0xff, 0xff };
struct ide_port_info d = pmac_port_info;
- pmif->cable_80 = 0;
pmif->broken_dma = pmif->broken_dma_warn = 0;
if (of_device_is_compatible(np, "shasta-ata")) {
pmif->kind = controller_sh_ata6;
@@ -979,6 +1001,7 @@ pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif, hw_regs_t *hw)
} else if (of_device_is_compatible(np, "keylargo-ata")) {
if (strcmp(np->name, "ata-4") == 0) {
pmif->kind = controller_kl_ata4;
+ d.port_ops = &pmac_ide_ata4_port_ops;
d.udma_mask = ATA_UDMA4;
} else
pmif->kind = controller_kl_ata3;
@@ -992,22 +1015,6 @@ pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif, hw_regs_t *hw)
bidp = of_get_property(np, "AAPL,bus-id", NULL);
pmif->aapl_bus_id = bidp ? *bidp : 0;
- /* Get cable type from device-tree */
- if (pmif->kind == controller_kl_ata4 || pmif->kind == controller_un_ata6
- || pmif->kind == controller_k2_ata6
- || pmif->kind == controller_sh_ata6) {
- const char* cable = of_get_property(np, "cable-type", NULL);
- if (cable && !strncmp(cable, "80-", 3))
- pmif->cable_80 = 1;
- }
- /* G5's seem to have incorrect cable type in device-tree. Let's assume
- * they have a 80 conductor cable, this seem to be always the case unless
- * the user mucked around
- */
- if (of_device_is_compatible(np, "K2-UATA") ||
- of_device_is_compatible(np, "shasta-ata"))
- pmif->cable_80 = 1;
-
/* On Kauai-type controllers, we make sure the FCR is correct */
if (pmif->kauai_fcr)
writel(KAUAI_FCR_UATA_MAGIC |
@@ -1053,7 +1060,6 @@ pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif, hw_regs_t *hw)
hwif->hwif_data = pmif;
ide_init_port_hw(hwif, hw);
- hwif->cbl = pmif->cable_80 ? ATA_CBL_PATA80 : ATA_CBL_PATA40;
printk(KERN_INFO "ide%d: Found Apple %s controller, bus ID %d%s, irq %d\n",
hwif->index, model_name[pmif->kind], pmif->aapl_bus_id,
@@ -1070,11 +1076,6 @@ pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif, hw_regs_t *hw)
}
}
-#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC
- if (pmif->cable_80 == 0)
- d.udma_mask &= ATA_UDMA2;
-#endif
-
idx[0] = hwif->index;
ide_device_add(idx, &d);
@@ -1147,8 +1148,6 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match)
base = ioremap(macio_resource_start(mdev, 0), 0x400);
regbase = (unsigned long) base;
- hwif->dev = &mdev->bus->pdev->dev;
-
pmif->mdev = mdev;
pmif->node = mdev->ofdev.node;
pmif->regbase = regbase;
@@ -1170,7 +1169,8 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match)
memset(&hw, 0, sizeof(hw));
pmac_ide_init_ports(&hw, pmif->regbase);
hw.irq = irq;
- hw.dev = &mdev->ofdev.dev;
+ hw.dev = &mdev->bus->pdev->dev;
+ hw.parent = &mdev->ofdev.dev;
rc = pmac_ide_setup_device(pmif, hwif, &hw);
if (rc != 0) {
@@ -1270,7 +1270,6 @@ pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id)
goto out_free_pmif;
}
- hwif->dev = &pdev->dev;
pmif->mdev = NULL;
pmif->node = np;
diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c
index 5171601fb255..9edb944bb6b9 100644
--- a/drivers/ide/setup-pci.c
+++ b/drivers/ide/setup-pci.c
@@ -6,19 +6,14 @@
* May be copied or modified under the terms of the GNU General Public License
*/
-#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/init.h>
-#include <linux/timer.h>
-#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/ide.h>
-#include <linux/dma-mapping.h>
#include <asm/io.h>
-#include <asm/irq.h>
/**
* ide_setup_pci_baseregs - place a PCI IDE controller native
@@ -87,7 +82,7 @@ unsigned long ide_pci_dma_base(ide_hwif_t *hwif, const struct ide_port_info *d)
unsigned long dma_base = 0;
u8 dma_stat = 0;
- if (hwif->mmio)
+ if (hwif->host_flags & IDE_HFLAG_MMIO)
return hwif->dma_base;
if (hwif->mate && hwif->mate->dma_base) {
@@ -319,25 +314,22 @@ static ide_hwif_t *ide_hwif_configure(struct pci_dev *dev,
ctl = pci_resource_start(dev, 2*port+1);
base = pci_resource_start(dev, 2*port);
- if ((ctl && !base) || (base && !ctl)) {
- printk(KERN_ERR "%s: inconsistent baseregs (BIOS) "
- "for port %d, skipping\n", d->name, port);
- return NULL;
- }
- }
- if (!ctl) {
+ } else {
/* Use default values */
ctl = port ? 0x374 : 0x3f4;
base = port ? 0x170 : 0x1f0;
}
- hwif = ide_find_port_slot(d);
- if (hwif == NULL) {
- printk(KERN_ERR "%s: too many IDE interfaces, no room in "
- "table\n", d->name);
+ if (!base || !ctl) {
+ printk(KERN_ERR "%s: bad PCI BARs for port %d, skipping\n",
+ d->name, port);
return NULL;
}
+ hwif = ide_find_port_slot(d);
+ if (hwif == NULL)
+ return NULL;
+
memset(&hw, 0, sizeof(hw));
hw.irq = irq;
hw.dev = &dev->dev;
@@ -346,8 +338,6 @@ static ide_hwif_t *ide_hwif_configure(struct pci_dev *dev,
ide_init_port_hw(hwif, &hw);
- hwif->dev = &dev->dev;
-
return hwif;
}
@@ -374,7 +364,7 @@ int ide_hwif_setup_dma(ide_hwif_t *hwif, const struct ide_port_info *d)
if (base == 0 || ide_pci_set_master(dev, d->name) < 0)
return -1;
- if (hwif->mmio)
+ if (hwif->host_flags & IDE_HFLAG_MMIO)
printk(KERN_INFO " %s: MMIO-DMA\n", hwif->name);
else
printk(KERN_INFO " %s: BM-DMA at 0x%04lx-0x%04lx\n",
diff --git a/drivers/ieee1394/csr1212.c b/drivers/ieee1394/csr1212.c
index e8122def164d..9f95337139e3 100644
--- a/drivers/ieee1394/csr1212.c
+++ b/drivers/ieee1394/csr1212.c
@@ -1049,6 +1049,24 @@ int csr1212_read(struct csr1212_csr *csr, u32 offset, void *buffer, u32 len)
return -ENOENT;
}
+/*
+ * Apparently there are many different wrong implementations of the CRC
+ * algorithm. We don't fail, we just warn... approximately once per GUID.
+ */
+static void
+csr1212_check_crc(const u32 *buffer, size_t length, u16 crc, __be32 *guid)
+{
+ static u64 last_bad_eui64;
+ u64 eui64 = ((u64)be32_to_cpu(guid[0]) << 32) | be32_to_cpu(guid[1]);
+
+ if (csr1212_crc16(buffer, length) == crc ||
+ csr1212_msft_crc16(buffer, length) == crc ||
+ eui64 == last_bad_eui64)
+ return;
+
+ printk(KERN_DEBUG "ieee1394: config ROM CRC error\n");
+ last_bad_eui64 = eui64;
+}
/* Parse a chunk of data as a Config ROM */
@@ -1092,11 +1110,8 @@ static int csr1212_parse_bus_info_block(struct csr1212_csr *csr)
return ret;
}
- /* Apparently there are many different wrong implementations of the CRC
- * algorithm. We don't fail, we just warn. */
- if ((csr1212_crc16(bi->data, bi->crc_length) != bi->crc) &&
- (csr1212_msft_crc16(bi->data, bi->crc_length) != bi->crc))
- printk(KERN_DEBUG "IEEE 1394 device has ROM CRC error\n");
+ csr1212_check_crc(bi->data, bi->crc_length, bi->crc,
+ &csr->bus_info_data[3]);
cr = CSR1212_MALLOC(sizeof(*cr));
if (!cr)
@@ -1205,11 +1220,8 @@ int csr1212_parse_keyval(struct csr1212_keyval *kv,
&cache->data[bytes_to_quads(kv->offset - cache->offset)];
kvi_len = be16_to_cpu(kvi->length);
- /* Apparently there are many different wrong implementations of the CRC
- * algorithm. We don't fail, we just warn. */
- if ((csr1212_crc16(kvi->data, kvi_len) != kvi->crc) &&
- (csr1212_msft_crc16(kvi->data, kvi_len) != kvi->crc))
- printk(KERN_DEBUG "IEEE 1394 device has ROM CRC error\n");
+ /* GUID is wrong in here in case of extended ROM. We don't care. */
+ csr1212_check_crc(kvi->data, kvi_len, kvi->crc, &cache->data[3]);
switch (kv->key.type) {
case CSR1212_KV_TYPE_DIRECTORY:
diff --git a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c
index 9d19aec5820a..b6eb2cf25914 100644
--- a/drivers/ieee1394/dv1394.c
+++ b/drivers/ieee1394/dv1394.c
@@ -2296,9 +2296,10 @@ static void dv1394_add_host(struct hpsb_host *host)
ohci = (struct ti_ohci *)host->hostdata;
- device_create(hpsb_protocol_class, NULL, MKDEV(
- IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_DV1394 * 16 + (id<<2)),
- "dv1394-%d", id);
+ device_create_drvdata(hpsb_protocol_class, NULL,
+ MKDEV(IEEE1394_MAJOR,
+ IEEE1394_MINOR_BLOCK_DV1394 * 16 + (id<<2)), NULL,
+ "dv1394-%d", id);
dv1394_init(ohci, DV1394_NTSC, MODE_RECEIVE);
dv1394_init(ohci, DV1394_NTSC, MODE_TRANSMIT);
diff --git a/drivers/ieee1394/highlevel.c b/drivers/ieee1394/highlevel.c
index fa2bfec0fca2..918ffc4fc8ac 100644
--- a/drivers/ieee1394/highlevel.c
+++ b/drivers/ieee1394/highlevel.c
@@ -228,10 +228,8 @@ void hpsb_register_highlevel(struct hpsb_highlevel *hl)
{
unsigned long flags;
+ hpsb_init_highlevel(hl);
INIT_LIST_HEAD(&hl->addr_list);
- INIT_LIST_HEAD(&hl->host_info_list);
-
- rwlock_init(&hl->host_info_lock);
down_write(&hl_drivers_sem);
list_add_tail(&hl->hl_list, &hl_drivers);
diff --git a/drivers/ieee1394/highlevel.h b/drivers/ieee1394/highlevel.h
index eb9fe321e09a..bc5d0854c17e 100644
--- a/drivers/ieee1394/highlevel.h
+++ b/drivers/ieee1394/highlevel.h
@@ -2,7 +2,7 @@
#define IEEE1394_HIGHLEVEL_H
#include <linux/list.h>
-#include <linux/spinlock_types.h>
+#include <linux/spinlock.h>
#include <linux/types.h>
struct module;
@@ -103,6 +103,17 @@ int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store,
void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction,
void *data, size_t length);
+/**
+ * hpsb_init_highlevel - initialize a struct hpsb_highlevel
+ *
+ * This is only necessary if hpsb_get_hostinfo_bykey can be called
+ * before hpsb_register_highlevel.
+ */
+static inline void hpsb_init_highlevel(struct hpsb_highlevel *hl)
+{
+ rwlock_init(&hl->host_info_lock);
+ INIT_LIST_HEAD(&hl->host_info_list);
+}
void hpsb_register_highlevel(struct hpsb_highlevel *hl);
void hpsb_unregister_highlevel(struct hpsb_highlevel *hl);
diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c
index 05710c7c1220..994a21e5a0aa 100644
--- a/drivers/ieee1394/nodemgr.c
+++ b/drivers/ieee1394/nodemgr.c
@@ -754,7 +754,8 @@ static void nodemgr_remove_uds(struct node_entry *ne)
*/
mutex_lock(&nodemgr_serialize_remove_uds);
for (;;) {
- dev = class_find_device(&nodemgr_ud_class, ne, __match_ne);
+ dev = class_find_device(&nodemgr_ud_class, NULL, ne,
+ __match_ne);
if (!dev)
break;
ud = container_of(dev, struct unit_directory, unit_dev);
@@ -901,7 +902,8 @@ static struct node_entry *find_entry_by_guid(u64 guid)
struct device *dev;
struct node_entry *ne;
- dev = class_find_device(&nodemgr_ne_class, &guid, __match_ne_guid);
+ dev = class_find_device(&nodemgr_ne_class, NULL, &guid,
+ __match_ne_guid);
if (!dev)
return NULL;
ne = container_of(dev, struct node_entry, node_dev);
@@ -940,7 +942,8 @@ static struct node_entry *find_entry_by_nodeid(struct hpsb_host *host,
param.host = host;
param.nodeid = nodeid;
- dev = class_find_device(&nodemgr_ne_class, &param, __match_ne_nodeid);
+ dev = class_find_device(&nodemgr_ne_class, NULL, &param,
+ __match_ne_nodeid);
if (!dev)
return NULL;
ne = container_of(dev, struct node_entry, node_dev);
@@ -1453,7 +1456,8 @@ static void nodemgr_suspend_ne(struct node_entry *ne)
ne->in_limbo = 1;
WARN_ON(device_create_file(&ne->device, &dev_attr_ne_in_limbo));
- class_for_each_device(&nodemgr_ud_class, ne, __nodemgr_driver_suspend);
+ class_for_each_device(&nodemgr_ud_class, NULL, ne,
+ __nodemgr_driver_suspend);
}
@@ -1462,7 +1466,8 @@ static void nodemgr_resume_ne(struct node_entry *ne)
ne->in_limbo = 0;
device_remove_file(&ne->device, &dev_attr_ne_in_limbo);
- class_for_each_device(&nodemgr_ud_class, ne, __nodemgr_driver_resume);
+ class_for_each_device(&nodemgr_ud_class, NULL, ne,
+ __nodemgr_driver_resume);
HPSB_DEBUG("Node resumed: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid);
}
@@ -1498,7 +1503,8 @@ static int __nodemgr_update_pdrv(struct device *dev, void *data)
static void nodemgr_update_pdrv(struct node_entry *ne)
{
- class_for_each_device(&nodemgr_ud_class, ne, __nodemgr_update_pdrv);
+ class_for_each_device(&nodemgr_ud_class, NULL, ne,
+ __nodemgr_update_pdrv);
}
@@ -1591,7 +1597,8 @@ static void nodemgr_node_probe(struct host_info *hi, int generation)
* while probes are time-consuming. (Well, those probes need some
* improvement...) */
- class_for_each_device(&nodemgr_ne_class, &param, __nodemgr_node_probe);
+ class_for_each_device(&nodemgr_ne_class, NULL, &param,
+ __nodemgr_node_probe);
/* If we had a bus reset while we were scanning the bus, it is
* possible that we did not probe all nodes. In that case, we
@@ -1826,7 +1833,7 @@ int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *))
hip.cb = cb;
hip.data = data;
- error = class_for_each_device(&hpsb_host_class, &hip,
+ error = class_for_each_device(&hpsb_host_class, NULL, &hip,
__nodemgr_for_each_host);
return error;
diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c
index ec2a0adbedb2..6fa9e4a21840 100644
--- a/drivers/ieee1394/raw1394.c
+++ b/drivers/ieee1394/raw1394.c
@@ -2549,8 +2549,8 @@ static int raw1394_mmap(struct file *file, struct vm_area_struct *vma)
}
/* ioctl is only used for rawiso operations */
-static int raw1394_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static long do_raw1394_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
struct file_info *fi = file->private_data;
void __user *argp = (void __user *)arg;
@@ -2656,6 +2656,16 @@ static int raw1394_ioctl(struct inode *inode, struct file *file,
return -EINVAL;
}
+static long raw1394_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ long ret;
+ lock_kernel();
+ ret = do_raw1394_ioctl(file, cmd, arg);
+ unlock_kernel();
+ return ret;
+}
+
#ifdef CONFIG_COMPAT
struct raw1394_iso_packets32 {
__u32 n_packets;
@@ -2690,7 +2700,7 @@ static long raw1394_iso_xmit_recv_packets32(struct file *file, unsigned int cmd,
!copy_from_user(&infos32, &arg->infos, sizeof infos32)) {
infos = compat_ptr(infos32);
if (!copy_to_user(&dst->infos, &infos, sizeof infos))
- err = raw1394_ioctl(NULL, file, cmd, (unsigned long)dst);
+ err = do_raw1394_ioctl(file, cmd, (unsigned long)dst);
}
return err;
}
@@ -2731,7 +2741,7 @@ static long raw1394_compat_ioctl(struct file *file,
case RAW1394_IOC_ISO_GET_STATUS:
case RAW1394_IOC_ISO_SHUTDOWN:
case RAW1394_IOC_ISO_QUEUE_ACTIVITY:
- err = raw1394_ioctl(NULL, file, cmd, arg);
+ err = do_raw1394_ioctl(file, cmd, arg);
break;
/* These request have different format. */
case RAW1394_IOC_ISO_RECV_PACKETS32:
@@ -2984,7 +2994,7 @@ static const struct file_operations raw1394_fops = {
.read = raw1394_read,
.write = raw1394_write,
.mmap = raw1394_mmap,
- .ioctl = raw1394_ioctl,
+ .unlocked_ioctl = raw1394_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = raw1394_compat_ioctl,
#endif
@@ -3000,10 +3010,10 @@ static int __init init_raw1394(void)
hpsb_register_highlevel(&raw1394_highlevel);
if (IS_ERR
- (device_create(
+ (device_create_drvdata(
hpsb_protocol_class, NULL,
MKDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_RAW1394 * 16),
- RAW1394_DEVICE_NAME))) {
+ NULL, RAW1394_DEVICE_NAME))) {
ret = -EFAULT;
goto out_unreg;
}
diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c
index a5ceff287a28..9cbf3154d243 100644
--- a/drivers/ieee1394/sbp2.c
+++ b/drivers/ieee1394/sbp2.c
@@ -186,6 +186,11 @@ MODULE_PARM_DESC(exclusive_login, "Exclusive login to sbp2 device "
* - delay inquiry
* Wait extra SBP2_INQUIRY_DELAY seconds after login before SCSI inquiry.
*
+ * - power condition
+ * Set the power condition field in the START STOP UNIT commands sent by
+ * sd_mod on suspend, resume, and shutdown (if manage_start_stop is on).
+ * Some disks need this to spin down or to resume properly.
+ *
* - override internal blacklist
* Instead of adding to the built-in blacklist, use only the workarounds
* specified in the module load parameter.
@@ -199,6 +204,8 @@ MODULE_PARM_DESC(workarounds, "Work around device bugs (default = 0"
", skip mode page 8 = " __stringify(SBP2_WORKAROUND_MODE_SENSE_8)
", fix capacity = " __stringify(SBP2_WORKAROUND_FIX_CAPACITY)
", delay inquiry = " __stringify(SBP2_WORKAROUND_DELAY_INQUIRY)
+ ", set power condition in start stop unit = "
+ __stringify(SBP2_WORKAROUND_POWER_CONDITION)
", override internal blacklist = " __stringify(SBP2_WORKAROUND_OVERRIDE)
", or a combination)");
@@ -359,18 +366,25 @@ static const struct {
.firmware_revision = 0x002800,
.model_id = 0x001010,
.workarounds = SBP2_WORKAROUND_INQUIRY_36 |
- SBP2_WORKAROUND_MODE_SENSE_8,
+ SBP2_WORKAROUND_MODE_SENSE_8 |
+ SBP2_WORKAROUND_POWER_CONDITION,
},
/* DViCO Momobay FX-3A with TSB42AA9A bridge */ {
.firmware_revision = 0x002800,
.model_id = 0x000000,
- .workarounds = SBP2_WORKAROUND_DELAY_INQUIRY,
+ .workarounds = SBP2_WORKAROUND_DELAY_INQUIRY |
+ SBP2_WORKAROUND_POWER_CONDITION,
},
/* Initio bridges, actually only needed for some older ones */ {
.firmware_revision = 0x000200,
.model_id = SBP2_ROM_VALUE_WILDCARD,
.workarounds = SBP2_WORKAROUND_INQUIRY_36,
},
+ /* PL-3507 bridge with Prolific firmware */ {
+ .firmware_revision = 0x012800,
+ .model_id = SBP2_ROM_VALUE_WILDCARD,
+ .workarounds = SBP2_WORKAROUND_POWER_CONDITION,
+ },
/* Symbios bridge */ {
.firmware_revision = 0xa0b800,
.model_id = SBP2_ROM_VALUE_WILDCARD,
@@ -1995,6 +2009,8 @@ static int sbp2scsi_slave_configure(struct scsi_device *sdev)
sdev->use_10_for_rw = 1;
+ if (sbp2_exclusive_login)
+ sdev->manage_start_stop = 1;
if (sdev->type == TYPE_ROM)
sdev->use_10_for_ms = 1;
if (sdev->type == TYPE_DISK &&
@@ -2002,6 +2018,8 @@ static int sbp2scsi_slave_configure(struct scsi_device *sdev)
sdev->skip_ms_page_8 = 1;
if (lu->workarounds & SBP2_WORKAROUND_FIX_CAPACITY)
sdev->fix_capacity = 1;
+ if (lu->workarounds & SBP2_WORKAROUND_POWER_CONDITION)
+ sdev->start_stop_pwr_cond = 1;
if (lu->workarounds & SBP2_WORKAROUND_128K_MAX_TRANS)
blk_queue_max_sectors(sdev->request_queue, 128 * 1024 / 512);
return 0;
diff --git a/drivers/ieee1394/sbp2.h b/drivers/ieee1394/sbp2.h
index 80d8e097b065..875428bc8d29 100644
--- a/drivers/ieee1394/sbp2.h
+++ b/drivers/ieee1394/sbp2.h
@@ -345,6 +345,7 @@ enum sbp2lu_state_types {
#define SBP2_WORKAROUND_FIX_CAPACITY 0x8
#define SBP2_WORKAROUND_DELAY_INQUIRY 0x10
#define SBP2_INQUIRY_DELAY 12
+#define SBP2_WORKAROUND_POWER_CONDITION 0x20
#define SBP2_WORKAROUND_OVERRIDE 0x100
#endif /* SBP2_H */
diff --git a/drivers/ieee1394/video1394.c b/drivers/ieee1394/video1394.c
index e24772d336e1..25db6e67fa4e 100644
--- a/drivers/ieee1394/video1394.c
+++ b/drivers/ieee1394/video1394.c
@@ -1341,9 +1341,9 @@ static void video1394_add_host (struct hpsb_host *host)
hpsb_set_hostinfo_key(&video1394_highlevel, host, ohci->host->id);
minor = IEEE1394_MINOR_BLOCK_VIDEO1394 * 16 + ohci->host->id;
- device_create(hpsb_protocol_class, NULL,
- MKDEV(IEEE1394_MAJOR, minor),
- "%s-%d", VIDEO1394_DRIVER_NAME, ohci->host->id);
+ device_create_drvdata(hpsb_protocol_class, NULL,
+ MKDEV(IEEE1394_MAJOR, minor), NULL,
+ "%s-%d", VIDEO1394_DRIVER_NAME, ohci->host->id);
}
@@ -1503,6 +1503,8 @@ static int __init video1394_init_module (void)
{
int ret;
+ hpsb_init_highlevel(&video1394_highlevel);
+
cdev_init(&video1394_cdev, &video1394_fops);
video1394_cdev.owner = THIS_MODULE;
ret = cdev_add(&video1394_cdev, IEEE1394_VIDEO1394_DEV, 16);
diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c
index 781ea5950373..e4eb8be3bb0c 100644
--- a/drivers/infiniband/core/addr.c
+++ b/drivers/infiniband/core/addr.c
@@ -4,28 +4,33 @@
* Copyright (c) 1999-2005, Mellanox Technologies, Inc. All rights reserved.
* Copyright (c) 2005 Intel Corporation. All rights reserved.
*
- * This Software is licensed under one of the following licenses:
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
*
- * 1) under the terms of the "Common Public License 1.0" a copy of which is
- * available from the Open Source Initiative, see
- * http://www.opensource.org/licenses/cpl.php.
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
*
- * 2) under the terms of the "The BSD License" a copy of which is
- * available from the Open Source Initiative, see
- * http://www.opensource.org/licenses/bsd-license.php.
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
*
- * 3) under the terms of the "GNU General Public License (GPL) Version 2" a
- * copy of which is available from the Open Source Initiative, see
- * http://www.opensource.org/licenses/gpl-license.php.
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
*
- * Licensee has the right to choose one of the above licenses.
- *
- * Redistributions of source code must retain the above copyright
- * notice and one of the license notices.
- *
- * Redistributions in binary form must reproduce both the above copyright
- * notice, one of the license notices in the documentation
- * and/or other materials provided with the distribution.
+ * 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/mutex.h>
diff --git a/drivers/infiniband/core/agent.h b/drivers/infiniband/core/agent.h
index fb9ed1489f95..6669287009c2 100644
--- a/drivers/infiniband/core/agent.h
+++ b/drivers/infiniband/core/agent.h
@@ -32,8 +32,6 @@
* 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.
- *
- * $Id: agent.h 1389 2004-12-27 22:56:47Z roland $
*/
#ifndef __AGENT_H_
diff --git a/drivers/infiniband/core/cache.c b/drivers/infiniband/core/cache.c
index e85f7013de57..68883565b725 100644
--- a/drivers/infiniband/core/cache.c
+++ b/drivers/infiniband/core/cache.c
@@ -31,8 +31,6 @@
* 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.
- *
- * $Id: cache.c 1349 2004-12-16 21:09:43Z roland $
*/
#include <linux/module.h>
diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c
index a47fe64e5c39..922d35f4fc08 100644
--- a/drivers/infiniband/core/cm.c
+++ b/drivers/infiniband/core/cm.c
@@ -31,8 +31,6 @@
* 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.
- *
- * $Id: cm.c 4311 2005-12-05 18:42:01Z sean.hefty $
*/
#include <linux/completion.h>
@@ -46,6 +44,7 @@
#include <linux/spinlock.h>
#include <linux/sysfs.h>
#include <linux/workqueue.h>
+#include <linux/kdev_t.h>
#include <rdma/ib_cache.h>
#include <rdma/ib_cm.h>
@@ -164,8 +163,8 @@ struct cm_port {
struct cm_device {
struct list_head list;
- struct ib_device *device;
- struct kobject dev_obj;
+ struct ib_device *ib_device;
+ struct device *device;
u8 ack_delay;
struct cm_port *port[0];
};
@@ -341,7 +340,7 @@ static void cm_init_av_for_response(struct cm_port *port, struct ib_wc *wc,
{
av->port = port;
av->pkey_index = wc->pkey_index;
- ib_init_ah_from_wc(port->cm_dev->device, port->port_num, wc,
+ ib_init_ah_from_wc(port->cm_dev->ib_device, port->port_num, wc,
grh, &av->ah_attr);
}
@@ -355,7 +354,7 @@ static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av)
read_lock_irqsave(&cm.device_lock, flags);
list_for_each_entry(cm_dev, &cm.device_list, list) {
- if (!ib_find_cached_gid(cm_dev->device, &path->sgid,
+ if (!ib_find_cached_gid(cm_dev->ib_device, &path->sgid,
&p, NULL)) {
port = cm_dev->port[p-1];
break;
@@ -366,13 +365,13 @@ static int cm_init_av_by_path(struct ib_sa_path_rec *path, struct cm_av *av)
if (!port)
return -EINVAL;
- ret = ib_find_cached_pkey(cm_dev->device, port->port_num,
+ ret = ib_find_cached_pkey(cm_dev->ib_device, port->port_num,
be16_to_cpu(path->pkey), &av->pkey_index);
if (ret)
return ret;
av->port = port;
- ib_init_ah_from_path(cm_dev->device, port->port_num, path,
+ ib_init_ah_from_path(cm_dev->ib_device, port->port_num, path,
&av->ah_attr);
av->timeout = path->packet_life_time + 1;
return 0;
@@ -1517,7 +1516,7 @@ static int cm_req_handler(struct cm_work *work)
req_msg = (struct cm_req_msg *)work->mad_recv_wc->recv_buf.mad;
- cm_id = ib_create_cm_id(work->port->cm_dev->device, NULL, NULL);
+ cm_id = ib_create_cm_id(work->port->cm_dev->ib_device, NULL, NULL);
if (IS_ERR(cm_id))
return PTR_ERR(cm_id);
@@ -1552,7 +1551,7 @@ static int cm_req_handler(struct cm_work *work)
cm_format_paths_from_req(req_msg, &work->path[0], &work->path[1]);
ret = cm_init_av_by_path(&work->path[0], &cm_id_priv->av);
if (ret) {
- ib_get_cached_gid(work->port->cm_dev->device,
+ ib_get_cached_gid(work->port->cm_dev->ib_device,
work->port->port_num, 0, &work->path[0].sgid);
ib_send_cm_rej(cm_id, IB_CM_REJ_INVALID_GID,
&work->path[0].sgid, sizeof work->path[0].sgid,
@@ -2952,7 +2951,7 @@ static int cm_sidr_req_handler(struct cm_work *work)
struct cm_sidr_req_msg *sidr_req_msg;
struct ib_wc *wc;
- cm_id = ib_create_cm_id(work->port->cm_dev->device, NULL, NULL);
+ cm_id = ib_create_cm_id(work->port->cm_dev->ib_device, NULL, NULL);
if (IS_ERR(cm_id))
return PTR_ERR(cm_id);
cm_id_priv = container_of(cm_id, struct cm_id_private, id);
@@ -3580,7 +3579,7 @@ static void cm_get_ack_delay(struct cm_device *cm_dev)
{
struct ib_device_attr attr;
- if (ib_query_device(cm_dev->device, &attr))
+ if (ib_query_device(cm_dev->ib_device, &attr))
cm_dev->ack_delay = 0; /* acks will rely on packet life time */
else
cm_dev->ack_delay = attr.local_ca_ack_delay;
@@ -3620,18 +3619,6 @@ static struct kobj_type cm_port_obj_type = {
.release = cm_release_port_obj
};
-static void cm_release_dev_obj(struct kobject *obj)
-{
- struct cm_device *cm_dev;
-
- cm_dev = container_of(obj, struct cm_device, dev_obj);
- kfree(cm_dev);
-}
-
-static struct kobj_type cm_dev_obj_type = {
- .release = cm_release_dev_obj
-};
-
struct class cm_class = {
.name = "infiniband_cm",
};
@@ -3642,7 +3629,7 @@ static int cm_create_port_fs(struct cm_port *port)
int i, ret;
ret = kobject_init_and_add(&port->port_obj, &cm_port_obj_type,
- &port->cm_dev->dev_obj,
+ &port->cm_dev->device->kobj,
"%d", port->port_num);
if (ret) {
kfree(port);
@@ -3678,7 +3665,7 @@ static void cm_remove_port_fs(struct cm_port *port)
kobject_put(&port->port_obj);
}
-static void cm_add_one(struct ib_device *device)
+static void cm_add_one(struct ib_device *ib_device)
{
struct cm_device *cm_dev;
struct cm_port *port;
@@ -3693,26 +3680,27 @@ static void cm_add_one(struct ib_device *device)
int ret;
u8 i;
- if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB)
+ if (rdma_node_get_transport(ib_device->node_type) != RDMA_TRANSPORT_IB)
return;
cm_dev = kzalloc(sizeof(*cm_dev) + sizeof(*port) *
- device->phys_port_cnt, GFP_KERNEL);
+ ib_device->phys_port_cnt, GFP_KERNEL);
if (!cm_dev)
return;
- cm_dev->device = device;
+ cm_dev->ib_device = ib_device;
cm_get_ack_delay(cm_dev);
- ret = kobject_init_and_add(&cm_dev->dev_obj, &cm_dev_obj_type,
- &cm_class.subsys.kobj, "%s", device->name);
- if (ret) {
+ cm_dev->device = device_create_drvdata(&cm_class, &ib_device->dev,
+ MKDEV(0, 0), NULL,
+ "%s", ib_device->name);
+ if (!cm_dev->device) {
kfree(cm_dev);
return;
}
set_bit(IB_MGMT_METHOD_SEND, reg_req.method_mask);
- for (i = 1; i <= device->phys_port_cnt; i++) {
+ for (i = 1; i <= ib_device->phys_port_cnt; i++) {
port = kzalloc(sizeof *port, GFP_KERNEL);
if (!port)
goto error1;
@@ -3725,7 +3713,7 @@ static void cm_add_one(struct ib_device *device)
if (ret)
goto error1;
- port->mad_agent = ib_register_mad_agent(device, i,
+ port->mad_agent = ib_register_mad_agent(ib_device, i,
IB_QPT_GSI,
&reg_req,
0,
@@ -3735,11 +3723,11 @@ static void cm_add_one(struct ib_device *device)
if (IS_ERR(port->mad_agent))
goto error2;
- ret = ib_modify_port(device, i, 0, &port_modify);
+ ret = ib_modify_port(ib_device, i, 0, &port_modify);
if (ret)
goto error3;
}
- ib_set_client_data(device, &cm_client, cm_dev);
+ ib_set_client_data(ib_device, &cm_client, cm_dev);
write_lock_irqsave(&cm.device_lock, flags);
list_add_tail(&cm_dev->list, &cm.device_list);
@@ -3755,14 +3743,14 @@ error1:
port_modify.clr_port_cap_mask = IB_PORT_CM_SUP;
while (--i) {
port = cm_dev->port[i-1];
- ib_modify_port(device, port->port_num, 0, &port_modify);
+ ib_modify_port(ib_device, port->port_num, 0, &port_modify);
ib_unregister_mad_agent(port->mad_agent);
cm_remove_port_fs(port);
}
- kobject_put(&cm_dev->dev_obj);
+ device_unregister(cm_dev->device);
}
-static void cm_remove_one(struct ib_device *device)
+static void cm_remove_one(struct ib_device *ib_device)
{
struct cm_device *cm_dev;
struct cm_port *port;
@@ -3772,7 +3760,7 @@ static void cm_remove_one(struct ib_device *device)
unsigned long flags;
int i;
- cm_dev = ib_get_client_data(device, &cm_client);
+ cm_dev = ib_get_client_data(ib_device, &cm_client);
if (!cm_dev)
return;
@@ -3780,14 +3768,14 @@ static void cm_remove_one(struct ib_device *device)
list_del(&cm_dev->list);
write_unlock_irqrestore(&cm.device_lock, flags);
- for (i = 1; i <= device->phys_port_cnt; i++) {
+ for (i = 1; i <= ib_device->phys_port_cnt; i++) {
port = cm_dev->port[i-1];
- ib_modify_port(device, port->port_num, 0, &port_modify);
+ ib_modify_port(ib_device, port->port_num, 0, &port_modify);
ib_unregister_mad_agent(port->mad_agent);
flush_workqueue(cm.wq);
cm_remove_port_fs(port);
}
- kobject_put(&cm_dev->dev_obj);
+ device_unregister(cm_dev->device);
}
static int __init ib_cm_init(void)
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 671f13738054..e5bd6172a1f6 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -4,29 +4,33 @@
* Copyright (c) 1999-2005, Mellanox Technologies, Inc. All rights reserved.
* Copyright (c) 2005-2006 Intel Corporation. All rights reserved.
*
- * This Software is licensed under one of the following licenses:
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
*
- * 1) under the terms of the "Common Public License 1.0" a copy of which is
- * available from the Open Source Initiative, see
- * http://www.opensource.org/licenses/cpl.php.
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
*
- * 2) under the terms of the "The BSD License" a copy of which is
- * available from the Open Source Initiative, see
- * http://www.opensource.org/licenses/bsd-license.php.
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
*
- * 3) under the terms of the "GNU General Public License (GPL) Version 2" a
- * copy of which is available from the Open Source Initiative, see
- * http://www.opensource.org/licenses/gpl-license.php.
- *
- * Licensee has the right to choose one of the above licenses.
- *
- * Redistributions of source code must retain the above copyright
- * notice and one of the license notices.
- *
- * Redistributions in binary form must reproduce both the above copyright
- * notice, one of the license notices in the documentation
- * and/or other materials provided with the distribution.
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
*
+ * 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/completion.h>
diff --git a/drivers/infiniband/core/core_priv.h b/drivers/infiniband/core/core_priv.h
index 7ad47a4b166b..05ac36e6acdb 100644
--- a/drivers/infiniband/core/core_priv.h
+++ b/drivers/infiniband/core/core_priv.h
@@ -28,8 +28,6 @@
* 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.
- *
- * $Id: core_priv.h 1349 2004-12-16 21:09:43Z roland $
*/
#ifndef _CORE_PRIV_H
diff --git a/drivers/infiniband/core/device.c b/drivers/infiniband/core/device.c
index 5ac5ffee05cb..7913b804311e 100644
--- a/drivers/infiniband/core/device.c
+++ b/drivers/infiniband/core/device.c
@@ -29,8 +29,6 @@
* 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.
- *
- * $Id: device.c 1349 2004-12-16 21:09:43Z roland $
*/
#include <linux/module.h>
diff --git a/drivers/infiniband/core/fmr_pool.c b/drivers/infiniband/core/fmr_pool.c
index 1286dc1b98b2..4507043d24c8 100644
--- a/drivers/infiniband/core/fmr_pool.c
+++ b/drivers/infiniband/core/fmr_pool.c
@@ -29,8 +29,6 @@
* 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.
- *
- * $Id: fmr_pool.c 2730 2005-06-28 16:43:03Z sean.hefty $
*/
#include <linux/errno.h>
diff --git a/drivers/infiniband/core/mad_priv.h b/drivers/infiniband/core/mad_priv.h
index 8b75010016ec..05ce331733b0 100644
--- a/drivers/infiniband/core/mad_priv.h
+++ b/drivers/infiniband/core/mad_priv.h
@@ -30,8 +30,6 @@
* 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.
- *
- * $Id: mad_priv.h 5596 2006-03-03 01:00:07Z sean.hefty $
*/
#ifndef __IB_MAD_PRIV_H__
diff --git a/drivers/infiniband/core/mad_rmpp.c b/drivers/infiniband/core/mad_rmpp.c
index a5e2a310f312..d0ef7d61c037 100644
--- a/drivers/infiniband/core/mad_rmpp.c
+++ b/drivers/infiniband/core/mad_rmpp.c
@@ -29,8 +29,6 @@
* 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.
- *
- * $Id: mad_rmpp.c 1921 2005-03-02 22:58:44Z sean.hefty $
*/
#include "mad_priv.h"
diff --git a/drivers/infiniband/core/mad_rmpp.h b/drivers/infiniband/core/mad_rmpp.h
index f0616fd22494..3d336bff1148 100644
--- a/drivers/infiniband/core/mad_rmpp.h
+++ b/drivers/infiniband/core/mad_rmpp.h
@@ -28,8 +28,6 @@
* 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.
- *
- * $Id: mad_rmpp.h 1921 2005-02-25 22:58:44Z sean.hefty $
*/
#ifndef __MAD_RMPP_H__
diff --git a/drivers/infiniband/core/packer.c b/drivers/infiniband/core/packer.c
index c972d7235764..019bd4b0863e 100644
--- a/drivers/infiniband/core/packer.c
+++ b/drivers/infiniband/core/packer.c
@@ -29,8 +29,6 @@
* 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.
- *
- * $Id: packer.c 1349 2004-12-16 21:09:43Z roland $
*/
#include <linux/string.h>
diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c
index cf474ec27070..1341de793e51 100644
--- a/drivers/infiniband/core/sa_query.c
+++ b/drivers/infiniband/core/sa_query.c
@@ -30,8 +30,6 @@
* 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.
- *
- * $Id: sa_query.c 2811 2005-07-06 18:11:43Z halr $
*/
#include <linux/module.h>
@@ -361,7 +359,7 @@ static void update_sm_ah(struct work_struct *work)
{
struct ib_sa_port *port =
container_of(work, struct ib_sa_port, update_task);
- struct ib_sa_sm_ah *new_ah, *old_ah;
+ struct ib_sa_sm_ah *new_ah;
struct ib_port_attr port_attr;
struct ib_ah_attr ah_attr;
@@ -397,12 +395,9 @@ static void update_sm_ah(struct work_struct *work)
}
spin_lock_irq(&port->ah_lock);
- old_ah = port->sm_ah;
port->sm_ah = new_ah;
spin_unlock_irq(&port->ah_lock);
- if (old_ah)
- kref_put(&old_ah->ref, free_sm_ah);
}
static void ib_sa_event(struct ib_event_handler *handler, struct ib_event *event)
@@ -413,8 +408,17 @@ static void ib_sa_event(struct ib_event_handler *handler, struct ib_event *event
event->event == IB_EVENT_PKEY_CHANGE ||
event->event == IB_EVENT_SM_CHANGE ||
event->event == IB_EVENT_CLIENT_REREGISTER) {
- struct ib_sa_device *sa_dev;
- sa_dev = container_of(handler, typeof(*sa_dev), event_handler);
+ unsigned long flags;
+ struct ib_sa_device *sa_dev =
+ container_of(handler, typeof(*sa_dev), event_handler);
+ struct ib_sa_port *port =
+ &sa_dev->port[event->element.port_num - sa_dev->start_port];
+
+ spin_lock_irqsave(&port->ah_lock, flags);
+ if (port->sm_ah)
+ kref_put(&port->sm_ah->ref, free_sm_ah);
+ port->sm_ah = NULL;
+ spin_unlock_irqrestore(&port->ah_lock, flags);
schedule_work(&sa_dev->port[event->element.port_num -
sa_dev->start_port].update_task);
@@ -519,6 +523,10 @@ static int alloc_mad(struct ib_sa_query *query, gfp_t gfp_mask)
unsigned long flags;
spin_lock_irqsave(&query->port->ah_lock, flags);
+ if (!query->port->sm_ah) {
+ spin_unlock_irqrestore(&query->port->ah_lock, flags);
+ return -EAGAIN;
+ }
kref_get(&query->port->sm_ah->ref);
query->sm_ah = query->port->sm_ah;
spin_unlock_irqrestore(&query->port->ah_lock, flags);
diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c
index 95756551cf7c..36a0ef97c6a5 100644
--- a/drivers/infiniband/core/sysfs.c
+++ b/drivers/infiniband/core/sysfs.c
@@ -30,8 +30,6 @@
* 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.
- *
- * $Id: sysfs.c 1349 2004-12-16 21:09:43Z roland $
*/
#include "core_priv.h"
diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c
index d7a6881b571d..54fc1de94959 100644
--- a/drivers/infiniband/core/ucm.c
+++ b/drivers/infiniband/core/ucm.c
@@ -29,8 +29,6 @@
* 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.
- *
- * $Id: ucm.c 4311 2005-12-05 18:42:01Z sean.hefty $
*/
#include <linux/completion.h>
diff --git a/drivers/infiniband/core/ud_header.c b/drivers/infiniband/core/ud_header.c
index 997c07db6d8f..8ec7876bedcf 100644
--- a/drivers/infiniband/core/ud_header.c
+++ b/drivers/infiniband/core/ud_header.c
@@ -29,8 +29,6 @@
* 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.
- *
- * $Id: ud_header.c 1349 2004-12-16 21:09:43Z roland $
*/
#include <linux/errno.h>
diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c
index a1768dbb0720..6f7c096abf13 100644
--- a/drivers/infiniband/core/umem.c
+++ b/drivers/infiniband/core/umem.c
@@ -30,8 +30,6 @@
* 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.
- *
- * $Id: uverbs_mem.c 2743 2005-06-28 22:27:59Z roland $
*/
#include <linux/mm.h>
diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c
index 840ede9ae965..eb58fcf843ac 100644
--- a/drivers/infiniband/core/user_mad.c
+++ b/drivers/infiniband/core/user_mad.c
@@ -31,8 +31,6 @@
* 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.
- *
- * $Id: user_mad.c 5596 2006-03-03 01:00:07Z sean.hefty $
*/
#include <linux/module.h>
diff --git a/drivers/infiniband/core/uverbs.h b/drivers/infiniband/core/uverbs.h
index 376a57ce1b40..b3ea9587dc80 100644
--- a/drivers/infiniband/core/uverbs.h
+++ b/drivers/infiniband/core/uverbs.h
@@ -32,8 +32,6 @@
* 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.
- *
- * $Id: uverbs.h 2559 2005-06-06 19:43:16Z roland $
*/
#ifndef UVERBS_H
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index 2c3bff5fe867..56feab6c251e 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -31,8 +31,6 @@
* 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.
- *
- * $Id: uverbs_cmd.c 2708 2005-06-24 17:27:21Z roland $
*/
#include <linux/file.h>
@@ -919,7 +917,7 @@ ssize_t ib_uverbs_poll_cq(struct ib_uverbs_file *file,
resp->wc[i].opcode = wc[i].opcode;
resp->wc[i].vendor_err = wc[i].vendor_err;
resp->wc[i].byte_len = wc[i].byte_len;
- resp->wc[i].imm_data = (__u32 __force) wc[i].imm_data;
+ resp->wc[i].ex.imm_data = (__u32 __force) wc[i].ex.imm_data;
resp->wc[i].qp_num = wc[i].qp->qp_num;
resp->wc[i].src_qp = wc[i].src_qp;
resp->wc[i].wc_flags = wc[i].wc_flags;
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index f806da184b51..9bc07f4fa844 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -32,8 +32,6 @@
* 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.
- *
- * $Id: uverbs_main.c 2733 2005-06-28 19:14:34Z roland $
*/
#include <linux/module.h>
diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c
index 05042089de6e..e0fbe5975866 100644
--- a/drivers/infiniband/core/verbs.c
+++ b/drivers/infiniband/core/verbs.c
@@ -34,8 +34,6 @@
* 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.
- *
- * $Id: verbs.c 1349 2004-12-16 21:09:43Z roland $
*/
#include <linux/errno.h>
@@ -755,6 +753,52 @@ int ib_dereg_mr(struct ib_mr *mr)
}
EXPORT_SYMBOL(ib_dereg_mr);
+struct ib_mr *ib_alloc_fast_reg_mr(struct ib_pd *pd, int max_page_list_len)
+{
+ struct ib_mr *mr;
+
+ if (!pd->device->alloc_fast_reg_mr)
+ return ERR_PTR(-ENOSYS);
+
+ mr = pd->device->alloc_fast_reg_mr(pd, max_page_list_len);
+
+ if (!IS_ERR(mr)) {
+ mr->device = pd->device;
+ mr->pd = pd;
+ mr->uobject = NULL;
+ atomic_inc(&pd->usecnt);
+ atomic_set(&mr->usecnt, 0);
+ }
+
+ return mr;
+}
+EXPORT_SYMBOL(ib_alloc_fast_reg_mr);
+
+struct ib_fast_reg_page_list *ib_alloc_fast_reg_page_list(struct ib_device *device,
+ int max_page_list_len)
+{
+ struct ib_fast_reg_page_list *page_list;
+
+ if (!device->alloc_fast_reg_page_list)
+ return ERR_PTR(-ENOSYS);
+
+ page_list = device->alloc_fast_reg_page_list(device, max_page_list_len);
+
+ if (!IS_ERR(page_list)) {
+ page_list->device = device;
+ page_list->max_page_list_len = max_page_list_len;
+ }
+
+ return page_list;
+}
+EXPORT_SYMBOL(ib_alloc_fast_reg_page_list);
+
+void ib_free_fast_reg_page_list(struct ib_fast_reg_page_list *page_list)
+{
+ page_list->device->free_fast_reg_page_list(page_list);
+}
+EXPORT_SYMBOL(ib_free_fast_reg_page_list);
+
/* Memory windows */
struct ib_mw *ib_alloc_mw(struct ib_pd *pd)
diff --git a/drivers/infiniband/hw/ehca/ehca_irq.c b/drivers/infiniband/hw/ehca/ehca_irq.c
index ce1ab0571be3..43180b952c1f 100644
--- a/drivers/infiniband/hw/ehca/ehca_irq.c
+++ b/drivers/infiniband/hw/ehca/ehca_irq.c
@@ -641,8 +641,8 @@ static inline int find_next_online_cpu(struct ehca_comp_pool *pool)
ehca_dmp(&cpu_online_map, sizeof(cpumask_t), "");
spin_lock_irqsave(&pool->last_cpu_lock, flags);
- cpu = next_cpu(pool->last_cpu, cpu_online_map);
- if (cpu == NR_CPUS)
+ cpu = next_cpu_nr(pool->last_cpu, cpu_online_map);
+ if (cpu >= nr_cpu_ids)
cpu = first_cpu(cpu_online_map);
pool->last_cpu = cpu;
spin_unlock_irqrestore(&pool->last_cpu_lock, flags);
diff --git a/drivers/infiniband/hw/ehca/ehca_reqs.c b/drivers/infiniband/hw/ehca/ehca_reqs.c
index f093b0033daf..b799b2710210 100644
--- a/drivers/infiniband/hw/ehca/ehca_reqs.c
+++ b/drivers/infiniband/hw/ehca/ehca_reqs.c
@@ -681,7 +681,7 @@ poll_cq_one_read_cqe:
wc->dlid_path_bits = cqe->dlid;
wc->src_qp = cqe->remote_qp_number;
wc->wc_flags = cqe->w_completion_flags;
- wc->imm_data = cpu_to_be32(cqe->immediate_data);
+ wc->ex.imm_data = cpu_to_be32(cqe->immediate_data);
wc->sl = cqe->service_level;
poll_cq_one_exit0:
diff --git a/drivers/infiniband/hw/ipath/ipath_cq.c b/drivers/infiniband/hw/ipath/ipath_cq.c
index a03bd28d9b48..d385e4168c97 100644
--- a/drivers/infiniband/hw/ipath/ipath_cq.c
+++ b/drivers/infiniband/hw/ipath/ipath_cq.c
@@ -82,7 +82,7 @@ void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int solicited)
wc->uqueue[head].opcode = entry->opcode;
wc->uqueue[head].vendor_err = entry->vendor_err;
wc->uqueue[head].byte_len = entry->byte_len;
- wc->uqueue[head].imm_data = (__u32 __force)entry->imm_data;
+ wc->uqueue[head].ex.imm_data = (__u32 __force) entry->ex.imm_data;
wc->uqueue[head].qp_num = entry->qp->qp_num;
wc->uqueue[head].src_qp = entry->src_qp;
wc->uqueue[head].wc_flags = entry->wc_flags;
diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c
index b472b15637f0..edaf667617df 100644
--- a/drivers/infiniband/hw/ipath/ipath_file_ops.c
+++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c
@@ -2453,7 +2453,7 @@ static int init_cdev(int minor, char *name, const struct file_operations *fops,
goto err_cdev;
}
- device = device_create(ipath_class, NULL, dev, name);
+ device = device_create_drvdata(ipath_class, NULL, dev, NULL, name);
if (IS_ERR(device)) {
ret = PTR_ERR(device);
diff --git a/drivers/infiniband/hw/ipath/ipath_iba7220.c b/drivers/infiniband/hw/ipath/ipath_iba7220.c
index 8eee7830f042..fb70712ac85c 100644
--- a/drivers/infiniband/hw/ipath/ipath_iba7220.c
+++ b/drivers/infiniband/hw/ipath/ipath_iba7220.c
@@ -2228,8 +2228,8 @@ static void ipath_autoneg_send(struct ipath_devdata *dd, int which)
0xffffffff, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x40000001, 0x1388, 0x15e, /* rest 0's */
};
- dcnt = sizeof(madpayload_start)/sizeof(madpayload_start[0]);
- hcnt = sizeof(hdr)/sizeof(hdr[0]);
+ dcnt = ARRAY_SIZE(madpayload_start);
+ hcnt = ARRAY_SIZE(hdr);
if (!swapped) {
/* for maintainability, do it at runtime */
for (i = 0; i < hcnt; i++) {
diff --git a/drivers/infiniband/hw/ipath/ipath_rc.c b/drivers/infiniband/hw/ipath/ipath_rc.c
index 108df667d2ee..97710522624d 100644
--- a/drivers/infiniband/hw/ipath/ipath_rc.c
+++ b/drivers/infiniband/hw/ipath/ipath_rc.c
@@ -1703,11 +1703,11 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
case OP(SEND_LAST_WITH_IMMEDIATE):
send_last_imm:
if (header_in_data) {
- wc.imm_data = *(__be32 *) data;
+ wc.ex.imm_data = *(__be32 *) data;
data += sizeof(__be32);
} else {
/* Immediate data comes after BTH */
- wc.imm_data = ohdr->u.imm_data;
+ wc.ex.imm_data = ohdr->u.imm_data;
}
hdrsize += 4;
wc.wc_flags = IB_WC_WITH_IMM;
diff --git a/drivers/infiniband/hw/ipath/ipath_ruc.c b/drivers/infiniband/hw/ipath/ipath_ruc.c
index a4b5521567fe..af051f757663 100644
--- a/drivers/infiniband/hw/ipath/ipath_ruc.c
+++ b/drivers/infiniband/hw/ipath/ipath_ruc.c
@@ -331,7 +331,7 @@ again:
switch (wqe->wr.opcode) {
case IB_WR_SEND_WITH_IMM:
wc.wc_flags = IB_WC_WITH_IMM;
- wc.imm_data = wqe->wr.ex.imm_data;
+ wc.ex.imm_data = wqe->wr.ex.imm_data;
/* FALLTHROUGH */
case IB_WR_SEND:
if (!ipath_get_rwqe(qp, 0))
@@ -342,7 +342,7 @@ again:
if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_WRITE)))
goto inv_err;
wc.wc_flags = IB_WC_WITH_IMM;
- wc.imm_data = wqe->wr.ex.imm_data;
+ wc.ex.imm_data = wqe->wr.ex.imm_data;
if (!ipath_get_rwqe(qp, 1))
goto rnr_nak;
/* FALLTHROUGH */
diff --git a/drivers/infiniband/hw/ipath/ipath_uc.c b/drivers/infiniband/hw/ipath/ipath_uc.c
index 0596ec16fcbd..82cc588b8bf2 100644
--- a/drivers/infiniband/hw/ipath/ipath_uc.c
+++ b/drivers/infiniband/hw/ipath/ipath_uc.c
@@ -379,11 +379,11 @@ void ipath_uc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
case OP(SEND_LAST_WITH_IMMEDIATE):
send_last_imm:
if (header_in_data) {
- wc.imm_data = *(__be32 *) data;
+ wc.ex.imm_data = *(__be32 *) data;
data += sizeof(__be32);
} else {
/* Immediate data comes after BTH */
- wc.imm_data = ohdr->u.imm_data;
+ wc.ex.imm_data = ohdr->u.imm_data;
}
hdrsize += 4;
wc.wc_flags = IB_WC_WITH_IMM;
@@ -483,11 +483,11 @@ void ipath_uc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
case OP(RDMA_WRITE_LAST_WITH_IMMEDIATE):
rdma_last_imm:
if (header_in_data) {
- wc.imm_data = *(__be32 *) data;
+ wc.ex.imm_data = *(__be32 *) data;
data += sizeof(__be32);
} else {
/* Immediate data comes after BTH */
- wc.imm_data = ohdr->u.imm_data;
+ wc.ex.imm_data = ohdr->u.imm_data;
}
hdrsize += 4;
wc.wc_flags = IB_WC_WITH_IMM;
diff --git a/drivers/infiniband/hw/ipath/ipath_ud.c b/drivers/infiniband/hw/ipath/ipath_ud.c
index 77ca8ca74e78..36aa242c487c 100644
--- a/drivers/infiniband/hw/ipath/ipath_ud.c
+++ b/drivers/infiniband/hw/ipath/ipath_ud.c
@@ -96,7 +96,7 @@ static void ipath_ud_loopback(struct ipath_qp *sqp, struct ipath_swqe *swqe)
if (swqe->wr.opcode == IB_WR_SEND_WITH_IMM) {
wc.wc_flags = IB_WC_WITH_IMM;
- wc.imm_data = swqe->wr.ex.imm_data;
+ wc.ex.imm_data = swqe->wr.ex.imm_data;
}
/*
@@ -492,14 +492,14 @@ void ipath_ud_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
if (qp->ibqp.qp_num > 1 &&
opcode == IB_OPCODE_UD_SEND_ONLY_WITH_IMMEDIATE) {
if (header_in_data) {
- wc.imm_data = *(__be32 *) data;
+ wc.ex.imm_data = *(__be32 *) data;
data += sizeof(__be32);
} else
- wc.imm_data = ohdr->u.ud.imm_data;
+ wc.ex.imm_data = ohdr->u.ud.imm_data;
wc.wc_flags = IB_WC_WITH_IMM;
hdrsize += sizeof(u32);
} else if (opcode == IB_OPCODE_UD_SEND_ONLY) {
- wc.imm_data = 0;
+ wc.ex.imm_data = 0;
wc.wc_flags = 0;
} else {
dev->n_pkt_drops++;
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.c b/drivers/infiniband/hw/ipath/ipath_verbs.c
index 7779165b2c2c..38c44dec51ca 100644
--- a/drivers/infiniband/hw/ipath/ipath_verbs.c
+++ b/drivers/infiniband/hw/ipath/ipath_verbs.c
@@ -35,6 +35,7 @@
#include <rdma/ib_user_verbs.h>
#include <linux/io.h>
#include <linux/utsname.h>
+#include <linux/rculist.h>
#include "ipath_kernel.h"
#include "ipath_verbs.h"
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c b/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c
index 9e5abf9c309d..d73e32232879 100644
--- a/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c
+++ b/drivers/infiniband/hw/ipath/ipath_verbs_mcast.c
@@ -31,8 +31,7 @@
* SOFTWARE.
*/
-#include <linux/list.h>
-#include <linux/rcupdate.h>
+#include <linux/rculist.h>
#include "ipath_verbs.h"
diff --git a/drivers/infiniband/hw/mlx4/cq.c b/drivers/infiniband/hw/mlx4/cq.c
index 4521319b1406..299f20832ab6 100644
--- a/drivers/infiniband/hw/mlx4/cq.c
+++ b/drivers/infiniband/hw/mlx4/cq.c
@@ -663,18 +663,18 @@ repoll:
switch (cqe->owner_sr_opcode & MLX4_CQE_OPCODE_MASK) {
case MLX4_RECV_OPCODE_RDMA_WRITE_IMM:
- wc->opcode = IB_WC_RECV_RDMA_WITH_IMM;
- wc->wc_flags = IB_WC_WITH_IMM;
- wc->imm_data = cqe->immed_rss_invalid;
+ wc->opcode = IB_WC_RECV_RDMA_WITH_IMM;
+ wc->wc_flags = IB_WC_WITH_IMM;
+ wc->ex.imm_data = cqe->immed_rss_invalid;
break;
case MLX4_RECV_OPCODE_SEND:
wc->opcode = IB_WC_RECV;
wc->wc_flags = 0;
break;
case MLX4_RECV_OPCODE_SEND_IMM:
- wc->opcode = IB_WC_RECV;
- wc->wc_flags = IB_WC_WITH_IMM;
- wc->imm_data = cqe->immed_rss_invalid;
+ wc->opcode = IB_WC_RECV;
+ wc->wc_flags = IB_WC_WITH_IMM;
+ wc->ex.imm_data = cqe->immed_rss_invalid;
break;
}
diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c
index a80df22deae8..4b0ac5d68c46 100644
--- a/drivers/infiniband/hw/mlx4/qp.c
+++ b/drivers/infiniband/hw/mlx4/qp.c
@@ -129,9 +129,10 @@ static void stamp_send_wqe(struct mlx4_ib_qp *qp, int n, int size)
int ind;
void *buf;
__be32 stamp;
+ struct mlx4_wqe_ctrl_seg *ctrl;
- s = roundup(size, 1U << qp->sq.wqe_shift);
if (qp->sq_max_wqes_per_wr > 1) {
+ s = roundup(size, 1U << qp->sq.wqe_shift);
for (i = 0; i < s; i += 64) {
ind = (i >> qp->sq.wqe_shift) + n;
stamp = ind & qp->sq.wqe_cnt ? cpu_to_be32(0x7fffffff) :
@@ -141,7 +142,8 @@ static void stamp_send_wqe(struct mlx4_ib_qp *qp, int n, int size)
*wqe = stamp;
}
} else {
- buf = get_send_wqe(qp, n & (qp->sq.wqe_cnt - 1));
+ ctrl = buf = get_send_wqe(qp, n & (qp->sq.wqe_cnt - 1));
+ s = (ctrl->fence_size & 0x3f) << 4;
for (i = 64; i < s; i += 64) {
wqe = buf + i;
*wqe = cpu_to_be32(0xffffffff);
@@ -1063,6 +1065,8 @@ static int __mlx4_ib_modify_qp(struct ib_qp *ibqp,
for (i = 0; i < qp->sq.wqe_cnt; ++i) {
ctrl = get_send_wqe(qp, i);
ctrl->owner_opcode = cpu_to_be32(1 << 31);
+ if (qp->sq_max_wqes_per_wr == 1)
+ ctrl->fence_size = 1 << (qp->sq.wqe_shift - 4);
stamp_send_wqe(qp, i, 1 << qp->sq.wqe_shift);
}
diff --git a/drivers/infiniband/hw/mthca/mthca_allocator.c b/drivers/infiniband/hw/mthca/mthca_allocator.c
index a76306709618..c5ccc2daab60 100644
--- a/drivers/infiniband/hw/mthca/mthca_allocator.c
+++ b/drivers/infiniband/hw/mthca/mthca_allocator.c
@@ -28,8 +28,6 @@
* 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.
- *
- * $Id: mthca_allocator.c 1349 2004-12-16 21:09:43Z roland $
*/
#include <linux/errno.h>
diff --git a/drivers/infiniband/hw/mthca/mthca_av.c b/drivers/infiniband/hw/mthca/mthca_av.c
index 4b111a852ff6..32f6c6315454 100644
--- a/drivers/infiniband/hw/mthca/mthca_av.c
+++ b/drivers/infiniband/hw/mthca/mthca_av.c
@@ -29,8 +29,6 @@
* 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.
- *
- * $Id: mthca_av.c 1349 2004-12-16 21:09:43Z roland $
*/
#include <linux/string.h>
diff --git a/drivers/infiniband/hw/mthca/mthca_catas.c b/drivers/infiniband/hw/mthca/mthca_catas.c
index e948158a28d9..40573e48439c 100644
--- a/drivers/infiniband/hw/mthca/mthca_catas.c
+++ b/drivers/infiniband/hw/mthca/mthca_catas.c
@@ -28,8 +28,6 @@
* 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.
- *
- * $Id$
*/
#include <linux/jiffies.h>
diff --git a/drivers/infiniband/hw/mthca/mthca_cmd.c b/drivers/infiniband/hw/mthca/mthca_cmd.c
index 54d230ee7d63..c33e1c53c799 100644
--- a/drivers/infiniband/hw/mthca/mthca_cmd.c
+++ b/drivers/infiniband/hw/mthca/mthca_cmd.c
@@ -30,8 +30,6 @@
* 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.
- *
- * $Id: mthca_cmd.c 1349 2004-12-16 21:09:43Z roland $
*/
#include <linux/completion.h>
diff --git a/drivers/infiniband/hw/mthca/mthca_cmd.h b/drivers/infiniband/hw/mthca/mthca_cmd.h
index 8928ca4a9325..6efd3265f248 100644
--- a/drivers/infiniband/hw/mthca/mthca_cmd.h
+++ b/drivers/infiniband/hw/mthca/mthca_cmd.h
@@ -30,8 +30,6 @@
* 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.
- *
- * $Id: mthca_cmd.h 1349 2004-12-16 21:09:43Z roland $
*/
#ifndef MTHCA_CMD_H
diff --git a/drivers/infiniband/hw/mthca/mthca_config_reg.h b/drivers/infiniband/hw/mthca/mthca_config_reg.h
index afa56bfaab2e..75671f75cac4 100644
--- a/drivers/infiniband/hw/mthca/mthca_config_reg.h
+++ b/drivers/infiniband/hw/mthca/mthca_config_reg.h
@@ -29,8 +29,6 @@
* 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.
- *
- * $Id: mthca_config_reg.h 1349 2004-12-16 21:09:43Z roland $
*/
#ifndef MTHCA_CONFIG_REG_H
diff --git a/drivers/infiniband/hw/mthca/mthca_cq.c b/drivers/infiniband/hw/mthca/mthca_cq.c
index 20401d2ba6b2..d9f4735c2b37 100644
--- a/drivers/infiniband/hw/mthca/mthca_cq.c
+++ b/drivers/infiniband/hw/mthca/mthca_cq.c
@@ -32,8 +32,6 @@
* 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.
- *
- * $Id: mthca_cq.c 1369 2004-12-20 16:17:07Z roland $
*/
#include <linux/hardirq.h>
@@ -622,13 +620,13 @@ static inline int mthca_poll_one(struct mthca_dev *dev,
case IB_OPCODE_SEND_LAST_WITH_IMMEDIATE:
case IB_OPCODE_SEND_ONLY_WITH_IMMEDIATE:
entry->wc_flags = IB_WC_WITH_IMM;
- entry->imm_data = cqe->imm_etype_pkey_eec;
+ entry->ex.imm_data = cqe->imm_etype_pkey_eec;
entry->opcode = IB_WC_RECV;
break;
case IB_OPCODE_RDMA_WRITE_LAST_WITH_IMMEDIATE:
case IB_OPCODE_RDMA_WRITE_ONLY_WITH_IMMEDIATE:
entry->wc_flags = IB_WC_WITH_IMM;
- entry->imm_data = cqe->imm_etype_pkey_eec;
+ entry->ex.imm_data = cqe->imm_etype_pkey_eec;
entry->opcode = IB_WC_RECV_RDMA_WITH_IMM;
break;
default:
diff --git a/drivers/infiniband/hw/mthca/mthca_dev.h b/drivers/infiniband/hw/mthca/mthca_dev.h
index 7bc32f8e377e..2997d8d564ea 100644
--- a/drivers/infiniband/hw/mthca/mthca_dev.h
+++ b/drivers/infiniband/hw/mthca/mthca_dev.h
@@ -32,8 +32,6 @@
* 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.
- *
- * $Id: mthca_dev.h 1349 2004-12-16 21:09:43Z roland $
*/
#ifndef MTHCA_DEV_H
diff --git a/drivers/infiniband/hw/mthca/mthca_doorbell.h b/drivers/infiniband/hw/mthca/mthca_doorbell.h
index b374dc395be1..14f51ef97d7e 100644
--- a/drivers/infiniband/hw/mthca/mthca_doorbell.h
+++ b/drivers/infiniband/hw/mthca/mthca_doorbell.h
@@ -30,8 +30,6 @@
* 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.
- *
- * $Id: mthca_doorbell.h 1349 2004-12-16 21:09:43Z roland $
*/
#include <linux/types.h>
diff --git a/drivers/infiniband/hw/mthca/mthca_eq.c b/drivers/infiniband/hw/mthca/mthca_eq.c
index 8bde7f98e58a..4e36aa7cb3d2 100644
--- a/drivers/infiniband/hw/mthca/mthca_eq.c
+++ b/drivers/infiniband/hw/mthca/mthca_eq.c
@@ -29,8 +29,6 @@
* 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.
- *
- * $Id: mthca_eq.c 1382 2004-12-24 02:21:02Z roland $
*/
#include <linux/errno.h>
diff --git a/drivers/infiniband/hw/mthca/mthca_mad.c b/drivers/infiniband/hw/mthca/mthca_mad.c
index 8b7e83e6e88f..640449582aba 100644
--- a/drivers/infiniband/hw/mthca/mthca_mad.c
+++ b/drivers/infiniband/hw/mthca/mthca_mad.c
@@ -30,8 +30,6 @@
* 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.
- *
- * $Id: mthca_mad.c 1349 2004-12-16 21:09:43Z roland $
*/
#include <linux/string.h>
diff --git a/drivers/infiniband/hw/mthca/mthca_main.c b/drivers/infiniband/hw/mthca/mthca_main.c
index 200cf13fc9bb..fb9f91b60f30 100644
--- a/drivers/infiniband/hw/mthca/mthca_main.c
+++ b/drivers/infiniband/hw/mthca/mthca_main.c
@@ -30,8 +30,6 @@
* 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.
- *
- * $Id: mthca_main.c 1396 2004-12-28 04:10:27Z roland $
*/
#include <linux/module.h>
diff --git a/drivers/infiniband/hw/mthca/mthca_mcg.c b/drivers/infiniband/hw/mthca/mthca_mcg.c
index a8ad072be074..3f5f94879208 100644
--- a/drivers/infiniband/hw/mthca/mthca_mcg.c
+++ b/drivers/infiniband/hw/mthca/mthca_mcg.c
@@ -28,8 +28,6 @@
* 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.
- *
- * $Id: mthca_mcg.c 1349 2004-12-16 21:09:43Z roland $
*/
#include <linux/string.h>
diff --git a/drivers/infiniband/hw/mthca/mthca_memfree.c b/drivers/infiniband/hw/mthca/mthca_memfree.c
index b224079d4e1f..9e77ba9d6718 100644
--- a/drivers/infiniband/hw/mthca/mthca_memfree.c
+++ b/drivers/infiniband/hw/mthca/mthca_memfree.c
@@ -30,8 +30,6 @@
* 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.
- *
- * $Id$
*/
#include <linux/mm.h>
diff --git a/drivers/infiniband/hw/mthca/mthca_memfree.h b/drivers/infiniband/hw/mthca/mthca_memfree.h
index a1ab06847b75..da9b8f9b884f 100644
--- a/drivers/infiniband/hw/mthca/mthca_memfree.h
+++ b/drivers/infiniband/hw/mthca/mthca_memfree.h
@@ -30,8 +30,6 @@
* 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.
- *
- * $Id$
*/
#ifndef MTHCA_MEMFREE_H
diff --git a/drivers/infiniband/hw/mthca/mthca_mr.c b/drivers/infiniband/hw/mthca/mthca_mr.c
index 820205dec560..8489b1e81c0f 100644
--- a/drivers/infiniband/hw/mthca/mthca_mr.c
+++ b/drivers/infiniband/hw/mthca/mthca_mr.c
@@ -29,8 +29,6 @@
* 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.
- *
- * $Id: mthca_mr.c 1349 2004-12-16 21:09:43Z roland $
*/
#include <linux/slab.h>
diff --git a/drivers/infiniband/hw/mthca/mthca_pd.c b/drivers/infiniband/hw/mthca/mthca_pd.c
index c1e950764bd8..266f14e47406 100644
--- a/drivers/infiniband/hw/mthca/mthca_pd.c
+++ b/drivers/infiniband/hw/mthca/mthca_pd.c
@@ -30,8 +30,6 @@
* 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.
- *
- * $Id: mthca_pd.c 1349 2004-12-16 21:09:43Z roland $
*/
#include <linux/errno.h>
diff --git a/drivers/infiniband/hw/mthca/mthca_profile.c b/drivers/infiniband/hw/mthca/mthca_profile.c
index 605a8d57fac6..d168c2540611 100644
--- a/drivers/infiniband/hw/mthca/mthca_profile.c
+++ b/drivers/infiniband/hw/mthca/mthca_profile.c
@@ -29,8 +29,6 @@
* 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.
- *
- * $Id: mthca_profile.c 1349 2004-12-16 21:09:43Z roland $
*/
#include <linux/module.h>
diff --git a/drivers/infiniband/hw/mthca/mthca_profile.h b/drivers/infiniband/hw/mthca/mthca_profile.h
index e76cb62d8e32..62b009cc8730 100644
--- a/drivers/infiniband/hw/mthca/mthca_profile.h
+++ b/drivers/infiniband/hw/mthca/mthca_profile.h
@@ -29,8 +29,6 @@
* 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.
- *
- * $Id: mthca_profile.h 1349 2004-12-16 21:09:43Z roland $
*/
#ifndef MTHCA_PROFILE_H
diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c
index be34f99ca625..87ad889e367b 100644
--- a/drivers/infiniband/hw/mthca/mthca_provider.c
+++ b/drivers/infiniband/hw/mthca/mthca_provider.c
@@ -32,8 +32,6 @@
* 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.
- *
- * $Id: mthca_provider.c 4859 2006-01-09 21:55:10Z roland $
*/
#include <rdma/ib_smi.h>
diff --git a/drivers/infiniband/hw/mthca/mthca_provider.h b/drivers/infiniband/hw/mthca/mthca_provider.h
index 934bf9544037..c621f8794b88 100644
--- a/drivers/infiniband/hw/mthca/mthca_provider.h
+++ b/drivers/infiniband/hw/mthca/mthca_provider.h
@@ -30,8 +30,6 @@
* 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.
- *
- * $Id: mthca_provider.h 1349 2004-12-16 21:09:43Z roland $
*/
#ifndef MTHCA_PROVIDER_H
diff --git a/drivers/infiniband/hw/mthca/mthca_qp.c b/drivers/infiniband/hw/mthca/mthca_qp.c
index 09dc3614cf2c..3b1c5baf13b1 100644
--- a/drivers/infiniband/hw/mthca/mthca_qp.c
+++ b/drivers/infiniband/hw/mthca/mthca_qp.c
@@ -31,8 +31,6 @@
* 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.
- *
- * $Id: mthca_qp.c 1355 2004-12-17 15:23:43Z roland $
*/
#include <linux/string.h>
diff --git a/drivers/infiniband/hw/mthca/mthca_reset.c b/drivers/infiniband/hw/mthca/mthca_reset.c
index 91934f2d9dba..acb6817f6060 100644
--- a/drivers/infiniband/hw/mthca/mthca_reset.c
+++ b/drivers/infiniband/hw/mthca/mthca_reset.c
@@ -28,8 +28,6 @@
* 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.
- *
- * $Id: mthca_reset.c 1349 2004-12-16 21:09:43Z roland $
*/
#include <linux/init.h>
diff --git a/drivers/infiniband/hw/mthca/mthca_srq.c b/drivers/infiniband/hw/mthca/mthca_srq.c
index a5ffff6e1026..4fabe62aab8a 100644
--- a/drivers/infiniband/hw/mthca/mthca_srq.c
+++ b/drivers/infiniband/hw/mthca/mthca_srq.c
@@ -28,8 +28,6 @@
* 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.
- *
- * $Id: mthca_srq.c 3047 2005-08-10 03:59:35Z roland $
*/
#include <linux/slab.h>
diff --git a/drivers/infiniband/hw/mthca/mthca_uar.c b/drivers/infiniband/hw/mthca/mthca_uar.c
index 8b728486410d..ca5900c96fcf 100644
--- a/drivers/infiniband/hw/mthca/mthca_uar.c
+++ b/drivers/infiniband/hw/mthca/mthca_uar.c
@@ -28,8 +28,6 @@
* 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.
- *
- * $Id$
*/
#include <asm/page.h> /* PAGE_SHIFT */
diff --git a/drivers/infiniband/hw/mthca/mthca_user.h b/drivers/infiniband/hw/mthca/mthca_user.h
index e1262c942db8..5fe56e810739 100644
--- a/drivers/infiniband/hw/mthca/mthca_user.h
+++ b/drivers/infiniband/hw/mthca/mthca_user.h
@@ -29,7 +29,6 @@
* 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 MTHCA_USER_H
diff --git a/drivers/infiniband/hw/mthca/mthca_wqe.h b/drivers/infiniband/hw/mthca/mthca_wqe.h
index b3551a8dea1d..341a5ae881c1 100644
--- a/drivers/infiniband/hw/mthca/mthca_wqe.h
+++ b/drivers/infiniband/hw/mthca/mthca_wqe.h
@@ -28,8 +28,6 @@
* 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.
- *
- * $Id: mthca_wqe.h 3047 2005-08-10 03:59:35Z roland $
*/
#ifndef MTHCA_WQE_H
diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c
index 9a4b40fae40d..6aa531d5276d 100644
--- a/drivers/infiniband/hw/nes/nes_cm.c
+++ b/drivers/infiniband/hw/nes/nes_cm.c
@@ -1603,7 +1603,6 @@ static struct nes_cm_listener *mini_cm_listen(struct nes_cm_core *cm_core,
return NULL;
}
- memset(listener, 0, sizeof(struct nes_cm_listener));
listener->loc_addr = htonl(cm_info->loc_addr);
listener->loc_port = htons(cm_info->loc_port);
listener->reused_node = 0;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h
index ca126fc2b853..8754b364f229 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib.h
+++ b/drivers/infiniband/ulp/ipoib/ipoib.h
@@ -30,8 +30,6 @@
* 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.
- *
- * $Id: ipoib.h 1358 2004-12-17 22:00:11Z roland $
*/
#ifndef _IPOIB_H
@@ -97,6 +95,7 @@ enum {
IPOIB_MCAST_FLAG_ATTACHED = 3,
MAX_SEND_CQE = 16,
+ IPOIB_CM_COPYBREAK = 256,
};
#define IPOIB_OP_RECV (1ul << 31)
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
index 97e67d36378f..6223fc39af70 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
@@ -28,8 +28,6 @@
* 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.
- *
- * $Id$
*/
#include <rdma/ib_cm.h>
@@ -525,6 +523,7 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
u64 mapping[IPOIB_CM_RX_SG];
int frags;
int has_srq;
+ struct sk_buff *small_skb;
ipoib_dbg_data(priv, "cm recv completion: id %d, status: %d\n",
wr_id, wc->status);
@@ -579,6 +578,23 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
}
}
+ if (wc->byte_len < IPOIB_CM_COPYBREAK) {
+ int dlen = wc->byte_len;
+
+ small_skb = dev_alloc_skb(dlen + 12);
+ if (small_skb) {
+ skb_reserve(small_skb, 12);
+ ib_dma_sync_single_for_cpu(priv->ca, rx_ring[wr_id].mapping[0],
+ dlen, DMA_FROM_DEVICE);
+ skb_copy_from_linear_data(skb, small_skb->data, dlen);
+ ib_dma_sync_single_for_device(priv->ca, rx_ring[wr_id].mapping[0],
+ dlen, DMA_FROM_DEVICE);
+ skb_put(small_skb, dlen);
+ skb = small_skb;
+ goto copied;
+ }
+ }
+
frags = PAGE_ALIGN(wc->byte_len - min(wc->byte_len,
(unsigned)IPOIB_CM_HEAD_SIZE)) / PAGE_SIZE;
@@ -601,6 +617,7 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc)
skb_put_frags(skb, IPOIB_CM_HEAD_SIZE, wc->byte_len, newskb);
+copied:
skb->protocol = ((struct ipoib_header *) skb->data)->proto;
skb_reset_mac_header(skb);
skb_pull(skb, IPOIB_ENCAP_LEN);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_fs.c b/drivers/infiniband/ulp/ipoib/ipoib_fs.c
index 8b882bbd1d05..961c585da216 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_fs.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_fs.c
@@ -28,8 +28,6 @@
* 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.
- *
- * $Id: ipoib_fs.c 1389 2004-12-27 22:56:47Z roland $
*/
#include <linux/err.h>
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
index f429bce24c20..eca8518d79a0 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c
@@ -31,8 +31,6 @@
* 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.
- *
- * $Id: ipoib_ib.c 1386 2004-12-27 16:23:17Z roland $
*/
#include <linux/delay.h>
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 2442090ac8d1..bfe1dbf99207 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -30,8 +30,6 @@
* 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.
- *
- * $Id: ipoib_main.c 1377 2004-12-23 19:57:12Z roland $
*/
#include "ipoib.h"
@@ -1304,6 +1302,12 @@ static int __init ipoib_init_module(void)
ipoib_max_conn_qp = min(ipoib_max_conn_qp, IPOIB_CM_MAX_CONN_QP);
#endif
+ /*
+ * When copying small received packets, we only copy from the
+ * linear data part of the SKB, so we rely on this condition.
+ */
+ BUILD_BUG_ON(IPOIB_CM_COPYBREAK > IPOIB_CM_HEAD_SIZE);
+
ret = ipoib_register_debugfs();
if (ret)
return ret;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
index 3f663fb852c1..4a6538b9301a 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
@@ -30,8 +30,6 @@
* 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.
- *
- * $Id: ipoib_multicast.c 1362 2004-12-18 15:56:29Z roland $
*/
#include <linux/skbuff.h>
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_verbs.c b/drivers/infiniband/ulp/ipoib/ipoib_verbs.c
index 8766d29ce3b7..810790ae7530 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_verbs.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_verbs.c
@@ -29,8 +29,6 @@
* 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.
- *
- * $Id: ipoib_verbs.c 1349 2004-12-16 21:09:43Z roland $
*/
#include "ipoib.h"
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c
index 1cdb5cfb0ff1..b08eb56196d3 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_vlan.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_vlan.c
@@ -28,8 +28,6 @@
* 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.
- *
- * $Id: ipoib_vlan.c 1349 2004-12-16 21:09:43Z roland $
*/
#include <linux/module.h>
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
index aeb58cae9a3f..75b9ebbb8bbb 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -42,9 +42,6 @@
* Zhenyu Wang
* Modified by:
* Erez Zilber
- *
- *
- * $Id: iscsi_iser.c 6965 2006-05-07 11:36:20Z ogerlitz $
*/
#include <linux/types.h>
@@ -74,6 +71,10 @@
#include "iscsi_iser.h"
+static struct scsi_host_template iscsi_iser_sht;
+static struct iscsi_transport iscsi_iser_transport;
+static struct scsi_transport_template *iscsi_iser_scsi_transport;
+
static unsigned int iscsi_max_lun = 512;
module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
@@ -94,7 +95,6 @@ iscsi_iser_recv(struct iscsi_conn *conn,
struct iscsi_hdr *hdr, char *rx_data, int rx_data_len)
{
int rc = 0;
- uint32_t ret_itt;
int datalen;
int ahslen;
@@ -110,12 +110,7 @@ iscsi_iser_recv(struct iscsi_conn *conn,
/* read AHS */
ahslen = hdr->hlength * 4;
- /* verify itt (itt encoding: age+cid+itt) */
- rc = iscsi_verify_itt(conn, hdr, &ret_itt);
-
- if (!rc)
- rc = iscsi_complete_pdu(conn, hdr, rx_data, rx_data_len);
-
+ rc = iscsi_complete_pdu(conn, hdr, rx_data, rx_data_len);
if (rc && rc != ISCSI_ERR_NO_SCSI_CMD)
goto error;
@@ -126,25 +121,33 @@ error:
/**
- * iscsi_iser_cmd_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
+ * iscsi_iser_task_init - Initialize task
+ * @task: iscsi task
*
- **/
+ * Initialize the task for the scsi command or mgmt command.
+ */
static int
-iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask)
+iscsi_iser_task_init(struct iscsi_task *task)
{
- struct iscsi_iser_conn *iser_conn = ctask->conn->dd_data;
- struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+ struct iscsi_iser_conn *iser_conn = task->conn->dd_data;
+ struct iscsi_iser_task *iser_task = task->dd_data;
- iser_ctask->command_sent = 0;
- iser_ctask->iser_conn = iser_conn;
- iser_ctask_rdma_init(iser_ctask);
+ /* mgmt task */
+ if (!task->sc) {
+ iser_task->desc.data = task->data;
+ return 0;
+ }
+
+ iser_task->command_sent = 0;
+ iser_task->iser_conn = iser_conn;
+ iser_task_rdma_init(iser_task);
return 0;
}
/**
- * iscsi_mtask_xmit - xmit management(immediate) task
+ * iscsi_iser_mtask_xmit - xmit management(immediate) task
* @conn: iscsi connection
- * @mtask: task management task
+ * @task: task management task
*
* Notes:
* The function can return -EAGAIN in which case caller must
@@ -153,20 +156,19 @@ iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask)
*
**/
static int
-iscsi_iser_mtask_xmit(struct iscsi_conn *conn,
- struct iscsi_mgmt_task *mtask)
+iscsi_iser_mtask_xmit(struct iscsi_conn *conn, struct iscsi_task *task)
{
int error = 0;
- debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id, mtask->itt);
+ debug_scsi("task deq [cid %d itt 0x%x]\n", conn->id, task->itt);
- error = iser_send_control(conn, mtask);
+ error = iser_send_control(conn, task);
- /* since iser xmits control with zero copy, mtasks can not be recycled
+ /* since iser xmits control with zero copy, tasks can not be recycled
* right after sending them.
* The recycling scheme is based on whether a response is expected
- * - if yes, the mtask is recycled at iscsi_complete_pdu
- * - if no, the mtask is recycled at iser_snd_completion
+ * - if yes, the task is recycled at iscsi_complete_pdu
+ * - if no, the task is recycled at iser_snd_completion
*/
if (error && error != -ENOBUFS)
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
@@ -175,97 +177,86 @@ iscsi_iser_mtask_xmit(struct iscsi_conn *conn,
}
static int
-iscsi_iser_ctask_xmit_unsol_data(struct iscsi_conn *conn,
- struct iscsi_cmd_task *ctask)
+iscsi_iser_task_xmit_unsol_data(struct iscsi_conn *conn,
+ struct iscsi_task *task)
{
struct iscsi_data hdr;
int error = 0;
/* Send data-out PDUs while there's still unsolicited data to send */
- while (ctask->unsol_count > 0) {
- iscsi_prep_unsolicit_data_pdu(ctask, &hdr);
+ while (task->unsol_count > 0) {
+ iscsi_prep_unsolicit_data_pdu(task, &hdr);
debug_scsi("Sending data-out: itt 0x%x, data count %d\n",
- hdr.itt, ctask->data_count);
+ hdr.itt, task->data_count);
/* the buffer description has been passed with the command */
/* Send the command */
- error = iser_send_data_out(conn, ctask, &hdr);
+ error = iser_send_data_out(conn, task, &hdr);
if (error) {
- ctask->unsol_datasn--;
- goto iscsi_iser_ctask_xmit_unsol_data_exit;
+ task->unsol_datasn--;
+ goto iscsi_iser_task_xmit_unsol_data_exit;
}
- ctask->unsol_count -= ctask->data_count;
+ task->unsol_count -= task->data_count;
debug_scsi("Need to send %d more as data-out PDUs\n",
- ctask->unsol_count);
+ task->unsol_count);
}
-iscsi_iser_ctask_xmit_unsol_data_exit:
+iscsi_iser_task_xmit_unsol_data_exit:
return error;
}
static int
-iscsi_iser_ctask_xmit(struct iscsi_conn *conn,
- struct iscsi_cmd_task *ctask)
+iscsi_iser_task_xmit(struct iscsi_task *task)
{
- struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+ struct iscsi_conn *conn = task->conn;
+ struct iscsi_iser_task *iser_task = task->dd_data;
int error = 0;
- if (ctask->sc->sc_data_direction == DMA_TO_DEVICE) {
- BUG_ON(scsi_bufflen(ctask->sc) == 0);
+ if (!task->sc)
+ return iscsi_iser_mtask_xmit(conn, task);
+
+ if (task->sc->sc_data_direction == DMA_TO_DEVICE) {
+ BUG_ON(scsi_bufflen(task->sc) == 0);
debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
- ctask->itt, scsi_bufflen(ctask->sc),
- ctask->imm_count, ctask->unsol_count);
+ task->itt, scsi_bufflen(task->sc),
+ task->imm_count, task->unsol_count);
}
- debug_scsi("ctask deq [cid %d itt 0x%x]\n",
- conn->id, ctask->itt);
+ debug_scsi("task deq [cid %d itt 0x%x]\n",
+ conn->id, task->itt);
/* Send the cmd PDU */
- if (!iser_ctask->command_sent) {
- error = iser_send_command(conn, ctask);
+ if (!iser_task->command_sent) {
+ error = iser_send_command(conn, task);
if (error)
- goto iscsi_iser_ctask_xmit_exit;
- iser_ctask->command_sent = 1;
+ goto iscsi_iser_task_xmit_exit;
+ iser_task->command_sent = 1;
}
/* Send unsolicited data-out PDU(s) if necessary */
- if (ctask->unsol_count)
- error = iscsi_iser_ctask_xmit_unsol_data(conn, ctask);
+ if (task->unsol_count)
+ error = iscsi_iser_task_xmit_unsol_data(conn, task);
- iscsi_iser_ctask_xmit_exit:
+ iscsi_iser_task_xmit_exit:
if (error && error != -ENOBUFS)
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
return error;
}
static void
-iscsi_iser_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+iscsi_iser_cleanup_task(struct iscsi_conn *conn, struct iscsi_task *task)
{
- struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
-
- if (iser_ctask->status == ISER_TASK_STATUS_STARTED) {
- iser_ctask->status = ISER_TASK_STATUS_COMPLETED;
- iser_ctask_rdma_finalize(iser_ctask);
- }
-}
+ struct iscsi_iser_task *iser_task = task->dd_data;
-static struct iser_conn *
-iscsi_iser_ib_conn_lookup(__u64 ep_handle)
-{
- struct iser_conn *ib_conn;
- struct iser_conn *uib_conn = (struct iser_conn *)(unsigned long)ep_handle;
+ /* mgmt tasks do not need special cleanup */
+ if (!task->sc)
+ return;
- mutex_lock(&ig.connlist_mutex);
- list_for_each_entry(ib_conn, &ig.connlist, conn_list) {
- if (ib_conn == uib_conn) {
- mutex_unlock(&ig.connlist_mutex);
- return ib_conn;
- }
+ if (iser_task->status == ISER_TASK_STATUS_STARTED) {
+ iser_task->status = ISER_TASK_STATUS_COMPLETED;
+ iser_task_rdma_finalize(iser_task);
}
- mutex_unlock(&ig.connlist_mutex);
- iser_err("no conn exists for eph %llx\n",(unsigned long long)ep_handle);
- return NULL;
}
static struct iscsi_cls_conn *
@@ -275,7 +266,7 @@ iscsi_iser_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
struct iscsi_cls_conn *cls_conn;
struct iscsi_iser_conn *iser_conn;
- cls_conn = iscsi_conn_setup(cls_session, conn_idx);
+ cls_conn = iscsi_conn_setup(cls_session, sizeof(*iser_conn), conn_idx);
if (!cls_conn)
return NULL;
conn = cls_conn->dd_data;
@@ -286,21 +277,11 @@ iscsi_iser_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
*/
conn->max_recv_dlength = 128;
- iser_conn = kzalloc(sizeof(*iser_conn), GFP_KERNEL);
- if (!iser_conn)
- goto conn_alloc_fail;
-
- /* currently this is the only field which need to be initiated */
- rwlock_init(&iser_conn->lock);
-
+ iser_conn = conn->dd_data;
conn->dd_data = iser_conn;
iser_conn->iscsi_conn = conn;
return cls_conn;
-
-conn_alloc_fail:
- iscsi_conn_teardown(cls_conn);
- return NULL;
}
static void
@@ -308,11 +289,18 @@ iscsi_iser_conn_destroy(struct iscsi_cls_conn *cls_conn)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_iser_conn *iser_conn = conn->dd_data;
+ struct iser_conn *ib_conn = iser_conn->ib_conn;
iscsi_conn_teardown(cls_conn);
- if (iser_conn->ib_conn)
- iser_conn->ib_conn->iser_conn = NULL;
- kfree(iser_conn);
+ /*
+ * Userspace will normally call the stop callback and
+ * already have freed the ib_conn, but if it goofed up then
+ * we free it here.
+ */
+ if (ib_conn) {
+ ib_conn->iser_conn = NULL;
+ iser_conn_put(ib_conn);
+ }
}
static int
@@ -323,6 +311,7 @@ iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session,
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_iser_conn *iser_conn;
struct iser_conn *ib_conn;
+ struct iscsi_endpoint *ep;
int error;
error = iscsi_conn_bind(cls_session, cls_conn, is_leading);
@@ -331,12 +320,14 @@ iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session,
/* the transport ep handle comes from user space so it must be
* verified against the global ib connections list */
- ib_conn = iscsi_iser_ib_conn_lookup(transport_eph);
- if (!ib_conn) {
+ ep = iscsi_lookup_endpoint(transport_eph);
+ if (!ep) {
iser_err("can't bind eph %llx\n",
(unsigned long long)transport_eph);
return -EINVAL;
}
+ ib_conn = ep->dd_data;
+
/* binds the iSER connection retrieved from the previously
* connected ep_handle to the iSCSI layer connection. exchanges
* connection pointers */
@@ -344,10 +335,30 @@ iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session,
iser_conn = conn->dd_data;
ib_conn->iser_conn = iser_conn;
iser_conn->ib_conn = ib_conn;
+ iser_conn_get(ib_conn);
+ return 0;
+}
- conn->recv_lock = &iser_conn->lock;
+static void
+iscsi_iser_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_iser_conn *iser_conn = conn->dd_data;
+ struct iser_conn *ib_conn = iser_conn->ib_conn;
- return 0;
+ /*
+ * Userspace may have goofed up and not bound the connection or
+ * might have only partially setup the connection.
+ */
+ if (ib_conn) {
+ iscsi_conn_stop(cls_conn, flag);
+ /*
+ * There is no unbind event so the stop callback
+ * must release the ref from the bind.
+ */
+ iser_conn_put(ib_conn);
+ }
+ iser_conn->ib_conn = NULL;
}
static int
@@ -363,55 +374,75 @@ iscsi_iser_conn_start(struct iscsi_cls_conn *cls_conn)
return iscsi_conn_start(cls_conn);
}
-static struct iscsi_transport iscsi_iser_transport;
+static void iscsi_iser_session_destroy(struct iscsi_cls_session *cls_session)
+{
+ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+
+ iscsi_host_remove(shost);
+ iscsi_host_free(shost);
+}
static struct iscsi_cls_session *
-iscsi_iser_session_create(struct iscsi_transport *iscsit,
- struct scsi_transport_template *scsit,
- uint16_t cmds_max, uint16_t qdepth,
- uint32_t initial_cmdsn, uint32_t *hostno)
+iscsi_iser_session_create(struct iscsi_endpoint *ep,
+ uint16_t cmds_max, uint16_t qdepth,
+ uint32_t initial_cmdsn, uint32_t *hostno)
{
struct iscsi_cls_session *cls_session;
struct iscsi_session *session;
+ struct Scsi_Host *shost;
int i;
- uint32_t hn;
- struct iscsi_cmd_task *ctask;
- struct iscsi_mgmt_task *mtask;
- struct iscsi_iser_cmd_task *iser_ctask;
- struct iser_desc *desc;
+ struct iscsi_task *task;
+ struct iscsi_iser_task *iser_task;
+ struct iser_conn *ib_conn;
+
+ shost = iscsi_host_alloc(&iscsi_iser_sht, 0, ISCSI_MAX_CMD_PER_LUN);
+ if (!shost)
+ return NULL;
+ shost->transportt = iscsi_iser_scsi_transport;
+ shost->max_lun = iscsi_max_lun;
+ shost->max_id = 0;
+ shost->max_channel = 0;
+ shost->max_cmd_len = 16;
+
+ /*
+ * older userspace tools (before 2.0-870) did not pass us
+ * the leading conn's ep so this will be NULL;
+ */
+ if (ep)
+ ib_conn = ep->dd_data;
+
+ if (iscsi_host_add(shost,
+ ep ? ib_conn->device->ib_device->dma_device : NULL))
+ goto free_host;
+ *hostno = shost->host_no;
/*
* we do not support setting can_queue cmd_per_lun from userspace yet
* because we preallocate so many resources
*/
- cls_session = iscsi_session_setup(iscsit, scsit,
+ cls_session = iscsi_session_setup(&iscsi_iser_transport, shost,
ISCSI_DEF_XMIT_CMDS_MAX,
- ISCSI_MAX_CMD_PER_LUN,
- sizeof(struct iscsi_iser_cmd_task),
- sizeof(struct iser_desc),
- initial_cmdsn, &hn);
+ sizeof(struct iscsi_iser_task),
+ initial_cmdsn, 0);
if (!cls_session)
- return NULL;
-
- *hostno = hn;
- session = class_to_transport_session(cls_session);
+ goto remove_host;
+ session = cls_session->dd_data;
+ shost->can_queue = session->scsi_cmds_max;
/* libiscsi setup itts, data and pool so just set desc fields */
for (i = 0; i < session->cmds_max; i++) {
- ctask = session->cmds[i];
- iser_ctask = ctask->dd_data;
- ctask->hdr = (struct iscsi_cmd *)&iser_ctask->desc.iscsi_header;
- ctask->hdr_max = sizeof(iser_ctask->desc.iscsi_header);
- }
-
- for (i = 0; i < session->mgmtpool_max; i++) {
- mtask = session->mgmt_cmds[i];
- desc = mtask->dd_data;
- mtask->hdr = &desc->iscsi_header;
- desc->data = mtask->data;
+ task = session->cmds[i];
+ iser_task = task->dd_data;
+ task->hdr = (struct iscsi_cmd *)&iser_task->desc.iscsi_header;
+ task->hdr_max = sizeof(iser_task->desc.iscsi_header);
}
-
return cls_session;
+
+remove_host:
+ iscsi_host_remove(shost);
+free_host:
+ iscsi_host_free(shost);
+ return NULL;
}
static int
@@ -484,34 +515,37 @@ iscsi_iser_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *s
stats->custom[3].value = conn->fmr_unalign_cnt;
}
-static int
-iscsi_iser_ep_connect(struct sockaddr *dst_addr, int non_blocking,
- __u64 *ep_handle)
+static struct iscsi_endpoint *
+iscsi_iser_ep_connect(struct sockaddr *dst_addr, int non_blocking)
{
int err;
struct iser_conn *ib_conn;
+ struct iscsi_endpoint *ep;
- err = iser_conn_init(&ib_conn);
- if (err)
- goto out;
+ ep = iscsi_create_endpoint(sizeof(*ib_conn));
+ if (!ep)
+ return ERR_PTR(-ENOMEM);
- err = iser_connect(ib_conn, NULL, (struct sockaddr_in *)dst_addr, non_blocking);
- if (!err)
- *ep_handle = (__u64)(unsigned long)ib_conn;
+ ib_conn = ep->dd_data;
+ ib_conn->ep = ep;
+ iser_conn_init(ib_conn);
-out:
- return err;
+ err = iser_connect(ib_conn, NULL, (struct sockaddr_in *)dst_addr,
+ non_blocking);
+ if (err) {
+ iscsi_destroy_endpoint(ep);
+ return ERR_PTR(err);
+ }
+ return ep;
}
static int
-iscsi_iser_ep_poll(__u64 ep_handle, int timeout_ms)
+iscsi_iser_ep_poll(struct iscsi_endpoint *ep, int timeout_ms)
{
- struct iser_conn *ib_conn = iscsi_iser_ib_conn_lookup(ep_handle);
+ struct iser_conn *ib_conn;
int rc;
- if (!ib_conn)
- return -EINVAL;
-
+ ib_conn = ep->dd_data;
rc = wait_event_interruptible_timeout(ib_conn->wait,
ib_conn->state == ISER_CONN_UP,
msecs_to_jiffies(timeout_ms));
@@ -533,13 +567,21 @@ iscsi_iser_ep_poll(__u64 ep_handle, int timeout_ms)
}
static void
-iscsi_iser_ep_disconnect(__u64 ep_handle)
+iscsi_iser_ep_disconnect(struct iscsi_endpoint *ep)
{
struct iser_conn *ib_conn;
- ib_conn = iscsi_iser_ib_conn_lookup(ep_handle);
- if (!ib_conn)
- return;
+ ib_conn = ep->dd_data;
+ if (ib_conn->iser_conn)
+ /*
+ * Must suspend xmit path if the ep is bound to the
+ * iscsi_conn, so we know we are not accessing the ib_conn
+ * when we free it.
+ *
+ * This may not be bound if the ep poll failed.
+ */
+ iscsi_suspend_tx(ib_conn->iser_conn->iscsi_conn);
+
iser_err("ib conn %p state %d\n",ib_conn, ib_conn->state);
iser_conn_terminate(ib_conn);
@@ -584,17 +626,14 @@ static struct iscsi_transport iscsi_iser_transport = {
ISCSI_USERNAME | ISCSI_PASSWORD |
ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
- ISCSI_PING_TMO | ISCSI_RECV_TMO,
+ ISCSI_PING_TMO | ISCSI_RECV_TMO |
+ ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
.host_param_mask = ISCSI_HOST_HWADDRESS |
ISCSI_HOST_NETDEV_NAME |
ISCSI_HOST_INITIATOR_NAME,
- .host_template = &iscsi_iser_sht,
- .conndata_size = sizeof(struct iscsi_conn),
- .max_lun = ISCSI_ISER_MAX_LUN,
- .max_cmd_len = ISCSI_ISER_MAX_CMD_LEN,
/* session management */
.create_session = iscsi_iser_session_create,
- .destroy_session = iscsi_session_teardown,
+ .destroy_session = iscsi_iser_session_destroy,
/* connection management */
.create_conn = iscsi_iser_conn_create,
.bind_conn = iscsi_iser_conn_bind,
@@ -603,17 +642,16 @@ static struct iscsi_transport iscsi_iser_transport = {
.get_conn_param = iscsi_conn_get_param,
.get_session_param = iscsi_session_get_param,
.start_conn = iscsi_iser_conn_start,
- .stop_conn = iscsi_conn_stop,
+ .stop_conn = iscsi_iser_conn_stop,
/* iscsi host params */
.get_host_param = iscsi_host_get_param,
.set_host_param = iscsi_host_set_param,
/* IO */
.send_pdu = iscsi_conn_send_pdu,
.get_stats = iscsi_iser_conn_get_stats,
- .init_cmd_task = iscsi_iser_cmd_init,
- .xmit_cmd_task = iscsi_iser_ctask_xmit,
- .xmit_mgmt_task = iscsi_iser_mtask_xmit,
- .cleanup_cmd_task = iscsi_iser_cleanup_ctask,
+ .init_task = iscsi_iser_task_init,
+ .xmit_task = iscsi_iser_task_xmit,
+ .cleanup_task = iscsi_iser_cleanup_task,
/* recovery */
.session_recovery_timedout = iscsi_session_recovery_timedout,
@@ -633,8 +671,6 @@ static int __init iser_init(void)
return -EINVAL;
}
- iscsi_iser_transport.max_lun = iscsi_max_lun;
-
memset(&ig, 0, sizeof(struct iser_global));
ig.desc_cache = kmem_cache_create("iser_descriptors",
@@ -650,7 +686,9 @@ static int __init iser_init(void)
mutex_init(&ig.connlist_mutex);
INIT_LIST_HEAD(&ig.connlist);
- if (!iscsi_register_transport(&iscsi_iser_transport)) {
+ iscsi_iser_scsi_transport = iscsi_register_transport(
+ &iscsi_iser_transport);
+ if (!iscsi_iser_scsi_transport) {
iser_err("iscsi_register_transport failed\n");
err = -EINVAL;
goto register_transport_failure;
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h
index a8c1b300e34d..81a82628a5f1 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.h
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.h
@@ -36,8 +36,6 @@
* 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.
- *
- * $Id: iscsi_iser.h 7051 2006-05-10 12:29:11Z ogerlitz $
*/
#ifndef __ISCSI_ISER_H__
#define __ISCSI_ISER_H__
@@ -96,7 +94,6 @@
/* support upto 512KB in one RDMA */
#define ISCSI_ISER_SG_TABLESIZE (0x80000 >> SHIFT_4K)
#define ISCSI_ISER_MAX_LUN 256
-#define ISCSI_ISER_MAX_CMD_LEN 16
/* QP settings */
/* Maximal bounds on received asynchronous PDUs */
@@ -174,7 +171,8 @@ struct iser_data_buf {
/* fwd declarations */
struct iser_device;
struct iscsi_iser_conn;
-struct iscsi_iser_cmd_task;
+struct iscsi_iser_task;
+struct iscsi_endpoint;
struct iser_mem_reg {
u32 lkey;
@@ -198,7 +196,7 @@ struct iser_regd_buf {
#define MAX_REGD_BUF_VECTOR_LEN 2
struct iser_dto {
- struct iscsi_iser_cmd_task *ctask;
+ struct iscsi_iser_task *task;
struct iser_conn *ib_conn;
int notify_enable;
@@ -242,7 +240,9 @@ struct iser_device {
struct iser_conn {
struct iscsi_iser_conn *iser_conn; /* iser conn for upcalls */
+ struct iscsi_endpoint *ep;
enum iser_ib_conn_state state; /* rdma connection state */
+ atomic_t refcount;
spinlock_t lock; /* used for state changes */
struct iser_device *device; /* device context */
struct rdma_cm_id *cma_id; /* CMA ID */
@@ -261,11 +261,9 @@ struct iser_conn {
struct iscsi_iser_conn {
struct iscsi_conn *iscsi_conn;/* ptr to iscsi conn */
struct iser_conn *ib_conn; /* iSER IB conn */
-
- rwlock_t lock;
};
-struct iscsi_iser_cmd_task {
+struct iscsi_iser_task {
struct iser_desc desc;
struct iscsi_iser_conn *iser_conn;
enum iser_task_status status;
@@ -298,22 +296,26 @@ extern int iser_debug_level;
/* allocate connection resources needed for rdma functionality */
int iser_conn_set_full_featured_mode(struct iscsi_conn *conn);
-int iser_send_control(struct iscsi_conn *conn,
- struct iscsi_mgmt_task *mtask);
+int iser_send_control(struct iscsi_conn *conn,
+ struct iscsi_task *task);
-int iser_send_command(struct iscsi_conn *conn,
- struct iscsi_cmd_task *ctask);
+int iser_send_command(struct iscsi_conn *conn,
+ struct iscsi_task *task);
-int iser_send_data_out(struct iscsi_conn *conn,
- struct iscsi_cmd_task *ctask,
- struct iscsi_data *hdr);
+int iser_send_data_out(struct iscsi_conn *conn,
+ struct iscsi_task *task,
+ struct iscsi_data *hdr);
void iscsi_iser_recv(struct iscsi_conn *conn,
struct iscsi_hdr *hdr,
char *rx_data,
int rx_data_len);
-int iser_conn_init(struct iser_conn **ib_conn);
+void iser_conn_init(struct iser_conn *ib_conn);
+
+void iser_conn_get(struct iser_conn *ib_conn);
+
+void iser_conn_put(struct iser_conn *ib_conn);
void iser_conn_terminate(struct iser_conn *ib_conn);
@@ -322,9 +324,9 @@ void iser_rcv_completion(struct iser_desc *desc,
void iser_snd_completion(struct iser_desc *desc);
-void iser_ctask_rdma_init(struct iscsi_iser_cmd_task *ctask);
+void iser_task_rdma_init(struct iscsi_iser_task *task);
-void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *ctask);
+void iser_task_rdma_finalize(struct iscsi_iser_task *task);
void iser_dto_buffs_release(struct iser_dto *dto);
@@ -334,10 +336,10 @@ void iser_reg_single(struct iser_device *device,
struct iser_regd_buf *regd_buf,
enum dma_data_direction direction);
-void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *ctask,
+void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_task *task,
enum iser_data_dir cmd_dir);
-int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *ctask,
+int iser_reg_rdma_mem(struct iscsi_iser_task *task,
enum iser_data_dir cmd_dir);
int iser_connect(struct iser_conn *ib_conn,
@@ -357,10 +359,10 @@ int iser_post_send(struct iser_desc *tx_desc);
int iser_conn_state_comp(struct iser_conn *ib_conn,
enum iser_ib_conn_state comp);
-int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask,
+int iser_dma_map_task_data(struct iscsi_iser_task *iser_task,
struct iser_data_buf *data,
enum iser_data_dir iser_dir,
enum dma_data_direction dma_dir);
-void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask);
+void iser_dma_unmap_task_data(struct iscsi_iser_task *iser_task);
#endif
diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
index 08dc81c46f41..cdd283189047 100644
--- a/drivers/infiniband/ulp/iser/iser_initiator.c
+++ b/drivers/infiniband/ulp/iser/iser_initiator.c
@@ -28,8 +28,6 @@
* 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.
- *
- * $Id: iser_initiator.c 6964 2006-05-07 11:11:43Z ogerlitz $
*/
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -66,46 +64,46 @@ static void iser_dto_add_regd_buff(struct iser_dto *dto,
/* Register user buffer memory and initialize passive rdma
* dto descriptor. Total data size is stored in
- * iser_ctask->data[ISER_DIR_IN].data_len
+ * iser_task->data[ISER_DIR_IN].data_len
*/
-static int iser_prepare_read_cmd(struct iscsi_cmd_task *ctask,
+static int iser_prepare_read_cmd(struct iscsi_task *task,
unsigned int edtl)
{
- struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+ struct iscsi_iser_task *iser_task = task->dd_data;
struct iser_regd_buf *regd_buf;
int err;
- struct iser_hdr *hdr = &iser_ctask->desc.iser_header;
- struct iser_data_buf *buf_in = &iser_ctask->data[ISER_DIR_IN];
+ struct iser_hdr *hdr = &iser_task->desc.iser_header;
+ struct iser_data_buf *buf_in = &iser_task->data[ISER_DIR_IN];
- err = iser_dma_map_task_data(iser_ctask,
+ err = iser_dma_map_task_data(iser_task,
buf_in,
ISER_DIR_IN,
DMA_FROM_DEVICE);
if (err)
return err;
- if (edtl > iser_ctask->data[ISER_DIR_IN].data_len) {
+ if (edtl > iser_task->data[ISER_DIR_IN].data_len) {
iser_err("Total data length: %ld, less than EDTL: "
"%d, in READ cmd BHS itt: %d, conn: 0x%p\n",
- iser_ctask->data[ISER_DIR_IN].data_len, edtl,
- ctask->itt, iser_ctask->iser_conn);
+ iser_task->data[ISER_DIR_IN].data_len, edtl,
+ task->itt, iser_task->iser_conn);
return -EINVAL;
}
- err = iser_reg_rdma_mem(iser_ctask,ISER_DIR_IN);
+ err = iser_reg_rdma_mem(iser_task,ISER_DIR_IN);
if (err) {
iser_err("Failed to set up Data-IN RDMA\n");
return err;
}
- regd_buf = &iser_ctask->rdma_regd[ISER_DIR_IN];
+ regd_buf = &iser_task->rdma_regd[ISER_DIR_IN];
hdr->flags |= ISER_RSV;
hdr->read_stag = cpu_to_be32(regd_buf->reg.rkey);
hdr->read_va = cpu_to_be64(regd_buf->reg.va);
iser_dbg("Cmd itt:%d READ tags RKEY:%#.4X VA:%#llX\n",
- ctask->itt, regd_buf->reg.rkey,
+ task->itt, regd_buf->reg.rkey,
(unsigned long long)regd_buf->reg.va);
return 0;
@@ -113,43 +111,43 @@ static int iser_prepare_read_cmd(struct iscsi_cmd_task *ctask,
/* Register user buffer memory and initialize passive rdma
* dto descriptor. Total data size is stored in
- * ctask->data[ISER_DIR_OUT].data_len
+ * task->data[ISER_DIR_OUT].data_len
*/
static int
-iser_prepare_write_cmd(struct iscsi_cmd_task *ctask,
+iser_prepare_write_cmd(struct iscsi_task *task,
unsigned int imm_sz,
unsigned int unsol_sz,
unsigned int edtl)
{
- struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+ struct iscsi_iser_task *iser_task = task->dd_data;
struct iser_regd_buf *regd_buf;
int err;
- struct iser_dto *send_dto = &iser_ctask->desc.dto;
- struct iser_hdr *hdr = &iser_ctask->desc.iser_header;
- struct iser_data_buf *buf_out = &iser_ctask->data[ISER_DIR_OUT];
+ struct iser_dto *send_dto = &iser_task->desc.dto;
+ struct iser_hdr *hdr = &iser_task->desc.iser_header;
+ struct iser_data_buf *buf_out = &iser_task->data[ISER_DIR_OUT];
- err = iser_dma_map_task_data(iser_ctask,
+ err = iser_dma_map_task_data(iser_task,
buf_out,
ISER_DIR_OUT,
DMA_TO_DEVICE);
if (err)
return err;
- if (edtl > iser_ctask->data[ISER_DIR_OUT].data_len) {
+ if (edtl > iser_task->data[ISER_DIR_OUT].data_len) {
iser_err("Total data length: %ld, less than EDTL: %d, "
"in WRITE cmd BHS itt: %d, conn: 0x%p\n",
- iser_ctask->data[ISER_DIR_OUT].data_len,
- edtl, ctask->itt, ctask->conn);
+ iser_task->data[ISER_DIR_OUT].data_len,
+ edtl, task->itt, task->conn);
return -EINVAL;
}
- err = iser_reg_rdma_mem(iser_ctask,ISER_DIR_OUT);
+ err = iser_reg_rdma_mem(iser_task,ISER_DIR_OUT);
if (err != 0) {
iser_err("Failed to register write cmd RDMA mem\n");
return err;
}
- regd_buf = &iser_ctask->rdma_regd[ISER_DIR_OUT];
+ regd_buf = &iser_task->rdma_regd[ISER_DIR_OUT];
if (unsol_sz < edtl) {
hdr->flags |= ISER_WSV;
@@ -158,13 +156,13 @@ iser_prepare_write_cmd(struct iscsi_cmd_task *ctask,
iser_dbg("Cmd itt:%d, WRITE tags, RKEY:%#.4X "
"VA:%#llX + unsol:%d\n",
- ctask->itt, regd_buf->reg.rkey,
+ task->itt, regd_buf->reg.rkey,
(unsigned long long)regd_buf->reg.va, unsol_sz);
}
if (imm_sz > 0) {
iser_dbg("Cmd itt:%d, WRITE, adding imm.data sz: %d\n",
- ctask->itt, imm_sz);
+ task->itt, imm_sz);
iser_dto_add_regd_buff(send_dto,
regd_buf,
0,
@@ -316,38 +314,38 @@ iser_check_xmit(struct iscsi_conn *conn, void *task)
/**
* iser_send_command - send command PDU
*/
-int iser_send_command(struct iscsi_conn *conn,
- struct iscsi_cmd_task *ctask)
+int iser_send_command(struct iscsi_conn *conn,
+ struct iscsi_task *task)
{
struct iscsi_iser_conn *iser_conn = conn->dd_data;
- struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+ struct iscsi_iser_task *iser_task = task->dd_data;
struct iser_dto *send_dto = NULL;
unsigned long edtl;
int err = 0;
struct iser_data_buf *data_buf;
- struct iscsi_cmd *hdr = ctask->hdr;
- struct scsi_cmnd *sc = ctask->sc;
+ struct iscsi_cmd *hdr = task->hdr;
+ struct scsi_cmnd *sc = task->sc;
if (!iser_conn_state_comp(iser_conn->ib_conn, ISER_CONN_UP)) {
iser_err("Failed to send, conn: 0x%p is not up\n", iser_conn->ib_conn);
return -EPERM;
}
- if (iser_check_xmit(conn, ctask))
+ if (iser_check_xmit(conn, task))
return -ENOBUFS;
edtl = ntohl(hdr->data_length);
/* build the tx desc regd header and add it to the tx desc dto */
- iser_ctask->desc.type = ISCSI_TX_SCSI_COMMAND;
- send_dto = &iser_ctask->desc.dto;
- send_dto->ctask = iser_ctask;
- iser_create_send_desc(iser_conn, &iser_ctask->desc);
+ iser_task->desc.type = ISCSI_TX_SCSI_COMMAND;
+ send_dto = &iser_task->desc.dto;
+ send_dto->task = iser_task;
+ iser_create_send_desc(iser_conn, &iser_task->desc);
if (hdr->flags & ISCSI_FLAG_CMD_READ)
- data_buf = &iser_ctask->data[ISER_DIR_IN];
+ data_buf = &iser_task->data[ISER_DIR_IN];
else
- data_buf = &iser_ctask->data[ISER_DIR_OUT];
+ data_buf = &iser_task->data[ISER_DIR_OUT];
if (scsi_sg_count(sc)) { /* using a scatter list */
data_buf->buf = scsi_sglist(sc);
@@ -357,15 +355,15 @@ int iser_send_command(struct iscsi_conn *conn,
data_buf->data_len = scsi_bufflen(sc);
if (hdr->flags & ISCSI_FLAG_CMD_READ) {
- err = iser_prepare_read_cmd(ctask, edtl);
+ err = iser_prepare_read_cmd(task, edtl);
if (err)
goto send_command_error;
}
if (hdr->flags & ISCSI_FLAG_CMD_WRITE) {
- err = iser_prepare_write_cmd(ctask,
- ctask->imm_count,
- ctask->imm_count +
- ctask->unsol_count,
+ err = iser_prepare_write_cmd(task,
+ task->imm_count,
+ task->imm_count +
+ task->unsol_count,
edtl);
if (err)
goto send_command_error;
@@ -380,27 +378,27 @@ int iser_send_command(struct iscsi_conn *conn,
goto send_command_error;
}
- iser_ctask->status = ISER_TASK_STATUS_STARTED;
+ iser_task->status = ISER_TASK_STATUS_STARTED;
- err = iser_post_send(&iser_ctask->desc);
+ err = iser_post_send(&iser_task->desc);
if (!err)
return 0;
send_command_error:
iser_dto_buffs_release(send_dto);
- iser_err("conn %p failed ctask->itt %d err %d\n",conn, ctask->itt, err);
+ iser_err("conn %p failed task->itt %d err %d\n",conn, task->itt, err);
return err;
}
/**
* iser_send_data_out - send data out PDU
*/
-int iser_send_data_out(struct iscsi_conn *conn,
- struct iscsi_cmd_task *ctask,
+int iser_send_data_out(struct iscsi_conn *conn,
+ struct iscsi_task *task,
struct iscsi_data *hdr)
{
struct iscsi_iser_conn *iser_conn = conn->dd_data;
- struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+ struct iscsi_iser_task *iser_task = task->dd_data;
struct iser_desc *tx_desc = NULL;
struct iser_dto *send_dto = NULL;
unsigned long buf_offset;
@@ -413,7 +411,7 @@ int iser_send_data_out(struct iscsi_conn *conn,
return -EPERM;
}
- if (iser_check_xmit(conn, ctask))
+ if (iser_check_xmit(conn, task))
return -ENOBUFS;
itt = (__force uint32_t)hdr->itt;
@@ -434,7 +432,7 @@ int iser_send_data_out(struct iscsi_conn *conn,
/* build the tx desc regd header and add it to the tx desc dto */
send_dto = &tx_desc->dto;
- send_dto->ctask = iser_ctask;
+ send_dto->task = iser_task;
iser_create_send_desc(iser_conn, tx_desc);
iser_reg_single(iser_conn->ib_conn->device,
@@ -442,15 +440,15 @@ int iser_send_data_out(struct iscsi_conn *conn,
/* all data was registered for RDMA, we can use the lkey */
iser_dto_add_regd_buff(send_dto,
- &iser_ctask->rdma_regd[ISER_DIR_OUT],
+ &iser_task->rdma_regd[ISER_DIR_OUT],
buf_offset,
data_seg_len);
- if (buf_offset + data_seg_len > iser_ctask->data[ISER_DIR_OUT].data_len) {
+ if (buf_offset + data_seg_len > iser_task->data[ISER_DIR_OUT].data_len) {
iser_err("Offset:%ld & DSL:%ld in Data-Out "
"inconsistent with total len:%ld, itt:%d\n",
buf_offset, data_seg_len,
- iser_ctask->data[ISER_DIR_OUT].data_len, itt);
+ iser_task->data[ISER_DIR_OUT].data_len, itt);
err = -EINVAL;
goto send_data_out_error;
}
@@ -470,10 +468,11 @@ send_data_out_error:
}
int iser_send_control(struct iscsi_conn *conn,
- struct iscsi_mgmt_task *mtask)
+ struct iscsi_task *task)
{
struct iscsi_iser_conn *iser_conn = conn->dd_data;
- struct iser_desc *mdesc = mtask->dd_data;
+ struct iscsi_iser_task *iser_task = task->dd_data;
+ struct iser_desc *mdesc = &iser_task->desc;
struct iser_dto *send_dto = NULL;
unsigned long data_seg_len;
int err = 0;
@@ -485,27 +484,27 @@ int iser_send_control(struct iscsi_conn *conn,
return -EPERM;
}
- if (iser_check_xmit(conn,mtask))
+ if (iser_check_xmit(conn, task))
return -ENOBUFS;
/* build the tx desc regd header and add it to the tx desc dto */
mdesc->type = ISCSI_TX_CONTROL;
send_dto = &mdesc->dto;
- send_dto->ctask = NULL;
+ send_dto->task = NULL;
iser_create_send_desc(iser_conn, mdesc);
device = iser_conn->ib_conn->device;
iser_reg_single(device, send_dto->regd[0], DMA_TO_DEVICE);
- data_seg_len = ntoh24(mtask->hdr->dlength);
+ data_seg_len = ntoh24(task->hdr->dlength);
if (data_seg_len > 0) {
regd_buf = &mdesc->data_regd_buf;
memset(regd_buf, 0, sizeof(struct iser_regd_buf));
regd_buf->device = device;
- regd_buf->virt_addr = mtask->data;
- regd_buf->data_size = mtask->data_count;
+ regd_buf->virt_addr = task->data;
+ regd_buf->data_size = task->data_count;
iser_reg_single(device, regd_buf,
DMA_TO_DEVICE);
iser_dto_add_regd_buff(send_dto, regd_buf,
@@ -535,15 +534,13 @@ send_control_error:
void iser_rcv_completion(struct iser_desc *rx_desc,
unsigned long dto_xfer_len)
{
- struct iser_dto *dto = &rx_desc->dto;
+ struct iser_dto *dto = &rx_desc->dto;
struct iscsi_iser_conn *conn = dto->ib_conn->iser_conn;
- struct iscsi_session *session = conn->iscsi_conn->session;
- struct iscsi_cmd_task *ctask;
- struct iscsi_iser_cmd_task *iser_ctask;
+ struct iscsi_task *task;
+ struct iscsi_iser_task *iser_task;
struct iscsi_hdr *hdr;
char *rx_data = NULL;
int rx_data_len = 0;
- unsigned int itt;
unsigned char opcode;
hdr = &rx_desc->iscsi_header;
@@ -559,19 +556,24 @@ void iser_rcv_completion(struct iser_desc *rx_desc,
opcode = hdr->opcode & ISCSI_OPCODE_MASK;
if (opcode == ISCSI_OP_SCSI_CMD_RSP) {
- itt = get_itt(hdr->itt); /* mask out cid and age bits */
- if (!(itt < session->cmds_max))
+ spin_lock(&conn->iscsi_conn->session->lock);
+ task = iscsi_itt_to_ctask(conn->iscsi_conn, hdr->itt);
+ if (task)
+ __iscsi_get_task(task);
+ spin_unlock(&conn->iscsi_conn->session->lock);
+
+ if (!task)
iser_err("itt can't be matched to task!!! "
- "conn %p opcode %d cmds_max %d itt %d\n",
- conn->iscsi_conn,opcode,session->cmds_max,itt);
- /* use the mapping given with the cmds array indexed by itt */
- ctask = (struct iscsi_cmd_task *)session->cmds[itt];
- iser_ctask = ctask->dd_data;
- iser_dbg("itt %d ctask %p\n",itt,ctask);
- iser_ctask->status = ISER_TASK_STATUS_COMPLETED;
- iser_ctask_rdma_finalize(iser_ctask);
+ "conn %p opcode %d itt %d\n",
+ conn->iscsi_conn, opcode, hdr->itt);
+ else {
+ iser_task = task->dd_data;
+ iser_dbg("itt %d task %p\n",hdr->itt, task);
+ iser_task->status = ISER_TASK_STATUS_COMPLETED;
+ iser_task_rdma_finalize(iser_task);
+ iscsi_put_task(task);
+ }
}
-
iser_dto_buffs_release(dto);
iscsi_iser_recv(conn->iscsi_conn, hdr, rx_data, rx_data_len);
@@ -592,7 +594,7 @@ void iser_snd_completion(struct iser_desc *tx_desc)
struct iser_conn *ib_conn = dto->ib_conn;
struct iscsi_iser_conn *iser_conn = ib_conn->iser_conn;
struct iscsi_conn *conn = iser_conn->iscsi_conn;
- struct iscsi_mgmt_task *mtask;
+ struct iscsi_task *task;
int resume_tx = 0;
iser_dbg("Initiator, Data sent dto=0x%p\n", dto);
@@ -615,36 +617,31 @@ void iser_snd_completion(struct iser_desc *tx_desc)
if (tx_desc->type == ISCSI_TX_CONTROL) {
/* this arithmetic is legal by libiscsi dd_data allocation */
- mtask = (void *) ((long)(void *)tx_desc -
- sizeof(struct iscsi_mgmt_task));
- if (mtask->hdr->itt == RESERVED_ITT) {
- struct iscsi_session *session = conn->session;
-
- spin_lock(&conn->session->lock);
- iscsi_free_mgmt_task(conn, mtask);
- spin_unlock(&session->lock);
- }
+ task = (void *) ((long)(void *)tx_desc -
+ sizeof(struct iscsi_task));
+ if (task->hdr->itt == RESERVED_ITT)
+ iscsi_put_task(task);
}
}
-void iser_ctask_rdma_init(struct iscsi_iser_cmd_task *iser_ctask)
+void iser_task_rdma_init(struct iscsi_iser_task *iser_task)
{
- iser_ctask->status = ISER_TASK_STATUS_INIT;
+ iser_task->status = ISER_TASK_STATUS_INIT;
- iser_ctask->dir[ISER_DIR_IN] = 0;
- iser_ctask->dir[ISER_DIR_OUT] = 0;
+ iser_task->dir[ISER_DIR_IN] = 0;
+ iser_task->dir[ISER_DIR_OUT] = 0;
- iser_ctask->data[ISER_DIR_IN].data_len = 0;
- iser_ctask->data[ISER_DIR_OUT].data_len = 0;
+ iser_task->data[ISER_DIR_IN].data_len = 0;
+ iser_task->data[ISER_DIR_OUT].data_len = 0;
- memset(&iser_ctask->rdma_regd[ISER_DIR_IN], 0,
+ memset(&iser_task->rdma_regd[ISER_DIR_IN], 0,
sizeof(struct iser_regd_buf));
- memset(&iser_ctask->rdma_regd[ISER_DIR_OUT], 0,
+ memset(&iser_task->rdma_regd[ISER_DIR_OUT], 0,
sizeof(struct iser_regd_buf));
}
-void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask)
+void iser_task_rdma_finalize(struct iscsi_iser_task *iser_task)
{
int deferred;
int is_rdma_aligned = 1;
@@ -653,17 +650,17 @@ void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask)
/* if we were reading, copy back to unaligned sglist,
* anyway dma_unmap and free the copy
*/
- if (iser_ctask->data_copy[ISER_DIR_IN].copy_buf != NULL) {
+ if (iser_task->data_copy[ISER_DIR_IN].copy_buf != NULL) {
is_rdma_aligned = 0;
- iser_finalize_rdma_unaligned_sg(iser_ctask, ISER_DIR_IN);
+ iser_finalize_rdma_unaligned_sg(iser_task, ISER_DIR_IN);
}
- if (iser_ctask->data_copy[ISER_DIR_OUT].copy_buf != NULL) {
+ if (iser_task->data_copy[ISER_DIR_OUT].copy_buf != NULL) {
is_rdma_aligned = 0;
- iser_finalize_rdma_unaligned_sg(iser_ctask, ISER_DIR_OUT);
+ iser_finalize_rdma_unaligned_sg(iser_task, ISER_DIR_OUT);
}
- if (iser_ctask->dir[ISER_DIR_IN]) {
- regd = &iser_ctask->rdma_regd[ISER_DIR_IN];
+ if (iser_task->dir[ISER_DIR_IN]) {
+ regd = &iser_task->rdma_regd[ISER_DIR_IN];
deferred = iser_regd_buff_release(regd);
if (deferred) {
iser_err("%d references remain for BUF-IN rdma reg\n",
@@ -671,8 +668,8 @@ void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask)
}
}
- if (iser_ctask->dir[ISER_DIR_OUT]) {
- regd = &iser_ctask->rdma_regd[ISER_DIR_OUT];
+ if (iser_task->dir[ISER_DIR_OUT]) {
+ regd = &iser_task->rdma_regd[ISER_DIR_OUT];
deferred = iser_regd_buff_release(regd);
if (deferred) {
iser_err("%d references remain for BUF-OUT rdma reg\n",
@@ -682,7 +679,7 @@ void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask)
/* if the data was unaligned, it was already unmapped and then copied */
if (is_rdma_aligned)
- iser_dma_unmap_task_data(iser_ctask);
+ iser_dma_unmap_task_data(iser_task);
}
void iser_dto_buffs_release(struct iser_dto *dto)
diff --git a/drivers/infiniband/ulp/iser/iser_memory.c b/drivers/infiniband/ulp/iser/iser_memory.c
index cac50c4dc159..b9453d068e9d 100644
--- a/drivers/infiniband/ulp/iser/iser_memory.c
+++ b/drivers/infiniband/ulp/iser/iser_memory.c
@@ -28,8 +28,6 @@
* 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.
- *
- * $Id: iser_memory.c 6964 2006-05-07 11:11:43Z ogerlitz $
*/
#include <linux/module.h>
#include <linux/kernel.h>
@@ -101,13 +99,13 @@ void iser_reg_single(struct iser_device *device,
/**
* iser_start_rdma_unaligned_sg
*/
-static int iser_start_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
+static int iser_start_rdma_unaligned_sg(struct iscsi_iser_task *iser_task,
enum iser_data_dir cmd_dir)
{
int dma_nents;
struct ib_device *dev;
char *mem = NULL;
- struct iser_data_buf *data = &iser_ctask->data[cmd_dir];
+ struct iser_data_buf *data = &iser_task->data[cmd_dir];
unsigned long cmd_data_len = data->data_len;
if (cmd_data_len > ISER_KMALLOC_THRESHOLD)
@@ -140,37 +138,37 @@ static int iser_start_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
}
}
- sg_init_one(&iser_ctask->data_copy[cmd_dir].sg_single, mem, cmd_data_len);
- iser_ctask->data_copy[cmd_dir].buf =
- &iser_ctask->data_copy[cmd_dir].sg_single;
- iser_ctask->data_copy[cmd_dir].size = 1;
+ sg_init_one(&iser_task->data_copy[cmd_dir].sg_single, mem, cmd_data_len);
+ iser_task->data_copy[cmd_dir].buf =
+ &iser_task->data_copy[cmd_dir].sg_single;
+ iser_task->data_copy[cmd_dir].size = 1;
- iser_ctask->data_copy[cmd_dir].copy_buf = mem;
+ iser_task->data_copy[cmd_dir].copy_buf = mem;
- dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
+ dev = iser_task->iser_conn->ib_conn->device->ib_device;
dma_nents = ib_dma_map_sg(dev,
- &iser_ctask->data_copy[cmd_dir].sg_single,
+ &iser_task->data_copy[cmd_dir].sg_single,
1,
(cmd_dir == ISER_DIR_OUT) ?
DMA_TO_DEVICE : DMA_FROM_DEVICE);
BUG_ON(dma_nents == 0);
- iser_ctask->data_copy[cmd_dir].dma_nents = dma_nents;
+ iser_task->data_copy[cmd_dir].dma_nents = dma_nents;
return 0;
}
/**
* iser_finalize_rdma_unaligned_sg
*/
-void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
+void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_task *iser_task,
enum iser_data_dir cmd_dir)
{
struct ib_device *dev;
struct iser_data_buf *mem_copy;
unsigned long cmd_data_len;
- dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
- mem_copy = &iser_ctask->data_copy[cmd_dir];
+ dev = iser_task->iser_conn->ib_conn->device->ib_device;
+ mem_copy = &iser_task->data_copy[cmd_dir];
ib_dma_unmap_sg(dev, &mem_copy->sg_single, 1,
(cmd_dir == ISER_DIR_OUT) ?
@@ -186,8 +184,8 @@ void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
/* copy back read RDMA to unaligned sg */
mem = mem_copy->copy_buf;
- sgl = (struct scatterlist *)iser_ctask->data[ISER_DIR_IN].buf;
- sg_size = iser_ctask->data[ISER_DIR_IN].size;
+ sgl = (struct scatterlist *)iser_task->data[ISER_DIR_IN].buf;
+ sg_size = iser_task->data[ISER_DIR_IN].size;
p = mem;
for_each_sg(sgl, sg, sg_size, i) {
@@ -200,7 +198,7 @@ void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
}
}
- cmd_data_len = iser_ctask->data[cmd_dir].data_len;
+ cmd_data_len = iser_task->data[cmd_dir].data_len;
if (cmd_data_len > ISER_KMALLOC_THRESHOLD)
free_pages((unsigned long)mem_copy->copy_buf,
@@ -378,15 +376,15 @@ static void iser_page_vec_build(struct iser_data_buf *data,
}
}
-int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask,
- struct iser_data_buf *data,
- enum iser_data_dir iser_dir,
- enum dma_data_direction dma_dir)
+int iser_dma_map_task_data(struct iscsi_iser_task *iser_task,
+ struct iser_data_buf *data,
+ enum iser_data_dir iser_dir,
+ enum dma_data_direction dma_dir)
{
struct ib_device *dev;
- iser_ctask->dir[iser_dir] = 1;
- dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
+ iser_task->dir[iser_dir] = 1;
+ dev = iser_task->iser_conn->ib_conn->device->ib_device;
data->dma_nents = ib_dma_map_sg(dev, data->buf, data->size, dma_dir);
if (data->dma_nents == 0) {
@@ -396,20 +394,20 @@ int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask,
return 0;
}
-void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask)
+void iser_dma_unmap_task_data(struct iscsi_iser_task *iser_task)
{
struct ib_device *dev;
struct iser_data_buf *data;
- dev = iser_ctask->iser_conn->ib_conn->device->ib_device;
+ dev = iser_task->iser_conn->ib_conn->device->ib_device;
- if (iser_ctask->dir[ISER_DIR_IN]) {
- data = &iser_ctask->data[ISER_DIR_IN];
+ if (iser_task->dir[ISER_DIR_IN]) {
+ data = &iser_task->data[ISER_DIR_IN];
ib_dma_unmap_sg(dev, data->buf, data->size, DMA_FROM_DEVICE);
}
- if (iser_ctask->dir[ISER_DIR_OUT]) {
- data = &iser_ctask->data[ISER_DIR_OUT];
+ if (iser_task->dir[ISER_DIR_OUT]) {
+ data = &iser_task->data[ISER_DIR_OUT];
ib_dma_unmap_sg(dev, data->buf, data->size, DMA_TO_DEVICE);
}
}
@@ -420,21 +418,21 @@ void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask)
*
* returns 0 on success, errno code on failure
*/
-int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *iser_ctask,
+int iser_reg_rdma_mem(struct iscsi_iser_task *iser_task,
enum iser_data_dir cmd_dir)
{
- struct iscsi_conn *iscsi_conn = iser_ctask->iser_conn->iscsi_conn;
- struct iser_conn *ib_conn = iser_ctask->iser_conn->ib_conn;
+ struct iscsi_conn *iscsi_conn = iser_task->iser_conn->iscsi_conn;
+ struct iser_conn *ib_conn = iser_task->iser_conn->ib_conn;
struct iser_device *device = ib_conn->device;
struct ib_device *ibdev = device->ib_device;
- struct iser_data_buf *mem = &iser_ctask->data[cmd_dir];
+ struct iser_data_buf *mem = &iser_task->data[cmd_dir];
struct iser_regd_buf *regd_buf;
int aligned_len;
int err;
int i;
struct scatterlist *sg;
- regd_buf = &iser_ctask->rdma_regd[cmd_dir];
+ regd_buf = &iser_task->rdma_regd[cmd_dir];
aligned_len = iser_data_buf_aligned_len(mem, ibdev);
if (aligned_len != mem->dma_nents) {
@@ -444,13 +442,13 @@ int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *iser_ctask,
iser_data_buf_dump(mem, ibdev);
/* unmap the command data before accessing it */
- iser_dma_unmap_task_data(iser_ctask);
+ iser_dma_unmap_task_data(iser_task);
/* allocate copy buf, if we are writing, copy the */
/* unaligned scatterlist, dma map the copy */
- if (iser_start_rdma_unaligned_sg(iser_ctask, cmd_dir) != 0)
+ if (iser_start_rdma_unaligned_sg(iser_task, cmd_dir) != 0)
return -ENOMEM;
- mem = &iser_ctask->data_copy[cmd_dir];
+ mem = &iser_task->data_copy[cmd_dir];
}
/* if there a single dma entry, FMR is not needed */
@@ -474,8 +472,9 @@ int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *iser_ctask,
err = iser_reg_page_vec(ib_conn, ib_conn->page_vec, &regd_buf->reg);
if (err) {
iser_data_buf_dump(mem, ibdev);
- iser_err("mem->dma_nents = %d (dlength = 0x%x)\n", mem->dma_nents,
- ntoh24(iser_ctask->desc.iscsi_header.dlength));
+ iser_err("mem->dma_nents = %d (dlength = 0x%x)\n",
+ mem->dma_nents,
+ ntoh24(iser_task->desc.iscsi_header.dlength));
iser_err("page_vec: data_size = 0x%x, length = %d, offset = 0x%x\n",
ib_conn->page_vec->data_size, ib_conn->page_vec->length,
ib_conn->page_vec->offset);
diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c
index d19cfe605ebb..3a917c1f796f 100644
--- a/drivers/infiniband/ulp/iser/iser_verbs.c
+++ b/drivers/infiniband/ulp/iser/iser_verbs.c
@@ -29,8 +29,6 @@
* 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.
- *
- * $Id: iser_verbs.c 7051 2006-05-10 12:29:11Z ogerlitz $
*/
#include <linux/kernel.h>
#include <linux/module.h>
@@ -325,7 +323,18 @@ static void iser_conn_release(struct iser_conn *ib_conn)
iser_device_try_release(device);
if (ib_conn->iser_conn)
ib_conn->iser_conn->ib_conn = NULL;
- kfree(ib_conn);
+ iscsi_destroy_endpoint(ib_conn->ep);
+}
+
+void iser_conn_get(struct iser_conn *ib_conn)
+{
+ atomic_inc(&ib_conn->refcount);
+}
+
+void iser_conn_put(struct iser_conn *ib_conn)
+{
+ if (atomic_dec_and_test(&ib_conn->refcount))
+ iser_conn_release(ib_conn);
}
/**
@@ -349,7 +358,7 @@ void iser_conn_terminate(struct iser_conn *ib_conn)
wait_event_interruptible(ib_conn->wait,
ib_conn->state == ISER_CONN_DOWN);
- iser_conn_release(ib_conn);
+ iser_conn_put(ib_conn);
}
static void iser_connect_error(struct rdma_cm_id *cma_id)
@@ -483,24 +492,15 @@ static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *eve
return ret;
}
-int iser_conn_init(struct iser_conn **ibconn)
+void iser_conn_init(struct iser_conn *ib_conn)
{
- struct iser_conn *ib_conn;
-
- ib_conn = kzalloc(sizeof *ib_conn, GFP_KERNEL);
- if (!ib_conn) {
- iser_err("can't alloc memory for struct iser_conn\n");
- return -ENOMEM;
- }
ib_conn->state = ISER_CONN_INIT;
init_waitqueue_head(&ib_conn->wait);
atomic_set(&ib_conn->post_recv_buf_count, 0);
atomic_set(&ib_conn->post_send_buf_count, 0);
+ atomic_set(&ib_conn->refcount, 1);
INIT_LIST_HEAD(&ib_conn->conn_list);
spin_lock_init(&ib_conn->lock);
-
- *ibconn = ib_conn;
- return 0;
}
/**
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index 435145709dd6..ed7c5f72cb8b 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -28,8 +28,6 @@
* 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.
- *
- * $Id: ib_srp.c 3932 2005-11-01 17:19:29Z roland $
*/
#include <linux/module.h>
@@ -49,8 +47,6 @@
#include <scsi/srp.h>
#include <scsi/scsi_transport_srp.h>
-#include <rdma/ib_cache.h>
-
#include "ib_srp.h"
#define DRV_NAME "ib_srp"
@@ -183,10 +179,10 @@ static int srp_init_qp(struct srp_target_port *target,
if (!attr)
return -ENOMEM;
- ret = ib_find_cached_pkey(target->srp_host->srp_dev->dev,
- target->srp_host->port,
- be16_to_cpu(target->path.pkey),
- &attr->pkey_index);
+ ret = ib_find_pkey(target->srp_host->srp_dev->dev,
+ target->srp_host->port,
+ be16_to_cpu(target->path.pkey),
+ &attr->pkey_index);
if (ret)
goto out;
@@ -1883,8 +1879,7 @@ static ssize_t srp_create_target(struct device *dev,
if (ret)
goto err;
- ib_get_cached_gid(host->srp_dev->dev, host->port, 0,
- &target->path.sgid);
+ ib_query_gid(host->srp_dev->dev, host->port, 0, &target->path.sgid);
shost_printk(KERN_DEBUG, target->scsi_host, PFX
"new target: id_ext %016llx ioc_guid %016llx pkey %04x "
diff --git a/drivers/infiniband/ulp/srp/ib_srp.h b/drivers/infiniband/ulp/srp/ib_srp.h
index 63d2ae724061..e185b907fc12 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.h
+++ b/drivers/infiniband/ulp/srp/ib_srp.h
@@ -28,8 +28,6 @@
* 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.
- *
- * $Id: ib_srp.h 3932 2005-11-01 17:19:29Z roland $
*/
#ifndef IB_SRP_H
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 3ad8bd9f7543..432699d61c58 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -15,7 +15,6 @@ if INPUT_MISC
config INPUT_PCSPKR
tristate "PC Speaker support"
depends on PCSPKR_PLATFORM
- depends on SND_PCSP=n
help
Say Y here if you want the standard PC Speaker to be used for
bells and whistles.
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index 9aafa96cb746..78eb7841174c 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -193,6 +193,13 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
},
},
{
+ .ident = "Fujitsu-Siemens Amilo Pro 2030",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO PRO V2030"),
+ },
+ },
+ {
/*
* No data is coming from the touchscreen unless KBC
* is in legacy mode.
diff --git a/drivers/input/xen-kbdfront.c b/drivers/input/xen-kbdfront.c
index 0f47f4697cdf..9ce3b3baf3a2 100644
--- a/drivers/input/xen-kbdfront.c
+++ b/drivers/input/xen-kbdfront.c
@@ -66,6 +66,9 @@ static irqreturn_t input_handler(int rq, void *dev_id)
case XENKBD_TYPE_MOTION:
input_report_rel(dev, REL_X, event->motion.rel_x);
input_report_rel(dev, REL_Y, event->motion.rel_y);
+ if (event->motion.rel_z)
+ input_report_rel(dev, REL_WHEEL,
+ -event->motion.rel_z);
break;
case XENKBD_TYPE_KEY:
dev = NULL;
@@ -84,6 +87,9 @@ static irqreturn_t input_handler(int rq, void *dev_id)
case XENKBD_TYPE_POS:
input_report_abs(dev, ABS_X, event->pos.abs_x);
input_report_abs(dev, ABS_Y, event->pos.abs_y);
+ if (event->pos.rel_z)
+ input_report_rel(dev, REL_WHEEL,
+ -event->pos.rel_z);
break;
}
if (dev)
@@ -152,7 +158,7 @@ static int __devinit xenkbd_probe(struct xenbus_device *dev,
ptr->evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS);
for (i = BTN_LEFT; i <= BTN_TASK; i++)
set_bit(i, ptr->keybit);
- ptr->relbit[0] = BIT(REL_X) | BIT(REL_Y);
+ ptr->relbit[0] = BIT(REL_X) | BIT(REL_Y) | BIT(REL_WHEEL);
input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0);
input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0);
@@ -294,6 +300,16 @@ InitWait:
*/
if (dev->state != XenbusStateConnected)
goto InitWait; /* no InitWait seen yet, fudge it */
+
+ /* Set input abs params to match backend screen res */
+ if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+ "width", "%d", &val) > 0)
+ input_set_abs_params(info->ptr, ABS_X, 0, val, 0, 0);
+
+ if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+ "height", "%d", &val) > 0)
+ input_set_abs_params(info->ptr, ABS_Y, 0, val, 0, 0);
+
break;
case XenbusStateClosing:
@@ -337,4 +353,6 @@ static void __exit xenkbd_cleanup(void)
module_init(xenkbd_init);
module_exit(xenkbd_cleanup);
+MODULE_DESCRIPTION("Xen virtual keyboard/pointer device frontend");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("xen:vkbd");
diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c
index 6ca0bb949ad3..bac01628e6fd 100644
--- a/drivers/isdn/capi/capi.c
+++ b/drivers/isdn/capi/capi.c
@@ -1547,7 +1547,8 @@ static int __init capi_init(void)
return PTR_ERR(capi_class);
}
- device_create(capi_class, NULL, MKDEV(capi_major, 0), "capi");
+ device_create_drvdata(capi_class, NULL, MKDEV(capi_major, 0), NULL,
+ "capi");
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
if (capinc_tty_init() < 0) {
diff --git a/drivers/isdn/hardware/eicon/divasmain.c b/drivers/isdn/hardware/eicon/divasmain.c
index 5fcbdccd7a53..16a874bb1561 100644
--- a/drivers/isdn/hardware/eicon/divasmain.c
+++ b/drivers/isdn/hardware/eicon/divasmain.c
@@ -806,7 +806,6 @@ static int DIVA_INIT_FUNCTION divas_init(void)
if (!create_divas_proc()) {
#ifdef MODULE
- remove_divas_proc();
divas_unregister_chrdev();
divasfunc_exit();
#endif
diff --git a/drivers/isdn/hardware/eicon/divasproc.c b/drivers/isdn/hardware/eicon/divasproc.c
index fae895828a17..040827288ec9 100644
--- a/drivers/isdn/hardware/eicon/divasproc.c
+++ b/drivers/isdn/hardware/eicon/divasproc.c
@@ -125,8 +125,8 @@ static const struct file_operations divas_fops = {
int create_divas_proc(void)
{
- proc_create(divas_proc_name, S_IFREG | S_IRUGO, proc_net_eicon,
- &divas_fops);
+ divas_proc_entry = proc_create(divas_proc_name, S_IFREG | S_IRUGO,
+ proc_net_eicon, &divas_fops);
if (!divas_proc_entry)
return (0);
diff --git a/drivers/isdn/hysdn/hysdn_procconf.c b/drivers/isdn/hysdn/hysdn_procconf.c
index 15906d005b05..484299b031f8 100644
--- a/drivers/isdn/hysdn/hysdn_procconf.c
+++ b/drivers/isdn/hysdn/hysdn_procconf.c
@@ -207,30 +207,17 @@ hysdn_conf_write(struct file *file, const char __user *buf, size_t count, loff_t
/* read conf file -> output card info data */
/*******************************************/
static ssize_t
-hysdn_conf_read(struct file *file, char __user *buf, size_t count, loff_t * off)
+hysdn_conf_read(struct file *file, char __user *buf, size_t count, loff_t *off)
{
char *cp;
- int i;
- if (file->f_mode & FMODE_READ) {
- if (!(cp = file->private_data))
- return (-EFAULT); /* should never happen */
- i = strlen(cp); /* get total string length */
- if (*off < i) {
- /* still bytes to transfer */
- cp += *off; /* point to desired data offset */
- i -= *off; /* remaining length */
- if (i > count)
- i = count; /* limit length to transfer */
- if (copy_to_user(buf, cp, i))
- return (-EFAULT); /* copy error */
- *off += i; /* adjust offset */
- } else
- return (0);
- } else
- return (-EPERM); /* no permission to read */
-
- return (i);
+ if (!(file->f_mode & FMODE_READ))
+ return -EPERM; /* no permission to read */
+
+ if (!(cp = file->private_data))
+ return -EFAULT; /* should never happen */
+
+ return simple_read_from_buffer(buf, count, off, cp, strlen(cp));
} /* hysdn_conf_read */
/******************/
diff --git a/drivers/lguest/lg.h b/drivers/lguest/lg.h
index 005bd045d2eb..5faefeaf6790 100644
--- a/drivers/lguest/lg.h
+++ b/drivers/lguest/lg.h
@@ -136,7 +136,6 @@ int run_guest(struct lg_cpu *cpu, unsigned long __user *user);
* first step in the migration to the kernel types. pte_pfn is already defined
* in the kernel. */
#define pgd_flags(x) (pgd_val(x) & ~PAGE_MASK)
-#define pte_flags(x) (pte_val(x) & ~PAGE_MASK)
#define pgd_pfn(x) (pgd_val(x) >> PAGE_SHIFT)
/* interrupts_and_traps.c: */
diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c
index dbaad39020a1..85a78672e952 100644
--- a/drivers/macintosh/adb.c
+++ b/drivers/macintosh/adb.c
@@ -855,7 +855,8 @@ adbdev_init(void)
adb_dev_class = class_create(THIS_MODULE, "adb");
if (IS_ERR(adb_dev_class))
return;
- device_create(adb_dev_class, NULL, MKDEV(ADB_MAJOR, 0), "adb");
+ device_create_drvdata(adb_dev_class, NULL, MKDEV(ADB_MAJOR, 0), NULL,
+ "adb");
platform_device_register(&adb_pfdev);
platform_driver_probe(&adb_pfdrv, adb_dummy_probe);
diff --git a/drivers/macintosh/mediabay.c b/drivers/macintosh/mediabay.c
index 82add26cc665..c34bdf852e32 100644
--- a/drivers/macintosh/mediabay.c
+++ b/drivers/macintosh/mediabay.c
@@ -556,7 +556,8 @@ static void media_bay_step(int i)
printk("mediabay %d, registering IDE...\n", i);
pmu_suspend();
ide_port_scan(bay->cd_port);
- bay->cd_index = bay->cd_port->index;
+ if (bay->cd_port->present)
+ bay->cd_index = bay->cd_port->index;
pmu_resume();
}
if (bay->cd_index == -1) {
diff --git a/drivers/mca/mca-bus.c b/drivers/mca/mca-bus.c
index 67b8e9453b19..ef2dbfe74714 100644
--- a/drivers/mca/mca-bus.c
+++ b/drivers/mca/mca-bus.c
@@ -40,7 +40,7 @@ static struct mca_bus *mca_root_busses[MAX_MCA_BUSSES];
struct mca_device_info {
short pos_id; /* the 2 byte pos id for this card */
- char name[DEVICE_NAME_SIZE];
+ char name[50];
};
static int mca_bus_match (struct device *dev, struct device_driver *drv)
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 610af916891e..b4a3c7d1451d 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -252,27 +252,10 @@ config DM_ZERO
config DM_MULTIPATH
tristate "Multipath target"
depends on BLK_DEV_DM
+ select SCSI_DH
---help---
Allow volume managers to support multipath hardware.
-config DM_MULTIPATH_EMC
- tristate "EMC CX/AX multipath support"
- depends on DM_MULTIPATH && BLK_DEV_DM
- ---help---
- Multipath support for EMC CX/AX series hardware.
-
-config DM_MULTIPATH_RDAC
- tristate "LSI/Engenio RDAC multipath support (EXPERIMENTAL)"
- depends on DM_MULTIPATH && BLK_DEV_DM && SCSI && EXPERIMENTAL
- ---help---
- Multipath support for LSI/Engenio RDAC.
-
-config DM_MULTIPATH_HP
- tristate "HP MSA multipath support (EXPERIMENTAL)"
- depends on DM_MULTIPATH && BLK_DEV_DM && SCSI && EXPERIMENTAL
- ---help---
- Multipath support for HP MSA (Active/Passive) series hardware.
-
config DM_DELAY
tristate "I/O delaying target (EXPERIMENTAL)"
depends on BLK_DEV_DM && EXPERIMENTAL
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index 7be09eeea293..f1ef33dfd8cf 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -4,11 +4,9 @@
dm-mod-objs := dm.o dm-table.o dm-target.o dm-linear.o dm-stripe.o \
dm-ioctl.o dm-io.o dm-kcopyd.o
-dm-multipath-objs := dm-hw-handler.o dm-path-selector.o dm-mpath.o
+dm-multipath-objs := dm-path-selector.o dm-mpath.o
dm-snapshot-objs := dm-snap.o dm-exception-store.o
dm-mirror-objs := dm-raid1.o
-dm-rdac-objs := dm-mpath-rdac.o
-dm-hp-sw-objs := dm-mpath-hp-sw.o
md-mod-objs := md.o bitmap.o
raid456-objs := raid5.o raid6algos.o raid6recov.o raid6tables.o \
raid6int1.o raid6int2.o raid6int4.o \
@@ -35,9 +33,6 @@ obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o
obj-$(CONFIG_DM_CRYPT) += dm-crypt.o
obj-$(CONFIG_DM_DELAY) += dm-delay.o
obj-$(CONFIG_DM_MULTIPATH) += dm-multipath.o dm-round-robin.o
-obj-$(CONFIG_DM_MULTIPATH_EMC) += dm-emc.o
-obj-$(CONFIG_DM_MULTIPATH_HP) += dm-hp-sw.o
-obj-$(CONFIG_DM_MULTIPATH_RDAC) += dm-rdac.o
obj-$(CONFIG_DM_SNAPSHOT) += dm-snapshot.o
obj-$(CONFIG_DM_MIRROR) += dm-mirror.o dm-log.o
obj-$(CONFIG_DM_ZERO) += dm-zero.o
diff --git a/drivers/md/dm-emc.c b/drivers/md/dm-emc.c
deleted file mode 100644
index 3ea5ad4b7805..000000000000
--- a/drivers/md/dm-emc.c
+++ /dev/null
@@ -1,345 +0,0 @@
-/*
- * Copyright (C) 2004 SUSE LINUX Products GmbH. All rights reserved.
- * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
- *
- * This file is released under the GPL.
- *
- * Multipath support for EMC CLARiiON AX/CX-series hardware.
- */
-
-#include "dm.h"
-#include "dm-hw-handler.h"
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-
-#define DM_MSG_PREFIX "multipath emc"
-
-struct emc_handler {
- spinlock_t lock;
-
- /* Whether we should send the short trespass command (FC-series)
- * or the long version (default for AX/CX CLARiiON arrays). */
- unsigned short_trespass;
- /* Whether or not to honor SCSI reservations when initiating a
- * switch-over. Default: Don't. */
- unsigned hr;
-
- unsigned char sense[SCSI_SENSE_BUFFERSIZE];
-};
-
-#define TRESPASS_PAGE 0x22
-#define EMC_FAILOVER_TIMEOUT (60 * HZ)
-
-/* Code borrowed from dm-lsi-rdac by Mike Christie */
-
-static inline void free_bio(struct bio *bio)
-{
- __free_page(bio->bi_io_vec[0].bv_page);
- bio_put(bio);
-}
-
-static void emc_endio(struct bio *bio, int error)
-{
- struct dm_path *path = bio->bi_private;
-
- /* We also need to look at the sense keys here whether or not to
- * switch to the next PG etc.
- *
- * For now simple logic: either it works or it doesn't.
- */
- if (error)
- dm_pg_init_complete(path, MP_FAIL_PATH);
- else
- dm_pg_init_complete(path, 0);
-
- /* request is freed in block layer */
- free_bio(bio);
-}
-
-static struct bio *get_failover_bio(struct dm_path *path, unsigned data_size)
-{
- struct bio *bio;
- struct page *page;
-
- bio = bio_alloc(GFP_ATOMIC, 1);
- if (!bio) {
- DMERR("get_failover_bio: bio_alloc() failed.");
- return NULL;
- }
-
- bio->bi_rw |= (1 << BIO_RW);
- bio->bi_bdev = path->dev->bdev;
- bio->bi_sector = 0;
- bio->bi_private = path;
- bio->bi_end_io = emc_endio;
-
- page = alloc_page(GFP_ATOMIC);
- if (!page) {
- DMERR("get_failover_bio: alloc_page() failed.");
- bio_put(bio);
- return NULL;
- }
-
- if (bio_add_page(bio, page, data_size, 0) != data_size) {
- DMERR("get_failover_bio: bio_add_page() failed.");
- __free_page(page);
- bio_put(bio);
- return NULL;
- }
-
- return bio;
-}
-
-static struct request *get_failover_req(struct emc_handler *h,
- struct bio *bio, struct dm_path *path)
-{
- struct request *rq;
- struct block_device *bdev = bio->bi_bdev;
- struct request_queue *q = bdev_get_queue(bdev);
-
- /* FIXME: Figure out why it fails with GFP_ATOMIC. */
- rq = blk_get_request(q, WRITE, __GFP_WAIT);
- if (!rq) {
- DMERR("get_failover_req: blk_get_request failed");
- return NULL;
- }
-
- blk_rq_append_bio(q, rq, bio);
-
- rq->sense = h->sense;
- memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
- rq->sense_len = 0;
-
- rq->timeout = EMC_FAILOVER_TIMEOUT;
- rq->cmd_type = REQ_TYPE_BLOCK_PC;
- rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
-
- return rq;
-}
-
-static struct request *emc_trespass_get(struct emc_handler *h,
- struct dm_path *path)
-{
- struct bio *bio;
- struct request *rq;
- unsigned char *page22;
- unsigned char long_trespass_pg[] = {
- 0, 0, 0, 0,
- TRESPASS_PAGE, /* Page code */
- 0x09, /* Page length - 2 */
- h->hr ? 0x01 : 0x81, /* Trespass code + Honor reservation bit */
- 0xff, 0xff, /* Trespass target */
- 0, 0, 0, 0, 0, 0 /* Reserved bytes / unknown */
- };
- unsigned char short_trespass_pg[] = {
- 0, 0, 0, 0,
- TRESPASS_PAGE, /* Page code */
- 0x02, /* Page length - 2 */
- h->hr ? 0x01 : 0x81, /* Trespass code + Honor reservation bit */
- 0xff, /* Trespass target */
- };
- unsigned data_size = h->short_trespass ? sizeof(short_trespass_pg) :
- sizeof(long_trespass_pg);
-
- /* get bio backing */
- if (data_size > PAGE_SIZE)
- /* this should never happen */
- return NULL;
-
- bio = get_failover_bio(path, data_size);
- if (!bio) {
- DMERR("emc_trespass_get: no bio");
- return NULL;
- }
-
- page22 = (unsigned char *)bio_data(bio);
- memset(page22, 0, data_size);
-
- memcpy(page22, h->short_trespass ?
- short_trespass_pg : long_trespass_pg, data_size);
-
- /* get request for block layer packet command */
- rq = get_failover_req(h, bio, path);
- if (!rq) {
- DMERR("emc_trespass_get: no rq");
- free_bio(bio);
- return NULL;
- }
-
- /* Prepare the command. */
- rq->cmd[0] = MODE_SELECT;
- rq->cmd[1] = 0x10;
- rq->cmd[4] = data_size;
- rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
-
- return rq;
-}
-
-static void emc_pg_init(struct hw_handler *hwh, unsigned bypassed,
- struct dm_path *path)
-{
- struct request *rq;
- struct request_queue *q = bdev_get_queue(path->dev->bdev);
-
- /*
- * We can either blindly init the pg (then look at the sense),
- * or we can send some commands to get the state here (then
- * possibly send the fo cmnd), or we can also have the
- * initial state passed into us and then get an update here.
- */
- if (!q) {
- DMINFO("emc_pg_init: no queue");
- goto fail_path;
- }
-
- /* FIXME: The request should be pre-allocated. */
- rq = emc_trespass_get(hwh->context, path);
- if (!rq) {
- DMERR("emc_pg_init: no rq");
- goto fail_path;
- }
-
- DMINFO("emc_pg_init: sending switch-over command");
- elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 1);
- return;
-
-fail_path:
- dm_pg_init_complete(path, MP_FAIL_PATH);
-}
-
-static struct emc_handler *alloc_emc_handler(void)
-{
- struct emc_handler *h = kzalloc(sizeof(*h), GFP_KERNEL);
-
- if (h)
- spin_lock_init(&h->lock);
-
- return h;
-}
-
-static int emc_create(struct hw_handler *hwh, unsigned argc, char **argv)
-{
- struct emc_handler *h;
- unsigned hr, short_trespass;
-
- if (argc == 0) {
- /* No arguments: use defaults */
- hr = 0;
- short_trespass = 0;
- } else if (argc != 2) {
- DMWARN("incorrect number of arguments");
- return -EINVAL;
- } else {
- if ((sscanf(argv[0], "%u", &short_trespass) != 1)
- || (short_trespass > 1)) {
- DMWARN("invalid trespass mode selected");
- return -EINVAL;
- }
-
- if ((sscanf(argv[1], "%u", &hr) != 1)
- || (hr > 1)) {
- DMWARN("invalid honor reservation flag selected");
- return -EINVAL;
- }
- }
-
- h = alloc_emc_handler();
- if (!h)
- return -ENOMEM;
-
- hwh->context = h;
-
- if ((h->short_trespass = short_trespass))
- DMWARN("short trespass command will be send");
- else
- DMWARN("long trespass command will be send");
-
- if ((h->hr = hr))
- DMWARN("honor reservation bit will be set");
- else
- DMWARN("honor reservation bit will not be set (default)");
-
- return 0;
-}
-
-static void emc_destroy(struct hw_handler *hwh)
-{
- struct emc_handler *h = (struct emc_handler *) hwh->context;
-
- kfree(h);
- hwh->context = NULL;
-}
-
-static unsigned emc_error(struct hw_handler *hwh, struct bio *bio)
-{
- /* FIXME: Patch from axboe still missing */
-#if 0
- int sense;
-
- if (bio->bi_error & BIO_SENSE) {
- sense = bio->bi_error & 0xffffff; /* sense key / asc / ascq */
-
- if (sense == 0x020403) {
- /* LUN Not Ready - Manual Intervention Required
- * indicates this is a passive path.
- *
- * FIXME: However, if this is seen and EVPD C0
- * indicates that this is due to a NDU in
- * progress, we should set FAIL_PATH too.
- * This indicates we might have to do a SCSI
- * inquiry in the end_io path. Ugh. */
- return MP_BYPASS_PG | MP_RETRY_IO;
- } else if (sense == 0x052501) {
- /* An array based copy is in progress. Do not
- * fail the path, do not bypass to another PG,
- * do not retry. Fail the IO immediately.
- * (Actually this is the same conclusion as in
- * the default handler, but lets make sure.) */
- return 0;
- } else if (sense == 0x062900) {
- /* Unit Attention Code. This is the first IO
- * to the new path, so just retry. */
- return MP_RETRY_IO;
- }
- }
-#endif
-
- /* Try default handler */
- return dm_scsi_err_handler(hwh, bio);
-}
-
-static struct hw_handler_type emc_hwh = {
- .name = "emc",
- .module = THIS_MODULE,
- .create = emc_create,
- .destroy = emc_destroy,
- .pg_init = emc_pg_init,
- .error = emc_error,
-};
-
-static int __init dm_emc_init(void)
-{
- int r = dm_register_hw_handler(&emc_hwh);
-
- if (r < 0)
- DMERR("register failed %d", r);
-
- DMINFO("version 0.0.3 loaded");
-
- return r;
-}
-
-static void __exit dm_emc_exit(void)
-{
- int r = dm_unregister_hw_handler(&emc_hwh);
-
- if (r < 0)
- DMERR("unregister failed %d", r);
-}
-
-module_init(dm_emc_init);
-module_exit(dm_emc_exit);
-
-MODULE_DESCRIPTION(DM_NAME " EMC CX/AX/FC-family multipath");
-MODULE_AUTHOR("Lars Marowsky-Bree <lmb@suse.de>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/md/dm-hw-handler.c b/drivers/md/dm-hw-handler.c
deleted file mode 100644
index 2ee84d8aa0bf..000000000000
--- a/drivers/md/dm-hw-handler.c
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
- *
- * This file is released under the GPL.
- *
- * Multipath hardware handler registration.
- */
-
-#include "dm.h"
-#include "dm-hw-handler.h"
-
-#include <linux/slab.h>
-
-struct hwh_internal {
- struct hw_handler_type hwht;
-
- struct list_head list;
- long use;
-};
-
-#define hwht_to_hwhi(__hwht) container_of((__hwht), struct hwh_internal, hwht)
-
-static LIST_HEAD(_hw_handlers);
-static DECLARE_RWSEM(_hwh_lock);
-
-static struct hwh_internal *__find_hw_handler_type(const char *name)
-{
- struct hwh_internal *hwhi;
-
- list_for_each_entry(hwhi, &_hw_handlers, list) {
- if (!strcmp(name, hwhi->hwht.name))
- return hwhi;
- }
-
- return NULL;
-}
-
-static struct hwh_internal *get_hw_handler(const char *name)
-{
- struct hwh_internal *hwhi;
-
- down_read(&_hwh_lock);
- hwhi = __find_hw_handler_type(name);
- if (hwhi) {
- if ((hwhi->use == 0) && !try_module_get(hwhi->hwht.module))
- hwhi = NULL;
- else
- hwhi->use++;
- }
- up_read(&_hwh_lock);
-
- return hwhi;
-}
-
-struct hw_handler_type *dm_get_hw_handler(const char *name)
-{
- struct hwh_internal *hwhi;
-
- if (!name)
- return NULL;
-
- hwhi = get_hw_handler(name);
- if (!hwhi) {
- request_module("dm-%s", name);
- hwhi = get_hw_handler(name);
- }
-
- return hwhi ? &hwhi->hwht : NULL;
-}
-
-void dm_put_hw_handler(struct hw_handler_type *hwht)
-{
- struct hwh_internal *hwhi;
-
- if (!hwht)
- return;
-
- down_read(&_hwh_lock);
- hwhi = __find_hw_handler_type(hwht->name);
- if (!hwhi)
- goto out;
-
- if (--hwhi->use == 0)
- module_put(hwhi->hwht.module);
-
- BUG_ON(hwhi->use < 0);
-
- out:
- up_read(&_hwh_lock);
-}
-
-static struct hwh_internal *_alloc_hw_handler(struct hw_handler_type *hwht)
-{
- struct hwh_internal *hwhi = kzalloc(sizeof(*hwhi), GFP_KERNEL);
-
- if (hwhi)
- hwhi->hwht = *hwht;
-
- return hwhi;
-}
-
-int dm_register_hw_handler(struct hw_handler_type *hwht)
-{
- int r = 0;
- struct hwh_internal *hwhi = _alloc_hw_handler(hwht);
-
- if (!hwhi)
- return -ENOMEM;
-
- down_write(&_hwh_lock);
-
- if (__find_hw_handler_type(hwht->name)) {
- kfree(hwhi);
- r = -EEXIST;
- } else
- list_add(&hwhi->list, &_hw_handlers);
-
- up_write(&_hwh_lock);
-
- return r;
-}
-
-int dm_unregister_hw_handler(struct hw_handler_type *hwht)
-{
- struct hwh_internal *hwhi;
-
- down_write(&_hwh_lock);
-
- hwhi = __find_hw_handler_type(hwht->name);
- if (!hwhi) {
- up_write(&_hwh_lock);
- return -EINVAL;
- }
-
- if (hwhi->use) {
- up_write(&_hwh_lock);
- return -ETXTBSY;
- }
-
- list_del(&hwhi->list);
-
- up_write(&_hwh_lock);
-
- kfree(hwhi);
-
- return 0;
-}
-
-unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct bio *bio)
-{
-#if 0
- int sense_key, asc, ascq;
-
- if (bio->bi_error & BIO_SENSE) {
- /* FIXME: This is just an initial guess. */
- /* key / asc / ascq */
- sense_key = (bio->bi_error >> 16) & 0xff;
- asc = (bio->bi_error >> 8) & 0xff;
- ascq = bio->bi_error & 0xff;
-
- switch (sense_key) {
- /* This block as a whole comes from the device.
- * So no point retrying on another path. */
- case 0x03: /* Medium error */
- case 0x05: /* Illegal request */
- case 0x07: /* Data protect */
- case 0x08: /* Blank check */
- case 0x0a: /* copy aborted */
- case 0x0c: /* obsolete - no clue ;-) */
- case 0x0d: /* volume overflow */
- case 0x0e: /* data miscompare */
- case 0x0f: /* reserved - no idea either. */
- return MP_ERROR_IO;
-
- /* For these errors it's unclear whether they
- * come from the device or the controller.
- * So just lets try a different path, and if
- * it eventually succeeds, user-space will clear
- * the paths again... */
- case 0x02: /* Not ready */
- case 0x04: /* Hardware error */
- case 0x09: /* vendor specific */
- case 0x0b: /* Aborted command */
- return MP_FAIL_PATH;
-
- case 0x06: /* Unit attention - might want to decode */
- if (asc == 0x04 && ascq == 0x01)
- /* "Unit in the process of
- * becoming ready" */
- return 0;
- return MP_FAIL_PATH;
-
- /* FIXME: For Unit Not Ready we may want
- * to have a generic pg activation
- * feature (START_UNIT). */
-
- /* Should these two ever end up in the
- * error path? I don't think so. */
- case 0x00: /* No sense */
- case 0x01: /* Recovered error */
- return 0;
- }
- }
-#endif
-
- /* We got no idea how to decode the other kinds of errors ->
- * assume generic error condition. */
- return MP_FAIL_PATH;
-}
-
-EXPORT_SYMBOL_GPL(dm_register_hw_handler);
-EXPORT_SYMBOL_GPL(dm_unregister_hw_handler);
-EXPORT_SYMBOL_GPL(dm_scsi_err_handler);
diff --git a/drivers/md/dm-hw-handler.h b/drivers/md/dm-hw-handler.h
deleted file mode 100644
index 46809dcb121a..000000000000
--- a/drivers/md/dm-hw-handler.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
- *
- * This file is released under the GPL.
- *
- * Multipath hardware handler registration.
- */
-
-#ifndef DM_HW_HANDLER_H
-#define DM_HW_HANDLER_H
-
-#include <linux/device-mapper.h>
-
-#include "dm-mpath.h"
-
-struct hw_handler_type;
-struct hw_handler {
- struct hw_handler_type *type;
- struct mapped_device *md;
- void *context;
-};
-
-/*
- * Constructs a hardware handler object, takes custom arguments
- */
-/* Information about a hardware handler type */
-struct hw_handler_type {
- char *name;
- struct module *module;
-
- int (*create) (struct hw_handler *handler, unsigned int argc,
- char **argv);
- void (*destroy) (struct hw_handler *hwh);
-
- void (*pg_init) (struct hw_handler *hwh, unsigned bypassed,
- struct dm_path *path);
- unsigned (*error) (struct hw_handler *hwh, struct bio *bio);
- int (*status) (struct hw_handler *hwh, status_type_t type,
- char *result, unsigned int maxlen);
-};
-
-/* Register a hardware handler */
-int dm_register_hw_handler(struct hw_handler_type *type);
-
-/* Unregister a hardware handler */
-int dm_unregister_hw_handler(struct hw_handler_type *type);
-
-/* Returns a registered hardware handler type */
-struct hw_handler_type *dm_get_hw_handler(const char *name);
-
-/* Releases a hardware handler */
-void dm_put_hw_handler(struct hw_handler_type *hwht);
-
-/* Default err function */
-unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct bio *bio);
-
-/* Error flags for err and dm_pg_init_complete */
-#define MP_FAIL_PATH 1
-#define MP_BYPASS_PG 2
-#define MP_ERROR_IO 4 /* Don't retry this I/O */
-#define MP_RETRY 8
-
-#endif
diff --git a/drivers/md/dm-mpath-hp-sw.c b/drivers/md/dm-mpath-hp-sw.c
deleted file mode 100644
index b63a0ab37c53..000000000000
--- a/drivers/md/dm-mpath-hp-sw.c
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Copyright (C) 2005 Mike Christie, All rights reserved.
- * Copyright (C) 2007 Red Hat, Inc. All rights reserved.
- * Authors: Mike Christie
- * Dave Wysochanski
- *
- * This file is released under the GPL.
- *
- * This module implements the specific path activation code for
- * HP StorageWorks and FSC FibreCat Asymmetric (Active/Passive)
- * storage arrays.
- * These storage arrays have controller-based failover, not
- * LUN-based failover. However, LUN-based failover is the design
- * of dm-multipath. Thus, this module is written for LUN-based failover.
- */
-#include <linux/blkdev.h>
-#include <linux/list.h>
-#include <linux/types.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_dbg.h>
-
-#include "dm.h"
-#include "dm-hw-handler.h"
-
-#define DM_MSG_PREFIX "multipath hp-sw"
-#define DM_HP_HWH_NAME "hp-sw"
-#define DM_HP_HWH_VER "1.0.0"
-
-struct hp_sw_context {
- unsigned char sense[SCSI_SENSE_BUFFERSIZE];
-};
-
-/*
- * hp_sw_error_is_retryable - Is an HP-specific check condition retryable?
- * @req: path activation request
- *
- * Examine error codes of request and determine whether the error is retryable.
- * Some error codes are already retried by scsi-ml (see
- * scsi_decide_disposition), but some HP specific codes are not.
- * The intent of this routine is to supply the logic for the HP specific
- * check conditions.
- *
- * Returns:
- * 1 - command completed with retryable error
- * 0 - command completed with non-retryable error
- *
- * Possible optimizations
- * 1. More hardware-specific error codes
- */
-static int hp_sw_error_is_retryable(struct request *req)
-{
- /*
- * NOT_READY is known to be retryable
- * For now we just dump out the sense data and call it retryable
- */
- if (status_byte(req->errors) == CHECK_CONDITION)
- __scsi_print_sense(DM_HP_HWH_NAME, req->sense, req->sense_len);
-
- /*
- * At this point we don't have complete information about all the error
- * codes from this hardware, so we are just conservative and retry
- * when in doubt.
- */
- return 1;
-}
-
-/*
- * hp_sw_end_io - Completion handler for HP path activation.
- * @req: path activation request
- * @error: scsi-ml error
- *
- * Check sense data, free request structure, and notify dm that
- * pg initialization has completed.
- *
- * Context: scsi-ml softirq
- *
- */
-static void hp_sw_end_io(struct request *req, int error)
-{
- struct dm_path *path = req->end_io_data;
- unsigned err_flags = 0;
-
- if (!error) {
- DMDEBUG("%s path activation command - success",
- path->dev->name);
- goto out;
- }
-
- if (hp_sw_error_is_retryable(req)) {
- DMDEBUG("%s path activation command - retry",
- path->dev->name);
- err_flags = MP_RETRY;
- goto out;
- }
-
- DMWARN("%s path activation fail - error=0x%x",
- path->dev->name, error);
- err_flags = MP_FAIL_PATH;
-
-out:
- req->end_io_data = NULL;
- __blk_put_request(req->q, req);
- dm_pg_init_complete(path, err_flags);
-}
-
-/*
- * hp_sw_get_request - Allocate an HP specific path activation request
- * @path: path on which request will be sent (needed for request queue)
- *
- * The START command is used for path activation request.
- * These arrays are controller-based failover, not LUN based.
- * One START command issued to a single path will fail over all
- * LUNs for the same controller.
- *
- * Possible optimizations
- * 1. Make timeout configurable
- * 2. Preallocate request
- */
-static struct request *hp_sw_get_request(struct dm_path *path)
-{
- struct request *req;
- struct block_device *bdev = path->dev->bdev;
- struct request_queue *q = bdev_get_queue(bdev);
- struct hp_sw_context *h = path->hwhcontext;
-
- req = blk_get_request(q, WRITE, GFP_NOIO);
- if (!req)
- goto out;
-
- req->timeout = 60 * HZ;
-
- req->errors = 0;
- req->cmd_type = REQ_TYPE_BLOCK_PC;
- req->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
- req->end_io_data = path;
- req->sense = h->sense;
- memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
-
- req->cmd[0] = START_STOP;
- req->cmd[4] = 1;
- req->cmd_len = COMMAND_SIZE(req->cmd[0]);
-
-out:
- return req;
-}
-
-/*
- * hp_sw_pg_init - HP path activation implementation.
- * @hwh: hardware handler specific data
- * @bypassed: unused; is the path group bypassed? (see dm-mpath.c)
- * @path: path to send initialization command
- *
- * Send an HP-specific path activation command on 'path'.
- * Do not try to optimize in any way, just send the activation command.
- * More than one path activation command may be sent to the same controller.
- * This seems to work fine for basic failover support.
- *
- * Possible optimizations
- * 1. Detect an in-progress activation request and avoid submitting another one
- * 2. Model the controller and only send a single activation request at a time
- * 3. Determine the state of a path before sending an activation request
- *
- * Context: kmpathd (see process_queued_ios() in dm-mpath.c)
- */
-static void hp_sw_pg_init(struct hw_handler *hwh, unsigned bypassed,
- struct dm_path *path)
-{
- struct request *req;
- struct hp_sw_context *h;
-
- path->hwhcontext = hwh->context;
- h = hwh->context;
-
- req = hp_sw_get_request(path);
- if (!req) {
- DMERR("%s path activation command - allocation fail",
- path->dev->name);
- goto retry;
- }
-
- DMDEBUG("%s path activation command - sent", path->dev->name);
-
- blk_execute_rq_nowait(req->q, NULL, req, 1, hp_sw_end_io);
- return;
-
-retry:
- dm_pg_init_complete(path, MP_RETRY);
-}
-
-static int hp_sw_create(struct hw_handler *hwh, unsigned argc, char **argv)
-{
- struct hp_sw_context *h;
-
- h = kmalloc(sizeof(*h), GFP_KERNEL);
- if (!h)
- return -ENOMEM;
-
- hwh->context = h;
-
- return 0;
-}
-
-static void hp_sw_destroy(struct hw_handler *hwh)
-{
- struct hp_sw_context *h = hwh->context;
-
- kfree(h);
-}
-
-static struct hw_handler_type hp_sw_hwh = {
- .name = DM_HP_HWH_NAME,
- .module = THIS_MODULE,
- .create = hp_sw_create,
- .destroy = hp_sw_destroy,
- .pg_init = hp_sw_pg_init,
-};
-
-static int __init hp_sw_init(void)
-{
- int r;
-
- r = dm_register_hw_handler(&hp_sw_hwh);
- if (r < 0)
- DMERR("register failed %d", r);
- else
- DMINFO("version " DM_HP_HWH_VER " loaded");
-
- return r;
-}
-
-static void __exit hp_sw_exit(void)
-{
- int r;
-
- r = dm_unregister_hw_handler(&hp_sw_hwh);
- if (r < 0)
- DMERR("unregister failed %d", r);
-}
-
-module_init(hp_sw_init);
-module_exit(hp_sw_exit);
-
-MODULE_DESCRIPTION("DM Multipath HP StorageWorks / FSC FibreCat (A/P) support");
-MODULE_AUTHOR("Mike Christie, Dave Wysochanski <dm-devel@redhat.com>");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(DM_HP_HWH_VER);
diff --git a/drivers/md/dm-mpath-rdac.c b/drivers/md/dm-mpath-rdac.c
deleted file mode 100644
index 95e77734880a..000000000000
--- a/drivers/md/dm-mpath-rdac.c
+++ /dev/null
@@ -1,700 +0,0 @@
-/*
- * Engenio/LSI RDAC DM HW handler
- *
- * Copyright (C) 2005 Mike Christie. All rights reserved.
- * Copyright (C) Chandra Seetharaman, IBM Corp. 2007
- *
- * 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 <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_eh.h>
-
-#define DM_MSG_PREFIX "multipath rdac"
-
-#include "dm.h"
-#include "dm-hw-handler.h"
-
-#define RDAC_DM_HWH_NAME "rdac"
-#define RDAC_DM_HWH_VER "0.4"
-
-/*
- * LSI mode page stuff
- *
- * These struct definitions and the forming of the
- * mode page were taken from the LSI RDAC 2.4 GPL'd
- * driver, and then converted to Linux conventions.
- */
-#define RDAC_QUIESCENCE_TIME 20;
-/*
- * Page Codes
- */
-#define RDAC_PAGE_CODE_REDUNDANT_CONTROLLER 0x2c
-
-/*
- * Controller modes definitions
- */
-#define RDAC_MODE_TRANSFER_ALL_LUNS 0x01
-#define RDAC_MODE_TRANSFER_SPECIFIED_LUNS 0x02
-
-/*
- * RDAC Options field
- */
-#define RDAC_FORCED_QUIESENCE 0x02
-
-#define RDAC_FAILOVER_TIMEOUT (60 * HZ)
-
-struct rdac_mode_6_hdr {
- u8 data_len;
- u8 medium_type;
- u8 device_params;
- u8 block_desc_len;
-};
-
-struct rdac_mode_10_hdr {
- u16 data_len;
- u8 medium_type;
- u8 device_params;
- u16 reserved;
- u16 block_desc_len;
-};
-
-struct rdac_mode_common {
- u8 controller_serial[16];
- u8 alt_controller_serial[16];
- u8 rdac_mode[2];
- u8 alt_rdac_mode[2];
- u8 quiescence_timeout;
- u8 rdac_options;
-};
-
-struct rdac_pg_legacy {
- struct rdac_mode_6_hdr hdr;
- u8 page_code;
- u8 page_len;
- struct rdac_mode_common common;
-#define MODE6_MAX_LUN 32
- u8 lun_table[MODE6_MAX_LUN];
- u8 reserved2[32];
- u8 reserved3;
- u8 reserved4;
-};
-
-struct rdac_pg_expanded {
- struct rdac_mode_10_hdr hdr;
- u8 page_code;
- u8 subpage_code;
- u8 page_len[2];
- struct rdac_mode_common common;
- u8 lun_table[256];
- u8 reserved3;
- u8 reserved4;
-};
-
-struct c9_inquiry {
- u8 peripheral_info;
- u8 page_code; /* 0xC9 */
- u8 reserved1;
- u8 page_len;
- u8 page_id[4]; /* "vace" */
- u8 avte_cvp;
- u8 path_prio;
- u8 reserved2[38];
-};
-
-#define SUBSYS_ID_LEN 16
-#define SLOT_ID_LEN 2
-
-struct c4_inquiry {
- u8 peripheral_info;
- u8 page_code; /* 0xC4 */
- u8 reserved1;
- u8 page_len;
- u8 page_id[4]; /* "subs" */
- u8 subsys_id[SUBSYS_ID_LEN];
- u8 revision[4];
- u8 slot_id[SLOT_ID_LEN];
- u8 reserved[2];
-};
-
-struct rdac_controller {
- u8 subsys_id[SUBSYS_ID_LEN];
- u8 slot_id[SLOT_ID_LEN];
- int use_10_ms;
- struct kref kref;
- struct list_head node; /* list of all controllers */
- spinlock_t lock;
- int submitted;
- struct list_head cmd_list; /* list of commands to be submitted */
- union {
- struct rdac_pg_legacy legacy;
- struct rdac_pg_expanded expanded;
- } mode_select;
-};
-struct c8_inquiry {
- u8 peripheral_info;
- u8 page_code; /* 0xC8 */
- u8 reserved1;
- u8 page_len;
- u8 page_id[4]; /* "edid" */
- u8 reserved2[3];
- u8 vol_uniq_id_len;
- u8 vol_uniq_id[16];
- u8 vol_user_label_len;
- u8 vol_user_label[60];
- u8 array_uniq_id_len;
- u8 array_unique_id[16];
- u8 array_user_label_len;
- u8 array_user_label[60];
- u8 lun[8];
-};
-
-struct c2_inquiry {
- u8 peripheral_info;
- u8 page_code; /* 0xC2 */
- u8 reserved1;
- u8 page_len;
- u8 page_id[4]; /* "swr4" */
- u8 sw_version[3];
- u8 sw_date[3];
- u8 features_enabled;
- u8 max_lun_supported;
- u8 partitions[239]; /* Total allocation length should be 0xFF */
-};
-
-struct rdac_handler {
- struct list_head entry; /* list waiting to submit MODE SELECT */
- unsigned timeout;
- struct rdac_controller *ctlr;
-#define UNINITIALIZED_LUN (1 << 8)
- unsigned lun;
- unsigned char sense[SCSI_SENSE_BUFFERSIZE];
- struct dm_path *path;
- struct work_struct work;
-#define SEND_C2_INQUIRY 1
-#define SEND_C4_INQUIRY 2
-#define SEND_C8_INQUIRY 3
-#define SEND_C9_INQUIRY 4
-#define SEND_MODE_SELECT 5
- int cmd_to_send;
- union {
- struct c2_inquiry c2;
- struct c4_inquiry c4;
- struct c8_inquiry c8;
- struct c9_inquiry c9;
- } inq;
-};
-
-static LIST_HEAD(ctlr_list);
-static DEFINE_SPINLOCK(list_lock);
-static struct workqueue_struct *rdac_wkqd;
-
-static inline int had_failures(struct request *req, int error)
-{
- return (error || host_byte(req->errors) != DID_OK ||
- msg_byte(req->errors) != COMMAND_COMPLETE);
-}
-
-static void rdac_resubmit_all(struct rdac_handler *h)
-{
- struct rdac_controller *ctlr = h->ctlr;
- struct rdac_handler *tmp, *h1;
-
- spin_lock(&ctlr->lock);
- list_for_each_entry_safe(h1, tmp, &ctlr->cmd_list, entry) {
- h1->cmd_to_send = SEND_C9_INQUIRY;
- queue_work(rdac_wkqd, &h1->work);
- list_del(&h1->entry);
- }
- ctlr->submitted = 0;
- spin_unlock(&ctlr->lock);
-}
-
-static void mode_select_endio(struct request *req, int error)
-{
- struct rdac_handler *h = req->end_io_data;
- struct scsi_sense_hdr sense_hdr;
- int sense = 0, fail = 0;
-
- if (had_failures(req, error)) {
- fail = 1;
- goto failed;
- }
-
- if (status_byte(req->errors) == CHECK_CONDITION) {
- scsi_normalize_sense(req->sense, SCSI_SENSE_BUFFERSIZE,
- &sense_hdr);
- sense = (sense_hdr.sense_key << 16) | (sense_hdr.asc << 8) |
- sense_hdr.ascq;
- /* If it is retryable failure, submit the c9 inquiry again */
- if (sense == 0x59136 || sense == 0x68b02 || sense == 0xb8b02 ||
- sense == 0x62900) {
- /* 0x59136 - Command lock contention
- * 0x[6b]8b02 - Quiesense in progress or achieved
- * 0x62900 - Power On, Reset, or Bus Device Reset
- */
- h->cmd_to_send = SEND_C9_INQUIRY;
- queue_work(rdac_wkqd, &h->work);
- goto done;
- }
- if (sense)
- DMINFO("MODE_SELECT failed on %s with sense 0x%x",
- h->path->dev->name, sense);
- }
-failed:
- if (fail || sense)
- dm_pg_init_complete(h->path, MP_FAIL_PATH);
- else
- dm_pg_init_complete(h->path, 0);
-
-done:
- rdac_resubmit_all(h);
- __blk_put_request(req->q, req);
-}
-
-static struct request *get_rdac_req(struct rdac_handler *h,
- void *buffer, unsigned buflen, int rw)
-{
- struct request *rq;
- struct request_queue *q = bdev_get_queue(h->path->dev->bdev);
-
- rq = blk_get_request(q, rw, GFP_KERNEL);
-
- if (!rq) {
- DMINFO("get_rdac_req: blk_get_request failed");
- return NULL;
- }
-
- if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_KERNEL)) {
- blk_put_request(rq);
- DMINFO("get_rdac_req: blk_rq_map_kern failed");
- return NULL;
- }
-
- rq->sense = h->sense;
- memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
- rq->sense_len = 0;
-
- rq->end_io_data = h;
- rq->timeout = h->timeout;
- rq->cmd_type = REQ_TYPE_BLOCK_PC;
- rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
- return rq;
-}
-
-static struct request *rdac_failover_get(struct rdac_handler *h)
-{
- struct request *rq;
- struct rdac_mode_common *common;
- unsigned data_size;
-
- if (h->ctlr->use_10_ms) {
- struct rdac_pg_expanded *rdac_pg;
-
- data_size = sizeof(struct rdac_pg_expanded);
- rdac_pg = &h->ctlr->mode_select.expanded;
- memset(rdac_pg, 0, data_size);
- common = &rdac_pg->common;
- rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER + 0x40;
- rdac_pg->subpage_code = 0x1;
- rdac_pg->page_len[0] = 0x01;
- rdac_pg->page_len[1] = 0x28;
- rdac_pg->lun_table[h->lun] = 0x81;
- } else {
- struct rdac_pg_legacy *rdac_pg;
-
- data_size = sizeof(struct rdac_pg_legacy);
- rdac_pg = &h->ctlr->mode_select.legacy;
- memset(rdac_pg, 0, data_size);
- common = &rdac_pg->common;
- rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER;
- rdac_pg->page_len = 0x68;
- rdac_pg->lun_table[h->lun] = 0x81;
- }
- common->rdac_mode[1] = RDAC_MODE_TRANSFER_SPECIFIED_LUNS;
- common->quiescence_timeout = RDAC_QUIESCENCE_TIME;
- common->rdac_options = RDAC_FORCED_QUIESENCE;
-
- /* get request for block layer packet command */
- rq = get_rdac_req(h, &h->ctlr->mode_select, data_size, WRITE);
- if (!rq) {
- DMERR("rdac_failover_get: no rq");
- return NULL;
- }
-
- /* Prepare the command. */
- if (h->ctlr->use_10_ms) {
- rq->cmd[0] = MODE_SELECT_10;
- rq->cmd[7] = data_size >> 8;
- rq->cmd[8] = data_size & 0xff;
- } else {
- rq->cmd[0] = MODE_SELECT;
- rq->cmd[4] = data_size;
- }
- rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
-
- return rq;
-}
-
-/* Acquires h->ctlr->lock */
-static void submit_mode_select(struct rdac_handler *h)
-{
- struct request *rq;
- struct request_queue *q = bdev_get_queue(h->path->dev->bdev);
-
- spin_lock(&h->ctlr->lock);
- if (h->ctlr->submitted) {
- list_add(&h->entry, &h->ctlr->cmd_list);
- goto drop_lock;
- }
-
- if (!q) {
- DMINFO("submit_mode_select: no queue");
- goto fail_path;
- }
-
- rq = rdac_failover_get(h);
- if (!rq) {
- DMERR("submit_mode_select: no rq");
- goto fail_path;
- }
-
- DMINFO("queueing MODE_SELECT command on %s", h->path->dev->name);
-
- blk_execute_rq_nowait(q, NULL, rq, 1, mode_select_endio);
- h->ctlr->submitted = 1;
- goto drop_lock;
-fail_path:
- dm_pg_init_complete(h->path, MP_FAIL_PATH);
-drop_lock:
- spin_unlock(&h->ctlr->lock);
-}
-
-static void release_ctlr(struct kref *kref)
-{
- struct rdac_controller *ctlr;
- ctlr = container_of(kref, struct rdac_controller, kref);
-
- spin_lock(&list_lock);
- list_del(&ctlr->node);
- spin_unlock(&list_lock);
- kfree(ctlr);
-}
-
-static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id)
-{
- struct rdac_controller *ctlr, *tmp;
-
- spin_lock(&list_lock);
-
- list_for_each_entry(tmp, &ctlr_list, node) {
- if ((memcmp(tmp->subsys_id, subsys_id, SUBSYS_ID_LEN) == 0) &&
- (memcmp(tmp->slot_id, slot_id, SLOT_ID_LEN) == 0)) {
- kref_get(&tmp->kref);
- spin_unlock(&list_lock);
- return tmp;
- }
- }
- ctlr = kmalloc(sizeof(*ctlr), GFP_ATOMIC);
- if (!ctlr)
- goto done;
-
- /* initialize fields of controller */
- memcpy(ctlr->subsys_id, subsys_id, SUBSYS_ID_LEN);
- memcpy(ctlr->slot_id, slot_id, SLOT_ID_LEN);
- kref_init(&ctlr->kref);
- spin_lock_init(&ctlr->lock);
- ctlr->submitted = 0;
- ctlr->use_10_ms = -1;
- INIT_LIST_HEAD(&ctlr->cmd_list);
- list_add(&ctlr->node, &ctlr_list);
-done:
- spin_unlock(&list_lock);
- return ctlr;
-}
-
-static void c4_endio(struct request *req, int error)
-{
- struct rdac_handler *h = req->end_io_data;
- struct c4_inquiry *sp;
-
- if (had_failures(req, error)) {
- dm_pg_init_complete(h->path, MP_FAIL_PATH);
- goto done;
- }
-
- sp = &h->inq.c4;
-
- h->ctlr = get_controller(sp->subsys_id, sp->slot_id);
-
- if (h->ctlr) {
- h->cmd_to_send = SEND_C9_INQUIRY;
- queue_work(rdac_wkqd, &h->work);
- } else
- dm_pg_init_complete(h->path, MP_FAIL_PATH);
-done:
- __blk_put_request(req->q, req);
-}
-
-static void c2_endio(struct request *req, int error)
-{
- struct rdac_handler *h = req->end_io_data;
- struct c2_inquiry *sp;
-
- if (had_failures(req, error)) {
- dm_pg_init_complete(h->path, MP_FAIL_PATH);
- goto done;
- }
-
- sp = &h->inq.c2;
-
- /* If more than MODE6_MAX_LUN luns are supported, use mode select 10 */
- if (sp->max_lun_supported >= MODE6_MAX_LUN)
- h->ctlr->use_10_ms = 1;
- else
- h->ctlr->use_10_ms = 0;
-
- h->cmd_to_send = SEND_MODE_SELECT;
- queue_work(rdac_wkqd, &h->work);
-done:
- __blk_put_request(req->q, req);
-}
-
-static void c9_endio(struct request *req, int error)
-{
- struct rdac_handler *h = req->end_io_data;
- struct c9_inquiry *sp;
-
- if (had_failures(req, error)) {
- dm_pg_init_complete(h->path, MP_FAIL_PATH);
- goto done;
- }
-
- /* We need to look at the sense keys here to take clear action.
- * For now simple logic: If the host is in AVT mode or if controller
- * owns the lun, return dm_pg_init_complete(), otherwise submit
- * MODE SELECT.
- */
- sp = &h->inq.c9;
-
- /* If in AVT mode, return success */
- if ((sp->avte_cvp >> 7) == 0x1) {
- dm_pg_init_complete(h->path, 0);
- goto done;
- }
-
- /* If the controller on this path owns the LUN, return success */
- if (sp->avte_cvp & 0x1) {
- dm_pg_init_complete(h->path, 0);
- goto done;
- }
-
- if (h->ctlr) {
- if (h->ctlr->use_10_ms == -1)
- h->cmd_to_send = SEND_C2_INQUIRY;
- else
- h->cmd_to_send = SEND_MODE_SELECT;
- } else
- h->cmd_to_send = SEND_C4_INQUIRY;
- queue_work(rdac_wkqd, &h->work);
-done:
- __blk_put_request(req->q, req);
-}
-
-static void c8_endio(struct request *req, int error)
-{
- struct rdac_handler *h = req->end_io_data;
- struct c8_inquiry *sp;
-
- if (had_failures(req, error)) {
- dm_pg_init_complete(h->path, MP_FAIL_PATH);
- goto done;
- }
-
- /* We need to look at the sense keys here to take clear action.
- * For now simple logic: Get the lun from the inquiry page.
- */
- sp = &h->inq.c8;
- h->lun = sp->lun[7]; /* currently it uses only one byte */
- h->cmd_to_send = SEND_C9_INQUIRY;
- queue_work(rdac_wkqd, &h->work);
-done:
- __blk_put_request(req->q, req);
-}
-
-static void submit_inquiry(struct rdac_handler *h, int page_code,
- unsigned int len, rq_end_io_fn endio)
-{
- struct request *rq;
- struct request_queue *q = bdev_get_queue(h->path->dev->bdev);
-
- if (!q)
- goto fail_path;
-
- rq = get_rdac_req(h, &h->inq, len, READ);
- if (!rq)
- goto fail_path;
-
- /* Prepare the command. */
- rq->cmd[0] = INQUIRY;
- rq->cmd[1] = 1;
- rq->cmd[2] = page_code;
- rq->cmd[4] = len;
- rq->cmd_len = COMMAND_SIZE(INQUIRY);
- blk_execute_rq_nowait(q, NULL, rq, 1, endio);
- return;
-
-fail_path:
- dm_pg_init_complete(h->path, MP_FAIL_PATH);
-}
-
-static void service_wkq(struct work_struct *work)
-{
- struct rdac_handler *h = container_of(work, struct rdac_handler, work);
-
- switch (h->cmd_to_send) {
- case SEND_C2_INQUIRY:
- submit_inquiry(h, 0xC2, sizeof(struct c2_inquiry), c2_endio);
- break;
- case SEND_C4_INQUIRY:
- submit_inquiry(h, 0xC4, sizeof(struct c4_inquiry), c4_endio);
- break;
- case SEND_C8_INQUIRY:
- submit_inquiry(h, 0xC8, sizeof(struct c8_inquiry), c8_endio);
- break;
- case SEND_C9_INQUIRY:
- submit_inquiry(h, 0xC9, sizeof(struct c9_inquiry), c9_endio);
- break;
- case SEND_MODE_SELECT:
- submit_mode_select(h);
- break;
- default:
- BUG();
- }
-}
-/*
- * only support subpage2c until we confirm that this is just a matter of
- * of updating firmware or not, and RDAC (basic AVT works already) for now
- * but we can add these in in when we get time and testers
- */
-static int rdac_create(struct hw_handler *hwh, unsigned argc, char **argv)
-{
- struct rdac_handler *h;
- unsigned timeout;
-
- if (argc == 0) {
- /* No arguments: use defaults */
- timeout = RDAC_FAILOVER_TIMEOUT;
- } else if (argc != 1) {
- DMWARN("incorrect number of arguments");
- return -EINVAL;
- } else {
- if (sscanf(argv[1], "%u", &timeout) != 1) {
- DMWARN("invalid timeout value");
- return -EINVAL;
- }
- }
-
- h = kzalloc(sizeof(*h), GFP_KERNEL);
- if (!h)
- return -ENOMEM;
-
- hwh->context = h;
- h->timeout = timeout;
- h->lun = UNINITIALIZED_LUN;
- INIT_WORK(&h->work, service_wkq);
- DMWARN("using RDAC command with timeout %u", h->timeout);
-
- return 0;
-}
-
-static void rdac_destroy(struct hw_handler *hwh)
-{
- struct rdac_handler *h = hwh->context;
-
- if (h->ctlr)
- kref_put(&h->ctlr->kref, release_ctlr);
- kfree(h);
- hwh->context = NULL;
-}
-
-static unsigned rdac_error(struct hw_handler *hwh, struct bio *bio)
-{
- /* Try default handler */
- return dm_scsi_err_handler(hwh, bio);
-}
-
-static void rdac_pg_init(struct hw_handler *hwh, unsigned bypassed,
- struct dm_path *path)
-{
- struct rdac_handler *h = hwh->context;
-
- h->path = path;
- switch (h->lun) {
- case UNINITIALIZED_LUN:
- submit_inquiry(h, 0xC8, sizeof(struct c8_inquiry), c8_endio);
- break;
- default:
- submit_inquiry(h, 0xC9, sizeof(struct c9_inquiry), c9_endio);
- }
-}
-
-static struct hw_handler_type rdac_handler = {
- .name = RDAC_DM_HWH_NAME,
- .module = THIS_MODULE,
- .create = rdac_create,
- .destroy = rdac_destroy,
- .pg_init = rdac_pg_init,
- .error = rdac_error,
-};
-
-static int __init rdac_init(void)
-{
- int r;
-
- rdac_wkqd = create_singlethread_workqueue("rdac_wkqd");
- if (!rdac_wkqd) {
- DMERR("Failed to create workqueue rdac_wkqd.");
- return -ENOMEM;
- }
-
- r = dm_register_hw_handler(&rdac_handler);
- if (r < 0) {
- DMERR("%s: register failed %d", RDAC_DM_HWH_NAME, r);
- destroy_workqueue(rdac_wkqd);
- return r;
- }
-
- DMINFO("%s: version %s loaded", RDAC_DM_HWH_NAME, RDAC_DM_HWH_VER);
- return 0;
-}
-
-static void __exit rdac_exit(void)
-{
- int r = dm_unregister_hw_handler(&rdac_handler);
-
- destroy_workqueue(rdac_wkqd);
- if (r < 0)
- DMERR("%s: unregister failed %d", RDAC_DM_HWH_NAME, r);
-}
-
-module_init(rdac_init);
-module_exit(rdac_exit);
-
-MODULE_DESCRIPTION("DM Multipath LSI/Engenio RDAC support");
-MODULE_AUTHOR("Mike Christie, Chandra Seetharaman");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(RDAC_DM_HWH_VER);
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index e7ee59e655d5..e8f704aa46f2 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -7,7 +7,6 @@
#include "dm.h"
#include "dm-path-selector.h"
-#include "dm-hw-handler.h"
#include "dm-bio-list.h"
#include "dm-bio-record.h"
#include "dm-uevent.h"
@@ -20,6 +19,7 @@
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/workqueue.h>
+#include <scsi/scsi_dh.h>
#include <asm/atomic.h>
#define DM_MSG_PREFIX "multipath"
@@ -61,7 +61,8 @@ struct multipath {
spinlock_t lock;
- struct hw_handler hw_handler;
+ const char *hw_handler_name;
+ struct work_struct activate_path;
unsigned nr_priority_groups;
struct list_head priority_groups;
unsigned pg_init_required; /* pg_init needs calling? */
@@ -106,9 +107,10 @@ typedef int (*action_fn) (struct pgpath *pgpath);
static struct kmem_cache *_mpio_cache;
-static struct workqueue_struct *kmultipathd;
+static struct workqueue_struct *kmultipathd, *kmpath_handlerd;
static void process_queued_ios(struct work_struct *work);
static void trigger_event(struct work_struct *work);
+static void activate_path(struct work_struct *work);
/*-----------------------------------------------
@@ -178,6 +180,7 @@ static struct multipath *alloc_multipath(struct dm_target *ti)
m->queue_io = 1;
INIT_WORK(&m->process_queued_ios, process_queued_ios);
INIT_WORK(&m->trigger_event, trigger_event);
+ INIT_WORK(&m->activate_path, activate_path);
m->mpio_pool = mempool_create_slab_pool(MIN_IOS, _mpio_cache);
if (!m->mpio_pool) {
kfree(m);
@@ -193,18 +196,13 @@ static struct multipath *alloc_multipath(struct dm_target *ti)
static void free_multipath(struct multipath *m)
{
struct priority_group *pg, *tmp;
- struct hw_handler *hwh = &m->hw_handler;
list_for_each_entry_safe(pg, tmp, &m->priority_groups, list) {
list_del(&pg->list);
free_priority_group(pg, m->ti);
}
- if (hwh->type) {
- hwh->type->destroy(hwh);
- dm_put_hw_handler(hwh->type);
- }
-
+ kfree(m->hw_handler_name);
mempool_destroy(m->mpio_pool);
kfree(m);
}
@@ -216,12 +214,10 @@ static void free_multipath(struct multipath *m)
static void __switch_pg(struct multipath *m, struct pgpath *pgpath)
{
- struct hw_handler *hwh = &m->hw_handler;
-
m->current_pg = pgpath->pg;
/* Must we initialise the PG first, and queue I/O till it's ready? */
- if (hwh->type && hwh->type->pg_init) {
+ if (m->hw_handler_name) {
m->pg_init_required = 1;
m->queue_io = 1;
} else {
@@ -409,7 +405,6 @@ static void process_queued_ios(struct work_struct *work)
{
struct multipath *m =
container_of(work, struct multipath, process_queued_ios);
- struct hw_handler *hwh = &m->hw_handler;
struct pgpath *pgpath = NULL;
unsigned init_required = 0, must_queue = 1;
unsigned long flags;
@@ -439,7 +434,7 @@ out:
spin_unlock_irqrestore(&m->lock, flags);
if (init_required)
- hwh->type->pg_init(hwh, pgpath->pg->bypassed, &pgpath->path);
+ queue_work(kmpath_handlerd, &m->activate_path);
if (!must_queue)
dispatch_queued_ios(m);
@@ -652,8 +647,6 @@ static struct priority_group *parse_priority_group(struct arg_set *as,
static int parse_hw_handler(struct arg_set *as, struct multipath *m)
{
- int r;
- struct hw_handler_type *hwht;
unsigned hw_argc;
struct dm_target *ti = m->ti;
@@ -661,30 +654,18 @@ static int parse_hw_handler(struct arg_set *as, struct multipath *m)
{0, 1024, "invalid number of hardware handler args"},
};
- r = read_param(_params, shift(as), &hw_argc, &ti->error);
- if (r)
+ if (read_param(_params, shift(as), &hw_argc, &ti->error))
return -EINVAL;
if (!hw_argc)
return 0;
- hwht = dm_get_hw_handler(shift(as));
- if (!hwht) {
+ m->hw_handler_name = kstrdup(shift(as), GFP_KERNEL);
+ request_module("scsi_dh_%s", m->hw_handler_name);
+ if (scsi_dh_handler_exist(m->hw_handler_name) == 0) {
ti->error = "unknown hardware handler type";
return -EINVAL;
}
-
- m->hw_handler.md = dm_table_get_md(ti->table);
- dm_put(m->hw_handler.md);
-
- r = hwht->create(&m->hw_handler, hw_argc - 1, as->argv);
- if (r) {
- dm_put_hw_handler(hwht);
- ti->error = "hardware handler constructor failed";
- return r;
- }
-
- m->hw_handler.type = hwht;
consume(as, hw_argc - 1);
return 0;
@@ -808,6 +789,7 @@ static void multipath_dtr(struct dm_target *ti)
{
struct multipath *m = (struct multipath *) ti->private;
+ flush_workqueue(kmpath_handlerd);
flush_workqueue(kmultipathd);
free_multipath(m);
}
@@ -1025,52 +1007,85 @@ static int pg_init_limit_reached(struct multipath *m, struct pgpath *pgpath)
return limit_reached;
}
-/*
- * pg_init must call this when it has completed its initialisation
- */
-void dm_pg_init_complete(struct dm_path *path, unsigned err_flags)
+static void pg_init_done(struct dm_path *path, int errors)
{
struct pgpath *pgpath = path_to_pgpath(path);
struct priority_group *pg = pgpath->pg;
struct multipath *m = pg->m;
unsigned long flags;
- /*
- * If requested, retry pg_init until maximum number of retries exceeded.
- * If retry not requested and PG already bypassed, always fail the path.
- */
- if (err_flags & MP_RETRY) {
- if (pg_init_limit_reached(m, pgpath))
- err_flags |= MP_FAIL_PATH;
- } else if (err_flags && pg->bypassed)
- err_flags |= MP_FAIL_PATH;
-
- if (err_flags & MP_FAIL_PATH)
+ /* device or driver problems */
+ switch (errors) {
+ case SCSI_DH_OK:
+ break;
+ case SCSI_DH_NOSYS:
+ if (!m->hw_handler_name) {
+ errors = 0;
+ break;
+ }
+ DMERR("Cannot failover device because scsi_dh_%s was not "
+ "loaded.", m->hw_handler_name);
+ /*
+ * Fail path for now, so we do not ping pong
+ */
fail_path(pgpath);
-
- if (err_flags & MP_BYPASS_PG)
+ break;
+ case SCSI_DH_DEV_TEMP_BUSY:
+ /*
+ * Probably doing something like FW upgrade on the
+ * controller so try the other pg.
+ */
bypass_pg(m, pg, 1);
+ break;
+ /* TODO: For SCSI_DH_RETRY we should wait a couple seconds */
+ case SCSI_DH_RETRY:
+ case SCSI_DH_IMM_RETRY:
+ case SCSI_DH_RES_TEMP_UNAVAIL:
+ if (pg_init_limit_reached(m, pgpath))
+ fail_path(pgpath);
+ errors = 0;
+ break;
+ default:
+ /*
+ * We probably do not want to fail the path for a device
+ * error, but this is what the old dm did. In future
+ * patches we can do more advanced handling.
+ */
+ fail_path(pgpath);
+ }
spin_lock_irqsave(&m->lock, flags);
- if (err_flags & ~MP_RETRY) {
+ if (errors) {
+ DMERR("Could not failover device. Error %d.", errors);
m->current_pgpath = NULL;
m->current_pg = NULL;
- } else if (!m->pg_init_required)
+ } else if (!m->pg_init_required) {
m->queue_io = 0;
+ pg->bypassed = 0;
+ }
m->pg_init_in_progress = 0;
queue_work(kmultipathd, &m->process_queued_ios);
spin_unlock_irqrestore(&m->lock, flags);
}
+static void activate_path(struct work_struct *work)
+{
+ int ret;
+ struct multipath *m =
+ container_of(work, struct multipath, activate_path);
+ struct dm_path *path = &m->current_pgpath->path;
+
+ ret = scsi_dh_activate(bdev_get_queue(path->dev->bdev));
+ pg_init_done(path, ret);
+}
+
/*
* end_io handling
*/
static int do_end_io(struct multipath *m, struct bio *bio,
int error, struct dm_mpath_io *mpio)
{
- struct hw_handler *hwh = &m->hw_handler;
- unsigned err_flags = MP_FAIL_PATH; /* Default behavior */
unsigned long flags;
if (!error)
@@ -1097,19 +1112,8 @@ static int do_end_io(struct multipath *m, struct bio *bio,
}
spin_unlock_irqrestore(&m->lock, flags);
- if (hwh->type && hwh->type->error)
- err_flags = hwh->type->error(hwh, bio);
-
- if (mpio->pgpath) {
- if (err_flags & MP_FAIL_PATH)
- fail_path(mpio->pgpath);
-
- if (err_flags & MP_BYPASS_PG)
- bypass_pg(m, mpio->pgpath->pg, 1);
- }
-
- if (err_flags & MP_ERROR_IO)
- return -EIO;
+ if (mpio->pgpath)
+ fail_path(mpio->pgpath);
requeue:
dm_bio_restore(&mpio->details, bio);
@@ -1194,7 +1198,6 @@ static int multipath_status(struct dm_target *ti, status_type_t type,
int sz = 0;
unsigned long flags;
struct multipath *m = (struct multipath *) ti->private;
- struct hw_handler *hwh = &m->hw_handler;
struct priority_group *pg;
struct pgpath *p;
unsigned pg_num;
@@ -1214,12 +1217,10 @@ static int multipath_status(struct dm_target *ti, status_type_t type,
DMEMIT("pg_init_retries %u ", m->pg_init_retries);
}
- if (hwh->type && hwh->type->status)
- sz += hwh->type->status(hwh, type, result + sz, maxlen - sz);
- else if (!hwh->type || type == STATUSTYPE_INFO)
+ if (!m->hw_handler_name || type == STATUSTYPE_INFO)
DMEMIT("0 ");
else
- DMEMIT("1 %s ", hwh->type->name);
+ DMEMIT("1 %s ", m->hw_handler_name);
DMEMIT("%u ", m->nr_priority_groups);
@@ -1422,6 +1423,21 @@ static int __init dm_multipath_init(void)
return -ENOMEM;
}
+ /*
+ * A separate workqueue is used to handle the device handlers
+ * to avoid overloading existing workqueue. Overloading the
+ * old workqueue would also create a bottleneck in the
+ * path of the storage hardware device activation.
+ */
+ kmpath_handlerd = create_singlethread_workqueue("kmpath_handlerd");
+ if (!kmpath_handlerd) {
+ DMERR("failed to create workqueue kmpath_handlerd");
+ destroy_workqueue(kmultipathd);
+ dm_unregister_target(&multipath_target);
+ kmem_cache_destroy(_mpio_cache);
+ return -ENOMEM;
+ }
+
DMINFO("version %u.%u.%u loaded",
multipath_target.version[0], multipath_target.version[1],
multipath_target.version[2]);
@@ -1433,6 +1449,7 @@ static void __exit dm_multipath_exit(void)
{
int r;
+ destroy_workqueue(kmpath_handlerd);
destroy_workqueue(kmultipathd);
r = dm_unregister_target(&multipath_target);
@@ -1441,8 +1458,6 @@ static void __exit dm_multipath_exit(void)
kmem_cache_destroy(_mpio_cache);
}
-EXPORT_SYMBOL_GPL(dm_pg_init_complete);
-
module_init(dm_multipath_init);
module_exit(dm_multipath_exit);
diff --git a/drivers/md/dm-mpath.h b/drivers/md/dm-mpath.h
index b9cdcbb3ed59..c198b856a452 100644
--- a/drivers/md/dm-mpath.h
+++ b/drivers/md/dm-mpath.h
@@ -16,7 +16,6 @@ struct dm_path {
unsigned is_active; /* Read-only */
void *pscontext; /* For path-selector use */
- void *hwhcontext; /* For hw-handler use */
};
/* Callback for hwh_pg_init_fn to use when complete */
diff --git a/drivers/media/common/tuners/tda18271-maps.c b/drivers/media/common/tuners/tda18271-maps.c
index 83e7561960c1..ab14ceb9e0ce 100644
--- a/drivers/media/common/tuners/tda18271-maps.c
+++ b/drivers/media/common/tuners/tda18271-maps.c
@@ -1,5 +1,5 @@
/*
- tda18271-tables.c - driver for the Philips / NXP TDA18271 silicon tuner
+ tda18271-maps.c - driver for the Philips / NXP TDA18271 silicon tuner
Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org>
diff --git a/drivers/media/common/tuners/tuner-i2c.h b/drivers/media/common/tuners/tuner-i2c.h
index cb1c7141f0c6..ef2aee7c576b 100644
--- a/drivers/media/common/tuners/tuner-i2c.h
+++ b/drivers/media/common/tuners/tuner-i2c.h
@@ -178,4 +178,12 @@ __fail: \
__ret; \
})
+#define hybrid_tuner_report_instance_count(state) \
+({ \
+ int __ret = 0; \
+ if (state) \
+ __ret = state->i2c_props.count; \
+ __ret; \
+})
+
#endif /* __TUNER_I2C_H__ */
diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c
index 0cbde17bfbb7..30eb07b7f9ef 100644
--- a/drivers/media/common/tuners/tuner-xc2028.c
+++ b/drivers/media/common/tuners/tuner-xc2028.c
@@ -254,7 +254,7 @@ static int load_all_firmwares(struct dvb_frontend *fe)
{
struct xc2028_data *priv = fe->tuner_priv;
const struct firmware *fw = NULL;
- unsigned char *p, *endp;
+ const unsigned char *p, *endp;
int rc = 0;
int n, n_array;
char name[33];
diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c
index ceae6db901ec..d21a5a750ce2 100644
--- a/drivers/media/common/tuners/xc5000.c
+++ b/drivers/media/common/tuners/xc5000.c
@@ -277,7 +277,7 @@ static int xc_read_reg(struct xc5000_priv *priv, u16 regAddr, u16 *i2cData)
return result;
}
-static int xc_load_i2c_sequence(struct dvb_frontend *fe, u8 i2c_sequence[])
+static int xc_load_i2c_sequence(struct dvb_frontend *fe, const u8 *i2c_sequence)
{
struct xc5000_priv *priv = fe->tuner_priv;
diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c
index 8b56d929f7fd..10f1b3a02b6b 100644
--- a/drivers/media/dvb/dvb-core/dvbdev.c
+++ b/drivers/media/dvb/dvb-core/dvbdev.c
@@ -229,9 +229,9 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
mutex_unlock(&dvbdev_register_lock);
- clsdev = device_create(dvb_class, adap->device,
+ clsdev = device_create_drvdata(dvb_class, adap->device,
MKDEV(DVB_MAJOR, nums2minor(adap->num, type, id)),
- "dvb%d.%s%d", adap->num, dnames[type], id);
+ NULL, "dvb%d.%s%d", adap->num, dnames[type], id);
if (IS_ERR(clsdev)) {
printk(KERN_ERR "%s: failed to create device dvb%d.%s%d (%ld)\n",
__func__, adap->num, dnames[type], id, PTR_ERR(clsdev));
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index f00a0eb40420..3f7b9b6326b3 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -241,3 +241,12 @@ config DVB_USB_AF9005_REMOTE
Say Y here to support the default remote control decoding for the
Afatech AF9005 based receiver.
+config DVB_USB_ANYSEE
+ tristate "Anysee DVB-T/C USB2.0 support"
+ depends on DVB_USB
+ select DVB_MT352 if !DVB_FE_CUSTOMISE
+ select DVB_ZL10353 if !DVB_FE_CUSTOMISE
+ select DVB_TDA10023 if !DVB_FE_CUSTOMISE
+ help
+ Say Y here to support the Anysee E30, Anysee E30 Plus or
+ Anysee E30 C Plus DVB USB2.0 receiver.
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
index c6511a6c0ab8..44c11e45e564 100644
--- a/drivers/media/dvb/dvb-usb/Makefile
+++ b/drivers/media/dvb/dvb-usb/Makefile
@@ -61,6 +61,9 @@ 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
+dvb-usb-anysee-objs = anysee.o
+obj-$(CONFIG_DVB_USB_ANYSEE) += dvb-usb-anysee.o
+
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
# due to tuner-xc3028
EXTRA_CFLAGS += -Idrivers/media/common/tuners
diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c
new file mode 100644
index 000000000000..dada92a14cf3
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/anysee.c
@@ -0,0 +1,555 @@
+/*
+ * DVB USB Linux driver for Anysee E30 DVB-C & DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * 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.
+ *
+ * TODO:
+ * - add smart card reader support for Conditional Access (CA)
+ *
+ * Card reader in Anysee is nothing more than ISO 7816 card reader.
+ * There is no hardware CAM in any Anysee device sold.
+ * In my understanding it should be implemented by making own module
+ * for ISO 7816 card reader, like dvb_ca_en50221 is implented. This
+ * module registers serial interface that can be used to comminicate
+ * with any ISO 7816 smart card.
+ *
+ * Any help according to implement serial smart card reader support
+ * is highly welcome!
+ */
+
+#include "anysee.h"
+#include "tda1002x.h"
+#include "mt352.h"
+#include "mt352_priv.h"
+#include "zl10353.h"
+
+/* debug */
+static int dvb_usb_anysee_debug;
+module_param_named(debug, dvb_usb_anysee_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+struct mutex anysee_usb_mutex;
+
+static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen,
+ u8 *rbuf, u8 rlen)
+{
+ struct anysee_state *state = d->priv;
+ int act_len, ret;
+ u8 buf[64];
+
+ if (slen > sizeof(buf))
+ slen = sizeof(buf);
+ memcpy(&buf[0], sbuf, slen);
+ buf[60] = state->seq++;
+
+ if (mutex_lock_interruptible(&anysee_usb_mutex) < 0)
+ return -EAGAIN;
+
+ /* We need receive one message more after dvb_usb_generic_rw due
+ to weird transaction flow, which is 1 x send + 2 x receive. */
+ ret = dvb_usb_generic_rw(d, buf, sizeof(buf), buf, sizeof(buf), 0);
+
+ if (!ret) {
+ /* receive 2nd answer */
+ ret = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev,
+ d->props.generic_bulk_ctrl_endpoint), buf, sizeof(buf),
+ &act_len, 2000);
+ if (ret)
+ err("%s: recv bulk message failed: %d", __func__, ret);
+ else {
+ deb_xfer("<<< ");
+ debug_dump(buf, act_len, deb_xfer);
+ }
+ }
+
+ /* read request, copy returned data to return buf */
+ if (!ret && rbuf && rlen)
+ memcpy(rbuf, buf, rlen);
+
+ mutex_unlock(&anysee_usb_mutex);
+
+ return ret;
+}
+
+static int anysee_read_reg(struct dvb_usb_device *d, u16 reg, u8 *val)
+{
+ u8 buf[] = {CMD_REG_READ, reg >> 8, reg & 0xff, 0x01};
+ int ret;
+ ret = anysee_ctrl_msg(d, buf, sizeof(buf), val, 1);
+ deb_info("%s: reg:%04x val:%02x\n", __func__, reg, *val);
+ return ret;
+}
+
+static int anysee_write_reg(struct dvb_usb_device *d, u16 reg, u8 val)
+{
+ u8 buf[] = {CMD_REG_WRITE, reg >> 8, reg & 0xff, 0x01, val};
+ deb_info("%s: reg:%04x val:%02x\n", __func__, reg, val);
+ return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0);
+}
+
+static int anysee_get_hw_info(struct dvb_usb_device *d, u8 *id)
+{
+ u8 buf[] = {CMD_GET_HW_INFO};
+ return anysee_ctrl_msg(d, buf, sizeof(buf), id, 3);
+}
+
+static int anysee_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ u8 buf[] = {CMD_STREAMING_CTRL, (u8)onoff, 0x00};
+ deb_info("%s: onoff:%02x\n", __func__, onoff);
+ return anysee_ctrl_msg(adap->dev, buf, sizeof(buf), NULL, 0);
+}
+
+static int anysee_led_ctrl(struct dvb_usb_device *d, u8 mode, u8 interval)
+{
+ u8 buf[] = {CMD_LED_AND_IR_CTRL, 0x01, mode, interval};
+ deb_info("%s: state:%02x interval:%02x\n", __func__, mode, interval);
+ return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0);
+}
+
+static int anysee_ir_ctrl(struct dvb_usb_device *d, u8 onoff)
+{
+ u8 buf[] = {CMD_LED_AND_IR_CTRL, 0x02, onoff};
+ deb_info("%s: onoff:%02x\n", __func__, onoff);
+ return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0);
+}
+
+static int anysee_init(struct dvb_usb_device *d)
+{
+ int ret;
+ /* LED light */
+ ret = anysee_led_ctrl(d, 0x01, 0x03);
+ if (ret)
+ return ret;
+
+ /* enable IR */
+ ret = anysee_ir_ctrl(d, 1);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/* I2C */
+static int anysee_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ int ret, inc, i = 0;
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ while (i < num) {
+ if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
+ u8 buf[6];
+ buf[0] = CMD_I2C_READ;
+ buf[1] = msg[i].addr + 1;
+ buf[2] = msg[i].buf[0];
+ buf[3] = 0x00;
+ buf[4] = 0x00;
+ buf[5] = 0x01;
+ ret = anysee_ctrl_msg(d, buf, sizeof(buf), msg[i+1].buf,
+ msg[i+1].len);
+ inc = 2;
+ } else {
+ u8 buf[4+msg[i].len];
+ buf[0] = CMD_I2C_WRITE;
+ buf[1] = msg[i].addr;
+ buf[2] = msg[i].len;
+ buf[3] = 0x01;
+ memcpy(&buf[4], msg[i].buf, msg[i].len);
+ ret = anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0);
+ inc = 1;
+ }
+ if (ret)
+ return ret;
+
+ i += inc;
+ }
+
+ mutex_unlock(&d->i2c_mutex);
+
+ return i;
+}
+
+static u32 anysee_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm anysee_i2c_algo = {
+ .master_xfer = anysee_master_xfer,
+ .functionality = anysee_i2c_func,
+};
+
+static int anysee_mt352_demod_init(struct dvb_frontend *fe)
+{
+ static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x28 };
+ static u8 reset [] = { RESET, 0x80 };
+ static u8 adc_ctl_1_cfg [] = { ADC_CTL_1, 0x40 };
+ static u8 agc_cfg [] = { AGC_TARGET, 0x28, 0x20 };
+ static u8 gpp_ctl_cfg [] = { GPP_CTL, 0x33 };
+ static u8 capt_range_cfg[] = { CAPT_RANGE, 0x32 };
+
+ mt352_write(fe, clock_config, sizeof(clock_config));
+ udelay(200);
+ mt352_write(fe, reset, sizeof(reset));
+ mt352_write(fe, adc_ctl_1_cfg, sizeof(adc_ctl_1_cfg));
+
+ mt352_write(fe, agc_cfg, sizeof(agc_cfg));
+ mt352_write(fe, gpp_ctl_cfg, sizeof(gpp_ctl_cfg));
+ mt352_write(fe, capt_range_cfg, sizeof(capt_range_cfg));
+
+ return 0;
+}
+
+/* Callbacks for DVB USB */
+static struct tda10023_config anysee_tda10023_config = {
+ .demod_address = 0x1a,
+ .invert = 0,
+ .xtal = 16000000,
+ .pll_m = 11,
+ .pll_p = 3,
+ .pll_n = 1,
+ .deltaf = 0xfed6,
+};
+
+static struct mt352_config anysee_mt352_config = {
+ .demod_address = 0x1e,
+ .demod_init = anysee_mt352_demod_init,
+};
+
+static struct zl10353_config anysee_zl10353_config = {
+ .demod_address = 0x1e,
+ .parallel_ts = 1,
+};
+
+static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ struct anysee_state *state = adap->dev->priv;
+ u8 hw_info[3];
+ u8 io_d; /* IO port D */
+
+ /* check which hardware we have
+ We must do this call two times to get reliable values (hw bug). */
+ ret = anysee_get_hw_info(adap->dev, hw_info);
+ if (ret)
+ return ret;
+ ret = anysee_get_hw_info(adap->dev, hw_info);
+ if (ret)
+ return ret;
+
+ /* Meaning of these info bytes are guessed. */
+ info("firmware version:%d.%d.%d hardware id:%d",
+ 0, hw_info[1], hw_info[2], hw_info[0]);
+
+ ret = anysee_read_reg(adap->dev, 0xb0, &io_d); /* IO port D */
+ if (ret)
+ return ret;
+ deb_info("%s: IO port D:%02x\n", __func__, io_d);
+
+ /* Select demod using trial and error method. */
+
+ /* Try to attach demodulator in following order:
+ model demod hw firmware
+ 1. E30 MT352 02 0.2.1
+ 2. E30 ZL10353 02 0.2.1
+ 3. E30 Plus ZL10353 06 0.1.0
+ 4. E30C Plus TDA10023 0a 0.1.0
+ E30C Plus TDA10023 0f 0.1.2 (not working)
+ */
+
+ /* Zarlink MT352 DVB-T demod inside of Samsung DNOS404ZH102A NIM */
+ adap->fe = dvb_attach(mt352_attach, &anysee_mt352_config,
+ &adap->dev->i2c_adap);
+ if (adap->fe != NULL) {
+ state->tuner = DVB_PLL_THOMSON_DTT7579;
+ return 0;
+ }
+
+ /* Zarlink ZL10353 DVB-T demod inside of Samsung DNOS404ZH103A NIM */
+ adap->fe = dvb_attach(zl10353_attach, &anysee_zl10353_config,
+ &adap->dev->i2c_adap);
+ if (adap->fe != NULL) {
+ state->tuner = DVB_PLL_THOMSON_DTT7579;
+ return 0;
+ }
+
+ /* connect demod on IO port D for TDA10023 & ZL10353 */
+ ret = anysee_write_reg(adap->dev, 0xb0, 0x25);
+ if (ret)
+ return ret;
+
+ /* Zarlink ZL10353 DVB-T demod inside of Samsung DNOS404ZH103A NIM */
+ adap->fe = dvb_attach(zl10353_attach, &anysee_zl10353_config,
+ &adap->dev->i2c_adap);
+ if (adap->fe != NULL) {
+ state->tuner = DVB_PLL_THOMSON_DTT7579;
+ return 0;
+ }
+
+ /* known not working (E30C Plus v0.1.2) */
+ if (hw_info[0] == 0x0f) {
+ info("this version of Anysee is not supported yet");
+ /* return IO port D to init value for safe */
+ ret = anysee_write_reg(adap->dev, 0xb0, io_d);
+ return -ENODEV;
+ }
+
+ /* Philips TDA10023 DVB-C demod */
+ adap->fe = dvb_attach(tda10023_attach, &anysee_tda10023_config,
+ &adap->dev->i2c_adap, 0x48);
+ if (adap->fe != NULL) {
+ state->tuner = DVB_PLL_SAMSUNG_DTOS403IH102A;
+ return 0;
+ }
+
+ /* return IO port D to init value for safe */
+ ret = anysee_write_reg(adap->dev, 0xb0, io_d);
+ if (ret)
+ return ret;
+
+ err("Unkown Anysee version: %02x %02x %02x. "\
+ "Please report the <linux-dvb@linuxtv.org>.",
+ hw_info[0], hw_info[1], hw_info[2]);
+
+ return -ENODEV;
+}
+
+static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ struct anysee_state *state = adap->dev->priv;
+ deb_info("%s: \n", __func__);
+
+ switch (state->tuner) {
+ case DVB_PLL_THOMSON_DTT7579:
+ /* Thomson dtt7579 (not sure) PLL inside of:
+ Samsung DNOS404ZH102A NIM
+ Samsung DNOS404ZH103A NIM */
+ dvb_attach(dvb_pll_attach, adap->fe, 0x61,
+ NULL, DVB_PLL_THOMSON_DTT7579);
+ break;
+ case DVB_PLL_SAMSUNG_DTOS403IH102A:
+ /* Unknown PLL inside of Samsung DTOS403IH102A tuner module */
+ dvb_attach(dvb_pll_attach, adap->fe, 0xc0,
+ &adap->dev->i2c_adap, DVB_PLL_SAMSUNG_DTOS403IH102A);
+ break;
+ }
+
+ return 0;
+}
+
+static int anysee_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
+{
+ u8 buf[] = {CMD_GET_IR_CODE};
+ struct dvb_usb_rc_key *keymap = d->props.rc_key_map;
+ u8 ircode[2];
+ int i, ret;
+
+ ret = anysee_ctrl_msg(d, buf, sizeof(buf), &ircode[0], 2);
+ if (ret)
+ return ret;
+
+ *event = 0;
+ *state = REMOTE_NO_KEY_PRESSED;
+
+ for (i = 0; i < d->props.rc_key_map_size; i++) {
+ if (keymap[i].custom == ircode[0] &&
+ keymap[i].data == ircode[1]) {
+ *event = keymap[i].event;
+ *state = REMOTE_KEY_PRESSED;
+ return 0;
+ }
+ }
+ return 0;
+}
+
+static struct dvb_usb_rc_key anysee_rc_keys[] = {
+ { 0x01, 0x00, KEY_0 },
+ { 0x01, 0x01, KEY_1 },
+ { 0x01, 0x02, KEY_2 },
+ { 0x01, 0x03, KEY_3 },
+ { 0x01, 0x04, KEY_4 },
+ { 0x01, 0x05, KEY_5 },
+ { 0x01, 0x06, KEY_6 },
+ { 0x01, 0x07, KEY_7 },
+ { 0x01, 0x08, KEY_8 },
+ { 0x01, 0x09, KEY_9 },
+ { 0x01, 0x0a, KEY_POWER },
+ { 0x01, 0x0b, KEY_DOCUMENTS }, /* * */
+ { 0x01, 0x19, KEY_FAVORITES },
+ { 0x01, 0x20, KEY_SLEEP },
+ { 0x01, 0x21, KEY_MODE }, /* 4:3 / 16:9 select */
+ { 0x01, 0x22, KEY_ZOOM },
+ { 0x01, 0x47, KEY_TEXT },
+ { 0x01, 0x16, KEY_TV }, /* TV / radio select */
+ { 0x01, 0x1e, KEY_LANGUAGE }, /* Second Audio Program */
+ { 0x01, 0x1a, KEY_SUBTITLE },
+ { 0x01, 0x1b, KEY_CAMERA }, /* screenshot */
+ { 0x01, 0x42, KEY_MUTE },
+ { 0x01, 0x0e, KEY_MENU },
+ { 0x01, 0x0f, KEY_EPG },
+ { 0x01, 0x17, KEY_INFO },
+ { 0x01, 0x10, KEY_EXIT },
+ { 0x01, 0x13, KEY_VOLUMEUP },
+ { 0x01, 0x12, KEY_VOLUMEDOWN },
+ { 0x01, 0x11, KEY_CHANNELUP },
+ { 0x01, 0x14, KEY_CHANNELDOWN },
+ { 0x01, 0x15, KEY_OK },
+ { 0x01, 0x1d, KEY_RED },
+ { 0x01, 0x1f, KEY_GREEN },
+ { 0x01, 0x1c, KEY_YELLOW },
+ { 0x01, 0x44, KEY_BLUE },
+ { 0x01, 0x0c, KEY_SHUFFLE }, /* snapshot */
+ { 0x01, 0x48, KEY_STOP },
+ { 0x01, 0x50, KEY_PLAY },
+ { 0x01, 0x51, KEY_PAUSE },
+ { 0x01, 0x49, KEY_RECORD },
+ { 0x01, 0x18, KEY_PREVIOUS }, /* |<< */
+ { 0x01, 0x0d, KEY_NEXT }, /* >>| */
+ { 0x01, 0x24, KEY_PROG1 }, /* F1 */
+ { 0x01, 0x25, KEY_PROG2 }, /* F2 */
+};
+
+/* DVB USB Driver stuff */
+static struct dvb_usb_device_properties anysee_properties;
+
+static int anysee_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct dvb_usb_device *d;
+ struct usb_host_interface *alt;
+ int ret;
+
+ mutex_init(&anysee_usb_mutex);
+
+ /* There is one interface with two alternate settings.
+ Alternate setting 0 is for bulk transfer.
+ Alternate setting 1 is for isochronous transfer.
+ We use bulk transfer (alternate setting 0). */
+ if (intf->num_altsetting < 1)
+ return -ENODEV;
+
+ ret = dvb_usb_device_init(intf, &anysee_properties, THIS_MODULE, &d,
+ adapter_nr);
+ if (ret)
+ return ret;
+
+ alt = usb_altnum_to_altsetting(intf, 0);
+ if (alt == NULL) {
+ deb_info("%s: no alt found!\n", __func__);
+ return -ENODEV;
+ }
+
+ ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber,
+ alt->desc.bAlternateSetting);
+ if (ret)
+ return ret;
+
+ if (d)
+ ret = anysee_init(d);
+
+ return ret;
+}
+
+static struct usb_device_id anysee_table [] = {
+ { USB_DEVICE(USB_VID_CYPRESS, USB_PID_ANYSEE) },
+ { USB_DEVICE(USB_VID_AMT, USB_PID_ANYSEE) },
+ { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, anysee_table);
+
+static struct dvb_usb_device_properties anysee_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = DEVICE_SPECIFIC,
+
+ .size_of_priv = sizeof(struct anysee_state),
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .streaming_ctrl = anysee_streaming_ctrl,
+ .frontend_attach = anysee_frontend_attach,
+ .tuner_attach = anysee_tuner_attach,
+ .stream = {
+ .type = USB_BULK,
+ .count = 8,
+ .endpoint = 0x82,
+ .u = {
+ .bulk = {
+ .buffersize = 512,
+ }
+ }
+ },
+ }
+ },
+
+ .rc_key_map = anysee_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(anysee_rc_keys),
+ .rc_query = anysee_rc_query,
+ .rc_interval = 200, /* windows driver uses 500ms */
+
+ .i2c_algo = &anysee_i2c_algo,
+
+ .generic_bulk_ctrl_endpoint = 1,
+
+ .num_device_descs = 1,
+ .devices = {
+ {
+ .name = "Anysee DVB USB2.0",
+ .cold_ids = {NULL},
+ .warm_ids = {&anysee_table[0],
+ &anysee_table[1], NULL},
+ },
+ }
+};
+
+static struct usb_driver anysee_driver = {
+ .name = "dvb_usb_anysee",
+ .probe = anysee_probe,
+ .disconnect = dvb_usb_device_exit,
+ .id_table = anysee_table,
+};
+
+/* module stuff */
+static int __init anysee_module_init(void)
+{
+ int ret;
+
+ ret = usb_register(&anysee_driver);
+ if (ret)
+ err("%s: usb_register failed. Error number %d", __func__, ret);
+
+ return ret;
+}
+
+static void __exit anysee_module_exit(void)
+{
+ /* deregister this driver from the USB subsystem */
+ usb_deregister(&anysee_driver);
+}
+
+module_init(anysee_module_init);
+module_exit(anysee_module_exit);
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Driver Anysee E30 DVB-C & DVB-T USB2.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/anysee.h b/drivers/media/dvb/dvb-usb/anysee.h
new file mode 100644
index 000000000000..48da3949ef0b
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/anysee.h
@@ -0,0 +1,304 @@
+/*
+ * DVB USB Linux driver for Anysee E30 DVB-C & DVB-T USB2.0 receiver
+ *
+ * Copyright (C) 2007 Antti Palosaari <crope@iki.fi>
+ *
+ * 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.
+ *
+ * TODO:
+ * - add smart card reader support for Conditional Access (CA)
+ *
+ * Card reader in Anysee is nothing more than ISO 7816 card reader.
+ * There is no hardware CAM in any Anysee device sold.
+ * In my understanding it should be implemented by making own module
+ * for ISO 7816 card reader, like dvb_ca_en50221 is implented. This
+ * module registers serial interface that can be used to comminicate
+ * with any ISO 7816 smart card.
+ *
+ * Any help according to implement serial smart card reader support
+ * is highly welcome!
+ */
+
+#ifndef _DVB_USB_ANYSEE_H_
+#define _DVB_USB_ANYSEE_H_
+
+#define DVB_USB_LOG_PREFIX "anysee"
+#include "dvb-usb.h"
+
+#define deb_info(args...) dprintk(dvb_usb_anysee_debug, 0x01, args)
+#define deb_xfer(args...) dprintk(dvb_usb_anysee_debug, 0x02, args)
+#define deb_rc(args...) dprintk(dvb_usb_anysee_debug, 0x04, args)
+#define deb_reg(args...) dprintk(dvb_usb_anysee_debug, 0x08, args)
+#define deb_i2c(args...) dprintk(dvb_usb_anysee_debug, 0x10, args)
+#define deb_fw(args...) dprintk(dvb_usb_anysee_debug, 0x20, args)
+
+enum cmd {
+ CMD_I2C_READ = 0x33,
+ CMD_I2C_WRITE = 0x31,
+ CMD_REG_READ = 0xb0,
+ CMD_REG_WRITE = 0xb1,
+ CMD_STREAMING_CTRL = 0x12,
+ CMD_LED_AND_IR_CTRL = 0x16,
+ CMD_GET_IR_CODE = 0x41,
+ CMD_GET_HW_INFO = 0x19,
+ CMD_SMARTCARD = 0x34,
+};
+
+struct anysee_state {
+ u8 tuner;
+ u8 seq;
+};
+
+#endif
+
+/***************************************************************************
+ * USB API description (reverse engineered)
+ ***************************************************************************
+
+Transaction flow:
+=================
+BULK[00001] >>> REQUEST PACKET 64 bytes
+BULK[00081] <<< REPLY PACKET #1 64 bytes (PREVIOUS TRANSACTION REPLY)
+BULK[00081] <<< REPLY PACKET #2 64 bytes (CURRENT TRANSACTION REPLY)
+
+General reply packet(s) are always used if not own reply defined.
+
+============================================================================
+| 00-63 | GENERAL REPLY PACKET #1 (PREVIOUS REPLY)
+============================================================================
+| 00 | reply data (if any) from previous transaction
+| | Just same reply packet as returned during previous transaction.
+| | Needed only if reply is missed in previous transaction.
+| | Just skip normally.
+----------------------------------------------------------------------------
+| 01-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | GENERAL REPLY PACKET #2 (CURRENT REPLY)
+============================================================================
+| 00 | reply data (if any)
+----------------------------------------------------------------------------
+| 01-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | I2C WRITE REQUEST PACKET
+============================================================================
+| 00 | 0x31 I2C write command
+----------------------------------------------------------------------------
+| 01 | i2c address
+----------------------------------------------------------------------------
+| 02 | data length
+| | 0x02 (for typical I2C reg / val pair)
+----------------------------------------------------------------------------
+| 03 | 0x01
+----------------------------------------------------------------------------
+| 04- | data
+----------------------------------------------------------------------------
+| -59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | I2C READ REQUEST PACKET
+============================================================================
+| 00 | 0x33 I2C read command
+----------------------------------------------------------------------------
+| 01 | i2c address + 1
+----------------------------------------------------------------------------
+| 02 | register
+----------------------------------------------------------------------------
+| 03 | 0x00
+----------------------------------------------------------------------------
+| 04 | 0x00
+----------------------------------------------------------------------------
+| 05 | 0x01
+----------------------------------------------------------------------------
+| 06-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | USB CONTROLLER REGISTER WRITE REQUEST PACKET
+============================================================================
+| 00 | 0xb1 register write command
+----------------------------------------------------------------------------
+| 01-02 | register
+----------------------------------------------------------------------------
+| 03 | 0x01
+----------------------------------------------------------------------------
+| 04 | value
+----------------------------------------------------------------------------
+| 05-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | USB CONTROLLER REGISTER READ REQUEST PACKET
+============================================================================
+| 00 | 0xb0 register read command
+----------------------------------------------------------------------------
+| 01-02 | register
+----------------------------------------------------------------------------
+| 03 | 0x01
+----------------------------------------------------------------------------
+| 04-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | LED CONTROL REQUEST PACKET
+============================================================================
+| 00 | 0x16 LED and IR control command
+----------------------------------------------------------------------------
+| 01 | 0x01 (LED)
+----------------------------------------------------------------------------
+| 03 | 0x00 blink
+| | 0x01 lights continuously
+----------------------------------------------------------------------------
+| 04 | blink interval
+| | 0x00 fastest (looks like LED lights continuously)
+| | 0xff slowest
+----------------------------------------------------------------------------
+| 05-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | IR CONTROL REQUEST PACKET
+============================================================================
+| 00 | 0x16 LED and IR control command
+----------------------------------------------------------------------------
+| 01 | 0x02 (IR)
+----------------------------------------------------------------------------
+| 03 | 0x00 IR disabled
+| | 0x01 IR enabled
+----------------------------------------------------------------------------
+| 04-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | STREAMING CONTROL REQUEST PACKET
+============================================================================
+| 00 | 0x12 streaming control command
+----------------------------------------------------------------------------
+| 01 | 0x00 streaming disabled
+| | 0x01 streaming enabled
+----------------------------------------------------------------------------
+| 02 | 0x00
+----------------------------------------------------------------------------
+| 03-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | REMOTE CONTROL REQUEST PACKET
+============================================================================
+| 00 | 0x41 remote control command
+----------------------------------------------------------------------------
+| 01-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | REMOTE CONTROL REPLY PACKET
+============================================================================
+| 00 | 0x00 code not received
+| | 0x01 code received
+----------------------------------------------------------------------------
+| 01 | remote control code
+----------------------------------------------------------------------------
+| 02-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | GET HARDWARE INFO REQUEST PACKET
+============================================================================
+| 00 | 0x19 get hardware info command
+----------------------------------------------------------------------------
+| 01-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | GET HARDWARE INFO REPLY PACKET
+============================================================================
+| 00 | hardware id
+----------------------------------------------------------------------------
+| 01-02 | firmware version
+----------------------------------------------------------------------------
+| 03-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+============================================================================
+| 00-63 | SMART CARD READER PACKET
+============================================================================
+| 00 | 0x34 smart card reader command
+----------------------------------------------------------------------------
+| xx |
+----------------------------------------------------------------------------
+| xx-59 | don't care
+----------------------------------------------------------------------------
+| 60 | packet sequence number
+----------------------------------------------------------------------------
+| 61-63 | don't care
+----------------------------------------------------------------------------
+
+*/
diff --git a/drivers/media/dvb/dvb-usb/au6610.c b/drivers/media/dvb/dvb-usb/au6610.c
index 2ccb90fa60c8..eb34cc3894e0 100644
--- a/drivers/media/dvb/dvb-usb/au6610.c
+++ b/drivers/media/dvb/dvb-usb/au6610.c
@@ -1,24 +1,31 @@
-/* DVB USB compliant linux driver for Sigmatek DVB-110 DVB-T USB2.0 receiver
+/*
+ * DVB USB Linux driver for Alcor Micro AU6610 DVB-T USB2.0.
*
* Copyright (C) 2006 Antti Palosaari <crope@iki.fi>
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation, version 2.
+ * 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.
*
- * see Documentation/dvb/README.dvb-usb for more information
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "au6610.h"
-
#include "zl10353.h"
#include "qt1010.h"
/* debug */
static int dvb_usb_au6610_debug;
module_param_named(debug, dvb_usb_au6610_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
-
+MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
static int au6610_usb_msg(struct dvb_usb_device *d, u8 operation, u8 addr,
@@ -42,9 +49,8 @@ static int au6610_usb_msg(struct dvb_usb_device *d, u8 operation, u8 addr,
}
ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), operation,
- USB_TYPE_VENDOR|USB_DIR_IN, addr << 1, index, usb_buf,
- sizeof(usb_buf), AU6610_USB_TIMEOUT);
-
+ USB_TYPE_VENDOR|USB_DIR_IN, addr << 1, index,
+ usb_buf, sizeof(usb_buf), AU6610_USB_TIMEOUT);
if (ret < 0)
return ret;
@@ -116,15 +122,6 @@ static struct i2c_algorithm au6610_i2c_algo = {
};
/* Callbacks for DVB USB */
-static int au6610_identify_state(struct usb_device *udev,
- struct dvb_usb_device_properties *props,
- struct dvb_usb_device_description **desc,
- int *cold)
-{
- *cold = 0;
- return 0;
-}
-
static struct zl10353_config au6610_zl10353_config = {
.demod_address = 0x0f,
.no_tuner = 1,
@@ -133,12 +130,12 @@ static struct zl10353_config au6610_zl10353_config = {
static int au6610_zl10353_frontend_attach(struct dvb_usb_adapter *adap)
{
- if ((adap->fe = dvb_attach(zl10353_attach, &au6610_zl10353_config,
- &adap->dev->i2c_adap)) != NULL) {
- return 0;
- }
+ adap->fe = dvb_attach(zl10353_attach, &au6610_zl10353_config,
+ &adap->dev->i2c_adap);
+ if (adap->fe == NULL)
+ return -ENODEV;
- return -EIO;
+ return 0;
}
static struct qt1010_config au6610_qt1010_config = {
@@ -171,7 +168,7 @@ static int au6610_probe(struct usb_interface *intf,
alt = usb_altnum_to_altsetting(intf, AU6610_ALTSETTING);
if (alt == NULL) {
- deb_rc("no alt found!\n");
+ deb_info("%s: no alt found!\n", __func__);
return -ENODEV;
}
ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber,
@@ -181,18 +178,19 @@ static int au6610_probe(struct usb_interface *intf,
return ret;
}
-
static struct usb_device_id au6610_table [] = {
{ USB_DEVICE(USB_VID_ALCOR_MICRO, USB_PID_SIGMATEK_DVB_110) },
{ } /* Terminating entry */
};
-MODULE_DEVICE_TABLE (usb, au6610_table);
+MODULE_DEVICE_TABLE(usb, au6610_table);
static struct dvb_usb_device_properties au6610_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
.usb_ctrl = DEVICE_SPECIFIC,
- .size_of_priv = 0,
- .identify_state = au6610_identify_state,
+
+ .size_of_priv = 0,
+
.num_adapters = 1,
.adapter = {
{
@@ -206,20 +204,22 @@ static struct dvb_usb_device_properties au6610_properties = {
.u = {
.isoc = {
.framesperurb = 40,
- .framesize = 942, /* maximum packet size */
- .interval = 1.25, /* 125 us */
+ .framesize = 942,
+ .interval = 1,
}
}
},
}
},
+
.i2c_algo = &au6610_i2c_algo,
+
.num_device_descs = 1,
.devices = {
{
- "Sigmatek DVB-110 DVB-T USB2.0",
- { &au6610_table[0], NULL },
- { NULL },
+ .name = "Sigmatek DVB-110 DVB-T USB2.0",
+ .cold_ids = {NULL},
+ .warm_ids = {&au6610_table[0], NULL},
},
}
};
@@ -236,12 +236,11 @@ static int __init au6610_module_init(void)
{
int ret;
- if ((ret = usb_register(&au6610_driver))) {
+ ret = usb_register(&au6610_driver);
+ if (ret)
err("usb_register failed. Error number %d", ret);
- return ret;
- }
- return 0;
+ return ret;
}
static void __exit au6610_module_exit(void)
@@ -250,10 +249,10 @@ static void __exit au6610_module_exit(void)
usb_deregister(&au6610_driver);
}
-module_init (au6610_module_init);
-module_exit (au6610_module_exit);
+module_init(au6610_module_init);
+module_exit(au6610_module_exit);
MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
-MODULE_DESCRIPTION("Driver Sigmatek DVB-110 DVB-T USB2.0 / AU6610");
+MODULE_DESCRIPTION("Driver for Alcor Micro AU6610 DVB-T USB2.0");
MODULE_VERSION("0.1");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/au6610.h b/drivers/media/dvb/dvb-usb/au6610.h
index 4161b054c713..7849abe2c614 100644
--- a/drivers/media/dvb/dvb-usb/au6610.h
+++ b/drivers/media/dvb/dvb-usb/au6610.h
@@ -1,10 +1,30 @@
+/*
+ * DVB USB Linux driver for Alcor Micro AU6610 DVB-T USB2.0.
+ *
+ * Copyright (C) 2006 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
#ifndef _DVB_USB_AU6610_H_
#define _DVB_USB_AU6610_H_
#define DVB_USB_LOG_PREFIX "au6610"
#include "dvb-usb.h"
-#define deb_rc(args...) dprintk(dvb_usb_au6610_debug,0x01,args)
+#define deb_info(args...) dprintk(dvb_usb_au6610_debug, 0x01, args)
#define AU6610_REQ_I2C_WRITE 0x14
#define AU6610_REQ_I2C_READ 0x13
diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c
index 720fcd1c3c1d..0286156704f2 100644
--- a/drivers/media/dvb/dvb-usb/cxusb.c
+++ b/drivers/media/dvb/dvb-usb/cxusb.c
@@ -24,6 +24,7 @@
* see Documentation/dvb/README.dvb-usb for more information
*/
#include <media/tuner.h>
+#include <linux/vmalloc.h>
#include "cxusb.h"
@@ -700,12 +701,26 @@ static int bluebird_patch_dvico_firmware_download(struct usb_device *udev,
if (fw->data[idoff] == (USB_VID_DVICO & 0xff) &&
fw->data[idoff + 1] == USB_VID_DVICO >> 8) {
- fw->data[idoff + 2] =
+ struct firmware new_fw;
+ u8 *new_fw_data = vmalloc(fw->size);
+ int ret;
+
+ if (!new_fw_data)
+ return -ENOMEM;
+
+ memcpy(new_fw_data, fw->data, fw->size);
+ new_fw.size = fw->size;
+ new_fw.data = new_fw_data;
+
+ new_fw_data[idoff + 2] =
le16_to_cpu(udev->descriptor.idProduct) + 1;
- fw->data[idoff + 3] =
+ new_fw_data[idoff + 3] =
le16_to_cpu(udev->descriptor.idProduct) >> 8;
- return usb_cypress_load_firmware(udev, fw, CYPRESS_FX2);
+ ret = usb_cypress_load_firmware(udev, &new_fw,
+ CYPRESS_FX2);
+ vfree(new_fw_data);
+ return ret;
}
}
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 34245d1b7dd9..e6b43fb3a148 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -14,6 +14,7 @@
#define USB_VID_AFATECH 0x15a4
#define USB_VID_ALCOR_MICRO 0x058f
#define USB_VID_ALINK 0x05e3
+#define USB_VID_AMT 0x1c73
#define USB_VID_ANCHOR 0x0547
#define USB_VID_ANSONIC 0x10b9
#define USB_VID_ANUBIS_ELECTRONIC 0x10fd
@@ -57,6 +58,7 @@
#define USB_PID_AFATECH_AF9005 0x9020
#define USB_VID_ALINK_DTU 0xf170
#define USB_PID_ANSONIC_DVBT_USB 0x6000
+#define USB_PID_ANYSEE 0x861f
#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001
#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002
#define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800
diff --git a/drivers/media/dvb/dvb-usb/gl861.c b/drivers/media/dvb/dvb-usb/gl861.c
index 0a8ac64a4e33..6f596ed41761 100644
--- a/drivers/media/dvb/dvb-usb/gl861.c
+++ b/drivers/media/dvb/dvb-usb/gl861.c
@@ -1,8 +1,8 @@
/* DVB USB compliant linux driver for GL861 USB2.0 devices.
*
* This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation, version 2.
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, version 2.
*
* see Documentation/dvb/README.dvb-usb for more information
*/
@@ -13,9 +13,9 @@
/* debug */
static int dvb_usb_gl861_debug;
-module_param_named(debug,dvb_usb_gl861_debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS);
-
+module_param_named(debug, dvb_usb_gl861_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))."
+ DVB_USB_DEBUG_STATUS);
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr,
@@ -47,6 +47,8 @@ static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr,
return -EINVAL;
}
+ msleep(1); /* avoid I2C errors */
+
return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), req, type,
value, index, rbuf, rlen, 2000);
}
@@ -68,7 +70,7 @@ static int gl861_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
/* write/read request */
if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf,
- msg[i].len, msg[i+1].buf, msg[i+1].len) < 0)
+ msg[i].len, msg[i+1].buf, msg[i+1].len) < 0)
break;
i++;
} else
@@ -92,16 +94,6 @@ static struct i2c_algorithm gl861_i2c_algo = {
};
/* Callbacks for DVB USB */
-static int gl861_identify_state(struct usb_device *udev,
- struct dvb_usb_device_properties *props,
- struct dvb_usb_device_description **desc,
- int *cold)
-{
- *cold = 0;
-
- return 0;
-}
-
static struct zl10353_config gl861_zl10353_config = {
.demod_address = 0x0f,
.no_tuner = 1,
@@ -110,12 +102,13 @@ static struct zl10353_config gl861_zl10353_config = {
static int gl861_frontend_attach(struct dvb_usb_adapter *adap)
{
- if ((adap->fe = dvb_attach(zl10353_attach, &gl861_zl10353_config,
- &adap->dev->i2c_adap)) != NULL) {
- return 0;
- }
- return -EIO;
+ adap->fe = dvb_attach(zl10353_attach, &gl861_zl10353_config,
+ &adap->dev->i2c_adap);
+ if (adap->fe == NULL)
+ return -EIO;
+
+ return 0;
}
static struct qt1010_config gl861_qt1010_config = {
@@ -164,7 +157,7 @@ static struct usb_device_id gl861_table [] = {
{ USB_DEVICE(USB_VID_ALINK, USB_VID_ALINK_DTU) },
{ } /* Terminating entry */
};
-MODULE_DEVICE_TABLE (usb, gl861_table);
+MODULE_DEVICE_TABLE(usb, gl861_table);
static struct dvb_usb_device_properties gl861_properties = {
.caps = DVB_USB_IS_AN_I2C_ADAPTER,
@@ -172,7 +165,6 @@ static struct dvb_usb_device_properties gl861_properties = {
.size_of_priv = 0,
- .identify_state = gl861_identify_state,
.num_adapters = 1,
.adapter = {{
@@ -189,18 +181,20 @@ static struct dvb_usb_device_properties gl861_properties = {
}
}
},
- }},
+ } },
.i2c_algo = &gl861_i2c_algo,
.num_device_descs = 2,
.devices = {
- { "MSI Mega Sky 55801 DVB-T USB2.0",
- { &gl861_table[0], NULL },
- { NULL },
+ {
+ .name = "MSI Mega Sky 55801 DVB-T USB2.0",
+ .cold_ids = { NULL },
+ .warm_ids = { &gl861_table[0], NULL },
},
- { "A-LINK DTU DVB-T USB2.0",
- { &gl861_table[1], NULL },
- { NULL },
+ {
+ .name = "A-LINK DTU DVB-T USB2.0",
+ .cold_ids = { NULL },
+ .warm_ids = { &gl861_table[1], NULL },
},
}
};
@@ -217,12 +211,11 @@ static int __init gl861_module_init(void)
{
int ret;
- if ((ret = usb_register(&gl861_driver))) {
+ ret = usb_register(&gl861_driver);
+ if (ret)
err("usb_register failed. Error number %d", ret);
- return ret;
- }
- return 0;
+ return ret;
}
static void __exit gl861_module_exit(void)
@@ -231,8 +224,8 @@ static void __exit gl861_module_exit(void)
usb_deregister(&gl861_driver);
}
-module_init (gl861_module_init);
-module_exit (gl861_module_exit);
+module_init(gl861_module_init);
+module_exit(gl861_module_exit);
MODULE_AUTHOR("Carl Lundqvist <comabug@gmail.com>");
MODULE_DESCRIPTION("Driver MSI Mega Sky 580 DVB-T USB2.0 / GL861");
diff --git a/drivers/media/dvb/dvb-usb/gl861.h b/drivers/media/dvb/dvb-usb/gl861.h
index 72a51afd5ee3..c54855e2c233 100644
--- a/drivers/media/dvb/dvb-usb/gl861.h
+++ b/drivers/media/dvb/dvb-usb/gl861.h
@@ -4,7 +4,7 @@
#define DVB_USB_LOG_PREFIX "gl861"
#include "dvb-usb.h"
-#define deb_rc(args...) dprintk(dvb_usb_gl861_debug,0x01,args)
+#define deb_rc(args...) dprintk(dvb_usb_gl861_debug, 0x01, args)
#define GL861_WRITE 0x40
#define GL861_READ 0xc0
diff --git a/drivers/media/dvb/dvb-usb/gp8psk.c b/drivers/media/dvb/dvb-usb/gp8psk.c
index 2653120673b7..d965a923f391 100644
--- a/drivers/media/dvb/dvb-usb/gp8psk.c
+++ b/drivers/media/dvb/dvb-usb/gp8psk.c
@@ -86,7 +86,8 @@ static int gp8psk_load_bcm4500fw(struct dvb_usb_device *d)
{
int ret;
const struct firmware *fw = NULL;
- u8 *ptr, *buf;
+ const u8 *ptr;
+ u8 *buf;
if ((ret = request_firmware(&fw, bcm4500_firmware,
&d->udev->dev)) != 0) {
err("did not find the bcm4500 firmware file. (%s) "
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index c20553c4da1f..fa7f0c563770 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -135,6 +135,20 @@ config DVB_CX22702
help
A DVB-T tuner module. Say Y when you want to support this frontend.
+config DVB_DRX397XD
+ tristate "Micronas DRX3975D/DRX3977D based"
+ depends on DVB_CORE && I2C && HOTPLUG
+ default m if DVB_FE_CUSTOMISE
+ select FW_LOADER
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
+ TODO:
+ This driver needs external firmware. Please use the command
+ "<kerneldir>/Documentation/dvb/get_dvb_firmware drx397xD" to
+ download/extract them, and then copy them to /usr/lib/hotplug/firmware
+ or /lib/firmware (depending on configuration of firmware hotplug).
+
config DVB_L64781
tristate "LSI L64781"
depends on DVB_CORE && I2C
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index a89dc0fc4c6f..028da55611c0 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_DVB_NXT6000) += nxt6000.o
obj-$(CONFIG_DVB_MT352) += mt352.o
obj-$(CONFIG_DVB_ZL10353) += zl10353.o
obj-$(CONFIG_DVB_CX22702) += cx22702.o
+obj-$(CONFIG_DVB_DRX397XD) += drx397xD.o
obj-$(CONFIG_DVB_TDA10021) += tda10021.o
obj-$(CONFIG_DVB_TDA10023) += tda10023.o
obj-$(CONFIG_DVB_STV0297) += stv0297.o
diff --git a/drivers/media/dvb/frontends/au8522.c b/drivers/media/dvb/frontends/au8522.c
index 084a280c2d7f..a972d42b9a08 100644
--- a/drivers/media/dvb/frontends/au8522.c
+++ b/drivers/media/dvb/frontends/au8522.c
@@ -26,7 +26,6 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include "dvb_frontend.h"
-#include "dvb-pll.h"
#include "au8522.h"
struct au8522_state {
diff --git a/drivers/media/dvb/frontends/bcm3510.c b/drivers/media/dvb/frontends/bcm3510.c
index d268e65e777d..cf5e576dfdcf 100644
--- a/drivers/media/dvb/frontends/bcm3510.c
+++ b/drivers/media/dvb/frontends/bcm3510.c
@@ -590,7 +590,8 @@ static void bcm3510_release(struct dvb_frontend* fe)
*/
#define BCM3510_DEFAULT_FIRMWARE "dvb-fe-bcm3510-01.fw"
-static int bcm3510_write_ram(struct bcm3510_state *st, u16 addr, u8 *b, u16 len)
+static int bcm3510_write_ram(struct bcm3510_state *st, u16 addr, const u8 *b,
+ u16 len)
{
int ret = 0,i;
bcm3510_register_value vH, vL,vD;
@@ -614,7 +615,7 @@ static int bcm3510_download_firmware(struct dvb_frontend* fe)
struct bcm3510_state* st = fe->demodulator_priv;
const struct firmware *fw;
u16 addr,len;
- u8 *b;
+ const u8 *b;
int ret,i;
deb_info("requesting firmware\n");
diff --git a/drivers/media/dvb/frontends/drx397xD.c b/drivers/media/dvb/frontends/drx397xD.c
new file mode 100644
index 000000000000..d71cce93d08e
--- /dev/null
+++ b/drivers/media/dvb/frontends/drx397xD.c
@@ -0,0 +1,1504 @@
+/*
+ * Driver for Micronas drx397xD demodulator
+ *
+ * Copyright (C) 2007 Henk Vergonet <Henk.Vergonet@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define DEBUG /* uncomment if you want debugging output */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/firmware.h>
+#include <asm/div64.h>
+
+#include "dvb_frontend.h"
+#include "drx397xD.h"
+
+static const char mod_name[] = "drx397xD";
+
+#define MAX_CLOCK_DRIFT 200 /* maximal 200 PPM allowed */
+
+#define F_SET_0D0h 1
+#define F_SET_0D4h 2
+
+typedef enum fw_ix {
+#define _FW_ENTRY(a, b) b
+#include "drx397xD_fw.h"
+} fw_ix_t;
+
+/* chip specifics */
+struct drx397xD_state {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend frontend;
+ struct drx397xD_config config;
+ fw_ix_t chip_rev;
+ int flags;
+ u32 bandwidth_parm; /* internal bandwidth conversions */
+ u32 f_osc; /* w90: actual osc frequency [Hz] */
+};
+
+/*******************************************************************************
+ * Firmware
+ ******************************************************************************/
+
+static const char *blob_name[] = {
+#define _BLOB_ENTRY(a, b) a
+#include "drx397xD_fw.h"
+};
+
+typedef enum blob_ix {
+#define _BLOB_ENTRY(a, b) b
+#include "drx397xD_fw.h"
+} blob_ix_t;
+
+static struct {
+ const char *name;
+ const struct firmware *file;
+ rwlock_t lock;
+ int refcnt;
+ u8 *data[ARRAY_SIZE(blob_name)];
+} fw[] = {
+#define _FW_ENTRY(a, b) { \
+ .name = a, \
+ .file = 0, \
+ .lock = RW_LOCK_UNLOCKED, \
+ .refcnt = 0, \
+ .data = { } }
+#include "drx397xD_fw.h"
+};
+
+/* use only with writer lock aquired */
+static void _drx_release_fw(struct drx397xD_state *s, fw_ix_t ix)
+{
+ memset(&fw[ix].data[0], 0, sizeof(fw[0].data));
+ if (fw[ix].file)
+ release_firmware(fw[ix].file);
+}
+
+static void drx_release_fw(struct drx397xD_state *s)
+{
+ fw_ix_t ix = s->chip_rev;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ write_lock(&fw[ix].lock);
+ if (fw[ix].refcnt) {
+ fw[ix].refcnt--;
+ if (fw[ix].refcnt == 0)
+ _drx_release_fw(s, ix);
+ }
+ write_unlock(&fw[ix].lock);
+}
+
+static int drx_load_fw(struct drx397xD_state *s, fw_ix_t ix)
+{
+ u8 *data;
+ size_t size, len;
+ int i = 0, j, rc = -EINVAL;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ if (ix < 0 || ix >= ARRAY_SIZE(fw))
+ return -EINVAL;
+ s->chip_rev = ix;
+
+ write_lock(&fw[ix].lock);
+ if (fw[ix].file) {
+ rc = 0;
+ goto exit_ok;
+ }
+ memset(&fw[ix].data[0], 0, sizeof(fw[0].data));
+
+ if (request_firmware(&fw[ix].file, fw[ix].name, &s->i2c->dev) != 0) {
+ printk(KERN_ERR "%s: Firmware \"%s\" not available\n",
+ mod_name, fw[ix].name);
+ rc = -ENOENT;
+ goto exit_err;
+ }
+
+ if (!fw[ix].file->data || fw[ix].file->size < 10)
+ goto exit_corrupt;
+
+ data = fw[ix].file->data;
+ size = fw[ix].file->size;
+
+ if (data[i++] != 2) /* check firmware version */
+ goto exit_corrupt;
+
+ do {
+ switch (data[i++]) {
+ case 0x00: /* bytecode */
+ if (i >= size)
+ break;
+ i += data[i];
+ case 0x01: /* reset */
+ case 0x02: /* sleep */
+ i++;
+ break;
+ case 0xfe: /* name */
+ len = strnlen(&data[i], size - i);
+ if (i + len + 1 >= size)
+ goto exit_corrupt;
+ if (data[i + len + 1] != 0)
+ goto exit_corrupt;
+ for (j = 0; j < ARRAY_SIZE(blob_name); j++) {
+ if (strcmp(blob_name[j], &data[i]) == 0) {
+ fw[ix].data[j] = &data[i + len + 1];
+ pr_debug("Loading %s\n", blob_name[j]);
+ }
+ }
+ i += len + 1;
+ break;
+ case 0xff: /* file terminator */
+ if (i == size) {
+ rc = 0;
+ goto exit_ok;
+ }
+ default:
+ goto exit_corrupt;
+ }
+ } while (i < size);
+ exit_corrupt:
+ printk(KERN_ERR "%s: Firmware is corrupt\n", mod_name);
+ exit_err:
+ _drx_release_fw(s, ix);
+ fw[ix].refcnt--;
+ exit_ok:
+ fw[ix].refcnt++;
+ write_unlock(&fw[ix].lock);
+ return rc;
+}
+
+/*******************************************************************************
+ * i2c bus IO
+ ******************************************************************************/
+
+static int write_fw(struct drx397xD_state *s, blob_ix_t ix)
+{
+ struct i2c_msg msg = {.addr = s->config.demod_address,.flags = 0 };
+ u8 *data;
+ int len, rc = 0, i = 0;
+
+ if (ix < 0 || ix >= ARRAY_SIZE(blob_name)) {
+ pr_debug("%s drx_fw_ix_t out of range\n", __FUNCTION__);
+ return -EINVAL;
+ }
+ pr_debug("%s %s\n", __FUNCTION__, blob_name[ix]);
+
+ read_lock(&fw[s->chip_rev].lock);
+ data = fw[s->chip_rev].data[ix];
+ if (!data) {
+ rc = -EINVAL;
+ goto exit_rc;
+ }
+
+ for (;;) {
+ switch (data[i++]) {
+ case 0: /* bytecode */
+ len = data[i++];
+ msg.len = len;
+ msg.buf = &data[i];
+ if (i2c_transfer(s->i2c, &msg, 1) != 1) {
+ rc = -EIO;
+ goto exit_rc;
+ }
+ i += len;
+ break;
+ case 1: /* reset */
+ case 2: /* sleep */
+ i++;
+ break;
+ default:
+ goto exit_rc;
+ }
+ }
+ exit_rc:
+ read_unlock(&fw[s->chip_rev].lock);
+ return 0;
+}
+
+/* Function is not endian safe, use the RD16 wrapper below */
+static int _read16(struct drx397xD_state *s, u32 i2c_adr)
+{
+ int rc;
+ u8 a[4];
+ u16 v;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = s->config.demod_address,
+ .flags = 0,
+ .buf = a,
+ .len = sizeof(a)
+ }
+ , {
+ .addr = s->config.demod_address,
+ .flags = I2C_M_RD,
+ .buf = (u8 *) & v,
+ .len = sizeof(v)
+ }
+ };
+
+ *(u32 *) a = i2c_adr;
+
+ rc = i2c_transfer(s->i2c, msg, 2);
+ if (rc != 2)
+ return -EIO;
+
+ return le16_to_cpu(v);
+}
+
+/* Function is not endian safe, use the WR16.. wrappers below */
+static int _write16(struct drx397xD_state *s, u32 i2c_adr, u16 val)
+{
+ u8 a[6];
+ int rc;
+ struct i2c_msg msg = {
+ .addr = s->config.demod_address,
+ .flags = 0,
+ .buf = a,
+ .len = sizeof(a)
+ };
+
+ *(u32 *) a = i2c_adr;
+ *(u16 *) & a[4] = val;
+
+ rc = i2c_transfer(s->i2c, &msg, 1);
+ if (rc != 1)
+ return -EIO;
+ return 0;
+}
+
+#define WR16(ss,adr, val) \
+ _write16(ss, I2C_ADR_C0(adr), cpu_to_le16(val))
+#define WR16_E0(ss,adr, val) \
+ _write16(ss, I2C_ADR_E0(adr), cpu_to_le16(val))
+#define RD16(ss,adr) \
+ _read16(ss, I2C_ADR_C0(adr))
+
+#define EXIT_RC( cmd ) if ( (rc = (cmd)) < 0) goto exit_rc
+
+/*******************************************************************************
+ * Tuner callback
+ ******************************************************************************/
+
+static int PLL_Set(struct drx397xD_state *s,
+ struct dvb_frontend_parameters *fep, int *df_tuner)
+{
+ struct dvb_frontend *fe = &s->frontend;
+ u32 f_tuner, f = fep->frequency;
+ int rc;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ if ((f > s->frontend.ops.tuner_ops.info.frequency_max) ||
+ (f < s->frontend.ops.tuner_ops.info.frequency_min))
+ return -EINVAL;
+
+ *df_tuner = 0;
+ if (!s->frontend.ops.tuner_ops.set_params ||
+ !s->frontend.ops.tuner_ops.get_frequency)
+ return -ENOSYS;
+
+ rc = s->frontend.ops.tuner_ops.set_params(fe, fep);
+ if (rc < 0)
+ return rc;
+
+ rc = s->frontend.ops.tuner_ops.get_frequency(fe, &f_tuner);
+ if (rc < 0)
+ return rc;
+
+ *df_tuner = f_tuner - f;
+ pr_debug("%s requested %d [Hz] tuner %d [Hz]\n", __FUNCTION__, f,
+ f_tuner);
+
+ return 0;
+}
+
+/*******************************************************************************
+ * Demodulator helper functions
+ ******************************************************************************/
+
+static int SC_WaitForReady(struct drx397xD_state *s)
+{
+ int cnt = 1000;
+ int rc;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ while (cnt--) {
+ rc = RD16(s, 0x820043);
+ if (rc == 0)
+ return 0;
+ }
+ return -1;
+}
+
+static int SC_SendCommand(struct drx397xD_state *s, int cmd)
+{
+ int rc;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ WR16(s, 0x820043, cmd);
+ SC_WaitForReady(s);
+ rc = RD16(s, 0x820042);
+ if ((rc & 0xffff) == 0xffff)
+ return -1;
+ return 0;
+}
+
+static int HI_Command(struct drx397xD_state *s, u16 cmd)
+{
+ int rc, cnt = 1000;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ rc = WR16(s, 0x420032, cmd);
+ if (rc < 0)
+ return rc;
+
+ do {
+ rc = RD16(s, 0x420032);
+ if (rc == 0) {
+ rc = RD16(s, 0x420031);
+ return rc;
+ }
+ if (rc < 0)
+ return rc;
+ } while (--cnt);
+ return rc;
+}
+
+static int HI_CfgCommand(struct drx397xD_state *s)
+{
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ WR16(s, 0x420033, 0x3973);
+ WR16(s, 0x420034, s->config.w50); // code 4, log 4
+ WR16(s, 0x420035, s->config.w52); // code 15, log 9
+ WR16(s, 0x420036, s->config.demod_address << 1);
+ WR16(s, 0x420037, s->config.w56); // code (set_i2c ?? initX 1 ), log 1
+// WR16(s, 0x420033, 0x3973);
+ if ((s->config.w56 & 8) == 0)
+ return HI_Command(s, 3);
+ return WR16(s, 0x420032, 0x3);
+}
+
+static const u8 fastIncrDecLUT_15273[] = {
+ 0x0e, 0x0f, 0x0f, 0x10, 0x11, 0x12, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1d, 0x1f
+};
+
+static const u8 slowIncrDecLUT_15272[] = {
+ 3, 4, 4, 5, 6
+};
+
+static int SetCfgIfAgc(struct drx397xD_state *s, struct drx397xD_CfgIfAgc *agc)
+{
+ u16 w06 = agc->w06;
+ u16 w08 = agc->w08;
+ u16 w0A = agc->w0A;
+ u16 w0C = agc->w0C;
+ int quot, rem, i, rc = -EINVAL;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ if (agc->w04 > 0x3ff)
+ goto exit_rc;
+
+ if (agc->d00 == 1) {
+ EXIT_RC(RD16(s, 0x0c20010));
+ rc &= ~0x10;
+ EXIT_RC(WR16(s, 0x0c20010, rc));
+ return WR16(s, 0x0c20030, agc->w04 & 0x7ff);
+ }
+
+ if (agc->d00 != 0)
+ goto exit_rc;
+ if (w0A < w08)
+ goto exit_rc;
+ if (w0A > 0x3ff)
+ goto exit_rc;
+ if (w0C > 0x3ff)
+ goto exit_rc;
+ if (w06 > 0x3ff)
+ goto exit_rc;
+
+ EXIT_RC(RD16(s, 0x0c20010));
+ rc |= 0x10;
+ EXIT_RC(WR16(s, 0x0c20010, rc));
+
+ EXIT_RC(WR16(s, 0x0c20025, (w06 >> 1) & 0x1ff));
+ EXIT_RC(WR16(s, 0x0c20031, (w0A - w08) >> 1));
+ EXIT_RC(WR16(s, 0x0c20032, ((w0A + w08) >> 1) - 0x1ff));
+
+ quot = w0C / 113;
+ rem = w0C % 113;
+ if (quot <= 8) {
+ quot = 8 - quot;
+ } else {
+ quot = 0;
+ rem += 113;
+ }
+
+ EXIT_RC(WR16(s, 0x0c20024, quot));
+
+ i = fastIncrDecLUT_15273[rem / 8];
+ EXIT_RC(WR16(s, 0x0c2002d, i));
+ EXIT_RC(WR16(s, 0x0c2002e, i));
+
+ i = slowIncrDecLUT_15272[rem / 28];
+ EXIT_RC(WR16(s, 0x0c2002b, i));
+ rc = WR16(s, 0x0c2002c, i);
+ exit_rc:
+ return rc;
+}
+
+static int SetCfgRfAgc(struct drx397xD_state *s, struct drx397xD_CfgRfAgc *agc)
+{
+ u16 w04 = agc->w04;
+ u16 w06 = agc->w06;
+ int rc = -1;
+
+ pr_debug("%s %d 0x%x 0x%x\n", __FUNCTION__, agc->d00, w04, w06);
+
+ if (w04 > 0x3ff)
+ goto exit_rc;
+
+ switch (agc->d00) {
+ case 1:
+ if (w04 == 0x3ff)
+ w04 = 0x400;
+
+ EXIT_RC(WR16(s, 0x0c20036, w04));
+ s->config.w9C &= ~2;
+ EXIT_RC(WR16(s, 0x0c20015, s->config.w9C));
+ EXIT_RC(RD16(s, 0x0c20010));
+ rc &= 0xbfdf;
+ EXIT_RC(WR16(s, 0x0c20010, rc));
+ EXIT_RC(RD16(s, 0x0c20013));
+ rc &= ~2;
+ break;
+ case 0:
+ // loc_8000659
+ s->config.w9C &= ~2;
+ EXIT_RC(WR16(s, 0x0c20015, s->config.w9C));
+ EXIT_RC(RD16(s, 0x0c20010));
+ rc &= 0xbfdf;
+ rc |= 0x4000;
+ EXIT_RC(WR16(s, 0x0c20010, rc));
+ EXIT_RC(WR16(s, 0x0c20051, (w06 >> 4) & 0x3f));
+ EXIT_RC(RD16(s, 0x0c20013));
+ rc &= ~2;
+ break;
+ default:
+ s->config.w9C |= 2;
+ EXIT_RC(WR16(s, 0x0c20015, s->config.w9C));
+ EXIT_RC(RD16(s, 0x0c20010));
+ rc &= 0xbfdf;
+ EXIT_RC(WR16(s, 0x0c20010, rc));
+
+ EXIT_RC(WR16(s, 0x0c20036, 0));
+
+ EXIT_RC(RD16(s, 0x0c20013));
+ rc |= 2;
+ }
+ rc = WR16(s, 0x0c20013, rc);
+ exit_rc:
+ return rc;
+}
+
+static int GetLockStatus(struct drx397xD_state *s, int *lockstat)
+{
+ int rc;
+
+ *lockstat = 0;
+
+ rc = RD16(s, 0x082004b);
+ if (rc < 0)
+ return rc;
+
+ if (s->config.d60 != 2)
+ return 0;
+
+ if ((rc & 7) == 7)
+ *lockstat |= 1;
+ if ((rc & 3) == 3)
+ *lockstat |= 2;
+ if (rc & 1)
+ *lockstat |= 4;
+ return 0;
+}
+
+static int CorrectSysClockDeviation(struct drx397xD_state *s)
+{
+ int rc = -EINVAL;
+ int lockstat;
+ u32 clk, clk_limit;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ if (s->config.d5C == 0) {
+ EXIT_RC(WR16(s, 0x08200e8, 0x010));
+ EXIT_RC(WR16(s, 0x08200e9, 0x113));
+ s->config.d5C = 1;
+ return rc;
+ }
+ if (s->config.d5C != 1)
+ goto exit_rc;
+
+ rc = RD16(s, 0x0820048);
+
+ rc = GetLockStatus(s, &lockstat);
+ if (rc < 0)
+ goto exit_rc;
+ if ((lockstat & 1) == 0)
+ goto exit_rc;
+
+ EXIT_RC(WR16(s, 0x0420033, 0x200));
+ EXIT_RC(WR16(s, 0x0420034, 0xc5));
+ EXIT_RC(WR16(s, 0x0420035, 0x10));
+ EXIT_RC(WR16(s, 0x0420036, 0x1));
+ EXIT_RC(WR16(s, 0x0420037, 0xa));
+ EXIT_RC(HI_Command(s, 6));
+ EXIT_RC(RD16(s, 0x0420040));
+ clk = rc;
+ EXIT_RC(RD16(s, 0x0420041));
+ clk |= rc << 16;
+
+ if (clk <= 0x26ffff)
+ goto exit_rc;
+ if (clk > 0x610000)
+ goto exit_rc;
+
+ if (!s->bandwidth_parm)
+ return -EINVAL;
+
+ /* round & convert to Hz */
+ clk = ((u64) (clk + 0x800000) * s->bandwidth_parm + (1 << 20)) >> 21;
+ clk_limit = s->config.f_osc * MAX_CLOCK_DRIFT / 1000;
+
+ if (clk - s->config.f_osc * 1000 + clk_limit <= 2 * clk_limit) {
+ s->f_osc = clk;
+ pr_debug("%s: osc %d %d [Hz]\n", __FUNCTION__,
+ s->config.f_osc * 1000, clk - s->config.f_osc * 1000);
+ }
+ rc = WR16(s, 0x08200e8, 0);
+ exit_rc:
+ return rc;
+}
+
+static int ConfigureMPEGOutput(struct drx397xD_state *s, int type)
+{
+ int rc, si, bp;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ si = s->config.wA0;
+ if (s->config.w98 == 0) {
+ si |= 1;
+ bp = 0;
+ } else {
+ si &= ~1;
+ bp = 0x200;
+ }
+ if (s->config.w9A == 0) {
+ si |= 0x80;
+ } else {
+ si &= ~0x80;
+ }
+
+ EXIT_RC(WR16(s, 0x2150045, 0));
+ EXIT_RC(WR16(s, 0x2150010, si));
+ EXIT_RC(WR16(s, 0x2150011, bp));
+ rc = WR16(s, 0x2150012, (type == 0 ? 0xfff : 0));
+ exit_rc:
+ return rc;
+}
+
+static int drx_tune(struct drx397xD_state *s,
+ struct dvb_frontend_parameters *fep)
+{
+ u16 v22 = 0;
+ u16 v1C = 0;
+ u16 v1A = 0;
+ u16 v18 = 0;
+ u32 edi = 0, ebx = 0, ebp = 0, edx = 0;
+ u16 v20 = 0, v1E = 0, v16 = 0, v14 = 0, v12 = 0, v10 = 0, v0E = 0;
+
+ int rc, df_tuner;
+ int a, b, c, d;
+ pr_debug("%s %d\n", __FUNCTION__, s->config.d60);
+
+ if (s->config.d60 != 2)
+ goto set_tuner;
+ rc = CorrectSysClockDeviation(s);
+ if (rc < 0)
+ goto set_tuner;
+
+ s->config.d60 = 1;
+ rc = ConfigureMPEGOutput(s, 0);
+ if (rc < 0)
+ goto set_tuner;
+ set_tuner:
+
+ rc = PLL_Set(s, fep, &df_tuner);
+ if (rc < 0) {
+ printk(KERN_ERR "Error in pll_set\n");
+ goto exit_rc;
+ }
+ msleep(200);
+
+ a = rc = RD16(s, 0x2150016);
+ if (rc < 0)
+ goto exit_rc;
+ b = rc = RD16(s, 0x2150010);
+ if (rc < 0)
+ goto exit_rc;
+ c = rc = RD16(s, 0x2150034);
+ if (rc < 0)
+ goto exit_rc;
+ d = rc = RD16(s, 0x2150035);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x2150014, c);
+ rc = WR16(s, 0x2150015, d);
+ rc = WR16(s, 0x2150010, 0);
+ rc = WR16(s, 0x2150000, 2);
+ rc = WR16(s, 0x2150036, 0x0fff);
+ rc = WR16(s, 0x2150016, a);
+
+ rc = WR16(s, 0x2150010, 2);
+ rc = WR16(s, 0x2150007, 0);
+ rc = WR16(s, 0x2150000, 1);
+ rc = WR16(s, 0x2110000, 0);
+ rc = WR16(s, 0x0800000, 0);
+ rc = WR16(s, 0x2800000, 0);
+ rc = WR16(s, 0x2110010, 0x664);
+
+ rc = write_fw(s, DRXD_ResetECRAM);
+ rc = WR16(s, 0x2110000, 1);
+
+ rc = write_fw(s, DRXD_InitSC);
+ if (rc < 0)
+ goto exit_rc;
+
+ rc = SetCfgIfAgc(s, &s->config.ifagc);
+ if (rc < 0)
+ goto exit_rc;
+
+ rc = SetCfgRfAgc(s, &s->config.rfagc);
+ if (rc < 0)
+ goto exit_rc;
+
+ if (fep->u.ofdm.transmission_mode != TRANSMISSION_MODE_2K)
+ v22 = 1;
+ switch (fep->u.ofdm.transmission_mode) {
+ case TRANSMISSION_MODE_8K:
+ edi = 1;
+ if (s->chip_rev == DRXD_FW_B1)
+ break;
+
+ rc = WR16(s, 0x2010010, 0);
+ if (rc < 0)
+ break;
+ v1C = 0x63;
+ v1A = 0x53;
+ v18 = 0x43;
+ break;
+ default:
+ edi = 0;
+ if (s->chip_rev == DRXD_FW_B1)
+ break;
+
+ rc = WR16(s, 0x2010010, 1);
+ if (rc < 0)
+ break;
+
+ v1C = 0x61;
+ v1A = 0x47;
+ v18 = 0x41;
+ }
+
+ switch (fep->u.ofdm.guard_interval) {
+ case GUARD_INTERVAL_1_4:
+ edi |= 0x0c;
+ break;
+ case GUARD_INTERVAL_1_8:
+ edi |= 0x08;
+ break;
+ case GUARD_INTERVAL_1_16:
+ edi |= 0x04;
+ break;
+ case GUARD_INTERVAL_1_32:
+ break;
+ default:
+ v22 |= 2;
+ }
+
+ ebx = 0;
+ ebp = 0;
+ v20 = 0;
+ v1E = 0;
+ v16 = 0;
+ v14 = 0;
+ v12 = 0;
+ v10 = 0;
+ v0E = 0;
+
+ switch (fep->u.ofdm.hierarchy_information) {
+ case HIERARCHY_1:
+ edi |= 0x40;
+ if (s->chip_rev == DRXD_FW_B1)
+ break;
+ rc = WR16(s, 0x1c10047, 1);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x2010012, 1);
+ if (rc < 0)
+ goto exit_rc;
+ ebx = 0x19f;
+ ebp = 0x1fb;
+ v20 = 0x0c0;
+ v1E = 0x195;
+ v16 = 0x1d6;
+ v14 = 0x1ef;
+ v12 = 4;
+ v10 = 5;
+ v0E = 5;
+ break;
+ case HIERARCHY_2:
+ edi |= 0x80;
+ if (s->chip_rev == DRXD_FW_B1)
+ break;
+ rc = WR16(s, 0x1c10047, 2);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x2010012, 2);
+ if (rc < 0)
+ goto exit_rc;
+ ebx = 0x08f;
+ ebp = 0x12f;
+ v20 = 0x0c0;
+ v1E = 0x11e;
+ v16 = 0x1d6;
+ v14 = 0x15e;
+ v12 = 4;
+ v10 = 5;
+ v0E = 5;
+ break;
+ case HIERARCHY_4:
+ edi |= 0xc0;
+ if (s->chip_rev == DRXD_FW_B1)
+ break;
+ rc = WR16(s, 0x1c10047, 3);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x2010012, 3);
+ if (rc < 0)
+ goto exit_rc;
+ ebx = 0x14d;
+ ebp = 0x197;
+ v20 = 0x0c0;
+ v1E = 0x1ce;
+ v16 = 0x1d6;
+ v14 = 0x11a;
+ v12 = 4;
+ v10 = 6;
+ v0E = 5;
+ break;
+ default:
+ v22 |= 8;
+ if (s->chip_rev == DRXD_FW_B1)
+ break;
+ rc = WR16(s, 0x1c10047, 0);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x2010012, 0);
+ if (rc < 0)
+ goto exit_rc;
+ // QPSK QAM16 QAM64
+ ebx = 0x19f; // 62
+ ebp = 0x1fb; // 15
+ v20 = 0x16a; // 62
+ v1E = 0x195; // 62
+ v16 = 0x1bb; // 15
+ v14 = 0x1ef; // 15
+ v12 = 5; // 16
+ v10 = 5; // 16
+ v0E = 5; // 16
+ }
+
+ switch (fep->u.ofdm.constellation) {
+ default:
+ v22 |= 4;
+ case QPSK:
+ if (s->chip_rev == DRXD_FW_B1)
+ break;
+
+ rc = WR16(s, 0x1c10046, 0);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x2010011, 0);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x201001a, 0x10);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x201001b, 0);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x201001c, 0);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x1c10062, v20);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x1c1002a, v1C);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x1c10015, v16);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x1c10016, v12);
+ if (rc < 0)
+ goto exit_rc;
+ break;
+ case QAM_16:
+ edi |= 0x10;
+ if (s->chip_rev == DRXD_FW_B1)
+ break;
+
+ rc = WR16(s, 0x1c10046, 1);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x2010011, 1);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x201001a, 0x10);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x201001b, 4);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x201001c, 0);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x1c10062, v1E);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x1c1002a, v1A);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x1c10015, v14);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x1c10016, v10);
+ if (rc < 0)
+ goto exit_rc;
+ break;
+ case QAM_64:
+ edi |= 0x20;
+ rc = WR16(s, 0x1c10046, 2);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x2010011, 2);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x201001a, 0x20);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x201001b, 8);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x201001c, 2);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x1c10062, ebx);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x1c1002a, v18);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x1c10015, ebp);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x1c10016, v0E);
+ if (rc < 0)
+ goto exit_rc;
+ break;
+ }
+
+ if (s->config.s20d24 == 1) {
+ rc = WR16(s, 0x2010013, 0);
+ } else {
+ rc = WR16(s, 0x2010013, 1);
+ edi |= 0x1000;
+ }
+
+ switch (fep->u.ofdm.code_rate_HP) {
+ default:
+ v22 |= 0x10;
+ case FEC_1_2:
+ if (s->chip_rev == DRXD_FW_B1)
+ break;
+ rc = WR16(s, 0x2090011, 0);
+ break;
+ case FEC_2_3:
+ edi |= 0x200;
+ if (s->chip_rev == DRXD_FW_B1)
+ break;
+ rc = WR16(s, 0x2090011, 1);
+ break;
+ case FEC_3_4:
+ edi |= 0x400;
+ if (s->chip_rev == DRXD_FW_B1)
+ break;
+ rc = WR16(s, 0x2090011, 2);
+ break;
+ case FEC_5_6: /* 5 */
+ edi |= 0x600;
+ if (s->chip_rev == DRXD_FW_B1)
+ break;
+ rc = WR16(s, 0x2090011, 3);
+ break;
+ case FEC_7_8: /* 7 */
+ edi |= 0x800;
+ if (s->chip_rev == DRXD_FW_B1)
+ break;
+ rc = WR16(s, 0x2090011, 4);
+ break;
+ };
+ if (rc < 0)
+ goto exit_rc;
+
+ switch (fep->u.ofdm.bandwidth) {
+ default:
+ rc = -EINVAL;
+ goto exit_rc;
+ case BANDWIDTH_8_MHZ: /* 0 */
+ case BANDWIDTH_AUTO:
+ rc = WR16(s, 0x0c2003f, 0x32);
+ s->bandwidth_parm = ebx = 0x8b8249; // 9142857
+ edx = 0;
+ break;
+ case BANDWIDTH_7_MHZ:
+ rc = WR16(s, 0x0c2003f, 0x3b);
+ s->bandwidth_parm = ebx = 0x7a1200; // 8000000
+ edx = 0x4807;
+ break;
+ case BANDWIDTH_6_MHZ:
+ rc = WR16(s, 0x0c2003f, 0x47);
+ s->bandwidth_parm = ebx = 0x68a1b6; // 6857142
+ edx = 0x0f07;
+ break;
+ };
+
+ if (rc < 0)
+ goto exit_rc;
+
+ rc = WR16(s, 0x08200ec, edx);
+ if (rc < 0)
+ goto exit_rc;
+
+ rc = RD16(s, 0x0820050);
+ if (rc < 0)
+ goto exit_rc;
+ rc = WR16(s, 0x0820050, rc);
+
+ {
+ /* Configure bandwidth specific factor */
+ ebx = div64_u64(((u64) (s->f_osc) << 21) + (ebx >> 1),
+ (u64)ebx) - 0x800000;
+ EXIT_RC(WR16(s, 0x0c50010, ebx & 0xffff));
+ EXIT_RC(WR16(s, 0x0c50011, ebx >> 16));
+
+ /* drx397xD oscillator calibration */
+ ebx = div64_u64(((u64) (s->config.f_if + df_tuner) << 28) +
+ (s->f_osc >> 1), (u64)s->f_osc);
+ }
+ ebx &= 0xfffffff;
+ if (fep->inversion == INVERSION_ON)
+ ebx = 0x10000000 - ebx;
+
+ EXIT_RC(WR16(s, 0x0c30010, ebx & 0xffff));
+ EXIT_RC(WR16(s, 0x0c30011, ebx >> 16));
+
+ EXIT_RC(WR16(s, 0x0800000, 1));
+ EXIT_RC(RD16(s, 0x0800000));
+
+
+ EXIT_RC(SC_WaitForReady(s));
+ EXIT_RC(WR16(s, 0x0820042, 0));
+ EXIT_RC(WR16(s, 0x0820041, v22));
+ EXIT_RC(WR16(s, 0x0820040, edi));
+ EXIT_RC(SC_SendCommand(s, 3));
+
+ rc = RD16(s, 0x0800000);
+
+ SC_WaitForReady(s);
+ WR16(s, 0x0820042, 0);
+ WR16(s, 0x0820041, 1);
+ WR16(s, 0x0820040, 1);
+ SC_SendCommand(s, 1);
+
+// rc = WR16(s, 0x2150000, 1);
+// if (rc < 0) goto exit_rc;
+
+ rc = WR16(s, 0x2150000, 2);
+ rc = WR16(s, 0x2150016, a);
+ rc = WR16(s, 0x2150010, 4);
+ rc = WR16(s, 0x2150036, 0);
+ rc = WR16(s, 0x2150000, 1);
+ s->config.d60 = 2;
+ exit_rc:
+ return rc;
+}
+
+/*******************************************************************************
+ * DVB interface
+ ******************************************************************************/
+
+static int drx397x_init(struct dvb_frontend *fe)
+{
+ struct drx397xD_state *s = fe->demodulator_priv;
+ int rc;
+
+ pr_debug("%s\n", __FUNCTION__);
+
+ s->config.rfagc.d00 = 2; /* 0x7c */
+ s->config.rfagc.w04 = 0;
+ s->config.rfagc.w06 = 0x3ff;
+
+ s->config.ifagc.d00 = 0; /* 0x68 */
+ s->config.ifagc.w04 = 0;
+ s->config.ifagc.w06 = 140;
+ s->config.ifagc.w08 = 0;
+ s->config.ifagc.w0A = 0x3ff;
+ s->config.ifagc.w0C = 0x388;
+
+ /* for signal strenght calculations */
+ s->config.ss76 = 820;
+ s->config.ss78 = 2200;
+ s->config.ss7A = 150;
+
+ /* HI_CfgCommand */
+ s->config.w50 = 4;
+ s->config.w52 = 9; // 0xf;
+
+ s->config.f_if = 42800000; /* d14: intermediate frequency [Hz] */
+ s->config.f_osc = 48000; /* s66 : oscillator frequency [kHz] */
+ s->config.w92 = 12000; // 20000;
+
+ s->config.w9C = 0x000e;
+ s->config.w9E = 0x0000;
+
+ /* ConfigureMPEGOutput params */
+ s->config.wA0 = 4;
+ s->config.w98 = 1; // 0;
+ s->config.w9A = 1;
+
+ /* get chip revision */
+ rc = RD16(s, 0x2410019);
+ if (rc < 0)
+ return -ENODEV;
+
+ if (rc == 0) {
+ printk(KERN_INFO "%s: chip revision A2\n", mod_name);
+ rc = drx_load_fw(s, DRXD_FW_A2);
+ } else {
+
+ rc = (rc >> 12) - 3;
+ switch (rc) {
+ case 1:
+ s->flags |= F_SET_0D4h;
+ case 0:
+ case 4:
+ s->flags |= F_SET_0D0h;
+ break;
+ case 2:
+ case 5:
+ break;
+ case 3:
+ s->flags |= F_SET_0D4h;
+ break;
+ default:
+ return -ENODEV;
+ };
+ printk(KERN_INFO "%s: chip revision B1.%d\n", mod_name, rc);
+ rc = drx_load_fw(s, DRXD_FW_B1);
+ }
+ if (rc < 0)
+ goto error;
+
+ rc = WR16(s, 0x0420033, 0x3973);
+ if (rc < 0)
+ goto error;
+
+ rc = HI_Command(s, 2);
+
+ msleep(1);
+
+ if (s->chip_rev == DRXD_FW_A2) {
+ rc = WR16(s, 0x043012d, 0x47F);
+ if (rc < 0)
+ goto error;
+ }
+ rc = WR16_E0(s, 0x0400000, 0);
+ if (rc < 0)
+ goto error;
+
+ if (s->config.w92 > 20000 || s->config.w92 % 4000) {
+ printk(KERN_ERR "%s: invalid osc frequency\n", mod_name);
+ rc = -1;
+ goto error;
+ }
+
+ rc = WR16(s, 0x2410010, 1);
+ if (rc < 0)
+ goto error;
+ rc = WR16(s, 0x2410011, 0x15);
+ if (rc < 0)
+ goto error;
+ rc = WR16(s, 0x2410012, s->config.w92 / 4000);
+ if (rc < 0)
+ goto error;
+#ifdef ORIG_FW
+ rc = WR16(s, 0x2410015, 2);
+ if (rc < 0)
+ goto error;
+#endif
+ rc = WR16(s, 0x2410017, 0x3973);
+ if (rc < 0)
+ goto error;
+
+ s->f_osc = s->config.f_osc * 1000; /* initial estimator */
+
+ s->config.w56 = 1;
+
+ rc = HI_CfgCommand(s);
+ if (rc < 0)
+ goto error;
+
+ rc = write_fw(s, DRXD_InitAtomicRead);
+ if (rc < 0)
+ goto error;
+
+ if (s->chip_rev == DRXD_FW_A2) {
+ rc = WR16(s, 0x2150013, 0);
+ if (rc < 0)
+ goto error;
+ }
+
+ rc = WR16_E0(s, 0x0400002, 0);
+ if (rc < 0)
+ goto error;
+ rc = WR16(s, 0x0400002, 0);
+ if (rc < 0)
+ goto error;
+
+ if (s->chip_rev == DRXD_FW_A2) {
+ rc = write_fw(s, DRXD_ResetCEFR);
+ if (rc < 0)
+ goto error;
+ }
+ rc = write_fw(s, DRXD_microcode);
+ if (rc < 0)
+ goto error;
+
+ s->config.w9C = 0x0e;
+ if (s->flags & F_SET_0D0h) {
+ s->config.w9C = 0;
+ rc = RD16(s, 0x0c20010);
+ if (rc < 0)
+ goto write_DRXD_InitFE_1;
+
+ rc &= ~0x1000;
+ rc = WR16(s, 0x0c20010, rc);
+ if (rc < 0)
+ goto write_DRXD_InitFE_1;
+
+ rc = RD16(s, 0x0c20011);
+ if (rc < 0)
+ goto write_DRXD_InitFE_1;
+
+ rc &= ~0x8;
+ rc = WR16(s, 0x0c20011, rc);
+ if (rc < 0)
+ goto write_DRXD_InitFE_1;
+
+ rc = WR16(s, 0x0c20012, 1);
+ }
+
+ write_DRXD_InitFE_1:
+
+ rc = write_fw(s, DRXD_InitFE_1);
+ if (rc < 0)
+ goto error;
+
+ rc = 1;
+ if (s->chip_rev == DRXD_FW_B1) {
+ if (s->flags & F_SET_0D0h)
+ rc = 0;
+ } else {
+ if (s->flags & F_SET_0D0h)
+ rc = 4;
+ }
+
+ rc = WR16(s, 0x0C20012, rc);
+ if (rc < 0)
+ goto error;
+
+ rc = WR16(s, 0x0C20013, s->config.w9E);
+ if (rc < 0)
+ goto error;
+ rc = WR16(s, 0x0C20015, s->config.w9C);
+ if (rc < 0)
+ goto error;
+
+ rc = write_fw(s, DRXD_InitFE_2);
+ if (rc < 0)
+ goto error;
+ rc = write_fw(s, DRXD_InitFT);
+ if (rc < 0)
+ goto error;
+ rc = write_fw(s, DRXD_InitCP);
+ if (rc < 0)
+ goto error;
+ rc = write_fw(s, DRXD_InitCE);
+ if (rc < 0)
+ goto error;
+ rc = write_fw(s, DRXD_InitEQ);
+ if (rc < 0)
+ goto error;
+ rc = write_fw(s, DRXD_InitEC);
+ if (rc < 0)
+ goto error;
+ rc = write_fw(s, DRXD_InitSC);
+ if (rc < 0)
+ goto error;
+
+ rc = SetCfgIfAgc(s, &s->config.ifagc);
+ if (rc < 0)
+ goto error;
+
+ rc = SetCfgRfAgc(s, &s->config.rfagc);
+ if (rc < 0)
+ goto error;
+
+ rc = ConfigureMPEGOutput(s, 1);
+ rc = WR16(s, 0x08201fe, 0x0017);
+ rc = WR16(s, 0x08201ff, 0x0101);
+
+ s->config.d5C = 0;
+ s->config.d60 = 1;
+ s->config.d48 = 1;
+ error:
+ return rc;
+}
+
+static int drx397x_get_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ return 0;
+}
+
+static int drx397x_set_frontend(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct drx397xD_state *s = fe->demodulator_priv;
+
+ s->config.s20d24 = 1; // 0;
+ return drx_tune(s, params);
+}
+
+static int drx397x_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings
+ *fe_tune_settings)
+{
+ fe_tune_settings->min_delay_ms = 10000;
+ fe_tune_settings->step_size = 0;
+ fe_tune_settings->max_drift = 0;
+ return 0;
+}
+
+static int drx397x_read_status(struct dvb_frontend *fe, fe_status_t * status)
+{
+ struct drx397xD_state *s = fe->demodulator_priv;
+ int lockstat;
+
+ GetLockStatus(s, &lockstat);
+ /* TODO */
+// if (lockstat & 1)
+// CorrectSysClockDeviation(s);
+
+ *status = 0;
+ if (lockstat & 2) {
+ CorrectSysClockDeviation(s);
+ ConfigureMPEGOutput(s, 1);
+ *status = FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI;
+ }
+ if (lockstat & 4) {
+ *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
+ }
+
+ return 0;
+}
+
+static int drx397x_read_ber(struct dvb_frontend *fe, unsigned int *ber)
+{
+ *ber = 0;
+ return 0;
+}
+
+static int drx397x_read_snr(struct dvb_frontend *fe, u16 * snr)
+{
+ *snr = 0;
+ return 0;
+}
+
+static int drx397x_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
+{
+ struct drx397xD_state *s = fe->demodulator_priv;
+ int rc;
+
+ if (s->config.ifagc.d00 == 2) {
+ *strength = 0xffff;
+ return 0;
+ }
+ rc = RD16(s, 0x0c20035);
+ if (rc < 0) {
+ *strength = 0;
+ return 0;
+ }
+ rc &= 0x3ff;
+ /* Signal strength is calculated using the following formula:
+ *
+ * a = 2200 * 150 / (2200 + 150);
+ * a = a * 3300 / (a + 820);
+ * b = 2200 * 3300 / (2200 + 820);
+ * c = (((b-a) * rc) >> 10 + a) << 4;
+ * strength = ~c & 0xffff;
+ *
+ * The following does the same but with less rounding errors:
+ */
+ *strength = ~(7720 + (rc * 30744 >> 10));
+ return 0;
+}
+
+static int drx397x_read_ucblocks(struct dvb_frontend *fe,
+ unsigned int *ucblocks)
+{
+ *ucblocks = 0;
+ return 0;
+}
+
+static int drx397x_sleep(struct dvb_frontend *fe)
+{
+ return 0;
+}
+
+static void drx397x_release(struct dvb_frontend *fe)
+{
+ struct drx397xD_state *s = fe->demodulator_priv;
+ printk(KERN_INFO "%s: release demodulator\n", mod_name);
+ if (s) {
+ drx_release_fw(s);
+ kfree(s);
+ }
+
+}
+
+static struct dvb_frontend_ops drx397x_ops = {
+
+ .info = {
+ .name = "Micronas DRX397xD DVB-T Frontend",
+ .type = FE_OFDM,
+ .frequency_min = 47125000,
+ .frequency_max = 855250000,
+ .frequency_stepsize = 166667,
+ .frequency_tolerance = 0,
+ .caps = /* 0x0C01B2EAE */
+ FE_CAN_FEC_1_2 | // = 0x2,
+ FE_CAN_FEC_2_3 | // = 0x4,
+ FE_CAN_FEC_3_4 | // = 0x8,
+ FE_CAN_FEC_5_6 | // = 0x20,
+ FE_CAN_FEC_7_8 | // = 0x80,
+ FE_CAN_FEC_AUTO | // = 0x200,
+ FE_CAN_QPSK | // = 0x400,
+ FE_CAN_QAM_16 | // = 0x800,
+ FE_CAN_QAM_64 | // = 0x2000,
+ FE_CAN_QAM_AUTO | // = 0x10000,
+ FE_CAN_TRANSMISSION_MODE_AUTO | // = 0x20000,
+ FE_CAN_GUARD_INTERVAL_AUTO | // = 0x80000,
+ FE_CAN_HIERARCHY_AUTO | // = 0x100000,
+ FE_CAN_RECOVER | // = 0x40000000,
+ FE_CAN_MUTE_TS // = 0x80000000
+ },
+
+ .release = drx397x_release,
+ .init = drx397x_init,
+ .sleep = drx397x_sleep,
+
+ .set_frontend = drx397x_set_frontend,
+ .get_tune_settings = drx397x_get_tune_settings,
+ .get_frontend = drx397x_get_frontend,
+
+ .read_status = drx397x_read_status,
+ .read_snr = drx397x_read_snr,
+ .read_signal_strength = drx397x_read_signal_strength,
+ .read_ber = drx397x_read_ber,
+ .read_ucblocks = drx397x_read_ucblocks,
+};
+
+struct dvb_frontend *drx397xD_attach(const struct drx397xD_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct drx397xD_state *s = NULL;
+
+ /* allocate memory for the internal state */
+ s = kzalloc(sizeof(struct drx397xD_state), GFP_KERNEL);
+ if (s == NULL)
+ goto error;
+
+ /* setup the state */
+ s->i2c = i2c;
+ memcpy(&s->config, config, sizeof(struct drx397xD_config));
+
+ /* check if the demod is there */
+ if (RD16(s, 0x2410019) < 0)
+ goto error;
+
+ /* create dvb_frontend */
+ memcpy(&s->frontend.ops, &drx397x_ops, sizeof(struct dvb_frontend_ops));
+ s->frontend.demodulator_priv = s;
+
+ return &s->frontend;
+ error:
+ kfree(s);
+ return NULL;
+}
+
+MODULE_DESCRIPTION("Micronas DRX397xD DVB-T Frontend");
+MODULE_AUTHOR("Henk Vergonet");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(drx397xD_attach);
diff --git a/drivers/media/dvb/frontends/drx397xD.h b/drivers/media/dvb/frontends/drx397xD.h
new file mode 100644
index 000000000000..ddc7a07971b7
--- /dev/null
+++ b/drivers/media/dvb/frontends/drx397xD.h
@@ -0,0 +1,130 @@
+/*
+ * Driver for Micronas DVB-T drx397xD demodulator
+ *
+ * Copyright (C) 2007 Henk vergonet <Henk.Vergonet@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
+ */
+
+#ifndef _DRX397XD_H_INCLUDED
+#define _DRX397XD_H_INCLUDED
+
+#include <linux/dvb/frontend.h>
+
+#define DRX_F_STEPSIZE 166667
+#define DRX_F_OFFSET 36000000
+
+#define I2C_ADR_C0(x) \
+( (u32)cpu_to_le32( \
+ (u32)( \
+ (((u32)(x) & (u32)0x000000ffUL) ) | \
+ (((u32)(x) & (u32)0x0000ff00UL) << 16) | \
+ (((u32)(x) & (u32)0x0fff0000UL) >> 8) | \
+ ( (u32)0x00c00000UL) \
+ )) \
+)
+
+#define I2C_ADR_E0(x) \
+( (u32)cpu_to_le32( \
+ (u32)( \
+ (((u32)(x) & (u32)0x000000ffUL) ) | \
+ (((u32)(x) & (u32)0x0000ff00UL) << 16) | \
+ (((u32)(x) & (u32)0x0fff0000UL) >> 8) | \
+ ( (u32)0x00e00000UL) \
+ )) \
+)
+
+struct drx397xD_CfgRfAgc /* 0x7c */
+{
+ int d00; /* 2 */
+ u16 w04;
+ u16 w06;
+};
+
+struct drx397xD_CfgIfAgc /* 0x68 */
+{
+ int d00; /* 0 */
+ u16 w04; /* 0 */
+ u16 w06;
+ u16 w08;
+ u16 w0A;
+ u16 w0C;
+};
+
+struct drx397xD_s20 {
+ int d04;
+ u32 d18;
+ u32 d1C;
+ u32 d20;
+ u32 d14;
+ u32 d24;
+ u32 d0C;
+ u32 d08;
+};
+
+struct drx397xD_config
+{
+ /* demodulator's I2C address */
+ u8 demod_address; /* 0x0f */
+
+ struct drx397xD_CfgIfAgc ifagc; /* 0x68 */
+ struct drx397xD_CfgRfAgc rfagc; /* 0x7c */
+ u32 s20d24;
+
+ /* HI_CfgCommand parameters */
+ u16 w50, w52, /* w54, */ w56;
+
+ int d5C;
+ int d60;
+ int d48;
+ int d28;
+
+ u32 f_if; /* d14: intermediate frequency [Hz] */
+ /* 36000000 on Cinergy 2400i DT */
+ /* 42800000 on Pinnacle Hybrid PRO 330e */
+
+ u16 f_osc; /* s66: 48000 oscillator frequency [kHz] */
+
+ u16 w92; /* 20000 */
+
+ u16 wA0;
+ u16 w98;
+ u16 w9A;
+
+ u16 w9C; /* 0xe0 */
+ u16 w9E; /* 0x00 */
+
+ /* used for signal strength calculations in
+ drx397x_read_signal_strength
+ */
+ u16 ss78; // 2200
+ u16 ss7A; // 150
+ u16 ss76; // 820
+};
+
+#if defined(CONFIG_DVB_DRX397XD) || (defined(CONFIG_DVB_DRX397XD_MODULE) && defined(MODULE))
+extern struct dvb_frontend* drx397xD_attach(const struct drx397xD_config *config,
+ struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend* drx397xD_attach(const struct drx397xD_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_DRX397XD */
+
+#endif /* _DRX397XD_H_INCLUDED */
diff --git a/drivers/media/dvb/frontends/drx397xD_fw.h b/drivers/media/dvb/frontends/drx397xD_fw.h
new file mode 100644
index 000000000000..01de02a81cd4
--- /dev/null
+++ b/drivers/media/dvb/frontends/drx397xD_fw.h
@@ -0,0 +1,40 @@
+/*
+ * Firmware definitions for Micronas drx397xD
+ *
+ * Copyright (C) 2007 Henk Vergonet <Henk.Vergonet@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef _FW_ENTRY
+ _FW_ENTRY("drx397xD.A2.fw", DRXD_FW_A2 = 0 ),
+ _FW_ENTRY("drx397xD.B1.fw", DRXD_FW_B1 ),
+#undef _FW_ENTRY
+#endif /* _FW_ENTRY */
+
+#ifdef _BLOB_ENTRY
+ _BLOB_ENTRY("InitAtomicRead", DRXD_InitAtomicRead = 0 ),
+ _BLOB_ENTRY("InitCE", DRXD_InitCE ),
+ _BLOB_ENTRY("InitCP", DRXD_InitCP ),
+ _BLOB_ENTRY("InitEC", DRXD_InitEC ),
+ _BLOB_ENTRY("InitEQ", DRXD_InitEQ ),
+ _BLOB_ENTRY("InitFE_1", DRXD_InitFE_1 ),
+ _BLOB_ENTRY("InitFE_2", DRXD_InitFE_2 ),
+ _BLOB_ENTRY("InitFT", DRXD_InitFT ),
+ _BLOB_ENTRY("InitSC", DRXD_InitSC ),
+ _BLOB_ENTRY("ResetCEFR", DRXD_ResetCEFR ),
+ _BLOB_ENTRY("ResetECRAM", DRXD_ResetECRAM ),
+ _BLOB_ENTRY("microcode", DRXD_microcode ),
+#undef _BLOB_ENTRY
+#endif /* _BLOB_ENTRY */
diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c
index a054894ff481..ea058153ebfa 100644
--- a/drivers/media/dvb/frontends/dvb-pll.c
+++ b/drivers/media/dvb/frontends/dvb-pll.c
@@ -343,6 +343,52 @@ static struct dvb_pll_desc dvb_pll_opera1 = {
}
};
+static void samsung_dtos403ih102a_set(struct dvb_frontend *fe, u8 *buf,
+ const struct dvb_frontend_parameters *params)
+{
+ struct dvb_pll_priv *priv = fe->tuner_priv;
+ struct i2c_msg msg = {
+ .addr = priv->pll_i2c_address,
+ .flags = 0,
+ .buf = buf,
+ .len = 4
+ };
+ int result;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+ result = i2c_transfer(priv->i2c, &msg, 1);
+ if (result != 1)
+ printk(KERN_ERR "%s: i2c_transfer failed:%d",
+ __func__, result);
+
+ buf[2] = 0x9e;
+ buf[3] = 0x90;
+
+ return;
+}
+
+/* unknown pll used in Samsung DTOS403IH102A DVB-C tuner */
+static struct dvb_pll_desc dvb_pll_samsung_dtos403ih102a = {
+ .name = "Samsung DTOS403IH102A",
+ .min = 44250000,
+ .max = 858000000,
+ .iffreq = 36125000,
+ .count = 8,
+ .set = samsung_dtos403ih102a_set,
+ .entries = {
+ { 135000000, 62500, 0xbe, 0x01 },
+ { 177000000, 62500, 0xf6, 0x01 },
+ { 370000000, 62500, 0xbe, 0x02 },
+ { 450000000, 62500, 0xf6, 0x02 },
+ { 466000000, 62500, 0xfe, 0x02 },
+ { 538000000, 62500, 0xbe, 0x08 },
+ { 826000000, 62500, 0xf6, 0x08 },
+ { 999999999, 62500, 0xfe, 0x08 },
+ }
+};
+
/* ----------------------------------------------------------- */
static struct dvb_pll_desc *pll_list[] = {
@@ -360,6 +406,7 @@ static struct dvb_pll_desc *pll_list[] = {
[DVB_PLL_SAMSUNG_TBMV] = &dvb_pll_samsung_tbmv,
[DVB_PLL_PHILIPS_SD1878_TDA8261] = &dvb_pll_philips_sd1878_tda8261,
[DVB_PLL_OPERA1] = &dvb_pll_opera1,
+ [DVB_PLL_SAMSUNG_DTOS403IH102A] = &dvb_pll_samsung_dtos403ih102a,
};
/* ----------------------------------------------------------- */
diff --git a/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h
index 872ca29e7cf3..05239f579ccf 100644
--- a/drivers/media/dvb/frontends/dvb-pll.h
+++ b/drivers/media/dvb/frontends/dvb-pll.h
@@ -22,6 +22,7 @@
#define DVB_PLL_SAMSUNG_TBMV 11
#define DVB_PLL_PHILIPS_SD1878_TDA8261 12
#define DVB_PLL_OPERA1 13
+#define DVB_PLL_SAMSUNG_DTOS403IH102A 14
/**
* Attach a dvb-pll to the supplied frontend structure.
diff --git a/drivers/media/dvb/frontends/nxt200x.c b/drivers/media/dvb/frontends/nxt200x.c
index 23d022852543..af298358e822 100644
--- a/drivers/media/dvb/frontends/nxt200x.c
+++ b/drivers/media/dvb/frontends/nxt200x.c
@@ -93,7 +93,8 @@ static u8 i2c_readbytes (struct nxt200x_state* state, u8 addr, u8* buf, u8 len)
return 0;
}
-static int nxt200x_writebytes (struct nxt200x_state* state, u8 reg, u8 *buf, u8 len)
+static int nxt200x_writebytes (struct nxt200x_state* state, u8 reg,
+ const u8 *buf, u8 len)
{
u8 buf2 [len+1];
int err;
diff --git a/drivers/media/dvb/frontends/or51211.c b/drivers/media/dvb/frontends/or51211.c
index 7eaa4765593f..6afe12aaca4e 100644
--- a/drivers/media/dvb/frontends/or51211.c
+++ b/drivers/media/dvb/frontends/or51211.c
@@ -69,7 +69,7 @@ struct or51211_state {
u32 current_frequency;
};
-static int i2c_writebytes (struct or51211_state* state, u8 reg, u8 *buf,
+static int i2c_writebytes (struct or51211_state* state, u8 reg, const u8 *buf,
int len)
{
int err;
@@ -77,7 +77,7 @@ static int i2c_writebytes (struct or51211_state* state, u8 reg, u8 *buf,
msg.addr = reg;
msg.flags = 0;
msg.len = len;
- msg.buf = buf;
+ msg.buf = (u8 *)buf;
if ((err = i2c_transfer (state->i2c, &msg, 1)) != 1) {
printk(KERN_WARNING "or51211: i2c_writebytes error "
diff --git a/drivers/media/dvb/frontends/s5h1409.c b/drivers/media/dvb/frontends/s5h1409.c
index b999ec424ff7..5ddb2dca305c 100644
--- a/drivers/media/dvb/frontends/s5h1409.c
+++ b/drivers/media/dvb/frontends/s5h1409.c
@@ -26,7 +26,6 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include "dvb_frontend.h"
-#include "dvb-pll.h"
#include "s5h1409.h"
struct s5h1409_state {
diff --git a/drivers/media/dvb/frontends/s5h1411.c b/drivers/media/dvb/frontends/s5h1411.c
index eb5bfc99d4e9..cff360ce1ba3 100644
--- a/drivers/media/dvb/frontends/s5h1411.c
+++ b/drivers/media/dvb/frontends/s5h1411.c
@@ -26,7 +26,6 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include "dvb_frontend.h"
-#include "dvb-pll.h"
#include "s5h1411.h"
struct s5h1411_state {
diff --git a/drivers/media/dvb/frontends/sp8870.c b/drivers/media/dvb/frontends/sp8870.c
index aa78aa14aad9..1c9a9b4051b9 100644
--- a/drivers/media/dvb/frontends/sp8870.c
+++ b/drivers/media/dvb/frontends/sp8870.c
@@ -98,7 +98,7 @@ static int sp8870_readreg (struct sp8870_state* state, u16 reg)
static int sp8870_firmware_upload (struct sp8870_state* state, const struct firmware *fw)
{
struct i2c_msg msg;
- char *fw_buf = fw->data;
+ const char *fw_buf = fw->data;
int fw_pos;
u8 tx_buf[255];
int tx_len;
diff --git a/drivers/media/dvb/frontends/sp887x.c b/drivers/media/dvb/frontends/sp887x.c
index 49f55877f513..4543609e1816 100644
--- a/drivers/media/dvb/frontends/sp887x.c
+++ b/drivers/media/dvb/frontends/sp887x.c
@@ -140,7 +140,7 @@ static int sp887x_initial_setup (struct dvb_frontend* fe, const struct firmware
u8 buf [BLOCKSIZE+2];
int i;
int fw_size = fw->size;
- unsigned char *mem = fw->data;
+ const unsigned char *mem = fw->data;
dprintk("%s\n", __func__);
diff --git a/drivers/media/dvb/frontends/tda10023.c b/drivers/media/dvb/frontends/tda10023.c
index 0727b80bc4d2..1c1eb7abc834 100644
--- a/drivers/media/dvb/frontends/tda10023.c
+++ b/drivers/media/dvb/frontends/tda10023.c
@@ -38,75 +38,29 @@
#include "dvb_frontend.h"
#include "tda1002x.h"
+#define REG0_INIT_VAL 0x23
struct tda10023_state {
struct i2c_adapter* i2c;
/* configuration settings */
- const struct tda1002x_config* config;
+ const struct tda10023_config *config;
struct dvb_frontend frontend;
u8 pwm;
u8 reg0;
-};
+ /* clock settings */
+ u32 xtal;
+ u8 pll_m;
+ u8 pll_p;
+ u8 pll_n;
+ u32 sysclk;
+};
#define dprintk(x...)
static int verbose;
-#define XTAL 28920000UL
-#define PLL_M 8UL
-#define PLL_P 4UL
-#define PLL_N 1UL
-#define SYSCLK (XTAL*PLL_M/(PLL_N*PLL_P)) // -> 57840000
-
-static u8 tda10023_inittab[]={
- // reg mask val
- 0x2a,0xff,0x02, // PLL3, Bypass, Power Down
- 0xff,0x64,0x00, // Sleep 100ms
- 0x2a,0xff,0x03, // PLL3, Bypass, Power Down
- 0xff,0x64,0x00, // Sleep 100ms
- 0x28,0xff,PLL_M-1, // PLL1 M=8
- 0x29,0xff,((PLL_P-1)<<6)|(PLL_N-1), // PLL2
- 0x00,0xff,0x23, // GPR FSAMPLING=1
- 0x2a,0xff,0x08, // PLL3 PSACLK=1
- 0xff,0x64,0x00, // Sleep 100ms
- 0x1f,0xff,0x00, // RESET
- 0xff,0x64,0x00, // Sleep 100ms
- 0xe6,0x0c,0x04, // RSCFG_IND
- 0x10,0xc0,0x80, // DECDVBCFG1 PBER=1
-
- 0x0e,0xff,0x82, // GAIN1
- 0x03,0x08,0x08, // CLKCONF DYN=1
- 0x2e,0xbf,0x30, // AGCCONF2 TRIAGC=0,POSAGC=ENAGCIF=1 PPWMTUN=0 PPWMIF=0
- 0x01,0xff,0x30, // AGCREF
- 0x1e,0x84,0x84, // CONTROL SACLK_ON=1
- 0x1b,0xff,0xc8, // ADC TWOS=1
- 0x3b,0xff,0xff, // IFMAX
- 0x3c,0xff,0x00, // IFMIN
- 0x34,0xff,0x00, // PWMREF
- 0x35,0xff,0xff, // TUNMAX
- 0x36,0xff,0x00, // TUNMIN
- 0x06,0xff,0x7f, // EQCONF1 POSI=7 ENADAPT=ENEQUAL=DFE=1 // 0x77
- 0x1c,0x30,0x30, // EQCONF2 STEPALGO=SGNALGO=1
- 0x37,0xff,0xf6, // DELTAF_LSB
- 0x38,0xff,0xff, // DELTAF_MSB
- 0x02,0xff,0x93, // AGCCONF1 IFS=1 KAGCIF=2 KAGCTUN=3
- 0x2d,0xff,0xf6, // SWEEP SWPOS=1 SWDYN=7 SWSTEP=1 SWLEN=2
- 0x04,0x10,0x00, // SWRAMP=1
- 0x12,0xff,0xa1, // INTP1 POCLKP=1 FEL=1 MFS=0
- 0x2b,0x01,0xa1, // INTS1
- 0x20,0xff,0x04, // INTP2 SWAPP=? MSBFIRSTP=? INTPSEL=?
- 0x2c,0xff,0x0d, // INTP/S TRIP=0 TRIS=0
- 0xc4,0xff,0x00,
- 0xc3,0x30,0x00,
- 0xb5,0xff,0x19, // ERAGC_THD
- 0x00,0x03,0x01, // GPR, CLBS soft reset
- 0x00,0x03,0x03, // GPR, CLBS soft reset
- 0xff,0x64,0x00, // Sleep 100ms
- 0xff,0xff,0xff
-};
-
static u8 tda10023_readreg (struct tda10023_state* state, u8 reg)
{
u8 b0 [] = { reg };
@@ -215,30 +169,34 @@ static int tda10023_set_symbolrate (struct tda10023_state* state, u32 sr)
s16 SFIL=0;
u16 NDEC = 0;
- if (sr < (u32)(SYSCLK/98.40)) {
+ /* avoid floating point operations multiplying syscloc and divider
+ by 10 */
+ u32 sysclk_x_10 = state->sysclk * 10;
+
+ if (sr < (u32)(sysclk_x_10/984)) {
NDEC=3;
SFIL=1;
- } else if (sr<(u32)(SYSCLK/64.0)) {
+ } else if (sr < (u32)(sysclk_x_10/640)) {
NDEC=3;
SFIL=0;
- } else if (sr<(u32)(SYSCLK/49.2)) {
+ } else if (sr < (u32)(sysclk_x_10/492)) {
NDEC=2;
SFIL=1;
- } else if (sr<(u32)(SYSCLK/32.0)) {
+ } else if (sr < (u32)(sysclk_x_10/320)) {
NDEC=2;
SFIL=0;
- } else if (sr<(u32)(SYSCLK/24.6)) {
+ } else if (sr < (u32)(sysclk_x_10/246)) {
NDEC=1;
SFIL=1;
- } else if (sr<(u32)(SYSCLK/16.0)) {
+ } else if (sr < (u32)(sysclk_x_10/160)) {
NDEC=1;
SFIL=0;
- } else if (sr<(u32)(SYSCLK/12.3)) {
+ } else if (sr < (u32)(sysclk_x_10/123)) {
NDEC=0;
SFIL=1;
}
- BDRI=SYSCLK*16;
+ BDRI = (state->sysclk)*16;
BDRI>>=NDEC;
BDRI +=sr/2;
BDRI /=sr;
@@ -251,11 +209,12 @@ static int tda10023_set_symbolrate (struct tda10023_state* state, u32 sr)
BDRX=1<<(24+NDEC);
BDRX*=sr;
- do_div(BDRX,SYSCLK); // BDRX/=SYSCLK;
+ do_div(BDRX, state->sysclk); /* BDRX/=SYSCLK; */
BDR=(s32)BDRX;
}
-// printk("Symbolrate %i, BDR %i BDRI %i, NDEC %i\n",sr,BDR,BDRI,NDEC);
+ dprintk("Symbolrate %i, BDR %i BDRI %i, NDEC %i\n",
+ sr, BDR, BDRI, NDEC);
tda10023_writebit (state, 0x03, 0xc0, NDEC<<6);
tda10023_writereg (state, 0x0a, BDR&255);
tda10023_writereg (state, 0x0b, (BDR>>8)&255);
@@ -268,8 +227,63 @@ static int tda10023_set_symbolrate (struct tda10023_state* state, u32 sr)
static int tda10023_init (struct dvb_frontend *fe)
{
struct tda10023_state* state = fe->demodulator_priv;
+ u8 tda10023_inittab[] = {
+/* reg mask val */
+/* 000 */ 0x2a, 0xff, 0x02, /* PLL3, Bypass, Power Down */
+/* 003 */ 0xff, 0x64, 0x00, /* Sleep 100ms */
+/* 006 */ 0x2a, 0xff, 0x03, /* PLL3, Bypass, Power Down */
+/* 009 */ 0xff, 0x64, 0x00, /* Sleep 100ms */
+ /* PLL1 */
+/* 012 */ 0x28, 0xff, (state->pll_m-1),
+ /* PLL2 */
+/* 015 */ 0x29, 0xff, ((state->pll_p-1)<<6)|(state->pll_n-1),
+ /* GPR FSAMPLING=1 */
+/* 018 */ 0x00, 0xff, REG0_INIT_VAL,
+/* 021 */ 0x2a, 0xff, 0x08, /* PLL3 PSACLK=1 */
+/* 024 */ 0xff, 0x64, 0x00, /* Sleep 100ms */
+/* 027 */ 0x1f, 0xff, 0x00, /* RESET */
+/* 030 */ 0xff, 0x64, 0x00, /* Sleep 100ms */
+/* 033 */ 0xe6, 0x0c, 0x04, /* RSCFG_IND */
+/* 036 */ 0x10, 0xc0, 0x80, /* DECDVBCFG1 PBER=1 */
+
+/* 039 */ 0x0e, 0xff, 0x82, /* GAIN1 */
+/* 042 */ 0x03, 0x08, 0x08, /* CLKCONF DYN=1 */
+/* 045 */ 0x2e, 0xbf, 0x30, /* AGCCONF2 TRIAGC=0,POSAGC=ENAGCIF=1
+ PPWMTUN=0 PPWMIF=0 */
+/* 048 */ 0x01, 0xff, 0x30, /* AGCREF */
+/* 051 */ 0x1e, 0x84, 0x84, /* CONTROL SACLK_ON=1 */
+/* 054 */ 0x1b, 0xff, 0xc8, /* ADC TWOS=1 */
+/* 057 */ 0x3b, 0xff, 0xff, /* IFMAX */
+/* 060 */ 0x3c, 0xff, 0x00, /* IFMIN */
+/* 063 */ 0x34, 0xff, 0x00, /* PWMREF */
+/* 066 */ 0x35, 0xff, 0xff, /* TUNMAX */
+/* 069 */ 0x36, 0xff, 0x00, /* TUNMIN */
+/* 072 */ 0x06, 0xff, 0x7f, /* EQCONF1 POSI=7 ENADAPT=ENEQUAL=DFE=1 */
+/* 075 */ 0x1c, 0x30, 0x30, /* EQCONF2 STEPALGO=SGNALGO=1 */
+/* 078 */ 0x37, 0xff, 0xf6, /* DELTAF_LSB */
+/* 081 */ 0x38, 0xff, 0xff, /* DELTAF_MSB */
+/* 084 */ 0x02, 0xff, 0x93, /* AGCCONF1 IFS=1 KAGCIF=2 KAGCTUN=3 */
+/* 087 */ 0x2d, 0xff, 0xf6, /* SWEEP SWPOS=1 SWDYN=7 SWSTEP=1 SWLEN=2 */
+/* 090 */ 0x04, 0x10, 0x00, /* SWRAMP=1 */
+/* 093 */ 0x12, 0xff, 0xa1, /* INTP1 POCLKP=1 FEL=1 MFS=0 */
+/* 096 */ 0x2b, 0x01, 0xa1, /* INTS1 */
+/* 099 */ 0x20, 0xff, 0x04, /* INTP2 SWAPP=? MSBFIRSTP=? INTPSEL=? */
+/* 102 */ 0x2c, 0xff, 0x0d, /* INTP/S TRIP=0 TRIS=0 */
+/* 105 */ 0xc4, 0xff, 0x00,
+/* 108 */ 0xc3, 0x30, 0x00,
+/* 111 */ 0xb5, 0xff, 0x19, /* ERAGC_THD */
+/* 114 */ 0x00, 0x03, 0x01, /* GPR, CLBS soft reset */
+/* 117 */ 0x00, 0x03, 0x03, /* GPR, CLBS soft reset */
+/* 120 */ 0xff, 0x64, 0x00, /* Sleep 100ms */
+/* 123 */ 0xff, 0xff, 0xff
+};
+ dprintk("DVB: TDA10023(%d): init chip\n", fe->dvb->num);
- dprintk("DVB: TDA10023(%d): init chip\n", fe->adapter->num);
+ /* override default values if set in config */
+ if (state->config->deltaf) {
+ tda10023_inittab[80] = (state->config->deltaf & 0xff);
+ tda10023_inittab[83] = (state->config->deltaf >> 8);
+ }
tda10023_writetab(state, tda10023_inittab);
@@ -456,12 +470,11 @@ static void tda10023_release(struct dvb_frontend* fe)
static struct dvb_frontend_ops tda10023_ops;
-struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config,
- struct i2c_adapter* i2c,
+struct dvb_frontend *tda10023_attach(const struct tda10023_config *config,
+ struct i2c_adapter *i2c,
u8 pwm)
{
struct tda10023_state* state = NULL;
- int i;
/* allocate memory for the internal state */
state = kmalloc(sizeof(struct tda10023_state), GFP_KERNEL);
@@ -470,22 +483,40 @@ struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config,
/* setup the state */
state->config = config;
state->i2c = i2c;
- memcpy(&state->frontend.ops, &tda10023_ops, sizeof(struct dvb_frontend_ops));
- state->pwm = pwm;
- for (i=0; i < ARRAY_SIZE(tda10023_inittab);i+=3) {
- if (tda10023_inittab[i] == 0x00) {
- state->reg0 = tda10023_inittab[i+2];
- break;
- }
- }
- // Wakeup if in standby
+ /* wakeup if in standby */
tda10023_writereg (state, 0x00, 0x33);
/* check if the demod is there */
if ((tda10023_readreg(state, 0x1a) & 0xf0) != 0x70) goto error;
/* create dvb_frontend */
memcpy(&state->frontend.ops, &tda10023_ops, sizeof(struct dvb_frontend_ops));
+ state->pwm = pwm;
+ state->reg0 = REG0_INIT_VAL;
+ if (state->config->xtal) {
+ state->xtal = state->config->xtal;
+ state->pll_m = state->config->pll_m;
+ state->pll_p = state->config->pll_p;
+ state->pll_n = state->config->pll_n;
+ } else {
+ /* set default values if not defined in config */
+ state->xtal = 28920000;
+ state->pll_m = 8;
+ state->pll_p = 4;
+ state->pll_n = 1;
+ }
+
+ /* calc sysclk */
+ state->sysclk = (state->xtal * state->pll_m / \
+ (state->pll_n * state->pll_p));
+
+ state->frontend.ops.info.symbol_rate_min = (state->sysclk/2)/64;
+ state->frontend.ops.info.symbol_rate_max = (state->sysclk/2)/4;
+
+ dprintk("DVB: TDA10023 %s: xtal:%d pll_m:%d pll_p:%d pll_n:%d\n",
+ __func__, state->xtal, state->pll_m, state->pll_p,
+ state->pll_n);
+
state->frontend.demodulator_priv = state;
return &state->frontend;
@@ -500,10 +531,10 @@ static struct dvb_frontend_ops tda10023_ops = {
.name = "Philips TDA10023 DVB-C",
.type = FE_QAM,
.frequency_stepsize = 62500,
- .frequency_min = 47000000,
+ .frequency_min = 47000000,
.frequency_max = 862000000,
- .symbol_rate_min = (SYSCLK/2)/64, /* SACLK/64 == (SYSCLK/2)/64 */
- .symbol_rate_max = (SYSCLK/2)/4, /* SACLK/4 */
+ .symbol_rate_min = 0, /* set in tda10023_attach */
+ .symbol_rate_max = 0, /* set in tda10023_attach */
.caps = 0x400 | //FE_CAN_QAM_4
FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 |
FE_CAN_QAM_128 | FE_CAN_QAM_256 |
diff --git a/drivers/media/dvb/frontends/tda1002x.h b/drivers/media/dvb/frontends/tda1002x.h
index 1bcc0d44b90b..4522b7ef53c9 100644
--- a/drivers/media/dvb/frontends/tda1002x.h
+++ b/drivers/media/dvb/frontends/tda1002x.h
@@ -26,11 +26,25 @@
#include <linux/dvb/frontend.h>
-struct tda1002x_config
-{
+struct tda1002x_config {
+ /* the demodulator's i2c address */
+ u8 demod_address;
+ u8 invert;
+};
+
+struct tda10023_config {
/* the demodulator's i2c address */
u8 demod_address;
u8 invert;
+
+ /* clock settings */
+ u32 xtal; /* defaults: 28920000 */
+ u8 pll_m; /* defaults: 8 */
+ u8 pll_p; /* defaults: 4 */
+ u8 pll_n; /* defaults: 1 */
+
+ /* input freq offset + baseband conversion type */
+ u16 deltaf;
};
#if defined(CONFIG_DVB_TDA10021) || (defined(CONFIG_DVB_TDA10021_MODULE) && defined(MODULE))
@@ -45,12 +59,15 @@ static inline struct dvb_frontend* tda10021_attach(const struct tda1002x_config*
}
#endif // CONFIG_DVB_TDA10021
-#if defined(CONFIG_DVB_TDA10023) || (defined(CONFIG_DVB_TDA10023_MODULE) && defined(MODULE))
-extern struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config,
- struct i2c_adapter* i2c, u8 pwm);
+#if defined(CONFIG_DVB_TDA10023) || \
+ (defined(CONFIG_DVB_TDA10023_MODULE) && defined(MODULE))
+extern struct dvb_frontend *tda10023_attach(
+ const struct tda10023_config *config,
+ struct i2c_adapter *i2c, u8 pwm);
#else
-static inline struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config,
- struct i2c_adapter* i2c, u8 pwm)
+static inline struct dvb_frontend *tda10023_attach(
+ const struct tda1002x_config *config,
+ struct i2c_adapter *i2c, u8 pwm)
{
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL;
diff --git a/drivers/media/dvb/frontends/tda10048.c b/drivers/media/dvb/frontends/tda10048.c
index 090fb7dd93c6..0ab8d86b3ae3 100644
--- a/drivers/media/dvb/frontends/tda10048.c
+++ b/drivers/media/dvb/frontends/tda10048.c
@@ -233,7 +233,7 @@ static u8 tda10048_readreg(struct tda10048_state *state, u8 reg)
}
static int tda10048_writeregbulk(struct tda10048_state *state, u8 reg,
- u8 *data, u16 len)
+ const u8 *data, u16 len)
{
int ret = -EREMOTEIO;
struct i2c_msg msg;
diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c
index 49973846373e..ff6b42781a5d 100644
--- a/drivers/media/dvb/frontends/tda1004x.c
+++ b/drivers/media/dvb/frontends/tda1004x.c
@@ -317,7 +317,7 @@ static int tda10046h_set_bandwidth(struct tda1004x_state *state,
}
static int tda1004x_do_upload(struct tda1004x_state *state,
- unsigned char *mem, unsigned int len,
+ const unsigned char *mem, unsigned int len,
u8 dspCodeCounterReg, u8 dspCodeInReg)
{
u8 buf[65];
diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig
index d4339b1b3b68..07643e010093 100644
--- a/drivers/media/dvb/ttpci/Kconfig
+++ b/drivers/media/dvb/ttpci/Kconfig
@@ -101,6 +101,7 @@ config DVB_BUDGET
config DVB_BUDGET_CI
tristate "Budget cards with onboard CI connector"
depends on DVB_BUDGET_CORE && I2C
+ depends on INPUT # due to IR
select DVB_STV0297 if !DVB_FE_CUSTOMISE
select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_TDA1004X if !DVB_FE_CUSTOMISE
diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c
index b30a5288e484..b7d1f2f18d3a 100644
--- a/drivers/media/dvb/ttpci/budget-av.c
+++ b/drivers/media/dvb/ttpci/budget-av.c
@@ -667,6 +667,11 @@ static struct tda1002x_config philips_cu1216_config_altaddress = {
.invert = 0,
};
+static struct tda10023_config philips_cu1216_tda10023_config = {
+ .demod_address = 0x0c,
+ .invert = 1,
+};
+
static int philips_tu1216_tuner_init(struct dvb_frontend *fe)
{
struct budget *budget = (struct budget *) fe->dvb->priv;
@@ -1019,9 +1024,10 @@ static void frontend_init(struct budget_av *budget_av)
case SUBID_DVBC_KNC1_PLUS_MK3:
budget_av->reinitialise_demod = 1;
budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240;
- fe = dvb_attach(tda10023_attach, &philips_cu1216_config,
- &budget_av->budget.i2c_adap,
- read_pwm(budget_av));
+ fe = dvb_attach(tda10023_attach,
+ &philips_cu1216_tda10023_config,
+ &budget_av->budget.i2c_adap,
+ read_pwm(budget_av));
if (fe) {
fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params;
}
diff --git a/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/drivers/media/dvb/ttusb-dec/ttusb_dec.c
index fefdc05e84ac..de5829b863fd 100644
--- a/drivers/media/dvb/ttusb-dec/ttusb_dec.c
+++ b/drivers/media/dvb/ttusb-dec/ttusb_dec.c
@@ -1275,7 +1275,7 @@ static int ttusb_dec_boot_dsp(struct ttusb_dec *dec)
u8 b1[] = { 0x61 };
u8 *b;
char idstring[21];
- u8 *firmware = NULL;
+ const u8 *firmware = NULL;
size_t firmware_size = 0;
u16 firmware_csum = 0;
__be16 firmware_csum_ns;
diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c
index 77354ca6e8e9..dc93a882b385 100644
--- a/drivers/media/radio/radio-si470x.c
+++ b/drivers/media/radio/radio-si470x.c
@@ -24,6 +24,19 @@
/*
+ * User Notes:
+ * - USB Audio is provided by the alsa snd_usb_audio module.
+ * For listing you have to redirect the sound, for example using:
+ * arecord -D hw:1,0 -r96000 -c2 -f S16_LE | artsdsp aplay -B -
+ * - regarding module parameters in /sys/module/radio_si470x/parameters:
+ * the contents of read-only files (0444) are not updated, even if
+ * space, band and de are changed using private video controls
+ * - increase tune_timeout, if you often get -EIO errors
+ * - hw_freq_seek returns -EAGAIN, when timed out or band limit is reached
+ */
+
+
+/*
* History:
* 2008-01-12 Tobias Lorenz <tobias.lorenz@gmx.net>
* Version 1.0.0
@@ -85,10 +98,14 @@
* Oliver Neukum <oliver@neukum.org>
* Version 1.0.7
* - usb autosuspend support
- * - unplugging fixed
+ * - unplugging fixed
+ * 2008-05-07 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.8
+ * - hardware frequency seek support
+ * - afc indication
+ * - more safety checks, let si470x_get_freq return errno
*
* ToDo:
- * - add seeking support
* - add firmware download/update support
* - RDS support: interrupt mode, instead of polling
* - add LED status output (check if that's not already done in firmware)
@@ -98,10 +115,10 @@
/* driver definitions */
#define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>"
#define DRIVER_NAME "radio-si470x"
-#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 7)
+#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 8)
#define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
#define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers"
-#define DRIVER_VERSION "1.0.7"
+#define DRIVER_VERSION "1.0.8"
/* kernel includes */
@@ -175,6 +192,11 @@ static unsigned int tune_timeout = 3000;
module_param(tune_timeout, uint, 0);
MODULE_PARM_DESC(tune_timeout, "Tune timeout: *3000*");
+/* Seek timeout */
+static unsigned int seek_timeout = 5000;
+module_param(seek_timeout, uint, 0);
+MODULE_PARM_DESC(seek_timeout, "Seek timeout: *5000*");
+
/* RDS buffer blocks */
static unsigned int rds_buf = 100;
module_param(rds_buf, uint, 0);
@@ -425,7 +447,8 @@ struct si470x_device {
/* driver management */
unsigned int users;
- unsigned char disconnected;
+ unsigned char disconnected;
+ struct mutex disconnect_lock;
/* Silabs internal registers (0..15) */
unsigned short registers[RADIO_REGISTER_NUM];
@@ -442,12 +465,6 @@ struct si470x_device {
/*
- * Lock to prevent kfree of data before all users have releases the device.
- */
-static DEFINE_MUTEX(open_close_lock);
-
-
-/*
* The frequency is set in units of 62.5 Hz when using V4L2_TUNER_CAP_LOW,
* 62.5 kHz otherwise.
* The tuner is able to have a channel spacing of 50, 100 or 200 kHz.
@@ -476,11 +493,11 @@ static int si470x_get_report(struct si470x_device *radio, void *buf, int size)
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
report[0], 2,
buf, size, usb_timeout);
+
if (retval < 0)
printk(KERN_WARNING DRIVER_NAME
": si470x_get_report: usb_control_msg returned %d\n",
retval);
-
return retval;
}
@@ -499,11 +516,11 @@ static int si470x_set_report(struct si470x_device *radio, void *buf, int size)
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
report[0], 2,
buf, size, usb_timeout);
+
if (retval < 0)
printk(KERN_WARNING DRIVER_NAME
": si470x_set_report: usb_control_msg returned %d\n",
retval);
-
return retval;
}
@@ -521,8 +538,7 @@ static int si470x_get_register(struct si470x_device *radio, int regnr)
retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
if (retval >= 0)
- radio->registers[regnr] = be16_to_cpu(get_unaligned(
- (unsigned short *) &buf[1]));
+ radio->registers[regnr] = get_unaligned_be16(&buf[1]);
return (retval < 0) ? -EINVAL : 0;
}
@@ -537,8 +553,7 @@ static int si470x_set_register(struct si470x_device *radio, int regnr)
int retval;
buf[0] = REGISTER_REPORT(regnr);
- put_unaligned(cpu_to_be16(radio->registers[regnr]),
- (unsigned short *) &buf[1]);
+ put_unaligned_be16(radio->registers[regnr], &buf[1]);
retval = si470x_set_report(radio, (void *) &buf, sizeof(buf));
@@ -561,9 +576,8 @@ static int si470x_get_all_registers(struct si470x_device *radio)
if (retval >= 0)
for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++)
- radio->registers[regnr] = be16_to_cpu(get_unaligned(
- (unsigned short *)
- &buf[regnr * RADIO_REGISTER_SIZE + 1]));
+ radio->registers[regnr] = get_unaligned_be16(
+ &buf[regnr * RADIO_REGISTER_SIZE + 1]);
return (retval < 0) ? -EINVAL : 0;
}
@@ -585,7 +599,7 @@ static int si470x_get_rds_registers(struct si470x_device *radio)
usb_rcvintpipe(radio->usbdev, 1),
(void *) &buf, sizeof(buf), &size, usb_timeout);
if (size != sizeof(buf))
- printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: "
+ printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: "
"return size differs: %d != %zu\n", size, sizeof(buf));
if (retval < 0)
printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: "
@@ -594,8 +608,8 @@ static int si470x_get_rds_registers(struct si470x_device *radio)
if (retval >= 0)
for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++)
radio->registers[STATUSRSSI + regnr] =
- be16_to_cpu(get_unaligned((unsigned short *)
- &buf[regnr * RADIO_REGISTER_SIZE + 1]));
+ get_unaligned_be16(
+ &buf[regnr * RADIO_REGISTER_SIZE + 1]);
return (retval < 0) ? -EINVAL : 0;
}
@@ -615,33 +629,39 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
radio->registers[CHANNEL] |= CHANNEL_TUNE | chan;
retval = si470x_set_register(radio, CHANNEL);
if (retval < 0)
- return retval;
+ goto done;
- /* wait till seek operation has completed */
+ /* wait till tune operation has completed */
timeout = jiffies + msecs_to_jiffies(tune_timeout);
do {
retval = si470x_get_register(radio, STATUSRSSI);
if (retval < 0)
- return retval;
+ goto stop;
timed_out = time_after(jiffies, timeout);
} while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) &&
(!timed_out));
+ if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
+ printk(KERN_WARNING DRIVER_NAME ": tune does not complete\n");
if (timed_out)
printk(KERN_WARNING DRIVER_NAME
- ": seek does not finish after %u ms\n", tune_timeout);
+ ": tune timed out after %u ms\n", tune_timeout);
+stop:
/* stop tuning */
radio->registers[CHANNEL] &= ~CHANNEL_TUNE;
- return si470x_set_register(radio, CHANNEL);
+ retval = si470x_set_register(radio, CHANNEL);
+
+done:
+ return retval;
}
/*
* si470x_get_freq - get the frequency
*/
-static unsigned int si470x_get_freq(struct si470x_device *radio)
+static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq)
{
- unsigned int spacing, band_bottom, freq;
+ unsigned int spacing, band_bottom;
unsigned short chan;
int retval;
@@ -667,14 +687,12 @@ static unsigned int si470x_get_freq(struct si470x_device *radio)
/* read channel */
retval = si470x_get_register(radio, READCHAN);
- if (retval < 0)
- return retval;
chan = radio->registers[READCHAN] & READCHAN_READCHAN;
/* Frequency (MHz) = Spacing (kHz) x Channel + Bottom of Band (MHz) */
- freq = chan * spacing + band_bottom;
+ *freq = chan * spacing + band_bottom;
- return freq;
+ return retval;
}
@@ -714,6 +732,62 @@ static int si470x_set_freq(struct si470x_device *radio, unsigned int freq)
/*
+ * si470x_set_seek - set seek
+ */
+static int si470x_set_seek(struct si470x_device *radio,
+ unsigned int wrap_around, unsigned int seek_upward)
+{
+ int retval = 0;
+ unsigned long timeout;
+ bool timed_out = 0;
+
+ /* start seeking */
+ radio->registers[POWERCFG] |= POWERCFG_SEEK;
+ if (wrap_around == 1)
+ radio->registers[POWERCFG] &= ~POWERCFG_SKMODE;
+ else
+ radio->registers[POWERCFG] |= POWERCFG_SKMODE;
+ if (seek_upward == 1)
+ radio->registers[POWERCFG] |= POWERCFG_SEEKUP;
+ else
+ radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP;
+ retval = si470x_set_register(radio, POWERCFG);
+ if (retval < 0)
+ goto done;
+
+ /* wait till seek operation has completed */
+ timeout = jiffies + msecs_to_jiffies(seek_timeout);
+ do {
+ retval = si470x_get_register(radio, STATUSRSSI);
+ if (retval < 0)
+ goto stop;
+ timed_out = time_after(jiffies, timeout);
+ } while (((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) &&
+ (!timed_out));
+ if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0)
+ printk(KERN_WARNING DRIVER_NAME ": seek does not complete\n");
+ if (radio->registers[STATUSRSSI] & STATUSRSSI_SF)
+ printk(KERN_WARNING DRIVER_NAME
+ ": seek failed / band limit reached\n");
+ if (timed_out)
+ printk(KERN_WARNING DRIVER_NAME
+ ": seek timed out after %u ms\n", seek_timeout);
+
+stop:
+ /* stop seeking */
+ radio->registers[POWERCFG] &= ~POWERCFG_SEEK;
+ retval = si470x_set_register(radio, POWERCFG);
+
+done:
+ /* try again, if timed out */
+ if ((retval == 0) && timed_out)
+ retval = -EAGAIN;
+
+ return retval;
+}
+
+
+/*
* si470x_start - switch on radio
*/
static int si470x_start(struct si470x_device *radio)
@@ -725,27 +799,30 @@ static int si470x_start(struct si470x_device *radio)
POWERCFG_DMUTE | POWERCFG_ENABLE | POWERCFG_RDSM;
retval = si470x_set_register(radio, POWERCFG);
if (retval < 0)
- return retval;
+ goto done;
/* sysconfig 1 */
radio->registers[SYSCONFIG1] = SYSCONFIG1_DE;
retval = si470x_set_register(radio, SYSCONFIG1);
if (retval < 0)
- return retval;
+ goto done;
/* sysconfig 2 */
radio->registers[SYSCONFIG2] =
- (0x3f << 8) | /* SEEKTH */
- (band << 6) | /* BAND */
- (space << 4) | /* SPACE */
- 15; /* VOLUME (max) */
+ (0x3f << 8) | /* SEEKTH */
+ ((band << 6) & SYSCONFIG2_BAND) | /* BAND */
+ ((space << 4) & SYSCONFIG2_SPACE) | /* SPACE */
+ 15; /* VOLUME (max) */
retval = si470x_set_register(radio, SYSCONFIG2);
if (retval < 0)
- return retval;
+ goto done;
/* reset last channel */
- return si470x_set_chan(radio,
+ retval = si470x_set_chan(radio,
radio->registers[CHANNEL] & CHANNEL_CHAN);
+
+done:
+ return retval;
}
@@ -760,13 +837,16 @@ static int si470x_stop(struct si470x_device *radio)
radio->registers[SYSCONFIG1] &= ~SYSCONFIG1_RDS;
retval = si470x_set_register(radio, SYSCONFIG1);
if (retval < 0)
- return retval;
+ goto done;
/* powercfg */
radio->registers[POWERCFG] &= ~POWERCFG_DMUTE;
/* POWERCFG_ENABLE has to automatically go low */
radio->registers[POWERCFG] |= POWERCFG_ENABLE | POWERCFG_DISABLE;
- return si470x_set_register(radio, POWERCFG);
+ retval = si470x_set_register(radio, POWERCFG);
+
+done:
+ return retval;
}
@@ -843,7 +923,7 @@ static void si470x_rds(struct si470x_device *radio)
};
/* Fill the V4L2 RDS buffer */
- put_unaligned(cpu_to_le16(rds), (unsigned short *) &tmpbuf);
+ put_unaligned_le16(rds, &tmpbuf);
tmpbuf[2] = blocknum; /* offset name */
tmpbuf[2] |= blocknum << 3; /* received offset */
if (bler > max_rds_errors)
@@ -883,8 +963,9 @@ static void si470x_work(struct work_struct *work)
struct si470x_device *radio = container_of(work, struct si470x_device,
work.work);
- if (radio->disconnected)
- return;
+ /* safety checks */
+ if (radio->disconnected)
+ return;
if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0)
return;
@@ -917,11 +998,15 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf,
/* block if no new data available */
while (radio->wr_index == radio->rd_index) {
- if (file->f_flags & O_NONBLOCK)
- return -EWOULDBLOCK;
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EWOULDBLOCK;
+ goto done;
+ }
if (wait_event_interruptible(radio->read_queue,
- radio->wr_index != radio->rd_index) < 0)
- return -EINTR;
+ radio->wr_index != radio->rd_index) < 0) {
+ retval = -EINTR;
+ goto done;
+ }
}
/* calculate block count from byte count */
@@ -950,6 +1035,7 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf,
}
mutex_unlock(&radio->lock);
+done:
return retval;
}
@@ -961,6 +1047,7 @@ static unsigned int si470x_fops_poll(struct file *file,
struct poll_table_struct *pts)
{
struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+ int retval = 0;
/* switch on rds reception */
if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) {
@@ -972,9 +1059,9 @@ static unsigned int si470x_fops_poll(struct file *file,
poll_wait(file, &radio->read_queue, pts);
if (radio->rd_index != radio->wr_index)
- return POLLIN | POLLRDNORM;
+ retval = POLLIN | POLLRDNORM;
- return 0;
+ return retval;
}
@@ -991,17 +1078,18 @@ static int si470x_fops_open(struct inode *inode, struct file *file)
retval = usb_autopm_get_interface(radio->intf);
if (retval < 0) {
radio->users--;
- return -EIO;
+ retval = -EIO;
+ goto done;
}
if (radio->users == 1) {
retval = si470x_start(radio);
if (retval < 0)
usb_autopm_put_interface(radio->intf);
- return retval;
}
- return 0;
+done:
+ return retval;
}
@@ -1011,20 +1099,23 @@ static int si470x_fops_open(struct inode *inode, struct file *file)
static int si470x_fops_release(struct inode *inode, struct file *file)
{
struct si470x_device *radio = video_get_drvdata(video_devdata(file));
- int retval = 0;
+ int retval = 0;
- if (!radio)
- return -ENODEV;
+ /* safety check */
+ if (!radio) {
+ retval = -ENODEV;
+ goto done;
+ }
- mutex_lock(&open_close_lock);
+ mutex_lock(&radio->disconnect_lock);
radio->users--;
if (radio->users == 0) {
- if (radio->disconnected) {
- video_unregister_device(radio->videodev);
- kfree(radio->buffer);
- kfree(radio);
- goto done;
- }
+ if (radio->disconnected) {
+ video_unregister_device(radio->videodev);
+ kfree(radio->buffer);
+ kfree(radio);
+ goto unlock;
+ }
/* stop rds reception */
cancel_delayed_work_sync(&radio->work);
@@ -1036,9 +1127,11 @@ static int si470x_fops_release(struct inode *inode, struct file *file)
usb_autopm_put_interface(radio->intf);
}
+unlock:
+ mutex_unlock(&radio->disconnect_lock);
+
done:
- mutex_unlock(&open_close_lock);
- return retval;
+ return retval;
}
@@ -1116,7 +1209,8 @@ static int si470x_vidioc_querycap(struct file *file, void *priv,
strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
sprintf(capability->bus_info, "USB");
capability->version = DRIVER_KERNEL_VERSION;
- capability->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ capability->capabilities = V4L2_CAP_HW_FREQ_SEEK |
+ V4L2_CAP_TUNER | V4L2_CAP_RADIO;
return 0;
}
@@ -1125,7 +1219,7 @@ static int si470x_vidioc_querycap(struct file *file, void *priv,
/*
* si470x_vidioc_g_input - get input
*/
-static int si470x_vidioc_g_input(struct file *filp, void *priv,
+static int si470x_vidioc_g_input(struct file *file, void *priv,
unsigned int *i)
{
*i = 0;
@@ -1137,12 +1231,18 @@ static int si470x_vidioc_g_input(struct file *filp, void *priv,
/*
* si470x_vidioc_s_input - set input
*/
-static int si470x_vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+static int si470x_vidioc_s_input(struct file *file, void *priv, unsigned int i)
{
+ int retval = 0;
+
+ /* safety checks */
if (i != 0)
- return -EINVAL;
+ retval = -EINVAL;
- return 0;
+ if (retval < 0)
+ printk(KERN_WARNING DRIVER_NAME
+ ": set input failed with %d\n", retval);
+ return retval;
}
@@ -1155,17 +1255,22 @@ static int si470x_vidioc_queryctrl(struct file *file, void *priv,
unsigned char i;
int retval = -EINVAL;
+ /* safety checks */
+ if (!qc->id)
+ goto done;
+
for (i = 0; i < ARRAY_SIZE(si470x_v4l2_queryctrl); i++) {
- if (qc->id && qc->id == si470x_v4l2_queryctrl[i].id) {
+ if (qc->id == si470x_v4l2_queryctrl[i].id) {
memcpy(qc, &(si470x_v4l2_queryctrl[i]), sizeof(*qc));
retval = 0;
break;
}
}
+
+done:
if (retval < 0)
printk(KERN_WARNING DRIVER_NAME
- ": query control failed with %d\n", retval);
-
+ ": query controls failed with %d\n", retval);
return retval;
}
@@ -1177,9 +1282,13 @@ static int si470x_vidioc_g_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+ int retval = 0;
- if (radio->disconnected)
- return -EIO;
+ /* safety checks */
+ if (radio->disconnected) {
+ retval = -EIO;
+ goto done;
+ }
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
@@ -1190,9 +1299,15 @@ static int si470x_vidioc_g_ctrl(struct file *file, void *priv,
ctrl->value = ((radio->registers[POWERCFG] &
POWERCFG_DMUTE) == 0) ? 1 : 0;
break;
+ default:
+ retval = -EINVAL;
}
- return 0;
+done:
+ if (retval < 0)
+ printk(KERN_WARNING DRIVER_NAME
+ ": get control failed with %d\n", retval);
+ return retval;
}
@@ -1203,10 +1318,13 @@ static int si470x_vidioc_s_ctrl(struct file *file, void *priv,
struct v4l2_control *ctrl)
{
struct si470x_device *radio = video_get_drvdata(video_devdata(file));
- int retval;
+ int retval = 0;
- if (radio->disconnected)
- return -EIO;
+ /* safety checks */
+ if (radio->disconnected) {
+ retval = -EIO;
+ goto done;
+ }
switch (ctrl->id) {
case V4L2_CID_AUDIO_VOLUME:
@@ -1224,10 +1342,11 @@ static int si470x_vidioc_s_ctrl(struct file *file, void *priv,
default:
retval = -EINVAL;
}
+
+done:
if (retval < 0)
printk(KERN_WARNING DRIVER_NAME
": set control failed with %d\n", retval);
-
return retval;
}
@@ -1238,13 +1357,22 @@ static int si470x_vidioc_s_ctrl(struct file *file, void *priv,
static int si470x_vidioc_g_audio(struct file *file, void *priv,
struct v4l2_audio *audio)
{
- if (audio->index > 1)
- return -EINVAL;
+ int retval = 0;
+
+ /* safety checks */
+ if (audio->index != 0) {
+ retval = -EINVAL;
+ goto done;
+ }
strcpy(audio->name, "Radio");
audio->capability = V4L2_AUDCAP_STEREO;
- return 0;
+done:
+ if (retval < 0)
+ printk(KERN_WARNING DRIVER_NAME
+ ": get audio failed with %d\n", retval);
+ return retval;
}
@@ -1254,10 +1382,19 @@ static int si470x_vidioc_g_audio(struct file *file, void *priv,
static int si470x_vidioc_s_audio(struct file *file, void *priv,
struct v4l2_audio *audio)
{
- if (audio->index != 0)
- return -EINVAL;
+ int retval = 0;
- return 0;
+ /* safety checks */
+ if (audio->index != 0) {
+ retval = -EINVAL;
+ goto done;
+ }
+
+done:
+ if (retval < 0)
+ printk(KERN_WARNING DRIVER_NAME
+ ": set audio failed with %d\n", retval);
+ return retval;
}
@@ -1268,20 +1405,23 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
struct v4l2_tuner *tuner)
{
struct si470x_device *radio = video_get_drvdata(video_devdata(file));
- int retval;
+ int retval = 0;
- if (radio->disconnected)
- return -EIO;
- if (tuner->index > 0)
- return -EINVAL;
+ /* safety checks */
+ if (radio->disconnected) {
+ retval = -EIO;
+ goto done;
+ }
+ if ((tuner->index != 0) && (tuner->type != V4L2_TUNER_RADIO)) {
+ retval = -EINVAL;
+ goto done;
+ }
- /* read status rssi */
retval = si470x_get_register(radio, STATUSRSSI);
if (retval < 0)
- return retval;
+ goto done;
strcpy(tuner->name, "FM");
- tuner->type = V4L2_TUNER_RADIO;
switch (band) {
/* 0: 87.5 - 108 MHz (USA, Europe, default) */
default:
@@ -1313,9 +1453,14 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv,
* 0x0101;
/* automatic frequency control: -1: freq to low, 1 freq to high */
- tuner->afc = 0;
+ /* AFCRL does only indicate that freq. differs, not if too low/high */
+ tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0;
- return 0;
+done:
+ if (retval < 0)
+ printk(KERN_WARNING DRIVER_NAME
+ ": get tuner failed with %d\n", retval);
+ return retval;
}
@@ -1326,12 +1471,17 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv,
struct v4l2_tuner *tuner)
{
struct si470x_device *radio = video_get_drvdata(video_devdata(file));
- int retval;
+ int retval = 0;
- if (radio->disconnected)
- return -EIO;
- if (tuner->index > 0)
- return -EINVAL;
+ /* safety checks */
+ if (radio->disconnected) {
+ retval = -EIO;
+ goto done;
+ }
+ if ((tuner->index != 0) && (tuner->type != V4L2_TUNER_RADIO)) {
+ retval = -EINVAL;
+ goto done;
+ }
if (tuner->audmode == V4L2_TUNER_MODE_MONO)
radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */
@@ -1339,10 +1489,11 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv,
radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */
retval = si470x_set_register(radio, POWERCFG);
+
+done:
if (retval < 0)
printk(KERN_WARNING DRIVER_NAME
": set tuner failed with %d\n", retval);
-
return retval;
}
@@ -1354,14 +1505,25 @@ static int si470x_vidioc_g_frequency(struct file *file, void *priv,
struct v4l2_frequency *freq)
{
struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+ int retval = 0;
- if (radio->disconnected)
- return -EIO;
+ /* safety checks */
+ if (radio->disconnected) {
+ retval = -EIO;
+ goto done;
+ }
+ if ((freq->tuner != 0) && (freq->type != V4L2_TUNER_RADIO)) {
+ retval = -EINVAL;
+ goto done;
+ }
- freq->type = V4L2_TUNER_RADIO;
- freq->frequency = si470x_get_freq(radio);
+ retval = si470x_get_freq(radio, &freq->frequency);
- return 0;
+done:
+ if (retval < 0)
+ printk(KERN_WARNING DRIVER_NAME
+ ": get frequency failed with %d\n", retval);
+ return retval;
}
@@ -1372,19 +1534,55 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv,
struct v4l2_frequency *freq)
{
struct si470x_device *radio = video_get_drvdata(video_devdata(file));
- int retval;
+ int retval = 0;
- if (radio->disconnected)
- return -EIO;
- if (freq->type != V4L2_TUNER_RADIO)
- return -EINVAL;
+ /* safety checks */
+ if (radio->disconnected) {
+ retval = -EIO;
+ goto done;
+ }
+ if ((freq->tuner != 0) && (freq->type != V4L2_TUNER_RADIO)) {
+ retval = -EINVAL;
+ goto done;
+ }
retval = si470x_set_freq(radio, freq->frequency);
+
+done:
if (retval < 0)
printk(KERN_WARNING DRIVER_NAME
": set frequency failed with %d\n", retval);
+ return retval;
+}
- return 0;
+
+/*
+ * si470x_vidioc_s_hw_freq_seek - set hardware frequency seek
+ */
+static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv,
+ struct v4l2_hw_freq_seek *seek)
+{
+ struct si470x_device *radio = video_get_drvdata(video_devdata(file));
+ int retval = 0;
+
+ /* safety checks */
+ if (radio->disconnected) {
+ retval = -EIO;
+ goto done;
+ }
+ if ((seek->tuner != 0) && (seek->type != V4L2_TUNER_RADIO)) {
+ retval = -EINVAL;
+ goto done;
+ }
+
+ retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward);
+
+done:
+ if (retval < 0)
+ printk(KERN_WARNING DRIVER_NAME
+ ": set hardware frequency seek failed with %d\n",
+ retval);
+ return retval;
}
@@ -1408,6 +1606,7 @@ static struct video_device si470x_viddev_template = {
.vidioc_s_tuner = si470x_vidioc_s_tuner,
.vidioc_g_frequency = si470x_vidioc_g_frequency,
.vidioc_s_frequency = si470x_vidioc_s_frequency,
+ .vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek,
.owner = THIS_MODULE,
};
@@ -1424,31 +1623,36 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct si470x_device *radio;
- int retval = -ENOMEM;
+ int retval = 0;
- /* private data allocation */
+ /* private data allocation and initialization */
radio = kzalloc(sizeof(struct si470x_device), GFP_KERNEL);
- if (!radio)
+ if (!radio) {
+ retval = -ENOMEM;
goto err_initial;
+ }
+ radio->users = 0;
+ radio->disconnected = 0;
+ radio->usbdev = interface_to_usbdev(intf);
+ radio->intf = intf;
+ mutex_init(&radio->disconnect_lock);
+ mutex_init(&radio->lock);
- /* video device allocation */
+ /* video device allocation and initialization */
radio->videodev = video_device_alloc();
- if (!radio->videodev)
+ if (!radio->videodev) {
+ retval = -ENOMEM;
goto err_radio;
-
- /* initial configuration */
+ }
memcpy(radio->videodev, &si470x_viddev_template,
sizeof(si470x_viddev_template));
- radio->users = 0;
- radio->usbdev = interface_to_usbdev(intf);
- radio->intf = intf;
- mutex_init(&radio->lock);
video_set_drvdata(radio->videodev, radio);
/* show some infos about the specific device */
- retval = -EIO;
- if (si470x_get_all_registers(radio) < 0)
+ if (si470x_get_all_registers(radio) < 0) {
+ retval = -EIO;
goto err_all;
+ }
printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4hx ChipID=0x%4.4hx\n",
radio->registers[DEVICEID], radio->registers[CHIPID]);
@@ -1474,8 +1678,10 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
/* rds buffer allocation */
radio->buf_size = rds_buf * 3;
radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL);
- if (!radio->buffer)
+ if (!radio->buffer) {
+ retval = -EIO;
goto err_all;
+ }
/* rds buffer configuration */
radio->wr_index = 0;
@@ -1487,6 +1693,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
/* register video device */
if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) {
+ retval = -EIO;
printk(KERN_WARNING DRIVER_NAME
": Could not register video device\n");
goto err_all;
@@ -1546,16 +1753,16 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf)
{
struct si470x_device *radio = usb_get_intfdata(intf);
- mutex_lock(&open_close_lock);
- radio->disconnected = 1;
+ mutex_lock(&radio->disconnect_lock);
+ radio->disconnected = 1;
cancel_delayed_work_sync(&radio->work);
usb_set_intfdata(intf, NULL);
- if (radio->users == 0) {
- video_unregister_device(radio->videodev);
- kfree(radio->buffer);
- kfree(radio);
- }
- mutex_unlock(&open_close_lock);
+ if (radio->users == 0) {
+ video_unregister_device(radio->videodev);
+ kfree(radio->buffer);
+ kfree(radio);
+ }
+ mutex_unlock(&radio->disconnect_lock);
}
diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c
index 8ef0424c26c4..1c56ae92ce74 100644
--- a/drivers/media/video/bt8xx/bttv-cards.c
+++ b/drivers/media/video/bt8xx/bttv-cards.c
@@ -3768,7 +3768,8 @@ static int terratec_active_radio_upgrade(struct bttv *btv)
#define BTTV_ALT_DCLK 0x100000
#define BTTV_ALT_NCONFIG 0x800000
-static int __devinit pvr_altera_load(struct bttv *btv, u8 *micro, u32 microlen)
+static int __devinit pvr_altera_load(struct bttv *btv, const u8 *micro,
+ u32 microlen)
{
u32 n;
u8 bits;
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
index 0165aac533bf..c4aa802494ff 100644
--- a/drivers/media/video/bt8xx/bttv-driver.c
+++ b/drivers/media/video/bt8xx/bttv-driver.c
@@ -2448,7 +2448,7 @@ pix_format_set_size (struct v4l2_pix_format * f,
}
}
-static int bttv_g_fmt_cap(struct file *file, void *priv,
+static int bttv_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct bttv_fh *fh = priv;
@@ -2461,7 +2461,7 @@ static int bttv_g_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int bttv_g_fmt_overlay(struct file *file, void *priv,
+static int bttv_g_fmt_vid_overlay(struct file *file, void *priv,
struct v4l2_format *f)
{
struct bttv_fh *fh = priv;
@@ -2472,7 +2472,7 @@ static int bttv_g_fmt_overlay(struct file *file, void *priv,
return 0;
}
-static int bttv_try_fmt_cap(struct file *file, void *priv,
+static int bttv_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
const struct bttv_format *fmt;
@@ -2532,7 +2532,7 @@ static int bttv_try_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int bttv_try_fmt_overlay(struct file *file, void *priv,
+static int bttv_try_fmt_vid_overlay(struct file *file, void *priv,
struct v4l2_format *f)
{
struct bttv_fh *fh = priv;
@@ -2542,7 +2542,7 @@ static int bttv_try_fmt_overlay(struct file *file, void *priv,
/* adjust_crop */ 0);
}
-static int bttv_s_fmt_cap(struct file *file, void *priv,
+static int bttv_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
int retval;
@@ -2556,7 +2556,7 @@ static int bttv_s_fmt_cap(struct file *file, void *priv,
if (0 != retval)
return retval;
- retval = bttv_try_fmt_cap(file, priv, f);
+ retval = bttv_try_fmt_vid_cap(file, priv, f);
if (0 != retval)
return retval;
@@ -2591,7 +2591,7 @@ static int bttv_s_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int bttv_s_fmt_overlay(struct file *file, void *priv,
+static int bttv_s_fmt_vid_overlay(struct file *file, void *priv,
struct v4l2_format *f)
{
struct bttv_fh *fh = priv;
@@ -2661,7 +2661,7 @@ static int bttv_querycap(struct file *file, void *priv,
return 0;
}
-static int bttv_enum_fmt_vbi(struct file *file, void *priv,
+static int bttv_enum_fmt_vbi_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
if (0 != f->index)
@@ -2692,7 +2692,7 @@ static int bttv_enum_fmt_cap_ovr(struct v4l2_fmtdesc *f)
return i;
}
-static int bttv_enum_fmt_cap(struct file *file, void *priv,
+static int bttv_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
int rc = bttv_enum_fmt_cap_ovr(f);
@@ -2703,7 +2703,7 @@ static int bttv_enum_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int bttv_enum_fmt_overlay(struct file *file, void *priv,
+static int bttv_enum_fmt_vid_overlay(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
int rc;
@@ -3362,18 +3362,18 @@ static struct video_device bttv_video_template =
.fops = &bttv_fops,
.minor = -1,
.vidioc_querycap = bttv_querycap,
- .vidioc_enum_fmt_cap = bttv_enum_fmt_cap,
- .vidioc_g_fmt_cap = bttv_g_fmt_cap,
- .vidioc_try_fmt_cap = bttv_try_fmt_cap,
- .vidioc_s_fmt_cap = bttv_s_fmt_cap,
- .vidioc_enum_fmt_overlay = bttv_enum_fmt_overlay,
- .vidioc_g_fmt_overlay = bttv_g_fmt_overlay,
- .vidioc_try_fmt_overlay = bttv_try_fmt_overlay,
- .vidioc_s_fmt_overlay = bttv_s_fmt_overlay,
- .vidioc_enum_fmt_vbi = bttv_enum_fmt_vbi,
- .vidioc_g_fmt_vbi = bttv_g_fmt_vbi,
- .vidioc_try_fmt_vbi = bttv_try_fmt_vbi,
- .vidioc_s_fmt_vbi = bttv_s_fmt_vbi,
+ .vidioc_enum_fmt_vid_cap = bttv_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = bttv_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = bttv_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = bttv_s_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_overlay = bttv_enum_fmt_vid_overlay,
+ .vidioc_g_fmt_vid_overlay = bttv_g_fmt_vid_overlay,
+ .vidioc_try_fmt_vid_overlay = bttv_try_fmt_vid_overlay,
+ .vidioc_s_fmt_vid_overlay = bttv_s_fmt_vid_overlay,
+ .vidioc_enum_fmt_vbi_cap = bttv_enum_fmt_vbi_cap,
+ .vidioc_g_fmt_vbi_cap = bttv_g_fmt_vbi_cap,
+ .vidioc_try_fmt_vbi_cap = bttv_try_fmt_vbi_cap,
+ .vidioc_s_fmt_vbi_cap = bttv_s_fmt_vbi_cap,
.vidioc_g_audio = bttv_g_audio,
.vidioc_s_audio = bttv_s_audio,
.vidioc_cropcap = bttv_cropcap,
diff --git a/drivers/media/video/bt8xx/bttv-vbi.c b/drivers/media/video/bt8xx/bttv-vbi.c
index bfdbc469e30f..68f28e5fa040 100644
--- a/drivers/media/video/bt8xx/bttv-vbi.c
+++ b/drivers/media/video/bt8xx/bttv-vbi.c
@@ -303,7 +303,7 @@ static int try_fmt(struct v4l2_vbi_format *f, const struct bttv_tvnorm *tvnorm,
return 0;
}
-int bttv_try_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt)
+int bttv_try_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt)
{
struct bttv_fh *fh = f;
struct bttv *btv = fh->btv;
@@ -321,7 +321,7 @@ int bttv_try_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt)
}
-int bttv_s_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt)
+int bttv_s_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt)
{
struct bttv_fh *fh = f;
struct bttv *btv = fh->btv;
@@ -369,7 +369,7 @@ int bttv_s_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt)
}
-int bttv_g_fmt_vbi(struct file *file, void *f, struct v4l2_format *frt)
+int bttv_g_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt)
{
struct bttv_fh *fh = f;
const struct bttv_tvnorm *tvnorm;
diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h
index 27da7b423275..955e6bcd8ae0 100644
--- a/drivers/media/video/bt8xx/bttvp.h
+++ b/drivers/media/video/bt8xx/bttvp.h
@@ -254,9 +254,9 @@ int bttv_overlay_risc(struct bttv *btv, struct bttv_overlay *ov,
/* ---------------------------------------------------------- */
/* bttv-vbi.c */
-int bttv_try_fmt_vbi(struct file *file, void *fh, struct v4l2_format *f);
-int bttv_g_fmt_vbi(struct file *file, void *fh, struct v4l2_format *f);
-int bttv_s_fmt_vbi(struct file *file, void *fh, struct v4l2_format *f);
+int bttv_try_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f);
+int bttv_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f);
+int bttv_s_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f);
extern struct videobuf_queue_ops bttv_vbi_qops;
diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c
index 5195b1f3378a..d99453faaab7 100644
--- a/drivers/media/video/cafe_ccic.c
+++ b/drivers/media/video/cafe_ccic.c
@@ -1593,7 +1593,7 @@ static struct v4l2_pix_format cafe_def_pix_format = {
.sizeimage = VGA_WIDTH*VGA_HEIGHT*2,
};
-static int cafe_vidioc_enum_fmt_cap(struct file *filp,
+static int cafe_vidioc_enum_fmt_vid_cap(struct file *filp,
void *priv, struct v4l2_fmtdesc *fmt)
{
struct cafe_camera *cam = priv;
@@ -1608,7 +1608,7 @@ static int cafe_vidioc_enum_fmt_cap(struct file *filp,
}
-static int cafe_vidioc_try_fmt_cap (struct file *filp, void *priv,
+static int cafe_vidioc_try_fmt_vid_cap(struct file *filp, void *priv,
struct v4l2_format *fmt)
{
struct cafe_camera *cam = priv;
@@ -1620,7 +1620,7 @@ static int cafe_vidioc_try_fmt_cap (struct file *filp, void *priv,
return ret;
}
-static int cafe_vidioc_s_fmt_cap(struct file *filp, void *priv,
+static int cafe_vidioc_s_fmt_vid_cap(struct file *filp, void *priv,
struct v4l2_format *fmt)
{
struct cafe_camera *cam = priv;
@@ -1635,7 +1635,7 @@ static int cafe_vidioc_s_fmt_cap(struct file *filp, void *priv,
/*
* See if the formatting works in principle.
*/
- ret = cafe_vidioc_try_fmt_cap(filp, priv, fmt);
+ ret = cafe_vidioc_try_fmt_vid_cap(filp, priv, fmt);
if (ret)
return ret;
/*
@@ -1670,7 +1670,7 @@ static int cafe_vidioc_s_fmt_cap(struct file *filp, void *priv,
* The V4l2 spec wants us to be smarter, and actually get this from
* the camera (and not mess with it at open time). Someday.
*/
-static int cafe_vidioc_g_fmt_cap(struct file *filp, void *priv,
+static int cafe_vidioc_g_fmt_vid_cap(struct file *filp, void *priv,
struct v4l2_format *f)
{
struct cafe_camera *cam = priv;
@@ -1780,10 +1780,10 @@ static struct video_device cafe_v4l_template = {
.release = cafe_v4l_dev_release,
.vidioc_querycap = cafe_vidioc_querycap,
- .vidioc_enum_fmt_cap = cafe_vidioc_enum_fmt_cap,
- .vidioc_try_fmt_cap = cafe_vidioc_try_fmt_cap,
- .vidioc_s_fmt_cap = cafe_vidioc_s_fmt_cap,
- .vidioc_g_fmt_cap = cafe_vidioc_g_fmt_cap,
+ .vidioc_enum_fmt_vid_cap = cafe_vidioc_enum_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = cafe_vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = cafe_vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = cafe_vidioc_g_fmt_vid_cap,
.vidioc_enum_input = cafe_vidioc_enum_input,
.vidioc_g_input = cafe_vidioc_g_input,
.vidioc_s_input = cafe_vidioc_s_input,
diff --git a/drivers/media/video/compat_ioctl32.c b/drivers/media/video/compat_ioctl32.c
index cefd1381e8de..54de0cd482e9 100644
--- a/drivers/media/video/compat_ioctl32.c
+++ b/drivers/media/video/compat_ioctl32.c
@@ -884,6 +884,7 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
case VIDIOC_G_INPUT32:
case VIDIOC_S_INPUT32:
case VIDIOC_TRY_FMT32:
+ case VIDIOC_S_HW_FREQ_SEEK:
ret = do_video_ioctl(file, cmd, arg);
break;
diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c
index 9a26751615c6..8f2959ae7cab 100644
--- a/drivers/media/video/cx18/cx18-av-core.c
+++ b/drivers/media/video/cx18/cx18-av-core.c
@@ -228,7 +228,7 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
if ((vid_input & ~0xff0) ||
luma < CX18_AV_SVIDEO_LUMA1 ||
- luma > CX18_AV_SVIDEO_LUMA4 ||
+ luma > CX18_AV_SVIDEO_LUMA8 ||
chroma < CX18_AV_SVIDEO_CHROMA4 ||
chroma > CX18_AV_SVIDEO_CHROMA8) {
CX18_ERR("0x%04x is not a valid video input!\n",
diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h
index 786901d72e9a..39f3c9397158 100644
--- a/drivers/media/video/cx18/cx18-av-core.h
+++ b/drivers/media/video/cx18/cx18-av-core.h
@@ -37,12 +37,16 @@ enum cx18_av_video_input {
CX18_AV_COMPOSITE7,
CX18_AV_COMPOSITE8,
- /* S-Video inputs consist of one luma input (In1-In4) ORed with one
+ /* S-Video inputs consist of one luma input (In1-In8) ORed with one
chroma input (In5-In8) */
CX18_AV_SVIDEO_LUMA1 = 0x10,
CX18_AV_SVIDEO_LUMA2 = 0x20,
CX18_AV_SVIDEO_LUMA3 = 0x30,
CX18_AV_SVIDEO_LUMA4 = 0x40,
+ CX18_AV_SVIDEO_LUMA5 = 0x50,
+ CX18_AV_SVIDEO_LUMA6 = 0x60,
+ CX18_AV_SVIDEO_LUMA7 = 0x70,
+ CX18_AV_SVIDEO_LUMA8 = 0x80,
CX18_AV_SVIDEO_CHROMA4 = 0x400,
CX18_AV_SVIDEO_CHROMA5 = 0x500,
CX18_AV_SVIDEO_CHROMA6 = 0x600,
diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c
index baccd079243d..c35eb53a34c6 100644
--- a/drivers/media/video/cx18/cx18-cards.c
+++ b/drivers/media/video/cx18/cx18-cards.c
@@ -23,6 +23,7 @@
#include "cx18-driver.h"
#include "cx18-cards.h"
+#include "cx18-av-core.h"
#include "cx18-i2c.h"
#include <media/cs5345.h>
@@ -54,22 +55,22 @@ static const struct cx18_card cx18_card_hvr1600_esmt = {
.hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER |
CX18_HW_CS5345 | CX18_HW_DVB,
.video_inputs = {
- { CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 },
- { CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 },
- { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 },
- { CX18_CARD_INPUT_SVIDEO2, 2, CX23418_SVIDEO2 },
- { CX18_CARD_INPUT_COMPOSITE2, 2, CX23418_COMPOSITE4 },
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 },
+ { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 },
+ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 },
+ { CX18_CARD_INPUT_SVIDEO2, 2, CX18_AV_SVIDEO2 },
+ { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE4 },
},
.audio_inputs = {
{ CX18_CARD_INPUT_AUD_TUNER,
- CX23418_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 },
+ CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 },
{ CX18_CARD_INPUT_LINE_IN1,
- CX23418_AUDIO_SERIAL, CS5345_IN_2 },
+ CX18_AV_AUDIO_SERIAL, CS5345_IN_2 },
{ CX18_CARD_INPUT_LINE_IN2,
- CX23418_AUDIO_SERIAL, CS5345_IN_2 },
+ CX18_AV_AUDIO_SERIAL, CS5345_IN_2 },
},
.radio_input = { CX18_CARD_INPUT_AUD_TUNER,
- CX23418_AUDIO_SERIAL, 0 },
+ CX18_AV_AUDIO_SERIAL, 0 },
.ddr = {
/* ESMT M13S128324A-5B memory */
.chip_config = 0x003,
@@ -94,22 +95,22 @@ static const struct cx18_card cx18_card_hvr1600_samsung = {
.hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER |
CX18_HW_CS5345 | CX18_HW_DVB,
.video_inputs = {
- { CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 },
- { CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 },
- { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 },
- { CX18_CARD_INPUT_SVIDEO2, 2, CX23418_SVIDEO2 },
- { CX18_CARD_INPUT_COMPOSITE2, 2, CX23418_COMPOSITE4 },
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 },
+ { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 },
+ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 },
+ { CX18_CARD_INPUT_SVIDEO2, 2, CX18_AV_SVIDEO2 },
+ { CX18_CARD_INPUT_COMPOSITE2, 2, CX18_AV_COMPOSITE4 },
},
.audio_inputs = {
{ CX18_CARD_INPUT_AUD_TUNER,
- CX23418_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 },
+ CX18_AV_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 },
{ CX18_CARD_INPUT_LINE_IN1,
- CX23418_AUDIO_SERIAL, CS5345_IN_2 },
+ CX18_AV_AUDIO_SERIAL, CS5345_IN_2 },
{ CX18_CARD_INPUT_LINE_IN2,
- CX23418_AUDIO_SERIAL, CS5345_IN_2 },
+ CX18_AV_AUDIO_SERIAL, CS5345_IN_2 },
},
.radio_input = { CX18_CARD_INPUT_AUD_TUNER,
- CX23418_AUDIO_SERIAL, 0 },
+ CX18_AV_AUDIO_SERIAL, 0 },
.ddr = {
/* Samsung K4D263238G-VC33 memory */
.chip_config = 0x003,
@@ -141,19 +142,19 @@ static const struct cx18_card cx18_card_h900 = {
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_all = CX18_HW_TUNER,
.video_inputs = {
- { CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE2 },
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
{ CX18_CARD_INPUT_SVIDEO1, 1,
- CX23418_SVIDEO_LUMA3 | CX23418_SVIDEO_CHROMA4 },
- { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE1 },
+ CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 },
+ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE1 },
},
.audio_inputs = {
{ CX18_CARD_INPUT_AUD_TUNER,
- CX23418_AUDIO8, 0 },
+ CX18_AV_AUDIO8, 0 },
{ CX18_CARD_INPUT_LINE_IN1,
- CX23418_AUDIO_SERIAL, 0 },
+ CX18_AV_AUDIO_SERIAL, 0 },
},
.radio_input = { CX18_CARD_INPUT_AUD_TUNER,
- CX23418_AUDIO_SERIAL, 0 },
+ CX18_AV_AUDIO_SERIAL, 0 },
.tuners = {
{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
},
@@ -188,18 +189,18 @@ static const struct cx18_card cx18_card_mpc718 = {
.hw_audio_ctrl = CX18_HW_CX23418,
.hw_all = CX18_HW_TUNER,
.video_inputs = {
- { CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 },
- { CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 },
- { CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 },
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 },
+ { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 },
+ { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE3 },
},
.audio_inputs = {
{ CX18_CARD_INPUT_AUD_TUNER,
- CX23418_AUDIO8, 0 },
+ CX18_AV_AUDIO8, 0 },
{ CX18_CARD_INPUT_LINE_IN1,
- CX23418_AUDIO_SERIAL, 0 },
+ CX18_AV_AUDIO_SERIAL, 0 },
},
.radio_input = { CX18_CARD_INPUT_AUD_TUNER,
- CX23418_AUDIO_SERIAL, 0 },
+ CX18_AV_AUDIO_SERIAL, 0 },
.tuners = {
/* XC3028 tuner */
{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h
index bccb67f0db16..3c2c0b92956c 100644
--- a/drivers/media/video/cx18/cx18-cards.h
+++ b/drivers/media/video/cx18/cx18-cards.h
@@ -36,36 +36,6 @@
#define CX18_CARD_INPUT_COMPOSITE2 5
#define CX18_CARD_INPUT_COMPOSITE3 6
-enum cx34180_video_input {
- /* Composite video inputs In1-In8 */
- CX23418_COMPOSITE1 = 1,
- CX23418_COMPOSITE2,
- CX23418_COMPOSITE3,
- CX23418_COMPOSITE4,
- CX23418_COMPOSITE5,
- CX23418_COMPOSITE6,
- CX23418_COMPOSITE7,
- CX23418_COMPOSITE8,
-
- /* S-Video inputs consist of one luma input (In1-In4) ORed with one
- chroma input (In5-In8) */
- CX23418_SVIDEO_LUMA1 = 0x10,
- CX23418_SVIDEO_LUMA2 = 0x20,
- CX23418_SVIDEO_LUMA3 = 0x30,
- CX23418_SVIDEO_LUMA4 = 0x40,
- CX23418_SVIDEO_CHROMA4 = 0x400,
- CX23418_SVIDEO_CHROMA5 = 0x500,
- CX23418_SVIDEO_CHROMA6 = 0x600,
- CX23418_SVIDEO_CHROMA7 = 0x700,
- CX23418_SVIDEO_CHROMA8 = 0x800,
-
- /* S-Video aliases for common luma/chroma combinations */
- CX23418_SVIDEO1 = 0x510,
- CX23418_SVIDEO2 = 0x620,
- CX23418_SVIDEO3 = 0x730,
- CX23418_SVIDEO4 = 0x840,
-};
-
/* audio inputs */
#define CX18_CARD_INPUT_AUD_TUNER 1
#define CX18_CARD_INPUT_LINE_IN1 2
@@ -75,16 +45,6 @@ enum cx34180_video_input {
#define CX18_CARD_MAX_AUDIO_INPUTS 3
#define CX18_CARD_MAX_TUNERS 2
-enum cx23418_audio_input {
- /* Audio inputs: serial or In4-In8 */
- CX23418_AUDIO_SERIAL,
- CX23418_AUDIO4 = 4,
- CX23418_AUDIO5,
- CX23418_AUDIO6,
- CX23418_AUDIO7,
- CX23418_AUDIO8,
-};
-
/* V4L2 capability aliases */
#define CX18_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \
V4L2_CAP_AUDIO | V4L2_CAP_READWRITE)
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index 2b810bb2a4c7..d9178843e8d2 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -614,7 +614,7 @@ static int __devinit cx18_probe(struct pci_dev *dev,
cx18_cards[cx18_cards_active] = cx;
cx->dev = dev;
cx->num = cx18_cards_active++;
- snprintf(cx->name, sizeof(cx->name) - 1, "cx18-%d", cx->num);
+ snprintf(cx->name, sizeof(cx->name), "cx18-%d", cx->num);
CX18_INFO("Initializing card #%d\n", cx->num);
spin_unlock(&cx18_cards_lock);
diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
index 20e05f230546..fd7112c11d35 100644
--- a/drivers/media/video/cx23885/cx23885-cards.c
+++ b/drivers/media/video/cx23885/cx23885-cards.c
@@ -143,6 +143,10 @@ struct cx23885_board cx23885_boards[] = {
.name = "Hauppauge WinTV-HVR1400",
.portc = CX23885_MPEG_DVB,
},
+ [CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP] = {
+ .name = "DViCO FusionHDTV7 Dual Express",
+ .portc = CX23885_MPEG_DVB,
+ },
};
const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
@@ -210,6 +214,10 @@ struct cx23885_subid cx23885_subids[] = {
.subvendor = 0x0070,
.subdevice = 0x8010,
.card = CX23885_BOARD_HAUPPAUGE_HVR1400,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xd618,
+ .card = CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP,
},
};
const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
@@ -477,6 +485,11 @@ void cx23885_card_setup(struct cx23885_dev *dev)
}
switch (dev->board) {
+ case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
+ ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
+ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ /* break omitted intentionally */
case CX23885_BOARD_DVICO_FUSIONHDTV_5_EXP:
ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c
index 022aa391937a..51dedb182c99 100644
--- a/drivers/media/video/cx23885/cx23885-dvb.c
+++ b/drivers/media/video/cx23885/cx23885-dvb.c
@@ -164,12 +164,28 @@ static struct s5h1409_config hauppauge_hvr1500q_config = {
.mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
};
+static struct s5h1409_config dvico_s5h1409_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_SERIAL_OUTPUT,
+ .gpio = S5H1409_GPIO_ON,
+ .qam_if = 44000,
+ .inversion = S5H1409_INVERSION_OFF,
+ .status_mode = S5H1409_DEMODLOCKING,
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
static struct xc5000_config hauppauge_hvr1500q_tunerconfig = {
.i2c_address = 0x61,
.if_khz = 5380,
.tuner_callback = cx23885_tuner_callback
};
+static struct xc5000_config dvico_xc5000_tunerconfig = {
+ .i2c_address = 0x64,
+ .if_khz = 5380,
+ .tuner_callback = cx23885_tuner_callback
+};
+
static struct tda829x_config tda829x_no_probe = {
.probe_tuner = TDA829X_DONT_PROBE,
};
@@ -453,6 +469,17 @@ static int dvb_register(struct cx23885_tsport *port)
fe->ops.tuner_ops.set_config(fe, &ctl);
}
break;
+ case CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP:
+ i2c_bus = &dev->i2c_bus[port->nr - 1];
+
+ port->dvb.frontend = dvb_attach(s5h1409_attach,
+ &dvico_s5h1409_config,
+ &i2c_bus->i2c_adap);
+ if (port->dvb.frontend != NULL)
+ dvb_attach(xc5000_attach, port->dvb.frontend,
+ &i2c_bus->i2c_adap,
+ &dvico_xc5000_tunerconfig, i2c_bus);
+ break;
default:
printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n",
dev->name);
diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c
index 84652210a28c..043fc4e5c586 100644
--- a/drivers/media/video/cx23885/cx23885-video.c
+++ b/drivers/media/video/cx23885/cx23885-video.c
@@ -915,7 +915,7 @@ static void init_controls(struct cx23885_dev *dev)
/* ------------------------------------------------------------------ */
/* VIDEO IOCTLS */
-static int vidioc_g_fmt_cap(struct file *file, void *priv,
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct cx23885_fh *fh = priv;
@@ -932,7 +932,7 @@ static int vidioc_g_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int vidioc_try_fmt_cap(struct file *file, void *priv,
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
@@ -983,7 +983,7 @@ static int vidioc_try_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int vidioc_s_fmt_cap(struct file *file, void *priv,
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct cx23885_fh *fh = priv;
@@ -991,7 +991,7 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv,
int err;
dprintk(2, "%s()\n", __func__);
- err = vidioc_try_fmt_cap(file, priv, f);
+ err = vidioc_try_fmt_vid_cap(file, priv, f);
if (0 != err)
return err;
@@ -1025,7 +1025,7 @@ static int vidioc_querycap(struct file *file, void *priv,
return 0;
}
-static int vidioc_enum_fmt_cap(struct file *file, void *priv,
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
if (unlikely(f->index >= ARRAY_SIZE(formats)))
@@ -1440,13 +1440,13 @@ static struct video_device cx23885_video_template = {
.fops = &video_fops,
.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_g_fmt_vbi = cx23885_vbi_fmt,
- .vidioc_try_fmt_vbi = cx23885_vbi_fmt,
- .vidioc_s_fmt_vbi = cx23885_vbi_fmt,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vbi_cap = cx23885_vbi_fmt,
+ .vidioc_try_fmt_vbi_cap = cx23885_vbi_fmt,
+ .vidioc_s_fmt_vbi_cap = cx23885_vbi_fmt,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
index 32af87f25e7b..00dfdc89d641 100644
--- a/drivers/media/video/cx23885/cx23885.h
+++ b/drivers/media/video/cx23885/cx23885.h
@@ -63,6 +63,7 @@
#define CX23885_BOARD_HAUPPAUGE_HVR1200 7
#define CX23885_BOARD_HAUPPAUGE_HVR1700 8
#define CX23885_BOARD_HAUPPAUGE_HVR1400 9
+#define CX23885_BOARD_DVICO_FUSIONHDTV_7_DUAL_EXP 10
/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */
#define CX23885_NORMS (\
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index 607efdcd22f8..1da6f134888d 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -433,7 +433,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
int chroma = vid_input & 0xf00;
if ((vid_input & ~0xff0) ||
- luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA4 ||
+ luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA8 ||
chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) {
v4l_err(client, "0x%04x is not a valid video input!\n",
vid_input);
diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c
index 620d295947ab..8d489a4b9570 100644
--- a/drivers/media/video/cx25840/cx25840-firmware.c
+++ b/drivers/media/video/cx25840/cx25840-firmware.c
@@ -79,7 +79,7 @@ static int check_fw_load(struct i2c_client *client, int size)
return 0;
}
-static int fw_write(struct i2c_client *client, u8 *data, int size)
+static int fw_write(struct i2c_client *client, const u8 *data, int size)
{
if (i2c_master_send(client, data, size) < size) {
v4l_err(client, "firmware load i2c failure\n");
@@ -93,7 +93,8 @@ int cx25840_loadfw(struct i2c_client *client)
{
struct cx25840_state *state = i2c_get_clientdata(client);
const struct firmware *fw = NULL;
- u8 buffer[4], *ptr;
+ u8 buffer[FWSEND];
+ const u8 *ptr;
int size, retval;
if (state->is_cx23885)
@@ -108,29 +109,23 @@ int cx25840_loadfw(struct i2c_client *client)
buffer[0] = 0x08;
buffer[1] = 0x02;
- buffer[2] = fw->data[0];
- buffer[3] = fw->data[1];
- retval = fw_write(client, buffer, 4);
- if (retval < 0) {
- release_firmware(fw);
- return retval;
- }
-
- size = fw->size - 2;
+ size = fw->size;
ptr = fw->data;
while (size > 0) {
- ptr[0] = 0x08;
- ptr[1] = 0x02;
- retval = fw_write(client, ptr, min(FWSEND, size + 2));
+ int len = min(FWSEND - 2, size);
+
+ memcpy(buffer + 2, ptr, len);
+
+ retval = fw_write(client, buffer, len + 2);
if (retval < 0) {
release_firmware(fw);
return retval;
}
- size -= FWSEND - 2;
- ptr += FWSEND - 2;
+ size -= len;
+ ptr += len;
}
end_fw_load(client);
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c
index 6c0c94c5ef91..fb67e4188b2f 100644
--- a/drivers/media/video/cx88/cx88-blackbird.c
+++ b/drivers/media/video/cx88/cx88-blackbird.c
@@ -737,7 +737,7 @@ static int vidioc_querycap (struct file *file, void *priv,
return 0;
}
-static int vidioc_enum_fmt_cap (struct file *file, void *priv,
+static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
if (f->index != 0)
@@ -749,7 +749,7 @@ static int vidioc_enum_fmt_cap (struct file *file, void *priv,
return 0;
}
-static int vidioc_g_fmt_cap (struct file *file, void *priv,
+static int vidioc_g_fmt_vid_cap (struct file *file, void *priv,
struct v4l2_format *f)
{
struct cx8802_fh *fh = priv;
@@ -768,7 +768,7 @@ static int vidioc_g_fmt_cap (struct file *file, void *priv,
return 0;
}
-static int vidioc_try_fmt_cap (struct file *file, void *priv,
+static int vidioc_try_fmt_vid_cap (struct file *file, void *priv,
struct v4l2_format *f)
{
struct cx8802_fh *fh = priv;
@@ -784,7 +784,7 @@ static int vidioc_try_fmt_cap (struct file *file, void *priv,
return 0;
}
-static int vidioc_s_fmt_cap (struct file *file, void *priv,
+static int vidioc_s_fmt_vid_cap (struct file *file, void *priv,
struct v4l2_format *f)
{
struct cx8802_fh *fh = priv;
@@ -1181,10 +1181,10 @@ static struct video_device cx8802_mpeg_template =
.minor = -1,
.vidioc_querymenu = vidioc_querymenu,
.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_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c
index eea23f95edb7..0fed5cd2ccea 100644
--- a/drivers/media/video/cx88/cx88-video.c
+++ b/drivers/media/video/cx88/cx88-video.c
@@ -1045,7 +1045,7 @@ static void init_controls(struct cx88_core *core)
/* ------------------------------------------------------------------ */
/* VIDEO IOCTLS */
-static int vidioc_g_fmt_cap (struct file *file, void *priv,
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct cx8800_fh *fh = priv;
@@ -1061,7 +1061,7 @@ static int vidioc_g_fmt_cap (struct file *file, void *priv,
return 0;
}
-static int vidioc_try_fmt_cap (struct file *file, void *priv,
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
@@ -1112,11 +1112,11 @@ static int vidioc_try_fmt_cap (struct file *file, void *priv,
return 0;
}
-static int vidioc_s_fmt_cap (struct file *file, void *priv,
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct cx8800_fh *fh = priv;
- int err = vidioc_try_fmt_cap (file,priv,f);
+ int err = vidioc_try_fmt_vid_cap (file,priv,f);
if (0 != err)
return err;
@@ -1147,7 +1147,7 @@ static int vidioc_querycap (struct file *file, void *priv,
return 0;
}
-static int vidioc_enum_fmt_cap (struct file *file, void *priv,
+static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
if (unlikely(f->index >= ARRAY_SIZE(formats)))
@@ -1690,13 +1690,13 @@ static struct video_device cx8800_video_template =
.fops = &video_fops,
.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_g_fmt_vbi = cx8800_vbi_fmt,
- .vidioc_try_fmt_vbi = cx8800_vbi_fmt,
- .vidioc_s_fmt_vbi = cx8800_vbi_fmt,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vbi_cap = cx8800_vbi_fmt,
+ .vidioc_try_fmt_vbi_cap = cx8800_vbi_fmt,
+ .vidioc_s_fmt_vbi_cap = cx8800_vbi_fmt,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c
index 92b2a6db4fdc..343dff0f8a04 100644
--- a/drivers/media/video/em28xx/em28xx-audio.c
+++ b/drivers/media/video/em28xx/em28xx-audio.c
@@ -415,6 +415,12 @@ static int em28xx_audio_init(struct em28xx *dev)
static int devnr;
int ret, err;
+ if (dev->has_audio_class) {
+ /* This device does not support the extension (in this case
+ the device is expecting the snd-usb-audio module */
+ return 0;
+ }
+
printk(KERN_INFO "em28xx-audio.c: probing for em28x1 "
"non standard usbaudio\n");
printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus "
@@ -458,6 +464,12 @@ static int em28xx_audio_fini(struct em28xx *dev)
if (dev == NULL)
return 0;
+ if (dev->has_audio_class) {
+ /* This device does not support the extension (in this case
+ the device is expecting the snd-usb-audio module */
+ return 0;
+ }
+
if (dev->adev) {
snd_card_free(dev->adev->sndcard);
kfree(dev->adev);
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index 3e4f3c7e92e7..938c51e1c86d 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -157,6 +157,28 @@ struct em28xx_board em28xx_boards[] = {
.tda9887_conf = TDA9887_PRESENT,
.tuner_type = TUNER_XC2028,
.mts_firmware = 1,
+ .has_dvb = 1,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = 0,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = 1,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = 1,
+ } },
+ },
+ [EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2] = {
+ .name = "Hauppauge WinTV HVR 900 (R2)",
+ .vchannels = 3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_XC2028,
+ .mts_firmware = 1,
.decoder = EM28XX_TVP5150,
.input = { {
.type = EM28XX_VMUX_TELEVISION,
@@ -195,6 +217,29 @@ struct em28xx_board em28xx_boards[] = {
.amux = 1,
} },
},
+ [EM2880_BOARD_PINNACLE_PCTV_HD_PRO] = {
+ .name = "Pinnacle PCTV HD Pro Stick",
+ .vchannels = 3,
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_XC2028,
+ .mts_firmware = 1,
+ .has_12mhz_i2s = 1,
+ .has_dvb = 1,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+ .amux = 0,
+ }, {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = TVP5150_COMPOSITE1,
+ .amux = 1,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = 1,
+ } },
+ },
[EM2880_BOARD_TERRATEC_HYBRID_XS] = {
.name = "Terratec Hybrid XS",
.vchannels = 3,
@@ -416,10 +461,12 @@ struct usb_device_id em28xx_id_table [] = {
.driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
{ USB_DEVICE(0x2304, 0x021a),
.driver_info = EM2820_BOARD_PINNACLE_DVC_90 },
+ { USB_DEVICE(0x2304, 0x0227),
+ .driver_info = EM2880_BOARD_PINNACLE_PCTV_HD_PRO },
{ USB_DEVICE(0x2040, 0x6500),
.driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 },
{ USB_DEVICE(0x2040, 0x6502),
- .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900 },
+ .driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2 },
{ USB_DEVICE(0x2040, 0x6513), /* HCW HVR-980 */
.driver_info = EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 },
{ USB_DEVICE(0x2040, 0x6517), /* HP HVR-950 */
@@ -524,6 +571,9 @@ void em28xx_pre_card_setup(struct em28xx *dev)
rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID);
if (rc > 0) {
switch (rc) {
+ case CHIP_ID_EM2860:
+ em28xx_info("chip ID is em2860\n");
+ break;
case CHIP_ID_EM2883:
em28xx_info("chip ID is em2882/em2883\n");
dev->wait_after_write = 0;
@@ -538,8 +588,10 @@ void em28xx_pre_card_setup(struct em28xx *dev)
switch (dev->model) {
case EM2880_BOARD_TERRATEC_PRODIGY_XS:
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
case EM2880_BOARD_TERRATEC_HYBRID_XS:
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950:
+ case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x27", 1);
em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
msleep(50);
@@ -572,7 +624,12 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
ctl->demod = XC3028_FE_ZARLINK456;
break;
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
+ /* djh - Not sure which demod we need here */
+ ctl->demod = XC3028_FE_DEFAULT;
+ break;
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950:
+ case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
/* FIXME: Better to specify the needed IF */
ctl->demod = XC3028_FE_DEFAULT;
break;
@@ -750,6 +807,7 @@ void em28xx_card_setup(struct em28xx *dev)
switch (dev->model) {
case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2:
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900:
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950:
{
struct tveeprom tv;
diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
index 8cf4983f0039..3fef70818dbe 100644
--- a/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/drivers/media/video/em28xx/em28xx-dvb.c
@@ -5,6 +5,7 @@
(c) 2008 Devin Heitmueller <devin.heitmueller@gmail.com>
- Fixes for the driver to properly work with HVR-950
+ - Fixes for the driver to properly work with Pinnacle PCTV HD Pro Stick
(c) 2008 Aidan Thornton <makosoft@googlemail.com>
@@ -26,6 +27,7 @@
#include "lgdt330x.h"
#include "zl10353.h"
+#include "drx397xD.h"
MODULE_DESCRIPTION("driver for em28xx based DVB cards");
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
@@ -227,6 +229,13 @@ static struct zl10353_config em28xx_zl10353_with_xc3028 = {
.if2 = 45600,
};
+#ifdef EM28XX_DRX397XD_SUPPORT
+/* [TODO] djh - not sure yet what the device config needs to contain */
+static struct drx397xD_config em28xx_drx397xD_with_xc3028 = {
+ .demod_address = (0xe0 >> 1),
+};
+#endif
+
/* ------------------------------------------------------------------ */
static int attach_xc3028(u8 addr, struct em28xx *dev)
@@ -382,6 +391,11 @@ static int dvb_init(struct em28xx *dev)
int result = 0;
struct em28xx_dvb *dvb;
+ if (!dev->has_dvb) {
+ /* This device does not support the extension */
+ return 0;
+ }
+
dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL);
if (dvb == NULL) {
@@ -394,6 +408,7 @@ static int dvb_init(struct em28xx *dev)
/* init frontend */
switch (dev->model) {
case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950:
+ case EM2880_BOARD_PINNACLE_PCTV_HD_PRO:
dvb->frontend = dvb_attach(lgdt330x_attach,
&em2880_lgdt3303_dev,
&dev->i2c_adap);
@@ -411,6 +426,19 @@ static int dvb_init(struct em28xx *dev)
goto out_free;
}
break;
+ case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2:
+#ifdef EM28XX_DRX397XD_SUPPORT
+ /* We don't have the config structure properly populated, so
+ this is commented out for now */
+ dvb->frontend = dvb_attach(drx397xD_attach,
+ &em28xx_drx397xD_with_xc3028,
+ &dev->i2c_adap);
+ if (attach_xc3028(0x61, dev) < 0) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ break;
+#endif
default:
printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card"
" isn't supported yet\n",
@@ -444,6 +472,11 @@ out_free:
static int dvb_fini(struct em28xx *dev)
{
+ if (!dev->has_dvb) {
+ /* This device does not support the extension */
+ return 0;
+ }
+
if (dev->dvb) {
unregister_dvb(dev->dvb);
dev->dvb = NULL;
diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h
index 9058bed07953..fac1ab23f621 100644
--- a/drivers/media/video/em28xx/em28xx-reg.h
+++ b/drivers/media/video/em28xx/em28xx-reg.h
@@ -84,5 +84,6 @@
/* FIXME: Need to be populated with the other chip ID's */
enum em28xx_chip_id {
+ CHIP_ID_EM2860 = 34,
CHIP_ID_EM2883 = 36,
};
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index fb163ecd9216..1c3e8b6291ea 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -683,7 +683,7 @@ static void get_scale(struct em28xx *dev,
IOCTL vidioc handling
------------------------------------------------------------------*/
-static int vidioc_g_fmt_cap(struct file *file, void *priv,
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct em28xx_fh *fh = priv;
@@ -706,7 +706,7 @@ static int vidioc_g_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int vidioc_try_fmt_cap(struct file *file, void *priv,
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct em28xx_fh *fh = priv;
@@ -766,7 +766,7 @@ static int vidioc_try_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int vidioc_s_fmt_cap(struct file *file, void *priv,
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct em28xx_fh *fh = priv;
@@ -777,7 +777,7 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv,
if (rc < 0)
return rc;
- vidioc_try_fmt_cap(file, priv, f);
+ vidioc_try_fmt_vid_cap(file, priv, f);
mutex_lock(&dev->lock);
@@ -826,7 +826,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm)
/* Adjusts width/height, if needed */
f.fmt.pix.width = dev->width;
f.fmt.pix.height = dev->height;
- vidioc_try_fmt_cap(file, priv, &f);
+ vidioc_try_fmt_vid_cap(file, priv, &f);
mutex_lock(&dev->lock);
@@ -1277,7 +1277,7 @@ static int vidioc_querycap(struct file *file, void *priv,
return 0;
}
-static int vidioc_enum_fmt_cap(struct file *file, void *priv,
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *fmtd)
{
if (fmtd->index != 0)
@@ -1292,7 +1292,7 @@ static int vidioc_enum_fmt_cap(struct file *file, void *priv,
}
/* Sliced VBI ioctls */
-static int vidioc_g_fmt_vbi_capture(struct file *file, void *priv,
+static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct em28xx_fh *fh = priv;
@@ -1316,7 +1316,7 @@ static int vidioc_g_fmt_vbi_capture(struct file *file, void *priv,
return rc;
}
-static int vidioc_try_set_vbi_capture(struct file *file, void *priv,
+static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct em28xx_fh *fh = priv;
@@ -1776,17 +1776,17 @@ static const struct video_device em28xx_video_template = {
.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_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_g_audio = vidioc_g_audio,
.vidioc_s_audio = vidioc_s_audio,
.vidioc_cropcap = vidioc_cropcap,
- .vidioc_g_fmt_vbi_capture = vidioc_g_fmt_vbi_capture,
- .vidioc_try_fmt_vbi_capture = vidioc_try_set_vbi_capture,
- .vidioc_s_fmt_vbi_capture = vidioc_try_set_vbi_capture,
+ .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap,
+ .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap,
+ .vidioc_s_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
@@ -1848,32 +1848,28 @@ static DEFINE_MUTEX(em28xx_extension_devlist_lock);
int em28xx_register_extension(struct em28xx_ops *ops)
{
- struct em28xx *h, *dev = NULL;
-
- list_for_each_entry(h, &em28xx_devlist, devlist)
- dev = h;
+ struct em28xx *dev = NULL;
mutex_lock(&em28xx_extension_devlist_lock);
list_add_tail(&ops->next, &em28xx_extension_devlist);
- if (dev)
- ops->init(dev);
-
+ list_for_each_entry(dev, &em28xx_devlist, devlist) {
+ if (dev)
+ ops->init(dev);
+ }
printk(KERN_INFO "Em28xx: Initialized (%s) extension\n", ops->name);
mutex_unlock(&em28xx_extension_devlist_lock);
-
return 0;
}
EXPORT_SYMBOL(em28xx_register_extension);
void em28xx_unregister_extension(struct em28xx_ops *ops)
{
- struct em28xx *h, *dev = NULL;
-
- list_for_each_entry(h, &em28xx_devlist, devlist)
- dev = h;
+ struct em28xx *dev = NULL;
- if (dev)
- ops->fini(dev);
+ list_for_each_entry(dev, &em28xx_devlist, devlist) {
+ if (dev)
+ ops->fini(dev);
+ }
mutex_lock(&em28xx_extension_devlist_lock);
printk(KERN_INFO "Em28xx: Removed (%s) extension\n", ops->name);
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index 002f170b211a..cff618b2d13c 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -55,6 +55,8 @@
#define EM2820_BOARD_PROLINK_PLAYTV_USB2 14
#define EM2800_BOARD_VGEAR_POCKETTV 15
#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_950 16
+#define EM2880_BOARD_PINNACLE_PCTV_HD_PRO 17
+#define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2 18
/* Limits minimum and default number of buffers */
#define EM28XX_MIN_BUF 4
diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c
index 4fb8faefe2ce..dc3fbb320684 100644
--- a/drivers/media/video/ivtv/ivtv-cards.c
+++ b/drivers/media/video/ivtv/ivtv-cards.c
@@ -923,7 +923,6 @@ static const struct ivtv_card ivtv_card_club3d = {
{ IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL },
},
.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
- .gpio_init = { .direction = 0x1000, .initial_value = 0x1000 }, /* tuner reset */
.xceive_pin = 12,
.tuners = {
{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
@@ -959,7 +958,7 @@ static const struct ivtv_card ivtv_card_avertv_mce116 = {
{ IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 },
},
/* enable line-in */
- .gpio_init = { .direction = 0xe400, .initial_value = 0x4400 },
+ .gpio_init = { .direction = 0xe000, .initial_value = 0x4000 },
.xceive_pin = 10,
.tuners = {
{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
@@ -1102,7 +1101,7 @@ static const struct ivtv_card ivtv_card_aver_m104 = {
},
.radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 },
/* enable line-in + reset tuner */
- .gpio_init = { .direction = 0xe400, .initial_value = 0x4000 },
+ .gpio_init = { .direction = 0xe000, .initial_value = 0x4000 },
.xceive_pin = 10,
.tuners = {
{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
@@ -1111,6 +1110,41 @@ static const struct ivtv_card ivtv_card_aver_m104 = {
.i2c = &ivtv_i2c_std,
};
+/* ------------------------------------------------------------------------- */
+
+/* Buffalo PC-MV5L/PCI cards */
+
+static const struct ivtv_card_pci_info ivtv_pci_buffalo[] = {
+ { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_MELCO, 0x052b },
+ { 0, 0, 0 }
+};
+
+static const struct ivtv_card ivtv_card_buffalo = {
+ .type = IVTV_CARD_BUFFALO_MV5L,
+ .name = "Buffalo PC-MV5L/PCI",
+ .v4l2_capabilities = IVTV_CAP_ENCODER,
+ .hw_video = IVTV_HW_CX25840,
+ .hw_audio = IVTV_HW_CX25840,
+ .hw_audio_ctrl = IVTV_HW_CX25840,
+ .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER,
+ .video_inputs = {
+ { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 },
+ { IVTV_CARD_INPUT_SVIDEO1, 1,
+ CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 },
+ { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 },
+ },
+ .audio_inputs = {
+ { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 },
+ { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL },
+ },
+ .xceive_pin = 12,
+ .tuners = {
+ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
+ },
+ .pci_list = ivtv_pci_buffalo,
+ .i2c = &ivtv_i2c_std,
+};
+
static const struct ivtv_card *ivtv_card_list[] = {
&ivtv_card_pvr250,
&ivtv_card_pvr350,
@@ -1137,6 +1171,7 @@ static const struct ivtv_card *ivtv_card_list[] = {
&ivtv_card_aver_pvr150,
&ivtv_card_aver_ezmaker,
&ivtv_card_aver_m104,
+ &ivtv_card_buffalo,
/* Variations of standard cards but with the same PCI IDs.
These cards must come last in this list. */
diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h
index 748485dcebbd..381af1bceef8 100644
--- a/drivers/media/video/ivtv/ivtv-cards.h
+++ b/drivers/media/video/ivtv/ivtv-cards.h
@@ -49,7 +49,8 @@
#define IVTV_CARD_AVER_PVR150PLUS 22 /* AVerMedia PVR-150 Plus */
#define IVTV_CARD_AVER_EZMAKER 23 /* AVerMedia EZMaker PCI Deluxe */
#define IVTV_CARD_AVER_M104 24 /* AverMedia M104 miniPCI card */
-#define IVTV_CARD_LAST 24
+#define IVTV_CARD_BUFFALO_MV5L 25 /* Buffalo PC-MV5L/PCI card */
+#define IVTV_CARD_LAST 25
/* Variants of existing cards but with the same PCI IDs. The driver
detects these based on other device information.
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
index 797e636771da..323dd68fa1c6 100644
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -191,6 +191,7 @@ MODULE_PARM_DESC(cardtype,
"\t\t\t23 = AverMedia PVR-150 Plus\n"
"\t\t\t24 = AverMedia EZMaker PCI Deluxe\n"
"\t\t\t25 = AverMedia M104 (not yet working)\n"
+ "\t\t\t26 = Buffalo PC-MV5L/PCI\n"
"\t\t\t 0 = Autodetect (default)\n"
"\t\t\t-1 = Ignore this card\n\t\t");
MODULE_PARM_DESC(pal, "Set PAL standard: BGH, DK, I, M, N, Nc, 60");
@@ -1019,7 +1020,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
ivtv_cards[ivtv_cards_active] = itv;
itv->dev = dev;
itv->num = ivtv_cards_active++;
- snprintf(itv->name, sizeof(itv->name) - 1, "ivtv%d", itv->num);
+ snprintf(itv->name, sizeof(itv->name), "ivtv%d", itv->num);
IVTV_INFO("Initializing card #%d\n", itv->num);
spin_unlock(&ivtv_cards_lock);
diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c
index d8ac09f3cce6..bc22905ea20f 100644
--- a/drivers/media/video/ivtv/ivtv-gpio.c
+++ b/drivers/media/video/ivtv/ivtv-gpio.c
@@ -146,15 +146,20 @@ int ivtv_reset_tuner_gpio(void *dev, int cmd, int value)
void ivtv_gpio_init(struct ivtv *itv)
{
- if (itv->card->gpio_init.direction == 0)
+ u16 pin = 0;
+
+ if (itv->card->xceive_pin)
+ pin = 1 << itv->card->xceive_pin;
+
+ if ((itv->card->gpio_init.direction | pin) == 0)
return;
IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n",
read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT));
/* init output data then direction */
- write_reg(itv->card->gpio_init.initial_value, IVTV_REG_GPIO_OUT);
- write_reg(itv->card->gpio_init.direction, IVTV_REG_GPIO_DIR);
+ write_reg(itv->card->gpio_init.initial_value | pin, IVTV_REG_GPIO_OUT);
+ write_reg(itv->card->gpio_init.direction | pin, IVTV_REG_GPIO_DIR);
}
static struct v4l2_queryctrl gpio_ctrl_mute = {
diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c
index 73be154f7f05..e8dbee443946 100644
--- a/drivers/media/video/ivtv/ivtvfb.c
+++ b/drivers/media/video/ivtv/ivtvfb.c
@@ -367,6 +367,87 @@ static int ivtvfb_prep_frame(struct ivtv *itv, int cmd, void __user *source,
return ivtvfb_prep_dec_dma_to_device(itv, dest_offset, source, count);
}
+static ssize_t ivtvfb_write(struct fb_info *info, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ unsigned long p = *ppos;
+ void *dst;
+ int err = 0;
+ unsigned long total_size;
+ struct ivtv *itv = (struct ivtv *) info->par;
+ unsigned long dma_offset =
+ IVTV_DECODER_OFFSET + itv->osd_info->video_rbase;
+ unsigned long dma_size;
+ u16 lead = 0, tail = 0;
+
+ if (info->state != FBINFO_STATE_RUNNING)
+ return -EPERM;
+
+ total_size = info->screen_size;
+
+ if (total_size == 0)
+ total_size = info->fix.smem_len;
+
+ if (p > total_size)
+ return -EFBIG;
+
+ if (count > total_size) {
+ err = -EFBIG;
+ count = total_size;
+ }
+
+ if (count + p > total_size) {
+ if (!err)
+ err = -ENOSPC;
+
+ count = total_size - p;
+ }
+
+ dst = (void __force *) (info->screen_base + p);
+
+ if (info->fbops->fb_sync)
+ info->fbops->fb_sync(info);
+
+ if (!access_ok(VERIFY_READ, buf, count)) {
+ IVTVFB_WARN("Invalid userspace pointer 0x%08lx\n",
+ (unsigned long)buf);
+ err = -EFAULT;
+ }
+
+ if (!err) {
+ /* If transfer size > threshold and both src/dst
+ addresses are aligned, use DMA */
+ if (count >= 4096 && ((u32)buf & 3) == ((u32)dst & 3)) {
+ /* Odd address = can't DMA. Align */
+ if ((u32)dst & 3) {
+ lead = 4 - ((u32)dst & 3);
+ memcpy(dst, buf, lead);
+ buf += lead;
+ dst += lead;
+ }
+ /* DMA resolution is 32 bits */
+ if ((count - lead) & 3)
+ tail = (count - lead) & 3;
+ /* DMA the data */
+ dma_size = count - lead - tail;
+ err = ivtvfb_prep_dec_dma_to_device(itv,
+ p + lead + dma_offset, (void *)buf, dma_size);
+ dst += dma_size;
+ buf += dma_size;
+ /* Copy any leftover data */
+ if (tail)
+ memcpy(dst, buf, tail);
+ } else {
+ memcpy(dst, buf, count);
+ }
+ }
+
+ if (!err)
+ *ppos += count;
+
+ return (err) ? err : count;
+}
+
static int ivtvfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
{
DEFINE_WAIT(wait);
@@ -824,6 +905,7 @@ static int ivtvfb_blank(int blank_mode, struct fb_info *info)
static struct fb_ops ivtvfb_ops = {
.owner = THIS_MODULE,
+ .fb_write = ivtvfb_write,
.fb_check_var = ivtvfb_check_var,
.fb_set_par = ivtvfb_set_par,
.fb_setcolreg = ivtvfb_setcolreg,
diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c
index e7ccbc895d7a..2fb5854cf6f0 100644
--- a/drivers/media/video/meye.c
+++ b/drivers/media/video/meye.c
@@ -1253,7 +1253,7 @@ static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c)
return 0;
}
-static int vidioc_enum_fmt_cap(struct file *file, void *fh,
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh,
struct v4l2_fmtdesc *f)
{
if (f->index > 1)
@@ -1283,7 +1283,7 @@ static int vidioc_enum_fmt_cap(struct file *file, void *fh,
return 0;
}
-static int vidioc_try_fmt_cap(struct file *file, void *fh,
+static int vidioc_try_fmt_vid_cap(struct file *file, void *fh,
struct v4l2_format *f)
{
if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -1316,7 +1316,8 @@ static int vidioc_try_fmt_cap(struct file *file, void *fh,
return 0;
}
-static int vidioc_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
+static int vidioc_g_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
{
if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
@@ -1346,7 +1347,8 @@ static int vidioc_g_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
return 0;
}
-static int vidioc_s_fmt_cap(struct file *file, void *fh, struct v4l2_format *f)
+static int vidioc_s_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
{
if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
@@ -1709,10 +1711,10 @@ static struct video_device meye_template = {
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_s_ctrl = vidioc_s_ctrl,
.vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap,
- .vidioc_try_fmt_cap = vidioc_try_fmt_cap,
- .vidioc_g_fmt_cap = vidioc_g_fmt_cap,
- .vidioc_s_fmt_cap = vidioc_s_fmt_cap,
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.c b/drivers/media/video/pvrusb2/pvrusb2-audio.c
index 8d859ccd48ec..cdedaa55f152 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-audio.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-audio.c
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.h b/drivers/media/video/pvrusb2/pvrusb2-audio.h
index 536339b68843..ac54eed3721b 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-audio.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-audio.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.c b/drivers/media/video/pvrusb2/pvrusb2-context.c
index 73dcb1c57ae6..7c19ff72e6b3 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-context.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-context.c
@@ -1,5 +1,4 @@
/*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.h b/drivers/media/video/pvrusb2/pvrusb2-context.h
index 745e270233c2..61801291c2af 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-context.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-context.h
@@ -1,5 +1,4 @@
/*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c
index 91a42f2473a7..0764fbfffb73 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.h b/drivers/media/video/pvrusb2/pvrusb2-ctrl.h
index c1680053cd64..0371ae6e6e4e 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-ctrl.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-ctrl.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
index 29d50597c88a..895859ec495a 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h
index 54b2844e7a71..66abf77f51fd 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debug.h b/drivers/media/video/pvrusb2/pvrusb2-debug.h
index 707d2d9635d7..be79249f8628 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-debug.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-debug.h
@@ -1,5 +1,4 @@
/*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
index b53121c78ff9..ca892fb78a5b 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.h b/drivers/media/video/pvrusb2/pvrusb2-debugifc.h
index 990b02d35d36..e24ff59f8605 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c
index 5bf6d8fda1f9..5d036e7e3f07 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2007 Mike Isely <isely@pobox.com>
*
@@ -182,7 +181,7 @@ static int pvr2_lgh06xf_attach(struct pvr2_dvb_adapter *adap)
return 0;
}
-struct pvr2_dvb_props pvr2_onair_creator_fe_props = {
+static struct pvr2_dvb_props pvr2_onair_creator_fe_props = {
.frontend_attach = pvr2_lgdt3303_attach,
.tuner_attach = pvr2_lgh06xf_attach,
};
@@ -242,7 +241,7 @@ static int pvr2_fcv1236d_attach(struct pvr2_dvb_adapter *adap)
return 0;
}
-struct pvr2_dvb_props pvr2_onair_usb2_fe_props = {
+static struct pvr2_dvb_props pvr2_onair_usb2_fe_props = {
.frontend_attach = pvr2_lgdt3302_attach,
.tuner_attach = pvr2_fcv1236d_attach,
};
@@ -315,7 +314,7 @@ static int pvr2_73xxx_tda18271_8295_attach(struct pvr2_dvb_adapter *adap)
return 0;
}
-struct pvr2_dvb_props pvr2_73xxx_dvb_props = {
+static struct pvr2_dvb_props pvr2_73xxx_dvb_props = {
.frontend_attach = pvr2_tda10048_attach,
.tuner_attach = pvr2_73xxx_tda18271_8295_attach,
};
@@ -418,12 +417,12 @@ static int pvr2_tda18271_8295_attach(struct pvr2_dvb_adapter *adap)
return 0;
}
-struct pvr2_dvb_props pvr2_750xx_dvb_props = {
+static struct pvr2_dvb_props pvr2_750xx_dvb_props = {
.frontend_attach = pvr2_s5h1409_attach,
.tuner_attach = pvr2_tda18271_8295_attach,
};
-struct pvr2_dvb_props pvr2_751xx_dvb_props = {
+static struct pvr2_dvb_props pvr2_751xx_dvb_props = {
.frontend_attach = pvr2_s5h1411_attach,
.tuner_attach = pvr2_tda18271_8295_attach,
};
diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/drivers/media/video/pvrusb2/pvrusb2-devattr.h
index d016f8b6c70b..e23ce1d2edd7 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-devattr.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-eeprom.c b/drivers/media/video/pvrusb2/pvrusb2-eeprom.c
index 5ef005947b04..299afa4fa969 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-eeprom.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-eeprom.c
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
diff --git a/drivers/media/video/pvrusb2/pvrusb2-eeprom.h b/drivers/media/video/pvrusb2/pvrusb2-eeprom.h
index 84242975dea7..cca3216f94cc 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-eeprom.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-eeprom.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
index c46d367f7472..a1252d673b41 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-encoder.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.h b/drivers/media/video/pvrusb2/pvrusb2-encoder.h
index 54caf2e3c428..232fefbcd1ac 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-encoder.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
diff --git a/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h b/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h
index abaada31e66e..b58369e7f30b 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2007 Michael Krufky <mkrufky@linuxtv.org>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
index a3fe251d6fd9..657f861593b3 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index 0a868888f389..f30e13f6ee74 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
@@ -40,6 +39,23 @@
#define TV_MIN_FREQ 55250000L
#define TV_MAX_FREQ 850000000L
+/* This defines a minimum interval that the decoder must remain quiet
+ before we are allowed to start it running. */
+#define TIME_MSEC_DECODER_WAIT 50
+
+/* This defines a minimum interval that the encoder must remain quiet
+ before we are allowed to configure it. I had this originally set to
+ 50msec, but Martin Dauskardt <martin.dauskardt@gmx.de> reports that
+ things work better when it's set to 100msec. */
+#define TIME_MSEC_ENCODER_WAIT 100
+
+/* This defines the minimum interval that the encoder must successfully run
+ before we consider that the encoder has run at least once since its
+ firmware has been loaded. This measurement is in important for cases
+ where we can't do something until we know that the encoder has been run
+ at least once. */
+#define TIME_MSEC_ENCODER_OK 250
+
static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL};
static DEFINE_MUTEX(pvr2_unit_mtx);
@@ -67,6 +83,16 @@ MODULE_PARM_DESC(video_std,"specify initial video standard");
module_param_array(tolerance, int, NULL, 0444);
MODULE_PARM_DESC(tolerance,"specify stream error tolerance");
+/* US Broadcast channel 7 (175.25 MHz) */
+static int default_tv_freq = 175250000L;
+/* 104.3 MHz, a usable FM station for my area */
+static int default_radio_freq = 104300000L;
+
+module_param_named(tv_freq, default_tv_freq, int, 0444);
+MODULE_PARM_DESC(tv_freq, "specify initial television frequency");
+module_param_named(radio_freq, default_radio_freq, int, 0444);
+MODULE_PARM_DESC(radio_freq, "specify initial radio frequency");
+
#define PVR2_CTL_WRITE_ENDPOINT 0x01
#define PVR2_CTL_READ_ENDPOINT 0x81
@@ -1701,10 +1727,8 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
are, but I set them to something usable in the Chicago area just
to make driver testing a little easier. */
- /* US Broadcast channel 7 (175.25 MHz) */
- hdw->freqValTelevision = 175250000L;
- /* 104.3 MHz, a usable FM station for my area */
- hdw->freqValRadio = 104300000L;
+ hdw->freqValTelevision = default_tv_freq;
+ hdw->freqValRadio = default_radio_freq;
// Do not use pvr2_reset_ctl_endpoints() here. It is not
// thread-safe against the normal pvr2_send_request() mechanism.
@@ -3421,7 +3445,7 @@ static void pvr2_hdw_cmd_modeswitch(struct pvr2_hdw *hdw,int digitalFl)
}
-void pvr2_led_ctrl_hauppauge(struct pvr2_hdw *hdw, int onoff)
+static void pvr2_led_ctrl_hauppauge(struct pvr2_hdw *hdw, int onoff)
{
/* change some GPIO data
*
@@ -3601,7 +3625,9 @@ static int state_eval_encoder_config(struct pvr2_hdw *hdw)
the encoder. */
if (!hdw->state_encoder_waitok) {
hdw->encoder_wait_timer.expires =
- jiffies + (HZ*50/1000);
+ jiffies +
+ (HZ * TIME_MSEC_ENCODER_WAIT
+ / 1000);
add_timer(&hdw->encoder_wait_timer);
}
}
@@ -3725,7 +3751,7 @@ static int state_eval_encoder_run(struct pvr2_hdw *hdw)
hdw->state_encoder_run = !0;
if (!hdw->state_encoder_runok) {
hdw->encoder_run_timer.expires =
- jiffies + (HZ*250/1000);
+ jiffies + (HZ * TIME_MSEC_ENCODER_OK / 1000);
add_timer(&hdw->encoder_run_timer);
}
}
@@ -3800,7 +3826,9 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw)
but before we did the pending check. */
if (!hdw->state_decoder_quiescent) {
hdw->quiescent_timer.expires =
- jiffies + (HZ*50/1000);
+ jiffies +
+ (HZ * TIME_MSEC_DECODER_WAIT
+ / 1000);
add_timer(&hdw->quiescent_timer);
}
}
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
index 20295e0c1995..c04956d304a7 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c
index 49773764383b..ccdb429fc7af 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c
index c650e02ccd00..55f04a0b2047 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h
index c838df6167f9..7fa38683b3b1 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
index 793c89a8d672..9d3c18b24744 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h
index bd0807b905bb..6ef7a1c0e935 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-io.c b/drivers/media/video/pvrusb2/pvrusb2-io.c
index 7aff8b720064..20b6ae0bb40d 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-io.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-io.c
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-io.h b/drivers/media/video/pvrusb2/pvrusb2-io.h
index 42fcf8281a87..afb7e87c0394 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-io.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-io.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/drivers/media/video/pvrusb2/pvrusb2-ioread.c
index c572212c9f15..05a1376405e7 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-ioread.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-ioread.c
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.h b/drivers/media/video/pvrusb2/pvrusb2-ioread.h
index 1d362f833588..100e0780e1aa 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-ioread.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-ioread.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-main.c b/drivers/media/video/pvrusb2/pvrusb2-main.c
index 332aced8a5a1..ad0d98c2ebb4 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-main.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-main.c
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
diff --git a/drivers/media/video/pvrusb2/pvrusb2-std.c b/drivers/media/video/pvrusb2/pvrusb2-std.c
index fdc5a2b49ca8..ca9f83a85ca5 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-std.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-std.c
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-std.h b/drivers/media/video/pvrusb2/pvrusb2-std.h
index 07c399375341..a35c53d0b320 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-std.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-std.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
index 0ff7a836a8a2..46a8c39ba030 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
@@ -71,6 +70,7 @@ struct pvr2_sysfs_ctl_item {
struct device_attribute attr_val;
struct device_attribute attr_custom;
struct pvr2_ctrl *cptr;
+ int ctl_id;
struct pvr2_sysfs *chptr;
struct pvr2_sysfs_ctl_item *item_next;
struct attribute *attr_gen[7];
@@ -83,38 +83,29 @@ struct pvr2_sysfs_class {
struct class class;
};
-static ssize_t show_name(int id,struct device *class_dev,char *buf)
+static ssize_t show_name(struct device *class_dev,
+ struct device_attribute *attr,
+ char *buf)
{
- struct pvr2_ctrl *cptr;
- struct pvr2_sysfs *sfp;
+ struct pvr2_sysfs_ctl_item *cip;
const char *name;
-
- sfp = (struct pvr2_sysfs *)class_dev->driver_data;
- if (!sfp) return -EINVAL;
- cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
- if (!cptr) return -EINVAL;
-
- name = pvr2_ctrl_get_desc(cptr);
- pvr2_sysfs_trace("pvr2_sysfs(%p) show_name(cid=%d) is %s",sfp,id,name);
-
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_name);
+ name = pvr2_ctrl_get_desc(cip->cptr);
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_name(cid=%d) is %s",
+ cip->chptr, cip->ctl_id, name);
if (!name) return -EINVAL;
-
- return scnprintf(buf,PAGE_SIZE,"%s\n",name);
+ return scnprintf(buf, PAGE_SIZE, "%s\n", name);
}
-static ssize_t show_type(int id,struct device *class_dev,char *buf)
+static ssize_t show_type(struct device *class_dev,
+ struct device_attribute *attr,
+ char *buf)
{
- struct pvr2_ctrl *cptr;
- struct pvr2_sysfs *sfp;
+ struct pvr2_sysfs_ctl_item *cip;
const char *name;
enum pvr2_ctl_type tp;
-
- sfp = (struct pvr2_sysfs *)class_dev->driver_data;
- if (!sfp) return -EINVAL;
- cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
- if (!cptr) return -EINVAL;
-
- tp = pvr2_ctrl_get_type(cptr);
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_type);
+ tp = pvr2_ctrl_get_type(cip->cptr);
switch (tp) {
case pvr2_ctl_int: name = "integer"; break;
case pvr2_ctl_enum: name = "enum"; break;
@@ -122,403 +113,178 @@ static ssize_t show_type(int id,struct device *class_dev,char *buf)
case pvr2_ctl_bool: name = "boolean"; break;
default: name = "?"; break;
}
- pvr2_sysfs_trace("pvr2_sysfs(%p) show_type(cid=%d) is %s",sfp,id,name);
-
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_type(cid=%d) is %s",
+ cip->chptr, cip->ctl_id, name);
if (!name) return -EINVAL;
-
- return scnprintf(buf,PAGE_SIZE,"%s\n",name);
+ return scnprintf(buf, PAGE_SIZE, "%s\n", name);
}
-static ssize_t show_min(int id,struct device *class_dev,char *buf)
+static ssize_t show_min(struct device *class_dev,
+ struct device_attribute *attr,
+ char *buf)
{
- struct pvr2_ctrl *cptr;
- struct pvr2_sysfs *sfp;
+ struct pvr2_sysfs_ctl_item *cip;
long val;
-
- sfp = (struct pvr2_sysfs *)class_dev->driver_data;
- if (!sfp) return -EINVAL;
- cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
- if (!cptr) return -EINVAL;
- val = pvr2_ctrl_get_min(cptr);
-
- pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %ld",sfp,id,val);
-
- return scnprintf(buf,PAGE_SIZE,"%ld\n",val);
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_min);
+ val = pvr2_ctrl_get_min(cip->cptr);
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %ld",
+ cip->chptr, cip->ctl_id, val);
+ return scnprintf(buf, PAGE_SIZE, "%ld\n", val);
}
-static ssize_t show_max(int id,struct device *class_dev,char *buf)
+static ssize_t show_max(struct device *class_dev,
+ struct device_attribute *attr,
+ char *buf)
{
- struct pvr2_ctrl *cptr;
- struct pvr2_sysfs *sfp;
+ struct pvr2_sysfs_ctl_item *cip;
long val;
-
- sfp = (struct pvr2_sysfs *)class_dev->driver_data;
- if (!sfp) return -EINVAL;
- cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
- if (!cptr) return -EINVAL;
- val = pvr2_ctrl_get_max(cptr);
-
- pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %ld",sfp,id,val);
-
- return scnprintf(buf,PAGE_SIZE,"%ld\n",val);
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_max);
+ val = pvr2_ctrl_get_max(cip->cptr);
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %ld",
+ cip->chptr, cip->ctl_id, val);
+ return scnprintf(buf, PAGE_SIZE, "%ld\n", val);
}
-static ssize_t show_val_norm(int id,struct device *class_dev,char *buf)
+static ssize_t show_val_norm(struct device *class_dev,
+ struct device_attribute *attr,
+ char *buf)
{
- struct pvr2_ctrl *cptr;
- struct pvr2_sysfs *sfp;
- int val,ret;
+ struct pvr2_sysfs_ctl_item *cip;
+ int val;
+ int ret;
unsigned int cnt = 0;
-
- sfp = (struct pvr2_sysfs *)class_dev->driver_data;
- if (!sfp) return -EINVAL;
- cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
- if (!cptr) return -EINVAL;
-
- ret = pvr2_ctrl_get_value(cptr,&val);
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_val);
+ ret = pvr2_ctrl_get_value(cip->cptr, &val);
if (ret < 0) return ret;
-
- ret = pvr2_ctrl_value_to_sym(cptr,~0,val,
- buf,PAGE_SIZE-1,&cnt);
-
+ ret = pvr2_ctrl_value_to_sym(cip->cptr, ~0, val,
+ buf, PAGE_SIZE - 1, &cnt);
pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_norm(cid=%d) is %.*s (%d)",
- sfp,id,cnt,buf,val);
+ cip->chptr, cip->ctl_id, cnt, buf, val);
buf[cnt] = '\n';
return cnt+1;
}
-static ssize_t show_val_custom(int id,struct device *class_dev,char *buf)
+static ssize_t show_val_custom(struct device *class_dev,
+ struct device_attribute *attr,
+ char *buf)
{
- struct pvr2_ctrl *cptr;
- struct pvr2_sysfs *sfp;
- int val,ret;
+ struct pvr2_sysfs_ctl_item *cip;
+ int val;
+ int ret;
unsigned int cnt = 0;
-
- sfp = (struct pvr2_sysfs *)class_dev->driver_data;
- if (!sfp) return -EINVAL;
- cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
- if (!cptr) return -EINVAL;
-
- ret = pvr2_ctrl_get_value(cptr,&val);
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_custom);
+ ret = pvr2_ctrl_get_value(cip->cptr, &val);
if (ret < 0) return ret;
-
- ret = pvr2_ctrl_custom_value_to_sym(cptr,~0,val,
- buf,PAGE_SIZE-1,&cnt);
-
+ ret = pvr2_ctrl_custom_value_to_sym(cip->cptr, ~0, val,
+ buf, PAGE_SIZE - 1, &cnt);
pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_custom(cid=%d) is %.*s (%d)",
- sfp,id,cnt,buf,val);
+ cip->chptr, cip->ctl_id, cnt, buf, val);
buf[cnt] = '\n';
return cnt+1;
}
-static ssize_t show_enum(int id,struct device *class_dev,char *buf)
+static ssize_t show_enum(struct device *class_dev,
+ struct device_attribute *attr,
+ char *buf)
{
- struct pvr2_ctrl *cptr;
- struct pvr2_sysfs *sfp;
+ struct pvr2_sysfs_ctl_item *cip;
long val;
- unsigned int bcnt,ccnt,ecnt;
-
- sfp = (struct pvr2_sysfs *)class_dev->driver_data;
- if (!sfp) return -EINVAL;
- cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
- if (!cptr) return -EINVAL;
- ecnt = pvr2_ctrl_get_cnt(cptr);
+ unsigned int bcnt, ccnt, ecnt;
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_enum);
+ ecnt = pvr2_ctrl_get_cnt(cip->cptr);
bcnt = 0;
for (val = 0; val < ecnt; val++) {
- pvr2_ctrl_get_valname(cptr,val,buf+bcnt,PAGE_SIZE-bcnt,&ccnt);
+ pvr2_ctrl_get_valname(cip->cptr, val, buf + bcnt,
+ PAGE_SIZE - bcnt, &ccnt);
if (!ccnt) continue;
bcnt += ccnt;
if (bcnt >= PAGE_SIZE) break;
buf[bcnt] = '\n';
bcnt++;
}
- pvr2_sysfs_trace("pvr2_sysfs(%p) show_enum(cid=%d)",sfp,id);
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_enum(cid=%d)",
+ cip->chptr, cip->ctl_id);
return bcnt;
}
-static ssize_t show_bits(int id,struct device *class_dev,char *buf)
+static ssize_t show_bits(struct device *class_dev,
+ struct device_attribute *attr,
+ char *buf)
{
- struct pvr2_ctrl *cptr;
- struct pvr2_sysfs *sfp;
- int valid_bits,msk;
- unsigned int bcnt,ccnt;
-
- sfp = (struct pvr2_sysfs *)class_dev->driver_data;
- if (!sfp) return -EINVAL;
- cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
- if (!cptr) return -EINVAL;
- valid_bits = pvr2_ctrl_get_mask(cptr);
+ struct pvr2_sysfs_ctl_item *cip;
+ int valid_bits, msk;
+ unsigned int bcnt, ccnt;
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_bits);
+ valid_bits = pvr2_ctrl_get_mask(cip->cptr);
bcnt = 0;
for (msk = 1; valid_bits; msk <<= 1) {
if (!(msk & valid_bits)) continue;
valid_bits &= ~msk;
- pvr2_ctrl_get_valname(cptr,msk,buf+bcnt,PAGE_SIZE-bcnt,&ccnt);
+ pvr2_ctrl_get_valname(cip->cptr, msk, buf + bcnt,
+ PAGE_SIZE - bcnt, &ccnt);
bcnt += ccnt;
if (bcnt >= PAGE_SIZE) break;
buf[bcnt] = '\n';
bcnt++;
}
- pvr2_sysfs_trace("pvr2_sysfs(%p) show_bits(cid=%d)",sfp,id);
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_bits(cid=%d)",
+ cip->chptr, cip->ctl_id);
return bcnt;
}
-static int store_val_any(int id,int customfl,struct pvr2_sysfs *sfp,
+static int store_val_any(struct pvr2_sysfs_ctl_item *cip, int customfl,
const char *buf,unsigned int count)
{
- struct pvr2_ctrl *cptr;
int ret;
int mask,val;
-
- cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
if (customfl) {
- ret = pvr2_ctrl_custom_sym_to_value(cptr,buf,count,&mask,&val);
+ ret = pvr2_ctrl_custom_sym_to_value(cip->cptr, buf, count,
+ &mask, &val);
} else {
- ret = pvr2_ctrl_sym_to_value(cptr,buf,count,&mask,&val);
+ ret = pvr2_ctrl_sym_to_value(cip->cptr, buf, count,
+ &mask, &val);
}
if (ret < 0) return ret;
- ret = pvr2_ctrl_set_mask_value(cptr,mask,val);
- pvr2_hdw_commit_ctl(sfp->channel.hdw);
+ ret = pvr2_ctrl_set_mask_value(cip->cptr, mask, val);
+ pvr2_hdw_commit_ctl(cip->chptr->channel.hdw);
return ret;
}
-static ssize_t store_val_norm(int id,struct device *class_dev,
- const char *buf,size_t count)
+static ssize_t store_val_norm(struct device *class_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
- struct pvr2_sysfs *sfp;
+ struct pvr2_sysfs_ctl_item *cip;
int ret;
- sfp = (struct pvr2_sysfs *)class_dev->driver_data;
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_val);
pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_norm(cid=%d) \"%.*s\"",
- sfp,id,(int)count,buf);
- ret = store_val_any(id,0,sfp,buf,count);
+ cip->chptr, cip->ctl_id, (int)count, buf);
+ ret = store_val_any(cip, 0, buf, count);
if (!ret) ret = count;
return ret;
}
-static ssize_t store_val_custom(int id,struct device *class_dev,
- const char *buf,size_t count)
+static ssize_t store_val_custom(struct device *class_dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
- struct pvr2_sysfs *sfp;
+ struct pvr2_sysfs_ctl_item *cip;
int ret;
- sfp = (struct pvr2_sysfs *)class_dev->driver_data;
+ cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_custom);
pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_custom(cid=%d) \"%.*s\"",
- sfp,id,(int)count,buf);
- ret = store_val_any(id,1,sfp,buf,count);
+ cip->chptr, cip->ctl_id, (int)count, buf);
+ ret = store_val_any(cip, 1, buf, count);
if (!ret) ret = count;
return ret;
}
-/*
- Mike Isely <isely@pobox.com> 30-April-2005
-
- This next batch of horrible preprocessor hackery is needed because the
- kernel's device_attribute mechanism fails to pass the actual
- attribute through to the show / store functions, which means we have no
- way to package up any attribute-specific parameters, like for example the
- control id. So we work around this brain-damage by encoding the control
- id into the show / store functions themselves and pick the function based
- on the control id we're setting up. These macros try to ease the pain.
- Yuck.
-*/
-
-#define CREATE_SHOW_INSTANCE(sf_name,ctl_id) \
-static ssize_t sf_name##_##ctl_id(struct device *class_dev, \
-struct device_attribute *attr, char *buf) \
-{ return sf_name(ctl_id,class_dev,buf); }
-
-#define CREATE_STORE_INSTANCE(sf_name,ctl_id) \
-static ssize_t sf_name##_##ctl_id(struct device *class_dev, \
-struct device_attribute *attr, const char *buf, size_t count) \
-{ return sf_name(ctl_id,class_dev,buf,count); }
-
-#define CREATE_BATCH(ctl_id) \
-CREATE_SHOW_INSTANCE(show_name,ctl_id) \
-CREATE_SHOW_INSTANCE(show_type,ctl_id) \
-CREATE_SHOW_INSTANCE(show_min,ctl_id) \
-CREATE_SHOW_INSTANCE(show_max,ctl_id) \
-CREATE_SHOW_INSTANCE(show_val_norm,ctl_id) \
-CREATE_SHOW_INSTANCE(show_val_custom,ctl_id) \
-CREATE_SHOW_INSTANCE(show_enum,ctl_id) \
-CREATE_SHOW_INSTANCE(show_bits,ctl_id) \
-CREATE_STORE_INSTANCE(store_val_norm,ctl_id) \
-CREATE_STORE_INSTANCE(store_val_custom,ctl_id) \
-
-CREATE_BATCH(0)
-CREATE_BATCH(1)
-CREATE_BATCH(2)
-CREATE_BATCH(3)
-CREATE_BATCH(4)
-CREATE_BATCH(5)
-CREATE_BATCH(6)
-CREATE_BATCH(7)
-CREATE_BATCH(8)
-CREATE_BATCH(9)
-CREATE_BATCH(10)
-CREATE_BATCH(11)
-CREATE_BATCH(12)
-CREATE_BATCH(13)
-CREATE_BATCH(14)
-CREATE_BATCH(15)
-CREATE_BATCH(16)
-CREATE_BATCH(17)
-CREATE_BATCH(18)
-CREATE_BATCH(19)
-CREATE_BATCH(20)
-CREATE_BATCH(21)
-CREATE_BATCH(22)
-CREATE_BATCH(23)
-CREATE_BATCH(24)
-CREATE_BATCH(25)
-CREATE_BATCH(26)
-CREATE_BATCH(27)
-CREATE_BATCH(28)
-CREATE_BATCH(29)
-CREATE_BATCH(30)
-CREATE_BATCH(31)
-CREATE_BATCH(32)
-CREATE_BATCH(33)
-CREATE_BATCH(34)
-CREATE_BATCH(35)
-CREATE_BATCH(36)
-CREATE_BATCH(37)
-CREATE_BATCH(38)
-CREATE_BATCH(39)
-CREATE_BATCH(40)
-CREATE_BATCH(41)
-CREATE_BATCH(42)
-CREATE_BATCH(43)
-CREATE_BATCH(44)
-CREATE_BATCH(45)
-CREATE_BATCH(46)
-CREATE_BATCH(47)
-CREATE_BATCH(48)
-CREATE_BATCH(49)
-CREATE_BATCH(50)
-CREATE_BATCH(51)
-CREATE_BATCH(52)
-CREATE_BATCH(53)
-CREATE_BATCH(54)
-CREATE_BATCH(55)
-CREATE_BATCH(56)
-CREATE_BATCH(57)
-CREATE_BATCH(58)
-CREATE_BATCH(59)
-
-struct pvr2_sysfs_func_set {
- ssize_t (*show_name)(struct device *,
- struct device_attribute *attr, char *);
- ssize_t (*show_type)(struct device *,
- struct device_attribute *attr, char *);
- ssize_t (*show_min)(struct device *,
- struct device_attribute *attr, char *);
- ssize_t (*show_max)(struct device *,
- struct device_attribute *attr, char *);
- ssize_t (*show_enum)(struct device *,
- struct device_attribute *attr, char *);
- ssize_t (*show_bits)(struct device *,
- struct device_attribute *attr, char *);
- ssize_t (*show_val_norm)(struct device *,
- struct device_attribute *attr, char *);
- ssize_t (*store_val_norm)(struct device *,
- struct device_attribute *attr,
- const char *,size_t);
- ssize_t (*show_val_custom)(struct device *,
- struct device_attribute *attr, char *);
- ssize_t (*store_val_custom)(struct device *,
- struct device_attribute *attr,
- const char *,size_t);
-};
-
-#define INIT_BATCH(ctl_id) \
-[ctl_id] = { \
- .show_name = show_name_##ctl_id, \
- .show_type = show_type_##ctl_id, \
- .show_min = show_min_##ctl_id, \
- .show_max = show_max_##ctl_id, \
- .show_enum = show_enum_##ctl_id, \
- .show_bits = show_bits_##ctl_id, \
- .show_val_norm = show_val_norm_##ctl_id, \
- .store_val_norm = store_val_norm_##ctl_id, \
- .show_val_custom = show_val_custom_##ctl_id, \
- .store_val_custom = store_val_custom_##ctl_id, \
-} \
-
-static struct pvr2_sysfs_func_set funcs[] = {
- INIT_BATCH(0),
- INIT_BATCH(1),
- INIT_BATCH(2),
- INIT_BATCH(3),
- INIT_BATCH(4),
- INIT_BATCH(5),
- INIT_BATCH(6),
- INIT_BATCH(7),
- INIT_BATCH(8),
- INIT_BATCH(9),
- INIT_BATCH(10),
- INIT_BATCH(11),
- INIT_BATCH(12),
- INIT_BATCH(13),
- INIT_BATCH(14),
- INIT_BATCH(15),
- INIT_BATCH(16),
- INIT_BATCH(17),
- INIT_BATCH(18),
- INIT_BATCH(19),
- INIT_BATCH(20),
- INIT_BATCH(21),
- INIT_BATCH(22),
- INIT_BATCH(23),
- INIT_BATCH(24),
- INIT_BATCH(25),
- INIT_BATCH(26),
- INIT_BATCH(27),
- INIT_BATCH(28),
- INIT_BATCH(29),
- INIT_BATCH(30),
- INIT_BATCH(31),
- INIT_BATCH(32),
- INIT_BATCH(33),
- INIT_BATCH(34),
- INIT_BATCH(35),
- INIT_BATCH(36),
- INIT_BATCH(37),
- INIT_BATCH(38),
- INIT_BATCH(39),
- INIT_BATCH(40),
- INIT_BATCH(41),
- INIT_BATCH(42),
- INIT_BATCH(43),
- INIT_BATCH(44),
- INIT_BATCH(45),
- INIT_BATCH(46),
- INIT_BATCH(47),
- INIT_BATCH(48),
- INIT_BATCH(49),
- INIT_BATCH(50),
- INIT_BATCH(51),
- INIT_BATCH(52),
- INIT_BATCH(53),
- INIT_BATCH(54),
- INIT_BATCH(55),
- INIT_BATCH(56),
- INIT_BATCH(57),
- INIT_BATCH(58),
- INIT_BATCH(59),
-};
-
-
static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id)
{
struct pvr2_sysfs_ctl_item *cip;
- struct pvr2_sysfs_func_set *fp;
struct pvr2_ctrl *cptr;
unsigned int cnt,acnt;
int ret;
- if ((ctl_id < 0) || (ctl_id >= ARRAY_SIZE(funcs))) {
- return;
- }
-
- fp = funcs + ctl_id;
cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,ctl_id);
if (!cptr) return;
@@ -527,6 +293,7 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id)
pvr2_sysfs_trace("Creating pvr2_sysfs_ctl_item id=%p",cip);
cip->cptr = cptr;
+ cip->ctl_id = ctl_id;
cip->chptr = sfp;
cip->item_next = NULL;
@@ -539,19 +306,19 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id)
cip->attr_name.attr.name = "name";
cip->attr_name.attr.mode = S_IRUGO;
- cip->attr_name.show = fp->show_name;
+ cip->attr_name.show = show_name;
cip->attr_type.attr.name = "type";
cip->attr_type.attr.mode = S_IRUGO;
- cip->attr_type.show = fp->show_type;
+ cip->attr_type.show = show_type;
cip->attr_min.attr.name = "min_val";
cip->attr_min.attr.mode = S_IRUGO;
- cip->attr_min.show = fp->show_min;
+ cip->attr_min.show = show_min;
cip->attr_max.attr.name = "max_val";
cip->attr_max.attr.mode = S_IRUGO;
- cip->attr_max.show = fp->show_max;
+ cip->attr_max.show = show_max;
cip->attr_val.attr.name = "cur_val";
cip->attr_val.attr.mode = S_IRUGO;
@@ -561,11 +328,11 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id)
cip->attr_enum.attr.name = "enum_val";
cip->attr_enum.attr.mode = S_IRUGO;
- cip->attr_enum.show = fp->show_enum;
+ cip->attr_enum.show = show_enum;
cip->attr_bits.attr.name = "bit_val";
cip->attr_bits.attr.mode = S_IRUGO;
- cip->attr_bits.show = fp->show_bits;
+ cip->attr_bits.show = show_bits;
if (pvr2_ctrl_is_writable(cptr)) {
cip->attr_val.attr.mode |= S_IWUSR|S_IWGRP;
@@ -576,12 +343,12 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id)
cip->attr_gen[acnt++] = &cip->attr_name.attr;
cip->attr_gen[acnt++] = &cip->attr_type.attr;
cip->attr_gen[acnt++] = &cip->attr_val.attr;
- cip->attr_val.show = fp->show_val_norm;
- cip->attr_val.store = fp->store_val_norm;
+ cip->attr_val.show = show_val_norm;
+ cip->attr_val.store = store_val_norm;
if (pvr2_ctrl_has_custom_symbols(cptr)) {
cip->attr_gen[acnt++] = &cip->attr_custom.attr;
- cip->attr_custom.show = fp->show_val_custom;
- cip->attr_custom.store = fp->store_val_custom;
+ cip->attr_custom.show = show_val_custom;
+ cip->attr_custom.store = store_val_custom;
}
switch (pvr2_ctrl_get_type(cptr)) {
case pvr2_ctl_enum:
diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.h b/drivers/media/video/pvrusb2/pvrusb2-sysfs.h
index ff9373b47f8f..6d875bfe7991 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-tuner.c b/drivers/media/video/pvrusb2/pvrusb2-tuner.c
index 05e65ce2e3a9..07775d1aad4e 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-tuner.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-tuner.c
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
diff --git a/drivers/media/video/pvrusb2/pvrusb2-tuner.h b/drivers/media/video/pvrusb2/pvrusb2-tuner.h
index 556f12aa9160..ef4afaf37b0a 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-tuner.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-tuner.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-util.h b/drivers/media/video/pvrusb2/pvrusb2-util.h
index e53aee416f56..92b75544ee2e 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-util.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-util.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
index e9b5d4e91327..0d72dc470fef 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.h b/drivers/media/video/pvrusb2/pvrusb2-v4l2.h
index 9a995e2d2256..34c011a7b107 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
index 2433a3160041..4059648c7056 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h
index 2b917fda02e4..4ff5b892b303 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
diff --git a/drivers/media/video/pvrusb2/pvrusb2-wm8775.c b/drivers/media/video/pvrusb2/pvrusb2-wm8775.c
index 66b4d36ef765..f6fcf0ac6118 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-wm8775.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-wm8775.c
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
diff --git a/drivers/media/video/pvrusb2/pvrusb2-wm8775.h b/drivers/media/video/pvrusb2/pvrusb2-wm8775.h
index 8aaeff4e1e20..807090961255 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-wm8775.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-wm8775.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
diff --git a/drivers/media/video/pvrusb2/pvrusb2.h b/drivers/media/video/pvrusb2/pvrusb2.h
index 1a9a4baf12b8..240de9b35661 100644
--- a/drivers/media/video/pvrusb2/pvrusb2.h
+++ b/drivers/media/video/pvrusb2/pvrusb2.h
@@ -1,6 +1,5 @@
/*
*
- * $Id$
*
* Copyright (C) 2005 Mike Isely <isely@pobox.com>
* Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index b111903aa322..677c482eccac 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -3940,32 +3940,111 @@ struct saa7134_board saa7134_boards[] = {
[SAA7134_BOARD_BEHOLD_M6] = {
/* Igor Kuznetsov <igk@igk.ru> */
/* Andrey Melnikoff <temnota@kmv.ru> */
- .name = "Beholder BeholdTV M6 / BeholdTV M6 Extra",
+ /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV M6",
.audio_clock = 0x00187de7,
.tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
.radio_type = UNSET,
.tuner_addr = ADDR_UNSET,
.radio_addr = ADDR_UNSET,
.tda9887_conf = TDA9887_PRESENT,
- .inputs = {{
+ .inputs = { {
.name = name_tv,
.vmux = 3,
.amux = TV,
.tv = 1,
- },{
+ }, {
.name = name_comp1,
.vmux = 1,
.amux = LINE1,
- },{
+ }, {
.name = name_svideo,
.vmux = 8,
.amux = LINE1,
- }},
+ } },
.radio = {
.name = name_radio,
.amux = LINE2,
},
.mpeg = SAA7134_MPEG_EMPRESS,
+ .video_out = CCIR656,
+ .vid_port_opts = (SET_T_CODE_POLARITY_NON_INVERTED |
+ SET_CLOCK_NOT_DELAYED |
+ SET_CLOCK_INVERTED |
+ SET_VSYNC_OFF),
+ },
+ [SAA7134_BOARD_BEHOLD_M63] = {
+ /* Igor Kuznetsov <igk@igk.ru> */
+ /* Andrey Melnikoff <temnota@kmv.ru> */
+ /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV M63",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ .mpeg = SAA7134_MPEG_EMPRESS,
+ .video_out = CCIR656,
+ .vid_port_opts = (SET_T_CODE_POLARITY_NON_INVERTED |
+ SET_CLOCK_NOT_DELAYED |
+ SET_CLOCK_INVERTED |
+ SET_VSYNC_OFF),
+ },
+ [SAA7134_BOARD_BEHOLD_M6_EXTRA] = {
+ /* Igor Kuznetsov <igk@igk.ru> */
+ /* Andrey Melnikoff <temnota@kmv.ru> */
+ /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */
+ .name = "Beholder BeholdTV M6 Extra",
+ .audio_clock = 0x00187de7,
+ /* FIXME: Must be PHILIPS_FM1216ME_MK5*/
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 3,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp1,
+ .vmux = 1,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE1,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = LINE2,
+ },
+ .mpeg = SAA7134_MPEG_EMPRESS,
+ .video_out = CCIR656,
+ .vid_port_opts = (SET_T_CODE_POLARITY_NON_INVERTED |
+ SET_CLOCK_NOT_DELAYED |
+ SET_CLOCK_INVERTED |
+ SET_VSYNC_OFF),
},
[SAA7134_BOARD_TWINHAN_DTV_DVB_3056] = {
.name = "Twinhan Hybrid DTV-DVB 3056 PCI",
@@ -5229,13 +5308,13 @@ struct pci_device_id saa7134_pci_tbl[] = {
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x5ace,
.subdevice = 0x6193,
- .driver_data = SAA7134_BOARD_BEHOLD_M6,
+ .driver_data = SAA7134_BOARD_BEHOLD_M6_EXTRA,
}, {
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
.subvendor = 0x5ace,
.subdevice = 0x6191,
- .driver_data = SAA7134_BOARD_BEHOLD_M6,
+ .driver_data = SAA7134_BOARD_BEHOLD_M63,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7133,
@@ -5595,6 +5674,8 @@ int saa7134_board_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_HAUPPAUGE_HVR1110:
case SAA7134_BOARD_BEHOLD_607_9FM:
case SAA7134_BOARD_BEHOLD_M6:
+ case SAA7134_BOARD_BEHOLD_M63:
+ case SAA7134_BOARD_BEHOLD_M6_EXTRA:
dev->has_remote = SAA7134_REMOTE_I2C;
break;
case SAA7134_BOARD_AVERMEDIA_A169_B:
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
index 469f93aac008..726f01ccedf0 100644
--- a/drivers/media/video/saa7134/saa7134-dvb.c
+++ b/drivers/media/video/saa7134/saa7134-dvb.c
@@ -1338,7 +1338,8 @@ static int dvb_init(struct saa7134_dev *dev)
return ret;
dettach_frontend:
- dvb_frontend_detach(dev->dvb.frontend);
+ if (dev->dvb.frontend)
+ dvb_frontend_detach(dev->dvb.frontend);
dev->dvb.frontend = NULL;
return -1;
diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c
index 81431ee41842..654b151fdb64 100644
--- a/drivers/media/video/saa7134/saa7134-empress.c
+++ b/drivers/media/video/saa7134/saa7134-empress.c
@@ -112,7 +112,6 @@ static int ts_release(struct inode *inode, struct file *file)
videobuf_stop(&dev->empress_tsq);
videobuf_mmap_free(&dev->empress_tsq);
- dev->empress_users--;
/* stop the encoder */
ts_reset_encoder(dev);
@@ -121,6 +120,8 @@ static int ts_release(struct inode *inode, struct file *file)
saa_writeb(SAA7134_AUDIO_MUTE_CTRL,
saa_readb(SAA7134_AUDIO_MUTE_CTRL) | (1 << 6));
+ dev->empress_users--;
+
return 0;
}
@@ -203,7 +204,7 @@ static int empress_s_input(struct file *file, void *priv, unsigned int i)
return 0;
}
-static int empress_enum_fmt_cap(struct file *file, void *priv,
+static int empress_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
if (f->index != 0)
@@ -215,11 +216,10 @@ static int empress_enum_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int empress_g_fmt_cap(struct file *file, void *priv,
+static int empress_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = file->private_data;
saa7134_i2c_call_clients(dev, VIDIOC_G_FMT, f);
@@ -229,11 +229,10 @@ static int empress_g_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int empress_s_fmt_cap(struct file *file, void *priv,
+static int empress_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = file->private_data;
saa7134_i2c_call_clients(dev, VIDIOC_S_FMT, f);
@@ -247,8 +246,7 @@ static int empress_s_fmt_cap(struct file *file, void *priv,
static int empress_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *p)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = file->private_data;
return videobuf_reqbufs(&dev->empress_tsq, p);
}
@@ -256,24 +254,21 @@ static int empress_reqbufs(struct file *file, void *priv,
static int empress_querybuf(struct file *file, void *priv,
struct v4l2_buffer *b)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = file->private_data;
return videobuf_querybuf(&dev->empress_tsq, b);
}
static int empress_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = file->private_data;
return videobuf_qbuf(&dev->empress_tsq, b);
}
static int empress_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = file->private_data;
return videobuf_dqbuf(&dev->empress_tsq, b,
file->f_flags & O_NONBLOCK);
@@ -282,8 +277,7 @@ static int empress_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
static int empress_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = file->private_data;
return videobuf_streamon(&dev->empress_tsq);
}
@@ -291,8 +285,7 @@ static int empress_streamon(struct file *file, void *priv,
static int empress_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = file->private_data;
return videobuf_streamoff(&dev->empress_tsq);
}
@@ -300,8 +293,7 @@ static int empress_streamoff(struct file *file, void *priv,
static int empress_s_ext_ctrls(struct file *file, void *priv,
struct v4l2_ext_controls *ctrls)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = file->private_data;
/* count == 0 is abused in saa6752hs.c, so that special
case is handled here explicitly. */
@@ -320,8 +312,7 @@ static int empress_s_ext_ctrls(struct file *file, void *priv,
static int empress_g_ext_ctrls(struct file *file, void *priv,
struct v4l2_ext_controls *ctrls)
{
- struct saa7134_fh *fh = priv;
- struct saa7134_dev *dev = fh->dev;
+ struct saa7134_dev *dev = file->private_data;
if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
return -EINVAL;
@@ -353,9 +344,9 @@ static struct video_device saa7134_empress_template =
.minor = -1,
.vidioc_querycap = empress_querycap,
- .vidioc_enum_fmt_cap = empress_enum_fmt_cap,
- .vidioc_s_fmt_cap = empress_s_fmt_cap,
- .vidioc_g_fmt_cap = empress_g_fmt_cap,
+ .vidioc_enum_fmt_vid_cap = empress_enum_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = empress_s_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = empress_g_fmt_vid_cap,
.vidioc_reqbufs = empress_reqbufs,
.vidioc_querybuf = empress_querybuf,
.vidioc_qbuf = empress_qbuf,
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
index 919632b10aae..88e144259e36 100644
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -531,6 +531,8 @@ void saa7134_set_i2c_ir(struct saa7134_dev *dev, struct IR_i2c *ir)
break;
case SAA7134_BOARD_BEHOLD_607_9FM:
case SAA7134_BOARD_BEHOLD_M6:
+ case SAA7134_BOARD_BEHOLD_M63:
+ case SAA7134_BOARD_BEHOLD_M6_EXTRA:
case SAA7134_BOARD_BEHOLD_H6:
snprintf(ir->c.name, sizeof(ir->c.name), "BeholdTV");
ir->get_key = get_key_beholdm6xx;
diff --git a/drivers/media/video/saa7134/saa7134-reg.h b/drivers/media/video/saa7134/saa7134-reg.h
index 86f5eefdb0f6..cf89d96d7295 100644
--- a/drivers/media/video/saa7134/saa7134-reg.h
+++ b/drivers/media/video/saa7134/saa7134-reg.h
@@ -368,6 +368,7 @@
#define SAA7135_DSP_RWCLEAR 0x586
#define SAA7135_DSP_RWCLEAR_RERR 1
+#define SAA7133_I2S_AUDIO_CONTROL 0x591
/* ------------------------------------------------------------------ */
/*
* Local variables:
diff --git a/drivers/media/video/saa7134/saa7134-tvaudio.c b/drivers/media/video/saa7134/saa7134-tvaudio.c
index 232af598d947..4cc3741c5472 100644
--- a/drivers/media/video/saa7134/saa7134-tvaudio.c
+++ b/drivers/media/video/saa7134/saa7134-tvaudio.c
@@ -873,13 +873,34 @@ void saa7134_enable_i2s(struct saa7134_dev *dev)
if (!card_is_empress(dev))
return;
- i2s_format = (dev->input->amux == TV) ? 0x00 : 0x01;
- /* enable I2S audio output for the mpeg encoder */
- saa_writeb(SAA7134_I2S_OUTPUT_SELECT, 0x80);
- saa_writeb(SAA7134_I2S_OUTPUT_FORMAT, i2s_format);
- saa_writeb(SAA7134_I2S_OUTPUT_LEVEL, 0x0F);
- saa_writeb(SAA7134_I2S_AUDIO_OUTPUT, 0x01);
+ if (dev->pci->device == PCI_DEVICE_ID_PHILIPS_SAA7130)
+ return;
+
+ /* configure GPIO for out */
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x0E000000, 0x00000000);
+
+ switch (dev->pci->device) {
+ case PCI_DEVICE_ID_PHILIPS_SAA7133:
+ case PCI_DEVICE_ID_PHILIPS_SAA7135:
+ /* Set I2S format (SONY)  */
+ saa_writeb(SAA7133_I2S_AUDIO_CONTROL, 0x00);
+ /* Start I2S */
+ saa_writeb(SAA7134_I2S_AUDIO_OUTPUT, 0x11);
+ break;
+
+ case PCI_DEVICE_ID_PHILIPS_SAA7134:
+ i2s_format = (dev->input->amux == TV) ? 0x00 : 0x01;
+
+ /* enable I2S audio output for the mpeg encoder */
+ saa_writeb(SAA7134_I2S_OUTPUT_SELECT, 0x80);
+ saa_writeb(SAA7134_I2S_OUTPUT_FORMAT, i2s_format);
+ saa_writeb(SAA7134_I2S_OUTPUT_LEVEL, 0x0F);
+ saa_writeb(SAA7134_I2S_AUDIO_OUTPUT, 0x01);
+
+ default:
+ break;
+ }
}
int saa7134_tvaudio_rx2mode(u32 rx)
diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c
index 48e1a01718ec..8a8b3e118f8b 100644
--- a/drivers/media/video/saa7134/saa7134-video.c
+++ b/drivers/media/video/saa7134/saa7134-video.c
@@ -1496,7 +1496,7 @@ static int video_mmap(struct file *file, struct vm_area_struct * vma)
/* ------------------------------------------------------------------ */
-static int saa7134_try_get_set_fmt_vbi(struct file *file, void *priv,
+static int saa7134_try_get_set_fmt_vbi_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct saa7134_fh *fh = priv;
@@ -1516,7 +1516,7 @@ static int saa7134_try_get_set_fmt_vbi(struct file *file, void *priv,
return 0;
}
-static int saa7134_g_fmt_cap(struct file *file, void *priv,
+static int saa7134_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct saa7134_fh *fh = priv;
@@ -1532,7 +1532,7 @@ static int saa7134_g_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int saa7134_g_fmt_overlay(struct file *file, void *priv,
+static int saa7134_g_fmt_vid_overlay(struct file *file, void *priv,
struct v4l2_format *f)
{
struct saa7134_fh *fh = priv;
@@ -1546,7 +1546,7 @@ static int saa7134_g_fmt_overlay(struct file *file, void *priv,
return 0;
}
-static int saa7134_try_fmt_cap(struct file *file, void *priv,
+static int saa7134_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct saa7134_fh *fh = priv;
@@ -1597,7 +1597,7 @@ static int saa7134_try_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int saa7134_try_fmt_overlay(struct file *file, void *priv,
+static int saa7134_try_fmt_vid_overlay(struct file *file, void *priv,
struct v4l2_format *f)
{
struct saa7134_fh *fh = priv;
@@ -1611,13 +1611,13 @@ static int saa7134_try_fmt_overlay(struct file *file, void *priv,
return verify_preview(dev, &f->fmt.win);
}
-static int saa7134_s_fmt_cap(struct file *file, void *priv,
+static int saa7134_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct saa7134_fh *fh = priv;
int err;
- err = saa7134_try_fmt_cap(file, priv, f);
+ err = saa7134_try_fmt_vid_cap(file, priv, f);
if (0 != err)
return err;
@@ -1628,7 +1628,7 @@ static int saa7134_s_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int saa7134_s_fmt_overlay(struct file *file, void *priv,
+static int saa7134_s_fmt_vid_overlay(struct file *file, void *priv,
struct v4l2_format *f)
{
struct saa7134_fh *fh = priv;
@@ -2028,7 +2028,7 @@ static int saa7134_s_priority(struct file *file, void *f,
return v4l2_prio_change(&dev->prio, &fh->prio, prio);
}
-static int saa7134_enum_fmt_cap(struct file *file, void *priv,
+static int saa7134_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
if (f->index >= FORMATS)
@@ -2042,7 +2042,7 @@ static int saa7134_enum_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int saa7134_enum_fmt_overlay(struct file *file, void *priv,
+static int saa7134_enum_fmt_vid_overlay(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
if (saa7134_no_overlay > 0) {
@@ -2061,7 +2061,7 @@ static int saa7134_enum_fmt_overlay(struct file *file, void *priv,
return 0;
}
-static int saa7134_enum_fmt_vbi(struct file *file, void *priv,
+static int saa7134_enum_fmt_vbi_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
if (0 != f->index)
@@ -2348,18 +2348,18 @@ struct video_device saa7134_video_template =
.fops = &video_fops,
.minor = -1,
.vidioc_querycap = saa7134_querycap,
- .vidioc_enum_fmt_cap = saa7134_enum_fmt_cap,
- .vidioc_g_fmt_cap = saa7134_g_fmt_cap,
- .vidioc_try_fmt_cap = saa7134_try_fmt_cap,
- .vidioc_s_fmt_cap = saa7134_s_fmt_cap,
- .vidioc_enum_fmt_overlay = saa7134_enum_fmt_overlay,
- .vidioc_g_fmt_overlay = saa7134_g_fmt_overlay,
- .vidioc_try_fmt_overlay = saa7134_try_fmt_overlay,
- .vidioc_s_fmt_overlay = saa7134_s_fmt_overlay,
- .vidioc_enum_fmt_vbi = saa7134_enum_fmt_vbi,
- .vidioc_g_fmt_vbi = saa7134_try_get_set_fmt_vbi,
- .vidioc_try_fmt_vbi = saa7134_try_get_set_fmt_vbi,
- .vidioc_s_fmt_vbi = saa7134_try_get_set_fmt_vbi,
+ .vidioc_enum_fmt_vid_cap = saa7134_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = saa7134_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = saa7134_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = saa7134_s_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_overlay = saa7134_enum_fmt_vid_overlay,
+ .vidioc_g_fmt_vid_overlay = saa7134_g_fmt_vid_overlay,
+ .vidioc_try_fmt_vid_overlay = saa7134_try_fmt_vid_overlay,
+ .vidioc_s_fmt_vid_overlay = saa7134_s_fmt_vid_overlay,
+ .vidioc_enum_fmt_vbi_cap = saa7134_enum_fmt_vbi_cap,
+ .vidioc_g_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap,
+ .vidioc_try_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap,
+ .vidioc_s_fmt_vbi_cap = saa7134_try_get_set_fmt_vbi_cap,
.vidioc_g_audio = saa7134_g_audio,
.vidioc_s_audio = saa7134_s_audio,
.vidioc_cropcap = saa7134_cropcap,
@@ -2458,13 +2458,14 @@ int saa7134_videoport_init(struct saa7134_dev *dev)
int vo = saa7134_boards[dev->board].video_out;
int video_reg;
unsigned int vid_port_opts = saa7134_boards[dev->board].vid_port_opts;
+
+ /* Configure videoport */
saa_writeb(SAA7134_VIDEO_PORT_CTRL0, video_out[vo][0]);
video_reg = video_out[vo][1];
if (vid_port_opts & SET_T_CODE_POLARITY_NON_INVERTED)
video_reg &= ~VP_T_CODE_P_INVERTED;
saa_writeb(SAA7134_VIDEO_PORT_CTRL1, video_reg);
saa_writeb(SAA7134_VIDEO_PORT_CTRL2, video_out[vo][2]);
- saa_writeb(SAA7134_VIDEO_PORT_CTRL3, video_out[vo][3]);
saa_writeb(SAA7134_VIDEO_PORT_CTRL4, video_out[vo][4]);
video_reg = video_out[vo][5];
if (vid_port_opts & SET_CLOCK_NOT_DELAYED)
@@ -2481,6 +2482,9 @@ int saa7134_videoport_init(struct saa7134_dev *dev)
saa_writeb(SAA7134_VIDEO_PORT_CTRL7, video_out[vo][7]);
saa_writeb(SAA7134_VIDEO_PORT_CTRL8, video_out[vo][8]);
+ /* Start videoport */
+ saa_writeb(SAA7134_VIDEO_PORT_CTRL3, video_out[vo][3]);
+
return 0;
}
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index 34ff0d4998f3..0a94be7afc1e 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -264,7 +264,8 @@ struct saa7134_format {
#define SAA7134_BOARD_AVERMEDIA_A700_PRO 140
#define SAA7134_BOARD_AVERMEDIA_A700_HYBRID 141
#define SAA7134_BOARD_BEHOLD_H6 142
-
+#define SAA7134_BOARD_BEHOLD_M63 143
+#define SAA7134_BOARD_BEHOLD_M6_EXTRA 144
#define SAA7134_MAXBOARDS 8
#define SAA7134_INPUT_MAX 8
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index a1b92446c8b4..208ed5278239 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -44,7 +44,7 @@ format_by_fourcc(struct soc_camera_device *icd, unsigned int fourcc)
return NULL;
}
-static int soc_camera_try_fmt_cap(struct file *file, void *priv,
+static int soc_camera_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct soc_camera_file *icf = file->private_data;
@@ -342,7 +342,7 @@ static struct file_operations soc_camera_fops = {
};
-static int soc_camera_s_fmt_cap(struct file *file, void *priv,
+static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct soc_camera_file *icf = file->private_data;
@@ -362,7 +362,7 @@ static int soc_camera_s_fmt_cap(struct file *file, void *priv,
/* buswidth may be further adjusted by the ici */
icd->buswidth = data_fmt->depth;
- ret = soc_camera_try_fmt_cap(file, icf, f);
+ ret = soc_camera_try_fmt_vid_cap(file, icf, f);
if (ret < 0)
return ret;
@@ -389,7 +389,7 @@ static int soc_camera_s_fmt_cap(struct file *file, void *priv,
return ici->ops->set_bus_param(icd, f->fmt.pix.pixelformat);
}
-static int soc_camera_enum_fmt_cap(struct file *file, void *priv,
+static int soc_camera_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
struct soc_camera_file *icf = file->private_data;
@@ -408,7 +408,7 @@ static int soc_camera_enum_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int soc_camera_g_fmt_cap(struct file *file, void *priv,
+static int soc_camera_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct soc_camera_file *icf = file->private_data;
@@ -935,15 +935,15 @@ int soc_camera_video_start(struct soc_camera_device *icd)
vdev->minor = -1;
vdev->tvnorms = V4L2_STD_UNKNOWN,
vdev->vidioc_querycap = soc_camera_querycap;
- vdev->vidioc_g_fmt_cap = soc_camera_g_fmt_cap;
- vdev->vidioc_enum_fmt_cap = soc_camera_enum_fmt_cap;
- vdev->vidioc_s_fmt_cap = soc_camera_s_fmt_cap;
+ vdev->vidioc_g_fmt_vid_cap = soc_camera_g_fmt_vid_cap;
+ vdev->vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap;
+ vdev->vidioc_s_fmt_vid_cap = soc_camera_s_fmt_vid_cap;
vdev->vidioc_enum_input = soc_camera_enum_input;
vdev->vidioc_g_input = soc_camera_g_input;
vdev->vidioc_s_input = soc_camera_s_input;
vdev->vidioc_s_std = soc_camera_s_std;
vdev->vidioc_reqbufs = soc_camera_reqbufs;
- vdev->vidioc_try_fmt_cap = soc_camera_try_fmt_cap;
+ vdev->vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap;
vdev->vidioc_querybuf = soc_camera_querybuf;
vdev->vidioc_qbuf = soc_camera_qbuf;
vdev->vidioc_dqbuf = soc_camera_dqbuf;
diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c
index b12c60cf5a09..f308c38d744f 100644
--- a/drivers/media/video/stk-webcam.c
+++ b/drivers/media/video/stk-webcam.c
@@ -981,7 +981,7 @@ static int stk_vidioc_s_ctrl(struct file *filp,
}
-static int stk_vidioc_enum_fmt_cap(struct file *filp,
+static int stk_vidioc_enum_fmt_vid_cap(struct file *filp,
void *priv, struct v4l2_fmtdesc *fmtd)
{
fmtd->flags = 0;
@@ -1025,7 +1025,7 @@ static struct stk_size {
{ .w = 176, .h = 144, .m = MODE_QCIF, },
};
-static int stk_vidioc_g_fmt_cap(struct file *filp,
+static int stk_vidioc_g_fmt_vid_cap(struct file *filp,
void *priv, struct v4l2_format *f)
{
struct v4l2_pix_format *pix_format = &f->fmt.pix;
@@ -1054,7 +1054,7 @@ static int stk_vidioc_g_fmt_cap(struct file *filp,
return 0;
}
-static int stk_vidioc_try_fmt_cap(struct file *filp,
+static int stk_vidioc_try_fmt_vid_cap(struct file *filp,
void *priv, struct v4l2_format *fmtd)
{
int i;
@@ -1131,7 +1131,7 @@ static int stk_setup_format(struct stk_camera *dev)
return stk_sensor_configure(dev);
}
-static int stk_vidioc_s_fmt_cap(struct file *filp,
+static int stk_vidioc_s_fmt_vid_cap(struct file *filp,
void *priv, struct v4l2_format *fmtd)
{
int ret;
@@ -1145,7 +1145,7 @@ static int stk_vidioc_s_fmt_cap(struct file *filp,
return -EBUSY;
if (dev->owner && dev->owner != filp)
return -EBUSY;
- ret = stk_vidioc_try_fmt_cap(filp, priv, fmtd);
+ ret = stk_vidioc_try_fmt_vid_cap(filp, priv, fmtd);
if (ret)
return ret;
dev->owner = filp;
@@ -1342,10 +1342,10 @@ static struct video_device stk_v4l_data = {
.release = stk_v4l_dev_release,
.vidioc_querycap = stk_vidioc_querycap,
- .vidioc_enum_fmt_cap = stk_vidioc_enum_fmt_cap,
- .vidioc_try_fmt_cap = stk_vidioc_try_fmt_cap,
- .vidioc_s_fmt_cap = stk_vidioc_s_fmt_cap,
- .vidioc_g_fmt_cap = stk_vidioc_g_fmt_cap,
+ .vidioc_enum_fmt_vid_cap = stk_vidioc_enum_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = stk_vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = stk_vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = stk_vidioc_g_fmt_vid_cap,
.vidioc_enum_input = stk_vidioc_enum_input,
.vidioc_s_input = stk_vidioc_s_input,
.vidioc_g_input = stk_vidioc_g_input,
diff --git a/drivers/media/video/tcm825x.c b/drivers/media/video/tcm825x.c
index 8f0100f67a91..29991d1cf13e 100644
--- a/drivers/media/video/tcm825x.c
+++ b/drivers/media/video/tcm825x.c
@@ -523,6 +523,9 @@ static int ioctl_g_ctrl(struct v4l2_int_device *s,
if (val < 0)
return val;
+ if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP)
+ val ^= sensor->platform_data->is_upside_down();
+
vc->value = val;
return 0;
}
@@ -556,6 +559,9 @@ static int ioctl_s_ctrl(struct v4l2_int_device *s,
if (lvc == NULL)
return -EINVAL;
+ if (vc->id == V4L2_CID_HFLIP || vc->id == V4L2_CID_VFLIP)
+ val ^= sensor->platform_data->is_upside_down();
+
val = val << lvc->start_bit;
if (tcm825x_write_reg_mask(client, lvc->reg, val))
return -EIO;
diff --git a/drivers/media/video/tcm825x.h b/drivers/media/video/tcm825x.h
index 966765b66b3a..770ebacfa344 100644
--- a/drivers/media/video/tcm825x.h
+++ b/drivers/media/video/tcm825x.h
@@ -182,6 +182,7 @@ struct tcm825x_platform_data {
int (*needs_reset)(struct v4l2_int_device *s, void *buf,
struct v4l2_pix_format *fmt);
int (*ifparm)(struct v4l2_ifparm *p);
+ int (*is_upside_down)(void);
};
/* Array of image sizes supported by TCM825X. These must be ordered from
diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c
index d97261ab430f..a9e5e08caec5 100644
--- a/drivers/media/video/usbvision/usbvision-video.c
+++ b/drivers/media/video/usbvision/usbvision-video.c
@@ -998,7 +998,7 @@ static int vidioc_streamoff(struct file *file,
return 0;
}
-static int vidioc_enum_fmt_cap (struct file *file, void *priv,
+static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv,
struct v4l2_fmtdesc *vfd)
{
if(vfd->index>=USBVISION_SUPPORTED_PALETTES-1) {
@@ -1012,7 +1012,7 @@ static int vidioc_enum_fmt_cap (struct file *file, void *priv,
return 0;
}
-static int vidioc_g_fmt_cap (struct file *file, void *priv,
+static int vidioc_g_fmt_vid_cap (struct file *file, void *priv,
struct v4l2_format *vf)
{
struct video_device *dev = video_devdata(file);
@@ -1030,7 +1030,7 @@ static int vidioc_g_fmt_cap (struct file *file, void *priv,
return 0;
}
-static int vidioc_try_fmt_cap (struct file *file, void *priv,
+static int vidioc_try_fmt_vid_cap (struct file *file, void *priv,
struct v4l2_format *vf)
{
struct video_device *dev = video_devdata(file);
@@ -1060,7 +1060,7 @@ static int vidioc_try_fmt_cap (struct file *file, void *priv,
return 0;
}
-static int vidioc_s_fmt_cap(struct file *file, void *priv,
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *vf)
{
struct video_device *dev = video_devdata(file);
@@ -1068,7 +1068,7 @@ static int vidioc_s_fmt_cap(struct file *file, void *priv,
(struct usb_usbvision *) video_get_drvdata(dev);
int ret;
- if( 0 != (ret=vidioc_try_fmt_cap (file, priv, vf)) ) {
+ if( 0 != (ret=vidioc_try_fmt_vid_cap (file, priv, vf)) ) {
return ret;
}
@@ -1407,10 +1407,10 @@ static struct video_device usbvision_video_template = {
.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_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c
index 31e8af0ba278..8da56c1c625f 100644
--- a/drivers/media/video/videodev.c
+++ b/drivers/media/video/videodev.c
@@ -331,6 +331,7 @@ static const char *v4l2_ioctls[] = {
[_IOC_NR(VIDIOC_DBG_G_REGISTER)] = "VIDIOC_DBG_G_REGISTER",
[_IOC_NR(VIDIOC_G_CHIP_IDENT)] = "VIDIOC_G_CHIP_IDENT",
+ [_IOC_NR(VIDIOC_S_HW_FREQ_SEEK)] = "VIDIOC_S_HW_FREQ_SEEK",
#endif
};
#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
@@ -732,35 +733,35 @@ static int check_fmt (struct video_device *vfd, enum v4l2_buf_type type)
{
switch (type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (vfd->vidioc_try_fmt_cap)
+ if (vfd->vidioc_try_fmt_vid_cap)
return (0);
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (vfd->vidioc_try_fmt_overlay)
+ if (vfd->vidioc_try_fmt_vid_overlay)
return (0);
break;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- if (vfd->vidioc_try_fmt_vbi)
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ if (vfd->vidioc_try_fmt_vid_out)
return (0);
break;
- case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- if (vfd->vidioc_try_fmt_vbi_output)
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ if (vfd->vidioc_try_fmt_vid_out_overlay)
return (0);
break;
- case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- if (vfd->vidioc_try_fmt_vbi_capture)
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (vfd->vidioc_try_fmt_vbi_cap)
return (0);
break;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- if (vfd->vidioc_try_fmt_video_output)
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ if (vfd->vidioc_try_fmt_vbi_out)
return (0);
break;
- case V4L2_BUF_TYPE_VBI_OUTPUT:
- if (vfd->vidioc_try_fmt_vbi_output)
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ if (vfd->vidioc_try_fmt_sliced_vbi_cap)
return (0);
break;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
- if (vfd->vidioc_try_fmt_output_overlay)
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ if (vfd->vidioc_try_fmt_sliced_vbi_out)
return (0);
break;
case V4L2_BUF_TYPE_PRIVATE:
@@ -877,46 +878,37 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
switch (type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (vfd->vidioc_enum_fmt_cap)
- ret=vfd->vidioc_enum_fmt_cap(file, fh, f);
+ if (vfd->vidioc_enum_fmt_vid_cap)
+ ret = vfd->vidioc_enum_fmt_vid_cap(file, fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (vfd->vidioc_enum_fmt_overlay)
- ret=vfd->vidioc_enum_fmt_overlay(file, fh, f);
+ if (vfd->vidioc_enum_fmt_vid_overlay)
+ ret = vfd->vidioc_enum_fmt_vid_overlay(file,
+ fh, f);
break;
+#if 1
+ /* V4L2_BUF_TYPE_VBI_CAPTURE should not support VIDIOC_ENUM_FMT
+ * according to the spec. The bttv and saa7134 drivers support
+ * it though, so just warn that this is deprecated and will be
+ * removed in the near future. */
case V4L2_BUF_TYPE_VBI_CAPTURE:
- if (vfd->vidioc_enum_fmt_vbi)
- ret=vfd->vidioc_enum_fmt_vbi(file, fh, f);
- break;
- case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- if (vfd->vidioc_enum_fmt_vbi_output)
- ret=vfd->vidioc_enum_fmt_vbi_output(file,
- fh, f);
- break;
- case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- if (vfd->vidioc_enum_fmt_vbi_capture)
- ret=vfd->vidioc_enum_fmt_vbi_capture(file,
- fh, f);
+ if (vfd->vidioc_enum_fmt_vbi_cap) {
+ printk(KERN_WARNING "vidioc_enum_fmt_vbi_cap will be removed in 2.6.28!\n");
+ ret = vfd->vidioc_enum_fmt_vbi_cap(file, fh, f);
+ }
break;
+#endif
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- if (vfd->vidioc_enum_fmt_video_output)
- ret=vfd->vidioc_enum_fmt_video_output(file,
- fh, f);
- break;
- case V4L2_BUF_TYPE_VBI_OUTPUT:
- if (vfd->vidioc_enum_fmt_vbi_output)
- ret=vfd->vidioc_enum_fmt_vbi_output(file,
- fh, f);
- break;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
- if (vfd->vidioc_enum_fmt_output_overlay)
- ret=vfd->vidioc_enum_fmt_output_overlay(file, fh, f);
+ if (vfd->vidioc_enum_fmt_vid_out)
+ ret = vfd->vidioc_enum_fmt_vid_out(file, fh, f);
break;
case V4L2_BUF_TYPE_PRIVATE:
if (vfd->vidioc_enum_fmt_type_private)
- ret=vfd->vidioc_enum_fmt_type_private(file,
+ ret = vfd->vidioc_enum_fmt_type_private(file,
fh, f);
break;
+ default:
+ break;
}
if (!ret)
dbgarg (cmd, "index=%d, type=%d, flags=%d, "
@@ -932,54 +924,54 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
case VIDIOC_G_FMT:
{
struct v4l2_format *f = (struct v4l2_format *)arg;
- enum v4l2_buf_type type=f->type;
- memset(&f->fmt.pix,0,sizeof(f->fmt.pix));
- f->type=type;
+ memset(f->fmt.raw_data, 0, sizeof(f->fmt.raw_data));
/* FIXME: Should be one dump per type */
- dbgarg (cmd, "type=%s\n", prt_names(type,
- v4l2_type_names));
+ dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names));
- switch (type) {
+ switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (vfd->vidioc_g_fmt_cap)
- ret=vfd->vidioc_g_fmt_cap(file, fh, f);
+ if (vfd->vidioc_g_fmt_vid_cap)
+ ret = vfd->vidioc_g_fmt_vid_cap(file, fh, f);
if (!ret)
v4l_print_pix_fmt(vfd,&f->fmt.pix);
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (vfd->vidioc_g_fmt_overlay)
- ret=vfd->vidioc_g_fmt_overlay(file, fh, f);
- break;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- if (vfd->vidioc_g_fmt_vbi)
- ret=vfd->vidioc_g_fmt_vbi(file, fh, f);
- break;
- case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- if (vfd->vidioc_g_fmt_vbi_output)
- ret=vfd->vidioc_g_fmt_vbi_output(file, fh, f);
- break;
- case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- if (vfd->vidioc_g_fmt_vbi_capture)
- ret=vfd->vidioc_g_fmt_vbi_capture(file, fh, f);
+ if (vfd->vidioc_g_fmt_vid_overlay)
+ ret = vfd->vidioc_g_fmt_vid_overlay(file,
+ fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- if (vfd->vidioc_g_fmt_video_output)
- ret=vfd->vidioc_g_fmt_video_output(file,
- fh, f);
+ if (vfd->vidioc_g_fmt_vid_out)
+ ret = vfd->vidioc_g_fmt_vid_out(file, fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
- if (vfd->vidioc_g_fmt_output_overlay)
- ret=vfd->vidioc_g_fmt_output_overlay(file, fh, f);
+ if (vfd->vidioc_g_fmt_vid_out_overlay)
+ ret = vfd->vidioc_g_fmt_vid_out_overlay(file,
+ fh, f);
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (vfd->vidioc_g_fmt_vbi_cap)
+ ret = vfd->vidioc_g_fmt_vbi_cap(file, fh, f);
break;
case V4L2_BUF_TYPE_VBI_OUTPUT:
- if (vfd->vidioc_g_fmt_vbi_output)
- ret=vfd->vidioc_g_fmt_vbi_output(file, fh, f);
+ if (vfd->vidioc_g_fmt_vbi_out)
+ ret = vfd->vidioc_g_fmt_vbi_out(file, fh, f);
+ break;
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ if (vfd->vidioc_g_fmt_sliced_vbi_cap)
+ ret = vfd->vidioc_g_fmt_sliced_vbi_cap(file,
+ fh, f);
+ break;
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ if (vfd->vidioc_g_fmt_sliced_vbi_out)
+ ret = vfd->vidioc_g_fmt_sliced_vbi_out(file,
+ fh, f);
break;
case V4L2_BUF_TYPE_PRIVATE:
if (vfd->vidioc_g_fmt_type_private)
- ret=vfd->vidioc_g_fmt_type_private(file,
+ ret = vfd->vidioc_g_fmt_type_private(file,
fh, f);
break;
}
@@ -997,42 +989,44 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
v4l_print_pix_fmt(vfd,&f->fmt.pix);
- if (vfd->vidioc_s_fmt_cap)
- ret=vfd->vidioc_s_fmt_cap(file, fh, f);
+ if (vfd->vidioc_s_fmt_vid_cap)
+ ret = vfd->vidioc_s_fmt_vid_cap(file, fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (vfd->vidioc_s_fmt_overlay)
- ret=vfd->vidioc_s_fmt_overlay(file, fh, f);
- break;
- case V4L2_BUF_TYPE_VBI_CAPTURE:
- if (vfd->vidioc_s_fmt_vbi)
- ret=vfd->vidioc_s_fmt_vbi(file, fh, f);
- break;
- case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- if (vfd->vidioc_s_fmt_vbi_output)
- ret=vfd->vidioc_s_fmt_vbi_output(file, fh, f);
- break;
- case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- if (vfd->vidioc_s_fmt_vbi_capture)
- ret=vfd->vidioc_s_fmt_vbi_capture(file, fh, f);
+ if (vfd->vidioc_s_fmt_vid_overlay)
+ ret = vfd->vidioc_s_fmt_vid_overlay(file,
+ fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- if (vfd->vidioc_s_fmt_video_output)
- ret=vfd->vidioc_s_fmt_video_output(file,
- fh, f);
+ if (vfd->vidioc_s_fmt_vid_out)
+ ret = vfd->vidioc_s_fmt_vid_out(file, fh, f);
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
- if (vfd->vidioc_s_fmt_output_overlay)
- ret=vfd->vidioc_s_fmt_output_overlay(file, fh, f);
+ if (vfd->vidioc_s_fmt_vid_out_overlay)
+ ret = vfd->vidioc_s_fmt_vid_out_overlay(file,
+ fh, f);
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (vfd->vidioc_s_fmt_vbi_cap)
+ ret = vfd->vidioc_s_fmt_vbi_cap(file, fh, f);
break;
case V4L2_BUF_TYPE_VBI_OUTPUT:
- if (vfd->vidioc_s_fmt_vbi_output)
- ret=vfd->vidioc_s_fmt_vbi_output(file,
- fh, f);
+ if (vfd->vidioc_s_fmt_vbi_out)
+ ret = vfd->vidioc_s_fmt_vbi_out(file, fh, f);
+ break;
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+ if (vfd->vidioc_s_fmt_sliced_vbi_cap)
+ ret = vfd->vidioc_s_fmt_sliced_vbi_cap(file,
+ fh, f);
+ break;
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ if (vfd->vidioc_s_fmt_sliced_vbi_out)
+ ret = vfd->vidioc_s_fmt_sliced_vbi_out(file,
+ fh, f);
break;
case V4L2_BUF_TYPE_PRIVATE:
if (vfd->vidioc_s_fmt_type_private)
- ret=vfd->vidioc_s_fmt_type_private(file,
+ ret = vfd->vidioc_s_fmt_type_private(file,
fh, f);
break;
}
@@ -1047,46 +1041,46 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
v4l2_type_names));
switch (f->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
- if (vfd->vidioc_try_fmt_cap)
- ret=vfd->vidioc_try_fmt_cap(file, fh, f);
+ if (vfd->vidioc_try_fmt_vid_cap)
+ ret = vfd->vidioc_try_fmt_vid_cap(file, fh, f);
if (!ret)
v4l_print_pix_fmt(vfd,&f->fmt.pix);
break;
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
- if (vfd->vidioc_try_fmt_overlay)
- ret=vfd->vidioc_try_fmt_overlay(file, fh, f);
+ if (vfd->vidioc_try_fmt_vid_overlay)
+ ret = vfd->vidioc_try_fmt_vid_overlay(file,
+ fh, f);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ if (vfd->vidioc_try_fmt_vid_out)
+ ret = vfd->vidioc_try_fmt_vid_out(file, fh, f);
+ break;
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
+ if (vfd->vidioc_try_fmt_vid_out_overlay)
+ ret = vfd->vidioc_try_fmt_vid_out_overlay(file,
+ fh, f);
break;
case V4L2_BUF_TYPE_VBI_CAPTURE:
- if (vfd->vidioc_try_fmt_vbi)
- ret=vfd->vidioc_try_fmt_vbi(file, fh, f);
+ if (vfd->vidioc_try_fmt_vbi_cap)
+ ret = vfd->vidioc_try_fmt_vbi_cap(file, fh, f);
break;
- case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
- if (vfd->vidioc_try_fmt_vbi_output)
- ret=vfd->vidioc_try_fmt_vbi_output(file,
- fh, f);
+ case V4L2_BUF_TYPE_VBI_OUTPUT:
+ if (vfd->vidioc_try_fmt_vbi_out)
+ ret = vfd->vidioc_try_fmt_vbi_out(file, fh, f);
break;
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
- if (vfd->vidioc_try_fmt_vbi_capture)
- ret=vfd->vidioc_try_fmt_vbi_capture(file,
- fh, f);
- break;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
- if (vfd->vidioc_try_fmt_video_output)
- ret=vfd->vidioc_try_fmt_video_output(file,
+ if (vfd->vidioc_try_fmt_sliced_vbi_cap)
+ ret = vfd->vidioc_try_fmt_sliced_vbi_cap(file,
fh, f);
break;
- case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
- if (vfd->vidioc_try_fmt_output_overlay)
- ret=vfd->vidioc_try_fmt_output_overlay(file, fh, f);
- break;
- case V4L2_BUF_TYPE_VBI_OUTPUT:
- if (vfd->vidioc_try_fmt_vbi_output)
- ret=vfd->vidioc_try_fmt_vbi_output(file,
+ case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
+ if (vfd->vidioc_try_fmt_sliced_vbi_out)
+ ret = vfd->vidioc_try_fmt_sliced_vbi_out(file,
fh, f);
break;
case V4L2_BUF_TYPE_PRIVATE:
if (vfd->vidioc_try_fmt_type_private)
- ret=vfd->vidioc_try_fmt_type_private(file,
+ ret = vfd->vidioc_try_fmt_type_private(file,
fh, f);
break;
}
@@ -1313,11 +1307,15 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
{
v4l2_std_id *id = arg;
- *id = vfd->current_norm;
-
- dbgarg (cmd, "value=%08Lx\n", (long long unsigned) *id);
+ ret = 0;
+ /* Calls the specific handler */
+ if (vfd->vidioc_g_std)
+ ret = vfd->vidioc_g_std(file, fh, id);
+ else
+ *id = vfd->current_norm;
- ret=0;
+ if (!ret)
+ dbgarg(cmd, "value=%08Lx\n", (long long unsigned)*id);
break;
}
case VIDIOC_S_STD:
@@ -1400,6 +1398,25 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
}
/* ------ output switching ---------- */
+ case VIDIOC_ENUMOUTPUT:
+ {
+ struct v4l2_output *p = arg;
+ int i = p->index;
+
+ if (!vfd->vidioc_enum_output)
+ break;
+ memset(p, 0, sizeof(*p));
+ p->index = i;
+
+ ret = vfd->vidioc_enum_output(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "index=%d, name=%s, type=%d, "
+ "audioset=%d, "
+ "modulator=%d, std=%08Lx\n",
+ p->index, p->name, p->type, p->audioset,
+ p->modulator, (unsigned long long)p->std);
+ break;
+ }
case VIDIOC_G_OUTPUT:
{
unsigned int *i = arg;
@@ -1792,16 +1809,17 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
}
case VIDIOC_G_FREQUENCY:
{
- struct v4l2_frequency *p=arg;
+ struct v4l2_frequency *p = arg;
+
if (!vfd->vidioc_g_frequency)
break;
- memset(p,0,sizeof(*p));
+ memset(p->reserved, 0, sizeof(p->reserved));
- ret=vfd->vidioc_g_frequency(file, fh, p);
+ ret = vfd->vidioc_g_frequency(file, fh, p);
if (!ret)
- dbgarg (cmd, "tuner=%d, type=%d, frequency=%d\n",
- p->tuner,p->type,p->frequency);
+ dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n",
+ p->tuner, p->type, p->frequency);
break;
}
case VIDIOC_S_FREQUENCY:
@@ -1868,6 +1886,17 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
ret = vfd->vidioc_default(file, fh, cmd, arg);
break;
}
+ case VIDIOC_S_HW_FREQ_SEEK:
+ {
+ struct v4l2_hw_freq_seek *p = arg;
+ if (!vfd->vidioc_s_hw_freq_seek)
+ break;
+ dbgarg(cmd,
+ "tuner=%d, type=%d, seek_upward=%d, wrap_around=%d\n",
+ p->tuner, p->type, p->seek_upward, p->wrap_around);
+ ret = vfd->vidioc_s_hw_freq_seek(file, fh, p);
+ break;
+ }
} /* switch */
if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) {
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index 845be1864f68..e4f5b0ff2fd9 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -39,6 +39,8 @@
#include <linux/highmem.h>
#include <linux/freezer.h>
+#define VIVI_MODULE_NAME "vivi"
+
/* Wake up at about 30 fps */
#define WAKE_NUMERATOR 30
#define WAKE_DENOMINATOR 1001
@@ -47,7 +49,7 @@
#include "font.h"
#define VIVI_MAJOR_VERSION 0
-#define VIVI_MINOR_VERSION 4
+#define VIVI_MINOR_VERSION 5
#define VIVI_RELEASE 0
#define VIVI_VERSION \
KERNEL_VERSION(VIVI_MAJOR_VERSION, VIVI_MINOR_VERSION, VIVI_RELEASE)
@@ -629,7 +631,7 @@ static int vidioc_querycap(struct file *file, void *priv,
return 0;
}
-static int vidioc_enum_fmt_cap(struct file *file, void *priv,
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
if (f->index > 0)
@@ -640,7 +642,7 @@ static int vidioc_enum_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int vidioc_g_fmt_cap(struct file *file, void *priv,
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct vivi_fh *fh = priv;
@@ -657,7 +659,7 @@ static int vidioc_g_fmt_cap(struct file *file, void *priv,
return (0);
}
-static int vidioc_try_fmt_cap(struct file *file, void *priv,
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct vivi_fh *fh = priv;
@@ -705,13 +707,13 @@ static int vidioc_try_fmt_cap(struct file *file, void *priv,
}
/*FIXME: This seems to be generic enough to be at videodev2 */
-static int vidioc_s_fmt_cap(struct file *file, void *priv,
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct vivi_fh *fh = priv;
struct videobuf_queue *q = &fh->vb_vidq;
- int ret = vidioc_try_fmt_cap(file, fh, f);
+ int ret = vidioc_try_fmt_vid_cap(file, fh, f);
if (ret < 0)
return (ret);
@@ -1016,10 +1018,15 @@ static int vivi_release(void)
list_del(list);
dev = list_entry(list, struct vivi_dev, vivi_devlist);
- if (-1 != dev->vfd->minor)
+ if (-1 != dev->vfd->minor) {
video_unregister_device(dev->vfd);
- else
+ printk(KERN_INFO "%s: /dev/video%d unregistered.\n",
+ VIVI_MODULE_NAME, dev->vfd->minor);
+ } else {
video_device_release(dev->vfd);
+ printk(KERN_INFO "%s: /dev/video%d released.\n",
+ VIVI_MODULE_NAME, dev->vfd->minor);
+ }
kfree(dev);
}
@@ -1065,10 +1072,10 @@ static struct video_device vivi_template = {
.release = video_device_release,
.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_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
.vidioc_reqbufs = vidioc_reqbufs,
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
@@ -1130,6 +1137,8 @@ static int __init vivi_init(void)
video_nr++;
dev->vfd = vfd;
+ printk(KERN_INFO "%s: V4L2 device registered as /dev/video%d\n",
+ VIVI_MODULE_NAME, vfd->minor);
}
if (ret < 0) {
@@ -1137,7 +1146,9 @@ static int __init vivi_init(void)
printk(KERN_INFO "Error %d while loading vivi driver\n", ret);
} else
printk(KERN_INFO "Video Technology Magazine Virtual Video "
- "Capture Board successfully loaded.\n");
+ "Capture Board ver %u.%u.%u successfully loaded.\n",
+ (VIVI_VERSION >> 16) & 0xFF, (VIVI_VERSION >> 8) & 0xFF,
+ VIVI_VERSION & 0xFF);
return ret;
}
diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c
index a0e49dc66301..485df2e36132 100644
--- a/drivers/media/video/zr364xx.c
+++ b/drivers/media/video/zr364xx.c
@@ -521,7 +521,7 @@ static int zr364xx_vidioc_g_ctrl(struct file *file, void *priv,
return 0;
}
-static int zr364xx_vidioc_enum_fmt_cap(struct file *file,
+static int zr364xx_vidioc_enum_fmt_vid_cap(struct file *file,
void *priv, struct v4l2_fmtdesc *f)
{
if (f->index > 0)
@@ -537,7 +537,7 @@ static int zr364xx_vidioc_enum_fmt_cap(struct file *file,
return 0;
}
-static int zr364xx_vidioc_try_fmt_cap(struct file *file, void *priv,
+static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct video_device *vdev = video_devdata(file);
@@ -564,7 +564,7 @@ static int zr364xx_vidioc_try_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int zr364xx_vidioc_g_fmt_cap(struct file *file, void *priv,
+static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct video_device *vdev = video_devdata(file);
@@ -589,7 +589,7 @@ static int zr364xx_vidioc_g_fmt_cap(struct file *file, void *priv,
return 0;
}
-static int zr364xx_vidioc_s_fmt_cap(struct file *file, void *priv,
+static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
struct video_device *vdev = video_devdata(file);
@@ -770,10 +770,10 @@ static struct video_device zr364xx_template = {
.minor = -1,
.vidioc_querycap = zr364xx_vidioc_querycap,
- .vidioc_enum_fmt_cap = zr364xx_vidioc_enum_fmt_cap,
- .vidioc_try_fmt_cap = zr364xx_vidioc_try_fmt_cap,
- .vidioc_s_fmt_cap = zr364xx_vidioc_s_fmt_cap,
- .vidioc_g_fmt_cap = zr364xx_vidioc_g_fmt_cap,
+ .vidioc_enum_fmt_vid_cap = zr364xx_vidioc_enum_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = zr364xx_vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = zr364xx_vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = zr364xx_vidioc_g_fmt_vid_cap,
.vidioc_enum_input = zr364xx_vidioc_enum_input,
.vidioc_g_input = zr364xx_vidioc_g_input,
.vidioc_s_input = zr364xx_vidioc_s_input,
diff --git a/drivers/memstick/host/jmb38x_ms.c b/drivers/memstick/host/jmb38x_ms.c
index a054668eda16..4e3bfbcdf155 100644
--- a/drivers/memstick/host/jmb38x_ms.c
+++ b/drivers/memstick/host/jmb38x_ms.c
@@ -51,7 +51,7 @@ struct jmb38x_ms_host {
void __iomem *addr;
spinlock_t lock;
int id;
- char host_id[DEVICE_ID_SIZE];
+ char host_id[32];
int irq;
unsigned int block_pos;
unsigned long timeout_jiffies;
@@ -781,7 +781,7 @@ static struct memstick_host *jmb38x_ms_alloc_host(struct jmb38x_ms *jm, int cnt)
spin_lock_init(&host->lock);
host->id = cnt;
- snprintf(host->host_id, DEVICE_ID_SIZE, DRIVER_NAME ":slot%d",
+ snprintf(host->host_id, sizeof(host->host_id), DRIVER_NAME ":slot%d",
host->id);
host->irq = jm->pdev->irq;
host->timeout_jiffies = msecs_to_jiffies(1000);
diff --git a/drivers/message/fusion/lsi/mpi.h b/drivers/message/fusion/lsi/mpi.h
index 1acbdd61b670..10b6ef758725 100644
--- a/drivers/message/fusion/lsi/mpi.h
+++ b/drivers/message/fusion/lsi/mpi.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000-2007 LSI Corporation.
+ * Copyright (c) 2000-2008 LSI Corporation.
*
*
* Name: mpi.h
diff --git a/drivers/message/fusion/lsi/mpi_cnfg.h b/drivers/message/fusion/lsi/mpi_cnfg.h
index 2bd8adae0f00..b2db3330c591 100644
--- a/drivers/message/fusion/lsi/mpi_cnfg.h
+++ b/drivers/message/fusion/lsi/mpi_cnfg.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000-2007 LSI Corporation.
+ * Copyright (c) 2000-2008 LSI Corporation.
*
*
* Name: mpi_cnfg.h
diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
index db3c892f87fb..a75811014a04 100644
--- a/drivers/message/fusion/mptbase.c
+++ b/drivers/message/fusion/mptbase.c
@@ -5,7 +5,7 @@
* For use with LSI PCI chip/adapter(s)
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
- * Copyright (c) 1999-2007 LSI Corporation
+ * Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/
@@ -103,7 +103,7 @@ static int mfcounter = 0;
* Public data...
*/
-struct proc_dir_entry *mpt_proc_root_dir;
+static struct proc_dir_entry *mpt_proc_root_dir;
#define WHOINIT_UNKNOWN 0xAA
@@ -253,6 +253,55 @@ mpt_get_cb_idx(MPT_DRIVER_CLASS dclass)
return 0;
}
+/**
+ * mpt_fault_reset_work - work performed on workq after ioc fault
+ * @work: input argument, used to derive ioc
+ *
+**/
+static void
+mpt_fault_reset_work(struct work_struct *work)
+{
+ MPT_ADAPTER *ioc =
+ container_of(work, MPT_ADAPTER, fault_reset_work.work);
+ u32 ioc_raw_state;
+ int rc;
+ unsigned long flags;
+
+ if (ioc->diagPending || !ioc->active)
+ goto out;
+
+ ioc_raw_state = mpt_GetIocState(ioc, 0);
+ if ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT) {
+ printk(MYIOC_s_WARN_FMT "IOC is in FAULT state (%04xh)!!!\n",
+ ioc->name, ioc_raw_state & MPI_DOORBELL_DATA_MASK);
+ printk(MYIOC_s_WARN_FMT "Issuing HardReset from %s!!\n",
+ ioc->name, __FUNCTION__);
+ rc = mpt_HardResetHandler(ioc, CAN_SLEEP);
+ printk(MYIOC_s_WARN_FMT "%s: HardReset: %s\n", ioc->name,
+ __FUNCTION__, (rc == 0) ? "success" : "failed");
+ ioc_raw_state = mpt_GetIocState(ioc, 0);
+ if ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT)
+ printk(MYIOC_s_WARN_FMT "IOC is in FAULT state after "
+ "reset (%04xh)\n", ioc->name, ioc_raw_state &
+ MPI_DOORBELL_DATA_MASK);
+ }
+
+ out:
+ /*
+ * Take turns polling alternate controller
+ */
+ if (ioc->alt_ioc)
+ ioc = ioc->alt_ioc;
+
+ /* rearm the timer */
+ spin_lock_irqsave(&ioc->fault_reset_work_lock, flags);
+ if (ioc->reset_work_q)
+ queue_delayed_work(ioc->reset_work_q, &ioc->fault_reset_work,
+ msecs_to_jiffies(MPT_POLLING_INTERVAL));
+ spin_unlock_irqrestore(&ioc->fault_reset_work_lock, flags);
+}
+
+
/*
* Process turbo (context) reply...
*/
@@ -1616,6 +1665,22 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
/* Find lookup slot. */
INIT_LIST_HEAD(&ioc->list);
+
+ /* Initialize workqueue */
+ INIT_DELAYED_WORK(&ioc->fault_reset_work, mpt_fault_reset_work);
+ spin_lock_init(&ioc->fault_reset_work_lock);
+
+ snprintf(ioc->reset_work_q_name, sizeof(ioc->reset_work_q_name), "mpt_poll_%d", ioc->id);
+ ioc->reset_work_q =
+ create_singlethread_workqueue(ioc->reset_work_q_name);
+ if (!ioc->reset_work_q) {
+ printk(MYIOC_s_ERR_FMT "Insufficient memory to add adapter!\n",
+ ioc->name);
+ pci_release_selected_regions(pdev, ioc->bars);
+ kfree(ioc);
+ return -ENOMEM;
+ }
+
dinitprintk(ioc, printk(MYIOC_s_INFO_FMT "facts @ %p, pfacts[0] @ %p\n",
ioc->name, &ioc->facts, &ioc->pfacts[0]));
@@ -1722,6 +1787,10 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
iounmap(ioc->memmap);
if (r != -5)
pci_release_selected_regions(pdev, ioc->bars);
+
+ destroy_workqueue(ioc->reset_work_q);
+ ioc->reset_work_q = NULL;
+
kfree(ioc);
pci_set_drvdata(pdev, NULL);
return r;
@@ -1754,6 +1823,10 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
}
#endif
+ if (!ioc->alt_ioc)
+ queue_delayed_work(ioc->reset_work_q, &ioc->fault_reset_work,
+ msecs_to_jiffies(MPT_POLLING_INTERVAL));
+
return 0;
}
@@ -1769,6 +1842,19 @@ mpt_detach(struct pci_dev *pdev)
MPT_ADAPTER *ioc = pci_get_drvdata(pdev);
char pname[32];
u8 cb_idx;
+ unsigned long flags;
+ struct workqueue_struct *wq;
+
+ /*
+ * Stop polling ioc for fault condition
+ */
+ spin_lock_irqsave(&ioc->fault_reset_work_lock, flags);
+ wq = ioc->reset_work_q;
+ ioc->reset_work_q = NULL;
+ spin_unlock_irqrestore(&ioc->fault_reset_work_lock, flags);
+ cancel_delayed_work(&ioc->fault_reset_work);
+ destroy_workqueue(wq);
+
sprintf(pname, MPT_PROCFS_MPTBASEDIR "/%s/summary", ioc->name);
remove_proc_entry(pname, NULL);
@@ -2062,7 +2148,8 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
if ((ret == 0) && (reason == MPT_HOSTEVENT_IOC_BRINGUP)) {
ioc->pci_irq = -1;
if (ioc->pcidev->irq) {
- if (ioc->msi_enable && !pci_enable_msi(ioc->pcidev))
+ if (ioc->msi_enable == 1 &&
+ !pci_enable_msi(ioc->pcidev))
printk(MYIOC_s_INFO_FMT "PCI-MSI enabled\n",
ioc->name);
else
@@ -2072,7 +2159,7 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
if (rc < 0) {
printk(MYIOC_s_ERR_FMT "Unable to allocate "
"interrupt %d!\n", ioc->name, ioc->pcidev->irq);
- if (ioc->msi_enable)
+ if (ioc->msi_enable == 1)
pci_disable_msi(ioc->pcidev);
return -EBUSY;
}
@@ -2268,7 +2355,7 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
out:
if ((ret != 0) && irq_allocated) {
free_irq(ioc->pci_irq, ioc);
- if (ioc->msi_enable)
+ if (ioc->msi_enable == 1)
pci_disable_msi(ioc->pcidev);
}
return ret;
@@ -2450,7 +2537,7 @@ mpt_adapter_dispose(MPT_ADAPTER *ioc)
if (ioc->pci_irq != -1) {
free_irq(ioc->pci_irq, ioc);
- if (ioc->msi_enable)
+ if (ioc->msi_enable == 1)
pci_disable_msi(ioc->pcidev);
ioc->pci_irq = -1;
}
@@ -7451,7 +7538,6 @@ EXPORT_SYMBOL(mpt_resume);
EXPORT_SYMBOL(mpt_suspend);
#endif
EXPORT_SYMBOL(ioc_list);
-EXPORT_SYMBOL(mpt_proc_root_dir);
EXPORT_SYMBOL(mpt_register);
EXPORT_SYMBOL(mpt_deregister);
EXPORT_SYMBOL(mpt_event_register);
diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h
index a8f617447d22..dff048cfa101 100644
--- a/drivers/message/fusion/mptbase.h
+++ b/drivers/message/fusion/mptbase.h
@@ -5,7 +5,7 @@
* LSIFC9xx/LSI409xx Fibre Channel
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
- * Copyright (c) 1999-2007 LSI Corporation
+ * Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/
@@ -73,11 +73,11 @@
#endif
#ifndef COPYRIGHT
-#define COPYRIGHT "Copyright (c) 1999-2007 " MODULEAUTHOR
+#define COPYRIGHT "Copyright (c) 1999-2008 " MODULEAUTHOR
#endif
-#define MPT_LINUX_VERSION_COMMON "3.04.06"
-#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.06"
+#define MPT_LINUX_VERSION_COMMON "3.04.07"
+#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-3.04.07"
#define WHAT_MAGIC_STRING "@" "(" "#" ")"
#define show_mptmod_ver(s,ver) \
@@ -176,6 +176,8 @@
/* debug print string length used for events and iocstatus */
# define EVENT_DESCR_STR_SZ 100
+#define MPT_POLLING_INTERVAL 1000 /* in milliseconds */
+
#ifdef __KERNEL__ /* { */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -705,10 +707,16 @@ typedef struct _MPT_ADAPTER
u8 fc_link_speed[2];
spinlock_t fc_rescan_work_lock;
struct work_struct fc_rescan_work;
- char fc_rescan_work_q_name[KOBJ_NAME_LEN];
+ char fc_rescan_work_q_name[20];
struct workqueue_struct *fc_rescan_work_q;
struct scsi_cmnd **ScsiLookup;
spinlock_t scsi_lookup_lock;
+
+ char reset_work_q_name[20];
+ struct workqueue_struct *reset_work_q;
+ struct delayed_work fault_reset_work;
+ spinlock_t fault_reset_work_lock;
+
} MPT_ADAPTER;
/*
@@ -919,7 +927,6 @@ extern int mpt_raid_phys_disk_pg0(MPT_ADAPTER *ioc, u8 phys_disk_num, pRaidPhys
* Public data decl's...
*/
extern struct list_head ioc_list;
-extern struct proc_dir_entry *mpt_proc_root_dir;
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
#endif /* } __KERNEL__ */
diff --git a/drivers/message/fusion/mptctl.c b/drivers/message/fusion/mptctl.c
index e630b50966ec..68c844b2859d 100644
--- a/drivers/message/fusion/mptctl.c
+++ b/drivers/message/fusion/mptctl.c
@@ -4,7 +4,7 @@
* For use with LSI PCI chip/adapters
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
- * Copyright (c) 1999-2007 LSI Corporation
+ * Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/
@@ -66,7 +66,7 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
-#define COPYRIGHT "Copyright (c) 1999-2007 LSI Corporation"
+#define COPYRIGHT "Copyright (c) 1999-2008 LSI Corporation"
#define MODULEAUTHOR "LSI Corporation"
#include "mptbase.h"
#include "mptctl.h"
diff --git a/drivers/message/fusion/mptctl.h b/drivers/message/fusion/mptctl.h
index 2c1890127e15..d564cc9ada6a 100644
--- a/drivers/message/fusion/mptctl.h
+++ b/drivers/message/fusion/mptctl.h
@@ -5,7 +5,7 @@
* LSIFC9xx/LSI409xx Fibre Channel
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
- * Copyright (c) 1999-2007 LSI Corporation
+ * Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/
diff --git a/drivers/message/fusion/mptdebug.h b/drivers/message/fusion/mptdebug.h
index ffdb0a6191b4..510b9f492093 100644
--- a/drivers/message/fusion/mptdebug.h
+++ b/drivers/message/fusion/mptdebug.h
@@ -3,7 +3,7 @@
* For use with LSI PCI chip/adapter(s)
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
- * Copyright (c) 1999-2007 LSI Corporation
+ * Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/
diff --git a/drivers/message/fusion/mptfc.c b/drivers/message/fusion/mptfc.c
index 1e24ab4ac38c..b36cae9ec6db 100644
--- a/drivers/message/fusion/mptfc.c
+++ b/drivers/message/fusion/mptfc.c
@@ -3,7 +3,7 @@
* For use with LSI PCI chip/adapter(s)
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
- * Copyright (c) 1999-2007 LSI Corporation
+ * Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/
@@ -1326,8 +1326,8 @@ mptfc_probe(struct pci_dev *pdev, const struct pci_device_id *id)
/* initialize workqueue */
- snprintf(ioc->fc_rescan_work_q_name, KOBJ_NAME_LEN, "mptfc_wq_%d",
- sh->host_no);
+ snprintf(ioc->fc_rescan_work_q_name, sizeof(ioc->fc_rescan_work_q_name),
+ "mptfc_wq_%d", sh->host_no);
ioc->fc_rescan_work_q =
create_singlethread_workqueue(ioc->fc_rescan_work_q_name);
if (!ioc->fc_rescan_work_q)
diff --git a/drivers/message/fusion/mptlan.c b/drivers/message/fusion/mptlan.c
index 7950fc678ed1..d709d92b7b30 100644
--- a/drivers/message/fusion/mptlan.c
+++ b/drivers/message/fusion/mptlan.c
@@ -4,7 +4,7 @@
* For use with LSI Fibre Channel PCI chip/adapters
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
- * Copyright (c) 2000-2007 LSI Corporation
+ * Copyright (c) 2000-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/
diff --git a/drivers/message/fusion/mptlan.h b/drivers/message/fusion/mptlan.h
index bafb67fc8181..33927ee7dc3b 100644
--- a/drivers/message/fusion/mptlan.h
+++ b/drivers/message/fusion/mptlan.h
@@ -4,7 +4,7 @@
* For use with LSI Fibre Channel PCI chip/adapters
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
- * Copyright (c) 2000-2007 LSI Corporation
+ * Copyright (c) 2000-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/
diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c
index 4d492ba232b0..b1147aa7afde 100644
--- a/drivers/message/fusion/mptsas.c
+++ b/drivers/message/fusion/mptsas.c
@@ -3,7 +3,7 @@
* For use with LSI PCI chip/adapter(s)
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
- * Copyright (c) 1999-2007 LSI Corporation
+ * Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
diff --git a/drivers/message/fusion/mptsas.h b/drivers/message/fusion/mptsas.h
index 7c150f50629a..2b544e0877e6 100644
--- a/drivers/message/fusion/mptsas.h
+++ b/drivers/message/fusion/mptsas.h
@@ -5,7 +5,7 @@
* LSIFC9xx/LSI409xx Fibre Channel
* running LSI MPT (Message Passing Technology) firmware.
*
- * Copyright (c) 1999-2007 LSI Corporation
+ * Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/
diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c
index c68ef00c2f92..d142b6b4b976 100644
--- a/drivers/message/fusion/mptscsih.c
+++ b/drivers/message/fusion/mptscsih.c
@@ -3,7 +3,7 @@
* For use with LSI PCI chip/adapter(s)
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
- * Copyright (c) 1999-2007 LSI Corporation
+ * Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/
diff --git a/drivers/message/fusion/mptscsih.h b/drivers/message/fusion/mptscsih.h
index 7ea7da0e090c..319aa3033371 100644
--- a/drivers/message/fusion/mptscsih.h
+++ b/drivers/message/fusion/mptscsih.h
@@ -5,7 +5,7 @@
* LSIFC9xx/LSI409xx Fibre Channel
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
- * Copyright (c) 1999-2007 LSI Corporation
+ * Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/
diff --git a/drivers/message/fusion/mptspi.c b/drivers/message/fusion/mptspi.c
index 25bcfcf36f2e..9b49516cf5a0 100644
--- a/drivers/message/fusion/mptspi.c
+++ b/drivers/message/fusion/mptspi.c
@@ -3,7 +3,7 @@
* For use with LSI PCI chip/adapter(s)
* running LSI Fusion MPT (Message Passing Technology) firmware.
*
- * Copyright (c) 1999-2007 LSI Corporation
+ * Copyright (c) 1999-2008 LSI Corporation
* (mailto:DL-MPTFusionLinux@lsi.com)
*
*/
@@ -447,6 +447,7 @@ static int mptspi_target_alloc(struct scsi_target *starget)
spi_max_offset(starget) = ioc->spi_data.maxSyncOffset;
spi_offset(starget) = 0;
+ spi_period(starget) = 0xFF;
mptspi_write_width(starget, 0);
return 0;
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 636af2862308..9dd836ea5fa2 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -219,6 +219,23 @@ config MSI_LAPTOP
If you have an MSI S270 laptop, say Y or M here.
+config COMPAL_LAPTOP
+ tristate "Compal Laptop Extras"
+ depends on X86
+ depends on ACPI_EC
+ depends on BACKLIGHT_CLASS_DEVICE
+ ---help---
+ This is a driver for laptops built by Compal:
+
+ Compal FL90/IFL90
+ Compal FL91/IFL91
+ Compal FL92/JFL92
+ Compal FT00/IFT00
+
+ It adds support for Bluetooth, WLAN and LCD brightness control.
+
+ If you have an Compal FL9x/IFL9x/FT00 laptop, say Y or M here.
+
config SONY_LAPTOP
tristate "Sony Laptop Extras"
depends on X86 && ACPI
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 1952875a272e..c818320d1dc7 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -5,15 +5,15 @@ obj- := misc.o # Dummy rule to force built-in.o to be made
obj-$(CONFIG_IBM_ASM) += ibmasm/
obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/
-obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
-obj-$(CONFIG_ACER_WMI) += acer-wmi.o
obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o
obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o
+obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
+obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o
+obj-$(CONFIG_ACER_WMI) += acer-wmi.o
obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o
obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o
obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o
-obj-$(CONFIG_LKDTM) += lkdtm.o
obj-$(CONFIG_TIFM_CORE) += tifm_core.o
obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
obj-$(CONFIG_PHANTOM) += phantom.o
diff --git a/drivers/misc/compal-laptop.c b/drivers/misc/compal-laptop.c
new file mode 100644
index 000000000000..b764c55c3365
--- /dev/null
+++ b/drivers/misc/compal-laptop.c
@@ -0,0 +1,437 @@
+/*-*-linux-c-*-*/
+
+/*
+ Copyright (C) 2008 Cezary Jackiewicz <cezary.jackiewicz (at) gmail.com>
+
+ based on MSI driver
+
+ Copyright (C) 2006 Lennart Poettering <mzxreary (at) 0pointer (dot) 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+ */
+
+/*
+ * comapl-laptop.c - Compal laptop support.
+ *
+ * This driver exports a few files in /sys/devices/platform/compal-laptop/:
+ *
+ * lcd_level - Screen brightness: contains a single integer in the
+ * range 0..7. (rw)
+ *
+ * wlan - wlan subsystem state: contains 0 or 1 (rw)
+ *
+ * bluetooth - Bluetooth subsystem state: contains 0 or 1 (rw)
+ *
+ * raw - raw value taken from embedded controller register (ro)
+ *
+ * In addition to these platform device attributes the driver
+ * registers itself in the Linux backlight control subsystem and is
+ * available to userspace under /sys/class/backlight/compal-laptop/.
+ *
+ * This driver might work on other laptops produced by Compal. If you
+ * want to try it you can pass force=1 as argument to the module which
+ * will force it to load even when the DMI data doesn't identify the
+ * laptop as IFL90.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/backlight.h>
+#include <linux/platform_device.h>
+#include <linux/autoconf.h>
+
+#define COMPAL_DRIVER_VERSION "0.2.5"
+
+#define COMPAL_LCD_LEVEL_MAX 8
+
+#define COMPAL_EC_COMMAND_WIRELESS 0xBB
+#define COMPAL_EC_COMMAND_LCD_LEVEL 0xB9
+
+#define KILLSWITCH_MASK 0x10
+#define WLAN_MASK 0x01
+#define BT_MASK 0x02
+
+static int force;
+module_param(force, bool, 0);
+MODULE_PARM_DESC(force, "Force driver load, ignore DMI data");
+
+/* Hardware access */
+
+static int set_lcd_level(int level)
+{
+ if (level < 0 || level >= COMPAL_LCD_LEVEL_MAX)
+ return -EINVAL;
+
+ ec_write(COMPAL_EC_COMMAND_LCD_LEVEL, level);
+
+ return 0;
+}
+
+static int get_lcd_level(void)
+{
+ u8 result;
+
+ ec_read(COMPAL_EC_COMMAND_LCD_LEVEL, &result);
+
+ return (int) result;
+}
+
+static int set_wlan_state(int state)
+{
+ u8 result, value;
+
+ ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
+
+ if ((result & KILLSWITCH_MASK) == 0)
+ return -EINVAL;
+ else {
+ if (state)
+ value = (u8) (result | WLAN_MASK);
+ else
+ value = (u8) (result & ~WLAN_MASK);
+ ec_write(COMPAL_EC_COMMAND_WIRELESS, value);
+ }
+
+ return 0;
+}
+
+static int set_bluetooth_state(int state)
+{
+ u8 result, value;
+
+ ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
+
+ if ((result & KILLSWITCH_MASK) == 0)
+ return -EINVAL;
+ else {
+ if (state)
+ value = (u8) (result | BT_MASK);
+ else
+ value = (u8) (result & ~BT_MASK);
+ ec_write(COMPAL_EC_COMMAND_WIRELESS, value);
+ }
+
+ return 0;
+}
+
+static int get_wireless_state(int *wlan, int *bluetooth)
+{
+ u8 result;
+
+ ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
+
+ if (wlan) {
+ if ((result & KILLSWITCH_MASK) == 0)
+ *wlan = 0;
+ else
+ *wlan = result & WLAN_MASK;
+ }
+
+ if (bluetooth) {
+ if ((result & KILLSWITCH_MASK) == 0)
+ *bluetooth = 0;
+ else
+ *bluetooth = (result & BT_MASK) >> 1;
+ }
+
+ return 0;
+}
+
+/* Backlight device stuff */
+
+static int bl_get_brightness(struct backlight_device *b)
+{
+ return get_lcd_level();
+}
+
+
+static int bl_update_status(struct backlight_device *b)
+{
+ return set_lcd_level(b->props.brightness);
+}
+
+static struct backlight_ops compalbl_ops = {
+ .get_brightness = bl_get_brightness,
+ .update_status = bl_update_status,
+};
+
+static struct backlight_device *compalbl_device;
+
+/* Platform device */
+
+static ssize_t show_wlan(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret, enabled;
+
+ ret = get_wireless_state(&enabled, NULL);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%i\n", enabled);
+}
+
+static ssize_t show_raw(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ u8 result;
+
+ ec_read(COMPAL_EC_COMMAND_WIRELESS, &result);
+
+ return sprintf(buf, "%i\n", result);
+}
+
+static ssize_t show_bluetooth(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret, enabled;
+
+ ret = get_wireless_state(NULL, &enabled);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%i\n", enabled);
+}
+
+static ssize_t show_lcd_level(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int ret;
+
+ ret = get_lcd_level();
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%i\n", ret);
+}
+
+static ssize_t store_lcd_level(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int level, ret;
+
+ if (sscanf(buf, "%i", &level) != 1 ||
+ (level < 0 || level >= COMPAL_LCD_LEVEL_MAX))
+ return -EINVAL;
+
+ ret = set_lcd_level(level);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t store_wlan_state(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int state, ret;
+
+ if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1))
+ return -EINVAL;
+
+ ret = set_wlan_state(state);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static ssize_t store_bluetooth_state(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int state, ret;
+
+ if (sscanf(buf, "%i", &state) != 1 || (state < 0 || state > 1))
+ return -EINVAL;
+
+ ret = set_bluetooth_state(state);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);
+static DEVICE_ATTR(bluetooth, 0644, show_bluetooth, store_bluetooth_state);
+static DEVICE_ATTR(wlan, 0644, show_wlan, store_wlan_state);
+static DEVICE_ATTR(raw, 0444, show_raw, NULL);
+
+static struct attribute *compal_attributes[] = {
+ &dev_attr_lcd_level.attr,
+ &dev_attr_bluetooth.attr,
+ &dev_attr_wlan.attr,
+ &dev_attr_raw.attr,
+ NULL
+};
+
+static struct attribute_group compal_attribute_group = {
+ .attrs = compal_attributes
+};
+
+static struct platform_driver compal_driver = {
+ .driver = {
+ .name = "compal-laptop",
+ .owner = THIS_MODULE,
+ }
+};
+
+static struct platform_device *compal_device;
+
+/* Initialization */
+
+static int dmi_check_cb(const struct dmi_system_id *id)
+{
+ printk(KERN_INFO "compal-laptop: Identified laptop model '%s'.\n",
+ id->ident);
+
+ return 0;
+}
+
+static struct dmi_system_id __initdata compal_dmi_table[] = {
+ {
+ .ident = "FL90/IFL90",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
+ DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
+ },
+ .callback = dmi_check_cb
+ },
+ {
+ .ident = "FL90/IFL90",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "IFL90"),
+ DMI_MATCH(DMI_BOARD_VERSION, "REFERENCE"),
+ },
+ .callback = dmi_check_cb
+ },
+ {
+ .ident = "FL91/IFL91",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "IFL91"),
+ DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
+ },
+ .callback = dmi_check_cb
+ },
+ {
+ .ident = "FL92/JFL92",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "JFL92"),
+ DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
+ },
+ .callback = dmi_check_cb
+ },
+ {
+ .ident = "FT00/IFT00",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "IFT00"),
+ DMI_MATCH(DMI_BOARD_VERSION, "IFT00"),
+ },
+ .callback = dmi_check_cb
+ },
+ { }
+};
+
+static int __init compal_init(void)
+{
+ int ret;
+
+ if (acpi_disabled)
+ return -ENODEV;
+
+ if (!force && !dmi_check_system(compal_dmi_table))
+ return -ENODEV;
+
+ /* Register backlight stuff */
+
+ compalbl_device = backlight_device_register("compal-laptop", NULL, NULL,
+ &compalbl_ops);
+ if (IS_ERR(compalbl_device))
+ return PTR_ERR(compalbl_device);
+
+ compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1;
+
+ ret = platform_driver_register(&compal_driver);
+ if (ret)
+ goto fail_backlight;
+
+ /* Register platform stuff */
+
+ compal_device = platform_device_alloc("compal-laptop", -1);
+ if (!compal_device) {
+ ret = -ENOMEM;
+ goto fail_platform_driver;
+ }
+
+ ret = platform_device_add(compal_device);
+ if (ret)
+ goto fail_platform_device1;
+
+ ret = sysfs_create_group(&compal_device->dev.kobj,
+ &compal_attribute_group);
+ if (ret)
+ goto fail_platform_device2;
+
+ printk(KERN_INFO "compal-laptop: driver "COMPAL_DRIVER_VERSION
+ " successfully loaded.\n");
+
+ return 0;
+
+fail_platform_device2:
+
+ platform_device_del(compal_device);
+
+fail_platform_device1:
+
+ platform_device_put(compal_device);
+
+fail_platform_driver:
+
+ platform_driver_unregister(&compal_driver);
+
+fail_backlight:
+
+ backlight_device_unregister(compalbl_device);
+
+ return ret;
+}
+
+static void __exit compal_cleanup(void)
+{
+
+ sysfs_remove_group(&compal_device->dev.kobj, &compal_attribute_group);
+ platform_device_unregister(compal_device);
+ platform_driver_unregister(&compal_driver);
+ backlight_device_unregister(compalbl_device);
+
+ printk(KERN_INFO "compal-laptop: driver unloaded.\n");
+}
+
+module_init(compal_init);
+module_exit(compal_cleanup);
+
+MODULE_AUTHOR("Cezary Jackiewicz");
+MODULE_DESCRIPTION("Compal Laptop Support");
+MODULE_VERSION(COMPAL_DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("dmi:*:rnIFL90:rvrIFT00:*");
+MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*");
+MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*");
+MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*");
+MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*");
diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c
index 71d1c84e2fa8..024e8a989709 100644
--- a/drivers/misc/phantom.c
+++ b/drivers/misc/phantom.c
@@ -394,8 +394,9 @@ static int __devinit phantom_probe(struct pci_dev *pdev,
goto err_irq;
}
- if (IS_ERR(device_create(phantom_class, &pdev->dev, MKDEV(phantom_major,
- minor), "phantom%u", minor)))
+ if (IS_ERR(device_create_drvdata(phantom_class, &pdev->dev,
+ MKDEV(phantom_major, minor),
+ NULL, "phantom%u", minor)))
dev_err(&pdev->dev, "can't create device\n");
pci_set_drvdata(pdev, pht);
diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c
index a0ce0b2fa03e..b5969298f3d3 100644
--- a/drivers/misc/thinkpad_acpi.c
+++ b/drivers/misc/thinkpad_acpi.c
@@ -1293,7 +1293,7 @@ static void tpacpi_input_send_radiosw(void)
mutex_lock(&tpacpi_inputdev_send_mutex);
input_report_switch(tpacpi_inputdev,
- SW_RADIO, !!wlsw);
+ SW_RFKILL_ALL, !!wlsw);
input_sync(tpacpi_inputdev);
mutex_unlock(&tpacpi_inputdev_send_mutex);
@@ -1921,6 +1921,29 @@ static struct attribute *hotkey_mask_attributes[] __initdata = {
&dev_attr_hotkey_wakeup_hotunplug_complete.attr,
};
+static void hotkey_exit(void)
+{
+#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
+ hotkey_poll_stop_sync();
+#endif
+
+ if (hotkey_dev_attributes)
+ delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
+
+ kfree(hotkey_keycode_map);
+
+ if (tp_features.hotkey) {
+ dbg_printk(TPACPI_DBG_EXIT,
+ "restoring original hot key mask\n");
+ /* no short-circuit boolean operator below! */
+ if ((hotkey_mask_set(hotkey_orig_mask) |
+ hotkey_status_set(hotkey_orig_status)) != 0)
+ printk(TPACPI_ERR
+ "failed to restore hot key mask "
+ "to BIOS defaults\n");
+ }
+}
+
static int __init hotkey_init(struct ibm_init_struct *iibm)
{
/* Requirements for changing the default keymaps:
@@ -2060,226 +2083,220 @@ static int __init hotkey_init(struct ibm_init_struct *iibm)
vdbg_printk(TPACPI_DBG_INIT, "hotkeys are %s\n",
str_supported(tp_features.hotkey));
- if (tp_features.hotkey) {
- hotkey_dev_attributes = create_attr_set(13, NULL);
- if (!hotkey_dev_attributes)
- return -ENOMEM;
- res = add_many_to_attr_set(hotkey_dev_attributes,
- hotkey_attributes,
- ARRAY_SIZE(hotkey_attributes));
- if (res)
- return res;
+ if (!tp_features.hotkey)
+ return 1;
- /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
- A30, R30, R31, T20-22, X20-21, X22-24. Detected by checking
- for HKEY interface version 0x100 */
- if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
- if ((hkeyv >> 8) != 1) {
- printk(TPACPI_ERR "unknown version of the "
- "HKEY interface: 0x%x\n", hkeyv);
- printk(TPACPI_ERR "please report this to %s\n",
- TPACPI_MAIL);
- } else {
- /*
- * MHKV 0x100 in A31, R40, R40e,
- * T4x, X31, and later
- */
- tp_features.hotkey_mask = 1;
- }
+ hotkey_dev_attributes = create_attr_set(13, NULL);
+ if (!hotkey_dev_attributes)
+ return -ENOMEM;
+ res = add_many_to_attr_set(hotkey_dev_attributes,
+ hotkey_attributes,
+ ARRAY_SIZE(hotkey_attributes));
+ if (res)
+ goto err_exit;
+
+ /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,
+ A30, R30, R31, T20-22, X20-21, X22-24. Detected by checking
+ for HKEY interface version 0x100 */
+ if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) {
+ if ((hkeyv >> 8) != 1) {
+ printk(TPACPI_ERR "unknown version of the "
+ "HKEY interface: 0x%x\n", hkeyv);
+ printk(TPACPI_ERR "please report this to %s\n",
+ TPACPI_MAIL);
+ } else {
+ /*
+ * MHKV 0x100 in A31, R40, R40e,
+ * T4x, X31, and later
+ */
+ tp_features.hotkey_mask = 1;
}
+ }
- vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n",
- str_supported(tp_features.hotkey_mask));
+ vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n",
+ str_supported(tp_features.hotkey_mask));
- if (tp_features.hotkey_mask) {
- if (!acpi_evalf(hkey_handle, &hotkey_all_mask,
- "MHKA", "qd")) {
- printk(TPACPI_ERR
- "missing MHKA handler, "
- "please report this to %s\n",
- TPACPI_MAIL);
- /* FN+F12, FN+F4, FN+F3 */
- hotkey_all_mask = 0x080cU;
- }
+ if (tp_features.hotkey_mask) {
+ if (!acpi_evalf(hkey_handle, &hotkey_all_mask,
+ "MHKA", "qd")) {
+ printk(TPACPI_ERR
+ "missing MHKA handler, "
+ "please report this to %s\n",
+ TPACPI_MAIL);
+ /* FN+F12, FN+F4, FN+F3 */
+ hotkey_all_mask = 0x080cU;
}
+ }
- /* hotkey_source_mask *must* be zero for
- * the first hotkey_mask_get */
- res = hotkey_status_get(&hotkey_orig_status);
- if (!res && tp_features.hotkey_mask) {
- res = hotkey_mask_get();
- hotkey_orig_mask = hotkey_mask;
- if (!res) {
- res = add_many_to_attr_set(
- hotkey_dev_attributes,
- hotkey_mask_attributes,
- ARRAY_SIZE(hotkey_mask_attributes));
- }
- }
+ /* hotkey_source_mask *must* be zero for
+ * the first hotkey_mask_get */
+ res = hotkey_status_get(&hotkey_orig_status);
+ if (res)
+ goto err_exit;
+
+ if (tp_features.hotkey_mask) {
+ res = hotkey_mask_get();
+ if (res)
+ goto err_exit;
+
+ hotkey_orig_mask = hotkey_mask;
+ res = add_many_to_attr_set(
+ hotkey_dev_attributes,
+ hotkey_mask_attributes,
+ ARRAY_SIZE(hotkey_mask_attributes));
+ if (res)
+ goto err_exit;
+ }
#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
- if (tp_features.hotkey_mask) {
- hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK
- & ~hotkey_all_mask;
- } else {
- hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK;
- }
+ if (tp_features.hotkey_mask) {
+ hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK
+ & ~hotkey_all_mask;
+ } else {
+ hotkey_source_mask = TPACPI_HKEY_NVRAM_GOOD_MASK;
+ }
- vdbg_printk(TPACPI_DBG_INIT,
- "hotkey source mask 0x%08x, polling freq %d\n",
- hotkey_source_mask, hotkey_poll_freq);
+ vdbg_printk(TPACPI_DBG_INIT,
+ "hotkey source mask 0x%08x, polling freq %d\n",
+ hotkey_source_mask, hotkey_poll_freq);
#endif
- /* Not all thinkpads have a hardware radio switch */
- if (!res && acpi_evalf(hkey_handle, &status, "WLSW", "qd")) {
- tp_features.hotkey_wlsw = 1;
- printk(TPACPI_INFO
- "radio switch found; radios are %s\n",
- enabled(status, 0));
- res = add_to_attr_set(hotkey_dev_attributes,
- &dev_attr_hotkey_radio_sw.attr);
- }
+ /* Not all thinkpads have a hardware radio switch */
+ if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) {
+ tp_features.hotkey_wlsw = 1;
+ printk(TPACPI_INFO
+ "radio switch found; radios are %s\n",
+ enabled(status, 0));
+ res = add_to_attr_set(hotkey_dev_attributes,
+ &dev_attr_hotkey_radio_sw.attr);
+ }
- /* For X41t, X60t, X61t Tablets... */
- if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) {
- tp_features.hotkey_tablet = 1;
- printk(TPACPI_INFO
- "possible tablet mode switch found; "
- "ThinkPad in %s mode\n",
- (status & TP_HOTKEY_TABLET_MASK)?
- "tablet" : "laptop");
- res = add_to_attr_set(hotkey_dev_attributes,
- &dev_attr_hotkey_tablet_mode.attr);
- }
+ /* For X41t, X60t, X61t Tablets... */
+ if (!res && acpi_evalf(hkey_handle, &status, "MHKG", "qd")) {
+ tp_features.hotkey_tablet = 1;
+ printk(TPACPI_INFO
+ "possible tablet mode switch found; "
+ "ThinkPad in %s mode\n",
+ (status & TP_HOTKEY_TABLET_MASK)?
+ "tablet" : "laptop");
+ res = add_to_attr_set(hotkey_dev_attributes,
+ &dev_attr_hotkey_tablet_mode.attr);
+ }
- if (!res)
- res = register_attr_set_with_sysfs(
- hotkey_dev_attributes,
- &tpacpi_pdev->dev.kobj);
- if (res)
- return res;
+ if (!res)
+ res = register_attr_set_with_sysfs(
+ hotkey_dev_attributes,
+ &tpacpi_pdev->dev.kobj);
+ if (res)
+ goto err_exit;
- /* Set up key map */
+ /* Set up key map */
- hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE,
- GFP_KERNEL);
- if (!hotkey_keycode_map) {
- printk(TPACPI_ERR
- "failed to allocate memory for key map\n");
- return -ENOMEM;
- }
+ hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE,
+ GFP_KERNEL);
+ if (!hotkey_keycode_map) {
+ printk(TPACPI_ERR
+ "failed to allocate memory for key map\n");
+ res = -ENOMEM;
+ goto err_exit;
+ }
- if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) {
- dbg_printk(TPACPI_DBG_INIT,
- "using Lenovo default hot key map\n");
- memcpy(hotkey_keycode_map, &lenovo_keycode_map,
- TPACPI_HOTKEY_MAP_SIZE);
+ if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) {
+ dbg_printk(TPACPI_DBG_INIT,
+ "using Lenovo default hot key map\n");
+ memcpy(hotkey_keycode_map, &lenovo_keycode_map,
+ TPACPI_HOTKEY_MAP_SIZE);
+ } else {
+ dbg_printk(TPACPI_DBG_INIT,
+ "using IBM default hot key map\n");
+ memcpy(hotkey_keycode_map, &ibm_keycode_map,
+ TPACPI_HOTKEY_MAP_SIZE);
+ }
+
+ set_bit(EV_KEY, tpacpi_inputdev->evbit);
+ set_bit(EV_MSC, tpacpi_inputdev->evbit);
+ set_bit(MSC_SCAN, tpacpi_inputdev->mscbit);
+ tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE;
+ tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN;
+ tpacpi_inputdev->keycode = hotkey_keycode_map;
+ for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) {
+ if (hotkey_keycode_map[i] != KEY_RESERVED) {
+ set_bit(hotkey_keycode_map[i],
+ tpacpi_inputdev->keybit);
} else {
- dbg_printk(TPACPI_DBG_INIT,
- "using IBM default hot key map\n");
- memcpy(hotkey_keycode_map, &ibm_keycode_map,
- TPACPI_HOTKEY_MAP_SIZE);
- }
-
- set_bit(EV_KEY, tpacpi_inputdev->evbit);
- set_bit(EV_MSC, tpacpi_inputdev->evbit);
- set_bit(MSC_SCAN, tpacpi_inputdev->mscbit);
- tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE;
- tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN;
- tpacpi_inputdev->keycode = hotkey_keycode_map;
- for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) {
- if (hotkey_keycode_map[i] != KEY_RESERVED) {
- set_bit(hotkey_keycode_map[i],
- tpacpi_inputdev->keybit);
- } else {
- if (i < sizeof(hotkey_reserved_mask)*8)
- hotkey_reserved_mask |= 1 << i;
- }
- }
-
- if (tp_features.hotkey_wlsw) {
- set_bit(EV_SW, tpacpi_inputdev->evbit);
- set_bit(SW_RADIO, tpacpi_inputdev->swbit);
- }
- if (tp_features.hotkey_tablet) {
- set_bit(EV_SW, tpacpi_inputdev->evbit);
- set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit);
+ if (i < sizeof(hotkey_reserved_mask)*8)
+ hotkey_reserved_mask |= 1 << i;
}
+ }
- /* Do not issue duplicate brightness change events to
- * userspace */
- if (!tp_features.bright_acpimode)
- /* update bright_acpimode... */
- tpacpi_check_std_acpi_brightness_support();
-
- if (tp_features.bright_acpimode) {
- printk(TPACPI_INFO
- "This ThinkPad has standard ACPI backlight "
- "brightness control, supported by the ACPI "
- "video driver\n");
- printk(TPACPI_NOTICE
- "Disabling thinkpad-acpi brightness events "
- "by default...\n");
-
- /* The hotkey_reserved_mask change below is not
- * necessary while the keys are at KEY_RESERVED in the
- * default map, but better safe than sorry, leave it
- * here as a marker of what we have to do, especially
- * when we finally become able to set this at runtime
- * on response to X.org requests */
- hotkey_reserved_mask |=
- (1 << TP_ACPI_HOTKEYSCAN_FNHOME)
- | (1 << TP_ACPI_HOTKEYSCAN_FNEND);
- }
+ if (tp_features.hotkey_wlsw) {
+ set_bit(EV_SW, tpacpi_inputdev->evbit);
+ set_bit(SW_RFKILL_ALL, tpacpi_inputdev->swbit);
+ }
+ if (tp_features.hotkey_tablet) {
+ set_bit(EV_SW, tpacpi_inputdev->evbit);
+ set_bit(SW_TABLET_MODE, tpacpi_inputdev->swbit);
+ }
- dbg_printk(TPACPI_DBG_INIT,
- "enabling hot key handling\n");
- res = hotkey_status_set(1);
- if (res)
- return res;
- res = hotkey_mask_set(((hotkey_all_mask | hotkey_source_mask)
- & ~hotkey_reserved_mask)
- | hotkey_orig_mask);
- if (res < 0 && res != -ENXIO)
- return res;
+ /* Do not issue duplicate brightness change events to
+ * userspace */
+ if (!tp_features.bright_acpimode)
+ /* update bright_acpimode... */
+ tpacpi_check_std_acpi_brightness_support();
- dbg_printk(TPACPI_DBG_INIT,
- "legacy hot key reporting over procfs %s\n",
- (hotkey_report_mode < 2) ?
- "enabled" : "disabled");
+ if (tp_features.bright_acpimode) {
+ printk(TPACPI_INFO
+ "This ThinkPad has standard ACPI backlight "
+ "brightness control, supported by the ACPI "
+ "video driver\n");
+ printk(TPACPI_NOTICE
+ "Disabling thinkpad-acpi brightness events "
+ "by default...\n");
+
+ /* The hotkey_reserved_mask change below is not
+ * necessary while the keys are at KEY_RESERVED in the
+ * default map, but better safe than sorry, leave it
+ * here as a marker of what we have to do, especially
+ * when we finally become able to set this at runtime
+ * on response to X.org requests */
+ hotkey_reserved_mask |=
+ (1 << TP_ACPI_HOTKEYSCAN_FNHOME)
+ | (1 << TP_ACPI_HOTKEYSCAN_FNEND);
+ }
+
+ dbg_printk(TPACPI_DBG_INIT, "enabling hot key handling\n");
+ res = hotkey_status_set(1);
+ if (res) {
+ hotkey_exit();
+ return res;
+ }
+ res = hotkey_mask_set(((hotkey_all_mask | hotkey_source_mask)
+ & ~hotkey_reserved_mask)
+ | hotkey_orig_mask);
+ if (res < 0 && res != -ENXIO) {
+ hotkey_exit();
+ return res;
+ }
- tpacpi_inputdev->open = &hotkey_inputdev_open;
- tpacpi_inputdev->close = &hotkey_inputdev_close;
+ dbg_printk(TPACPI_DBG_INIT,
+ "legacy hot key reporting over procfs %s\n",
+ (hotkey_report_mode < 2) ?
+ "enabled" : "disabled");
- hotkey_poll_setup_safe(1);
- tpacpi_input_send_radiosw();
- tpacpi_input_send_tabletsw();
- }
+ tpacpi_inputdev->open = &hotkey_inputdev_open;
+ tpacpi_inputdev->close = &hotkey_inputdev_close;
- return (tp_features.hotkey)? 0 : 1;
-}
+ hotkey_poll_setup_safe(1);
+ tpacpi_input_send_radiosw();
+ tpacpi_input_send_tabletsw();
-static void hotkey_exit(void)
-{
-#ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL
- hotkey_poll_stop_sync();
-#endif
+ return 0;
- if (tp_features.hotkey) {
- dbg_printk(TPACPI_DBG_EXIT,
- "restoring original hot key mask\n");
- /* no short-circuit boolean operator below! */
- if ((hotkey_mask_set(hotkey_orig_mask) |
- hotkey_status_set(hotkey_orig_status)) != 0)
- printk(TPACPI_ERR
- "failed to restore hot key mask "
- "to BIOS defaults\n");
- }
+err_exit:
+ delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
+ hotkey_dev_attributes = NULL;
- if (hotkey_dev_attributes) {
- delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);
- hotkey_dev_attributes = NULL;
- }
+ return (res < 0)? res : 1;
}
static void hotkey_notify(struct ibm_struct *ibm, u32 event)
@@ -3319,7 +3336,7 @@ static struct tpacpi_led_classdev tpacpi_led_thinklight = {
static int __init light_init(struct ibm_init_struct *iibm)
{
- int rc = 0;
+ int rc;
vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n");
@@ -3337,20 +3354,23 @@ static int __init light_init(struct ibm_init_struct *iibm)
tp_features.light_status =
acpi_evalf(ec_handle, NULL, "KBLT", "qv");
- vdbg_printk(TPACPI_DBG_INIT, "light is %s\n",
- str_supported(tp_features.light));
+ vdbg_printk(TPACPI_DBG_INIT, "light is %s, light status is %s\n",
+ str_supported(tp_features.light),
+ str_supported(tp_features.light_status));
- if (tp_features.light) {
- rc = led_classdev_register(&tpacpi_pdev->dev,
- &tpacpi_led_thinklight.led_classdev);
- }
+ if (!tp_features.light)
+ return 1;
+
+ rc = led_classdev_register(&tpacpi_pdev->dev,
+ &tpacpi_led_thinklight.led_classdev);
if (rc < 0) {
tp_features.light = 0;
tp_features.light_status = 0;
- } else {
- rc = (tp_features.light)? 0 : 1;
+ } else {
+ rc = 0;
}
+
return rc;
}
@@ -3833,7 +3853,7 @@ static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = {
"tpacpi::standby",
};
-static int led_get_status(unsigned int led)
+static int led_get_status(const unsigned int led)
{
int status;
enum led_status_t led_s;
@@ -3857,41 +3877,42 @@ static int led_get_status(unsigned int led)
/* not reached */
}
-static int led_set_status(unsigned int led, enum led_status_t ledstatus)
+static int led_set_status(const unsigned int led,
+ const enum led_status_t ledstatus)
{
/* off, on, blink. Index is led_status_t */
- static const int led_sled_arg1[] = { 0, 1, 3 };
- static const int led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */
- static const int led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */
- static const int led_led_arg1[] = { 0, 0x80, 0xc0 };
+ static const unsigned int led_sled_arg1[] = { 0, 1, 3 };
+ static const unsigned int led_led_arg1[] = { 0, 0x80, 0xc0 };
int rc = 0;
switch (led_supported) {
case TPACPI_LED_570:
- /* 570 */
- led = 1 << led;
- if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
- led, led_sled_arg1[ledstatus]))
- rc = -EIO;
- break;
+ /* 570 */
+ if (led > 7)
+ return -EINVAL;
+ if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
+ (1 << led), led_sled_arg1[ledstatus]))
+ rc = -EIO;
+ break;
case TPACPI_LED_OLD:
- /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
- led = 1 << led;
- rc = ec_write(TPACPI_LED_EC_HLMS, led);
- if (rc >= 0)
- rc = ec_write(TPACPI_LED_EC_HLBL,
- led * led_exp_hlbl[ledstatus]);
- if (rc >= 0)
- rc = ec_write(TPACPI_LED_EC_HLCL,
- led * led_exp_hlcl[ledstatus]);
- break;
+ /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */
+ if (led > 7)
+ return -EINVAL;
+ rc = ec_write(TPACPI_LED_EC_HLMS, (1 << led));
+ if (rc >= 0)
+ rc = ec_write(TPACPI_LED_EC_HLBL,
+ (ledstatus == TPACPI_LED_BLINK) << led);
+ if (rc >= 0)
+ rc = ec_write(TPACPI_LED_EC_HLCL,
+ (ledstatus != TPACPI_LED_OFF) << led);
+ break;
case TPACPI_LED_NEW:
- /* all others */
- if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
- led, led_led_arg1[ledstatus]))
- rc = -EIO;
- break;
+ /* all others */
+ if (!acpi_evalf(led_handle, NULL, NULL, "vdd",
+ led, led_led_arg1[ledstatus]))
+ rc = -EIO;
+ break;
default:
rc = -ENXIO;
}
@@ -3978,7 +3999,6 @@ static void led_exit(void)
}
kfree(tpacpi_leds);
- tpacpi_leds = NULL;
}
static int __init led_init(struct ibm_init_struct *iibm)
@@ -4802,7 +4822,6 @@ static void brightness_exit(void)
vdbg_printk(TPACPI_DBG_EXIT,
"calling backlight_device_unregister()\n");
backlight_device_unregister(ibm_backlight_device);
- ibm_backlight_device = NULL;
}
}
@@ -5764,11 +5783,16 @@ static int __init fan_init(struct ibm_init_struct *iibm)
fan_control_access_mode != TPACPI_FAN_WR_NONE) {
rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj,
&fan_attr_group);
- if (!(rc < 0))
- rc = driver_create_file(&tpacpi_hwmon_pdriver.driver,
- &driver_attr_fan_watchdog);
if (rc < 0)
return rc;
+
+ rc = driver_create_file(&tpacpi_hwmon_pdriver.driver,
+ &driver_attr_fan_watchdog);
+ if (rc < 0) {
+ sysfs_remove_group(&tpacpi_sensors_pdev->dev.kobj,
+ &fan_attr_group);
+ return rc;
+ }
return 0;
} else
return 1;
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 5d3ac512ce16..8026bb0fde55 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -27,10 +27,13 @@ static void mtd_notify_add(struct mtd_info* mtd)
if (!mtd)
return;
- device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2), "mtd%d", mtd->index);
+ device_create_drvdata(mtd_class, NULL,
+ MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
+ NULL, "mtd%d", mtd->index);
- device_create(mtd_class, NULL,
- MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), "mtd%dro", mtd->index);
+ device_create_drvdata(mtd_class, NULL,
+ MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
+ NULL, "mtd%dro", mtd->index);
}
static void mtd_notify_remove(struct mtd_info* mtd)
diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c
index 961416ac0616..a5b19944eca8 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -355,15 +355,34 @@ static void kill_volumes(struct ubi_device *ubi)
}
/**
+ * free_user_volumes - free all user volumes.
+ * @ubi: UBI device description object
+ *
+ * Normally the volumes are freed at the release function of the volume device
+ * objects. However, on error paths the volumes have to be freed before the
+ * device objects have been initialized.
+ */
+static void free_user_volumes(struct ubi_device *ubi)
+{
+ int i;
+
+ for (i = 0; i < ubi->vtbl_slots; i++)
+ if (ubi->volumes[i]) {
+ kfree(ubi->volumes[i]->eba_tbl);
+ kfree(ubi->volumes[i]);
+ }
+}
+
+/**
* uif_init - initialize user interfaces for an UBI device.
* @ubi: UBI device description object
*
* This function returns zero in case of success and a negative error code in
- * case of failure.
+ * case of failure. Note, this function destroys all volumes if it failes.
*/
static int uif_init(struct ubi_device *ubi)
{
- int i, err;
+ int i, err, do_free = 0;
dev_t dev;
sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num);
@@ -410,10 +429,13 @@ static int uif_init(struct ubi_device *ubi)
out_volumes:
kill_volumes(ubi);
+ do_free = 0;
out_sysfs:
ubi_sysfs_close(ubi);
cdev_del(&ubi->cdev);
out_unreg:
+ if (do_free)
+ free_user_volumes(ubi);
unregister_chrdev_region(ubi->cdev.dev, ubi->vtbl_slots + 1);
ubi_err("cannot initialize UBI %s, error %d", ubi->ubi_name, err);
return err;
@@ -422,6 +444,10 @@ out_unreg:
/**
* uif_close - close user interfaces for an UBI device.
* @ubi: UBI device description object
+ *
+ * Note, since this function un-registers UBI volume device objects (@vol->dev),
+ * the memory allocated voe the volumes is freed as well (in the release
+ * function).
*/
static void uif_close(struct ubi_device *ubi)
{
@@ -432,6 +458,21 @@ static void uif_close(struct ubi_device *ubi)
}
/**
+ * free_internal_volumes - free internal volumes.
+ * @ubi: UBI device description object
+ */
+static void free_internal_volumes(struct ubi_device *ubi)
+{
+ int i;
+
+ for (i = ubi->vtbl_slots;
+ i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) {
+ kfree(ubi->volumes[i]->eba_tbl);
+ kfree(ubi->volumes[i]);
+ }
+}
+
+/**
* attach_by_scanning - attach an MTD device using scanning method.
* @ubi: UBI device descriptor
*
@@ -475,6 +516,7 @@ static int attach_by_scanning(struct ubi_device *ubi)
out_wl:
ubi_wl_close(ubi);
out_vtbl:
+ free_internal_volumes(ubi);
vfree(ubi->vtbl);
out_si:
ubi_scan_destroy_si(si);
@@ -530,7 +572,11 @@ static int io_init(struct ubi_device *ubi)
ubi->min_io_size = ubi->mtd->writesize;
ubi->hdrs_min_io_size = ubi->mtd->writesize >> ubi->mtd->subpage_sft;
- /* Make sure minimal I/O unit is power of 2 */
+ /*
+ * Make sure minimal I/O unit is power of 2. Note, there is no
+ * fundamental reason for this assumption. It is just an optimization
+ * which allows us to avoid costly division operations.
+ */
if (!is_power_of_2(ubi->min_io_size)) {
ubi_err("min. I/O unit (%d) is not power of 2",
ubi->min_io_size);
@@ -581,7 +627,7 @@ static int io_init(struct ubi_device *ubi)
if (ubi->vid_hdr_offset < UBI_EC_HDR_SIZE ||
ubi->leb_start < ubi->vid_hdr_offset + UBI_VID_HDR_SIZE ||
ubi->leb_start > ubi->peb_size - UBI_VID_HDR_SIZE ||
- ubi->leb_start % ubi->min_io_size) {
+ ubi->leb_start & (ubi->min_io_size - 1)) {
ubi_err("bad VID header (%d) or data offsets (%d)",
ubi->vid_hdr_offset, ubi->leb_start);
return -EINVAL;
@@ -646,7 +692,7 @@ static int autoresize(struct ubi_device *ubi, int vol_id)
/*
* Clear the auto-resize flag in the volume in-memory copy of the
- * volume table, and 'ubi_resize_volume()' will propogate this change
+ * volume table, and 'ubi_resize_volume()' will propagate this change
* to the flash.
*/
ubi->vtbl[vol_id].flags &= ~UBI_VTBL_AUTORESIZE_FLG;
@@ -655,7 +701,7 @@ static int autoresize(struct ubi_device *ubi, int vol_id)
struct ubi_vtbl_record vtbl_rec;
/*
- * No avalilable PEBs to re-size the volume, clear the flag on
+ * No available PEBs to re-size the volume, clear the flag on
* flash and exit.
*/
memcpy(&vtbl_rec, &ubi->vtbl[vol_id],
@@ -688,7 +734,7 @@ static int autoresize(struct ubi_device *ubi, int vol_id)
*
* This function attaches MTD device @mtd_dev to UBI and assign @ubi_num number
* to the newly created UBI device, unless @ubi_num is %UBI_DEV_NUM_AUTO, in
- * which case this function finds a vacant device nubert and assings it
+ * which case this function finds a vacant device number and assigns it
* automatically. Returns the new UBI device number in case of success and a
* negative error code in case of failure.
*
@@ -698,7 +744,7 @@ static int autoresize(struct ubi_device *ubi, int vol_id)
int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
{
struct ubi_device *ubi;
- int i, err;
+ int i, err, do_free = 1;
/*
* Check if we already have the same MTD device attached.
@@ -798,7 +844,7 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
err = uif_init(ubi);
if (err)
- goto out_detach;
+ goto out_nofree;
ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name);
if (IS_ERR(ubi->bgt_thread)) {
@@ -835,9 +881,13 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
out_uif:
uif_close(ubi);
+out_nofree:
+ do_free = 0;
out_detach:
- ubi_eba_close(ubi);
ubi_wl_close(ubi);
+ if (do_free)
+ free_user_volumes(ubi);
+ free_internal_volumes(ubi);
vfree(ubi->vtbl);
out_free:
vfree(ubi->peb_buf1);
@@ -899,8 +949,8 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
kthread_stop(ubi->bgt_thread);
uif_close(ubi);
- ubi_eba_close(ubi);
ubi_wl_close(ubi);
+ free_internal_volumes(ubi);
vfree(ubi->vtbl);
put_mtd_device(ubi->mtd);
vfree(ubi->peb_buf1);
diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c
index 9d6aae5449b6..34175ec53b73 100644
--- a/drivers/mtd/ubi/cdev.c
+++ b/drivers/mtd/ubi/cdev.c
@@ -290,7 +290,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf,
off = do_div(tmp, vol->usable_leb_size);
lnum = tmp;
- if (off % ubi->min_io_size) {
+ if (off & (ubi->min_io_size - 1)) {
dbg_err("unaligned position");
return -EINVAL;
}
@@ -299,7 +299,7 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf,
count_save = count = vol->used_bytes - *offp;
/* We can write only in fractions of the minimum I/O unit */
- if (count % ubi->min_io_size) {
+ if (count & (ubi->min_io_size - 1)) {
dbg_err("unaligned write length");
return -EINVAL;
}
@@ -559,7 +559,7 @@ static int verify_mkvol_req(const struct ubi_device *ubi,
if (req->alignment > ubi->leb_size)
goto bad;
- n = req->alignment % ubi->min_io_size;
+ n = req->alignment & (ubi->min_io_size - 1);
if (req->alignment != 1 && n)
goto bad;
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c
index 7ce91ca742b1..623d25f4855f 100644
--- a/drivers/mtd/ubi/eba.c
+++ b/drivers/mtd/ubi/eba.c
@@ -752,7 +752,7 @@ int ubi_eba_write_leb_st(struct ubi_device *ubi, struct ubi_volume *vol,
/* If this is the last LEB @len may be unaligned */
len = ALIGN(data_size, ubi->min_io_size);
else
- ubi_assert(len % ubi->min_io_size == 0);
+ ubi_assert(!(len & (ubi->min_io_size - 1)));
vid_hdr = ubi_zalloc_vid_hdr(ubi, GFP_NOFS);
if (!vid_hdr)
@@ -1233,20 +1233,3 @@ out_free:
}
return err;
}
-
-/**
- * ubi_eba_close - close EBA unit.
- * @ubi: UBI device description object
- */
-void ubi_eba_close(const struct ubi_device *ubi)
-{
- int i, num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT;
-
- dbg_eba("close EBA unit");
-
- for (i = 0; i < num_volumes; i++) {
- if (!ubi->volumes[i])
- continue;
- kfree(ubi->volumes[i]->eba_tbl);
- }
-}
diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c
index a70d58823f8d..51508832566d 100644
--- a/drivers/mtd/ubi/kapi.c
+++ b/drivers/mtd/ubi/kapi.c
@@ -397,8 +397,8 @@ int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf,
return -EROFS;
if (lnum < 0 || lnum >= vol->reserved_pebs || offset < 0 || len < 0 ||
- offset + len > vol->usable_leb_size || offset % ubi->min_io_size ||
- len % ubi->min_io_size)
+ offset + len > vol->usable_leb_size ||
+ offset & (ubi->min_io_size - 1) || len & (ubi->min_io_size - 1))
return -EINVAL;
if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM &&
@@ -447,7 +447,7 @@ int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf,
return -EROFS;
if (lnum < 0 || lnum >= vol->reserved_pebs || len < 0 ||
- len > vol->usable_leb_size || len % ubi->min_io_size)
+ len > vol->usable_leb_size || len & (ubi->min_io_size - 1))
return -EINVAL;
if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM &&
diff --git a/drivers/mtd/ubi/misc.c b/drivers/mtd/ubi/misc.c
index 93e052812012..22ad31402945 100644
--- a/drivers/mtd/ubi/misc.c
+++ b/drivers/mtd/ubi/misc.c
@@ -37,7 +37,7 @@ int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf,
{
int i;
- ubi_assert(length % ubi->min_io_size == 0);
+ ubi_assert(!(length & (ubi->min_io_size - 1)));
for (i = length - 1; i >= 0; i--)
if (((const uint8_t *)buf)[i] != 0xFF)
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index 67dcbd11c15c..940f6b7deec3 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -477,7 +477,6 @@ int ubi_eba_atomic_leb_change(struct ubi_device *ubi, struct ubi_volume *vol,
int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
struct ubi_vid_hdr *vid_hdr);
int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si);
-void ubi_eba_close(const struct ubi_device *ubi);
/* wl.c */
int ubi_wl_get_peb(struct ubi_device *ubi, int dtype);
diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c
index ddaa1a56cc69..6fa1ab3f2a70 100644
--- a/drivers/mtd/ubi/upd.c
+++ b/drivers/mtd/ubi/upd.c
@@ -237,10 +237,10 @@ static int write_leb(struct ubi_device *ubi, struct ubi_volume *vol, int lnum,
int err;
if (vol->vol_type == UBI_DYNAMIC_VOLUME) {
- len = ALIGN(len, ubi->min_io_size);
- memset(buf + len, 0xFF, len - len);
+ int l = ALIGN(len, ubi->min_io_size);
- len = ubi_calc_data_len(ubi, buf, len);
+ memset(buf + len, 0xFF, l - len);
+ len = ubi_calc_data_len(ubi, buf, l);
if (len == 0) {
dbg_msg("all %d bytes contain 0xFF - skip", len);
return 0;
diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c
index 5be58d85c639..367b04176e0a 100644
--- a/drivers/mtd/ubi/vmt.c
+++ b/drivers/mtd/ubi/vmt.c
@@ -127,6 +127,7 @@ static void vol_release(struct device *dev)
{
struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev);
+ kfree(vol->eba_tbl);
kfree(vol);
}
@@ -201,7 +202,7 @@ static void volume_sysfs_close(struct ubi_volume *vol)
*/
int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
{
- int i, err, vol_id = req->vol_id, dont_free = 0;
+ int i, err, vol_id = req->vol_id, do_free = 1;
struct ubi_volume *vol;
struct ubi_vtbl_record vtbl_rec;
uint64_t bytes;
@@ -365,14 +366,14 @@ int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req)
out_sysfs:
/*
- * We have registered our device, we should not free the volume*
+ * We have registered our device, we should not free the volume
* description object in this function in case of an error - it is
* freed by the release function.
*
* Get device reference to prevent the release function from being
* called just after sysfs has been closed.
*/
- dont_free = 1;
+ do_free = 0;
get_device(&vol->dev);
volume_sysfs_close(vol);
out_gluebi:
@@ -382,17 +383,18 @@ out_gluebi:
out_cdev:
cdev_del(&vol->cdev);
out_mapping:
- kfree(vol->eba_tbl);
+ if (do_free)
+ kfree(vol->eba_tbl);
out_acc:
spin_lock(&ubi->volumes_lock);
ubi->rsvd_pebs -= vol->reserved_pebs;
ubi->avail_pebs += vol->reserved_pebs;
out_unlock:
spin_unlock(&ubi->volumes_lock);
- if (dont_free)
- put_device(&vol->dev);
- else
+ if (do_free)
kfree(vol);
+ else
+ put_device(&vol->dev);
ubi_err("cannot create volume %d, error %d", vol_id, err);
return err;
}
@@ -445,8 +447,6 @@ int ubi_remove_volume(struct ubi_volume_desc *desc)
goto out_err;
}
- kfree(vol->eba_tbl);
- vol->eba_tbl = NULL;
cdev_del(&vol->cdev);
volume_sysfs_close(vol);
@@ -727,7 +727,7 @@ static void paranoid_check_volume(struct ubi_device *ubi, int vol_id)
goto fail;
}
- n = vol->alignment % ubi->min_io_size;
+ n = vol->alignment & (ubi->min_io_size - 1);
if (vol->alignment != 1 && n) {
ubi_err("alignment is not multiple of min I/O unit");
goto fail;
diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c
index af36b12be278..d9af11a8682b 100644
--- a/drivers/mtd/ubi/vtbl.c
+++ b/drivers/mtd/ubi/vtbl.c
@@ -127,7 +127,7 @@ static int vtbl_check(const struct ubi_device *ubi,
const struct ubi_vtbl_record *vtbl)
{
int i, n, reserved_pebs, alignment, data_pad, vol_type, name_len;
- int upd_marker;
+ int upd_marker, err;
uint32_t crc;
const char *name;
@@ -153,7 +153,7 @@ static int vtbl_check(const struct ubi_device *ubi,
if (reserved_pebs == 0) {
if (memcmp(&vtbl[i], &empty_vtbl_record,
UBI_VTBL_RECORD_SIZE)) {
- dbg_err("bad empty record");
+ err = 2;
goto bad;
}
continue;
@@ -161,56 +161,57 @@ static int vtbl_check(const struct ubi_device *ubi,
if (reserved_pebs < 0 || alignment < 0 || data_pad < 0 ||
name_len < 0) {
- dbg_err("negative values");
+ err = 3;
goto bad;
}
if (alignment > ubi->leb_size || alignment == 0) {
- dbg_err("bad alignment");
+ err = 4;
goto bad;
}
- n = alignment % ubi->min_io_size;
+ n = alignment & (ubi->min_io_size - 1);
if (alignment != 1 && n) {
- dbg_err("alignment is not multiple of min I/O unit");
+ err = 5;
goto bad;
}
n = ubi->leb_size % alignment;
if (data_pad != n) {
dbg_err("bad data_pad, has to be %d", n);
+ err = 6;
goto bad;
}
if (vol_type != UBI_VID_DYNAMIC && vol_type != UBI_VID_STATIC) {
- dbg_err("bad vol_type");
+ err = 7;
goto bad;
}
if (upd_marker != 0 && upd_marker != 1) {
- dbg_err("bad upd_marker");
+ err = 8;
goto bad;
}
if (reserved_pebs > ubi->good_peb_count) {
dbg_err("too large reserved_pebs, good PEBs %d",
ubi->good_peb_count);
+ err = 9;
goto bad;
}
if (name_len > UBI_VOL_NAME_MAX) {
- dbg_err("too long volume name, max %d",
- UBI_VOL_NAME_MAX);
+ err = 10;
goto bad;
}
if (name[0] == '\0') {
- dbg_err("NULL volume name");
+ err = 11;
goto bad;
}
if (name_len != strnlen(name, name_len + 1)) {
- dbg_err("bad name_len");
+ err = 12;
goto bad;
}
}
@@ -235,7 +236,7 @@ static int vtbl_check(const struct ubi_device *ubi,
return 0;
bad:
- ubi_err("volume table check failed, record %d", i);
+ ubi_err("volume table check failed: record %d, error %d", i, err);
ubi_dbg_dump_vtbl_record(&vtbl[i], i);
return -EINVAL;
}
@@ -384,7 +385,16 @@ static struct ubi_vtbl_record *process_lvol(struct ubi_device *ubi,
err = ubi_io_read_data(ubi, leb[seb->lnum], seb->pnum, 0,
ubi->vtbl_size);
if (err == UBI_IO_BITFLIPS || err == -EBADMSG)
- /* Scrub the PEB later */
+ /*
+ * Scrub the PEB later. Note, -EBADMSG indicates an
+ * uncorrectable ECC error, but we have our own CRC and
+ * the data will be checked later. If the data is OK,
+ * the PEB will be scrubbed (because we set
+ * seb->scrub). If the data is not OK, the contents of
+ * the PEB will be recovered from the second copy, and
+ * seb->scrub will be cleared in
+ * 'ubi_scan_add_used()'.
+ */
seb->scrub = 1;
else if (err)
goto out_free;
@@ -620,30 +630,32 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si,
static int check_sv(const struct ubi_volume *vol,
const struct ubi_scan_volume *sv)
{
+ int err;
+
if (sv->highest_lnum >= vol->reserved_pebs) {
- dbg_err("bad highest_lnum");
+ err = 1;
goto bad;
}
if (sv->leb_count > vol->reserved_pebs) {
- dbg_err("bad leb_count");
+ err = 2;
goto bad;
}
if (sv->vol_type != vol->vol_type) {
- dbg_err("bad vol_type");
+ err = 3;
goto bad;
}
if (sv->used_ebs > vol->reserved_pebs) {
- dbg_err("bad used_ebs");
+ err = 4;
goto bad;
}
if (sv->data_pad != vol->data_pad) {
- dbg_err("bad data_pad");
+ err = 5;
goto bad;
}
return 0;
bad:
- ubi_err("bad scanning information");
+ ubi_err("bad scanning information, error %d", err);
ubi_dbg_dump_sv(sv);
ubi_dbg_dump_vol_info(vol);
return -EINVAL;
@@ -672,14 +684,13 @@ static int check_scanning_info(const struct ubi_device *ubi,
return -EINVAL;
}
- if (si->highest_vol_id >= ubi->vtbl_slots + UBI_INT_VOL_COUNT&&
+ if (si->highest_vol_id >= ubi->vtbl_slots + UBI_INT_VOL_COUNT &&
si->highest_vol_id < UBI_INTERNAL_VOL_START) {
ubi_err("too large volume ID %d found by scanning",
si->highest_vol_id);
return -EINVAL;
}
-
for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) {
cond_resched();
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index a471a491f0ab..cc8fe2934d2b 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -1368,7 +1368,7 @@ int ubi_thread(void *u)
int err;
if (kthread_should_stop())
- goto out;
+ break;
if (try_to_freeze())
continue;
@@ -1403,7 +1403,6 @@ int ubi_thread(void *u)
cond_resched();
}
-out:
dbg_wl("background thread \"%s\" is killed", ubi->bgt_name);
return 0;
}
diff --git a/drivers/net/7990.c b/drivers/net/7990.c
index 750a46f4bc58..ad6b8a5b6574 100644
--- a/drivers/net/7990.c
+++ b/drivers/net/7990.c
@@ -506,6 +506,7 @@ int lance_open (struct net_device *dev)
return res;
}
+EXPORT_SYMBOL_GPL(lance_open);
int lance_close (struct net_device *dev)
{
@@ -521,6 +522,7 @@ int lance_close (struct net_device *dev)
return 0;
}
+EXPORT_SYMBOL_GPL(lance_close);
void lance_tx_timeout(struct net_device *dev)
{
@@ -529,7 +531,7 @@ void lance_tx_timeout(struct net_device *dev)
dev->trans_start = jiffies;
netif_wake_queue (dev);
}
-
+EXPORT_SYMBOL_GPL(lance_tx_timeout);
int lance_start_xmit (struct sk_buff *skb, struct net_device *dev)
{
@@ -586,6 +588,7 @@ int lance_start_xmit (struct sk_buff *skb, struct net_device *dev)
return 0;
}
+EXPORT_SYMBOL_GPL(lance_start_xmit);
/* taken from the depca driver via a2065.c */
static void lance_load_multicast (struct net_device *dev)
@@ -654,6 +657,7 @@ void lance_set_multicast (struct net_device *dev)
if (!stopped)
netif_start_queue (dev);
}
+EXPORT_SYMBOL_GPL(lance_set_multicast);
#ifdef CONFIG_NET_POLL_CONTROLLER
void lance_poll(struct net_device *dev)
diff --git a/drivers/net/arm/at91_ether.c b/drivers/net/arm/at91_ether.c
index 1e39e78f1778..71f7cec30911 100644
--- a/drivers/net/arm/at91_ether.c
+++ b/drivers/net/arm/at91_ether.c
@@ -677,7 +677,7 @@ static void at91ether_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo
{
strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
strlcpy(info->version, DRV_VERSION, sizeof(info->version));
- strlcpy(info->bus_info, dev->dev.parent->bus_id, sizeof(info->bus_info));
+ strlcpy(info->bus_info, dev_name(dev->dev.parent), sizeof(info->bus_info));
}
static const struct ethtool_ops at91ether_ethtool_ops = {
diff --git a/drivers/net/arm/ep93xx_eth.c b/drivers/net/arm/ep93xx_eth.c
index ecd8fc6146e9..7a14980f3472 100644
--- a/drivers/net/arm/ep93xx_eth.c
+++ b/drivers/net/arm/ep93xx_eth.c
@@ -848,7 +848,7 @@ static int ep93xx_eth_probe(struct platform_device *pdev)
ep->res = request_mem_region(pdev->resource[0].start,
pdev->resource[0].end - pdev->resource[0].start + 1,
- pdev->dev.bus_id);
+ dev_name(&pdev->dev));
if (ep->res == NULL) {
dev_err(&pdev->dev, "Could not reserve memory region\n");
err = -ENOMEM;
diff --git a/drivers/net/arm/etherh.c b/drivers/net/arm/etherh.c
index 00081d2b9cd5..f6e6d505227c 100644
--- a/drivers/net/arm/etherh.c
+++ b/drivers/net/arm/etherh.c
@@ -535,7 +535,7 @@ static int __init etherh_addr(char *addr, struct expansion_card *ec)
if (!ecard_readchunk(&cd, ec, 0xf5, 0)) {
printk(KERN_ERR "%s: unable to read podule description string\n",
- ec->dev.bus_id);
+ dev_name(&ec->dev));
goto no_addr;
}
@@ -554,7 +554,7 @@ static int __init etherh_addr(char *addr, struct expansion_card *ec)
}
printk(KERN_ERR "%s: unable to parse MAC address: %s\n",
- ec->dev.bus_id, cd.d.string);
+ dev_name(&ec->dev), cd.d.string);
no_addr:
return -ENODEV;
@@ -585,7 +585,7 @@ static void etherh_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *i
{
strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
strlcpy(info->version, DRV_VERSION, sizeof(info->version));
- strlcpy(info->bus_info, dev->dev.parent->bus_id,
+ strlcpy(info->bus_info, dev_name(dev->dev.parent),
sizeof(info->bus_info));
}
diff --git a/drivers/net/atlx/atl1.c b/drivers/net/atlx/atl1.c
index 6ddc911e7d15..99e0b4cdc56f 100644
--- a/drivers/net/atlx/atl1.c
+++ b/drivers/net/atlx/atl1.c
@@ -637,22 +637,6 @@ static s32 atl1_phy_leave_power_saving(struct atl1_hw *hw)
}
/*
- * Force the PHY into power saving mode using vendor magic.
- */
-#ifdef CONFIG_PM
-static void atl1_phy_enter_power_saving(struct atl1_hw *hw)
-{
- atl1_write_phy_reg(hw, MII_DBG_ADDR, 0);
- atl1_write_phy_reg(hw, MII_DBG_DATA, 0x124E);
- atl1_write_phy_reg(hw, MII_DBG_ADDR, 2);
- atl1_write_phy_reg(hw, MII_DBG_DATA, 0x3000);
- atl1_write_phy_reg(hw, MII_DBG_ADDR, 3);
- atl1_write_phy_reg(hw, MII_DBG_DATA, 0);
-
-}
-#endif
-
-/*
* Resets the PHY and make all config validate
* hw - Struct containing variables accessed by shared code
*
@@ -2860,7 +2844,6 @@ disable_wol:
ctrl |= PCIE_PHYMISC_FORCE_RCV_DET;
iowrite32(ctrl, hw->hw_addr + REG_PCIE_PHYMISC);
ioread32(hw->hw_addr + REG_PCIE_PHYMISC);
- atl1_phy_enter_power_saving(hw);
hw->phy_configured = false;
pci_enable_wake(pdev, pci_choose_state(pdev, state), 0);
exit:
diff --git a/drivers/net/cxgb3/common.h b/drivers/net/cxgb3/common.h
index 579bee42a5cb..8e8ebd788537 100644
--- a/drivers/net/cxgb3/common.h
+++ b/drivers/net/cxgb3/common.h
@@ -686,8 +686,9 @@ int t3_seeprom_write(struct adapter *adapter, u32 addr, __le32 data);
int t3_seeprom_wp(struct adapter *adapter, int enable);
int t3_get_tp_version(struct adapter *adapter, u32 *vers);
int t3_check_tpsram_version(struct adapter *adapter, int *must_load);
-int t3_check_tpsram(struct adapter *adapter, u8 *tp_ram, unsigned int size);
-int t3_set_proto_sram(struct adapter *adap, u8 *data);
+int t3_check_tpsram(struct adapter *adapter, const u8 *tp_ram,
+ unsigned int size);
+int t3_set_proto_sram(struct adapter *adap, const u8 *data);
int t3_read_flash(struct adapter *adapter, unsigned int addr,
unsigned int nwords, u32 *data, int byte_oriented);
int t3_load_fw(struct adapter *adapter, const u8 * fw_data, unsigned int size);
diff --git a/drivers/net/cxgb3/t3_hw.c b/drivers/net/cxgb3/t3_hw.c
index d405a932c73a..47d51788a462 100644
--- a/drivers/net/cxgb3/t3_hw.c
+++ b/drivers/net/cxgb3/t3_hw.c
@@ -923,7 +923,8 @@ int t3_check_tpsram_version(struct adapter *adapter, int *must_load)
* Checks if an adapter's tp sram is compatible with the driver.
* Returns 0 if the versions are compatible, a negative error otherwise.
*/
-int t3_check_tpsram(struct adapter *adapter, u8 *tp_sram, unsigned int size)
+int t3_check_tpsram(struct adapter *adapter, const u8 *tp_sram,
+ unsigned int size)
{
u32 csum;
unsigned int i;
@@ -2875,10 +2876,10 @@ static void ulp_config(struct adapter *adap, const struct tp_params *p)
*
* Write the contents of the protocol SRAM.
*/
-int t3_set_proto_sram(struct adapter *adap, u8 *data)
+int t3_set_proto_sram(struct adapter *adap, const u8 *data)
{
int i;
- __be32 *buf = (__be32 *)data;
+ const __be32 *buf = (const __be32 *)data;
for (i = 0; i < PROTO_SRAM_LINES; i++) {
t3_write_reg(adap, A_TP_EMBED_OP_FIELD5, be32_to_cpu(*buf++));
diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c
index 287a61918739..faae01dc1c4b 100644
--- a/drivers/net/ehea/ehea_main.c
+++ b/drivers/net/ehea/ehea_main.c
@@ -1766,16 +1766,20 @@ static int ehea_set_mac_addr(struct net_device *dev, void *sa)
mutex_lock(&ehea_bcmc_regs.lock);
/* Deregister old MAC in pHYP */
- ret = ehea_broadcast_reg_helper(port, H_DEREG_BCMC);
- if (ret)
- goto out_upregs;
+ if (port->state == EHEA_PORT_UP) {
+ ret = ehea_broadcast_reg_helper(port, H_DEREG_BCMC);
+ if (ret)
+ goto out_upregs;
+ }
port->mac_addr = cb0->port_mac_addr << 16;
/* Register new MAC in pHYP */
- ret = ehea_broadcast_reg_helper(port, H_REG_BCMC);
- if (ret)
- goto out_upregs;
+ if (port->state == EHEA_PORT_UP) {
+ ret = ehea_broadcast_reg_helper(port, H_REG_BCMC);
+ if (ret)
+ goto out_upregs;
+ }
ret = 0;
diff --git a/drivers/net/fec_mpc52xx.c b/drivers/net/fec_mpc52xx.c
index 5f9c42e7a7f1..329edd9c08fc 100644
--- a/drivers/net/fec_mpc52xx.c
+++ b/drivers/net/fec_mpc52xx.c
@@ -78,7 +78,7 @@ module_param_array_named(mac, mpc52xx_fec_mac_addr, byte, NULL, 0);
MODULE_PARM_DESC(mac, "six hex digits, ie. 0x1,0x2,0xc0,0x01,0xba,0xbe");
#define MPC52xx_MESSAGES_DEFAULT ( NETIF_MSG_DRV | NETIF_MSG_PROBE | \
- NETIF_MSG_LINK | NETIF_MSG_IFDOWN | NETIF_MSG_IFDOWN )
+ NETIF_MSG_LINK | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP)
static int debug = -1; /* the above default */
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "debugging messages level");
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index 9eca97fb0a54..2cb244763292 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -3273,6 +3273,20 @@ static void nv_link_irq(struct net_device *dev)
dprintk(KERN_DEBUG "%s: link change notification done.\n", dev->name);
}
+static void nv_msi_workaround(struct fe_priv *np)
+{
+
+ /* Need to toggle the msi irq mask within the ethernet device,
+ * otherwise, future interrupts will not be detected.
+ */
+ if (np->msi_flags & NV_MSI_ENABLED) {
+ u8 __iomem *base = np->base;
+
+ writel(0, base + NvRegMSIIrqMask);
+ writel(NVREG_MSI_VECTOR_0_ENABLED, base + NvRegMSIIrqMask);
+ }
+}
+
static irqreturn_t nv_nic_irq(int foo, void *data)
{
struct net_device *dev = (struct net_device *) data;
@@ -3295,6 +3309,8 @@ static irqreturn_t nv_nic_irq(int foo, void *data)
if (!(events & np->irqmask))
break;
+ nv_msi_workaround(np);
+
spin_lock(&np->lock);
nv_tx_done(dev);
spin_unlock(&np->lock);
@@ -3410,6 +3426,8 @@ static irqreturn_t nv_nic_irq_optimized(int foo, void *data)
if (!(events & np->irqmask))
break;
+ nv_msi_workaround(np);
+
spin_lock(&np->lock);
nv_tx_done_optimized(dev, TX_WORK_PER_LOOP);
spin_unlock(&np->lock);
@@ -3750,6 +3768,8 @@ static irqreturn_t nv_nic_irq_test(int foo, void *data)
if (!(events & NVREG_IRQ_TIMER))
return IRQ_RETVAL(0);
+ nv_msi_workaround(np);
+
spin_lock(&np->lock);
np->intr_test = 1;
spin_unlock(&np->lock);
diff --git a/drivers/net/ipg.c b/drivers/net/ipg.c
index 9b358f61ed7f..679a0826780e 100644
--- a/drivers/net/ipg.c
+++ b/drivers/net/ipg.c
@@ -577,12 +577,12 @@ static void ipg_nic_set_multicast_list(struct net_device *dev)
/* NIC to be configured in promiscuous mode. */
receivemode = IPG_RM_RECEIVEALLFRAMES;
} else if ((dev->flags & IFF_ALLMULTI) ||
- (dev->flags & IFF_MULTICAST &
+ ((dev->flags & IFF_MULTICAST) &&
(dev->mc_count > IPG_MULTICAST_HASHTABLE_SIZE))) {
/* NIC to be configured to receive all multicast
* frames. */
receivemode |= IPG_RM_RECEIVEMULTICAST;
- } else if (dev->flags & IFF_MULTICAST & (dev->mc_count > 0)) {
+ } else if ((dev->flags & IFF_MULTICAST) && (dev->mc_count > 0)) {
/* NIC to be configured to receive selected
* multicast addresses. */
receivemode |= IPG_RM_RECEIVEMULTICASTHASH;
diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig
index ce816ba9c40d..e6317557a531 100644
--- a/drivers/net/irda/Kconfig
+++ b/drivers/net/irda/Kconfig
@@ -329,6 +329,7 @@ config PXA_FICP
config MCS_FIR
tristate "MosChip MCS7780 IrDA-USB dongle"
depends on IRDA && USB && EXPERIMENTAL
+ select CRC32
help
Say Y or M here if you want to build support for the MosChip
MCS7780 IrDA-USB bridge device driver.
diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c
index 6f50ed7b183f..18b471cd1447 100644
--- a/drivers/net/irda/irda-usb.c
+++ b/drivers/net/irda/irda-usb.c
@@ -1024,7 +1024,7 @@ static int irda_usb_is_receiving(struct irda_usb_cb *self)
* Upload firmware code to SigmaTel 421X IRDA-USB dongle
*/
static int stir421x_fw_upload(struct irda_usb_cb *self,
- unsigned char *patch,
+ const unsigned char *patch,
const unsigned int patch_len)
{
int ret = -ENOMEM;
@@ -1073,11 +1073,11 @@ static int stir421x_fw_upload(struct irda_usb_cb *self,
*/
static int stir421x_patch_device(struct irda_usb_cb *self)
{
- unsigned int i;
- int ret;
- char stir421x_fw_name[11];
- const struct firmware *fw;
- unsigned char *fw_version_ptr; /* pointer to version string */
+ unsigned int i;
+ int ret;
+ char stir421x_fw_name[11];
+ const struct firmware *fw;
+ const unsigned char *fw_version_ptr; /* pointer to version string */
unsigned long fw_version = 0;
/*
diff --git a/drivers/net/ixgbe/ixgbe_82598.c b/drivers/net/ixgbe/ixgbe_82598.c
index 6321b059ce13..2f38e847e2cd 100644
--- a/drivers/net/ixgbe/ixgbe_82598.c
+++ b/drivers/net/ixgbe/ixgbe_82598.c
@@ -58,8 +58,8 @@ static s32 ixgbe_reset_hw_82598(struct ixgbe_hw *hw);
static s32 ixgbe_get_invariants_82598(struct ixgbe_hw *hw)
{
- hw->mac.num_rx_queues = IXGBE_82598_MAX_TX_QUEUES;
- hw->mac.num_tx_queues = IXGBE_82598_MAX_RX_QUEUES;
+ hw->mac.num_rx_queues = IXGBE_82598_MAX_RX_QUEUES;
+ hw->mac.num_tx_queues = IXGBE_82598_MAX_TX_QUEUES;
hw->mac.num_rx_addrs = IXGBE_82598_RAR_ENTRIES;
/* PHY ops are filled in by default properly for Fiber only */
diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
index c36a03ae9bfb..860d75d81f82 100644
--- a/drivers/net/macvlan.c
+++ b/drivers/net/macvlan.c
@@ -20,7 +20,7 @@
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/string.h>
-#include <linux/list.h>
+#include <linux/rculist.h>
#include <linux/notifier.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c
index e0d76c75aea0..823bb6d35334 100644
--- a/drivers/net/myri10ge/myri10ge.c
+++ b/drivers/net/myri10ge/myri10ge.c
@@ -529,6 +529,7 @@ static int myri10ge_load_hotplug_firmware(struct myri10ge_priv *mgp, u32 * size)
unsigned crc, reread_crc;
const struct firmware *fw;
struct device *dev = &mgp->pdev->dev;
+ unsigned char *fw_readback;
struct mcp_gen_header *hdr;
size_t hdr_offset;
int status;
@@ -571,9 +572,15 @@ static int myri10ge_load_hotplug_firmware(struct myri10ge_priv *mgp, u32 * size)
mb();
readb(mgp->sram);
}
+ fw_readback = vmalloc(fw->size);
+ if (!fw_readback) {
+ status = -ENOMEM;
+ goto abort_with_fw;
+ }
/* corruption checking is good for parity recovery and buggy chipset */
- memcpy_fromio(fw->data, mgp->sram + MYRI10GE_FW_OFFSET, fw->size);
- reread_crc = crc32(~0, fw->data, fw->size);
+ memcpy_fromio(fw_readback, mgp->sram + MYRI10GE_FW_OFFSET, fw->size);
+ reread_crc = crc32(~0, fw_readback, fw->size);
+ vfree(fw_readback);
if (crc != reread_crc) {
dev_err(dev, "CRC failed(fw-len=%u), got 0x%x (expect 0x%x)\n",
(unsigned)fw->size, reread_crc, crc);
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 16a0e7de5888..e831b7e39234 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -309,11 +309,6 @@ void phy_disconnect(struct phy_device *phydev)
}
EXPORT_SYMBOL(phy_disconnect);
-static int phy_compare_id(struct device *dev, void *data)
-{
- return strcmp((char *)data, dev->bus_id) ? 0 : 1;
-}
-
/**
* phy_attach - attach a network device to a particular PHY device
* @dev: network device to attach
@@ -337,8 +332,7 @@ struct phy_device *phy_attach(struct net_device *dev,
/* Search the list of PHY devices on the mdio bus for the
* PHY with the requested name */
- d = bus_find_device(bus, NULL, (void *)bus_id, phy_compare_id);
-
+ d = bus_find_device_by_name(bus, NULL, bus_id);
if (d) {
phydev = to_phy_device(d);
} else {
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c
index 1f4ca2b54a73..d77493564188 100644
--- a/drivers/net/ppp_generic.c
+++ b/drivers/net/ppp_generic.c
@@ -856,7 +856,8 @@ static int __init ppp_init(void)
err = PTR_ERR(ppp_class);
goto out_chrdev;
}
- device_create(ppp_class, NULL, MKDEV(PPP_MAJOR, 0), "ppp");
+ device_create_drvdata(ppp_class, NULL, MKDEV(PPP_MAJOR, 0),
+ NULL, "ppp");
}
out:
diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c
index 58a26a47af29..bafb69b6f7cb 100644
--- a/drivers/net/pppoe.c
+++ b/drivers/net/pppoe.c
@@ -341,12 +341,6 @@ static int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb)
struct pppox_sock *relay_po;
if (sk->sk_state & PPPOX_BOUND) {
- struct pppoe_hdr *ph = pppoe_hdr(skb);
- int len = ntohs(ph->length);
- skb_pull_rcsum(skb, sizeof(struct pppoe_hdr));
- if (pskb_trim_rcsum(skb, len))
- goto abort_kfree;
-
ppp_input(&po->chan, skb);
} else if (sk->sk_state & PPPOX_RELAY) {
relay_po = get_item_by_addr(&po->pppoe_relay);
@@ -357,7 +351,6 @@ static int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb)
if ((sk_pppox(relay_po)->sk_state & PPPOX_CONNECTED) == 0)
goto abort_put;
- skb_pull(skb, sizeof(struct pppoe_hdr));
if (!__pppoe_xmit(sk_pppox(relay_po), skb))
goto abort_put;
} else {
@@ -388,6 +381,7 @@ static int pppoe_rcv(struct sk_buff *skb,
{
struct pppoe_hdr *ph;
struct pppox_sock *po;
+ int len;
if (!(skb = skb_share_check(skb, GFP_ATOMIC)))
goto out;
@@ -399,10 +393,21 @@ static int pppoe_rcv(struct sk_buff *skb,
goto drop;
ph = pppoe_hdr(skb);
+ len = ntohs(ph->length);
+
+ skb_pull_rcsum(skb, sizeof(*ph));
+ if (skb->len < len)
+ goto drop;
po = get_item(ph->sid, eth_hdr(skb)->h_source, dev->ifindex);
- if (po != NULL)
- return sk_receive_skb(sk_pppox(po), skb, 0);
+ if (!po)
+ goto drop;
+
+ if (pskb_trim_rcsum(skb, len))
+ goto drop;
+
+ return sk_receive_skb(sk_pppox(po), skb, 0);
+
drop:
kfree_skb(skb);
out:
@@ -427,12 +432,12 @@ static int pppoe_disc_rcv(struct sk_buff *skb,
if (dev_net(dev) != &init_net)
goto abort;
- if (!pskb_may_pull(skb, sizeof(struct pppoe_hdr)))
- goto abort;
-
if (!(skb = skb_share_check(skb, GFP_ATOMIC)))
goto out;
+ if (!pskb_may_pull(skb, sizeof(struct pppoe_hdr)))
+ goto abort;
+
ph = pppoe_hdr(skb);
if (ph->code != PADT_CODE)
goto abort;
@@ -937,12 +942,10 @@ static int pppoe_recvmsg(struct kiocb *iocb, struct socket *sock,
m->msg_namelen = 0;
if (skb) {
- struct pppoe_hdr *ph = pppoe_hdr(skb);
- const int len = ntohs(ph->length);
-
- error = memcpy_toiovec(m->msg_iov, (unsigned char *) &ph->tag[0], len);
+ total_len = min(total_len, skb->len);
+ error = skb_copy_datagram_iovec(skb, 0, m->msg_iov, total_len);
if (error == 0)
- error = len;
+ error = total_len;
}
kfree_skb(skb);
diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c
index 70cfdb46aa27..f9298827a76c 100644
--- a/drivers/net/pppol2tp.c
+++ b/drivers/net/pppol2tp.c
@@ -783,14 +783,18 @@ static int pppol2tp_recvmsg(struct kiocb *iocb, struct socket *sock,
err = 0;
skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
flags & MSG_DONTWAIT, &err);
- if (skb) {
- err = memcpy_toiovec(msg->msg_iov, (unsigned char *) skb->data,
- skb->len);
- if (err < 0)
- goto do_skb_free;
- err = skb->len;
- }
-do_skb_free:
+ if (!skb)
+ goto end;
+
+ if (len > skb->len)
+ len = skb->len;
+ else if (len < skb->len)
+ msg->msg_flags |= MSG_TRUNC;
+
+ err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len);
+ if (likely(err == 0))
+ err = len;
+
kfree_skb(skb);
end:
return err;
diff --git a/drivers/net/r6040.c b/drivers/net/r6040.c
index 169edc154928..858b191517b3 100644
--- a/drivers/net/r6040.c
+++ b/drivers/net/r6040.c
@@ -733,7 +733,7 @@ static void r6040_timer(unsigned long data)
}
/* Timer active again */
- mod_timer(&lp->timer, jiffies + round_jiffies(HZ));
+ mod_timer(&lp->timer, round_jiffies(jiffies + HZ));
}
/* Read/set MAC address routines */
diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c
index d3f749c72d41..790db89db345 100644
--- a/drivers/net/sfc/falcon.c
+++ b/drivers/net/sfc/falcon.c
@@ -733,8 +733,10 @@ void falcon_fini_rx(struct efx_rx_queue *rx_queue)
continue;
break;
}
- if (rc)
+ if (rc) {
EFX_ERR(efx, "failed to flush rx queue %d\n", rx_queue->queue);
+ efx_schedule_reset(efx, RESET_TYPE_INVISIBLE);
+ }
/* Remove RX descriptor ring from card */
EFX_ZERO_OWORD(rx_desc_ptr);
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
index 3bb60530d4d7..62436b3a18c6 100644
--- a/drivers/net/sky2.c
+++ b/drivers/net/sky2.c
@@ -4404,7 +4404,9 @@ static int sky2_resume(struct pci_dev *pdev)
if (err) {
printk(KERN_ERR PFX "%s: could not up: %d\n",
dev->name, err);
+ rtnl_lock();
dev_close(dev);
+ rtnl_unlock();
goto out;
}
}
diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h
index 69e97a1cb1c4..8606818653f8 100644
--- a/drivers/net/smc91x.h
+++ b/drivers/net/smc91x.h
@@ -93,14 +93,14 @@
#define SMC_insw(a, r, p, l) insw ((unsigned long *)((a) + (r)), p, l)
# endif
/* check if the mac in reg is valid */
-#define SMC_GET_MAC_ADDR(addr) \
+#define SMC_GET_MAC_ADDR(lp, addr) \
do { \
unsigned int __v; \
- __v = SMC_inw(ioaddr, ADDR0_REG); \
+ __v = SMC_inw(ioaddr, ADDR0_REG(lp)); \
addr[0] = __v; addr[1] = __v >> 8; \
- __v = SMC_inw(ioaddr, ADDR1_REG); \
+ __v = SMC_inw(ioaddr, ADDR1_REG(lp)); \
addr[2] = __v; addr[3] = __v >> 8; \
- __v = SMC_inw(ioaddr, ADDR2_REG); \
+ __v = SMC_inw(ioaddr, ADDR2_REG(lp)); \
addr[4] = __v; addr[5] = __v >> 8; \
if (*(u32 *)(&addr[0]) == 0xFFFFFFFF) { \
random_ether_addr(addr); \
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
index 07b3f77e7626..cc4bde852542 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/tg3.c
@@ -64,8 +64,8 @@
#define DRV_MODULE_NAME "tg3"
#define PFX DRV_MODULE_NAME ": "
-#define DRV_MODULE_VERSION "3.92"
-#define DRV_MODULE_RELDATE "May 2, 2008"
+#define DRV_MODULE_VERSION "3.92.1"
+#define DRV_MODULE_RELDATE "June 9, 2008"
#define TG3_DEF_MAC_MODE 0
#define TG3_DEF_RX_MODE 0
@@ -1295,6 +1295,21 @@ static void tg3_frob_aux_power(struct tg3 *tp)
GRC_LCLCTRL_GPIO_OUTPUT0 |
GRC_LCLCTRL_GPIO_OUTPUT1),
100);
+ } else if (tp->pdev->device == PCI_DEVICE_ID_TIGON3_5761) {
+ /* The 5761 non-e device swaps GPIO 0 and GPIO 2. */
+ u32 grc_local_ctrl = GRC_LCLCTRL_GPIO_OE0 |
+ GRC_LCLCTRL_GPIO_OE1 |
+ GRC_LCLCTRL_GPIO_OE2 |
+ GRC_LCLCTRL_GPIO_OUTPUT0 |
+ GRC_LCLCTRL_GPIO_OUTPUT1 |
+ tp->grc_local_ctrl;
+ tw32_wait_f(GRC_LOCAL_CTRL, grc_local_ctrl, 100);
+
+ grc_local_ctrl |= GRC_LCLCTRL_GPIO_OUTPUT2;
+ tw32_wait_f(GRC_LOCAL_CTRL, grc_local_ctrl, 100);
+
+ grc_local_ctrl &= ~GRC_LCLCTRL_GPIO_OUTPUT0;
+ tw32_wait_f(GRC_LOCAL_CTRL, grc_local_ctrl, 100);
} else {
u32 no_gpio2;
u32 grc_local_ctrl = 0;
@@ -3168,8 +3183,7 @@ static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset)
err |= tg3_readphy(tp, MII_BMCR, &bmcr);
if ((tp->link_config.autoneg == AUTONEG_ENABLE) && !force_reset &&
- (tp->tg3_flags2 & TG3_FLG2_PARALLEL_DETECT) &&
- tp->link_config.flowctrl == tp->link_config.active_flowctrl) {
+ (tp->tg3_flags2 & TG3_FLG2_PARALLEL_DETECT)) {
/* do nothing, just check for link up at the end */
} else if (tp->link_config.autoneg == AUTONEG_ENABLE) {
u32 adv, new_adv;
@@ -8599,7 +8613,7 @@ static int tg3_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
(cmd->speed == SPEED_1000))
return -EINVAL;
else if ((cmd->speed == SPEED_1000) &&
- (tp->tg3_flags2 & TG3_FLAG_10_100_ONLY))
+ (tp->tg3_flags & TG3_FLAG_10_100_ONLY))
return -EINVAL;
tg3_full_lock(tp, 0);
@@ -11768,6 +11782,15 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755)
tp->grc_local_ctrl |= GRC_LCLCTRL_GPIO_UART_SEL;
+ if (tp->pdev->device == PCI_DEVICE_ID_TIGON3_5761) {
+ /* Turn off the debug UART. */
+ tp->grc_local_ctrl |= GRC_LCLCTRL_GPIO_UART_SEL;
+ if (tp->tg3_flags2 & TG3_FLG2_IS_NIC)
+ /* Keep VMain power. */
+ tp->grc_local_ctrl |= GRC_LCLCTRL_GPIO_OE0 |
+ GRC_LCLCTRL_GPIO_OUTPUT0;
+ }
+
/* Force the chip into D0. */
err = tg3_set_power_state(tp, PCI_D0);
if (err) {
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 5450eac9e263..4452306d5328 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -44,11 +44,15 @@ struct virtnet_info
/* The skb we couldn't send because buffers were full. */
struct sk_buff *last_xmit_skb;
+ /* If we need to free in a timer, this is it. */
+ struct timer_list xmit_free_timer;
+
/* Number of input buffers, and max we've ever had. */
unsigned int num, max;
/* For cleaning up after transmission. */
struct tasklet_struct tasklet;
+ bool free_in_tasklet;
/* Receive & send queues. */
struct sk_buff_head recv;
@@ -72,7 +76,7 @@ static void skb_xmit_done(struct virtqueue *svq)
/* Suppress further interrupts. */
svq->vq_ops->disable_cb(svq);
- /* We were waiting for more output buffers. */
+ /* We were probably waiting for more output buffers. */
netif_wake_queue(vi->dev);
/* Make sure we re-xmit last_xmit_skb: if there are no more packets
@@ -94,9 +98,7 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb,
BUG_ON(len > MAX_PACKET_LEN);
skb_trim(skb, len);
- skb->protocol = eth_type_trans(skb, dev);
- pr_debug("Receiving skb proto 0x%04x len %i type %i\n",
- ntohs(skb->protocol), skb->len, skb->pkt_type);
+
dev->stats.rx_bytes += skb->len;
dev->stats.rx_packets++;
@@ -106,6 +108,10 @@ static void receive_skb(struct net_device *dev, struct sk_buff *skb,
goto frame_err;
}
+ skb->protocol = eth_type_trans(skb, dev);
+ pr_debug("Receiving skb proto 0x%04x len %i type %i\n",
+ ntohs(skb->protocol), skb->len, skb->pkt_type);
+
if (hdr->gso_type != VIRTIO_NET_HDR_GSO_NONE) {
pr_debug("GSO!\n");
switch (hdr->gso_type & ~VIRTIO_NET_HDR_GSO_ECN) {
@@ -238,9 +244,25 @@ static void free_old_xmit_skbs(struct virtnet_info *vi)
}
}
+/* If the virtio transport doesn't always notify us when all in-flight packets
+ * are consumed, we fall back to using this function on a timer to free them. */
+static void xmit_free(unsigned long data)
+{
+ struct virtnet_info *vi = (void *)data;
+
+ netif_tx_lock(vi->dev);
+
+ free_old_xmit_skbs(vi);
+
+ if (!skb_queue_empty(&vi->send))
+ mod_timer(&vi->xmit_free_timer, jiffies + (HZ/10));
+
+ netif_tx_unlock(vi->dev);
+}
+
static int xmit_skb(struct virtnet_info *vi, struct sk_buff *skb)
{
- int num;
+ int num, err;
struct scatterlist sg[2+MAX_SKB_FRAGS];
struct virtio_net_hdr *hdr;
const unsigned char *dest = ((struct ethhdr *)skb->data)->h_dest;
@@ -283,7 +305,11 @@ static int xmit_skb(struct virtnet_info *vi, struct sk_buff *skb)
vnet_hdr_to_sg(sg, skb);
num = skb_to_sgvec(skb, sg+1, 0, skb->len) + 1;
- return vi->svq->vq_ops->add_buf(vi->svq, sg, num, 0, skb);
+ err = vi->svq->vq_ops->add_buf(vi->svq, sg, num, 0, skb);
+ if (!err && !vi->free_in_tasklet)
+ mod_timer(&vi->xmit_free_timer, jiffies + (HZ/10));
+
+ return err;
}
static void xmit_tasklet(unsigned long data)
@@ -295,6 +321,8 @@ static void xmit_tasklet(unsigned long data)
vi->svq->vq_ops->kick(vi->svq);
vi->last_xmit_skb = NULL;
}
+ if (vi->free_in_tasklet)
+ free_old_xmit_skbs(vi);
netif_tx_unlock_bh(vi->dev);
}
@@ -435,6 +463,10 @@ static int virtnet_probe(struct virtio_device *vdev)
vi->vdev = vdev;
vdev->priv = vi;
+ /* If they give us a callback when all buffers are done, we don't need
+ * the timer. */
+ vi->free_in_tasklet = virtio_has_feature(vdev,VIRTIO_F_NOTIFY_ON_EMPTY);
+
/* We expect two virtqueues, receive then send. */
vi->rvq = vdev->config->find_vq(vdev, 0, skb_recv_done);
if (IS_ERR(vi->rvq)) {
@@ -454,6 +486,9 @@ static int virtnet_probe(struct virtio_device *vdev)
tasklet_init(&vi->tasklet, xmit_tasklet, (unsigned long)vi);
+ if (!vi->free_in_tasklet)
+ setup_timer(&vi->xmit_free_timer, xmit_free, (unsigned long)vi);
+
err = register_netdev(dev);
if (err) {
pr_debug("virtio_net: registering device failed\n");
@@ -491,6 +526,9 @@ static void virtnet_remove(struct virtio_device *vdev)
/* Stop all the virtqueues. */
vdev->config->reset(vdev);
+ if (!vi->free_in_tasklet)
+ del_timer_sync(&vi->xmit_free_timer);
+
/* Free our skbs in send and recv queues, if any. */
while ((skb = __skb_dequeue(&vi->recv)) != NULL) {
kfree_skb(skb);
@@ -514,7 +552,7 @@ static struct virtio_device_id id_table[] = {
static unsigned int features[] = {
VIRTIO_NET_F_CSUM, VIRTIO_NET_F_GSO, VIRTIO_NET_F_MAC,
VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_HOST_TSO6,
- VIRTIO_NET_F_HOST_ECN,
+ VIRTIO_NET_F_HOST_ECN, VIRTIO_F_NOTIFY_ON_EMPTY,
};
static struct virtio_driver virtio_net = {
diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c
index b0fce1387eaf..bbf168031411 100644
--- a/drivers/net/wan/cosa.c
+++ b/drivers/net/wan/cosa.c
@@ -396,9 +396,9 @@ static int __init cosa_init(void)
err = PTR_ERR(cosa_class);
goto out_chrdev;
}
- for (i=0; i<nr_cards; i++) {
- device_create(cosa_class, NULL, MKDEV(cosa_major, i), "cosa%d", i);
- }
+ for (i = 0; i < nr_cards; i++)
+ device_create_drvdata(cosa_class, NULL, MKDEV(cosa_major, i),
+ NULL, "cosa%d", i);
err = 0;
goto out;
diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c
index 438e63ecccf1..d1acef7e0b14 100644
--- a/drivers/net/wireless/atmel.c
+++ b/drivers/net/wireless/atmel.c
@@ -560,7 +560,7 @@ static const struct {
static void build_wpa_mib(struct atmel_private *priv);
static int atmel_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
static void atmel_copy_to_card(struct net_device *dev, u16 dest,
- unsigned char *src, u16 len);
+ const unsigned char *src, u16 len);
static void atmel_copy_to_host(struct net_device *dev, unsigned char *dest,
u16 src, u16 len);
static void atmel_set_gcr(struct net_device *dev, u16 mask);
@@ -3853,7 +3853,7 @@ static int reset_atmel_card(struct net_device *dev)
if (priv->card_type == CARD_TYPE_EEPROM) {
/* copy in firmware if needed */
const struct firmware *fw_entry = NULL;
- unsigned char *fw;
+ const unsigned char *fw;
int len = priv->firmware_length;
if (!(fw = priv->firmware)) {
if (priv->firmware_type == ATMEL_FW_TYPE_NONE) {
@@ -4120,7 +4120,7 @@ static void atmel_writeAR(struct net_device *dev, u16 data)
}
static void atmel_copy_to_card(struct net_device *dev, u16 dest,
- unsigned char *src, u16 len)
+ const unsigned char *src, u16 len)
{
int i;
atmel_writeAR(dev, dest);
diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c
index 729336774828..6e704608947c 100644
--- a/drivers/net/wireless/ipw2200.c
+++ b/drivers/net/wireless/ipw2200.c
@@ -1753,6 +1753,8 @@ static int ipw_radio_kill_sw(struct ipw_priv *priv, int disable_radio)
if (priv->workqueue) {
cancel_delayed_work(&priv->request_scan);
+ cancel_delayed_work(&priv->request_direct_scan);
+ cancel_delayed_work(&priv->request_passive_scan);
cancel_delayed_work(&priv->scan_event);
}
queue_work(priv->workqueue, &priv->down);
@@ -2005,6 +2007,8 @@ static void ipw_irq_tasklet(struct ipw_priv *priv)
wake_up_interruptible(&priv->wait_command_queue);
priv->status &= ~(STATUS_ASSOCIATED | STATUS_ASSOCIATING);
cancel_delayed_work(&priv->request_scan);
+ cancel_delayed_work(&priv->request_direct_scan);
+ cancel_delayed_work(&priv->request_passive_scan);
cancel_delayed_work(&priv->scan_event);
schedule_work(&priv->link_down);
queue_delayed_work(priv->workqueue, &priv->rf_kill, 2 * HZ);
@@ -4712,6 +4716,12 @@ static void ipw_rx_notification(struct ipw_priv *priv,
priv->status &= ~STATUS_SCAN_FORCED;
#endif /* CONFIG_IPW2200_MONITOR */
+ /* Do queued direct scans first */
+ if (priv->status & STATUS_DIRECT_SCAN_PENDING) {
+ queue_delayed_work(priv->workqueue,
+ &priv->request_direct_scan, 0);
+ }
+
if (!(priv->status & (STATUS_ASSOCIATED |
STATUS_ASSOCIATING |
STATUS_ROAMING |
@@ -6267,7 +6277,7 @@ static void ipw_add_scan_channels(struct ipw_priv *priv,
}
}
-static int ipw_request_scan_helper(struct ipw_priv *priv, int type)
+static int ipw_request_scan_helper(struct ipw_priv *priv, int type, int direct)
{
struct ipw_scan_request_ext scan;
int err = 0, scan_type;
@@ -6278,22 +6288,31 @@ static int ipw_request_scan_helper(struct ipw_priv *priv, int type)
mutex_lock(&priv->mutex);
+ if (direct && (priv->direct_scan_ssid_len == 0)) {
+ IPW_DEBUG_HC("Direct scan requested but no SSID to scan for\n");
+ priv->status &= ~STATUS_DIRECT_SCAN_PENDING;
+ goto done;
+ }
+
if (priv->status & STATUS_SCANNING) {
- IPW_DEBUG_HC("Concurrent scan requested. Ignoring.\n");
- priv->status |= STATUS_SCAN_PENDING;
+ IPW_DEBUG_HC("Concurrent scan requested. Queuing.\n");
+ priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING :
+ STATUS_SCAN_PENDING;
goto done;
}
if (!(priv->status & STATUS_SCAN_FORCED) &&
priv->status & STATUS_SCAN_ABORTING) {
IPW_DEBUG_HC("Scan request while abort pending. Queuing.\n");
- priv->status |= STATUS_SCAN_PENDING;
+ priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING :
+ STATUS_SCAN_PENDING;
goto done;
}
if (priv->status & STATUS_RF_KILL_MASK) {
- IPW_DEBUG_HC("Aborting scan due to RF Kill activation\n");
- priv->status |= STATUS_SCAN_PENDING;
+ IPW_DEBUG_HC("Queuing scan due to RF Kill activation\n");
+ priv->status |= direct ? STATUS_DIRECT_SCAN_PENDING :
+ STATUS_SCAN_PENDING;
goto done;
}
@@ -6321,6 +6340,7 @@ static int ipw_request_scan_helper(struct ipw_priv *priv, int type)
cpu_to_le16(20);
scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(120);
+ scan.dwell_time[IPW_SCAN_ACTIVE_DIRECT_SCAN] = cpu_to_le16(20);
#ifdef CONFIG_IPW2200_MONITOR
if (priv->ieee->iw_mode == IW_MODE_MONITOR) {
@@ -6360,13 +6380,23 @@ static int ipw_request_scan_helper(struct ipw_priv *priv, int type)
cpu_to_le16(2000);
} else {
#endif /* CONFIG_IPW2200_MONITOR */
- /* If we are roaming, then make this a directed scan for the
- * current network. Otherwise, ensure that every other scan
- * is a fast channel hop scan */
- if ((priv->status & STATUS_ROAMING)
- || (!(priv->status & STATUS_ASSOCIATED)
- && (priv->config & CFG_STATIC_ESSID)
- && (le32_to_cpu(scan.full_scan_index) % 2))) {
+ /* Honor direct scans first, otherwise if we are roaming make
+ * this a direct scan for the current network. Finally,
+ * ensure that every other scan is a fast channel hop scan */
+ if (direct) {
+ err = ipw_send_ssid(priv, priv->direct_scan_ssid,
+ priv->direct_scan_ssid_len);
+ if (err) {
+ IPW_DEBUG_HC("Attempt to send SSID command "
+ "failed\n");
+ goto done;
+ }
+
+ scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
+ } else if ((priv->status & STATUS_ROAMING)
+ || (!(priv->status & STATUS_ASSOCIATED)
+ && (priv->config & CFG_STATIC_ESSID)
+ && (le32_to_cpu(scan.full_scan_index) % 2))) {
err = ipw_send_ssid(priv, priv->essid, priv->essid_len);
if (err) {
IPW_DEBUG_HC("Attempt to send SSID command "
@@ -6391,7 +6421,12 @@ send_request:
}
priv->status |= STATUS_SCANNING;
- priv->status &= ~STATUS_SCAN_PENDING;
+ if (direct) {
+ priv->status &= ~STATUS_DIRECT_SCAN_PENDING;
+ priv->direct_scan_ssid_len = 0;
+ } else
+ priv->status &= ~STATUS_SCAN_PENDING;
+
queue_delayed_work(priv->workqueue, &priv->scan_check,
IPW_SCAN_CHECK_WATCHDOG);
done:
@@ -6402,15 +6437,22 @@ done:
static void ipw_request_passive_scan(struct work_struct *work)
{
struct ipw_priv *priv =
- container_of(work, struct ipw_priv, request_passive_scan);
- ipw_request_scan_helper(priv, IW_SCAN_TYPE_PASSIVE);
+ container_of(work, struct ipw_priv, request_passive_scan.work);
+ ipw_request_scan_helper(priv, IW_SCAN_TYPE_PASSIVE, 0);
}
static void ipw_request_scan(struct work_struct *work)
{
struct ipw_priv *priv =
container_of(work, struct ipw_priv, request_scan.work);
- ipw_request_scan_helper(priv, IW_SCAN_TYPE_ACTIVE);
+ ipw_request_scan_helper(priv, IW_SCAN_TYPE_ACTIVE, 0);
+}
+
+static void ipw_request_direct_scan(struct work_struct *work)
+{
+ struct ipw_priv *priv =
+ container_of(work, struct ipw_priv, request_direct_scan.work);
+ ipw_request_scan_helper(priv, IW_SCAN_TYPE_ACTIVE, 1);
}
static void ipw_bg_abort_scan(struct work_struct *work)
@@ -9477,99 +9519,38 @@ static int ipw_wx_get_retry(struct net_device *dev,
return 0;
}
-static int ipw_request_direct_scan(struct ipw_priv *priv, char *essid,
- int essid_len)
-{
- struct ipw_scan_request_ext scan;
- int err = 0, scan_type;
-
- if (!(priv->status & STATUS_INIT) ||
- (priv->status & STATUS_EXIT_PENDING))
- return 0;
-
- mutex_lock(&priv->mutex);
-
- if (priv->status & STATUS_RF_KILL_MASK) {
- IPW_DEBUG_HC("Aborting scan due to RF kill activation\n");
- priv->status |= STATUS_SCAN_PENDING;
- goto done;
- }
-
- IPW_DEBUG_HC("starting request direct scan!\n");
-
- if (priv->status & (STATUS_SCANNING | STATUS_SCAN_ABORTING)) {
- /* We should not sleep here; otherwise we will block most
- * of the system (for instance, we hold rtnl_lock when we
- * get here).
- */
- err = -EAGAIN;
- goto done;
- }
- memset(&scan, 0, sizeof(scan));
-
- if (priv->config & CFG_SPEED_SCAN)
- scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
- cpu_to_le16(30);
- else
- scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_SCAN] =
- cpu_to_le16(20);
-
- scan.dwell_time[IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN] =
- cpu_to_le16(20);
- scan.dwell_time[IPW_SCAN_PASSIVE_FULL_DWELL_SCAN] = cpu_to_le16(120);
- scan.dwell_time[IPW_SCAN_ACTIVE_DIRECT_SCAN] = cpu_to_le16(20);
-
- scan.full_scan_index = cpu_to_le32(ieee80211_get_scans(priv->ieee));
-
- err = ipw_send_ssid(priv, essid, essid_len);
- if (err) {
- IPW_DEBUG_HC("Attempt to send SSID command failed\n");
- goto done;
- }
- scan_type = IPW_SCAN_ACTIVE_BROADCAST_AND_DIRECT_SCAN;
-
- ipw_add_scan_channels(priv, &scan, scan_type);
-
- err = ipw_send_scan_request_ext(priv, &scan);
- if (err) {
- IPW_DEBUG_HC("Sending scan command failed: %08X\n", err);
- goto done;
- }
-
- priv->status |= STATUS_SCANNING;
-
- done:
- mutex_unlock(&priv->mutex);
- return err;
-}
-
static int ipw_wx_set_scan(struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu, char *extra)
{
struct ipw_priv *priv = ieee80211_priv(dev);
struct iw_scan_req *req = (struct iw_scan_req *)extra;
+ struct delayed_work *work = NULL;
mutex_lock(&priv->mutex);
+
priv->user_requested_scan = 1;
- mutex_unlock(&priv->mutex);
if (wrqu->data.length == sizeof(struct iw_scan_req)) {
if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
- ipw_request_direct_scan(priv, req->essid,
- req->essid_len);
- return 0;
- }
- if (req->scan_type == IW_SCAN_TYPE_PASSIVE) {
- queue_work(priv->workqueue,
- &priv->request_passive_scan);
- return 0;
+ int len = min((int)req->essid_len,
+ (int)sizeof(priv->direct_scan_ssid));
+ memcpy(priv->direct_scan_ssid, req->essid, len);
+ priv->direct_scan_ssid_len = len;
+ work = &priv->request_direct_scan;
+ } else if (req->scan_type == IW_SCAN_TYPE_PASSIVE) {
+ work = &priv->request_passive_scan;
}
+ } else {
+ /* Normal active broadcast scan */
+ work = &priv->request_scan;
}
+ mutex_unlock(&priv->mutex);
+
IPW_DEBUG_WX("Start scan\n");
- queue_delayed_work(priv->workqueue, &priv->request_scan, 0);
+ queue_delayed_work(priv->workqueue, work, 0);
return 0;
}
@@ -10731,6 +10712,8 @@ static void ipw_link_up(struct ipw_priv *priv)
}
cancel_delayed_work(&priv->request_scan);
+ cancel_delayed_work(&priv->request_direct_scan);
+ cancel_delayed_work(&priv->request_passive_scan);
cancel_delayed_work(&priv->scan_event);
ipw_reset_stats(priv);
/* Ensure the rate is updated immediately */
@@ -10761,6 +10744,8 @@ static void ipw_link_down(struct ipw_priv *priv)
/* Cancel any queued work ... */
cancel_delayed_work(&priv->request_scan);
+ cancel_delayed_work(&priv->request_direct_scan);
+ cancel_delayed_work(&priv->request_passive_scan);
cancel_delayed_work(&priv->adhoc_check);
cancel_delayed_work(&priv->gather_stats);
@@ -10800,8 +10785,9 @@ static int __devinit ipw_setup_deferred_work(struct ipw_priv *priv)
INIT_WORK(&priv->up, ipw_bg_up);
INIT_WORK(&priv->down, ipw_bg_down);
INIT_DELAYED_WORK(&priv->request_scan, ipw_request_scan);
+ INIT_DELAYED_WORK(&priv->request_direct_scan, ipw_request_direct_scan);
+ INIT_DELAYED_WORK(&priv->request_passive_scan, ipw_request_passive_scan);
INIT_DELAYED_WORK(&priv->scan_event, ipw_scan_event);
- INIT_WORK(&priv->request_passive_scan, ipw_request_passive_scan);
INIT_DELAYED_WORK(&priv->gather_stats, ipw_bg_gather_stats);
INIT_WORK(&priv->abort_scan, ipw_bg_abort_scan);
INIT_WORK(&priv->roam, ipw_bg_roam);
@@ -11835,6 +11821,8 @@ static void __devexit ipw_pci_remove(struct pci_dev *pdev)
cancel_delayed_work(&priv->adhoc_check);
cancel_delayed_work(&priv->gather_stats);
cancel_delayed_work(&priv->request_scan);
+ cancel_delayed_work(&priv->request_direct_scan);
+ cancel_delayed_work(&priv->request_passive_scan);
cancel_delayed_work(&priv->scan_event);
cancel_delayed_work(&priv->rf_kill);
cancel_delayed_work(&priv->scan_check);
diff --git a/drivers/net/wireless/ipw2200.h b/drivers/net/wireless/ipw2200.h
index cd3295b66dd6..d4ab28b73b32 100644
--- a/drivers/net/wireless/ipw2200.h
+++ b/drivers/net/wireless/ipw2200.h
@@ -1037,6 +1037,7 @@ struct ipw_cmd { /* XXX */
#define STATUS_DISASSOC_PENDING (1<<12)
#define STATUS_STATE_PENDING (1<<13)
+#define STATUS_DIRECT_SCAN_PENDING (1<<19)
#define STATUS_SCAN_PENDING (1<<20)
#define STATUS_SCANNING (1<<21)
#define STATUS_SCAN_ABORTING (1<<22)
@@ -1292,6 +1293,8 @@ struct ipw_priv {
struct iw_public_data wireless_data;
int user_requested_scan;
+ u8 direct_scan_ssid[IW_ESSID_MAX_SIZE];
+ u8 direct_scan_ssid_len;
struct workqueue_struct *workqueue;
@@ -1301,8 +1304,9 @@ struct ipw_priv {
struct work_struct system_config;
struct work_struct rx_replenish;
struct delayed_work request_scan;
+ struct delayed_work request_direct_scan;
+ struct delayed_work request_passive_scan;
struct delayed_work scan_event;
- struct work_struct request_passive_scan;
struct work_struct adapter_restart;
struct delayed_work rf_kill;
struct work_struct up;
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-led.c b/drivers/net/wireless/iwlwifi/iwl-3945-led.c
index d200d08fb086..8b1528e52d43 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945-led.c
+++ b/drivers/net/wireless/iwlwifi/iwl-3945-led.c
@@ -229,14 +229,15 @@ static int iwl3945_led_register_led(struct iwl3945_priv *priv,
led->led_dev.brightness_set = iwl3945_led_brightness_set;
led->led_dev.default_trigger = trigger;
+ led->priv = priv;
+ led->type = type;
+
ret = led_classdev_register(device, &led->led_dev);
if (ret) {
IWL_ERROR("Error: failed to register led handler.\n");
return ret;
}
- led->priv = priv;
- led->type = type;
led->registered = 1;
if (set_led && led->led_on)
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c
index 6328b9593877..8124fd9b1353 100644
--- a/drivers/net/wireless/libertas/cmd.c
+++ b/drivers/net/wireless/libertas/cmd.c
@@ -1842,6 +1842,9 @@ static void lbs_send_confirmsleep(struct lbs_private *priv)
spin_lock_irqsave(&priv->driver_lock, flags);
+ /* We don't get a response on the sleep-confirmation */
+ priv->dnld_sent = DNLD_RES_RECEIVED;
+
/* If nothing to do, go back to sleep (?) */
if (!__kfifo_len(priv->event_fifo) && !priv->resp_len[priv->resp_idx])
priv->psstate = PS_STATE_SLEEP;
@@ -1904,12 +1907,12 @@ void lbs_ps_confirm_sleep(struct lbs_private *priv)
lbs_deb_enter(LBS_DEB_HOST);
+ spin_lock_irqsave(&priv->driver_lock, flags);
if (priv->dnld_sent) {
allowed = 0;
lbs_deb_host("dnld_sent was set\n");
}
- spin_lock_irqsave(&priv->driver_lock, flags);
/* In-progress command? */
if (priv->cur_cmd) {
allowed = 0;
diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c
index 54280e292ea5..d075b448da94 100644
--- a/drivers/net/wireless/libertas/if_cs.c
+++ b/drivers/net/wireless/libertas/if_cs.c
@@ -122,7 +122,7 @@ static inline void if_cs_write16(struct if_cs_card *card, uint reg, u16 val)
static inline void if_cs_write16_rep(
struct if_cs_card *card,
uint reg,
- void *buf,
+ const void *buf,
unsigned long count)
{
if (debug_output)
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
index 51f664bbee9d..3dd537be87d8 100644
--- a/drivers/net/wireless/libertas/if_sdio.c
+++ b/drivers/net/wireless/libertas/if_sdio.c
@@ -392,7 +392,7 @@ static int if_sdio_prog_helper(struct if_sdio_card *card)
unsigned long timeout;
u8 *chunk_buffer;
u32 chunk_size;
- u8 *firmware;
+ const u8 *firmware;
size_t size;
lbs_deb_enter(LBS_DEB_SDIO);
@@ -508,7 +508,7 @@ static int if_sdio_prog_real(struct if_sdio_card *card)
unsigned long timeout;
u8 *chunk_buffer;
u32 chunk_size;
- u8 *firmware;
+ const u8 *firmware;
size_t size, req_size;
lbs_deb_enter(LBS_DEB_SDIO);
diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c
index 8032df72aaab..a25b670e8339 100644
--- a/drivers/net/wireless/libertas/if_usb.c
+++ b/drivers/net/wireless/libertas/if_usb.c
@@ -293,7 +293,7 @@ static void if_usb_disconnect(struct usb_interface *intf)
static int if_usb_send_fw_pkt(struct if_usb_card *cardp)
{
struct fwdata *fwdata = cardp->ep_out_buf;
- uint8_t *firmware = cardp->fw->data;
+ const uint8_t *firmware = cardp->fw->data;
/* If we got a CRC failure on the last block, back
up and retry it */
@@ -746,7 +746,7 @@ static int if_usb_issue_boot_command(struct if_usb_card *cardp, int ivalue)
* len image length
* @return 0 or -1
*/
-static int check_fwfile_format(uint8_t *data, uint32_t totlen)
+static int check_fwfile_format(const uint8_t *data, uint32_t totlen)
{
uint32_t bincmd, exit;
uint32_t blksize, offset, len;
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index e1f066068590..acfc4bfcc262 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -732,8 +732,8 @@ static int lbs_thread(void *data)
lbs_deb_thread("4: currenttxskb %p, dnld_sent %d\n",
priv->currenttxskb, priv->dnld_sent);
- spin_lock_irq(&priv->driver_lock);
/* Process any pending command response */
+ spin_lock_irq(&priv->driver_lock);
resp_idx = priv->resp_idx;
if (priv->resp_len[resp_idx]) {
spin_unlock_irq(&priv->driver_lock);
diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c
index 1610a7308c1d..815c095ef797 100644
--- a/drivers/net/wireless/p54/p54usb.c
+++ b/drivers/net/wireless/p54/p54usb.c
@@ -376,7 +376,8 @@ static int p54u_upload_firmware_3887(struct ieee80211_hw *dev)
const struct firmware *fw_entry = NULL;
int err, alen;
u8 carry = 0;
- u8 *buf, *tmp, *data;
+ u8 *buf, *tmp;
+ const u8 *data;
unsigned int left, remains, block_size;
struct x2_header *hdr;
unsigned long timeout;
@@ -523,7 +524,7 @@ static int p54u_upload_firmware_net2280(struct ieee80211_hw *dev)
void *buf;
__le32 reg;
unsigned int remains, offset;
- u8 *data;
+ const u8 *data;
buf = kmalloc(512, GFP_KERNEL);
if (!buf) {
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index 611d98320593..20dea777ee45 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -511,8 +511,8 @@ struct rt2x00lib_ops {
*/
int (*probe_hw) (struct rt2x00_dev *rt2x00dev);
char *(*get_firmware_name) (struct rt2x00_dev *rt2x00dev);
- u16 (*get_firmware_crc) (void *data, const size_t len);
- int (*load_firmware) (struct rt2x00_dev *rt2x00dev, void *data,
+ u16 (*get_firmware_crc) (const void *data, const size_t len);
+ int (*load_firmware) (struct rt2x00_dev *rt2x00dev, const void *data,
const size_t len);
/*
diff --git a/drivers/net/wireless/rt2x00/rt2x00pci.h b/drivers/net/wireless/rt2x00/rt2x00pci.h
index 9d1cdb99431c..b41967ecbf6d 100644
--- a/drivers/net/wireless/rt2x00/rt2x00pci.h
+++ b/drivers/net/wireless/rt2x00/rt2x00pci.h
@@ -82,7 +82,7 @@ static inline void rt2x00pci_register_write(struct rt2x00_dev *rt2x00dev,
static inline void
rt2x00pci_register_multiwrite(struct rt2x00_dev *rt2x00dev,
const unsigned long offset,
- void *value, const u16 length)
+ const void *value, const u16 length)
{
memcpy_toio(rt2x00dev->csr.base + offset, value, length);
}
diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c
index 14bc7b281659..bb78de5290cd 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/rt2x00/rt61pci.c
@@ -915,7 +915,7 @@ static char *rt61pci_get_firmware_name(struct rt2x00_dev *rt2x00dev)
return fw_name;
}
-static u16 rt61pci_get_firmware_crc(void *data, const size_t len)
+static u16 rt61pci_get_firmware_crc(const void *data, const size_t len)
{
u16 crc;
@@ -932,7 +932,7 @@ static u16 rt61pci_get_firmware_crc(void *data, const size_t len)
return crc;
}
-static int rt61pci_load_firmware(struct rt2x00_dev *rt2x00dev, void *data,
+static int rt61pci_load_firmware(struct rt2x00_dev *rt2x00dev, const void *data,
const size_t len)
{
int i;
diff --git a/drivers/net/wireless/rt2x00/rt73usb.c b/drivers/net/wireless/rt2x00/rt73usb.c
index da19a3a91f4d..36c2f17505c6 100644
--- a/drivers/net/wireless/rt2x00/rt73usb.c
+++ b/drivers/net/wireless/rt2x00/rt73usb.c
@@ -850,7 +850,7 @@ static char *rt73usb_get_firmware_name(struct rt2x00_dev *rt2x00dev)
return FIRMWARE_RT2571;
}
-static u16 rt73usb_get_firmware_crc(void *data, const size_t len)
+static u16 rt73usb_get_firmware_crc(const void *data, const size_t len)
{
u16 crc;
@@ -867,13 +867,13 @@ static u16 rt73usb_get_firmware_crc(void *data, const size_t len)
return crc;
}
-static int rt73usb_load_firmware(struct rt2x00_dev *rt2x00dev, void *data,
+static int rt73usb_load_firmware(struct rt2x00_dev *rt2x00dev, const void *data,
const size_t len)
{
unsigned int i;
int status;
u32 reg;
- char *ptr = data;
+ const char *ptr = data;
char *cache;
int buflen;
int timeout;
diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c
index d5c0c66188ca..78baa0f7926d 100644
--- a/drivers/net/wireless/zd1201.c
+++ b/drivers/net/wireless/zd1201.c
@@ -49,7 +49,7 @@ MODULE_DEVICE_TABLE(usb, zd1201_table);
static int zd1201_fw_upload(struct usb_device *dev, int apfw)
{
const struct firmware *fw_entry;
- char *data;
+ const char *data;
unsigned long len;
int err;
unsigned char ret;
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 6424e5a2c83d..418606ac1c3b 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -719,7 +719,7 @@ int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length)
fc = le16_to_cpu(*((__le16 *) buffer));
is_qos = ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) &&
- ((fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_QOS_DATA);
+ (fc & IEEE80211_STYPE_QOS_DATA);
is_4addr = (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) ==
(IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS);
need_padding = is_qos ^ is_4addr;
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 4d1ce2e7361e..7d63f8ced24b 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -2,7 +2,7 @@
# Makefile for the PCI bus specific drivers.
#
-obj-y += access.o bus.o probe.o remove.o pci.o quirks.o \
+obj-y += access.o bus.o probe.o remove.o pci.o quirks.o slot.o \
pci-driver.o search.o pci-sysfs.o rom.o setup-res.o
obj-$(CONFIG_PROC_FS) += proc.o
diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c
index f8c187a763bd..93e37f0666ab 100644
--- a/drivers/pci/hotplug/acpi_pcihp.c
+++ b/drivers/pci/hotplug/acpi_pcihp.c
@@ -30,6 +30,7 @@
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
+#include <linux/pci-acpi.h>
#include <acpi/acpi.h>
#include <acpi/acpi_bus.h>
#include <acpi/actypes.h>
@@ -299,7 +300,7 @@ free_and_return:
*
* @handle - the handle of the hotplug controller.
*/
-acpi_status acpi_run_oshp(acpi_handle handle)
+static acpi_status acpi_run_oshp(acpi_handle handle)
{
acpi_status status;
struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -322,9 +323,6 @@ acpi_status acpi_run_oshp(acpi_handle handle)
kfree(string.pointer);
return status;
}
-EXPORT_SYMBOL_GPL(acpi_run_oshp);
-
-
/* acpi_get_hp_params_from_firmware
*
@@ -374,6 +372,85 @@ acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus,
}
EXPORT_SYMBOL_GPL(acpi_get_hp_params_from_firmware);
+/**
+ * acpi_get_hp_hw_control_from_firmware
+ * @dev: the pci_dev of the bridge that has a hotplug controller
+ * @flags: requested control bits for _OSC
+ *
+ * Attempt to take hotplug control from firmware.
+ */
+int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags)
+{
+ acpi_status status;
+ acpi_handle chandle, handle = DEVICE_ACPI_HANDLE(&(dev->dev));
+ struct pci_dev *pdev = dev;
+ struct pci_bus *parent;
+ struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
+
+ flags &= (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
+ OSC_SHPC_NATIVE_HP_CONTROL |
+ OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+ if (!flags) {
+ err("Invalid flags %u specified!\n", flags);
+ return -EINVAL;
+ }
+
+ /*
+ * Per PCI firmware specification, we should run the ACPI _OSC
+ * method to get control of hotplug hardware before using it. If
+ * an _OSC is missing, we look for an OSHP to do the same thing.
+ * To handle different BIOS behavior, we look for _OSC and OSHP
+ * within the scope of the hotplug controller and its parents,
+ * upto the host bridge under which this controller exists.
+ */
+ while (!handle) {
+ /*
+ * This hotplug controller was not listed in the ACPI name
+ * space at all. Try to get acpi handle of parent pci bus.
+ */
+ if (!pdev || !pdev->bus->parent)
+ break;
+ parent = pdev->bus->parent;
+ dbg("Could not find %s in acpi namespace, trying parent\n",
+ pci_name(pdev));
+ if (!parent->self)
+ /* Parent must be a host bridge */
+ handle = acpi_get_pci_rootbridge_handle(
+ pci_domain_nr(parent),
+ parent->number);
+ else
+ handle = DEVICE_ACPI_HANDLE(&(parent->self->dev));
+ pdev = parent->self;
+ }
+
+ while (handle) {
+ acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
+ dbg("Trying to get hotplug control for %s \n",
+ (char *)string.pointer);
+ status = pci_osc_control_set(handle, flags);
+ if (status == AE_NOT_FOUND)
+ status = acpi_run_oshp(handle);
+ if (ACPI_SUCCESS(status)) {
+ dbg("Gained control for hotplug HW for pci %s (%s)\n",
+ pci_name(dev), (char *)string.pointer);
+ kfree(string.pointer);
+ return 0;
+ }
+ if (acpi_root_bridge(handle))
+ break;
+ chandle = handle;
+ status = acpi_get_parent(chandle, &handle);
+ if (ACPI_FAILURE(status))
+ break;
+ }
+
+ dbg("Cannot get control of hotplug hardware for pci %s\n",
+ pci_name(dev));
+
+ kfree(string.pointer);
+ return -ENODEV;
+}
+EXPORT_SYMBOL(acpi_get_hp_hw_control_from_firmware);
/* acpi_root_bridge - check to see if this acpi object is a root bridge
*
diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h
index 7a29164d4b32..5a58b075dd8d 100644
--- a/drivers/pci/hotplug/acpiphp.h
+++ b/drivers/pci/hotplug/acpiphp.h
@@ -36,7 +36,7 @@
#define _ACPIPHP_H
#include <linux/acpi.h>
-#include <linux/kobject.h> /* for KOBJ_NAME_LEN */
+#include <linux/kobject.h>
#include <linux/mutex.h>
#include <linux/pci_hotplug.h>
@@ -51,7 +51,7 @@
#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg)
/* name size which is used for entries in pcihpfs */
-#define SLOT_NAME_SIZE KOBJ_NAME_LEN /* {_SUN} */
+#define SLOT_NAME_SIZE 20 /* {_SUN} */
struct acpiphp_bridge;
struct acpiphp_slot;
@@ -215,7 +215,6 @@ extern u8 acpiphp_get_power_status (struct acpiphp_slot *slot);
extern u8 acpiphp_get_attention_status (struct acpiphp_slot *slot);
extern u8 acpiphp_get_latch_status (struct acpiphp_slot *slot);
extern u8 acpiphp_get_adapter_status (struct acpiphp_slot *slot);
-extern u32 acpiphp_get_address (struct acpiphp_slot *slot);
/* variables */
extern int acpiphp_debug;
diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c
index 7af68ba27903..0e496e866a84 100644
--- a/drivers/pci/hotplug/acpiphp_core.c
+++ b/drivers/pci/hotplug/acpiphp_core.c
@@ -70,7 +70,6 @@ static int disable_slot (struct hotplug_slot *slot);
static int set_attention_status (struct hotplug_slot *slot, u8 value);
static int get_power_status (struct hotplug_slot *slot, u8 *value);
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
-static int get_address (struct hotplug_slot *slot, u32 *value);
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
@@ -83,7 +82,6 @@ static struct hotplug_slot_ops acpi_hotplug_slot_ops = {
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_status,
- .get_address = get_address,
};
@@ -274,23 +272,6 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
return 0;
}
-
-/**
- * get_address - get pci address of a slot
- * @hotplug_slot: slot to get status
- * @value: pointer to struct pci_busdev (seg, bus, dev)
- */
-static int get_address(struct hotplug_slot *hotplug_slot, u32 *value)
-{
- struct slot *slot = hotplug_slot->private;
-
- dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
-
- *value = acpiphp_get_address(slot->acpi_slot);
-
- return 0;
-}
-
static int __init init_acpi(void)
{
int retval;
@@ -357,7 +338,11 @@ int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
acpiphp_slot->slot = slot;
snprintf(slot->name, sizeof(slot->name), "%u", slot->acpi_slot->sun);
- retval = pci_hp_register(slot->hotplug_slot);
+ retval = pci_hp_register(slot->hotplug_slot,
+ acpiphp_slot->bridge->pci_bus,
+ acpiphp_slot->device);
+ if (retval == -EBUSY)
+ goto error_hpslot;
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
goto error_hpslot;
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index 648596d469f6..9342c848db29 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -258,7 +258,12 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
bridge->pci_bus->number, slot->device);
retval = acpiphp_register_hotplug_slot(slot);
if (retval) {
- warn("acpiphp_register_hotplug_slot failed(err code = 0x%x)\n", retval);
+ if (retval == -EBUSY)
+ warn("Slot %d already registered by another "
+ "hotplug driver\n", slot->sun);
+ else
+ warn("acpiphp_register_hotplug_slot failed "
+ "(err code = 0x%x)\n", retval);
goto err_exit;
}
}
@@ -1867,19 +1872,3 @@ u8 acpiphp_get_adapter_status(struct acpiphp_slot *slot)
return (sta == 0) ? 0 : 1;
}
-
-
-/*
- * pci address (seg/bus/dev)
- */
-u32 acpiphp_get_address(struct acpiphp_slot *slot)
-{
- u32 address;
- struct pci_bus *pci_bus = slot->bridge->pci_bus;
-
- address = (pci_domain_nr(pci_bus) << 16) |
- (pci_bus->number << 8) |
- slot->device;
-
- return address;
-}
diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c
index ede9051fdb5d..2b7c45e39370 100644
--- a/drivers/pci/hotplug/acpiphp_ibm.c
+++ b/drivers/pci/hotplug/acpiphp_ibm.c
@@ -33,8 +33,10 @@
#include <linux/kobject.h>
#include <asm/uaccess.h>
#include <linux/moduleparam.h>
+#include <linux/pci.h>
#include "acpiphp.h"
+#include "../pci.h"
#define DRIVER_VERSION "1.0.1"
#define DRIVER_AUTHOR "Irene Zubarev <zubarev@us.ibm.com>, Vernon Mauery <vernux@us.ibm.com>"
@@ -430,7 +432,7 @@ static int __init ibm_acpiphp_init(void)
int retval = 0;
acpi_status status;
struct acpi_device *device;
- struct kobject *sysdir = &pci_hotplug_slots_kset->kobj;
+ struct kobject *sysdir = &pci_slots_kset->kobj;
dbg("%s\n", __func__);
@@ -477,7 +479,7 @@ init_return:
static void __exit ibm_acpiphp_exit(void)
{
acpi_status status;
- struct kobject *sysdir = &pci_hotplug_slots_kset->kobj;
+ struct kobject *sysdir = &pci_slots_kset->kobj;
dbg("%s\n", __func__);
diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c
index d8a6b80ab42a..935947991dc9 100644
--- a/drivers/pci/hotplug/cpci_hotplug_core.c
+++ b/drivers/pci/hotplug/cpci_hotplug_core.c
@@ -285,7 +285,7 @@ cpci_hp_register_bus(struct pci_bus *bus, u8 first, u8 last)
info->attention_status = cpci_get_attention_status(slot);
dbg("registering slot %s", slot->hotplug_slot->name);
- status = pci_hp_register(slot->hotplug_slot);
+ status = pci_hp_register(slot->hotplug_slot, bus, i);
if (status) {
err("pci_hp_register failed with error %d", status);
goto error_name;
diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c
index 36b115b27b0b..54defec51d08 100644
--- a/drivers/pci/hotplug/cpqphp_core.c
+++ b/drivers/pci/hotplug/cpqphp_core.c
@@ -434,7 +434,9 @@ static int ctrl_slot_setup(struct controller *ctrl,
slot->bus, slot->device,
slot->number, ctrl->slot_device_offset,
slot_number);
- result = pci_hp_register(hotplug_slot);
+ result = pci_hp_register(hotplug_slot,
+ ctrl->pci_dev->subordinate,
+ slot->device);
if (result) {
err("pci_hp_register failed with error %d\n", result);
goto error_name;
diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c
index 7e9a827c2687..40337a06c18a 100644
--- a/drivers/pci/hotplug/fakephp.c
+++ b/drivers/pci/hotplug/fakephp.c
@@ -66,6 +66,7 @@ struct dummy_slot {
struct pci_dev *dev;
struct work_struct remove_work;
unsigned long removed;
+ char name[8];
};
static int debug;
@@ -100,6 +101,7 @@ static int add_slot(struct pci_dev *dev)
struct dummy_slot *dslot;
struct hotplug_slot *slot;
int retval = -ENOMEM;
+ static int count = 1;
slot = kzalloc(sizeof(struct hotplug_slot), GFP_KERNEL);
if (!slot)
@@ -113,18 +115,18 @@ static int add_slot(struct pci_dev *dev)
slot->info->max_bus_speed = PCI_SPEED_UNKNOWN;
slot->info->cur_bus_speed = PCI_SPEED_UNKNOWN;
- slot->name = &dev->dev.bus_id[0];
- dbg("slot->name = %s\n", slot->name);
-
dslot = kzalloc(sizeof(struct dummy_slot), GFP_KERNEL);
if (!dslot)
goto error_info;
+ slot->name = dslot->name;
+ snprintf(slot->name, sizeof(dslot->name), "fake%d", count++);
+ dbg("slot->name = %s\n", slot->name);
slot->ops = &dummy_hotplug_slot_ops;
slot->release = &dummy_release;
slot->private = dslot;
- retval = pci_hp_register(slot);
+ retval = pci_hp_register(slot, dev->bus, PCI_SLOT(dev->devfn));
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
goto error_dslot;
@@ -148,17 +150,17 @@ error:
static int __init pci_scan_buses(void)
{
struct pci_dev *dev = NULL;
- int retval = 0;
+ int lastslot = 0;
while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
- retval = add_slot(dev);
- if (retval) {
- pci_dev_put(dev);
- break;
- }
+ if (PCI_FUNC(dev->devfn) > 0 &&
+ lastslot == PCI_SLOT(dev->devfn))
+ continue;
+ lastslot = PCI_SLOT(dev->devfn);
+ add_slot(dev);
}
- return retval;
+ return 0;
}
static void remove_slot(struct dummy_slot *dslot)
@@ -296,23 +298,9 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
return 0;
}
-/* find the hotplug_slot for the pci_dev */
-static struct hotplug_slot *get_slot_from_dev(struct pci_dev *dev)
-{
- struct dummy_slot *dslot;
-
- list_for_each_entry(dslot, &slot_list, node) {
- if (dslot->dev == dev)
- return dslot->slot;
- }
- return NULL;
-}
-
-
static int disable_slot(struct hotplug_slot *slot)
{
struct dummy_slot *dslot;
- struct hotplug_slot *hslot;
struct pci_dev *dev;
int func;
@@ -322,41 +310,27 @@ static int disable_slot(struct hotplug_slot *slot)
dbg("%s - physical_slot = %s\n", __func__, slot->name);
- /* don't disable bridged devices just yet, we can't handle them easily... */
- if (dslot->dev->subordinate) {
- err("Can't remove PCI devices with other PCI devices behind it yet.\n");
- return -ENODEV;
- }
- if (test_and_set_bit(0, &dslot->removed)) {
- dbg("Slot already scheduled for removal\n");
- return -ENODEV;
- }
- /* search for subfunctions and disable them first */
- if (!(dslot->dev->devfn & 7)) {
- for (func = 1; func < 8; func++) {
- dev = pci_get_slot(dslot->dev->bus,
- dslot->dev->devfn + func);
- if (dev) {
- hslot = get_slot_from_dev(dev);
- if (hslot)
- disable_slot(hslot);
- else {
- err("Hotplug slot not found for subfunction of PCI device\n");
- return -ENODEV;
- }
- pci_dev_put(dev);
- } else
- dbg("No device in slot found\n");
+ for (func = 7; func >= 0; func--) {
+ dev = pci_get_slot(dslot->dev->bus, dslot->dev->devfn + func);
+ if (!dev)
+ continue;
+
+ if (test_and_set_bit(0, &dslot->removed)) {
+ dbg("Slot already scheduled for removal\n");
+ return -ENODEV;
}
- }
- /* remove the device from the pci core */
- pci_remove_bus_device(dslot->dev);
+ /* queue work item to blow away this sysfs entry and other
+ * parts.
+ */
+ INIT_WORK(&dslot->remove_work, remove_slot_worker);
+ queue_work(dummyphp_wq, &dslot->remove_work);
- /* queue work item to blow away this sysfs entry and other parts. */
- INIT_WORK(&dslot->remove_work, remove_slot_worker);
- queue_work(dummyphp_wq, &dslot->remove_work);
+ /* blow away this sysfs entry and other parts. */
+ remove_slot(dslot);
+ pci_dev_put(dev);
+ }
return 0;
}
diff --git a/drivers/pci/hotplug/ibmphp_ebda.c b/drivers/pci/hotplug/ibmphp_ebda.c
index dca7efc14be2..8467d0287325 100644
--- a/drivers/pci/hotplug/ibmphp_ebda.c
+++ b/drivers/pci/hotplug/ibmphp_ebda.c
@@ -1001,7 +1001,8 @@ static int __init ebda_rsrc_controller (void)
tmp_slot = list_entry (list, struct slot, ibm_slot_list);
snprintf (tmp_slot->hotplug_slot->name, 30, "%s", create_file_name (tmp_slot));
- pci_hp_register (tmp_slot->hotplug_slot);
+ pci_hp_register(tmp_slot->hotplug_slot,
+ pci_find_bus(0, tmp_slot->bus), tmp_slot->device);
}
print_ebda_hpc ();
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
index a11021e8ce37..b3b2d8cf4370 100644
--- a/drivers/pci/hotplug/pci_hotplug_core.c
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -40,6 +40,7 @@
#include <linux/pci.h>
#include <linux/pci_hotplug.h>
#include <asm/uaccess.h>
+#include "../pci.h"
#define MY_NAME "pci_hotplug"
@@ -60,41 +61,7 @@ static int debug;
//////////////////////////////////////////////////////////////////
static LIST_HEAD(pci_hotplug_slot_list);
-
-struct kset *pci_hotplug_slots_kset;
-
-static ssize_t hotplug_slot_attr_show(struct kobject *kobj,
- struct attribute *attr, char *buf)
-{
- struct hotplug_slot *slot = to_hotplug_slot(kobj);
- struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
- return attribute->show ? attribute->show(slot, buf) : -EIO;
-}
-
-static ssize_t hotplug_slot_attr_store(struct kobject *kobj,
- struct attribute *attr, const char *buf, size_t len)
-{
- struct hotplug_slot *slot = to_hotplug_slot(kobj);
- struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
- return attribute->store ? attribute->store(slot, buf, len) : -EIO;
-}
-
-static struct sysfs_ops hotplug_slot_sysfs_ops = {
- .show = hotplug_slot_attr_show,
- .store = hotplug_slot_attr_store,
-};
-
-static void hotplug_slot_release(struct kobject *kobj)
-{
- struct hotplug_slot *slot = to_hotplug_slot(kobj);
- if (slot->release)
- slot->release(slot);
-}
-
-static struct kobj_type hotplug_slot_ktype = {
- .sysfs_ops = &hotplug_slot_sysfs_ops,
- .release = &hotplug_slot_release,
-};
+static DEFINE_SPINLOCK(pci_hotplug_slot_list_lock);
/* these strings match up with the values in pci_bus_speed */
static char *pci_bus_speed_strings[] = {
@@ -149,16 +116,15 @@ GET_STATUS(power_status, u8)
GET_STATUS(attention_status, u8)
GET_STATUS(latch_status, u8)
GET_STATUS(adapter_status, u8)
-GET_STATUS(address, u32)
GET_STATUS(max_bus_speed, enum pci_bus_speed)
GET_STATUS(cur_bus_speed, enum pci_bus_speed)
-static ssize_t power_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t power_read_file(struct pci_slot *slot, char *buf)
{
int retval;
u8 value;
- retval = get_power_status (slot, &value);
+ retval = get_power_status(slot->hotplug, &value);
if (retval)
goto exit;
retval = sprintf (buf, "%d\n", value);
@@ -166,9 +132,10 @@ exit:
return retval;
}
-static ssize_t power_write_file (struct hotplug_slot *slot, const char *buf,
+static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
size_t count)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
unsigned long lpower;
u8 power;
int retval = 0;
@@ -204,29 +171,30 @@ exit:
return count;
}
-static struct hotplug_slot_attribute hotplug_slot_attr_power = {
+static struct pci_slot_attribute hotplug_slot_attr_power = {
.attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
.show = power_read_file,
.store = power_write_file
};
-static ssize_t attention_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t attention_read_file(struct pci_slot *slot, char *buf)
{
int retval;
u8 value;
- retval = get_attention_status (slot, &value);
+ retval = get_attention_status(slot->hotplug, &value);
if (retval)
goto exit;
- retval = sprintf (buf, "%d\n", value);
+ retval = sprintf(buf, "%d\n", value);
exit:
return retval;
}
-static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf,
+static ssize_t attention_write_file(struct pci_slot *slot, const char *buf,
size_t count)
{
+ struct hotplug_slot_ops *ops = slot->hotplug->ops;
unsigned long lattention;
u8 attention;
int retval = 0;
@@ -235,13 +203,13 @@ static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf,
attention = (u8)(lattention & 0xff);
dbg (" - attention = %d\n", attention);
- if (!try_module_get(slot->ops->owner)) {
+ if (!try_module_get(ops->owner)) {
retval = -ENODEV;
goto exit;
}
- if (slot->ops->set_attention_status)
- retval = slot->ops->set_attention_status(slot, attention);
- module_put(slot->ops->owner);
+ if (ops->set_attention_status)
+ retval = ops->set_attention_status(slot->hotplug, attention);
+ module_put(ops->owner);
exit:
if (retval)
@@ -249,18 +217,18 @@ exit:
return count;
}
-static struct hotplug_slot_attribute hotplug_slot_attr_attention = {
+static struct pci_slot_attribute hotplug_slot_attr_attention = {
.attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
.show = attention_read_file,
.store = attention_write_file
};
-static ssize_t latch_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t latch_read_file(struct pci_slot *slot, char *buf)
{
int retval;
u8 value;
- retval = get_latch_status (slot, &value);
+ retval = get_latch_status(slot->hotplug, &value);
if (retval)
goto exit;
retval = sprintf (buf, "%d\n", value);
@@ -269,17 +237,17 @@ exit:
return retval;
}
-static struct hotplug_slot_attribute hotplug_slot_attr_latch = {
+static struct pci_slot_attribute hotplug_slot_attr_latch = {
.attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
.show = latch_read_file,
};
-static ssize_t presence_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t presence_read_file(struct pci_slot *slot, char *buf)
{
int retval;
u8 value;
- retval = get_adapter_status (slot, &value);
+ retval = get_adapter_status(slot->hotplug, &value);
if (retval)
goto exit;
retval = sprintf (buf, "%d\n", value);
@@ -288,42 +256,20 @@ exit:
return retval;
}
-static struct hotplug_slot_attribute hotplug_slot_attr_presence = {
+static struct pci_slot_attribute hotplug_slot_attr_presence = {
.attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
.show = presence_read_file,
};
-static ssize_t address_read_file (struct hotplug_slot *slot, char *buf)
-{
- int retval;
- u32 address;
-
- retval = get_address (slot, &address);
- if (retval)
- goto exit;
- retval = sprintf (buf, "%04x:%02x:%02x\n",
- (address >> 16) & 0xffff,
- (address >> 8) & 0xff,
- address & 0xff);
-
-exit:
- return retval;
-}
-
-static struct hotplug_slot_attribute hotplug_slot_attr_address = {
- .attr = {.name = "address", .mode = S_IFREG | S_IRUGO},
- .show = address_read_file,
-};
-
static char *unknown_speed = "Unknown bus speed";
-static ssize_t max_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t max_bus_speed_read_file(struct pci_slot *slot, char *buf)
{
char *speed_string;
int retval;
enum pci_bus_speed value;
- retval = get_max_bus_speed (slot, &value);
+ retval = get_max_bus_speed(slot->hotplug, &value);
if (retval)
goto exit;
@@ -338,18 +284,18 @@ exit:
return retval;
}
-static struct hotplug_slot_attribute hotplug_slot_attr_max_bus_speed = {
+static struct pci_slot_attribute hotplug_slot_attr_max_bus_speed = {
.attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO},
.show = max_bus_speed_read_file,
};
-static ssize_t cur_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
+static ssize_t cur_bus_speed_read_file(struct pci_slot *slot, char *buf)
{
char *speed_string;
int retval;
enum pci_bus_speed value;
- retval = get_cur_bus_speed (slot, &value);
+ retval = get_cur_bus_speed(slot->hotplug, &value);
if (retval)
goto exit;
@@ -364,14 +310,15 @@ exit:
return retval;
}
-static struct hotplug_slot_attribute hotplug_slot_attr_cur_bus_speed = {
+static struct pci_slot_attribute hotplug_slot_attr_cur_bus_speed = {
.attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO},
.show = cur_bus_speed_read_file,
};
-static ssize_t test_write_file (struct hotplug_slot *slot, const char *buf,
+static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
size_t count)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
unsigned long ltest;
u32 test;
int retval = 0;
@@ -394,13 +341,14 @@ exit:
return count;
}
-static struct hotplug_slot_attribute hotplug_slot_attr_test = {
+static struct pci_slot_attribute hotplug_slot_attr_test = {
.attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
.store = test_write_file
};
-static int has_power_file (struct hotplug_slot *slot)
+static int has_power_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if ((slot->ops->enable_slot) ||
@@ -410,8 +358,9 @@ static int has_power_file (struct hotplug_slot *slot)
return -ENOENT;
}
-static int has_attention_file (struct hotplug_slot *slot)
+static int has_attention_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if ((slot->ops->set_attention_status) ||
@@ -420,8 +369,9 @@ static int has_attention_file (struct hotplug_slot *slot)
return -ENOENT;
}
-static int has_latch_file (struct hotplug_slot *slot)
+static int has_latch_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_latch_status)
@@ -429,8 +379,9 @@ static int has_latch_file (struct hotplug_slot *slot)
return -ENOENT;
}
-static int has_adapter_file (struct hotplug_slot *slot)
+static int has_adapter_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_adapter_status)
@@ -438,17 +389,9 @@ static int has_adapter_file (struct hotplug_slot *slot)
return -ENOENT;
}
-static int has_address_file (struct hotplug_slot *slot)
-{
- if ((!slot) || (!slot->ops))
- return -ENODEV;
- if (slot->ops->get_address)
- return 0;
- return -ENOENT;
-}
-
-static int has_max_bus_speed_file (struct hotplug_slot *slot)
+static int has_max_bus_speed_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_max_bus_speed)
@@ -456,8 +399,9 @@ static int has_max_bus_speed_file (struct hotplug_slot *slot)
return -ENOENT;
}
-static int has_cur_bus_speed_file (struct hotplug_slot *slot)
+static int has_cur_bus_speed_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->get_cur_bus_speed)
@@ -465,8 +409,9 @@ static int has_cur_bus_speed_file (struct hotplug_slot *slot)
return -ENOENT;
}
-static int has_test_file (struct hotplug_slot *slot)
+static int has_test_file(struct pci_slot *pci_slot)
{
+ struct hotplug_slot *slot = pci_slot->hotplug;
if ((!slot) || (!slot->ops))
return -ENODEV;
if (slot->ops->hardware_test)
@@ -474,7 +419,7 @@ static int has_test_file (struct hotplug_slot *slot)
return -ENOENT;
}
-static int fs_add_slot (struct hotplug_slot *slot)
+static int fs_add_slot(struct pci_slot *slot)
{
int retval = 0;
@@ -505,13 +450,6 @@ static int fs_add_slot (struct hotplug_slot *slot)
goto exit_adapter;
}
- if (has_address_file(slot) == 0) {
- retval = sysfs_create_file(&slot->kobj,
- &hotplug_slot_attr_address.attr);
- if (retval)
- goto exit_address;
- }
-
if (has_max_bus_speed_file(slot) == 0) {
retval = sysfs_create_file(&slot->kobj,
&hotplug_slot_attr_max_bus_speed.attr);
@@ -544,10 +482,6 @@ exit_cur_speed:
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
exit_max_speed:
- if (has_address_file(slot) == 0)
- sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
-
-exit_address:
if (has_adapter_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
@@ -567,7 +501,7 @@ exit:
return retval;
}
-static void fs_remove_slot (struct hotplug_slot *slot)
+static void fs_remove_slot(struct pci_slot *slot)
{
if (has_power_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
@@ -581,9 +515,6 @@ static void fs_remove_slot (struct hotplug_slot *slot)
if (has_adapter_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
- if (has_address_file(slot) == 0)
- sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
-
if (has_max_bus_speed_file(slot) == 0)
sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
@@ -599,12 +530,16 @@ static struct hotplug_slot *get_slot_from_name (const char *name)
struct hotplug_slot *slot;
struct list_head *tmp;
+ spin_lock(&pci_hotplug_slot_list_lock);
list_for_each (tmp, &pci_hotplug_slot_list) {
slot = list_entry (tmp, struct hotplug_slot, slot_list);
if (strcmp(slot->name, name) == 0)
- return slot;
+ goto out;
}
- return NULL;
+ slot = NULL;
+out:
+ spin_unlock(&pci_hotplug_slot_list_lock);
+ return slot;
}
/**
@@ -616,9 +551,10 @@ static struct hotplug_slot *get_slot_from_name (const char *name)
*
* Returns 0 if successful, anything else for an error.
*/
-int pci_hp_register (struct hotplug_slot *slot)
+int pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, int slot_nr)
{
int result;
+ struct pci_slot *pci_slot;
struct hotplug_slot *tmp;
if (slot == NULL)
@@ -636,19 +572,44 @@ int pci_hp_register (struct hotplug_slot *slot)
if (tmp)
return -EEXIST;
- slot->kobj.kset = pci_hotplug_slots_kset;
- result = kobject_init_and_add(&slot->kobj, &hotplug_slot_ktype, NULL,
- "%s", slot->name);
- if (result) {
- err("Unable to register kobject '%s'", slot->name);
- return -EINVAL;
+ /*
+ * No problems if we call this interface from both ACPI_PCI_SLOT
+ * driver and call it here again. If we've already created the
+ * pci_slot, the interface will simply bump the refcount.
+ */
+ pci_slot = pci_create_slot(bus, slot_nr, slot->name);
+ if (IS_ERR(pci_slot))
+ return PTR_ERR(pci_slot);
+
+ if (pci_slot->hotplug) {
+ dbg("%s: already claimed\n", __func__);
+ pci_destroy_slot(pci_slot);
+ return -EBUSY;
}
- list_add (&slot->slot_list, &pci_hotplug_slot_list);
+ slot->pci_slot = pci_slot;
+ pci_slot->hotplug = slot;
+
+ /*
+ * Allow pcihp drivers to override the ACPI_PCI_SLOT name.
+ */
+ if (strcmp(kobject_name(&pci_slot->kobj), slot->name)) {
+ result = kobject_rename(&pci_slot->kobj, slot->name);
+ if (result) {
+ pci_destroy_slot(pci_slot);
+ return result;
+ }
+ }
+
+ spin_lock(&pci_hotplug_slot_list_lock);
+ list_add(&slot->slot_list, &pci_hotplug_slot_list);
+ spin_unlock(&pci_hotplug_slot_list_lock);
+
+ result = fs_add_slot(pci_slot);
+ kobject_uevent(&pci_slot->kobj, KOBJ_ADD);
+ dbg("Added slot %s to the list\n", slot->name);
+
- result = fs_add_slot (slot);
- kobject_uevent(&slot->kobj, KOBJ_ADD);
- dbg ("Added slot %s to the list\n", slot->name);
return result;
}
@@ -661,22 +622,30 @@ int pci_hp_register (struct hotplug_slot *slot)
*
* Returns 0 if successful, anything else for an error.
*/
-int pci_hp_deregister (struct hotplug_slot *slot)
+int pci_hp_deregister(struct hotplug_slot *hotplug)
{
struct hotplug_slot *temp;
+ struct pci_slot *slot;
- if (slot == NULL)
+ if (!hotplug)
return -ENODEV;
- temp = get_slot_from_name (slot->name);
- if (temp != slot) {
+ temp = get_slot_from_name(hotplug->name);
+ if (temp != hotplug)
return -ENODEV;
- }
- list_del (&slot->slot_list);
- fs_remove_slot (slot);
- dbg ("Removed slot %s from the list\n", slot->name);
- kobject_put(&slot->kobj);
+ spin_lock(&pci_hotplug_slot_list_lock);
+ list_del(&hotplug->slot_list);
+ spin_unlock(&pci_hotplug_slot_list_lock);
+
+ slot = hotplug->pci_slot;
+ fs_remove_slot(slot);
+ dbg("Removed slot %s from the list\n", hotplug->name);
+
+ hotplug->release(hotplug);
+ slot->hotplug = NULL;
+ pci_destroy_slot(slot);
+
return 0;
}
@@ -690,13 +659,15 @@ int pci_hp_deregister (struct hotplug_slot *slot)
*
* Returns 0 if successful, anything else for an error.
*/
-int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
+int __must_check pci_hp_change_slot_info(struct hotplug_slot *hotplug,
struct hotplug_slot_info *info)
{
- if ((slot == NULL) || (info == NULL))
+ struct pci_slot *slot;
+ if (!hotplug || !info)
return -ENODEV;
+ slot = hotplug->pci_slot;
- memcpy (slot->info, info, sizeof (struct hotplug_slot_info));
+ memcpy(hotplug->info, info, sizeof(struct hotplug_slot_info));
return 0;
}
@@ -704,36 +675,22 @@ int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
static int __init pci_hotplug_init (void)
{
int result;
- struct kset *pci_bus_kset;
- pci_bus_kset = bus_get_kset(&pci_bus_type);
-
- pci_hotplug_slots_kset = kset_create_and_add("slots", NULL,
- &pci_bus_kset->kobj);
- if (!pci_hotplug_slots_kset) {
- result = -ENOMEM;
- err("Register subsys error\n");
- goto exit;
- }
result = cpci_hotplug_init(debug);
if (result) {
err ("cpci_hotplug_init with error %d\n", result);
- goto err_subsys;
+ goto err_cpci;
}
info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
- goto exit;
-err_subsys:
- kset_unregister(pci_hotplug_slots_kset);
-exit:
+err_cpci:
return result;
}
static void __exit pci_hotplug_exit (void)
{
cpci_hotplug_exit();
- kset_unregister(pci_hotplug_slots_kset);
}
module_init(pci_hotplug_init);
@@ -745,7 +702,6 @@ MODULE_LICENSE("GPL");
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
-EXPORT_SYMBOL_GPL(pci_hotplug_slots_kset);
EXPORT_SYMBOL_GPL(pci_hp_register);
EXPORT_SYMBOL_GPL(pci_hp_deregister);
EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
index 79c9ddaad3fb..8492fab800cc 100644
--- a/drivers/pci/hotplug/pciehp.h
+++ b/drivers/pci/hotplug/pciehp.h
@@ -202,8 +202,13 @@ struct hpc_ops {
#include <acpi/actypes.h>
#include <linux/pci-acpi.h>
-#define pciehp_get_hp_hw_control_from_firmware(dev) \
- pciehp_acpi_get_hp_hw_control_from_firmware(dev)
+static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev)
+{
+ u32 flags = (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
+ OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+ return acpi_get_hp_hw_control_from_firmware(dev, flags);
+}
+
static inline int pciehp_get_hp_params_from_firmware(struct pci_dev *dev,
struct hotplug_params *hpp)
{
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index 48a2ed378914..7b21c86e4bff 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -41,7 +41,7 @@ int pciehp_debug;
int pciehp_poll_mode;
int pciehp_poll_time;
int pciehp_force;
-int pciehp_slot_with_bus;
+static int pciehp_slot_with_bus;
struct workqueue_struct *pciehp_wq;
#define DRIVER_VERSION "0.4"
@@ -72,7 +72,6 @@ static int get_power_status (struct hotplug_slot *slot, u8 *value);
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
-static int get_address (struct hotplug_slot *slot, u32 *value);
static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
@@ -85,7 +84,6 @@ static struct hotplug_slot_ops pciehp_hotplug_slot_ops = {
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_status,
- .get_address = get_address,
.get_max_bus_speed = get_max_bus_speed,
.get_cur_bus_speed = get_cur_bus_speed,
};
@@ -252,7 +250,9 @@ static int init_slots(struct controller *ctrl)
dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x "
"slot_device_offset=%x\n", slot->bus, slot->device,
slot->hp_slot, slot->number, ctrl->slot_device_offset);
- retval = pci_hp_register(hotplug_slot);
+ retval = pci_hp_register(hotplug_slot,
+ ctrl->pci_dev->subordinate,
+ slot->device);
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
if (retval == -EEXIST)
@@ -263,7 +263,7 @@ static int init_slots(struct controller *ctrl)
}
/* create additional sysfs entries */
if (EMI(ctrl)) {
- retval = sysfs_create_file(&hotplug_slot->kobj,
+ retval = sysfs_create_file(&hotplug_slot->pci_slot->kobj,
&hotplug_slot_attr_lock.attr);
if (retval) {
pci_hp_deregister(hotplug_slot);
@@ -296,7 +296,7 @@ static void cleanup_slots(struct controller *ctrl)
slot = list_entry(tmp, struct slot, slot_list);
list_del(&slot->slot_list);
if (EMI(ctrl))
- sysfs_remove_file(&slot->hotplug_slot->kobj,
+ sysfs_remove_file(&slot->hotplug_slot->pci_slot->kobj,
&hotplug_slot_attr_lock.attr);
cancel_delayed_work(&slot->work);
flush_scheduled_work();
@@ -398,19 +398,8 @@ static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
return 0;
}
-static int get_address(struct hotplug_slot *hotplug_slot, u32 *value)
-{
- struct slot *slot = hotplug_slot->private;
- struct pci_bus *bus = slot->ctrl->pci_dev->subordinate;
-
- dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
-
- *value = (pci_domain_nr(bus) << 16) | (slot->bus << 8) | slot->device;
-
- return 0;
-}
-
-static int get_max_bus_speed(struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+static int get_max_bus_speed(struct hotplug_slot *hotplug_slot,
+ enum pci_bus_speed *value)
{
struct slot *slot = hotplug_slot->private;
int retval;
@@ -444,7 +433,13 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_
struct controller *ctrl;
struct slot *t_slot;
u8 value;
- struct pci_dev *pdev;
+ struct pci_dev *pdev = dev->port;
+
+ if (pciehp_force)
+ dbg("Bypassing BIOS check for pciehp use on %s\n",
+ pci_name(pdev));
+ else if (pciehp_get_hp_hw_control_from_firmware(pdev))
+ goto err_out_none;
ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
if (!ctrl) {
@@ -453,9 +448,6 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_
}
INIT_LIST_HEAD(&ctrl->slot_list);
- pdev = dev->port;
- ctrl->pci_dev = pdev;
-
rc = pcie_init(ctrl, dev);
if (rc) {
dbg("%s: controller initialization failed\n", PCIE_MODULE_NAME);
@@ -471,7 +463,12 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_
/* Setup the slot information structures */
rc = init_slots(ctrl);
if (rc) {
- err("%s: slot initialization failed\n", PCIE_MODULE_NAME);
+ if (rc == -EBUSY)
+ warn("%s: slot already registered by another "
+ "hotplug driver\n", PCIE_MODULE_NAME);
+ else
+ err("%s: slot initialization failed\n",
+ PCIE_MODULE_NAME);
goto err_out_release_ctlr;
}
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 79f104963166..0cfaedf2edca 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -268,9 +268,8 @@ completed:
return timeout;
}
-static inline int pcie_wait_cmd(struct controller *ctrl, int poll)
+static inline void pcie_wait_cmd(struct controller *ctrl, int poll)
{
- int retval = 0;
unsigned int msecs = pciehp_poll_mode ? 2500 : 1000;
unsigned long timeout = msecs_to_jiffies(msecs);
int rc;
@@ -278,16 +277,9 @@ static inline int pcie_wait_cmd(struct controller *ctrl, int poll)
if (poll)
rc = pcie_poll_cmd(ctrl);
else
- rc = wait_event_interruptible_timeout(ctrl->queue,
- !ctrl->cmd_busy, timeout);
+ rc = wait_event_timeout(ctrl->queue, !ctrl->cmd_busy, timeout);
if (!rc)
dbg("Command not completed in 1000 msec\n");
- else if (rc < 0) {
- retval = -EINTR;
- info("Command was interrupted by a signal\n");
- }
-
- return retval;
}
/**
@@ -365,7 +357,7 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
if (!(slot_ctrl & HP_INTR_ENABLE) ||
!(slot_ctrl & CMD_CMPL_INTR_ENABLE))
poll = 1;
- retval = pcie_wait_cmd(ctrl, poll);
+ pcie_wait_cmd(ctrl, poll);
}
out:
mutex_unlock(&ctrl->ctrl_lock);
@@ -785,7 +777,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
intr_loc |= detected;
if (!intr_loc)
return IRQ_NONE;
- if (pciehp_writew(ctrl, SLOTSTATUS, detected)) {
+ if (detected && pciehp_writew(ctrl, SLOTSTATUS, detected)) {
err("%s: Cannot write to SLOTSTATUS\n", __func__);
return IRQ_NONE;
}
@@ -797,7 +789,7 @@ static irqreturn_t pcie_isr(int irq, void *dev_id)
if (intr_loc & CMD_COMPLETED) {
ctrl->cmd_busy = 0;
smp_mb();
- wake_up_interruptible(&ctrl->queue);
+ wake_up(&ctrl->queue);
}
if (!(intr_loc & ~CMD_COMPLETED))
@@ -1017,75 +1009,6 @@ static struct hpc_ops pciehp_hpc_ops = {
.check_lnk_status = hpc_check_lnk_status,
};
-#ifdef CONFIG_ACPI
-static int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev)
-{
- acpi_status status;
- acpi_handle chandle, handle = DEVICE_ACPI_HANDLE(&(dev->dev));
- struct pci_dev *pdev = dev;
- struct pci_bus *parent;
- struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
-
- /*
- * Per PCI firmware specification, we should run the ACPI _OSC
- * method to get control of hotplug hardware before using it.
- * If an _OSC is missing, we look for an OSHP to do the same thing.
- * To handle different BIOS behavior, we look for _OSC and OSHP
- * within the scope of the hotplug controller and its parents, upto
- * the host bridge under which this controller exists.
- */
- while (!handle) {
- /*
- * This hotplug controller was not listed in the ACPI name
- * space at all. Try to get acpi handle of parent pci bus.
- */
- if (!pdev || !pdev->bus->parent)
- break;
- parent = pdev->bus->parent;
- dbg("Could not find %s in acpi namespace, trying parent\n",
- pci_name(pdev));
- if (!parent->self)
- /* Parent must be a host bridge */
- handle = acpi_get_pci_rootbridge_handle(
- pci_domain_nr(parent),
- parent->number);
- else
- handle = DEVICE_ACPI_HANDLE(
- &(parent->self->dev));
- pdev = parent->self;
- }
-
- while (handle) {
- acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
- dbg("Trying to get hotplug control for %s \n",
- (char *)string.pointer);
- status = pci_osc_control_set(handle,
- OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL |
- OSC_PCI_EXPRESS_NATIVE_HP_CONTROL);
- if (status == AE_NOT_FOUND)
- status = acpi_run_oshp(handle);
- if (ACPI_SUCCESS(status)) {
- dbg("Gained control for hotplug HW for pci %s (%s)\n",
- pci_name(dev), (char *)string.pointer);
- kfree(string.pointer);
- return 0;
- }
- if (acpi_root_bridge(handle))
- break;
- chandle = handle;
- status = acpi_get_parent(chandle, &handle);
- if (ACPI_FAILURE(status))
- break;
- }
-
- dbg("Cannot get control of hotplug hardware for pci %s\n",
- pci_name(dev));
-
- kfree(string.pointer);
- return -1;
-}
-#endif
-
static int pcie_init_hardware_part1(struct controller *ctrl,
struct pcie_device *dev)
{
@@ -1122,23 +1045,10 @@ int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev)
if (pcie_write_cmd(ctrl, cmd, mask)) {
err("%s: Cannot enable software notification\n", __func__);
- goto abort;
+ return -1;
}
- if (pciehp_force)
- dbg("Bypassing BIOS check for pciehp use on %s\n",
- pci_name(ctrl->pci_dev));
- else if (pciehp_get_hp_hw_control_from_firmware(ctrl->pci_dev))
- goto abort_disable_intr;
-
return 0;
-
- /* We end up here for the many possible ways to fail this API. */
-abort_disable_intr:
- if (pcie_write_cmd(ctrl, 0, HP_INTR_ENABLE))
- err("%s : disabling interrupts failed\n", __func__);
-abort:
- return -1;
}
static inline void dbg_ctrl(struct controller *ctrl)
@@ -1176,7 +1086,7 @@ static inline void dbg_ctrl(struct controller *ctrl)
dbg(" Comamnd Completed : %3s\n", NO_CMD_CMPL(ctrl)? "no" : "yes");
pciehp_readw(ctrl, SLOTSTATUS, &reg16);
dbg("Slot Status : 0x%04x\n", reg16);
- pciehp_readw(ctrl, SLOTSTATUS, &reg16);
+ pciehp_readw(ctrl, SLOTCTRL, &reg16);
dbg("Slot Control : 0x%04x\n", reg16);
}
diff --git a/drivers/pci/hotplug/rpadlpar_sysfs.c b/drivers/pci/hotplug/rpadlpar_sysfs.c
index 779c5db71be4..a796301ea03f 100644
--- a/drivers/pci/hotplug/rpadlpar_sysfs.c
+++ b/drivers/pci/hotplug/rpadlpar_sysfs.c
@@ -14,8 +14,10 @@
*/
#include <linux/kobject.h>
#include <linux/string.h>
+#include <linux/pci.h>
#include <linux/pci_hotplug.h>
#include "rpadlpar.h"
+#include "../pci.h"
#define DLPAR_KOBJ_NAME "control"
@@ -27,7 +29,6 @@
#define MAX_DRC_NAME_LEN 64
-
static ssize_t add_slot_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t nbytes)
{
@@ -112,7 +113,7 @@ int dlpar_sysfs_init(void)
int error;
dlpar_kobj = kobject_create_and_add(DLPAR_KOBJ_NAME,
- &pci_hotplug_slots_kset->kobj);
+ &pci_slots_kset->kobj);
if (!dlpar_kobj)
return -EINVAL;
diff --git a/drivers/pci/hotplug/rpaphp_slot.c b/drivers/pci/hotplug/rpaphp_slot.c
index 56197b600d36..9b714ea93d20 100644
--- a/drivers/pci/hotplug/rpaphp_slot.c
+++ b/drivers/pci/hotplug/rpaphp_slot.c
@@ -33,33 +33,6 @@
#include <asm/rtas.h>
#include "rpaphp.h"
-static ssize_t address_read_file (struct hotplug_slot *php_slot, char *buf)
-{
- int retval;
- struct slot *slot = (struct slot *)php_slot->private;
- struct pci_bus *bus;
-
- if (!slot)
- return -ENOENT;
-
- bus = slot->bus;
- if (!bus)
- return -ENOENT;
-
- if (bus->self)
- retval = sprintf(buf, pci_name(bus->self));
- else
- retval = sprintf(buf, "%04x:%02x:00.0",
- pci_domain_nr(bus), bus->number);
-
- return retval;
-}
-
-static struct hotplug_slot_attribute php_attr_address = {
- .attr = {.name = "address", .mode = S_IFREG | S_IRUGO},
- .show = address_read_file,
-};
-
/* free up the memory used by a slot */
static void rpaphp_release_slot(struct hotplug_slot *hotplug_slot)
{
@@ -135,9 +108,6 @@ int rpaphp_deregister_slot(struct slot *slot)
list_del(&slot->rpaphp_slot_list);
- /* remove "address" file */
- sysfs_remove_file(&php_slot->kobj, &php_attr_address.attr);
-
retval = pci_hp_deregister(php_slot);
if (retval)
err("Problem unregistering a slot %s\n", slot->name);
@@ -151,6 +121,7 @@ int rpaphp_register_slot(struct slot *slot)
{
struct hotplug_slot *php_slot = slot->hotplug_slot;
int retval;
+ int slotno;
dbg("%s registering slot:path[%s] index[%x], name[%s] pdomain[%x] type[%d]\n",
__func__, slot->dn->full_name, slot->index, slot->name,
@@ -162,19 +133,16 @@ int rpaphp_register_slot(struct slot *slot)
return -EAGAIN;
}
- retval = pci_hp_register(php_slot);
+ if (slot->dn->child)
+ slotno = PCI_SLOT(PCI_DN(slot->dn->child)->devfn);
+ else
+ slotno = -1;
+ retval = pci_hp_register(php_slot, slot->bus, slotno);
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
return retval;
}
- /* create "address" file */
- retval = sysfs_create_file(&php_slot->kobj, &php_attr_address.attr);
- if (retval) {
- err("sysfs_create_file failed with error %d\n", retval);
- goto sysfs_fail;
- }
-
/* add slot to our internal list */
list_add(&slot->rpaphp_slot_list, &rpaphp_slot_head);
info("Slot [%s] registered\n", slot->name);
diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c
index 2fe37cd85b69..410fe0394a8e 100644
--- a/drivers/pci/hotplug/sgi_hotplug.c
+++ b/drivers/pci/hotplug/sgi_hotplug.c
@@ -197,13 +197,15 @@ static int sn_hp_slot_private_alloc(struct hotplug_slot *bss_hotplug_slot,
static struct hotplug_slot * sn_hp_destroy(void)
{
struct slot *slot;
+ struct pci_slot *pci_slot;
struct hotplug_slot *bss_hotplug_slot = NULL;
list_for_each_entry(slot, &sn_hp_list, hp_list) {
bss_hotplug_slot = slot->hotplug_slot;
+ pci_slot = bss_hotplug_slot->pci_slot;
list_del(&((struct slot *)bss_hotplug_slot->private)->
hp_list);
- sysfs_remove_file(&bss_hotplug_slot->kobj,
+ sysfs_remove_file(&pci_slot->kobj,
&sn_slot_path_attr.attr);
break;
}
@@ -614,6 +616,7 @@ static void sn_release_slot(struct hotplug_slot *bss_hotplug_slot)
static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
{
int device;
+ struct pci_slot *pci_slot;
struct hotplug_slot *bss_hotplug_slot;
int rc = 0;
@@ -650,11 +653,12 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
bss_hotplug_slot->ops = &sn_hotplug_slot_ops;
bss_hotplug_slot->release = &sn_release_slot;
- rc = pci_hp_register(bss_hotplug_slot);
+ rc = pci_hp_register(bss_hotplug_slot, pci_bus, device);
if (rc)
goto register_err;
- rc = sysfs_create_file(&bss_hotplug_slot->kobj,
+ pci_slot = bss_hotplug_slot->pci_slot;
+ rc = sysfs_create_file(&pci_slot->kobj,
&sn_slot_path_attr.attr);
if (rc)
goto register_err;
@@ -664,7 +668,7 @@ static int sn_hotplug_slot_register(struct pci_bus *pci_bus)
register_err:
dev_dbg(&pci_bus->self->dev, "bus failed to register with err = %d\n",
- rc);
+ rc);
alloc_err:
if (rc == -ENOMEM)
diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h
index f66e8d6315ab..8a026f750deb 100644
--- a/drivers/pci/hotplug/shpchp.h
+++ b/drivers/pci/hotplug/shpchp.h
@@ -170,6 +170,7 @@ extern void shpchp_queue_pushbutton_work(struct work_struct *work);
extern int shpc_init( struct controller *ctrl, struct pci_dev *pdev);
#ifdef CONFIG_ACPI
+#include <linux/pci-acpi.h>
static inline int get_hp_params_from_firmware(struct pci_dev *dev,
struct hotplug_params *hpp)
{
@@ -177,14 +178,15 @@ static inline int get_hp_params_from_firmware(struct pci_dev *dev,
return -ENODEV;
return 0;
}
-#define get_hp_hw_control_from_firmware(pdev) \
- do { \
- if (DEVICE_ACPI_HANDLE(&(pdev->dev))) \
- acpi_run_oshp(DEVICE_ACPI_HANDLE(&(pdev->dev)));\
- } while (0)
+
+static inline int get_hp_hw_control_from_firmware(struct pci_dev *dev)
+{
+ u32 flags = OSC_SHPC_NATIVE_HP_CONTROL;
+ return acpi_get_hp_hw_control_from_firmware(dev, flags);
+}
#else
#define get_hp_params_from_firmware(dev, hpp) (-ENODEV)
-#define get_hp_hw_control_from_firmware(dev) do { } while (0)
+#define get_hp_hw_control_from_firmware(dev) (0)
#endif
struct ctrl_reg {
diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c
index 97848654652a..a8cbd039b85b 100644
--- a/drivers/pci/hotplug/shpchp_core.c
+++ b/drivers/pci/hotplug/shpchp_core.c
@@ -39,7 +39,7 @@
int shpchp_debug;
int shpchp_poll_mode;
int shpchp_poll_time;
-int shpchp_slot_with_bus;
+static int shpchp_slot_with_bus;
struct workqueue_struct *shpchp_wq;
#define DRIVER_VERSION "0.4"
@@ -68,7 +68,6 @@ static int get_power_status (struct hotplug_slot *slot, u8 *value);
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
-static int get_address (struct hotplug_slot *slot, u32 *value);
static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
@@ -81,7 +80,6 @@ static struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_status,
- .get_address = get_address,
.get_max_bus_speed = get_max_bus_speed,
.get_cur_bus_speed = get_cur_bus_speed,
};
@@ -159,7 +157,8 @@ static int init_slots(struct controller *ctrl)
dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x "
"slot_device_offset=%x\n", slot->bus, slot->device,
slot->hp_slot, slot->number, ctrl->slot_device_offset);
- retval = pci_hp_register(slot->hotplug_slot);
+ retval = pci_hp_register(slot->hotplug_slot,
+ ctrl->pci_dev->subordinate, slot->device);
if (retval) {
err("pci_hp_register failed with error %d\n", retval);
if (retval == -EEXIST)
@@ -288,19 +287,8 @@ static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value)
return 0;
}
-static int get_address (struct hotplug_slot *hotplug_slot, u32 *value)
-{
- struct slot *slot = get_slot(hotplug_slot);
- struct pci_bus *bus = slot->ctrl->pci_dev->subordinate;
-
- dbg("%s - physical_slot = %s\n", __func__, hotplug_slot->name);
-
- *value = (pci_domain_nr(bus) << 16) | (slot->bus << 8) | slot->device;
-
- return 0;
-}
-
-static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
+static int get_max_bus_speed(struct hotplug_slot *hotplug_slot,
+ enum pci_bus_speed *value)
{
struct slot *slot = get_slot(hotplug_slot);
int retval;
@@ -330,13 +318,14 @@ static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_sp
static int is_shpc_capable(struct pci_dev *dev)
{
- if ((dev->vendor == PCI_VENDOR_ID_AMD) || (dev->device ==
- PCI_DEVICE_ID_AMD_GOLAM_7450))
- return 1;
- if (pci_find_capability(dev, PCI_CAP_ID_SHPC))
- return 1;
-
- return 0;
+ if ((dev->vendor == PCI_VENDOR_ID_AMD) || (dev->device ==
+ PCI_DEVICE_ID_AMD_GOLAM_7450))
+ return 1;
+ if (!pci_find_capability(dev, PCI_CAP_ID_SHPC))
+ return 0;
+ if (get_hp_hw_control_from_firmware(dev))
+ return 0;
+ return 1;
}
static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
diff --git a/drivers/pci/hotplug/shpchp_hpc.c b/drivers/pci/hotplug/shpchp_hpc.c
index 7d770b2cd889..7a0bff364cd4 100644
--- a/drivers/pci/hotplug/shpchp_hpc.c
+++ b/drivers/pci/hotplug/shpchp_hpc.c
@@ -1084,7 +1084,6 @@ int shpc_init(struct controller *ctrl, struct pci_dev *pdev)
dbg("%s: HPC at b:d:f:irq=0x%x:%x:%x:%x\n", __func__,
pdev->bus->number, PCI_SLOT(pdev->devfn),
PCI_FUNC(pdev->devfn), pdev->irq);
- get_hp_hw_control_from_firmware(pdev);
/*
* If this is the first controller to be initialized,
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c
index 66c0fd21894b..4f05d91a0fd8 100644
--- a/drivers/pci/intel-iommu.c
+++ b/drivers/pci/intel-iommu.c
@@ -1725,7 +1725,6 @@ int __init init_dmars(void)
deferred_flush = kzalloc(g_num_of_iommus *
sizeof(struct deferred_flush_tables), GFP_KERNEL);
if (!deferred_flush) {
- kfree(g_iommus);
ret = -ENOMEM;
goto error;
}
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 8c61304cbb37..ccb1974464fb 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -70,12 +70,10 @@ arch_teardown_msi_irqs(struct pci_dev *dev)
}
}
-static void msi_set_enable(struct pci_dev *dev, int enable)
+static void __msi_set_enable(struct pci_dev *dev, int pos, int enable)
{
- int pos;
u16 control;
- pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
if (pos) {
pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);
control &= ~PCI_MSI_FLAGS_ENABLE;
@@ -85,6 +83,11 @@ static void msi_set_enable(struct pci_dev *dev, int enable)
}
}
+static void msi_set_enable(struct pci_dev *dev, int enable)
+{
+ __msi_set_enable(dev, pci_find_capability(dev, PCI_CAP_ID_MSI), enable);
+}
+
static void msix_set_enable(struct pci_dev *dev, int enable)
{
int pos;
@@ -141,7 +144,8 @@ static void msi_set_mask_bits(unsigned int irq, u32 mask, u32 flag)
mask_bits |= flag & mask;
pci_write_config_dword(entry->dev, pos, mask_bits);
} else {
- msi_set_enable(entry->dev, !flag);
+ __msi_set_enable(entry->dev, entry->msi_attrib.pos,
+ !flag);
}
break;
case PCI_CAP_ID_MSIX:
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index 9d6fc8e6285d..056ea80ee27a 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -21,12 +21,19 @@
struct acpi_osc_data {
acpi_handle handle;
- u32 ctrlset_buf[3];
- u32 global_ctrlsets;
+ u32 support_set;
+ u32 control_set;
+ int is_queried;
+ u32 query_result;
struct list_head sibiling;
};
static LIST_HEAD(acpi_osc_data_list);
+struct acpi_osc_args {
+ u32 capbuf[3];
+ u32 query_result;
+};
+
static struct acpi_osc_data *acpi_get_osc_data(acpi_handle handle)
{
struct acpi_osc_data *data;
@@ -44,42 +51,18 @@ static struct acpi_osc_data *acpi_get_osc_data(acpi_handle handle)
return data;
}
-static u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40, 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66};
+static u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40,
+ 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66};
-static acpi_status
-acpi_query_osc (
- acpi_handle handle,
- u32 level,
- void *context,
- void **retval )
+static acpi_status acpi_run_osc(acpi_handle handle,
+ struct acpi_osc_args *osc_args)
{
- acpi_status status;
- struct acpi_object_list input;
- union acpi_object in_params[4];
- struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
- union acpi_object *out_obj;
- u32 osc_dw0;
- acpi_status *ret_status = (acpi_status *)retval;
- struct acpi_osc_data *osc_data;
- u32 flags = (unsigned long)context, temp;
- acpi_handle tmp;
-
- status = acpi_get_handle(handle, "_OSC", &tmp);
- if (ACPI_FAILURE(status))
- return status;
-
- osc_data = acpi_get_osc_data(handle);
- if (!osc_data) {
- printk(KERN_ERR "acpi osc data array is full\n");
- return AE_ERROR;
- }
-
- osc_data->ctrlset_buf[OSC_SUPPORT_TYPE] |= (flags & OSC_SUPPORT_MASKS);
-
- /* do _OSC query for all possible controls */
- temp = osc_data->ctrlset_buf[OSC_CONTROL_TYPE];
- osc_data->ctrlset_buf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
- osc_data->ctrlset_buf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS;
+ acpi_status status;
+ struct acpi_object_list input;
+ union acpi_object in_params[4];
+ struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
+ union acpi_object *out_obj;
+ u32 osc_dw0, flags = osc_args->capbuf[OSC_QUERY_TYPE];
/* Setting up input parameters */
input.count = 4;
@@ -93,20 +76,19 @@ acpi_query_osc (
in_params[2].integer.value = 3;
in_params[3].type = ACPI_TYPE_BUFFER;
in_params[3].buffer.length = 12;
- in_params[3].buffer.pointer = (u8 *)osc_data->ctrlset_buf;
+ in_params[3].buffer.pointer = (u8 *)osc_args->capbuf;
status = acpi_evaluate_object(handle, "_OSC", &input, &output);
if (ACPI_FAILURE(status))
- goto out_nofree;
- out_obj = output.pointer;
+ return status;
+ out_obj = output.pointer;
if (out_obj->type != ACPI_TYPE_BUFFER) {
- printk(KERN_DEBUG
- "Evaluate _OSC returns wrong type\n");
+ printk(KERN_DEBUG "Evaluate _OSC returns wrong type\n");
status = AE_TYPE;
- goto query_osc_out;
+ goto out_kfree;
}
- osc_dw0 = *((u32 *) out_obj->buffer.pointer);
+ osc_dw0 = *((u32 *)out_obj->buffer.pointer);
if (osc_dw0) {
if (osc_dw0 & OSC_REQUEST_ERROR)
printk(KERN_DEBUG "_OSC request fails\n");
@@ -115,93 +97,58 @@ acpi_query_osc (
if (osc_dw0 & OSC_INVALID_REVISION_ERROR)
printk(KERN_DEBUG "_OSC invalid revision\n");
if (osc_dw0 & OSC_CAPABILITIES_MASK_ERROR) {
- /* Update Global Control Set */
- osc_data->global_ctrlsets =
- *((u32 *)(out_obj->buffer.pointer + 8));
- status = AE_OK;
- goto query_osc_out;
+ if (flags & OSC_QUERY_ENABLE)
+ goto out_success;
+ printk(KERN_DEBUG "_OSC FW not grant req. control\n");
+ status = AE_SUPPORT;
+ goto out_kfree;
}
status = AE_ERROR;
- goto query_osc_out;
+ goto out_kfree;
}
-
- /* Update Global Control Set */
- osc_data->global_ctrlsets = *((u32 *)(out_obj->buffer.pointer + 8));
+out_success:
+ if (flags & OSC_QUERY_ENABLE)
+ osc_args->query_result =
+ *((u32 *)(out_obj->buffer.pointer + 8));
status = AE_OK;
-query_osc_out:
+out_kfree:
kfree(output.pointer);
-out_nofree:
- *ret_status = status;
-
- osc_data->ctrlset_buf[OSC_QUERY_TYPE] = !OSC_QUERY_ENABLE;
- osc_data->ctrlset_buf[OSC_CONTROL_TYPE] = temp;
- if (ACPI_FAILURE(status)) {
- /* no osc support at all */
- osc_data->ctrlset_buf[OSC_SUPPORT_TYPE] = 0;
- }
-
return status;
}
-
-static acpi_status
-acpi_run_osc (
- acpi_handle handle,
- void *context)
+static acpi_status acpi_query_osc(acpi_handle handle,
+ u32 level, void *context, void **retval)
{
- acpi_status status;
- struct acpi_object_list input;
- union acpi_object in_params[4];
- struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
- union acpi_object *out_obj;
- u32 osc_dw0;
-
- /* Setting up input parameters */
- input.count = 4;
- input.pointer = in_params;
- in_params[0].type = ACPI_TYPE_BUFFER;
- in_params[0].buffer.length = 16;
- in_params[0].buffer.pointer = OSC_UUID;
- in_params[1].type = ACPI_TYPE_INTEGER;
- in_params[1].integer.value = 1;
- in_params[2].type = ACPI_TYPE_INTEGER;
- in_params[2].integer.value = 3;
- in_params[3].type = ACPI_TYPE_BUFFER;
- in_params[3].buffer.length = 12;
- in_params[3].buffer.pointer = (u8 *)context;
+ acpi_status status;
+ struct acpi_osc_data *osc_data;
+ u32 flags = (unsigned long)context, support_set;
+ acpi_handle tmp;
+ struct acpi_osc_args osc_args;
- status = acpi_evaluate_object(handle, "_OSC", &input, &output);
- if (ACPI_FAILURE (status))
+ status = acpi_get_handle(handle, "_OSC", &tmp);
+ if (ACPI_FAILURE(status))
return status;
- out_obj = output.pointer;
- if (out_obj->type != ACPI_TYPE_BUFFER) {
- printk(KERN_DEBUG
- "Evaluate _OSC returns wrong type\n");
- status = AE_TYPE;
- goto run_osc_out;
+ osc_data = acpi_get_osc_data(handle);
+ if (!osc_data) {
+ printk(KERN_ERR "acpi osc data array is full\n");
+ return AE_ERROR;
}
- osc_dw0 = *((u32 *) out_obj->buffer.pointer);
- if (osc_dw0) {
- if (osc_dw0 & OSC_REQUEST_ERROR)
- printk(KERN_DEBUG "_OSC request fails\n");
- if (osc_dw0 & OSC_INVALID_UUID_ERROR)
- printk(KERN_DEBUG "_OSC invalid UUID\n");
- if (osc_dw0 & OSC_INVALID_REVISION_ERROR)
- printk(KERN_DEBUG "_OSC invalid revision\n");
- if (osc_dw0 & OSC_CAPABILITIES_MASK_ERROR) {
- printk(KERN_DEBUG "_OSC FW not grant req. control\n");
- status = AE_SUPPORT;
- goto run_osc_out;
- }
- status = AE_ERROR;
- goto run_osc_out;
+
+ /* do _OSC query for all possible controls */
+ support_set = osc_data->support_set | (flags & OSC_SUPPORT_MASKS);
+ osc_args.capbuf[OSC_QUERY_TYPE] = OSC_QUERY_ENABLE;
+ osc_args.capbuf[OSC_SUPPORT_TYPE] = support_set;
+ osc_args.capbuf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS;
+
+ status = acpi_run_osc(handle, &osc_args);
+ if (ACPI_SUCCESS(status)) {
+ osc_data->support_set = support_set;
+ osc_data->query_result = osc_args.query_result;
+ osc_data->is_queried = 1;
}
- status = AE_OK;
-run_osc_out:
- kfree(output.pointer);
return status;
}
@@ -215,15 +162,11 @@ run_osc_out:
**/
acpi_status __pci_osc_support_set(u32 flags, const char *hid)
{
- acpi_status retval = AE_NOT_FOUND;
-
- if (!(flags & OSC_SUPPORT_MASKS)) {
+ if (!(flags & OSC_SUPPORT_MASKS))
return AE_TYPE;
- }
- acpi_get_devices(hid,
- acpi_query_osc,
- (void *)(unsigned long)flags,
- (void **) &retval );
+
+ acpi_get_devices(hid, acpi_query_osc,
+ (void *)(unsigned long)flags, NULL);
return AE_OK;
}
@@ -236,10 +179,11 @@ acpi_status __pci_osc_support_set(u32 flags, const char *hid)
**/
acpi_status pci_osc_control_set(acpi_handle handle, u32 flags)
{
- acpi_status status;
- u32 ctrlset;
+ acpi_status status;
+ u32 ctrlset, control_set;
acpi_handle tmp;
struct acpi_osc_data *osc_data;
+ struct acpi_osc_args osc_args;
status = acpi_get_handle(handle, "_OSC", &tmp);
if (ACPI_FAILURE(status))
@@ -252,19 +196,21 @@ acpi_status pci_osc_control_set(acpi_handle handle, u32 flags)
}
ctrlset = (flags & OSC_CONTROL_MASKS);
- if (!ctrlset) {
+ if (!ctrlset)
return AE_TYPE;
- }
- if (osc_data->ctrlset_buf[OSC_SUPPORT_TYPE] &&
- ((osc_data->global_ctrlsets & ctrlset) != ctrlset)) {
+
+ if (osc_data->is_queried &&
+ ((osc_data->query_result & ctrlset) != ctrlset))
return AE_SUPPORT;
- }
- osc_data->ctrlset_buf[OSC_CONTROL_TYPE] |= ctrlset;
- status = acpi_run_osc(handle, osc_data->ctrlset_buf);
- if (ACPI_FAILURE (status)) {
- osc_data->ctrlset_buf[OSC_CONTROL_TYPE] &= ~ctrlset;
- }
-
+
+ control_set = osc_data->control_set | ctrlset;
+ osc_args.capbuf[OSC_QUERY_TYPE] = 0;
+ osc_args.capbuf[OSC_SUPPORT_TYPE] = osc_data->support_set;
+ osc_args.capbuf[OSC_CONTROL_TYPE] = control_set;
+ status = acpi_run_osc(handle, &osc_args);
+ if (ACPI_SUCCESS(status))
+ osc_data->control_set = control_set;
+
return status;
}
EXPORT_SYMBOL(pci_osc_control_set);
@@ -293,13 +239,11 @@ EXPORT_SYMBOL(pci_osc_control_set);
* choose highest power _SxD or any lower power
*/
-static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev,
- pm_message_t state)
+static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev)
{
int acpi_state;
- acpi_state = acpi_pm_device_sleep_state(&pdev->dev,
- device_may_wakeup(&pdev->dev), NULL);
+ acpi_state = acpi_pm_device_sleep_state(&pdev->dev, NULL);
if (acpi_state < 0)
return PCI_POWER_ERROR;
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 72cf61ed8f96..a13f53486114 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -181,7 +181,7 @@ static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev,
any need to change it. */
struct mempolicy *oldpol;
cpumask_t oldmask = current->cpus_allowed;
- int node = pcibus_to_node(dev->bus);
+ int node = dev_to_node(&dev->dev);
if (node >= 0) {
node_to_cpumask_ptr(nodecpumask, node);
@@ -274,7 +274,57 @@ static int pci_device_remove(struct device * dev)
return 0;
}
-static int pci_device_suspend(struct device * dev, pm_message_t state)
+static void pci_device_shutdown(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct pci_driver *drv = pci_dev->driver;
+
+ if (drv && drv->shutdown)
+ drv->shutdown(pci_dev);
+ pci_msi_shutdown(pci_dev);
+ pci_msix_shutdown(pci_dev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+
+/*
+ * Default "suspend" method for devices that have no driver provided suspend,
+ * or not even a driver at all.
+ */
+static void pci_default_pm_suspend(struct pci_dev *pci_dev)
+{
+ pci_save_state(pci_dev);
+ /*
+ * mark its power state as "unknown", since we don't know if
+ * e.g. the BIOS will change its device state when we suspend.
+ */
+ if (pci_dev->current_state == PCI_D0)
+ pci_dev->current_state = PCI_UNKNOWN;
+}
+
+/*
+ * Default "resume" method for devices that have no driver provided resume,
+ * or not even a driver at all.
+ */
+static int pci_default_pm_resume(struct pci_dev *pci_dev)
+{
+ int retval = 0;
+
+ /* restore the PCI config space */
+ pci_restore_state(pci_dev);
+ /* if the device was enabled before suspend, reenable */
+ retval = pci_reenable_device(pci_dev);
+ /*
+ * if the device was busmaster before the suspend, make it busmaster
+ * again
+ */
+ if (pci_dev->is_busmaster)
+ pci_set_master(pci_dev);
+
+ return retval;
+}
+
+static int pci_legacy_suspend(struct device *dev, pm_message_t state)
{
struct pci_dev * pci_dev = to_pci_dev(dev);
struct pci_driver * drv = pci_dev->driver;
@@ -284,18 +334,12 @@ static int pci_device_suspend(struct device * dev, pm_message_t state)
i = drv->suspend(pci_dev, state);
suspend_report_result(drv->suspend, i);
} else {
- pci_save_state(pci_dev);
- /*
- * mark its power state as "unknown", since we don't know if
- * e.g. the BIOS will change its device state when we suspend.
- */
- if (pci_dev->current_state == PCI_D0)
- pci_dev->current_state = PCI_UNKNOWN;
+ pci_default_pm_suspend(pci_dev);
}
return i;
}
-static int pci_device_suspend_late(struct device * dev, pm_message_t state)
+static int pci_legacy_suspend_late(struct device *dev, pm_message_t state)
{
struct pci_dev * pci_dev = to_pci_dev(dev);
struct pci_driver * drv = pci_dev->driver;
@@ -308,26 +352,7 @@ static int pci_device_suspend_late(struct device * dev, pm_message_t state)
return i;
}
-/*
- * Default resume method for devices that have no driver provided resume,
- * or not even a driver at all.
- */
-static int pci_default_resume(struct pci_dev *pci_dev)
-{
- int retval = 0;
-
- /* restore the PCI config space */
- pci_restore_state(pci_dev);
- /* if the device was enabled before suspend, reenable */
- retval = pci_reenable_device(pci_dev);
- /* if the device was busmaster before the suspend, make it busmaster again */
- if (pci_dev->is_busmaster)
- pci_set_master(pci_dev);
-
- return retval;
-}
-
-static int pci_device_resume(struct device * dev)
+static int pci_legacy_resume(struct device *dev)
{
int error;
struct pci_dev * pci_dev = to_pci_dev(dev);
@@ -336,34 +361,313 @@ static int pci_device_resume(struct device * dev)
if (drv && drv->resume)
error = drv->resume(pci_dev);
else
- error = pci_default_resume(pci_dev);
+ error = pci_default_pm_resume(pci_dev);
return error;
}
-static int pci_device_resume_early(struct device * dev)
+static int pci_legacy_resume_early(struct device *dev)
{
int error = 0;
struct pci_dev * pci_dev = to_pci_dev(dev);
struct pci_driver * drv = pci_dev->driver;
- pci_fixup_device(pci_fixup_resume, pci_dev);
-
if (drv && drv->resume_early)
error = drv->resume_early(pci_dev);
return error;
}
-static void pci_device_shutdown(struct device *dev)
+static int pci_pm_prepare(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int error = 0;
+
+ if (drv && drv->pm && drv->pm->prepare)
+ error = drv->pm->prepare(dev);
+
+ return error;
+}
+
+static void pci_pm_complete(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+
+ if (drv && drv->pm && drv->pm->complete)
+ drv->pm->complete(dev);
+}
+
+#ifdef CONFIG_SUSPEND
+
+static int pci_pm_suspend(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct device_driver *drv = dev->driver;
+ int error = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->suspend) {
+ error = drv->pm->suspend(dev);
+ suspend_report_result(drv->pm->suspend, error);
+ } else {
+ pci_default_pm_suspend(pci_dev);
+ }
+ } else {
+ error = pci_legacy_suspend(dev, PMSG_SUSPEND);
+ }
+ pci_fixup_device(pci_fixup_suspend, pci_dev);
+
+ return error;
+}
+
+static int pci_pm_suspend_noirq(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
struct pci_driver *drv = pci_dev->driver;
+ int error = 0;
- if (drv && drv->shutdown)
- drv->shutdown(pci_dev);
- pci_msi_shutdown(pci_dev);
- pci_msix_shutdown(pci_dev);
+ if (drv && drv->pm) {
+ if (drv->pm->suspend_noirq) {
+ error = drv->pm->suspend_noirq(dev);
+ suspend_report_result(drv->pm->suspend_noirq, error);
+ }
+ } else {
+ error = pci_legacy_suspend_late(dev, PMSG_SUSPEND);
+ }
+
+ return error;
}
+static int pci_pm_resume(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct device_driver *drv = dev->driver;
+ int error;
+
+ pci_fixup_device(pci_fixup_resume, pci_dev);
+
+ if (drv && drv->pm) {
+ error = drv->pm->resume ? drv->pm->resume(dev) :
+ pci_default_pm_resume(pci_dev);
+ } else {
+ error = pci_legacy_resume(dev);
+ }
+
+ return error;
+}
+
+static int pci_pm_resume_noirq(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct pci_driver *drv = pci_dev->driver;
+ int error = 0;
+
+ pci_fixup_device(pci_fixup_resume_early, pci_dev);
+
+ if (drv && drv->pm) {
+ if (drv->pm->resume_noirq)
+ error = drv->pm->resume_noirq(dev);
+ } else {
+ error = pci_legacy_resume_early(dev);
+ }
+
+ return error;
+}
+
+#else /* !CONFIG_SUSPEND */
+
+#define pci_pm_suspend NULL
+#define pci_pm_suspend_noirq NULL
+#define pci_pm_resume NULL
+#define pci_pm_resume_noirq NULL
+
+#endif /* !CONFIG_SUSPEND */
+
+#ifdef CONFIG_HIBERNATION
+
+static int pci_pm_freeze(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct device_driver *drv = dev->driver;
+ int error = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->freeze) {
+ error = drv->pm->freeze(dev);
+ suspend_report_result(drv->pm->freeze, error);
+ } else {
+ pci_default_pm_suspend(pci_dev);
+ }
+ } else {
+ error = pci_legacy_suspend(dev, PMSG_FREEZE);
+ pci_fixup_device(pci_fixup_suspend, pci_dev);
+ }
+
+ return error;
+}
+
+static int pci_pm_freeze_noirq(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct pci_driver *drv = pci_dev->driver;
+ int error = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->freeze_noirq) {
+ error = drv->pm->freeze_noirq(dev);
+ suspend_report_result(drv->pm->freeze_noirq, error);
+ }
+ } else {
+ error = pci_legacy_suspend_late(dev, PMSG_FREEZE);
+ }
+
+ return error;
+}
+
+static int pci_pm_thaw(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int error = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->thaw)
+ error = drv->pm->thaw(dev);
+ } else {
+ pci_fixup_device(pci_fixup_resume, to_pci_dev(dev));
+ error = pci_legacy_resume(dev);
+ }
+
+ return error;
+}
+
+static int pci_pm_thaw_noirq(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct pci_driver *drv = pci_dev->driver;
+ int error = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->thaw_noirq)
+ error = drv->pm->thaw_noirq(dev);
+ } else {
+ pci_fixup_device(pci_fixup_resume_early, pci_dev);
+ error = pci_legacy_resume_early(dev);
+ }
+
+ return error;
+}
+
+static int pci_pm_poweroff(struct device *dev)
+{
+ struct device_driver *drv = dev->driver;
+ int error = 0;
+
+ pci_fixup_device(pci_fixup_suspend, to_pci_dev(dev));
+
+ if (drv && drv->pm) {
+ if (drv->pm->poweroff) {
+ error = drv->pm->poweroff(dev);
+ suspend_report_result(drv->pm->poweroff, error);
+ }
+ } else {
+ error = pci_legacy_suspend(dev, PMSG_HIBERNATE);
+ }
+
+ return error;
+}
+
+static int pci_pm_poweroff_noirq(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct pci_driver *drv = pci_dev->driver;
+ int error = 0;
+
+ if (drv && drv->pm) {
+ if (drv->pm->poweroff_noirq) {
+ error = drv->pm->poweroff_noirq(dev);
+ suspend_report_result(drv->pm->poweroff_noirq, error);
+ }
+ } else {
+ error = pci_legacy_suspend_late(dev, PMSG_HIBERNATE);
+ }
+
+ return error;
+}
+
+static int pci_pm_restore(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct device_driver *drv = dev->driver;
+ int error;
+
+ if (drv && drv->pm) {
+ error = drv->pm->restore ? drv->pm->restore(dev) :
+ pci_default_pm_resume(pci_dev);
+ } else {
+ error = pci_legacy_resume(dev);
+ }
+ pci_fixup_device(pci_fixup_resume, pci_dev);
+
+ return error;
+}
+
+static int pci_pm_restore_noirq(struct device *dev)
+{
+ struct pci_dev *pci_dev = to_pci_dev(dev);
+ struct pci_driver *drv = pci_dev->driver;
+ int error = 0;
+
+ pci_fixup_device(pci_fixup_resume, pci_dev);
+
+ if (drv && drv->pm) {
+ if (drv->pm->restore_noirq)
+ error = drv->pm->restore_noirq(dev);
+ } else {
+ error = pci_legacy_resume_early(dev);
+ }
+ pci_fixup_device(pci_fixup_resume_early, pci_dev);
+
+ return error;
+}
+
+#else /* !CONFIG_HIBERNATION */
+
+#define pci_pm_freeze NULL
+#define pci_pm_freeze_noirq NULL
+#define pci_pm_thaw NULL
+#define pci_pm_thaw_noirq NULL
+#define pci_pm_poweroff NULL
+#define pci_pm_poweroff_noirq NULL
+#define pci_pm_restore NULL
+#define pci_pm_restore_noirq NULL
+
+#endif /* !CONFIG_HIBERNATION */
+
+struct pm_ext_ops pci_pm_ops = {
+ .base = {
+ .prepare = pci_pm_prepare,
+ .complete = pci_pm_complete,
+ .suspend = pci_pm_suspend,
+ .resume = pci_pm_resume,
+ .freeze = pci_pm_freeze,
+ .thaw = pci_pm_thaw,
+ .poweroff = pci_pm_poweroff,
+ .restore = pci_pm_restore,
+ },
+ .suspend_noirq = pci_pm_suspend_noirq,
+ .resume_noirq = pci_pm_resume_noirq,
+ .freeze_noirq = pci_pm_freeze_noirq,
+ .thaw_noirq = pci_pm_thaw_noirq,
+ .poweroff_noirq = pci_pm_poweroff_noirq,
+ .restore_noirq = pci_pm_restore_noirq,
+};
+
+#define PCI_PM_OPS_PTR &pci_pm_ops
+
+#else /* !CONFIG_PM_SLEEP */
+
+#define PCI_PM_OPS_PTR NULL
+
+#endif /* !CONFIG_PM_SLEEP */
+
/**
* __pci_register_driver - register a new pci driver
* @drv: the driver structure to register
@@ -386,6 +690,9 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,
drv->driver.owner = owner;
drv->driver.mod_name = mod_name;
+ if (drv->pm)
+ drv->driver.pm = &drv->pm->base;
+
spin_lock_init(&drv->dynids.lock);
INIT_LIST_HEAD(&drv->dynids.list);
@@ -511,12 +818,9 @@ struct bus_type pci_bus_type = {
.uevent = pci_uevent,
.probe = pci_device_probe,
.remove = pci_device_remove,
- .suspend = pci_device_suspend,
- .suspend_late = pci_device_suspend_late,
- .resume_early = pci_device_resume_early,
- .resume = pci_device_resume,
.shutdown = pci_device_shutdown,
.dev_attrs = pci_dev_attrs,
+ .pm = PCI_PM_OPS_PTR,
};
static int __init pci_driver_init(void)
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index 271d41cc05ab..6f3c7446c329 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -489,13 +489,13 @@ pci_mmap_legacy_mem(struct kobject *kobj, struct bin_attribute *attr,
* @kobj: kobject for mapping
* @attr: struct bin_attribute for the file being mapped
* @vma: struct vm_area_struct passed into the mmap
+ * @write_combine: 1 for write_combine mapping
*
* Use the regular PCI mapping routines to map a PCI resource into userspace.
- * FIXME: write combining? maybe automatic for prefetchable regions?
*/
static int
pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
- struct vm_area_struct *vma)
+ struct vm_area_struct *vma, int write_combine)
{
struct pci_dev *pdev = to_pci_dev(container_of(kobj,
struct device, kobj));
@@ -518,7 +518,21 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
vma->vm_pgoff += start >> PAGE_SHIFT;
mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
- return pci_mmap_page_range(pdev, vma, mmap_type, 0);
+ return pci_mmap_page_range(pdev, vma, mmap_type, write_combine);
+}
+
+static int
+pci_mmap_resource_uc(struct kobject *kobj, struct bin_attribute *attr,
+ struct vm_area_struct *vma)
+{
+ return pci_mmap_resource(kobj, attr, vma, 0);
+}
+
+static int
+pci_mmap_resource_wc(struct kobject *kobj, struct bin_attribute *attr,
+ struct vm_area_struct *vma)
+{
+ return pci_mmap_resource(kobj, attr, vma, 1);
}
/**
@@ -541,9 +555,46 @@ pci_remove_resource_files(struct pci_dev *pdev)
sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
kfree(res_attr);
}
+
+ res_attr = pdev->res_attr_wc[i];
+ if (res_attr) {
+ sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
+ kfree(res_attr);
+ }
}
}
+static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine)
+{
+ /* allocate attribute structure, piggyback attribute name */
+ int name_len = write_combine ? 13 : 10;
+ struct bin_attribute *res_attr;
+ int retval;
+
+ res_attr = kzalloc(sizeof(*res_attr) + name_len, GFP_ATOMIC);
+ if (res_attr) {
+ char *res_attr_name = (char *)(res_attr + 1);
+
+ if (write_combine) {
+ pdev->res_attr_wc[num] = res_attr;
+ sprintf(res_attr_name, "resource%d_wc", num);
+ res_attr->mmap = pci_mmap_resource_wc;
+ } else {
+ pdev->res_attr[num] = res_attr;
+ sprintf(res_attr_name, "resource%d", num);
+ res_attr->mmap = pci_mmap_resource_uc;
+ }
+ res_attr->attr.name = res_attr_name;
+ res_attr->attr.mode = S_IRUSR | S_IWUSR;
+ res_attr->size = pci_resource_len(pdev, num);
+ res_attr->private = &pdev->resource[num];
+ retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
+ } else
+ retval = -ENOMEM;
+
+ return retval;
+}
+
/**
* pci_create_resource_files - create resource files in sysfs for @dev
* @dev: dev in question
@@ -557,31 +608,19 @@ static int pci_create_resource_files(struct pci_dev *pdev)
/* Expose the PCI resources from this device as files */
for (i = 0; i < PCI_ROM_RESOURCE; i++) {
- struct bin_attribute *res_attr;
/* skip empty resources */
if (!pci_resource_len(pdev, i))
continue;
- /* allocate attribute structure, piggyback attribute name */
- res_attr = kzalloc(sizeof(*res_attr) + 10, GFP_ATOMIC);
- if (res_attr) {
- char *res_attr_name = (char *)(res_attr + 1);
-
- pdev->res_attr[i] = res_attr;
- sprintf(res_attr_name, "resource%d", i);
- res_attr->attr.name = res_attr_name;
- res_attr->attr.mode = S_IRUSR | S_IWUSR;
- res_attr->size = pci_resource_len(pdev, i);
- res_attr->mmap = pci_mmap_resource;
- res_attr->private = &pdev->resource[i];
- retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
- if (retval) {
- pci_remove_resource_files(pdev);
- return retval;
- }
- } else {
- return -ENOMEM;
+ retval = pci_create_attr(pdev, i, 0);
+ /* for prefetchable resources, create a WC mappable file */
+ if (!retval && pdev->resource[i].flags & IORESOURCE_PREFETCH)
+ retval = pci_create_attr(pdev, i, 1);
+
+ if (retval) {
+ pci_remove_resource_files(pdev);
+ return retval;
}
}
return 0;
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index e4548ab2a93c..7869f8f75c9e 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1,6 +1,4 @@
/*
- * $Id: pci.c,v 1.91 1999/01/21 13:34:01 davem Exp $
- *
* PCI Bus Services, see include/linux/pci.h for further explanation.
*
* Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter,
@@ -508,7 +506,7 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
return 0;
}
-pci_power_t (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_t state);
+pci_power_t (*platform_pci_choose_state)(struct pci_dev *dev);
/**
* pci_choose_state - Choose the power state of a PCI device
@@ -528,7 +526,7 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
return PCI_D0;
if (platform_pci_choose_state) {
- ret = platform_pci_choose_state(dev, state);
+ ret = platform_pci_choose_state(dev);
if (ret != PCI_POWER_ERROR)
return ret;
}
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 0a497c1b4227..e0eff35825a6 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -6,8 +6,7 @@ extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
extern void pci_cleanup_rom(struct pci_dev *dev);
/* Firmware callbacks */
-extern pci_power_t (*platform_pci_choose_state)(struct pci_dev *dev,
- pm_message_t state);
+extern pci_power_t (*platform_pci_choose_state)(struct pci_dev *dev);
extern int (*platform_pci_set_power_state)(struct pci_dev *dev,
pci_power_t state);
@@ -106,3 +105,16 @@ pci_match_one_device(const struct pci_device_id *id, const struct pci_dev *dev)
}
struct pci_dev *pci_find_upstream_pcie_bridge(struct pci_dev *pdev);
+
+/* PCI slot sysfs helper code */
+#define to_pci_slot(s) container_of(s, struct pci_slot, kobj)
+
+extern struct kset *pci_slots_kset;
+
+struct pci_slot_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct pci_slot *, char *);
+ ssize_t (*store)(struct pci_slot *, const char *, size_t);
+};
+#define to_pci_slot_attr(s) container_of(s, struct pci_slot_attribute, attr)
+
diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c
index 07c3bdb6edc2..b7a3aa602d40 100644
--- a/drivers/pci/pcie/aer/aerdrv.c
+++ b/drivers/pci/pcie/aer/aerdrv.c
@@ -26,6 +26,7 @@
#include <linux/pcieport_if.h>
#include "aerdrv.h"
+#include "../../pci.h"
/*
* Version Information
diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c
index 3f0976868eda..359fe5568df1 100644
--- a/drivers/pci/pcie/portdrv_bus.c
+++ b/drivers/pci/pcie/portdrv_bus.c
@@ -13,6 +13,7 @@
#include <linux/pm.h>
#include <linux/pcieport_if.h>
+#include "portdrv.h"
static int pcie_port_bus_match(struct device *dev, struct device_driver *drv);
static int pcie_port_bus_suspend(struct device *dev, pm_message_t state);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 3706ce7972dd..4562827b7e22 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -414,6 +414,7 @@ static struct pci_bus * pci_alloc_bus(void)
INIT_LIST_HEAD(&b->node);
INIT_LIST_HEAD(&b->children);
INIT_LIST_HEAD(&b->devices);
+ INIT_LIST_HEAD(&b->slots);
}
return b;
}
@@ -857,6 +858,49 @@ int pci_cfg_space_size_ext(struct pci_dev *dev)
return PCI_CFG_SPACE_SIZE;
}
+/**
+ * pci_disable_pme - Disable the PME function of PCI device
+ * @dev: PCI device affected
+ * -EINVAL is returned if PCI device doesn't support PME.
+ * Zero is returned if the PME is supported and can be disabled.
+ */
+static int pci_disable_pme(struct pci_dev *dev)
+{
+ int pm;
+ u16 value;
+
+ /* find PCI PM capability in list */
+ pm = pci_find_capability(dev, PCI_CAP_ID_PM);
+
+ /* If device doesn't support PM Capabilities, it means that PME is
+ * not supported.
+ */
+ if (!pm)
+ return -EINVAL;
+ /* Check device's ability to generate PME# */
+ pci_read_config_word(dev, pm + PCI_PM_PMC, &value);
+
+ value &= PCI_PM_CAP_PME_MASK;
+ /* Check if it can generate PME# */
+ if (!value) {
+ /*
+ * If it is zero, it means that PME is still unsupported
+ * although there exists the PM capability.
+ */
+ return -EINVAL;
+ }
+
+ pci_read_config_word(dev, pm + PCI_PM_CTRL, &value);
+
+ /* Clear PME_Status by writing 1 to it */
+ value |= PCI_PM_CTRL_PME_STATUS ;
+ /* Disable PME enable bit */
+ value &= ~PCI_PM_CTRL_PME_ENABLE;
+ pci_write_config_word(dev, pm + PCI_PM_CTRL, value);
+
+ return 0;
+}
+
int pci_cfg_space_size(struct pci_dev *dev)
{
int pos;
@@ -964,6 +1008,7 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
}
pci_vpd_pci22_init(dev);
+ pci_disable_pme(dev);
return dev;
}
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index 963a97642ae9..4400dffbd93a 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -1,6 +1,4 @@
/*
- * $Id: proc.c,v 1.13 1998/05/12 07:36:07 mj Exp $
- *
* Procfs interface for the PCI bus.
*
* Copyright (c) 1997--1999 Martin Mares <mj@ucw.cz>
@@ -482,5 +480,5 @@ static int __init pci_proc_init(void)
return 0;
}
-__initcall(pci_proc_init);
+device_initcall(pci_proc_init);
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index dabb563f51d9..92b52ebd0eb6 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -556,7 +556,7 @@ static void quirk_via_ioapic(struct pci_dev *dev)
pci_write_config_byte (dev, 0x58, tmp);
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, quirk_via_ioapic);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, quirk_via_ioapic);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686, quirk_via_ioapic);
/*
* VIA 8237: Some BIOSs don't set the 'Bypass APIC De-Assert Message' Bit.
@@ -576,7 +576,7 @@ static void quirk_via_vt8237_bypass_apic_deassert(struct pci_dev *dev)
}
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, quirk_via_vt8237_bypass_apic_deassert);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, quirk_via_vt8237_bypass_apic_deassert);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, quirk_via_vt8237_bypass_apic_deassert);
/*
* The AMD io apic can hang the box when an apic irq is masked.
@@ -622,7 +622,7 @@ static void quirk_amd_8131_ioapic(struct pci_dev *dev)
}
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic);
#endif /* CONFIG_X86_IO_APIC */
/*
@@ -774,7 +774,7 @@ static void quirk_cardbus_legacy(struct pci_dev *dev)
pci_write_config_dword(dev, PCI_CB_LEGACY_MODE_BASE, 0);
}
DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy);
-DECLARE_PCI_FIXUP_RESUME(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy);
/*
* Following the PCI ordering rules is optional on the AMD762. I'm not
@@ -797,7 +797,7 @@ static void quirk_amd_ordering(struct pci_dev *dev)
}
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, quirk_amd_ordering);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, quirk_amd_ordering);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_FE_GATE_700C, quirk_amd_ordering);
/*
* DreamWorks provided workaround for Dunord I-3000 problem
@@ -865,7 +865,7 @@ static void quirk_disable_pxb(struct pci_dev *pdev)
}
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, quirk_disable_pxb);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, quirk_disable_pxb);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, quirk_disable_pxb);
static void __devinit quirk_amd_ide_mode(struct pci_dev *pdev)
{
@@ -885,9 +885,9 @@ static void __devinit quirk_amd_ide_mode(struct pci_dev *pdev)
}
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk_amd_ide_mode);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk_amd_ide_mode);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk_amd_ide_mode);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode);
/*
* Serverworks CSB5 IDE does not fully support native mode
@@ -1054,6 +1054,20 @@ static void __init asus_hides_smbus_hostbridge(struct pci_dev *dev)
* its on-board VGA controller */
asus_hides_smbus = 1;
}
+ else if (dev->device == PCI_DEVICE_ID_INTEL_82845G_IG)
+ switch(dev->subsystem_device) {
+ case 0x00b8: /* Compaq Evo D510 CMT */
+ case 0x00b9: /* Compaq Evo D510 SFF */
+ asus_hides_smbus = 1;
+ }
+ else if (dev->device == PCI_DEVICE_ID_INTEL_82815_CGC)
+ switch (dev->subsystem_device) {
+ case 0x001A: /* Compaq Deskpro EN SSF P667 815E */
+ /* Motherboard doesn't have host bridge
+ * subvendor/subdevice IDs, therefore checking
+ * its on-board VGA controller */
+ asus_hides_smbus = 1;
+ }
}
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82845_HB, asus_hides_smbus_hostbridge);
@@ -1068,6 +1082,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82855GM_HB, as
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82915GM_HB, asus_hides_smbus_hostbridge);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82810_IG3, asus_hides_smbus_hostbridge);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82845G_IG, asus_hides_smbus_hostbridge);
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82815_CGC, asus_hides_smbus_hostbridge);
static void asus_hides_smbus_lpc(struct pci_dev *dev)
{
@@ -1093,31 +1109,61 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, asu
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, asus_hides_smbus_lpc);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, asus_hides_smbus_lpc);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, asus_hides_smbus_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, asus_hides_smbus_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, asus_hides_smbus_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, asus_hides_smbus_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, asus_hides_smbus_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, asus_hides_smbus_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, asus_hides_smbus_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, asus_hides_smbus_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, asus_hides_smbus_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, asus_hides_smbus_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, asus_hides_smbus_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, asus_hides_smbus_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, asus_hides_smbus_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, asus_hides_smbus_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, asus_hides_smbus_lpc);
-static void asus_hides_smbus_lpc_ich6(struct pci_dev *dev)
+/* It appears we just have one such device. If not, we have a warning */
+static void __iomem *asus_rcba_base;
+static void asus_hides_smbus_lpc_ich6_suspend(struct pci_dev *dev)
{
- u32 val, rcba;
- void __iomem *base;
+ u32 rcba;
if (likely(!asus_hides_smbus))
return;
+ WARN_ON(asus_rcba_base);
+
pci_read_config_dword(dev, 0xF0, &rcba);
- base = ioremap_nocache(rcba & 0xFFFFC000, 0x4000); /* use bits 31:14, 16 kB aligned */
- if (base == NULL) return;
- val=readl(base + 0x3418); /* read the Function Disable register, dword mode only */
- writel(val & 0xFFFFFFF7, base + 0x3418); /* enable the SMBus device */
- iounmap(base);
+ /* use bits 31:14, 16 kB aligned */
+ asus_rcba_base = ioremap_nocache(rcba & 0xFFFFC000, 0x4000);
+ if (asus_rcba_base == NULL)
+ return;
+}
+
+static void asus_hides_smbus_lpc_ich6_resume_early(struct pci_dev *dev)
+{
+ u32 val;
+
+ if (likely(!asus_hides_smbus || !asus_rcba_base))
+ return;
+ /* read the Function Disable register, dword mode only */
+ val = readl(asus_rcba_base + 0x3418);
+ writel(val & 0xFFFFFFF7, asus_rcba_base + 0x3418); /* enable the SMBus device */
+}
+
+static void asus_hides_smbus_lpc_ich6_resume(struct pci_dev *dev)
+{
+ if (likely(!asus_hides_smbus || !asus_rcba_base))
+ return;
+ iounmap(asus_rcba_base);
+ asus_rcba_base = NULL;
dev_info(&dev->dev, "Enabled ICH6/i801 SMBus device\n");
}
+
+static void asus_hides_smbus_lpc_ich6(struct pci_dev *dev)
+{
+ asus_hides_smbus_lpc_ich6_suspend(dev);
+ asus_hides_smbus_lpc_ich6_resume_early(dev);
+ asus_hides_smbus_lpc_ich6_resume(dev);
+}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6);
+DECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6_suspend);
+DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6_resume);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, asus_hides_smbus_lpc_ich6_resume_early);
/*
* SiS 96x south bridge: BIOS typically hides SMBus device...
@@ -1135,10 +1181,10 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_961, quirk_sis_96x_
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_962, quirk_sis_96x_smbus);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_963, quirk_sis_96x_smbus);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC, quirk_sis_96x_smbus);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_961, quirk_sis_96x_smbus);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_962, quirk_sis_96x_smbus);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_963, quirk_sis_96x_smbus);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC, quirk_sis_96x_smbus);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_961, quirk_sis_96x_smbus);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_962, quirk_sis_96x_smbus);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_963, quirk_sis_96x_smbus);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_LPC, quirk_sis_96x_smbus);
/*
* ... This is further complicated by the fact that some SiS96x south
@@ -1172,7 +1218,7 @@ static void quirk_sis_503(struct pci_dev *dev)
quirk_sis_96x_smbus(dev);
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, quirk_sis_503);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, quirk_sis_503);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, quirk_sis_503);
/*
@@ -1205,7 +1251,7 @@ static void asus_hides_ac97_lpc(struct pci_dev *dev)
}
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, asus_hides_ac97_lpc);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, asus_hides_ac97_lpc);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, asus_hides_ac97_lpc);
#if defined(CONFIG_ATA) || defined(CONFIG_ATA_MODULE)
@@ -1270,12 +1316,12 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, qui
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365, quirk_jmicron_ata);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366, quirk_jmicron_ata);
DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, quirk_jmicron_ata);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB360, quirk_jmicron_ata);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB361, quirk_jmicron_ata);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, quirk_jmicron_ata);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365, quirk_jmicron_ata);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366, quirk_jmicron_ata);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, quirk_jmicron_ata);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB360, quirk_jmicron_ata);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB361, quirk_jmicron_ata);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, quirk_jmicron_ata);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365, quirk_jmicron_ata);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366, quirk_jmicron_ata);
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, quirk_jmicron_ata);
#endif
@@ -1521,6 +1567,10 @@ extern struct pci_fixup __start_pci_fixups_enable[];
extern struct pci_fixup __end_pci_fixups_enable[];
extern struct pci_fixup __start_pci_fixups_resume[];
extern struct pci_fixup __end_pci_fixups_resume[];
+extern struct pci_fixup __start_pci_fixups_resume_early[];
+extern struct pci_fixup __end_pci_fixups_resume_early[];
+extern struct pci_fixup __start_pci_fixups_suspend[];
+extern struct pci_fixup __end_pci_fixups_suspend[];
void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev)
@@ -1553,6 +1603,16 @@ void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev)
end = __end_pci_fixups_resume;
break;
+ case pci_fixup_resume_early:
+ start = __start_pci_fixups_resume_early;
+ end = __end_pci_fixups_resume_early;
+ break;
+
+ case pci_fixup_suspend:
+ start = __start_pci_fixups_suspend;
+ end = __end_pci_fixups_suspend;
+ break;
+
default:
/* stupid compiler warning, you would think with an enum... */
return;
@@ -1629,7 +1689,7 @@ static void quirk_nvidia_ck804_pcie_aer_ext_cap(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE,
quirk_nvidia_ck804_pcie_aer_ext_cap);
-DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE,
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE,
quirk_nvidia_ck804_pcie_aer_ext_cap);
static void __devinit quirk_via_cx700_pci_parking_caching(struct pci_dev *dev)
diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c
new file mode 100644
index 000000000000..7e5b85cbd948
--- /dev/null
+++ b/drivers/pci/slot.c
@@ -0,0 +1,233 @@
+/*
+ * drivers/pci/slot.c
+ * Copyright (C) 2006 Matthew Wilcox <matthew@wil.cx>
+ * Copyright (C) 2006-2008 Hewlett-Packard Development Company, L.P.
+ * Alex Chiang <achiang@hp.com>
+ */
+
+#include <linux/kobject.h>
+#include <linux/pci.h>
+#include <linux/err.h>
+#include "pci.h"
+
+struct kset *pci_slots_kset;
+EXPORT_SYMBOL_GPL(pci_slots_kset);
+
+static ssize_t pci_slot_attr_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct pci_slot *slot = to_pci_slot(kobj);
+ struct pci_slot_attribute *attribute = to_pci_slot_attr(attr);
+ return attribute->show ? attribute->show(slot, buf) : -EIO;
+}
+
+static ssize_t pci_slot_attr_store(struct kobject *kobj,
+ struct attribute *attr, const char *buf, size_t len)
+{
+ struct pci_slot *slot = to_pci_slot(kobj);
+ struct pci_slot_attribute *attribute = to_pci_slot_attr(attr);
+ return attribute->store ? attribute->store(slot, buf, len) : -EIO;
+}
+
+static struct sysfs_ops pci_slot_sysfs_ops = {
+ .show = pci_slot_attr_show,
+ .store = pci_slot_attr_store,
+};
+
+static ssize_t address_read_file(struct pci_slot *slot, char *buf)
+{
+ if (slot->number == 0xff)
+ return sprintf(buf, "%04x:%02x\n",
+ pci_domain_nr(slot->bus),
+ slot->bus->number);
+ else
+ return sprintf(buf, "%04x:%02x:%02x\n",
+ pci_domain_nr(slot->bus),
+ slot->bus->number,
+ slot->number);
+}
+
+static void pci_slot_release(struct kobject *kobj)
+{
+ struct pci_slot *slot = to_pci_slot(kobj);
+
+ pr_debug("%s: releasing pci_slot on %x:%d\n", __func__,
+ slot->bus->number, slot->number);
+
+ list_del(&slot->list);
+
+ kfree(slot);
+}
+
+static struct pci_slot_attribute pci_slot_attr_address =
+ __ATTR(address, (S_IFREG | S_IRUGO), address_read_file, NULL);
+
+static struct attribute *pci_slot_default_attrs[] = {
+ &pci_slot_attr_address.attr,
+ NULL,
+};
+
+static struct kobj_type pci_slot_ktype = {
+ .sysfs_ops = &pci_slot_sysfs_ops,
+ .release = &pci_slot_release,
+ .default_attrs = pci_slot_default_attrs,
+};
+
+/**
+ * pci_create_slot - create or increment refcount for physical PCI slot
+ * @parent: struct pci_bus of parent bridge
+ * @slot_nr: PCI_SLOT(pci_dev->devfn) or -1 for placeholder
+ * @name: user visible string presented in /sys/bus/pci/slots/<name>
+ *
+ * PCI slots have first class attributes such as address, speed, width,
+ * and a &struct pci_slot is used to manage them. This interface will
+ * either return a new &struct pci_slot to the caller, or if the pci_slot
+ * already exists, its refcount will be incremented.
+ *
+ * Slots are uniquely identified by a @pci_bus, @slot_nr, @name tuple.
+ *
+ * Placeholder slots:
+ * In most cases, @pci_bus, @slot_nr will be sufficient to uniquely identify
+ * a slot. There is one notable exception - pSeries (rpaphp), where the
+ * @slot_nr cannot be determined until a device is actually inserted into
+ * the slot. In this scenario, the caller may pass -1 for @slot_nr.
+ *
+ * The following semantics are imposed when the caller passes @slot_nr ==
+ * -1. First, the check for existing %struct pci_slot is skipped, as the
+ * caller may know about several unpopulated slots on a given %struct
+ * pci_bus, and each slot would have a @slot_nr of -1. Uniqueness for
+ * these slots is then determined by the @name parameter. We expect
+ * kobject_init_and_add() to warn us if the caller attempts to create
+ * multiple slots with the same name. The other change in semantics is
+ * user-visible, which is the 'address' parameter presented in sysfs will
+ * consist solely of a dddd:bb tuple, where dddd is the PCI domain of the
+ * %struct pci_bus and bb is the bus number. In other words, the devfn of
+ * the 'placeholder' slot will not be displayed.
+ */
+
+struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr,
+ const char *name)
+{
+ struct pci_slot *slot;
+ int err;
+
+ down_write(&pci_bus_sem);
+
+ if (slot_nr == -1)
+ goto placeholder;
+
+ /* If we've already created this slot, bump refcount and return. */
+ list_for_each_entry(slot, &parent->slots, list) {
+ if (slot->number == slot_nr) {
+ kobject_get(&slot->kobj);
+ pr_debug("%s: inc refcount to %d on %04x:%02x:%02x\n",
+ __func__,
+ atomic_read(&slot->kobj.kref.refcount),
+ pci_domain_nr(parent), parent->number,
+ slot_nr);
+ goto out;
+ }
+ }
+
+placeholder:
+ slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+ if (!slot) {
+ slot = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+
+ slot->bus = parent;
+ slot->number = slot_nr;
+
+ slot->kobj.kset = pci_slots_kset;
+ err = kobject_init_and_add(&slot->kobj, &pci_slot_ktype, NULL,
+ "%s", name);
+ if (err) {
+ printk(KERN_ERR "Unable to register kobject %s\n", name);
+ goto err;
+ }
+
+ INIT_LIST_HEAD(&slot->list);
+ list_add(&slot->list, &parent->slots);
+
+ /* Don't care if debug printk has a -1 for slot_nr */
+ pr_debug("%s: created pci_slot on %04x:%02x:%02x\n",
+ __func__, pci_domain_nr(parent), parent->number, slot_nr);
+
+ out:
+ up_write(&pci_bus_sem);
+ return slot;
+ err:
+ kfree(slot);
+ slot = ERR_PTR(err);
+ goto out;
+}
+EXPORT_SYMBOL_GPL(pci_create_slot);
+
+/**
+ * pci_update_slot_number - update %struct pci_slot -> number
+ * @slot - %struct pci_slot to update
+ * @slot_nr - new number for slot
+ *
+ * The primary purpose of this interface is to allow callers who earlier
+ * created a placeholder slot in pci_create_slot() by passing a -1 as
+ * slot_nr, to update their %struct pci_slot with the correct @slot_nr.
+ */
+
+void pci_update_slot_number(struct pci_slot *slot, int slot_nr)
+{
+ int name_count = 0;
+ struct pci_slot *tmp;
+
+ down_write(&pci_bus_sem);
+
+ list_for_each_entry(tmp, &slot->bus->slots, list) {
+ WARN_ON(tmp->number == slot_nr);
+ if (!strcmp(kobject_name(&tmp->kobj), kobject_name(&slot->kobj)))
+ name_count++;
+ }
+
+ if (name_count > 1)
+ printk(KERN_WARNING "pci_update_slot_number found %d slots with the same name: %s\n", name_count, kobject_name(&slot->kobj));
+
+ slot->number = slot_nr;
+ up_write(&pci_bus_sem);
+}
+EXPORT_SYMBOL_GPL(pci_update_slot_number);
+
+/**
+ * pci_destroy_slot - decrement refcount for physical PCI slot
+ * @slot: struct pci_slot to decrement
+ *
+ * %struct pci_slot is refcounted, so destroying them is really easy; we
+ * just call kobject_put on its kobj and let our release methods do the
+ * rest.
+ */
+
+void pci_destroy_slot(struct pci_slot *slot)
+{
+ pr_debug("%s: dec refcount to %d on %04x:%02x:%02x\n", __func__,
+ atomic_read(&slot->kobj.kref.refcount) - 1,
+ pci_domain_nr(slot->bus), slot->bus->number, slot->number);
+
+ down_write(&pci_bus_sem);
+ kobject_put(&slot->kobj);
+ up_write(&pci_bus_sem);
+}
+EXPORT_SYMBOL_GPL(pci_destroy_slot);
+
+static int pci_slot_init(void)
+{
+ struct kset *pci_bus_kset;
+
+ pci_bus_kset = bus_get_kset(&pci_bus_type);
+ pci_slots_kset = kset_create_and_add("slots", NULL,
+ &pci_bus_kset->kobj);
+ if (!pci_slots_kset) {
+ printk(KERN_ERR "PCI: Slot initialization failure\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+subsys_initcall(pci_slot_init);
diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c
index 50902773beaf..c1b9ea34977b 100644
--- a/drivers/pnp/pnpacpi/core.c
+++ b/drivers/pnp/pnpacpi/core.c
@@ -117,9 +117,7 @@ static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state)
{
int power_state;
- power_state = acpi_pm_device_sleep_state(&dev->dev,
- device_may_wakeup(&dev->dev),
- NULL);
+ power_state = acpi_pm_device_sleep_state(&dev->dev, NULL);
if (power_state < 0)
power_state = (state.event == PM_EVENT_ON) ?
ACPI_STATE_D0 : ACPI_STATE_D3;
diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c
index 0201c8adfda7..46c791adb894 100644
--- a/drivers/pnp/pnpacpi/rsparser.c
+++ b/drivers/pnp/pnpacpi/rsparser.c
@@ -50,15 +50,17 @@ static int irq_flags(int triggering, int polarity, int shareable)
flags = IORESOURCE_IRQ_HIGHEDGE;
}
- if (shareable)
+ if (shareable == ACPI_SHARED)
flags |= IORESOURCE_IRQ_SHAREABLE;
return flags;
}
-static void decode_irq_flags(int flag, int *triggering, int *polarity)
+static void decode_irq_flags(struct pnp_dev *dev, int flags, int *triggering,
+ int *polarity, int *shareable)
{
- switch (flag) {
+ switch (flags & (IORESOURCE_IRQ_LOWLEVEL | IORESOURCE_IRQ_HIGHLEVEL |
+ IORESOURCE_IRQ_LOWEDGE | IORESOURCE_IRQ_HIGHEDGE)) {
case IORESOURCE_IRQ_LOWLEVEL:
*triggering = ACPI_LEVEL_SENSITIVE;
*polarity = ACPI_ACTIVE_LOW;
@@ -75,7 +77,18 @@ static void decode_irq_flags(int flag, int *triggering, int *polarity)
*triggering = ACPI_EDGE_SENSITIVE;
*polarity = ACPI_ACTIVE_HIGH;
break;
+ default:
+ dev_err(&dev->dev, "can't encode invalid IRQ mode %#x\n",
+ flags);
+ *triggering = ACPI_EDGE_SENSITIVE;
+ *polarity = ACPI_ACTIVE_HIGH;
+ break;
}
+
+ if (flags & IORESOURCE_IRQ_SHAREABLE)
+ *shareable = ACPI_SHARED;
+ else
+ *shareable = ACPI_EXCLUSIVE;
}
static void pnpacpi_parse_allocated_irqresource(struct pnp_dev *dev,
@@ -742,6 +755,9 @@ static acpi_status pnpacpi_type_resources(struct acpi_resource *res, void *data)
if (pnpacpi_supported_resource(res)) {
(*resource)->type = res->type;
(*resource)->length = sizeof(struct acpi_resource);
+ if (res->type == ACPI_RESOURCE_TYPE_IRQ)
+ (*resource)->data.irq.descriptor_length =
+ res->data.irq.descriptor_length;
(*resource)++;
}
@@ -788,22 +804,21 @@ static void pnpacpi_encode_irq(struct pnp_dev *dev,
struct resource *p)
{
struct acpi_resource_irq *irq = &resource->data.irq;
- int triggering, polarity;
+ int triggering, polarity, shareable;
- decode_irq_flags(p->flags & IORESOURCE_BITS, &triggering, &polarity);
+ decode_irq_flags(dev, p->flags, &triggering, &polarity, &shareable);
irq->triggering = triggering;
irq->polarity = polarity;
- if (triggering == ACPI_EDGE_SENSITIVE)
- irq->sharable = ACPI_EXCLUSIVE;
- else
- irq->sharable = ACPI_SHARED;
+ irq->sharable = shareable;
irq->interrupt_count = 1;
irq->interrupts[0] = p->start;
- dev_dbg(&dev->dev, " encode irq %d %s %s %s\n", (int) p->start,
+ dev_dbg(&dev->dev, " encode irq %d %s %s %s (%d-byte descriptor)\n",
+ (int) p->start,
triggering == ACPI_LEVEL_SENSITIVE ? "level" : "edge",
polarity == ACPI_ACTIVE_LOW ? "low" : "high",
- irq->sharable == ACPI_SHARED ? "shared" : "exclusive");
+ irq->sharable == ACPI_SHARED ? "shared" : "exclusive",
+ irq->descriptor_length);
}
static void pnpacpi_encode_ext_irq(struct pnp_dev *dev,
@@ -811,16 +826,13 @@ static void pnpacpi_encode_ext_irq(struct pnp_dev *dev,
struct resource *p)
{
struct acpi_resource_extended_irq *extended_irq = &resource->data.extended_irq;
- int triggering, polarity;
+ int triggering, polarity, shareable;
- decode_irq_flags(p->flags & IORESOURCE_BITS, &triggering, &polarity);
+ decode_irq_flags(dev, p->flags, &triggering, &polarity, &shareable);
extended_irq->producer_consumer = ACPI_CONSUMER;
extended_irq->triggering = triggering;
extended_irq->polarity = polarity;
- if (triggering == ACPI_EDGE_SENSITIVE)
- extended_irq->sharable = ACPI_EXCLUSIVE;
- else
- extended_irq->sharable = ACPI_SHARED;
+ extended_irq->sharable = shareable;
extended_irq->interrupt_count = 1;
extended_irq->interrupts[0] = p->start;
diff --git a/drivers/power/apm_power.c b/drivers/power/apm_power.c
index a4892275659d..936bae560fa1 100644
--- a/drivers/power/apm_power.c
+++ b/drivers/power/apm_power.c
@@ -78,7 +78,7 @@ static void find_main_battery(void)
main_battery = NULL;
bp.main = main_battery;
- error = class_for_each_device(power_supply_class, &bp,
+ error = class_for_each_device(power_supply_class, NULL, &bp,
__find_main_battery);
if (error) {
main_battery = bp.main;
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
index af1633eb3b70..cb1ccb472921 100644
--- a/drivers/power/power_supply_core.c
+++ b/drivers/power/power_supply_core.c
@@ -41,7 +41,7 @@ static void power_supply_changed_work(struct work_struct *work)
dev_dbg(psy->dev, "%s\n", __func__);
- class_for_each_device(power_supply_class, psy,
+ class_for_each_device(power_supply_class, NULL, psy,
__power_supply_changed_work);
power_supply_update_leds(psy);
@@ -79,7 +79,7 @@ int power_supply_am_i_supplied(struct power_supply *psy)
{
int error;
- error = class_for_each_device(power_supply_class, psy,
+ error = class_for_each_device(power_supply_class, NULL, psy,
__power_supply_am_i_supplied);
dev_dbg(psy->dev, "%s %d\n", __func__, error);
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 7e3ad4f3b343..75676b432d7b 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -265,7 +265,7 @@ struct rtc_device *rtc_class_open(char *name)
struct device *dev;
struct rtc_device *rtc = NULL;
- dev = class_find_device(rtc_class, name, __rtc_match);
+ dev = class_find_device(rtc_class, NULL, name, __rtc_match);
if (dev)
rtc = to_rtc_device(dev);
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 1a4025683362..c1d180cbb0c4 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -215,7 +215,7 @@ static int dasd_state_known_to_basic(struct dasd_device *device)
return rc;
}
/* register 'device' debug area, used for all DBF_DEV_XXX calls */
- device->debug_area = debug_register(device->cdev->dev.bus_id, 1, 1,
+ device->debug_area = debug_register(dev_name(&device->cdev->dev), 1, 1,
8 * sizeof(long));
debug_register_view(device->debug_area, &debug_sprintf_view);
debug_set_level(device->debug_area, DBF_WARNING);
@@ -933,7 +933,7 @@ static void dasd_handle_killed_request(struct ccw_device *cdev,
MESSAGE(KERN_DEBUG,
"invalid status in handle_killed_request: "
"bus_id %s, status %02x",
- cdev->dev.bus_id, cqr->status);
+ dev_name(&cdev->dev), cqr->status);
return;
}
@@ -942,7 +942,7 @@ static void dasd_handle_killed_request(struct ccw_device *cdev,
device != dasd_device_from_cdev_locked(cdev) ||
strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s",
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
return;
}
@@ -982,11 +982,11 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
break;
case -ETIMEDOUT:
printk(KERN_WARNING"%s(%s): request timed out\n",
- __func__, cdev->dev.bus_id);
+ __func__, dev_name(&cdev->dev));
break;
default:
printk(KERN_WARNING"%s(%s): unknown error %ld\n",
- __func__, cdev->dev.bus_id, PTR_ERR(irb));
+ __func__, dev_name(&cdev->dev), PTR_ERR(irb));
}
dasd_handle_killed_request(cdev, intparm);
return;
@@ -995,14 +995,14 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
now = get_clock();
DBF_EVENT(DBF_ERR, "Interrupt: bus_id %s CS/DS %04x ip %08x",
- cdev->dev.bus_id, ((irb->scsw.cstat<<8)|irb->scsw.dstat),
- (unsigned int) intparm);
+ dev_name(&cdev->dev), ((irb->scsw.cmd.cstat << 8) |
+ irb->scsw.cmd.dstat), (unsigned int) intparm);
/* check for unsolicited interrupts */
cqr = (struct dasd_ccw_req *) intparm;
- if (!cqr || ((irb->scsw.cc == 1) &&
- (irb->scsw.fctl & SCSW_FCTL_START_FUNC) &&
- (irb->scsw.stctl & SCSW_STCTL_STATUS_PEND)) ) {
+ if (!cqr || ((irb->scsw.cmd.cc == 1) &&
+ (irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC) &&
+ (irb->scsw.cmd.stctl & SCSW_STCTL_STATUS_PEND))) {
if (cqr && cqr->status == DASD_CQR_IN_IO)
cqr->status = DASD_CQR_QUEUED;
device = dasd_device_from_cdev_locked(cdev);
@@ -1019,13 +1019,13 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
if (!device ||
strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s",
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
return;
}
/* Check for clear pending */
if (cqr->status == DASD_CQR_CLEAR_PENDING &&
- irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) {
+ irb->scsw.cmd.fctl & SCSW_FCTL_CLEAR_FUNC) {
cqr->status = DASD_CQR_CLEARED;
dasd_device_clear_timer(device);
wake_up(&dasd_flush_wq);
@@ -1037,15 +1037,15 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
if (cqr->status != DASD_CQR_IN_IO) {
MESSAGE(KERN_DEBUG,
"invalid status: bus_id %s, status %02x",
- cdev->dev.bus_id, cqr->status);
+ dev_name(&cdev->dev), cqr->status);
return;
}
DBF_DEV_EVENT(DBF_DEBUG, device, "Int: CS/DS 0x%04x for cqr %p",
- ((irb->scsw.cstat << 8) | irb->scsw.dstat), cqr);
+ ((irb->scsw.cmd.cstat << 8) | irb->scsw.cmd.dstat), cqr);
next = NULL;
expires = 0;
- if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) &&
- irb->scsw.cstat == 0 && !irb->esw.esw0.erw.cons) {
+ if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) &&
+ irb->scsw.cmd.cstat == 0 && !irb->esw.esw0.erw.cons) {
/* request was completed successfully */
cqr->status = DASD_CQR_SUCCESS;
cqr->stopclk = now;
@@ -2134,14 +2134,14 @@ int dasd_generic_probe(struct ccw_device *cdev,
if (ret) {
printk(KERN_WARNING
"dasd_generic_probe: could not set ccw-device options "
- "for %s\n", cdev->dev.bus_id);
+ "for %s\n", dev_name(&cdev->dev));
return ret;
}
ret = dasd_add_sysfs_files(cdev);
if (ret) {
printk(KERN_WARNING
"dasd_generic_probe: could not add sysfs entries "
- "for %s\n", cdev->dev.bus_id);
+ "for %s\n", dev_name(&cdev->dev));
return ret;
}
cdev->handler = &dasd_int_handler;
@@ -2152,13 +2152,13 @@ int dasd_generic_probe(struct ccw_device *cdev,
* initial probe.
*/
if ((dasd_get_feature(cdev, DASD_FEATURE_INITIAL_ONLINE) > 0 ) ||
- (dasd_autodetect && dasd_busid_known(cdev->dev.bus_id) != 0))
+ (dasd_autodetect && dasd_busid_known(dev_name(&cdev->dev)) != 0))
ret = ccw_device_set_online(cdev);
if (ret)
printk(KERN_WARNING
"dasd_generic_probe: could not initially "
"online ccw-device %s; return code: %d\n",
- cdev->dev.bus_id, ret);
+ dev_name(&cdev->dev), ret);
return 0;
}
@@ -2224,7 +2224,7 @@ int dasd_generic_set_online(struct ccw_device *cdev,
printk (KERN_WARNING
"dasd_generic couldn't online device %s "
"- discipline DIAG not available\n",
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
dasd_delete_device(device);
return -ENODEV;
}
@@ -2248,7 +2248,7 @@ int dasd_generic_set_online(struct ccw_device *cdev,
printk (KERN_WARNING
"dasd_generic couldn't online device %s "
"with discipline %s rc=%i\n",
- cdev->dev.bus_id, discipline->name, rc);
+ dev_name(&cdev->dev), discipline->name, rc);
module_put(discipline->owner);
module_put(base_discipline->owner);
dasd_delete_device(device);
@@ -2259,7 +2259,7 @@ int dasd_generic_set_online(struct ccw_device *cdev,
if (device->state <= DASD_STATE_KNOWN) {
printk (KERN_WARNING
"dasd_generic discipline not found for %s\n",
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
rc = -ENODEV;
dasd_set_target_state(device, DASD_STATE_NEW);
if (device->block)
@@ -2267,7 +2267,7 @@ int dasd_generic_set_online(struct ccw_device *cdev,
dasd_delete_device(device);
} else
pr_debug("dasd_generic device %s found\n",
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
/* FIXME: we have to wait for the root device but we don't want
* to wait for each single device but for all at once. */
diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c
index e6700df52df4..b8f9c00633f3 100644
--- a/drivers/s390/block/dasd_3990_erp.c
+++ b/drivers/s390/block/dasd_3990_erp.c
@@ -1397,7 +1397,7 @@ static struct dasd_ccw_req *dasd_3990_erp_inspect_alias(
DEV_MESSAGE(KERN_ERR, cqr->startdev,
"ERP on alias device for request %p,"
" recover on base device %s", cqr,
- cqr->block->base->cdev->dev.bus_id);
+ dev_name(&cqr->block->base->cdev->dev));
}
dasd_eckd_reset_ccw_to_base_io(cqr);
erp->startdev = cqr->block->base;
@@ -1572,7 +1572,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
/* determine the address of the CCW to be restarted */
/* Imprecise ending is not set -> addr from IRB-SCSW */
- cpa = default_erp->refers->irb.scsw.cpa;
+ cpa = default_erp->refers->irb.scsw.cmd.cpa;
if (cpa == 0) {
@@ -1725,7 +1725,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense)
/* determine the address of the CCW to be restarted */
/* Imprecise ending is not set -> addr from IRB-SCSW */
- cpa = previous_erp->irb.scsw.cpa;
+ cpa = previous_erp->irb.scsw.cmd.cpa;
if (cpa == 0) {
@@ -2171,7 +2171,7 @@ dasd_3990_erp_control_check(struct dasd_ccw_req *erp)
{
struct dasd_device *device = erp->startdev;
- if (erp->refers->irb.scsw.cstat & (SCHN_STAT_INTF_CTRL_CHK
+ if (erp->refers->irb.scsw.cmd.cstat & (SCHN_STAT_INTF_CTRL_CHK
| SCHN_STAT_CHN_CTRL_CHK)) {
DEV_MESSAGE(KERN_DEBUG, device, "%s",
"channel or interface control check");
@@ -2352,9 +2352,9 @@ dasd_3990_erp_error_match(struct dasd_ccw_req *cqr1, struct dasd_ccw_req *cqr2)
if ((cqr1->irb.esw.esw0.erw.cons == 0) &&
(cqr2->irb.esw.esw0.erw.cons == 0)) {
- if ((cqr1->irb.scsw.cstat & (SCHN_STAT_INTF_CTRL_CHK |
+ if ((cqr1->irb.scsw.cmd.cstat & (SCHN_STAT_INTF_CTRL_CHK |
SCHN_STAT_CHN_CTRL_CHK)) ==
- (cqr2->irb.scsw.cstat & (SCHN_STAT_INTF_CTRL_CHK |
+ (cqr2->irb.scsw.cmd.cstat & (SCHN_STAT_INTF_CTRL_CHK |
SCHN_STAT_CHN_CTRL_CHK)))
return 1; /* match with ifcc*/
}
@@ -2622,8 +2622,9 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr)
}
/* double-check if current erp/cqr was successfull */
- if ((cqr->irb.scsw.cstat == 0x00) &&
- (cqr->irb.scsw.dstat == (DEV_STAT_CHN_END|DEV_STAT_DEV_END))) {
+ if ((cqr->irb.scsw.cmd.cstat == 0x00) &&
+ (cqr->irb.scsw.cmd.dstat ==
+ (DEV_STAT_CHN_END | DEV_STAT_DEV_END))) {
DEV_MESSAGE(KERN_DEBUG, device,
"ERP called for successful request %p"
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index d774e79476fe..794d1d472beb 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -515,9 +515,9 @@ dasd_devmap_from_cdev(struct ccw_device *cdev)
{
struct dasd_devmap *devmap;
- devmap = dasd_find_busid(cdev->dev.bus_id);
+ devmap = dasd_find_busid(dev_name(&cdev->dev));
if (IS_ERR(devmap))
- devmap = dasd_add_busid(cdev->dev.bus_id,
+ devmap = dasd_add_busid(dev_name(&cdev->dev),
DASD_FEATURE_DEFAULT);
return devmap;
}
@@ -584,7 +584,7 @@ dasd_delete_device(struct dasd_device *device)
unsigned long flags;
/* First remove device pointer from devmap. */
- devmap = dasd_find_busid(device->cdev->dev.bus_id);
+ devmap = dasd_find_busid(dev_name(&device->cdev->dev));
BUG_ON(IS_ERR(devmap));
spin_lock(&dasd_devmap_lock);
if (devmap->device != device) {
@@ -674,7 +674,7 @@ dasd_ro_show(struct device *dev, struct device_attribute *attr, char *buf)
struct dasd_devmap *devmap;
int ro_flag;
- devmap = dasd_find_busid(dev->bus_id);
+ devmap = dasd_find_busid(dev_name(dev));
if (!IS_ERR(devmap))
ro_flag = (devmap->features & DASD_FEATURE_READONLY) != 0;
else
@@ -723,7 +723,7 @@ dasd_erplog_show(struct device *dev, struct device_attribute *attr, char *buf)
struct dasd_devmap *devmap;
int erplog;
- devmap = dasd_find_busid(dev->bus_id);
+ devmap = dasd_find_busid(dev_name(dev));
if (!IS_ERR(devmap))
erplog = (devmap->features & DASD_FEATURE_ERPLOG) != 0;
else
@@ -770,7 +770,7 @@ dasd_use_diag_show(struct device *dev, struct device_attribute *attr, char *buf)
struct dasd_devmap *devmap;
int use_diag;
- devmap = dasd_find_busid(dev->bus_id);
+ devmap = dasd_find_busid(dev_name(dev));
if (!IS_ERR(devmap))
use_diag = (devmap->features & DASD_FEATURE_USEDIAG) != 0;
else
@@ -876,7 +876,7 @@ dasd_alias_show(struct device *dev, struct device_attribute *attr, char *buf)
struct dasd_devmap *devmap;
int alias;
- devmap = dasd_find_busid(dev->bus_id);
+ devmap = dasd_find_busid(dev_name(dev));
spin_lock(&dasd_devmap_lock);
if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) {
spin_unlock(&dasd_devmap_lock);
@@ -899,7 +899,7 @@ dasd_vendor_show(struct device *dev, struct device_attribute *attr, char *buf)
struct dasd_devmap *devmap;
char *vendor;
- devmap = dasd_find_busid(dev->bus_id);
+ devmap = dasd_find_busid(dev_name(dev));
spin_lock(&dasd_devmap_lock);
if (!IS_ERR(devmap) && strlen(devmap->uid.vendor) > 0)
vendor = devmap->uid.vendor;
@@ -923,7 +923,7 @@ dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf)
char ua_string[3];
struct dasd_uid *uid;
- devmap = dasd_find_busid(dev->bus_id);
+ devmap = dasd_find_busid(dev_name(dev));
spin_lock(&dasd_devmap_lock);
if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) {
spin_unlock(&dasd_devmap_lock);
@@ -962,7 +962,7 @@ dasd_eer_show(struct device *dev, struct device_attribute *attr, char *buf)
struct dasd_devmap *devmap;
int eer_flag;
- devmap = dasd_find_busid(dev->bus_id);
+ devmap = dasd_find_busid(dev_name(dev));
if (!IS_ERR(devmap) && devmap->device)
eer_flag = dasd_eer_enabled(devmap->device);
else
@@ -1024,7 +1024,7 @@ dasd_get_uid(struct ccw_device *cdev, struct dasd_uid *uid)
{
struct dasd_devmap *devmap;
- devmap = dasd_find_busid(cdev->dev.bus_id);
+ devmap = dasd_find_busid(dev_name(&cdev->dev));
if (IS_ERR(devmap))
return PTR_ERR(devmap);
spin_lock(&dasd_devmap_lock);
@@ -1047,7 +1047,7 @@ dasd_set_uid(struct ccw_device *cdev, struct dasd_uid *uid)
{
struct dasd_devmap *devmap;
- devmap = dasd_find_busid(cdev->dev.bus_id);
+ devmap = dasd_find_busid(dev_name(&cdev->dev));
if (IS_ERR(devmap))
return PTR_ERR(devmap);
@@ -1067,7 +1067,7 @@ dasd_get_feature(struct ccw_device *cdev, int feature)
{
struct dasd_devmap *devmap;
- devmap = dasd_find_busid(cdev->dev.bus_id);
+ devmap = dasd_find_busid(dev_name(&cdev->dev));
if (IS_ERR(devmap))
return PTR_ERR(devmap);
@@ -1083,7 +1083,7 @@ dasd_set_feature(struct ccw_device *cdev, int feature, int flag)
{
struct dasd_devmap *devmap;
- devmap = dasd_find_busid(cdev->dev.bus_id);
+ devmap = dasd_find_busid(dev_name(&cdev->dev));
if (IS_ERR(devmap))
return PTR_ERR(devmap);
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index a0edae091b5e..9164ada7e717 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -84,7 +84,7 @@ dasd_eckd_probe (struct ccw_device *cdev)
if (ret) {
printk(KERN_WARNING
"dasd_eckd_probe: could not set ccw-device options "
- "for %s\n", cdev->dev.bus_id);
+ "for %s\n", dev_name(&cdev->dev));
return ret;
}
ret = dasd_generic_probe(cdev, &dasd_eckd_discipline);
@@ -1404,13 +1404,14 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device,
/* first of all check for state change pending interrupt */
mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
- if ((irb->scsw.dstat & mask) == mask) {
+ if ((irb->scsw.cmd.dstat & mask) == mask) {
dasd_generic_handle_state_change(device);
return;
}
/* summary unit check */
- if ((irb->scsw.dstat & DEV_STAT_UNIT_CHECK) && irb->ecw[7] == 0x0D) {
+ if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
+ (irb->ecw[7] == 0x0D)) {
dasd_alias_handle_summary_unit_check(device, irb);
return;
}
@@ -2065,14 +2066,14 @@ static void dasd_eckd_dump_sense(struct dasd_device *device,
/* dump the sense data */
len = sprintf(page, KERN_ERR PRINTK_HEADER
" I/O status report for device %s:\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
" in req: %p CS: 0x%02X DS: 0x%02X\n", req,
- irb->scsw.cstat, irb->scsw.dstat);
+ irb->scsw.cmd.cstat, irb->scsw.cmd.dstat);
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
" device %s: Failing CCW: %p\n",
- device->cdev->dev.bus_id,
- (void *) (addr_t) irb->scsw.cpa);
+ dev_name(&device->cdev->dev),
+ (void *) (addr_t) irb->scsw.cmd.cpa);
if (irb->esw.esw0.erw.cons) {
for (sl = 0; sl < 4; sl++) {
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
@@ -2122,7 +2123,8 @@ static void dasd_eckd_dump_sense(struct dasd_device *device,
/* scsw->cda is either valid or zero */
len = 0;
from = ++to;
- fail = (struct ccw1 *)(addr_t) irb->scsw.cpa; /* failing CCW */
+ fail = (struct ccw1 *)(addr_t)
+ irb->scsw.cmd.cpa; /* failing CCW */
if (from < fail - 2) {
from = fail - 2; /* there is a gap - print header */
len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n");
diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c
index 6e53ab606e97..11b57d12e5a6 100644
--- a/drivers/s390/block/dasd_eer.c
+++ b/drivers/s390/block/dasd_eer.c
@@ -307,7 +307,7 @@ static void dasd_eer_write_standard_trigger(struct dasd_device *device,
do_gettimeofday(&tv);
header.tv_sec = tv.tv_sec;
header.tv_usec = tv.tv_usec;
- strncpy(header.busid, device->cdev->dev.bus_id, DASD_EER_BUSID_SIZE);
+ strncpy(header.busid, dev_name(&device->cdev->dev), DASD_EER_BUSID_SIZE);
spin_lock_irqsave(&bufferlock, flags);
list_for_each_entry(eerb, &bufferlist, list) {
@@ -347,7 +347,7 @@ static void dasd_eer_write_snss_trigger(struct dasd_device *device,
do_gettimeofday(&tv);
header.tv_sec = tv.tv_sec;
header.tv_usec = tv.tv_usec;
- strncpy(header.busid, device->cdev->dev.bus_id, DASD_EER_BUSID_SIZE);
+ strncpy(header.busid, dev_name(&device->cdev->dev), DASD_EER_BUSID_SIZE);
spin_lock_irqsave(&bufferlock, flags);
list_for_each_entry(eerb, &bufferlist, list) {
diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c
index 116611583df8..08db1ad41c58 100644
--- a/drivers/s390/block/dasd_fba.c
+++ b/drivers/s390/block/dasd_fba.c
@@ -222,7 +222,7 @@ static void dasd_fba_handle_unsolicited_interrupt(struct dasd_device *device,
/* first of all check for state change pending interrupt */
mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
- if ((irb->scsw.dstat & mask) == mask) {
+ if ((irb->scsw.cmd.dstat & mask) == mask) {
dasd_generic_handle_state_change(device);
return;
}
@@ -446,14 +446,14 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
}
len = sprintf(page, KERN_ERR PRINTK_HEADER
" I/O status report for device %s:\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
" in req: %p CS: 0x%02X DS: 0x%02X\n", req,
- irb->scsw.cstat, irb->scsw.dstat);
+ irb->scsw.cmd.cstat, irb->scsw.cmd.dstat);
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
" device %s: Failing CCW: %p\n",
- device->cdev->dev.bus_id,
- (void *) (addr_t) irb->scsw.cpa);
+ dev_name(&device->cdev->dev),
+ (void *) (addr_t) irb->scsw.cmd.cpa);
if (irb->esw.esw0.erw.cons) {
for (sl = 0; sl < 4; sl++) {
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
@@ -498,11 +498,11 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
/* print failing CCW area */
len = 0;
- if (act < ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2) {
- act = ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2;
+ if (act < ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2) {
+ act = ((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa) - 2;
len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n");
}
- end = min((struct ccw1 *)(addr_t) irb->scsw.cpa + 2, last);
+ end = min((struct ccw1 *)(addr_t) irb->scsw.cmd.cpa + 2, last);
while (act <= end) {
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
" CCW %p: %08X %08X DAT:",
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index fb2f931cf844..035d69155d41 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -126,7 +126,7 @@ do { \
#define DEV_MESSAGE(d_loglevel,d_device,d_string,d_args...)\
do { \
printk(d_loglevel PRINTK_HEADER " %s: " d_string "\n", \
- d_device->cdev->dev.bus_id, d_args); \
+ dev_name(&d_device->cdev->dev), d_args); \
DBF_DEV_EVENT(DBF_ALERT, d_device, d_string, d_args); \
} while(0)
@@ -140,7 +140,7 @@ do { \
#define DEV_MESSAGE_LOG(d_loglevel,d_device,d_string,d_args...)\
do { \
printk(d_loglevel PRINTK_HEADER " %s: " d_string "\n", \
- d_device->cdev->dev.bus_id, d_args); \
+ dev_name(&d_device->cdev->dev), d_args); \
} while(0)
#define MESSAGE_LOG(d_loglevel,d_string,d_args...)\
diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c
index 03c0e40a92ff..6ea60f3f72c0 100644
--- a/drivers/s390/block/dasd_proc.c
+++ b/drivers/s390/block/dasd_proc.c
@@ -67,7 +67,7 @@ dasd_devices_show(struct seq_file *m, void *v)
return 0;
}
/* Print device number. */
- seq_printf(m, "%s", device->cdev->dev.bus_id);
+ seq_printf(m, "%s", dev_name(&device->cdev->dev));
/* Print discipline string. */
if (device != NULL && device->discipline != NULL)
seq_printf(m, "(%s)", device->discipline->name);
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c
index bb52d2fbac18..9cf04ad5ef19 100644
--- a/drivers/s390/block/dcssblk.c
+++ b/drivers/s390/block/dcssblk.c
@@ -91,7 +91,7 @@ static struct rw_semaphore dcssblk_devices_sem;
static void
dcssblk_release_segment(struct device *dev)
{
- PRINT_DEBUG("segment release fn called for %s\n", dev->bus_id);
+ PRINT_DEBUG("segment release fn called for %s\n", dev_name(dev));
kfree(container_of(dev, struct dcssblk_dev_info, dev));
module_put(THIS_MODULE);
}
@@ -349,7 +349,7 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char
}
strcpy(dev_info->segment_name, local_buf);
- strlcpy(dev_info->dev.bus_id, local_buf, BUS_ID_SIZE);
+ dev_set_name(&dev_info->dev, local_buf);
dev_info->dev.release = dcssblk_release_segment;
INIT_LIST_HEAD(&dev_info->lh);
@@ -604,7 +604,8 @@ dcssblk_make_request(struct request_queue *q, struct bio *bio)
case SEG_TYPE_SC:
/* cannot write to these segments */
if (bio_data_dir(bio) == WRITE) {
- PRINT_WARN("rejecting write to ro segment %s\n", dev_info->dev.bus_id);
+ PRINT_WARN("rejecting write to ro segment %s\n",
+ dev_name(&dev_info->dev));
goto fail;
}
}
diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c
index 3e5653c92f4b..310eda82cde2 100644
--- a/drivers/s390/char/con3215.c
+++ b/drivers/s390/char/con3215.c
@@ -385,8 +385,8 @@ raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
raw = cdev->dev.driver_data;
req = (struct raw3215_req *) intparm;
- cstat = irb->scsw.cstat;
- dstat = irb->scsw.dstat;
+ cstat = irb->scsw.cmd.cstat;
+ dstat = irb->scsw.cmd.dstat;
if (cstat != 0) {
raw->message = KERN_WARNING
"Got nonzero channel status in raw3215_irq "
@@ -415,7 +415,7 @@ raw3215_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
return; /* That shouldn't happen ... */
if (req->type == RAW3215_READ) {
/* store residual count, then wait for device end */
- req->residual = irb->scsw.count;
+ req->residual = irb->scsw.cmd.count;
}
if (dstat == 0x08)
break;
diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c
index 0b040557db02..3c07974886ed 100644
--- a/drivers/s390/char/con3270.c
+++ b/drivers/s390/char/con3270.c
@@ -411,15 +411,15 @@ static int
con3270_irq(struct con3270 *cp, struct raw3270_request *rq, struct irb *irb)
{
/* Handle ATTN. Schedule tasklet to read aid. */
- if (irb->scsw.dstat & DEV_STAT_ATTENTION)
+ if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION)
con3270_issue_read(cp);
if (rq) {
- if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
rq->rc = -EIO;
else
/* Normal end. Copy residual count. */
- rq->rescnt = irb->scsw.count;
+ rq->rescnt = irb->scsw.cmd.count;
}
return RAW3270_IO_DONE;
}
diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c
index ef36f2132aa4..f0d67af96402 100644
--- a/drivers/s390/char/fs3270.c
+++ b/drivers/s390/char/fs3270.c
@@ -216,17 +216,17 @@ static int
fs3270_irq(struct fs3270 *fp, struct raw3270_request *rq, struct irb *irb)
{
/* Handle ATTN. Set indication and wake waiters for attention. */
- if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
fp->attention = 1;
wake_up(&fp->wait);
}
if (rq) {
- if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
rq->rc = -EIO;
else
/* Normal end. Copy residual count. */
- rq->rescnt = irb->scsw.count;
+ rq->rescnt = irb->scsw.cmd.count;
}
return RAW3270_IO_DONE;
}
diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c
index 848ef7e8523f..bd3e2dcf6b33 100644
--- a/drivers/s390/char/raw3270.c
+++ b/drivers/s390/char/raw3270.c
@@ -372,17 +372,17 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
if (IS_ERR(irb))
rc = RAW3270_IO_RETRY;
- else if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) {
+ else if (irb->scsw.cmd.fctl & SCSW_FCTL_HALT_FUNC) {
rq->rc = -EIO;
rc = RAW3270_IO_DONE;
- } else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END |
- DEV_STAT_UNIT_EXCEP)) {
+ } else if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END |
+ DEV_STAT_UNIT_EXCEP)) {
/* Handle CE-DE-UE and subsequent UDE */
set_bit(RAW3270_FLAGS_BUSY, &rp->flags);
rc = RAW3270_IO_BUSY;
} else if (test_bit(RAW3270_FLAGS_BUSY, &rp->flags)) {
/* Wait for UDE if busy flag is set. */
- if (irb->scsw.dstat & DEV_STAT_DEV_END) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
clear_bit(RAW3270_FLAGS_BUSY, &rp->flags);
/* Got it, now retry. */
rc = RAW3270_IO_RETRY;
@@ -497,7 +497,7 @@ raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq,
* Unit-Check Processing:
* Expect Command Reject or Intervention Required.
*/
- if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
/* Request finished abnormally. */
if (irb->ecw[0] & SNS0_INTERVENTION_REQ) {
set_bit(RAW3270_FLAGS_BUSY, &view->dev->flags);
@@ -505,16 +505,16 @@ raw3270_init_irq(struct raw3270_view *view, struct raw3270_request *rq,
}
}
if (rq) {
- if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
if (irb->ecw[0] & SNS0_CMD_REJECT)
rq->rc = -EOPNOTSUPP;
else
rq->rc = -EIO;
} else
/* Request finished normally. Copy residual count. */
- rq->rescnt = irb->scsw.count;
+ rq->rescnt = irb->scsw.cmd.count;
}
- if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
set_bit(RAW3270_FLAGS_ATTN, &view->dev->flags);
wake_up(&raw3270_wait_queue);
}
@@ -1178,17 +1178,19 @@ static int raw3270_create_attributes(struct raw3270 *rp)
if (rc)
goto out;
- rp->clttydev = device_create(class3270, &rp->cdev->dev,
- MKDEV(IBM_TTY3270_MAJOR, rp->minor),
- "tty%s", rp->cdev->dev.bus_id);
+ rp->clttydev = device_create_drvdata(class3270, &rp->cdev->dev,
+ MKDEV(IBM_TTY3270_MAJOR, rp->minor),
+ NULL,
+ "tty%s", dev_name(&rp->cdev->dev));
if (IS_ERR(rp->clttydev)) {
rc = PTR_ERR(rp->clttydev);
goto out_ttydev;
}
- rp->cltubdev = device_create(class3270, &rp->cdev->dev,
- MKDEV(IBM_FS3270_MAJOR, rp->minor),
- "tub%s", rp->cdev->dev.bus_id);
+ rp->cltubdev = device_create_drvdata(class3270, &rp->cdev->dev,
+ MKDEV(IBM_FS3270_MAJOR, rp->minor),
+ NULL,
+ "tub%s", dev_name(&rp->cdev->dev));
if (!IS_ERR(rp->cltubdev))
goto out;
diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c
index 62576af36f47..3e577f655b18 100644
--- a/drivers/s390/char/sclp_vt220.c
+++ b/drivers/s390/char/sclp_vt220.c
@@ -773,6 +773,7 @@ sclp_vt220_con_init(void)
{
int rc;
+ INIT_LIST_HEAD(&sclp_vt220_register.list);
if (!CONSOLE_IS_SCLP)
return 0;
rc = __sclp_vt220_init();
diff --git a/drivers/s390/char/tape_34xx.c b/drivers/s390/char/tape_34xx.c
index 874adf365e46..22ca34361ed7 100644
--- a/drivers/s390/char/tape_34xx.c
+++ b/drivers/s390/char/tape_34xx.c
@@ -196,7 +196,7 @@ tape_34xx_erp_retry(struct tape_request *request)
static int
tape_34xx_unsolicited_irq(struct tape_device *device, struct irb *irb)
{
- if (irb->scsw.dstat == 0x85 /* READY */) {
+ if (irb->scsw.cmd.dstat == 0x85) { /* READY */
/* A medium was inserted in the drive. */
DBF_EVENT(6, "xuud med\n");
tape_34xx_delete_sbid_from(device, 0);
@@ -844,22 +844,22 @@ tape_34xx_irq(struct tape_device *device, struct tape_request *request,
if (request == NULL)
return tape_34xx_unsolicited_irq(device, irb);
- if ((irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) &&
- (irb->scsw.dstat & DEV_STAT_DEV_END) &&
+ if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) &&
+ (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) &&
(request->op == TO_WRI)) {
/* Write at end of volume */
PRINT_INFO("End of volume\n"); /* XXX */
return tape_34xx_erp_failed(request, -ENOSPC);
}
- if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
return tape_34xx_unit_check(device, request, irb);
- if (irb->scsw.dstat & DEV_STAT_DEV_END) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
/*
* A unit exception occurs on skipping over a tapemark block.
*/
- if (irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) {
if (request->op == TO_BSB || request->op == TO_FSB)
request->rescnt++;
else
diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c
index 8246ef3ab095..c92debfec826 100644
--- a/drivers/s390/char/tape_3590.c
+++ b/drivers/s390/char/tape_3590.c
@@ -837,13 +837,13 @@ tape_3590_erp_retry(struct tape_device *device, struct tape_request *request,
static int
tape_3590_unsolicited_irq(struct tape_device *device, struct irb *irb)
{
- if (irb->scsw.dstat == DEV_STAT_CHN_END)
+ if (irb->scsw.cmd.dstat == DEV_STAT_CHN_END)
/* Probably result of halt ssch */
return TAPE_IO_PENDING;
- else if (irb->scsw.dstat == 0x85)
+ else if (irb->scsw.cmd.dstat == 0x85)
/* Device Ready */
DBF_EVENT(3, "unsol.irq! tape ready: %08x\n", device->cdev_id);
- else if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+ else if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
tape_3590_schedule_work(device, TO_READ_ATTMSG);
} else {
DBF_EVENT(3, "unsol.irq! dev end: %08x\n", device->cdev_id);
@@ -910,7 +910,7 @@ tape_3590_erp_swap(struct tape_device *device, struct tape_request *request,
* should proceed with the new tape... this
* should probably be done in user space!
*/
- PRINT_WARN("(%s): Swap Tape Device!\n", device->cdev->dev.bus_id);
+ PRINT_WARN("(%s): Swap Tape Device!\n", dev_name(&device->cdev->dev));
return tape_3590_erp_basic(device, request, irb, -EIO);
}
@@ -1003,40 +1003,40 @@ tape_3590_print_mim_msg_f0(struct tape_device *device, struct irb *irb)
/* Exception Message */
switch (sense->fmt.f70.emc) {
case 0x02:
- PRINT_WARN("(%s): Data degraded\n", device->cdev->dev.bus_id);
+ PRINT_WARN("(%s): Data degraded\n", dev_name(&device->cdev->dev));
break;
case 0x03:
PRINT_WARN("(%s): Data degraded in partion %i\n",
- device->cdev->dev.bus_id, sense->fmt.f70.mp);
+ dev_name(&device->cdev->dev), sense->fmt.f70.mp);
break;
case 0x04:
- PRINT_WARN("(%s): Medium degraded\n", device->cdev->dev.bus_id);
+ PRINT_WARN("(%s): Medium degraded\n", dev_name(&device->cdev->dev));
break;
case 0x05:
PRINT_WARN("(%s): Medium degraded in partition %i\n",
- device->cdev->dev.bus_id, sense->fmt.f70.mp);
+ dev_name(&device->cdev->dev), sense->fmt.f70.mp);
break;
case 0x06:
- PRINT_WARN("(%s): Block 0 Error\n", device->cdev->dev.bus_id);
+ PRINT_WARN("(%s): Block 0 Error\n", dev_name(&device->cdev->dev));
break;
case 0x07:
PRINT_WARN("(%s): Medium Exception 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f70.md);
+ dev_name(&device->cdev->dev), sense->fmt.f70.md);
break;
default:
PRINT_WARN("(%s): MIM ExMsg: 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f70.emc);
+ dev_name(&device->cdev->dev), sense->fmt.f70.emc);
break;
}
/* Service Message */
switch (sense->fmt.f70.smc) {
case 0x02:
PRINT_WARN("(%s): Reference Media maintenance procedure %i\n",
- device->cdev->dev.bus_id, sense->fmt.f70.md);
+ dev_name(&device->cdev->dev), sense->fmt.f70.md);
break;
default:
PRINT_WARN("(%s): MIM ServiceMsg: 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f70.smc);
+ dev_name(&device->cdev->dev), sense->fmt.f70.smc);
break;
}
}
@@ -1054,101 +1054,101 @@ tape_3590_print_io_sim_msg_f1(struct tape_device *device, struct irb *irb)
switch (sense->fmt.f71.emc) {
case 0x01:
PRINT_WARN("(%s): Effect of failure is unknown\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case 0x02:
PRINT_WARN("(%s): CU Exception - no performance impact\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case 0x03:
PRINT_WARN("(%s): CU Exception on channel interface 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
+ dev_name(&device->cdev->dev), sense->fmt.f71.md[0]);
break;
case 0x04:
PRINT_WARN("(%s): CU Exception on device path 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
+ dev_name(&device->cdev->dev), sense->fmt.f71.md[0]);
break;
case 0x05:
PRINT_WARN("(%s): CU Exception on library path 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
+ dev_name(&device->cdev->dev), sense->fmt.f71.md[0]);
break;
case 0x06:
PRINT_WARN("(%s): CU Exception on node 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
+ dev_name(&device->cdev->dev), sense->fmt.f71.md[0]);
break;
case 0x07:
PRINT_WARN("(%s): CU Exception on partition 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
+ dev_name(&device->cdev->dev), sense->fmt.f71.md[0]);
break;
default:
PRINT_WARN("(%s): SIM ExMsg: 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.emc);
+ dev_name(&device->cdev->dev), sense->fmt.f71.emc);
}
/* Service Message */
switch (sense->fmt.f71.smc) {
case 0x01:
PRINT_WARN("(%s): Repair impact is unknown\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case 0x02:
PRINT_WARN("(%s): Repair will not impact cu performance\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case 0x03:
if (sense->fmt.f71.mdf == 0)
PRINT_WARN("(%s): Repair will disable node "
"0x%x on CU\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1]);
else
PRINT_WARN("(%s): Repair will disable nodes "
"(0x%x-0x%x) on CU\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
break;
case 0x04:
if (sense->fmt.f71.mdf == 0)
PRINT_WARN("(%s): Repair will disable cannel path "
"0x%x on CU\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1]);
else
PRINT_WARN("(%s): Repair will disable cannel paths "
"(0x%x-0x%x) on CU\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
break;
case 0x05:
if (sense->fmt.f71.mdf == 0)
PRINT_WARN("(%s): Repair will disable device path "
"0x%x on CU\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1]);
else
PRINT_WARN("(%s): Repair will disable device paths "
"(0x%x-0x%x) on CU\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
break;
case 0x06:
if (sense->fmt.f71.mdf == 0)
PRINT_WARN("(%s): Repair will disable library path "
"0x%x on CU\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1]);
else
PRINT_WARN("(%s): Repair will disable library paths "
"(0x%x-0x%x) on CU\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
break;
case 0x07:
PRINT_WARN("(%s): Repair will disable access to CU\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
default:
PRINT_WARN("(%s): SIM ServiceMsg: 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.smc);
+ dev_name(&device->cdev->dev), sense->fmt.f71.smc);
}
}
@@ -1165,104 +1165,104 @@ tape_3590_print_dev_sim_msg_f2(struct tape_device *device, struct irb *irb)
switch (sense->fmt.f71.emc) {
case 0x01:
PRINT_WARN("(%s): Effect of failure is unknown\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case 0x02:
PRINT_WARN("(%s): DV Exception - no performance impact\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case 0x03:
PRINT_WARN("(%s): DV Exception on channel interface 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
+ dev_name(&device->cdev->dev), sense->fmt.f71.md[0]);
break;
case 0x04:
PRINT_WARN("(%s): DV Exception on loader 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
+ dev_name(&device->cdev->dev), sense->fmt.f71.md[0]);
break;
case 0x05:
PRINT_WARN("(%s): DV Exception on message display 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.md[0]);
+ dev_name(&device->cdev->dev), sense->fmt.f71.md[0]);
break;
case 0x06:
PRINT_WARN("(%s): DV Exception in tape path\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case 0x07:
PRINT_WARN("(%s): DV Exception in drive\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
default:
PRINT_WARN("(%s): DSIM ExMsg: 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.emc);
+ dev_name(&device->cdev->dev), sense->fmt.f71.emc);
}
/* Service Message */
switch (sense->fmt.f71.smc) {
case 0x01:
PRINT_WARN("(%s): Repair impact is unknown\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case 0x02:
PRINT_WARN("(%s): Repair will not impact device performance\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case 0x03:
if (sense->fmt.f71.mdf == 0)
PRINT_WARN("(%s): Repair will disable channel path "
"0x%x on DV\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1]);
else
PRINT_WARN("(%s): Repair will disable channel path "
"(0x%x-0x%x) on DV\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
break;
case 0x04:
if (sense->fmt.f71.mdf == 0)
PRINT_WARN("(%s): Repair will disable interface 0x%x "
"on DV\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1]);
else
PRINT_WARN("(%s): Repair will disable interfaces "
"(0x%x-0x%x) on DV\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
break;
case 0x05:
if (sense->fmt.f71.mdf == 0)
PRINT_WARN("(%s): Repair will disable loader 0x%x "
"on DV\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1]);
else
PRINT_WARN("(%s): Repair will disable loader "
"(0x%x-0x%x) on DV\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
break;
case 0x07:
PRINT_WARN("(%s): Repair will disable access to DV\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case 0x08:
if (sense->fmt.f71.mdf == 0)
PRINT_WARN("(%s): Repair will disable message "
"display 0x%x on DV\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1]);
else
PRINT_WARN("(%s): Repair will disable message "
"displays (0x%x-0x%x) on DV\n",
- device->cdev->dev.bus_id,
+ dev_name(&device->cdev->dev),
sense->fmt.f71.md[1], sense->fmt.f71.md[2]);
break;
case 0x09:
- PRINT_WARN("(%s): Clean DV\n", device->cdev->dev.bus_id);
+ PRINT_WARN("(%s): Clean DV\n", dev_name(&device->cdev->dev));
break;
default:
PRINT_WARN("(%s): DSIM ServiceMsg: 0x%02x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.smc);
+ dev_name(&device->cdev->dev), sense->fmt.f71.smc);
}
}
@@ -1279,18 +1279,18 @@ tape_3590_print_era_msg(struct tape_device *device, struct irb *irb)
return;
if ((sense->mc > 0) && (sense->mc < TAPE_3590_MAX_MSG)) {
if (tape_3590_msg[sense->mc] != NULL)
- PRINT_WARN("(%s): %s\n", device->cdev->dev.bus_id,
+ PRINT_WARN("(%s): %s\n", dev_name(&device->cdev->dev),
tape_3590_msg[sense->mc]);
else {
PRINT_WARN("(%s): Message Code 0x%x\n",
- device->cdev->dev.bus_id, sense->mc);
+ dev_name(&device->cdev->dev), sense->mc);
}
return;
}
if (sense->mc == 0xf0) {
/* Standard Media Information Message */
PRINT_WARN("(%s): MIM SEV=%i, MC=%02x, ES=%x/%x, "
- "RC=%02x-%04x-%02x\n", device->cdev->dev.bus_id,
+ "RC=%02x-%04x-%02x\n", dev_name(&device->cdev->dev),
sense->fmt.f70.sev, sense->mc,
sense->fmt.f70.emc, sense->fmt.f70.smc,
sense->fmt.f70.refcode, sense->fmt.f70.mid,
@@ -1302,7 +1302,7 @@ tape_3590_print_era_msg(struct tape_device *device, struct irb *irb)
/* Standard I/O Subsystem Service Information Message */
PRINT_WARN("(%s): IOSIM SEV=%i, DEVTYPE=3590/%02x, "
"MC=%02x, ES=%x/%x, REF=0x%04x-0x%04x-0x%04x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.sev,
+ dev_name(&device->cdev->dev), sense->fmt.f71.sev,
device->cdev->id.dev_model,
sense->mc, sense->fmt.f71.emc,
sense->fmt.f71.smc, sense->fmt.f71.refcode1,
@@ -1314,7 +1314,7 @@ tape_3590_print_era_msg(struct tape_device *device, struct irb *irb)
/* Standard Device Service Information Message */
PRINT_WARN("(%s): DEVSIM SEV=%i, DEVTYPE=3590/%02x, "
"MC=%02x, ES=%x/%x, REF=0x%04x-0x%04x-0x%04x\n",
- device->cdev->dev.bus_id, sense->fmt.f71.sev,
+ dev_name(&device->cdev->dev), sense->fmt.f71.sev,
device->cdev->id.dev_model,
sense->mc, sense->fmt.f71.emc,
sense->fmt.f71.smc, sense->fmt.f71.refcode1,
@@ -1327,7 +1327,7 @@ tape_3590_print_era_msg(struct tape_device *device, struct irb *irb)
return;
}
PRINT_WARN("(%s): Device Message(%x)\n",
- device->cdev->dev.bus_id, sense->mc);
+ dev_name(&device->cdev->dev), sense->mc);
}
static int tape_3590_crypt_error(struct tape_device *device,
@@ -1336,10 +1336,11 @@ static int tape_3590_crypt_error(struct tape_device *device,
u8 cu_rc, ekm_rc1;
u16 ekm_rc2;
u32 drv_rc;
- char *bus_id, *sense;
+ const char *bus_id;
+ char *sense;
sense = ((struct tape_3590_sense *) irb->ecw)->fmt.data;
- bus_id = device->cdev->dev.bus_id;
+ bus_id = dev_name(&device->cdev->dev);
cu_rc = sense[0];
drv_rc = *((u32*) &sense[5]) & 0xffffff;
ekm_rc1 = sense[9];
@@ -1440,7 +1441,7 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request,
* "device intervention" is not very meaningfull
*/
PRINT_WARN("(%s): Tape operation when medium not loaded\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
tape_med_state_set(device, MS_UNLOADED);
tape_3590_schedule_work(device, TO_CRYPT_OFF);
return tape_3590_erp_basic(device, request, irb, -ENOMEDIUM);
@@ -1487,18 +1488,18 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request,
case 0x6020:
PRINT_WARN("(%s): Cartridge of wrong type ?\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
return tape_3590_erp_basic(device, request, irb, -EMEDIUMTYPE);
case 0x8011:
PRINT_WARN("(%s): Another host has reserved the tape device\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
return tape_3590_erp_basic(device, request, irb, -EPERM);
case 0x8013:
PRINT_WARN("(%s): Another host has privileged access to the "
- "tape device\n", device->cdev->dev.bus_id);
+ "tape device\n", dev_name(&device->cdev->dev));
PRINT_WARN("(%s): To solve the problem unload the current "
- "cartridge!\n", device->cdev->dev.bus_id);
+ "cartridge!\n", dev_name(&device->cdev->dev));
return tape_3590_erp_basic(device, request, irb, -EPERM);
default:
return tape_3590_erp_basic(device, request, irb, -EIO);
@@ -1515,18 +1516,19 @@ tape_3590_irq(struct tape_device *device, struct tape_request *request,
if (request == NULL)
return tape_3590_unsolicited_irq(device, irb);
- if ((irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) &&
- (irb->scsw.dstat & DEV_STAT_DEV_END) && (request->op == TO_WRI)) {
+ if ((irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) &&
+ (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) &&
+ (request->op == TO_WRI)) {
/* Write at end of volume */
DBF_EVENT(2, "End of volume\n");
return tape_3590_erp_failed(device, request, irb, -ENOSPC);
}
- if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
return tape_3590_unit_check(device, request, irb);
- if (irb->scsw.dstat & DEV_STAT_DEV_END) {
- if (irb->scsw.dstat == DEV_STAT_UNIT_EXCEP) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_DEV_END) {
+ if (irb->scsw.cmd.dstat == DEV_STAT_UNIT_EXCEP) {
if (request->op == TO_FSB || request->op == TO_BSB)
request->rescnt++;
else
@@ -1536,12 +1538,12 @@ tape_3590_irq(struct tape_device *device, struct tape_request *request,
return tape_3590_done(device, request);
}
- if (irb->scsw.dstat & DEV_STAT_CHN_END) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_CHN_END) {
DBF_EVENT(2, "cannel end\n");
return TAPE_IO_PENDING;
}
- if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
DBF_EVENT(2, "Unit Attention when busy..\n");
return TAPE_IO_PENDING;
}
@@ -1598,7 +1600,7 @@ tape_3590_setup_device(struct tape_device *device)
rc = tape_3590_read_dev_chars(device, rdc_data);
if (rc) {
DBF_LH(3, "Read device characteristics failed!\n");
- goto fail_kmalloc;
+ goto fail_rdc_data;
}
rc = tape_std_assign(device);
if (rc)
diff --git a/drivers/s390/char/tape_block.c b/drivers/s390/char/tape_block.c
index 95da72bc17e8..a25b8bf54f41 100644
--- a/drivers/s390/char/tape_block.c
+++ b/drivers/s390/char/tape_block.c
@@ -278,7 +278,7 @@ tapeblock_cleanup_device(struct tape_device *device)
if (!device->blk_data.disk) {
PRINT_ERR("(%s): No gendisk to clean up!\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
goto cleanup_queue;
}
diff --git a/drivers/s390/char/tape_class.c b/drivers/s390/char/tape_class.c
index 6dfdb7c17981..12c2a5aaf31b 100644
--- a/drivers/s390/char/tape_class.c
+++ b/drivers/s390/char/tape_class.c
@@ -69,10 +69,9 @@ struct tape_class_device *register_tape_dev(
if (rc)
goto fail_with_cdev;
- tcd->class_device = device_create(tape_class, device,
- tcd->char_device->dev,
- "%s", tcd->device_name
- );
+ tcd->class_device = device_create_drvdata(tape_class, device,
+ tcd->char_device->dev,
+ NULL, "%s", tcd->device_name);
rc = IS_ERR(tcd->class_device) ? PTR_ERR(tcd->class_device) : 0;
if (rc)
goto fail_with_cdev;
diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c
index c20e3c548343..9c1e5fcc505c 100644
--- a/drivers/s390/char/tape_core.c
+++ b/drivers/s390/char/tape_core.c
@@ -215,12 +215,12 @@ tape_med_state_set(struct tape_device *device, enum tape_medium_state newstate)
case MS_UNLOADED:
device->tape_generic_status |= GMT_DR_OPEN(~0);
PRINT_INFO("(%s): Tape is unloaded\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
case MS_LOADED:
device->tape_generic_status &= ~GMT_DR_OPEN(~0);
PRINT_INFO("(%s): Tape has been mounted\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
break;
default:
// print nothing
@@ -415,7 +415,7 @@ tape_generic_offline(struct tape_device *device)
device->cdev_id);
PRINT_WARN("(%s): Set offline failed "
"- drive in use.\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
spin_unlock_irq(get_ccwdev_lock(device->cdev));
return -EBUSY;
}
@@ -538,7 +538,7 @@ tape_generic_probe(struct ccw_device *cdev)
ret = sysfs_create_group(&cdev->dev.kobj, &tape_attr_group);
if (ret) {
tape_put_device(device);
- PRINT_ERR("probe failed for tape device %s\n", cdev->dev.bus_id);
+ PRINT_ERR("probe failed for tape device %s\n", dev_name(&cdev->dev));
return ret;
}
cdev->dev.driver_data = device;
@@ -546,7 +546,7 @@ tape_generic_probe(struct ccw_device *cdev)
device->cdev = cdev;
ccw_device_get_id(cdev, &dev_id);
device->cdev_id = devid_to_int(&dev_id);
- PRINT_INFO("tape device %s found\n", cdev->dev.bus_id);
+ PRINT_INFO("tape device %s found\n", dev_name(&cdev->dev));
return ret;
}
@@ -616,7 +616,7 @@ tape_generic_remove(struct ccw_device *cdev)
device->cdev_id);
PRINT_WARN("(%s): Drive in use vanished - "
"expect trouble!\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
PRINT_WARN("State was %i\n", device->tape_state);
tape_state_set(device, TS_NOT_OPER);
__tape_discard_requests(device);
@@ -839,8 +839,8 @@ tape_dump_sense(struct tape_device* device, struct tape_request *request,
PRINT_INFO("-------------------------------------------------\n");
PRINT_INFO("DSTAT : %02x CSTAT: %02x CPA: %04x\n",
- irb->scsw.dstat, irb->scsw.cstat, irb->scsw.cpa);
- PRINT_INFO("DEVICE: %s\n", device->cdev->dev.bus_id);
+ irb->scsw.cmd.dstat, irb->scsw.cmd.cstat, irb->scsw.cmd.cpa);
+ PRINT_INFO("DEVICE: %s\n", dev_name(&device->cdev->dev));
if (request != NULL)
PRINT_INFO("OP : %s\n", tape_op_verbose[request->op]);
@@ -867,7 +867,7 @@ tape_dump_sense_dbf(struct tape_device *device, struct tape_request *request,
else
op = "---";
DBF_EVENT(3, "DSTAT : %02x CSTAT: %02x\n",
- irb->scsw.dstat,irb->scsw.cstat);
+ irb->scsw.cmd.dstat, irb->scsw.cmd.cstat);
DBF_EVENT(3, "DEVICE: %08x OP\t: %s\n", device->cdev_id, op);
sptr = (unsigned int *) irb->ecw;
DBF_EVENT(3, "%08x %08x\n", sptr[0], sptr[1]);
@@ -1051,7 +1051,7 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
device = (struct tape_device *) cdev->dev.driver_data;
if (device == NULL) {
PRINT_ERR("could not get device structure for %s "
- "in interrupt\n", cdev->dev.bus_id);
+ "in interrupt\n", dev_name(&cdev->dev));
return;
}
request = (struct tape_request *) intparm;
@@ -1064,13 +1064,13 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
switch (PTR_ERR(irb)) {
case -ETIMEDOUT:
PRINT_WARN("(%s): Request timed out\n",
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
case -EIO:
__tape_end_request(device, request, -EIO);
break;
default:
PRINT_ERR("(%s): Unexpected i/o error %li\n",
- cdev->dev.bus_id,
+ dev_name(&cdev->dev),
PTR_ERR(irb));
}
return;
@@ -1083,10 +1083,11 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
* error might still apply. So we just schedule the request to be
* started later.
*/
- if (irb->scsw.cc != 0 && (irb->scsw.fctl & SCSW_FCTL_START_FUNC) &&
+ if (irb->scsw.cmd.cc != 0 &&
+ (irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC) &&
(request->status == TAPE_REQUEST_IN_IO)) {
DBF_EVENT(3,"(%08x): deferred cc=%i, fctl=%i. restarting\n",
- device->cdev_id, irb->scsw.cc, irb->scsw.fctl);
+ device->cdev_id, irb->scsw.cmd.cc, irb->scsw.cmd.fctl);
request->status = TAPE_REQUEST_QUEUED;
schedule_delayed_work(&device->tape_dnr, HZ);
return;
@@ -1094,8 +1095,8 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
/* May be an unsolicited irq */
if(request != NULL)
- request->rescnt = irb->scsw.count;
- else if ((irb->scsw.dstat == 0x85 || irb->scsw.dstat == 0x80) &&
+ request->rescnt = irb->scsw.cmd.count;
+ else if ((irb->scsw.cmd.dstat == 0x85 || irb->scsw.cmd.dstat == 0x80) &&
!list_empty(&device->req_queue)) {
/* Not Ready to Ready after long busy ? */
struct tape_request *req;
@@ -1111,7 +1112,7 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
return;
}
}
- if (irb->scsw.dstat != 0x0c) {
+ if (irb->scsw.cmd.dstat != 0x0c) {
/* Set the 'ONLINE' flag depending on sense byte 1 */
if(*(((__u8 *) irb->ecw) + 1) & SENSE_DRIVE_ONLINE)
device->tape_generic_status |= GMT_ONLINE(~0);
diff --git a/drivers/s390/char/tape_proc.c b/drivers/s390/char/tape_proc.c
index e7c888c14e71..8a376af926a7 100644
--- a/drivers/s390/char/tape_proc.c
+++ b/drivers/s390/char/tape_proc.c
@@ -52,7 +52,7 @@ static int tape_proc_show(struct seq_file *m, void *v)
return 0;
spin_lock_irq(get_ccwdev_lock(device->cdev));
seq_printf(m, "%d\t", (int) n);
- seq_printf(m, "%-10.10s ", device->cdev->dev.bus_id);
+ seq_printf(m, "%-10.10s ", dev_name(&device->cdev->dev));
seq_printf(m, "%04X/", device->cdev->id.cu_type);
seq_printf(m, "%02X\t", device->cdev->id.cu_model);
seq_printf(m, "%04X/", device->cdev->id.dev_type);
diff --git a/drivers/s390/char/tape_std.c b/drivers/s390/char/tape_std.c
index 2a1af4e60be0..1abeeaa25d6f 100644
--- a/drivers/s390/char/tape_std.c
+++ b/drivers/s390/char/tape_std.c
@@ -47,7 +47,7 @@ tape_std_assign_timeout(unsigned long data)
rc = tape_cancel_io(device, request);
if(rc)
PRINT_ERR("(%s): Assign timeout: Cancel failed with rc = %i\n",
- device->cdev->dev.bus_id, rc);
+ dev_name(&device->cdev->dev), rc);
}
@@ -83,7 +83,7 @@ tape_std_assign(struct tape_device *device)
if (rc != 0) {
PRINT_WARN("%s: assign failed - device might be busy\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
DBF_EVENT(3, "%08x: assign failed - device might be busy\n",
device->cdev_id);
} else {
@@ -106,7 +106,7 @@ tape_std_unassign (struct tape_device *device)
DBF_EVENT(3, "(%08x): Can't unassign device\n",
device->cdev_id);
PRINT_WARN("(%s): Can't unassign device - device gone\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
return -EIO;
}
@@ -120,7 +120,7 @@ tape_std_unassign (struct tape_device *device)
if ((rc = tape_do_io(device, request)) != 0) {
DBF_EVENT(3, "%08x: Unassign failed\n", device->cdev_id);
- PRINT_WARN("%s: Unassign failed\n", device->cdev->dev.bus_id);
+ PRINT_WARN("%s: Unassign failed\n", dev_name(&device->cdev->dev));
} else {
DBF_EVENT(3, "%08x: Tape unassigned\n", device->cdev_id);
}
@@ -634,10 +634,10 @@ tape_std_mtcompression(struct tape_device *device, int mt_count)
DBF_EXCEPTION(6, "xcom parm\n");
if (*device->modeset_byte & 0x08)
PRINT_INFO("(%s) Compression is currently on\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
else
PRINT_INFO("(%s) Compression is currently off\n",
- device->cdev->dev.bus_id);
+ dev_name(&device->cdev->dev));
PRINT_INFO("Use 1 to switch compression on, 0 to "
"switch it off\n");
return -EINVAL;
diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c
index 5043150019ac..e4ba8bdce32e 100644
--- a/drivers/s390/char/tty3270.c
+++ b/drivers/s390/char/tty3270.c
@@ -663,7 +663,7 @@ static int
tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb)
{
/* Handle ATTN. Schedule tasklet to read aid. */
- if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
if (!tp->throttle)
tty3270_issue_read(tp, 0);
else
@@ -671,11 +671,11 @@ tty3270_irq(struct tty3270 *tp, struct raw3270_request *rq, struct irb *irb)
}
if (rq) {
- if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK)
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
rq->rc = -EIO;
else
/* Normal end. Copy residual count. */
- rq->rescnt = irb->scsw.count;
+ rq->rescnt = irb->scsw.cmd.count;
}
return RAW3270_IO_DONE;
}
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index 2c2428cc05d8..7ace5859b5d8 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -738,8 +738,7 @@ static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv)
dev = kzalloc(sizeof(struct device), GFP_KERNEL);
if (dev) {
- snprintf(dev->bus_id, BUS_ID_SIZE, "%s",
- priv->internal_name);
+ dev_set_name(dev, priv->internal_name);
dev->bus = &iucv_bus;
dev->parent = iucv_root;
dev->driver = &vmlogrdr_driver;
@@ -765,7 +764,7 @@ static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv)
priv->class_device = device_create_drvdata(vmlogrdr_class, dev,
MKDEV(vmlogrdr_major,
priv->minor_num),
- priv, "%s", dev->bus_id);
+ priv, "%s", dev_name(dev));
if (IS_ERR(priv->class_device)) {
ret = PTR_ERR(priv->class_device);
priv->class_device=NULL;
diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c
index 83ae9a852f00..22696a2e15dd 100644
--- a/drivers/s390/char/vmur.c
+++ b/drivers/s390/char/vmur.c
@@ -277,7 +277,8 @@ static void ur_int_handler(struct ccw_device *cdev, unsigned long intparm,
struct urdev *urd;
TRACE("ur_int_handler: intparm=0x%lx cstat=%02x dstat=%02x res=%u\n",
- intparm, irb->scsw.cstat, irb->scsw.dstat, irb->scsw.count);
+ intparm, irb->scsw.cmd.cstat, irb->scsw.cmd.dstat,
+ irb->scsw.cmd.count);
if (!intparm) {
TRACE("ur_int_handler: unsolicited interrupt\n");
@@ -288,7 +289,7 @@ static void ur_int_handler(struct ccw_device *cdev, unsigned long intparm,
/* On special conditions irb is an error pointer */
if (IS_ERR(irb))
urd->io_request_rc = PTR_ERR(irb);
- else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
+ else if (irb->scsw.cmd.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
urd->io_request_rc = 0;
else
urd->io_request_rc = -EIO;
@@ -879,18 +880,19 @@ static int ur_set_online(struct ccw_device *cdev)
goto fail_free_cdev;
if (urd->cdev->id.cu_type == READER_PUNCH_DEVTYPE) {
if (urd->class == DEV_CLASS_UR_I)
- sprintf(node_id, "vmrdr-%s", cdev->dev.bus_id);
+ sprintf(node_id, "vmrdr-%s", dev_name(&cdev->dev));
if (urd->class == DEV_CLASS_UR_O)
- sprintf(node_id, "vmpun-%s", cdev->dev.bus_id);
+ sprintf(node_id, "vmpun-%s", dev_name(&cdev->dev));
} else if (urd->cdev->id.cu_type == PRINTER_DEVTYPE) {
- sprintf(node_id, "vmprt-%s", cdev->dev.bus_id);
+ sprintf(node_id, "vmprt-%s", dev_name(&cdev->dev));
} else {
rc = -ENOTSUPP;
goto fail_free_cdev;
}
- urd->device = device_create(vmur_class, NULL, urd->char_device->dev,
- "%s", node_id);
+ urd->device = device_create_drvdata(vmur_class, NULL,
+ urd->char_device->dev, NULL,
+ "%s", node_id);
if (IS_ERR(urd->device)) {
rc = PTR_ERR(urd->device);
TRACE("ur_set_online: device_create rc=%d\n", rc);
diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile
index cfaf77b320f5..149a658173fe 100644
--- a/drivers/s390/cio/Makefile
+++ b/drivers/s390/cio/Makefile
@@ -2,7 +2,8 @@
# Makefile for the S/390 common i/o drivers
#
-obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o
+obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o scsw.o fcx.o \
+ itcw.o isc.o
ccw_device-objs += device.o device_fsm.o device_ops.o
ccw_device-objs += device_id.o device_pgid.o device_status.o
obj-y += ccw_device.o cmf.o
diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c
index b7a07a866291..fe6cea15bbaf 100644
--- a/drivers/s390/cio/airq.c
+++ b/drivers/s390/cio/airq.c
@@ -15,6 +15,7 @@
#include <linux/rcupdate.h>
#include <asm/airq.h>
+#include <asm/isc.h>
#include "cio.h"
#include "cio_debug.h"
@@ -33,15 +34,15 @@ struct airq_t {
void *drv_data;
};
-static union indicator_t indicators;
-static struct airq_t *airqs[NR_AIRQS];
+static union indicator_t indicators[MAX_ISC];
+static struct airq_t *airqs[MAX_ISC][NR_AIRQS];
-static int register_airq(struct airq_t *airq)
+static int register_airq(struct airq_t *airq, u8 isc)
{
int i;
for (i = 0; i < NR_AIRQS; i++)
- if (!cmpxchg(&airqs[i], NULL, airq))
+ if (!cmpxchg(&airqs[isc][i], NULL, airq))
return i;
return -ENOMEM;
}
@@ -50,18 +51,21 @@ static int register_airq(struct airq_t *airq)
* s390_register_adapter_interrupt() - register adapter interrupt handler
* @handler: adapter handler to be registered
* @drv_data: driver data passed with each call to the handler
+ * @isc: isc for which the handler should be called
*
* Returns:
* Pointer to the indicator to be used on success
* ERR_PTR() if registration failed
*/
void *s390_register_adapter_interrupt(adapter_int_handler_t handler,
- void *drv_data)
+ void *drv_data, u8 isc)
{
struct airq_t *airq;
char dbf_txt[16];
int ret;
+ if (isc > MAX_ISC)
+ return ERR_PTR(-EINVAL);
airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL);
if (!airq) {
ret = -ENOMEM;
@@ -69,34 +73,35 @@ void *s390_register_adapter_interrupt(adapter_int_handler_t handler,
}
airq->handler = handler;
airq->drv_data = drv_data;
- ret = register_airq(airq);
- if (ret < 0)
- kfree(airq);
+
+ ret = register_airq(airq, isc);
out:
snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%d", ret);
CIO_TRACE_EVENT(4, dbf_txt);
- if (ret < 0)
+ if (ret < 0) {
+ kfree(airq);
return ERR_PTR(ret);
- else
- return &indicators.byte[ret];
+ } else
+ return &indicators[isc].byte[ret];
}
EXPORT_SYMBOL(s390_register_adapter_interrupt);
/**
* s390_unregister_adapter_interrupt - unregister adapter interrupt handler
* @ind: indicator for which the handler is to be unregistered
+ * @isc: interruption subclass
*/
-void s390_unregister_adapter_interrupt(void *ind)
+void s390_unregister_adapter_interrupt(void *ind, u8 isc)
{
struct airq_t *airq;
char dbf_txt[16];
int i;
- i = (int) ((addr_t) ind) - ((addr_t) &indicators.byte[0]);
+ i = (int) ((addr_t) ind) - ((addr_t) &indicators[isc].byte[0]);
snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%d", i);
CIO_TRACE_EVENT(4, dbf_txt);
- indicators.byte[i] = 0;
- airq = xchg(&airqs[i], NULL);
+ indicators[isc].byte[i] = 0;
+ airq = xchg(&airqs[isc][i], NULL);
/*
* Allow interrupts to complete. This will ensure that the airq handle
* is no longer referenced by any interrupt handler.
@@ -108,7 +113,7 @@ EXPORT_SYMBOL(s390_unregister_adapter_interrupt);
#define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8))
-void do_adapter_IO(void)
+void do_adapter_IO(u8 isc)
{
int w;
int i;
@@ -120,22 +125,22 @@ void do_adapter_IO(void)
* fetch operations.
*/
for (w = 0; w < NR_AIRQ_WORDS; w++) {
- word = indicators.word[w];
+ word = indicators[isc].word[w];
i = w * NR_AIRQS_PER_WORD;
/*
* Check bytes within word for active indicators.
*/
while (word) {
if (word & INDICATOR_MASK) {
- airq = airqs[i];
+ airq = airqs[isc][i];
if (likely(airq))
- airq->handler(&indicators.byte[i],
+ airq->handler(&indicators[isc].byte[i],
airq->drv_data);
else
/*
* Reset ill-behaved indicator.
*/
- indicators.byte[i] = 0;
+ indicators[isc].byte[i] = 0;
}
word <<= 8;
i++;
diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c
index a4a5f2efea48..0bfcbbe375c4 100644
--- a/drivers/s390/cio/blacklist.c
+++ b/drivers/s390/cio/blacklist.c
@@ -97,8 +97,8 @@ static int pure_hex(char **cp, unsigned int *val, int min_digit,
return 0;
}
-static int parse_busid(char *str, int *cssid, int *ssid, int *devno,
- int msgtrigger)
+static int parse_busid(char *str, unsigned int *cssid, unsigned int *ssid,
+ unsigned int *devno, int msgtrigger)
{
char *str_work;
int val, rc, ret;
@@ -148,7 +148,7 @@ out:
static int blacklist_parse_parameters(char *str, range_action action,
int msgtrigger)
{
- int from_cssid, to_cssid, from_ssid, to_ssid, from, to;
+ unsigned int from_cssid, to_cssid, from_ssid, to_ssid, from, to;
int rc, totalrc;
char *parm;
range_action ra;
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index 26a930e832bd..859ea264801b 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -264,8 +264,7 @@ int ccwgroup_create_from_string(struct device *root, unsigned int creator_id,
gdev->dev.parent = root;
gdev->dev.release = ccwgroup_release;
- snprintf (gdev->dev.bus_id, BUS_ID_SIZE, "%s",
- gdev->cdev[0]->dev.bus_id);
+ dev_set_name(&gdev->dev, "%s", dev_name(&gdev->cdev[0]->dev));
rc = device_register(&gdev->dev);
if (rc)
diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c
index 297cdceb0ca4..f7f5a1f3fbf7 100644
--- a/drivers/s390/cio/chp.c
+++ b/drivers/s390/cio/chp.c
@@ -18,6 +18,7 @@
#include <asm/chpid.h>
#include <asm/sclp.h>
+#include "../s390mach.h"
#include "cio.h"
#include "css.h"
#include "ioasm.h"
@@ -94,6 +95,7 @@ u8 chp_get_sch_opm(struct subchannel *sch)
}
return opm;
}
+EXPORT_SYMBOL_GPL(chp_get_sch_opm);
/**
* chp_is_registered - check if a channel-path is registered
@@ -401,8 +403,7 @@ int chp_new(struct chp_id chpid)
chp->state = 1;
chp->dev.parent = &channel_subsystems[chpid.cssid]->device;
chp->dev.release = chp_release;
- snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp%x.%02x", chpid.cssid,
- chpid.id);
+ dev_set_name(&chp->dev, "chp%x.%02x", chpid.cssid, chpid.id);
/* Obtain channel path description and fill it in. */
ret = chsc_determine_channel_path_description(chpid, &chp->desc);
@@ -413,8 +414,7 @@ int chp_new(struct chp_id chpid)
goto out_free;
}
/* Get channel-measurement characteristics. */
- if (css_characteristics_avail && css_chsc_characteristics.scmc
- && css_chsc_characteristics.secm) {
+ if (css_chsc_characteristics.scmc && css_chsc_characteristics.secm) {
ret = chsc_get_channel_measurement_chars(chp);
if (ret)
goto out_free;
@@ -476,26 +476,74 @@ void *chp_get_chp_desc(struct chp_id chpid)
/**
* chp_process_crw - process channel-path status change
- * @id: channel-path ID number
- * @status: non-zero if channel-path has become available, zero otherwise
+ * @crw0: channel report-word to handler
+ * @crw1: second channel-report word (always NULL)
+ * @overflow: crw overflow indication
*
* Handle channel-report-words indicating that the status of a channel-path
* has changed.
*/
-void chp_process_crw(int id, int status)
+static void chp_process_crw(struct crw *crw0, struct crw *crw1,
+ int overflow)
{
struct chp_id chpid;
+ if (overflow) {
+ css_schedule_eval_all();
+ return;
+ }
+ CIO_CRW_EVENT(2, "CRW reports slct=%d, oflw=%d, "
+ "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
+ crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc,
+ crw0->erc, crw0->rsid);
+ /*
+ * Check for solicited machine checks. These are
+ * created by reset channel path and need not be
+ * handled here.
+ */
+ if (crw0->slct) {
+ CIO_CRW_EVENT(2, "solicited machine check for "
+ "channel path %02X\n", crw0->rsid);
+ return;
+ }
chp_id_init(&chpid);
- chpid.id = id;
- if (status) {
+ chpid.id = crw0->rsid;
+ switch (crw0->erc) {
+ case CRW_ERC_IPARM: /* Path has come. */
if (!chp_is_registered(chpid))
chp_new(chpid);
chsc_chp_online(chpid);
- } else
+ break;
+ case CRW_ERC_PERRI: /* Path has gone. */
+ case CRW_ERC_PERRN:
chsc_chp_offline(chpid);
+ break;
+ default:
+ CIO_CRW_EVENT(2, "Don't know how to handle erc=%x\n",
+ crw0->erc);
+ }
}
+int chp_ssd_get_mask(struct chsc_ssd_info *ssd, struct res_acc_data *data)
+{
+ int i;
+ int mask;
+
+ for (i = 0; i < 8; i++) {
+ mask = 0x80 >> i;
+ if (!(ssd->path_mask & mask))
+ continue;
+ if (!chp_id_is_equal(&ssd->chpid[i], &data->chpid))
+ continue;
+ if ((ssd->fla_valid_mask & mask) &&
+ ((ssd->fla[i] & data->fla_mask) != data->fla))
+ continue;
+ return mask;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(chp_ssd_get_mask);
+
static inline int info_bit_num(struct chp_id id)
{
return id.id + id.cssid * (__MAX_CHPID + 1);
@@ -654,10 +702,16 @@ static int cfg_wait_idle(void)
static int __init chp_init(void)
{
struct chp_id chpid;
+ int ret;
+ ret = s390_register_crw_handler(CRW_RSC_CPATH, chp_process_crw);
+ if (ret)
+ return ret;
chp_wq = create_singlethread_workqueue("cio_chp");
- if (!chp_wq)
+ if (!chp_wq) {
+ s390_unregister_crw_handler(CRW_RSC_CPATH);
return -ENOMEM;
+ }
INIT_WORK(&cfg_work, cfg_func);
init_waitqueue_head(&cfg_wait_queue);
if (info_update())
diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h
index 65286563c592..dffe2771ddac 100644
--- a/drivers/s390/cio/chp.h
+++ b/drivers/s390/cio/chp.h
@@ -12,12 +12,24 @@
#include <linux/device.h>
#include <asm/chpid.h>
#include "chsc.h"
+#include "css.h"
#define CHP_STATUS_STANDBY 0
#define CHP_STATUS_CONFIGURED 1
#define CHP_STATUS_RESERVED 2
#define CHP_STATUS_NOT_RECOGNIZED 3
+#define CHP_ONLINE 0
+#define CHP_OFFLINE 1
+#define CHP_VARY_ON 2
+#define CHP_VARY_OFF 3
+
+struct res_acc_data {
+ struct chp_id chpid;
+ u32 fla_mask;
+ u16 fla;
+};
+
static inline int chp_test_bit(u8 *bitmap, int num)
{
int byte = num >> 3;
@@ -42,12 +54,11 @@ int chp_get_status(struct chp_id chpid);
u8 chp_get_sch_opm(struct subchannel *sch);
int chp_is_registered(struct chp_id chpid);
void *chp_get_chp_desc(struct chp_id chpid);
-void chp_process_crw(int id, int available);
void chp_remove_cmg_attr(struct channel_path *chp);
int chp_add_cmg_attr(struct channel_path *chp);
int chp_new(struct chp_id chpid);
void chp_cfg_schedule(struct chp_id chpid, int configure);
void chp_cfg_cancel_deconfigure(struct chp_id chpid);
int chp_info_get_status(struct chp_id chpid);
-
+int chp_ssd_get_mask(struct chsc_ssd_info *, struct res_acc_data *);
#endif /* S390_CHP_H */
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 5de86908b0d0..62b0b16fe3d3 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -2,8 +2,7 @@
* drivers/s390/cio/chsc.c
* S/390 common I/O routines -- channel subsystem call
*
- * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
- * IBM Corporation
+ * Copyright IBM Corp. 1999,2008
* Author(s): Ingo Adlung (adlung@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
* Arnd Bergmann (arndb@de.ibm.com)
@@ -17,6 +16,7 @@
#include <asm/cio.h>
#include <asm/chpid.h>
+#include "../s390mach.h"
#include "css.h"
#include "cio.h"
#include "cio_debug.h"
@@ -127,77 +127,12 @@ out_free:
return ret;
}
-static int check_for_io_on_path(struct subchannel *sch, int mask)
-{
- int cc;
-
- cc = stsch(sch->schid, &sch->schib);
- if (cc)
- return 0;
- if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == mask)
- return 1;
- return 0;
-}
-
-static void terminate_internal_io(struct subchannel *sch)
-{
- if (cio_clear(sch)) {
- /* Recheck device in case clear failed. */
- sch->lpm = 0;
- if (device_trigger_verify(sch) != 0)
- css_schedule_eval(sch->schid);
- return;
- }
- /* Request retry of internal operation. */
- device_set_intretry(sch);
- /* Call handler. */
- if (sch->driver && sch->driver->termination)
- sch->driver->termination(sch);
-}
-
static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data)
{
- int j;
- int mask;
- struct chp_id *chpid = data;
- struct schib schib;
-
- for (j = 0; j < 8; j++) {
- mask = 0x80 >> j;
- if ((sch->schib.pmcw.pim & mask) &&
- (sch->schib.pmcw.chpid[j] == chpid->id))
- break;
- }
- if (j >= 8)
- return 0;
-
spin_lock_irq(sch->lock);
-
- stsch(sch->schid, &schib);
- if (!css_sch_is_valid(&schib))
- goto out_unreg;
- memcpy(&sch->schib, &schib, sizeof(struct schib));
- /* Check for single path devices. */
- if (sch->schib.pmcw.pim == 0x80)
- goto out_unreg;
-
- if (check_for_io_on_path(sch, mask)) {
- if (device_is_online(sch))
- device_kill_io(sch);
- else {
- terminate_internal_io(sch);
- /* Re-start path verification. */
- if (sch->driver && sch->driver->verify)
- sch->driver->verify(sch);
- }
- } else {
- /* trigger path verification. */
- if (sch->driver && sch->driver->verify)
- sch->driver->verify(sch);
- else if (sch->lpm == mask)
+ if (sch->driver && sch->driver->chp_event)
+ if (sch->driver->chp_event(sch, data, CHP_OFFLINE) != 0)
goto out_unreg;
- }
-
spin_unlock_irq(sch->lock);
return 0;
@@ -242,53 +177,11 @@ static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data)
return 0;
}
-struct res_acc_data {
- struct chp_id chpid;
- u32 fla_mask;
- u16 fla;
-};
-
-static int get_res_chpid_mask(struct chsc_ssd_info *ssd,
- struct res_acc_data *data)
-{
- int i;
- int mask;
-
- for (i = 0; i < 8; i++) {
- mask = 0x80 >> i;
- if (!(ssd->path_mask & mask))
- continue;
- if (!chp_id_is_equal(&ssd->chpid[i], &data->chpid))
- continue;
- if ((ssd->fla_valid_mask & mask) &&
- ((ssd->fla[i] & data->fla_mask) != data->fla))
- continue;
- return mask;
- }
- return 0;
-}
-
static int __s390_process_res_acc(struct subchannel *sch, void *data)
{
- int chp_mask, old_lpm;
- struct res_acc_data *res_data = data;
-
spin_lock_irq(sch->lock);
- chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data);
- if (chp_mask == 0)
- goto out;
- if (stsch(sch->schid, &sch->schib))
- goto out;
- old_lpm = sch->lpm;
- sch->lpm = ((sch->schib.pmcw.pim &
- sch->schib.pmcw.pam &
- sch->schib.pmcw.pom)
- | chp_mask) & sch->opm;
- if (!old_lpm && sch->lpm)
- device_trigger_reprobe(sch);
- else if (sch->driver && sch->driver->verify)
- sch->driver->verify(sch);
-out:
+ if (sch->driver && sch->driver->chp_event)
+ sch->driver->chp_event(sch, data, CHP_ONLINE);
spin_unlock_irq(sch->lock);
return 0;
@@ -480,17 +373,25 @@ static void chsc_process_sei(struct chsc_sei_area *sei_area)
}
}
-void chsc_process_crw(void)
+static void chsc_process_crw(struct crw *crw0, struct crw *crw1, int overflow)
{
struct chsc_sei_area *sei_area;
+ if (overflow) {
+ css_schedule_eval_all();
+ return;
+ }
+ CIO_CRW_EVENT(2, "CRW reports slct=%d, oflw=%d, "
+ "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
+ crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc,
+ crw0->erc, crw0->rsid);
if (!sei_page)
return;
/* Access to sei_page is serialized through machine check handler
* thread, so no need for locking. */
sei_area = sei_page;
- CIO_TRACE_EVENT( 2, "prcss");
+ CIO_TRACE_EVENT(2, "prcss");
do {
memset(sei_area, 0, sizeof(*sei_area));
sei_area->request.length = 0x0010;
@@ -509,114 +410,36 @@ void chsc_process_crw(void)
} while (sei_area->flags & 0x80);
}
-static int __chp_add_new_sch(struct subchannel_id schid, void *data)
-{
- struct schib schib;
-
- if (stsch_err(schid, &schib))
- /* We're through */
- return -ENXIO;
-
- /* Put it on the slow path. */
- css_schedule_eval(schid);
- return 0;
-}
-
-
-static int __chp_add(struct subchannel *sch, void *data)
-{
- int i, mask;
- struct chp_id *chpid = data;
-
- spin_lock_irq(sch->lock);
- for (i=0; i<8; i++) {
- mask = 0x80 >> i;
- if ((sch->schib.pmcw.pim & mask) &&
- (sch->schib.pmcw.chpid[i] == chpid->id))
- break;
- }
- if (i==8) {
- spin_unlock_irq(sch->lock);
- return 0;
- }
- if (stsch(sch->schid, &sch->schib)) {
- spin_unlock_irq(sch->lock);
- css_schedule_eval(sch->schid);
- return 0;
- }
- sch->lpm = ((sch->schib.pmcw.pim &
- sch->schib.pmcw.pam &
- sch->schib.pmcw.pom)
- | mask) & sch->opm;
-
- if (sch->driver && sch->driver->verify)
- sch->driver->verify(sch);
-
- spin_unlock_irq(sch->lock);
-
- return 0;
-}
-
void chsc_chp_online(struct chp_id chpid)
{
char dbf_txt[15];
+ struct res_acc_data res_data;
sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id);
CIO_TRACE_EVENT(2, dbf_txt);
if (chp_get_status(chpid) != 0) {
+ memset(&res_data, 0, sizeof(struct res_acc_data));
+ res_data.chpid = chpid;
/* Wait until previous actions have settled. */
css_wait_for_slow_path();
- for_each_subchannel_staged(__chp_add, __chp_add_new_sch,
- &chpid);
+ for_each_subchannel_staged(__s390_process_res_acc, NULL,
+ &res_data);
}
}
static void __s390_subchannel_vary_chpid(struct subchannel *sch,
struct chp_id chpid, int on)
{
- int chp, old_lpm;
- int mask;
unsigned long flags;
+ struct res_acc_data res_data;
+ memset(&res_data, 0, sizeof(struct res_acc_data));
+ res_data.chpid = chpid;
spin_lock_irqsave(sch->lock, flags);
- old_lpm = sch->lpm;
- for (chp = 0; chp < 8; chp++) {
- mask = 0x80 >> chp;
- if (!(sch->ssd_info.path_mask & mask))
- continue;
- if (!chp_id_is_equal(&sch->ssd_info.chpid[chp], &chpid))
- continue;
-
- if (on) {
- sch->opm |= mask;
- sch->lpm |= mask;
- if (!old_lpm)
- device_trigger_reprobe(sch);
- else if (sch->driver && sch->driver->verify)
- sch->driver->verify(sch);
- break;
- }
- sch->opm &= ~mask;
- sch->lpm &= ~mask;
- if (check_for_io_on_path(sch, mask)) {
- if (device_is_online(sch))
- /* Path verification is done after killing. */
- device_kill_io(sch);
- else {
- /* Kill and retry internal I/O. */
- terminate_internal_io(sch);
- /* Re-start path verification. */
- if (sch->driver && sch->driver->verify)
- sch->driver->verify(sch);
- }
- } else if (!sch->lpm) {
- if (device_trigger_verify(sch) != 0)
- css_schedule_eval(sch->schid);
- } else if (sch->driver && sch->driver->verify)
- sch->driver->verify(sch);
- break;
- }
+ if (sch->driver && sch->driver->chp_event)
+ sch->driver->chp_event(sch, &res_data,
+ on ? CHP_VARY_ON : CHP_VARY_OFF);
spin_unlock_irqrestore(sch->lock, flags);
}
@@ -937,15 +760,23 @@ out:
int __init chsc_alloc_sei_area(void)
{
+ int ret;
+
sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!sei_page)
+ if (!sei_page) {
CIO_MSG_EVENT(0, "Can't allocate page for processing of "
"chsc machine checks!\n");
- return (sei_page ? 0 : -ENOMEM);
+ return -ENOMEM;
+ }
+ ret = s390_register_crw_handler(CRW_RSC_CSS, chsc_process_crw);
+ if (ret)
+ kfree(sei_page);
+ return ret;
}
void __init chsc_free_sei_area(void)
{
+ s390_unregister_crw_handler(CRW_RSC_CSS);
kfree(sei_page);
}
@@ -1043,3 +874,52 @@ exit:
EXPORT_SYMBOL_GPL(css_general_characteristics);
EXPORT_SYMBOL_GPL(css_chsc_characteristics);
+
+int chsc_sstpc(void *page, unsigned int op, u16 ctrl)
+{
+ struct {
+ struct chsc_header request;
+ unsigned int rsvd0;
+ unsigned int op : 8;
+ unsigned int rsvd1 : 8;
+ unsigned int ctrl : 16;
+ unsigned int rsvd2[5];
+ struct chsc_header response;
+ unsigned int rsvd3[7];
+ } __attribute__ ((packed)) *rr;
+ int rc;
+
+ memset(page, 0, PAGE_SIZE);
+ rr = page;
+ rr->request.length = 0x0020;
+ rr->request.code = 0x0033;
+ rr->op = op;
+ rr->ctrl = ctrl;
+ rc = chsc(rr);
+ if (rc)
+ return -EIO;
+ rc = (rr->response.code == 0x0001) ? 0 : -EIO;
+ return rc;
+}
+
+int chsc_sstpi(void *page, void *result, size_t size)
+{
+ struct {
+ struct chsc_header request;
+ unsigned int rsvd0[3];
+ struct chsc_header response;
+ char data[size];
+ } __attribute__ ((packed)) *rr;
+ int rc;
+
+ memset(page, 0, PAGE_SIZE);
+ rr = page;
+ rr->request.length = 0x0010;
+ rr->request.code = 0x0038;
+ rc = chsc(rr);
+ if (rc)
+ return -EIO;
+ memcpy(result, &rr->data, size);
+ return (rr->response.code == 0x0001) ? 0 : -EIO;
+}
+
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h
index d1f5db1e69b9..2a38b5090228 100644
--- a/drivers/s390/cio/chsc.h
+++ b/drivers/s390/cio/chsc.h
@@ -36,8 +36,6 @@ struct channel_path_desc {
struct channel_path;
-extern void chsc_process_crw(void);
-
struct css_general_char {
u64 : 41;
u32 aif : 1; /* bit 41 */
@@ -51,7 +49,9 @@ struct css_general_char {
u32 qebsm : 1; /* bit 58 */
u32 : 8;
u32 aif_osa : 1; /* bit 67 */
- u32 : 28;
+ u32 : 20;
+ u32 fcx : 1; /* bit 88 */
+ u32 : 7;
}__attribute__((packed));
struct css_chsc_char {
@@ -78,7 +78,6 @@ struct chsc_ssd_info {
extern int chsc_get_ssd_info(struct subchannel_id schid,
struct chsc_ssd_info *ssd);
extern int chsc_determine_css_characteristics(void);
-extern int css_characteristics_avail;
extern int chsc_alloc_sei_area(void);
extern void chsc_free_sei_area(void);
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 82c6a2d45128..52bfcdefa29d 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -2,7 +2,7 @@
* drivers/s390/cio/cio.c
* S/390 common I/O routines -- low level i/o calls
*
- * Copyright (C) IBM Corp. 1999,2006
+ * Copyright IBM Corp. 1999,2008
* Author(s): Ingo Adlung (adlung@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
* Arnd Bergmann (arndb@de.ibm.com)
@@ -24,7 +24,9 @@
#include <asm/ipl.h>
#include <asm/chpid.h>
#include <asm/airq.h>
+#include <asm/isc.h>
#include <asm/cpu.h>
+#include <asm/fcx.h>
#include "cio.h"
#include "css.h"
#include "chsc.h"
@@ -128,7 +130,7 @@ cio_tpi(void)
local_bh_disable();
irq_enter ();
spin_lock(sch->lock);
- memcpy (&sch->schib.scsw, &irb->scsw, sizeof (struct scsw));
+ memcpy(&sch->schib.scsw, &irb->scsw, sizeof(union scsw));
if (sch->driver && sch->driver->irq)
sch->driver->irq(sch);
spin_unlock(sch->lock);
@@ -152,7 +154,7 @@ cio_start_handle_notoper(struct subchannel *sch, __u8 lpm)
CIO_MSG_EVENT(2, "cio_start: 'not oper' status for "
"subchannel 0.%x.%04x!\n", sch->schid.ssid,
sch->schid.sch_no);
- sprintf(dbf_text, "no%s", sch->dev.bus_id);
+ sprintf(dbf_text, "no%s", dev_name(&sch->dev));
CIO_TRACE_EVENT(0, dbf_text);
CIO_HEX_EVENT(0, &sch->schib, sizeof (struct schib));
@@ -167,30 +169,30 @@ cio_start_key (struct subchannel *sch, /* subchannel structure */
{
char dbf_txt[15];
int ccode;
- struct orb *orb;
+ union orb *orb;
CIO_TRACE_EVENT(4, "stIO");
- CIO_TRACE_EVENT(4, sch->dev.bus_id);
+ CIO_TRACE_EVENT(4, dev_name(&sch->dev));
orb = &to_io_private(sch)->orb;
/* sch is always under 2G. */
- orb->intparm = (u32)(addr_t)sch;
- orb->fmt = 1;
+ orb->cmd.intparm = (u32)(addr_t)sch;
+ orb->cmd.fmt = 1;
- orb->pfch = sch->options.prefetch == 0;
- orb->spnd = sch->options.suspend;
- orb->ssic = sch->options.suspend && sch->options.inter;
- orb->lpm = (lpm != 0) ? lpm : sch->lpm;
+ orb->cmd.pfch = sch->options.prefetch == 0;
+ orb->cmd.spnd = sch->options.suspend;
+ orb->cmd.ssic = sch->options.suspend && sch->options.inter;
+ orb->cmd.lpm = (lpm != 0) ? lpm : sch->lpm;
#ifdef CONFIG_64BIT
/*
* for 64 bit we always support 64 bit IDAWs with 4k page size only
*/
- orb->c64 = 1;
- orb->i2k = 0;
+ orb->cmd.c64 = 1;
+ orb->cmd.i2k = 0;
#endif
- orb->key = key >> 4;
+ orb->cmd.key = key >> 4;
/* issue "Start Subchannel" */
- orb->cpa = (__u32) __pa(cpa);
+ orb->cmd.cpa = (__u32) __pa(cpa);
ccode = ssch(sch->schid, orb);
/* process condition code */
@@ -202,7 +204,7 @@ cio_start_key (struct subchannel *sch, /* subchannel structure */
/*
* initialize device status information
*/
- sch->schib.scsw.actl |= SCSW_ACTL_START_PEND;
+ sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND;
return 0;
case 1: /* status pending */
case 2: /* busy */
@@ -228,7 +230,7 @@ cio_resume (struct subchannel *sch)
int ccode;
CIO_TRACE_EVENT (4, "resIO");
- CIO_TRACE_EVENT (4, sch->dev.bus_id);
+ CIO_TRACE_EVENT (4, dev_name(&sch->dev));
ccode = rsch (sch->schid);
@@ -237,7 +239,7 @@ cio_resume (struct subchannel *sch)
switch (ccode) {
case 0:
- sch->schib.scsw.actl |= SCSW_ACTL_RESUME_PEND;
+ sch->schib.scsw.cmd.actl |= SCSW_ACTL_RESUME_PEND;
return 0;
case 1:
return -EBUSY;
@@ -265,7 +267,7 @@ cio_halt(struct subchannel *sch)
return -ENODEV;
CIO_TRACE_EVENT (2, "haltIO");
- CIO_TRACE_EVENT (2, sch->dev.bus_id);
+ CIO_TRACE_EVENT (2, dev_name(&sch->dev));
/*
* Issue "Halt subchannel" and process condition code
@@ -277,7 +279,7 @@ cio_halt(struct subchannel *sch)
switch (ccode) {
case 0:
- sch->schib.scsw.actl |= SCSW_ACTL_HALT_PEND;
+ sch->schib.scsw.cmd.actl |= SCSW_ACTL_HALT_PEND;
return 0;
case 1: /* status pending */
case 2: /* busy */
@@ -300,7 +302,7 @@ cio_clear(struct subchannel *sch)
return -ENODEV;
CIO_TRACE_EVENT (2, "clearIO");
- CIO_TRACE_EVENT (2, sch->dev.bus_id);
+ CIO_TRACE_EVENT (2, dev_name(&sch->dev));
/*
* Issue "Clear subchannel" and process condition code
@@ -312,7 +314,7 @@ cio_clear(struct subchannel *sch)
switch (ccode) {
case 0:
- sch->schib.scsw.actl |= SCSW_ACTL_CLEAR_PEND;
+ sch->schib.scsw.cmd.actl |= SCSW_ACTL_CLEAR_PEND;
return 0;
default: /* device not operational */
return -ENODEV;
@@ -336,7 +338,7 @@ cio_cancel (struct subchannel *sch)
return -ENODEV;
CIO_TRACE_EVENT (2, "cancelIO");
- CIO_TRACE_EVENT (2, sch->dev.bus_id);
+ CIO_TRACE_EVENT (2, dev_name(&sch->dev));
ccode = xsch (sch->schid);
@@ -387,8 +389,10 @@ cio_modify (struct subchannel *sch)
return ret;
}
-/*
- * Enable subchannel.
+/**
+ * cio_enable_subchannel - enable a subchannel.
+ * @sch: subchannel to be enabled
+ * @intparm: interruption parameter to set
*/
int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
{
@@ -398,7 +402,7 @@ int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
int ret;
CIO_TRACE_EVENT (2, "ensch");
- CIO_TRACE_EVENT (2, sch->dev.bus_id);
+ CIO_TRACE_EVENT (2, dev_name(&sch->dev));
if (sch_is_pseudo_sch(sch))
return -EINVAL;
@@ -434,12 +438,13 @@ int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
CIO_TRACE_EVENT (2, dbf_txt);
return ret;
}
+EXPORT_SYMBOL_GPL(cio_enable_subchannel);
-/*
- * Disable subchannel.
+/**
+ * cio_disable_subchannel - disable a subchannel.
+ * @sch: subchannel to disable
*/
-int
-cio_disable_subchannel (struct subchannel *sch)
+int cio_disable_subchannel(struct subchannel *sch)
{
char dbf_txt[15];
int ccode;
@@ -447,7 +452,7 @@ cio_disable_subchannel (struct subchannel *sch)
int ret;
CIO_TRACE_EVENT (2, "dissch");
- CIO_TRACE_EVENT (2, sch->dev.bus_id);
+ CIO_TRACE_EVENT (2, dev_name(&sch->dev));
if (sch_is_pseudo_sch(sch))
return 0;
@@ -455,7 +460,7 @@ cio_disable_subchannel (struct subchannel *sch)
if (ccode == 3) /* Not operational. */
return -ENODEV;
- if (sch->schib.scsw.actl != 0)
+ if (scsw_actl(&sch->schib.scsw) != 0)
/*
* the disable function must not be called while there are
* requests pending for completion !
@@ -484,6 +489,7 @@ cio_disable_subchannel (struct subchannel *sch)
CIO_TRACE_EVENT (2, dbf_txt);
return ret;
}
+EXPORT_SYMBOL_GPL(cio_disable_subchannel);
int cio_create_sch_lock(struct subchannel *sch)
{
@@ -494,27 +500,61 @@ int cio_create_sch_lock(struct subchannel *sch)
return 0;
}
-/*
- * cio_validate_subchannel()
+static int cio_check_devno_blacklisted(struct subchannel *sch)
+{
+ if (is_blacklisted(sch->schid.ssid, sch->schib.pmcw.dev)) {
+ /*
+ * This device must not be known to Linux. So we simply
+ * say that there is no device and return ENODEV.
+ */
+ CIO_MSG_EVENT(6, "Blacklisted device detected "
+ "at devno %04X, subchannel set %x\n",
+ sch->schib.pmcw.dev, sch->schid.ssid);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int cio_validate_io_subchannel(struct subchannel *sch)
+{
+ /* Initialization for io subchannels. */
+ if (!css_sch_is_valid(&sch->schib))
+ return -ENODEV;
+
+ /* Devno is valid. */
+ return cio_check_devno_blacklisted(sch);
+}
+
+static int cio_validate_msg_subchannel(struct subchannel *sch)
+{
+ /* Initialization for message subchannels. */
+ if (!css_sch_is_valid(&sch->schib))
+ return -ENODEV;
+
+ /* Devno is valid. */
+ return cio_check_devno_blacklisted(sch);
+}
+
+/**
+ * cio_validate_subchannel - basic validation of subchannel
+ * @sch: subchannel structure to be filled out
+ * @schid: subchannel id
*
* Find out subchannel type and initialize struct subchannel.
* Return codes:
- * SUBCHANNEL_TYPE_IO for a normal io subchannel
- * SUBCHANNEL_TYPE_CHSC for a chsc subchannel
- * SUBCHANNEL_TYPE_MESSAGE for a messaging subchannel
- * SUBCHANNEL_TYPE_ADM for a adm(?) subchannel
+ * 0 on success
* -ENXIO for non-defined subchannels
- * -ENODEV for subchannels with invalid device number or blacklisted devices
+ * -ENODEV for invalid subchannels or blacklisted devices
+ * -EIO for subchannels in an invalid subchannel set
*/
-int
-cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
+int cio_validate_subchannel(struct subchannel *sch, struct subchannel_id schid)
{
char dbf_txt[15];
int ccode;
int err;
- sprintf (dbf_txt, "valsch%x", schid.sch_no);
- CIO_TRACE_EVENT (4, dbf_txt);
+ sprintf(dbf_txt, "valsch%x", schid.sch_no);
+ CIO_TRACE_EVENT(4, dbf_txt);
/* Nuke all fields. */
memset(sch, 0, sizeof(struct subchannel));
@@ -529,8 +569,10 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
}
mutex_init(&sch->reg_mutex);
/* Set a name for the subchannel */
- snprintf (sch->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x", schid.ssid,
- schid.sch_no);
+ if (cio_is_console(schid))
+ sch->dev.init_name = cio_get_console_sch_name(schid);
+ else
+ dev_set_name(&sch->dev, "0.%x.%04x", schid.ssid, schid.sch_no);
/*
* The first subchannel that is not-operational (ccode==3)
@@ -546,65 +588,21 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
/* Copy subchannel type from path management control word. */
sch->st = sch->schib.pmcw.st;
- /*
- * ... just being curious we check for non I/O subchannels
- */
- if (sch->st != 0) {
- CIO_MSG_EVENT(4, "Subchannel 0.%x.%04x reports "
- "non-I/O subchannel type %04X\n",
- sch->schid.ssid, sch->schid.sch_no, sch->st);
- /* We stop here for non-io subchannels. */
- err = sch->st;
- goto out;
+ switch (sch->st) {
+ case SUBCHANNEL_TYPE_IO:
+ err = cio_validate_io_subchannel(sch);
+ break;
+ case SUBCHANNEL_TYPE_MSG:
+ err = cio_validate_msg_subchannel(sch);
+ break;
+ default:
+ err = 0;
}
-
- /* Initialization for io subchannels. */
- if (!css_sch_is_valid(&sch->schib)) {
- err = -ENODEV;
+ if (err)
goto out;
- }
- /* Devno is valid. */
- if (is_blacklisted (sch->schid.ssid, sch->schib.pmcw.dev)) {
- /*
- * This device must not be known to Linux. So we simply
- * say that there is no device and return ENODEV.
- */
- CIO_MSG_EVENT(6, "Blacklisted device detected "
- "at devno %04X, subchannel set %x\n",
- sch->schib.pmcw.dev, sch->schid.ssid);
- err = -ENODEV;
- goto out;
- }
- if (cio_is_console(sch->schid))
- sch->opm = 0xff;
- else
- sch->opm = chp_get_sch_opm(sch);
- sch->lpm = sch->schib.pmcw.pam & sch->opm;
- sch->isc = 3;
-
- CIO_MSG_EVENT(6, "Detected device %04x on subchannel 0.%x.%04X "
- "- PIM = %02X, PAM = %02X, POM = %02X\n",
- sch->schib.pmcw.dev, sch->schid.ssid,
- sch->schid.sch_no, sch->schib.pmcw.pim,
- sch->schib.pmcw.pam, sch->schib.pmcw.pom);
-
- /*
- * We now have to initially ...
- * ... enable "concurrent sense"
- * ... enable "multipath mode" if more than one
- * CHPID is available. This is done regardless
- * whether multiple paths are available for us.
- */
- sch->schib.pmcw.csense = 1; /* concurrent sense */
- sch->schib.pmcw.ena = 0;
- if ((sch->lpm & (sch->lpm - 1)) != 0)
- sch->schib.pmcw.mp = 1; /* multipath mode */
- /* clean up possible residual cmf stuff */
- sch->schib.pmcw.mme = 0;
- sch->schib.pmcw.mbfc = 0;
- sch->schib.pmcw.mbi = 0;
- sch->schib.mba = 0;
+ CIO_MSG_EVENT(4, "Subchannel 0.%x.%04x reports subchannel type %04X\n",
+ sch->schid.ssid, sch->schid.sch_no, sch->st);
return 0;
out:
if (!cio_is_console(schid))
@@ -645,7 +643,7 @@ do_IRQ (struct pt_regs *regs)
*/
if (tpi_info->adapter_IO == 1 &&
tpi_info->int_type == IO_INTERRUPT_TYPE) {
- do_adapter_IO();
+ do_adapter_IO(tpi_info->isc);
continue;
}
sch = (struct subchannel *)(unsigned long)tpi_info->intparm;
@@ -679,6 +677,7 @@ do_IRQ (struct pt_regs *regs)
#ifdef CONFIG_CCW_CONSOLE
static struct subchannel console_subchannel;
+static char console_sch_name[10] = "0.x.xxxx";
static struct io_subchannel_private console_priv;
static int console_subchannel_in_use;
@@ -704,9 +703,9 @@ void wait_cons_dev(void)
if (!console_subchannel_in_use)
return;
- /* disable all but isc 7 (console device) */
+ /* disable all but the console isc */
__ctl_store (save_cr6, 6, 6);
- cr6 = 0x01000000;
+ cr6 = 1UL << (31 - CONSOLE_ISC);
__ctl_load (cr6, 6, 6);
do {
@@ -714,7 +713,7 @@ void wait_cons_dev(void)
if (!cio_tpi())
cpu_relax();
spin_lock(console_subchannel.lock);
- } while (console_subchannel.schib.scsw.actl != 0);
+ } while (console_subchannel.schib.scsw.cmd.actl != 0);
/*
* restore previous isc value
*/
@@ -788,15 +787,15 @@ cio_probe_console(void)
}
/*
- * enable console I/O-interrupt subclass 7
+ * enable console I/O-interrupt subclass
*/
- ctl_set_bit(6, 24);
- console_subchannel.isc = 7;
- console_subchannel.schib.pmcw.isc = 7;
+ isc_register(CONSOLE_ISC);
+ console_subchannel.schib.pmcw.isc = CONSOLE_ISC;
console_subchannel.schib.pmcw.intparm =
(u32)(addr_t)&console_subchannel;
ret = cio_modify(&console_subchannel);
if (ret) {
+ isc_unregister(CONSOLE_ISC);
console_subchannel_in_use = 0;
return ERR_PTR(ret);
}
@@ -808,7 +807,7 @@ cio_release_console(void)
{
console_subchannel.schib.pmcw.intparm = 0;
cio_modify(&console_subchannel);
- ctl_clear_bit(6, 24);
+ isc_unregister(CONSOLE_ISC);
console_subchannel_in_use = 0;
}
@@ -829,6 +828,12 @@ cio_get_console_subchannel(void)
return &console_subchannel;
}
+const char *cio_get_console_sch_name(struct subchannel_id schid)
+{
+ snprintf(console_sch_name, 10, "0.%x.%04x", schid.ssid, schid.sch_no);
+ return (const char *)console_sch_name;
+}
+
#endif
static int
__disable_subchannel_easy(struct subchannel_id schid, struct schib *schib)
@@ -862,7 +867,7 @@ static void udelay_reset(unsigned long usecs)
}
static int
-__clear_subchannel_easy(struct subchannel_id schid)
+__clear_io_subchannel_easy(struct subchannel_id schid)
{
int retry;
@@ -919,11 +924,19 @@ static int __shutdown_subchannel_easy(struct subchannel_id schid, void *data)
case -ENODEV:
break;
default: /* -EBUSY */
- if (__clear_subchannel_easy(schid))
- break; /* give up... */
+ switch (schib.pmcw.st) {
+ case SUBCHANNEL_TYPE_IO:
+ if (__clear_io_subchannel_easy(schid))
+ goto out; /* give up... */
+ break;
+ default:
+ /* No default clear strategy */
+ break;
+ }
stsch(schid, &schib);
__disable_subchannel_easy(schid, &schib);
}
+out:
return 0;
}
@@ -1066,3 +1079,61 @@ int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo)
iplinfo->is_qdio = schib.pmcw.qf;
return 0;
}
+
+/**
+ * cio_tm_start_key - perform start function
+ * @sch: subchannel on which to perform the start function
+ * @tcw: transport-command word to be started
+ * @lpm: mask of paths to use
+ * @key: storage key to use for storage access
+ *
+ * Start the tcw on the given subchannel. Return zero on success, non-zero
+ * otherwise.
+ */
+int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key)
+{
+ int cc;
+ union orb *orb = &to_io_private(sch)->orb;
+
+ memset(orb, 0, sizeof(union orb));
+ orb->tm.intparm = (u32) (addr_t) sch;
+ orb->tm.key = key >> 4;
+ orb->tm.b = 1;
+ orb->tm.lpm = lpm ? lpm : sch->lpm;
+ orb->tm.tcw = (u32) (addr_t) tcw;
+ cc = ssch(sch->schid, orb);
+ switch (cc) {
+ case 0:
+ return 0;
+ case 1:
+ case 2:
+ return -EBUSY;
+ default:
+ return cio_start_handle_notoper(sch, lpm);
+ }
+}
+
+/**
+ * cio_tm_intrg - perform interrogate function
+ * @sch - subchannel on which to perform the interrogate function
+ *
+ * If the specified subchannel is running in transport-mode, perform the
+ * interrogate function. Return zero on success, non-zero otherwie.
+ */
+int cio_tm_intrg(struct subchannel *sch)
+{
+ int cc;
+
+ if (!to_io_private(sch)->orb.tm.b)
+ return -EINVAL;
+ cc = xsch(sch->schid);
+ switch (cc) {
+ case 0:
+ case 2:
+ return 0;
+ case 1:
+ return -EBUSY;
+ default:
+ return -ENODEV;
+ }
+}
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index 6e933aebe013..d287dd6b843b 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -3,7 +3,10 @@
#include <linux/mutex.h>
#include <linux/device.h>
+#include <linux/mod_devicetable.h>
#include <asm/chpid.h>
+#include <asm/cio.h>
+#include <asm/fcx.h>
#include "chsc.h"
#include "schid.h"
@@ -13,7 +16,7 @@
struct pmcw {
u32 intparm; /* interruption parameter */
u32 qf : 1; /* qdio facility */
- u32 res0 : 1; /* reserved zeros */
+ u32 w : 1;
u32 isc : 3; /* interruption sublass */
u32 res5 : 3; /* reserved zeros */
u32 ena : 1; /* enabled */
@@ -47,7 +50,7 @@ struct pmcw {
*/
struct schib {
struct pmcw pmcw; /* path management control word */
- struct scsw scsw; /* subchannel status word */
+ union scsw scsw; /* subchannel status word */
__u64 mba; /* measurement block address */
__u8 mda[4]; /* model dependent area */
} __attribute__ ((packed,aligned(4)));
@@ -99,8 +102,11 @@ extern int cio_set_options (struct subchannel *, int);
extern int cio_get_options (struct subchannel *);
extern int cio_modify (struct subchannel *);
+int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key);
+int cio_tm_intrg(struct subchannel *sch);
+
int cio_create_sch_lock(struct subchannel *);
-void do_adapter_IO(void);
+void do_adapter_IO(u8 isc);
void do_IRQ(struct pt_regs *);
/* Use with care. */
@@ -111,11 +117,15 @@ extern int cio_is_console(struct subchannel_id);
extern struct subchannel *cio_get_console_subchannel(void);
extern spinlock_t * cio_get_console_lock(void);
extern void *cio_get_console_priv(void);
+extern const char *cio_get_console_sch_name(struct subchannel_id schid);
+extern const char *cio_get_console_cdev_name(struct subchannel *sch);
#else
#define cio_is_console(schid) 0
#define cio_get_console_subchannel() NULL
#define cio_get_console_lock() NULL
#define cio_get_console_priv() NULL
+#define cio_get_console_sch_name(schid) NULL
+#define cio_get_console_cdev_name(sch) NULL
#endif
#endif
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index 2808b6833b9e..77523131a05e 100644
--- a/drivers/s390/cio/cmf.c
+++ b/drivers/s390/cio/cmf.c
@@ -341,12 +341,12 @@ static int cmf_copy_block(struct ccw_device *cdev)
if (stsch(sch->schid, &sch->schib))
return -ENODEV;
- if (sch->schib.scsw.fctl & SCSW_FCTL_START_FUNC) {
+ if (scsw_fctl(&sch->schib.scsw) & SCSW_FCTL_START_FUNC) {
/* Don't copy if a start function is in progress. */
- if ((!(sch->schib.scsw.actl & SCSW_ACTL_SUSPENDED)) &&
- (sch->schib.scsw.actl &
+ if ((!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_SUSPENDED)) &&
+ (scsw_actl(&sch->schib.scsw) &
(SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) &&
- (!(sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS)))
+ (!(scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_SEC_STATUS)))
return -EBUSY;
}
cmb_data = cdev->private->cmb;
@@ -1344,8 +1344,7 @@ static int __init init_cmf(void)
* to basic mode.
*/
if (format == CMF_AUTODETECT) {
- if (!css_characteristics_avail ||
- !css_general_characteristics.ext_mb) {
+ if (!css_general_characteristics.ext_mb) {
format = CMF_BASIC;
} else {
format = CMF_EXTENDED;
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index a76956512b2d..7839415a3aff 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -2,8 +2,7 @@
* drivers/s390/cio/css.c
* driver for channel subsystem
*
- * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
- * IBM Corporation
+ * Copyright IBM Corp. 2002,2008
* Author(s): Arnd Bergmann (arndb@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
*/
@@ -14,7 +13,9 @@
#include <linux/errno.h>
#include <linux/list.h>
#include <linux/reboot.h>
+#include <asm/isc.h>
+#include "../s390mach.h"
#include "css.h"
#include "cio.h"
#include "cio_debug.h"
@@ -30,8 +31,6 @@ static int max_ssid = 0;
struct channel_subsystem *channel_subsystems[__MAX_CSSID + 1];
-int css_characteristics_avail = 0;
-
int
for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *data)
{
@@ -121,25 +120,6 @@ css_alloc_subchannel(struct subchannel_id schid)
kfree(sch);
return ERR_PTR(ret);
}
-
- if (sch->st != SUBCHANNEL_TYPE_IO) {
- /* For now we ignore all non-io subchannels. */
- kfree(sch);
- return ERR_PTR(-EINVAL);
- }
-
- /*
- * Set intparm to subchannel address.
- * This is fine even on 64bit since the subchannel is always located
- * under 2G.
- */
- sch->schib.pmcw.intparm = (u32)(addr_t)sch;
- ret = cio_modify(sch);
- if (ret) {
- kfree(sch->lock);
- kfree(sch);
- return ERR_PTR(ret);
- }
return sch;
}
@@ -177,12 +157,17 @@ static int css_sch_device_register(struct subchannel *sch)
return ret;
}
+/**
+ * css_sch_device_unregister - unregister a subchannel
+ * @sch: subchannel to be unregistered
+ */
void css_sch_device_unregister(struct subchannel *sch)
{
mutex_lock(&sch->reg_mutex);
device_unregister(&sch->dev);
mutex_unlock(&sch->reg_mutex);
}
+EXPORT_SYMBOL_GPL(css_sch_device_unregister);
static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw)
{
@@ -229,6 +214,41 @@ void css_update_ssd_info(struct subchannel *sch)
}
}
+static ssize_t type_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct subchannel *sch = to_subchannel(dev);
+
+ return sprintf(buf, "%01x\n", sch->st);
+}
+
+static DEVICE_ATTR(type, 0444, type_show, NULL);
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct subchannel *sch = to_subchannel(dev);
+
+ return sprintf(buf, "css:t%01X\n", sch->st);
+}
+
+static DEVICE_ATTR(modalias, 0444, modalias_show, NULL);
+
+static struct attribute *subch_attrs[] = {
+ &dev_attr_type.attr,
+ &dev_attr_modalias.attr,
+ NULL,
+};
+
+static struct attribute_group subch_attr_group = {
+ .attrs = subch_attrs,
+};
+
+static struct attribute_group *default_subch_attr_groups[] = {
+ &subch_attr_group,
+ NULL,
+};
+
static int css_register_subchannel(struct subchannel *sch)
{
int ret;
@@ -237,16 +257,17 @@ static int css_register_subchannel(struct subchannel *sch)
sch->dev.parent = &channel_subsystems[0]->device;
sch->dev.bus = &css_bus_type;
sch->dev.release = &css_subchannel_release;
- sch->dev.groups = subch_attr_groups;
+ sch->dev.groups = default_subch_attr_groups;
/*
* We don't want to generate uevents for I/O subchannels that don't
* have a working ccw device behind them since they will be
* unregistered before they can be used anyway, so we delay the add
* uevent until after device recognition was successful.
+ * Note that we suppress the uevent for all subchannel types;
+ * the subchannel driver can decide itself when it wants to inform
+ * userspace of its existence.
*/
- if (!cio_is_console(sch->schid))
- /* Console is special, no need to suppress. */
- sch->dev.uevent_suppress = 1;
+ sch->dev.uevent_suppress = 1;
css_update_ssd_info(sch);
/* make it known to the system */
ret = css_sch_device_register(sch);
@@ -255,10 +276,19 @@ static int css_register_subchannel(struct subchannel *sch)
sch->schid.ssid, sch->schid.sch_no, ret);
return ret;
}
+ if (!sch->driver) {
+ /*
+ * No driver matched. Generate the uevent now so that
+ * a fitting driver module may be loaded based on the
+ * modalias.
+ */
+ sch->dev.uevent_suppress = 0;
+ kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
+ }
return ret;
}
-static int css_probe_device(struct subchannel_id schid)
+int css_probe_device(struct subchannel_id schid)
{
int ret;
struct subchannel *sch;
@@ -301,116 +331,12 @@ int css_sch_is_valid(struct schib *schib)
{
if ((schib->pmcw.st == SUBCHANNEL_TYPE_IO) && !schib->pmcw.dnv)
return 0;
+ if ((schib->pmcw.st == SUBCHANNEL_TYPE_MSG) && !schib->pmcw.w)
+ return 0;
return 1;
}
EXPORT_SYMBOL_GPL(css_sch_is_valid);
-static int css_get_subchannel_status(struct subchannel *sch)
-{
- struct schib schib;
-
- if (stsch(sch->schid, &schib))
- return CIO_GONE;
- if (!css_sch_is_valid(&schib))
- return CIO_GONE;
- if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev))
- return CIO_REVALIDATE;
- if (!sch->lpm)
- return CIO_NO_PATH;
- return CIO_OPER;
-}
-
-static int css_evaluate_known_subchannel(struct subchannel *sch, int slow)
-{
- int event, ret, disc;
- unsigned long flags;
- enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE } action;
-
- spin_lock_irqsave(sch->lock, flags);
- disc = device_is_disconnected(sch);
- if (disc && slow) {
- /* Disconnected devices are evaluated directly only.*/
- spin_unlock_irqrestore(sch->lock, flags);
- return 0;
- }
- /* No interrupt after machine check - kill pending timers. */
- device_kill_pending_timer(sch);
- if (!disc && !slow) {
- /* Non-disconnected devices are evaluated on the slow path. */
- spin_unlock_irqrestore(sch->lock, flags);
- return -EAGAIN;
- }
- event = css_get_subchannel_status(sch);
- CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n",
- sch->schid.ssid, sch->schid.sch_no, event,
- disc ? "disconnected" : "normal",
- slow ? "slow" : "fast");
- /* Analyze subchannel status. */
- action = NONE;
- switch (event) {
- case CIO_NO_PATH:
- if (disc) {
- /* Check if paths have become available. */
- action = REPROBE;
- break;
- }
- /* fall through */
- case CIO_GONE:
- /* Prevent unwanted effects when opening lock. */
- cio_disable_subchannel(sch);
- device_set_disconnected(sch);
- /* Ask driver what to do with device. */
- action = UNREGISTER;
- if (sch->driver && sch->driver->notify) {
- spin_unlock_irqrestore(sch->lock, flags);
- ret = sch->driver->notify(sch, event);
- spin_lock_irqsave(sch->lock, flags);
- if (ret)
- action = NONE;
- }
- break;
- case CIO_REVALIDATE:
- /* Device will be removed, so no notify necessary. */
- if (disc)
- /* Reprobe because immediate unregister might block. */
- action = REPROBE;
- else
- action = UNREGISTER_PROBE;
- break;
- case CIO_OPER:
- if (disc)
- /* Get device operational again. */
- action = REPROBE;
- break;
- }
- /* Perform action. */
- ret = 0;
- switch (action) {
- case UNREGISTER:
- case UNREGISTER_PROBE:
- /* Unregister device (will use subchannel lock). */
- spin_unlock_irqrestore(sch->lock, flags);
- css_sch_device_unregister(sch);
- spin_lock_irqsave(sch->lock, flags);
-
- /* Reset intparm to zeroes. */
- sch->schib.pmcw.intparm = 0;
- cio_modify(sch);
- break;
- case REPROBE:
- device_trigger_reprobe(sch);
- break;
- default:
- break;
- }
- spin_unlock_irqrestore(sch->lock, flags);
- /* Probe if necessary. */
- if (action == UNREGISTER_PROBE)
- ret = css_probe_device(sch->schid);
-
- return ret;
-}
-
static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow)
{
struct schib schib;
@@ -429,6 +355,21 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow)
return css_probe_device(schid);
}
+static int css_evaluate_known_subchannel(struct subchannel *sch, int slow)
+{
+ int ret = 0;
+
+ if (sch->driver) {
+ if (sch->driver->sch_event)
+ ret = sch->driver->sch_event(sch, slow);
+ else
+ dev_dbg(&sch->dev,
+ "Got subchannel machine check but "
+ "no sch_event handler provided.\n");
+ }
+ return ret;
+}
+
static void css_evaluate_subchannel(struct subchannel_id schid, int slow)
{
struct subchannel *sch;
@@ -596,18 +537,29 @@ EXPORT_SYMBOL_GPL(css_schedule_reprobe);
/*
* Called from the machine check handler for subchannel report words.
*/
-void css_process_crw(int rsid1, int rsid2)
+static void css_process_crw(struct crw *crw0, struct crw *crw1, int overflow)
{
struct subchannel_id mchk_schid;
- CIO_CRW_EVENT(2, "source is subchannel %04X, subsystem id %x\n",
- rsid1, rsid2);
+ if (overflow) {
+ css_schedule_eval_all();
+ return;
+ }
+ CIO_CRW_EVENT(2, "CRW0 reports slct=%d, oflw=%d, "
+ "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
+ crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc,
+ crw0->erc, crw0->rsid);
+ if (crw1)
+ CIO_CRW_EVENT(2, "CRW1 reports slct=%d, oflw=%d, "
+ "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n",
+ crw1->slct, crw1->oflw, crw1->chn, crw1->rsc,
+ crw1->anc, crw1->erc, crw1->rsid);
init_subchannel_id(&mchk_schid);
- mchk_schid.sch_no = rsid1;
- if (rsid2 != 0)
- mchk_schid.ssid = (rsid2 >> 8) & 3;
+ mchk_schid.sch_no = crw0->rsid;
+ if (crw1)
+ mchk_schid.ssid = (crw1->rsid >> 8) & 3;
- /*
+ /*
* Since we are always presented with IPI in the CRW, we have to
* use stsch() to find out if the subchannel in question has come
* or gone.
@@ -658,7 +610,7 @@ __init_channel_subsystem(struct subchannel_id schid, void *data)
static void __init
css_generate_pgid(struct channel_subsystem *css, u32 tod_high)
{
- if (css_characteristics_avail && css_general_characteristics.mcss) {
+ if (css_general_characteristics.mcss) {
css->global_pgid.pgid_high.ext_cssid.version = 0x80;
css->global_pgid.pgid_high.ext_cssid.cssid = css->cssid;
} else {
@@ -741,7 +693,7 @@ static int __init setup_css(int nr)
return -ENOMEM;
css->pseudo_subchannel->dev.parent = &css->device;
css->pseudo_subchannel->dev.release = css_subchannel_release;
- sprintf(css->pseudo_subchannel->dev.bus_id, "defunct");
+ dev_set_name(&css->pseudo_subchannel->dev, "defunct");
ret = cio_create_sch_lock(css->pseudo_subchannel);
if (ret) {
kfree(css->pseudo_subchannel);
@@ -750,7 +702,7 @@ static int __init setup_css(int nr)
mutex_init(&css->mutex);
css->valid = 1;
css->cssid = nr;
- sprintf(css->device.bus_id, "css%x", nr);
+ dev_set_name(&css->device, "css%x", nr);
css->device.release = channel_subsystem_release;
tod_high = (u32) (get_clock() >> 32);
css_generate_pgid(css, tod_high);
@@ -795,8 +747,6 @@ init_channel_subsystem (void)
ret = chsc_determine_css_characteristics();
if (ret == -ENOMEM)
goto out; /* No need to continue. */
- if (ret == 0)
- css_characteristics_avail = 1;
ret = chsc_alloc_sei_area();
if (ret)
@@ -806,6 +756,10 @@ init_channel_subsystem (void)
if (ret)
goto out;
+ ret = s390_register_crw_handler(CRW_RSC_SCH, css_process_crw);
+ if (ret)
+ goto out;
+
if ((ret = bus_register(&css_bus_type)))
goto out;
@@ -836,8 +790,7 @@ init_channel_subsystem (void)
ret = device_register(&css->device);
if (ret)
goto out_free_all;
- if (css_characteristics_avail &&
- css_chsc_characteristics.secm) {
+ if (css_chsc_characteristics.secm) {
ret = device_create_file(&css->device,
&dev_attr_cm_enable);
if (ret)
@@ -852,7 +805,8 @@ init_channel_subsystem (void)
goto out_pseudo;
css_init_done = 1;
- ctl_set_bit(6, 28);
+ /* Enable default isc for I/O subchannels. */
+ isc_register(IO_SCH_ISC);
for_each_subchannel(__init_channel_subsystem, NULL);
return 0;
@@ -875,7 +829,7 @@ out_unregister:
i--;
css = channel_subsystems[i];
device_unregister(&css->pseudo_subchannel->dev);
- if (css_characteristics_avail && css_chsc_characteristics.secm)
+ if (css_chsc_characteristics.secm)
device_remove_file(&css->device,
&dev_attr_cm_enable);
device_unregister(&css->device);
@@ -883,6 +837,7 @@ out_unregister:
out_bus:
bus_unregister(&css_bus_type);
out:
+ s390_unregister_crw_handler(CRW_RSC_CSS);
chsc_free_sei_area();
kfree(slow_subchannel_set);
printk(KERN_WARNING"cio: failed to initialize css driver (%d)!\n",
@@ -945,12 +900,25 @@ static void css_shutdown(struct device *dev)
sch->driver->shutdown(sch);
}
+static int css_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct subchannel *sch = to_subchannel(dev);
+ int ret;
+
+ ret = add_uevent_var(env, "ST=%01X", sch->st);
+ if (ret)
+ return ret;
+ ret = add_uevent_var(env, "MODALIAS=css:t%01X", sch->st);
+ return ret;
+}
+
struct bus_type css_bus_type = {
.name = "css",
.match = css_bus_match,
.probe = css_probe,
.remove = css_remove,
.shutdown = css_shutdown,
+ .uevent = css_uevent,
};
/**
@@ -985,4 +953,3 @@ subsys_initcall(init_channel_subsystem);
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(css_bus_type);
-EXPORT_SYMBOL_GPL(css_characteristics_avail);
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h
index e1913518f354..3ec3dc5a1e5e 100644
--- a/drivers/s390/cio/css.h
+++ b/drivers/s390/cio/css.h
@@ -58,20 +58,27 @@ struct pgid {
__u32 tod_high; /* high word TOD clock */
} __attribute__ ((packed));
-/*
- * A css driver handles all subchannels of one type.
- * Currently, we only care about I/O subchannels (type 0), these
- * have a ccw_device connected to them.
- */
struct subchannel;
+/**
+ * struct css_driver - device driver for subchannels
+ * @owner: owning module
+ * @subchannel_type: subchannel type supported by this driver
+ * @drv: embedded device driver structure
+ * @irq: called on interrupts
+ * @chp_event: called for events affecting a channel path
+ * @sch_event: called for events affecting the subchannel
+ * @probe: function called on probe
+ * @remove: function called on remove
+ * @shutdown: called at device shutdown
+ * @name: name of the device driver
+ */
struct css_driver {
struct module *owner;
unsigned int subchannel_type;
struct device_driver drv;
void (*irq)(struct subchannel *);
- int (*notify)(struct subchannel *, int);
- void (*verify)(struct subchannel *);
- void (*termination)(struct subchannel *);
+ int (*chp_event)(struct subchannel *, void *, int);
+ int (*sch_event)(struct subchannel *, int);
int (*probe)(struct subchannel *);
int (*remove)(struct subchannel *);
void (*shutdown)(struct subchannel *);
@@ -89,13 +96,13 @@ extern int css_driver_register(struct css_driver *);
extern void css_driver_unregister(struct css_driver *);
extern void css_sch_device_unregister(struct subchannel *);
-extern struct subchannel * get_subchannel_by_schid(struct subchannel_id);
+extern int css_probe_device(struct subchannel_id);
+extern struct subchannel *get_subchannel_by_schid(struct subchannel_id);
extern int css_init_done;
int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *),
int (*fn_unknown)(struct subchannel_id,
void *), void *data);
extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *);
-extern void css_process_crw(int, int);
extern void css_reiterate_subchannels(void);
void css_update_ssd_info(struct subchannel *sch);
@@ -121,20 +128,6 @@ struct channel_subsystem {
extern struct bus_type css_bus_type;
extern struct channel_subsystem *channel_subsystems[];
-/* Some helper functions for disconnected state. */
-int device_is_disconnected(struct subchannel *);
-void device_set_disconnected(struct subchannel *);
-void device_trigger_reprobe(struct subchannel *);
-
-/* Helper functions for vary on/off. */
-int device_is_online(struct subchannel *);
-void device_kill_io(struct subchannel *);
-void device_set_intretry(struct subchannel *sch);
-int device_trigger_verify(struct subchannel *sch);
-
-/* Machine check helper function. */
-void device_kill_pending_timer(struct subchannel *);
-
/* Helper functions to build lists for the slow path. */
void css_schedule_eval(struct subchannel_id schid);
void css_schedule_eval_all(void);
@@ -145,6 +138,4 @@ int css_sch_is_valid(struct schib *);
extern struct workqueue_struct *slow_path_wq;
void css_wait_for_slow_path(void);
-
-extern struct attribute_group *subch_attr_groups[];
#endif
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index e22813db74a2..52a6566684b7 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -2,8 +2,7 @@
* drivers/s390/cio/device.c
* bus driver for ccw devices
*
- * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
- * IBM Corporation
+ * Copyright IBM Corp. 2002,2008
* Author(s): Arnd Bergmann (arndb@de.ibm.com)
* Cornelia Huck (cornelia.huck@de.ibm.com)
* Martin Schwidefsky (schwidefsky@de.ibm.com)
@@ -23,7 +22,9 @@
#include <asm/cio.h>
#include <asm/param.h> /* HZ */
#include <asm/cmb.h>
+#include <asm/isc.h>
+#include "chp.h"
#include "cio.h"
#include "cio_debug.h"
#include "css.h"
@@ -125,19 +126,17 @@ struct bus_type ccw_bus_type;
static void io_subchannel_irq(struct subchannel *);
static int io_subchannel_probe(struct subchannel *);
static int io_subchannel_remove(struct subchannel *);
-static int io_subchannel_notify(struct subchannel *, int);
-static void io_subchannel_verify(struct subchannel *);
-static void io_subchannel_ioterm(struct subchannel *);
static void io_subchannel_shutdown(struct subchannel *);
+static int io_subchannel_sch_event(struct subchannel *, int);
+static int io_subchannel_chp_event(struct subchannel *, void *, int);
static struct css_driver io_subchannel_driver = {
.owner = THIS_MODULE,
.subchannel_type = SUBCHANNEL_TYPE_IO,
.name = "io_subchannel",
.irq = io_subchannel_irq,
- .notify = io_subchannel_notify,
- .verify = io_subchannel_verify,
- .termination = io_subchannel_ioterm,
+ .sch_event = io_subchannel_sch_event,
+ .chp_event = io_subchannel_chp_event,
.probe = io_subchannel_probe,
.remove = io_subchannel_remove,
.shutdown = io_subchannel_shutdown,
@@ -584,19 +583,14 @@ static DEVICE_ATTR(modalias, 0444, modalias_show, NULL);
static DEVICE_ATTR(online, 0644, online_show, online_store);
static DEVICE_ATTR(availability, 0444, available_show, NULL);
-static struct attribute * subch_attrs[] = {
+static struct attribute *io_subchannel_attrs[] = {
&dev_attr_chpids.attr,
&dev_attr_pimpampom.attr,
NULL,
};
-static struct attribute_group subch_attr_group = {
- .attrs = subch_attrs,
-};
-
-struct attribute_group *subch_attr_groups[] = {
- &subch_attr_group,
- NULL,
+static struct attribute_group io_subchannel_attr_group = {
+ .attrs = io_subchannel_attrs,
};
static struct attribute * ccwdev_attrs[] = {
@@ -790,7 +784,7 @@ static void sch_attach_device(struct subchannel *sch,
sch_set_cdev(sch, cdev);
cdev->private->schid = sch->schid;
cdev->ccwlock = sch->lock;
- device_trigger_reprobe(sch);
+ ccw_device_trigger_reprobe(cdev);
spin_unlock_irq(sch->lock);
}
@@ -1037,7 +1031,6 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
struct ccw_device_private *priv;
sch_set_cdev(sch, cdev);
- sch->driver = &io_subchannel_driver;
cdev->ccwlock = sch->lock;
/* Init private data. */
@@ -1051,8 +1044,11 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
init_timer(&priv->timer);
/* Set an initial name for the device. */
- snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x",
- sch->schid.ssid, sch->schib.pmcw.dev);
+ if (cio_is_console(sch->schid))
+ cdev->dev.init_name = cio_get_console_cdev_name(sch);
+ else
+ dev_set_name(&cdev->dev, "0.%x.%04x",
+ sch->schid.ssid, sch->schib.pmcw.dev);
/* Increase counter of devices currently in recognition. */
atomic_inc(&ccw_device_init_count);
@@ -1117,13 +1113,38 @@ static void io_subchannel_irq(struct subchannel *sch)
cdev = sch_get_cdev(sch);
CIO_TRACE_EVENT(3, "IRQ");
- CIO_TRACE_EVENT(3, sch->dev.bus_id);
+ CIO_TRACE_EVENT(3, dev_name(&sch->dev));
if (cdev)
dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
}
-static int
-io_subchannel_probe (struct subchannel *sch)
+static void io_subchannel_init_fields(struct subchannel *sch)
+{
+ if (cio_is_console(sch->schid))
+ sch->opm = 0xff;
+ else
+ sch->opm = chp_get_sch_opm(sch);
+ sch->lpm = sch->schib.pmcw.pam & sch->opm;
+ sch->isc = cio_is_console(sch->schid) ? CONSOLE_ISC : IO_SCH_ISC;
+
+ CIO_MSG_EVENT(6, "Detected device %04x on subchannel 0.%x.%04X"
+ " - PIM = %02X, PAM = %02X, POM = %02X\n",
+ sch->schib.pmcw.dev, sch->schid.ssid,
+ sch->schid.sch_no, sch->schib.pmcw.pim,
+ sch->schib.pmcw.pam, sch->schib.pmcw.pom);
+ /* Initially set up some fields in the pmcw. */
+ sch->schib.pmcw.ena = 0;
+ sch->schib.pmcw.csense = 1; /* concurrent sense */
+ if ((sch->lpm & (sch->lpm - 1)) != 0)
+ sch->schib.pmcw.mp = 1; /* multipath mode */
+ /* clean up possible residual cmf stuff */
+ sch->schib.pmcw.mme = 0;
+ sch->schib.pmcw.mbfc = 0;
+ sch->schib.pmcw.mbi = 0;
+ sch->schib.mba = 0;
+}
+
+static int io_subchannel_probe(struct subchannel *sch)
{
struct ccw_device *cdev;
int rc;
@@ -1132,11 +1153,21 @@ io_subchannel_probe (struct subchannel *sch)
cdev = sch_get_cdev(sch);
if (cdev) {
+ rc = sysfs_create_group(&sch->dev.kobj,
+ &io_subchannel_attr_group);
+ if (rc)
+ CIO_MSG_EVENT(0, "Failed to create io subchannel "
+ "attributes for subchannel "
+ "0.%x.%04x (rc=%d)\n",
+ sch->schid.ssid, sch->schid.sch_no, rc);
/*
* This subchannel already has an associated ccw_device.
- * Register it and exit. This happens for all early
- * device, e.g. the console.
+ * Throw the delayed uevent for the subchannel, register
+ * the ccw_device and exit. This happens for all early
+ * devices, e.g. the console.
*/
+ sch->dev.uevent_suppress = 0;
+ kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
cdev->dev.groups = ccwdev_attr_groups;
device_initialize(&cdev->dev);
ccw_device_register(cdev);
@@ -1152,17 +1183,24 @@ io_subchannel_probe (struct subchannel *sch)
get_device(&cdev->dev);
return 0;
}
+ io_subchannel_init_fields(sch);
/*
* First check if a fitting device may be found amongst the
* disconnected devices or in the orphanage.
*/
dev_id.devno = sch->schib.pmcw.dev;
dev_id.ssid = sch->schid.ssid;
+ rc = sysfs_create_group(&sch->dev.kobj,
+ &io_subchannel_attr_group);
+ if (rc)
+ return rc;
/* Allocate I/O subchannel private data. */
sch->private = kzalloc(sizeof(struct io_subchannel_private),
GFP_KERNEL | GFP_DMA);
- if (!sch->private)
- return -ENOMEM;
+ if (!sch->private) {
+ rc = -ENOMEM;
+ goto out_err;
+ }
cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL);
if (!cdev)
cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent),
@@ -1181,8 +1219,8 @@ io_subchannel_probe (struct subchannel *sch)
}
cdev = io_subchannel_create_ccwdev(sch);
if (IS_ERR(cdev)) {
- kfree(sch->private);
- return PTR_ERR(cdev);
+ rc = PTR_ERR(cdev);
+ goto out_err;
}
rc = io_subchannel_recog(cdev, sch);
if (rc) {
@@ -1191,9 +1229,12 @@ io_subchannel_probe (struct subchannel *sch)
spin_unlock_irqrestore(sch->lock, flags);
if (cdev->dev.release)
cdev->dev.release(&cdev->dev);
- kfree(sch->private);
+ goto out_err;
}
-
+ return 0;
+out_err:
+ kfree(sch->private);
+ sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
return rc;
}
@@ -1214,6 +1255,7 @@ io_subchannel_remove (struct subchannel *sch)
ccw_device_unregister(cdev);
put_device(&cdev->dev);
kfree(sch->private);
+ sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
return 0;
}
@@ -1224,11 +1266,7 @@ static int io_subchannel_notify(struct subchannel *sch, int event)
cdev = sch_get_cdev(sch);
if (!cdev)
return 0;
- if (!cdev->drv)
- return 0;
- if (!cdev->online)
- return 0;
- return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0;
+ return ccw_device_notify(cdev, event);
}
static void io_subchannel_verify(struct subchannel *sch)
@@ -1240,22 +1278,98 @@ static void io_subchannel_verify(struct subchannel *sch)
dev_fsm_event(cdev, DEV_EVENT_VERIFY);
}
-static void io_subchannel_ioterm(struct subchannel *sch)
+static int check_for_io_on_path(struct subchannel *sch, int mask)
{
- struct ccw_device *cdev;
+ int cc;
- cdev = sch_get_cdev(sch);
- if (!cdev)
- return;
- /* Internal I/O will be retried by the interrupt handler. */
- if (cdev->private->flags.intretry)
+ cc = stsch(sch->schid, &sch->schib);
+ if (cc)
+ return 0;
+ if (scsw_actl(&sch->schib.scsw) && sch->schib.pmcw.lpum == mask)
+ return 1;
+ return 0;
+}
+
+static void terminate_internal_io(struct subchannel *sch,
+ struct ccw_device *cdev)
+{
+ if (cio_clear(sch)) {
+ /* Recheck device in case clear failed. */
+ sch->lpm = 0;
+ if (cdev->online)
+ dev_fsm_event(cdev, DEV_EVENT_VERIFY);
+ else
+ css_schedule_eval(sch->schid);
return;
+ }
cdev->private->state = DEV_STATE_CLEAR_VERIFY;
+ /* Request retry of internal operation. */
+ cdev->private->flags.intretry = 1;
+ /* Call handler. */
if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm,
ERR_PTR(-EIO));
}
+static void io_subchannel_terminate_path(struct subchannel *sch, u8 mask)
+{
+ struct ccw_device *cdev;
+
+ cdev = sch_get_cdev(sch);
+ if (!cdev)
+ return;
+ if (check_for_io_on_path(sch, mask)) {
+ if (cdev->private->state == DEV_STATE_ONLINE)
+ ccw_device_kill_io(cdev);
+ else {
+ terminate_internal_io(sch, cdev);
+ /* Re-start path verification. */
+ dev_fsm_event(cdev, DEV_EVENT_VERIFY);
+ }
+ } else
+ /* trigger path verification. */
+ dev_fsm_event(cdev, DEV_EVENT_VERIFY);
+
+}
+
+static int io_subchannel_chp_event(struct subchannel *sch, void *data,
+ int event)
+{
+ int mask;
+ struct res_acc_data *res_data;
+
+ res_data = data;
+ mask = chp_ssd_get_mask(&sch->ssd_info, res_data);
+ if (!mask)
+ return 0;
+ switch (event) {
+ case CHP_VARY_OFF:
+ sch->opm &= ~mask;
+ sch->lpm &= ~mask;
+ io_subchannel_terminate_path(sch, mask);
+ break;
+ case CHP_VARY_ON:
+ sch->opm |= mask;
+ sch->lpm |= mask;
+ io_subchannel_verify(sch);
+ break;
+ case CHP_OFFLINE:
+ if (stsch(sch->schid, &sch->schib))
+ return -ENXIO;
+ if (!css_sch_is_valid(&sch->schib))
+ return -ENODEV;
+ io_subchannel_terminate_path(sch, mask);
+ break;
+ case CHP_ONLINE:
+ if (stsch(sch->schid, &sch->schib))
+ return -ENXIO;
+ sch->lpm |= mask & sch->opm;
+ io_subchannel_verify(sch);
+ break;
+ }
+ return 0;
+}
+
static void
io_subchannel_shutdown(struct subchannel *sch)
{
@@ -1285,8 +1399,198 @@ io_subchannel_shutdown(struct subchannel *sch)
cio_disable_subchannel(sch);
}
+static int io_subchannel_get_status(struct subchannel *sch)
+{
+ struct schib schib;
+
+ if (stsch(sch->schid, &schib) || !schib.pmcw.dnv)
+ return CIO_GONE;
+ if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev))
+ return CIO_REVALIDATE;
+ if (!sch->lpm)
+ return CIO_NO_PATH;
+ return CIO_OPER;
+}
+
+static int device_is_disconnected(struct ccw_device *cdev)
+{
+ if (!cdev)
+ return 0;
+ return (cdev->private->state == DEV_STATE_DISCONNECTED ||
+ cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID);
+}
+
+static int recovery_check(struct device *dev, void *data)
+{
+ struct ccw_device *cdev = to_ccwdev(dev);
+ int *redo = data;
+
+ spin_lock_irq(cdev->ccwlock);
+ switch (cdev->private->state) {
+ case DEV_STATE_DISCONNECTED:
+ CIO_MSG_EVENT(3, "recovery: trigger 0.%x.%04x\n",
+ cdev->private->dev_id.ssid,
+ cdev->private->dev_id.devno);
+ dev_fsm_event(cdev, DEV_EVENT_VERIFY);
+ *redo = 1;
+ break;
+ case DEV_STATE_DISCONNECTED_SENSE_ID:
+ *redo = 1;
+ break;
+ }
+ spin_unlock_irq(cdev->ccwlock);
+
+ return 0;
+}
+
+static void recovery_work_func(struct work_struct *unused)
+{
+ int redo = 0;
+
+ bus_for_each_dev(&ccw_bus_type, NULL, &redo, recovery_check);
+ if (redo) {
+ spin_lock_irq(&recovery_lock);
+ if (!timer_pending(&recovery_timer)) {
+ if (recovery_phase < ARRAY_SIZE(recovery_delay) - 1)
+ recovery_phase++;
+ mod_timer(&recovery_timer, jiffies +
+ recovery_delay[recovery_phase] * HZ);
+ }
+ spin_unlock_irq(&recovery_lock);
+ } else
+ CIO_MSG_EVENT(4, "recovery: end\n");
+}
+
+static DECLARE_WORK(recovery_work, recovery_work_func);
+
+static void recovery_func(unsigned long data)
+{
+ /*
+ * We can't do our recovery in softirq context and it's not
+ * performance critical, so we schedule it.
+ */
+ schedule_work(&recovery_work);
+}
+
+static void ccw_device_schedule_recovery(void)
+{
+ unsigned long flags;
+
+ CIO_MSG_EVENT(4, "recovery: schedule\n");
+ spin_lock_irqsave(&recovery_lock, flags);
+ if (!timer_pending(&recovery_timer) || (recovery_phase != 0)) {
+ recovery_phase = 0;
+ mod_timer(&recovery_timer, jiffies + recovery_delay[0] * HZ);
+ }
+ spin_unlock_irqrestore(&recovery_lock, flags);
+}
+
+static void device_set_disconnected(struct ccw_device *cdev)
+{
+ if (!cdev)
+ return;
+ ccw_device_set_timeout(cdev, 0);
+ cdev->private->flags.fake_irb = 0;
+ cdev->private->state = DEV_STATE_DISCONNECTED;
+ if (cdev->online)
+ ccw_device_schedule_recovery();
+}
+
+static int io_subchannel_sch_event(struct subchannel *sch, int slow)
+{
+ int event, ret, disc;
+ unsigned long flags;
+ enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE } action;
+ struct ccw_device *cdev;
+
+ spin_lock_irqsave(sch->lock, flags);
+ cdev = sch_get_cdev(sch);
+ disc = device_is_disconnected(cdev);
+ if (disc && slow) {
+ /* Disconnected devices are evaluated directly only.*/
+ spin_unlock_irqrestore(sch->lock, flags);
+ return 0;
+ }
+ /* No interrupt after machine check - kill pending timers. */
+ if (cdev)
+ ccw_device_set_timeout(cdev, 0);
+ if (!disc && !slow) {
+ /* Non-disconnected devices are evaluated on the slow path. */
+ spin_unlock_irqrestore(sch->lock, flags);
+ return -EAGAIN;
+ }
+ event = io_subchannel_get_status(sch);
+ CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n",
+ sch->schid.ssid, sch->schid.sch_no, event,
+ disc ? "disconnected" : "normal",
+ slow ? "slow" : "fast");
+ /* Analyze subchannel status. */
+ action = NONE;
+ switch (event) {
+ case CIO_NO_PATH:
+ if (disc) {
+ /* Check if paths have become available. */
+ action = REPROBE;
+ break;
+ }
+ /* fall through */
+ case CIO_GONE:
+ /* Prevent unwanted effects when opening lock. */
+ cio_disable_subchannel(sch);
+ device_set_disconnected(cdev);
+ /* Ask driver what to do with device. */
+ action = UNREGISTER;
+ spin_unlock_irqrestore(sch->lock, flags);
+ ret = io_subchannel_notify(sch, event);
+ spin_lock_irqsave(sch->lock, flags);
+ if (ret)
+ action = NONE;
+ break;
+ case CIO_REVALIDATE:
+ /* Device will be removed, so no notify necessary. */
+ if (disc)
+ /* Reprobe because immediate unregister might block. */
+ action = REPROBE;
+ else
+ action = UNREGISTER_PROBE;
+ break;
+ case CIO_OPER:
+ if (disc)
+ /* Get device operational again. */
+ action = REPROBE;
+ break;
+ }
+ /* Perform action. */
+ ret = 0;
+ switch (action) {
+ case UNREGISTER:
+ case UNREGISTER_PROBE:
+ /* Unregister device (will use subchannel lock). */
+ spin_unlock_irqrestore(sch->lock, flags);
+ css_sch_device_unregister(sch);
+ spin_lock_irqsave(sch->lock, flags);
+
+ /* Reset intparm to zeroes. */
+ sch->schib.pmcw.intparm = 0;
+ cio_modify(sch);
+ break;
+ case REPROBE:
+ ccw_device_trigger_reprobe(cdev);
+ break;
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(sch->lock, flags);
+ /* Probe if necessary. */
+ if (action == UNREGISTER_PROBE)
+ ret = css_probe_device(sch->schid);
+
+ return ret;
+}
+
#ifdef CONFIG_CCW_CONSOLE
static struct ccw_device console_cdev;
+static char console_cdev_name[10] = "0.x.xxxx";
static struct ccw_device_private console_private;
static int console_cdev_in_use;
@@ -1297,14 +1601,16 @@ spinlock_t * cio_get_console_lock(void)
return &ccw_console_lock;
}
-static int
-ccw_device_console_enable (struct ccw_device *cdev, struct subchannel *sch)
+static int ccw_device_console_enable(struct ccw_device *cdev,
+ struct subchannel *sch)
{
int rc;
/* Attach subchannel private data. */
sch->private = cio_get_console_priv();
memset(sch->private, 0, sizeof(struct io_subchannel_private));
+ io_subchannel_init_fields(sch);
+ sch->driver = &io_subchannel_driver;
/* Initialize the ccw_device structure. */
cdev->dev.parent= &sch->dev;
rc = io_subchannel_recog(cdev, sch);
@@ -1355,6 +1661,14 @@ ccw_device_probe_console(void)
console_cdev.online = 1;
return &console_cdev;
}
+
+
+const char *cio_get_console_cdev_name(struct subchannel *sch)
+{
+ snprintf(console_cdev_name, 10, "0.%x.%04x",
+ sch->schid.ssid, sch->schib.pmcw.dev);
+ return (const char *)console_cdev_name;
+}
#endif
/*
@@ -1367,7 +1681,7 @@ __ccwdev_check_busid(struct device *dev, void *id)
bus_id = id;
- return (strncmp(bus_id, dev->bus_id, BUS_ID_SIZE) == 0);
+ return (strncmp(bus_id, dev_name(dev), BUS_ID_SIZE) == 0);
}
@@ -1515,71 +1829,6 @@ ccw_device_get_subchannel_id(struct ccw_device *cdev)
return sch->schid;
}
-static int recovery_check(struct device *dev, void *data)
-{
- struct ccw_device *cdev = to_ccwdev(dev);
- int *redo = data;
-
- spin_lock_irq(cdev->ccwlock);
- switch (cdev->private->state) {
- case DEV_STATE_DISCONNECTED:
- CIO_MSG_EVENT(4, "recovery: trigger 0.%x.%04x\n",
- cdev->private->dev_id.ssid,
- cdev->private->dev_id.devno);
- dev_fsm_event(cdev, DEV_EVENT_VERIFY);
- *redo = 1;
- break;
- case DEV_STATE_DISCONNECTED_SENSE_ID:
- *redo = 1;
- break;
- }
- spin_unlock_irq(cdev->ccwlock);
-
- return 0;
-}
-
-static void recovery_work_func(struct work_struct *unused)
-{
- int redo = 0;
-
- bus_for_each_dev(&ccw_bus_type, NULL, &redo, recovery_check);
- if (redo) {
- spin_lock_irq(&recovery_lock);
- if (!timer_pending(&recovery_timer)) {
- if (recovery_phase < ARRAY_SIZE(recovery_delay) - 1)
- recovery_phase++;
- mod_timer(&recovery_timer, jiffies +
- recovery_delay[recovery_phase] * HZ);
- }
- spin_unlock_irq(&recovery_lock);
- } else
- CIO_MSG_EVENT(4, "recovery: end\n");
-}
-
-static DECLARE_WORK(recovery_work, recovery_work_func);
-
-static void recovery_func(unsigned long data)
-{
- /*
- * We can't do our recovery in softirq context and it's not
- * performance critical, so we schedule it.
- */
- schedule_work(&recovery_work);
-}
-
-void ccw_device_schedule_recovery(void)
-{
- unsigned long flags;
-
- CIO_MSG_EVENT(4, "recovery: schedule\n");
- spin_lock_irqsave(&recovery_lock, flags);
- if (!timer_pending(&recovery_timer) || (recovery_phase != 0)) {
- recovery_phase = 0;
- mod_timer(&recovery_timer, jiffies + recovery_delay[0] * HZ);
- }
- spin_unlock_irqrestore(&recovery_lock, flags);
-}
-
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(ccw_device_set_online);
EXPORT_SYMBOL(ccw_device_set_offline);
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index cb08092be39f..9800a8335a3f 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -88,8 +88,6 @@ int ccw_device_recognition(struct ccw_device *);
int ccw_device_online(struct ccw_device *);
int ccw_device_offline(struct ccw_device *);
-void ccw_device_schedule_recovery(void);
-
/* Function prototypes for device status and basic sense stuff. */
void ccw_device_accumulate_irb(struct ccw_device *, struct irb *);
void ccw_device_accumulate_basic_sense(struct ccw_device *, struct irb *);
@@ -118,6 +116,11 @@ int ccw_device_call_handler(struct ccw_device *);
int ccw_device_stlck(struct ccw_device *);
+/* Helper function for machine check handling. */
+void ccw_device_trigger_reprobe(struct ccw_device *);
+void ccw_device_kill_io(struct ccw_device *);
+int ccw_device_notify(struct ccw_device *, int);
+
/* qdio needs this. */
void ccw_device_set_timeout(struct ccw_device *, int);
extern struct subchannel_id ccw_device_get_subchannel_id(struct ccw_device *);
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index e268d5a77c12..30d27f5157df 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -2,8 +2,7 @@
* drivers/s390/cio/device_fsm.c
* finite state machine for device handling
*
- * Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
- * IBM Corporation
+ * Copyright IBM Corp. 2002,2008
* Author(s): Cornelia Huck (cornelia.huck@de.ibm.com)
* Martin Schwidefsky (schwidefsky@de.ibm.com)
*/
@@ -27,65 +26,6 @@
static int timeout_log_enabled;
-int
-device_is_online(struct subchannel *sch)
-{
- struct ccw_device *cdev;
-
- cdev = sch_get_cdev(sch);
- if (!cdev)
- return 0;
- return (cdev->private->state == DEV_STATE_ONLINE);
-}
-
-int
-device_is_disconnected(struct subchannel *sch)
-{
- struct ccw_device *cdev;
-
- cdev = sch_get_cdev(sch);
- if (!cdev)
- return 0;
- return (cdev->private->state == DEV_STATE_DISCONNECTED ||
- cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID);
-}
-
-void
-device_set_disconnected(struct subchannel *sch)
-{
- struct ccw_device *cdev;
-
- cdev = sch_get_cdev(sch);
- if (!cdev)
- return;
- ccw_device_set_timeout(cdev, 0);
- cdev->private->flags.fake_irb = 0;
- cdev->private->state = DEV_STATE_DISCONNECTED;
- if (cdev->online)
- ccw_device_schedule_recovery();
-}
-
-void device_set_intretry(struct subchannel *sch)
-{
- struct ccw_device *cdev;
-
- cdev = sch_get_cdev(sch);
- if (!cdev)
- return;
- cdev->private->flags.intretry = 1;
-}
-
-int device_trigger_verify(struct subchannel *sch)
-{
- struct ccw_device *cdev;
-
- cdev = sch_get_cdev(sch);
- if (!cdev || !cdev->online)
- return -EINVAL;
- dev_fsm_event(cdev, DEV_EVENT_VERIFY);
- return 0;
-}
-
static int __init ccw_timeout_log_setup(char *unused)
{
timeout_log_enabled = 1;
@@ -99,31 +39,45 @@ static void ccw_timeout_log(struct ccw_device *cdev)
struct schib schib;
struct subchannel *sch;
struct io_subchannel_private *private;
+ union orb *orb;
int cc;
sch = to_subchannel(cdev->dev.parent);
private = to_io_private(sch);
+ orb = &private->orb;
cc = stsch(sch->schid, &schib);
printk(KERN_WARNING "cio: ccw device timeout occurred at %llx, "
"device information:\n", get_clock());
printk(KERN_WARNING "cio: orb:\n");
print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1,
- &private->orb, sizeof(private->orb), 0);
- printk(KERN_WARNING "cio: ccw device bus id: %s\n", cdev->dev.bus_id);
- printk(KERN_WARNING "cio: subchannel bus id: %s\n", sch->dev.bus_id);
+ orb, sizeof(*orb), 0);
+ printk(KERN_WARNING "cio: ccw device bus id: %s\n",
+ dev_name(&cdev->dev));
+ printk(KERN_WARNING "cio: subchannel bus id: %s\n",
+ dev_name(&sch->dev));
printk(KERN_WARNING "cio: subchannel lpm: %02x, opm: %02x, "
"vpm: %02x\n", sch->lpm, sch->opm, sch->vpm);
- if ((void *)(addr_t)private->orb.cpa == &private->sense_ccw ||
- (void *)(addr_t)private->orb.cpa == cdev->private->iccws)
- printk(KERN_WARNING "cio: last channel program (intern):\n");
- else
- printk(KERN_WARNING "cio: last channel program:\n");
-
- print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1,
- (void *)(addr_t)private->orb.cpa,
- sizeof(struct ccw1), 0);
+ if (orb->tm.b) {
+ printk(KERN_WARNING "cio: orb indicates transport mode\n");
+ printk(KERN_WARNING "cio: last tcw:\n");
+ print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1,
+ (void *)(addr_t)orb->tm.tcw,
+ sizeof(struct tcw), 0);
+ } else {
+ printk(KERN_WARNING "cio: orb indicates command mode\n");
+ if ((void *)(addr_t)orb->cmd.cpa == &private->sense_ccw ||
+ (void *)(addr_t)orb->cmd.cpa == cdev->private->iccws)
+ printk(KERN_WARNING "cio: last channel program "
+ "(intern):\n");
+ else
+ printk(KERN_WARNING "cio: last channel program:\n");
+
+ print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1,
+ (void *)(addr_t)orb->cmd.cpa,
+ sizeof(struct ccw1), 0);
+ }
printk(KERN_WARNING "cio: ccw device state: %d\n",
cdev->private->state);
printk(KERN_WARNING "cio: store subchannel returned: cc=%d\n", cc);
@@ -171,18 +125,6 @@ ccw_device_set_timeout(struct ccw_device *cdev, int expires)
add_timer(&cdev->private->timer);
}
-/* Kill any pending timers after machine check. */
-void
-device_kill_pending_timer(struct subchannel *sch)
-{
- struct ccw_device *cdev;
-
- cdev = sch_get_cdev(sch);
- if (!cdev)
- return;
- ccw_device_set_timeout(cdev, 0);
-}
-
/*
* Cancel running i/o. This is called repeatedly since halt/clear are
* asynchronous operations. We do one try with cio_cancel, two tries
@@ -205,15 +147,18 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
/* Not operational -> done. */
return 0;
/* Stage 1: cancel io. */
- if (!(sch->schib.scsw.actl & SCSW_ACTL_HALT_PEND) &&
- !(sch->schib.scsw.actl & SCSW_ACTL_CLEAR_PEND)) {
- ret = cio_cancel(sch);
- if (ret != -EINVAL)
- return ret;
- /* cancel io unsuccessful. From now on it is asynchronous. */
+ if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_HALT_PEND) &&
+ !(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) {
+ if (!scsw_is_tm(&sch->schib.scsw)) {
+ ret = cio_cancel(sch);
+ if (ret != -EINVAL)
+ return ret;
+ }
+ /* cancel io unsuccessful or not applicable (transport mode).
+ * Continue with asynchronous instructions. */
cdev->private->iretry = 3; /* 3 halt retries. */
}
- if (!(sch->schib.scsw.actl & SCSW_ACTL_CLEAR_PEND)) {
+ if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) {
/* Stage 2: halt io. */
if (cdev->private->iretry) {
cdev->private->iretry--;
@@ -388,34 +333,30 @@ ccw_device_sense_id_done(struct ccw_device *cdev, int err)
}
}
+int ccw_device_notify(struct ccw_device *cdev, int event)
+{
+ if (!cdev->drv)
+ return 0;
+ if (!cdev->online)
+ return 0;
+ return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0;
+}
+
static void
ccw_device_oper_notify(struct work_struct *work)
{
struct ccw_device_private *priv;
struct ccw_device *cdev;
- struct subchannel *sch;
int ret;
- unsigned long flags;
priv = container_of(work, struct ccw_device_private, kick_work);
cdev = priv->cdev;
- spin_lock_irqsave(cdev->ccwlock, flags);
- sch = to_subchannel(cdev->dev.parent);
- if (sch->driver && sch->driver->notify) {
- spin_unlock_irqrestore(cdev->ccwlock, flags);
- ret = sch->driver->notify(sch, CIO_OPER);
- spin_lock_irqsave(cdev->ccwlock, flags);
- } else
- ret = 0;
+ ret = ccw_device_notify(cdev, CIO_OPER);
if (ret) {
/* Reenable channel measurements, if needed. */
- spin_unlock_irqrestore(cdev->ccwlock, flags);
cmf_reenable(cdev);
- spin_lock_irqsave(cdev->ccwlock, flags);
wake_up(&cdev->private->wait_q);
- }
- spin_unlock_irqrestore(cdev->ccwlock, flags);
- if (!ret)
+ } else
/* Driver doesn't want device back. */
ccw_device_do_unreg_rereg(work);
}
@@ -621,10 +562,11 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
/* Deliver fake irb to device driver, if needed. */
if (cdev->private->flags.fake_irb) {
memset(&cdev->private->irb, 0, sizeof(struct irb));
- cdev->private->irb.scsw.cc = 1;
- cdev->private->irb.scsw.fctl = SCSW_FCTL_START_FUNC;
- cdev->private->irb.scsw.actl = SCSW_ACTL_START_PEND;
- cdev->private->irb.scsw.stctl = SCSW_STCTL_STATUS_PEND;
+ cdev->private->irb.scsw.cmd.cc = 1;
+ cdev->private->irb.scsw.cmd.fctl = SCSW_FCTL_START_FUNC;
+ cdev->private->irb.scsw.cmd.actl = SCSW_ACTL_START_PEND;
+ cdev->private->irb.scsw.cmd.stctl =
+ SCSW_STCTL_STATUS_PEND;
cdev->private->flags.fake_irb = 0;
if (cdev->handler)
cdev->handler(cdev, cdev->private->intparm,
@@ -718,13 +660,10 @@ ccw_device_offline(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent);
if (stsch(sch->schid, &sch->schib) || !sch->schib.pmcw.dnv)
return -ENODEV;
- if (cdev->private->state != DEV_STATE_ONLINE) {
- if (sch->schib.scsw.actl != 0)
- return -EBUSY;
- return -EINVAL;
- }
- if (sch->schib.scsw.actl != 0)
+ if (scsw_actl(&sch->schib.scsw) != 0)
return -EBUSY;
+ if (cdev->private->state != DEV_STATE_ONLINE)
+ return -EINVAL;
/* Are we doing path grouping? */
if (!cdev->private->options.pgroup) {
/* No, set state offline immediately. */
@@ -799,9 +738,9 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
*/
stsch(sch->schid, &sch->schib);
- if (sch->schib.scsw.actl != 0 ||
- (sch->schib.scsw.stctl & SCSW_STCTL_STATUS_PEND) ||
- (cdev->private->irb.scsw.stctl & SCSW_STCTL_STATUS_PEND)) {
+ if (scsw_actl(&sch->schib.scsw) != 0 ||
+ (scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_STATUS_PEND) ||
+ (scsw_stctl(&cdev->private->irb.scsw) & SCSW_STCTL_STATUS_PEND)) {
/*
* No final status yet or final status not yet delivered
* to the device driver. Can't do path verfication now,
@@ -823,13 +762,13 @@ static void
ccw_device_irq(struct ccw_device *cdev, enum dev_event dev_event)
{
struct irb *irb;
+ int is_cmd;
irb = (struct irb *) __LC_IRB;
+ is_cmd = !scsw_is_tm(&irb->scsw);
/* Check for unsolicited interrupt. */
- if ((irb->scsw.stctl ==
- (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS))
- && (!irb->scsw.cc)) {
- if ((irb->scsw.dstat & DEV_STAT_UNIT_CHECK) &&
+ if (!scsw_is_solicited(&irb->scsw)) {
+ if (is_cmd && (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
!irb->esw.esw0.erw.cons) {
/* Unit check but no sense data. Need basic sense. */
if (ccw_device_do_sense(cdev, irb) != 0)
@@ -848,7 +787,7 @@ call_handler_unsol:
}
/* Accumulate status and find out if a basic sense is needed. */
ccw_device_accumulate_irb(cdev, irb);
- if (cdev->private->flags.dosense) {
+ if (is_cmd && cdev->private->flags.dosense) {
if (ccw_device_do_sense(cdev, irb) == 0) {
cdev->private->state = DEV_STATE_W4SENSE;
}
@@ -892,9 +831,9 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
irb = (struct irb *) __LC_IRB;
/* Check for unsolicited interrupt. */
- if (irb->scsw.stctl ==
- (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
- if (irb->scsw.cc == 1)
+ if (scsw_stctl(&irb->scsw) ==
+ (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
+ if (scsw_cc(&irb->scsw) == 1)
/* Basic sense hasn't started. Try again. */
ccw_device_do_sense(cdev, irb);
else {
@@ -912,7 +851,8 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
* only deliver the halt/clear interrupt to the device driver as if it
* had killed the original request.
*/
- if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
+ if (scsw_fctl(&irb->scsw) &
+ (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
/* Retry Basic Sense if requested. */
if (cdev->private->flags.intretry) {
cdev->private->flags.intretry = 0;
@@ -986,12 +926,10 @@ ccw_device_killing_timeout(struct ccw_device *cdev, enum dev_event dev_event)
ERR_PTR(-EIO));
}
-void device_kill_io(struct subchannel *sch)
+void ccw_device_kill_io(struct ccw_device *cdev)
{
int ret;
- struct ccw_device *cdev;
- cdev = sch_get_cdev(sch);
ret = ccw_device_cancel_halt_clear(cdev);
if (ret == -EBUSY) {
ccw_device_set_timeout(cdev, 3*HZ);
@@ -1021,9 +959,9 @@ ccw_device_stlck_done(struct ccw_device *cdev, enum dev_event dev_event)
case DEV_EVENT_INTERRUPT:
irb = (struct irb *) __LC_IRB;
/* Check for unsolicited interrupt. */
- if ((irb->scsw.stctl ==
+ if ((scsw_stctl(&irb->scsw) ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) &&
- (!irb->scsw.cc))
+ (!scsw_cc(&irb->scsw)))
/* FIXME: we should restart stlck here, but this
* is extremely unlikely ... */
goto out_wakeup;
@@ -1055,17 +993,14 @@ ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event)
ccw_device_sense_id_start(cdev);
}
-void
-device_trigger_reprobe(struct subchannel *sch)
+void ccw_device_trigger_reprobe(struct ccw_device *cdev)
{
- struct ccw_device *cdev;
+ struct subchannel *sch;
- cdev = sch_get_cdev(sch);
- if (!cdev)
- return;
if (cdev->private->state != DEV_STATE_DISCONNECTED)
return;
+ sch = to_subchannel(cdev->dev.parent);
/* Update some values. */
if (stsch(sch->schid, &sch->schib))
return;
@@ -1081,7 +1016,6 @@ device_trigger_reprobe(struct subchannel *sch)
sch->schib.pmcw.ena = 0;
if ((sch->lpm & (sch->lpm - 1)) != 0)
sch->schib.pmcw.mp = 1;
- sch->schib.pmcw.intparm = (u32)(addr_t)sch;
/* We should also udate ssd info, but this has to wait. */
/* Check if this is another device which appeared on the same sch. */
if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
diff --git a/drivers/s390/cio/device_id.c b/drivers/s390/cio/device_id.c
index cba7020517ed..1bdaa614e34f 100644
--- a/drivers/s390/cio/device_id.c
+++ b/drivers/s390/cio/device_id.c
@@ -196,7 +196,7 @@ ccw_device_check_sense_id(struct ccw_device *cdev)
irb = &cdev->private->irb;
/* Check the error cases. */
- if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
+ if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
/* Retry Sense ID if requested. */
if (cdev->private->flags.intretry) {
cdev->private->flags.intretry = 0;
@@ -234,10 +234,10 @@ ccw_device_check_sense_id(struct ccw_device *cdev)
irb->ecw[6], irb->ecw[7]);
return -EAGAIN;
}
- if (irb->scsw.cc == 3) {
+ if (irb->scsw.cmd.cc == 3) {
u8 lpm;
- lpm = to_io_private(sch)->orb.lpm;
+ lpm = to_io_private(sch)->orb.cmd.lpm;
if ((lpm & sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0)
CIO_MSG_EVENT(4, "SenseID : path %02X for device %04x "
"on subchannel 0.%x.%04x is "
@@ -248,9 +248,9 @@ ccw_device_check_sense_id(struct ccw_device *cdev)
}
/* Did we get a proper answer ? */
- if (irb->scsw.cc == 0 && cdev->private->senseid.cu_type != 0xFFFF &&
+ if (irb->scsw.cmd.cc == 0 && cdev->private->senseid.cu_type != 0xFFFF &&
cdev->private->senseid.reserved == 0xFF) {
- if (irb->scsw.count < sizeof(struct senseid) - 8)
+ if (irb->scsw.cmd.count < sizeof(struct senseid) - 8)
cdev->private->flags.esid = 1;
return 0; /* Success */
}
@@ -260,7 +260,7 @@ ccw_device_check_sense_id(struct ccw_device *cdev)
"subchannel 0.%x.%04x returns status %02X%02X\n",
cdev->private->dev_id.devno, sch->schid.ssid,
sch->schid.sch_no,
- irb->scsw.dstat, irb->scsw.cstat);
+ irb->scsw.cmd.dstat, irb->scsw.cmd.cstat);
return -EAGAIN;
}
@@ -277,9 +277,9 @@ ccw_device_sense_id_irq(struct ccw_device *cdev, enum dev_event dev_event)
sch = to_subchannel(cdev->dev.parent);
irb = (struct irb *) __LC_IRB;
/* Retry sense id, if needed. */
- if (irb->scsw.stctl ==
+ if (irb->scsw.cmd.stctl ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
- if ((irb->scsw.cc == 1) || !irb->scsw.actl) {
+ if ((irb->scsw.cmd.cc == 1) || !irb->scsw.cmd.actl) {
ret = __ccw_device_sense_id_start(cdev);
if (ret && ret != -EBUSY)
ccw_device_sense_id_done(cdev, ret);
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index f308ad55a6d5..eabcc42d63df 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -17,6 +17,7 @@
#include <asm/ccwdev.h>
#include <asm/idals.h>
#include <asm/chpid.h>
+#include <asm/fcx.h>
#include "cio.h"
#include "cio_debug.h"
@@ -179,8 +180,8 @@ int ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa,
return -EBUSY;
}
if (cdev->private->state != DEV_STATE_ONLINE ||
- ((sch->schib.scsw.stctl & SCSW_STCTL_PRIM_STATUS) &&
- !(sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS)) ||
+ ((sch->schib.scsw.cmd.stctl & SCSW_STCTL_PRIM_STATUS) &&
+ !(sch->schib.scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS)) ||
cdev->private->flags.doverify)
return -EBUSY;
ret = cio_set_options (sch, flags);
@@ -379,7 +380,7 @@ int ccw_device_resume(struct ccw_device *cdev)
if (cdev->private->state == DEV_STATE_NOT_OPER)
return -ENODEV;
if (cdev->private->state != DEV_STATE_ONLINE ||
- !(sch->schib.scsw.actl & SCSW_ACTL_SUSPENDED))
+ !(sch->schib.scsw.cmd.actl & SCSW_ACTL_SUSPENDED))
return -EINVAL;
return cio_resume(sch);
}
@@ -404,7 +405,7 @@ ccw_device_call_handler(struct ccw_device *cdev)
* - fast notification was requested (primary status)
* - unsolicited interrupts
*/
- stctl = cdev->private->irb.scsw.stctl;
+ stctl = scsw_stctl(&cdev->private->irb.scsw);
ending_status = (stctl & SCSW_STCTL_SEC_STATUS) ||
(stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)) ||
(stctl == SCSW_STCTL_STATUS_PEND);
@@ -497,7 +498,7 @@ ccw_device_stlck(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent);
CIO_TRACE_EVENT(2, "stl lock");
- CIO_TRACE_EVENT(2, cdev->dev.bus_id);
+ CIO_TRACE_EVENT(2, dev_name(&cdev->dev));
buf = kmalloc(32*sizeof(char), GFP_DMA|GFP_KERNEL);
if (!buf)
@@ -528,14 +529,15 @@ ccw_device_stlck(struct ccw_device *cdev)
cio_disable_subchannel(sch); //FIXME: return code?
goto out_unlock;
}
- cdev->private->irb.scsw.actl |= SCSW_ACTL_START_PEND;
+ cdev->private->irb.scsw.cmd.actl |= SCSW_ACTL_START_PEND;
spin_unlock_irqrestore(sch->lock, flags);
- wait_event(cdev->private->wait_q, cdev->private->irb.scsw.actl == 0);
+ wait_event(cdev->private->wait_q,
+ cdev->private->irb.scsw.cmd.actl == 0);
spin_lock_irqsave(sch->lock, flags);
cio_disable_subchannel(sch); //FIXME: return code?
- if ((cdev->private->irb.scsw.dstat !=
+ if ((cdev->private->irb.scsw.cmd.dstat !=
(DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ||
- (cdev->private->irb.scsw.cstat != 0))
+ (cdev->private->irb.scsw.cmd.cstat != 0))
ret = -EIO;
/* Clear irb. */
memset(&cdev->private->irb, 0, sizeof(struct irb));
@@ -568,6 +570,122 @@ void ccw_device_get_id(struct ccw_device *cdev, struct ccw_dev_id *dev_id)
}
EXPORT_SYMBOL(ccw_device_get_id);
+/**
+ * ccw_device_tm_start_key - perform start function
+ * @cdev: ccw device on which to perform the start function
+ * @tcw: transport-command word to be started
+ * @intparm: user defined parameter to be passed to the interrupt handler
+ * @lpm: mask of paths to use
+ * @key: storage key to use for storage access
+ *
+ * Start the tcw on the given ccw device. Return zero on success, non-zero
+ * otherwise.
+ */
+int ccw_device_tm_start_key(struct ccw_device *cdev, struct tcw *tcw,
+ unsigned long intparm, u8 lpm, u8 key)
+{
+ struct subchannel *sch;
+ int rc;
+
+ sch = to_subchannel(cdev->dev.parent);
+ if (cdev->private->state != DEV_STATE_ONLINE)
+ return -EIO;
+ /* Adjust requested path mask to excluded varied off paths. */
+ if (lpm) {
+ lpm &= sch->opm;
+ if (lpm == 0)
+ return -EACCES;
+ }
+ rc = cio_tm_start_key(sch, tcw, lpm, key);
+ if (rc == 0)
+ cdev->private->intparm = intparm;
+ return rc;
+}
+EXPORT_SYMBOL(ccw_device_tm_start_key);
+
+/**
+ * ccw_device_tm_start_timeout_key - perform start function
+ * @cdev: ccw device on which to perform the start function
+ * @tcw: transport-command word to be started
+ * @intparm: user defined parameter to be passed to the interrupt handler
+ * @lpm: mask of paths to use
+ * @key: storage key to use for storage access
+ * @expires: time span in jiffies after which to abort request
+ *
+ * Start the tcw on the given ccw device. Return zero on success, non-zero
+ * otherwise.
+ */
+int ccw_device_tm_start_timeout_key(struct ccw_device *cdev, struct tcw *tcw,
+ unsigned long intparm, u8 lpm, u8 key,
+ int expires)
+{
+ int ret;
+
+ ccw_device_set_timeout(cdev, expires);
+ ret = ccw_device_tm_start_key(cdev, tcw, intparm, lpm, key);
+ if (ret != 0)
+ ccw_device_set_timeout(cdev, 0);
+ return ret;
+}
+EXPORT_SYMBOL(ccw_device_tm_start_timeout_key);
+
+/**
+ * ccw_device_tm_start - perform start function
+ * @cdev: ccw device on which to perform the start function
+ * @tcw: transport-command word to be started
+ * @intparm: user defined parameter to be passed to the interrupt handler
+ * @lpm: mask of paths to use
+ *
+ * Start the tcw on the given ccw device. Return zero on success, non-zero
+ * otherwise.
+ */
+int ccw_device_tm_start(struct ccw_device *cdev, struct tcw *tcw,
+ unsigned long intparm, u8 lpm)
+{
+ return ccw_device_tm_start_key(cdev, tcw, intparm, lpm,
+ PAGE_DEFAULT_KEY);
+}
+EXPORT_SYMBOL(ccw_device_tm_start);
+
+/**
+ * ccw_device_tm_start_timeout - perform start function
+ * @cdev: ccw device on which to perform the start function
+ * @tcw: transport-command word to be started
+ * @intparm: user defined parameter to be passed to the interrupt handler
+ * @lpm: mask of paths to use
+ * @expires: time span in jiffies after which to abort request
+ *
+ * Start the tcw on the given ccw device. Return zero on success, non-zero
+ * otherwise.
+ */
+int ccw_device_tm_start_timeout(struct ccw_device *cdev, struct tcw *tcw,
+ unsigned long intparm, u8 lpm, int expires)
+{
+ return ccw_device_tm_start_timeout_key(cdev, tcw, intparm, lpm,
+ PAGE_DEFAULT_KEY, expires);
+}
+EXPORT_SYMBOL(ccw_device_tm_start_timeout);
+
+/**
+ * ccw_device_tm_intrg - perform interrogate function
+ * @cdev: ccw device on which to perform the interrogate function
+ *
+ * Perform an interrogate function on the given ccw device. Return zero on
+ * success, non-zero otherwise.
+ */
+int ccw_device_tm_intrg(struct ccw_device *cdev)
+{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
+
+ if (cdev->private->state != DEV_STATE_ONLINE)
+ return -EIO;
+ if (!scsw_is_tm(&sch->schib.scsw) ||
+ !(scsw_actl(&sch->schib.scsw) | SCSW_ACTL_START_PEND))
+ return -EINVAL;
+ return cio_tm_intrg(sch);
+}
+EXPORT_SYMBOL(ccw_device_tm_intrg);
+
// FIXME: these have to go:
int
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c
index 5cf7be008e98..86bc94eb607f 100644
--- a/drivers/s390/cio/device_pgid.c
+++ b/drivers/s390/cio/device_pgid.c
@@ -28,13 +28,13 @@
* Helper function called from interrupt context to decide whether an
* operation should be tried again.
*/
-static int __ccw_device_should_retry(struct scsw *scsw)
+static int __ccw_device_should_retry(union scsw *scsw)
{
/* CC is only valid if start function bit is set. */
- if ((scsw->fctl & SCSW_FCTL_START_FUNC) && scsw->cc == 1)
+ if ((scsw->cmd.fctl & SCSW_FCTL_START_FUNC) && scsw->cmd.cc == 1)
return 1;
/* No more activity. For sense and set PGID we stubbornly try again. */
- if (!scsw->actl)
+ if (!scsw->cmd.actl)
return 1;
return 0;
}
@@ -125,7 +125,7 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent);
irb = &cdev->private->irb;
- if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
+ if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
/* Retry Sense PGID if requested. */
if (cdev->private->flags.intretry) {
cdev->private->flags.intretry = 0;
@@ -155,10 +155,10 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev)
irb->ecw[6], irb->ecw[7]);
return -EAGAIN;
}
- if (irb->scsw.cc == 3) {
+ if (irb->scsw.cmd.cc == 3) {
u8 lpm;
- lpm = to_io_private(sch)->orb.lpm;
+ lpm = to_io_private(sch)->orb.cmd.lpm;
CIO_MSG_EVENT(3, "SNID - Device %04x on Subchannel 0.%x.%04x,"
" lpm %02X, became 'not operational'\n",
cdev->private->dev_id.devno, sch->schid.ssid,
@@ -188,7 +188,7 @@ ccw_device_sense_pgid_irq(struct ccw_device *cdev, enum dev_event dev_event)
irb = (struct irb *) __LC_IRB;
- if (irb->scsw.stctl ==
+ if (irb->scsw.cmd.stctl ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
if (__ccw_device_should_retry(&irb->scsw)) {
ret = __ccw_device_sense_pgid_start(cdev);
@@ -331,7 +331,7 @@ __ccw_device_check_pgid(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent);
irb = &cdev->private->irb;
- if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
+ if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
/* Retry Set PGID if requested. */
if (cdev->private->flags.intretry) {
cdev->private->flags.intretry = 0;
@@ -355,7 +355,7 @@ __ccw_device_check_pgid(struct ccw_device *cdev)
irb->ecw[6], irb->ecw[7]);
return -EAGAIN;
}
- if (irb->scsw.cc == 3) {
+ if (irb->scsw.cmd.cc == 3) {
CIO_MSG_EVENT(3, "SPID - Device %04x on Subchannel 0.%x.%04x,"
" lpm %02X, became 'not operational'\n",
cdev->private->dev_id.devno, sch->schid.ssid,
@@ -376,7 +376,7 @@ static int __ccw_device_check_nop(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent);
irb = &cdev->private->irb;
- if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
+ if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
/* Retry NOP if requested. */
if (cdev->private->flags.intretry) {
cdev->private->flags.intretry = 0;
@@ -384,7 +384,7 @@ static int __ccw_device_check_nop(struct ccw_device *cdev)
}
return -ETIME;
}
- if (irb->scsw.cc == 3) {
+ if (irb->scsw.cmd.cc == 3) {
CIO_MSG_EVENT(3, "NOP - Device %04x on Subchannel 0.%x.%04x,"
" lpm %02X, became 'not operational'\n",
cdev->private->dev_id.devno, sch->schid.ssid,
@@ -438,7 +438,7 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
irb = (struct irb *) __LC_IRB;
- if (irb->scsw.stctl ==
+ if (irb->scsw.cmd.stctl ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
if (__ccw_device_should_retry(&irb->scsw))
__ccw_device_verify_start(cdev);
@@ -544,7 +544,7 @@ ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event)
irb = (struct irb *) __LC_IRB;
- if (irb->scsw.stctl ==
+ if (irb->scsw.cmd.stctl ==
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
if (__ccw_device_should_retry(&irb->scsw))
__ccw_device_disband_start(cdev);
diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c
index 4a38993000f2..1b03c5423be2 100644
--- a/drivers/s390/cio/device_status.c
+++ b/drivers/s390/cio/device_status.c
@@ -29,9 +29,11 @@
static void
ccw_device_msg_control_check(struct ccw_device *cdev, struct irb *irb)
{
- if (!(irb->scsw.cstat & (SCHN_STAT_CHN_DATA_CHK |
- SCHN_STAT_CHN_CTRL_CHK |
- SCHN_STAT_INTF_CTRL_CHK)))
+ char dbf_text[15];
+
+ if (!scsw_is_valid_cstat(&irb->scsw) ||
+ !(scsw_cstat(&irb->scsw) & (SCHN_STAT_CHN_DATA_CHK |
+ SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK)))
return;
CIO_MSG_EVENT(0, "Channel-Check or Interface-Control-Check "
"received"
@@ -39,15 +41,10 @@ ccw_device_msg_control_check(struct ccw_device *cdev, struct irb *irb)
": %02X sch_stat : %02X\n",
cdev->private->dev_id.devno, cdev->private->schid.ssid,
cdev->private->schid.sch_no,
- irb->scsw.dstat, irb->scsw.cstat);
-
- if (irb->scsw.cc != 3) {
- char dbf_text[15];
-
- sprintf(dbf_text, "chk%x", cdev->private->schid.sch_no);
- CIO_TRACE_EVENT(0, dbf_text);
- CIO_HEX_EVENT(0, irb, sizeof (struct irb));
- }
+ scsw_dstat(&irb->scsw), scsw_cstat(&irb->scsw));
+ sprintf(dbf_text, "chk%x", cdev->private->schid.sch_no);
+ CIO_TRACE_EVENT(0, dbf_text);
+ CIO_HEX_EVENT(0, irb, sizeof(struct irb));
}
/*
@@ -81,12 +78,12 @@ ccw_device_accumulate_ecw(struct ccw_device *cdev, struct irb *irb)
* are condition that have to be met for the extended control
* bit to have meaning. Sick.
*/
- cdev->private->irb.scsw.ectl = 0;
- if ((irb->scsw.stctl & SCSW_STCTL_ALERT_STATUS) &&
- !(irb->scsw.stctl & SCSW_STCTL_INTER_STATUS))
- cdev->private->irb.scsw.ectl = irb->scsw.ectl;
+ cdev->private->irb.scsw.cmd.ectl = 0;
+ if ((irb->scsw.cmd.stctl & SCSW_STCTL_ALERT_STATUS) &&
+ !(irb->scsw.cmd.stctl & SCSW_STCTL_INTER_STATUS))
+ cdev->private->irb.scsw.cmd.ectl = irb->scsw.cmd.ectl;
/* Check if extended control word is valid. */
- if (!cdev->private->irb.scsw.ectl)
+ if (!cdev->private->irb.scsw.cmd.ectl)
return;
/* Copy concurrent sense / model dependent information. */
memcpy (&cdev->private->irb.ecw, irb->ecw, sizeof (irb->ecw));
@@ -98,11 +95,12 @@ ccw_device_accumulate_ecw(struct ccw_device *cdev, struct irb *irb)
static int
ccw_device_accumulate_esw_valid(struct irb *irb)
{
- if (!irb->scsw.eswf && irb->scsw.stctl == SCSW_STCTL_STATUS_PEND)
+ if (!irb->scsw.cmd.eswf &&
+ (irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND))
return 0;
- if (irb->scsw.stctl ==
- (SCSW_STCTL_INTER_STATUS|SCSW_STCTL_STATUS_PEND) &&
- !(irb->scsw.actl & SCSW_ACTL_SUSPENDED))
+ if (irb->scsw.cmd.stctl ==
+ (SCSW_STCTL_INTER_STATUS|SCSW_STCTL_STATUS_PEND) &&
+ !(irb->scsw.cmd.actl & SCSW_ACTL_SUSPENDED))
return 0;
return 1;
}
@@ -125,7 +123,7 @@ ccw_device_accumulate_esw(struct ccw_device *cdev, struct irb *irb)
cdev_irb->esw.esw1.lpum = irb->esw.esw1.lpum;
/* Copy subchannel logout information if esw is of format 0. */
- if (irb->scsw.eswf) {
+ if (irb->scsw.cmd.eswf) {
cdev_sublog = &cdev_irb->esw.esw0.sublog;
sublog = &irb->esw.esw0.sublog;
/* Copy extended status flags. */
@@ -134,7 +132,7 @@ ccw_device_accumulate_esw(struct ccw_device *cdev, struct irb *irb)
* Copy fields that have a meaning for channel data check
* channel control check and interface control check.
*/
- if (irb->scsw.cstat & (SCHN_STAT_CHN_DATA_CHK |
+ if (irb->scsw.cmd.cstat & (SCHN_STAT_CHN_DATA_CHK |
SCHN_STAT_CHN_CTRL_CHK |
SCHN_STAT_INTF_CTRL_CHK)) {
/* Copy ancillary report bit. */
@@ -155,7 +153,7 @@ ccw_device_accumulate_esw(struct ccw_device *cdev, struct irb *irb)
/* Copy i/o-error alert. */
cdev_sublog->ioerr = sublog->ioerr;
/* Copy channel path timeout bit. */
- if (irb->scsw.cstat & SCHN_STAT_INTF_CTRL_CHK)
+ if (irb->scsw.cmd.cstat & SCHN_STAT_INTF_CTRL_CHK)
cdev_irb->esw.esw0.erw.cpt = irb->esw.esw0.erw.cpt;
/* Copy failing storage address validity flag. */
cdev_irb->esw.esw0.erw.fsavf = irb->esw.esw0.erw.fsavf;
@@ -200,24 +198,24 @@ ccw_device_accumulate_irb(struct ccw_device *cdev, struct irb *irb)
* If not, the remaining bit have no meaning and we must ignore them.
* The esw is not meaningful as well...
*/
- if (!(irb->scsw.stctl & SCSW_STCTL_STATUS_PEND))
+ if (!(scsw_stctl(&irb->scsw) & SCSW_STCTL_STATUS_PEND))
return;
/* Check for channel checks and interface control checks. */
ccw_device_msg_control_check(cdev, irb);
/* Check for path not operational. */
- if (irb->scsw.pno && irb->scsw.fctl != 0 &&
- (!(irb->scsw.stctl & SCSW_STCTL_INTER_STATUS) ||
- (irb->scsw.actl & SCSW_ACTL_SUSPENDED)))
+ if (scsw_is_valid_pno(&irb->scsw) && scsw_pno(&irb->scsw))
ccw_device_path_notoper(cdev);
-
+ /* No irb accumulation for transport mode irbs. */
+ if (scsw_is_tm(&irb->scsw)) {
+ memcpy(&cdev->private->irb, irb, sizeof(struct irb));
+ return;
+ }
/*
* Don't accumulate unsolicited interrupts.
*/
- if ((irb->scsw.stctl ==
- (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) &&
- (!irb->scsw.cc))
+ if (!scsw_is_solicited(&irb->scsw))
return;
cdev_irb = &cdev->private->irb;
@@ -227,62 +225,63 @@ ccw_device_accumulate_irb(struct ccw_device *cdev, struct irb *irb)
* status at the subchannel has been cleared and we must not pass
* intermediate accumulated status to the device driver.
*/
- if (irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC)
+ if (irb->scsw.cmd.fctl & SCSW_FCTL_CLEAR_FUNC)
memset(&cdev->private->irb, 0, sizeof(struct irb));
/* Copy bits which are valid only for the start function. */
- if (irb->scsw.fctl & SCSW_FCTL_START_FUNC) {
+ if (irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC) {
/* Copy key. */
- cdev_irb->scsw.key = irb->scsw.key;
+ cdev_irb->scsw.cmd.key = irb->scsw.cmd.key;
/* Copy suspend control bit. */
- cdev_irb->scsw.sctl = irb->scsw.sctl;
+ cdev_irb->scsw.cmd.sctl = irb->scsw.cmd.sctl;
/* Accumulate deferred condition code. */
- cdev_irb->scsw.cc |= irb->scsw.cc;
+ cdev_irb->scsw.cmd.cc |= irb->scsw.cmd.cc;
/* Copy ccw format bit. */
- cdev_irb->scsw.fmt = irb->scsw.fmt;
+ cdev_irb->scsw.cmd.fmt = irb->scsw.cmd.fmt;
/* Copy prefetch bit. */
- cdev_irb->scsw.pfch = irb->scsw.pfch;
+ cdev_irb->scsw.cmd.pfch = irb->scsw.cmd.pfch;
/* Copy initial-status-interruption-control. */
- cdev_irb->scsw.isic = irb->scsw.isic;
+ cdev_irb->scsw.cmd.isic = irb->scsw.cmd.isic;
/* Copy address limit checking control. */
- cdev_irb->scsw.alcc = irb->scsw.alcc;
+ cdev_irb->scsw.cmd.alcc = irb->scsw.cmd.alcc;
/* Copy suppress suspend bit. */
- cdev_irb->scsw.ssi = irb->scsw.ssi;
+ cdev_irb->scsw.cmd.ssi = irb->scsw.cmd.ssi;
}
/* Take care of the extended control bit and extended control word. */
ccw_device_accumulate_ecw(cdev, irb);
/* Accumulate function control. */
- cdev_irb->scsw.fctl |= irb->scsw.fctl;
+ cdev_irb->scsw.cmd.fctl |= irb->scsw.cmd.fctl;
/* Copy activity control. */
- cdev_irb->scsw.actl= irb->scsw.actl;
+ cdev_irb->scsw.cmd.actl = irb->scsw.cmd.actl;
/* Accumulate status control. */
- cdev_irb->scsw.stctl |= irb->scsw.stctl;
+ cdev_irb->scsw.cmd.stctl |= irb->scsw.cmd.stctl;
/*
* Copy ccw address if it is valid. This is a bit simplified
* but should be close enough for all practical purposes.
*/
- if ((irb->scsw.stctl & SCSW_STCTL_PRIM_STATUS) ||
- ((irb->scsw.stctl ==
+ if ((irb->scsw.cmd.stctl & SCSW_STCTL_PRIM_STATUS) ||
+ ((irb->scsw.cmd.stctl ==
(SCSW_STCTL_INTER_STATUS|SCSW_STCTL_STATUS_PEND)) &&
- (irb->scsw.actl & SCSW_ACTL_DEVACT) &&
- (irb->scsw.actl & SCSW_ACTL_SCHACT)) ||
- (irb->scsw.actl & SCSW_ACTL_SUSPENDED))
- cdev_irb->scsw.cpa = irb->scsw.cpa;
+ (irb->scsw.cmd.actl & SCSW_ACTL_DEVACT) &&
+ (irb->scsw.cmd.actl & SCSW_ACTL_SCHACT)) ||
+ (irb->scsw.cmd.actl & SCSW_ACTL_SUSPENDED))
+ cdev_irb->scsw.cmd.cpa = irb->scsw.cmd.cpa;
/* Accumulate device status, but not the device busy flag. */
- cdev_irb->scsw.dstat &= ~DEV_STAT_BUSY;
+ cdev_irb->scsw.cmd.dstat &= ~DEV_STAT_BUSY;
/* dstat is not always valid. */
- if (irb->scsw.stctl &
+ if (irb->scsw.cmd.stctl &
(SCSW_STCTL_PRIM_STATUS | SCSW_STCTL_SEC_STATUS
| SCSW_STCTL_INTER_STATUS | SCSW_STCTL_ALERT_STATUS))
- cdev_irb->scsw.dstat |= irb->scsw.dstat;
+ cdev_irb->scsw.cmd.dstat |= irb->scsw.cmd.dstat;
/* Accumulate subchannel status. */
- cdev_irb->scsw.cstat |= irb->scsw.cstat;
+ cdev_irb->scsw.cmd.cstat |= irb->scsw.cmd.cstat;
/* Copy residual count if it is valid. */
- if ((irb->scsw.stctl & SCSW_STCTL_PRIM_STATUS) &&
- (irb->scsw.cstat & ~(SCHN_STAT_PCI | SCHN_STAT_INCORR_LEN)) == 0)
- cdev_irb->scsw.count = irb->scsw.count;
+ if ((irb->scsw.cmd.stctl & SCSW_STCTL_PRIM_STATUS) &&
+ (irb->scsw.cmd.cstat & ~(SCHN_STAT_PCI | SCHN_STAT_INCORR_LEN))
+ == 0)
+ cdev_irb->scsw.cmd.count = irb->scsw.cmd.count;
/* Take care of bits in the extended status word. */
ccw_device_accumulate_esw(cdev, irb);
@@ -299,7 +298,7 @@ ccw_device_accumulate_irb(struct ccw_device *cdev, struct irb *irb)
* sense facility available/supported when enabling the
* concurrent sense facility.
*/
- if ((cdev_irb->scsw.dstat & DEV_STAT_UNIT_CHECK) &&
+ if ((cdev_irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
!(cdev_irb->esw.esw0.erw.cons))
cdev->private->flags.dosense = 1;
}
@@ -317,7 +316,7 @@ ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb)
sch = to_subchannel(cdev->dev.parent);
/* A sense is required, can we do it now ? */
- if ((irb->scsw.actl & (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) != 0)
+ if (scsw_actl(&irb->scsw) & (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT))
/*
* we received an Unit Check but we have no final
* status yet, therefore we must delay the SENSE
@@ -355,20 +354,18 @@ ccw_device_accumulate_basic_sense(struct ccw_device *cdev, struct irb *irb)
* If not, the remaining bit have no meaning and we must ignore them.
* The esw is not meaningful as well...
*/
- if (!(irb->scsw.stctl & SCSW_STCTL_STATUS_PEND))
+ if (!(scsw_stctl(&irb->scsw) & SCSW_STCTL_STATUS_PEND))
return;
/* Check for channel checks and interface control checks. */
ccw_device_msg_control_check(cdev, irb);
/* Check for path not operational. */
- if (irb->scsw.pno && irb->scsw.fctl != 0 &&
- (!(irb->scsw.stctl & SCSW_STCTL_INTER_STATUS) ||
- (irb->scsw.actl & SCSW_ACTL_SUSPENDED)))
+ if (scsw_is_valid_pno(&irb->scsw) && scsw_pno(&irb->scsw))
ccw_device_path_notoper(cdev);
- if (!(irb->scsw.dstat & DEV_STAT_UNIT_CHECK) &&
- (irb->scsw.dstat & DEV_STAT_CHN_END)) {
+ if (!(irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
+ (irb->scsw.cmd.dstat & DEV_STAT_CHN_END)) {
cdev->private->irb.esw.esw0.erw.cons = 1;
cdev->private->flags.dosense = 0;
}
@@ -386,11 +383,11 @@ int
ccw_device_accumulate_and_sense(struct ccw_device *cdev, struct irb *irb)
{
ccw_device_accumulate_irb(cdev, irb);
- if ((irb->scsw.actl & (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) != 0)
+ if ((irb->scsw.cmd.actl & (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) != 0)
return -EBUSY;
/* Check for basic sense. */
if (cdev->private->flags.dosense &&
- !(irb->scsw.dstat & DEV_STAT_UNIT_CHECK)) {
+ !(irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)) {
cdev->private->irb.esw.esw0.erw.cons = 1;
cdev->private->flags.dosense = 0;
return 0;
diff --git a/drivers/s390/cio/fcx.c b/drivers/s390/cio/fcx.c
new file mode 100644
index 000000000000..61677dfbdc9b
--- /dev/null
+++ b/drivers/s390/cio/fcx.c
@@ -0,0 +1,350 @@
+/*
+ * Functions for assembling fcx enabled I/O control blocks.
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <asm/fcx.h>
+#include "cio.h"
+
+/**
+ * tcw_get_intrg - return pointer to associated interrogate tcw
+ * @tcw: pointer to the original tcw
+ *
+ * Return a pointer to the interrogate tcw associated with the specified tcw
+ * or %NULL if there is no associated interrogate tcw.
+ */
+struct tcw *tcw_get_intrg(struct tcw *tcw)
+{
+ return (struct tcw *) ((addr_t) tcw->intrg);
+}
+EXPORT_SYMBOL(tcw_get_intrg);
+
+/**
+ * tcw_get_data - return pointer to input/output data associated with tcw
+ * @tcw: pointer to the tcw
+ *
+ * Return the input or output data address specified in the tcw depending
+ * on whether the r-bit or the w-bit is set. If neither bit is set, return
+ * %NULL.
+ */
+void *tcw_get_data(struct tcw *tcw)
+{
+ if (tcw->r)
+ return (void *) ((addr_t) tcw->input);
+ if (tcw->w)
+ return (void *) ((addr_t) tcw->output);
+ return NULL;
+}
+EXPORT_SYMBOL(tcw_get_data);
+
+/**
+ * tcw_get_tccb - return pointer to tccb associated with tcw
+ * @tcw: pointer to the tcw
+ *
+ * Return pointer to the tccb associated with this tcw.
+ */
+struct tccb *tcw_get_tccb(struct tcw *tcw)
+{
+ return (struct tccb *) ((addr_t) tcw->tccb);
+}
+EXPORT_SYMBOL(tcw_get_tccb);
+
+/**
+ * tcw_get_tsb - return pointer to tsb associated with tcw
+ * @tcw: pointer to the tcw
+ *
+ * Return pointer to the tsb associated with this tcw.
+ */
+struct tsb *tcw_get_tsb(struct tcw *tcw)
+{
+ return (struct tsb *) ((addr_t) tcw->tsb);
+}
+EXPORT_SYMBOL(tcw_get_tsb);
+
+/**
+ * tcw_init - initialize tcw data structure
+ * @tcw: pointer to the tcw to be initialized
+ * @r: initial value of the r-bit
+ * @w: initial value of the w-bit
+ *
+ * Initialize all fields of the specified tcw data structure with zero and
+ * fill in the format, flags, r and w fields.
+ */
+void tcw_init(struct tcw *tcw, int r, int w)
+{
+ memset(tcw, 0, sizeof(struct tcw));
+ tcw->format = TCW_FORMAT_DEFAULT;
+ tcw->flags = TCW_FLAGS_TIDAW_FORMAT(TCW_TIDAW_FORMAT_DEFAULT);
+ if (r)
+ tcw->r = 1;
+ if (w)
+ tcw->w = 1;
+}
+EXPORT_SYMBOL(tcw_init);
+
+static inline size_t tca_size(struct tccb *tccb)
+{
+ return tccb->tcah.tcal - 12;
+}
+
+static u32 calc_dcw_count(struct tccb *tccb)
+{
+ int offset;
+ struct dcw *dcw;
+ u32 count = 0;
+ size_t size;
+
+ size = tca_size(tccb);
+ for (offset = 0; offset < size;) {
+ dcw = (struct dcw *) &tccb->tca[offset];
+ count += dcw->count;
+ if (!(dcw->flags & DCW_FLAGS_CC))
+ break;
+ offset += sizeof(struct dcw) + ALIGN((int) dcw->cd_count, 4);
+ }
+ return count;
+}
+
+static u32 calc_cbc_size(struct tidaw *tidaw, int num)
+{
+ int i;
+ u32 cbc_data;
+ u32 cbc_count = 0;
+ u64 data_count = 0;
+
+ for (i = 0; i < num; i++) {
+ if (tidaw[i].flags & TIDAW_FLAGS_LAST)
+ break;
+ /* TODO: find out if padding applies to total of data
+ * transferred or data transferred by this tidaw. Assumption:
+ * applies to total. */
+ data_count += tidaw[i].count;
+ if (tidaw[i].flags & TIDAW_FLAGS_INSERT_CBC) {
+ cbc_data = 4 + ALIGN(data_count, 4) - data_count;
+ cbc_count += cbc_data;
+ data_count += cbc_data;
+ }
+ }
+ return cbc_count;
+}
+
+/**
+ * tcw_finalize - finalize tcw length fields and tidaw list
+ * @tcw: pointer to the tcw
+ * @num_tidaws: the number of tidaws used to address input/output data or zero
+ * if no tida is used
+ *
+ * Calculate the input-/output-count and tccbl field in the tcw, add a
+ * tcat the tccb and terminate the data tidaw list if used.
+ *
+ * Note: in case input- or output-tida is used, the tidaw-list must be stored
+ * in contiguous storage (no ttic). The tcal field in the tccb must be
+ * up-to-date.
+ */
+void tcw_finalize(struct tcw *tcw, int num_tidaws)
+{
+ struct tidaw *tidaw;
+ struct tccb *tccb;
+ struct tccb_tcat *tcat;
+ u32 count;
+
+ /* Terminate tidaw list. */
+ tidaw = tcw_get_data(tcw);
+ if (num_tidaws > 0)
+ tidaw[num_tidaws - 1].flags |= TIDAW_FLAGS_LAST;
+ /* Add tcat to tccb. */
+ tccb = tcw_get_tccb(tcw);
+ tcat = (struct tccb_tcat *) &tccb->tca[tca_size(tccb)];
+ memset(tcat, 0, sizeof(tcat));
+ /* Calculate tcw input/output count and tcat transport count. */
+ count = calc_dcw_count(tccb);
+ if (tcw->w && (tcw->flags & TCW_FLAGS_OUTPUT_TIDA))
+ count += calc_cbc_size(tidaw, num_tidaws);
+ if (tcw->r)
+ tcw->input_count = count;
+ else if (tcw->w)
+ tcw->output_count = count;
+ tcat->count = ALIGN(count, 4) + 4;
+ /* Calculate tccbl. */
+ tcw->tccbl = (sizeof(struct tccb) + tca_size(tccb) +
+ sizeof(struct tccb_tcat) - 20) >> 2;
+}
+EXPORT_SYMBOL(tcw_finalize);
+
+/**
+ * tcw_set_intrg - set the interrogate tcw address of a tcw
+ * @tcw: the tcw address
+ * @intrg_tcw: the address of the interrogate tcw
+ *
+ * Set the address of the interrogate tcw in the specified tcw.
+ */
+void tcw_set_intrg(struct tcw *tcw, struct tcw *intrg_tcw)
+{
+ tcw->intrg = (u32) ((addr_t) intrg_tcw);
+}
+EXPORT_SYMBOL(tcw_set_intrg);
+
+/**
+ * tcw_set_data - set data address and tida flag of a tcw
+ * @tcw: the tcw address
+ * @data: the data address
+ * @use_tidal: zero of the data address specifies a contiguous block of data,
+ * non-zero if it specifies a list if tidaws.
+ *
+ * Set the input/output data address of a tcw (depending on the value of the
+ * r-flag and w-flag). If @use_tidal is non-zero, the corresponding tida flag
+ * is set as well.
+ */
+void tcw_set_data(struct tcw *tcw, void *data, int use_tidal)
+{
+ if (tcw->r) {
+ tcw->input = (u64) ((addr_t) data);
+ if (use_tidal)
+ tcw->flags |= TCW_FLAGS_INPUT_TIDA;
+ } else if (tcw->w) {
+ tcw->output = (u64) ((addr_t) data);
+ if (use_tidal)
+ tcw->flags |= TCW_FLAGS_OUTPUT_TIDA;
+ }
+}
+EXPORT_SYMBOL(tcw_set_data);
+
+/**
+ * tcw_set_tccb - set tccb address of a tcw
+ * @tcw: the tcw address
+ * @tccb: the tccb address
+ *
+ * Set the address of the tccb in the specified tcw.
+ */
+void tcw_set_tccb(struct tcw *tcw, struct tccb *tccb)
+{
+ tcw->tccb = (u64) ((addr_t) tccb);
+}
+EXPORT_SYMBOL(tcw_set_tccb);
+
+/**
+ * tcw_set_tsb - set tsb address of a tcw
+ * @tcw: the tcw address
+ * @tsb: the tsb address
+ *
+ * Set the address of the tsb in the specified tcw.
+ */
+void tcw_set_tsb(struct tcw *tcw, struct tsb *tsb)
+{
+ tcw->tsb = (u64) ((addr_t) tsb);
+}
+EXPORT_SYMBOL(tcw_set_tsb);
+
+/**
+ * tccb_init - initialize tccb
+ * @tccb: the tccb address
+ * @size: the maximum size of the tccb
+ * @sac: the service-action-code to be user
+ *
+ * Initialize the header of the specified tccb by resetting all values to zero
+ * and filling in defaults for format, sac and initial tcal fields.
+ */
+void tccb_init(struct tccb *tccb, size_t size, u32 sac)
+{
+ memset(tccb, 0, size);
+ tccb->tcah.format = TCCB_FORMAT_DEFAULT;
+ tccb->tcah.sac = sac;
+ tccb->tcah.tcal = 12;
+}
+EXPORT_SYMBOL(tccb_init);
+
+/**
+ * tsb_init - initialize tsb
+ * @tsb: the tsb address
+ *
+ * Initialize the specified tsb by resetting all values to zero.
+ */
+void tsb_init(struct tsb *tsb)
+{
+ memset(tsb, 0, sizeof(tsb));
+}
+EXPORT_SYMBOL(tsb_init);
+
+/**
+ * tccb_add_dcw - add a dcw to the tccb
+ * @tccb: the tccb address
+ * @tccb_size: the maximum tccb size
+ * @cmd: the dcw command
+ * @flags: flags for the dcw
+ * @cd: pointer to control data for this dcw or NULL if none is required
+ * @cd_count: number of control data bytes for this dcw
+ * @count: number of data bytes for this dcw
+ *
+ * Add a new dcw to the specified tccb by writing the dcw information specified
+ * by @cmd, @flags, @cd, @cd_count and @count to the tca of the tccb. Return
+ * a pointer to the newly added dcw on success or -%ENOSPC if the new dcw
+ * would exceed the available space as defined by @tccb_size.
+ *
+ * Note: the tcal field of the tccb header will be updates to reflect added
+ * content.
+ */
+struct dcw *tccb_add_dcw(struct tccb *tccb, size_t tccb_size, u8 cmd, u8 flags,
+ void *cd, u8 cd_count, u32 count)
+{
+ struct dcw *dcw;
+ int size;
+ int tca_offset;
+
+ /* Check for space. */
+ tca_offset = tca_size(tccb);
+ size = ALIGN(sizeof(struct dcw) + cd_count, 4);
+ if (sizeof(struct tccb_tcah) + tca_offset + size +
+ sizeof(struct tccb_tcat) > tccb_size)
+ return ERR_PTR(-ENOSPC);
+ /* Add dcw to tca. */
+ dcw = (struct dcw *) &tccb->tca[tca_offset];
+ memset(dcw, 0, size);
+ dcw->cmd = cmd;
+ dcw->flags = flags;
+ dcw->count = count;
+ dcw->cd_count = cd_count;
+ if (cd)
+ memcpy(&dcw->cd[0], cd, cd_count);
+ tccb->tcah.tcal += size;
+ return dcw;
+}
+EXPORT_SYMBOL(tccb_add_dcw);
+
+/**
+ * tcw_add_tidaw - add a tidaw to a tcw
+ * @tcw: the tcw address
+ * @num_tidaws: the current number of tidaws
+ * @flags: flags for the new tidaw
+ * @addr: address value for the new tidaw
+ * @count: count value for the new tidaw
+ *
+ * Add a new tidaw to the input/output data tidaw-list of the specified tcw
+ * (depending on the value of the r-flag and w-flag) and return a pointer to
+ * the new tidaw.
+ *
+ * Note: the tidaw-list is assumed to be contiguous with no ttics. The caller
+ * must ensure that there is enough space for the new tidaw. The last-tidaw
+ * flag for the last tidaw in the list will be set by tcw_finalize.
+ */
+struct tidaw *tcw_add_tidaw(struct tcw *tcw, int num_tidaws, u8 flags,
+ void *addr, u32 count)
+{
+ struct tidaw *tidaw;
+
+ /* Add tidaw to tidaw-list. */
+ tidaw = ((struct tidaw *) tcw_get_data(tcw)) + num_tidaws;
+ memset(tidaw, 0, sizeof(struct tidaw));
+ tidaw->flags = flags;
+ tidaw->count = count;
+ tidaw->addr = (u64) ((addr_t) addr);
+ return tidaw;
+}
+EXPORT_SYMBOL(tcw_add_tidaw);
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h
index 8c613160bfce..b774960e76af 100644
--- a/drivers/s390/cio/io_sch.h
+++ b/drivers/s390/cio/io_sch.h
@@ -4,9 +4,9 @@
#include "schid.h"
/*
- * operation request block
+ * command-mode operation request block
*/
-struct orb {
+struct cmd_orb {
u32 intparm; /* interruption parameter */
u32 key : 4; /* flags, like key, suspend control, etc. */
u32 spnd : 1; /* suspend control */
@@ -28,8 +28,36 @@ struct orb {
u32 cpa; /* channel program address */
} __attribute__ ((packed, aligned(4)));
+/*
+ * transport-mode operation request block
+ */
+struct tm_orb {
+ u32 intparm;
+ u32 key:4;
+ u32 :9;
+ u32 b:1;
+ u32 :2;
+ u32 lpm:8;
+ u32 :7;
+ u32 x:1;
+ u32 tcw;
+ u32 prio:8;
+ u32 :8;
+ u32 rsvpgm:8;
+ u32 :8;
+ u32 :32;
+ u32 :32;
+ u32 :32;
+ u32 :32;
+} __attribute__ ((packed, aligned(4)));
+
+union orb {
+ struct cmd_orb cmd;
+ struct tm_orb tm;
+} __attribute__ ((packed, aligned(4)));
+
struct io_subchannel_private {
- struct orb orb; /* operation request block */
+ union orb orb; /* operation request block */
struct ccw1 sense_ccw; /* static ccw for sense command */
} __attribute__ ((aligned(8)));
@@ -95,16 +123,18 @@ struct ccw_device_private {
void *cmb_wait; /* deferred cmb enable/disable */
};
-static inline int ssch(struct subchannel_id schid, volatile struct orb *addr)
+static inline int ssch(struct subchannel_id schid, volatile union orb *addr)
{
register struct subchannel_id reg1 asm("1") = schid;
- int ccode;
+ int ccode = -EIO;
asm volatile(
" ssch 0(%2)\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
+ "0: ipm %0\n"
+ " srl %0,28\n"
+ "1:\n"
+ EX_TABLE(0b, 1b)
+ : "+d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
return ccode;
}
diff --git a/drivers/s390/cio/isc.c b/drivers/s390/cio/isc.c
new file mode 100644
index 000000000000..c592087be0f1
--- /dev/null
+++ b/drivers/s390/cio/isc.c
@@ -0,0 +1,68 @@
+/*
+ * Functions for registration of I/O interruption subclasses on s390.
+ *
+ * Copyright IBM Corp. 2008
+ * Authors: Sebastian Ott <sebott@linux.vnet.ibm.com>
+ */
+
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <asm/isc.h>
+
+static unsigned int isc_refs[MAX_ISC + 1];
+static DEFINE_SPINLOCK(isc_ref_lock);
+
+
+/**
+ * isc_register - register an I/O interruption subclass.
+ * @isc: I/O interruption subclass to register
+ *
+ * The number of users for @isc is increased. If this is the first user to
+ * register @isc, the corresponding I/O interruption subclass mask is enabled.
+ *
+ * Context:
+ * This function must not be called in interrupt context.
+ */
+void isc_register(unsigned int isc)
+{
+ if (isc > MAX_ISC) {
+ WARN_ON(1);
+ return;
+ }
+
+ spin_lock(&isc_ref_lock);
+ if (isc_refs[isc] == 0)
+ ctl_set_bit(6, 31 - isc);
+ isc_refs[isc]++;
+ spin_unlock(&isc_ref_lock);
+}
+EXPORT_SYMBOL_GPL(isc_register);
+
+/**
+ * isc_unregister - unregister an I/O interruption subclass.
+ * @isc: I/O interruption subclass to unregister
+ *
+ * The number of users for @isc is decreased. If this is the last user to
+ * unregister @isc, the corresponding I/O interruption subclass mask is
+ * disabled.
+ * Note: This function must not be called if isc_register() hasn't been called
+ * before by the driver for @isc.
+ *
+ * Context:
+ * This function must not be called in interrupt context.
+ */
+void isc_unregister(unsigned int isc)
+{
+ spin_lock(&isc_ref_lock);
+ /* check for misuse */
+ if (isc > MAX_ISC || isc_refs[isc] == 0) {
+ WARN_ON(1);
+ goto out_unlock;
+ }
+ if (isc_refs[isc] == 1)
+ ctl_clear_bit(6, 31 - isc);
+ isc_refs[isc]--;
+out_unlock:
+ spin_unlock(&isc_ref_lock);
+}
+EXPORT_SYMBOL_GPL(isc_unregister);
diff --git a/drivers/s390/cio/itcw.c b/drivers/s390/cio/itcw.c
new file mode 100644
index 000000000000..17da9ab932ed
--- /dev/null
+++ b/drivers/s390/cio/itcw.c
@@ -0,0 +1,327 @@
+/*
+ * Functions for incremental construction of fcx enabled I/O control blocks.
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <asm/fcx.h>
+#include <asm/itcw.h>
+
+/**
+ * struct itcw - incremental tcw helper data type
+ *
+ * This structure serves as a handle for the incremental construction of a
+ * tcw and associated tccb, tsb, data tidaw-list plus an optional interrogate
+ * tcw and associated data. The data structures are contained inside a single
+ * contiguous buffer provided by the user.
+ *
+ * The itcw construction functions take care of overall data integrity:
+ * - reset unused fields to zero
+ * - fill in required pointers
+ * - ensure required alignment for data structures
+ * - prevent data structures to cross 4k-byte boundary where required
+ * - calculate tccb-related length fields
+ * - optionally provide ready-made interrogate tcw and associated structures
+ *
+ * Restrictions apply to the itcws created with these construction functions:
+ * - tida only supported for data address, not for tccb
+ * - only contiguous tidaw-lists (no ttic)
+ * - total number of bytes required per itcw may not exceed 4k bytes
+ * - either read or write operation (may not work with r=0 and w=0)
+ *
+ * Example:
+ * struct itcw *itcw;
+ * void *buffer;
+ * size_t size;
+ *
+ * size = itcw_calc_size(1, 2, 0);
+ * buffer = kmalloc(size, GFP_DMA);
+ * if (!buffer)
+ * return -ENOMEM;
+ * itcw = itcw_init(buffer, size, ITCW_OP_READ, 1, 2, 0);
+ * if (IS_ERR(itcw))
+ * return PTR_ER(itcw);
+ * itcw_add_dcw(itcw, 0x2, 0, NULL, 0, 72);
+ * itcw_add_tidaw(itcw, 0, 0x30000, 20);
+ * itcw_add_tidaw(itcw, 0, 0x40000, 52);
+ * itcw_finalize(itcw);
+ *
+ */
+struct itcw {
+ struct tcw *tcw;
+ struct tcw *intrg_tcw;
+ int num_tidaws;
+ int max_tidaws;
+ int intrg_num_tidaws;
+ int intrg_max_tidaws;
+};
+
+/**
+ * itcw_get_tcw - return pointer to tcw associated with the itcw
+ * @itcw: address of the itcw
+ *
+ * Return pointer to the tcw associated with the itcw.
+ */
+struct tcw *itcw_get_tcw(struct itcw *itcw)
+{
+ return itcw->tcw;
+}
+EXPORT_SYMBOL(itcw_get_tcw);
+
+/**
+ * itcw_calc_size - return the size of an itcw with the given parameters
+ * @intrg: if non-zero, add an interrogate tcw
+ * @max_tidaws: maximum number of tidaws to be used for data addressing or zero
+ * if no tida is to be used.
+ * @intrg_max_tidaws: maximum number of tidaws to be used for data addressing
+ * by the interrogate tcw, if specified
+ *
+ * Calculate and return the number of bytes required to hold an itcw with the
+ * given parameters and assuming tccbs with maximum size.
+ *
+ * Note that the resulting size also contains bytes needed for alignment
+ * padding as well as padding to ensure that data structures don't cross a
+ * 4k-boundary where required.
+ */
+size_t itcw_calc_size(int intrg, int max_tidaws, int intrg_max_tidaws)
+{
+ size_t len;
+
+ /* Main data. */
+ len = sizeof(struct itcw);
+ len += /* TCW */ sizeof(struct tcw) + /* TCCB */ TCCB_MAX_SIZE +
+ /* TSB */ sizeof(struct tsb) +
+ /* TIDAL */ max_tidaws * sizeof(struct tidaw);
+ /* Interrogate data. */
+ if (intrg) {
+ len += /* TCW */ sizeof(struct tcw) + /* TCCB */ TCCB_MAX_SIZE +
+ /* TSB */ sizeof(struct tsb) +
+ /* TIDAL */ intrg_max_tidaws * sizeof(struct tidaw);
+ }
+ /* Maximum required alignment padding. */
+ len += /* Initial TCW */ 63 + /* Interrogate TCCB */ 7;
+ /* Maximum padding for structures that may not cross 4k boundary. */
+ if ((max_tidaws > 0) || (intrg_max_tidaws > 0))
+ len += max(max_tidaws, intrg_max_tidaws) *
+ sizeof(struct tidaw) - 1;
+ return len;
+}
+EXPORT_SYMBOL(itcw_calc_size);
+
+#define CROSS4K(x, l) (((x) & ~4095) != ((x + l) & ~4095))
+
+static inline void *fit_chunk(addr_t *start, addr_t end, size_t len,
+ int align, int check_4k)
+{
+ addr_t addr;
+
+ addr = ALIGN(*start, align);
+ if (check_4k && CROSS4K(addr, len)) {
+ addr = ALIGN(addr, 4096);
+ addr = ALIGN(addr, align);
+ }
+ if (addr + len > end)
+ return ERR_PTR(-ENOSPC);
+ *start = addr + len;
+ return (void *) addr;
+}
+
+/**
+ * itcw_init - initialize incremental tcw data structure
+ * @buffer: address of buffer to use for data structures
+ * @size: number of bytes in buffer
+ * @op: %ITCW_OP_READ for a read operation tcw, %ITCW_OP_WRITE for a write
+ * operation tcw
+ * @intrg: if non-zero, add and initialize an interrogate tcw
+ * @max_tidaws: maximum number of tidaws to be used for data addressing or zero
+ * if no tida is to be used.
+ * @intrg_max_tidaws: maximum number of tidaws to be used for data addressing
+ * by the interrogate tcw, if specified
+ *
+ * Prepare the specified buffer to be used as an incremental tcw, i.e. a
+ * helper data structure that can be used to construct a valid tcw by
+ * successive calls to other helper functions. Note: the buffer needs to be
+ * located below the 2G address limit. The resulting tcw has the following
+ * restrictions:
+ * - no tccb tidal
+ * - input/output tidal is contiguous (no ttic)
+ * - total data should not exceed 4k
+ * - tcw specifies either read or write operation
+ *
+ * On success, return pointer to the resulting incremental tcw data structure,
+ * ERR_PTR otherwise.
+ */
+struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg,
+ int max_tidaws, int intrg_max_tidaws)
+{
+ struct itcw *itcw;
+ void *chunk;
+ addr_t start;
+ addr_t end;
+
+ /* Check for 2G limit. */
+ start = (addr_t) buffer;
+ end = start + size;
+ if (end > (1 << 31))
+ return ERR_PTR(-EINVAL);
+ memset(buffer, 0, size);
+ /* ITCW. */
+ chunk = fit_chunk(&start, end, sizeof(struct itcw), 1, 0);
+ if (IS_ERR(chunk))
+ return chunk;
+ itcw = chunk;
+ itcw->max_tidaws = max_tidaws;
+ itcw->intrg_max_tidaws = intrg_max_tidaws;
+ /* Main TCW. */
+ chunk = fit_chunk(&start, end, sizeof(struct tcw), 64, 0);
+ if (IS_ERR(chunk))
+ return chunk;
+ itcw->tcw = chunk;
+ tcw_init(itcw->tcw, (op == ITCW_OP_READ) ? 1 : 0,
+ (op == ITCW_OP_WRITE) ? 1 : 0);
+ /* Interrogate TCW. */
+ if (intrg) {
+ chunk = fit_chunk(&start, end, sizeof(struct tcw), 64, 0);
+ if (IS_ERR(chunk))
+ return chunk;
+ itcw->intrg_tcw = chunk;
+ tcw_init(itcw->intrg_tcw, 1, 0);
+ tcw_set_intrg(itcw->tcw, itcw->intrg_tcw);
+ }
+ /* Data TIDAL. */
+ if (max_tidaws > 0) {
+ chunk = fit_chunk(&start, end, sizeof(struct tidaw) *
+ max_tidaws, 16, 1);
+ if (IS_ERR(chunk))
+ return chunk;
+ tcw_set_data(itcw->tcw, chunk, 1);
+ }
+ /* Interrogate data TIDAL. */
+ if (intrg && (intrg_max_tidaws > 0)) {
+ chunk = fit_chunk(&start, end, sizeof(struct tidaw) *
+ intrg_max_tidaws, 16, 1);
+ if (IS_ERR(chunk))
+ return chunk;
+ tcw_set_data(itcw->intrg_tcw, chunk, 1);
+ }
+ /* TSB. */
+ chunk = fit_chunk(&start, end, sizeof(struct tsb), 8, 0);
+ if (IS_ERR(chunk))
+ return chunk;
+ tsb_init(chunk);
+ tcw_set_tsb(itcw->tcw, chunk);
+ /* Interrogate TSB. */
+ if (intrg) {
+ chunk = fit_chunk(&start, end, sizeof(struct tsb), 8, 0);
+ if (IS_ERR(chunk))
+ return chunk;
+ tsb_init(chunk);
+ tcw_set_tsb(itcw->intrg_tcw, chunk);
+ }
+ /* TCCB. */
+ chunk = fit_chunk(&start, end, TCCB_MAX_SIZE, 8, 0);
+ if (IS_ERR(chunk))
+ return chunk;
+ tccb_init(chunk, TCCB_MAX_SIZE, TCCB_SAC_DEFAULT);
+ tcw_set_tccb(itcw->tcw, chunk);
+ /* Interrogate TCCB. */
+ if (intrg) {
+ chunk = fit_chunk(&start, end, TCCB_MAX_SIZE, 8, 0);
+ if (IS_ERR(chunk))
+ return chunk;
+ tccb_init(chunk, TCCB_MAX_SIZE, TCCB_SAC_INTRG);
+ tcw_set_tccb(itcw->intrg_tcw, chunk);
+ tccb_add_dcw(chunk, TCCB_MAX_SIZE, DCW_CMD_INTRG, 0, NULL,
+ sizeof(struct dcw_intrg_data), 0);
+ tcw_finalize(itcw->intrg_tcw, 0);
+ }
+ return itcw;
+}
+EXPORT_SYMBOL(itcw_init);
+
+/**
+ * itcw_add_dcw - add a dcw to the itcw
+ * @itcw: address of the itcw
+ * @cmd: the dcw command
+ * @flags: flags for the dcw
+ * @cd: address of control data for this dcw or NULL if none is required
+ * @cd_count: number of control data bytes for this dcw
+ * @count: number of data bytes for this dcw
+ *
+ * Add a new dcw to the specified itcw by writing the dcw information specified
+ * by @cmd, @flags, @cd, @cd_count and @count to the tca of the tccb. Return
+ * a pointer to the newly added dcw on success or -%ENOSPC if the new dcw
+ * would exceed the available space.
+ *
+ * Note: the tcal field of the tccb header will be updated to reflect added
+ * content.
+ */
+struct dcw *itcw_add_dcw(struct itcw *itcw, u8 cmd, u8 flags, void *cd,
+ u8 cd_count, u32 count)
+{
+ return tccb_add_dcw(tcw_get_tccb(itcw->tcw), TCCB_MAX_SIZE, cmd,
+ flags, cd, cd_count, count);
+}
+EXPORT_SYMBOL(itcw_add_dcw);
+
+/**
+ * itcw_add_tidaw - add a tidaw to the itcw
+ * @itcw: address of the itcw
+ * @flags: flags for the new tidaw
+ * @addr: address value for the new tidaw
+ * @count: count value for the new tidaw
+ *
+ * Add a new tidaw to the input/output data tidaw-list of the specified itcw
+ * (depending on the value of the r-flag and w-flag). Return a pointer to
+ * the new tidaw on success or -%ENOSPC if the new tidaw would exceed the
+ * available space.
+ *
+ * Note: the tidaw-list is assumed to be contiguous with no ttics. The
+ * last-tidaw flag for the last tidaw in the list will be set by itcw_finalize.
+ */
+struct tidaw *itcw_add_tidaw(struct itcw *itcw, u8 flags, void *addr, u32 count)
+{
+ if (itcw->num_tidaws >= itcw->max_tidaws)
+ return ERR_PTR(-ENOSPC);
+ return tcw_add_tidaw(itcw->tcw, itcw->num_tidaws++, flags, addr, count);
+}
+EXPORT_SYMBOL(itcw_add_tidaw);
+
+/**
+ * itcw_set_data - set data address and tida flag of the itcw
+ * @itcw: address of the itcw
+ * @addr: the data address
+ * @use_tidal: zero of the data address specifies a contiguous block of data,
+ * non-zero if it specifies a list if tidaws.
+ *
+ * Set the input/output data address of the itcw (depending on the value of the
+ * r-flag and w-flag). If @use_tidal is non-zero, the corresponding tida flag
+ * is set as well.
+ */
+void itcw_set_data(struct itcw *itcw, void *addr, int use_tidal)
+{
+ tcw_set_data(itcw->tcw, addr, use_tidal);
+}
+EXPORT_SYMBOL(itcw_set_data);
+
+/**
+ * itcw_finalize - calculate length and count fields of the itcw
+ * @itcw: address of the itcw
+ *
+ * Calculate tcw input-/output-count and tccbl fields and add a tcat the tccb.
+ * In case input- or output-tida is used, the tidaw-list must be stored in
+ * continuous storage (no ttic). The tcal field in the tccb must be
+ * up-to-date.
+ */
+void itcw_finalize(struct itcw *itcw)
+{
+ tcw_finalize(itcw->tcw, itcw->num_tidaws);
+}
+EXPORT_SYMBOL(itcw_finalize);
diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c
index 445cf364e461..34dd6ea0e9f0 100644
--- a/drivers/s390/cio/qdio.c
+++ b/drivers/s390/cio/qdio.c
@@ -2001,21 +2001,21 @@ qdio_handle_activate_check(struct ccw_device *cdev, unsigned long intparm,
irq_ptr = cdev->private->qdio_data;
QDIO_DBF_TEXT2(1, trace, "ick2");
- sprintf(dbf_text,"%s", cdev->dev.bus_id);
+ sprintf(dbf_text,"%s", dev_name(&cdev->dev));
QDIO_DBF_TEXT2(1,trace,dbf_text);
QDIO_DBF_HEX2(0,trace,&intparm,sizeof(int));
QDIO_DBF_HEX2(0,trace,&dstat,sizeof(int));
QDIO_DBF_HEX2(0,trace,&cstat,sizeof(int));
QDIO_PRINT_ERR("received check condition on activate " \
"queues on device %s (cs=x%x, ds=x%x).\n",
- cdev->dev.bus_id, cstat, dstat);
+ dev_name(&cdev->dev), cstat, dstat);
if (irq_ptr->no_input_qs) {
q=irq_ptr->input_qs[0];
} else if (irq_ptr->no_output_qs) {
q=irq_ptr->output_qs[0];
} else {
QDIO_PRINT_ERR("oops... no queue registered for device %s!?\n",
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
goto omit_handler_call;
}
q->handler(q->cdev,QDIO_STATUS_ACTIVATE_CHECK_CONDITION|
@@ -2045,7 +2045,7 @@ qdio_timeout_handler(struct ccw_device *cdev)
char dbf_text[15];
QDIO_DBF_TEXT2(0, trace, "qtoh");
- sprintf(dbf_text, "%s", cdev->dev.bus_id);
+ sprintf(dbf_text, "%s", dev_name(&cdev->dev));
QDIO_DBF_TEXT2(0, trace, dbf_text);
irq_ptr = cdev->private->qdio_data;
@@ -2095,23 +2095,23 @@ qdio_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
#ifdef CONFIG_QDIO_DEBUG
QDIO_DBF_TEXT4(0, trace, "qint");
- sprintf(dbf_text, "%s", cdev->dev.bus_id);
+ sprintf(dbf_text, "%s", dev_name(&cdev->dev));
QDIO_DBF_TEXT4(0, trace, dbf_text);
#endif /* CONFIG_QDIO_DEBUG */
if (!intparm) {
QDIO_PRINT_ERR("got unsolicited interrupt in qdio " \
- "handler, device %s\n", cdev->dev.bus_id);
+ "handler, device %s\n", dev_name(&cdev->dev));
return;
}
irq_ptr = cdev->private->qdio_data;
if (!irq_ptr) {
QDIO_DBF_TEXT2(1, trace, "uint");
- sprintf(dbf_text,"%s", cdev->dev.bus_id);
+ sprintf(dbf_text,"%s", dev_name(&cdev->dev));
QDIO_DBF_TEXT2(1,trace,dbf_text);
QDIO_PRINT_ERR("received interrupt on unused device %s!\n",
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
return;
}
@@ -2120,14 +2120,14 @@ qdio_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
switch (PTR_ERR(irb)) {
case -EIO:
QDIO_PRINT_ERR("i/o error on device %s\n",
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
return;
case -ETIMEDOUT:
qdio_timeout_handler(cdev);
return;
default:
QDIO_PRINT_ERR("unknown error state %ld on device %s\n",
- PTR_ERR(irb), cdev->dev.bus_id);
+ PTR_ERR(irb), dev_name(&cdev->dev));
return;
}
}
@@ -2139,8 +2139,8 @@ qdio_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
QDIO_DBF_TEXT4(0, trace, dbf_text);
#endif /* CONFIG_QDIO_DEBUG */
- cstat = irb->scsw.cstat;
- dstat = irb->scsw.dstat;
+ cstat = irb->scsw.cmd.cstat;
+ dstat = irb->scsw.cmd.dstat;
switch (irq_ptr->state) {
case QDIO_IRQ_STATE_INACTIVE:
@@ -2165,7 +2165,7 @@ qdio_handler(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
default:
QDIO_PRINT_ERR("got interrupt for queues in state %d on " \
"device %s?!\n",
- irq_ptr->state, cdev->dev.bus_id);
+ irq_ptr->state, dev_name(&cdev->dev));
}
wake_up(&cdev->private->wait_q);
@@ -2353,9 +2353,6 @@ tiqdio_check_chsc_availability(void)
{
char dbf_text[15];
- if (!css_characteristics_avail)
- return -EIO;
-
/* Check for bit 41. */
if (!css_general_characteristics.aif) {
QDIO_PRINT_WARN("Adapter interruption facility not " \
@@ -2675,7 +2672,7 @@ qdio_shutdown(struct ccw_device *cdev, int how)
irq_ptr->state == QDIO_IRQ_STATE_ERR);
} else {
QDIO_PRINT_INFO("ccw_device_{halt,clear} returned %d for "
- "device %s\n", result, cdev->dev.bus_id);
+ "device %s\n", result, dev_name(&cdev->dev));
spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
result = rc;
goto out;
@@ -3722,7 +3719,8 @@ tiqdio_register_thinints(void)
char dbf_text[20];
tiqdio_ind =
- s390_register_adapter_interrupt(&tiqdio_thinint_handler, NULL);
+ s390_register_adapter_interrupt(&tiqdio_thinint_handler, NULL,
+ TIQDIO_THININT_ISC);
if (IS_ERR(tiqdio_ind)) {
sprintf(dbf_text, "regthn%lx", PTR_ERR(tiqdio_ind));
QDIO_DBF_TEXT0(0,setup,dbf_text);
@@ -3738,7 +3736,8 @@ static void
tiqdio_unregister_thinints(void)
{
if (tiqdio_ind)
- s390_unregister_adapter_interrupt(tiqdio_ind);
+ s390_unregister_adapter_interrupt(tiqdio_ind,
+ TIQDIO_THININT_ISC);
}
static int
@@ -3899,6 +3898,7 @@ init_QDIO(void)
qdio_mempool_alloc,
qdio_mempool_free, NULL);
+ isc_register(QDIO_AIRQ_ISC);
if (tiqdio_check_chsc_availability())
QDIO_PRINT_ERR("Not all CHSCs supported. Continuing.\n");
@@ -3911,6 +3911,7 @@ static void __exit
cleanup_QDIO(void)
{
tiqdio_unregister_thinints();
+ isc_unregister(QDIO_AIRQ_ISC);
qdio_remove_procfs_entry();
qdio_release_qdio_memory();
qdio_unregister_dbf_views();
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index c3df6b2c38b7..733934a166b1 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -2,6 +2,7 @@
#define _CIO_QDIO_H
#include <asm/page.h>
+#include <asm/isc.h>
#include "schid.h"
@@ -26,7 +27,7 @@
*/
#define IQDIO_FILL_LEVEL_TO_POLL 4
-#define TIQDIO_THININT_ISC 3
+#define TIQDIO_THININT_ISC QDIO_AIRQ_ISC
#define TIQDIO_DELAY_TARGET 0
#define QDIO_BUSY_BIT_PATIENCE 100 /* in microsecs */
#define QDIO_BUSY_BIT_GIVE_UP 10000000 /* 10 seconds */
diff --git a/drivers/s390/cio/scsw.c b/drivers/s390/cio/scsw.c
new file mode 100644
index 000000000000..f8da25ab576d
--- /dev/null
+++ b/drivers/s390/cio/scsw.c
@@ -0,0 +1,843 @@
+/*
+ * Helper functions for scsw access.
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <asm/cio.h>
+#include "css.h"
+#include "chsc.h"
+
+/**
+ * scsw_is_tm - check for transport mode scsw
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the specified scsw is a transport mode scsw, zero
+ * otherwise.
+ */
+int scsw_is_tm(union scsw *scsw)
+{
+ return css_general_characteristics.fcx && (scsw->tm.x == 1);
+}
+EXPORT_SYMBOL(scsw_is_tm);
+
+/**
+ * scsw_key - return scsw key field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the key field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_key(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw->tm.key;
+ else
+ return scsw->cmd.key;
+}
+EXPORT_SYMBOL(scsw_key);
+
+/**
+ * scsw_eswf - return scsw eswf field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the eswf field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_eswf(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw->tm.eswf;
+ else
+ return scsw->cmd.eswf;
+}
+EXPORT_SYMBOL(scsw_eswf);
+
+/**
+ * scsw_cc - return scsw cc field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the cc field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_cc(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw->tm.cc;
+ else
+ return scsw->cmd.cc;
+}
+EXPORT_SYMBOL(scsw_cc);
+
+/**
+ * scsw_ectl - return scsw ectl field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the ectl field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_ectl(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw->tm.ectl;
+ else
+ return scsw->cmd.ectl;
+}
+EXPORT_SYMBOL(scsw_ectl);
+
+/**
+ * scsw_pno - return scsw pno field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the pno field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_pno(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw->tm.pno;
+ else
+ return scsw->cmd.pno;
+}
+EXPORT_SYMBOL(scsw_pno);
+
+/**
+ * scsw_fctl - return scsw fctl field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the fctl field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_fctl(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw->tm.fctl;
+ else
+ return scsw->cmd.fctl;
+}
+EXPORT_SYMBOL(scsw_fctl);
+
+/**
+ * scsw_actl - return scsw actl field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the actl field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_actl(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw->tm.actl;
+ else
+ return scsw->cmd.actl;
+}
+EXPORT_SYMBOL(scsw_actl);
+
+/**
+ * scsw_stctl - return scsw stctl field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the stctl field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_stctl(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw->tm.stctl;
+ else
+ return scsw->cmd.stctl;
+}
+EXPORT_SYMBOL(scsw_stctl);
+
+/**
+ * scsw_dstat - return scsw dstat field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the dstat field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_dstat(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw->tm.dstat;
+ else
+ return scsw->cmd.dstat;
+}
+EXPORT_SYMBOL(scsw_dstat);
+
+/**
+ * scsw_cstat - return scsw cstat field
+ * @scsw: pointer to scsw
+ *
+ * Return the value of the cstat field of the specified scsw, regardless of
+ * whether it is a transport mode or command mode scsw.
+ */
+u32 scsw_cstat(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw->tm.cstat;
+ else
+ return scsw->cmd.cstat;
+}
+EXPORT_SYMBOL(scsw_cstat);
+
+/**
+ * scsw_cmd_is_valid_key - check key field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the key field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_key(union scsw *scsw)
+{
+ return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_key);
+
+/**
+ * scsw_cmd_is_valid_sctl - check fctl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the fctl field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_sctl(union scsw *scsw)
+{
+ return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_sctl);
+
+/**
+ * scsw_cmd_is_valid_eswf - check eswf field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the eswf field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_eswf(union scsw *scsw)
+{
+ return (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_eswf);
+
+/**
+ * scsw_cmd_is_valid_cc - check cc field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the cc field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_cc(union scsw *scsw)
+{
+ return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) &&
+ (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_cc);
+
+/**
+ * scsw_cmd_is_valid_fmt - check fmt field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the fmt field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_fmt(union scsw *scsw)
+{
+ return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_fmt);
+
+/**
+ * scsw_cmd_is_valid_pfch - check pfch field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the pfch field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_pfch(union scsw *scsw)
+{
+ return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_pfch);
+
+/**
+ * scsw_cmd_is_valid_isic - check isic field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the isic field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_isic(union scsw *scsw)
+{
+ return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_isic);
+
+/**
+ * scsw_cmd_is_valid_alcc - check alcc field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the alcc field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_alcc(union scsw *scsw)
+{
+ return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_alcc);
+
+/**
+ * scsw_cmd_is_valid_ssi - check ssi field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the ssi field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_ssi(union scsw *scsw)
+{
+ return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_ssi);
+
+/**
+ * scsw_cmd_is_valid_zcc - check zcc field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the zcc field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_zcc(union scsw *scsw)
+{
+ return (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) &&
+ (scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_zcc);
+
+/**
+ * scsw_cmd_is_valid_ectl - check ectl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the ectl field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_ectl(union scsw *scsw)
+{
+ return (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) &&
+ !(scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS) &&
+ (scsw->cmd.stctl & SCSW_STCTL_ALERT_STATUS);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_ectl);
+
+/**
+ * scsw_cmd_is_valid_pno - check pno field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the pno field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_pno(union scsw *scsw)
+{
+ return (scsw->cmd.fctl != 0) &&
+ (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) &&
+ (!(scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS) ||
+ ((scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS) &&
+ (scsw->cmd.actl & SCSW_ACTL_SUSPENDED)));
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_pno);
+
+/**
+ * scsw_cmd_is_valid_fctl - check fctl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the fctl field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_fctl(union scsw *scsw)
+{
+ /* Only valid if pmcw.dnv == 1*/
+ return 1;
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_fctl);
+
+/**
+ * scsw_cmd_is_valid_actl - check actl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the actl field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_actl(union scsw *scsw)
+{
+ /* Only valid if pmcw.dnv == 1*/
+ return 1;
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_actl);
+
+/**
+ * scsw_cmd_is_valid_stctl - check stctl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the stctl field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_stctl(union scsw *scsw)
+{
+ /* Only valid if pmcw.dnv == 1*/
+ return 1;
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_stctl);
+
+/**
+ * scsw_cmd_is_valid_dstat - check dstat field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the dstat field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_dstat(union scsw *scsw)
+{
+ return (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) &&
+ (scsw->cmd.cc != 3);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_dstat);
+
+/**
+ * scsw_cmd_is_valid_cstat - check cstat field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the cstat field of the specified command mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_cmd_is_valid_cstat(union scsw *scsw)
+{
+ return (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) &&
+ (scsw->cmd.cc != 3);
+}
+EXPORT_SYMBOL(scsw_cmd_is_valid_cstat);
+
+/**
+ * scsw_tm_is_valid_key - check key field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the key field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_key(union scsw *scsw)
+{
+ return (scsw->tm.fctl & SCSW_FCTL_START_FUNC);
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_key);
+
+/**
+ * scsw_tm_is_valid_eswf - check eswf field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the eswf field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_eswf(union scsw *scsw)
+{
+ return (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND);
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_eswf);
+
+/**
+ * scsw_tm_is_valid_cc - check cc field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the cc field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_cc(union scsw *scsw)
+{
+ return (scsw->tm.fctl & SCSW_FCTL_START_FUNC) &&
+ (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND);
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_cc);
+
+/**
+ * scsw_tm_is_valid_fmt - check fmt field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the fmt field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_fmt(union scsw *scsw)
+{
+ return 1;
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_fmt);
+
+/**
+ * scsw_tm_is_valid_x - check x field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the x field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_x(union scsw *scsw)
+{
+ return 1;
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_x);
+
+/**
+ * scsw_tm_is_valid_q - check q field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the q field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_q(union scsw *scsw)
+{
+ return 1;
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_q);
+
+/**
+ * scsw_tm_is_valid_ectl - check ectl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the ectl field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_ectl(union scsw *scsw)
+{
+ return (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND) &&
+ !(scsw->tm.stctl & SCSW_STCTL_INTER_STATUS) &&
+ (scsw->tm.stctl & SCSW_STCTL_ALERT_STATUS);
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_ectl);
+
+/**
+ * scsw_tm_is_valid_pno - check pno field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the pno field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_pno(union scsw *scsw)
+{
+ return (scsw->tm.fctl != 0) &&
+ (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND) &&
+ (!(scsw->tm.stctl & SCSW_STCTL_INTER_STATUS) ||
+ ((scsw->tm.stctl & SCSW_STCTL_INTER_STATUS) &&
+ (scsw->tm.actl & SCSW_ACTL_SUSPENDED)));
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_pno);
+
+/**
+ * scsw_tm_is_valid_fctl - check fctl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the fctl field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_fctl(union scsw *scsw)
+{
+ /* Only valid if pmcw.dnv == 1*/
+ return 1;
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_fctl);
+
+/**
+ * scsw_tm_is_valid_actl - check actl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the actl field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_actl(union scsw *scsw)
+{
+ /* Only valid if pmcw.dnv == 1*/
+ return 1;
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_actl);
+
+/**
+ * scsw_tm_is_valid_stctl - check stctl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the stctl field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_stctl(union scsw *scsw)
+{
+ /* Only valid if pmcw.dnv == 1*/
+ return 1;
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_stctl);
+
+/**
+ * scsw_tm_is_valid_dstat - check dstat field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the dstat field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_dstat(union scsw *scsw)
+{
+ return (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND) &&
+ (scsw->tm.cc != 3);
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_dstat);
+
+/**
+ * scsw_tm_is_valid_cstat - check cstat field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the cstat field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_cstat(union scsw *scsw)
+{
+ return (scsw->tm.stctl & SCSW_STCTL_STATUS_PEND) &&
+ (scsw->tm.cc != 3);
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_cstat);
+
+/**
+ * scsw_tm_is_valid_fcxs - check fcxs field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the fcxs field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_fcxs(union scsw *scsw)
+{
+ return 1;
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_fcxs);
+
+/**
+ * scsw_tm_is_valid_schxs - check schxs field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the schxs field of the specified transport mode scsw is
+ * valid, zero otherwise.
+ */
+int scsw_tm_is_valid_schxs(union scsw *scsw)
+{
+ return (scsw->tm.cstat & (SCHN_STAT_PROG_CHECK |
+ SCHN_STAT_INTF_CTRL_CHK |
+ SCHN_STAT_PROT_CHECK |
+ SCHN_STAT_CHN_DATA_CHK));
+}
+EXPORT_SYMBOL(scsw_tm_is_valid_schxs);
+
+/**
+ * scsw_is_valid_actl - check actl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the actl field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_actl(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_valid_actl(scsw);
+ else
+ return scsw_cmd_is_valid_actl(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_actl);
+
+/**
+ * scsw_is_valid_cc - check cc field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the cc field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_cc(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_valid_cc(scsw);
+ else
+ return scsw_cmd_is_valid_cc(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_cc);
+
+/**
+ * scsw_is_valid_cstat - check cstat field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the cstat field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_cstat(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_valid_cstat(scsw);
+ else
+ return scsw_cmd_is_valid_cstat(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_cstat);
+
+/**
+ * scsw_is_valid_dstat - check dstat field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the dstat field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_dstat(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_valid_dstat(scsw);
+ else
+ return scsw_cmd_is_valid_dstat(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_dstat);
+
+/**
+ * scsw_is_valid_ectl - check ectl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the ectl field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_ectl(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_valid_ectl(scsw);
+ else
+ return scsw_cmd_is_valid_ectl(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_ectl);
+
+/**
+ * scsw_is_valid_eswf - check eswf field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the eswf field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_eswf(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_valid_eswf(scsw);
+ else
+ return scsw_cmd_is_valid_eswf(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_eswf);
+
+/**
+ * scsw_is_valid_fctl - check fctl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the fctl field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_fctl(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_valid_fctl(scsw);
+ else
+ return scsw_cmd_is_valid_fctl(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_fctl);
+
+/**
+ * scsw_is_valid_key - check key field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the key field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_key(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_valid_key(scsw);
+ else
+ return scsw_cmd_is_valid_key(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_key);
+
+/**
+ * scsw_is_valid_pno - check pno field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the pno field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_pno(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_valid_pno(scsw);
+ else
+ return scsw_cmd_is_valid_pno(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_pno);
+
+/**
+ * scsw_is_valid_stctl - check stctl field validity
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the stctl field of the specified scsw is valid,
+ * regardless of whether it is a transport mode or command mode scsw.
+ * Return zero if the field does not contain a valid value.
+ */
+int scsw_is_valid_stctl(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_valid_stctl(scsw);
+ else
+ return scsw_cmd_is_valid_stctl(scsw);
+}
+EXPORT_SYMBOL(scsw_is_valid_stctl);
+
+/**
+ * scsw_cmd_is_solicited - check for solicited scsw
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the command mode scsw indicates that the associated
+ * status condition is solicited, zero if it is unsolicited.
+ */
+int scsw_cmd_is_solicited(union scsw *scsw)
+{
+ return (scsw->cmd.cc != 0) || (scsw->cmd.stctl !=
+ (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS));
+}
+EXPORT_SYMBOL(scsw_cmd_is_solicited);
+
+/**
+ * scsw_tm_is_solicited - check for solicited scsw
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the transport mode scsw indicates that the associated
+ * status condition is solicited, zero if it is unsolicited.
+ */
+int scsw_tm_is_solicited(union scsw *scsw)
+{
+ return (scsw->tm.cc != 0) || (scsw->tm.stctl !=
+ (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS));
+}
+EXPORT_SYMBOL(scsw_tm_is_solicited);
+
+/**
+ * scsw_is_solicited - check for solicited scsw
+ * @scsw: pointer to scsw
+ *
+ * Return non-zero if the transport or command mode scsw indicates that the
+ * associated status condition is solicited, zero if it is unsolicited.
+ */
+int scsw_is_solicited(union scsw *scsw)
+{
+ if (scsw_is_tm(scsw))
+ return scsw_tm_is_solicited(scsw);
+ else
+ return scsw_cmd_is_solicited(scsw);
+}
+EXPORT_SYMBOL(scsw_is_solicited);
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index a1ab3e3efd11..d9f38b90afa7 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -859,8 +859,8 @@ static void ap_scan_bus(struct work_struct *unused)
ap_dev->device.bus = &ap_bus_type;
ap_dev->device.parent = ap_root_device;
- snprintf(ap_dev->device.bus_id, BUS_ID_SIZE, "card%02x",
- AP_QID_DEVICE(ap_dev->qid));
+ dev_set_name(&ap_dev->device, "card%02x",
+ AP_QID_DEVICE(ap_dev->qid));
ap_dev->device.release = ap_device_release;
rc = device_register(&ap_dev->device);
if (rc) {
diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c
index 5ab34340919b..3d1160e9e5a4 100644
--- a/drivers/s390/kvm/kvm_virtio.c
+++ b/drivers/s390/kvm/kvm_virtio.c
@@ -23,6 +23,7 @@
#include <asm/kvm_virtio.h>
#include <asm/setup.h>
#include <asm/s390_ext.h>
+#include <asm/s390_rdev.h>
#define VIRTIO_SUBCODE_64 0x0D00
@@ -236,10 +237,7 @@ static struct virtio_config_ops kvm_vq_configspace_ops = {
* The root device for the kvm virtio devices.
* This makes them appear as /sys/devices/kvm_s390/0,1,2 not /sys/devices/0,1,2.
*/
-static struct device kvm_root = {
- .parent = NULL,
- .bus_id = "kvm_s390",
-};
+static struct device *kvm_root;
/*
* adds a new device and register it with virtio
@@ -256,7 +254,7 @@ static void add_kvm_device(struct kvm_device_desc *d, unsigned int offset)
return;
}
- kdev->vdev.dev.parent = &kvm_root;
+ kdev->vdev.dev.parent = kvm_root;
kdev->vdev.id.device = d->type;
kdev->vdev.config = &kvm_vq_configspace_ops;
kdev->desc = d;
@@ -312,15 +310,16 @@ static int __init kvm_devices_init(void)
if (!MACHINE_IS_KVM)
return -ENODEV;
- rc = device_register(&kvm_root);
- if (rc) {
+ kvm_root = s390_root_dev_register("kvm_s390");
+ if (IS_ERR(kvm_root)) {
+ rc = PTR_ERR(kvm_root);
printk(KERN_ERR "Could not register kvm_s390 root device");
return rc;
}
rc = vmem_add_mapping(PFN_PHYS(max_pfn), PAGE_SIZE);
if (rc) {
- device_unregister(&kvm_root);
+ s390_root_dev_unregister(kvm_root);
return rc;
}
diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c
index 04a1d7bf678c..ff7bd263f33a 100644
--- a/drivers/s390/net/claw.c
+++ b/drivers/s390/net/claw.c
@@ -313,7 +313,7 @@ claw_probe(struct ccwgroup_device *cgdev)
probe_error(cgdev);
put_device(&cgdev->dev);
printk(KERN_WARNING "Out of memory %s %s Exit Line %d \n",
- cgdev->cdev[0]->dev.bus_id,__func__,__LINE__);
+ dev_name(&cgdev->cdev[0]->dev),__func__,__LINE__);
CLAW_DBF_TEXT_(2,setup,"probex%d",-ENOMEM);
return -ENOMEM;
}
@@ -323,7 +323,7 @@ claw_probe(struct ccwgroup_device *cgdev)
probe_error(cgdev);
put_device(&cgdev->dev);
printk(KERN_WARNING "Out of memory %s %s Exit Line %d \n",
- cgdev->cdev[0]->dev.bus_id,__func__,__LINE__);
+ dev_name(&cgdev->cdev[0]->dev),__func__,__LINE__);
CLAW_DBF_TEXT_(2,setup,"probex%d",-ENOMEM);
return -ENOMEM;
}
@@ -340,11 +340,12 @@ claw_probe(struct ccwgroup_device *cgdev)
probe_error(cgdev);
put_device(&cgdev->dev);
printk(KERN_WARNING "add_files failed %s %s Exit Line %d \n",
- cgdev->cdev[0]->dev.bus_id,__func__,__LINE__);
+ dev_name(&cgdev->cdev[0]->dev),__func__,__LINE__);
CLAW_DBF_TEXT_(2,setup,"probex%d",rc);
return rc;
}
- printk(KERN_INFO "claw: sysfs files added for %s\n",cgdev->cdev[0]->dev.bus_id);
+ printk(KERN_INFO "claw: sysfs files added for %s\n",
+ dev_name(&cgdev->cdev[0]->dev));
privptr->p_env->p_priv = privptr;
cgdev->cdev[0]->handler = claw_irq_handler;
cgdev->cdev[1]->handler = claw_irq_handler;
@@ -703,7 +704,8 @@ claw_irq_handler(struct ccw_device *cdev,
if (!cdev->dev.driver_data) {
printk(KERN_WARNING "claw: unsolicited interrupt for device:"
"%s received c-%02x d-%02x\n",
- cdev->dev.bus_id,irb->scsw.cstat, irb->scsw.dstat);
+ dev_name(&cdev->dev), irb->scsw.cmd.cstat,
+ irb->scsw.cmd.dstat);
#ifdef FUNCTRACE
printk(KERN_INFO "claw: %s() "
"exit on line %d\n",__func__,__LINE__);
@@ -720,7 +722,7 @@ claw_irq_handler(struct ccw_device *cdev,
p_ch = &privptr->channel[WRITE];
else {
printk(KERN_WARNING "claw: Can't determine channel for "
- "interrupt, device %s\n", cdev->dev.bus_id);
+ "interrupt, device %s\n", dev_name(&cdev->dev));
CLAW_DBF_TEXT(2,trace,"badchan");
return;
}
@@ -732,22 +734,23 @@ claw_irq_handler(struct ccw_device *cdev,
#ifdef IOTRACE
printk(KERN_INFO "%s: interrupt for device: %04x "
"received c-%02x d-%02x state-%02x\n",
- dev->name, p_ch->devno, irb->scsw.cstat,
- irb->scsw.dstat, p_ch->claw_state);
+ dev->name, p_ch->devno, irb->scsw.cmd.cstat,
+ irb->scsw.cmd.dstat, p_ch->claw_state);
#endif
/* Copy interruption response block. */
memcpy(p_ch->irb, irb, sizeof(struct irb));
/* Check for good subchannel return code, otherwise error message */
- if (irb->scsw.cstat && !(irb->scsw.cstat & SCHN_STAT_PCI)) {
+ if (irb->scsw.cmd.cstat && !(irb->scsw.cmd.cstat & SCHN_STAT_PCI)) {
printk(KERN_INFO "%s: subchannel check for device: %04x -"
" Sch Stat %02x Dev Stat %02x CPA - %04x\n",
dev->name, p_ch->devno,
- irb->scsw.cstat, irb->scsw.dstat,irb->scsw.cpa);
+ irb->scsw.cmd.cstat, irb->scsw.cmd.dstat,
+ irb->scsw.cmd.cpa);
#ifdef IOTRACE
dumpit((char *)irb,sizeof(struct irb));
- dumpit((char *)(unsigned long)irb->scsw.cpa,
+ dumpit((char *)(unsigned long)irb->scsw.cmd.cpa,
sizeof(struct ccw1));
#endif
#ifdef FUNCTRACE
@@ -759,22 +762,24 @@ claw_irq_handler(struct ccw_device *cdev,
}
/* Check the reason-code of a unit check */
- if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK)
ccw_check_unit_check(p_ch, irb->ecw[0]);
- }
/* State machine to bring the connection up, down and to restart */
- p_ch->last_dstat = irb->scsw.dstat;
+ p_ch->last_dstat = irb->scsw.cmd.dstat;
switch (p_ch->claw_state) {
case CLAW_STOP:/* HALT_IO by claw_release (halt sequence) */
#ifdef DEBUGMSG
printk(KERN_INFO "%s: CLAW_STOP enter\n", dev->name);
#endif
- if (!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
- (p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
- (p_ch->irb->scsw.stctl ==
- (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
+ if (!((p_ch->irb->scsw.cmd.stctl &
+ SCSW_STCTL_SEC_STATUS) ||
+ (p_ch->irb->scsw.cmd.stctl ==
+ SCSW_STCTL_STATUS_PEND) ||
+ (p_ch->irb->scsw.cmd.stctl ==
+ (SCSW_STCTL_ALERT_STATUS |
+ SCSW_STCTL_STATUS_PEND)))) {
#ifdef FUNCTRACE
printk(KERN_INFO "%s:%s Exit on line %d\n",
dev->name,__func__,__LINE__);
@@ -798,10 +803,13 @@ claw_irq_handler(struct ccw_device *cdev,
printk(KERN_INFO "%s: process CLAW_STAT_HALT_IO\n",
dev->name);
#endif
- if (!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
- (p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
- (p_ch->irb->scsw.stctl ==
- (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
+ if (!((p_ch->irb->scsw.cmd.stctl &
+ SCSW_STCTL_SEC_STATUS) ||
+ (p_ch->irb->scsw.cmd.stctl ==
+ SCSW_STCTL_STATUS_PEND) ||
+ (p_ch->irb->scsw.cmd.stctl ==
+ (SCSW_STCTL_ALERT_STATUS |
+ SCSW_STCTL_STATUS_PEND)))) {
#ifdef FUNCTRACE
printk(KERN_INFO "%s:%s Exit on line %d\n",
dev->name,__func__,__LINE__);
@@ -827,9 +835,9 @@ claw_irq_handler(struct ccw_device *cdev,
printk(KERN_WARNING "claw: unsolicited "
"interrupt for device:"
"%s received c-%02x d-%02x\n",
- cdev->dev.bus_id,
- irb->scsw.cstat,
- irb->scsw.dstat);
+ dev_name(&cdev->dev),
+ irb->scsw.cmd.cstat,
+ irb->scsw.cmd.dstat);
return;
}
#ifdef DEBUGMSG
@@ -844,7 +852,7 @@ claw_irq_handler(struct ccw_device *cdev,
return;
case CLAW_START_READ:
CLAW_DBF_TEXT(4,trace,"ReadIRQ");
- if (p_ch->irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+ if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
clear_bit(0, (void *)&p_ch->IO_active);
if ((p_ch->irb->ecw[0] & 0x41) == 0x41 ||
(p_ch->irb->ecw[0] & 0x40) == 0x40 ||
@@ -863,8 +871,8 @@ claw_irq_handler(struct ccw_device *cdev,
CLAW_DBF_TEXT(4,trace,"notrdy");
return;
}
- if ((p_ch->irb->scsw.cstat & SCHN_STAT_PCI) &&
- (p_ch->irb->scsw.dstat==0)) {
+ if ((p_ch->irb->scsw.cmd.cstat & SCHN_STAT_PCI) &&
+ (p_ch->irb->scsw.cmd.dstat == 0)) {
if (test_and_set_bit(CLAW_BH_ACTIVE,
(void *)&p_ch->flag_a) == 0) {
tasklet_schedule(&p_ch->tasklet);
@@ -879,10 +887,13 @@ claw_irq_handler(struct ccw_device *cdev,
CLAW_DBF_TEXT(4,trace,"PCI_read");
return;
}
- if(!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
- (p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
- (p_ch->irb->scsw.stctl ==
- (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
+ if (!((p_ch->irb->scsw.cmd.stctl &
+ SCSW_STCTL_SEC_STATUS) ||
+ (p_ch->irb->scsw.cmd.stctl ==
+ SCSW_STCTL_STATUS_PEND) ||
+ (p_ch->irb->scsw.cmd.stctl ==
+ (SCSW_STCTL_ALERT_STATUS |
+ SCSW_STCTL_STATUS_PEND)))) {
#ifdef FUNCTRACE
printk(KERN_INFO "%s:%s Exit on line %d\n",
dev->name,__func__,__LINE__);
@@ -911,7 +922,7 @@ claw_irq_handler(struct ccw_device *cdev,
CLAW_DBF_TEXT(4,trace,"RdIRQXit");
return;
case CLAW_START_WRITE:
- if (p_ch->irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+ if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
printk(KERN_INFO "%s: Unit Check Occured in "
"write channel\n",dev->name);
clear_bit(0, (void *)&p_ch->IO_active);
@@ -934,16 +945,19 @@ claw_irq_handler(struct ccw_device *cdev,
CLAW_DBF_TEXT(4,trace,"rstrtwrt");
return;
}
- if (p_ch->irb->scsw.dstat & DEV_STAT_UNIT_EXCEP) {
+ if (p_ch->irb->scsw.cmd.dstat & DEV_STAT_UNIT_EXCEP) {
clear_bit(0, (void *)&p_ch->IO_active);
printk(KERN_INFO "%s: Unit Exception "
"Occured in write channel\n",
dev->name);
}
- if(!((p_ch->irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
- (p_ch->irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
- (p_ch->irb->scsw.stctl ==
- (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))) {
+ if (!((p_ch->irb->scsw.cmd.stctl &
+ SCSW_STCTL_SEC_STATUS) ||
+ (p_ch->irb->scsw.cmd.stctl ==
+ SCSW_STCTL_STATUS_PEND) ||
+ (p_ch->irb->scsw.cmd.stctl ==
+ (SCSW_STCTL_ALERT_STATUS |
+ SCSW_STCTL_STATUS_PEND)))) {
#ifdef FUNCTRACE
printk(KERN_INFO "%s:%s Exit on line %d\n",
dev->name,__func__,__LINE__);
@@ -1483,7 +1497,7 @@ ccw_check_return_code(struct ccw_device *cdev, int return_code)
{
#ifdef FUNCTRACE
printk(KERN_INFO "%s: %s() > enter \n",
- cdev->dev.bus_id,__func__);
+ dev_name(&cdev->dev),__func__);
#endif
CLAW_DBF_TEXT(4,trace,"ccwret");
#ifdef DEBUGMSG
@@ -1495,28 +1509,28 @@ ccw_check_return_code(struct ccw_device *cdev, int return_code)
switch (return_code) {
case -EBUSY:
printk(KERN_INFO "%s: Busy !\n",
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
break;
case -ENODEV:
printk(KERN_EMERG "%s: Missing device called "
- "for IO ENODEV\n", cdev->dev.bus_id);
+ "for IO ENODEV\n", dev_name(&cdev->dev));
break;
case -EIO:
printk(KERN_EMERG "%s: Status pending... EIO \n",
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
break;
case -EINVAL:
printk(KERN_EMERG "%s: Invalid Dev State EINVAL \n",
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
break;
default:
printk(KERN_EMERG "%s: Unknown error in "
- "Do_IO %d\n",cdev->dev.bus_id, return_code);
+ "Do_IO %d\n",dev_name(&cdev->dev), return_code);
}
}
#ifdef FUNCTRACE
printk(KERN_INFO "%s: %s() > exit on line %d\n",
- cdev->dev.bus_id,__func__,__LINE__);
+ dev_name(&cdev->dev),__func__,__LINE__);
#endif
CLAW_DBF_TEXT(4,trace,"ccwret");
} /* end of ccw_check_return_code */
@@ -3909,13 +3923,13 @@ add_channel(struct ccw_device *cdev,int i,struct claw_privbk *privptr)
struct ccw_dev_id dev_id;
#ifdef FUNCTRACE
- printk(KERN_INFO "%s:%s Enter\n",cdev->dev.bus_id,__func__);
+ printk(KERN_INFO "%s:%s Enter\n",dev_name(&cdev->dev),__func__);
#endif
- CLAW_DBF_TEXT_(2,setup,"%s",cdev->dev.bus_id);
+ CLAW_DBF_TEXT_(2,setup,"%s",dev_name(&cdev->dev));
privptr->channel[i].flag = i+1; /* Read is 1 Write is 2 */
p_ch = &privptr->channel[i];
p_ch->cdev = cdev;
- snprintf(p_ch->id, CLAW_ID_SIZE, "cl-%s", cdev->dev.bus_id);
+ snprintf(p_ch->id, CLAW_ID_SIZE, "cl-%s", dev_name(&cdev->dev));
ccw_device_get_id(cdev, &dev_id);
p_ch->devno = dev_id.devno;
if ((p_ch->irb = kzalloc(sizeof (struct irb),GFP_KERNEL)) == NULL) {
@@ -3929,7 +3943,7 @@ add_channel(struct ccw_device *cdev,int i,struct claw_privbk *privptr)
}
#ifdef FUNCTRACE
printk(KERN_INFO "%s:%s Exit on line %d\n",
- cdev->dev.bus_id,__func__,__LINE__);
+ dev_name(&cdev->dev),__func__,__LINE__);
#endif
return 0;
}
@@ -3953,7 +3967,8 @@ claw_new_device(struct ccwgroup_device *cgdev)
struct ccw_dev_id dev_id;
pr_debug("%s() called\n", __func__);
- printk(KERN_INFO "claw: add for %s\n",cgdev->cdev[READ]->dev.bus_id);
+ printk(KERN_INFO "claw: add for %s\n",
+ dev_name(&cgdev->cdev[READ]->dev));
CLAW_DBF_TEXT(2,setup,"new_dev");
privptr = cgdev->dev.driver_data;
cgdev->cdev[READ]->dev.driver_data = privptr;
@@ -3978,14 +3993,14 @@ claw_new_device(struct ccwgroup_device *cgdev)
if (ret != 0) {
printk(KERN_WARNING
"claw: ccw_device_set_online %s READ failed "
- "with ret = %d\n",cgdev->cdev[READ]->dev.bus_id,ret);
+ "with ret = %d\n", dev_name(&cgdev->cdev[READ]->dev),ret);
goto out;
}
ret = ccw_device_set_online(cgdev->cdev[WRITE]);
if (ret != 0) {
printk(KERN_WARNING
"claw: ccw_device_set_online %s WRITE failed "
- "with ret = %d\n",cgdev->cdev[WRITE]->dev.bus_id, ret);
+ "with ret = %d\n", dev_name(&cgdev->cdev[WRITE]->dev), ret);
goto out;
}
dev = alloc_netdev(0,"claw%d",claw_init_netdevice);
@@ -4066,7 +4081,7 @@ claw_shutdown_device(struct ccwgroup_device *cgdev)
int ret;
pr_debug("%s() called\n", __func__);
- CLAW_DBF_TEXT_(2,setup,"%s",cgdev->dev.bus_id);
+ CLAW_DBF_TEXT_(2,setup,"%s", dev_name(&cgdev->dev));
priv = cgdev->dev.driver_data;
if (!priv)
return -ENODEV;
@@ -4096,14 +4111,14 @@ claw_remove_device(struct ccwgroup_device *cgdev)
struct claw_privbk *priv;
pr_debug("%s() called\n", __func__);
- CLAW_DBF_TEXT_(2,setup,"%s",cgdev->dev.bus_id);
+ CLAW_DBF_TEXT_(2,setup,"%s", dev_name(&cgdev->dev));
priv = cgdev->dev.driver_data;
if (!priv) {
printk(KERN_WARNING "claw: %s() no Priv exiting\n",__func__);
return;
}
printk(KERN_INFO "claw: %s() called %s will be removed.\n",
- __func__,cgdev->cdev[0]->dev.bus_id);
+ __func__, dev_name(&cgdev->cdev[0]->dev));
if (cgdev->state == CCWGROUP_ONLINE)
claw_shutdown_device(cgdev);
claw_remove_files(&cgdev->dev);
diff --git a/drivers/s390/net/claw.h b/drivers/s390/net/claw.h
index 1a89d989f348..71c967fbaca3 100644
--- a/drivers/s390/net/claw.h
+++ b/drivers/s390/net/claw.h
@@ -85,7 +85,7 @@
#define CLAW_MAX_DEV 256 /* max claw devices */
#define MAX_NAME_LEN 8 /* host name, adapter name length */
#define CLAW_FRAME_SIZE 4096
-#define CLAW_ID_SIZE BUS_ID_SIZE+3
+#define CLAW_ID_SIZE 20+3
/* state machine codes used in claw_irq_handler */
diff --git a/drivers/s390/net/ctcm_fsms.c b/drivers/s390/net/ctcm_fsms.c
index 2a106f3a076d..7e6bd387f4d8 100644
--- a/drivers/s390/net/ctcm_fsms.c
+++ b/drivers/s390/net/ctcm_fsms.c
@@ -257,9 +257,9 @@ static void chx_txdone(fsm_instance *fi, int event, void *arg)
if (duration > ch->prof.tx_time)
ch->prof.tx_time = duration;
- if (ch->irb->scsw.count != 0)
+ if (ch->irb->scsw.cmd.count != 0)
ctcm_pr_debug("%s: TX not complete, remaining %d bytes\n",
- dev->name, ch->irb->scsw.count);
+ dev->name, ch->irb->scsw.cmd.count);
fsm_deltimer(&ch->timer);
while ((skb = skb_dequeue(&ch->io_queue))) {
priv->stats.tx_packets++;
@@ -353,7 +353,7 @@ static void chx_rx(fsm_instance *fi, int event, void *arg)
struct channel *ch = arg;
struct net_device *dev = ch->netdev;
struct ctcm_priv *priv = dev->priv;
- int len = ch->max_bufsize - ch->irb->scsw.count;
+ int len = ch->max_bufsize - ch->irb->scsw.cmd.count;
struct sk_buff *skb = ch->trans_skb;
__u16 block_len = *((__u16 *)skb->data);
int check_len;
@@ -1234,9 +1234,9 @@ static void ctcmpc_chx_txdone(fsm_instance *fi, int event, void *arg)
if (duration > ch->prof.tx_time)
ch->prof.tx_time = duration;
- if (ch->irb->scsw.count != 0)
+ if (ch->irb->scsw.cmd.count != 0)
ctcm_pr_debug("%s: TX not complete, remaining %d bytes\n",
- dev->name, ch->irb->scsw.count);
+ dev->name, ch->irb->scsw.cmd.count);
fsm_deltimer(&ch->timer);
while ((skb = skb_dequeue(&ch->io_queue))) {
priv->stats.tx_packets++;
@@ -1394,7 +1394,7 @@ static void ctcmpc_chx_rx(fsm_instance *fi, int event, void *arg)
struct sk_buff *skb = ch->trans_skb;
struct sk_buff *new_skb;
unsigned long saveflags = 0; /* avoids compiler warning */
- int len = ch->max_bufsize - ch->irb->scsw.count;
+ int len = ch->max_bufsize - ch->irb->scsw.cmd.count;
if (do_debug_data) {
CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, "mpc_ch_rx %s cp:%i %s\n",
diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c
index d52843da4f55..30e525817e89 100644
--- a/drivers/s390/net/ctcm_main.c
+++ b/drivers/s390/net/ctcm_main.c
@@ -284,18 +284,18 @@ static long ctcm_check_irb_error(struct ccw_device *cdev, struct irb *irb)
return 0;
CTCM_DBF_TEXT_(ERROR, CTC_DBF_WARN, "irb error %ld on device %s\n",
- PTR_ERR(irb), cdev->dev.bus_id);
+ PTR_ERR(irb), dev_name(&cdev->dev));
switch (PTR_ERR(irb)) {
case -EIO:
- ctcm_pr_warn("i/o-error on device %s\n", cdev->dev.bus_id);
+ ctcm_pr_warn("i/o-error on device %s\n", dev_name(&cdev->dev));
break;
case -ETIMEDOUT:
- ctcm_pr_warn("timeout on device %s\n", cdev->dev.bus_id);
+ ctcm_pr_warn("timeout on device %s\n", dev_name(&cdev->dev));
break;
default:
ctcm_pr_warn("unknown error %ld on device %s\n",
- PTR_ERR(irb), cdev->dev.bus_id);
+ PTR_ERR(irb), dev_name(&cdev->dev));
}
return PTR_ERR(irb);
}
@@ -1236,8 +1236,8 @@ static void ctcm_irq_handler(struct ccw_device *cdev,
/* Check for unsolicited interrupts. */
if (cgdev == NULL) {
ctcm_pr_warn("ctcm: Got unsolicited irq: %s c-%02x d-%02x\n",
- cdev->dev.bus_id, irb->scsw.cstat,
- irb->scsw.dstat);
+ dev_name(&cdev->dev), irb->scsw.cmd.cstat,
+ irb->scsw.cmd.dstat);
return;
}
@@ -1250,14 +1250,14 @@ static void ctcm_irq_handler(struct ccw_device *cdev,
ch = priv->channel[WRITE];
else {
ctcm_pr_err("ctcm: Can't determine channel for interrupt, "
- "device %s\n", cdev->dev.bus_id);
+ "device %s\n", dev_name(&cdev->dev));
return;
}
dev = (struct net_device *)(ch->netdev);
if (dev == NULL) {
ctcm_pr_crit("ctcm: %s dev=NULL bus_id=%s, ch=0x%p\n",
- __FUNCTION__, cdev->dev.bus_id, ch);
+ __FUNCTION__, dev_name(&cdev->dev), ch);
return;
}
@@ -1266,40 +1266,40 @@ static void ctcm_irq_handler(struct ccw_device *cdev,
"received c-%02x d-%02x\n",
dev->name,
ch->id,
- irb->scsw.cstat,
- irb->scsw.dstat);
+ irb->scsw.cmd.cstat,
+ irb->scsw.cmd.dstat);
/* Copy interruption response block. */
memcpy(ch->irb, irb, sizeof(struct irb));
/* Check for good subchannel return code, otherwise error message */
- if (irb->scsw.cstat) {
+ if (irb->scsw.cmd.cstat) {
fsm_event(ch->fsm, CTC_EVENT_SC_UNKNOWN, ch);
ctcm_pr_warn("%s: subchannel check for dev: %s - %02x %02x\n",
- dev->name, ch->id, irb->scsw.cstat,
- irb->scsw.dstat);
+ dev->name, ch->id, irb->scsw.cmd.cstat,
+ irb->scsw.cmd.dstat);
return;
}
/* Check the reason-code of a unit check */
- if (irb->scsw.dstat & DEV_STAT_UNIT_CHECK) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) {
ccw_unit_check(ch, irb->ecw[0]);
return;
}
- if (irb->scsw.dstat & DEV_STAT_BUSY) {
- if (irb->scsw.dstat & DEV_STAT_ATTENTION)
+ if (irb->scsw.cmd.dstat & DEV_STAT_BUSY) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION)
fsm_event(ch->fsm, CTC_EVENT_ATTNBUSY, ch);
else
fsm_event(ch->fsm, CTC_EVENT_BUSY, ch);
return;
}
- if (irb->scsw.dstat & DEV_STAT_ATTENTION) {
+ if (irb->scsw.cmd.dstat & DEV_STAT_ATTENTION) {
fsm_event(ch->fsm, CTC_EVENT_ATTN, ch);
return;
}
- if ((irb->scsw.stctl & SCSW_STCTL_SEC_STATUS) ||
- (irb->scsw.stctl == SCSW_STCTL_STATUS_PEND) ||
- (irb->scsw.stctl ==
+ if ((irb->scsw.cmd.stctl & SCSW_STCTL_SEC_STATUS) ||
+ (irb->scsw.cmd.stctl == SCSW_STCTL_STATUS_PEND) ||
+ (irb->scsw.cmd.stctl ==
(SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)))
fsm_event(ch->fsm, CTC_EVENT_FINSTAT, ch);
else
@@ -1392,7 +1392,7 @@ static int add_channel(struct ccw_device *cdev, enum channel_types type,
goto nomem_return;
ch->cdev = cdev;
- snprintf(ch->id, CTCM_ID_SIZE, "ch-%s", cdev->dev.bus_id);
+ snprintf(ch->id, CTCM_ID_SIZE, "ch-%s", dev_name(&cdev->dev));
ch->type = type;
/**
@@ -1550,8 +1550,10 @@ static int ctcm_new_device(struct ccwgroup_device *cgdev)
type = get_channel_type(&cgdev->cdev[0]->id);
- snprintf(read_id, CTCM_ID_SIZE, "ch-%s", cgdev->cdev[0]->dev.bus_id);
- snprintf(write_id, CTCM_ID_SIZE, "ch-%s", cgdev->cdev[1]->dev.bus_id);
+ snprintf(read_id, CTCM_ID_SIZE, "ch-%s",
+ dev_name(&cgdev->cdev[0]->dev));
+ snprintf(write_id, CTCM_ID_SIZE, "ch-%s",
+ dev_name(&cgdev->cdev[1]->dev));
ret = add_channel(cgdev->cdev[0], type, priv);
if (ret)
diff --git a/drivers/s390/net/ctcm_main.h b/drivers/s390/net/ctcm_main.h
index 95b0c0b6ebc6..50d34f31573e 100644
--- a/drivers/s390/net/ctcm_main.h
+++ b/drivers/s390/net/ctcm_main.h
@@ -80,7 +80,7 @@
#define READ 0
#define WRITE 1
-#define CTCM_ID_SIZE BUS_ID_SIZE+3
+#define CTCM_ID_SIZE 20+3
struct ctcm_profile {
unsigned long maxmulti;
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c
index dd22f4b37037..1c4a33d4e31a 100644
--- a/drivers/s390/net/lcs.c
+++ b/drivers/s390/net/lcs.c
@@ -492,7 +492,7 @@ lcs_start_channel(struct lcs_channel *channel)
unsigned long flags;
int rc;
- LCS_DBF_TEXT_(4,trace,"ssch%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(4,trace,"ssch%s", dev_name(&channel->ccwdev->dev));
spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
rc = ccw_device_start(channel->ccwdev,
channel->ccws + channel->io_idx, 0, 0,
@@ -501,7 +501,7 @@ lcs_start_channel(struct lcs_channel *channel)
channel->state = LCS_CH_STATE_RUNNING;
spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
if (rc) {
- LCS_DBF_TEXT_(4,trace,"essh%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(4,trace,"essh%s", dev_name(&channel->ccwdev->dev));
PRINT_ERR("Error in starting channel, rc=%d!\n", rc);
}
return rc;
@@ -514,12 +514,12 @@ lcs_clear_channel(struct lcs_channel *channel)
int rc;
LCS_DBF_TEXT(4,trace,"clearch");
- LCS_DBF_TEXT_(4,trace,"%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(4,trace,"%s", dev_name(&channel->ccwdev->dev));
spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
rc = ccw_device_clear(channel->ccwdev, (addr_t) channel);
spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
if (rc) {
- LCS_DBF_TEXT_(4,trace,"ecsc%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(4,trace,"ecsc%s", dev_name(&channel->ccwdev->dev));
return rc;
}
wait_event(channel->wait_q, (channel->state == LCS_CH_STATE_CLEARED));
@@ -540,13 +540,13 @@ lcs_stop_channel(struct lcs_channel *channel)
if (channel->state == LCS_CH_STATE_STOPPED)
return 0;
LCS_DBF_TEXT(4,trace,"haltsch");
- LCS_DBF_TEXT_(4,trace,"%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(4,trace,"%s", dev_name(&channel->ccwdev->dev));
channel->state = LCS_CH_STATE_INIT;
spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
rc = ccw_device_halt(channel->ccwdev, (addr_t) channel);
spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
if (rc) {
- LCS_DBF_TEXT_(4,trace,"ehsc%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(4,trace,"ehsc%s", dev_name(&channel->ccwdev->dev));
return rc;
}
/* Asynchronous halt initialted. Wait for its completion. */
@@ -632,10 +632,10 @@ __lcs_resume_channel(struct lcs_channel *channel)
return 0;
if (channel->ccws[channel->io_idx].flags & CCW_FLAG_SUSPEND)
return 0;
- LCS_DBF_TEXT_(5, trace, "rsch%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(5, trace, "rsch%s", dev_name(&channel->ccwdev->dev));
rc = ccw_device_resume(channel->ccwdev);
if (rc) {
- LCS_DBF_TEXT_(4, trace, "ersc%s", channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(4, trace, "ersc%s", dev_name(&channel->ccwdev->dev));
PRINT_ERR("Error in lcs_resume_channel: rc=%d\n",rc);
} else
channel->state = LCS_CH_STATE_RUNNING;
@@ -1302,18 +1302,18 @@ lcs_check_irb_error(struct ccw_device *cdev, struct irb *irb)
switch (PTR_ERR(irb)) {
case -EIO:
- PRINT_WARN("i/o-error on device %s\n", cdev->dev.bus_id);
+ PRINT_WARN("i/o-error on device %s\n", dev_name(&cdev->dev));
LCS_DBF_TEXT(2, trace, "ckirberr");
LCS_DBF_TEXT_(2, trace, " rc%d", -EIO);
break;
case -ETIMEDOUT:
- PRINT_WARN("timeout on device %s\n", cdev->dev.bus_id);
+ PRINT_WARN("timeout on device %s\n", dev_name(&cdev->dev));
LCS_DBF_TEXT(2, trace, "ckirberr");
LCS_DBF_TEXT_(2, trace, " rc%d", -ETIMEDOUT);
break;
default:
PRINT_WARN("unknown error %ld on device %s\n", PTR_ERR(irb),
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
LCS_DBF_TEXT(2, trace, "ckirberr");
LCS_DBF_TEXT(2, trace, " rc???");
}
@@ -1327,8 +1327,8 @@ lcs_get_problem(struct ccw_device *cdev, struct irb *irb)
char *sense;
sense = (char *) irb->ecw;
- cstat = irb->scsw.cstat;
- dstat = irb->scsw.dstat;
+ cstat = irb->scsw.cmd.cstat;
+ dstat = irb->scsw.cmd.dstat;
if (cstat & (SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK |
SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHAIN_CHECK |
@@ -1388,17 +1388,19 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
else
channel = &card->write;
- cstat = irb->scsw.cstat;
- dstat = irb->scsw.dstat;
- LCS_DBF_TEXT_(5, trace, "Rint%s",cdev->dev.bus_id);
- LCS_DBF_TEXT_(5, trace, "%4x%4x",irb->scsw.cstat, irb->scsw.dstat);
- LCS_DBF_TEXT_(5, trace, "%4x%4x",irb->scsw.fctl, irb->scsw.actl);
+ cstat = irb->scsw.cmd.cstat;
+ dstat = irb->scsw.cmd.dstat;
+ LCS_DBF_TEXT_(5, trace, "Rint%s",dev_name(&cdev->dev));
+ LCS_DBF_TEXT_(5, trace, "%4x%4x", irb->scsw.cmd.cstat,
+ irb->scsw.cmd.dstat);
+ LCS_DBF_TEXT_(5, trace, "%4x%4x", irb->scsw.cmd.fctl,
+ irb->scsw.cmd.actl);
/* Check for channel and device errors presented */
rc = lcs_get_problem(cdev, irb);
if (rc || (dstat & DEV_STAT_UNIT_EXCEP)) {
PRINT_WARN("check on device %s, dstat=0x%X, cstat=0x%X \n",
- cdev->dev.bus_id, dstat, cstat);
+ dev_name(&cdev->dev), dstat, cstat);
if (rc) {
channel->state = LCS_CH_STATE_ERROR;
}
@@ -1410,11 +1412,11 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
}
/* How far in the ccw chain have we processed? */
if ((channel->state != LCS_CH_STATE_INIT) &&
- (irb->scsw.fctl & SCSW_FCTL_START_FUNC)) {
- index = (struct ccw1 *) __va((addr_t) irb->scsw.cpa)
+ (irb->scsw.cmd.fctl & SCSW_FCTL_START_FUNC)) {
+ index = (struct ccw1 *) __va((addr_t) irb->scsw.cmd.cpa)
- channel->ccws;
- if ((irb->scsw.actl & SCSW_ACTL_SUSPENDED) ||
- (irb->scsw.cstat & SCHN_STAT_PCI))
+ if ((irb->scsw.cmd.actl & SCSW_ACTL_SUSPENDED) ||
+ (irb->scsw.cmd.cstat & SCHN_STAT_PCI))
/* Bloody io subsystem tells us lies about cpa... */
index = (index - 1) & (LCS_NUM_BUFFS - 1);
while (channel->io_idx != index) {
@@ -1425,25 +1427,24 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
}
}
- if ((irb->scsw.dstat & DEV_STAT_DEV_END) ||
- (irb->scsw.dstat & DEV_STAT_CHN_END) ||
- (irb->scsw.dstat & DEV_STAT_UNIT_CHECK))
+ if ((irb->scsw.cmd.dstat & DEV_STAT_DEV_END) ||
+ (irb->scsw.cmd.dstat & DEV_STAT_CHN_END) ||
+ (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK))
/* Mark channel as stopped. */
channel->state = LCS_CH_STATE_STOPPED;
- else if (irb->scsw.actl & SCSW_ACTL_SUSPENDED)
+ else if (irb->scsw.cmd.actl & SCSW_ACTL_SUSPENDED)
/* CCW execution stopped on a suspend bit. */
channel->state = LCS_CH_STATE_SUSPENDED;
- if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC) {
- if (irb->scsw.cc != 0) {
+ if (irb->scsw.cmd.fctl & SCSW_FCTL_HALT_FUNC) {
+ if (irb->scsw.cmd.cc != 0) {
ccw_device_halt(channel->ccwdev, (addr_t) channel);
return;
}
/* The channel has been stopped by halt_IO. */
channel->state = LCS_CH_STATE_HALTED;
}
- if (irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) {
+ if (irb->scsw.cmd.fctl & SCSW_FCTL_CLEAR_FUNC)
channel->state = LCS_CH_STATE_CLEARED;
- }
/* Do the rest in the tasklet. */
tasklet_schedule(&channel->irq_tasklet);
}
@@ -1461,7 +1462,7 @@ lcs_tasklet(unsigned long data)
int rc;
channel = (struct lcs_channel *) data;
- LCS_DBF_TEXT_(5, trace, "tlet%s",channel->ccwdev->dev.bus_id);
+ LCS_DBF_TEXT_(5, trace, "tlet%s",dev_name(&channel->ccwdev->dev));
/* Check for processed buffers. */
iob = channel->iob;
@@ -2244,7 +2245,7 @@ lcs_recovery(void *ptr)
return 0;
LCS_DBF_TEXT(4, trace, "recover2");
gdev = card->gdev;
- PRINT_WARN("Recovery of device %s started...\n", gdev->dev.bus_id);
+ PRINT_WARN("Recovery of device %s started...\n", dev_name(&gdev->dev));
rc = __lcs_shutdown_device(gdev, 1);
rc = lcs_new_device(gdev);
if (!rc)
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c
index e4ba6a0372ac..6dd816943d38 100644
--- a/drivers/s390/net/netiucv.c
+++ b/drivers/s390/net/netiucv.c
@@ -1422,7 +1422,7 @@ static ssize_t user_write(struct device *dev, struct device_attribute *attr,
(ndev->flags & (IFF_UP | IFF_RUNNING))) {
/* username changed while the interface is active. */
PRINT_WARN("netiucv: device %s active, connected to %s\n",
- dev->bus_id, priv->conn->userid);
+ dev_name(dev), priv->conn->userid);
PRINT_WARN("netiucv: user cannot be updated\n");
IUCV_DBF_TEXT(setup, 2, "user_write: device active\n");
return -EBUSY;
@@ -1757,7 +1757,7 @@ static int netiucv_register_device(struct net_device *ndev)
IUCV_DBF_TEXT(trace, 3, __func__);
if (dev) {
- snprintf(dev->bus_id, BUS_ID_SIZE, "net%s", ndev->name);
+ dev_set_name(dev, "net%s", ndev->name);
dev->bus = &iucv_bus;
dev->parent = iucv_root;
/*
diff --git a/drivers/s390/net/qeth_core.h b/drivers/s390/net/qeth_core.h
index 699ac11debd8..62ec963a0be0 100644
--- a/drivers/s390/net/qeth_core.h
+++ b/drivers/s390/net/qeth_core.h
@@ -90,11 +90,11 @@ struct qeth_dbf_info {
#define CARD_RDEV(card) card->read.ccwdev
#define CARD_WDEV(card) card->write.ccwdev
#define CARD_DDEV(card) card->data.ccwdev
-#define CARD_BUS_ID(card) card->gdev->dev.bus_id
-#define CARD_RDEV_ID(card) card->read.ccwdev->dev.bus_id
-#define CARD_WDEV_ID(card) card->write.ccwdev->dev.bus_id
-#define CARD_DDEV_ID(card) card->data.ccwdev->dev.bus_id
-#define CHANNEL_ID(channel) channel->ccwdev->dev.bus_id
+#define CARD_BUS_ID(card) dev_name(&card->gdev->dev)
+#define CARD_RDEV_ID(card) dev_name(&card->read.ccwdev->dev)
+#define CARD_WDEV_ID(card) dev_name(&card->write.ccwdev->dev)
+#define CARD_DDEV_ID(card) dev_name(&card->data.ccwdev->dev)
+#define CHANNEL_ID(channel) dev_name(&channel->ccwdev->dev)
/**
* card stuff
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 436bf1f6d4a6..114c6f5ad7dd 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -290,9 +290,6 @@ int qeth_set_large_send(struct qeth_card *card,
card->dev->features |= NETIF_F_TSO | NETIF_F_SG |
NETIF_F_HW_CSUM;
} else {
- PRINT_WARN("TSO not supported on %s. "
- "large_send set to 'no'.\n",
- card->dev->name);
card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG |
NETIF_F_HW_CSUM);
card->options.large_send = QETH_LARGE_SEND_NO;
@@ -738,15 +735,15 @@ static int qeth_get_problem(struct ccw_device *cdev, struct irb *irb)
char *sense;
sense = (char *) irb->ecw;
- cstat = irb->scsw.cstat;
- dstat = irb->scsw.dstat;
+ cstat = irb->scsw.cmd.cstat;
+ dstat = irb->scsw.cmd.dstat;
if (cstat & (SCHN_STAT_CHN_CTRL_CHK | SCHN_STAT_INTF_CTRL_CHK |
SCHN_STAT_CHN_DATA_CHK | SCHN_STAT_CHAIN_CHECK |
SCHN_STAT_PROT_CHECK | SCHN_STAT_PROG_CHECK)) {
QETH_DBF_TEXT(TRACE, 2, "CGENCHK");
PRINT_WARN("check on device %s, dstat=x%x, cstat=x%x ",
- cdev->dev.bus_id, dstat, cstat);
+ dev_name(&cdev->dev), dstat, cstat);
print_hex_dump(KERN_WARNING, "qeth: irb ", DUMP_PREFIX_OFFSET,
16, 1, irb, 64, 1);
return 1;
@@ -785,12 +782,12 @@ static long __qeth_check_irb_error(struct ccw_device *cdev,
switch (PTR_ERR(irb)) {
case -EIO:
- PRINT_WARN("i/o-error on device %s\n", cdev->dev.bus_id);
+ PRINT_WARN("i/o-error on device %s\n", dev_name(&cdev->dev));
QETH_DBF_TEXT(TRACE, 2, "ckirberr");
QETH_DBF_TEXT_(TRACE, 2, " rc%d", -EIO);
break;
case -ETIMEDOUT:
- PRINT_WARN("timeout on device %s\n", cdev->dev.bus_id);
+ PRINT_WARN("timeout on device %s\n", dev_name(&cdev->dev));
QETH_DBF_TEXT(TRACE, 2, "ckirberr");
QETH_DBF_TEXT_(TRACE, 2, " rc%d", -ETIMEDOUT);
if (intparm == QETH_RCD_PARM) {
@@ -804,7 +801,7 @@ static long __qeth_check_irb_error(struct ccw_device *cdev,
break;
default:
PRINT_WARN("unknown error %ld on device %s\n", PTR_ERR(irb),
- cdev->dev.bus_id);
+ dev_name(&cdev->dev));
QETH_DBF_TEXT(TRACE, 2, "ckirberr");
QETH_DBF_TEXT(TRACE, 2, " rc???");
}
@@ -826,8 +823,8 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm,
if (__qeth_check_irb_error(cdev, intparm, irb))
return;
- cstat = irb->scsw.cstat;
- dstat = irb->scsw.dstat;
+ cstat = irb->scsw.cmd.cstat;
+ dstat = irb->scsw.cmd.dstat;
card = CARD_FROM_CDEV(cdev);
if (!card)
@@ -845,10 +842,10 @@ static void qeth_irq(struct ccw_device *cdev, unsigned long intparm,
}
atomic_set(&channel->irq_pending, 0);
- if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC))
+ if (irb->scsw.cmd.fctl & (SCSW_FCTL_CLEAR_FUNC))
channel->state = CH_STATE_STOPPED;
- if (irb->scsw.fctl & (SCSW_FCTL_HALT_FUNC))
+ if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC))
channel->state = CH_STATE_HALTED;
/*let's wake up immediately on data channel*/
@@ -1407,12 +1404,6 @@ static void qeth_init_func_level(struct qeth_card *card)
}
}
-static inline __u16 qeth_raw_devno_from_bus_id(char *id)
-{
- id += (strlen(id) - 4);
- return (__u16) simple_strtoul(id, &id, 16);
-}
-
static int qeth_idx_activate_get_answer(struct qeth_channel *channel,
void (*idx_reply_cb)(struct qeth_channel *,
struct qeth_cmd_buffer *))
@@ -1439,7 +1430,7 @@ static int qeth_idx_activate_get_answer(struct qeth_channel *channel,
spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
if (rc) {
- PRINT_ERR("Error2 in activating channel rc=%d\n", rc);
+ QETH_DBF_MESSAGE(2, "Error2 in activating channel rc=%d\n", rc);
QETH_DBF_TEXT_(SETUP, 2, "2err%d", rc);
atomic_set(&channel->irq_pending, 0);
wake_up(&card->wait_q);
@@ -1468,6 +1459,7 @@ static int qeth_idx_activate_channel(struct qeth_channel *channel,
__u16 temp;
__u8 tmp;
int rc;
+ struct ccw_dev_id temp_devid;
card = CARD_FROM_CDEV(channel->ccwdev);
@@ -1494,8 +1486,8 @@ static int qeth_idx_activate_channel(struct qeth_channel *channel,
&card->token.issuer_rm_w, QETH_MPC_TOKEN_LENGTH);
memcpy(QETH_IDX_ACT_FUNC_LEVEL(iob->data),
&card->info.func_level, sizeof(__u16));
- temp = qeth_raw_devno_from_bus_id(CARD_DDEV_ID(card));
- memcpy(QETH_IDX_ACT_QDIO_DEV_CUA(iob->data), &temp, 2);
+ ccw_device_get_id(CARD_DDEV(card), &temp_devid);
+ memcpy(QETH_IDX_ACT_QDIO_DEV_CUA(iob->data), &temp_devid.devno, 2);
temp = (card->info.cula << 8) + card->info.unit_addr2;
memcpy(QETH_IDX_ACT_QDIO_DEV_REALADDR(iob->data), &temp, 2);
@@ -1508,7 +1500,8 @@ static int qeth_idx_activate_channel(struct qeth_channel *channel,
spin_unlock_irqrestore(get_ccwdev_lock(channel->ccwdev), flags);
if (rc) {
- PRINT_ERR("Error1 in activating channel. rc=%d\n", rc);
+ QETH_DBF_MESSAGE(2, "Error1 in activating channel. rc=%d\n",
+ rc);
QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
atomic_set(&channel->irq_pending, 0);
wake_up(&card->wait_q);
@@ -1658,7 +1651,6 @@ int qeth_send_control_data(struct qeth_card *card, int len,
reply = qeth_alloc_reply(card);
if (!reply) {
- PRINT_WARN("Could not alloc qeth_reply!\n");
return -ENOMEM;
}
reply->callback = reply_cb;
@@ -2612,15 +2604,9 @@ void qeth_queue_input_buffer(struct qeth_card *card, int index)
if (newcount < count) {
/* we are in memory shortage so we switch back to
traditional skb allocation and drop packages */
- if (!atomic_read(&card->force_alloc_skb) &&
- net_ratelimit())
- PRINT_WARN("Switch to alloc skb\n");
atomic_set(&card->force_alloc_skb, 3);
count = newcount;
} else {
- if ((atomic_read(&card->force_alloc_skb) == 1) &&
- net_ratelimit())
- PRINT_WARN("Switch to sg\n");
atomic_add_unless(&card->force_alloc_skb, -1, 0);
}
@@ -3034,7 +3020,7 @@ int qeth_get_elements_no(struct qeth_card *card, void *hdr,
elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE)
+ skb->len) >> PAGE_SHIFT);
if ((elements_needed + elems) > QETH_MAX_BUFFER_ELEMENTS(card)) {
- PRINT_ERR("Invalid size of IP packet "
+ QETH_DBF_MESSAGE(2, "Invalid size of IP packet "
"(Number=%d / Length=%d). Discarded.\n",
(elements_needed+elems), skb->len);
return 0;
@@ -3247,8 +3233,6 @@ int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
* free buffers) to handle eddp context */
if (qeth_eddp_check_buffers_for_context(queue, ctx)
< 0) {
- if (net_ratelimit())
- PRINT_WARN("eddp tx_dropped 1\n");
rc = -EBUSY;
goto out;
}
@@ -3260,7 +3244,6 @@ int qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
tmp = qeth_eddp_fill_buffer(queue, ctx,
queue->next_buf_to_fill);
if (tmp < 0) {
- PRINT_ERR("eddp tx_dropped 2\n");
rc = -EBUSY;
goto out;
}
@@ -3602,8 +3585,6 @@ int qeth_snmp_command(struct qeth_card *card, char __user *udata)
if ((!qeth_adp_supported(card, IPA_SETADP_SET_SNMP_CONTROL)) &&
(!card->options.layer2)) {
- PRINT_WARN("SNMP Query MIBS not supported "
- "on %s!\n", QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
/* skip 4 bytes (data_len struct member) to get req_len */
@@ -3634,7 +3615,7 @@ int qeth_snmp_command(struct qeth_card *card, char __user *udata)
rc = qeth_send_ipa_snmp_cmd(card, iob, QETH_SETADP_BASE_LEN + req_len,
qeth_snmp_command_cb, (void *)&qinfo);
if (rc)
- PRINT_WARN("SNMP command failed on %s: (0x%x)\n",
+ QETH_DBF_MESSAGE(2, "SNMP command failed on %s: (0x%x)\n",
QETH_CARD_IFNAME(card), rc);
else {
if (copy_to_user(udata, qinfo.udata, qinfo.udata_len))
@@ -3807,8 +3788,8 @@ retry:
if (mpno)
mpno = min(mpno - 1, QETH_MAX_PORTNO);
if (card->info.portno > mpno) {
- PRINT_ERR("Device %s does not offer port number %d \n.",
- CARD_BUS_ID(card), card->info.portno);
+ QETH_DBF_MESSAGE(2, "Device %s does not offer port number %d"
+ "\n.", CARD_BUS_ID(card), card->info.portno);
rc = -ENODEV;
goto out;
}
@@ -3985,8 +3966,6 @@ struct sk_buff *qeth_core_get_next_skb(struct qeth_card *card,
return skb;
no_mem:
if (net_ratelimit()) {
- PRINT_WARN("No memory for packet received on %s.\n",
- QETH_CARD_IFNAME(card));
QETH_DBF_TEXT(TRACE, 2, "noskbmem");
QETH_DBF_TEXT_(TRACE, 2, "%s", CARD_BUS_ID(card));
}
@@ -4004,15 +3983,17 @@ static void qeth_unregister_dbf_views(void)
}
}
-void qeth_dbf_longtext(enum qeth_dbf_names dbf_nix, int level, char *text, ...)
+void qeth_dbf_longtext(enum qeth_dbf_names dbf_nix, int level, char *fmt, ...)
{
char dbf_txt_buf[32];
+ va_list args;
if (level > (qeth_dbf[dbf_nix].id)->level)
return;
- snprintf(dbf_txt_buf, sizeof(dbf_txt_buf), text);
+ va_start(args, fmt);
+ vsnprintf(dbf_txt_buf, sizeof(dbf_txt_buf), fmt, args);
+ va_end(args);
debug_text_event(qeth_dbf[dbf_nix].id, level, dbf_txt_buf);
-
}
EXPORT_SYMBOL_GPL(qeth_dbf_longtext);
@@ -4092,7 +4073,7 @@ static int qeth_core_probe_device(struct ccwgroup_device *gdev)
if (!get_device(dev))
return -ENODEV;
- QETH_DBF_TEXT_(SETUP, 2, "%s", gdev->dev.bus_id);
+ QETH_DBF_TEXT_(SETUP, 2, "%s", dev_name(&gdev->dev));
card = qeth_alloc_card();
if (!card) {
diff --git a/drivers/s390/net/qeth_core_offl.c b/drivers/s390/net/qeth_core_offl.c
index 822df8362856..452874e89740 100644
--- a/drivers/s390/net/qeth_core_offl.c
+++ b/drivers/s390/net/qeth_core_offl.c
@@ -122,8 +122,8 @@ int qeth_eddp_fill_buffer(struct qeth_qdio_out_q *queue,
if (element == 0)
return -EBUSY;
else {
- PRINT_WARN("could only partially fill eddp "
- "buffer!\n");
+ QETH_DBF_MESSAGE(2, "could only partially fill"
+ "eddp buffer!\n");
goto out;
}
}
@@ -143,8 +143,6 @@ int qeth_eddp_fill_buffer(struct qeth_qdio_out_q *queue,
if (must_refcnt) {
must_refcnt = 0;
if (qeth_eddp_buf_ref_context(buf, ctx)) {
- PRINT_WARN("no memory to create eddp context "
- "reference\n");
goto out_check;
}
}
diff --git a/drivers/s390/net/qeth_core_sys.c b/drivers/s390/net/qeth_core_sys.c
index 08a50f057284..c26e842ad905 100644
--- a/drivers/s390/net/qeth_core_sys.c
+++ b/drivers/s390/net/qeth_core_sys.c
@@ -129,7 +129,6 @@ static ssize_t qeth_dev_portno_store(struct device *dev,
portno = simple_strtoul(buf, &tmp, 16);
if (portno > QETH_MAX_PORTNO) {
- PRINT_WARN("portno 0x%X is out of range\n", portno);
return -EINVAL;
}
@@ -223,8 +222,6 @@ static ssize_t qeth_dev_prioqing_store(struct device *dev,
* if though we have to permit priority queueing
*/
if (card->qdio.no_out_queues == 1) {
- PRINT_WARN("Priority queueing disabled due "
- "to hardware limitations!\n");
card->qdio.do_prio_queueing = QETH_PRIOQ_DEFAULT;
return -EPERM;
}
@@ -250,7 +247,6 @@ static ssize_t qeth_dev_prioqing_store(struct device *dev,
card->qdio.do_prio_queueing = QETH_NO_PRIO_QUEUEING;
card->qdio.default_out_queue = QETH_DEFAULT_QUEUE;
} else {
- PRINT_WARN("Unknown queueing type '%s'\n", tmp);
return -EINVAL;
}
return count;
@@ -291,9 +287,6 @@ static ssize_t qeth_dev_bufcnt_store(struct device *dev,
((cnt > QETH_IN_BUF_COUNT_MAX) ? QETH_IN_BUF_COUNT_MAX : cnt);
if (old_cnt != cnt) {
rc = qeth_realloc_buffer_pool(card, cnt);
- if (rc)
- PRINT_WARN("Error (%d) while setting "
- "buffer count.\n", rc);
}
return count;
}
@@ -355,7 +348,6 @@ static ssize_t qeth_dev_performance_stats_store(struct device *dev,
card->perf_stats.initial_rx_packets = card->stats.rx_packets;
card->perf_stats.initial_tx_packets = card->stats.tx_packets;
} else {
- PRINT_WARN("performance_stats: write 0 or 1 to this file!\n");
return -EINVAL;
}
return count;
@@ -399,7 +391,6 @@ static ssize_t qeth_dev_layer2_store(struct device *dev,
newdis = QETH_DISCIPLINE_LAYER2;
break;
default:
- PRINT_WARN("layer2: write 0 or 1 to this file!\n");
return -EINVAL;
}
@@ -463,7 +454,6 @@ static ssize_t qeth_dev_large_send_store(struct device *dev,
} else if (!strcmp(tmp, "TSO")) {
type = QETH_LARGE_SEND_TSO;
} else {
- PRINT_WARN("large_send: invalid mode %s!\n", tmp);
return -EINVAL;
}
if (card->options.large_send == type)
@@ -503,8 +493,6 @@ static ssize_t qeth_dev_blkt_store(struct qeth_card *card,
if (i <= max_value) {
*value = i;
} else {
- PRINT_WARN("blkt total time: write values between"
- " 0 and %d to this file!\n", max_value);
return -EINVAL;
}
return count;
diff --git a/drivers/s390/net/qeth_l2_main.c b/drivers/s390/net/qeth_l2_main.c
index 86ec50ddae13..f682f7b14480 100644
--- a/drivers/s390/net/qeth_l2_main.c
+++ b/drivers/s390/net/qeth_l2_main.c
@@ -101,19 +101,16 @@ static struct net_device *qeth_l2_netdev_by_devno(unsigned char *read_dev_no)
{
struct qeth_card *card;
struct net_device *ndev;
- unsigned char *readno;
- __u16 temp_dev_no, card_dev_no;
- char *endp;
+ __u16 temp_dev_no;
unsigned long flags;
+ struct ccw_dev_id read_devid;
ndev = NULL;
memcpy(&temp_dev_no, read_dev_no, 2);
read_lock_irqsave(&qeth_core_card_list.rwlock, flags);
list_for_each_entry(card, &qeth_core_card_list.list, list) {
- readno = CARD_RDEV_ID(card);
- readno += (strlen(readno) - 4);
- card_dev_no = simple_strtoul(readno, &endp, 16);
- if (card_dev_no == temp_dev_no) {
+ ccw_device_get_id(CARD_RDEV(card), &read_devid);
+ if (read_devid.devno == temp_dev_no) {
ndev = card->dev;
break;
}
@@ -134,14 +131,14 @@ static int qeth_l2_send_setgroupmac_cb(struct qeth_card *card,
mac = &cmd->data.setdelmac.mac[0];
/* MAC already registered, needed in couple/uncouple case */
if (cmd->hdr.return_code == 0x2005) {
- PRINT_WARN("Group MAC %02x:%02x:%02x:%02x:%02x:%02x " \
+ QETH_DBF_MESSAGE(2, "Group MAC %02x:%02x:%02x:%02x:%02x:%02x "
"already existing on %s \n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
QETH_CARD_IFNAME(card));
cmd->hdr.return_code = 0;
}
if (cmd->hdr.return_code)
- PRINT_ERR("Could not set group MAC " \
+ QETH_DBF_MESSAGE(2, "Could not set group MAC "
"%02x:%02x:%02x:%02x:%02x:%02x on %s: %x\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
QETH_CARD_IFNAME(card), cmd->hdr.return_code);
@@ -166,7 +163,7 @@ static int qeth_l2_send_delgroupmac_cb(struct qeth_card *card,
cmd = (struct qeth_ipa_cmd *) data;
mac = &cmd->data.setdelmac.mac[0];
if (cmd->hdr.return_code)
- PRINT_ERR("Could not delete group MAC " \
+ QETH_DBF_MESSAGE(2, "Could not delete group MAC "
"%02x:%02x:%02x:%02x:%02x:%02x on %s: %x\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
QETH_CARD_IFNAME(card), cmd->hdr.return_code);
@@ -186,10 +183,8 @@ static void qeth_l2_add_mc(struct qeth_card *card, __u8 *mac)
mc = kmalloc(sizeof(struct qeth_mc_mac), GFP_ATOMIC);
- if (!mc) {
- PRINT_ERR("no mem vor mc mac address\n");
+ if (!mc)
return;
- }
memcpy(mc->mc_addr, mac, OSA_ADDR_LEN);
mc->mc_addrlen = OSA_ADDR_LEN;
@@ -280,7 +275,7 @@ static int qeth_l2_send_setdelvlan_cb(struct qeth_card *card,
QETH_DBF_TEXT(TRACE, 2, "L2sdvcb");
cmd = (struct qeth_ipa_cmd *) data;
if (cmd->hdr.return_code) {
- PRINT_ERR("Error in processing VLAN %i on %s: 0x%x. "
+ QETH_DBF_MESSAGE(2, "Error in processing VLAN %i on %s: 0x%x. "
"Continuing\n", cmd->data.setdelvlan.vlan_id,
QETH_CARD_IFNAME(card), cmd->hdr.return_code);
QETH_DBF_TEXT_(TRACE, 2, "L2VL%4x", cmd->hdr.command);
@@ -333,8 +328,6 @@ static void qeth_l2_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
spin_lock_bh(&card->vlanlock);
list_add_tail(&id->list, &card->vid_list);
spin_unlock_bh(&card->vlanlock);
- } else {
- PRINT_ERR("no memory for vid\n");
}
}
@@ -550,16 +543,15 @@ static int qeth_l2_request_initial_mac(struct qeth_card *card)
rc = qeth_query_setadapterparms(card);
if (rc) {
- PRINT_WARN("could not query adapter parameters on device %s: "
- "x%x\n", CARD_BUS_ID(card), rc);
+ QETH_DBF_MESSAGE(2, "could not query adapter parameters on "
+ "device %s: x%x\n", CARD_BUS_ID(card), rc);
}
if (card->info.guestlan) {
rc = qeth_setadpparms_change_macaddr(card);
if (rc) {
- PRINT_WARN("couldn't get MAC address on "
- "device %s: x%x\n",
- CARD_BUS_ID(card), rc);
+ QETH_DBF_MESSAGE(2, "couldn't get MAC address on "
+ "device %s: x%x\n", CARD_BUS_ID(card), rc);
QETH_DBF_TEXT_(SETUP, 2, "1err%d", rc);
return rc;
}
@@ -585,8 +577,6 @@ static int qeth_l2_set_mac_address(struct net_device *dev, void *p)
}
if (card->info.type == QETH_CARD_TYPE_OSN) {
- PRINT_WARN("Setting MAC address on %s is not supported.\n",
- dev->name);
QETH_DBF_TEXT(TRACE, 3, "setmcOSN");
return -EOPNOTSUPP;
}
@@ -666,7 +656,7 @@ static int qeth_l2_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
ctx = qeth_eddp_create_context(card, new_skb, hdr,
skb->sk->sk_protocol);
if (ctx == NULL) {
- PRINT_WARN("could not create eddp context\n");
+ QETH_DBF_MESSAGE(2, "could not create eddp context\n");
goto tx_drop;
}
} else {
@@ -731,6 +721,7 @@ tx_drop:
if ((new_skb != skb) && new_skb)
dev_kfree_skb_any(new_skb);
dev_kfree_skb_any(skb);
+ netif_wake_queue(dev);
return NETDEV_TX_OK;
}
@@ -1155,7 +1146,7 @@ static int qeth_osn_send_control_data(struct qeth_card *card, int len,
(addr_t) iob, 0, 0);
spin_unlock_irqrestore(get_ccwdev_lock(card->write.ccwdev), flags);
if (rc) {
- PRINT_WARN("qeth_osn_send_control_data: "
+ QETH_DBF_MESSAGE(2, "qeth_osn_send_control_data: "
"ccw_device_start rc = %i\n", rc);
QETH_DBF_TEXT_(TRACE, 2, " err%d", rc);
qeth_release_buffer(iob->channel, iob);
diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c
index 94a8ead64ed4..999552c83bbe 100644
--- a/drivers/s390/net/qeth_l3_main.c
+++ b/drivers/s390/net/qeth_l3_main.c
@@ -311,7 +311,6 @@ static struct qeth_ipaddr *qeth_l3_get_addr_buffer(
addr = kzalloc(sizeof(struct qeth_ipaddr), GFP_ATOMIC);
if (addr == NULL) {
- PRINT_WARN("Not enough memory to add address\n");
return NULL;
}
addr->type = QETH_IP_TYPE_NORMAL;
@@ -649,15 +648,6 @@ static void qeth_l3_correct_routing_type(struct qeth_card *card,
}
}
out_inval:
- PRINT_WARN("Routing type '%s' not supported for interface %s.\n"
- "Router status set to 'no router'.\n",
- ((*type == PRIMARY_ROUTER)? "primary router" :
- (*type == SECONDARY_ROUTER)? "secondary router" :
- (*type == PRIMARY_CONNECTOR)? "primary connector" :
- (*type == SECONDARY_CONNECTOR)? "secondary connector" :
- (*type == MULTICAST_ROUTER)? "multicast router" :
- "unknown"),
- card->dev->name);
*type = NO_ROUTER;
}
@@ -674,9 +664,9 @@ int qeth_l3_setrouting_v4(struct qeth_card *card)
QETH_PROT_IPV4);
if (rc) {
card->options.route4.type = NO_ROUTER;
- PRINT_WARN("Error (0x%04x) while setting routing type on %s. "
- "Type set to 'no router'.\n",
- rc, QETH_CARD_IFNAME(card));
+ QETH_DBF_MESSAGE(2, "Error (0x%04x) while setting routing type"
+ " on %s. Type set to 'no router'.\n", rc,
+ QETH_CARD_IFNAME(card));
}
return rc;
}
@@ -697,9 +687,9 @@ int qeth_l3_setrouting_v6(struct qeth_card *card)
QETH_PROT_IPV6);
if (rc) {
card->options.route6.type = NO_ROUTER;
- PRINT_WARN("Error (0x%04x) while setting routing type on %s. "
- "Type set to 'no router'.\n",
- rc, QETH_CARD_IFNAME(card));
+ QETH_DBF_MESSAGE(2, "Error (0x%04x) while setting routing type"
+ " on %s. Type set to 'no router'.\n", rc,
+ QETH_CARD_IFNAME(card));
}
#endif
return rc;
@@ -737,7 +727,6 @@ int qeth_l3_add_ipato_entry(struct qeth_card *card,
if (!memcmp(ipatoe->addr, new->addr,
(ipatoe->proto == QETH_PROT_IPV4)? 4:16) &&
(ipatoe->mask_bits == new->mask_bits)) {
- PRINT_WARN("ipato entry already exists!\n");
rc = -EEXIST;
break;
}
@@ -802,7 +791,6 @@ int qeth_l3_add_vipa(struct qeth_card *card, enum qeth_prot_versions proto,
rc = -EEXIST;
spin_unlock_irqrestore(&card->ip_lock, flags);
if (rc) {
- PRINT_WARN("Cannot add VIPA. Address already exists!\n");
return rc;
}
if (!qeth_l3_add_ip(card, ipaddr))
@@ -867,7 +855,6 @@ int qeth_l3_add_rxip(struct qeth_card *card, enum qeth_prot_versions proto,
rc = -EEXIST;
spin_unlock_irqrestore(&card->ip_lock, flags);
if (rc) {
- PRINT_WARN("Cannot add RXIP. Address already exists!\n");
return rc;
}
if (!qeth_l3_add_ip(card, ipaddr))
@@ -1020,23 +1007,23 @@ static int qeth_l3_setadapter_hstr(struct qeth_card *card)
IPA_SETADP_SET_BROADCAST_MODE,
card->options.broadcast_mode);
if (rc)
- PRINT_WARN("couldn't set broadcast mode on "
+ QETH_DBF_MESSAGE(2, "couldn't set broadcast mode on "
"device %s: x%x\n",
CARD_BUS_ID(card), rc);
rc = qeth_l3_send_setadp_mode(card,
IPA_SETADP_ALTER_MAC_ADDRESS,
card->options.macaddr_mode);
if (rc)
- PRINT_WARN("couldn't set macaddr mode on "
+ QETH_DBF_MESSAGE(2, "couldn't set macaddr mode on "
"device %s: x%x\n", CARD_BUS_ID(card), rc);
return rc;
}
if (card->options.broadcast_mode == QETH_TR_BROADCAST_LOCAL)
- PRINT_WARN("set adapter parameters not available "
+ QETH_DBF_MESSAGE(2, "set adapter parameters not available "
"to set broadcast mode, using ALLRINGS "
"on device %s:\n", CARD_BUS_ID(card));
if (card->options.macaddr_mode == QETH_TR_MACADDR_CANONICAL)
- PRINT_WARN("set adapter parameters not available "
+ QETH_DBF_MESSAGE(2, "set adapter parameters not available "
"to set macaddr mode, using NONCANONICAL "
"on device %s:\n", CARD_BUS_ID(card));
return 0;
@@ -2070,7 +2057,7 @@ static struct qeth_card *qeth_l3_get_card_from_dev(struct net_device *dev)
card = netdev_priv(dev);
else if (rc == QETH_VLAN_CARD)
card = netdev_priv(vlan_dev_info(dev)->real_dev);
- if (card->options.layer2)
+ if (card && card->options.layer2)
card = NULL;
QETH_DBF_TEXT_(TRACE, 4, "%d", rc);
return card ;
@@ -2182,8 +2169,6 @@ static int qeth_l3_arp_set_no_entries(struct qeth_card *card, int no_entries)
if (card->info.guestlan)
return -EOPNOTSUPP;
if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
- PRINT_WARN("ARP processing not supported "
- "on %s!\n", QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING,
@@ -2191,8 +2176,8 @@ static int qeth_l3_arp_set_no_entries(struct qeth_card *card, int no_entries)
no_entries);
if (rc) {
tmp = rc;
- PRINT_WARN("Could not set number of ARP entries on %s: "
- "%s (0x%x/%d)\n", QETH_CARD_IFNAME(card),
+ QETH_DBF_MESSAGE(2, "Could not set number of ARP entries on "
+ "%s: %s (0x%x/%d)\n", QETH_CARD_IFNAME(card),
qeth_l3_arp_get_error_cause(&rc), tmp, tmp);
}
return rc;
@@ -2260,9 +2245,6 @@ static int qeth_l3_arp_query_cb(struct qeth_card *card,
qdata->no_entries * uentry_size){
QETH_DBF_TEXT_(TRACE, 4, "qaer3%i", -ENOMEM);
cmd->hdr.return_code = -ENOMEM;
- PRINT_WARN("query ARP user space buffer is too small for "
- "the returned number of ARP entries. "
- "Aborting query!\n");
goto out_error;
}
QETH_DBF_TEXT_(TRACE, 4, "anore%i",
@@ -2324,8 +2306,6 @@ static int qeth_l3_arp_query(struct qeth_card *card, char __user *udata)
if (!qeth_is_supported(card,/*IPA_QUERY_ARP_ADDR_INFO*/
IPA_ARP_PROCESSING)) {
- PRINT_WARN("ARP processing not supported "
- "on %s!\n", QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
/* get size of userspace buffer and mask_bits -> 6 bytes */
@@ -2344,7 +2324,7 @@ static int qeth_l3_arp_query(struct qeth_card *card, char __user *udata)
qeth_l3_arp_query_cb, (void *)&qinfo);
if (rc) {
tmp = rc;
- PRINT_WARN("Error while querying ARP cache on %s: %s "
+ QETH_DBF_MESSAGE(2, "Error while querying ARP cache on %s: %s "
"(0x%x/%d)\n", QETH_CARD_IFNAME(card),
qeth_l3_arp_get_error_cause(&rc), tmp, tmp);
if (copy_to_user(udata, qinfo.udata, 4))
@@ -2375,8 +2355,6 @@ static int qeth_l3_arp_add_entry(struct qeth_card *card,
if (card->info.guestlan)
return -EOPNOTSUPP;
if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
- PRINT_WARN("ARP processing not supported "
- "on %s!\n", QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
@@ -2391,10 +2369,9 @@ static int qeth_l3_arp_add_entry(struct qeth_card *card,
if (rc) {
tmp = rc;
qeth_l3_ipaddr4_to_string((u8 *)entry->ipaddr, buf);
- PRINT_WARN("Could not add ARP entry for address %s on %s: "
- "%s (0x%x/%d)\n",
- buf, QETH_CARD_IFNAME(card),
- qeth_l3_arp_get_error_cause(&rc), tmp, tmp);
+ QETH_DBF_MESSAGE(2, "Could not add ARP entry for address %s "
+ "on %s: %s (0x%x/%d)\n", buf, QETH_CARD_IFNAME(card),
+ qeth_l3_arp_get_error_cause(&rc), tmp, tmp);
}
return rc;
}
@@ -2417,8 +2394,6 @@ static int qeth_l3_arp_remove_entry(struct qeth_card *card,
if (card->info.guestlan)
return -EOPNOTSUPP;
if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
- PRINT_WARN("ARP processing not supported "
- "on %s!\n", QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
memcpy(buf, entry, 12);
@@ -2433,10 +2408,9 @@ static int qeth_l3_arp_remove_entry(struct qeth_card *card,
tmp = rc;
memset(buf, 0, 16);
qeth_l3_ipaddr4_to_string((u8 *)entry->ipaddr, buf);
- PRINT_WARN("Could not delete ARP entry for address %s on %s: "
- "%s (0x%x/%d)\n",
- buf, QETH_CARD_IFNAME(card),
- qeth_l3_arp_get_error_cause(&rc), tmp, tmp);
+ QETH_DBF_MESSAGE(2, "Could not delete ARP entry for address %s"
+ " on %s: %s (0x%x/%d)\n", buf, QETH_CARD_IFNAME(card),
+ qeth_l3_arp_get_error_cause(&rc), tmp, tmp);
}
return rc;
}
@@ -2456,16 +2430,14 @@ static int qeth_l3_arp_flush_cache(struct qeth_card *card)
if (card->info.guestlan || (card->info.type == QETH_CARD_TYPE_IQD))
return -EOPNOTSUPP;
if (!qeth_is_supported(card, IPA_ARP_PROCESSING)) {
- PRINT_WARN("ARP processing not supported "
- "on %s!\n", QETH_CARD_IFNAME(card));
return -EOPNOTSUPP;
}
rc = qeth_l3_send_simple_setassparms(card, IPA_ARP_PROCESSING,
IPA_CMD_ASS_ARP_FLUSH_CACHE, 0);
if (rc) {
tmp = rc;
- PRINT_WARN("Could not flush ARP cache on %s: %s (0x%x/%d)\n",
- QETH_CARD_IFNAME(card),
+ QETH_DBF_MESSAGE(2, "Could not flush ARP cache on %s: %s "
+ "(0x%x/%d)\n", QETH_CARD_IFNAME(card),
qeth_l3_arp_get_error_cause(&rc), tmp, tmp);
}
return rc;
@@ -2724,7 +2696,7 @@ static int qeth_l3_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
ctx = qeth_eddp_create_context(card, new_skb, hdr,
skb->sk->sk_protocol);
if (ctx == NULL) {
- PRINT_WARN("could not create eddp context\n");
+ QETH_DBF_MESSAGE(2, "could not create eddp context\n");
goto tx_drop;
}
} else {
@@ -2792,6 +2764,7 @@ tx_drop:
if ((new_skb != skb) && new_skb)
dev_kfree_skb_any(new_skb);
dev_kfree_skb_any(skb);
+ netif_wake_queue(dev);
return NETDEV_TX_OK;
}
diff --git a/drivers/s390/net/qeth_l3_sys.c b/drivers/s390/net/qeth_l3_sys.c
index 08f51fd902c4..ac1993708ae9 100644
--- a/drivers/s390/net/qeth_l3_sys.c
+++ b/drivers/s390/net/qeth_l3_sys.c
@@ -85,7 +85,6 @@ static ssize_t qeth_l3_dev_route_store(struct qeth_card *card,
} else if (!strcmp(tmp, "multicast_router")) {
route->type = MULTICAST_ROUTER;
} else {
- PRINT_WARN("Invalid routing type '%s'.\n", tmp);
return -EINVAL;
}
if (((card->state == CARD_STATE_SOFTSETUP) ||
@@ -137,9 +136,6 @@ static ssize_t qeth_l3_dev_route6_store(struct device *dev,
return -EINVAL;
if (!qeth_is_supported(card, IPA_IPV6)) {
- PRINT_WARN("IPv6 not supported for interface %s.\n"
- "Routing status no changed.\n",
- QETH_CARD_IFNAME(card));
return -ENOTSUPP;
}
@@ -179,7 +175,6 @@ static ssize_t qeth_l3_dev_fake_broadcast_store(struct device *dev,
if ((i == 0) || (i == 1))
card->options.fake_broadcast = i;
else {
- PRINT_WARN("fake_broadcast: write 0 or 1 to this file!\n");
return -EINVAL;
}
return count;
@@ -220,7 +215,6 @@ static ssize_t qeth_l3_dev_broadcast_mode_store(struct device *dev,
if (!((card->info.link_type == QETH_LINK_TYPE_HSTR) ||
(card->info.link_type == QETH_LINK_TYPE_LANE_TR))) {
- PRINT_WARN("Device is not a tokenring device!\n");
return -EINVAL;
}
@@ -233,8 +227,6 @@ static ssize_t qeth_l3_dev_broadcast_mode_store(struct device *dev,
card->options.broadcast_mode = QETH_TR_BROADCAST_ALLRINGS;
return count;
} else {
- PRINT_WARN("broadcast_mode: invalid mode %s!\n",
- tmp);
return -EINVAL;
}
return count;
@@ -275,7 +267,6 @@ static ssize_t qeth_l3_dev_canonical_macaddr_store(struct device *dev,
if (!((card->info.link_type == QETH_LINK_TYPE_HSTR) ||
(card->info.link_type == QETH_LINK_TYPE_LANE_TR))) {
- PRINT_WARN("Device is not a tokenring device!\n");
return -EINVAL;
}
@@ -285,7 +276,6 @@ static ssize_t qeth_l3_dev_canonical_macaddr_store(struct device *dev,
QETH_TR_MACADDR_CANONICAL :
QETH_TR_MACADDR_NONCANONICAL;
else {
- PRINT_WARN("canonical_macaddr: write 0 or 1 to this file!\n");
return -EINVAL;
}
return count;
@@ -327,7 +317,6 @@ static ssize_t qeth_l3_dev_checksum_store(struct device *dev,
else if (!strcmp(tmp, "no_checksumming"))
card->options.checksum_type = NO_CHECKSUMMING;
else {
- PRINT_WARN("Unknown checksumming type '%s'\n", tmp);
return -EINVAL;
}
return count;
@@ -382,8 +371,6 @@ static ssize_t qeth_l3_dev_ipato_enable_store(struct device *dev,
} else if (!strcmp(tmp, "0")) {
card->ipato.enabled = 0;
} else {
- PRINT_WARN("ipato_enable: write 0, 1 or 'toggle' to "
- "this file\n");
return -EINVAL;
}
return count;
@@ -422,8 +409,6 @@ static ssize_t qeth_l3_dev_ipato_invert4_store(struct device *dev,
} else if (!strcmp(tmp, "0")) {
card->ipato.invert4 = 0;
} else {
- PRINT_WARN("ipato_invert4: write 0, 1 or 'toggle' to "
- "this file\n");
return -EINVAL;
}
return count;
@@ -486,13 +471,10 @@ static int qeth_l3_parse_ipatoe(const char *buf, enum qeth_prot_versions proto,
/* get address string */
end = strchr(start, '/');
if (!end || (end - start >= 40)) {
- PRINT_WARN("Invalid format for ipato_addx/delx. "
- "Use <ip addr>/<mask bits>\n");
return -EINVAL;
}
strncpy(buffer, start, end - start);
if (qeth_l3_string_to_ipaddr(buffer, proto, addr)) {
- PRINT_WARN("Invalid IP address format!\n");
return -EINVAL;
}
start = end + 1;
@@ -500,7 +482,6 @@ static int qeth_l3_parse_ipatoe(const char *buf, enum qeth_prot_versions proto,
if (!strlen(start) ||
(tmp == start) ||
(*mask_bits > ((proto == QETH_PROT_IPV4) ? 32 : 128))) {
- PRINT_WARN("Invalid mask bits for ipato_addx/delx !\n");
return -EINVAL;
}
return 0;
@@ -520,7 +501,6 @@ static ssize_t qeth_l3_dev_ipato_add_store(const char *buf, size_t count,
ipatoe = kzalloc(sizeof(struct qeth_ipato_entry), GFP_KERNEL);
if (!ipatoe) {
- PRINT_WARN("No memory to allocate ipato entry\n");
return -ENOMEM;
}
ipatoe->proto = proto;
@@ -609,8 +589,6 @@ static ssize_t qeth_l3_dev_ipato_invert6_store(struct device *dev,
} else if (!strcmp(tmp, "0")) {
card->ipato.invert6 = 0;
} else {
- PRINT_WARN("ipato_invert6: write 0, 1 or 'toggle' to "
- "this file\n");
return -EINVAL;
}
return count;
@@ -724,7 +702,6 @@ static int qeth_l3_parse_vipae(const char *buf, enum qeth_prot_versions proto,
u8 *addr)
{
if (qeth_l3_string_to_ipaddr(buf, proto, addr)) {
- PRINT_WARN("Invalid IP address format!\n");
return -EINVAL;
}
return 0;
@@ -891,7 +868,6 @@ static int qeth_l3_parse_rxipe(const char *buf, enum qeth_prot_versions proto,
u8 *addr)
{
if (qeth_l3_string_to_ipaddr(buf, proto, addr)) {
- PRINT_WARN("Invalid IP address format!\n");
return -EINVAL;
}
return 0;
diff --git a/drivers/s390/s390_rdev.c b/drivers/s390/s390_rdev.c
index 3c7145d9f9a1..64371c05a3b3 100644
--- a/drivers/s390/s390_rdev.c
+++ b/drivers/s390/s390_rdev.c
@@ -30,7 +30,7 @@ s390_root_dev_register(const char *name)
dev = kzalloc(sizeof(struct device), GFP_KERNEL);
if (!dev)
return ERR_PTR(-ENOMEM);
- strncpy(dev->bus_id, name, min(strlen(name), (size_t)BUS_ID_SIZE));
+ dev_set_name(dev, name);
dev->release = s390_root_dev_release;
ret = device_register(dev);
if (ret) {
diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c
index 5080f343ad74..834e9ee7e934 100644
--- a/drivers/s390/s390mach.c
+++ b/drivers/s390/s390mach.c
@@ -2,10 +2,10 @@
* drivers/s390/s390mach.c
* S/390 machine check handler
*
- * S390 version
- * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Copyright IBM Corp. 2000,2008
* Author(s): Ingo Adlung (adlung@de.ibm.com)
* Martin Schwidefsky (schwidefsky@de.ibm.com)
+ * Cornelia Huck <cornelia.huck@de.ibm.com>
*/
#include <linux/init.h>
@@ -18,10 +18,6 @@
#include <asm/etr.h>
#include <asm/lowcore.h>
#include <asm/cio.h>
-#include "cio/cio.h"
-#include "cio/chsc.h"
-#include "cio/css.h"
-#include "cio/chp.h"
#include "s390mach.h"
static struct semaphore m_sem;
@@ -36,13 +32,40 @@ s390_handle_damage(char *msg)
for(;;);
}
+static crw_handler_t crw_handlers[NR_RSCS];
+
+/**
+ * s390_register_crw_handler() - register a channel report word handler
+ * @rsc: reporting source code to handle
+ * @handler: handler to be registered
+ *
+ * Returns %0 on success and a negative error value otherwise.
+ */
+int s390_register_crw_handler(int rsc, crw_handler_t handler)
+{
+ if ((rsc < 0) || (rsc >= NR_RSCS))
+ return -EINVAL;
+ if (!cmpxchg(&crw_handlers[rsc], NULL, handler))
+ return 0;
+ return -EBUSY;
+}
+
+/**
+ * s390_unregister_crw_handler() - unregister a channel report word handler
+ * @rsc: reporting source code to handle
+ */
+void s390_unregister_crw_handler(int rsc)
+{
+ if ((rsc < 0) || (rsc >= NR_RSCS))
+ return;
+ xchg(&crw_handlers[rsc], NULL);
+ synchronize_sched();
+}
+
/*
* Retrieve CRWs and call function to handle event.
- *
- * Note : we currently process CRWs for io and chsc subchannels only
*/
-static int
-s390_collect_crw_info(void *param)
+static int s390_collect_crw_info(void *param)
{
struct crw crw[2];
int ccode;
@@ -84,57 +107,24 @@ repeat:
crw[chain].rsid);
/* Check for overflows. */
if (crw[chain].oflw) {
+ int i;
+
pr_debug("%s: crw overflow detected!\n", __func__);
- css_schedule_eval_all();
+ for (i = 0; i < NR_RSCS; i++) {
+ if (crw_handlers[i])
+ crw_handlers[i](NULL, NULL, 1);
+ }
chain = 0;
continue;
}
- switch (crw[chain].rsc) {
- case CRW_RSC_SCH:
- if (crw[0].chn && !chain)
- break;
- pr_debug("source is subchannel %04X\n", crw[0].rsid);
- css_process_crw(crw[0].rsid, chain ? crw[1].rsid : 0);
- break;
- case CRW_RSC_MONITOR:
- pr_debug("source is monitoring facility\n");
- break;
- case CRW_RSC_CPATH:
- pr_debug("source is channel path %02X\n", crw[0].rsid);
- /*
- * Check for solicited machine checks. These are
- * created by reset channel path and need not be
- * reported to the common I/O layer.
- */
- if (crw[chain].slct) {
- pr_debug("solicited machine check for "
- "channel path %02X\n", crw[0].rsid);
- break;
- }
- switch (crw[0].erc) {
- case CRW_ERC_IPARM: /* Path has come. */
- chp_process_crw(crw[0].rsid, 1);
- break;
- case CRW_ERC_PERRI: /* Path has gone. */
- case CRW_ERC_PERRN:
- chp_process_crw(crw[0].rsid, 0);
- break;
- default:
- pr_debug("Don't know how to handle erc=%x\n",
- crw[0].erc);
- }
- break;
- case CRW_RSC_CONFIG:
- pr_debug("source is configuration-alert facility\n");
- break;
- case CRW_RSC_CSS:
- pr_debug("source is channel subsystem\n");
- chsc_process_crw();
- break;
- default:
- pr_debug("unknown source\n");
- break;
+ if (crw[0].chn && !chain) {
+ chain++;
+ continue;
}
+ if (crw_handlers[crw[chain].rsc])
+ crw_handlers[crw[chain].rsc](&crw[0],
+ chain ? &crw[1] : NULL,
+ 0);
/* chain is always 0 or 1 here. */
chain = crw[chain].chn ? chain + 1 : 0;
}
@@ -207,6 +197,7 @@ s390_handle_mcck(void)
do_exit(SIGSEGV);
}
}
+EXPORT_SYMBOL_GPL(s390_handle_mcck);
/*
* returns 0 if all registers could be validated
@@ -467,6 +458,10 @@ s390_do_machine_check(struct pt_regs *regs)
etr_sync_check();
if (S390_lowcore.external_damage_code & (1U << ED_ETR_SWITCH))
etr_switch_to_local();
+ if (S390_lowcore.external_damage_code & (1U << ED_STP_SYNC))
+ stp_sync_check();
+ if (S390_lowcore.external_damage_code & (1U << ED_STP_ISLAND))
+ stp_island_check();
}
if (mci->se)
diff --git a/drivers/s390/s390mach.h b/drivers/s390/s390mach.h
index ca681f9b67fc..d39f8b697d27 100644
--- a/drivers/s390/s390mach.h
+++ b/drivers/s390/s390mach.h
@@ -72,6 +72,13 @@ struct crw {
__u32 rsid : 16; /* reporting-source ID */
} __attribute__ ((packed));
+typedef void (*crw_handler_t)(struct crw *, struct crw *, int);
+
+extern int s390_register_crw_handler(int rsc, crw_handler_t handler);
+extern void s390_unregister_crw_handler(int rsc);
+
+#define NR_RSCS 16
+
#define CRW_RSC_MONITOR 0x2 /* monitoring facility */
#define CRW_RSC_SCH 0x3 /* subchannel */
#define CRW_RSC_CPATH 0x4 /* channel path */
@@ -105,6 +112,9 @@ static inline int stcrw(struct crw *pcrw )
#define ED_ETR_SYNC 12 /* External damage ETR sync check */
#define ED_ETR_SWITCH 13 /* External damage ETR switch to local */
+#define ED_STP_SYNC 7 /* External damage STP sync check */
+#define ED_STP_ISLAND 6 /* External damage STP island check */
+
struct pt_regs;
void s390_handle_mcck(void);
diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c
index 8c7e2b778ef1..1383f42f21ff 100644
--- a/drivers/s390/scsi/zfcp_aux.c
+++ b/drivers/s390/scsi/zfcp_aux.c
@@ -839,7 +839,7 @@ zfcp_unit_enqueue(struct zfcp_port *port, fcp_lun_t fcp_lun)
unit->fcp_lun = fcp_lun;
/* setup for sysfs registration */
- snprintf(unit->sysfs_device.bus_id, BUS_ID_SIZE, "0x%016llx", fcp_lun);
+ dev_set_name(&unit->sysfs_device, "0x%016llx", fcp_lun);
unit->sysfs_device.parent = &port->sysfs_device;
unit->sysfs_device.release = zfcp_sysfs_unit_release;
dev_set_drvdata(&unit->sysfs_device, unit);
@@ -847,6 +847,14 @@ zfcp_unit_enqueue(struct zfcp_port *port, fcp_lun_t fcp_lun)
/* mark unit unusable as long as sysfs registration is not complete */
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
+ spin_lock_init(&unit->latencies.lock);
+ unit->latencies.write.channel.min = 0xFFFFFFFF;
+ unit->latencies.write.fabric.min = 0xFFFFFFFF;
+ unit->latencies.read.channel.min = 0xFFFFFFFF;
+ unit->latencies.read.fabric.min = 0xFFFFFFFF;
+ unit->latencies.cmd.channel.min = 0xFFFFFFFF;
+ unit->latencies.cmd.fabric.min = 0xFFFFFFFF;
+
if (device_register(&unit->sysfs_device)) {
kfree(unit);
return NULL;
@@ -962,6 +970,27 @@ static void zfcp_dummy_release(struct device *dev)
return;
}
+int zfcp_status_read_refill(struct zfcp_adapter *adapter)
+{
+ while (atomic_read(&adapter->stat_miss) > 0)
+ if (zfcp_fsf_status_read(adapter, ZFCP_WAIT_FOR_SBAL))
+ break;
+ else
+ atomic_dec(&adapter->stat_miss);
+
+ if (ZFCP_STATUS_READS_RECOM <= atomic_read(&adapter->stat_miss)) {
+ zfcp_erp_adapter_reopen(adapter, 0, 103, NULL);
+ return 1;
+ }
+ return 0;
+}
+
+static void _zfcp_status_read_scheduler(struct work_struct *work)
+{
+ zfcp_status_read_refill(container_of(work, struct zfcp_adapter,
+ stat_work));
+}
+
/*
* Enqueues an adapter at the end of the adapter list in the driver data.
* All adapter internal structures are set up.
@@ -1055,6 +1084,7 @@ zfcp_adapter_enqueue(struct ccw_device *ccw_device)
/* initialize lock of associated request queue */
rwlock_init(&adapter->request_queue.queue_lock);
+ INIT_WORK(&adapter->stat_work, _zfcp_status_read_scheduler);
/* mark adapter unusable as long as sysfs registration is not complete */
atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &adapter->status);
@@ -1066,8 +1096,7 @@ zfcp_adapter_enqueue(struct ccw_device *ccw_device)
adapter->generic_services.parent = &adapter->ccw_device->dev;
adapter->generic_services.release = zfcp_dummy_release;
- snprintf(adapter->generic_services.bus_id, BUS_ID_SIZE,
- "generic_services");
+ dev_set_name(&adapter->generic_services, "generic_services");
if (device_register(&adapter->generic_services))
goto generic_services_failed;
@@ -1115,6 +1144,7 @@ zfcp_adapter_dequeue(struct zfcp_adapter *adapter)
int retval = 0;
unsigned long flags;
+ cancel_work_sync(&adapter->stat_work);
zfcp_adapter_scsi_unregister(adapter);
device_unregister(&adapter->generic_services);
zfcp_sysfs_adapter_remove_files(&adapter->ccw_device->dev);
@@ -1218,24 +1248,19 @@ zfcp_port_enqueue(struct zfcp_adapter *adapter, wwn_t wwpn, u32 status,
if (status & ZFCP_STATUS_PORT_WKA) {
switch (d_id) {
case ZFCP_DID_DIRECTORY_SERVICE:
- snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
- "directory");
+ dev_set_name(&port->sysfs_device, "directory");
break;
case ZFCP_DID_MANAGEMENT_SERVICE:
- snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
- "management");
+ dev_set_name(&port->sysfs_device, "management");
break;
case ZFCP_DID_KEY_DISTRIBUTION_SERVICE:
- snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
- "key_distribution");
+ dev_set_name(&port->sysfs_device, "key_distribution");
break;
case ZFCP_DID_ALIAS_SERVICE:
- snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
- "alias");
+ dev_set_name(&port->sysfs_device, "alias");
break;
case ZFCP_DID_TIME_SERVICE:
- snprintf(port->sysfs_device.bus_id, BUS_ID_SIZE,
- "time");
+ dev_set_name(&port->sysfs_device, "time");
break;
default:
kfree(port);
@@ -1244,8 +1269,7 @@ zfcp_port_enqueue(struct zfcp_adapter *adapter, wwn_t wwpn, u32 status,
port->d_id = d_id;
port->sysfs_device.parent = &adapter->generic_services;
} else {
- snprintf(port->sysfs_device.bus_id,
- BUS_ID_SIZE, "0x%016llx", wwpn);
+ dev_set_name(&port->sysfs_device, "0x%016llx", wwpn);
port->sysfs_device.parent = &adapter->ccw_device->dev;
}
port->sysfs_device.release = zfcp_sysfs_port_release;
@@ -1502,19 +1526,16 @@ zfcp_gid_pn_buffers_alloc(struct zfcp_gid_pn_data **gid_pn, mempool_t *pool)
{
struct zfcp_gid_pn_data *data;
- if (pool != NULL) {
+ if (pool)
data = mempool_alloc(pool, GFP_ATOMIC);
- if (likely(data != NULL)) {
- data->ct.pool = pool;
- }
- } else {
+ else
data = kmem_cache_alloc(zfcp_data.gid_pn_cache, GFP_ATOMIC);
- }
if (NULL == data)
return -ENOMEM;
memset(data, 0, sizeof(*data));
+ data->ct.pool = pool;
sg_init_table(&data->req , 1);
sg_init_table(&data->resp , 1);
data->ct.req = &data->req;
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index c8bad675dbd1..558dae9639f3 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -186,8 +186,8 @@ void zfcp_hba_dbf_event_fsf_response(struct zfcp_fsf_req *fsf_req)
fsf_status_qual, FSF_STATUS_QUALIFIER_SIZE);
response->fsf_req_status = fsf_req->status;
response->sbal_first = fsf_req->sbal_first;
- response->sbal_curr = fsf_req->sbal_curr;
response->sbal_last = fsf_req->sbal_last;
+ response->sbal_response = fsf_req->sbal_response;
response->pool = fsf_req->pool != NULL;
response->erp_action = (unsigned long)fsf_req->erp_action;
@@ -268,7 +268,7 @@ void zfcp_hba_dbf_event_fsf_unsol(const char *tag, struct zfcp_adapter *adapter,
strncpy(rec->tag, "stat", ZFCP_DBF_TAG_SIZE);
strncpy(rec->tag2, tag, ZFCP_DBF_TAG_SIZE);
- rec->u.status.failed = adapter->status_read_failed;
+ rec->u.status.failed = atomic_read(&adapter->stat_miss);
if (status_buffer != NULL) {
rec->u.status.status_type = status_buffer->status_type;
rec->u.status.status_subtype = status_buffer->status_subtype;
@@ -355,8 +355,8 @@ static void zfcp_hba_dbf_view_response(char **p,
FSF_STATUS_QUALIFIER_SIZE, 0, FSF_STATUS_QUALIFIER_SIZE);
zfcp_dbf_out(p, "fsf_req_status", "0x%08x", r->fsf_req_status);
zfcp_dbf_out(p, "sbal_first", "0x%02x", r->sbal_first);
- zfcp_dbf_out(p, "sbal_curr", "0x%02x", r->sbal_curr);
zfcp_dbf_out(p, "sbal_last", "0x%02x", r->sbal_last);
+ zfcp_dbf_out(p, "sbal_response", "0x%02x", r->sbal_response);
zfcp_dbf_out(p, "pool", "0x%02x", r->pool);
switch (r->fsf_command) {
@@ -670,24 +670,20 @@ static struct debug_view zfcp_rec_dbf_view = {
* zfcp_rec_dbf_event_thread - trace event related to recovery thread operation
* @id2: identifier for event
* @adapter: adapter
- * @lock: non-zero value indicates that erp_lock has not yet been acquired
+ * This function assumes that the caller is holding erp_lock.
*/
-void zfcp_rec_dbf_event_thread(u8 id2, struct zfcp_adapter *adapter, int lock)
+void zfcp_rec_dbf_event_thread(u8 id2, struct zfcp_adapter *adapter)
{
struct zfcp_rec_dbf_record *r = &adapter->rec_dbf_buf;
unsigned long flags = 0;
struct list_head *entry;
unsigned ready = 0, running = 0, total;
- if (lock)
- read_lock_irqsave(&adapter->erp_lock, flags);
list_for_each(entry, &adapter->erp_ready_head)
ready++;
list_for_each(entry, &adapter->erp_running_head)
running++;
total = adapter->erp_total_count;
- if (lock)
- read_unlock_irqrestore(&adapter->erp_lock, flags);
spin_lock_irqsave(&adapter->rec_dbf_lock, flags);
memset(r, 0, sizeof(*r));
@@ -696,10 +692,25 @@ void zfcp_rec_dbf_event_thread(u8 id2, struct zfcp_adapter *adapter, int lock)
r->u.thread.total = total;
r->u.thread.ready = ready;
r->u.thread.running = running;
- debug_event(adapter->rec_dbf, 5, r, sizeof(*r));
+ debug_event(adapter->rec_dbf, 6, r, sizeof(*r));
spin_unlock_irqrestore(&adapter->rec_dbf_lock, flags);
}
+/**
+ * zfcp_rec_dbf_event_thread - trace event related to recovery thread operation
+ * @id2: identifier for event
+ * @adapter: adapter
+ * This function assumes that the caller does not hold erp_lock.
+ */
+void zfcp_rec_dbf_event_thread_lock(u8 id2, struct zfcp_adapter *adapter)
+{
+ unsigned long flags;
+
+ read_lock_irqsave(&adapter->erp_lock, flags);
+ zfcp_rec_dbf_event_thread(id2, adapter);
+ read_unlock_irqrestore(&adapter->erp_lock, flags);
+}
+
static void zfcp_rec_dbf_event_target(u8 id2, void *ref,
struct zfcp_adapter *adapter,
atomic_t *status, atomic_t *erp_count,
@@ -823,7 +834,7 @@ void zfcp_rec_dbf_event_action(u8 id2, struct zfcp_erp_action *erp_action)
r->u.action.status = erp_action->status;
r->u.action.step = erp_action->step;
r->u.action.fsf_req = (unsigned long)erp_action->fsf_req;
- debug_event(adapter->rec_dbf, 4, r, sizeof(*r));
+ debug_event(adapter->rec_dbf, 5, r, sizeof(*r));
spin_unlock_irqrestore(&adapter->rec_dbf_lock, flags);
}
diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h
index 54c34e483457..d04aea604974 100644
--- a/drivers/s390/scsi/zfcp_dbf.h
+++ b/drivers/s390/scsi/zfcp_dbf.h
@@ -38,7 +38,7 @@ struct zfcp_rec_dbf_record_thread {
u32 total;
u32 ready;
u32 running;
-} __attribute__ ((packed));
+};
struct zfcp_rec_dbf_record_target {
u64 ref;
@@ -47,7 +47,7 @@ struct zfcp_rec_dbf_record_target {
u64 wwpn;
u64 fcp_lun;
u32 erp_count;
-} __attribute__ ((packed));
+};
struct zfcp_rec_dbf_record_trigger {
u8 want;
@@ -59,14 +59,14 @@ struct zfcp_rec_dbf_record_trigger {
u64 action;
u64 wwpn;
u64 fcp_lun;
-} __attribute__ ((packed));
+};
struct zfcp_rec_dbf_record_action {
u32 status;
u32 step;
u64 action;
u64 fsf_req;
-} __attribute__ ((packed));
+};
struct zfcp_rec_dbf_record {
u8 id;
@@ -77,7 +77,7 @@ struct zfcp_rec_dbf_record {
struct zfcp_rec_dbf_record_target target;
struct zfcp_rec_dbf_record_trigger trigger;
} u;
-} __attribute__ ((packed));
+};
enum {
ZFCP_REC_DBF_ID_ACTION,
@@ -97,8 +97,8 @@ struct zfcp_hba_dbf_record_response {
u8 fsf_status_qual[FSF_STATUS_QUALIFIER_SIZE];
u32 fsf_req_status;
u8 sbal_first;
- u8 sbal_curr;
u8 sbal_last;
+ u8 sbal_response;
u8 pool;
u64 erp_action;
union {
diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h
index bda8c77b22da..c2fe92babe4f 100644
--- a/drivers/s390/scsi/zfcp_def.h
+++ b/drivers/s390/scsi/zfcp_def.h
@@ -136,7 +136,6 @@ zfcp_address_to_sg(void *address, struct scatterlist *list, unsigned int size)
#define ZFCP_QTCB_VERSION FSF_QTCB_CURRENT_VERSION
/* ATTENTION: value must not be used by hardware */
#define FSF_QTCB_UNSOLICITED_STATUS 0x6305
-#define ZFCP_STATUS_READ_FAILED_THRESHOLD 3
#define ZFCP_STATUS_READS_RECOM FSF_STATUS_READS_RECOM
/* Do 1st retry in 1 second, then double the timeout for each following retry */
@@ -708,6 +707,24 @@ struct zfcp_erp_action {
struct timer_list timer;
};
+struct fsf_latency_record {
+ u32 min;
+ u32 max;
+ u64 sum;
+};
+
+struct latency_cont {
+ struct fsf_latency_record channel;
+ struct fsf_latency_record fabric;
+ u64 counter;
+};
+
+struct zfcp_latencies {
+ struct latency_cont read;
+ struct latency_cont write;
+ struct latency_cont cmd;
+ spinlock_t lock;
+};
struct zfcp_adapter {
struct list_head list; /* list of adapters */
@@ -723,6 +740,7 @@ struct zfcp_adapter {
u32 adapter_features; /* FCP channel features */
u32 connection_features; /* host connection features */
u32 hardware_version; /* of FCP channel */
+ u16 timer_ticks; /* time int for a tick */
struct Scsi_Host *scsi_host; /* Pointer to mid-layer */
struct list_head port_list_head; /* remote port list */
struct list_head port_remove_lh; /* head of ports to be
@@ -740,7 +758,8 @@ struct zfcp_adapter {
rwlock_t abort_lock; /* Protects against SCSI
stack abort/command
completion races */
- u16 status_read_failed; /* # failed status reads */
+ atomic_t stat_miss; /* # missing status reads*/
+ struct work_struct stat_work;
atomic_t status; /* status of this adapter */
struct list_head erp_ready_head; /* error recovery for this
adapter/devices */
@@ -822,6 +841,7 @@ struct zfcp_unit {
struct scsi_device *device; /* scsi device struct pointer */
struct zfcp_erp_action erp_action; /* pending error recovery */
atomic_t erp_counter;
+ struct zfcp_latencies latencies;
};
/* FSF request */
@@ -831,12 +851,12 @@ struct zfcp_fsf_req {
struct zfcp_adapter *adapter; /* adapter request belongs to */
u8 sbal_number; /* nr of SBALs free for use */
u8 sbal_first; /* first SBAL for this request */
- u8 sbal_last; /* last possible SBAL for
+ u8 sbal_last; /* last SBAL for this request */
+ u8 sbal_limit; /* last possible SBAL for
this reuest */
- u8 sbal_curr; /* current SBAL during creation
- of request */
u8 sbale_curr; /* current SBALE during creation
of request */
+ u8 sbal_response; /* SBAL used in interrupt */
wait_queue_head_t completion_wq; /* can be used by a routine
to wait for completion */
volatile u32 status; /* status of this request */
@@ -870,7 +890,7 @@ struct zfcp_data {
struct semaphore config_sema; /* serialises configuration
changes */
atomic_t loglevel; /* current loglevel */
- char init_busid[BUS_ID_SIZE];
+ char init_busid[20];
wwn_t init_wwpn;
fcp_lun_t init_fcp_lun;
char *driver_version;
@@ -922,7 +942,7 @@ extern void _zfcp_hex_dump(char *, int);
_zfcp_hex_dump(addr, count); \
}
-#define zfcp_get_busid_by_adapter(adapter) (adapter->ccw_device->dev.bus_id)
+#define zfcp_get_busid_by_adapter(adapter) (dev_name(&adapter->ccw_device->dev))
#define zfcp_get_busid_by_port(port) (zfcp_get_busid_by_adapter(port->adapter))
#define zfcp_get_busid_by_unit(unit) (zfcp_get_busid_by_port(unit->port))
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index 805484658dd9..d05b37054897 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -783,7 +783,7 @@ zfcp_erp_action_ready(struct zfcp_erp_action *erp_action)
zfcp_erp_action_to_ready(erp_action);
up(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread(2, adapter, 0);
+ zfcp_rec_dbf_event_thread(2, adapter);
}
/*
@@ -995,7 +995,7 @@ zfcp_erp_thread_kill(struct zfcp_adapter *adapter)
atomic_set_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_KILL, &adapter->status);
up(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread(2, adapter, 1);
+ zfcp_rec_dbf_event_thread_lock(2, adapter);
wait_event(adapter->erp_thread_wqh,
!atomic_test_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP,
@@ -1050,9 +1050,9 @@ zfcp_erp_thread(void *data)
* no action in 'ready' queue to be processed and
* thread is not to be killed
*/
- zfcp_rec_dbf_event_thread(4, adapter, 1);
+ zfcp_rec_dbf_event_thread_lock(4, adapter);
down_interruptible(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread(5, adapter, 1);
+ zfcp_rec_dbf_event_thread_lock(5, adapter);
}
atomic_clear_mask(ZFCP_STATUS_ADAPTER_ERP_THREAD_UP, &adapter->status);
@@ -2062,9 +2062,9 @@ zfcp_erp_adapter_strategy_open_fsf_xconfig(struct zfcp_erp_action *erp_action)
* _must_ be the one belonging to the 'exchange config
* data' request.
*/
- zfcp_rec_dbf_event_thread(6, adapter, 1);
+ zfcp_rec_dbf_event_thread_lock(6, adapter);
down(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread(7, adapter, 1);
+ zfcp_rec_dbf_event_thread_lock(7, adapter);
if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) {
ZFCP_LOG_INFO("error: exchange of configuration data "
"for adapter %s timed out\n",
@@ -2118,9 +2118,9 @@ zfcp_erp_adapter_strategy_open_fsf_xport(struct zfcp_erp_action *erp_action)
}
ret = ZFCP_ERP_SUCCEEDED;
- zfcp_rec_dbf_event_thread(8, adapter, 1);
+ zfcp_rec_dbf_event_thread_lock(8, adapter);
down(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread(9, adapter, 1);
+ zfcp_rec_dbf_event_thread_lock(9, adapter);
if (erp_action->status & ZFCP_STATUS_ERP_TIMEDOUT) {
ZFCP_LOG_INFO("error: exchange port data timed out (adapter "
"%s)\n", zfcp_get_busid_by_adapter(adapter));
@@ -2139,25 +2139,10 @@ static int
zfcp_erp_adapter_strategy_open_fsf_statusread(struct zfcp_erp_action
*erp_action)
{
- int retval = ZFCP_ERP_SUCCEEDED;
- int temp_ret;
struct zfcp_adapter *adapter = erp_action->adapter;
- int i;
- adapter->status_read_failed = 0;
- for (i = 0; i < ZFCP_STATUS_READS_RECOM; i++) {
- temp_ret = zfcp_fsf_status_read(adapter, ZFCP_WAIT_FOR_SBAL);
- if (temp_ret < 0) {
- ZFCP_LOG_INFO("error: set-up of unsolicited status "
- "notification failed on adapter %s\n",
- zfcp_get_busid_by_adapter(adapter));
- retval = ZFCP_ERP_FAILED;
- i--;
- break;
- }
- }
-
- return retval;
+ atomic_set(&adapter->stat_miss, 16);
+ return zfcp_status_read_refill(adapter);
}
/*
@@ -2891,7 +2876,7 @@ static int zfcp_erp_action_enqueue(int want, struct zfcp_adapter *adapter,
/* finally put it into 'ready' queue and kick erp thread */
list_add_tail(&erp_action->list, &adapter->erp_ready_head);
up(&adapter->erp_ready_sem);
- zfcp_rec_dbf_event_thread(1, adapter, 0);
+ zfcp_rec_dbf_event_thread(1, adapter);
retval = 0;
out:
zfcp_rec_dbf_event_trigger(id, ref, want, need, erp_action,
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index 6abf178fda5d..e47ab8d05b67 100644
--- a/drivers/s390/scsi/zfcp_ext.h
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -92,8 +92,10 @@ extern void zfcp_fsf_start_timer(struct zfcp_fsf_req *, unsigned long);
extern void zfcp_erp_start_timer(struct zfcp_fsf_req *);
extern void zfcp_fsf_req_dismiss_all(struct zfcp_adapter *);
extern int zfcp_fsf_status_read(struct zfcp_adapter *, int);
+extern int zfcp_status_read_refill(struct zfcp_adapter *adapter);
extern int zfcp_fsf_req_create(struct zfcp_adapter *, u32, int, mempool_t *,
- unsigned long *, struct zfcp_fsf_req **);
+ unsigned long *, struct zfcp_fsf_req **)
+ __acquires(adapter->request_queue.queue_lock);
extern int zfcp_fsf_send_ct(struct zfcp_send_ct *, mempool_t *,
struct zfcp_erp_action *);
extern int zfcp_fsf_send_els(struct zfcp_send_els *);
@@ -167,8 +169,8 @@ extern void zfcp_erp_port_access_changed(struct zfcp_port *, u8, void *);
extern void zfcp_erp_unit_access_changed(struct zfcp_unit *, u8, void *);
/******************************** AUX ****************************************/
-extern void zfcp_rec_dbf_event_thread(u8 id, struct zfcp_adapter *adapter,
- int lock);
+extern void zfcp_rec_dbf_event_thread(u8 id, struct zfcp_adapter *adapter);
+extern void zfcp_rec_dbf_event_thread_lock(u8 id, struct zfcp_adapter *adapter);
extern void zfcp_rec_dbf_event_adapter(u8 id, void *ref, struct zfcp_adapter *);
extern void zfcp_rec_dbf_event_port(u8 id, void *ref, struct zfcp_port *port);
extern void zfcp_rec_dbf_event_unit(u8 id, void *ref, struct zfcp_unit *unit);
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index b2ea4ea051f5..01f9b27daa8c 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -712,7 +712,7 @@ zfcp_fsf_status_read(struct zfcp_adapter *adapter, int req_flags)
struct fsf_status_read_buffer *status_buffer;
unsigned long lock_flags;
volatile struct qdio_buffer_element *sbale;
- int retval = 0;
+ int retval;
/* setup new FSF request */
retval = zfcp_fsf_req_create(adapter, FSF_QTCB_UNSOLICITED_STATUS,
@@ -726,17 +726,16 @@ zfcp_fsf_status_read(struct zfcp_adapter *adapter, int req_flags)
goto failed_req_create;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_last, 0);
sbale[0].flags |= SBAL_FLAGS0_TYPE_STATUS;
sbale[2].flags |= SBAL_FLAGS_LAST_ENTRY;
fsf_req->sbale_curr = 2;
+ retval = -ENOMEM;
status_buffer =
mempool_alloc(adapter->pool.data_status_read, GFP_ATOMIC);
- if (!status_buffer) {
- ZFCP_LOG_NORMAL("bug: could not get some buffer\n");
+ if (!status_buffer)
goto failed_buf;
- }
memset(status_buffer, 0, sizeof (struct fsf_status_read_buffer));
fsf_req->data = (unsigned long) status_buffer;
@@ -1029,21 +1028,9 @@ zfcp_fsf_status_read_handler(struct zfcp_fsf_req *fsf_req)
* FIXME:
* allocation failure possible? (Is this code needed?)
*/
- retval = zfcp_fsf_status_read(adapter, 0);
- if (retval < 0) {
- ZFCP_LOG_INFO("Failed to create unsolicited status read "
- "request for the adapter %s.\n",
- zfcp_get_busid_by_adapter(adapter));
- /* temporary fix to avoid status read buffer shortage */
- adapter->status_read_failed++;
- if ((ZFCP_STATUS_READS_RECOM - adapter->status_read_failed)
- < ZFCP_STATUS_READ_FAILED_THRESHOLD) {
- ZFCP_LOG_INFO("restart adapter %s due to status read "
- "buffer shortage\n",
- zfcp_get_busid_by_adapter(adapter));
- zfcp_erp_adapter_reopen(adapter, 0, 103, fsf_req);
- }
- }
+
+ atomic_inc(&adapter->stat_miss);
+ schedule_work(&adapter->stat_work);
out:
return retval;
}
@@ -1088,7 +1075,7 @@ zfcp_fsf_abort_fcp_command(unsigned long old_req_id,
&unit->status)))
goto unit_blocked;
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_last, 0);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -1308,7 +1295,7 @@ zfcp_fsf_send_ct(struct zfcp_send_ct *ct, mempool_t *pool,
goto failed_req;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_last, 0);
if (zfcp_use_one_sbal(ct->req, ct->req_count,
ct->resp, ct->resp_count)){
/* both request buffer and response buffer
@@ -1606,7 +1593,7 @@ zfcp_fsf_send_els(struct zfcp_send_els *els)
goto port_blocked;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_last, 0);
if (zfcp_use_one_sbal(els->req, els->req_count,
els->resp, els->resp_count)){
/* both request buffer and response buffer
@@ -1670,7 +1657,7 @@ zfcp_fsf_send_els(struct zfcp_send_els *els)
fsf_req->qtcb->bottom.support.timeout = ZFCP_ELS_TIMEOUT;
fsf_req->data = (unsigned long) els;
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_last, 0);
zfcp_san_dbf_event_els_request(fsf_req);
@@ -1885,7 +1872,7 @@ zfcp_fsf_exchange_config_data(struct zfcp_erp_action *erp_action)
return retval;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_last, 0);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -1938,7 +1925,7 @@ zfcp_fsf_exchange_config_data_sync(struct zfcp_adapter *adapter,
return retval;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_last, 0);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -2005,6 +1992,7 @@ zfcp_fsf_exchange_config_evaluate(struct zfcp_fsf_req *fsf_req, int xchg_ok)
fc_host_supported_classes(shost) =
FC_COS_CLASS2 | FC_COS_CLASS3;
adapter->hydra_version = bottom->adapter_type;
+ adapter->timer_ticks = bottom->timer_interval;
if (fc_host_permanent_port_name(shost) == -1)
fc_host_permanent_port_name(shost) =
fc_host_port_name(shost);
@@ -2199,7 +2187,7 @@ zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action)
return retval;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_last, 0);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -2261,7 +2249,7 @@ zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *adapter,
if (data)
fsf_req->data = (unsigned long) data;
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_last, 0);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -2371,7 +2359,7 @@ zfcp_fsf_open_port(struct zfcp_erp_action *erp_action)
goto out;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_last, 0);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -2603,7 +2591,7 @@ zfcp_fsf_close_port(struct zfcp_erp_action *erp_action)
goto out;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_last, 0);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -2732,7 +2720,7 @@ zfcp_fsf_close_physical_port(struct zfcp_erp_action *erp_action)
goto out;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_last, 0);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -2927,7 +2915,7 @@ zfcp_fsf_open_unit(struct zfcp_erp_action *erp_action)
goto out;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_last, 0);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -3242,7 +3230,7 @@ zfcp_fsf_close_unit(struct zfcp_erp_action *erp_action)
goto out;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_last, 0);
sbale[0].flags |= SBAL_FLAGS0_TYPE_READ;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -3625,7 +3613,7 @@ zfcp_fsf_send_fcp_command_task_management(struct zfcp_adapter *adapter,
fsf_req->qtcb->bottom.io.fcp_cmnd_length =
sizeof (struct fcp_cmnd_iu) + sizeof (fcp_dl_t);
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_last, 0);
sbale[0].flags |= SBAL_FLAGS0_TYPE_WRITE;
sbale[1].flags |= SBAL_FLAGS_LAST_ENTRY;
@@ -3649,6 +3637,46 @@ zfcp_fsf_send_fcp_command_task_management(struct zfcp_adapter *adapter,
return fsf_req;
}
+static void zfcp_fsf_update_lat(struct fsf_latency_record *lat_rec, u32 lat)
+{
+ lat_rec->sum += lat;
+ if (lat_rec->min > lat)
+ lat_rec->min = lat;
+ if (lat_rec->max < lat)
+ lat_rec->max = lat;
+}
+
+static void zfcp_fsf_req_latency(struct zfcp_fsf_req *fsf_req)
+{
+ struct fsf_qual_latency_info *lat_inf;
+ struct latency_cont *lat;
+ struct zfcp_unit *unit;
+ unsigned long flags;
+
+ lat_inf = &fsf_req->qtcb->prefix.prot_status_qual.latency_info;
+ unit = fsf_req->unit;
+
+ switch (fsf_req->qtcb->bottom.io.data_direction) {
+ case FSF_DATADIR_READ:
+ lat = &unit->latencies.read;
+ break;
+ case FSF_DATADIR_WRITE:
+ lat = &unit->latencies.write;
+ break;
+ case FSF_DATADIR_CMND:
+ lat = &unit->latencies.cmd;
+ break;
+ default:
+ return;
+ }
+
+ spin_lock_irqsave(&unit->latencies.lock, flags);
+ zfcp_fsf_update_lat(&lat->channel, lat_inf->channel_lat);
+ zfcp_fsf_update_lat(&lat->fabric, lat_inf->fabric_lat);
+ lat->counter++;
+ spin_unlock_irqrestore(&unit->latencies.lock, flags);
+}
+
/*
* function: zfcp_fsf_send_fcp_command_handler
*
@@ -3922,6 +3950,9 @@ zfcp_fsf_send_fcp_command_task_handler(struct zfcp_fsf_req *fsf_req)
fcp_rsp_iu->fcp_sns_len);
}
+ if (fsf_req->adapter->adapter_features & FSF_FEATURE_MEASUREMENT_DATA)
+ zfcp_fsf_req_latency(fsf_req);
+
/* check FCP_RSP_INFO */
if (unlikely(fcp_rsp_iu->validity.bits.fcp_rsp_len_valid)) {
ZFCP_LOG_DEBUG("rsp_len is valid\n");
@@ -4207,7 +4238,7 @@ zfcp_fsf_control_file(struct zfcp_adapter *adapter,
goto unlock_queue_lock;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_last, 0);
sbale[0].flags |= direction;
bottom = &fsf_req->qtcb->bottom.support;
@@ -4559,14 +4590,14 @@ zfcp_fsf_req_create(struct zfcp_adapter *adapter, u32 fsf_cmd, int req_flags,
}
fsf_req->sbal_number = 1;
fsf_req->sbal_first = req_queue->free_index;
- fsf_req->sbal_curr = req_queue->free_index;
+ fsf_req->sbal_last = req_queue->free_index;
fsf_req->sbale_curr = 1;
if (likely(req_flags & ZFCP_REQ_AUTO_CLEANUP)) {
fsf_req->status |= ZFCP_STATUS_FSFREQ_CLEANUP;
}
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_last, 0);
/* setup common SBALE fields */
sbale[0].addr = (void *) fsf_req->req_id;
diff --git a/drivers/s390/scsi/zfcp_fsf.h b/drivers/s390/scsi/zfcp_fsf.h
index 099970b27001..8b1a7d9c840f 100644
--- a/drivers/s390/scsi/zfcp_fsf.h
+++ b/drivers/s390/scsi/zfcp_fsf.h
@@ -323,11 +323,18 @@ struct fsf_link_down_info {
u8 vendor_specific_code;
} __attribute__ ((packed));
+struct fsf_qual_latency_info {
+ u32 channel_lat;
+ u32 fabric_lat;
+ u8 res1[8];
+} __attribute__ ((packed));
+
union fsf_prot_status_qual {
u64 doubleword[FSF_PROT_STATUS_QUAL_SIZE / sizeof(u64)];
struct fsf_qual_version_error version_error;
struct fsf_qual_sequence_error sequence_error;
struct fsf_link_down_info link_down_info;
+ struct fsf_qual_latency_info latency_info;
} __attribute__ ((packed));
struct fsf_qtcb_prefix {
@@ -437,7 +444,9 @@ struct fsf_qtcb_bottom_config {
u32 fc_link_speed;
u32 adapter_type;
u32 peer_d_id;
- u8 res2[12];
+ u8 res1[2];
+ u16 timer_interval;
+ u8 res2[8];
u32 s_id;
struct fsf_nport_serv_param nport_serv_param;
u8 reserved_nport_serv_param[16];
diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c
index 8ca5f074c687..e71547357f62 100644
--- a/drivers/s390/scsi/zfcp_qdio.c
+++ b/drivers/s390/scsi/zfcp_qdio.c
@@ -235,7 +235,7 @@ zfcp_qdio_request_handler(struct ccw_device *ccw_device,
* zfcp_qdio_reqid_check - checks for valid reqids.
*/
static void zfcp_qdio_reqid_check(struct zfcp_adapter *adapter,
- unsigned long req_id)
+ unsigned long req_id, int sbal)
{
struct zfcp_fsf_req *fsf_req;
unsigned long flags;
@@ -255,6 +255,7 @@ static void zfcp_qdio_reqid_check(struct zfcp_adapter *adapter,
atomic_dec(&adapter->reqs_active);
spin_unlock_irqrestore(&adapter->req_list_lock, flags);
+ fsf_req->sbal_response = sbal;
/* finish the FSF request */
zfcp_fsf_req_complete(fsf_req);
}
@@ -321,7 +322,7 @@ zfcp_qdio_response_handler(struct ccw_device *ccw_device,
/* look for QDIO request identifiers in SB */
buffere = &buffer->element[buffere_index];
zfcp_qdio_reqid_check(adapter,
- (unsigned long) buffere->addr);
+ (unsigned long) buffere->addr, i);
/*
* A single used SBALE per inbound SBALE has been
@@ -415,7 +416,7 @@ zfcp_qdio_sbale_resp(struct zfcp_fsf_req *fsf_req, int sbal, int sbale)
volatile struct qdio_buffer_element *
zfcp_qdio_sbale_curr(struct zfcp_fsf_req *fsf_req)
{
- return zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr,
+ return zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_last,
fsf_req->sbale_curr);
}
@@ -432,9 +433,9 @@ zfcp_qdio_sbal_limit(struct zfcp_fsf_req *fsf_req, int max_sbals)
{
int count = atomic_read(&fsf_req->adapter->request_queue.free_count);
count = min(count, max_sbals);
- fsf_req->sbal_last = fsf_req->sbal_first;
- fsf_req->sbal_last += (count - 1);
- fsf_req->sbal_last %= QDIO_MAX_BUFFERS_PER_Q;
+ fsf_req->sbal_limit = fsf_req->sbal_first;
+ fsf_req->sbal_limit += (count - 1);
+ fsf_req->sbal_limit %= QDIO_MAX_BUFFERS_PER_Q;
}
/**
@@ -443,7 +444,7 @@ zfcp_qdio_sbal_limit(struct zfcp_fsf_req *fsf_req, int max_sbals)
* @fsf_req: zfcp_fsf_req to be processed
* @sbtype: SBAL flags which have to be set in first SBALE of new SBAL
*
- * This function changes sbal_curr, sbale_curr, sbal_number of fsf_req.
+ * This function changes sbal_last, sbale_curr, sbal_number of fsf_req.
*/
static volatile struct qdio_buffer_element *
zfcp_qdio_sbal_chain(struct zfcp_fsf_req *fsf_req, unsigned long sbtype)
@@ -455,16 +456,16 @@ zfcp_qdio_sbal_chain(struct zfcp_fsf_req *fsf_req, unsigned long sbtype)
sbale->flags |= SBAL_FLAGS_LAST_ENTRY;
/* don't exceed last allowed SBAL */
- if (fsf_req->sbal_curr == fsf_req->sbal_last)
+ if (fsf_req->sbal_last == fsf_req->sbal_limit)
return NULL;
/* set chaining flag in first SBALE of current SBAL */
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_last, 0);
sbale->flags |= SBAL_FLAGS0_MORE_SBALS;
/* calculate index of next SBAL */
- fsf_req->sbal_curr++;
- fsf_req->sbal_curr %= QDIO_MAX_BUFFERS_PER_Q;
+ fsf_req->sbal_last++;
+ fsf_req->sbal_last %= QDIO_MAX_BUFFERS_PER_Q;
/* keep this requests number of SBALs up-to-date */
fsf_req->sbal_number++;
@@ -523,7 +524,7 @@ static inline int
zfcp_qdio_sbals_wipe(struct zfcp_fsf_req *fsf_req)
{
return zfcp_qdio_sbals_zero(&fsf_req->adapter->request_queue,
- fsf_req->sbal_first, fsf_req->sbal_curr);
+ fsf_req->sbal_first, fsf_req->sbal_last);
}
@@ -601,7 +602,7 @@ zfcp_qdio_sbals_from_sg(struct zfcp_fsf_req *fsf_req, unsigned long sbtype,
zfcp_qdio_sbal_limit(fsf_req, max_sbals);
/* set storage-block type for current SBAL */
- sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_curr, 0);
+ sbale = zfcp_qdio_sbale_req(fsf_req, fsf_req->sbal_last, 0);
sbale->flags |= sbtype;
/* process all segements of scatter-gather list */
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 01687559dc06..4b0c85acb0f0 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -778,6 +778,68 @@ struct fc_function_template zfcp_transport_functions = {
.disable_target_scan = 1,
};
+#define ZFCP_DEFINE_LATENCY_ATTR(_name) \
+static ssize_t \
+zfcp_sysfs_unit_##_name##_latency_show(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) { \
+ struct scsi_device *sdev = to_scsi_device(dev); \
+ struct zfcp_unit *unit = sdev->hostdata; \
+ struct zfcp_latencies *lat = &unit->latencies; \
+ struct zfcp_adapter *adapter = unit->port->adapter; \
+ unsigned long flags; \
+ unsigned long long fsum, fmin, fmax, csum, cmin, cmax, cc; \
+ \
+ spin_lock_irqsave(&lat->lock, flags); \
+ fsum = lat->_name.fabric.sum * adapter->timer_ticks; \
+ fmin = lat->_name.fabric.min * adapter->timer_ticks; \
+ fmax = lat->_name.fabric.max * adapter->timer_ticks; \
+ csum = lat->_name.channel.sum * adapter->timer_ticks; \
+ cmin = lat->_name.channel.min * adapter->timer_ticks; \
+ cmax = lat->_name.channel.max * adapter->timer_ticks; \
+ cc = lat->_name.counter; \
+ spin_unlock_irqrestore(&lat->lock, flags); \
+ \
+ do_div(fsum, 1000); \
+ do_div(fmin, 1000); \
+ do_div(fmax, 1000); \
+ do_div(csum, 1000); \
+ do_div(cmin, 1000); \
+ do_div(cmax, 1000); \
+ \
+ return sprintf(buf, "%llu %llu %llu %llu %llu %llu %llu\n", \
+ fmin, fmax, fsum, cmin, cmax, csum, cc); \
+} \
+static ssize_t \
+zfcp_sysfs_unit_##_name##_latency_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct scsi_device *sdev = to_scsi_device(dev); \
+ struct zfcp_unit *unit = sdev->hostdata; \
+ struct zfcp_latencies *lat = &unit->latencies; \
+ unsigned long flags; \
+ \
+ spin_lock_irqsave(&lat->lock, flags); \
+ lat->_name.fabric.sum = 0; \
+ lat->_name.fabric.min = 0xFFFFFFFF; \
+ lat->_name.fabric.max = 0; \
+ lat->_name.channel.sum = 0; \
+ lat->_name.channel.min = 0xFFFFFFFF; \
+ lat->_name.channel.max = 0; \
+ lat->_name.counter = 0; \
+ spin_unlock_irqrestore(&lat->lock, flags); \
+ \
+ return (ssize_t) count; \
+} \
+static DEVICE_ATTR(_name##_latency, S_IWUSR | S_IRUGO, \
+ zfcp_sysfs_unit_##_name##_latency_show, \
+ zfcp_sysfs_unit_##_name##_latency_store);
+
+ZFCP_DEFINE_LATENCY_ATTR(read);
+ZFCP_DEFINE_LATENCY_ATTR(write);
+ZFCP_DEFINE_LATENCY_ATTR(cmd);
+
/**
* ZFCP_DEFINE_SCSI_ATTR
* @_name: name of show attribute
@@ -808,6 +870,9 @@ static struct device_attribute *zfcp_sysfs_sdev_attrs[] = {
&dev_attr_fcp_lun,
&dev_attr_wwpn,
&dev_attr_hba_id,
+ &dev_attr_read_latency,
+ &dev_attr_write_latency,
+ &dev_attr_cmd_latency,
NULL
};
diff --git a/drivers/sbus/sbus.c b/drivers/sbus/sbus.c
index c37d7c2587ff..73a86d09bba8 100644
--- a/drivers/sbus/sbus.c
+++ b/drivers/sbus/sbus.c
@@ -78,7 +78,7 @@ static void __init fill_sbus_device(struct device_node *dp, struct sbus_dev *sde
else
sdev->ofdev.dev.parent = &sdev->bus->ofdev.dev;
sdev->ofdev.dev.bus = &sbus_bus_type;
- sprintf(sdev->ofdev.dev.bus_id, "sbus[%08x]", dp->node);
+ dev_set_name(&sdev->ofdev.dev, "sbus[%08x]", dp->node);
if (of_device_register(&sdev->ofdev) != 0)
printk(KERN_DEBUG "sbus: device registration error for %s!\n",
@@ -257,11 +257,11 @@ static void __init build_one_sbus(struct device_node *dp, int num_sbus)
sbus->ofdev.node = dp;
sbus->ofdev.dev.parent = NULL;
sbus->ofdev.dev.bus = &sbus_bus_type;
- sprintf(sbus->ofdev.dev.bus_id, "sbus%d", num_sbus);
+ dev_set_name(&sbus->ofdev.dev, "sbus%d", num_sbus);
if (of_device_register(&sbus->ofdev) != 0)
printk(KERN_DEBUG "sbus: device registration error for %s!\n",
- sbus->ofdev.dev.bus_id);
+ dev_name(&sbus->ofdev.dev));
dev_dp = dp->child;
while (dev_dp) {
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 81ccbd7f9e34..7886ec6e5f87 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -1771,4 +1771,6 @@ endif # SCSI_LOWLEVEL
source "drivers/scsi/pcmcia/Kconfig"
+source "drivers/scsi/device_handler/Kconfig"
+
endmenu
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 6c775e350c98..aa8272e188ea 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_SCSI_ISCSI_ATTRS) += scsi_transport_iscsi.o
obj-$(CONFIG_SCSI_SAS_ATTRS) += scsi_transport_sas.o
obj-$(CONFIG_SCSI_SAS_LIBSAS) += libsas/
obj-$(CONFIG_SCSI_SRP_ATTRS) += scsi_transport_srp.o
+obj-$(CONFIG_SCSI_DH) += device_handler/
obj-$(CONFIG_ISCSI_TCP) += libiscsi.o iscsi_tcp.o
obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o
diff --git a/drivers/scsi/aacraid/commctrl.c b/drivers/scsi/aacraid/commctrl.c
index 5fd83deab36c..a7355260cfcf 100644
--- a/drivers/scsi/aacraid/commctrl.c
+++ b/drivers/scsi/aacraid/commctrl.c
@@ -41,6 +41,7 @@
#include <linux/kthread.h>
#include <linux/semaphore.h>
#include <asm/uaccess.h>
+#include <scsi/scsi_host.h>
#include "aacraid.h"
@@ -581,6 +582,14 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
for (i = 0; i < upsg->count; i++) {
u64 addr;
void* p;
+ if (upsg->sg[i].count >
+ (dev->adapter_info.options &
+ AAC_OPT_NEW_COMM) ?
+ (dev->scsi_host_ptr->max_sectors << 9) :
+ 65536) {
+ rcode = -EINVAL;
+ goto cleanup;
+ }
/* Does this really need to be GFP_DMA? */
p = kmalloc(upsg->sg[i].count,GFP_KERNEL|__GFP_DMA);
if(!p) {
@@ -625,6 +634,14 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
for (i = 0; i < usg->count; i++) {
u64 addr;
void* p;
+ if (usg->sg[i].count >
+ (dev->adapter_info.options &
+ AAC_OPT_NEW_COMM) ?
+ (dev->scsi_host_ptr->max_sectors << 9) :
+ 65536) {
+ rcode = -EINVAL;
+ goto cleanup;
+ }
/* Does this really need to be GFP_DMA? */
p = kmalloc(usg->sg[i].count,GFP_KERNEL|__GFP_DMA);
if(!p) {
@@ -667,6 +684,14 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
for (i = 0; i < upsg->count; i++) {
uintptr_t addr;
void* p;
+ if (usg->sg[i].count >
+ (dev->adapter_info.options &
+ AAC_OPT_NEW_COMM) ?
+ (dev->scsi_host_ptr->max_sectors << 9) :
+ 65536) {
+ rcode = -EINVAL;
+ goto cleanup;
+ }
/* Does this really need to be GFP_DMA? */
p = kmalloc(usg->sg[i].count,GFP_KERNEL|__GFP_DMA);
if(!p) {
@@ -698,6 +723,14 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
for (i = 0; i < upsg->count; i++) {
dma_addr_t addr;
void* p;
+ if (upsg->sg[i].count >
+ (dev->adapter_info.options &
+ AAC_OPT_NEW_COMM) ?
+ (dev->scsi_host_ptr->max_sectors << 9) :
+ 65536) {
+ rcode = -EINVAL;
+ goto cleanup;
+ }
p = kmalloc(upsg->sg[i].count, GFP_KERNEL);
if (!p) {
dprintk((KERN_DEBUG"aacraid: Could not allocate SG buffer - size = %d buffer number %d of %d\n",
diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c
index 1f7c83607f84..5797b2c42f08 100644
--- a/drivers/scsi/aacraid/linit.c
+++ b/drivers/scsi/aacraid/linit.c
@@ -862,7 +862,7 @@ static ssize_t aac_show_bios_version(struct device *device,
return len;
}
-ssize_t aac_show_serial_number(struct device *device,
+static ssize_t aac_show_serial_number(struct device *device,
struct device_attribute *attr, char *buf)
{
struct aac_dev *dev = (struct aac_dev*)class_to_shost(device)->hostdata;
diff --git a/drivers/scsi/aic94xx/aic94xx_sds.c b/drivers/scsi/aic94xx/aic94xx_sds.c
index 4446e3d584dc..8630a75b2872 100644
--- a/drivers/scsi/aic94xx/aic94xx_sds.c
+++ b/drivers/scsi/aic94xx/aic94xx_sds.c
@@ -1093,9 +1093,9 @@ out:
* @bytes_to_verify: total bytes to verify
*/
int asd_verify_flash_seg(struct asd_ha_struct *asd_ha,
- void *src, u32 dest_offset, u32 bytes_to_verify)
+ const void *src, u32 dest_offset, u32 bytes_to_verify)
{
- u8 *src_buf;
+ const u8 *src_buf;
u8 flash_char;
int err;
u32 nv_offset, reg, i;
@@ -1105,7 +1105,7 @@ int asd_verify_flash_seg(struct asd_ha_struct *asd_ha,
err = FLASH_OK;
nv_offset = dest_offset;
- src_buf = (u8 *)src;
+ src_buf = (const u8 *)src;
for (i = 0; i < bytes_to_verify; i++) {
flash_char = asd_read_reg_byte(asd_ha, reg + nv_offset + i);
if (flash_char != src_buf[i]) {
@@ -1124,9 +1124,9 @@ int asd_verify_flash_seg(struct asd_ha_struct *asd_ha,
* @bytes_to_write: total bytes to write
*/
int asd_write_flash_seg(struct asd_ha_struct *asd_ha,
- void *src, u32 dest_offset, u32 bytes_to_write)
+ const void *src, u32 dest_offset, u32 bytes_to_write)
{
- u8 *src_buf;
+ const u8 *src_buf;
u32 nv_offset, reg, i;
int err;
@@ -1153,7 +1153,7 @@ int asd_write_flash_seg(struct asd_ha_struct *asd_ha,
return err;
}
- src_buf = (u8 *)src;
+ src_buf = (const u8 *)src;
for (i = 0; i < bytes_to_write; i++) {
/* Setup program command sequence */
switch (asd_ha->hw_prof.flash.method) {
diff --git a/drivers/scsi/aic94xx/aic94xx_sds.h b/drivers/scsi/aic94xx/aic94xx_sds.h
index bb9795a04dc3..a06dc0114b8c 100644
--- a/drivers/scsi/aic94xx/aic94xx_sds.h
+++ b/drivers/scsi/aic94xx/aic94xx_sds.h
@@ -110,9 +110,9 @@ struct bios_file_header {
};
int asd_verify_flash_seg(struct asd_ha_struct *asd_ha,
- void *src, u32 dest_offset, u32 bytes_to_verify);
+ const void *src, u32 dest_offset, u32 bytes_to_verify);
int asd_write_flash_seg(struct asd_ha_struct *asd_ha,
- void *src, u32 dest_offset, u32 bytes_to_write);
+ const void *src, u32 dest_offset, u32 bytes_to_write);
int asd_chk_write_status(struct asd_ha_struct *asd_ha,
u32 sector_addr, u8 erase_flag);
int asd_check_flash_type(struct asd_ha_struct *asd_ha);
diff --git a/drivers/scsi/aic94xx/aic94xx_seq.c b/drivers/scsi/aic94xx/aic94xx_seq.c
index f4272ac4c685..8f98e33155e9 100644
--- a/drivers/scsi/aic94xx/aic94xx_seq.c
+++ b/drivers/scsi/aic94xx/aic94xx_seq.c
@@ -46,7 +46,7 @@
static const struct firmware *sequencer_fw;
static u16 cseq_vecs[CSEQ_NUM_VECS], lseq_vecs[LSEQ_NUM_VECS], mode2_task,
cseq_idle_loop, lseq_idle_loop;
-static u8 *cseq_code, *lseq_code;
+static const u8 *cseq_code, *lseq_code;
static u32 cseq_code_size, lseq_code_size;
static u16 first_scb_site_no = 0xFFFF;
@@ -1235,7 +1235,8 @@ int asd_release_firmware(void)
static int asd_request_firmware(struct asd_ha_struct *asd_ha)
{
int err, i;
- struct sequencer_file_header header, *hdr_ptr;
+ struct sequencer_file_header header;
+ const struct sequencer_file_header *hdr_ptr;
u32 csum = 0;
u16 *ptr_cseq_vecs, *ptr_lseq_vecs;
@@ -1249,7 +1250,7 @@ static int asd_request_firmware(struct asd_ha_struct *asd_ha)
if (err)
return err;
- hdr_ptr = (struct sequencer_file_header *)sequencer_fw->data;
+ hdr_ptr = (const struct sequencer_file_header *)sequencer_fw->data;
header.csum = le32_to_cpu(hdr_ptr->csum);
header.major = le32_to_cpu(hdr_ptr->major);
diff --git a/drivers/scsi/device_handler/Kconfig b/drivers/scsi/device_handler/Kconfig
new file mode 100644
index 000000000000..2adc0f666b68
--- /dev/null
+++ b/drivers/scsi/device_handler/Kconfig
@@ -0,0 +1,32 @@
+#
+# SCSI Device Handler configuration
+#
+
+menuconfig SCSI_DH
+ tristate "SCSI Device Handlers"
+ depends on SCSI
+ default n
+ help
+ SCSI Device Handlers provide device specific support for
+ devices utilized in multipath configurations. Say Y here to
+ select support for specific hardware.
+
+config SCSI_DH_RDAC
+ tristate "LSI RDAC Device Handler"
+ depends on SCSI_DH
+ help
+ If you have a LSI RDAC select y. Otherwise, say N.
+
+config SCSI_DH_HP_SW
+ tristate "HP/COMPAQ MSA Device Handler"
+ depends on SCSI_DH
+ help
+ If you have a HP/COMPAQ MSA device that requires START_STOP to
+ be sent to start it and cannot upgrade the firmware then select y.
+ Otherwise, say N.
+
+config SCSI_DH_EMC
+ tristate "EMC CLARiiON Device Handler"
+ depends on SCSI_DH
+ help
+ If you have a EMC CLARiiON select y. Otherwise, say N.
diff --git a/drivers/scsi/device_handler/Makefile b/drivers/scsi/device_handler/Makefile
new file mode 100644
index 000000000000..35272e93b1c8
--- /dev/null
+++ b/drivers/scsi/device_handler/Makefile
@@ -0,0 +1,7 @@
+#
+# SCSI Device Handler
+#
+obj-$(CONFIG_SCSI_DH) += scsi_dh.o
+obj-$(CONFIG_SCSI_DH_RDAC) += scsi_dh_rdac.o
+obj-$(CONFIG_SCSI_DH_HP_SW) += scsi_dh_hp_sw.o
+obj-$(CONFIG_SCSI_DH_EMC) += scsi_dh_emc.o
diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c
new file mode 100644
index 000000000000..ab6c21cd9689
--- /dev/null
+++ b/drivers/scsi/device_handler/scsi_dh.c
@@ -0,0 +1,162 @@
+/*
+ * SCSI device handler infrastruture.
+ *
+ * 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.
+ *
+ * Copyright IBM Corporation, 2007
+ * Authors:
+ * Chandra Seetharaman <sekharan@us.ibm.com>
+ * Mike Anderson <andmike@linux.vnet.ibm.com>
+ */
+
+#include <scsi/scsi_dh.h>
+#include "../scsi_priv.h"
+
+static DEFINE_SPINLOCK(list_lock);
+static LIST_HEAD(scsi_dh_list);
+
+static struct scsi_device_handler *get_device_handler(const char *name)
+{
+ struct scsi_device_handler *tmp, *found = NULL;
+
+ spin_lock(&list_lock);
+ list_for_each_entry(tmp, &scsi_dh_list, list) {
+ if (!strcmp(tmp->name, name)) {
+ found = tmp;
+ break;
+ }
+ }
+ spin_unlock(&list_lock);
+ return found;
+}
+
+static int scsi_dh_notifier_add(struct device *dev, void *data)
+{
+ struct scsi_device_handler *scsi_dh = data;
+
+ scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_ADD_DEVICE, dev);
+ return 0;
+}
+
+/*
+ * scsi_register_device_handler - register a device handler personality
+ * module.
+ * @scsi_dh - device handler to be registered.
+ *
+ * Returns 0 on success, -EBUSY if handler already registered.
+ */
+int scsi_register_device_handler(struct scsi_device_handler *scsi_dh)
+{
+ int ret = -EBUSY;
+ struct scsi_device_handler *tmp;
+
+ tmp = get_device_handler(scsi_dh->name);
+ if (tmp)
+ goto done;
+
+ ret = bus_register_notifier(&scsi_bus_type, &scsi_dh->nb);
+
+ bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add);
+ spin_lock(&list_lock);
+ list_add(&scsi_dh->list, &scsi_dh_list);
+ spin_unlock(&list_lock);
+
+done:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(scsi_register_device_handler);
+
+static int scsi_dh_notifier_remove(struct device *dev, void *data)
+{
+ struct scsi_device_handler *scsi_dh = data;
+
+ scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_DEL_DEVICE, dev);
+ return 0;
+}
+
+/*
+ * scsi_unregister_device_handler - register a device handler personality
+ * module.
+ * @scsi_dh - device handler to be unregistered.
+ *
+ * Returns 0 on success, -ENODEV if handler not registered.
+ */
+int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
+{
+ int ret = -ENODEV;
+ struct scsi_device_handler *tmp;
+
+ tmp = get_device_handler(scsi_dh->name);
+ if (!tmp)
+ goto done;
+
+ ret = bus_unregister_notifier(&scsi_bus_type, &scsi_dh->nb);
+
+ bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh,
+ scsi_dh_notifier_remove);
+ spin_lock(&list_lock);
+ list_del(&scsi_dh->list);
+ spin_unlock(&list_lock);
+
+done:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(scsi_unregister_device_handler);
+
+/*
+ * scsi_dh_activate - activate the path associated with the scsi_device
+ * corresponding to the given request queue.
+ * @q - Request queue that is associated with the scsi_device to be
+ * activated.
+ */
+int scsi_dh_activate(struct request_queue *q)
+{
+ int err = 0;
+ unsigned long flags;
+ struct scsi_device *sdev;
+ struct scsi_device_handler *scsi_dh = NULL;
+
+ spin_lock_irqsave(q->queue_lock, flags);
+ sdev = q->queuedata;
+ if (sdev && sdev->scsi_dh_data)
+ scsi_dh = sdev->scsi_dh_data->scsi_dh;
+ if (!scsi_dh || !get_device(&sdev->sdev_gendev))
+ err = SCSI_DH_NOSYS;
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ if (err)
+ return err;
+
+ if (scsi_dh->activate)
+ err = scsi_dh->activate(sdev);
+ put_device(&sdev->sdev_gendev);
+ return err;
+}
+EXPORT_SYMBOL_GPL(scsi_dh_activate);
+
+/*
+ * scsi_dh_handler_exist - Return TRUE(1) if a device handler exists for
+ * the given name. FALSE(0) otherwise.
+ * @name - name of the device handler.
+ */
+int scsi_dh_handler_exist(const char *name)
+{
+ return (get_device_handler(name) != NULL);
+}
+EXPORT_SYMBOL_GPL(scsi_dh_handler_exist);
+
+MODULE_DESCRIPTION("SCSI device handler");
+MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c
new file mode 100644
index 000000000000..ed53f14007a2
--- /dev/null
+++ b/drivers/scsi/device_handler/scsi_dh_emc.c
@@ -0,0 +1,499 @@
+/*
+ * Target driver for EMC CLARiiON AX/CX-series hardware.
+ * Based on code from Lars Marowsky-Bree <lmb@suse.de>
+ * and Ed Goggin <egoggin@emc.com>.
+ *
+ * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2006 Mike Christie
+ *
+ * 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, 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <scsi/scsi.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_dh.h>
+#include <scsi/scsi_device.h>
+
+#define CLARIION_NAME "emc_clariion"
+
+#define CLARIION_TRESPASS_PAGE 0x22
+#define CLARIION_BUFFER_SIZE 0x80
+#define CLARIION_TIMEOUT (60 * HZ)
+#define CLARIION_RETRIES 3
+#define CLARIION_UNBOUND_LU -1
+
+static unsigned char long_trespass[] = {
+ 0, 0, 0, 0,
+ CLARIION_TRESPASS_PAGE, /* Page code */
+ 0x09, /* Page length - 2 */
+ 0x81, /* Trespass code + Honor reservation bit */
+ 0xff, 0xff, /* Trespass target */
+ 0, 0, 0, 0, 0, 0 /* Reserved bytes / unknown */
+};
+
+static unsigned char long_trespass_hr[] = {
+ 0, 0, 0, 0,
+ CLARIION_TRESPASS_PAGE, /* Page code */
+ 0x09, /* Page length - 2 */
+ 0x01, /* Trespass code + Honor reservation bit */
+ 0xff, 0xff, /* Trespass target */
+ 0, 0, 0, 0, 0, 0 /* Reserved bytes / unknown */
+};
+
+static unsigned char short_trespass[] = {
+ 0, 0, 0, 0,
+ CLARIION_TRESPASS_PAGE, /* Page code */
+ 0x02, /* Page length - 2 */
+ 0x81, /* Trespass code + Honor reservation bit */
+ 0xff, /* Trespass target */
+};
+
+static unsigned char short_trespass_hr[] = {
+ 0, 0, 0, 0,
+ CLARIION_TRESPASS_PAGE, /* Page code */
+ 0x02, /* Page length - 2 */
+ 0x01, /* Trespass code + Honor reservation bit */
+ 0xff, /* Trespass target */
+};
+
+struct clariion_dh_data {
+ /*
+ * Use short trespass command (FC-series) or the long version
+ * (default for AX/CX CLARiiON arrays).
+ */
+ unsigned short_trespass;
+ /*
+ * Whether or not (default) to honor SCSI reservations when
+ * initiating a switch-over.
+ */
+ unsigned hr;
+ /* I/O buffer for both MODE_SELECT and INQUIRY commands. */
+ char buffer[CLARIION_BUFFER_SIZE];
+ /*
+ * SCSI sense buffer for commands -- assumes serial issuance
+ * and completion sequence of all commands for same multipath.
+ */
+ unsigned char sense[SCSI_SENSE_BUFFERSIZE];
+ /* which SP (A=0,B=1,UNBOUND=-1) is dflt SP for path's mapped dev */
+ int default_sp;
+ /* which SP (A=0,B=1,UNBOUND=-1) is active for path's mapped dev */
+ int current_sp;
+};
+
+static inline struct clariion_dh_data
+ *get_clariion_data(struct scsi_device *sdev)
+{
+ struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
+ BUG_ON(scsi_dh_data == NULL);
+ return ((struct clariion_dh_data *) scsi_dh_data->buf);
+}
+
+/*
+ * Parse MODE_SELECT cmd reply.
+ */
+static int trespass_endio(struct scsi_device *sdev, int result)
+{
+ int err = SCSI_DH_OK;
+ struct scsi_sense_hdr sshdr;
+ struct clariion_dh_data *csdev = get_clariion_data(sdev);
+ char *sense = csdev->sense;
+
+ if (status_byte(result) == CHECK_CONDITION &&
+ scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) {
+ sdev_printk(KERN_ERR, sdev, "Found valid sense data 0x%2x, "
+ "0x%2x, 0x%2x while sending CLARiiON trespass "
+ "command.\n", sshdr.sense_key, sshdr.asc,
+ sshdr.ascq);
+
+ if ((sshdr.sense_key == 0x05) && (sshdr.asc == 0x04) &&
+ (sshdr.ascq == 0x00)) {
+ /*
+ * Array based copy in progress -- do not send
+ * mode_select or copy will be aborted mid-stream.
+ */
+ sdev_printk(KERN_INFO, sdev, "Array Based Copy in "
+ "progress while sending CLARiiON trespass "
+ "command.\n");
+ err = SCSI_DH_DEV_TEMP_BUSY;
+ } else if ((sshdr.sense_key == 0x02) && (sshdr.asc == 0x04) &&
+ (sshdr.ascq == 0x03)) {
+ /*
+ * LUN Not Ready - Manual Intervention Required
+ * indicates in-progress ucode upgrade (NDU).
+ */
+ sdev_printk(KERN_INFO, sdev, "Detected in-progress "
+ "ucode upgrade NDU operation while sending "
+ "CLARiiON trespass command.\n");
+ err = SCSI_DH_DEV_TEMP_BUSY;
+ } else
+ err = SCSI_DH_DEV_FAILED;
+ } else if (result) {
+ sdev_printk(KERN_ERR, sdev, "Error 0x%x while sending "
+ "CLARiiON trespass command.\n", result);
+ err = SCSI_DH_IO;
+ }
+
+ return err;
+}
+
+static int parse_sp_info_reply(struct scsi_device *sdev, int result,
+ int *default_sp, int *current_sp, int *new_current_sp)
+{
+ int err = SCSI_DH_OK;
+ struct clariion_dh_data *csdev = get_clariion_data(sdev);
+
+ if (result == 0) {
+ /* check for in-progress ucode upgrade (NDU) */
+ if (csdev->buffer[48] != 0) {
+ sdev_printk(KERN_NOTICE, sdev, "Detected in-progress "
+ "ucode upgrade NDU operation while finding "
+ "current active SP.");
+ err = SCSI_DH_DEV_TEMP_BUSY;
+ } else {
+ *default_sp = csdev->buffer[5];
+
+ if (csdev->buffer[4] == 2)
+ /* SP for path is current */
+ *current_sp = csdev->buffer[8];
+ else {
+ if (csdev->buffer[4] == 1)
+ /* SP for this path is NOT current */
+ if (csdev->buffer[8] == 0)
+ *current_sp = 1;
+ else
+ *current_sp = 0;
+ else
+ /* unbound LU or LUNZ */
+ *current_sp = CLARIION_UNBOUND_LU;
+ }
+ *new_current_sp = csdev->buffer[8];
+ }
+ } else {
+ struct scsi_sense_hdr sshdr;
+
+ err = SCSI_DH_IO;
+
+ if (scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE,
+ &sshdr))
+ sdev_printk(KERN_ERR, sdev, "Found valid sense data "
+ "0x%2x, 0x%2x, 0x%2x while finding current "
+ "active SP.", sshdr.sense_key, sshdr.asc,
+ sshdr.ascq);
+ else
+ sdev_printk(KERN_ERR, sdev, "Error 0x%x finding "
+ "current active SP.", result);
+ }
+
+ return err;
+}
+
+static int sp_info_endio(struct scsi_device *sdev, int result,
+ int mode_select_sent, int *done)
+{
+ struct clariion_dh_data *csdev = get_clariion_data(sdev);
+ int err_flags, default_sp, current_sp, new_current_sp;
+
+ err_flags = parse_sp_info_reply(sdev, result, &default_sp,
+ &current_sp, &new_current_sp);
+
+ if (err_flags != SCSI_DH_OK)
+ goto done;
+
+ if (mode_select_sent) {
+ csdev->default_sp = default_sp;
+ csdev->current_sp = current_sp;
+ } else {
+ /*
+ * Issue the actual module_selec request IFF either
+ * (1) we do not know the identity of the current SP OR
+ * (2) what we think we know is actually correct.
+ */
+ if ((current_sp != CLARIION_UNBOUND_LU) &&
+ (new_current_sp != current_sp)) {
+
+ csdev->default_sp = default_sp;
+ csdev->current_sp = current_sp;
+
+ sdev_printk(KERN_INFO, sdev, "Ignoring path group "
+ "switch-over command for CLARiiON SP%s since "
+ " mapped device is already initialized.",
+ current_sp ? "B" : "A");
+ if (done)
+ *done = 1; /* as good as doing it */
+ }
+ }
+done:
+ return err_flags;
+}
+
+/*
+* Get block request for REQ_BLOCK_PC command issued to path. Currently
+* limited to MODE_SELECT (trespass) and INQUIRY (VPD page 0xC0) commands.
+*
+* Uses data and sense buffers in hardware handler context structure and
+* assumes serial servicing of commands, both issuance and completion.
+*/
+static struct request *get_req(struct scsi_device *sdev, int cmd)
+{
+ struct clariion_dh_data *csdev = get_clariion_data(sdev);
+ struct request *rq;
+ unsigned char *page22;
+ int len = 0;
+
+ rq = blk_get_request(sdev->request_queue,
+ (cmd == MODE_SELECT) ? WRITE : READ, GFP_ATOMIC);
+ if (!rq) {
+ sdev_printk(KERN_INFO, sdev, "get_req: blk_get_request failed");
+ return NULL;
+ }
+
+ memset(&rq->cmd, 0, BLK_MAX_CDB);
+ rq->cmd[0] = cmd;
+ rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
+
+ switch (cmd) {
+ case MODE_SELECT:
+ if (csdev->short_trespass) {
+ page22 = csdev->hr ? short_trespass_hr : short_trespass;
+ len = sizeof(short_trespass);
+ } else {
+ page22 = csdev->hr ? long_trespass_hr : long_trespass;
+ len = sizeof(long_trespass);
+ }
+ /*
+ * Can't DMA from kernel BSS -- must copy selected trespass
+ * command mode page contents to context buffer which is
+ * allocated by kmalloc.
+ */
+ BUG_ON((len > CLARIION_BUFFER_SIZE));
+ memcpy(csdev->buffer, page22, len);
+ rq->cmd_flags |= REQ_RW;
+ rq->cmd[1] = 0x10;
+ break;
+ case INQUIRY:
+ rq->cmd[1] = 0x1;
+ rq->cmd[2] = 0xC0;
+ len = CLARIION_BUFFER_SIZE;
+ memset(csdev->buffer, 0, CLARIION_BUFFER_SIZE);
+ break;
+ default:
+ BUG_ON(1);
+ break;
+ }
+
+ rq->cmd[4] = len;
+ rq->cmd_type = REQ_TYPE_BLOCK_PC;
+ rq->cmd_flags |= REQ_FAILFAST;
+ rq->timeout = CLARIION_TIMEOUT;
+ rq->retries = CLARIION_RETRIES;
+
+ rq->sense = csdev->sense;
+ memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+ rq->sense_len = 0;
+
+ if (blk_rq_map_kern(sdev->request_queue, rq, csdev->buffer,
+ len, GFP_ATOMIC)) {
+ __blk_put_request(rq->q, rq);
+ return NULL;
+ }
+
+ return rq;
+}
+
+static int send_cmd(struct scsi_device *sdev, int cmd)
+{
+ struct request *rq = get_req(sdev, cmd);
+
+ if (!rq)
+ return SCSI_DH_RES_TEMP_UNAVAIL;
+
+ return blk_execute_rq(sdev->request_queue, NULL, rq, 1);
+}
+
+static int clariion_activate(struct scsi_device *sdev)
+{
+ int result, done = 0;
+
+ result = send_cmd(sdev, INQUIRY);
+ result = sp_info_endio(sdev, result, 0, &done);
+ if (result || done)
+ goto done;
+
+ result = send_cmd(sdev, MODE_SELECT);
+ result = trespass_endio(sdev, result);
+ if (result)
+ goto done;
+
+ result = send_cmd(sdev, INQUIRY);
+ result = sp_info_endio(sdev, result, 1, NULL);
+done:
+ return result;
+}
+
+static int clariion_check_sense(struct scsi_device *sdev,
+ struct scsi_sense_hdr *sense_hdr)
+{
+ switch (sense_hdr->sense_key) {
+ case NOT_READY:
+ if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x03)
+ /*
+ * LUN Not Ready - Manual Intervention Required
+ * indicates this is a passive path.
+ *
+ * FIXME: However, if this is seen and EVPD C0
+ * indicates that this is due to a NDU in
+ * progress, we should set FAIL_PATH too.
+ * This indicates we might have to do a SCSI
+ * inquiry in the end_io path. Ugh.
+ *
+ * Can return FAILED only when we want the error
+ * recovery process to kick in.
+ */
+ return SUCCESS;
+ break;
+ case ILLEGAL_REQUEST:
+ if (sense_hdr->asc == 0x25 && sense_hdr->ascq == 0x01)
+ /*
+ * An array based copy is in progress. Do not
+ * fail the path, do not bypass to another PG,
+ * do not retry. Fail the IO immediately.
+ * (Actually this is the same conclusion as in
+ * the default handler, but lets make sure.)
+ *
+ * Can return FAILED only when we want the error
+ * recovery process to kick in.
+ */
+ return SUCCESS;
+ break;
+ case UNIT_ATTENTION:
+ if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00)
+ /*
+ * Unit Attention Code. This is the first IO
+ * to the new path, so just retry.
+ */
+ return NEEDS_RETRY;
+ break;
+ }
+
+ /* success just means we do not care what scsi-ml does */
+ return SUCCESS;
+}
+
+static const struct {
+ char *vendor;
+ char *model;
+} clariion_dev_list[] = {
+ {"DGC", "RAID"},
+ {"DGC", "DISK"},
+ {NULL, NULL},
+};
+
+static int clariion_bus_notify(struct notifier_block *, unsigned long, void *);
+
+static struct scsi_device_handler clariion_dh = {
+ .name = CLARIION_NAME,
+ .module = THIS_MODULE,
+ .nb.notifier_call = clariion_bus_notify,
+ .check_sense = clariion_check_sense,
+ .activate = clariion_activate,
+};
+
+/*
+ * TODO: need some interface so we can set trespass values
+ */
+static int clariion_bus_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct device *dev = data;
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct scsi_dh_data *scsi_dh_data;
+ struct clariion_dh_data *h;
+ int i, found = 0;
+ unsigned long flags;
+
+ if (action == BUS_NOTIFY_ADD_DEVICE) {
+ for (i = 0; clariion_dev_list[i].vendor; i++) {
+ if (!strncmp(sdev->vendor, clariion_dev_list[i].vendor,
+ strlen(clariion_dev_list[i].vendor)) &&
+ !strncmp(sdev->model, clariion_dev_list[i].model,
+ strlen(clariion_dev_list[i].model))) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ goto out;
+
+ scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+ + sizeof(*h) , GFP_KERNEL);
+ if (!scsi_dh_data) {
+ sdev_printk(KERN_ERR, sdev, "Attach failed %s.\n",
+ CLARIION_NAME);
+ goto out;
+ }
+
+ scsi_dh_data->scsi_dh = &clariion_dh;
+ h = (struct clariion_dh_data *) scsi_dh_data->buf;
+ h->default_sp = CLARIION_UNBOUND_LU;
+ h->current_sp = CLARIION_UNBOUND_LU;
+
+ spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+ sdev->scsi_dh_data = scsi_dh_data;
+ spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+
+ sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", CLARIION_NAME);
+ try_module_get(THIS_MODULE);
+
+ } else if (action == BUS_NOTIFY_DEL_DEVICE) {
+ if (sdev->scsi_dh_data == NULL ||
+ sdev->scsi_dh_data->scsi_dh != &clariion_dh)
+ goto out;
+
+ spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+ scsi_dh_data = sdev->scsi_dh_data;
+ sdev->scsi_dh_data = NULL;
+ spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+
+ sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n",
+ CLARIION_NAME);
+
+ kfree(scsi_dh_data);
+ module_put(THIS_MODULE);
+ }
+
+out:
+ return 0;
+}
+
+static int __init clariion_init(void)
+{
+ int r;
+
+ r = scsi_register_device_handler(&clariion_dh);
+ if (r != 0)
+ printk(KERN_ERR "Failed to register scsi device handler.");
+ return r;
+}
+
+static void __exit clariion_exit(void)
+{
+ scsi_unregister_device_handler(&clariion_dh);
+}
+
+module_init(clariion_init);
+module_exit(clariion_exit);
+
+MODULE_DESCRIPTION("EMC CX/AX/FC-family driver");
+MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, Chandra Seetharaman <sekharan@us.ibm.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/device_handler/scsi_dh_hp_sw.c b/drivers/scsi/device_handler/scsi_dh_hp_sw.c
new file mode 100644
index 000000000000..12ceab7b3662
--- /dev/null
+++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c
@@ -0,0 +1,202 @@
+/*
+ * Basic HP/COMPAQ MSA 1000 support. This is only needed if your HW cannot be
+ * upgraded.
+ *
+ * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2006 Mike Christie
+ *
+ * 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, 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_dh.h>
+
+#define HP_SW_NAME "hp_sw"
+
+#define HP_SW_TIMEOUT (60 * HZ)
+#define HP_SW_RETRIES 3
+
+struct hp_sw_dh_data {
+ unsigned char sense[SCSI_SENSE_BUFFERSIZE];
+ int retries;
+};
+
+static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev)
+{
+ struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
+ BUG_ON(scsi_dh_data == NULL);
+ return ((struct hp_sw_dh_data *) scsi_dh_data->buf);
+}
+
+static int hp_sw_done(struct scsi_device *sdev)
+{
+ struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
+ struct scsi_sense_hdr sshdr;
+ int rc;
+
+ sdev_printk(KERN_INFO, sdev, "hp_sw_done\n");
+
+ rc = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sshdr);
+ if (!rc)
+ goto done;
+ switch (sshdr.sense_key) {
+ case NOT_READY:
+ if ((sshdr.asc == 0x04) && (sshdr.ascq == 3)) {
+ rc = SCSI_DH_RETRY;
+ h->retries++;
+ break;
+ }
+ /* fall through */
+ default:
+ h->retries++;
+ rc = SCSI_DH_IMM_RETRY;
+ }
+
+done:
+ if (rc == SCSI_DH_OK || rc == SCSI_DH_IO)
+ h->retries = 0;
+ else if (h->retries > HP_SW_RETRIES) {
+ h->retries = 0;
+ rc = SCSI_DH_IO;
+ }
+ return rc;
+}
+
+static int hp_sw_activate(struct scsi_device *sdev)
+{
+ struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
+ struct request *req;
+ int ret = SCSI_DH_RES_TEMP_UNAVAIL;
+
+ req = blk_get_request(sdev->request_queue, WRITE, GFP_ATOMIC);
+ if (!req)
+ goto done;
+
+ sdev_printk(KERN_INFO, sdev, "sending START_STOP.");
+
+ req->cmd_type = REQ_TYPE_BLOCK_PC;
+ req->cmd_flags |= REQ_FAILFAST;
+ req->cmd_len = COMMAND_SIZE(START_STOP);
+ memset(req->cmd, 0, MAX_COMMAND_SIZE);
+ req->cmd[0] = START_STOP;
+ req->cmd[4] = 1; /* Start spin cycle */
+ req->timeout = HP_SW_TIMEOUT;
+ req->sense = h->sense;
+ memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
+ req->sense_len = 0;
+
+ ret = blk_execute_rq(req->q, NULL, req, 1);
+ if (!ret) /* SUCCESS */
+ ret = hp_sw_done(sdev);
+ else
+ ret = SCSI_DH_IO;
+done:
+ return ret;
+}
+
+static const struct {
+ char *vendor;
+ char *model;
+} hp_sw_dh_data_list[] = {
+ {"COMPAQ", "MSA"},
+ {"HP", "HSV"},
+ {"DEC", "HSG80"},
+ {NULL, NULL},
+};
+
+static int hp_sw_bus_notify(struct notifier_block *, unsigned long, void *);
+
+static struct scsi_device_handler hp_sw_dh = {
+ .name = HP_SW_NAME,
+ .module = THIS_MODULE,
+ .nb.notifier_call = hp_sw_bus_notify,
+ .activate = hp_sw_activate,
+};
+
+static int hp_sw_bus_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct device *dev = data;
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct scsi_dh_data *scsi_dh_data;
+ int i, found = 0;
+ unsigned long flags;
+
+ if (action == BUS_NOTIFY_ADD_DEVICE) {
+ for (i = 0; hp_sw_dh_data_list[i].vendor; i++) {
+ if (!strncmp(sdev->vendor, hp_sw_dh_data_list[i].vendor,
+ strlen(hp_sw_dh_data_list[i].vendor)) &&
+ !strncmp(sdev->model, hp_sw_dh_data_list[i].model,
+ strlen(hp_sw_dh_data_list[i].model))) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ goto out;
+
+ scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+ + sizeof(struct hp_sw_dh_data) , GFP_KERNEL);
+ if (!scsi_dh_data) {
+ sdev_printk(KERN_ERR, sdev, "Attach Failed %s.\n",
+ HP_SW_NAME);
+ goto out;
+ }
+
+ scsi_dh_data->scsi_dh = &hp_sw_dh;
+ spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+ sdev->scsi_dh_data = scsi_dh_data;
+ spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+ try_module_get(THIS_MODULE);
+
+ sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", HP_SW_NAME);
+ } else if (action == BUS_NOTIFY_DEL_DEVICE) {
+ if (sdev->scsi_dh_data == NULL ||
+ sdev->scsi_dh_data->scsi_dh != &hp_sw_dh)
+ goto out;
+
+ spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+ scsi_dh_data = sdev->scsi_dh_data;
+ sdev->scsi_dh_data = NULL;
+ spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+ module_put(THIS_MODULE);
+
+ sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n", HP_SW_NAME);
+
+ kfree(scsi_dh_data);
+ }
+
+out:
+ return 0;
+}
+
+static int __init hp_sw_init(void)
+{
+ return scsi_register_device_handler(&hp_sw_dh);
+}
+
+static void __exit hp_sw_exit(void)
+{
+ scsi_unregister_device_handler(&hp_sw_dh);
+}
+
+module_init(hp_sw_init);
+module_exit(hp_sw_exit);
+
+MODULE_DESCRIPTION("HP MSA 1000");
+MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c
new file mode 100644
index 000000000000..6fff077a888d
--- /dev/null
+++ b/drivers/scsi/device_handler/scsi_dh_rdac.c
@@ -0,0 +1,691 @@
+/*
+ * Engenio/LSI RDAC SCSI Device Handler
+ *
+ * Copyright (C) 2005 Mike Christie. All rights reserved.
+ * Copyright (C) Chandra Seetharaman, IBM Corp. 2007
+ *
+ * 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 <scsi/scsi.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_dh.h>
+
+#define RDAC_NAME "rdac"
+
+/*
+ * LSI mode page stuff
+ *
+ * These struct definitions and the forming of the
+ * mode page were taken from the LSI RDAC 2.4 GPL'd
+ * driver, and then converted to Linux conventions.
+ */
+#define RDAC_QUIESCENCE_TIME 20;
+/*
+ * Page Codes
+ */
+#define RDAC_PAGE_CODE_REDUNDANT_CONTROLLER 0x2c
+
+/*
+ * Controller modes definitions
+ */
+#define RDAC_MODE_TRANSFER_SPECIFIED_LUNS 0x02
+
+/*
+ * RDAC Options field
+ */
+#define RDAC_FORCED_QUIESENCE 0x02
+
+#define RDAC_TIMEOUT (60 * HZ)
+#define RDAC_RETRIES 3
+
+struct rdac_mode_6_hdr {
+ u8 data_len;
+ u8 medium_type;
+ u8 device_params;
+ u8 block_desc_len;
+};
+
+struct rdac_mode_10_hdr {
+ u16 data_len;
+ u8 medium_type;
+ u8 device_params;
+ u16 reserved;
+ u16 block_desc_len;
+};
+
+struct rdac_mode_common {
+ u8 controller_serial[16];
+ u8 alt_controller_serial[16];
+ u8 rdac_mode[2];
+ u8 alt_rdac_mode[2];
+ u8 quiescence_timeout;
+ u8 rdac_options;
+};
+
+struct rdac_pg_legacy {
+ struct rdac_mode_6_hdr hdr;
+ u8 page_code;
+ u8 page_len;
+ struct rdac_mode_common common;
+#define MODE6_MAX_LUN 32
+ u8 lun_table[MODE6_MAX_LUN];
+ u8 reserved2[32];
+ u8 reserved3;
+ u8 reserved4;
+};
+
+struct rdac_pg_expanded {
+ struct rdac_mode_10_hdr hdr;
+ u8 page_code;
+ u8 subpage_code;
+ u8 page_len[2];
+ struct rdac_mode_common common;
+ u8 lun_table[256];
+ u8 reserved3;
+ u8 reserved4;
+};
+
+struct c9_inquiry {
+ u8 peripheral_info;
+ u8 page_code; /* 0xC9 */
+ u8 reserved1;
+ u8 page_len;
+ u8 page_id[4]; /* "vace" */
+ u8 avte_cvp;
+ u8 path_prio;
+ u8 reserved2[38];
+};
+
+#define SUBSYS_ID_LEN 16
+#define SLOT_ID_LEN 2
+
+struct c4_inquiry {
+ u8 peripheral_info;
+ u8 page_code; /* 0xC4 */
+ u8 reserved1;
+ u8 page_len;
+ u8 page_id[4]; /* "subs" */
+ u8 subsys_id[SUBSYS_ID_LEN];
+ u8 revision[4];
+ u8 slot_id[SLOT_ID_LEN];
+ u8 reserved[2];
+};
+
+struct rdac_controller {
+ u8 subsys_id[SUBSYS_ID_LEN];
+ u8 slot_id[SLOT_ID_LEN];
+ int use_ms10;
+ struct kref kref;
+ struct list_head node; /* list of all controllers */
+ union {
+ struct rdac_pg_legacy legacy;
+ struct rdac_pg_expanded expanded;
+ } mode_select;
+};
+struct c8_inquiry {
+ u8 peripheral_info;
+ u8 page_code; /* 0xC8 */
+ u8 reserved1;
+ u8 page_len;
+ u8 page_id[4]; /* "edid" */
+ u8 reserved2[3];
+ u8 vol_uniq_id_len;
+ u8 vol_uniq_id[16];
+ u8 vol_user_label_len;
+ u8 vol_user_label[60];
+ u8 array_uniq_id_len;
+ u8 array_unique_id[16];
+ u8 array_user_label_len;
+ u8 array_user_label[60];
+ u8 lun[8];
+};
+
+struct c2_inquiry {
+ u8 peripheral_info;
+ u8 page_code; /* 0xC2 */
+ u8 reserved1;
+ u8 page_len;
+ u8 page_id[4]; /* "swr4" */
+ u8 sw_version[3];
+ u8 sw_date[3];
+ u8 features_enabled;
+ u8 max_lun_supported;
+ u8 partitions[239]; /* Total allocation length should be 0xFF */
+};
+
+struct rdac_dh_data {
+ struct rdac_controller *ctlr;
+#define UNINITIALIZED_LUN (1 << 8)
+ unsigned lun;
+#define RDAC_STATE_ACTIVE 0
+#define RDAC_STATE_PASSIVE 1
+ unsigned char state;
+ unsigned char sense[SCSI_SENSE_BUFFERSIZE];
+ union {
+ struct c2_inquiry c2;
+ struct c4_inquiry c4;
+ struct c8_inquiry c8;
+ struct c9_inquiry c9;
+ } inq;
+};
+
+static LIST_HEAD(ctlr_list);
+static DEFINE_SPINLOCK(list_lock);
+
+static inline struct rdac_dh_data *get_rdac_data(struct scsi_device *sdev)
+{
+ struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
+ BUG_ON(scsi_dh_data == NULL);
+ return ((struct rdac_dh_data *) scsi_dh_data->buf);
+}
+
+static struct request *get_rdac_req(struct scsi_device *sdev,
+ void *buffer, unsigned buflen, int rw)
+{
+ struct request *rq;
+ struct request_queue *q = sdev->request_queue;
+ struct rdac_dh_data *h = get_rdac_data(sdev);
+
+ rq = blk_get_request(q, rw, GFP_KERNEL);
+
+ if (!rq) {
+ sdev_printk(KERN_INFO, sdev,
+ "get_rdac_req: blk_get_request failed.\n");
+ return NULL;
+ }
+
+ if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_KERNEL)) {
+ blk_put_request(rq);
+ sdev_printk(KERN_INFO, sdev,
+ "get_rdac_req: blk_rq_map_kern failed.\n");
+ return NULL;
+ }
+
+ memset(&rq->cmd, 0, BLK_MAX_CDB);
+ rq->sense = h->sense;
+ memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+ rq->sense_len = 0;
+
+ rq->cmd_type = REQ_TYPE_BLOCK_PC;
+ rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
+ rq->retries = RDAC_RETRIES;
+ rq->timeout = RDAC_TIMEOUT;
+
+ return rq;
+}
+
+static struct request *rdac_failover_get(struct scsi_device *sdev)
+{
+ struct request *rq;
+ struct rdac_mode_common *common;
+ unsigned data_size;
+ struct rdac_dh_data *h = get_rdac_data(sdev);
+
+ if (h->ctlr->use_ms10) {
+ struct rdac_pg_expanded *rdac_pg;
+
+ data_size = sizeof(struct rdac_pg_expanded);
+ rdac_pg = &h->ctlr->mode_select.expanded;
+ memset(rdac_pg, 0, data_size);
+ common = &rdac_pg->common;
+ rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER + 0x40;
+ rdac_pg->subpage_code = 0x1;
+ rdac_pg->page_len[0] = 0x01;
+ rdac_pg->page_len[1] = 0x28;
+ rdac_pg->lun_table[h->lun] = 0x81;
+ } else {
+ struct rdac_pg_legacy *rdac_pg;
+
+ data_size = sizeof(struct rdac_pg_legacy);
+ rdac_pg = &h->ctlr->mode_select.legacy;
+ memset(rdac_pg, 0, data_size);
+ common = &rdac_pg->common;
+ rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER;
+ rdac_pg->page_len = 0x68;
+ rdac_pg->lun_table[h->lun] = 0x81;
+ }
+ common->rdac_mode[1] = RDAC_MODE_TRANSFER_SPECIFIED_LUNS;
+ common->quiescence_timeout = RDAC_QUIESCENCE_TIME;
+ common->rdac_options = RDAC_FORCED_QUIESENCE;
+
+ /* get request for block layer packet command */
+ rq = get_rdac_req(sdev, &h->ctlr->mode_select, data_size, WRITE);
+ if (!rq)
+ return NULL;
+
+ /* Prepare the command. */
+ if (h->ctlr->use_ms10) {
+ rq->cmd[0] = MODE_SELECT_10;
+ rq->cmd[7] = data_size >> 8;
+ rq->cmd[8] = data_size & 0xff;
+ } else {
+ rq->cmd[0] = MODE_SELECT;
+ rq->cmd[4] = data_size;
+ }
+ rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
+
+ return rq;
+}
+
+static void release_controller(struct kref *kref)
+{
+ struct rdac_controller *ctlr;
+ ctlr = container_of(kref, struct rdac_controller, kref);
+
+ spin_lock(&list_lock);
+ list_del(&ctlr->node);
+ spin_unlock(&list_lock);
+ kfree(ctlr);
+}
+
+static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id)
+{
+ struct rdac_controller *ctlr, *tmp;
+
+ spin_lock(&list_lock);
+
+ list_for_each_entry(tmp, &ctlr_list, node) {
+ if ((memcmp(tmp->subsys_id, subsys_id, SUBSYS_ID_LEN) == 0) &&
+ (memcmp(tmp->slot_id, slot_id, SLOT_ID_LEN) == 0)) {
+ kref_get(&tmp->kref);
+ spin_unlock(&list_lock);
+ return tmp;
+ }
+ }
+ ctlr = kmalloc(sizeof(*ctlr), GFP_ATOMIC);
+ if (!ctlr)
+ goto done;
+
+ /* initialize fields of controller */
+ memcpy(ctlr->subsys_id, subsys_id, SUBSYS_ID_LEN);
+ memcpy(ctlr->slot_id, slot_id, SLOT_ID_LEN);
+ kref_init(&ctlr->kref);
+ ctlr->use_ms10 = -1;
+ list_add(&ctlr->node, &ctlr_list);
+done:
+ spin_unlock(&list_lock);
+ return ctlr;
+}
+
+static int submit_inquiry(struct scsi_device *sdev, int page_code,
+ unsigned int len)
+{
+ struct request *rq;
+ struct request_queue *q = sdev->request_queue;
+ struct rdac_dh_data *h = get_rdac_data(sdev);
+ int err = SCSI_DH_RES_TEMP_UNAVAIL;
+
+ rq = get_rdac_req(sdev, &h->inq, len, READ);
+ if (!rq)
+ goto done;
+
+ /* Prepare the command. */
+ rq->cmd[0] = INQUIRY;
+ rq->cmd[1] = 1;
+ rq->cmd[2] = page_code;
+ rq->cmd[4] = len;
+ rq->cmd_len = COMMAND_SIZE(INQUIRY);
+ err = blk_execute_rq(q, NULL, rq, 1);
+ if (err == -EIO)
+ err = SCSI_DH_IO;
+done:
+ return err;
+}
+
+static int get_lun(struct scsi_device *sdev)
+{
+ int err;
+ struct c8_inquiry *inqp;
+ struct rdac_dh_data *h = get_rdac_data(sdev);
+
+ err = submit_inquiry(sdev, 0xC8, sizeof(struct c8_inquiry));
+ if (err == SCSI_DH_OK) {
+ inqp = &h->inq.c8;
+ h->lun = inqp->lun[7]; /* currently it uses only one byte */
+ }
+ return err;
+}
+
+#define RDAC_OWNED 0
+#define RDAC_UNOWNED 1
+#define RDAC_FAILED 2
+static int check_ownership(struct scsi_device *sdev)
+{
+ int err;
+ struct c9_inquiry *inqp;
+ struct rdac_dh_data *h = get_rdac_data(sdev);
+
+ err = submit_inquiry(sdev, 0xC9, sizeof(struct c9_inquiry));
+ if (err == SCSI_DH_OK) {
+ err = RDAC_UNOWNED;
+ inqp = &h->inq.c9;
+ /*
+ * If in AVT mode or if the path already owns the LUN,
+ * return RDAC_OWNED;
+ */
+ if (((inqp->avte_cvp >> 7) == 0x1) ||
+ ((inqp->avte_cvp & 0x1) != 0))
+ err = RDAC_OWNED;
+ } else
+ err = RDAC_FAILED;
+ return err;
+}
+
+static int initialize_controller(struct scsi_device *sdev)
+{
+ int err;
+ struct c4_inquiry *inqp;
+ struct rdac_dh_data *h = get_rdac_data(sdev);
+
+ err = submit_inquiry(sdev, 0xC4, sizeof(struct c4_inquiry));
+ if (err == SCSI_DH_OK) {
+ inqp = &h->inq.c4;
+ h->ctlr = get_controller(inqp->subsys_id, inqp->slot_id);
+ if (!h->ctlr)
+ err = SCSI_DH_RES_TEMP_UNAVAIL;
+ }
+ return err;
+}
+
+static int set_mode_select(struct scsi_device *sdev)
+{
+ int err;
+ struct c2_inquiry *inqp;
+ struct rdac_dh_data *h = get_rdac_data(sdev);
+
+ err = submit_inquiry(sdev, 0xC2, sizeof(struct c2_inquiry));
+ if (err == SCSI_DH_OK) {
+ inqp = &h->inq.c2;
+ /*
+ * If more than MODE6_MAX_LUN luns are supported, use
+ * mode select 10
+ */
+ if (inqp->max_lun_supported >= MODE6_MAX_LUN)
+ h->ctlr->use_ms10 = 1;
+ else
+ h->ctlr->use_ms10 = 0;
+ }
+ return err;
+}
+
+static int mode_select_handle_sense(struct scsi_device *sdev)
+{
+ struct scsi_sense_hdr sense_hdr;
+ struct rdac_dh_data *h = get_rdac_data(sdev);
+ int sense, err = SCSI_DH_IO, ret;
+
+ ret = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sense_hdr);
+ if (!ret)
+ goto done;
+
+ err = SCSI_DH_OK;
+ sense = (sense_hdr.sense_key << 16) | (sense_hdr.asc << 8) |
+ sense_hdr.ascq;
+ /* If it is retryable failure, submit the c9 inquiry again */
+ if (sense == 0x59136 || sense == 0x68b02 || sense == 0xb8b02 ||
+ sense == 0x62900) {
+ /* 0x59136 - Command lock contention
+ * 0x[6b]8b02 - Quiesense in progress or achieved
+ * 0x62900 - Power On, Reset, or Bus Device Reset
+ */
+ err = SCSI_DH_RETRY;
+ }
+
+ if (sense)
+ sdev_printk(KERN_INFO, sdev,
+ "MODE_SELECT failed with sense 0x%x.\n", sense);
+done:
+ return err;
+}
+
+static int send_mode_select(struct scsi_device *sdev)
+{
+ struct request *rq;
+ struct request_queue *q = sdev->request_queue;
+ struct rdac_dh_data *h = get_rdac_data(sdev);
+ int err = SCSI_DH_RES_TEMP_UNAVAIL;
+
+ rq = rdac_failover_get(sdev);
+ if (!rq)
+ goto done;
+
+ sdev_printk(KERN_INFO, sdev, "queueing MODE_SELECT command.\n");
+
+ err = blk_execute_rq(q, NULL, rq, 1);
+ if (err != SCSI_DH_OK)
+ err = mode_select_handle_sense(sdev);
+ if (err == SCSI_DH_OK)
+ h->state = RDAC_STATE_ACTIVE;
+done:
+ return err;
+}
+
+static int rdac_activate(struct scsi_device *sdev)
+{
+ struct rdac_dh_data *h = get_rdac_data(sdev);
+ int err = SCSI_DH_OK;
+
+ if (h->lun == UNINITIALIZED_LUN) {
+ err = get_lun(sdev);
+ if (err != SCSI_DH_OK)
+ goto done;
+ }
+
+ err = check_ownership(sdev);
+ switch (err) {
+ case RDAC_UNOWNED:
+ break;
+ case RDAC_OWNED:
+ err = SCSI_DH_OK;
+ goto done;
+ case RDAC_FAILED:
+ default:
+ err = SCSI_DH_IO;
+ goto done;
+ }
+
+ if (!h->ctlr) {
+ err = initialize_controller(sdev);
+ if (err != SCSI_DH_OK)
+ goto done;
+ }
+
+ if (h->ctlr->use_ms10 == -1) {
+ err = set_mode_select(sdev);
+ if (err != SCSI_DH_OK)
+ goto done;
+ }
+
+ err = send_mode_select(sdev);
+done:
+ return err;
+}
+
+static int rdac_prep_fn(struct scsi_device *sdev, struct request *req)
+{
+ struct rdac_dh_data *h = get_rdac_data(sdev);
+ int ret = BLKPREP_OK;
+
+ if (h->state != RDAC_STATE_ACTIVE) {
+ ret = BLKPREP_KILL;
+ req->cmd_flags |= REQ_QUIET;
+ }
+ return ret;
+
+}
+
+static int rdac_check_sense(struct scsi_device *sdev,
+ struct scsi_sense_hdr *sense_hdr)
+{
+ struct rdac_dh_data *h = get_rdac_data(sdev);
+ switch (sense_hdr->sense_key) {
+ case NOT_READY:
+ if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x81)
+ /* LUN Not Ready - Storage firmware incompatible
+ * Manual code synchonisation required.
+ *
+ * Nothing we can do here. Try to bypass the path.
+ */
+ return SUCCESS;
+ if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0xA1)
+ /* LUN Not Ready - Quiescense in progress
+ *
+ * Just retry and wait.
+ */
+ return NEEDS_RETRY;
+ break;
+ case ILLEGAL_REQUEST:
+ if (sense_hdr->asc == 0x94 && sense_hdr->ascq == 0x01) {
+ /* Invalid Request - Current Logical Unit Ownership.
+ * Controller is not the current owner of the LUN,
+ * Fail the path, so that the other path be used.
+ */
+ h->state = RDAC_STATE_PASSIVE;
+ return SUCCESS;
+ }
+ break;
+ case UNIT_ATTENTION:
+ if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00)
+ /*
+ * Power On, Reset, or Bus Device Reset, just retry.
+ */
+ return NEEDS_RETRY;
+ break;
+ }
+ /* success just means we do not care what scsi-ml does */
+ return SCSI_RETURN_NOT_HANDLED;
+}
+
+static const struct {
+ char *vendor;
+ char *model;
+} rdac_dev_list[] = {
+ {"IBM", "1722"},
+ {"IBM", "1724"},
+ {"IBM", "1726"},
+ {"IBM", "1742"},
+ {"IBM", "1814"},
+ {"IBM", "1815"},
+ {"IBM", "1818"},
+ {"IBM", "3526"},
+ {"SGI", "TP9400"},
+ {"SGI", "TP9500"},
+ {"SGI", "IS"},
+ {"STK", "OPENstorage D280"},
+ {"SUN", "CSM200_R"},
+ {"SUN", "LCSM100_F"},
+ {NULL, NULL},
+};
+
+static int rdac_bus_notify(struct notifier_block *, unsigned long, void *);
+
+static struct scsi_device_handler rdac_dh = {
+ .name = RDAC_NAME,
+ .module = THIS_MODULE,
+ .nb.notifier_call = rdac_bus_notify,
+ .prep_fn = rdac_prep_fn,
+ .check_sense = rdac_check_sense,
+ .activate = rdac_activate,
+};
+
+/*
+ * TODO: need some interface so we can set trespass values
+ */
+static int rdac_bus_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct device *dev = data;
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct scsi_dh_data *scsi_dh_data;
+ struct rdac_dh_data *h;
+ int i, found = 0;
+ unsigned long flags;
+
+ if (action == BUS_NOTIFY_ADD_DEVICE) {
+ for (i = 0; rdac_dev_list[i].vendor; i++) {
+ if (!strncmp(sdev->vendor, rdac_dev_list[i].vendor,
+ strlen(rdac_dev_list[i].vendor)) &&
+ !strncmp(sdev->model, rdac_dev_list[i].model,
+ strlen(rdac_dev_list[i].model))) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ goto out;
+
+ scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+ + sizeof(*h) , GFP_KERNEL);
+ if (!scsi_dh_data) {
+ sdev_printk(KERN_ERR, sdev, "Attach failed %s.\n",
+ RDAC_NAME);
+ goto out;
+ }
+
+ scsi_dh_data->scsi_dh = &rdac_dh;
+ h = (struct rdac_dh_data *) scsi_dh_data->buf;
+ h->lun = UNINITIALIZED_LUN;
+ h->state = RDAC_STATE_ACTIVE;
+ spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+ sdev->scsi_dh_data = scsi_dh_data;
+ spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+ try_module_get(THIS_MODULE);
+
+ sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", RDAC_NAME);
+
+ } else if (action == BUS_NOTIFY_DEL_DEVICE) {
+ if (sdev->scsi_dh_data == NULL ||
+ sdev->scsi_dh_data->scsi_dh != &rdac_dh)
+ goto out;
+
+ spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+ scsi_dh_data = sdev->scsi_dh_data;
+ sdev->scsi_dh_data = NULL;
+ spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+
+ h = (struct rdac_dh_data *) scsi_dh_data->buf;
+ if (h->ctlr)
+ kref_put(&h->ctlr->kref, release_controller);
+ kfree(scsi_dh_data);
+ module_put(THIS_MODULE);
+ sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n", RDAC_NAME);
+ }
+
+out:
+ return 0;
+}
+
+static int __init rdac_init(void)
+{
+ int r;
+
+ r = scsi_register_device_handler(&rdac_dh);
+ if (r != 0)
+ printk(KERN_ERR "Failed to register scsi device handler.");
+ return r;
+}
+
+static void __exit rdac_exit(void)
+{
+ scsi_unregister_device_handler(&rdac_dh);
+}
+
+module_init(rdac_init);
+module_exit(rdac_exit);
+
+MODULE_DESCRIPTION("Multipath LSI/Engenio RDAC driver");
+MODULE_AUTHOR("Mike Christie, Chandra Seetharaman");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c
index 8508816f303d..d1944d5449f4 100644
--- a/drivers/scsi/dpt_i2o.c
+++ b/drivers/scsi/dpt_i2o.c
@@ -270,8 +270,8 @@ rebuild_sys_tab:
pHba->initialized = TRUE;
pHba->state &= ~DPTI_STATE_RESET;
if (adpt_sysfs_class) {
- struct device *dev = device_create(adpt_sysfs_class,
- NULL, MKDEV(DPTI_I2O_MAJOR, pHba->unit),
+ struct device *dev = device_create_drvdata(adpt_sysfs_class,
+ NULL, MKDEV(DPTI_I2O_MAJOR, pHba->unit), NULL,
"dpti%d", pHba->unit);
if (IS_ERR(dev)) {
printk(KERN_WARNING"dpti%d: unable to "
diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index 3690360d7a79..9f2f037017a5 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -232,8 +232,8 @@ int scsi_add_host(struct Scsi_Host *shost, struct device *dev)
}
if (shost->transportt->create_work_queue) {
- snprintf(shost->work_q_name, KOBJ_NAME_LEN, "scsi_wq_%d",
- shost->host_no);
+ snprintf(shost->work_q_name, sizeof(shost->work_q_name),
+ "scsi_wq_%d", shost->host_no);
shost->work_q = create_singlethread_workqueue(
shost->work_q_name);
if (!shost->work_q) {
@@ -462,7 +462,8 @@ struct Scsi_Host *scsi_host_lookup(unsigned short hostnum)
struct device *cdev;
struct Scsi_Host *shost = ERR_PTR(-ENXIO);
- cdev = class_find_device(&shost_class, &hostnum, __scsi_host_match);
+ cdev = class_find_device(&shost_class, NULL, &hostnum,
+ __scsi_host_match);
if (cdev)
shost = scsi_host_get(class_to_shost(cdev));
diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c
index 44d8d5163a1a..683bce375c74 100644
--- a/drivers/scsi/ide-scsi.c
+++ b/drivers/scsi/ide-scsi.c
@@ -60,6 +60,13 @@
#define IDESCSI_DEBUG_LOG 0
+#if IDESCSI_DEBUG_LOG
+#define debug_log(fmt, args...) \
+ printk(KERN_INFO "ide-scsi: " fmt, ## args)
+#else
+#define debug_log(fmt, args...) do {} while (0)
+#endif
+
/*
* SCSI command transformation layer
*/
@@ -129,14 +136,15 @@ static inline idescsi_scsi_t *drive_to_idescsi(ide_drive_t *ide_drive)
#define IDESCSI_PC_RQ 90
/*
- * PIO data transfer routines using the scatter gather table.
+ * PIO data transfer routine using the scatter gather table.
*/
-static void idescsi_input_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc,
- unsigned int bcount)
+static void ide_scsi_io_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc,
+ unsigned int bcount, int write)
{
ide_hwif_t *hwif = drive->hwif;
- int count;
+ xfer_func_t *xf = write ? hwif->output_data : hwif->input_data;
char *buf;
+ int count;
while (bcount) {
count = min(pc->sg->length - pc->b_count, bcount);
@@ -145,13 +153,13 @@ static void idescsi_input_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc,
local_irq_save(flags);
buf = kmap_atomic(sg_page(pc->sg), KM_IRQ0) +
- pc->sg->offset;
- hwif->input_data(drive, NULL, buf + pc->b_count, count);
+ pc->sg->offset;
+ xf(drive, NULL, buf + pc->b_count, count);
kunmap_atomic(buf - pc->sg->offset, KM_IRQ0);
local_irq_restore(flags);
} else {
buf = sg_virt(pc->sg);
- hwif->input_data(drive, NULL, buf + pc->b_count, count);
+ xf(drive, NULL, buf + pc->b_count, count);
}
bcount -= count; pc->b_count += count;
if (pc->b_count == pc->sg->length) {
@@ -163,51 +171,34 @@ static void idescsi_input_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc,
}
if (bcount) {
- printk (KERN_ERR "ide-scsi: scatter gather table too small, discarding data\n");
- ide_pad_transfer(drive, 0, bcount);
+ printk(KERN_ERR "%s: scatter gather table too small, %s\n",
+ drive->name, write ? "padding with zeros"
+ : "discarding data");
+ ide_pad_transfer(drive, write, bcount);
}
}
-static void idescsi_output_buffers(ide_drive_t *drive, struct ide_atapi_pc *pc,
- unsigned int bcount)
+static void ide_scsi_hex_dump(u8 *data, int len)
{
- ide_hwif_t *hwif = drive->hwif;
- int count;
- char *buf;
+ print_hex_dump(KERN_CONT, "", DUMP_PREFIX_NONE, 16, 1, data, len, 0);
+}
- while (bcount) {
- count = min(pc->sg->length - pc->b_count, bcount);
- if (PageHighMem(sg_page(pc->sg))) {
- unsigned long flags;
+static int idescsi_end_request(ide_drive_t *, int, int);
- local_irq_save(flags);
- buf = kmap_atomic(sg_page(pc->sg), KM_IRQ0) +
- pc->sg->offset;
- hwif->output_data(drive, NULL, buf + pc->b_count, count);
- kunmap_atomic(buf - pc->sg->offset, KM_IRQ0);
- local_irq_restore(flags);
- } else {
- buf = sg_virt(pc->sg);
- hwif->output_data(drive, NULL, buf + pc->b_count, count);
- }
- bcount -= count; pc->b_count += count;
- if (pc->b_count == pc->sg->length) {
- if (!--pc->sg_cnt)
- break;
- pc->sg = sg_next(pc->sg);
- pc->b_count = 0;
- }
- }
+static void ide_scsi_callback(ide_drive_t *drive)
+{
+ idescsi_scsi_t *scsi = drive_to_idescsi(drive);
+ struct ide_atapi_pc *pc = scsi->pc;
- if (bcount) {
- printk (KERN_ERR "ide-scsi: scatter gather table too small, padding with zeros\n");
- ide_pad_transfer(drive, 1, bcount);
- }
-}
+ if (pc->flags & PC_FLAG_TIMEDOUT)
+ debug_log("%s: got timed out packet %lu at %lu\n", __func__,
+ pc->scsi_cmd->serial_number, jiffies);
+ /* end this request now - scsi should retry it*/
+ else if (test_bit(IDESCSI_LOG_CMD, &scsi->log))
+ printk(KERN_INFO "Packet command completed, %d bytes"
+ " transferred\n", pc->xferred);
-static void ide_scsi_hex_dump(u8 *data, int len)
-{
- print_hex_dump(KERN_CONT, "", DUMP_PREFIX_NONE, 16, 1, data, len, 0);
+ idescsi_end_request(drive, 1, 0);
}
static int idescsi_check_condition(ide_drive_t *drive,
@@ -228,14 +219,16 @@ static int idescsi_check_condition(ide_drive_t *drive,
kfree(pc);
return -ENOMEM;
}
- ide_init_drive_cmd(rq);
+ blk_rq_init(NULL, rq);
rq->special = (char *) pc;
pc->rq = rq;
pc->buf = buf;
pc->c[0] = REQUEST_SENSE;
pc->c[4] = pc->req_xfer = pc->buf_size = SCSI_SENSE_BUFFERSIZE;
rq->cmd_type = REQ_TYPE_SENSE;
+ rq->cmd_flags |= REQ_PREEMPT;
pc->timeout = jiffies + WAIT_READY;
+ pc->callback = ide_scsi_callback;
/* NOTE! Save the failed packet command in "rq->buffer" */
rq->buffer = (void *) failed_cmd->special;
pc->scsi_cmd = ((struct ide_atapi_pc *) failed_cmd->special)->scsi_cmd;
@@ -244,11 +237,10 @@ static int idescsi_check_condition(ide_drive_t *drive,
ide_scsi_hex_dump(pc->c, 6);
}
rq->rq_disk = scsi->disk;
- return ide_do_drive_cmd(drive, rq, ide_preempt);
+ ide_do_drive_cmd(drive, rq);
+ return 0;
}
-static int idescsi_end_request(ide_drive_t *, int, int);
-
static ide_startstop_t
idescsi_atapi_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err)
{
@@ -256,7 +248,7 @@ idescsi_atapi_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err)
if (ide_read_status(drive) & (BUSY_STAT | DRQ_STAT))
/* force an abort */
- hwif->OUTBSYNC(drive, WIN_IDLEIMMEDIATE,
+ hwif->OUTBSYNC(hwif, WIN_IDLEIMMEDIATE,
hwif->io_ports.command_addr);
rq->errors++;
@@ -269,10 +261,9 @@ idescsi_atapi_error(ide_drive_t *drive, struct request *rq, u8 stat, u8 err)
static ide_startstop_t
idescsi_atapi_abort(ide_drive_t *drive, struct request *rq)
{
-#if IDESCSI_DEBUG_LOG
- printk(KERN_WARNING "idescsi_atapi_abort called for %lu\n",
+ debug_log("%s called for %lu\n", __func__,
((struct ide_atapi_pc *) rq->special)->scsi_cmd->serial_number);
-#endif
+
rq->errors |= ERROR_MAX;
idescsi_end_request(drive, 0, 0);
@@ -351,9 +342,9 @@ static int idescsi_expiry(ide_drive_t *drive)
idescsi_scsi_t *scsi = drive_to_idescsi(drive);
struct ide_atapi_pc *pc = scsi->pc;
-#if IDESCSI_DEBUG_LOG
- printk(KERN_WARNING "idescsi_expiry called for %lu at %lu\n", pc->scsi_cmd->serial_number, jiffies);
-#endif
+ debug_log("%s called for %lu at %lu\n", __func__,
+ pc->scsi_cmd->serial_number, jiffies);
+
pc->flags |= PC_FLAG_TIMEDOUT;
return 0; /* we do not want the ide subsystem to retry */
@@ -365,141 +356,19 @@ static int idescsi_expiry(ide_drive_t *drive)
static ide_startstop_t idescsi_pc_intr (ide_drive_t *drive)
{
idescsi_scsi_t *scsi = drive_to_idescsi(drive);
- ide_hwif_t *hwif = drive->hwif;
struct ide_atapi_pc *pc = scsi->pc;
- struct request *rq = pc->rq;
- unsigned int temp;
- u16 bcount;
- u8 stat, ireason;
-
-#if IDESCSI_DEBUG_LOG
- printk (KERN_INFO "ide-scsi: Reached idescsi_pc_intr interrupt handler\n");
-#endif /* IDESCSI_DEBUG_LOG */
-
- if (pc->flags & PC_FLAG_TIMEDOUT) {
-#if IDESCSI_DEBUG_LOG
- printk(KERN_WARNING "idescsi_pc_intr: got timed out packet %lu at %lu\n",
- pc->scsi_cmd->serial_number, jiffies);
-#endif
- /* end this request now - scsi should retry it*/
- idescsi_end_request (drive, 1, 0);
- return ide_stopped;
- }
- if (pc->flags & PC_FLAG_DMA_IN_PROGRESS) {
- pc->flags &= ~PC_FLAG_DMA_IN_PROGRESS;
-#if IDESCSI_DEBUG_LOG
- printk ("ide-scsi: %s: DMA complete\n", drive->name);
-#endif /* IDESCSI_DEBUG_LOG */
- pc->xferred = pc->req_xfer;
- (void)hwif->dma_ops->dma_end(drive);
- }
-
- /* Clear the interrupt */
- stat = ide_read_status(drive);
-
- if ((stat & DRQ_STAT) == 0) {
- /* No more interrupts */
- if (test_bit(IDESCSI_LOG_CMD, &scsi->log))
- printk(KERN_INFO "Packet command completed, %d bytes"
- " transferred\n", pc->xferred);
- local_irq_enable_in_hardirq();
- if (stat & ERR_STAT)
- rq->errors++;
- idescsi_end_request (drive, 1, 0);
- return ide_stopped;
- }
- bcount = (hwif->INB(hwif->io_ports.lbah_addr) << 8) |
- hwif->INB(hwif->io_ports.lbam_addr);
- ireason = hwif->INB(hwif->io_ports.nsect_addr);
-
- if (ireason & CD) {
- printk(KERN_ERR "ide-scsi: CoD != 0 in idescsi_pc_intr\n");
- return ide_do_reset (drive);
- }
- if (ireason & IO) {
- temp = pc->xferred + bcount;
- if (temp > pc->req_xfer) {
- if (temp > pc->buf_size) {
- printk(KERN_ERR "ide-scsi: The scsi wants to "
- "send us more data than expected "
- "- discarding data\n");
- temp = pc->buf_size - pc->xferred;
- if (temp) {
- pc->flags &= ~PC_FLAG_WRITING;
- if (pc->sg)
- idescsi_input_buffers(drive, pc,
- temp);
- else
- hwif->input_data(drive, NULL,
- pc->cur_pos, temp);
- printk(KERN_ERR "ide-scsi: transferred"
- " %d of %d bytes\n",
- temp, bcount);
- }
- pc->xferred += temp;
- pc->cur_pos += temp;
- ide_pad_transfer(drive, 0, bcount - temp);
- ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc), idescsi_expiry);
- return ide_started;
- }
-#if IDESCSI_DEBUG_LOG
- printk (KERN_NOTICE "ide-scsi: The scsi wants to send us more data than expected - allowing transfer\n");
-#endif /* IDESCSI_DEBUG_LOG */
- }
- }
- if (ireason & IO) {
- pc->flags &= ~PC_FLAG_WRITING;
- if (pc->sg)
- idescsi_input_buffers(drive, pc, bcount);
- else
- hwif->input_data(drive, NULL, pc->cur_pos, bcount);
- } else {
- pc->flags |= PC_FLAG_WRITING;
- if (pc->sg)
- idescsi_output_buffers(drive, pc, bcount);
- else
- hwif->output_data(drive, NULL, pc->cur_pos, bcount);
- }
- /* Update the current position */
- pc->xferred += bcount;
- pc->cur_pos += bcount;
- /* And set the interrupt handler again */
- ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc), idescsi_expiry);
- return ide_started;
+ return ide_pc_intr(drive, pc, idescsi_pc_intr, get_timeout(pc),
+ idescsi_expiry, NULL, NULL, NULL,
+ ide_scsi_io_buffers);
}
static ide_startstop_t idescsi_transfer_pc(ide_drive_t *drive)
{
- ide_hwif_t *hwif = drive->hwif;
idescsi_scsi_t *scsi = drive_to_idescsi(drive);
- struct ide_atapi_pc *pc = scsi->pc;
- ide_startstop_t startstop;
- u8 ireason;
- if (ide_wait_stat(&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
- printk(KERN_ERR "ide-scsi: Strange, packet command "
- "initiated yet DRQ isn't asserted\n");
- return startstop;
- }
- ireason = hwif->INB(hwif->io_ports.nsect_addr);
- if ((ireason & CD) == 0 || (ireason & IO)) {
- printk(KERN_ERR "ide-scsi: (IO,CoD) != (0,1) while "
- "issuing a packet command\n");
- return ide_do_reset (drive);
- }
- BUG_ON(HWGROUP(drive)->handler != NULL);
- /* Set the interrupt routine */
- ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc), idescsi_expiry);
-
- /* Send the actual packet */
- hwif->output_data(drive, NULL, scsi->pc->c, 12);
-
- if (pc->flags & PC_FLAG_DMA_OK) {
- pc->flags |= PC_FLAG_DMA_IN_PROGRESS;
- hwif->dma_ops->dma_start(drive);
- }
- return ide_started;
+ return ide_transfer_pc(drive, scsi->pc, idescsi_pc_intr,
+ get_timeout(scsi->pc), idescsi_expiry);
}
static inline int idescsi_set_direction(struct ide_atapi_pc *pc)
@@ -545,38 +414,12 @@ static ide_startstop_t idescsi_issue_pc(ide_drive_t *drive,
struct ide_atapi_pc *pc)
{
idescsi_scsi_t *scsi = drive_to_idescsi(drive);
- ide_hwif_t *hwif = drive->hwif;
- u16 bcount;
- u8 dma = 0;
/* Set the current packet command */
scsi->pc = pc;
- /* We haven't transferred any data yet */
- pc->xferred = 0;
- pc->cur_pos = pc->buf;
- /* Request to transfer the entire buffer at once */
- bcount = min(pc->req_xfer, 63 * 1024);
-
- if (drive->using_dma && !idescsi_map_sg(drive, pc)) {
- hwif->sg_mapped = 1;
- dma = !hwif->dma_ops->dma_setup(drive);
- hwif->sg_mapped = 0;
- }
-
- ide_pktcmd_tf_load(drive, IDE_TFLAG_NO_SELECT_MASK, bcount, dma);
- if (dma)
- pc->flags |= PC_FLAG_DMA_OK;
-
- if (test_bit(IDESCSI_DRQ_INTERRUPT, &scsi->flags)) {
- ide_execute_command(drive, WIN_PACKETCMD, &idescsi_transfer_pc,
- get_timeout(pc), idescsi_expiry);
- return ide_started;
- } else {
- /* Issue the packet command */
- ide_execute_pkt_cmd(drive);
- return idescsi_transfer_pc(drive);
- }
+ return ide_issue_pc(drive, pc, idescsi_transfer_pc,
+ get_timeout(pc), idescsi_expiry);
}
/*
@@ -584,14 +427,22 @@ static ide_startstop_t idescsi_issue_pc(ide_drive_t *drive,
*/
static ide_startstop_t idescsi_do_request (ide_drive_t *drive, struct request *rq, sector_t block)
{
-#if IDESCSI_DEBUG_LOG
- printk (KERN_INFO "dev: %s, cmd: %x, errors: %d\n", rq->rq_disk->disk_name,rq->cmd[0],rq->errors);
- printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %d\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
-#endif /* IDESCSI_DEBUG_LOG */
+ debug_log("dev: %s, cmd: %x, errors: %d\n", rq->rq_disk->disk_name,
+ rq->cmd[0], rq->errors);
+ debug_log("sector: %ld, nr_sectors: %ld, current_nr_sectors: %d\n",
+ rq->sector, rq->nr_sectors, rq->current_nr_sectors);
if (blk_sense_request(rq) || blk_special_request(rq)) {
- return idescsi_issue_pc(drive,
- (struct ide_atapi_pc *) rq->special);
+ struct ide_atapi_pc *pc = (struct ide_atapi_pc *)rq->special;
+ idescsi_scsi_t *scsi = drive_to_idescsi(drive);
+
+ if (test_bit(IDESCSI_DRQ_INTERRUPT, &scsi->flags))
+ pc->flags |= PC_FLAG_DRQ_INTERRUPT;
+
+ if (drive->using_dma && !idescsi_map_sg(drive, pc))
+ pc->flags |= PC_FLAG_DMA_OK;
+
+ return idescsi_issue_pc(drive, pc);
}
blk_dump_rq_flags(rq, "ide-scsi: unsup command");
idescsi_end_request (drive, 0, 0);
@@ -646,6 +497,8 @@ static void ide_scsi_remove(ide_drive_t *drive)
put_disk(g);
ide_scsi_put(scsi);
+
+ drive->scsi = 0;
}
static int ide_scsi_probe(ide_drive_t *);
@@ -765,6 +618,8 @@ static int idescsi_queue (struct scsi_cmnd *cmd,
memset (pc->c, 0, 12);
pc->flags = 0;
+ if (cmd->sc_data_direction == DMA_TO_DEVICE)
+ pc->flags |= PC_FLAG_WRITING;
pc->rq = rq;
memcpy (pc->c, cmd->cmnd, cmd->cmd_len);
pc->buf = NULL;
@@ -775,6 +630,7 @@ static int idescsi_queue (struct scsi_cmnd *cmd,
pc->scsi_cmd = cmd;
pc->done = done;
pc->timeout = jiffies + cmd->timeout_per_command;
+ pc->callback = ide_scsi_callback;
if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) {
printk ("ide-scsi: %s: que %lu, cmd = ", drive->name, cmd->serial_number);
@@ -785,12 +641,11 @@ static int idescsi_queue (struct scsi_cmnd *cmd,
}
}
- ide_init_drive_cmd (rq);
+ blk_rq_init(NULL, rq);
rq->special = (char *) pc;
rq->cmd_type = REQ_TYPE_SPECIAL;
spin_unlock_irq(host->host_lock);
- rq->rq_disk = scsi->disk;
- (void) ide_do_drive_cmd (drive, rq, ide_end);
+ blk_execute_rq_nowait(drive->queue, scsi->disk, rq, 0, NULL);
spin_lock_irq(host->host_lock);
return 0;
abort:
@@ -985,6 +840,8 @@ static int ide_scsi_probe(ide_drive_t *drive)
!(host = scsi_host_alloc(&idescsi_template,sizeof(idescsi_scsi_t))))
return -ENODEV;
+ drive->scsi = 1;
+
g = alloc_disk(1 << PARTN_BITS);
if (!g)
goto out_host_put;
@@ -993,10 +850,10 @@ static int ide_scsi_probe(ide_drive_t *drive)
host->max_id = 1;
-#if IDESCSI_DEBUG_LOG
if (drive->id->last_lun)
- printk(KERN_NOTICE "%s: id->last_lun=%u\n", drive->name, drive->id->last_lun);
-#endif
+ debug_log("%s: id->last_lun=%u\n", drive->name,
+ drive->id->last_lun);
+
if ((drive->id->last_lun & 0x7) != 7)
host->max_lun = (drive->id->last_lun & 0x7) + 1;
else
@@ -1025,6 +882,7 @@ static int ide_scsi_probe(ide_drive_t *drive)
put_disk(g);
out_host_put:
+ drive->scsi = 0;
scsi_host_put(host);
return err;
}
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index 72b9b2a0eba3..0bd8b3dc3c19 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -64,6 +64,10 @@ MODULE_LICENSE("GPL");
#define BUG_ON(expr)
#endif
+static struct scsi_transport_template *iscsi_tcp_scsi_transport;
+static struct scsi_host_template iscsi_sht;
+static struct iscsi_transport iscsi_tcp_transport;
+
static unsigned int iscsi_max_lun = 512;
module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
@@ -494,39 +498,43 @@ iscsi_tcp_data_recv_prep(struct iscsi_tcp_conn *tcp_conn)
* must be called with session lock
*/
static void
-iscsi_tcp_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+iscsi_tcp_cleanup_task(struct iscsi_conn *conn, struct iscsi_task *task)
{
- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
struct iscsi_r2t_info *r2t;
- /* flush ctask's r2t queues */
- while (__kfifo_get(tcp_ctask->r2tqueue, (void*)&r2t, sizeof(void*))) {
- __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
+ /* nothing to do for mgmt tasks */
+ if (!task->sc)
+ return;
+
+ /* flush task's r2t queues */
+ while (__kfifo_get(tcp_task->r2tqueue, (void*)&r2t, sizeof(void*))) {
+ __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
sizeof(void*));
- debug_scsi("iscsi_tcp_cleanup_ctask pending r2t dropped\n");
+ debug_scsi("iscsi_tcp_cleanup_task pending r2t dropped\n");
}
- r2t = tcp_ctask->r2t;
+ r2t = tcp_task->r2t;
if (r2t != NULL) {
- __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
+ __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
sizeof(void*));
- tcp_ctask->r2t = NULL;
+ tcp_task->r2t = NULL;
}
}
/**
* iscsi_data_rsp - SCSI Data-In Response processing
* @conn: iscsi connection
- * @ctask: scsi command task
+ * @task: scsi command task
**/
static int
-iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_task *task)
{
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)tcp_conn->in.hdr;
struct iscsi_session *session = conn->session;
- struct scsi_cmnd *sc = ctask->sc;
+ struct scsi_cmnd *sc = task->sc;
int datasn = be32_to_cpu(rhdr->datasn);
unsigned total_in_length = scsi_in(sc)->length;
@@ -534,18 +542,18 @@ iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
if (tcp_conn->in.datalen == 0)
return 0;
- if (tcp_ctask->exp_datasn != datasn) {
- debug_tcp("%s: ctask->exp_datasn(%d) != rhdr->datasn(%d)\n",
- __FUNCTION__, tcp_ctask->exp_datasn, datasn);
+ if (tcp_task->exp_datasn != datasn) {
+ debug_tcp("%s: task->exp_datasn(%d) != rhdr->datasn(%d)\n",
+ __func__, tcp_task->exp_datasn, datasn);
return ISCSI_ERR_DATASN;
}
- tcp_ctask->exp_datasn++;
+ tcp_task->exp_datasn++;
- tcp_ctask->data_offset = be32_to_cpu(rhdr->offset);
- if (tcp_ctask->data_offset + tcp_conn->in.datalen > total_in_length) {
+ tcp_task->data_offset = be32_to_cpu(rhdr->offset);
+ if (tcp_task->data_offset + tcp_conn->in.datalen > total_in_length) {
debug_tcp("%s: data_offset(%d) + data_len(%d) > total_length_in(%d)\n",
- __FUNCTION__, tcp_ctask->data_offset,
+ __func__, tcp_task->data_offset,
tcp_conn->in.datalen, total_in_length);
return ISCSI_ERR_DATA_OFFSET;
}
@@ -574,7 +582,7 @@ iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
/**
* iscsi_solicit_data_init - initialize first Data-Out
* @conn: iscsi connection
- * @ctask: scsi command task
+ * @task: scsi command task
* @r2t: R2T info
*
* Notes:
@@ -584,7 +592,7 @@ iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
* This function is called with connection lock taken.
**/
static void
-iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_task *task,
struct iscsi_r2t_info *r2t)
{
struct iscsi_data *hdr;
@@ -595,8 +603,8 @@ iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
hdr->datasn = cpu_to_be32(r2t->solicit_datasn);
r2t->solicit_datasn++;
hdr->opcode = ISCSI_OP_SCSI_DATA_OUT;
- memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun));
- hdr->itt = ctask->hdr->itt;
+ memcpy(hdr->lun, task->hdr->lun, sizeof(hdr->lun));
+ hdr->itt = task->hdr->itt;
hdr->exp_statsn = r2t->exp_statsn;
hdr->offset = cpu_to_be32(r2t->data_offset);
if (r2t->data_length > conn->max_xmit_dlength) {
@@ -616,14 +624,14 @@ iscsi_solicit_data_init(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
/**
* iscsi_r2t_rsp - iSCSI R2T Response processing
* @conn: iscsi connection
- * @ctask: scsi command task
+ * @task: scsi command task
**/
static int
-iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_task *task)
{
struct iscsi_r2t_info *r2t;
struct iscsi_session *session = conn->session;
- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct iscsi_r2t_rsp *rhdr = (struct iscsi_r2t_rsp *)tcp_conn->in.hdr;
int r2tsn = be32_to_cpu(rhdr->r2tsn);
@@ -636,23 +644,23 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
return ISCSI_ERR_DATALEN;
}
- if (tcp_ctask->exp_datasn != r2tsn){
- debug_tcp("%s: ctask->exp_datasn(%d) != rhdr->r2tsn(%d)\n",
- __FUNCTION__, tcp_ctask->exp_datasn, r2tsn);
+ if (tcp_task->exp_datasn != r2tsn){
+ debug_tcp("%s: task->exp_datasn(%d) != rhdr->r2tsn(%d)\n",
+ __func__, tcp_task->exp_datasn, r2tsn);
return ISCSI_ERR_R2TSN;
}
/* fill-in new R2T associated with the task */
iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
- if (!ctask->sc || session->state != ISCSI_STATE_LOGGED_IN) {
+ if (!task->sc || session->state != ISCSI_STATE_LOGGED_IN) {
iscsi_conn_printk(KERN_INFO, conn,
"dropping R2T itt %d in recovery.\n",
- ctask->itt);
+ task->itt);
return 0;
}
- rc = __kfifo_get(tcp_ctask->r2tpool.queue, (void*)&r2t, sizeof(void*));
+ rc = __kfifo_get(tcp_task->r2tpool.queue, (void*)&r2t, sizeof(void*));
BUG_ON(!rc);
r2t->exp_statsn = rhdr->statsn;
@@ -660,7 +668,7 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
if (r2t->data_length == 0) {
iscsi_conn_printk(KERN_ERR, conn,
"invalid R2T with zero data len\n");
- __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
+ __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
sizeof(void*));
return ISCSI_ERR_DATALEN;
}
@@ -671,12 +679,12 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
r2t->data_length, session->max_burst);
r2t->data_offset = be32_to_cpu(rhdr->data_offset);
- if (r2t->data_offset + r2t->data_length > scsi_out(ctask->sc)->length) {
+ if (r2t->data_offset + r2t->data_length > scsi_out(task->sc)->length) {
iscsi_conn_printk(KERN_ERR, conn,
"invalid R2T with data len %u at offset %u "
"and total length %d\n", r2t->data_length,
- r2t->data_offset, scsi_out(ctask->sc)->length);
- __kfifo_put(tcp_ctask->r2tpool.queue, (void*)&r2t,
+ r2t->data_offset, scsi_out(task->sc)->length);
+ __kfifo_put(tcp_task->r2tpool.queue, (void*)&r2t,
sizeof(void*));
return ISCSI_ERR_DATALEN;
}
@@ -684,13 +692,13 @@ iscsi_r2t_rsp(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
r2t->ttt = rhdr->ttt; /* no flip */
r2t->solicit_datasn = 0;
- iscsi_solicit_data_init(conn, ctask, r2t);
+ iscsi_solicit_data_init(conn, task, r2t);
- tcp_ctask->exp_datasn = r2tsn + 1;
- __kfifo_put(tcp_ctask->r2tqueue, (void*)&r2t, sizeof(void*));
+ tcp_task->exp_datasn = r2tsn + 1;
+ __kfifo_put(tcp_task->r2tqueue, (void*)&r2t, sizeof(void*));
conn->r2t_pdus_cnt++;
- iscsi_requeue_ctask(ctask);
+ iscsi_requeue_task(task);
return 0;
}
@@ -733,10 +741,8 @@ static int
iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
{
int rc = 0, opcode, ahslen;
- struct iscsi_session *session = conn->session;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- struct iscsi_cmd_task *ctask;
- uint32_t itt;
+ struct iscsi_task *task;
/* verify PDU length */
tcp_conn->in.datalen = ntoh24(hdr->dlength);
@@ -754,7 +760,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
opcode = hdr->opcode & ISCSI_OPCODE_MASK;
/* verify itt (itt encoding: age+cid+itt) */
- rc = iscsi_verify_itt(conn, hdr, &itt);
+ rc = iscsi_verify_itt(conn, hdr->itt);
if (rc)
return rc;
@@ -763,16 +769,21 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
switch(opcode) {
case ISCSI_OP_SCSI_DATA_IN:
- ctask = session->cmds[itt];
spin_lock(&conn->session->lock);
- rc = iscsi_data_rsp(conn, ctask);
- spin_unlock(&conn->session->lock);
- if (rc)
- return rc;
+ task = iscsi_itt_to_ctask(conn, hdr->itt);
+ if (!task)
+ rc = ISCSI_ERR_BAD_ITT;
+ else
+ rc = iscsi_data_rsp(conn, task);
+ if (rc) {
+ spin_unlock(&conn->session->lock);
+ break;
+ }
+
if (tcp_conn->in.datalen) {
- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
struct hash_desc *rx_hash = NULL;
- struct scsi_data_buffer *sdb = scsi_in(ctask->sc);
+ struct scsi_data_buffer *sdb = scsi_in(task->sc);
/*
* Setup copy of Data-In into the Scsi_Cmnd
@@ -787,17 +798,21 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
debug_tcp("iscsi_tcp_begin_data_in(%p, offset=%d, "
"datalen=%d)\n", tcp_conn,
- tcp_ctask->data_offset,
+ tcp_task->data_offset,
tcp_conn->in.datalen);
- return iscsi_segment_seek_sg(&tcp_conn->in.segment,
- sdb->table.sgl,
- sdb->table.nents,
- tcp_ctask->data_offset,
- tcp_conn->in.datalen,
- iscsi_tcp_process_data_in,
- rx_hash);
+ rc = iscsi_segment_seek_sg(&tcp_conn->in.segment,
+ sdb->table.sgl,
+ sdb->table.nents,
+ tcp_task->data_offset,
+ tcp_conn->in.datalen,
+ iscsi_tcp_process_data_in,
+ rx_hash);
+ spin_unlock(&conn->session->lock);
+ return rc;
}
- /* fall through */
+ rc = __iscsi_complete_pdu(conn, hdr, NULL, 0);
+ spin_unlock(&conn->session->lock);
+ break;
case ISCSI_OP_SCSI_CMD_RSP:
if (tcp_conn->in.datalen) {
iscsi_tcp_data_recv_prep(tcp_conn);
@@ -806,15 +821,17 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
rc = iscsi_complete_pdu(conn, hdr, NULL, 0);
break;
case ISCSI_OP_R2T:
- ctask = session->cmds[itt];
- if (ahslen)
+ spin_lock(&conn->session->lock);
+ task = iscsi_itt_to_ctask(conn, hdr->itt);
+ if (!task)
+ rc = ISCSI_ERR_BAD_ITT;
+ else if (ahslen)
rc = ISCSI_ERR_AHSLEN;
- else if (ctask->sc->sc_data_direction == DMA_TO_DEVICE) {
- spin_lock(&session->lock);
- rc = iscsi_r2t_rsp(conn, ctask);
- spin_unlock(&session->lock);
- } else
+ else if (task->sc->sc_data_direction == DMA_TO_DEVICE)
+ rc = iscsi_r2t_rsp(conn, task);
+ else
rc = ISCSI_ERR_PROTO;
+ spin_unlock(&conn->session->lock);
break;
case ISCSI_OP_LOGIN_RSP:
case ISCSI_OP_TEXT_RSP:
@@ -1176,7 +1193,7 @@ iscsi_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr, size_t hdrlen)
{
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- debug_tcp("%s(%p%s)\n", __FUNCTION__, tcp_conn,
+ debug_tcp("%s(%p%s)\n", __func__, tcp_conn,
conn->hdrdgst_en? ", digest enabled" : "");
/* Clear the data segment - needs to be filled in by the
@@ -1185,7 +1202,7 @@ iscsi_tcp_send_hdr_prep(struct iscsi_conn *conn, void *hdr, size_t hdrlen)
/* If header digest is enabled, compute the CRC and
* place the digest into the same buffer. We make
- * sure that both iscsi_tcp_ctask and mtask have
+ * sure that both iscsi_tcp_task and mtask have
* sufficient room.
*/
if (conn->hdrdgst_en) {
@@ -1217,7 +1234,7 @@ iscsi_tcp_send_data_prep(struct iscsi_conn *conn, struct scatterlist *sg,
struct hash_desc *tx_hash = NULL;
unsigned int hdr_spec_len;
- debug_tcp("%s(%p, offset=%d, datalen=%d%s)\n", __FUNCTION__,
+ debug_tcp("%s(%p, offset=%d, datalen=%d%s)\n", __func__,
tcp_conn, offset, len,
conn->datadgst_en? ", digest enabled" : "");
@@ -1242,7 +1259,7 @@ iscsi_tcp_send_linear_data_prepare(struct iscsi_conn *conn, void *data,
struct hash_desc *tx_hash = NULL;
unsigned int hdr_spec_len;
- debug_tcp("%s(%p, datalen=%d%s)\n", __FUNCTION__, tcp_conn, len,
+ debug_tcp("%s(%p, datalen=%d%s)\n", __func__, tcp_conn, len,
conn->datadgst_en? ", digest enabled" : "");
/* Make sure the datalen matches what the caller
@@ -1260,7 +1277,7 @@ iscsi_tcp_send_linear_data_prepare(struct iscsi_conn *conn, void *data,
/**
* iscsi_solicit_data_cont - initialize next Data-Out
* @conn: iscsi connection
- * @ctask: scsi command task
+ * @task: scsi command task
* @r2t: R2T info
* @left: bytes left to transfer
*
@@ -1271,7 +1288,7 @@ iscsi_tcp_send_linear_data_prepare(struct iscsi_conn *conn, void *data,
* Called under connection lock.
**/
static int
-iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_task *task,
struct iscsi_r2t_info *r2t)
{
struct iscsi_data *hdr;
@@ -1288,8 +1305,8 @@ iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
hdr->datasn = cpu_to_be32(r2t->solicit_datasn);
r2t->solicit_datasn++;
hdr->opcode = ISCSI_OP_SCSI_DATA_OUT;
- memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun));
- hdr->itt = ctask->hdr->itt;
+ memcpy(hdr->lun, task->hdr->lun, sizeof(hdr->lun));
+ hdr->itt = task->hdr->itt;
hdr->exp_statsn = r2t->exp_statsn;
new_offset = r2t->data_offset + r2t->sent;
hdr->offset = cpu_to_be32(new_offset);
@@ -1307,89 +1324,76 @@ iscsi_solicit_data_cont(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
}
/**
- * iscsi_tcp_ctask - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
+ * iscsi_tcp_task - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
* @conn: iscsi connection
- * @ctask: scsi command task
+ * @task: scsi command task
* @sc: scsi command
**/
static int
-iscsi_tcp_ctask_init(struct iscsi_cmd_task *ctask)
+iscsi_tcp_task_init(struct iscsi_task *task)
{
- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
- struct iscsi_conn *conn = ctask->conn;
- struct scsi_cmnd *sc = ctask->sc;
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+ struct iscsi_conn *conn = task->conn;
+ struct scsi_cmnd *sc = task->sc;
int err;
- BUG_ON(__kfifo_len(tcp_ctask->r2tqueue));
- tcp_ctask->sent = 0;
- tcp_ctask->exp_datasn = 0;
+ if (!sc) {
+ /*
+ * mgmt tasks do not have a scatterlist since they come
+ * in from the iscsi interface.
+ */
+ debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id,
+ task->itt);
+
+ /* Prepare PDU, optionally w/ immediate data */
+ iscsi_tcp_send_hdr_prep(conn, task->hdr, sizeof(*task->hdr));
+
+ /* If we have immediate data, attach a payload */
+ if (task->data_count)
+ iscsi_tcp_send_linear_data_prepare(conn, task->data,
+ task->data_count);
+ return 0;
+ }
+
+ BUG_ON(__kfifo_len(tcp_task->r2tqueue));
+ tcp_task->sent = 0;
+ tcp_task->exp_datasn = 0;
/* Prepare PDU, optionally w/ immediate data */
- debug_scsi("ctask deq [cid %d itt 0x%x imm %d unsol %d]\n",
- conn->id, ctask->itt, ctask->imm_count,
- ctask->unsol_count);
- iscsi_tcp_send_hdr_prep(conn, ctask->hdr, ctask->hdr_len);
+ debug_scsi("task deq [cid %d itt 0x%x imm %d unsol %d]\n",
+ conn->id, task->itt, task->imm_count,
+ task->unsol_count);
+ iscsi_tcp_send_hdr_prep(conn, task->hdr, task->hdr_len);
- if (!ctask->imm_count)
+ if (!task->imm_count)
return 0;
/* If we have immediate data, attach a payload */
err = iscsi_tcp_send_data_prep(conn, scsi_out(sc)->table.sgl,
scsi_out(sc)->table.nents,
- 0, ctask->imm_count);
+ 0, task->imm_count);
if (err)
return err;
- tcp_ctask->sent += ctask->imm_count;
- ctask->imm_count = 0;
- return 0;
-}
-
-/**
- * iscsi_tcp_mtask_xmit - xmit management(immediate) task
- * @conn: iscsi connection
- * @mtask: task management task
- *
- * Notes:
- * The function can return -EAGAIN in which case caller must
- * call it again later, or recover. '0' return code means successful
- * xmit.
- **/
-static int
-iscsi_tcp_mtask_xmit(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask)
-{
- int rc;
-
- /* Flush any pending data first. */
- rc = iscsi_tcp_flush(conn);
- if (rc < 0)
- return rc;
-
- if (mtask->hdr->itt == RESERVED_ITT) {
- struct iscsi_session *session = conn->session;
-
- spin_lock_bh(&session->lock);
- iscsi_free_mgmt_task(conn, mtask);
- spin_unlock_bh(&session->lock);
- }
-
+ tcp_task->sent += task->imm_count;
+ task->imm_count = 0;
return 0;
}
/*
- * iscsi_tcp_ctask_xmit - xmit normal PDU task
- * @conn: iscsi connection
- * @ctask: iscsi command task
+ * iscsi_tcp_task_xmit - xmit normal PDU task
+ * @task: iscsi command task
*
* We're expected to return 0 when everything was transmitted succesfully,
* -EAGAIN if there's still data in the queue, or != 0 for any other kind
* of error.
*/
static int
-iscsi_tcp_ctask_xmit(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+iscsi_tcp_task_xmit(struct iscsi_task *task)
{
- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
- struct scsi_cmnd *sc = ctask->sc;
- struct scsi_data_buffer *sdb = scsi_out(sc);
+ struct iscsi_conn *conn = task->conn;
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
+ struct scsi_cmnd *sc = task->sc;
+ struct scsi_data_buffer *sdb;
int rc = 0;
flush:
@@ -1398,31 +1402,39 @@ flush:
if (rc < 0)
return rc;
+ /* mgmt command */
+ if (!sc) {
+ if (task->hdr->itt == RESERVED_ITT)
+ iscsi_put_task(task);
+ return 0;
+ }
+
/* Are we done already? */
if (sc->sc_data_direction != DMA_TO_DEVICE)
return 0;
- if (ctask->unsol_count != 0) {
- struct iscsi_data *hdr = &tcp_ctask->unsol_dtask.hdr;
+ sdb = scsi_out(sc);
+ if (task->unsol_count != 0) {
+ struct iscsi_data *hdr = &tcp_task->unsol_dtask.hdr;
/* Prepare a header for the unsolicited PDU.
* The amount of data we want to send will be
- * in ctask->data_count.
+ * in task->data_count.
* FIXME: return the data count instead.
*/
- iscsi_prep_unsolicit_data_pdu(ctask, hdr);
+ iscsi_prep_unsolicit_data_pdu(task, hdr);
debug_tcp("unsol dout [itt 0x%x doff %d dlen %d]\n",
- ctask->itt, tcp_ctask->sent, ctask->data_count);
+ task->itt, tcp_task->sent, task->data_count);
iscsi_tcp_send_hdr_prep(conn, hdr, sizeof(*hdr));
rc = iscsi_tcp_send_data_prep(conn, sdb->table.sgl,
- sdb->table.nents, tcp_ctask->sent,
- ctask->data_count);
+ sdb->table.nents, tcp_task->sent,
+ task->data_count);
if (rc)
goto fail;
- tcp_ctask->sent += ctask->data_count;
- ctask->unsol_count -= ctask->data_count;
+ tcp_task->sent += task->data_count;
+ task->unsol_count -= task->data_count;
goto flush;
} else {
struct iscsi_session *session = conn->session;
@@ -1431,22 +1443,22 @@ flush:
/* All unsolicited PDUs sent. Check for solicited PDUs.
*/
spin_lock_bh(&session->lock);
- r2t = tcp_ctask->r2t;
+ r2t = tcp_task->r2t;
if (r2t != NULL) {
/* Continue with this R2T? */
- if (!iscsi_solicit_data_cont(conn, ctask, r2t)) {
+ if (!iscsi_solicit_data_cont(conn, task, r2t)) {
debug_scsi(" done with r2t %p\n", r2t);
- __kfifo_put(tcp_ctask->r2tpool.queue,
+ __kfifo_put(tcp_task->r2tpool.queue,
(void*)&r2t, sizeof(void*));
- tcp_ctask->r2t = r2t = NULL;
+ tcp_task->r2t = r2t = NULL;
}
}
if (r2t == NULL) {
- __kfifo_get(tcp_ctask->r2tqueue, (void*)&tcp_ctask->r2t,
+ __kfifo_get(tcp_task->r2tqueue, (void*)&tcp_task->r2t,
sizeof(void*));
- r2t = tcp_ctask->r2t;
+ r2t = tcp_task->r2t;
}
spin_unlock_bh(&session->lock);
@@ -1457,7 +1469,7 @@ flush:
}
debug_scsi("sol dout %p [dsn %d itt 0x%x doff %d dlen %d]\n",
- r2t, r2t->solicit_datasn - 1, ctask->itt,
+ r2t, r2t->solicit_datasn - 1, task->itt,
r2t->data_offset + r2t->sent, r2t->data_count);
iscsi_tcp_send_hdr_prep(conn, &r2t->dtask.hdr,
@@ -1469,7 +1481,7 @@ flush:
r2t->data_count);
if (rc)
goto fail;
- tcp_ctask->sent += r2t->data_count;
+ tcp_task->sent += r2t->data_count;
r2t->sent += r2t->data_count;
goto flush;
}
@@ -1486,7 +1498,7 @@ iscsi_tcp_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
struct iscsi_cls_conn *cls_conn;
struct iscsi_tcp_conn *tcp_conn;
- cls_conn = iscsi_conn_setup(cls_session, conn_idx);
+ cls_conn = iscsi_conn_setup(cls_session, sizeof(*tcp_conn), conn_idx);
if (!cls_conn)
return NULL;
conn = cls_conn->dd_data;
@@ -1496,18 +1508,14 @@ iscsi_tcp_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
*/
conn->max_recv_dlength = ISCSI_DEF_MAX_RECV_SEG_LEN;
- tcp_conn = kzalloc(sizeof(*tcp_conn), GFP_KERNEL);
- if (!tcp_conn)
- goto tcp_conn_alloc_fail;
-
- conn->dd_data = tcp_conn;
+ tcp_conn = conn->dd_data;
tcp_conn->iscsi_conn = conn;
tcp_conn->tx_hash.tfm = crypto_alloc_hash("crc32c", 0,
CRYPTO_ALG_ASYNC);
tcp_conn->tx_hash.flags = 0;
if (IS_ERR(tcp_conn->tx_hash.tfm))
- goto free_tcp_conn;
+ goto free_conn;
tcp_conn->rx_hash.tfm = crypto_alloc_hash("crc32c", 0,
CRYPTO_ALG_ASYNC);
@@ -1519,14 +1527,12 @@ iscsi_tcp_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
free_tx_tfm:
crypto_free_hash(tcp_conn->tx_hash.tfm);
-free_tcp_conn:
+free_conn:
iscsi_conn_printk(KERN_ERR, conn,
"Could not create connection due to crc32c "
"loading error. Make sure the crc32c "
"module is built as a module or into the "
"kernel\n");
- kfree(tcp_conn);
-tcp_conn_alloc_fail:
iscsi_conn_teardown(cls_conn);
return NULL;
}
@@ -1547,7 +1553,6 @@ iscsi_tcp_release_conn(struct iscsi_conn *conn)
spin_lock_bh(&session->lock);
tcp_conn->sock = NULL;
- conn->recv_lock = NULL;
spin_unlock_bh(&session->lock);
sockfd_put(sock);
}
@@ -1559,20 +1564,32 @@ iscsi_tcp_conn_destroy(struct iscsi_cls_conn *cls_conn)
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
iscsi_tcp_release_conn(conn);
- iscsi_conn_teardown(cls_conn);
if (tcp_conn->tx_hash.tfm)
crypto_free_hash(tcp_conn->tx_hash.tfm);
if (tcp_conn->rx_hash.tfm)
crypto_free_hash(tcp_conn->rx_hash.tfm);
- kfree(tcp_conn);
+ iscsi_conn_teardown(cls_conn);
}
static void
iscsi_tcp_conn_stop(struct iscsi_cls_conn *cls_conn, int flag)
{
struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
+
+ /* userspace may have goofed up and not bound us */
+ if (!tcp_conn->sock)
+ return;
+ /*
+ * Make sure our recv side is stopped.
+ * Older tools called conn stop before ep_disconnect
+ * so IO could still be coming in.
+ */
+ write_lock_bh(&tcp_conn->sock->sk->sk_callback_lock);
+ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
+ write_unlock_bh(&tcp_conn->sock->sk->sk_callback_lock);
iscsi_conn_stop(cls_conn, flag);
iscsi_tcp_release_conn(conn);
@@ -1623,6 +1640,8 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
struct iscsi_cls_conn *cls_conn, uint64_t transport_eph,
int is_leading)
{
+ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+ struct iscsi_host *ihost = shost_priv(shost);
struct iscsi_conn *conn = cls_conn->dd_data;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct sock *sk;
@@ -1646,8 +1665,8 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
if (err)
goto free_socket;
- err = iscsi_tcp_get_addr(conn, sock, conn->local_address,
- &conn->local_port, kernel_getsockname);
+ err = iscsi_tcp_get_addr(conn, sock, ihost->local_address,
+ &ihost->local_port, kernel_getsockname);
if (err)
goto free_socket;
@@ -1664,13 +1683,6 @@ iscsi_tcp_conn_bind(struct iscsi_cls_session *cls_session,
sk->sk_sndtimeo = 15 * HZ; /* FIXME: make it configurable */
sk->sk_allocation = GFP_ATOMIC;
- /* FIXME: disable Nagle's algorithm */
-
- /*
- * Intercept TCP callbacks for sendfile like receive
- * processing.
- */
- conn->recv_lock = &sk->sk_callback_lock;
iscsi_conn_set_callbacks(conn);
tcp_conn->sendpage = tcp_conn->sock->ops->sendpage;
/*
@@ -1684,21 +1696,6 @@ free_socket:
return err;
}
-/* called with host lock */
-static void
-iscsi_tcp_mtask_init(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask)
-{
- debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id, mtask->itt);
-
- /* Prepare PDU, optionally w/ immediate data */
- iscsi_tcp_send_hdr_prep(conn, mtask->hdr, sizeof(*mtask->hdr));
-
- /* If we have immediate data, attach a payload */
- if (mtask->data_count)
- iscsi_tcp_send_linear_data_prepare(conn, mtask->data,
- mtask->data_count);
-}
-
static int
iscsi_r2tpool_alloc(struct iscsi_session *session)
{
@@ -1709,8 +1706,8 @@ iscsi_r2tpool_alloc(struct iscsi_session *session)
* initialize per-task: R2T pool and xmit queue
*/
for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
- struct iscsi_cmd_task *ctask = session->cmds[cmd_i];
- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+ struct iscsi_task *task = session->cmds[cmd_i];
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
/*
* pre-allocated x4 as much r2ts to handle race when
@@ -1719,16 +1716,16 @@ iscsi_r2tpool_alloc(struct iscsi_session *session)
*/
/* R2T pool */
- if (iscsi_pool_init(&tcp_ctask->r2tpool, session->max_r2t * 4, NULL,
+ if (iscsi_pool_init(&tcp_task->r2tpool, session->max_r2t * 4, NULL,
sizeof(struct iscsi_r2t_info))) {
goto r2t_alloc_fail;
}
/* R2T xmit queue */
- tcp_ctask->r2tqueue = kfifo_alloc(
+ tcp_task->r2tqueue = kfifo_alloc(
session->max_r2t * 4 * sizeof(void*), GFP_KERNEL, NULL);
- if (tcp_ctask->r2tqueue == ERR_PTR(-ENOMEM)) {
- iscsi_pool_free(&tcp_ctask->r2tpool);
+ if (tcp_task->r2tqueue == ERR_PTR(-ENOMEM)) {
+ iscsi_pool_free(&tcp_task->r2tpool);
goto r2t_alloc_fail;
}
}
@@ -1737,11 +1734,11 @@ iscsi_r2tpool_alloc(struct iscsi_session *session)
r2t_alloc_fail:
for (i = 0; i < cmd_i; i++) {
- struct iscsi_cmd_task *ctask = session->cmds[i];
- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+ struct iscsi_task *task = session->cmds[i];
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
- kfifo_free(tcp_ctask->r2tqueue);
- iscsi_pool_free(&tcp_ctask->r2tpool);
+ kfifo_free(tcp_task->r2tqueue);
+ iscsi_pool_free(&tcp_task->r2tpool);
}
return -ENOMEM;
}
@@ -1752,11 +1749,11 @@ iscsi_r2tpool_free(struct iscsi_session *session)
int i;
for (i = 0; i < session->cmds_max; i++) {
- struct iscsi_cmd_task *ctask = session->cmds[i];
- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
+ struct iscsi_task *task = session->cmds[i];
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
- kfifo_free(tcp_ctask->r2tqueue);
- iscsi_pool_free(&tcp_ctask->r2tpool);
+ kfifo_free(tcp_task->r2tqueue);
+ iscsi_pool_free(&tcp_task->r2tpool);
}
}
@@ -1821,29 +1818,6 @@ iscsi_tcp_conn_get_param(struct iscsi_cls_conn *cls_conn,
return len;
}
-static int
-iscsi_tcp_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param,
- char *buf)
-{
- struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
- int len;
-
- switch (param) {
- case ISCSI_HOST_PARAM_IPADDRESS:
- spin_lock_bh(&session->lock);
- if (!session->leadconn)
- len = -ENODEV;
- else
- len = sprintf(buf, "%s\n",
- session->leadconn->local_address);
- spin_unlock_bh(&session->lock);
- break;
- default:
- return iscsi_host_get_param(shost, param, buf);
- }
- return len;
-}
-
static void
iscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats)
{
@@ -1869,54 +1843,71 @@ iscsi_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats)
}
static struct iscsi_cls_session *
-iscsi_tcp_session_create(struct iscsi_transport *iscsit,
- struct scsi_transport_template *scsit,
- uint16_t cmds_max, uint16_t qdepth,
- uint32_t initial_cmdsn, uint32_t *hostno)
+iscsi_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
+ uint16_t qdepth, uint32_t initial_cmdsn,
+ uint32_t *hostno)
{
struct iscsi_cls_session *cls_session;
struct iscsi_session *session;
- uint32_t hn;
+ struct Scsi_Host *shost;
int cmd_i;
- cls_session = iscsi_session_setup(iscsit, scsit, cmds_max, qdepth,
- sizeof(struct iscsi_tcp_cmd_task),
- sizeof(struct iscsi_tcp_mgmt_task),
- initial_cmdsn, &hn);
- if (!cls_session)
+ if (ep) {
+ printk(KERN_ERR "iscsi_tcp: invalid ep %p.\n", ep);
return NULL;
- *hostno = hn;
-
- session = class_to_transport_session(cls_session);
- for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
- struct iscsi_cmd_task *ctask = session->cmds[cmd_i];
- struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;
-
- ctask->hdr = &tcp_ctask->hdr.cmd_hdr;
- ctask->hdr_max = sizeof(tcp_ctask->hdr) - ISCSI_DIGEST_SIZE;
}
- for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++) {
- struct iscsi_mgmt_task *mtask = session->mgmt_cmds[cmd_i];
- struct iscsi_tcp_mgmt_task *tcp_mtask = mtask->dd_data;
+ shost = iscsi_host_alloc(&iscsi_sht, 0, qdepth);
+ if (!shost)
+ return NULL;
+ shost->transportt = iscsi_tcp_scsi_transport;
+ shost->max_lun = iscsi_max_lun;
+ shost->max_id = 0;
+ shost->max_channel = 0;
+ shost->max_cmd_len = SCSI_MAX_VARLEN_CDB_SIZE;
+ shost->can_queue = cmds_max;
+
+ if (iscsi_host_add(shost, NULL))
+ goto free_host;
+ *hostno = shost->host_no;
+
+ cls_session = iscsi_session_setup(&iscsi_tcp_transport, shost, cmds_max,
+ sizeof(struct iscsi_tcp_task),
+ initial_cmdsn, 0);
+ if (!cls_session)
+ goto remove_host;
+ session = cls_session->dd_data;
+
+ shost->can_queue = session->scsi_cmds_max;
+ for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
+ struct iscsi_task *task = session->cmds[cmd_i];
+ struct iscsi_tcp_task *tcp_task = task->dd_data;
- mtask->hdr = (struct iscsi_hdr *) &tcp_mtask->hdr;
+ task->hdr = &tcp_task->hdr.cmd_hdr;
+ task->hdr_max = sizeof(tcp_task->hdr) - ISCSI_DIGEST_SIZE;
}
- if (iscsi_r2tpool_alloc(class_to_transport_session(cls_session)))
- goto r2tpool_alloc_fail;
-
+ if (iscsi_r2tpool_alloc(session))
+ goto remove_session;
return cls_session;
-r2tpool_alloc_fail:
+remove_session:
iscsi_session_teardown(cls_session);
+remove_host:
+ iscsi_host_remove(shost);
+free_host:
+ iscsi_host_free(shost);
return NULL;
}
static void iscsi_tcp_session_destroy(struct iscsi_cls_session *cls_session)
{
- iscsi_r2tpool_free(class_to_transport_session(cls_session));
- iscsi_session_teardown(cls_session);
+ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+
+ iscsi_r2tpool_free(cls_session->dd_data);
+
+ iscsi_host_remove(shost);
+ iscsi_host_free(shost);
}
static int iscsi_tcp_slave_configure(struct scsi_device *sdev)
@@ -1971,14 +1962,11 @@ static struct iscsi_transport iscsi_tcp_transport = {
ISCSI_USERNAME_IN | ISCSI_PASSWORD_IN |
ISCSI_FAST_ABORT | ISCSI_ABORT_TMO |
ISCSI_LU_RESET_TMO |
- ISCSI_PING_TMO | ISCSI_RECV_TMO,
+ ISCSI_PING_TMO | ISCSI_RECV_TMO |
+ ISCSI_IFACE_NAME | ISCSI_INITIATOR_NAME,
.host_param_mask = ISCSI_HOST_HWADDRESS | ISCSI_HOST_IPADDRESS |
ISCSI_HOST_INITIATOR_NAME |
ISCSI_HOST_NETDEV_NAME,
- .host_template = &iscsi_sht,
- .conndata_size = sizeof(struct iscsi_conn),
- .max_conn = 1,
- .max_cmd_len = 16,
/* session management */
.create_session = iscsi_tcp_session_create,
.destroy_session = iscsi_tcp_session_destroy,
@@ -1992,16 +1980,14 @@ static struct iscsi_transport iscsi_tcp_transport = {
.start_conn = iscsi_conn_start,
.stop_conn = iscsi_tcp_conn_stop,
/* iscsi host params */
- .get_host_param = iscsi_tcp_host_get_param,
+ .get_host_param = iscsi_host_get_param,
.set_host_param = iscsi_host_set_param,
/* IO */
.send_pdu = iscsi_conn_send_pdu,
.get_stats = iscsi_conn_get_stats,
- .init_cmd_task = iscsi_tcp_ctask_init,
- .init_mgmt_task = iscsi_tcp_mtask_init,
- .xmit_cmd_task = iscsi_tcp_ctask_xmit,
- .xmit_mgmt_task = iscsi_tcp_mtask_xmit,
- .cleanup_cmd_task = iscsi_tcp_cleanup_ctask,
+ .init_task = iscsi_tcp_task_init,
+ .xmit_task = iscsi_tcp_task_xmit,
+ .cleanup_task = iscsi_tcp_cleanup_task,
/* recovery */
.session_recovery_timedout = iscsi_session_recovery_timedout,
};
@@ -2014,9 +2000,10 @@ iscsi_tcp_init(void)
iscsi_max_lun);
return -EINVAL;
}
- iscsi_tcp_transport.max_lun = iscsi_max_lun;
- if (!iscsi_register_transport(&iscsi_tcp_transport))
+ iscsi_tcp_scsi_transport = iscsi_register_transport(
+ &iscsi_tcp_transport);
+ if (!iscsi_tcp_scsi_transport)
return -ENODEV;
return 0;
diff --git a/drivers/scsi/iscsi_tcp.h b/drivers/scsi/iscsi_tcp.h
index ed0b991d1e72..498d8ca39848 100644
--- a/drivers/scsi/iscsi_tcp.h
+++ b/drivers/scsi/iscsi_tcp.h
@@ -103,11 +103,6 @@ struct iscsi_data_task {
char hdrext[ISCSI_DIGEST_SIZE];/* Header-Digest */
};
-struct iscsi_tcp_mgmt_task {
- struct iscsi_hdr hdr;
- char hdrext[ISCSI_DIGEST_SIZE]; /* Header-Digest */
-};
-
struct iscsi_r2t_info {
__be32 ttt; /* copied from R2T */
__be32 exp_statsn; /* copied from R2T */
@@ -119,7 +114,7 @@ struct iscsi_r2t_info {
struct iscsi_data_task dtask; /* Data-Out header buf */
};
-struct iscsi_tcp_cmd_task {
+struct iscsi_tcp_task {
struct iscsi_hdr_buff {
struct iscsi_cmd cmd_hdr;
char hdrextbuf[ISCSI_MAX_AHS_SIZE +
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index b43bf1d60dac..8b4e412a0974 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -38,14 +38,6 @@
#include <scsi/scsi_transport_iscsi.h>
#include <scsi/libiscsi.h>
-struct iscsi_session *
-class_to_transport_session(struct iscsi_cls_session *cls_session)
-{
- struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
- return iscsi_hostdata(shost->hostdata);
-}
-EXPORT_SYMBOL_GPL(class_to_transport_session);
-
/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */
#define SNA32_CHECK 2147483648UL
@@ -87,68 +79,70 @@ iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
* xmit thread
*/
if (!list_empty(&session->leadconn->xmitqueue) ||
- !list_empty(&session->leadconn->mgmtqueue))
- scsi_queue_work(session->host,
- &session->leadconn->xmitwork);
+ !list_empty(&session->leadconn->mgmtqueue)) {
+ if (!(session->tt->caps & CAP_DATA_PATH_OFFLOAD))
+ scsi_queue_work(session->host,
+ &session->leadconn->xmitwork);
+ }
}
}
EXPORT_SYMBOL_GPL(iscsi_update_cmdsn);
-void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask,
+void iscsi_prep_unsolicit_data_pdu(struct iscsi_task *task,
struct iscsi_data *hdr)
{
- struct iscsi_conn *conn = ctask->conn;
+ struct iscsi_conn *conn = task->conn;
memset(hdr, 0, sizeof(struct iscsi_data));
hdr->ttt = cpu_to_be32(ISCSI_RESERVED_TAG);
- hdr->datasn = cpu_to_be32(ctask->unsol_datasn);
- ctask->unsol_datasn++;
+ hdr->datasn = cpu_to_be32(task->unsol_datasn);
+ task->unsol_datasn++;
hdr->opcode = ISCSI_OP_SCSI_DATA_OUT;
- memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun));
+ memcpy(hdr->lun, task->hdr->lun, sizeof(hdr->lun));
- hdr->itt = ctask->hdr->itt;
+ hdr->itt = task->hdr->itt;
hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
- hdr->offset = cpu_to_be32(ctask->unsol_offset);
+ hdr->offset = cpu_to_be32(task->unsol_offset);
- if (ctask->unsol_count > conn->max_xmit_dlength) {
+ if (task->unsol_count > conn->max_xmit_dlength) {
hton24(hdr->dlength, conn->max_xmit_dlength);
- ctask->data_count = conn->max_xmit_dlength;
- ctask->unsol_offset += ctask->data_count;
+ task->data_count = conn->max_xmit_dlength;
+ task->unsol_offset += task->data_count;
hdr->flags = 0;
} else {
- hton24(hdr->dlength, ctask->unsol_count);
- ctask->data_count = ctask->unsol_count;
+ hton24(hdr->dlength, task->unsol_count);
+ task->data_count = task->unsol_count;
hdr->flags = ISCSI_FLAG_CMD_FINAL;
}
}
EXPORT_SYMBOL_GPL(iscsi_prep_unsolicit_data_pdu);
-static int iscsi_add_hdr(struct iscsi_cmd_task *ctask, unsigned len)
+static int iscsi_add_hdr(struct iscsi_task *task, unsigned len)
{
- unsigned exp_len = ctask->hdr_len + len;
+ unsigned exp_len = task->hdr_len + len;
- if (exp_len > ctask->hdr_max) {
+ if (exp_len > task->hdr_max) {
WARN_ON(1);
return -EINVAL;
}
WARN_ON(len & (ISCSI_PAD_LEN - 1)); /* caller must pad the AHS */
- ctask->hdr_len = exp_len;
+ task->hdr_len = exp_len;
return 0;
}
/*
* make an extended cdb AHS
*/
-static int iscsi_prep_ecdb_ahs(struct iscsi_cmd_task *ctask)
+static int iscsi_prep_ecdb_ahs(struct iscsi_task *task)
{
- struct scsi_cmnd *cmd = ctask->sc;
+ struct scsi_cmnd *cmd = task->sc;
unsigned rlen, pad_len;
unsigned short ahslength;
struct iscsi_ecdb_ahdr *ecdb_ahdr;
int rc;
- ecdb_ahdr = iscsi_next_hdr(ctask);
+ ecdb_ahdr = iscsi_next_hdr(task);
rlen = cmd->cmd_len - ISCSI_CDB_SIZE;
BUG_ON(rlen > sizeof(ecdb_ahdr->ecdb));
@@ -156,7 +150,7 @@ static int iscsi_prep_ecdb_ahs(struct iscsi_cmd_task *ctask)
pad_len = iscsi_padding(rlen);
- rc = iscsi_add_hdr(ctask, sizeof(ecdb_ahdr->ahslength) +
+ rc = iscsi_add_hdr(task, sizeof(ecdb_ahdr->ahslength) +
sizeof(ecdb_ahdr->ahstype) + ahslength + pad_len);
if (rc)
return rc;
@@ -171,19 +165,19 @@ static int iscsi_prep_ecdb_ahs(struct iscsi_cmd_task *ctask)
debug_scsi("iscsi_prep_ecdb_ahs: varlen_cdb_len %d "
"rlen %d pad_len %d ahs_length %d iscsi_headers_size %u\n",
- cmd->cmd_len, rlen, pad_len, ahslength, ctask->hdr_len);
+ cmd->cmd_len, rlen, pad_len, ahslength, task->hdr_len);
return 0;
}
-static int iscsi_prep_bidi_ahs(struct iscsi_cmd_task *ctask)
+static int iscsi_prep_bidi_ahs(struct iscsi_task *task)
{
- struct scsi_cmnd *sc = ctask->sc;
+ struct scsi_cmnd *sc = task->sc;
struct iscsi_rlength_ahdr *rlen_ahdr;
int rc;
- rlen_ahdr = iscsi_next_hdr(ctask);
- rc = iscsi_add_hdr(ctask, sizeof(*rlen_ahdr));
+ rlen_ahdr = iscsi_next_hdr(task);
+ rc = iscsi_add_hdr(task, sizeof(*rlen_ahdr));
if (rc)
return rc;
@@ -203,28 +197,28 @@ static int iscsi_prep_bidi_ahs(struct iscsi_cmd_task *ctask)
/**
* iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu
- * @ctask: iscsi cmd task
+ * @task: iscsi task
*
* Prep basic iSCSI PDU fields for a scsi cmd pdu. The LLD should set
* fields like dlength or final based on how much data it sends
*/
-static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
+static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task)
{
- struct iscsi_conn *conn = ctask->conn;
+ struct iscsi_conn *conn = task->conn;
struct iscsi_session *session = conn->session;
- struct iscsi_cmd *hdr = ctask->hdr;
- struct scsi_cmnd *sc = ctask->sc;
+ struct iscsi_cmd *hdr = task->hdr;
+ struct scsi_cmnd *sc = task->sc;
unsigned hdrlength, cmd_len;
int rc;
- ctask->hdr_len = 0;
- rc = iscsi_add_hdr(ctask, sizeof(*hdr));
+ task->hdr_len = 0;
+ rc = iscsi_add_hdr(task, sizeof(*hdr));
if (rc)
return rc;
hdr->opcode = ISCSI_OP_SCSI_CMD;
hdr->flags = ISCSI_ATTR_SIMPLE;
int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun);
- hdr->itt = build_itt(ctask->itt, session->age);
+ hdr->itt = build_itt(task->itt, session->age);
hdr->cmdsn = cpu_to_be32(session->cmdsn);
session->cmdsn++;
hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
@@ -232,17 +226,17 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
if (cmd_len < ISCSI_CDB_SIZE)
memset(&hdr->cdb[cmd_len], 0, ISCSI_CDB_SIZE - cmd_len);
else if (cmd_len > ISCSI_CDB_SIZE) {
- rc = iscsi_prep_ecdb_ahs(ctask);
+ rc = iscsi_prep_ecdb_ahs(task);
if (rc)
return rc;
cmd_len = ISCSI_CDB_SIZE;
}
memcpy(hdr->cdb, sc->cmnd, cmd_len);
- ctask->imm_count = 0;
+ task->imm_count = 0;
if (scsi_bidi_cmnd(sc)) {
hdr->flags |= ISCSI_FLAG_CMD_READ;
- rc = iscsi_prep_bidi_ahs(ctask);
+ rc = iscsi_prep_bidi_ahs(task);
if (rc)
return rc;
}
@@ -264,28 +258,28 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
*
* pad_count bytes to be sent as zero-padding
*/
- ctask->unsol_count = 0;
- ctask->unsol_offset = 0;
- ctask->unsol_datasn = 0;
+ task->unsol_count = 0;
+ task->unsol_offset = 0;
+ task->unsol_datasn = 0;
if (session->imm_data_en) {
if (out_len >= session->first_burst)
- ctask->imm_count = min(session->first_burst,
+ task->imm_count = min(session->first_burst,
conn->max_xmit_dlength);
else
- ctask->imm_count = min(out_len,
+ task->imm_count = min(out_len,
conn->max_xmit_dlength);
- hton24(hdr->dlength, ctask->imm_count);
+ hton24(hdr->dlength, task->imm_count);
} else
zero_data(hdr->dlength);
if (!session->initial_r2t_en) {
- ctask->unsol_count = min(session->first_burst, out_len)
- - ctask->imm_count;
- ctask->unsol_offset = ctask->imm_count;
+ task->unsol_count = min(session->first_burst, out_len)
+ - task->imm_count;
+ task->unsol_offset = task->imm_count;
}
- if (!ctask->unsol_count)
+ if (!task->unsol_count)
/* No unsolicit Data-Out's */
hdr->flags |= ISCSI_FLAG_CMD_FINAL;
} else {
@@ -298,7 +292,7 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
}
/* calculate size of additional header segments (AHSs) */
- hdrlength = ctask->hdr_len - sizeof(*hdr);
+ hdrlength = task->hdr_len - sizeof(*hdr);
WARN_ON(hdrlength & (ISCSI_PAD_LEN-1));
hdrlength /= ISCSI_PAD_LEN;
@@ -306,76 +300,115 @@ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
WARN_ON(hdrlength >= 256);
hdr->hlength = hdrlength & 0xFF;
- if (conn->session->tt->init_cmd_task(conn->ctask))
- return EIO;
+ if (conn->session->tt->init_task &&
+ conn->session->tt->init_task(task))
+ return -EIO;
+
+ task->state = ISCSI_TASK_RUNNING;
+ list_move_tail(&task->running, &conn->run_list);
conn->scsicmd_pdus_cnt++;
- debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x "
- "len %d bidi_len %d cmdsn %d win %d]\n",
- scsi_bidi_cmnd(sc) ? "bidirectional" :
- sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read",
- conn->id, sc, sc->cmnd[0], ctask->itt,
- scsi_bufflen(sc), scsi_bidi_cmnd(sc) ? scsi_in(sc)->length : 0,
- session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
+ debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x len %d "
+ "bidi_len %d cmdsn %d win %d]\n", scsi_bidi_cmnd(sc) ?
+ "bidirectional" : sc->sc_data_direction == DMA_TO_DEVICE ?
+ "write" : "read", conn->id, sc, sc->cmnd[0], task->itt,
+ scsi_bufflen(sc),
+ scsi_bidi_cmnd(sc) ? scsi_in(sc)->length : 0,
+ session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
return 0;
}
/**
- * iscsi_complete_command - return command back to scsi-ml
- * @ctask: iscsi cmd task
+ * iscsi_complete_command - finish a task
+ * @task: iscsi cmd task
*
* Must be called with session lock.
- * This function returns the scsi command to scsi-ml and returns
- * the cmd task to the pool of available cmd tasks.
+ * This function returns the scsi command to scsi-ml or cleans
+ * up mgmt tasks then returns the task to the pool.
*/
-static void iscsi_complete_command(struct iscsi_cmd_task *ctask)
+static void iscsi_complete_command(struct iscsi_task *task)
{
- struct iscsi_conn *conn = ctask->conn;
+ struct iscsi_conn *conn = task->conn;
struct iscsi_session *session = conn->session;
- struct scsi_cmnd *sc = ctask->sc;
+ struct scsi_cmnd *sc = task->sc;
- ctask->state = ISCSI_TASK_COMPLETED;
- ctask->sc = NULL;
- /* SCSI eh reuses commands to verify us */
- sc->SCp.ptr = NULL;
- if (conn->ctask == ctask)
- conn->ctask = NULL;
- list_del_init(&ctask->running);
- __kfifo_put(session->cmdpool.queue, (void*)&ctask, sizeof(void*));
- sc->scsi_done(sc);
+ list_del_init(&task->running);
+ task->state = ISCSI_TASK_COMPLETED;
+ task->sc = NULL;
+
+ if (conn->task == task)
+ conn->task = NULL;
+ /*
+ * login task is preallocated so do not free
+ */
+ if (conn->login_task == task)
+ return;
+
+ __kfifo_put(session->cmdpool.queue, (void*)&task, sizeof(void*));
+
+ if (conn->ping_task == task)
+ conn->ping_task = NULL;
+
+ if (sc) {
+ task->sc = NULL;
+ /* SCSI eh reuses commands to verify us */
+ sc->SCp.ptr = NULL;
+ /*
+ * queue command may call this to free the task, but
+ * not have setup the sc callback
+ */
+ if (sc->scsi_done)
+ sc->scsi_done(sc);
+ }
+}
+
+void __iscsi_get_task(struct iscsi_task *task)
+{
+ atomic_inc(&task->refcount);
}
+EXPORT_SYMBOL_GPL(__iscsi_get_task);
-static void __iscsi_get_ctask(struct iscsi_cmd_task *ctask)
+static void __iscsi_put_task(struct iscsi_task *task)
{
- atomic_inc(&ctask->refcount);
+ if (atomic_dec_and_test(&task->refcount))
+ iscsi_complete_command(task);
}
-static void __iscsi_put_ctask(struct iscsi_cmd_task *ctask)
+void iscsi_put_task(struct iscsi_task *task)
{
- if (atomic_dec_and_test(&ctask->refcount))
- iscsi_complete_command(ctask);
+ struct iscsi_session *session = task->conn->session;
+
+ spin_lock_bh(&session->lock);
+ __iscsi_put_task(task);
+ spin_unlock_bh(&session->lock);
}
+EXPORT_SYMBOL_GPL(iscsi_put_task);
/*
* session lock must be held
*/
-static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
+static void fail_command(struct iscsi_conn *conn, struct iscsi_task *task,
int err)
{
struct scsi_cmnd *sc;
- sc = ctask->sc;
+ sc = task->sc;
if (!sc)
return;
- if (ctask->state == ISCSI_TASK_PENDING)
+ if (task->state == ISCSI_TASK_PENDING)
/*
* cmd never made it to the xmit thread, so we should not count
* the cmd in the sequencing
*/
conn->session->queued_cmdsn--;
else
- conn->session->tt->cleanup_cmd_task(conn, ctask);
+ conn->session->tt->cleanup_task(conn, task);
+ /*
+ * Check if cleanup_task dropped the lock and the command completed,
+ */
+ if (!task->sc)
+ return;
sc->result = err;
if (!scsi_bidi_cmnd(sc))
@@ -384,39 +417,63 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
scsi_out(sc)->resid = scsi_out(sc)->length;
scsi_in(sc)->resid = scsi_in(sc)->length;
}
- if (conn->ctask == ctask)
- conn->ctask = NULL;
+
+ if (conn->task == task)
+ conn->task = NULL;
/* release ref from queuecommand */
- __iscsi_put_ctask(ctask);
+ __iscsi_put_task(task);
}
-/**
- * iscsi_free_mgmt_task - return mgmt task back to pool
- * @conn: iscsi connection
- * @mtask: mtask
- *
- * Must be called with session lock.
- */
-void iscsi_free_mgmt_task(struct iscsi_conn *conn,
- struct iscsi_mgmt_task *mtask)
+static int iscsi_prep_mgmt_task(struct iscsi_conn *conn,
+ struct iscsi_task *task)
{
- list_del_init(&mtask->running);
- if (conn->login_mtask == mtask)
- return;
+ struct iscsi_session *session = conn->session;
+ struct iscsi_hdr *hdr = (struct iscsi_hdr *)task->hdr;
+ struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr;
- if (conn->ping_mtask == mtask)
- conn->ping_mtask = NULL;
- __kfifo_put(conn->session->mgmtpool.queue,
- (void*)&mtask, sizeof(void*));
+ if (conn->session->state == ISCSI_STATE_LOGGING_OUT)
+ return -ENOTCONN;
+
+ if (hdr->opcode != (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) &&
+ hdr->opcode != (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE))
+ nop->exp_statsn = cpu_to_be32(conn->exp_statsn);
+ /*
+ * pre-format CmdSN for outgoing PDU.
+ */
+ nop->cmdsn = cpu_to_be32(session->cmdsn);
+ if (hdr->itt != RESERVED_ITT) {
+ hdr->itt = build_itt(task->itt, session->age);
+ /*
+ * TODO: We always use immediate, so we never hit this.
+ * If we start to send tmfs or nops as non-immediate then
+ * we should start checking the cmdsn numbers for mgmt tasks.
+ */
+ if (conn->c_stage == ISCSI_CONN_STARTED &&
+ !(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
+ session->queued_cmdsn++;
+ session->cmdsn++;
+ }
+ }
+
+ if (session->tt->init_task)
+ session->tt->init_task(task);
+
+ if ((hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT)
+ session->state = ISCSI_STATE_LOGGING_OUT;
+
+ list_move_tail(&task->running, &conn->mgmt_run_list);
+ debug_scsi("mgmtpdu [op 0x%x hdr->itt 0x%x datalen %d]\n",
+ hdr->opcode & ISCSI_OPCODE_MASK, hdr->itt,
+ task->data_count);
+ return 0;
}
-EXPORT_SYMBOL_GPL(iscsi_free_mgmt_task);
-static struct iscsi_mgmt_task *
+static struct iscsi_task *
__iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
char *data, uint32_t data_size)
{
struct iscsi_session *session = conn->session;
- struct iscsi_mgmt_task *mtask;
+ struct iscsi_task *task;
if (session->state == ISCSI_STATE_TERMINATE)
return NULL;
@@ -426,29 +483,56 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
/*
* Login and Text are sent serially, in
* request-followed-by-response sequence.
- * Same mtask can be used. Same ITT must be used.
- * Note that login_mtask is preallocated at conn_create().
+ * Same task can be used. Same ITT must be used.
+ * Note that login_task is preallocated at conn_create().
*/
- mtask = conn->login_mtask;
+ task = conn->login_task;
else {
BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE);
BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED);
- if (!__kfifo_get(session->mgmtpool.queue,
- (void*)&mtask, sizeof(void*)))
+ if (!__kfifo_get(session->cmdpool.queue,
+ (void*)&task, sizeof(void*)))
return NULL;
+
+ if ((hdr->opcode == (ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE)) &&
+ hdr->ttt == RESERVED_ITT) {
+ conn->ping_task = task;
+ conn->last_ping = jiffies;
+ }
}
+ /*
+ * released in complete pdu for task we expect a response for, and
+ * released by the lld when it has transmitted the task for
+ * pdus we do not expect a response for.
+ */
+ atomic_set(&task->refcount, 1);
+ task->conn = conn;
+ task->sc = NULL;
if (data_size) {
- memcpy(mtask->data, data, data_size);
- mtask->data_count = data_size;
+ memcpy(task->data, data, data_size);
+ task->data_count = data_size;
+ } else
+ task->data_count = 0;
+
+ memcpy(task->hdr, hdr, sizeof(struct iscsi_hdr));
+ INIT_LIST_HEAD(&task->running);
+ list_add_tail(&task->running, &conn->mgmtqueue);
+
+ if (session->tt->caps & CAP_DATA_PATH_OFFLOAD) {
+ if (iscsi_prep_mgmt_task(conn, task)) {
+ __iscsi_put_task(task);
+ return NULL;
+ }
+
+ if (session->tt->xmit_task(task))
+ task = NULL;
+
} else
- mtask->data_count = 0;
+ scsi_queue_work(conn->session->host, &conn->xmitwork);
- memcpy(mtask->hdr, hdr, sizeof(struct iscsi_hdr));
- INIT_LIST_HEAD(&mtask->running);
- list_add_tail(&mtask->running, &conn->mgmtqueue);
- return mtask;
+ return task;
}
int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr,
@@ -462,7 +546,6 @@ int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr,
if (!__iscsi_conn_send_pdu(conn, hdr, data, data_size))
err = -EPERM;
spin_unlock_bh(&session->lock);
- scsi_queue_work(session->host, &conn->xmitwork);
return err;
}
EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);
@@ -471,7 +554,7 @@ EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);
* iscsi_cmd_rsp - SCSI Command Response processing
* @conn: iscsi connection
* @hdr: iscsi header
- * @ctask: scsi command task
+ * @task: scsi command task
* @data: cmd data buffer
* @datalen: len of buffer
*
@@ -479,12 +562,12 @@ EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);
* then completes the command and task.
**/
static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
- struct iscsi_cmd_task *ctask, char *data,
+ struct iscsi_task *task, char *data,
int datalen)
{
struct iscsi_cmd_rsp *rhdr = (struct iscsi_cmd_rsp *)hdr;
struct iscsi_session *session = conn->session;
- struct scsi_cmnd *sc = ctask->sc;
+ struct scsi_cmnd *sc = task->sc;
iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);
conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1;
@@ -508,7 +591,7 @@ invalid_datalen:
goto out;
}
- senselen = be16_to_cpu(get_unaligned((__be16 *) data));
+ senselen = get_unaligned_be16(data);
if (datalen < senselen)
goto invalid_datalen;
@@ -544,10 +627,10 @@ invalid_datalen:
}
out:
debug_scsi("done [sc %lx res %d itt 0x%x]\n",
- (long)sc, sc->result, ctask->itt);
+ (long)sc, sc->result, task->itt);
conn->scsirsp_pdus_cnt++;
- __iscsi_put_ctask(ctask);
+ __iscsi_put_task(task);
}
static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
@@ -572,9 +655,9 @@ static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
static void iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr)
{
struct iscsi_nopout hdr;
- struct iscsi_mgmt_task *mtask;
+ struct iscsi_task *task;
- if (!rhdr && conn->ping_mtask)
+ if (!rhdr && conn->ping_task)
return;
memset(&hdr, 0, sizeof(struct iscsi_nopout));
@@ -588,18 +671,9 @@ static void iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr)
} else
hdr.ttt = RESERVED_ITT;
- mtask = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)&hdr, NULL, 0);
- if (!mtask) {
+ task = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)&hdr, NULL, 0);
+ if (!task)
iscsi_conn_printk(KERN_ERR, conn, "Could not send nopout\n");
- return;
- }
-
- /* only track our nops */
- if (!rhdr) {
- conn->ping_mtask = mtask;
- conn->last_ping = jiffies;
- }
- scsi_queue_work(conn->session->host, &conn->xmitwork);
}
static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
@@ -628,6 +702,31 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
}
/**
+ * iscsi_itt_to_task - look up task by itt
+ * @conn: iscsi connection
+ * @itt: itt
+ *
+ * This should be used for mgmt tasks like login and nops, or if
+ * the LDD's itt space does not include the session age.
+ *
+ * The session lock must be held.
+ */
+static struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt)
+{
+ struct iscsi_session *session = conn->session;
+ uint32_t i;
+
+ if (itt == RESERVED_ITT)
+ return NULL;
+
+ i = get_itt(itt);
+ if (i >= session->cmds_max)
+ return NULL;
+
+ return session->cmds[i];
+}
+
+/**
* __iscsi_complete_pdu - complete pdu
* @conn: iscsi conn
* @hdr: iscsi header
@@ -638,108 +737,28 @@ static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
* queuecommand or send generic. session lock must be held and verify
* itt must have been called.
*/
-static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
- char *data, int datalen)
+int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
+ char *data, int datalen)
{
struct iscsi_session *session = conn->session;
int opcode = hdr->opcode & ISCSI_OPCODE_MASK, rc = 0;
- struct iscsi_cmd_task *ctask;
- struct iscsi_mgmt_task *mtask;
+ struct iscsi_task *task;
uint32_t itt;
conn->last_recv = jiffies;
+ rc = iscsi_verify_itt(conn, hdr->itt);
+ if (rc)
+ return rc;
+
if (hdr->itt != RESERVED_ITT)
itt = get_itt(hdr->itt);
else
itt = ~0U;
- if (itt < session->cmds_max) {
- ctask = session->cmds[itt];
-
- debug_scsi("cmdrsp [op 0x%x cid %d itt 0x%x len %d]\n",
- opcode, conn->id, ctask->itt, datalen);
-
- switch(opcode) {
- case ISCSI_OP_SCSI_CMD_RSP:
- BUG_ON((void*)ctask != ctask->sc->SCp.ptr);
- iscsi_scsi_cmd_rsp(conn, hdr, ctask, data,
- datalen);
- break;
- case ISCSI_OP_SCSI_DATA_IN:
- BUG_ON((void*)ctask != ctask->sc->SCp.ptr);
- if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
- conn->scsirsp_pdus_cnt++;
- __iscsi_put_ctask(ctask);
- }
- break;
- case ISCSI_OP_R2T:
- /* LLD handles this for now */
- break;
- default:
- rc = ISCSI_ERR_BAD_OPCODE;
- break;
- }
- } else if (itt >= ISCSI_MGMT_ITT_OFFSET &&
- itt < ISCSI_MGMT_ITT_OFFSET + session->mgmtpool_max) {
- mtask = session->mgmt_cmds[itt - ISCSI_MGMT_ITT_OFFSET];
-
- debug_scsi("immrsp [op 0x%x cid %d itt 0x%x len %d]\n",
- opcode, conn->id, mtask->itt, datalen);
+ debug_scsi("[op 0x%x cid %d itt 0x%x len %d]\n",
+ opcode, conn->id, itt, datalen);
- iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
- switch(opcode) {
- case ISCSI_OP_LOGOUT_RSP:
- if (datalen) {
- rc = ISCSI_ERR_PROTO;
- break;
- }
- conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
- /* fall through */
- case ISCSI_OP_LOGIN_RSP:
- case ISCSI_OP_TEXT_RSP:
- /*
- * login related PDU's exp_statsn is handled in
- * userspace
- */
- if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))
- rc = ISCSI_ERR_CONN_FAILED;
- iscsi_free_mgmt_task(conn, mtask);
- break;
- case ISCSI_OP_SCSI_TMFUNC_RSP:
- if (datalen) {
- rc = ISCSI_ERR_PROTO;
- break;
- }
-
- iscsi_tmf_rsp(conn, hdr);
- iscsi_free_mgmt_task(conn, mtask);
- break;
- case ISCSI_OP_NOOP_IN:
- if (hdr->ttt != cpu_to_be32(ISCSI_RESERVED_TAG) ||
- datalen) {
- rc = ISCSI_ERR_PROTO;
- break;
- }
- conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
-
- if (conn->ping_mtask != mtask) {
- /*
- * If this is not in response to one of our
- * nops then it must be from userspace.
- */
- if (iscsi_recv_pdu(conn->cls_conn, hdr, data,
- datalen))
- rc = ISCSI_ERR_CONN_FAILED;
- } else
- mod_timer(&conn->transport_timer,
- jiffies + conn->recv_timeout);
- iscsi_free_mgmt_task(conn, mtask);
- break;
- default:
- rc = ISCSI_ERR_BAD_OPCODE;
- break;
- }
- } else if (itt == ~0U) {
+ if (itt == ~0U) {
iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
switch(opcode) {
@@ -766,11 +785,104 @@ static int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
rc = ISCSI_ERR_BAD_OPCODE;
break;
}
- } else
- rc = ISCSI_ERR_BAD_ITT;
+ goto out;
+ }
+ switch(opcode) {
+ case ISCSI_OP_SCSI_CMD_RSP:
+ case ISCSI_OP_SCSI_DATA_IN:
+ task = iscsi_itt_to_ctask(conn, hdr->itt);
+ if (!task)
+ return ISCSI_ERR_BAD_ITT;
+ break;
+ case ISCSI_OP_R2T:
+ /*
+ * LLD handles R2Ts if they need to.
+ */
+ return 0;
+ case ISCSI_OP_LOGOUT_RSP:
+ case ISCSI_OP_LOGIN_RSP:
+ case ISCSI_OP_TEXT_RSP:
+ case ISCSI_OP_SCSI_TMFUNC_RSP:
+ case ISCSI_OP_NOOP_IN:
+ task = iscsi_itt_to_task(conn, hdr->itt);
+ if (!task)
+ return ISCSI_ERR_BAD_ITT;
+ break;
+ default:
+ return ISCSI_ERR_BAD_OPCODE;
+ }
+
+ switch(opcode) {
+ case ISCSI_OP_SCSI_CMD_RSP:
+ iscsi_scsi_cmd_rsp(conn, hdr, task, data, datalen);
+ break;
+ case ISCSI_OP_SCSI_DATA_IN:
+ if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
+ conn->scsirsp_pdus_cnt++;
+ iscsi_update_cmdsn(session,
+ (struct iscsi_nopin*) hdr);
+ __iscsi_put_task(task);
+ }
+ break;
+ case ISCSI_OP_LOGOUT_RSP:
+ iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
+ if (datalen) {
+ rc = ISCSI_ERR_PROTO;
+ break;
+ }
+ conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
+ goto recv_pdu;
+ case ISCSI_OP_LOGIN_RSP:
+ case ISCSI_OP_TEXT_RSP:
+ iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
+ /*
+ * login related PDU's exp_statsn is handled in
+ * userspace
+ */
+ goto recv_pdu;
+ case ISCSI_OP_SCSI_TMFUNC_RSP:
+ iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
+ if (datalen) {
+ rc = ISCSI_ERR_PROTO;
+ break;
+ }
+
+ iscsi_tmf_rsp(conn, hdr);
+ __iscsi_put_task(task);
+ break;
+ case ISCSI_OP_NOOP_IN:
+ iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);
+ if (hdr->ttt != cpu_to_be32(ISCSI_RESERVED_TAG) || datalen) {
+ rc = ISCSI_ERR_PROTO;
+ break;
+ }
+ conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
+
+ if (conn->ping_task != task)
+ /*
+ * If this is not in response to one of our
+ * nops then it must be from userspace.
+ */
+ goto recv_pdu;
+
+ mod_timer(&conn->transport_timer, jiffies + conn->recv_timeout);
+ __iscsi_put_task(task);
+ break;
+ default:
+ rc = ISCSI_ERR_BAD_OPCODE;
+ break;
+ }
+
+out:
+ return rc;
+recv_pdu:
+ if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))
+ rc = ISCSI_ERR_CONN_FAILED;
+ __iscsi_put_task(task);
return rc;
}
+EXPORT_SYMBOL_GPL(__iscsi_complete_pdu);
int iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
char *data, int datalen)
@@ -784,51 +896,63 @@ int iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
}
EXPORT_SYMBOL_GPL(iscsi_complete_pdu);
-/* verify itt (itt encoding: age+cid+itt) */
-int iscsi_verify_itt(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
- uint32_t *ret_itt)
+int iscsi_verify_itt(struct iscsi_conn *conn, itt_t itt)
{
struct iscsi_session *session = conn->session;
- struct iscsi_cmd_task *ctask;
- uint32_t itt;
+ uint32_t i;
- if (hdr->itt != RESERVED_ITT) {
- if (((__force u32)hdr->itt & ISCSI_AGE_MASK) !=
- (session->age << ISCSI_AGE_SHIFT)) {
- iscsi_conn_printk(KERN_ERR, conn,
- "received itt %x expected session "
- "age (%x)\n", (__force u32)hdr->itt,
- session->age & ISCSI_AGE_MASK);
- return ISCSI_ERR_BAD_ITT;
- }
+ if (itt == RESERVED_ITT)
+ return 0;
- itt = get_itt(hdr->itt);
- } else
- itt = ~0U;
+ if (((__force u32)itt & ISCSI_AGE_MASK) !=
+ (session->age << ISCSI_AGE_SHIFT)) {
+ iscsi_conn_printk(KERN_ERR, conn,
+ "received itt %x expected session age (%x)\n",
+ (__force u32)itt, session->age);
+ return ISCSI_ERR_BAD_ITT;
+ }
- if (itt < session->cmds_max) {
- ctask = session->cmds[itt];
+ i = get_itt(itt);
+ if (i >= session->cmds_max) {
+ iscsi_conn_printk(KERN_ERR, conn,
+ "received invalid itt index %u (max cmds "
+ "%u.\n", i, session->cmds_max);
+ return ISCSI_ERR_BAD_ITT;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iscsi_verify_itt);
- if (!ctask->sc) {
- iscsi_conn_printk(KERN_INFO, conn, "dropping ctask "
- "with itt 0x%x\n", ctask->itt);
- /* force drop */
- return ISCSI_ERR_NO_SCSI_CMD;
- }
+/**
+ * iscsi_itt_to_ctask - look up ctask by itt
+ * @conn: iscsi connection
+ * @itt: itt
+ *
+ * This should be used for cmd tasks.
+ *
+ * The session lock must be held.
+ */
+struct iscsi_task *iscsi_itt_to_ctask(struct iscsi_conn *conn, itt_t itt)
+{
+ struct iscsi_task *task;
- if (ctask->sc->SCp.phase != session->age) {
- iscsi_conn_printk(KERN_ERR, conn,
- "iscsi: ctask's session age %d, "
- "expected %d\n", ctask->sc->SCp.phase,
- session->age);
- return ISCSI_ERR_SESSION_FAILED;
- }
+ if (iscsi_verify_itt(conn, itt))
+ return NULL;
+
+ task = iscsi_itt_to_task(conn, itt);
+ if (!task || !task->sc)
+ return NULL;
+
+ if (task->sc->SCp.phase != conn->session->age) {
+ iscsi_session_printk(KERN_ERR, conn->session,
+ "task's session age %d, expected %d\n",
+ task->sc->SCp.phase, conn->session->age);
+ return NULL;
}
- *ret_itt = itt;
- return 0;
+ return task;
}
-EXPORT_SYMBOL_GPL(iscsi_verify_itt);
+EXPORT_SYMBOL_GPL(iscsi_itt_to_ctask);
void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err)
{
@@ -850,61 +974,6 @@ void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err)
}
EXPORT_SYMBOL_GPL(iscsi_conn_failure);
-static void iscsi_prep_mtask(struct iscsi_conn *conn,
- struct iscsi_mgmt_task *mtask)
-{
- struct iscsi_session *session = conn->session;
- struct iscsi_hdr *hdr = mtask->hdr;
- struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr;
-
- if (hdr->opcode != (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) &&
- hdr->opcode != (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE))
- nop->exp_statsn = cpu_to_be32(conn->exp_statsn);
- /*
- * pre-format CmdSN for outgoing PDU.
- */
- nop->cmdsn = cpu_to_be32(session->cmdsn);
- if (hdr->itt != RESERVED_ITT) {
- hdr->itt = build_itt(mtask->itt, session->age);
- /*
- * TODO: We always use immediate, so we never hit this.
- * If we start to send tmfs or nops as non-immediate then
- * we should start checking the cmdsn numbers for mgmt tasks.
- */
- if (conn->c_stage == ISCSI_CONN_STARTED &&
- !(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
- session->queued_cmdsn++;
- session->cmdsn++;
- }
- }
-
- if (session->tt->init_mgmt_task)
- session->tt->init_mgmt_task(conn, mtask);
-
- debug_scsi("mgmtpdu [op 0x%x hdr->itt 0x%x datalen %d]\n",
- hdr->opcode & ISCSI_OPCODE_MASK, hdr->itt,
- mtask->data_count);
-}
-
-static int iscsi_xmit_mtask(struct iscsi_conn *conn)
-{
- struct iscsi_hdr *hdr = conn->mtask->hdr;
- int rc;
-
- if ((hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT)
- conn->session->state = ISCSI_STATE_LOGGING_OUT;
- spin_unlock_bh(&conn->session->lock);
-
- rc = conn->session->tt->xmit_mgmt_task(conn, conn->mtask);
- spin_lock_bh(&conn->session->lock);
- if (rc)
- return rc;
-
- /* done with this in-progress mtask */
- conn->mtask = NULL;
- return 0;
-}
-
static int iscsi_check_cmdsn_window_closed(struct iscsi_conn *conn)
{
struct iscsi_session *session = conn->session;
@@ -922,37 +991,38 @@ static int iscsi_check_cmdsn_window_closed(struct iscsi_conn *conn)
return 0;
}
-static int iscsi_xmit_ctask(struct iscsi_conn *conn)
+static int iscsi_xmit_task(struct iscsi_conn *conn)
{
- struct iscsi_cmd_task *ctask = conn->ctask;
+ struct iscsi_task *task = conn->task;
int rc;
- __iscsi_get_ctask(ctask);
+ __iscsi_get_task(task);
spin_unlock_bh(&conn->session->lock);
- rc = conn->session->tt->xmit_cmd_task(conn, ctask);
+ rc = conn->session->tt->xmit_task(task);
spin_lock_bh(&conn->session->lock);
- __iscsi_put_ctask(ctask);
+ __iscsi_put_task(task);
if (!rc)
- /* done with this ctask */
- conn->ctask = NULL;
+ /* done with this task */
+ conn->task = NULL;
return rc;
}
/**
- * iscsi_requeue_ctask - requeue ctask to run from session workqueue
- * @ctask: ctask to requeue
+ * iscsi_requeue_task - requeue task to run from session workqueue
+ * @task: task to requeue
*
- * LLDs that need to run a ctask from the session workqueue should call
- * this. The session lock must be held.
+ * LLDs that need to run a task from the session workqueue should call
+ * this. The session lock must be held. This should only be called
+ * by software drivers.
*/
-void iscsi_requeue_ctask(struct iscsi_cmd_task *ctask)
+void iscsi_requeue_task(struct iscsi_task *task)
{
- struct iscsi_conn *conn = ctask->conn;
+ struct iscsi_conn *conn = task->conn;
- list_move_tail(&ctask->running, &conn->requeue);
+ list_move_tail(&task->running, &conn->requeue);
scsi_queue_work(conn->session->host, &conn->xmitwork);
}
-EXPORT_SYMBOL_GPL(iscsi_requeue_ctask);
+EXPORT_SYMBOL_GPL(iscsi_requeue_task);
/**
* iscsi_data_xmit - xmit any command into the scheduled connection
@@ -974,14 +1044,8 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
return -ENODATA;
}
- if (conn->ctask) {
- rc = iscsi_xmit_ctask(conn);
- if (rc)
- goto again;
- }
-
- if (conn->mtask) {
- rc = iscsi_xmit_mtask(conn);
+ if (conn->task) {
+ rc = iscsi_xmit_task(conn);
if (rc)
goto again;
}
@@ -993,17 +1057,14 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
*/
check_mgmt:
while (!list_empty(&conn->mgmtqueue)) {
- conn->mtask = list_entry(conn->mgmtqueue.next,
- struct iscsi_mgmt_task, running);
- if (conn->session->state == ISCSI_STATE_LOGGING_OUT) {
- iscsi_free_mgmt_task(conn, conn->mtask);
- conn->mtask = NULL;
+ conn->task = list_entry(conn->mgmtqueue.next,
+ struct iscsi_task, running);
+ if (iscsi_prep_mgmt_task(conn, conn->task)) {
+ __iscsi_put_task(conn->task);
+ conn->task = NULL;
continue;
}
-
- iscsi_prep_mtask(conn, conn->mtask);
- list_move_tail(conn->mgmtqueue.next, &conn->mgmt_run_list);
- rc = iscsi_xmit_mtask(conn);
+ rc = iscsi_xmit_task(conn);
if (rc)
goto again;
}
@@ -1013,24 +1074,21 @@ check_mgmt:
if (conn->tmf_state == TMF_QUEUED)
break;
- conn->ctask = list_entry(conn->xmitqueue.next,
- struct iscsi_cmd_task, running);
+ conn->task = list_entry(conn->xmitqueue.next,
+ struct iscsi_task, running);
if (conn->session->state == ISCSI_STATE_LOGGING_OUT) {
- fail_command(conn, conn->ctask, DID_IMM_RETRY << 16);
+ fail_command(conn, conn->task, DID_IMM_RETRY << 16);
continue;
}
- if (iscsi_prep_scsi_cmd_pdu(conn->ctask)) {
- fail_command(conn, conn->ctask, DID_ABORT << 16);
+ if (iscsi_prep_scsi_cmd_pdu(conn->task)) {
+ fail_command(conn, conn->task, DID_ABORT << 16);
continue;
}
-
- conn->ctask->state = ISCSI_TASK_RUNNING;
- list_move_tail(conn->xmitqueue.next, &conn->run_list);
- rc = iscsi_xmit_ctask(conn);
+ rc = iscsi_xmit_task(conn);
if (rc)
goto again;
/*
- * we could continuously get new ctask requests so
+ * we could continuously get new task requests so
* we need to check the mgmt queue for nops that need to
* be sent to aviod starvation
*/
@@ -1048,11 +1106,11 @@ check_mgmt:
if (conn->session->state == ISCSI_STATE_LOGGING_OUT)
break;
- conn->ctask = list_entry(conn->requeue.next,
- struct iscsi_cmd_task, running);
- conn->ctask->state = ISCSI_TASK_RUNNING;
+ conn->task = list_entry(conn->requeue.next,
+ struct iscsi_task, running);
+ conn->task->state = ISCSI_TASK_RUNNING;
list_move_tail(conn->requeue.next, &conn->run_list);
- rc = iscsi_xmit_ctask(conn);
+ rc = iscsi_xmit_task(conn);
if (rc)
goto again;
if (!list_empty(&conn->mgmtqueue))
@@ -1096,11 +1154,12 @@ enum {
int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
{
+ struct iscsi_cls_session *cls_session;
struct Scsi_Host *host;
int reason = 0;
struct iscsi_session *session;
struct iscsi_conn *conn;
- struct iscsi_cmd_task *ctask = NULL;
+ struct iscsi_task *task = NULL;
sc->scsi_done = done;
sc->result = 0;
@@ -1109,10 +1168,11 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
host = sc->device->host;
spin_unlock(host->host_lock);
- session = iscsi_hostdata(host->hostdata);
+ cls_session = starget_to_session(scsi_target(sc->device));
+ session = cls_session->dd_data;
spin_lock(&session->lock);
- reason = iscsi_session_chkready(session_to_cls(session));
+ reason = iscsi_session_chkready(cls_session);
if (reason) {
sc->result = reason;
goto fault;
@@ -1167,26 +1227,39 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
goto reject;
}
- if (!__kfifo_get(session->cmdpool.queue, (void*)&ctask,
+ if (!__kfifo_get(session->cmdpool.queue, (void*)&task,
sizeof(void*))) {
reason = FAILURE_OOM;
goto reject;
}
- session->queued_cmdsn++;
-
sc->SCp.phase = session->age;
- sc->SCp.ptr = (char *)ctask;
-
- atomic_set(&ctask->refcount, 1);
- ctask->state = ISCSI_TASK_PENDING;
- ctask->conn = conn;
- ctask->sc = sc;
- INIT_LIST_HEAD(&ctask->running);
+ sc->SCp.ptr = (char *)task;
+
+ atomic_set(&task->refcount, 1);
+ task->state = ISCSI_TASK_PENDING;
+ task->conn = conn;
+ task->sc = sc;
+ INIT_LIST_HEAD(&task->running);
+ list_add_tail(&task->running, &conn->xmitqueue);
+
+ if (session->tt->caps & CAP_DATA_PATH_OFFLOAD) {
+ if (iscsi_prep_scsi_cmd_pdu(task)) {
+ sc->result = DID_ABORT << 16;
+ sc->scsi_done = NULL;
+ iscsi_complete_command(task);
+ goto fault;
+ }
+ if (session->tt->xmit_task(task)) {
+ sc->scsi_done = NULL;
+ iscsi_complete_command(task);
+ reason = FAILURE_SESSION_NOT_READY;
+ goto reject;
+ }
+ } else
+ scsi_queue_work(session->host, &conn->xmitwork);
- list_add_tail(&ctask->running, &conn->xmitqueue);
+ session->queued_cmdsn++;
spin_unlock(&session->lock);
-
- scsi_queue_work(host, &conn->xmitwork);
spin_lock(host->host_lock);
return 0;
@@ -1205,7 +1278,7 @@ fault:
scsi_out(sc)->resid = scsi_out(sc)->length;
scsi_in(sc)->resid = scsi_in(sc)->length;
}
- sc->scsi_done(sc);
+ done(sc);
spin_lock(host->host_lock);
return 0;
}
@@ -1222,7 +1295,7 @@ EXPORT_SYMBOL_GPL(iscsi_change_queue_depth);
void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session)
{
- struct iscsi_session *session = class_to_transport_session(cls_session);
+ struct iscsi_session *session = cls_session->dd_data;
spin_lock_bh(&session->lock);
if (session->state != ISCSI_STATE_LOGGED_IN) {
@@ -1236,9 +1309,13 @@ EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout);
int iscsi_eh_host_reset(struct scsi_cmnd *sc)
{
- struct Scsi_Host *host = sc->device->host;
- struct iscsi_session *session = iscsi_hostdata(host->hostdata);
- struct iscsi_conn *conn = session->leadconn;
+ struct iscsi_cls_session *cls_session;
+ struct iscsi_session *session;
+ struct iscsi_conn *conn;
+
+ cls_session = starget_to_session(scsi_target(sc->device));
+ session = cls_session->dd_data;
+ conn = session->leadconn;
mutex_lock(&session->eh_mutex);
spin_lock_bh(&session->lock);
@@ -1300,11 +1377,11 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
int timeout)
{
struct iscsi_session *session = conn->session;
- struct iscsi_mgmt_task *mtask;
+ struct iscsi_task *task;
- mtask = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)hdr,
+ task = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)hdr,
NULL, 0);
- if (!mtask) {
+ if (!task) {
spin_unlock_bh(&session->lock);
iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
spin_lock_bh(&session->lock);
@@ -1320,7 +1397,6 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
spin_unlock_bh(&session->lock);
mutex_unlock(&session->eh_mutex);
- scsi_queue_work(session->host, &conn->xmitwork);
/*
* block eh thread until:
@@ -1339,7 +1415,7 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
mutex_lock(&session->eh_mutex);
spin_lock_bh(&session->lock);
- /* if the session drops it will clean up the mtask */
+ /* if the session drops it will clean up the task */
if (age != session->age ||
session->state != ISCSI_STATE_LOGGED_IN)
return -ENOTCONN;
@@ -1353,48 +1429,51 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn,
static void fail_all_commands(struct iscsi_conn *conn, unsigned lun,
int error)
{
- struct iscsi_cmd_task *ctask, *tmp;
+ struct iscsi_task *task, *tmp;
- if (conn->ctask && (conn->ctask->sc->device->lun == lun || lun == -1))
- conn->ctask = NULL;
+ if (conn->task && (conn->task->sc->device->lun == lun || lun == -1))
+ conn->task = NULL;
/* flush pending */
- list_for_each_entry_safe(ctask, tmp, &conn->xmitqueue, running) {
- if (lun == ctask->sc->device->lun || lun == -1) {
+ list_for_each_entry_safe(task, tmp, &conn->xmitqueue, running) {
+ if (lun == task->sc->device->lun || lun == -1) {
debug_scsi("failing pending sc %p itt 0x%x\n",
- ctask->sc, ctask->itt);
- fail_command(conn, ctask, error << 16);
+ task->sc, task->itt);
+ fail_command(conn, task, error << 16);
}
}
- list_for_each_entry_safe(ctask, tmp, &conn->requeue, running) {
- if (lun == ctask->sc->device->lun || lun == -1) {
+ list_for_each_entry_safe(task, tmp, &conn->requeue, running) {
+ if (lun == task->sc->device->lun || lun == -1) {
debug_scsi("failing requeued sc %p itt 0x%x\n",
- ctask->sc, ctask->itt);
- fail_command(conn, ctask, error << 16);
+ task->sc, task->itt);
+ fail_command(conn, task, error << 16);
}
}
/* fail all other running */
- list_for_each_entry_safe(ctask, tmp, &conn->run_list, running) {
- if (lun == ctask->sc->device->lun || lun == -1) {
+ list_for_each_entry_safe(task, tmp, &conn->run_list, running) {
+ if (lun == task->sc->device->lun || lun == -1) {
debug_scsi("failing in progress sc %p itt 0x%x\n",
- ctask->sc, ctask->itt);
- fail_command(conn, ctask, DID_BUS_BUSY << 16);
+ task->sc, task->itt);
+ fail_command(conn, task, DID_BUS_BUSY << 16);
}
}
}
-static void iscsi_suspend_tx(struct iscsi_conn *conn)
+void iscsi_suspend_tx(struct iscsi_conn *conn)
{
set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
- scsi_flush_work(conn->session->host);
+ if (!(conn->session->tt->caps & CAP_DATA_PATH_OFFLOAD))
+ scsi_flush_work(conn->session->host);
}
+EXPORT_SYMBOL_GPL(iscsi_suspend_tx);
static void iscsi_start_tx(struct iscsi_conn *conn)
{
clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
- scsi_queue_work(conn->session->host, &conn->xmitwork);
+ if (!(conn->session->tt->caps & CAP_DATA_PATH_OFFLOAD))
+ scsi_queue_work(conn->session->host, &conn->xmitwork);
}
static enum scsi_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
@@ -1405,7 +1484,7 @@ static enum scsi_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
enum scsi_eh_timer_return rc = EH_NOT_HANDLED;
cls_session = starget_to_session(scsi_target(scmd->device));
- session = class_to_transport_session(cls_session);
+ session = cls_session->dd_data;
debug_scsi("scsi cmd %p timedout\n", scmd);
@@ -1443,7 +1522,7 @@ static enum scsi_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd)
jiffies))
rc = EH_RESET_TIMER;
/* if in the middle of checking the transport then give us more time */
- if (conn->ping_mtask)
+ if (conn->ping_task)
rc = EH_RESET_TIMER;
done:
spin_unlock(&session->lock);
@@ -1467,7 +1546,7 @@ static void iscsi_check_transport_timeouts(unsigned long data)
recv_timeout *= HZ;
last_recv = conn->last_recv;
- if (conn->ping_mtask &&
+ if (conn->ping_task &&
time_before_eq(conn->last_ping + (conn->ping_timeout * HZ),
jiffies)) {
iscsi_conn_printk(KERN_ERR, conn, "ping timeout of %d secs "
@@ -1493,27 +1572,30 @@ done:
spin_unlock(&session->lock);
}
-static void iscsi_prep_abort_task_pdu(struct iscsi_cmd_task *ctask,
+static void iscsi_prep_abort_task_pdu(struct iscsi_task *task,
struct iscsi_tm *hdr)
{
memset(hdr, 0, sizeof(*hdr));
hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE;
hdr->flags = ISCSI_TM_FUNC_ABORT_TASK & ISCSI_FLAG_TM_FUNC_MASK;
hdr->flags |= ISCSI_FLAG_CMD_FINAL;
- memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun));
- hdr->rtt = ctask->hdr->itt;
- hdr->refcmdsn = ctask->hdr->cmdsn;
+ memcpy(hdr->lun, task->hdr->lun, sizeof(hdr->lun));
+ hdr->rtt = task->hdr->itt;
+ hdr->refcmdsn = task->hdr->cmdsn;
}
int iscsi_eh_abort(struct scsi_cmnd *sc)
{
- struct Scsi_Host *host = sc->device->host;
- struct iscsi_session *session = iscsi_hostdata(host->hostdata);
+ struct iscsi_cls_session *cls_session;
+ struct iscsi_session *session;
struct iscsi_conn *conn;
- struct iscsi_cmd_task *ctask;
+ struct iscsi_task *task;
struct iscsi_tm *hdr;
int rc, age;
+ cls_session = starget_to_session(scsi_target(sc->device));
+ session = cls_session->dd_data;
+
mutex_lock(&session->eh_mutex);
spin_lock_bh(&session->lock);
/*
@@ -1542,17 +1624,17 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
conn->eh_abort_cnt++;
age = session->age;
- ctask = (struct iscsi_cmd_task *)sc->SCp.ptr;
- debug_scsi("aborting [sc %p itt 0x%x]\n", sc, ctask->itt);
+ task = (struct iscsi_task *)sc->SCp.ptr;
+ debug_scsi("aborting [sc %p itt 0x%x]\n", sc, task->itt);
- /* ctask completed before time out */
- if (!ctask->sc) {
+ /* task completed before time out */
+ if (!task->sc) {
debug_scsi("sc completed while abort in progress\n");
goto success;
}
- if (ctask->state == ISCSI_TASK_PENDING) {
- fail_command(conn, ctask, DID_ABORT << 16);
+ if (task->state == ISCSI_TASK_PENDING) {
+ fail_command(conn, task, DID_ABORT << 16);
goto success;
}
@@ -1562,7 +1644,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
conn->tmf_state = TMF_QUEUED;
hdr = &conn->tmhdr;
- iscsi_prep_abort_task_pdu(ctask, hdr);
+ iscsi_prep_abort_task_pdu(task, hdr);
if (iscsi_exec_task_mgmt_fn(conn, hdr, age, session->abort_timeout)) {
rc = FAILED;
@@ -1572,16 +1654,20 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
switch (conn->tmf_state) {
case TMF_SUCCESS:
spin_unlock_bh(&session->lock);
+ /*
+ * stop tx side incase the target had sent a abort rsp but
+ * the initiator was still writing out data.
+ */
iscsi_suspend_tx(conn);
/*
- * clean up task if aborted. grab the recv lock as a writer
+ * we do not stop the recv side because targets have been
+ * good and have never sent us a successful tmf response
+ * then sent more data for the cmd.
*/
- write_lock_bh(conn->recv_lock);
spin_lock(&session->lock);
- fail_command(conn, ctask, DID_ABORT << 16);
+ fail_command(conn, task, DID_ABORT << 16);
conn->tmf_state = TMF_INITIAL;
spin_unlock(&session->lock);
- write_unlock_bh(conn->recv_lock);
iscsi_start_tx(conn);
goto success_unlocked;
case TMF_TIMEDOUT:
@@ -1591,7 +1677,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
case TMF_NOT_FOUND:
if (!sc->SCp.ptr) {
conn->tmf_state = TMF_INITIAL;
- /* ctask completed before tmf abort response */
+ /* task completed before tmf abort response */
debug_scsi("sc completed while abort in progress\n");
goto success;
}
@@ -1604,7 +1690,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc)
success:
spin_unlock_bh(&session->lock);
success_unlocked:
- debug_scsi("abort success [sc %lx itt 0x%x]\n", (long)sc, ctask->itt);
+ debug_scsi("abort success [sc %lx itt 0x%x]\n", (long)sc, task->itt);
mutex_unlock(&session->eh_mutex);
return SUCCESS;
@@ -1612,7 +1698,7 @@ failed:
spin_unlock_bh(&session->lock);
failed_unlocked:
debug_scsi("abort failed [sc %p itt 0x%x]\n", sc,
- ctask ? ctask->itt : 0);
+ task ? task->itt : 0);
mutex_unlock(&session->eh_mutex);
return FAILED;
}
@@ -1630,12 +1716,15 @@ static void iscsi_prep_lun_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr)
int iscsi_eh_device_reset(struct scsi_cmnd *sc)
{
- struct Scsi_Host *host = sc->device->host;
- struct iscsi_session *session = iscsi_hostdata(host->hostdata);
+ struct iscsi_cls_session *cls_session;
+ struct iscsi_session *session;
struct iscsi_conn *conn;
struct iscsi_tm *hdr;
int rc = FAILED;
+ cls_session = starget_to_session(scsi_target(sc->device));
+ session = cls_session->dd_data;
+
debug_scsi("LU Reset [sc %p lun %u]\n", sc, sc->device->lun);
mutex_lock(&session->eh_mutex);
@@ -1678,13 +1767,11 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc)
spin_unlock_bh(&session->lock);
iscsi_suspend_tx(conn);
- /* need to grab the recv lock then session lock */
- write_lock_bh(conn->recv_lock);
+
spin_lock(&session->lock);
fail_all_commands(conn, sc->device->lun, DID_ERROR);
conn->tmf_state = TMF_INITIAL;
spin_unlock(&session->lock);
- write_unlock_bh(conn->recv_lock);
iscsi_start_tx(conn);
goto done;
@@ -1760,177 +1847,197 @@ void iscsi_pool_free(struct iscsi_pool *q)
}
EXPORT_SYMBOL_GPL(iscsi_pool_free);
-/*
- * iSCSI Session's hostdata organization:
+/**
+ * iscsi_host_add - add host to system
+ * @shost: scsi host
+ * @pdev: parent device
*
- * *------------------* <== hostdata_session(host->hostdata)
- * | ptr to class sess|
- * |------------------| <== iscsi_hostdata(host->hostdata)
- * | iscsi_session |
- * *------------------*
+ * This should be called by partial offload and software iscsi drivers
+ * to add a host to the system.
*/
+int iscsi_host_add(struct Scsi_Host *shost, struct device *pdev)
+{
+ return scsi_add_host(shost, pdev);
+}
+EXPORT_SYMBOL_GPL(iscsi_host_add);
-#define hostdata_privsize(_sz) (sizeof(unsigned long) + _sz + \
- _sz % sizeof(unsigned long))
+/**
+ * iscsi_host_alloc - allocate a host and driver data
+ * @sht: scsi host template
+ * @dd_data_size: driver host data size
+ * @qdepth: default device queue depth
+ *
+ * This should be called by partial offload and software iscsi drivers.
+ * To access the driver specific memory use the iscsi_host_priv() macro.
+ */
+struct Scsi_Host *iscsi_host_alloc(struct scsi_host_template *sht,
+ int dd_data_size, uint16_t qdepth)
+{
+ struct Scsi_Host *shost;
-#define hostdata_session(_hostdata) (iscsi_ptr(*(unsigned long *)_hostdata))
+ shost = scsi_host_alloc(sht, sizeof(struct iscsi_host) + dd_data_size);
+ if (!shost)
+ return NULL;
+ shost->transportt->eh_timed_out = iscsi_eh_cmd_timed_out;
+
+ if (qdepth > ISCSI_MAX_CMD_PER_LUN || qdepth < 1) {
+ if (qdepth != 0)
+ printk(KERN_ERR "iscsi: invalid queue depth of %d. "
+ "Queue depth must be between 1 and %d.\n",
+ qdepth, ISCSI_MAX_CMD_PER_LUN);
+ qdepth = ISCSI_DEF_CMD_PER_LUN;
+ }
+ shost->cmd_per_lun = qdepth;
+ return shost;
+}
+EXPORT_SYMBOL_GPL(iscsi_host_alloc);
+
+/**
+ * iscsi_host_remove - remove host and sessions
+ * @shost: scsi host
+ *
+ * This will also remove any sessions attached to the host, but if userspace
+ * is managing the session at the same time this will break. TODO: add
+ * refcounting to the netlink iscsi interface so a rmmod or host hot unplug
+ * does not remove the memory from under us.
+ */
+void iscsi_host_remove(struct Scsi_Host *shost)
+{
+ iscsi_host_for_each_session(shost, iscsi_session_teardown);
+ scsi_remove_host(shost);
+}
+EXPORT_SYMBOL_GPL(iscsi_host_remove);
+
+void iscsi_host_free(struct Scsi_Host *shost)
+{
+ struct iscsi_host *ihost = shost_priv(shost);
+
+ kfree(ihost->netdev);
+ kfree(ihost->hwaddress);
+ kfree(ihost->initiatorname);
+ scsi_host_put(shost);
+}
+EXPORT_SYMBOL_GPL(iscsi_host_free);
/**
* iscsi_session_setup - create iscsi cls session and host and session
- * @scsit: scsi transport template
* @iscsit: iscsi transport template
- * @cmds_max: scsi host can queue
- * @qdepth: scsi host cmds per lun
- * @cmd_task_size: LLD ctask private data size
- * @mgmt_task_size: LLD mtask private data size
+ * @shost: scsi host
+ * @cmds_max: session can queue
+ * @cmd_task_size: LLD task private data size
* @initial_cmdsn: initial CmdSN
- * @hostno: host no allocated
*
* This can be used by software iscsi_transports that allocate
* a session per scsi host.
- **/
+ *
+ * Callers should set cmds_max to the largest total numer (mgmt + scsi) of
+ * tasks they support. The iscsi layer reserves ISCSI_MGMT_CMDS_MAX tasks
+ * for nop handling and login/logout requests.
+ */
struct iscsi_cls_session *
-iscsi_session_setup(struct iscsi_transport *iscsit,
- struct scsi_transport_template *scsit,
- uint16_t cmds_max, uint16_t qdepth,
- int cmd_task_size, int mgmt_task_size,
- uint32_t initial_cmdsn, uint32_t *hostno)
+iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost,
+ uint16_t cmds_max, int cmd_task_size,
+ uint32_t initial_cmdsn, unsigned int id)
{
- struct Scsi_Host *shost;
struct iscsi_session *session;
struct iscsi_cls_session *cls_session;
- int cmd_i;
+ int cmd_i, scsi_cmds, total_cmds = cmds_max;
+ /*
+ * The iscsi layer needs some tasks for nop handling and tmfs,
+ * so the cmds_max must at least be greater than ISCSI_MGMT_CMDS_MAX
+ * + 1 command for scsi IO.
+ */
+ if (total_cmds < ISCSI_TOTAL_CMDS_MIN) {
+ printk(KERN_ERR "iscsi: invalid can_queue of %d. can_queue "
+ "must be a power of two that is at least %d.\n",
+ total_cmds, ISCSI_TOTAL_CMDS_MIN);
+ return NULL;
+ }
- if (qdepth > ISCSI_MAX_CMD_PER_LUN || qdepth < 1) {
- if (qdepth != 0)
- printk(KERN_ERR "iscsi: invalid queue depth of %d. "
- "Queue depth must be between 1 and %d.\n",
- qdepth, ISCSI_MAX_CMD_PER_LUN);
- qdepth = ISCSI_DEF_CMD_PER_LUN;
+ if (total_cmds > ISCSI_TOTAL_CMDS_MAX) {
+ printk(KERN_ERR "iscsi: invalid can_queue of %d. can_queue "
+ "must be a power of 2 less than or equal to %d.\n",
+ cmds_max, ISCSI_TOTAL_CMDS_MAX);
+ total_cmds = ISCSI_TOTAL_CMDS_MAX;
}
- if (!is_power_of_2(cmds_max) || cmds_max >= ISCSI_MGMT_ITT_OFFSET ||
- cmds_max < 2) {
- if (cmds_max != 0)
- printk(KERN_ERR "iscsi: invalid can_queue of %d. "
- "can_queue must be a power of 2 and between "
- "2 and %d - setting to %d.\n", cmds_max,
- ISCSI_MGMT_ITT_OFFSET, ISCSI_DEF_XMIT_CMDS_MAX);
- cmds_max = ISCSI_DEF_XMIT_CMDS_MAX;
+ if (!is_power_of_2(total_cmds)) {
+ printk(KERN_ERR "iscsi: invalid can_queue of %d. can_queue "
+ "must be a power of 2.\n", total_cmds);
+ total_cmds = rounddown_pow_of_two(total_cmds);
+ if (total_cmds < ISCSI_TOTAL_CMDS_MIN)
+ return NULL;
+ printk(KERN_INFO "iscsi: Rounding can_queue to %d.\n",
+ total_cmds);
}
+ scsi_cmds = total_cmds - ISCSI_MGMT_CMDS_MAX;
- shost = scsi_host_alloc(iscsit->host_template,
- hostdata_privsize(sizeof(*session)));
- if (!shost)
+ cls_session = iscsi_alloc_session(shost, iscsit,
+ sizeof(struct iscsi_session));
+ if (!cls_session)
return NULL;
-
- /* the iscsi layer takes one task for reserve */
- shost->can_queue = cmds_max - 1;
- shost->cmd_per_lun = qdepth;
- shost->max_id = 1;
- shost->max_channel = 0;
- shost->max_lun = iscsit->max_lun;
- shost->max_cmd_len = iscsit->max_cmd_len;
- shost->transportt = scsit;
- shost->transportt->create_work_queue = 1;
- shost->transportt->eh_timed_out = iscsi_eh_cmd_timed_out;
- *hostno = shost->host_no;
-
- session = iscsi_hostdata(shost->hostdata);
- memset(session, 0, sizeof(struct iscsi_session));
+ session = cls_session->dd_data;
+ session->cls_session = cls_session;
session->host = shost;
session->state = ISCSI_STATE_FREE;
session->fast_abort = 1;
session->lu_reset_timeout = 15;
session->abort_timeout = 10;
- session->mgmtpool_max = ISCSI_MGMT_CMDS_MAX;
- session->cmds_max = cmds_max;
+ session->scsi_cmds_max = scsi_cmds;
+ session->cmds_max = total_cmds;
session->queued_cmdsn = session->cmdsn = initial_cmdsn;
session->exp_cmdsn = initial_cmdsn + 1;
session->max_cmdsn = initial_cmdsn + 1;
session->max_r2t = 1;
session->tt = iscsit;
mutex_init(&session->eh_mutex);
+ spin_lock_init(&session->lock);
/* initialize SCSI PDU commands pool */
if (iscsi_pool_init(&session->cmdpool, session->cmds_max,
(void***)&session->cmds,
- cmd_task_size + sizeof(struct iscsi_cmd_task)))
+ cmd_task_size + sizeof(struct iscsi_task)))
goto cmdpool_alloc_fail;
/* pre-format cmds pool with ITT */
for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) {
- struct iscsi_cmd_task *ctask = session->cmds[cmd_i];
+ struct iscsi_task *task = session->cmds[cmd_i];
if (cmd_task_size)
- ctask->dd_data = &ctask[1];
- ctask->itt = cmd_i;
- INIT_LIST_HEAD(&ctask->running);
- }
-
- spin_lock_init(&session->lock);
-
- /* initialize immediate command pool */
- if (iscsi_pool_init(&session->mgmtpool, session->mgmtpool_max,
- (void***)&session->mgmt_cmds,
- mgmt_task_size + sizeof(struct iscsi_mgmt_task)))
- goto mgmtpool_alloc_fail;
-
-
- /* pre-format immediate cmds pool with ITT */
- for (cmd_i = 0; cmd_i < session->mgmtpool_max; cmd_i++) {
- struct iscsi_mgmt_task *mtask = session->mgmt_cmds[cmd_i];
-
- if (mgmt_task_size)
- mtask->dd_data = &mtask[1];
- mtask->itt = ISCSI_MGMT_ITT_OFFSET + cmd_i;
- INIT_LIST_HEAD(&mtask->running);
+ task->dd_data = &task[1];
+ task->itt = cmd_i;
+ INIT_LIST_HEAD(&task->running);
}
- if (scsi_add_host(shost, NULL))
- goto add_host_fail;
-
if (!try_module_get(iscsit->owner))
- goto cls_session_fail;
-
- cls_session = iscsi_create_session(shost, iscsit, 0);
- if (!cls_session)
- goto module_put;
- *(unsigned long*)shost->hostdata = (unsigned long)cls_session;
+ goto module_get_fail;
+ if (iscsi_add_session(cls_session, id))
+ goto cls_session_fail;
return cls_session;
-module_put:
- module_put(iscsit->owner);
cls_session_fail:
- scsi_remove_host(shost);
-add_host_fail:
- iscsi_pool_free(&session->mgmtpool);
-mgmtpool_alloc_fail:
+ module_put(iscsit->owner);
+module_get_fail:
iscsi_pool_free(&session->cmdpool);
cmdpool_alloc_fail:
- scsi_host_put(shost);
+ iscsi_free_session(cls_session);
return NULL;
}
EXPORT_SYMBOL_GPL(iscsi_session_setup);
/**
* iscsi_session_teardown - destroy session, host, and cls_session
- * shost: scsi host
+ * @cls_session: iscsi session
*
- * This can be used by software iscsi_transports that allocate
- * a session per scsi host.
- **/
+ * The driver must have called iscsi_remove_session before
+ * calling this.
+ */
void iscsi_session_teardown(struct iscsi_cls_session *cls_session)
{
- struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
- struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+ struct iscsi_session *session = cls_session->dd_data;
struct module *owner = cls_session->transport->owner;
- iscsi_remove_session(cls_session);
- scsi_remove_host(shost);
-
- iscsi_pool_free(&session->mgmtpool);
iscsi_pool_free(&session->cmdpool);
kfree(session->password);
@@ -1938,12 +2045,10 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session)
kfree(session->username);
kfree(session->username_in);
kfree(session->targetname);
- kfree(session->netdev);
- kfree(session->hwaddress);
kfree(session->initiatorname);
+ kfree(session->ifacename);
- iscsi_free_session(cls_session);
- scsi_host_put(shost);
+ iscsi_destroy_session(cls_session);
module_put(owner);
}
EXPORT_SYMBOL_GPL(iscsi_session_teardown);
@@ -1951,22 +2056,26 @@ EXPORT_SYMBOL_GPL(iscsi_session_teardown);
/**
* iscsi_conn_setup - create iscsi_cls_conn and iscsi_conn
* @cls_session: iscsi_cls_session
+ * @dd_size: private driver data size
* @conn_idx: cid
- **/
+ */
struct iscsi_cls_conn *
-iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
+iscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size,
+ uint32_t conn_idx)
{
- struct iscsi_session *session = class_to_transport_session(cls_session);
+ struct iscsi_session *session = cls_session->dd_data;
struct iscsi_conn *conn;
struct iscsi_cls_conn *cls_conn;
char *data;
- cls_conn = iscsi_create_conn(cls_session, conn_idx);
+ cls_conn = iscsi_create_conn(cls_session, sizeof(*conn) + dd_size,
+ conn_idx);
if (!cls_conn)
return NULL;
conn = cls_conn->dd_data;
- memset(conn, 0, sizeof(*conn));
+ memset(conn, 0, sizeof(*conn) + dd_size);
+ conn->dd_data = cls_conn->dd_data + sizeof(*conn);
conn->session = session;
conn->cls_conn = cls_conn;
conn->c_stage = ISCSI_CONN_INITIAL_STAGE;
@@ -1985,30 +2094,30 @@ iscsi_conn_setup(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
INIT_LIST_HEAD(&conn->requeue);
INIT_WORK(&conn->xmitwork, iscsi_xmitworker);
- /* allocate login_mtask used for the login/text sequences */
+ /* allocate login_task used for the login/text sequences */
spin_lock_bh(&session->lock);
- if (!__kfifo_get(session->mgmtpool.queue,
- (void*)&conn->login_mtask,
+ if (!__kfifo_get(session->cmdpool.queue,
+ (void*)&conn->login_task,
sizeof(void*))) {
spin_unlock_bh(&session->lock);
- goto login_mtask_alloc_fail;
+ goto login_task_alloc_fail;
}
spin_unlock_bh(&session->lock);
data = kmalloc(ISCSI_DEF_MAX_RECV_SEG_LEN, GFP_KERNEL);
if (!data)
- goto login_mtask_data_alloc_fail;
- conn->login_mtask->data = conn->data = data;
+ goto login_task_data_alloc_fail;
+ conn->login_task->data = conn->data = data;
init_timer(&conn->tmf_timer);
init_waitqueue_head(&conn->ehwait);
return cls_conn;
-login_mtask_data_alloc_fail:
- __kfifo_put(session->mgmtpool.queue, (void*)&conn->login_mtask,
+login_task_data_alloc_fail:
+ __kfifo_put(session->cmdpool.queue, (void*)&conn->login_task,
sizeof(void*));
-login_mtask_alloc_fail:
+login_task_alloc_fail:
iscsi_destroy_conn(cls_conn);
return NULL;
}
@@ -2068,7 +2177,7 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn)
spin_lock_bh(&session->lock);
kfree(conn->data);
kfree(conn->persistent_address);
- __kfifo_put(session->mgmtpool.queue, (void*)&conn->login_mtask,
+ __kfifo_put(session->cmdpool.queue, (void*)&conn->login_task,
sizeof(void*));
if (session->leadconn == conn)
session->leadconn = NULL;
@@ -2140,7 +2249,7 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
}
spin_unlock_bh(&session->lock);
- iscsi_unblock_session(session_to_cls(session));
+ iscsi_unblock_session(session->cls_session);
wake_up(&conn->ehwait);
return 0;
}
@@ -2149,21 +2258,23 @@ EXPORT_SYMBOL_GPL(iscsi_conn_start);
static void
flush_control_queues(struct iscsi_session *session, struct iscsi_conn *conn)
{
- struct iscsi_mgmt_task *mtask, *tmp;
+ struct iscsi_task *task, *tmp;
/* handle pending */
- list_for_each_entry_safe(mtask, tmp, &conn->mgmtqueue, running) {
- debug_scsi("flushing pending mgmt task itt 0x%x\n", mtask->itt);
- iscsi_free_mgmt_task(conn, mtask);
+ list_for_each_entry_safe(task, tmp, &conn->mgmtqueue, running) {
+ debug_scsi("flushing pending mgmt task itt 0x%x\n", task->itt);
+ /* release ref from prep task */
+ __iscsi_put_task(task);
}
/* handle running */
- list_for_each_entry_safe(mtask, tmp, &conn->mgmt_run_list, running) {
- debug_scsi("flushing running mgmt task itt 0x%x\n", mtask->itt);
- iscsi_free_mgmt_task(conn, mtask);
+ list_for_each_entry_safe(task, tmp, &conn->mgmt_run_list, running) {
+ debug_scsi("flushing running mgmt task itt 0x%x\n", task->itt);
+ /* release ref from prep task */
+ __iscsi_put_task(task);
}
- conn->mtask = NULL;
+ conn->task = NULL;
}
static void iscsi_start_session_recovery(struct iscsi_session *session,
@@ -2182,17 +2293,6 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
}
/*
- * The LLD either freed/unset the lock on us, or userspace called
- * stop but did not create a proper connection (connection was never
- * bound or it was unbound then stop was called).
- */
- if (!conn->recv_lock) {
- spin_unlock_bh(&session->lock);
- mutex_unlock(&session->eh_mutex);
- return;
- }
-
- /*
* When this is called for the in_login state, we only want to clean
* up the login task and connection. We do not need to block and set
* the recovery state again
@@ -2208,11 +2308,6 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
spin_unlock_bh(&session->lock);
iscsi_suspend_tx(conn);
-
- write_lock_bh(conn->recv_lock);
- set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx);
- write_unlock_bh(conn->recv_lock);
-
/*
* for connection level recovery we should not calculate
* header digest. conn->hdr_size used for optimization
@@ -2225,7 +2320,7 @@ static void iscsi_start_session_recovery(struct iscsi_session *session,
if (session->state == ISCSI_STATE_IN_RECOVERY &&
old_stop_stage != STOP_CONN_RECOVER) {
debug_scsi("blocking session\n");
- iscsi_block_session(session_to_cls(session));
+ iscsi_block_session(session->cls_session);
}
}
@@ -2260,7 +2355,7 @@ EXPORT_SYMBOL_GPL(iscsi_conn_stop);
int iscsi_conn_bind(struct iscsi_cls_session *cls_session,
struct iscsi_cls_conn *cls_conn, int is_leading)
{
- struct iscsi_session *session = class_to_transport_session(cls_session);
+ struct iscsi_session *session = cls_session->dd_data;
struct iscsi_conn *conn = cls_conn->dd_data;
spin_lock_bh(&session->lock);
@@ -2399,6 +2494,14 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
if (!conn->persistent_address)
return -ENOMEM;
break;
+ case ISCSI_PARAM_IFACE_NAME:
+ if (!session->ifacename)
+ session->ifacename = kstrdup(buf, GFP_KERNEL);
+ break;
+ case ISCSI_PARAM_INITIATOR_NAME:
+ if (!session->initiatorname)
+ session->initiatorname = kstrdup(buf, GFP_KERNEL);
+ break;
default:
return -ENOSYS;
}
@@ -2410,8 +2513,7 @@ EXPORT_SYMBOL_GPL(iscsi_set_param);
int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
enum iscsi_param param, char *buf)
{
- struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
- struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+ struct iscsi_session *session = cls_session->dd_data;
int len;
switch(param) {
@@ -2466,6 +2568,15 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
case ISCSI_PARAM_PASSWORD_IN:
len = sprintf(buf, "%s\n", session->password_in);
break;
+ case ISCSI_PARAM_IFACE_NAME:
+ len = sprintf(buf, "%s\n", session->ifacename);
+ break;
+ case ISCSI_PARAM_INITIATOR_NAME:
+ if (!session->initiatorname)
+ len = sprintf(buf, "%s\n", "unknown");
+ else
+ len = sprintf(buf, "%s\n", session->initiatorname);
+ break;
default:
return -ENOSYS;
}
@@ -2525,29 +2636,35 @@ EXPORT_SYMBOL_GPL(iscsi_conn_get_param);
int iscsi_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param,
char *buf)
{
- struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+ struct iscsi_host *ihost = shost_priv(shost);
int len;
switch (param) {
case ISCSI_HOST_PARAM_NETDEV_NAME:
- if (!session->netdev)
+ if (!ihost->netdev)
len = sprintf(buf, "%s\n", "default");
else
- len = sprintf(buf, "%s\n", session->netdev);
+ len = sprintf(buf, "%s\n", ihost->netdev);
break;
case ISCSI_HOST_PARAM_HWADDRESS:
- if (!session->hwaddress)
+ if (!ihost->hwaddress)
len = sprintf(buf, "%s\n", "default");
else
- len = sprintf(buf, "%s\n", session->hwaddress);
+ len = sprintf(buf, "%s\n", ihost->hwaddress);
break;
case ISCSI_HOST_PARAM_INITIATOR_NAME:
- if (!session->initiatorname)
+ if (!ihost->initiatorname)
len = sprintf(buf, "%s\n", "unknown");
else
- len = sprintf(buf, "%s\n", session->initiatorname);
+ len = sprintf(buf, "%s\n", ihost->initiatorname);
+ break;
+ case ISCSI_HOST_PARAM_IPADDRESS:
+ if (!strlen(ihost->local_address))
+ len = sprintf(buf, "%s\n", "unknown");
+ else
+ len = sprintf(buf, "%s\n",
+ ihost->local_address);
break;
-
default:
return -ENOSYS;
}
@@ -2559,20 +2676,20 @@ EXPORT_SYMBOL_GPL(iscsi_host_get_param);
int iscsi_host_set_param(struct Scsi_Host *shost, enum iscsi_host_param param,
char *buf, int buflen)
{
- struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+ struct iscsi_host *ihost = shost_priv(shost);
switch (param) {
case ISCSI_HOST_PARAM_NETDEV_NAME:
- if (!session->netdev)
- session->netdev = kstrdup(buf, GFP_KERNEL);
+ if (!ihost->netdev)
+ ihost->netdev = kstrdup(buf, GFP_KERNEL);
break;
case ISCSI_HOST_PARAM_HWADDRESS:
- if (!session->hwaddress)
- session->hwaddress = kstrdup(buf, GFP_KERNEL);
+ if (!ihost->hwaddress)
+ ihost->hwaddress = kstrdup(buf, GFP_KERNEL);
break;
case ISCSI_HOST_PARAM_INITIATOR_NAME:
- if (!session->initiatorname)
- session->initiatorname = kstrdup(buf, GFP_KERNEL);
+ if (!ihost->initiatorname)
+ ihost->initiatorname = kstrdup(buf, GFP_KERNEL);
break;
default:
return -ENOSYS;
diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c
index fd63b06d9ef1..11aa917629ac 100644
--- a/drivers/scsi/mesh.c
+++ b/drivers/scsi/mesh.c
@@ -1765,7 +1765,7 @@ static int mesh_suspend(struct macio_dev *mdev, pm_message_t mesg)
default:
return 0;
}
- if (mesg.event == mdev->ofdev.dev.power.power_state.event)
+ if (ms->phase == sleeping)
return 0;
scsi_block_requests(ms->host);
@@ -1780,8 +1780,6 @@ static int mesh_suspend(struct macio_dev *mdev, pm_message_t mesg)
disable_irq(ms->meshintr);
set_mesh_power(ms, 0);
- mdev->ofdev.dev.power.power_state = mesg;
-
return 0;
}
@@ -1790,7 +1788,7 @@ static int mesh_resume(struct macio_dev *mdev)
struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
unsigned long flags;
- if (mdev->ofdev.dev.power.power_state.event == PM_EVENT_ON)
+ if (ms->phase != sleeping)
return 0;
set_mesh_power(ms, 1);
@@ -1801,8 +1799,6 @@ static int mesh_resume(struct macio_dev *mdev)
enable_irq(ms->meshintr);
scsi_unblock_requests(ms->host);
- mdev->ofdev.dev.power.power_state.event = PM_EVENT_ON;
-
return 0;
}
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index 0c786944d2c2..5822dd595826 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -113,9 +113,6 @@ static struct iscsi_transport qla4xxx_iscsi_transport = {
.host_param_mask = ISCSI_HOST_HWADDRESS |
ISCSI_HOST_IPADDRESS |
ISCSI_HOST_INITIATOR_NAME,
- .sessiondata_size = sizeof(struct ddb_entry),
- .host_template = &qla4xxx_driver_template,
-
.tgt_dscvr = qla4xxx_tgt_dscvr,
.get_conn_param = qla4xxx_conn_get_param,
.get_session_param = qla4xxx_sess_get_param,
@@ -275,7 +272,7 @@ int qla4xxx_add_sess(struct ddb_entry *ddb_entry)
return err;
}
- ddb_entry->conn = iscsi_create_conn(ddb_entry->sess, 0);
+ ddb_entry->conn = iscsi_create_conn(ddb_entry->sess, 0, 0);
if (!ddb_entry->conn) {
iscsi_remove_session(ddb_entry->sess);
DEBUG2(printk(KERN_ERR "Could not add connection.\n"));
@@ -292,7 +289,8 @@ struct ddb_entry *qla4xxx_alloc_sess(struct scsi_qla_host *ha)
struct ddb_entry *ddb_entry;
struct iscsi_cls_session *sess;
- sess = iscsi_alloc_session(ha->host, &qla4xxx_iscsi_transport);
+ sess = iscsi_alloc_session(ha->host, &qla4xxx_iscsi_transport,
+ sizeof(struct ddb_entry));
if (!sess)
return NULL;
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index 110e776d1a07..36c92f961e15 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -855,9 +855,18 @@ void scsi_finish_command(struct scsi_cmnd *cmd)
good_bytes = scsi_bufflen(cmd);
if (cmd->request->cmd_type != REQ_TYPE_BLOCK_PC) {
+ int old_good_bytes = good_bytes;
drv = scsi_cmd_to_driver(cmd);
if (drv->done)
good_bytes = drv->done(cmd);
+ /*
+ * USB may not give sense identifying bad sector and
+ * simply return a residue instead, so subtract off the
+ * residue if drv->done() error processing indicates no
+ * change to the completion length.
+ */
+ if (good_bytes == old_good_bytes)
+ good_bytes -= scsi_get_resid(cmd);
}
scsi_io_completion(cmd, good_bytes);
}
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index f6600bfb5bde..3901125455c9 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -94,6 +94,7 @@ static const char * scsi_debug_version_date = "20070104";
#define DEF_VIRTUAL_GB 0
#define DEF_FAKE_RW 0
#define DEF_VPD_USE_HOSTNO 1
+#define DEF_SECTOR_SIZE 512
/* bit mask values for scsi_debug_opts */
#define SCSI_DEBUG_OPT_NOISE 1
@@ -142,6 +143,7 @@ static int scsi_debug_no_lun_0 = DEF_NO_LUN_0;
static int scsi_debug_virtual_gb = DEF_VIRTUAL_GB;
static int scsi_debug_fake_rw = DEF_FAKE_RW;
static int scsi_debug_vpd_use_hostno = DEF_VPD_USE_HOSTNO;
+static int scsi_debug_sector_size = DEF_SECTOR_SIZE;
static int scsi_debug_cmnd_count = 0;
@@ -157,11 +159,6 @@ static int sdebug_heads; /* heads per disk */
static int sdebug_cylinders_per; /* cylinders per surface */
static int sdebug_sectors_per; /* sectors per cylinder */
-/* default sector size is 512 bytes, 2**9 bytes */
-#define POW2_SECT_SIZE 9
-#define SECT_SIZE (1 << POW2_SECT_SIZE)
-#define SECT_SIZE_PER(TGT) SECT_SIZE
-
#define SDEBUG_MAX_PARTS 4
#define SDEBUG_SENSE_LEN 32
@@ -878,8 +875,8 @@ static int resp_readcap(struct scsi_cmnd * scp,
arr[2] = 0xff;
arr[3] = 0xff;
}
- arr[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
- arr[7] = SECT_SIZE_PER(target) & 0xff;
+ arr[6] = (scsi_debug_sector_size >> 8) & 0xff;
+ arr[7] = scsi_debug_sector_size & 0xff;
return fill_from_dev_buffer(scp, arr, SDEBUG_READCAP_ARR_SZ);
}
@@ -902,10 +899,10 @@ static int resp_readcap16(struct scsi_cmnd * scp,
capac = sdebug_capacity - 1;
for (k = 0; k < 8; ++k, capac >>= 8)
arr[7 - k] = capac & 0xff;
- arr[8] = (SECT_SIZE_PER(target) >> 24) & 0xff;
- arr[9] = (SECT_SIZE_PER(target) >> 16) & 0xff;
- arr[10] = (SECT_SIZE_PER(target) >> 8) & 0xff;
- arr[11] = SECT_SIZE_PER(target) & 0xff;
+ arr[8] = (scsi_debug_sector_size >> 24) & 0xff;
+ arr[9] = (scsi_debug_sector_size >> 16) & 0xff;
+ arr[10] = (scsi_debug_sector_size >> 8) & 0xff;
+ arr[11] = scsi_debug_sector_size & 0xff;
return fill_from_dev_buffer(scp, arr,
min(alloc_len, SDEBUG_READCAP16_ARR_SZ));
}
@@ -1019,20 +1016,20 @@ static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target)
static int resp_format_pg(unsigned char * p, int pcontrol, int target)
{ /* Format device page for mode_sense */
- unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0x40, 0, 0, 0};
-
- memcpy(p, format_pg, sizeof(format_pg));
- p[10] = (sdebug_sectors_per >> 8) & 0xff;
- p[11] = sdebug_sectors_per & 0xff;
- p[12] = (SECT_SIZE >> 8) & 0xff;
- p[13] = SECT_SIZE & 0xff;
- if (DEV_REMOVEABLE(target))
- p[20] |= 0x20; /* should agree with INQUIRY */
- if (1 == pcontrol)
- memset(p + 2, 0, sizeof(format_pg) - 2);
- return sizeof(format_pg);
+ unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0x40, 0, 0, 0};
+
+ memcpy(p, format_pg, sizeof(format_pg));
+ p[10] = (sdebug_sectors_per >> 8) & 0xff;
+ p[11] = sdebug_sectors_per & 0xff;
+ p[12] = (scsi_debug_sector_size >> 8) & 0xff;
+ p[13] = scsi_debug_sector_size & 0xff;
+ if (DEV_REMOVEABLE(target))
+ p[20] |= 0x20; /* should agree with INQUIRY */
+ if (1 == pcontrol)
+ memset(p + 2, 0, sizeof(format_pg) - 2);
+ return sizeof(format_pg);
}
static int resp_caching_pg(unsigned char * p, int pcontrol, int target)
@@ -1206,8 +1203,8 @@ static int resp_mode_sense(struct scsi_cmnd * scp, int target,
ap[2] = (sdebug_capacity >> 8) & 0xff;
ap[3] = sdebug_capacity & 0xff;
}
- ap[6] = (SECT_SIZE_PER(target) >> 8) & 0xff;
- ap[7] = SECT_SIZE_PER(target) & 0xff;
+ ap[6] = (scsi_debug_sector_size >> 8) & 0xff;
+ ap[7] = scsi_debug_sector_size & 0xff;
offset += bd_len;
ap = arr + offset;
} else if (16 == bd_len) {
@@ -1215,10 +1212,10 @@ static int resp_mode_sense(struct scsi_cmnd * scp, int target,
for (k = 0; k < 8; ++k, capac >>= 8)
ap[7 - k] = capac & 0xff;
- ap[12] = (SECT_SIZE_PER(target) >> 24) & 0xff;
- ap[13] = (SECT_SIZE_PER(target) >> 16) & 0xff;
- ap[14] = (SECT_SIZE_PER(target) >> 8) & 0xff;
- ap[15] = SECT_SIZE_PER(target) & 0xff;
+ ap[12] = (scsi_debug_sector_size >> 24) & 0xff;
+ ap[13] = (scsi_debug_sector_size >> 16) & 0xff;
+ ap[14] = (scsi_debug_sector_size >> 8) & 0xff;
+ ap[15] = scsi_debug_sector_size & 0xff;
offset += bd_len;
ap = arr + offset;
}
@@ -1519,10 +1516,10 @@ static int do_device_access(struct scsi_cmnd *scmd,
if (block + num > sdebug_store_sectors)
rest = block + num - sdebug_store_sectors;
- ret = func(scmd, fake_storep + (block * SECT_SIZE),
- (num - rest) * SECT_SIZE);
+ ret = func(scmd, fake_storep + (block * scsi_debug_sector_size),
+ (num - rest) * scsi_debug_sector_size);
if (!ret && rest)
- ret = func(scmd, fake_storep, rest * SECT_SIZE);
+ ret = func(scmd, fake_storep, rest * scsi_debug_sector_size);
return ret;
}
@@ -1575,10 +1572,10 @@ static int resp_write(struct scsi_cmnd *SCpnt, unsigned long long lba,
write_unlock_irqrestore(&atomic_rw, iflags);
if (-1 == ret)
return (DID_ERROR << 16);
- else if ((ret < (num * SECT_SIZE)) &&
+ else if ((ret < (num * scsi_debug_sector_size)) &&
(SCSI_DEBUG_OPT_NOISE & scsi_debug_opts))
printk(KERN_INFO "scsi_debug: write: cdb indicated=%u, "
- " IO sent=%d bytes\n", num * SECT_SIZE, ret);
+ " IO sent=%d bytes\n", num * scsi_debug_sector_size, ret);
return 0;
}
@@ -2085,6 +2082,7 @@ module_param_named(scsi_level, scsi_debug_scsi_level, int, S_IRUGO);
module_param_named(virtual_gb, scsi_debug_virtual_gb, int, S_IRUGO | S_IWUSR);
module_param_named(vpd_use_hostno, scsi_debug_vpd_use_hostno, int,
S_IRUGO | S_IWUSR);
+module_param_named(sector_size, scsi_debug_sector_size, int, S_IRUGO);
MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
MODULE_DESCRIPTION("SCSI debug adapter driver");
@@ -2106,6 +2104,7 @@ MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])");
MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=5[SPC-3])");
MODULE_PARM_DESC(virtual_gb, "virtual gigabyte size (def=0 -> use dev_size_mb)");
MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique dev ids)");
+MODULE_PARM_DESC(sector_size, "hardware sector size in bytes (def=512)");
static char sdebug_info[256];
@@ -2158,8 +2157,9 @@ static int scsi_debug_proc_info(struct Scsi_Host *host, char *buffer, char **sta
scsi_debug_dev_size_mb, scsi_debug_opts, scsi_debug_every_nth,
scsi_debug_cmnd_count, scsi_debug_delay,
scsi_debug_max_luns, scsi_debug_scsi_level,
- SECT_SIZE, sdebug_cylinders_per, sdebug_heads, sdebug_sectors_per,
- num_aborts, num_dev_resets, num_bus_resets, num_host_resets);
+ scsi_debug_sector_size, sdebug_cylinders_per, sdebug_heads,
+ sdebug_sectors_per, num_aborts, num_dev_resets, num_bus_resets,
+ num_host_resets);
if (pos < offset) {
len = 0;
begin = pos;
@@ -2434,6 +2434,12 @@ static ssize_t sdebug_vpd_use_hostno_store(struct device_driver * ddp,
DRIVER_ATTR(vpd_use_hostno, S_IRUGO | S_IWUSR, sdebug_vpd_use_hostno_show,
sdebug_vpd_use_hostno_store);
+static ssize_t sdebug_sector_size_show(struct device_driver * ddp, char * buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%u\n", scsi_debug_sector_size);
+}
+DRIVER_ATTR(sector_size, S_IRUGO, sdebug_sector_size_show, NULL);
+
/* Note: The following function creates attribute files in the
/sys/bus/pseudo/drivers/scsi_debug directory. The advantage of these
files (over those found in the /sys/module/scsi_debug/parameters
@@ -2459,11 +2465,13 @@ static int do_create_driverfs_files(void)
ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
+ ret |= driver_create_file(&sdebug_driverfs_driver, &driver_attr_sector_size);
return ret;
}
static void do_remove_driverfs_files(void)
{
+ driver_remove_file(&sdebug_driverfs_driver, &driver_attr_sector_size);
driver_remove_file(&sdebug_driverfs_driver, &driver_attr_vpd_use_hostno);
driver_remove_file(&sdebug_driverfs_driver, &driver_attr_virtual_gb);
driver_remove_file(&sdebug_driverfs_driver, &driver_attr_scsi_level);
@@ -2499,10 +2507,22 @@ static int __init scsi_debug_init(void)
int k;
int ret;
+ switch (scsi_debug_sector_size) {
+ case 512:
+ case 1024:
+ case 2048:
+ case 4096:
+ break;
+ default:
+ printk(KERN_ERR "scsi_debug_init: invalid sector_size %u\n",
+ scsi_debug_sector_size);
+ return -EINVAL;
+ }
+
if (scsi_debug_dev_size_mb < 1)
scsi_debug_dev_size_mb = 1; /* force minimum 1 MB ramdisk */
sz = (unsigned long)scsi_debug_dev_size_mb * 1048576;
- sdebug_store_sectors = sz / SECT_SIZE;
+ sdebug_store_sectors = sz / scsi_debug_sector_size;
sdebug_capacity = get_sdebug_capacity();
/* play around with geometry, don't waste too much on track 0 */
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index eaf5a8add1ba..006a95916f72 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -298,6 +298,7 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost,
*/
static int scsi_check_sense(struct scsi_cmnd *scmd)
{
+ struct scsi_device *sdev = scmd->device;
struct scsi_sense_hdr sshdr;
if (! scsi_command_normalize_sense(scmd, &sshdr))
@@ -306,6 +307,16 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
if (scsi_sense_is_deferred(&sshdr))
return NEEDS_RETRY;
+ if (sdev->scsi_dh_data && sdev->scsi_dh_data->scsi_dh &&
+ sdev->scsi_dh_data->scsi_dh->check_sense) {
+ int rc;
+
+ rc = sdev->scsi_dh_data->scsi_dh->check_sense(sdev, &sshdr);
+ if (rc != SCSI_RETURN_NOT_HANDLED)
+ return rc;
+ /* handler does not care. Drop down to default handling */
+ }
+
/*
* Previous logic looked for FILEMARK, EOM or ILI which are
* mainly associated with tapes and returned SUCCESS.
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index a82d2fe80fb5..033c58a65f50 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -1160,6 +1160,14 @@ int scsi_setup_fs_cmnd(struct scsi_device *sdev, struct request *req)
if (ret != BLKPREP_OK)
return ret;
+
+ if (unlikely(sdev->scsi_dh_data && sdev->scsi_dh_data->scsi_dh
+ && sdev->scsi_dh_data->scsi_dh->prep_fn)) {
+ ret = sdev->scsi_dh_data->scsi_dh->prep_fn(sdev, req);
+ if (ret != BLKPREP_OK)
+ return ret;
+ }
+
/*
* Filesystem requests must transfer data.
*/
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 93d2b6714453..b6e561059779 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -439,6 +439,7 @@ struct bus_type scsi_bus_type = {
.resume = scsi_bus_resume,
.remove = scsi_bus_remove,
};
+EXPORT_SYMBOL_GPL(scsi_bus_type);
int scsi_sysfs_register(void)
{
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index 5fd64e70029d..a272b9a2c869 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -417,15 +417,16 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev,
fc_host->next_vport_number = 0;
fc_host->npiv_vports_inuse = 0;
- snprintf(fc_host->work_q_name, KOBJ_NAME_LEN, "fc_wq_%d",
- shost->host_no);
+ snprintf(fc_host->work_q_name, sizeof(fc_host->work_q_name),
+ "fc_wq_%d", shost->host_no);
fc_host->work_q = create_singlethread_workqueue(
fc_host->work_q_name);
if (!fc_host->work_q)
return -ENOMEM;
- snprintf(fc_host->devloss_work_q_name, KOBJ_NAME_LEN, "fc_dl_%d",
- shost->host_no);
+ snprintf(fc_host->devloss_work_q_name,
+ sizeof(fc_host->devloss_work_q_name),
+ "fc_dl_%d", shost->host_no);
fc_host->devloss_work_q = create_singlethread_workqueue(
fc_host->devloss_work_q_name);
if (!fc_host->devloss_work_q) {
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index 65d1737eb664..cb1df9f1528a 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -30,9 +30,10 @@
#include <scsi/scsi_transport_iscsi.h>
#include <scsi/iscsi_if.h>
-#define ISCSI_SESSION_ATTRS 19
+#define ISCSI_SESSION_ATTRS 21
#define ISCSI_CONN_ATTRS 13
#define ISCSI_HOST_ATTRS 4
+
#define ISCSI_TRANSPORT_VERSION "2.0-869"
struct iscsi_internal {
@@ -101,16 +102,10 @@ show_transport_##name(struct device *dev, \
static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);
show_transport_attr(caps, "0x%x");
-show_transport_attr(max_lun, "%d");
-show_transport_attr(max_conn, "%d");
-show_transport_attr(max_cmd_len, "%d");
static struct attribute *iscsi_transport_attrs[] = {
&dev_attr_handle.attr,
&dev_attr_caps.attr,
- &dev_attr_max_lun.attr,
- &dev_attr_max_conn.attr,
- &dev_attr_max_cmd_len.attr,
NULL,
};
@@ -118,21 +113,135 @@ static struct attribute_group iscsi_transport_group = {
.attrs = iscsi_transport_attrs,
};
+/*
+ * iSCSI endpoint attrs
+ */
+#define iscsi_dev_to_endpoint(_dev) \
+ container_of(_dev, struct iscsi_endpoint, dev)
+
+#define ISCSI_ATTR(_prefix,_name,_mode,_show,_store) \
+struct device_attribute dev_attr_##_prefix##_##_name = \
+ __ATTR(_name,_mode,_show,_store)
+
+static void iscsi_endpoint_release(struct device *dev)
+{
+ struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev);
+ kfree(ep);
+}
+
+static struct class iscsi_endpoint_class = {
+ .name = "iscsi_endpoint",
+ .dev_release = iscsi_endpoint_release,
+};
+
+static ssize_t
+show_ep_handle(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev);
+ return sprintf(buf, "%u\n", ep->id);
+}
+static ISCSI_ATTR(ep, handle, S_IRUGO, show_ep_handle, NULL);
+
+static struct attribute *iscsi_endpoint_attrs[] = {
+ &dev_attr_ep_handle.attr,
+ NULL,
+};
+
+static struct attribute_group iscsi_endpoint_group = {
+ .attrs = iscsi_endpoint_attrs,
+};
+
+#define ISCSI_MAX_EPID -1
+
+static int iscsi_match_epid(struct device *dev, void *data)
+{
+ struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev);
+ unsigned int *epid = (unsigned int *) data;
+
+ return *epid == ep->id;
+}
+
+struct iscsi_endpoint *
+iscsi_create_endpoint(int dd_size)
+{
+ struct device *dev;
+ struct iscsi_endpoint *ep;
+ unsigned int id;
+ int err;
+ for (id = 1; id < ISCSI_MAX_EPID; id++) {
+ dev = class_find_device(&iscsi_endpoint_class, NULL, &id,
+ iscsi_match_epid);
+ if (!dev)
+ break;
+ }
+ if (id == ISCSI_MAX_EPID) {
+ printk(KERN_ERR "Too many connections. Max supported %u\n",
+ ISCSI_MAX_EPID - 1);
+ return NULL;
+ }
+
+ ep = kzalloc(sizeof(*ep) + dd_size, GFP_KERNEL);
+ if (!ep)
+ return NULL;
+
+ ep->id = id;
+ ep->dev.class = &iscsi_endpoint_class;
+ snprintf(ep->dev.bus_id, BUS_ID_SIZE, "ep-%u", id);
+ err = device_register(&ep->dev);
+ if (err)
+ goto free_ep;
+
+ err = sysfs_create_group(&ep->dev.kobj, &iscsi_endpoint_group);
+ if (err)
+ goto unregister_dev;
+
+ if (dd_size)
+ ep->dd_data = &ep[1];
+ return ep;
+
+unregister_dev:
+ device_unregister(&ep->dev);
+ return NULL;
+
+free_ep:
+ kfree(ep);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(iscsi_create_endpoint);
+
+void iscsi_destroy_endpoint(struct iscsi_endpoint *ep)
+{
+ sysfs_remove_group(&ep->dev.kobj, &iscsi_endpoint_group);
+ device_unregister(&ep->dev);
+}
+EXPORT_SYMBOL_GPL(iscsi_destroy_endpoint);
+
+struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle)
+{
+ struct device *dev;
+
+ dev = class_find_device(&iscsi_endpoint_class, NULL, &handle,
+ iscsi_match_epid);
+ if (!dev)
+ return NULL;
+
+ return iscsi_dev_to_endpoint(dev);
+}
+EXPORT_SYMBOL_GPL(iscsi_lookup_endpoint);
static int iscsi_setup_host(struct transport_container *tc, struct device *dev,
struct device *cdev)
{
struct Scsi_Host *shost = dev_to_shost(dev);
- struct iscsi_host *ihost = shost->shost_data;
+ struct iscsi_cls_host *ihost = shost->shost_data;
memset(ihost, 0, sizeof(*ihost));
- INIT_LIST_HEAD(&ihost->sessions);
- mutex_init(&ihost->mutex);
atomic_set(&ihost->nr_scans, 0);
+ mutex_init(&ihost->mutex);
- snprintf(ihost->scan_workq_name, KOBJ_NAME_LEN, "iscsi_scan_%d",
- shost->host_no);
+ snprintf(ihost->scan_workq_name, sizeof(ihost->scan_workq_name),
+ "iscsi_scan_%d", shost->host_no);
ihost->scan_workq = create_singlethread_workqueue(
ihost->scan_workq_name);
if (!ihost->scan_workq)
@@ -144,7 +253,7 @@ static int iscsi_remove_host(struct transport_container *tc, struct device *dev,
struct device *cdev)
{
struct Scsi_Host *shost = dev_to_shost(dev);
- struct iscsi_host *ihost = shost->shost_data;
+ struct iscsi_cls_host *ihost = shost->shost_data;
destroy_workqueue(ihost->scan_workq);
return 0;
@@ -287,6 +396,24 @@ static int iscsi_is_session_dev(const struct device *dev)
return dev->release == iscsi_session_release;
}
+static int iscsi_iter_session_fn(struct device *dev, void *data)
+{
+ void (* fn) (struct iscsi_cls_session *) = data;
+
+ if (!iscsi_is_session_dev(dev))
+ return 0;
+ fn(iscsi_dev_to_session(dev));
+ return 0;
+}
+
+void iscsi_host_for_each_session(struct Scsi_Host *shost,
+ void (*fn)(struct iscsi_cls_session *))
+{
+ device_for_each_child(&shost->shost_gendev, fn,
+ iscsi_iter_session_fn);
+}
+EXPORT_SYMBOL_GPL(iscsi_host_for_each_session);
+
/**
* iscsi_scan_finished - helper to report when running scans are done
* @shost: scsi host
@@ -297,7 +424,7 @@ static int iscsi_is_session_dev(const struct device *dev)
*/
int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time)
{
- struct iscsi_host *ihost = shost->shost_data;
+ struct iscsi_cls_host *ihost = shost->shost_data;
/*
* qla4xxx will have kicked off some session unblocks before calling
* scsi_scan_host, so just wait for them to complete.
@@ -306,42 +433,76 @@ int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time)
}
EXPORT_SYMBOL_GPL(iscsi_scan_finished);
-static int iscsi_user_scan(struct Scsi_Host *shost, uint channel,
- uint id, uint lun)
+struct iscsi_scan_data {
+ unsigned int channel;
+ unsigned int id;
+ unsigned int lun;
+};
+
+static int iscsi_user_scan_session(struct device *dev, void *data)
{
- struct iscsi_host *ihost = shost->shost_data;
+ struct iscsi_scan_data *scan_data = data;
struct iscsi_cls_session *session;
+ struct Scsi_Host *shost;
+ struct iscsi_cls_host *ihost;
+ unsigned long flags;
+ unsigned int id;
+
+ if (!iscsi_is_session_dev(dev))
+ return 0;
+
+ session = iscsi_dev_to_session(dev);
+ shost = iscsi_session_to_shost(session);
+ ihost = shost->shost_data;
mutex_lock(&ihost->mutex);
- list_for_each_entry(session, &ihost->sessions, host_list) {
- if ((channel == SCAN_WILD_CARD || channel == 0) &&
- (id == SCAN_WILD_CARD || id == session->target_id))
- scsi_scan_target(&session->dev, 0,
- session->target_id, lun, 1);
+ spin_lock_irqsave(&session->lock, flags);
+ if (session->state != ISCSI_SESSION_LOGGED_IN) {
+ spin_unlock_irqrestore(&session->lock, flags);
+ mutex_unlock(&ihost->mutex);
+ return 0;
}
- mutex_unlock(&ihost->mutex);
+ id = session->target_id;
+ spin_unlock_irqrestore(&session->lock, flags);
+ if (id != ISCSI_MAX_TARGET) {
+ if ((scan_data->channel == SCAN_WILD_CARD ||
+ scan_data->channel == 0) &&
+ (scan_data->id == SCAN_WILD_CARD ||
+ scan_data->id == id))
+ scsi_scan_target(&session->dev, 0, id,
+ scan_data->lun, 1);
+ }
+ mutex_unlock(&ihost->mutex);
return 0;
}
+static int iscsi_user_scan(struct Scsi_Host *shost, uint channel,
+ uint id, uint lun)
+{
+ struct iscsi_scan_data scan_data;
+
+ scan_data.channel = channel;
+ scan_data.id = id;
+ scan_data.lun = lun;
+
+ return device_for_each_child(&shost->shost_gendev, &scan_data,
+ iscsi_user_scan_session);
+}
+
static void iscsi_scan_session(struct work_struct *work)
{
struct iscsi_cls_session *session =
container_of(work, struct iscsi_cls_session, scan_work);
struct Scsi_Host *shost = iscsi_session_to_shost(session);
- struct iscsi_host *ihost = shost->shost_data;
- unsigned long flags;
+ struct iscsi_cls_host *ihost = shost->shost_data;
+ struct iscsi_scan_data scan_data;
- spin_lock_irqsave(&session->lock, flags);
- if (session->state != ISCSI_SESSION_LOGGED_IN) {
- spin_unlock_irqrestore(&session->lock, flags);
- goto done;
- }
- spin_unlock_irqrestore(&session->lock, flags);
+ scan_data.channel = 0;
+ scan_data.id = SCAN_WILD_CARD;
+ scan_data.lun = SCAN_WILD_CARD;
- scsi_scan_target(&session->dev, 0, session->target_id,
- SCAN_WILD_CARD, 1);
-done:
+ iscsi_user_scan_session(&session->dev, &scan_data);
atomic_dec(&ihost->nr_scans);
}
@@ -381,7 +542,7 @@ static void __iscsi_unblock_session(struct work_struct *work)
container_of(work, struct iscsi_cls_session,
unblock_work);
struct Scsi_Host *shost = iscsi_session_to_shost(session);
- struct iscsi_host *ihost = shost->shost_data;
+ struct iscsi_cls_host *ihost = shost->shost_data;
unsigned long flags;
/*
@@ -449,15 +610,19 @@ static void __iscsi_unbind_session(struct work_struct *work)
container_of(work, struct iscsi_cls_session,
unbind_work);
struct Scsi_Host *shost = iscsi_session_to_shost(session);
- struct iscsi_host *ihost = shost->shost_data;
+ struct iscsi_cls_host *ihost = shost->shost_data;
+ unsigned long flags;
/* Prevent new scans and make sure scanning is not in progress */
mutex_lock(&ihost->mutex);
- if (list_empty(&session->host_list)) {
+ spin_lock_irqsave(&session->lock, flags);
+ if (session->target_id == ISCSI_MAX_TARGET) {
+ spin_unlock_irqrestore(&session->lock, flags);
mutex_unlock(&ihost->mutex);
return;
}
- list_del_init(&session->host_list);
+ session->target_id = ISCSI_MAX_TARGET;
+ spin_unlock_irqrestore(&session->lock, flags);
mutex_unlock(&ihost->mutex);
scsi_remove_target(&session->dev);
@@ -467,18 +632,18 @@ static void __iscsi_unbind_session(struct work_struct *work)
static int iscsi_unbind_session(struct iscsi_cls_session *session)
{
struct Scsi_Host *shost = iscsi_session_to_shost(session);
- struct iscsi_host *ihost = shost->shost_data;
+ struct iscsi_cls_host *ihost = shost->shost_data;
return queue_work(ihost->scan_workq, &session->unbind_work);
}
struct iscsi_cls_session *
-iscsi_alloc_session(struct Scsi_Host *shost,
- struct iscsi_transport *transport)
+iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
+ int dd_size)
{
struct iscsi_cls_session *session;
- session = kzalloc(sizeof(*session) + transport->sessiondata_size,
+ session = kzalloc(sizeof(*session) + dd_size,
GFP_KERNEL);
if (!session)
return NULL;
@@ -487,7 +652,6 @@ iscsi_alloc_session(struct Scsi_Host *shost,
session->recovery_tmo = 120;
session->state = ISCSI_SESSION_FREE;
INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout);
- INIT_LIST_HEAD(&session->host_list);
INIT_LIST_HEAD(&session->sess_list);
INIT_WORK(&session->unblock_work, __iscsi_unblock_session);
INIT_WORK(&session->block_work, __iscsi_block_session);
@@ -500,22 +664,57 @@ iscsi_alloc_session(struct Scsi_Host *shost,
session->dev.parent = &shost->shost_gendev;
session->dev.release = iscsi_session_release;
device_initialize(&session->dev);
- if (transport->sessiondata_size)
+ if (dd_size)
session->dd_data = &session[1];
return session;
}
EXPORT_SYMBOL_GPL(iscsi_alloc_session);
+static int iscsi_get_next_target_id(struct device *dev, void *data)
+{
+ struct iscsi_cls_session *session;
+ unsigned long flags;
+ int err = 0;
+
+ if (!iscsi_is_session_dev(dev))
+ return 0;
+
+ session = iscsi_dev_to_session(dev);
+ spin_lock_irqsave(&session->lock, flags);
+ if (*((unsigned int *) data) == session->target_id)
+ err = -EEXIST;
+ spin_unlock_irqrestore(&session->lock, flags);
+ return err;
+}
+
int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
{
struct Scsi_Host *shost = iscsi_session_to_shost(session);
- struct iscsi_host *ihost;
+ struct iscsi_cls_host *ihost;
unsigned long flags;
+ unsigned int id = target_id;
int err;
ihost = shost->shost_data;
session->sid = atomic_add_return(1, &iscsi_session_nr);
- session->target_id = target_id;
+
+ if (id == ISCSI_MAX_TARGET) {
+ for (id = 0; id < ISCSI_MAX_TARGET; id++) {
+ err = device_for_each_child(&shost->shost_gendev, &id,
+ iscsi_get_next_target_id);
+ if (!err)
+ break;
+ }
+
+ if (id == ISCSI_MAX_TARGET) {
+ iscsi_cls_session_printk(KERN_ERR, session,
+ "Too many iscsi targets. Max "
+ "number of targets is %d.\n",
+ ISCSI_MAX_TARGET - 1);
+ goto release_host;
+ }
+ }
+ session->target_id = id;
snprintf(session->dev.bus_id, BUS_ID_SIZE, "session%u",
session->sid);
@@ -531,10 +730,6 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
list_add(&session->sess_list, &sesslist);
spin_unlock_irqrestore(&sesslock, flags);
- mutex_lock(&ihost->mutex);
- list_add(&session->host_list, &ihost->sessions);
- mutex_unlock(&ihost->mutex);
-
iscsi_session_event(session, ISCSI_KEVENT_CREATE_SESSION);
return 0;
@@ -548,18 +743,18 @@ EXPORT_SYMBOL_GPL(iscsi_add_session);
* iscsi_create_session - create iscsi class session
* @shost: scsi host
* @transport: iscsi transport
+ * @dd_size: private driver data size
* @target_id: which target
*
* This can be called from a LLD or iscsi_transport.
*/
struct iscsi_cls_session *
-iscsi_create_session(struct Scsi_Host *shost,
- struct iscsi_transport *transport,
- unsigned int target_id)
+iscsi_create_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
+ int dd_size, unsigned int target_id)
{
struct iscsi_cls_session *session;
- session = iscsi_alloc_session(shost, transport);
+ session = iscsi_alloc_session(shost, transport, dd_size);
if (!session)
return NULL;
@@ -595,7 +790,7 @@ static int iscsi_iter_destroy_conn_fn(struct device *dev, void *data)
void iscsi_remove_session(struct iscsi_cls_session *session)
{
struct Scsi_Host *shost = iscsi_session_to_shost(session);
- struct iscsi_host *ihost = shost->shost_data;
+ struct iscsi_cls_host *ihost = shost->shost_data;
unsigned long flags;
int err;
@@ -661,6 +856,7 @@ EXPORT_SYMBOL_GPL(iscsi_destroy_session);
/**
* iscsi_create_conn - create iscsi class connection
* @session: iscsi cls session
+ * @dd_size: private driver data size
* @cid: connection id
*
* This can be called from a LLD or iscsi_transport. The connection
@@ -673,18 +869,17 @@ EXPORT_SYMBOL_GPL(iscsi_destroy_session);
* non-zero.
*/
struct iscsi_cls_conn *
-iscsi_create_conn(struct iscsi_cls_session *session, uint32_t cid)
+iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid)
{
struct iscsi_transport *transport = session->transport;
struct iscsi_cls_conn *conn;
unsigned long flags;
int err;
- conn = kzalloc(sizeof(*conn) + transport->conndata_size, GFP_KERNEL);
+ conn = kzalloc(sizeof(*conn) + dd_size, GFP_KERNEL);
if (!conn)
return NULL;
-
- if (transport->conndata_size)
+ if (dd_size)
conn->dd_data = &conn[1];
INIT_LIST_HEAD(&conn->conn_list);
@@ -1017,21 +1212,20 @@ int iscsi_session_event(struct iscsi_cls_session *session,
EXPORT_SYMBOL_GPL(iscsi_session_event);
static int
-iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_uevent *ev)
+iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_endpoint *ep,
+ struct iscsi_uevent *ev, uint32_t initial_cmdsn,
+ uint16_t cmds_max, uint16_t queue_depth)
{
struct iscsi_transport *transport = priv->iscsi_transport;
struct iscsi_cls_session *session;
- uint32_t hostno;
+ uint32_t host_no;
- session = transport->create_session(transport, &priv->t,
- ev->u.c_session.cmds_max,
- ev->u.c_session.queue_depth,
- ev->u.c_session.initial_cmdsn,
- &hostno);
+ session = transport->create_session(ep, cmds_max, queue_depth,
+ initial_cmdsn, &host_no);
if (!session)
return -ENOMEM;
- ev->r.c_session_ret.host_no = hostno;
+ ev->r.c_session_ret.host_no = host_no;
ev->r.c_session_ret.sid = session->sid;
return 0;
}
@@ -1106,6 +1300,7 @@ static int
iscsi_if_transport_ep(struct iscsi_transport *transport,
struct iscsi_uevent *ev, int msg_type)
{
+ struct iscsi_endpoint *ep;
struct sockaddr *dst_addr;
int rc = 0;
@@ -1115,22 +1310,33 @@ iscsi_if_transport_ep(struct iscsi_transport *transport,
return -EINVAL;
dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
- rc = transport->ep_connect(dst_addr,
- ev->u.ep_connect.non_blocking,
- &ev->r.ep_connect_ret.handle);
+ ep = transport->ep_connect(dst_addr,
+ ev->u.ep_connect.non_blocking);
+ if (IS_ERR(ep))
+ return PTR_ERR(ep);
+
+ ev->r.ep_connect_ret.handle = ep->id;
break;
case ISCSI_UEVENT_TRANSPORT_EP_POLL:
if (!transport->ep_poll)
return -EINVAL;
- ev->r.retcode = transport->ep_poll(ev->u.ep_poll.ep_handle,
+ ep = iscsi_lookup_endpoint(ev->u.ep_poll.ep_handle);
+ if (!ep)
+ return -EINVAL;
+
+ ev->r.retcode = transport->ep_poll(ep,
ev->u.ep_poll.timeout_ms);
break;
case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
if (!transport->ep_disconnect)
return -EINVAL;
- transport->ep_disconnect(ev->u.ep_disconnect.ep_handle);
+ ep = iscsi_lookup_endpoint(ev->u.ep_disconnect.ep_handle);
+ if (!ep)
+ return -EINVAL;
+
+ transport->ep_disconnect(ep);
break;
}
return rc;
@@ -1195,6 +1401,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
struct iscsi_internal *priv;
struct iscsi_cls_session *session;
struct iscsi_cls_conn *conn;
+ struct iscsi_endpoint *ep = NULL;
priv = iscsi_if_transport_lookup(iscsi_ptr(ev->transport_handle));
if (!priv)
@@ -1208,7 +1415,20 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
switch (nlh->nlmsg_type) {
case ISCSI_UEVENT_CREATE_SESSION:
- err = iscsi_if_create_session(priv, ev);
+ err = iscsi_if_create_session(priv, ep, ev,
+ ev->u.c_session.initial_cmdsn,
+ ev->u.c_session.cmds_max,
+ ev->u.c_session.queue_depth);
+ break;
+ case ISCSI_UEVENT_CREATE_BOUND_SESSION:
+ ep = iscsi_lookup_endpoint(ev->u.c_bound_session.ep_handle);
+ if (!ep)
+ return -EINVAL;
+
+ err = iscsi_if_create_session(priv, ep, ev,
+ ev->u.c_bound_session.initial_cmdsn,
+ ev->u.c_bound_session.cmds_max,
+ ev->u.c_bound_session.queue_depth);
break;
case ISCSI_UEVENT_DESTROY_SESSION:
session = iscsi_session_lookup(ev->u.d_session.sid);
@@ -1414,6 +1634,8 @@ iscsi_session_attr(password_in, ISCSI_PARAM_PASSWORD_IN, 1);
iscsi_session_attr(fast_abort, ISCSI_PARAM_FAST_ABORT, 0);
iscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0);
iscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0);
+iscsi_session_attr(ifacename, ISCSI_PARAM_IFACE_NAME, 0);
+iscsi_session_attr(initiatorname, ISCSI_PARAM_INITIATOR_NAME, 0)
static ssize_t
show_priv_session_state(struct device *dev, struct device_attribute *attr,
@@ -1580,6 +1802,8 @@ iscsi_register_transport(struct iscsi_transport *tt)
priv->daemon_pid = -1;
priv->iscsi_transport = tt;
priv->t.user_scan = iscsi_user_scan;
+ if (!(tt->caps & CAP_DATA_PATH_OFFLOAD))
+ priv->t.create_work_queue = 1;
priv->dev.class = &iscsi_transport_class;
snprintf(priv->dev.bus_id, BUS_ID_SIZE, "%s", tt->name);
@@ -1595,7 +1819,7 @@ iscsi_register_transport(struct iscsi_transport *tt)
priv->t.host_attrs.ac.attrs = &priv->host_attrs[0];
priv->t.host_attrs.ac.class = &iscsi_host_class.class;
priv->t.host_attrs.ac.match = iscsi_host_match;
- priv->t.host_size = sizeof(struct iscsi_host);
+ priv->t.host_size = sizeof(struct iscsi_cls_host);
transport_container_register(&priv->t.host_attrs);
SETUP_HOST_RD_ATTR(netdev, ISCSI_HOST_NETDEV_NAME);
@@ -1653,6 +1877,8 @@ iscsi_register_transport(struct iscsi_transport *tt)
SETUP_SESSION_RD_ATTR(fast_abort, ISCSI_FAST_ABORT);
SETUP_SESSION_RD_ATTR(abort_tmo, ISCSI_ABORT_TMO);
SETUP_SESSION_RD_ATTR(lu_reset_tmo,ISCSI_LU_RESET_TMO);
+ SETUP_SESSION_RD_ATTR(ifacename, ISCSI_IFACE_NAME);
+ SETUP_SESSION_RD_ATTR(initiatorname, ISCSI_INITIATOR_NAME);
SETUP_PRIV_SESSION_RD_ATTR(recovery_tmo);
SETUP_PRIV_SESSION_RD_ATTR(state);
@@ -1668,6 +1894,7 @@ iscsi_register_transport(struct iscsi_transport *tt)
unregister_dev:
device_unregister(&priv->dev);
+ return NULL;
free_priv:
kfree(priv);
return NULL;
@@ -1715,10 +1942,14 @@ static __init int iscsi_transport_init(void)
if (err)
return err;
- err = transport_class_register(&iscsi_host_class);
+ err = class_register(&iscsi_endpoint_class);
if (err)
goto unregister_transport_class;
+ err = transport_class_register(&iscsi_host_class);
+ if (err)
+ goto unregister_endpoint_class;
+
err = transport_class_register(&iscsi_connection_class);
if (err)
goto unregister_host_class;
@@ -1727,8 +1958,8 @@ static __init int iscsi_transport_init(void)
if (err)
goto unregister_conn_class;
- nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, 1, iscsi_if_rx, NULL,
- THIS_MODULE);
+ nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, 1, iscsi_if_rx,
+ NULL, THIS_MODULE);
if (!nls) {
err = -ENOBUFS;
goto unregister_session_class;
@@ -1748,6 +1979,8 @@ unregister_conn_class:
transport_class_unregister(&iscsi_connection_class);
unregister_host_class:
transport_class_unregister(&iscsi_host_class);
+unregister_endpoint_class:
+ class_unregister(&iscsi_endpoint_class);
unregister_transport_class:
class_unregister(&iscsi_transport_class);
return err;
@@ -1760,6 +1993,7 @@ static void __exit iscsi_transport_exit(void)
transport_class_unregister(&iscsi_connection_class);
transport_class_unregister(&iscsi_session_class);
transport_class_unregister(&iscsi_host_class);
+ class_unregister(&iscsi_endpoint_class);
class_unregister(&iscsi_transport_class);
}
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 01cefbb2d539..d53312c42547 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1124,6 +1124,8 @@ sd_spinup_disk(struct scsi_disk *sdkp)
cmd[1] = 1; /* Return immediately */
memset((void *) &cmd[2], 0, 8);
cmd[4] = 1; /* Start spin cycle */
+ if (sdkp->device->start_stop_pwr_cond)
+ cmd[4] |= 1 << 4;
scsi_execute_req(sdkp->device, cmd, DMA_NONE,
NULL, 0, &sshdr,
SD_TIMEOUT, SD_MAX_RETRIES);
@@ -1790,6 +1792,9 @@ static int sd_start_stop_device(struct scsi_disk *sdkp, int start)
if (start)
cmd[4] |= 1; /* START */
+ if (sdp->start_stop_pwr_cond)
+ cmd[4] |= start ? 1 << 4 : 3 << 4; /* Active or Standby */
+
if (!scsi_device_online(sdp))
return -ENODEV;
diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c
index 7ee86d4a7618..c82df8bd4d89 100644
--- a/drivers/scsi/sr.c
+++ b/drivers/scsi/sr.c
@@ -178,6 +178,9 @@ int sr_test_unit_ready(struct scsi_device *sdev, struct scsi_sense_hdr *sshdr)
the_result = scsi_execute_req(sdev, cmd, DMA_NONE, NULL,
0, sshdr, SR_TIMEOUT,
retries--);
+ if (scsi_sense_valid(sshdr) &&
+ sshdr->sense_key == UNIT_ATTENTION)
+ sdev->changed = 1;
} while (retries > 0 &&
(!scsi_status_is_good(the_result) ||
diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c
index ce6ee92b3a1b..f3c3071a8bac 100644
--- a/drivers/serial/sh-sci.c
+++ b/drivers/serial/sh-sci.c
@@ -410,7 +410,6 @@ static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
#endif
#if defined(CONFIG_CPU_SUBTYPE_SH7760) || \
- defined(CONFIG_CPU_SUBTYPE_SH7763) || \
defined(CONFIG_CPU_SUBTYPE_SH7780) || \
defined(CONFIG_CPU_SUBTYPE_SH7785)
static inline int scif_txroom(struct uart_port *port)
@@ -422,6 +421,22 @@ static inline int scif_rxroom(struct uart_port *port)
{
return sci_in(port, SCRFDR) & 0xff;
}
+#elif defined(CONFIG_CPU_SUBTYPE_SH7763)
+static inline int scif_txroom(struct uart_port *port)
+{
+ if((port->mapbase == 0xffe00000) || (port->mapbase == 0xffe08000)) /* SCIF0/1*/
+ return SCIF_TXROOM_MAX - (sci_in(port, SCTFDR) & 0xff);
+ else /* SCIF2 */
+ return SCIF2_TXROOM_MAX - (sci_in(port, SCFDR) >> 8);
+}
+
+static inline int scif_rxroom(struct uart_port *port)
+{
+ if((port->mapbase == 0xffe00000) || (port->mapbase == 0xffe08000)) /* SCIF0/1*/
+ return sci_in(port, SCRFDR) & 0xff;
+ else /* SCIF2 */
+ return sci_in(port, SCFDR) & SCIF2_RFDC_MASK;
+}
#else
static inline int scif_txroom(struct uart_port *port)
{
diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h
index eb84833233fd..cd728df6a01a 100644
--- a/drivers/serial/sh-sci.h
+++ b/drivers/serial/sh-sci.h
@@ -123,8 +123,9 @@
#elif defined(CONFIG_CPU_SUBTYPE_SH7763)
# define SCSPTR0 0xffe00024 /* 16 bit SCIF */
# define SCSPTR1 0xffe08024 /* 16 bit SCIF */
+# define SCSPTR2 0xffe10020 /* 16 bit SCIF/IRDA */
# define SCIF_ORER 0x0001 /* overrun error bit */
-# define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
+# define SCSCR_INIT(port) 0x38 /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
# define SCIF_ONLY
#elif defined(CONFIG_CPU_SUBTYPE_SH7770)
# define SCSPTR0 0xff923020 /* 16 bit SCIF */
@@ -188,6 +189,7 @@
defined(CONFIG_CPU_SUBTYPE_SH7750S) || \
defined(CONFIG_CPU_SUBTYPE_SH7751) || \
defined(CONFIG_CPU_SUBTYPE_SH7751R) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7763) || \
defined(CONFIG_CPU_SUBTYPE_SH7780) || \
defined(CONFIG_CPU_SUBTYPE_SH7785) || \
defined(CONFIG_CPU_SUBTYPE_SHX3)
@@ -225,14 +227,21 @@
#if defined(CONFIG_CPU_SUBTYPE_SH7705) || \
defined(CONFIG_CPU_SUBTYPE_SH7720) || \
defined(CONFIG_CPU_SUBTYPE_SH7721)
-#define SCIF_ORER 0x0200
-#define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK | SCIF_ORER)
-#define SCIF_RFDC_MASK 0x007f
-#define SCIF_TXROOM_MAX 64
+# define SCIF_ORER 0x0200
+# define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK | SCIF_ORER)
+# define SCIF_RFDC_MASK 0x007f
+# define SCIF_TXROOM_MAX 64
+#elif defined(CONFIG_CPU_SUBTYPE_SH7763)
+# define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK )
+# define SCIF_RFDC_MASK 0x007f
+# define SCIF_TXROOM_MAX 64
+/* SH7763 SCIF2 support */
+# define SCIF2_RFDC_MASK 0x001f
+# define SCIF2_TXROOM_MAX 16
#else
-#define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK)
-#define SCIF_RFDC_MASK 0x001f
-#define SCIF_TXROOM_MAX 16
+# define SCIF_ERRORS ( SCIF_PER | SCIF_FER | SCIF_ER | SCIF_BRK)
+# define SCIF_RFDC_MASK 0x001f
+# define SCIF_TXROOM_MAX 16
#endif
#if defined(SCI_ONLY)
@@ -445,11 +454,16 @@ SCIF_FNS(SCFCR, 0x0c, 8, 0x18, 16)
defined(CONFIG_CPU_SUBTYPE_SH7763) || \
defined(CONFIG_CPU_SUBTYPE_SH7780) || \
defined(CONFIG_CPU_SUBTYPE_SH7785)
-SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16)
SCIF_FNS(SCTFDR, 0x0e, 16, 0x1C, 16)
SCIF_FNS(SCRFDR, 0x0e, 16, 0x20, 16)
SCIF_FNS(SCSPTR, 0, 0, 0x24, 16)
SCIF_FNS(SCLSR, 0, 0, 0x28, 16)
+#if defined(CONFIG_CPU_SUBTYPE_SH7763)
+/* SH7763 SCIF2 */
+SCIF_FNS(SCFDR, 0, 0, 0x1C, 16)
+SCIF_FNS(SCSPTR2, 0, 0, 0x20, 16)
+SCIF_FNS(SCLSR2, 0, 0, 0x24, 16)
+#endif /* CONFIG_CPU_SUBTYPE_SH7763 */
#else
SCIF_FNS(SCFDR, 0x0e, 16, 0x1C, 16)
#if defined(CONFIG_CPU_SUBTYPE_SH7722)
@@ -652,6 +666,9 @@ static inline int sci_rxd_in(struct uart_port *port)
return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
if (port->mapbase == 0xffe08000)
return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
+ if (port->mapbase == 0xffe10000)
+ return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF/IRDA */
+
return 1;
}
#elif defined(CONFIG_CPU_SUBTYPE_SH7770)
@@ -764,8 +781,7 @@ static inline int sci_rxd_in(struct uart_port *port)
* -- Mitch Davis - 15 Jul 2000
*/
-#if defined(CONFIG_CPU_SUBTYPE_SH7763) || \
- defined(CONFIG_CPU_SUBTYPE_SH7780) || \
+#if defined(CONFIG_CPU_SUBTYPE_SH7780) || \
defined(CONFIG_CPU_SUBTYPE_SH7785)
#define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(16*bps)-1)
#elif defined(CONFIG_CPU_SUBTYPE_SH7705) || \
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 1ad12afc6ba0..1771b2456bfa 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -502,7 +502,7 @@ struct spi_master *spi_busnum_to_master(u16 bus_num)
struct device *dev;
struct spi_master *master = NULL;
- dev = class_find_device(&spi_master_class, &bus_num,
+ dev = class_find_device(&spi_master_class, NULL, &bus_num,
__spi_master_match);
if (dev)
master = container_of(dev, struct spi_master, dev);
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
index 799337f7fde1..55b58ea40273 100644
--- a/drivers/spi/spidev.c
+++ b/drivers/spi/spidev.c
@@ -575,7 +575,8 @@ static int spidev_probe(struct spi_device *spi)
struct device *dev;
spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
- dev = device_create(spidev_class, &spi->dev, spidev->devt,
+ dev = device_create_drvdata(spidev_class, &spi->dev,
+ spidev->devt, spidev,
"spidev%d.%d",
spi->master->bus_num, spi->chip_select);
status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
@@ -585,7 +586,6 @@ static int spidev_probe(struct spi_device *spi)
}
if (status == 0) {
set_bit(minor, minors);
- spi_set_drvdata(spi, spidev);
list_add(&spidev->device_entry, &device_list);
}
mutex_unlock(&device_list_lock);
diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig
index a4aaab9c7ddc..2e9079df26b3 100644
--- a/drivers/uio/Kconfig
+++ b/drivers/uio/Kconfig
@@ -15,7 +15,7 @@ if UIO
config UIO_CIF
tristate "generic Hilscher CIF Card driver"
- depends on UIO && PCI
+ depends on PCI
default n
help
Driver for Hilscher CIF DeviceNet and Profibus cards. This
@@ -26,9 +26,15 @@ config UIO_CIF
To compile this driver as a module, choose M here: the module
will be called uio_cif.
+config UIO_PDRV
+ tristate "Userspace I/O platform driver"
+ help
+ Generic platform driver for Userspace I/O devices.
+
+ If you don't know what to do here, say N.
+
config UIO_SMX
tristate "SMX cryptengine UIO interface"
- depends on UIO
default n
help
Userspace IO interface to the Cryptography engine found on the
diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile
index 18c45662431e..e00ce0def1a0 100644
--- a/drivers/uio/Makefile
+++ b/drivers/uio/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_UIO) += uio.o
obj-$(CONFIG_UIO_CIF) += uio_cif.o
+obj-$(CONFIG_UIO_PDRV) += uio_pdrv.o
obj-$(CONFIG_UIO_SMX) += uio_smx.o
diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c
index 0a12e90ad416..626b58ebf188 100644
--- a/drivers/uio/uio.c
+++ b/drivers/uio/uio.c
@@ -420,6 +420,31 @@ static ssize_t uio_read(struct file *filep, char __user *buf,
return retval;
}
+static ssize_t uio_write(struct file *filep, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct uio_listener *listener = filep->private_data;
+ struct uio_device *idev = listener->dev;
+ ssize_t retval;
+ s32 irq_on;
+
+ if (idev->info->irq == UIO_IRQ_NONE)
+ return -EIO;
+
+ if (count != sizeof(s32))
+ return -EINVAL;
+
+ if (!idev->info->irqcontrol)
+ return -ENOSYS;
+
+ if (copy_from_user(&irq_on, buf, count))
+ return -EFAULT;
+
+ retval = idev->info->irqcontrol(idev->info, irq_on);
+
+ return retval ? retval : sizeof(s32);
+}
+
static int uio_find_mem_index(struct vm_area_struct *vma)
{
int mi;
@@ -539,6 +564,7 @@ static const struct file_operations uio_fops = {
.open = uio_open,
.release = uio_release,
.read = uio_read,
+ .write = uio_write,
.mmap = uio_mmap,
.poll = uio_poll,
.fasync = uio_fasync,
diff --git a/drivers/uio/uio_pdrv.c b/drivers/uio/uio_pdrv.c
new file mode 100644
index 000000000000..5d0d2e85d982
--- /dev/null
+++ b/drivers/uio/uio_pdrv.c
@@ -0,0 +1,118 @@
+/*
+ * drivers/uio/uio_pdrv.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * 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.
+ */
+#include <linux/platform_device.h>
+#include <linux/uio_driver.h>
+#include <linux/stringify.h>
+
+#define DRIVER_NAME "uio"
+
+struct uio_platdata {
+ struct uio_info *uioinfo;
+};
+
+static int uio_pdrv_probe(struct platform_device *pdev)
+{
+ struct uio_info *uioinfo = pdev->dev.platform_data;
+ struct uio_platdata *pdata;
+ struct uio_mem *uiomem;
+ int ret = -ENODEV;
+ int i;
+
+ if (!uioinfo || !uioinfo->name || !uioinfo->version) {
+ dev_dbg(&pdev->dev, "%s: err_uioinfo\n", __func__);
+ goto err_uioinfo;
+ }
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ ret = -ENOMEM;
+ dev_dbg(&pdev->dev, "%s: err_alloc_pdata\n", __func__);
+ goto err_alloc_pdata;
+ }
+
+ pdata->uioinfo = uioinfo;
+
+ uiomem = &uioinfo->mem[0];
+
+ for (i = 0; i < pdev->num_resources; ++i) {
+ struct resource *r = &pdev->resource[i];
+
+ if (r->flags != IORESOURCE_MEM)
+ continue;
+
+ if (uiomem >= &uioinfo->mem[MAX_UIO_MAPS]) {
+ dev_warn(&pdev->dev, "device has more than "
+ __stringify(MAX_UIO_MAPS)
+ " I/O memory resources.\n");
+ break;
+ }
+
+ uiomem->memtype = UIO_MEM_PHYS;
+ uiomem->addr = r->start;
+ uiomem->size = r->end - r->start + 1;
+ ++uiomem;
+ }
+
+ while (uiomem < &uioinfo->mem[MAX_UIO_MAPS]) {
+ uiomem->size = 0;
+ ++uiomem;
+ }
+
+ pdata->uioinfo->priv = pdata;
+
+ ret = uio_register_device(&pdev->dev, pdata->uioinfo);
+
+ if (ret) {
+ kfree(pdata);
+err_alloc_pdata:
+err_uioinfo:
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, pdata);
+
+ return 0;
+}
+
+static int uio_pdrv_remove(struct platform_device *pdev)
+{
+ struct uio_platdata *pdata = platform_get_drvdata(pdev);
+
+ uio_unregister_device(pdata->uioinfo);
+
+ return 0;
+}
+
+static struct platform_driver uio_pdrv = {
+ .probe = uio_pdrv_probe,
+ .remove = uio_pdrv_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init uio_pdrv_init(void)
+{
+ return platform_driver_register(&uio_pdrv);
+}
+
+static void __exit uio_pdrv_exit(void)
+{
+ platform_driver_unregister(&uio_pdrv);
+}
+module_init(uio_pdrv_init);
+module_exit(uio_pdrv_exit);
+
+MODULE_AUTHOR("Uwe Kleine-Koenig");
+MODULE_DESCRIPTION("Userspace I/O platform driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRIVER_NAME);
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 755823cdf62a..10e291892430 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -95,6 +95,8 @@ config USB
source "drivers/usb/core/Kconfig"
+source "drivers/usb/wusbcore/Kconfig"
+
source "drivers/usb/host/Kconfig"
source "drivers/usb/class/Kconfig"
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index a419c42e880e..8b7c419b876e 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -16,9 +16,12 @@ obj-$(CONFIG_USB_UHCI_HCD) += host/
obj-$(CONFIG_USB_SL811_HCD) += host/
obj-$(CONFIG_USB_U132_HCD) += host/
obj-$(CONFIG_USB_R8A66597_HCD) += host/
+obj-$(CONFIG_USB_HWA_HCD) += host/
obj-$(CONFIG_USB_C67X00_HCD) += c67x00/
+obj-$(CONFIG_USB_WUSB) += wusbcore/
+
obj-$(CONFIG_USB_ACM) += class/
obj-$(CONFIG_USB_PRINTER) += class/
diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c
index 5ea3093bc40f..90583d6a5949 100644
--- a/drivers/usb/atm/cxacru.c
+++ b/drivers/usb/atm/cxacru.c
@@ -820,7 +820,7 @@ reschedule:
}
static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw,
- u8 code1, u8 code2, u32 addr, u8 *data, int size)
+ u8 code1, u8 code2, u32 addr, const u8 *data, int size)
{
int ret;
u8 *buf;
diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c
index 7d27c9cf3c43..76fce44c2f9a 100644
--- a/drivers/usb/atm/speedtch.c
+++ b/drivers/usb/atm/speedtch.c
@@ -829,7 +829,6 @@ static int speedtch_bind(struct usbatm_data *usbatm,
if (use_isoc) {
const struct usb_host_interface *desc = data_intf->cur_altsetting;
const __u8 target_address = USB_DIR_IN | usbatm->driver->isoc_in;
- int i;
use_isoc = 0; /* fall back to bulk if endpoint not found */
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index 5f71ff3aee35..cb01b5106efd 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -579,7 +579,7 @@ MODULE_PARM_DESC(annex,
* uea_send_modem_cmd - Send a command for pre-firmware devices.
*/
static int uea_send_modem_cmd(struct usb_device *usb,
- u16 addr, u16 size, u8 * buff)
+ u16 addr, u16 size, const u8 *buff)
{
int ret = -ENOMEM;
u8 *xfer_buff;
@@ -604,7 +604,8 @@ static int uea_send_modem_cmd(struct usb_device *usb,
static void uea_upload_pre_firmware(const struct firmware *fw_entry, void *context)
{
struct usb_device *usb = context;
- u8 *pfw, value;
+ const u8 *pfw;
+ u8 value;
u32 crc = 0;
int ret, size;
@@ -720,7 +721,7 @@ static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
/*
* Make sure that the DSP code provided is safe to use.
*/
-static int check_dsp_e1(u8 *dsp, unsigned int len)
+static int check_dsp_e1(const u8 *dsp, unsigned int len)
{
u8 pagecount, blockcount;
u16 blocksize;
@@ -771,7 +772,7 @@ static int check_dsp_e1(u8 *dsp, unsigned int len)
return 0;
}
-static int check_dsp_e4(u8 *dsp, int len)
+static int check_dsp_e4(const u8 *dsp, int len)
{
int i;
struct l1_code *p = (struct l1_code *) dsp;
@@ -819,7 +820,7 @@ static int check_dsp_e4(u8 *dsp, int len)
/*
* send data to the idma pipe
* */
-static int uea_idma_write(struct uea_softc *sc, void *data, u32 size)
+static int uea_idma_write(struct uea_softc *sc, const void *data, u32 size)
{
int ret = -ENOMEM;
u8 *xfer_buff;
@@ -903,7 +904,7 @@ static void uea_load_page_e1(struct work_struct *work)
u16 ovl = sc->ovl;
struct block_info_e1 bi;
- u8 *p;
+ const u8 *p;
u8 pagecount, blockcount;
u16 blockaddr, blocksize;
u32 pageoffset;
@@ -986,7 +987,7 @@ static void __uea_load_page_e4(struct uea_softc *sc, u8 pageno, int boot)
bi.wReserved = cpu_to_be16(UEA_RESERVED);
do {
- u8 *blockoffset;
+ const u8 *blockoffset;
unsigned int blocksize;
blockidx = &p->page_header[blockno];
@@ -1095,7 +1096,7 @@ static inline int wait_cmv_ack(struct uea_softc *sc)
#define UCDC_SEND_ENCAPSULATED_COMMAND 0x00
static int uea_request(struct uea_softc *sc,
- u16 value, u16 index, u16 size, void *data)
+ u16 value, u16 index, u16 size, const void *data)
{
u8 *xfer_buff;
int ret = -ENOMEM;
@@ -1891,7 +1892,8 @@ static int load_XILINX_firmware(struct uea_softc *sc)
{
const struct firmware *fw_entry;
int ret, size, u, ln;
- u8 *pfw, value;
+ const u8 *pfw;
+ u8 value;
char *fw_name = FW_DIR "930-fpga.bin";
uea_enters(INS_TO_USBDEV(sc));
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index 83d9dc379d96..a681d9b92bda 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -46,8 +46,6 @@
* 2000-07-05: Ashley Montanaro <ashley@compsoc.man.ac.uk>
* Converted file reading routine to dump to buffer once
* per device, not per bus
- *
- * $Id: devices.c,v 1.5 2000/01/11 13:58:21 tom Exp $
*/
#include <linux/fs.h>
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index de17738f3acb..6d82defc6e46 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -19,8 +19,6 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: devio.c,v 1.7 2000/02/01 17:28:48 fliegl Exp $
- *
* This file implements the usbfs/x/y files, where
* x is the bus number and y the device number.
*
@@ -1687,9 +1685,13 @@ static int usb_classdev_add(struct usb_device *dev)
{
int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1);
- dev->usb_classdev = device_create(usb_classdev_class, &dev->dev,
- MKDEV(USB_DEVICE_MAJOR, minor),
- "usbdev%d.%d", dev->bus->busnum, dev->devnum);
+ dev->usb_classdev = device_create_drvdata(usb_classdev_class,
+ &dev->dev,
+ MKDEV(USB_DEVICE_MAJOR,
+ minor), NULL,
+ "usbdev%d.%d",
+ dev->bus->busnum,
+ dev->devnum);
if (IS_ERR(dev->usb_classdev))
return PTR_ERR(dev->usb_classdev);
@@ -1748,6 +1750,11 @@ int __init usb_devio_init(void)
usb_classdev_class = NULL;
goto out;
}
+ /* devices of this class shadow the major:minor of their parent
+ * device, so clear ->dev_kobj to prevent adding duplicate entries
+ * to /sys/dev
+ */
+ usb_classdev_class->dev_kobj = NULL;
usb_register_notify(&usbdev_nb);
#endif
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 1e56f1cfa6dc..8da1a56659be 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -257,15 +257,16 @@ static int usb_unbind_interface(struct device *dev)
udev = interface_to_usbdev(intf);
error = usb_autoresume_device(udev);
- /* release all urbs for this interface */
- usb_disable_interface(interface_to_usbdev(intf), intf);
+ /* Terminate all URBs for this interface unless the driver
+ * supports "soft" unbinding.
+ */
+ if (!driver->soft_unbind)
+ usb_disable_interface(udev, intf);
driver->disconnect(intf);
/* reset other interface state */
- usb_set_interface(interface_to_usbdev(intf),
- intf->altsetting[0].desc.bInterfaceNumber,
- 0);
+ usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0);
usb_set_intfdata(intf, NULL);
intf->condition = USB_INTERFACE_UNBOUND;
@@ -586,7 +587,7 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
struct usb_device *usb_dev;
/* driver is often null here; dev_dbg() would oops */
- pr_debug("usb %s: uevent\n", dev->bus_id);
+ pr_debug("usb %s: uevent\n", dev_name(dev));
if (is_usb_device(dev))
usb_dev = to_usb_device(dev);
@@ -596,11 +597,11 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
}
if (usb_dev->devnum < 0) {
- pr_debug("usb %s: already deleted?\n", dev->bus_id);
+ pr_debug("usb %s: already deleted?\n", dev_name(dev));
return -ENODEV;
}
if (!usb_dev->bus) {
- pr_debug("usb %s: bus removed?\n", dev->bus_id);
+ pr_debug("usb %s: bus removed?\n", dev_name(dev));
return -ENODEV;
}
@@ -805,8 +806,6 @@ static int usb_resume_device(struct usb_device *udev)
if (udev->state == USB_STATE_NOTATTACHED)
goto done;
- if (udev->state != USB_STATE_SUSPENDED && !udev->reset_resume)
- goto done;
/* Can't resume it if it doesn't have a driver. */
if (udev->dev.driver == NULL) {
@@ -1173,11 +1172,8 @@ static int usb_resume_both(struct usb_device *udev)
* then we're stuck. */
status = usb_resume_device(udev);
}
- } else {
-
- /* Needed for reset-resume */
+ } else if (udev->reset_resume)
status = usb_resume_device(udev);
- }
if (status == 0 && udev->actconfig) {
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
@@ -1542,14 +1538,11 @@ static int usb_resume(struct device *dev)
udev = to_usb_device(dev);
/* If udev->skip_sys_resume is set then udev was already suspended
- * when the system suspend started, so we don't want to resume
- * udev during this system wakeup. However a reset-resume counts
- * as a wakeup event, so allow a reset-resume to occur if remote
- * wakeup is enabled. */
- if (udev->skip_sys_resume) {
- if (!(udev->reset_resume && udev->do_remote_wakeup))
- return -EHOSTUNREACH;
- }
+ * when the system sleep started, so we don't want to resume it
+ * during this system wakeup.
+ */
+ if (udev->skip_sys_resume)
+ return 0;
return usb_external_resume_device(udev);
}
diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c
index fae55a31e26d..22912136fc14 100644
--- a/drivers/usb/core/endpoint.c
+++ b/drivers/usb/core/endpoint.c
@@ -296,7 +296,7 @@ int usb_create_ep_files(struct device *parent,
retval = endpoint_get_minor(ep_dev);
if (retval) {
dev_err(parent, "can not allocate minor number for %s\n",
- ep_dev->dev.bus_id);
+ dev_name(&ep_dev->dev));
goto error_register;
}
@@ -307,7 +307,7 @@ int usb_create_ep_files(struct device *parent,
ep_dev->dev.class = ep_class->class;
ep_dev->dev.parent = parent;
ep_dev->dev.release = ep_device_release;
- snprintf(ep_dev->dev.bus_id, BUS_ID_SIZE, "usbdev%d.%d_ep%02x",
+ dev_set_name(&ep_dev->dev, "usbdev%d.%d_ep%02x",
udev->bus->busnum, udev->devnum,
endpoint->desc.bEndpointAddress);
diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c
index 8133c99c6c5c..b78c7fd99d4f 100644
--- a/drivers/usb/core/file.c
+++ b/drivers/usb/core/file.c
@@ -147,7 +147,7 @@ int usb_register_dev(struct usb_interface *intf,
int retval = -EINVAL;
int minor_base = class_driver->minor_base;
int minor = 0;
- char name[BUS_ID_SIZE];
+ char name[20];
char *temp;
#ifdef CONFIG_USB_DYNAMIC_MINORS
@@ -187,14 +187,15 @@ int usb_register_dev(struct usb_interface *intf,
intf->minor = minor;
/* create a usb class device for this usb interface */
- snprintf(name, BUS_ID_SIZE, class_driver->name, minor - minor_base);
+ snprintf(name, sizeof(name), class_driver->name, minor - minor_base);
temp = strrchr(name, '/');
- if (temp && (temp[1] != 0x00))
+ if (temp && (temp[1] != '\0'))
++temp;
else
temp = name;
- intf->usb_dev = device_create(usb_class->class, &intf->dev,
- MKDEV(USB_MAJOR, minor), "%s", temp);
+ intf->usb_dev = device_create_drvdata(usb_class->class, &intf->dev,
+ MKDEV(USB_MAJOR, minor), NULL,
+ "%s", temp);
if (IS_ERR(intf->usb_dev)) {
down_write(&minor_rwsem);
usb_minors[intf->minor] = NULL;
@@ -224,7 +225,7 @@ void usb_deregister_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver)
{
int minor_base = class_driver->minor_base;
- char name[BUS_ID_SIZE];
+ char name[20];
#ifdef CONFIG_USB_DYNAMIC_MINORS
minor_base = 0;
@@ -239,7 +240,7 @@ void usb_deregister_dev(struct usb_interface *intf,
usb_minors[intf->minor] = NULL;
up_write(&minor_rwsem);
- snprintf(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base);
+ snprintf(name, sizeof(name), class_driver->name, intf->minor - minor_base);
device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor));
intf->usb_dev = NULL;
intf->minor = -1;
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 09a53e7f3327..d49cbc16e286 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -900,14 +900,14 @@ static int register_root_hub(struct usb_hcd *hcd)
if (retval != sizeof usb_dev->descriptor) {
mutex_unlock(&usb_bus_list_lock);
dev_dbg (parent_dev, "can't read %s device descriptor %d\n",
- usb_dev->dev.bus_id, retval);
+ dev_name(&usb_dev->dev), retval);
return (retval < 0) ? retval : -EMSGSIZE;
}
retval = usb_new_device (usb_dev);
if (retval) {
dev_err (parent_dev, "can't register root hub for %s, %d\n",
- usb_dev->dev.bus_id, retval);
+ dev_name(&usb_dev->dev), retval);
}
mutex_unlock(&usb_bus_list_lock);
@@ -1744,7 +1744,7 @@ EXPORT_SYMBOL_GPL (usb_hc_died);
* If memory is unavailable, returns NULL.
*/
struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
- struct device *dev, char *bus_name)
+ struct device *dev, const char *bus_name)
{
struct usb_hcd *hcd;
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index a0bf5df6cb6f..ee247da88c73 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -233,7 +233,7 @@ extern void usb_hcd_disable_endpoint(struct usb_device *udev,
extern int usb_hcd_get_frame_number(struct usb_device *udev);
extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
- struct device *dev, char *bus_name);
+ struct device *dev, const char *bus_name);
extern struct usb_hcd *usb_get_hcd(struct usb_hcd *hcd);
extern void usb_put_hcd(struct usb_hcd *hcd);
extern int usb_add_hcd(struct usb_hcd *hcd,
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 8eb4da332f56..0f04c00651c4 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -72,7 +72,6 @@ struct usb_hub {
unsigned limited_power:1;
unsigned quiescing:1;
- unsigned activating:1;
unsigned disconnected:1;
unsigned has_indicators:1;
@@ -131,6 +130,10 @@ MODULE_PARM_DESC(use_both_schemes,
DECLARE_RWSEM(ehci_cf_port_reset_rwsem);
EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
+#define HUB_DEBOUNCE_TIMEOUT 1500
+#define HUB_DEBOUNCE_STEP 25
+#define HUB_DEBOUNCE_STABLE 100
+
static inline char *portspeed(int portstatus)
{
@@ -535,37 +538,6 @@ static void hub_power_on(struct usb_hub *hub)
msleep(max(pgood_delay, (unsigned) 100));
}
-static void hub_quiesce(struct usb_hub *hub)
-{
- /* (nonblocking) khubd and related activity won't re-trigger */
- hub->quiescing = 1;
- hub->activating = 0;
-
- /* (blocking) stop khubd and related activity */
- usb_kill_urb(hub->urb);
- if (hub->has_indicators)
- cancel_delayed_work_sync(&hub->leds);
- if (hub->tt.hub)
- cancel_work_sync(&hub->tt.kevent);
-}
-
-static void hub_activate(struct usb_hub *hub)
-{
- int status;
-
- hub->quiescing = 0;
- hub->activating = 1;
-
- status = usb_submit_urb(hub->urb, GFP_NOIO);
- if (status < 0)
- dev_err(hub->intfdev, "activate --> %d\n", status);
- if (hub->has_indicators && blinkenlights)
- schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
-
- /* scan all ports ASAP */
- kick_khubd(hub);
-}
-
static int hub_hub_status(struct usb_hub *hub,
u16 *status, u16 *change)
{
@@ -624,101 +596,149 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
kick_khubd(hub);
}
-/* caller has locked the hub device */
-static void hub_stop(struct usb_hub *hub)
-{
- struct usb_device *hdev = hub->hdev;
- int i;
-
- /* Disconnect all the children */
- for (i = 0; i < hdev->maxchild; ++i) {
- if (hdev->children[i])
- usb_disconnect(&hdev->children[i]);
- }
- hub_quiesce(hub);
-}
-
-#define HUB_RESET 1
-#define HUB_RESUME 2
-#define HUB_RESET_RESUME 3
-
-#ifdef CONFIG_PM
+enum hub_activation_type {
+ HUB_INIT, HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME
+};
-static void hub_restart(struct usb_hub *hub, int type)
+static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
{
struct usb_device *hdev = hub->hdev;
int port1;
+ int status;
+ bool need_debounce_delay = false;
+
+ /* After a resume, port power should still be on.
+ * For any other type of activation, turn it on.
+ */
+ if (type != HUB_RESUME)
+ hub_power_on(hub);
- /* Check each of the children to see if they require
- * USB-PERSIST handling or disconnection. Also check
- * each unoccupied port to make sure it is still disabled.
+ /* Check each port and set hub->change_bits to let khubd know
+ * which ports need attention.
*/
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
struct usb_device *udev = hdev->children[port1-1];
- int status = 0;
u16 portstatus, portchange;
- if (!udev || udev->state == USB_STATE_NOTATTACHED) {
- if (type != HUB_RESET) {
- status = hub_port_status(hub, port1,
- &portstatus, &portchange);
- if (status == 0 && (portstatus &
- USB_PORT_STAT_ENABLE))
- clear_port_feature(hdev, port1,
- USB_PORT_FEAT_ENABLE);
- }
- continue;
- }
+ portstatus = portchange = 0;
+ status = hub_port_status(hub, port1, &portstatus, &portchange);
+ if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
+ dev_dbg(hub->intfdev,
+ "port %d: status %04x change %04x\n",
+ port1, portstatus, portchange);
- /* Was the power session lost while we were suspended? */
- switch (type) {
- case HUB_RESET_RESUME:
- portstatus = 0;
- portchange = USB_PORT_STAT_C_CONNECTION;
- break;
+ /* After anything other than HUB_RESUME (i.e., initialization
+ * or any sort of reset), every port should be disabled.
+ * Unconnected ports should likewise be disabled (paranoia),
+ * and so should ports for which we have no usb_device.
+ */
+ if ((portstatus & USB_PORT_STAT_ENABLE) && (
+ type != HUB_RESUME ||
+ !(portstatus & USB_PORT_STAT_CONNECTION) ||
+ !udev ||
+ udev->state == USB_STATE_NOTATTACHED)) {
+ clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
+ portstatus &= ~USB_PORT_STAT_ENABLE;
+ }
- case HUB_RESET:
- case HUB_RESUME:
- status = hub_port_status(hub, port1,
- &portstatus, &portchange);
- break;
+ /* Clear status-change flags; we'll debounce later */
+ if (portchange & USB_PORT_STAT_C_CONNECTION) {
+ need_debounce_delay = true;
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_CONNECTION);
+ }
+ if (portchange & USB_PORT_STAT_C_ENABLE) {
+ need_debounce_delay = true;
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_ENABLE);
}
- /* For "USB_PERSIST"-enabled children we must
- * mark the child device for reset-resume and
- * turn off the various status changes to prevent
- * khubd from disconnecting it later.
- */
- if (udev->persist_enabled && status == 0 &&
- !(portstatus & USB_PORT_STAT_ENABLE)) {
- if (portchange & USB_PORT_STAT_C_ENABLE)
- clear_port_feature(hub->hdev, port1,
- USB_PORT_FEAT_C_ENABLE);
- if (portchange & USB_PORT_STAT_C_CONNECTION)
- clear_port_feature(hub->hdev, port1,
- USB_PORT_FEAT_C_CONNECTION);
+ if (!udev || udev->state == USB_STATE_NOTATTACHED) {
+ /* Tell khubd to disconnect the device or
+ * check for a new connection
+ */
+ if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
+ set_bit(port1, hub->change_bits);
+
+ } else if (portstatus & USB_PORT_STAT_ENABLE) {
+ /* The power session apparently survived the resume.
+ * If there was an overcurrent or suspend change
+ * (i.e., remote wakeup request), have khubd
+ * take care of it.
+ */
+ if (portchange)
+ set_bit(port1, hub->change_bits);
+
+ } else if (udev->persist_enabled) {
+#ifdef CONFIG_PM
udev->reset_resume = 1;
- }
+#endif
+ set_bit(port1, hub->change_bits);
- /* Otherwise for a reset_resume we must disconnect the child,
- * but as we may not lock the child device here
- * we have to do a "logical" disconnect.
- */
- else if (type == HUB_RESET_RESUME)
- hub_port_logical_disconnect(hub, port1);
+ } else {
+ /* The power session is gone; tell khubd */
+ usb_set_device_state(udev, USB_STATE_NOTATTACHED);
+ set_bit(port1, hub->change_bits);
+ }
}
- hub_activate(hub);
+ /* If no port-status-change flags were set, we don't need any
+ * debouncing. If flags were set we can try to debounce the
+ * ports all at once right now, instead of letting khubd do them
+ * one at a time later on.
+ *
+ * If any port-status changes do occur during this delay, khubd
+ * will see them later and handle them normally.
+ */
+ if (need_debounce_delay)
+ msleep(HUB_DEBOUNCE_STABLE);
+
+ hub->quiescing = 0;
+
+ status = usb_submit_urb(hub->urb, GFP_NOIO);
+ if (status < 0)
+ dev_err(hub->intfdev, "activate --> %d\n", status);
+ if (hub->has_indicators && blinkenlights)
+ schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
+
+ /* Scan all ports that need attention */
+ kick_khubd(hub);
}
-#endif /* CONFIG_PM */
+enum hub_quiescing_type {
+ HUB_DISCONNECT, HUB_PRE_RESET, HUB_SUSPEND
+};
+
+static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
+{
+ struct usb_device *hdev = hub->hdev;
+ int i;
+
+ /* khubd and related activity won't re-trigger */
+ hub->quiescing = 1;
+
+ if (type != HUB_SUSPEND) {
+ /* Disconnect all the children */
+ for (i = 0; i < hdev->maxchild; ++i) {
+ if (hdev->children[i])
+ usb_disconnect(&hdev->children[i]);
+ }
+ }
+
+ /* Stop khubd and related activity */
+ usb_kill_urb(hub->urb);
+ if (hub->has_indicators)
+ cancel_delayed_work_sync(&hub->leds);
+ if (hub->tt.hub)
+ cancel_work_sync(&hub->tt.kevent);
+}
/* caller has locked the hub device */
static int hub_pre_reset(struct usb_interface *intf)
{
struct usb_hub *hub = usb_get_intfdata(intf);
- hub_stop(hub);
+ hub_quiesce(hub, HUB_PRE_RESET);
return 0;
}
@@ -727,8 +747,7 @@ static int hub_post_reset(struct usb_interface *intf)
{
struct usb_hub *hub = usb_get_intfdata(intf);
- hub_power_on(hub);
- hub_activate(hub);
+ hub_activate(hub, HUB_POST_RESET);
return 0;
}
@@ -974,8 +993,7 @@ static int hub_configure(struct usb_hub *hub,
if (hub->has_indicators && blinkenlights)
hub->indicator [0] = INDICATOR_CYCLE;
- hub_power_on(hub);
- hub_activate(hub);
+ hub_activate(hub, HUB_INIT);
return 0;
fail:
@@ -1007,7 +1025,7 @@ static void hub_disconnect(struct usb_interface *intf)
/* Disconnect all children and quiesce the hub */
hub->error = 0;
- hub_stop(hub);
+ hub_quiesce(hub, HUB_DISCONNECT);
usb_set_intfdata (intf, NULL);
@@ -1779,6 +1797,45 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
#ifdef CONFIG_PM
+#define MASK_BITS (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION | \
+ USB_PORT_STAT_SUSPEND)
+#define WANT_BITS (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION)
+
+/* Determine whether the device on a port is ready for a normal resume,
+ * is ready for a reset-resume, or should be disconnected.
+ */
+static int check_port_resume_type(struct usb_device *udev,
+ struct usb_hub *hub, int port1,
+ int status, unsigned portchange, unsigned portstatus)
+{
+ /* Is the device still present? */
+ if (status || (portstatus & MASK_BITS) != WANT_BITS) {
+ if (status >= 0)
+ status = -ENODEV;
+ }
+
+ /* Can't do a normal resume if the port isn't enabled */
+ else if (!(portstatus & USB_PORT_STAT_ENABLE) && !udev->reset_resume)
+ status = -ENODEV;
+
+ if (status) {
+ dev_dbg(hub->intfdev,
+ "port %d status %04x.%04x after resume, %d\n",
+ port1, portchange, portstatus, status);
+ } else if (udev->reset_resume) {
+
+ /* Late port handoff can set status-change bits */
+ if (portchange & USB_PORT_STAT_C_CONNECTION)
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_CONNECTION);
+ if (portchange & USB_PORT_STAT_C_ENABLE)
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_ENABLE);
+ }
+
+ return status;
+}
+
#ifdef CONFIG_USB_SUSPEND
/*
@@ -1983,7 +2040,6 @@ int usb_port_resume(struct usb_device *udev)
int port1 = udev->portnum;
int status;
u16 portchange, portstatus;
- unsigned mask_flags, want_flags;
/* Skip the initial Clear-Suspend step for a remote wakeup */
status = hub_port_status(hub, port1, &portstatus, &portchange);
@@ -2012,33 +2068,20 @@ int usb_port_resume(struct usb_device *udev)
*/
status = hub_port_status(hub, port1, &portstatus, &portchange);
- SuspendCleared:
- if (udev->reset_resume)
- want_flags = USB_PORT_STAT_POWER
- | USB_PORT_STAT_CONNECTION;
- else
- want_flags = USB_PORT_STAT_POWER
- | USB_PORT_STAT_CONNECTION
- | USB_PORT_STAT_ENABLE;
- mask_flags = want_flags | USB_PORT_STAT_SUSPEND;
-
- if (status < 0 || (portstatus & mask_flags) != want_flags) {
- dev_dbg(hub->intfdev,
- "port %d status %04x.%04x after resume, %d\n",
- port1, portchange, portstatus, status);
- if (status >= 0)
- status = -ENODEV;
- } else {
- if (portchange & USB_PORT_STAT_C_SUSPEND)
- clear_port_feature(hub->hdev, port1,
- USB_PORT_FEAT_C_SUSPEND);
- /* TRSMRCY = 10 msec */
- msleep(10);
- }
+ /* TRSMRCY = 10 msec */
+ msleep(10);
}
+ SuspendCleared:
+ if (status == 0) {
+ if (portchange & USB_PORT_STAT_C_SUSPEND)
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_SUSPEND);
+ }
clear_bit(port1, hub->busy_bits);
+ status = check_port_resume_type(udev,
+ hub, port1, status, portchange, portstatus);
if (status == 0)
status = finish_port_resume(udev);
if (status < 0) {
@@ -2048,17 +2091,16 @@ int usb_port_resume(struct usb_device *udev)
return status;
}
+/* caller has locked udev */
static int remote_wakeup(struct usb_device *udev)
{
int status = 0;
- usb_lock_device(udev);
if (udev->state == USB_STATE_SUSPENDED) {
dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
usb_mark_last_busy(udev);
status = usb_external_resume_device(udev);
}
- usb_unlock_device(udev);
return status;
}
@@ -2071,12 +2113,23 @@ int usb_port_suspend(struct usb_device *udev)
return 0;
}
+/* However we may need to do a reset-resume */
+
int usb_port_resume(struct usb_device *udev)
{
- int status = 0;
+ struct usb_hub *hub = hdev_to_hub(udev->parent);
+ int port1 = udev->portnum;
+ int status;
+ u16 portchange, portstatus;
+
+ status = hub_port_status(hub, port1, &portstatus, &portchange);
+ status = check_port_resume_type(udev,
+ hub, port1, status, portchange, portstatus);
- /* However we may need to do a reset-resume */
- if (udev->reset_resume) {
+ if (status) {
+ dev_dbg(&udev->dev, "can't resume, status %d\n", status);
+ hub_port_logical_disconnect(hub, port1);
+ } else if (udev->reset_resume) {
dev_dbg(&udev->dev, "reset-resume\n");
status = usb_reset_device(udev);
}
@@ -2112,7 +2165,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
dev_dbg(&intf->dev, "%s\n", __func__);
/* stop khubd and related activity */
- hub_quiesce(hub);
+ hub_quiesce(hub, HUB_SUSPEND);
return 0;
}
@@ -2121,7 +2174,7 @@ static int hub_resume(struct usb_interface *intf)
struct usb_hub *hub = usb_get_intfdata(intf);
dev_dbg(&intf->dev, "%s\n", __func__);
- hub_restart(hub, HUB_RESUME);
+ hub_activate(hub, HUB_RESUME);
return 0;
}
@@ -2130,8 +2183,7 @@ static int hub_reset_resume(struct usb_interface *intf)
struct usb_hub *hub = usb_get_intfdata(intf);
dev_dbg(&intf->dev, "%s\n", __func__);
- hub_power_on(hub);
- hub_restart(hub, HUB_RESET_RESUME);
+ hub_activate(hub, HUB_RESET_RESUME);
return 0;
}
@@ -2181,11 +2233,6 @@ static inline int remote_wakeup(struct usb_device *udev)
* every 25ms for transient disconnects. When the port status has been
* unchanged for 100ms it returns the port status.
*/
-
-#define HUB_DEBOUNCE_TIMEOUT 1500
-#define HUB_DEBOUNCE_STEP 25
-#define HUB_DEBOUNCE_STABLE 100
-
static int hub_port_debounce(struct usb_hub *hub, int port1)
{
int ret;
@@ -2592,9 +2639,11 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
struct usb_device *hdev = hub->hdev;
struct device *hub_dev = hub->intfdev;
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
- u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
+ unsigned wHubCharacteristics =
+ le16_to_cpu(hub->descriptor->wHubCharacteristics);
+ struct usb_device *udev;
int status, i;
-
+
dev_dbg (hub_dev,
"port %d, status %04x, change %04x, %s\n",
port1, portstatus, portchange, portspeed (portstatus));
@@ -2603,30 +2652,73 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
set_port_led(hub, port1, HUB_LED_AUTO);
hub->indicator[port1-1] = INDICATOR_AUTO;
}
-
- /* Disconnect any existing devices under this port */
- if (hdev->children[port1-1])
- usb_disconnect(&hdev->children[port1-1]);
- clear_bit(port1, hub->change_bits);
#ifdef CONFIG_USB_OTG
/* during HNP, don't repeat the debounce */
if (hdev->bus->is_b_host)
- portchange &= ~USB_PORT_STAT_C_CONNECTION;
+ portchange &= ~(USB_PORT_STAT_C_CONNECTION |
+ USB_PORT_STAT_C_ENABLE);
#endif
- if (portchange & USB_PORT_STAT_C_CONNECTION) {
+ /* Try to use the debounce delay for protection against
+ * port-enable changes caused, for example, by EMI.
+ */
+ if (portchange & (USB_PORT_STAT_C_CONNECTION |
+ USB_PORT_STAT_C_ENABLE)) {
status = hub_port_debounce(hub, port1);
if (status < 0) {
if (printk_ratelimit())
dev_err (hub_dev, "connect-debounce failed, "
"port %d disabled\n", port1);
- goto done;
+ portstatus &= ~USB_PORT_STAT_CONNECTION;
+ } else {
+ portstatus = status;
}
- portstatus = status;
}
- /* Return now if nothing is connected */
+ /* Try to resuscitate an existing device */
+ udev = hdev->children[port1-1];
+ if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&
+ udev->state != USB_STATE_NOTATTACHED) {
+
+ usb_lock_device(udev);
+ if (portstatus & USB_PORT_STAT_ENABLE) {
+ status = 0; /* Nothing to do */
+ } else if (!udev->persist_enabled) {
+ status = -ENODEV; /* Mustn't resuscitate */
+
+#ifdef CONFIG_USB_SUSPEND
+ } else if (udev->state == USB_STATE_SUSPENDED) {
+ /* For a suspended device, treat this as a
+ * remote wakeup event.
+ */
+ if (udev->do_remote_wakeup)
+ status = remote_wakeup(udev);
+
+ /* Otherwise leave it be; devices can't tell the
+ * difference between suspended and disabled.
+ */
+ else
+ status = 0;
+#endif
+
+ } else {
+ status = usb_reset_composite_device(udev, NULL);
+ }
+ usb_unlock_device(udev);
+
+ if (status == 0) {
+ clear_bit(port1, hub->change_bits);
+ return;
+ }
+ }
+
+ /* Disconnect any existing devices under this port */
+ if (udev)
+ usb_disconnect(&hdev->children[port1-1]);
+ clear_bit(port1, hub->change_bits);
+
+ /* Return now if debouncing failed or nothing is connected */
if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
/* maybe switch power back on (e.g. root hub was reset) */
@@ -2640,7 +2732,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
}
for (i = 0; i < SET_CONFIG_TRIES; i++) {
- struct usb_device *udev;
/* reallocate for each attempt, since references
* to the previous one can escape in various ways
@@ -2821,7 +2912,7 @@ static void hub_events(void)
/* If the hub has died, clean up after it */
if (hdev->state == USB_STATE_NOTATTACHED) {
hub->error = -ENODEV;
- hub_stop(hub);
+ hub_quiesce(hub, HUB_DISCONNECT);
goto loop;
}
@@ -2857,7 +2948,7 @@ static void hub_events(void)
continue;
connect_change = test_bit(i, hub->change_bits);
if (!test_and_clear_bit(i, hub->event_bits) &&
- !connect_change && !hub->activating)
+ !connect_change)
continue;
ret = hub_port_status(hub, i,
@@ -2865,11 +2956,6 @@ static void hub_events(void)
if (ret < 0)
continue;
- if (hub->activating && !hdev->children[i-1] &&
- (portstatus &
- USB_PORT_STAT_CONNECTION))
- connect_change = 1;
-
if (portchange & USB_PORT_STAT_C_CONNECTION) {
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_CONNECTION);
@@ -2904,11 +2990,16 @@ static void hub_events(void)
}
if (portchange & USB_PORT_STAT_C_SUSPEND) {
+ struct usb_device *udev;
+
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_SUSPEND);
- if (hdev->children[i-1]) {
+ udev = hdev->children[i-1];
+ if (udev) {
+ usb_lock_device(udev);
ret = remote_wakeup(hdev->
children[i-1]);
+ usb_unlock_device(udev);
if (ret < 0)
connect_change = 1;
} else {
@@ -2965,8 +3056,6 @@ static void hub_events(void)
}
}
- hub->activating = 0;
-
loop_autopm:
/* Allow autosuspend if we're not going to run again */
if (list_empty(&hub->event_list))
@@ -3317,3 +3406,86 @@ int usb_reset_composite_device(struct usb_device *udev,
return ret;
}
EXPORT_SYMBOL_GPL(usb_reset_composite_device);
+
+/* Support for delayed resets */
+struct usb_dev_reset_ctx {
+ struct work_struct ws;
+ struct usb_device *usb_dev;
+};
+
+/*
+ * We need to lock the device before resetting, but we may already have the
+ * lock. So we try to lock the device, if it fails (returns < 0) then we
+ * cannot proceed. If it returned 1 then we acquired the lock here and need
+ * to release the lock. If it returned 0 then we already have the lock and
+ * we leave it to the piece that acquired the lock to release it.
+ */
+static void usb_dev_reset_delayed_task(struct work_struct *ws)
+{
+ struct usb_dev_reset_ctx *reset_ctx =
+ container_of(ws, struct usb_dev_reset_ctx, ws);
+ struct usb_device *usb_dev = reset_ctx->usb_dev;
+ struct device *dev = &usb_dev->dev;
+ int had_to_lock;
+ int result = 0;
+
+ had_to_lock = usb_lock_device_for_reset(usb_dev, NULL);
+ if (had_to_lock < 0) {
+ if (had_to_lock != -ENODEV) /* ignore dissapearance */
+ dev_err(dev, "Cannot lock device for reset: %d\n",
+ had_to_lock);
+ } else {
+ result = usb_reset_device(usb_dev);
+ if (result < 0 && result != -ENODEV)
+ dev_err(dev, "Unable to reset device: %d\n", result);
+ if (had_to_lock)
+ usb_unlock_device(usb_dev);
+ }
+ usb_dev->delayed_reset = 0;
+ usb_put_dev(usb_dev);
+ module_put(THIS_MODULE);
+ kfree(reset_ctx);
+}
+
+/**
+ * usb_dev_reset_delayed: Schedule a delayed USB device reset
+ * @usb_dev: USB device that needs to be reset. We assume you have a valid
+ * reference on it (so it won't dissapear) until we take another one that
+ * we'll own.
+ *
+ * Allocates a context structure containing a workqueue struct with
+ * all the pertinent info; gets a reference to @usb_dev and schedules
+ * the call that will be executed later on.
+ *
+ * NOTE: for use in atomic contexts
+ */
+void usb_dev_reset_delayed(struct usb_device *usb_dev)
+{
+ struct usb_dev_reset_ctx *reset_ctx;
+ struct device *dev = &usb_dev->dev;
+ reset_ctx = kmalloc(sizeof(*reset_ctx), GFP_ATOMIC);
+ if (reset_ctx == NULL) {
+ if (printk_ratelimit())
+ dev_err(dev, "USB: cannot allocate memory for "
+ "delayed device reset\n");
+ return;
+ }
+ if (try_module_get(THIS_MODULE) == 0)
+ goto error_module_get;
+ usb_get_dev(usb_dev);
+ if (usb_dev->delayed_reset)
+ goto error_pending;
+ usb_dev->delayed_reset = 1;
+ reset_ctx->usb_dev = usb_dev;
+ INIT_WORK(&reset_ctx->ws, usb_dev_reset_delayed_task);
+ schedule_work(&reset_ctx->ws);
+ return;
+
+error_pending:
+ usb_put_dev(usb_dev);
+ module_put(THIS_MODULE);
+error_module_get:
+ kfree(reset_ctx);
+ return;
+}
+EXPORT_SYMBOL_GPL(usb_dev_reset_delayed);
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index fe47d145255a..315363b744a3 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1090,7 +1090,7 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
if (!device_is_registered(&interface->dev))
continue;
dev_dbg(&dev->dev, "unregistering interface %s\n",
- interface->dev.bus_id);
+ dev_name(&interface->dev));
device_del(&interface->dev);
usb_remove_sysfs_intf_files(interface);
}
@@ -1611,7 +1611,7 @@ free_interfaces:
intf->dev.dma_mask = dev->dev.dma_mask;
device_initialize(&intf->dev);
mark_quiesced(intf);
- sprintf(&intf->dev.bus_id[0], "%d-%s:%d.%d",
+ dev_set_name(&intf->dev, "%d-%s:%d.%d",
dev->bus->busnum, dev->devpath,
configuration, alt->desc.bInterfaceNumber);
}
@@ -1631,12 +1631,12 @@ free_interfaces:
dev_dbg(&dev->dev,
"adding %s (config #%d, interface %d)\n",
- intf->dev.bus_id, configuration,
+ dev_name(&intf->dev), configuration,
intf->cur_altsetting->desc.bInterfaceNumber);
ret = device_add(&intf->dev);
if (ret != 0) {
dev_err(&dev->dev, "device_add(%s) --> %d\n",
- intf->dev.bus_id, ret);
+ dev_name(&intf->dev), ret);
continue;
}
usb_create_sysfs_intf_files(intf);
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 325774375837..84fcaa6a21ec 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -308,7 +308,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
* by location for diagnostics, tools, driver model, etc. The
* string is a path along hub ports, from the root. Each device's
* dev->devpath will be stable until USB is re-cabled, and hubs
- * are often labeled with these port numbers. The bus_id isn't
+ * are often labeled with these port numbers. The name isn't
* as stable: bus->busnum changes easily from modprobe order,
* cardbus or pci hotplugging, and so on.
*/
@@ -316,7 +316,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
dev->devpath[0] = '0';
dev->dev.parent = bus->controller;
- sprintf(&dev->dev.bus_id[0], "usb%d", bus->busnum);
+ dev_set_name(&dev->dev, "usb%d", bus->busnum);
root_hub = 1;
} else {
/* match any labeling on the hubs; it's one-based */
@@ -328,8 +328,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
"%s.%d", parent->devpath, port1);
dev->dev.parent = &parent->dev;
- sprintf(&dev->dev.bus_id[0], "%d-%s",
- bus->busnum, dev->devpath);
+ dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath);
/* hub driver sets up TT records */
}
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 12357255d740..37008b8f2c72 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -22,9 +22,11 @@ obj-$(CONFIG_USB_M66592) += m66592-udc.o
#
# USB gadget drivers
#
-g_zero-objs := zero.o usbstring.o config.o epautoconf.o
+C_UTILS = composite.o usbstring.o config.o epautoconf.o
+
+g_zero-objs := zero.o f_sourcesink.o f_loopback.o $(C_UTILS)
g_ether-objs := ether.o usbstring.o config.o epautoconf.o
-g_serial-objs := serial.o usbstring.o config.o epautoconf.o
+g_serial-objs := serial.o u_serial.o f_acm.o f_serial.o $(C_UTILS)
g_midi-objs := gmidi.o usbstring.o config.o epautoconf.o
gadgetfs-objs := inode.o
g_file_storage-objs := file_storage.o usbstring.o config.o \
diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c
index f261d2a9a5f0..1500e1b3c302 100644
--- a/drivers/usb/gadget/amd5536udc.c
+++ b/drivers/usb/gadget/amd5536udc.c
@@ -3342,7 +3342,7 @@ static int udc_probe(struct udc *dev)
spin_lock_init(&dev->lock);
dev->gadget.ops = &udc_ops;
- strcpy(dev->gadget.dev.bus_id, "gadget");
+ dev_set_name(&dev->gadget.dev, "gadget");
dev->gadget.dev.release = gadget_release;
dev->gadget.name = name;
dev->gadget.name = name;
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index 274c60a970cd..856255f21b79 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -1687,6 +1687,19 @@ static int __init at91udc_probe(struct platform_device *pdev)
udc->board.pullup_active_low);
}
+ /* newer chips have more FIFO memory than rm9200 */
+ if (cpu_is_at91sam9260()) {
+ udc->ep[0].maxpacket = 64;
+ udc->ep[3].maxpacket = 64;
+ udc->ep[4].maxpacket = 512;
+ udc->ep[5].maxpacket = 512;
+ } else if (cpu_is_at91sam9261()) {
+ udc->ep[3].maxpacket = 64;
+ } else if (cpu_is_at91sam9263()) {
+ udc->ep[0].maxpacket = 64;
+ udc->ep[3].maxpacket = 64;
+ }
+
udc->udp_baseaddr = ioremap(res->start, res->end - res->start + 1);
if (!udc->udp_baseaddr) {
retval = -ENOMEM;
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
new file mode 100644
index 000000000000..09ba41e5f7f8
--- /dev/null
+++ b/drivers/usb/gadget/composite.c
@@ -0,0 +1,1041 @@
+/*
+ * composite.c - infrastructure for Composite USB Gadgets
+ *
+ * Copyright (C) 2006-2008 David Brownell
+ *
+ * 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
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kallsyms.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+
+#include <linux/usb/composite.h>
+
+
+/*
+ * The code in this file is utility code, used to build a gadget driver
+ * from one or more "function" drivers, one or more "configuration"
+ * objects, and a "usb_composite_driver" by gluing them together along
+ * with the relevant device-wide data.
+ */
+
+/* big enough to hold our biggest descriptor */
+#define USB_BUFSIZ 512
+
+static struct usb_composite_driver *composite;
+
+/* Some systems will need runtime overrides for the product identifers
+ * published in the device descriptor, either numbers or strings or both.
+ * String parameters are in UTF-8 (superset of ASCII's 7 bit characters).
+ */
+
+static ushort idVendor;
+module_param(idVendor, ushort, 0);
+MODULE_PARM_DESC(idVendor, "USB Vendor ID");
+
+static ushort idProduct;
+module_param(idProduct, ushort, 0);
+MODULE_PARM_DESC(idProduct, "USB Product ID");
+
+static ushort bcdDevice;
+module_param(bcdDevice, ushort, 0);
+MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
+
+static char *iManufacturer;
+module_param(iManufacturer, charp, 0);
+MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
+
+static char *iProduct;
+module_param(iProduct, charp, 0);
+MODULE_PARM_DESC(iProduct, "USB Product string");
+
+static char *iSerialNumber;
+module_param(iSerialNumber, charp, 0);
+MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * usb_add_function() - add a function to a configuration
+ * @config: the configuration
+ * @function: the function being added
+ * Context: single threaded during gadget setup
+ *
+ * After initialization, each configuration must have one or more
+ * functions added to it. Adding a function involves calling its @bind()
+ * method to allocate resources such as interface and string identifiers
+ * and endpoints.
+ *
+ * This function returns the value of the function's bind(), which is
+ * zero for success else a negative errno value.
+ */
+int __init usb_add_function(struct usb_configuration *config,
+ struct usb_function *function)
+{
+ int value = -EINVAL;
+
+ DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n",
+ function->name, function,
+ config->label, config);
+
+ if (!function->set_alt || !function->disable)
+ goto done;
+
+ function->config = config;
+ list_add_tail(&function->list, &config->functions);
+
+ /* REVISIT *require* function->bind? */
+ if (function->bind) {
+ value = function->bind(config, function);
+ if (value < 0) {
+ list_del(&function->list);
+ function->config = NULL;
+ }
+ } else
+ value = 0;
+
+ /* We allow configurations that don't work at both speeds.
+ * If we run into a lowspeed Linux system, treat it the same
+ * as full speed ... it's the function drivers that will need
+ * to avoid bulk and ISO transfers.
+ */
+ if (!config->fullspeed && function->descriptors)
+ config->fullspeed = true;
+ if (!config->highspeed && function->hs_descriptors)
+ config->highspeed = true;
+
+done:
+ if (value)
+ DBG(config->cdev, "adding '%s'/%p --> %d\n",
+ function->name, function, value);
+ return value;
+}
+
+/**
+ * usb_interface_id() - allocate an unused interface ID
+ * @config: configuration associated with the interface
+ * @function: function handling the interface
+ * Context: single threaded during gadget setup
+ *
+ * usb_interface_id() is called from usb_function.bind() callbacks to
+ * allocate new interface IDs. The function driver will then store that
+ * ID in interface, association, CDC union, and other descriptors. It
+ * will also handle any control requests targetted at that interface,
+ * particularly changing its altsetting via set_alt(). There may
+ * also be class-specific or vendor-specific requests to handle.
+ *
+ * All interface identifier should be allocated using this routine, to
+ * ensure that for example different functions don't wrongly assign
+ * different meanings to the same identifier. Note that since interface
+ * identifers are configuration-specific, functions used in more than
+ * one configuration (or more than once in a given configuration) need
+ * multiple versions of the relevant descriptors.
+ *
+ * Returns the interface ID which was allocated; or -ENODEV if no
+ * more interface IDs can be allocated.
+ */
+int __init usb_interface_id(struct usb_configuration *config,
+ struct usb_function *function)
+{
+ unsigned id = config->next_interface_id;
+
+ if (id < MAX_CONFIG_INTERFACES) {
+ config->interface[id] = function;
+ config->next_interface_id = id + 1;
+ return id;
+ }
+ return -ENODEV;
+}
+
+static int config_buf(struct usb_configuration *config,
+ enum usb_device_speed speed, void *buf, u8 type)
+{
+ struct usb_config_descriptor *c = buf;
+ void *next = buf + USB_DT_CONFIG_SIZE;
+ int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE;
+ struct usb_function *f;
+ int status;
+
+ /* write the config descriptor */
+ c = buf;
+ c->bLength = USB_DT_CONFIG_SIZE;
+ c->bDescriptorType = type;
+ /* wTotalLength is written later */
+ c->bNumInterfaces = config->next_interface_id;
+ c->bConfigurationValue = config->bConfigurationValue;
+ c->iConfiguration = config->iConfiguration;
+ c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes;
+ c->bMaxPower = config->bMaxPower;
+
+ /* There may be e.g. OTG descriptors */
+ if (config->descriptors) {
+ status = usb_descriptor_fillbuf(next, len,
+ config->descriptors);
+ if (status < 0)
+ return status;
+ len -= status;
+ next += status;
+ }
+
+ /* add each function's descriptors */
+ list_for_each_entry(f, &config->functions, list) {
+ struct usb_descriptor_header **descriptors;
+
+ if (speed == USB_SPEED_HIGH)
+ descriptors = f->hs_descriptors;
+ else
+ descriptors = f->descriptors;
+ if (!descriptors)
+ continue;
+ status = usb_descriptor_fillbuf(next, len,
+ (const struct usb_descriptor_header **) descriptors);
+ if (status < 0)
+ return status;
+ len -= status;
+ next += status;
+ }
+
+ len = next - buf;
+ c->wTotalLength = cpu_to_le16(len);
+ return len;
+}
+
+static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ struct usb_configuration *c;
+ u8 type = w_value >> 8;
+ enum usb_device_speed speed = USB_SPEED_UNKNOWN;
+
+ if (gadget_is_dualspeed(gadget)) {
+ int hs = 0;
+
+ if (gadget->speed == USB_SPEED_HIGH)
+ hs = 1;
+ if (type == USB_DT_OTHER_SPEED_CONFIG)
+ hs = !hs;
+ if (hs)
+ speed = USB_SPEED_HIGH;
+
+ }
+
+ /* This is a lookup by config *INDEX* */
+ w_value &= 0xff;
+ list_for_each_entry(c, &cdev->configs, list) {
+ /* ignore configs that won't work at this speed */
+ if (speed == USB_SPEED_HIGH) {
+ if (!c->highspeed)
+ continue;
+ } else {
+ if (!c->fullspeed)
+ continue;
+ }
+ if (w_value == 0)
+ return config_buf(c, speed, cdev->req->buf, type);
+ w_value--;
+ }
+ return -EINVAL;
+}
+
+static int count_configs(struct usb_composite_dev *cdev, unsigned type)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ struct usb_configuration *c;
+ unsigned count = 0;
+ int hs = 0;
+
+ if (gadget_is_dualspeed(gadget)) {
+ if (gadget->speed == USB_SPEED_HIGH)
+ hs = 1;
+ if (type == USB_DT_DEVICE_QUALIFIER)
+ hs = !hs;
+ }
+ list_for_each_entry(c, &cdev->configs, list) {
+ /* ignore configs that won't work at this speed */
+ if (hs) {
+ if (!c->highspeed)
+ continue;
+ } else {
+ if (!c->fullspeed)
+ continue;
+ }
+ count++;
+ }
+ return count;
+}
+
+static void device_qual(struct usb_composite_dev *cdev)
+{
+ struct usb_qualifier_descriptor *qual = cdev->req->buf;
+
+ qual->bLength = sizeof(*qual);
+ qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER;
+ /* POLICY: same bcdUSB and device type info at both speeds */
+ qual->bcdUSB = cdev->desc.bcdUSB;
+ qual->bDeviceClass = cdev->desc.bDeviceClass;
+ qual->bDeviceSubClass = cdev->desc.bDeviceSubClass;
+ qual->bDeviceProtocol = cdev->desc.bDeviceProtocol;
+ /* ASSUME same EP0 fifo size at both speeds */
+ qual->bMaxPacketSize0 = cdev->desc.bMaxPacketSize0;
+ qual->bNumConfigurations = count_configs(cdev, USB_DT_DEVICE_QUALIFIER);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void reset_config(struct usb_composite_dev *cdev)
+{
+ struct usb_function *f;
+
+ DBG(cdev, "reset config\n");
+
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (f->disable)
+ f->disable(f);
+ }
+ cdev->config = NULL;
+}
+
+static int set_config(struct usb_composite_dev *cdev,
+ const struct usb_ctrlrequest *ctrl, unsigned number)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ struct usb_configuration *c = NULL;
+ int result = -EINVAL;
+ unsigned power = gadget_is_otg(gadget) ? 8 : 100;
+ int tmp;
+
+ if (cdev->config)
+ reset_config(cdev);
+
+ if (number) {
+ list_for_each_entry(c, &cdev->configs, list) {
+ if (c->bConfigurationValue == number) {
+ result = 0;
+ break;
+ }
+ }
+ if (result < 0)
+ goto done;
+ } else
+ result = 0;
+
+ INFO(cdev, "%s speed config #%d: %s\n",
+ ({ char *speed;
+ switch (gadget->speed) {
+ case USB_SPEED_LOW: speed = "low"; break;
+ case USB_SPEED_FULL: speed = "full"; break;
+ case USB_SPEED_HIGH: speed = "high"; break;
+ default: speed = "?"; break;
+ } ; speed; }), number, c ? c->label : "");
+
+ if (!c)
+ goto done;
+
+ cdev->config = c;
+
+ /* Initialize all interfaces by setting them to altsetting zero. */
+ for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) {
+ struct usb_function *f = c->interface[tmp];
+
+ if (!f)
+ break;
+
+ result = f->set_alt(f, tmp, 0);
+ if (result < 0) {
+ DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n",
+ tmp, f->name, f, result);
+
+ reset_config(cdev);
+ goto done;
+ }
+ }
+
+ /* when we return, be sure our power usage is valid */
+ power = 2 * c->bMaxPower;
+done:
+ usb_gadget_vbus_draw(gadget, power);
+ return result;
+}
+
+/**
+ * usb_add_config() - add a configuration to a device.
+ * @cdev: wraps the USB gadget
+ * @config: the configuration, with bConfigurationValue assigned
+ * Context: single threaded during gadget setup
+ *
+ * One of the main tasks of a composite driver's bind() routine is to
+ * add each of the configurations it supports, using this routine.
+ *
+ * This function returns the value of the configuration's bind(), which
+ * is zero for success else a negative errno value. Binding configurations
+ * assigns global resources including string IDs, and per-configuration
+ * resources such as interface IDs and endpoints.
+ */
+int __init usb_add_config(struct usb_composite_dev *cdev,
+ struct usb_configuration *config)
+{
+ int status = -EINVAL;
+ struct usb_configuration *c;
+
+ DBG(cdev, "adding config #%u '%s'/%p\n",
+ config->bConfigurationValue,
+ config->label, config);
+
+ if (!config->bConfigurationValue || !config->bind)
+ goto done;
+
+ /* Prevent duplicate configuration identifiers */
+ list_for_each_entry(c, &cdev->configs, list) {
+ if (c->bConfigurationValue == config->bConfigurationValue) {
+ status = -EBUSY;
+ goto done;
+ }
+ }
+
+ config->cdev = cdev;
+ list_add_tail(&config->list, &cdev->configs);
+
+ INIT_LIST_HEAD(&config->functions);
+ config->next_interface_id = 0;
+
+ status = config->bind(config);
+ if (status < 0) {
+ list_del(&config->list);
+ config->cdev = NULL;
+ } else {
+ unsigned i;
+
+ DBG(cdev, "cfg %d/%p speeds:%s%s\n",
+ config->bConfigurationValue, config,
+ config->highspeed ? " high" : "",
+ config->fullspeed
+ ? (gadget_is_dualspeed(cdev->gadget)
+ ? " full"
+ : " full/low")
+ : "");
+
+ for (i = 0; i < MAX_CONFIG_INTERFACES; i++) {
+ struct usb_function *f = config->interface[i];
+
+ if (!f)
+ continue;
+ DBG(cdev, " interface %d = %s/%p\n",
+ i, f->name, f);
+ }
+ }
+
+ /* set_alt(), or next config->bind(), sets up
+ * ep->driver_data as needed.
+ */
+ usb_ep_autoconfig_reset(cdev->gadget);
+
+done:
+ if (status)
+ DBG(cdev, "added config '%s'/%u --> %d\n", config->label,
+ config->bConfigurationValue, status);
+ return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* We support strings in multiple languages ... string descriptor zero
+ * says which languages are supported. The typical case will be that
+ * only one language (probably English) is used, with I18N handled on
+ * the host side.
+ */
+
+static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf)
+{
+ const struct usb_gadget_strings *s;
+ u16 language;
+ __le16 *tmp;
+
+ while (*sp) {
+ s = *sp;
+ language = cpu_to_le16(s->language);
+ for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) {
+ if (*tmp == language)
+ goto repeat;
+ }
+ *tmp++ = language;
+repeat:
+ sp++;
+ }
+}
+
+static int lookup_string(
+ struct usb_gadget_strings **sp,
+ void *buf,
+ u16 language,
+ int id
+)
+{
+ struct usb_gadget_strings *s;
+ int value;
+
+ while (*sp) {
+ s = *sp++;
+ if (s->language != language)
+ continue;
+ value = usb_gadget_get_string(s, id, buf);
+ if (value > 0)
+ return value;
+ }
+ return -EINVAL;
+}
+
+static int get_string(struct usb_composite_dev *cdev,
+ void *buf, u16 language, int id)
+{
+ struct usb_configuration *c;
+ struct usb_function *f;
+ int len;
+
+ /* Yes, not only is USB's I18N support probably more than most
+ * folk will ever care about ... also, it's all supported here.
+ * (Except for UTF8 support for Unicode's "Astral Planes".)
+ */
+
+ /* 0 == report all available language codes */
+ if (id == 0) {
+ struct usb_string_descriptor *s = buf;
+ struct usb_gadget_strings **sp;
+
+ memset(s, 0, 256);
+ s->bDescriptorType = USB_DT_STRING;
+
+ sp = composite->strings;
+ if (sp)
+ collect_langs(sp, s->wData);
+
+ list_for_each_entry(c, &cdev->configs, list) {
+ sp = c->strings;
+ if (sp)
+ collect_langs(sp, s->wData);
+
+ list_for_each_entry(f, &c->functions, list) {
+ sp = f->strings;
+ if (sp)
+ collect_langs(sp, s->wData);
+ }
+ }
+
+ for (len = 0; s->wData[len] && len <= 126; len++)
+ continue;
+ if (!len)
+ return -EINVAL;
+
+ s->bLength = 2 * (len + 1);
+ return s->bLength;
+ }
+
+ /* Otherwise, look up and return a specified string. String IDs
+ * are device-scoped, so we look up each string table we're told
+ * about. These lookups are infrequent; simpler-is-better here.
+ */
+ if (composite->strings) {
+ len = lookup_string(composite->strings, buf, language, id);
+ if (len > 0)
+ return len;
+ }
+ list_for_each_entry(c, &cdev->configs, list) {
+ if (c->strings) {
+ len = lookup_string(c->strings, buf, language, id);
+ if (len > 0)
+ return len;
+ }
+ list_for_each_entry(f, &c->functions, list) {
+ if (!f->strings)
+ continue;
+ len = lookup_string(f->strings, buf, language, id);
+ if (len > 0)
+ return len;
+ }
+ }
+ return -EINVAL;
+}
+
+/**
+ * usb_string_id() - allocate an unused string ID
+ * @cdev: the device whose string descriptor IDs are being allocated
+ * Context: single threaded during gadget setup
+ *
+ * @usb_string_id() is called from bind() callbacks to allocate
+ * string IDs. Drivers for functions, configurations, or gadgets will
+ * then store that ID in the appropriate descriptors and string table.
+ *
+ * All string identifier should be allocated using this routine, to
+ * ensure that for example different functions don't wrongly assign
+ * different meanings to the same identifier.
+ */
+int __init usb_string_id(struct usb_composite_dev *cdev)
+{
+ if (cdev->next_string_id < 254) {
+ /* string id 0 is reserved */
+ cdev->next_string_id++;
+ return cdev->next_string_id;
+ }
+ return -ENODEV;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ if (req->status || req->actual != req->length)
+ DBG((struct usb_composite_dev *) ep->driver_data,
+ "setup complete --> %d, %d/%d\n",
+ req->status, req->actual, req->length);
+}
+
+/*
+ * The setup() callback implements all the ep0 functionality that's
+ * not handled lower down, in hardware or the hardware driver(like
+ * device and endpoint feature flags, and their status). It's all
+ * housekeeping for the gadget function we're implementing. Most of
+ * the work is in config and function specific setup.
+ */
+static int
+composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_request *req = cdev->req;
+ int value = -EOPNOTSUPP;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+ struct usb_function *f = NULL;
+
+ /* partial re-init of the response message; the function or the
+ * gadget might need to intercept e.g. a control-OUT completion
+ * when we delegate to it.
+ */
+ req->zero = 0;
+ req->complete = composite_setup_complete;
+ req->length = USB_BUFSIZ;
+ gadget->ep0->driver_data = cdev;
+
+ switch (ctrl->bRequest) {
+
+ /* we handle all standard USB descriptors */
+ case USB_REQ_GET_DESCRIPTOR:
+ if (ctrl->bRequestType != USB_DIR_IN)
+ goto unknown;
+ switch (w_value >> 8) {
+
+ case USB_DT_DEVICE:
+ cdev->desc.bNumConfigurations =
+ count_configs(cdev, USB_DT_DEVICE);
+ value = min(w_length, (u16) sizeof cdev->desc);
+ memcpy(req->buf, &cdev->desc, value);
+ break;
+ case USB_DT_DEVICE_QUALIFIER:
+ if (!gadget_is_dualspeed(gadget))
+ break;
+ device_qual(cdev);
+ value = min_t(int, w_length,
+ sizeof(struct usb_qualifier_descriptor));
+ break;
+ case USB_DT_OTHER_SPEED_CONFIG:
+ if (!gadget_is_dualspeed(gadget))
+ break;
+ /* FALLTHROUGH */
+ case USB_DT_CONFIG:
+ value = config_desc(cdev, w_value);
+ if (value >= 0)
+ value = min(w_length, (u16) value);
+ break;
+ case USB_DT_STRING:
+ value = get_string(cdev, req->buf,
+ w_index, w_value & 0xff);
+ if (value >= 0)
+ value = min(w_length, (u16) value);
+ break;
+ }
+ break;
+
+ /* any number of configs can work */
+ case USB_REQ_SET_CONFIGURATION:
+ if (ctrl->bRequestType != 0)
+ goto unknown;
+ if (gadget_is_otg(gadget)) {
+ if (gadget->a_hnp_support)
+ DBG(cdev, "HNP available\n");
+ else if (gadget->a_alt_hnp_support)
+ DBG(cdev, "HNP on another port\n");
+ else
+ VDBG(cdev, "HNP inactive\n");
+ }
+ spin_lock(&cdev->lock);
+ value = set_config(cdev, ctrl, w_value);
+ spin_unlock(&cdev->lock);
+ break;
+ case USB_REQ_GET_CONFIGURATION:
+ if (ctrl->bRequestType != USB_DIR_IN)
+ goto unknown;
+ if (cdev->config)
+ *(u8 *)req->buf = cdev->config->bConfigurationValue;
+ else
+ *(u8 *)req->buf = 0;
+ value = min(w_length, (u16) 1);
+ break;
+
+ /* function drivers must handle get/set altsetting; if there's
+ * no get() method, we know only altsetting zero works.
+ */
+ case USB_REQ_SET_INTERFACE:
+ if (ctrl->bRequestType != USB_RECIP_INTERFACE)
+ goto unknown;
+ if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)
+ break;
+ f = cdev->config->interface[w_index];
+ if (!f)
+ break;
+ if (w_value && !f->get_alt)
+ break;
+ value = f->set_alt(f, w_index, w_value);
+ break;
+ case USB_REQ_GET_INTERFACE:
+ if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
+ goto unknown;
+ if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)
+ break;
+ f = cdev->config->interface[w_index];
+ if (!f)
+ break;
+ /* lots of interfaces only need altsetting zero... */
+ value = f->get_alt ? f->get_alt(f, w_index) : 0;
+ if (value < 0)
+ break;
+ *((u8 *)req->buf) = value;
+ value = min(w_length, (u16) 1);
+ break;
+ default:
+unknown:
+ VDBG(cdev,
+ "non-core control req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+
+ /* functions always handle their interfaces ... punt other
+ * recipients (endpoint, other, WUSB, ...) to the current
+ * configuration code.
+ *
+ * REVISIT it could make sense to let the composite device
+ * take such requests too, if that's ever needed: to work
+ * in config 0, etc.
+ */
+ if ((ctrl->bRequestType & USB_RECIP_MASK)
+ == USB_RECIP_INTERFACE) {
+ f = cdev->config->interface[w_index];
+ if (f && f->setup)
+ value = f->setup(f, ctrl);
+ else
+ f = NULL;
+ }
+ if (value < 0 && !f) {
+ struct usb_configuration *c;
+
+ c = cdev->config;
+ if (c && c->setup)
+ value = c->setup(c, ctrl);
+ }
+
+ goto done;
+ }
+
+ /* respond with data transfer before status phase? */
+ if (value >= 0) {
+ req->length = value;
+ req->zero = value < w_length;
+ value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0) {
+ DBG(cdev, "ep_queue --> %d\n", value);
+ req->status = 0;
+ composite_setup_complete(gadget->ep0, req);
+ }
+ }
+
+done:
+ /* device either stalls (value < 0) or reports success */
+ return value;
+}
+
+static void composite_disconnect(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ unsigned long flags;
+
+ /* REVISIT: should we have config and device level
+ * disconnect callbacks?
+ */
+ spin_lock_irqsave(&cdev->lock, flags);
+ if (cdev->config)
+ reset_config(cdev);
+ spin_unlock_irqrestore(&cdev->lock, flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void /* __init_or_exit */
+composite_unbind(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ unsigned long flags;
+
+ /* composite_disconnect() must already have been called
+ * by the underlying peripheral controller driver!
+ */
+ WARN_ON(cdev->config);
+
+ spin_lock_irqsave(&cdev->lock, flags);
+ while (!list_empty(&cdev->configs)) {
+ struct usb_configuration *c;
+
+ c = list_first_entry(&cdev->configs,
+ struct usb_configuration, list);
+ while (!list_empty(&c->functions)) {
+ struct usb_function *f;
+
+ f = list_first_entry(&c->functions,
+ struct usb_function, list);
+ list_del(&f->list);
+ if (f->unbind) {
+ DBG(cdev, "unbind function '%s'/%p\n",
+ f->name, f);
+ f->unbind(c, f);
+ /* may free memory for "f" */
+ }
+ }
+ list_del(&c->list);
+ if (c->unbind) {
+ DBG(cdev, "unbind config '%s'/%p\n", c->label, c);
+ c->unbind(c);
+ /* may free memory for "c" */
+ }
+ }
+ if (composite->unbind)
+ composite->unbind(cdev);
+ spin_unlock_irqrestore(&cdev->lock, flags);
+
+ if (cdev->req) {
+ kfree(cdev->req->buf);
+ usb_ep_free_request(gadget->ep0, cdev->req);
+ }
+ kfree(cdev);
+ set_gadget_data(gadget, NULL);
+ composite = NULL;
+}
+
+static void __init
+string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s)
+{
+ struct usb_string *str = tab->strings;
+
+ for (str = tab->strings; str->s; str++) {
+ if (str->id == id) {
+ str->s = s;
+ return;
+ }
+ }
+}
+
+static void __init
+string_override(struct usb_gadget_strings **tab, u8 id, const char *s)
+{
+ while (*tab) {
+ string_override_one(*tab, id, s);
+ tab++;
+ }
+}
+
+static int __init composite_bind(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev;
+ int status = -ENOMEM;
+
+ cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
+ if (!cdev)
+ return status;
+
+ spin_lock_init(&cdev->lock);
+ cdev->gadget = gadget;
+ set_gadget_data(gadget, cdev);
+ INIT_LIST_HEAD(&cdev->configs);
+
+ /* preallocate control response and buffer */
+ cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+ if (!cdev->req)
+ goto fail;
+ cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
+ if (!cdev->req->buf)
+ goto fail;
+ cdev->req->complete = composite_setup_complete;
+ gadget->ep0->driver_data = cdev;
+
+ cdev->bufsiz = USB_BUFSIZ;
+ cdev->driver = composite;
+
+ usb_gadget_set_selfpowered(gadget);
+
+ /* interface and string IDs start at zero via kzalloc.
+ * we force endpoints to start unassigned; few controller
+ * drivers will zero ep->driver_data.
+ */
+ usb_ep_autoconfig_reset(cdev->gadget);
+
+ /* composite gadget needs to assign strings for whole device (like
+ * serial number), register function drivers, potentially update
+ * power state and consumption, etc
+ */
+ status = composite->bind(cdev);
+ if (status < 0)
+ goto fail;
+
+ cdev->desc = *composite->dev;
+ cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
+
+ /* standardized runtime overrides for device ID data */
+ if (idVendor)
+ cdev->desc.idVendor = cpu_to_le16(idVendor);
+ if (idProduct)
+ cdev->desc.idProduct = cpu_to_le16(idProduct);
+ if (bcdDevice)
+ cdev->desc.bcdDevice = cpu_to_le16(bcdDevice);
+
+ /* strings can't be assigned before bind() allocates the
+ * releavnt identifiers
+ */
+ if (cdev->desc.iManufacturer && iManufacturer)
+ string_override(composite->strings,
+ cdev->desc.iManufacturer, iManufacturer);
+ if (cdev->desc.iProduct && iProduct)
+ string_override(composite->strings,
+ cdev->desc.iProduct, iProduct);
+ if (cdev->desc.iSerialNumber && iSerialNumber)
+ string_override(composite->strings,
+ cdev->desc.iSerialNumber, iSerialNumber);
+
+ INFO(cdev, "%s ready\n", composite->name);
+ return 0;
+
+fail:
+ composite_unbind(gadget);
+ return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+composite_suspend(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_function *f;
+
+ /* REVISIT: should we have config and device level
+ * suspend/resume callbacks?
+ */
+ DBG(cdev, "suspend\n");
+ if (cdev->config) {
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (f->suspend)
+ f->suspend(f);
+ }
+ }
+}
+
+static void
+composite_resume(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_function *f;
+
+ /* REVISIT: should we have config and device level
+ * suspend/resume callbacks?
+ */
+ DBG(cdev, "resume\n");
+ if (cdev->config) {
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (f->resume)
+ f->resume(f);
+ }
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_gadget_driver composite_driver = {
+ .speed = USB_SPEED_HIGH,
+
+ .bind = composite_bind,
+ .unbind = __exit_p(composite_unbind),
+
+ .setup = composite_setup,
+ .disconnect = composite_disconnect,
+
+ .suspend = composite_suspend,
+ .resume = composite_resume,
+
+ .driver = {
+ .owner = THIS_MODULE,
+ },
+};
+
+/**
+ * usb_composite_register() - register a composite driver
+ * @driver: the driver to register
+ * Context: single threaded during gadget setup
+ *
+ * This function is used to register drivers using the composite driver
+ * framework. The return value is zero, or a negative errno value.
+ * Those values normally come from the driver's @bind method, which does
+ * all the work of setting up the driver to match the hardware.
+ *
+ * On successful return, the gadget is ready to respond to requests from
+ * the host, unless one of its components invokes usb_gadget_disconnect()
+ * while it was binding. That would usually be done in order to wait for
+ * some userspace participation.
+ */
+int __init usb_composite_register(struct usb_composite_driver *driver)
+{
+ if (!driver || !driver->dev || !driver->bind || composite)
+ return -EINVAL;
+
+ if (!driver->name)
+ driver->name = "composite";
+ composite_driver.function = (char *) driver->name;
+ composite_driver.driver.name = driver->name;
+ composite = driver;
+
+ return usb_gadget_register_driver(&composite_driver);
+}
+
+/**
+ * usb_composite_unregister() - unregister a composite driver
+ * @driver: the driver to unregister
+ *
+ * This function is used to unregister drivers using the composite
+ * driver framework.
+ */
+void __exit usb_composite_unregister(struct usb_composite_driver *driver)
+{
+ if (composite != driver)
+ return;
+ usb_gadget_unregister_driver(&composite_driver);
+}
diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c
index a4e54b2743f0..92a28f5cd31f 100644
--- a/drivers/usb/gadget/config.c
+++ b/drivers/usb/gadget/config.c
@@ -96,7 +96,7 @@ int usb_gadget_config_buf(
/* config descriptor first */
if (length < USB_DT_CONFIG_SIZE || !desc)
return -EINVAL;
- *cp = *config;
+ *cp = *config;
/* then interface/endpoint/class/vendor/... */
len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf,
@@ -115,3 +115,76 @@ int usb_gadget_config_buf(
return len;
}
+/**
+ * usb_copy_descriptors - copy a vector of USB descriptors
+ * @src: null-terminated vector to copy
+ * Context: initialization code, which may sleep
+ *
+ * This makes a copy of a vector of USB descriptors. Its primary use
+ * is to support usb_function objects which can have multiple copies,
+ * each needing different descriptors. Functions may have static
+ * tables of descriptors, which are used as templates and customized
+ * with identifiers (for interfaces, strings, endpoints, and more)
+ * as needed by a given function instance.
+ */
+struct usb_descriptor_header **__init
+usb_copy_descriptors(struct usb_descriptor_header **src)
+{
+ struct usb_descriptor_header **tmp;
+ unsigned bytes;
+ unsigned n_desc;
+ void *mem;
+ struct usb_descriptor_header **ret;
+
+ /* count descriptors and their sizes; then add vector size */
+ for (bytes = 0, n_desc = 0, tmp = src; *tmp; tmp++, n_desc++)
+ bytes += (*tmp)->bLength;
+ bytes += (n_desc + 1) * sizeof(*tmp);
+
+ mem = kmalloc(bytes, GFP_KERNEL);
+ if (!mem)
+ return NULL;
+
+ /* fill in pointers starting at "tmp",
+ * to descriptors copied starting at "mem";
+ * and return "ret"
+ */
+ ret = tmp = mem;
+ mem += (n_desc + 1) * sizeof(*tmp);
+ while (*src) {
+ memcpy(mem, *src, (*src)->bLength);
+ *tmp = mem;
+ tmp++;
+ mem += (*src)->bLength;
+ src++;
+ }
+ *tmp = NULL;
+
+ return ret;
+}
+
+/**
+ * usb_find_endpoint - find a copy of an endpoint descriptor
+ * @src: original vector of descriptors
+ * @copy: copy of @src
+ * @ep: endpoint descriptor found in @src
+ *
+ * This returns the copy of the @match descriptor made for @copy. Its
+ * intended use is to help remembering the endpoint descriptor to use
+ * when enabling a given endpoint.
+ */
+struct usb_endpoint_descriptor *__init
+usb_find_endpoint(
+ struct usb_descriptor_header **src,
+ struct usb_descriptor_header **copy,
+ struct usb_endpoint_descriptor *match
+)
+{
+ while (*src) {
+ if (*src == (void *) match)
+ return (void *)*copy;
+ src++;
+ copy++;
+ }
+ return NULL;
+}
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index 42036192a03c..1f64a436dd7b 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -862,7 +862,7 @@ static int dummy_udc_probe (struct platform_device *pdev)
/* maybe claim OTG support, though we won't complete HNP */
dum->gadget.is_otg = (dummy_to_hcd(dum)->self.otg_port != 0);
- strcpy (dum->gadget.dev.bus_id, "gadget");
+ dev_set_name(&dum->gadget.dev, "gadget");
dum->gadget.dev.parent = &pdev->dev;
dum->gadget.dev.release = dummy_gadget_release;
rc = device_register (&dum->gadget.dev);
@@ -1865,7 +1865,7 @@ static int dummy_hcd_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc);
- hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, pdev->dev.bus_id);
+ hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, dev_name(&pdev));
if (!hcd)
return -ENOMEM;
the_controller = hcd_to_dummy (hcd);
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 8bdad221fa91..9462e30192d8 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -159,6 +159,7 @@ ep_matches (
/* MATCH!! */
/* report address */
+ desc->bEndpointAddress &= USB_DIR_IN;
if (isdigit (ep->name [2])) {
u8 num = simple_strtol (&ep->name [2], NULL, 10);
desc->bEndpointAddress |= num;
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 8d61ea67a817..5cbb39267f13 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -1642,7 +1642,7 @@ static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)
strlcpy(p->driver, shortname, sizeof p->driver);
strlcpy(p->version, DRIVER_VERSION, sizeof p->version);
strlcpy(p->fw_version, dev->gadget->name, sizeof p->fw_version);
- strlcpy (p->bus_info, dev->gadget->dev.bus_id, sizeof p->bus_info);
+ strlcpy (p->bus_info, dev_name(&dev->gadget->dev), sizeof p->bus_info);
}
static u32 eth_get_link(struct net_device *net)
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c
new file mode 100644
index 000000000000..96fbf7f53011
--- /dev/null
+++ b/drivers/usb/gadget/f_acm.c
@@ -0,0 +1,587 @@
+/*
+ * f_acm.c -- USB CDC ACM function driver
+ *
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 by David Brownell
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * either version 2 of that License or (at your option) any later version.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/utsname.h>
+#include <linux/device.h>
+
+#include "u_serial.h"
+#include "gadget_chips.h"
+
+
+/*
+ * This CDC ACM function support just wraps control functions and
+ * notifications around the generic serial-over-usb code.
+ *
+ * Because CDC ACM is standardized by the USB-IF, many host operating
+ * systems have drivers for it. Accordingly, ACM is the preferred
+ * interop solution for serial-port type connections. The control
+ * models are often not necessary, and in any case don't do much in
+ * this bare-bones implementation.
+ */
+
+struct acm_ep_descs {
+ struct usb_endpoint_descriptor *in;
+ struct usb_endpoint_descriptor *out;
+ struct usb_endpoint_descriptor *notify;
+};
+
+struct f_acm {
+ struct gserial port;
+ u8 ctrl_id, data_id;
+ u8 port_num;
+
+ struct usb_descriptor_header **fs_function;
+ struct acm_ep_descs fs;
+ struct usb_descriptor_header **hs_function;
+ struct acm_ep_descs hs;
+
+ struct usb_ep *notify;
+ struct usb_endpoint_descriptor *notify_desc;
+
+ struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
+ u16 port_handshake_bits;
+#define RS232_RTS (1 << 1) /* unused with full duplex */
+#define RS232_DTR (1 << 0) /* host is ready for data r/w */
+};
+
+static inline struct f_acm *func_to_acm(struct usb_function *f)
+{
+ return container_of(f, struct f_acm, port.func);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* notification endpoint uses smallish and infrequent fixed-size messages */
+
+#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */
+#define GS_NOTIFY_MAXPACKET 8
+
+/* interface and class descriptors: */
+
+static struct usb_interface_descriptor acm_control_interface_desc __initdata = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ /* .bInterfaceNumber = DYNAMIC */
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
+ .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
+ /* .iInterface = DYNAMIC */
+};
+
+static struct usb_interface_descriptor acm_data_interface_desc __initdata = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ /* .bInterfaceNumber = DYNAMIC */
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_CDC_DATA,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ /* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc acm_header_desc __initdata = {
+ .bLength = sizeof(acm_header_desc),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_HEADER_TYPE,
+ .bcdCDC = __constant_cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_call_mgmt_descriptor
+acm_call_mgmt_descriptor __initdata = {
+ .bLength = sizeof(acm_call_mgmt_descriptor),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
+ .bmCapabilities = 0,
+ /* .bDataInterface = DYNAMIC */
+};
+
+static struct usb_cdc_acm_descriptor acm_descriptor __initdata = {
+ .bLength = sizeof(acm_descriptor),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_ACM_TYPE,
+ .bmCapabilities = (1 << 1),
+};
+
+static struct usb_cdc_union_desc acm_union_desc __initdata = {
+ .bLength = sizeof(acm_union_desc),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_UNION_TYPE,
+ /* .bMasterInterface0 = DYNAMIC */
+ /* .bSlaveInterface0 = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor acm_fs_notify_desc __initdata = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET),
+ .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL,
+};
+
+static struct usb_endpoint_descriptor acm_fs_in_desc __initdata = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor acm_fs_out_desc __initdata = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *acm_fs_function[] __initdata = {
+ (struct usb_descriptor_header *) &acm_control_interface_desc,
+ (struct usb_descriptor_header *) &acm_header_desc,
+ (struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &acm_descriptor,
+ (struct usb_descriptor_header *) &acm_union_desc,
+ (struct usb_descriptor_header *) &acm_fs_notify_desc,
+ (struct usb_descriptor_header *) &acm_data_interface_desc,
+ (struct usb_descriptor_header *) &acm_fs_in_desc,
+ (struct usb_descriptor_header *) &acm_fs_out_desc,
+ NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor acm_hs_notify_desc __initdata = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET),
+ .bInterval = GS_LOG2_NOTIFY_INTERVAL+4,
+};
+
+static struct usb_endpoint_descriptor acm_hs_in_desc __initdata = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor acm_hs_out_desc __initdata = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *acm_hs_function[] __initdata = {
+ (struct usb_descriptor_header *) &acm_control_interface_desc,
+ (struct usb_descriptor_header *) &acm_header_desc,
+ (struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &acm_descriptor,
+ (struct usb_descriptor_header *) &acm_union_desc,
+ (struct usb_descriptor_header *) &acm_hs_notify_desc,
+ (struct usb_descriptor_header *) &acm_data_interface_desc,
+ (struct usb_descriptor_header *) &acm_hs_in_desc,
+ (struct usb_descriptor_header *) &acm_hs_out_desc,
+ NULL,
+};
+
+/* string descriptors: */
+
+#define ACM_CTRL_IDX 0
+#define ACM_DATA_IDX 1
+
+/* static strings, in UTF-8 */
+static struct usb_string acm_string_defs[] = {
+ [ACM_CTRL_IDX].s = "CDC ACM Control",
+ [ACM_DATA_IDX].s = "CDC ACM Data",
+ { /* ZEROES END LIST */ },
+};
+
+static struct usb_gadget_strings acm_string_table = {
+ .language = 0x0409, /* en-us */
+ .strings = acm_string_defs,
+};
+
+static struct usb_gadget_strings *acm_strings[] = {
+ &acm_string_table,
+ NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* ACM control ... data handling is delegated to tty library code.
+ * The main task of this function is to activate and deactivate
+ * that code based on device state; track parameters like line
+ * speed, handshake state, and so on; and issue notifications.
+ */
+
+static void acm_complete_set_line_coding(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ struct f_acm *acm = ep->driver_data;
+ struct usb_composite_dev *cdev = acm->port.func.config->cdev;
+
+ if (req->status != 0) {
+ DBG(cdev, "acm ttyGS%d completion, err %d\n",
+ acm->port_num, req->status);
+ return;
+ }
+
+ /* normal completion */
+ if (req->actual != sizeof(acm->port_line_coding)) {
+ DBG(cdev, "acm ttyGS%d short resp, len %d\n",
+ acm->port_num, req->actual);
+ usb_ep_set_halt(ep);
+ } else {
+ struct usb_cdc_line_coding *value = req->buf;
+
+ /* REVISIT: we currently just remember this data.
+ * If we change that, (a) validate it first, then
+ * (b) update whatever hardware needs updating,
+ * (c) worry about locking. This is information on
+ * the order of 9600-8-N-1 ... most of which means
+ * nothing unless we control a real RS232 line.
+ */
+ acm->port_line_coding = *value;
+ }
+}
+
+static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+ struct f_acm *acm = func_to_acm(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = cdev->req;
+ int value = -EOPNOTSUPP;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+ /* composite driver infrastructure handles everything except
+ * CDC class messages; interface activation uses set_alt().
+ */
+ switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+ /* SET_LINE_CODING ... just read and save what the host sends */
+ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_REQ_SET_LINE_CODING:
+ if (w_length != sizeof(struct usb_cdc_line_coding)
+ || w_index != acm->ctrl_id)
+ goto invalid;
+
+ value = w_length;
+ cdev->gadget->ep0->driver_data = acm;
+ req->complete = acm_complete_set_line_coding;
+ break;
+
+ /* GET_LINE_CODING ... return what host sent, or initial value */
+ case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_REQ_GET_LINE_CODING:
+ if (w_index != acm->ctrl_id)
+ goto invalid;
+
+ value = min_t(unsigned, w_length,
+ sizeof(struct usb_cdc_line_coding));
+ memcpy(req->buf, &acm->port_line_coding, value);
+ break;
+
+ /* SET_CONTROL_LINE_STATE ... save what the host sent */
+ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+ if (w_index != acm->ctrl_id)
+ goto invalid;
+
+ value = 0;
+
+ /* FIXME we should not allow data to flow until the
+ * host sets the RS232_DTR bit; and when it clears
+ * that bit, we should return to that no-flow state.
+ */
+ acm->port_handshake_bits = w_value;
+ break;
+
+ default:
+invalid:
+ VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ }
+
+ /* respond with data transfer or status phase? */
+ if (value >= 0) {
+ DBG(cdev, "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
+ acm->port_num, ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ req->zero = 0;
+ req->length = value;
+ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0)
+ ERROR(cdev, "acm response on ttyGS%d, err %d\n",
+ acm->port_num, value);
+ }
+
+ /* device either stalls (value < 0) or reports success */
+ return value;
+}
+
+static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct f_acm *acm = func_to_acm(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+
+ /* we know alt == 0, so this is an activation or a reset */
+
+ /* REVISIT hardware on PXA handles SET_INTERFACE;
+ * this is probably doing resets wrong ...
+ */
+
+ if (intf == acm->ctrl_id) {
+ /* REVISIT this may need more work when we start to
+ * send notifications ...
+ */
+ if (acm->notify->driver_data) {
+ VDBG(cdev, "reset acm control interface %d\n", intf);
+ usb_ep_disable(acm->notify);
+ } else {
+ VDBG(cdev, "init acm ctrl interface %d\n", intf);
+ acm->notify_desc = ep_choose(cdev->gadget,
+ acm->hs.notify,
+ acm->fs.notify);
+ }
+ usb_ep_enable(acm->notify, acm->notify_desc);
+ acm->notify->driver_data = acm;
+
+ } else if (intf == acm->data_id) {
+ if (acm->port.in->driver_data) {
+ DBG(cdev, "reset acm ttyGS%d\n", acm->port_num);
+ gserial_disconnect(&acm->port);
+ } else {
+ DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
+ acm->port.in_desc = ep_choose(cdev->gadget,
+ acm->hs.in, acm->fs.in);
+ acm->port.out_desc = ep_choose(cdev->gadget,
+ acm->hs.out, acm->fs.out);
+ }
+ gserial_connect(&acm->port, acm->port_num);
+
+ } else
+ return -EINVAL;
+
+ return 0;
+}
+
+static void acm_disable(struct usb_function *f)
+{
+ struct f_acm *acm = func_to_acm(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+
+ DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num);
+ gserial_disconnect(&acm->port);
+ usb_ep_disable(acm->notify);
+ acm->notify->driver_data = NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ACM function driver setup/binding */
+static int __init
+acm_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_acm *acm = func_to_acm(f);
+ int status;
+ struct usb_ep *ep;
+
+ /* allocate instance-specific interface IDs, and patch descriptors */
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ acm->ctrl_id = status;
+
+ acm_control_interface_desc.bInterfaceNumber = status;
+ acm_union_desc .bMasterInterface0 = status;
+
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ acm->data_id = status;
+
+ acm_data_interface_desc.bInterfaceNumber = status;
+ acm_union_desc.bSlaveInterface0 = status;
+ acm_call_mgmt_descriptor.bDataInterface = status;
+
+ status = -ENODEV;
+
+ /* allocate instance-specific endpoints */
+ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc);
+ if (!ep)
+ goto fail;
+ acm->port.in = ep;
+ ep->driver_data = cdev; /* claim */
+
+ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc);
+ if (!ep)
+ goto fail;
+ acm->port.out = ep;
+ ep->driver_data = cdev; /* claim */
+
+ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc);
+ if (!ep)
+ goto fail;
+ acm->notify = ep;
+ ep->driver_data = cdev; /* claim */
+
+ /* copy descriptors, and track endpoint copies */
+ f->descriptors = usb_copy_descriptors(acm_fs_function);
+
+ acm->fs.in = usb_find_endpoint(acm_fs_function,
+ f->descriptors, &acm_fs_in_desc);
+ acm->fs.out = usb_find_endpoint(acm_fs_function,
+ f->descriptors, &acm_fs_out_desc);
+ acm->fs.notify = usb_find_endpoint(acm_fs_function,
+ f->descriptors, &acm_fs_notify_desc);
+
+ /* support all relevant hardware speeds... we expect that when
+ * hardware is dual speed, all bulk-capable endpoints work at
+ * both speeds
+ */
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ acm_hs_in_desc.bEndpointAddress =
+ acm_fs_in_desc.bEndpointAddress;
+ acm_hs_out_desc.bEndpointAddress =
+ acm_fs_out_desc.bEndpointAddress;
+ acm_hs_notify_desc.bEndpointAddress =
+ acm_fs_notify_desc.bEndpointAddress;
+
+ /* copy descriptors, and track endpoint copies */
+ f->hs_descriptors = usb_copy_descriptors(acm_hs_function);
+
+ acm->hs.in = usb_find_endpoint(acm_hs_function,
+ f->hs_descriptors, &acm_hs_in_desc);
+ acm->hs.out = usb_find_endpoint(acm_hs_function,
+ f->hs_descriptors, &acm_hs_out_desc);
+ acm->hs.notify = usb_find_endpoint(acm_hs_function,
+ f->hs_descriptors, &acm_hs_notify_desc);
+ }
+
+ /* FIXME provide a callback for triggering notifications */
+
+ DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+ acm->port_num,
+ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ acm->port.in->name, acm->port.out->name,
+ acm->notify->name);
+ return 0;
+
+fail:
+ /* we might as well release our claims on endpoints */
+ if (acm->notify)
+ acm->notify->driver_data = NULL;
+ if (acm->port.out)
+ acm->port.out->driver_data = NULL;
+ if (acm->port.in)
+ acm->port.in->driver_data = NULL;
+
+ ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
+
+ return status;
+}
+
+static void
+acm_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ if (gadget_is_dualspeed(c->cdev->gadget))
+ usb_free_descriptors(f->hs_descriptors);
+ usb_free_descriptors(f->descriptors);
+ kfree(func_to_acm(f));
+}
+
+/* Some controllers can't support CDC ACM ... */
+static inline bool can_support_cdc(struct usb_configuration *c)
+{
+ /* SH3 doesn't support multiple interfaces */
+ if (gadget_is_sh(c->cdev->gadget))
+ return false;
+
+ /* sa1100 doesn't have a third interrupt endpoint */
+ if (gadget_is_sa1100(c->cdev->gadget))
+ return false;
+
+ /* everything else is *probably* fine ... */
+ return true;
+}
+
+/**
+ * acm_bind_config - add a CDC ACM function to a configuration
+ * @c: the configuration to support the CDC ACM instance
+ * @port_num: /dev/ttyGS* port this interface will use
+ * Context: single threaded during gadget setup
+ *
+ * Returns zero on success, else negative errno.
+ *
+ * Caller must have called @gserial_setup() with enough ports to
+ * handle all the ones it binds. Caller is also responsible
+ * for calling @gserial_cleanup() before module unload.
+ */
+int __init acm_bind_config(struct usb_configuration *c, u8 port_num)
+{
+ struct f_acm *acm;
+ int status;
+
+ if (!can_support_cdc(c))
+ return -EINVAL;
+
+ /* REVISIT might want instance-specific strings to help
+ * distinguish instances ...
+ */
+
+ /* maybe allocate device-global string IDs, and patch descriptors */
+ if (acm_string_defs[ACM_CTRL_IDX].id == 0) {
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ acm_string_defs[ACM_CTRL_IDX].id = status;
+
+ acm_control_interface_desc.iInterface = status;
+
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ acm_string_defs[ACM_DATA_IDX].id = status;
+
+ acm_data_interface_desc.iInterface = status;
+ }
+
+ /* allocate and initialize one new instance */
+ acm = kzalloc(sizeof *acm, GFP_KERNEL);
+ if (!acm)
+ return -ENOMEM;
+
+ acm->port_num = port_num;
+
+ acm->port.func.name = "acm";
+ acm->port.func.strings = acm_strings;
+ /* descriptors are per-instance copies */
+ acm->port.func.bind = acm_bind;
+ acm->port.func.unbind = acm_unbind;
+ acm->port.func.set_alt = acm_set_alt;
+ acm->port.func.setup = acm_setup;
+ acm->port.func.disable = acm_disable;
+
+ status = usb_add_function(c, &acm->port.func);
+ if (status)
+ kfree(acm);
+ return status;
+}
diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c
new file mode 100644
index 000000000000..53b618d30351
--- /dev/null
+++ b/drivers/usb/gadget/f_loopback.c
@@ -0,0 +1,383 @@
+/*
+ * f_loopback.c - USB peripheral loopback configuration driver
+ *
+ * Copyright (C) 2003-2008 David Brownell
+ * All rights reserved.
+ *
+ * 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
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/utsname.h>
+#include <linux/device.h>
+
+#include "g_zero.h"
+#include "gadget_chips.h"
+
+
+/*
+ * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals,
+ *
+ * This takes messages of various sizes written OUT to a device, and loops
+ * them back so they can be read IN from it. It has been used by certain
+ * test applications. It supports limited testing of data queueing logic.
+ *
+ *
+ * This is currently packaged as a configuration driver, which can't be
+ * combined with other functions to make composite devices. However, it
+ * can be combined with other independent configurations.
+ */
+struct f_loopback {
+ struct usb_function function;
+
+ struct usb_ep *in_ep;
+ struct usb_ep *out_ep;
+};
+
+static inline struct f_loopback *func_to_loop(struct usb_function *f)
+{
+ return container_of(f, struct f_loopback, function);
+}
+
+static unsigned qlen = 32;
+module_param(qlen, uint, 0);
+MODULE_PARM_DESC(qlenn, "depth of loopback queue");
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_interface_descriptor loopback_intf = {
+ .bLength = sizeof loopback_intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ /* .iInterface = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor fs_source_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_sink_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *fs_loopback_descs[] = {
+ (struct usb_descriptor_header *) &loopback_intf,
+ (struct usb_descriptor_header *) &fs_sink_desc,
+ (struct usb_descriptor_header *) &fs_source_desc,
+ NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor hs_source_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_sink_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *hs_loopback_descs[] = {
+ (struct usb_descriptor_header *) &loopback_intf,
+ (struct usb_descriptor_header *) &hs_source_desc,
+ (struct usb_descriptor_header *) &hs_sink_desc,
+ NULL,
+};
+
+/* function-specific strings: */
+
+static struct usb_string strings_loopback[] = {
+ [0].s = "loop input to output",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_loop = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_loopback,
+};
+
+static struct usb_gadget_strings *loopback_strings[] = {
+ &stringtab_loop,
+ NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init
+loopback_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_loopback *loop = func_to_loop(f);
+ int id;
+
+ /* allocate interface ID(s) */
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ loopback_intf.bInterfaceNumber = id;
+
+ /* allocate endpoints */
+
+ loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
+ if (!loop->in_ep) {
+autoconf_fail:
+ ERROR(cdev, "%s: can't autoconfigure on %s\n",
+ f->name, cdev->gadget->name);
+ return -ENODEV;
+ }
+ loop->in_ep->driver_data = cdev; /* claim */
+
+ loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc);
+ if (!loop->out_ep)
+ goto autoconf_fail;
+ loop->out_ep->driver_data = cdev; /* claim */
+
+ /* support high speed hardware */
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ hs_source_desc.bEndpointAddress =
+ fs_source_desc.bEndpointAddress;
+ hs_sink_desc.bEndpointAddress =
+ fs_sink_desc.bEndpointAddress;
+ f->hs_descriptors = hs_loopback_descs;
+ }
+
+ DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
+ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ f->name, loop->in_ep->name, loop->out_ep->name);
+ return 0;
+}
+
+static void
+loopback_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ kfree(func_to_loop(f));
+}
+
+static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_loopback *loop = ep->driver_data;
+ struct usb_composite_dev *cdev = loop->function.config->cdev;
+ int status = req->status;
+
+ switch (status) {
+
+ case 0: /* normal completion? */
+ if (ep == loop->out_ep) {
+ /* loop this OUT packet back IN to the host */
+ req->zero = (req->actual < req->length);
+ req->length = req->actual;
+ status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC);
+ if (status == 0)
+ return;
+
+ /* "should never get here" */
+ ERROR(cdev, "can't loop %s to %s: %d\n",
+ ep->name, loop->in_ep->name,
+ status);
+ }
+
+ /* queue the buffer for some later OUT packet */
+ req->length = buflen;
+ status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC);
+ if (status == 0)
+ return;
+
+ /* "should never get here" */
+ /* FALLTHROUGH */
+
+ default:
+ ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name,
+ status, req->actual, req->length);
+ /* FALLTHROUGH */
+
+ /* NOTE: since this driver doesn't maintain an explicit record
+ * of requests it submitted (just maintains qlen count), we
+ * rely on the hardware driver to clean up on disconnect or
+ * endpoint disable.
+ */
+ case -ECONNABORTED: /* hardware forced ep reset */
+ case -ECONNRESET: /* request dequeued */
+ case -ESHUTDOWN: /* disconnect from host */
+ free_ep_req(ep, req);
+ return;
+ }
+}
+
+static void disable_loopback(struct f_loopback *loop)
+{
+ struct usb_composite_dev *cdev;
+
+ cdev = loop->function.config->cdev;
+ disable_endpoints(cdev, loop->in_ep, loop->out_ep);
+ VDBG(cdev, "%s disabled\n", loop->function.name);
+}
+
+static int
+enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
+{
+ int result = 0;
+ const struct usb_endpoint_descriptor *src, *sink;
+ struct usb_ep *ep;
+ struct usb_request *req;
+ unsigned i;
+
+ src = ep_choose(cdev->gadget, &hs_source_desc, &fs_source_desc);
+ sink = ep_choose(cdev->gadget, &hs_sink_desc, &fs_sink_desc);
+
+ /* one endpoint writes data back IN to the host */
+ ep = loop->in_ep;
+ result = usb_ep_enable(ep, src);
+ if (result < 0)
+ return result;
+ ep->driver_data = loop;
+
+ /* one endpoint just reads OUT packets */
+ ep = loop->out_ep;
+ result = usb_ep_enable(ep, sink);
+ if (result < 0) {
+fail0:
+ ep = loop->in_ep;
+ usb_ep_disable(ep);
+ ep->driver_data = NULL;
+ return result;
+ }
+ ep->driver_data = loop;
+
+ /* allocate a bunch of read buffers and queue them all at once.
+ * we buffer at most 'qlen' transfers; fewer if any need more
+ * than 'buflen' bytes each.
+ */
+ for (i = 0; i < qlen && result == 0; i++) {
+ req = alloc_ep_req(ep);
+ if (req) {
+ req->complete = loopback_complete;
+ result = usb_ep_queue(ep, req, GFP_ATOMIC);
+ if (result)
+ ERROR(cdev, "%s queue req --> %d\n",
+ ep->name, result);
+ } else {
+ usb_ep_disable(ep);
+ ep->driver_data = NULL;
+ result = -ENOMEM;
+ goto fail0;
+ }
+ }
+
+ DBG(cdev, "%s enabled\n", loop->function.name);
+ return result;
+}
+
+static int loopback_set_alt(struct usb_function *f,
+ unsigned intf, unsigned alt)
+{
+ struct f_loopback *loop = func_to_loop(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+
+ /* we know alt is zero */
+ if (loop->in_ep->driver_data)
+ disable_loopback(loop);
+ return enable_loopback(cdev, loop);
+}
+
+static void loopback_disable(struct usb_function *f)
+{
+ struct f_loopback *loop = func_to_loop(f);
+
+ disable_loopback(loop);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int __init loopback_bind_config(struct usb_configuration *c)
+{
+ struct f_loopback *loop;
+ int status;
+
+ loop = kzalloc(sizeof *loop, GFP_KERNEL);
+ if (!loop)
+ return -ENOMEM;
+
+ loop->function.name = "loopback";
+ loop->function.descriptors = fs_loopback_descs;
+ loop->function.bind = loopback_bind;
+ loop->function.unbind = loopback_unbind;
+ loop->function.set_alt = loopback_set_alt;
+ loop->function.disable = loopback_disable;
+
+ status = usb_add_function(c, &loop->function);
+ if (status)
+ kfree(loop);
+ return status;
+}
+
+static struct usb_configuration loopback_driver = {
+ .label = "loopback",
+ .strings = loopback_strings,
+ .bind = loopback_bind_config,
+ .bConfigurationValue = 2,
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+ .bMaxPower = 1, /* 2 mA, minimal */
+ /* .iConfiguration = DYNAMIC */
+};
+
+/**
+ * loopback_add - add a loopback testing configuration to a device
+ * @cdev: the device to support the loopback configuration
+ */
+int __init loopback_add(struct usb_composite_dev *cdev)
+{
+ int id;
+
+ /* allocate string ID(s) */
+ id = usb_string_id(cdev);
+ if (id < 0)
+ return id;
+ strings_loopback[0].id = id;
+
+ loopback_intf.iInterface = id;
+ loopback_driver.iConfiguration = id;
+
+ /* FIXME pass in bConfigurationValue and OTG stuff */
+
+ /* support OTG systems */
+ if (gadget_is_otg(cdev->gadget)) {
+ loopback_driver.descriptors = otg_desc;
+ loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+ return usb_add_config(cdev, &loopback_driver);
+}
diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c
new file mode 100644
index 000000000000..8360bd33a368
--- /dev/null
+++ b/drivers/usb/gadget/f_serial.c
@@ -0,0 +1,298 @@
+/*
+ * f_serial.c - generic USB serial function
+ *
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 by David Brownell
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * either version 2 of that License or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+
+#include "u_serial.h"
+#include "gadget_chips.h"
+
+
+/*
+ * This function packages a simple "generic serial" port with no real
+ * control mechanisms, just raw data transfer over two bulk endpoints.
+ *
+ * Because it's not standardized, this isn't as interoperable as the
+ * CDC ACM driver. However, for many purposes it's just as functional
+ * if you can arrange appropriate host side drivers.
+ */
+
+struct gser_descs {
+ struct usb_endpoint_descriptor *in;
+ struct usb_endpoint_descriptor *out;
+};
+
+struct f_gser {
+ struct gserial port;
+ u8 data_id;
+ u8 port_num;
+
+ struct usb_descriptor_header **fs_function;
+ struct gser_descs fs;
+ struct usb_descriptor_header **hs_function;
+ struct gser_descs hs;
+};
+
+static inline struct f_gser *func_to_gser(struct usb_function *f)
+{
+ return container_of(f, struct f_gser, port.func);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* interface descriptor: */
+
+static struct usb_interface_descriptor gser_interface_desc __initdata = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ /* .bInterfaceNumber = DYNAMIC */
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ /* .iInterface = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor gser_fs_in_desc __initdata = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor gser_fs_out_desc __initdata = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *gser_fs_function[] __initdata = {
+ (struct usb_descriptor_header *) &gser_interface_desc,
+ (struct usb_descriptor_header *) &gser_fs_in_desc,
+ (struct usb_descriptor_header *) &gser_fs_out_desc,
+ NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor gser_hs_in_desc __initdata = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor gser_hs_out_desc __initdata = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *gser_hs_function[] __initdata = {
+ (struct usb_descriptor_header *) &gser_interface_desc,
+ (struct usb_descriptor_header *) &gser_hs_in_desc,
+ (struct usb_descriptor_header *) &gser_hs_out_desc,
+ NULL,
+};
+
+/* string descriptors: */
+
+static struct usb_string gser_string_defs[] = {
+ [0].s = "Generic Serial",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings gser_string_table = {
+ .language = 0x0409, /* en-us */
+ .strings = gser_string_defs,
+};
+
+static struct usb_gadget_strings *gser_strings[] = {
+ &gser_string_table,
+ NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct f_gser *gser = func_to_gser(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+
+ /* we know alt == 0, so this is an activation or a reset */
+
+ /* REVISIT hardware on PXA handles SET_INTERFACE;
+ * this is probably doing resets wrong ...
+ */
+ if (gser->port.in->driver_data) {
+ DBG(cdev, "reset generic ttyGS%d\n", gser->port_num);
+ gserial_disconnect(&gser->port);
+ } else {
+ DBG(cdev, "activate generic ttyGS%d\n", gser->port_num);
+ gser->port.in_desc = ep_choose(cdev->gadget,
+ gser->hs.in, gser->fs.in);
+ gser->port.out_desc = ep_choose(cdev->gadget,
+ gser->hs.out, gser->fs.out);
+ }
+ gserial_connect(&gser->port, gser->port_num);
+ return 0;
+}
+
+static void gser_disable(struct usb_function *f)
+{
+ struct f_gser *gser = func_to_gser(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+
+ DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num);
+ gserial_disconnect(&gser->port);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* serial function driver setup/binding */
+
+static int __init
+gser_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_gser *gser = func_to_gser(f);
+ int status;
+ struct usb_ep *ep;
+
+ /* allocate instance-specific interface IDs */
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ gser->data_id = status;
+ gser_interface_desc.bInterfaceNumber = status;
+
+ status = -ENODEV;
+
+ /* allocate instance-specific endpoints */
+ ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_in_desc);
+ if (!ep)
+ goto fail;
+ gser->port.in = ep;
+ ep->driver_data = cdev; /* claim */
+
+ ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_out_desc);
+ if (!ep)
+ goto fail;
+ gser->port.out = ep;
+ ep->driver_data = cdev; /* claim */
+
+ /* copy descriptors, and track endpoint copies */
+ f->descriptors = usb_copy_descriptors(gser_fs_function);
+
+ gser->fs.in = usb_find_endpoint(gser_fs_function,
+ f->descriptors, &gser_fs_in_desc);
+ gser->fs.out = usb_find_endpoint(gser_fs_function,
+ f->descriptors, &gser_fs_out_desc);
+
+
+ /* support all relevant hardware speeds... we expect that when
+ * hardware is dual speed, all bulk-capable endpoints work at
+ * both speeds
+ */
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ gser_hs_in_desc.bEndpointAddress =
+ gser_fs_in_desc.bEndpointAddress;
+ gser_hs_out_desc.bEndpointAddress =
+ gser_fs_out_desc.bEndpointAddress;
+
+ /* copy descriptors, and track endpoint copies */
+ f->hs_descriptors = usb_copy_descriptors(gser_hs_function);
+
+ gser->hs.in = usb_find_endpoint(gser_hs_function,
+ f->hs_descriptors, &gser_hs_in_desc);
+ gser->hs.out = usb_find_endpoint(gser_hs_function,
+ f->hs_descriptors, &gser_hs_out_desc);
+ }
+
+ DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
+ gser->port_num,
+ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ gser->port.in->name, gser->port.out->name);
+ return 0;
+
+fail:
+ /* we might as well release our claims on endpoints */
+ if (gser->port.out)
+ gser->port.out->driver_data = NULL;
+ if (gser->port.in)
+ gser->port.in->driver_data = NULL;
+
+ ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
+
+ return status;
+}
+
+static void
+gser_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ if (gadget_is_dualspeed(c->cdev->gadget))
+ usb_free_descriptors(f->hs_descriptors);
+ usb_free_descriptors(f->descriptors);
+ kfree(func_to_gser(f));
+}
+
+/**
+ * gser_bind_config - add a generic serial function to a configuration
+ * @c: the configuration to support the serial instance
+ * @port_num: /dev/ttyGS* port this interface will use
+ * Context: single threaded during gadget setup
+ *
+ * Returns zero on success, else negative errno.
+ *
+ * Caller must have called @gserial_setup() with enough ports to
+ * handle all the ones it binds. Caller is also responsible
+ * for calling @gserial_cleanup() before module unload.
+ */
+int __init gser_bind_config(struct usb_configuration *c, u8 port_num)
+{
+ struct f_gser *gser;
+ int status;
+
+ /* REVISIT might want instance-specific strings to help
+ * distinguish instances ...
+ */
+
+ /* maybe allocate device-global string ID */
+ if (gser_string_defs[0].id == 0) {
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ gser_string_defs[0].id = status;
+ }
+
+ /* allocate and initialize one new instance */
+ gser = kzalloc(sizeof *gser, GFP_KERNEL);
+ if (!gser)
+ return -ENOMEM;
+
+ gser->port_num = port_num;
+
+ gser->port.func.name = "gser";
+ gser->port.func.strings = gser_strings;
+ gser->port.func.bind = gser_bind;
+ gser->port.func.unbind = gser_unbind;
+ gser->port.func.set_alt = gser_set_alt;
+ gser->port.func.disable = gser_disable;
+
+ status = usb_add_function(c, &gser->port.func);
+ if (status)
+ kfree(gser);
+ return status;
+}
diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c
new file mode 100644
index 000000000000..b3a1c3ee3ad5
--- /dev/null
+++ b/drivers/usb/gadget/f_sourcesink.c
@@ -0,0 +1,589 @@
+/*
+ * f_sourcesink.c - USB peripheral source/sink configuration driver
+ *
+ * Copyright (C) 2003-2008 David Brownell
+ * All rights reserved.
+ *
+ * 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
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/utsname.h>
+#include <linux/device.h>
+
+#include "g_zero.h"
+#include "gadget_chips.h"
+
+
+/*
+ * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral
+ * controller drivers.
+ *
+ * This just sinks bulk packets OUT to the peripheral and sources them IN
+ * to the host, optionally with specific data patterns for integrity tests.
+ * As such it supports basic functionality and load tests.
+ *
+ * In terms of control messaging, this supports all the standard requests
+ * plus two that support control-OUT tests. If the optional "autoresume"
+ * mode is enabled, it provides good functional coverage for the "USBCV"
+ * test harness from USB-IF.
+ *
+ * Note that because this doesn't queue more than one request at a time,
+ * some other function must be used to test queueing logic. The network
+ * link (g_ether) is the best overall option for that, since its TX and RX
+ * queues are relatively independent, will receive a range of packet sizes,
+ * and can often be made to run out completely. Those issues are important
+ * when stress testing peripheral controller drivers.
+ *
+ *
+ * This is currently packaged as a configuration driver, which can't be
+ * combined with other functions to make composite devices. However, it
+ * can be combined with other independent configurations.
+ */
+struct f_sourcesink {
+ struct usb_function function;
+
+ struct usb_ep *in_ep;
+ struct usb_ep *out_ep;
+ struct timer_list resume;
+};
+
+static inline struct f_sourcesink *func_to_ss(struct usb_function *f)
+{
+ return container_of(f, struct f_sourcesink, function);
+}
+
+static unsigned autoresume;
+module_param(autoresume, uint, 0);
+MODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup");
+
+static unsigned pattern;
+module_param(pattern, uint, 0);
+MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63 ");
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_interface_descriptor source_sink_intf = {
+ .bLength = sizeof source_sink_intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ /* .iInterface = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor fs_source_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_sink_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *fs_source_sink_descs[] = {
+ (struct usb_descriptor_header *) &source_sink_intf,
+ (struct usb_descriptor_header *) &fs_sink_desc,
+ (struct usb_descriptor_header *) &fs_source_desc,
+ NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor hs_source_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_sink_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *hs_source_sink_descs[] = {
+ (struct usb_descriptor_header *) &source_sink_intf,
+ (struct usb_descriptor_header *) &hs_source_desc,
+ (struct usb_descriptor_header *) &hs_sink_desc,
+ NULL,
+};
+
+/* function-specific strings: */
+
+static struct usb_string strings_sourcesink[] = {
+ [0].s = "source and sink data",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_sourcesink = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_sourcesink,
+};
+
+static struct usb_gadget_strings *sourcesink_strings[] = {
+ &stringtab_sourcesink,
+ NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static void sourcesink_autoresume(unsigned long _c)
+{
+ struct usb_composite_dev *cdev = (void *)_c;
+ struct usb_gadget *g = cdev->gadget;
+
+ /* Normally the host would be woken up for something
+ * more significant than just a timer firing; likely
+ * because of some direct user request.
+ */
+ if (g->speed != USB_SPEED_UNKNOWN) {
+ int status = usb_gadget_wakeup(g);
+ DBG(cdev, "%s --> %d\n", __func__, status);
+ }
+}
+
+static int __init
+sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_sourcesink *ss = func_to_ss(f);
+ int id;
+
+ /* allocate interface ID(s) */
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ source_sink_intf.bInterfaceNumber = id;
+
+ /* allocate endpoints */
+ ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
+ if (!ss->in_ep) {
+autoconf_fail:
+ ERROR(cdev, "%s: can't autoconfigure on %s\n",
+ f->name, cdev->gadget->name);
+ return -ENODEV;
+ }
+ ss->in_ep->driver_data = cdev; /* claim */
+
+ ss->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc);
+ if (!ss->out_ep)
+ goto autoconf_fail;
+ ss->out_ep->driver_data = cdev; /* claim */
+
+ setup_timer(&ss->resume, sourcesink_autoresume,
+ (unsigned long) c->cdev);
+
+ /* support high speed hardware */
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ hs_source_desc.bEndpointAddress =
+ fs_source_desc.bEndpointAddress;
+ hs_sink_desc.bEndpointAddress =
+ fs_sink_desc.bEndpointAddress;
+ f->hs_descriptors = hs_source_sink_descs;
+ }
+
+ DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
+ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ f->name, ss->in_ep->name, ss->out_ep->name);
+ return 0;
+}
+
+static void
+sourcesink_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ kfree(func_to_ss(f));
+}
+
+/* optionally require specific source/sink data patterns */
+static int check_read_data(struct f_sourcesink *ss, struct usb_request *req)
+{
+ unsigned i;
+ u8 *buf = req->buf;
+ struct usb_composite_dev *cdev = ss->function.config->cdev;
+
+ for (i = 0; i < req->actual; i++, buf++) {
+ switch (pattern) {
+
+ /* all-zeroes has no synchronization issues */
+ case 0:
+ if (*buf == 0)
+ continue;
+ break;
+
+ /* "mod63" stays in sync with short-terminated transfers,
+ * OR otherwise when host and gadget agree on how large
+ * each usb transfer request should be. Resync is done
+ * with set_interface or set_config. (We *WANT* it to
+ * get quickly out of sync if controllers or their drivers
+ * stutter for any reason, including buffer duplcation...)
+ */
+ case 1:
+ if (*buf == (u8)(i % 63))
+ continue;
+ break;
+ }
+ ERROR(cdev, "bad OUT byte, buf[%d] = %d\n", i, *buf);
+ usb_ep_set_halt(ss->out_ep);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void reinit_write_data(struct usb_ep *ep, struct usb_request *req)
+{
+ unsigned i;
+ u8 *buf = req->buf;
+
+ switch (pattern) {
+ case 0:
+ memset(req->buf, 0, req->length);
+ break;
+ case 1:
+ for (i = 0; i < req->length; i++)
+ *buf++ = (u8) (i % 63);
+ break;
+ }
+}
+
+static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_sourcesink *ss = ep->driver_data;
+ struct usb_composite_dev *cdev = ss->function.config->cdev;
+ int status = req->status;
+
+ switch (status) {
+
+ case 0: /* normal completion? */
+ if (ep == ss->out_ep) {
+ check_read_data(ss, req);
+ memset(req->buf, 0x55, req->length);
+ } else
+ reinit_write_data(ep, req);
+ break;
+
+ /* this endpoint is normally active while we're configured */
+ case -ECONNABORTED: /* hardware forced ep reset */
+ case -ECONNRESET: /* request dequeued */
+ case -ESHUTDOWN: /* disconnect from host */
+ VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status,
+ req->actual, req->length);
+ if (ep == ss->out_ep)
+ check_read_data(ss, req);
+ free_ep_req(ep, req);
+ return;
+
+ case -EOVERFLOW: /* buffer overrun on read means that
+ * we didn't provide a big enough
+ * buffer.
+ */
+ default:
+#if 1
+ DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name,
+ status, req->actual, req->length);
+#endif
+ case -EREMOTEIO: /* short read */
+ break;
+ }
+
+ status = usb_ep_queue(ep, req, GFP_ATOMIC);
+ if (status) {
+ ERROR(cdev, "kill %s: resubmit %d bytes --> %d\n",
+ ep->name, req->length, status);
+ usb_ep_set_halt(ep);
+ /* FIXME recover later ... somehow */
+ }
+}
+
+static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in)
+{
+ struct usb_ep *ep;
+ struct usb_request *req;
+ int status;
+
+ ep = is_in ? ss->in_ep : ss->out_ep;
+ req = alloc_ep_req(ep);
+ if (!req)
+ return -ENOMEM;
+
+ req->complete = source_sink_complete;
+ if (is_in)
+ reinit_write_data(ep, req);
+ else
+ memset(req->buf, 0x55, req->length);
+
+ status = usb_ep_queue(ep, req, GFP_ATOMIC);
+ if (status) {
+ struct usb_composite_dev *cdev;
+
+ cdev = ss->function.config->cdev;
+ ERROR(cdev, "start %s %s --> %d\n",
+ is_in ? "IN" : "OUT",
+ ep->name, status);
+ free_ep_req(ep, req);
+ }
+
+ return status;
+}
+
+static void disable_source_sink(struct f_sourcesink *ss)
+{
+ struct usb_composite_dev *cdev;
+
+ cdev = ss->function.config->cdev;
+ disable_endpoints(cdev, ss->in_ep, ss->out_ep);
+ del_timer(&ss->resume);
+ VDBG(cdev, "%s disabled\n", ss->function.name);
+}
+
+static int
+enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss)
+{
+ int result = 0;
+ const struct usb_endpoint_descriptor *src, *sink;
+ struct usb_ep *ep;
+
+ src = ep_choose(cdev->gadget, &hs_source_desc, &fs_source_desc);
+ sink = ep_choose(cdev->gadget, &hs_sink_desc, &fs_sink_desc);
+
+ /* one endpoint writes (sources) zeroes IN (to the host) */
+ ep = ss->in_ep;
+ result = usb_ep_enable(ep, src);
+ if (result < 0)
+ return result;
+ ep->driver_data = ss;
+
+ result = source_sink_start_ep(ss, true);
+ if (result < 0) {
+fail:
+ ep = ss->in_ep;
+ usb_ep_disable(ep);
+ ep->driver_data = NULL;
+ return result;
+ }
+
+ /* one endpoint reads (sinks) anything OUT (from the host) */
+ ep = ss->out_ep;
+ result = usb_ep_enable(ep, sink);
+ if (result < 0)
+ goto fail;
+ ep->driver_data = ss;
+
+ result = source_sink_start_ep(ss, false);
+ if (result < 0) {
+ usb_ep_disable(ep);
+ ep->driver_data = NULL;
+ goto fail;
+ }
+
+ DBG(cdev, "%s enabled\n", ss->function.name);
+ return result;
+}
+
+static int sourcesink_set_alt(struct usb_function *f,
+ unsigned intf, unsigned alt)
+{
+ struct f_sourcesink *ss = func_to_ss(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+
+ /* we know alt is zero */
+ if (ss->in_ep->driver_data)
+ disable_source_sink(ss);
+ return enable_source_sink(cdev, ss);
+}
+
+static void sourcesink_disable(struct usb_function *f)
+{
+ struct f_sourcesink *ss = func_to_ss(f);
+
+ disable_source_sink(ss);
+}
+
+static void sourcesink_suspend(struct usb_function *f)
+{
+ struct f_sourcesink *ss = func_to_ss(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+
+ if (cdev->gadget->speed == USB_SPEED_UNKNOWN)
+ return;
+
+ if (autoresume) {
+ mod_timer(&ss->resume, jiffies + (HZ * autoresume));
+ DBG(cdev, "suspend, wakeup in %d seconds\n", autoresume);
+ } else
+ DBG(cdev, "%s\n", __func__);
+}
+
+static void sourcesink_resume(struct usb_function *f)
+{
+ struct f_sourcesink *ss = func_to_ss(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+
+ DBG(cdev, "%s\n", __func__);
+ del_timer(&ss->resume);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int __init sourcesink_bind_config(struct usb_configuration *c)
+{
+ struct f_sourcesink *ss;
+ int status;
+
+ ss = kzalloc(sizeof *ss, GFP_KERNEL);
+ if (!ss)
+ return -ENOMEM;
+
+ ss->function.name = "source/sink";
+ ss->function.descriptors = fs_source_sink_descs;
+ ss->function.bind = sourcesink_bind;
+ ss->function.unbind = sourcesink_unbind;
+ ss->function.set_alt = sourcesink_set_alt;
+ ss->function.disable = sourcesink_disable;
+ ss->function.suspend = sourcesink_suspend;
+ ss->function.resume = sourcesink_resume;
+
+ status = usb_add_function(c, &ss->function);
+ if (status)
+ kfree(ss);
+ return status;
+}
+
+static int sourcesink_setup(struct usb_configuration *c,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_request *req = c->cdev->req;
+ int value = -EOPNOTSUPP;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+ /* composite driver infrastructure handles everything except
+ * the two control test requests.
+ */
+ switch (ctrl->bRequest) {
+
+ /*
+ * These are the same vendor-specific requests supported by
+ * Intel's USB 2.0 compliance test devices. We exceed that
+ * device spec by allowing multiple-packet requests.
+ *
+ * NOTE: the Control-OUT data stays in req->buf ... better
+ * would be copying it into a scratch buffer, so that other
+ * requests may safely intervene.
+ */
+ case 0x5b: /* control WRITE test -- fill the buffer */
+ if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR))
+ goto unknown;
+ if (w_value || w_index)
+ break;
+ /* just read that many bytes into the buffer */
+ if (w_length > req->length)
+ break;
+ value = w_length;
+ break;
+ case 0x5c: /* control READ test -- return the buffer */
+ if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR))
+ goto unknown;
+ if (w_value || w_index)
+ break;
+ /* expect those bytes are still in the buffer; send back */
+ if (w_length > req->length)
+ break;
+ value = w_length;
+ break;
+
+ default:
+unknown:
+ VDBG(c->cdev,
+ "unknown control req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ }
+
+ /* respond with data transfer or status phase? */
+ if (value >= 0) {
+ VDBG(c->cdev, "source/sink req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ req->zero = 0;
+ req->length = value;
+ value = usb_ep_queue(c->cdev->gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0)
+ ERROR(c->cdev, "source/sinkc response, err %d\n",
+ value);
+ }
+
+ /* device either stalls (value < 0) or reports success */
+ return value;
+}
+
+static struct usb_configuration sourcesink_driver = {
+ .label = "source/sink",
+ .strings = sourcesink_strings,
+ .bind = sourcesink_bind_config,
+ .setup = sourcesink_setup,
+ .bConfigurationValue = 3,
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+ .bMaxPower = 1, /* 2 mA, minimal */
+ /* .iConfiguration = DYNAMIC */
+};
+
+/**
+ * sourcesink_add - add a source/sink testing configuration to a device
+ * @cdev: the device to support the configuration
+ */
+int __init sourcesink_add(struct usb_composite_dev *cdev)
+{
+ int id;
+
+ /* allocate string ID(s) */
+ id = usb_string_id(cdev);
+ if (id < 0)
+ return id;
+ strings_sourcesink[0].id = id;
+
+ source_sink_intf.iInterface = id;
+ sourcesink_driver.iConfiguration = id;
+
+ /* FIXME pass in bConfigurationValue and OTG stuff */
+
+ /* support autoresume for remote wakeup testing */
+ if (autoresume)
+ sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+
+ /* support OTG systems */
+ if (gadget_is_otg(cdev->gadget)) {
+ sourcesink_driver.descriptors = otg_desc;
+ sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+ return usb_add_config(cdev, &sourcesink_driver);
+}
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index 47bb9f09a1aa..15c24edbb61a 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -3867,8 +3867,8 @@ static int __init fsg_bind(struct usb_gadget *gadget)
curlun->dev.parent = &gadget->dev;
curlun->dev.driver = &fsg_driver.driver;
dev_set_drvdata(&curlun->dev, fsg);
- snprintf(curlun->dev.bus_id, BUS_ID_SIZE,
- "%s-lun%d", gadget->dev.bus_id, i);
+ dev_set_name(&curlun->dev,"%s-lun%d",
+ dev_name(&gadget->dev), i);
if ((rc = device_register(&curlun->dev)) != 0) {
INFO(fsg, "failed to register LUN%d: %d\n", i, rc);
diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_usb2_udc.c
index 18687543d7fa..1695382f30fe 100644
--- a/drivers/usb/gadget/fsl_usb2_udc.c
+++ b/drivers/usb/gadget/fsl_usb2_udc.c
@@ -2331,7 +2331,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
udc_controller->gadget.name = driver_name;
/* Setup gadget.dev and register with kernel */
- strcpy(udc_controller->gadget.dev.bus_id, "gadget");
+ dev_set_name(&udc_controller->gadget.dev, "gadget");
udc_controller->gadget.dev.release = fsl_udc_release;
udc_controller->gadget.dev.parent = &pdev->dev;
ret = device_register(&udc_controller->gadget.dev);
diff --git a/drivers/usb/gadget/g_zero.h b/drivers/usb/gadget/g_zero.h
new file mode 100644
index 000000000000..8c28330501cb
--- /dev/null
+++ b/drivers/usb/gadget/g_zero.h
@@ -0,0 +1,25 @@
+/*
+ * This header declares the utility functions used by "Gadget Zero", plus
+ * interfaces to its two single-configuration function drivers.
+ */
+
+#ifndef __G_ZERO_H
+#define __G_ZERO_H
+
+#include <linux/usb/composite.h>
+
+/* global state */
+extern unsigned buflen;
+extern const struct usb_descriptor_header **otg_desc;
+
+/* common utilities */
+struct usb_request *alloc_ep_req(struct usb_ep *ep);
+void free_ep_req(struct usb_ep *ep, struct usb_request *req);
+void disable_endpoints(struct usb_composite_dev *cdev,
+ struct usb_ep *in, struct usb_ep *out);
+
+/* configuration-specific linkup */
+int sourcesink_add(struct usb_composite_dev *cdev);
+int loopback_add(struct usb_composite_dev *cdev);
+
+#endif /* __G_ZERO_H */
diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c
index be6613afedbf..48f1c63b7013 100644
--- a/drivers/usb/gadget/goku_udc.c
+++ b/drivers/usb/gadget/goku_udc.c
@@ -1790,7 +1790,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
dev->gadget.ops = &goku_ops;
/* the "gadget" abstracts/virtualizes the controller */
- strcpy(dev->gadget.dev.bus_id, "gadget");
+ dev_set_name(&dev->gadget.dev, "gadget");
dev->gadget.dev.parent = &pdev->dev;
dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
dev->gadget.dev.release = gadget_release;
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index 69b0a2754f2a..dffb249c2a74 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -32,6 +32,7 @@
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/poll.h>
+#include <linux/smp_lock.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
@@ -483,8 +484,7 @@ ep_release (struct inode *inode, struct file *fd)
return 0;
}
-static int ep_ioctl (struct inode *inode, struct file *fd,
- unsigned code, unsigned long value)
+static long ep_ioctl(struct file *fd, unsigned code, unsigned long value)
{
struct ep_data *data = fd->private_data;
int status;
@@ -740,7 +740,7 @@ static const struct file_operations ep_io_operations = {
.read = ep_read,
.write = ep_write,
- .ioctl = ep_ioctl,
+ .unlocked_ioctl = ep_ioctl,
.release = ep_release,
.aio_read = ep_aio_read,
@@ -1294,15 +1294,18 @@ out:
return mask;
}
-static int dev_ioctl (struct inode *inode, struct file *fd,
- unsigned code, unsigned long value)
+static long dev_ioctl (struct file *fd, unsigned code, unsigned long value)
{
struct dev_data *dev = fd->private_data;
struct usb_gadget *gadget = dev->gadget;
+ long ret = -ENOTTY;
- if (gadget->ops->ioctl)
- return gadget->ops->ioctl (gadget, code, value);
- return -ENOTTY;
+ if (gadget->ops->ioctl) {
+ lock_kernel();
+ ret = gadget->ops->ioctl (gadget, code, value);
+ unlock_kernel();
+ }
+ return ret;
}
/* used after device configuration */
@@ -1314,7 +1317,7 @@ static const struct file_operations ep0_io_operations = {
.write = ep0_write,
.fasync = ep0_fasync,
.poll = ep0_poll,
- .ioctl = dev_ioctl,
+ .unlocked_ioctl = dev_ioctl,
.release = dev_release,
};
@@ -1964,7 +1967,7 @@ static const struct file_operations dev_init_operations = {
.open = dev_open,
.write = dev_config,
.fasync = ep0_fasync,
- .ioctl = dev_ioctl,
+ .unlocked_ioctl = dev_ioctl,
.release = dev_release,
};
diff --git a/drivers/usb/gadget/lh7a40x_udc.c b/drivers/usb/gadget/lh7a40x_udc.c
index 825abd2621b3..c6e7df04c69a 100644
--- a/drivers/usb/gadget/lh7a40x_udc.c
+++ b/drivers/usb/gadget/lh7a40x_udc.c
@@ -1970,7 +1970,7 @@ static const struct usb_gadget_ops lh7a40x_udc_ops = {
static void nop_release(struct device *dev)
{
- DEBUG("%s %s\n", __func__, dev->bus_id);
+ DEBUG("%s %s\n", __func__, dev_name(dev));
}
static struct lh7a40x_udc memory = {
diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c
index ee6b35fa870f..8da7535c0c70 100644
--- a/drivers/usb/gadget/m66592-udc.c
+++ b/drivers/usb/gadget/m66592-udc.c
@@ -1593,7 +1593,7 @@ static int __init m66592_probe(struct platform_device *pdev)
m66592->gadget.ops = &m66592_gadget_ops;
device_initialize(&m66592->gadget.dev);
- strcpy(m66592->gadget.dev.bus_id, "gadget");
+ dev_set_name(&m66592->gadget, "gadget");
m66592->gadget.is_dualspeed = 1;
m66592->gadget.dev.parent = &pdev->dev;
m66592->gadget.dev.dma_mask = pdev->dev.dma_mask;
diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c
index e01862300169..b67ab677af72 100644
--- a/drivers/usb/gadget/net2280.c
+++ b/drivers/usb/gadget/net2280.c
@@ -2768,7 +2768,7 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
dev->gadget.is_dualspeed = 1;
/* the "gadget" abstracts/virtualizes the controller */
- strcpy (dev->gadget.dev.bus_id, "gadget");
+ dev_set_name(&dev->gadget.dev, "gadget");
dev->gadget.dev.parent = &pdev->dev;
dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
dev->gadget.dev.release = gadget_release;
diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c
index 881d74c3d964..8feacaec682e 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/omap_udc.c
@@ -2662,7 +2662,7 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv)
udc->gadget.name = driver_name;
device_initialize(&udc->gadget.dev);
- strcpy (udc->gadget.dev.bus_id, "gadget");
+ dev_set_name(&udc->gadget.dev, "gadget");
udc->gadget.dev.release = omap_udc_release;
udc->gadget.dev.parent = &odev->dev;
if (use_dma)
diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c
index 76be75e3ab8f..6a84a78321e1 100644
--- a/drivers/usb/gadget/printer.c
+++ b/drivers/usb/gadget/printer.c
@@ -827,9 +827,8 @@ printer_poll(struct file *fd, poll_table *wait)
return status;
}
-static int
-printer_ioctl(struct inode *inode, struct file *fd, unsigned int code,
- unsigned long arg)
+static long
+printer_ioctl(struct file *fd, unsigned int code, unsigned long arg)
{
struct printer_dev *dev = fd->private_data;
unsigned long flags;
@@ -868,7 +867,7 @@ static struct file_operations printer_io_operations = {
.write = printer_write,
.fsync = printer_fsync,
.poll = printer_poll,
- .ioctl = printer_ioctl,
+ .unlocked_ioctl = printer_ioctl,
.release = printer_close
};
@@ -1360,8 +1359,8 @@ printer_bind(struct usb_gadget *gadget)
/* Setup the sysfs files for the printer gadget. */
- dev->pdev = device_create(usb_gadget_class, NULL, g_printer_devno,
- "g_printer");
+ dev->pdev = device_create_drvdata(usb_gadget_class, NULL,
+ g_printer_devno, NULL, "g_printer");
if (IS_ERR(dev->pdev)) {
ERROR(dev, "Failed to create device: g_printer\n");
goto fail;
diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c
index 08f699b1fc57..32c0f131886b 100644
--- a/drivers/usb/gadget/pxa2xx_udc.c
+++ b/drivers/usb/gadget/pxa2xx_udc.c
@@ -1813,7 +1813,7 @@ pxa2xx_udc_irq(int irq, void *_dev)
static void nop_release (struct device *dev)
{
- DMSG("%s %s\n", __func__, dev->bus_id);
+ DMSG("%s %s\n", __func__, dev_name(dev));
}
/* this uses load-time allocation and initialization (instead of
diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c
index d0677f5d3cd5..d252015576fb 100644
--- a/drivers/usb/gadget/rndis.c
+++ b/drivers/usb/gadget/rndis.c
@@ -1,8 +1,6 @@
/*
* RNDIS MSG parser
*
- * Version: $Id: rndis.c,v 1.19 2004/03/25 21:33:46 robert Exp $
- *
* Authors: Benedikt Spranger, Pengutronix
* Robert Schwebel, Pengutronix
*
@@ -30,6 +28,7 @@
#include <linux/init.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
#include <linux/netdevice.h>
#include <asm/io.h>
@@ -1296,14 +1295,11 @@ int rndis_rm_hdr(struct sk_buff *skb)
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
-static int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof,
- void *data)
+static int rndis_proc_show(struct seq_file *m, void *v)
{
- char *out = page;
- int len;
- rndis_params *param = (rndis_params *) data;
+ rndis_params *param = m->private;
- out += snprintf (out, count,
+ seq_printf(m,
"Config Nr. %d\n"
"used : %s\n"
"state : %s\n"
@@ -1326,25 +1322,13 @@ static int rndis_proc_read (char *page, char **start, off_t off, int count, int
(param->media_state) ? 0 : param->speed*100,
(param->media_state) ? "disconnected" : "connected",
param->vendorID, param->vendorDescr);
-
- len = out - page;
- len -= off;
-
- if (len < count) {
- *eof = 1;
- if (len <= 0)
- return 0;
- } else
- len = count;
-
- *start = page + off;
- return len;
+ return 0;
}
-static int rndis_proc_write (struct file *file, const char __user *buffer,
- unsigned long count, void *data)
+static ssize_t rndis_proc_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
{
- rndis_params *p = data;
+ rndis_params *p = PDE(file->f_path.dentry->d_inode)->data;
u32 speed = 0;
int i, fl_speed = 0;
@@ -1386,6 +1370,20 @@ static int rndis_proc_write (struct file *file, const char __user *buffer,
return count;
}
+static int rndis_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, rndis_proc_show, PDE(inode)->data);
+}
+
+static const struct file_operations rndis_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = rndis_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = rndis_proc_write,
+};
+
#define NAME_TEMPLATE "driver/rndis-%03d"
static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS];
@@ -1403,7 +1401,9 @@ int __init rndis_init (void)
sprintf (name, NAME_TEMPLATE, i);
if (!(rndis_connect_state [i]
- = create_proc_entry (name, 0660, NULL)))
+ = proc_create_data(name, 0660, NULL,
+ &rndis_proc_fops,
+ (void *)(rndis_per_dev_params + i))))
{
DBG("%s :remove entries", __func__);
while (i) {
@@ -1413,11 +1413,6 @@ int __init rndis_init (void)
DBG("\n");
return -EIO;
}
-
- rndis_connect_state [i]->write_proc = rndis_proc_write;
- rndis_connect_state [i]->read_proc = rndis_proc_read;
- rndis_connect_state [i]->data = (void *)
- (rndis_per_dev_params + i);
#endif
rndis_per_dev_params [i].confignr = i;
rndis_per_dev_params [i].used = 0;
diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h
index 397b149f3ca7..b917b4f34ea8 100644
--- a/drivers/usb/gadget/rndis.h
+++ b/drivers/usb/gadget/rndis.h
@@ -1,8 +1,6 @@
/*
* RNDIS Definitions for Remote NDIS
*
- * Version: $Id: rndis.h,v 1.15 2004/03/25 21:33:46 robert Exp $
- *
* Authors: Benedikt Spranger, Pengutronix
* Robert Schwebel, Pengutronix
*
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c
index fa019fa73334..c96fab8e970b 100644
--- a/drivers/usb/gadget/serial.c
+++ b/drivers/usb/gadget/serial.c
@@ -1,15 +1,8 @@
/*
- * g_serial.c -- USB gadget serial driver
+ * serial.c -- USB gadget serial driver
*
- * Copyright 2003 (C) Al Borchers (alborchers@steinerpoint.com)
- *
- * This code is based in part on the Gadget Zero driver, which
- * is Copyright (C) 2003 by David Brownell, all rights reserved.
- *
- * This code also borrows from usbserial.c, which is
- * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
- * Copyright (C) 2000 Peter Berger (pberger@brimson.com)
- * Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 by David Brownell
*
* This software is distributed under the terms of the GNU General
* Public License ("GPL") as published by the Free Software Foundation,
@@ -22,2254 +15,236 @@
#include <linux/tty.h>
#include <linux/tty_flip.h>
-#include <linux/usb/ch9.h>
-#include <linux/usb/cdc.h>
-#include <linux/usb/gadget.h>
-
+#include "u_serial.h"
#include "gadget_chips.h"
/* Defines */
-#define GS_VERSION_STR "v2.2"
-#define GS_VERSION_NUM 0x2200
+#define GS_VERSION_STR "v2.4"
+#define GS_VERSION_NUM 0x2400
#define GS_LONG_NAME "Gadget Serial"
-#define GS_SHORT_NAME "g_serial"
-
-#define GS_MAJOR 127
-#define GS_MINOR_START 0
-
-/* REVISIT only one port is supported for now;
- * see gs_{send,recv}_packet() ... no multiplexing,
- * and no support for multiple ACM devices.
- */
-#define GS_NUM_PORTS 1
-
-#define GS_NUM_CONFIGS 1
-#define GS_NO_CONFIG_ID 0
-#define GS_BULK_CONFIG_ID 1
-#define GS_ACM_CONFIG_ID 2
-
-#define GS_MAX_NUM_INTERFACES 2
-#define GS_BULK_INTERFACE_ID 0
-#define GS_CONTROL_INTERFACE_ID 0
-#define GS_DATA_INTERFACE_ID 1
-
-#define GS_MAX_DESC_LEN 256
-
-#define GS_DEFAULT_READ_Q_SIZE 32
-#define GS_DEFAULT_WRITE_Q_SIZE 32
-
-#define GS_DEFAULT_WRITE_BUF_SIZE 8192
-#define GS_TMP_BUF_SIZE 8192
-
-#define GS_CLOSE_TIMEOUT 15
-
-#define GS_DEFAULT_USE_ACM 0
-
-/* 9600-8-N-1 ... matches init_termios.c_cflag and defaults
- * expected by "usbser.sys" on MS-Windows.
- */
-#define GS_DEFAULT_DTE_RATE 9600
-#define GS_DEFAULT_DATA_BITS 8
-#define GS_DEFAULT_PARITY USB_CDC_NO_PARITY
-#define GS_DEFAULT_CHAR_FORMAT USB_CDC_1_STOP_BITS
-
-/* maxpacket and other transfer characteristics vary by speed. */
-static inline struct usb_endpoint_descriptor *
-choose_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs,
- struct usb_endpoint_descriptor *fs)
-{
- if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
- return hs;
- return fs;
-}
-
-
-/* debug settings */
-#ifdef DEBUG
-static int debug = 1;
-#else
-#define debug 0
-#endif
-
-#define gs_debug(format, arg...) \
- do { if (debug) pr_debug(format, ## arg); } while (0)
-#define gs_debug_level(level, format, arg...) \
- do { if (debug >= level) pr_debug(format, ## arg); } while (0)
+#define GS_VERSION_NAME GS_LONG_NAME " " GS_VERSION_STR
+/*-------------------------------------------------------------------------*/
/* Thanks to NetChip Technologies for donating this product ID.
- *
- * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
- * Instead: allocate your own, using normal USB-IF procedures.
- */
+*
+* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
+* Instead: allocate your own, using normal USB-IF procedures.
+*/
#define GS_VENDOR_ID 0x0525 /* NetChip */
#define GS_PRODUCT_ID 0xa4a6 /* Linux-USB Serial Gadget */
#define GS_CDC_PRODUCT_ID 0xa4a7 /* ... as CDC-ACM */
-#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */
-#define GS_NOTIFY_MAXPACKET 8
+/* string IDs are assigned dynamically */
+#define STRING_MANUFACTURER_IDX 0
+#define STRING_PRODUCT_IDX 1
+#define STRING_DESCRIPTION_IDX 2
-/* circular buffer */
-struct gs_buf {
- unsigned int buf_size;
- char *buf_buf;
- char *buf_get;
- char *buf_put;
-};
+static char manufacturer[50];
-/* the port structure holds info for each port, one for each minor number */
-struct gs_port {
- struct gs_dev *port_dev; /* pointer to device struct */
- struct tty_struct *port_tty; /* pointer to tty struct */
- spinlock_t port_lock;
- int port_num;
- int port_open_count;
- int port_in_use; /* open/close in progress */
- wait_queue_head_t port_write_wait;/* waiting to write */
- struct gs_buf *port_write_buf;
- struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
- u16 port_handshake_bits;
-#define RS232_RTS (1 << 1)
-#define RS232_DTE (1 << 0)
+static struct usb_string strings_dev[] = {
+ [STRING_MANUFACTURER_IDX].s = manufacturer,
+ [STRING_PRODUCT_IDX].s = GS_VERSION_NAME,
+ [STRING_DESCRIPTION_IDX].s = NULL /* updated; f(use_acm) */,
+ { } /* end of list */
};
-/* the device structure holds info for the USB device */
-struct gs_dev {
- struct usb_gadget *dev_gadget; /* gadget device pointer */
- spinlock_t dev_lock; /* lock for set/reset config */
- int dev_config; /* configuration number */
- struct usb_ep *dev_notify_ep; /* address of notify endpoint */
- struct usb_ep *dev_in_ep; /* address of in endpoint */
- struct usb_ep *dev_out_ep; /* address of out endpoint */
- struct usb_endpoint_descriptor /* descriptor of notify ep */
- *dev_notify_ep_desc;
- struct usb_endpoint_descriptor /* descriptor of in endpoint */
- *dev_in_ep_desc;
- struct usb_endpoint_descriptor /* descriptor of out endpoint */
- *dev_out_ep_desc;
- struct usb_request *dev_ctrl_req; /* control request */
- struct list_head dev_req_list; /* list of write requests */
- int dev_sched_port; /* round robin port scheduled */
- struct gs_port *dev_port[GS_NUM_PORTS]; /* the ports */
+static struct usb_gadget_strings stringtab_dev = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
};
-
-/* Functions */
-
-/* tty driver internals */
-static int gs_send(struct gs_dev *dev);
-static int gs_send_packet(struct gs_dev *dev, char *packet,
- unsigned int size);
-static int gs_recv_packet(struct gs_dev *dev, char *packet,
- unsigned int size);
-static void gs_read_complete(struct usb_ep *ep, struct usb_request *req);
-static void gs_write_complete(struct usb_ep *ep, struct usb_request *req);
-
-/* gadget driver internals */
-static int gs_set_config(struct gs_dev *dev, unsigned config);
-static void gs_reset_config(struct gs_dev *dev);
-static int gs_build_config_buf(u8 *buf, struct usb_gadget *g,
- u8 type, unsigned int index, int is_otg);
-
-static struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned int len,
- gfp_t kmalloc_flags);
-static void gs_free_req(struct usb_ep *ep, struct usb_request *req);
-
-static int gs_alloc_ports(struct gs_dev *dev, gfp_t kmalloc_flags);
-static void gs_free_ports(struct gs_dev *dev);
-
-/* circular buffer */
-static struct gs_buf *gs_buf_alloc(unsigned int size, gfp_t kmalloc_flags);
-static void gs_buf_free(struct gs_buf *gb);
-static void gs_buf_clear(struct gs_buf *gb);
-static unsigned int gs_buf_data_avail(struct gs_buf *gb);
-static unsigned int gs_buf_space_avail(struct gs_buf *gb);
-static unsigned int gs_buf_put(struct gs_buf *gb, const char *buf,
- unsigned int count);
-static unsigned int gs_buf_get(struct gs_buf *gb, char *buf,
- unsigned int count);
-
-
-/* Globals */
-
-static struct gs_dev *gs_device;
-
-static struct mutex gs_open_close_lock[GS_NUM_PORTS];
-
-
-/*-------------------------------------------------------------------------*/
-
-/* USB descriptors */
-
-#define GS_MANUFACTURER_STR_ID 1
-#define GS_PRODUCT_STR_ID 2
-#define GS_SERIAL_STR_ID 3
-#define GS_BULK_CONFIG_STR_ID 4
-#define GS_ACM_CONFIG_STR_ID 5
-#define GS_CONTROL_STR_ID 6
-#define GS_DATA_STR_ID 7
-
-/* static strings, in UTF-8 */
-static char manufacturer[50];
-static struct usb_string gs_strings[] = {
- { GS_MANUFACTURER_STR_ID, manufacturer },
- { GS_PRODUCT_STR_ID, GS_LONG_NAME },
- { GS_BULK_CONFIG_STR_ID, "Gadget Serial Bulk" },
- { GS_ACM_CONFIG_STR_ID, "Gadget Serial CDC ACM" },
- { GS_CONTROL_STR_ID, "Gadget Serial Control" },
- { GS_DATA_STR_ID, "Gadget Serial Data" },
- { } /* end of list */
-};
-
-static struct usb_gadget_strings gs_string_table = {
- .language = 0x0409, /* en-us */
- .strings = gs_strings,
+static struct usb_gadget_strings *dev_strings[] = {
+ &stringtab_dev,
+ NULL,
};
-static struct usb_device_descriptor gs_device_desc = {
+static struct usb_device_descriptor device_desc = {
.bLength = USB_DT_DEVICE_SIZE,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = __constant_cpu_to_le16(0x0200),
+ /* .bDeviceClass = f(use_acm) */
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
+ /* .bMaxPacketSize0 = f(hardware) */
.idVendor = __constant_cpu_to_le16(GS_VENDOR_ID),
- .idProduct = __constant_cpu_to_le16(GS_PRODUCT_ID),
- .iManufacturer = GS_MANUFACTURER_STR_ID,
- .iProduct = GS_PRODUCT_STR_ID,
- .bNumConfigurations = GS_NUM_CONFIGS,
+ /* .idProduct = f(use_acm) */
+ /* .iManufacturer = DYNAMIC */
+ /* .iProduct = DYNAMIC */
+ .bNumConfigurations = 1,
};
-static struct usb_otg_descriptor gs_otg_descriptor = {
- .bLength = sizeof(gs_otg_descriptor),
+static struct usb_otg_descriptor otg_descriptor = {
+ .bLength = sizeof otg_descriptor,
.bDescriptorType = USB_DT_OTG,
- .bmAttributes = USB_OTG_SRP,
-};
-
-static struct usb_config_descriptor gs_bulk_config_desc = {
- .bLength = USB_DT_CONFIG_SIZE,
- .bDescriptorType = USB_DT_CONFIG,
- /* .wTotalLength computed dynamically */
- .bNumInterfaces = 1,
- .bConfigurationValue = GS_BULK_CONFIG_ID,
- .iConfiguration = GS_BULK_CONFIG_STR_ID,
- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
- .bMaxPower = 1,
-};
-
-static struct usb_config_descriptor gs_acm_config_desc = {
- .bLength = USB_DT_CONFIG_SIZE,
- .bDescriptorType = USB_DT_CONFIG,
- /* .wTotalLength computed dynamically */
- .bNumInterfaces = 2,
- .bConfigurationValue = GS_ACM_CONFIG_ID,
- .iConfiguration = GS_ACM_CONFIG_STR_ID,
- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
- .bMaxPower = 1,
-};
-
-static const struct usb_interface_descriptor gs_bulk_interface_desc = {
- .bLength = USB_DT_INTERFACE_SIZE,
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = GS_BULK_INTERFACE_ID,
- .bNumEndpoints = 2,
- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
- .bInterfaceSubClass = 0,
- .bInterfaceProtocol = 0,
- .iInterface = GS_DATA_STR_ID,
-};
-
-static const struct usb_interface_descriptor gs_control_interface_desc = {
- .bLength = USB_DT_INTERFACE_SIZE,
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = GS_CONTROL_INTERFACE_ID,
- .bNumEndpoints = 1,
- .bInterfaceClass = USB_CLASS_COMM,
- .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
- .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
- .iInterface = GS_CONTROL_STR_ID,
-};
-
-static const struct usb_interface_descriptor gs_data_interface_desc = {
- .bLength = USB_DT_INTERFACE_SIZE,
- .bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = GS_DATA_INTERFACE_ID,
- .bNumEndpoints = 2,
- .bInterfaceClass = USB_CLASS_CDC_DATA,
- .bInterfaceSubClass = 0,
- .bInterfaceProtocol = 0,
- .iInterface = GS_DATA_STR_ID,
-};
-
-static const struct usb_cdc_header_desc gs_header_desc = {
- .bLength = sizeof(gs_header_desc),
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubType = USB_CDC_HEADER_TYPE,
- .bcdCDC = __constant_cpu_to_le16(0x0110),
-};
-
-static const struct usb_cdc_call_mgmt_descriptor gs_call_mgmt_descriptor = {
- .bLength = sizeof(gs_call_mgmt_descriptor),
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
- .bmCapabilities = 0,
- .bDataInterface = 1, /* index of data interface */
-};
-
-static struct usb_cdc_acm_descriptor gs_acm_descriptor = {
- .bLength = sizeof(gs_acm_descriptor),
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubType = USB_CDC_ACM_TYPE,
- .bmCapabilities = (1 << 1),
-};
-
-static const struct usb_cdc_union_desc gs_union_desc = {
- .bLength = sizeof(gs_union_desc),
- .bDescriptorType = USB_DT_CS_INTERFACE,
- .bDescriptorSubType = USB_CDC_UNION_TYPE,
- .bMasterInterface0 = 0, /* index of control interface */
- .bSlaveInterface0 = 1, /* index of data interface */
-};
-
-static struct usb_endpoint_descriptor gs_fullspeed_notify_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET),
- .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL,
-};
-
-static struct usb_endpoint_descriptor gs_fullspeed_in_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_endpoint_descriptor gs_fullspeed_out_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_OUT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
-};
-
-static const struct usb_descriptor_header *gs_bulk_fullspeed_function[] = {
- (struct usb_descriptor_header *) &gs_otg_descriptor,
- (struct usb_descriptor_header *) &gs_bulk_interface_desc,
- (struct usb_descriptor_header *) &gs_fullspeed_in_desc,
- (struct usb_descriptor_header *) &gs_fullspeed_out_desc,
- NULL,
-};
-
-static const struct usb_descriptor_header *gs_acm_fullspeed_function[] = {
- (struct usb_descriptor_header *) &gs_otg_descriptor,
- (struct usb_descriptor_header *) &gs_control_interface_desc,
- (struct usb_descriptor_header *) &gs_header_desc,
- (struct usb_descriptor_header *) &gs_call_mgmt_descriptor,
- (struct usb_descriptor_header *) &gs_acm_descriptor,
- (struct usb_descriptor_header *) &gs_union_desc,
- (struct usb_descriptor_header *) &gs_fullspeed_notify_desc,
- (struct usb_descriptor_header *) &gs_data_interface_desc,
- (struct usb_descriptor_header *) &gs_fullspeed_in_desc,
- (struct usb_descriptor_header *) &gs_fullspeed_out_desc,
- NULL,
-};
-static struct usb_endpoint_descriptor gs_highspeed_notify_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bEndpointAddress = USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET),
- .bInterval = GS_LOG2_NOTIFY_INTERVAL+4,
-};
-
-static struct usb_endpoint_descriptor gs_highspeed_in_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = __constant_cpu_to_le16(512),
-};
-
-static struct usb_endpoint_descriptor gs_highspeed_out_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = __constant_cpu_to_le16(512),
-};
-
-static struct usb_qualifier_descriptor gs_qualifier_desc = {
- .bLength = sizeof(struct usb_qualifier_descriptor),
- .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
- .bcdUSB = __constant_cpu_to_le16 (0x0200),
- /* assumes ep0 uses the same value for both speeds ... */
- .bNumConfigurations = GS_NUM_CONFIGS,
-};
-
-static const struct usb_descriptor_header *gs_bulk_highspeed_function[] = {
- (struct usb_descriptor_header *) &gs_otg_descriptor,
- (struct usb_descriptor_header *) &gs_bulk_interface_desc,
- (struct usb_descriptor_header *) &gs_highspeed_in_desc,
- (struct usb_descriptor_header *) &gs_highspeed_out_desc,
- NULL,
+ /* REVISIT SRP-only hardware is possible, although
+ * it would not be called "OTG" ...
+ */
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
};
-static const struct usb_descriptor_header *gs_acm_highspeed_function[] = {
- (struct usb_descriptor_header *) &gs_otg_descriptor,
- (struct usb_descriptor_header *) &gs_control_interface_desc,
- (struct usb_descriptor_header *) &gs_header_desc,
- (struct usb_descriptor_header *) &gs_call_mgmt_descriptor,
- (struct usb_descriptor_header *) &gs_acm_descriptor,
- (struct usb_descriptor_header *) &gs_union_desc,
- (struct usb_descriptor_header *) &gs_highspeed_notify_desc,
- (struct usb_descriptor_header *) &gs_data_interface_desc,
- (struct usb_descriptor_header *) &gs_highspeed_in_desc,
- (struct usb_descriptor_header *) &gs_highspeed_out_desc,
+static const struct usb_descriptor_header *otg_desc[] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
NULL,
};
-
/*-------------------------------------------------------------------------*/
/* Module */
-MODULE_DESCRIPTION(GS_LONG_NAME);
+MODULE_DESCRIPTION(GS_VERSION_NAME);
MODULE_AUTHOR("Al Borchers");
+MODULE_AUTHOR("David Brownell");
MODULE_LICENSE("GPL");
-#ifdef DEBUG
-module_param(debug, int, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(debug, "Enable debugging, 0=off, 1=on");
-#endif
-
-static unsigned int read_q_size = GS_DEFAULT_READ_Q_SIZE;
-module_param(read_q_size, uint, S_IRUGO);
-MODULE_PARM_DESC(read_q_size, "Read request queue size, default=32");
-
-static unsigned int write_q_size = GS_DEFAULT_WRITE_Q_SIZE;
-module_param(write_q_size, uint, S_IRUGO);
-MODULE_PARM_DESC(write_q_size, "Write request queue size, default=32");
+static int use_acm = true;
+module_param(use_acm, bool, 0);
+MODULE_PARM_DESC(use_acm, "Use CDC ACM, default=yes");
-static unsigned int write_buf_size = GS_DEFAULT_WRITE_BUF_SIZE;
-module_param(write_buf_size, uint, S_IRUGO);
-MODULE_PARM_DESC(write_buf_size, "Write buffer size, default=8192");
-
-static unsigned int use_acm = GS_DEFAULT_USE_ACM;
-module_param(use_acm, uint, S_IRUGO);
-MODULE_PARM_DESC(use_acm, "Use CDC ACM, 0=no, 1=yes, default=no");
+static unsigned n_ports = 1;
+module_param(n_ports, uint, 0);
+MODULE_PARM_DESC(n_ports, "number of ports to create, default=1");
/*-------------------------------------------------------------------------*/
-/* TTY Driver */
-
-/*
- * gs_open
- */
-static int gs_open(struct tty_struct *tty, struct file *file)
-{
- int port_num;
- unsigned long flags;
- struct gs_port *port;
- struct gs_dev *dev;
- struct gs_buf *buf;
- struct mutex *mtx;
- int ret;
-
- port_num = tty->index;
-
- gs_debug("gs_open: (%d,%p,%p)\n", port_num, tty, file);
-
- if (port_num < 0 || port_num >= GS_NUM_PORTS) {
- pr_err("gs_open: (%d,%p,%p) invalid port number\n",
- port_num, tty, file);
- return -ENODEV;
- }
-
- dev = gs_device;
-
- if (dev == NULL) {
- pr_err("gs_open: (%d,%p,%p) NULL device pointer\n",
- port_num, tty, file);
- return -ENODEV;
- }
-
- mtx = &gs_open_close_lock[port_num];
- if (mutex_lock_interruptible(mtx)) {
- pr_err("gs_open: (%d,%p,%p) interrupted waiting for mutex\n",
- port_num, tty, file);
- return -ERESTARTSYS;
- }
-
- spin_lock_irqsave(&dev->dev_lock, flags);
-
- if (dev->dev_config == GS_NO_CONFIG_ID) {
- pr_err("gs_open: (%d,%p,%p) device is not connected\n",
- port_num, tty, file);
- ret = -ENODEV;
- goto exit_unlock_dev;
- }
-
- port = dev->dev_port[port_num];
-
- if (port == NULL) {
- pr_err("gs_open: (%d,%p,%p) NULL port pointer\n",
- port_num, tty, file);
- ret = -ENODEV;
- goto exit_unlock_dev;
- }
-
- spin_lock(&port->port_lock);
- spin_unlock(&dev->dev_lock);
-
- if (port->port_dev == NULL) {
- pr_err("gs_open: (%d,%p,%p) port disconnected (1)\n",
- port_num, tty, file);
- ret = -EIO;
- goto exit_unlock_port;
- }
-
- if (port->port_open_count > 0) {
- ++port->port_open_count;
- gs_debug("gs_open: (%d,%p,%p) already open\n",
- port_num, tty, file);
- ret = 0;
- goto exit_unlock_port;
- }
-
- tty->driver_data = NULL;
-
- /* mark port as in use, we can drop port lock and sleep if necessary */
- port->port_in_use = 1;
-
- /* allocate write buffer on first open */
- if (port->port_write_buf == NULL) {
- spin_unlock_irqrestore(&port->port_lock, flags);
- buf = gs_buf_alloc(write_buf_size, GFP_KERNEL);
- spin_lock_irqsave(&port->port_lock, flags);
-
- /* might have been disconnected while asleep, check */
- if (port->port_dev == NULL) {
- pr_err("gs_open: (%d,%p,%p) port disconnected (2)\n",
- port_num, tty, file);
- port->port_in_use = 0;
- ret = -EIO;
- goto exit_unlock_port;
- }
-
- if ((port->port_write_buf=buf) == NULL) {
- pr_err("gs_open: (%d,%p,%p) cannot allocate "
- "port write buffer\n",
- port_num, tty, file);
- port->port_in_use = 0;
- ret = -ENOMEM;
- goto exit_unlock_port;
- }
-
- }
-
- /* wait for carrier detect (not implemented) */
-
- /* might have been disconnected while asleep, check */
- if (port->port_dev == NULL) {
- pr_err("gs_open: (%d,%p,%p) port disconnected (3)\n",
- port_num, tty, file);
- port->port_in_use = 0;
- ret = -EIO;
- goto exit_unlock_port;
- }
-
- tty->driver_data = port;
- port->port_tty = tty;
- port->port_open_count = 1;
- port->port_in_use = 0;
-
- gs_debug("gs_open: (%d,%p,%p) completed\n", port_num, tty, file);
-
- ret = 0;
-
-exit_unlock_port:
- spin_unlock_irqrestore(&port->port_lock, flags);
- mutex_unlock(mtx);
- return ret;
-
-exit_unlock_dev:
- spin_unlock_irqrestore(&dev->dev_lock, flags);
- mutex_unlock(mtx);
- return ret;
-
-}
-
-/*
- * gs_close
- */
-
-static int gs_write_finished_event_safely(struct gs_port *p)
-{
- int cond;
-
- spin_lock_irq(&(p)->port_lock);
- cond = !(p)->port_dev || !gs_buf_data_avail((p)->port_write_buf);
- spin_unlock_irq(&(p)->port_lock);
- return cond;
-}
-
-static void gs_close(struct tty_struct *tty, struct file *file)
-{
- struct gs_port *port = tty->driver_data;
- struct mutex *mtx;
-
- if (port == NULL) {
- pr_err("gs_close: NULL port pointer\n");
- return;
- }
-
- gs_debug("gs_close: (%d,%p,%p)\n", port->port_num, tty, file);
-
- mtx = &gs_open_close_lock[port->port_num];
- mutex_lock(mtx);
-
- spin_lock_irq(&port->port_lock);
-
- if (port->port_open_count == 0) {
- pr_err("gs_close: (%d,%p,%p) port is already closed\n",
- port->port_num, tty, file);
- goto exit;
- }
-
- if (port->port_open_count > 1) {
- --port->port_open_count;
- goto exit;
- }
-
- /* free disconnected port on final close */
- if (port->port_dev == NULL) {
- kfree(port);
- goto exit;
- }
-
- /* mark port as closed but in use, we can drop port lock */
- /* and sleep if necessary */
- port->port_in_use = 1;
- port->port_open_count = 0;
-
- /* wait for write buffer to drain, or */
- /* at most GS_CLOSE_TIMEOUT seconds */
- if (gs_buf_data_avail(port->port_write_buf) > 0) {
- spin_unlock_irq(&port->port_lock);
- wait_event_interruptible_timeout(port->port_write_wait,
- gs_write_finished_event_safely(port),
- GS_CLOSE_TIMEOUT * HZ);
- spin_lock_irq(&port->port_lock);
- }
-
- /* free disconnected port on final close */
- /* (might have happened during the above sleep) */
- if (port->port_dev == NULL) {
- kfree(port);
- goto exit;
- }
-
- gs_buf_clear(port->port_write_buf);
-
- tty->driver_data = NULL;
- port->port_tty = NULL;
- port->port_in_use = 0;
-
- gs_debug("gs_close: (%d,%p,%p) completed\n",
- port->port_num, tty, file);
-
-exit:
- spin_unlock_irq(&port->port_lock);
- mutex_unlock(mtx);
-}
-
-/*
- * gs_write
- */
-static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
+static int __init serial_bind_config(struct usb_configuration *c)
{
- unsigned long flags;
- struct gs_port *port = tty->driver_data;
- int ret;
-
- if (port == NULL) {
- pr_err("gs_write: NULL port pointer\n");
- return -EIO;
- }
-
- gs_debug("gs_write: (%d,%p) writing %d bytes\n", port->port_num, tty,
- count);
-
- if (count == 0)
- return 0;
-
- spin_lock_irqsave(&port->port_lock, flags);
-
- if (port->port_dev == NULL) {
- pr_err("gs_write: (%d,%p) port is not connected\n",
- port->port_num, tty);
- ret = -EIO;
- goto exit;
- }
-
- if (port->port_open_count == 0) {
- pr_err("gs_write: (%d,%p) port is closed\n",
- port->port_num, tty);
- ret = -EBADF;
- goto exit;
- }
-
- count = gs_buf_put(port->port_write_buf, buf, count);
-
- spin_unlock_irqrestore(&port->port_lock, flags);
-
- gs_send(gs_device);
-
- gs_debug("gs_write: (%d,%p) wrote %d bytes\n", port->port_num, tty,
- count);
+ unsigned i;
+ int status = 0;
- return count;
-
-exit:
- spin_unlock_irqrestore(&port->port_lock, flags);
- return ret;
-}
-
-/*
- * gs_put_char
- */
-static int gs_put_char(struct tty_struct *tty, unsigned char ch)
-{
- unsigned long flags;
- struct gs_port *port = tty->driver_data;
- int ret = 0;
-
- if (port == NULL) {
- pr_err("gs_put_char: NULL port pointer\n");
- return 0;
- }
-
- gs_debug("gs_put_char: (%d,%p) char=0x%x, called from %p\n",
- port->port_num, tty, ch, __builtin_return_address(0));
-
- spin_lock_irqsave(&port->port_lock, flags);
-
- if (port->port_dev == NULL) {
- pr_err("gs_put_char: (%d,%p) port is not connected\n",
- port->port_num, tty);
- goto exit;
- }
-
- if (port->port_open_count == 0) {
- pr_err("gs_put_char: (%d,%p) port is closed\n",
- port->port_num, tty);
- goto exit;
- }
-
- ret = gs_buf_put(port->port_write_buf, &ch, 1);
-
-exit:
- spin_unlock_irqrestore(&port->port_lock, flags);
- return ret;
-}
-
-/*
- * gs_flush_chars
- */
-static void gs_flush_chars(struct tty_struct *tty)
-{
- unsigned long flags;
- struct gs_port *port = tty->driver_data;
-
- if (port == NULL) {
- pr_err("gs_flush_chars: NULL port pointer\n");
- return;
- }
-
- gs_debug("gs_flush_chars: (%d,%p)\n", port->port_num, tty);
-
- spin_lock_irqsave(&port->port_lock, flags);
-
- if (port->port_dev == NULL) {
- pr_err("gs_flush_chars: (%d,%p) port is not connected\n",
- port->port_num, tty);
- goto exit;
- }
-
- if (port->port_open_count == 0) {
- pr_err("gs_flush_chars: (%d,%p) port is closed\n",
- port->port_num, tty);
- goto exit;
- }
-
- spin_unlock_irqrestore(&port->port_lock, flags);
-
- gs_send(gs_device);
-
- return;
-
-exit:
- spin_unlock_irqrestore(&port->port_lock, flags);
-}
-
-/*
- * gs_write_room
- */
-static int gs_write_room(struct tty_struct *tty)
-{
-
- int room = 0;
- unsigned long flags;
- struct gs_port *port = tty->driver_data;
-
-
- if (port == NULL)
- return 0;
-
- spin_lock_irqsave(&port->port_lock, flags);
-
- if (port->port_dev != NULL && port->port_open_count > 0
- && port->port_write_buf != NULL)
- room = gs_buf_space_avail(port->port_write_buf);
-
- spin_unlock_irqrestore(&port->port_lock, flags);
-
- gs_debug("gs_write_room: (%d,%p) room=%d\n",
- port->port_num, tty, room);
-
- return room;
-}
-
-/*
- * gs_chars_in_buffer
- */
-static int gs_chars_in_buffer(struct tty_struct *tty)
-{
- int chars = 0;
- unsigned long flags;
- struct gs_port *port = tty->driver_data;
-
- if (port == NULL)
- return 0;
-
- spin_lock_irqsave(&port->port_lock, flags);
-
- if (port->port_dev != NULL && port->port_open_count > 0
- && port->port_write_buf != NULL)
- chars = gs_buf_data_avail(port->port_write_buf);
-
- spin_unlock_irqrestore(&port->port_lock, flags);
-
- gs_debug("gs_chars_in_buffer: (%d,%p) chars=%d\n",
- port->port_num, tty, chars);
-
- return chars;
-}
-
-/*
- * gs_throttle
- */
-static void gs_throttle(struct tty_struct *tty)
-{
-}
-
-/*
- * gs_unthrottle
- */
-static void gs_unthrottle(struct tty_struct *tty)
-{
-}
-
-/*
- * gs_break
- */
-static void gs_break(struct tty_struct *tty, int break_state)
-{
-}
-
-/*
- * gs_ioctl
- */
-static int gs_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct gs_port *port = tty->driver_data;
-
- if (port == NULL) {
- pr_err("gs_ioctl: NULL port pointer\n");
- return -EIO;
+ for (i = 0; i < n_ports && status == 0; i++) {
+ if (use_acm)
+ status = acm_bind_config(c, i);
+ else
+ status = gser_bind_config(c, i);
}
-
- gs_debug("gs_ioctl: (%d,%p,%p) cmd=0x%4.4x, arg=%lu\n",
- port->port_num, tty, file, cmd, arg);
-
- /* handle ioctls */
-
- /* could not handle ioctl */
- return -ENOIOCTLCMD;
-}
-
-/*
- * gs_set_termios
- */
-static void gs_set_termios(struct tty_struct *tty, struct ktermios *old)
-{
+ return status;
}
-static const struct tty_operations gs_tty_ops = {
- .open = gs_open,
- .close = gs_close,
- .write = gs_write,
- .put_char = gs_put_char,
- .flush_chars = gs_flush_chars,
- .write_room = gs_write_room,
- .ioctl = gs_ioctl,
- .set_termios = gs_set_termios,
- .throttle = gs_throttle,
- .unthrottle = gs_unthrottle,
- .break_ctl = gs_break,
- .chars_in_buffer = gs_chars_in_buffer,
+static struct usb_configuration serial_config_driver = {
+ /* .label = f(use_acm) */
+ .bind = serial_bind_config,
+ /* .bConfigurationValue = f(use_acm) */
+ /* .iConfiguration = DYNAMIC */
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+ .bMaxPower = 1, /* 2 mA, minimal */
};
-/*-------------------------------------------------------------------------*/
-
-/*
-* gs_send
-*
-* This function finds available write requests, calls
-* gs_send_packet to fill these packets with data, and
-* continues until either there are no more write requests
-* available or no more data to send. This function is
-* run whenever data arrives or write requests are available.
-*/
-static int gs_send(struct gs_dev *dev)
-{
- int ret,len;
- unsigned long flags;
- struct usb_ep *ep;
- struct usb_request *req;
-
- if (dev == NULL) {
- pr_err("gs_send: NULL device pointer\n");
- return -ENODEV;
- }
-
- spin_lock_irqsave(&dev->dev_lock, flags);
-
- ep = dev->dev_in_ep;
-
- while(!list_empty(&dev->dev_req_list)) {
-
- req = list_entry(dev->dev_req_list.next,
- struct usb_request, list);
-
- len = gs_send_packet(dev, req->buf, ep->maxpacket);
-
- if (len > 0) {
- gs_debug_level(3, "gs_send: len=%d, 0x%2.2x "
- "0x%2.2x 0x%2.2x ...\n", len,
- *((unsigned char *)req->buf),
- *((unsigned char *)req->buf+1),
- *((unsigned char *)req->buf+2));
- list_del(&req->list);
- req->length = len;
- spin_unlock_irqrestore(&dev->dev_lock, flags);
- if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) {
- pr_err(
- "gs_send: cannot queue read request, ret=%d\n",
- ret);
- spin_lock_irqsave(&dev->dev_lock, flags);
- break;
- }
- spin_lock_irqsave(&dev->dev_lock, flags);
- } else {
- break;
- }
-
- }
-
- spin_unlock_irqrestore(&dev->dev_lock, flags);
-
- return 0;
-}
-
-/*
- * gs_send_packet
- *
- * If there is data to send, a packet is built in the given
- * buffer and the size is returned. If there is no data to
- * send, 0 is returned. If there is any error a negative
- * error number is returned.
- *
- * Called during USB completion routine, on interrupt time.
- *
- * We assume that disconnect will not happen until all completion
- * routines have completed, so we can assume that the dev_port
- * array does not change during the lifetime of this function.
- */
-static int gs_send_packet(struct gs_dev *dev, char *packet, unsigned int size)
-{
- unsigned int len;
- struct gs_port *port;
-
- /* TEMPORARY -- only port 0 is supported right now */
- port = dev->dev_port[0];
-
- if (port == NULL) {
- pr_err("gs_send_packet: port=%d, NULL port pointer\n", 0);
- return -EIO;
- }
-
- spin_lock(&port->port_lock);
-
- len = gs_buf_data_avail(port->port_write_buf);
- if (len < size)
- size = len;
-
- if (size == 0)
- goto exit;
-
- size = gs_buf_get(port->port_write_buf, packet, size);
-
- if (port->port_tty)
- wake_up_interruptible(&port->port_tty->write_wait);
-
-exit:
- spin_unlock(&port->port_lock);
- return size;
-}
-
-/*
- * gs_recv_packet
- *
- * Called for each USB packet received. Reads the packet
- * header and stuffs the data in the appropriate tty buffer.
- * Returns 0 if successful, or a negative error number.
- *
- * Called during USB completion routine, on interrupt time.
- *
- * We assume that disconnect will not happen until all completion
- * routines have completed, so we can assume that the dev_port
- * array does not change during the lifetime of this function.
- */
-static int gs_recv_packet(struct gs_dev *dev, char *packet, unsigned int size)
-{
- unsigned int len;
- struct gs_port *port;
- int ret;
- struct tty_struct *tty;
-
- /* TEMPORARY -- only port 0 is supported right now */
- port = dev->dev_port[0];
-
- if (port == NULL) {
- pr_err("gs_recv_packet: port=%d, NULL port pointer\n",
- port->port_num);
- return -EIO;
- }
-
- spin_lock(&port->port_lock);
-
- if (port->port_open_count == 0) {
- pr_err("gs_recv_packet: port=%d, port is closed\n",
- port->port_num);
- ret = -EIO;
- goto exit;
- }
-
-
- tty = port->port_tty;
-
- if (tty == NULL) {
- pr_err("gs_recv_packet: port=%d, NULL tty pointer\n",
- port->port_num);
- ret = -EIO;
- goto exit;
- }
-
- if (port->port_tty->magic != TTY_MAGIC) {
- pr_err("gs_recv_packet: port=%d, bad tty magic\n",
- port->port_num);
- ret = -EIO;
- goto exit;
- }
-
- len = tty_buffer_request_room(tty, size);
- if (len > 0) {
- tty_insert_flip_string(tty, packet, len);
- tty_flip_buffer_push(port->port_tty);
- wake_up_interruptible(&port->port_tty->read_wait);
- }
- ret = 0;
-exit:
- spin_unlock(&port->port_lock);
- return ret;
-}
-
-/*
-* gs_read_complete
-*/
-static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
+static int __init gs_bind(struct usb_composite_dev *cdev)
{
- int ret;
- struct gs_dev *dev = ep->driver_data;
-
- if (dev == NULL) {
- pr_err("gs_read_complete: NULL device pointer\n");
- return;
- }
+ int gcnum;
+ struct usb_gadget *gadget = cdev->gadget;
+ int status;
- switch(req->status) {
- case 0:
- /* normal completion */
- gs_recv_packet(dev, req->buf, req->actual);
-requeue:
- req->length = ep->maxpacket;
- if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) {
- pr_err(
- "gs_read_complete: cannot queue read request, ret=%d\n",
- ret);
- }
- break;
-
- case -ESHUTDOWN:
- /* disconnect */
- gs_debug("gs_read_complete: shutdown\n");
- gs_free_req(ep, req);
- break;
-
- default:
- /* unexpected */
- pr_err(
- "gs_read_complete: unexpected status error, status=%d\n",
- req->status);
- goto requeue;
- break;
- }
-}
-
-/*
-* gs_write_complete
-*/
-static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
-{
- struct gs_dev *dev = ep->driver_data;
-
- if (dev == NULL) {
- pr_err("gs_write_complete: NULL device pointer\n");
- return;
- }
+ status = gserial_setup(cdev->gadget, n_ports);
+ if (status < 0)
+ return status;
- switch(req->status) {
- case 0:
- /* normal completion */
-requeue:
- spin_lock(&dev->dev_lock);
- list_add(&req->list, &dev->dev_req_list);
- spin_unlock(&dev->dev_lock);
-
- gs_send(dev);
-
- break;
-
- case -ESHUTDOWN:
- /* disconnect */
- gs_debug("gs_write_complete: shutdown\n");
- gs_free_req(ep, req);
- break;
-
- default:
- pr_err(
- "gs_write_complete: unexpected status error, status=%d\n",
- req->status);
- goto requeue;
- break;
- }
-}
+ /* Allocate string descriptor numbers ... note that string
+ * contents can be overridden by the composite_dev glue.
+ */
-/*-------------------------------------------------------------------------*/
+ /* device description: manufacturer, product */
+ snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
+ init_utsname()->sysname, init_utsname()->release,
+ gadget->name);
+ status = usb_string_id(cdev);
+ if (status < 0)
+ goto fail;
+ strings_dev[STRING_MANUFACTURER_IDX].id = status;
-/* Gadget Driver */
+ device_desc.iManufacturer = status;
-/*
- * gs_unbind
- *
- * Called on module unload. Frees the control request and device
- * structure.
- */
-static void /* __init_or_exit */ gs_unbind(struct usb_gadget *gadget)
-{
- struct gs_dev *dev = get_gadget_data(gadget);
+ status = usb_string_id(cdev);
+ if (status < 0)
+ goto fail;
+ strings_dev[STRING_PRODUCT_IDX].id = status;
- gs_device = NULL;
+ device_desc.iProduct = status;
- /* read/write requests already freed, only control request remains */
- if (dev != NULL) {
- if (dev->dev_ctrl_req != NULL) {
- gs_free_req(gadget->ep0, dev->dev_ctrl_req);
- dev->dev_ctrl_req = NULL;
- }
- gs_reset_config(dev);
- gs_free_ports(dev);
- kfree(dev);
- set_gadget_data(gadget, NULL);
- }
+ /* config description */
+ status = usb_string_id(cdev);
+ if (status < 0)
+ goto fail;
+ strings_dev[STRING_DESCRIPTION_IDX].id = status;
- pr_info("gs_unbind: %s %s unbound\n", GS_LONG_NAME,
- GS_VERSION_STR);
-}
-
-/*
- * gs_bind
- *
- * Called on module load. Allocates and initializes the device
- * structure and a control request.
- */
-static int __init gs_bind(struct usb_gadget *gadget)
-{
- int ret;
- struct usb_ep *ep;
- struct gs_dev *dev;
- int gcnum;
-
- /* Some controllers can't support CDC ACM:
- * - sh doesn't support multiple interfaces or configs;
- * - sa1100 doesn't have a third interrupt endpoint
- */
- if (gadget_is_sh(gadget) || gadget_is_sa1100(gadget))
- use_acm = 0;
+ serial_config_driver.iConfiguration = status;
+ /* set up other descriptors */
gcnum = usb_gadget_controller_number(gadget);
if (gcnum >= 0)
- gs_device_desc.bcdDevice =
- cpu_to_le16(GS_VERSION_NUM | gcnum);
+ device_desc.bcdDevice = cpu_to_le16(GS_VERSION_NUM | gcnum);
else {
+ /* this is so simple (for now, no altsettings) that it
+ * SHOULD NOT have problems with bulk-capable hardware.
+ * so warn about unrcognized controllers -- don't panic.
+ *
+ * things like configuration and altsetting numbering
+ * can need hardware-specific attention though.
+ */
pr_warning("gs_bind: controller '%s' not recognized\n",
gadget->name);
- /* unrecognized, but safe unless bulk is REALLY quirky */
- gs_device_desc.bcdDevice =
- __constant_cpu_to_le16(GS_VERSION_NUM|0x0099);
- }
-
- dev = kzalloc(sizeof(struct gs_dev), GFP_KERNEL);
- if (dev == NULL)
- return -ENOMEM;
-
- usb_ep_autoconfig_reset(gadget);
-
- ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc);
- if (!ep)
- goto autoconf_fail;
- dev->dev_in_ep = ep;
- ep->driver_data = dev; /* claim the endpoint */
-
- ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc);
- if (!ep)
- goto autoconf_fail;
- dev->dev_out_ep = ep;
- ep->driver_data = dev; /* claim the endpoint */
-
- if (use_acm) {
- ep = usb_ep_autoconfig(gadget, &gs_fullspeed_notify_desc);
- if (!ep) {
- pr_err("gs_bind: cannot run ACM on %s\n", gadget->name);
- goto autoconf_fail;
- }
- gs_device_desc.idProduct = __constant_cpu_to_le16(
- GS_CDC_PRODUCT_ID),
- dev->dev_notify_ep = ep;
- ep->driver_data = dev; /* claim the endpoint */
- }
-
- gs_device_desc.bDeviceClass = use_acm
- ? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC;
- gs_device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
-
- if (gadget_is_dualspeed(gadget)) {
- gs_qualifier_desc.bDeviceClass = use_acm
- ? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC;
- /* assume ep0 uses the same packet size for both speeds */
- gs_qualifier_desc.bMaxPacketSize0 =
- gs_device_desc.bMaxPacketSize0;
- /* assume endpoints are dual-speed */
- gs_highspeed_notify_desc.bEndpointAddress =
- gs_fullspeed_notify_desc.bEndpointAddress;
- gs_highspeed_in_desc.bEndpointAddress =
- gs_fullspeed_in_desc.bEndpointAddress;
- gs_highspeed_out_desc.bEndpointAddress =
- gs_fullspeed_out_desc.bEndpointAddress;
- }
-
- usb_gadget_set_selfpowered(gadget);
-
- if (gadget_is_otg(gadget)) {
- gs_otg_descriptor.bmAttributes |= USB_OTG_HNP,
- gs_bulk_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
- gs_acm_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ device_desc.bcdDevice =
+ __constant_cpu_to_le16(GS_VERSION_NUM | 0x0099);
}
- gs_device = dev;
-
- snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- gadget->name);
-
- dev->dev_gadget = gadget;
- spin_lock_init(&dev->dev_lock);
- INIT_LIST_HEAD(&dev->dev_req_list);
- set_gadget_data(gadget, dev);
-
- if ((ret=gs_alloc_ports(dev, GFP_KERNEL)) != 0) {
- pr_err("gs_bind: cannot allocate ports\n");
- gs_unbind(gadget);
- return ret;
+ if (gadget_is_otg(cdev->gadget)) {
+ serial_config_driver.descriptors = otg_desc;
+ serial_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
- /* preallocate control response and buffer */
- dev->dev_ctrl_req = gs_alloc_req(gadget->ep0, GS_MAX_DESC_LEN,
- GFP_KERNEL);
- if (dev->dev_ctrl_req == NULL) {
- gs_unbind(gadget);
- return -ENOMEM;
- }
- gadget->ep0->driver_data = dev;
+ /* register our configuration */
+ status = usb_add_config(cdev, &serial_config_driver);
+ if (status < 0)
+ goto fail;
- pr_info("gs_bind: %s %s bound\n",
- GS_LONG_NAME, GS_VERSION_STR);
+ INFO(cdev, "%s\n", GS_VERSION_NAME);
return 0;
-autoconf_fail:
- kfree(dev);
- pr_err("gs_bind: cannot autoconfigure on %s\n", gadget->name);
- return -ENODEV;
-}
-
-static int gs_setup_standard(struct usb_gadget *gadget,
- const struct usb_ctrlrequest *ctrl)
-{
- int ret = -EOPNOTSUPP;
- struct gs_dev *dev = get_gadget_data(gadget);
- struct usb_request *req = dev->dev_ctrl_req;
- u16 wIndex = le16_to_cpu(ctrl->wIndex);
- u16 wValue = le16_to_cpu(ctrl->wValue);
- u16 wLength = le16_to_cpu(ctrl->wLength);
-
- switch (ctrl->bRequest) {
- case USB_REQ_GET_DESCRIPTOR:
- if (ctrl->bRequestType != USB_DIR_IN)
- break;
-
- switch (wValue >> 8) {
- case USB_DT_DEVICE:
- ret = min(wLength,
- (u16)sizeof(struct usb_device_descriptor));
- memcpy(req->buf, &gs_device_desc, ret);
- break;
-
- case USB_DT_DEVICE_QUALIFIER:
- if (!gadget_is_dualspeed(gadget))
- break;
- ret = min(wLength,
- (u16)sizeof(struct usb_qualifier_descriptor));
- memcpy(req->buf, &gs_qualifier_desc, ret);
- break;
-
- case USB_DT_OTHER_SPEED_CONFIG:
- if (!gadget_is_dualspeed(gadget))
- break;
- /* fall through */
- case USB_DT_CONFIG:
- ret = gs_build_config_buf(req->buf, gadget,
- wValue >> 8, wValue & 0xff,
- gadget_is_otg(gadget));
- if (ret >= 0)
- ret = min(wLength, (u16)ret);
- break;
-
- case USB_DT_STRING:
- /* wIndex == language code. */
- ret = usb_gadget_get_string(&gs_string_table,
- wValue & 0xff, req->buf);
- if (ret >= 0)
- ret = min(wLength, (u16)ret);
- break;
- }
- break;
-
- case USB_REQ_SET_CONFIGURATION:
- if (ctrl->bRequestType != 0)
- break;
- spin_lock(&dev->dev_lock);
- ret = gs_set_config(dev, wValue);
- spin_unlock(&dev->dev_lock);
- break;
-
- case USB_REQ_GET_CONFIGURATION:
- if (ctrl->bRequestType != USB_DIR_IN)
- break;
- *(u8 *)req->buf = dev->dev_config;
- ret = min(wLength, (u16)1);
- break;
-
- case USB_REQ_SET_INTERFACE:
- if (ctrl->bRequestType != USB_RECIP_INTERFACE
- || !dev->dev_config
- || wIndex >= GS_MAX_NUM_INTERFACES)
- break;
- if (dev->dev_config == GS_BULK_CONFIG_ID
- && wIndex != GS_BULK_INTERFACE_ID)
- break;
- /* no alternate interface settings */
- if (wValue != 0)
- break;
- spin_lock(&dev->dev_lock);
- /* PXA hardware partially handles SET_INTERFACE;
- * we need to kluge around that interference. */
- if (gadget_is_pxa(gadget)) {
- ret = gs_set_config(dev, use_acm ?
- GS_ACM_CONFIG_ID : GS_BULK_CONFIG_ID);
- goto set_interface_done;
- }
- if (dev->dev_config != GS_BULK_CONFIG_ID
- && wIndex == GS_CONTROL_INTERFACE_ID) {
- if (dev->dev_notify_ep) {
- usb_ep_disable(dev->dev_notify_ep);
- usb_ep_enable(dev->dev_notify_ep, dev->dev_notify_ep_desc);
- }
- } else {
- usb_ep_disable(dev->dev_in_ep);
- usb_ep_disable(dev->dev_out_ep);
- usb_ep_enable(dev->dev_in_ep, dev->dev_in_ep_desc);
- usb_ep_enable(dev->dev_out_ep, dev->dev_out_ep_desc);
- }
- ret = 0;
-set_interface_done:
- spin_unlock(&dev->dev_lock);
- break;
-
- case USB_REQ_GET_INTERFACE:
- if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)
- || dev->dev_config == GS_NO_CONFIG_ID)
- break;
- if (wIndex >= GS_MAX_NUM_INTERFACES
- || (dev->dev_config == GS_BULK_CONFIG_ID
- && wIndex != GS_BULK_INTERFACE_ID)) {
- ret = -EDOM;
- break;
- }
- /* no alternate interface settings */
- *(u8 *)req->buf = 0;
- ret = min(wLength, (u16)1);
- break;
-
- default:
- pr_err("gs_setup: unknown standard request, type=%02x, "
- "request=%02x, value=%04x, index=%04x, length=%d\n",
- ctrl->bRequestType, ctrl->bRequest,
- wValue, wIndex, wLength);
- break;
- }
-
- return ret;
+fail:
+ gserial_cleanup();
+ return status;
}
-static void gs_setup_complete_set_line_coding(struct usb_ep *ep,
- struct usb_request *req)
-{
- struct gs_dev *dev = ep->driver_data;
- struct gs_port *port = dev->dev_port[0]; /* ACM only has one port */
-
- switch (req->status) {
- case 0:
- /* normal completion */
- if (req->actual != sizeof(port->port_line_coding))
- usb_ep_set_halt(ep);
- else if (port) {
- struct usb_cdc_line_coding *value = req->buf;
-
- /* REVISIT: we currently just remember this data.
- * If we change that, (a) validate it first, then
- * (b) update whatever hardware needs updating.
- */
- spin_lock(&port->port_lock);
- port->port_line_coding = *value;
- spin_unlock(&port->port_lock);
- }
- break;
-
- case -ESHUTDOWN:
- /* disconnect */
- gs_free_req(ep, req);
- break;
-
- default:
- /* unexpected */
- break;
- }
- return;
-}
-
-static int gs_setup_class(struct usb_gadget *gadget,
- const struct usb_ctrlrequest *ctrl)
-{
- int ret = -EOPNOTSUPP;
- struct gs_dev *dev = get_gadget_data(gadget);
- struct gs_port *port = dev->dev_port[0]; /* ACM only has one port */
- struct usb_request *req = dev->dev_ctrl_req;
- u16 wIndex = le16_to_cpu(ctrl->wIndex);
- u16 wValue = le16_to_cpu(ctrl->wValue);
- u16 wLength = le16_to_cpu(ctrl->wLength);
-
- switch (ctrl->bRequest) {
- case USB_CDC_REQ_SET_LINE_CODING:
- if (wLength != sizeof(struct usb_cdc_line_coding))
- break;
- ret = wLength;
- req->complete = gs_setup_complete_set_line_coding;
- break;
-
- case USB_CDC_REQ_GET_LINE_CODING:
- ret = min_t(int, wLength, sizeof(struct usb_cdc_line_coding));
- if (port) {
- spin_lock(&port->port_lock);
- memcpy(req->buf, &port->port_line_coding, ret);
- spin_unlock(&port->port_lock);
- }
- break;
-
- case USB_CDC_REQ_SET_CONTROL_LINE_STATE:
- if (wLength != 0)
- break;
- ret = 0;
- if (port) {
- /* REVISIT: we currently just remember this data.
- * If we change that, update whatever hardware needs
- * updating.
- */
- spin_lock(&port->port_lock);
- port->port_handshake_bits = wValue;
- spin_unlock(&port->port_lock);
- }
- break;
-
- default:
- /* NOTE: strictly speaking, we should accept AT-commands
- * using SEND_ENCPSULATED_COMMAND/GET_ENCAPSULATED_RESPONSE.
- * But our call management descriptor says we don't handle
- * call management, so we should be able to get by without
- * handling those "required" commands (except by stalling).
- */
- pr_err("gs_setup: unknown class request, "
- "type=%02x, request=%02x, value=%04x, "
- "index=%04x, length=%d\n",
- ctrl->bRequestType, ctrl->bRequest,
- wValue, wIndex, wLength);
- break;
- }
-
- return ret;
-}
-
-/*
- * gs_setup_complete
- */
-static void gs_setup_complete(struct usb_ep *ep, struct usb_request *req)
-{
- if (req->status || req->actual != req->length) {
- pr_err("gs_setup_complete: status error, status=%d, "
- "actual=%d, length=%d\n",
- req->status, req->actual, req->length);
- }
-}
-
-/*
- * gs_setup
- *
- * Implements all the control endpoint functionality that's not
- * handled in hardware or the hardware driver.
- *
- * Returns the size of the data sent to the host, or a negative
- * error number.
- */
-static int gs_setup(struct usb_gadget *gadget,
- const struct usb_ctrlrequest *ctrl)
-{
- int ret = -EOPNOTSUPP;
- struct gs_dev *dev = get_gadget_data(gadget);
- struct usb_request *req = dev->dev_ctrl_req;
- u16 wIndex = le16_to_cpu(ctrl->wIndex);
- u16 wValue = le16_to_cpu(ctrl->wValue);
- u16 wLength = le16_to_cpu(ctrl->wLength);
-
- req->complete = gs_setup_complete;
-
- switch (ctrl->bRequestType & USB_TYPE_MASK) {
- case USB_TYPE_STANDARD:
- ret = gs_setup_standard(gadget, ctrl);
- break;
-
- case USB_TYPE_CLASS:
- ret = gs_setup_class(gadget, ctrl);
- break;
-
- default:
- pr_err("gs_setup: unknown request, type=%02x, request=%02x, "
- "value=%04x, index=%04x, length=%d\n",
- ctrl->bRequestType, ctrl->bRequest,
- wValue, wIndex, wLength);
- break;
- }
-
- /* respond with data transfer before status phase? */
- if (ret >= 0) {
- req->length = ret;
- req->zero = ret < wLength
- && (ret % gadget->ep0->maxpacket) == 0;
- ret = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
- if (ret < 0) {
- pr_err("gs_setup: cannot queue response, ret=%d\n",
- ret);
- req->status = 0;
- gs_setup_complete(gadget->ep0, req);
- }
- }
-
- /* device either stalls (ret < 0) or reports success */
- return ret;
-}
-
-/*
- * gs_disconnect
- *
- * Called when the device is disconnected. Frees the closed
- * ports and disconnects open ports. Open ports will be freed
- * on close. Then reallocates the ports for the next connection.
- */
-static void gs_disconnect(struct usb_gadget *gadget)
-{
- unsigned long flags;
- struct gs_dev *dev = get_gadget_data(gadget);
-
- spin_lock_irqsave(&dev->dev_lock, flags);
-
- gs_reset_config(dev);
-
- /* free closed ports and disconnect open ports */
- /* (open ports will be freed when closed) */
- gs_free_ports(dev);
-
- /* re-allocate ports for the next connection */
- if (gs_alloc_ports(dev, GFP_ATOMIC) != 0)
- pr_err("gs_disconnect: cannot re-allocate ports\n");
-
- spin_unlock_irqrestore(&dev->dev_lock, flags);
-
- pr_info("gs_disconnect: %s disconnected\n", GS_LONG_NAME);
-}
-
-static struct usb_gadget_driver gs_gadget_driver = {
-#ifdef CONFIG_USB_GADGET_DUALSPEED
- .speed = USB_SPEED_HIGH,
-#else
- .speed = USB_SPEED_FULL,
-#endif /* CONFIG_USB_GADGET_DUALSPEED */
- .function = GS_LONG_NAME,
- .bind = gs_bind,
- .unbind = gs_unbind,
- .setup = gs_setup,
- .disconnect = gs_disconnect,
- .driver = {
- .name = GS_SHORT_NAME,
- .owner = THIS_MODULE,
- },
+static struct usb_composite_driver gserial_driver = {
+ .name = "g_serial",
+ .dev = &device_desc,
+ .strings = dev_strings,
+ .bind = gs_bind,
};
-/*
- * gs_set_config
- *
- * Configures the device by enabling device specific
- * optimizations, setting up the endpoints, allocating
- * read and write requests and queuing read requests.
- *
- * The device lock must be held when calling this function.
- */
-static int gs_set_config(struct gs_dev *dev, unsigned config)
+static int __init init(void)
{
- int i;
- int ret = 0;
- struct usb_gadget *gadget = dev->dev_gadget;
- struct usb_ep *ep;
- struct usb_endpoint_descriptor *out, *in, *notify;
- struct usb_request *req;
-
- if (dev == NULL) {
- pr_err("gs_set_config: NULL device pointer\n");
- return 0;
- }
-
- if (config == dev->dev_config)
- return 0;
-
- gs_reset_config(dev);
-
- switch (config) {
- case GS_NO_CONFIG_ID:
- return 0;
- case GS_BULK_CONFIG_ID:
- if (use_acm)
- return -EINVAL;
- break;
- case GS_ACM_CONFIG_ID:
- if (!use_acm)
- return -EINVAL;
- break;
- default:
- return -EINVAL;
- }
-
- in = choose_ep_desc(gadget,
- &gs_highspeed_in_desc,
- &gs_fullspeed_in_desc);
- out = choose_ep_desc(gadget,
- &gs_highspeed_out_desc,
- &gs_fullspeed_out_desc);
- notify = dev->dev_notify_ep
- ? choose_ep_desc(gadget,
- &gs_highspeed_notify_desc,
- &gs_fullspeed_notify_desc)
- : NULL;
-
- ret = usb_ep_enable(dev->dev_in_ep, in);
- if (ret == 0) {
- dev->dev_in_ep_desc = in;
- } else {
- pr_debug("%s: cannot enable %s %s, ret=%d\n",
- __func__, "IN", dev->dev_in_ep->name, ret);
- return ret;
- }
-
- ret = usb_ep_enable(dev->dev_out_ep, out);
- if (ret == 0) {
- dev->dev_out_ep_desc = out;
- } else {
- pr_debug("%s: cannot enable %s %s, ret=%d\n",
- __func__, "OUT", dev->dev_out_ep->name, ret);
-fail0:
- usb_ep_disable(dev->dev_in_ep);
- return ret;
- }
-
- if (notify) {
- ret = usb_ep_enable(dev->dev_notify_ep, notify);
- if (ret == 0) {
- dev->dev_notify_ep_desc = notify;
- } else {
- pr_debug("%s: cannot enable %s %s, ret=%d\n",
- __func__, "NOTIFY",
- dev->dev_notify_ep->name, ret);
- usb_ep_disable(dev->dev_out_ep);
- goto fail0;
- }
- }
-
- dev->dev_config = config;
-
- /* allocate and queue read requests */
- ep = dev->dev_out_ep;
- for (i=0; i<read_q_size && ret == 0; i++) {
- if ((req=gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC))) {
- req->complete = gs_read_complete;
- if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) {
- pr_err("gs_set_config: cannot queue read "
- "request, ret=%d\n", ret);
- }
- } else {
- pr_err("gs_set_config: cannot allocate "
- "read requests\n");
- ret = -ENOMEM;
- goto exit_reset_config;
- }
- }
-
- /* allocate write requests, and put on free list */
- ep = dev->dev_in_ep;
- for (i=0; i<write_q_size; i++) {
- req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
- if (req) {
- req->complete = gs_write_complete;
- list_add(&req->list, &dev->dev_req_list);
- } else {
- pr_err("gs_set_config: cannot allocate "
- "write requests\n");
- ret = -ENOMEM;
- goto exit_reset_config;
- }
- }
-
- /* REVISIT the ACM mode should be able to actually *issue* some
- * notifications, for at least serial state change events if
- * not also for network connection; say so in bmCapabilities.
+ /* We *could* export two configs; that'd be much cleaner...
+ * but neither of these product IDs was defined that way.
*/
-
- pr_info("gs_set_config: %s configured, %s speed %s config\n",
- GS_LONG_NAME,
- gadget->speed == USB_SPEED_HIGH ? "high" : "full",
- config == GS_BULK_CONFIG_ID ? "BULK" : "CDC-ACM");
-
- return 0;
-
-exit_reset_config:
- gs_reset_config(dev);
- return ret;
-}
-
-/*
- * gs_reset_config
- *
- * Mark the device as not configured, disable all endpoints,
- * which forces completion of pending I/O and frees queued
- * requests, and free the remaining write requests on the
- * free list.
- *
- * The device lock must be held when calling this function.
- */
-static void gs_reset_config(struct gs_dev *dev)
-{
- struct usb_request *req;
-
- if (dev == NULL) {
- pr_err("gs_reset_config: NULL device pointer\n");
- return;
- }
-
- if (dev->dev_config == GS_NO_CONFIG_ID)
- return;
-
- dev->dev_config = GS_NO_CONFIG_ID;
-
- /* free write requests on the free list */
- while(!list_empty(&dev->dev_req_list)) {
- req = list_entry(dev->dev_req_list.next,
- struct usb_request, list);
- list_del(&req->list);
- gs_free_req(dev->dev_in_ep, req);
- }
-
- /* disable endpoints, forcing completion of pending i/o; */
- /* completion handlers free their requests in this case */
- if (dev->dev_notify_ep)
- usb_ep_disable(dev->dev_notify_ep);
- usb_ep_disable(dev->dev_in_ep);
- usb_ep_disable(dev->dev_out_ep);
-}
-
-/*
- * gs_build_config_buf
- *
- * Builds the config descriptors in the given buffer and returns the
- * length, or a negative error number.
- */
-static int gs_build_config_buf(u8 *buf, struct usb_gadget *g,
- u8 type, unsigned int index, int is_otg)
-{
- int len;
- int high_speed = 0;
- const struct usb_config_descriptor *config_desc;
- const struct usb_descriptor_header **function;
-
- if (index >= gs_device_desc.bNumConfigurations)
- return -EINVAL;
-
- /* other speed switches high and full speed */
- if (gadget_is_dualspeed(g)) {
- high_speed = (g->speed == USB_SPEED_HIGH);
- if (type == USB_DT_OTHER_SPEED_CONFIG)
- high_speed = !high_speed;
- }
-
if (use_acm) {
- config_desc = &gs_acm_config_desc;
- function = high_speed
- ? gs_acm_highspeed_function
- : gs_acm_fullspeed_function;
+ serial_config_driver.label = "CDC ACM config";
+ serial_config_driver.bConfigurationValue = 2;
+ device_desc.bDeviceClass = USB_CLASS_COMM;
+ device_desc.idProduct =
+ __constant_cpu_to_le16(GS_CDC_PRODUCT_ID);
} else {
- config_desc = &gs_bulk_config_desc;
- function = high_speed
- ? gs_bulk_highspeed_function
- : gs_bulk_fullspeed_function;
- }
-
- /* for now, don't advertise srp-only devices */
- if (!is_otg)
- function++;
-
- len = usb_gadget_config_buf(config_desc, buf, GS_MAX_DESC_LEN, function);
- if (len < 0)
- return len;
-
- ((struct usb_config_descriptor *)buf)->bDescriptorType = type;
-
- return len;
-}
-
-/*
- * gs_alloc_req
- *
- * Allocate a usb_request and its buffer. Returns a pointer to the
- * usb_request or NULL if there is an error.
- */
-static struct usb_request *
-gs_alloc_req(struct usb_ep *ep, unsigned int len, gfp_t kmalloc_flags)
-{
- struct usb_request *req;
-
- if (ep == NULL)
- return NULL;
-
- req = usb_ep_alloc_request(ep, kmalloc_flags);
-
- if (req != NULL) {
- req->length = len;
- req->buf = kmalloc(len, kmalloc_flags);
- if (req->buf == NULL) {
- usb_ep_free_request(ep, req);
- return NULL;
- }
+ serial_config_driver.label = "Generic Serial config";
+ serial_config_driver.bConfigurationValue = 1;
+ device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
+ device_desc.idProduct =
+ __constant_cpu_to_le16(GS_PRODUCT_ID);
}
+ strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label;
- return req;
+ return usb_composite_register(&gserial_driver);
}
+module_init(init);
-/*
- * gs_free_req
- *
- * Free a usb_request and its buffer.
- */
-static void gs_free_req(struct usb_ep *ep, struct usb_request *req)
+static void __exit cleanup(void)
{
- if (ep != NULL && req != NULL) {
- kfree(req->buf);
- usb_ep_free_request(ep, req);
- }
-}
-
-/*
- * gs_alloc_ports
- *
- * Allocate all ports and set the gs_dev struct to point to them.
- * Return 0 if successful, or a negative error number.
- *
- * The device lock is normally held when calling this function.
- */
-static int gs_alloc_ports(struct gs_dev *dev, gfp_t kmalloc_flags)
-{
- int i;
- struct gs_port *port;
-
- if (dev == NULL)
- return -EIO;
-
- for (i=0; i<GS_NUM_PORTS; i++) {
- if ((port=kzalloc(sizeof(struct gs_port), kmalloc_flags)) == NULL)
- return -ENOMEM;
-
- port->port_dev = dev;
- port->port_num = i;
- port->port_line_coding.dwDTERate = cpu_to_le32(GS_DEFAULT_DTE_RATE);
- port->port_line_coding.bCharFormat = GS_DEFAULT_CHAR_FORMAT;
- port->port_line_coding.bParityType = GS_DEFAULT_PARITY;
- port->port_line_coding.bDataBits = GS_DEFAULT_DATA_BITS;
- spin_lock_init(&port->port_lock);
- init_waitqueue_head(&port->port_write_wait);
-
- dev->dev_port[i] = port;
- }
-
- return 0;
-}
-
-/*
- * gs_free_ports
- *
- * Free all closed ports. Open ports are disconnected by
- * freeing their write buffers, setting their device pointers
- * and the pointers to them in the device to NULL. These
- * ports will be freed when closed.
- *
- * The device lock is normally held when calling this function.
- */
-static void gs_free_ports(struct gs_dev *dev)
-{
- int i;
- unsigned long flags;
- struct gs_port *port;
-
- if (dev == NULL)
- return;
-
- for (i=0; i<GS_NUM_PORTS; i++) {
- if ((port=dev->dev_port[i]) != NULL) {
- dev->dev_port[i] = NULL;
-
- spin_lock_irqsave(&port->port_lock, flags);
-
- if (port->port_write_buf != NULL) {
- gs_buf_free(port->port_write_buf);
- port->port_write_buf = NULL;
- }
-
- if (port->port_open_count > 0 || port->port_in_use) {
- port->port_dev = NULL;
- wake_up_interruptible(&port->port_write_wait);
- if (port->port_tty) {
- tty_hangup(port->port_tty);
- }
- spin_unlock_irqrestore(&port->port_lock, flags);
- } else {
- spin_unlock_irqrestore(&port->port_lock, flags);
- kfree(port);
- }
-
- }
- }
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* Circular Buffer */
-
-/*
- * gs_buf_alloc
- *
- * Allocate a circular buffer and all associated memory.
- */
-static struct gs_buf *gs_buf_alloc(unsigned int size, gfp_t kmalloc_flags)
-{
- struct gs_buf *gb;
-
- if (size == 0)
- return NULL;
-
- gb = kmalloc(sizeof(struct gs_buf), kmalloc_flags);
- if (gb == NULL)
- return NULL;
-
- gb->buf_buf = kmalloc(size, kmalloc_flags);
- if (gb->buf_buf == NULL) {
- kfree(gb);
- return NULL;
- }
-
- gb->buf_size = size;
- gb->buf_get = gb->buf_put = gb->buf_buf;
-
- return gb;
-}
-
-/*
- * gs_buf_free
- *
- * Free the buffer and all associated memory.
- */
-static void gs_buf_free(struct gs_buf *gb)
-{
- if (gb) {
- kfree(gb->buf_buf);
- kfree(gb);
- }
-}
-
-/*
- * gs_buf_clear
- *
- * Clear out all data in the circular buffer.
- */
-static void gs_buf_clear(struct gs_buf *gb)
-{
- if (gb != NULL)
- gb->buf_get = gb->buf_put;
- /* equivalent to a get of all data available */
-}
-
-/*
- * gs_buf_data_avail
- *
- * Return the number of bytes of data available in the circular
- * buffer.
- */
-static unsigned int gs_buf_data_avail(struct gs_buf *gb)
-{
- if (gb != NULL)
- return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size;
- else
- return 0;
-}
-
-/*
- * gs_buf_space_avail
- *
- * Return the number of bytes of space available in the circular
- * buffer.
- */
-static unsigned int gs_buf_space_avail(struct gs_buf *gb)
-{
- if (gb != NULL)
- return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size;
- else
- return 0;
-}
-
-/*
- * gs_buf_put
- *
- * Copy data data from a user buffer and put it into the circular buffer.
- * Restrict to the amount of space available.
- *
- * Return the number of bytes copied.
- */
-static unsigned int
-gs_buf_put(struct gs_buf *gb, const char *buf, unsigned int count)
-{
- unsigned int len;
-
- if (gb == NULL)
- return 0;
-
- len = gs_buf_space_avail(gb);
- if (count > len)
- count = len;
-
- if (count == 0)
- return 0;
-
- len = gb->buf_buf + gb->buf_size - gb->buf_put;
- if (count > len) {
- memcpy(gb->buf_put, buf, len);
- memcpy(gb->buf_buf, buf+len, count - len);
- gb->buf_put = gb->buf_buf + count - len;
- } else {
- memcpy(gb->buf_put, buf, count);
- if (count < len)
- gb->buf_put += count;
- else /* count == len */
- gb->buf_put = gb->buf_buf;
- }
-
- return count;
-}
-
-/*
- * gs_buf_get
- *
- * Get data from the circular buffer and copy to the given buffer.
- * Restrict to the amount of data available.
- *
- * Return the number of bytes copied.
- */
-static unsigned int
-gs_buf_get(struct gs_buf *gb, char *buf, unsigned int count)
-{
- unsigned int len;
-
- if (gb == NULL)
- return 0;
-
- len = gs_buf_data_avail(gb);
- if (count > len)
- count = len;
-
- if (count == 0)
- return 0;
-
- len = gb->buf_buf + gb->buf_size - gb->buf_get;
- if (count > len) {
- memcpy(buf, gb->buf_get, len);
- memcpy(buf+len, gb->buf_buf, count - len);
- gb->buf_get = gb->buf_buf + count - len;
- } else {
- memcpy(buf, gb->buf_get, count);
- if (count < len)
- gb->buf_get += count;
- else /* count == len */
- gb->buf_get = gb->buf_buf;
- }
-
- return count;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static struct tty_driver *gs_tty_driver;
-
-/*
- * gs_module_init
- *
- * Register as a USB gadget driver and a tty driver.
- */
-static int __init gs_module_init(void)
-{
- int i;
- int retval;
-
- retval = usb_gadget_register_driver(&gs_gadget_driver);
- if (retval) {
- pr_err("gs_module_init: cannot register gadget driver, "
- "ret=%d\n", retval);
- return retval;
- }
-
- gs_tty_driver = alloc_tty_driver(GS_NUM_PORTS);
- if (!gs_tty_driver)
- return -ENOMEM;
- gs_tty_driver->owner = THIS_MODULE;
- gs_tty_driver->driver_name = GS_SHORT_NAME;
- gs_tty_driver->name = "ttygs";
- gs_tty_driver->major = GS_MAJOR;
- gs_tty_driver->minor_start = GS_MINOR_START;
- gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
- gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
- gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
- gs_tty_driver->init_termios = tty_std_termios;
- /* must match GS_DEFAULT_DTE_RATE and friends */
- gs_tty_driver->init_termios.c_cflag =
- B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- gs_tty_driver->init_termios.c_ispeed = GS_DEFAULT_DTE_RATE;
- gs_tty_driver->init_termios.c_ospeed = GS_DEFAULT_DTE_RATE;
- tty_set_operations(gs_tty_driver, &gs_tty_ops);
-
- for (i = 0; i < GS_NUM_PORTS; i++)
- mutex_init(&gs_open_close_lock[i]);
-
- retval = tty_register_driver(gs_tty_driver);
- if (retval) {
- usb_gadget_unregister_driver(&gs_gadget_driver);
- put_tty_driver(gs_tty_driver);
- pr_err("gs_module_init: cannot register tty driver, "
- "ret=%d\n", retval);
- return retval;
- }
-
- pr_info("gs_module_init: %s %s loaded\n",
- GS_LONG_NAME, GS_VERSION_STR);
- return 0;
-}
-module_init(gs_module_init);
-
-/*
- * gs_module_exit
- *
- * Unregister as a tty driver and a USB gadget driver.
- */
-static void __exit gs_module_exit(void)
-{
- tty_unregister_driver(gs_tty_driver);
- put_tty_driver(gs_tty_driver);
- usb_gadget_unregister_driver(&gs_gadget_driver);
-
- pr_info("gs_module_exit: %s %s unloaded\n",
- GS_LONG_NAME, GS_VERSION_STR);
+ usb_composite_unregister(&gserial_driver);
+ gserial_cleanup();
}
-module_exit(gs_module_exit);
+module_exit(cleanup);
diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c
new file mode 100644
index 000000000000..72d548de3807
--- /dev/null
+++ b/drivers/usb/gadget/u_serial.c
@@ -0,0 +1,1270 @@
+/*
+ * u_serial.c - utilities for USB gadget "serial port"/TTY support
+ *
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 David Brownell
+ *
+ * This code also borrows from usbserial.c, which is
+ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2000 Peter Berger (pberger@brimson.com)
+ * Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com)
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * either version 2 of that License or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+#include "u_serial.h"
+
+
+/*
+ * This module encapsulates the TTY layer glue needed to provide basic
+ * "serial port" functionality through the USB gadget stack. Each such
+ * port is exposed through a /dev/ttyGS* node.
+ *
+ * After initialization (gserial_setup), these TTY port devices stay
+ * available until they are removed (gserial_cleanup). Each one may be
+ * connected to a USB function (gserial_connect), or disconnected (with
+ * gserial_disconnect) when the USB host issues a config change event.
+ * Data can only flow when the port is connected to the host.
+ *
+ * A given TTY port can be made available in multiple configurations.
+ * For example, each one might expose a ttyGS0 node which provides a
+ * login application. In one case that might use CDC ACM interface 0,
+ * while another configuration might use interface 3 for that. The
+ * work to handle that (including descriptor management) is not part
+ * of this module.
+ *
+ * Configurations may expose more than one TTY port. For example, if
+ * ttyGS0 provides login service, then ttyGS1 might provide dialer access
+ * for a telephone or fax link. And ttyGS2 might be something that just
+ * needs a simple byte stream interface for some messaging protocol that
+ * is managed in userspace ... OBEX, PTP, and MTP have been mentioned.
+ */
+
+/*
+ * gserial is the lifecycle interface, used by USB functions
+ * gs_port is the I/O nexus, used by the tty driver
+ * tty_struct links to the tty/filesystem framework
+ *
+ * gserial <---> gs_port ... links will be null when the USB link is
+ * inactive; managed by gserial_{connect,disconnect}().
+ * gserial->ioport == usb_ep->driver_data ... gs_port
+ * gs_port->port_usb ... gserial
+ *
+ * gs_port <---> tty_struct ... links will be null when the TTY file
+ * isn't opened; managed by gs_open()/gs_close()
+ * gserial->port_tty ... tty_struct
+ * tty_struct->driver_data ... gserial
+ */
+
+/* RX and TX queues can buffer QUEUE_SIZE packets before they hit the
+ * next layer of buffering. For TX that's a circular buffer; for RX
+ * consider it a NOP. A third layer is provided by the TTY code.
+ */
+#define QUEUE_SIZE 16
+#define WRITE_BUF_SIZE 8192 /* TX only */
+
+/* circular buffer */
+struct gs_buf {
+ unsigned buf_size;
+ char *buf_buf;
+ char *buf_get;
+ char *buf_put;
+};
+
+/*
+ * The port structure holds info for each port, one for each minor number
+ * (and thus for each /dev/ node).
+ */
+struct gs_port {
+ spinlock_t port_lock;
+
+ struct gserial *port_usb;
+ struct tty_struct *port_tty;
+
+ unsigned open_count;
+ bool openclose; /* open/close in progress */
+ u8 port_num;
+
+ wait_queue_head_t close_wait; /* wait for last close */
+
+ struct list_head read_pool;
+ struct tasklet_struct push;
+
+ struct list_head write_pool;
+ struct gs_buf port_write_buf;
+ wait_queue_head_t drain_wait; /* wait while writes drain */
+
+ /* REVISIT this state ... */
+ struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
+};
+
+/* increase N_PORTS if you need more */
+#define N_PORTS 4
+static struct portmaster {
+ struct mutex lock; /* protect open/close */
+ struct gs_port *port;
+} ports[N_PORTS];
+static unsigned n_ports;
+
+#define GS_CLOSE_TIMEOUT 15 /* seconds */
+
+
+
+#ifdef VERBOSE_DEBUG
+#define pr_vdebug(fmt, arg...) \
+ pr_debug(fmt, ##arg)
+#else
+#define pr_vdebug(fmt, arg...) \
+ ({ if (0) pr_debug(fmt, ##arg); })
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* Circular Buffer */
+
+/*
+ * gs_buf_alloc
+ *
+ * Allocate a circular buffer and all associated memory.
+ */
+static int gs_buf_alloc(struct gs_buf *gb, unsigned size)
+{
+ gb->buf_buf = kmalloc(size, GFP_KERNEL);
+ if (gb->buf_buf == NULL)
+ return -ENOMEM;
+
+ gb->buf_size = size;
+ gb->buf_get = gb->buf_put = gb->buf_buf;
+
+ return 0;
+}
+
+/*
+ * gs_buf_free
+ *
+ * Free the buffer and all associated memory.
+ */
+static void gs_buf_free(struct gs_buf *gb)
+{
+ kfree(gb->buf_buf);
+ gb->buf_buf = NULL;
+}
+
+/*
+ * gs_buf_clear
+ *
+ * Clear out all data in the circular buffer.
+ */
+static void gs_buf_clear(struct gs_buf *gb)
+{
+ gb->buf_get = gb->buf_put;
+ /* equivalent to a get of all data available */
+}
+
+/*
+ * gs_buf_data_avail
+ *
+ * Return the number of bytes of data available in the circular
+ * buffer.
+ */
+static unsigned gs_buf_data_avail(struct gs_buf *gb)
+{
+ return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size;
+}
+
+/*
+ * gs_buf_space_avail
+ *
+ * Return the number of bytes of space available in the circular
+ * buffer.
+ */
+static unsigned gs_buf_space_avail(struct gs_buf *gb)
+{
+ return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size;
+}
+
+/*
+ * gs_buf_put
+ *
+ * Copy data data from a user buffer and put it into the circular buffer.
+ * Restrict to the amount of space available.
+ *
+ * Return the number of bytes copied.
+ */
+static unsigned
+gs_buf_put(struct gs_buf *gb, const char *buf, unsigned count)
+{
+ unsigned len;
+
+ len = gs_buf_space_avail(gb);
+ if (count > len)
+ count = len;
+
+ if (count == 0)
+ return 0;
+
+ len = gb->buf_buf + gb->buf_size - gb->buf_put;
+ if (count > len) {
+ memcpy(gb->buf_put, buf, len);
+ memcpy(gb->buf_buf, buf+len, count - len);
+ gb->buf_put = gb->buf_buf + count - len;
+ } else {
+ memcpy(gb->buf_put, buf, count);
+ if (count < len)
+ gb->buf_put += count;
+ else /* count == len */
+ gb->buf_put = gb->buf_buf;
+ }
+
+ return count;
+}
+
+/*
+ * gs_buf_get
+ *
+ * Get data from the circular buffer and copy to the given buffer.
+ * Restrict to the amount of data available.
+ *
+ * Return the number of bytes copied.
+ */
+static unsigned
+gs_buf_get(struct gs_buf *gb, char *buf, unsigned count)
+{
+ unsigned len;
+
+ len = gs_buf_data_avail(gb);
+ if (count > len)
+ count = len;
+
+ if (count == 0)
+ return 0;
+
+ len = gb->buf_buf + gb->buf_size - gb->buf_get;
+ if (count > len) {
+ memcpy(buf, gb->buf_get, len);
+ memcpy(buf+len, gb->buf_buf, count - len);
+ gb->buf_get = gb->buf_buf + count - len;
+ } else {
+ memcpy(buf, gb->buf_get, count);
+ if (count < len)
+ gb->buf_get += count;
+ else /* count == len */
+ gb->buf_get = gb->buf_buf;
+ }
+
+ return count;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* I/O glue between TTY (upper) and USB function (lower) driver layers */
+
+/*
+ * gs_alloc_req
+ *
+ * Allocate a usb_request and its buffer. Returns a pointer to the
+ * usb_request or NULL if there is an error.
+ */
+static struct usb_request *
+gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
+{
+ struct usb_request *req;
+
+ req = usb_ep_alloc_request(ep, kmalloc_flags);
+
+ if (req != NULL) {
+ req->length = len;
+ req->buf = kmalloc(len, kmalloc_flags);
+ if (req->buf == NULL) {
+ usb_ep_free_request(ep, req);
+ return NULL;
+ }
+ }
+
+ return req;
+}
+
+/*
+ * gs_free_req
+ *
+ * Free a usb_request and its buffer.
+ */
+static void gs_free_req(struct usb_ep *ep, struct usb_request *req)
+{
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+}
+
+/*
+ * gs_send_packet
+ *
+ * If there is data to send, a packet is built in the given
+ * buffer and the size is returned. If there is no data to
+ * send, 0 is returned.
+ *
+ * Called with port_lock held.
+ */
+static unsigned
+gs_send_packet(struct gs_port *port, char *packet, unsigned size)
+{
+ unsigned len;
+
+ len = gs_buf_data_avail(&port->port_write_buf);
+ if (len < size)
+ size = len;
+ if (size != 0)
+ size = gs_buf_get(&port->port_write_buf, packet, size);
+ return size;
+}
+
+/*
+ * gs_start_tx
+ *
+ * This function finds available write requests, calls
+ * gs_send_packet to fill these packets with data, and
+ * continues until either there are no more write requests
+ * available or no more data to send. This function is
+ * run whenever data arrives or write requests are available.
+ *
+ * Context: caller owns port_lock; port_usb is non-null.
+ */
+static int gs_start_tx(struct gs_port *port)
+/*
+__releases(&port->port_lock)
+__acquires(&port->port_lock)
+*/
+{
+ struct list_head *pool = &port->write_pool;
+ struct usb_ep *in = port->port_usb->in;
+ int status = 0;
+ bool do_tty_wake = false;
+
+ while (!list_empty(pool)) {
+ struct usb_request *req;
+ int len;
+
+ req = list_entry(pool->next, struct usb_request, list);
+ len = gs_send_packet(port, req->buf, in->maxpacket);
+ if (len == 0) {
+ wake_up_interruptible(&port->drain_wait);
+ break;
+ }
+ do_tty_wake = true;
+
+ req->length = len;
+ list_del(&req->list);
+
+#ifdef VERBOSE_DEBUG
+ pr_debug("%s: %s, len=%d, 0x%02x 0x%02x 0x%02x ...\n",
+ __func__, in->name, len, *((u8 *)req->buf),
+ *((u8 *)req->buf+1), *((u8 *)req->buf+2));
+#endif
+
+ /* Drop lock while we call out of driver; completions
+ * could be issued while we do so. Disconnection may
+ * happen too; maybe immediately before we queue this!
+ *
+ * NOTE that we may keep sending data for a while after
+ * the TTY closed (dev->ioport->port_tty is NULL).
+ */
+ spin_unlock(&port->port_lock);
+ status = usb_ep_queue(in, req, GFP_ATOMIC);
+ spin_lock(&port->port_lock);
+
+ if (status) {
+ pr_debug("%s: %s %s err %d\n",
+ __func__, "queue", in->name, status);
+ list_add(&req->list, pool);
+ break;
+ }
+
+ /* abort immediately after disconnect */
+ if (!port->port_usb)
+ break;
+ }
+
+ if (do_tty_wake && port->port_tty)
+ tty_wakeup(port->port_tty);
+ return status;
+}
+
+static void gs_rx_push(unsigned long _port)
+{
+ struct gs_port *port = (void *)_port;
+ struct tty_struct *tty = port->port_tty;
+
+ /* With low_latency, tty_flip_buffer_push() doesn't put its
+ * real work through a workqueue, so the ldisc has a better
+ * chance to keep up with peak USB data rates.
+ */
+ if (tty) {
+ tty_flip_buffer_push(tty);
+ wake_up_interruptible(&tty->read_wait);
+ }
+}
+
+/*
+ * gs_recv_packet
+ *
+ * Called for each USB packet received. Reads the packet
+ * header and stuffs the data in the appropriate tty buffer.
+ * Returns 0 if successful, or a negative error number.
+ *
+ * Called during USB completion routine, on interrupt time.
+ * With port_lock.
+ */
+static int gs_recv_packet(struct gs_port *port, char *packet, unsigned size)
+{
+ unsigned len;
+ struct tty_struct *tty;
+
+ /* I/O completions can continue for a while after close(), until the
+ * request queue empties. Just discard any data we receive, until
+ * something reopens this TTY ... as if there were no HW flow control.
+ */
+ tty = port->port_tty;
+ if (tty == NULL) {
+ pr_vdebug("%s: ttyGS%d, after close\n",
+ __func__, port->port_num);
+ return -EIO;
+ }
+
+ len = tty_insert_flip_string(tty, packet, size);
+ if (len > 0)
+ tasklet_schedule(&port->push);
+ if (len < size)
+ pr_debug("%s: ttyGS%d, drop %d bytes\n",
+ __func__, port->port_num, size - len);
+ return 0;
+}
+
+/*
+ * Context: caller owns port_lock, and port_usb is set
+ */
+static unsigned gs_start_rx(struct gs_port *port)
+/*
+__releases(&port->port_lock)
+__acquires(&port->port_lock)
+*/
+{
+ struct list_head *pool = &port->read_pool;
+ struct usb_ep *out = port->port_usb->out;
+ unsigned started = 0;
+
+ while (!list_empty(pool)) {
+ struct usb_request *req;
+ int status;
+ struct tty_struct *tty;
+
+ /* no more rx if closed or throttled */
+ tty = port->port_tty;
+ if (!tty || test_bit(TTY_THROTTLED, &tty->flags))
+ break;
+
+ req = list_entry(pool->next, struct usb_request, list);
+ list_del(&req->list);
+ req->length = out->maxpacket;
+
+ /* drop lock while we call out; the controller driver
+ * may need to call us back (e.g. for disconnect)
+ */
+ spin_unlock(&port->port_lock);
+ status = usb_ep_queue(out, req, GFP_ATOMIC);
+ spin_lock(&port->port_lock);
+
+ if (status) {
+ pr_debug("%s: %s %s err %d\n",
+ __func__, "queue", out->name, status);
+ list_add(&req->list, pool);
+ break;
+ }
+ started++;
+
+ /* abort immediately after disconnect */
+ if (!port->port_usb)
+ break;
+ }
+ return started;
+}
+
+static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ int status;
+ struct gs_port *port = ep->driver_data;
+
+ spin_lock(&port->port_lock);
+ list_add(&req->list, &port->read_pool);
+
+ switch (req->status) {
+ case 0:
+ /* normal completion */
+ status = gs_recv_packet(port, req->buf, req->actual);
+ if (status && status != -EIO)
+ pr_debug("%s: %s %s err %d\n",
+ __func__, "recv", ep->name, status);
+ gs_start_rx(port);
+ break;
+
+ case -ESHUTDOWN:
+ /* disconnect */
+ pr_vdebug("%s: %s shutdown\n", __func__, ep->name);
+ break;
+
+ default:
+ /* presumably a transient fault */
+ pr_warning("%s: unexpected %s status %d\n",
+ __func__, ep->name, req->status);
+ gs_start_rx(port);
+ break;
+ }
+ spin_unlock(&port->port_lock);
+}
+
+static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct gs_port *port = ep->driver_data;
+
+ spin_lock(&port->port_lock);
+ list_add(&req->list, &port->write_pool);
+
+ switch (req->status) {
+ default:
+ /* presumably a transient fault */
+ pr_warning("%s: unexpected %s status %d\n",
+ __func__, ep->name, req->status);
+ /* FALL THROUGH */
+ case 0:
+ /* normal completion */
+ gs_start_tx(port);
+ break;
+
+ case -ESHUTDOWN:
+ /* disconnect */
+ pr_vdebug("%s: %s shutdown\n", __func__, ep->name);
+ break;
+ }
+
+ spin_unlock(&port->port_lock);
+}
+
+static void gs_free_requests(struct usb_ep *ep, struct list_head *head)
+{
+ struct usb_request *req;
+
+ while (!list_empty(head)) {
+ req = list_entry(head->next, struct usb_request, list);
+ list_del(&req->list);
+ gs_free_req(ep, req);
+ }
+}
+
+static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head,
+ void (*fn)(struct usb_ep *, struct usb_request *))
+{
+ int i;
+ struct usb_request *req;
+
+ /* Pre-allocate up to QUEUE_SIZE transfers, but if we can't
+ * do quite that many this time, don't fail ... we just won't
+ * be as speedy as we might otherwise be.
+ */
+ for (i = 0; i < QUEUE_SIZE; i++) {
+ req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
+ if (!req)
+ return list_empty(head) ? -ENOMEM : 0;
+ req->complete = fn;
+ list_add_tail(&req->list, head);
+ }
+ return 0;
+}
+
+/**
+ * gs_start_io - start USB I/O streams
+ * @dev: encapsulates endpoints to use
+ * Context: holding port_lock; port_tty and port_usb are non-null
+ *
+ * We only start I/O when something is connected to both sides of
+ * this port. If nothing is listening on the host side, we may
+ * be pointlessly filling up our TX buffers and FIFO.
+ */
+static int gs_start_io(struct gs_port *port)
+{
+ struct list_head *head = &port->read_pool;
+ struct usb_ep *ep = port->port_usb->out;
+ int status;
+ unsigned started;
+
+ /* Allocate RX and TX I/O buffers. We can't easily do this much
+ * earlier (with GFP_KERNEL) because the requests are coupled to
+ * endpoints, as are the packet sizes we'll be using. Different
+ * configurations may use different endpoints with a given port;
+ * and high speed vs full speed changes packet sizes too.
+ */
+ status = gs_alloc_requests(ep, head, gs_read_complete);
+ if (status)
+ return status;
+
+ status = gs_alloc_requests(port->port_usb->in, &port->write_pool,
+ gs_write_complete);
+ if (status) {
+ gs_free_requests(ep, head);
+ return status;
+ }
+
+ /* queue read requests */
+ started = gs_start_rx(port);
+
+ /* unblock any pending writes into our circular buffer */
+ if (started) {
+ tty_wakeup(port->port_tty);
+ } else {
+ gs_free_requests(ep, head);
+ gs_free_requests(port->port_usb->in, &port->write_pool);
+ }
+
+ return started ? 0 : status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* TTY Driver */
+
+/*
+ * gs_open sets up the link between a gs_port and its associated TTY.
+ * That link is broken *only* by TTY close(), and all driver methods
+ * know that.
+ */
+static int gs_open(struct tty_struct *tty, struct file *file)
+{
+ int port_num = tty->index;
+ struct gs_port *port;
+ int status;
+
+ if (port_num < 0 || port_num >= n_ports)
+ return -ENXIO;
+
+ do {
+ mutex_lock(&ports[port_num].lock);
+ port = ports[port_num].port;
+ if (!port)
+ status = -ENODEV;
+ else {
+ spin_lock_irq(&port->port_lock);
+
+ /* already open? Great. */
+ if (port->open_count) {
+ status = 0;
+ port->open_count++;
+
+ /* currently opening/closing? wait ... */
+ } else if (port->openclose) {
+ status = -EBUSY;
+
+ /* ... else we do the work */
+ } else {
+ status = -EAGAIN;
+ port->openclose = true;
+ }
+ spin_unlock_irq(&port->port_lock);
+ }
+ mutex_unlock(&ports[port_num].lock);
+
+ switch (status) {
+ default:
+ /* fully handled */
+ return status;
+ case -EAGAIN:
+ /* must do the work */
+ break;
+ case -EBUSY:
+ /* wait for EAGAIN task to finish */
+ msleep(1);
+ /* REVISIT could have a waitchannel here, if
+ * concurrent open performance is important
+ */
+ break;
+ }
+ } while (status != -EAGAIN);
+
+ /* Do the "real open" */
+ spin_lock_irq(&port->port_lock);
+
+ /* allocate circular buffer on first open */
+ if (port->port_write_buf.buf_buf == NULL) {
+
+ spin_unlock_irq(&port->port_lock);
+ status = gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE);
+ spin_lock_irq(&port->port_lock);
+
+ if (status) {
+ pr_debug("gs_open: ttyGS%d (%p,%p) no buffer\n",
+ port->port_num, tty, file);
+ port->openclose = false;
+ goto exit_unlock_port;
+ }
+ }
+
+ /* REVISIT if REMOVED (ports[].port NULL), abort the open
+ * to let rmmod work faster (but this way isn't wrong).
+ */
+
+ /* REVISIT maybe wait for "carrier detect" */
+
+ tty->driver_data = port;
+ port->port_tty = tty;
+
+ port->open_count = 1;
+ port->openclose = false;
+
+ /* low_latency means ldiscs work in tasklet context, without
+ * needing a workqueue schedule ... easier to keep up.
+ */
+ tty->low_latency = 1;
+
+ /* if connected, start the I/O stream */
+ if (port->port_usb) {
+ pr_debug("gs_open: start ttyGS%d\n", port->port_num);
+ gs_start_io(port);
+
+ /* REVISIT for ACM, issue "network connected" event */
+ }
+
+ pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file);
+
+ status = 0;
+
+exit_unlock_port:
+ spin_unlock_irq(&port->port_lock);
+ return status;
+}
+
+static int gs_writes_finished(struct gs_port *p)
+{
+ int cond;
+
+ /* return true on disconnect or empty buffer */
+ spin_lock_irq(&p->port_lock);
+ cond = (p->port_usb == NULL) || !gs_buf_data_avail(&p->port_write_buf);
+ spin_unlock_irq(&p->port_lock);
+
+ return cond;
+}
+
+static void gs_close(struct tty_struct *tty, struct file *file)
+{
+ struct gs_port *port = tty->driver_data;
+
+ spin_lock_irq(&port->port_lock);
+
+ if (port->open_count != 1) {
+ if (port->open_count == 0)
+ WARN_ON(1);
+ else
+ --port->open_count;
+ goto exit;
+ }
+
+ pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file);
+
+ /* mark port as closing but in use; we can drop port lock
+ * and sleep if necessary
+ */
+ port->openclose = true;
+ port->open_count = 0;
+
+ if (port->port_usb)
+ /* REVISIT for ACM, issue "network disconnected" event */;
+
+ /* wait for circular write buffer to drain, disconnect, or at
+ * most GS_CLOSE_TIMEOUT seconds; then discard the rest
+ */
+ if (gs_buf_data_avail(&port->port_write_buf) > 0
+ && port->port_usb) {
+ spin_unlock_irq(&port->port_lock);
+ wait_event_interruptible_timeout(port->drain_wait,
+ gs_writes_finished(port),
+ GS_CLOSE_TIMEOUT * HZ);
+ spin_lock_irq(&port->port_lock);
+ }
+
+ /* Iff we're disconnected, there can be no I/O in flight so it's
+ * ok to free the circular buffer; else just scrub it. And don't
+ * let the push tasklet fire again until we're re-opened.
+ */
+ if (port->port_usb == NULL)
+ gs_buf_free(&port->port_write_buf);
+ else
+ gs_buf_clear(&port->port_write_buf);
+
+ tasklet_kill(&port->push);
+
+ tty->driver_data = NULL;
+ port->port_tty = NULL;
+
+ port->openclose = false;
+
+ pr_debug("gs_close: ttyGS%d (%p,%p) done!\n",
+ port->port_num, tty, file);
+
+ wake_up_interruptible(&port->close_wait);
+exit:
+ spin_unlock_irq(&port->port_lock);
+}
+
+static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+ struct gs_port *port = tty->driver_data;
+ unsigned long flags;
+ int status;
+
+ pr_vdebug("gs_write: ttyGS%d (%p) writing %d bytes\n",
+ port->port_num, tty, count);
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (count)
+ count = gs_buf_put(&port->port_write_buf, buf, count);
+ /* treat count == 0 as flush_chars() */
+ if (port->port_usb)
+ status = gs_start_tx(port);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ return count;
+}
+
+static int gs_put_char(struct tty_struct *tty, unsigned char ch)
+{
+ struct gs_port *port = tty->driver_data;
+ unsigned long flags;
+ int status;
+
+ pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %p\n",
+ port->port_num, tty, ch, __builtin_return_address(0));
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ status = gs_buf_put(&port->port_write_buf, &ch, 1);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ return status;
+}
+
+static void gs_flush_chars(struct tty_struct *tty)
+{
+ struct gs_port *port = tty->driver_data;
+ unsigned long flags;
+
+ pr_vdebug("gs_flush_chars: (%d,%p)\n", port->port_num, tty);
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (port->port_usb)
+ gs_start_tx(port);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static int gs_write_room(struct tty_struct *tty)
+{
+ struct gs_port *port = tty->driver_data;
+ unsigned long flags;
+ int room = 0;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (port->port_usb)
+ room = gs_buf_space_avail(&port->port_write_buf);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ pr_vdebug("gs_write_room: (%d,%p) room=%d\n",
+ port->port_num, tty, room);
+
+ return room;
+}
+
+static int gs_chars_in_buffer(struct tty_struct *tty)
+{
+ struct gs_port *port = tty->driver_data;
+ unsigned long flags;
+ int chars = 0;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ chars = gs_buf_data_avail(&port->port_write_buf);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ pr_vdebug("gs_chars_in_buffer: (%d,%p) chars=%d\n",
+ port->port_num, tty, chars);
+
+ return chars;
+}
+
+/* undo side effects of setting TTY_THROTTLED */
+static void gs_unthrottle(struct tty_struct *tty)
+{
+ struct gs_port *port = tty->driver_data;
+ unsigned long flags;
+ unsigned started = 0;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (port->port_usb)
+ started = gs_start_rx(port);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ pr_vdebug("gs_unthrottle: ttyGS%d, %d packets\n",
+ port->port_num, started);
+}
+
+#if 0
+
+static void gs_break(struct tty_struct *tty, int break_state)
+{
+}
+
+static int gs_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned cmd, unsigned long arg)
+{
+ struct gs_port *port = tty->driver_data;
+
+ pr_debug("gs_ioctl: (%d,%p,%p) cmd=0x%4.4x, arg=%lu\n",
+ port->port_num, tty, file, cmd, arg);
+
+ /* handle ioctls */
+
+ /* could not handle ioctl */
+ return -ENOIOCTLCMD;
+}
+
+static void gs_set_termios(struct tty_struct *tty, struct ktermios *old)
+{
+}
+
+#endif
+
+static const struct tty_operations gs_tty_ops = {
+ .open = gs_open,
+ .close = gs_close,
+ .write = gs_write,
+ .put_char = gs_put_char,
+ .flush_chars = gs_flush_chars,
+ .write_room = gs_write_room,
+ .chars_in_buffer = gs_chars_in_buffer,
+ .unthrottle = gs_unthrottle,
+#if 0
+ .break_ctl = gs_break,
+ .ioctl = gs_ioctl,
+ .set_termios = gs_set_termios,
+#endif
+};
+
+/*-------------------------------------------------------------------------*/
+
+static struct tty_driver *gs_tty_driver;
+
+static int __init
+gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
+{
+ struct gs_port *port;
+
+ port = kzalloc(sizeof(struct gs_port), GFP_KERNEL);
+ if (port == NULL)
+ return -ENOMEM;
+
+ spin_lock_init(&port->port_lock);
+ init_waitqueue_head(&port->close_wait);
+ init_waitqueue_head(&port->drain_wait);
+
+ tasklet_init(&port->push, gs_rx_push, (unsigned long) port);
+
+ INIT_LIST_HEAD(&port->read_pool);
+ INIT_LIST_HEAD(&port->write_pool);
+
+ port->port_num = port_num;
+ port->port_line_coding = *coding;
+
+ ports[port_num].port = port;
+
+ return 0;
+}
+
+/**
+ * gserial_setup - initialize TTY driver for one or more ports
+ * @g: gadget to associate with these ports
+ * @count: how many ports to support
+ * Context: may sleep
+ *
+ * The TTY stack needs to know in advance how many devices it should
+ * plan to manage. Use this call to set up the ports you will be
+ * exporting through USB. Later, connect them to functions based
+ * on what configuration is activated by the USB host; and disconnect
+ * them as appropriate.
+ *
+ * An example would be a two-configuration device in which both
+ * configurations expose port 0, but through different functions.
+ * One configuration could even expose port 1 while the other
+ * one doesn't.
+ *
+ * Returns negative errno or zero.
+ */
+int __init gserial_setup(struct usb_gadget *g, unsigned count)
+{
+ unsigned i;
+ struct usb_cdc_line_coding coding;
+ int status;
+
+ if (count == 0 || count > N_PORTS)
+ return -EINVAL;
+
+ gs_tty_driver = alloc_tty_driver(count);
+ if (!gs_tty_driver)
+ return -ENOMEM;
+
+ gs_tty_driver->owner = THIS_MODULE;
+ gs_tty_driver->driver_name = "g_serial";
+ gs_tty_driver->name = "ttyGS";
+ /* uses dynamically assigned dev_t values */
+
+ gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+ gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+ gs_tty_driver->init_termios = tty_std_termios;
+
+ /* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on
+ * MS-Windows. Otherwise, most of these flags shouldn't affect
+ * anything unless we were to actually hook up to a serial line.
+ */
+ gs_tty_driver->init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ gs_tty_driver->init_termios.c_ispeed = 9600;
+ gs_tty_driver->init_termios.c_ospeed = 9600;
+
+ coding.dwDTERate = __constant_cpu_to_le32(9600);
+ coding.bCharFormat = 8;
+ coding.bParityType = USB_CDC_NO_PARITY;
+ coding.bDataBits = USB_CDC_1_STOP_BITS;
+
+ tty_set_operations(gs_tty_driver, &gs_tty_ops);
+
+ /* make devices be openable */
+ for (i = 0; i < count; i++) {
+ mutex_init(&ports[i].lock);
+ status = gs_port_alloc(i, &coding);
+ if (status) {
+ count = i;
+ goto fail;
+ }
+ }
+ n_ports = count;
+
+ /* export the driver ... */
+ status = tty_register_driver(gs_tty_driver);
+ if (status) {
+ put_tty_driver(gs_tty_driver);
+ pr_err("%s: cannot register, err %d\n",
+ __func__, status);
+ goto fail;
+ }
+
+ /* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */
+ for (i = 0; i < count; i++) {
+ struct device *tty_dev;
+
+ tty_dev = tty_register_device(gs_tty_driver, i, &g->dev);
+ if (IS_ERR(tty_dev))
+ pr_warning("%s: no classdev for port %d, err %ld\n",
+ __func__, i, PTR_ERR(tty_dev));
+ }
+
+ pr_debug("%s: registered %d ttyGS* device%s\n", __func__,
+ count, (count == 1) ? "" : "s");
+
+ return status;
+fail:
+ while (count--)
+ kfree(ports[count].port);
+ put_tty_driver(gs_tty_driver);
+ gs_tty_driver = NULL;
+ return status;
+}
+
+static int gs_closed(struct gs_port *port)
+{
+ int cond;
+
+ spin_lock_irq(&port->port_lock);
+ cond = (port->open_count == 0) && !port->openclose;
+ spin_unlock_irq(&port->port_lock);
+ return cond;
+}
+
+/**
+ * gserial_cleanup - remove TTY-over-USB driver and devices
+ * Context: may sleep
+ *
+ * This is called to free all resources allocated by @gserial_setup().
+ * Accordingly, it may need to wait until some open /dev/ files have
+ * closed.
+ *
+ * The caller must have issued @gserial_disconnect() for any ports
+ * that had previously been connected, so that there is never any
+ * I/O pending when it's called.
+ */
+void gserial_cleanup(void)
+{
+ unsigned i;
+ struct gs_port *port;
+
+ /* start sysfs and /dev/ttyGS* node removal */
+ for (i = 0; i < n_ports; i++)
+ tty_unregister_device(gs_tty_driver, i);
+
+ for (i = 0; i < n_ports; i++) {
+ /* prevent new opens */
+ mutex_lock(&ports[i].lock);
+ port = ports[i].port;
+ ports[i].port = NULL;
+ mutex_unlock(&ports[i].lock);
+
+ /* wait for old opens to finish */
+ wait_event(port->close_wait, gs_closed(port));
+
+ WARN_ON(port->port_usb != NULL);
+
+ kfree(port);
+ }
+ n_ports = 0;
+
+ tty_unregister_driver(gs_tty_driver);
+ gs_tty_driver = NULL;
+
+ pr_debug("%s: cleaned up ttyGS* support\n", __func__);
+}
+
+/**
+ * gserial_connect - notify TTY I/O glue that USB link is active
+ * @gser: the function, set up with endpoints and descriptors
+ * @port_num: which port is active
+ * Context: any (usually from irq)
+ *
+ * This is called activate endpoints and let the TTY layer know that
+ * the connection is active ... not unlike "carrier detect". It won't
+ * necessarily start I/O queues; unless the TTY is held open by any
+ * task, there would be no point. However, the endpoints will be
+ * activated so the USB host can perform I/O, subject to basic USB
+ * hardware flow control.
+ *
+ * Caller needs to have set up the endpoints and USB function in @dev
+ * before calling this, as well as the appropriate (speed-specific)
+ * endpoint descriptors, and also have set up the TTY driver by calling
+ * @gserial_setup().
+ *
+ * Returns negative errno or zero.
+ * On success, ep->driver_data will be overwritten.
+ */
+int gserial_connect(struct gserial *gser, u8 port_num)
+{
+ struct gs_port *port;
+ unsigned long flags;
+ int status;
+
+ if (!gs_tty_driver || port_num >= n_ports)
+ return -ENXIO;
+
+ /* we "know" gserial_cleanup() hasn't been called */
+ port = ports[port_num].port;
+
+ /* activate the endpoints */
+ status = usb_ep_enable(gser->in, gser->in_desc);
+ if (status < 0)
+ return status;
+ gser->in->driver_data = port;
+
+ status = usb_ep_enable(gser->out, gser->out_desc);
+ if (status < 0)
+ goto fail_out;
+ gser->out->driver_data = port;
+
+ /* then tell the tty glue that I/O can work */
+ spin_lock_irqsave(&port->port_lock, flags);
+ gser->ioport = port;
+ port->port_usb = gser;
+
+ /* REVISIT unclear how best to handle this state...
+ * we don't really couple it with the Linux TTY.
+ */
+ gser->port_line_coding = port->port_line_coding;
+
+ /* REVISIT if waiting on "carrier detect", signal. */
+
+ /* REVISIT for ACM, issue "network connection" status notification:
+ * connected if open_count, else disconnected.
+ */
+
+ /* if it's already open, start I/O */
+ if (port->open_count) {
+ pr_debug("gserial_connect: start ttyGS%d\n", port->port_num);
+ gs_start_io(port);
+ }
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ return status;
+
+fail_out:
+ usb_ep_disable(gser->in);
+ gser->in->driver_data = NULL;
+ return status;
+}
+
+/**
+ * gserial_disconnect - notify TTY I/O glue that USB link is inactive
+ * @gser: the function, on which gserial_connect() was called
+ * Context: any (usually from irq)
+ *
+ * This is called to deactivate endpoints and let the TTY layer know
+ * that the connection went inactive ... not unlike "hangup".
+ *
+ * On return, the state is as if gserial_connect() had never been called;
+ * there is no active USB I/O on these endpoints.
+ */
+void gserial_disconnect(struct gserial *gser)
+{
+ struct gs_port *port = gser->ioport;
+ unsigned long flags;
+
+ if (!port)
+ return;
+
+ /* tell the TTY glue not to do I/O here any more */
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ /* REVISIT as above: how best to track this? */
+ port->port_line_coding = gser->port_line_coding;
+
+ port->port_usb = NULL;
+ gser->ioport = NULL;
+ if (port->open_count > 0 || port->openclose) {
+ wake_up_interruptible(&port->drain_wait);
+ if (port->port_tty)
+ tty_hangup(port->port_tty);
+ }
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ /* disable endpoints, aborting down any active I/O */
+ usb_ep_disable(gser->out);
+ gser->out->driver_data = NULL;
+
+ usb_ep_disable(gser->in);
+ gser->in->driver_data = NULL;
+
+ /* finally, free any unused/unusable I/O buffers */
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (port->open_count == 0 && !port->openclose)
+ gs_buf_free(&port->port_write_buf);
+ gs_free_requests(gser->out, &port->read_pool);
+ gs_free_requests(gser->in, &port->write_pool);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h
new file mode 100644
index 000000000000..eef881b8a574
--- /dev/null
+++ b/drivers/usb/gadget/u_serial.h
@@ -0,0 +1,47 @@
+#ifndef __U_SERIAL_H
+#define __U_SERIAL_H
+
+#include <linux/usb/composite.h>
+#include <linux/usb/cdc.h>
+
+/*
+ * One non-multiplexed "serial" I/O port ... there can be several of these
+ * on any given USB peripheral device, if it provides enough endpoints.
+ *
+ * The "u_serial" utility module exists to do one thing: manage TTY style
+ * I/O using the USB peripheral endpoints listed here, including hookups
+ * to sysfs and /dev for each logical "tty" device.
+ *
+ * REVISIT need TTY --> USB event flow too, so ACM can report open/close
+ * as carrier detect events. There's other ACM state too...
+ *
+ * REVISIT someday, allow multiplexing several TTYs over these endpoints.
+ */
+struct gserial {
+ struct usb_function func;
+
+ /* port is managed by gserial_{connect,disconnect} */
+ struct gs_port *ioport;
+
+ struct usb_ep *in;
+ struct usb_ep *out;
+ struct usb_endpoint_descriptor *in_desc;
+ struct usb_endpoint_descriptor *out_desc;
+
+ /* REVISIT avoid this CDC-ACM support harder ... */
+ struct usb_cdc_line_coding port_line_coding; /* 9600-8-N-1 etc */
+};
+
+/* port setup/teardown is handled by gadget driver */
+int gserial_setup(struct usb_gadget *g, unsigned n_ports);
+void gserial_cleanup(void);
+
+/* connect/disconnect is handled by individual functions */
+int gserial_connect(struct gserial *, u8 port_num);
+void gserial_disconnect(struct gserial *);
+
+/* functions are bound to configurations by a config or gadget driver */
+int acm_bind_config(struct usb_configuration *c, u8 port_num);
+int gser_bind_config(struct usb_configuration *c, u8 port_num);
+
+#endif /* __U_SERIAL_H */
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
index fce4924dbbe8..7a87d2c1ad03 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/zero.c
@@ -30,12 +30,7 @@
*
* It supports two similar configurations. One sinks whatever the usb host
* writes, and in return sources zeroes. The other loops whatever the host
- * writes back, so the host can read it. Module options include:
- *
- * buflen=N default N=4096, buffer size used
- * qlen=N default N=32, how many buffers in the loopback queue
- * loopdefault default false, list loopback config first
- * autoresume=N default N=0, seconds before triggering remote wakeup
+ * writes back, so the host can read it.
*
* Many drivers will only have one configuration, letting them be much
* simpler if they also don't support high speed operation (like this
@@ -47,94 +42,35 @@
* work with low capability USB controllers without four bulk endpoints.
*/
+/*
+ * driver assumes self-powered hardware, and
+ * has no way for users to trigger remote wakeup.
+ */
+
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
#include <linux/utsname.h>
#include <linux/device.h>
-#include <linux/usb/ch9.h>
-#include <linux/usb/gadget.h>
-
+#include "g_zero.h"
#include "gadget_chips.h"
/*-------------------------------------------------------------------------*/
-#define DRIVER_VERSION "Earth Day 2008"
+#define DRIVER_VERSION "Cinco de Mayo 2008"
-static const char shortname[] = "zero";
static const char longname[] = "Gadget Zero";
-static const char source_sink[] = "source and sink data";
-static const char loopback[] = "loop input to output";
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * driver assumes self-powered hardware, and
- * has no way for users to trigger remote wakeup.
- *
- * this version autoconfigures as much as possible,
- * which is reasonable for most "bulk-only" drivers.
- */
-static const char *EP_IN_NAME; /* source */
-static const char *EP_OUT_NAME; /* sink */
-
-/*-------------------------------------------------------------------------*/
-
-/* big enough to hold our biggest descriptor */
-#define USB_BUFSIZ 256
-
-struct zero_dev {
- spinlock_t lock;
- struct usb_gadget *gadget;
- struct usb_request *req; /* for control responses */
-
- /* when configured, we have one of two configs:
- * - source data (in to host) and sink it (out from host)
- * - or loop it back (out from host back in to host)
- */
- u8 config;
- struct usb_ep *in_ep, *out_ep;
-
- /* autoresume timer */
- struct timer_list resume;
-};
-
-#define DBG(d, fmt, args...) \
- dev_dbg(&(d)->gadget->dev , fmt , ## args)
-#define VDBG(d, fmt, args...) \
- dev_vdbg(&(d)->gadget->dev , fmt , ## args)
-#define ERROR(d, fmt, args...) \
- dev_err(&(d)->gadget->dev , fmt , ## args)
-#define WARN(d, fmt, args...) \
- dev_warn(&(d)->gadget->dev , fmt , ## args)
-#define INFO(d, fmt, args...) \
- dev_info(&(d)->gadget->dev , fmt , ## args)
-
-/*-------------------------------------------------------------------------*/
-
-static unsigned buflen = 4096;
-static unsigned qlen = 32;
-static unsigned pattern = 0;
-
-module_param(buflen, uint, S_IRUGO);
-module_param(qlen, uint, S_IRUGO);
-module_param(pattern, uint, S_IRUGO|S_IWUSR);
-
-/*
- * if it's nonzero, autoresume says how many seconds to wait
- * before trying to wake up the host after suspend.
- */
-static unsigned autoresume = 0;
-module_param(autoresume, uint, 0);
+unsigned buflen = 4096;
+module_param(buflen, uint, 0);
/*
* Normally the "loopback" configuration is second (index 1) so
* it's not the default. Here's where to change that order, to
- * work better with hosts where config changes are problematic.
- * Or controllers (like superh) that only support one config.
+ * work better with hosts where config changes are problematic or
+ * controllers (like original superh) that only support one config.
*/
static int loopdefault = 0;
module_param(loopdefault, bool, S_IRUGO|S_IWUSR);
@@ -156,24 +92,6 @@ module_param(loopdefault, bool, S_IRUGO|S_IWUSR);
/*-------------------------------------------------------------------------*/
-/*
- * DESCRIPTORS ... most are static, but strings and (full)
- * configuration descriptors are built on demand.
- */
-
-#define STRING_MANUFACTURER 25
-#define STRING_PRODUCT 42
-#define STRING_SERIAL 101
-#define STRING_SOURCE_SINK 250
-#define STRING_LOOPBACK 251
-
-/*
- * This device advertises two configurations; these numbers work
- * on a pxa250 as well as more flexible hardware.
- */
-#define CONFIG_SOURCE_SINK 3
-#define CONFIG_LOOPBACK 2
-
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
@@ -183,248 +101,64 @@ static struct usb_device_descriptor device_desc = {
.idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM),
.idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM),
- .iManufacturer = STRING_MANUFACTURER,
- .iProduct = STRING_PRODUCT,
- .iSerialNumber = STRING_SERIAL,
.bNumConfigurations = 2,
};
-static struct usb_config_descriptor source_sink_config = {
- .bLength = sizeof source_sink_config,
- .bDescriptorType = USB_DT_CONFIG,
-
- /* compute wTotalLength on the fly */
- .bNumInterfaces = 1,
- .bConfigurationValue = CONFIG_SOURCE_SINK,
- .iConfiguration = STRING_SOURCE_SINK,
- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
- .bMaxPower = 1, /* self-powered */
-};
-
-static struct usb_config_descriptor loopback_config = {
- .bLength = sizeof loopback_config,
- .bDescriptorType = USB_DT_CONFIG,
-
- /* compute wTotalLength on the fly */
- .bNumInterfaces = 1,
- .bConfigurationValue = CONFIG_LOOPBACK,
- .iConfiguration = STRING_LOOPBACK,
- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
- .bMaxPower = 1, /* self-powered */
-};
-
+#ifdef CONFIG_USB_OTG
static struct usb_otg_descriptor otg_descriptor = {
.bLength = sizeof otg_descriptor,
.bDescriptorType = USB_DT_OTG,
- .bmAttributes = USB_OTG_SRP,
-};
-
-/* one interface in each configuration */
-
-static const struct usb_interface_descriptor source_sink_intf = {
- .bLength = sizeof source_sink_intf,
- .bDescriptorType = USB_DT_INTERFACE,
-
- .bNumEndpoints = 2,
- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
- .iInterface = STRING_SOURCE_SINK,
-};
-
-static const struct usb_interface_descriptor loopback_intf = {
- .bLength = sizeof loopback_intf,
- .bDescriptorType = USB_DT_INTERFACE,
-
- .bNumEndpoints = 2,
- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
- .iInterface = STRING_LOOPBACK,
-};
-
-/* two full speed bulk endpoints; their use is config-dependent */
-
-static struct usb_endpoint_descriptor fs_source_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bEndpointAddress = USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
-};
-
-static struct usb_endpoint_descriptor fs_sink_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bEndpointAddress = USB_DIR_OUT,
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
-};
-
-static const struct usb_descriptor_header *fs_source_sink_function[] = {
- (struct usb_descriptor_header *) &otg_descriptor,
- (struct usb_descriptor_header *) &source_sink_intf,
- (struct usb_descriptor_header *) &fs_sink_desc,
- (struct usb_descriptor_header *) &fs_source_desc,
- NULL,
-};
-
-static const struct usb_descriptor_header *fs_loopback_function[] = {
- (struct usb_descriptor_header *) &otg_descriptor,
- (struct usb_descriptor_header *) &loopback_intf,
- (struct usb_descriptor_header *) &fs_sink_desc,
- (struct usb_descriptor_header *) &fs_source_desc,
- NULL,
-};
-
-/*
- * usb 2.0 devices need to expose both high speed and full speed
- * descriptors, unless they only run at full speed.
- *
- * that means alternate endpoint descriptors (bigger packets)
- * and a "device qualifier" ... plus more construction options
- * for the config descriptor.
- */
-
-static struct usb_endpoint_descriptor hs_source_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = __constant_cpu_to_le16(512),
-};
-
-static struct usb_endpoint_descriptor hs_sink_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT,
-
- .bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = __constant_cpu_to_le16(512),
-};
-
-static struct usb_qualifier_descriptor dev_qualifier = {
- .bLength = sizeof dev_qualifier,
- .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
-
- .bcdUSB = __constant_cpu_to_le16(0x0200),
- .bDeviceClass = USB_CLASS_VENDOR_SPEC,
-
- .bNumConfigurations = 2,
+ /* REVISIT SRP-only hardware is possible, although
+ * it would not be called "OTG" ...
+ */
+ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
};
-static const struct usb_descriptor_header *hs_source_sink_function[] = {
+const struct usb_descriptor_header **otg_desc = {
(struct usb_descriptor_header *) &otg_descriptor,
- (struct usb_descriptor_header *) &source_sink_intf,
- (struct usb_descriptor_header *) &hs_source_desc,
- (struct usb_descriptor_header *) &hs_sink_desc,
NULL,
};
+#endif
-static const struct usb_descriptor_header *hs_loopback_function[] = {
- (struct usb_descriptor_header *) &otg_descriptor,
- (struct usb_descriptor_header *) &loopback_intf,
- (struct usb_descriptor_header *) &hs_source_desc,
- (struct usb_descriptor_header *) &hs_sink_desc,
- NULL,
-};
+/* string IDs are assigned dynamically */
-/* maxpacket and other transfer characteristics vary by speed. */
-static inline struct usb_endpoint_descriptor *
-ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs,
- struct usb_endpoint_descriptor *fs)
-{
- if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
- return hs;
- return fs;
-}
+#define STRING_MANUFACTURER_IDX 0
+#define STRING_PRODUCT_IDX 1
+#define STRING_SERIAL_IDX 2
static char manufacturer[50];
/* default serial number takes at least two packets */
static char serial[] = "0123456789.0123456789.0123456789";
-
-/* static strings, in UTF-8 */
-static struct usb_string strings[] = {
- { STRING_MANUFACTURER, manufacturer, },
- { STRING_PRODUCT, longname, },
- { STRING_SERIAL, serial, },
- { STRING_LOOPBACK, loopback, },
- { STRING_SOURCE_SINK, source_sink, },
+static struct usb_string strings_dev[] = {
+ [STRING_MANUFACTURER_IDX].s = manufacturer,
+ [STRING_PRODUCT_IDX].s = longname,
+ [STRING_SERIAL_IDX].s = serial,
{ } /* end of list */
};
-static struct usb_gadget_strings stringtab = {
+static struct usb_gadget_strings stringtab_dev = {
.language = 0x0409, /* en-us */
- .strings = strings,
+ .strings = strings_dev,
};
-/*
- * config descriptors are also handcrafted. these must agree with code
- * that sets configurations, and with code managing interfaces and their
- * altsettings. other complexity may come from:
- *
- * - high speed support, including "other speed config" rules
- * - multiple configurations
- * - interfaces with alternate settings
- * - embedded class or vendor-specific descriptors
- *
- * this handles high speed, and has a second config that could as easily
- * have been an alternate interface setting (on most hardware).
- *
- * NOTE: to demonstrate (and test) more USB capabilities, this driver
- * should include an altsetting to test interrupt transfers, including
- * high bandwidth modes at high speed. (Maybe work like Intel's test
- * device?)
- */
-static int config_buf(struct usb_gadget *gadget,
- u8 *buf, u8 type, unsigned index)
-{
- int is_source_sink;
- int len;
- const struct usb_descriptor_header **function;
- int hs = 0;
-
- /* two configurations will always be index 0 and index 1 */
- if (index > 1)
- return -EINVAL;
- is_source_sink = loopdefault ? (index == 1) : (index == 0);
-
- if (gadget_is_dualspeed(gadget)) {
- hs = (gadget->speed == USB_SPEED_HIGH);
- if (type == USB_DT_OTHER_SPEED_CONFIG)
- hs = !hs;
- }
- if (hs)
- function = is_source_sink
- ? hs_source_sink_function
- : hs_loopback_function;
- else
- function = is_source_sink
- ? fs_source_sink_function
- : fs_loopback_function;
-
- /* for now, don't advertise srp-only devices */
- if (!gadget_is_otg(gadget))
- function++;
-
- len = usb_gadget_config_buf(is_source_sink
- ? &source_sink_config
- : &loopback_config,
- buf, USB_BUFSIZ, function);
- if (len < 0)
- return len;
- ((struct usb_config_descriptor *) buf)->bDescriptorType = type;
- return len;
-}
+static struct usb_gadget_strings *dev_strings[] = {
+ &stringtab_dev,
+ NULL,
+};
/*-------------------------------------------------------------------------*/
-static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length)
+struct usb_request *alloc_ep_req(struct usb_ep *ep)
{
struct usb_request *req;
req = usb_ep_alloc_request(ep, GFP_ATOMIC);
if (req) {
- req->length = length;
- req->buf = kmalloc(length, GFP_ATOMIC);
+ req->length = buflen;
+ req->buf = kmalloc(buflen, GFP_ATOMIC);
if (!req->buf) {
usb_ep_free_request(ep, req);
req = NULL;
@@ -433,681 +167,73 @@ static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length)
return req;
}
-static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
+void free_ep_req(struct usb_ep *ep, struct usb_request *req)
{
kfree(req->buf);
usb_ep_free_request(ep, req);
}
-/*-------------------------------------------------------------------------*/
-
-/*
- * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripherals,
- * this just sinks bulk packets OUT to the peripheral and sources them IN
- * to the host, optionally with specific data patterns.
- *
- * In terms of control messaging, this supports all the standard requests
- * plus two that support control-OUT tests.
- *
- * Note that because this doesn't queue more than one request at a time,
- * some other function must be used to test queueing logic. The network
- * link (g_ether) is probably the best option for that.
- */
-
-/* optionally require specific source/sink data patterns */
-
-static int
-check_read_data(
- struct zero_dev *dev,
- struct usb_ep *ep,
- struct usb_request *req
-)
+static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
{
- unsigned i;
- u8 *buf = req->buf;
-
- for (i = 0; i < req->actual; i++, buf++) {
- switch (pattern) {
- /* all-zeroes has no synchronization issues */
- case 0:
- if (*buf == 0)
- continue;
- break;
- /* mod63 stays in sync with short-terminated transfers,
- * or otherwise when host and gadget agree on how large
- * each usb transfer request should be. resync is done
- * with set_interface or set_config.
- */
- case 1:
- if (*buf == (u8)(i % 63))
- continue;
- break;
- }
- ERROR(dev, "bad OUT byte, buf[%d] = %d\n", i, *buf);
- usb_ep_set_halt(ep);
- return -EINVAL;
+ int value;
+
+ if (ep->driver_data) {
+ value = usb_ep_disable(ep);
+ if (value < 0)
+ DBG(cdev, "disable %s --> %d\n",
+ ep->name, value);
+ ep->driver_data = NULL;
}
- return 0;
}
-static void reinit_write_data(struct usb_ep *ep, struct usb_request *req)
+void disable_endpoints(struct usb_composite_dev *cdev,
+ struct usb_ep *in, struct usb_ep *out)
{
- unsigned i;
- u8 *buf = req->buf;
-
- switch (pattern) {
- case 0:
- memset(req->buf, 0, req->length);
- break;
- case 1:
- for (i = 0; i < req->length; i++)
- *buf++ = (u8) (i % 63);
- break;
- }
-}
-
-/* if there is only one request in the queue, there'll always be an
- * irq delay between end of one request and start of the next.
- * that prevents using hardware dma queues.
- */
-static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
-{
- struct zero_dev *dev = ep->driver_data;
- int status = req->status;
-
- switch (status) {
-
- case 0: /* normal completion? */
- if (ep == dev->out_ep) {
- check_read_data(dev, ep, req);
- memset(req->buf, 0x55, req->length);
- } else
- reinit_write_data(ep, req);
- break;
-
- /* this endpoint is normally active while we're configured */
- case -ECONNABORTED: /* hardware forced ep reset */
- case -ECONNRESET: /* request dequeued */
- case -ESHUTDOWN: /* disconnect from host */
- VDBG(dev, "%s gone (%d), %d/%d\n", ep->name, status,
- req->actual, req->length);
- if (ep == dev->out_ep)
- check_read_data(dev, ep, req);
- free_ep_req(ep, req);
- return;
-
- case -EOVERFLOW: /* buffer overrun on read means that
- * we didn't provide a big enough
- * buffer.
- */
- default:
-#if 1
- DBG(dev, "%s complete --> %d, %d/%d\n", ep->name,
- status, req->actual, req->length);
-#endif
- case -EREMOTEIO: /* short read */
- break;
- }
-
- status = usb_ep_queue(ep, req, GFP_ATOMIC);
- if (status) {
- ERROR(dev, "kill %s: resubmit %d bytes --> %d\n",
- ep->name, req->length, status);
- usb_ep_set_halt(ep);
- /* FIXME recover later ... somehow */
- }
-}
-
-static struct usb_request *source_sink_start_ep(struct usb_ep *ep)
-{
- struct usb_request *req;
- int status;
-
- req = alloc_ep_req(ep, buflen);
- if (!req)
- return NULL;
-
- memset(req->buf, 0, req->length);
- req->complete = source_sink_complete;
-
- if (strcmp(ep->name, EP_IN_NAME) == 0)
- reinit_write_data(ep, req);
- else
- memset(req->buf, 0x55, req->length);
-
- status = usb_ep_queue(ep, req, GFP_ATOMIC);
- if (status) {
- struct zero_dev *dev = ep->driver_data;
-
- ERROR(dev, "start %s --> %d\n", ep->name, status);
- free_ep_req(ep, req);
- req = NULL;
- }
-
- return req;
-}
-
-static int set_source_sink_config(struct zero_dev *dev)
-{
- int result = 0;
- struct usb_ep *ep;
- struct usb_gadget *gadget = dev->gadget;
-
- gadget_for_each_ep(ep, gadget) {
- const struct usb_endpoint_descriptor *d;
-
- /* one endpoint writes (sources) zeroes in (to the host) */
- if (strcmp(ep->name, EP_IN_NAME) == 0) {
- d = ep_desc(gadget, &hs_source_desc, &fs_source_desc);
- result = usb_ep_enable(ep, d);
- if (result == 0) {
- ep->driver_data = dev;
- if (source_sink_start_ep(ep) != NULL) {
- dev->in_ep = ep;
- continue;
- }
- usb_ep_disable(ep);
- result = -EIO;
- }
-
- /* one endpoint reads (sinks) anything out (from the host) */
- } else if (strcmp(ep->name, EP_OUT_NAME) == 0) {
- d = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc);
- result = usb_ep_enable(ep, d);
- if (result == 0) {
- ep->driver_data = dev;
- if (source_sink_start_ep(ep) != NULL) {
- dev->out_ep = ep;
- continue;
- }
- usb_ep_disable(ep);
- result = -EIO;
- }
-
- /* ignore any other endpoints */
- } else
- continue;
-
- /* stop on error */
- ERROR(dev, "can't start %s, result %d\n", ep->name, result);
- break;
- }
- if (result == 0)
- DBG(dev, "buflen %d\n", buflen);
-
- /* caller is responsible for cleanup on error */
- return result;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
-{
- struct zero_dev *dev = ep->driver_data;
- int status = req->status;
-
- switch (status) {
-
- case 0: /* normal completion? */
- if (ep == dev->out_ep) {
- /* loop this OUT packet back IN to the host */
- req->zero = (req->actual < req->length);
- req->length = req->actual;
- status = usb_ep_queue(dev->in_ep, req, GFP_ATOMIC);
- if (status == 0)
- return;
-
- /* "should never get here" */
- ERROR(dev, "can't loop %s to %s: %d\n",
- ep->name, dev->in_ep->name,
- status);
- }
-
- /* queue the buffer for some later OUT packet */
- req->length = buflen;
- status = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC);
- if (status == 0)
- return;
-
- /* "should never get here" */
- /* FALLTHROUGH */
-
- default:
- ERROR(dev, "%s loop complete --> %d, %d/%d\n", ep->name,
- status, req->actual, req->length);
- /* FALLTHROUGH */
-
- /* NOTE: since this driver doesn't maintain an explicit record
- * of requests it submitted (just maintains qlen count), we
- * rely on the hardware driver to clean up on disconnect or
- * endpoint disable.
- */
- case -ECONNABORTED: /* hardware forced ep reset */
- case -ECONNRESET: /* request dequeued */
- case -ESHUTDOWN: /* disconnect from host */
- free_ep_req(ep, req);
- return;
- }
-}
-
-static int set_loopback_config(struct zero_dev *dev)
-{
- int result = 0;
- struct usb_ep *ep;
- struct usb_gadget *gadget = dev->gadget;
-
- gadget_for_each_ep(ep, gadget) {
- const struct usb_endpoint_descriptor *d;
-
- /* one endpoint writes data back IN to the host */
- if (strcmp(ep->name, EP_IN_NAME) == 0) {
- d = ep_desc(gadget, &hs_source_desc, &fs_source_desc);
- result = usb_ep_enable(ep, d);
- if (result == 0) {
- ep->driver_data = dev;
- dev->in_ep = ep;
- continue;
- }
-
- /* one endpoint just reads OUT packets */
- } else if (strcmp(ep->name, EP_OUT_NAME) == 0) {
- d = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc);
- result = usb_ep_enable(ep, d);
- if (result == 0) {
- ep->driver_data = dev;
- dev->out_ep = ep;
- continue;
- }
-
- /* ignore any other endpoints */
- } else
- continue;
-
- /* stop on error */
- ERROR(dev, "can't enable %s, result %d\n", ep->name, result);
- break;
- }
-
- /* allocate a bunch of read buffers and queue them all at once.
- * we buffer at most 'qlen' transfers; fewer if any need more
- * than 'buflen' bytes each.
- */
- if (result == 0) {
- struct usb_request *req;
- unsigned i;
-
- ep = dev->out_ep;
- for (i = 0; i < qlen && result == 0; i++) {
- req = alloc_ep_req(ep, buflen);
- if (req) {
- req->complete = loopback_complete;
- result = usb_ep_queue(ep, req, GFP_ATOMIC);
- if (result)
- DBG(dev, "%s queue req --> %d\n",
- ep->name, result);
- } else
- result = -ENOMEM;
- }
- }
- if (result == 0)
- DBG(dev, "qlen %d, buflen %d\n", qlen, buflen);
-
- /* caller is responsible for cleanup on error */
- return result;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void zero_reset_config(struct zero_dev *dev)
-{
- if (dev->config == 0)
- return;
-
- DBG(dev, "reset config\n");
-
- /* just disable endpoints, forcing completion of pending i/o.
- * all our completion handlers free their requests in this case.
- */
- if (dev->in_ep) {
- usb_ep_disable(dev->in_ep);
- dev->in_ep = NULL;
- }
- if (dev->out_ep) {
- usb_ep_disable(dev->out_ep);
- dev->out_ep = NULL;
- }
- dev->config = 0;
- del_timer(&dev->resume);
-}
-
-/* change our operational config. this code must agree with the code
- * that returns config descriptors, and altsetting code.
- *
- * it's also responsible for power management interactions. some
- * configurations might not work with our current power sources.
- *
- * note that some device controller hardware will constrain what this
- * code can do, perhaps by disallowing more than one configuration or
- * by limiting configuration choices (like the pxa2xx).
- */
-static int zero_set_config(struct zero_dev *dev, unsigned number)
-{
- int result = 0;
- struct usb_gadget *gadget = dev->gadget;
-
- if (number == dev->config)
- return 0;
-
- if (gadget_is_sa1100(gadget) && dev->config) {
- /* tx fifo is full, but we can't clear it...*/
- ERROR(dev, "can't change configurations\n");
- return -ESPIPE;
- }
- zero_reset_config(dev);
-
- switch (number) {
- case CONFIG_SOURCE_SINK:
- result = set_source_sink_config(dev);
- break;
- case CONFIG_LOOPBACK:
- result = set_loopback_config(dev);
- break;
- default:
- result = -EINVAL;
- /* FALL THROUGH */
- case 0:
- return result;
- }
-
- if (!result && (!dev->in_ep || !dev->out_ep))
- result = -ENODEV;
- if (result)
- zero_reset_config(dev);
- else {
- char *speed;
-
- switch (gadget->speed) {
- case USB_SPEED_LOW: speed = "low"; break;
- case USB_SPEED_FULL: speed = "full"; break;
- case USB_SPEED_HIGH: speed = "high"; break;
- default: speed = "?"; break;
- }
-
- dev->config = number;
- INFO(dev, "%s speed config #%d: %s\n", speed, number,
- (number == CONFIG_SOURCE_SINK)
- ? source_sink : loopback);
- }
- return result;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void zero_setup_complete(struct usb_ep *ep, struct usb_request *req)
-{
- if (req->status || req->actual != req->length)
- DBG((struct zero_dev *) ep->driver_data,
- "setup complete --> %d, %d/%d\n",
- req->status, req->actual, req->length);
-}
-
-/*
- * The setup() callback implements all the ep0 functionality that's
- * not handled lower down, in hardware or the hardware driver (like
- * device and endpoint feature flags, and their status). It's all
- * housekeeping for the gadget function we're implementing. Most of
- * the work is in config-specific setup.
- */
-static int
-zero_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
-{
- struct zero_dev *dev = get_gadget_data(gadget);
- struct usb_request *req = dev->req;
- int value = -EOPNOTSUPP;
- u16 w_index = le16_to_cpu(ctrl->wIndex);
- u16 w_value = le16_to_cpu(ctrl->wValue);
- u16 w_length = le16_to_cpu(ctrl->wLength);
-
- /* usually this stores reply data in the pre-allocated ep0 buffer,
- * but config change events will reconfigure hardware.
- */
- req->zero = 0;
- switch (ctrl->bRequest) {
-
- case USB_REQ_GET_DESCRIPTOR:
- if (ctrl->bRequestType != USB_DIR_IN)
- goto unknown;
- switch (w_value >> 8) {
-
- case USB_DT_DEVICE:
- value = min(w_length, (u16) sizeof device_desc);
- memcpy(req->buf, &device_desc, value);
- break;
- case USB_DT_DEVICE_QUALIFIER:
- if (!gadget_is_dualspeed(gadget))
- break;
- value = min(w_length, (u16) sizeof dev_qualifier);
- memcpy(req->buf, &dev_qualifier, value);
- break;
-
- case USB_DT_OTHER_SPEED_CONFIG:
- if (!gadget_is_dualspeed(gadget))
- break;
- // FALLTHROUGH
- case USB_DT_CONFIG:
- value = config_buf(gadget, req->buf,
- w_value >> 8,
- w_value & 0xff);
- if (value >= 0)
- value = min(w_length, (u16) value);
- break;
-
- case USB_DT_STRING:
- /* wIndex == language code.
- * this driver only handles one language, you can
- * add string tables for other languages, using
- * any UTF-8 characters
- */
- value = usb_gadget_get_string(&stringtab,
- w_value & 0xff, req->buf);
- if (value >= 0)
- value = min(w_length, (u16) value);
- break;
- }
- break;
-
- /* currently two configs, two speeds */
- case USB_REQ_SET_CONFIGURATION:
- if (ctrl->bRequestType != 0)
- goto unknown;
- if (gadget->a_hnp_support)
- DBG(dev, "HNP available\n");
- else if (gadget->a_alt_hnp_support)
- DBG(dev, "HNP needs a different root port\n");
- else
- VDBG(dev, "HNP inactive\n");
- spin_lock(&dev->lock);
- value = zero_set_config(dev, w_value);
- spin_unlock(&dev->lock);
- break;
- case USB_REQ_GET_CONFIGURATION:
- if (ctrl->bRequestType != USB_DIR_IN)
- goto unknown;
- *(u8 *)req->buf = dev->config;
- value = min(w_length, (u16) 1);
- break;
-
- /* until we add altsetting support, or other interfaces,
- * only 0/0 are possible. pxa2xx only supports 0/0 (poorly)
- * and already killed pending endpoint I/O.
- */
- case USB_REQ_SET_INTERFACE:
- if (ctrl->bRequestType != USB_RECIP_INTERFACE)
- goto unknown;
- spin_lock(&dev->lock);
- if (dev->config && w_index == 0 && w_value == 0) {
- u8 config = dev->config;
-
- /* resets interface configuration, forgets about
- * previous transaction state (queued bufs, etc)
- * and re-inits endpoint state (toggle etc)
- * no response queued, just zero status == success.
- * if we had more than one interface we couldn't
- * use this "reset the config" shortcut.
- */
- zero_reset_config(dev);
- zero_set_config(dev, config);
- value = 0;
- }
- spin_unlock(&dev->lock);
- break;
- case USB_REQ_GET_INTERFACE:
- if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
- goto unknown;
- if (!dev->config)
- break;
- if (w_index != 0) {
- value = -EDOM;
- break;
- }
- *(u8 *)req->buf = 0;
- value = min(w_length, (u16) 1);
- break;
-
- /*
- * These are the same vendor-specific requests supported by
- * Intel's USB 2.0 compliance test devices. We exceed that
- * device spec by allowing multiple-packet requests.
- */
- case 0x5b: /* control WRITE test -- fill the buffer */
- if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR))
- goto unknown;
- if (w_value || w_index)
- break;
- /* just read that many bytes into the buffer */
- if (w_length > USB_BUFSIZ)
- break;
- value = w_length;
- break;
- case 0x5c: /* control READ test -- return the buffer */
- if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR))
- goto unknown;
- if (w_value || w_index)
- break;
- /* expect those bytes are still in the buffer; send back */
- if (w_length > USB_BUFSIZ
- || w_length != req->length)
- break;
- value = w_length;
- break;
-
- default:
-unknown:
- VDBG(dev,
- "unknown control req%02x.%02x v%04x i%04x l%d\n",
- ctrl->bRequestType, ctrl->bRequest,
- w_value, w_index, w_length);
- }
-
- /* respond with data transfer before status phase? */
- if (value >= 0) {
- req->length = value;
- req->zero = value < w_length;
- value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
- if (value < 0) {
- DBG(dev, "ep_queue --> %d\n", value);
- req->status = 0;
- zero_setup_complete(gadget->ep0, req);
- }
- }
-
- /* device either stalls (value < 0) or reports success */
- return value;
-}
-
-static void zero_disconnect(struct usb_gadget *gadget)
-{
- struct zero_dev *dev = get_gadget_data(gadget);
- unsigned long flags;
-
- spin_lock_irqsave(&dev->lock, flags);
- zero_reset_config(dev);
-
- /* a more significant application might have some non-usb
- * activities to quiesce here, saving resources like power
- * or pushing the notification up a network stack.
- */
- spin_unlock_irqrestore(&dev->lock, flags);
-
- /* next we may get setup() calls to enumerate new connections;
- * or an unbind() during shutdown (including removing module).
- */
-}
-
-static void zero_autoresume(unsigned long _dev)
-{
- struct zero_dev *dev = (struct zero_dev *) _dev;
- int status;
-
- /* normally the host would be woken up for something
- * more significant than just a timer firing...
- */
- if (dev->gadget->speed != USB_SPEED_UNKNOWN) {
- status = usb_gadget_wakeup(dev->gadget);
- DBG(dev, "wakeup --> %d\n", status);
- }
+ disable_ep(cdev, in);
+ disable_ep(cdev, out);
}
/*-------------------------------------------------------------------------*/
-static void zero_unbind(struct usb_gadget *gadget)
+static int __init zero_bind(struct usb_composite_dev *cdev)
{
- struct zero_dev *dev = get_gadget_data(gadget);
-
- DBG(dev, "unbind\n");
-
- /* we've already been disconnected ... no i/o is active */
- if (dev->req) {
- dev->req->length = USB_BUFSIZ;
- free_ep_req(gadget->ep0, dev->req);
- }
- del_timer_sync(&dev->resume);
- kfree(dev);
- set_gadget_data(gadget, NULL);
-}
-
-static int __init zero_bind(struct usb_gadget *gadget)
-{
- struct zero_dev *dev;
- struct usb_ep *ep;
int gcnum;
+ struct usb_gadget *gadget = cdev->gadget;
+ int id;
- /* FIXME this can't yet work right with SH ... it has only
- * one configuration, numbered one.
+ /* Allocate string descriptor numbers ... note that string
+ * contents can be overridden by the composite_dev glue.
*/
- if (gadget_is_sh(gadget))
- return -ENODEV;
-
- /* Bulk-only drivers like this one SHOULD be able to
- * autoconfigure on any sane usb controller driver,
- * but there may also be important quirks to address.
+ id = usb_string_id(cdev);
+ if (id < 0)
+ return id;
+ strings_dev[STRING_MANUFACTURER_IDX].id = id;
+ device_desc.iManufacturer = id;
+
+ id = usb_string_id(cdev);
+ if (id < 0)
+ return id;
+ strings_dev[STRING_PRODUCT_IDX].id = id;
+ device_desc.iProduct = id;
+
+ id = usb_string_id(cdev);
+ if (id < 0)
+ return id;
+ strings_dev[STRING_SERIAL_IDX].id = id;
+ device_desc.iSerialNumber = id;
+
+ /* Register primary, then secondary configuration. Note that
+ * SH3 only allows one config...
*/
- usb_ep_autoconfig_reset(gadget);
- ep = usb_ep_autoconfig(gadget, &fs_source_desc);
- if (!ep) {
-autoconf_fail:
- pr_err("%s: can't autoconfigure on %s\n",
- shortname, gadget->name);
- return -ENODEV;
+ if (loopdefault) {
+ loopback_add(cdev);
+ if (!gadget_is_sh(gadget))
+ sourcesink_add(cdev);
+ } else {
+ sourcesink_add(cdev);
+ if (!gadget_is_sh(gadget))
+ loopback_add(cdev);
}
- EP_IN_NAME = ep->name;
- ep->driver_data = ep; /* claim */
-
- ep = usb_ep_autoconfig(gadget, &fs_sink_desc);
- if (!ep)
- goto autoconf_fail;
- EP_OUT_NAME = ep->name;
- ep->driver_data = ep; /* claim */
gcnum = usb_gadget_controller_number(gadget);
if (gcnum >= 0)
@@ -1115,144 +241,44 @@ autoconf_fail:
else {
/* gadget zero is so simple (for now, no altsettings) that
* it SHOULD NOT have problems with bulk-capable hardware.
- * so warn about unrcognized controllers, don't panic.
+ * so just warn about unrcognized controllers -- don't panic.
*
* things like configuration and altsetting numbering
* can need hardware-specific attention though.
*/
pr_warning("%s: controller '%s' not recognized\n",
- shortname, gadget->name);
+ longname, gadget->name);
device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
}
- /* ok, we made sense of the hardware ... */
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
- spin_lock_init(&dev->lock);
- dev->gadget = gadget;
- set_gadget_data(gadget, dev);
-
- init_timer(&dev->resume);
- dev->resume.function = zero_autoresume;
- dev->resume.data = (unsigned long) dev;
-
- /* preallocate control response and buffer */
- dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
- if (!dev->req)
- goto enomem;
- dev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
- if (!dev->req->buf)
- goto enomem;
-
- dev->req->complete = zero_setup_complete;
-
- device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
-
- if (gadget_is_dualspeed(gadget)) {
- /* assume ep0 uses the same value for both speeds ... */
- dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0;
-
- /* and that all endpoints are dual-speed */
- hs_source_desc.bEndpointAddress =
- fs_source_desc.bEndpointAddress;
- hs_sink_desc.bEndpointAddress =
- fs_sink_desc.bEndpointAddress;
- }
-
- if (gadget_is_otg(gadget)) {
- otg_descriptor.bmAttributes |= USB_OTG_HNP,
- source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
- loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
- }
-
- usb_gadget_set_selfpowered(gadget);
-
- if (autoresume) {
- source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
- loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
- }
-
- gadget->ep0->driver_data = dev;
-
- INFO(dev, "%s, version: " DRIVER_VERSION "\n", longname);
- INFO(dev, "using %s, OUT %s IN %s\n", gadget->name,
- EP_OUT_NAME, EP_IN_NAME);
+ INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname);
snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
init_utsname()->sysname, init_utsname()->release,
gadget->name);
return 0;
-
-enomem:
- zero_unbind(gadget);
- return -ENOMEM;
}
-/*-------------------------------------------------------------------------*/
-
-static void zero_suspend(struct usb_gadget *gadget)
-{
- struct zero_dev *dev = get_gadget_data(gadget);
-
- if (gadget->speed == USB_SPEED_UNKNOWN)
- return;
-
- if (autoresume) {
- mod_timer(&dev->resume, jiffies + (HZ * autoresume));
- DBG(dev, "suspend, wakeup in %d seconds\n", autoresume);
- } else
- DBG(dev, "suspend\n");
-}
-
-static void zero_resume(struct usb_gadget *gadget)
-{
- struct zero_dev *dev = get_gadget_data(gadget);
-
- DBG(dev, "resume\n");
- del_timer(&dev->resume);
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-static struct usb_gadget_driver zero_driver = {
-#ifdef CONFIG_USB_GADGET_DUALSPEED
- .speed = USB_SPEED_HIGH,
-#else
- .speed = USB_SPEED_FULL,
-#endif
- .function = (char *) longname,
+static struct usb_composite_driver zero_driver = {
+ .name = "zero",
+ .dev = &device_desc,
+ .strings = dev_strings,
.bind = zero_bind,
- .unbind = __exit_p(zero_unbind),
-
- .setup = zero_setup,
- .disconnect = zero_disconnect,
-
- .suspend = zero_suspend,
- .resume = zero_resume,
-
- .driver = {
- .name = (char *) shortname,
- .owner = THIS_MODULE,
- },
};
MODULE_AUTHOR("David Brownell");
MODULE_LICENSE("GPL");
-
static int __init init(void)
{
- return usb_gadget_register_driver(&zero_driver);
+ return usb_composite_register(&zero_driver);
}
module_init(init);
static void __exit cleanup(void)
{
- usb_gadget_unregister_driver(&zero_driver);
+ usb_composite_unregister(&zero_driver);
}
module_exit(cleanup);
-
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 228797e54f9c..515044129bb9 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -305,3 +305,29 @@ config SUPERH_ON_CHIP_R8A66597
help
This driver enables support for the on-chip R8A66597 in the
SH7366 and SH7723 processors.
+
+config USB_WHCI_HCD
+ tristate "Wireless USB Host Controller Interface (WHCI) driver"
+ depends on PCI && USB
+ select USB_WUSB
+ select UWB_WHCI
+ help
+ A driver for PCI-based Wireless USB Host Controllers that are
+ compliant with the WHCI specification.
+
+ To compile this driver a module, choose M here: the module
+ will be called "whci-hcd".
+
+config USB_HWA_HCD
+ tristate "Host Wire Adapter (HWA) driver"
+ depends on USB
+ select USB_WUSB
+ select UWB_HWA
+ help
+ This driver enables you to connect Wireless USB devices to
+ your system using a Host Wire Adaptor USB dongle. This is an
+ UWB Radio Controller and WUSB Host Controller connected to
+ your machine via USB (specified in WUSB1.0).
+
+ To compile this driver a module, choose M here: the module
+ will be called "hwa-hc".
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index f1edda2dcfde..23be22224044 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -8,6 +8,8 @@ endif
isp1760-objs := isp1760-hcd.o isp1760-if.o
+obj-$(CONFIG_USB_WHCI_HCD) += whci/
+
obj-$(CONFIG_PCI) += pci-quirks.o
obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
@@ -19,3 +21,4 @@ obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
obj-$(CONFIG_USB_ISP1760_HCD) += isp1760.o
+obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 0f82fdcaef09..b0f8ed5a7fb9 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -676,7 +676,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
"%s\n"
"SUSPENDED (no register access)\n",
hcd->self.controller->bus->name,
- hcd->self.controller->bus_id,
+ dev_name(hcd->self.controller),
hcd->product_desc);
goto done;
}
@@ -688,7 +688,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
"%s\n"
"EHCI %x.%02x, hcd state %d\n",
hcd->self.controller->bus->name,
- hcd->self.controller->bus_id,
+ dev_name(hcd->self.controller),
hcd->product_desc,
i >> 8, i & 0x0ff, hcd->state);
size -= temp;
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index 7370d6187c64..f710a2d3423a 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -56,7 +56,7 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver,
pdata = (struct fsl_usb2_platform_data *)pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev,
- "No platform data for %s.\n", pdev->dev.bus_id);
+ "No platform data for %s.\n", dev_name(&pdev->dev));
return -ENODEV;
}
@@ -69,7 +69,7 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver,
(pdata->operating_mode == FSL_USB2_DR_OTG))) {
dev_err(&pdev->dev,
"Non Host Mode configured for %s. Wrong driver linked.\n",
- pdev->dev.bus_id);
+ dev_name(&pdev->dev));
return -ENODEV;
}
@@ -77,12 +77,12 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver,
if (!res) {
dev_err(&pdev->dev,
"Found HC with no IRQ. Check %s setup!\n",
- pdev->dev.bus_id);
+ dev_name(&pdev->dev));
return -ENODEV;
}
irq = res->start;
- hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id);
+ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
retval = -ENOMEM;
goto err1;
@@ -92,7 +92,7 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver,
if (!res) {
dev_err(&pdev->dev,
"Found HC with no register addr. Check %s setup!\n",
- pdev->dev.bus_id);
+ dev_name(&pdev->dev));
retval = -ENODEV;
goto err2;
}
@@ -132,7 +132,7 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver,
err2:
usb_put_hcd(hcd);
err1:
- dev_err(&pdev->dev, "init %s fail, %d\n", pdev->dev.bus_id, retval);
+ dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), retval);
return retval;
}
diff --git a/drivers/usb/host/ehci-ixp4xx.c b/drivers/usb/host/ehci-ixp4xx.c
index 9d042f220097..f9575c409124 100644
--- a/drivers/usb/host/ehci-ixp4xx.c
+++ b/drivers/usb/host/ehci-ixp4xx.c
@@ -77,12 +77,12 @@ static int ixp4xx_ehci_probe(struct platform_device *pdev)
if (!res) {
dev_err(&pdev->dev,
"Found HC with no IRQ. Check %s setup!\n",
- pdev->dev.bus_id);
+ dev_name(&pdev->dev));
return -ENODEV;
}
irq = res->start;
- hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id);
+ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
retval = -ENOMEM;
goto fail_create_hcd;
@@ -92,7 +92,7 @@ static int ixp4xx_ehci_probe(struct platform_device *pdev)
if (!res) {
dev_err(&pdev->dev,
"Found HC with no register addr. Check %s setup!\n",
- pdev->dev.bus_id);
+ dev_name(&pdev->dev));
retval = -ENODEV;
goto fail_request_resource;
}
@@ -126,7 +126,7 @@ fail_ioremap:
fail_request_resource:
usb_put_hcd(hcd);
fail_create_hcd:
- dev_err(&pdev->dev, "init %s fail, %d\n", pdev->dev.bus_id, retval);
+ dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), retval);
return retval;
}
diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c
index ab625f0ba1d9..5fbdc14e63b3 100644
--- a/drivers/usb/host/ehci-orion.c
+++ b/drivers/usb/host/ehci-orion.c
@@ -204,7 +204,7 @@ static int __init ehci_orion_drv_probe(struct platform_device *pdev)
if (irq <= 0) {
dev_err(&pdev->dev,
"Found HC with no IRQ. Check %s setup!\n",
- pdev->dev.bus_id);
+ dev_name(&pdev->dev));
err = -ENODEV;
goto err1;
}
@@ -213,7 +213,7 @@ static int __init ehci_orion_drv_probe(struct platform_device *pdev)
if (!res) {
dev_err(&pdev->dev,
"Found HC with no register addr. Check %s setup!\n",
- pdev->dev.bus_id);
+ dev_name(&pdev->dev));
err = -ENODEV;
goto err1;
}
@@ -233,7 +233,7 @@ static int __init ehci_orion_drv_probe(struct platform_device *pdev)
}
hcd = usb_create_hcd(&ehci_orion_hc_driver,
- &pdev->dev, pdev->dev.bus_id);
+ &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
err = -ENOMEM;
goto err3;
@@ -276,7 +276,7 @@ err2:
release_mem_region(res->start, res->end - res->start + 1);
err1:
dev_err(&pdev->dev, "init %s fail, %d\n",
- pdev->dev.bus_id, err);
+ dev_name(&pdev->dev), err);
return err;
}
diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c
index 37e6abeb794c..0eba894bcb01 100644
--- a/drivers/usb/host/ehci-ps3.c
+++ b/drivers/usb/host/ehci-ps3.c
@@ -128,7 +128,7 @@ static int ps3_ehci_probe(struct ps3_system_bus_device *dev)
dev->core.dma_mask = &dummy_mask; /* FIXME: for improper usb code */
- hcd = usb_create_hcd(&ps3_ehci_hc_driver, &dev->core, dev->core.bus_id);
+ hcd = usb_create_hcd(&ps3_ehci_hc_driver, &dev->core, dev_name(&dev->core));
if (!hcd) {
dev_dbg(&dev->core, "%s:%d: usb_create_hcd failed\n", __func__,
diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c
new file mode 100644
index 000000000000..a811c482b949
--- /dev/null
+++ b/drivers/usb/host/hwa-hc.c
@@ -0,0 +1,935 @@
+/*
+ * Host Wire Adapter:
+ * Driver glue, HWA-specific functions, bridges to WAHC and WUSBHC
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * The HWA driver is a simple layer that forwards requests to the WAHC
+ * (Wire Adater Host Controller) or WUSBHC (Wireless USB Host
+ * Controller) layers.
+ *
+ * Host Wire Adapter is the 'WUSB 1.0 standard' name for Wireless-USB
+ * Host Controller that is connected to your system via USB (a USB
+ * dongle that implements a USB host...). There is also a Device Wired
+ * Adaptor, DWA (Wireless USB hub) that uses the same mechanism for
+ * transferring data (it is after all a USB host connected via
+ * Wireless USB), we have a common layer called Wire Adapter Host
+ * Controller that does all the hard work. The WUSBHC (Wireless USB
+ * Host Controller) is the part common to WUSB Host Controllers, the
+ * HWA and the PCI-based one, that is implemented following the WHCI
+ * spec. All these layers are implemented in ../wusbcore.
+ *
+ * The main functions are hwahc_op_urb_{en,de}queue(), that pass the
+ * job of converting a URB to a Wire Adapter
+ *
+ * Entry points:
+ *
+ * hwahc_driver_*() Driver initialization, registration and
+ * teardown.
+ *
+ * hwahc_probe() New device came up, create an instance for
+ * it [from device enumeration].
+ *
+ * hwahc_disconnect() Remove device instance [from device
+ * enumeration].
+ *
+ * [__]hwahc_op_*() Host-Wire-Adaptor specific functions for
+ * starting/stopping/etc (some might be made also
+ * DWA).
+ */
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/wait.h>
+#include <linux/completion.h>
+#include "../wusbcore/wa-hc.h"
+#include "../wusbcore/wusbhc.h"
+
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+struct hwahc {
+ struct wusbhc wusbhc; /* has to be 1st */
+ struct wahc wa;
+ u8 buffer[16]; /* for misc usb transactions */
+};
+
+/**
+ * FIXME should be wusbhc
+ *
+ * NOTE: we need to cache the Cluster ID because later...there is no
+ * way to get it :)
+ */
+static int __hwahc_set_cluster_id(struct hwahc *hwahc, u8 cluster_id)
+{
+ int result;
+ struct wusbhc *wusbhc = &hwahc->wusbhc;
+ struct wahc *wa = &hwahc->wa;
+ struct device *dev = &wa->usb_iface->dev;
+
+ result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ WUSB_REQ_SET_CLUSTER_ID,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ cluster_id,
+ wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+ NULL, 0, 1000 /* FIXME: arbitrary */);
+ if (result < 0)
+ dev_err(dev, "Cannot set WUSB Cluster ID to 0x%02x: %d\n",
+ cluster_id, result);
+ else
+ wusbhc->cluster_id = cluster_id;
+ dev_info(dev, "Wireless USB Cluster ID set to 0x%02x\n", cluster_id);
+ return result;
+}
+
+static int __hwahc_op_set_num_dnts(struct wusbhc *wusbhc, u8 interval, u8 slots)
+{
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct wahc *wa = &hwahc->wa;
+
+ return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ WUSB_REQ_SET_NUM_DNTS,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ interval << 8 | slots,
+ wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+ NULL, 0, 1000 /* FIXME: arbitrary */);
+}
+
+/*
+ * Reset a WUSB host controller and wait for it to complete doing it.
+ *
+ * @usb_hcd: Pointer to WUSB Host Controller instance.
+ *
+ */
+static int hwahc_op_reset(struct usb_hcd *usb_hcd)
+{
+ int result;
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct device *dev = &hwahc->wa.usb_iface->dev;
+
+ d_fnstart(4, dev, "(hwahc %p)\n", hwahc);
+ mutex_lock(&wusbhc->mutex);
+ wa_nep_disarm(&hwahc->wa);
+ result = __wa_set_feature(&hwahc->wa, WA_RESET);
+ if (result < 0) {
+ dev_err(dev, "error commanding HC to reset: %d\n", result);
+ goto error_unlock;
+ }
+ d_printf(3, dev, "reset: waiting for device to change state\n");
+ result = __wa_wait_status(&hwahc->wa, WA_STATUS_RESETTING, 0);
+ if (result < 0) {
+ dev_err(dev, "error waiting for HC to reset: %d\n", result);
+ goto error_unlock;
+ }
+error_unlock:
+ mutex_unlock(&wusbhc->mutex);
+ d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result);
+ return result;
+}
+
+/*
+ * FIXME: break this function up
+ */
+static int hwahc_op_start(struct usb_hcd *usb_hcd)
+{
+ u8 addr;
+ int result;
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct device *dev = &hwahc->wa.usb_iface->dev;
+
+ /* Set up a Host Info WUSB Information Element */
+ d_fnstart(4, dev, "(hwahc %p)\n", hwahc);
+ result = -ENOSPC;
+ mutex_lock(&wusbhc->mutex);
+ /* Start the numbering from the top so that the bottom
+ * range of the unauth addr space is used for devices,
+ * the top for HCs; use 0xfe - RC# */
+ addr = wusb_cluster_id_get();
+ if (addr == 0)
+ goto error_cluster_id_get;
+ result = __hwahc_set_cluster_id(hwahc, addr);
+ if (result < 0)
+ goto error_set_cluster_id;
+
+ result = wa_nep_arm(&hwahc->wa, GFP_KERNEL);
+ if (result < 0) {
+ dev_err(dev, "cannot listen to notifications: %d\n", result);
+ goto error_stop;
+ }
+ usb_hcd->uses_new_polling = 1;
+ usb_hcd->poll_rh = 1;
+ usb_hcd->state = HC_STATE_RUNNING;
+ result = 0;
+out:
+ mutex_unlock(&wusbhc->mutex);
+ d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result);
+ return result;
+
+error_stop:
+ __wa_stop(&hwahc->wa);
+error_set_cluster_id:
+ wusb_cluster_id_put(wusbhc->cluster_id);
+error_cluster_id_get:
+ goto out;
+
+}
+
+/*
+ * FIXME: break this function up
+ */
+static int __hwahc_op_wusbhc_start(struct wusbhc *wusbhc)
+{
+ int result;
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct device *dev = &hwahc->wa.usb_iface->dev;
+
+ /* Set up a Host Info WUSB Information Element */
+ d_fnstart(4, dev, "(hwahc %p)\n", hwahc);
+ result = -ENOSPC;
+
+ result = __wa_set_feature(&hwahc->wa, WA_ENABLE);
+ if (result < 0) {
+ dev_err(dev, "error commanding HC to start: %d\n", result);
+ goto error_stop;
+ }
+ result = __wa_wait_status(&hwahc->wa, WA_ENABLE, WA_ENABLE);
+ if (result < 0) {
+ dev_err(dev, "error waiting for HC to start: %d\n", result);
+ goto error_stop;
+ }
+ result = 0;
+out:
+ d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result);
+ return result;
+
+error_stop:
+ result = __wa_clear_feature(&hwahc->wa, WA_ENABLE);
+ goto out;
+}
+
+static int hwahc_op_suspend(struct usb_hcd *usb_hcd, pm_message_t msg)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ dev_err(wusbhc->dev, "%s (%p [%p], 0x%lx) UNIMPLEMENTED\n", __func__,
+ usb_hcd, hwahc, *(unsigned long *) &msg);
+ return -ENOSYS;
+}
+
+static int hwahc_op_resume(struct usb_hcd *usb_hcd)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+
+ dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__,
+ usb_hcd, hwahc);
+ return -ENOSYS;
+}
+
+static void __hwahc_op_wusbhc_stop(struct wusbhc *wusbhc)
+{
+ int result;
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct device *dev = &hwahc->wa.usb_iface->dev;
+
+ d_fnstart(4, dev, "(hwahc %p)\n", hwahc);
+ /* Nothing for now */
+ d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result);
+ return;
+}
+
+/*
+ * No need to abort pipes, as when this is called, all the children
+ * has been disconnected and that has done it [through
+ * usb_disable_interface() -> usb_disable_endpoint() ->
+ * hwahc_op_ep_disable() - >rpipe_ep_disable()].
+ */
+static void hwahc_op_stop(struct usb_hcd *usb_hcd)
+{
+ int result;
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct wahc *wa = &hwahc->wa;
+ struct device *dev = &wa->usb_iface->dev;
+
+ d_fnstart(4, dev, "(hwahc %p)\n", hwahc);
+ mutex_lock(&wusbhc->mutex);
+ wusbhc_stop(wusbhc);
+ wa_nep_disarm(&hwahc->wa);
+ result = __wa_stop(&hwahc->wa);
+ wusb_cluster_id_put(wusbhc->cluster_id);
+ mutex_unlock(&wusbhc->mutex);
+ d_fnend(4, dev, "(hwahc %p) = %d\n", hwahc, result);
+ return;
+}
+
+static int hwahc_op_get_frame_number(struct usb_hcd *usb_hcd)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+
+ dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__,
+ usb_hcd, hwahc);
+ return -ENOSYS;
+}
+
+static int hwahc_op_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb,
+ gfp_t gfp)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+
+ return wa_urb_enqueue(&hwahc->wa, urb->ep, urb, gfp);
+}
+
+static int hwahc_op_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb,
+ int status)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+
+ return wa_urb_dequeue(&hwahc->wa, urb);
+}
+
+/*
+ * Release resources allocated for an endpoint
+ *
+ * If there is an associated rpipe to this endpoint, go ahead and put it.
+ */
+static void hwahc_op_endpoint_disable(struct usb_hcd *usb_hcd,
+ struct usb_host_endpoint *ep)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+
+ rpipe_ep_disable(&hwahc->wa, ep);
+}
+
+/*
+ * Set the UWB MAS allocation for the WUSB cluster
+ *
+ * @stream_index: stream to use (-1 for cancelling the allocation)
+ * @mas: mas bitmap to use
+ */
+static int __hwahc_op_bwa_set(struct wusbhc *wusbhc, s8 stream_index,
+ const struct uwb_mas_bm *mas)
+{
+ int result;
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct wahc *wa = &hwahc->wa;
+ struct device *dev = &wa->usb_iface->dev;
+ u8 mas_le[UWB_NUM_MAS/8];
+
+ /* Set the stream index */
+ result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ WUSB_REQ_SET_STREAM_IDX,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ stream_index,
+ wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+ NULL, 0, 1000 /* FIXME: arbitrary */);
+ if (result < 0) {
+ dev_err(dev, "Cannot set WUSB stream index: %d\n", result);
+ goto out;
+ }
+ uwb_mas_bm_copy_le(mas_le, mas);
+ /* Set the MAS allocation */
+ result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ WUSB_REQ_SET_WUSB_MAS,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+ mas_le, 32, 1000 /* FIXME: arbitrary */);
+ if (result < 0)
+ dev_err(dev, "Cannot set WUSB MAS allocation: %d\n", result);
+out:
+ return result;
+}
+
+/*
+ * Add an IE to the host's MMC
+ *
+ * @interval: See WUSB1.0[8.5.3.1]
+ * @repeat_cnt: See WUSB1.0[8.5.3.1]
+ * @handle: See WUSB1.0[8.5.3.1]
+ * @wuie: Pointer to the header of the WUSB IE data to add.
+ * MUST BE allocated in a kmalloc buffer (no stack or
+ * vmalloc).
+ *
+ * NOTE: the format of the WUSB IEs for MMCs are different to the
+ * normal MBOA MAC IEs (IE Id + Length in MBOA MAC vs. Length +
+ * Id in WUSB IEs). Standards...you gotta love'em.
+ */
+static int __hwahc_op_mmcie_add(struct wusbhc *wusbhc, u8 interval,
+ u8 repeat_cnt, u8 handle,
+ struct wuie_hdr *wuie)
+{
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct wahc *wa = &hwahc->wa;
+ u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
+
+ return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ WUSB_REQ_ADD_MMC_IE,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ interval << 8 | repeat_cnt,
+ handle << 8 | iface_no,
+ wuie, wuie->bLength, 1000 /* FIXME: arbitrary */);
+}
+
+/*
+ * Remove an IE to the host's MMC
+ *
+ * @handle: See WUSB1.0[8.5.3.1]
+ */
+static int __hwahc_op_mmcie_rm(struct wusbhc *wusbhc, u8 handle)
+{
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct wahc *wa = &hwahc->wa;
+ u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
+ return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ WUSB_REQ_REMOVE_MMC_IE,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, handle << 8 | iface_no,
+ NULL, 0, 1000 /* FIXME: arbitrary */);
+}
+
+/*
+ * Update device information for a given fake port
+ *
+ * @port_idx: Fake port to which device is connected (wusbhc index, not
+ * USB port number).
+ */
+static int __hwahc_op_dev_info_set(struct wusbhc *wusbhc,
+ struct wusb_dev *wusb_dev)
+{
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct wahc *wa = &hwahc->wa;
+ u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
+ struct hwa_dev_info *dev_info;
+ int ret;
+
+ /* fill out the Device Info buffer and send it */
+ dev_info = kzalloc(sizeof(struct hwa_dev_info), GFP_KERNEL);
+ if (!dev_info)
+ return -ENOMEM;
+ uwb_mas_bm_copy_le(dev_info->bmDeviceAvailability,
+ &wusb_dev->availability);
+ dev_info->bDeviceAddress = wusb_dev->addr;
+
+ /*
+ * If the descriptors haven't been read yet, use a default PHY
+ * rate of 53.3 Mbit/s only. The correct value will be used
+ * when this will be called again as part of the
+ * authentication process (which occurs after the descriptors
+ * have been read).
+ */
+ if (wusb_dev->wusb_cap_descr)
+ dev_info->wPHYRates = wusb_dev->wusb_cap_descr->wPHYRates;
+ else
+ dev_info->wPHYRates = cpu_to_le16(USB_WIRELESS_PHY_53);
+
+ ret = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ WUSB_REQ_SET_DEV_INFO,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, wusb_dev->port_idx << 8 | iface_no,
+ dev_info, sizeof(struct hwa_dev_info),
+ 1000 /* FIXME: arbitrary */);
+ kfree(dev_info);
+ return ret;
+}
+
+/*
+ * Set host's idea of which encryption (and key) method to use when
+ * talking to ad evice on a given port.
+ *
+ * If key is NULL, it means disable encryption for that "virtual port"
+ * (used when we disconnect).
+ */
+static int __hwahc_dev_set_key(struct wusbhc *wusbhc, u8 port_idx, u32 tkid,
+ const void *key, size_t key_size,
+ union wusb_key_index key_idx)
+{
+ int result = -ENOMEM;
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct wahc *wa = &hwahc->wa;
+ u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
+ struct usb_key_descriptor *keyd;
+ size_t keyd_len;
+
+ keyd_len = sizeof(*keyd) + key_size;
+ keyd = kzalloc(keyd_len, GFP_KERNEL);
+ if (keyd == NULL)
+ return -ENOMEM;
+
+ keyd->bLength = keyd_len;
+ keyd->bDescriptorType = USB_DT_KEY;
+ keyd->tTKID[0] = (tkid >> 0) & 0xff;
+ keyd->tTKID[1] = (tkid >> 8) & 0xff;
+ keyd->tTKID[2] = (tkid >> 16) & 0xff;
+ memcpy(keyd->bKeyData, key, key_size);
+
+ result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ USB_REQ_SET_DESCRIPTOR,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ USB_DT_KEY << 8 | key_idx.value,
+ port_idx << 8 | iface_no,
+ keyd, keyd_len, 1000 /* FIXME: arbitrary */);
+
+ memset(keyd, 0, sizeof(*keyd)); /* clear keys etc. */
+ kfree(keyd);
+ return result;
+}
+
+/*
+ * Set host's idea of which encryption (and key) method to use when
+ * talking to ad evice on a given port.
+ *
+ * If key is NULL, it means disable encryption for that "virtual port"
+ * (used when we disconnect).
+ */
+static int __hwahc_op_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid,
+ const void *key, size_t key_size)
+{
+ int result = -ENOMEM;
+ struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ struct wahc *wa = &hwahc->wa;
+ u8 iface_no = wa->usb_iface->cur_altsetting->desc.bInterfaceNumber;
+ u8 encryption_value;
+
+ /* Tell the host which key to use to talk to the device */
+ if (key) {
+ union wusb_key_index key_idx = { .value = 0, };
+
+ key_idx.index = 0;
+ key_idx.type = WUSB_KEY_INDEX_TYPE_PTK;
+ key_idx.originator = WUSB_KEY_INDEX_ORIGINATOR_HOST;
+
+ result = __hwahc_dev_set_key(wusbhc, port_idx, tkid,
+ key, key_size, key_idx);
+ if (result < 0)
+ goto error_set_key;
+ encryption_value = wusbhc->ccm1_etd->bEncryptionValue;
+ } else {
+ /* FIXME: this should come from wusbhc->etd[UNSECURE].value */
+ encryption_value = 0;
+ }
+
+ /* Set the encryption type for commmunicating with the device */
+ result = usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ USB_REQ_SET_ENCRYPTION,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ encryption_value, port_idx << 8 | iface_no,
+ NULL, 0, 1000 /* FIXME: arbitrary */);
+ if (result < 0)
+ dev_err(wusbhc->dev, "Can't set host's WUSB encryption for "
+ "port index %u to %s (value %d): %d\n", port_idx,
+ wusb_et_name(wusbhc->ccm1_etd->bEncryptionType),
+ wusbhc->ccm1_etd->bEncryptionValue, result);
+error_set_key:
+ return result;
+}
+
+/*
+ * Set host's GTK key
+ */
+static int __hwahc_op_set_gtk(struct wusbhc *wusbhc, u32 tkid,
+ const void *key, size_t key_size)
+{
+ union wusb_key_index key_idx = { .value = 0, };
+ int result;
+
+ key_idx.index = 0;
+ key_idx.type = WUSB_KEY_INDEX_TYPE_GTK;
+ key_idx.originator = WUSB_KEY_INDEX_ORIGINATOR_HOST;
+
+ result = __hwahc_dev_set_key(wusbhc, 0, tkid,
+ key, key_size, key_idx);
+
+ return result;
+}
+
+/*
+ * Get the Wire Adapter class-specific descriptor
+ *
+ * NOTE: this descriptor comes with the big bundled configuration
+ * descriptor that includes the interfaces' and endpoints', so
+ * we just look for it in the cached copy kept by the USB stack.
+ *
+ * NOTE2: We convert LE fields to CPU order.
+ */
+static int wa_fill_descr(struct wahc *wa)
+{
+ int result;
+ struct device *dev = &wa->usb_iface->dev;
+ char *itr;
+ struct usb_device *usb_dev = wa->usb_dev;
+ struct usb_descriptor_header *hdr;
+ struct usb_wa_descriptor *wa_descr;
+ size_t itr_size, actconfig_idx;
+
+ actconfig_idx = (usb_dev->actconfig - usb_dev->config) /
+ sizeof(usb_dev->config[0]);
+ itr = usb_dev->rawdescriptors[actconfig_idx];
+ itr_size = le16_to_cpu(usb_dev->actconfig->desc.wTotalLength);
+ while (itr_size >= sizeof(*hdr)) {
+ hdr = (struct usb_descriptor_header *) itr;
+ d_printf(3, dev, "Extra device descriptor: "
+ "type %02x/%u bytes @ %zu (%zu left)\n",
+ hdr->bDescriptorType, hdr->bLength,
+ (itr - usb_dev->rawdescriptors[actconfig_idx]),
+ itr_size);
+ if (hdr->bDescriptorType == USB_DT_WIRE_ADAPTER)
+ goto found;
+ itr += hdr->bLength;
+ itr_size -= hdr->bLength;
+ }
+ dev_err(dev, "cannot find Wire Adapter Class descriptor\n");
+ return -ENODEV;
+
+found:
+ result = -EINVAL;
+ if (hdr->bLength > itr_size) { /* is it available? */
+ dev_err(dev, "incomplete Wire Adapter Class descriptor "
+ "(%zu bytes left, %u needed)\n",
+ itr_size, hdr->bLength);
+ goto error;
+ }
+ if (hdr->bLength < sizeof(*wa->wa_descr)) {
+ dev_err(dev, "short Wire Adapter Class descriptor\n");
+ goto error;
+ }
+ wa->wa_descr = wa_descr = (struct usb_wa_descriptor *) hdr;
+ /* Make LE fields CPU order */
+ wa_descr->bcdWAVersion = le16_to_cpu(wa_descr->bcdWAVersion);
+ wa_descr->wNumRPipes = le16_to_cpu(wa_descr->wNumRPipes);
+ wa_descr->wRPipeMaxBlock = le16_to_cpu(wa_descr->wRPipeMaxBlock);
+ if (wa_descr->bcdWAVersion > 0x0100)
+ dev_warn(dev, "Wire Adapter v%d.%d newer than groked v1.0\n",
+ wa_descr->bcdWAVersion & 0xff00 >> 8,
+ wa_descr->bcdWAVersion & 0x00ff);
+ result = 0;
+error:
+ return result;
+}
+
+static struct hc_driver hwahc_hc_driver = {
+ .description = "hwa-hcd",
+ .product_desc = "Wireless USB HWA host controller",
+ .hcd_priv_size = sizeof(struct hwahc) - sizeof(struct usb_hcd),
+ .irq = NULL, /* FIXME */
+ .flags = HCD_USB2, /* FIXME */
+ .reset = hwahc_op_reset,
+ .start = hwahc_op_start,
+ .pci_suspend = hwahc_op_suspend,
+ .pci_resume = hwahc_op_resume,
+ .stop = hwahc_op_stop,
+ .get_frame_number = hwahc_op_get_frame_number,
+ .urb_enqueue = hwahc_op_urb_enqueue,
+ .urb_dequeue = hwahc_op_urb_dequeue,
+ .endpoint_disable = hwahc_op_endpoint_disable,
+
+ .hub_status_data = wusbhc_rh_status_data,
+ .hub_control = wusbhc_rh_control,
+ .bus_suspend = wusbhc_rh_suspend,
+ .bus_resume = wusbhc_rh_resume,
+ .start_port_reset = wusbhc_rh_start_port_reset,
+};
+
+static int hwahc_security_create(struct hwahc *hwahc)
+{
+ int result;
+ struct wusbhc *wusbhc = &hwahc->wusbhc;
+ struct usb_device *usb_dev = hwahc->wa.usb_dev;
+ struct device *dev = &usb_dev->dev;
+ struct usb_security_descriptor *secd;
+ struct usb_encryption_descriptor *etd;
+ void *itr, *top;
+ size_t itr_size, needed, bytes;
+ u8 index;
+ char buf[64];
+
+ /* Find the host's security descriptors in the config descr bundle */
+ index = (usb_dev->actconfig - usb_dev->config) /
+ sizeof(usb_dev->config[0]);
+ itr = usb_dev->rawdescriptors[index];
+ itr_size = le16_to_cpu(usb_dev->actconfig->desc.wTotalLength);
+ top = itr + itr_size;
+ result = __usb_get_extra_descriptor(usb_dev->rawdescriptors[index],
+ le16_to_cpu(usb_dev->actconfig->desc.wTotalLength),
+ USB_DT_SECURITY, (void **) &secd);
+ if (result == -1) {
+ dev_warn(dev, "BUG? WUSB host has no security descriptors\n");
+ return 0;
+ }
+ needed = sizeof(*secd);
+ if (top - (void *)secd < needed) {
+ dev_err(dev, "BUG? Not enough data to process security "
+ "descriptor header (%zu bytes left vs %zu needed)\n",
+ top - (void *) secd, needed);
+ return 0;
+ }
+ needed = le16_to_cpu(secd->wTotalLength);
+ if (top - (void *)secd < needed) {
+ dev_err(dev, "BUG? Not enough data to process security "
+ "descriptors (%zu bytes left vs %zu needed)\n",
+ top - (void *) secd, needed);
+ return 0;
+ }
+ /* Walk over the sec descriptors and store CCM1's on wusbhc */
+ itr = (void *) secd + sizeof(*secd);
+ top = (void *) secd + le16_to_cpu(secd->wTotalLength);
+ index = 0;
+ bytes = 0;
+ while (itr < top) {
+ etd = itr;
+ if (top - itr < sizeof(*etd)) {
+ dev_err(dev, "BUG: bad host security descriptor; "
+ "not enough data (%zu vs %zu left)\n",
+ top - itr, sizeof(*etd));
+ break;
+ }
+ if (etd->bLength < sizeof(*etd)) {
+ dev_err(dev, "BUG: bad host encryption descriptor; "
+ "descriptor is too short "
+ "(%zu vs %zu needed)\n",
+ (size_t)etd->bLength, sizeof(*etd));
+ break;
+ }
+ itr += etd->bLength;
+ bytes += snprintf(buf + bytes, sizeof(buf) - bytes,
+ "%s (0x%02x) ",
+ wusb_et_name(etd->bEncryptionType),
+ etd->bEncryptionValue);
+ wusbhc->ccm1_etd = etd;
+ }
+ dev_info(dev, "supported encryption types: %s\n", buf);
+ if (wusbhc->ccm1_etd == NULL) {
+ dev_err(dev, "E: host doesn't support CCM-1 crypto\n");
+ return 0;
+ }
+ /* Pretty print what we support */
+ return 0;
+}
+
+static void hwahc_security_release(struct hwahc *hwahc)
+{
+ /* nothing to do here so far... */
+}
+
+static int hwahc_create(struct hwahc *hwahc, struct usb_interface *iface)
+{
+ int result;
+ struct device *dev = &iface->dev;
+ struct wusbhc *wusbhc = &hwahc->wusbhc;
+ struct wahc *wa = &hwahc->wa;
+ struct usb_device *usb_dev = interface_to_usbdev(iface);
+
+ wa->usb_dev = usb_get_dev(usb_dev); /* bind the USB device */
+ wa->usb_iface = usb_get_intf(iface);
+ wusbhc->dev = dev;
+ wusbhc->uwb_rc = uwb_rc_get_by_grandpa(iface->dev.parent);
+ if (wusbhc->uwb_rc == NULL) {
+ result = -ENODEV;
+ dev_err(dev, "Cannot get associated UWB Host Controller\n");
+ goto error_rc_get;
+ }
+ result = wa_fill_descr(wa); /* Get the device descriptor */
+ if (result < 0)
+ goto error_fill_descriptor;
+ if (wa->wa_descr->bNumPorts > USB_MAXCHILDREN) {
+ dev_err(dev, "FIXME: USB_MAXCHILDREN too low for WUSB "
+ "adapter (%u ports)\n", wa->wa_descr->bNumPorts);
+ wusbhc->ports_max = USB_MAXCHILDREN;
+ } else {
+ wusbhc->ports_max = wa->wa_descr->bNumPorts;
+ }
+ wusbhc->mmcies_max = wa->wa_descr->bNumMMCIEs;
+ wusbhc->start = __hwahc_op_wusbhc_start;
+ wusbhc->stop = __hwahc_op_wusbhc_stop;
+ wusbhc->mmcie_add = __hwahc_op_mmcie_add;
+ wusbhc->mmcie_rm = __hwahc_op_mmcie_rm;
+ wusbhc->dev_info_set = __hwahc_op_dev_info_set;
+ wusbhc->bwa_set = __hwahc_op_bwa_set;
+ wusbhc->set_num_dnts = __hwahc_op_set_num_dnts;
+ wusbhc->set_ptk = __hwahc_op_set_ptk;
+ wusbhc->set_gtk = __hwahc_op_set_gtk;
+ result = hwahc_security_create(hwahc);
+ if (result < 0) {
+ dev_err(dev, "Can't initialize security: %d\n", result);
+ goto error_security_create;
+ }
+ wa->wusb = wusbhc; /* FIXME: ugly, need to fix */
+ result = wusbhc_create(&hwahc->wusbhc);
+ if (result < 0) {
+ dev_err(dev, "Can't create WUSB HC structures: %d\n", result);
+ goto error_wusbhc_create;
+ }
+ result = wa_create(&hwahc->wa, iface);
+ if (result < 0)
+ goto error_wa_create;
+ return 0;
+
+error_wa_create:
+ wusbhc_destroy(&hwahc->wusbhc);
+error_wusbhc_create:
+ /* WA Descr fill allocs no resources */
+error_security_create:
+error_fill_descriptor:
+ uwb_rc_put(wusbhc->uwb_rc);
+error_rc_get:
+ usb_put_intf(iface);
+ usb_put_dev(usb_dev);
+ return result;
+}
+
+static void hwahc_destroy(struct hwahc *hwahc)
+{
+ struct wusbhc *wusbhc = &hwahc->wusbhc;
+
+ d_fnstart(1, NULL, "(hwahc %p)\n", hwahc);
+ mutex_lock(&wusbhc->mutex);
+ __wa_destroy(&hwahc->wa);
+ wusbhc_destroy(&hwahc->wusbhc);
+ hwahc_security_release(hwahc);
+ hwahc->wusbhc.dev = NULL;
+ uwb_rc_put(wusbhc->uwb_rc);
+ usb_put_intf(hwahc->wa.usb_iface);
+ usb_put_dev(hwahc->wa.usb_dev);
+ mutex_unlock(&wusbhc->mutex);
+ d_fnend(1, NULL, "(hwahc %p) = void\n", hwahc);
+}
+
+static void hwahc_init(struct hwahc *hwahc)
+{
+ wa_init(&hwahc->wa);
+}
+
+static int hwahc_probe(struct usb_interface *usb_iface,
+ const struct usb_device_id *id)
+{
+ int result;
+ struct usb_hcd *usb_hcd;
+ struct wusbhc *wusbhc;
+ struct hwahc *hwahc;
+ struct device *dev = &usb_iface->dev;
+
+ d_fnstart(4, dev, "(%p, %p)\n", usb_iface, id);
+ result = -ENOMEM;
+ usb_hcd = usb_create_hcd(&hwahc_hc_driver, &usb_iface->dev, "wusb-hwa");
+ if (usb_hcd == NULL) {
+ dev_err(dev, "unable to allocate instance\n");
+ goto error_alloc;
+ }
+ usb_hcd->wireless = 1;
+ usb_hcd->flags |= HCD_FLAG_SAW_IRQ;
+ wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+ hwahc_init(hwahc);
+ result = hwahc_create(hwahc, usb_iface);
+ if (result < 0) {
+ dev_err(dev, "Cannot initialize internals: %d\n", result);
+ goto error_hwahc_create;
+ }
+ result = usb_add_hcd(usb_hcd, 0, 0);
+ if (result < 0) {
+ dev_err(dev, "Cannot add HCD: %d\n", result);
+ goto error_add_hcd;
+ }
+ result = wusbhc_b_create(&hwahc->wusbhc);
+ if (result < 0) {
+ dev_err(dev, "Cannot setup phase B of WUSBHC: %d\n", result);
+ goto error_wusbhc_b_create;
+ }
+ d_fnend(4, dev, "(%p, %p) = 0\n", usb_iface, id);
+ return 0;
+
+error_wusbhc_b_create:
+ usb_remove_hcd(usb_hcd);
+error_add_hcd:
+ hwahc_destroy(hwahc);
+error_hwahc_create:
+ usb_put_hcd(usb_hcd);
+error_alloc:
+ d_fnend(4, dev, "(%p, %p) = %d\n", usb_iface, id, result);
+ return result;
+}
+
+static void hwahc_disconnect(struct usb_interface *usb_iface)
+{
+ struct usb_hcd *usb_hcd;
+ struct wusbhc *wusbhc;
+ struct hwahc *hwahc;
+
+ usb_hcd = usb_get_intfdata(usb_iface);
+ wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+
+ d_fnstart(1, NULL, "(hwahc %p [usb_iface %p])\n", hwahc, usb_iface);
+ wusbhc_b_destroy(&hwahc->wusbhc);
+ usb_remove_hcd(usb_hcd);
+ hwahc_destroy(hwahc);
+ usb_put_hcd(usb_hcd);
+ d_fnend(1, NULL, "(hwahc %p [usb_iface %p]) = void\n", hwahc,
+ usb_iface);
+}
+
+/** USB device ID's that we handle */
+static struct usb_device_id hwahc_id_table[] = {
+ /* FIXME: use class labels for this */
+ { USB_INTERFACE_INFO(0xe0, 0x02, 0x01), },
+ {},
+};
+MODULE_DEVICE_TABLE(usb, hwahc_id_table);
+
+static struct usb_driver hwahc_driver = {
+ .name = "hwa-hc",
+ .probe = hwahc_probe,
+ .disconnect = hwahc_disconnect,
+ .id_table = hwahc_id_table,
+};
+
+static int __init hwahc_driver_init(void)
+{
+ int result;
+ result = usb_register(&hwahc_driver);
+ if (result < 0) {
+ printk(KERN_ERR "WA-CDS: Cannot register USB driver: %d\n",
+ result);
+ goto error_usb_register;
+ }
+ return 0;
+
+error_usb_register:
+ return result;
+
+}
+module_init(hwahc_driver_init);
+
+static void __exit hwahc_driver_exit(void)
+{
+ usb_deregister(&hwahc_driver);
+}
+module_exit(hwahc_driver_exit);
+
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("Host Wired Adapter USB Host Control Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
index 20b9a0d07420..109dee82a376 100644
--- a/drivers/usb/host/isp116x-hcd.c
+++ b/drivers/usb/host/isp116x-hcd.c
@@ -1592,7 +1592,7 @@ static int __devinit isp116x_probe(struct platform_device *pdev)
}
/* allocate and initialize hcd */
- hcd = usb_create_hcd(&isp116x_hc_driver, &pdev->dev, pdev->dev.bus_id);
+ hcd = usb_create_hcd(&isp116x_hc_driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
ret = -ENOMEM;
goto err5;
diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c
index c9cec8738261..1bcb64af1fec 100644
--- a/drivers/usb/host/isp1760-hcd.c
+++ b/drivers/usb/host/isp1760-hcd.c
@@ -782,8 +782,8 @@ static void enqueue_one_int_qtd(u32 int_regs, u32 payload,
qtd->status |= slot << 16;
}
-void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh,
- struct isp1760_qtd *qtd)
+static void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh,
+ struct isp1760_qtd *qtd)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
u32 skip_map, or_map;
@@ -816,8 +816,8 @@ void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh,
isp1760_writel(buffstatus, hcd->regs + HC_BUFFER_STATUS_REG);
}
-void enqueue_an_INT_packet(struct usb_hcd *hcd, struct isp1760_qh *qh,
- struct isp1760_qtd *qtd)
+static void enqueue_an_INT_packet(struct usb_hcd *hcd, struct isp1760_qh *qh,
+ struct isp1760_qtd *qtd)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
u32 skip_map, or_map;
@@ -2195,7 +2195,7 @@ struct usb_hcd *isp1760_register(u64 res_start, u64 res_len, int irq,
/* prevent usb-core allocating DMA pages */
dev->dma_mask = NULL;
- hcd = usb_create_hcd(&isp1760_hc_driver, dev, dev->bus_id);
+ hcd = usb_create_hcd(&isp1760_hc_driver, dev, dev_name(dev));
if (!hcd)
return ERR_PTR(-ENOMEM);
@@ -2207,14 +2207,14 @@ struct usb_hcd *isp1760_register(u64 res_start, u64 res_len, int irq,
goto err_put;
}
- ret = usb_add_hcd(hcd, irq, irqflags);
- if (ret)
- goto err_unmap;
-
hcd->irq = irq;
hcd->rsrc_start = res_start;
hcd->rsrc_len = res_len;
+ ret = usb_add_hcd(hcd, irq, irqflags);
+ if (ret)
+ goto err_unmap;
+
return hcd;
err_unmap:
diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c
index c9db3fe98726..ad833661ff34 100644
--- a/drivers/usb/host/isp1760-if.c
+++ b/drivers/usb/host/isp1760-if.c
@@ -41,7 +41,7 @@ static int of_isp1760_probe(struct of_device *dev,
return -ENXIO;
res = request_mem_region(memory.start, memory.end - memory.start + 1,
- dev->dev.bus_id);
+ dev_name(&dev->dev));
if (!res)
return -EBUSY;
@@ -56,7 +56,7 @@ static int of_isp1760_probe(struct of_device *dev,
oirq.size);
hcd = isp1760_register(memory.start, res_len, virq,
- IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev->dev.bus_id);
+ IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev));
if (IS_ERR(hcd)) {
ret = PTR_ERR(hcd);
goto release_reg;
@@ -200,7 +200,7 @@ static int __devinit isp1761_pci_probe(struct pci_dev *dev,
dev->dev.dma_mask = NULL;
hcd = isp1760_register(pci_mem_phy0, length, dev->irq,
- IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev->dev.bus_id);
+ IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev));
pci_set_drvdata(dev, hcd);
if (!hcd)
return 0;
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index c96db1153dcf..c411b93f239c 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -91,7 +91,7 @@ static void at91_stop_hc(struct platform_device *pdev)
/*-------------------------------------------------------------------------*/
-static int usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *);
+static void usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *);
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
@@ -184,13 +184,14 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
* context, "rmmod" or something similar.
*
*/
-static int usb_hcd_at91_remove(struct usb_hcd *hcd,
+static void usb_hcd_at91_remove(struct usb_hcd *hcd,
struct platform_device *pdev)
{
usb_remove_hcd(hcd);
at91_stop_hc(pdev);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
if (cpu_is_at91sam9261())
clk_put(hclk);
@@ -199,7 +200,6 @@ static int usb_hcd_at91_remove(struct usb_hcd *hcd,
fclk = iclk = hclk = NULL;
dev_set_drvdata(&pdev->dev, NULL);
- return 0;
}
/*-------------------------------------------------------------------------*/
@@ -308,7 +308,8 @@ static int ohci_hcd_at91_drv_remove(struct platform_device *pdev)
}
device_init_wakeup(&pdev->dev, 0);
- return usb_hcd_at91_remove(platform_get_drvdata(pdev), pdev);
+ usb_hcd_at91_remove(platform_get_drvdata(pdev), pdev);
+ return 0;
}
#ifdef CONFIG_PM
diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c
index e06bfaebec54..7cef1d2f7ccc 100644
--- a/drivers/usb/host/ohci-dbg.c
+++ b/drivers/usb/host/ohci-dbg.c
@@ -651,7 +651,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
"%s\n"
"%s version " DRIVER_VERSION "\n",
hcd->self.controller->bus->name,
- hcd->self.controller->bus_id,
+ dev_name(hcd->self.controller),
hcd->product_desc,
hcd_name);
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 33f1c1c32edf..30860a728bf8 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -974,7 +974,7 @@ MODULE_LICENSE ("GPL");
#define PCI_DRIVER ohci_pci_driver
#endif
-#ifdef CONFIG_SA1111
+#if defined(CONFIG_ARCH_SA1100) && defined(CONFIG_SA1111)
#include "ohci-sa1111.c"
#define SA1111_DRIVER ohci_hcd_sa1111_driver
#endif
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
index 6859fb5f1d6f..1044eb18c790 100644
--- a/drivers/usb/host/ohci-omap.c
+++ b/drivers/usb/host/ohci-omap.c
@@ -326,7 +326,7 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver,
}
- hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id);
+ hcd = usb_create_hcd (driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
retval = -ENOMEM;
goto err0;
diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c
index 664f07ee8732..c461b93a2207 100644
--- a/drivers/usb/host/ohci-pnx4008.c
+++ b/drivers/usb/host/ohci-pnx4008.c
@@ -388,7 +388,7 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev)
while ((__raw_readl(USB_OTG_CLK_STAT) & USB_CLOCK_MASK) !=
USB_CLOCK_MASK) ;
- hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id);
+ hcd = usb_create_hcd (driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
err("Failed to allocate HC buffer");
ret = -ENOMEM;
diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c
index 50e55db13636..7ac53264ead3 100644
--- a/drivers/usb/host/ohci-ppc-of.c
+++ b/drivers/usb/host/ohci-ppc-of.c
@@ -14,8 +14,8 @@
*/
#include <linux/signal.h>
+#include <linux/of_platform.h>
-#include <asm/of_platform.h>
#include <asm/prom.h>
diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c
index bfdeb0d22d05..2089d8a46c4b 100644
--- a/drivers/usb/host/ohci-ps3.c
+++ b/drivers/usb/host/ohci-ps3.c
@@ -128,7 +128,7 @@ static int ps3_ohci_probe(struct ps3_system_bus_device *dev)
dev->core.dma_mask = &dummy_mask; /* FIXME: for improper usb code */
- hcd = usb_create_hcd(&ps3_ohci_hc_driver, &dev->core, dev->core.bus_id);
+ hcd = usb_create_hcd(&ps3_ohci_hc_driver, &dev->core, dev_name(&dev->core));
if (!hcd) {
dev_dbg(&dev->core, "%s:%d: usb_create_hcd failed\n", __func__,
diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c
index 9c9f3b59186f..3199382add69 100644
--- a/drivers/usb/host/ohci-q.c
+++ b/drivers/usb/host/ohci-q.c
@@ -159,9 +159,6 @@ static int ed_schedule (struct ohci_hcd *ohci, struct ed *ed)
{
int branch;
- if (ohci_to_hcd(ohci)->state == HC_STATE_QUIESCING)
- return -EAGAIN;
-
ed->state = ED_OPER;
ed->ed_prev = NULL;
ed->ed_next = NULL;
diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c
index e899a77dfb83..cff23637cfcc 100644
--- a/drivers/usb/host/ohci-sm501.c
+++ b/drivers/usb/host/ohci-sm501.c
@@ -142,7 +142,7 @@ static int ohci_hcd_sm501_drv_probe(struct platform_device *pdev)
goto err2;
}
- hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id);
+ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
retval = -ENOMEM;
goto err2;
diff --git a/drivers/usb/host/ohci-ssb.c b/drivers/usb/host/ohci-ssb.c
index c4265caec780..23fd6a886bdd 100644
--- a/drivers/usb/host/ohci-ssb.c
+++ b/drivers/usb/host/ohci-ssb.c
@@ -112,7 +112,7 @@ static int ssb_ohci_attach(struct ssb_device *dev)
ssb_device_enable(dev, flags);
hcd = usb_create_hcd(&ssb_ohci_hc_driver, dev->dev,
- dev->dev->bus_id);
+ dev_name(dev->dev));
if (!hcd)
goto err_dev_disable;
ohcidev = hcd_to_ssb_ohci(hcd);
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index 426575247b23..340d72da554a 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -1674,7 +1674,7 @@ sl811h_probe(struct platform_device *dev)
}
/* allocate and initialize hcd */
- hcd = usb_create_hcd(&sl811h_hc_driver, &dev->dev, dev->dev.bus_id);
+ hcd = usb_create_hcd(&sl811h_hc_driver, &dev->dev, dev_name(&dev->dev));
if (!hcd) {
retval = -ENOMEM;
goto err5;
diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c
index f29307405bb3..228f2b070f2b 100644
--- a/drivers/usb/host/u132-hcd.c
+++ b/drivers/usb/host/u132-hcd.c
@@ -3113,7 +3113,7 @@ static int __devinit u132_probe(struct platform_device *pdev)
if (pdev->dev.dma_mask)
return -EINVAL;
- hcd = usb_create_hcd(&u132_hc_driver, &pdev->dev, pdev->dev.bus_id);
+ hcd = usb_create_hcd(&u132_hc_driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
printk(KERN_ERR "failed to create the usb hcd struct for U132\n"
);
diff --git a/drivers/usb/host/whci/Kbuild b/drivers/usb/host/whci/Kbuild
new file mode 100644
index 000000000000..26a3871ea0f9
--- /dev/null
+++ b/drivers/usb/host/whci/Kbuild
@@ -0,0 +1,11 @@
+obj-$(CONFIG_USB_WHCI_HCD) += whci-hcd.o
+
+whci-hcd-y := \
+ asl.o \
+ hcd.o \
+ hw.o \
+ init.o \
+ int.o \
+ pzl.o \
+ qset.o \
+ wusb.o
diff --git a/drivers/usb/host/whci/asl.c b/drivers/usb/host/whci/asl.c
new file mode 100644
index 000000000000..5b62d1feb087
--- /dev/null
+++ b/drivers/usb/host/whci/asl.c
@@ -0,0 +1,366 @@
+/*
+ * Wireless Host Controller (WHC) asynchronous schedule management.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/uwb/umc.h>
+#include <linux/usb.h>
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+#if D_LOCAL >= 4
+static void dump_asl(struct whc *whc, const char *tag)
+{
+ struct device *dev = &whc->umc->dev;
+ struct whc_qset *qset;
+
+ d_printf(4, dev, "ASL %s\n", tag);
+
+ list_for_each_entry(qset, &whc->async_list, list_node) {
+ dump_qset(qset, dev);
+ }
+}
+#else
+static inline void dump_asl(struct whc *whc, const char *tag)
+{
+}
+#endif
+
+
+static void qset_get_next_prev(struct whc *whc, struct whc_qset *qset,
+ struct whc_qset **next, struct whc_qset **prev)
+{
+ struct list_head *n, *p;
+
+ BUG_ON(list_empty(&whc->async_list));
+
+ n = qset->list_node.next;
+ if (n == &whc->async_list)
+ n = n->next;
+ p = qset->list_node.prev;
+ if (p == &whc->async_list)
+ p = p->prev;
+
+ *next = container_of(n, struct whc_qset, list_node);
+ *prev = container_of(p, struct whc_qset, list_node);
+
+}
+
+static void asl_qset_insert_begin(struct whc *whc, struct whc_qset *qset)
+{
+ qset_clear(whc, qset);
+ list_move(&qset->list_node, &whc->async_list);
+ qset->in_sw_list = true;
+}
+
+static void asl_qset_insert(struct whc *whc, struct whc_qset *qset)
+{
+ struct whc_qset *next, *prev;
+
+ /* Link into ASL. */
+ qset_get_next_prev(whc, qset, &next, &prev);
+ whc_qset_set_link_ptr(&qset->qh.link, next->qset_dma);
+ whc_qset_set_link_ptr(&prev->qh.link, qset->qset_dma);
+ qset->in_hw_list = true;
+}
+
+static void asl_qset_remove(struct whc *whc, struct whc_qset *qset)
+{
+ struct whc_qset *prev, *next;
+
+ qset_get_next_prev(whc, qset, &next, &prev);
+
+ list_move(&qset->list_node, &whc->async_removed_list);
+ qset->in_sw_list = false;
+
+ /*
+ * No more qsets in the ASL? The caller must stop the ASL as
+ * it's no longer valid.
+ */
+ if (list_empty(&whc->async_list))
+ return;
+
+ /* Remove from ASL. */
+ whc_qset_set_link_ptr(&prev->qh.link, next->qset_dma);
+ qset->in_hw_list = false;
+}
+
+/**
+ * process_qset - process any recently inactivated or halted qTDs in a
+ * qset.
+ *
+ * After inactive qTDs are removed, new qTDs can be added if the
+ * urb queue still contains URBs.
+ *
+ * Returns any additional WUSBCMD bits for the ASL sync command (i.e.,
+ * WUSBCMD_ASYNC_QSET_RM if a halted qset was removed).
+ */
+static uint32_t process_qset(struct whc *whc, struct whc_qset *qset)
+{
+ enum whc_update update = 0;
+ uint32_t status = 0;
+
+ while (qset->ntds) {
+ struct whc_qtd *td;
+ int t;
+
+ t = qset->td_start;
+ td = &qset->qtd[qset->td_start];
+ status = le32_to_cpu(td->status);
+
+ /*
+ * Nothing to do with a still active qTD.
+ */
+ if (status & QTD_STS_ACTIVE)
+ break;
+
+ if (status & QTD_STS_HALTED) {
+ /* Ug, an error. */
+ process_halted_qtd(whc, qset, td);
+ goto done;
+ }
+
+ /* Mmm, a completed qTD. */
+ process_inactive_qtd(whc, qset, td);
+ }
+
+ update |= qset_add_qtds(whc, qset);
+
+done:
+ /*
+ * Remove this qset from the ASL if requested, but only if has
+ * no qTDs.
+ */
+ if (qset->remove && qset->ntds == 0) {
+ asl_qset_remove(whc, qset);
+ update |= WHC_UPDATE_REMOVED;
+ }
+ return update;
+}
+
+void asl_start(struct whc *whc)
+{
+ struct whc_qset *qset;
+
+ qset = list_first_entry(&whc->async_list, struct whc_qset, list_node);
+
+ le_writeq(qset->qset_dma | QH_LINK_NTDS(8), whc->base + WUSBASYNCLISTADDR);
+
+ whc_write_wusbcmd(whc, WUSBCMD_ASYNC_EN, WUSBCMD_ASYNC_EN);
+ whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS,
+ WUSBSTS_ASYNC_SCHED, WUSBSTS_ASYNC_SCHED,
+ 1000, "start ASL");
+}
+
+void asl_stop(struct whc *whc)
+{
+ whc_write_wusbcmd(whc, WUSBCMD_ASYNC_EN, 0);
+ whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS,
+ WUSBSTS_ASYNC_SCHED, 0,
+ 1000, "stop ASL");
+}
+
+void asl_update(struct whc *whc, uint32_t wusbcmd)
+{
+ whc_write_wusbcmd(whc, wusbcmd, wusbcmd);
+ wait_event(whc->async_list_wq,
+ (le_readl(whc->base + WUSBCMD) & WUSBCMD_ASYNC_UPDATED) == 0);
+}
+
+/**
+ * scan_async_work - scan the ASL for qsets to process.
+ *
+ * Process each qset in the ASL in turn and then signal the WHC that
+ * the ASL has been updated.
+ *
+ * Then start, stop or update the asynchronous schedule as required.
+ */
+void scan_async_work(struct work_struct *work)
+{
+ struct whc *whc = container_of(work, struct whc, async_work);
+ struct whc_qset *qset, *t;
+ enum whc_update update = 0;
+
+ spin_lock_irq(&whc->lock);
+
+ dump_asl(whc, "before processing");
+
+ /*
+ * Transerve the software list backwards so new qsets can be
+ * safely inserted into the ASL without making it non-circular.
+ */
+ list_for_each_entry_safe_reverse(qset, t, &whc->async_list, list_node) {
+ if (!qset->in_hw_list) {
+ asl_qset_insert(whc, qset);
+ update |= WHC_UPDATE_ADDED;
+ }
+
+ update |= process_qset(whc, qset);
+ }
+
+ dump_asl(whc, "after processing");
+
+ spin_unlock_irq(&whc->lock);
+
+ if (update) {
+ uint32_t wusbcmd = WUSBCMD_ASYNC_UPDATED | WUSBCMD_ASYNC_SYNCED_DB;
+ if (update & WHC_UPDATE_REMOVED)
+ wusbcmd |= WUSBCMD_ASYNC_QSET_RM;
+ asl_update(whc, wusbcmd);
+ }
+
+ /*
+ * Now that the ASL is updated, complete the removal of any
+ * removed qsets.
+ */
+ spin_lock(&whc->lock);
+
+ list_for_each_entry_safe(qset, t, &whc->async_removed_list, list_node) {
+ qset_remove_complete(whc, qset);
+ }
+
+ spin_unlock(&whc->lock);
+}
+
+/**
+ * asl_urb_enqueue - queue an URB onto the asynchronous list (ASL).
+ * @whc: the WHCI host controller
+ * @urb: the URB to enqueue
+ * @mem_flags: flags for any memory allocations
+ *
+ * The qset for the endpoint is obtained and the urb queued on to it.
+ *
+ * Work is scheduled to update the hardware's view of the ASL.
+ */
+int asl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags)
+{
+ struct whc_qset *qset;
+ int err;
+ unsigned long flags;
+
+ spin_lock_irqsave(&whc->lock, flags);
+
+ qset = get_qset(whc, urb, GFP_ATOMIC);
+ if (qset == NULL)
+ err = -ENOMEM;
+ else
+ err = qset_add_urb(whc, qset, urb, GFP_ATOMIC);
+ if (!err) {
+ usb_hcd_link_urb_to_ep(&whc->wusbhc.usb_hcd, urb);
+ if (!qset->in_sw_list)
+ asl_qset_insert_begin(whc, qset);
+ }
+
+ spin_unlock_irqrestore(&whc->lock, flags);
+
+ if (!err)
+ queue_work(whc->workqueue, &whc->async_work);
+
+ return 0;
+}
+
+/**
+ * asl_urb_dequeue - remove an URB (qset) from the async list.
+ * @whc: the WHCI host controller
+ * @urb: the URB to dequeue
+ * @status: the current status of the URB
+ *
+ * URBs that do yet have qTDs can simply be removed from the software
+ * queue, otherwise the qset must be removed from the ASL so the qTDs
+ * can be removed.
+ */
+int asl_urb_dequeue(struct whc *whc, struct urb *urb, int status)
+{
+ struct whc_urb *wurb = urb->hcpriv;
+ struct whc_qset *qset = wurb->qset;
+ struct whc_std *std, *t;
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&whc->lock, flags);
+
+ ret = usb_hcd_check_unlink_urb(&whc->wusbhc.usb_hcd, urb, status);
+ if (ret < 0)
+ goto out;
+
+ list_for_each_entry_safe(std, t, &qset->stds, list_node) {
+ if (std->urb == urb)
+ qset_free_std(whc, std);
+ else
+ std->qtd = NULL; /* so this std is re-added when the qset is */
+ }
+
+ asl_qset_remove(whc, qset);
+ wurb->status = status;
+ wurb->is_async = true;
+ queue_work(whc->workqueue, &wurb->dequeue_work);
+
+out:
+ spin_unlock_irqrestore(&whc->lock, flags);
+
+ return ret;
+}
+
+/**
+ * asl_qset_delete - delete a qset from the ASL
+ */
+void asl_qset_delete(struct whc *whc, struct whc_qset *qset)
+{
+ qset->remove = 1;
+ queue_work(whc->workqueue, &whc->async_work);
+ qset_delete(whc, qset);
+}
+
+/**
+ * asl_init - initialize the asynchronous schedule list
+ *
+ * A dummy qset with no qTDs is added to the ASL to simplify removing
+ * qsets (no need to stop the ASL when the last qset is removed).
+ */
+int asl_init(struct whc *whc)
+{
+ struct whc_qset *qset;
+
+ qset = qset_alloc(whc, GFP_KERNEL);
+ if (qset == NULL)
+ return -ENOMEM;
+
+ asl_qset_insert_begin(whc, qset);
+ asl_qset_insert(whc, qset);
+
+ return 0;
+}
+
+/**
+ * asl_clean_up - free ASL resources
+ *
+ * The ASL is stopped and empty except for the dummy qset.
+ */
+void asl_clean_up(struct whc *whc)
+{
+ struct whc_qset *qset;
+
+ if (!list_empty(&whc->async_list)) {
+ qset = list_first_entry(&whc->async_list, struct whc_qset, list_node);
+ list_del(&qset->list_node);
+ qset_free(whc, qset);
+ }
+}
diff --git a/drivers/usb/host/whci/hcd.c b/drivers/usb/host/whci/hcd.c
new file mode 100644
index 000000000000..e2017411de18
--- /dev/null
+++ b/drivers/usb/host/whci/hcd.c
@@ -0,0 +1,339 @@
+/*
+ * Wireless Host Controller (WHC) driver.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/uwb/umc.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+/*
+ * One time initialization.
+ *
+ * Nothing to do here.
+ */
+static int whc_reset(struct usb_hcd *usb_hcd)
+{
+ return 0;
+}
+
+/*
+ * Start the wireless host controller.
+ *
+ * Start device notification.
+ *
+ * Put hc into run state, set DNTS parameters.
+ */
+static int whc_start(struct usb_hcd *usb_hcd)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ u8 bcid;
+ int ret;
+
+ mutex_lock(&wusbhc->mutex);
+
+ le_writel(WUSBINTR_GEN_CMD_DONE
+ | WUSBINTR_HOST_ERR
+ | WUSBINTR_ASYNC_SCHED_SYNCED
+ | WUSBINTR_DNTS_INT
+ | WUSBINTR_ERR_INT
+ | WUSBINTR_INT,
+ whc->base + WUSBINTR);
+
+ /* set cluster ID */
+ bcid = wusb_cluster_id_get();
+ ret = whc_set_cluster_id(whc, bcid);
+ if (ret < 0)
+ goto out;
+ wusbhc->cluster_id = bcid;
+
+ /* start HC */
+ whc_write_wusbcmd(whc, WUSBCMD_RUN, WUSBCMD_RUN);
+
+ usb_hcd->uses_new_polling = 1;
+ usb_hcd->poll_rh = 1;
+ usb_hcd->state = HC_STATE_RUNNING;
+
+out:
+ mutex_unlock(&wusbhc->mutex);
+ return ret;
+}
+
+
+/*
+ * Stop the wireless host controller.
+ *
+ * Stop device notification.
+ *
+ * Wait for pending transfer to stop? Put hc into stop state?
+ */
+static void whc_stop(struct usb_hcd *usb_hcd)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+
+ mutex_lock(&wusbhc->mutex);
+
+ wusbhc_stop(wusbhc);
+
+ /* stop HC */
+ le_writel(0, whc->base + WUSBINTR);
+ whc_write_wusbcmd(whc, WUSBCMD_RUN, 0);
+ whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS,
+ WUSBSTS_HCHALTED, WUSBSTS_HCHALTED,
+ 100, "HC to halt");
+
+ wusb_cluster_id_put(wusbhc->cluster_id);
+
+ mutex_unlock(&wusbhc->mutex);
+}
+
+static int whc_get_frame_number(struct usb_hcd *usb_hcd)
+{
+ /* Frame numbers are not applicable to WUSB. */
+ return -ENOSYS;
+}
+
+
+/*
+ * Queue an URB to the ASL or PZL
+ */
+static int whc_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ int ret;
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_INTERRUPT:
+ ret = pzl_urb_enqueue(whc, urb, mem_flags);
+ break;
+ case PIPE_ISOCHRONOUS:
+ dev_err(&whc->umc->dev, "isochronous transfers unsupported\n");
+ ret = -ENOTSUPP;
+ break;
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ default:
+ ret = asl_urb_enqueue(whc, urb, mem_flags);
+ break;
+ };
+
+ return ret;
+}
+
+/*
+ * Remove a queued URB from the ASL or PZL.
+ */
+static int whc_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb, int status)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ int ret;
+
+ switch (usb_pipetype(urb->pipe)) {
+ case PIPE_INTERRUPT:
+ ret = pzl_urb_dequeue(whc, urb, status);
+ break;
+ case PIPE_ISOCHRONOUS:
+ ret = -ENOTSUPP;
+ break;
+ case PIPE_CONTROL:
+ case PIPE_BULK:
+ default:
+ ret = asl_urb_dequeue(whc, urb, status);
+ break;
+ };
+
+ return ret;
+}
+
+/*
+ * Wait for all URBs to the endpoint to be completed, then delete the
+ * qset.
+ */
+static void whc_endpoint_disable(struct usb_hcd *usb_hcd,
+ struct usb_host_endpoint *ep)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ struct whc_qset *qset;
+
+ qset = ep->hcpriv;
+ if (qset) {
+ ep->hcpriv = NULL;
+ if (usb_endpoint_xfer_bulk(&ep->desc)
+ || usb_endpoint_xfer_control(&ep->desc))
+ asl_qset_delete(whc, qset);
+ else
+ pzl_qset_delete(whc, qset);
+ }
+}
+
+static struct hc_driver whc_hc_driver = {
+ .description = "whci-hcd",
+ .product_desc = "Wireless host controller",
+ .hcd_priv_size = sizeof(struct whc) - sizeof(struct usb_hcd),
+ .irq = whc_int_handler,
+ .flags = HCD_USB2,
+
+ .reset = whc_reset,
+ .start = whc_start,
+ .stop = whc_stop,
+ .get_frame_number = whc_get_frame_number,
+ .urb_enqueue = whc_urb_enqueue,
+ .urb_dequeue = whc_urb_dequeue,
+ .endpoint_disable = whc_endpoint_disable,
+
+ .hub_status_data = wusbhc_rh_status_data,
+ .hub_control = wusbhc_rh_control,
+ .bus_suspend = wusbhc_rh_suspend,
+ .bus_resume = wusbhc_rh_resume,
+ .start_port_reset = wusbhc_rh_start_port_reset,
+};
+
+static int whc_probe(struct umc_dev *umc)
+{
+ int ret = -ENOMEM;
+ struct usb_hcd *usb_hcd;
+ struct wusbhc *wusbhc = NULL;
+ struct whc *whc = NULL;
+ struct device *dev = &umc->dev;
+
+ usb_hcd = usb_create_hcd(&whc_hc_driver, dev, "whci");
+ if (usb_hcd == NULL) {
+ dev_err(dev, "unable to create hcd\n");
+ goto error;
+ }
+
+ usb_hcd->wireless = 1;
+
+ wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ whc = wusbhc_to_whc(wusbhc);
+ whc->umc = umc;
+
+ ret = whc_init(whc);
+ if (ret)
+ goto error;
+
+ wusbhc->dev = dev;
+ wusbhc->uwb_rc = uwb_rc_get_by_grandpa(umc->dev.parent);
+ if (!wusbhc->uwb_rc) {
+ ret = -ENODEV;
+ dev_err(dev, "cannot get radio controller\n");
+ goto error;
+ }
+
+ if (whc->n_devices > USB_MAXCHILDREN) {
+ dev_warn(dev, "USB_MAXCHILDREN too low for WUSB adapter (%u ports)\n",
+ whc->n_devices);
+ wusbhc->ports_max = USB_MAXCHILDREN;
+ } else
+ wusbhc->ports_max = whc->n_devices;
+ wusbhc->mmcies_max = whc->n_mmc_ies;
+ wusbhc->start = whc_wusbhc_start;
+ wusbhc->stop = whc_wusbhc_stop;
+ wusbhc->mmcie_add = whc_mmcie_add;
+ wusbhc->mmcie_rm = whc_mmcie_rm;
+ wusbhc->dev_info_set = whc_dev_info_set;
+ wusbhc->bwa_set = whc_bwa_set;
+ wusbhc->set_num_dnts = whc_set_num_dnts;
+ wusbhc->set_ptk = whc_set_ptk;
+ wusbhc->set_gtk = whc_set_gtk;
+
+ ret = wusbhc_create(wusbhc);
+ if (ret)
+ goto error_wusbhc_create;
+
+ ret = usb_add_hcd(usb_hcd, whc->umc->irq, IRQF_SHARED);
+ if (ret) {
+ dev_err(dev, "cannot add HCD: %d\n", ret);
+ goto error_usb_add_hcd;
+ }
+
+ ret = wusbhc_b_create(wusbhc);
+ if (ret) {
+ dev_err(dev, "WUSBHC phase B setup failed: %d\n", ret);
+ goto error_wusbhc_b_create;
+ }
+
+ return 0;
+
+error_wusbhc_b_create:
+ usb_remove_hcd(usb_hcd);
+error_usb_add_hcd:
+ wusbhc_destroy(wusbhc);
+error_wusbhc_create:
+ uwb_rc_put(wusbhc->uwb_rc);
+error:
+ whc_clean_up(whc);
+ if (usb_hcd)
+ usb_put_hcd(usb_hcd);
+ return ret;
+}
+
+
+static void whc_remove(struct umc_dev *umc)
+{
+ struct usb_hcd *usb_hcd = dev_get_drvdata(&umc->dev);
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+
+ if (usb_hcd) {
+ wusbhc_b_destroy(wusbhc);
+ usb_remove_hcd(usb_hcd);
+ wusbhc_destroy(wusbhc);
+ uwb_rc_put(wusbhc->uwb_rc);
+ whc_clean_up(whc);
+ usb_put_hcd(usb_hcd);
+ }
+}
+
+static struct umc_driver whci_hc_driver = {
+ .name = "whci-hcd",
+ .cap_id = UMC_CAP_ID_WHCI_WUSB_HC,
+ .probe = whc_probe,
+ .remove = whc_remove,
+};
+
+static int __init whci_hc_driver_init(void)
+{
+ return umc_driver_register(&whci_hc_driver);
+}
+module_init(whci_hc_driver_init);
+
+static void __exit whci_hc_driver_exit(void)
+{
+ umc_driver_unregister(&whci_hc_driver);
+}
+module_exit(whci_hc_driver_exit);
+
+/* PCI device ID's that we handle (so it gets loaded) */
+static struct pci_device_id whci_hcd_id_table[] = {
+ { PCI_DEVICE_CLASS(PCI_CLASS_WIRELESS_WHCI, ~0) },
+ { /* empty last entry */ }
+};
+MODULE_DEVICE_TABLE(pci, whci_hcd_id_table);
+
+MODULE_DESCRIPTION("WHCI Wireless USB host controller driver");
+MODULE_AUTHOR("Cambridge Silicon Radio Ltd.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/whci/hw.c b/drivers/usb/host/whci/hw.c
new file mode 100644
index 000000000000..22d38f23c3f0
--- /dev/null
+++ b/drivers/usb/host/whci/hw.c
@@ -0,0 +1,80 @@
+/*
+ * Wireless Host Controller (WHC) hardware access helpers.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/uwb/umc.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+void whc_write_wusbcmd(struct whc *whc, u32 mask, u32 val)
+{
+ unsigned long flags;
+ u32 cmd;
+
+ spin_lock_irqsave(&whc->lock, flags);
+
+ cmd = le_readl(whc->base + WUSBCMD);
+ cmd = (cmd & ~mask) | val;
+ le_writel(cmd, whc->base + WUSBCMD);
+
+ spin_unlock_irqrestore(&whc->lock, flags);
+}
+
+int whc_do_gencmd(struct whc *whc, u32 cmd, u32 params, void *addr, size_t len)
+{
+ unsigned long flags;
+ dma_addr_t dma_addr;
+ int t, ret = 0;
+
+ if (addr)
+ dma_addr = dma_map_single(&whc->umc->dev, addr, len, DMA_TO_DEVICE);
+ else
+ dma_addr = 0;
+
+ mutex_lock(&whc->mutex);
+
+ /* poke registers to start cmd */
+ spin_lock_irqsave(&whc->lock, flags);
+
+ le_writel(params, whc->base + WUSBGENCMDPARAMS);
+ le_writeq(dma_addr, whc->base + WUSBGENADDR);
+
+ le_writel(WUSBGENCMDSTS_ACTIVE | WUSBGENCMDSTS_IOC | cmd,
+ whc->base + WUSBGENCMDSTS);
+
+ spin_unlock_irqrestore(&whc->lock, flags);
+
+ /* wait for command to complete */
+ t = wait_event_timeout(whc->cmd_wq,
+ (le_readl(whc->base + WUSBGENCMDSTS) & WUSBGENCMDSTS_ACTIVE) == 0,
+ WHC_GENCMD_TIMEOUT_MS);
+ if (t == 0) {
+ ret = -ETIMEDOUT;
+ dev_err(&whc->umc->dev, "generic command timeout (%04x/%04x)\n",
+ cmd, params);
+ }
+
+ mutex_unlock(&whc->mutex);
+
+ if (addr)
+ dma_unmap_single(&whc->umc->dev, dma_addr, len, DMA_TO_DEVICE);
+
+ return ret;
+}
diff --git a/drivers/usb/host/whci/init.c b/drivers/usb/host/whci/init.c
new file mode 100644
index 000000000000..f1856450f841
--- /dev/null
+++ b/drivers/usb/host/whci/init.c
@@ -0,0 +1,181 @@
+/*
+ * Wireless Host Controller (WHC) initialization.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/uwb/umc.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+/*
+ * Reset the host controller.
+ */
+static void whc_hw_reset(struct whc *whc)
+{
+ le_writel(WUSBCMD_WHCRESET, whc->base + WUSBCMD);
+ whci_wait_for(&whc->umc->dev, whc->base + WUSBCMD, WUSBCMD_WHCRESET, 0,
+ 100, "reset");
+}
+
+static void whc_hw_init_di_buf(struct whc *whc)
+{
+ int d;
+
+ /* Disable all entries in the Device Information buffer. */
+ for (d = 0; d < whc->n_devices; d++)
+ whc->di_buf[d].addr_sec_info = WHC_DI_DISABLE;
+
+ le_writeq(whc->di_buf_dma, whc->base + WUSBDEVICEINFOADDR);
+}
+
+static void whc_hw_init_dn_buf(struct whc *whc)
+{
+ /* Clear the Device Notification buffer to ensure the V (valid)
+ * bits are clear. */
+ memset(whc->dn_buf, 0, 4096);
+
+ le_writeq(whc->dn_buf_dma, whc->base + WUSBDNTSBUFADDR);
+}
+
+int whc_init(struct whc *whc)
+{
+ u32 whcsparams;
+ int ret, i;
+ resource_size_t start, len;
+
+ spin_lock_init(&whc->lock);
+ mutex_init(&whc->mutex);
+ init_waitqueue_head(&whc->cmd_wq);
+ init_waitqueue_head(&whc->async_list_wq);
+ init_waitqueue_head(&whc->periodic_list_wq);
+ whc->workqueue = create_singlethread_workqueue(dev_name(&whc->umc->dev));
+ if (whc->workqueue == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ INIT_WORK(&whc->dn_work, whc_dn_work);
+
+ INIT_WORK(&whc->async_work, scan_async_work);
+ INIT_LIST_HEAD(&whc->async_list);
+ INIT_LIST_HEAD(&whc->async_removed_list);
+
+ INIT_WORK(&whc->periodic_work, scan_periodic_work);
+ for (i = 0; i < 5; i++)
+ INIT_LIST_HEAD(&whc->periodic_list[i]);
+ INIT_LIST_HEAD(&whc->periodic_removed_list);
+
+ /*
+ * Map HC registers.
+ */
+ start = whc->umc->resource.start;
+ len = whc->umc->resource.end - start + 1;
+ if (!request_mem_region(start, len, "whci-hc")) {
+ dev_err(&whc->umc->dev, "can't request HC region\n");
+ ret = -EBUSY;
+ goto error;
+ }
+ whc->base_phys = start;
+ whc->base = ioremap(start, len);
+ if (!whc->base) {
+ dev_err(&whc->umc->dev, "ioremap\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ whc_hw_reset(whc);
+
+ /* Read maximum number of devices, keys and MMC IEs. */
+ whcsparams = le_readl(whc->base + WHCSPARAMS);
+ whc->n_devices = WHCSPARAMS_TO_N_DEVICES(whcsparams);
+ whc->n_keys = WHCSPARAMS_TO_N_KEYS(whcsparams);
+ whc->n_mmc_ies = WHCSPARAMS_TO_N_MMC_IES(whcsparams);
+
+ dev_dbg(&whc->umc->dev, "N_DEVICES = %d, N_KEYS = %d, N_MMC_IES = %d\n",
+ whc->n_devices, whc->n_keys, whc->n_mmc_ies);
+
+ whc->qset_pool = dma_pool_create("qset", &whc->umc->dev,
+ sizeof(struct whc_qset), 64, 0);
+ if (whc->qset_pool == NULL) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ ret = asl_init(whc);
+ if (ret < 0)
+ goto error;
+ ret = pzl_init(whc);
+ if (ret < 0)
+ goto error;
+
+ /*
+ * Allocate and initialize the Device Information buffer and
+ * the Device Notification buffer.
+ */
+
+ whc->dn_buf = dma_alloc_coherent(&whc->umc->dev,
+ sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES,
+ &whc->dn_buf_dma, GFP_KERNEL);
+ if (!whc->dn_buf) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ whc_hw_init_dn_buf(whc);
+
+ whc->di_buf = dma_alloc_coherent(&whc->umc->dev,
+ sizeof(struct di_buf_entry) * whc->n_devices,
+ &whc->di_buf_dma, GFP_KERNEL);
+ if (!whc->di_buf) {
+ ret = -ENOMEM;
+ goto error;
+ }
+ whc_hw_init_di_buf(whc);
+
+ return 0;
+
+error:
+ whc_clean_up(whc);
+ return ret;
+}
+
+void whc_clean_up(struct whc *whc)
+{
+ resource_size_t len;
+
+ if (whc->di_buf)
+ dma_free_coherent(&whc->umc->dev, sizeof(struct di_buf_entry) * whc->n_devices,
+ whc->di_buf, whc->di_buf_dma);
+ if (whc->dn_buf)
+ dma_free_coherent(&whc->umc->dev, sizeof(struct dn_buf_entry) * WHC_N_DN_ENTRIES,
+ whc->dn_buf, whc->dn_buf_dma);
+
+ pzl_clean_up(whc);
+ asl_clean_up(whc);
+
+ if (whc->qset_pool)
+ dma_pool_destroy(whc->qset_pool);
+
+ len = whc->umc->resource.end - whc->umc->resource.start + 1;
+ if (whc->base)
+ iounmap(whc->base);
+ if (whc->base_phys)
+ release_mem_region(whc->base_phys, len);
+
+ if (whc->workqueue)
+ destroy_workqueue(whc->workqueue);
+}
diff --git a/drivers/usb/host/whci/int.c b/drivers/usb/host/whci/int.c
new file mode 100644
index 000000000000..fce01174aa9b
--- /dev/null
+++ b/drivers/usb/host/whci/int.c
@@ -0,0 +1,95 @@
+/*
+ * Wireless Host Controller (WHC) interrupt handling.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/uwb/umc.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+static void transfer_done(struct whc *whc)
+{
+ queue_work(whc->workqueue, &whc->async_work);
+ queue_work(whc->workqueue, &whc->periodic_work);
+}
+
+irqreturn_t whc_int_handler(struct usb_hcd *hcd)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(hcd);
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ u32 sts;
+
+ sts = le_readl(whc->base + WUSBSTS);
+ if (!(sts & WUSBSTS_INT_MASK))
+ return IRQ_NONE;
+ le_writel(sts & WUSBSTS_INT_MASK, whc->base + WUSBSTS);
+
+ if (sts & WUSBSTS_GEN_CMD_DONE)
+ wake_up(&whc->cmd_wq);
+
+ if (sts & WUSBSTS_HOST_ERR)
+ dev_err(&whc->umc->dev, "FIXME: host system error\n");
+
+ if (sts & WUSBSTS_ASYNC_SCHED_SYNCED)
+ wake_up(&whc->async_list_wq);
+
+ if (sts & WUSBSTS_PERIODIC_SCHED_SYNCED)
+ wake_up(&whc->periodic_list_wq);
+
+ if (sts & WUSBSTS_DNTS_INT)
+ queue_work(whc->workqueue, &whc->dn_work);
+
+ /*
+ * A transfer completed (see [WHCI] section 4.7.1.2 for when
+ * this occurs).
+ */
+ if (sts & (WUSBSTS_INT | WUSBSTS_ERR_INT))
+ transfer_done(whc);
+
+ return IRQ_HANDLED;
+}
+
+static int process_dn_buf(struct whc *whc)
+{
+ struct wusbhc *wusbhc = &whc->wusbhc;
+ struct dn_buf_entry *dn;
+ int processed = 0;
+
+ for (dn = whc->dn_buf; dn < whc->dn_buf + WHC_N_DN_ENTRIES; dn++) {
+ if (dn->status & WHC_DN_STATUS_VALID) {
+ wusbhc_handle_dn(wusbhc, dn->src_addr,
+ (struct wusb_dn_hdr *)dn->dn_data,
+ dn->msg_size);
+ dn->status &= ~WHC_DN_STATUS_VALID;
+ processed++;
+ }
+ }
+ return processed;
+}
+
+void whc_dn_work(struct work_struct *work)
+{
+ struct whc *whc = container_of(work, struct whc, dn_work);
+ int processed;
+
+ do {
+ processed = process_dn_buf(whc);
+ } while (processed);
+}
diff --git a/drivers/usb/host/whci/pzl.c b/drivers/usb/host/whci/pzl.c
new file mode 100644
index 000000000000..8d62df0c330b
--- /dev/null
+++ b/drivers/usb/host/whci/pzl.c
@@ -0,0 +1,398 @@
+/*
+ * Wireless Host Controller (WHC) periodic schedule management.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/uwb/umc.h>
+#include <linux/usb.h>
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+#if D_LOCAL >= 4
+static void dump_pzl(struct whc *whc, const char *tag)
+{
+ struct device *dev = &whc->umc->dev;
+ struct whc_qset *qset;
+ int period = 0;
+
+ d_printf(4, dev, "PZL %s\n", tag);
+
+ for (period = 0; period < 5; period++) {
+ d_printf(4, dev, "Period %d\n", period);
+ list_for_each_entry(qset, &whc->periodic_list[period], list_node) {
+ dump_qset(qset, dev);
+ }
+ }
+}
+#else
+static inline void dump_pzl(struct whc *whc, const char *tag)
+{
+}
+#endif
+
+static void update_pzl_pointers(struct whc *whc, int period, u64 addr)
+{
+ switch (period) {
+ case 0:
+ whc_qset_set_link_ptr(&whc->pz_list[0], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[2], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[4], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[6], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[8], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[10], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[12], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[14], addr);
+ break;
+ case 1:
+ whc_qset_set_link_ptr(&whc->pz_list[1], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[5], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[9], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[13], addr);
+ break;
+ case 2:
+ whc_qset_set_link_ptr(&whc->pz_list[3], addr);
+ whc_qset_set_link_ptr(&whc->pz_list[11], addr);
+ break;
+ case 3:
+ whc_qset_set_link_ptr(&whc->pz_list[7], addr);
+ break;
+ case 4:
+ whc_qset_set_link_ptr(&whc->pz_list[15], addr);
+ break;
+ }
+}
+
+/*
+ * Return the 'period' to use for this qset. The minimum interval for
+ * the endpoint is used so whatever urbs are submitted the device is
+ * polled often enough.
+ */
+static int qset_get_period(struct whc *whc, struct whc_qset *qset)
+{
+ uint8_t bInterval = qset->ep->desc.bInterval;
+
+ if (bInterval < 6)
+ bInterval = 6;
+ if (bInterval > 10)
+ bInterval = 10;
+ return bInterval - 6;
+}
+
+static void qset_insert_in_sw_list(struct whc *whc, struct whc_qset *qset)
+{
+ int period;
+
+ period = qset_get_period(whc, qset);
+
+ qset_clear(whc, qset);
+ list_move(&qset->list_node, &whc->periodic_list[period]);
+ qset->in_sw_list = true;
+}
+
+static void pzl_qset_remove(struct whc *whc, struct whc_qset *qset)
+{
+ list_move(&qset->list_node, &whc->periodic_removed_list);
+ qset->in_hw_list = false;
+ qset->in_sw_list = false;
+}
+
+/**
+ * pzl_process_qset - process any recently inactivated or halted qTDs
+ * in a qset.
+ *
+ * After inactive qTDs are removed, new qTDs can be added if the
+ * urb queue still contains URBs.
+ *
+ * Returns the schedule updates required.
+ */
+static enum whc_update pzl_process_qset(struct whc *whc, struct whc_qset *qset)
+{
+ enum whc_update update = 0;
+ uint32_t status = 0;
+
+ while (qset->ntds) {
+ struct whc_qtd *td;
+ int t;
+
+ t = qset->td_start;
+ td = &qset->qtd[qset->td_start];
+ status = le32_to_cpu(td->status);
+
+ /*
+ * Nothing to do with a still active qTD.
+ */
+ if (status & QTD_STS_ACTIVE)
+ break;
+
+ if (status & QTD_STS_HALTED) {
+ /* Ug, an error. */
+ process_halted_qtd(whc, qset, td);
+ goto done;
+ }
+
+ /* Mmm, a completed qTD. */
+ process_inactive_qtd(whc, qset, td);
+ }
+
+ update |= qset_add_qtds(whc, qset);
+
+done:
+ /*
+ * If there are no qTDs in this qset, remove it from the PZL.
+ */
+ if (qset->remove && qset->ntds == 0) {
+ pzl_qset_remove(whc, qset);
+ update |= WHC_UPDATE_REMOVED;
+ }
+
+ return update;
+}
+
+/**
+ * pzl_start - start the periodic schedule
+ * @whc: the WHCI host controller
+ *
+ * The PZL must be valid (e.g., all entries in the list should have
+ * the T bit set).
+ */
+void pzl_start(struct whc *whc)
+{
+ le_writeq(whc->pz_list_dma, whc->base + WUSBPERIODICLISTBASE);
+
+ whc_write_wusbcmd(whc, WUSBCMD_PERIODIC_EN, WUSBCMD_PERIODIC_EN);
+ whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS,
+ WUSBSTS_PERIODIC_SCHED, WUSBSTS_PERIODIC_SCHED,
+ 1000, "start PZL");
+}
+
+/**
+ * pzl_stop - stop the periodic schedule
+ * @whc: the WHCI host controller
+ */
+void pzl_stop(struct whc *whc)
+{
+ whc_write_wusbcmd(whc, WUSBCMD_PERIODIC_EN, 0);
+ whci_wait_for(&whc->umc->dev, whc->base + WUSBSTS,
+ WUSBSTS_PERIODIC_SCHED, 0,
+ 1000, "stop PZL");
+}
+
+void pzl_update(struct whc *whc, uint32_t wusbcmd)
+{
+ whc_write_wusbcmd(whc, wusbcmd, wusbcmd);
+ wait_event(whc->periodic_list_wq,
+ (le_readl(whc->base + WUSBCMD) & WUSBCMD_PERIODIC_UPDATED) == 0);
+}
+
+static void update_pzl_hw_view(struct whc *whc)
+{
+ struct whc_qset *qset, *t;
+ int period;
+ u64 tmp_qh = 0;
+
+ for (period = 0; period < 5; period++) {
+ list_for_each_entry_safe(qset, t, &whc->periodic_list[period], list_node) {
+ whc_qset_set_link_ptr(&qset->qh.link, tmp_qh);
+ tmp_qh = qset->qset_dma;
+ qset->in_hw_list = true;
+ }
+ update_pzl_pointers(whc, period, tmp_qh);
+ }
+}
+
+/**
+ * scan_periodic_work - scan the PZL for qsets to process.
+ *
+ * Process each qset in the PZL in turn and then signal the WHC that
+ * the PZL has been updated.
+ *
+ * Then start, stop or update the periodic schedule as required.
+ */
+void scan_periodic_work(struct work_struct *work)
+{
+ struct whc *whc = container_of(work, struct whc, periodic_work);
+ struct whc_qset *qset, *t;
+ enum whc_update update = 0;
+ int period;
+
+ spin_lock_irq(&whc->lock);
+
+ dump_pzl(whc, "before processing");
+
+ for (period = 4; period >= 0; period--) {
+ list_for_each_entry_safe(qset, t, &whc->periodic_list[period], list_node) {
+ if (!qset->in_hw_list)
+ update |= WHC_UPDATE_ADDED;
+ update |= pzl_process_qset(whc, qset);
+ }
+ }
+
+ if (update & (WHC_UPDATE_ADDED | WHC_UPDATE_REMOVED))
+ update_pzl_hw_view(whc);
+
+ dump_pzl(whc, "after processing");
+
+ spin_unlock_irq(&whc->lock);
+
+ if (update) {
+ uint32_t wusbcmd = WUSBCMD_PERIODIC_UPDATED | WUSBCMD_PERIODIC_SYNCED_DB;
+ if (update & WHC_UPDATE_REMOVED)
+ wusbcmd |= WUSBCMD_PERIODIC_QSET_RM;
+ pzl_update(whc, wusbcmd);
+ }
+
+ /*
+ * Now that the PZL is updated, complete the removal of any
+ * removed qsets.
+ */
+ spin_lock(&whc->lock);
+
+ list_for_each_entry_safe(qset, t, &whc->periodic_removed_list, list_node) {
+ qset_remove_complete(whc, qset);
+ }
+
+ spin_unlock(&whc->lock);
+}
+
+/**
+ * pzl_urb_enqueue - queue an URB onto the periodic list (PZL)
+ * @whc: the WHCI host controller
+ * @urb: the URB to enqueue
+ * @mem_flags: flags for any memory allocations
+ *
+ * The qset for the endpoint is obtained and the urb queued on to it.
+ *
+ * Work is scheduled to update the hardware's view of the PZL.
+ */
+int pzl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags)
+{
+ struct whc_qset *qset;
+ int err;
+ unsigned long flags;
+
+ spin_lock_irqsave(&whc->lock, flags);
+
+ qset = get_qset(whc, urb, GFP_ATOMIC);
+ if (qset == NULL)
+ err = -ENOMEM;
+ else
+ err = qset_add_urb(whc, qset, urb, GFP_ATOMIC);
+ if (!err) {
+ usb_hcd_link_urb_to_ep(&whc->wusbhc.usb_hcd, urb);
+ if (!qset->in_sw_list)
+ qset_insert_in_sw_list(whc, qset);
+ }
+
+ spin_unlock_irqrestore(&whc->lock, flags);
+
+ if (!err)
+ queue_work(whc->workqueue, &whc->periodic_work);
+
+ return 0;
+}
+
+/**
+ * pzl_urb_dequeue - remove an URB (qset) from the periodic list
+ * @whc: the WHCI host controller
+ * @urb: the URB to dequeue
+ * @status: the current status of the URB
+ *
+ * URBs that do yet have qTDs can simply be removed from the software
+ * queue, otherwise the qset must be removed so the qTDs can be safely
+ * removed.
+ */
+int pzl_urb_dequeue(struct whc *whc, struct urb *urb, int status)
+{
+ struct whc_urb *wurb = urb->hcpriv;
+ struct whc_qset *qset = wurb->qset;
+ struct whc_std *std, *t;
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&whc->lock, flags);
+
+ ret = usb_hcd_check_unlink_urb(&whc->wusbhc.usb_hcd, urb, status);
+ if (ret < 0)
+ goto out;
+
+ list_for_each_entry_safe(std, t, &qset->stds, list_node) {
+ if (std->urb == urb)
+ qset_free_std(whc, std);
+ else
+ std->qtd = NULL; /* so this std is re-added when the qset is */
+ }
+
+ pzl_qset_remove(whc, qset);
+ wurb->status = status;
+ wurb->is_async = false;
+ queue_work(whc->workqueue, &wurb->dequeue_work);
+
+out:
+ spin_unlock_irqrestore(&whc->lock, flags);
+
+ return ret;
+}
+
+/**
+ * pzl_qset_delete - delete a qset from the PZL
+ */
+void pzl_qset_delete(struct whc *whc, struct whc_qset *qset)
+{
+ qset->remove = 1;
+ queue_work(whc->workqueue, &whc->periodic_work);
+ qset_delete(whc, qset);
+}
+
+
+/**
+ * pzl_init - initialize the periodic zone list
+ * @whc: the WHCI host controller
+ */
+int pzl_init(struct whc *whc)
+{
+ int i;
+
+ whc->pz_list = dma_alloc_coherent(&whc->umc->dev, sizeof(u64) * 16,
+ &whc->pz_list_dma, GFP_KERNEL);
+ if (whc->pz_list == NULL)
+ return -ENOMEM;
+
+ /* Set T bit on all elements in PZL. */
+ for (i = 0; i < 16; i++)
+ whc->pz_list[i] = cpu_to_le64(QH_LINK_NTDS(8) | QH_LINK_T);
+
+ le_writeq(whc->pz_list_dma, whc->base + WUSBPERIODICLISTBASE);
+
+ return 0;
+}
+
+/**
+ * pzl_clean_up - free PZL resources
+ * @whc: the WHCI host controller
+ *
+ * The PZL is stopped and empty.
+ */
+void pzl_clean_up(struct whc *whc)
+{
+ if (whc->pz_list)
+ dma_free_coherent(&whc->umc->dev, sizeof(u64) * 16, whc->pz_list,
+ whc->pz_list_dma);
+}
diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c
new file mode 100644
index 000000000000..a20d00854104
--- /dev/null
+++ b/drivers/usb/host/whci/qset.c
@@ -0,0 +1,563 @@
+/*
+ * Wireless Host Controller (WHC) qset management.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+#include <linux/dma-mapping.h>
+#include <linux/uwb/umc.h>
+#include <linux/usb.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+void dump_qset(struct whc_qset *qset, struct device *dev)
+{
+ struct whc_std *std;
+ struct urb *urb = NULL;
+ int i;
+
+ dev_dbg(dev, "qset %08x\n", (u32)qset->qset_dma);
+ dev_dbg(dev, " -> %08x\n", (u32)qset->qh.link);
+ dev_dbg(dev, " info: %08x %08x %08x\n",
+ qset->qh.info1, qset->qh.info2, qset->qh.info3);
+ dev_dbg(dev, " sts: %04x errs: %d\n", qset->qh.status, qset->qh.err_count);
+ dev_dbg(dev, " TD: sts: %08x opts: %08x\n",
+ qset->qh.overlay.qtd.status, qset->qh.overlay.qtd.options);
+
+ for (i = 0; i < WHCI_QSET_TD_MAX; i++) {
+ dev_dbg(dev, " %c%c TD[%d]: sts: %08x opts: %08x ptr: %08x\n",
+ i == qset->td_start ? 'S' : ' ',
+ i == qset->td_end ? 'E' : ' ',
+ i, qset->qtd[i].status, qset->qtd[i].options,
+ (u32)qset->qtd[i].page_list_ptr);
+ }
+ dev_dbg(dev, " ntds: %d\n", qset->ntds);
+ list_for_each_entry(std, &qset->stds, list_node) {
+ if (urb != std->urb) {
+ urb = std->urb;
+ dev_dbg(dev, " urb %p transferred: %d bytes\n", urb,
+ urb->actual_length);
+ }
+ if (std->qtd)
+ dev_dbg(dev, " sTD[%td]: %zu bytes @ %08x\n",
+ std->qtd - &qset->qtd[0],
+ std->len, std->num_pointers ?
+ (u32)(std->pl_virt[0].buf_ptr) : (u32)std->dma_addr);
+ else
+ dev_dbg(dev, " sTD[-]: %zd bytes @ %08x\n",
+ std->len, std->num_pointers ?
+ (u32)(std->pl_virt[0].buf_ptr) : (u32)std->dma_addr);
+ }
+}
+
+struct whc_qset *qset_alloc(struct whc *whc, gfp_t mem_flags)
+{
+ struct whc_qset *qset;
+ dma_addr_t dma;
+
+ qset = dma_pool_alloc(whc->qset_pool, mem_flags, &dma);
+ if (qset == NULL)
+ return NULL;
+ memset(qset, 0, sizeof(struct whc_qset));
+
+ qset->qset_dma = dma;
+ qset->whc = whc;
+
+ INIT_LIST_HEAD(&qset->list_node);
+ INIT_LIST_HEAD(&qset->stds);
+
+ return qset;
+}
+
+/**
+ * qset_fill_qh - fill the static endpoint state in a qset's QHead
+ * @qset: the qset whose QH needs initializing with static endpoint
+ * state
+ * @urb: an urb for a transfer to this endpoint
+ */
+static void qset_fill_qh(struct whc_qset *qset, struct urb *urb)
+{
+ struct usb_device *usb_dev = urb->dev;
+ struct usb_wireless_ep_comp_descriptor *epcd;
+ bool is_out;
+
+ is_out = usb_pipeout(urb->pipe);
+
+ epcd = (struct usb_wireless_ep_comp_descriptor *)qset->ep->extra;
+
+ if (epcd) {
+ qset->max_seq = epcd->bMaxSequence;
+ qset->max_burst = epcd->bMaxBurst;
+ } else {
+ qset->max_seq = 2;
+ qset->max_burst = 1;
+ }
+
+ qset->qh.info1 = cpu_to_le32(
+ QH_INFO1_EP(usb_pipeendpoint(urb->pipe))
+ | (is_out ? QH_INFO1_DIR_OUT : QH_INFO1_DIR_IN)
+ | usb_pipe_to_qh_type(urb->pipe)
+ | QH_INFO1_DEV_INFO_IDX(wusb_port_no_to_idx(usb_dev->portnum))
+ | QH_INFO1_MAX_PKT_LEN(usb_maxpacket(urb->dev, urb->pipe, is_out))
+ );
+ qset->qh.info2 = cpu_to_le32(
+ QH_INFO2_BURST(qset->max_burst)
+ | QH_INFO2_DBP(0)
+ | QH_INFO2_MAX_COUNT(3)
+ | QH_INFO2_MAX_RETRY(3)
+ | QH_INFO2_MAX_SEQ(qset->max_seq - 1)
+ );
+ /* FIXME: where can we obtain these Tx parameters from? Why
+ * doesn't the chip know what Tx power to use? It knows the Rx
+ * strength and can presumably guess the Tx power required
+ * from that? */
+ qset->qh.info3 = cpu_to_le32(
+ QH_INFO3_TX_RATE_53_3
+ | QH_INFO3_TX_PWR(0) /* 0 == max power */
+ );
+}
+
+/**
+ * qset_clear - clear fields in a qset so it may be reinserted into a
+ * schedule
+ */
+void qset_clear(struct whc *whc, struct whc_qset *qset)
+{
+ qset->td_start = qset->td_end = qset->ntds = 0;
+ qset->remove = 0;
+
+ qset->qh.link = cpu_to_le32(QH_LINK_NTDS(8) | QH_LINK_T);
+ qset->qh.status = cpu_to_le16(QH_STATUS_ICUR(qset->td_start));
+ qset->qh.err_count = 0;
+ qset->qh.cur_window = cpu_to_le32((1 << qset->max_burst) - 1);
+ qset->qh.scratch[0] = 0;
+ qset->qh.scratch[1] = 0;
+ qset->qh.scratch[2] = 0;
+
+ memset(&qset->qh.overlay, 0, sizeof(qset->qh.overlay));
+
+ init_completion(&qset->remove_complete);
+}
+
+/**
+ * get_qset - get the qset for an async endpoint
+ *
+ * A new qset is created if one does not already exist.
+ */
+struct whc_qset *get_qset(struct whc *whc, struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct whc_qset *qset;
+
+ qset = urb->ep->hcpriv;
+ if (qset == NULL) {
+ qset = qset_alloc(whc, mem_flags);
+ if (qset == NULL)
+ return NULL;
+
+ qset->ep = urb->ep;
+ urb->ep->hcpriv = qset;
+ qset_fill_qh(qset, urb);
+ }
+ return qset;
+}
+
+void qset_remove_complete(struct whc *whc, struct whc_qset *qset)
+{
+ list_del_init(&qset->list_node);
+ complete(&qset->remove_complete);
+}
+
+/**
+ * qset_add_qtds - add qTDs for an URB to a qset
+ *
+ * Returns true if the list (ASL/PZL) must be updated because (for a
+ * WHCI 0.95 controller) an activated qTD was pointed to be iCur.
+ */
+enum whc_update qset_add_qtds(struct whc *whc, struct whc_qset *qset)
+{
+ struct whc_std *std;
+ enum whc_update update = 0;
+
+ list_for_each_entry(std, &qset->stds, list_node) {
+ struct whc_qtd *qtd;
+ uint32_t status;
+
+ if (qset->ntds >= WHCI_QSET_TD_MAX
+ || (qset->pause_after_urb && std->urb != qset->pause_after_urb))
+ break;
+
+ if (std->qtd)
+ continue; /* already has a qTD */
+
+ qtd = std->qtd = &qset->qtd[qset->td_end];
+
+ /* Fill in setup bytes for control transfers. */
+ if (usb_pipecontrol(std->urb->pipe))
+ memcpy(qtd->setup, std->urb->setup_packet, 8);
+
+ status = QTD_STS_ACTIVE | QTD_STS_LEN(std->len);
+
+ if (whc_std_last(std) && usb_pipeout(std->urb->pipe))
+ status |= QTD_STS_LAST_PKT;
+
+ /*
+ * For an IN transfer the iAlt field should be set so
+ * the h/w will automatically advance to the next
+ * transfer. However, if there are 8 or more TDs
+ * remaining in this transfer then iAlt cannot be set
+ * as it could point to somewhere in this transfer.
+ */
+ if (std->ntds_remaining < WHCI_QSET_TD_MAX) {
+ int ialt;
+ ialt = (qset->td_end + std->ntds_remaining) % WHCI_QSET_TD_MAX;
+ status |= QTD_STS_IALT(ialt);
+ } else if (usb_pipein(std->urb->pipe))
+ qset->pause_after_urb = std->urb;
+
+ if (std->num_pointers)
+ qtd->options = cpu_to_le32(QTD_OPT_IOC);
+ else
+ qtd->options = cpu_to_le32(QTD_OPT_IOC | QTD_OPT_SMALL);
+ qtd->page_list_ptr = cpu_to_le64(std->dma_addr);
+
+ qtd->status = cpu_to_le32(status);
+
+ if (QH_STATUS_TO_ICUR(qset->qh.status) == qset->td_end)
+ update = WHC_UPDATE_UPDATED;
+
+ if (++qset->td_end >= WHCI_QSET_TD_MAX)
+ qset->td_end = 0;
+ qset->ntds++;
+ }
+
+ return update;
+}
+
+/**
+ * qset_remove_qtd - remove the first qTD from a qset.
+ *
+ * The qTD might be still active (if it's part of a IN URB that
+ * resulted in a short read) so ensure it's deactivated.
+ */
+static void qset_remove_qtd(struct whc *whc, struct whc_qset *qset)
+{
+ qset->qtd[qset->td_start].status = 0;
+
+ if (++qset->td_start >= WHCI_QSET_TD_MAX)
+ qset->td_start = 0;
+ qset->ntds--;
+}
+
+/**
+ * qset_free_std - remove an sTD and free it.
+ * @whc: the WHCI host controller
+ * @std: the sTD to remove and free.
+ */
+void qset_free_std(struct whc *whc, struct whc_std *std)
+{
+ list_del(&std->list_node);
+ if (std->num_pointers)
+ dma_unmap_single(whc->wusbhc.dev, std->dma_addr,
+ std->num_pointers * sizeof(struct whc_page_list_entry),
+ DMA_TO_DEVICE);
+ kfree(std);
+}
+
+/**
+ * qset_remove_qtds - remove an URB's qTDs (and sTDs).
+ */
+static void qset_remove_qtds(struct whc *whc, struct whc_qset *qset,
+ struct urb *urb)
+{
+ struct whc_std *std, *t;
+
+ list_for_each_entry_safe(std, t, &qset->stds, list_node) {
+ if (std->urb != urb)
+ break;
+ if (std->qtd != NULL)
+ qset_remove_qtd(whc, qset);
+ qset_free_std(whc, std);
+ }
+}
+
+/**
+ * qset_free_stds - free any remaining sTDs for an URB.
+ */
+static void qset_free_stds(struct whc_qset *qset, struct urb *urb)
+{
+ struct whc_std *std, *t;
+
+ list_for_each_entry_safe(std, t, &qset->stds, list_node) {
+ if (std->urb == urb)
+ qset_free_std(qset->whc, std);
+ }
+}
+
+static int qset_fill_page_list(struct whc *whc, struct whc_std *std, gfp_t mem_flags)
+{
+ dma_addr_t dma_addr = std->dma_addr;
+ dma_addr_t sp, ep;
+ size_t std_len = std->len;
+ size_t pl_len;
+ int p;
+
+ sp = ALIGN(dma_addr, WHCI_PAGE_SIZE);
+ ep = dma_addr + std_len;
+ std->num_pointers = DIV_ROUND_UP(ep - sp, WHCI_PAGE_SIZE);
+
+ pl_len = std->num_pointers * sizeof(struct whc_page_list_entry);
+ std->pl_virt = kmalloc(pl_len, mem_flags);
+ if (std->pl_virt == NULL)
+ return -ENOMEM;
+ std->dma_addr = dma_map_single(whc->wusbhc.dev, std->pl_virt, pl_len, DMA_TO_DEVICE);
+
+ for (p = 0; p < std->num_pointers; p++) {
+ std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr);
+ dma_addr = ALIGN(dma_addr + WHCI_PAGE_SIZE, WHCI_PAGE_SIZE);
+ }
+
+ return 0;
+}
+
+/**
+ * urb_dequeue_work - executes asl/pzl update and gives back the urb to the system.
+ */
+static void urb_dequeue_work(struct work_struct *work)
+{
+ struct whc_urb *wurb = container_of(work, struct whc_urb, dequeue_work);
+ struct whc_qset *qset = wurb->qset;
+ struct whc *whc = qset->whc;
+ unsigned long flags;
+
+ if (wurb->is_async == true)
+ asl_update(whc, WUSBCMD_ASYNC_UPDATED
+ | WUSBCMD_ASYNC_SYNCED_DB
+ | WUSBCMD_ASYNC_QSET_RM);
+ else
+ pzl_update(whc, WUSBCMD_PERIODIC_UPDATED
+ | WUSBCMD_PERIODIC_SYNCED_DB
+ | WUSBCMD_PERIODIC_QSET_RM);
+
+ spin_lock_irqsave(&whc->lock, flags);
+ qset_remove_urb(whc, qset, wurb->urb, wurb->status);
+ spin_unlock_irqrestore(&whc->lock, flags);
+}
+
+/**
+ * qset_add_urb - add an urb to the qset's queue.
+ *
+ * The URB is chopped into sTDs, one for each qTD that will required.
+ * At least one qTD (and sTD) is required even if the transfer has no
+ * data (e.g., for some control transfers).
+ */
+int qset_add_urb(struct whc *whc, struct whc_qset *qset, struct urb *urb,
+ gfp_t mem_flags)
+{
+ struct whc_urb *wurb;
+ int remaining = urb->transfer_buffer_length;
+ u64 transfer_dma = urb->transfer_dma;
+ int ntds_remaining;
+
+ ntds_remaining = DIV_ROUND_UP(remaining, QTD_MAX_XFER_SIZE);
+ if (ntds_remaining == 0)
+ ntds_remaining = 1;
+
+ wurb = kzalloc(sizeof(struct whc_urb), mem_flags);
+ if (wurb == NULL)
+ goto err_no_mem;
+ urb->hcpriv = wurb;
+ wurb->qset = qset;
+ wurb->urb = urb;
+ INIT_WORK(&wurb->dequeue_work, urb_dequeue_work);
+
+ while (ntds_remaining) {
+ struct whc_std *std;
+ size_t std_len;
+
+ std = kmalloc(sizeof(struct whc_std), mem_flags);
+ if (std == NULL)
+ goto err_no_mem;
+
+ std_len = remaining;
+ if (std_len > QTD_MAX_XFER_SIZE)
+ std_len = QTD_MAX_XFER_SIZE;
+
+ std->urb = urb;
+ std->dma_addr = transfer_dma;
+ std->len = std_len;
+ std->ntds_remaining = ntds_remaining;
+ std->qtd = NULL;
+
+ INIT_LIST_HEAD(&std->list_node);
+ list_add_tail(&std->list_node, &qset->stds);
+
+ if (std_len > WHCI_PAGE_SIZE) {
+ if (qset_fill_page_list(whc, std, mem_flags) < 0)
+ goto err_no_mem;
+ } else
+ std->num_pointers = 0;
+
+ ntds_remaining--;
+ remaining -= std_len;
+ transfer_dma += std_len;
+ }
+
+ return 0;
+
+err_no_mem:
+ qset_free_stds(qset, urb);
+ return -ENOMEM;
+}
+
+/**
+ * qset_remove_urb - remove an URB from the urb queue.
+ *
+ * The URB is returned to the USB subsystem.
+ */
+void qset_remove_urb(struct whc *whc, struct whc_qset *qset,
+ struct urb *urb, int status)
+{
+ struct wusbhc *wusbhc = &whc->wusbhc;
+
+ usb_hcd_unlink_urb_from_ep(&wusbhc->usb_hcd, urb);
+ /* Drop the lock as urb->complete() may enqueue another urb. */
+ spin_unlock(&whc->lock);
+ wusbhc_giveback_urb(wusbhc, urb, status);
+ spin_lock(&whc->lock);
+
+ kfree(urb->hcpriv);
+}
+
+/**
+ * get_urb_status_from_qtd - get the completed urb status from qTD status
+ * @urb: completed urb
+ * @status: qTD status
+ */
+static int get_urb_status_from_qtd(struct urb *urb, u32 status)
+{
+ if (status & QTD_STS_HALTED) {
+ if (status & QTD_STS_DBE)
+ return usb_pipein(urb->pipe) ? -ENOSR : -ECOMM;
+ else if (status & QTD_STS_BABBLE)
+ return -EOVERFLOW;
+ else if (status & QTD_STS_RCE)
+ return -ETIME;
+ return -EPIPE;
+ }
+ if (usb_pipein(urb->pipe)
+ && (urb->transfer_flags & URB_SHORT_NOT_OK)
+ && urb->actual_length < urb->transfer_buffer_length)
+ return -EREMOTEIO;
+ return 0;
+}
+
+/**
+ * process_inactive_qtd - process an inactive (but not halted) qTD.
+ *
+ * Update the urb with the transfer bytes from the qTD, if the urb is
+ * completely transfered or (in the case of an IN only) the LPF is
+ * set, then the transfer is complete and the urb should be returned
+ * to the system.
+ */
+void process_inactive_qtd(struct whc *whc, struct whc_qset *qset,
+ struct whc_qtd *qtd)
+{
+ struct whc_std *std = list_first_entry(&qset->stds, struct whc_std, list_node);
+ struct urb *urb = std->urb;
+ uint32_t status;
+ bool complete;
+
+ status = le32_to_cpu(qtd->status);
+
+ urb->actual_length += std->len - QTD_STS_TO_LEN(status);
+
+ if (usb_pipein(urb->pipe) && (status & QTD_STS_LAST_PKT))
+ complete = true;
+ else
+ complete = whc_std_last(std);
+
+ qset_remove_qtd(whc, qset);
+ qset_free_std(whc, std);
+
+ /*
+ * Transfers for this URB are complete? Then return it to the
+ * USB subsystem.
+ */
+ if (complete) {
+ qset_remove_qtds(whc, qset, urb);
+ qset_remove_urb(whc, qset, urb, get_urb_status_from_qtd(urb, status));
+
+ /*
+ * If iAlt isn't valid then the hardware didn't
+ * advance iCur. Adjust the start and end pointers to
+ * match iCur.
+ */
+ if (!(status & QTD_STS_IALT_VALID))
+ qset->td_start = qset->td_end
+ = QH_STATUS_TO_ICUR(le16_to_cpu(qset->qh.status));
+ qset->pause_after_urb = NULL;
+ }
+}
+
+/**
+ * process_halted_qtd - process a qset with a halted qtd
+ *
+ * Remove all the qTDs for the failed URB and return the failed URB to
+ * the USB subsystem. Then remove all other qTDs so the qset can be
+ * removed.
+ *
+ * FIXME: this is the point where rate adaptation can be done. If a
+ * transfer failed because it exceeded the maximum number of retries
+ * then it could be reactivated with a slower rate without having to
+ * remove the qset.
+ */
+void process_halted_qtd(struct whc *whc, struct whc_qset *qset,
+ struct whc_qtd *qtd)
+{
+ struct whc_std *std = list_first_entry(&qset->stds, struct whc_std, list_node);
+ struct urb *urb = std->urb;
+ int urb_status;
+
+ urb_status = get_urb_status_from_qtd(urb, le32_to_cpu(qtd->status));
+
+ qset_remove_qtds(whc, qset, urb);
+ qset_remove_urb(whc, qset, urb, urb_status);
+
+ list_for_each_entry(std, &qset->stds, list_node) {
+ if (qset->ntds == 0)
+ break;
+ qset_remove_qtd(whc, qset);
+ std->qtd = NULL;
+ }
+
+ qset->remove = 1;
+}
+
+void qset_free(struct whc *whc, struct whc_qset *qset)
+{
+ dma_pool_free(whc->qset_pool, qset, qset->qset_dma);
+}
+
+/**
+ * qset_delete - wait for a qset to be unused, then free it.
+ */
+void qset_delete(struct whc *whc, struct whc_qset *qset)
+{
+ wait_for_completion(&qset->remove_complete);
+ qset_free(whc, qset);
+}
diff --git a/drivers/usb/host/whci/whcd.h b/drivers/usb/host/whci/whcd.h
new file mode 100644
index 000000000000..0ed0e7753249
--- /dev/null
+++ b/drivers/usb/host/whci/whcd.h
@@ -0,0 +1,195 @@
+/*
+ * Wireless Host Controller (WHC) private header.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+#ifndef __WHCD_H
+#define __WHCD_H
+
+#include <linux/uwb/whci.h>
+#include <linux/workqueue.h>
+
+#include "whci-hc.h"
+
+/* Generic command timeout. */
+#define WHC_GENCMD_TIMEOUT_MS 100
+
+
+struct whc {
+ struct wusbhc wusbhc;
+ struct umc_dev *umc;
+
+ resource_size_t base_phys;
+ void __iomem *base;
+ int irq;
+
+ u8 n_devices;
+ u8 n_keys;
+ u8 n_mmc_ies;
+
+ u64 *pz_list;
+ struct dn_buf_entry *dn_buf;
+ struct di_buf_entry *di_buf;
+ dma_addr_t pz_list_dma;
+ dma_addr_t dn_buf_dma;
+ dma_addr_t di_buf_dma;
+
+ spinlock_t lock;
+ struct mutex mutex;
+
+ wait_queue_head_t cmd_wq;
+
+ struct workqueue_struct *workqueue;
+ struct work_struct dn_work;
+
+ struct dma_pool *qset_pool;
+
+ struct list_head async_list;
+ struct list_head async_removed_list;
+ wait_queue_head_t async_list_wq;
+ struct work_struct async_work;
+
+ struct list_head periodic_list[5];
+ struct list_head periodic_removed_list;
+ wait_queue_head_t periodic_list_wq;
+ struct work_struct periodic_work;
+};
+
+#define wusbhc_to_whc(w) (container_of((w), struct whc, wusbhc))
+
+/**
+ * struct whc_std - a software TD.
+ * @urb: the URB this sTD is for.
+ * @offset: start of the URB's data for this TD.
+ * @len: the length of data in the associated TD.
+ * @ntds_remaining: number of TDs (starting from this one) in this transfer.
+ *
+ * Queued URBs may require more TDs than are available in a qset so we
+ * use a list of these "software TDs" (sTDs) to hold per-TD data.
+ */
+struct whc_std {
+ struct urb *urb;
+ size_t len;
+ int ntds_remaining;
+ struct whc_qtd *qtd;
+
+ struct list_head list_node;
+ int num_pointers;
+ dma_addr_t dma_addr;
+ struct whc_page_list_entry *pl_virt;
+};
+
+/**
+ * struct whc_urb - per URB host controller structure.
+ * @urb: the URB this struct is for.
+ * @qset: the qset associated to the URB.
+ * @dequeue_work: the work to remove the URB when dequeued.
+ * @is_async: the URB belongs to async sheduler or not.
+ * @status: the status to be returned when calling wusbhc_giveback_urb.
+ */
+struct whc_urb {
+ struct urb *urb;
+ struct whc_qset *qset;
+ struct work_struct dequeue_work;
+ bool is_async;
+ int status;
+};
+
+/**
+ * whc_std_last - is this sTD the URB's last?
+ * @std: the sTD to check.
+ */
+static inline bool whc_std_last(struct whc_std *std)
+{
+ return std->ntds_remaining <= 1;
+}
+
+enum whc_update {
+ WHC_UPDATE_ADDED = 0x01,
+ WHC_UPDATE_REMOVED = 0x02,
+ WHC_UPDATE_UPDATED = 0x04,
+};
+
+/* init.c */
+int whc_init(struct whc *whc);
+void whc_clean_up(struct whc *whc);
+
+/* hw.c */
+void whc_write_wusbcmd(struct whc *whc, u32 mask, u32 val);
+int whc_do_gencmd(struct whc *whc, u32 cmd, u32 params, void *addr, size_t len);
+
+/* wusb.c */
+int whc_wusbhc_start(struct wusbhc *wusbhc);
+void whc_wusbhc_stop(struct wusbhc *wusbhc);
+int whc_mmcie_add(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt,
+ u8 handle, struct wuie_hdr *wuie);
+int whc_mmcie_rm(struct wusbhc *wusbhc, u8 handle);
+int whc_bwa_set(struct wusbhc *wusbhc, s8 stream_index, const struct uwb_mas_bm *mas_bm);
+int whc_dev_info_set(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev);
+int whc_set_num_dnts(struct wusbhc *wusbhc, u8 interval, u8 slots);
+int whc_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid,
+ const void *ptk, size_t key_size);
+int whc_set_gtk(struct wusbhc *wusbhc, u32 tkid,
+ const void *gtk, size_t key_size);
+int whc_set_cluster_id(struct whc *whc, u8 bcid);
+
+/* int.c */
+irqreturn_t whc_int_handler(struct usb_hcd *hcd);
+void whc_dn_work(struct work_struct *work);
+
+/* asl.c */
+void asl_start(struct whc *whc);
+void asl_stop(struct whc *whc);
+int asl_init(struct whc *whc);
+void asl_clean_up(struct whc *whc);
+int asl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags);
+int asl_urb_dequeue(struct whc *whc, struct urb *urb, int status);
+void asl_qset_delete(struct whc *whc, struct whc_qset *qset);
+void scan_async_work(struct work_struct *work);
+
+/* pzl.c */
+int pzl_init(struct whc *whc);
+void pzl_clean_up(struct whc *whc);
+void pzl_start(struct whc *whc);
+void pzl_stop(struct whc *whc);
+int pzl_urb_enqueue(struct whc *whc, struct urb *urb, gfp_t mem_flags);
+int pzl_urb_dequeue(struct whc *whc, struct urb *urb, int status);
+void pzl_qset_delete(struct whc *whc, struct whc_qset *qset);
+void scan_periodic_work(struct work_struct *work);
+
+/* qset.c */
+struct whc_qset *qset_alloc(struct whc *whc, gfp_t mem_flags);
+void qset_free(struct whc *whc, struct whc_qset *qset);
+struct whc_qset *get_qset(struct whc *whc, struct urb *urb, gfp_t mem_flags);
+void qset_delete(struct whc *whc, struct whc_qset *qset);
+void qset_clear(struct whc *whc, struct whc_qset *qset);
+int qset_add_urb(struct whc *whc, struct whc_qset *qset, struct urb *urb,
+ gfp_t mem_flags);
+void qset_free_std(struct whc *whc, struct whc_std *std);
+void qset_remove_urb(struct whc *whc, struct whc_qset *qset,
+ struct urb *urb, int status);
+void process_halted_qtd(struct whc *whc, struct whc_qset *qset,
+ struct whc_qtd *qtd);
+void process_inactive_qtd(struct whc *whc, struct whc_qset *qset,
+ struct whc_qtd *qtd);
+enum whc_update qset_add_qtds(struct whc *whc, struct whc_qset *qset);
+void qset_remove_complete(struct whc *whc, struct whc_qset *qset);
+void dump_qset(struct whc_qset *qset, struct device *dev);
+void pzl_update(struct whc *whc, uint32_t wusbcmd);
+void asl_update(struct whc *whc, uint32_t wusbcmd);
+
+#endif /* #ifndef __WHCD_H */
diff --git a/drivers/usb/host/whci/whci-hc.h b/drivers/usb/host/whci/whci-hc.h
new file mode 100644
index 000000000000..cb5fc94180d4
--- /dev/null
+++ b/drivers/usb/host/whci/whci-hc.h
@@ -0,0 +1,413 @@
+/*
+ * Wireless Host Controller (WHC) data structures.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+#ifndef _WHCI_WHCI_HC_H
+#define _WHCI_WHCI_HC_H
+
+#include <linux/list.h>
+
+/**
+ * WHCI_PAGE_SIZE - page size use by WHCI
+ *
+ * WHCI assumes that host system uses pages of 4096 octets.
+ */
+#define WHCI_PAGE_SIZE 4096
+
+
+/**
+ * QTD_MAX_TXFER_SIZE - max number of bytes to transfer with a single
+ * qtd.
+ *
+ * This is 2^20 - 1.
+ */
+#define QTD_MAX_XFER_SIZE 1048575
+
+
+/**
+ * struct whc_qtd - Queue Element Transfer Descriptors (qTD)
+ *
+ * This describes the data for a bulk, control or interrupt transfer.
+ *
+ * [WHCI] section 3.2.4
+ */
+struct whc_qtd {
+ __le32 status; /*< remaining transfer len and transfer status */
+ __le32 options;
+ __le64 page_list_ptr; /*< physical pointer to data buffer page list*/
+ __u8 setup[8]; /*< setup data for control transfers */
+} __attribute__((packed));
+
+#define QTD_STS_ACTIVE (1 << 31) /* enable execution of transaction */
+#define QTD_STS_HALTED (1 << 30) /* transfer halted */
+#define QTD_STS_DBE (1 << 29) /* data buffer error */
+#define QTD_STS_BABBLE (1 << 28) /* babble detected */
+#define QTD_STS_RCE (1 << 27) /* retry count exceeded */
+#define QTD_STS_LAST_PKT (1 << 26) /* set Last Packet Flag in WUSB header */
+#define QTD_STS_INACTIVE (1 << 25) /* queue set is marked inactive */
+#define QTD_STS_IALT_VALID (1 << 23) /* iAlt field is valid */
+#define QTD_STS_IALT(i) (QTD_STS_IALT_VALID | ((i) << 20)) /* iAlt field */
+#define QTD_STS_LEN(l) ((l) << 0) /* transfer length */
+#define QTD_STS_TO_LEN(s) ((s) & 0x000fffff)
+
+#define QTD_OPT_IOC (1 << 1) /* page_list_ptr points to buffer directly */
+#define QTD_OPT_SMALL (1 << 0) /* interrupt on complete */
+
+/**
+ * struct whc_itd - Isochronous Queue Element Transfer Descriptors (iTD)
+ *
+ * This describes the data and other parameters for an isochronous
+ * transfer.
+ *
+ * [WHCI] section 3.2.5
+ */
+struct whc_itd {
+ __le16 presentation_time; /*< presentation time for OUT transfers */
+ __u8 num_segments; /*< number of data segments in segment list */
+ __u8 status; /*< command execution status */
+ __le32 options; /*< misc transfer options */
+ __le64 page_list_ptr; /*< physical pointer to data buffer page list */
+ __le64 seg_list_ptr; /*< physical pointer to segment list */
+} __attribute__((packed));
+
+#define ITD_STS_ACTIVE (1 << 7) /* enable execution of transaction */
+#define ITD_STS_DBE (1 << 5) /* data buffer error */
+#define ITD_STS_BABBLE (1 << 4) /* babble detected */
+#define ITD_STS_INACTIVE (1 << 1) /* queue set is marked inactive */
+
+#define ITD_OPT_IOC (1 << 1) /* interrupt on complete */
+#define ITD_OPT_SMALL (1 << 0) /* page_list_ptr points to buffer directly */
+
+/**
+ * Page list entry.
+ *
+ * A TD's page list must contain sufficient page list entries for the
+ * total data length in the TD.
+ *
+ * [WHCI] section 3.2.4.3
+ */
+struct whc_page_list_entry {
+ __le64 buf_ptr; /*< physical pointer to buffer */
+} __attribute__((packed));
+
+/**
+ * struct whc_seg_list_entry - Segment list entry.
+ *
+ * Describes a portion of the data buffer described in the containing
+ * qTD's page list.
+ *
+ * seg_ptr = qtd->page_list_ptr[qtd->seg_list_ptr[seg].idx].buf_ptr
+ * + qtd->seg_list_ptr[seg].offset;
+ *
+ * Segments can't cross page boundries.
+ *
+ * [WHCI] section 3.2.5.5
+ */
+struct whc_seg_list_entry {
+ __le16 len; /*< segment length */
+ __u8 idx; /*< index into page list */
+ __u8 status; /*< segment status */
+ __le16 offset; /*< 12 bit offset into page */
+} __attribute__((packed));
+
+/**
+ * struct whc_qhead - endpoint and status information for a qset.
+ *
+ * [WHCI] section 3.2.6
+ */
+struct whc_qhead {
+ __le64 link; /*< next qset in list */
+ __le32 info1;
+ __le32 info2;
+ __le32 info3;
+ __le16 status;
+ __le16 err_count; /*< transaction error count */
+ __le32 cur_window;
+ __le32 scratch[3]; /*< h/w scratch area */
+ union {
+ struct whc_qtd qtd;
+ struct whc_itd itd;
+ } overlay;
+} __attribute__((packed));
+
+#define QH_LINK_PTR_MASK (~0x03Full)
+#define QH_LINK_PTR(ptr) ((ptr) & QH_LINK_PTR_MASK)
+#define QH_LINK_IQS (1 << 4) /* isochronous queue set */
+#define QH_LINK_NTDS(n) (((n) - 1) << 1) /* number of TDs in queue set */
+#define QH_LINK_T (1 << 0) /* last queue set in periodic schedule list */
+
+#define QH_INFO1_EP(e) ((e) << 0) /* endpoint number */
+#define QH_INFO1_DIR_IN (1 << 4) /* IN transfer */
+#define QH_INFO1_DIR_OUT (0 << 4) /* OUT transfer */
+#define QH_INFO1_TR_TYPE_CTRL (0x0 << 5) /* control transfer */
+#define QH_INFO1_TR_TYPE_ISOC (0x1 << 5) /* isochronous transfer */
+#define QH_INFO1_TR_TYPE_BULK (0x2 << 5) /* bulk transfer */
+#define QH_INFO1_TR_TYPE_INT (0x3 << 5) /* interrupt */
+#define QH_INFO1_TR_TYPE_LP_INT (0x7 << 5) /* low power interrupt */
+#define QH_INFO1_DEV_INFO_IDX(i) ((i) << 8) /* index into device info buffer */
+#define QH_INFO1_SET_INACTIVE (1 << 15) /* set inactive after transfer */
+#define QH_INFO1_MAX_PKT_LEN(l) ((l) << 16) /* maximum packet length */
+
+#define QH_INFO2_BURST(b) ((b) << 0) /* maximum burst length */
+#define QH_INFO2_DBP(p) ((p) << 5) /* data burst policy (see [WUSB] table 5-7) */
+#define QH_INFO2_MAX_COUNT(c) ((c) << 8) /* max isoc/int pkts per zone */
+#define QH_INFO2_RQS (1 << 15) /* reactivate queue set */
+#define QH_INFO2_MAX_RETRY(r) ((r) << 16) /* maximum transaction retries */
+#define QH_INFO2_MAX_SEQ(s) ((s) << 20) /* maximum sequence number */
+#define QH_INFO3_MAX_DELAY(d) ((d) << 0) /* maximum stream delay in 125 us units (isoc only) */
+#define QH_INFO3_INTERVAL(i) ((i) << 16) /* segment interval in 125 us units (isoc only) */
+
+#define QH_INFO3_TX_RATE_53_3 (0 << 24)
+#define QH_INFO3_TX_RATE_80 (1 << 24)
+#define QH_INFO3_TX_RATE_106_7 (2 << 24)
+#define QH_INFO3_TX_RATE_160 (3 << 24)
+#define QH_INFO3_TX_RATE_200 (4 << 24)
+#define QH_INFO3_TX_RATE_320 (5 << 24)
+#define QH_INFO3_TX_RATE_400 (6 << 24)
+#define QH_INFO3_TX_RATE_480 (7 << 24)
+#define QH_INFO3_TX_PWR(p) ((p) << 29) /* transmit power (see [WUSB] section 5.2.1.2) */
+
+#define QH_STATUS_FLOW_CTRL (1 << 15)
+#define QH_STATUS_ICUR(i) ((i) << 5)
+#define QH_STATUS_TO_ICUR(s) (((s) >> 5) & 0x7)
+
+/**
+ * usb_pipe_to_qh_type - USB core pipe type to QH transfer type
+ *
+ * Returns the QH type field for a USB core pipe type.
+ */
+static inline unsigned usb_pipe_to_qh_type(unsigned pipe)
+{
+ static const unsigned type[] = {
+ [PIPE_ISOCHRONOUS] = QH_INFO1_TR_TYPE_ISOC,
+ [PIPE_INTERRUPT] = QH_INFO1_TR_TYPE_INT,
+ [PIPE_CONTROL] = QH_INFO1_TR_TYPE_CTRL,
+ [PIPE_BULK] = QH_INFO1_TR_TYPE_BULK,
+ };
+ return type[usb_pipetype(pipe)];
+}
+
+/**
+ * Maxiumum number of TDs in a qset.
+ */
+#define WHCI_QSET_TD_MAX 8
+
+/**
+ * struct whc_qset - WUSB data transfers to a specific endpoint
+ * @qh: the QHead of this qset
+ * @qtd: up to 8 qTDs (for qsets for control, bulk and interrupt
+ * transfers)
+ * @itd: up to 8 iTDs (for qsets for isochronous transfers)
+ * @qset_dma: DMA address for this qset
+ * @whc: WHCI HC this qset is for
+ * @ep: endpoint
+ * @stds: list of sTDs queued to this qset
+ * @ntds: number of qTDs queued (not necessarily the same as nTDs
+ * field in the QH)
+ * @td_start: index of the first qTD in the list
+ * @td_end: index of next free qTD in the list (provided
+ * ntds < WHCI_QSET_TD_MAX)
+ *
+ * Queue Sets (qsets) are added to the asynchronous schedule list
+ * (ASL) or the periodic zone list (PZL).
+ *
+ * qsets may contain up to 8 TDs (either qTDs or iTDs as appropriate).
+ * Each TD may refer to at most 1 MiB of data. If a single transfer
+ * has > 8MiB of data, TDs can be reused as they are completed since
+ * the TD list is used as a circular buffer. Similarly, several
+ * (smaller) transfers may be queued in a qset.
+ *
+ * WHCI controllers may cache portions of the qsets in the ASL and
+ * PZL, requiring the WHCD to inform the WHC that the lists have been
+ * updated (fields changed or qsets inserted or removed). For safe
+ * insertion and removal of qsets from the lists the schedule must be
+ * stopped to avoid races in updating the QH link pointers.
+ *
+ * Since the HC is free to execute qsets in any order, all transfers
+ * to an endpoint should use the same qset to ensure transfers are
+ * executed in the order they're submitted.
+ *
+ * [WHCI] section 3.2.3
+ */
+struct whc_qset {
+ struct whc_qhead qh;
+ union {
+ struct whc_qtd qtd[WHCI_QSET_TD_MAX];
+ struct whc_itd itd[WHCI_QSET_TD_MAX];
+ };
+
+ /* private data for WHCD */
+ dma_addr_t qset_dma;
+ struct whc *whc;
+ struct usb_host_endpoint *ep;
+ struct list_head stds;
+ int ntds;
+ int td_start;
+ int td_end;
+ struct list_head list_node;
+ unsigned in_sw_list:1;
+ unsigned in_hw_list:1;
+ unsigned remove:1;
+ struct urb *pause_after_urb;
+ struct completion remove_complete;
+ int max_burst;
+ int max_seq;
+};
+
+static inline void whc_qset_set_link_ptr(u64 *ptr, u64 target)
+{
+ if (target)
+ *ptr = (*ptr & ~(QH_LINK_PTR_MASK | QH_LINK_T)) | QH_LINK_PTR(target);
+ else
+ *ptr = QH_LINK_T;
+}
+
+/**
+ * struct di_buf_entry - Device Information (DI) buffer entry.
+ *
+ * There's one of these per connected device.
+ */
+struct di_buf_entry {
+ __le32 availability_info[8]; /*< MAS availability information, one MAS per bit */
+ __le32 addr_sec_info; /*< addressing and security info */
+ __le32 reserved[7];
+} __attribute__((packed));
+
+#define WHC_DI_SECURE (1 << 31)
+#define WHC_DI_DISABLE (1 << 30)
+#define WHC_DI_KEY_IDX(k) ((k) << 8)
+#define WHC_DI_KEY_IDX_MASK 0x0000ff00
+#define WHC_DI_DEV_ADDR(a) ((a) << 0)
+#define WHC_DI_DEV_ADDR_MASK 0x000000ff
+
+/**
+ * struct dn_buf_entry - Device Notification (DN) buffer entry.
+ *
+ * [WHCI] section 3.2.8
+ */
+struct dn_buf_entry {
+ __u8 msg_size; /*< number of octets of valid DN data */
+ __u8 reserved1;
+ __u8 src_addr; /*< source address */
+ __u8 status; /*< buffer entry status */
+ __le32 tkid; /*< TKID for source device, valid if secure bit is set */
+ __u8 dn_data[56]; /*< up to 56 octets of DN data */
+} __attribute__((packed));
+
+#define WHC_DN_STATUS_VALID (1 << 7) /* buffer entry is valid */
+#define WHC_DN_STATUS_SECURE (1 << 6) /* notification received using secure frame */
+
+#define WHC_N_DN_ENTRIES (4096 / sizeof(struct dn_buf_entry))
+
+
+/*
+ * HC registers.
+ *
+ * [WHCI] section 2.4
+ */
+
+#define WHCIVERSION 0x00
+
+#define WHCSPARAMS 0x04
+# define WHCSPARAMS_TO_N_MMC_IES(p) (((p) >> 16) & 0xff)
+# define WHCSPARAMS_TO_N_KEYS(p) (((p) >> 8) & 0xff)
+# define WHCSPARAMS_TO_N_DEVICES(p) (((p) >> 0) & 0x7f)
+
+#define WUSBCMD 0x08
+# define WUSBCMD_BCID(b) ((b) << 16)
+# define WUSBCMD_BCID_MASK (0xff << 16)
+# define WUSBCMD_ASYNC_QSET_RM (1 << 12)
+# define WUSBCMD_PERIODIC_QSET_RM (1 << 11)
+# define WUSBCMD_WUSBSI(s) ((s) << 8)
+# define WUSBCMD_WUSBSI_MASK (0x7 << 8)
+# define WUSBCMD_ASYNC_SYNCED_DB (1 << 7)
+# define WUSBCMD_PERIODIC_SYNCED_DB (1 << 6)
+# define WUSBCMD_ASYNC_UPDATED (1 << 5)
+# define WUSBCMD_PERIODIC_UPDATED (1 << 4)
+# define WUSBCMD_ASYNC_EN (1 << 3)
+# define WUSBCMD_PERIODIC_EN (1 << 2)
+# define WUSBCMD_WHCRESET (1 << 1)
+# define WUSBCMD_RUN (1 << 0)
+
+#define WUSBSTS 0x0c
+# define WUSBSTS_ASYNC_SCHED (1 << 15)
+# define WUSBSTS_PERIODIC_SCHED (1 << 14)
+# define WUSBSTS_DNTS_SCHED (1 << 13)
+# define WUSBSTS_HCHALTED (1 << 12)
+# define WUSBSTS_GEN_CMD_DONE (1 << 9)
+# define WUSBSTS_CHAN_TIME_ROLLOVER (1 << 8)
+# define WUSBSTS_DNTS_OVERFLOW (1 << 7)
+# define WUSBSTS_BPST_ADJUSTMENT_CHANGED (1 << 6)
+# define WUSBSTS_HOST_ERR (1 << 5)
+# define WUSBSTS_ASYNC_SCHED_SYNCED (1 << 4)
+# define WUSBSTS_PERIODIC_SCHED_SYNCED (1 << 3)
+# define WUSBSTS_DNTS_INT (1 << 2)
+# define WUSBSTS_ERR_INT (1 << 1)
+# define WUSBSTS_INT (1 << 0)
+# define WUSBSTS_INT_MASK 0x3ff
+
+#define WUSBINTR 0x10
+# define WUSBINTR_GEN_CMD_DONE (1 << 9)
+# define WUSBINTR_CHAN_TIME_ROLLOVER (1 << 8)
+# define WUSBINTR_DNTS_OVERFLOW (1 << 7)
+# define WUSBINTR_BPST_ADJUSTMENT_CHANGED (1 << 6)
+# define WUSBINTR_HOST_ERR (1 << 5)
+# define WUSBINTR_ASYNC_SCHED_SYNCED (1 << 4)
+# define WUSBINTR_PERIODIC_SCHED_SYNCED (1 << 3)
+# define WUSBINTR_DNTS_INT (1 << 2)
+# define WUSBINTR_ERR_INT (1 << 1)
+# define WUSBINTR_INT (1 << 0)
+# define WUSBINTR_ALL 0x3ff
+
+#define WUSBGENCMDSTS 0x14
+# define WUSBGENCMDSTS_ACTIVE (1 << 31)
+# define WUSBGENCMDSTS_ERROR (1 << 24)
+# define WUSBGENCMDSTS_IOC (1 << 23)
+# define WUSBGENCMDSTS_MMCIE_ADD 0x01
+# define WUSBGENCMDSTS_MMCIE_RM 0x02
+# define WUSBGENCMDSTS_SET_MAS 0x03
+# define WUSBGENCMDSTS_CHAN_STOP 0x04
+# define WUSBGENCMDSTS_RWP_EN 0x05
+
+#define WUSBGENCMDPARAMS 0x18
+#define WUSBGENADDR 0x20
+#define WUSBASYNCLISTADDR 0x28
+#define WUSBDNTSBUFADDR 0x30
+#define WUSBDEVICEINFOADDR 0x38
+
+#define WUSBSETSECKEYCMD 0x40
+# define WUSBSETSECKEYCMD_SET (1 << 31)
+# define WUSBSETSECKEYCMD_ERASE (1 << 30)
+# define WUSBSETSECKEYCMD_GTK (1 << 8)
+# define WUSBSETSECKEYCMD_IDX(i) ((i) << 0)
+
+#define WUSBTKID 0x44
+#define WUSBSECKEY 0x48
+#define WUSBPERIODICLISTBASE 0x58
+#define WUSBMASINDEX 0x60
+
+#define WUSBDNTSCTRL 0x64
+# define WUSBDNTSCTRL_ACTIVE (1 << 31)
+# define WUSBDNTSCTRL_INTERVAL(i) ((i) << 8)
+# define WUSBDNTSCTRL_SLOTS(s) ((s) << 0)
+
+#define WUSBTIME 0x68
+#define WUSBBPST 0x6c
+#define WUSBDIBUPDATED 0x70
+
+#endif /* #ifndef _WHCI_WHCI_HC_H */
diff --git a/drivers/usb/host/whci/wusb.c b/drivers/usb/host/whci/wusb.c
new file mode 100644
index 000000000000..66e4ddcd961d
--- /dev/null
+++ b/drivers/usb/host/whci/wusb.c
@@ -0,0 +1,241 @@
+/*
+ * Wireless Host Controller (WHC) WUSB operations.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/uwb/umc.h>
+#define D_LOCAL 1
+#include <linux/uwb/debug.h>
+
+#include "../../wusbcore/wusbhc.h"
+
+#include "whcd.h"
+
+#if D_LOCAL >= 1
+static void dump_di(struct whc *whc, int idx)
+{
+ struct di_buf_entry *di = &whc->di_buf[idx];
+ struct device *dev = &whc->umc->dev;
+ char buf[128];
+
+ bitmap_scnprintf(buf, sizeof(buf), (unsigned long *)di->availability_info, UWB_NUM_MAS);
+
+ d_printf(1, dev, "DI[%d]\n", idx);
+ d_printf(1, dev, " availability: %s\n", buf);
+ d_printf(1, dev, " %c%c key idx: %d dev addr: %d\n",
+ (di->addr_sec_info & WHC_DI_SECURE) ? 'S' : ' ',
+ (di->addr_sec_info & WHC_DI_DISABLE) ? 'D' : ' ',
+ (di->addr_sec_info & WHC_DI_KEY_IDX_MASK) >> 8,
+ (di->addr_sec_info & WHC_DI_DEV_ADDR_MASK));
+}
+#else
+static inline void dump_di(struct whc *whc, int idx)
+{
+}
+#endif
+
+static int whc_update_di(struct whc *whc, int idx)
+{
+ int offset = idx / 32;
+ u32 bit = 1 << (idx % 32);
+
+ dump_di(whc, idx);
+
+ le_writel(bit, whc->base + WUSBDIBUPDATED + offset);
+
+ return whci_wait_for(&whc->umc->dev,
+ whc->base + WUSBDIBUPDATED + offset, bit, 0,
+ 100, "DI update");
+}
+
+/*
+ * WHCI starts and stops MMCs based on there being a valid GTK so
+ * these need only start/stop the asynchronous and periodic schedules.
+ */
+
+int whc_wusbhc_start(struct wusbhc *wusbhc)
+{
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+
+ asl_start(whc);
+ pzl_start(whc);
+
+ return 0;
+}
+
+void whc_wusbhc_stop(struct wusbhc *wusbhc)
+{
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+
+ pzl_stop(whc);
+ asl_stop(whc);
+}
+
+int whc_mmcie_add(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt,
+ u8 handle, struct wuie_hdr *wuie)
+{
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ u32 params;
+
+ params = (interval << 24)
+ | (repeat_cnt << 16)
+ | (wuie->bLength << 8)
+ | handle;
+
+ return whc_do_gencmd(whc, WUSBGENCMDSTS_MMCIE_ADD, params, wuie, wuie->bLength);
+}
+
+int whc_mmcie_rm(struct wusbhc *wusbhc, u8 handle)
+{
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ u32 params;
+
+ params = handle;
+
+ return whc_do_gencmd(whc, WUSBGENCMDSTS_MMCIE_RM, params, NULL, 0);
+}
+
+int whc_bwa_set(struct wusbhc *wusbhc, s8 stream_index, const struct uwb_mas_bm *mas_bm)
+{
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+
+ if (stream_index >= 0)
+ whc_write_wusbcmd(whc, WUSBCMD_WUSBSI_MASK, WUSBCMD_WUSBSI(stream_index));
+
+ return whc_do_gencmd(whc, WUSBGENCMDSTS_SET_MAS, 0, (void *)mas_bm, sizeof(*mas_bm));
+}
+
+int whc_dev_info_set(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
+{
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ int idx = wusb_dev->port_idx;
+ struct di_buf_entry *di = &whc->di_buf[idx];
+ int ret;
+
+ mutex_lock(&whc->mutex);
+
+ uwb_mas_bm_copy_le(di->availability_info, &wusb_dev->availability);
+ di->addr_sec_info &= ~(WHC_DI_DISABLE | WHC_DI_DEV_ADDR_MASK);
+ di->addr_sec_info |= WHC_DI_DEV_ADDR(wusb_dev->addr);
+
+ ret = whc_update_di(whc, idx);
+
+ mutex_unlock(&whc->mutex);
+
+ return ret;
+}
+
+/*
+ * Set the number of Device Notification Time Slots (DNTS) and enable
+ * device notifications.
+ */
+int whc_set_num_dnts(struct wusbhc *wusbhc, u8 interval, u8 slots)
+{
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ u32 dntsctrl;
+
+ dntsctrl = WUSBDNTSCTRL_ACTIVE
+ | WUSBDNTSCTRL_INTERVAL(interval)
+ | WUSBDNTSCTRL_SLOTS(slots);
+
+ le_writel(dntsctrl, whc->base + WUSBDNTSCTRL);
+
+ return 0;
+}
+
+static int whc_set_key(struct whc *whc, u8 key_index, uint32_t tkid,
+ const void *key, size_t key_size, bool is_gtk)
+{
+ uint32_t setkeycmd;
+ uint32_t seckey[4];
+ int i;
+ int ret;
+
+ memcpy(seckey, key, key_size);
+ setkeycmd = WUSBSETSECKEYCMD_SET | WUSBSETSECKEYCMD_IDX(key_index);
+ if (is_gtk)
+ setkeycmd |= WUSBSETSECKEYCMD_GTK;
+
+ le_writel(tkid, whc->base + WUSBTKID);
+ for (i = 0; i < 4; i++)
+ le_writel(seckey[i], whc->base + WUSBSECKEY + 4*i);
+ le_writel(setkeycmd, whc->base + WUSBSETSECKEYCMD);
+
+ ret = whci_wait_for(&whc->umc->dev, whc->base + WUSBSETSECKEYCMD,
+ WUSBSETSECKEYCMD_SET, 0, 100, "set key");
+
+ return ret;
+}
+
+/**
+ * whc_set_ptk - set the PTK to use for a device.
+ *
+ * The index into the key table for this PTK is the same as the
+ * device's port index.
+ */
+int whc_set_ptk(struct wusbhc *wusbhc, u8 port_idx, u32 tkid,
+ const void *ptk, size_t key_size)
+{
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ struct di_buf_entry *di = &whc->di_buf[port_idx];
+ int ret;
+
+ mutex_lock(&whc->mutex);
+
+ if (ptk) {
+ ret = whc_set_key(whc, port_idx, tkid, ptk, key_size, false);
+ if (ret)
+ goto out;
+
+ di->addr_sec_info &= ~WHC_DI_KEY_IDX_MASK;
+ di->addr_sec_info |= WHC_DI_SECURE | WHC_DI_KEY_IDX(port_idx);
+ } else
+ di->addr_sec_info &= ~WHC_DI_SECURE;
+
+ ret = whc_update_di(whc, port_idx);
+out:
+ mutex_unlock(&whc->mutex);
+ return ret;
+}
+
+/**
+ * whc_set_gtk - set the GTK for subsequent broadcast packets
+ *
+ * The GTK is stored in the last entry in the key table (the previous
+ * N_DEVICES entries are for the per-device PTKs).
+ */
+int whc_set_gtk(struct wusbhc *wusbhc, u32 tkid,
+ const void *gtk, size_t key_size)
+{
+ struct whc *whc = wusbhc_to_whc(wusbhc);
+ int ret;
+
+ mutex_lock(&whc->mutex);
+
+ ret = whc_set_key(whc, whc->n_devices, tkid, gtk, key_size, true);
+
+ mutex_unlock(&whc->mutex);
+
+ return ret;
+}
+
+int whc_set_cluster_id(struct whc *whc, u8 bcid)
+{
+ whc_write_wusbcmd(whc, WUSBCMD_BCID_MASK, WUSBCMD_BCID(bcid));
+ return 0;
+}
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index eb6c06979f3b..ee79c619d89b 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -272,6 +272,7 @@ config USB_TEST
config USB_ISIGHTFW
tristate "iSight firmware loading support"
depends on USB
+ select FW_LOADER
help
This driver loads firmware for USB Apple iSight cameras, allowing
them to be driven by the USB video class driver available at
@@ -280,3 +281,13 @@ config USB_ISIGHTFW
The firmware for this driver must be extracted from the MacOS
driver beforehand. Tools for doing so are available at
http://bersace03.free.fr
+
+config USB_GOTEMP
+ tristate "GoTemp USB thermometer driver support"
+ depends on USB
+ help
+ Say Y here if you want to connect a GoTemp USB thermometer
+ device to your computer's USB port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gotemp.
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index aba091cb5ec0..d783410ad562 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_USB_CYTHERM) += cytherm.o
obj-$(CONFIG_USB_EMI26) += emi26.o
obj-$(CONFIG_USB_EMI62) += emi62.o
obj-$(CONFIG_USB_FTDI_ELAN) += ftdi-elan.o
+obj-$(CONFIG_USB_GOTEMP) += gotemp.o
obj-$(CONFIG_USB_IDMOUSE) += idmouse.o
obj-$(CONFIG_USB_IOWARRIOR) += iowarrior.o
obj-$(CONFIG_USB_ISIGHTFW) += isight_firmware.o
diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c
index 093938697426..d2f61d5510e7 100644
--- a/drivers/usb/misc/auerswald.c
+++ b/drivers/usb/misc/auerswald.c
@@ -1421,7 +1421,8 @@ ofail: mutex_unlock(&cp->mutex);
/* IOCTL functions */
-static int auerchar_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+static long auerchar_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
pauerchar_t ccp = (pauerchar_t) file->private_data;
int ret = 0;
@@ -1452,7 +1453,7 @@ static int auerchar_ioctl (struct inode *inode, struct file *file, unsigned int
mutex_unlock(&ccp->mutex);
return -ENODEV;
}
-
+ lock_kernel();
switch (cmd) {
/* return != 0 if Transmitt channel ready to send */
@@ -1547,9 +1548,10 @@ static int auerchar_ioctl (struct inode *inode, struct file *file, unsigned int
default:
dbg ("IOCTL_AU_UNKNOWN");
- ret = -ENOIOCTLCMD;
+ ret = -ENOTTY;
break;
}
+ unlock_kernel();
/* release the mutexes */
mutex_unlock(&cp->mutex);
mutex_unlock(&ccp->mutex);
@@ -1860,7 +1862,7 @@ static const struct file_operations auerswald_fops =
.llseek = no_llseek,
.read = auerchar_read,
.write = auerchar_write,
- .ioctl = auerchar_ioctl,
+ .unlocked_ioctl = auerchar_ioctl,
.open = auerchar_open,
.release = auerchar_release,
};
diff --git a/drivers/usb/misc/emi62.c b/drivers/usb/misc/emi62.c
index 1a2b79ac5e10..0b1727d4308b 100644
--- a/drivers/usb/misc/emi62.c
+++ b/drivers/usb/misc/emi62.c
@@ -6,8 +6,6 @@
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, as published by
* the Free Software Foundation, version 2.
- *
- * $Id: emi62.c,v 1.15 2002/04/23 06:13:59 tapio Exp $
*/
#include <linux/kernel.h>
#include <linux/errno.h>
diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c
index ec88b3bfee46..97c280971532 100644
--- a/drivers/usb/misc/ftdi-elan.c
+++ b/drivers/usb/misc/ftdi-elan.c
@@ -656,29 +656,6 @@ static int ftdi_elan_release(struct inode *inode, struct file *file)
}
-#define FTDI_ELAN_IOC_MAGIC 0xA1
-#define FTDI_ELAN_IOCDEBUG _IOC(_IOC_WRITE, FTDI_ELAN_IOC_MAGIC, 1, 132)
-static int ftdi_elan_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- switch (cmd) {
- case FTDI_ELAN_IOCDEBUG:{
- char line[132];
- int size = strncpy_from_user(line,
- (const char __user *)arg, sizeof(line));
- if (size < 0) {
- return -EINVAL;
- } else {
- printk(KERN_ERR "TODO: ioctl %s\n", line);
- return 0;
- }
- }
- default:
- return -EFAULT;
- }
-}
-
-
/*
*
* blocking bulk reads are used to get data from the device
@@ -1222,7 +1199,6 @@ error_1:
static const struct file_operations ftdi_elan_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
- .ioctl = ftdi_elan_ioctl,
.read = ftdi_elan_read,
.write = ftdi_elan_write,
.open = ftdi_elan_open,
diff --git a/drivers/usb/misc/gotemp.c b/drivers/usb/misc/gotemp.c
new file mode 100644
index 000000000000..0c04ef736edc
--- /dev/null
+++ b/drivers/usb/misc/gotemp.c
@@ -0,0 +1,301 @@
+/*
+ * USB GoTemp driver
+ *
+ * Copyright (C) 2005 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+
+#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com"
+#define DRIVER_DESC "USB GoTemp driver"
+
+#define VENDOR_ID 0x08f7
+#define PRODUCT_ID 0x0002
+
+/* table of devices that work with this driver */
+static struct usb_device_id id_table [] = {
+ { USB_DEVICE(VENDOR_ID, PRODUCT_ID) },
+ { },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+struct gotemp {
+ struct usb_device *udev;
+ int temp;
+ unsigned char *int_in_buffer;
+ __u8 int_in_endpointAddr;
+ struct urb *int_in_urb;
+};
+
+#define CMD_ID_GET_STATUS 0x10
+#define CMD_ID_WRITE_LOCAL_NV_MEM_1BYTE 0x11
+#define CMD_ID_WRITE_LOCAL_NV_MEM_2BYTES 0x12
+#define CMD_ID_WRITE_LOCAL_NV_MEM_3BYTES 0x13
+#define CMD_ID_WRITE_LOCAL_NV_MEM_4BYTES 0x14
+#define CMD_ID_WRITE_LOCAL_NV_MEM_5BYTES 0x15
+#define CMD_ID_WRITE_LOCAL_NV_MEM_6BYTES 0x16
+#define CMD_ID_READ_LOCAL_NV_MEM 0x17
+#define CMD_ID_START_MEASUREMENTS 0x18
+#define CMD_ID_STOP_MEASUREMENTS 0x19
+#define CMD_ID_INIT 0x1A
+#define CMD_ID_SET_MEASUREMENT_PERIOD 0x1B
+#define CMD_ID_GET_MEASUREMENT_PERIOD 0x1C
+#define CMD_ID_SET_LED_STATE 0x1D
+#define CMD_ID_GET_LED_STATE 0x1E
+#define CMD_ID_GET_SERIAL_NUMBER 0x20
+
+struct output_packet {
+ u8 cmd;
+ u8 params[7];
+} __attribute__ ((packed));
+
+struct measurement_packet {
+ u8 measurements_in_packet;
+ u8 rolling_counter;
+ __le16 measurement0;
+ __le16 measurement1;
+ __le16 measurement2;
+} __attribute__ ((packed));
+
+static int send_cmd(struct gotemp *gdev, u8 cmd)
+{
+ struct output_packet *pkt;
+ int retval;
+
+ pkt = kzalloc(sizeof(*pkt), GFP_KERNEL);
+ if (!pkt)
+ return -ENOMEM;
+ pkt->cmd = cmd;
+
+ retval = usb_control_msg(gdev->udev,
+ usb_sndctrlpipe(gdev->udev, 0),
+ 0x09, /* bRequest = SET_REPORT */
+ 0x21, /* bRequestType = 00100001 */
+ 0x0200, /* or is it 0x0002? */
+ 0x0000, /* interface 0 */
+ pkt, sizeof(*pkt), 10000);
+ dev_dbg(&gdev->udev->dev, "retval=%d\n", retval);
+ if (retval == sizeof(*pkt))
+ retval = 0;
+
+ kfree(pkt);
+ return retval;
+}
+
+static void init_dev(struct gotemp *gdev)
+{
+ int retval;
+
+ /* First send an init message */
+ send_cmd(gdev, CMD_ID_INIT);
+
+ /* hack hack hack */
+ /* problem is, we want a usb_interrupt_msg() call to read the interrupt
+ * endpoint right now. only after it is flushed, can we properly start
+ * up the measurements. */
+ msleep(1000);
+
+ /* kick off interrupt urb */
+ retval = usb_submit_urb(gdev->int_in_urb, GFP_KERNEL);
+ if (retval)
+ dev_err(&gdev->udev->dev,
+ "%s - Error %d submitting interrupt urb\n",
+ __func__, retval);
+
+ msleep(3000);
+ send_cmd(gdev, CMD_ID_START_MEASUREMENTS);
+}
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct gotemp *gdev = usb_get_intfdata(intf);
+
+ return sprintf(buf, "%d\n", gdev->temp);
+}
+
+static DEVICE_ATTR(temp, S_IRUGO, show_temp, NULL);
+
+static void read_int_callback(struct urb *urb)
+{
+ struct gotemp *gdev = urb->context;
+ unsigned char *data = urb->transfer_buffer;
+ struct measurement_packet *measurement = urb->transfer_buffer;
+ int retval;
+ int i;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d",
+ __func__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d",
+ __func__, urb->status);
+ goto exit;
+ }
+
+ dev_info(&urb->dev->dev, "int read data: ");
+ for (i = 0; i < urb->actual_length; ++i)
+ printk("%02x ", data[i]);
+ printk("\n");
+
+ dev_dbg(&urb->dev->dev, "counter %d, temp=%d\n",
+ measurement->rolling_counter,
+ measurement->measurement0);
+ gdev->temp = le16_to_cpu(measurement->measurement0);
+
+exit:
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(&urb->dev->dev,
+ "%s - Error %d submitting interrupt urb\n",
+ __func__, retval);
+}
+
+static int gotemp_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct gotemp *gdev = NULL;
+ int retval = -ENOMEM;
+ int i;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint = NULL;
+ size_t buffer_size = 0;
+
+ gdev = kzalloc(sizeof(struct gotemp), GFP_KERNEL);
+ if (gdev == NULL) {
+ dev_err(&interface->dev, "Out of memory\n");
+ goto error;
+ }
+
+ gdev->udev = usb_get_dev(udev);
+
+ /* find the one control endpoint of this device */
+ iface_desc = interface->cur_altsetting;
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (usb_endpoint_is_int_in(endpoint)) {
+ buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+ gdev->int_in_endpointAddr = endpoint->bEndpointAddress;
+ gdev->int_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (!gdev->int_in_buffer) {
+ dev_err(&interface->dev,
+ "Could not allocate buffer");
+ goto error;
+ }
+ break;
+ }
+ }
+ if (!gdev->int_in_endpointAddr) {
+ dev_err(&interface->dev, "Could not find int-in endpoint");
+ retval = -ENODEV;
+ goto error;
+ }
+
+ gdev->int_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!gdev->int_in_urb) {
+ dev_err(&interface->dev, "No free urbs available\n");
+ goto error;
+ }
+ usb_fill_int_urb(gdev->int_in_urb, udev,
+ usb_rcvintpipe(udev,
+ endpoint->bEndpointAddress),
+ gdev->int_in_buffer, buffer_size,
+ read_int_callback, gdev,
+ endpoint->bInterval);
+
+ usb_set_intfdata(interface, gdev);
+
+ init_dev(gdev);
+
+ /*
+ * this must come last - after this call the device is active
+ * if we delayed any initialization until after this, the user
+ * would read garbage
+ */
+ retval = device_create_file(&interface->dev, &dev_attr_temp);
+ if (retval)
+ goto error;
+
+ dev_info(&interface->dev, "USB GoTemp device now attached\n");
+ return 0;
+
+error:
+ usb_set_intfdata(interface, NULL);
+ if (gdev) {
+ usb_free_urb(gdev->int_in_urb);
+ kfree(gdev->int_in_buffer);
+ }
+ kfree(gdev);
+ return retval;
+}
+
+static void gotemp_disconnect(struct usb_interface *interface)
+{
+ struct gotemp *gdev;
+
+ gdev = usb_get_intfdata(interface);
+
+ device_remove_file(&interface->dev, &dev_attr_temp);
+ /* intfdata must remain valid while reads are under way */
+ usb_set_intfdata(interface, NULL);
+
+ usb_put_dev(gdev->udev);
+
+ usb_kill_urb(gdev->int_in_urb);
+ usb_free_urb(gdev->int_in_urb);
+ kfree(gdev->int_in_buffer);
+ kfree(gdev);
+
+ dev_info(&interface->dev, "USB GoTemp now disconnected\n");
+}
+
+static struct usb_driver gotemp_driver = {
+ .name = "gotemp",
+ .probe = gotemp_probe,
+ .disconnect = gotemp_disconnect,
+ .id_table = id_table,
+};
+
+static int __init gotemp_init(void)
+{
+ int retval = 0;
+
+ retval = usb_register(&gotemp_driver);
+ if (retval)
+ err("usb_register failed. Error number %d", retval);
+ return retval;
+}
+
+static void __exit gotemp_exit(void)
+{
+ usb_deregister(&gotemp_driver);
+}
+
+module_init(gotemp_init);
+module_exit(gotemp_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c
index 1cb54a28347f..e6ca9979e3ae 100644
--- a/drivers/usb/misc/iowarrior.c
+++ b/drivers/usb/misc/iowarrior.c
@@ -474,8 +474,8 @@ exit:
/**
* iowarrior_ioctl
*/
-static int iowarrior_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg)
+static long iowarrior_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
struct iowarrior *dev = NULL;
__u8 *buffer;
@@ -493,6 +493,7 @@ static int iowarrior_ioctl(struct inode *inode, struct file *file,
return -ENOMEM;
/* lock this object */
+ lock_kernel();
mutex_lock(&dev->mutex);
/* verify that the device wasn't unplugged */
@@ -584,6 +585,7 @@ static int iowarrior_ioctl(struct inode *inode, struct file *file,
error_out:
/* unlock the device */
mutex_unlock(&dev->mutex);
+ unlock_kernel();
kfree(buffer);
return retval;
}
@@ -719,7 +721,7 @@ static const struct file_operations iowarrior_fops = {
.owner = THIS_MODULE,
.write = iowarrior_write,
.read = iowarrior_read,
- .ioctl = iowarrior_ioctl,
+ .unlocked_ioctl = iowarrior_ioctl,
.open = iowarrior_open,
.release = iowarrior_release,
.poll = iowarrior_poll,
diff --git a/drivers/usb/misc/isight_firmware.c b/drivers/usb/misc/isight_firmware.c
index 390e04885536..d94aa7387608 100644
--- a/drivers/usb/misc/isight_firmware.c
+++ b/drivers/usb/misc/isight_firmware.c
@@ -39,9 +39,12 @@ static int isight_firmware_load(struct usb_interface *intf,
struct usb_device *dev = interface_to_usbdev(intf);
int llen, len, req, ret = 0;
const struct firmware *firmware;
- unsigned char *buf;
+ unsigned char *buf = kmalloc(50, GFP_KERNEL);
unsigned char data[4];
- char *ptr;
+ const u8 *ptr;
+
+ if (!buf)
+ return -ENOMEM;
if (request_firmware(&firmware, "isight.fw", &dev->dev) != 0) {
printk(KERN_ERR "Unable to load isight firmware\n");
@@ -59,7 +62,7 @@ static int isight_firmware_load(struct usb_interface *intf,
goto out;
}
- while (1) {
+ while (ptr+4 <= firmware->data+firmware->size) {
memcpy(data, ptr, 4);
len = (data[0] << 8 | data[1]);
req = (data[2] << 8 | data[3]);
@@ -71,10 +74,14 @@ static int isight_firmware_load(struct usb_interface *intf,
continue;
for (; len > 0; req += 50) {
- llen = len > 50 ? 50 : len;
+ llen = min(len, 50);
len -= llen;
-
- buf = kmalloc(llen, GFP_KERNEL);
+ if (ptr+llen > firmware->data+firmware->size) {
+ printk(KERN_ERR
+ "Malformed isight firmware");
+ ret = -ENODEV;
+ goto out;
+ }
memcpy(buf, ptr, llen);
ptr += llen;
@@ -89,16 +96,18 @@ static int isight_firmware_load(struct usb_interface *intf,
goto out;
}
- kfree(buf);
}
}
+
if (usb_control_msg
(dev, usb_sndctrlpipe(dev, 0), 0xa0, 0x40, 0xe600, 0, "\0", 1,
300) != 1) {
printk(KERN_ERR "isight firmware loading completion failed\n");
ret = -ENODEV;
}
+
out:
+ kfree(buf);
release_firmware(firmware);
return ret;
}
diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c
index 330c18e390b8..248a12aacef6 100644
--- a/drivers/usb/misc/rio500.c
+++ b/drivers/usb/misc/rio500.c
@@ -104,9 +104,7 @@ static int close_rio(struct inode *inode, struct file *file)
return 0;
}
-static int
-ioctl_rio(struct inode *inode, struct file *file, unsigned int cmd,
- unsigned long arg)
+static long ioctl_rio(struct file *file, unsigned int cmd, unsigned long arg)
{
struct RioCommand rio_cmd;
struct rio_usb_data *rio = &rio_instance;
@@ -116,6 +114,7 @@ ioctl_rio(struct inode *inode, struct file *file, unsigned int cmd,
int retries;
int retval=0;
+ lock_kernel();
mutex_lock(&(rio->lock));
/* Sanity check to make sure rio is connected, powered, etc */
if (rio->present == 0 || rio->rio_dev == NULL) {
@@ -254,6 +253,7 @@ ioctl_rio(struct inode *inode, struct file *file, unsigned int cmd,
err_out:
mutex_unlock(&(rio->lock));
+ unlock_kernel();
return retval;
}
@@ -433,7 +433,7 @@ file_operations usb_rio_fops = {
.owner = THIS_MODULE,
.read = read_rio,
.write = write_rio,
- .ioctl = ioctl_rio,
+ .unlocked_ioctl = ioctl_rio,
.open = open_rio,
.release = close_rio,
};
diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c
index cb7fa0eaf3ae..8dca796475e8 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.c
+++ b/drivers/usb/misc/sisusbvga/sisusb.c
@@ -2982,9 +2982,8 @@ sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y,
return retval;
}
-static int
-sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
- unsigned long arg)
+static long
+sisusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct sisusb_usb_data *sisusb;
struct sisusb_info x;
@@ -2995,6 +2994,7 @@ sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
return -ENODEV;
+ lock_kernel();
mutex_lock(&sisusb->lock);
/* Sanity check */
@@ -3053,6 +3053,7 @@ sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
err_out:
mutex_unlock(&sisusb->lock);
+ unlock_kernel();
return retval;
}
@@ -3066,9 +3067,7 @@ sisusb_compat_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
case SISUSB_GET_CONFIG_SIZE:
case SISUSB_GET_CONFIG:
case SISUSB_COMMAND:
- lock_kernel();
- retval = sisusb_ioctl(f->f_path.dentry->d_inode, f, cmd, arg);
- unlock_kernel();
+ retval = sisusb_ioctl(f, cmd, arg);
return retval;
default:
@@ -3087,7 +3086,7 @@ static const struct file_operations usb_sisusb_fops = {
#ifdef SISUSB_NEW_CONFIG_COMPAT
.compat_ioctl = sisusb_compat_ioctl,
#endif
- .ioctl = sisusb_ioctl
+ .unlocked_ioctl = sisusb_ioctl
};
static struct usb_class_driver usb_sisusb_class = {
diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c
index 7f7021ee4189..2db4228fbb01 100644
--- a/drivers/usb/misc/usblcd.c
+++ b/drivers/usb/misc/usblcd.c
@@ -146,7 +146,7 @@ static ssize_t lcd_read(struct file *file, char __user * buffer, size_t count, l
return retval;
}
-static int lcd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct usb_lcd *dev;
u16 bcdDevice;
@@ -158,12 +158,14 @@ static int lcd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, u
switch (cmd) {
case IOCTL_GET_HARD_VERSION:
+ lock_kernel();
bcdDevice = le16_to_cpu((dev->udev)->descriptor.bcdDevice);
sprintf(buf,"%1d%1d.%1d%1d",
(bcdDevice & 0xF000)>>12,
(bcdDevice & 0xF00)>>8,
(bcdDevice & 0xF0)>>4,
(bcdDevice & 0xF));
+ unlock_kernel();
if (copy_to_user((void __user *)arg,buf,strlen(buf))!=0)
return -EFAULT;
break;
@@ -272,7 +274,7 @@ static const struct file_operations lcd_fops = {
.read = lcd_read,
.write = lcd_write,
.open = lcd_open,
- .ioctl = lcd_ioctl,
+ .unlocked_ioctl = lcd_ioctl,
.release = lcd_release,
};
diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c
index 49145534e06e..5f7d7e283fc1 100644
--- a/drivers/usb/mon/mon_bin.c
+++ b/drivers/usb/mon/mon_bin.c
@@ -1156,8 +1156,9 @@ int mon_bin_add(struct mon_bus *mbus, const struct usb_bus *ubus)
if (minor >= MON_BIN_MAX_MINOR)
return 0;
- dev = device_create(mon_bin_class, ubus? ubus->controller: NULL,
- MKDEV(MAJOR(mon_bin_dev0), minor), "usbmon%d", minor);
+ dev = device_create_drvdata(mon_bin_class, ubus? ubus->controller: NULL,
+ MKDEV(MAJOR(mon_bin_dev0), minor), NULL,
+ "usbmon%d", minor);
if (IS_ERR(dev))
return 0;
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index 9ba64ccc1359..19bd00a6e01a 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -523,6 +523,14 @@ config USB_SERIAL_TI
To compile this driver as a module, choose M here: the
module will be called ti_usb_3410_5052.
+config USB_SERIAL_TI_FIRMWARE
+ bool "USB TI 3410/5052 Firmware"
+ depends on USB_SERIAL_TI
+ help
+ say Y to include the firmware into the kernel for compatibility
+ with older setups. Say N if you have the firmware files installed
+ in /etc/firmware.
+
config USB_SERIAL_CYBERJACK
tristate "USB REINER SCT cyberJack pinpad/e-com chipcard reader"
---help---
diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c
index f5b57b196c5a..2bc5576c443a 100644
--- a/drivers/usb/serial/cp2101.c
+++ b/drivers/usb/serial/cp2101.c
@@ -365,8 +365,8 @@ static void cp2101_close (struct usb_serial_port *port, struct file * filp)
static void cp2101_get_termios (struct usb_serial_port *port)
{
unsigned int cflag, modem_ctl[4];
- int baud;
- int bits;
+ unsigned int baud;
+ unsigned int bits;
dbg("%s - port %d", __func__, port->number);
@@ -498,7 +498,7 @@ static void cp2101_set_termios (struct usb_serial_port *port,
struct ktermios *old_termios)
{
unsigned int cflag, old_cflag;
- int baud=0, bits;
+ unsigned int baud = 0, bits;
unsigned int modem_ctl[4];
dbg("%s - port %d", __func__, port->number);
@@ -654,7 +654,7 @@ static void cp2101_set_termios (struct usb_serial_port *port,
static int cp2101_tiocmset (struct usb_serial_port *port, struct file *file,
unsigned int set, unsigned int clear)
{
- int control = 0;
+ unsigned int control = 0;
dbg("%s - port %d", __func__, port->number);
@@ -683,7 +683,8 @@ static int cp2101_tiocmset (struct usb_serial_port *port, struct file *file,
static int cp2101_tiocmget (struct usb_serial_port *port, struct file *file)
{
- int control, result;
+ unsigned int control;
+ int result;
dbg("%s - port %d", __func__, port->number);
@@ -703,7 +704,7 @@ static int cp2101_tiocmget (struct usb_serial_port *port, struct file *file)
static void cp2101_break_ctl (struct usb_serial_port *port, int break_state)
{
- int state;
+ unsigned int state;
dbg("%s - port %d", __func__, port->number);
if (break_state == 0)
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index 04a56f300ea6..28bc6fcf44f0 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -229,8 +229,6 @@
* in case a wake up is lost.
* - Following Documentation/DocBook/kernel-locking.pdf no spin locks
* are held when calling copy_to/from_user or printk.
-*
-* $Id: digi_acceleport.c,v 1.80.1.2 2000/11/02 05:45:08 root Exp $
*/
#include <linux/kernel.h>
@@ -573,6 +571,7 @@ static struct usb_serial_driver digi_acceleport_4_device = {
static long cond_wait_interruptible_timeout_irqrestore(
wait_queue_head_t *q, long timeout,
spinlock_t *lock, unsigned long flags)
+__releases(lock)
{
DEFINE_WAIT(wait);
diff --git a/drivers/usb/serial/io_fw_down3.h b/drivers/usb/serial/io_fw_down3.h
index 4496b068c50f..9dcaf17d5053 100644
--- a/drivers/usb/serial/io_fw_down3.h
+++ b/drivers/usb/serial/io_fw_down3.h
@@ -5,22 +5,22 @@
//**************************************************************
-static int IMAGE_SIZE = 12938;
+static const int fw_size = 12938;
-struct EDGE_FIRMWARE_VERSION_INFO
+struct fw_revision
{
unsigned char MajorVersion;
unsigned char MinorVersion;
unsigned short BuildNumber;
};
-static struct EDGE_FIRMWARE_VERSION_INFO IMAGE_VERSION_NAME =
+static const struct fw_revision fw_version =
{
4, 80, 0 // Major, Minor, Build
};
-static unsigned char IMAGE_ARRAY_NAME[] =
+static const unsigned char fw_image[] =
{
// struct ImageHdr
// {
@@ -841,7 +841,4 @@ static unsigned char IMAGE_ARRAY_NAME[] =
0x12, 0x1e, 0x14, 0x22, 0xc2, 0x08, 0x22,
};
-#undef IMAGE_VERSION_NAME
-
-#undef IMAGE_ARRAY_NAME
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index 05e4fa730730..2c50318ca114 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -18,8 +18,8 @@
*
* Version history:
*
- * July 11, 2002 Removed 4 port device structure since all TI UMP
- * chips have only 2 ports
+ * July 11, 2002 Removed 4 port device structure since all TI UMP
+ * chips have only 2 ports
* David Iacovelli (davidi@ionetworks.com)
*
*/
@@ -37,7 +37,7 @@
#include <linux/mutex.h>
#include <linux/serial.h>
#include <linux/ioctl.h>
-#include <asm/uaccess.h>
+#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
@@ -54,27 +54,25 @@
/* firmware image code */
-#define IMAGE_VERSION_NAME PagableOperationalCodeImageVersion
-#define IMAGE_ARRAY_NAME PagableOperationalCodeImage
-#define IMAGE_SIZE PagableOperationalCodeSize
-#include "io_fw_down3.h" /* Define array OperationalCodeImage[] */
+#include "io_fw_down3.h" /* Define array fw_image[] */
#define EPROM_PAGE_SIZE 64
struct edgeport_uart_buf_desc {
- __u32 count; // Number of bytes currently in buffer
+ __u32 count; /* Number of bytes currently in buffer */
};
/* different hardware types */
#define HARDWARE_TYPE_930 0
#define HARDWARE_TYPE_TIUMP 1
-// IOCTL_PRIVATE_TI_GET_MODE Definitions
-#define TI_MODE_CONFIGURING 0 // Device has not entered start device
-#define TI_MODE_BOOT 1 // Staying in boot mode
-#define TI_MODE_DOWNLOAD 2 // Made it to download mode
-#define TI_MODE_TRANSITIONING 3 // Currently in boot mode but transitioning to download mode
+/* IOCTL_PRIVATE_TI_GET_MODE Definitions */
+#define TI_MODE_CONFIGURING 0 /* Device has not entered start device */
+#define TI_MODE_BOOT 1 /* Staying in boot mode */
+#define TI_MODE_DOWNLOAD 2 /* Made it to download mode */
+#define TI_MODE_TRANSITIONING 3 /* Currently in boot mode but
+ transitioning to download mode */
/* read urb state */
#define EDGE_READ_URB_RUNNING 0
@@ -88,10 +86,9 @@ struct edgeport_uart_buf_desc {
/* Product information read from the Edgeport */
-struct product_info
-{
- int TiMode; // Current TI Mode
- __u8 hardware_type; // Type of hardware
+struct product_info {
+ int TiMode; /* Current TI Mode */
+ __u8 hardware_type; /* Type of hardware */
} __attribute__((packed));
/* circular buffer */
@@ -122,7 +119,7 @@ struct edgeport_port {
happen */
struct edgeport_serial *edge_serial;
struct usb_serial_port *port;
- __u8 bUartMode; /* Port type, 0: RS232, etc. */
+ __u8 bUartMode; /* Port type, 0: RS232, etc. */
spinlock_t ep_lock;
int ep_read_urb_state;
int ep_write_urb_in_use;
@@ -131,8 +128,9 @@ struct edgeport_port {
struct edgeport_serial {
struct product_info product_info;
- u8 TI_I2C_Type; // Type of I2C in UMP
- u8 TiReadI2C; // Set to TRUE if we have read the I2c in Boot Mode
+ u8 TI_I2C_Type; /* Type of I2C in UMP */
+ u8 TiReadI2C; /* Set to TRUE if we have read the
+ I2c in Boot Mode */
struct mutex es_lock;
int num_ports_open;
struct usb_serial *serial;
@@ -220,7 +218,7 @@ static struct usb_device_id id_table_combined [] = {
{ }
};
-MODULE_DEVICE_TABLE (usb, id_table_combined);
+MODULE_DEVICE_TABLE(usb, id_table_combined);
static struct usb_driver io_driver = {
.name = "io_ti",
@@ -231,23 +229,24 @@ static struct usb_driver io_driver = {
};
-static struct EDGE_FIRMWARE_VERSION_INFO OperationalCodeImageVersion;
+static struct fw_revision op_fw_version;
static int debug;
-static int TIStayInBootMode = 0;
static int low_latency = EDGE_LOW_LATENCY;
static int closing_wait = EDGE_CLOSING_WAIT;
-static int ignore_cpu_rev = 0;
-static int default_uart_mode = 0; /* RS232 */
+static int ignore_cpu_rev;
+static int default_uart_mode; /* RS232 */
-static void edge_tty_recv(struct device *dev, struct tty_struct *tty, unsigned char *data, int length);
+static void edge_tty_recv(struct device *dev, struct tty_struct *tty,
+ unsigned char *data, int length);
static void stop_read(struct edgeport_port *edge_port);
static int restart_read(struct edgeport_port *edge_port);
-static void edge_set_termios (struct usb_serial_port *port, struct ktermios *old_termios);
+static void edge_set_termios(struct usb_serial_port *port,
+ struct ktermios *old_termios);
static void edge_send(struct usb_serial_port *port);
/* sysfs attributes */
@@ -266,87 +265,57 @@ static unsigned int edge_buf_get(struct edge_buf *eb, char *buf,
unsigned int count);
-static int TIReadVendorRequestSync (struct usb_device *dev,
- __u8 request,
- __u16 value,
- __u16 index,
- u8 *data,
- int size)
+static int ti_vread_sync(struct usb_device *dev, __u8 request,
+ __u16 value, __u16 index, u8 *data, int size)
{
int status;
- status = usb_control_msg (dev,
- usb_rcvctrlpipe(dev, 0),
- request,
- (USB_TYPE_VENDOR |
- USB_RECIP_DEVICE |
- USB_DIR_IN),
- value,
- index,
- data,
- size,
- 1000);
+ status = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
+ (USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN),
+ value, index, data, size, 1000);
if (status < 0)
return status;
if (status != size) {
- dbg ("%s - wanted to write %d, but only wrote %d",
- __func__, size, status);
+ dbg("%s - wanted to write %d, but only wrote %d",
+ __func__, size, status);
return -ECOMM;
}
return 0;
}
-static int TISendVendorRequestSync (struct usb_device *dev,
- __u8 request,
- __u16 value,
- __u16 index,
- u8 *data,
- int size)
+static int ti_vsend_sync(struct usb_device *dev, __u8 request,
+ __u16 value, __u16 index, u8 *data, int size)
{
int status;
- status = usb_control_msg (dev,
- usb_sndctrlpipe(dev, 0),
- request,
- (USB_TYPE_VENDOR |
- USB_RECIP_DEVICE |
- USB_DIR_OUT),
- value,
- index,
- data,
- size,
- 1000);
+ status = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
+ (USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT),
+ value, index, data, size, 1000);
if (status < 0)
return status;
if (status != size) {
- dbg ("%s - wanted to write %d, but only wrote %d",
+ dbg("%s - wanted to write %d, but only wrote %d",
__func__, size, status);
return -ECOMM;
}
return 0;
}
-static int TIWriteCommandSync (struct usb_device *dev, __u8 command,
+static int send_cmd(struct usb_device *dev, __u8 command,
__u8 moduleid, __u16 value, u8 *data,
int size)
{
- return TISendVendorRequestSync (dev,
- command, // Request
- value, // wValue
- moduleid, // wIndex
- data, // TransferBuffer
- size); // TransferBufferLength
-
+ return ti_vsend_sync(dev, command, value, moduleid, data, size);
}
/* clear tx/rx buffers and fifo in TI UMP */
-static int TIPurgeDataSync (struct usb_serial_port *port, __u16 mask)
+static int purge_port(struct usb_serial_port *port, __u16 mask)
{
int port_number = port->number - port->serial->minor;
- dbg ("%s - port %d, mask %x", __func__, port_number, mask);
+ dbg("%s - port %d, mask %x", __func__, port_number, mask);
- return TIWriteCommandSync (port->serial->dev,
+ return send_cmd(port->serial->dev,
UMPC_PURGE_PORT,
(__u8)(UMPM_UART1_PORT + port_number),
mask,
@@ -355,92 +324,87 @@ static int TIPurgeDataSync (struct usb_serial_port *port, __u16 mask)
}
/**
- * TIReadDownloadMemory - Read edgeport memory from TI chip
+ * read_download_mem - Read edgeport memory from TI chip
* @dev: usb device pointer
* @start_address: Device CPU address at which to read
* @length: Length of above data
* @address_type: Can read both XDATA and I2C
* @buffer: pointer to input data buffer
*/
-static int TIReadDownloadMemory(struct usb_device *dev, int start_address,
+static int read_download_mem(struct usb_device *dev, int start_address,
int length, __u8 address_type, __u8 *buffer)
{
int status = 0;
__u8 read_length;
__be16 be_start_address;
-
- dbg ("%s - @ %x for %d", __func__, start_address, length);
+
+ dbg("%s - @ %x for %d", __func__, start_address, length);
/* Read in blocks of 64 bytes
* (TI firmware can't handle more than 64 byte reads)
*/
while (length) {
if (length > 64)
- read_length= 64;
+ read_length = 64;
else
read_length = (__u8)length;
if (read_length > 1) {
- dbg ("%s - @ %x for %d", __func__,
+ dbg("%s - @ %x for %d", __func__,
start_address, read_length);
}
- be_start_address = cpu_to_be16 (start_address);
- status = TIReadVendorRequestSync (dev,
- UMPC_MEMORY_READ, // Request
- (__u16)address_type, // wValue (Address type)
- (__force __u16)be_start_address, // wIndex (Address to read)
- buffer, // TransferBuffer
- read_length); // TransferBufferLength
+ be_start_address = cpu_to_be16(start_address);
+ status = ti_vread_sync(dev, UMPC_MEMORY_READ,
+ (__u16)address_type,
+ (__force __u16)be_start_address,
+ buffer, read_length);
if (status) {
- dbg ("%s - ERROR %x", __func__, status);
+ dbg("%s - ERROR %x", __func__, status);
return status;
}
- if (read_length > 1) {
+ if (read_length > 1)
usb_serial_debug_data(debug, &dev->dev, __func__,
read_length, buffer);
- }
/* Update pointers/length */
start_address += read_length;
buffer += read_length;
length -= read_length;
}
-
+
return status;
}
-static int TIReadRam (struct usb_device *dev, int start_address, int length, __u8 *buffer)
+static int read_ram(struct usb_device *dev, int start_address,
+ int length, __u8 *buffer)
{
- return TIReadDownloadMemory (dev,
- start_address,
- length,
- DTK_ADDR_SPACE_XDATA,
- buffer);
+ return read_download_mem(dev, start_address, length,
+ DTK_ADDR_SPACE_XDATA, buffer);
}
/* Read edgeport memory to a given block */
-static int TIReadBootMemory (struct edgeport_serial *serial, int start_address, int length, __u8 * buffer)
+static int read_boot_mem(struct edgeport_serial *serial,
+ int start_address, int length, __u8 *buffer)
{
int status = 0;
int i;
- for (i=0; i< length; i++) {
- status = TIReadVendorRequestSync (serial->serial->dev,
- UMPC_MEMORY_READ, // Request
- serial->TI_I2C_Type, // wValue (Address type)
- (__u16)(start_address+i), // wIndex
- &buffer[i], // TransferBuffer
- 0x01); // TransferBufferLength
+ for (i = 0; i < length; i++) {
+ status = ti_vread_sync(serial->serial->dev,
+ UMPC_MEMORY_READ, serial->TI_I2C_Type,
+ (__u16)(start_address+i), &buffer[i], 0x01);
if (status) {
- dbg ("%s - ERROR %x", __func__, status);
+ dbg("%s - ERROR %x", __func__, status);
return status;
}
}
- dbg ("%s - start_address = %x, length = %d", __func__, start_address, length);
- usb_serial_debug_data(debug, &serial->serial->dev->dev, __func__, length, buffer);
+ dbg("%s - start_address = %x, length = %d",
+ __func__, start_address, length);
+ usb_serial_debug_data(debug, &serial->serial->dev->dev,
+ __func__, length, buffer);
serial->TiReadI2C = 1;
@@ -448,7 +412,8 @@ static int TIReadBootMemory (struct edgeport_serial *serial, int start_address,
}
/* Write given block to TI EPROM memory */
-static int TIWriteBootMemory (struct edgeport_serial *serial, int start_address, int length, __u8 *buffer)
+static int write_boot_mem(struct edgeport_serial *serial,
+ int start_address, int length, __u8 *buffer)
{
int status = 0;
int i;
@@ -456,57 +421,58 @@ static int TIWriteBootMemory (struct edgeport_serial *serial, int start_address,
/* Must do a read before write */
if (!serial->TiReadI2C) {
- status = TIReadBootMemory(serial, 0, 1, &temp);
+ status = read_boot_mem(serial, 0, 1, &temp);
if (status)
return status;
}
- for (i=0; i < length; ++i) {
- status = TISendVendorRequestSync (serial->serial->dev,
- UMPC_MEMORY_WRITE, // Request
- buffer[i], // wValue
- (__u16)(i+start_address), // wIndex
- NULL, // TransferBuffer
- 0); // TransferBufferLength
+ for (i = 0; i < length; ++i) {
+ status = ti_vsend_sync(serial->serial->dev,
+ UMPC_MEMORY_WRITE, buffer[i],
+ (__u16)(i + start_address), NULL, 0);
if (status)
return status;
}
- dbg ("%s - start_sddr = %x, length = %d", __func__, start_address, length);
- usb_serial_debug_data(debug, &serial->serial->dev->dev, __func__, length, buffer);
+ dbg("%s - start_sddr = %x, length = %d",
+ __func__, start_address, length);
+ usb_serial_debug_data(debug, &serial->serial->dev->dev,
+ __func__, length, buffer);
return status;
}
/* Write edgeport I2C memory to TI chip */
-static int TIWriteDownloadI2C (struct edgeport_serial *serial, int start_address, int length, __u8 address_type, __u8 *buffer)
+static int write_i2c_mem(struct edgeport_serial *serial,
+ int start_address, int length, __u8 address_type, __u8 *buffer)
{
int status = 0;
int write_length;
__be16 be_start_address;
/* We can only send a maximum of 1 aligned byte page at a time */
-
+
/* calulate the number of bytes left in the first page */
- write_length = EPROM_PAGE_SIZE - (start_address & (EPROM_PAGE_SIZE - 1));
+ write_length = EPROM_PAGE_SIZE -
+ (start_address & (EPROM_PAGE_SIZE - 1));
if (write_length > length)
write_length = length;
- dbg ("%s - BytesInFirstPage Addr = %x, length = %d", __func__, start_address, write_length);
- usb_serial_debug_data(debug, &serial->serial->dev->dev, __func__, write_length, buffer);
+ dbg("%s - BytesInFirstPage Addr = %x, length = %d",
+ __func__, start_address, write_length);
+ usb_serial_debug_data(debug, &serial->serial->dev->dev,
+ __func__, write_length, buffer);
/* Write first page */
- be_start_address = cpu_to_be16 (start_address);
- status = TISendVendorRequestSync (serial->serial->dev,
- UMPC_MEMORY_WRITE, // Request
- (__u16)address_type, // wValue
- (__force __u16)be_start_address, // wIndex
- buffer, // TransferBuffer
- write_length);
+ be_start_address = cpu_to_be16(start_address);
+ status = ti_vsend_sync(serial->serial->dev,
+ UMPC_MEMORY_WRITE, (__u16)address_type,
+ (__force __u16)be_start_address,
+ buffer, write_length);
if (status) {
- dbg ("%s - ERROR %d", __func__, status);
+ dbg("%s - ERROR %d", __func__, status);
return status;
}
@@ -514,29 +480,31 @@ static int TIWriteDownloadI2C (struct edgeport_serial *serial, int start_address
start_address += write_length;
buffer += write_length;
- /* We should be aligned now -- can write max page size bytes at a time */
+ /* We should be aligned now -- can write
+ max page size bytes at a time */
while (length) {
if (length > EPROM_PAGE_SIZE)
write_length = EPROM_PAGE_SIZE;
else
write_length = length;
- dbg ("%s - Page Write Addr = %x, length = %d", __func__, start_address, write_length);
- usb_serial_debug_data(debug, &serial->serial->dev->dev, __func__, write_length, buffer);
+ dbg("%s - Page Write Addr = %x, length = %d",
+ __func__, start_address, write_length);
+ usb_serial_debug_data(debug, &serial->serial->dev->dev,
+ __func__, write_length, buffer);
/* Write next page */
- be_start_address = cpu_to_be16 (start_address);
- status = TISendVendorRequestSync (serial->serial->dev,
- UMPC_MEMORY_WRITE, // Request
- (__u16)address_type, // wValue
- (__force __u16)be_start_address, // wIndex
- buffer, // TransferBuffer
- write_length); // TransferBufferLength
+ be_start_address = cpu_to_be16(start_address);
+ status = ti_vsend_sync(serial->serial->dev, UMPC_MEMORY_WRITE,
+ (__u16)address_type,
+ (__force __u16)be_start_address,
+ buffer, write_length);
if (status) {
- dev_err (&serial->serial->dev->dev, "%s - ERROR %d\n", __func__, status);
+ dev_err(&serial->serial->dev->dev, "%s - ERROR %d\n",
+ __func__, status);
return status;
}
-
+
length -= write_length;
start_address += write_length;
buffer += write_length;
@@ -545,25 +513,25 @@ static int TIWriteDownloadI2C (struct edgeport_serial *serial, int start_address
}
/* Examine the UMP DMA registers and LSR
- *
+ *
* Check the MSBit of the X and Y DMA byte count registers.
* A zero in this bit indicates that the TX DMA buffers are empty
* then check the TX Empty bit in the UART.
*/
-static int TIIsTxActive (struct edgeport_port *port)
+static int tx_active(struct edgeport_port *port)
{
int status;
struct out_endpoint_desc_block *oedb;
__u8 *lsr;
int bytes_left = 0;
- oedb = kmalloc (sizeof (* oedb), GFP_KERNEL);
+ oedb = kmalloc(sizeof(*oedb), GFP_KERNEL);
if (!oedb) {
- dev_err (&port->port->dev, "%s - out of memory\n", __func__);
+ dev_err(&port->port->dev, "%s - out of memory\n", __func__);
return -ENOMEM;
}
- lsr = kmalloc (1, GFP_KERNEL); /* Sigh, that's right, just one byte,
+ lsr = kmalloc(1, GFP_KERNEL); /* Sigh, that's right, just one byte,
as not all platforms can do DMA
from stack */
if (!lsr) {
@@ -571,43 +539,39 @@ static int TIIsTxActive (struct edgeport_port *port)
return -ENOMEM;
}
/* Read the DMA Count Registers */
- status = TIReadRam (port->port->serial->dev,
- port->dma_address,
- sizeof( *oedb),
- (void *)oedb);
-
+ status = read_ram(port->port->serial->dev, port->dma_address,
+ sizeof(*oedb), (void *)oedb);
if (status)
goto exit_is_tx_active;
- dbg ("%s - XByteCount 0x%X", __func__, oedb->XByteCount);
+ dbg("%s - XByteCount 0x%X", __func__, oedb->XByteCount);
/* and the LSR */
- status = TIReadRam (port->port->serial->dev,
- port->uart_base + UMPMEM_OFFS_UART_LSR,
- 1,
- lsr);
+ status = read_ram(port->port->serial->dev,
+ port->uart_base + UMPMEM_OFFS_UART_LSR, 1, lsr);
if (status)
goto exit_is_tx_active;
- dbg ("%s - LSR = 0x%X", __func__, *lsr);
-
+ dbg("%s - LSR = 0x%X", __func__, *lsr);
+
/* If either buffer has data or we are transmitting then return TRUE */
- if ((oedb->XByteCount & 0x80 ) != 0 )
+ if ((oedb->XByteCount & 0x80) != 0)
bytes_left += 64;
- if ((*lsr & UMP_UART_LSR_TX_MASK ) == 0 )
+ if ((*lsr & UMP_UART_LSR_TX_MASK) == 0)
bytes_left += 1;
/* We return Not Active if we get any kind of error */
exit_is_tx_active:
- dbg ("%s - return %d", __func__, bytes_left );
+ dbg("%s - return %d", __func__, bytes_left);
kfree(lsr);
kfree(oedb);
return bytes_left;
}
-static void TIChasePort(struct edgeport_port *port, unsigned long timeout, int flush)
+static void chase_port(struct edgeport_port *port, unsigned long timeout,
+ int flush)
{
int baud_rate;
struct tty_struct *tty = port->port->tty;
@@ -615,7 +579,7 @@ static void TIChasePort(struct edgeport_port *port, unsigned long timeout, int f
unsigned long flags;
if (!timeout)
- timeout = (HZ*EDGE_CLOSING_WAIT)/100;
+ timeout = (HZ * EDGE_CLOSING_WAIT)/100;
/* wait for data to drain from the buffer */
spin_lock_irqsave(&port->ep_lock, flags);
@@ -625,7 +589,8 @@ static void TIChasePort(struct edgeport_port *port, unsigned long timeout, int f
set_current_state(TASK_INTERRUPTIBLE);
if (edge_buf_data_avail(port->ep_out_buf) == 0
|| timeout == 0 || signal_pending(current)
- || !usb_get_intfdata(port->port->serial->interface)) /* disconnect */
+ || !usb_get_intfdata(port->port->serial->interface))
+ /* disconnect */
break;
spin_unlock_irqrestore(&port->ep_lock, flags);
timeout = schedule_timeout(timeout);
@@ -640,8 +605,9 @@ static void TIChasePort(struct edgeport_port *port, unsigned long timeout, int f
/* wait for data to drain from the device */
timeout += jiffies;
while ((long)(jiffies - timeout) < 0 && !signal_pending(current)
- && usb_get_intfdata(port->port->serial->interface)) { /* not disconnected */
- if (!TIIsTxActive(port))
+ && usb_get_intfdata(port->port->serial->interface)) {
+ /* not disconnected */
+ if (!tx_active(port))
break;
msleep(10);
}
@@ -651,72 +617,72 @@ static void TIChasePort(struct edgeport_port *port, unsigned long timeout, int f
return;
/* wait one more character time, based on baud rate */
- /* (TIIsTxActive doesn't seem to wait for the last byte) */
- if ((baud_rate=port->baud_rate) == 0)
+ /* (tx_active doesn't seem to wait for the last byte) */
+ baud_rate = port->baud_rate;
+ if (baud_rate == 0)
baud_rate = 50;
msleep(max(1, DIV_ROUND_UP(10000, baud_rate)));
}
-static int TIChooseConfiguration (struct usb_device *dev)
+static int choose_config(struct usb_device *dev)
{
- // There may be multiple configurations on this device, in which case
- // we would need to read and parse all of them to find out which one
- // we want. However, we just support one config at this point,
- // configuration # 1, which is Config Descriptor 0.
+ /*
+ * There may be multiple configurations on this device, in which case
+ * we would need to read and parse all of them to find out which one
+ * we want. However, we just support one config at this point,
+ * configuration # 1, which is Config Descriptor 0.
+ */
- dbg ("%s - Number of Interfaces = %d", __func__, dev->config->desc.bNumInterfaces);
- dbg ("%s - MAX Power = %d", __func__, dev->config->desc.bMaxPower*2);
+ dbg("%s - Number of Interfaces = %d",
+ __func__, dev->config->desc.bNumInterfaces);
+ dbg("%s - MAX Power = %d",
+ __func__, dev->config->desc.bMaxPower * 2);
if (dev->config->desc.bNumInterfaces != 1) {
- dev_err (&dev->dev, "%s - bNumInterfaces is not 1, ERROR!\n", __func__);
+ dev_err(&dev->dev, "%s - bNumInterfaces is not 1, ERROR!\n",
+ __func__);
return -ENODEV;
}
return 0;
}
-static int TIReadRom (struct edgeport_serial *serial, int start_address, int length, __u8 *buffer)
+static int read_rom(struct edgeport_serial *serial,
+ int start_address, int length, __u8 *buffer)
{
int status;
if (serial->product_info.TiMode == TI_MODE_DOWNLOAD) {
- status = TIReadDownloadMemory (serial->serial->dev,
+ status = read_download_mem(serial->serial->dev,
start_address,
length,
serial->TI_I2C_Type,
buffer);
} else {
- status = TIReadBootMemory (serial,
- start_address,
- length,
- buffer);
+ status = read_boot_mem(serial, start_address, length,
+ buffer);
}
-
return status;
}
-static int TIWriteRom (struct edgeport_serial *serial, int start_address, int length, __u8 *buffer)
+static int write_rom(struct edgeport_serial *serial, int start_address,
+ int length, __u8 *buffer)
{
if (serial->product_info.TiMode == TI_MODE_BOOT)
- return TIWriteBootMemory (serial,
- start_address,
- length,
- buffer);
+ return write_boot_mem(serial, start_address, length,
+ buffer);
if (serial->product_info.TiMode == TI_MODE_DOWNLOAD)
- return TIWriteDownloadI2C (serial,
- start_address,
- length,
- serial->TI_I2C_Type,
- buffer);
-
+ return write_i2c_mem(serial, start_address, length,
+ serial->TI_I2C_Type, buffer);
return -EINVAL;
}
/* Read a descriptor header from I2C based on type */
-static int TIGetDescriptorAddress (struct edgeport_serial *serial, int desc_type, struct ti_i2c_desc *rom_desc)
+static int get_descriptor_addr(struct edgeport_serial *serial,
+ int desc_type, struct ti_i2c_desc *rom_desc)
{
int start_address;
int status;
@@ -724,41 +690,42 @@ static int TIGetDescriptorAddress (struct edgeport_serial *serial, int desc_type
/* Search for requested descriptor in I2C */
start_address = 2;
do {
- status = TIReadRom (serial,
+ status = read_rom(serial,
start_address,
sizeof(struct ti_i2c_desc),
- (__u8 *)rom_desc );
+ (__u8 *)rom_desc);
if (status)
return 0;
if (rom_desc->Type == desc_type)
return start_address;
- start_address = start_address + sizeof(struct ti_i2c_desc) + rom_desc->Size;
+ start_address = start_address + sizeof(struct ti_i2c_desc)
+ + rom_desc->Size;
} while ((start_address < TI_MAX_I2C_SIZE) && rom_desc->Type);
-
+
return 0;
}
/* Validate descriptor checksum */
-static int ValidChecksum(struct ti_i2c_desc *rom_desc, __u8 *buffer)
+static int valid_csum(struct ti_i2c_desc *rom_desc, __u8 *buffer)
{
__u16 i;
__u8 cs = 0;
- for (i=0; i < rom_desc->Size; i++) {
+ for (i = 0; i < rom_desc->Size; i++)
cs = (__u8)(cs + buffer[i]);
- }
+
if (cs != rom_desc->CheckSum) {
- dbg ("%s - Mismatch %x - %x", __func__, rom_desc->CheckSum, cs);
+ dbg("%s - Mismatch %x - %x", __func__, rom_desc->CheckSum, cs);
return -EINVAL;
}
return 0;
}
/* Make sure that the I2C image is good */
-static int TiValidateI2cImage (struct edgeport_serial *serial)
+static int check_i2c_image(struct edgeport_serial *serial)
{
struct device *dev = &serial->serial->dev->dev;
int status = 0;
@@ -767,120 +734,124 @@ static int TiValidateI2cImage (struct edgeport_serial *serial)
__u8 *buffer;
__u16 ttype;
- rom_desc = kmalloc (sizeof (*rom_desc), GFP_KERNEL);
+ rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL);
if (!rom_desc) {
- dev_err (dev, "%s - out of memory\n", __func__);
+ dev_err(dev, "%s - out of memory\n", __func__);
return -ENOMEM;
}
- buffer = kmalloc (TI_MAX_I2C_SIZE, GFP_KERNEL);
+ buffer = kmalloc(TI_MAX_I2C_SIZE, GFP_KERNEL);
if (!buffer) {
- dev_err (dev, "%s - out of memory when allocating buffer\n", __func__);
- kfree (rom_desc);
+ dev_err(dev, "%s - out of memory when allocating buffer\n",
+ __func__);
+ kfree(rom_desc);
return -ENOMEM;
}
- // Read the first byte (Signature0) must be 0x52 or 0x10
- status = TIReadRom (serial, 0, 1, buffer);
+ /* Read the first byte (Signature0) must be 0x52 or 0x10 */
+ status = read_rom(serial, 0, 1, buffer);
if (status)
- goto ExitTiValidateI2cImage;
+ goto out;
if (*buffer != UMP5152 && *buffer != UMP3410) {
- dev_err (dev, "%s - invalid buffer signature\n", __func__);
+ dev_err(dev, "%s - invalid buffer signature\n", __func__);
status = -ENODEV;
- goto ExitTiValidateI2cImage;
+ goto out;
}
do {
- // Validate the I2C
- status = TIReadRom (serial,
+ /* Validate the I2C */
+ status = read_rom(serial,
start_address,
sizeof(struct ti_i2c_desc),
(__u8 *)rom_desc);
if (status)
break;
- if ((start_address + sizeof(struct ti_i2c_desc) + rom_desc->Size) > TI_MAX_I2C_SIZE) {
+ if ((start_address + sizeof(struct ti_i2c_desc) +
+ rom_desc->Size) > TI_MAX_I2C_SIZE) {
status = -ENODEV;
- dbg ("%s - structure too big, erroring out.", __func__);
+ dbg("%s - structure too big, erroring out.", __func__);
break;
}
- dbg ("%s Type = 0x%x", __func__, rom_desc->Type);
+ dbg("%s Type = 0x%x", __func__, rom_desc->Type);
- // Skip type 2 record
+ /* Skip type 2 record */
ttype = rom_desc->Type & 0x0f;
- if ( ttype != I2C_DESC_TYPE_FIRMWARE_BASIC
- && ttype != I2C_DESC_TYPE_FIRMWARE_AUTO ) {
- // Read the descriptor data
- status = TIReadRom(serial,
- start_address+sizeof(struct ti_i2c_desc),
- rom_desc->Size,
- buffer);
+ if (ttype != I2C_DESC_TYPE_FIRMWARE_BASIC
+ && ttype != I2C_DESC_TYPE_FIRMWARE_AUTO) {
+ /* Read the descriptor data */
+ status = read_rom(serial, start_address +
+ sizeof(struct ti_i2c_desc),
+ rom_desc->Size, buffer);
if (status)
break;
- status = ValidChecksum(rom_desc, buffer);
+ status = valid_csum(rom_desc, buffer);
if (status)
break;
}
- start_address = start_address + sizeof(struct ti_i2c_desc) + rom_desc->Size;
+ start_address = start_address + sizeof(struct ti_i2c_desc) +
+ rom_desc->Size;
- } while ((rom_desc->Type != I2C_DESC_TYPE_ION) && (start_address < TI_MAX_I2C_SIZE));
+ } while ((rom_desc->Type != I2C_DESC_TYPE_ION) &&
+ (start_address < TI_MAX_I2C_SIZE));
- if ((rom_desc->Type != I2C_DESC_TYPE_ION) || (start_address > TI_MAX_I2C_SIZE))
+ if ((rom_desc->Type != I2C_DESC_TYPE_ION) ||
+ (start_address > TI_MAX_I2C_SIZE))
status = -ENODEV;
-ExitTiValidateI2cImage:
- kfree (buffer);
- kfree (rom_desc);
+out:
+ kfree(buffer);
+ kfree(rom_desc);
return status;
}
-static int TIReadManufDescriptor (struct edgeport_serial *serial, __u8 *buffer)
+static int get_manuf_info(struct edgeport_serial *serial, __u8 *buffer)
{
int status;
int start_address;
struct ti_i2c_desc *rom_desc;
struct edge_ti_manuf_descriptor *desc;
- rom_desc = kmalloc (sizeof (*rom_desc), GFP_KERNEL);
+ rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL);
if (!rom_desc) {
- dev_err (&serial->serial->dev->dev, "%s - out of memory\n", __func__);
+ dev_err(&serial->serial->dev->dev, "%s - out of memory\n",
+ __func__);
return -ENOMEM;
}
- start_address = TIGetDescriptorAddress (serial, I2C_DESC_TYPE_ION, rom_desc);
+ start_address = get_descriptor_addr(serial, I2C_DESC_TYPE_ION,
+ rom_desc);
if (!start_address) {
- dbg ("%s - Edge Descriptor not found in I2C", __func__);
+ dbg("%s - Edge Descriptor not found in I2C", __func__);
status = -ENODEV;
goto exit;
}
- // Read the descriptor data
- status = TIReadRom (serial,
- start_address+sizeof(struct ti_i2c_desc),
- rom_desc->Size,
- buffer);
+ /* Read the descriptor data */
+ status = read_rom(serial, start_address+sizeof(struct ti_i2c_desc),
+ rom_desc->Size, buffer);
if (status)
goto exit;
-
- status = ValidChecksum(rom_desc, buffer);
-
+
+ status = valid_csum(rom_desc, buffer);
+
desc = (struct edge_ti_manuf_descriptor *)buffer;
- dbg ( "%s - IonConfig 0x%x", __func__, desc->IonConfig );
- dbg ( "%s - Version %d", __func__, desc->Version );
- dbg ( "%s - Cpu/Board 0x%x", __func__, desc->CpuRev_BoardRev );
- dbg ( "%s - NumPorts %d", __func__, desc->NumPorts );
- dbg ( "%s - NumVirtualPorts %d", __func__, desc->NumVirtualPorts );
- dbg ( "%s - TotalPorts %d", __func__, desc->TotalPorts );
+ dbg("%s - IonConfig 0x%x", __func__, desc->IonConfig);
+ dbg("%s - Version %d", __func__, desc->Version);
+ dbg("%s - Cpu/Board 0x%x", __func__, desc->CpuRev_BoardRev);
+ dbg("%s - NumPorts %d", __func__, desc->NumPorts);
+ dbg("%s - NumVirtualPorts %d", __func__, desc->NumVirtualPorts);
+ dbg("%s - TotalPorts %d", __func__, desc->TotalPorts);
exit:
- kfree (rom_desc);
+ kfree(rom_desc);
return status;
}
/* Build firmware header used for firmware update */
-static int BuildI2CFirmwareHeader (__u8 *header, struct device *dev)
+static int build_i2c_fw_hdr(__u8 *header, struct device *dev)
{
__u8 *buffer;
int buffer_size;
@@ -890,155 +861,155 @@ static int BuildI2CFirmwareHeader (__u8 *header, struct device *dev)
struct ti_i2c_image_header *img_header;
struct ti_i2c_firmware_rec *firmware_rec;
- // In order to update the I2C firmware we must change the type 2 record to type 0xF2.
- // This will force the UMP to come up in Boot Mode. Then while in boot mode, the driver
- // will download the latest firmware (padded to 15.5k) into the UMP ram.
- // And finally when the device comes back up in download mode the driver will cause
- // the new firmware to be copied from the UMP Ram to I2C and the firmware will update
- // the record type from 0xf2 to 0x02.
-
- // Allocate a 15.5k buffer + 2 bytes for version number (Firmware Record)
- buffer_size = (((1024 * 16) - 512 )+ sizeof(struct ti_i2c_firmware_rec));
-
- buffer = kmalloc (buffer_size, GFP_KERNEL);
+ /*
+ * In order to update the I2C firmware we must change the type 2
+ * record to type 0xF2. This will force the UMP to come up in Boot
+ * Mode. Then while in boot mode, the driver will download the latest
+ * firmware (padded to 15.5k) into the UMP ram. Finally when the device
+ * comes back up in download mode the driver will cause the new
+ * firmware to be copied from the UMP Ram to I2C and the firmware
+ * will update the record type from 0xf2 to 0x02.
+ */
+
+ /* Allocate a 15.5k buffer + 2 bytes for version number
+ (Firmware Record) */
+ buffer_size = (((1024 * 16) - 512) +
+ sizeof(struct ti_i2c_firmware_rec));
+
+ buffer = kmalloc(buffer_size, GFP_KERNEL);
if (!buffer) {
- dev_err (dev, "%s - out of memory\n", __func__);
+ dev_err(dev, "%s - out of memory\n", __func__);
return -ENOMEM;
}
-
- // Set entire image of 0xffs
- memset (buffer, 0xff, buffer_size);
- // Copy version number into firmware record
+ /* Set entire image of 0xffs */
+ memset(buffer, 0xff, buffer_size);
+
+ /* Copy version number into firmware record */
firmware_rec = (struct ti_i2c_firmware_rec *)buffer;
- firmware_rec->Ver_Major = OperationalCodeImageVersion.MajorVersion;
- firmware_rec->Ver_Minor = OperationalCodeImageVersion.MinorVersion;
+ firmware_rec->Ver_Major = op_fw_version.MajorVersion;
+ firmware_rec->Ver_Minor = op_fw_version.MinorVersion;
- // Pointer to fw_down memory image
- img_header = (struct ti_i2c_image_header *)&PagableOperationalCodeImage[0];
+ /* Pointer to fw_down memory image */
+ img_header = (struct ti_i2c_image_header *)&fw_image[0];
- memcpy (buffer + sizeof(struct ti_i2c_firmware_rec),
- &PagableOperationalCodeImage[sizeof(struct ti_i2c_image_header)],
+ memcpy(buffer + sizeof(struct ti_i2c_firmware_rec),
+ &fw_image[sizeof(struct ti_i2c_image_header)],
le16_to_cpu(img_header->Length));
- for (i=0; i < buffer_size; i++) {
+ for (i = 0; i < buffer_size; i++)
cs = (__u8)(cs + buffer[i]);
- }
-
- kfree (buffer);
+ kfree(buffer);
- // Build new header
+ /* Build new header */
i2c_header = (struct ti_i2c_desc *)header;
- firmware_rec = (struct ti_i2c_firmware_rec*)i2c_header->Data;
-
+ firmware_rec = (struct ti_i2c_firmware_rec *)i2c_header->Data;
+
i2c_header->Type = I2C_DESC_TYPE_FIRMWARE_BLANK;
i2c_header->Size = (__u16)buffer_size;
i2c_header->CheckSum = cs;
- firmware_rec->Ver_Major = OperationalCodeImageVersion.MajorVersion;
- firmware_rec->Ver_Minor = OperationalCodeImageVersion.MinorVersion;
+ firmware_rec->Ver_Major = op_fw_version.MajorVersion;
+ firmware_rec->Ver_Minor = op_fw_version.MinorVersion;
return 0;
}
/* Try to figure out what type of I2c we have */
-static int TIGetI2cTypeInBootMode (struct edgeport_serial *serial)
+static int i2c_type_bootmode(struct edgeport_serial *serial)
{
int status;
__u8 data;
-
- // Try to read type 2
- status = TIReadVendorRequestSync (serial->serial->dev,
- UMPC_MEMORY_READ, // Request
- DTK_ADDR_SPACE_I2C_TYPE_II, // wValue (Address type)
- 0, // wIndex
- &data, // TransferBuffer
- 0x01); // TransferBufferLength
+
+ /* Try to read type 2 */
+ status = ti_vread_sync(serial->serial->dev, UMPC_MEMORY_READ,
+ DTK_ADDR_SPACE_I2C_TYPE_II, 0, &data, 0x01);
if (status)
- dbg ("%s - read 2 status error = %d", __func__, status);
+ dbg("%s - read 2 status error = %d", __func__, status);
else
- dbg ("%s - read 2 data = 0x%x", __func__, data);
+ dbg("%s - read 2 data = 0x%x", __func__, data);
if ((!status) && (data == UMP5152 || data == UMP3410)) {
- dbg ("%s - ROM_TYPE_II", __func__);
+ dbg("%s - ROM_TYPE_II", __func__);
serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_II;
return 0;
}
- // Try to read type 3
- status = TIReadVendorRequestSync (serial->serial->dev,
- UMPC_MEMORY_READ, // Request
- DTK_ADDR_SPACE_I2C_TYPE_III, // wValue (Address type)
- 0, // wIndex
- &data, // TransferBuffer
- 0x01); // TransferBufferLength
+ /* Try to read type 3 */
+ status = ti_vread_sync(serial->serial->dev, UMPC_MEMORY_READ,
+ DTK_ADDR_SPACE_I2C_TYPE_III, 0, &data, 0x01);
if (status)
- dbg ("%s - read 3 status error = %d", __func__, status);
+ dbg("%s - read 3 status error = %d", __func__, status);
else
- dbg ("%s - read 2 data = 0x%x", __func__, data);
+ dbg("%s - read 2 data = 0x%x", __func__, data);
if ((!status) && (data == UMP5152 || data == UMP3410)) {
- dbg ("%s - ROM_TYPE_III", __func__);
+ dbg("%s - ROM_TYPE_III", __func__);
serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_III;
return 0;
}
- dbg ("%s - Unknown", __func__);
+ dbg("%s - Unknown", __func__);
serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_II;
return -ENODEV;
}
-static int TISendBulkTransferSync (struct usb_serial *serial, void *buffer, int length, int *num_sent)
+static int bulk_xfer(struct usb_serial *serial, void *buffer,
+ int length, int *num_sent)
{
int status;
- status = usb_bulk_msg (serial->dev,
- usb_sndbulkpipe(serial->dev,
- serial->port[0]->bulk_out_endpointAddress),
- buffer,
- length,
- num_sent,
- 1000);
+ status = usb_bulk_msg(serial->dev,
+ usb_sndbulkpipe(serial->dev,
+ serial->port[0]->bulk_out_endpointAddress),
+ buffer, length, num_sent, 1000);
return status;
}
/* Download given firmware image to the device (IN BOOT MODE) */
-static int TIDownloadCodeImage (struct edgeport_serial *serial, __u8 *image, int image_length)
+static int download_code(struct edgeport_serial *serial, __u8 *image,
+ int image_length)
{
int status = 0;
int pos;
int transfer;
int done;
- // Transfer firmware image
+ /* Transfer firmware image */
for (pos = 0; pos < image_length; ) {
- // Read the next buffer from file
+ /* Read the next buffer from file */
transfer = image_length - pos;
if (transfer > EDGE_FW_BULK_MAX_PACKET_SIZE)
transfer = EDGE_FW_BULK_MAX_PACKET_SIZE;
- // Transfer data
- status = TISendBulkTransferSync (serial->serial, &image[pos], transfer, &done);
+ /* Transfer data */
+ status = bulk_xfer(serial->serial, &image[pos],
+ transfer, &done);
if (status)
break;
- // Advance buffer pointer
+ /* Advance buffer pointer */
pos += done;
}
return status;
}
-// FIXME!!!
-static int TIConfigureBootDevice (struct usb_device *dev)
+/* FIXME!!! */
+static int config_boot_dev(struct usb_device *dev)
{
return 0;
}
+static int ti_cpu_rev(struct edge_ti_manuf_descriptor *desc)
+{
+ return TI_GET_CPU_REVISION(desc->CpuRev_BoardRev);
+}
+
/**
* DownloadTIFirmware - Download run-time operating firmware to the TI5052
- *
+ *
* This routine downloads the main operating code into the TI5052, using the
* boot code already burned into E2PROM or ROM.
*/
-static int TIDownloadFirmware (struct edgeport_serial *serial)
+static int download_fw(struct edgeport_serial *serial)
{
struct device *dev = &serial->serial->dev->dev;
int status = 0;
@@ -1057,28 +1028,31 @@ static int TIDownloadFirmware (struct edgeport_serial *serial)
/* Default to type 2 i2c */
serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_II;
- status = TIChooseConfiguration (serial->serial->dev);
+ status = choose_config(serial->serial->dev);
if (status)
return status;
interface = &serial->serial->interface->cur_altsetting->desc;
if (!interface) {
- dev_err (dev, "%s - no interface set, error!\n", __func__);
+ dev_err(dev, "%s - no interface set, error!\n", __func__);
return -ENODEV;
}
- // Setup initial mode -- the default mode 0 is TI_MODE_CONFIGURING
- // if we have more than one endpoint we are definitely in download mode
+ /*
+ * Setup initial mode -- the default mode 0 is TI_MODE_CONFIGURING
+ * if we have more than one endpoint we are definitely in download
+ * mode
+ */
if (interface->bNumEndpoints > 1)
serial->product_info.TiMode = TI_MODE_DOWNLOAD;
else
- // Otherwise we will remain in configuring mode
+ /* Otherwise we will remain in configuring mode */
serial->product_info.TiMode = TI_MODE_CONFIGURING;
- // Save Download Version Number
- OperationalCodeImageVersion.MajorVersion = PagableOperationalCodeImageVersion.MajorVersion;
- OperationalCodeImageVersion.MinorVersion = PagableOperationalCodeImageVersion.MinorVersion;
- OperationalCodeImageVersion.BuildNumber = PagableOperationalCodeImageVersion.BuildNumber;
+ /* Save Download Version Number */
+ op_fw_version.MajorVersion = fw_version.MajorVersion;
+ op_fw_version.MinorVersion = fw_version.MinorVersion;
+ op_fw_version.BuildNumber = fw_version.BuildNumber;
/********************************************************************/
/* Download Mode */
@@ -1088,256 +1062,276 @@ static int TIDownloadFirmware (struct edgeport_serial *serial)
dbg("%s - RUNNING IN DOWNLOAD MODE", __func__);
- status = TiValidateI2cImage (serial);
+ status = check_i2c_image(serial);
if (status) {
dbg("%s - DOWNLOAD MODE -- BAD I2C", __func__);
return status;
}
-
+
/* Validate Hardware version number
* Read Manufacturing Descriptor from TI Based Edgeport
*/
- ti_manuf_desc = kmalloc (sizeof (*ti_manuf_desc), GFP_KERNEL);
+ ti_manuf_desc = kmalloc(sizeof(*ti_manuf_desc), GFP_KERNEL);
if (!ti_manuf_desc) {
- dev_err (dev, "%s - out of memory.\n", __func__);
+ dev_err(dev, "%s - out of memory.\n", __func__);
return -ENOMEM;
}
- status = TIReadManufDescriptor (serial, (__u8 *)ti_manuf_desc);
+ status = get_manuf_info(serial, (__u8 *)ti_manuf_desc);
if (status) {
- kfree (ti_manuf_desc);
+ kfree(ti_manuf_desc);
return status;
}
- // Check version number of ION descriptor
- if (!ignore_cpu_rev && TI_GET_CPU_REVISION(ti_manuf_desc->CpuRev_BoardRev) < 2) {
- dbg ( "%s - Wrong CPU Rev %d (Must be 2)", __func__,
- TI_GET_CPU_REVISION(ti_manuf_desc->CpuRev_BoardRev));
- kfree (ti_manuf_desc);
- return -EINVAL;
+ /* Check version number of ION descriptor */
+ if (!ignore_cpu_rev && ti_cpu_rev(ti_manuf_desc) < 2) {
+ dbg("%s - Wrong CPU Rev %d (Must be 2)",
+ __func__, ti_cpu_rev(ti_manuf_desc));
+ kfree(ti_manuf_desc);
+ return -EINVAL;
}
- rom_desc = kmalloc (sizeof (*rom_desc), GFP_KERNEL);
+ rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL);
if (!rom_desc) {
- dev_err (dev, "%s - out of memory.\n", __func__);
- kfree (ti_manuf_desc);
+ dev_err(dev, "%s - out of memory.\n", __func__);
+ kfree(ti_manuf_desc);
return -ENOMEM;
}
- // Search for type 2 record (firmware record)
- if ((start_address = TIGetDescriptorAddress (serial, I2C_DESC_TYPE_FIRMWARE_BASIC, rom_desc)) != 0) {
+ /* Search for type 2 record (firmware record) */
+ start_address = get_descriptor_addr(serial,
+ I2C_DESC_TYPE_FIRMWARE_BASIC, rom_desc);
+ if (start_address != 0) {
struct ti_i2c_firmware_rec *firmware_version;
__u8 record;
- dbg ("%s - Found Type FIRMWARE (Type 2) record", __func__);
+ dbg("%s - Found Type FIRMWARE (Type 2) record",
+ __func__);
- firmware_version = kmalloc (sizeof (*firmware_version), GFP_KERNEL);
+ firmware_version = kmalloc(sizeof(*firmware_version),
+ GFP_KERNEL);
if (!firmware_version) {
- dev_err (dev, "%s - out of memory.\n", __func__);
- kfree (rom_desc);
- kfree (ti_manuf_desc);
+ dev_err(dev, "%s - out of memory.\n",
+ __func__);
+ kfree(rom_desc);
+ kfree(ti_manuf_desc);
return -ENOMEM;
}
- // Validate version number
- // Read the descriptor data
- status = TIReadRom (serial,
- start_address+sizeof(struct ti_i2c_desc),
+ /* Validate version number
+ * Read the descriptor data
+ */
+ status = read_rom(serial, start_address +
+ sizeof(struct ti_i2c_desc),
sizeof(struct ti_i2c_firmware_rec),
(__u8 *)firmware_version);
if (status) {
- kfree (firmware_version);
- kfree (rom_desc);
- kfree (ti_manuf_desc);
+ kfree(firmware_version);
+ kfree(rom_desc);
+ kfree(ti_manuf_desc);
return status;
}
- // Check version number of download with current version in I2c
- download_cur_ver = (firmware_version->Ver_Major << 8) +
+ /* Check version number of download with current
+ version in I2c */
+ download_cur_ver = (firmware_version->Ver_Major << 8) +
(firmware_version->Ver_Minor);
- download_new_ver = (OperationalCodeImageVersion.MajorVersion << 8) +
- (OperationalCodeImageVersion.MinorVersion);
+ download_new_ver = (op_fw_version.MajorVersion << 8) +
+ (op_fw_version.MinorVersion);
- dbg ("%s - >>>Firmware Versions Device %d.%d Driver %d.%d",
+ dbg("%s - >>>FW Versions Device %d.%d Driver %d.%d",
__func__,
firmware_version->Ver_Major,
firmware_version->Ver_Minor,
- OperationalCodeImageVersion.MajorVersion,
- OperationalCodeImageVersion.MinorVersion);
+ op_fw_version.MajorVersion,
+ op_fw_version.MinorVersion);
- // Check if we have an old version in the I2C and update if necessary
+ /* Check if we have an old version in the I2C and
+ update if necessary */
if (download_cur_ver != download_new_ver) {
- dbg ("%s - Update I2C Download from %d.%d to %d.%d",
+ dbg("%s - Update I2C dld from %d.%d to %d.%d",
__func__,
firmware_version->Ver_Major,
firmware_version->Ver_Minor,
- OperationalCodeImageVersion.MajorVersion,
- OperationalCodeImageVersion.MinorVersion);
-
- // In order to update the I2C firmware we must change the type 2 record to type 0xF2.
- // This will force the UMP to come up in Boot Mode. Then while in boot mode, the driver
- // will download the latest firmware (padded to 15.5k) into the UMP ram.
- // And finally when the device comes back up in download mode the driver will cause
- // the new firmware to be copied from the UMP Ram to I2C and the firmware will update
- // the record type from 0xf2 to 0x02.
+ op_fw_version.MajorVersion,
+ op_fw_version.MinorVersion);
+
+ /* In order to update the I2C firmware we must
+ * change the type 2 record to type 0xF2. This
+ * will force the UMP to come up in Boot Mode.
+ * Then while in boot mode, the driver will
+ * download the latest firmware (padded to
+ * 15.5k) into the UMP ram. Finally when the
+ * device comes back up in download mode the
+ * driver will cause the new firmware to be
+ * copied from the UMP Ram to I2C and the
+ * firmware will update the record type from
+ * 0xf2 to 0x02.
+ */
record = I2C_DESC_TYPE_FIRMWARE_BLANK;
- // Change the I2C Firmware record type to 0xf2 to trigger an update
- status = TIWriteRom (serial,
- start_address,
- sizeof(record),
- &record);
+ /* Change the I2C Firmware record type to
+ 0xf2 to trigger an update */
+ status = write_rom(serial, start_address,
+ sizeof(record), &record);
if (status) {
- kfree (firmware_version);
- kfree (rom_desc);
- kfree (ti_manuf_desc);
+ kfree(firmware_version);
+ kfree(rom_desc);
+ kfree(ti_manuf_desc);
return status;
}
- // verify the write -- must do this in order for write to
- // complete before we do the hardware reset
- status = TIReadRom (serial,
+ /* verify the write -- must do this in order
+ * for write to complete before we do the
+ * hardware reset
+ */
+ status = read_rom(serial,
start_address,
sizeof(record),
&record);
if (status) {
- kfree (firmware_version);
- kfree (rom_desc);
- kfree (ti_manuf_desc);
+ kfree(firmware_version);
+ kfree(rom_desc);
+ kfree(ti_manuf_desc);
return status;
}
if (record != I2C_DESC_TYPE_FIRMWARE_BLANK) {
- dev_err (dev, "%s - error resetting device\n", __func__);
- kfree (firmware_version);
- kfree (rom_desc);
- kfree (ti_manuf_desc);
+ dev_err(dev,
+ "%s - error resetting device\n",
+ __func__);
+ kfree(firmware_version);
+ kfree(rom_desc);
+ kfree(ti_manuf_desc);
return -ENODEV;
}
- dbg ("%s - HARDWARE RESET", __func__);
+ dbg("%s - HARDWARE RESET", __func__);
- // Reset UMP -- Back to BOOT MODE
- status = TISendVendorRequestSync (serial->serial->dev,
- UMPC_HARDWARE_RESET, // Request
- 0, // wValue
- 0, // wIndex
- NULL, // TransferBuffer
- 0); // TransferBufferLength
+ /* Reset UMP -- Back to BOOT MODE */
+ status = ti_vsend_sync(serial->serial->dev,
+ UMPC_HARDWARE_RESET,
+ 0, 0, NULL, 0);
- dbg ( "%s - HARDWARE RESET return %d", __func__, status);
+ dbg("%s - HARDWARE RESET return %d",
+ __func__, status);
/* return an error on purpose. */
- kfree (firmware_version);
- kfree (rom_desc);
- kfree (ti_manuf_desc);
+ kfree(firmware_version);
+ kfree(rom_desc);
+ kfree(ti_manuf_desc);
return -ENODEV;
}
- kfree (firmware_version);
+ kfree(firmware_version);
}
- // Search for type 0xF2 record (firmware blank record)
- else if ((start_address = TIGetDescriptorAddress (serial, I2C_DESC_TYPE_FIRMWARE_BLANK, rom_desc)) != 0) {
- #define HEADER_SIZE (sizeof(struct ti_i2c_desc) + sizeof(struct ti_i2c_firmware_rec))
+ /* Search for type 0xF2 record (firmware blank record) */
+ else if ((start_address = get_descriptor_addr(serial, I2C_DESC_TYPE_FIRMWARE_BLANK, rom_desc)) != 0) {
+#define HEADER_SIZE (sizeof(struct ti_i2c_desc) + \
+ sizeof(struct ti_i2c_firmware_rec))
__u8 *header;
__u8 *vheader;
- header = kmalloc (HEADER_SIZE, GFP_KERNEL);
+ header = kmalloc(HEADER_SIZE, GFP_KERNEL);
if (!header) {
- dev_err (dev, "%s - out of memory.\n", __func__);
- kfree (rom_desc);
- kfree (ti_manuf_desc);
+ dev_err(dev, "%s - out of memory.\n", __func__);
+ kfree(rom_desc);
+ kfree(ti_manuf_desc);
return -ENOMEM;
}
-
- vheader = kmalloc (HEADER_SIZE, GFP_KERNEL);
+
+ vheader = kmalloc(HEADER_SIZE, GFP_KERNEL);
if (!vheader) {
- dev_err (dev, "%s - out of memory.\n", __func__);
- kfree (header);
- kfree (rom_desc);
- kfree (ti_manuf_desc);
+ dev_err(dev, "%s - out of memory.\n", __func__);
+ kfree(header);
+ kfree(rom_desc);
+ kfree(ti_manuf_desc);
return -ENOMEM;
}
-
- dbg ("%s - Found Type BLANK FIRMWARE (Type F2) record", __func__);
-
- // In order to update the I2C firmware we must change the type 2 record to type 0xF2.
- // This will force the UMP to come up in Boot Mode. Then while in boot mode, the driver
- // will download the latest firmware (padded to 15.5k) into the UMP ram.
- // And finally when the device comes back up in download mode the driver will cause
- // the new firmware to be copied from the UMP Ram to I2C and the firmware will update
- // the record type from 0xf2 to 0x02.
- status = BuildI2CFirmwareHeader(header, dev);
+
+ dbg("%s - Found Type BLANK FIRMWARE (Type F2) record",
+ __func__);
+
+ /*
+ * In order to update the I2C firmware we must change
+ * the type 2 record to type 0xF2. This will force the
+ * UMP to come up in Boot Mode. Then while in boot
+ * mode, the driver will download the latest firmware
+ * (padded to 15.5k) into the UMP ram. Finally when the
+ * device comes back up in download mode the driver
+ * will cause the new firmware to be copied from the
+ * UMP Ram to I2C and the firmware will update the
+ * record type from 0xf2 to 0x02.
+ */
+ status = build_i2c_fw_hdr(header, dev);
if (status) {
- kfree (vheader);
- kfree (header);
- kfree (rom_desc);
- kfree (ti_manuf_desc);
+ kfree(vheader);
+ kfree(header);
+ kfree(rom_desc);
+ kfree(ti_manuf_desc);
return status;
}
- // Update I2C with type 0xf2 record with correct size and checksum
- status = TIWriteRom (serial,
+ /* Update I2C with type 0xf2 record with correct
+ size and checksum */
+ status = write_rom(serial,
start_address,
HEADER_SIZE,
header);
if (status) {
- kfree (vheader);
- kfree (header);
- kfree (rom_desc);
- kfree (ti_manuf_desc);
+ kfree(vheader);
+ kfree(header);
+ kfree(rom_desc);
+ kfree(ti_manuf_desc);
return status;
}
- // verify the write -- must do this in order for write to
- // complete before we do the hardware reset
- status = TIReadRom (serial,
- start_address,
- HEADER_SIZE,
- vheader);
+ /* verify the write -- must do this in order for
+ write to complete before we do the hardware reset */
+ status = read_rom(serial, start_address,
+ HEADER_SIZE, vheader);
if (status) {
- dbg ("%s - can't read header back", __func__);
- kfree (vheader);
- kfree (header);
- kfree (rom_desc);
- kfree (ti_manuf_desc);
+ dbg("%s - can't read header back", __func__);
+ kfree(vheader);
+ kfree(header);
+ kfree(rom_desc);
+ kfree(ti_manuf_desc);
return status;
}
if (memcmp(vheader, header, HEADER_SIZE)) {
- dbg ("%s - write download record failed", __func__);
- kfree (vheader);
- kfree (header);
- kfree (rom_desc);
- kfree (ti_manuf_desc);
+ dbg("%s - write download record failed",
+ __func__);
+ kfree(vheader);
+ kfree(header);
+ kfree(rom_desc);
+ kfree(ti_manuf_desc);
return status;
}
- kfree (vheader);
- kfree (header);
+ kfree(vheader);
+ kfree(header);
- dbg ("%s - Start firmware update", __func__);
+ dbg("%s - Start firmware update", __func__);
- // Tell firmware to copy download image into I2C
- status = TISendVendorRequestSync (serial->serial->dev,
- UMPC_COPY_DNLD_TO_I2C, // Request
- 0, // wValue
- 0, // wIndex
- NULL, // TransferBuffer
- 0); // TransferBufferLength
+ /* Tell firmware to copy download image into I2C */
+ status = ti_vsend_sync(serial->serial->dev,
+ UMPC_COPY_DNLD_TO_I2C, 0, 0, NULL, 0);
- dbg ("%s - Update complete 0x%x", __func__, status);
+ dbg("%s - Update complete 0x%x", __func__, status);
if (status) {
- dev_err (dev, "%s - UMPC_COPY_DNLD_TO_I2C failed\n", __func__);
- kfree (rom_desc);
- kfree (ti_manuf_desc);
+ dev_err(dev,
+ "%s - UMPC_COPY_DNLD_TO_I2C failed\n",
+ __func__);
+ kfree(rom_desc);
+ kfree(ti_manuf_desc);
return status;
}
}
- // The device is running the download code
- kfree (rom_desc);
- kfree (ti_manuf_desc);
+ /* The device is running the download code */
+ kfree(rom_desc);
+ kfree(ti_manuf_desc);
return 0;
}
@@ -1346,32 +1340,26 @@ static int TIDownloadFirmware (struct edgeport_serial *serial)
/********************************************************************/
dbg("%s - RUNNING IN BOOT MODE", __func__);
- // Configure the TI device so we can use the BULK pipes for download
- status = TIConfigureBootDevice (serial->serial->dev);
+ /* Configure the TI device so we can use the BULK pipes for download */
+ status = config_boot_dev(serial->serial->dev);
if (status)
return status;
- if (le16_to_cpu(serial->serial->dev->descriptor.idVendor) != USB_VENDOR_ID_ION) {
- dbg ("%s - VID = 0x%x", __func__,
+ if (le16_to_cpu(serial->serial->dev->descriptor.idVendor)
+ != USB_VENDOR_ID_ION) {
+ dbg("%s - VID = 0x%x", __func__,
le16_to_cpu(serial->serial->dev->descriptor.idVendor));
serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_II;
- goto StayInBootMode;
+ goto stayinbootmode;
}
- // We have an ION device (I2c Must be programmed)
- // Determine I2C image type
- if (TIGetI2cTypeInBootMode(serial)) {
- goto StayInBootMode;
- }
+ /* We have an ION device (I2c Must be programmed)
+ Determine I2C image type */
+ if (i2c_type_bootmode(serial))
+ goto stayinbootmode;
- // Registry variable set?
- if (TIStayInBootMode) {
- dbg ("%s - TIStayInBootMode", __func__);
- goto StayInBootMode;
- }
-
- // Check for ION Vendor ID and that the I2C is valid
- if (!TiValidateI2cImage(serial)) {
+ /* Check for ION Vendor ID and that the I2C is valid */
+ if (!check_i2c_image(serial)) {
struct ti_i2c_image_header *header;
int i;
__u8 cs = 0;
@@ -1381,241 +1369,124 @@ static int TIDownloadFirmware (struct edgeport_serial *serial)
/* Validate Hardware version number
* Read Manufacturing Descriptor from TI Based Edgeport
*/
- ti_manuf_desc = kmalloc (sizeof (*ti_manuf_desc), GFP_KERNEL);
+ ti_manuf_desc = kmalloc(sizeof(*ti_manuf_desc), GFP_KERNEL);
if (!ti_manuf_desc) {
- dev_err (dev, "%s - out of memory.\n", __func__);
+ dev_err(dev, "%s - out of memory.\n", __func__);
return -ENOMEM;
}
- status = TIReadManufDescriptor (serial, (__u8 *)ti_manuf_desc);
+ status = get_manuf_info(serial, (__u8 *)ti_manuf_desc);
if (status) {
- kfree (ti_manuf_desc);
- goto StayInBootMode;
+ kfree(ti_manuf_desc);
+ goto stayinbootmode;
}
- // Check for version 2
- if (!ignore_cpu_rev && TI_GET_CPU_REVISION(ti_manuf_desc->CpuRev_BoardRev) < 2) {
- dbg ("%s - Wrong CPU Rev %d (Must be 2)", __func__,
- TI_GET_CPU_REVISION(ti_manuf_desc->CpuRev_BoardRev));
- kfree (ti_manuf_desc);
- goto StayInBootMode;
+ /* Check for version 2 */
+ if (!ignore_cpu_rev && ti_cpu_rev(ti_manuf_desc) < 2) {
+ dbg("%s - Wrong CPU Rev %d (Must be 2)",
+ __func__, ti_cpu_rev(ti_manuf_desc));
+ kfree(ti_manuf_desc);
+ goto stayinbootmode;
}
- kfree (ti_manuf_desc);
+ kfree(ti_manuf_desc);
- // In order to update the I2C firmware we must change the type 2 record to type 0xF2.
- // This will force the UMP to come up in Boot Mode. Then while in boot mode, the driver
- // will download the latest firmware (padded to 15.5k) into the UMP ram.
- // And finally when the device comes back up in download mode the driver will cause
- // the new firmware to be copied from the UMP Ram to I2C and the firmware will update
- // the record type from 0xf2 to 0x02.
-
/*
+ * In order to update the I2C firmware we must change the type
+ * 2 record to type 0xF2. This will force the UMP to come up
+ * in Boot Mode. Then while in boot mode, the driver will
+ * download the latest firmware (padded to 15.5k) into the
+ * UMP ram. Finally when the device comes back up in download
+ * mode the driver will cause the new firmware to be copied
+ * from the UMP Ram to I2C and the firmware will update the
+ * record type from 0xf2 to 0x02.
+ *
* Do we really have to copy the whole firmware image,
* or could we do this in place!
*/
- // Allocate a 15.5k buffer + 3 byte header
- buffer_size = (((1024 * 16) - 512) + sizeof(struct ti_i2c_image_header));
- buffer = kmalloc (buffer_size, GFP_KERNEL);
+ /* Allocate a 15.5k buffer + 3 byte header */
+ buffer_size = (((1024 * 16) - 512) +
+ sizeof(struct ti_i2c_image_header));
+ buffer = kmalloc(buffer_size, GFP_KERNEL);
if (!buffer) {
- dev_err (dev, "%s - out of memory\n", __func__);
+ dev_err(dev, "%s - out of memory\n", __func__);
return -ENOMEM;
}
-
- // Initialize the buffer to 0xff (pad the buffer)
- memset (buffer, 0xff, buffer_size);
- memcpy (buffer, &PagableOperationalCodeImage[0], PagableOperationalCodeSize);
+ /* Initialize the buffer to 0xff (pad the buffer) */
+ memset(buffer, 0xff, buffer_size);
+ memcpy(buffer, &fw_image[0], fw_size);
- for(i = sizeof(struct ti_i2c_image_header); i < buffer_size; i++) {
+ for (i = sizeof(struct ti_i2c_image_header);
+ i < buffer_size; i++)
cs = (__u8)(cs + buffer[i]);
- }
-
+
header = (struct ti_i2c_image_header *)buffer;
-
- // update length and checksum after padding
- header->Length = cpu_to_le16((__u16)(buffer_size - sizeof(struct ti_i2c_image_header)));
+
+ /* update length and checksum after padding */
+ header->Length = cpu_to_le16((__u16)(buffer_size -
+ sizeof(struct ti_i2c_image_header)));
header->CheckSum = cs;
- // Download the operational code
- dbg ("%s - Downloading operational code image (TI UMP)", __func__);
- status = TIDownloadCodeImage (serial, buffer, buffer_size);
+ /* Download the operational code */
+ dbg("%s - Downloading operational code image (TI UMP)",
+ __func__);
+ status = download_code(serial, buffer, buffer_size);
- kfree (buffer);
+ kfree(buffer);
if (status) {
- dbg ("%s - Error downloading operational code image", __func__);
+ dbg("%s - Error downloading operational code image",
+ __func__);
return status;
}
- // Device will reboot
+ /* Device will reboot */
serial->product_info.TiMode = TI_MODE_TRANSITIONING;
- dbg ("%s - Download successful -- Device rebooting...", __func__);
+ dbg("%s - Download successful -- Device rebooting...",
+ __func__);
/* return an error on purpose */
return -ENODEV;
}
-StayInBootMode:
- // Eprom is invalid or blank stay in boot mode
+stayinbootmode:
+ /* Eprom is invalid or blank stay in boot mode */
dbg("%s - STAYING IN BOOT MODE", __func__);
serial->product_info.TiMode = TI_MODE_BOOT;
return 0;
}
-
-static int TISetDtr (struct edgeport_port *port)
-{
- int port_number = port->port->number - port->port->serial->minor;
-
- dbg ("%s", __func__);
- port->shadow_mcr |= MCR_DTR;
-
- return TIWriteCommandSync (port->port->serial->dev,
- UMPC_SET_CLR_DTR,
- (__u8)(UMPM_UART1_PORT + port_number),
- 1, /* set */
- NULL,
- 0);
-}
-
-static int TIClearDtr (struct edgeport_port *port)
+static int ti_do_config(struct edgeport_port *port, int feature, int on)
{
int port_number = port->port->number - port->port->serial->minor;
-
- dbg ("%s", __func__);
- port->shadow_mcr &= ~MCR_DTR;
-
- return TIWriteCommandSync (port->port->serial->dev,
- UMPC_SET_CLR_DTR,
- (__u8)(UMPM_UART1_PORT + port_number),
- 0, /* clear */
- NULL,
- 0);
+ on = !!on; /* 1 or 0 not bitmask */
+ return send_cmd(port->port->serial->dev,
+ feature, (__u8)(UMPM_UART1_PORT + port_number),
+ on, NULL, 0);
}
-static int TISetRts (struct edgeport_port *port)
-{
- int port_number = port->port->number - port->port->serial->minor;
-
- dbg ("%s", __func__);
- port->shadow_mcr |= MCR_RTS;
-
- return TIWriteCommandSync (port->port->serial->dev,
- UMPC_SET_CLR_RTS,
- (__u8)(UMPM_UART1_PORT + port_number),
- 1, /* set */
- NULL,
- 0);
-}
-
-static int TIClearRts (struct edgeport_port *port)
-{
- int port_number = port->port->number - port->port->serial->minor;
-
- dbg ("%s", __func__);
- port->shadow_mcr &= ~MCR_RTS;
-
- return TIWriteCommandSync (port->port->serial->dev,
- UMPC_SET_CLR_RTS,
- (__u8)(UMPM_UART1_PORT + port_number),
- 0, /* clear */
- NULL,
- 0);
-}
-
-static int TISetLoopBack (struct edgeport_port *port)
-{
- int port_number = port->port->number - port->port->serial->minor;
-
- dbg ("%s", __func__);
-
- return TIWriteCommandSync (port->port->serial->dev,
- UMPC_SET_CLR_LOOPBACK,
- (__u8)(UMPM_UART1_PORT + port_number),
- 1, /* set */
- NULL,
- 0);
-}
-
-static int TIClearLoopBack (struct edgeport_port *port)
-{
- int port_number = port->port->number - port->port->serial->minor;
-
- dbg ("%s", __func__);
-
- return TIWriteCommandSync (port->port->serial->dev,
- UMPC_SET_CLR_LOOPBACK,
- (__u8)(UMPM_UART1_PORT + port_number),
- 0, /* clear */
- NULL,
- 0);
-}
-
-static int TISetBreak (struct edgeport_port *port)
-{
- int port_number = port->port->number - port->port->serial->minor;
-
- dbg ("%s", __func__);
-
- return TIWriteCommandSync (port->port->serial->dev,
- UMPC_SET_CLR_BREAK,
- (__u8)(UMPM_UART1_PORT + port_number),
- 1, /* set */
- NULL,
- 0);
-}
-
-static int TIClearBreak (struct edgeport_port *port)
-{
- int port_number = port->port->number - port->port->serial->minor;
-
- dbg ("%s", __func__);
-
- return TIWriteCommandSync (port->port->serial->dev,
- UMPC_SET_CLR_BREAK,
- (__u8)(UMPM_UART1_PORT + port_number),
- 0, /* clear */
- NULL,
- 0);
-}
-
-static int TIRestoreMCR (struct edgeport_port *port, __u8 mcr)
+static int restore_mcr(struct edgeport_port *port, __u8 mcr)
{
int status = 0;
- dbg ("%s - %x", __func__, mcr);
-
- if (mcr & MCR_DTR)
- status = TISetDtr (port);
- else
- status = TIClearDtr (port);
+ dbg("%s - %x", __func__, mcr);
+ status = ti_do_config(port, UMPC_SET_CLR_DTR, mcr & MCR_DTR);
if (status)
return status;
-
- if (mcr & MCR_RTS)
- status = TISetRts (port);
- else
- status = TIClearRts (port);
-
+ status = ti_do_config(port, UMPC_SET_CLR_RTS, mcr & MCR_RTS);
if (status)
return status;
-
- if (mcr & MCR_LOOPBACK)
- status = TISetLoopBack (port);
- else
- status = TIClearLoopBack (port);
-
- return status;
+ return ti_do_config(port, UMPC_SET_CLR_LOOPBACK, mcr & MCR_LOOPBACK);
}
/* Convert TI LSR to standard UART flags */
-static __u8 MapLineStatus (__u8 ti_lsr)
+static __u8 map_line_status(__u8 ti_lsr)
{
__u8 lsr = 0;
@@ -1627,22 +1498,23 @@ static __u8 MapLineStatus (__u8 ti_lsr)
MAP_FLAG(UMP_UART_LSR_PE_MASK, LSR_PAR_ERR) /* parity error */
MAP_FLAG(UMP_UART_LSR_FE_MASK, LSR_FRM_ERR) /* framing error */
MAP_FLAG(UMP_UART_LSR_BR_MASK, LSR_BREAK) /* break detected */
- MAP_FLAG(UMP_UART_LSR_RX_MASK, LSR_RX_AVAIL) /* receive data available */
- MAP_FLAG(UMP_UART_LSR_TX_MASK, LSR_TX_EMPTY) /* transmit holding register empty */
+ MAP_FLAG(UMP_UART_LSR_RX_MASK, LSR_RX_AVAIL) /* rx data available */
+ MAP_FLAG(UMP_UART_LSR_TX_MASK, LSR_TX_EMPTY) /* tx hold reg empty */
#undef MAP_FLAG
return lsr;
}
-static void handle_new_msr (struct edgeport_port *edge_port, __u8 msr)
+static void handle_new_msr(struct edgeport_port *edge_port, __u8 msr)
{
struct async_icount *icount;
struct tty_struct *tty;
- dbg ("%s - %02x", __func__, msr);
+ dbg("%s - %02x", __func__, msr);
- if (msr & (EDGEPORT_MSR_DELTA_CTS | EDGEPORT_MSR_DELTA_DSR | EDGEPORT_MSR_DELTA_RI | EDGEPORT_MSR_DELTA_CD)) {
+ if (msr & (EDGEPORT_MSR_DELTA_CTS | EDGEPORT_MSR_DELTA_DSR |
+ EDGEPORT_MSR_DELTA_RI | EDGEPORT_MSR_DELTA_CD)) {
icount = &edge_port->icount;
/* update input line counters */
@@ -1654,7 +1526,7 @@ static void handle_new_msr (struct edgeport_port *edge_port, __u8 msr)
icount->dcd++;
if (msr & EDGEPORT_MSR_DELTA_RI)
icount->rng++;
- wake_up_interruptible (&edge_port->delta_msr_wait);
+ wake_up_interruptible(&edge_port->delta_msr_wait);
}
/* Save the new modem status */
@@ -1674,26 +1546,28 @@ static void handle_new_msr (struct edgeport_port *edge_port, __u8 msr)
return;
}
-static void handle_new_lsr (struct edgeport_port *edge_port, int lsr_data, __u8 lsr, __u8 data)
+static void handle_new_lsr(struct edgeport_port *edge_port, int lsr_data,
+ __u8 lsr, __u8 data)
{
struct async_icount *icount;
- __u8 new_lsr = (__u8)(lsr & (__u8)(LSR_OVER_ERR | LSR_PAR_ERR | LSR_FRM_ERR | LSR_BREAK));
+ __u8 new_lsr = (__u8)(lsr & (__u8)(LSR_OVER_ERR | LSR_PAR_ERR |
+ LSR_FRM_ERR | LSR_BREAK));
- dbg ("%s - %02x", __func__, new_lsr);
+ dbg("%s - %02x", __func__, new_lsr);
edge_port->shadow_lsr = lsr;
- if (new_lsr & LSR_BREAK) {
+ if (new_lsr & LSR_BREAK)
/*
* Parity and Framing errors only count if they
* occur exclusive of a break being received.
*/
new_lsr &= (__u8)(LSR_OVER_ERR | LSR_BREAK);
- }
/* Place LSR data byte into Rx buffer */
if (lsr_data && edge_port->port->tty)
- edge_tty_recv(&edge_port->port->dev, edge_port->port->tty, &data, 1);
+ edge_tty_recv(&edge_port->port->dev, edge_port->port->tty,
+ &data, 1);
/* update input line counters */
icount = &edge_port->icount;
@@ -1708,7 +1582,7 @@ static void handle_new_lsr (struct edgeport_port *edge_port, int lsr_data, __u8
}
-static void edge_interrupt_callback (struct urb *urb)
+static void edge_interrupt_callback(struct urb *urb)
{
struct edgeport_serial *edge_serial = urb->context;
struct usb_serial_port *port;
@@ -1742,66 +1616,71 @@ static void edge_interrupt_callback (struct urb *urb)
}
if (!length) {
- dbg ("%s - no data in urb", __func__);
+ dbg("%s - no data in urb", __func__);
goto exit;
}
-
- usb_serial_debug_data(debug, &edge_serial->serial->dev->dev, __func__, length, data);
-
+
+ usb_serial_debug_data(debug, &edge_serial->serial->dev->dev,
+ __func__, length, data);
+
if (length != 2) {
- dbg ("%s - expecting packet of size 2, got %d", __func__, length);
+ dbg("%s - expecting packet of size 2, got %d",
+ __func__, length);
goto exit;
}
- port_number = TIUMP_GET_PORT_FROM_CODE (data[0]);
- function = TIUMP_GET_FUNC_FROM_CODE (data[0]);
- dbg ("%s - port_number %d, function %d, info 0x%x",
+ port_number = TIUMP_GET_PORT_FROM_CODE(data[0]);
+ function = TIUMP_GET_FUNC_FROM_CODE(data[0]);
+ dbg("%s - port_number %d, function %d, info 0x%x",
__func__, port_number, function, data[1]);
port = edge_serial->serial->port[port_number];
edge_port = usb_get_serial_port_data(port);
if (!edge_port) {
- dbg ("%s - edge_port not found", __func__);
+ dbg("%s - edge_port not found", __func__);
return;
}
switch (function) {
case TIUMP_INTERRUPT_CODE_LSR:
- lsr = MapLineStatus(data[1]);
+ lsr = map_line_status(data[1]);
if (lsr & UMP_UART_LSR_DATA_MASK) {
- /* Save the LSR event for bulk read completion routine */
- dbg ("%s - LSR Event Port %u LSR Status = %02x",
+ /* Save the LSR event for bulk read
+ completion routine */
+ dbg("%s - LSR Event Port %u LSR Status = %02x",
__func__, port_number, lsr);
edge_port->lsr_event = 1;
edge_port->lsr_mask = lsr;
} else {
- dbg ("%s - ===== Port %d LSR Status = %02x ======",
+ dbg("%s - ===== Port %d LSR Status = %02x ======",
__func__, port_number, lsr);
- handle_new_lsr (edge_port, 0, lsr, 0);
+ handle_new_lsr(edge_port, 0, lsr, 0);
}
break;
- case TIUMP_INTERRUPT_CODE_MSR: // MSR
+ case TIUMP_INTERRUPT_CODE_MSR: /* MSR */
/* Copy MSR from UMP */
msr = data[1];
- dbg ("%s - ===== Port %u MSR Status = %02x ======\n",
+ dbg("%s - ===== Port %u MSR Status = %02x ======\n",
__func__, port_number, msr);
- handle_new_msr (edge_port, msr);
+ handle_new_msr(edge_port, msr);
break;
default:
- dev_err (&urb->dev->dev, "%s - Unknown Interrupt code from UMP %x\n",
- __func__, data[1]);
+ dev_err(&urb->dev->dev,
+ "%s - Unknown Interrupt code from UMP %x\n",
+ __func__, data[1]);
break;
-
+
}
exit:
- retval = usb_submit_urb (urb, GFP_ATOMIC);
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
if (retval)
- dev_err (&urb->dev->dev, "%s - usb_submit_urb failed with result %d\n",
+ dev_err(&urb->dev->dev,
+ "%s - usb_submit_urb failed with result %d\n",
__func__, retval);
}
-static void edge_bulk_in_callback (struct urb *urb)
+static void edge_bulk_in_callback(struct urb *urb)
{
struct edgeport_port *edge_port = urb->context;
unsigned char *data = urb->transfer_buffer;
@@ -1824,15 +1703,16 @@ static void edge_bulk_in_callback (struct urb *urb)
__func__, status);
return;
default:
- dev_err (&urb->dev->dev,"%s - nonzero read bulk status received: %d\n",
- __func__, status);
+ dev_err(&urb->dev->dev,
+ "%s - nonzero read bulk status received: %d\n",
+ __func__, status);
}
if (status == -EPIPE)
goto exit;
if (status) {
- dev_err(&urb->dev->dev,"%s - stopping read!\n", __func__);
+ dev_err(&urb->dev->dev, "%s - stopping read!\n", __func__);
return;
}
@@ -1840,9 +1720,9 @@ static void edge_bulk_in_callback (struct urb *urb)
if (edge_port->lsr_event) {
edge_port->lsr_event = 0;
- dbg ("%s ===== Port %u LSR Status = %02x, Data = %02x ======",
+ dbg("%s ===== Port %u LSR Status = %02x, Data = %02x ======",
__func__, port_number, edge_port->lsr_mask, *data);
- handle_new_lsr (edge_port, 1, edge_port->lsr_mask, *data);
+ handle_new_lsr(edge_port, 1, edge_port->lsr_mask, *data);
/* Adjust buffer length/pointer */
--urb->actual_length;
++data;
@@ -1850,13 +1730,14 @@ static void edge_bulk_in_callback (struct urb *urb)
tty = edge_port->port->tty;
if (tty && urb->actual_length) {
- usb_serial_debug_data(debug, &edge_port->port->dev, __func__, urb->actual_length, data);
-
- if (edge_port->close_pending) {
- dbg ("%s - close is pending, dropping data on the floor.", __func__);
- } else {
- edge_tty_recv(&edge_port->port->dev, tty, data, urb->actual_length);
- }
+ usb_serial_debug_data(debug, &edge_port->port->dev,
+ __func__, urb->actual_length, data);
+ if (edge_port->close_pending)
+ dbg("%s - close pending, dropping data on the floor",
+ __func__);
+ else
+ edge_tty_recv(&edge_port->port->dev, tty, data,
+ urb->actual_length);
edge_port->icount.rx += urb->actual_length;
}
@@ -1871,37 +1752,31 @@ exit:
}
spin_unlock(&edge_port->ep_lock);
if (retval)
- dev_err (&urb->dev->dev, "%s - usb_submit_urb failed with result %d\n",
+ dev_err(&urb->dev->dev,
+ "%s - usb_submit_urb failed with result %d\n",
__func__, retval);
}
-static void edge_tty_recv(struct device *dev, struct tty_struct *tty, unsigned char *data, int length)
+static void edge_tty_recv(struct device *dev, struct tty_struct *tty,
+ unsigned char *data, int length)
{
- int cnt;
-
- do {
- cnt = tty_buffer_request_room(tty, length);
- if (cnt < length) {
- dev_err(dev, "%s - dropping data, %d bytes lost\n",
- __func__, length - cnt);
- if(cnt == 0)
- break;
- }
- tty_insert_flip_string(tty, data, cnt);
- data += cnt;
- length -= cnt;
- } while (length > 0);
+ int queued;
+ tty_buffer_request_room(tty, length);
+ queued = tty_insert_flip_string(tty, data, length);
+ if (queued < length)
+ dev_err(dev, "%s - dropping data, %d bytes lost\n",
+ __func__, length - queued);
tty_flip_buffer_push(tty);
}
-static void edge_bulk_out_callback (struct urb *urb)
+static void edge_bulk_out_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
int status = urb->status;
- dbg ("%s - port %d", __func__, port->number);
+ dbg("%s - port %d", __func__, port->number);
edge_port->ep_write_urb_in_use = 0;
@@ -1925,7 +1800,7 @@ static void edge_bulk_out_callback (struct urb *urb)
edge_send(port);
}
-static int edge_open (struct usb_serial_port *port, struct file * filp)
+static int edge_open(struct usb_serial_port *port, struct file *filp)
{
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
struct edgeport_serial *edge_serial;
@@ -1945,118 +1820,119 @@ static int edge_open (struct usb_serial_port *port, struct file * filp)
port_number = port->number - port->serial->minor;
switch (port_number) {
- case 0:
- edge_port->uart_base = UMPMEM_BASE_UART1;
- edge_port->dma_address = UMPD_OEDB1_ADDRESS;
- break;
- case 1:
- edge_port->uart_base = UMPMEM_BASE_UART2;
- edge_port->dma_address = UMPD_OEDB2_ADDRESS;
- break;
- default:
- dev_err (&port->dev, "Unknown port number!!!\n");
- return -ENODEV;
+ case 0:
+ edge_port->uart_base = UMPMEM_BASE_UART1;
+ edge_port->dma_address = UMPD_OEDB1_ADDRESS;
+ break;
+ case 1:
+ edge_port->uart_base = UMPMEM_BASE_UART2;
+ edge_port->dma_address = UMPD_OEDB2_ADDRESS;
+ break;
+ default:
+ dev_err(&port->dev, "Unknown port number!!!\n");
+ return -ENODEV;
}
- dbg ("%s - port_number = %d, uart_base = %04x, dma_address = %04x",
- __func__, port_number, edge_port->uart_base, edge_port->dma_address);
+ dbg("%s - port_number = %d, uart_base = %04x, dma_address = %04x",
+ __func__, port_number, edge_port->uart_base,
+ edge_port->dma_address);
dev = port->serial->dev;
- memset (&(edge_port->icount), 0x00, sizeof(edge_port->icount));
- init_waitqueue_head (&edge_port->delta_msr_wait);
+ memset(&(edge_port->icount), 0x00, sizeof(edge_port->icount));
+ init_waitqueue_head(&edge_port->delta_msr_wait);
/* turn off loopback */
- status = TIClearLoopBack (edge_port);
+ status = ti_do_config(edge_port, UMPC_SET_CLR_LOOPBACK, 0);
if (status) {
- dev_err(&port->dev,"%s - cannot send clear loopback command, %d\n",
+ dev_err(&port->dev,
+ "%s - cannot send clear loopback command, %d\n",
__func__, status);
return status;
}
-
+
/* set up the port settings */
- edge_set_termios (port, port->tty->termios);
+ edge_set_termios(port, port->tty->termios);
/* open up the port */
/* milliseconds to timeout for DMA transfer */
transaction_timeout = 2;
- edge_port->ump_read_timeout = max (20, ((transaction_timeout * 3) / 2) );
+ edge_port->ump_read_timeout =
+ max(20, ((transaction_timeout * 3) / 2));
- // milliseconds to timeout for DMA transfer
- open_settings = (u8)(UMP_DMA_MODE_CONTINOUS |
- UMP_PIPE_TRANS_TIMEOUT_ENA |
+ /* milliseconds to timeout for DMA transfer */
+ open_settings = (u8)(UMP_DMA_MODE_CONTINOUS |
+ UMP_PIPE_TRANS_TIMEOUT_ENA |
(transaction_timeout << 2));
- dbg ("%s - Sending UMPC_OPEN_PORT", __func__);
+ dbg("%s - Sending UMPC_OPEN_PORT", __func__);
/* Tell TI to open and start the port */
- status = TIWriteCommandSync (dev,
- UMPC_OPEN_PORT,
- (u8)(UMPM_UART1_PORT + port_number),
- open_settings,
- NULL,
- 0);
+ status = send_cmd(dev, UMPC_OPEN_PORT,
+ (u8)(UMPM_UART1_PORT + port_number), open_settings, NULL, 0);
if (status) {
- dev_err(&port->dev,"%s - cannot send open command, %d\n", __func__, status);
+ dev_err(&port->dev, "%s - cannot send open command, %d\n",
+ __func__, status);
return status;
}
/* Start the DMA? */
- status = TIWriteCommandSync (dev,
- UMPC_START_PORT,
- (u8)(UMPM_UART1_PORT + port_number),
- 0,
- NULL,
- 0);
+ status = send_cmd(dev, UMPC_START_PORT,
+ (u8)(UMPM_UART1_PORT + port_number), 0, NULL, 0);
if (status) {
- dev_err(&port->dev,"%s - cannot send start DMA command, %d\n", __func__, status);
+ dev_err(&port->dev, "%s - cannot send start DMA command, %d\n",
+ __func__, status);
return status;
}
/* Clear TX and RX buffers in UMP */
- status = TIPurgeDataSync (port, UMP_PORT_DIR_OUT | UMP_PORT_DIR_IN);
+ status = purge_port(port, UMP_PORT_DIR_OUT | UMP_PORT_DIR_IN);
if (status) {
- dev_err(&port->dev,"%s - cannot send clear buffers command, %d\n", __func__, status);
+ dev_err(&port->dev,
+ "%s - cannot send clear buffers command, %d\n",
+ __func__, status);
return status;
}
/* Read Initial MSR */
- status = TIReadVendorRequestSync (dev,
- UMPC_READ_MSR, // Request
- 0, // wValue
- (__u16)(UMPM_UART1_PORT + port_number), // wIndex (Address)
- &edge_port->shadow_msr, // TransferBuffer
- 1); // TransferBufferLength
+ status = ti_vread_sync(dev, UMPC_READ_MSR, 0,
+ (__u16)(UMPM_UART1_PORT + port_number),
+ &edge_port->shadow_msr, 1);
if (status) {
- dev_err(&port->dev,"%s - cannot send read MSR command, %d\n", __func__, status);
+ dev_err(&port->dev, "%s - cannot send read MSR command, %d\n",
+ __func__, status);
return status;
}
- dbg ("ShadowMSR 0x%X", edge_port->shadow_msr);
-
+ dbg("ShadowMSR 0x%X", edge_port->shadow_msr);
+
/* Set Initial MCR */
edge_port->shadow_mcr = MCR_RTS | MCR_DTR;
- dbg ("ShadowMCR 0x%X", edge_port->shadow_mcr);
+ dbg("ShadowMCR 0x%X", edge_port->shadow_mcr);
edge_serial = edge_port->edge_serial;
if (mutex_lock_interruptible(&edge_serial->es_lock))
return -ERESTARTSYS;
if (edge_serial->num_ports_open == 0) {
- /* we are the first port to be opened, let's post the interrupt urb */
+ /* we are the first port to open, post the interrupt urb */
urb = edge_serial->serial->port[0]->interrupt_in_urb;
if (!urb) {
- dev_err (&port->dev, "%s - no interrupt urb present, exiting\n", __func__);
+ dev_err(&port->dev,
+ "%s - no interrupt urb present, exiting\n",
+ __func__);
status = -EINVAL;
goto release_es_lock;
}
urb->complete = edge_interrupt_callback;
urb->context = edge_serial;
urb->dev = dev;
- status = usb_submit_urb (urb, GFP_KERNEL);
+ status = usb_submit_urb(urb, GFP_KERNEL);
if (status) {
- dev_err (&port->dev, "%s - usb_submit_urb failed with value %d\n", __func__, status);
+ dev_err(&port->dev,
+ "%s - usb_submit_urb failed with value %d\n",
+ __func__, status);
goto release_es_lock;
}
}
@@ -2065,13 +1941,14 @@ static int edge_open (struct usb_serial_port *port, struct file * filp)
* reset the data toggle on the bulk endpoints to work around bug in
* host controllers where things get out of sync some times
*/
- usb_clear_halt (dev, port->write_urb->pipe);
- usb_clear_halt (dev, port->read_urb->pipe);
+ usb_clear_halt(dev, port->write_urb->pipe);
+ usb_clear_halt(dev, port->read_urb->pipe);
/* start up our bulk read urb */
urb = port->read_urb;
if (!urb) {
- dev_err (&port->dev, "%s - no read urb present, exiting\n", __func__);
+ dev_err(&port->dev, "%s - no read urb present, exiting\n",
+ __func__);
status = -EINVAL;
goto unlink_int_urb;
}
@@ -2079,9 +1956,11 @@ static int edge_open (struct usb_serial_port *port, struct file * filp)
urb->complete = edge_bulk_in_callback;
urb->context = edge_port;
urb->dev = dev;
- status = usb_submit_urb (urb, GFP_KERNEL);
+ status = usb_submit_urb(urb, GFP_KERNEL);
if (status) {
- dev_err (&port->dev, "%s - read bulk usb_submit_urb failed with value %d\n", __func__, status);
+ dev_err(&port->dev,
+ "%s - read bulk usb_submit_urb failed with value %d\n",
+ __func__, status);
goto unlink_int_urb;
}
@@ -2099,7 +1978,7 @@ release_es_lock:
return status;
}
-static void edge_close (struct usb_serial_port *port, struct file *filp)
+static void edge_close(struct usb_serial_port *port, struct file *filp)
{
struct edgeport_serial *edge_serial;
struct edgeport_port *edge_port;
@@ -2107,18 +1986,18 @@ static void edge_close (struct usb_serial_port *port, struct file *filp)
int status;
dbg("%s - port %d", __func__, port->number);
-
+
edge_serial = usb_get_serial_data(port->serial);
edge_port = usb_get_serial_port_data(port);
- if ((edge_serial == NULL) || (edge_port == NULL))
+ if (edge_serial == NULL || edge_port == NULL)
return;
-
- /* The bulkreadcompletion routine will check
+
+ /* The bulkreadcompletion routine will check
* this flag and dump add read data */
edge_port->close_pending = 1;
/* chase the port close and flush */
- TIChasePort (edge_port, (HZ*closing_wait)/100, 1);
+ chase_port(edge_port, (HZ * closing_wait) / 100, 1);
usb_kill_urb(port->read_urb);
usb_kill_urb(port->write_urb);
@@ -2128,7 +2007,7 @@ static void edge_close (struct usb_serial_port *port, struct file *filp)
* send a close port command to it */
dbg("%s - send umpc_close_port", __func__);
port_number = port->number - port->serial->minor;
- status = TIWriteCommandSync (port->serial->dev,
+ status = send_cmd(port->serial->dev,
UMPC_CLOSE_PORT,
(__u8)(UMPM_UART1_PORT + port_number),
0,
@@ -2147,7 +2026,8 @@ static void edge_close (struct usb_serial_port *port, struct file *filp)
dbg("%s - exited", __func__);
}
-static int edge_write (struct usb_serial_port *port, const unsigned char *data, int count)
+static int edge_write(struct usb_serial_port *port, const unsigned char *data,
+ int count)
{
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
unsigned long flags;
@@ -2203,11 +2083,12 @@ static void edge_send(struct usb_serial_port *port)
spin_unlock_irqrestore(&edge_port->ep_lock, flags);
- usb_serial_debug_data(debug, &port->dev, __func__, count, port->write_urb->transfer_buffer);
+ usb_serial_debug_data(debug, &port->dev, __func__, count,
+ port->write_urb->transfer_buffer);
/* set up our urb */
- usb_fill_bulk_urb (port->write_urb, port->serial->dev,
- usb_sndbulkpipe (port->serial->dev,
+ usb_fill_bulk_urb(port->write_urb, port->serial->dev,
+ usb_sndbulkpipe(port->serial->dev,
port->bulk_out_endpointAddress),
port->write_urb->transfer_buffer, count,
edge_bulk_out_callback,
@@ -2216,22 +2097,21 @@ static void edge_send(struct usb_serial_port *port)
/* send the data out the bulk port */
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
if (result) {
- dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __func__, result);
+ dev_err(&port->dev,
+ "%s - failed submitting write urb, error %d\n",
+ __func__, result);
edge_port->ep_write_urb_in_use = 0;
- // TODO: reschedule edge_send
- } else {
+ /* TODO: reschedule edge_send */
+ } else
edge_port->icount.tx += count;
- }
/* wakeup any process waiting for writes to complete */
/* there is now more room in the buffer for new writes */
- if (tty) {
- /* let the tty driver wakeup if it has a special write_wakeup function */
+ if (tty)
tty_wakeup(tty);
- }
}
-static int edge_write_room (struct usb_serial_port *port)
+static int edge_write_room(struct usb_serial_port *port)
{
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
int room = 0;
@@ -2240,9 +2120,9 @@ static int edge_write_room (struct usb_serial_port *port)
dbg("%s - port %d", __func__, port->number);
if (edge_port == NULL)
- return -ENODEV;
+ return 0;
if (edge_port->close_pending == 1)
- return -ENODEV;
+ return 0;
spin_lock_irqsave(&edge_port->ep_lock, flags);
room = edge_buf_space_avail(edge_port->ep_out_buf);
@@ -2252,7 +2132,7 @@ static int edge_write_room (struct usb_serial_port *port)
return room;
}
-static int edge_chars_in_buffer (struct usb_serial_port *port)
+static int edge_chars_in_buffer(struct usb_serial_port *port)
{
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
int chars = 0;
@@ -2261,22 +2141,22 @@ static int edge_chars_in_buffer (struct usb_serial_port *port)
dbg("%s - port %d", __func__, port->number);
if (edge_port == NULL)
- return -ENODEV;
+ return 0;
if (edge_port->close_pending == 1)
- return -ENODEV;
+ return 0;
spin_lock_irqsave(&edge_port->ep_lock, flags);
chars = edge_buf_data_avail(edge_port->ep_out_buf);
spin_unlock_irqrestore(&edge_port->ep_lock, flags);
- dbg ("%s - returns %d", __func__, chars);
+ dbg("%s - returns %d", __func__, chars);
return chars;
}
-static void edge_throttle (struct usb_serial_port *port)
+static void edge_throttle(struct usb_serial_port *port)
{
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
- struct tty_struct *tty;
+ struct tty_struct *tty = port->tty;
int status;
dbg("%s - port %d", __func__, port->number);
@@ -2284,19 +2164,14 @@ static void edge_throttle (struct usb_serial_port *port)
if (edge_port == NULL)
return;
- tty = port->tty;
- if (!tty) {
- dbg ("%s - no tty available", __func__);
- return;
- }
-
/* if we are implementing XON/XOFF, send the stop character */
if (I_IXOFF(tty)) {
unsigned char stop_char = STOP_CHAR(tty);
- status = edge_write (port, &stop_char, 1);
- if (status <= 0) {
- dev_err(&port->dev, "%s - failed to write stop character, %d\n", __func__, status);
- }
+ status = edge_write(port, &stop_char, 1);
+ if (status <= 0)
+ dev_err(&port->dev,
+ "%s - failed to write stop character, %d\n",
+ __func__, status);
}
/* if we are implementing RTS/CTS, stop reads */
@@ -2306,10 +2181,10 @@ static void edge_throttle (struct usb_serial_port *port)
}
-static void edge_unthrottle (struct usb_serial_port *port)
+static void edge_unthrottle(struct usb_serial_port *port)
{
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
- struct tty_struct *tty;
+ struct tty_struct *tty = port->tty;
int status;
dbg("%s - port %d", __func__, port->number);
@@ -2317,27 +2192,23 @@ static void edge_unthrottle (struct usb_serial_port *port)
if (edge_port == NULL)
return;
- tty = port->tty;
- if (!tty) {
- dbg ("%s - no tty available", __func__);
- return;
- }
-
/* if we are implementing XON/XOFF, send the start character */
if (I_IXOFF(tty)) {
unsigned char start_char = START_CHAR(tty);
- status = edge_write (port, &start_char, 1);
- if (status <= 0) {
- dev_err(&port->dev, "%s - failed to write start character, %d\n", __func__, status);
- }
+ status = edge_write(port, &start_char, 1);
+ if (status <= 0)
+ dev_err(&port->dev,
+ "%s - failed to write start character, %d\n",
+ __func__, status);
}
-
/* if we are implementing RTS/CTS, restart reads */
/* are the Edgeport will assert the RTS line */
if (C_CRTSCTS(tty)) {
status = restart_read(edge_port);
if (status)
- dev_err(&port->dev, "%s - read bulk usb_submit_urb failed with value %d\n", __func__, status);
+ dev_err(&port->dev,
+ "%s - read bulk usb_submit_urb failed: %d\n",
+ __func__, status);
}
}
@@ -2378,22 +2249,26 @@ static int restart_read(struct edgeport_port *edge_port)
return status;
}
-static void change_port_settings (struct edgeport_port *edge_port, struct ktermios *old_termios)
+static void change_port_settings(struct edgeport_port *edge_port,
+ struct ktermios *old_termios)
{
struct ump_uart_config *config;
struct tty_struct *tty;
int baud;
unsigned cflag;
int status;
- int port_number = edge_port->port->number - edge_port->port->serial->minor;
+ int port_number = edge_port->port->number -
+ edge_port->port->serial->minor;
dbg("%s - port %d", __func__, edge_port->port->number);
tty = edge_port->port->tty;
- config = kmalloc (sizeof (*config), GFP_KERNEL);
+ config = kmalloc(sizeof(*config), GFP_KERNEL);
if (!config) {
- dev_err (&edge_port->port->dev, "%s - out of memory\n", __func__);
+ *tty->termios = *old_termios;
+ dev_err(&edge_port->port->dev, "%s - out of memory\n",
+ __func__);
return;
}
@@ -2407,22 +2282,22 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi
config->bUartMode = (__u8)(edge_port->bUartMode);
switch (cflag & CSIZE) {
- case CS5:
- config->bDataBits = UMP_UART_CHAR5BITS;
- dbg ("%s - data bits = 5", __func__);
- break;
- case CS6:
- config->bDataBits = UMP_UART_CHAR6BITS;
- dbg ("%s - data bits = 6", __func__);
- break;
- case CS7:
- config->bDataBits = UMP_UART_CHAR7BITS;
- dbg ("%s - data bits = 7", __func__);
- break;
- default:
- case CS8:
- config->bDataBits = UMP_UART_CHAR8BITS;
- dbg ("%s - data bits = 8", __func__);
+ case CS5:
+ config->bDataBits = UMP_UART_CHAR5BITS;
+ dbg("%s - data bits = 5", __func__);
+ break;
+ case CS6:
+ config->bDataBits = UMP_UART_CHAR6BITS;
+ dbg("%s - data bits = 6", __func__);
+ break;
+ case CS7:
+ config->bDataBits = UMP_UART_CHAR7BITS;
+ dbg("%s - data bits = 7", __func__);
+ break;
+ default:
+ case CS8:
+ config->bDataBits = UMP_UART_CHAR8BITS;
+ dbg("%s - data bits = 8", __func__);
break;
}
@@ -2437,7 +2312,7 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi
dbg("%s - parity = even", __func__);
}
} else {
- config->bParity = UMP_UART_NOPARITY;
+ config->bParity = UMP_UART_NOPARITY;
dbg("%s - parity = none", __func__);
}
@@ -2460,29 +2335,26 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi
restart_read(edge_port);
}
- /* if we are implementing XON/XOFF, set the start and stop character in the device */
- if (I_IXOFF(tty) || I_IXON(tty)) {
- config->cXon = START_CHAR(tty);
- config->cXoff = STOP_CHAR(tty);
+ /* if we are implementing XON/XOFF, set the start and stop
+ character in the device */
+ config->cXon = START_CHAR(tty);
+ config->cXoff = STOP_CHAR(tty);
- /* if we are implementing INBOUND XON/XOFF */
- if (I_IXOFF(tty)) {
- config->wFlags |= UMP_MASK_UART_FLAGS_IN_X;
- dbg ("%s - INBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x",
- __func__, config->cXon, config->cXoff);
- } else {
- dbg ("%s - INBOUND XON/XOFF is disabled", __func__);
- }
+ /* if we are implementing INBOUND XON/XOFF */
+ if (I_IXOFF(tty)) {
+ config->wFlags |= UMP_MASK_UART_FLAGS_IN_X;
+ dbg("%s - INBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x",
+ __func__, config->cXon, config->cXoff);
+ } else
+ dbg("%s - INBOUND XON/XOFF is disabled", __func__);
- /* if we are implementing OUTBOUND XON/XOFF */
- if (I_IXON(tty)) {
- config->wFlags |= UMP_MASK_UART_FLAGS_OUT_X;
- dbg ("%s - OUTBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x",
- __func__, config->cXon, config->cXoff);
- } else {
- dbg ("%s - OUTBOUND XON/XOFF is disabled", __func__);
- }
- }
+ /* if we are implementing OUTBOUND XON/XOFF */
+ if (I_IXON(tty)) {
+ config->wFlags |= UMP_MASK_UART_FLAGS_OUT_X;
+ dbg("%s - OUTBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x",
+ __func__, config->cXon, config->cXoff);
+ } else
+ dbg("%s - OUTBOUND XON/XOFF is disabled", __func__);
tty->termios->c_cflag &= ~CMSPAR;
@@ -2499,62 +2371,52 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi
/* FIXME: Recompute actual baud from divisor here */
- dbg ("%s - baud rate = %d, wBaudRate = %d", __func__, baud, config->wBaudRate);
+ dbg("%s - baud rate = %d, wBaudRate = %d", __func__, baud,
+ config->wBaudRate);
- dbg ("wBaudRate: %d", (int)(461550L / config->wBaudRate));
- dbg ("wFlags: 0x%x", config->wFlags);
- dbg ("bDataBits: %d", config->bDataBits);
- dbg ("bParity: %d", config->bParity);
- dbg ("bStopBits: %d", config->bStopBits);
- dbg ("cXon: %d", config->cXon);
- dbg ("cXoff: %d", config->cXoff);
- dbg ("bUartMode: %d", config->bUartMode);
+ dbg("wBaudRate: %d", (int)(461550L / config->wBaudRate));
+ dbg("wFlags: 0x%x", config->wFlags);
+ dbg("bDataBits: %d", config->bDataBits);
+ dbg("bParity: %d", config->bParity);
+ dbg("bStopBits: %d", config->bStopBits);
+ dbg("cXon: %d", config->cXon);
+ dbg("cXoff: %d", config->cXoff);
+ dbg("bUartMode: %d", config->bUartMode);
/* move the word values into big endian mode */
- cpu_to_be16s (&config->wFlags);
- cpu_to_be16s (&config->wBaudRate);
+ cpu_to_be16s(&config->wFlags);
+ cpu_to_be16s(&config->wBaudRate);
- status = TIWriteCommandSync (edge_port->port->serial->dev,
- UMPC_SET_CONFIG,
+ status = send_cmd(edge_port->port->serial->dev, UMPC_SET_CONFIG,
(__u8)(UMPM_UART1_PORT + port_number),
- 0,
- (__u8 *)config,
- sizeof(*config));
- if (status) {
- dbg ("%s - error %d when trying to write config to device",
+ 0, (__u8 *)config, sizeof(*config));
+ if (status)
+ dbg("%s - error %d when trying to write config to device",
__func__, status);
- }
-
- kfree (config);
-
+ kfree(config);
return;
}
-static void edge_set_termios (struct usb_serial_port *port, struct ktermios *old_termios)
+static void edge_set_termios(struct usb_serial_port *port,
+ struct ktermios *old_termios)
{
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
struct tty_struct *tty = port->tty;
- unsigned int cflag;
-
- cflag = tty->termios->c_cflag;
dbg("%s - clfag %08x iflag %08x", __func__,
tty->termios->c_cflag, tty->termios->c_iflag);
dbg("%s - old clfag %08x old iflag %08x", __func__,
old_termios->c_cflag, old_termios->c_iflag);
-
dbg("%s - port %d", __func__, port->number);
if (edge_port == NULL)
return;
-
/* change the port settings to the new ones specified */
- change_port_settings (edge_port, old_termios);
-
- return;
+ change_port_settings(edge_port, old_termios);
}
-static int edge_tiocmset (struct usb_serial_port *port, struct file *file, unsigned int set, unsigned int clear)
+static int edge_tiocmset(struct usb_serial_port *port, struct file *file,
+ unsigned int set, unsigned int clear)
{
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
unsigned int mcr;
@@ -2581,8 +2443,7 @@ static int edge_tiocmset (struct usb_serial_port *port, struct file *file, unsig
edge_port->shadow_mcr = mcr;
spin_unlock_irqrestore(&edge_port->ep_lock, flags);
- TIRestoreMCR (edge_port, mcr);
-
+ restore_mcr(edge_port, mcr);
return 0;
}
@@ -2614,7 +2475,8 @@ static int edge_tiocmget(struct usb_serial_port *port, struct file *file)
return result;
}
-static int get_serial_info (struct edgeport_port *edge_port, struct serial_struct __user *retinfo)
+static int get_serial_info(struct edgeport_port *edge_port,
+ struct serial_struct __user *retinfo)
{
struct serial_struct tmp;
@@ -2632,17 +2494,14 @@ static int get_serial_info (struct edgeport_port *edge_port, struct serial_struc
tmp.baud_base = 9600;
tmp.close_delay = 5*HZ;
tmp.closing_wait = closing_wait;
-// tmp.custom_divisor = state->custom_divisor;
-// tmp.hub6 = state->hub6;
-// tmp.io_type = state->io_type;
-
if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
return -EFAULT;
return 0;
}
-static int edge_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg)
+static int edge_ioctl(struct usb_serial_port *port, struct file *file,
+ unsigned int cmd, unsigned long arg)
{
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
struct async_icount cnow;
@@ -2651,81 +2510,63 @@ static int edge_ioctl (struct usb_serial_port *port, struct file *file, unsigned
dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd);
switch (cmd) {
- case TIOCINQ:
- dbg("%s - (%d) TIOCINQ", __func__, port->number);
-// return get_number_bytes_avail(edge_port, (unsigned int *) arg);
- break;
-
- case TIOCSERGETLSR:
- dbg("%s - (%d) TIOCSERGETLSR", __func__, port->number);
-// return get_lsr_info(edge_port, (unsigned int *) arg);
- break;
-
- case TIOCGSERIAL:
- dbg("%s - (%d) TIOCGSERIAL", __func__, port->number);
- return get_serial_info(edge_port, (struct serial_struct __user *) arg);
- break;
-
- case TIOCSSERIAL:
- dbg("%s - (%d) TIOCSSERIAL", __func__, port->number);
- break;
-
- case TIOCMIWAIT:
- dbg("%s - (%d) TIOCMIWAIT", __func__, port->number);
- cprev = edge_port->icount;
- while (1) {
- interruptible_sleep_on(&edge_port->delta_msr_wait);
- /* see if a signal did it */
- if (signal_pending(current))
- return -ERESTARTSYS;
- cnow = edge_port->icount;
- if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
- cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
- return -EIO; /* no change => error */
- if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
- ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
- ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
- ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
- return 0;
- }
- cprev = cnow;
+ case TIOCGSERIAL:
+ dbg("%s - (%d) TIOCGSERIAL", __func__, port->number);
+ return get_serial_info(edge_port,
+ (struct serial_struct __user *) arg);
+ case TIOCMIWAIT:
+ dbg("%s - (%d) TIOCMIWAIT", __func__, port->number);
+ cprev = edge_port->icount;
+ while (1) {
+ interruptible_sleep_on(&edge_port->delta_msr_wait);
+ /* see if a signal did it */
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ cnow = edge_port->icount;
+ if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+ cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+ return -EIO; /* no change => error */
+ if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+ ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+ ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
+ ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) {
+ return 0;
}
- /* not reached */
- break;
-
- case TIOCGICOUNT:
- dbg ("%s - (%d) TIOCGICOUNT RX=%d, TX=%d", __func__,
- port->number, edge_port->icount.rx, edge_port->icount.tx);
- if (copy_to_user((void __user *)arg, &edge_port->icount, sizeof(edge_port->icount)))
- return -EFAULT;
- return 0;
+ cprev = cnow;
+ }
+ /* not reached */
+ break;
+ case TIOCGICOUNT:
+ dbg("%s - (%d) TIOCGICOUNT RX=%d, TX=%d", __func__,
+ port->number, edge_port->icount.rx, edge_port->icount.tx);
+ if (copy_to_user((void __user *)arg, &edge_port->icount,
+ sizeof(edge_port->icount)))
+ return -EFAULT;
+ return 0;
}
-
return -ENOIOCTLCMD;
}
-static void edge_break (struct usb_serial_port *port, int break_state)
+static void edge_break(struct usb_serial_port *port, int on)
{
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
int status;
+ int bv = 0; /* Off */
- dbg ("%s - state = %d", __func__, break_state);
+ dbg("%s - state = %d", __func__, on);
/* chase the port close */
- TIChasePort (edge_port, 0, 0);
+ chase_port(edge_port, 0, 0);
- if (break_state == -1) {
- status = TISetBreak (edge_port);
- } else {
- status = TIClearBreak (edge_port);
- }
- if (status) {
- dbg ("%s - error %d sending break set/clear command.",
+ if (on == -1)
+ bv = 1; /* On */
+ status = ti_do_config(edge_port, UMPC_SET_CLR_BREAK, bv);
+ if (status)
+ dbg("%s - error %d sending break set/clear command.",
__func__, status);
- }
}
-static int edge_startup (struct usb_serial *serial)
+static int edge_startup(struct usb_serial *serial)
{
struct edgeport_serial *edge_serial;
struct edgeport_port *edge_port;
@@ -2745,9 +2586,9 @@ static int edge_startup (struct usb_serial *serial)
edge_serial->serial = serial;
usb_set_serial_data(serial, edge_serial);
- status = TIDownloadFirmware (edge_serial);
+ status = download_fw(edge_serial);
if (status) {
- kfree (edge_serial);
+ kfree(edge_serial);
return status;
}
@@ -2755,13 +2596,15 @@ static int edge_startup (struct usb_serial *serial)
for (i = 0; i < serial->num_ports; ++i) {
edge_port = kzalloc(sizeof(struct edgeport_port), GFP_KERNEL);
if (edge_port == NULL) {
- dev_err(&serial->dev->dev, "%s - Out of memory\n", __func__);
+ dev_err(&serial->dev->dev, "%s - Out of memory\n",
+ __func__);
goto cleanup;
}
spin_lock_init(&edge_port->ep_lock);
edge_port->ep_out_buf = edge_buf_alloc(EDGE_OUT_BUF_SIZE);
if (edge_port->ep_out_buf == NULL) {
- dev_err(&serial->dev->dev, "%s - Out of memory\n", __func__);
+ dev_err(&serial->dev->dev, "%s - Out of memory\n",
+ __func__);
kfree(edge_port);
goto cleanup;
}
@@ -2770,27 +2613,27 @@ static int edge_startup (struct usb_serial *serial)
usb_set_serial_port_data(serial->port[i], edge_port);
edge_port->bUartMode = default_uart_mode;
}
-
+
return 0;
cleanup:
- for (--i; i>=0; --i) {
+ for (--i; i >= 0; --i) {
edge_port = usb_get_serial_port_data(serial->port[i]);
edge_buf_free(edge_port->ep_out_buf);
kfree(edge_port);
usb_set_serial_port_data(serial->port[i], NULL);
}
- kfree (edge_serial);
+ kfree(edge_serial);
usb_set_serial_data(serial, NULL);
return -ENOMEM;
}
-static void edge_shutdown (struct usb_serial *serial)
+static void edge_shutdown(struct usb_serial *serial)
{
int i;
struct edgeport_port *edge_port;
- dbg ("%s", __func__);
+ dbg("%s", __func__);
for (i = 0; i < serial->num_ports; ++i) {
edge_port = usb_get_serial_port_data(serial->port[i]);
@@ -2832,7 +2675,8 @@ static ssize_t store_uart_mode(struct device *dev,
return count;
}
-static DEVICE_ATTR(uart_mode, S_IWUSR | S_IRUGO, show_uart_mode, store_uart_mode);
+static DEVICE_ATTR(uart_mode, S_IWUSR | S_IRUGO, show_uart_mode,
+ store_uart_mode);
static int edge_create_sysfs_attrs(struct usb_serial_port *port)
{
@@ -2902,9 +2746,9 @@ static void edge_buf_free(struct edge_buf *eb)
static void edge_buf_clear(struct edge_buf *eb)
{
- if (eb != NULL)
- eb->buf_get = eb->buf_put;
- /* equivalent to a get of all data available */
+ if (eb != NULL)
+ eb->buf_get = eb->buf_put;
+ /* equivalent to a get of all data available */
}
@@ -2917,10 +2761,9 @@ static void edge_buf_clear(struct edge_buf *eb)
static unsigned int edge_buf_data_avail(struct edge_buf *eb)
{
- if (eb != NULL)
- return ((eb->buf_size + eb->buf_put - eb->buf_get) % eb->buf_size);
- else
+ if (eb == NULL)
return 0;
+ return ((eb->buf_size + eb->buf_put - eb->buf_get) % eb->buf_size);
}
@@ -2933,10 +2776,9 @@ static unsigned int edge_buf_data_avail(struct edge_buf *eb)
static unsigned int edge_buf_space_avail(struct edge_buf *eb)
{
- if (eb != NULL)
- return ((eb->buf_size + eb->buf_get - eb->buf_put - 1) % eb->buf_size);
- else
+ if (eb == NULL)
return 0;
+ return ((eb->buf_size + eb->buf_get - eb->buf_put - 1) % eb->buf_size);
}
@@ -3093,7 +2935,7 @@ static int __init edgeport_init(void)
if (retval)
goto failed_2port_device_register;
retval = usb_register(&io_driver);
- if (retval)
+ if (retval)
goto failed_usb_register;
info(DRIVER_DESC " " DRIVER_VERSION);
return 0;
@@ -3105,11 +2947,11 @@ failed_1port_device_register:
return retval;
}
-static void __exit edgeport_exit (void)
+static void __exit edgeport_exit(void)
{
- usb_deregister (&io_driver);
- usb_serial_deregister (&edgeport_1port_device);
- usb_serial_deregister (&edgeport_2port_device);
+ usb_deregister(&io_driver);
+ usb_serial_deregister(&edgeport_1port_device);
+ usb_serial_deregister(&edgeport_2port_device);
}
module_init(edgeport_init);
@@ -3130,8 +2972,8 @@ module_param(closing_wait, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(closing_wait, "Maximum wait for data to drain, in .01 secs");
module_param(ignore_cpu_rev, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(ignore_cpu_rev, "Ignore the cpu revision when connecting to a device");
+MODULE_PARM_DESC(ignore_cpu_rev,
+ "Ignore the cpu revision when connecting to a device");
module_param(default_uart_mode, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(default_uart_mode, "Default uart_mode, 0=RS232, ...");
-
diff --git a/drivers/usb/serial/keyspan_pda.S b/drivers/usb/serial/keyspan_pda.S
index 418fe69aa5e0..006154b82eeb 100644
--- a/drivers/usb/serial/keyspan_pda.S
+++ b/drivers/usb/serial/keyspan_pda.S
@@ -1,4 +1,4 @@
-/* $Id: loop.s,v 1.23 2000/03/20 09:49:06 warner Exp $
+/*
*
* Firmware for the Keyspan PDA Serial Adapter, a USB serial port based on
* the EzUSB microcontroller.
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
index f328948d74e3..79787eda9524 100644
--- a/drivers/usb/serial/kl5kusb105.c
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -236,7 +236,7 @@ static int klsi_105_get_line_state(struct usb_serial_port *port,
if (rc < 0)
err("Reading line status failed (error = %d)", rc);
else {
- status = le16_to_cpu(get_unaligned((__le16 *)status_buf));
+ status = get_unaligned_le16(status_buf);
info("%s - read status %x %x", __func__,
status_buf[0], status_buf[1]);
diff --git a/drivers/usb/serial/ti_fw_3410.h b/drivers/usb/serial/ti_fw_3410.h
index 71e88579dfe0..712e4df5d599 100644
--- a/drivers/usb/serial/ti_fw_3410.h
+++ b/drivers/usb/serial/ti_fw_3410.h
@@ -882,4 +882,8 @@ static unsigned char ti_fw_3410[] = {
0x00,0x00,
};
+static struct firmware fw_3410 = {
+ sizeof(ti_fw_3410),
+ ti_fw_3410
+};
#endif /* ifndef _TI_FW_3410_H_ */
diff --git a/drivers/usb/serial/ti_fw_5052.h b/drivers/usb/serial/ti_fw_5052.h
index 6ccf40a09798..2194121b69e4 100644
--- a/drivers/usb/serial/ti_fw_5052.h
+++ b/drivers/usb/serial/ti_fw_5052.h
@@ -882,4 +882,9 @@ static unsigned char ti_fw_5052[] = {
0x00,
};
+static struct firmware fw_5052 = {
+ sizeof(ti_fw_5052),
+ ti_fw_5052
+};
+
#endif /* ifndef _TI_FW_5052_H_ */
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index a1c8aef01417..a63e21577008 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -70,6 +70,7 @@
#include <linux/kernel.h>
#include <linux/errno.h>
+#include <linux/firmware.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/tty.h>
@@ -86,9 +87,14 @@
#include <linux/usb/serial.h>
#include "ti_usb_3410_5052.h"
+
+#ifdef CONFIG_USB_SERIAL_TI_FIRMWARE
+/* Keep compatibility option for a while so we don't break systems
+ as we kick the firmware out into user space. In time this will
+ go away */
#include "ti_fw_3410.h" /* firmware image for 3410 */
#include "ti_fw_5052.h" /* firmware image for 5052 */
-
+#endif
/* Defines */
@@ -194,8 +200,7 @@ static int ti_command_in_sync(struct ti_device *tdev, __u8 command,
static int ti_write_byte(struct ti_device *tdev, unsigned long addr,
__u8 mask, __u8 byte);
-static int ti_download_firmware(struct ti_device *tdev,
- unsigned char *firmware, unsigned int firmware_size);
+static int ti_download_firmware(struct ti_device *tdev, int type);
/* circular buffer */
static struct circ_buf *ti_buf_alloc(void);
@@ -429,13 +434,10 @@ static int ti_startup(struct usb_serial *serial)
/* if we have only 1 configuration, download firmware */
if (dev->descriptor.bNumConfigurations == 1) {
-
if (tdev->td_is_3410)
- status = ti_download_firmware(tdev, ti_fw_3410,
- sizeof(ti_fw_3410));
+ status = ti_download_firmware(tdev, 3410);
else
- status = ti_download_firmware(tdev, ti_fw_5052,
- sizeof(ti_fw_5052));
+ status = ti_download_firmware(tdev, 5052);
if (status)
goto free_tdev;
@@ -1656,50 +1658,82 @@ static int ti_write_byte(struct ti_device *tdev, unsigned long addr,
return status;
}
-
-static int ti_download_firmware(struct ti_device *tdev,
- unsigned char *firmware, unsigned int firmware_size)
+static int ti_do_download(struct usb_device *dev, int pipe,
+ u8 *buffer, int size)
{
- int status = 0;
- int buffer_size;
int pos;
- int len;
+ u8 cs = 0;
int done;
- __u8 cs = 0;
- __u8 *buffer;
- struct usb_device *dev = tdev->td_serial->dev;
struct ti_firmware_header *header;
- unsigned int pipe = usb_sndbulkpipe(dev,
- tdev->td_serial->port[0]->bulk_out_endpointAddress);
-
-
- buffer_size = TI_FIRMWARE_BUF_SIZE + sizeof(struct ti_firmware_header);
- buffer = kmalloc(buffer_size, GFP_KERNEL);
- if (!buffer) {
- dev_err(&dev->dev, "%s - out of memory\n", __func__);
- return -ENOMEM;
- }
-
- memcpy(buffer, firmware, firmware_size);
- memset(buffer+firmware_size, 0xff, buffer_size-firmware_size);
+ int status;
+ int len;
- for(pos = sizeof(struct ti_firmware_header); pos < buffer_size; pos++)
+ for(pos = sizeof(struct ti_firmware_header); pos < size; pos++)
cs = (__u8)(cs + buffer[pos]);
header = (struct ti_firmware_header *)buffer;
- header->wLength = cpu_to_le16((__u16)(buffer_size - sizeof(struct ti_firmware_header)));
+ header->wLength = cpu_to_le16((__u16)(size
+ - sizeof(struct ti_firmware_header)));
header->bCheckSum = cs;
dbg("%s - downloading firmware", __func__);
- for (pos = 0; pos < buffer_size; pos += done) {
- len = min(buffer_size - pos, TI_DOWNLOAD_MAX_PACKET_SIZE);
- status = usb_bulk_msg(dev, pipe, buffer+pos, len, &done, 1000);
+ for (pos = 0; pos < size; pos += done) {
+ len = min(size - pos, TI_DOWNLOAD_MAX_PACKET_SIZE);
+ status = usb_bulk_msg(dev, pipe, buffer + pos, len,
+ &done, 1000);
if (status)
break;
}
+ return status;
+}
+
+
+static int ti_download_firmware(struct ti_device *tdev, int type)
+{
+ int status = -ENOMEM;
+ int buffer_size;
+ __u8 *buffer;
+ struct usb_device *dev = tdev->td_serial->dev;
+ unsigned int pipe = usb_sndbulkpipe(dev,
+ tdev->td_serial->port[0]->bulk_out_endpointAddress);
+ const struct firmware *fw_p;
- kfree(buffer);
+#ifdef CONFIG_USB_SERIAL_TI_FIRMWARE
+ switch (type) {
+ case 3410:
+ fw_p = &fw_3410;
+ break;
+ case 5052:
+ fw_p = &fw_5052;
+ break;
+ default:
+ return -EINVAL;
+ }
+#else
+ char buf[32];
+ sprintf(buf, "ti_usb-%d.bin", type);
+
+ if (request_firmware(&fw_p, buf, &dev->dev)) {
+ dev_err(&dev->dev, "%s - firmware not found\n", __func__);
+ return -ENOENT;
+ }
+ if (fw_p->size > TI_FIRMWARE_BUF_SIZE) {
+ dev_err(&dev->dev, "%s - firmware too large\n", __func__);
+ return -ENOENT;
+ }
+#endif
+ buffer_size = TI_FIRMWARE_BUF_SIZE + sizeof(struct ti_firmware_header);
+ buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (buffer) {
+ memcpy(buffer, fw_p->data, fw_p->size);
+ memset(buffer + fw_p->size, 0xff, buffer_size - fw_p->size);
+ ti_do_download(dev, pipe, buffer, fw_p->size);
+ kfree(buffer);
+ }
+#ifdef CONFIG_USB_SERIAL_TI_FIRMWARE
+ release_firmware(fw_p);
+#endif
if (status) {
dev_err(&dev->dev, "%s - error downloading firmware, %d\n", __func__, status);
return status;
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index 0cb0d77dc429..717e376795e4 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -505,7 +505,7 @@ static void port_release(struct device *dev)
{
struct usb_serial_port *port = to_usb_serial_port(dev);
- dbg ("%s - %s", __func__, dev->bus_id);
+ dbg ("%s - %s", __func__, dev_name(dev));
port_free(port);
}
@@ -938,8 +938,8 @@ int usb_serial_probe(struct usb_interface *interface,
port->dev.bus = &usb_serial_bus_type;
port->dev.release = &port_release;
- snprintf (&port->dev.bus_id[0], sizeof(port->dev.bus_id), "ttyUSB%d", port->number);
- dbg ("%s - registering %s", __func__, port->dev.bus_id);
+ dev_set_name(&port->dev, "ttyUSB%d", port->number);
+ dbg ("%s - registering %s", __func__, dev_name(&port->dev));
retval = device_register(&port->dev);
if (retval)
dev_err(&port->dev, "Error registering port device, "
diff --git a/drivers/usb/serial/xircom_pgs.S b/drivers/usb/serial/xircom_pgs.S
index 05d99dd63776..fa7ccaa6b396 100644
--- a/drivers/usb/serial/xircom_pgs.S
+++ b/drivers/usb/serial/xircom_pgs.S
@@ -1,4 +1,4 @@
-/* $Id: loop.s,v 1.23 2000/03/20 09:49:06 warner Exp $
+/*
*
* Firmware for the Keyspan PDA Serial Adapter, a USB serial port based on
* the EzUSB microcontroller.
diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c
index 579e9f52053a..17f1ae232919 100644
--- a/drivers/usb/storage/datafab.c
+++ b/drivers/usb/storage/datafab.c
@@ -1,7 +1,5 @@
/* Driver for Datafab USB Compact Flash reader
*
- * $Id: datafab.c,v 1.7 2002/02/25 00:40:13 mdharm Exp $
- *
* datafab driver v0.1:
*
* First release
diff --git a/drivers/usb/storage/debug.c b/drivers/usb/storage/debug.c
index 01e430654a13..a2b5526c9fa0 100644
--- a/drivers/usb/storage/debug.c
+++ b/drivers/usb/storage/debug.c
@@ -1,8 +1,6 @@
/* Driver for USB Mass Storage compliant devices
* Debugging Functions Source Code File
*
- * $Id: debug.c,v 1.9 2002/04/22 03:39:43 mdharm Exp $
- *
* Current development and maintenance by:
* (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
diff --git a/drivers/usb/storage/debug.h b/drivers/usb/storage/debug.h
index 77e244a8c376..dbb985d52423 100644
--- a/drivers/usb/storage/debug.h
+++ b/drivers/usb/storage/debug.h
@@ -1,8 +1,6 @@
/* Driver for USB Mass Storage compliant devices
* Debugging Functions Header File
*
- * $Id: debug.h,v 1.6 2001/01/12 23:51:04 mdharm Exp $
- *
* Current development and maintenance by:
* (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
diff --git a/drivers/usb/storage/dpcm.c b/drivers/usb/storage/dpcm.c
index 9a410b5a6e5b..939923471af4 100644
--- a/drivers/usb/storage/dpcm.c
+++ b/drivers/usb/storage/dpcm.c
@@ -1,7 +1,5 @@
/* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader
*
- * $Id: dpcm.c,v 1.4 2001/06/11 02:54:25 mdharm Exp $
- *
* DPCM driver v0.1:
*
* First release
diff --git a/drivers/usb/storage/dpcm.h b/drivers/usb/storage/dpcm.h
index 81b464cfcc1e..e7b7b0f120d7 100644
--- a/drivers/usb/storage/dpcm.h
+++ b/drivers/usb/storage/dpcm.h
@@ -1,7 +1,5 @@
/* Driver for Microtech DPCM-USB CompactFlash/SmartMedia reader
*
- * $Id: dpcm.h,v 1.2 2000/08/25 00:13:51 mdharm Exp $
- *
* DPCM driver v0.1:
*
* First release
diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c
index f5a4e8d6a3b1..7a4d45677227 100644
--- a/drivers/usb/storage/freecom.c
+++ b/drivers/usb/storage/freecom.c
@@ -1,7 +1,5 @@
/* Driver for Freecom USB/IDE adaptor
*
- * $Id: freecom.c,v 1.22 2002/04/22 03:39:43 mdharm Exp $
- *
* Freecom v0.1:
*
* First release
diff --git a/drivers/usb/storage/freecom.h b/drivers/usb/storage/freecom.h
index 1b012d62d0a8..20d0fe6ba0c8 100644
--- a/drivers/usb/storage/freecom.h
+++ b/drivers/usb/storage/freecom.h
@@ -1,7 +1,5 @@
/* Driver for Freecom USB/IDE adaptor
*
- * $Id: freecom.h,v 1.4 2000/08/29 14:49:15 dlbrown Exp $
- *
* Freecom v0.1:
*
* First release
diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c
index 187dd1e01093..4995bb595aef 100644
--- a/drivers/usb/storage/initializers.c
+++ b/drivers/usb/storage/initializers.c
@@ -1,7 +1,5 @@
/* Special Initializers for certain USB Mass Storage devices
*
- * $Id: initializers.c,v 1.2 2000/09/06 22:35:57 mdharm Exp $
- *
* Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
diff --git a/drivers/usb/storage/initializers.h b/drivers/usb/storage/initializers.h
index ad3ffd4236c2..529327fbb06b 100644
--- a/drivers/usb/storage/initializers.h
+++ b/drivers/usb/storage/initializers.h
@@ -1,7 +1,5 @@
/* Header file for Special Initializers for certain USB Mass Storage devices
*
- * $Id: initializers.h,v 1.1 2000/08/29 23:07:02 mdharm Exp $
- *
* Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c
index 3addcd8f827b..383abf2516a5 100644
--- a/drivers/usb/storage/isd200.c
+++ b/drivers/usb/storage/isd200.c
@@ -1,7 +1,5 @@
/* Transport & Protocol Driver for In-System Design, Inc. ISD200 ASIC
*
- * $Id: isd200.c,v 1.16 2002/04/22 03:39:43 mdharm Exp $
- *
* Current development and maintenance:
* (C) 2001-2002 Björn Stenberg (bjorn@haxx.se)
*
@@ -586,7 +584,7 @@ static void isd200_invoke_transport( struct us_data *us,
/* if the command gets aborted by the higher layers, we need to
* short-circuit all other processing
*/
- if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) {
+ if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
US_DEBUGP("-- command was aborted\n");
goto Handle_Abort;
}
@@ -633,7 +631,7 @@ static void isd200_invoke_transport( struct us_data *us,
if (need_auto_sense) {
result = isd200_read_regs(us);
- if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) {
+ if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
US_DEBUGP("-- auto-sense aborted\n");
goto Handle_Abort;
}
@@ -663,7 +661,7 @@ static void isd200_invoke_transport( struct us_data *us,
srb->result = DID_ABORT << 16;
/* permit the reset transfer to take place */
- clear_bit(US_FLIDX_ABORTING, &us->flags);
+ clear_bit(US_FLIDX_ABORTING, &us->dflags);
/* Need reset here */
}
diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c
index 61097cbb1585..df67f13c9e73 100644
--- a/drivers/usb/storage/jumpshot.c
+++ b/drivers/usb/storage/jumpshot.c
@@ -1,7 +1,5 @@
/* Driver for Lexar "Jumpshot" Compact Flash reader
*
- * $Id: jumpshot.c,v 1.7 2002/02/25 00:40:13 mdharm Exp $
- *
* jumpshot driver v0.1:
*
* First release
diff --git a/drivers/usb/storage/protocol.c b/drivers/usb/storage/protocol.c
index b9b8ede61fb3..3b3357e20ea7 100644
--- a/drivers/usb/storage/protocol.c
+++ b/drivers/usb/storage/protocol.c
@@ -1,7 +1,5 @@
/* Driver for USB Mass Storage compliant devices
*
- * $Id: protocol.c,v 1.14 2002/04/22 03:39:43 mdharm Exp $
- *
* Current development and maintenance by:
* (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
diff --git a/drivers/usb/storage/protocol.h b/drivers/usb/storage/protocol.h
index 8737a36891ca..487056ffb516 100644
--- a/drivers/usb/storage/protocol.h
+++ b/drivers/usb/storage/protocol.h
@@ -1,8 +1,6 @@
/* Driver for USB Mass Storage compliant devices
* Protocol Functions Header File
*
- * $Id: protocol.h,v 1.4 2001/02/13 07:10:03 mdharm Exp $
- *
* Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index 3fcde9f0fa5f..b4c9e0f18a82 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -1,8 +1,6 @@
/* Driver for USB Mass Storage compliant devices
* SCSI layer glue code
*
- * $Id: scsiglue.c,v 1.26 2002/04/22 03:39:43 mdharm Exp $
- *
* Current development and maintenance by:
* (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
@@ -116,10 +114,10 @@ static int slave_configure(struct scsi_device *sdev)
* while others have trouble with more than 64K. At this time we
* are limiting both to 32K (64 sectores).
*/
- if (us->flags & (US_FL_MAX_SECTORS_64 | US_FL_MAX_SECTORS_MIN)) {
+ if (us->fflags & (US_FL_MAX_SECTORS_64 | US_FL_MAX_SECTORS_MIN)) {
unsigned int max_sectors = 64;
- if (us->flags & US_FL_MAX_SECTORS_MIN)
+ if (us->fflags & US_FL_MAX_SECTORS_MIN)
max_sectors = PAGE_CACHE_SIZE >> 9;
if (sdev->request_queue->max_sectors > max_sectors)
blk_queue_max_sectors(sdev->request_queue,
@@ -148,7 +146,7 @@ static int slave_configure(struct scsi_device *sdev)
* majority of devices work fine, but a few still can't
* handle it. The sd driver will simply assume those
* devices are write-enabled. */
- if (us->flags & US_FL_NO_WP_DETECT)
+ if (us->fflags & US_FL_NO_WP_DETECT)
sdev->skip_ms_page_3f = 1;
/* A number of devices have problems with MODE SENSE for
@@ -158,13 +156,13 @@ static int slave_configure(struct scsi_device *sdev)
/* Some disks return the total number of blocks in response
* to READ CAPACITY rather than the highest block number.
* If this device makes that mistake, tell the sd driver. */
- if (us->flags & US_FL_FIX_CAPACITY)
+ if (us->fflags & US_FL_FIX_CAPACITY)
sdev->fix_capacity = 1;
/* A few disks have two indistinguishable version, one of
* which reports the correct capacity and the other does not.
* The sd driver has to guess which is the case. */
- if (us->flags & US_FL_CAPACITY_HEURISTICS)
+ if (us->fflags & US_FL_CAPACITY_HEURISTICS)
sdev->guess_capacity = 1;
/* Some devices report a SCSI revision level above 2 but are
@@ -213,7 +211,7 @@ static int slave_configure(struct scsi_device *sdev)
/* Some devices choke when they receive a PREVENT-ALLOW MEDIUM
* REMOVAL command, so suppress those commands. */
- if (us->flags & US_FL_NOT_LOCKABLE)
+ if (us->fflags & US_FL_NOT_LOCKABLE)
sdev->lockable = 0;
/* this is to satisfy the compiler, tho I don't think the
@@ -238,7 +236,7 @@ static int queuecommand(struct scsi_cmnd *srb,
}
/* fail the command if we are disconnecting */
- if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+ if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) {
US_DEBUGP("Fail command during disconnect\n");
srb->result = DID_NO_CONNECT << 16;
done(srb);
@@ -248,7 +246,7 @@ static int queuecommand(struct scsi_cmnd *srb,
/* enqueue the command and wake up the control thread */
srb->scsi_done = done;
us->srb = srb;
- up(&(us->sema));
+ complete(&us->cmnd_ready);
return 0;
}
@@ -280,9 +278,9 @@ static int command_abort(struct scsi_cmnd *srb)
* with the reset). Note that we must retain the host lock while
* calling usb_stor_stop_transport(); otherwise it might interfere
* with an auto-reset that begins as soon as we release the lock. */
- set_bit(US_FLIDX_TIMED_OUT, &us->flags);
- if (!test_bit(US_FLIDX_RESETTING, &us->flags)) {
- set_bit(US_FLIDX_ABORTING, &us->flags);
+ set_bit(US_FLIDX_TIMED_OUT, &us->dflags);
+ if (!test_bit(US_FLIDX_RESETTING, &us->dflags)) {
+ set_bit(US_FLIDX_ABORTING, &us->dflags);
usb_stor_stop_transport(us);
}
scsi_unlock(us_to_host(us));
@@ -329,7 +327,7 @@ void usb_stor_report_device_reset(struct us_data *us)
struct Scsi_Host *host = us_to_host(us);
scsi_report_device_reset(host, 0, 0);
- if (us->flags & US_FL_SCM_MULT_TARG) {
+ if (us->fflags & US_FL_SCM_MULT_TARG) {
for (i = 1; i < host->max_id; ++i)
scsi_report_device_reset(host, 0, i);
}
@@ -400,7 +398,7 @@ static int proc_info (struct Scsi_Host *host, char *buffer,
pos += sprintf(pos, " Quirks:");
#define US_FLAG(name, value) \
- if (us->flags & value) pos += sprintf(pos, " " #name);
+ if (us->fflags & value) pos += sprintf(pos, " " #name);
US_DO_ALL_FLAGS
#undef US_FLAG
diff --git a/drivers/usb/storage/scsiglue.h b/drivers/usb/storage/scsiglue.h
index 737e4fa6045f..ffa1cca93d2c 100644
--- a/drivers/usb/storage/scsiglue.h
+++ b/drivers/usb/storage/scsiglue.h
@@ -1,8 +1,6 @@
/* Driver for USB Mass Storage compliant devices
* SCSI Connecting Glue Header File
*
- * $Id: scsiglue.h,v 1.4 2000/08/25 00:13:51 mdharm Exp $
- *
* Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c
index 8972b17da843..c5a54b872c24 100644
--- a/drivers/usb/storage/sddr09.c
+++ b/drivers/usb/storage/sddr09.c
@@ -1,6 +1,5 @@
/* Driver for SanDisk SDDR-09 SmartMedia reader
*
- * $Id: sddr09.c,v 1.24 2002/04/22 03:39:43 mdharm Exp $
* (c) 2000, 2001 Robert Baruch (autophile@starband.net)
* (c) 2002 Andries Brouwer (aeb@cwi.nl)
* Developed with the assistance of:
diff --git a/drivers/usb/storage/sddr09.h b/drivers/usb/storage/sddr09.h
index c03089a9ec38..e50033ad7b19 100644
--- a/drivers/usb/storage/sddr09.h
+++ b/drivers/usb/storage/sddr09.h
@@ -1,8 +1,6 @@
/* Driver for SanDisk SDDR-09 SmartMedia reader
* Header File
*
- * $Id: sddr09.h,v 1.5 2000/08/25 00:13:51 mdharm Exp $
- *
* Current development and maintenance by:
* (c) 2000 Robert Baruch (autophile@dol.net)
* (c) 2002 Andries Brouwer (aeb@cwi.nl)
diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c
index 6d14327c921d..0d8df7577899 100644
--- a/drivers/usb/storage/sddr55.c
+++ b/drivers/usb/storage/sddr55.c
@@ -1,7 +1,5 @@
/* Driver for SanDisk SDDR-55 SmartMedia reader
*
- * $Id:$
- *
* SDDR55 driver v0.1:
*
* First release
diff --git a/drivers/usb/storage/sddr55.h b/drivers/usb/storage/sddr55.h
index d6bd32f6c9f3..a815a0470c84 100644
--- a/drivers/usb/storage/sddr55.h
+++ b/drivers/usb/storage/sddr55.h
@@ -1,8 +1,6 @@
/* Driver for SanDisk SDDR-55 SmartMedia reader
* Header File
*
- * $Id:$
- *
* Current development and maintenance by:
* (c) 2002 Simon Munton
*
diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c
index 570c1250f6f3..ae6d64810d2a 100644
--- a/drivers/usb/storage/shuttle_usbat.c
+++ b/drivers/usb/storage/shuttle_usbat.c
@@ -1,7 +1,5 @@
/* Driver for SCM Microsystems (a.k.a. Shuttle) USB-ATAPI cable
*
- * $Id: shuttle_usbat.c,v 1.17 2002/04/22 03:39:43 mdharm Exp $
- *
* Current development and maintenance by:
* (c) 2000, 2001 Robert Baruch (autophile@starband.net)
* (c) 2004, 2005 Daniel Drake <dsd@gentoo.org>
diff --git a/drivers/usb/storage/shuttle_usbat.h b/drivers/usb/storage/shuttle_usbat.h
index 3ddf143a1dec..d8bfc43e9044 100644
--- a/drivers/usb/storage/shuttle_usbat.h
+++ b/drivers/usb/storage/shuttle_usbat.h
@@ -1,8 +1,6 @@
/* Driver for SCM Microsystems USB-ATAPI cable
* Header File
*
- * $Id: shuttle_usbat.h,v 1.5 2000/09/17 14:44:52 groovyjava Exp $
- *
* Current development and maintenance by:
* (c) 2000 Robert Baruch (autophile@dol.net)
* (c) 2004, 2005 Daniel Drake <dsd@gentoo.org>
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index 6610d2dd1e7f..08d3a13fec2c 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -1,7 +1,5 @@
/* Driver for USB Mass Storage compliant devices
*
- * $Id: transport.c,v 1.47 2002/04/22 03:39:43 mdharm Exp $
- *
* Current development and maintenance by:
* (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
@@ -75,14 +73,14 @@
* by a separate code path.)
*
* The abort function (usb_storage_command_abort() in scsiglue.c) first
- * sets the machine state and the ABORTING bit in us->flags to prevent
+ * sets the machine state and the ABORTING bit in us->dflags to prevent
* new URBs from being submitted. It then calls usb_stor_stop_transport()
- * below, which atomically tests-and-clears the URB_ACTIVE bit in us->flags
+ * below, which atomically tests-and-clears the URB_ACTIVE bit in us->dflags
* to see if the current_urb needs to be stopped. Likewise, the SG_ACTIVE
* bit is tested to see if the current_sg scatter-gather request needs to be
* stopped. The timeout callback routine does much the same thing.
*
- * When a disconnect occurs, the DISCONNECTING bit in us->flags is set to
+ * When a disconnect occurs, the DISCONNECTING bit in us->dflags is set to
* prevent new URBs from being submitted, and usb_stor_stop_transport() is
* called to stop any ongoing requests.
*
@@ -127,8 +125,8 @@ static int usb_stor_msg_common(struct us_data *us, int timeout)
long timeleft;
int status;
- /* don't submit URBs during abort/disconnect processing */
- if (us->flags & ABORTING_OR_DISCONNECTING)
+ /* don't submit URBs during abort processing */
+ if (test_bit(US_FLIDX_ABORTING, &us->dflags))
return -EIO;
/* set up data structures for the wakeup system */
@@ -159,13 +157,13 @@ static int usb_stor_msg_common(struct us_data *us, int timeout)
/* since the URB has been submitted successfully, it's now okay
* to cancel it */
- set_bit(US_FLIDX_URB_ACTIVE, &us->flags);
+ set_bit(US_FLIDX_URB_ACTIVE, &us->dflags);
- /* did an abort/disconnect occur during the submission? */
- if (us->flags & ABORTING_OR_DISCONNECTING) {
+ /* did an abort occur during the submission? */
+ if (test_bit(US_FLIDX_ABORTING, &us->dflags)) {
/* cancel the URB, if it hasn't been cancelled already */
- if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->flags)) {
+ if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags)) {
US_DEBUGP("-- cancelling URB\n");
usb_unlink_urb(us->current_urb);
}
@@ -175,7 +173,7 @@ static int usb_stor_msg_common(struct us_data *us, int timeout)
timeleft = wait_for_completion_interruptible_timeout(
&urb_done, timeout ? : MAX_SCHEDULE_TIMEOUT);
- clear_bit(US_FLIDX_URB_ACTIVE, &us->flags);
+ clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags);
if (timeleft <= 0) {
US_DEBUGP("%s -- cancelling URB\n",
@@ -419,8 +417,8 @@ static int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe,
{
int result;
- /* don't submit s-g requests during abort/disconnect processing */
- if (us->flags & ABORTING_OR_DISCONNECTING)
+ /* don't submit s-g requests during abort processing */
+ if (test_bit(US_FLIDX_ABORTING, &us->dflags))
return USB_STOR_XFER_ERROR;
/* initialize the scatter-gather request block */
@@ -435,13 +433,13 @@ static int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe,
/* since the block has been initialized successfully, it's now
* okay to cancel it */
- set_bit(US_FLIDX_SG_ACTIVE, &us->flags);
+ set_bit(US_FLIDX_SG_ACTIVE, &us->dflags);
- /* did an abort/disconnect occur during the submission? */
- if (us->flags & ABORTING_OR_DISCONNECTING) {
+ /* did an abort occur during the submission? */
+ if (test_bit(US_FLIDX_ABORTING, &us->dflags)) {
/* cancel the request, if it hasn't been cancelled already */
- if (test_and_clear_bit(US_FLIDX_SG_ACTIVE, &us->flags)) {
+ if (test_and_clear_bit(US_FLIDX_SG_ACTIVE, &us->dflags)) {
US_DEBUGP("-- cancelling sg request\n");
usb_sg_cancel(&us->current_sg);
}
@@ -449,7 +447,7 @@ static int usb_stor_bulk_transfer_sglist(struct us_data *us, unsigned int pipe,
/* wait for the completion of the transfer */
usb_sg_wait(&us->current_sg);
- clear_bit(US_FLIDX_SG_ACTIVE, &us->flags);
+ clear_bit(US_FLIDX_SG_ACTIVE, &us->dflags);
result = us->current_sg.status;
if (act_len)
@@ -530,7 +528,7 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
/* if the command gets aborted by the higher layers, we need to
* short-circuit all other processing
*/
- if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) {
+ if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
US_DEBUGP("-- command was aborted\n");
srb->result = DID_ABORT << 16;
goto Handle_Errors;
@@ -616,7 +614,7 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
/* let's clean up right away */
scsi_eh_restore_cmnd(srb, &ses);
- if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) {
+ if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
US_DEBUGP("-- auto-sense aborted\n");
srb->result = DID_ABORT << 16;
goto Handle_Errors;
@@ -629,7 +627,7 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
* auto-sense is perfectly valid
*/
srb->result = DID_ERROR << 16;
- if (!(us->flags & US_FL_SCM_MULT_TARG))
+ if (!(us->fflags & US_FL_SCM_MULT_TARG))
goto Handle_Errors;
return;
}
@@ -679,8 +677,8 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
/* Set the RESETTING bit, and clear the ABORTING bit so that
* the reset may proceed. */
scsi_lock(us_to_host(us));
- set_bit(US_FLIDX_RESETTING, &us->flags);
- clear_bit(US_FLIDX_ABORTING, &us->flags);
+ set_bit(US_FLIDX_RESETTING, &us->dflags);
+ clear_bit(US_FLIDX_ABORTING, &us->dflags);
scsi_unlock(us_to_host(us));
/* We must release the device lock because the pre_reset routine
@@ -695,7 +693,7 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us)
scsi_unlock(us_to_host(us));
us->transport_reset(us);
}
- clear_bit(US_FLIDX_RESETTING, &us->flags);
+ clear_bit(US_FLIDX_RESETTING, &us->dflags);
}
/* Stop the current URB transfer */
@@ -707,13 +705,13 @@ void usb_stor_stop_transport(struct us_data *us)
* let's wake it up. The test_and_clear_bit() call
* guarantees that if a URB has just been submitted,
* it won't be cancelled more than once. */
- if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->flags)) {
+ if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->dflags)) {
US_DEBUGP("-- cancelling URB\n");
usb_unlink_urb(us->current_urb);
}
/* If we are waiting for a scatter-gather operation, cancel it. */
- if (test_and_clear_bit(US_FLIDX_SG_ACTIVE, &us->flags)) {
+ if (test_and_clear_bit(US_FLIDX_SG_ACTIVE, &us->dflags)) {
US_DEBUGP("-- cancelling sg request\n");
usb_sg_cancel(&us->current_sg);
}
@@ -914,7 +912,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
unsigned int cbwlen = US_BULK_CB_WRAP_LEN;
/* Take care of BULK32 devices; set extra byte to 0 */
- if ( unlikely(us->flags & US_FL_BULK32)) {
+ if (unlikely(us->fflags & US_FL_BULK32)) {
cbwlen = 32;
us->iobuf[31] = 0;
}
@@ -925,7 +923,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
bcb->Flags = srb->sc_data_direction == DMA_FROM_DEVICE ? 1 << 7 : 0;
bcb->Tag = ++us->tag;
bcb->Lun = srb->device->lun;
- if (us->flags & US_FL_SCM_MULT_TARG)
+ if (us->fflags & US_FL_SCM_MULT_TARG)
bcb->Lun |= srb->device->id << 4;
bcb->Length = srb->cmd_len;
@@ -951,7 +949,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
/* Some USB-IDE converter chips need a 100us delay between the
* command phase and the data phase. Some devices need a little
* more than that, probably because of clock rate inaccuracies. */
- if (unlikely(us->flags & US_FL_GO_SLOW))
+ if (unlikely(us->fflags & US_FL_GO_SLOW))
udelay(125);
if (transfer_length) {
@@ -1010,7 +1008,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
US_DEBUGP("Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n",
le32_to_cpu(bcs->Signature), bcs->Tag,
residue, bcs->Status);
- if (!(bcs->Tag == us->tag || (us->flags & US_FL_BULK_IGNORE_TAG)) ||
+ if (!(bcs->Tag == us->tag || (us->fflags & US_FL_BULK_IGNORE_TAG)) ||
bcs->Status > US_BULK_STAT_PHASE) {
US_DEBUGP("Bulk logical error\n");
return USB_STOR_TRANSPORT_ERROR;
@@ -1035,7 +1033,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us)
/* try to compute the actual residue, based on how much data
* was really transferred and what the device tells us */
if (residue) {
- if (!(us->flags & US_FL_IGNORE_RESIDUE)) {
+ if (!(us->fflags & US_FL_IGNORE_RESIDUE)) {
residue = min(residue, transfer_length);
scsi_set_resid(srb, max(scsi_get_resid(srb),
(int) residue));
@@ -1090,7 +1088,7 @@ static int usb_stor_reset_common(struct us_data *us,
int result;
int result2;
- if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+ if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) {
US_DEBUGP("No reset during disconnect\n");
return -EIO;
}
@@ -1103,12 +1101,12 @@ static int usb_stor_reset_common(struct us_data *us,
return result;
}
- /* Give the device some time to recover from the reset,
- * but don't delay disconnect processing. */
- wait_event_interruptible_timeout(us->delay_wait,
- test_bit(US_FLIDX_DISCONNECTING, &us->flags),
- HZ*6);
- if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+ /* Give the device some time to recover from the reset,
+ * but don't delay disconnect processing. */
+ wait_event_interruptible_timeout(us->delay_wait,
+ test_bit(US_FLIDX_DISCONNECTING, &us->dflags),
+ HZ*6);
+ if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) {
US_DEBUGP("Reset interrupted by disconnect\n");
return -EIO;
}
@@ -1170,7 +1168,7 @@ int usb_stor_port_reset(struct us_data *us)
US_DEBUGP("unable to lock device for reset: %d\n", result);
else {
/* Were we disconnected while waiting for the lock? */
- if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+ if (test_bit(US_FLIDX_DISCONNECTING, &us->dflags)) {
result = -EIO;
US_DEBUGP("No reset during disconnect\n");
} else {
diff --git a/drivers/usb/storage/transport.h b/drivers/usb/storage/transport.h
index ada7c2f43f84..e70b88182f0e 100644
--- a/drivers/usb/storage/transport.h
+++ b/drivers/usb/storage/transport.h
@@ -1,8 +1,6 @@
/* Driver for USB Mass Storage compliant devices
* Transport Functions Header File
*
- * $Id: transport.h,v 1.18 2002/04/21 02:57:59 mdharm Exp $
- *
* Current development and maintenance by:
* (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 45fe3663fa7f..25b4a5b3975e 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -1,8 +1,6 @@
/* Driver for USB Mass Storage compliant devices
* Unusual Devices File
*
- * $Id: unusual_devs.h,v 1.32 2002/02/25 02:41:24 mdharm Exp $
- *
* Current development and maintenance by:
* (c) 2000-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index e268aacb773a..bfea851be985 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -1,7 +1,5 @@
/* Driver for USB Mass Storage compliant devices
*
- * $Id: usb.c,v 1.75 2002/04/22 03:39:43 mdharm Exp $
- *
* Current development and maintenance by:
* (c) 1999-2003 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
@@ -312,26 +310,27 @@ static int usb_stor_control_thread(void * __us)
for(;;) {
US_DEBUGP("*** thread sleeping.\n");
- if(down_interruptible(&us->sema))
+ if (wait_for_completion_interruptible(&us->cmnd_ready))
break;
-
+
US_DEBUGP("*** thread awakened.\n");
/* lock the device pointers */
mutex_lock(&(us->dev_mutex));
- /* if the device has disconnected, we are free to exit */
- if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
- US_DEBUGP("-- exiting\n");
+ /* lock access to the state */
+ scsi_lock(host);
+
+ /* When we are called with no command pending, we're done */
+ if (us->srb == NULL) {
+ scsi_unlock(host);
mutex_unlock(&us->dev_mutex);
+ US_DEBUGP("-- exiting\n");
break;
}
- /* lock access to the state */
- scsi_lock(host);
-
/* has the command timed out *already* ? */
- if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) {
+ if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
us->srb->result = DID_ABORT << 16;
goto SkipForAbort;
}
@@ -350,7 +349,7 @@ static int usb_stor_control_thread(void * __us)
* the maximum known LUN
*/
else if (us->srb->device->id &&
- !(us->flags & US_FL_SCM_MULT_TARG)) {
+ !(us->fflags & US_FL_SCM_MULT_TARG)) {
US_DEBUGP("Bad target number (%d:%d)\n",
us->srb->device->id, us->srb->device->lun);
us->srb->result = DID_BAD_TARGET << 16;
@@ -365,7 +364,7 @@ static int usb_stor_control_thread(void * __us)
/* Handle those devices which need us to fake
* their inquiry data */
else if ((us->srb->cmnd[0] == INQUIRY) &&
- (us->flags & US_FL_FIX_INQUIRY)) {
+ (us->fflags & US_FL_FIX_INQUIRY)) {
unsigned char data_ptr[36] = {
0x00, 0x80, 0x02, 0x02,
0x1F, 0x00, 0x00, 0x00};
@@ -384,12 +383,8 @@ static int usb_stor_control_thread(void * __us)
/* lock access to the state */
scsi_lock(host);
- /* did the command already complete because of a disconnect? */
- if (!us->srb)
- ; /* nothing to do */
-
/* indicate that the command is done */
- else if (us->srb->result != DID_ABORT << 16) {
+ if (us->srb->result != DID_ABORT << 16) {
US_DEBUGP("scsi cmd done, result=0x%x\n",
us->srb->result);
us->srb->scsi_done(us->srb);
@@ -403,12 +398,12 @@ SkipForAbort:
* the TIMED_OUT flag, not srb->result == DID_ABORT, because
* the timeout might have occurred after the command had
* already completed with a different result code. */
- if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) {
+ if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
complete(&(us->notify));
/* Allow USB transfers to resume */
- clear_bit(US_FLIDX_ABORTING, &us->flags);
- clear_bit(US_FLIDX_TIMED_OUT, &us->flags);
+ clear_bit(US_FLIDX_ABORTING, &us->dflags);
+ clear_bit(US_FLIDX_TIMED_OUT, &us->dflags);
}
/* finished working on this command */
@@ -500,9 +495,9 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id)
us->protocol = (unusual_dev->useTransport == US_PR_DEVICE) ?
idesc->bInterfaceProtocol :
unusual_dev->useTransport;
- us->flags = USB_US_ORIG_FLAGS(id->driver_info);
+ us->fflags = USB_US_ORIG_FLAGS(id->driver_info);
- if (us->flags & US_FL_IGNORE_DEVICE) {
+ if (us->fflags & US_FL_IGNORE_DEVICE) {
printk(KERN_INFO USB_STORAGE "device ignored\n");
return -ENODEV;
}
@@ -512,7 +507,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id)
* disable it if we're in full-speed
*/
if (dev->speed != USB_SPEED_HIGH)
- us->flags &= ~US_FL_GO_SLOW;
+ us->fflags &= ~US_FL_GO_SLOW;
/* Log a message if a non-generic unusual_dev entry contains an
* unnecessary subclass or protocol override. This may stimulate
@@ -533,7 +528,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id)
if (unusual_dev->useTransport != US_PR_DEVICE &&
us->protocol == idesc->bInterfaceProtocol)
msg += 2;
- if (msg >= 0 && !(us->flags & US_FL_NEED_OVERRIDE))
+ if (msg >= 0 && !(us->fflags & US_FL_NEED_OVERRIDE))
printk(KERN_NOTICE USB_STORAGE "This device "
"(%04x,%04x,%04x S %02x P %02x)"
" has %s in unusual_devs.h (kernel"
@@ -663,7 +658,7 @@ static int get_transport(struct us_data *us)
US_DEBUGP("Transport: %s\n", us->transport_name);
/* fix for single-lun devices */
- if (us->flags & US_FL_SINGLE_LUN)
+ if (us->fflags & US_FL_SINGLE_LUN)
us->max_lun = 0;
return 0;
}
@@ -820,12 +815,11 @@ static void usb_stor_release_resources(struct us_data *us)
US_DEBUGP("-- %s\n", __func__);
/* Tell the control thread to exit. The SCSI host must
- * already have been removed so it won't try to queue
- * any more commands.
+ * already have been removed and the DISCONNECTING flag set
+ * so that we won't accept any more commands.
*/
US_DEBUGP("-- sending exit command to thread\n");
- set_bit(US_FLIDX_DISCONNECTING, &us->flags);
- up(&us->sema);
+ complete(&us->cmnd_ready);
if (us->ctl_thread)
kthread_stop(us->ctl_thread);
@@ -859,39 +853,36 @@ static void dissociate_dev(struct us_data *us)
usb_set_intfdata(us->pusb_intf, NULL);
}
-/* First stage of disconnect processing: stop all commands and remove
- * the host */
+/* First stage of disconnect processing: stop SCSI scanning,
+ * remove the host, and stop accepting new commands
+ */
static void quiesce_and_remove_host(struct us_data *us)
{
struct Scsi_Host *host = us_to_host(us);
- /* Prevent new USB transfers, stop the current command, and
- * interrupt a SCSI-scan or device-reset delay */
- scsi_lock(host);
- set_bit(US_FLIDX_DISCONNECTING, &us->flags);
- scsi_unlock(host);
- usb_stor_stop_transport(us);
- wake_up(&us->delay_wait);
+ /* If the device is really gone, cut short reset delays */
+ if (us->pusb_dev->state == USB_STATE_NOTATTACHED)
+ set_bit(US_FLIDX_DISCONNECTING, &us->dflags);
- /* queuecommand won't accept any new commands and the control
- * thread won't execute a previously-queued command. If there
- * is such a command pending, complete it with an error. */
- mutex_lock(&us->dev_mutex);
- if (us->srb) {
- us->srb->result = DID_NO_CONNECT << 16;
- scsi_lock(host);
- us->srb->scsi_done(us->srb);
- us->srb = NULL;
- complete(&us->notify); /* in case of an abort */
- scsi_unlock(host);
- }
- mutex_unlock(&us->dev_mutex);
+ /* Prevent SCSI-scanning (if it hasn't started yet)
+ * and wait for the SCSI-scanning thread to stop.
+ */
+ set_bit(US_FLIDX_DONT_SCAN, &us->dflags);
+ wake_up(&us->delay_wait);
+ wait_for_completion(&us->scanning_done);
- /* Now we own no commands so it's safe to remove the SCSI host */
+ /* Removing the host will perform an orderly shutdown: caches
+ * synchronized, disks spun down, etc.
+ */
scsi_remove_host(host);
- /* Wait for the SCSI-scanning thread to stop */
- wait_for_completion(&us->scanning_done);
+ /* Prevent any new commands from being accepted and cut short
+ * reset delays.
+ */
+ scsi_lock(host);
+ set_bit(US_FLIDX_DISCONNECTING, &us->dflags);
+ scsi_unlock(host);
+ wake_up(&us->delay_wait);
}
/* Second stage of disconnect processing: deallocate all resources */
@@ -919,16 +910,16 @@ static int usb_stor_scan_thread(void * __us)
printk(KERN_DEBUG "usb-storage: waiting for device "
"to settle before scanning\n");
wait_event_freezable_timeout(us->delay_wait,
- test_bit(US_FLIDX_DISCONNECTING, &us->flags),
+ test_bit(US_FLIDX_DONT_SCAN, &us->dflags),
delay_use * HZ);
}
/* If the device is still connected, perform the scanning */
- if (!test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+ if (!test_bit(US_FLIDX_DONT_SCAN, &us->dflags)) {
/* For bulk-only devices, determine the max LUN value */
if (us->protocol == US_PR_BULK &&
- !(us->flags & US_FL_SINGLE_LUN)) {
+ !(us->fflags & US_FL_SINGLE_LUN)) {
mutex_lock(&us->dev_mutex);
us->max_lun = usb_stor_Bulk_max_lun(us);
mutex_unlock(&us->dev_mutex);
@@ -975,7 +966,7 @@ static int storage_probe(struct usb_interface *intf,
us = host_to_us(host);
memset(us, 0, sizeof(struct us_data));
mutex_init(&(us->dev_mutex));
- init_MUTEX_LOCKED(&(us->sema));
+ init_completion(&us->cmnd_ready);
init_completion(&(us->notify));
init_waitqueue_head(&us->delay_wait);
init_completion(&us->scanning_done);
@@ -1023,6 +1014,7 @@ static int storage_probe(struct usb_interface *intf,
if (IS_ERR(th)) {
printk(KERN_WARNING USB_STORAGE
"Unable to start the device-scanning thread\n");
+ complete(&us->scanning_done);
quiesce_and_remove_host(us);
result = PTR_ERR(th);
goto BadDevice;
@@ -1065,6 +1057,7 @@ static struct usb_driver usb_storage_driver = {
.pre_reset = storage_pre_reset,
.post_reset = storage_post_reset,
.id_table = storage_usb_ids,
+ .soft_unbind = 1,
};
static int __init usb_stor_init(void)
diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
index 8d87503e2560..a4ad73bd832d 100644
--- a/drivers/usb/storage/usb.h
+++ b/drivers/usb/storage/usb.h
@@ -1,8 +1,6 @@
/* Driver for USB Mass Storage compliant devices
* Main Header File
*
- * $Id: usb.h,v 1.21 2002/04/21 02:57:59 mdharm Exp $
- *
* Current development and maintenance by:
* (c) 1999-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
*
@@ -67,16 +65,14 @@ struct us_unusual_dev {
};
-/* Dynamic flag definitions: used in set_bit() etc. */
-#define US_FLIDX_URB_ACTIVE 18 /* 0x00040000 current_urb is in use */
-#define US_FLIDX_SG_ACTIVE 19 /* 0x00080000 current_sg is in use */
-#define US_FLIDX_ABORTING 20 /* 0x00100000 abort is in progress */
-#define US_FLIDX_DISCONNECTING 21 /* 0x00200000 disconnect in progress */
-#define ABORTING_OR_DISCONNECTING ((1UL << US_FLIDX_ABORTING) | \
- (1UL << US_FLIDX_DISCONNECTING))
-#define US_FLIDX_RESETTING 22 /* 0x00400000 device reset in progress */
-#define US_FLIDX_TIMED_OUT 23 /* 0x00800000 SCSI midlayer timed out */
-
+/* Dynamic bitflag definitions (us->dflags): used in set_bit() etc. */
+#define US_FLIDX_URB_ACTIVE 0 /* current_urb is in use */
+#define US_FLIDX_SG_ACTIVE 1 /* current_sg is in use */
+#define US_FLIDX_ABORTING 2 /* abort is in progress */
+#define US_FLIDX_DISCONNECTING 3 /* disconnect in progress */
+#define US_FLIDX_RESETTING 4 /* device reset in progress */
+#define US_FLIDX_TIMED_OUT 5 /* SCSI midlayer timed out */
+#define US_FLIDX_DONT_SCAN 6 /* don't scan (disconnect) */
#define USB_STOR_STRING_LEN 32
@@ -109,7 +105,8 @@ struct us_data {
struct usb_device *pusb_dev; /* this usb_device */
struct usb_interface *pusb_intf; /* this interface */
struct us_unusual_dev *unusual_dev; /* device-filter entry */
- unsigned long flags; /* from filter initially */
+ unsigned long fflags; /* fixed flags from filter */
+ unsigned long dflags; /* dynamic atomic bitflags */
unsigned int send_bulk_pipe; /* cached pipe values */
unsigned int recv_bulk_pipe;
unsigned int send_ctrl_pipe;
@@ -147,7 +144,7 @@ struct us_data {
struct task_struct *ctl_thread; /* the control thread */
/* mutual exclusion and synchronization structures */
- struct semaphore sema; /* to sleep thread on */
+ struct completion cmnd_ready; /* to sleep thread on */
struct completion notify; /* thread begin/end */
wait_queue_head_t delay_wait; /* wait during scan, reset */
struct completion scanning_done; /* wait for scan thread */
diff --git a/drivers/usb/wusbcore/Kconfig b/drivers/usb/wusbcore/Kconfig
new file mode 100644
index 000000000000..add077e5c5d0
--- /dev/null
+++ b/drivers/usb/wusbcore/Kconfig
@@ -0,0 +1,17 @@
+#
+# Wireless USB Core configuration
+#
+config USB_WUSB
+ tristate "Enable Wireless USB extensions"
+ depends on USB
+ select UWB
+ select CRYPTO
+ select CRYPTO_BLKCIPHER
+ select CRYPTO_CBC
+ select CRYPTO_MANAGER
+ select CRYPTO_AES
+ help
+ Enable the host-side support for Wireless USB.
+
+ To compile this support select Y (built in). It is safe to
+ select even if you don't have the hardware.
diff --git a/drivers/usb/wusbcore/Makefile b/drivers/usb/wusbcore/Makefile
new file mode 100644
index 000000000000..7a4d00724039
--- /dev/null
+++ b/drivers/usb/wusbcore/Makefile
@@ -0,0 +1,20 @@
+obj-$(CONFIG_USB_WUSB) += wusbcore.o wusb-cbaf.o
+obj-$(CONFIG_USB_HWA_HCD) += wusb-wa.o
+
+wusbcore-objs := \
+ crypto.o \
+ devconnect.o \
+ dev-sysfs.o \
+ mmc.o \
+ pal.o \
+ rh.o \
+ reservation.o \
+ security.o \
+ wusbhc.o
+
+wusb-cbaf-objs := cbaf.o
+
+wusb-wa-objs := wa-hc.o \
+ wa-nep.o \
+ wa-rpipe.o \
+ wa-xfer.o
diff --git a/drivers/usb/wusbcore/cbaf.c b/drivers/usb/wusbcore/cbaf.c
new file mode 100644
index 000000000000..1dab3936e7d2
--- /dev/null
+++ b/drivers/usb/wusbcore/cbaf.c
@@ -0,0 +1,620 @@
+/*
+ * Wireless USB - Cable Based Association
+ *
+ *
+ * Copyright (C) 2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * WUSB devices have to be paired (authenticated in WUSB lingo) so
+ * that they can connect to the system.
+ *
+ * One way of pairing is using CBA-Cable Based Authentication, devices
+ * that can connect via wired or wireless USB. First time you plug
+ * them with a cable, pairing is done between host and device and
+ * subsequent times, you can connect wirelessly without having to
+ * pair. That's the idea.
+ *
+ * This driver does nothing Earth shattering. It just provides an
+ * interface to chat with the wire-connected device so we can get a
+ * CDID (device ID) that might have been previously associated to a
+ * CHID (host ID) and to set up a new <CHID,CDID,CK> triplet
+ * (connection context), with the CK being the secret, or connection
+ * key. This is the pairing data.
+ *
+ * When a device with the CBA capability connects, the probe routine
+ * just creates a bunch of sysfs files that a user space enumeration
+ * manager uses to allow it to connect wirelessly to the system or not.
+ *
+ * The process goes like this:
+ *
+ * 1. device plugs, cbaf is loaded, notifications happen
+ *
+ * 2. the connection manager sees a device with CBAF capability (the
+ * wusb_{host_info,cdid,cc} files are in /sys/device/blah/OURDEVICE).
+ *
+ * 3. CM (connection manager) writes the CHID (host ID) and a host
+ * name into the wusb_host_info file. This gets sent to the device.
+ *
+ * 4. CM cats the wusb_cdid file; this asks the device if it has any
+ * CDID associated to the CHDI we just wrote before. If it does, it
+ * is printed, along with the device 'friendly name' and the band
+ * groups the device supports.
+ *
+ * 5. CM looks up its database
+ *
+ * 5.1 If it has a matching CHID,CDID entry, the device has been
+ * authorized before (paired). Now we can optionally ask the user
+ * if he wants to allow the device to connect. Then we generate a
+ * new CDID and CK, send it to the device and update the database
+ * (writing to the wusb_cc file so they are uploaded to the device).
+ *
+ * 5.2 If the CDID is zero (or we didn't find a matching CDID in our
+ * database), we assume the device is not known. We ask the user
+ * if s/he wants to allow the device to be connected wirelessly
+ * to the system. If nope, nothing else is done (FIXME: maybe
+ * send a zero CDID to clean up our CHID?). If yes, we generate
+ * random CDID and CKs (and write them to the wusb_cc file so
+ * they are uploaded to the device).
+ *
+ * 6. device is unplugged
+ *
+ * When the device tries to connect wirelessly, it will present it's
+ * CDID to the WUSB host controller with ID CHID, which will query the
+ * database. If found, the host will (with a 4way handshake) challenge
+ * the device to demonstrate it has the CK secret key (from our
+ * database) without actually exchanging it. Once satisfied, crypto
+ * keys are derived from the CK, the device is connected and all
+ * communication is crypted.
+ *
+ *
+ * NOTES ABOUT THE IMPLEMENTATION
+ *
+ * The descriptors sent back and forth use this horrible format from
+ * hell on which each field is actually a field ID, field length and
+ * then the field itself. How stupid can that get, taking into account
+ * the structures are defined by the spec?? oh well.
+ *
+ *
+ * FIXME: we don't provide a way to tell the device the pairing failed
+ * (ie: send a CC_DATA_FAIL). Should add some day.
+ */
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/version.h>
+#include <linux/usb.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/random.h>
+#include <linux/mutex.h>
+#include <linux/uwb.h>
+#include <linux/usb/wusb.h>
+#include <linux/usb/association.h>
+
+#undef D_LOCAL
+#define D_LOCAL 6
+#include <linux/uwb/debug.h>
+
+/* An instance of a Cable-Based-Association-Framework device */
+struct cbaf {
+ struct usb_device *usb_dev;
+ struct usb_interface *usb_iface;
+ void *buffer;
+ size_t buffer_size;
+
+ struct wusb_ckhdid chid;/* Host Information */
+ char host_name[65]; /* max length:
+ Assoc Models Suplement 1.0[T4-7] */
+ u16 host_band_groups;
+
+ struct wusb_ckhdid cdid;/* Device Information */
+ char device_name[65]; /* max length:
+ Assoc Models Suplement 1.0[T4-7] */
+ u16 device_band_groups;
+ struct wusb_ckhdid ck; /* Connection Key */
+};
+
+/*
+ * Verify that a CBAF USB-interface has what we need
+ *
+ * (like we care, we are going to fail the enumeration if not :)
+ *
+ * FIXME: ugly function, need to split
+ */
+static int cbaf_check(struct cbaf *cbaf)
+{
+ int result;
+ struct device *dev = &cbaf->usb_iface->dev;
+ struct wusb_cbaf_assoc_info *assoc_info;
+ struct wusb_cbaf_assoc_request *assoc_request;
+ size_t assoc_size;
+ void *itr, *top;
+ unsigned ar_index;
+ int ar_rhi_idx = -1, ar_assoc_idx = -1;
+
+ result = usb_control_msg(
+ cbaf->usb_dev, usb_rcvctrlpipe(cbaf->usb_dev, 0),
+ CBAF_REQ_GET_ASSOCIATION_INFORMATION,
+ USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+ cbaf->buffer, cbaf->buffer_size, 1000 /* FIXME: arbitrary */);
+ if (result < 0) {
+ dev_err(dev, "cannot get available association types: %d\n",
+ result);
+ goto error_get_assoc_types;
+ }
+ assoc_info = cbaf->buffer;
+ if (result < sizeof(*assoc_info)) {
+ dev_err(dev, "not enough data to decode association info "
+ "header (%zu vs %zu bytes required)\n",
+ (size_t)result, sizeof(*assoc_info));
+ goto error_bad_header;
+ }
+ assoc_size = le16_to_cpu(assoc_info->Length);
+ if (result < assoc_size) {
+ dev_err(dev, "not enough data to decode association info "
+ "(%zu vs %zu bytes required)\n",
+ (size_t)assoc_size, sizeof(*assoc_info));
+ goto error_bad_data;
+ }
+ /*
+ * From now on, we just verify, but won't error out unless we
+ * don't find the AR_TYPE_WUSB_{RETRIEVE_HOST_INFO,ASSOCIATE}
+ * types.
+ */
+ ar_index = 0;
+ itr = cbaf->buffer + sizeof(*assoc_info);
+ top = cbaf->buffer + assoc_size;
+ d_printf(1, dev, "Found %u association requests (%zu bytes)\n",
+ assoc_info->NumAssociationRequests, assoc_size);
+ while (itr < top) {
+ u16 ar_type, ar_subtype;
+ u32 ar_size;
+ const char *ar_name;
+
+ assoc_request = itr;
+ if (top - itr < sizeof(*assoc_request)) {
+ dev_err(dev, "not enough data to decode associaton "
+ "request (%zu vs %zu bytes needed)\n",
+ top - itr, sizeof(*assoc_request));
+ break;
+ }
+ ar_type = le16_to_cpu(assoc_request->AssociationTypeId);
+ ar_subtype = le16_to_cpu(assoc_request->AssociationSubTypeId);
+ ar_size = le32_to_cpu(assoc_request->AssociationTypeInfoSize);
+ switch (ar_type) {
+ case AR_TYPE_WUSB:
+ /* Verify we have what is mandated by AMS1.0 */
+ switch (ar_subtype) {
+ case AR_TYPE_WUSB_RETRIEVE_HOST_INFO:
+ ar_name = "retrieve_host_info";
+ ar_rhi_idx = ar_index;
+ break;
+ case AR_TYPE_WUSB_ASSOCIATE:
+ /* send assoc data */
+ ar_name = "associate";
+ ar_assoc_idx = ar_index;
+ break;
+ default:
+ ar_name = "unknown";
+ };
+ break;
+ default:
+ ar_name = "unknown";
+ };
+ d_printf(1, dev, "association request #%02u: 0x%04x/%04x "
+ "(%zu bytes): %s\n",
+ assoc_request->AssociationDataIndex, ar_type,
+ ar_subtype, (size_t)ar_size, ar_name);
+
+ itr += sizeof(*assoc_request);
+ ar_index++;
+ }
+ if (ar_rhi_idx == -1) {
+ dev_err(dev, "Missing RETRIEVE_HOST_INFO association "
+ "request\n");
+ goto error_bad_reqs;
+ }
+ if (ar_assoc_idx == -1) {
+ dev_err(dev, "Missing ASSOCIATE association request\n");
+ goto error_bad_reqs;
+ }
+ return 0;
+
+error_bad_header:
+error_bad_data:
+error_bad_reqs:
+error_get_assoc_types:
+ return -EINVAL;
+}
+
+static const struct wusb_cbaf_host_info cbaf_host_info_defaults = {
+ .AssociationTypeId_hdr = WUSB_AR_AssociationTypeId,
+ .AssociationTypeId = cpu_to_le16(AR_TYPE_WUSB),
+ .AssociationSubTypeId_hdr = WUSB_AR_AssociationSubTypeId,
+ .AssociationSubTypeId = cpu_to_le16(AR_TYPE_WUSB_RETRIEVE_HOST_INFO),
+ .CHID_hdr = WUSB_AR_CHID,
+ .LangID_hdr = WUSB_AR_LangID,
+ .HostFriendlyName_hdr = WUSB_AR_HostFriendlyName,
+};
+
+/* Send WUSB host information (CHID and name) to a CBAF device */
+static int cbaf_send_host_info(struct cbaf *cbaf)
+{
+ struct wusb_cbaf_host_info *hi;
+ size_t hi_size;
+
+ hi = cbaf->buffer;
+ memset(hi, 0, sizeof(*hi));
+ *hi = cbaf_host_info_defaults;
+ hi->CHID = cbaf->chid;
+ hi->LangID = 0; /* FIXME: I guess... */
+ strncpy(hi->HostFriendlyName, cbaf->host_name,
+ hi->HostFriendlyName_hdr.len);
+ hi->HostFriendlyName_hdr.len =
+ cpu_to_le16(strlen(hi->HostFriendlyName));
+ hi_size = sizeof(*hi) + strlen(hi->HostFriendlyName);
+ return usb_control_msg(cbaf->usb_dev, usb_sndctrlpipe(cbaf->usb_dev, 0),
+ CBAF_REQ_SET_ASSOCIATION_RESPONSE,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0x0101,
+ cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+ hi, hi_size, 1000 /* FIXME: arbitrary */);
+}
+
+/* Show current CHID info we have set from user space */
+static ssize_t cbaf_wusb_host_info_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_interface *iface = to_usb_interface(dev);
+ struct cbaf *cbaf = usb_get_intfdata(iface);
+ char pr_chid[WUSB_CKHDID_STRSIZE];
+
+ ckhdid_printf(pr_chid, sizeof(pr_chid), &cbaf->chid);
+ return scnprintf(buf, PAGE_SIZE, "CHID: %s\nName: %s\n",
+ pr_chid, cbaf->host_name);
+}
+
+/*
+ * Get a host info CHID from user space and send it to the device.
+ *
+ * The user can recover a CC from the device associated to that CHID
+ * by cat'ing wusb_connection_context.
+ */
+static ssize_t cbaf_wusb_host_info_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ ssize_t result;
+ struct usb_interface *iface = to_usb_interface(dev);
+ struct cbaf *cbaf = usb_get_intfdata(iface);
+
+ result = sscanf(buf,
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%04hx %64s\n",
+ &cbaf->chid.data[0] , &cbaf->chid.data[1],
+ &cbaf->chid.data[2] , &cbaf->chid.data[3],
+ &cbaf->chid.data[4] , &cbaf->chid.data[5],
+ &cbaf->chid.data[6] , &cbaf->chid.data[7],
+ &cbaf->chid.data[8] , &cbaf->chid.data[9],
+ &cbaf->chid.data[10], &cbaf->chid.data[11],
+ &cbaf->chid.data[12], &cbaf->chid.data[13],
+ &cbaf->chid.data[14], &cbaf->chid.data[15],
+ &cbaf->host_band_groups, cbaf->host_name);
+ if (result != 18) {
+ dev_err(dev, "Unrecognized CHID (need 16 8-bit hex digits, "
+ "a 16 bit hex band group mask "
+ "and a host name, got only %d)\n", (int)result);
+ return -EINVAL;
+ }
+ result = cbaf_send_host_info(cbaf);
+ if (result < 0)
+ dev_err(dev, "Couldn't send host information to device: %d\n",
+ (int)result);
+ else
+ d_printf(1, dev, "HI sent, wusb_cc can be read now\n");
+ return result < 0? result : size;
+}
+static DEVICE_ATTR(wusb_host_info, 0600, cbaf_wusb_host_info_show,
+ cbaf_wusb_host_info_store);
+
+static const struct wusb_cbaf_device_info cbaf_device_info_defaults = {
+ .Length_hdr = WUSB_AR_Length,
+ .CDID_hdr = WUSB_AR_CDID,
+ .BandGroups_hdr = WUSB_AR_BandGroups,
+ .LangID_hdr = WUSB_AR_LangID,
+ .DeviceFriendlyName_hdr = WUSB_AR_DeviceFriendlyName,
+};
+
+/*
+ * Get device's information (CDID) associated to CHID
+ *
+ * The device will return it's information (CDID, name, bandgroups)
+ * associated to the CHID we have set before, or 0 CDID and default
+ * name and bandgroup if no CHID set or unknown.
+ */
+static int cbaf_cdid_get(struct cbaf *cbaf)
+{
+ int result;
+ struct device *dev = &cbaf->usb_iface->dev;
+ struct wusb_cbaf_device_info *di;
+ size_t needed, dev_name_size;
+
+ di = cbaf->buffer;
+ result = usb_control_msg(
+ cbaf->usb_dev, usb_rcvctrlpipe(cbaf->usb_dev, 0),
+ CBAF_REQ_GET_ASSOCIATION_REQUEST,
+ USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0x0200, cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+ di, cbaf->buffer_size, 1000 /* FIXME: arbitrary */);
+ if (result < 0) {
+ dev_err(dev, "Cannot request device information: %d\n", result);
+ goto error_req_di;
+ }
+ needed = result < sizeof(*di)? sizeof(*di) : le32_to_cpu(di->Length);
+ if (result < needed) {
+ dev_err(dev, "Not enough data in DEVICE_INFO reply (%zu vs "
+ "%zu bytes needed)\n", (size_t)result, needed);
+ goto error_bad_di;
+ }
+ cbaf->cdid = di->CDID;
+ dev_name_size = le16_to_cpu(di->DeviceFriendlyName_hdr.len);
+ dev_name_size = dev_name_size > 65 - 1? 65 - 1 : dev_name_size;
+ memcpy(cbaf->device_name, di->DeviceFriendlyName, dev_name_size);
+ cbaf->device_name[dev_name_size] = 0;
+ cbaf->device_band_groups = le16_to_cpu(di->BandGroups);
+ result = 0;
+error_req_di:
+error_bad_di:
+ return result;
+}
+
+/*
+ * Get device information and print it to sysfs
+ *
+ * See cbaf_cdid_get()
+ */
+static ssize_t cbaf_wusb_cdid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t result;
+ struct usb_interface *iface = to_usb_interface(dev);
+ struct cbaf *cbaf = usb_get_intfdata(iface);
+ char pr_cdid[WUSB_CKHDID_STRSIZE];
+
+ result = cbaf_cdid_get(cbaf);
+ if (result < 0) {
+ dev_err(dev, "Cannot read device information: %d\n",
+ (int)result);
+ goto error_get_di;
+ }
+ ckhdid_printf(pr_cdid, sizeof(pr_cdid), &cbaf->cdid);
+ result = scnprintf(buf, PAGE_SIZE,
+ "CDID: %s\nName: %s\nBand_groups: 0x%04x\n",
+ pr_cdid, cbaf->device_name,
+ cbaf->device_band_groups);
+error_get_di:
+ return result;
+}
+static DEVICE_ATTR(wusb_cdid, 0600, cbaf_wusb_cdid_show, NULL);
+
+static const struct wusb_cbaf_cc_data cbaf_cc_data_defaults = {
+ .AssociationTypeId_hdr = WUSB_AR_AssociationTypeId,
+ .AssociationTypeId = cpu_to_le16(AR_TYPE_WUSB),
+ .AssociationSubTypeId_hdr = WUSB_AR_AssociationSubTypeId,
+ .AssociationSubTypeId = cpu_to_le16(AR_TYPE_WUSB_ASSOCIATE),
+ .Length_hdr = WUSB_AR_Length,
+ .Length = cpu_to_le32(sizeof(struct wusb_cbaf_cc_data)),
+ .ConnectionContext_hdr = WUSB_AR_ConnectionContext,
+ .BandGroups_hdr = WUSB_AR_BandGroups,
+};
+
+static const struct wusb_cbaf_cc_data_fail cbaf_cc_data_fail_defaults = {
+ .AssociationTypeId_hdr = WUSB_AR_AssociationTypeId,
+ .AssociationSubTypeId_hdr = WUSB_AR_AssociationSubTypeId,
+ .Length_hdr = WUSB_AR_Length,
+ .AssociationStatus_hdr = WUSB_AR_AssociationStatus,
+};
+
+/*
+ * Send a new CC to the device
+ *
+ * So we update the CK and send the whole thing to the device
+ */
+static int cbaf_cc_upload(struct cbaf *cbaf)
+{
+ int result;
+ struct device *dev = &cbaf->usb_iface->dev;
+ struct wusb_cbaf_cc_data *ccd;
+ char pr_cdid[WUSB_CKHDID_STRSIZE];
+
+ ccd = cbaf->buffer;
+ *ccd = cbaf_cc_data_defaults;
+ ccd->CHID = cbaf->chid;
+ ccd->CDID = cbaf->cdid;
+ ccd->CK = cbaf->ck;
+ ccd->BandGroups = cpu_to_le16(cbaf->host_band_groups);
+ result = usb_control_msg(
+ cbaf->usb_dev, usb_sndctrlpipe(cbaf->usb_dev, 0),
+ CBAF_REQ_SET_ASSOCIATION_RESPONSE,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0x0201, cbaf->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+ ccd, sizeof(*ccd), 1000 /* FIXME: arbitrary */);
+ d_printf(1, dev, "Uploaded CC:\n");
+ ckhdid_printf(pr_cdid, sizeof(pr_cdid), &ccd->CHID);
+ d_printf(1, dev, " CHID %s\n", pr_cdid);
+ ckhdid_printf(pr_cdid, sizeof(pr_cdid), &ccd->CDID);
+ d_printf(1, dev, " CDID %s\n", pr_cdid);
+ ckhdid_printf(pr_cdid, sizeof(pr_cdid), &ccd->CK);
+ d_printf(1, dev, " CK %s\n", pr_cdid);
+ d_printf(1, dev, " bandgroups 0x%04x\n", cbaf->host_band_groups);
+ return result;
+}
+
+/*
+ * Send a new CC to the device
+ *
+ * We take the CDID and CK from user space, the rest from the info we
+ * set with host_info.
+ */
+static ssize_t cbaf_wusb_cc_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ ssize_t result;
+ struct usb_interface *iface = to_usb_interface(dev);
+ struct cbaf *cbaf = usb_get_intfdata(iface);
+
+ result = sscanf(buf,
+ "CDID: %02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx\n"
+ "CK: %02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx\n",
+ &cbaf->cdid.data[0] , &cbaf->cdid.data[1],
+ &cbaf->cdid.data[2] , &cbaf->cdid.data[3],
+ &cbaf->cdid.data[4] , &cbaf->cdid.data[5],
+ &cbaf->cdid.data[6] , &cbaf->cdid.data[7],
+ &cbaf->cdid.data[8] , &cbaf->cdid.data[9],
+ &cbaf->cdid.data[10], &cbaf->cdid.data[11],
+ &cbaf->cdid.data[12], &cbaf->cdid.data[13],
+ &cbaf->cdid.data[14], &cbaf->cdid.data[15],
+
+ &cbaf->ck.data[0] , &cbaf->ck.data[1],
+ &cbaf->ck.data[2] , &cbaf->ck.data[3],
+ &cbaf->ck.data[4] , &cbaf->ck.data[5],
+ &cbaf->ck.data[6] , &cbaf->ck.data[7],
+ &cbaf->ck.data[8] , &cbaf->ck.data[9],
+ &cbaf->ck.data[10], &cbaf->ck.data[11],
+ &cbaf->ck.data[12], &cbaf->ck.data[13],
+ &cbaf->ck.data[14], &cbaf->ck.data[15]);
+ if (result != 32) {
+ dev_err(dev, "Unrecognized CHID/CK (need 32 8-bit "
+ "hex digits, got only %d)\n", (int)result);
+ return -EINVAL;
+ }
+ result = cbaf_cc_upload(cbaf);
+ if (result < 0)
+ dev_err(dev, "Couldn't upload connection context: %d\n",
+ (int)result);
+ else
+ d_printf(1, dev, "Connection context uploaded\n");
+ return result < 0? result : size;
+}
+static DEVICE_ATTR(wusb_cc, 0600, NULL, cbaf_wusb_cc_store);
+
+static struct attribute *cbaf_dev_attrs[] = {
+ &dev_attr_wusb_host_info.attr,
+ &dev_attr_wusb_cdid.attr,
+ &dev_attr_wusb_cc.attr,
+ NULL,
+};
+
+static struct attribute_group cbaf_dev_attr_group = {
+ .name = NULL, /* we want them in the same directory */
+ .attrs = cbaf_dev_attrs,
+};
+
+static int cbaf_probe(struct usb_interface *iface,
+ const struct usb_device_id *id)
+{
+ int result;
+ struct cbaf *cbaf;
+ struct device *dev = &iface->dev;
+
+ result = -ENOMEM;
+ cbaf = kzalloc(sizeof(*cbaf), GFP_KERNEL);
+ if (cbaf == NULL) {
+ dev_err(dev, "Unable to allocate instance\n");
+ goto error_kzalloc;
+ }
+ cbaf->buffer = kmalloc(512, GFP_KERNEL);
+ if (cbaf->buffer == NULL)
+ goto error_kmalloc_buffer;
+ cbaf->buffer_size = 512;
+ cbaf->usb_dev = usb_get_dev(interface_to_usbdev(iface));
+ cbaf->usb_iface = usb_get_intf(iface);
+ result = cbaf_check(cbaf);
+ if (result < 0)
+ goto error_check;
+ result = sysfs_create_group(&dev->kobj, &cbaf_dev_attr_group);
+ if (result < 0) {
+ dev_err(dev, "Can't register sysfs attr group: %d\n", result);
+ goto error_create_group;
+ }
+ usb_set_intfdata(iface, cbaf);
+ d_printf(2, dev, "CBA attached\n");
+ return 0;
+
+error_create_group:
+error_check:
+ kfree(cbaf->buffer);
+error_kmalloc_buffer:
+ kfree(cbaf);
+error_kzalloc:
+ return result;
+}
+
+static void cbaf_disconnect(struct usb_interface *iface)
+{
+ struct cbaf *cbaf = usb_get_intfdata(iface);
+ struct device *dev = &iface->dev;
+ sysfs_remove_group(&dev->kobj, &cbaf_dev_attr_group);
+ usb_set_intfdata(iface, NULL);
+ usb_put_intf(iface);
+ kfree(cbaf->buffer);
+ /* paranoia: clean up crypto keys */
+ memset(cbaf, 0, sizeof(*cbaf));
+ kfree(cbaf);
+ d_printf(1, dev, "CBA detached\n");
+}
+
+static struct usb_device_id cbaf_id_table[] = {
+ { USB_INTERFACE_INFO(0xef, 0x03, 0x01), },
+ { },
+};
+MODULE_DEVICE_TABLE(usb, cbaf_id_table);
+
+static struct usb_driver cbaf_driver = {
+ .name = "wusb-cbaf",
+ .id_table = cbaf_id_table,
+ .probe = cbaf_probe,
+ .disconnect = cbaf_disconnect,
+};
+
+static int __init cbaf_driver_init(void)
+{
+ return usb_register(&cbaf_driver);
+}
+module_init(cbaf_driver_init);
+
+static void __exit cbaf_driver_exit(void)
+{
+ usb_deregister(&cbaf_driver);
+}
+module_exit(cbaf_driver_exit);
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("Wireless USB Cable Based Association");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/wusbcore/crypto.c b/drivers/usb/wusbcore/crypto.c
new file mode 100644
index 000000000000..c36c4389baae
--- /dev/null
+++ b/drivers/usb/wusbcore/crypto.c
@@ -0,0 +1,538 @@
+/*
+ * Ultra Wide Band
+ * AES-128 CCM Encryption
+ *
+ * Copyright (C) 2007 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * We don't do any encryption here; we use the Linux Kernel's AES-128
+ * crypto modules to construct keys and payload blocks in a way
+ * defined by WUSB1.0[6]. Check the erratas, as typos are are patched
+ * there.
+ *
+ * Thanks a zillion to John Keys for his help and clarifications over
+ * the designed-by-a-committee text.
+ *
+ * So the idea is that there is this basic Pseudo-Random-Function
+ * defined in WUSB1.0[6.5] which is the core of everything. It works
+ * by tweaking some blocks, AES crypting them and then xoring
+ * something else with them (this seems to be called CBC(AES) -- can
+ * you tell I know jack about crypto?). So we just funnel it into the
+ * Linux Crypto API.
+ *
+ * We leave a crypto test module so we can verify that vectors match,
+ * every now and then.
+ *
+ * Block size: 16 bytes -- AES seems to do things in 'block sizes'. I
+ * am learning a lot...
+ *
+ * Conveniently, some data structures that need to be
+ * funneled through AES are...16 bytes in size!
+ */
+
+#include <linux/crypto.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/uwb.h>
+#include <linux/usb/wusb.h>
+#include <linux/scatterlist.h>
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+
+/*
+ * Block of data, as understood by AES-CCM
+ *
+ * The code assumes this structure is nothing but a 16 byte array
+ * (packed in a struct to avoid common mess ups that I usually do with
+ * arrays and enforcing type checking).
+ */
+struct aes_ccm_block {
+ u8 data[16];
+} __attribute__((packed));
+
+/*
+ * Counter-mode Blocks (WUSB1.0[6.4])
+ *
+ * According to CCM (or so it seems), for the purpose of calculating
+ * the MIC, the message is broken in N counter-mode blocks, B0, B1,
+ * ... BN.
+ *
+ * B0 contains flags, the CCM nonce and l(m).
+ *
+ * B1 contains l(a), the MAC header, the encryption offset and padding.
+ *
+ * If EO is nonzero, additional blocks are built from payload bytes
+ * until EO is exahusted (FIXME: padding to 16 bytes, I guess). The
+ * padding is not xmitted.
+ */
+
+/* WUSB1.0[T6.4] */
+struct aes_ccm_b0 {
+ u8 flags; /* 0x59, per CCM spec */
+ struct aes_ccm_nonce ccm_nonce;
+ __be16 lm;
+} __attribute__((packed));
+
+/* WUSB1.0[T6.5] */
+struct aes_ccm_b1 {
+ __be16 la;
+ u8 mac_header[10];
+ __le16 eo;
+ u8 security_reserved; /* This is always zero */
+ u8 padding; /* 0 */
+} __attribute__((packed));
+
+/*
+ * Encryption Blocks (WUSB1.0[6.4.4])
+ *
+ * CCM uses Ax blocks to generate a keystream with which the MIC and
+ * the message's payload are encoded. A0 always encrypts/decrypts the
+ * MIC. Ax (x>0) are used for the sucesive payload blocks.
+ *
+ * The x is the counter, and is increased for each block.
+ */
+struct aes_ccm_a {
+ u8 flags; /* 0x01, per CCM spec */
+ struct aes_ccm_nonce ccm_nonce;
+ __be16 counter; /* Value of x */
+} __attribute__((packed));
+
+static void bytewise_xor(void *_bo, const void *_bi1, const void *_bi2,
+ size_t size)
+{
+ u8 *bo = _bo;
+ const u8 *bi1 = _bi1, *bi2 = _bi2;
+ size_t itr;
+ for (itr = 0; itr < size; itr++)
+ bo[itr] = bi1[itr] ^ bi2[itr];
+}
+
+/*
+ * CC-MAC function WUSB1.0[6.5]
+ *
+ * Take a data string and produce the encrypted CBC Counter-mode MIC
+ *
+ * Note the names for most function arguments are made to (more or
+ * less) match those used in the pseudo-function definition given in
+ * WUSB1.0[6.5].
+ *
+ * @tfm_cbc: CBC(AES) blkcipher handle (initialized)
+ *
+ * @tfm_aes: AES cipher handle (initialized)
+ *
+ * @mic: buffer for placing the computed MIC (Message Integrity
+ * Code). This is exactly 8 bytes, and we expect the buffer to
+ * be at least eight bytes in length.
+ *
+ * @key: 128 bit symmetric key
+ *
+ * @n: CCM nonce
+ *
+ * @a: ASCII string, 14 bytes long (I guess zero padded if needed;
+ * we use exactly 14 bytes).
+ *
+ * @b: data stream to be processed; cannot be a global or const local
+ * (will confuse the scatterlists)
+ *
+ * @blen: size of b...
+ *
+ * Still not very clear how this is done, but looks like this: we
+ * create block B0 (as WUSB1.0[6.5] says), then we AES-crypt it with
+ * @key. We bytewise xor B0 with B1 (1) and AES-crypt that. Then we
+ * take the payload and divide it in blocks (16 bytes), xor them with
+ * the previous crypto result (16 bytes) and crypt it, repeat the next
+ * block with the output of the previous one, rinse wash (I guess this
+ * is what AES CBC mode means...but I truly have no idea). So we use
+ * the CBC(AES) blkcipher, that does precisely that. The IV (Initial
+ * Vector) is 16 bytes and is set to zero, so
+ *
+ * See rfc3610. Linux crypto has a CBC implementation, but the
+ * documentation is scarce, to say the least, and the example code is
+ * so intricated that is difficult to understand how things work. Most
+ * of this is guess work -- bite me.
+ *
+ * (1) Created as 6.5 says, again, using as l(a) 'Blen + 14', and
+ * using the 14 bytes of @a to fill up
+ * b1.{mac_header,e0,security_reserved,padding}.
+ *
+ * NOTE: The definiton of l(a) in WUSB1.0[6.5] vs the definition of
+ * l(m) is orthogonal, they bear no relationship, so it is not
+ * in conflict with the parameter's relation that
+ * WUSB1.0[6.4.2]) defines.
+ *
+ * NOTE: WUSB1.0[A.1]: Host Nonce is missing a nibble? (1e); fixed in
+ * first errata released on 2005/07.
+ *
+ * NOTE: we need to clean IV to zero at each invocation to make sure
+ * we start with a fresh empty Initial Vector, so that the CBC
+ * works ok.
+ *
+ * NOTE: blen is not aligned to a block size, we'll pad zeros, that's
+ * what sg[4] is for. Maybe there is a smarter way to do this.
+ */
+static int wusb_ccm_mac(struct crypto_blkcipher *tfm_cbc,
+ struct crypto_cipher *tfm_aes, void *mic,
+ const struct aes_ccm_nonce *n,
+ const struct aes_ccm_label *a, const void *b,
+ size_t blen)
+{
+ int result = 0;
+ struct blkcipher_desc desc;
+ struct aes_ccm_b0 b0;
+ struct aes_ccm_b1 b1;
+ struct aes_ccm_a ax;
+ struct scatterlist sg[4], sg_dst;
+ void *iv, *dst_buf;
+ size_t ivsize, dst_size;
+ const u8 bzero[16] = { 0 };
+ size_t zero_padding;
+
+ d_fnstart(3, NULL, "(tfm_cbc %p, tfm_aes %p, mic %p, "
+ "n %p, a %p, b %p, blen %zu)\n",
+ tfm_cbc, tfm_aes, mic, n, a, b, blen);
+ /*
+ * These checks should be compile time optimized out
+ * ensure @a fills b1's mac_header and following fields
+ */
+ WARN_ON(sizeof(*a) != sizeof(b1) - sizeof(b1.la));
+ WARN_ON(sizeof(b0) != sizeof(struct aes_ccm_block));
+ WARN_ON(sizeof(b1) != sizeof(struct aes_ccm_block));
+ WARN_ON(sizeof(ax) != sizeof(struct aes_ccm_block));
+
+ result = -ENOMEM;
+ zero_padding = sizeof(struct aes_ccm_block)
+ - blen % sizeof(struct aes_ccm_block);
+ zero_padding = blen % sizeof(struct aes_ccm_block);
+ if (zero_padding)
+ zero_padding = sizeof(struct aes_ccm_block) - zero_padding;
+ dst_size = blen + sizeof(b0) + sizeof(b1) + zero_padding;
+ dst_buf = kzalloc(dst_size, GFP_KERNEL);
+ if (dst_buf == NULL) {
+ printk(KERN_ERR "E: can't alloc destination buffer\n");
+ goto error_dst_buf;
+ }
+
+ iv = crypto_blkcipher_crt(tfm_cbc)->iv;
+ ivsize = crypto_blkcipher_ivsize(tfm_cbc);
+ memset(iv, 0, ivsize);
+
+ /* Setup B0 */
+ b0.flags = 0x59; /* Format B0 */
+ b0.ccm_nonce = *n;
+ b0.lm = cpu_to_be16(0); /* WUSB1.0[6.5] sez l(m) is 0 */
+
+ /* Setup B1
+ *
+ * The WUSB spec is anything but clear! WUSB1.0[6.5]
+ * says that to initialize B1 from A with 'l(a) = blen +
+ * 14'--after clarification, it means to use A's contents
+ * for MAC Header, EO, sec reserved and padding.
+ */
+ b1.la = cpu_to_be16(blen + 14);
+ memcpy(&b1.mac_header, a, sizeof(*a));
+
+ d_printf(4, NULL, "I: B0 (%zu bytes)\n", sizeof(b0));
+ d_dump(4, NULL, &b0, sizeof(b0));
+ d_printf(4, NULL, "I: B1 (%zu bytes)\n", sizeof(b1));
+ d_dump(4, NULL, &b1, sizeof(b1));
+ d_printf(4, NULL, "I: B (%zu bytes)\n", blen);
+ d_dump(4, NULL, b, blen);
+ d_printf(4, NULL, "I: B 0-padding (%zu bytes)\n", zero_padding);
+ d_printf(4, NULL, "D: IV before crypto (%zu)\n", ivsize);
+ d_dump(4, NULL, iv, ivsize);
+
+ sg_init_table(sg, ARRAY_SIZE(sg));
+ sg_set_buf(&sg[0], &b0, sizeof(b0));
+ sg_set_buf(&sg[1], &b1, sizeof(b1));
+ sg_set_buf(&sg[2], b, blen);
+ /* 0 if well behaved :) */
+ sg_set_buf(&sg[3], bzero, zero_padding);
+ sg_init_one(&sg_dst, dst_buf, dst_size);
+
+ desc.tfm = tfm_cbc;
+ desc.flags = 0;
+ result = crypto_blkcipher_encrypt(&desc, &sg_dst, sg, dst_size);
+ if (result < 0) {
+ printk(KERN_ERR "E: can't compute CBC-MAC tag (MIC): %d\n",
+ result);
+ goto error_cbc_crypt;
+ }
+ d_printf(4, NULL, "D: MIC tag\n");
+ d_dump(4, NULL, iv, ivsize);
+
+ /* Now we crypt the MIC Tag (*iv) with Ax -- values per WUSB1.0[6.5]
+ * The procedure is to AES crypt the A0 block and XOR the MIC
+ * Tag agains it; we only do the first 8 bytes and place it
+ * directly in the destination buffer.
+ *
+ * POS Crypto API: size is assumed to be AES's block size.
+ * Thanks for documenting it -- tip taken from airo.c
+ */
+ ax.flags = 0x01; /* as per WUSB 1.0 spec */
+ ax.ccm_nonce = *n;
+ ax.counter = 0;
+ crypto_cipher_encrypt_one(tfm_aes, (void *)&ax, (void *)&ax);
+ bytewise_xor(mic, &ax, iv, 8);
+ d_printf(4, NULL, "D: CTR[MIC]\n");
+ d_dump(4, NULL, &ax, 8);
+ d_printf(4, NULL, "D: CCM-MIC tag\n");
+ d_dump(4, NULL, mic, 8);
+ result = 8;
+error_cbc_crypt:
+ kfree(dst_buf);
+error_dst_buf:
+ d_fnend(3, NULL, "(tfm_cbc %p, tfm_aes %p, mic %p, "
+ "n %p, a %p, b %p, blen %zu)\n",
+ tfm_cbc, tfm_aes, mic, n, a, b, blen);
+ return result;
+}
+
+/*
+ * WUSB Pseudo Random Function (WUSB1.0[6.5])
+ *
+ * @b: buffer to the source data; cannot be a global or const local
+ * (will confuse the scatterlists)
+ */
+ssize_t wusb_prf(void *out, size_t out_size,
+ const u8 key[16], const struct aes_ccm_nonce *_n,
+ const struct aes_ccm_label *a,
+ const void *b, size_t blen, size_t len)
+{
+ ssize_t result, bytes = 0, bitr;
+ struct aes_ccm_nonce n = *_n;
+ struct crypto_blkcipher *tfm_cbc;
+ struct crypto_cipher *tfm_aes;
+ u64 sfn = 0;
+ __le64 sfn_le;
+
+ d_fnstart(3, NULL, "(out %p, out_size %zu, key %p, _n %p, "
+ "a %p, b %p, blen %zu, len %zu)\n", out, out_size,
+ key, _n, a, b, blen, len);
+
+ tfm_cbc = crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm_cbc)) {
+ result = PTR_ERR(tfm_cbc);
+ printk(KERN_ERR "E: can't load CBC(AES): %d\n", (int)result);
+ goto error_alloc_cbc;
+ }
+ result = crypto_blkcipher_setkey(tfm_cbc, key, 16);
+ if (result < 0) {
+ printk(KERN_ERR "E: can't set CBC key: %d\n", (int)result);
+ goto error_setkey_cbc;
+ }
+
+ tfm_aes = crypto_alloc_cipher("aes", 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(tfm_aes)) {
+ result = PTR_ERR(tfm_aes);
+ printk(KERN_ERR "E: can't load AES: %d\n", (int)result);
+ goto error_alloc_aes;
+ }
+ result = crypto_cipher_setkey(tfm_aes, key, 16);
+ if (result < 0) {
+ printk(KERN_ERR "E: can't set AES key: %d\n", (int)result);
+ goto error_setkey_aes;
+ }
+
+ for (bitr = 0; bitr < (len + 63) / 64; bitr++) {
+ sfn_le = cpu_to_le64(sfn++);
+ memcpy(&n.sfn, &sfn_le, sizeof(n.sfn)); /* n.sfn++... */
+ result = wusb_ccm_mac(tfm_cbc, tfm_aes, out + bytes,
+ &n, a, b, blen);
+ if (result < 0)
+ goto error_ccm_mac;
+ bytes += result;
+ }
+ result = bytes;
+error_ccm_mac:
+error_setkey_aes:
+ crypto_free_cipher(tfm_aes);
+error_alloc_aes:
+error_setkey_cbc:
+ crypto_free_blkcipher(tfm_cbc);
+error_alloc_cbc:
+ d_fnend(3, NULL, "(out %p, out_size %zu, key %p, _n %p, "
+ "a %p, b %p, blen %zu, len %zu) = %d\n", out, out_size,
+ key, _n, a, b, blen, len, (int)bytes);
+ return result;
+}
+
+/* WUSB1.0[A.2] test vectors */
+static const u8 stv_hsmic_key[16] = {
+ 0x4b, 0x79, 0xa3, 0xcf, 0xe5, 0x53, 0x23, 0x9d,
+ 0xd7, 0xc1, 0x6d, 0x1c, 0x2d, 0xab, 0x6d, 0x3f
+};
+
+static const struct aes_ccm_nonce stv_hsmic_n = {
+ .sfn = { 0 },
+ .tkid = { 0x76, 0x98, 0x01, },
+ .dest_addr = { .data = { 0xbe, 0x00 } },
+ .src_addr = { .data = { 0x76, 0x98 } },
+};
+
+/*
+ * Out-of-band MIC Generation verification code
+ *
+ */
+static int wusb_oob_mic_verify(void)
+{
+ int result;
+ u8 mic[8];
+ /* WUSB1.0[A.2] test vectors
+ *
+ * Need to keep it in the local stack as GCC 4.1.3something
+ * messes up and generates noise.
+ */
+ struct usb_handshake stv_hsmic_hs = {
+ .bMessageNumber = 2,
+ .bStatus = 00,
+ .tTKID = { 0x76, 0x98, 0x01 },
+ .bReserved = 00,
+ .CDID = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+ 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+ 0x3c, 0x3d, 0x3e, 0x3f },
+ .nonce = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
+ 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+ 0x2c, 0x2d, 0x2e, 0x2f },
+ .MIC = { 0x75, 0x6a, 0x97, 0x51, 0x0c, 0x8c,
+ 0x14, 0x7b } ,
+ };
+ size_t hs_size;
+
+ result = wusb_oob_mic(mic, stv_hsmic_key, &stv_hsmic_n, &stv_hsmic_hs);
+ if (result < 0)
+ printk(KERN_ERR "E: WUSB OOB MIC test: failed: %d\n", result);
+ else if (memcmp(stv_hsmic_hs.MIC, mic, sizeof(mic))) {
+ printk(KERN_ERR "E: OOB MIC test: "
+ "mismatch between MIC result and WUSB1.0[A2]\n");
+ hs_size = sizeof(stv_hsmic_hs) - sizeof(stv_hsmic_hs.MIC);
+ printk(KERN_ERR "E: Handshake2 in: (%zu bytes)\n", hs_size);
+ dump_bytes(NULL, &stv_hsmic_hs, hs_size);
+ printk(KERN_ERR "E: CCM Nonce in: (%zu bytes)\n",
+ sizeof(stv_hsmic_n));
+ dump_bytes(NULL, &stv_hsmic_n, sizeof(stv_hsmic_n));
+ printk(KERN_ERR "E: MIC out:\n");
+ dump_bytes(NULL, mic, sizeof(mic));
+ printk(KERN_ERR "E: MIC out (from WUSB1.0[A.2]):\n");
+ dump_bytes(NULL, stv_hsmic_hs.MIC, sizeof(stv_hsmic_hs.MIC));
+ result = -EINVAL;
+ } else
+ result = 0;
+ return result;
+}
+
+/*
+ * Test vectors for Key derivation
+ *
+ * These come from WUSB1.0[6.5.1], the vectors in WUSB1.0[A.1]
+ * (errata corrected in 2005/07).
+ */
+static const u8 stv_key_a1[16] __attribute__ ((__aligned__(4))) = {
+ 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87,
+ 0x78, 0x69, 0x5a, 0x4b, 0x3c, 0x2d, 0x1e, 0x0f
+};
+
+static const struct aes_ccm_nonce stv_keydvt_n_a1 = {
+ .sfn = { 0 },
+ .tkid = { 0x76, 0x98, 0x01, },
+ .dest_addr = { .data = { 0xbe, 0x00 } },
+ .src_addr = { .data = { 0x76, 0x98 } },
+};
+
+static const struct wusb_keydvt_out stv_keydvt_out_a1 = {
+ .kck = {
+ 0x4b, 0x79, 0xa3, 0xcf, 0xe5, 0x53, 0x23, 0x9d,
+ 0xd7, 0xc1, 0x6d, 0x1c, 0x2d, 0xab, 0x6d, 0x3f
+ },
+ .ptk = {
+ 0xc8, 0x70, 0x62, 0x82, 0xb6, 0x7c, 0xe9, 0x06,
+ 0x7b, 0xc5, 0x25, 0x69, 0xf2, 0x36, 0x61, 0x2d
+ }
+};
+
+/*
+ * Performa a test to make sure we match the vectors defined in
+ * WUSB1.0[A.1](Errata2006/12)
+ */
+static int wusb_key_derive_verify(void)
+{
+ int result = 0;
+ struct wusb_keydvt_out keydvt_out;
+ /* These come from WUSB1.0[A.1] + 2006/12 errata
+ * NOTE: can't make this const or global -- somehow it seems
+ * the scatterlists for crypto get confused and we get
+ * bad data. There is no doc on this... */
+ struct wusb_keydvt_in stv_keydvt_in_a1 = {
+ .hnonce = {
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
+ },
+ .dnonce = {
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f
+ }
+ };
+
+ result = wusb_key_derive(&keydvt_out, stv_key_a1, &stv_keydvt_n_a1,
+ &stv_keydvt_in_a1);
+ if (result < 0)
+ printk(KERN_ERR "E: WUSB key derivation test: "
+ "derivation failed: %d\n", result);
+ if (memcmp(&stv_keydvt_out_a1, &keydvt_out, sizeof(keydvt_out))) {
+ printk(KERN_ERR "E: WUSB key derivation test: "
+ "mismatch between key derivation result "
+ "and WUSB1.0[A1] Errata 2006/12\n");
+ printk(KERN_ERR "E: keydvt in: key (%zu bytes)\n",
+ sizeof(stv_key_a1));
+ dump_bytes(NULL, stv_key_a1, sizeof(stv_key_a1));
+ printk(KERN_ERR "E: keydvt in: nonce (%zu bytes)\n",
+ sizeof(stv_keydvt_n_a1));
+ dump_bytes(NULL, &stv_keydvt_n_a1, sizeof(stv_keydvt_n_a1));
+ printk(KERN_ERR "E: keydvt in: hnonce & dnonce (%zu bytes)\n",
+ sizeof(stv_keydvt_in_a1));
+ dump_bytes(NULL, &stv_keydvt_in_a1, sizeof(stv_keydvt_in_a1));
+ printk(KERN_ERR "E: keydvt out: KCK\n");
+ dump_bytes(NULL, &keydvt_out.kck, sizeof(keydvt_out.kck));
+ printk(KERN_ERR "E: keydvt out: PTK\n");
+ dump_bytes(NULL, &keydvt_out.ptk, sizeof(keydvt_out.ptk));
+ result = -EINVAL;
+ } else
+ result = 0;
+ return result;
+}
+
+/*
+ * Initialize crypto system
+ *
+ * FIXME: we do nothing now, other than verifying. Later on we'll
+ * cache the encryption stuff, so that's why we have a separate init.
+ */
+int wusb_crypto_init(void)
+{
+ int result;
+
+ result = wusb_key_derive_verify();
+ if (result < 0)
+ return result;
+ return wusb_oob_mic_verify();
+}
+
+void wusb_crypto_exit(void)
+{
+ /* FIXME: free cached crypto transforms */
+}
diff --git a/drivers/usb/wusbcore/dev-sysfs.c b/drivers/usb/wusbcore/dev-sysfs.c
new file mode 100644
index 000000000000..7d6a614df914
--- /dev/null
+++ b/drivers/usb/wusbcore/dev-sysfs.c
@@ -0,0 +1,199 @@
+/*
+ * WUSB devices
+ * sysfs bindings
+ *
+ * Copyright (C) 2007 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Get them out of the way...
+ */
+
+#include <linux/jiffies.h>
+#include <linux/ctype.h>
+#include <linux/workqueue.h>
+#include "wusbhc.h"
+
+#undef D_LOCAL
+#define D_LOCAL 4
+#include <linux/uwb/debug.h>
+
+static ssize_t wusb_dev_disconnect_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE,
+ "write a non zero value to this file to "
+ "force disconnect\n");
+}
+
+static ssize_t wusb_dev_disconnect_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct usb_device *usb_dev;
+ struct wusbhc *wusbhc;
+ unsigned command;
+ u8 port_idx;
+
+ if (sscanf(buf, "%u\n", &command) != 1)
+ return -EINVAL;
+ if (command == 0)
+ return size;
+ usb_dev = to_usb_device(dev);
+ wusbhc = wusbhc_get_by_usb_dev(usb_dev);
+ if (wusbhc == NULL)
+ return -ENODEV;
+
+ mutex_lock(&wusbhc->mutex);
+ port_idx = wusb_port_no_to_idx(usb_dev->portnum);
+ __wusbhc_dev_disable(wusbhc, port_idx);
+ mutex_unlock(&wusbhc->mutex);
+ wusbhc_put(wusbhc);
+ return size;
+}
+static DEVICE_ATTR(wusb_dev_disconnect, 0644, wusb_dev_disconnect_show,
+ wusb_dev_disconnect_store);
+
+static ssize_t wusb_cdid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t result;
+ struct wusb_dev *wusb_dev;
+
+ wusb_dev = wusb_dev_get_by_usb_dev(to_usb_device(dev));
+ if (wusb_dev == NULL)
+ return -ENODEV;
+ result = ckhdid_printf(buf, PAGE_SIZE, &wusb_dev->cdid);
+ wusb_dev_put(wusb_dev);
+ return result;
+}
+static DEVICE_ATTR(wusb_cdid, 0444, wusb_cdid_show, NULL);
+
+static ssize_t wusb_dev_cc_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE,
+ "Write\n"
+ "\n"
+ "CHID: [16 hex digits]\n"
+ "CDID: [16 hex digits]\n"
+ "CK: [16 hex digits]\n"
+ "\n"
+ "to this file to force a 4way handshake negotiation\n"
+ "[will renew pair wise and group wise key if "
+ "succesful].\n");
+}
+
+static ssize_t wusb_dev_cc_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ int result;
+ struct usb_device *usb_dev;
+ struct wusbhc *wusbhc;
+ struct wusb_ckhdid chid, cdid, ck;
+
+ result = sscanf(buf,
+ "CHID: %02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx\n"
+ "CDID: %02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx\n"
+ "CK: %02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx\n",
+ &chid.data[0] , &chid.data[1],
+ &chid.data[2] , &chid.data[3],
+ &chid.data[4] , &chid.data[5],
+ &chid.data[6] , &chid.data[7],
+ &chid.data[8] , &chid.data[9],
+ &chid.data[10], &chid.data[11],
+ &chid.data[12], &chid.data[13],
+ &chid.data[14], &chid.data[15],
+
+ &cdid.data[0] , &cdid.data[1],
+ &cdid.data[2] , &cdid.data[3],
+ &cdid.data[4] , &cdid.data[5],
+ &cdid.data[6] , &cdid.data[7],
+ &cdid.data[8] , &cdid.data[9],
+ &cdid.data[10], &cdid.data[11],
+ &cdid.data[12], &cdid.data[13],
+ &cdid.data[14], &cdid.data[15],
+
+ &ck.data[0] , &ck.data[1],
+ &ck.data[2] , &ck.data[3],
+ &ck.data[4] , &ck.data[5],
+ &ck.data[6] , &ck.data[7],
+ &ck.data[8] , &ck.data[9],
+ &ck.data[10], &ck.data[11],
+ &ck.data[12], &ck.data[13],
+ &ck.data[14], &ck.data[15]);
+ if (result != 48) {
+ dev_err(dev, "Unrecognized CHID/CDID/CK (need 48 8-bit "
+ "hex digits, got only %d)\n", result);
+ return -EINVAL;
+ }
+
+ usb_dev = to_usb_device(dev);
+ wusbhc = wusbhc_get_by_usb_dev(usb_dev);
+ if (wusbhc == NULL)
+ return -ENODEV;
+ result = wusb_dev_4way_handshake(wusbhc, usb_dev->wusb_dev,
+ &chid, &cdid, &ck);
+ memset(&chid, 0, sizeof(chid));
+ memset(&cdid, 0, sizeof(cdid));
+ memset(&ck, 0, sizeof(ck));
+ wusbhc_put(wusbhc);
+ return result < 0? result : size;
+}
+static DEVICE_ATTR(wusb_dev_cc, 0644, wusb_dev_cc_show, wusb_dev_cc_store);
+
+static struct attribute *wusb_dev_attrs[] = {
+ &dev_attr_wusb_dev_disconnect.attr,
+ &dev_attr_wusb_cdid.attr,
+ &dev_attr_wusb_dev_cc.attr,
+ NULL,
+};
+
+static struct attribute_group wusb_dev_attr_group = {
+ .name = NULL, /* we want them in the same directory */
+ .attrs = wusb_dev_attrs,
+};
+
+int wusb_dev_sysfs_add(struct wusbhc *wusbhc, struct usb_device *usb_dev,
+ struct wusb_dev *wusb_dev)
+{
+ int result = sysfs_create_group(&usb_dev->dev.kobj,
+ &wusb_dev_attr_group);
+ struct device *dev = &usb_dev->dev;
+ if (result < 0)
+ dev_err(dev, "Cannot register WUSB-dev attributes: %d\n",
+ result);
+ return result;
+}
+
+void wusb_dev_sysfs_rm(struct wusb_dev *wusb_dev)
+{
+ struct usb_device *usb_dev = wusb_dev->usb_dev;
+ if (usb_dev)
+ sysfs_remove_group(&usb_dev->dev.kobj, &wusb_dev_attr_group);
+}
diff --git a/drivers/usb/wusbcore/devconnect.c b/drivers/usb/wusbcore/devconnect.c
new file mode 100644
index 000000000000..386db5adcafa
--- /dev/null
+++ b/drivers/usb/wusbcore/devconnect.c
@@ -0,0 +1,1319 @@
+/*
+ * WUSB Wire Adapter: Control/Data Streaming Interface (WUSB[8])
+ * Device Connect handling
+ *
+ * Copyright (C) 2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ * FIXME: this file needs to be broken up, it's grown too big
+ *
+ *
+ * WUSB1.0[7.1, 7.5.1, ]
+ *
+ * WUSB device connection is kind of messy. Some background:
+ *
+ * When a device wants to connect it scans the UWB radio channels
+ * looking for a WUSB Channel; a WUSB channel is defined by MMCs
+ * (Micro Managed Commands or something like that) [see
+ * Design-overview for more on this] .
+ *
+ * So, device scans the radio, finds MMCs and thus a host and checks
+ * when the next DNTS is. It sends a Device Notification Connect
+ * (DN_Connect); the host picks it up (through nep.c and notif.c, ends
+ * up in wusb_devconnect_ack(), which creates a wusb_dev structure in
+ * wusbhc->port[port_number].wusb_dev), assigns an unauth address
+ * to the device (this means from 0x80 to 0xfe) and sends, in the MMC
+ * a Connect Ack Information Element (ConnAck IE).
+ *
+ * So now the device now has a WUSB address. From now on, we use
+ * that to talk to it in the RPipes.
+ *
+ * ASSUMPTIONS:
+ *
+ * - We use the the as device address the port number where it is
+ * connected (port 0 doesn't exist). For unauth, it is 128 + that.
+ *
+ * ROADMAP:
+ *
+ * This file contains the logic for doing that--entry points:
+ *
+ * wusb_devconnect_ack() Ack a device until _acked() called.
+ * Called by notif.c:wusb_handle_dn_connect()
+ * when a DN_Connect is received.
+ *
+ * wusbhc_devconnect_auth() Called by rh.c:wusbhc_rh_port_reset() when
+ * doing the device connect sequence.
+ *
+ * wusb_devconnect_acked() Ack done, release resources.
+ *
+ * wusb_handle_dn_alive() Called by notif.c:wusb_handle_dn()
+ * for processing a DN_Alive pong from a device.
+ *
+ * wusb_handle_dn_disconnect()Called by notif.c:wusb_handle_dn() to
+ * process a disconenct request from a
+ * device.
+ *
+ * wusb_dev_reset() Called by rh.c:wusbhc_rh_port_reset() when
+ * resetting a device.
+ *
+ * __wusb_dev_disable() Called by rh.c:wusbhc_rh_clear_port_feat() when
+ * disabling a port.
+ *
+ * wusb_devconnect_create() Called when creating the host by
+ * lc.c:wusbhc_create().
+ *
+ * wusb_devconnect_destroy() Cleanup called removing the host. Called
+ * by lc.c:wusbhc_destroy().
+ *
+ * Each Wireless USB host maintains a list of DN_Connect requests
+ * (actually we maintain a list of pending Connect Acks, the
+ * wusbhc->ca_list).
+ *
+ * LIFE CYCLE OF port->wusb_dev
+ *
+ * Before the @wusbhc structure put()s the reference it owns for
+ * port->wusb_dev [and clean the wusb_dev pointer], it needs to
+ * lock @wusbhc->mutex.
+ */
+
+#include <linux/jiffies.h>
+#include <linux/ctype.h>
+#include <linux/workqueue.h>
+#include "wusbhc.h"
+
+#undef D_LOCAL
+#define D_LOCAL 1
+#include <linux/uwb/debug.h>
+
+static void wusbhc_devconnect_acked_work(struct work_struct *work);
+
+static void wusb_dev_free(struct wusb_dev *wusb_dev)
+{
+ if (wusb_dev) {
+ kfree(wusb_dev->set_gtk_req);
+ usb_free_urb(wusb_dev->set_gtk_urb);
+ kfree(wusb_dev);
+ }
+}
+
+static struct wusb_dev *wusb_dev_alloc(struct wusbhc *wusbhc)
+{
+ struct wusb_dev *wusb_dev;
+ struct urb *urb;
+ struct usb_ctrlrequest *req;
+
+ wusb_dev = kzalloc(sizeof(*wusb_dev), GFP_KERNEL);
+ if (wusb_dev == NULL)
+ goto err;
+
+ wusb_dev->wusbhc = wusbhc;
+
+ INIT_WORK(&wusb_dev->devconnect_acked_work, wusbhc_devconnect_acked_work);
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (urb == NULL)
+ goto err;
+
+ req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+ if (req == NULL)
+ goto err;
+
+ req->bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
+ req->bRequest = USB_REQ_SET_DESCRIPTOR;
+ req->wValue = cpu_to_le16(USB_DT_KEY << 8 | wusbhc->gtk_index.value);
+ req->wIndex = 0;
+ req->wLength = cpu_to_le16(wusbhc->gtk.descr.bLength);
+
+ wusb_dev->set_gtk_urb = urb;
+ wusb_dev->set_gtk_req = req;
+
+ return wusb_dev;
+err:
+ wusb_dev_free(wusb_dev);
+ return NULL;
+}
+
+
+/*
+ * Using the Connect-Ack list, fill out the @wusbhc Connect-Ack WUSB IE
+ * properly so that it can be added to the MMC.
+ *
+ * We just get the @wusbhc->ca_list and fill out the first four ones or
+ * less (per-spec WUSB1.0[7.5, before T7-38). If the ConnectAck WUSB
+ * IE is not allocated, we alloc it.
+ *
+ * @wusbhc->mutex must be taken
+ */
+static void wusbhc_fill_cack_ie(struct wusbhc *wusbhc)
+{
+ unsigned cnt;
+ struct wusb_dev *dev_itr;
+ struct wuie_connect_ack *cack_ie;
+
+ cack_ie = &wusbhc->cack_ie;
+ cnt = 0;
+ list_for_each_entry(dev_itr, &wusbhc->cack_list, cack_node) {
+ cack_ie->blk[cnt].CDID = dev_itr->cdid;
+ cack_ie->blk[cnt].bDeviceAddress = dev_itr->addr;
+ if (++cnt >= WUIE_ELT_MAX)
+ break;
+ }
+ cack_ie->hdr.bLength = sizeof(cack_ie->hdr)
+ + cnt * sizeof(cack_ie->blk[0]);
+}
+
+/*
+ * Register a new device that wants to connect
+ *
+ * A new device wants to connect, so we add it to the Connect-Ack
+ * list. We give it an address in the unauthorized range (bit 8 set);
+ * user space will have to drive authorization further on.
+ *
+ * @dev_addr: address to use for the device (which is also the port
+ * number).
+ *
+ * @wusbhc->mutex must be taken
+ */
+static struct wusb_dev *wusbhc_cack_add(struct wusbhc *wusbhc,
+ struct wusb_dn_connect *dnc,
+ const char *pr_cdid, u8 port_idx)
+{
+ struct device *dev = wusbhc->dev;
+ struct wusb_dev *wusb_dev;
+ int new_connection = dnc->new_connection;
+ u8 dev_addr;
+ int result;
+
+ d_fnstart(3, dev, "(wusbhc %p port_idx %d)\n", wusbhc, port_idx);
+ /* Is it registered already? */
+ list_for_each_entry(wusb_dev, &wusbhc->cack_list, cack_node)
+ if (!memcmp(&wusb_dev->cdid, &dnc->CDID,
+ sizeof(wusb_dev->cdid)))
+ return wusb_dev;
+ /* We don't have it, create an entry, register it */
+ wusb_dev = wusb_dev_alloc(wusbhc);
+ if (wusb_dev == NULL) {
+ if (printk_ratelimit())
+ dev_err(dev, "DN CONNECT: no memory to process %s's %s "
+ "request\n", pr_cdid,
+ new_connection? "connect" : "reconnect");
+ return NULL;
+ }
+ wusb_dev_init(wusb_dev);
+ wusb_dev->cdid = dnc->CDID;
+ wusb_dev->port_idx = port_idx;
+
+ /*
+ * Devices are always available within the cluster reservation
+ * and since the hardware will take the intersection of the
+ * per-device availability and the cluster reservation, the
+ * per-device availability can simply be set to always
+ * available.
+ */
+ bitmap_fill(wusb_dev->availability.bm, UWB_NUM_MAS);
+
+ /* FIXME: handle reconnects instead of assuming connects are
+ always new. */
+ if (1 && new_connection == 0)
+ new_connection = 1;
+ if (new_connection) {
+ dev_addr = (port_idx + 2) | WUSB_DEV_ADDR_UNAUTH;
+
+ dev_info(dev, "Connecting new WUSB device to address %u, "
+ "port %u\n", dev_addr, port_idx);
+
+ result = wusb_set_dev_addr(wusbhc, wusb_dev, dev_addr);
+ if (result < 0)
+ return NULL;
+ }
+ wusb_dev->entry_ts = jiffies;
+ list_add_tail(&wusb_dev->cack_node, &wusbhc->cack_list);
+ wusbhc->cack_count++;
+ wusbhc_fill_cack_ie(wusbhc);
+ d_fnend(3, dev, "(wusbhc %p port_idx %d)\n", wusbhc, port_idx);
+ return wusb_dev;
+}
+
+/*
+ * Remove a Connect-Ack context entry from the HCs view
+ *
+ * @wusbhc->mutex must be taken
+ */
+static void wusbhc_cack_rm(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
+{
+ struct device *dev = wusbhc->dev;
+ d_fnstart(3, dev, "(wusbhc %p wusb_dev %p)\n", wusbhc, wusb_dev);
+ list_del_init(&wusb_dev->cack_node);
+ wusbhc->cack_count--;
+ wusbhc_fill_cack_ie(wusbhc);
+ d_fnend(3, dev, "(wusbhc %p wusb_dev %p) = void\n", wusbhc, wusb_dev);
+}
+
+/*
+ * @wusbhc->mutex must be taken */
+static
+void wusbhc_devconnect_acked(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
+{
+ struct device *dev = wusbhc->dev;
+ d_fnstart(3, dev, "(wusbhc %p wusb_dev %p)\n", wusbhc, wusb_dev);
+ wusbhc_cack_rm(wusbhc, wusb_dev);
+ if (wusbhc->cack_count)
+ wusbhc_mmcie_set(wusbhc, 0, 0, &wusbhc->cack_ie.hdr);
+ else
+ wusbhc_mmcie_rm(wusbhc, &wusbhc->cack_ie.hdr);
+ d_fnend(3, dev, "(wusbhc %p wusb_dev %p) = void\n", wusbhc, wusb_dev);
+}
+
+static void wusbhc_devconnect_acked_work(struct work_struct *work)
+{
+ struct wusb_dev *wusb_dev = container_of(work, struct wusb_dev,
+ devconnect_acked_work);
+ struct wusbhc *wusbhc = wusb_dev->wusbhc;
+
+ mutex_lock(&wusbhc->mutex);
+ wusbhc_devconnect_acked(wusbhc, wusb_dev);
+ mutex_unlock(&wusbhc->mutex);
+}
+
+/*
+ * Ack a device for connection
+ *
+ * FIXME: docs
+ *
+ * @pr_cdid: Printable CDID...hex Use @dnc->cdid for the real deal.
+ *
+ * So we get the connect ack IE (may have been allocated already),
+ * find an empty connect block, an empty virtual port, create an
+ * address with it (see below), make it an unauth addr [bit 7 set] and
+ * set the MMC.
+ *
+ * Addresses: because WUSB hosts have no downstream hubs, we can do a
+ * 1:1 mapping between 'port number' and device
+ * address. This simplifies many things, as during this
+ * initial connect phase the USB stack has no knoledge of
+ * the device and hasn't assigned an address yet--we know
+ * USB's choose_address() will use the same euristics we
+ * use here, so we can assume which address will be assigned.
+ *
+ * USB stack always assigns address 1 to the root hub, so
+ * to the port number we add 2 (thus virtual port #0 is
+ * addr #2).
+ *
+ * @wusbhc shall be referenced
+ */
+static
+void wusbhc_devconnect_ack(struct wusbhc *wusbhc, struct wusb_dn_connect *dnc,
+ const char *pr_cdid)
+{
+ int result;
+ struct device *dev = wusbhc->dev;
+ struct wusb_dev *wusb_dev;
+ struct wusb_port *port;
+ unsigned idx, devnum;
+
+ d_fnstart(3, dev, "(%p, %p, %s)\n", wusbhc, dnc, pr_cdid);
+ mutex_lock(&wusbhc->mutex);
+
+ /* Check we are not handling it already */
+ for (idx = 0; idx < wusbhc->ports_max; idx++) {
+ port = wusb_port_by_idx(wusbhc, idx);
+ if (port->wusb_dev
+ && !memcmp(&dnc->CDID, &port->wusb_dev->cdid,
+ sizeof(dnc->CDID))) {
+ if (printk_ratelimit())
+ dev_err(dev, "Already handling dev %s "
+ " (it might be slow)\n", pr_cdid);
+ goto error_unlock;
+ }
+ }
+ /* Look up those fake ports we have for a free one */
+ for (idx = 0; idx < wusbhc->ports_max; idx++) {
+ port = wusb_port_by_idx(wusbhc, idx);
+ if (port->power && port->connection == 0)
+ break;
+ }
+ if (idx >= wusbhc->ports_max) {
+ dev_err(dev, "Host controller can't connect more devices "
+ "(%u already connected); device %s rejected\n",
+ wusbhc->ports_max, pr_cdid);
+ /* NOTE: we could send a WUIE_Disconnect here, but we haven't
+ * event acked, so the device will eventually timeout the
+ * connection, right? */
+ goto error_unlock;
+ }
+
+ devnum = idx + 2;
+
+ /* Make sure we are using no crypto on that "virtual port" */
+ wusbhc->set_ptk(wusbhc, idx, 0, NULL, 0);
+
+ /* Grab a filled in Connect-Ack context, fill out the
+ * Connect-Ack Wireless USB IE, set the MMC */
+ wusb_dev = wusbhc_cack_add(wusbhc, dnc, pr_cdid, idx);
+ if (wusb_dev == NULL)
+ goto error_unlock;
+ result = wusbhc_mmcie_set(wusbhc, 0, 0, &wusbhc->cack_ie.hdr);
+ if (result < 0)
+ goto error_unlock;
+ /* Give the device at least 2ms (WUSB1.0[7.5.1p3]), let's do
+ * three for a good measure */
+ msleep(3);
+ port->wusb_dev = wusb_dev;
+ port->connection = 1;
+ port->c_connection = 1;
+ port->reset_count = 0;
+ /* Now the port status changed to connected; khubd will
+ * pick the change up and try to reset the port to bring it to
+ * the enabled state--so this process returns up to the stack
+ * and it calls back into wusbhc_rh_port_reset() who will call
+ * devconnect_auth().
+ */
+error_unlock:
+ mutex_unlock(&wusbhc->mutex);
+ d_fnend(3, dev, "(%p, %p, %s) = void\n", wusbhc, dnc, pr_cdid);
+ return;
+
+}
+
+/*
+ * Disconnect a Wireless USB device from its fake port
+ *
+ * Marks the port as disconnected so that khubd can pick up the change
+ * and drops our knowledge about the device.
+ *
+ * Assumes there is a device connected
+ *
+ * @port_index: zero based port number
+ *
+ * NOTE: @wusbhc->mutex is locked
+ *
+ * WARNING: From here it is not very safe to access anything hanging off
+ * wusb_dev
+ */
+static void __wusbhc_dev_disconnect(struct wusbhc *wusbhc,
+ struct wusb_port *port)
+{
+ struct device *dev = wusbhc->dev;
+ struct wusb_dev *wusb_dev = port->wusb_dev;
+
+ d_fnstart(3, dev, "(wusbhc %p, port %p)\n", wusbhc, port);
+ port->connection = 0;
+ port->enable = 0;
+ port->suspend = 0;
+ port->reset = 0;
+ port->low_speed = 0;
+ port->high_speed = 0;
+ port->c_connection = 1;
+ port->c_enable = 1;
+ if (wusb_dev) {
+ if (!list_empty(&wusb_dev->cack_node))
+ list_del_init(&wusb_dev->cack_node);
+ /* For the one in cack_add() */
+ wusb_dev_put(wusb_dev);
+ }
+ port->wusb_dev = NULL;
+ /* don't reset the reset_count to zero or wusbhc_rh_port_reset will get
+ * confused! We only reset to zero when we connect a new device.
+ */
+
+ /* After a device disconnects, change the GTK (see [WUSB]
+ * section 6.2.11.2). */
+ wusbhc_gtk_rekey(wusbhc);
+
+ d_fnend(3, dev, "(wusbhc %p, port %p) = void\n", wusbhc, port);
+ /* The Wireless USB part has forgotten about the device already; now
+ * khubd's timer will pick up the disconnection and remove the USB
+ * device from the system
+ */
+}
+
+/*
+ * Authenticate a device into the WUSB Cluster
+ *
+ * Called from the Root Hub code (rh.c:wusbhc_rh_port_reset()) when
+ * asking for a reset on a port that is not enabled (ie: first connect
+ * on the port).
+ *
+ * Performs the 4way handshake to allow the device to comunicate w/ the
+ * WUSB Cluster securely; once done, issue a request to the device for
+ * it to change to address 0.
+ *
+ * This mimics the reset step of Wired USB that once resetting a
+ * device, leaves the port in enabled state and the dev with the
+ * default address (0).
+ *
+ * WUSB1.0[7.1.2]
+ *
+ * @port_idx: port where the change happened--This is the index into
+ * the wusbhc port array, not the USB port number.
+ */
+int wusbhc_devconnect_auth(struct wusbhc *wusbhc, u8 port_idx)
+{
+ struct device *dev = wusbhc->dev;
+ struct wusb_port *port = wusb_port_by_idx(wusbhc, port_idx);
+
+ d_fnstart(3, dev, "(%p, %u)\n", wusbhc, port_idx);
+ port->reset = 0;
+ port->c_reset = 1;
+ port->enable = 1;
+ port->c_enable = 1;
+ d_fnend(3, dev, "(%p, %u) = 0\n", wusbhc, port_idx);
+ return 0;
+}
+
+/*
+ * Refresh the list of keep alives to emit in the MMC
+ *
+ * Some devices don't respond to keep alives unless they've been
+ * authenticated, so skip unauthenticated devices.
+ *
+ * We only publish the first four devices that have a coming timeout
+ * condition. Then when we are done processing those, we go for the
+ * next ones. We ignore the ones that have timed out already (they'll
+ * be purged).
+ *
+ * This might cause the first devices to timeout the last devices in
+ * the port array...FIXME: come up with a better algorithm?
+ *
+ * Note we can't do much about MMC's ops errors; we hope next refresh
+ * will kind of handle it.
+ *
+ * NOTE: @wusbhc->mutex is locked
+ */
+static void __wusbhc_keep_alive(struct wusbhc *wusbhc)
+{
+ int result;
+ struct device *dev = wusbhc->dev;
+ unsigned cnt;
+ struct wusb_dev *wusb_dev;
+ struct wusb_port *wusb_port;
+ struct wuie_keep_alive *ie = &wusbhc->keep_alive_ie;
+ unsigned keep_alives, old_keep_alives;
+
+ d_fnstart(5, dev, "(wusbhc %p)\n", wusbhc);
+ old_keep_alives = ie->hdr.bLength - sizeof(ie->hdr);
+ keep_alives = 0;
+ for (cnt = 0;
+ keep_alives <= WUIE_ELT_MAX && cnt < wusbhc->ports_max;
+ cnt++) {
+ unsigned tt = msecs_to_jiffies(wusbhc->trust_timeout);
+
+ wusb_port = wusb_port_by_idx(wusbhc, cnt);
+ wusb_dev = wusb_port->wusb_dev;
+
+ if (wusb_dev == NULL)
+ continue;
+ if (wusb_dev->usb_dev == NULL || !wusb_dev->usb_dev->authenticated)
+ continue;
+
+ if (time_after(jiffies, wusb_dev->entry_ts + tt)) {
+ dev_err(dev, "KEEPALIVE: device %u timed out\n",
+ wusb_dev->addr);
+ __wusbhc_dev_disconnect(wusbhc, wusb_port);
+ } else if (time_after(jiffies, wusb_dev->entry_ts + tt/2)) {
+ /* Approaching timeout cut out, need to refresh */
+ ie->bDeviceAddress[keep_alives++] = wusb_dev->addr;
+ }
+ }
+ if (keep_alives & 0x1) /* pad to even number ([WUSB] section 7.5.9) */
+ ie->bDeviceAddress[keep_alives++] = 0x7f;
+ ie->hdr.bLength = sizeof(ie->hdr) +
+ keep_alives*sizeof(ie->bDeviceAddress[0]);
+ if (keep_alives > 0) {
+ result = wusbhc_mmcie_set(wusbhc, 10, 5, &ie->hdr);
+ if (result < 0 && printk_ratelimit())
+ dev_err(dev, "KEEPALIVE: can't set MMC: %d\n", result);
+ } else if (old_keep_alives != 0)
+ wusbhc_mmcie_rm(wusbhc, &ie->hdr);
+ d_fnend(5, dev, "(wusbhc %p) = void\n", wusbhc);
+}
+
+/*
+ * Do a run through all devices checking for timeouts
+ */
+static void wusbhc_keep_alive_run(struct work_struct *ws)
+{
+ struct delayed_work *dw =
+ container_of(ws, struct delayed_work, work);
+ struct wusbhc *wusbhc =
+ container_of(dw, struct wusbhc, keep_alive_timer);
+
+ d_fnstart(5, wusbhc->dev, "(wusbhc %p)\n", wusbhc);
+ if (wusbhc->active) {
+ mutex_lock(&wusbhc->mutex);
+ __wusbhc_keep_alive(wusbhc);
+ mutex_unlock(&wusbhc->mutex);
+ queue_delayed_work(wusbd, &wusbhc->keep_alive_timer,
+ (wusbhc->trust_timeout * CONFIG_HZ)/1000/2);
+ }
+ d_fnend(5, wusbhc->dev, "(wusbhc %p) = void\n", wusbhc);
+ return;
+}
+
+/*
+ * Find the wusb_dev from its device address.
+ *
+ * The device can be found directly from the address (see
+ * wusb_cack_add() for where the device address is set to port_idx
+ * +2), except when the address is zero.
+ */
+static struct wusb_dev *wusbhc_find_dev_by_addr(struct wusbhc *wusbhc, u8 addr)
+{
+ int p;
+
+ if (addr == 0xff) /* unconnected */
+ return NULL;
+
+ if (addr > 0) {
+ int port = (addr & ~0x80) - 2;
+ if (port < 0 || port >= wusbhc->ports_max)
+ return NULL;
+ return wusb_port_by_idx(wusbhc, port)->wusb_dev;
+ }
+
+ /* Look for the device with address 0. */
+ for (p = 0; p < wusbhc->ports_max; p++) {
+ struct wusb_dev *wusb_dev = wusb_port_by_idx(wusbhc, p)->wusb_dev;
+ if (wusb_dev && wusb_dev->addr == addr)
+ return wusb_dev;
+ }
+ return NULL;
+}
+
+/*
+ * Handle a DN_Alive notification (WUSB1.0[7.6.1])
+ *
+ * This just updates the device activity timestamp and then refreshes
+ * the keep alive IE.
+ *
+ * @wusbhc shall be referenced and unlocked
+ */
+static void wusbhc_handle_dn_alive(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
+{
+ struct device *dev = wusbhc->dev;
+
+ d_printf(2, dev, "DN ALIVE: device 0x%02x pong\n", wusb_dev->addr);
+
+ mutex_lock(&wusbhc->mutex);
+ wusb_dev->entry_ts = jiffies;
+ __wusbhc_keep_alive(wusbhc);
+ mutex_unlock(&wusbhc->mutex);
+}
+
+/*
+ * Handle a DN_Connect notification (WUSB1.0[7.6.1])
+ *
+ * @wusbhc
+ * @pkt_hdr
+ * @size: Size of the buffer where the notification resides; if the
+ * notification data suggests there should be more data than
+ * available, an error will be signaled and the whole buffer
+ * consumed.
+ *
+ * @wusbhc->mutex shall be held
+ */
+static void wusbhc_handle_dn_connect(struct wusbhc *wusbhc,
+ struct wusb_dn_hdr *dn_hdr,
+ size_t size)
+{
+ struct device *dev = wusbhc->dev;
+ struct wusb_dn_connect *dnc;
+ char pr_cdid[WUSB_CKHDID_STRSIZE];
+ static const char *beacon_behaviour[] = {
+ "reserved",
+ "self-beacon",
+ "directed-beacon",
+ "no-beacon"
+ };
+
+ d_fnstart(3, dev, "(%p, %p, %zu)\n", wusbhc, dn_hdr, size);
+ if (size < sizeof(*dnc)) {
+ dev_err(dev, "DN CONNECT: short notification (%zu < %zu)\n",
+ size, sizeof(*dnc));
+ goto out;
+ }
+
+ dnc = container_of(dn_hdr, struct wusb_dn_connect, hdr);
+ dnc->bmAttributes = le16_to_cpu(dnc->bmAttributes_le);
+ ckhdid_printf(pr_cdid, sizeof(pr_cdid), &dnc->CDID);
+ dev_info(dev, "DN CONNECT: device %s @ %x (%s) wants to %s\n",
+ pr_cdid,
+ dnc->prev_dev_addr, beacon_behaviour[dnc->beacon_behaviour],
+ dnc->new_connection? "connect" : "reconnect");
+ /* ACK the connect */
+ wusbhc_devconnect_ack(wusbhc, dnc, pr_cdid);
+out:
+ d_fnend(3, dev, "(%p, %p, %zu) = void\n",
+ wusbhc, dn_hdr, size);
+ return;
+}
+
+/*
+ * Handle a DN_Disconnect notification (WUSB1.0[7.6.1])
+ *
+ * Device is going down -- do the disconnect.
+ *
+ * @wusbhc shall be referenced and unlocked
+ */
+static void wusbhc_handle_dn_disconnect(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
+{
+ struct device *dev = wusbhc->dev;
+
+ dev_info(dev, "DN DISCONNECT: device 0x%02x going down\n", wusb_dev->addr);
+
+ mutex_lock(&wusbhc->mutex);
+ __wusbhc_dev_disconnect(wusbhc, wusb_port_by_idx(wusbhc, wusb_dev->port_idx));
+ mutex_unlock(&wusbhc->mutex);
+}
+
+/*
+ * Reset a WUSB device on a HWA
+ *
+ * @wusbhc
+ * @port_idx Index of the port where the device is
+ *
+ * In Wireless USB, a reset is more or less equivalent to a full
+ * disconnect; so we just do a full disconnect and send the device a
+ * Device Reset IE (WUSB1.0[7.5.11]) giving it a few millisecs (6 MMCs).
+ *
+ * @wusbhc should be refcounted and unlocked
+ */
+int wusbhc_dev_reset(struct wusbhc *wusbhc, u8 port_idx)
+{
+ int result;
+ struct device *dev = wusbhc->dev;
+ struct wusb_dev *wusb_dev;
+ struct wuie_reset *ie;
+
+ d_fnstart(3, dev, "(%p, %u)\n", wusbhc, port_idx);
+ mutex_lock(&wusbhc->mutex);
+ result = 0;
+ wusb_dev = wusb_port_by_idx(wusbhc, port_idx)->wusb_dev;
+ if (wusb_dev == NULL) {
+ /* reset no device? ignore */
+ dev_dbg(dev, "RESET: no device at port %u, ignoring\n",
+ port_idx);
+ goto error_unlock;
+ }
+ result = -ENOMEM;
+ ie = kzalloc(sizeof(*ie), GFP_KERNEL);
+ if (ie == NULL)
+ goto error_unlock;
+ ie->hdr.bLength = sizeof(ie->hdr) + sizeof(ie->CDID);
+ ie->hdr.bIEIdentifier = WUIE_ID_RESET_DEVICE;
+ ie->CDID = wusb_dev->cdid;
+ result = wusbhc_mmcie_set(wusbhc, 0xff, 6, &ie->hdr);
+ if (result < 0) {
+ dev_err(dev, "RESET: cant's set MMC: %d\n", result);
+ goto error_kfree;
+ }
+ __wusbhc_dev_disconnect(wusbhc, wusb_port_by_idx(wusbhc, port_idx));
+
+ /* 120ms, hopefully 6 MMCs (FIXME) */
+ msleep(120);
+ wusbhc_mmcie_rm(wusbhc, &ie->hdr);
+error_kfree:
+ kfree(ie);
+error_unlock:
+ mutex_unlock(&wusbhc->mutex);
+ d_fnend(3, dev, "(%p, %u) = %d\n", wusbhc, port_idx, result);
+ return result;
+}
+
+/*
+ * Handle a Device Notification coming a host
+ *
+ * The Device Notification comes from a host (HWA, DWA or WHCI)
+ * wrapped in a set of headers. Somebody else has peeled off those
+ * headers for us and we just get one Device Notifications.
+ *
+ * Invalid DNs (e.g., too short) are discarded.
+ *
+ * @wusbhc shall be referenced
+ *
+ * FIXMES:
+ * - implement priorities as in WUSB1.0[Table 7-55]?
+ */
+void wusbhc_handle_dn(struct wusbhc *wusbhc, u8 srcaddr,
+ struct wusb_dn_hdr *dn_hdr, size_t size)
+{
+ struct device *dev = wusbhc->dev;
+ struct wusb_dev *wusb_dev;
+
+ d_fnstart(3, dev, "(%p, %p)\n", wusbhc, dn_hdr);
+
+ if (size < sizeof(struct wusb_dn_hdr)) {
+ dev_err(dev, "DN data shorter than DN header (%d < %d)\n",
+ (int)size, (int)sizeof(struct wusb_dn_hdr));
+ goto out;
+ }
+
+ wusb_dev = wusbhc_find_dev_by_addr(wusbhc, srcaddr);
+ if (wusb_dev == NULL && dn_hdr->bType != WUSB_DN_CONNECT) {
+ dev_dbg(dev, "ignoring DN %d from unconnected device %02x\n",
+ dn_hdr->bType, srcaddr);
+ goto out;
+ }
+
+ switch (dn_hdr->bType) {
+ case WUSB_DN_CONNECT:
+ wusbhc_handle_dn_connect(wusbhc, dn_hdr, size);
+ break;
+ case WUSB_DN_ALIVE:
+ wusbhc_handle_dn_alive(wusbhc, wusb_dev);
+ break;
+ case WUSB_DN_DISCONNECT:
+ wusbhc_handle_dn_disconnect(wusbhc, wusb_dev);
+ break;
+ case WUSB_DN_EPRDY:
+ case WUSB_DN_MASAVAILCHANGED:
+ case WUSB_DN_RWAKE:
+ case WUSB_DN_SLEEP:
+ dev_warn(dev, "ignoring DN %u from %u\n",
+ dn_hdr->bType, srcaddr);
+ break;
+ default:
+ dev_warn(dev, "unknown DN %u (%d octets) from %u\n",
+ dn_hdr->bType, (int)size, srcaddr);
+ }
+out:
+ d_fnend(3, dev, "(%p, %p) = void\n", wusbhc, dn_hdr);
+ return;
+}
+EXPORT_SYMBOL_GPL(wusbhc_handle_dn);
+
+/*
+ * Disconnect a WUSB device from a the cluster
+ *
+ * @wusbhc
+ * @port Fake port where the device is (wusbhc index, not USB port number).
+ *
+ * In Wireless USB, a disconnect is basically telling the device he is
+ * being disconnected and forgetting about him.
+ *
+ * We send the device a Device Disconnect IE (WUSB1.0[7.5.11]) for 100
+ * ms and then keep going.
+ *
+ * We don't do much in case of error; we always pretend we disabled
+ * the port and disconnected the device. If physically the request
+ * didn't get there (many things can fail in the way there), the stack
+ * will reject the device's communication attempts.
+ *
+ * @wusbhc should be refcounted and locked
+ */
+void __wusbhc_dev_disable(struct wusbhc *wusbhc, u8 port_idx)
+{
+ int result;
+ struct device *dev = wusbhc->dev;
+ struct wusb_dev *wusb_dev;
+ struct wuie_disconnect *ie;
+
+ d_fnstart(3, dev, "(%p, %u)\n", wusbhc, port_idx);
+ result = 0;
+ wusb_dev = wusb_port_by_idx(wusbhc, port_idx)->wusb_dev;
+ if (wusb_dev == NULL) {
+ /* reset no device? ignore */
+ dev_dbg(dev, "DISCONNECT: no device at port %u, ignoring\n",
+ port_idx);
+ goto error;
+ }
+ __wusbhc_dev_disconnect(wusbhc, wusb_port_by_idx(wusbhc, port_idx));
+
+ result = -ENOMEM;
+ ie = kzalloc(sizeof(*ie), GFP_KERNEL);
+ if (ie == NULL)
+ goto error;
+ ie->hdr.bLength = sizeof(*ie);
+ ie->hdr.bIEIdentifier = WUIE_ID_DEVICE_DISCONNECT;
+ ie->bDeviceAddress = wusb_dev->addr;
+ result = wusbhc_mmcie_set(wusbhc, 0, 0, &ie->hdr);
+ if (result < 0) {
+ dev_err(dev, "DISCONNECT: can't set MMC: %d\n", result);
+ goto error_kfree;
+ }
+
+ /* 120ms, hopefully 6 MMCs */
+ msleep(100);
+ wusbhc_mmcie_rm(wusbhc, &ie->hdr);
+error_kfree:
+ kfree(ie);
+error:
+ d_fnend(3, dev, "(%p, %u) = %d\n", wusbhc, port_idx, result);
+ return;
+}
+
+static void wusb_cap_descr_printf(const unsigned level, struct device *dev,
+ const struct usb_wireless_cap_descriptor *wcd)
+{
+ d_printf(level, dev,
+ "WUSB Capability Descriptor\n"
+ " bDevCapabilityType 0x%02x\n"
+ " bmAttributes 0x%02x\n"
+ " wPhyRates 0x%04x\n"
+ " bmTFITXPowerInfo 0x%02x\n"
+ " bmFFITXPowerInfo 0x%02x\n"
+ " bmBandGroup 0x%04x\n"
+ " bReserved 0x%02x\n",
+ wcd->bDevCapabilityType,
+ wcd->bmAttributes,
+ le16_to_cpu(wcd->wPHYRates),
+ wcd->bmTFITXPowerInfo,
+ wcd->bmFFITXPowerInfo,
+ wcd->bmBandGroup,
+ wcd->bReserved);
+}
+
+/*
+ * Walk over the BOS descriptor, verify and grok it
+ *
+ * @usb_dev: referenced
+ * @wusb_dev: referenced and unlocked
+ *
+ * The BOS descriptor is defined at WUSB1.0[7.4.1], and it defines a
+ * "flexible" way to wrap all kinds of descriptors inside an standard
+ * descriptor (wonder why they didn't use normal descriptors,
+ * btw). Not like they lack code.
+ *
+ * At the end we go to look for the WUSB Device Capabilities
+ * (WUSB1.0[7.4.1.1]) that is wrapped in a device capability descriptor
+ * that is part of the BOS descriptor set. That tells us what does the
+ * device support (dual role, beacon type, UWB PHY rates).
+ */
+static int wusb_dev_bos_grok(struct usb_device *usb_dev,
+ struct wusb_dev *wusb_dev,
+ struct usb_bos_descriptor *bos, size_t desc_size)
+{
+ ssize_t result;
+ struct device *dev = &usb_dev->dev;
+ void *itr, *top;
+
+ /* Walk over BOS capabilities, verify them */
+ itr = (void *)bos + sizeof(*bos);
+ top = itr + desc_size - sizeof(*bos);
+ while (itr < top) {
+ struct usb_dev_cap_header *cap_hdr = itr;
+ size_t cap_size;
+ u8 cap_type;
+ if (top - itr < sizeof(*cap_hdr)) {
+ dev_err(dev, "Device BUG? premature end of BOS header "
+ "data [offset 0x%02x]: only %zu bytes left\n",
+ (int)(itr - (void *)bos), top - itr);
+ result = -ENOSPC;
+ goto error_bad_cap;
+ }
+ cap_size = cap_hdr->bLength;
+ cap_type = cap_hdr->bDevCapabilityType;
+ d_printf(4, dev, "BOS Capability: 0x%02x (%zu bytes)\n",
+ cap_type, cap_size);
+ if (cap_size == 0)
+ break;
+ if (cap_size > top - itr) {
+ dev_err(dev, "Device BUG? premature end of BOS data "
+ "[offset 0x%02x cap %02x %zu bytes]: "
+ "only %zu bytes left\n",
+ (int)(itr - (void *)bos),
+ cap_type, cap_size, top - itr);
+ result = -EBADF;
+ goto error_bad_cap;
+ }
+ d_dump(3, dev, itr, cap_size);
+ switch (cap_type) {
+ case USB_CAP_TYPE_WIRELESS_USB:
+ if (cap_size != sizeof(*wusb_dev->wusb_cap_descr))
+ dev_err(dev, "Device BUG? WUSB Capability "
+ "descriptor is %zu bytes vs %zu "
+ "needed\n", cap_size,
+ sizeof(*wusb_dev->wusb_cap_descr));
+ else {
+ wusb_dev->wusb_cap_descr = itr;
+ wusb_cap_descr_printf(3, dev, itr);
+ }
+ break;
+ default:
+ dev_err(dev, "BUG? Unknown BOS capability 0x%02x "
+ "(%zu bytes) at offset 0x%02x\n", cap_type,
+ cap_size, (int)(itr - (void *)bos));
+ }
+ itr += cap_size;
+ }
+ result = 0;
+error_bad_cap:
+ return result;
+}
+
+/*
+ * Add information from the BOS descriptors to the device
+ *
+ * @usb_dev: referenced
+ * @wusb_dev: referenced and unlocked
+ *
+ * So what we do is we alloc a space for the BOS descriptor of 64
+ * bytes; read the first four bytes which include the wTotalLength
+ * field (WUSB1.0[T7-26]) and if it fits in those 64 bytes, read the
+ * whole thing. If not we realloc to that size.
+ *
+ * Then we call the groking function, that will fill up
+ * wusb_dev->wusb_cap_descr, which is what we'll need later on.
+ */
+static int wusb_dev_bos_add(struct usb_device *usb_dev,
+ struct wusb_dev *wusb_dev)
+{
+ ssize_t result;
+ struct device *dev = &usb_dev->dev;
+ struct usb_bos_descriptor *bos;
+ size_t alloc_size = 32, desc_size = 4;
+
+ bos = kmalloc(alloc_size, GFP_KERNEL);
+ if (bos == NULL)
+ return -ENOMEM;
+ result = usb_get_descriptor(usb_dev, USB_DT_BOS, 0, bos, desc_size);
+ if (result < 4) {
+ dev_err(dev, "Can't get BOS descriptor or too short: %zd\n",
+ result);
+ goto error_get_descriptor;
+ }
+ desc_size = le16_to_cpu(bos->wTotalLength);
+ if (desc_size >= alloc_size) {
+ kfree(bos);
+ alloc_size = desc_size;
+ bos = kmalloc(alloc_size, GFP_KERNEL);
+ if (bos == NULL)
+ return -ENOMEM;
+ }
+ result = usb_get_descriptor(usb_dev, USB_DT_BOS, 0, bos, desc_size);
+ if (result < 0 || result != desc_size) {
+ dev_err(dev, "Can't get BOS descriptor or too short (need "
+ "%zu bytes): %zd\n", desc_size, result);
+ goto error_get_descriptor;
+ }
+ if (result < sizeof(*bos)
+ || le16_to_cpu(bos->wTotalLength) != desc_size) {
+ dev_err(dev, "Can't get BOS descriptor or too short (need "
+ "%zu bytes): %zd\n", desc_size, result);
+ goto error_get_descriptor;
+ }
+ d_printf(2, dev, "Got BOS descriptor %zd bytes, %u capabilities\n",
+ result, bos->bNumDeviceCaps);
+ d_dump(2, dev, bos, result);
+ result = wusb_dev_bos_grok(usb_dev, wusb_dev, bos, result);
+ if (result < 0)
+ goto error_bad_bos;
+ wusb_dev->bos = bos;
+ return 0;
+
+error_bad_bos:
+error_get_descriptor:
+ kfree(bos);
+ wusb_dev->wusb_cap_descr = NULL;
+ return result;
+}
+
+static void wusb_dev_bos_rm(struct wusb_dev *wusb_dev)
+{
+ kfree(wusb_dev->bos);
+ wusb_dev->wusb_cap_descr = NULL;
+};
+
+static struct usb_wireless_cap_descriptor wusb_cap_descr_default = {
+ .bLength = sizeof(wusb_cap_descr_default),
+ .bDescriptorType = USB_DT_DEVICE_CAPABILITY,
+ .bDevCapabilityType = USB_CAP_TYPE_WIRELESS_USB,
+
+ .bmAttributes = USB_WIRELESS_BEACON_NONE,
+ .wPHYRates = cpu_to_le16(USB_WIRELESS_PHY_53),
+ .bmTFITXPowerInfo = 0,
+ .bmFFITXPowerInfo = 0,
+ .bmBandGroup = cpu_to_le16(0x0001), /* WUSB1.0[7.4.1] bottom */
+ .bReserved = 0
+};
+
+/*
+ * USB stack's device addition Notifier Callback
+ *
+ * Called from drivers/usb/core/hub.c when a new device is added; we
+ * use this hook to perform certain WUSB specific setup work on the
+ * new device. As well, it is the first time we can connect the
+ * wusb_dev and the usb_dev. So we note it down in wusb_dev and take a
+ * reference that we'll drop.
+ *
+ * First we need to determine if the device is a WUSB device (else we
+ * ignore it). For that we use the speed setting (USB_SPEED_VARIABLE)
+ * [FIXME: maybe we'd need something more definitive]. If so, we track
+ * it's usb_busd and from there, the WUSB HC.
+ *
+ * Because all WUSB HCs are contained in a 'struct wusbhc', voila, we
+ * get the wusbhc for the device.
+ *
+ * We have a reference on @usb_dev (as we are called at the end of its
+ * enumeration).
+ *
+ * NOTE: @usb_dev locked
+ */
+static void wusb_dev_add_ncb(struct usb_device *usb_dev)
+{
+ int result = 0;
+ struct wusb_dev *wusb_dev;
+ struct wusbhc *wusbhc;
+ struct device *dev = &usb_dev->dev;
+ u8 port_idx;
+
+ if (usb_dev->wusb == 0 || usb_dev->devnum == 1)
+ return; /* skip non wusb and wusb RHs */
+
+ d_fnstart(3, dev, "(usb_dev %p)\n", usb_dev);
+
+ wusbhc = wusbhc_get_by_usb_dev(usb_dev);
+ if (wusbhc == NULL)
+ goto error_nodev;
+ mutex_lock(&wusbhc->mutex);
+ wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, usb_dev);
+ port_idx = wusb_port_no_to_idx(usb_dev->portnum);
+ mutex_unlock(&wusbhc->mutex);
+ if (wusb_dev == NULL)
+ goto error_nodev;
+ wusb_dev->usb_dev = usb_get_dev(usb_dev);
+ usb_dev->wusb_dev = wusb_dev_get(wusb_dev);
+ result = wusb_dev_sec_add(wusbhc, usb_dev, wusb_dev);
+ if (result < 0) {
+ dev_err(dev, "Cannot enable security: %d\n", result);
+ goto error_sec_add;
+ }
+ /* Now query the device for it's BOS and attach it to wusb_dev */
+ result = wusb_dev_bos_add(usb_dev, wusb_dev);
+ if (result < 0) {
+ dev_err(dev, "Cannot get BOS descriptors: %d\n", result);
+ goto error_bos_add;
+ }
+ result = wusb_dev_sysfs_add(wusbhc, usb_dev, wusb_dev);
+ if (result < 0)
+ goto error_add_sysfs;
+out:
+ wusb_dev_put(wusb_dev);
+ wusbhc_put(wusbhc);
+error_nodev:
+ d_fnend(3, dev, "(usb_dev %p) = void\n", usb_dev);
+ return;
+
+ wusb_dev_sysfs_rm(wusb_dev);
+error_add_sysfs:
+ wusb_dev_bos_rm(wusb_dev);
+error_bos_add:
+ wusb_dev_sec_rm(wusb_dev);
+error_sec_add:
+ mutex_lock(&wusbhc->mutex);
+ __wusbhc_dev_disconnect(wusbhc, wusb_port_by_idx(wusbhc, port_idx));
+ mutex_unlock(&wusbhc->mutex);
+ goto out;
+}
+
+/*
+ * Undo all the steps done at connection by the notifier callback
+ *
+ * NOTE: @usb_dev locked
+ */
+static void wusb_dev_rm_ncb(struct usb_device *usb_dev)
+{
+ struct wusb_dev *wusb_dev = usb_dev->wusb_dev;
+
+ if (usb_dev->wusb == 0 || usb_dev->devnum == 1)
+ return; /* skip non wusb and wusb RHs */
+
+ wusb_dev_sysfs_rm(wusb_dev);
+ wusb_dev_bos_rm(wusb_dev);
+ wusb_dev_sec_rm(wusb_dev);
+ wusb_dev->usb_dev = NULL;
+ usb_dev->wusb_dev = NULL;
+ wusb_dev_put(wusb_dev);
+ usb_put_dev(usb_dev);
+}
+
+/*
+ * Handle notifications from the USB stack (notifier call back)
+ *
+ * This is called when the USB stack does a
+ * usb_{bus,device}_{add,remove}() so we can do WUSB specific
+ * handling. It is called with [for the case of
+ * USB_DEVICE_{ADD,REMOVE} with the usb_dev locked.
+ */
+int wusb_usb_ncb(struct notifier_block *nb, unsigned long val,
+ void *priv)
+{
+ int result = NOTIFY_OK;
+
+ switch (val) {
+ case USB_DEVICE_ADD:
+ wusb_dev_add_ncb(priv);
+ break;
+ case USB_DEVICE_REMOVE:
+ wusb_dev_rm_ncb(priv);
+ break;
+ case USB_BUS_ADD:
+ /* ignore (for now) */
+ case USB_BUS_REMOVE:
+ break;
+ default:
+ WARN_ON(1);
+ result = NOTIFY_BAD;
+ };
+ return result;
+}
+
+/*
+ * Return a referenced wusb_dev given a @wusbhc and @usb_dev
+ */
+struct wusb_dev *__wusb_dev_get_by_usb_dev(struct wusbhc *wusbhc,
+ struct usb_device *usb_dev)
+{
+ struct wusb_dev *wusb_dev;
+ u8 port_idx;
+
+ port_idx = wusb_port_no_to_idx(usb_dev->portnum);
+ BUG_ON(port_idx > wusbhc->ports_max);
+ wusb_dev = wusb_port_by_idx(wusbhc, port_idx)->wusb_dev;
+ if (wusb_dev != NULL) /* ops, device is gone */
+ wusb_dev_get(wusb_dev);
+ return wusb_dev;
+}
+EXPORT_SYMBOL_GPL(__wusb_dev_get_by_usb_dev);
+
+void wusb_dev_destroy(struct kref *_wusb_dev)
+{
+ struct wusb_dev *wusb_dev
+ = container_of(_wusb_dev, struct wusb_dev, refcnt);
+ list_del_init(&wusb_dev->cack_node);
+ wusb_dev_free(wusb_dev);
+ d_fnend(1, NULL, "%s (wusb_dev %p) = void\n", __func__, wusb_dev);
+}
+EXPORT_SYMBOL_GPL(wusb_dev_destroy);
+
+/*
+ * Create all the device connect handling infrastructure
+ *
+ * This is basically the device info array, Connect Acknowledgement
+ * (cack) lists, keep-alive timers (and delayed work thread).
+ */
+int wusbhc_devconnect_create(struct wusbhc *wusbhc)
+{
+ d_fnstart(3, wusbhc->dev, "(wusbhc %p)\n", wusbhc);
+
+ wusbhc->keep_alive_ie.hdr.bIEIdentifier = WUIE_ID_KEEP_ALIVE;
+ wusbhc->keep_alive_ie.hdr.bLength = sizeof(wusbhc->keep_alive_ie.hdr);
+ INIT_DELAYED_WORK(&wusbhc->keep_alive_timer, wusbhc_keep_alive_run);
+
+ wusbhc->cack_ie.hdr.bIEIdentifier = WUIE_ID_CONNECTACK;
+ wusbhc->cack_ie.hdr.bLength = sizeof(wusbhc->cack_ie.hdr);
+ INIT_LIST_HEAD(&wusbhc->cack_list);
+
+ d_fnend(3, wusbhc->dev, "(wusbhc %p) = void\n", wusbhc);
+ return 0;
+}
+
+/*
+ * Release all resources taken by the devconnect stuff
+ */
+void wusbhc_devconnect_destroy(struct wusbhc *wusbhc)
+{
+ d_fnstart(3, wusbhc->dev, "(wusbhc %p)\n", wusbhc);
+ d_fnend(3, wusbhc->dev, "(wusbhc %p) = void\n", wusbhc);
+}
+
+/*
+ * wusbhc_devconnect_start - start accepting device connections
+ * @wusbhc: the WUSB HC
+ *
+ * Sets the Host Info IE to accept all new connections.
+ *
+ * FIXME: This also enables the keep alives but this is not necessary
+ * until there are connected and authenticated devices.
+ */
+int wusbhc_devconnect_start(struct wusbhc *wusbhc,
+ const struct wusb_ckhdid *chid)
+{
+ struct device *dev = wusbhc->dev;
+ struct wuie_host_info *hi;
+ int result;
+
+ hi = kzalloc(sizeof(*hi), GFP_KERNEL);
+ if (hi == NULL)
+ return -ENOMEM;
+
+ hi->hdr.bLength = sizeof(*hi);
+ hi->hdr.bIEIdentifier = WUIE_ID_HOST_INFO;
+ hi->connect_avail = WUIE_HI_CAP_ALL;
+ hi->p2p_drd = 0;
+ hi->stream_index = wusbhc->rsv->stream;
+ hi->CHID = *chid;
+ result = wusbhc_mmcie_set(wusbhc, 0, 0, &hi->hdr);
+ if (result < 0) {
+ dev_err(dev, "Cannot add Host Info MMCIE: %d\n", result);
+ goto error_mmcie_set;
+ }
+ wusbhc->wuie_host_info = hi;
+
+ queue_delayed_work(wusbd, &wusbhc->keep_alive_timer,
+ (wusbhc->trust_timeout*CONFIG_HZ)/1000/2);
+
+ return 0;
+
+error_mmcie_set:
+ kfree(hi);
+ return result;
+}
+
+/*
+ * wusbhc_devconnect_stop - stop managing connected devices
+ * @wusbhc: the WUSB HC
+ *
+ * Removes the Host Info IE and stops the keep alives.
+ *
+ * FIXME: should this disconnect all devices?
+ */
+void wusbhc_devconnect_stop(struct wusbhc *wusbhc)
+{
+ cancel_delayed_work_sync(&wusbhc->keep_alive_timer);
+ WARN_ON(!list_empty(&wusbhc->cack_list));
+
+ wusbhc_mmcie_rm(wusbhc, &wusbhc->wuie_host_info->hdr);
+ kfree(wusbhc->wuie_host_info);
+ wusbhc->wuie_host_info = NULL;
+}
+
+/*
+ * wusb_set_dev_addr - set the WUSB device address used by the host
+ * @wusbhc: the WUSB HC the device is connect to
+ * @wusb_dev: the WUSB device
+ * @addr: new device address
+ */
+int wusb_set_dev_addr(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev, u8 addr)
+{
+ int result;
+
+ wusb_dev->addr = addr;
+ result = wusbhc->dev_info_set(wusbhc, wusb_dev);
+ if (result < 0)
+ dev_err(wusbhc->dev, "device %d: failed to set device "
+ "address\n", wusb_dev->port_idx);
+ else
+ dev_info(wusbhc->dev, "device %d: %s addr %u\n",
+ wusb_dev->port_idx,
+ (addr & WUSB_DEV_ADDR_UNAUTH) ? "unauth" : "auth",
+ wusb_dev->addr);
+
+ return result;
+}
diff --git a/drivers/usb/wusbcore/mmc.c b/drivers/usb/wusbcore/mmc.c
new file mode 100644
index 000000000000..e5390b77aaaa
--- /dev/null
+++ b/drivers/usb/wusbcore/mmc.c
@@ -0,0 +1,329 @@
+/*
+ * WUSB Wire Adapter: Control/Data Streaming Interface (WUSB[8])
+ * MMC (Microscheduled Management Command) handling
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * WUIEs and MMC IEs...well, they are almost the same at the end. MMC
+ * IEs are Wireless USB IEs that go into the MMC period...[what is
+ * that? look in Design-overview.txt].
+ *
+ *
+ * This is a simple subsystem to keep track of which IEs are being
+ * sent by the host in the MMC period.
+ *
+ * For each WUIE we ask to send, we keep it in an array, so we can
+ * request its removal later, or replace the content. They are tracked
+ * by pointer, so be sure to use the same pointer if you want to
+ * remove it or update the contents.
+ *
+ * FIXME:
+ * - add timers that autoremove intervalled IEs?
+ */
+#include <linux/usb/wusb.h>
+#include "wusbhc.h"
+
+/* Initialize the MMCIEs handling mechanism */
+int wusbhc_mmcie_create(struct wusbhc *wusbhc)
+{
+ u8 mmcies = wusbhc->mmcies_max;
+ wusbhc->mmcie = kzalloc(mmcies * sizeof(wusbhc->mmcie[0]), GFP_KERNEL);
+ if (wusbhc->mmcie == NULL)
+ return -ENOMEM;
+ mutex_init(&wusbhc->mmcie_mutex);
+ return 0;
+}
+
+/* Release resources used by the MMCIEs handling mechanism */
+void wusbhc_mmcie_destroy(struct wusbhc *wusbhc)
+{
+ kfree(wusbhc->mmcie);
+}
+
+/*
+ * Add or replace an MMC Wireless USB IE.
+ *
+ * @interval: See WUSB1.0[8.5.3.1]
+ * @repeat_cnt: See WUSB1.0[8.5.3.1]
+ * @handle: See WUSB1.0[8.5.3.1]
+ * @wuie: Pointer to the header of the WUSB IE data to add.
+ * MUST BE allocated in a kmalloc buffer (no stack or
+ * vmalloc).
+ * THE CALLER ALWAYS OWNS THE POINTER (we don't free it
+ * on remove, we just forget about it).
+ * @returns: 0 if ok, < 0 errno code on error.
+ *
+ * Goes over the *whole* @wusbhc->mmcie array looking for (a) the
+ * first free spot and (b) if @wuie is already in the array (aka:
+ * transmitted in the MMCs) the spot were it is.
+ *
+ * If present, we "overwrite it" (update).
+ *
+ *
+ * NOTE: Need special ordering rules -- see below WUSB1.0 Table 7-38.
+ * The host uses the handle as the 'sort' index. We
+ * allocate the last one always for the WUIE_ID_HOST_INFO, and
+ * the rest, first come first serve in inverse order.
+ *
+ * Host software must make sure that it adds the other IEs in
+ * the right order... the host hardware is responsible for
+ * placing the WCTA IEs in the right place with the other IEs
+ * set by host software.
+ *
+ * NOTE: we can access wusbhc->wa_descr without locking because it is
+ * read only.
+ */
+int wusbhc_mmcie_set(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt,
+ struct wuie_hdr *wuie)
+{
+ int result = -ENOBUFS;
+ struct device *dev = wusbhc->dev;
+ unsigned handle, itr;
+
+ /* Search a handle, taking into account the ordering */
+ mutex_lock(&wusbhc->mmcie_mutex);
+ switch (wuie->bIEIdentifier) {
+ case WUIE_ID_HOST_INFO:
+ /* Always last */
+ handle = wusbhc->mmcies_max - 1;
+ break;
+ case WUIE_ID_ISOCH_DISCARD:
+ dev_err(wusbhc->dev, "Special ordering case for WUIE ID 0x%x "
+ "unimplemented\n", wuie->bIEIdentifier);
+ result = -ENOSYS;
+ goto error_unlock;
+ default:
+ /* search for it or find the last empty slot */
+ handle = ~0;
+ for (itr = 0; itr < wusbhc->mmcies_max - 1; itr++) {
+ if (wusbhc->mmcie[itr] == wuie) {
+ handle = itr;
+ break;
+ }
+ if (wusbhc->mmcie[itr] == NULL)
+ handle = itr;
+ }
+ if (handle == ~0) {
+ if (printk_ratelimit())
+ dev_err(dev, "MMC handle space exhausted\n");
+ goto error_unlock;
+ }
+ }
+ result = (wusbhc->mmcie_add)(wusbhc, interval, repeat_cnt, handle,
+ wuie);
+ if (result >= 0)
+ wusbhc->mmcie[handle] = wuie;
+error_unlock:
+ mutex_unlock(&wusbhc->mmcie_mutex);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wusbhc_mmcie_set);
+
+/*
+ * Remove an MMC IE previously added with wusbhc_mmcie_set()
+ *
+ * @wuie Pointer used to add the WUIE
+ */
+void wusbhc_mmcie_rm(struct wusbhc *wusbhc, struct wuie_hdr *wuie)
+{
+ int result;
+ struct device *dev = wusbhc->dev;
+ unsigned handle, itr;
+
+ mutex_lock(&wusbhc->mmcie_mutex);
+ for (itr = 0; itr < wusbhc->mmcies_max; itr++)
+ if (wusbhc->mmcie[itr] == wuie) {
+ handle = itr;
+ goto found;
+ }
+ mutex_unlock(&wusbhc->mmcie_mutex);
+ return;
+
+found:
+ result = (wusbhc->mmcie_rm)(wusbhc, handle);
+ if (result == 0)
+ wusbhc->mmcie[itr] = NULL;
+ else if (printk_ratelimit())
+ dev_err(dev, "MMC: Failed to remove IE %p (0x%02x)\n",
+ wuie, wuie->bIEIdentifier);
+ mutex_unlock(&wusbhc->mmcie_mutex);
+ return;
+}
+EXPORT_SYMBOL_GPL(wusbhc_mmcie_rm);
+
+/*
+ * wusbhc_start - start transmitting MMCs and accepting connections
+ * @wusbhc: the HC to start
+ * @chid: the CHID to use for this host
+ *
+ * Establishes a cluster reservation, enables device connections, and
+ * starts MMCs with appropriate DNTS parameters.
+ */
+int wusbhc_start(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid)
+{
+ int result;
+ struct device *dev = wusbhc->dev;
+
+ WARN_ON(wusbhc->wuie_host_info != NULL);
+
+ result = wusbhc_rsv_establish(wusbhc);
+ if (result < 0) {
+ dev_err(dev, "cannot establish cluster reservation: %d\n",
+ result);
+ goto error_rsv_establish;
+ }
+
+ result = wusbhc_devconnect_start(wusbhc, chid);
+ if (result < 0) {
+ dev_err(dev, "error enabling device connections: %d\n", result);
+ goto error_devconnect_start;
+ }
+
+ result = wusbhc_sec_start(wusbhc);
+ if (result < 0) {
+ dev_err(dev, "error starting security in the HC: %d\n", result);
+ goto error_sec_start;
+ }
+ /* FIXME: the choice of the DNTS parameters is somewhat
+ * arbitrary */
+ result = wusbhc->set_num_dnts(wusbhc, 0, 15);
+ if (result < 0) {
+ dev_err(dev, "Cannot set DNTS parameters: %d\n", result);
+ goto error_set_num_dnts;
+ }
+ result = wusbhc->start(wusbhc);
+ if (result < 0) {
+ dev_err(dev, "error starting wusbch: %d\n", result);
+ goto error_wusbhc_start;
+ }
+ wusbhc->active = 1;
+ return 0;
+
+error_wusbhc_start:
+ wusbhc_sec_stop(wusbhc);
+error_set_num_dnts:
+error_sec_start:
+ wusbhc_devconnect_stop(wusbhc);
+error_devconnect_start:
+ wusbhc_rsv_terminate(wusbhc);
+error_rsv_establish:
+ return result;
+}
+
+/*
+ * Disconnect all from the WUSB Channel
+ *
+ * Send a Host Disconnect IE in the MMC, wait, don't send it any more
+ */
+static int __wusbhc_host_disconnect_ie(struct wusbhc *wusbhc)
+{
+ int result = -ENOMEM;
+ struct wuie_host_disconnect *host_disconnect_ie;
+ might_sleep();
+ host_disconnect_ie = kmalloc(sizeof(*host_disconnect_ie), GFP_KERNEL);
+ if (host_disconnect_ie == NULL)
+ goto error_alloc;
+ host_disconnect_ie->hdr.bLength = sizeof(*host_disconnect_ie);
+ host_disconnect_ie->hdr.bIEIdentifier = WUIE_ID_HOST_DISCONNECT;
+ result = wusbhc_mmcie_set(wusbhc, 0, 0, &host_disconnect_ie->hdr);
+ if (result < 0)
+ goto error_mmcie_set;
+
+ /* WUSB1.0[8.5.3.1 & 7.5.2] */
+ msleep(100);
+ wusbhc_mmcie_rm(wusbhc, &host_disconnect_ie->hdr);
+error_mmcie_set:
+ kfree(host_disconnect_ie);
+error_alloc:
+ return result;
+}
+
+/*
+ * wusbhc_stop - stop transmitting MMCs
+ * @wusbhc: the HC to stop
+ *
+ * Send a Host Disconnect IE, wait, remove all the MMCs (stop sending MMCs).
+ *
+ * If we can't allocate a Host Stop IE, screw it, we don't notify the
+ * devices we are disconnecting...
+ */
+void wusbhc_stop(struct wusbhc *wusbhc)
+{
+ if (wusbhc->active) {
+ wusbhc->active = 0;
+ wusbhc->stop(wusbhc);
+ wusbhc_sec_stop(wusbhc);
+ __wusbhc_host_disconnect_ie(wusbhc);
+ wusbhc_devconnect_stop(wusbhc);
+ wusbhc_rsv_terminate(wusbhc);
+ }
+}
+EXPORT_SYMBOL_GPL(wusbhc_stop);
+
+/*
+ * Change the CHID in a WUSB Channel
+ *
+ * If it is just a new CHID, send a Host Disconnect IE and then change
+ * the CHID IE.
+ */
+static int __wusbhc_chid_change(struct wusbhc *wusbhc,
+ const struct wusb_ckhdid *chid)
+{
+ int result = -ENOSYS;
+ struct device *dev = wusbhc->dev;
+ dev_err(dev, "%s() not implemented yet\n", __func__);
+ return result;
+
+ BUG_ON(wusbhc->wuie_host_info == NULL);
+ __wusbhc_host_disconnect_ie(wusbhc);
+ wusbhc->wuie_host_info->CHID = *chid;
+ result = wusbhc_mmcie_set(wusbhc, 0, 0, &wusbhc->wuie_host_info->hdr);
+ if (result < 0)
+ dev_err(dev, "Can't update Host Info WUSB IE: %d\n", result);
+ return result;
+}
+
+/*
+ * Set/reset/update a new CHID
+ *
+ * Depending on the previous state of the MMCs, start, stop or change
+ * the sent MMC. This effectively switches the host controller on and
+ * off (radio wise).
+ */
+int wusbhc_chid_set(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid)
+{
+ int result = 0;
+
+ if (memcmp(chid, &wusb_ckhdid_zero, sizeof(chid)) == 0)
+ chid = NULL;
+
+ mutex_lock(&wusbhc->mutex);
+ if (wusbhc->active) {
+ if (chid)
+ result = __wusbhc_chid_change(wusbhc, chid);
+ else
+ wusbhc_stop(wusbhc);
+ } else {
+ if (chid)
+ wusbhc_start(wusbhc, chid);
+ }
+ mutex_unlock(&wusbhc->mutex);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wusbhc_chid_set);
diff --git a/drivers/usb/wusbcore/pal.c b/drivers/usb/wusbcore/pal.c
new file mode 100644
index 000000000000..cc126b444734
--- /dev/null
+++ b/drivers/usb/wusbcore/pal.c
@@ -0,0 +1,39 @@
+/*
+ * Wireless USB Host Controller
+ * UWB Protocol Adaptation Layer (PAL) glue.
+ *
+ * Copyright (C) 2008 Cambridge Silicon Radio 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include "wusbhc.h"
+
+/**
+ * wusbhc_pal_register - register the WUSB HC as a UWB PAL
+ * @wusbhc: the WUSB HC
+ */
+int wusbhc_pal_register(struct wusbhc *wusbhc)
+{
+ uwb_pal_init(&wusbhc->pal);
+
+ return uwb_pal_register(wusbhc->uwb_rc, &wusbhc->pal);
+}
+
+/**
+ * wusbhc_pal_register - unregister the WUSB HC as a UWB PAL
+ * @wusbhc: the WUSB HC
+ */
+void wusbhc_pal_unregister(struct wusbhc *wusbhc)
+{
+ uwb_pal_unregister(wusbhc->uwb_rc, &wusbhc->pal);
+}
diff --git a/drivers/usb/wusbcore/reservation.c b/drivers/usb/wusbcore/reservation.c
new file mode 100644
index 000000000000..fc63e77ded2d
--- /dev/null
+++ b/drivers/usb/wusbcore/reservation.c
@@ -0,0 +1,115 @@
+/*
+ * WUSB cluster reservation management
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+#include <linux/uwb.h>
+
+#include "wusbhc.h"
+
+/*
+ * WUSB cluster reservations are multicast reservations with the
+ * broadcast cluster ID (BCID) as the target DevAddr.
+ *
+ * FIXME: consider adjusting the reservation depending on what devices
+ * are attached.
+ */
+
+static int wusbhc_bwa_set(struct wusbhc *wusbhc, u8 stream,
+ const struct uwb_mas_bm *mas)
+{
+ if (mas == NULL)
+ mas = &uwb_mas_bm_zero;
+ return wusbhc->bwa_set(wusbhc, stream, mas);
+}
+
+/**
+ * wusbhc_rsv_complete_cb - WUSB HC reservation complete callback
+ * @rsv: the reservation
+ *
+ * Either set or clear the HC's view of the reservation.
+ *
+ * FIXME: when a reservation is denied the HC should be stopped.
+ */
+static void wusbhc_rsv_complete_cb(struct uwb_rsv *rsv)
+{
+ struct wusbhc *wusbhc = rsv->pal_priv;
+ struct device *dev = wusbhc->dev;
+ char buf[72];
+
+ switch (rsv->state) {
+ case UWB_RSV_STATE_O_ESTABLISHED:
+ bitmap_scnprintf(buf, sizeof(buf), rsv->mas.bm, UWB_NUM_MAS);
+ dev_dbg(dev, "established reservation: %s\n", buf);
+ wusbhc_bwa_set(wusbhc, rsv->stream, &rsv->mas);
+ break;
+ case UWB_RSV_STATE_NONE:
+ dev_dbg(dev, "removed reservation\n");
+ wusbhc_bwa_set(wusbhc, 0, NULL);
+ wusbhc->rsv = NULL;
+ break;
+ default:
+ dev_dbg(dev, "unexpected reservation state: %d\n", rsv->state);
+ break;
+ }
+}
+
+
+/**
+ * wusbhc_rsv_establish - establish a reservation for the cluster
+ * @wusbhc: the WUSB HC requesting a bandwith reservation
+ */
+int wusbhc_rsv_establish(struct wusbhc *wusbhc)
+{
+ struct uwb_rc *rc = wusbhc->uwb_rc;
+ struct uwb_rsv *rsv;
+ struct uwb_dev_addr bcid;
+ int ret;
+
+ rsv = uwb_rsv_create(rc, wusbhc_rsv_complete_cb, wusbhc);
+ if (rsv == NULL)
+ return -ENOMEM;
+
+ bcid.data[0] = wusbhc->cluster_id;
+ bcid.data[1] = 0;
+
+ rsv->owner = &rc->uwb_dev;
+ rsv->target.type = UWB_RSV_TARGET_DEVADDR;
+ rsv->target.devaddr = bcid;
+ rsv->type = UWB_DRP_TYPE_PRIVATE;
+ rsv->max_mas = 256;
+ rsv->min_mas = 16; /* one MAS per zone? */
+ rsv->sparsity = 16; /* at least one MAS in each zone? */
+ rsv->is_multicast = true;
+
+ ret = uwb_rsv_establish(rsv);
+ if (ret == 0)
+ wusbhc->rsv = rsv;
+ else
+ uwb_rsv_destroy(rsv);
+ return ret;
+}
+
+
+/**
+ * wusbhc_rsv_terminate - terminate any cluster reservation
+ * @wusbhc: the WUSB host whose reservation is to be terminated
+ */
+void wusbhc_rsv_terminate(struct wusbhc *wusbhc)
+{
+ if (wusbhc->rsv)
+ uwb_rsv_terminate(wusbhc->rsv);
+}
diff --git a/drivers/usb/wusbcore/rh.c b/drivers/usb/wusbcore/rh.c
new file mode 100644
index 000000000000..9ae36dfb0750
--- /dev/null
+++ b/drivers/usb/wusbcore/rh.c
@@ -0,0 +1,477 @@
+/*
+ * Wireless USB Host Controller
+ * Root Hub operations
+ *
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * We fake a root hub that has fake ports (as many as simultaneous
+ * devices the Wireless USB Host Controller can deal with). For each
+ * port we keep an state in @wusbhc->port[index] identical to the one
+ * specified in the USB2.0[ch11] spec and some extra device
+ * information that complements the one in 'struct usb_device' (as
+ * this lacs a hcpriv pointer).
+ *
+ * Note this is common to WHCI and HWA host controllers.
+ *
+ * Through here we enable most of the state changes that the USB stack
+ * will use to connect or disconnect devices. We need to do some
+ * forced adaptation of Wireless USB device states vs. wired:
+ *
+ * USB: WUSB:
+ *
+ * Port Powered-off port slot n/a
+ * Powered-on port slot available
+ * Disconnected port slot available
+ * Connected port slot assigned device
+ * device sent DN_Connect
+ * device was authenticated
+ * Enabled device is authenticated, transitioned
+ * from unauth -> auth -> default address
+ * -> enabled
+ * Reset disconnect
+ * Disable disconnect
+ *
+ * This maps the standard USB port states with the WUSB device states
+ * so we can fake ports without having to modify the USB stack.
+ *
+ * FIXME: this process will change in the future
+ *
+ *
+ * ENTRY POINTS
+ *
+ * Our entry points into here are, as in hcd.c, the USB stack root hub
+ * ops defined in the usb_hcd struct:
+ *
+ * wusbhc_rh_status_data() Provide hub and port status data bitmap
+ *
+ * wusbhc_rh_control() Execution of all the major requests
+ * you can do to a hub (Set|Clear
+ * features, get descriptors, status, etc).
+ *
+ * wusbhc_rh_[suspend|resume]() That
+ *
+ * wusbhc_rh_start_port_reset() ??? unimplemented
+ */
+#include "wusbhc.h"
+
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+/*
+ * Reset a fake port
+ *
+ * This can be called to reset a port from any other state or to reset
+ * it when connecting. In Wireless USB they are different; when doing
+ * a new connect that involves going over the authentication. When
+ * just reseting, its a different story.
+ *
+ * The Linux USB stack resets a port twice before it considers it
+ * enabled, so we have to detect and ignore that.
+ *
+ * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
+ *
+ * Supposedly we are the only thread accesing @wusbhc->port; in any
+ * case, maybe we should move the mutex locking from
+ * wusbhc_devconnect_auth() to here.
+ *
+ * @port_idx refers to the wusbhc's port index, not the USB port number
+ */
+static int wusbhc_rh_port_reset(struct wusbhc *wusbhc, u8 port_idx)
+{
+ int result = 0;
+ struct wusb_port *port = wusb_port_by_idx(wusbhc, port_idx);
+
+ d_fnstart(3, wusbhc->dev, "(wusbhc %p port_idx %u)\n",
+ wusbhc, port_idx);
+ if (port->reset_count == 0) {
+ wusbhc_devconnect_auth(wusbhc, port_idx);
+ port->reset_count++;
+ } else if (port->reset_count == 1)
+ /* see header */
+ d_printf(2, wusbhc->dev, "Ignoring second reset on port_idx "
+ "%u\n", port_idx);
+ else
+ result = wusbhc_dev_reset(wusbhc, port_idx);
+ d_fnend(3, wusbhc->dev, "(wusbhc %p port_idx %u) = %d\n",
+ wusbhc, port_idx, result);
+ return result;
+}
+
+/*
+ * Return the hub change status bitmap
+ *
+ * The bits in the change status bitmap are cleared when a
+ * ClearPortFeature request is issued (USB2.0[11.12.3,11.12.4].
+ *
+ * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
+ *
+ * WARNING!! This gets called from atomic context; we cannot get the
+ * mutex--the only race condition we can find is some bit
+ * changing just after we copy it, which shouldn't be too
+ * big of a problem [and we can't make it an spinlock
+ * because other parts need to take it and sleep] .
+ *
+ * @usb_hcd is refcounted, so it won't dissapear under us
+ * and before killing a host, the polling of the root hub
+ * would be stopped anyway.
+ */
+int wusbhc_rh_status_data(struct usb_hcd *usb_hcd, char *_buf)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ size_t cnt, size;
+ unsigned long *buf = (unsigned long *) _buf;
+
+ d_fnstart(1, wusbhc->dev, "(wusbhc %p)\n", wusbhc);
+ /* WE DON'T LOCK, see comment */
+ size = wusbhc->ports_max + 1 /* hub bit */;
+ size = (size + 8 - 1) / 8; /* round to bytes */
+ for (cnt = 0; cnt < wusbhc->ports_max; cnt++)
+ if (wusb_port_by_idx(wusbhc, cnt)->change)
+ set_bit(cnt + 1, buf);
+ else
+ clear_bit(cnt + 1, buf);
+ d_fnend(1, wusbhc->dev, "(wusbhc %p) %u, buffer:\n", wusbhc, (int)size);
+ d_dump(1, wusbhc->dev, _buf, size);
+ return size;
+}
+EXPORT_SYMBOL_GPL(wusbhc_rh_status_data);
+
+/*
+ * Return the hub's desciptor
+ *
+ * NOTE: almost cut and paste from ehci-hub.c
+ *
+ * @wusbhc is assumed referenced and @wusbhc->mutex unlocked
+ */
+static int wusbhc_rh_get_hub_descr(struct wusbhc *wusbhc, u16 wValue,
+ u16 wIndex,
+ struct usb_hub_descriptor *descr,
+ u16 wLength)
+{
+ u16 temp = 1 + (wusbhc->ports_max / 8);
+ u8 length = 7 + 2 * temp;
+
+ if (wLength < length)
+ return -ENOSPC;
+ descr->bDescLength = 7 + 2 * temp;
+ descr->bDescriptorType = 0x29; /* HUB type */
+ descr->bNbrPorts = wusbhc->ports_max;
+ descr->wHubCharacteristics = cpu_to_le16(
+ 0x00 /* All ports power at once */
+ | 0x00 /* not part of compound device */
+ | 0x10 /* No overcurrent protection */
+ | 0x00 /* 8 FS think time FIXME ?? */
+ | 0x00); /* No port indicators */
+ descr->bPwrOn2PwrGood = 0;
+ descr->bHubContrCurrent = 0;
+ /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */
+ memset(&descr->bitmap [0], 0, temp);
+ memset(&descr->bitmap [temp], 0xff, temp);
+ return 0;
+}
+
+/*
+ * Clear a hub feature
+ *
+ * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
+ *
+ * Nothing to do, so no locking needed ;)
+ */
+static int wusbhc_rh_clear_hub_feat(struct wusbhc *wusbhc, u16 feature)
+{
+ int result;
+ struct device *dev = wusbhc->dev;
+
+ d_fnstart(4, dev, "(%p, feature 0x%04u)\n", wusbhc, feature);
+ switch (feature) {
+ case C_HUB_LOCAL_POWER:
+ /* FIXME: maybe plug bit 0 to the power input status,
+ * if any?
+ * see wusbhc_rh_get_hub_status() */
+ case C_HUB_OVER_CURRENT:
+ result = 0;
+ break;
+ default:
+ result = -EPIPE;
+ }
+ d_fnend(4, dev, "(%p, feature 0x%04u), %d\n", wusbhc, feature, result);
+ return result;
+}
+
+/*
+ * Return hub status (it is always zero...)
+ *
+ * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
+ *
+ * Nothing to do, so no locking needed ;)
+ */
+static int wusbhc_rh_get_hub_status(struct wusbhc *wusbhc, u32 *buf,
+ u16 wLength)
+{
+ /* FIXME: maybe plug bit 0 to the power input status (if any)? */
+ *buf = 0;
+ return 0;
+}
+
+/*
+ * Set a port feature
+ *
+ * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
+ */
+static int wusbhc_rh_set_port_feat(struct wusbhc *wusbhc, u16 feature,
+ u8 selector, u8 port_idx)
+{
+ int result = -EINVAL;
+ struct device *dev = wusbhc->dev;
+
+ d_fnstart(4, dev, "(feat 0x%04u, selector 0x%u, port_idx %d)\n",
+ feature, selector, port_idx);
+
+ if (port_idx > wusbhc->ports_max)
+ goto error;
+
+ switch (feature) {
+ /* According to USB2.0[11.24.2.13]p2, these features
+ * are not required to be implemented. */
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ case USB_PORT_FEAT_C_ENABLE:
+ case USB_PORT_FEAT_C_SUSPEND:
+ case USB_PORT_FEAT_C_CONNECTION:
+ case USB_PORT_FEAT_C_RESET:
+ result = 0;
+ break;
+
+ case USB_PORT_FEAT_POWER:
+ /* No such thing, but we fake it works */
+ mutex_lock(&wusbhc->mutex);
+ wusb_port_by_idx(wusbhc, port_idx)->power = 1;
+ mutex_unlock(&wusbhc->mutex);
+ result = 0;
+ break;
+ case USB_PORT_FEAT_RESET:
+ result = wusbhc_rh_port_reset(wusbhc, port_idx);
+ break;
+ case USB_PORT_FEAT_ENABLE:
+ case USB_PORT_FEAT_SUSPEND:
+ dev_err(dev, "(port_idx %d) set feat %d/%d UNIMPLEMENTED\n",
+ port_idx, feature, selector);
+ result = -ENOSYS;
+ break;
+ default:
+ dev_err(dev, "(port_idx %d) set feat %d/%d UNKNOWN\n",
+ port_idx, feature, selector);
+ result = -EPIPE;
+ break;
+ }
+error:
+ d_fnend(4, dev, "(feat 0x%04u, selector 0x%u, port_idx %d) = %d\n",
+ feature, selector, port_idx, result);
+ return result;
+}
+
+/*
+ * Clear a port feature...
+ *
+ * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
+ */
+static int wusbhc_rh_clear_port_feat(struct wusbhc *wusbhc, u16 feature,
+ u8 selector, u8 port_idx)
+{
+ int result = -EINVAL;
+ struct device *dev = wusbhc->dev;
+
+ d_fnstart(4, dev, "(wusbhc %p feat 0x%04x selector %d port_idx %d)\n",
+ wusbhc, feature, selector, port_idx);
+
+ if (port_idx > wusbhc->ports_max)
+ goto error;
+
+ mutex_lock(&wusbhc->mutex);
+ result = 0;
+ switch (feature) {
+ case USB_PORT_FEAT_POWER: /* fake port always on */
+ /* According to USB2.0[11.24.2.7.1.4], no need to implement? */
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ wusb_port_by_idx(wusbhc, port_idx)->c_reset = 0;
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ wusb_port_by_idx(wusbhc, port_idx)->c_connection = 0;
+ break;
+ case USB_PORT_FEAT_ENABLE:
+ __wusbhc_dev_disable(wusbhc, port_idx);
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ wusb_port_by_idx(wusbhc, port_idx)->c_enable = 0;
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ case USB_PORT_FEAT_C_SUSPEND:
+ case 0xffff: /* ??? FIXME */
+ dev_err(dev, "(port_idx %d) Clear feat %d/%d UNIMPLEMENTED\n",
+ port_idx, feature, selector);
+ /* dump_stack(); */
+ result = -ENOSYS;
+ break;
+ default:
+ dev_err(dev, "(port_idx %d) Clear feat %d/%d UNKNOWN\n",
+ port_idx, feature, selector);
+ result = -EPIPE;
+ break;
+ }
+ mutex_unlock(&wusbhc->mutex);
+error:
+ d_fnend(4, dev, "(wusbhc %p feat 0x%04x selector %d port_idx %d) = "
+ "%d\n", wusbhc, feature, selector, port_idx, result);
+ return result;
+}
+
+/*
+ * Return the port's status
+ *
+ * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
+ */
+static int wusbhc_rh_get_port_status(struct wusbhc *wusbhc, u16 port_idx,
+ u32 *_buf, u16 wLength)
+{
+ int result = -EINVAL;
+ u16 *buf = (u16 *) _buf;
+
+ d_fnstart(1, wusbhc->dev, "(wusbhc %p port_idx %u wLength %u)\n",
+ wusbhc, port_idx, wLength);
+ if (port_idx > wusbhc->ports_max)
+ goto error;
+ mutex_lock(&wusbhc->mutex);
+ buf[0] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->status);
+ buf[1] = cpu_to_le16(wusb_port_by_idx(wusbhc, port_idx)->change);
+ result = 0;
+ mutex_unlock(&wusbhc->mutex);
+error:
+ d_fnend(1, wusbhc->dev, "(wusbhc %p) = %d, buffer:\n", wusbhc, result);
+ d_dump(1, wusbhc->dev, _buf, wLength);
+ return result;
+}
+
+/*
+ * Entry point for Root Hub operations
+ *
+ * @wusbhc is assumed referenced and @wusbhc->mutex unlocked.
+ */
+int wusbhc_rh_control(struct usb_hcd *usb_hcd, u16 reqntype, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ int result = -ENOSYS;
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+
+ switch (reqntype) {
+ case GetHubDescriptor:
+ result = wusbhc_rh_get_hub_descr(
+ wusbhc, wValue, wIndex,
+ (struct usb_hub_descriptor *) buf, wLength);
+ break;
+ case ClearHubFeature:
+ result = wusbhc_rh_clear_hub_feat(wusbhc, wValue);
+ break;
+ case GetHubStatus:
+ result = wusbhc_rh_get_hub_status(wusbhc, (u32 *)buf, wLength);
+ break;
+
+ case SetPortFeature:
+ result = wusbhc_rh_set_port_feat(wusbhc, wValue, wIndex >> 8,
+ (wIndex & 0xff) - 1);
+ break;
+ case ClearPortFeature:
+ result = wusbhc_rh_clear_port_feat(wusbhc, wValue, wIndex >> 8,
+ (wIndex & 0xff) - 1);
+ break;
+ case GetPortStatus:
+ result = wusbhc_rh_get_port_status(wusbhc, wIndex - 1,
+ (u32 *)buf, wLength);
+ break;
+
+ case SetHubFeature:
+ default:
+ dev_err(wusbhc->dev, "%s (%p [%p], %x, %x, %x, %p, %x) "
+ "UNIMPLEMENTED\n", __func__, usb_hcd, wusbhc, reqntype,
+ wValue, wIndex, buf, wLength);
+ /* dump_stack(); */
+ result = -ENOSYS;
+ }
+ return result;
+}
+EXPORT_SYMBOL_GPL(wusbhc_rh_control);
+
+int wusbhc_rh_suspend(struct usb_hcd *usb_hcd)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__,
+ usb_hcd, wusbhc);
+ /* dump_stack(); */
+ return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(wusbhc_rh_suspend);
+
+int wusbhc_rh_resume(struct usb_hcd *usb_hcd)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__,
+ usb_hcd, wusbhc);
+ /* dump_stack(); */
+ return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(wusbhc_rh_resume);
+
+int wusbhc_rh_start_port_reset(struct usb_hcd *usb_hcd, unsigned port_idx)
+{
+ struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+ dev_err(wusbhc->dev, "%s (%p [%p], port_idx %u) UNIMPLEMENTED\n",
+ __func__, usb_hcd, wusbhc, port_idx);
+ WARN_ON(1);
+ return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(wusbhc_rh_start_port_reset);
+
+static void wusb_port_init(struct wusb_port *port)
+{
+ port->high_speed = 1;
+}
+
+/*
+ * Alloc fake port specific fields and status.
+ */
+int wusbhc_rh_create(struct wusbhc *wusbhc)
+{
+ int result = -ENOMEM;
+ size_t port_size, itr;
+ port_size = wusbhc->ports_max * sizeof(wusbhc->port[0]);
+ wusbhc->port = kzalloc(port_size, GFP_KERNEL);
+ if (wusbhc->port == NULL)
+ goto error_port_alloc;
+ for (itr = 0; itr < wusbhc->ports_max; itr++)
+ wusb_port_init(&wusbhc->port[itr]);
+ result = 0;
+error_port_alloc:
+ return result;
+}
+
+void wusbhc_rh_destroy(struct wusbhc *wusbhc)
+{
+ kfree(wusbhc->port);
+}
diff --git a/drivers/usb/wusbcore/security.c b/drivers/usb/wusbcore/security.c
new file mode 100644
index 000000000000..0c88fd3ce0c2
--- /dev/null
+++ b/drivers/usb/wusbcore/security.c
@@ -0,0 +1,644 @@
+/*
+ * Wireless USB Host Controller
+ * Security support: encryption enablement, etc
+ *
+ * Copyright (C) 2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ */
+#include <linux/types.h>
+#include <linux/usb/ch9.h>
+#include <linux/random.h>
+#include "wusbhc.h"
+
+/*
+ * DEBUG & SECURITY WARNING!!!!
+ *
+ * If you enable this past 1, the debug code will weaken the
+ * cryptographic safety of the system (on purpose, for debugging).
+ *
+ * Weaken means:
+ * we print secret keys and intermediate values all the way,
+ */
+#undef D_LOCAL
+#define D_LOCAL 2
+#include <linux/uwb/debug.h>
+
+static void wusbhc_set_gtk_callback(struct urb *urb);
+static void wusbhc_gtk_rekey_done_work(struct work_struct *work);
+
+int wusbhc_sec_create(struct wusbhc *wusbhc)
+{
+ wusbhc->gtk.descr.bLength = sizeof(wusbhc->gtk.descr) + sizeof(wusbhc->gtk.data);
+ wusbhc->gtk.descr.bDescriptorType = USB_DT_KEY;
+ wusbhc->gtk.descr.bReserved = 0;
+
+ wusbhc->gtk_index.index = 0;
+ wusbhc->gtk_index.type = WUSB_KEY_INDEX_TYPE_GTK;
+ wusbhc->gtk_index.originator = WUSB_KEY_INDEX_ORIGINATOR_HOST;
+
+ INIT_WORK(&wusbhc->gtk_rekey_done_work, wusbhc_gtk_rekey_done_work);
+
+ return 0;
+}
+
+
+/* Called when the HC is destroyed */
+void wusbhc_sec_destroy(struct wusbhc *wusbhc)
+{
+}
+
+
+/**
+ * wusbhc_next_tkid - generate a new, currently unused, TKID
+ * @wusbhc: the WUSB host controller
+ * @wusb_dev: the device whose PTK the TKID is for
+ * (or NULL for a TKID for a GTK)
+ *
+ * The generated TKID consist of two parts: the device's authenicated
+ * address (or 0 or a GTK); and an incrementing number. This ensures
+ * that TKIDs cannot be shared between devices and by the time the
+ * incrementing number wraps around the older TKIDs will no longer be
+ * in use (a maximum of two keys may be active at any one time).
+ */
+static u32 wusbhc_next_tkid(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
+{
+ u32 *tkid;
+ u32 addr;
+
+ if (wusb_dev == NULL) {
+ tkid = &wusbhc->gtk_tkid;
+ addr = 0;
+ } else {
+ tkid = &wusb_port_by_idx(wusbhc, wusb_dev->port_idx)->ptk_tkid;
+ addr = wusb_dev->addr & 0x7f;
+ }
+
+ *tkid = (addr << 8) | ((*tkid + 1) & 0xff);
+
+ return *tkid;
+}
+
+static void wusbhc_generate_gtk(struct wusbhc *wusbhc)
+{
+ const size_t key_size = sizeof(wusbhc->gtk.data);
+ u32 tkid;
+
+ tkid = wusbhc_next_tkid(wusbhc, NULL);
+
+ wusbhc->gtk.descr.tTKID[0] = (tkid >> 0) & 0xff;
+ wusbhc->gtk.descr.tTKID[1] = (tkid >> 8) & 0xff;
+ wusbhc->gtk.descr.tTKID[2] = (tkid >> 16) & 0xff;
+
+ get_random_bytes(wusbhc->gtk.descr.bKeyData, key_size);
+}
+
+/**
+ * wusbhc_sec_start - start the security management process
+ * @wusbhc: the WUSB host controller
+ *
+ * Generate and set an initial GTK on the host controller.
+ *
+ * Called when the HC is started.
+ */
+int wusbhc_sec_start(struct wusbhc *wusbhc)
+{
+ const size_t key_size = sizeof(wusbhc->gtk.data);
+ int result;
+
+ wusbhc_generate_gtk(wusbhc);
+
+ result = wusbhc->set_gtk(wusbhc, wusbhc->gtk_tkid,
+ &wusbhc->gtk.descr.bKeyData, key_size);
+ if (result < 0)
+ dev_err(wusbhc->dev, "cannot set GTK for the host: %d\n",
+ result);
+
+ return result;
+}
+
+/**
+ * wusbhc_sec_stop - stop the security management process
+ * @wusbhc: the WUSB host controller
+ *
+ * Wait for any pending GTK rekeys to stop.
+ */
+void wusbhc_sec_stop(struct wusbhc *wusbhc)
+{
+ cancel_work_sync(&wusbhc->gtk_rekey_done_work);
+}
+
+
+/** @returns encryption type name */
+const char *wusb_et_name(u8 x)
+{
+ switch (x) {
+ case USB_ENC_TYPE_UNSECURE: return "unsecure";
+ case USB_ENC_TYPE_WIRED: return "wired";
+ case USB_ENC_TYPE_CCM_1: return "CCM-1";
+ case USB_ENC_TYPE_RSA_1: return "RSA-1";
+ default: return "unknown";
+ }
+}
+EXPORT_SYMBOL_GPL(wusb_et_name);
+
+/*
+ * Set the device encryption method
+ *
+ * We tell the device which encryption method to use; we do this when
+ * setting up the device's security.
+ */
+static int wusb_dev_set_encryption(struct usb_device *usb_dev, int value)
+{
+ int result;
+ struct device *dev = &usb_dev->dev;
+ struct wusb_dev *wusb_dev = usb_dev->wusb_dev;
+
+ if (value) {
+ value = wusb_dev->ccm1_etd.bEncryptionValue;
+ } else {
+ /* FIXME: should be wusb_dev->etd[UNSECURE].bEncryptionValue */
+ value = 0;
+ }
+ /* Set device's */
+ result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
+ USB_REQ_SET_ENCRYPTION,
+ USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+ value, 0, NULL, 0, 1000 /* FIXME: arbitrary */);
+ if (result < 0)
+ dev_err(dev, "Can't set device's WUSB encryption to "
+ "%s (value %d): %d\n",
+ wusb_et_name(wusb_dev->ccm1_etd.bEncryptionType),
+ wusb_dev->ccm1_etd.bEncryptionValue, result);
+ return result;
+}
+
+/*
+ * Set the GTK to be used by a device.
+ *
+ * The device must be authenticated.
+ */
+static int wusb_dev_set_gtk(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev)
+{
+ struct usb_device *usb_dev = wusb_dev->usb_dev;
+
+ return usb_control_msg(
+ usb_dev, usb_sndctrlpipe(usb_dev, 0),
+ USB_REQ_SET_DESCRIPTOR,
+ USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+ USB_DT_KEY << 8 | wusbhc->gtk_index.value, 0,
+ &wusbhc->gtk.descr, wusbhc->gtk.descr.bLength,
+ 1000);
+}
+
+
+/* FIXME: prototype for adding security */
+int wusb_dev_sec_add(struct wusbhc *wusbhc,
+ struct usb_device *usb_dev, struct wusb_dev *wusb_dev)
+{
+ int result, bytes, secd_size;
+ struct device *dev = &usb_dev->dev;
+ struct usb_security_descriptor secd;
+ const struct usb_encryption_descriptor *etd, *ccm1_etd = NULL;
+ void *secd_buf;
+ const void *itr, *top;
+ char buf[64];
+
+ d_fnstart(3, dev, "(usb_dev %p, wusb_dev %p)\n", usb_dev, wusb_dev);
+ result = usb_get_descriptor(usb_dev, USB_DT_SECURITY,
+ 0, &secd, sizeof(secd));
+ if (result < sizeof(secd)) {
+ dev_err(dev, "Can't read security descriptor or "
+ "not enough data: %d\n", result);
+ goto error_secd;
+ }
+ secd_size = le16_to_cpu(secd.wTotalLength);
+ d_printf(5, dev, "got %d bytes of sec descriptor, total is %d\n",
+ result, secd_size);
+ secd_buf = kmalloc(secd_size, GFP_KERNEL);
+ if (secd_buf == NULL) {
+ dev_err(dev, "Can't allocate space for security descriptors\n");
+ goto error_secd_alloc;
+ }
+ result = usb_get_descriptor(usb_dev, USB_DT_SECURITY,
+ 0, secd_buf, secd_size);
+ if (result < secd_size) {
+ dev_err(dev, "Can't read security descriptor or "
+ "not enough data: %d\n", result);
+ goto error_secd_all;
+ }
+ d_printf(5, dev, "got %d bytes of sec descriptors\n", result);
+ bytes = 0;
+ itr = secd_buf + sizeof(secd);
+ top = secd_buf + result;
+ while (itr < top) {
+ etd = itr;
+ if (top - itr < sizeof(*etd)) {
+ dev_err(dev, "BUG: bad device security descriptor; "
+ "not enough data (%zu vs %zu bytes left)\n",
+ top - itr, sizeof(*etd));
+ break;
+ }
+ if (etd->bLength < sizeof(*etd)) {
+ dev_err(dev, "BUG: bad device encryption descriptor; "
+ "descriptor is too short "
+ "(%u vs %zu needed)\n",
+ etd->bLength, sizeof(*etd));
+ break;
+ }
+ itr += etd->bLength;
+ bytes += snprintf(buf + bytes, sizeof(buf) - bytes,
+ "%s (0x%02x/%02x) ",
+ wusb_et_name(etd->bEncryptionType),
+ etd->bEncryptionValue, etd->bAuthKeyIndex);
+ if (etd->bEncryptionType == USB_ENC_TYPE_CCM_1)
+ ccm1_etd = etd;
+ }
+ /* This code only supports CCM1 as of now. */
+ /* FIXME: user has to choose which sec mode to use?
+ * In theory we want CCM */
+ if (ccm1_etd == NULL) {
+ dev_err(dev, "WUSB device doesn't support CCM1 encryption, "
+ "can't use!\n");
+ result = -EINVAL;
+ goto error_no_ccm1;
+ }
+ wusb_dev->ccm1_etd = *ccm1_etd;
+ dev_info(dev, "supported encryption: %s; using %s (0x%02x/%02x)\n",
+ buf, wusb_et_name(ccm1_etd->bEncryptionType),
+ ccm1_etd->bEncryptionValue, ccm1_etd->bAuthKeyIndex);
+ result = 0;
+ kfree(secd_buf);
+out:
+ d_fnend(3, dev, "(usb_dev %p, wusb_dev %p) = %d\n",
+ usb_dev, wusb_dev, result);
+ return result;
+
+
+error_no_ccm1:
+error_secd_all:
+ kfree(secd_buf);
+error_secd_alloc:
+error_secd:
+ goto out;
+}
+
+void wusb_dev_sec_rm(struct wusb_dev *wusb_dev)
+{
+ /* Nothing so far */
+}
+
+static void hs_printk(unsigned level, struct device *dev,
+ struct usb_handshake *hs)
+{
+ d_printf(level, dev,
+ " bMessageNumber: %u\n"
+ " bStatus: %u\n"
+ " tTKID: %02x %02x %02x\n"
+ " CDID: %02x %02x %02x %02x %02x %02x %02x %02x\n"
+ " %02x %02x %02x %02x %02x %02x %02x %02x\n"
+ " nonce: %02x %02x %02x %02x %02x %02x %02x %02x\n"
+ " %02x %02x %02x %02x %02x %02x %02x %02x\n"
+ " MIC: %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ hs->bMessageNumber, hs->bStatus,
+ hs->tTKID[2], hs->tTKID[1], hs->tTKID[0],
+ hs->CDID[0], hs->CDID[1], hs->CDID[2], hs->CDID[3],
+ hs->CDID[4], hs->CDID[5], hs->CDID[6], hs->CDID[7],
+ hs->CDID[8], hs->CDID[9], hs->CDID[10], hs->CDID[11],
+ hs->CDID[12], hs->CDID[13], hs->CDID[14], hs->CDID[15],
+ hs->nonce[0], hs->nonce[1], hs->nonce[2], hs->nonce[3],
+ hs->nonce[4], hs->nonce[5], hs->nonce[6], hs->nonce[7],
+ hs->nonce[8], hs->nonce[9], hs->nonce[10], hs->nonce[11],
+ hs->nonce[12], hs->nonce[13], hs->nonce[14], hs->nonce[15],
+ hs->MIC[0], hs->MIC[1], hs->MIC[2], hs->MIC[3],
+ hs->MIC[4], hs->MIC[5], hs->MIC[6], hs->MIC[7]);
+}
+
+/**
+ * Update the address of an unauthenticated WUSB device
+ *
+ * Once we have successfully authenticated, we take it to addr0 state
+ * and then to a normal address.
+ *
+ * Before the device's address (as known by it) was usb_dev->devnum |
+ * 0x80 (unauthenticated address). With this we update it to usb_dev->devnum.
+ */
+static int wusb_dev_update_address(struct wusbhc *wusbhc,
+ struct wusb_dev *wusb_dev)
+{
+ int result = -ENOMEM;
+ struct usb_device *usb_dev = wusb_dev->usb_dev;
+ struct device *dev = &usb_dev->dev;
+ u8 new_address = wusb_dev->addr & 0x7F;
+
+ /* Set address 0 */
+ result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
+ USB_REQ_SET_ADDRESS, 0,
+ 0, 0, NULL, 0, 1000 /* FIXME: arbitrary */);
+ if (result < 0) {
+ dev_err(dev, "auth failed: can't set address 0: %d\n",
+ result);
+ goto error_addr0;
+ }
+ result = wusb_set_dev_addr(wusbhc, wusb_dev, 0);
+ if (result < 0)
+ goto error_addr0;
+ usb_ep0_reinit(usb_dev);
+
+ /* Set new (authenticated) address. */
+ result = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
+ USB_REQ_SET_ADDRESS, 0,
+ new_address, 0, NULL, 0,
+ 1000 /* FIXME: arbitrary */);
+ if (result < 0) {
+ dev_err(dev, "auth failed: can't set address %u: %d\n",
+ new_address, result);
+ goto error_addr;
+ }
+ result = wusb_set_dev_addr(wusbhc, wusb_dev, new_address);
+ if (result < 0)
+ goto error_addr;
+ usb_ep0_reinit(usb_dev);
+ usb_dev->authenticated = 1;
+error_addr:
+error_addr0:
+ return result;
+}
+
+/*
+ *
+ *
+ */
+/* FIXME: split and cleanup */
+int wusb_dev_4way_handshake(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev,
+ struct wusb_ckhdid *chid, struct wusb_ckhdid *cdid,
+ struct wusb_ckhdid *ck)
+{
+ int result = -ENOMEM;
+ struct usb_device *usb_dev = wusb_dev->usb_dev;
+ struct device *dev = &usb_dev->dev;
+ u32 tkid;
+ __le32 tkid_le;
+ struct usb_handshake *hs;
+ struct aes_ccm_nonce ccm_n;
+ u8 mic[8];
+ struct wusb_keydvt_in keydvt_in;
+ struct wusb_keydvt_out keydvt_out;
+
+ hs = kzalloc(3*sizeof(hs[0]), GFP_KERNEL);
+ if (hs == NULL) {
+ dev_err(dev, "can't allocate handshake data\n");
+ goto error_kzalloc;
+ }
+
+ /* We need to turn encryption before beginning the 4way
+ * hshake (WUSB1.0[.3.2.2]) */
+ result = wusb_dev_set_encryption(usb_dev, 1);
+ if (result < 0)
+ goto error_dev_set_encryption;
+
+ tkid = wusbhc_next_tkid(wusbhc, wusb_dev);
+ tkid_le = cpu_to_le32(tkid);
+
+ hs[0].bMessageNumber = 1;
+ hs[0].bStatus = 0;
+ memcpy(hs[0].tTKID, &tkid_le, sizeof(hs[0].tTKID));
+ hs[0].bReserved = 0;
+ memcpy(hs[0].CDID, cdid, sizeof(hs[0].CDID));
+ get_random_bytes(&hs[0].nonce, sizeof(hs[0].nonce));
+ memset(hs[0].MIC, 0, sizeof(hs[0].MIC)); /* Per WUSB1.0[T7-22] */
+
+ d_printf(1, dev, "I: sending hs1:\n");
+ hs_printk(2, dev, &hs[0]);
+
+ result = usb_control_msg(
+ usb_dev, usb_sndctrlpipe(usb_dev, 0),
+ USB_REQ_SET_HANDSHAKE,
+ USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+ 1, 0, &hs[0], sizeof(hs[0]), 1000 /* FIXME: arbitrary */);
+ if (result < 0) {
+ dev_err(dev, "Handshake1: request failed: %d\n", result);
+ goto error_hs1;
+ }
+
+ /* Handshake 2, from the device -- need to verify fields */
+ result = usb_control_msg(
+ usb_dev, usb_rcvctrlpipe(usb_dev, 0),
+ USB_REQ_GET_HANDSHAKE,
+ USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+ 2, 0, &hs[1], sizeof(hs[1]), 1000 /* FIXME: arbitrary */);
+ if (result < 0) {
+ dev_err(dev, "Handshake2: request failed: %d\n", result);
+ goto error_hs2;
+ }
+ d_printf(1, dev, "got HS2:\n");
+ hs_printk(2, dev, &hs[1]);
+
+ result = -EINVAL;
+ if (hs[1].bMessageNumber != 2) {
+ dev_err(dev, "Handshake2 failed: bad message number %u\n",
+ hs[1].bMessageNumber);
+ goto error_hs2;
+ }
+ if (hs[1].bStatus != 0) {
+ dev_err(dev, "Handshake2 failed: bad status %u\n",
+ hs[1].bStatus);
+ goto error_hs2;
+ }
+ if (memcmp(hs[0].tTKID, hs[1].tTKID, sizeof(hs[0].tTKID))) {
+ dev_err(dev, "Handshake2 failed: TKID mismatch "
+ "(#1 0x%02x%02x%02x vs #2 0x%02x%02x%02x)\n",
+ hs[0].tTKID[0], hs[0].tTKID[1], hs[0].tTKID[2],
+ hs[1].tTKID[0], hs[1].tTKID[1], hs[1].tTKID[2]);
+ goto error_hs2;
+ }
+ if (memcmp(hs[0].CDID, hs[1].CDID, sizeof(hs[0].CDID))) {
+ dev_err(dev, "Handshake2 failed: CDID mismatch\n");
+ goto error_hs2;
+ }
+
+ /* Setup the CCM nonce */
+ memset(&ccm_n.sfn, 0, sizeof(ccm_n.sfn)); /* Per WUSB1.0[6.5.2] */
+ memcpy(ccm_n.tkid, &tkid_le, sizeof(ccm_n.tkid));
+ ccm_n.src_addr = wusbhc->uwb_rc->uwb_dev.dev_addr;
+ ccm_n.dest_addr.data[0] = wusb_dev->addr;
+ ccm_n.dest_addr.data[1] = 0;
+
+ /* Derive the KCK and PTK from CK, the CCM, H and D nonces */
+ memcpy(keydvt_in.hnonce, hs[0].nonce, sizeof(keydvt_in.hnonce));
+ memcpy(keydvt_in.dnonce, hs[1].nonce, sizeof(keydvt_in.dnonce));
+ result = wusb_key_derive(&keydvt_out, ck->data, &ccm_n, &keydvt_in);
+ if (result < 0) {
+ dev_err(dev, "Handshake2 failed: cannot derive keys: %d\n",
+ result);
+ goto error_hs2;
+ }
+ d_printf(2, dev, "KCK:\n");
+ d_dump(2, dev, keydvt_out.kck, sizeof(keydvt_out.kck));
+ d_printf(2, dev, "PTK:\n");
+ d_dump(2, dev, keydvt_out.ptk, sizeof(keydvt_out.ptk));
+
+ /* Compute MIC and verify it */
+ result = wusb_oob_mic(mic, keydvt_out.kck, &ccm_n, &hs[1]);
+ if (result < 0) {
+ dev_err(dev, "Handshake2 failed: cannot compute MIC: %d\n",
+ result);
+ goto error_hs2;
+ }
+
+ d_printf(2, dev, "MIC:\n");
+ d_dump(2, dev, mic, sizeof(mic));
+ if (memcmp(hs[1].MIC, mic, sizeof(hs[1].MIC))) {
+ dev_err(dev, "Handshake2 failed: MIC mismatch\n");
+ goto error_hs2;
+ }
+
+ /* Send Handshake3 */
+ hs[2].bMessageNumber = 3;
+ hs[2].bStatus = 0;
+ memcpy(hs[2].tTKID, &tkid_le, sizeof(hs[2].tTKID));
+ hs[2].bReserved = 0;
+ memcpy(hs[2].CDID, cdid, sizeof(hs[2].CDID));
+ memcpy(hs[2].nonce, hs[0].nonce, sizeof(hs[2].nonce));
+ result = wusb_oob_mic(hs[2].MIC, keydvt_out.kck, &ccm_n, &hs[2]);
+ if (result < 0) {
+ dev_err(dev, "Handshake3 failed: cannot compute MIC: %d\n",
+ result);
+ goto error_hs2;
+ }
+
+ d_printf(1, dev, "I: sending hs3:\n");
+ hs_printk(2, dev, &hs[2]);
+
+ result = usb_control_msg(
+ usb_dev, usb_sndctrlpipe(usb_dev, 0),
+ USB_REQ_SET_HANDSHAKE,
+ USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+ 3, 0, &hs[2], sizeof(hs[2]), 1000 /* FIXME: arbitrary */);
+ if (result < 0) {
+ dev_err(dev, "Handshake3: request failed: %d\n", result);
+ goto error_hs3;
+ }
+
+ d_printf(1, dev, "I: turning on encryption on host for device\n");
+ d_dump(2, dev, keydvt_out.ptk, sizeof(keydvt_out.ptk));
+ result = wusbhc->set_ptk(wusbhc, wusb_dev->port_idx, tkid,
+ keydvt_out.ptk, sizeof(keydvt_out.ptk));
+ if (result < 0)
+ goto error_wusbhc_set_ptk;
+
+ d_printf(1, dev, "I: setting a GTK\n");
+ result = wusb_dev_set_gtk(wusbhc, wusb_dev);
+ if (result < 0) {
+ dev_err(dev, "Set GTK for device: request failed: %d\n",
+ result);
+ goto error_wusbhc_set_gtk;
+ }
+
+ /* Update the device's address from unauth to auth */
+ if (usb_dev->authenticated == 0) {
+ d_printf(1, dev, "I: updating addres to auth from non-auth\n");
+ result = wusb_dev_update_address(wusbhc, wusb_dev);
+ if (result < 0)
+ goto error_dev_update_address;
+ }
+ result = 0;
+ d_printf(1, dev, "I: 4way handshke done, device authenticated\n");
+
+error_dev_update_address:
+error_wusbhc_set_gtk:
+error_wusbhc_set_ptk:
+error_hs3:
+error_hs2:
+error_hs1:
+ memset(hs, 0, 3*sizeof(hs[0]));
+ memset(&keydvt_out, 0, sizeof(keydvt_out));
+ memset(&keydvt_in, 0, sizeof(keydvt_in));
+ memset(&ccm_n, 0, sizeof(ccm_n));
+ memset(mic, 0, sizeof(mic));
+ if (result < 0) {
+ /* error path */
+ wusb_dev_set_encryption(usb_dev, 0);
+ }
+error_dev_set_encryption:
+ kfree(hs);
+error_kzalloc:
+ return result;
+}
+
+/*
+ * Once all connected and authenticated devices have received the new
+ * GTK, switch the host to using it.
+ */
+static void wusbhc_gtk_rekey_done_work(struct work_struct *work)
+{
+ struct wusbhc *wusbhc = container_of(work, struct wusbhc, gtk_rekey_done_work);
+ size_t key_size = sizeof(wusbhc->gtk.data);
+
+ mutex_lock(&wusbhc->mutex);
+
+ if (--wusbhc->pending_set_gtks == 0)
+ wusbhc->set_gtk(wusbhc, wusbhc->gtk_tkid, &wusbhc->gtk.descr.bKeyData, key_size);
+
+ mutex_unlock(&wusbhc->mutex);
+}
+
+static void wusbhc_set_gtk_callback(struct urb *urb)
+{
+ struct wusbhc *wusbhc = urb->context;
+
+ queue_work(wusbd, &wusbhc->gtk_rekey_done_work);
+}
+
+/**
+ * wusbhc_gtk_rekey - generate and distribute a new GTK
+ * @wusbhc: the WUSB host controller
+ *
+ * Generate a new GTK and distribute it to all connected and
+ * authenticated devices. When all devices have the new GTK, the host
+ * starts using it.
+ *
+ * This must be called after every device disconnect (see [WUSB]
+ * section 6.2.11.2).
+ */
+void wusbhc_gtk_rekey(struct wusbhc *wusbhc)
+{
+ static const size_t key_size = sizeof(wusbhc->gtk.data);
+ int p;
+
+ wusbhc_generate_gtk(wusbhc);
+
+ for (p = 0; p < wusbhc->ports_max; p++) {
+ struct wusb_dev *wusb_dev;
+
+ wusb_dev = wusbhc->port[p].wusb_dev;
+ if (!wusb_dev || !wusb_dev->usb_dev | !wusb_dev->usb_dev->authenticated)
+ continue;
+
+ usb_fill_control_urb(wusb_dev->set_gtk_urb, wusb_dev->usb_dev,
+ usb_sndctrlpipe(wusb_dev->usb_dev, 0),
+ (void *)wusb_dev->set_gtk_req,
+ &wusbhc->gtk.descr, wusbhc->gtk.descr.bLength,
+ wusbhc_set_gtk_callback, wusbhc);
+ if (usb_submit_urb(wusb_dev->set_gtk_urb, GFP_KERNEL) == 0)
+ wusbhc->pending_set_gtks++;
+ }
+ if (wusbhc->pending_set_gtks == 0)
+ wusbhc->set_gtk(wusbhc, wusbhc->gtk_tkid, &wusbhc->gtk.descr.bKeyData, key_size);
+}
diff --git a/drivers/usb/wusbcore/wa-hc.c b/drivers/usb/wusbcore/wa-hc.c
new file mode 100644
index 000000000000..c0b167f915ad
--- /dev/null
+++ b/drivers/usb/wusbcore/wa-hc.c
@@ -0,0 +1,83 @@
+/*
+ * Wire Adapter Host Controller Driver
+ * Common items to HWA and DWA based HCDs
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ */
+#include "wa-hc.h"
+
+/**
+ * Assumes
+ *
+ * wa->usb_dev and wa->usb_iface initialized and refcounted,
+ * wa->wa_descr initialized.
+ */
+int wa_create(struct wahc *wa, struct usb_interface *iface)
+{
+ int result;
+ struct device *dev = &iface->dev;
+
+ result = wa_rpipes_create(wa);
+ if (result < 0)
+ goto error_rpipes_create;
+ /* Fill up Data Transfer EP pointers */
+ wa->dti_epd = &iface->cur_altsetting->endpoint[1].desc;
+ wa->dto_epd = &iface->cur_altsetting->endpoint[2].desc;
+ wa->xfer_result_size = le16_to_cpu(wa->dti_epd->wMaxPacketSize);
+ wa->xfer_result = kmalloc(wa->xfer_result_size, GFP_KERNEL);
+ if (wa->xfer_result == NULL)
+ goto error_xfer_result_alloc;
+ result = wa_nep_create(wa, iface);
+ if (result < 0) {
+ dev_err(dev, "WA-CDS: can't initialize notif endpoint: %d\n",
+ result);
+ goto error_nep_create;
+ }
+ return 0;
+
+error_nep_create:
+ kfree(wa->xfer_result);
+error_xfer_result_alloc:
+ wa_rpipes_destroy(wa);
+error_rpipes_create:
+ return result;
+}
+EXPORT_SYMBOL_GPL(wa_create);
+
+
+void __wa_destroy(struct wahc *wa)
+{
+ if (wa->dti_urb) {
+ usb_kill_urb(wa->dti_urb);
+ usb_put_urb(wa->dti_urb);
+ usb_kill_urb(wa->buf_in_urb);
+ usb_put_urb(wa->buf_in_urb);
+ }
+ kfree(wa->xfer_result);
+ wa_nep_destroy(wa);
+ wa_rpipes_destroy(wa);
+}
+EXPORT_SYMBOL_GPL(__wa_destroy);
+
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("Wireless USB Wire Adapter core");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/wusbcore/wa-hc.h b/drivers/usb/wusbcore/wa-hc.h
new file mode 100644
index 000000000000..d0b6022fbb4a
--- /dev/null
+++ b/drivers/usb/wusbcore/wa-hc.h
@@ -0,0 +1,416 @@
+/*
+ * HWA Host Controller Driver
+ * Wire Adapter Control/Data Streaming Iface (WUSB1.0[8])
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * This driver implements a USB Host Controller (struct usb_hcd) for a
+ * Wireless USB Host Controller based on the Wireless USB 1.0
+ * Host-Wire-Adapter specification (in layman terms, a USB-dongle that
+ * implements a Wireless USB host).
+ *
+ * Check out the Design-overview.txt file in the source documentation
+ * for other details on the implementation.
+ *
+ * Main blocks:
+ *
+ * driver glue with the driver API, workqueue daemon
+ *
+ * lc RC instance life cycle management (create, destroy...)
+ *
+ * hcd glue with the USB API Host Controller Interface API.
+ *
+ * nep Notification EndPoint managent: collect notifications
+ * and queue them with the workqueue daemon.
+ *
+ * Handle notifications as coming from the NEP. Sends them
+ * off others to their respective modules (eg: connect,
+ * disconnect and reset go to devconnect).
+ *
+ * rpipe Remote Pipe management; rpipe is what we use to write
+ * to an endpoint on a WUSB device that is connected to a
+ * HWA RC.
+ *
+ * xfer Transfer managment -- this is all the code that gets a
+ * buffer and pushes it to a device (or viceversa). *
+ *
+ * Some day a lot of this code will be shared between this driver and
+ * the drivers for DWA (xfer, rpipe).
+ *
+ * All starts at driver.c:hwahc_probe(), when one of this guys is
+ * connected. hwahc_disconnect() stops it.
+ *
+ * During operation, the main driver is devices connecting or
+ * disconnecting. They cause the HWA RC to send notifications into
+ * nep.c:hwahc_nep_cb() that will dispatch them to
+ * notif.c:wa_notif_dispatch(). From there they will fan to cause
+ * device connects, disconnects, etc.
+ *
+ * Note much of the activity is difficult to follow. For example a
+ * device connect goes to devconnect, which will cause the "fake" root
+ * hub port to show a connect and stop there. Then khubd will notice
+ * and call into the rh.c:hwahc_rc_port_reset() code to authenticate
+ * the device (and this might require user intervention) and enable
+ * the port.
+ *
+ * We also have a timer workqueue going from devconnect.c that
+ * schedules in hwahc_devconnect_create().
+ *
+ * The rest of the traffic is in the usual entry points of a USB HCD,
+ * which are hooked up in driver.c:hwahc_rc_driver, and defined in
+ * hcd.c.
+ */
+
+#ifndef __HWAHC_INTERNAL_H__
+#define __HWAHC_INTERNAL_H__
+
+#include <linux/completion.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/uwb.h>
+#include <linux/usb/wusb.h>
+#include <linux/usb/wusb-wa.h>
+
+struct wusbhc;
+struct wahc;
+extern void wa_urb_enqueue_run(struct work_struct *ws);
+
+/**
+ * RPipe instance
+ *
+ * @descr's fields are kept in LE, as we need to send it back and
+ * forth.
+ *
+ * @wa is referenced when set
+ *
+ * @segs_available is the number of requests segments that still can
+ * be submitted to the controller without overloading
+ * it. It is initialized to descr->wRequests when
+ * aiming.
+ *
+ * A rpipe supports a max of descr->wRequests at the same time; before
+ * submitting seg_lock has to be taken. If segs_avail > 0, then we can
+ * submit; if not, we have to queue them.
+ */
+struct wa_rpipe {
+ struct kref refcnt;
+ struct usb_rpipe_descriptor descr;
+ struct usb_host_endpoint *ep;
+ struct wahc *wa;
+ spinlock_t seg_lock;
+ struct list_head seg_list;
+ atomic_t segs_available;
+ u8 buffer[1]; /* For reads/writes on USB */
+};
+
+
+/**
+ * Instance of a HWA Host Controller
+ *
+ * Except where a more specific lock/mutex applies or atomic, all
+ * fields protected by @mutex.
+ *
+ * @wa_descr Can be accessed without locking because it is in
+ * the same area where the device descriptors were
+ * read, so it is guaranteed to exist umodified while
+ * the device exists.
+ *
+ * Endianess has been converted to CPU's.
+ *
+ * @nep_* can be accessed without locking as its processing is
+ * serialized; we submit a NEP URB and it comes to
+ * hwahc_nep_cb(), which won't issue another URB until it is
+ * done processing it.
+ *
+ * @xfer_list:
+ *
+ * List of active transfers to verify existence from a xfer id
+ * gotten from the xfer result message. Can't use urb->list because
+ * it goes by endpoint, and we don't know the endpoint at the time
+ * when we get the xfer result message. We can't really rely on the
+ * pointer (will have to change for 64 bits) as the xfer id is 32 bits.
+ *
+ * @xfer_delayed_list: List of transfers that need to be started
+ * (with a workqueue, because they were
+ * submitted from an atomic context).
+ *
+ * FIXME: this needs to be layered up: a wusbhc layer (for sharing
+ * comonalities with WHCI), a wa layer (for sharing
+ * comonalities with DWA-RC).
+ */
+struct wahc {
+ struct usb_device *usb_dev;
+ struct usb_interface *usb_iface;
+
+ /* HC to deliver notifications */
+ union {
+ struct wusbhc *wusb;
+ struct dwahc *dwa;
+ };
+
+ const struct usb_endpoint_descriptor *dto_epd, *dti_epd;
+ const struct usb_wa_descriptor *wa_descr;
+
+ struct urb *nep_urb; /* Notification EndPoint [lockless] */
+ struct edc nep_edc;
+ void *nep_buffer;
+ size_t nep_buffer_size;
+
+ atomic_t notifs_queued;
+
+ u16 rpipes;
+ unsigned long *rpipe_bm; /* rpipe usage bitmap */
+ spinlock_t rpipe_bm_lock; /* protect rpipe_bm */
+ struct mutex rpipe_mutex; /* assigning resources to endpoints */
+
+ struct urb *dti_urb; /* URB for reading xfer results */
+ struct urb *buf_in_urb; /* URB for reading data in */
+ struct edc dti_edc; /* DTI error density counter */
+ struct wa_xfer_result *xfer_result; /* real size = dti_ep maxpktsize */
+ size_t xfer_result_size;
+
+ s32 status; /* For reading status */
+
+ struct list_head xfer_list;
+ struct list_head xfer_delayed_list;
+ spinlock_t xfer_list_lock;
+ struct work_struct xfer_work;
+ atomic_t xfer_id_count;
+};
+
+
+extern int wa_create(struct wahc *wa, struct usb_interface *iface);
+extern void __wa_destroy(struct wahc *wa);
+
+
+/* Miscellaneous constants */
+enum {
+ /** Max number of EPROTO errors we tolerate on the NEP in a
+ * period of time */
+ HWAHC_EPROTO_MAX = 16,
+ /** Period of time for EPROTO errors (in jiffies) */
+ HWAHC_EPROTO_PERIOD = 4 * HZ,
+};
+
+
+/* Notification endpoint handling */
+extern int wa_nep_create(struct wahc *, struct usb_interface *);
+extern void wa_nep_destroy(struct wahc *);
+
+static inline int wa_nep_arm(struct wahc *wa, gfp_t gfp_mask)
+{
+ struct urb *urb = wa->nep_urb;
+ urb->transfer_buffer = wa->nep_buffer;
+ urb->transfer_buffer_length = wa->nep_buffer_size;
+ return usb_submit_urb(urb, gfp_mask);
+}
+
+static inline void wa_nep_disarm(struct wahc *wa)
+{
+ usb_kill_urb(wa->nep_urb);
+}
+
+
+/* RPipes */
+static inline void wa_rpipe_init(struct wahc *wa)
+{
+ spin_lock_init(&wa->rpipe_bm_lock);
+ mutex_init(&wa->rpipe_mutex);
+}
+
+static inline void wa_init(struct wahc *wa)
+{
+ edc_init(&wa->nep_edc);
+ atomic_set(&wa->notifs_queued, 0);
+ wa_rpipe_init(wa);
+ edc_init(&wa->dti_edc);
+ INIT_LIST_HEAD(&wa->xfer_list);
+ INIT_LIST_HEAD(&wa->xfer_delayed_list);
+ spin_lock_init(&wa->xfer_list_lock);
+ INIT_WORK(&wa->xfer_work, wa_urb_enqueue_run);
+ atomic_set(&wa->xfer_id_count, 1);
+}
+
+/**
+ * Destroy a pipe (when refcount drops to zero)
+ *
+ * Assumes it has been moved to the "QUIESCING" state.
+ */
+struct wa_xfer;
+extern void rpipe_destroy(struct kref *_rpipe);
+static inline
+void __rpipe_get(struct wa_rpipe *rpipe)
+{
+ kref_get(&rpipe->refcnt);
+}
+extern int rpipe_get_by_ep(struct wahc *, struct usb_host_endpoint *,
+ struct urb *, gfp_t);
+static inline void rpipe_put(struct wa_rpipe *rpipe)
+{
+ kref_put(&rpipe->refcnt, rpipe_destroy);
+
+}
+extern void rpipe_ep_disable(struct wahc *, struct usb_host_endpoint *);
+extern int wa_rpipes_create(struct wahc *);
+extern void wa_rpipes_destroy(struct wahc *);
+static inline void rpipe_avail_dec(struct wa_rpipe *rpipe)
+{
+ atomic_dec(&rpipe->segs_available);
+}
+
+/**
+ * Returns true if the rpipe is ready to submit more segments.
+ */
+static inline int rpipe_avail_inc(struct wa_rpipe *rpipe)
+{
+ return atomic_inc_return(&rpipe->segs_available) > 0
+ && !list_empty(&rpipe->seg_list);
+}
+
+
+/* Transferring data */
+extern int wa_urb_enqueue(struct wahc *, struct usb_host_endpoint *,
+ struct urb *, gfp_t);
+extern int wa_urb_dequeue(struct wahc *, struct urb *);
+extern void wa_handle_notif_xfer(struct wahc *, struct wa_notif_hdr *);
+
+
+/* Misc
+ *
+ * FIXME: Refcounting for the actual @hwahc object is not correct; I
+ * mean, this should be refcounting on the HCD underneath, but
+ * it is not. In any case, the semantics for HCD refcounting
+ * are *weird*...on refcount reaching zero it just frees
+ * it...no RC specific function is called...unless I miss
+ * something.
+ *
+ * FIXME: has to go away in favour of an 'struct' hcd based sollution
+ */
+static inline struct wahc *wa_get(struct wahc *wa)
+{
+ usb_get_intf(wa->usb_iface);
+ return wa;
+}
+
+static inline void wa_put(struct wahc *wa)
+{
+ usb_put_intf(wa->usb_iface);
+}
+
+
+static inline int __wa_feature(struct wahc *wa, unsigned op, u16 feature)
+{
+ return usb_control_msg(wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ op ? USB_REQ_SET_FEATURE : USB_REQ_CLEAR_FEATURE,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ feature,
+ wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+ NULL, 0, 1000 /* FIXME: arbitrary */);
+}
+
+
+static inline int __wa_set_feature(struct wahc *wa, u16 feature)
+{
+ return __wa_feature(wa, 1, feature);
+}
+
+
+static inline int __wa_clear_feature(struct wahc *wa, u16 feature)
+{
+ return __wa_feature(wa, 0, feature);
+}
+
+
+/**
+ * Return the status of a Wire Adapter
+ *
+ * @wa: Wire Adapter instance
+ * @returns < 0 errno code on error, or status bitmap as described
+ * in WUSB1.0[8.3.1.6].
+ *
+ * NOTE: need malloc, some arches don't take USB from the stack
+ */
+static inline
+s32 __wa_get_status(struct wahc *wa)
+{
+ s32 result;
+ result = usb_control_msg(
+ wa->usb_dev, usb_rcvctrlpipe(wa->usb_dev, 0),
+ USB_REQ_GET_STATUS,
+ USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, wa->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+ &wa->status, sizeof(wa->status),
+ 1000 /* FIXME: arbitrary */);
+ if (result >= 0)
+ result = wa->status;
+ return result;
+}
+
+
+/**
+ * Waits until the Wire Adapter's status matches @mask/@value
+ *
+ * @wa: Wire Adapter instance.
+ * @returns < 0 errno code on error, otherwise status.
+ *
+ * Loop until the WAs status matches the mask and value (status & mask
+ * == value). Timeout if it doesn't happen.
+ *
+ * FIXME: is there an official specification on how long status
+ * changes can take?
+ */
+static inline s32 __wa_wait_status(struct wahc *wa, u32 mask, u32 value)
+{
+ s32 result;
+ unsigned loops = 10;
+ do {
+ msleep(50);
+ result = __wa_get_status(wa);
+ if ((result & mask) == value)
+ break;
+ if (loops-- == 0) {
+ result = -ETIMEDOUT;
+ break;
+ }
+ } while (result >= 0);
+ return result;
+}
+
+
+/** Command @hwahc to stop, @returns 0 if ok, < 0 errno code on error */
+static inline int __wa_stop(struct wahc *wa)
+{
+ int result;
+ struct device *dev = &wa->usb_iface->dev;
+
+ result = __wa_clear_feature(wa, WA_ENABLE);
+ if (result < 0 && result != -ENODEV) {
+ dev_err(dev, "error commanding HC to stop: %d\n", result);
+ goto out;
+ }
+ result = __wa_wait_status(wa, WA_ENABLE, 0);
+ if (result < 0 && result != -ENODEV)
+ dev_err(dev, "error waiting for HC to stop: %d\n", result);
+out:
+ return 0;
+}
+
+
+#endif /* #ifndef __HWAHC_INTERNAL_H__ */
diff --git a/drivers/usb/wusbcore/wa-nep.c b/drivers/usb/wusbcore/wa-nep.c
new file mode 100644
index 000000000000..b0f3d65f2746
--- /dev/null
+++ b/drivers/usb/wusbcore/wa-nep.c
@@ -0,0 +1,313 @@
+/*
+ * WUSB Wire Adapter: Control/Data Streaming Interface (WUSB[8])
+ * Notification EndPoint support
+ *
+ * Copyright (C) 2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * This part takes care of getting the notification from the hw
+ * only and dispatching through wusbwad into
+ * wa_notif_dispatch. Handling is done there.
+ *
+ * WA notifications are limited in size; most of them are three or
+ * four bytes long, and the longest is the HWA Device Notification,
+ * which would not exceed 38 bytes (DNs are limited in payload to 32
+ * bytes plus 3 bytes header (WUSB1.0[7.6p2]), plus 3 bytes HWA
+ * header (WUSB1.0[8.5.4.2]).
+ *
+ * It is not clear if more than one Device Notification can be packed
+ * in a HWA Notification, I assume no because of the wording in
+ * WUSB1.0[8.5.4.2]. In any case, the bigger any notification could
+ * get is 256 bytes (as the bLength field is a byte).
+ *
+ * So what we do is we have this buffer and read into it; when a
+ * notification arrives we schedule work to a specific, single thread
+ * workqueue (so notifications are serialized) and copy the
+ * notification data. After scheduling the work, we rearm the read from
+ * the notification endpoint.
+ *
+ * Entry points here are:
+ *
+ * wa_nep_[create|destroy]() To initialize/release this subsystem
+ *
+ * wa_nep_cb() Callback for the notification
+ * endpoint; when data is ready, this
+ * does the dispatching.
+ */
+#include <linux/workqueue.h>
+#include <linux/ctype.h>
+#include <linux/uwb/debug.h>
+#include "wa-hc.h"
+#include "wusbhc.h"
+
+/* Structure for queueing notifications to the workqueue */
+struct wa_notif_work {
+ struct work_struct work;
+ struct wahc *wa;
+ size_t size;
+ u8 data[];
+};
+
+/*
+ * Process incoming notifications from the WA's Notification EndPoint
+ * [the wuswad daemon, basically]
+ *
+ * @_nw: Pointer to a descriptor which has the pointer to the
+ * @wa, the size of the buffer and the work queue
+ * structure (so we can free all when done).
+ * @returns 0 if ok, < 0 errno code on error.
+ *
+ * All notifications follow the same format; they need to start with a
+ * 'struct wa_notif_hdr' header, so it is easy to parse through
+ * them. We just break the buffer in individual notifications (the
+ * standard doesn't say if it can be done or is forbidden, so we are
+ * cautious) and dispatch each.
+ *
+ * So the handling layers are is:
+ *
+ * WA specific notification (from NEP)
+ * Device Notification Received -> wa_handle_notif_dn()
+ * WUSB Device notification generic handling
+ * BPST Adjustment -> wa_handle_notif_bpst_adj()
+ * ... -> ...
+ *
+ * @wa has to be referenced
+ */
+static void wa_notif_dispatch(struct work_struct *ws)
+{
+ void *itr;
+ u8 missing = 0;
+ struct wa_notif_work *nw = container_of(ws, struct wa_notif_work, work);
+ struct wahc *wa = nw->wa;
+ struct wa_notif_hdr *notif_hdr;
+ size_t size;
+
+ struct device *dev = &wa->usb_iface->dev;
+
+#if 0
+ /* FIXME: need to check for this??? */
+ if (usb_hcd->state == HC_STATE_QUIESCING) /* Going down? */
+ goto out; /* screw it */
+#endif
+ atomic_dec(&wa->notifs_queued); /* Throttling ctl */
+ dev = &wa->usb_iface->dev;
+ size = nw->size;
+ itr = nw->data;
+
+ while (size) {
+ if (size < sizeof(*notif_hdr)) {
+ missing = sizeof(*notif_hdr) - size;
+ goto exhausted_buffer;
+ }
+ notif_hdr = itr;
+ if (size < notif_hdr->bLength)
+ goto exhausted_buffer;
+ itr += notif_hdr->bLength;
+ size -= notif_hdr->bLength;
+ /* Dispatch the notification [don't use itr or size!] */
+ switch (notif_hdr->bNotifyType) {
+ case HWA_NOTIF_DN: {
+ struct hwa_notif_dn *hwa_dn;
+ hwa_dn = container_of(notif_hdr, struct hwa_notif_dn,
+ hdr);
+ if (hwa_dn->secure)
+ dev_info(dev, "HWA DN: secure = %u\n",
+ hwa_dn->secure);
+ wusbhc_handle_dn(wa->wusb, hwa_dn->bSourceDeviceAddr,
+ hwa_dn->dndata,
+ notif_hdr->bLength - sizeof(*hwa_dn));
+ break;
+ }
+ case WA_NOTIF_TRANSFER:
+ wa_handle_notif_xfer(wa, notif_hdr);
+ break;
+ case DWA_NOTIF_RWAKE:
+ case DWA_NOTIF_PORTSTATUS:
+ case HWA_NOTIF_BPST_ADJ:
+ /* FIXME: unimplemented WA NOTIFs */
+ /* fallthru */
+ default:
+ if (printk_ratelimit()) {
+ dev_err(dev, "HWA: unknown notification 0x%x, "
+ "%zu bytes; discarding\n",
+ notif_hdr->bNotifyType,
+ (size_t)notif_hdr->bLength);
+ dump_bytes(dev, notif_hdr, 16);
+ }
+ break;
+ }
+ }
+out:
+ wa_put(wa);
+ kfree(nw);
+ return;
+
+ /* THIS SHOULD NOT HAPPEN
+ *
+ * Buffer exahusted with partial data remaining; just warn and
+ * discard the data, as this should not happen.
+ */
+exhausted_buffer:
+ if (!printk_ratelimit())
+ goto out;
+ dev_warn(dev, "HWA: device sent short notification, "
+ "%d bytes missing; discarding %d bytes.\n",
+ missing, (int)size);
+ dump_bytes(dev, itr, size);
+ goto out;
+}
+
+/*
+ * Deliver incoming WA notifications to the wusbwa workqueue
+ *
+ * @wa: Pointer the Wire Adapter Controller Data Streaming
+ * instance (part of an 'struct usb_hcd').
+ * @size: Size of the received buffer
+ * @returns 0 if ok, < 0 errno code on error.
+ *
+ * The input buffer is @wa->nep_buffer, with @size bytes
+ * (guaranteed to fit in the allocated space,
+ * @wa->nep_buffer_size).
+ */
+static int wa_nep_queue(struct wahc *wa, size_t size)
+{
+ int result = 0;
+ struct device *dev = &wa->usb_iface->dev;
+ struct wa_notif_work *nw;
+
+ /* dev_fnstart(dev, "(wa %p, size %zu)\n", wa, size); */
+ BUG_ON(size > wa->nep_buffer_size);
+ if (size == 0)
+ goto out;
+ if (atomic_read(&wa->notifs_queued) > 200) {
+ if (printk_ratelimit())
+ dev_err(dev, "Too many notifications queued, "
+ "throttling back\n");
+ goto out;
+ }
+ nw = kzalloc(sizeof(*nw) + size, GFP_ATOMIC);
+ if (nw == NULL) {
+ if (printk_ratelimit())
+ dev_err(dev, "No memory to queue notification\n");
+ goto out;
+ }
+ INIT_WORK(&nw->work, wa_notif_dispatch);
+ nw->wa = wa_get(wa);
+ nw->size = size;
+ memcpy(nw->data, wa->nep_buffer, size);
+ atomic_inc(&wa->notifs_queued); /* Throttling ctl */
+ queue_work(wusbd, &nw->work);
+out:
+ /* dev_fnend(dev, "(wa %p, size %zu) = result\n", wa, size, result); */
+ return result;
+}
+
+/*
+ * Callback for the notification event endpoint
+ *
+ * Check's that everything is fine and then passes the data to be
+ * queued to the workqueue.
+ */
+static void wa_nep_cb(struct urb *urb)
+{
+ int result;
+ struct wahc *wa = urb->context;
+ struct device *dev = &wa->usb_iface->dev;
+
+ switch (result = urb->status) {
+ case 0:
+ result = wa_nep_queue(wa, urb->actual_length);
+ if (result < 0)
+ dev_err(dev, "NEP: unable to process notification(s): "
+ "%d\n", result);
+ break;
+ case -ECONNRESET: /* Not an error, but a controlled situation; */
+ case -ENOENT: /* (we killed the URB)...so, no broadcast */
+ case -ESHUTDOWN:
+ dev_dbg(dev, "NEP: going down %d\n", urb->status);
+ goto out;
+ default: /* On general errors, we retry unless it gets ugly */
+ if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS,
+ EDC_ERROR_TIMEFRAME)) {
+ dev_err(dev, "NEP: URB max acceptable errors "
+ "exceeded, resetting device\n");
+ usb_dev_reset_delayed(wa->usb_dev);
+ goto out;
+ }
+ dev_err(dev, "NEP: URB error %d\n", urb->status);
+ }
+ result = wa_nep_arm(wa, GFP_ATOMIC);
+ if (result < 0) {
+ dev_err(dev, "NEP: cannot submit URB: %d\n", result);
+ usb_dev_reset_delayed(wa->usb_dev);
+ }
+out:
+ return;
+}
+
+/*
+ * Initialize @wa's notification and event's endpoint stuff
+ *
+ * This includes the allocating the read buffer, the context ID
+ * allocation bitmap, the URB and submitting the URB.
+ */
+int wa_nep_create(struct wahc *wa, struct usb_interface *iface)
+{
+ int result;
+ struct usb_endpoint_descriptor *epd;
+ struct usb_device *usb_dev = interface_to_usbdev(iface);
+ struct device *dev = &iface->dev;
+
+ edc_init(&wa->nep_edc);
+ epd = &iface->cur_altsetting->endpoint[0].desc;
+ wa->nep_buffer_size = 1024;
+ wa->nep_buffer = kmalloc(wa->nep_buffer_size, GFP_KERNEL);
+ if (wa->nep_buffer == NULL) {
+ dev_err(dev, "Unable to allocate notification's read buffer\n");
+ goto error_nep_buffer;
+ }
+ wa->nep_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (wa->nep_urb == NULL) {
+ dev_err(dev, "Unable to allocate notification URB\n");
+ goto error_urb_alloc;
+ }
+ usb_fill_int_urb(wa->nep_urb, usb_dev,
+ usb_rcvintpipe(usb_dev, epd->bEndpointAddress),
+ wa->nep_buffer, wa->nep_buffer_size,
+ wa_nep_cb, wa, epd->bInterval);
+ result = wa_nep_arm(wa, GFP_KERNEL);
+ if (result < 0) {
+ dev_err(dev, "Cannot submit notification URB: %d\n", result);
+ goto error_nep_arm;
+ }
+ return 0;
+
+error_nep_arm:
+ usb_free_urb(wa->nep_urb);
+error_urb_alloc:
+ kfree(wa->nep_buffer);
+error_nep_buffer:
+ return -ENOMEM;
+}
+
+void wa_nep_destroy(struct wahc *wa)
+{
+ wa_nep_disarm(wa);
+ usb_free_urb(wa->nep_urb);
+ kfree(wa->nep_buffer);
+}
diff --git a/drivers/usb/wusbcore/wa-rpipe.c b/drivers/usb/wusbcore/wa-rpipe.c
new file mode 100644
index 000000000000..eb78b9d0b268
--- /dev/null
+++ b/drivers/usb/wusbcore/wa-rpipe.c
@@ -0,0 +1,562 @@
+/*
+ * WUSB Wire Adapter
+ * rpipe management
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ *
+ * RPIPE
+ *
+ * Targetted at different downstream endpoints
+ *
+ * Descriptor: use to config the remote pipe.
+ *
+ * The number of blocks could be dynamic (wBlocks in descriptor is
+ * 0)--need to schedule them then.
+ *
+ * Each bit in wa->rpipe_bm represents if an rpipe is being used or
+ * not. Rpipes are represented with a 'struct wa_rpipe' that is
+ * attached to the hcpriv member of a 'struct usb_host_endpoint'.
+ *
+ * When you need to xfer data to an endpoint, you get an rpipe for it
+ * with wa_ep_rpipe_get(), which gives you a reference to the rpipe
+ * and keeps a single one (the first one) with the endpoint. When you
+ * are done transferring, you drop that reference. At the end the
+ * rpipe is always allocated and bound to the endpoint. There it might
+ * be recycled when not used.
+ *
+ * Addresses:
+ *
+ * We use a 1:1 mapping mechanism between port address (0 based
+ * index, actually) and the address. The USB stack knows about this.
+ *
+ * USB Stack port number 4 (1 based)
+ * WUSB code port index 3 (0 based)
+ * USB Addresss 5 (2 based -- 0 is for default, 1 for root hub)
+ *
+ * Now, because we don't use the concept as default address exactly
+ * like the (wired) USB code does, we need to kind of skip it. So we
+ * never take addresses from the urb->pipe, but from the
+ * urb->dev->devnum, to make sure that we always have the right
+ * destination address.
+ */
+#include <linux/init.h>
+#include <asm/atomic.h>
+#include <linux/bitmap.h>
+#include "wusbhc.h"
+#include "wa-hc.h"
+
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+
+static int __rpipe_get_descr(struct wahc *wa,
+ struct usb_rpipe_descriptor *descr, u16 index)
+{
+ ssize_t result;
+ struct device *dev = &wa->usb_iface->dev;
+
+ /* Get the RPIPE descriptor -- we cannot use the usb_get_descriptor()
+ * function because the arguments are different.
+ */
+ d_printf(1, dev, "rpipe %u: get descr\n", index);
+ result = usb_control_msg(
+ wa->usb_dev, usb_rcvctrlpipe(wa->usb_dev, 0),
+ USB_REQ_GET_DESCRIPTOR,
+ USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_RPIPE,
+ USB_DT_RPIPE<<8, index, descr, sizeof(*descr),
+ 1000 /* FIXME: arbitrary */);
+ if (result < 0) {
+ dev_err(dev, "rpipe %u: get descriptor failed: %d\n",
+ index, (int)result);
+ goto error;
+ }
+ if (result < sizeof(*descr)) {
+ dev_err(dev, "rpipe %u: got short descriptor "
+ "(%zd vs %zd bytes needed)\n",
+ index, result, sizeof(*descr));
+ result = -EINVAL;
+ goto error;
+ }
+ result = 0;
+
+error:
+ return result;
+}
+
+/*
+ *
+ * The descriptor is assumed to be properly initialized (ie: you got
+ * it through __rpipe_get_descr()).
+ */
+static int __rpipe_set_descr(struct wahc *wa,
+ struct usb_rpipe_descriptor *descr, u16 index)
+{
+ ssize_t result;
+ struct device *dev = &wa->usb_iface->dev;
+
+ /* we cannot use the usb_get_descriptor() function because the
+ * arguments are different.
+ */
+ d_printf(1, dev, "rpipe %u: set descr\n", index);
+ result = usb_control_msg(
+ wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ USB_REQ_SET_DESCRIPTOR,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE,
+ USB_DT_RPIPE<<8, index, descr, sizeof(*descr),
+ HZ / 10);
+ if (result < 0) {
+ dev_err(dev, "rpipe %u: set descriptor failed: %d\n",
+ index, (int)result);
+ goto error;
+ }
+ if (result < sizeof(*descr)) {
+ dev_err(dev, "rpipe %u: sent short descriptor "
+ "(%zd vs %zd bytes required)\n",
+ index, result, sizeof(*descr));
+ result = -EINVAL;
+ goto error;
+ }
+ result = 0;
+
+error:
+ return result;
+
+}
+
+static void rpipe_init(struct wa_rpipe *rpipe)
+{
+ kref_init(&rpipe->refcnt);
+ spin_lock_init(&rpipe->seg_lock);
+ INIT_LIST_HEAD(&rpipe->seg_list);
+}
+
+static unsigned rpipe_get_idx(struct wahc *wa, unsigned rpipe_idx)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&wa->rpipe_bm_lock, flags);
+ rpipe_idx = find_next_zero_bit(wa->rpipe_bm, wa->rpipes, rpipe_idx);
+ if (rpipe_idx < wa->rpipes)
+ set_bit(rpipe_idx, wa->rpipe_bm);
+ spin_unlock_irqrestore(&wa->rpipe_bm_lock, flags);
+
+ return rpipe_idx;
+}
+
+static void rpipe_put_idx(struct wahc *wa, unsigned rpipe_idx)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&wa->rpipe_bm_lock, flags);
+ clear_bit(rpipe_idx, wa->rpipe_bm);
+ spin_unlock_irqrestore(&wa->rpipe_bm_lock, flags);
+}
+
+void rpipe_destroy(struct kref *_rpipe)
+{
+ struct wa_rpipe *rpipe = container_of(_rpipe, struct wa_rpipe, refcnt);
+ u8 index = le16_to_cpu(rpipe->descr.wRPipeIndex);
+ d_fnstart(1, NULL, "(rpipe %p %u)\n", rpipe, index);
+ if (rpipe->ep)
+ rpipe->ep->hcpriv = NULL;
+ rpipe_put_idx(rpipe->wa, index);
+ wa_put(rpipe->wa);
+ kfree(rpipe);
+ d_fnend(1, NULL, "(rpipe %p %u)\n", rpipe, index);
+}
+EXPORT_SYMBOL_GPL(rpipe_destroy);
+
+/*
+ * Locate an idle rpipe, create an structure for it and return it
+ *
+ * @wa is referenced and unlocked
+ * @crs enum rpipe_attr, required endpoint characteristics
+ *
+ * The rpipe can be used only sequentially (not in parallel).
+ *
+ * The rpipe is moved into the "ready" state.
+ */
+static int rpipe_get_idle(struct wa_rpipe **prpipe, struct wahc *wa, u8 crs,
+ gfp_t gfp)
+{
+ int result;
+ unsigned rpipe_idx;
+ struct wa_rpipe *rpipe;
+ struct device *dev = &wa->usb_iface->dev;
+
+ d_fnstart(3, dev, "(wa %p crs 0x%02x)\n", wa, crs);
+ rpipe = kzalloc(sizeof(*rpipe), gfp);
+ if (rpipe == NULL)
+ return -ENOMEM;
+ rpipe_init(rpipe);
+
+ /* Look for an idle pipe */
+ for (rpipe_idx = 0; rpipe_idx < wa->rpipes; rpipe_idx++) {
+ rpipe_idx = rpipe_get_idx(wa, rpipe_idx);
+ if (rpipe_idx >= wa->rpipes) /* no more pipes :( */
+ break;
+ result = __rpipe_get_descr(wa, &rpipe->descr, rpipe_idx);
+ if (result < 0)
+ dev_err(dev, "Can't get descriptor for rpipe %u: %d\n",
+ rpipe_idx, result);
+ else if ((rpipe->descr.bmCharacteristics & crs) != 0)
+ goto found;
+ rpipe_put_idx(wa, rpipe_idx);
+ }
+ *prpipe = NULL;
+ kfree(rpipe);
+ d_fnend(3, dev, "(wa %p crs 0x%02x) = -ENXIO\n", wa, crs);
+ return -ENXIO;
+
+found:
+ set_bit(rpipe_idx, wa->rpipe_bm);
+ rpipe->wa = wa_get(wa);
+ *prpipe = rpipe;
+ d_fnstart(3, dev, "(wa %p crs 0x%02x) = 0\n", wa, crs);
+ return 0;
+}
+
+static int __rpipe_reset(struct wahc *wa, unsigned index)
+{
+ int result;
+ struct device *dev = &wa->usb_iface->dev;
+
+ d_printf(1, dev, "rpipe %u: reset\n", index);
+ result = usb_control_msg(
+ wa->usb_dev, usb_sndctrlpipe(wa->usb_dev, 0),
+ USB_REQ_RPIPE_RESET,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_RPIPE,
+ 0, index, NULL, 0, 1000 /* FIXME: arbitrary */);
+ if (result < 0)
+ dev_err(dev, "rpipe %u: reset failed: %d\n",
+ index, result);
+ return result;
+}
+
+/*
+ * Fake companion descriptor for ep0
+ *
+ * See WUSB1.0[7.4.4], most of this is zero for bulk/int/ctl
+ */
+static struct usb_wireless_ep_comp_descriptor epc0 = {
+ .bLength = sizeof(epc0),
+ .bDescriptorType = USB_DT_WIRELESS_ENDPOINT_COMP,
+/* .bMaxBurst = 1, */
+ .bMaxSequence = 31,
+};
+
+/*
+ * Look for EP companion descriptor
+ *
+ * Get there, look for Inara in the endpoint's extra descriptors
+ */
+static struct usb_wireless_ep_comp_descriptor *rpipe_epc_find(
+ struct device *dev, struct usb_host_endpoint *ep)
+{
+ void *itr;
+ size_t itr_size;
+ struct usb_descriptor_header *hdr;
+ struct usb_wireless_ep_comp_descriptor *epcd;
+
+ d_fnstart(3, dev, "(ep %p)\n", ep);
+ if (ep->desc.bEndpointAddress == 0) {
+ epcd = &epc0;
+ goto out;
+ }
+ itr = ep->extra;
+ itr_size = ep->extralen;
+ epcd = NULL;
+ while (itr_size > 0) {
+ if (itr_size < sizeof(*hdr)) {
+ dev_err(dev, "HW Bug? ep 0x%02x: extra descriptors "
+ "at offset %zu: only %zu bytes left\n",
+ ep->desc.bEndpointAddress,
+ itr - (void *) ep->extra, itr_size);
+ break;
+ }
+ hdr = itr;
+ if (hdr->bDescriptorType == USB_DT_WIRELESS_ENDPOINT_COMP) {
+ epcd = itr;
+ break;
+ }
+ if (hdr->bLength > itr_size) {
+ dev_err(dev, "HW Bug? ep 0x%02x: extra descriptor "
+ "at offset %zu (type 0x%02x) "
+ "length %d but only %zu bytes left\n",
+ ep->desc.bEndpointAddress,
+ itr - (void *) ep->extra, hdr->bDescriptorType,
+ hdr->bLength, itr_size);
+ break;
+ }
+ itr += hdr->bLength;
+ itr_size -= hdr->bDescriptorType;
+ }
+out:
+ d_fnend(3, dev, "(ep %p) = %p\n", ep, epcd);
+ return epcd;
+}
+
+/*
+ * Aim an rpipe to its device & endpoint destination
+ *
+ * Make sure we change the address to unauthenticathed if the device
+ * is WUSB and it is not authenticated.
+ */
+static int rpipe_aim(struct wa_rpipe *rpipe, struct wahc *wa,
+ struct usb_host_endpoint *ep, struct urb *urb, gfp_t gfp)
+{
+ int result = -ENOMSG; /* better code for lack of companion? */
+ struct device *dev = &wa->usb_iface->dev;
+ struct usb_device *usb_dev = urb->dev;
+ struct usb_wireless_ep_comp_descriptor *epcd;
+ u8 unauth;
+
+ d_fnstart(3, dev, "(rpipe %p wa %p ep %p, urb %p)\n",
+ rpipe, wa, ep, urb);
+ epcd = rpipe_epc_find(dev, ep);
+ if (epcd == NULL) {
+ dev_err(dev, "ep 0x%02x: can't find companion descriptor\n",
+ ep->desc.bEndpointAddress);
+ goto error;
+ }
+ unauth = usb_dev->wusb && !usb_dev->authenticated? 0x80 : 0;
+ __rpipe_reset(wa, le16_to_cpu(rpipe->descr.wRPipeIndex));
+ atomic_set(&rpipe->segs_available, le16_to_cpu(rpipe->descr.wRequests));
+ /* FIXME: block allocation system; request with queuing and timeout */
+ /* FIXME: compute so seg_size > ep->maxpktsize */
+ rpipe->descr.wBlocks = cpu_to_le16(16); /* given */
+ /* ep0 maxpktsize is 0x200 (WUSB1.0[4.8.1]) */
+ rpipe->descr.wMaxPacketSize = cpu_to_le16(ep->desc.wMaxPacketSize);
+ rpipe->descr.bHSHubAddress = 0; /* reserved: zero */
+ rpipe->descr.bHSHubPort = wusb_port_no_to_idx(urb->dev->portnum);
+ /* FIXME: use maximum speed as supported or recommended by device */
+ rpipe->descr.bSpeed = usb_pipeendpoint(urb->pipe) == 0?
+ UWB_PHY_RATE_53 : UWB_PHY_RATE_200;
+ d_printf(2, dev, "addr %u (0x%02x) rpipe #%u ep# %u speed %d\n",
+ urb->dev->devnum, urb->dev->devnum | unauth,
+ le16_to_cpu(rpipe->descr.wRPipeIndex),
+ usb_pipeendpoint(urb->pipe), rpipe->descr.bSpeed);
+ /* see security.c:wusb_update_address() */
+ if (unlikely(urb->dev->devnum == 0x80))
+ rpipe->descr.bDeviceAddress = 0;
+ else
+ rpipe->descr.bDeviceAddress = urb->dev->devnum | unauth;
+ rpipe->descr.bEndpointAddress = ep->desc.bEndpointAddress;
+ /* FIXME: bDataSequence */
+ rpipe->descr.bDataSequence = 0;
+ /* FIXME: dwCurrentWindow */
+ rpipe->descr.dwCurrentWindow = cpu_to_le32(1);
+ /* FIXME: bMaxDataSequence */
+ rpipe->descr.bMaxDataSequence = epcd->bMaxSequence - 1;
+ rpipe->descr.bInterval = ep->desc.bInterval;
+ /* FIXME: bOverTheAirInterval */
+ rpipe->descr.bOverTheAirInterval = 0; /* 0 if not isoc */
+ /* FIXME: xmit power & preamble blah blah */
+ rpipe->descr.bmAttribute = ep->desc.bmAttributes & 0x03;
+ /* rpipe->descr.bmCharacteristics RO */
+ /* FIXME: bmRetryOptions */
+ rpipe->descr.bmRetryOptions = 15;
+ /* FIXME: use for assessing link quality? */
+ rpipe->descr.wNumTransactionErrors = 0;
+ result = __rpipe_set_descr(wa, &rpipe->descr,
+ le16_to_cpu(rpipe->descr.wRPipeIndex));
+ if (result < 0) {
+ dev_err(dev, "Cannot aim rpipe: %d\n", result);
+ goto error;
+ }
+ result = 0;
+error:
+ d_fnend(3, dev, "(rpipe %p wa %p ep %p urb %p) = %d\n",
+ rpipe, wa, ep, urb, result);
+ return result;
+}
+
+/*
+ * Check an aimed rpipe to make sure it points to where we want
+ *
+ * We use bit 19 of the Linux USB pipe bitmap for unauth vs auth
+ * space; when it is like that, we or 0x80 to make an unauth address.
+ */
+static int rpipe_check_aim(const struct wa_rpipe *rpipe, const struct wahc *wa,
+ const struct usb_host_endpoint *ep,
+ const struct urb *urb, gfp_t gfp)
+{
+ int result = 0; /* better code for lack of companion? */
+ struct device *dev = &wa->usb_iface->dev;
+ struct usb_device *usb_dev = urb->dev;
+ u8 unauth = (usb_dev->wusb && !usb_dev->authenticated)? 0x80 : 0;
+ u8 portnum = wusb_port_no_to_idx(urb->dev->portnum);
+
+ d_fnstart(3, dev, "(rpipe %p wa %p ep %p, urb %p)\n",
+ rpipe, wa, ep, urb);
+#define AIM_CHECK(rdf, val, text) \
+ do { \
+ if (rpipe->descr.rdf != (val)) { \
+ dev_err(dev, \
+ "rpipe aim discrepancy: " #rdf " " text "\n", \
+ rpipe->descr.rdf, (val)); \
+ result = -EINVAL; \
+ WARN_ON(1); \
+ } \
+ } while (0)
+ AIM_CHECK(wMaxPacketSize, cpu_to_le16(ep->desc.wMaxPacketSize),
+ "(%u vs %u)");
+ AIM_CHECK(bHSHubPort, portnum, "(%u vs %u)");
+ AIM_CHECK(bSpeed, usb_pipeendpoint(urb->pipe) == 0 ?
+ UWB_PHY_RATE_53 : UWB_PHY_RATE_200,
+ "(%u vs %u)");
+ AIM_CHECK(bDeviceAddress, urb->dev->devnum | unauth, "(%u vs %u)");
+ AIM_CHECK(bEndpointAddress, ep->desc.bEndpointAddress, "(%u vs %u)");
+ AIM_CHECK(bInterval, ep->desc.bInterval, "(%u vs %u)");
+ AIM_CHECK(bmAttribute, ep->desc.bmAttributes & 0x03, "(%u vs %u)");
+#undef AIM_CHECK
+ return result;
+}
+
+#ifndef CONFIG_BUG
+#define CONFIG_BUG 0
+#endif
+
+/*
+ * Make sure there is an rpipe allocated for an endpoint
+ *
+ * If already allocated, we just refcount it; if not, we get an
+ * idle one, aim it to the right location and take it.
+ *
+ * Attaches to ep->hcpriv and rpipe->ep to ep.
+ */
+int rpipe_get_by_ep(struct wahc *wa, struct usb_host_endpoint *ep,
+ struct urb *urb, gfp_t gfp)
+{
+ int result = 0;
+ struct device *dev = &wa->usb_iface->dev;
+ struct wa_rpipe *rpipe;
+ u8 eptype;
+
+ d_fnstart(3, dev, "(wa %p ep %p urb %p gfp 0x%08x)\n", wa, ep, urb,
+ gfp);
+ mutex_lock(&wa->rpipe_mutex);
+ rpipe = ep->hcpriv;
+ if (rpipe != NULL) {
+ if (CONFIG_BUG == 1) {
+ result = rpipe_check_aim(rpipe, wa, ep, urb, gfp);
+ if (result < 0)
+ goto error;
+ }
+ __rpipe_get(rpipe);
+ d_printf(2, dev, "ep 0x%02x: reusing rpipe %u\n",
+ ep->desc.bEndpointAddress,
+ le16_to_cpu(rpipe->descr.wRPipeIndex));
+ } else {
+ /* hmm, assign idle rpipe, aim it */
+ result = -ENOBUFS;
+ eptype = ep->desc.bmAttributes & 0x03;
+ result = rpipe_get_idle(&rpipe, wa, 1 << eptype, gfp);
+ if (result < 0)
+ goto error;
+ result = rpipe_aim(rpipe, wa, ep, urb, gfp);
+ if (result < 0) {
+ rpipe_put(rpipe);
+ goto error;
+ }
+ ep->hcpriv = rpipe;
+ rpipe->ep = ep;
+ __rpipe_get(rpipe); /* for caching into ep->hcpriv */
+ d_printf(2, dev, "ep 0x%02x: using rpipe %u\n",
+ ep->desc.bEndpointAddress,
+ le16_to_cpu(rpipe->descr.wRPipeIndex));
+ }
+ d_dump(4, dev, &rpipe->descr, sizeof(rpipe->descr));
+error:
+ mutex_unlock(&wa->rpipe_mutex);
+ d_fnend(3, dev, "(wa %p ep %p urb %p gfp 0x%08x)\n", wa, ep, urb, gfp);
+ return result;
+}
+
+/*
+ * Allocate the bitmap for each rpipe.
+ */
+int wa_rpipes_create(struct wahc *wa)
+{
+ wa->rpipes = wa->wa_descr->wNumRPipes;
+ wa->rpipe_bm = kzalloc(BITS_TO_LONGS(wa->rpipes)*sizeof(unsigned long),
+ GFP_KERNEL);
+ if (wa->rpipe_bm == NULL)
+ return -ENOMEM;
+ return 0;
+}
+
+void wa_rpipes_destroy(struct wahc *wa)
+{
+ struct device *dev = &wa->usb_iface->dev;
+ d_fnstart(3, dev, "(wa %p)\n", wa);
+ if (!bitmap_empty(wa->rpipe_bm, wa->rpipes)) {
+ char buf[256];
+ WARN_ON(1);
+ bitmap_scnprintf(buf, sizeof(buf), wa->rpipe_bm, wa->rpipes);
+ dev_err(dev, "BUG: pipes not released on exit: %s\n", buf);
+ }
+ kfree(wa->rpipe_bm);
+ d_fnend(3, dev, "(wa %p)\n", wa);
+}
+
+/*
+ * Release resources allocated for an endpoint
+ *
+ * If there is an associated rpipe to this endpoint, Abort any pending
+ * transfers and put it. If the rpipe ends up being destroyed,
+ * __rpipe_destroy() will cleanup ep->hcpriv.
+ *
+ * This is called before calling hcd->stop(), so you don't need to do
+ * anything else in there.
+ */
+void rpipe_ep_disable(struct wahc *wa, struct usb_host_endpoint *ep)
+{
+ struct device *dev = &wa->usb_iface->dev;
+ struct wa_rpipe *rpipe;
+ d_fnstart(2, dev, "(wa %p ep %p)\n", wa, ep);
+ mutex_lock(&wa->rpipe_mutex);
+ rpipe = ep->hcpriv;
+ if (rpipe != NULL) {
+ unsigned rc = atomic_read(&rpipe->refcnt.refcount);
+ int result;
+ u16 index = le16_to_cpu(rpipe->descr.wRPipeIndex);
+
+ if (rc != 1)
+ d_printf(1, dev, "(wa %p ep %p) rpipe %p refcnt %u\n",
+ wa, ep, rpipe, rc);
+
+ d_printf(1, dev, "rpipe %u: abort\n", index);
+ result = usb_control_msg(
+ wa->usb_dev, usb_rcvctrlpipe(wa->usb_dev, 0),
+ USB_REQ_RPIPE_ABORT,
+ USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_RPIPE,
+ 0, index, NULL, 0, 1000 /* FIXME: arbitrary */);
+ if (result < 0 && result != -ENODEV /* dev is gone */)
+ d_printf(1, dev, "(wa %p rpipe %u): abort failed: %d\n",
+ wa, index, result);
+ rpipe_put(rpipe);
+ }
+ mutex_unlock(&wa->rpipe_mutex);
+ d_fnend(2, dev, "(wa %p ep %p)\n", wa, ep);
+ return;
+}
+EXPORT_SYMBOL_GPL(rpipe_ep_disable);
diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c
new file mode 100644
index 000000000000..e344f749ae4f
--- /dev/null
+++ b/drivers/usb/wusbcore/wa-xfer.c
@@ -0,0 +1,1709 @@
+/*
+ * WUSB Wire Adapter
+ * Data transfer and URB enqueing
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * How transfers work: get a buffer, break it up in segments (segment
+ * size is a multiple of the maxpacket size). For each segment issue a
+ * segment request (struct wa_xfer_*), then send the data buffer if
+ * out or nothing if in (all over the DTO endpoint).
+ *
+ * For each submitted segment request, a notification will come over
+ * the NEP endpoint and a transfer result (struct xfer_result) will
+ * arrive in the DTI URB. Read it, get the xfer ID, see if there is
+ * data coming (inbound transfer), schedule a read and handle it.
+ *
+ * Sounds simple, it is a pain to implement.
+ *
+ *
+ * ENTRY POINTS
+ *
+ * FIXME
+ *
+ * LIFE CYCLE / STATE DIAGRAM
+ *
+ * FIXME
+ *
+ * THIS CODE IS DISGUSTING
+ *
+ * Warned you are; it's my second try and still not happy with it.
+ *
+ * NOTES:
+ *
+ * - No iso
+ *
+ * - Supports DMA xfers, control, bulk and maybe interrupt
+ *
+ * - Does not recycle unused rpipes
+ *
+ * An rpipe is assigned to an endpoint the first time it is used,
+ * and then it's there, assigned, until the endpoint is disabled
+ * (destroyed [{h,d}wahc_op_ep_disable()]. The assignment of the
+ * rpipe to the endpoint is done under the wa->rpipe_sem semaphore
+ * (should be a mutex).
+ *
+ * Two methods it could be done:
+ *
+ * (a) set up a timer everytime an rpipe's use count drops to 1
+ * (which means unused) or when a transfer ends. Reset the
+ * timer when a xfer is queued. If the timer expires, release
+ * the rpipe [see rpipe_ep_disable()].
+ *
+ * (b) when looking for free rpipes to attach [rpipe_get_by_ep()],
+ * when none are found go over the list, check their endpoint
+ * and their activity record (if no last-xfer-done-ts in the
+ * last x seconds) take it
+ *
+ * However, due to the fact that we have a set of limited
+ * resources (max-segments-at-the-same-time per xfer,
+ * xfers-per-ripe, blocks-per-rpipe, rpipes-per-host), at the end
+ * we are going to have to rebuild all this based on an scheduler,
+ * to where we have a list of transactions to do and based on the
+ * availability of the different requried components (blocks,
+ * rpipes, segment slots, etc), we go scheduling them. Painful.
+ */
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/hash.h>
+#include "wa-hc.h"
+#include "wusbhc.h"
+
+#undef D_LOCAL
+#define D_LOCAL 0 /* 0 disabled, > 0 different levels... */
+#include <linux/uwb/debug.h>
+
+enum {
+ WA_SEGS_MAX = 255,
+};
+
+enum wa_seg_status {
+ WA_SEG_NOTREADY,
+ WA_SEG_READY,
+ WA_SEG_DELAYED,
+ WA_SEG_SUBMITTED,
+ WA_SEG_PENDING,
+ WA_SEG_DTI_PENDING,
+ WA_SEG_DONE,
+ WA_SEG_ERROR,
+ WA_SEG_ABORTED,
+};
+
+static void wa_xfer_delayed_run(struct wa_rpipe *);
+
+/*
+ * Life cycle governed by 'struct urb' (the refcount of the struct is
+ * that of the 'struct urb' and usb_free_urb() would free the whole
+ * struct).
+ */
+struct wa_seg {
+ struct urb urb;
+ struct urb *dto_urb; /* for data output? */
+ struct list_head list_node; /* for rpipe->req_list */
+ struct wa_xfer *xfer; /* out xfer */
+ u8 index; /* which segment we are */
+ enum wa_seg_status status;
+ ssize_t result; /* bytes xfered or error */
+ struct wa_xfer_hdr xfer_hdr;
+ u8 xfer_extra[]; /* xtra space for xfer_hdr_ctl */
+};
+
+static void wa_seg_init(struct wa_seg *seg)
+{
+ /* usb_init_urb() repeats a lot of work, so we do it here */
+ kref_init(&seg->urb.kref);
+}
+
+/*
+ * Protected by xfer->lock
+ *
+ */
+struct wa_xfer {
+ struct kref refcnt;
+ struct list_head list_node;
+ spinlock_t lock;
+ u32 id;
+
+ struct wahc *wa; /* Wire adapter we are plugged to */
+ struct usb_host_endpoint *ep;
+ struct urb *urb; /* URB we are transfering for */
+ struct wa_seg **seg; /* transfer segments */
+ u8 segs, segs_submitted, segs_done;
+ unsigned is_inbound:1;
+ unsigned is_dma:1;
+ size_t seg_size;
+ int result;
+
+ gfp_t gfp; /* allocation mask */
+
+ struct wusb_dev *wusb_dev; /* for activity timestamps */
+};
+
+static inline void wa_xfer_init(struct wa_xfer *xfer)
+{
+ kref_init(&xfer->refcnt);
+ INIT_LIST_HEAD(&xfer->list_node);
+ spin_lock_init(&xfer->lock);
+}
+
+/*
+ * Destory a transfer structure
+ *
+ * Note that the xfer->seg[index] thingies follow the URB life cycle,
+ * so we need to put them, not free them.
+ */
+static void wa_xfer_destroy(struct kref *_xfer)
+{
+ struct wa_xfer *xfer = container_of(_xfer, struct wa_xfer, refcnt);
+ if (xfer->seg) {
+ unsigned cnt;
+ for (cnt = 0; cnt < xfer->segs; cnt++) {
+ if (xfer->is_inbound)
+ usb_put_urb(xfer->seg[cnt]->dto_urb);
+ usb_put_urb(&xfer->seg[cnt]->urb);
+ }
+ }
+ kfree(xfer);
+ d_printf(2, NULL, "xfer %p destroyed\n", xfer);
+}
+
+static void wa_xfer_get(struct wa_xfer *xfer)
+{
+ kref_get(&xfer->refcnt);
+}
+
+static void wa_xfer_put(struct wa_xfer *xfer)
+{
+ d_fnstart(3, NULL, "(xfer %p) -- ref count bef put %d\n",
+ xfer, atomic_read(&xfer->refcnt.refcount));
+ kref_put(&xfer->refcnt, wa_xfer_destroy);
+ d_fnend(3, NULL, "(xfer %p) = void\n", xfer);
+}
+
+/*
+ * xfer is referenced
+ *
+ * xfer->lock has to be unlocked
+ *
+ * We take xfer->lock for setting the result; this is a barrier
+ * against drivers/usb/core/hcd.c:unlink1() being called after we call
+ * usb_hcd_giveback_urb() and wa_urb_dequeue() trying to get a
+ * reference to the transfer.
+ */
+static void wa_xfer_giveback(struct wa_xfer *xfer)
+{
+ unsigned long flags;
+ d_fnstart(3, NULL, "(xfer %p)\n", xfer);
+ spin_lock_irqsave(&xfer->wa->xfer_list_lock, flags);
+ list_del_init(&xfer->list_node);
+ spin_unlock_irqrestore(&xfer->wa->xfer_list_lock, flags);
+ /* FIXME: segmentation broken -- kills DWA */
+ wusbhc_giveback_urb(xfer->wa->wusb, xfer->urb, xfer->result);
+ wa_put(xfer->wa);
+ wa_xfer_put(xfer);
+ d_fnend(3, NULL, "(xfer %p) = void\n", xfer);
+}
+
+/*
+ * xfer is referenced
+ *
+ * xfer->lock has to be unlocked
+ */
+static void wa_xfer_completion(struct wa_xfer *xfer)
+{
+ d_fnstart(3, NULL, "(xfer %p)\n", xfer);
+ if (xfer->wusb_dev)
+ wusb_dev_put(xfer->wusb_dev);
+ rpipe_put(xfer->ep->hcpriv);
+ wa_xfer_giveback(xfer);
+ d_fnend(3, NULL, "(xfer %p) = void\n", xfer);
+ return;
+}
+
+/*
+ * If transfer is done, wrap it up and return true
+ *
+ * xfer->lock has to be locked
+ */
+static unsigned __wa_xfer_is_done(struct wa_xfer *xfer)
+{
+ unsigned result, cnt;
+ struct wa_seg *seg;
+ struct urb *urb = xfer->urb;
+ unsigned found_short = 0;
+
+ d_fnstart(3, NULL, "(xfer %p)\n", xfer);
+ result = xfer->segs_done == xfer->segs_submitted;
+ if (result == 0)
+ goto out;
+ urb->actual_length = 0;
+ for (cnt = 0; cnt < xfer->segs; cnt++) {
+ seg = xfer->seg[cnt];
+ switch (seg->status) {
+ case WA_SEG_DONE:
+ if (found_short && seg->result > 0) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "xfer %p#%u: bad short "
+ "segments (%zu)\n", xfer, cnt,
+ seg->result);
+ urb->status = -EINVAL;
+ goto out;
+ }
+ urb->actual_length += seg->result;
+ if (seg->result < xfer->seg_size
+ && cnt != xfer->segs-1)
+ found_short = 1;
+ d_printf(2, NULL, "xfer %p#%u: DONE short %d "
+ "result %zu urb->actual_length %d\n",
+ xfer, seg->index, found_short, seg->result,
+ urb->actual_length);
+ break;
+ case WA_SEG_ERROR:
+ xfer->result = seg->result;
+ d_printf(2, NULL, "xfer %p#%u: ERROR result %zu\n",
+ xfer, seg->index, seg->result);
+ goto out;
+ case WA_SEG_ABORTED:
+ WARN_ON(urb->status != -ECONNRESET
+ && urb->status != -ENOENT);
+ d_printf(2, NULL, "xfer %p#%u ABORTED: result %d\n",
+ xfer, seg->index, urb->status);
+ xfer->result = urb->status;
+ goto out;
+ default:
+ /* if (printk_ratelimit()) */
+ printk(KERN_ERR "xfer %p#%u: "
+ "is_done bad state %d\n",
+ xfer, cnt, seg->status);
+ xfer->result = -EINVAL;
+ WARN_ON(1);
+ goto out;
+ }
+ }
+ xfer->result = 0;
+out:
+ d_fnend(3, NULL, "(xfer %p) = void\n", xfer);
+ return result;
+}
+
+/*
+ * Initialize a transfer's ID
+ *
+ * We need to use a sequential number; if we use the pointer or the
+ * hash of the pointer, it can repeat over sequential transfers and
+ * then it will confuse the HWA....wonder why in hell they put a 32
+ * bit handle in there then.
+ */
+static void wa_xfer_id_init(struct wa_xfer *xfer)
+{
+ xfer->id = atomic_add_return(1, &xfer->wa->xfer_id_count);
+}
+
+/*
+ * Return the xfer's ID associated with xfer
+ *
+ * Need to generate a
+ */
+static u32 wa_xfer_id(struct wa_xfer *xfer)
+{
+ return xfer->id;
+}
+
+/*
+ * Search for a transfer list ID on the HCD's URB list
+ *
+ * For 32 bit architectures, we use the pointer itself; for 64 bits, a
+ * 32-bit hash of the pointer.
+ *
+ * @returns NULL if not found.
+ */
+static struct wa_xfer *wa_xfer_get_by_id(struct wahc *wa, u32 id)
+{
+ unsigned long flags;
+ struct wa_xfer *xfer_itr;
+ spin_lock_irqsave(&wa->xfer_list_lock, flags);
+ list_for_each_entry(xfer_itr, &wa->xfer_list, list_node) {
+ if (id == xfer_itr->id) {
+ wa_xfer_get(xfer_itr);
+ goto out;
+ }
+ }
+ xfer_itr = NULL;
+out:
+ spin_unlock_irqrestore(&wa->xfer_list_lock, flags);
+ return xfer_itr;
+}
+
+struct wa_xfer_abort_buffer {
+ struct urb urb;
+ struct wa_xfer_abort cmd;
+};
+
+static void __wa_xfer_abort_cb(struct urb *urb)
+{
+ struct wa_xfer_abort_buffer *b = urb->context;
+ usb_put_urb(&b->urb);
+}
+
+/*
+ * Aborts an ongoing transaction
+ *
+ * Assumes the transfer is referenced and locked and in a submitted
+ * state (mainly that there is an endpoint/rpipe assigned).
+ *
+ * The callback (see above) does nothing but freeing up the data by
+ * putting the URB. Because the URB is allocated at the head of the
+ * struct, the whole space we allocated is kfreed.
+ *
+ * We'll get an 'aborted transaction' xfer result on DTI, that'll
+ * politely ignore because at this point the transaction has been
+ * marked as aborted already.
+ */
+static void __wa_xfer_abort(struct wa_xfer *xfer)
+{
+ int result;
+ struct device *dev = &xfer->wa->usb_iface->dev;
+ struct wa_xfer_abort_buffer *b;
+ struct wa_rpipe *rpipe = xfer->ep->hcpriv;
+
+ b = kmalloc(sizeof(*b), GFP_ATOMIC);
+ if (b == NULL)
+ goto error_kmalloc;
+ b->cmd.bLength = sizeof(b->cmd);
+ b->cmd.bRequestType = WA_XFER_ABORT;
+ b->cmd.wRPipe = rpipe->descr.wRPipeIndex;
+ b->cmd.dwTransferID = wa_xfer_id(xfer);
+
+ usb_init_urb(&b->urb);
+ usb_fill_bulk_urb(&b->urb, xfer->wa->usb_dev,
+ usb_sndbulkpipe(xfer->wa->usb_dev,
+ xfer->wa->dto_epd->bEndpointAddress),
+ &b->cmd, sizeof(b->cmd), __wa_xfer_abort_cb, b);
+ result = usb_submit_urb(&b->urb, GFP_ATOMIC);
+ if (result < 0)
+ goto error_submit;
+ return; /* callback frees! */
+
+
+error_submit:
+ if (printk_ratelimit())
+ dev_err(dev, "xfer %p: Can't submit abort request: %d\n",
+ xfer, result);
+ kfree(b);
+error_kmalloc:
+ return;
+
+}
+
+/*
+ *
+ * @returns < 0 on error, transfer segment request size if ok
+ */
+static ssize_t __wa_xfer_setup_sizes(struct wa_xfer *xfer,
+ enum wa_xfer_type *pxfer_type)
+{
+ ssize_t result;
+ struct device *dev = &xfer->wa->usb_iface->dev;
+ size_t maxpktsize;
+ struct urb *urb = xfer->urb;
+ struct wa_rpipe *rpipe = xfer->ep->hcpriv;
+
+ d_fnstart(3, dev, "(xfer %p [rpipe %p] urb %p)\n",
+ xfer, rpipe, urb);
+ switch (rpipe->descr.bmAttribute & 0x3) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ *pxfer_type = WA_XFER_TYPE_CTL;
+ result = sizeof(struct wa_xfer_ctl);
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ case USB_ENDPOINT_XFER_BULK:
+ *pxfer_type = WA_XFER_TYPE_BI;
+ result = sizeof(struct wa_xfer_bi);
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ dev_err(dev, "FIXME: ISOC not implemented\n");
+ result = -ENOSYS;
+ goto error;
+ default:
+ /* never happens */
+ BUG();
+ result = -EINVAL; /* shut gcc up */
+ };
+ xfer->is_inbound = urb->pipe & USB_DIR_IN? 1 : 0;
+ xfer->is_dma = urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP? 1 : 0;
+ xfer->seg_size = le16_to_cpu(rpipe->descr.wBlocks)
+ * 1 << (xfer->wa->wa_descr->bRPipeBlockSize - 1);
+ /* Compute the segment size and make sure it is a multiple of
+ * the maxpktsize (WUSB1.0[8.3.3.1])...not really too much of
+ * a check (FIXME) */
+ maxpktsize = le16_to_cpu(rpipe->descr.wMaxPacketSize);
+ if (xfer->seg_size < maxpktsize) {
+ dev_err(dev, "HW BUG? seg_size %zu smaller than maxpktsize "
+ "%zu\n", xfer->seg_size, maxpktsize);
+ result = -EINVAL;
+ goto error;
+ }
+ xfer->seg_size = (xfer->seg_size / maxpktsize) * maxpktsize;
+ xfer->segs = (urb->transfer_buffer_length + xfer->seg_size - 1)
+ / xfer->seg_size;
+ if (xfer->segs >= WA_SEGS_MAX) {
+ dev_err(dev, "BUG? ops, number of segments %d bigger than %d\n",
+ (int)(urb->transfer_buffer_length / xfer->seg_size),
+ WA_SEGS_MAX);
+ result = -EINVAL;
+ goto error;
+ }
+ if (xfer->segs == 0 && *pxfer_type == WA_XFER_TYPE_CTL)
+ xfer->segs = 1;
+error:
+ d_fnend(3, dev, "(xfer %p [rpipe %p] urb %p) = %d\n",
+ xfer, rpipe, urb, (int)result);
+ return result;
+}
+
+/** Fill in the common request header and xfer-type specific data. */
+static void __wa_xfer_setup_hdr0(struct wa_xfer *xfer,
+ struct wa_xfer_hdr *xfer_hdr0,
+ enum wa_xfer_type xfer_type,
+ size_t xfer_hdr_size)
+{
+ struct wa_rpipe *rpipe = xfer->ep->hcpriv;
+
+ xfer_hdr0 = &xfer->seg[0]->xfer_hdr;
+ xfer_hdr0->bLength = xfer_hdr_size;
+ xfer_hdr0->bRequestType = xfer_type;
+ xfer_hdr0->wRPipe = rpipe->descr.wRPipeIndex;
+ xfer_hdr0->dwTransferID = wa_xfer_id(xfer);
+ xfer_hdr0->bTransferSegment = 0;
+ switch (xfer_type) {
+ case WA_XFER_TYPE_CTL: {
+ struct wa_xfer_ctl *xfer_ctl =
+ container_of(xfer_hdr0, struct wa_xfer_ctl, hdr);
+ xfer_ctl->bmAttribute = xfer->is_inbound? 1 : 0;
+ BUG_ON(xfer->urb->transfer_flags & URB_NO_SETUP_DMA_MAP
+ && xfer->urb->setup_packet == NULL);
+ memcpy(&xfer_ctl->baSetupData, xfer->urb->setup_packet,
+ sizeof(xfer_ctl->baSetupData));
+ break;
+ }
+ case WA_XFER_TYPE_BI:
+ break;
+ case WA_XFER_TYPE_ISO:
+ printk(KERN_ERR "FIXME: ISOC not implemented\n");
+ default:
+ BUG();
+ };
+}
+
+/*
+ * Callback for the OUT data phase of the segment request
+ *
+ * Check wa_seg_cb(); most comments also apply here because this
+ * function does almost the same thing and they work closely
+ * together.
+ *
+ * If the seg request has failed but this DTO phase has suceeded,
+ * wa_seg_cb() has already failed the segment and moved the
+ * status to WA_SEG_ERROR, so this will go through 'case 0' and
+ * effectively do nothing.
+ */
+static void wa_seg_dto_cb(struct urb *urb)
+{
+ struct wa_seg *seg = urb->context;
+ struct wa_xfer *xfer = seg->xfer;
+ struct wahc *wa;
+ struct device *dev;
+ struct wa_rpipe *rpipe;
+ unsigned long flags;
+ unsigned rpipe_ready = 0;
+ u8 done = 0;
+
+ d_fnstart(3, NULL, "(urb %p [%d])\n", urb, urb->status);
+ switch (urb->status) {
+ case 0:
+ spin_lock_irqsave(&xfer->lock, flags);
+ wa = xfer->wa;
+ dev = &wa->usb_iface->dev;
+ d_printf(2, dev, "xfer %p#%u: data out done (%d bytes)\n",
+ xfer, seg->index, urb->actual_length);
+ if (seg->status < WA_SEG_PENDING)
+ seg->status = WA_SEG_PENDING;
+ seg->result = urb->actual_length;
+ spin_unlock_irqrestore(&xfer->lock, flags);
+ break;
+ case -ECONNRESET: /* URB unlinked; no need to do anything */
+ case -ENOENT: /* as it was done by the who unlinked us */
+ break;
+ default: /* Other errors ... */
+ spin_lock_irqsave(&xfer->lock, flags);
+ wa = xfer->wa;
+ dev = &wa->usb_iface->dev;
+ rpipe = xfer->ep->hcpriv;
+ if (printk_ratelimit())
+ dev_err(dev, "xfer %p#%u: data out error %d\n",
+ xfer, seg->index, urb->status);
+ if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS,
+ EDC_ERROR_TIMEFRAME)){
+ dev_err(dev, "DTO: URB max acceptable errors "
+ "exceeded, resetting device\n");
+ usb_dev_reset_delayed(wa->usb_dev);
+ }
+ if (seg->status != WA_SEG_ERROR) {
+ seg->status = WA_SEG_ERROR;
+ seg->result = urb->status;
+ xfer->segs_done++;
+ __wa_xfer_abort(xfer);
+ rpipe_ready = rpipe_avail_inc(rpipe);
+ done = __wa_xfer_is_done(xfer);
+ }
+ spin_unlock_irqrestore(&xfer->lock, flags);
+ if (done)
+ wa_xfer_completion(xfer);
+ if (rpipe_ready)
+ wa_xfer_delayed_run(rpipe);
+ }
+ d_fnend(3, NULL, "(urb %p [%d]) = void\n", urb, urb->status);
+}
+
+/*
+ * Callback for the segment request
+ *
+ * If succesful transition state (unless already transitioned or
+ * outbound transfer); otherwise, take a note of the error, mark this
+ * segment done and try completion.
+ *
+ * Note we don't access until we are sure that the transfer hasn't
+ * been cancelled (ECONNRESET, ENOENT), which could mean that
+ * seg->xfer could be already gone.
+ *
+ * We have to check before setting the status to WA_SEG_PENDING
+ * because sometimes the xfer result callback arrives before this
+ * callback (geeeeeeze), so it might happen that we are already in
+ * another state. As well, we don't set it if the transfer is inbound,
+ * as in that case, wa_seg_dto_cb will do it when the OUT data phase
+ * finishes.
+ */
+static void wa_seg_cb(struct urb *urb)
+{
+ struct wa_seg *seg = urb->context;
+ struct wa_xfer *xfer = seg->xfer;
+ struct wahc *wa;
+ struct device *dev;
+ struct wa_rpipe *rpipe;
+ unsigned long flags;
+ unsigned rpipe_ready;
+ u8 done = 0;
+
+ d_fnstart(3, NULL, "(urb %p [%d])\n", urb, urb->status);
+ switch (urb->status) {
+ case 0:
+ spin_lock_irqsave(&xfer->lock, flags);
+ wa = xfer->wa;
+ dev = &wa->usb_iface->dev;
+ d_printf(2, dev, "xfer %p#%u: request done\n",
+ xfer, seg->index);
+ if (xfer->is_inbound && seg->status < WA_SEG_PENDING)
+ seg->status = WA_SEG_PENDING;
+ spin_unlock_irqrestore(&xfer->lock, flags);
+ break;
+ case -ECONNRESET: /* URB unlinked; no need to do anything */
+ case -ENOENT: /* as it was done by the who unlinked us */
+ break;
+ default: /* Other errors ... */
+ spin_lock_irqsave(&xfer->lock, flags);
+ wa = xfer->wa;
+ dev = &wa->usb_iface->dev;
+ rpipe = xfer->ep->hcpriv;
+ if (printk_ratelimit())
+ dev_err(dev, "xfer %p#%u: request error %d\n",
+ xfer, seg->index, urb->status);
+ if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS,
+ EDC_ERROR_TIMEFRAME)){
+ dev_err(dev, "DTO: URB max acceptable errors "
+ "exceeded, resetting device\n");
+ usb_dev_reset_delayed(wa->usb_dev);
+ }
+ usb_unlink_urb(seg->dto_urb);
+ seg->status = WA_SEG_ERROR;
+ seg->result = urb->status;
+ xfer->segs_done++;
+ __wa_xfer_abort(xfer);
+ rpipe_ready = rpipe_avail_inc(rpipe);
+ done = __wa_xfer_is_done(xfer);
+ spin_unlock_irqrestore(&xfer->lock, flags);
+ if (done)
+ wa_xfer_completion(xfer);
+ if (rpipe_ready)
+ wa_xfer_delayed_run(rpipe);
+ }
+ d_fnend(3, NULL, "(urb %p [%d]) = void\n", urb, urb->status);
+}
+
+/*
+ * Allocate the segs array and initialize each of them
+ *
+ * The segments are freed by wa_xfer_destroy() when the xfer use count
+ * drops to zero; however, because each segment is given the same life
+ * cycle as the USB URB it contains, it is actually freed by
+ * usb_put_urb() on the contained USB URB (twisted, eh?).
+ */
+static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size)
+{
+ int result, cnt;
+ size_t alloc_size = sizeof(*xfer->seg[0])
+ - sizeof(xfer->seg[0]->xfer_hdr) + xfer_hdr_size;
+ struct usb_device *usb_dev = xfer->wa->usb_dev;
+ const struct usb_endpoint_descriptor *dto_epd = xfer->wa->dto_epd;
+ struct wa_seg *seg;
+ size_t buf_itr, buf_size, buf_itr_size;
+
+ result = -ENOMEM;
+ xfer->seg = kzalloc(xfer->segs * sizeof(xfer->seg[0]), GFP_ATOMIC);
+ if (xfer->seg == NULL)
+ goto error_segs_kzalloc;
+ buf_itr = 0;
+ buf_size = xfer->urb->transfer_buffer_length;
+ for (cnt = 0; cnt < xfer->segs; cnt++) {
+ seg = xfer->seg[cnt] = kzalloc(alloc_size, GFP_ATOMIC);
+ if (seg == NULL)
+ goto error_seg_kzalloc;
+ wa_seg_init(seg);
+ seg->xfer = xfer;
+ seg->index = cnt;
+ usb_fill_bulk_urb(&seg->urb, usb_dev,
+ usb_sndbulkpipe(usb_dev,
+ dto_epd->bEndpointAddress),
+ &seg->xfer_hdr, xfer_hdr_size,
+ wa_seg_cb, seg);
+ buf_itr_size = buf_size > xfer->seg_size?
+ xfer->seg_size : buf_size;
+ if (xfer->is_inbound == 0 && buf_size > 0) {
+ seg->dto_urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (seg->dto_urb == NULL)
+ goto error_dto_alloc;
+ usb_fill_bulk_urb(
+ seg->dto_urb, usb_dev,
+ usb_sndbulkpipe(usb_dev,
+ dto_epd->bEndpointAddress),
+ NULL, 0, wa_seg_dto_cb, seg);
+ if (xfer->is_dma) {
+ seg->dto_urb->transfer_dma =
+ xfer->urb->transfer_dma + buf_itr;
+ seg->dto_urb->transfer_flags |=
+ URB_NO_TRANSFER_DMA_MAP;
+ } else
+ seg->dto_urb->transfer_buffer =
+ xfer->urb->transfer_buffer + buf_itr;
+ seg->dto_urb->transfer_buffer_length = buf_itr_size;
+ }
+ seg->status = WA_SEG_READY;
+ buf_itr += buf_itr_size;
+ buf_size -= buf_itr_size;
+ }
+ return 0;
+
+error_dto_alloc:
+ kfree(xfer->seg[cnt]);
+ cnt--;
+error_seg_kzalloc:
+ /* use the fact that cnt is left at were it failed */
+ for (; cnt > 0; cnt--) {
+ if (xfer->is_inbound == 0)
+ kfree(xfer->seg[cnt]->dto_urb);
+ kfree(xfer->seg[cnt]);
+ }
+error_segs_kzalloc:
+ return result;
+}
+
+/*
+ * Allocates all the stuff needed to submit a transfer
+ *
+ * Breaks the whole data buffer in a list of segments, each one has a
+ * structure allocated to it and linked in xfer->seg[index]
+ *
+ * FIXME: merge setup_segs() and the last part of this function, no
+ * need to do two for loops when we could run everything in a
+ * single one
+ */
+static int __wa_xfer_setup(struct wa_xfer *xfer, struct urb *urb)
+{
+ int result;
+ struct device *dev = &xfer->wa->usb_iface->dev;
+ enum wa_xfer_type xfer_type = 0; /* shut up GCC */
+ size_t xfer_hdr_size, cnt, transfer_size;
+ struct wa_xfer_hdr *xfer_hdr0, *xfer_hdr;
+
+ d_fnstart(3, dev, "(xfer %p [rpipe %p] urb %p)\n",
+ xfer, xfer->ep->hcpriv, urb);
+
+ result = __wa_xfer_setup_sizes(xfer, &xfer_type);
+ if (result < 0)
+ goto error_setup_sizes;
+ xfer_hdr_size = result;
+ result = __wa_xfer_setup_segs(xfer, xfer_hdr_size);
+ if (result < 0) {
+ dev_err(dev, "xfer %p: Failed to allocate %d segments: %d\n",
+ xfer, xfer->segs, result);
+ goto error_setup_segs;
+ }
+ /* Fill the first header */
+ xfer_hdr0 = &xfer->seg[0]->xfer_hdr;
+ wa_xfer_id_init(xfer);
+ __wa_xfer_setup_hdr0(xfer, xfer_hdr0, xfer_type, xfer_hdr_size);
+
+ /* Fill remainig headers */
+ xfer_hdr = xfer_hdr0;
+ transfer_size = urb->transfer_buffer_length;
+ xfer_hdr0->dwTransferLength = transfer_size > xfer->seg_size?
+ xfer->seg_size : transfer_size;
+ transfer_size -= xfer->seg_size;
+ for (cnt = 1; cnt < xfer->segs; cnt++) {
+ xfer_hdr = &xfer->seg[cnt]->xfer_hdr;
+ memcpy(xfer_hdr, xfer_hdr0, xfer_hdr_size);
+ xfer_hdr->bTransferSegment = cnt;
+ xfer_hdr->dwTransferLength = transfer_size > xfer->seg_size?
+ cpu_to_le32(xfer->seg_size)
+ : cpu_to_le32(transfer_size);
+ xfer->seg[cnt]->status = WA_SEG_READY;
+ transfer_size -= xfer->seg_size;
+ }
+ xfer_hdr->bTransferSegment |= 0x80; /* this is the last segment */
+ result = 0;
+error_setup_segs:
+error_setup_sizes:
+ d_fnend(3, dev, "(xfer %p [rpipe %p] urb %p) = %d\n",
+ xfer, xfer->ep->hcpriv, urb, result);
+ return result;
+}
+
+/*
+ *
+ *
+ * rpipe->seg_lock is held!
+ */
+static int __wa_seg_submit(struct wa_rpipe *rpipe, struct wa_xfer *xfer,
+ struct wa_seg *seg)
+{
+ int result;
+ result = usb_submit_urb(&seg->urb, GFP_ATOMIC);
+ if (result < 0) {
+ printk(KERN_ERR "xfer %p#%u: REQ submit failed: %d\n",
+ xfer, seg->index, result);
+ goto error_seg_submit;
+ }
+ if (seg->dto_urb) {
+ result = usb_submit_urb(seg->dto_urb, GFP_ATOMIC);
+ if (result < 0) {
+ printk(KERN_ERR "xfer %p#%u: DTO submit failed: %d\n",
+ xfer, seg->index, result);
+ goto error_dto_submit;
+ }
+ }
+ seg->status = WA_SEG_SUBMITTED;
+ rpipe_avail_dec(rpipe);
+ return 0;
+
+error_dto_submit:
+ usb_unlink_urb(&seg->urb);
+error_seg_submit:
+ seg->status = WA_SEG_ERROR;
+ seg->result = result;
+ return result;
+}
+
+/*
+ * Execute more queued request segments until the maximum concurrent allowed
+ *
+ * The ugly unlock/lock sequence on the error path is needed as the
+ * xfer->lock normally nests the seg_lock and not viceversa.
+ *
+ */
+static void wa_xfer_delayed_run(struct wa_rpipe *rpipe)
+{
+ int result;
+ struct device *dev = &rpipe->wa->usb_iface->dev;
+ struct wa_seg *seg;
+ struct wa_xfer *xfer;
+ unsigned long flags;
+
+ d_fnstart(1, dev, "(rpipe #%d) %d segments available\n",
+ le16_to_cpu(rpipe->descr.wRPipeIndex),
+ atomic_read(&rpipe->segs_available));
+ spin_lock_irqsave(&rpipe->seg_lock, flags);
+ while (atomic_read(&rpipe->segs_available) > 0
+ && !list_empty(&rpipe->seg_list)) {
+ seg = list_entry(rpipe->seg_list.next, struct wa_seg,
+ list_node);
+ list_del(&seg->list_node);
+ xfer = seg->xfer;
+ result = __wa_seg_submit(rpipe, xfer, seg);
+ d_printf(1, dev, "xfer %p#%u submitted from delayed "
+ "[%d segments available] %d\n",
+ xfer, seg->index,
+ atomic_read(&rpipe->segs_available), result);
+ if (unlikely(result < 0)) {
+ spin_unlock_irqrestore(&rpipe->seg_lock, flags);
+ spin_lock_irqsave(&xfer->lock, flags);
+ __wa_xfer_abort(xfer);
+ xfer->segs_done++;
+ spin_unlock_irqrestore(&xfer->lock, flags);
+ spin_lock_irqsave(&rpipe->seg_lock, flags);
+ }
+ }
+ spin_unlock_irqrestore(&rpipe->seg_lock, flags);
+ d_fnend(1, dev, "(rpipe #%d) = void, %d segments available\n",
+ le16_to_cpu(rpipe->descr.wRPipeIndex),
+ atomic_read(&rpipe->segs_available));
+
+}
+
+/*
+ *
+ * xfer->lock is taken
+ *
+ * On failure submitting we just stop submitting and return error;
+ * wa_urb_enqueue_b() will execute the completion path
+ */
+static int __wa_xfer_submit(struct wa_xfer *xfer)
+{
+ int result;
+ struct wahc *wa = xfer->wa;
+ struct device *dev = &wa->usb_iface->dev;
+ unsigned cnt;
+ struct wa_seg *seg;
+ unsigned long flags;
+ struct wa_rpipe *rpipe = xfer->ep->hcpriv;
+ size_t maxrequests = le16_to_cpu(rpipe->descr.wRequests);
+ u8 available;
+ u8 empty;
+
+ d_fnstart(3, dev, "(xfer %p [rpipe %p])\n",
+ xfer, xfer->ep->hcpriv);
+
+ spin_lock_irqsave(&wa->xfer_list_lock, flags);
+ list_add_tail(&xfer->list_node, &wa->xfer_list);
+ spin_unlock_irqrestore(&wa->xfer_list_lock, flags);
+
+ BUG_ON(atomic_read(&rpipe->segs_available) > maxrequests);
+ result = 0;
+ spin_lock_irqsave(&rpipe->seg_lock, flags);
+ for (cnt = 0; cnt < xfer->segs; cnt++) {
+ available = atomic_read(&rpipe->segs_available);
+ empty = list_empty(&rpipe->seg_list);
+ seg = xfer->seg[cnt];
+ d_printf(2, dev, "xfer %p#%u: available %u empty %u (%s)\n",
+ xfer, cnt, available, empty,
+ available == 0 || !empty? "delayed" : "submitted");
+ if (available == 0 || !empty) {
+ d_printf(1, dev, "xfer %p#%u: delayed\n", xfer, cnt);
+ seg->status = WA_SEG_DELAYED;
+ list_add_tail(&seg->list_node, &rpipe->seg_list);
+ } else {
+ result = __wa_seg_submit(rpipe, xfer, seg);
+ if (result < 0)
+ goto error_seg_submit;
+ }
+ xfer->segs_submitted++;
+ }
+ spin_unlock_irqrestore(&rpipe->seg_lock, flags);
+ d_fnend(3, dev, "(xfer %p [rpipe %p]) = void\n", xfer,
+ xfer->ep->hcpriv);
+ return result;
+
+error_seg_submit:
+ __wa_xfer_abort(xfer);
+ spin_unlock_irqrestore(&rpipe->seg_lock, flags);
+ d_fnend(3, dev, "(xfer %p [rpipe %p]) = void\n", xfer,
+ xfer->ep->hcpriv);
+ return result;
+}
+
+/*
+ * Second part of a URB/transfer enqueuement
+ *
+ * Assumes this comes from wa_urb_enqueue() [maybe through
+ * wa_urb_enqueue_run()]. At this point:
+ *
+ * xfer->wa filled and refcounted
+ * xfer->ep filled with rpipe refcounted if
+ * delayed == 0
+ * xfer->urb filled and refcounted (this is the case when called
+ * from wa_urb_enqueue() as we come from usb_submit_urb()
+ * and when called by wa_urb_enqueue_run(), as we took an
+ * extra ref dropped by _run() after we return).
+ * xfer->gfp filled
+ *
+ * If we fail at __wa_xfer_submit(), then we just check if we are done
+ * and if so, we run the completion procedure. However, if we are not
+ * yet done, we do nothing and wait for the completion handlers from
+ * the submitted URBs or from the xfer-result path to kick in. If xfer
+ * result never kicks in, the xfer will timeout from the USB code and
+ * dequeue() will be called.
+ */
+static void wa_urb_enqueue_b(struct wa_xfer *xfer)
+{
+ int result;
+ unsigned long flags;
+ struct urb *urb = xfer->urb;
+ struct wahc *wa = xfer->wa;
+ struct wusbhc *wusbhc = wa->wusb;
+ struct device *dev = &wa->usb_iface->dev;
+ struct wusb_dev *wusb_dev;
+ unsigned done;
+
+ d_fnstart(3, dev, "(wa %p urb %p)\n", wa, urb);
+ result = rpipe_get_by_ep(wa, xfer->ep, urb, xfer->gfp);
+ if (result < 0)
+ goto error_rpipe_get;
+ result = -ENODEV;
+ /* FIXME: segmentation broken -- kills DWA */
+ mutex_lock(&wusbhc->mutex); /* get a WUSB dev */
+ if (urb->dev == NULL)
+ goto error_dev_gone;
+ wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, urb->dev);
+ if (wusb_dev == NULL) {
+ mutex_unlock(&wusbhc->mutex);
+ goto error_dev_gone;
+ }
+ mutex_unlock(&wusbhc->mutex);
+
+ spin_lock_irqsave(&xfer->lock, flags);
+ xfer->wusb_dev = wusb_dev;
+ result = urb->status;
+ if (urb->status != -EINPROGRESS)
+ goto error_dequeued;
+
+ result = __wa_xfer_setup(xfer, urb);
+ if (result < 0)
+ goto error_xfer_setup;
+ result = __wa_xfer_submit(xfer);
+ if (result < 0)
+ goto error_xfer_submit;
+ spin_unlock_irqrestore(&xfer->lock, flags);
+ d_fnend(3, dev, "(wa %p urb %p) = void\n", wa, urb);
+ return;
+
+ /* this is basically wa_xfer_completion() broken up wa_xfer_giveback()
+ * does a wa_xfer_put() that will call wa_xfer_destroy() and clean
+ * upundo setup().
+ */
+error_xfer_setup:
+error_dequeued:
+ spin_unlock_irqrestore(&xfer->lock, flags);
+ /* FIXME: segmentation broken, kills DWA */
+ if (wusb_dev)
+ wusb_dev_put(wusb_dev);
+error_dev_gone:
+ rpipe_put(xfer->ep->hcpriv);
+error_rpipe_get:
+ xfer->result = result;
+ wa_xfer_giveback(xfer);
+ d_fnend(3, dev, "(wa %p urb %p) = (void) %d\n", wa, urb, result);
+ return;
+
+error_xfer_submit:
+ done = __wa_xfer_is_done(xfer);
+ xfer->result = result;
+ spin_unlock_irqrestore(&xfer->lock, flags);
+ if (done)
+ wa_xfer_completion(xfer);
+ d_fnend(3, dev, "(wa %p urb %p) = (void) %d\n", wa, urb, result);
+ return;
+}
+
+/*
+ * Execute the delayed transfers in the Wire Adapter @wa
+ *
+ * We need to be careful here, as dequeue() could be called in the
+ * middle. That's why we do the whole thing under the
+ * wa->xfer_list_lock. If dequeue() jumps in, it first locks urb->lock
+ * and then checks the list -- so as we would be acquiring in inverse
+ * order, we just drop the lock once we have the xfer and reacquire it
+ * later.
+ */
+void wa_urb_enqueue_run(struct work_struct *ws)
+{
+ struct wahc *wa = container_of(ws, struct wahc, xfer_work);
+ struct device *dev = &wa->usb_iface->dev;
+ struct wa_xfer *xfer, *next;
+ struct urb *urb;
+
+ d_fnstart(3, dev, "(wa %p)\n", wa);
+ spin_lock_irq(&wa->xfer_list_lock);
+ list_for_each_entry_safe(xfer, next, &wa->xfer_delayed_list,
+ list_node) {
+ list_del_init(&xfer->list_node);
+ spin_unlock_irq(&wa->xfer_list_lock);
+
+ urb = xfer->urb;
+ wa_urb_enqueue_b(xfer);
+ usb_put_urb(urb); /* taken when queuing */
+
+ spin_lock_irq(&wa->xfer_list_lock);
+ }
+ spin_unlock_irq(&wa->xfer_list_lock);
+ d_fnend(3, dev, "(wa %p) = void\n", wa);
+}
+EXPORT_SYMBOL_GPL(wa_urb_enqueue_run);
+
+/*
+ * Submit a transfer to the Wire Adapter in a delayed way
+ *
+ * The process of enqueuing involves possible sleeps() [see
+ * enqueue_b(), for the rpipe_get() and the mutex_lock()]. If we are
+ * in an atomic section, we defer the enqueue_b() call--else we call direct.
+ *
+ * @urb: We own a reference to it done by the HCI Linux USB stack that
+ * will be given up by calling usb_hcd_giveback_urb() or by
+ * returning error from this function -> ergo we don't have to
+ * refcount it.
+ */
+int wa_urb_enqueue(struct wahc *wa, struct usb_host_endpoint *ep,
+ struct urb *urb, gfp_t gfp)
+{
+ int result;
+ struct device *dev = &wa->usb_iface->dev;
+ struct wa_xfer *xfer;
+ unsigned long my_flags;
+ unsigned cant_sleep = irqs_disabled() | in_atomic();
+
+ d_fnstart(3, dev, "(wa %p ep %p urb %p [%d] gfp 0x%x)\n",
+ wa, ep, urb, urb->transfer_buffer_length, gfp);
+
+ if (urb->transfer_buffer == NULL
+ && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
+ && urb->transfer_buffer_length != 0) {
+ dev_err(dev, "BUG? urb %p: NULL xfer buffer & NODMA\n", urb);
+ dump_stack();
+ }
+
+ result = -ENOMEM;
+ xfer = kzalloc(sizeof(*xfer), gfp);
+ if (xfer == NULL)
+ goto error_kmalloc;
+
+ result = -ENOENT;
+ if (urb->status != -EINPROGRESS) /* cancelled */
+ goto error_dequeued; /* before starting? */
+ wa_xfer_init(xfer);
+ xfer->wa = wa_get(wa);
+ xfer->urb = urb;
+ xfer->gfp = gfp;
+ xfer->ep = ep;
+ urb->hcpriv = xfer;
+ d_printf(2, dev, "xfer %p urb %p pipe 0x%02x [%d bytes] %s %s %s\n",
+ xfer, urb, urb->pipe, urb->transfer_buffer_length,
+ urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP? "dma" : "nodma",
+ urb->pipe & USB_DIR_IN? "inbound" : "outbound",
+ cant_sleep? "deferred" : "inline");
+ if (cant_sleep) {
+ usb_get_urb(urb);
+ spin_lock_irqsave(&wa->xfer_list_lock, my_flags);
+ list_add_tail(&xfer->list_node, &wa->xfer_delayed_list);
+ spin_unlock_irqrestore(&wa->xfer_list_lock, my_flags);
+ queue_work(wusbd, &wa->xfer_work);
+ } else {
+ wa_urb_enqueue_b(xfer);
+ }
+ d_fnend(3, dev, "(wa %p ep %p urb %p [%d] gfp 0x%x) = 0\n",
+ wa, ep, urb, urb->transfer_buffer_length, gfp);
+ return 0;
+
+error_dequeued:
+ kfree(xfer);
+error_kmalloc:
+ d_fnend(3, dev, "(wa %p ep %p urb %p [%d] gfp 0x%x) = %d\n",
+ wa, ep, urb, urb->transfer_buffer_length, gfp, result);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wa_urb_enqueue);
+
+/*
+ * Dequeue a URB and make sure uwb_hcd_giveback_urb() [completion
+ * handler] is called.
+ *
+ * Until a transfer goes successfully through wa_urb_enqueue() it
+ * needs to be dequeued with completion calling; when stuck in delayed
+ * or before wa_xfer_setup() is called, we need to do completion.
+ *
+ * not setup If there is no hcpriv yet, that means that that enqueue
+ * still had no time to set the xfer up. Because
+ * urb->status should be other than -EINPROGRESS,
+ * enqueue() will catch that and bail out.
+ *
+ * If the transfer has gone through setup, we just need to clean it
+ * up. If it has gone through submit(), we have to abort it [with an
+ * asynch request] and then make sure we cancel each segment.
+ *
+ */
+int wa_urb_dequeue(struct wahc *wa, struct urb *urb)
+{
+ struct device *dev = &wa->usb_iface->dev;
+ unsigned long flags, flags2;
+ struct wa_xfer *xfer;
+ struct wa_seg *seg;
+ struct wa_rpipe *rpipe;
+ unsigned cnt;
+ unsigned rpipe_ready = 0;
+
+ d_fnstart(3, dev, "(wa %p, urb %p)\n", wa, urb);
+
+ d_printf(1, dev, "xfer %p urb %p: aborting\n", urb->hcpriv, urb);
+ xfer = urb->hcpriv;
+ if (xfer == NULL) {
+ /* NOthing setup yet enqueue will see urb->status !=
+ * -EINPROGRESS (by hcd layer) and bail out with
+ * error, no need to do completion
+ */
+ BUG_ON(urb->status == -EINPROGRESS);
+ goto out;
+ }
+ spin_lock_irqsave(&xfer->lock, flags);
+ rpipe = xfer->ep->hcpriv;
+ /* Check the delayed list -> if there, release and complete */
+ spin_lock_irqsave(&wa->xfer_list_lock, flags2);
+ if (!list_empty(&xfer->list_node) && xfer->seg == NULL)
+ goto dequeue_delayed;
+ spin_unlock_irqrestore(&wa->xfer_list_lock, flags2);
+ if (xfer->seg == NULL) /* still hasn't reached */
+ goto out_unlock; /* setup(), enqueue_b() completes */
+ /* Ok, the xfer is in flight already, it's been setup and submitted.*/
+ __wa_xfer_abort(xfer);
+ for (cnt = 0; cnt < xfer->segs; cnt++) {
+ seg = xfer->seg[cnt];
+ switch (seg->status) {
+ case WA_SEG_NOTREADY:
+ case WA_SEG_READY:
+ printk(KERN_ERR "xfer %p#%u: dequeue bad state %u\n",
+ xfer, cnt, seg->status);
+ WARN_ON(1);
+ break;
+ case WA_SEG_DELAYED:
+ seg->status = WA_SEG_ABORTED;
+ spin_lock_irqsave(&rpipe->seg_lock, flags2);
+ list_del(&seg->list_node);
+ xfer->segs_done++;
+ rpipe_ready = rpipe_avail_inc(rpipe);
+ spin_unlock_irqrestore(&rpipe->seg_lock, flags2);
+ break;
+ case WA_SEG_SUBMITTED:
+ seg->status = WA_SEG_ABORTED;
+ usb_unlink_urb(&seg->urb);
+ if (xfer->is_inbound == 0)
+ usb_unlink_urb(seg->dto_urb);
+ xfer->segs_done++;
+ rpipe_ready = rpipe_avail_inc(rpipe);
+ break;
+ case WA_SEG_PENDING:
+ seg->status = WA_SEG_ABORTED;
+ xfer->segs_done++;
+ rpipe_ready = rpipe_avail_inc(rpipe);
+ break;
+ case WA_SEG_DTI_PENDING:
+ usb_unlink_urb(wa->dti_urb);
+ seg->status = WA_SEG_ABORTED;
+ xfer->segs_done++;
+ rpipe_ready = rpipe_avail_inc(rpipe);
+ break;
+ case WA_SEG_DONE:
+ case WA_SEG_ERROR:
+ case WA_SEG_ABORTED:
+ break;
+ }
+ }
+ xfer->result = urb->status; /* -ENOENT or -ECONNRESET */
+ __wa_xfer_is_done(xfer);
+ spin_unlock_irqrestore(&xfer->lock, flags);
+ wa_xfer_completion(xfer);
+ if (rpipe_ready)
+ wa_xfer_delayed_run(rpipe);
+ d_fnend(3, dev, "(wa %p, urb %p) = 0\n", wa, urb);
+ return 0;
+
+out_unlock:
+ spin_unlock_irqrestore(&xfer->lock, flags);
+out:
+ d_fnend(3, dev, "(wa %p, urb %p) = 0\n", wa, urb);
+ return 0;
+
+dequeue_delayed:
+ list_del_init(&xfer->list_node);
+ spin_unlock_irqrestore(&wa->xfer_list_lock, flags2);
+ xfer->result = urb->status;
+ spin_unlock_irqrestore(&xfer->lock, flags);
+ wa_xfer_giveback(xfer);
+ usb_put_urb(urb); /* we got a ref in enqueue() */
+ d_fnend(3, dev, "(wa %p, urb %p) = 0\n", wa, urb);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wa_urb_dequeue);
+
+/*
+ * Translation from WA status codes (WUSB1.0 Table 8.15) to errno
+ * codes
+ *
+ * Positive errno values are internal inconsistencies and should be
+ * flagged louder. Negative are to be passed up to the user in the
+ * normal way.
+ *
+ * @status: USB WA status code -- high two bits are stripped.
+ */
+static int wa_xfer_status_to_errno(u8 status)
+{
+ int errno;
+ u8 real_status = status;
+ static int xlat[] = {
+ [WA_XFER_STATUS_SUCCESS] = 0,
+ [WA_XFER_STATUS_HALTED] = -EPIPE,
+ [WA_XFER_STATUS_DATA_BUFFER_ERROR] = -ENOBUFS,
+ [WA_XFER_STATUS_BABBLE] = -EOVERFLOW,
+ [WA_XFER_RESERVED] = EINVAL,
+ [WA_XFER_STATUS_NOT_FOUND] = 0,
+ [WA_XFER_STATUS_INSUFFICIENT_RESOURCE] = -ENOMEM,
+ [WA_XFER_STATUS_TRANSACTION_ERROR] = -EILSEQ,
+ [WA_XFER_STATUS_ABORTED] = -EINTR,
+ [WA_XFER_STATUS_RPIPE_NOT_READY] = EINVAL,
+ [WA_XFER_INVALID_FORMAT] = EINVAL,
+ [WA_XFER_UNEXPECTED_SEGMENT_NUMBER] = EINVAL,
+ [WA_XFER_STATUS_RPIPE_TYPE_MISMATCH] = EINVAL,
+ };
+ status &= 0x3f;
+
+ if (status == 0)
+ return 0;
+ if (status >= ARRAY_SIZE(xlat)) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "%s(): BUG? "
+ "Unknown WA transfer status 0x%02x\n",
+ __func__, real_status);
+ return -EINVAL;
+ }
+ errno = xlat[status];
+ if (unlikely(errno > 0)) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "%s(): BUG? "
+ "Inconsistent WA status: 0x%02x\n",
+ __func__, real_status);
+ errno = -errno;
+ }
+ return errno;
+}
+
+/*
+ * Process a xfer result completion message
+ *
+ * inbound transfers: need to schedule a DTI read
+ *
+ * FIXME: this functio needs to be broken up in parts
+ */
+static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer)
+{
+ int result;
+ struct device *dev = &wa->usb_iface->dev;
+ unsigned long flags;
+ u8 seg_idx;
+ struct wa_seg *seg;
+ struct wa_rpipe *rpipe;
+ struct wa_xfer_result *xfer_result = wa->xfer_result;
+ u8 done = 0;
+ u8 usb_status;
+ unsigned rpipe_ready = 0;
+
+ d_fnstart(3, dev, "(wa %p xfer %p)\n", wa, xfer);
+ spin_lock_irqsave(&xfer->lock, flags);
+ seg_idx = xfer_result->bTransferSegment & 0x7f;
+ if (unlikely(seg_idx >= xfer->segs))
+ goto error_bad_seg;
+ seg = xfer->seg[seg_idx];
+ rpipe = xfer->ep->hcpriv;
+ usb_status = xfer_result->bTransferStatus;
+ d_printf(2, dev, "xfer %p#%u: bTransferStatus 0x%02x (seg %u)\n",
+ xfer, seg_idx, usb_status, seg->status);
+ if (seg->status == WA_SEG_ABORTED
+ || seg->status == WA_SEG_ERROR) /* already handled */
+ goto segment_aborted;
+ if (seg->status == WA_SEG_SUBMITTED) /* ops, got here */
+ seg->status = WA_SEG_PENDING; /* before wa_seg{_dto}_cb() */
+ if (seg->status != WA_SEG_PENDING) {
+ if (printk_ratelimit())
+ dev_err(dev, "xfer %p#%u: Bad segment state %u\n",
+ xfer, seg_idx, seg->status);
+ seg->status = WA_SEG_PENDING; /* workaround/"fix" it */
+ }
+ if (usb_status & 0x80) {
+ seg->result = wa_xfer_status_to_errno(usb_status);
+ dev_err(dev, "DTI: xfer %p#%u failed (0x%02x)\n",
+ xfer, seg->index, usb_status);
+ goto error_complete;
+ }
+ /* FIXME: we ignore warnings, tally them for stats */
+ if (usb_status & 0x40) /* Warning?... */
+ usb_status = 0; /* ... pass */
+ if (xfer->is_inbound) { /* IN data phase: read to buffer */
+ seg->status = WA_SEG_DTI_PENDING;
+ BUG_ON(wa->buf_in_urb->status == -EINPROGRESS);
+ if (xfer->is_dma) {
+ wa->buf_in_urb->transfer_dma =
+ xfer->urb->transfer_dma
+ + seg_idx * xfer->seg_size;
+ wa->buf_in_urb->transfer_flags
+ |= URB_NO_TRANSFER_DMA_MAP;
+ } else {
+ wa->buf_in_urb->transfer_buffer =
+ xfer->urb->transfer_buffer
+ + seg_idx * xfer->seg_size;
+ wa->buf_in_urb->transfer_flags
+ &= ~URB_NO_TRANSFER_DMA_MAP;
+ }
+ wa->buf_in_urb->transfer_buffer_length =
+ le32_to_cpu(xfer_result->dwTransferLength);
+ wa->buf_in_urb->context = seg;
+ result = usb_submit_urb(wa->buf_in_urb, GFP_ATOMIC);
+ if (result < 0)
+ goto error_submit_buf_in;
+ } else {
+ /* OUT data phase, complete it -- */
+ seg->status = WA_SEG_DONE;
+ seg->result = le32_to_cpu(xfer_result->dwTransferLength);
+ xfer->segs_done++;
+ rpipe_ready = rpipe_avail_inc(rpipe);
+ done = __wa_xfer_is_done(xfer);
+ }
+ spin_unlock_irqrestore(&xfer->lock, flags);
+ if (done)
+ wa_xfer_completion(xfer);
+ if (rpipe_ready)
+ wa_xfer_delayed_run(rpipe);
+ d_fnend(3, dev, "(wa %p xfer %p) = void\n", wa, xfer);
+ return;
+
+
+error_submit_buf_in:
+ if (edc_inc(&wa->dti_edc, EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
+ dev_err(dev, "DTI: URB max acceptable errors "
+ "exceeded, resetting device\n");
+ usb_dev_reset_delayed(wa->usb_dev);
+ }
+ if (printk_ratelimit())
+ dev_err(dev, "xfer %p#%u: can't submit DTI data phase: %d\n",
+ xfer, seg_idx, result);
+ seg->result = result;
+error_complete:
+ seg->status = WA_SEG_ERROR;
+ xfer->segs_done++;
+ rpipe_ready = rpipe_avail_inc(rpipe);
+ __wa_xfer_abort(xfer);
+ done = __wa_xfer_is_done(xfer);
+ spin_unlock_irqrestore(&xfer->lock, flags);
+ if (done)
+ wa_xfer_completion(xfer);
+ if (rpipe_ready)
+ wa_xfer_delayed_run(rpipe);
+ d_fnend(3, dev, "(wa %p xfer %p) = void [segment/DTI-submit error]\n",
+ wa, xfer);
+ return;
+
+
+error_bad_seg:
+ spin_unlock_irqrestore(&xfer->lock, flags);
+ wa_urb_dequeue(wa, xfer->urb);
+ if (printk_ratelimit())
+ dev_err(dev, "xfer %p#%u: bad segment\n", xfer, seg_idx);
+ if (edc_inc(&wa->dti_edc, EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
+ dev_err(dev, "DTI: URB max acceptable errors "
+ "exceeded, resetting device\n");
+ usb_dev_reset_delayed(wa->usb_dev);
+ }
+ d_fnend(3, dev, "(wa %p xfer %p) = void [bad seg]\n", wa, xfer);
+ return;
+
+
+segment_aborted:
+ /* nothing to do, as the aborter did the completion */
+ spin_unlock_irqrestore(&xfer->lock, flags);
+ d_fnend(3, dev, "(wa %p xfer %p) = void [segment aborted]\n",
+ wa, xfer);
+ return;
+
+}
+
+/*
+ * Callback for the IN data phase
+ *
+ * If succesful transition state; otherwise, take a note of the
+ * error, mark this segment done and try completion.
+ *
+ * Note we don't access until we are sure that the transfer hasn't
+ * been cancelled (ECONNRESET, ENOENT), which could mean that
+ * seg->xfer could be already gone.
+ */
+static void wa_buf_in_cb(struct urb *urb)
+{
+ struct wa_seg *seg = urb->context;
+ struct wa_xfer *xfer = seg->xfer;
+ struct wahc *wa;
+ struct device *dev;
+ struct wa_rpipe *rpipe;
+ unsigned rpipe_ready;
+ unsigned long flags;
+ u8 done = 0;
+
+ d_fnstart(3, NULL, "(urb %p [%d])\n", urb, urb->status);
+ switch (urb->status) {
+ case 0:
+ spin_lock_irqsave(&xfer->lock, flags);
+ wa = xfer->wa;
+ dev = &wa->usb_iface->dev;
+ rpipe = xfer->ep->hcpriv;
+ d_printf(2, dev, "xfer %p#%u: data in done (%zu bytes)\n",
+ xfer, seg->index, (size_t)urb->actual_length);
+ seg->status = WA_SEG_DONE;
+ seg->result = urb->actual_length;
+ xfer->segs_done++;
+ rpipe_ready = rpipe_avail_inc(rpipe);
+ done = __wa_xfer_is_done(xfer);
+ spin_unlock_irqrestore(&xfer->lock, flags);
+ if (done)
+ wa_xfer_completion(xfer);
+ if (rpipe_ready)
+ wa_xfer_delayed_run(rpipe);
+ break;
+ case -ECONNRESET: /* URB unlinked; no need to do anything */
+ case -ENOENT: /* as it was done by the who unlinked us */
+ break;
+ default: /* Other errors ... */
+ spin_lock_irqsave(&xfer->lock, flags);
+ wa = xfer->wa;
+ dev = &wa->usb_iface->dev;
+ rpipe = xfer->ep->hcpriv;
+ if (printk_ratelimit())
+ dev_err(dev, "xfer %p#%u: data in error %d\n",
+ xfer, seg->index, urb->status);
+ if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS,
+ EDC_ERROR_TIMEFRAME)){
+ dev_err(dev, "DTO: URB max acceptable errors "
+ "exceeded, resetting device\n");
+ usb_dev_reset_delayed(wa->usb_dev);
+ }
+ seg->status = WA_SEG_ERROR;
+ seg->result = urb->status;
+ xfer->segs_done++;
+ rpipe_ready = rpipe_avail_inc(rpipe);
+ __wa_xfer_abort(xfer);
+ done = __wa_xfer_is_done(xfer);
+ spin_unlock_irqrestore(&xfer->lock, flags);
+ if (done)
+ wa_xfer_completion(xfer);
+ if (rpipe_ready)
+ wa_xfer_delayed_run(rpipe);
+ }
+ d_fnend(3, NULL, "(urb %p [%d]) = void\n", urb, urb->status);
+}
+
+/*
+ * Handle an incoming transfer result buffer
+ *
+ * Given a transfer result buffer, it completes the transfer (possibly
+ * scheduling and buffer in read) and then resubmits the DTI URB for a
+ * new transfer result read.
+ *
+ *
+ * The xfer_result DTI URB state machine
+ *
+ * States: OFF | RXR (Read-Xfer-Result) | RBI (Read-Buffer-In)
+ *
+ * We start in OFF mode, the first xfer_result notification [through
+ * wa_handle_notif_xfer()] moves us to RXR by posting the DTI-URB to
+ * read.
+ *
+ * We receive a buffer -- if it is not a xfer_result, we complain and
+ * repost the DTI-URB. If it is a xfer_result then do the xfer seg
+ * request accounting. If it is an IN segment, we move to RBI and post
+ * a BUF-IN-URB to the right buffer. The BUF-IN-URB callback will
+ * repost the DTI-URB and move to RXR state. if there was no IN
+ * segment, it will repost the DTI-URB.
+ *
+ * We go back to OFF when we detect a ENOENT or ESHUTDOWN (or too many
+ * errors) in the URBs.
+ */
+static void wa_xfer_result_cb(struct urb *urb)
+{
+ int result;
+ struct wahc *wa = urb->context;
+ struct device *dev = &wa->usb_iface->dev;
+ struct wa_xfer_result *xfer_result;
+ u32 xfer_id;
+ struct wa_xfer *xfer;
+ u8 usb_status;
+
+ d_fnstart(3, dev, "(%p)\n", wa);
+ BUG_ON(wa->dti_urb != urb);
+ switch (wa->dti_urb->status) {
+ case 0:
+ /* We have a xfer result buffer; check it */
+ d_printf(2, dev, "DTI: xfer result %d bytes at %p\n",
+ urb->actual_length, urb->transfer_buffer);
+ d_dump(3, dev, urb->transfer_buffer, urb->actual_length);
+ if (wa->dti_urb->actual_length != sizeof(*xfer_result)) {
+ dev_err(dev, "DTI Error: xfer result--bad size "
+ "xfer result (%d bytes vs %zu needed)\n",
+ urb->actual_length, sizeof(*xfer_result));
+ break;
+ }
+ xfer_result = wa->xfer_result;
+ if (xfer_result->hdr.bLength != sizeof(*xfer_result)) {
+ dev_err(dev, "DTI Error: xfer result--"
+ "bad header length %u\n",
+ xfer_result->hdr.bLength);
+ break;
+ }
+ if (xfer_result->hdr.bNotifyType != WA_XFER_RESULT) {
+ dev_err(dev, "DTI Error: xfer result--"
+ "bad header type 0x%02x\n",
+ xfer_result->hdr.bNotifyType);
+ break;
+ }
+ usb_status = xfer_result->bTransferStatus & 0x3f;
+ if (usb_status == WA_XFER_STATUS_ABORTED
+ || usb_status == WA_XFER_STATUS_NOT_FOUND)
+ /* taken care of already */
+ break;
+ xfer_id = xfer_result->dwTransferID;
+ xfer = wa_xfer_get_by_id(wa, xfer_id);
+ if (xfer == NULL) {
+ /* FIXME: transaction might have been cancelled */
+ dev_err(dev, "DTI Error: xfer result--"
+ "unknown xfer 0x%08x (status 0x%02x)\n",
+ xfer_id, usb_status);
+ break;
+ }
+ wa_xfer_result_chew(wa, xfer);
+ wa_xfer_put(xfer);
+ break;
+ case -ENOENT: /* (we killed the URB)...so, no broadcast */
+ case -ESHUTDOWN: /* going away! */
+ dev_dbg(dev, "DTI: going down! %d\n", urb->status);
+ goto out;
+ default:
+ /* Unknown error */
+ if (edc_inc(&wa->dti_edc, EDC_MAX_ERRORS,
+ EDC_ERROR_TIMEFRAME)) {
+ dev_err(dev, "DTI: URB max acceptable errors "
+ "exceeded, resetting device\n");
+ usb_dev_reset_delayed(wa->usb_dev);
+ goto out;
+ }
+ if (printk_ratelimit())
+ dev_err(dev, "DTI: URB error %d\n", urb->status);
+ break;
+ }
+ /* Resubmit the DTI URB */
+ result = usb_submit_urb(wa->dti_urb, GFP_ATOMIC);
+ if (result < 0) {
+ dev_err(dev, "DTI Error: Could not submit DTI URB (%d), "
+ "resetting\n", result);
+ usb_dev_reset_delayed(wa->usb_dev);
+ }
+out:
+ d_fnend(3, dev, "(%p) = void\n", wa);
+ return;
+}
+
+/*
+ * Transfer complete notification
+ *
+ * Called from the notif.c code. We get a notification on EP2 saying
+ * that some endpoint has some transfer result data available. We are
+ * about to read it.
+ *
+ * To speed up things, we always have a URB reading the DTI URB; we
+ * don't really set it up and start it until the first xfer complete
+ * notification arrives, which is what we do here.
+ *
+ * Follow up in wa_xfer_result_cb(), as that's where the whole state
+ * machine starts.
+ *
+ * So here we just initialize the DTI URB for reading transfer result
+ * notifications and also the buffer-in URB, for reading buffers. Then
+ * we just submit the DTI URB.
+ *
+ * @wa shall be referenced
+ */
+void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr)
+{
+ int result;
+ struct device *dev = &wa->usb_iface->dev;
+ struct wa_notif_xfer *notif_xfer;
+ const struct usb_endpoint_descriptor *dti_epd = wa->dti_epd;
+
+ d_fnstart(4, dev, "(%p, %p)\n", wa, notif_hdr);
+ notif_xfer = container_of(notif_hdr, struct wa_notif_xfer, hdr);
+ BUG_ON(notif_hdr->bNotifyType != WA_NOTIF_TRANSFER);
+
+ if ((0x80 | notif_xfer->bEndpoint) != dti_epd->bEndpointAddress) {
+ /* FIXME: hardcoded limitation, adapt */
+ dev_err(dev, "BUG: DTI ep is %u, not %u (hack me)\n",
+ notif_xfer->bEndpoint, dti_epd->bEndpointAddress);
+ goto error;
+ }
+ if (wa->dti_urb != NULL) /* DTI URB already started */
+ goto out;
+
+ wa->dti_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (wa->dti_urb == NULL) {
+ dev_err(dev, "Can't allocate DTI URB\n");
+ goto error_dti_urb_alloc;
+ }
+ usb_fill_bulk_urb(
+ wa->dti_urb, wa->usb_dev,
+ usb_rcvbulkpipe(wa->usb_dev, 0x80 | notif_xfer->bEndpoint),
+ wa->xfer_result, wa->xfer_result_size,
+ wa_xfer_result_cb, wa);
+
+ wa->buf_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (wa->buf_in_urb == NULL) {
+ dev_err(dev, "Can't allocate BUF-IN URB\n");
+ goto error_buf_in_urb_alloc;
+ }
+ usb_fill_bulk_urb(
+ wa->buf_in_urb, wa->usb_dev,
+ usb_rcvbulkpipe(wa->usb_dev, 0x80 | notif_xfer->bEndpoint),
+ NULL, 0, wa_buf_in_cb, wa);
+ result = usb_submit_urb(wa->dti_urb, GFP_KERNEL);
+ if (result < 0) {
+ dev_err(dev, "DTI Error: Could not submit DTI URB (%d), "
+ "resetting\n", result);
+ goto error_dti_urb_submit;
+ }
+out:
+ d_fnend(4, dev, "(%p, %p) = void\n", wa, notif_hdr);
+ return;
+
+error_dti_urb_submit:
+ usb_put_urb(wa->buf_in_urb);
+error_buf_in_urb_alloc:
+ usb_put_urb(wa->dti_urb);
+ wa->dti_urb = NULL;
+error_dti_urb_alloc:
+error:
+ usb_dev_reset_delayed(wa->usb_dev);
+ d_fnend(4, dev, "(%p, %p) = void\n", wa, notif_hdr);
+ return;
+}
diff --git a/drivers/usb/wusbcore/wusbhc.c b/drivers/usb/wusbcore/wusbhc.c
new file mode 100644
index 000000000000..135b541bf3e3
--- /dev/null
+++ b/drivers/usb/wusbcore/wusbhc.c
@@ -0,0 +1,392 @@
+/*
+ * Wireless USB Host Controller
+ * sysfs glue, wusbcore module support and life cycle management
+ *
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Creation/destruction of wusbhc is split in two parts; that that
+ * doesn't require the HCD to be added (wusbhc_{create,destroy}) and
+ * the one that requires (phase B, wusbhc_b_{create,destroy}).
+ *
+ * This is so because usb_add_hcd() will start the HC, and thus, all
+ * the HC specific stuff has to be already initialiazed (like sysfs
+ * thingies).
+ */
+#include <linux/device.h>
+#include <linux/module.h>
+#include "wusbhc.h"
+
+/**
+ * Extract the wusbhc that corresponds to a USB Host Controller class device
+ *
+ * WARNING! Apply only if @dev is that of a
+ * wusbhc.usb_hcd.self->class_dev; otherwise, you loose.
+ */
+static struct wusbhc *usbhc_dev_to_wusbhc(struct device *dev)
+{
+ struct usb_bus *usb_bus = dev_get_drvdata(dev);
+ struct usb_hcd *usb_hcd = bus_to_hcd(usb_bus);
+ return usb_hcd_to_wusbhc(usb_hcd);
+}
+
+/*
+ * Show & store the current WUSB trust timeout
+ *
+ * We don't do locking--it is an 'atomic' value.
+ *
+ * The units that we store/show are always MILLISECONDS. However, the
+ * value of trust_timeout is jiffies.
+ */
+static ssize_t wusb_trust_timeout_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev);
+
+ return scnprintf(buf, PAGE_SIZE, "%u ms\n", wusbhc->trust_timeout);
+}
+
+static ssize_t wusb_trust_timeout_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev);
+ ssize_t result = -ENOSYS;
+ unsigned trust_timeout;
+
+ result = sscanf(buf, "%u", &trust_timeout);
+ if (result != 1) {
+ result = -EINVAL;
+ goto out;
+ }
+ /* FIXME: maybe we should check for range validity? */
+ wusbhc->trust_timeout = trust_timeout;
+ cancel_delayed_work(&wusbhc->keep_alive_timer);
+ flush_workqueue(wusbd);
+ queue_delayed_work(wusbd, &wusbhc->keep_alive_timer,
+ (trust_timeout * CONFIG_HZ)/1000/2);
+out:
+ return result < 0? result : size;
+}
+static DEVICE_ATTR(wusb_trust_timeout, 0644, wusb_trust_timeout_show,
+ wusb_trust_timeout_store);
+
+/*
+ * Show & store the current WUSB CHID
+ */
+static ssize_t wusb_chid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev);
+ ssize_t result = 0;
+
+ if (wusbhc->wuie_host_info != NULL)
+ result += ckhdid_printf(buf, PAGE_SIZE,
+ &wusbhc->wuie_host_info->CHID);
+ return result;
+}
+
+/*
+ * Store a new CHID
+ *
+ * This will (FIXME) trigger many changes.
+ *
+ * - Send an all zeros CHID and it will stop the controller
+ * - Send a non-zero CHID and it will start it
+ * (unless it was started, it will just change the CHID,
+ * diconnecting all devices first).
+ *
+ * So first we scan the MMC we are sent and then we act on it. We
+ * read it in the same format as we print it, an ASCII string of 16
+ * hex bytes.
+ *
+ * See wusbhc_chid_set() for more info.
+ */
+static ssize_t wusb_chid_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct wusbhc *wusbhc = usbhc_dev_to_wusbhc(dev);
+ struct wusb_ckhdid chid;
+ ssize_t result;
+
+ result = sscanf(buf,
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx\n",
+ &chid.data[0] , &chid.data[1] ,
+ &chid.data[2] , &chid.data[3] ,
+ &chid.data[4] , &chid.data[5] ,
+ &chid.data[6] , &chid.data[7] ,
+ &chid.data[8] , &chid.data[9] ,
+ &chid.data[10], &chid.data[11],
+ &chid.data[12], &chid.data[13],
+ &chid.data[14], &chid.data[15]);
+ if (result != 16) {
+ dev_err(dev, "Unrecognized CHID (need 16 8-bit hex digits): "
+ "%d\n", (int)result);
+ return -EINVAL;
+ }
+ result = wusbhc_chid_set(wusbhc, &chid);
+ return result < 0? result : size;
+}
+static DEVICE_ATTR(wusb_chid, 0644, wusb_chid_show, wusb_chid_store);
+
+/* Group all the WUSBHC attributes */
+static struct attribute *wusbhc_attrs[] = {
+ &dev_attr_wusb_trust_timeout.attr,
+ &dev_attr_wusb_chid.attr,
+ NULL,
+};
+
+static struct attribute_group wusbhc_attr_group = {
+ .name = NULL, /* we want them in the same directory */
+ .attrs = wusbhc_attrs,
+};
+
+/*
+ * Create a wusbhc instance
+ *
+ * NOTEs:
+ *
+ * - assumes *wusbhc has been zeroed and wusbhc->usb_hcd has been
+ * initialized but not added.
+ *
+ * - fill out ports_max, mmcies_max and mmcie_{add,rm} before calling.
+ *
+ * - fill out wusbhc->uwb_rc and refcount it before calling
+ * - fill out the wusbhc->sec_modes array
+ */
+int wusbhc_create(struct wusbhc *wusbhc)
+{
+ int result = 0;
+
+ wusbhc->trust_timeout = WUSB_TRUST_TIMEOUT_MS;
+ mutex_init(&wusbhc->mutex);
+ result = wusbhc_mmcie_create(wusbhc);
+ if (result < 0)
+ goto error_mmcie_create;
+ result = wusbhc_devconnect_create(wusbhc);
+ if (result < 0)
+ goto error_devconnect_create;
+ result = wusbhc_rh_create(wusbhc);
+ if (result < 0)
+ goto error_rh_create;
+ result = wusbhc_sec_create(wusbhc);
+ if (result < 0)
+ goto error_sec_create;
+ result = wusbhc_pal_register(wusbhc);
+ if (result < 0)
+ goto error_pal_register;
+ return 0;
+
+error_pal_register:
+ wusbhc_sec_destroy(wusbhc);
+error_sec_create:
+ wusbhc_rh_destroy(wusbhc);
+error_rh_create:
+ wusbhc_devconnect_destroy(wusbhc);
+error_devconnect_create:
+ wusbhc_mmcie_destroy(wusbhc);
+error_mmcie_create:
+ return result;
+}
+EXPORT_SYMBOL_GPL(wusbhc_create);
+
+static inline struct kobject *wusbhc_kobj(struct wusbhc *wusbhc)
+{
+ return &wusbhc->usb_hcd.self.controller->kobj;
+}
+
+/*
+ * Phase B of a wusbhc instance creation
+ *
+ * Creates fields that depend on wusbhc->usb_hcd having been
+ * added. This is where we create the sysfs files in
+ * /sys/class/usb_host/usb_hostX/.
+ *
+ * NOTE: Assumes wusbhc->usb_hcd has been already added by the upper
+ * layer (hwahc or whci)
+ */
+int wusbhc_b_create(struct wusbhc *wusbhc)
+{
+ int result = 0;
+ struct device *dev = wusbhc->usb_hcd.self.controller;
+
+ result = sysfs_create_group(wusbhc_kobj(wusbhc), &wusbhc_attr_group);
+ if (result < 0) {
+ dev_err(dev, "Cannot register WUSBHC attributes: %d\n", result);
+ goto error_create_attr_group;
+ }
+ /* Yep, I plan to add stuff here... */
+error_create_attr_group:
+ return result;
+}
+EXPORT_SYMBOL_GPL(wusbhc_b_create);
+
+void wusbhc_b_destroy(struct wusbhc *wusbhc)
+{
+ sysfs_remove_group(wusbhc_kobj(wusbhc), &wusbhc_attr_group);
+}
+EXPORT_SYMBOL_GPL(wusbhc_b_destroy);
+
+void wusbhc_destroy(struct wusbhc *wusbhc)
+{
+ wusbhc_pal_unregister(wusbhc);
+ wusbhc_sec_destroy(wusbhc);
+ wusbhc_rh_destroy(wusbhc);
+ wusbhc_devconnect_destroy(wusbhc);
+ wusbhc_mmcie_destroy(wusbhc);
+}
+EXPORT_SYMBOL_GPL(wusbhc_destroy);
+
+struct workqueue_struct *wusbd;
+EXPORT_SYMBOL_GPL(wusbd);
+
+/*
+ * WUSB Cluster ID allocation map
+ *
+ * Each WUSB bus in a channel is identified with a Cluster Id in the
+ * unauth address pace (WUSB1.0[4.3]). We take the range 0xe0 to 0xff
+ * (that's space for 31 WUSB controllers, as 0xff can't be taken). We
+ * start taking from 0xff, 0xfe, 0xfd... (hence the += or -= 0xff).
+ *
+ * For each one we taken, we pin it in the bitap
+ */
+#define CLUSTER_IDS 32
+static DECLARE_BITMAP(wusb_cluster_id_table, CLUSTER_IDS);
+static DEFINE_SPINLOCK(wusb_cluster_ids_lock);
+
+/*
+ * Get a WUSB Cluster ID
+ *
+ * Need to release with wusb_cluster_id_put() when done w/ it.
+ */
+/* FIXME: coordinate with the choose_addres() from the USB stack */
+/* we want to leave the top of the 128 range for cluster addresses and
+ * the bottom for device addresses (as we map them one on one with
+ * ports). */
+u8 wusb_cluster_id_get(void)
+{
+ u8 id;
+ spin_lock(&wusb_cluster_ids_lock);
+ id = find_first_zero_bit(wusb_cluster_id_table, CLUSTER_IDS);
+ if (id > CLUSTER_IDS) {
+ id = 0;
+ goto out;
+ }
+ set_bit(id, wusb_cluster_id_table);
+ id = (u8) 0xff - id;
+out:
+ spin_unlock(&wusb_cluster_ids_lock);
+ return id;
+
+}
+EXPORT_SYMBOL_GPL(wusb_cluster_id_get);
+
+/*
+ * Release a WUSB Cluster ID
+ *
+ * Obtained it with wusb_cluster_id_get()
+ */
+void wusb_cluster_id_put(u8 id)
+{
+ id = 0xff - id;
+ BUG_ON(id >= CLUSTER_IDS);
+ spin_lock(&wusb_cluster_ids_lock);
+ WARN_ON(!test_bit(id, wusb_cluster_id_table));
+ clear_bit(id, wusb_cluster_id_table);
+ spin_unlock(&wusb_cluster_ids_lock);
+}
+EXPORT_SYMBOL_GPL(wusb_cluster_id_put);
+
+/*
+ * wusbhc_giveback_urb - return an URB to the USB core
+ *
+ * [WUSB] sections 4.13 and 7.5.1 specifies the stop retrasmittion
+ * condition for the WCONNECTACK_IE is that the host has observed the
+ * associated device responding to control transfer.
+ */
+void wusbhc_giveback_urb(struct wusbhc *wusbhc, struct urb *urb, int status)
+{
+ struct wusb_dev *wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, urb->dev);
+
+ if (status == 0) {
+ wusb_dev->entry_ts = jiffies;
+
+ if (!list_empty(&wusb_dev->cack_node))
+ queue_work(wusbd, &wusb_dev->devconnect_acked_work);
+ }
+
+ usb_hcd_giveback_urb(&wusbhc->usb_hcd, urb, status);
+}
+EXPORT_SYMBOL_GPL(wusbhc_giveback_urb);
+
+static struct notifier_block wusb_usb_notifier = {
+ .notifier_call = wusb_usb_ncb,
+ .priority = INT_MAX /* Need to be called first of all */
+};
+
+static int __init wusbcore_init(void)
+{
+ int result;
+ result = wusb_crypto_init();
+ if (result < 0)
+ goto error_crypto_init;
+ /* WQ is singlethread because we need to serialize notifications */
+ wusbd = create_singlethread_workqueue("wusbd");
+ if (wusbd == NULL) {
+ result = -ENOMEM;
+ printk(KERN_ERR "WUSB-core: Cannot create wusbd workqueue\n");
+ goto error_wusbd_create;
+ }
+ usb_register_notify(&wusb_usb_notifier);
+ bitmap_zero(wusb_cluster_id_table, CLUSTER_IDS);
+ set_bit(0, wusb_cluster_id_table); /* reserve Cluster ID 0xff */
+ return 0;
+
+error_wusbd_create:
+ wusb_crypto_exit();
+error_crypto_init:
+ return result;
+
+}
+module_init(wusbcore_init);
+
+static void __exit wusbcore_exit(void)
+{
+ clear_bit(0, wusb_cluster_id_table);
+ if (!bitmap_empty(wusb_cluster_id_table, CLUSTER_IDS)) {
+ char buf[256];
+ bitmap_scnprintf(buf, sizeof(buf), wusb_cluster_id_table,
+ CLUSTER_IDS);
+ printk(KERN_ERR "BUG: WUSB Cluster IDs not released "
+ "on exit: %s\n", buf);
+ WARN_ON(1);
+ }
+ usb_unregister_notify(&wusb_usb_notifier);
+ destroy_workqueue(wusbd);
+ wusb_crypto_exit();
+}
+module_exit(wusbcore_exit);
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("Wireless USB core");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/wusbcore/wusbhc.h b/drivers/usb/wusbcore/wusbhc.h
new file mode 100644
index 000000000000..7a7719b664f7
--- /dev/null
+++ b/drivers/usb/wusbcore/wusbhc.h
@@ -0,0 +1,525 @@
+/*
+ * Wireless USB Host Controller
+ * Common infrastructure for WHCI and HWA WUSB-HC drivers
+ *
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * This driver implements parts common to all Wireless USB Host
+ * Controllers (struct wusbhc, embedding a struct usb_hcd) and is used
+ * by:
+ *
+ * - hwahc: HWA, USB-dongle that implements a Wireless USB host
+ * controller, (Wireless USB 1.0 Host-Wire-Adapter specification).
+ *
+ * - whci: WHCI, a PCI card with a wireless host controller
+ * (Wireless Host Controller Interface 1.0 specification).
+ *
+ * Check out the Design-overview.txt file in the source documentation
+ * for other details on the implementation.
+ *
+ * Main blocks:
+ *
+ * rh Root Hub emulation (part of the HCD glue)
+ *
+ * devconnect Handle all the issues related to device connection,
+ * authentication, disconnection, timeout, reseting,
+ * keepalives, etc.
+ *
+ * mmc MMC IE broadcasting handling
+ *
+ * A host controller driver just initializes its stuff and as part of
+ * that, creates a 'struct wusbhc' instance that handles all the
+ * common WUSB mechanisms. Links in the function ops that are specific
+ * to it and then registers the host controller. Ready to run.
+ */
+
+#ifndef __WUSBHC_H__
+#define __WUSBHC_H__
+
+#include <linux/usb.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/kref.h>
+#include <linux/workqueue.h>
+#include <linux/byteorder/bitfields.h>
+/* FIXME: Yes, I know: BAD--it's not my fault the USB HC iface is not
+ * public */
+#include <linux/../../drivers/usb/core/hcd.h>
+#include <linux/uwb.h>
+#include <linux/usb/wusb.h>
+
+
+/**
+ * Wireless USB device
+ *
+ * Describe a WUSB device connected to the cluster. This struct
+ * belongs to the 'struct wusb_port' it is attached to and it is
+ * responsible for putting and clearing the pointer to it.
+ *
+ * Note this "complements" the 'struct usb_device' that the usb_hcd
+ * keeps for each connected USB device. However, it extends some
+ * information that is not available (there is no hcpriv ptr in it!)
+ * *and* most importantly, it's life cycle is different. It is created
+ * as soon as we get a DN_Connect (connect request notification) from
+ * the device through the WUSB host controller; the USB stack doesn't
+ * create the device until we authenticate it. FIXME: this will
+ * change.
+ *
+ * @bos: This is allocated when the BOS descriptors are read from
+ * the device and freed upon the wusb_dev struct dying.
+ * @wusb_cap_descr: points into @bos, and has been verified to be size
+ * safe.
+ */
+struct wusb_dev {
+ struct kref refcnt;
+ struct wusbhc *wusbhc;
+ struct list_head cack_node; /* Connect-Ack list */
+ u8 port_idx;
+ u8 addr;
+ u8 beacon_type:4;
+ struct usb_encryption_descriptor ccm1_etd;
+ struct wusb_ckhdid cdid;
+ unsigned long entry_ts;
+ struct usb_bos_descriptor *bos;
+ struct usb_wireless_cap_descriptor *wusb_cap_descr;
+ struct uwb_mas_bm availability;
+ struct work_struct devconnect_acked_work;
+ struct urb *set_gtk_urb;
+ struct usb_ctrlrequest *set_gtk_req;
+ struct usb_device *usb_dev;
+};
+
+#define WUSB_DEV_ADDR_UNAUTH 0x80
+
+static inline void wusb_dev_init(struct wusb_dev *wusb_dev)
+{
+ kref_init(&wusb_dev->refcnt);
+ /* no need to init the cack_node */
+}
+
+extern void wusb_dev_destroy(struct kref *_wusb_dev);
+
+static inline struct wusb_dev *wusb_dev_get(struct wusb_dev *wusb_dev)
+{
+ kref_get(&wusb_dev->refcnt);
+ return wusb_dev;
+}
+
+static inline void wusb_dev_put(struct wusb_dev *wusb_dev)
+{
+ kref_put(&wusb_dev->refcnt, wusb_dev_destroy);
+}
+
+/**
+ * Wireless USB Host Controlller root hub "fake" ports
+ * (state and device information)
+ *
+ * Wireless USB is wireless, so there are no ports; but we
+ * fake'em. Each RC can connect a max of devices at the same time
+ * (given in the Wireless Adapter descriptor, bNumPorts or WHCI's
+ * caps), referred to in wusbhc->ports_max.
+ *
+ * See rh.c for more information.
+ *
+ * The @status and @change are packed and made to layout exactly like
+ * USB2.0[11.24.2.7], so we don't have to do much when getting the
+ * port's status [other than fake endianess changes].
+ *
+ * WUSB1.0[7.1], USB2.0[11.24.2.7.1,fig 11-10],
+ * include/linux/usb_ch9.h (#define USB_PORT_STAT_*)
+ */
+struct wusb_port {
+ union {
+ __le16 status;
+ DECL_BF_LE12(
+ u16 connection:1,
+ u16 enable:1,
+ u16 suspend:1,
+ u16 over_current:1,
+ u16 reset:1,
+ u16 reserved:3,
+ u16 power:1,
+ u16 low_speed:1,
+ u16 high_speed:1,
+ u16 test:1,
+ u16 indicator:1,
+ u16 reserved2:3
+ ) __attribute__((packed));
+ };
+ union {
+ __le16 change;
+ DECL_BF_LE7(
+ u16 c_connection:1,
+ u16 c_enable:1,
+ u16 c_suspend:1,
+ u16 c_over_current:1,
+ u16 c_reset:1,
+ u16 reserved1:3,
+ u16 reserved2:8
+ ) __attribute__((packed));
+ };
+ struct wusb_dev *wusb_dev; /* connected device's info */
+ unsigned reset_count;
+ u32 ptk_tkid;
+};
+
+/**
+ * WUSB Host Controller specifics
+ *
+ * All fields that are common to all Wireless USB controller types
+ * (HWA and WHCI) are grouped here. Host Controller
+ * functions/operations that only deal with general Wireless USB HC
+ * issues use this data type to refer to the host.
+ *
+ * @usb_hcd Instantiation of a USB host controller
+ * (initialized by upper layer [HWA=HC or WHCI].
+ *
+ * @dev Device that implements this; initialized by the
+ * upper layer (HWA-HC, WHCI...); this device should
+ * have a refcount.
+ *
+ * @trust_timeout After this time without hearing for device
+ * activity, we consider the device gone and we have to
+ * re-authenticate.
+ *
+ * Can be accessed w/o locking--however, read to a
+ * local variable then use.
+ *
+ * @chid WUSB Cluster Host ID: this is supposed to be a
+ * unique value that doesn't change across reboots (so
+ * that your devices do not require re-association).
+ *
+ * Read/Write protected by @mutex
+ *
+ * @dev_info This array has ports_max elements. It is used to
+ * give the HC information about the WUSB devices (see
+ * 'struct wusb_dev_info').
+ *
+ * For HWA we need to allocate it in heap; for WHCI it
+ * needs to be permanently mapped, so we keep it for
+ * both and make it easy. Call wusbhc->dev_info_set()
+ * to update an entry.
+ *
+ * @ports_max Number of simultaneous device connections (fake
+ * ports) this HC will take. Read-only.
+ *
+ * @port Array of port status for each fake root port. Guaranteed to
+ * always be the same lenght during device existence
+ * [this allows for some unlocked but referenced reading].
+ *
+ * @mmcies_max Max number of Information Elements this HC can send
+ * in its MMC. Read-only.
+ *
+ * @mmcie_add HC specific operation (WHCI or HWA) for adding an
+ * MMCIE.
+ *
+ * @mmcie_rm HC specific operation (WHCI or HWA) for removing an
+ * MMCIE.
+ *
+ * @enc_types Array which describes the encryptions methods
+ * supported by the host as described in WUSB1.0 --
+ * one entry per supported method. As of WUSB1.0 there
+ * is only four methods, we make space for eight just in
+ * case they decide to add some more (and pray they do
+ * it in sequential order). if 'enc_types[enc_method]
+ * != 0', then it is supported by the host. enc_method
+ * is USB_ENC_TYPE*.
+ *
+ * @set_ptk: Set the PTK and enable encryption for a device. Or, if
+ * the supplied key is NULL, disable encryption for that
+ * device.
+ *
+ * @set_gtk: Set the GTK to be used for all future broadcast packets
+ * (i.e., MMCs). With some hardware, setting the GTK may start
+ * MMC transmission.
+ *
+ * NOTE:
+ *
+ * - If wusb_dev->usb_dev is not NULL, then usb_dev is valid
+ * (wusb_dev has a refcount on it). Likewise, if usb_dev->wusb_dev
+ * is not NULL, usb_dev->wusb_dev is valid (usb_dev keeps a
+ * refcount on it).
+ *
+ * Most of the times when you need to use it, it will be non-NULL,
+ * so there is no real need to check for it (wusb_dev will
+ * dissapear before usb_dev).
+ *
+ * - The following fields need to be filled out before calling
+ * wusbhc_create(): ports_max, mmcies_max, mmcie_{add,rm}.
+ *
+ * - there is no wusbhc_init() method, we do everything in
+ * wusbhc_create().
+ *
+ * - Creation is done in two phases, wusbhc_create() and
+ * wusbhc_create_b(); b are the parts that need to be called after
+ * calling usb_hcd_add(&wusbhc->usb_hcd).
+ */
+struct wusbhc {
+ struct usb_hcd usb_hcd; /* HAS TO BE 1st */
+ struct device *dev;
+ struct uwb_rc *uwb_rc;
+ struct uwb_pal pal;
+
+ unsigned trust_timeout; /* in jiffies */
+ struct wuie_host_info *wuie_host_info; /* Includes CHID */
+
+ struct mutex mutex; /* locks everything else */
+ u16 cluster_id; /* Wireless USB Cluster ID */
+ struct wusb_port *port; /* Fake port status handling */
+ struct wusb_dev_info *dev_info; /* for Set Device Info mgmt */
+ u8 ports_max;
+ unsigned active:1; /* currently xmit'ing MMCs */
+ struct wuie_keep_alive keep_alive_ie; /* protected by mutex */
+ struct delayed_work keep_alive_timer;
+ struct list_head cack_list; /* Connect acknowledging */
+ size_t cack_count; /* protected by 'mutex' */
+ struct wuie_connect_ack cack_ie;
+ struct uwb_rsv *rsv; /* cluster bandwidth reservation */
+
+ struct mutex mmcie_mutex; /* MMC WUIE handling */
+ struct wuie_hdr **mmcie; /* WUIE array */
+ u8 mmcies_max;
+ /* FIXME: make wusbhc_ops? */
+ int (*start)(struct wusbhc *wusbhc);
+ void (*stop)(struct wusbhc *wusbhc);
+ int (*mmcie_add)(struct wusbhc *wusbhc, u8 interval, u8 repeat_cnt,
+ u8 handle, struct wuie_hdr *wuie);
+ int (*mmcie_rm)(struct wusbhc *wusbhc, u8 handle);
+ int (*dev_info_set)(struct wusbhc *, struct wusb_dev *wusb_dev);
+ int (*bwa_set)(struct wusbhc *wusbhc, s8 stream_index,
+ const struct uwb_mas_bm *);
+ int (*set_ptk)(struct wusbhc *wusbhc, u8 port_idx,
+ u32 tkid, const void *key, size_t key_size);
+ int (*set_gtk)(struct wusbhc *wusbhc,
+ u32 tkid, const void *key, size_t key_size);
+ int (*set_num_dnts)(struct wusbhc *wusbhc, u8 interval, u8 slots);
+
+ struct {
+ struct usb_key_descriptor descr;
+ u8 data[16]; /* GTK key data */
+ } __attribute__((packed)) gtk;
+ union wusb_key_index gtk_index;
+ u32 gtk_tkid;
+ struct work_struct gtk_rekey_done_work;
+ int pending_set_gtks;
+
+ struct usb_encryption_descriptor *ccm1_etd;
+};
+
+#define usb_hcd_to_wusbhc(u) container_of((u), struct wusbhc, usb_hcd)
+
+
+extern int wusbhc_create(struct wusbhc *);
+extern int wusbhc_b_create(struct wusbhc *);
+extern void wusbhc_b_destroy(struct wusbhc *);
+extern void wusbhc_destroy(struct wusbhc *);
+extern int wusb_dev_sysfs_add(struct wusbhc *, struct usb_device *,
+ struct wusb_dev *);
+extern void wusb_dev_sysfs_rm(struct wusb_dev *);
+extern int wusbhc_sec_create(struct wusbhc *);
+extern int wusbhc_sec_start(struct wusbhc *);
+extern void wusbhc_sec_stop(struct wusbhc *);
+extern void wusbhc_sec_destroy(struct wusbhc *);
+extern void wusbhc_giveback_urb(struct wusbhc *wusbhc, struct urb *urb,
+ int status);
+
+int wusbhc_pal_register(struct wusbhc *wusbhc);
+void wusbhc_pal_unregister(struct wusbhc *wusbhc);
+
+/*
+ * Return @usb_dev's @usb_hcd (properly referenced) or NULL if gone
+ *
+ * @usb_dev: USB device, UNLOCKED and referenced (or otherwise, safe ptr)
+ *
+ * This is a safe assumption as @usb_dev->bus is referenced all the
+ * time during the @usb_dev life cycle.
+ */
+static inline struct usb_hcd *usb_hcd_get_by_usb_dev(struct usb_device *usb_dev)
+{
+ struct usb_hcd *usb_hcd;
+ usb_hcd = container_of(usb_dev->bus, struct usb_hcd, self);
+ return usb_get_hcd(usb_hcd);
+}
+
+/*
+ * Increment the reference count on a wusbhc.
+ *
+ * @wusbhc's life cycle is identical to that of the underlying usb_hcd.
+ */
+static inline struct wusbhc *wusbhc_get(struct wusbhc *wusbhc)
+{
+ return usb_get_hcd(&wusbhc->usb_hcd)? wusbhc : NULL;
+}
+
+/*
+ * Return the wusbhc associated to a @usb_dev
+ *
+ * @usb_dev: USB device, UNLOCKED and referenced (or otherwise, safe ptr)
+ *
+ * @returns: wusbhc for @usb_dev; NULL if the @usb_dev is being torn down.
+ * WARNING: referenced at the usb_hcd level, unlocked
+ *
+ * FIXME: move offline
+ */
+static inline struct wusbhc *wusbhc_get_by_usb_dev(struct usb_device *usb_dev)
+{
+ struct wusbhc *wusbhc = NULL;
+ struct usb_hcd *usb_hcd;
+ if (usb_dev->devnum > 1 && !usb_dev->wusb) {
+ /* but root hubs */
+ dev_err(&usb_dev->dev, "devnum %d wusb %d\n", usb_dev->devnum,
+ usb_dev->wusb);
+ BUG_ON(usb_dev->devnum > 1 && !usb_dev->wusb);
+ }
+ usb_hcd = usb_hcd_get_by_usb_dev(usb_dev);
+ if (usb_hcd == NULL)
+ return NULL;
+ BUG_ON(usb_hcd->wireless == 0);
+ return wusbhc = usb_hcd_to_wusbhc(usb_hcd);
+}
+
+
+static inline void wusbhc_put(struct wusbhc *wusbhc)
+{
+ usb_put_hcd(&wusbhc->usb_hcd);
+}
+
+int wusbhc_start(struct wusbhc *wusbhc, const struct wusb_ckhdid *chid);
+void wusbhc_stop(struct wusbhc *wusbhc);
+extern int wusbhc_chid_set(struct wusbhc *, const struct wusb_ckhdid *);
+
+/* Device connect handling */
+extern int wusbhc_devconnect_create(struct wusbhc *);
+extern void wusbhc_devconnect_destroy(struct wusbhc *);
+extern int wusbhc_devconnect_start(struct wusbhc *wusbhc,
+ const struct wusb_ckhdid *chid);
+extern void wusbhc_devconnect_stop(struct wusbhc *wusbhc);
+extern int wusbhc_devconnect_auth(struct wusbhc *, u8);
+extern void wusbhc_handle_dn(struct wusbhc *, u8 srcaddr,
+ struct wusb_dn_hdr *dn_hdr, size_t size);
+extern int wusbhc_dev_reset(struct wusbhc *wusbhc, u8 port);
+extern void __wusbhc_dev_disable(struct wusbhc *wusbhc, u8 port);
+extern int wusb_usb_ncb(struct notifier_block *nb, unsigned long val,
+ void *priv);
+extern int wusb_set_dev_addr(struct wusbhc *wusbhc, struct wusb_dev *wusb_dev,
+ u8 addr);
+
+/* Wireless USB fake Root Hub methods */
+extern int wusbhc_rh_create(struct wusbhc *);
+extern void wusbhc_rh_destroy(struct wusbhc *);
+
+extern int wusbhc_rh_status_data(struct usb_hcd *, char *);
+extern int wusbhc_rh_control(struct usb_hcd *, u16, u16, u16, char *, u16);
+extern int wusbhc_rh_suspend(struct usb_hcd *);
+extern int wusbhc_rh_resume(struct usb_hcd *);
+extern int wusbhc_rh_start_port_reset(struct usb_hcd *, unsigned);
+
+/* MMC handling */
+extern int wusbhc_mmcie_create(struct wusbhc *);
+extern void wusbhc_mmcie_destroy(struct wusbhc *);
+extern int wusbhc_mmcie_set(struct wusbhc *, u8 interval, u8 repeat_cnt,
+ struct wuie_hdr *);
+extern void wusbhc_mmcie_rm(struct wusbhc *, struct wuie_hdr *);
+
+/* Bandwidth reservation */
+int wusbhc_rsv_establish(struct wusbhc *wusbhc);
+void wusbhc_rsv_terminate(struct wusbhc *wusbhc);
+
+/*
+ * I've always said
+ * I wanted a wedding in a church...
+ *
+ * but lately I've been thinking about
+ * the Botanical Gardens.
+ *
+ * We could do it by the tulips.
+ * It'll be beautiful
+ *
+ * --Security!
+ */
+extern int wusb_dev_sec_add(struct wusbhc *, struct usb_device *,
+ struct wusb_dev *);
+extern void wusb_dev_sec_rm(struct wusb_dev *) ;
+extern int wusb_dev_4way_handshake(struct wusbhc *, struct wusb_dev *,
+ struct wusb_ckhdid *chid,
+ struct wusb_ckhdid *cdid,
+ struct wusb_ckhdid *ck);
+void wusbhc_gtk_rekey(struct wusbhc *wusbhc);
+
+
+/* WUSB Cluster ID handling */
+extern u8 wusb_cluster_id_get(void);
+extern void wusb_cluster_id_put(u8);
+
+/*
+ * wusb_port_by_idx - return the port associated to a zero-based port index
+ *
+ * NOTE: valid without locking as long as wusbhc is referenced (as the
+ * number of ports doesn't change). The data pointed to has to
+ * be verified though :)
+ */
+static inline struct wusb_port *wusb_port_by_idx(struct wusbhc *wusbhc,
+ u8 port_idx)
+{
+ return &wusbhc->port[port_idx];
+}
+
+/*
+ * wusb_port_no_to_idx - Convert port number (per usb_dev->portnum) to
+ * a port_idx.
+ *
+ * USB stack USB ports are 1 based!!
+ *
+ * NOTE: only valid for WUSB devices!!!
+ */
+static inline u8 wusb_port_no_to_idx(u8 port_no)
+{
+ return port_no - 1;
+}
+
+extern struct wusb_dev *__wusb_dev_get_by_usb_dev(struct wusbhc *,
+ struct usb_device *);
+
+/*
+ * Return a referenced wusb_dev given a @usb_dev
+ *
+ * Returns NULL if the usb_dev is being torn down.
+ *
+ * FIXME: move offline
+ */
+static inline
+struct wusb_dev *wusb_dev_get_by_usb_dev(struct usb_device *usb_dev)
+{
+ struct wusbhc *wusbhc;
+ struct wusb_dev *wusb_dev;
+ wusbhc = wusbhc_get_by_usb_dev(usb_dev);
+ if (wusbhc == NULL)
+ return NULL;
+ mutex_lock(&wusbhc->mutex);
+ wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, usb_dev);
+ mutex_unlock(&wusbhc->mutex);
+ wusbhc_put(wusbhc);
+ return wusb_dev;
+}
+
+/* Misc */
+
+extern struct workqueue_struct *wusbd;
+#endif /* #ifndef __WUSBHC_H__ */
diff --git a/drivers/uwb/Kconfig b/drivers/uwb/Kconfig
new file mode 100644
index 000000000000..317e9fefbc01
--- /dev/null
+++ b/drivers/uwb/Kconfig
@@ -0,0 +1,89 @@
+#
+# UWB device configuration
+#
+
+menuconfig UWB
+ tristate "Ultra Wide Band devices"
+ depends on PCI
+ default n
+ help
+ UWB is a high-bandwidth, low-power, point-to-point radio
+ technology using a wide spectrum (3.1-10.6GHz). It is
+ optimized for in-room use (480Mbps at 2 meters, 110Mbps at
+ 10m). It serves as the transport layer for other protocols,
+ such as Wireless USB (WUSB), IP (WLP) and upcoming
+ Bluetooth and 1394
+
+ The topology is peer to peer; however, higher level
+ protocols (such as WUSB) might impose a master/slave
+ relationship.
+
+ Say Y here if your computer has UWB radio controllers (USB or PCI)
+ based. You will need to enable the radio controllers
+ below. It is ok to select all of them, no harm done.
+
+ For more help check the UWB and WUSB related files in
+ <file:Documentation/usb/>.
+
+ To compile the UWB stack as a module, choose M here.
+
+if UWB
+
+config UWB_HWA
+ tristate "UWB Radio Control driver for WUSB-compliant USB dongles (HWA)"
+ depends on USB
+ help
+ This driver enables the radio controller for HWA USB
+ devices. HWA stands for Host Wire Adapter, and it is a UWB
+ Radio Controller connected to your system via USB. Most of
+ them come with a Wireless USB host controller also.
+
+ To compile this driver select Y (built in) or M (module). It
+ is safe to select any even if you do not have the hardware.
+
+config UWB_WHCI
+ tristate "UWB Radio Control driver for WHCI-compliant cards"
+ depends on PCI
+ help
+ This driver enables the radio controller for WHCI cards.
+
+ WHCI is an specification developed by Intel
+ (http://www.intel.com/technology/comms/wusb/whci.htm) much
+ in the spirit of USB's EHCI, but for UWB and Wireless USB
+ radio/host controllers connected via memmory mapping (eg:
+ PCI). Most of these cards come also with a Wireless USB host
+ controller.
+
+ To compile this driver select Y (built in) or M (module). It
+ is safe to select any even if you do not have the hardware.
+
+config UWB_WLP
+ tristate "Support WiMedia Link Protocol (Ethernet/IP over UWB)"
+ depends on UWB && NET
+ help
+ This is a common library for drivers that implement
+ networking over UWB.
+
+config UWB_I1480U
+ tristate "Support for Intel Wireless UWB Link 1480 HWA"
+ depends on UWB_HWA
+ select FW_LOADER
+ help
+ This driver enables support for the i1480 when connected via
+ USB. It consists of a firmware uploader that will enable it
+ to behave as an HWA device.
+
+ To compile this driver select Y (built in) or M (module). It
+ is safe to select any even if you do not have the hardware.
+
+config UWB_I1480U_WLP
+ tristate "Support for Intel Wireless UWB Link 1480 HWA's WLP interface"
+ depends on UWB_I1480U && UWB_WLP && NET
+ help
+ This driver enables WLP support for the i1480 when connected via
+ USB. WLP is the WiMedia Link Protocol, or IP over UWB.
+
+ To compile this driver select Y (built in) or M (module). It
+ is safe to select any even if you don't have the hardware.
+
+endif # UWB
diff --git a/drivers/uwb/Makefile b/drivers/uwb/Makefile
new file mode 100644
index 000000000000..257e6908304c
--- /dev/null
+++ b/drivers/uwb/Makefile
@@ -0,0 +1,29 @@
+obj-$(CONFIG_UWB) += uwb.o
+obj-$(CONFIG_UWB_WLP) += wlp/
+obj-$(CONFIG_UWB_WHCI) += umc.o whci.o whc-rc.o
+obj-$(CONFIG_UWB_HWA) += hwa-rc.o
+obj-$(CONFIG_UWB_I1480U) += i1480/
+
+uwb-objs := \
+ address.o \
+ beacon.o \
+ driver.o \
+ drp.o \
+ drp-avail.o \
+ drp-ie.o \
+ est.o \
+ ie.o \
+ lc-dev.o \
+ lc-rc.o \
+ neh.o \
+ pal.o \
+ reset.o \
+ rsv.o \
+ scan.o \
+ uwb-debug.o \
+ uwbd.o
+
+umc-objs := \
+ umc-bus.o \
+ umc-dev.o \
+ umc-drv.o
diff --git a/drivers/uwb/address.c b/drivers/uwb/address.c
new file mode 100644
index 000000000000..760758147a64
--- /dev/null
+++ b/drivers/uwb/address.c
@@ -0,0 +1,374 @@
+/*
+ * Ultra Wide Band
+ * Address management
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/random.h>
+#include <linux/etherdevice.h>
+#include <linux/uwb/debug.h>
+#include "uwb-internal.h"
+
+
+/** Device Address Management command */
+struct uwb_rc_cmd_dev_addr_mgmt {
+ struct uwb_rccb rccb;
+ u8 bmOperationType;
+ u8 baAddr[6];
+} __attribute__((packed));
+
+
+/**
+ * Low level command for setting/getting UWB radio's addresses
+ *
+ * @hwarc: HWA Radio Control interface instance
+ * @bmOperationType:
+ * Set/get, MAC/DEV (see WUSB1.0[8.6.2.2])
+ * @baAddr: address buffer--assumed to have enough data to hold
+ * the address type requested.
+ * @reply: Pointer to reply buffer (can be stack allocated)
+ * @returns: 0 if ok, < 0 errno code on error.
+ *
+ * @cmd has to be allocated because USB cannot grok USB or vmalloc
+ * buffers depending on your combination of host architecture.
+ */
+static
+int uwb_rc_dev_addr_mgmt(struct uwb_rc *rc,
+ u8 bmOperationType, const u8 *baAddr,
+ struct uwb_rc_evt_dev_addr_mgmt *reply)
+{
+ int result;
+ struct uwb_rc_cmd_dev_addr_mgmt *cmd;
+
+ result = -ENOMEM;
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL)
+ goto error_kzalloc;
+ cmd->rccb.bCommandType = UWB_RC_CET_GENERAL;
+ cmd->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_DEV_ADDR_MGMT);
+ cmd->bmOperationType = bmOperationType;
+ if (baAddr) {
+ size_t size = 0;
+ switch (bmOperationType >> 1) {
+ case 0: size = 2; break;
+ case 1: size = 6; break;
+ default: BUG();
+ }
+ memcpy(cmd->baAddr, baAddr, size);
+ }
+ reply->rceb.bEventType = UWB_RC_CET_GENERAL;
+ reply->rceb.wEvent = UWB_RC_CMD_DEV_ADDR_MGMT;
+ result = uwb_rc_cmd(rc, "DEV-ADDR-MGMT",
+ &cmd->rccb, sizeof(*cmd),
+ &reply->rceb, sizeof(*reply));
+ if (result < 0)
+ goto error_cmd;
+ if (result < sizeof(*reply)) {
+ dev_err(&rc->uwb_dev.dev,
+ "DEV-ADDR-MGMT: not enough data replied: "
+ "%d vs %zu bytes needed\n", result, sizeof(*reply));
+ result = -ENOMSG;
+ } else if (reply->bResultCode != UWB_RC_RES_SUCCESS) {
+ dev_err(&rc->uwb_dev.dev,
+ "DEV-ADDR-MGMT: command execution failed: %s (%d)\n",
+ uwb_rc_strerror(reply->bResultCode),
+ reply->bResultCode);
+ result = -EIO;
+ } else
+ result = 0;
+error_cmd:
+ kfree(cmd);
+error_kzalloc:
+ return result;
+}
+
+
+/**
+ * Set the UWB RC MAC or device address.
+ *
+ * @rc: UWB Radio Controller
+ * @_addr: Pointer to address to write [assumed to be either a
+ * 'struct uwb_mac_addr *' or a 'struct uwb_dev_addr *'].
+ * @type: Type of address to set (UWB_ADDR_DEV or UWB_ADDR_MAC).
+ * @returns: 0 if ok, < 0 errno code on error.
+ *
+ * Some anal retentivity here: even if both 'struct
+ * uwb_{dev,mac}_addr' have the actual byte array in the same offset
+ * and I could just pass _addr to hwarc_cmd_dev_addr_mgmt(), I prefer
+ * to use some syntatic sugar in case someday we decide to change the
+ * format of the structs. The compiler will optimize it out anyway.
+ */
+static int uwb_rc_addr_set(struct uwb_rc *rc,
+ const void *_addr, enum uwb_addr_type type)
+{
+ int result;
+ u8 bmOperationType = 0x1; /* Set address */
+ const struct uwb_dev_addr *dev_addr = _addr;
+ const struct uwb_mac_addr *mac_addr = _addr;
+ struct uwb_rc_evt_dev_addr_mgmt reply;
+ const u8 *baAddr;
+
+ result = -EINVAL;
+ switch (type) {
+ case UWB_ADDR_DEV:
+ baAddr = dev_addr->data;
+ break;
+ case UWB_ADDR_MAC:
+ baAddr = mac_addr->data;
+ bmOperationType |= 0x2;
+ break;
+ default:
+ return result;
+ }
+ return uwb_rc_dev_addr_mgmt(rc, bmOperationType, baAddr, &reply);
+}
+
+
+/**
+ * Get the UWB radio's MAC or device address.
+ *
+ * @rc: UWB Radio Controller
+ * @_addr: Where to write the address data [assumed to be either a
+ * 'struct uwb_mac_addr *' or a 'struct uwb_dev_addr *'].
+ * @type: Type of address to get (UWB_ADDR_DEV or UWB_ADDR_MAC).
+ * @returns: 0 if ok (and *_addr set), < 0 errno code on error.
+ *
+ * See comment in uwb_rc_addr_set() about anal retentivity in the
+ * type handling of the address variables.
+ */
+static int uwb_rc_addr_get(struct uwb_rc *rc,
+ void *_addr, enum uwb_addr_type type)
+{
+ int result;
+ u8 bmOperationType = 0x0; /* Get address */
+ struct uwb_rc_evt_dev_addr_mgmt evt;
+ struct uwb_dev_addr *dev_addr = _addr;
+ struct uwb_mac_addr *mac_addr = _addr;
+ u8 *baAddr;
+
+ result = -EINVAL;
+ switch (type) {
+ case UWB_ADDR_DEV:
+ baAddr = dev_addr->data;
+ break;
+ case UWB_ADDR_MAC:
+ bmOperationType |= 0x2;
+ baAddr = mac_addr->data;
+ break;
+ default:
+ return result;
+ }
+ result = uwb_rc_dev_addr_mgmt(rc, bmOperationType, baAddr, &evt);
+ if (result == 0)
+ switch (type) {
+ case UWB_ADDR_DEV:
+ memcpy(&dev_addr->data, evt.baAddr,
+ sizeof(dev_addr->data));
+ break;
+ case UWB_ADDR_MAC:
+ memcpy(&mac_addr->data, evt.baAddr,
+ sizeof(mac_addr->data));
+ break;
+ default: /* shut gcc up */
+ BUG();
+ }
+ return result;
+}
+
+
+/** Get @rc's MAC address to @addr */
+int uwb_rc_mac_addr_get(struct uwb_rc *rc,
+ struct uwb_mac_addr *addr) {
+ return uwb_rc_addr_get(rc, addr, UWB_ADDR_MAC);
+}
+EXPORT_SYMBOL_GPL(uwb_rc_mac_addr_get);
+
+
+/** Get @rc's device address to @addr */
+int uwb_rc_dev_addr_get(struct uwb_rc *rc,
+ struct uwb_dev_addr *addr) {
+ return uwb_rc_addr_get(rc, addr, UWB_ADDR_DEV);
+}
+EXPORT_SYMBOL_GPL(uwb_rc_dev_addr_get);
+
+
+/** Set @rc's address to @addr */
+int uwb_rc_mac_addr_set(struct uwb_rc *rc,
+ const struct uwb_mac_addr *addr)
+{
+ int result = -EINVAL;
+ mutex_lock(&rc->uwb_dev.mutex);
+ result = uwb_rc_addr_set(rc, addr, UWB_ADDR_MAC);
+ mutex_unlock(&rc->uwb_dev.mutex);
+ return result;
+}
+
+
+/** Set @rc's address to @addr */
+int uwb_rc_dev_addr_set(struct uwb_rc *rc,
+ const struct uwb_dev_addr *addr)
+{
+ int result = -EINVAL;
+ mutex_lock(&rc->uwb_dev.mutex);
+ result = uwb_rc_addr_set(rc, addr, UWB_ADDR_DEV);
+ rc->uwb_dev.dev_addr = *addr;
+ mutex_unlock(&rc->uwb_dev.mutex);
+ return result;
+}
+
+/* Returns !0 if given address is already assigned to device. */
+int __uwb_mac_addr_assigned_check(struct device *dev, void *_addr)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_mac_addr *addr = _addr;
+
+ if (!uwb_mac_addr_cmp(addr, &uwb_dev->mac_addr))
+ return !0;
+ return 0;
+}
+
+/* Returns !0 if given address is already assigned to device. */
+int __uwb_dev_addr_assigned_check(struct device *dev, void *_addr)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_dev_addr *addr = _addr;
+ if (!uwb_dev_addr_cmp(addr, &uwb_dev->dev_addr))
+ return !0;
+ return 0;
+}
+
+/**
+ * uwb_dev_addr_assign - assigned a generated DevAddr to a radio controller
+ * @rc: the (local) radio controller device requiring a new DevAddr
+ *
+ * A new DevAddr is required when:
+ * - first setting up a radio controller
+ * - if the hardware reports a DevAddr conflict
+ *
+ * The DevAddr is randomly generated in the generated DevAddr range
+ * [0x100, 0xfeff]. The number of devices in a beacon group is limited
+ * by mMaxBPLength (96) so this address space will never be exhausted.
+ *
+ * [ECMA-368] 17.1.1, 17.16.
+ */
+int uwb_rc_dev_addr_assign(struct uwb_rc *rc)
+{
+ struct uwb_dev_addr new_addr;
+
+ do {
+ get_random_bytes(new_addr.data, sizeof(new_addr.data));
+ } while (new_addr.data[0] == 0x00 || new_addr.data[0] == 0xff
+ || __uwb_dev_addr_assigned(rc, &new_addr));
+
+ return uwb_rc_dev_addr_set(rc, &new_addr);
+}
+
+/**
+ * uwbd_evt_handle_rc_dev_addr_conflict - handle a DEV_ADDR_CONFLICT event
+ * @evt: the DEV_ADDR_CONFLICT notification from the radio controller
+ *
+ * A new (non-conflicting) DevAddr is assigned to the radio controller.
+ *
+ * [ECMA-368] 17.1.1.1.
+ */
+int uwbd_evt_handle_rc_dev_addr_conflict(struct uwb_event *evt)
+{
+ struct uwb_rc *rc = evt->rc;
+
+ return uwb_rc_dev_addr_assign(rc);
+}
+
+/*
+ * Print the 48-bit EUI MAC address of the radio controller when
+ * reading /sys/class/uwb_rc/XX/mac_address
+ */
+static ssize_t uwb_rc_mac_addr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_rc *rc = uwb_dev->rc;
+ struct uwb_mac_addr addr;
+ ssize_t result;
+
+ mutex_lock(&rc->uwb_dev.mutex);
+ result = uwb_rc_addr_get(rc, &addr, UWB_ADDR_MAC);
+ mutex_unlock(&rc->uwb_dev.mutex);
+ if (result >= 0) {
+ result = uwb_mac_addr_print(buf, UWB_ADDR_STRSIZE, &addr);
+ buf[result++] = '\n';
+ }
+ return result;
+}
+
+/*
+ * Parse a 48 bit address written to /sys/class/uwb_rc/XX/mac_address
+ * and if correct, set it.
+ */
+static ssize_t uwb_rc_mac_addr_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_rc *rc = uwb_dev->rc;
+ struct uwb_mac_addr addr;
+ ssize_t result;
+
+ result = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\n",
+ &addr.data[0], &addr.data[1], &addr.data[2],
+ &addr.data[3], &addr.data[4], &addr.data[5]);
+ if (result != 6) {
+ result = -EINVAL;
+ goto out;
+ }
+ if (is_multicast_ether_addr(addr.data)) {
+ dev_err(&rc->uwb_dev.dev, "refusing to set multicast "
+ "MAC address %s\n", buf);
+ result = -EINVAL;
+ goto out;
+ }
+ result = uwb_rc_mac_addr_set(rc, &addr);
+ if (result == 0)
+ rc->uwb_dev.mac_addr = addr;
+out:
+ return result < 0? result : size;
+}
+DEVICE_ATTR(mac_address, S_IRUGO | S_IWUSR, uwb_rc_mac_addr_show, uwb_rc_mac_addr_store);
+
+/** Print @addr to @buf, @return bytes written */
+size_t __uwb_addr_print(char *buf, size_t buf_size, const unsigned char *addr,
+ int type)
+{
+ size_t result;
+ if (type)
+ result = scnprintf(buf, buf_size,
+ "%02x:%02x:%02x:%02x:%02x:%02x",
+ addr[0], addr[1], addr[2],
+ addr[3], addr[4], addr[5]);
+ else
+ result = scnprintf(buf, buf_size, "%02x:%02x",
+ addr[1], addr[0]);
+ return result;
+}
+EXPORT_SYMBOL_GPL(__uwb_addr_print);
diff --git a/drivers/uwb/beacon.c b/drivers/uwb/beacon.c
new file mode 100644
index 000000000000..0b2bc581dc96
--- /dev/null
+++ b/drivers/uwb/beacon.c
@@ -0,0 +1,707 @@
+/*
+ * Ultra Wide Band
+ * Beacon management
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kdev_t.h>
+#include "uwb-internal.h"
+
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+/** Start Beaconing command structure */
+struct uwb_rc_cmd_start_beacon {
+ struct uwb_rccb rccb;
+ __le16 wBPSTOffset;
+ u8 bChannelNumber;
+} __attribute__((packed));
+
+
+static int uwb_rc_start_beacon(struct uwb_rc *rc, u16 bpst_offset, u8 channel)
+{
+ int result;
+ struct uwb_rc_cmd_start_beacon *cmd;
+ struct uwb_rc_evt_confirm reply;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL)
+ return -ENOMEM;
+ cmd->rccb.bCommandType = UWB_RC_CET_GENERAL;
+ cmd->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_START_BEACON);
+ cmd->wBPSTOffset = cpu_to_le16(bpst_offset);
+ cmd->bChannelNumber = channel;
+ reply.rceb.bEventType = UWB_RC_CET_GENERAL;
+ reply.rceb.wEvent = UWB_RC_CMD_START_BEACON;
+ result = uwb_rc_cmd(rc, "START-BEACON", &cmd->rccb, sizeof(*cmd),
+ &reply.rceb, sizeof(reply));
+ if (result < 0)
+ goto error_cmd;
+ if (reply.bResultCode != UWB_RC_RES_SUCCESS) {
+ dev_err(&rc->uwb_dev.dev,
+ "START-BEACON: command execution failed: %s (%d)\n",
+ uwb_rc_strerror(reply.bResultCode), reply.bResultCode);
+ result = -EIO;
+ }
+error_cmd:
+ kfree(cmd);
+ return result;
+}
+
+static int uwb_rc_stop_beacon(struct uwb_rc *rc)
+{
+ int result;
+ struct uwb_rccb *cmd;
+ struct uwb_rc_evt_confirm reply;
+
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL)
+ return -ENOMEM;
+ cmd->bCommandType = UWB_RC_CET_GENERAL;
+ cmd->wCommand = cpu_to_le16(UWB_RC_CMD_STOP_BEACON);
+ reply.rceb.bEventType = UWB_RC_CET_GENERAL;
+ reply.rceb.wEvent = UWB_RC_CMD_STOP_BEACON;
+ result = uwb_rc_cmd(rc, "STOP-BEACON", cmd, sizeof(*cmd),
+ &reply.rceb, sizeof(reply));
+ if (result < 0)
+ goto error_cmd;
+ if (reply.bResultCode != UWB_RC_RES_SUCCESS) {
+ dev_err(&rc->uwb_dev.dev,
+ "STOP-BEACON: command execution failed: %s (%d)\n",
+ uwb_rc_strerror(reply.bResultCode), reply.bResultCode);
+ result = -EIO;
+ }
+error_cmd:
+ kfree(cmd);
+ return result;
+}
+
+/*
+ * Start/stop beacons
+ *
+ * @rc: UWB Radio Controller to operate on
+ * @channel: UWB channel on which to beacon (WUSB[table
+ * 5-12]). If -1, stop beaconing.
+ * @bpst_offset: Beacon Period Start Time offset; FIXME-do zero
+ *
+ * According to WHCI 0.95 [4.13.6] the driver will only receive the RCEB
+ * of a SET IE command after the device sent the first beacon that includes
+ * the IEs specified in the SET IE command. So, after we start beaconing we
+ * check if there is anything in the IE cache and call the SET IE command
+ * if needed.
+ */
+int uwb_rc_beacon(struct uwb_rc *rc, int channel, unsigned bpst_offset)
+{
+ int result;
+ struct device *dev = &rc->uwb_dev.dev;
+
+ mutex_lock(&rc->uwb_dev.mutex);
+ if (channel < 0)
+ channel = -1;
+ if (channel == -1)
+ result = uwb_rc_stop_beacon(rc);
+ else {
+ /* channel >= 0...dah */
+ result = uwb_rc_start_beacon(rc, bpst_offset, channel);
+ if (result < 0)
+ goto out_up;
+ if (le16_to_cpu(rc->ies->wIELength) > 0) {
+ result = uwb_rc_set_ie(rc, rc->ies);
+ if (result < 0) {
+ dev_err(dev, "Cannot set new IE on device: "
+ "%d\n", result);
+ result = uwb_rc_stop_beacon(rc);
+ channel = -1;
+ bpst_offset = 0;
+ } else
+ result = 0;
+ }
+ }
+
+ if (result < 0)
+ goto out_up;
+ rc->beaconing = channel;
+
+ uwb_notify(rc, NULL, uwb_bg_joined(rc) ? UWB_NOTIF_BG_JOIN : UWB_NOTIF_BG_LEAVE);
+
+out_up:
+ mutex_unlock(&rc->uwb_dev.mutex);
+ return result;
+}
+
+/*
+ * Beacon cache
+ *
+ * The purpose of this is to speed up the lookup of becon information
+ * when a new beacon arrives. The UWB Daemon uses it also to keep a
+ * tab of which devices are in radio distance and which not. When a
+ * device's beacon stays present for more than a certain amount of
+ * time, it is considered a new, usable device. When a beacon ceases
+ * to be received for a certain amount of time, it is considered that
+ * the device is gone.
+ *
+ * FIXME: use an allocator for the entries
+ * FIXME: use something faster for search than a list
+ */
+
+struct uwb_beca uwb_beca = {
+ .list = LIST_HEAD_INIT(uwb_beca.list),
+ .mutex = __MUTEX_INITIALIZER(uwb_beca.mutex)
+};
+
+
+void uwb_bce_kfree(struct kref *_bce)
+{
+ struct uwb_beca_e *bce = container_of(_bce, struct uwb_beca_e, refcnt);
+
+ kfree(bce->be);
+ kfree(bce);
+}
+
+
+/* Find a beacon by dev addr in the cache */
+static
+struct uwb_beca_e *__uwb_beca_find_bydev(const struct uwb_dev_addr *dev_addr)
+{
+ struct uwb_beca_e *bce, *next;
+ list_for_each_entry_safe(bce, next, &uwb_beca.list, node) {
+ d_printf(6, NULL, "looking for addr %02x:%02x in %02x:%02x\n",
+ dev_addr->data[0], dev_addr->data[1],
+ bce->dev_addr.data[0], bce->dev_addr.data[1]);
+ if (!memcmp(&bce->dev_addr, dev_addr, sizeof(bce->dev_addr)))
+ goto out;
+ }
+ bce = NULL;
+out:
+ return bce;
+}
+
+/* Find a beacon by dev addr in the cache */
+static
+struct uwb_beca_e *__uwb_beca_find_bymac(const struct uwb_mac_addr *mac_addr)
+{
+ struct uwb_beca_e *bce, *next;
+ list_for_each_entry_safe(bce, next, &uwb_beca.list, node) {
+ if (!memcmp(bce->mac_addr, mac_addr->data,
+ sizeof(bce->mac_addr)))
+ goto out;
+ }
+ bce = NULL;
+out:
+ return bce;
+}
+
+/**
+ * uwb_dev_get_by_devaddr - get a UWB device with a specific DevAddr
+ * @rc: the radio controller that saw the device
+ * @devaddr: DevAddr of the UWB device to find
+ *
+ * There may be more than one matching device (in the case of a
+ * DevAddr conflict), but only the first one is returned.
+ */
+struct uwb_dev *uwb_dev_get_by_devaddr(struct uwb_rc *rc,
+ const struct uwb_dev_addr *devaddr)
+{
+ struct uwb_dev *found = NULL;
+ struct uwb_beca_e *bce;
+
+ mutex_lock(&uwb_beca.mutex);
+ bce = __uwb_beca_find_bydev(devaddr);
+ if (bce)
+ found = uwb_dev_try_get(rc, bce->uwb_dev);
+ mutex_unlock(&uwb_beca.mutex);
+
+ return found;
+}
+
+/**
+ * uwb_dev_get_by_macaddr - get a UWB device with a specific EUI-48
+ * @rc: the radio controller that saw the device
+ * @devaddr: EUI-48 of the UWB device to find
+ */
+struct uwb_dev *uwb_dev_get_by_macaddr(struct uwb_rc *rc,
+ const struct uwb_mac_addr *macaddr)
+{
+ struct uwb_dev *found = NULL;
+ struct uwb_beca_e *bce;
+
+ mutex_lock(&uwb_beca.mutex);
+ bce = __uwb_beca_find_bymac(macaddr);
+ if (bce)
+ found = uwb_dev_try_get(rc, bce->uwb_dev);
+ mutex_unlock(&uwb_beca.mutex);
+
+ return found;
+}
+
+/* Initialize a beacon cache entry */
+static void uwb_beca_e_init(struct uwb_beca_e *bce)
+{
+ mutex_init(&bce->mutex);
+ kref_init(&bce->refcnt);
+ stats_init(&bce->lqe_stats);
+ stats_init(&bce->rssi_stats);
+}
+
+/*
+ * Add a beacon to the cache
+ *
+ * @be: Beacon event information
+ * @bf: Beacon frame (part of b, really)
+ * @ts_jiffies: Timestamp (in jiffies) when the beacon was received
+ */
+struct uwb_beca_e *__uwb_beca_add(struct uwb_rc_evt_beacon *be,
+ struct uwb_beacon_frame *bf,
+ unsigned long ts_jiffies)
+{
+ struct uwb_beca_e *bce;
+
+ bce = kzalloc(sizeof(*bce), GFP_KERNEL);
+ if (bce == NULL)
+ return NULL;
+ uwb_beca_e_init(bce);
+ bce->ts_jiffies = ts_jiffies;
+ bce->uwb_dev = NULL;
+ list_add(&bce->node, &uwb_beca.list);
+ return bce;
+}
+
+/*
+ * Wipe out beacon entries that became stale
+ *
+ * Remove associated devicest too.
+ */
+void uwb_beca_purge(void)
+{
+ struct uwb_beca_e *bce, *next;
+ unsigned long now = jiffies;
+ mutex_lock(&uwb_beca.mutex);
+ list_for_each_entry_safe(bce, next, &uwb_beca.list, node) {
+ if (now - bce->ts_jiffies
+ > msecs_to_jiffies(beacon_timeout_ms)) {
+ uwbd_dev_offair(bce);
+ list_del(&bce->node);
+ uwb_bce_put(bce);
+ }
+ }
+ mutex_unlock(&uwb_beca.mutex);
+}
+
+/* Clean up the whole beacon cache. Called on shutdown */
+void uwb_beca_release(void)
+{
+ struct uwb_beca_e *bce, *next;
+ mutex_lock(&uwb_beca.mutex);
+ list_for_each_entry_safe(bce, next, &uwb_beca.list, node) {
+ list_del(&bce->node);
+ uwb_bce_put(bce);
+ }
+ mutex_unlock(&uwb_beca.mutex);
+}
+
+static void uwb_beacon_print(struct uwb_rc *rc, struct uwb_rc_evt_beacon *be,
+ struct uwb_beacon_frame *bf)
+{
+ char macbuf[UWB_ADDR_STRSIZE];
+ char devbuf[UWB_ADDR_STRSIZE];
+ char dstbuf[UWB_ADDR_STRSIZE];
+
+ uwb_mac_addr_print(macbuf, sizeof(macbuf), &bf->Device_Identifier);
+ uwb_dev_addr_print(devbuf, sizeof(devbuf), &bf->hdr.SrcAddr);
+ uwb_dev_addr_print(dstbuf, sizeof(dstbuf), &bf->hdr.DestAddr);
+ dev_info(&rc->uwb_dev.dev,
+ "BEACON from %s to %s (ch%u offset %u slot %u MAC %s)\n",
+ devbuf, dstbuf, be->bChannelNumber, be->wBPSTOffset,
+ bf->Beacon_Slot_Number, macbuf);
+}
+
+/*
+ * @bce: beacon cache entry, referenced
+ */
+ssize_t uwb_bce_print_IEs(struct uwb_dev *uwb_dev, struct uwb_beca_e *bce,
+ char *buf, size_t size)
+{
+ ssize_t result = 0;
+ struct uwb_rc_evt_beacon *be;
+ struct uwb_beacon_frame *bf;
+ struct uwb_buf_ctx ctx = {
+ .buf = buf,
+ .bytes = 0,
+ .size = size
+ };
+
+ mutex_lock(&bce->mutex);
+ be = bce->be;
+ if (be == NULL)
+ goto out;
+ bf = (void *) be->BeaconInfo;
+ uwb_ie_for_each(uwb_dev, uwb_ie_dump_hex, &ctx,
+ bf->IEData, be->wBeaconInfoLength - sizeof(*bf));
+ result = ctx.bytes;
+out:
+ mutex_unlock(&bce->mutex);
+ return result;
+}
+
+/*
+ * Verify that the beacon event, frame and IEs are ok
+ */
+static int uwb_verify_beacon(struct uwb_rc *rc, struct uwb_event *evt,
+ struct uwb_rc_evt_beacon *be)
+{
+ int result = -EINVAL;
+ struct uwb_beacon_frame *bf;
+ struct device *dev = &rc->uwb_dev.dev;
+
+ /* Is there enough data to decode a beacon frame? */
+ if (evt->size < sizeof(*be) + sizeof(*bf)) {
+ dev_err(dev, "BEACON event: Not enough data to decode "
+ "(%zu vs %zu bytes needed)\n", evt->size,
+ sizeof(*be) + sizeof(*bf));
+ goto error;
+ }
+ /* FIXME: make sure beacon frame IEs are fine and that the whole thing
+ * is consistent */
+ result = 0;
+error:
+ return result;
+}
+
+/*
+ * Handle UWB_RC_EVT_BEACON events
+ *
+ * We check the beacon cache to see how the received beacon fares. If
+ * is there already we refresh the timestamp. If not we create a new
+ * entry.
+ *
+ * According to the WHCI and WUSB specs, only one beacon frame is
+ * allowed per notification block, so we don't bother about scanning
+ * for more.
+ */
+int uwbd_evt_handle_rc_beacon(struct uwb_event *evt)
+{
+ int result = -EINVAL;
+ struct uwb_rc *rc;
+ struct uwb_rc_evt_beacon *be;
+ struct uwb_beacon_frame *bf;
+ struct uwb_beca_e *bce;
+ struct device *dev = &evt->rc->uwb_dev.dev;
+ unsigned long last_ts;
+
+ rc = evt->rc;
+ be = container_of(evt->rceb, struct uwb_rc_evt_beacon, rceb);
+ result = uwb_verify_beacon(rc, evt, be);
+ if (result < 0)
+ return result;
+
+ /* Ignore beacon if it is from an alien. */
+ if (be->bBeaconType == UWB_RC_BEACON_TYPE_OL_ALIEN ||
+ be->bBeaconType == UWB_RC_BEACON_TYPE_NOL_ALIEN) {
+ if (printk_ratelimit())
+ dev_err(dev, "BEACON received from ALIEN. Action? \n");
+ result = -ENOSYS;
+ return 0;
+ }
+ bf = (struct uwb_beacon_frame *) be->BeaconInfo;
+
+ /*
+ * Drop beacons from devices with a NULL EUI-48 -- they cannot
+ * be uniquely identified.
+ *
+ * It's expected that these will all be WUSB devices and they
+ * have a WUSB specific connection method so ignoring them
+ * here shouldn't be a problem.
+ */
+ if (uwb_mac_addr_bcast(&bf->Device_Identifier))
+ return 0;
+
+ mutex_lock(&uwb_beca.mutex);
+ bce = __uwb_beca_find_bymac(&bf->Device_Identifier);
+ if (bce == NULL) {
+ /* Not in there, a new device is pinging */
+ uwb_beacon_print(evt->rc, be, bf);
+ bce = __uwb_beca_add(be, bf, evt->ts_jiffies);
+ if (bce == NULL) {
+ mutex_unlock(&uwb_beca.mutex);
+ return -ENOMEM;
+ }
+ }
+ mutex_unlock(&uwb_beca.mutex);
+
+ mutex_lock(&bce->mutex);
+ /* purge old beacon data */
+ kfree(bce->be);
+
+ last_ts = bce->ts_jiffies;
+
+ /* Update commonly used fields */
+ bce->ts_jiffies = evt->ts_jiffies;
+ bce->be = be;
+ bce->dev_addr = bf->hdr.SrcAddr;
+ bce->mac_addr = &bf->Device_Identifier;
+ be->wBPSTOffset = le16_to_cpu(be->wBPSTOffset);
+ be->wBeaconInfoLength = le16_to_cpu(be->wBeaconInfoLength);
+ stats_add_sample(&bce->lqe_stats, be->bLQI - 7);
+ stats_add_sample(&bce->rssi_stats, be->bRSSI + 18);
+
+ /*
+ * Wait until at least mMaxLostBeacons beacons have been
+ * received before considering a new device as present.
+ */
+ bce->hits++;
+ if (bce->uwb_dev == NULL && bce->hits > UWB_MAX_LOST_BEACONS) {
+ if (time_before(bce->ts_jiffies,
+ last_ts + msecs_to_jiffies(beacon_timeout_ms)))
+ uwbd_dev_onair(evt->rc, bce);
+ else
+ bce->hits = 0;
+ }
+ mutex_unlock(&bce->mutex);
+
+ return 1; /* we keep the event data */
+}
+
+/*
+ * Handle UWB_RC_EVT_BEACON_SIZE events
+ *
+ * XXXXX
+ */
+int uwbd_evt_handle_rc_beacon_size(struct uwb_event *evt)
+{
+ int result = -EINVAL;
+ struct device *dev = &evt->rc->uwb_dev.dev;
+ struct uwb_rc_evt_beacon_size *bs;
+
+ /* Is there enough data to decode the event? */
+ if (evt->size < sizeof(*bs)) {
+ dev_err(dev, "BEACON SIZE notification: Not enough data to "
+ "decode (%zu vs %zu bytes needed)\n",
+ evt->size, sizeof(*bs));
+ goto error;
+ }
+ bs = container_of(evt->rceb, struct uwb_rc_evt_beacon_size, rceb);
+ if (0)
+ dev_info(dev, "Beacon size changed to %u bytes "
+ "(FIXME: action?)\n", le16_to_cpu(bs->wNewBeaconSize));
+ else {
+ /* temporary hack until we do something with this message... */
+ static unsigned count;
+ if (++count % 1000 == 0)
+ dev_info(dev, "Beacon size changed %u times "
+ "(FIXME: action?)\n", count);
+ }
+ result = 0;
+error:
+ return result;
+}
+
+/*
+ * Handle UWB_RC_EVT_BP_SLOT_CHANGE events
+ *
+ * XXXXX
+ */
+int uwbd_evt_handle_rc_bp_slot_change(struct uwb_event *evt)
+{
+ int result = -EINVAL;
+ struct device *dev = &evt->rc->uwb_dev.dev;
+ struct uwb_rc_evt_bp_slot_change *bpsc;
+
+ /* Is there enough data to decode a beacon frame? */
+ if (evt->size < sizeof(*bpsc)) {
+ printk(KERN_ERR "BP SLOT CHANGE event: Not enough data\n");
+ goto error;
+ }
+ bpsc = container_of(evt->rceb, struct uwb_rc_evt_bp_slot_change, rceb);
+ dev_info(dev, "BP slot changed to #%u (FIXME: action?)\n",
+ bpsc->bSlotNumber);
+ result = 0;
+error:
+ return result;
+}
+
+/**
+ * Handle UWB_RC_EVT_BP_SLOT_CHANGE events
+ *
+ * XXXXX
+ */
+struct uwb_ie_bpo {
+ struct uwb_ie_hdr hdr;
+ u8 bp_length;
+ u8 data[];
+} __attribute__((packed));
+
+int uwbd_evt_handle_rc_bpoie_change(struct uwb_event *evt)
+{
+ int result = -EINVAL;
+ struct device *dev = &evt->rc->uwb_dev.dev;
+ struct uwb_rc_evt_bpoie_change *bpoiec;
+ struct uwb_ie_bpo *bpoie;
+ static unsigned count; /* FIXME: this is a temp hack */
+ size_t iesize;
+
+ /* Is there enough data to decode it? */
+ if (evt->size < sizeof(*bpoiec)) {
+ dev_err(dev, "BPOIEC notification: Not enough data to "
+ "decode (%zu vs %zu bytes needed)\n",
+ evt->size, sizeof(*bpoiec));
+ goto error;
+ }
+ bpoiec = container_of(evt->rceb, struct uwb_rc_evt_bpoie_change, rceb);
+ iesize = le16_to_cpu(bpoiec->wBPOIELength);
+ if (iesize < sizeof(*bpoie)) {
+ dev_err(dev, "BPOIEC notification: Not enough IE data to "
+ "decode (%zu vs %zu bytes needed)\n",
+ iesize, sizeof(*bpoie));
+ goto error;
+ }
+ if (++count % 1000 == 0) /* Lame placeholder */
+ dev_info(dev, "BPOIE: %u changes received\n", count);
+ /*
+ * FIXME: At this point we should go over all the IEs in the
+ * bpoiec->BPOIE array and act on each.
+ */
+ result = 0;
+error:
+ return result;
+}
+
+/**
+ * uwb_bg_joined - is the RC in a beacon group?
+ * @rc: the radio controller
+ *
+ * Returns true if the radio controller is in a beacon group (even if
+ * it's the sole member).
+ */
+int uwb_bg_joined(struct uwb_rc *rc)
+{
+ return rc->beaconing != -1;
+}
+EXPORT_SYMBOL_GPL(uwb_bg_joined);
+
+/*
+ * Print beaconing state.
+ */
+static ssize_t uwb_rc_beacon_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_rc *rc = uwb_dev->rc;
+ ssize_t result;
+
+ mutex_lock(&rc->uwb_dev.mutex);
+ result = sprintf(buf,
+ "state: %s\n"
+ "channel: %d\n"
+ "\n"
+ "# CHANNEL [BPST-OFFSET]\n"
+ "# start beaconing on CHANNEL "
+ "(or stop if CHANNEL is -1)\n"
+ "# AA:BB:CC:DD:EE:FF\n"
+ "# XX:YY\n"
+ "# try joining the beacon group containing a "
+ "specific device\n"
+ "# (EUI-48 or DevAddr)\n",
+ rc->beaconing == -1? "disabled" : "enabled",
+ rc->beaconing);
+ mutex_unlock(&rc->uwb_dev.mutex);
+ return result;
+}
+
+/*
+ * Start beaconing on the specified channel, or stop beaconing.
+ *
+ * The BPST offset of when to start searching for a beacon group to
+ * join may be specified.
+ */
+static ssize_t uwb_rc_beacon_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_rc *rc = uwb_dev->rc;
+ struct uwb_dev_addr devaddr;
+ struct uwb_mac_addr macaddr;
+ unsigned bpst_offset = 0;
+ ssize_t result;
+ int channel;
+
+ result = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx\n",
+ &macaddr.data[0], &macaddr.data[1], &macaddr.data[2],
+ &macaddr.data[3], &macaddr.data[4], &macaddr.data[5]);
+ if (result == 6) {
+ /* given a device, extract params */
+ struct uwb_beca_e *beca;
+ result = -ENODEV;
+ mutex_lock(&uwb_beca.mutex);
+ beca = __uwb_beca_find_bymac(&macaddr);
+ if (beca != NULL) {
+ mutex_lock(&beca->mutex);
+ channel = beca->be->bChannelNumber;
+ bpst_offset = beca->be->wBPSTOffset;
+ mutex_unlock(&beca->mutex);
+ result = 0;
+ }
+ mutex_unlock(&uwb_beca.mutex);
+ if (result < 0)
+ goto error;
+ if (rc->scan_type != UWB_SCAN_DISABLED)
+ uwb_rc_scan(rc, channel, UWB_SCAN_DISABLED, 0);
+ goto out_beacon;
+ }
+ result = sscanf(buf, "%hhx:%hhx\n", &devaddr.data[1], &devaddr.data[0]);
+ if (result == 2) {
+ /* given a device, extract params */
+ struct uwb_beca_e *beca;
+ result = -ENODEV;
+ mutex_lock(&uwb_beca.mutex);
+ beca = __uwb_beca_find_bydev(&devaddr);
+ if (beca != NULL) {
+ mutex_lock(&beca->mutex);
+ channel = beca->be->bChannelNumber;
+ bpst_offset = beca->be->wBPSTOffset;
+ mutex_unlock(&beca->mutex);
+ result = 0;
+ }
+ mutex_unlock(&uwb_beca.mutex);
+ if (result < 0)
+ goto error;
+ if (rc->scan_type != UWB_SCAN_DISABLED)
+ uwb_rc_scan(rc, channel, UWB_SCAN_DISABLED, 0);
+ goto out_beacon;
+ }
+ result = sscanf(buf, "%d %u\n", &channel, &bpst_offset);
+ if (result >= 1)
+ goto out_beacon;
+ result = -EINVAL;
+ goto error;
+
+out_beacon:
+ result = uwb_rc_beacon(rc, channel, bpst_offset);
+error:
+ return result < 0 ? result : size;
+}
+DEVICE_ATTR(beacon, S_IRUGO | S_IWUSR, uwb_rc_beacon_show, uwb_rc_beacon_store);
diff --git a/drivers/uwb/driver.c b/drivers/uwb/driver.c
new file mode 100644
index 000000000000..7eee8e408308
--- /dev/null
+++ b/drivers/uwb/driver.c
@@ -0,0 +1,142 @@
+/*
+ * Ultra Wide Band
+ * Driver initialization, etc
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ *
+ * Life cycle: FIXME: explain
+ *
+ * UWB radio controller:
+ *
+ * 1. alloc a uwb_rc, zero it
+ * 2. call uwb_rc_init() on it to set it up + ops (won't do any
+ * kind of allocation)
+ * 3. register (now it is owned by the UWB stack--deregister before
+ * freeing/destroying).
+ * 4. It lives on it's own now (UWB stack handles)--when it
+ * disconnects, call unregister()
+ * 5. free it.
+ *
+ * Make sure you have a reference to the uwb_rc before calling
+ * any of the UWB API functions.
+ *
+ * TODO:
+ *
+ * 1. Locking and life cycle management is crappy still. All entry
+ * points to the UWB HCD API assume you have a reference on the
+ * uwb_rc structure and that it won't go away. They mutex lock it
+ * before doing anything.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kdev_t.h>
+#include <linux/random.h>
+#include <linux/uwb/debug.h>
+#include "uwb-internal.h"
+
+
+/* UWB stack attributes (or 'global' constants) */
+
+
+/**
+ * If a beacon dissapears for longer than this, then we consider the
+ * device who was represented by that beacon to be gone.
+ *
+ * ECMA-368[17.2.3, last para] establishes that a device must not
+ * consider a device to be its neighbour if he doesn't receive a beacon
+ * for more than mMaxLostBeacons. mMaxLostBeacons is defined in
+ * ECMA-368[17.16] as 3; because we can get only one beacon per
+ * superframe, that'd be 3 * 65ms = 195 ~ 200 ms. Let's give it time
+ * for jitter and stuff and make it 500 ms.
+ */
+unsigned long beacon_timeout_ms = 500;
+
+static
+ssize_t beacon_timeout_ms_show(struct class *class, char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%lu\n", beacon_timeout_ms);
+}
+
+static
+ssize_t beacon_timeout_ms_store(struct class *class,
+ const char *buf, size_t size)
+{
+ unsigned long bt;
+ ssize_t result;
+ result = sscanf(buf, "%lu", &bt);
+ if (result != 1)
+ return -EINVAL;
+ beacon_timeout_ms = bt;
+ return size;
+}
+
+static struct class_attribute uwb_class_attrs[] = {
+ __ATTR(beacon_timeout_ms, S_IWUSR | S_IRUGO,
+ beacon_timeout_ms_show, beacon_timeout_ms_store),
+ __ATTR_NULL,
+};
+
+/** Device model classes */
+struct class uwb_rc_class = {
+ .name = "uwb_rc",
+ .class_attrs = uwb_class_attrs,
+};
+
+
+static int __init uwb_subsys_init(void)
+{
+ int result = 0;
+
+ result = uwb_est_create();
+ if (result < 0) {
+ printk(KERN_ERR "uwb: Can't initialize EST subsystem\n");
+ goto error_est_init;
+ }
+
+ result = class_register(&uwb_rc_class);
+ if (result < 0)
+ goto error_uwb_rc_class_register;
+ uwbd_start();
+ return 0;
+
+error_uwb_rc_class_register:
+ uwb_est_destroy();
+error_est_init:
+ return result;
+}
+module_init(uwb_subsys_init);
+
+static void __exit uwb_subsys_exit(void)
+{
+ uwbd_stop();
+ class_unregister(&uwb_rc_class);
+ uwb_est_destroy();
+ return;
+}
+module_exit(uwb_subsys_exit);
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("Ultra Wide Band core");
+MODULE_LICENSE("GPL");
diff --git a/drivers/uwb/drp-avail.c b/drivers/uwb/drp-avail.c
new file mode 100644
index 000000000000..4cc781e09fc8
--- /dev/null
+++ b/drivers/uwb/drp-avail.c
@@ -0,0 +1,288 @@
+/*
+ * Ultra Wide Band
+ * DRP availability management
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Reinette Chatre <reinette.chatre@intel.com>
+ * Copyright (C) 2008 Cambridge Silicon Radio 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * Manage DRP Availability (the MAS available for DRP
+ * reservations). Thus:
+ *
+ * - Handle DRP Availability Change notifications
+ *
+ * - Allow the reservation manager to indicate MAS reserved/released
+ * by local (owned by/targeted at the radio controller)
+ * reservations.
+ *
+ * - Based on the two sources above, generate a DRP Availability IE to
+ * be included in the beacon.
+ *
+ * See also the documentation for struct uwb_drp_avail.
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/bitmap.h>
+#include "uwb-internal.h"
+
+/**
+ * uwb_drp_avail_init - initialize an RC's MAS availability
+ *
+ * All MAS are available initially. The RC will inform use which
+ * slots are used for the BP (it may change in size).
+ */
+void uwb_drp_avail_init(struct uwb_rc *rc)
+{
+ bitmap_fill(rc->drp_avail.global, UWB_NUM_MAS);
+ bitmap_fill(rc->drp_avail.local, UWB_NUM_MAS);
+ bitmap_fill(rc->drp_avail.pending, UWB_NUM_MAS);
+}
+
+/*
+ * Determine MAS available for new local reservations.
+ *
+ * avail = global & local & pending
+ */
+static void uwb_drp_available(struct uwb_rc *rc, struct uwb_mas_bm *avail)
+{
+ bitmap_and(avail->bm, rc->drp_avail.global, rc->drp_avail.local, UWB_NUM_MAS);
+ bitmap_and(avail->bm, avail->bm, rc->drp_avail.pending, UWB_NUM_MAS);
+}
+
+/**
+ * uwb_drp_avail_reserve_pending - reserve MAS for a new reservation
+ * @rc: the radio controller
+ * @mas: the MAS to reserve
+ *
+ * Returns 0 on success, or -EBUSY if the MAS requested aren't available.
+ */
+int uwb_drp_avail_reserve_pending(struct uwb_rc *rc, struct uwb_mas_bm *mas)
+{
+ struct uwb_mas_bm avail;
+
+ uwb_drp_available(rc, &avail);
+ if (!bitmap_subset(mas->bm, avail.bm, UWB_NUM_MAS))
+ return -EBUSY;
+
+ bitmap_andnot(rc->drp_avail.pending, rc->drp_avail.pending, mas->bm, UWB_NUM_MAS);
+ return 0;
+}
+
+/**
+ * uwb_drp_avail_reserve - reserve MAS for an established reservation
+ * @rc: the radio controller
+ * @mas: the MAS to reserve
+ */
+void uwb_drp_avail_reserve(struct uwb_rc *rc, struct uwb_mas_bm *mas)
+{
+ bitmap_or(rc->drp_avail.pending, rc->drp_avail.pending, mas->bm, UWB_NUM_MAS);
+ bitmap_andnot(rc->drp_avail.local, rc->drp_avail.local, mas->bm, UWB_NUM_MAS);
+ rc->drp_avail.ie_valid = false;
+}
+
+/**
+ * uwb_drp_avail_release - release MAS from a pending or established reservation
+ * @rc: the radio controller
+ * @mas: the MAS to release
+ */
+void uwb_drp_avail_release(struct uwb_rc *rc, struct uwb_mas_bm *mas)
+{
+ bitmap_or(rc->drp_avail.local, rc->drp_avail.local, mas->bm, UWB_NUM_MAS);
+ bitmap_or(rc->drp_avail.pending, rc->drp_avail.pending, mas->bm, UWB_NUM_MAS);
+ rc->drp_avail.ie_valid = false;
+}
+
+/**
+ * uwb_drp_avail_ie_update - update the DRP Availability IE
+ * @rc: the radio controller
+ *
+ * avail = global & local
+ */
+void uwb_drp_avail_ie_update(struct uwb_rc *rc)
+{
+ struct uwb_mas_bm avail;
+
+ bitmap_and(avail.bm, rc->drp_avail.global, rc->drp_avail.local, UWB_NUM_MAS);
+
+ rc->drp_avail.ie.hdr.element_id = UWB_IE_DRP_AVAILABILITY;
+ rc->drp_avail.ie.hdr.length = UWB_NUM_MAS / 8;
+ uwb_mas_bm_copy_le(rc->drp_avail.ie.bmp, &avail);
+ rc->drp_avail.ie_valid = true;
+}
+
+/**
+ * Create an unsigned long from a buffer containing a byte stream.
+ *
+ * @array: pointer to buffer
+ * @itr: index of buffer from where we start
+ * @len: the buffer's remaining size may not be exact multiple of
+ * sizeof(unsigned long), @len is the length of buffer that needs
+ * to be converted. This will be sizeof(unsigned long) or smaller
+ * (BUG if not). If it is smaller then we will pad the remaining
+ * space of the result with zeroes.
+ */
+static
+unsigned long get_val(u8 *array, size_t itr, size_t len)
+{
+ unsigned long val = 0;
+ size_t top = itr + len;
+
+ BUG_ON(len > sizeof(val));
+
+ while (itr < top) {
+ val <<= 8;
+ val |= array[top - 1];
+ top--;
+ }
+ val <<= 8 * (sizeof(val) - len); /* padding */
+ return val;
+}
+
+/**
+ * Initialize bitmap from data buffer.
+ *
+ * The bitmap to be converted could come from a IE, for example a
+ * DRP Availability IE.
+ * From ECMA-368 1.0 [16.8.7]: "
+ * octets: 1 1 N * (0 to 32)
+ * Element ID Length (=N) DRP Availability Bitmap
+ *
+ * The DRP Availability Bitmap field is up to 256 bits long, one
+ * bit for each MAS in the superframe, where the least-significant
+ * bit of the field corresponds to the first MAS in the superframe
+ * and successive bits correspond to successive MASs."
+ *
+ * The DRP Availability bitmap is in octets from 0 to 32, so octet
+ * 32 contains bits for MAS 1-8, etc. If the bitmap is smaller than 32
+ * octets, the bits in octets not included at the end of the bitmap are
+ * treated as zero. In this case (when the bitmap is smaller than 32
+ * octets) the MAS represented range from MAS 1 to MAS (size of bitmap)
+ * with the last octet still containing bits for MAS 1-8, etc.
+ *
+ * For example:
+ * F00F0102 03040506 0708090A 0B0C0D0E 0F010203
+ * ^^^^
+ * ||||
+ * ||||
+ * |||\LSB of byte is MAS 9
+ * ||\MSB of byte is MAS 16
+ * |\LSB of first byte is MAS 1
+ * \ MSB of byte is MAS 8
+ *
+ * An example of this encoding can be found in ECMA-368 Annex-D [Table D.11]
+ *
+ * The resulting bitmap will have the following mapping:
+ * bit position 0 == MAS 1
+ * bit position 1 == MAS 2
+ * ...
+ * bit position (UWB_NUM_MAS - 1) == MAS UWB_NUM_MAS
+ *
+ * @bmp_itr: pointer to bitmap (can be declared with DECLARE_BITMAP)
+ * @buffer: pointer to buffer containing bitmap data in big endian
+ * format (MSB first)
+ * @buffer_size:number of bytes with which bitmap should be initialized
+ */
+static
+void buffer_to_bmp(unsigned long *bmp_itr, void *_buffer,
+ size_t buffer_size)
+{
+ u8 *buffer = _buffer;
+ size_t itr, len;
+ unsigned long val;
+
+ itr = 0;
+ while (itr < buffer_size) {
+ len = buffer_size - itr >= sizeof(val) ?
+ sizeof(val) : buffer_size - itr;
+ val = get_val(buffer, itr, len);
+ bmp_itr[itr / sizeof(val)] = val;
+ itr += sizeof(val);
+ }
+}
+
+
+/**
+ * Extract DRP Availability bitmap from the notification.
+ *
+ * The notification that comes in contains a bitmap of (UWB_NUM_MAS / 8) bytes
+ * We convert that to our internal representation.
+ */
+static
+int uwbd_evt_get_drp_avail(struct uwb_event *evt, unsigned long *bmp)
+{
+ struct device *dev = &evt->rc->uwb_dev.dev;
+ struct uwb_rc_evt_drp_avail *drp_evt;
+ int result = -EINVAL;
+
+ /* Is there enough data to decode the event? */
+ if (evt->size < sizeof(*drp_evt)) {
+ dev_err(dev, "DRP Availability Change: Not enough "
+ "data to decode event [%zu bytes, %zu "
+ "needed]\n", evt->size, sizeof(*drp_evt));
+ goto error;
+ }
+ drp_evt = container_of(evt->rceb, struct uwb_rc_evt_drp_avail, rceb);
+ buffer_to_bmp(bmp, drp_evt->bmp, UWB_NUM_MAS/8);
+ result = 0;
+error:
+ return result;
+}
+
+
+/**
+ * Process an incoming DRP Availability notification.
+ *
+ * @evt: Event information (packs the actual event data, which
+ * radio controller it came to, etc).
+ *
+ * @returns: 0 on success (so uwbd() frees the event buffer), < 0
+ * on error.
+ *
+ * According to ECMA-368 1.0 [16.8.7], bits set to ONE indicate that
+ * the MAS slot is available, bits set to ZERO indicate that the slot
+ * is busy.
+ *
+ * So we clear available slots, we set used slots :)
+ *
+ * The notification only marks non-availability based on the BP and
+ * received DRP IEs that are not for this radio controller. A copy of
+ * this bitmap is needed to generate the real availability (which
+ * includes local and pending reservations).
+ *
+ * The DRP Availability IE that this radio controller emits will need
+ * to be updated.
+ */
+int uwbd_evt_handle_rc_drp_avail(struct uwb_event *evt)
+{
+ int result;
+ struct uwb_rc *rc = evt->rc;
+ DECLARE_BITMAP(bmp, UWB_NUM_MAS);
+
+ result = uwbd_evt_get_drp_avail(evt, bmp);
+ if (result < 0)
+ return result;
+
+ mutex_lock(&rc->rsvs_mutex);
+ bitmap_copy(rc->drp_avail.global, bmp, UWB_NUM_MAS);
+ rc->drp_avail.ie_valid = false;
+ mutex_unlock(&rc->rsvs_mutex);
+
+ uwb_rsv_sched_update(rc);
+
+ return 0;
+}
diff --git a/drivers/uwb/drp-ie.c b/drivers/uwb/drp-ie.c
new file mode 100644
index 000000000000..ed51e28a87f6
--- /dev/null
+++ b/drivers/uwb/drp-ie.c
@@ -0,0 +1,233 @@
+/*
+ * UWB DRP IE management.
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Copyright (C) 2008 Cambridge Silicon Radio 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/random.h>
+#include <linux/uwb.h>
+
+#include "uwb-internal.h"
+
+/*
+ * Allocate a DRP IE.
+ *
+ * To save having to free/allocate a DRP IE when its MAS changes,
+ * enough memory is allocated for the maxiumum number of DRP
+ * allocation fields. This gives an overhead per reservation of up to
+ * (UWB_NUM_ZONES - 1) * 4 = 60 octets.
+ */
+static struct uwb_ie_drp *uwb_drp_ie_alloc(void)
+{
+ struct uwb_ie_drp *drp_ie;
+ unsigned tiebreaker;
+
+ drp_ie = kzalloc(sizeof(struct uwb_ie_drp) +
+ UWB_NUM_ZONES * sizeof(struct uwb_drp_alloc),
+ GFP_KERNEL);
+ if (drp_ie) {
+ drp_ie->hdr.element_id = UWB_IE_DRP;
+
+ get_random_bytes(&tiebreaker, sizeof(unsigned));
+ drp_ie->tiebreaker = tiebreaker & 1;
+ }
+ return drp_ie;
+}
+
+
+/*
+ * Fill a DRP IE's allocation fields from a MAS bitmap.
+ */
+static void uwb_drp_ie_from_bm(struct uwb_ie_drp *drp_ie,
+ struct uwb_mas_bm *mas)
+{
+ int z, i, num_fields = 0, next = 0;
+ struct uwb_drp_alloc *zones;
+ __le16 current_bmp;
+ DECLARE_BITMAP(tmp_bmp, UWB_NUM_MAS);
+ DECLARE_BITMAP(tmp_mas_bm, UWB_MAS_PER_ZONE);
+
+ zones = drp_ie->allocs;
+
+ bitmap_copy(tmp_bmp, mas->bm, UWB_NUM_MAS);
+
+ /* Determine unique MAS bitmaps in zones from bitmap. */
+ for (z = 0; z < UWB_NUM_ZONES; z++) {
+ bitmap_copy(tmp_mas_bm, tmp_bmp, UWB_MAS_PER_ZONE);
+ if (bitmap_weight(tmp_mas_bm, UWB_MAS_PER_ZONE) > 0) {
+ bool found = false;
+ current_bmp = (__le16) *tmp_mas_bm;
+ for (i = 0; i < next; i++) {
+ if (current_bmp == zones[i].mas_bm) {
+ zones[i].zone_bm |= 1 << z;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ num_fields++;
+ zones[next].zone_bm = 1 << z;
+ zones[next].mas_bm = current_bmp;
+ next++;
+ }
+ }
+ bitmap_shift_right(tmp_bmp, tmp_bmp, UWB_MAS_PER_ZONE, UWB_NUM_MAS);
+ }
+
+ /* Store in format ready for transmission (le16). */
+ for (i = 0; i < num_fields; i++) {
+ drp_ie->allocs[i].zone_bm = cpu_to_le16(zones[i].zone_bm);
+ drp_ie->allocs[i].mas_bm = cpu_to_le16(zones[i].mas_bm);
+ }
+
+ drp_ie->hdr.length = sizeof(struct uwb_ie_drp) - sizeof(struct uwb_ie_hdr)
+ + num_fields * sizeof(struct uwb_drp_alloc);
+}
+
+/**
+ * uwb_drp_ie_update - update a reservation's DRP IE
+ * @rsv: the reservation
+ */
+int uwb_drp_ie_update(struct uwb_rsv *rsv)
+{
+ struct device *dev = &rsv->rc->uwb_dev.dev;
+ struct uwb_ie_drp *drp_ie;
+ int reason_code, status;
+
+ switch (rsv->state) {
+ case UWB_RSV_STATE_NONE:
+ kfree(rsv->drp_ie);
+ rsv->drp_ie = NULL;
+ return 0;
+ case UWB_RSV_STATE_O_INITIATED:
+ reason_code = UWB_DRP_REASON_ACCEPTED;
+ status = 0;
+ break;
+ case UWB_RSV_STATE_O_PENDING:
+ reason_code = UWB_DRP_REASON_ACCEPTED;
+ status = 0;
+ break;
+ case UWB_RSV_STATE_O_MODIFIED:
+ reason_code = UWB_DRP_REASON_MODIFIED;
+ status = 1;
+ break;
+ case UWB_RSV_STATE_O_ESTABLISHED:
+ reason_code = UWB_DRP_REASON_ACCEPTED;
+ status = 1;
+ break;
+ case UWB_RSV_STATE_T_ACCEPTED:
+ reason_code = UWB_DRP_REASON_ACCEPTED;
+ status = 1;
+ break;
+ case UWB_RSV_STATE_T_DENIED:
+ reason_code = UWB_DRP_REASON_DENIED;
+ status = 0;
+ break;
+ default:
+ dev_dbg(dev, "rsv with unhandled state (%d)\n", rsv->state);
+ return -EINVAL;
+ }
+
+ if (rsv->drp_ie == NULL) {
+ rsv->drp_ie = uwb_drp_ie_alloc();
+ if (rsv->drp_ie == NULL)
+ return -ENOMEM;
+ }
+ drp_ie = rsv->drp_ie;
+
+ drp_ie->owner = uwb_rsv_is_owner(rsv);
+ drp_ie->status = status;
+ drp_ie->reason_code = reason_code;
+ drp_ie->stream_index = rsv->stream;
+ drp_ie->type = rsv->type;
+ drp_ie->DRP_Control = cpu_to_le16(drp_ie->DRP_Control);
+
+ if (uwb_rsv_is_owner(rsv)) {
+ switch (rsv->target.type) {
+ case UWB_RSV_TARGET_DEV:
+ drp_ie->dev_addr = rsv->target.dev->dev_addr;
+ break;
+ case UWB_RSV_TARGET_DEVADDR:
+ drp_ie->dev_addr = rsv->target.devaddr;
+ break;
+ }
+ } else
+ drp_ie->dev_addr = rsv->owner->dev_addr;
+
+ uwb_drp_ie_from_bm(drp_ie, &rsv->mas);
+
+ rsv->ie_valid = true;
+ return 0;
+}
+
+/*
+ * Set MAS bits from given MAS bitmap in a single zone of large bitmap.
+ *
+ * We are given a zone id and the MAS bitmap of bits that need to be set in
+ * this zone. Note that this zone may already have bits set and this only
+ * adds settings - we cannot simply assign the MAS bitmap contents to the
+ * zone contents. We iterate over the the bits (MAS) in the zone and set the
+ * bits that are set in the given MAS bitmap.
+ */
+static
+void uwb_drp_ie_single_zone_to_bm(struct uwb_mas_bm *bm, u8 zone, u16 mas_bm)
+{
+ int mas;
+ u16 mas_mask;
+
+ for (mas = 0; mas < UWB_MAS_PER_ZONE; mas++) {
+ mas_mask = 1 << mas;
+ if (mas_bm & mas_mask)
+ set_bit(zone * UWB_NUM_ZONES + mas, bm->bm);
+ }
+}
+
+/**
+ * uwb_drp_ie_zones_to_bm - convert DRP allocation fields to a bitmap
+ * @mas: MAS bitmap that will be populated to correspond to the
+ * allocation fields in the DRP IE
+ * @drp_ie: the DRP IE that contains the allocation fields.
+ *
+ * The input format is an array of MAS allocation fields (16 bit Zone
+ * bitmap, 16 bit MAS bitmap) as described in [ECMA-368] section
+ * 16.8.6. The output is a full 256 bit MAS bitmap.
+ *
+ * We go over all the allocation fields, for each allocation field we
+ * know which zones are impacted. We iterate over all the zones
+ * impacted and call a function that will set the correct MAS bits in
+ * each zone.
+ */
+void uwb_drp_ie_to_bm(struct uwb_mas_bm *bm, const struct uwb_ie_drp *drp_ie)
+{
+ int numallocs = (drp_ie->hdr.length - 4) / 4;
+ const struct uwb_drp_alloc *alloc;
+ int cnt;
+ u16 zone_bm, mas_bm;
+ u8 zone;
+ u16 zone_mask;
+
+ for (cnt = 0; cnt < numallocs; cnt++) {
+ alloc = &drp_ie->allocs[cnt];
+ zone_bm = le16_to_cpu(alloc->zone_bm);
+ mas_bm = le16_to_cpu(alloc->mas_bm);
+ for (zone = 0; zone < UWB_NUM_ZONES; zone++) {
+ zone_mask = 1 << zone;
+ if (zone_bm & zone_mask)
+ uwb_drp_ie_single_zone_to_bm(bm, zone, mas_bm);
+ }
+ }
+}
diff --git a/drivers/uwb/drp.c b/drivers/uwb/drp.c
new file mode 100644
index 000000000000..b0dcef578b32
--- /dev/null
+++ b/drivers/uwb/drp.c
@@ -0,0 +1,451 @@
+/*
+ * Ultra Wide Band
+ * Dynamic Reservation Protocol handling
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ * Copyright (C) 2008 Cambridge Silicon Radio 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/delay.h>
+#include "uwb-internal.h"
+
+/**
+ * Construct and send the SET DRP IE
+ *
+ * @rc: UWB Host controller
+ * @returns: >= 0 number of bytes still available in the beacon
+ * < 0 errno code on error.
+ *
+ * See WUSB[8.6.2.7]: The host must set all the DRP IEs that it wants the
+ * device to include in its beacon at the same time. We thus have to
+ * traverse all reservations and include the DRP IEs of all PENDING
+ * and NEGOTIATED reservations in a SET DRP command for transmission.
+ *
+ * A DRP Availability IE is appended.
+ *
+ * rc->uwb_dev.mutex is held
+ *
+ * FIXME We currently ignore the returned value indicating the remaining space
+ * in beacon. This could be used to deny reservation requests earlier if
+ * determined that they would cause the beacon space to be exceeded.
+ */
+static
+int uwb_rc_gen_send_drp_ie(struct uwb_rc *rc)
+{
+ int result;
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_rc_cmd_set_drp_ie *cmd;
+ struct uwb_rc_evt_set_drp_ie reply;
+ struct uwb_rsv *rsv;
+ int num_bytes = 0;
+ u8 *IEDataptr;
+
+ result = -ENOMEM;
+ /* First traverse all reservations to determine memory needed. */
+ list_for_each_entry(rsv, &rc->reservations, rc_node) {
+ if (rsv->drp_ie != NULL)
+ num_bytes += rsv->drp_ie->hdr.length + 2;
+ }
+ num_bytes += sizeof(rc->drp_avail.ie);
+ cmd = kzalloc(sizeof(*cmd) + num_bytes, GFP_KERNEL);
+ if (cmd == NULL)
+ goto error;
+ cmd->rccb.bCommandType = UWB_RC_CET_GENERAL;
+ cmd->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_SET_DRP_IE);
+ cmd->wIELength = num_bytes;
+ IEDataptr = (u8 *)&cmd->IEData[0];
+
+ /* Next traverse all reservations to place IEs in allocated memory. */
+ list_for_each_entry(rsv, &rc->reservations, rc_node) {
+ if (rsv->drp_ie != NULL) {
+ memcpy(IEDataptr, rsv->drp_ie,
+ rsv->drp_ie->hdr.length + 2);
+ IEDataptr += rsv->drp_ie->hdr.length + 2;
+ }
+ }
+ memcpy(IEDataptr, &rc->drp_avail.ie, sizeof(rc->drp_avail.ie));
+
+ reply.rceb.bEventType = UWB_RC_CET_GENERAL;
+ reply.rceb.wEvent = UWB_RC_CMD_SET_DRP_IE;
+ result = uwb_rc_cmd(rc, "SET-DRP-IE", &cmd->rccb,
+ sizeof(*cmd) + num_bytes, &reply.rceb,
+ sizeof(reply));
+ if (result < 0)
+ goto error_cmd;
+ result = le16_to_cpu(reply.wRemainingSpace);
+ if (reply.bResultCode != UWB_RC_RES_SUCCESS) {
+ dev_err(&rc->uwb_dev.dev, "SET-DRP-IE: command execution "
+ "failed: %s (%d). RemainingSpace in beacon "
+ "= %d\n", uwb_rc_strerror(reply.bResultCode),
+ reply.bResultCode, result);
+ result = -EIO;
+ } else {
+ dev_dbg(dev, "SET-DRP-IE sent. RemainingSpace in beacon "
+ "= %d.\n", result);
+ result = 0;
+ }
+error_cmd:
+ kfree(cmd);
+error:
+ return result;
+
+}
+/**
+ * Send all DRP IEs associated with this host
+ *
+ * @returns: >= 0 number of bytes still available in the beacon
+ * < 0 errno code on error.
+ *
+ * As per the protocol we obtain the host controller device lock to access
+ * bandwidth structures.
+ */
+int uwb_rc_send_all_drp_ie(struct uwb_rc *rc)
+{
+ int result;
+
+ mutex_lock(&rc->uwb_dev.mutex);
+ result = uwb_rc_gen_send_drp_ie(rc);
+ mutex_unlock(&rc->uwb_dev.mutex);
+ return result;
+}
+
+void uwb_drp_handle_timeout(struct uwb_rsv *rsv)
+{
+ struct device *dev = &rsv->rc->uwb_dev.dev;
+
+ dev_dbg(dev, "reservation timeout in state %s (%d)\n",
+ uwb_rsv_state_str(rsv->state), rsv->state);
+
+ switch (rsv->state) {
+ case UWB_RSV_STATE_O_INITIATED:
+ if (rsv->is_multicast) {
+ uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED);
+ return;
+ }
+ break;
+ case UWB_RSV_STATE_O_ESTABLISHED:
+ if (rsv->is_multicast)
+ return;
+ break;
+ default:
+ break;
+ }
+ uwb_rsv_remove(rsv);
+}
+
+/*
+ * Based on the DRP IE, transition a target reservation to a new
+ * state.
+ */
+static void uwb_drp_process_target(struct uwb_rc *rc, struct uwb_rsv *rsv,
+ struct uwb_ie_drp *drp_ie)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+
+ if (drp_ie->status) {
+ switch (drp_ie->reason_code) {
+ case UWB_DRP_REASON_ACCEPTED:
+ uwb_rsv_set_state(rsv, UWB_RSV_STATE_T_ACCEPTED);
+ break;
+ case UWB_DRP_REASON_MODIFIED:
+ dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n",
+ drp_ie->reason_code, drp_ie->status);
+ break;
+ default:
+ dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n",
+ drp_ie->reason_code, drp_ie->status);
+ }
+ } else {
+ switch (drp_ie->reason_code) {
+ case UWB_DRP_REASON_ACCEPTED:
+ /* New reservations are handled in uwb_rsv_find(). */
+ break;
+ case UWB_DRP_REASON_DENIED:
+ uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE);
+ break;
+ case UWB_DRP_REASON_CONFLICT:
+ case UWB_DRP_REASON_MODIFIED:
+ dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n",
+ drp_ie->reason_code, drp_ie->status);
+ break;
+ default:
+ dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n",
+ drp_ie->reason_code, drp_ie->status);
+ }
+ }
+}
+
+/*
+ * Based on the DRP IE, transition an owner reservation to a new
+ * state.
+ */
+static void uwb_drp_process_owner(struct uwb_rc *rc, struct uwb_rsv *rsv,
+ struct uwb_ie_drp *drp_ie)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+
+ if (drp_ie->status) {
+ switch (drp_ie->reason_code) {
+ case UWB_DRP_REASON_ACCEPTED:
+ uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_ESTABLISHED);
+ break;
+ case UWB_DRP_REASON_MODIFIED:
+ dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n",
+ drp_ie->reason_code, drp_ie->status);
+ break;
+ default:
+ dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n",
+ drp_ie->reason_code, drp_ie->status);
+ }
+ } else {
+ switch (drp_ie->reason_code) {
+ case UWB_DRP_REASON_PENDING:
+ uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_PENDING);
+ break;
+ case UWB_DRP_REASON_DENIED:
+ uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE);
+ break;
+ case UWB_DRP_REASON_CONFLICT:
+ case UWB_DRP_REASON_MODIFIED:
+ dev_err(dev, "FIXME: unhandled reason code (%d/%d)\n",
+ drp_ie->reason_code, drp_ie->status);
+ break;
+ default:
+ dev_warn(dev, "ignoring invalid DRP IE state (%d/%d)\n",
+ drp_ie->reason_code, drp_ie->status);
+ }
+ }
+}
+
+/*
+ * Process a received DRP IE, it's either for a reservation owned by
+ * the RC or targeted at it (or it's for a WUSB cluster reservation).
+ */
+static void uwb_drp_process(struct uwb_rc *rc, struct uwb_dev *src,
+ struct uwb_ie_drp *drp_ie)
+{
+ struct uwb_rsv *rsv;
+
+ rsv = uwb_rsv_find(rc, src, drp_ie);
+ if (!rsv) {
+ /*
+ * No reservation? It's either for a recently
+ * terminated reservation; or the DRP IE couldn't be
+ * processed (e.g., an invalid IE or out of memory).
+ */
+ return;
+ }
+
+ /*
+ * Do nothing with DRP IEs for reservations that have been
+ * terminated.
+ */
+ if (rsv->state == UWB_RSV_STATE_NONE) {
+ uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE);
+ return;
+ }
+
+ if (drp_ie->owner)
+ uwb_drp_process_target(rc, rsv, drp_ie);
+ else
+ uwb_drp_process_owner(rc, rsv, drp_ie);
+}
+
+
+/*
+ * Process all the DRP IEs (both DRP IEs and the DRP Availability IE)
+ * from a device.
+ */
+static
+void uwb_drp_process_all(struct uwb_rc *rc, struct uwb_rc_evt_drp *drp_evt,
+ size_t ielen, struct uwb_dev *src_dev)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_ie_hdr *ie_hdr;
+ void *ptr;
+
+ ptr = drp_evt->IEData;
+ for (;;) {
+ ie_hdr = uwb_ie_next(&ptr, &ielen);
+ if (!ie_hdr)
+ break;
+
+ switch (ie_hdr->element_id) {
+ case UWB_IE_DRP_AVAILABILITY:
+ /* FIXME: does something need to be done with this? */
+ break;
+ case UWB_IE_DRP:
+ uwb_drp_process(rc, src_dev, (struct uwb_ie_drp *)ie_hdr);
+ break;
+ default:
+ dev_warn(dev, "unexpected IE in DRP notification\n");
+ break;
+ }
+ }
+
+ if (ielen > 0)
+ dev_warn(dev, "%d octets remaining in DRP notification\n",
+ (int)ielen);
+}
+
+
+/*
+ * Go through all the DRP IEs and find the ones that conflict with our
+ * reservations.
+ *
+ * FIXME: must resolve the conflict according the the rules in
+ * [ECMA-368].
+ */
+static
+void uwb_drp_process_conflict_all(struct uwb_rc *rc, struct uwb_rc_evt_drp *drp_evt,
+ size_t ielen, struct uwb_dev *src_dev)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_ie_hdr *ie_hdr;
+ struct uwb_ie_drp *drp_ie;
+ void *ptr;
+
+ ptr = drp_evt->IEData;
+ for (;;) {
+ ie_hdr = uwb_ie_next(&ptr, &ielen);
+ if (!ie_hdr)
+ break;
+
+ drp_ie = container_of(ie_hdr, struct uwb_ie_drp, hdr);
+
+ /* FIXME: check if this DRP IE conflicts. */
+ }
+
+ if (ielen > 0)
+ dev_warn(dev, "%d octets remaining in DRP notification\n",
+ (int)ielen);
+}
+
+
+/*
+ * Terminate all reservations owned by, or targeted at, 'uwb_dev'.
+ */
+static void uwb_drp_terminate_all(struct uwb_rc *rc, struct uwb_dev *uwb_dev)
+{
+ struct uwb_rsv *rsv;
+
+ list_for_each_entry(rsv, &rc->reservations, rc_node) {
+ if (rsv->owner == uwb_dev
+ || (rsv->target.type == UWB_RSV_TARGET_DEV && rsv->target.dev == uwb_dev))
+ uwb_rsv_remove(rsv);
+ }
+}
+
+
+/**
+ * uwbd_evt_handle_rc_drp - handle a DRP_IE event
+ * @evt: the DRP_IE event from the radio controller
+ *
+ * This processes DRP notifications from the radio controller, either
+ * initiating a new reservation or transitioning an existing
+ * reservation into a different state.
+ *
+ * DRP notifications can occur for three different reasons:
+ *
+ * - UWB_DRP_NOTIF_DRP_IE_RECVD: one or more DRP IEs with the RC as
+ * the target or source have been recieved.
+ *
+ * These DRP IEs could be new or for an existing reservation.
+ *
+ * If the DRP IE for an existing reservation ceases to be to
+ * recieved for at least mMaxLostBeacons, the reservation should be
+ * considered to be terminated. Note that the TERMINATE reason (see
+ * below) may not always be signalled (e.g., the remote device has
+ * two or more reservations established with the RC).
+ *
+ * - UWB_DRP_NOTIF_CONFLICT: DRP IEs from any device in the beacon
+ * group conflict with the RC's reservations.
+ *
+ * - UWB_DRP_NOTIF_TERMINATE: DRP IEs are no longer being received
+ * from a device (i.e., it's terminated all reservations).
+ *
+ * Only the software state of the reservations is changed; the setting
+ * of the radio controller's DRP IEs is done after all the events in
+ * an event buffer are processed. This saves waiting multiple times
+ * for the SET_DRP_IE command to complete.
+ */
+int uwbd_evt_handle_rc_drp(struct uwb_event *evt)
+{
+ struct device *dev = &evt->rc->uwb_dev.dev;
+ struct uwb_rc *rc = evt->rc;
+ struct uwb_rc_evt_drp *drp_evt;
+ size_t ielength, bytes_left;
+ struct uwb_dev_addr src_addr;
+ struct uwb_dev *src_dev;
+ int reason;
+
+ /* Is there enough data to decode the event (and any IEs in
+ its payload)? */
+ if (evt->size < sizeof(*drp_evt)) {
+ dev_err(dev, "DRP event: Not enough data to decode event "
+ "[%zu bytes left, %zu needed]\n",
+ evt->size, sizeof(*drp_evt));
+ return 0;
+ }
+ bytes_left = evt->size - sizeof(*drp_evt);
+ drp_evt = container_of(evt->rceb, struct uwb_rc_evt_drp, rceb);
+ ielength = le16_to_cpu(drp_evt->wIELength);
+ if (bytes_left != ielength) {
+ dev_err(dev, "DRP event: Not enough data in payload [%zu"
+ "bytes left, %zu declared in the event]\n",
+ bytes_left, ielength);
+ return 0;
+ }
+
+ memcpy(src_addr.data, &drp_evt->wSrcAddr, sizeof(src_addr));
+ src_dev = uwb_dev_get_by_devaddr(rc, &src_addr);
+ if (!src_dev) {
+ /*
+ * A DRP notification from an unrecognized device.
+ *
+ * This is probably from a WUSB device that doesn't
+ * have an EUI-48 and therefore doesn't show up in the
+ * UWB device database. It's safe to simply ignore
+ * these.
+ */
+ return 0;
+ }
+
+ mutex_lock(&rc->rsvs_mutex);
+
+ reason = drp_evt->Reason & 0x0f;
+
+ switch (reason) {
+ case UWB_DRP_NOTIF_DRP_IE_RCVD:
+ uwb_drp_process_all(rc, drp_evt, ielength, src_dev);
+ break;
+ case UWB_DRP_NOTIF_CONFLICT:
+ uwb_drp_process_conflict_all(rc, drp_evt, ielength, src_dev);
+ break;
+ case UWB_DRP_NOTIF_TERMINATE:
+ uwb_drp_terminate_all(rc, src_dev);
+ break;
+ default:
+ dev_warn(dev, "ignored DRP event with reason code: %d\n", reason);
+ break;
+ }
+
+ mutex_unlock(&rc->rsvs_mutex);
+
+ uwb_dev_put(src_dev);
+ return 0;
+}
diff --git a/drivers/uwb/est.c b/drivers/uwb/est.c
new file mode 100644
index 000000000000..bdb009a6ce89
--- /dev/null
+++ b/drivers/uwb/est.c
@@ -0,0 +1,414 @@
+/*
+ * Ultra Wide Band Radio Control
+ * Event Size Tables management
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ *
+ * Infrastructure, code and data tables for guessing the size of
+ * events received on the notification endpoints of UWB radio
+ * controllers.
+ *
+ * You define a table of events and for each, its size and how to get
+ * the extra size.
+ *
+ * ENTRY POINTS:
+ *
+ * uwb_est_{init/destroy}(): To initialize/release the EST subsystem.
+ *
+ * uwb_est_[u]register(): To un/register event size tables
+ * uwb_est_grow()
+ *
+ * uwb_est_find_size(): Get the size of an event
+ * uwb_est_get_size()
+ */
+#include <linux/spinlock.h>
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+#include "uwb-internal.h"
+
+
+struct uwb_est {
+ u16 type_event_high;
+ u16 vendor, product;
+ u8 entries;
+ const struct uwb_est_entry *entry;
+};
+
+
+static struct uwb_est *uwb_est;
+static u8 uwb_est_size;
+static u8 uwb_est_used;
+static DEFINE_RWLOCK(uwb_est_lock);
+
+/**
+ * WUSB Standard Event Size Table, HWA-RC interface
+ *
+ * Sizes for events and notifications type 0 (general), high nibble 0.
+ */
+static
+struct uwb_est_entry uwb_est_00_00xx[] = {
+ [UWB_RC_EVT_BEACON] = {
+ .size = sizeof(struct uwb_rc_evt_beacon),
+ .offset = 1 + offsetof(struct uwb_rc_evt_beacon,
+ wBeaconInfoLength),
+ },
+ [UWB_RC_EVT_BEACON_SIZE] = {
+ .size = sizeof(struct uwb_rc_evt_beacon_size),
+ },
+ [UWB_RC_EVT_BPOIE_CHANGE] = {
+ .size = sizeof(struct uwb_rc_evt_bpoie_change),
+ .offset = 1 + offsetof(struct uwb_rc_evt_bpoie_change,
+ wBPOIELength),
+ },
+ [UWB_RC_EVT_DRP_AVAIL] = {
+ .size = sizeof(struct uwb_rc_evt_drp_avail)
+ },
+ [UWB_RC_EVT_DRP] = {
+ .size = sizeof(struct uwb_rc_evt_drp),
+ .offset = 1 + offsetof(struct uwb_rc_evt_drp, wIELength),
+ },
+ [UWB_RC_EVT_BP_SLOT_CHANGE] = {
+ .size = sizeof(struct uwb_rc_evt_bp_slot_change),
+ },
+ [UWB_RC_CMD_CHANNEL_CHANGE] = { .size = 5 },
+ [UWB_RC_CMD_DEV_ADDR_MGMT] = {
+ .size = sizeof(struct uwb_rc_evt_dev_addr_mgmt) },
+ [UWB_RC_CMD_GET_IE] = {
+ .size = sizeof(struct uwb_rc_evt_get_ie),
+ .offset = 1 + offsetof(struct uwb_rc_evt_get_ie, wIELength),
+ },
+ [UWB_RC_CMD_RESET] = { .size = 5 },
+ [UWB_RC_CMD_SCAN] = { .size = 5 },
+ [UWB_RC_CMD_SET_BEACON_FILTER] = { .size = 5 },
+ [UWB_RC_CMD_SET_DRP_IE] = { .size = 7 },
+ [UWB_RC_CMD_SET_IE] = { .size = 7 },
+ [UWB_RC_CMD_START_BEACON] = { .size = 5 },
+ [UWB_RC_CMD_STOP_BEACON] = { .size = 5 },
+};
+
+
+/**
+ * Initialize the EST subsystem
+ *
+ * Register the standard tables also.
+ *
+ * FIXME: tag init
+ */
+int uwb_est_create(void)
+{
+ int result;
+
+ uwb_est_size = 2;
+ uwb_est_used = 0;
+ uwb_est = kmalloc(uwb_est_size * sizeof(uwb_est[0]), GFP_KERNEL);
+ if (uwb_est == NULL)
+ return -ENOMEM;
+ memset(uwb_est, 0, uwb_est_size * sizeof(uwb_est[0]));
+ result = uwb_est_register(
+ UWB_RC_CET_GENERAL, 0, 0xffff, 0xffff, uwb_est_00_00xx,
+ sizeof(uwb_est_00_00xx) / sizeof(uwb_est_00_00xx[0]));
+ return result;
+}
+
+
+/** Clean it up */
+void uwb_est_destroy(void)
+{
+ kfree(uwb_est);
+ uwb_est = NULL;
+ uwb_est_size = uwb_est_used = 0;
+}
+
+
+/**
+ * Double the capacity of the EST table
+ *
+ * @returns 0 if ok, < 0 errno no error.
+ */
+static
+int uwb_est_grow(void)
+{
+ size_t actual_size = uwb_est_size * sizeof(uwb_est[0]);
+ void *new = kmalloc(2 * actual_size, GFP_ATOMIC);
+ if (new == NULL)
+ return -ENOMEM;
+ memcpy(new, uwb_est, actual_size);
+ memset(new + actual_size, 0, actual_size);
+ kfree(uwb_est);
+ uwb_est = new;
+ uwb_est_size *= 2;
+ return 0;
+}
+
+
+/**
+ * Register an event size table
+ *
+ * Makes room for it if the table is full, and then inserts it in the
+ * right position (entries are sorted by type, event_high, vendor and
+ * then product).
+ *
+ * @vendor: vendor code for matching against the device (0x0000 and
+ * 0xffff mean any); use 0x0000 to force all to match without
+ * checking possible vendor specific ones, 0xfffff to match
+ * after checking vendor specific ones.
+ *
+ * @product: product code from that vendor; same matching rules, use
+ * 0x0000 for not allowing vendor specific matches, 0xffff
+ * for allowing.
+ *
+ * This arragement just makes the tables sort differenty. Because the
+ * table is sorted by growing type-event_high-vendor-product, a zero
+ * vendor will match before than a 0x456a vendor, that will match
+ * before a 0xfffff vendor.
+ *
+ * @returns 0 if ok, < 0 errno on error (-ENOENT if not found).
+ */
+/* FIXME: add bus type to vendor/product code */
+int uwb_est_register(u8 type, u8 event_high, u16 vendor, u16 product,
+ const struct uwb_est_entry *entry, size_t entries)
+{
+ unsigned long flags;
+ unsigned itr;
+ u16 type_event_high;
+ int result = 0;
+
+ write_lock_irqsave(&uwb_est_lock, flags);
+ if (uwb_est_used == uwb_est_size) {
+ result = uwb_est_grow();
+ if (result < 0)
+ goto out;
+ }
+ /* Find the right spot to insert it in */
+ type_event_high = type << 8 | event_high;
+ for (itr = 0; itr < uwb_est_used; itr++)
+ if (uwb_est[itr].type_event_high < type
+ && uwb_est[itr].vendor < vendor
+ && uwb_est[itr].product < product)
+ break;
+
+ /* Shift others to make room for the new one? */
+ if (itr < uwb_est_used)
+ memmove(&uwb_est[itr+1], &uwb_est[itr], uwb_est_used - itr);
+ uwb_est[itr].type_event_high = type << 8 | event_high;
+ uwb_est[itr].vendor = vendor;
+ uwb_est[itr].product = product;
+ uwb_est[itr].entry = entry;
+ uwb_est[itr].entries = entries;
+ uwb_est_used++;
+out:
+ write_unlock_irqrestore(&uwb_est_lock, flags);
+ return result;
+}
+EXPORT_SYMBOL_GPL(uwb_est_register);
+
+
+/**
+ * Unregister an event size table
+ *
+ * This just removes the specified entry and moves the ones after it
+ * to fill in the gap. This is needed to keep the list sorted; no
+ * reallocation is done to reduce the size of the table.
+ *
+ * We unregister by all the data we used to register instead of by
+ * pointer to the @entry array because we might have used the same
+ * table for a bunch of IDs (for example).
+ *
+ * @returns 0 if ok, < 0 errno on error (-ENOENT if not found).
+ */
+int uwb_est_unregister(u8 type, u8 event_high, u16 vendor, u16 product,
+ const struct uwb_est_entry *entry, size_t entries)
+{
+ unsigned long flags;
+ unsigned itr;
+ struct uwb_est est_cmp = {
+ .type_event_high = type << 8 | event_high,
+ .vendor = vendor,
+ .product = product,
+ .entry = entry,
+ .entries = entries
+ };
+ write_lock_irqsave(&uwb_est_lock, flags);
+ for (itr = 0; itr < uwb_est_used; itr++)
+ if (!memcmp(&uwb_est[itr], &est_cmp, sizeof(est_cmp)))
+ goto found;
+ write_unlock_irqrestore(&uwb_est_lock, flags);
+ return -ENOENT;
+
+found:
+ if (itr < uwb_est_used - 1) /* Not last one? move ones above */
+ memmove(&uwb_est[itr], &uwb_est[itr+1], uwb_est_used - itr - 1);
+ uwb_est_used--;
+ write_unlock_irqrestore(&uwb_est_lock, flags);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(uwb_est_unregister);
+
+
+/**
+ * Get the size of an event from a table
+ *
+ * @rceb: pointer to the buffer with the event
+ * @rceb_size: size of the area pointed to by @rceb in bytes.
+ * @returns: > 0 Size of the event
+ * -ENOSPC An area big enough was not provided to look
+ * ahead into the event's guts and guess the size.
+ * -EINVAL Unknown event code (wEvent).
+ *
+ * This will look at the received RCEB and guess what is the total
+ * size. For variable sized events, it will look further ahead into
+ * their length field to see how much data should be read.
+ *
+ * Note this size is *not* final--the neh (Notification/Event Handle)
+ * might specificy an extra size to add.
+ */
+static
+ssize_t uwb_est_get_size(struct uwb_rc *uwb_rc, struct uwb_est *est,
+ u8 event_low, const struct uwb_rceb *rceb,
+ size_t rceb_size)
+{
+ unsigned offset;
+ ssize_t size;
+ struct device *dev = &uwb_rc->uwb_dev.dev;
+ const struct uwb_est_entry *entry;
+
+ size = -ENOENT;
+ if (event_low >= est->entries) { /* in range? */
+ if (printk_ratelimit())
+ dev_err(dev, "EST %p 0x%04x/%04x/%04x[%u]: "
+ "event %u out of range\n",
+ est, est->type_event_high, est->vendor,
+ est->product, est->entries,
+ event_low);
+ goto out;
+ }
+ size = -ENOENT;
+ entry = &est->entry[event_low];
+ if (entry->size == 0 && entry->offset == 0) { /* unknown? */
+ if (printk_ratelimit())
+ dev_err(dev, "EST %p 0x%04x/%04x/%04x[%u]: "
+ "event %u unknown\n",
+ est, est->type_event_high, est->vendor,
+ est->product, est->entries, event_low);
+ goto out;
+ }
+ offset = entry->offset; /* extra fries with that? */
+ if (offset == 0)
+ size = entry->size;
+ else {
+ /* Ops, got an extra size field at 'offset'--read it */
+ const void *ptr = rceb;
+ size_t type_size = 0;
+ offset--;
+ size = -ENOSPC; /* enough data for more? */
+ switch (entry->type) {
+ case UWB_EST_16: type_size = sizeof(__le16); break;
+ case UWB_EST_8: type_size = sizeof(u8); break;
+ default: BUG();
+ }
+ if (offset + type_size >= rceb_size) {
+ if (printk_ratelimit())
+ dev_err(dev, "EST %p 0x%04x/%04x/%04x[%u]: "
+ "not enough data to read extra size\n",
+ est, est->type_event_high, est->vendor,
+ est->product, est->entries);
+ goto out;
+ }
+ size = entry->size;
+ ptr += offset;
+ switch (entry->type) {
+ case UWB_EST_16: size += le16_to_cpu(*(__le16 *)ptr); break;
+ case UWB_EST_8: size += *(u8 *)ptr; break;
+ default: BUG();
+ }
+ }
+out:
+ return size;
+}
+
+
+/**
+ * Guesses the size of a WA event
+ *
+ * @rceb: pointer to the buffer with the event
+ * @rceb_size: size of the area pointed to by @rceb in bytes.
+ * @returns: > 0 Size of the event
+ * -ENOSPC An area big enough was not provided to look
+ * ahead into the event's guts and guess the size.
+ * -EINVAL Unknown event code (wEvent).
+ *
+ * This will look at the received RCEB and guess what is the total
+ * size by checking all the tables registered with
+ * uwb_est_register(). For variable sized events, it will look further
+ * ahead into their length field to see how much data should be read.
+ *
+ * Note this size is *not* final--the neh (Notification/Event Handle)
+ * might specificy an extra size to add or replace.
+ */
+ssize_t uwb_est_find_size(struct uwb_rc *rc, const struct uwb_rceb *rceb,
+ size_t rceb_size)
+{
+ /* FIXME: add vendor/product data */
+ ssize_t size;
+ struct device *dev = &rc->uwb_dev.dev;
+ unsigned long flags;
+ unsigned itr;
+ u16 type_event_high, event;
+ u8 *ptr = (u8 *) rceb;
+
+ read_lock_irqsave(&uwb_est_lock, flags);
+ d_printf(2, dev, "Size query for event 0x%02x/%04x/%02x,"
+ " buffer size %ld\n",
+ (unsigned) rceb->bEventType,
+ (unsigned) le16_to_cpu(rceb->wEvent),
+ (unsigned) rceb->bEventContext,
+ (long) rceb_size);
+ size = -ENOSPC;
+ if (rceb_size < sizeof(*rceb))
+ goto out;
+ event = le16_to_cpu(rceb->wEvent);
+ type_event_high = rceb->bEventType << 8 | (event & 0xff00) >> 8;
+ for (itr = 0; itr < uwb_est_used; itr++) {
+ d_printf(3, dev, "Checking EST 0x%04x/%04x/%04x\n",
+ uwb_est[itr].type_event_high, uwb_est[itr].vendor,
+ uwb_est[itr].product);
+ if (uwb_est[itr].type_event_high != type_event_high)
+ continue;
+ size = uwb_est_get_size(rc, &uwb_est[itr],
+ event & 0x00ff, rceb, rceb_size);
+ /* try more tables that might handle the same type */
+ if (size != -ENOENT)
+ goto out;
+ }
+ /* FIXME: downgrade to _dbg() */
+ if (printk_ratelimit())
+ dev_err(dev, "event 0x%02x/%04x/%02x: no handlers available; "
+ "RCEB %02x %02x %02x %02x\n",
+ (unsigned) rceb->bEventType,
+ (unsigned) le16_to_cpu(rceb->wEvent),
+ (unsigned) rceb->bEventContext,
+ ptr[0], ptr[1], ptr[2], ptr[3]);
+ size = -ENOENT;
+out:
+ read_unlock_irqrestore(&uwb_est_lock, flags);
+ return size;
+}
+EXPORT_SYMBOL_GPL(uwb_est_find_size);
diff --git a/drivers/uwb/hwa-rc.c b/drivers/uwb/hwa-rc.c
new file mode 100644
index 000000000000..7c15f99c7f7d
--- /dev/null
+++ b/drivers/uwb/hwa-rc.c
@@ -0,0 +1,912 @@
+/*
+ * WUSB Host Wire Adapter: Radio Control Interface (WUSB[8.6])
+ * Radio Control command/event transport
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Initialize the Radio Control interface Driver.
+ *
+ * For each device probed, creates an 'struct hwarc' which contains
+ * just the representation of the UWB Radio Controller, and the logic
+ * for reading notifications and passing them to the UWB Core.
+ *
+ * So we initialize all of those, register the UWB Radio Controller
+ * and setup the notification/event handle to pipe the notifications
+ * to the UWB management Daemon.
+ *
+ * Command and event filtering.
+ *
+ * This is the driver for the Radio Control Interface described in WUSB
+ * 1.0. The core UWB module assumes that all drivers are compliant to the
+ * WHCI 0.95 specification. We thus create a filter that parses all
+ * incoming messages from the (WUSB 1.0) device and manipulate them to
+ * conform to the WHCI 0.95 specification. Similarly, outgoing messages
+ * are parsed and manipulated to conform to the WUSB 1.0 compliant messages
+ * that the device expects. Only a few messages are affected:
+ * Affected events:
+ * UWB_RC_EVT_BEACON
+ * UWB_RC_EVT_BP_SLOT_CHANGE
+ * UWB_RC_EVT_DRP_AVAIL
+ * UWB_RC_EVT_DRP
+ * Affected commands:
+ * UWB_RC_CMD_SCAN
+ * UWB_RC_CMD_SET_DRP_IE
+ *
+ *
+ *
+ */
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/wusb.h>
+#include <linux/usb/wusb-wa.h>
+#include <linux/uwb.h>
+#include "uwb-internal.h"
+#define D_LOCAL 1
+#include <linux/uwb/debug.h>
+
+/* The device uses commands and events from the WHCI specification, although
+ * reporting itself as WUSB compliant. */
+#define WUSB_QUIRK_WHCI_CMD_EVT 0x01
+
+/**
+ * Descriptor for an instance of the UWB Radio Control Driver that
+ * attaches to the RCI interface of the Host Wired Adapter.
+ *
+ * Unless there is a lock specific to the 'data members', all access
+ * is protected by uwb_rc->mutex.
+ *
+ * The NEEP (Notification/Event EndPoint) URB (@neep_urb) writes to
+ * @rd_buffer. Note there is no locking because it is perfectly (heh!)
+ * serialized--probe() submits an URB, callback is called, processes
+ * the data (synchronously), submits another URB, and so on. There is
+ * no concurrent access to the buffer.
+ */
+struct hwarc {
+ struct usb_device *usb_dev;
+ struct usb_interface *usb_iface;
+ struct uwb_rc *uwb_rc; /* UWB host controller */
+ struct urb *neep_urb; /* Notification endpoint handling */
+ struct edc neep_edc;
+ void *rd_buffer; /* NEEP read buffer */
+};
+
+
+/* Beacon received notification (WUSB 1.0 [8.6.3.2]) */
+struct uwb_rc_evt_beacon_WUSB_0100 {
+ struct uwb_rceb rceb;
+ u8 bChannelNumber;
+ __le16 wBPSTOffset;
+ u8 bLQI;
+ u8 bRSSI;
+ __le16 wBeaconInfoLength;
+ u8 BeaconInfo[];
+} __attribute__((packed));
+
+/**
+ * Filter WUSB 1.0 BEACON RCV notification to be WHCI 0.95
+ *
+ * @header: the incoming event
+ * @buf_size: size of buffer containing incoming event
+ * @new_size: size of event after filtering completed
+ *
+ * The WHCI 0.95 spec has a "Beacon Type" field. This value is unknown at
+ * the time we receive the beacon from WUSB so we just set it to
+ * UWB_RC_BEACON_TYPE_NEIGHBOR as a default.
+ * The solution below allocates memory upon receipt of every beacon from a
+ * WUSB device. This will deteriorate performance. What is the right way to
+ * do this?
+ */
+static
+int hwarc_filter_evt_beacon_WUSB_0100(struct uwb_rc *rc,
+ struct uwb_rceb **header,
+ const size_t buf_size,
+ size_t *new_size)
+{
+ struct uwb_rc_evt_beacon_WUSB_0100 *be;
+ struct uwb_rc_evt_beacon *newbe;
+ size_t bytes_left, ielength;
+ struct device *dev = &rc->uwb_dev.dev;
+
+ be = container_of(*header, struct uwb_rc_evt_beacon_WUSB_0100, rceb);
+ bytes_left = buf_size;
+ if (bytes_left < sizeof(*be)) {
+ dev_err(dev, "Beacon Received Notification: Not enough data "
+ "to decode for filtering (%zu vs %zu bytes needed)\n",
+ bytes_left, sizeof(*be));
+ return -EINVAL;
+ }
+ bytes_left -= sizeof(*be);
+ ielength = le16_to_cpu(be->wBeaconInfoLength);
+ if (bytes_left < ielength) {
+ dev_err(dev, "Beacon Received Notification: Not enough data "
+ "to decode IEs (%zu vs %zu bytes needed)\n",
+ bytes_left, ielength);
+ return -EINVAL;
+ }
+ newbe = kzalloc(sizeof(*newbe) + ielength, GFP_ATOMIC);
+ if (newbe == NULL)
+ return -ENOMEM;
+ newbe->rceb = be->rceb;
+ newbe->bChannelNumber = be->bChannelNumber;
+ newbe->bBeaconType = UWB_RC_BEACON_TYPE_NEIGHBOR;
+ newbe->wBPSTOffset = be->wBPSTOffset;
+ newbe->bLQI = be->bLQI;
+ newbe->bRSSI = be->bRSSI;
+ newbe->wBeaconInfoLength = be->wBeaconInfoLength;
+ memcpy(newbe->BeaconInfo, be->BeaconInfo, ielength);
+ *header = &newbe->rceb;
+ *new_size = sizeof(*newbe) + ielength;
+ return 1; /* calling function will free memory */
+}
+
+
+/* DRP Availability change notification (WUSB 1.0 [8.6.3.8]) */
+struct uwb_rc_evt_drp_avail_WUSB_0100 {
+ struct uwb_rceb rceb;
+ __le16 wIELength;
+ u8 IEData[];
+} __attribute__((packed));
+
+/**
+ * Filter WUSB 1.0 DRP AVAILABILITY CHANGE notification to be WHCI 0.95
+ *
+ * @header: the incoming event
+ * @buf_size: size of buffer containing incoming event
+ * @new_size: size of event after filtering completed
+ */
+static
+int hwarc_filter_evt_drp_avail_WUSB_0100(struct uwb_rc *rc,
+ struct uwb_rceb **header,
+ const size_t buf_size,
+ size_t *new_size)
+{
+ struct uwb_rc_evt_drp_avail_WUSB_0100 *da;
+ struct uwb_rc_evt_drp_avail *newda;
+ struct uwb_ie_hdr *ie_hdr;
+ size_t bytes_left, ielength;
+ struct device *dev = &rc->uwb_dev.dev;
+
+
+ da = container_of(*header, struct uwb_rc_evt_drp_avail_WUSB_0100, rceb);
+ bytes_left = buf_size;
+ if (bytes_left < sizeof(*da)) {
+ dev_err(dev, "Not enough data to decode DRP Avail "
+ "Notification for filtering. Expected %zu, "
+ "received %zu.\n", (size_t)sizeof(*da), bytes_left);
+ return -EINVAL;
+ }
+ bytes_left -= sizeof(*da);
+ ielength = le16_to_cpu(da->wIELength);
+ if (bytes_left < ielength) {
+ dev_err(dev, "DRP Avail Notification filter: IE length "
+ "[%zu bytes] does not match actual length "
+ "[%zu bytes].\n", ielength, bytes_left);
+ return -EINVAL;
+ }
+ if (ielength < sizeof(*ie_hdr)) {
+ dev_err(dev, "DRP Avail Notification filter: Not enough "
+ "data to decode IE [%zu bytes, %zu needed]\n",
+ ielength, sizeof(*ie_hdr));
+ return -EINVAL;
+ }
+ ie_hdr = (void *) da->IEData;
+ if (ie_hdr->length > 32) {
+ dev_err(dev, "DRP Availability Change event has unexpected "
+ "length for filtering. Expected < 32 bytes, "
+ "got %zu bytes.\n", (size_t)ie_hdr->length);
+ return -EINVAL;
+ }
+ newda = kzalloc(sizeof(*newda), GFP_ATOMIC);
+ if (newda == NULL)
+ return -ENOMEM;
+ newda->rceb = da->rceb;
+ memcpy(newda->bmp, (u8 *) ie_hdr + sizeof(*ie_hdr), ie_hdr->length);
+ *header = &newda->rceb;
+ *new_size = sizeof(*newda);
+ return 1; /* calling function will free memory */
+}
+
+
+/* DRP notification (WUSB 1.0 [8.6.3.9]) */
+struct uwb_rc_evt_drp_WUSB_0100 {
+ struct uwb_rceb rceb;
+ struct uwb_dev_addr wSrcAddr;
+ u8 bExplicit;
+ __le16 wIELength;
+ u8 IEData[];
+} __attribute__((packed));
+
+/**
+ * Filter WUSB 1.0 DRP Notification to be WHCI 0.95
+ *
+ * @header: the incoming event
+ * @buf_size: size of buffer containing incoming event
+ * @new_size: size of event after filtering completed
+ *
+ * It is hard to manage DRP reservations without having a Reason code.
+ * Unfortunately there is none in the WUSB spec. We just set the default to
+ * DRP IE RECEIVED.
+ * We do not currently use the bBeaconSlotNumber value, so we set this to
+ * zero for now.
+ */
+static
+int hwarc_filter_evt_drp_WUSB_0100(struct uwb_rc *rc,
+ struct uwb_rceb **header,
+ const size_t buf_size,
+ size_t *new_size)
+{
+ struct uwb_rc_evt_drp_WUSB_0100 *drpev;
+ struct uwb_rc_evt_drp *newdrpev;
+ size_t bytes_left, ielength;
+ struct device *dev = &rc->uwb_dev.dev;
+
+ drpev = container_of(*header, struct uwb_rc_evt_drp_WUSB_0100, rceb);
+ bytes_left = buf_size;
+ if (bytes_left < sizeof(*drpev)) {
+ dev_err(dev, "Not enough data to decode DRP Notification "
+ "for filtering. Expected %zu, received %zu.\n",
+ (size_t)sizeof(*drpev), bytes_left);
+ return -EINVAL;
+ }
+ ielength = le16_to_cpu(drpev->wIELength);
+ bytes_left -= sizeof(*drpev);
+ if (bytes_left < ielength) {
+ dev_err(dev, "DRP Notification filter: header length [%zu "
+ "bytes] does not match actual length [%zu "
+ "bytes].\n", ielength, bytes_left);
+ return -EINVAL;
+ }
+ newdrpev = kzalloc(sizeof(*newdrpev) + ielength, GFP_ATOMIC);
+ if (newdrpev == NULL)
+ return -ENOMEM;
+ newdrpev->rceb = drpev->rceb;
+ newdrpev->wSrcAddr = drpev->wSrcAddr;
+ newdrpev->Reason = UWB_DRP_NOTIF_DRP_IE_RCVD;
+ newdrpev->bBeaconSlotNumber = 0;
+ newdrpev->wIELength = drpev->wIELength;
+ memcpy(newdrpev->IEData, drpev->IEData, ielength);
+ *header = &newdrpev->rceb;
+ *new_size = sizeof(*newdrpev) + ielength;
+ return 1; /* calling function will free memory */
+}
+
+
+/* Scan Command (WUSB 1.0 [8.6.2.5]) */
+struct uwb_rc_cmd_scan_WUSB_0100 {
+ struct uwb_rccb rccb;
+ u8 bChannelNumber;
+ u8 bScanState;
+} __attribute__((packed));
+
+/**
+ * Filter WHCI 0.95 SCAN command to be WUSB 1.0 SCAN command
+ *
+ * @header: command sent to device (compliant to WHCI 0.95)
+ * @size: size of command sent to device
+ *
+ * We only reduce the size by two bytes because the WUSB 1.0 scan command
+ * does not have the last field (wStarttime). Also, make sure we don't send
+ * the device an unexpected scan type.
+ */
+static
+int hwarc_filter_cmd_scan_WUSB_0100(struct uwb_rc *rc,
+ struct uwb_rccb **header,
+ size_t *size)
+{
+ struct uwb_rc_cmd_scan *sc;
+
+ sc = container_of(*header, struct uwb_rc_cmd_scan, rccb);
+
+ if (sc->bScanState == UWB_SCAN_ONLY_STARTTIME)
+ sc->bScanState = UWB_SCAN_ONLY;
+ /* Don't send the last two bytes. */
+ *size -= 2;
+ return 0;
+}
+
+
+/* SET DRP IE command (WUSB 1.0 [8.6.2.7]) */
+struct uwb_rc_cmd_set_drp_ie_WUSB_0100 {
+ struct uwb_rccb rccb;
+ u8 bExplicit;
+ __le16 wIELength;
+ struct uwb_ie_drp IEData[];
+} __attribute__((packed));
+
+/**
+ * Filter WHCI 0.95 SET DRP IE command to be WUSB 1.0 SET DRP IE command
+ *
+ * @header: command sent to device (compliant to WHCI 0.95)
+ * @size: size of command sent to device
+ *
+ * WUSB has an extra bExplicit field - we assume always explicit
+ * negotiation so this field is set. The command expected by the device is
+ * thus larger than the one prepared by the driver so we need to
+ * reallocate memory to accommodate this.
+ * We trust the driver to send us the correct data so no checking is done
+ * on incoming data - evn though it is variable length.
+ */
+static
+int hwarc_filter_cmd_set_drp_ie_WUSB_0100(struct uwb_rc *rc,
+ struct uwb_rccb **header,
+ size_t *size)
+{
+ struct uwb_rc_cmd_set_drp_ie *orgcmd;
+ struct uwb_rc_cmd_set_drp_ie_WUSB_0100 *cmd;
+ size_t ielength;
+
+ orgcmd = container_of(*header, struct uwb_rc_cmd_set_drp_ie, rccb);
+ ielength = le16_to_cpu(orgcmd->wIELength);
+ cmd = kzalloc(sizeof(*cmd) + ielength, GFP_KERNEL);
+ if (cmd == NULL)
+ return -ENOMEM;
+ cmd->rccb = orgcmd->rccb;
+ cmd->bExplicit = 0;
+ cmd->wIELength = orgcmd->wIELength;
+ memcpy(cmd->IEData, orgcmd->IEData, ielength);
+ *header = &cmd->rccb;
+ *size = sizeof(*cmd) + ielength;
+ return 1; /* calling function will free memory */
+}
+
+
+/**
+ * Filter data from WHCI driver to WUSB device
+ *
+ * @header: WHCI 0.95 compliant command from driver
+ * @size: length of command
+ *
+ * The routine managing commands to the device (uwb_rc_cmd()) will call the
+ * filtering function pointer (if it exists) before it passes any data to
+ * the device. At this time the command has been formatted according to
+ * WHCI 0.95 and is ready to be sent to the device.
+ *
+ * The filter function will be provided with the current command and its
+ * length. The function will manipulate the command if necessary and
+ * potentially reallocate memory for a command that needed more memory that
+ * the given command. If new memory was created the function will return 1
+ * to indicate to the calling function that the memory need to be freed
+ * when not needed any more. The size will contain the new length of the
+ * command.
+ * If memory has not been allocated we rely on the original mechanisms to
+ * free the memory of the command - even when we reduce the value of size.
+ */
+static
+int hwarc_filter_cmd_WUSB_0100(struct uwb_rc *rc, struct uwb_rccb **header,
+ size_t *size)
+{
+ int result;
+ struct uwb_rccb *rccb = *header;
+ int cmd = le16_to_cpu(rccb->wCommand);
+ switch (cmd) {
+ case UWB_RC_CMD_SCAN:
+ result = hwarc_filter_cmd_scan_WUSB_0100(rc, header, size);
+ break;
+ case UWB_RC_CMD_SET_DRP_IE:
+ result = hwarc_filter_cmd_set_drp_ie_WUSB_0100(rc, header, size);
+ break;
+ default:
+ result = -ENOANO;
+ break;
+ }
+ return result;
+}
+
+
+/**
+ * Filter data from WHCI driver to WUSB device
+ *
+ * @header: WHCI 0.95 compliant command from driver
+ * @size: length of command
+ *
+ * Filter commands based on which protocol the device supports. The WUSB
+ * errata should be the same as WHCI 0.95 so we do not filter that here -
+ * only WUSB 1.0.
+ */
+static
+int hwarc_filter_cmd(struct uwb_rc *rc, struct uwb_rccb **header,
+ size_t *size)
+{
+ int result = -ENOANO;
+ if (rc->version == 0x0100)
+ result = hwarc_filter_cmd_WUSB_0100(rc, header, size);
+ return result;
+}
+
+
+/**
+ * Compute return value as sum of incoming value and value at given offset
+ *
+ * @rceb: event for which we compute the size, it contains a variable
+ * length field.
+ * @core_size: size of the "non variable" part of the event
+ * @offset: place in event where the length of the variable part is stored
+ * @buf_size: total length of buffer in which event arrived - we need to make
+ * sure we read the offset in memory that is still part of the event
+ */
+static
+ssize_t hwarc_get_event_size(struct uwb_rc *rc, const struct uwb_rceb *rceb,
+ size_t core_size, size_t offset,
+ const size_t buf_size)
+{
+ ssize_t size = -ENOSPC;
+ const void *ptr = rceb;
+ size_t type_size = sizeof(__le16);
+ struct device *dev = &rc->uwb_dev.dev;
+
+ if (offset + type_size >= buf_size) {
+ dev_err(dev, "Not enough data to read extra size of event "
+ "0x%02x/%04x/%02x, only got %zu bytes.\n",
+ rceb->bEventType, le16_to_cpu(rceb->wEvent),
+ rceb->bEventContext, buf_size);
+ goto out;
+ }
+ ptr += offset;
+ size = core_size + le16_to_cpu(*(__le16 *)ptr);
+out:
+ return size;
+}
+
+
+/* Beacon slot change notification (WUSB 1.0 [8.6.3.5]) */
+struct uwb_rc_evt_bp_slot_change_WUSB_0100 {
+ struct uwb_rceb rceb;
+ u8 bSlotNumber;
+} __attribute__((packed));
+
+
+/**
+ * Filter data from WUSB device to WHCI driver
+ *
+ * @header: incoming event
+ * @buf_size: size of buffer in which event arrived
+ * @_event_size: actual size of event in the buffer
+ * @new_size: size of event after filtered
+ *
+ * We don't know how the buffer is constructed - there may be more than one
+ * event in it so buffer length does not determine event length. We first
+ * determine the expected size of the incoming event. This value is passed
+ * back only if the actual filtering succeeded (so we know the computed
+ * expected size is correct). This value will be zero if
+ * the event did not need any filtering.
+ *
+ * WHCI interprets the BP Slot Change event's data differently than
+ * WUSB. The event sizes are exactly the same. The data field
+ * indicates the new beacon slot in which a RC is transmitting its
+ * beacon. The maximum value of this is 96 (wMacBPLength ECMA-368
+ * 17.16 (Table 117)). We thus know that the WUSB value will not set
+ * the bit bNoSlot, so we don't really do anything (placeholder).
+ */
+static
+int hwarc_filter_event_WUSB_0100(struct uwb_rc *rc, struct uwb_rceb **header,
+ const size_t buf_size, size_t *_real_size,
+ size_t *_new_size)
+{
+ int result = -ENOANO;
+ struct uwb_rceb *rceb = *header;
+ int event = le16_to_cpu(rceb->wEvent);
+ size_t event_size;
+ size_t core_size, offset;
+
+ if (rceb->bEventType != UWB_RC_CET_GENERAL)
+ goto out;
+ switch (event) {
+ case UWB_RC_EVT_BEACON:
+ core_size = sizeof(struct uwb_rc_evt_beacon_WUSB_0100);
+ offset = offsetof(struct uwb_rc_evt_beacon_WUSB_0100,
+ wBeaconInfoLength);
+ event_size = hwarc_get_event_size(rc, rceb, core_size,
+ offset, buf_size);
+ if (event_size < 0)
+ goto out;
+ *_real_size = event_size;
+ result = hwarc_filter_evt_beacon_WUSB_0100(rc, header,
+ buf_size, _new_size);
+ break;
+ case UWB_RC_EVT_BP_SLOT_CHANGE:
+ *_new_size = *_real_size =
+ sizeof(struct uwb_rc_evt_bp_slot_change_WUSB_0100);
+ result = 0;
+ break;
+
+ case UWB_RC_EVT_DRP_AVAIL:
+ core_size = sizeof(struct uwb_rc_evt_drp_avail_WUSB_0100);
+ offset = offsetof(struct uwb_rc_evt_drp_avail_WUSB_0100,
+ wIELength);
+ event_size = hwarc_get_event_size(rc, rceb, core_size,
+ offset, buf_size);
+ if (event_size < 0)
+ goto out;
+ *_real_size = event_size;
+ result = hwarc_filter_evt_drp_avail_WUSB_0100(
+ rc, header, buf_size, _new_size);
+ break;
+
+ case UWB_RC_EVT_DRP:
+ core_size = sizeof(struct uwb_rc_evt_drp_WUSB_0100);
+ offset = offsetof(struct uwb_rc_evt_drp_WUSB_0100, wIELength);
+ event_size = hwarc_get_event_size(rc, rceb, core_size,
+ offset, buf_size);
+ if (event_size < 0)
+ goto out;
+ *_real_size = event_size;
+ result = hwarc_filter_evt_drp_WUSB_0100(rc, header,
+ buf_size, _new_size);
+ break;
+
+ default:
+ break;
+ }
+out:
+ return result;
+}
+
+/**
+ * Filter data from WUSB device to WHCI driver
+ *
+ * @header: incoming event
+ * @buf_size: size of buffer in which event arrived
+ * @_event_size: actual size of event in the buffer
+ * @_new_size: size of event after filtered
+ *
+ * Filter events based on which protocol the device supports. The WUSB
+ * errata should be the same as WHCI 0.95 so we do not filter that here -
+ * only WUSB 1.0.
+ *
+ * If we don't handle it, we return -ENOANO (why the weird error code?
+ * well, so if I get it, I can pinpoint in the code that raised
+ * it...after all, not too many places use the higher error codes).
+ */
+static
+int hwarc_filter_event(struct uwb_rc *rc, struct uwb_rceb **header,
+ const size_t buf_size, size_t *_real_size,
+ size_t *_new_size)
+{
+ int result = -ENOANO;
+ if (rc->version == 0x0100)
+ result = hwarc_filter_event_WUSB_0100(
+ rc, header, buf_size, _real_size, _new_size);
+ return result;
+}
+
+
+/**
+ * Execute an UWB RC command on HWA
+ *
+ * @rc: Instance of a Radio Controller that is a HWA
+ * @cmd: Buffer containing the RCCB and payload to execute
+ * @cmd_size: Size of the command buffer.
+ *
+ * NOTE: rc's mutex has to be locked
+ */
+static
+int hwarc_cmd(struct uwb_rc *uwb_rc, const struct uwb_rccb *cmd, size_t cmd_size)
+{
+ struct hwarc *hwarc = uwb_rc->priv;
+ return usb_control_msg(
+ hwarc->usb_dev, usb_sndctrlpipe(hwarc->usb_dev, 0),
+ WA_EXEC_RC_CMD, USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, hwarc->usb_iface->cur_altsetting->desc.bInterfaceNumber,
+ (void *) cmd, cmd_size, 100 /* FIXME: this is totally arbitrary */);
+}
+
+/**
+ * Callback for the notification and event endpoint
+ *
+ * Check's that everything is fine and then passes the read data to
+ * the notification/event handling mechanism (neh).
+ */
+static
+void hwarc_neep_cb(struct urb *urb)
+{
+ struct hwarc *hwarc = urb->context;
+ struct usb_interface *usb_iface = hwarc->usb_iface;
+ struct device *dev = &usb_iface->dev;
+ int result;
+
+ switch (result = urb->status) {
+ case 0:
+ d_printf(3, dev, "NEEP: receive stat %d, %zu bytes\n",
+ urb->status, (size_t)urb->actual_length);
+ uwb_rc_neh_grok(hwarc->uwb_rc, urb->transfer_buffer,
+ urb->actual_length);
+ break;
+ case -ECONNRESET: /* Not an error, but a controlled situation; */
+ case -ENOENT: /* (we killed the URB)...so, no broadcast */
+ d_printf(2, dev, "NEEP: URB reset/noent %d\n", urb->status);
+ goto out;
+ case -ESHUTDOWN: /* going away! */
+ d_printf(2, dev, "NEEP: URB down %d\n", urb->status);
+ goto out;
+ default: /* On general errors, retry unless it gets ugly */
+ if (edc_inc(&hwarc->neep_edc, EDC_MAX_ERRORS,
+ EDC_ERROR_TIMEFRAME))
+ goto error_exceeded;
+ dev_err(dev, "NEEP: URB error %d\n", urb->status);
+ }
+ result = usb_submit_urb(urb, GFP_ATOMIC);
+ d_printf(3, dev, "NEEP: submit %d\n", result);
+ if (result < 0) {
+ dev_err(dev, "NEEP: Can't resubmit URB (%d) resetting device\n",
+ result);
+ goto error;
+ }
+out:
+ return;
+
+error_exceeded:
+ dev_err(dev, "NEEP: URB max acceptable errors "
+ "exceeded, resetting device\n");
+error:
+ usb_dev_reset_delayed(hwarc->usb_dev);
+ uwb_rc_neh_error(hwarc->uwb_rc, result);
+ return;
+}
+
+static void hwarc_init(struct hwarc *hwarc)
+{
+ edc_init(&hwarc->neep_edc);
+}
+
+/**
+ * Initialize the notification/event endpoint stuff
+ *
+ * Note this is effectively a parallel thread; it knows that
+ * hwarc->uwb_rc always exists because the existence of a 'hwarc'
+ * means that there is a reverence on the hwarc->uwb_rc (see
+ * _probe()), and thus _neep_cb() can execute safely.
+ */
+static int hwarc_neep_init(struct hwarc *hwarc, struct usb_interface *iface)
+{
+ int result;
+ struct usb_endpoint_descriptor *epd;
+ struct usb_device *usb_dev = interface_to_usbdev(iface);
+ struct device *dev = &iface->dev;
+
+ epd = &iface->cur_altsetting->endpoint[0].desc;
+ hwarc->rd_buffer = (void *) __get_free_page(GFP_KERNEL);
+ if (hwarc->rd_buffer == NULL) {
+ dev_err(dev, "Unable to allocate notification's read buffer\n");
+ goto error_rd_buffer;
+ }
+ hwarc->neep_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (hwarc->neep_urb == NULL) {
+ dev_err(dev, "Unable to allocate notification URB\n");
+ goto error_urb_alloc;
+ }
+ usb_fill_int_urb(hwarc->neep_urb, usb_dev,
+ usb_rcvintpipe(usb_dev, epd->bEndpointAddress),
+ hwarc->rd_buffer, PAGE_SIZE,
+ hwarc_neep_cb, hwarc, epd->bInterval);
+ result = usb_submit_urb(hwarc->neep_urb, GFP_ATOMIC);
+ if (result < 0) {
+ dev_err(dev, "Cannot submit notification URB: %d\n", result);
+ goto error_neep_submit;
+ }
+ return 0;
+
+error_neep_submit:
+ usb_free_urb(hwarc->neep_urb);
+error_urb_alloc:
+ free_page((unsigned long)hwarc->rd_buffer);
+error_rd_buffer:
+ return -ENOMEM;
+}
+
+
+/** Clean up all the notification endpoint resources */
+static void hwarc_neep_release(struct hwarc *hwarc)
+{
+ usb_kill_urb(hwarc->neep_urb);
+ usb_free_urb(hwarc->neep_urb);
+ free_page((unsigned long)hwarc->rd_buffer);
+}
+
+/**
+ * Get the version from class-specific descriptor
+ *
+ * NOTE: this descriptor comes with the big bundled configuration
+ * descriptor that includes the interfaces' and endpoints', so
+ * we just look for it in the cached copy kept by the USB stack.
+ *
+ * NOTE2: We convert LE fields to CPU order.
+ */
+static int hwarc_get_version(struct uwb_rc *rc)
+{
+ int result;
+
+ struct hwarc *hwarc = rc->priv;
+ struct uwb_rc_control_intf_class_desc *descr;
+ struct device *dev = &rc->uwb_dev.dev;
+ struct usb_device *usb_dev = hwarc->usb_dev;
+ char *itr;
+ struct usb_descriptor_header *hdr;
+ size_t itr_size, actconfig_idx;
+ u16 version;
+
+ actconfig_idx = (usb_dev->actconfig - usb_dev->config) /
+ sizeof(usb_dev->config[0]);
+ itr = usb_dev->rawdescriptors[actconfig_idx];
+ itr_size = le16_to_cpu(usb_dev->actconfig->desc.wTotalLength);
+ while (itr_size >= sizeof(*hdr)) {
+ hdr = (struct usb_descriptor_header *) itr;
+ d_printf(3, dev, "Extra device descriptor: "
+ "type %02x/%u bytes @ %zu (%zu left)\n",
+ hdr->bDescriptorType, hdr->bLength,
+ (itr - usb_dev->rawdescriptors[actconfig_idx]),
+ itr_size);
+ if (hdr->bDescriptorType == USB_DT_CS_RADIO_CONTROL)
+ goto found;
+ itr += hdr->bLength;
+ itr_size -= hdr->bLength;
+ }
+ dev_err(dev, "cannot find Radio Control Interface Class descriptor\n");
+ return -ENODEV;
+
+found:
+ result = -EINVAL;
+ if (hdr->bLength > itr_size) { /* is it available? */
+ dev_err(dev, "incomplete Radio Control Interface Class "
+ "descriptor (%zu bytes left, %u needed)\n",
+ itr_size, hdr->bLength);
+ goto error;
+ }
+ if (hdr->bLength < sizeof(*descr)) {
+ dev_err(dev, "short Radio Control Interface Class "
+ "descriptor\n");
+ goto error;
+ }
+ descr = (struct uwb_rc_control_intf_class_desc *) hdr;
+ /* Make LE fields CPU order */
+ version = __le16_to_cpu(descr->bcdRCIVersion);
+ if (version != 0x0100) {
+ dev_err(dev, "Device reports protocol version 0x%04x. We "
+ "do not support that. \n", version);
+ result = -EINVAL;
+ goto error;
+ }
+ rc->version = version;
+ d_printf(3, dev, "Device supports WUSB protocol version 0x%04x \n",
+ rc->version);
+ result = 0;
+error:
+ return result;
+}
+
+/*
+ * By creating a 'uwb_rc', we have a reference on it -- that reference
+ * is the one we drop when we disconnect.
+ *
+ * No need to switch altsettings; according to WUSB1.0[8.6.1.1], there
+ * is only one altsetting allowed.
+ */
+static int hwarc_probe(struct usb_interface *iface,
+ const struct usb_device_id *id)
+{
+ int result;
+ struct uwb_rc *uwb_rc;
+ struct hwarc *hwarc;
+ struct device *dev = &iface->dev;
+
+ result = -ENOMEM;
+ uwb_rc = uwb_rc_alloc();
+ if (uwb_rc == NULL) {
+ dev_err(dev, "unable to allocate RC instance\n");
+ goto error_rc_alloc;
+ }
+ hwarc = kzalloc(sizeof(*hwarc), GFP_KERNEL);
+ if (hwarc == NULL) {
+ dev_err(dev, "unable to allocate HWA RC instance\n");
+ goto error_alloc;
+ }
+ hwarc_init(hwarc);
+ hwarc->usb_dev = usb_get_dev(interface_to_usbdev(iface));
+ hwarc->usb_iface = usb_get_intf(iface);
+ result = hwarc_neep_init(hwarc, iface);
+ if (result < 0) {
+ dev_err(dev, "Cannot initialize notif/event endpoint: %d\n",
+ result);
+ goto error_neep_init;
+ }
+ hwarc->uwb_rc = uwb_rc;
+ if (id->driver_info & WUSB_QUIRK_WHCI_CMD_EVT)
+ result = uwb_rc_add(uwb_rc, dev, hwarc, hwarc_cmd,
+ NULL, NULL);
+ else
+ result = uwb_rc_add(uwb_rc, dev, hwarc, hwarc_cmd,
+ hwarc_filter_cmd, hwarc_filter_event);
+ if (result < 0)
+ goto error_rc_add;
+ result = hwarc_get_version(uwb_rc);
+ if (result < 0) {
+ dev_err(dev, "cannot retrieve version of RC \n");
+ goto error_get_version;
+ }
+ usb_set_intfdata(iface, hwarc);
+ return 0;
+
+ uwb_rc_rm(uwb_rc);
+error_rc_add:
+error_get_version:
+ hwarc_neep_release(hwarc);
+error_neep_init:
+ usb_put_intf(iface);
+ usb_put_dev(hwarc->usb_dev);
+error_alloc:
+ uwb_rc_put(uwb_rc);
+error_rc_alloc:
+ return result;
+}
+
+static void hwarc_disconnect(struct usb_interface *iface)
+{
+ struct hwarc *hwarc = usb_get_intfdata(iface);
+ struct uwb_rc *uwb_rc = hwarc->uwb_rc;
+
+ usb_set_intfdata(hwarc->usb_iface, NULL);
+ uwb_rc_rm(uwb_rc);
+ hwarc_neep_release(hwarc);
+ usb_put_intf(hwarc->usb_iface);
+ usb_put_dev(hwarc->usb_dev);
+ d_printf(1, &hwarc->usb_iface->dev, "freed hwarc %p\n", hwarc);
+ kfree(hwarc);
+ uwb_rc_put(uwb_rc); /* when creating the device, refcount = 1 */
+}
+
+/** USB device ID's that we handle */
+static struct usb_device_id hwarc_id_table[] = {
+ /* D-Link DUB-1210 */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3d02, 0xe0, 0x01, 0x02),
+ .driver_info = WUSB_QUIRK_WHCI_CMD_EVT },
+ /* Generic match for the Radio Control interface */
+ { USB_INTERFACE_INFO(0xe0, 0x01, 0x02), },
+ { },
+};
+MODULE_DEVICE_TABLE(usb, hwarc_id_table);
+
+static struct usb_driver hwarc_driver = {
+ .name = "hwa-rc",
+ .probe = hwarc_probe,
+ .disconnect = hwarc_disconnect,
+ .id_table = hwarc_id_table,
+};
+
+static int __init hwarc_driver_init(void)
+{
+ int result;
+ result = usb_register(&hwarc_driver);
+ if (result < 0)
+ printk(KERN_ERR "HWA-RC: Cannot register USB driver: %d\n",
+ result);
+ return result;
+
+}
+module_init(hwarc_driver_init);
+
+static void __exit hwarc_driver_exit(void)
+{
+ usb_deregister(&hwarc_driver);
+}
+module_exit(hwarc_driver_exit);
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("Host Wireless Adapter Radio Control Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/uwb/i1480/Makefile b/drivers/uwb/i1480/Makefile
new file mode 100644
index 000000000000..212bbc7d4c32
--- /dev/null
+++ b/drivers/uwb/i1480/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_UWB_I1480U) += dfu/ i1480-est.o
+obj-$(CONFIG_UWB_I1480U_WLP) += i1480u-wlp/
diff --git a/drivers/uwb/i1480/dfu/Makefile b/drivers/uwb/i1480/dfu/Makefile
new file mode 100644
index 000000000000..bd1b9f25424c
--- /dev/null
+++ b/drivers/uwb/i1480/dfu/Makefile
@@ -0,0 +1,9 @@
+obj-$(CONFIG_UWB_I1480U) += i1480-dfu-usb.o
+
+i1480-dfu-usb-objs := \
+ dfu.o \
+ mac.o \
+ phy.o \
+ usb.o
+
+
diff --git a/drivers/uwb/i1480/dfu/dfu.c b/drivers/uwb/i1480/dfu/dfu.c
new file mode 100644
index 000000000000..ebffaf542153
--- /dev/null
+++ b/drivers/uwb/i1480/dfu/dfu.c
@@ -0,0 +1,281 @@
+/*
+ * Intel Wireless UWB Link 1480
+ * Main driver
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Common code for firmware upload used by the USB and PCI version;
+ * i1480_fw_upload() takes a device descriptor and uses the function
+ * pointers it provides to upload firmware and prepare the PHY.
+ *
+ * As well, provides common functions used by the rest of the code.
+ */
+#include "i1480-dfu.h"
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/uwb.h>
+#include <linux/random.h>
+
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+/** @return 0 if If @evt is a valid reply event; otherwise complain */
+int i1480_rceb_check(const struct i1480 *i1480, const struct uwb_rceb *rceb,
+ const char *cmd, u8 context,
+ unsigned expected_type, unsigned expected_event)
+{
+ int result = 0;
+ struct device *dev = i1480->dev;
+ if (rceb->bEventContext != context) {
+ dev_err(dev, "%s: "
+ "unexpected context id 0x%02x (expected 0x%02x)\n",
+ cmd, rceb->bEventContext, context);
+ result = -EINVAL;
+ }
+ if (rceb->bEventType != expected_type) {
+ dev_err(dev, "%s: "
+ "unexpected event type 0x%02x (expected 0x%02x)\n",
+ cmd, rceb->bEventType, expected_type);
+ result = -EINVAL;
+ }
+ if (le16_to_cpu(rceb->wEvent) != expected_event) {
+ dev_err(dev, "%s: "
+ "unexpected event 0x%04x (expected 0x%04x)\n",
+ cmd, le16_to_cpu(rceb->wEvent), expected_event);
+ result = -EINVAL;
+ }
+ return result;
+}
+EXPORT_SYMBOL_GPL(i1480_rceb_check);
+
+
+/**
+ * Execute a Radio Control Command
+ *
+ * Command data has to be in i1480->cmd_buf.
+ *
+ * @returns size of the reply data filled in i1480->evt_buf or < 0 errno
+ * code on error.
+ */
+ssize_t i1480_cmd(struct i1480 *i1480, const char *cmd_name, size_t cmd_size,
+ size_t reply_size)
+{
+ ssize_t result;
+ struct uwb_rceb *reply = i1480->evt_buf;
+ struct uwb_rccb *cmd = i1480->cmd_buf;
+ u16 expected_event = reply->wEvent;
+ u8 expected_type = reply->bEventType;
+ u8 context;
+
+ d_fnstart(3, i1480->dev, "(%p, %s, %zu)\n", i1480, cmd_name, cmd_size);
+ init_completion(&i1480->evt_complete);
+ i1480->evt_result = -EINPROGRESS;
+ do {
+ get_random_bytes(&context, 1);
+ } while (context == 0x00 || context == 0xff);
+ cmd->bCommandContext = context;
+ result = i1480->cmd(i1480, cmd_name, cmd_size);
+ if (result < 0)
+ goto error;
+ /* wait for the callback to report a event was received */
+ result = wait_for_completion_interruptible_timeout(
+ &i1480->evt_complete, HZ);
+ if (result == 0) {
+ result = -ETIMEDOUT;
+ goto error;
+ }
+ if (result < 0)
+ goto error;
+ result = i1480->evt_result;
+ if (result < 0) {
+ dev_err(i1480->dev, "%s: command reply reception failed: %zd\n",
+ cmd_name, result);
+ goto error;
+ }
+ if (result != reply_size) {
+ dev_err(i1480->dev, "%s returned only %zu bytes, %zu expected\n",
+ cmd_name, result, reply_size);
+ result = -EINVAL;
+ goto error;
+ }
+ /* Verify we got the right event in response */
+ result = i1480_rceb_check(i1480, i1480->evt_buf, cmd_name, context,
+ expected_type, expected_event);
+error:
+ d_fnend(3, i1480->dev, "(%p, %s, %zu) = %zd\n",
+ i1480, cmd_name, cmd_size, result);
+ return result;
+}
+EXPORT_SYMBOL_GPL(i1480_cmd);
+
+
+/**
+ * Get information about the MAC and PHY
+ *
+ * @wa: Wired adaptor
+ * @neh: Notification/event handler
+ * @reply: Pointer to the reply event buffer
+ * @returns: 0 if ok, < 0 errno code on error.
+ */
+static
+int i1480_cmd_get_mac_phy_info(struct i1480 *i1480)
+{
+ int result;
+ struct uwb_rccb *cmd = i1480->cmd_buf;
+ struct i1480_evt_confirm_GMPI *reply = i1480->evt_buf;
+
+ cmd->bCommandType = i1480_CET_VS1;
+ cmd->wCommand = cpu_to_le16(i1480_CMD_GET_MAC_PHY_INFO);
+ reply->rceb.bEventType = i1480_CET_VS1;
+ reply->rceb.wEvent = i1480_EVT_GET_MAC_PHY_INFO;
+ result = i1480_cmd(i1480, "GET_MAC_PHY_INFO", sizeof(*cmd),
+ sizeof(*reply));
+ if (result < 0)
+ goto out;
+ if (le16_to_cpu(reply->status) != 0x00) {
+ dev_err(i1480->dev,
+ "GET_MAC_PHY_INFO: command execution failed: %d\n",
+ reply->status);
+ result = -EIO;
+ }
+out:
+ return result;
+}
+
+
+/**
+ * Get i1480's info and print it
+ *
+ * @wa: Wire Adapter
+ * @neh: Notification/event handler
+ * @returns: 0 if ok, < 0 errno code on error.
+ */
+static
+int i1480_check_info(struct i1480 *i1480)
+{
+ struct i1480_evt_confirm_GMPI *reply = i1480->evt_buf;
+ int result;
+ unsigned mac_fw_rev;
+#if i1480_FW <= 0x00000302
+ unsigned phy_fw_rev;
+#endif
+ if (i1480->quirk_no_check_info) {
+ dev_err(i1480->dev, "firmware info check disabled\n");
+ return 0;
+ }
+
+ result = i1480_cmd_get_mac_phy_info(i1480);
+ if (result < 0) {
+ dev_err(i1480->dev, "Cannot get MAC & PHY information: %d\n",
+ result);
+ goto out;
+ }
+ mac_fw_rev = le16_to_cpu(reply->mac_fw_rev);
+#if i1480_FW > 0x00000302
+ dev_info(i1480->dev,
+ "HW v%02hx "
+ "MAC FW v%02hx.%02hx caps %04hx "
+ "PHY type %02hx v%02hx caps %02hx %02hx %02hx\n",
+ reply->hw_rev, mac_fw_rev >> 8, mac_fw_rev & 0xff,
+ le16_to_cpu(reply->mac_caps),
+ reply->phy_vendor, reply->phy_rev,
+ reply->phy_caps[0], reply->phy_caps[1], reply->phy_caps[2]);
+#else
+ phy_fw_rev = le16_to_cpu(reply->phy_fw_rev);
+ dev_info(i1480->dev, "MAC FW v%02hx.%02hx caps %04hx "
+ " PHY FW v%02hx.%02hx caps %04hx\n",
+ mac_fw_rev >> 8, mac_fw_rev & 0xff,
+ le16_to_cpu(reply->mac_caps),
+ phy_fw_rev >> 8, phy_fw_rev & 0xff,
+ le16_to_cpu(reply->phy_caps));
+#endif
+ dev_dbg(i1480->dev,
+ "key-stores:%hu mcast-addr-stores:%hu sec-modes:%hu\n",
+ (unsigned short) reply->key_stores,
+ le16_to_cpu(reply->mcast_addr_stores),
+ (unsigned short) reply->sec_mode_supported);
+ /* FIXME: complain if fw version too low -- pending for
+ * numbering to stabilize */
+out:
+ return result;
+}
+
+
+static
+int i1480_print_state(struct i1480 *i1480)
+{
+ int result;
+ u32 *buf = (u32 *) i1480->cmd_buf;
+
+ result = i1480->read(i1480, 0x80080000, 2 * sizeof(*buf));
+ if (result < 0) {
+ dev_err(i1480->dev, "cannot read U & L states: %d\n", result);
+ goto error;
+ }
+ dev_info(i1480->dev, "state U 0x%08x, L 0x%08x\n", buf[0], buf[1]);
+error:
+ return result;
+}
+
+
+/*
+ * PCI probe, firmware uploader
+ *
+ * _mac_fw_upload() will call rc_setup(), which needs an rc_release().
+ */
+int i1480_fw_upload(struct i1480 *i1480)
+{
+ int result;
+
+ result = i1480_pre_fw_upload(i1480); /* PHY pre fw */
+ if (result < 0 && result != -ENOENT) {
+ i1480_print_state(i1480);
+ goto error;
+ }
+ result = i1480_mac_fw_upload(i1480); /* MAC fw */
+ if (result < 0) {
+ if (result == -ENOENT)
+ dev_err(i1480->dev, "Cannot locate MAC FW file '%s'\n",
+ i1480->mac_fw_name);
+ else
+ i1480_print_state(i1480);
+ goto error;
+ }
+ result = i1480_phy_fw_upload(i1480); /* PHY fw */
+ if (result < 0 && result != -ENOENT) {
+ i1480_print_state(i1480);
+ goto error_rc_release;
+ }
+ result = i1480_check_info(i1480);
+ if (result < 0) {
+ dev_warn(i1480->dev, "Warning! Cannot check firmware info: %d\n",
+ result);
+ result = 0;
+ }
+ dev_info(i1480->dev, "firmware uploaded successfully\n");
+error_rc_release:
+ if (i1480->rc_release)
+ i1480->rc_release(i1480);
+ result = 0;
+error:
+ return result;
+}
+EXPORT_SYMBOL_GPL(i1480_fw_upload);
diff --git a/drivers/uwb/i1480/dfu/i1480-dfu.h b/drivers/uwb/i1480/dfu/i1480-dfu.h
new file mode 100644
index 000000000000..4103b287ac71
--- /dev/null
+++ b/drivers/uwb/i1480/dfu/i1480-dfu.h
@@ -0,0 +1,263 @@
+/*
+ * i1480 Device Firmware Upload
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * This driver is the firmware uploader for the Intel Wireless UWB
+ * Link 1480 device (both in the USB and PCI incarnations).
+ *
+ * The process is quite simple: we stop the device, write the firmware
+ * to its memory and then restart it. Wait for the device to let us
+ * know it is done booting firmware. Ready.
+ *
+ * We might have to upload before or after a phy firmware (which might
+ * be done in two methods, using a normal firmware image or through
+ * the MPI port).
+ *
+ * Because USB and PCI use common methods, we just make ops out of the
+ * common operations (read, write, wait_init_done and cmd) and
+ * implement them in usb.c and pci.c.
+ *
+ * The flow is (some parts omitted):
+ *
+ * i1480_{usb,pci}_probe() On enumerate/discovery
+ * i1480_fw_upload()
+ * i1480_pre_fw_upload()
+ * __mac_fw_upload()
+ * fw_hdrs_load()
+ * mac_fw_hdrs_push()
+ * i1480->write() [i1480_{usb,pci}_write()]
+ * i1480_fw_cmp()
+ * i1480->read() [i1480_{usb,pci}_read()]
+ * i1480_mac_fw_upload()
+ * __mac_fw_upload()
+ * i1480->setup(()
+ * i1480->wait_init_done()
+ * i1480_cmd_reset()
+ * i1480->cmd() [i1480_{usb,pci}_cmd()]
+ * ...
+ * i1480_phy_fw_upload()
+ * request_firmware()
+ * i1480_mpi_write()
+ * i1480->cmd() [i1480_{usb,pci}_cmd()]
+ * i1480_check_info()
+ *
+ * Once the probe function enumerates the device and uploads the
+ * firmware, we just exit with -ENODEV, as we don't really want to
+ * attach to the device.
+ */
+#ifndef __i1480_DFU_H__
+#define __i1480_DFU_H__
+
+#include <linux/uwb/spec.h>
+#include <linux/types.h>
+#include <linux/completion.h>
+
+#define i1480_FW_UPLOAD_MODE_MASK (cpu_to_le32(0x00000018))
+
+#if i1480_FW > 0x00000302
+#define i1480_RCEB_EXTENDED
+#endif
+
+struct uwb_rccb;
+struct uwb_rceb;
+
+/*
+ * Common firmware upload handlers
+ *
+ * Normally you embed this struct in another one specific to your hw.
+ *
+ * @write Write to device's memory from buffer.
+ * @read Read from device's memory to i1480->evt_buf.
+ * @setup Setup device after basic firmware is uploaded
+ * @wait_init_done
+ * Wait for the device to send a notification saying init
+ * is done.
+ * @cmd FOP for issuing the command to the hardware. The
+ * command data is contained in i1480->cmd_buf and the size
+ * is supplied as an argument. The command replied is put
+ * in i1480->evt_buf and the size in i1480->evt_result (or if
+ * an error, a < 0 errno code).
+ *
+ * @cmd_buf Memory buffer used to send commands to the device.
+ * Allocated by the upper layers i1480_fw_upload().
+ * Size has to be @buf_size.
+ * @evt_buf Memory buffer used to place the async notifications
+ * received by the hw. Allocated by the upper layers
+ * i1480_fw_upload().
+ * Size has to be @buf_size.
+ * @cmd_complete
+ * Low level driver uses this to notify code waiting afor
+ * an event that the event has arrived and data is in
+ * i1480->evt_buf (and size/result in i1480->evt_result).
+ * @hw_rev
+ * Use this value to activate dfu code to support new revisions
+ * of hardware. i1480_init() sets this to a default value.
+ * It should be updated by the USB and PCI code.
+ */
+struct i1480 {
+ struct device *dev;
+
+ int (*write)(struct i1480 *, u32 addr, const void *, size_t);
+ int (*read)(struct i1480 *, u32 addr, size_t);
+ int (*rc_setup)(struct i1480 *);
+ void (*rc_release)(struct i1480 *);
+ int (*wait_init_done)(struct i1480 *);
+ int (*cmd)(struct i1480 *, const char *cmd_name, size_t cmd_size);
+ const char *pre_fw_name;
+ const char *mac_fw_name;
+ const char *mac_fw_name_deprecate; /* FIXME: Will go away */
+ const char *phy_fw_name;
+ u8 hw_rev;
+
+ size_t buf_size; /* size of both evt_buf and cmd_buf */
+ void *evt_buf, *cmd_buf;
+ ssize_t evt_result;
+ struct completion evt_complete;
+
+ u8 quirk_no_check_info:1;
+};
+
+static inline
+void i1480_init(struct i1480 *i1480)
+{
+ i1480->hw_rev = 1;
+ init_completion(&i1480->evt_complete);
+}
+
+extern int i1480_fw_upload(struct i1480 *);
+extern int i1480_pre_fw_upload(struct i1480 *);
+extern int i1480_mac_fw_upload(struct i1480 *);
+extern int i1480_phy_fw_upload(struct i1480 *);
+extern ssize_t i1480_cmd(struct i1480 *, const char *, size_t, size_t);
+extern int i1480_rceb_check(const struct i1480 *,
+ const struct uwb_rceb *, const char *, u8,
+ unsigned, unsigned);
+
+enum {
+ /* Vendor specific command type */
+ i1480_CET_VS1 = 0xfd,
+ /* i1480 commands */
+ i1480_CMD_SET_IP_MAS = 0x000e,
+ i1480_CMD_GET_MAC_PHY_INFO = 0x0003,
+ i1480_CMD_MPI_WRITE = 0x000f,
+ i1480_CMD_MPI_READ = 0x0010,
+ /* i1480 events */
+#if i1480_FW > 0x00000302
+ i1480_EVT_CONFIRM = 0x0002,
+ i1480_EVT_RM_INIT_DONE = 0x0101,
+ i1480_EVT_DEV_ADD = 0x0103,
+ i1480_EVT_DEV_RM = 0x0104,
+ i1480_EVT_DEV_ID_CHANGE = 0x0105,
+ i1480_EVT_GET_MAC_PHY_INFO = i1480_CMD_GET_MAC_PHY_INFO,
+#else
+ i1480_EVT_CONFIRM = 0x0002,
+ i1480_EVT_RM_INIT_DONE = 0x0101,
+ i1480_EVT_DEV_ADD = 0x0103,
+ i1480_EVT_DEV_RM = 0x0104,
+ i1480_EVT_DEV_ID_CHANGE = 0x0105,
+ i1480_EVT_GET_MAC_PHY_INFO = i1480_EVT_CONFIRM,
+#endif
+};
+
+
+struct i1480_evt_confirm {
+ struct uwb_rceb rceb;
+#ifdef i1480_RCEB_EXTENDED
+ __le16 wParamLength;
+#endif
+ u8 bResultCode;
+} __attribute__((packed));
+
+
+struct i1480_rceb {
+ struct uwb_rceb rceb;
+#ifdef i1480_RCEB_EXTENDED
+ __le16 wParamLength;
+#endif
+} __attribute__((packed));
+
+
+/**
+ * Get MAC & PHY Information confirm event structure
+ *
+ * Confirm event returned by the command.
+ */
+struct i1480_evt_confirm_GMPI {
+#if i1480_FW > 0x00000302
+ struct uwb_rceb rceb;
+ __le16 wParamLength;
+ __le16 status;
+ u8 mac_addr[6]; /* EUI-64 bit IEEE address [still 8 bytes?] */
+ u8 dev_addr[2];
+ __le16 mac_fw_rev; /* major = v >> 8; minor = v & 0xff */
+ u8 hw_rev;
+ u8 phy_vendor;
+ u8 phy_rev; /* major v = >> 8; minor = v & 0xff */
+ __le16 mac_caps;
+ u8 phy_caps[3];
+ u8 key_stores;
+ __le16 mcast_addr_stores;
+ u8 sec_mode_supported;
+#else
+ struct uwb_rceb rceb;
+ u8 status;
+ u8 mac_addr[8]; /* EUI-64 bit IEEE address [still 8 bytes?] */
+ u8 dev_addr[2];
+ __le16 mac_fw_rev; /* major = v >> 8; minor = v & 0xff */
+ __le16 phy_fw_rev; /* major v = >> 8; minor = v & 0xff */
+ __le16 mac_caps;
+ u8 phy_caps;
+ u8 key_stores;
+ __le16 mcast_addr_stores;
+ u8 sec_mode_supported;
+#endif
+} __attribute__((packed));
+
+
+struct i1480_cmd_mpi_write {
+ struct uwb_rccb rccb;
+ __le16 size;
+ u8 data[];
+};
+
+
+struct i1480_cmd_mpi_read {
+ struct uwb_rccb rccb;
+ __le16 size;
+ struct {
+ u8 page, offset;
+ } __attribute__((packed)) data[];
+} __attribute__((packed));
+
+
+struct i1480_evt_mpi_read {
+ struct uwb_rceb rceb;
+#ifdef i1480_RCEB_EXTENDED
+ __le16 wParamLength;
+#endif
+ u8 bResultCode;
+ __le16 size;
+ struct {
+ u8 page, offset, value;
+ } __attribute__((packed)) data[];
+} __attribute__((packed));
+
+
+#endif /* #ifndef __i1480_DFU_H__ */
diff --git a/drivers/uwb/i1480/dfu/mac.c b/drivers/uwb/i1480/dfu/mac.c
new file mode 100644
index 000000000000..22f22279413a
--- /dev/null
+++ b/drivers/uwb/i1480/dfu/mac.c
@@ -0,0 +1,529 @@
+/*
+ * Intel Wireless UWB Link 1480
+ * MAC Firmware upload implementation
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Implementation of the code for parsing the firmware file (extract
+ * the headers and binary code chunks) in the fw_*() functions. The
+ * code to upload pre and mac firmwares is the same, so it uses a
+ * common entry point in __mac_fw_upload(), which uses the i1480
+ * function pointers to push the firmware to the device.
+ */
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/uwb.h>
+#include "i1480-dfu.h"
+
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+/*
+ * Descriptor for a continuous segment of MAC fw data
+ */
+struct fw_hdr {
+ unsigned long address;
+ size_t length;
+ const u32 *bin;
+ struct fw_hdr *next;
+};
+
+
+/* Free a chain of firmware headers */
+static
+void fw_hdrs_free(struct fw_hdr *hdr)
+{
+ struct fw_hdr *next;
+
+ while (hdr) {
+ next = hdr->next;
+ kfree(hdr);
+ hdr = next;
+ }
+}
+
+
+/* Fill a firmware header descriptor from a memory buffer */
+static
+int fw_hdr_load(struct i1480 *i1480, struct fw_hdr *hdr, unsigned hdr_cnt,
+ const char *_data, const u32 *data_itr, const u32 *data_top)
+{
+ size_t hdr_offset = (const char *) data_itr - _data;
+ size_t remaining_size = (void *) data_top - (void *) data_itr;
+ if (data_itr + 2 > data_top) {
+ dev_err(i1480->dev, "fw hdr #%u/%zu: EOF reached in header at "
+ "offset %zu, limit %zu\n",
+ hdr_cnt, hdr_offset,
+ (const char *) data_itr + 2 - _data,
+ (const char *) data_top - _data);
+ return -EINVAL;
+ }
+ hdr->next = NULL;
+ hdr->address = le32_to_cpu(*data_itr++);
+ hdr->length = le32_to_cpu(*data_itr++);
+ hdr->bin = data_itr;
+ if (hdr->length > remaining_size) {
+ dev_err(i1480->dev, "fw hdr #%u/%zu: EOF reached in data; "
+ "chunk too long (%zu bytes), only %zu left\n",
+ hdr_cnt, hdr_offset, hdr->length, remaining_size);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+/**
+ * Get a buffer where the firmware is supposed to be and create a
+ * chain of headers linking them together.
+ *
+ * @phdr: where to place the pointer to the first header (headers link
+ * to the next via the @hdr->next ptr); need to free the whole
+ * chain when done.
+ *
+ * @_data: Pointer to the data buffer.
+ *
+ * @_data_size: Size of the data buffer (bytes); data size has to be a
+ * multiple of 4. Function will fail if not.
+ *
+ * Goes over the whole binary blob; reads the first chunk and creates
+ * a fw hdr from it (which points to where the data is in @_data and
+ * the length of the chunk); then goes on to the next chunk until
+ * done. Each header is linked to the next.
+ */
+static
+int fw_hdrs_load(struct i1480 *i1480, struct fw_hdr **phdr,
+ const char *_data, size_t data_size)
+{
+ int result;
+ unsigned hdr_cnt = 0;
+ u32 *data = (u32 *) _data, *data_itr, *data_top;
+ struct fw_hdr *hdr, **prev_hdr = phdr;
+
+ result = -EINVAL;
+ /* Check size is ok and pointer is aligned */
+ if (data_size % sizeof(u32) != 0)
+ goto error;
+ if ((unsigned long) _data % sizeof(u16) != 0)
+ goto error;
+ *phdr = NULL;
+ data_itr = data;
+ data_top = (u32 *) (_data + data_size);
+ while (data_itr < data_top) {
+ result = -ENOMEM;
+ hdr = kmalloc(sizeof(*hdr), GFP_KERNEL);
+ if (hdr == NULL) {
+ dev_err(i1480->dev, "Cannot allocate fw header "
+ "for chunk #%u\n", hdr_cnt);
+ goto error_alloc;
+ }
+ result = fw_hdr_load(i1480, hdr, hdr_cnt,
+ _data, data_itr, data_top);
+ if (result < 0)
+ goto error_load;
+ data_itr += 2 + hdr->length;
+ *prev_hdr = hdr;
+ prev_hdr = &hdr->next;
+ hdr_cnt++;
+ };
+ *prev_hdr = NULL;
+ return 0;
+
+error_load:
+ kfree(hdr);
+error_alloc:
+ fw_hdrs_free(*phdr);
+error:
+ return result;
+}
+
+
+/**
+ * Compares a chunk of fw with one in the devices's memory
+ *
+ * @i1480: Device instance
+ * @hdr: Pointer to the firmware chunk
+ * @returns: 0 if equal, < 0 errno on error. If > 0, it is the offset
+ * where the difference was found (plus one).
+ *
+ * Kind of dirty and simplistic, but does the trick in both the PCI
+ * and USB version. We do a quick[er] memcmp(), and if it fails, we do
+ * a byte-by-byte to find the offset.
+ */
+static
+ssize_t i1480_fw_cmp(struct i1480 *i1480, struct fw_hdr *hdr)
+{
+ ssize_t result = 0;
+ u32 src_itr = 0, cnt;
+ size_t size = hdr->length*sizeof(hdr->bin[0]);
+ size_t chunk_size;
+ u8 *bin = (u8 *) hdr->bin;
+
+ while (size > 0) {
+ chunk_size = size < i1480->buf_size? size : i1480->buf_size;
+ result = i1480->read(i1480, hdr->address + src_itr, chunk_size);
+ if (result < 0) {
+ dev_err(i1480->dev, "error reading for verification: "
+ "%zd\n", result);
+ goto error;
+ }
+ if (memcmp(i1480->cmd_buf, bin + src_itr, result)) {
+ u8 *buf = i1480->cmd_buf;
+ d_printf(2, i1480->dev,
+ "original data @ %p + %u, %zu bytes\n",
+ bin, src_itr, result);
+ d_dump(4, i1480->dev, bin + src_itr, result);
+ for (cnt = 0; cnt < result; cnt++)
+ if (bin[src_itr + cnt] != buf[cnt]) {
+ dev_err(i1480->dev, "byte failed at "
+ "src_itr %u cnt %u [0x%02x "
+ "vs 0x%02x]\n", src_itr, cnt,
+ bin[src_itr + cnt], buf[cnt]);
+ result = src_itr + cnt + 1;
+ goto cmp_failed;
+ }
+ }
+ src_itr += result;
+ size -= result;
+ }
+ result = 0;
+error:
+cmp_failed:
+ return result;
+}
+
+
+/**
+ * Writes firmware headers to the device.
+ *
+ * @prd: PRD instance
+ * @hdr: Processed firmware
+ * @returns: 0 if ok, < 0 errno on error.
+ */
+static
+int mac_fw_hdrs_push(struct i1480 *i1480, struct fw_hdr *hdr,
+ const char *fw_name, const char *fw_tag)
+{
+ struct device *dev = i1480->dev;
+ ssize_t result = 0;
+ struct fw_hdr *hdr_itr;
+ int verif_retry_count;
+
+ d_fnstart(3, dev, "(%p, %p)\n", i1480, hdr);
+ /* Now, header by header, push them to the hw */
+ for (hdr_itr = hdr; hdr_itr != NULL; hdr_itr = hdr_itr->next) {
+ verif_retry_count = 0;
+retry:
+ dev_dbg(dev, "fw chunk (%zu @ 0x%08lx)\n",
+ hdr_itr->length * sizeof(hdr_itr->bin[0]),
+ hdr_itr->address);
+ result = i1480->write(i1480, hdr_itr->address, hdr_itr->bin,
+ hdr_itr->length*sizeof(hdr_itr->bin[0]));
+ if (result < 0) {
+ dev_err(dev, "%s fw '%s': write failed (%zuB @ 0x%lx):"
+ " %zd\n", fw_tag, fw_name,
+ hdr_itr->length * sizeof(hdr_itr->bin[0]),
+ hdr_itr->address, result);
+ break;
+ }
+ result = i1480_fw_cmp(i1480, hdr_itr);
+ if (result < 0) {
+ dev_err(dev, "%s fw '%s': verification read "
+ "failed (%zuB @ 0x%lx): %zd\n",
+ fw_tag, fw_name,
+ hdr_itr->length * sizeof(hdr_itr->bin[0]),
+ hdr_itr->address, result);
+ break;
+ }
+ if (result > 0) { /* Offset where it failed + 1 */
+ result--;
+ dev_err(dev, "%s fw '%s': WARNING: verification "
+ "failed at 0x%lx: retrying\n",
+ fw_tag, fw_name, hdr_itr->address + result);
+ if (++verif_retry_count < 3)
+ goto retry; /* write this block again! */
+ dev_err(dev, "%s fw '%s': verification failed at 0x%lx: "
+ "tried %d times\n", fw_tag, fw_name,
+ hdr_itr->address + result, verif_retry_count);
+ result = -EINVAL;
+ break;
+ }
+ }
+ d_fnend(3, dev, "(%zd)\n", result);
+ return result;
+}
+
+
+/** Puts the device in firmware upload mode.*/
+static
+int mac_fw_upload_enable(struct i1480 *i1480)
+{
+ int result;
+ u32 reg = 0x800000c0;
+ u32 *buffer = (u32 *)i1480->cmd_buf;
+
+ if (i1480->hw_rev > 1)
+ reg = 0x8000d0d4;
+ result = i1480->read(i1480, reg, sizeof(u32));
+ if (result < 0)
+ goto error_cmd;
+ *buffer &= ~i1480_FW_UPLOAD_MODE_MASK;
+ result = i1480->write(i1480, reg, buffer, sizeof(u32));
+ if (result < 0)
+ goto error_cmd;
+ return 0;
+error_cmd:
+ dev_err(i1480->dev, "can't enable fw upload mode: %d\n", result);
+ return result;
+}
+
+
+/** Gets the device out of firmware upload mode. */
+static
+int mac_fw_upload_disable(struct i1480 *i1480)
+{
+ int result;
+ u32 reg = 0x800000c0;
+ u32 *buffer = (u32 *)i1480->cmd_buf;
+
+ if (i1480->hw_rev > 1)
+ reg = 0x8000d0d4;
+ result = i1480->read(i1480, reg, sizeof(u32));
+ if (result < 0)
+ goto error_cmd;
+ *buffer |= i1480_FW_UPLOAD_MODE_MASK;
+ result = i1480->write(i1480, reg, buffer, sizeof(u32));
+ if (result < 0)
+ goto error_cmd;
+ return 0;
+error_cmd:
+ dev_err(i1480->dev, "can't disable fw upload mode: %d\n", result);
+ return result;
+}
+
+
+
+/**
+ * Generic function for uploading a MAC firmware.
+ *
+ * @i1480: Device instance
+ * @fw_name: Name of firmware file to upload.
+ * @fw_tag: Name of the firmware type (for messages)
+ * [eg: MAC, PRE]
+ * @do_wait: Wait for device to emit initialization done message (0
+ * for PRE fws, 1 for MAC fws).
+ * @returns: 0 if ok, < 0 errno on error.
+ */
+static
+int __mac_fw_upload(struct i1480 *i1480, const char *fw_name,
+ const char *fw_tag)
+{
+ int result;
+ const struct firmware *fw;
+ struct fw_hdr *fw_hdrs;
+
+ d_fnstart(3, i1480->dev, "(%p, %s, %s)\n", i1480, fw_name, fw_tag);
+ result = request_firmware(&fw, fw_name, i1480->dev);
+ if (result < 0) /* Up to caller to complain on -ENOENT */
+ goto out;
+ d_printf(3, i1480->dev, "%s fw '%s': uploading\n", fw_tag, fw_name);
+ result = fw_hdrs_load(i1480, &fw_hdrs, fw->data, fw->size);
+ if (result < 0) {
+ dev_err(i1480->dev, "%s fw '%s': failed to parse firmware "
+ "file: %d\n", fw_tag, fw_name, result);
+ goto out_release;
+ }
+ result = mac_fw_upload_enable(i1480);
+ if (result < 0)
+ goto out_hdrs_release;
+ result = mac_fw_hdrs_push(i1480, fw_hdrs, fw_name, fw_tag);
+ mac_fw_upload_disable(i1480);
+out_hdrs_release:
+ if (result >= 0)
+ dev_info(i1480->dev, "%s fw '%s': uploaded\n", fw_tag, fw_name);
+ else
+ dev_err(i1480->dev, "%s fw '%s': failed to upload (%d), "
+ "power cycle device\n", fw_tag, fw_name, result);
+ fw_hdrs_free(fw_hdrs);
+out_release:
+ release_firmware(fw);
+out:
+ d_fnend(3, i1480->dev, "(%p, %s, %s) = %d\n", i1480, fw_name, fw_tag,
+ result);
+ return result;
+}
+
+
+/**
+ * Upload a pre-PHY firmware
+ *
+ */
+int i1480_pre_fw_upload(struct i1480 *i1480)
+{
+ int result;
+ result = __mac_fw_upload(i1480, i1480->pre_fw_name, "PRE");
+ if (result == 0)
+ msleep(400);
+ return result;
+}
+
+
+/**
+ * Reset a the MAC and PHY
+ *
+ * @i1480: Device's instance
+ * @returns: 0 if ok, < 0 errno code on error
+ *
+ * We put the command on kmalloc'ed memory as some arches cannot do
+ * USB from the stack. The reply event is copied from an stage buffer,
+ * so it can be in the stack. See WUSB1.0[8.6.2.4] for more details.
+ *
+ * We issue the reset to make sure the UWB controller reinits the PHY;
+ * this way we can now if the PHY init went ok.
+ */
+static
+int i1480_cmd_reset(struct i1480 *i1480)
+{
+ int result;
+ struct uwb_rccb *cmd = (void *) i1480->cmd_buf;
+ struct i1480_evt_reset {
+ struct uwb_rceb rceb;
+ u8 bResultCode;
+ } __attribute__((packed)) *reply = (void *) i1480->evt_buf;
+
+ result = -ENOMEM;
+ cmd->bCommandType = UWB_RC_CET_GENERAL;
+ cmd->wCommand = cpu_to_le16(UWB_RC_CMD_RESET);
+ reply->rceb.bEventType = UWB_RC_CET_GENERAL;
+ reply->rceb.wEvent = UWB_RC_CMD_RESET;
+ result = i1480_cmd(i1480, "RESET", sizeof(*cmd), sizeof(*reply));
+ if (result < 0)
+ goto out;
+ if (reply->bResultCode != UWB_RC_RES_SUCCESS) {
+ dev_err(i1480->dev, "RESET: command execution failed: %u\n",
+ reply->bResultCode);
+ result = -EIO;
+ }
+out:
+ return result;
+
+}
+
+
+/** Wait for the MAC FW to start running */
+static
+int i1480_fw_is_running_q(struct i1480 *i1480)
+{
+ int cnt = 0;
+ int result;
+ u32 *val = (u32 *) i1480->cmd_buf;
+
+ d_fnstart(3, i1480->dev, "(i1480 %p)\n", i1480);
+ for (cnt = 0; cnt < 10; cnt++) {
+ msleep(100);
+ result = i1480->read(i1480, 0x80080000, 4);
+ if (result < 0) {
+ dev_err(i1480->dev, "Can't read 0x8008000: %d\n", result);
+ goto out;
+ }
+ if (*val == 0x55555555UL) /* fw running? cool */
+ goto out;
+ if (printk_ratelimit())
+ d_printf(5, i1480->dev, "read #%d: 0x%08x\n", cnt, *val);
+ }
+ dev_err(i1480->dev, "Timed out waiting for fw to start\n");
+ result = -ETIMEDOUT;
+out:
+ d_fnend(3, i1480->dev, "(i1480 %p) = %d\n", i1480, result);
+ return result;
+
+}
+
+
+/**
+ * Upload MAC firmware, wait for it to start
+ *
+ * @i1480: Device instance
+ * @fw_name: Name of the file that contains the firmware
+ *
+ * This has to be called after the pre fw has been uploaded (if
+ * there is any).
+ */
+int i1480_mac_fw_upload(struct i1480 *i1480)
+{
+ int result = 0, deprecated_name = 0;
+ struct i1480_rceb *rcebe = (void *) i1480->evt_buf;
+
+ d_fnstart(3, i1480->dev, "(%p)\n", i1480);
+ result = __mac_fw_upload(i1480, i1480->mac_fw_name, "MAC");
+ if (result == -ENOENT) {
+ result = __mac_fw_upload(i1480, i1480->mac_fw_name_deprecate,
+ "MAC");
+ deprecated_name = 1;
+ }
+ if (result < 0)
+ return result;
+ if (deprecated_name == 1)
+ dev_warn(i1480->dev,
+ "WARNING: firmware file name %s is deprecated, "
+ "please rename to %s\n",
+ i1480->mac_fw_name_deprecate, i1480->mac_fw_name);
+ result = i1480_fw_is_running_q(i1480);
+ if (result < 0)
+ goto error_fw_not_running;
+ result = i1480->rc_setup? i1480->rc_setup(i1480) : 0;
+ if (result < 0) {
+ dev_err(i1480->dev, "Cannot setup after MAC fw upload: %d\n",
+ result);
+ goto error_setup;
+ }
+ result = i1480->wait_init_done(i1480); /* wait init'on */
+ if (result < 0) {
+ dev_err(i1480->dev, "MAC fw '%s': Initialization timed out "
+ "(%d)\n", i1480->mac_fw_name, result);
+ goto error_init_timeout;
+ }
+ /* verify we got the right initialization done event */
+ if (i1480->evt_result != sizeof(*rcebe)) {
+ dev_err(i1480->dev, "MAC fw '%s': initialization event returns "
+ "wrong size (%zu bytes vs %zu needed)\n",
+ i1480->mac_fw_name, i1480->evt_result, sizeof(*rcebe));
+ dump_bytes(i1480->dev, rcebe, min(i1480->evt_result, (ssize_t)32));
+ goto error_size;
+ }
+ result = -EIO;
+ if (rcebe->rceb.bEventType != i1480_CET_VS1
+ || le16_to_cpu(rcebe->rceb.wEvent) != i1480_EVT_RM_INIT_DONE) {
+ dev_err(i1480->dev, "wrong initialization event 0x%02x/%04x/%02x "
+ "received; expected 0x%02x/%04x/00\n",
+ rcebe->rceb.bEventType, le16_to_cpu(rcebe->rceb.wEvent),
+ rcebe->rceb.bEventContext, i1480_CET_VS1,
+ i1480_EVT_RM_INIT_DONE);
+ goto error_init_timeout;
+ }
+ result = i1480_cmd_reset(i1480);
+ if (result < 0)
+ dev_err(i1480->dev, "MAC fw '%s': MBOA reset failed (%d)\n",
+ i1480->mac_fw_name, result);
+error_fw_not_running:
+error_init_timeout:
+error_size:
+error_setup:
+ d_fnend(3, i1480->dev, "(i1480 %p) = %d\n", i1480, result);
+ return result;
+}
diff --git a/drivers/uwb/i1480/dfu/phy.c b/drivers/uwb/i1480/dfu/phy.c
new file mode 100644
index 000000000000..3b1a87de8e63
--- /dev/null
+++ b/drivers/uwb/i1480/dfu/phy.c
@@ -0,0 +1,203 @@
+/*
+ * Intel Wireless UWB Link 1480
+ * PHY parameters upload
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Code for uploading the PHY parameters to the PHY through the UWB
+ * Radio Control interface.
+ *
+ * We just send the data through the MPI interface using HWA-like
+ * commands and then reset the PHY to make sure it is ok.
+ */
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/usb/wusb.h>
+#include "i1480-dfu.h"
+
+
+/**
+ * Write a value array to an address of the MPI interface
+ *
+ * @i1480: Device descriptor
+ * @data: Data array to write
+ * @size: Size of the data array
+ * @returns: 0 if ok, < 0 errno code on error.
+ *
+ * The data array is organized into pairs:
+ *
+ * ADDRESS VALUE
+ *
+ * ADDRESS is BE 16 bit unsigned, VALUE 8 bit unsigned. Size thus has
+ * to be a multiple of three.
+ */
+static
+int i1480_mpi_write(struct i1480 *i1480, const void *data, size_t size)
+{
+ int result;
+ struct i1480_cmd_mpi_write *cmd = i1480->cmd_buf;
+ struct i1480_evt_confirm *reply = i1480->evt_buf;
+
+ BUG_ON(size > 480);
+ result = -ENOMEM;
+ cmd->rccb.bCommandType = i1480_CET_VS1;
+ cmd->rccb.wCommand = cpu_to_le16(i1480_CMD_MPI_WRITE);
+ cmd->size = cpu_to_le16(size);
+ memcpy(cmd->data, data, size);
+ reply->rceb.bEventType = i1480_CET_VS1;
+ reply->rceb.wEvent = i1480_CMD_MPI_WRITE;
+ result = i1480_cmd(i1480, "MPI-WRITE", sizeof(*cmd) + size, sizeof(*reply));
+ if (result < 0)
+ goto out;
+ if (reply->bResultCode != UWB_RC_RES_SUCCESS) {
+ dev_err(i1480->dev, "MPI-WRITE: command execution failed: %d\n",
+ reply->bResultCode);
+ result = -EIO;
+ }
+out:
+ return result;
+}
+
+
+/**
+ * Read a value array to from an address of the MPI interface
+ *
+ * @i1480: Device descriptor
+ * @data: where to place the read array
+ * @srcaddr: Where to read from
+ * @size: Size of the data read array
+ * @returns: 0 if ok, < 0 errno code on error.
+ *
+ * The command data array is organized into pairs ADDR0 ADDR1..., and
+ * the returned data in ADDR0 VALUE0 ADDR1 VALUE1...
+ *
+ * We generate the command array to be a sequential read and then
+ * rearrange the result.
+ *
+ * We use the i1480->cmd_buf for the command, i1480->evt_buf for the reply.
+ *
+ * As the reply has to fit in 512 bytes (i1480->evt_buffer), the max amount
+ * of values we can read is (512 - sizeof(*reply)) / 3
+ */
+static
+int i1480_mpi_read(struct i1480 *i1480, u8 *data, u16 srcaddr, size_t size)
+{
+ int result;
+ struct i1480_cmd_mpi_read *cmd = i1480->cmd_buf;
+ struct i1480_evt_mpi_read *reply = i1480->evt_buf;
+ unsigned cnt;
+
+ memset(i1480->cmd_buf, 0x69, 512);
+ memset(i1480->evt_buf, 0x69, 512);
+
+ BUG_ON(size > (i1480->buf_size - sizeof(*reply)) / 3);
+ result = -ENOMEM;
+ cmd->rccb.bCommandType = i1480_CET_VS1;
+ cmd->rccb.wCommand = cpu_to_le16(i1480_CMD_MPI_READ);
+ cmd->size = cpu_to_le16(3*size);
+ for (cnt = 0; cnt < size; cnt++) {
+ cmd->data[cnt].page = (srcaddr + cnt) >> 8;
+ cmd->data[cnt].offset = (srcaddr + cnt) & 0xff;
+ }
+ reply->rceb.bEventType = i1480_CET_VS1;
+ reply->rceb.wEvent = i1480_CMD_MPI_READ;
+ result = i1480_cmd(i1480, "MPI-READ", sizeof(*cmd) + 2*size,
+ sizeof(*reply) + 3*size);
+ if (result < 0)
+ goto out;
+ if (reply->bResultCode != UWB_RC_RES_SUCCESS) {
+ dev_err(i1480->dev, "MPI-READ: command execution failed: %d\n",
+ reply->bResultCode);
+ result = -EIO;
+ }
+ for (cnt = 0; cnt < size; cnt++) {
+ if (reply->data[cnt].page != (srcaddr + cnt) >> 8)
+ dev_err(i1480->dev, "MPI-READ: page inconsistency at "
+ "index %u: expected 0x%02x, got 0x%02x\n", cnt,
+ (srcaddr + cnt) >> 8, reply->data[cnt].page);
+ if (reply->data[cnt].offset != ((srcaddr + cnt) & 0x00ff))
+ dev_err(i1480->dev, "MPI-READ: offset inconsistency at "
+ "index %u: expected 0x%02x, got 0x%02x\n", cnt,
+ (srcaddr + cnt) & 0x00ff,
+ reply->data[cnt].offset);
+ data[cnt] = reply->data[cnt].value;
+ }
+ result = 0;
+out:
+ return result;
+}
+
+
+/**
+ * Upload a PHY firmware, wait for it to start
+ *
+ * @i1480: Device instance
+ * @fw_name: Name of the file that contains the firmware
+ *
+ * We assume the MAC fw is up and running. This means we can use the
+ * MPI interface to write the PHY firmware. Once done, we issue an
+ * MBOA Reset, which will force the MAC to reset and reinitialize the
+ * PHY. If that works, we are ready to go.
+ *
+ * Max packet size for the MPI write is 512, so the max buffer is 480
+ * (which gives us 160 byte triads of MSB, LSB and VAL for the data).
+ */
+int i1480_phy_fw_upload(struct i1480 *i1480)
+{
+ int result;
+ const struct firmware *fw;
+ const char *data_itr, *data_top;
+ const size_t MAX_BLK_SIZE = 480; /* 160 triads */
+ size_t data_size;
+ u8 phy_stat;
+
+ result = request_firmware(&fw, i1480->phy_fw_name, i1480->dev);
+ if (result < 0)
+ goto out;
+ /* Loop writing data in chunks as big as possible until done. */
+ for (data_itr = fw->data, data_top = data_itr + fw->size;
+ data_itr < data_top; data_itr += MAX_BLK_SIZE) {
+ data_size = min(MAX_BLK_SIZE, (size_t) (data_top - data_itr));
+ result = i1480_mpi_write(i1480, data_itr, data_size);
+ if (result < 0)
+ goto error_mpi_write;
+ }
+ /* Read MPI page 0, offset 6; if 0, PHY was initialized correctly. */
+ result = i1480_mpi_read(i1480, &phy_stat, 0x0006, 1);
+ if (result < 0) {
+ dev_err(i1480->dev, "PHY: can't get status: %d\n", result);
+ goto error_mpi_status;
+ }
+ if (phy_stat != 0) {
+ result = -ENODEV;
+ dev_info(i1480->dev, "error, PHY not ready: %u\n", phy_stat);
+ goto error_phy_status;
+ }
+ dev_info(i1480->dev, "PHY fw '%s': uploaded\n", i1480->phy_fw_name);
+error_phy_status:
+error_mpi_status:
+error_mpi_write:
+ release_firmware(fw);
+ if (result < 0)
+ dev_err(i1480->dev, "PHY fw '%s': failed to upload (%d), "
+ "power cycle device\n", i1480->phy_fw_name, result);
+out:
+ return result;
+}
diff --git a/drivers/uwb/i1480/dfu/usb.c b/drivers/uwb/i1480/dfu/usb.c
new file mode 100644
index 000000000000..3be642b719b6
--- /dev/null
+++ b/drivers/uwb/i1480/dfu/usb.c
@@ -0,0 +1,501 @@
+/*
+ * Intel Wireless UWB Link 1480
+ * USB SKU firmware upload implementation
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * This driver will prepare the i1480 device to behave as a real
+ * Wireless USB HWA adaptor by uploading the firmware.
+ *
+ * When the device is connected or driver is loaded, i1480_usb_probe()
+ * is called--this will allocate and initialize the device structure,
+ * fill in the pointers to the common functions (read, write,
+ * wait_init_done and cmd for HWA command execution) and once that is
+ * done, call the common firmware uploading routine. Then clean up and
+ * return -ENODEV, as we don't attach to the device.
+ *
+ * The rest are the basic ops we implement that the fw upload code
+ * uses to do its job. All the ops in the common code are i1480->NAME,
+ * the functions are i1480_usb_NAME().
+ */
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/usb.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/uwb.h>
+#include <linux/usb/wusb.h>
+#include <linux/usb/wusb-wa.h>
+#include "i1480-dfu.h"
+
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+
+struct i1480_usb {
+ struct i1480 i1480;
+ struct usb_device *usb_dev;
+ struct usb_interface *usb_iface;
+ struct urb *neep_urb; /* URB for reading from EP1 */
+};
+
+
+static
+void i1480_usb_init(struct i1480_usb *i1480_usb)
+{
+ i1480_init(&i1480_usb->i1480);
+}
+
+
+static
+int i1480_usb_create(struct i1480_usb *i1480_usb, struct usb_interface *iface)
+{
+ struct usb_device *usb_dev = interface_to_usbdev(iface);
+ int result = -ENOMEM;
+
+ i1480_usb->usb_dev = usb_get_dev(usb_dev); /* bind the USB device */
+ i1480_usb->usb_iface = usb_get_intf(iface);
+ usb_set_intfdata(iface, i1480_usb); /* Bind the driver to iface0 */
+ i1480_usb->neep_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (i1480_usb->neep_urb == NULL)
+ goto error;
+ return 0;
+
+error:
+ usb_set_intfdata(iface, NULL);
+ usb_put_intf(iface);
+ usb_put_dev(usb_dev);
+ return result;
+}
+
+
+static
+void i1480_usb_destroy(struct i1480_usb *i1480_usb)
+{
+ usb_kill_urb(i1480_usb->neep_urb);
+ usb_free_urb(i1480_usb->neep_urb);
+ usb_set_intfdata(i1480_usb->usb_iface, NULL);
+ usb_put_intf(i1480_usb->usb_iface);
+ usb_put_dev(i1480_usb->usb_dev);
+}
+
+
+/**
+ * Write a buffer to a memory address in the i1480 device
+ *
+ * @i1480: i1480 instance
+ * @memory_address:
+ * Address where to write the data buffer to.
+ * @buffer: Buffer to the data
+ * @size: Size of the buffer [has to be < 512].
+ * @returns: 0 if ok, < 0 errno code on error.
+ *
+ * Data buffers to USB cannot be on the stack or in vmalloc'ed areas,
+ * so we copy it to the local i1480 buffer before proceeding. In any
+ * case, we have a max size we can send, soooo.
+ */
+static
+int i1480_usb_write(struct i1480 *i1480, u32 memory_address,
+ const void *buffer, size_t size)
+{
+ int result = 0;
+ struct i1480_usb *i1480_usb = container_of(i1480, struct i1480_usb, i1480);
+ size_t buffer_size, itr = 0;
+
+ d_fnstart(3, i1480->dev, "(%p, 0x%08x, %p, %zu)\n",
+ i1480, memory_address, buffer, size);
+ BUG_ON(size & 0x3); /* Needs to be a multiple of 4 */
+ while (size > 0) {
+ buffer_size = size < i1480->buf_size?
+ size : i1480->buf_size;
+ memcpy(i1480->cmd_buf, buffer + itr, buffer_size);
+ result = usb_control_msg(
+ i1480_usb->usb_dev, usb_sndctrlpipe(i1480_usb->usb_dev, 0),
+ 0xf0, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ cpu_to_le16(memory_address & 0xffff),
+ cpu_to_le16((memory_address >> 16) & 0xffff),
+ i1480->cmd_buf, buffer_size, 100 /* FIXME: arbitrary */);
+ if (result < 0)
+ break;
+ d_printf(3, i1480->dev,
+ "wrote @ 0x%08x %u bytes (of %zu bytes requested)\n",
+ memory_address, result, buffer_size);
+ d_dump(4, i1480->dev, i1480->cmd_buf, result);
+ itr += result;
+ memory_address += result;
+ size -= result;
+ }
+ d_fnend(3, i1480->dev, "(%p, 0x%08x, %p, %zu) = %d\n",
+ i1480, memory_address, buffer, size, result);
+ return result;
+}
+
+
+/**
+ * Read a block [max size 512] of the device's memory to @i1480's buffer.
+ *
+ * @i1480: i1480 instance
+ * @memory_address:
+ * Address where to read from.
+ * @size: Size to read. Smaller than or equal to 512.
+ * @returns: >= 0 number of bytes written if ok, < 0 errno code on error.
+ *
+ * NOTE: if the memory address or block is incorrect, you might get a
+ * stall or a different memory read. Caller has to verify the
+ * memory address and size passed back in the @neh structure.
+ */
+static
+int i1480_usb_read(struct i1480 *i1480, u32 addr, size_t size)
+{
+ ssize_t result = 0, bytes = 0;
+ size_t itr, read_size = i1480->buf_size;
+ struct i1480_usb *i1480_usb = container_of(i1480, struct i1480_usb, i1480);
+
+ d_fnstart(3, i1480->dev, "(%p, 0x%08x, %zu)\n",
+ i1480, addr, size);
+ BUG_ON(size > i1480->buf_size);
+ BUG_ON(size & 0x3); /* Needs to be a multiple of 4 */
+ BUG_ON(read_size > 512);
+
+ if (addr >= 0x8000d200 && addr < 0x8000d400) /* Yeah, HW quirk */
+ read_size = 4;
+
+ for (itr = 0; itr < size; itr += read_size) {
+ size_t itr_addr = addr + itr;
+ size_t itr_size = min(read_size, size - itr);
+ result = usb_control_msg(
+ i1480_usb->usb_dev, usb_rcvctrlpipe(i1480_usb->usb_dev, 0),
+ 0xf0, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ cpu_to_le16(itr_addr & 0xffff),
+ cpu_to_le16((itr_addr >> 16) & 0xffff),
+ i1480->cmd_buf + itr, itr_size,
+ 100 /* FIXME: arbitrary */);
+ if (result < 0) {
+ dev_err(i1480->dev, "%s: USB read error: %zd\n",
+ __func__, result);
+ goto out;
+ }
+ if (result != itr_size) {
+ result = -EIO;
+ dev_err(i1480->dev,
+ "%s: partial read got only %zu bytes vs %zu expected\n",
+ __func__, result, itr_size);
+ goto out;
+ }
+ bytes += result;
+ }
+ result = bytes;
+out:
+ d_fnend(3, i1480->dev, "(%p, 0x%08x, %zu) = %zd\n",
+ i1480, addr, size, result);
+ if (result > 0)
+ d_dump(4, i1480->dev, i1480->cmd_buf, result);
+ return result;
+}
+
+
+/**
+ * Callback for reads on the notification/event endpoint
+ *
+ * Just enables the completion read handler.
+ */
+static
+void i1480_usb_neep_cb(struct urb *urb)
+{
+ struct i1480 *i1480 = urb->context;
+ struct device *dev = i1480->dev;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ECONNRESET: /* Not an error, but a controlled situation; */
+ case -ENOENT: /* (we killed the URB)...so, no broadcast */
+ dev_dbg(dev, "NEEP: reset/noent %d\n", urb->status);
+ break;
+ case -ESHUTDOWN: /* going away! */
+ dev_dbg(dev, "NEEP: down %d\n", urb->status);
+ break;
+ default:
+ dev_err(dev, "NEEP: unknown status %d\n", urb->status);
+ break;
+ }
+ i1480->evt_result = urb->actual_length;
+ complete(&i1480->evt_complete);
+ return;
+}
+
+
+/**
+ * Wait for the MAC FW to initialize
+ *
+ * MAC FW sends a 0xfd/0101/00 notification to EP1 when done
+ * initializing. Get that notification into i1480->evt_buf; upper layer
+ * will verify it.
+ *
+ * Set i1480->evt_result with the result of getting the event or its
+ * size (if succesful).
+ *
+ * Delivers the data directly to i1480->evt_buf
+ */
+static
+int i1480_usb_wait_init_done(struct i1480 *i1480)
+{
+ int result;
+ struct device *dev = i1480->dev;
+ struct i1480_usb *i1480_usb = container_of(i1480, struct i1480_usb, i1480);
+ struct usb_endpoint_descriptor *epd;
+
+ d_fnstart(3, dev, "(%p)\n", i1480);
+ init_completion(&i1480->evt_complete);
+ i1480->evt_result = -EINPROGRESS;
+ epd = &i1480_usb->usb_iface->cur_altsetting->endpoint[0].desc;
+ usb_fill_int_urb(i1480_usb->neep_urb, i1480_usb->usb_dev,
+ usb_rcvintpipe(i1480_usb->usb_dev, epd->bEndpointAddress),
+ i1480->evt_buf, i1480->buf_size,
+ i1480_usb_neep_cb, i1480, epd->bInterval);
+ result = usb_submit_urb(i1480_usb->neep_urb, GFP_KERNEL);
+ if (result < 0) {
+ dev_err(dev, "init done: cannot submit NEEP read: %d\n",
+ result);
+ goto error_submit;
+ }
+ /* Wait for the USB callback to get the data */
+ result = wait_for_completion_interruptible_timeout(
+ &i1480->evt_complete, HZ);
+ if (result <= 0) {
+ result = result == 0? -ETIMEDOUT : result;
+ goto error_wait;
+ }
+ usb_kill_urb(i1480_usb->neep_urb);
+ d_fnend(3, dev, "(%p) = 0\n", i1480);
+ return 0;
+
+error_wait:
+ usb_kill_urb(i1480_usb->neep_urb);
+error_submit:
+ i1480->evt_result = result;
+ d_fnend(3, dev, "(%p) = %d\n", i1480, result);
+ return result;
+}
+
+
+/**
+ * Generic function for issuing commands to the i1480
+ *
+ * @i1480: i1480 instance
+ * @cmd_name: Name of the command (for error messages)
+ * @cmd: Pointer to command buffer
+ * @cmd_size: Size of the command buffer
+ * @reply: Buffer for the reply event
+ * @reply_size: Expected size back (including RCEB); the reply buffer
+ * is assumed to be as big as this.
+ * @returns: >= 0 size of the returned event data if ok,
+ * < 0 errno code on error.
+ *
+ * Arms the NE handle, issues the command to the device and checks the
+ * basics of the reply event.
+ */
+static
+int i1480_usb_cmd(struct i1480 *i1480, const char *cmd_name, size_t cmd_size)
+{
+ int result;
+ struct device *dev = i1480->dev;
+ struct i1480_usb *i1480_usb = container_of(i1480, struct i1480_usb, i1480);
+ struct usb_endpoint_descriptor *epd;
+ struct uwb_rccb *cmd = i1480->cmd_buf;
+ u8 iface_no;
+
+ d_fnstart(3, dev, "(%p, %s, %zu)\n", i1480, cmd_name, cmd_size);
+ /* Post a read on the notification & event endpoint */
+ iface_no = i1480_usb->usb_iface->cur_altsetting->desc.bInterfaceNumber;
+ epd = &i1480_usb->usb_iface->cur_altsetting->endpoint[0].desc;
+ usb_fill_int_urb(
+ i1480_usb->neep_urb, i1480_usb->usb_dev,
+ usb_rcvintpipe(i1480_usb->usb_dev, epd->bEndpointAddress),
+ i1480->evt_buf, i1480->buf_size,
+ i1480_usb_neep_cb, i1480, epd->bInterval);
+ result = usb_submit_urb(i1480_usb->neep_urb, GFP_KERNEL);
+ if (result < 0) {
+ dev_err(dev, "%s: cannot submit NEEP read: %d\n",
+ cmd_name, result);
+ goto error_submit_ep1;
+ }
+ /* Now post the command on EP0 */
+ result = usb_control_msg(
+ i1480_usb->usb_dev, usb_sndctrlpipe(i1480_usb->usb_dev, 0),
+ WA_EXEC_RC_CMD,
+ USB_DIR_OUT | USB_RECIP_INTERFACE | USB_TYPE_CLASS,
+ 0, iface_no,
+ cmd, cmd_size,
+ 100 /* FIXME: this is totally arbitrary */);
+ if (result < 0) {
+ dev_err(dev, "%s: control request failed: %d\n",
+ cmd_name, result);
+ goto error_submit_ep0;
+ }
+ d_fnend(3, dev, "(%p, %s, %zu) = %d\n",
+ i1480, cmd_name, cmd_size, result);
+ return result;
+
+error_submit_ep0:
+ usb_kill_urb(i1480_usb->neep_urb);
+error_submit_ep1:
+ d_fnend(3, dev, "(%p, %s, %zu) = %d\n",
+ i1480, cmd_name, cmd_size, result);
+ return result;
+}
+
+
+/*
+ * Probe a i1480 device for uploading firmware.
+ *
+ * We attach only to interface #0, which is the radio control interface.
+ */
+static
+int i1480_usb_probe(struct usb_interface *iface, const struct usb_device_id *id)
+{
+ struct i1480_usb *i1480_usb;
+ struct i1480 *i1480;
+ struct device *dev = &iface->dev;
+ int result;
+
+ result = -ENODEV;
+ if (iface->cur_altsetting->desc.bInterfaceNumber != 0) {
+ dev_dbg(dev, "not attaching to iface %d\n",
+ iface->cur_altsetting->desc.bInterfaceNumber);
+ goto error;
+ }
+ if (iface->num_altsetting > 1
+ && interface_to_usbdev(iface)->descriptor.idProduct == 0xbabe) {
+ /* Need altsetting #1 [HW QUIRK] or EP1 won't work */
+ result = usb_set_interface(interface_to_usbdev(iface), 0, 1);
+ if (result < 0)
+ dev_warn(dev,
+ "can't set altsetting 1 on iface 0: %d\n",
+ result);
+ }
+
+ result = -ENOMEM;
+ i1480_usb = kzalloc(sizeof(*i1480_usb), GFP_KERNEL);
+ if (i1480_usb == NULL) {
+ dev_err(dev, "Unable to allocate instance\n");
+ goto error;
+ }
+ i1480_usb_init(i1480_usb);
+
+ i1480 = &i1480_usb->i1480;
+ i1480->buf_size = 512;
+ i1480->cmd_buf = kmalloc(2 * i1480->buf_size, GFP_KERNEL);
+ if (i1480->cmd_buf == NULL) {
+ dev_err(dev, "Cannot allocate transfer buffers\n");
+ result = -ENOMEM;
+ goto error_buf_alloc;
+ }
+ i1480->evt_buf = i1480->cmd_buf + i1480->buf_size;
+
+ result = i1480_usb_create(i1480_usb, iface);
+ if (result < 0) {
+ dev_err(dev, "Cannot create instance: %d\n", result);
+ goto error_create;
+ }
+
+ /* setup the fops and upload the firmare */
+ i1480->pre_fw_name = "i1480-pre-phy-0.0.bin";
+ i1480->mac_fw_name = "i1480-usb-0.0.bin";
+ i1480->mac_fw_name_deprecate = "ptc-0.0.bin";
+ i1480->phy_fw_name = "i1480-phy-0.0.bin";
+ i1480->dev = &iface->dev;
+ i1480->write = i1480_usb_write;
+ i1480->read = i1480_usb_read;
+ i1480->rc_setup = NULL;
+ i1480->wait_init_done = i1480_usb_wait_init_done;
+ i1480->cmd = i1480_usb_cmd;
+
+ result = i1480_fw_upload(&i1480_usb->i1480); /* the real thing */
+ if (result >= 0) {
+ usb_reset_device(i1480_usb->usb_dev);
+ result = -ENODEV; /* we don't want to bind to the iface */
+ }
+ i1480_usb_destroy(i1480_usb);
+error_create:
+ kfree(i1480->cmd_buf);
+error_buf_alloc:
+ kfree(i1480_usb);
+error:
+ return result;
+}
+
+#define i1480_USB_DEV(v, p) \
+{ \
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE \
+ | USB_DEVICE_ID_MATCH_DEV_INFO \
+ | USB_DEVICE_ID_MATCH_INT_INFO, \
+ .idVendor = (v), \
+ .idProduct = (p), \
+ .bDeviceClass = 0xff, \
+ .bDeviceSubClass = 0xff, \
+ .bDeviceProtocol = 0xff, \
+ .bInterfaceClass = 0xff, \
+ .bInterfaceSubClass = 0xff, \
+ .bInterfaceProtocol = 0xff, \
+}
+
+
+/** USB device ID's that we handle */
+static struct usb_device_id i1480_usb_id_table[] = {
+ i1480_USB_DEV(0x8086, 0xdf3b),
+ i1480_USB_DEV(0x15a9, 0x0005),
+ i1480_USB_DEV(0x07d1, 0x3802),
+ i1480_USB_DEV(0x050d, 0x305a),
+ i1480_USB_DEV(0x3495, 0x3007),
+ {},
+};
+MODULE_DEVICE_TABLE(usb, i1480_usb_id_table);
+
+
+static struct usb_driver i1480_dfu_driver = {
+ .name = "i1480-dfu-usb",
+ .id_table = i1480_usb_id_table,
+ .probe = i1480_usb_probe,
+ .disconnect = NULL,
+};
+
+
+/*
+ * Initialize the i1480 DFU driver.
+ *
+ * We also need to register our function for guessing event sizes.
+ */
+static int __init i1480_dfu_driver_init(void)
+{
+ return usb_register(&i1480_dfu_driver);
+}
+module_init(i1480_dfu_driver_init);
+
+
+static void __exit i1480_dfu_driver_exit(void)
+{
+ usb_deregister(&i1480_dfu_driver);
+}
+module_exit(i1480_dfu_driver_exit);
+
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("Intel Wireless UWB Link 1480 firmware uploader for USB");
+MODULE_LICENSE("GPL");
diff --git a/drivers/uwb/i1480/i1480-est.c b/drivers/uwb/i1480/i1480-est.c
new file mode 100644
index 000000000000..7bf8c6febae7
--- /dev/null
+++ b/drivers/uwb/i1480/i1480-est.c
@@ -0,0 +1,99 @@
+/*
+ * Intel Wireless UWB Link 1480
+ * Event Size tables for Wired Adaptors
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/uwb.h>
+#include "dfu/i1480-dfu.h"
+
+
+/** Event size table for wEvents 0x00XX */
+static struct uwb_est_entry i1480_est_fd00[] = {
+ /* Anybody expecting this response has to use
+ * neh->extra_size to specify the real size that will
+ * come back. */
+ [i1480_EVT_CONFIRM] = { .size = sizeof(struct i1480_evt_confirm) },
+ [i1480_CMD_SET_IP_MAS] = { .size = sizeof(struct i1480_evt_confirm) },
+#ifdef i1480_RCEB_EXTENDED
+ [0x09] = {
+ .size = sizeof(struct i1480_rceb),
+ .offset = 1 + offsetof(struct i1480_rceb, wParamLength),
+ },
+#endif
+};
+
+/** Event size table for wEvents 0x01XX */
+static struct uwb_est_entry i1480_est_fd01[] = {
+ [0xff & i1480_EVT_RM_INIT_DONE] = { .size = sizeof(struct i1480_rceb) },
+ [0xff & i1480_EVT_DEV_ADD] = { .size = sizeof(struct i1480_rceb) + 9 },
+ [0xff & i1480_EVT_DEV_RM] = { .size = sizeof(struct i1480_rceb) + 9 },
+ [0xff & i1480_EVT_DEV_ID_CHANGE] = {
+ .size = sizeof(struct i1480_rceb) + 2 },
+};
+
+static int i1480_est_init(void)
+{
+ int result = uwb_est_register(i1480_CET_VS1, 0x00, 0x8086, 0x0c3b,
+ i1480_est_fd00,
+ ARRAY_SIZE(i1480_est_fd00));
+ if (result < 0) {
+ printk(KERN_ERR "Can't register EST table fd00: %d\n", result);
+ return result;
+ }
+ result = uwb_est_register(i1480_CET_VS1, 0x01, 0x8086, 0x0c3b,
+ i1480_est_fd01, ARRAY_SIZE(i1480_est_fd01));
+ if (result < 0) {
+ printk(KERN_ERR "Can't register EST table fd01: %d\n", result);
+ return result;
+ }
+ return 0;
+}
+module_init(i1480_est_init);
+
+static void i1480_est_exit(void)
+{
+ uwb_est_unregister(i1480_CET_VS1, 0x00, 0x8086, 0x0c3b,
+ i1480_est_fd00, ARRAY_SIZE(i1480_est_fd00));
+ uwb_est_unregister(i1480_CET_VS1, 0x01, 0x8086, 0x0c3b,
+ i1480_est_fd01, ARRAY_SIZE(i1480_est_fd01));
+}
+module_exit(i1480_est_exit);
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("i1480's Vendor Specific Event Size Tables");
+MODULE_LICENSE("GPL");
+
+/**
+ * USB device ID's that we handle
+ *
+ * [so we are loaded when this kind device is connected]
+ */
+static struct usb_device_id i1480_est_id_table[] = {
+ { USB_DEVICE(0x8086, 0xdf3b), },
+ { USB_DEVICE(0x8086, 0x0c3b), },
+ { },
+};
+MODULE_DEVICE_TABLE(usb, i1480_est_id_table);
diff --git a/drivers/uwb/i1480/i1480-wlp.h b/drivers/uwb/i1480/i1480-wlp.h
new file mode 100644
index 000000000000..37b67e1a6c32
--- /dev/null
+++ b/drivers/uwb/i1480/i1480-wlp.h
@@ -0,0 +1,155 @@
+/*
+ * Intel 1480 Wireless UWB Link
+ * WLP specific definitions
+ *
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ */
+
+#ifndef __i1480_wlp_h__
+#define __i1480_wlp_h__
+
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/uwb.h>
+#include <linux/if_ether.h>
+
+/* New simplified header format? */
+#undef WLP_HDR_FMT_2 /* FIXME: rename */
+
+/**
+ * Values of the Delivery ID & Type field when PCA or DRP
+ *
+ * The Delivery ID & Type field in the WLP TX header indicates whether
+ * the frame is PCA or DRP. This is done based on the high level bit of
+ * this field.
+ * We use this constant to test if the traffic is PCA or DRP as follows:
+ * if (wlp_tx_hdr->delivery_id_type & WLP_DRP)
+ * this is DRP traffic
+ * else
+ * this is PCA traffic
+ */
+enum deliver_id_type_bit {
+ WLP_DRP = 8,
+};
+
+/**
+ * WLP TX header
+ *
+ * Indicates UWB/WLP-specific transmission parameters for a network
+ * packet.
+ */
+struct wlp_tx_hdr {
+ /* dword 0 */
+ struct uwb_dev_addr dstaddr;
+ u8 key_index;
+ DECL_BF_LE3(
+ u8 delivery_id_type:4,
+ enum uwb_ack_pol ack_pol:3,
+ u8 rts_cts:1
+ ) __attribute__((packed));
+ /* dword 1 */
+ DECL_BF_LE2(
+ enum uwb_phy_rate phy_rate:4,
+ s8 tx_power_ctl:4
+ ) __attribute__((packed));
+#ifndef WLP_HDR_FMT_2
+ u8 reserved;
+ __le16 oui01; /* FIXME: not so sure if __le16 or u8[2] */
+ /* dword 2 */
+ u8 oui2; /* if all LE, it could be merged */
+ __le16 prid;
+#endif
+} __attribute__((packed));
+
+
+/**
+ * WLP RX header
+ *
+ * Provides UWB/WLP-specific transmission data for a received
+ * network packet.
+ */
+struct wlp_rx_hdr {
+ /* dword 0 */
+ struct uwb_dev_addr dstaddr;
+ struct uwb_dev_addr srcaddr;
+ /* dword 1 */
+ u8 LQI;
+ s8 RSSI;
+ u8 reserved3;
+#ifndef WLP_HDR_FMT_2
+ u8 oui0;
+ /* dword 2 */
+ __le16 oui12;
+ __le16 prid;
+#endif
+} __attribute__((packed));
+
+
+/** User configurable options for WLP */
+struct wlp_options {
+ struct mutex mutex; /* access to user configurable options*/
+ struct wlp_tx_hdr def_tx_hdr; /* default tx hdr */
+ u8 pca_base_priority;
+ u8 bw_alloc; /*index into bw_allocs[] for PCA/DRP reservations*/
+};
+
+
+static inline
+void wlp_options_init(struct wlp_options *options)
+{
+ mutex_init(&options->mutex);
+ options->def_tx_hdr.ack_pol = UWB_ACK_INM;
+ options->def_tx_hdr.rts_cts = 1;
+ /* FIXME: default to phy caps */
+ options->def_tx_hdr.phy_rate = UWB_PHY_RATE_480;
+#ifndef WLP_HDR_FMT_2
+ options->def_tx_hdr.prid = cpu_to_le16(0x0000);
+#endif
+}
+
+
+/* sysfs helpers */
+
+extern ssize_t uwb_pca_base_priority_store(struct wlp_options *,
+ const char *, size_t);
+extern ssize_t uwb_pca_base_priority_show(const struct wlp_options *, char *);
+extern ssize_t uwb_bw_alloc_store(struct wlp_options *, const char *, size_t);
+extern ssize_t uwb_bw_alloc_show(const struct wlp_options *, char *);
+extern ssize_t uwb_ack_policy_store(struct wlp_options *,
+ const char *, size_t);
+extern ssize_t uwb_ack_policy_show(const struct wlp_options *, char *);
+extern ssize_t uwb_rts_cts_store(struct wlp_options *, const char *, size_t);
+extern ssize_t uwb_rts_cts_show(const struct wlp_options *, char *);
+extern ssize_t uwb_phy_rate_store(struct wlp_options *, const char *, size_t);
+extern ssize_t uwb_phy_rate_show(const struct wlp_options *, char *);
+
+
+/** Simple bandwidth allocation (temporary and too simple) */
+struct wlp_bw_allocs {
+ const char *name;
+ struct {
+ u8 mask, stream;
+ } tx, rx;
+};
+
+
+#endif /* #ifndef __i1480_wlp_h__ */
diff --git a/drivers/uwb/i1480/i1480u-wlp/Makefile b/drivers/uwb/i1480/i1480u-wlp/Makefile
new file mode 100644
index 000000000000..fe6709b8e68b
--- /dev/null
+++ b/drivers/uwb/i1480/i1480u-wlp/Makefile
@@ -0,0 +1,8 @@
+obj-$(CONFIG_UWB_I1480U_WLP) += i1480u-wlp.o
+
+i1480u-wlp-objs := \
+ lc.o \
+ netdev.o \
+ rx.o \
+ sysfs.o \
+ tx.o
diff --git a/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h b/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h
new file mode 100644
index 000000000000..d51cb9364c92
--- /dev/null
+++ b/drivers/uwb/i1480/i1480u-wlp/i1480u-wlp.h
@@ -0,0 +1,269 @@
+/*
+ * Intel 1480 Wireless UWB Link USB
+ * Header formats, constants, general internal interfaces
+ *
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * This is not an standard interface.
+ *
+ * FIXME: docs
+ *
+ * i1480u-wlp is pretty simple: two endpoints, one for tx, one for
+ * rx. rx is polled. Network packets (ethernet, whatever) are wrapped
+ * in i1480 TX or RX headers (for sending over the air), and these
+ * packets are wrapped in UNTD headers (for sending to the WLP UWB
+ * controller).
+ *
+ * UNTD packets (UNTD hdr + i1480 hdr + network packet) packets
+ * cannot be bigger than i1480u_MAX_FRG_SIZE. When this happens, the
+ * i1480 packet is broken in chunks/packets:
+ *
+ * UNTD-1st.hdr + i1480.hdr + payload
+ * UNTD-next.hdr + payload
+ * ...
+ * UNTD-last.hdr + payload
+ *
+ * so that each packet is smaller or equal than i1480u_MAX_FRG_SIZE.
+ *
+ * All HW structures and bitmaps are little endian, so we need to play
+ * ugly tricks when defining bitfields. Hoping for the day GCC
+ * implements __attribute__((endian(1234))).
+ *
+ * FIXME: ROADMAP to the whole implementation
+ */
+
+#ifndef __i1480u_wlp_h__
+#define __i1480u_wlp_h__
+
+#include <linux/usb.h>
+#include <linux/netdevice.h>
+#include <linux/uwb.h> /* struct uwb_rc, struct uwb_notifs_handler */
+#include <linux/wlp.h>
+#include "../i1480-wlp.h"
+
+#undef i1480u_FLOW_CONTROL /* Enable flow control code */
+
+/**
+ * Basic flow control
+ */
+enum {
+ i1480u_TX_INFLIGHT_MAX = 1000,
+ i1480u_TX_INFLIGHT_THRESHOLD = 100,
+};
+
+/** Maximum size of a transaction that we can tx/rx */
+enum {
+ /* Maximum packet size computed as follows: max UNTD header (8) +
+ * i1480 RX header (8) + max Ethernet header and payload (4096) +
+ * Padding added by skb_reserve (2) to make post Ethernet payload
+ * start on 16 byte boundary*/
+ i1480u_MAX_RX_PKT_SIZE = 4114,
+ i1480u_MAX_FRG_SIZE = 512,
+ i1480u_RX_BUFS = 9,
+};
+
+
+/**
+ * UNTD packet type
+ *
+ * We need to fragment any payload whose UNTD packet is going to be
+ * bigger than i1480u_MAX_FRG_SIZE.
+ */
+enum i1480u_pkt_type {
+ i1480u_PKT_FRAG_1ST = 0x1,
+ i1480u_PKT_FRAG_NXT = 0x0,
+ i1480u_PKT_FRAG_LST = 0x2,
+ i1480u_PKT_FRAG_CMP = 0x3
+};
+enum {
+ i1480u_PKT_NONE = 0x4,
+};
+
+/** USB Network Transfer Descriptor - common */
+struct untd_hdr {
+ DECL_BF_LE4(
+ enum i1480u_pkt_type type:2,
+ u8 rx_tx:1, /* !0 == rx */
+ u8 reserved1:2,
+ u8 reserved2:3
+ ) __attribute__((packed));
+ __le16 len;
+} __attribute__((packed));
+
+
+/**
+ * USB Network Transfer Descriptor - Complete Packet
+ *
+ * This is for a packet that is smaller (header + payload) than
+ * i1480u_MAX_FRG_SIZE.
+ *
+ * @hdr.total_len is the size of the payload; the payload doesn't
+ * count this header nor the padding, but includes the size of i1480
+ * header.
+ */
+struct untd_hdr_cmp {
+ struct untd_hdr hdr;
+ u8 padding;
+} __attribute__((packed));
+
+
+/**
+ * USB Network Transfer Descriptor - First fragment
+ *
+ * @hdr.len is the size of the *whole packet* (excluding UNTD
+ * headers); @fragment_len is the size of the payload (excluding UNTD
+ * headers, but including i1480 headers).
+ */
+struct untd_hdr_1st {
+ struct untd_hdr hdr;
+ __le16 fragment_len;
+ u8 padding[3];
+} __attribute__((packed));
+
+
+/**
+ * USB Network Transfer Descriptor - Next / Last [Rest]
+ *
+ * @hdr.len is the size of the payload, not including headrs.
+ */
+struct untd_hdr_rst {
+ struct untd_hdr hdr;
+ u8 padding;
+} __attribute__((packed));
+
+
+/**
+ * Transmission context
+ *
+ * Wraps all the stuff needed to track a pending/active tx
+ * operation.
+ */
+struct i1480u_tx {
+ struct list_head list_node;
+ struct i1480u *i1480u;
+ struct urb *urb;
+
+ struct sk_buff *skb;
+ struct wlp_tx_hdr *wlp_tx_hdr;
+
+ void *buf; /* if NULL, no new buf was used */
+ size_t buf_size;
+};
+
+/**
+ * Basic flow control
+ *
+ * We maintain a basic flow control counter. "count" how many TX URBs are
+ * outstanding. Only allow "max"
+ * TX URBs to be outstanding. If this value is reached the queue will be
+ * stopped. The queue will be restarted when there are
+ * "threshold" URBs outstanding.
+ * Maintain a counter of how many time the TX queue needed to be restarted
+ * due to the "max" being exceeded and the "threshold" reached again. The
+ * timestamp "restart_ts" is to keep track from when the counter was last
+ * queried (see sysfs handling of file wlp_tx_inflight).
+ */
+struct i1480u_tx_inflight {
+ atomic_t count;
+ unsigned long max;
+ unsigned long threshold;
+ unsigned long restart_ts;
+ atomic_t restart_count;
+};
+
+/**
+ * Instance of a i1480u WLP interface
+ *
+ * Keeps references to the USB device that wraps it, as well as it's
+ * interface and associated UWB host controller. As well, it also
+ * keeps a link to the netdevice for integration into the networking
+ * stack.
+ * We maintian separate error history for the tx and rx endpoints because
+ * the implementation does not rely on locking - having one shared
+ * structure between endpoints may cause problems. Adding locking to the
+ * implementation will have higher cost than adding a separate structure.
+ */
+struct i1480u {
+ struct usb_device *usb_dev;
+ struct usb_interface *usb_iface;
+ struct net_device *net_dev;
+
+ spinlock_t lock;
+ struct net_device_stats stats;
+
+ /* RX context handling */
+ struct sk_buff *rx_skb;
+ struct uwb_dev_addr rx_srcaddr;
+ size_t rx_untd_pkt_size;
+ struct i1480u_rx_buf {
+ struct i1480u *i1480u; /* back pointer */
+ struct urb *urb;
+ struct sk_buff *data; /* i1480u_MAX_RX_PKT_SIZE each */
+ } rx_buf[i1480u_RX_BUFS]; /* N bufs */
+
+ spinlock_t tx_list_lock; /* TX context */
+ struct list_head tx_list;
+ u8 tx_stream;
+
+ struct stats lqe_stats, rssi_stats; /* radio statistics */
+
+ /* Options we can set from sysfs */
+ struct wlp_options options;
+ struct uwb_notifs_handler uwb_notifs_handler;
+ struct edc tx_errors;
+ struct edc rx_errors;
+ struct wlp wlp;
+#ifdef i1480u_FLOW_CONTROL
+ struct urb *notif_urb;
+ struct edc notif_edc; /* error density counter */
+ u8 notif_buffer[1];
+#endif
+ struct i1480u_tx_inflight tx_inflight;
+};
+
+/* Internal interfaces */
+extern void i1480u_rx_cb(struct urb *urb);
+extern int i1480u_rx_setup(struct i1480u *);
+extern void i1480u_rx_release(struct i1480u *);
+extern void i1480u_tx_release(struct i1480u *);
+extern int i1480u_xmit_frame(struct wlp *, struct sk_buff *,
+ struct uwb_dev_addr *);
+extern void i1480u_stop_queue(struct wlp *);
+extern void i1480u_start_queue(struct wlp *);
+extern int i1480u_sysfs_setup(struct i1480u *);
+extern void i1480u_sysfs_release(struct i1480u *);
+
+/* netdev interface */
+extern int i1480u_open(struct net_device *);
+extern int i1480u_stop(struct net_device *);
+extern int i1480u_hard_start_xmit(struct sk_buff *, struct net_device *);
+extern void i1480u_tx_timeout(struct net_device *);
+extern int i1480u_set_config(struct net_device *, struct ifmap *);
+extern struct net_device_stats *i1480u_get_stats(struct net_device *);
+extern int i1480u_change_mtu(struct net_device *, int);
+extern void i1480u_uwb_notifs_cb(void *, struct uwb_dev *, enum uwb_notifs);
+
+/* bandwidth allocation callback */
+extern void i1480u_bw_alloc_cb(struct uwb_rsv *);
+
+/* Sys FS */
+extern struct attribute_group i1480u_wlp_attr_group;
+
+#endif /* #ifndef __i1480u_wlp_h__ */
diff --git a/drivers/uwb/i1480/i1480u-wlp/lc.c b/drivers/uwb/i1480/i1480u-wlp/lc.c
new file mode 100644
index 000000000000..8341d877407b
--- /dev/null
+++ b/drivers/uwb/i1480/i1480u-wlp/lc.c
@@ -0,0 +1,421 @@
+/*
+ * WUSB Wire Adapter: WLP interface
+ * Driver for the Linux Network stack.
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ *
+ * This implements a very simple network driver for the WLP USB
+ * device that is associated to a UWB (Ultra Wide Band) host.
+ *
+ * This is seen as an interface of a composite device. Once the UWB
+ * host has an association to another WLP capable device, the
+ * networking interface (aka WLP) can start to send packets back and
+ * forth.
+ *
+ * Limitations:
+ *
+ * - Hand cranked; can't ifup the interface until there is an association
+ *
+ * - BW allocation very simplistic [see i1480u_mas_set() and callees].
+ *
+ *
+ * ROADMAP:
+ *
+ * ENTRY POINTS (driver model):
+ *
+ * i1480u_driver_{exit,init}(): initialization of the driver.
+ *
+ * i1480u_probe(): called by the driver code when a device
+ * matching 'i1480u_id_table' is connected.
+ *
+ * This allocs a netdev instance, inits with
+ * i1480u_add(), then registers_netdev().
+ * i1480u_init()
+ * i1480u_add()
+ *
+ * i1480u_disconnect(): device has been disconnected/module
+ * is being removed.
+ * i1480u_rm()
+ */
+#include <linux/version.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/uwb/debug.h>
+#include "i1480u-wlp.h"
+
+
+
+static inline
+void i1480u_init(struct i1480u *i1480u)
+{
+ /* nothing so far... doesn't it suck? */
+ spin_lock_init(&i1480u->lock);
+ INIT_LIST_HEAD(&i1480u->tx_list);
+ spin_lock_init(&i1480u->tx_list_lock);
+ wlp_options_init(&i1480u->options);
+ edc_init(&i1480u->tx_errors);
+ edc_init(&i1480u->rx_errors);
+#ifdef i1480u_FLOW_CONTROL
+ edc_init(&i1480u->notif_edc);
+#endif
+ stats_init(&i1480u->lqe_stats);
+ stats_init(&i1480u->rssi_stats);
+ wlp_init(&i1480u->wlp);
+}
+
+/**
+ * Fill WLP device information structure
+ *
+ * The structure will contain a few character arrays, each ending with a
+ * null terminated string. Each string has to fit (excluding terminating
+ * character) into a specified range obtained from the WLP substack.
+ *
+ * It is still not clear exactly how this device information should be
+ * obtained. Until we find out we use the USB device descriptor as backup, some
+ * information elements have intuitive mappings, other not.
+ */
+static
+void i1480u_fill_device_info(struct wlp *wlp, struct wlp_device_info *dev_info)
+{
+ struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp);
+ struct usb_device *usb_dev = i1480u->usb_dev;
+ /* Treat device name and model name the same */
+ if (usb_dev->descriptor.iProduct) {
+ usb_string(usb_dev, usb_dev->descriptor.iProduct,
+ dev_info->name, sizeof(dev_info->name));
+ usb_string(usb_dev, usb_dev->descriptor.iProduct,
+ dev_info->model_name, sizeof(dev_info->model_name));
+ }
+ if (usb_dev->descriptor.iManufacturer)
+ usb_string(usb_dev, usb_dev->descriptor.iManufacturer,
+ dev_info->manufacturer,
+ sizeof(dev_info->manufacturer));
+ scnprintf(dev_info->model_nr, sizeof(dev_info->model_nr), "%04x",
+ __le16_to_cpu(usb_dev->descriptor.bcdDevice));
+ if (usb_dev->descriptor.iSerialNumber)
+ usb_string(usb_dev, usb_dev->descriptor.iSerialNumber,
+ dev_info->serial, sizeof(dev_info->serial));
+ /* FIXME: where should we obtain category? */
+ dev_info->prim_dev_type.category = cpu_to_le16(WLP_DEV_CAT_OTHER);
+ /* FIXME: Complete OUI and OUIsubdiv attributes */
+}
+
+#ifdef i1480u_FLOW_CONTROL
+/**
+ * Callback for the notification endpoint
+ *
+ * This mostly controls the xon/xoff protocol. In case of hard error,
+ * we stop the queue. If not, we always retry.
+ */
+static
+void i1480u_notif_cb(struct urb *urb, struct pt_regs *regs)
+{
+ struct i1480u *i1480u = urb->context;
+ struct usb_interface *usb_iface = i1480u->usb_iface;
+ struct device *dev = &usb_iface->dev;
+ int result;
+
+ switch (urb->status) {
+ case 0: /* Got valid data, do xon/xoff */
+ switch (i1480u->notif_buffer[0]) {
+ case 'N':
+ dev_err(dev, "XOFF STOPPING queue at %lu\n", jiffies);
+ netif_stop_queue(i1480u->net_dev);
+ break;
+ case 'A':
+ dev_err(dev, "XON STARTING queue at %lu\n", jiffies);
+ netif_start_queue(i1480u->net_dev);
+ break;
+ default:
+ dev_err(dev, "NEP: unknown data 0x%02hhx\n",
+ i1480u->notif_buffer[0]);
+ }
+ break;
+ case -ECONNRESET: /* Controlled situation ... */
+ case -ENOENT: /* we killed the URB... */
+ dev_err(dev, "NEP: URB reset/noent %d\n", urb->status);
+ goto error;
+ case -ESHUTDOWN: /* going away! */
+ dev_err(dev, "NEP: URB down %d\n", urb->status);
+ goto error;
+ default: /* Retry unless it gets ugly */
+ if (edc_inc(&i1480u->notif_edc, EDC_MAX_ERRORS,
+ EDC_ERROR_TIMEFRAME)) {
+ dev_err(dev, "NEP: URB max acceptable errors "
+ "exceeded; resetting device\n");
+ goto error_reset;
+ }
+ dev_err(dev, "NEP: URB error %d\n", urb->status);
+ break;
+ }
+ result = usb_submit_urb(urb, GFP_ATOMIC);
+ if (result < 0) {
+ dev_err(dev, "NEP: Can't resubmit URB: %d; resetting device\n",
+ result);
+ goto error_reset;
+ }
+ return;
+
+error_reset:
+ usb_dev_reset_delayed(i1480u->usb_dev);
+error:
+ netif_stop_queue(i1480u->net_dev);
+ return;
+}
+#endif
+
+static
+int i1480u_add(struct i1480u *i1480u, struct usb_interface *iface)
+{
+ int result = -ENODEV;
+ struct wlp *wlp = &i1480u->wlp;
+ struct usb_device *usb_dev = interface_to_usbdev(iface);
+ struct net_device *net_dev = i1480u->net_dev;
+ struct uwb_rc *rc;
+ struct uwb_dev *uwb_dev;
+#ifdef i1480u_FLOW_CONTROL
+ struct usb_endpoint_descriptor *epd;
+#endif
+
+ i1480u->usb_dev = usb_get_dev(usb_dev);
+ i1480u->usb_iface = iface;
+ rc = uwb_rc_get_by_grandpa(&i1480u->usb_dev->dev);
+ if (rc == NULL) {
+ dev_err(&iface->dev, "Cannot get associated UWB Radio "
+ "Controller\n");
+ goto out;
+ }
+ wlp->xmit_frame = i1480u_xmit_frame;
+ wlp->fill_device_info = i1480u_fill_device_info;
+ wlp->stop_queue = i1480u_stop_queue;
+ wlp->start_queue = i1480u_start_queue;
+ result = wlp_setup(wlp, rc);
+ if (result < 0) {
+ dev_err(&iface->dev, "Cannot setup WLP\n");
+ goto error_wlp_setup;
+ }
+ result = 0;
+ ether_setup(net_dev); /* make it an etherdevice */
+ uwb_dev = &rc->uwb_dev;
+ /* FIXME: hookup address change notifications? */
+
+ memcpy(net_dev->dev_addr, uwb_dev->mac_addr.data,
+ sizeof(net_dev->dev_addr));
+
+ net_dev->hard_header_len = sizeof(struct untd_hdr_cmp)
+ + sizeof(struct wlp_tx_hdr)
+ + WLP_DATA_HLEN
+ + ETH_HLEN;
+ net_dev->mtu = 3500;
+ net_dev->tx_queue_len = 20; /* FIXME: maybe use 1000? */
+
+/* net_dev->flags &= ~IFF_BROADCAST; FIXME: BUG in firmware */
+ /* FIXME: multicast disabled */
+ net_dev->flags &= ~IFF_MULTICAST;
+ net_dev->features &= ~NETIF_F_SG;
+ net_dev->features &= ~NETIF_F_FRAGLIST;
+ /* All NETIF_F_*_CSUM disabled */
+ net_dev->features |= NETIF_F_HIGHDMA;
+ net_dev->watchdog_timeo = 5*HZ; /* FIXME: a better default? */
+
+ net_dev->open = i1480u_open;
+ net_dev->stop = i1480u_stop;
+ net_dev->hard_start_xmit = i1480u_hard_start_xmit;
+ net_dev->tx_timeout = i1480u_tx_timeout;
+ net_dev->get_stats = i1480u_get_stats;
+ net_dev->set_config = i1480u_set_config;
+ net_dev->change_mtu = i1480u_change_mtu;
+
+#ifdef i1480u_FLOW_CONTROL
+ /* Notification endpoint setup (submitted when we open the device) */
+ i1480u->notif_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (i1480u->notif_urb == NULL) {
+ dev_err(&iface->dev, "Unable to allocate notification URB\n");
+ result = -ENOMEM;
+ goto error_urb_alloc;
+ }
+ epd = &iface->cur_altsetting->endpoint[0].desc;
+ usb_fill_int_urb(i1480u->notif_urb, usb_dev,
+ usb_rcvintpipe(usb_dev, epd->bEndpointAddress),
+ i1480u->notif_buffer, sizeof(i1480u->notif_buffer),
+ i1480u_notif_cb, i1480u, epd->bInterval);
+
+#endif
+
+ i1480u->tx_inflight.max = i1480u_TX_INFLIGHT_MAX;
+ i1480u->tx_inflight.threshold = i1480u_TX_INFLIGHT_THRESHOLD;
+ i1480u->tx_inflight.restart_ts = jiffies;
+ usb_set_intfdata(iface, i1480u);
+ return result;
+
+#ifdef i1480u_FLOW_CONTROL
+error_urb_alloc:
+#endif
+ wlp_remove(wlp);
+error_wlp_setup:
+ uwb_rc_put(rc);
+out:
+ usb_put_dev(i1480u->usb_dev);
+ return result;
+}
+
+static void i1480u_rm(struct i1480u *i1480u)
+{
+ struct uwb_rc *rc = i1480u->wlp.rc;
+ usb_set_intfdata(i1480u->usb_iface, NULL);
+#ifdef i1480u_FLOW_CONTROL
+ usb_kill_urb(i1480u->notif_urb);
+ usb_free_urb(i1480u->notif_urb);
+#endif
+ wlp_remove(&i1480u->wlp);
+ uwb_rc_put(rc);
+ usb_put_dev(i1480u->usb_dev);
+}
+
+/** Just setup @net_dev's i1480u private data */
+static void i1480u_netdev_setup(struct net_device *net_dev)
+{
+ struct i1480u *i1480u = netdev_priv(net_dev);
+ /* Initialize @i1480u */
+ memset(i1480u, 0, sizeof(*i1480u));
+ i1480u_init(i1480u);
+}
+
+/**
+ * Probe a i1480u interface and register it
+ *
+ * @iface: USB interface to link to
+ * @id: USB class/subclass/protocol id
+ * @returns: 0 if ok, < 0 errno code on error.
+ *
+ * Does basic housekeeping stuff and then allocs a netdev with space
+ * for the i1480u data. Initializes, registers in i1480u, registers in
+ * netdev, ready to go.
+ */
+static int i1480u_probe(struct usb_interface *iface,
+ const struct usb_device_id *id)
+{
+ int result;
+ struct net_device *net_dev;
+ struct device *dev = &iface->dev;
+ struct i1480u *i1480u;
+
+ /* Allocate instance [calls i1480u_netdev_setup() on it] */
+ result = -ENOMEM;
+ net_dev = alloc_netdev(sizeof(*i1480u), "wlp%d", i1480u_netdev_setup);
+ if (net_dev == NULL) {
+ dev_err(dev, "no memory for network device instance\n");
+ goto error_alloc_netdev;
+ }
+ SET_NETDEV_DEV(net_dev, dev);
+ i1480u = netdev_priv(net_dev);
+ i1480u->net_dev = net_dev;
+ result = i1480u_add(i1480u, iface); /* Now setup all the wlp stuff */
+ if (result < 0) {
+ dev_err(dev, "cannot add i1480u device: %d\n", result);
+ goto error_i1480u_add;
+ }
+ result = register_netdev(net_dev); /* Okey dokey, bring it up */
+ if (result < 0) {
+ dev_err(dev, "cannot register network device: %d\n", result);
+ goto error_register_netdev;
+ }
+ i1480u_sysfs_setup(i1480u);
+ if (result < 0)
+ goto error_sysfs_init;
+ return 0;
+
+error_sysfs_init:
+ unregister_netdev(net_dev);
+error_register_netdev:
+ i1480u_rm(i1480u);
+error_i1480u_add:
+ free_netdev(net_dev);
+error_alloc_netdev:
+ return result;
+}
+
+
+/**
+ * Disconect a i1480u from the system.
+ *
+ * i1480u_stop() has been called before, so al the rx and tx contexts
+ * have been taken down already. Make sure the queue is stopped,
+ * unregister netdev and i1480u, free and kill.
+ */
+static void i1480u_disconnect(struct usb_interface *iface)
+{
+ struct i1480u *i1480u;
+ struct net_device *net_dev;
+
+ i1480u = usb_get_intfdata(iface);
+ net_dev = i1480u->net_dev;
+ netif_stop_queue(net_dev);
+#ifdef i1480u_FLOW_CONTROL
+ usb_kill_urb(i1480u->notif_urb);
+#endif
+ i1480u_sysfs_release(i1480u);
+ unregister_netdev(net_dev);
+ i1480u_rm(i1480u);
+ free_netdev(net_dev);
+}
+
+static struct usb_device_id i1480u_id_table[] = {
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE \
+ | USB_DEVICE_ID_MATCH_DEV_INFO \
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x8086,
+ .idProduct = 0x0c3b,
+ .bDeviceClass = 0xef,
+ .bDeviceSubClass = 0x02,
+ .bDeviceProtocol = 0x02,
+ .bInterfaceClass = 0xff,
+ .bInterfaceSubClass = 0xff,
+ .bInterfaceProtocol = 0xff,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(usb, i1480u_id_table);
+
+static struct usb_driver i1480u_driver = {
+ .name = KBUILD_MODNAME,
+ .probe = i1480u_probe,
+ .disconnect = i1480u_disconnect,
+ .id_table = i1480u_id_table,
+};
+
+static int __init i1480u_driver_init(void)
+{
+ return usb_register(&i1480u_driver);
+}
+module_init(i1480u_driver_init);
+
+
+static void __exit i1480u_driver_exit(void)
+{
+ usb_deregister(&i1480u_driver);
+}
+module_exit(i1480u_driver_exit);
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("i1480 Wireless UWB Link WLP networking for USB");
+MODULE_LICENSE("GPL");
diff --git a/drivers/uwb/i1480/i1480u-wlp/netdev.c b/drivers/uwb/i1480/i1480u-wlp/netdev.c
new file mode 100644
index 000000000000..66fe07a69c1b
--- /dev/null
+++ b/drivers/uwb/i1480/i1480u-wlp/netdev.c
@@ -0,0 +1,367 @@
+/*
+ * WUSB Wire Adapter: WLP interface
+ * Driver for the Linux Network stack.
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ *
+ * Implementation of the netdevice linkage (except tx and rx related stuff).
+ *
+ * ROADMAP:
+ *
+ * ENTRY POINTS (Net device):
+ *
+ * i1480u_open(): Called when we ifconfig up the interface;
+ * associates to a UWB host controller, reserves
+ * bandwidth (MAS), sets up RX USB URB and starts
+ * the queue.
+ *
+ * i1480u_stop(): Called when we ifconfig down a interface;
+ * reverses _open().
+ *
+ * i1480u_set_config():
+ */
+
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/uwb/debug.h>
+#include "i1480u-wlp.h"
+
+struct i1480u_cmd_set_ip_mas {
+ struct uwb_rccb rccb;
+ struct uwb_dev_addr addr;
+ u8 stream;
+ u8 owner;
+ u8 type; /* enum uwb_drp_type */
+ u8 baMAS[32];
+} __attribute__((packed));
+
+
+static
+int i1480u_set_ip_mas(
+ struct uwb_rc *rc,
+ const struct uwb_dev_addr *dstaddr,
+ u8 stream, u8 owner, u8 type, unsigned long *mas)
+{
+
+ int result;
+ struct i1480u_cmd_set_ip_mas *cmd;
+ struct uwb_rc_evt_confirm reply;
+
+ result = -ENOMEM;
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL)
+ goto error_kzalloc;
+ cmd->rccb.bCommandType = 0xfd;
+ cmd->rccb.wCommand = cpu_to_le16(0x000e);
+ cmd->addr = *dstaddr;
+ cmd->stream = stream;
+ cmd->owner = owner;
+ cmd->type = type;
+ if (mas == NULL)
+ memset(cmd->baMAS, 0x00, sizeof(cmd->baMAS));
+ else
+ memcpy(cmd->baMAS, mas, sizeof(cmd->baMAS));
+ reply.rceb.bEventType = 0xfd;
+ reply.rceb.wEvent = cpu_to_le16(0x000e);
+ result = uwb_rc_cmd(rc, "SET-IP-MAS", &cmd->rccb, sizeof(*cmd),
+ &reply.rceb, sizeof(reply));
+ if (result < 0)
+ goto error_cmd;
+ if (reply.bResultCode != UWB_RC_RES_FAIL) {
+ dev_err(&rc->uwb_dev.dev,
+ "SET-IP-MAS: command execution failed: %d\n",
+ reply.bResultCode);
+ result = -EIO;
+ }
+error_cmd:
+ kfree(cmd);
+error_kzalloc:
+ return result;
+}
+
+/*
+ * Inform a WLP interface of a MAS reservation
+ *
+ * @rc is assumed refcnted.
+ */
+/* FIXME: detect if remote device is WLP capable? */
+static int i1480u_mas_set_dev(struct uwb_dev *uwb_dev, struct uwb_rc *rc,
+ u8 stream, u8 owner, u8 type, unsigned long *mas)
+{
+ int result = 0;
+ struct device *dev = &rc->uwb_dev.dev;
+
+ result = i1480u_set_ip_mas(rc, &uwb_dev->dev_addr, stream, owner,
+ type, mas);
+ if (result < 0) {
+ char rcaddrbuf[UWB_ADDR_STRSIZE], devaddrbuf[UWB_ADDR_STRSIZE];
+ uwb_dev_addr_print(rcaddrbuf, sizeof(rcaddrbuf),
+ &rc->uwb_dev.dev_addr);
+ uwb_dev_addr_print(devaddrbuf, sizeof(devaddrbuf),
+ &uwb_dev->dev_addr);
+ dev_err(dev, "Set IP MAS (%s to %s) failed: %d\n",
+ rcaddrbuf, devaddrbuf, result);
+ }
+ return result;
+}
+
+/**
+ * Called by bandwidth allocator when change occurs in reservation.
+ *
+ * @rsv: The reservation that is being established, modified, or
+ * terminated.
+ *
+ * When a reservation is established, modified, or terminated the upper layer
+ * (WLP here) needs set/update the currently available Media Access Slots
+ * that can be use for IP traffic.
+ *
+ * Our action taken during failure depends on how the reservation is being
+ * changed:
+ * - if reservation is being established we do nothing if we cannot set the
+ * new MAS to be used
+ * - if reservation is being terminated we revert back to PCA whether the
+ * SET IP MAS command succeeds or not.
+ */
+void i1480u_bw_alloc_cb(struct uwb_rsv *rsv)
+{
+ int result = 0;
+ struct i1480u *i1480u = rsv->pal_priv;
+ struct device *dev = &i1480u->usb_iface->dev;
+ struct uwb_dev *target_dev = rsv->target.dev;
+ struct uwb_rc *rc = i1480u->wlp.rc;
+ u8 stream = rsv->stream;
+ int type = rsv->type;
+ int is_owner = rsv->owner == &rc->uwb_dev;
+ unsigned long *bmp = rsv->mas.bm;
+
+ dev_err(dev, "WLP callback called - sending set ip mas\n");
+ /*user cannot change options while setting configuration*/
+ mutex_lock(&i1480u->options.mutex);
+ switch (rsv->state) {
+ case UWB_RSV_STATE_T_ACCEPTED:
+ case UWB_RSV_STATE_O_ESTABLISHED:
+ result = i1480u_mas_set_dev(target_dev, rc, stream, is_owner,
+ type, bmp);
+ if (result < 0) {
+ dev_err(dev, "MAS reservation failed: %d\n", result);
+ goto out;
+ }
+ if (is_owner) {
+ i1480u->options.def_tx_hdr.delivery_id_type = 8 | stream;
+ i1480u->options.def_tx_hdr.rts_cts = 0;
+ }
+ break;
+ case UWB_RSV_STATE_NONE:
+ /* revert back to PCA */
+ result = i1480u_mas_set_dev(target_dev, rc, stream, is_owner,
+ type, bmp);
+ if (result < 0)
+ dev_err(dev, "MAS reservation failed: %d\n", result);
+ /* Revert to PCA even though SET IP MAS failed. */
+ i1480u->options.def_tx_hdr.delivery_id_type =
+ i1480u->options.pca_base_priority;
+ i1480u->options.def_tx_hdr.rts_cts = 1;
+ break;
+ default:
+ dev_err(dev, "unexpected WLP reservation state: %s (%d).\n",
+ uwb_rsv_state_str(rsv->state), rsv->state);
+ break;
+ }
+out:
+ mutex_unlock(&i1480u->options.mutex);
+ return;
+}
+
+/**
+ *
+ * Called on 'ifconfig up'
+ */
+int i1480u_open(struct net_device *net_dev)
+{
+ int result;
+ struct i1480u *i1480u = netdev_priv(net_dev);
+ struct wlp *wlp = &i1480u->wlp;
+ struct uwb_rc *rc;
+ struct device *dev = &i1480u->usb_iface->dev;
+
+ rc = wlp->rc;
+ result = i1480u_rx_setup(i1480u); /* Alloc RX stuff */
+ if (result < 0)
+ goto error_rx_setup;
+ netif_wake_queue(net_dev);
+#ifdef i1480u_FLOW_CONTROL
+ result = usb_submit_urb(i1480u->notif_urb, GFP_KERNEL);;
+ if (result < 0) {
+ dev_err(dev, "Can't submit notification URB: %d\n", result);
+ goto error_notif_urb_submit;
+ }
+#endif
+ i1480u->uwb_notifs_handler.cb = i1480u_uwb_notifs_cb;
+ i1480u->uwb_notifs_handler.data = i1480u;
+ if (uwb_bg_joined(rc))
+ netif_carrier_on(net_dev);
+ else
+ netif_carrier_off(net_dev);
+ uwb_notifs_register(rc, &i1480u->uwb_notifs_handler);
+ /* Interface is up with an address, now we can create WSS */
+ result = wlp_wss_setup(net_dev, &wlp->wss);
+ if (result < 0) {
+ dev_err(dev, "Can't create WSS: %d. \n", result);
+ goto error_notif_deregister;
+ }
+ return 0;
+error_notif_deregister:
+ uwb_notifs_deregister(rc, &i1480u->uwb_notifs_handler);
+#ifdef i1480u_FLOW_CONTROL
+error_notif_urb_submit:
+#endif
+ netif_stop_queue(net_dev);
+ i1480u_rx_release(i1480u);
+error_rx_setup:
+ return result;
+}
+
+
+/**
+ * Called on 'ifconfig down'
+ */
+int i1480u_stop(struct net_device *net_dev)
+{
+ struct i1480u *i1480u = netdev_priv(net_dev);
+ struct wlp *wlp = &i1480u->wlp;
+ struct uwb_rc *rc = wlp->rc;
+
+ BUG_ON(wlp->rc == NULL);
+ wlp_wss_remove(&wlp->wss);
+ uwb_notifs_deregister(rc, &i1480u->uwb_notifs_handler);
+ netif_carrier_off(net_dev);
+#ifdef i1480u_FLOW_CONTROL
+ usb_kill_urb(i1480u->notif_urb);
+#endif
+ netif_stop_queue(net_dev);
+ i1480u_rx_release(i1480u);
+ i1480u_tx_release(i1480u);
+ return 0;
+}
+
+
+/** Report statistics */
+struct net_device_stats *i1480u_get_stats(struct net_device *net_dev)
+{
+ struct i1480u *i1480u = netdev_priv(net_dev);
+ return &i1480u->stats;
+}
+
+
+/**
+ *
+ * Change the interface config--we probably don't have to do anything.
+ */
+int i1480u_set_config(struct net_device *net_dev, struct ifmap *map)
+{
+ int result;
+ struct i1480u *i1480u = netdev_priv(net_dev);
+ BUG_ON(i1480u->wlp.rc == NULL);
+ result = 0;
+ return result;
+}
+
+/**
+ * Change the MTU of the interface
+ */
+int i1480u_change_mtu(struct net_device *net_dev, int mtu)
+{
+ static union {
+ struct wlp_tx_hdr tx;
+ struct wlp_rx_hdr rx;
+ } i1480u_all_hdrs;
+
+ if (mtu < ETH_HLEN) /* We encap eth frames */
+ return -ERANGE;
+ if (mtu > 4000 - sizeof(i1480u_all_hdrs))
+ return -ERANGE;
+ net_dev->mtu = mtu;
+ return 0;
+}
+
+
+/**
+ * Callback function to handle events from UWB
+ * When we see other devices we know the carrier is ok,
+ * if we are the only device in the beacon group we set the carrier
+ * state to off.
+ * */
+void i1480u_uwb_notifs_cb(void *data, struct uwb_dev *uwb_dev,
+ enum uwb_notifs event)
+{
+ struct i1480u *i1480u = data;
+ struct net_device *net_dev = i1480u->net_dev;
+ struct device *dev = &i1480u->usb_iface->dev;
+ switch (event) {
+ case UWB_NOTIF_BG_JOIN:
+ netif_carrier_on(net_dev);
+ dev_info(dev, "Link is up\n");
+ break;
+ case UWB_NOTIF_BG_LEAVE:
+ netif_carrier_off(net_dev);
+ dev_info(dev, "Link is down\n");
+ break;
+ default:
+ dev_err(dev, "don't know how to handle event %d from uwb\n",
+ event);
+ }
+}
+
+/**
+ * Stop the network queue
+ *
+ * Enable WLP substack to stop network queue. We also set the flow control
+ * threshold at this time to prevent the flow control from restarting the
+ * queue.
+ *
+ * we are loosing the current threshold value here ... FIXME?
+ */
+void i1480u_stop_queue(struct wlp *wlp)
+{
+ struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp);
+ struct net_device *net_dev = i1480u->net_dev;
+ i1480u->tx_inflight.threshold = 0;
+ netif_stop_queue(net_dev);
+}
+
+/**
+ * Start the network queue
+ *
+ * Enable WLP substack to start network queue. Also re-enable the flow
+ * control to manage the queue again.
+ *
+ * We re-enable the flow control by storing the default threshold in the
+ * flow control threshold. This means that if the user modified the
+ * threshold before the queue was stopped and restarted that information
+ * will be lost. FIXME?
+ */
+void i1480u_start_queue(struct wlp *wlp)
+{
+ struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp);
+ struct net_device *net_dev = i1480u->net_dev;
+ i1480u->tx_inflight.threshold = i1480u_TX_INFLIGHT_THRESHOLD;
+ netif_start_queue(net_dev);
+}
diff --git a/drivers/uwb/i1480/i1480u-wlp/rx.c b/drivers/uwb/i1480/i1480u-wlp/rx.c
new file mode 100644
index 000000000000..6161772e9f76
--- /dev/null
+++ b/drivers/uwb/i1480/i1480u-wlp/rx.c
@@ -0,0 +1,491 @@
+/*
+ * WUSB Wire Adapter: WLP interface
+ * Driver for the Linux Network stack.
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * i1480u's RX handling is simple. i1480u will send the received
+ * network packets broken up in fragments; 1 to N fragments make a
+ * packet, we assemble them together and deliver the packet with netif_rx().
+ *
+ * Beacuse each USB transfer is a *single* fragment (except when the
+ * transfer contains a first fragment), each URB called thus
+ * back contains one or two fragments. So we queue N URBs, each with its own
+ * fragment buffer. When a URB is done, we process it (adding to the
+ * current skb from the fragment buffer until complete). Once
+ * processed, we requeue the URB. There is always a bunch of URBs
+ * ready to take data, so the intergap should be minimal.
+ *
+ * An URB's transfer buffer is the data field of a socket buffer. This
+ * reduces copying as data can be passed directly to network layer. If a
+ * complete packet or 1st fragment is received the URB's transfer buffer is
+ * taken away from it and used to send data to the network layer. In this
+ * case a new transfer buffer is allocated to the URB before being requeued.
+ * If a "NEXT" or "LAST" fragment is received, the fragment contents is
+ * appended to the RX packet under construction and the transfer buffer
+ * is reused. To be able to use this buffer to assemble complete packets
+ * we set each buffer's size to that of the MAX ethernet packet that can
+ * be received. There is thus room for improvement in memory usage.
+ *
+ * When the max tx fragment size increases, we should be able to read
+ * data into the skbs directly with very simple code.
+ *
+ * ROADMAP:
+ *
+ * ENTRY POINTS:
+ *
+ * i1480u_rx_setup(): setup RX context [from i1480u_open()]
+ *
+ * i1480u_rx_release(): release RX context [from i1480u_stop()]
+ *
+ * i1480u_rx_cb(): called when the RX USB URB receives a
+ * packet. It removes the header and pushes it up
+ * the Linux netdev stack with netif_rx().
+ *
+ * i1480u_rx_buffer()
+ * i1480u_drop() and i1480u_fix()
+ * i1480u_skb_deliver
+ *
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "i1480u-wlp.h"
+
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+
+/**
+ * Setup the RX context
+ *
+ * Each URB is provided with a transfer_buffer that is the data field
+ * of a new socket buffer.
+ */
+int i1480u_rx_setup(struct i1480u *i1480u)
+{
+ int result, cnt;
+ struct device *dev = &i1480u->usb_iface->dev;
+ struct net_device *net_dev = i1480u->net_dev;
+ struct usb_endpoint_descriptor *epd;
+ struct sk_buff *skb;
+
+ /* Alloc RX stuff */
+ i1480u->rx_skb = NULL; /* not in process of receiving packet */
+ result = -ENOMEM;
+ epd = &i1480u->usb_iface->cur_altsetting->endpoint[1].desc;
+ for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) {
+ struct i1480u_rx_buf *rx_buf = &i1480u->rx_buf[cnt];
+ rx_buf->i1480u = i1480u;
+ skb = dev_alloc_skb(i1480u_MAX_RX_PKT_SIZE);
+ if (!skb) {
+ dev_err(dev,
+ "RX: cannot allocate RX buffer %d\n", cnt);
+ result = -ENOMEM;
+ goto error;
+ }
+ skb->dev = net_dev;
+ skb->ip_summed = CHECKSUM_NONE;
+ skb_reserve(skb, 2);
+ rx_buf->data = skb;
+ rx_buf->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (unlikely(rx_buf->urb == NULL)) {
+ dev_err(dev, "RX: cannot allocate URB %d\n", cnt);
+ result = -ENOMEM;
+ goto error;
+ }
+ usb_fill_bulk_urb(rx_buf->urb, i1480u->usb_dev,
+ usb_rcvbulkpipe(i1480u->usb_dev, epd->bEndpointAddress),
+ rx_buf->data->data, i1480u_MAX_RX_PKT_SIZE - 2,
+ i1480u_rx_cb, rx_buf);
+ result = usb_submit_urb(rx_buf->urb, GFP_NOIO);
+ if (unlikely(result < 0)) {
+ dev_err(dev, "RX: cannot submit URB %d: %d\n",
+ cnt, result);
+ goto error;
+ }
+ }
+ return 0;
+
+error:
+ i1480u_rx_release(i1480u);
+ return result;
+}
+
+
+/** Release resources associated to the rx context */
+void i1480u_rx_release(struct i1480u *i1480u)
+{
+ int cnt;
+ for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) {
+ if (i1480u->rx_buf[cnt].data)
+ dev_kfree_skb(i1480u->rx_buf[cnt].data);
+ if (i1480u->rx_buf[cnt].urb) {
+ usb_kill_urb(i1480u->rx_buf[cnt].urb);
+ usb_free_urb(i1480u->rx_buf[cnt].urb);
+ }
+ }
+ if (i1480u->rx_skb != NULL)
+ dev_kfree_skb(i1480u->rx_skb);
+}
+
+static
+void i1480u_rx_unlink_urbs(struct i1480u *i1480u)
+{
+ int cnt;
+ for (cnt = 0; cnt < i1480u_RX_BUFS; cnt++) {
+ if (i1480u->rx_buf[cnt].urb)
+ usb_unlink_urb(i1480u->rx_buf[cnt].urb);
+ }
+}
+
+/** Fix an out-of-sequence packet */
+#define i1480u_fix(i1480u, msg...) \
+do { \
+ if (printk_ratelimit()) \
+ dev_err(&i1480u->usb_iface->dev, msg); \
+ dev_kfree_skb_irq(i1480u->rx_skb); \
+ i1480u->rx_skb = NULL; \
+ i1480u->rx_untd_pkt_size = 0; \
+} while (0)
+
+
+/** Drop an out-of-sequence packet */
+#define i1480u_drop(i1480u, msg...) \
+do { \
+ if (printk_ratelimit()) \
+ dev_err(&i1480u->usb_iface->dev, msg); \
+ i1480u->stats.rx_dropped++; \
+} while (0)
+
+
+
+
+/** Finalizes setting up the SKB and delivers it
+ *
+ * We first pass the incoming frame to WLP substack for verification. It
+ * may also be a WLP association frame in which case WLP will take over the
+ * processing. If WLP does not take it over it will still verify it, if the
+ * frame is invalid the skb will be freed by WLP and we will not continue
+ * parsing.
+ * */
+static
+void i1480u_skb_deliver(struct i1480u *i1480u)
+{
+ int should_parse;
+ struct net_device *net_dev = i1480u->net_dev;
+ struct device *dev = &i1480u->usb_iface->dev;
+
+ d_printf(6, dev, "RX delivered pre skb(%p), %u bytes\n",
+ i1480u->rx_skb, i1480u->rx_skb->len);
+ d_dump(7, dev, i1480u->rx_skb->data, i1480u->rx_skb->len);
+ should_parse = wlp_receive_frame(dev, &i1480u->wlp, i1480u->rx_skb,
+ &i1480u->rx_srcaddr);
+ if (!should_parse)
+ goto out;
+ i1480u->rx_skb->protocol = eth_type_trans(i1480u->rx_skb, net_dev);
+ d_printf(5, dev, "RX delivered skb(%p), %u bytes\n",
+ i1480u->rx_skb, i1480u->rx_skb->len);
+ d_dump(7, dev, i1480u->rx_skb->data,
+ i1480u->rx_skb->len > 72? 72 : i1480u->rx_skb->len);
+ i1480u->stats.rx_packets++;
+ i1480u->stats.rx_bytes += i1480u->rx_untd_pkt_size;
+ net_dev->last_rx = jiffies;
+ /* FIXME: flow control: check netif_rx() retval */
+
+ netif_rx(i1480u->rx_skb); /* deliver */
+out:
+ i1480u->rx_skb = NULL;
+ i1480u->rx_untd_pkt_size = 0;
+}
+
+
+/**
+ * Process a buffer of data received from the USB RX endpoint
+ *
+ * First fragment arrives with next or last fragment. All other fragments
+ * arrive alone.
+ *
+ * /me hates long functions.
+ */
+static
+void i1480u_rx_buffer(struct i1480u_rx_buf *rx_buf)
+{
+ unsigned pkt_completed = 0; /* !0 when we got all pkt fragments */
+ size_t untd_hdr_size, untd_frg_size;
+ size_t i1480u_hdr_size;
+ struct wlp_rx_hdr *i1480u_hdr = NULL;
+
+ struct i1480u *i1480u = rx_buf->i1480u;
+ struct sk_buff *skb = rx_buf->data;
+ int size_left = rx_buf->urb->actual_length;
+ void *ptr = rx_buf->urb->transfer_buffer; /* also rx_buf->data->data */
+ struct untd_hdr *untd_hdr;
+
+ struct net_device *net_dev = i1480u->net_dev;
+ struct device *dev = &i1480u->usb_iface->dev;
+ struct sk_buff *new_skb;
+
+#if 0
+ dev_fnstart(dev,
+ "(i1480u %p ptr %p size_left %zu)\n", i1480u, ptr, size_left);
+ dev_err(dev, "RX packet, %zu bytes\n", size_left);
+ dump_bytes(dev, ptr, size_left);
+#endif
+ i1480u_hdr_size = sizeof(struct wlp_rx_hdr);
+
+ while (size_left > 0) {
+ if (pkt_completed) {
+ i1480u_drop(i1480u, "RX: fragment follows completed"
+ "packet in same buffer. Dropping\n");
+ break;
+ }
+ untd_hdr = ptr;
+ if (size_left < sizeof(*untd_hdr)) { /* Check the UNTD header */
+ i1480u_drop(i1480u, "RX: short UNTD header! Dropping\n");
+ goto out;
+ }
+ if (unlikely(untd_hdr->rx_tx == 0)) { /* Paranoia: TX set? */
+ i1480u_drop(i1480u, "RX: TX bit set! Dropping\n");
+ goto out;
+ }
+ if (untd_hdr->reserved1) {
+ i1480u_drop(i1480u, "RX: reserved1 is not 0 (but %u)! "
+ "Dropping\n", untd_hdr->reserved1);
+ goto out;
+ }
+ switch (untd_hdr->type) { /* Check the UNTD header type */
+ case i1480u_PKT_FRAG_1ST: {
+ struct untd_hdr_1st *untd_hdr_1st = (void *) untd_hdr;
+ dev_dbg(dev, "1st fragment\n");
+ untd_hdr_size = sizeof(struct untd_hdr_1st);
+ if (i1480u->rx_skb != NULL)
+ i1480u_fix(i1480u, "RX: 1st fragment out of "
+ "sequence! Fixing\n");
+ if (size_left < untd_hdr_size + i1480u_hdr_size) {
+ i1480u_drop(i1480u, "RX: short 1st fragment! "
+ "Dropping\n");
+ goto out;
+ }
+ i1480u->rx_untd_pkt_size = le16_to_cpu(untd_hdr->len)
+ - i1480u_hdr_size;
+ untd_frg_size = le16_to_cpu(untd_hdr_1st->fragment_len);
+ if (size_left < untd_hdr_size + untd_frg_size) {
+ i1480u_drop(i1480u,
+ "RX: short payload! Dropping\n");
+ goto out;
+ }
+ i1480u->rx_skb = skb;
+ i1480u_hdr = (void *) untd_hdr_1st + untd_hdr_size;
+ i1480u->rx_srcaddr = i1480u_hdr->srcaddr;
+ skb_put(i1480u->rx_skb, untd_hdr_size + untd_frg_size);
+ skb_pull(i1480u->rx_skb, untd_hdr_size + i1480u_hdr_size);
+ stats_add_sample(&i1480u->lqe_stats, (s8) i1480u_hdr->LQI - 7);
+ stats_add_sample(&i1480u->rssi_stats, i1480u_hdr->RSSI + 18);
+ rx_buf->data = NULL; /* need to create new buffer */
+ break;
+ }
+ case i1480u_PKT_FRAG_NXT: {
+ dev_dbg(dev, "nxt fragment\n");
+ untd_hdr_size = sizeof(struct untd_hdr_rst);
+ if (i1480u->rx_skb == NULL) {
+ i1480u_drop(i1480u, "RX: next fragment out of "
+ "sequence! Dropping\n");
+ goto out;
+ }
+ if (size_left < untd_hdr_size) {
+ i1480u_drop(i1480u, "RX: short NXT fragment! "
+ "Dropping\n");
+ goto out;
+ }
+ untd_frg_size = le16_to_cpu(untd_hdr->len);
+ if (size_left < untd_hdr_size + untd_frg_size) {
+ i1480u_drop(i1480u,
+ "RX: short payload! Dropping\n");
+ goto out;
+ }
+ memmove(skb_put(i1480u->rx_skb, untd_frg_size),
+ ptr + untd_hdr_size, untd_frg_size);
+ break;
+ }
+ case i1480u_PKT_FRAG_LST: {
+ dev_dbg(dev, "Lst fragment\n");
+ untd_hdr_size = sizeof(struct untd_hdr_rst);
+ if (i1480u->rx_skb == NULL) {
+ i1480u_drop(i1480u, "RX: last fragment out of "
+ "sequence! Dropping\n");
+ goto out;
+ }
+ if (size_left < untd_hdr_size) {
+ i1480u_drop(i1480u, "RX: short LST fragment! "
+ "Dropping\n");
+ goto out;
+ }
+ untd_frg_size = le16_to_cpu(untd_hdr->len);
+ if (size_left < untd_frg_size + untd_hdr_size) {
+ i1480u_drop(i1480u,
+ "RX: short payload! Dropping\n");
+ goto out;
+ }
+ memmove(skb_put(i1480u->rx_skb, untd_frg_size),
+ ptr + untd_hdr_size, untd_frg_size);
+ pkt_completed = 1;
+ break;
+ }
+ case i1480u_PKT_FRAG_CMP: {
+ dev_dbg(dev, "cmp fragment\n");
+ untd_hdr_size = sizeof(struct untd_hdr_cmp);
+ if (i1480u->rx_skb != NULL)
+ i1480u_fix(i1480u, "RX: fix out-of-sequence CMP"
+ " fragment!\n");
+ if (size_left < untd_hdr_size + i1480u_hdr_size) {
+ i1480u_drop(i1480u, "RX: short CMP fragment! "
+ "Dropping\n");
+ goto out;
+ }
+ i1480u->rx_untd_pkt_size = le16_to_cpu(untd_hdr->len);
+ untd_frg_size = i1480u->rx_untd_pkt_size;
+ if (size_left < i1480u->rx_untd_pkt_size + untd_hdr_size) {
+ i1480u_drop(i1480u,
+ "RX: short payload! Dropping\n");
+ goto out;
+ }
+ i1480u->rx_skb = skb;
+ i1480u_hdr = (void *) untd_hdr + untd_hdr_size;
+ i1480u->rx_srcaddr = i1480u_hdr->srcaddr;
+ stats_add_sample(&i1480u->lqe_stats, (s8) i1480u_hdr->LQI - 7);
+ stats_add_sample(&i1480u->rssi_stats, i1480u_hdr->RSSI + 18);
+ skb_put(i1480u->rx_skb, untd_hdr_size + i1480u->rx_untd_pkt_size);
+ skb_pull(i1480u->rx_skb, untd_hdr_size + i1480u_hdr_size);
+ rx_buf->data = NULL; /* for hand off skb to network stack */
+ pkt_completed = 1;
+ i1480u->rx_untd_pkt_size -= i1480u_hdr_size; /* accurate stat */
+ break;
+ }
+ default:
+ i1480u_drop(i1480u, "RX: unknown packet type %u! "
+ "Dropping\n", untd_hdr->type);
+ goto out;
+ }
+ size_left -= untd_hdr_size + untd_frg_size;
+ if (size_left > 0)
+ ptr += untd_hdr_size + untd_frg_size;
+ }
+ if (pkt_completed)
+ i1480u_skb_deliver(i1480u);
+out:
+ /* recreate needed RX buffers*/
+ if (rx_buf->data == NULL) {
+ /* buffer is being used to receive packet, create new */
+ new_skb = dev_alloc_skb(i1480u_MAX_RX_PKT_SIZE);
+ if (!new_skb) {
+ if (printk_ratelimit())
+ dev_err(dev,
+ "RX: cannot allocate RX buffer\n");
+ } else {
+ new_skb->dev = net_dev;
+ new_skb->ip_summed = CHECKSUM_NONE;
+ skb_reserve(new_skb, 2);
+ rx_buf->data = new_skb;
+ }
+ }
+ return;
+}
+
+
+/**
+ * Called when an RX URB has finished receiving or has found some kind
+ * of error condition.
+ *
+ * LIMITATIONS:
+ *
+ * - We read USB-transfers, each transfer contains a SINGLE fragment
+ * (can contain a complete packet, or a 1st, next, or last fragment
+ * of a packet).
+ * Looks like a transfer can contain more than one fragment (07/18/06)
+ *
+ * - Each transfer buffer is the size of the maximum packet size (minus
+ * headroom), i1480u_MAX_PKT_SIZE - 2
+ *
+ * - We always read the full USB-transfer, no partials.
+ *
+ * - Each transfer is read directly into a skb. This skb will be used to
+ * send data to the upper layers if it is the first fragment or a complete
+ * packet. In the other cases the data will be copied from the skb to
+ * another skb that is being prepared for the upper layers from a prev
+ * first fragment.
+ *
+ * It is simply too much of a pain. Gosh, there should be a unified
+ * SG infrastructure for *everything* [so that I could declare a SG
+ * buffer, pass it to USB for receiving, append some space to it if
+ * I wish, receive more until I have the whole chunk, adapt
+ * pointers on each fragment to remove hardware headers and then
+ * attach that to an skbuff and netif_rx()].
+ */
+void i1480u_rx_cb(struct urb *urb)
+{
+ int result;
+ int do_parse_buffer = 1;
+ struct i1480u_rx_buf *rx_buf = urb->context;
+ struct i1480u *i1480u = rx_buf->i1480u;
+ struct device *dev = &i1480u->usb_iface->dev;
+ unsigned long flags;
+ u8 rx_buf_idx = rx_buf - i1480u->rx_buf;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ECONNRESET: /* Not an error, but a controlled situation; */
+ case -ENOENT: /* (we killed the URB)...so, no broadcast */
+ case -ESHUTDOWN: /* going away! */
+ dev_err(dev, "RX URB[%u]: goind down %d\n",
+ rx_buf_idx, urb->status);
+ goto error;
+ default:
+ dev_err(dev, "RX URB[%u]: unknown status %d\n",
+ rx_buf_idx, urb->status);
+ if (edc_inc(&i1480u->rx_errors, EDC_MAX_ERRORS,
+ EDC_ERROR_TIMEFRAME)) {
+ dev_err(dev, "RX: max acceptable errors exceeded,"
+ " resetting device.\n");
+ i1480u_rx_unlink_urbs(i1480u);
+ usb_dev_reset_delayed(i1480u->usb_dev);
+ goto error;
+ }
+ do_parse_buffer = 0;
+ break;
+ }
+ spin_lock_irqsave(&i1480u->lock, flags);
+ /* chew the data fragments, extract network packets */
+ if (do_parse_buffer) {
+ i1480u_rx_buffer(rx_buf);
+ if (rx_buf->data) {
+ rx_buf->urb->transfer_buffer = rx_buf->data->data;
+ result = usb_submit_urb(rx_buf->urb, GFP_ATOMIC);
+ if (result < 0) {
+ dev_err(dev, "RX URB[%u]: cannot submit %d\n",
+ rx_buf_idx, result);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&i1480u->lock, flags);
+error:
+ return;
+}
+
diff --git a/drivers/uwb/i1480/i1480u-wlp/sysfs.c b/drivers/uwb/i1480/i1480u-wlp/sysfs.c
new file mode 100644
index 000000000000..f0b8079d5ba0
--- /dev/null
+++ b/drivers/uwb/i1480/i1480u-wlp/sysfs.c
@@ -0,0 +1,446 @@
+/*
+ * WUSB Wire Adapter: WLP interface
+ * Sysfs interfaces
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/uwb/debug.h>
+#include <linux/device.h>
+#include "i1480u-wlp.h"
+
+
+/**
+ *
+ * @dev: Class device from the net_device; assumed refcnted.
+ *
+ * Yes, I don't lock--we assume it is refcounted and I am getting a
+ * single byte value that is kind of atomic to read.
+ */
+ssize_t uwb_phy_rate_show(const struct wlp_options *options, char *buf)
+{
+ return sprintf(buf,
+ "%u\n"
+ "\n"
+ "# echo UWB-PHY-RATE\n"
+ "#\n"
+ "# 0 - 53.3 Mbps\n"
+ "# 1 - 80 Mbps\n"
+ "# 2 - 106 Mbps\n"
+ "# 3 - 160 Mbps\n"
+ "# 4 - 200 Mbps\n"
+ "# 5 - 320 Mbps\n"
+ "# 6 - 400 Mbps\n"
+ "# 7 - 480 Mbps\n",
+ options->def_tx_hdr.phy_rate);
+}
+EXPORT_SYMBOL_GPL(uwb_phy_rate_show);
+
+
+ssize_t uwb_phy_rate_store(struct wlp_options *options,
+ const char *buf, size_t size)
+{
+ ssize_t result;
+ unsigned rate;
+
+ result = sscanf(buf, "%u\n", &rate);
+ if (result != 1) {
+ result = -EINVAL;
+ goto out;
+ }
+ result = -EINVAL;
+ if (rate >= UWB_PHY_RATE_INVALID)
+ goto out;
+ options->def_tx_hdr.phy_rate = rate;
+ result = 0;
+out:
+ return result < 0? result : size;
+}
+EXPORT_SYMBOL_GPL(uwb_phy_rate_store);
+
+
+ssize_t uwb_rts_cts_show(const struct wlp_options *options, char *buf)
+{
+ return sprintf(buf,
+ "%u\n"
+ "\n"
+ "# echo UWB-RTS-CTS\n"
+ "#\n"
+ "# 0 - RTS/CTS disabled\n"
+ "# 1 - RTS/CTS enabled\n",
+ options->def_tx_hdr.rts_cts);
+}
+EXPORT_SYMBOL_GPL(uwb_rts_cts_show);
+
+
+ssize_t uwb_rts_cts_store(struct wlp_options *options,
+ const char *buf, size_t size)
+{
+ ssize_t result;
+ unsigned value;
+
+ result = sscanf(buf, "%u\n", &value);
+ if (result != 1) {
+ result = -EINVAL;
+ goto out;
+ }
+ result = -EINVAL;
+ options->def_tx_hdr.rts_cts = value? 1 : 0;
+ result = 0;
+out:
+ return result < 0? result : size;
+}
+EXPORT_SYMBOL_GPL(uwb_rts_cts_store);
+
+
+ssize_t uwb_ack_policy_show(const struct wlp_options *options, char *buf)
+{
+ return sprintf(buf,
+ "%u\n"
+ "\n"
+ "# echo ACK-POLICY\n"
+ "#\n"
+ "# 0 - No Ack\n"
+ "# 1 - Immediate Ack\n"
+ "# 2 - B Ack\n"
+ "# 2 - B Req Ack\n",
+ options->def_tx_hdr.ack_pol);
+}
+EXPORT_SYMBOL_GPL(uwb_ack_policy_show);
+
+
+ssize_t uwb_ack_policy_store(struct wlp_options *options,
+ const char *buf, size_t size)
+{
+ ssize_t result;
+ unsigned value;
+
+ result = sscanf(buf, "%u\n", &value);
+ if (result != 1 || value > UWB_ACK_B_REQ) {
+ result = -EINVAL;
+ goto out;
+ }
+ options->def_tx_hdr.ack_pol = value;
+ result = 0;
+out:
+ return result < 0? result : size;
+}
+EXPORT_SYMBOL_GPL(uwb_ack_policy_store);
+
+
+/**
+ * Show the PCA base priority.
+ *
+ * We can access without locking, as the value is (for now) orthogonal
+ * to other values.
+ */
+ssize_t uwb_pca_base_priority_show(const struct wlp_options *options,
+ char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE,
+ "%u\n"
+ "\n"
+ "# echo PCA BASE PRIORITY\n"
+ "#\n"
+ "# 0 - AC_BE (Best effort)\n"
+ "# 1 - AC_BK (Background)\n"
+ "# 2 - AC_BK (Background)\n"
+ "# 3 - AC_BE (Best Effort)\n"
+ "# 4 - AC_VI (Video)\n"
+ "# 5 - AC_VI (Video)\n"
+ "# 6 - AC_VO (Voice)\n"
+ "# 7 - AC_VO (Voice)\n",
+ options->pca_base_priority);
+}
+EXPORT_SYMBOL_GPL(uwb_pca_base_priority_show);
+
+
+/**
+ * Set the PCA base priority.
+ *
+ * We can access without locking, as the value is (for now) orthogonal
+ * to other values.
+ */
+ssize_t uwb_pca_base_priority_store(struct wlp_options *options,
+ const char *buf, size_t size)
+{
+ ssize_t result = -EINVAL;
+ u8 pca_base_priority;
+
+ result = sscanf(buf, "%hhu\n", &pca_base_priority);
+ if (result != 1) {
+ result = -EINVAL;
+ goto out;
+ }
+ result = -EINVAL;
+ if (pca_base_priority >= 8)
+ goto out;
+ options->pca_base_priority = pca_base_priority;
+ /* Update TX header if we are currently using PCA. */
+ if (result >= 0 && (options->def_tx_hdr.delivery_id_type & 8) == 0)
+ options->def_tx_hdr.delivery_id_type = options->pca_base_priority;
+ result = 0;
+out:
+ return result < 0? result : size;
+}
+EXPORT_SYMBOL_GPL(uwb_pca_base_priority_store);
+
+/**
+ * Show current inflight values
+ *
+ * Will print the current MAX and THRESHOLD values for the basic flow
+ * control. In addition it will report how many times the TX queue needed
+ * to be restarted since the last time this query was made.
+ */
+static ssize_t wlp_tx_inflight_show(struct i1480u_tx_inflight *inflight,
+ char *buf)
+{
+ ssize_t result;
+ unsigned long sec_elapsed = (jiffies - inflight->restart_ts)/HZ;
+ unsigned long restart_count = atomic_read(&inflight->restart_count);
+
+ result = scnprintf(buf, PAGE_SIZE, "%lu %lu %d %lu %lu %lu\n"
+ "#read: threshold max inflight_count restarts "
+ "seconds restarts/sec\n"
+ "#write: threshold max\n",
+ inflight->threshold, inflight->max,
+ atomic_read(&inflight->count),
+ restart_count, sec_elapsed,
+ sec_elapsed == 0 ? 0 : restart_count/sec_elapsed);
+ inflight->restart_ts = jiffies;
+ atomic_set(&inflight->restart_count, 0);
+ return result;
+}
+
+static
+ssize_t wlp_tx_inflight_store(struct i1480u_tx_inflight *inflight,
+ const char *buf, size_t size)
+{
+ unsigned long in_threshold, in_max;
+ ssize_t result;
+ result = sscanf(buf, "%lu %lu", &in_threshold, &in_max);
+ if (result != 2)
+ return -EINVAL;
+ if (in_max <= in_threshold)
+ return -EINVAL;
+ inflight->max = in_max;
+ inflight->threshold = in_threshold;
+ return size;
+}
+/*
+ * Glue (or function adaptors) for accesing info on sysfs
+ *
+ * [we need this indirection because the PCI driver does almost the
+ * same]
+ *
+ * Linux 2.6.21 changed how 'struct netdevice' does attributes (from
+ * having a 'struct class_dev' to having a 'struct device'). That is
+ * quite of a pain.
+ *
+ * So we try to abstract that here. i1480u_SHOW() and i1480u_STORE()
+ * create adaptors for extracting the 'struct i1480u' from a 'struct
+ * dev' and calling a function for doing a sysfs operation (as we have
+ * them factorized already). i1480u_ATTR creates the attribute file
+ * (CLASS_DEVICE_ATTR or DEVICE_ATTR) and i1480u_ATTR_NAME produces a
+ * class_device_attr_NAME or device_attr_NAME (for group registration).
+ */
+#include <linux/version.h>
+
+#define i1480u_SHOW(name, fn, param) \
+static ssize_t i1480u_show_##name(struct device *dev, \
+ struct device_attribute *attr,\
+ char *buf) \
+{ \
+ struct i1480u *i1480u = netdev_priv(to_net_dev(dev)); \
+ return fn(&i1480u->param, buf); \
+}
+
+#define i1480u_STORE(name, fn, param) \
+static ssize_t i1480u_store_##name(struct device *dev, \
+ struct device_attribute *attr,\
+ const char *buf, size_t size)\
+{ \
+ struct i1480u *i1480u = netdev_priv(to_net_dev(dev)); \
+ return fn(&i1480u->param, buf, size); \
+}
+
+#define i1480u_ATTR(name, perm) static DEVICE_ATTR(name, perm, \
+ i1480u_show_##name,\
+ i1480u_store_##name)
+
+#define i1480u_ATTR_SHOW(name) static DEVICE_ATTR(name, \
+ S_IRUGO, \
+ i1480u_show_##name, NULL)
+
+#define i1480u_ATTR_NAME(a) (dev_attr_##a)
+
+
+/*
+ * Sysfs adaptors
+ */
+i1480u_SHOW(uwb_phy_rate, uwb_phy_rate_show, options);
+i1480u_STORE(uwb_phy_rate, uwb_phy_rate_store, options);
+i1480u_ATTR(uwb_phy_rate, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(uwb_rts_cts, uwb_rts_cts_show, options);
+i1480u_STORE(uwb_rts_cts, uwb_rts_cts_store, options);
+i1480u_ATTR(uwb_rts_cts, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(uwb_ack_policy, uwb_ack_policy_show, options);
+i1480u_STORE(uwb_ack_policy, uwb_ack_policy_store, options);
+i1480u_ATTR(uwb_ack_policy, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(uwb_pca_base_priority, uwb_pca_base_priority_show, options);
+i1480u_STORE(uwb_pca_base_priority, uwb_pca_base_priority_store, options);
+i1480u_ATTR(uwb_pca_base_priority, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_eda, wlp_eda_show, wlp);
+i1480u_STORE(wlp_eda, wlp_eda_store, wlp);
+i1480u_ATTR(wlp_eda, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_uuid, wlp_uuid_show, wlp);
+i1480u_STORE(wlp_uuid, wlp_uuid_store, wlp);
+i1480u_ATTR(wlp_uuid, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_dev_name, wlp_dev_name_show, wlp);
+i1480u_STORE(wlp_dev_name, wlp_dev_name_store, wlp);
+i1480u_ATTR(wlp_dev_name, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_dev_manufacturer, wlp_dev_manufacturer_show, wlp);
+i1480u_STORE(wlp_dev_manufacturer, wlp_dev_manufacturer_store, wlp);
+i1480u_ATTR(wlp_dev_manufacturer, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_dev_model_name, wlp_dev_model_name_show, wlp);
+i1480u_STORE(wlp_dev_model_name, wlp_dev_model_name_store, wlp);
+i1480u_ATTR(wlp_dev_model_name, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_dev_model_nr, wlp_dev_model_nr_show, wlp);
+i1480u_STORE(wlp_dev_model_nr, wlp_dev_model_nr_store, wlp);
+i1480u_ATTR(wlp_dev_model_nr, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_dev_serial, wlp_dev_serial_show, wlp);
+i1480u_STORE(wlp_dev_serial, wlp_dev_serial_store, wlp);
+i1480u_ATTR(wlp_dev_serial, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_dev_prim_category, wlp_dev_prim_category_show, wlp);
+i1480u_STORE(wlp_dev_prim_category, wlp_dev_prim_category_store, wlp);
+i1480u_ATTR(wlp_dev_prim_category, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_dev_prim_OUI, wlp_dev_prim_OUI_show, wlp);
+i1480u_STORE(wlp_dev_prim_OUI, wlp_dev_prim_OUI_store, wlp);
+i1480u_ATTR(wlp_dev_prim_OUI, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_dev_prim_OUI_sub, wlp_dev_prim_OUI_sub_show, wlp);
+i1480u_STORE(wlp_dev_prim_OUI_sub, wlp_dev_prim_OUI_sub_store, wlp);
+i1480u_ATTR(wlp_dev_prim_OUI_sub, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_dev_prim_subcat, wlp_dev_prim_subcat_show, wlp);
+i1480u_STORE(wlp_dev_prim_subcat, wlp_dev_prim_subcat_store, wlp);
+i1480u_ATTR(wlp_dev_prim_subcat, S_IRUGO | S_IWUSR);
+
+i1480u_SHOW(wlp_neighborhood, wlp_neighborhood_show, wlp);
+i1480u_ATTR_SHOW(wlp_neighborhood);
+
+i1480u_SHOW(wss_activate, wlp_wss_activate_show, wlp.wss);
+i1480u_STORE(wss_activate, wlp_wss_activate_store, wlp.wss);
+i1480u_ATTR(wss_activate, S_IRUGO | S_IWUSR);
+
+/*
+ * Show the (min, max, avg) Line Quality Estimate (LQE, in dB) as over
+ * the last 256 received WLP frames (ECMA-368 13.3).
+ *
+ * [the -7dB that have to be substracted from the LQI to make the LQE
+ * are already taken into account].
+ */
+i1480u_SHOW(wlp_lqe, stats_show, lqe_stats);
+i1480u_STORE(wlp_lqe, stats_store, lqe_stats);
+i1480u_ATTR(wlp_lqe, S_IRUGO | S_IWUSR);
+
+/*
+ * Show the Receive Signal Strength Indicator averaged over all the
+ * received WLP frames (ECMA-368 13.3). Still is not clear what
+ * this value is, but is kind of a percentage of the signal strength
+ * at the antenna.
+ */
+i1480u_SHOW(wlp_rssi, stats_show, rssi_stats);
+i1480u_STORE(wlp_rssi, stats_store, rssi_stats);
+i1480u_ATTR(wlp_rssi, S_IRUGO | S_IWUSR);
+
+/**
+ * We maintain a basic flow control counter. "count" how many TX URBs are
+ * outstanding. Only allow "max"
+ * TX URBs to be outstanding. If this value is reached the queue will be
+ * stopped. The queue will be restarted when there are
+ * "threshold" URBs outstanding.
+ */
+i1480u_SHOW(wlp_tx_inflight, wlp_tx_inflight_show, tx_inflight);
+i1480u_STORE(wlp_tx_inflight, wlp_tx_inflight_store, tx_inflight);
+i1480u_ATTR(wlp_tx_inflight, S_IRUGO | S_IWUSR);
+
+static struct attribute *i1480u_attrs[] = {
+ &i1480u_ATTR_NAME(uwb_phy_rate).attr,
+ &i1480u_ATTR_NAME(uwb_rts_cts).attr,
+ &i1480u_ATTR_NAME(uwb_ack_policy).attr,
+ &i1480u_ATTR_NAME(uwb_pca_base_priority).attr,
+ &i1480u_ATTR_NAME(wlp_lqe).attr,
+ &i1480u_ATTR_NAME(wlp_rssi).attr,
+ &i1480u_ATTR_NAME(wlp_eda).attr,
+ &i1480u_ATTR_NAME(wlp_uuid).attr,
+ &i1480u_ATTR_NAME(wlp_dev_name).attr,
+ &i1480u_ATTR_NAME(wlp_dev_manufacturer).attr,
+ &i1480u_ATTR_NAME(wlp_dev_model_name).attr,
+ &i1480u_ATTR_NAME(wlp_dev_model_nr).attr,
+ &i1480u_ATTR_NAME(wlp_dev_serial).attr,
+ &i1480u_ATTR_NAME(wlp_dev_prim_category).attr,
+ &i1480u_ATTR_NAME(wlp_dev_prim_OUI).attr,
+ &i1480u_ATTR_NAME(wlp_dev_prim_OUI_sub).attr,
+ &i1480u_ATTR_NAME(wlp_dev_prim_subcat).attr,
+ &i1480u_ATTR_NAME(wlp_neighborhood).attr,
+ &i1480u_ATTR_NAME(wss_activate).attr,
+ &i1480u_ATTR_NAME(wlp_tx_inflight).attr,
+ NULL,
+};
+
+static struct attribute_group i1480u_attr_group = {
+ .name = NULL, /* we want them in the same directory */
+ .attrs = i1480u_attrs,
+};
+
+int i1480u_sysfs_setup(struct i1480u *i1480u)
+{
+ int result;
+ struct device *dev = &i1480u->usb_iface->dev;
+ result = sysfs_create_group(&i1480u->net_dev->dev.kobj,
+ &i1480u_attr_group);
+ if (result < 0)
+ dev_err(dev, "cannot initialize sysfs attributes: %d\n",
+ result);
+ return result;
+}
+
+
+void i1480u_sysfs_release(struct i1480u *i1480u)
+{
+ sysfs_remove_group(&i1480u->net_dev->dev.kobj,
+ &i1480u_attr_group);
+}
diff --git a/drivers/uwb/i1480/i1480u-wlp/tx.c b/drivers/uwb/i1480/i1480u-wlp/tx.c
new file mode 100644
index 000000000000..cf8056738149
--- /dev/null
+++ b/drivers/uwb/i1480/i1480u-wlp/tx.c
@@ -0,0 +1,638 @@
+/*
+ * WUSB Wire Adapter: WLP interface
+ * Deal with TX (massaging data to transmit, handling it)
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Transmission engine. Get an skb, create from that a WLP transmit
+ * context, add a WLP TX header (which we keep prefilled in the
+ * device's instance), fill out the target-specific fields and
+ * fire it.
+ *
+ * ROADMAP:
+ *
+ * Entry points:
+ *
+ * i1480u_tx_release(): called by i1480u_disconnect() to release
+ * pending tx contexts.
+ *
+ * i1480u_tx_cb(): callback for TX contexts (USB URBs)
+ * i1480u_tx_destroy():
+ *
+ * i1480u_tx_timeout(): called for timeout handling from the
+ * network stack.
+ *
+ * i1480u_hard_start_xmit(): called for transmitting an skb from
+ * the network stack. Will interact with WLP
+ * substack to verify and prepare frame.
+ * i1480u_xmit_frame(): actual transmission on hardware
+ *
+ * i1480u_tx_create() Creates TX context
+ * i1480u_tx_create_1() For packets in 1 fragment
+ * i1480u_tx_create_n() For packets in >1 fragments
+ *
+ * TODO:
+ *
+ * - FIXME: rewrite using usb_sg_*(), add asynch support to
+ * usb_sg_*(). It might not make too much sense as most of
+ * the times the MTU will be smaller than one page...
+ */
+
+#include "i1480u-wlp.h"
+#define D_LOCAL 5
+#include <linux/uwb/debug.h>
+
+enum {
+ /* This is only for Next and Last TX packets */
+ i1480u_MAX_PL_SIZE = i1480u_MAX_FRG_SIZE
+ - sizeof(struct untd_hdr_rst),
+};
+
+/** Free resources allocated to a i1480u tx context. */
+static
+void i1480u_tx_free(struct i1480u_tx *wtx)
+{
+ kfree(wtx->buf);
+ if (wtx->skb)
+ dev_kfree_skb_irq(wtx->skb);
+ usb_free_urb(wtx->urb);
+ kfree(wtx);
+}
+
+static
+void i1480u_tx_destroy(struct i1480u *i1480u, struct i1480u_tx *wtx)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&i1480u->tx_list_lock, flags); /* not active any more */
+ list_del(&wtx->list_node);
+ i1480u_tx_free(wtx);
+ spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
+}
+
+static
+void i1480u_tx_unlink_urbs(struct i1480u *i1480u)
+{
+ unsigned long flags;
+ struct i1480u_tx *wtx, *next;
+
+ spin_lock_irqsave(&i1480u->tx_list_lock, flags);
+ list_for_each_entry_safe(wtx, next, &i1480u->tx_list, list_node) {
+ usb_unlink_urb(wtx->urb);
+ }
+ spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
+}
+
+
+/**
+ * Callback for a completed tx USB URB.
+ *
+ * TODO:
+ *
+ * - FIXME: recover errors more gracefully
+ * - FIXME: handle NAKs (I dont think they come here) for flow ctl
+ */
+static
+void i1480u_tx_cb(struct urb *urb)
+{
+ struct i1480u_tx *wtx = urb->context;
+ struct i1480u *i1480u = wtx->i1480u;
+ struct net_device *net_dev = i1480u->net_dev;
+ struct device *dev = &i1480u->usb_iface->dev;
+ unsigned long flags;
+
+ switch (urb->status) {
+ case 0:
+ spin_lock_irqsave(&i1480u->lock, flags);
+ i1480u->stats.tx_packets++;
+ i1480u->stats.tx_bytes += urb->actual_length;
+ spin_unlock_irqrestore(&i1480u->lock, flags);
+ break;
+ case -ECONNRESET: /* Not an error, but a controlled situation; */
+ case -ENOENT: /* (we killed the URB)...so, no broadcast */
+ dev_dbg(dev, "notif endp: reset/noent %d\n", urb->status);
+ netif_stop_queue(net_dev);
+ break;
+ case -ESHUTDOWN: /* going away! */
+ dev_dbg(dev, "notif endp: down %d\n", urb->status);
+ netif_stop_queue(net_dev);
+ break;
+ default:
+ dev_err(dev, "TX: unknown URB status %d\n", urb->status);
+ if (edc_inc(&i1480u->tx_errors, EDC_MAX_ERRORS,
+ EDC_ERROR_TIMEFRAME)) {
+ dev_err(dev, "TX: max acceptable errors exceeded."
+ "Reset device.\n");
+ netif_stop_queue(net_dev);
+ i1480u_tx_unlink_urbs(i1480u);
+ usb_dev_reset_delayed(i1480u->usb_dev);
+ }
+ break;
+ }
+ i1480u_tx_destroy(i1480u, wtx);
+ if (atomic_dec_return(&i1480u->tx_inflight.count)
+ <= i1480u->tx_inflight.threshold
+ && netif_queue_stopped(net_dev)
+ && i1480u->tx_inflight.threshold != 0) {
+ if (d_test(2) && printk_ratelimit())
+ d_printf(2, dev, "Restart queue. \n");
+ netif_start_queue(net_dev);
+ atomic_inc(&i1480u->tx_inflight.restart_count);
+ }
+ return;
+}
+
+
+/**
+ * Given a buffer that doesn't fit in a single fragment, create an
+ * scatter/gather structure for delivery to the USB pipe.
+ *
+ * Implements functionality of i1480u_tx_create().
+ *
+ * @wtx: tx descriptor
+ * @skb: skb to send
+ * @gfp_mask: gfp allocation mask
+ * @returns: Pointer to @wtx if ok, NULL on error.
+ *
+ * Sorry, TOO LONG a function, but breaking it up is kind of hard
+ *
+ * This will break the buffer in chunks smaller than
+ * i1480u_MAX_FRG_SIZE (including the header) and add proper headers
+ * to each:
+ *
+ * 1st header \
+ * i1480 tx header | fragment 1
+ * fragment data /
+ * nxt header \ fragment 2
+ * fragment data /
+ * ..
+ * ..
+ * last header \ fragment 3
+ * last fragment data /
+ *
+ * This does not fill the i1480 TX header, it is left up to the
+ * caller to do that; you can get it from @wtx->wlp_tx_hdr.
+ *
+ * This function consumes the skb unless there is an error.
+ */
+static
+int i1480u_tx_create_n(struct i1480u_tx *wtx, struct sk_buff *skb,
+ gfp_t gfp_mask)
+{
+ int result;
+ void *pl;
+ size_t pl_size;
+
+ void *pl_itr, *buf_itr;
+ size_t pl_size_left, frgs, pl_size_1st, frg_pl_size = 0;
+ struct untd_hdr_1st *untd_hdr_1st;
+ struct wlp_tx_hdr *wlp_tx_hdr;
+ struct untd_hdr_rst *untd_hdr_rst;
+
+ wtx->skb = NULL;
+ pl = skb->data;
+ pl_itr = pl;
+ pl_size = skb->len;
+ pl_size_left = pl_size; /* payload size */
+ /* First fragment; fits as much as i1480u_MAX_FRG_SIZE minus
+ * the headers */
+ pl_size_1st = i1480u_MAX_FRG_SIZE
+ - sizeof(struct untd_hdr_1st) - sizeof(struct wlp_tx_hdr);
+ BUG_ON(pl_size_1st > pl_size);
+ pl_size_left -= pl_size_1st;
+ /* The rest have an smaller header (no i1480 TX header). We
+ * need to break up the payload in blocks smaller than
+ * i1480u_MAX_PL_SIZE (payload excluding header). */
+ frgs = (pl_size_left + i1480u_MAX_PL_SIZE - 1) / i1480u_MAX_PL_SIZE;
+ /* Allocate space for the new buffer. In this new buffer we'll
+ * place the headers followed by the data fragment, headers,
+ * data fragments, etc..
+ */
+ result = -ENOMEM;
+ wtx->buf_size = sizeof(*untd_hdr_1st)
+ + sizeof(*wlp_tx_hdr)
+ + frgs * sizeof(*untd_hdr_rst)
+ + pl_size;
+ wtx->buf = kmalloc(wtx->buf_size, gfp_mask);
+ if (wtx->buf == NULL)
+ goto error_buf_alloc;
+
+ buf_itr = wtx->buf; /* We got the space, let's fill it up */
+ /* Fill 1st fragment */
+ untd_hdr_1st = buf_itr;
+ buf_itr += sizeof(*untd_hdr_1st);
+ untd_hdr_1st->hdr.type = i1480u_PKT_FRAG_1ST;
+ untd_hdr_1st->hdr.rx_tx = 0;
+ untd_hdr_1st->hdr.reserved1 = 0;
+ untd_hdr_1st->hdr.reserved2 = 0;
+ untd_hdr_1st->hdr.len = cpu_to_le16(pl_size + sizeof(*wlp_tx_hdr));
+ untd_hdr_1st->fragment_len =
+ cpu_to_le16(pl_size_1st + sizeof(*wlp_tx_hdr));
+ memset(untd_hdr_1st->padding, 0, sizeof(untd_hdr_1st->padding));
+ /* Set up i1480 header info */
+ wlp_tx_hdr = wtx->wlp_tx_hdr = buf_itr;
+ buf_itr += sizeof(*wlp_tx_hdr);
+ /* Copy the first fragment */
+ memcpy(buf_itr, pl_itr, pl_size_1st);
+ pl_itr += pl_size_1st;
+ buf_itr += pl_size_1st;
+
+ /* Now do each remaining fragment */
+ result = -EINVAL;
+ while (pl_size_left > 0) {
+ d_printf(5, NULL, "ITR HDR: pl_size_left %zu buf_itr %zu\n",
+ pl_size_left, buf_itr - wtx->buf);
+ if (buf_itr + sizeof(*untd_hdr_rst) - wtx->buf
+ > wtx->buf_size) {
+ printk(KERN_ERR "BUG: no space for header\n");
+ goto error_bug;
+ }
+ d_printf(5, NULL, "ITR HDR 2: pl_size_left %zu buf_itr %zu\n",
+ pl_size_left, buf_itr - wtx->buf);
+ untd_hdr_rst = buf_itr;
+ buf_itr += sizeof(*untd_hdr_rst);
+ if (pl_size_left > i1480u_MAX_PL_SIZE) {
+ frg_pl_size = i1480u_MAX_PL_SIZE;
+ untd_hdr_rst->hdr.type = i1480u_PKT_FRAG_NXT;
+ } else {
+ frg_pl_size = pl_size_left;
+ untd_hdr_rst->hdr.type = i1480u_PKT_FRAG_LST;
+ }
+ d_printf(5, NULL,
+ "ITR PL: pl_size_left %zu buf_itr %zu frg_pl_size %zu\n",
+ pl_size_left, buf_itr - wtx->buf, frg_pl_size);
+ untd_hdr_rst->hdr.rx_tx = 0;
+ untd_hdr_rst->hdr.reserved1 = 0;
+ untd_hdr_rst->hdr.reserved2 = 0;
+ untd_hdr_rst->hdr.len = cpu_to_le16(frg_pl_size);
+ untd_hdr_rst->padding = 0;
+ if (buf_itr + frg_pl_size - wtx->buf
+ > wtx->buf_size) {
+ printk(KERN_ERR "BUG: no space for payload\n");
+ goto error_bug;
+ }
+ memcpy(buf_itr, pl_itr, frg_pl_size);
+ buf_itr += frg_pl_size;
+ pl_itr += frg_pl_size;
+ pl_size_left -= frg_pl_size;
+ d_printf(5, NULL,
+ "ITR PL 2: pl_size_left %zu buf_itr %zu frg_pl_size %zu\n",
+ pl_size_left, buf_itr - wtx->buf, frg_pl_size);
+ }
+ dev_kfree_skb_irq(skb);
+ return 0;
+
+error_bug:
+ printk(KERN_ERR
+ "BUG: skb %u bytes\n"
+ "BUG: frg_pl_size %zd i1480u_MAX_FRG_SIZE %u\n"
+ "BUG: buf_itr %zu buf_size %zu pl_size_left %zu\n",
+ skb->len,
+ frg_pl_size, i1480u_MAX_FRG_SIZE,
+ buf_itr - wtx->buf, wtx->buf_size, pl_size_left);
+
+ kfree(wtx->buf);
+error_buf_alloc:
+ return result;
+}
+
+
+/**
+ * Given a buffer that fits in a single fragment, fill out a @wtx
+ * struct for transmitting it down the USB pipe.
+ *
+ * Uses the fact that we have space reserved in front of the skbuff
+ * for hardware headers :]
+ *
+ * This does not fill the i1480 TX header, it is left up to the
+ * caller to do that; you can get it from @wtx->wlp_tx_hdr.
+ *
+ * @pl: pointer to payload data
+ * @pl_size: size of the payuload
+ *
+ * This function does not consume the @skb.
+ */
+static
+int i1480u_tx_create_1(struct i1480u_tx *wtx, struct sk_buff *skb,
+ gfp_t gfp_mask)
+{
+ struct untd_hdr_cmp *untd_hdr_cmp;
+ struct wlp_tx_hdr *wlp_tx_hdr;
+
+ wtx->buf = NULL;
+ wtx->skb = skb;
+ BUG_ON(skb_headroom(skb) < sizeof(*wlp_tx_hdr));
+ wlp_tx_hdr = (void *) __skb_push(skb, sizeof(*wlp_tx_hdr));
+ wtx->wlp_tx_hdr = wlp_tx_hdr;
+ BUG_ON(skb_headroom(skb) < sizeof(*untd_hdr_cmp));
+ untd_hdr_cmp = (void *) __skb_push(skb, sizeof(*untd_hdr_cmp));
+
+ untd_hdr_cmp->hdr.type = i1480u_PKT_FRAG_CMP;
+ untd_hdr_cmp->hdr.rx_tx = 0;
+ untd_hdr_cmp->hdr.reserved1 = 0;
+ untd_hdr_cmp->hdr.len = cpu_to_le16(skb->len - sizeof(*untd_hdr_cmp));
+ untd_hdr_cmp->hdr.reserved2 = 0;
+ untd_hdr_cmp->padding = 0;
+ return 0;
+}
+
+
+/**
+ * Given a skb to transmit, massage it to become palatable for the TX pipe
+ *
+ * This will break the buffer in chunks smaller than
+ * i1480u_MAX_FRG_SIZE and add proper headers to each.
+ *
+ * 1st header \
+ * i1480 tx header | fragment 1
+ * fragment data /
+ * nxt header \ fragment 2
+ * fragment data /
+ * ..
+ * ..
+ * last header \ fragment 3
+ * last fragment data /
+ *
+ * Each fragment will be always smaller or equal to i1480u_MAX_FRG_SIZE.
+ *
+ * If the first fragment is smaller than i1480u_MAX_FRG_SIZE, then the
+ * following is composed:
+ *
+ * complete header \
+ * i1480 tx header | single fragment
+ * packet data /
+ *
+ * We were going to use s/g support, but because the interface is
+ * synch and at the end there is plenty of overhead to do it, it
+ * didn't seem that worth for data that is going to be smaller than
+ * one page.
+ */
+static
+struct i1480u_tx *i1480u_tx_create(struct i1480u *i1480u,
+ struct sk_buff *skb, gfp_t gfp_mask)
+{
+ int result;
+ struct usb_endpoint_descriptor *epd;
+ int usb_pipe;
+ unsigned long flags;
+
+ struct i1480u_tx *wtx;
+ const size_t pl_max_size =
+ i1480u_MAX_FRG_SIZE - sizeof(struct untd_hdr_cmp)
+ - sizeof(struct wlp_tx_hdr);
+
+ wtx = kmalloc(sizeof(*wtx), gfp_mask);
+ if (wtx == NULL)
+ goto error_wtx_alloc;
+ wtx->urb = usb_alloc_urb(0, gfp_mask);
+ if (wtx->urb == NULL)
+ goto error_urb_alloc;
+ epd = &i1480u->usb_iface->cur_altsetting->endpoint[2].desc;
+ usb_pipe = usb_sndbulkpipe(i1480u->usb_dev, epd->bEndpointAddress);
+ /* Fits in a single complete packet or need to split? */
+ if (skb->len > pl_max_size) {
+ result = i1480u_tx_create_n(wtx, skb, gfp_mask);
+ if (result < 0)
+ goto error_create;
+ usb_fill_bulk_urb(wtx->urb, i1480u->usb_dev, usb_pipe,
+ wtx->buf, wtx->buf_size, i1480u_tx_cb, wtx);
+ } else {
+ result = i1480u_tx_create_1(wtx, skb, gfp_mask);
+ if (result < 0)
+ goto error_create;
+ usb_fill_bulk_urb(wtx->urb, i1480u->usb_dev, usb_pipe,
+ skb->data, skb->len, i1480u_tx_cb, wtx);
+ }
+ spin_lock_irqsave(&i1480u->tx_list_lock, flags);
+ list_add(&wtx->list_node, &i1480u->tx_list);
+ spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
+ return wtx;
+
+error_create:
+ kfree(wtx->urb);
+error_urb_alloc:
+ kfree(wtx);
+error_wtx_alloc:
+ return NULL;
+}
+
+/**
+ * Actual fragmentation and transmission of frame
+ *
+ * @wlp: WLP substack data structure
+ * @skb: To be transmitted
+ * @dst: Device address of destination
+ * @returns: 0 on success, <0 on failure
+ *
+ * This function can also be called directly (not just from
+ * hard_start_xmit), so we also check here if the interface is up before
+ * taking sending anything.
+ */
+int i1480u_xmit_frame(struct wlp *wlp, struct sk_buff *skb,
+ struct uwb_dev_addr *dst)
+{
+ int result = -ENXIO;
+ struct i1480u *i1480u = container_of(wlp, struct i1480u, wlp);
+ struct device *dev = &i1480u->usb_iface->dev;
+ struct net_device *net_dev = i1480u->net_dev;
+ struct i1480u_tx *wtx;
+ struct wlp_tx_hdr *wlp_tx_hdr;
+ static unsigned char dev_bcast[2] = { 0xff, 0xff };
+#if 0
+ int lockup = 50;
+#endif
+
+ d_fnstart(6, dev, "(skb %p (%u), net_dev %p)\n", skb, skb->len,
+ net_dev);
+ BUG_ON(i1480u->wlp.rc == NULL);
+ if ((net_dev->flags & IFF_UP) == 0)
+ goto out;
+ result = -EBUSY;
+ if (atomic_read(&i1480u->tx_inflight.count) >= i1480u->tx_inflight.max) {
+ if (d_test(2) && printk_ratelimit())
+ d_printf(2, dev, "Max frames in flight "
+ "stopping queue.\n");
+ netif_stop_queue(net_dev);
+ goto error_max_inflight;
+ }
+ result = -ENOMEM;
+ wtx = i1480u_tx_create(i1480u, skb, GFP_ATOMIC);
+ if (unlikely(wtx == NULL)) {
+ if (printk_ratelimit())
+ dev_err(dev, "TX: no memory for WLP TX URB,"
+ "dropping packet (in flight %d)\n",
+ atomic_read(&i1480u->tx_inflight.count));
+ netif_stop_queue(net_dev);
+ goto error_wtx_alloc;
+ }
+ wtx->i1480u = i1480u;
+ /* Fill out the i1480 header; @i1480u->def_tx_hdr read without
+ * locking. We do so because they are kind of orthogonal to
+ * each other (and thus not changed in an atomic batch).
+ * The ETH header is right after the WLP TX header. */
+ wlp_tx_hdr = wtx->wlp_tx_hdr;
+ *wlp_tx_hdr = i1480u->options.def_tx_hdr;
+ wlp_tx_hdr->dstaddr = *dst;
+ if (!memcmp(&wlp_tx_hdr->dstaddr, dev_bcast, sizeof(dev_bcast)) &&
+ (wlp_tx_hdr->delivery_id_type & WLP_DRP)) {
+ /*Broadcast message directed to DRP host. Send as best effort
+ * on PCA. */
+ wlp_tx_hdr->delivery_id_type = i1480u->options.pca_base_priority;
+ }
+
+#if 0
+ dev_info(dev, "TX delivering skb -> USB, %zu bytes\n", skb->len);
+ dump_bytes(dev, skb->data, skb->len > 72? 72 : skb->len);
+#endif
+#if 0
+ /* simulates a device lockup after every lockup# packets */
+ if (lockup && ((i1480u->stats.tx_packets + 1) % lockup) == 0) {
+ /* Simulate a dropped transmit interrupt */
+ net_dev->trans_start = jiffies;
+ netif_stop_queue(net_dev);
+ dev_err(dev, "Simulate lockup at %ld\n", jiffies);
+ return result;
+ }
+#endif
+
+ result = usb_submit_urb(wtx->urb, GFP_ATOMIC); /* Go baby */
+ if (result < 0) {
+ dev_err(dev, "TX: cannot submit URB: %d\n", result);
+ /* We leave the freeing of skb to calling function */
+ wtx->skb = NULL;
+ goto error_tx_urb_submit;
+ }
+ atomic_inc(&i1480u->tx_inflight.count);
+ net_dev->trans_start = jiffies;
+ d_fnend(6, dev, "(skb %p (%u), net_dev %p) = %d\n", skb, skb->len,
+ net_dev, result);
+ return result;
+
+error_tx_urb_submit:
+ i1480u_tx_destroy(i1480u, wtx);
+error_wtx_alloc:
+error_max_inflight:
+out:
+ d_fnend(6, dev, "(skb %p (%u), net_dev %p) = %d\n", skb, skb->len,
+ net_dev, result);
+ return result;
+}
+
+
+/**
+ * Transmit an skb Called when an skbuf has to be transmitted
+ *
+ * The skb is first passed to WLP substack to ensure this is a valid
+ * frame. If valid the device address of destination will be filled and
+ * the WLP header prepended to the skb. If this step fails we fake sending
+ * the frame, if we return an error the network stack will just keep trying.
+ *
+ * Broadcast frames inside a WSS needs to be treated special as multicast is
+ * not supported. A broadcast frame is sent as unicast to each member of the
+ * WSS - this is done by the WLP substack when it finds a broadcast frame.
+ * So, we test if the WLP substack took over the skb and only transmit it
+ * if it has not (been taken over).
+ *
+ * @net_dev->xmit_lock is held
+ */
+int i1480u_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev)
+{
+ int result;
+ struct i1480u *i1480u = netdev_priv(net_dev);
+ struct device *dev = &i1480u->usb_iface->dev;
+ struct uwb_dev_addr dst;
+
+ d_fnstart(6, dev, "(skb %p (%u), net_dev %p)\n", skb, skb->len,
+ net_dev);
+ BUG_ON(i1480u->wlp.rc == NULL);
+ if ((net_dev->flags & IFF_UP) == 0)
+ goto error;
+ result = wlp_prepare_tx_frame(dev, &i1480u->wlp, skb, &dst);
+ if (result < 0) {
+ dev_err(dev, "WLP verification of TX frame failed (%d). "
+ "Dropping packet.\n", result);
+ goto error;
+ } else if (result == 1) {
+ d_printf(6, dev, "WLP will transmit frame. \n");
+ /* trans_start time will be set when WLP actually transmits
+ * the frame */
+ goto out;
+ }
+ d_printf(6, dev, "Transmitting frame. \n");
+ result = i1480u_xmit_frame(&i1480u->wlp, skb, &dst);
+ if (result < 0) {
+ dev_err(dev, "Frame TX failed (%d).\n", result);
+ goto error;
+ }
+ d_fnend(6, dev, "(skb %p (%u), net_dev %p) = %d\n", skb, skb->len,
+ net_dev, result);
+ return NETDEV_TX_OK;
+error:
+ dev_kfree_skb_any(skb);
+ i1480u->stats.tx_dropped++;
+out:
+ d_fnend(6, dev, "(skb %p (%u), net_dev %p) = %d\n", skb, skb->len,
+ net_dev, result);
+ return NETDEV_TX_OK;
+}
+
+
+/**
+ * Called when a pkt transmission doesn't complete in a reasonable period
+ * Device reset may sleep - do it outside of interrupt context (delayed)
+ */
+void i1480u_tx_timeout(struct net_device *net_dev)
+{
+ struct i1480u *i1480u = netdev_priv(net_dev);
+
+ usb_dev_reset_delayed(i1480u->usb_dev);
+}
+
+
+void i1480u_tx_release(struct i1480u *i1480u)
+{
+ unsigned long flags;
+ struct i1480u_tx *wtx, *next;
+ int count = 0, empty;
+
+ spin_lock_irqsave(&i1480u->tx_list_lock, flags);
+ list_for_each_entry_safe(wtx, next, &i1480u->tx_list, list_node) {
+ count++;
+ usb_unlink_urb(wtx->urb);
+ }
+ spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
+ count = count*10; /* i1480ut 200ms per unlinked urb (intervals of 20ms) */
+ /*
+ * We don't like this sollution too much (dirty as it is), but
+ * it is cheaper than putting a refcount on each i1480u_tx and
+ * i1480uting for all of them to go away...
+ *
+ * Called when no more packets can be added to tx_list
+ * so can i1480ut for it to be empty.
+ */
+ while (1) {
+ spin_lock_irqsave(&i1480u->tx_list_lock, flags);
+ empty = list_empty(&i1480u->tx_list);
+ spin_unlock_irqrestore(&i1480u->tx_list_lock, flags);
+ if (empty)
+ break;
+ count--;
+ BUG_ON(count == 0);
+ msleep(20);
+ }
+}
diff --git a/drivers/uwb/ie.c b/drivers/uwb/ie.c
new file mode 100644
index 000000000000..d54fe09a986d
--- /dev/null
+++ b/drivers/uwb/ie.c
@@ -0,0 +1,570 @@
+/*
+ * Ultra Wide Band
+ * Information Element Handling
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ * Reinette Chatre <reinette.chatre@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ */
+
+#include "uwb-internal.h"
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+/**
+ * uwb_ie_next - get the next IE in a buffer
+ * @ptr: start of the buffer containing the IE data
+ * @len: length of the buffer
+ *
+ * Both @ptr and @len are updated so subsequent calls to uwb_ie_next()
+ * will get the next IE.
+ *
+ * NULL is returned (and @ptr and @len will not be updated) if there
+ * are no more IEs in the buffer or the buffer is too short.
+ */
+struct uwb_ie_hdr *uwb_ie_next(void **ptr, size_t *len)
+{
+ struct uwb_ie_hdr *hdr;
+ size_t ie_len;
+
+ if (*len < sizeof(struct uwb_ie_hdr))
+ return NULL;
+
+ hdr = *ptr;
+ ie_len = sizeof(struct uwb_ie_hdr) + hdr->length;
+
+ if (*len < ie_len)
+ return NULL;
+
+ *ptr += ie_len;
+ *len -= ie_len;
+
+ return hdr;
+}
+EXPORT_SYMBOL_GPL(uwb_ie_next);
+
+/**
+ * Get the IEs that a radio controller is sending in its beacon
+ *
+ * @uwb_rc: UWB Radio Controller
+ * @returns: Size read from the system
+ *
+ * We don't need to lock the uwb_rc's mutex because we don't modify
+ * anything. Once done with the iedata buffer, call
+ * uwb_rc_ie_release(iedata). Don't call kfree on it.
+ */
+ssize_t uwb_rc_get_ie(struct uwb_rc *uwb_rc, struct uwb_rc_evt_get_ie **pget_ie)
+{
+ ssize_t result;
+ struct device *dev = &uwb_rc->uwb_dev.dev;
+ struct uwb_rccb *cmd = NULL;
+ struct uwb_rceb *reply = NULL;
+ struct uwb_rc_evt_get_ie *get_ie;
+
+ d_fnstart(3, dev, "(%p, %p)\n", uwb_rc, pget_ie);
+ result = -ENOMEM;
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL)
+ goto error_kzalloc;
+ cmd->bCommandType = UWB_RC_CET_GENERAL;
+ cmd->wCommand = cpu_to_le16(UWB_RC_CMD_GET_IE);
+ result = uwb_rc_vcmd(uwb_rc, "GET_IE", cmd, sizeof(*cmd),
+ UWB_RC_CET_GENERAL, UWB_RC_CMD_GET_IE,
+ &reply);
+ if (result < 0)
+ goto error_cmd;
+ get_ie = container_of(reply, struct uwb_rc_evt_get_ie, rceb);
+ if (result < sizeof(*get_ie)) {
+ dev_err(dev, "not enough data returned for decoding GET IE "
+ "(%zu bytes received vs %zu needed)\n",
+ result, sizeof(*get_ie));
+ result = -EINVAL;
+ } else if (result < sizeof(*get_ie) + le16_to_cpu(get_ie->wIELength)) {
+ dev_err(dev, "not enough data returned for decoding GET IE "
+ "payload (%zu bytes received vs %zu needed)\n", result,
+ sizeof(*get_ie) + le16_to_cpu(get_ie->wIELength));
+ result = -EINVAL;
+ } else
+ *pget_ie = get_ie;
+error_cmd:
+ kfree(cmd);
+error_kzalloc:
+ d_fnend(3, dev, "(%p, %p) = %d\n", uwb_rc, pget_ie, (int)result);
+ return result;
+}
+EXPORT_SYMBOL_GPL(uwb_rc_get_ie);
+
+
+/*
+ * Given a pointer to an IE, print it in ASCII/hex followed by a new line
+ *
+ * @ie_hdr: pointer to the IE header. Length is in there, and it is
+ * guaranteed that the ie_hdr->length bytes following it are
+ * safely accesible.
+ *
+ * @_data: context data passed from uwb_ie_for_each(), an struct output_ctx
+ */
+int uwb_ie_dump_hex(struct uwb_dev *uwb_dev, const struct uwb_ie_hdr *ie_hdr,
+ size_t offset, void *_ctx)
+{
+ struct uwb_buf_ctx *ctx = _ctx;
+ const u8 *pl = (void *)(ie_hdr + 1);
+ u8 pl_itr;
+
+ ctx->bytes += scnprintf(ctx->buf + ctx->bytes, ctx->size - ctx->bytes,
+ "%02x %02x ", (unsigned) ie_hdr->element_id,
+ (unsigned) ie_hdr->length);
+ pl_itr = 0;
+ while (pl_itr < ie_hdr->length && ctx->bytes < ctx->size)
+ ctx->bytes += scnprintf(ctx->buf + ctx->bytes,
+ ctx->size - ctx->bytes,
+ "%02x ", (unsigned) pl[pl_itr++]);
+ if (ctx->bytes < ctx->size)
+ ctx->buf[ctx->bytes++] = '\n';
+ return 0;
+}
+EXPORT_SYMBOL_GPL(uwb_ie_dump_hex);
+
+
+/**
+ * Verify that a pointer in a buffer points to valid IE
+ *
+ * @start: pointer to start of buffer in which IE appears
+ * @itr: pointer to IE inside buffer that will be verified
+ * @top: pointer to end of buffer
+ *
+ * @returns: 0 if IE is valid, <0 otherwise
+ *
+ * Verification involves checking that the buffer can contain a
+ * header and the amount of data reported in the IE header can be found in
+ * the buffer.
+ */
+static
+int uwb_rc_ie_verify(struct uwb_dev *uwb_dev, const void *start,
+ const void *itr, const void *top)
+{
+ struct device *dev = &uwb_dev->dev;
+ const struct uwb_ie_hdr *ie_hdr;
+
+ if (top - itr < sizeof(*ie_hdr)) {
+ dev_err(dev, "Bad IE: no data to decode header "
+ "(%zu bytes left vs %zu needed) at offset %zu\n",
+ top - itr, sizeof(*ie_hdr), itr - start);
+ return -EINVAL;
+ }
+ ie_hdr = itr;
+ itr += sizeof(*ie_hdr);
+ if (top - itr < ie_hdr->length) {
+ dev_err(dev, "Bad IE: not enough data for payload "
+ "(%zu bytes left vs %zu needed) at offset %zu\n",
+ top - itr, (size_t)ie_hdr->length,
+ (void *)ie_hdr - start);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+/**
+ * Walk a buffer filled with consecutive IE's a buffer
+ *
+ * @uwb_dev: UWB device this IEs belong to (for err messages mainly)
+ *
+ * @fn: function to call with each IE; if it returns 0, we keep
+ * traversing the buffer. If it returns !0, we'll stop and return
+ * that value.
+ *
+ * @data: pointer passed to @fn
+ *
+ * @buf: buffer where the consecutive IEs are located
+ *
+ * @size: size of @buf
+ *
+ * Each IE is checked for basic correctness (there is space left for
+ * the header and the payload). If that test is failed, we stop
+ * processing. For every good IE, @fn is called.
+ */
+ssize_t uwb_ie_for_each(struct uwb_dev *uwb_dev, uwb_ie_f fn, void *data,
+ const void *buf, size_t size)
+{
+ ssize_t result = 0;
+ const struct uwb_ie_hdr *ie_hdr;
+ const void *itr = buf, *top = itr + size;
+
+ while (itr < top) {
+ if (uwb_rc_ie_verify(uwb_dev, buf, itr, top) != 0)
+ break;
+ ie_hdr = itr;
+ itr += sizeof(*ie_hdr) + ie_hdr->length;
+ result = fn(uwb_dev, ie_hdr, itr - buf, data);
+ if (result != 0)
+ break;
+ }
+ return result;
+}
+EXPORT_SYMBOL_GPL(uwb_ie_for_each);
+
+
+/**
+ * Replace all IEs currently being transmitted by a device
+ *
+ * @cmd: pointer to the SET-IE command with the IEs to set
+ * @size: size of @buf
+ */
+int uwb_rc_set_ie(struct uwb_rc *rc, struct uwb_rc_cmd_set_ie *cmd)
+{
+ int result;
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_rc_evt_set_ie reply;
+
+ reply.rceb.bEventType = UWB_RC_CET_GENERAL;
+ reply.rceb.wEvent = UWB_RC_CMD_SET_IE;
+ result = uwb_rc_cmd(rc, "SET-IE", &cmd->rccb,
+ sizeof(*cmd) + le16_to_cpu(cmd->wIELength),
+ &reply.rceb, sizeof(reply));
+ if (result < 0)
+ goto error_cmd;
+ else if (result != sizeof(reply)) {
+ dev_err(dev, "SET-IE: not enough data to decode reply "
+ "(%d bytes received vs %zu needed)\n",
+ result, sizeof(reply));
+ result = -EIO;
+ } else if (reply.bResultCode != UWB_RC_RES_SUCCESS) {
+ dev_err(dev, "SET-IE: command execution failed: %s (%d)\n",
+ uwb_rc_strerror(reply.bResultCode), reply.bResultCode);
+ result = -EIO;
+ } else
+ result = 0;
+error_cmd:
+ return result;
+}
+
+/**
+ * Determine by IE id if IE is host settable
+ * WUSB 1.0 [8.6.2.8 Table 8.85]
+ *
+ * EXCEPTION:
+ * All but UWB_IE_WLP appears in Table 8.85 from WUSB 1.0. Setting this IE
+ * is required for the WLP substack to perform association with its WSS so
+ * we hope that the WUSB spec will be changed to reflect this.
+ */
+static
+int uwb_rc_ie_is_host_settable(enum uwb_ie element_id)
+{
+ if (element_id == UWB_PCA_AVAILABILITY ||
+ element_id == UWB_BP_SWITCH_IE ||
+ element_id == UWB_MAC_CAPABILITIES_IE ||
+ element_id == UWB_PHY_CAPABILITIES_IE ||
+ element_id == UWB_APP_SPEC_PROBE_IE ||
+ element_id == UWB_IDENTIFICATION_IE ||
+ element_id == UWB_MASTER_KEY_ID_IE ||
+ element_id == UWB_IE_WLP ||
+ element_id == UWB_APP_SPEC_IE)
+ return 1;
+ return 0;
+}
+
+
+/**
+ * Extract Host Settable IEs from IE
+ *
+ * @ie_data: pointer to buffer containing all IEs
+ * @size: size of buffer
+ *
+ * @returns: length of buffer that only includes host settable IEs
+ *
+ * Given a buffer of IEs we move all Host Settable IEs to front of buffer
+ * by overwriting the IEs that are not Host Settable.
+ * Buffer length is adjusted accordingly.
+ */
+static
+ssize_t uwb_rc_parse_host_settable_ie(struct uwb_dev *uwb_dev,
+ void *ie_data, size_t size)
+{
+ size_t new_len = size;
+ struct uwb_ie_hdr *ie_hdr;
+ size_t ie_length;
+ void *itr = ie_data, *top = itr + size;
+
+ while (itr < top) {
+ if (uwb_rc_ie_verify(uwb_dev, ie_data, itr, top) != 0)
+ break;
+ ie_hdr = itr;
+ ie_length = sizeof(*ie_hdr) + ie_hdr->length;
+ if (uwb_rc_ie_is_host_settable(ie_hdr->element_id)) {
+ itr += ie_length;
+ } else {
+ memmove(itr, itr + ie_length, top - (itr + ie_length));
+ new_len -= ie_length;
+ top -= ie_length;
+ }
+ }
+ return new_len;
+}
+
+
+/* Cleanup the whole IE management subsystem */
+void uwb_rc_ie_init(struct uwb_rc *uwb_rc)
+{
+ mutex_init(&uwb_rc->ies_mutex);
+}
+
+
+/**
+ * Set up cache for host settable IEs currently being transmitted
+ *
+ * First we just call GET-IE to get the current IEs being transmitted
+ * (or we workaround and pretend we did) and (because the format is
+ * the same) reuse that as the IE cache (with the command prefix, as
+ * explained in 'struct uwb_rc').
+ *
+ * @returns: size of cache created
+ */
+ssize_t uwb_rc_ie_setup(struct uwb_rc *uwb_rc)
+{
+ struct device *dev = &uwb_rc->uwb_dev.dev;
+ ssize_t result;
+ size_t capacity;
+ struct uwb_rc_evt_get_ie *ie_info;
+
+ d_fnstart(3, dev, "(%p)\n", uwb_rc);
+ mutex_lock(&uwb_rc->ies_mutex);
+ result = uwb_rc_get_ie(uwb_rc, &ie_info);
+ if (result < 0)
+ goto error_get_ie;
+ capacity = result;
+ d_printf(5, dev, "Got IEs %zu bytes (%zu long at %p)\n", result,
+ (size_t)le16_to_cpu(ie_info->wIELength), ie_info);
+
+ /* Remove IEs that host should not set. */
+ result = uwb_rc_parse_host_settable_ie(&uwb_rc->uwb_dev,
+ ie_info->IEData, le16_to_cpu(ie_info->wIELength));
+ if (result < 0)
+ goto error_parse;
+ d_printf(5, dev, "purged non-settable IEs to %zu bytes\n", result);
+ uwb_rc->ies = (void *) ie_info;
+ uwb_rc->ies->rccb.bCommandType = UWB_RC_CET_GENERAL;
+ uwb_rc->ies->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_SET_IE);
+ uwb_rc->ies_capacity = capacity;
+ d_printf(5, dev, "IE cache at %p %zu bytes, %zu capacity\n",
+ ie_info, result, capacity);
+ result = 0;
+error_parse:
+error_get_ie:
+ mutex_unlock(&uwb_rc->ies_mutex);
+ d_fnend(3, dev, "(%p) = %zu\n", uwb_rc, result);
+ return result;
+}
+
+
+/* Cleanup the whole IE management subsystem */
+void uwb_rc_ie_release(struct uwb_rc *uwb_rc)
+{
+ kfree(uwb_rc->ies);
+ uwb_rc->ies = NULL;
+ uwb_rc->ies_capacity = 0;
+}
+
+
+static
+int __acc_size(struct uwb_dev *uwb_dev, const struct uwb_ie_hdr *ie_hdr,
+ size_t offset, void *_ctx)
+{
+ size_t *acc_size = _ctx;
+ *acc_size += sizeof(*ie_hdr) + ie_hdr->length;
+ d_printf(6, &uwb_dev->dev, "new acc size %zu\n", *acc_size);
+ return 0;
+}
+
+
+/**
+ * Add a new IE to IEs currently being transmitted by device
+ *
+ * @ies: the buffer containing the new IE or IEs to be added to
+ * the device's beacon. The buffer will be verified for
+ * consistence (meaning the headers should be right) and
+ * consistent with the buffer size.
+ * @size: size of @ies (in bytes, total buffer size)
+ * @returns: 0 if ok, <0 errno code on error
+ *
+ * According to WHCI 0.95 [4.13.6] the driver will only receive the RCEB
+ * after the device sent the first beacon that includes the IEs specified
+ * in the SET IE command. We thus cannot send this command if the device is
+ * not beaconing. Instead, a SET IE command will be sent later right after
+ * we start beaconing.
+ *
+ * Setting an IE on the device will overwrite all current IEs in device. So
+ * we take the current IEs being transmitted by the device, append the
+ * new one, and call SET IE with all the IEs needed.
+ *
+ * The local IE cache will only be updated with the new IE if SET IE
+ * completed successfully.
+ */
+int uwb_rc_ie_add(struct uwb_rc *uwb_rc,
+ const struct uwb_ie_hdr *ies, size_t size)
+{
+ int result = 0;
+ struct device *dev = &uwb_rc->uwb_dev.dev;
+ struct uwb_rc_cmd_set_ie *new_ies;
+ size_t ies_size, total_size, acc_size = 0;
+
+ if (uwb_rc->ies == NULL)
+ return -ESHUTDOWN;
+ uwb_ie_for_each(&uwb_rc->uwb_dev, __acc_size, &acc_size, ies, size);
+ if (acc_size != size) {
+ dev_err(dev, "BUG: bad IEs, misconstructed headers "
+ "[%zu bytes reported vs %zu calculated]\n",
+ size, acc_size);
+ WARN_ON(1);
+ return -EINVAL;
+ }
+ mutex_lock(&uwb_rc->ies_mutex);
+ ies_size = le16_to_cpu(uwb_rc->ies->wIELength);
+ total_size = sizeof(*uwb_rc->ies) + ies_size;
+ if (total_size + size > uwb_rc->ies_capacity) {
+ d_printf(4, dev, "Reallocating IE cache from %p capacity %zu "
+ "to capacity %zu\n", uwb_rc->ies, uwb_rc->ies_capacity,
+ total_size + size);
+ new_ies = kzalloc(total_size + size, GFP_KERNEL);
+ if (new_ies == NULL) {
+ dev_err(dev, "No memory for adding new IE\n");
+ result = -ENOMEM;
+ goto error_alloc;
+ }
+ memcpy(new_ies, uwb_rc->ies, total_size);
+ uwb_rc->ies_capacity = total_size + size;
+ kfree(uwb_rc->ies);
+ uwb_rc->ies = new_ies;
+ d_printf(4, dev, "New IE cache at %p capacity %zu\n",
+ uwb_rc->ies, uwb_rc->ies_capacity);
+ }
+ memcpy((void *)uwb_rc->ies + total_size, ies, size);
+ uwb_rc->ies->wIELength = cpu_to_le16(ies_size + size);
+ if (uwb_rc->beaconing != -1) {
+ result = uwb_rc_set_ie(uwb_rc, uwb_rc->ies);
+ if (result < 0) {
+ dev_err(dev, "Cannot set new IE on device: %d\n",
+ result);
+ uwb_rc->ies->wIELength = cpu_to_le16(ies_size);
+ } else
+ result = 0;
+ }
+ d_printf(4, dev, "IEs now occupy %hu bytes of %zu capacity at %p\n",
+ le16_to_cpu(uwb_rc->ies->wIELength), uwb_rc->ies_capacity,
+ uwb_rc->ies);
+error_alloc:
+ mutex_unlock(&uwb_rc->ies_mutex);
+ return result;
+}
+EXPORT_SYMBOL_GPL(uwb_rc_ie_add);
+
+
+/*
+ * Remove an IE from internal cache
+ *
+ * We are dealing with our internal IE cache so no need to verify that the
+ * IEs are valid (it has been done already).
+ *
+ * Should be called with ies_mutex held
+ *
+ * We do not break out once an IE is found in the cache. It is currently
+ * possible to have more than one IE with the same ID included in the
+ * beacon. We don't reallocate, we just mark the size smaller.
+ */
+static
+int uwb_rc_ie_cache_rm(struct uwb_rc *uwb_rc, enum uwb_ie to_remove)
+{
+ struct uwb_ie_hdr *ie_hdr;
+ size_t new_len = le16_to_cpu(uwb_rc->ies->wIELength);
+ void *itr = uwb_rc->ies->IEData;
+ void *top = itr + new_len;
+
+ while (itr < top) {
+ ie_hdr = itr;
+ if (ie_hdr->element_id != to_remove) {
+ itr += sizeof(*ie_hdr) + ie_hdr->length;
+ } else {
+ int ie_length;
+ ie_length = sizeof(*ie_hdr) + ie_hdr->length;
+ if (top - itr != ie_length)
+ memmove(itr, itr + ie_length, top - itr + ie_length);
+ top -= ie_length;
+ new_len -= ie_length;
+ }
+ }
+ uwb_rc->ies->wIELength = cpu_to_le16(new_len);
+ return 0;
+}
+
+
+/**
+ * Remove an IE currently being transmitted by device
+ *
+ * @element_id: id of IE to be removed from device's beacon
+ */
+int uwb_rc_ie_rm(struct uwb_rc *uwb_rc, enum uwb_ie element_id)
+{
+ struct device *dev = &uwb_rc->uwb_dev.dev;
+ int result;
+
+ if (uwb_rc->ies == NULL)
+ return -ESHUTDOWN;
+ mutex_lock(&uwb_rc->ies_mutex);
+ result = uwb_rc_ie_cache_rm(uwb_rc, element_id);
+ if (result < 0)
+ dev_err(dev, "Cannot remove IE from cache.\n");
+ if (uwb_rc->beaconing != -1) {
+ result = uwb_rc_set_ie(uwb_rc, uwb_rc->ies);
+ if (result < 0)
+ dev_err(dev, "Cannot set new IE on device.\n");
+ }
+ mutex_unlock(&uwb_rc->ies_mutex);
+ return result;
+}
+EXPORT_SYMBOL_GPL(uwb_rc_ie_rm);
+
+
+/**
+ * Create and set new Identification IE
+ *
+ * Currently only sets the Vendor ID. The Vendor ID is set from the OUI,
+ * which is obtained from the first three bytes from the MAC address.
+ */
+int uwb_rc_set_identification_ie(struct uwb_rc *uwb_rc)
+{
+ struct {
+ struct uwb_identification_ie id_ie;
+ struct uwb_dev_info dev_info;
+ struct uwb_vendor_id vendor_id;
+ } ie_data;
+
+ ie_data.id_ie.hdr.element_id = UWB_IDENTIFICATION_IE;
+ ie_data.id_ie.hdr.length = sizeof(struct uwb_dev_info) +
+ sizeof(struct uwb_vendor_id);
+
+ ie_data.dev_info.type = UWB_DEV_INFO_VENDOR_ID;
+ ie_data.dev_info.length = sizeof(struct uwb_vendor_id);
+
+ ie_data.vendor_id.data[0] = uwb_rc->uwb_dev.mac_addr.data[0];
+ ie_data.vendor_id.data[1] = uwb_rc->uwb_dev.mac_addr.data[1];
+ ie_data.vendor_id.data[2] = uwb_rc->uwb_dev.mac_addr.data[2];
+
+ return uwb_rc_ie_add(uwb_rc, &ie_data.id_ie.hdr, sizeof(ie_data));
+}
diff --git a/drivers/uwb/lc-dev.c b/drivers/uwb/lc-dev.c
new file mode 100644
index 000000000000..5f73ad4d2311
--- /dev/null
+++ b/drivers/uwb/lc-dev.c
@@ -0,0 +1,517 @@
+/*
+ * Ultra Wide Band
+ * Life cycle of devices
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kdev_t.h>
+#include <linux/random.h>
+#include "uwb-internal.h"
+
+#define D_LOCAL 1
+#include <linux/uwb/debug.h>
+
+
+/* We initialize addresses to 0xff (invalid, as it is bcast) */
+static inline void uwb_dev_addr_init(struct uwb_dev_addr *addr)
+{
+ memset(&addr->data, 0xff, sizeof(addr->data));
+}
+
+static inline void uwb_mac_addr_init(struct uwb_mac_addr *addr)
+{
+ memset(&addr->data, 0xff, sizeof(addr->data));
+}
+
+/* @returns !0 if a device @addr is a broadcast address */
+static inline int uwb_dev_addr_bcast(const struct uwb_dev_addr *addr)
+{
+ static const struct uwb_dev_addr bcast = { .data = { 0xff, 0xff } };
+ return !uwb_dev_addr_cmp(addr, &bcast);
+}
+
+/*
+ * Add callback @new to be called when an event occurs in @rc.
+ */
+int uwb_notifs_register(struct uwb_rc *rc, struct uwb_notifs_handler *new)
+{
+ if (mutex_lock_interruptible(&rc->notifs_chain.mutex))
+ return -ERESTARTSYS;
+ list_add(&new->list_node, &rc->notifs_chain.list);
+ mutex_unlock(&rc->notifs_chain.mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(uwb_notifs_register);
+
+/*
+ * Remove event handler (callback)
+ */
+int uwb_notifs_deregister(struct uwb_rc *rc, struct uwb_notifs_handler *entry)
+{
+ if (mutex_lock_interruptible(&rc->notifs_chain.mutex))
+ return -ERESTARTSYS;
+ list_del(&entry->list_node);
+ mutex_unlock(&rc->notifs_chain.mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(uwb_notifs_deregister);
+
+/*
+ * Notify all event handlers of a given event on @rc
+ *
+ * We are called with a valid reference to the device. Obtain another
+ * reference before handing off to callback, release on return.
+ */
+void uwb_notify(struct uwb_rc *rc, struct uwb_dev *uwb_dev, enum uwb_notifs event)
+{
+ struct uwb_notifs_handler *handler;
+ if (mutex_lock_interruptible(&rc->notifs_chain.mutex))
+ return;
+ if (!list_empty(&rc->notifs_chain.list)) {
+ uwb_dev_get(uwb_dev);
+ list_for_each_entry(handler, &rc->notifs_chain.list,
+ list_node) {
+ handler->cb(handler->data, uwb_dev, event);
+ }
+ uwb_dev_put(uwb_dev);
+ }
+ mutex_unlock(&rc->notifs_chain.mutex);
+}
+
+/*
+ * Release the backing device of a uwb_dev that has been dynamically allocated.
+ */
+static void uwb_dev_sys_release(struct device *dev)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+
+ d_fnstart(4, NULL, "(dev %p uwb_dev %p)\n", dev, uwb_dev);
+ uwb_bce_put(uwb_dev->bce);
+ d_printf(0, &uwb_dev->dev, "uwb_dev %p freed\n", uwb_dev);
+ memset(uwb_dev, 0x69, sizeof(*uwb_dev));
+ kfree(uwb_dev);
+ d_fnend(4, NULL, "(dev %p uwb_dev %p) = void\n", dev, uwb_dev);
+}
+
+/*
+ * Initialize a UWB device instance
+ *
+ * Alloc, zero and call this function.
+ */
+void uwb_dev_init(struct uwb_dev *uwb_dev)
+{
+ mutex_init(&uwb_dev->mutex);
+ device_initialize(&uwb_dev->dev);
+ uwb_dev->dev.release = uwb_dev_sys_release;
+ uwb_dev_addr_init(&uwb_dev->dev_addr);
+ uwb_mac_addr_init(&uwb_dev->mac_addr);
+ bitmap_fill(uwb_dev->streams, UWB_NUM_GLOBAL_STREAMS);
+}
+
+static ssize_t uwb_dev_EUI_48_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ char addr[UWB_ADDR_STRSIZE];
+
+ uwb_mac_addr_print(addr, sizeof(addr), &uwb_dev->mac_addr);
+ return sprintf(buf, "%s\n", addr);
+}
+static DEVICE_ATTR(EUI_48, S_IRUGO, uwb_dev_EUI_48_show, NULL);
+
+static ssize_t uwb_dev_DevAddr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ char addr[UWB_ADDR_STRSIZE];
+
+ uwb_dev_addr_print(addr, sizeof(addr), &uwb_dev->dev_addr);
+ return sprintf(buf, "%s\n", addr);
+}
+static DEVICE_ATTR(DevAddr, S_IRUGO, uwb_dev_DevAddr_show, NULL);
+
+/*
+ * Show the BPST of this device.
+ *
+ * Calculated from the receive time of the device's beacon and it's
+ * slot number.
+ */
+static ssize_t uwb_dev_BPST_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_beca_e *bce;
+ struct uwb_beacon_frame *bf;
+ u16 bpst;
+
+ if (&uwb_dev->rc->uwb_dev == uwb_dev) /* local radio controller */
+ return 0;
+
+ bce = uwb_dev->bce;
+ mutex_lock(&bce->mutex);
+ bf = (struct uwb_beacon_frame *)bce->be->BeaconInfo;
+ bpst = bce->be->wBPSTOffset
+ - (u16)(bf->Beacon_Slot_Number * UWB_BEACON_SLOT_LENGTH_US);
+ mutex_unlock(&bce->mutex);
+
+ return sprintf(buf, "%d\n", bpst);
+}
+static DEVICE_ATTR(BPST, S_IRUGO, uwb_dev_BPST_show, NULL);
+
+/*
+ * Show the IEs a device is beaconing
+ *
+ * We need to access the beacon cache, so we just lock it really
+ * quick, print the IEs and unlock.
+ *
+ * We have a reference on the cache entry, so that should be
+ * quite safe.
+ */
+static ssize_t uwb_dev_IEs_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t result;
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+
+ if (&uwb_dev->rc->uwb_dev == uwb_dev) /* This is a local radio controller */
+ result = uwb_rc_print_IEs(uwb_dev->rc, buf, PAGE_SIZE);
+ else
+ result = uwb_bce_print_IEs(uwb_dev, uwb_dev->bce,
+ buf, PAGE_SIZE);
+ return result;
+}
+static DEVICE_ATTR(IEs, S_IRUGO | S_IWUSR, uwb_dev_IEs_show, NULL);
+
+static ssize_t uwb_dev_LQE_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_beca_e *bce = uwb_dev->bce;
+ size_t result;
+
+ /* is this a local device? */
+ if (bce == NULL)
+ return 0;
+ mutex_lock(&bce->mutex);
+ result = stats_show(&uwb_dev->bce->lqe_stats, buf);
+ mutex_unlock(&bce->mutex);
+ return result;
+}
+
+static ssize_t uwb_dev_LQE_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_beca_e *bce = uwb_dev->bce;
+ ssize_t result;
+
+ /* is this a local device? */
+ if (bce == NULL)
+ return 0;
+ mutex_lock(&bce->mutex);
+ result = stats_store(&uwb_dev->bce->lqe_stats, buf, size);
+ mutex_unlock(&bce->mutex);
+ return result;
+}
+static DEVICE_ATTR(LQE, S_IRUGO | S_IWUSR, uwb_dev_LQE_show, uwb_dev_LQE_store);
+
+static ssize_t uwb_dev_RSSI_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_beca_e *bce = uwb_dev->bce;
+ size_t result;
+
+ /* is this a local device? */
+ if (bce == NULL)
+ return 0;
+ mutex_lock(&bce->mutex);
+ result = stats_show(&uwb_dev->bce->rssi_stats, buf);
+ mutex_unlock(&bce->mutex);
+ return result;
+}
+
+static ssize_t uwb_dev_RSSI_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_beca_e *bce = uwb_dev->bce;
+ ssize_t result;
+
+ /* is this a local device? */
+ if (bce == NULL)
+ return 0;
+ mutex_lock(&bce->mutex);
+ result = stats_store(&uwb_dev->bce->rssi_stats, buf, size);
+ mutex_unlock(&bce->mutex);
+ return result;
+}
+static DEVICE_ATTR(RSSI, S_IRUGO | S_IWUSR, uwb_dev_RSSI_show, uwb_dev_RSSI_store);
+
+
+static struct attribute *dev_attrs[] = {
+ &dev_attr_EUI_48.attr,
+ &dev_attr_DevAddr.attr,
+ &dev_attr_BPST.attr,
+ &dev_attr_IEs.attr,
+ &dev_attr_LQE.attr,
+ &dev_attr_RSSI.attr,
+ NULL,
+};
+
+static struct attribute_group dev_attr_group = {
+ .name = "uwb",
+ .attrs = dev_attrs,
+};
+
+static struct attribute_group *groups[] = {
+ &dev_attr_group,
+ NULL,
+};
+
+/**
+ * Device SYSFS registration
+ *
+ *
+ */
+static int __uwb_dev_sys_add(struct uwb_dev *uwb_dev, struct device *parent_dev)
+{
+ int result;
+ struct device *dev;
+
+ d_fnstart(4, NULL, "(uwb_dev %p parent_dev %p)\n", uwb_dev, parent_dev);
+ BUG_ON(parent_dev == NULL);
+
+ dev = &uwb_dev->dev;
+ dev->groups = groups;
+ dev->parent = parent_dev;
+ dev_set_drvdata(dev, uwb_dev);
+
+ result = device_add(dev);
+ d_fnend(4, NULL, "(uwb_dev %p parent_dev %p) = %d\n", uwb_dev, parent_dev, result);
+ return result;
+}
+
+
+static void __uwb_dev_sys_rm(struct uwb_dev *uwb_dev)
+{
+ d_fnstart(4, NULL, "(uwb_dev %p)\n", uwb_dev);
+ dev_set_drvdata(&uwb_dev->dev, NULL);
+ device_del(&uwb_dev->dev);
+ d_fnend(4, NULL, "(uwb_dev %p) = void\n", uwb_dev);
+}
+
+
+/**
+ * Register and initialize a new UWB device
+ *
+ * Did you call uwb_dev_init() on it?
+ *
+ * @parent_rc: is the parent radio controller who has the link to the
+ * device. When registering the UWB device that is a UWB
+ * Radio Controller, we point back to it.
+ *
+ * If registering the device that is part of a radio, caller has set
+ * rc->uwb_dev->dev. Otherwise it is to be left NULL--a new one will
+ * be allocated.
+ */
+int uwb_dev_add(struct uwb_dev *uwb_dev, struct device *parent_dev,
+ struct uwb_rc *parent_rc)
+{
+ int result;
+ struct device *dev;
+
+ BUG_ON(uwb_dev == NULL);
+ BUG_ON(parent_dev == NULL);
+ BUG_ON(parent_rc == NULL);
+
+ mutex_lock(&uwb_dev->mutex);
+ dev = &uwb_dev->dev;
+ uwb_dev->rc = parent_rc;
+ if (strlen(dev_name(dev)) == 0) /* radios print their own! */
+ uwb_mac_addr_print((char *)dev_name(dev), strlen(dev_name(dev)),
+ &uwb_dev->mac_addr);
+ result = __uwb_dev_sys_add(uwb_dev, parent_dev);
+ if (result < 0)
+ printk(KERN_ERR "UWB: unable to register dev %s with sysfs: %d\n",
+ dev_name(dev), result);
+ mutex_unlock(&uwb_dev->mutex);
+ return result;
+}
+
+
+void uwb_dev_rm(struct uwb_dev *uwb_dev)
+{
+ mutex_lock(&uwb_dev->mutex);
+ __uwb_dev_sys_rm(uwb_dev);
+ mutex_unlock(&uwb_dev->mutex);
+}
+
+
+static
+int __uwb_dev_try_get(struct device *dev, void *__target_uwb_dev)
+{
+ struct uwb_dev *target_uwb_dev = __target_uwb_dev;
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ if (uwb_dev == target_uwb_dev) {
+ uwb_dev_get(uwb_dev);
+ return 1;
+ } else
+ return 0;
+}
+
+
+/**
+ * Given a UWB device descriptor, validate and refcount it
+ *
+ * @returns NULL if the device does not exist or is quiescing; the ptr to
+ * it otherwise.
+ */
+struct uwb_dev *uwb_dev_try_get(struct uwb_rc *rc, struct uwb_dev *uwb_dev)
+{
+ if (uwb_dev_for_each(rc, __uwb_dev_try_get, uwb_dev))
+ return uwb_dev;
+ else
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(uwb_dev_try_get);
+
+
+/**
+ * Remove a device from the system [grunt for other functions]
+ */
+int __uwb_dev_offair(struct uwb_dev *uwb_dev, struct uwb_rc *rc)
+{
+ struct device *dev = &uwb_dev->dev;
+ char macbuf[UWB_ADDR_STRSIZE], devbuf[UWB_ADDR_STRSIZE];
+
+ d_fnstart(3, NULL, "(dev %p [uwb_dev %p], uwb_rc %p)\n", dev, uwb_dev, rc);
+ uwb_mac_addr_print(macbuf, sizeof(macbuf), &uwb_dev->mac_addr);
+ uwb_dev_addr_print(devbuf, sizeof(devbuf), &uwb_dev->dev_addr);
+ dev_info(dev, "uwb device (mac %s dev %s) disconnected from %s %s\n",
+ macbuf, devbuf,
+ rc? rc->uwb_dev.dev.parent->bus->name : "n/a",
+ rc? dev_name(rc->uwb_dev.dev.parent) : "");
+ uwb_dev_rm(uwb_dev);
+ uwb_dev_put(uwb_dev); /* for the creation in _onair() */
+ d_fnend(3, NULL, "(dev %p [uwb_dev %p], uwb_rc %p) = 0\n", dev, uwb_dev, rc);
+ return 0;
+}
+
+
+/**
+ * A device went off the air, clean up after it!
+ *
+ * This is called by the UWB Daemon (through the beacon purge function
+ * uwb_bcn_cache_purge) when it is detected that a device has been in
+ * radio silence for a while.
+ *
+ * If this device is actually a local radio controller we don't need
+ * to go through the offair process, as it is not registered as that.
+ *
+ * NOTE: uwb_bcn_cache.mutex is held!
+ */
+void uwbd_dev_offair(struct uwb_beca_e *bce)
+{
+ struct uwb_rc *rc;
+ struct uwb_dev *uwb_dev;
+
+ uwb_dev = bce->uwb_dev;
+ rc = uwb_dev->rc;
+
+ uwb_notify(rc, uwb_dev, UWB_NOTIF_OFFAIR);
+ __uwb_dev_offair(uwb_dev, rc);
+}
+
+
+/**
+ * A device went on the air, start it up!
+ *
+ * This is called by the UWB Daemon when it is detected that a device
+ * has popped up in the radio range of the radio controller.
+ *
+ * It will just create the freaking device, register the beacon and
+ * stuff and yatla, done.
+ *
+ *
+ * NOTE: uwb_beca.mutex is held, bce->mutex is held
+ */
+void uwbd_dev_onair(struct uwb_rc *rc, struct uwb_beca_e *bce)
+{
+ int result;
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_dev *uwb_dev;
+ char macbuf[UWB_ADDR_STRSIZE], devbuf[UWB_ADDR_STRSIZE];
+
+ uwb_mac_addr_print(macbuf, sizeof(macbuf), bce->mac_addr);
+ uwb_dev_addr_print(devbuf, sizeof(devbuf), &bce->dev_addr);
+ uwb_dev = kcalloc(1, sizeof(*uwb_dev), GFP_KERNEL);
+ if (uwb_dev == NULL) {
+ dev_err(dev, "new device %s: Cannot allocate memory\n",
+ macbuf);
+ return;
+ }
+ uwb_dev_init(uwb_dev); /* This sets refcnt to one, we own it */
+ uwb_dev->mac_addr = *bce->mac_addr;
+ uwb_dev->dev_addr = bce->dev_addr;
+ result = uwb_dev_add(uwb_dev, &rc->uwb_dev.dev, rc);
+ if (result < 0) {
+ dev_err(dev, "new device %s: cannot instantiate device\n",
+ macbuf);
+ goto error_dev_add;
+ }
+ /* plug the beacon cache */
+ bce->uwb_dev = uwb_dev;
+ uwb_dev->bce = bce;
+ uwb_bce_get(bce); /* released in uwb_dev_sys_release() */
+ dev_info(dev, "uwb device (mac %s dev %s) connected to %s %s\n",
+ macbuf, devbuf, rc->uwb_dev.dev.parent->bus->name,
+ dev_name(rc->uwb_dev.dev.parent));
+ uwb_notify(rc, uwb_dev, UWB_NOTIF_ONAIR);
+ return;
+
+error_dev_add:
+ kfree(uwb_dev);
+ return;
+}
+
+/**
+ * Iterate over the list of UWB devices, calling a @function on each
+ *
+ * See docs for bus_for_each()....
+ *
+ * @rc: radio controller for the devices.
+ * @function: function to call.
+ * @priv: data to pass to @function.
+ * @returns: 0 if no invocation of function() returned a value
+ * different to zero. That value otherwise.
+ */
+int uwb_dev_for_each(struct uwb_rc *rc, uwb_dev_for_each_f function, void *priv)
+{
+ return device_for_each_child(&rc->uwb_dev.dev, priv, function);
+}
+EXPORT_SYMBOL_GPL(uwb_dev_for_each);
diff --git a/drivers/uwb/lc-rc.c b/drivers/uwb/lc-rc.c
new file mode 100644
index 000000000000..f314b98eab61
--- /dev/null
+++ b/drivers/uwb/lc-rc.c
@@ -0,0 +1,485 @@
+/*
+ * Ultra Wide Band
+ * Life cycle of radio controllers
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ *
+ * A UWB radio controller is also a UWB device, so it embeds one...
+ *
+ * List of RCs comes from the 'struct class uwb_rc_class'.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/random.h>
+#include <linux/kdev_t.h>
+#include <linux/etherdevice.h>
+#include <linux/usb.h>
+
+#define D_LOCAL 1
+#include <linux/uwb/debug.h>
+#include "uwb-internal.h"
+
+
+struct uwb_rcs {
+ size_t count;
+ struct mutex mutex;
+};
+
+/** List of UWB RCs we keep around. */
+static
+struct uwb_rcs uwb_rcs = {
+ .count = 0,
+ .mutex = __MUTEX_INITIALIZER(uwb_rcs.mutex),
+};
+
+
+/**
+ * Release the backing device of a uwb_rc that has been dynamically allocated.
+ */
+static void uwb_rc_sys_release(struct device *dev)
+{
+ struct uwb_dev *uwb_dev = container_of(dev, struct uwb_dev, dev);
+ struct uwb_rc *rc = container_of(uwb_dev, struct uwb_rc, uwb_dev);
+
+ uwb_rc_neh_destroy(rc);
+ uwb_rc_ie_release(rc);
+ d_printf(1, dev, "freed uwb_rc %p\n", rc);
+ kfree(rc);
+}
+
+
+void uwb_rc_init(struct uwb_rc *rc)
+{
+ struct uwb_dev *uwb_dev = &rc->uwb_dev;
+
+ uwb_dev_init(uwb_dev);
+ rc->uwb_dev.dev.class = &uwb_rc_class;
+ rc->uwb_dev.dev.release = uwb_rc_sys_release;
+ uwb_rc_neh_create(rc);
+ rc->beaconing = -1;
+ rc->scan_type = UWB_SCAN_DISABLED;
+ INIT_LIST_HEAD(&rc->notifs_chain.list);
+ mutex_init(&rc->notifs_chain.mutex);
+ uwb_drp_avail_init(rc);
+ uwb_rc_ie_init(rc);
+ uwb_rsv_init(rc);
+ uwb_rc_pal_init(rc);
+}
+EXPORT_SYMBOL_GPL(uwb_rc_init);
+
+
+struct uwb_rc *uwb_rc_alloc(void)
+{
+ struct uwb_rc *rc;
+ rc = kzalloc(sizeof(*rc), GFP_KERNEL);
+ if (rc == NULL)
+ return NULL;
+ uwb_rc_init(rc);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(uwb_rc_alloc);
+
+static struct attribute *rc_attrs[] = {
+ &dev_attr_mac_address.attr,
+ &dev_attr_scan.attr,
+ &dev_attr_beacon.attr,
+ &dev_attr_event_stats.attr,
+ NULL,
+};
+
+static struct attribute_group rc_attr_group = {
+ .name = "uwb_rc",
+ .attrs = rc_attrs,
+};
+
+/*
+ * Registration of sysfs specific stuff
+ */
+static int uwb_rc_sys_add(struct uwb_rc *rc)
+{
+ return sysfs_create_group(&rc->uwb_dev.dev.kobj, &rc_attr_group);
+}
+
+
+static void __uwb_rc_sys_rm(struct uwb_rc *rc)
+{
+ sysfs_remove_group(&rc->uwb_dev.dev.kobj, &rc_attr_group);
+}
+
+/**
+ * uwb_rc_mac_addr_setup - get an RC's EUI-48 address or set it
+ * @rc: the radio controller.
+ *
+ * If the EUI-48 address is 00:00:00:00:00:00 or FF:FF:FF:FF:FF:FF
+ * then a random locally administered EUI-48 is generated and set on
+ * the device. The probability of address collisions is sufficiently
+ * unlikely (1/2^40 = 9.1e-13) that they're not checked for.
+ */
+static
+int uwb_rc_mac_addr_setup(struct uwb_rc *rc)
+{
+ int result;
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_dev *uwb_dev = &rc->uwb_dev;
+ char devname[UWB_ADDR_STRSIZE];
+ struct uwb_mac_addr addr;
+
+ result = uwb_rc_mac_addr_get(rc, &addr);
+ if (result < 0) {
+ dev_err(dev, "cannot retrieve UWB EUI-48 address: %d\n", result);
+ return result;
+ }
+
+ if (uwb_mac_addr_unset(&addr) || uwb_mac_addr_bcast(&addr)) {
+ addr.data[0] = 0x02; /* locally adminstered and unicast */
+ get_random_bytes(&addr.data[1], sizeof(addr.data)-1);
+
+ result = uwb_rc_mac_addr_set(rc, &addr);
+ if (result < 0) {
+ uwb_mac_addr_print(devname, sizeof(devname), &addr);
+ dev_err(dev, "cannot set EUI-48 address %s: %d\n",
+ devname, result);
+ return result;
+ }
+ }
+ uwb_dev->mac_addr = addr;
+ return 0;
+}
+
+
+
+static int uwb_rc_setup(struct uwb_rc *rc)
+{
+ int result;
+ struct device *dev = &rc->uwb_dev.dev;
+
+ result = uwb_rc_reset(rc);
+ if (result < 0) {
+ dev_err(dev, "cannot reset UWB radio: %d\n", result);
+ goto error;
+ }
+ result = uwb_rc_mac_addr_setup(rc);
+ if (result < 0) {
+ dev_err(dev, "cannot setup UWB MAC address: %d\n", result);
+ goto error;
+ }
+ result = uwb_rc_dev_addr_assign(rc);
+ if (result < 0) {
+ dev_err(dev, "cannot assign UWB DevAddr: %d\n", result);
+ goto error;
+ }
+ result = uwb_rc_ie_setup(rc);
+ if (result < 0) {
+ dev_err(dev, "cannot setup IE subsystem: %d\n", result);
+ goto error_ie_setup;
+ }
+ result = uwb_rc_set_identification_ie(rc);
+ if (result < 0) {
+ dev_err(dev, "cannot set Identification IE: %d\n",
+ result);
+ goto error_set_id_ie;
+ }
+ result = uwb_rsv_setup(rc);
+ if (result < 0) {
+ dev_err(dev, "cannot setup reservation subsystem: %d\n", result);
+ goto error_rsv_setup;
+ }
+ uwb_dbg_add_rc(rc);
+ return 0;
+
+error_rsv_setup:
+error_set_id_ie:
+ uwb_rc_ie_release(rc);
+error_ie_setup:
+error:
+ return result;
+}
+
+
+/**
+ * Register a new UWB radio controller
+ *
+ * Did you call uwb_rc_init() on your rc?
+ *
+ * We assume that this is being called with a > 0 refcount on
+ * it [through ops->{get|put}_device(). We'll take our own, though.
+ *
+ * @parent_dev is our real device, the one that provides the actual UWB device
+ */
+int uwb_rc_add(struct uwb_rc *rc, struct device *parent_dev, void *priv,
+ int (*cmd)(struct uwb_rc *, const struct uwb_rccb *, size_t),
+ int (*filter_cmd)(struct uwb_rc *, struct uwb_rccb **, size_t *),
+ int (*filter_event)(struct uwb_rc *, struct uwb_rceb **,
+ const size_t, size_t *, size_t *))
+{
+ int result;
+ size_t rc_count;
+ struct device *dev;
+ char macbuf[UWB_ADDR_STRSIZE], devbuf[UWB_ADDR_STRSIZE];
+
+ mutex_lock(&uwb_rcs.mutex);
+ rc_count = uwb_rcs.count++;
+ mutex_unlock(&uwb_rcs.mutex);
+
+ dev = &rc->uwb_dev.dev;
+ dev_set_name(dev, "uwb%d", (int)rc_count);
+
+ rc->priv = priv;
+ rc->cmd = cmd;
+ rc->filter_cmd = filter_cmd;
+ rc->filter_event = filter_event;
+
+ result = uwb_rc_setup(rc);
+ if (result < 0) {
+ dev_err(dev, "cannot setup UWB radio controller: %d\n", result);
+ goto error_rc_setup;
+ }
+
+ result = uwb_dev_add(&rc->uwb_dev, parent_dev, rc);
+ if (result < 0 && result != -EADDRNOTAVAIL)
+ goto error_dev_add;
+
+ result = uwb_rc_sys_add(rc);
+ if (result < 0) {
+ dev_err(parent_dev, "cannot register UWB radio controller "
+ "dev attributes: %d\n", result);
+ goto error_sys_add;
+ }
+
+ uwb_mac_addr_print(macbuf, sizeof(macbuf), &rc->uwb_dev.mac_addr);
+ uwb_dev_addr_print(devbuf, sizeof(devbuf), &rc->uwb_dev.dev_addr);
+ dev_info(dev,
+ "new uwb radio controller (mac %s dev %s) on %s %s\n",
+ macbuf, devbuf, parent_dev->bus->name, dev_name(parent_dev));
+ rc->ready = 1;
+ return 0;
+
+error_sys_add:
+ uwb_dev_rm(&rc->uwb_dev);
+error_dev_add:
+error_rc_setup:
+ return result;
+}
+EXPORT_SYMBOL_GPL(uwb_rc_add);
+
+
+static int uwb_dev_offair_helper(struct device *dev, void *priv)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+
+ return __uwb_dev_offair(uwb_dev, uwb_dev->rc);
+}
+
+/*
+ * Remove a Radio Controller; stop beaconing/scanning, disconnect all children
+ */
+void uwb_rc_rm(struct uwb_rc *rc)
+{
+ rc->ready = 0;
+
+ uwb_dbg_del_rc(rc);
+ uwb_rsv_cleanup(rc);
+ uwb_rc_ie_rm(rc, UWB_IDENTIFICATION_IE);
+ if (rc->beaconing >= 0)
+ uwb_rc_beacon(rc, -1, 0);
+ if (rc->scan_type != UWB_SCAN_DISABLED)
+ uwb_rc_scan(rc, rc->scanning, UWB_SCAN_DISABLED, 0);
+ uwb_rc_reset(rc);
+
+ uwb_dev_lock(&rc->uwb_dev);
+ rc->priv = NULL;
+ rc->cmd = NULL;
+ uwb_dev_unlock(&rc->uwb_dev);
+ mutex_lock(&uwb_beca.mutex);
+ uwb_dev_for_each(rc, uwb_dev_offair_helper, NULL);
+ __uwb_rc_sys_rm(rc);
+ mutex_unlock(&uwb_beca.mutex);
+ uwb_dev_rm(&rc->uwb_dev);
+}
+EXPORT_SYMBOL_GPL(uwb_rc_rm);
+
+static int find_rc_try_get(struct device *dev, void *data)
+{
+ struct uwb_rc *target_rc = data;
+ struct uwb_rc *rc = dev_get_drvdata(dev);
+
+ if (rc == NULL) {
+ WARN_ON(1);
+ return 0;
+ }
+ if (rc == target_rc) {
+ if (rc->ready == 0)
+ return 0;
+ else
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Given a radio controller descriptor, validate and refcount it
+ *
+ * @returns NULL if the rc does not exist or is quiescing; the ptr to
+ * it otherwise.
+ */
+struct uwb_rc *__uwb_rc_try_get(struct uwb_rc *target_rc)
+{
+ struct device *dev;
+ struct uwb_rc *rc = NULL;
+
+ dev = class_find_device(&uwb_rc_class, NULL, target_rc,
+ find_rc_try_get);
+ if (dev) {
+ rc = dev_get_drvdata(dev);
+ __uwb_rc_get(rc);
+ }
+ return rc;
+}
+EXPORT_SYMBOL_GPL(__uwb_rc_try_get);
+
+/*
+ * RC get for external refcount acquirers...
+ *
+ * Increments the refcount of the device and it's backend modules
+ */
+static inline struct uwb_rc *uwb_rc_get(struct uwb_rc *rc)
+{
+ if (rc->ready == 0)
+ return NULL;
+ uwb_dev_get(&rc->uwb_dev);
+ return rc;
+}
+
+static int find_rc_grandpa(struct device *dev, void *data)
+{
+ struct device *grandpa_dev = data;
+ struct uwb_rc *rc = dev_get_drvdata(dev);
+
+ if (rc->uwb_dev.dev.parent->parent == grandpa_dev) {
+ rc = uwb_rc_get(rc);
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Locate and refcount a radio controller given a common grand-parent
+ *
+ * @grandpa_dev Pointer to the 'grandparent' device structure.
+ * @returns NULL If the rc does not exist or is quiescing; the ptr to
+ * it otherwise, properly referenced.
+ *
+ * The Radio Control interface (or the UWB Radio Controller) is always
+ * an interface of a device. The parent is the interface, the
+ * grandparent is the device that encapsulates the interface.
+ *
+ * There is no need to lock around as the "grandpa" would be
+ * refcounted by the target, and to remove the referemes, the
+ * uwb_rc_class->sem would have to be taken--we hold it, ergo we
+ * should be safe.
+ */
+struct uwb_rc *uwb_rc_get_by_grandpa(const struct device *grandpa_dev)
+{
+ struct device *dev;
+ struct uwb_rc *rc = NULL;
+
+ dev = class_find_device(&uwb_rc_class, NULL, (void *)grandpa_dev,
+ find_rc_grandpa);
+ if (dev)
+ rc = dev_get_drvdata(dev);
+ return rc;
+}
+EXPORT_SYMBOL_GPL(uwb_rc_get_by_grandpa);
+
+/**
+ * Find a radio controller by device address
+ *
+ * @returns the pointer to the radio controller, properly referenced
+ */
+static int find_rc_dev(struct device *dev, void *data)
+{
+ struct uwb_dev_addr *addr = data;
+ struct uwb_rc *rc = dev_get_drvdata(dev);
+
+ if (rc == NULL) {
+ WARN_ON(1);
+ return 0;
+ }
+ if (!uwb_dev_addr_cmp(&rc->uwb_dev.dev_addr, addr)) {
+ rc = uwb_rc_get(rc);
+ return 1;
+ }
+ return 0;
+}
+
+struct uwb_rc *uwb_rc_get_by_dev(const struct uwb_dev_addr *addr)
+{
+ struct device *dev;
+ struct uwb_rc *rc = NULL;
+
+ dev = class_find_device(&uwb_rc_class, NULL, (void *)addr,
+ find_rc_dev);
+ if (dev)
+ rc = dev_get_drvdata(dev);
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(uwb_rc_get_by_dev);
+
+/**
+ * Drop a reference on a radio controller
+ *
+ * This is the version that should be done by entities external to the
+ * UWB Radio Control stack (ie: clients of the API).
+ */
+void uwb_rc_put(struct uwb_rc *rc)
+{
+ __uwb_rc_put(rc);
+}
+EXPORT_SYMBOL_GPL(uwb_rc_put);
+
+/*
+ *
+ *
+ */
+ssize_t uwb_rc_print_IEs(struct uwb_rc *uwb_rc, char *buf, size_t size)
+{
+ ssize_t result;
+ struct uwb_rc_evt_get_ie *ie_info;
+ struct uwb_buf_ctx ctx;
+
+ result = uwb_rc_get_ie(uwb_rc, &ie_info);
+ if (result < 0)
+ goto error_get_ie;
+ ctx.buf = buf;
+ ctx.size = size;
+ ctx.bytes = 0;
+ uwb_ie_for_each(&uwb_rc->uwb_dev, uwb_ie_dump_hex, &ctx,
+ ie_info->IEData, result - sizeof(*ie_info));
+ result = ctx.bytes;
+ kfree(ie_info);
+error_get_ie:
+ return result;
+}
+
diff --git a/drivers/uwb/neh.c b/drivers/uwb/neh.c
new file mode 100644
index 000000000000..655734cf1d47
--- /dev/null
+++ b/drivers/uwb/neh.c
@@ -0,0 +1,615 @@
+/*
+ * WUSB Wire Adapter: Radio Control Interface (WUSB[8])
+ * Notification and Event Handling
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * The RC interface of the Host Wire Adapter (USB dongle) or WHCI PCI
+ * card delivers a stream of notifications and events to the
+ * notification end event endpoint or area. This code takes care of
+ * getting a buffer with that data, breaking it up in separate
+ * notifications and events and then deliver those.
+ *
+ * Events are answers to commands and they carry a context ID that
+ * associates them to the command. Notifications are that,
+ * notifications, they come out of the blue and have a context ID of
+ * zero. Think of the context ID kind of like a handler. The
+ * uwb_rc_neh_* code deals with managing context IDs.
+ *
+ * This is why you require a handle to operate on a UWB host. When you
+ * open a handle a context ID is assigned to you.
+ *
+ * So, as it is done is:
+ *
+ * 1. Add an event handler [uwb_rc_neh_add()] (assigns a ctx id)
+ * 2. Issue command [rc->cmd(rc, ...)]
+ * 3. Arm the timeout timer [uwb_rc_neh_arm()]
+ * 4, Release the reference to the neh [uwb_rc_neh_put()]
+ * 5. Wait for the callback
+ * 6. Command result (RCEB) is passed to the callback
+ *
+ * If (2) fails, you should remove the handle [uwb_rc_neh_rm()]
+ * instead of arming the timer.
+ *
+ * Handles are for using in *serialized* code, single thread.
+ *
+ * When the notification/event comes, the IRQ handler/endpoint
+ * callback passes the data read to uwb_rc_neh_grok() which will break
+ * it up in a discrete series of events, look up who is listening for
+ * them and execute the pertinent callbacks.
+ *
+ * If the reader detects an error while reading the data stream, call
+ * uwb_rc_neh_error().
+ *
+ * CONSTRAINTS/ASSUMPTIONS:
+ *
+ * - Most notifications/events are small (less thank .5k), copying
+ * around is ok.
+ *
+ * - Notifications/events are ALWAYS smaller than PAGE_SIZE
+ *
+ * - Notifications/events always come in a single piece (ie: a buffer
+ * will always contain entire notifications/events).
+ *
+ * - we cannot know in advance how long each event is (because they
+ * lack a length field in their header--smart move by the standards
+ * body, btw). So we need a facility to get the event size given the
+ * header. This is what the EST code does (notif/Event Size
+ * Tables), check nest.c--as well, you can associate the size to
+ * the handle [w/ neh->extra_size()].
+ *
+ * - Most notifications/events are fixed size; only a few are variable
+ * size (NEST takes care of that).
+ *
+ * - Listeners of events expect them, so they usually provide a
+ * buffer, as they know the size. Listeners to notifications don't,
+ * so we allocate their buffers dynamically.
+ */
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/err.h>
+
+#include "uwb-internal.h"
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+/*
+ * UWB Radio Controller Notification/Event Handle
+ *
+ * Represents an entity waiting for an event coming from the UWB Radio
+ * Controller with a given context id (context) and type (evt_type and
+ * evt). On reception of the notification/event, the callback (cb) is
+ * called with the event.
+ *
+ * If the timer expires before the event is received, the callback is
+ * called with -ETIMEDOUT as the event size.
+ */
+struct uwb_rc_neh {
+ struct kref kref;
+
+ struct uwb_rc *rc;
+ u8 evt_type;
+ __le16 evt;
+ u8 context;
+ uwb_rc_neh_cb_f cb;
+ void *arg;
+
+ struct timer_list timer;
+ struct list_head list_node;
+};
+
+static void uwb_rc_neh_timer(unsigned long arg);
+
+static void uwb_rc_neh_release(struct kref *kref)
+{
+ struct uwb_rc_neh *neh = container_of(kref, struct uwb_rc_neh, kref);
+
+ kfree(neh);
+}
+
+static void uwb_rc_neh_get(struct uwb_rc_neh *neh)
+{
+ kref_get(&neh->kref);
+}
+
+/**
+ * uwb_rc_neh_put - release reference to a neh
+ * @neh: the neh
+ */
+void uwb_rc_neh_put(struct uwb_rc_neh *neh)
+{
+ kref_put(&neh->kref, uwb_rc_neh_release);
+}
+
+
+/**
+ * Assigns @neh a context id from @rc's pool
+ *
+ * @rc: UWB Radio Controller descriptor; @rc->neh_lock taken
+ * @neh: Notification/Event Handle
+ * @returns 0 if context id was assigned ok; < 0 errno on error (if
+ * all the context IDs are taken).
+ *
+ * (assumes @wa is locked).
+ *
+ * NOTE: WUSB spec reserves context ids 0x00 for notifications and
+ * 0xff is invalid, so they must not be used. Initialization
+ * fills up those two in the bitmap so they are not allocated.
+ *
+ * We spread the allocation around to reduce the posiblity of two
+ * consecutive opened @neh's getting the same context ID assigned (to
+ * avoid surprises with late events that timed out long time ago). So
+ * first we search from where @rc->ctx_roll is, if not found, we
+ * search from zero.
+ */
+static
+int __uwb_rc_ctx_get(struct uwb_rc *rc, struct uwb_rc_neh *neh)
+{
+ int result;
+ result = find_next_zero_bit(rc->ctx_bm, UWB_RC_CTX_MAX,
+ rc->ctx_roll++);
+ if (result < UWB_RC_CTX_MAX)
+ goto found;
+ result = find_first_zero_bit(rc->ctx_bm, UWB_RC_CTX_MAX);
+ if (result < UWB_RC_CTX_MAX)
+ goto found;
+ return -ENFILE;
+found:
+ set_bit(result, rc->ctx_bm);
+ neh->context = result;
+ return 0;
+}
+
+
+/** Releases @neh's context ID back to @rc (@rc->neh_lock is locked). */
+static
+void __uwb_rc_ctx_put(struct uwb_rc *rc, struct uwb_rc_neh *neh)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+ if (neh->context == 0)
+ return;
+ if (test_bit(neh->context, rc->ctx_bm) == 0) {
+ dev_err(dev, "context %u not set in bitmap\n",
+ neh->context);
+ WARN_ON(1);
+ }
+ clear_bit(neh->context, rc->ctx_bm);
+ neh->context = 0;
+}
+
+/**
+ * uwb_rc_neh_add - add a neh for a radio controller command
+ * @rc: the radio controller
+ * @cmd: the radio controller command
+ * @expected_type: the type of the expected response event
+ * @expected_event: the expected event ID
+ * @cb: callback for when the event is received
+ * @arg: argument for the callback
+ *
+ * Creates a neh and adds it to the list of those waiting for an
+ * event. A context ID will be assigned to the command.
+ */
+struct uwb_rc_neh *uwb_rc_neh_add(struct uwb_rc *rc, struct uwb_rccb *cmd,
+ u8 expected_type, u16 expected_event,
+ uwb_rc_neh_cb_f cb, void *arg)
+{
+ int result;
+ unsigned long flags;
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_rc_neh *neh;
+
+ neh = kzalloc(sizeof(*neh), GFP_KERNEL);
+ if (neh == NULL) {
+ result = -ENOMEM;
+ goto error_kzalloc;
+ }
+
+ kref_init(&neh->kref);
+ INIT_LIST_HEAD(&neh->list_node);
+ init_timer(&neh->timer);
+ neh->timer.function = uwb_rc_neh_timer;
+ neh->timer.data = (unsigned long)neh;
+
+ neh->rc = rc;
+ neh->evt_type = expected_type;
+ neh->evt = cpu_to_le16(expected_event);
+ neh->cb = cb;
+ neh->arg = arg;
+
+ spin_lock_irqsave(&rc->neh_lock, flags);
+ result = __uwb_rc_ctx_get(rc, neh);
+ if (result >= 0) {
+ cmd->bCommandContext = neh->context;
+ list_add_tail(&neh->list_node, &rc->neh_list);
+ uwb_rc_neh_get(neh);
+ }
+ spin_unlock_irqrestore(&rc->neh_lock, flags);
+ if (result < 0)
+ goto error_ctx_get;
+
+ return neh;
+
+error_ctx_get:
+ kfree(neh);
+error_kzalloc:
+ dev_err(dev, "cannot open handle to radio controller: %d\n", result);
+ return ERR_PTR(result);
+}
+
+static void __uwb_rc_neh_rm(struct uwb_rc *rc, struct uwb_rc_neh *neh)
+{
+ del_timer(&neh->timer);
+ __uwb_rc_ctx_put(rc, neh);
+ list_del(&neh->list_node);
+}
+
+/**
+ * uwb_rc_neh_rm - remove a neh.
+ * @rc: the radio controller
+ * @neh: the neh to remove
+ *
+ * Remove an active neh immediately instead of waiting for the event
+ * (or a time out).
+ */
+void uwb_rc_neh_rm(struct uwb_rc *rc, struct uwb_rc_neh *neh)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&rc->neh_lock, flags);
+ __uwb_rc_neh_rm(rc, neh);
+ spin_unlock_irqrestore(&rc->neh_lock, flags);
+
+ uwb_rc_neh_put(neh);
+}
+
+/**
+ * uwb_rc_neh_arm - arm an event handler timeout timer
+ *
+ * @rc: UWB Radio Controller
+ * @neh: Notification/event handler for @rc
+ *
+ * The timer is only armed if the neh is active.
+ */
+void uwb_rc_neh_arm(struct uwb_rc *rc, struct uwb_rc_neh *neh)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&rc->neh_lock, flags);
+ if (neh->context)
+ mod_timer(&neh->timer,
+ jiffies + msecs_to_jiffies(UWB_RC_CMD_TIMEOUT_MS));
+ spin_unlock_irqrestore(&rc->neh_lock, flags);
+}
+
+static void uwb_rc_neh_cb(struct uwb_rc_neh *neh, struct uwb_rceb *rceb, size_t size)
+{
+ (*neh->cb)(neh->rc, neh->arg, rceb, size);
+ uwb_rc_neh_put(neh);
+}
+
+static bool uwb_rc_neh_match(struct uwb_rc_neh *neh, const struct uwb_rceb *rceb)
+{
+ return neh->evt_type == rceb->bEventType
+ && neh->evt == rceb->wEvent
+ && neh->context == rceb->bEventContext;
+}
+
+/**
+ * Find the handle waiting for a RC Radio Control Event
+ *
+ * @rc: UWB Radio Controller
+ * @rceb: Pointer to the RCEB buffer
+ * @event_size: Pointer to the size of the RCEB buffer. Might be
+ * adjusted to take into account the @neh->extra_size
+ * settings.
+ *
+ * If the listener has no buffer (NULL buffer), one is allocated for
+ * the right size (the amount of data received). @neh->ptr will point
+ * to the event payload, which always starts with a 'struct
+ * uwb_rceb'. kfree() it when done.
+ */
+static
+struct uwb_rc_neh *uwb_rc_neh_lookup(struct uwb_rc *rc,
+ const struct uwb_rceb *rceb)
+{
+ struct uwb_rc_neh *neh = NULL, *h;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rc->neh_lock, flags);
+
+ list_for_each_entry(h, &rc->neh_list, list_node) {
+ if (uwb_rc_neh_match(h, rceb)) {
+ neh = h;
+ break;
+ }
+ }
+
+ if (neh)
+ __uwb_rc_neh_rm(rc, neh);
+
+ spin_unlock_irqrestore(&rc->neh_lock, flags);
+
+ return neh;
+}
+
+
+/**
+ * Process notifications coming from the radio control interface
+ *
+ * @rc: UWB Radio Control Interface descriptor
+ * @neh: Notification/Event Handler @neh->ptr points to
+ * @uwb_evt->buffer.
+ *
+ * This function is called by the event/notif handling subsystem when
+ * notifications arrive (hwarc_probe() arms a notification/event handle
+ * that calls back this function for every received notification; this
+ * function then will rearm itself).
+ *
+ * Notification data buffers are dynamically allocated by the NEH
+ * handling code in neh.c [uwb_rc_neh_lookup()]. What is actually
+ * allocated is space to contain the notification data.
+ *
+ * Buffers are prefixed with a Radio Control Event Block (RCEB) as
+ * defined by the WUSB Wired-Adapter Radio Control interface. We
+ * just use it for the notification code.
+ *
+ * On each case statement we just transcode endianess of the different
+ * fields. We declare a pointer to a RCI definition of an event, and
+ * then to a UWB definition of the same event (which are the same,
+ * remember). Event if we use different pointers
+ */
+static
+void uwb_rc_notif(struct uwb_rc *rc, struct uwb_rceb *rceb, ssize_t size)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_event *uwb_evt;
+
+ if (size == -ESHUTDOWN)
+ return;
+ if (size < 0) {
+ dev_err(dev, "ignoring event with error code %zu\n",
+ size);
+ return;
+ }
+
+ uwb_evt = kzalloc(sizeof(*uwb_evt), GFP_ATOMIC);
+ if (unlikely(uwb_evt == NULL)) {
+ dev_err(dev, "no memory to queue event 0x%02x/%04x/%02x\n",
+ rceb->bEventType, le16_to_cpu(rceb->wEvent),
+ rceb->bEventContext);
+ return;
+ }
+ uwb_evt->rc = __uwb_rc_get(rc); /* will be put by uwbd's uwbd_event_handle() */
+ uwb_evt->rceb = rceb;
+ uwb_evt->size = size;
+ uwb_evt->ts_jiffies = jiffies;
+
+ switch (le16_to_cpu(rceb->wEvent)) {
+ /* Trap some vendor specific events
+ *
+ * FIXME: move this to handling in ptc-est, where we
+ * register a NULL event handler for these two guys
+ * using the Intel IDs.
+ */
+ case 0x0103:
+ dev_info(dev, "FIXME: DEVICE ADD\n");
+ return;
+ case 0x0104:
+ dev_info(dev, "FIXME: DEVICE RM\n");
+ return;
+ default:
+ break;
+ }
+
+ uwbd_event_queue(uwb_evt);
+}
+
+static void uwb_rc_neh_grok_event(struct uwb_rc *rc, struct uwb_rceb *rceb, size_t size)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_rc_neh *neh;
+ struct uwb_rceb *notif;
+
+ if (rceb->bEventContext == 0) {
+ notif = kmalloc(size, GFP_ATOMIC);
+ if (notif) {
+ memcpy(notif, rceb, size);
+ uwb_rc_notif(rc, notif, size);
+ } else
+ dev_err(dev, "event 0x%02x/%04x/%02x (%zu bytes): no memory\n",
+ rceb->bEventType, le16_to_cpu(rceb->wEvent),
+ rceb->bEventContext, size);
+ } else {
+ neh = uwb_rc_neh_lookup(rc, rceb);
+ if (neh)
+ uwb_rc_neh_cb(neh, rceb, size);
+ else if (printk_ratelimit())
+ dev_warn(dev, "event 0x%02x/%04x/%02x (%zu bytes): nobody cared\n",
+ rceb->bEventType, le16_to_cpu(rceb->wEvent),
+ rceb->bEventContext, size);
+ }
+}
+
+/**
+ * Given a buffer with one or more UWB RC events/notifications, break
+ * them up and dispatch them.
+ *
+ * @rc: UWB Radio Controller
+ * @buf: Buffer with the stream of notifications/events
+ * @buf_size: Amount of data in the buffer
+ *
+ * Note each notification/event starts always with a 'struct
+ * uwb_rceb', so the minimum size if 4 bytes.
+ *
+ * The device may pass us events formatted differently than expected.
+ * These are first filtered, potentially creating a new event in a new
+ * memory location. If a new event is created by the filter it is also
+ * freed here.
+ *
+ * For each notif/event, tries to guess the size looking at the EST
+ * tables, then looks for a neh that is waiting for that event and if
+ * found, copies the payload to the neh's buffer and calls it back. If
+ * not, the data is ignored.
+ *
+ * Note that if we can't find a size description in the EST tables, we
+ * still might find a size in the 'neh' handle in uwb_rc_neh_lookup().
+ *
+ * Assumptions:
+ *
+ * @rc->neh_lock is NOT taken
+ *
+ * We keep track of various sizes here:
+ * size: contains the size of the buffer that is processed for the
+ * incoming event. this buffer may contain events that are not
+ * formatted as WHCI.
+ * real_size: the actual space taken by this event in the buffer.
+ * We need to keep track of the real size of an event to be able to
+ * advance the buffer correctly.
+ * event_size: the size of the event as expected by the core layer
+ * [OR] the size of the event after filtering. if the filtering
+ * created a new event in a new memory location then this is
+ * effectively the size of a new event buffer
+ */
+void uwb_rc_neh_grok(struct uwb_rc *rc, void *buf, size_t buf_size)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+ void *itr;
+ struct uwb_rceb *rceb;
+ size_t size, real_size, event_size;
+ int needtofree;
+
+ d_fnstart(3, dev, "(rc %p buf %p %zu buf_size)\n", rc, buf, buf_size);
+ d_printf(2, dev, "groking event block: %zu bytes\n", buf_size);
+ itr = buf;
+ size = buf_size;
+ while (size > 0) {
+ if (size < sizeof(*rceb)) {
+ dev_err(dev, "not enough data in event buffer to "
+ "process incoming events (%zu left, minimum is "
+ "%zu)\n", size, sizeof(*rceb));
+ break;
+ }
+
+ rceb = itr;
+ if (rc->filter_event) {
+ needtofree = rc->filter_event(rc, &rceb, size,
+ &real_size, &event_size);
+ if (needtofree < 0 && needtofree != -ENOANO) {
+ dev_err(dev, "BUG: Unable to filter event "
+ "(0x%02x/%04x/%02x) from "
+ "device. \n", rceb->bEventType,
+ le16_to_cpu(rceb->wEvent),
+ rceb->bEventContext);
+ break;
+ }
+ } else
+ needtofree = -ENOANO;
+ /* do real processing if there was no filtering or the
+ * filtering didn't act */
+ if (needtofree == -ENOANO) {
+ ssize_t ret = uwb_est_find_size(rc, rceb, size);
+ if (ret < 0)
+ break;
+ if (ret > size) {
+ dev_err(dev, "BUG: hw sent incomplete event "
+ "0x%02x/%04x/%02x (%zd bytes), only got "
+ "%zu bytes. We don't handle that.\n",
+ rceb->bEventType, le16_to_cpu(rceb->wEvent),
+ rceb->bEventContext, ret, size);
+ break;
+ }
+ real_size = event_size = ret;
+ }
+ uwb_rc_neh_grok_event(rc, rceb, event_size);
+
+ if (needtofree == 1)
+ kfree(rceb);
+
+ itr += real_size;
+ size -= real_size;
+ d_printf(2, dev, "consumed %zd bytes, %zu left\n",
+ event_size, size);
+ }
+ d_fnend(3, dev, "(rc %p buf %p %zu buf_size) = void\n", rc, buf, buf_size);
+}
+EXPORT_SYMBOL_GPL(uwb_rc_neh_grok);
+
+
+/**
+ * The entity that reads from the device notification/event channel has
+ * detected an error.
+ *
+ * @rc: UWB Radio Controller
+ * @error: Errno error code
+ *
+ */
+void uwb_rc_neh_error(struct uwb_rc *rc, int error)
+{
+ struct uwb_rc_neh *neh, *next;
+ unsigned long flags;
+
+ BUG_ON(error >= 0);
+ spin_lock_irqsave(&rc->neh_lock, flags);
+ list_for_each_entry_safe(neh, next, &rc->neh_list, list_node) {
+ __uwb_rc_neh_rm(rc, neh);
+ uwb_rc_neh_cb(neh, NULL, error);
+ }
+ spin_unlock_irqrestore(&rc->neh_lock, flags);
+}
+EXPORT_SYMBOL_GPL(uwb_rc_neh_error);
+
+
+static void uwb_rc_neh_timer(unsigned long arg)
+{
+ struct uwb_rc_neh *neh = (struct uwb_rc_neh *)arg;
+ struct uwb_rc *rc = neh->rc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&rc->neh_lock, flags);
+ __uwb_rc_neh_rm(rc, neh);
+ spin_unlock_irqrestore(&rc->neh_lock, flags);
+
+ uwb_rc_neh_cb(neh, NULL, -ETIMEDOUT);
+}
+
+/** Initializes the @rc's neh subsystem
+ */
+void uwb_rc_neh_create(struct uwb_rc *rc)
+{
+ spin_lock_init(&rc->neh_lock);
+ INIT_LIST_HEAD(&rc->neh_list);
+ set_bit(0, rc->ctx_bm); /* 0 is reserved (see [WUSB] table 8-65) */
+ set_bit(0xff, rc->ctx_bm); /* and 0xff is invalid */
+ rc->ctx_roll = 1;
+}
+
+
+/** Release's the @rc's neh subsystem */
+void uwb_rc_neh_destroy(struct uwb_rc *rc)
+{
+ unsigned long flags;
+ struct uwb_rc_neh *neh, *next;
+
+ spin_lock_irqsave(&rc->neh_lock, flags);
+ list_for_each_entry_safe(neh, next, &rc->neh_list, list_node) {
+ __uwb_rc_neh_rm(rc, neh);
+ uwb_rc_neh_put(neh);
+ }
+ spin_unlock_irqrestore(&rc->neh_lock, flags);
+}
diff --git a/drivers/uwb/pal.c b/drivers/uwb/pal.c
new file mode 100644
index 000000000000..5508993a820e
--- /dev/null
+++ b/drivers/uwb/pal.c
@@ -0,0 +1,71 @@
+/*
+ * UWB PAL support.
+ *
+ * Copyright (C) 2008 Cambridge Silicon Radio 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+#include <linux/uwb.h>
+
+#include "uwb-internal.h"
+
+/**
+ * uwb_pal_init - initialize a UWB PAL
+ * @pal: the PAL to initialize
+ */
+void uwb_pal_init(struct uwb_pal *pal)
+{
+ INIT_LIST_HEAD(&pal->node);
+}
+EXPORT_SYMBOL_GPL(uwb_pal_init);
+
+/**
+ * uwb_pal_register - register a UWB PAL
+ * @rc: the radio controller the PAL will be using
+ * @pal: the PAL
+ *
+ * The PAL must be initialized with uwb_pal_init().
+ */
+int uwb_pal_register(struct uwb_rc *rc, struct uwb_pal *pal)
+{
+ spin_lock(&rc->pal_lock);
+ list_add(&pal->node, &rc->pals);
+ spin_unlock(&rc->pal_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(uwb_pal_register);
+
+/**
+ * uwb_pal_register - unregister a UWB PAL
+ * @rc: the radio controller the PAL was using
+ * @pal: the PAL
+ */
+void uwb_pal_unregister(struct uwb_rc *rc, struct uwb_pal *pal)
+{
+ spin_lock(&rc->pal_lock);
+ list_del(&pal->node);
+ spin_unlock(&rc->pal_lock);
+}
+EXPORT_SYMBOL_GPL(uwb_pal_unregister);
+
+/**
+ * uwb_rc_pal_init - initialize the PAL related parts of a radio controller
+ * @rc: the radio controller
+ */
+void uwb_rc_pal_init(struct uwb_rc *rc)
+{
+ spin_lock_init(&rc->pal_lock);
+ INIT_LIST_HEAD(&rc->pals);
+}
diff --git a/drivers/uwb/reset.c b/drivers/uwb/reset.c
new file mode 100644
index 000000000000..70a95c9c2dee
--- /dev/null
+++ b/drivers/uwb/reset.c
@@ -0,0 +1,319 @@
+/*
+ * Ultra Wide Band
+ * UWB basic command support and radio reset
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME:
+ *
+ * - docs
+ *
+ * - Now we are serializing (using the uwb_dev->mutex) the command
+ * execution; it should be parallelized as much as possible some
+ * day.
+ */
+#include <linux/kernel.h>
+#include <linux/err.h>
+
+#include "uwb-internal.h"
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+/**
+ * Command result codes (WUSB1.0[T8-69])
+ */
+static
+const char *__strerror[] = {
+ "success",
+ "failure",
+ "hardware failure",
+ "no more slots",
+ "beacon is too large",
+ "invalid parameter",
+ "unsupported power level",
+ "time out (wa) or invalid ie data (whci)",
+ "beacon size exceeded",
+ "cancelled",
+ "invalid state",
+ "invalid size",
+ "ack not recieved",
+ "no more asie notification",
+};
+
+
+/** Return a string matching the given error code */
+const char *uwb_rc_strerror(unsigned code)
+{
+ if (code == 255)
+ return "time out";
+ if (code >= ARRAY_SIZE(__strerror))
+ return "unknown error";
+ return __strerror[code];
+}
+
+static
+int uwb_rc_cmd_async(struct uwb_rc *rc, const char *cmd_name,
+ struct uwb_rccb *cmd, size_t cmd_size,
+ u8 expected_type, u16 expected_event,
+ uwb_rc_neh_cb_f cb, void *arg)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_rc_neh *neh;
+ int needtofree = 0;
+ int result;
+
+ uwb_dev_lock(&rc->uwb_dev); /* Protect against rc->priv being removed */
+ if (rc->priv == NULL) {
+ uwb_dev_unlock(&rc->uwb_dev);
+ return -ESHUTDOWN;
+ }
+
+ if (rc->filter_cmd) {
+ needtofree = rc->filter_cmd(rc, &cmd, &cmd_size);
+ if (needtofree < 0 && needtofree != -ENOANO) {
+ dev_err(dev, "%s: filter error: %d\n",
+ cmd_name, needtofree);
+ uwb_dev_unlock(&rc->uwb_dev);
+ return needtofree;
+ }
+ }
+
+ neh = uwb_rc_neh_add(rc, cmd, expected_type, expected_event, cb, arg);
+ if (IS_ERR(neh)) {
+ result = PTR_ERR(neh);
+ goto out;
+ }
+
+ result = rc->cmd(rc, cmd, cmd_size);
+ uwb_dev_unlock(&rc->uwb_dev);
+ if (result < 0)
+ uwb_rc_neh_rm(rc, neh);
+ else
+ uwb_rc_neh_arm(rc, neh);
+ uwb_rc_neh_put(neh);
+out:
+ if (needtofree == 1)
+ kfree(cmd);
+ return result < 0 ? result : 0;
+}
+
+struct uwb_rc_cmd_done_params {
+ struct completion completion;
+ struct uwb_rceb *reply;
+ ssize_t reply_size;
+};
+
+static void uwb_rc_cmd_done(struct uwb_rc *rc, void *arg,
+ struct uwb_rceb *reply, ssize_t reply_size)
+{
+ struct uwb_rc_cmd_done_params *p = (struct uwb_rc_cmd_done_params *)arg;
+
+ if (reply_size > 0) {
+ if (p->reply)
+ reply_size = min(p->reply_size, reply_size);
+ else
+ p->reply = kmalloc(reply_size, GFP_ATOMIC);
+
+ if (p->reply)
+ memcpy(p->reply, reply, reply_size);
+ else
+ reply_size = -ENOMEM;
+ }
+ p->reply_size = reply_size;
+ complete(&p->completion);
+}
+
+
+/**
+ * Generic function for issuing commands to the Radio Control Interface
+ *
+ * @rc: UWB Radio Control descriptor
+ * @cmd_name: Name of the command being issued (for error messages)
+ * @cmd: Pointer to rccb structure containing the command;
+ * normally you embed this structure as the first member of
+ * the full command structure.
+ * @cmd_size: Size of the whole command buffer pointed to by @cmd.
+ * @reply: Pointer to where to store the reply
+ * @reply_size: @reply's size
+ * @expected_type: Expected type in the return event
+ * @expected_event: Expected event code in the return event
+ * @preply: Here a pointer to where the event data is received will
+ * be stored. Once done with the data, free with kfree().
+ *
+ * This function is generic; it works for commands that return a fixed
+ * and known size or for commands that return a variable amount of data.
+ *
+ * If a buffer is provided, that is used, although it could be chopped
+ * to the maximum size of the buffer. If the buffer is NULL, then one
+ * be allocated in *preply with the whole contents of the reply.
+ *
+ * @rc needs to be referenced
+ */
+static
+ssize_t __uwb_rc_cmd(struct uwb_rc *rc, const char *cmd_name,
+ struct uwb_rccb *cmd, size_t cmd_size,
+ struct uwb_rceb *reply, size_t reply_size,
+ u8 expected_type, u16 expected_event,
+ struct uwb_rceb **preply)
+{
+ ssize_t result = 0;
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_rc_cmd_done_params params;
+
+ init_completion(&params.completion);
+ params.reply = reply;
+ params.reply_size = reply_size;
+
+ result = uwb_rc_cmd_async(rc, cmd_name, cmd, cmd_size,
+ expected_type, expected_event,
+ uwb_rc_cmd_done, &params);
+ if (result)
+ return result;
+
+ wait_for_completion(&params.completion);
+
+ if (preply)
+ *preply = params.reply;
+
+ if (params.reply_size < 0)
+ dev_err(dev, "%s: confirmation event 0x%02x/%04x/%02x "
+ "reception failed: %d\n", cmd_name,
+ expected_type, expected_event, cmd->bCommandContext,
+ (int)params.reply_size);
+ return params.reply_size;
+}
+
+
+/**
+ * Generic function for issuing commands to the Radio Control Interface
+ *
+ * @rc: UWB Radio Control descriptor
+ * @cmd_name: Name of the command being issued (for error messages)
+ * @cmd: Pointer to rccb structure containing the command;
+ * normally you embed this structure as the first member of
+ * the full command structure.
+ * @cmd_size: Size of the whole command buffer pointed to by @cmd.
+ * @reply: Pointer to the beginning of the confirmation event
+ * buffer. Normally bigger than an 'struct hwarc_rceb'.
+ * You need to fill out reply->bEventType and reply->wEvent (in
+ * cpu order) as the function will use them to verify the
+ * confirmation event.
+ * @reply_size: Size of the reply buffer
+ *
+ * The function checks that the length returned in the reply is at
+ * least as big as @reply_size; if not, it will be deemed an error and
+ * -EIO returned.
+ *
+ * @rc needs to be referenced
+ */
+ssize_t uwb_rc_cmd(struct uwb_rc *rc, const char *cmd_name,
+ struct uwb_rccb *cmd, size_t cmd_size,
+ struct uwb_rceb *reply, size_t reply_size)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+ ssize_t result;
+
+ result = __uwb_rc_cmd(rc, cmd_name,
+ cmd, cmd_size, reply, reply_size,
+ reply->bEventType, reply->wEvent, NULL);
+
+ if (result > 0 && result < reply_size) {
+ dev_err(dev, "%s: not enough data returned for decoding reply "
+ "(%zu bytes received vs at least %zu needed)\n",
+ cmd_name, result, reply_size);
+ result = -EIO;
+ }
+ return result;
+}
+EXPORT_SYMBOL_GPL(uwb_rc_cmd);
+
+
+/**
+ * Generic function for issuing commands to the Radio Control
+ * Interface that return an unknown amount of data
+ *
+ * @rc: UWB Radio Control descriptor
+ * @cmd_name: Name of the command being issued (for error messages)
+ * @cmd: Pointer to rccb structure containing the command;
+ * normally you embed this structure as the first member of
+ * the full command structure.
+ * @cmd_size: Size of the whole command buffer pointed to by @cmd.
+ * @expected_type: Expected type in the return event
+ * @expected_event: Expected event code in the return event
+ * @preply: Here a pointer to where the event data is received will
+ * be stored. Once done with the data, free with kfree().
+ *
+ * The function checks that the length returned in the reply is at
+ * least as big as a 'struct uwb_rceb *'; if not, it will be deemed an
+ * error and -EIO returned.
+ *
+ * @rc needs to be referenced
+ */
+ssize_t uwb_rc_vcmd(struct uwb_rc *rc, const char *cmd_name,
+ struct uwb_rccb *cmd, size_t cmd_size,
+ u8 expected_type, u16 expected_event,
+ struct uwb_rceb **preply)
+{
+ return __uwb_rc_cmd(rc, cmd_name, cmd, cmd_size, NULL, 0,
+ expected_type, expected_event, preply);
+}
+EXPORT_SYMBOL_GPL(uwb_rc_vcmd);
+
+
+/**
+ * Reset a UWB Host Controller (and all radio settings)
+ *
+ * @rc: Host Controller descriptor
+ * @returns: 0 if ok, < 0 errno code on error
+ *
+ * We put the command on kmalloc'ed memory as some arches cannot do
+ * USB from the stack. The reply event is copied from an stage buffer,
+ * so it can be in the stack. See WUSB1.0[8.6.2.4] for more details.
+ */
+int uwb_rc_reset(struct uwb_rc *rc)
+{
+ int result = -ENOMEM;
+ struct uwb_rc_evt_confirm reply;
+ struct uwb_rccb *cmd;
+ size_t cmd_size = sizeof(*cmd);
+
+ mutex_lock(&rc->uwb_dev.mutex);
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL)
+ goto error_kzalloc;
+ cmd->bCommandType = UWB_RC_CET_GENERAL;
+ cmd->wCommand = cpu_to_le16(UWB_RC_CMD_RESET);
+ reply.rceb.bEventType = UWB_RC_CET_GENERAL;
+ reply.rceb.wEvent = UWB_RC_CMD_RESET;
+ result = uwb_rc_cmd(rc, "RESET", cmd, cmd_size,
+ &reply.rceb, sizeof(reply));
+ if (result < 0)
+ goto error_cmd;
+ if (reply.bResultCode != UWB_RC_RES_SUCCESS) {
+ dev_err(&rc->uwb_dev.dev,
+ "RESET: command execution failed: %s (%d)\n",
+ uwb_rc_strerror(reply.bResultCode), reply.bResultCode);
+ result = -EIO;
+ }
+error_cmd:
+ kfree(cmd);
+error_kzalloc:
+ mutex_unlock(&rc->uwb_dev.mutex);
+ return result;
+}
diff --git a/drivers/uwb/rsv.c b/drivers/uwb/rsv.c
new file mode 100644
index 000000000000..03efa27c88ad
--- /dev/null
+++ b/drivers/uwb/rsv.c
@@ -0,0 +1,677 @@
+/*
+ * UWB reservation management.
+ *
+ * Copyright (C) 2008 Cambridge Silicon Radio 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/uwb.h>
+
+#include "uwb-internal.h"
+
+static void uwb_rsv_timer(unsigned long arg);
+
+static const char *rsv_states[] = {
+ [UWB_RSV_STATE_NONE] = "none",
+ [UWB_RSV_STATE_O_INITIATED] = "initiated",
+ [UWB_RSV_STATE_O_PENDING] = "pending",
+ [UWB_RSV_STATE_O_MODIFIED] = "modified",
+ [UWB_RSV_STATE_O_ESTABLISHED] = "established",
+ [UWB_RSV_STATE_T_ACCEPTED] = "accepted",
+ [UWB_RSV_STATE_T_DENIED] = "denied",
+ [UWB_RSV_STATE_T_PENDING] = "pending",
+};
+
+static const char *rsv_types[] = {
+ [UWB_DRP_TYPE_ALIEN_BP] = "alien-bp",
+ [UWB_DRP_TYPE_HARD] = "hard",
+ [UWB_DRP_TYPE_SOFT] = "soft",
+ [UWB_DRP_TYPE_PRIVATE] = "private",
+ [UWB_DRP_TYPE_PCA] = "pca",
+};
+
+/**
+ * uwb_rsv_state_str - return a string for a reservation state
+ * @state: the reservation state.
+ */
+const char *uwb_rsv_state_str(enum uwb_rsv_state state)
+{
+ if (state < UWB_RSV_STATE_NONE || state >= UWB_RSV_STATE_LAST)
+ return "unknown";
+ return rsv_states[state];
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_state_str);
+
+/**
+ * uwb_rsv_type_str - return a string for a reservation type
+ * @type: the reservation type
+ */
+const char *uwb_rsv_type_str(enum uwb_drp_type type)
+{
+ if (type < UWB_DRP_TYPE_ALIEN_BP || type > UWB_DRP_TYPE_PCA)
+ return "invalid";
+ return rsv_types[type];
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_type_str);
+
+static void uwb_rsv_dump(struct uwb_rsv *rsv)
+{
+ struct device *dev = &rsv->rc->uwb_dev.dev;
+ struct uwb_dev_addr devaddr;
+ char owner[UWB_ADDR_STRSIZE], target[UWB_ADDR_STRSIZE];
+
+ uwb_dev_addr_print(owner, sizeof(owner), &rsv->owner->dev_addr);
+ if (rsv->target.type == UWB_RSV_TARGET_DEV)
+ devaddr = rsv->target.dev->dev_addr;
+ else
+ devaddr = rsv->target.devaddr;
+ uwb_dev_addr_print(target, sizeof(target), &devaddr);
+
+ dev_dbg(dev, "rsv %s -> %s: %s\n", owner, target, uwb_rsv_state_str(rsv->state));
+}
+
+/*
+ * Get a free stream index for a reservation.
+ *
+ * If the target is a DevAddr (e.g., a WUSB cluster reservation) then
+ * the stream is allocated from a pool of per-RC stream indexes,
+ * otherwise a unique stream index for the target is selected.
+ */
+static int uwb_rsv_get_stream(struct uwb_rsv *rsv)
+{
+ struct uwb_rc *rc = rsv->rc;
+ unsigned long *streams_bm;
+ int stream;
+
+ switch (rsv->target.type) {
+ case UWB_RSV_TARGET_DEV:
+ streams_bm = rsv->target.dev->streams;
+ break;
+ case UWB_RSV_TARGET_DEVADDR:
+ streams_bm = rc->uwb_dev.streams;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ stream = find_first_zero_bit(streams_bm, UWB_NUM_STREAMS);
+ if (stream >= UWB_NUM_STREAMS)
+ return -EBUSY;
+
+ rsv->stream = stream;
+ set_bit(stream, streams_bm);
+
+ return 0;
+}
+
+static void uwb_rsv_put_stream(struct uwb_rsv *rsv)
+{
+ struct uwb_rc *rc = rsv->rc;
+ unsigned long *streams_bm;
+
+ switch (rsv->target.type) {
+ case UWB_RSV_TARGET_DEV:
+ streams_bm = rsv->target.dev->streams;
+ break;
+ case UWB_RSV_TARGET_DEVADDR:
+ streams_bm = rc->uwb_dev.streams;
+ break;
+ default:
+ return;
+ }
+
+ clear_bit(rsv->stream, streams_bm);
+}
+
+/*
+ * Generate a MAS allocation with a single row component.
+ */
+static void uwb_rsv_gen_alloc_row(struct uwb_mas_bm *mas,
+ int first_mas, int mas_per_zone,
+ int zs, int ze)
+{
+ struct uwb_mas_bm col;
+ int z;
+
+ bitmap_zero(mas->bm, UWB_NUM_MAS);
+ bitmap_zero(col.bm, UWB_NUM_MAS);
+ bitmap_fill(col.bm, mas_per_zone);
+ bitmap_shift_left(col.bm, col.bm, first_mas + zs * UWB_MAS_PER_ZONE, UWB_NUM_MAS);
+
+ for (z = zs; z <= ze; z++) {
+ bitmap_or(mas->bm, mas->bm, col.bm, UWB_NUM_MAS);
+ bitmap_shift_left(col.bm, col.bm, UWB_MAS_PER_ZONE, UWB_NUM_MAS);
+ }
+}
+
+/*
+ * Allocate some MAS for this reservation based on current local
+ * availability, the reservation parameters (max_mas, min_mas,
+ * sparsity), and the WiMedia rules for MAS allocations.
+ *
+ * Returns -EBUSY is insufficient free MAS are available.
+ *
+ * FIXME: to simplify this, only safe reservations with a single row
+ * component in zones 1 to 15 are tried (zone 0 is skipped to avoid
+ * problems with the MAS reserved for the BP).
+ *
+ * [ECMA-368] section B.2.
+ */
+static int uwb_rsv_alloc_mas(struct uwb_rsv *rsv)
+{
+ static const int safe_mas_in_row[UWB_NUM_ZONES] = {
+ 8, 7, 6, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 2, 1,
+ };
+ int n, r;
+ struct uwb_mas_bm mas;
+ bool found = false;
+
+ /*
+ * Search all valid safe allocations until either: too few MAS
+ * are available; or the smallest allocation with sufficient
+ * MAS is found.
+ *
+ * The top of the zones are preferred, so space for larger
+ * allocations is available in the bottom of the zone (e.g., a
+ * 15 MAS allocation should start in row 14 leaving space for
+ * a 120 MAS allocation at row 0).
+ */
+ for (n = safe_mas_in_row[0]; n >= 1; n--) {
+ int num_mas;
+
+ num_mas = n * (UWB_NUM_ZONES - 1);
+ if (num_mas < rsv->min_mas)
+ break;
+ if (found && num_mas < rsv->max_mas)
+ break;
+
+ for (r = UWB_MAS_PER_ZONE-1; r >= 0; r--) {
+ if (safe_mas_in_row[r] < n)
+ continue;
+ uwb_rsv_gen_alloc_row(&mas, r, n, 1, UWB_NUM_ZONES);
+ if (uwb_drp_avail_reserve_pending(rsv->rc, &mas) == 0) {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ return -EBUSY;
+
+ bitmap_copy(rsv->mas.bm, mas.bm, UWB_NUM_MAS);
+ return 0;
+}
+
+static void uwb_rsv_stroke_timer(struct uwb_rsv *rsv)
+{
+ int sframes = UWB_MAX_LOST_BEACONS;
+
+ /*
+ * Multicast reservations can become established within 1
+ * super frame and should not be terminated if no response is
+ * received.
+ */
+ if (rsv->is_multicast) {
+ if (rsv->state == UWB_RSV_STATE_O_INITIATED)
+ sframes = 1;
+ if (rsv->state == UWB_RSV_STATE_O_ESTABLISHED)
+ sframes = 0;
+ }
+
+ rsv->expired = false;
+ if (sframes > 0) {
+ /*
+ * Add an additional 2 superframes to account for the
+ * time to send the SET DRP IE command.
+ */
+ unsigned timeout_us = (sframes + 2) * UWB_SUPERFRAME_LENGTH_US;
+ mod_timer(&rsv->timer, jiffies + usecs_to_jiffies(timeout_us));
+ } else
+ del_timer(&rsv->timer);
+}
+
+/*
+ * Update a reservations state, and schedule an update of the
+ * transmitted DRP IEs.
+ */
+static void uwb_rsv_state_update(struct uwb_rsv *rsv,
+ enum uwb_rsv_state new_state)
+{
+ rsv->state = new_state;
+ rsv->ie_valid = false;
+
+ uwb_rsv_dump(rsv);
+
+ uwb_rsv_stroke_timer(rsv);
+ uwb_rsv_sched_update(rsv->rc);
+}
+
+static void uwb_rsv_callback(struct uwb_rsv *rsv)
+{
+ if (rsv->callback)
+ rsv->callback(rsv);
+}
+
+void uwb_rsv_set_state(struct uwb_rsv *rsv, enum uwb_rsv_state new_state)
+{
+ if (rsv->state == new_state) {
+ switch (rsv->state) {
+ case UWB_RSV_STATE_O_ESTABLISHED:
+ case UWB_RSV_STATE_T_ACCEPTED:
+ case UWB_RSV_STATE_NONE:
+ uwb_rsv_stroke_timer(rsv);
+ break;
+ default:
+ /* Expecting a state transition so leave timer
+ as-is. */
+ break;
+ }
+ return;
+ }
+
+ switch (new_state) {
+ case UWB_RSV_STATE_NONE:
+ uwb_drp_avail_release(rsv->rc, &rsv->mas);
+ uwb_rsv_put_stream(rsv);
+ uwb_rsv_state_update(rsv, UWB_RSV_STATE_NONE);
+ uwb_rsv_callback(rsv);
+ break;
+ case UWB_RSV_STATE_O_INITIATED:
+ uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_INITIATED);
+ break;
+ case UWB_RSV_STATE_O_PENDING:
+ uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_PENDING);
+ break;
+ case UWB_RSV_STATE_O_ESTABLISHED:
+ uwb_drp_avail_reserve(rsv->rc, &rsv->mas);
+ uwb_rsv_state_update(rsv, UWB_RSV_STATE_O_ESTABLISHED);
+ uwb_rsv_callback(rsv);
+ break;
+ case UWB_RSV_STATE_T_ACCEPTED:
+ uwb_drp_avail_reserve(rsv->rc, &rsv->mas);
+ uwb_rsv_state_update(rsv, UWB_RSV_STATE_T_ACCEPTED);
+ uwb_rsv_callback(rsv);
+ break;
+ case UWB_RSV_STATE_T_DENIED:
+ uwb_rsv_state_update(rsv, UWB_RSV_STATE_T_DENIED);
+ break;
+ default:
+ dev_err(&rsv->rc->uwb_dev.dev, "unhandled state: %s (%d)\n",
+ uwb_rsv_state_str(new_state), new_state);
+ }
+}
+
+static struct uwb_rsv *uwb_rsv_alloc(struct uwb_rc *rc)
+{
+ struct uwb_rsv *rsv;
+
+ rsv = kzalloc(sizeof(struct uwb_rsv), GFP_KERNEL);
+ if (!rsv)
+ return NULL;
+
+ INIT_LIST_HEAD(&rsv->rc_node);
+ INIT_LIST_HEAD(&rsv->pal_node);
+ init_timer(&rsv->timer);
+ rsv->timer.function = uwb_rsv_timer;
+ rsv->timer.data = (unsigned long)rsv;
+
+ rsv->rc = rc;
+
+ return rsv;
+}
+
+static void uwb_rsv_free(struct uwb_rsv *rsv)
+{
+ uwb_dev_put(rsv->owner);
+ if (rsv->target.type == UWB_RSV_TARGET_DEV)
+ uwb_dev_put(rsv->target.dev);
+ kfree(rsv);
+}
+
+/**
+ * uwb_rsv_create - allocate and initialize a UWB reservation structure
+ * @rc: the radio controller
+ * @cb: callback to use when the reservation completes or terminates
+ * @pal_priv: data private to the PAL to be passed in the callback
+ *
+ * The callback is called when the state of the reservation changes from:
+ *
+ * - pending to accepted
+ * - pending to denined
+ * - accepted to terminated
+ * - pending to terminated
+ */
+struct uwb_rsv *uwb_rsv_create(struct uwb_rc *rc, uwb_rsv_cb_f cb, void *pal_priv)
+{
+ struct uwb_rsv *rsv;
+
+ rsv = uwb_rsv_alloc(rc);
+ if (!rsv)
+ return NULL;
+
+ rsv->callback = cb;
+ rsv->pal_priv = pal_priv;
+
+ return rsv;
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_create);
+
+void uwb_rsv_remove(struct uwb_rsv *rsv)
+{
+ if (rsv->state != UWB_RSV_STATE_NONE)
+ uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE);
+ del_timer_sync(&rsv->timer);
+ list_del(&rsv->rc_node);
+ uwb_rsv_free(rsv);
+}
+
+/**
+ * uwb_rsv_destroy - free a UWB reservation structure
+ * @rsv: the reservation to free
+ *
+ * The reservation will be terminated if it is pending or established.
+ */
+void uwb_rsv_destroy(struct uwb_rsv *rsv)
+{
+ struct uwb_rc *rc = rsv->rc;
+
+ mutex_lock(&rc->rsvs_mutex);
+ uwb_rsv_remove(rsv);
+ mutex_unlock(&rc->rsvs_mutex);
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_destroy);
+
+/**
+ * usb_rsv_establish - start a reservation establishment
+ * @rsv: the reservation
+ *
+ * The PAL should fill in @rsv's owner, target, type, max_mas,
+ * min_mas, sparsity and is_multicast fields. If the target is a
+ * uwb_dev it must be referenced.
+ *
+ * The reservation's callback will be called when the reservation is
+ * accepted, denied or times out.
+ */
+int uwb_rsv_establish(struct uwb_rsv *rsv)
+{
+ struct uwb_rc *rc = rsv->rc;
+ int ret;
+
+ mutex_lock(&rc->rsvs_mutex);
+
+ ret = uwb_rsv_get_stream(rsv);
+ if (ret)
+ goto out;
+
+ ret = uwb_rsv_alloc_mas(rsv);
+ if (ret) {
+ uwb_rsv_put_stream(rsv);
+ goto out;
+ }
+
+ list_add_tail(&rsv->rc_node, &rc->reservations);
+ rsv->owner = &rc->uwb_dev;
+ uwb_dev_get(rsv->owner);
+ uwb_rsv_set_state(rsv, UWB_RSV_STATE_O_INITIATED);
+out:
+ mutex_unlock(&rc->rsvs_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_establish);
+
+/**
+ * uwb_rsv_modify - modify an already established reservation
+ * @rsv: the reservation to modify
+ * @max_mas: new maximum MAS to reserve
+ * @min_mas: new minimum MAS to reserve
+ * @sparsity: new sparsity to use
+ *
+ * FIXME: implement this once there are PALs that use it.
+ */
+int uwb_rsv_modify(struct uwb_rsv *rsv, int max_mas, int min_mas, int sparsity)
+{
+ return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_modify);
+
+/**
+ * uwb_rsv_terminate - terminate an established reservation
+ * @rsv: the reservation to terminate
+ *
+ * A reservation is terminated by removing the DRP IE from the beacon,
+ * the other end will consider the reservation to be terminated when
+ * it does not see the DRP IE for at least mMaxLostBeacons.
+ *
+ * If applicable, the reference to the target uwb_dev will be released.
+ */
+void uwb_rsv_terminate(struct uwb_rsv *rsv)
+{
+ struct uwb_rc *rc = rsv->rc;
+
+ mutex_lock(&rc->rsvs_mutex);
+
+ uwb_rsv_set_state(rsv, UWB_RSV_STATE_NONE);
+
+ mutex_unlock(&rc->rsvs_mutex);
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_terminate);
+
+/**
+ * uwb_rsv_accept - accept a new reservation from a peer
+ * @rsv: the reservation
+ * @cb: call back for reservation changes
+ * @pal_priv: data to be passed in the above call back
+ *
+ * Reservation requests from peers are denied unless a PAL accepts it
+ * by calling this function.
+ */
+void uwb_rsv_accept(struct uwb_rsv *rsv, uwb_rsv_cb_f cb, void *pal_priv)
+{
+ rsv->callback = cb;
+ rsv->pal_priv = pal_priv;
+ rsv->state = UWB_RSV_STATE_T_ACCEPTED;
+}
+EXPORT_SYMBOL_GPL(uwb_rsv_accept);
+
+/*
+ * Is a received DRP IE for this reservation?
+ */
+static bool uwb_rsv_match(struct uwb_rsv *rsv, struct uwb_dev *src,
+ struct uwb_ie_drp *drp_ie)
+{
+ struct uwb_dev_addr *rsv_src;
+
+ if (rsv->stream != drp_ie->stream_index)
+ return false;
+
+ switch (rsv->target.type) {
+ case UWB_RSV_TARGET_DEVADDR:
+ return rsv->stream == drp_ie->stream_index;
+ case UWB_RSV_TARGET_DEV:
+ if (drp_ie->owner)
+ rsv_src = &rsv->owner->dev_addr;
+ else
+ rsv_src = &rsv->target.dev->dev_addr;
+ return uwb_dev_addr_cmp(&src->dev_addr, rsv_src) == 0;
+ }
+ return false;
+}
+
+static struct uwb_rsv *uwb_rsv_new_target(struct uwb_rc *rc,
+ struct uwb_dev *src,
+ struct uwb_ie_drp *drp_ie)
+{
+ struct uwb_rsv *rsv;
+ struct uwb_pal *pal;
+ enum uwb_rsv_state state;
+
+ rsv = uwb_rsv_alloc(rc);
+ if (!rsv)
+ return NULL;
+
+ rsv->rc = rc;
+ rsv->owner = src;
+ uwb_dev_get(rsv->owner);
+ rsv->target.type = UWB_RSV_TARGET_DEV;
+ rsv->target.dev = &rc->uwb_dev;
+ rsv->type = drp_ie->type;
+ rsv->stream = drp_ie->stream_index;
+ set_bit(rsv->stream, rsv->owner->streams);
+ uwb_drp_ie_to_bm(&rsv->mas, drp_ie);
+
+ /*
+ * See if any PALs are interested in this reservation. If not,
+ * deny the request.
+ */
+ rsv->state = UWB_RSV_STATE_T_DENIED;
+ spin_lock(&rc->pal_lock);
+ list_for_each_entry(pal, &rc->pals, node) {
+ if (pal->new_rsv)
+ pal->new_rsv(rsv);
+ if (rsv->state == UWB_RSV_STATE_T_ACCEPTED)
+ break;
+ }
+ spin_unlock(&rc->pal_lock);
+
+ list_add_tail(&rsv->rc_node, &rc->reservations);
+ state = rsv->state;
+ rsv->state = UWB_RSV_STATE_NONE;
+ uwb_rsv_set_state(rsv, state);
+
+ return rsv;
+}
+
+/**
+ * uwb_rsv_find - find a reservation for a received DRP IE.
+ * @rc: the radio controller
+ * @src: source of the DRP IE
+ * @drp_ie: the DRP IE
+ *
+ * If the reservation cannot be found and the DRP IE is from a peer
+ * attempting to establish a new reservation, create a new reservation
+ * and add it to the list.
+ */
+struct uwb_rsv *uwb_rsv_find(struct uwb_rc *rc, struct uwb_dev *src,
+ struct uwb_ie_drp *drp_ie)
+{
+ struct uwb_rsv *rsv;
+
+ list_for_each_entry(rsv, &rc->reservations, rc_node) {
+ if (uwb_rsv_match(rsv, src, drp_ie))
+ return rsv;
+ }
+
+ if (drp_ie->owner)
+ return uwb_rsv_new_target(rc, src, drp_ie);
+
+ return NULL;
+}
+
+/*
+ * Go through all the reservations and check for timeouts and (if
+ * necessary) update their DRP IEs.
+ *
+ * FIXME: look at building the SET_DRP_IE command here rather than
+ * having to rescan the list in uwb_rc_send_all_drp_ie().
+ */
+static bool uwb_rsv_update_all(struct uwb_rc *rc)
+{
+ struct uwb_rsv *rsv, *t;
+ bool ie_updated = false;
+
+ list_for_each_entry_safe(rsv, t, &rc->reservations, rc_node) {
+ if (rsv->expired)
+ uwb_drp_handle_timeout(rsv);
+ if (!rsv->ie_valid) {
+ uwb_drp_ie_update(rsv);
+ ie_updated = true;
+ }
+ }
+
+ return ie_updated;
+}
+
+void uwb_rsv_sched_update(struct uwb_rc *rc)
+{
+ queue_work(rc->rsv_workq, &rc->rsv_update_work);
+}
+
+/*
+ * Update DRP IEs and, if necessary, the DRP Availability IE and send
+ * the updated IEs to the radio controller.
+ */
+static void uwb_rsv_update_work(struct work_struct *work)
+{
+ struct uwb_rc *rc = container_of(work, struct uwb_rc, rsv_update_work);
+ bool ie_updated;
+
+ mutex_lock(&rc->rsvs_mutex);
+
+ ie_updated = uwb_rsv_update_all(rc);
+
+ if (!rc->drp_avail.ie_valid) {
+ uwb_drp_avail_ie_update(rc);
+ ie_updated = true;
+ }
+
+ if (ie_updated)
+ uwb_rc_send_all_drp_ie(rc);
+
+ mutex_unlock(&rc->rsvs_mutex);
+}
+
+static void uwb_rsv_timer(unsigned long arg)
+{
+ struct uwb_rsv *rsv = (struct uwb_rsv *)arg;
+
+ rsv->expired = true;
+ uwb_rsv_sched_update(rsv->rc);
+}
+
+void uwb_rsv_init(struct uwb_rc *rc)
+{
+ INIT_LIST_HEAD(&rc->reservations);
+ mutex_init(&rc->rsvs_mutex);
+ INIT_WORK(&rc->rsv_update_work, uwb_rsv_update_work);
+
+ bitmap_complement(rc->uwb_dev.streams, rc->uwb_dev.streams, UWB_NUM_STREAMS);
+}
+
+int uwb_rsv_setup(struct uwb_rc *rc)
+{
+ char name[16];
+
+ snprintf(name, sizeof(name), "%s_rsvd", dev_name(&rc->uwb_dev.dev));
+ rc->rsv_workq = create_singlethread_workqueue(name);
+ if (rc->rsv_workq == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void uwb_rsv_cleanup(struct uwb_rc *rc)
+{
+ struct uwb_rsv *rsv, *t;
+
+ mutex_lock(&rc->rsvs_mutex);
+ list_for_each_entry_safe(rsv, t, &rc->reservations, rc_node) {
+ uwb_rsv_remove(rsv);
+ }
+ mutex_unlock(&rc->rsvs_mutex);
+
+ cancel_work_sync(&rc->rsv_update_work);
+ destroy_workqueue(rc->rsv_workq);
+}
diff --git a/drivers/uwb/scan.c b/drivers/uwb/scan.c
new file mode 100644
index 000000000000..e8e62359c6ce
--- /dev/null
+++ b/drivers/uwb/scan.c
@@ -0,0 +1,159 @@
+/*
+ * Ultra Wide Band
+ * Scanning management
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ *
+ * FIXME: docs
+ * FIXME: there are issues here on how BEACON and SCAN on USB RCI deal
+ * with each other. Currently seems that START_BEACON while
+ * SCAN_ONLY will cancel the scan, so we need to update the
+ * state here. Clarification request sent by email on
+ * 10/05/2005.
+ * 10/28/2005 No clear answer heard--maybe we'll hack the API
+ * so that when we start beaconing, if the HC is
+ * scanning in a mode not compatible with beaconing
+ * we just fail.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include "uwb-internal.h"
+
+
+/**
+ * Start/stop scanning in a radio controller
+ *
+ * @rc: UWB Radio Controlller
+ * @channel: Channel to scan; encodings in WUSB1.0[Table 5.12]
+ * @type: Type of scanning to do.
+ * @bpst_offset: value at which to start scanning (if type ==
+ * UWB_SCAN_ONLY_STARTTIME)
+ * @returns: 0 if ok, < 0 errno code on error
+ *
+ * We put the command on kmalloc'ed memory as some arches cannot do
+ * USB from the stack. The reply event is copied from an stage buffer,
+ * so it can be in the stack. See WUSB1.0[8.6.2.4] for more details.
+ */
+int uwb_rc_scan(struct uwb_rc *rc,
+ unsigned channel, enum uwb_scan_type type,
+ unsigned bpst_offset)
+{
+ int result;
+ struct uwb_rc_cmd_scan *cmd;
+ struct uwb_rc_evt_confirm reply;
+
+ result = -ENOMEM;
+ cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
+ if (cmd == NULL)
+ goto error_kzalloc;
+ mutex_lock(&rc->uwb_dev.mutex);
+ cmd->rccb.bCommandType = UWB_RC_CET_GENERAL;
+ cmd->rccb.wCommand = cpu_to_le16(UWB_RC_CMD_SCAN);
+ cmd->bChannelNumber = channel;
+ cmd->bScanState = type;
+ cmd->wStartTime = cpu_to_le16(bpst_offset);
+ reply.rceb.bEventType = UWB_RC_CET_GENERAL;
+ reply.rceb.wEvent = UWB_RC_CMD_SCAN;
+ result = uwb_rc_cmd(rc, "SCAN", &cmd->rccb, sizeof(*cmd),
+ &reply.rceb, sizeof(reply));
+ if (result < 0)
+ goto error_cmd;
+ if (reply.bResultCode != UWB_RC_RES_SUCCESS) {
+ dev_err(&rc->uwb_dev.dev,
+ "SCAN: command execution failed: %s (%d)\n",
+ uwb_rc_strerror(reply.bResultCode), reply.bResultCode);
+ result = -EIO;
+ goto error_cmd;
+ }
+ rc->scanning = channel;
+ rc->scan_type = type;
+error_cmd:
+ mutex_unlock(&rc->uwb_dev.mutex);
+ kfree(cmd);
+error_kzalloc:
+ return result;
+}
+
+static const char *scan_type_name[] = {
+ "scanning-only",
+ "scanning-outside beacon period",
+ "scanning-while inactive",
+ "scanning-disabled",
+ "scanning-only starttime"
+};
+
+/*
+ * Print scanning state
+ */
+static ssize_t uwb_rc_scan_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_rc *rc = uwb_dev->rc;
+ enum uwb_scan_type type;
+ ssize_t result;
+ unsigned cnt;
+
+ mutex_lock(&rc->uwb_dev.mutex);
+ type = rc->scan_type;
+ BUG_ON(type > UWB_SCAN_ONLY_STARTTIME || type < UWB_SCAN_ONLY);
+ result = sprintf(buf,
+ "state: %s\n"
+ "channel: %d\n"
+ "\n"
+ "# CHANNEL TYPE [BPST-OFFSET]\n"
+ "# start/stop scanning on CHANNEL\n"
+ "#\n"
+ "# Types:\n",
+ scan_type_name[type], rc->scanning);
+ mutex_unlock(&rc->uwb_dev.mutex);
+ for (cnt = 0; cnt <= UWB_SCAN_ONLY_STARTTIME; cnt++)
+ result += sprintf(buf + result, "# %d - %s\n",
+ cnt, scan_type_name[cnt]);
+ return result;
+}
+
+/*
+ *
+ */
+static ssize_t uwb_rc_scan_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+ struct uwb_rc *rc = uwb_dev->rc;
+ ssize_t result;
+ unsigned channel;
+ unsigned type;
+ unsigned bpst_offset = 0;
+
+ result = sscanf(buf, "%u %u %u\n", &channel, &type, &bpst_offset);
+ if (result < 2 || type >= UWB_SCAN_TOP) {
+ result = -EINVAL;
+ goto error;
+ }
+ result = uwb_rc_scan(rc, channel, type, bpst_offset);
+error:
+ return result < 0 ? result : size;
+}
+
+/** Radio Control sysfs interface (declaration) */
+DEVICE_ATTR(scan, S_IRUGO | S_IWUSR, uwb_rc_scan_show, uwb_rc_scan_store);
diff --git a/drivers/uwb/umc-bus.c b/drivers/uwb/umc-bus.c
new file mode 100644
index 000000000000..b165c83fad0f
--- /dev/null
+++ b/drivers/uwb/umc-bus.c
@@ -0,0 +1,185 @@
+/*
+ * Bus for UWB Multi-interface Controller capabilities.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ *
+ * This file is released under the GNU GPL v2.
+ */
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/workqueue.h>
+#include <linux/uwb/umc.h>
+#include <linux/pci.h>
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+
+/**
+ * umc_match_pci_id - match a UMC driver to a UMC device's parent PCI device.
+ * @umc_drv: umc driver with match_data pointing to a zero-terminated
+ * table of pci_device_id's.
+ * @umc: umc device whose parent is to be matched.
+ */
+int umc_match_pci_id(struct umc_driver *umc_drv, struct umc_dev *umc)
+{
+ const struct pci_device_id *id_table = umc_drv->match_data;
+ struct pci_dev *pci;
+
+ if (umc->dev.parent->bus != &pci_bus_type)
+ return 0;
+
+ pci = to_pci_dev(umc->dev.parent);
+ return pci_match_id(id_table, pci) != NULL;
+}
+EXPORT_SYMBOL_GPL(umc_match_pci_id);
+
+static int umc_bus_rescan_helper(struct device *dev, void *data)
+{
+ int ret = 0;
+
+ if (!dev->driver)
+ ret = device_attach(dev);
+
+ return ret < 0 ? ret : 0;
+}
+
+static void umc_bus_rescan(void)
+{
+ int err;
+
+ /*
+ * We can't use bus_rescan_devices() here as it deadlocks when
+ * it tries to retake the dev->parent semaphore.
+ */
+ err = bus_for_each_dev(&umc_bus_type, NULL, NULL, umc_bus_rescan_helper);
+ if (err < 0)
+ printk(KERN_WARNING "%s: rescan of bus failed: %d\n",
+ KBUILD_MODNAME, err);
+}
+
+static int umc_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct umc_dev *umc = to_umc_dev(dev);
+ struct umc_driver *umc_driver = to_umc_driver(drv);
+
+ if (umc->cap_id == umc_driver->cap_id) {
+ if (umc_driver->match)
+ return umc_driver->match(umc_driver, umc);
+ else
+ return 1;
+ }
+ return 0;
+}
+
+static int umc_device_probe(struct device *dev)
+{
+ struct umc_dev *umc;
+ struct umc_driver *umc_driver;
+ int err;
+
+ umc_driver = to_umc_driver(dev->driver);
+ umc = to_umc_dev(dev);
+
+ get_device(dev);
+ err = umc_driver->probe(umc);
+ if (err)
+ put_device(dev);
+ else
+ umc_bus_rescan();
+
+ return err;
+}
+
+static int umc_device_remove(struct device *dev)
+{
+ struct umc_dev *umc;
+ struct umc_driver *umc_driver;
+
+ umc_driver = to_umc_driver(dev->driver);
+ umc = to_umc_dev(dev);
+
+ umc_driver->remove(umc);
+ put_device(dev);
+ return 0;
+}
+
+static int umc_device_suspend(struct device *dev, pm_message_t state)
+{
+ struct umc_dev *umc;
+ struct umc_driver *umc_driver;
+ int err = 0;
+
+ umc = to_umc_dev(dev);
+
+ if (dev->driver) {
+ umc_driver = to_umc_driver(dev->driver);
+ if (umc_driver->suspend)
+ err = umc_driver->suspend(umc, state);
+ }
+ return err;
+}
+
+static int umc_device_resume(struct device *dev)
+{
+ struct umc_dev *umc;
+ struct umc_driver *umc_driver;
+ int err = 0;
+
+ umc = to_umc_dev(dev);
+
+ if (dev->driver) {
+ umc_driver = to_umc_driver(dev->driver);
+ if (umc_driver->resume)
+ err = umc_driver->resume(umc);
+ }
+ return err;
+}
+
+static ssize_t capability_id_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct umc_dev *umc = to_umc_dev(dev);
+
+ return sprintf(buf, "0x%02x\n", umc->cap_id);
+}
+
+static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct umc_dev *umc = to_umc_dev(dev);
+
+ return sprintf(buf, "0x%04x\n", umc->version);
+}
+
+static struct device_attribute umc_dev_attrs[] = {
+ __ATTR_RO(capability_id),
+ __ATTR_RO(version),
+ __ATTR_NULL,
+};
+
+struct bus_type umc_bus_type = {
+ .name = "umc",
+ .match = umc_bus_match,
+ .probe = umc_device_probe,
+ .remove = umc_device_remove,
+ .suspend = umc_device_suspend,
+ .resume = umc_device_resume,
+ .dev_attrs = umc_dev_attrs,
+};
+EXPORT_SYMBOL_GPL(umc_bus_type);
+
+static int __init umc_bus_init(void)
+{
+ return bus_register(&umc_bus_type);
+}
+module_init(umc_bus_init);
+
+static void __exit umc_bus_exit(void)
+{
+ d_fnstart(4, NULL, "()\n");
+ bus_unregister(&umc_bus_type);
+ d_fnend(4, NULL, "() = void\n");
+}
+module_exit(umc_bus_exit);
+
+MODULE_DESCRIPTION("UWB Multi-interface Controller capability bus");
+MODULE_AUTHOR("Cambridge Silicon Radio Ltd.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/uwb/umc-dev.c b/drivers/uwb/umc-dev.c
new file mode 100644
index 000000000000..53207e14cd8f
--- /dev/null
+++ b/drivers/uwb/umc-dev.c
@@ -0,0 +1,103 @@
+/*
+ * UWB Multi-interface Controller device management.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ *
+ * This file is released under the GNU GPL v2.
+ */
+#include <linux/kernel.h>
+#include <linux/uwb/umc.h>
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+static void umc_device_release(struct device *dev)
+{
+ struct umc_dev *umc = to_umc_dev(dev);
+
+ kfree(umc);
+}
+
+/**
+ * umc_device_create - allocate a child UMC device
+ * @parent: parent of the new UMC device.
+ * @n: index of the new device.
+ *
+ * The new UMC device will have a bus ID of the parent with '-n'
+ * appended.
+ */
+struct umc_dev *umc_device_create(struct device *parent, int n)
+{
+ struct umc_dev *umc;
+
+ umc = kzalloc(sizeof(struct umc_dev), GFP_KERNEL);
+ if (umc) {
+ dev_set_name(&umc->dev, "%s-%d", dev_name(parent), n);
+ umc->dev.parent = parent;
+ umc->dev.bus = &umc_bus_type;
+ umc->dev.release = umc_device_release;
+
+ umc->dev.dma_mask = parent->dma_mask;
+ }
+ return umc;
+}
+EXPORT_SYMBOL_GPL(umc_device_create);
+
+/**
+ * umc_device_register - register a UMC device
+ * @umc: pointer to the UMC device
+ *
+ * The memory resource for the UMC device is acquired and the device
+ * registered with the system.
+ */
+int umc_device_register(struct umc_dev *umc)
+{
+ int err;
+
+ d_fnstart(3, &umc->dev, "(umc_dev %p)\n", umc);
+
+ err = request_resource(umc->resource.parent, &umc->resource);
+ if (err < 0) {
+ dev_err(&umc->dev, "can't allocate resource range "
+ "%016Lx to %016Lx: %d\n",
+ (unsigned long long)umc->resource.start,
+ (unsigned long long)umc->resource.end,
+ err);
+ goto error_request_resource;
+ }
+
+ err = device_register(&umc->dev);
+ if (err < 0)
+ goto error_device_register;
+ d_fnend(3, &umc->dev, "(umc_dev %p) = 0\n", umc);
+ return 0;
+
+error_device_register:
+ release_resource(&umc->resource);
+error_request_resource:
+ d_fnend(3, &umc->dev, "(umc_dev %p) = %d\n", umc, err);
+ return err;
+}
+EXPORT_SYMBOL_GPL(umc_device_register);
+
+/**
+ * umc_device_unregister - unregister a UMC device
+ * @umc: pointer to the UMC device
+ *
+ * First we unregister the device, make sure the driver can do it's
+ * resource release thing and then we try to release any left over
+ * resources. We take a ref to the device, to make sure it doesn't
+ * dissapear under our feet.
+ */
+void umc_device_unregister(struct umc_dev *umc)
+{
+ struct device *dev;
+ if (!umc)
+ return;
+ dev = get_device(&umc->dev);
+ d_fnstart(3, dev, "(umc_dev %p)\n", umc);
+ device_unregister(&umc->dev);
+ release_resource(&umc->resource);
+ d_fnend(3, dev, "(umc_dev %p) = void\n", umc);
+ put_device(dev);
+}
+EXPORT_SYMBOL_GPL(umc_device_unregister);
diff --git a/drivers/uwb/umc-drv.c b/drivers/uwb/umc-drv.c
new file mode 100644
index 000000000000..367b5eb85d60
--- /dev/null
+++ b/drivers/uwb/umc-drv.c
@@ -0,0 +1,31 @@
+/*
+ * UWB Multi-interface Controller driver management.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ *
+ * This file is released under the GNU GPL v2.
+ */
+#include <linux/kernel.h>
+#include <linux/uwb/umc.h>
+
+int __umc_driver_register(struct umc_driver *umc_drv, struct module *module,
+ const char *mod_name)
+{
+ umc_drv->driver.name = umc_drv->name;
+ umc_drv->driver.owner = module;
+ umc_drv->driver.mod_name = mod_name;
+ umc_drv->driver.bus = &umc_bus_type;
+
+ return driver_register(&umc_drv->driver);
+}
+EXPORT_SYMBOL_GPL(__umc_driver_register);
+
+/**
+ * umc_driver_register - unregister a UMC capabiltity driver.
+ * @umc_drv: pointer to the driver.
+ */
+void umc_driver_unregister(struct umc_driver *umc_drv)
+{
+ driver_unregister(&umc_drv->driver);
+}
+EXPORT_SYMBOL_GPL(umc_driver_unregister);
diff --git a/drivers/uwb/uwb-debug.c b/drivers/uwb/uwb-debug.c
new file mode 100644
index 000000000000..5828cbc4a292
--- /dev/null
+++ b/drivers/uwb/uwb-debug.c
@@ -0,0 +1,367 @@
+/*
+ * Ultra Wide Band
+ * Debug support
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: doc
+ */
+
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/notifier.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/seq_file.h>
+
+#include <linux/uwb/debug-cmd.h>
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+#include "uwb-internal.h"
+
+void dump_bytes(struct device *dev, const void *_buf, size_t rsize)
+{
+ const char *buf = _buf;
+ char line[32];
+ size_t offset = 0;
+ int cnt, cnt2;
+ for (cnt = 0; cnt < rsize; cnt += 8) {
+ size_t rtop = rsize - cnt < 8? rsize - cnt : 8;
+ for (offset = cnt2 = 0; cnt2 < rtop; cnt2++) {
+ offset += scnprintf(line + offset, sizeof(line) - offset,
+ "%02x ", buf[cnt + cnt2] & 0xff);
+ }
+ if (dev)
+ dev_info(dev, "%s\n", line);
+ else
+ printk(KERN_INFO "%s\n", line);
+ }
+}
+EXPORT_SYMBOL_GPL(dump_bytes);
+
+/*
+ * Debug interface
+ *
+ * Per radio controller debugfs files (in uwb/uwbN/):
+ *
+ * command: Flexible command interface (see <linux/uwb/debug-cmd.h>).
+ *
+ * reservations: information on reservations.
+ *
+ * accept: Set to true (Y or 1) to accept reservation requests from
+ * peers.
+ *
+ * drp_avail: DRP availability information.
+ */
+
+struct uwb_dbg {
+ struct uwb_pal pal;
+
+ u32 accept;
+ struct list_head rsvs;
+
+ struct dentry *root_d;
+ struct dentry *command_f;
+ struct dentry *reservations_f;
+ struct dentry *accept_f;
+ struct dentry *drp_avail_f;
+};
+
+static struct dentry *root_dir;
+
+static void uwb_dbg_rsv_cb(struct uwb_rsv *rsv)
+{
+ struct uwb_rc *rc = rsv->rc;
+ struct device *dev = &rc->uwb_dev.dev;
+ struct uwb_dev_addr devaddr;
+ char owner[UWB_ADDR_STRSIZE], target[UWB_ADDR_STRSIZE];
+
+ uwb_dev_addr_print(owner, sizeof(owner), &rsv->owner->dev_addr);
+ if (rsv->target.type == UWB_RSV_TARGET_DEV)
+ devaddr = rsv->target.dev->dev_addr;
+ else
+ devaddr = rsv->target.devaddr;
+ uwb_dev_addr_print(target, sizeof(target), &devaddr);
+
+ dev_dbg(dev, "debug: rsv %s -> %s: %s\n",
+ owner, target, uwb_rsv_state_str(rsv->state));
+}
+
+static int cmd_rsv_establish(struct uwb_rc *rc,
+ struct uwb_dbg_cmd_rsv_establish *cmd)
+{
+ struct uwb_mac_addr macaddr;
+ struct uwb_rsv *rsv;
+ struct uwb_dev *target;
+ int ret;
+
+ memcpy(&macaddr, cmd->target, sizeof(macaddr));
+ target = uwb_dev_get_by_macaddr(rc, &macaddr);
+ if (target == NULL)
+ return -ENODEV;
+
+ rsv = uwb_rsv_create(rc, uwb_dbg_rsv_cb, NULL);
+ if (rsv == NULL) {
+ uwb_dev_put(target);
+ return -ENOMEM;
+ }
+
+ rsv->owner = &rc->uwb_dev;
+ rsv->target.type = UWB_RSV_TARGET_DEV;
+ rsv->target.dev = target;
+ rsv->type = cmd->type;
+ rsv->max_mas = cmd->max_mas;
+ rsv->min_mas = cmd->min_mas;
+ rsv->sparsity = cmd->sparsity;
+
+ ret = uwb_rsv_establish(rsv);
+ if (ret)
+ uwb_rsv_destroy(rsv);
+ else
+ list_add_tail(&rsv->pal_node, &rc->dbg->rsvs);
+
+ return ret;
+}
+
+static int cmd_rsv_terminate(struct uwb_rc *rc,
+ struct uwb_dbg_cmd_rsv_terminate *cmd)
+{
+ struct uwb_rsv *rsv, *found = NULL;
+ int i = 0;
+
+ list_for_each_entry(rsv, &rc->dbg->rsvs, pal_node) {
+ if (i == cmd->index) {
+ found = rsv;
+ break;
+ }
+ }
+ if (!found)
+ return -EINVAL;
+
+ list_del(&found->pal_node);
+ uwb_rsv_terminate(found);
+
+ return 0;
+}
+
+static int command_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+
+ return 0;
+}
+
+static ssize_t command_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *off)
+{
+ struct uwb_rc *rc = file->private_data;
+ struct uwb_dbg_cmd cmd;
+ int ret;
+
+ if (len != sizeof(struct uwb_dbg_cmd))
+ return -EINVAL;
+
+ if (copy_from_user(&cmd, buf, len) != 0)
+ return -EFAULT;
+
+ switch (cmd.type) {
+ case UWB_DBG_CMD_RSV_ESTABLISH:
+ ret = cmd_rsv_establish(rc, &cmd.rsv_establish);
+ break;
+ case UWB_DBG_CMD_RSV_TERMINATE:
+ ret = cmd_rsv_terminate(rc, &cmd.rsv_terminate);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret < 0 ? ret : len;
+}
+
+static struct file_operations command_fops = {
+ .open = command_open,
+ .write = command_write,
+ .read = NULL,
+ .llseek = no_llseek,
+ .owner = THIS_MODULE,
+};
+
+static int reservations_print(struct seq_file *s, void *p)
+{
+ struct uwb_rc *rc = s->private;
+ struct uwb_rsv *rsv;
+
+ mutex_lock(&rc->rsvs_mutex);
+
+ list_for_each_entry(rsv, &rc->reservations, rc_node) {
+ struct uwb_dev_addr devaddr;
+ char owner[UWB_ADDR_STRSIZE], target[UWB_ADDR_STRSIZE];
+ bool is_owner;
+ char buf[72];
+
+ uwb_dev_addr_print(owner, sizeof(owner), &rsv->owner->dev_addr);
+ if (rsv->target.type == UWB_RSV_TARGET_DEV) {
+ devaddr = rsv->target.dev->dev_addr;
+ is_owner = &rc->uwb_dev == rsv->owner;
+ } else {
+ devaddr = rsv->target.devaddr;
+ is_owner = true;
+ }
+ uwb_dev_addr_print(target, sizeof(target), &devaddr);
+
+ seq_printf(s, "%c %s -> %s: %s\n",
+ is_owner ? 'O' : 'T',
+ owner, target, uwb_rsv_state_str(rsv->state));
+ seq_printf(s, " stream: %d type: %s\n",
+ rsv->stream, uwb_rsv_type_str(rsv->type));
+ bitmap_scnprintf(buf, sizeof(buf), rsv->mas.bm, UWB_NUM_MAS);
+ seq_printf(s, " %s\n", buf);
+ }
+
+ mutex_unlock(&rc->rsvs_mutex);
+
+ return 0;
+}
+
+static int reservations_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, reservations_print, inode->i_private);
+}
+
+static struct file_operations reservations_fops = {
+ .open = reservations_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int drp_avail_print(struct seq_file *s, void *p)
+{
+ struct uwb_rc *rc = s->private;
+ char buf[72];
+
+ bitmap_scnprintf(buf, sizeof(buf), rc->drp_avail.global, UWB_NUM_MAS);
+ seq_printf(s, "global: %s\n", buf);
+ bitmap_scnprintf(buf, sizeof(buf), rc->drp_avail.local, UWB_NUM_MAS);
+ seq_printf(s, "local: %s\n", buf);
+ bitmap_scnprintf(buf, sizeof(buf), rc->drp_avail.pending, UWB_NUM_MAS);
+ seq_printf(s, "pending: %s\n", buf);
+
+ return 0;
+}
+
+static int drp_avail_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, drp_avail_print, inode->i_private);
+}
+
+static struct file_operations drp_avail_fops = {
+ .open = drp_avail_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static void uwb_dbg_new_rsv(struct uwb_rsv *rsv)
+{
+ struct uwb_rc *rc = rsv->rc;
+
+ if (rc->dbg->accept)
+ uwb_rsv_accept(rsv, uwb_dbg_rsv_cb, NULL);
+}
+
+/**
+ * uwb_dbg_add_rc - add a debug interface for a radio controller
+ * @rc: the radio controller
+ */
+void uwb_dbg_add_rc(struct uwb_rc *rc)
+{
+ rc->dbg = kzalloc(sizeof(struct uwb_dbg), GFP_KERNEL);
+ if (rc->dbg == NULL)
+ return;
+
+ INIT_LIST_HEAD(&rc->dbg->rsvs);
+
+ uwb_pal_init(&rc->dbg->pal);
+ rc->dbg->pal.new_rsv = uwb_dbg_new_rsv;
+ uwb_pal_register(rc, &rc->dbg->pal);
+ if (root_dir) {
+ rc->dbg->root_d = debugfs_create_dir(dev_name(&rc->uwb_dev.dev),
+ root_dir);
+ rc->dbg->command_f = debugfs_create_file("command", 0200,
+ rc->dbg->root_d, rc,
+ &command_fops);
+ rc->dbg->reservations_f = debugfs_create_file("reservations", 0444,
+ rc->dbg->root_d, rc,
+ &reservations_fops);
+ rc->dbg->accept_f = debugfs_create_bool("accept", 0644,
+ rc->dbg->root_d,
+ &rc->dbg->accept);
+ rc->dbg->drp_avail_f = debugfs_create_file("drp_avail", 0444,
+ rc->dbg->root_d, rc,
+ &drp_avail_fops);
+ }
+}
+
+/**
+ * uwb_dbg_add_rc - remove a radio controller's debug interface
+ * @rc: the radio controller
+ */
+void uwb_dbg_del_rc(struct uwb_rc *rc)
+{
+ struct uwb_rsv *rsv, *t;
+
+ if (rc->dbg == NULL)
+ return;
+
+ list_for_each_entry_safe(rsv, t, &rc->dbg->rsvs, pal_node) {
+ uwb_rsv_destroy(rsv);
+ }
+
+ uwb_pal_unregister(rc, &rc->dbg->pal);
+
+ if (root_dir) {
+ debugfs_remove(rc->dbg->drp_avail_f);
+ debugfs_remove(rc->dbg->accept_f);
+ debugfs_remove(rc->dbg->reservations_f);
+ debugfs_remove(rc->dbg->command_f);
+ debugfs_remove(rc->dbg->root_d);
+ }
+}
+
+/**
+ * uwb_dbg_exit - initialize the debug interface sub-module
+ */
+void uwb_dbg_init(void)
+{
+ root_dir = debugfs_create_dir("uwb", NULL);
+}
+
+/**
+ * uwb_dbg_exit - clean-up the debug interface sub-module
+ */
+void uwb_dbg_exit(void)
+{
+ debugfs_remove(root_dir);
+}
diff --git a/drivers/uwb/uwb-internal.h b/drivers/uwb/uwb-internal.h
new file mode 100644
index 000000000000..e0dc4aac1151
--- /dev/null
+++ b/drivers/uwb/uwb-internal.h
@@ -0,0 +1,283 @@
+/*
+ * Ultra Wide Band
+ * UWB internal API
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * This contains most of the internal API for UWB. This is stuff used
+ * across the stack that of course, is of no interest to the rest.
+ *
+ * Some parts might end up going public (like uwb_rc_*())...
+ */
+
+#ifndef __UWB_INTERNAL_H__
+#define __UWB_INTERNAL_H__
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/uwb.h>
+#include <linux/mutex.h>
+
+struct uwb_beca_e;
+
+/* General device API */
+extern void uwb_dev_init(struct uwb_dev *uwb_dev);
+extern int __uwb_dev_offair(struct uwb_dev *, struct uwb_rc *);
+extern int uwb_dev_add(struct uwb_dev *uwb_dev, struct device *parent_dev,
+ struct uwb_rc *parent_rc);
+extern void uwb_dev_rm(struct uwb_dev *uwb_dev);
+extern void uwbd_dev_onair(struct uwb_rc *, struct uwb_beca_e *);
+extern void uwbd_dev_offair(struct uwb_beca_e *);
+void uwb_notify(struct uwb_rc *rc, struct uwb_dev *uwb_dev, enum uwb_notifs event);
+
+/* General UWB Radio Controller Internal API */
+extern struct uwb_rc *__uwb_rc_try_get(struct uwb_rc *);
+static inline struct uwb_rc *__uwb_rc_get(struct uwb_rc *rc)
+{
+ uwb_dev_get(&rc->uwb_dev);
+ return rc;
+}
+
+static inline void __uwb_rc_put(struct uwb_rc *rc)
+{
+ uwb_dev_put(&rc->uwb_dev);
+}
+
+extern int uwb_rc_reset(struct uwb_rc *rc);
+extern int uwb_rc_beacon(struct uwb_rc *rc,
+ int channel, unsigned bpst_offset);
+extern int uwb_rc_scan(struct uwb_rc *rc,
+ unsigned channel, enum uwb_scan_type type,
+ unsigned bpst_offset);
+extern int uwb_rc_send_all_drp_ie(struct uwb_rc *rc);
+extern ssize_t uwb_rc_print_IEs(struct uwb_rc *rc, char *, size_t);
+extern void uwb_rc_ie_init(struct uwb_rc *);
+extern void uwb_rc_ie_init(struct uwb_rc *);
+extern ssize_t uwb_rc_ie_setup(struct uwb_rc *);
+extern void uwb_rc_ie_release(struct uwb_rc *);
+extern int uwb_rc_ie_add(struct uwb_rc *,
+ const struct uwb_ie_hdr *, size_t);
+extern int uwb_rc_ie_rm(struct uwb_rc *, enum uwb_ie);
+extern int uwb_rc_set_identification_ie(struct uwb_rc *);
+
+extern const char *uwb_rc_strerror(unsigned code);
+
+/*
+ * Time to wait for a response to an RC command.
+ *
+ * Some commands can take a long time to response. e.g., START_BEACON
+ * may scan for several superframes before joining an existing beacon
+ * group and this can take around 600 ms.
+ */
+#define UWB_RC_CMD_TIMEOUT_MS 1000 /* ms */
+
+/*
+ * Notification/Event Handlers
+ */
+
+struct uwb_rc_neh;
+
+typedef void (*uwb_rc_neh_cb_f)(struct uwb_rc *rc, void *arg,
+ struct uwb_rceb *reply, ssize_t reply_size);
+
+void uwb_rc_neh_create(struct uwb_rc *rc);
+void uwb_rc_neh_destroy(struct uwb_rc *rc);
+
+struct uwb_rc_neh *uwb_rc_neh_add(struct uwb_rc *rc, struct uwb_rccb *cmd,
+ u8 expected_type, u16 expected_event,
+ uwb_rc_neh_cb_f cb, void *arg);
+void uwb_rc_neh_rm(struct uwb_rc *rc, struct uwb_rc_neh *neh);
+void uwb_rc_neh_arm(struct uwb_rc *rc, struct uwb_rc_neh *neh);
+void uwb_rc_neh_put(struct uwb_rc_neh *neh);
+
+/* Event size tables */
+extern int uwb_est_create(void);
+extern void uwb_est_destroy(void);
+
+
+/*
+ * UWB Events & management daemon
+ *
+ * The UWBD daemon receives events following the RCI interface for
+ * notifications (with an RCEB header)...basically it acts on the
+ * notifications from that the radio controllers pipe to him.
+ *
+ * FIXME: rename events to notifications (as that's what they
+ * are)...however, that'll mess with uwb_notifs.
+ */
+
+/**
+ * UWB Event
+ *
+ * @rc: Radio controller that emitted the event (referenced)
+ * @size: Size of the buffer (ie: Guaranteed to contain at least
+ * a full 'struct uwb_rceb')
+ * @ts_jiffies: Timestamp, when was it received
+ * @rceb: Pointer to a kmalloced() event payload
+ */
+/* FIXME: rename to uwb_evt for consistency */
+struct uwb_event {
+ struct list_head list_node;
+ struct uwb_rc *rc;
+ size_t size;
+ unsigned long ts_jiffies;
+ struct uwb_rceb *rceb;
+};
+extern void uwbd_start(void);
+extern void uwbd_stop(void);
+extern struct uwb_event *uwb_event_alloc(size_t, gfp_t gfp_mask);
+extern void uwbd_event_queue(struct uwb_event *);
+
+/* UWB event handlers */
+extern int uwbd_evt_handle_rc_beacon(struct uwb_event *);
+extern int uwbd_evt_handle_rc_beacon_size(struct uwb_event *);
+extern int uwbd_evt_handle_rc_bpoie_change(struct uwb_event *);
+extern int uwbd_evt_handle_rc_bp_slot_change(struct uwb_event *);
+extern int uwbd_evt_handle_rc_drp(struct uwb_event *);
+extern int uwbd_evt_handle_rc_drp_avail(struct uwb_event *);
+
+
+/*
+ * Address management
+ */
+int uwb_rc_dev_addr_assign(struct uwb_rc *rc);
+int uwbd_evt_handle_rc_dev_addr_conflict(struct uwb_event *evt);
+
+/*
+ * UWB Beacon Cache
+ *
+ * Each beacon we received is kept in a cache--when we receive that
+ * beacon consistently, that means there is a new device that we have
+ * to add to the system.
+ */
+
+extern unsigned long beacon_timeout_ms;
+
+/** Beacon cache list */
+struct uwb_beca {
+ struct list_head list;
+ size_t entries;
+ struct mutex mutex;
+};
+
+extern struct uwb_beca uwb_beca;
+
+/**
+ * Beacon cache entry
+ *
+ * @jiffies_refresh: last time a beacon was received that refreshed
+ * this cache entry.
+ * @uwb_dev: device connected to this beacon. This pointer is not
+ * safe, you need to get it with uwb_dev_try_get()
+ *
+ * @hits: how many time we have seen this beacon since last time we
+ * cleared it
+ */
+struct uwb_beca_e {
+ struct mutex mutex;
+ struct kref refcnt;
+ struct list_head node;
+ struct uwb_mac_addr *mac_addr;
+ struct uwb_dev_addr dev_addr;
+ u8 hits;
+ unsigned long ts_jiffies;
+ struct uwb_dev *uwb_dev;
+ struct uwb_rc_evt_beacon *be;
+ struct stats lqe_stats, rssi_stats; /* radio statistics */
+};
+struct uwb_beacon_frame;
+extern ssize_t uwb_bce_print_IEs(struct uwb_dev *, struct uwb_beca_e *,
+ char *, size_t);
+extern struct uwb_beca_e *__uwb_beca_add(struct uwb_rc_evt_beacon *,
+ struct uwb_beacon_frame *,
+ unsigned long);
+
+extern void uwb_bce_kfree(struct kref *_bce);
+static inline void uwb_bce_get(struct uwb_beca_e *bce)
+{
+ kref_get(&bce->refcnt);
+}
+static inline void uwb_bce_put(struct uwb_beca_e *bce)
+{
+ kref_put(&bce->refcnt, uwb_bce_kfree);
+}
+extern void uwb_beca_purge(void);
+extern void uwb_beca_release(void);
+
+struct uwb_dev *uwb_dev_get_by_devaddr(struct uwb_rc *rc,
+ const struct uwb_dev_addr *devaddr);
+struct uwb_dev *uwb_dev_get_by_macaddr(struct uwb_rc *rc,
+ const struct uwb_mac_addr *macaddr);
+
+/* -- UWB Sysfs representation */
+extern struct class uwb_rc_class;
+extern struct device_attribute dev_attr_mac_address;
+extern struct device_attribute dev_attr_dev_address;
+extern struct device_attribute dev_attr_beacon;
+extern struct device_attribute dev_attr_scan;
+extern struct device_attribute dev_attr_event_stats;
+
+/* -- DRP Bandwidth allocator: bandwidth allocations, reservations, DRP */
+void uwb_rsv_init(struct uwb_rc *rc);
+int uwb_rsv_setup(struct uwb_rc *rc);
+void uwb_rsv_cleanup(struct uwb_rc *rc);
+
+void uwb_rsv_set_state(struct uwb_rsv *rsv, enum uwb_rsv_state new_state);
+void uwb_rsv_remove(struct uwb_rsv *rsv);
+struct uwb_rsv *uwb_rsv_find(struct uwb_rc *rc, struct uwb_dev *src,
+ struct uwb_ie_drp *drp_ie);
+void uwb_rsv_sched_update(struct uwb_rc *rc);
+
+void uwb_drp_handle_timeout(struct uwb_rsv *rsv);
+int uwb_drp_ie_update(struct uwb_rsv *rsv);
+void uwb_drp_ie_to_bm(struct uwb_mas_bm *bm, const struct uwb_ie_drp *drp_ie);
+
+void uwb_drp_avail_init(struct uwb_rc *rc);
+int uwb_drp_avail_reserve_pending(struct uwb_rc *rc, struct uwb_mas_bm *mas);
+void uwb_drp_avail_reserve(struct uwb_rc *rc, struct uwb_mas_bm *mas);
+void uwb_drp_avail_release(struct uwb_rc *rc, struct uwb_mas_bm *mas);
+void uwb_drp_avail_ie_update(struct uwb_rc *rc);
+
+/* -- PAL support */
+void uwb_rc_pal_init(struct uwb_rc *rc);
+
+/* -- Misc */
+
+extern ssize_t uwb_mac_frame_hdr_print(char *, size_t,
+ const struct uwb_mac_frame_hdr *);
+
+/* -- Debug interface */
+void uwb_dbg_init(void);
+void uwb_dbg_exit(void);
+void uwb_dbg_add_rc(struct uwb_rc *rc);
+void uwb_dbg_del_rc(struct uwb_rc *rc);
+
+/* Workarounds for version specific stuff */
+
+static inline void uwb_dev_lock(struct uwb_dev *uwb_dev)
+{
+ down(&uwb_dev->dev.sem);
+}
+
+static inline void uwb_dev_unlock(struct uwb_dev *uwb_dev)
+{
+ up(&uwb_dev->dev.sem);
+}
+
+#endif /* #ifndef __UWB_INTERNAL_H__ */
diff --git a/drivers/uwb/uwbd.c b/drivers/uwb/uwbd.c
new file mode 100644
index 000000000000..89a8975b34d6
--- /dev/null
+++ b/drivers/uwb/uwbd.c
@@ -0,0 +1,425 @@
+/*
+ * Ultra Wide Band
+ * Neighborhood Management Daemon
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * This daemon takes care of maintaing information that describes the
+ * UWB neighborhood that the radios in this machine can see. It also
+ * keeps a tab of which devices are visible, makes sure each HC sits
+ * on a different channel to avoid interfering, etc.
+ *
+ * Different drivers (radio controller, device, any API in general)
+ * communicate with this daemon through an event queue. Daemon wakes
+ * up, takes a list of events and handles them one by one; handling
+ * function is extracted from a table based on the event's type and
+ * subtype. Events are freed only if the handling function says so.
+ *
+ * . Lock protecting the event list has to be an spinlock and locked
+ * with IRQSAVE because it might be called from an interrupt
+ * context (ie: when events arrive and the notification drops
+ * down from the ISR).
+ *
+ * . UWB radio controller drivers queue events to the daemon using
+ * uwbd_event_queue(). They just get the event, chew it to make it
+ * look like UWBD likes it and pass it in a buffer allocated with
+ * uwb_event_alloc().
+ *
+ * EVENTS
+ *
+ * Events have a type, a subtype, a lenght, some other stuff and the
+ * data blob, which depends on the event. The header is 'struct
+ * uwb_event'; for payloads, see 'struct uwbd_evt_*'.
+ *
+ * EVENT HANDLER TABLES
+ *
+ * To find a handling function for an event, the type is used to index
+ * a subtype-table in the type-table. The subtype-table is indexed
+ * with the subtype to get the function that handles the event. Start
+ * with the main type-table 'uwbd_evt_type_handler'.
+ *
+ * DEVICES
+ *
+ * Devices are created when a bunch of beacons have been received and
+ * it is stablished that the device has stable radio presence. CREATED
+ * only, not configured. Devices are ONLY configured when an
+ * Application-Specific IE Probe is receieved, in which the device
+ * declares which Protocol ID it groks. Then the device is CONFIGURED
+ * (and the driver->probe() stuff of the device model is invoked).
+ *
+ * Devices are considered disconnected when a certain number of
+ * beacons are not received in an amount of time.
+ *
+ * Handler functions are called normally uwbd_evt_handle_*().
+ */
+
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/freezer.h>
+#include "uwb-internal.h"
+
+#define D_LOCAL 1
+#include <linux/uwb/debug.h>
+
+
+/**
+ * UWBD Event handler function signature
+ *
+ * Return !0 if the event needs not to be freed (ie the handler
+ * takes/took care of it). 0 means the daemon code will free the
+ * event.
+ *
+ * @evt->rc is already referenced and guaranteed to exist. See
+ * uwb_evt_handle().
+ */
+typedef int (*uwbd_evt_handler_f)(struct uwb_event *);
+
+/**
+ * Properties of a UWBD event
+ *
+ * @handler: the function that will handle this event
+ * @count: number of instances since startup that this event has been
+ * seen
+ * @name: text name of event
+ */
+struct uwbd_event {
+ uwbd_evt_handler_f handler;
+ unsigned long count;
+ const char *name;
+};
+
+/** Table of handlers for and properties of the UWBD Radio Control Events */
+static
+struct uwbd_event uwbd_events[] = {
+ [UWB_RC_EVT_BEACON] = {
+ .handler = uwbd_evt_handle_rc_beacon,
+ .count = 0,
+ .name = "BEACON_RECEIVED"
+ },
+ [UWB_RC_EVT_BEACON_SIZE] = {
+ .handler = uwbd_evt_handle_rc_beacon_size,
+ .count = 0,
+ .name = "BEACON_SIZE_CHANGE"
+ },
+ [UWB_RC_EVT_BPOIE_CHANGE] = {
+ .handler = uwbd_evt_handle_rc_bpoie_change,
+ .count = 0,
+ .name = "BPOIE_CHANGE"
+ },
+ [UWB_RC_EVT_BP_SLOT_CHANGE] = {
+ .handler = uwbd_evt_handle_rc_bp_slot_change,
+ .count = 0,
+ .name = "BP_SLOT_CHANGE"
+ },
+ [UWB_RC_EVT_DRP_AVAIL] = {
+ .handler = uwbd_evt_handle_rc_drp_avail,
+ .count = 0,
+ .name = "DRP_AVAILABILITY_CHANGE"
+ },
+ [UWB_RC_EVT_DRP] = {
+ .handler = uwbd_evt_handle_rc_drp,
+ .count = 0,
+ .name = "DRP"
+ },
+ [UWB_RC_EVT_DEV_ADDR_CONFLICT] = {
+ .handler = uwbd_evt_handle_rc_dev_addr_conflict,
+ .count = 0,
+ .name = "DEV_ADDR_CONFLICT",
+ },
+};
+
+
+
+struct uwbd_evt_type_handler {
+ const char *name;
+ struct uwbd_event *uwbd_events;
+ size_t size;
+};
+
+#define UWBD_EVT_TYPE_HANDLER(n,a) { \
+ .name = (n), \
+ .uwbd_events = (a), \
+ .size = sizeof(a)/sizeof((a)[0]) \
+}
+
+
+/** Table of handlers for each UWBD Event type. */
+static
+struct uwbd_evt_type_handler uwbd_evt_type_handlers[] = {
+ [UWB_RC_CET_GENERAL] = UWBD_EVT_TYPE_HANDLER("RC", uwbd_events)
+};
+
+static const
+size_t uwbd_evt_type_handlers_len =
+ sizeof(uwbd_evt_type_handlers) / sizeof(uwbd_evt_type_handlers[0]);
+
+
+/**
+ * Handle an event passed to the UWB Daemon
+ *
+ * @evt: the event to handle
+ * @returns: 0 if the event can be kfreed, !0 on the contrary
+ * (somebody else took ownership) [coincidentally, returning
+ * a <0 errno code will free it :)].
+ *
+ * Looks up the two indirection tables (one for the type, one for the
+ * subtype) to decide which function handles it and then calls the
+ * handler.
+ *
+ * The event structure passed to the event handler has the radio
+ * controller in @evt->rc referenced. The reference will be dropped
+ * once the handler returns, so if it needs it for longer (async),
+ * it'll need to take another one.
+ */
+static
+int uwbd_event_handle(struct uwb_event *evt)
+{
+ int result;
+ struct uwbd_evt_type_handler *type_table;
+ uwbd_evt_handler_f handler;
+ struct uwb_rc *rc;
+ u8 type, context;
+ u16 event;
+
+ rc = evt->rc;
+ if (rc == NULL) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "UWBD: BUG: received event "
+ "with NULL rc\n");
+ return -EINVAL;
+ }
+ result = 0;
+ if (!rc->ready) /* Not ready to process stuff, drop it */
+ goto out_put;
+ type = evt->rceb->bEventType;
+ event = le16_to_cpu(evt->rceb->wEvent);
+ context = evt->rceb->bEventContext;
+ result = -EINVAL;
+ if (type > uwbd_evt_type_handlers_len) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "UWBD: event type %u: unknown "
+ "(too high)\n", type);
+ goto out_put;
+ }
+ type_table = &uwbd_evt_type_handlers[type];
+ if (type_table->uwbd_events == NULL) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "UWBD: event type %u: unknown\n", type);
+ goto out_put;
+ }
+ if (event > type_table->size) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "UWBD: event %s[%u]: "
+ "unknown (too high)\n", type_table->name, event);
+ goto out_put;
+ }
+ handler = type_table->uwbd_events[event].handler;
+ if (handler == NULL) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "UWBD: event %s[%u]: unknown\n",
+ type_table->name, event);
+ goto out_put;
+ }
+ d_printf(3, NULL, "processing 0x%02x/%04x/%02x, %zu bytes\n",
+ type, event, context, evt->size);
+ type_table->uwbd_events[event].count++;
+ result = (*handler)(evt);
+ if (result < 0) {
+ if (printk_ratelimit())
+ printk(KERN_ERR "UWBD: event 0x%02x/%04x/%02x, "
+ "table %s[%u]: handling failed: %d\n",
+ type, event, context, type_table->name,
+ event, result);
+ result = 0; /* make sure uwbd() frees the evt buffer */
+ }
+out_put:
+ __uwb_rc_put(rc); /* for the __uwb_rc_get() in uwb_rc_notif_cb() */
+ return result;
+}
+
+
+/* The UWB Daemon */
+
+
+/** Daemon's PID: used to decide if we can queue or not */
+static int uwbd_pid;
+/** Daemon's task struct for managing the kthread */
+static struct task_struct *uwbd_task;
+/** Daemon's waitqueue for waiting for new events */
+static DECLARE_WAIT_QUEUE_HEAD(uwbd_wq);
+/** Daemon's list of events; we queue/dequeue here */
+static struct list_head uwbd_event_list = LIST_HEAD_INIT(uwbd_event_list);
+/** Daemon's list lock to protect concurent access */
+static DEFINE_SPINLOCK(uwbd_event_list_lock);
+
+
+/**
+ * UWB Daemon
+ *
+ * Listens to all UWB notifications and takes care to track the state
+ * of the UWB neighboorhood for the kernel. When we do a run, we
+ * spinlock, move the list to a private copy and release the
+ * lock. Hold it as little as possible. Not a conflict: it is
+ * guaranteed we own the events in the private list.
+ *
+ * FIXME: should change so we don't have a 1HZ timer all the time, but
+ * only if there are devices.
+ */
+static int uwbd(void *unused)
+{
+ unsigned long flags;
+ struct list_head list = LIST_HEAD_INIT(list);
+ struct uwb_event *evt, *nxt;
+ int should_stop = 0, should_keep;
+ while (1) {
+ wait_event_interruptible_timeout(
+ uwbd_wq,
+ !list_empty(&uwbd_event_list)
+ || (should_stop = kthread_should_stop()),
+ HZ);
+ if (should_stop)
+ break;
+ try_to_freeze();
+ spin_lock_irqsave(&uwbd_event_list_lock, flags);
+ list_splice_init(&uwbd_event_list, &list);
+ spin_unlock_irqrestore(&uwbd_event_list_lock, flags);
+ list_for_each_entry_safe(evt, nxt, &list, list_node) {
+ list_del(&evt->list_node);
+ should_keep = uwbd_event_handle(evt);
+ if (!should_keep)
+ kfree(evt->rceb);
+ kfree(evt);
+ }
+ uwb_beca_purge(); /* Purge devices that left */
+ }
+ return 0;
+}
+
+
+/** Start the UWB daemon */
+void uwbd_start(void)
+{
+ uwbd_task = kthread_run(uwbd, NULL, "uwbd");
+ if (uwbd_task == NULL)
+ printk(KERN_ERR "UWB: Cannot start management daemon; "
+ "UWB won't work\n");
+ else
+ uwbd_pid = uwbd_task->pid;
+}
+
+/* Stop the UWB daemon and free any unprocessed events */
+void uwbd_stop(void)
+{
+ unsigned long flags;
+ struct uwb_event *evt, *nxt;
+ kthread_stop(uwbd_task);
+ spin_lock_irqsave(&uwbd_event_list_lock, flags);
+ uwbd_pid = 0;
+ list_for_each_entry_safe(evt, nxt, &uwbd_event_list, list_node) {
+ kfree(evt->rceb);
+ kfree(evt);
+ }
+ spin_unlock_irqrestore(&uwbd_event_list_lock, flags);
+ uwb_beca_release();
+}
+
+/*
+ * Queue an event for the management daemon
+ *
+ * When some lower layer receives an event, it uses this function to
+ * push it forward to the UWB daemon.
+ *
+ * Once you pass the event, you don't own it any more, but the daemon
+ * does. It will uwb_event_free() it when done, so make sure you
+ * uwb_event_alloc()ed it or bad things will happen.
+ *
+ * If the daemon is not running, we just free the event.
+ */
+void uwbd_event_queue(struct uwb_event *evt)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&uwbd_event_list_lock, flags);
+ if (uwbd_pid != 0) {
+ list_add(&evt->list_node, &uwbd_event_list);
+ wake_up_all(&uwbd_wq);
+ } else {
+ kfree(evt->rceb);
+ kfree(evt);
+ }
+ spin_unlock_irqrestore(&uwbd_event_list_lock, flags);
+ return;
+}
+
+/**
+ * Print the statistics for all events in a table
+ *
+ * @returns: the number of characters printed to buffer, 0 if buffer is
+ * full
+ */
+static size_t uwbd_print_evt_stats(struct uwbd_evt_type_handler *type_table,
+ char *buf, size_t buf_size)
+{
+ struct uwbd_event *events;
+ size_t evtmax;
+ unsigned cnt;
+ ssize_t result;
+ ssize_t org_buf_size = buf_size;
+
+ events = type_table->uwbd_events;
+ evtmax = type_table->size;
+ for (cnt = 0; cnt < evtmax; cnt++) {
+ if (events[cnt].handler != NULL) {
+ result = scnprintf(buf, buf_size,
+ "%-5s \t %-25s\t %lu \n",
+ type_table->name,
+ events[cnt].name,
+ events[cnt].count);
+ buf_size = result >= buf_size ? 0 :
+ buf_size - result;
+ if (buf_size > 0)
+ buf += result;
+ }
+ }
+ return org_buf_size - buf_size;
+}
+
+/**
+ * Print the statistics of all notifications from the device.
+ *
+ * The tables of all event types are traversed to get statistics of every
+ * notification handled by the driver.
+ */
+static ssize_t uwbd_evt_stats_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned cnt;
+ struct uwbd_evt_type_handler *type_table;
+ size_t consumed;
+
+ consumed = sprintf(buf, "#EVENT_TYPE EVENT NUM_RECEIVED\n\n");
+ for (cnt = 0; cnt < uwbd_evt_type_handlers_len; cnt++) {
+ type_table = &uwbd_evt_type_handlers[cnt];
+ if (type_table->uwbd_events != NULL && consumed < PAGE_SIZE)
+ consumed += uwbd_print_evt_stats(type_table,
+ buf + consumed,
+ PAGE_SIZE - consumed);
+ }
+ return consumed;
+}
+DEVICE_ATTR(event_stats, S_IRUGO, uwbd_evt_stats_show, NULL);
diff --git a/drivers/uwb/whc-rc.c b/drivers/uwb/whc-rc.c
new file mode 100644
index 000000000000..f889e4ac44ec
--- /dev/null
+++ b/drivers/uwb/whc-rc.c
@@ -0,0 +1,511 @@
+/*
+ * Wireless Host Controller: Radio Control Interface (WHCI v0.95[2.3])
+ * Radio Control command/event transport to the UWB stack
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Initialize and hook up the Radio Control interface.
+ *
+ * For each device probed, creates an 'struct whcrc' which contains
+ * just the representation of the UWB Radio Controller, and the logic
+ * for reading notifications and passing them to the UWB Core.
+ *
+ * So we initialize all of those, register the UWB Radio Controller
+ * and setup the notification/event handle to pipe the notifications
+ * to the UWB management Daemon.
+ *
+ * Once uwb_rc_add() is called, the UWB stack takes control, resets
+ * the radio and readies the device to take commands the UWB
+ * API/user-space.
+ *
+ * Note this driver is just a transport driver; the commands are
+ * formed at the UWB stack and given to this driver who will deliver
+ * them to the hw and transfer the replies/notifications back to the
+ * UWB stack through the UWB daemon (UWBD).
+ */
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/uwb.h>
+#include <linux/uwb/whci.h>
+#include <linux/uwb/umc.h>
+#include "uwb-internal.h"
+
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+/**
+ * Descriptor for an instance of the UWB Radio Control Driver that
+ * attaches to the URC interface of the WHCI PCI card.
+ *
+ * Unless there is a lock specific to the 'data members', all access
+ * is protected by uwb_rc->mutex.
+ */
+struct whcrc {
+ struct umc_dev *umc_dev;
+ struct uwb_rc *uwb_rc; /* UWB host controller */
+
+ unsigned long area;
+ void __iomem *rc_base;
+ size_t rc_len;
+ spinlock_t irq_lock;
+
+ void *evt_buf, *cmd_buf;
+ dma_addr_t evt_dma_buf, cmd_dma_buf;
+ wait_queue_head_t cmd_wq;
+ struct work_struct event_work;
+};
+
+
+/**
+ * Execute an UWB RC command on WHCI/RC
+ *
+ * @rc: Instance of a Radio Controller that is a whcrc
+ * @cmd: Buffer containing the RCCB and payload to execute
+ * @cmd_size: Size of the command buffer.
+ *
+ * We copy the command into whcrc->cmd_buf (as it is pretty and
+ * aligned`and physically contiguous) and then press the right keys in
+ * the controller's URCCMD register to get it to read it. We might
+ * have to wait for the cmd_sem to be open to us.
+ *
+ * NOTE: rc's mutex has to be locked
+ */
+static int whcrc_cmd(struct uwb_rc *uwb_rc,
+ const struct uwb_rccb *cmd, size_t cmd_size)
+{
+ int result = 0;
+ struct whcrc *whcrc = uwb_rc->priv;
+ struct device *dev = &whcrc->umc_dev->dev;
+ u32 urccmd;
+
+ d_fnstart(3, dev, "(%p, %p, %zu)\n", uwb_rc, cmd, cmd_size);
+ might_sleep();
+
+ if (cmd_size >= 4096) {
+ result = -E2BIG;
+ goto error;
+ }
+
+ result = wait_event_timeout(whcrc->cmd_wq,
+ !(le_readl(whcrc->rc_base + URCCMD) & URCCMD_ACTIVE), HZ/2);
+ if (result == 0) {
+ dev_err(dev, "device is not ready to execute commands\n");
+ result = -ETIMEDOUT;
+ goto error;
+ }
+
+ memmove(whcrc->cmd_buf, cmd, cmd_size);
+ le_writeq(whcrc->cmd_dma_buf, whcrc->rc_base + URCCMDADDR);
+
+ spin_lock(&whcrc->irq_lock);
+ urccmd = le_readl(whcrc->rc_base + URCCMD);
+ urccmd &= ~(URCCMD_EARV | URCCMD_SIZE_MASK);
+ le_writel(urccmd | URCCMD_ACTIVE | URCCMD_IWR | cmd_size,
+ whcrc->rc_base + URCCMD);
+ spin_unlock(&whcrc->irq_lock);
+
+error:
+ d_fnend(3, dev, "(%p, %p, %zu) = %d\n",
+ uwb_rc, cmd, cmd_size, result);
+ return result;
+
+
+
+}
+
+/**
+ * Reset event reception mechanism and tell hw we are ready to get more
+ *
+ * We have read all the events in the event buffer, so we are ready to
+ * reset it to the beginning.
+ *
+ * This is only called during initialization or after an event buffer
+ * has been retired. This means we can be sure that event processing
+ * is disabled and it's safe to update the URCEVTADDR register.
+ *
+ * There's no need to wait for the event processing to start as the
+ * URC will not clear URCCMD_ACTIVE until (internal) event buffer
+ * space is available.
+ */
+static
+void whcrc_enable_events(struct whcrc *whcrc)
+{
+ struct device *dev = &whcrc->umc_dev->dev;
+ u32 urccmd;
+
+ d_fnstart(4, dev, "(whcrc %p)\n", whcrc);
+
+ le_writeq(whcrc->evt_dma_buf, whcrc->rc_base + URCEVTADDR);
+
+ spin_lock(&whcrc->irq_lock);
+ urccmd = le_readl(whcrc->rc_base + URCCMD) & ~URCCMD_ACTIVE;
+ le_writel(urccmd | URCCMD_EARV, whcrc->rc_base + URCCMD);
+ spin_unlock(&whcrc->irq_lock);
+
+ d_fnend(4, dev, "(whcrc %p) = void\n", whcrc);
+}
+
+static void whcrc_event_work(struct work_struct *work)
+{
+ struct whcrc *whcrc = container_of(work, struct whcrc, event_work);
+ struct device *dev = &whcrc->umc_dev->dev;
+ size_t size;
+ u64 urcevtaddr;
+
+ urcevtaddr = le_readq(whcrc->rc_base + URCEVTADDR);
+ size = urcevtaddr & URCEVTADDR_OFFSET_MASK;
+
+ d_printf(3, dev, "received %zu octet event\n", size);
+ d_dump(4, dev, whcrc->evt_buf, size > 32? 32 : size);
+
+ uwb_rc_neh_grok(whcrc->uwb_rc, whcrc->evt_buf, size);
+ whcrc_enable_events(whcrc);
+}
+
+/**
+ * Catch interrupts?
+ *
+ * We ack inmediately (and expect the hw to do the right thing and
+ * raise another IRQ if things have changed :)
+ */
+static
+irqreturn_t whcrc_irq_cb(int irq, void *_whcrc)
+{
+ struct whcrc *whcrc = _whcrc;
+ struct device *dev = &whcrc->umc_dev->dev;
+ u32 urcsts;
+
+ d_fnstart(4, dev, "irq %d _whcrc %p)\n", irq, _whcrc);
+ urcsts = le_readl(whcrc->rc_base + URCSTS);
+ if (!(urcsts & URCSTS_INT_MASK))
+ return IRQ_NONE;
+ le_writel(urcsts & URCSTS_INT_MASK, whcrc->rc_base + URCSTS);
+
+ d_printf(4, dev, "acked 0x%08x, urcsts 0x%08x\n",
+ le_readl(whcrc->rc_base + URCSTS), urcsts);
+
+ if (whcrc->uwb_rc == NULL) {
+ if (printk_ratelimit())
+ dev_dbg(dev, "Received interrupt when not yet "
+ "ready!\n");
+ goto out;
+ }
+
+ if (urcsts & URCSTS_HSE) {
+ dev_err(dev, "host system error -- hardware halted\n");
+ /* FIXME: do something sensible here */
+ goto out;
+ }
+ if (urcsts & URCSTS_ER) {
+ d_printf(3, dev, "ER: event ready\n");
+ schedule_work(&whcrc->event_work);
+ }
+ if (urcsts & URCSTS_RCI) {
+ d_printf(3, dev, "RCI: ready to execute another command\n");
+ wake_up_all(&whcrc->cmd_wq);
+ }
+out:
+ return IRQ_HANDLED;
+}
+
+
+/**
+ * Initialize a UMC RC interface: map regions, get (shared) IRQ
+ */
+static
+int whcrc_setup_rc_umc(struct whcrc *whcrc)
+{
+ int result = 0;
+ struct device *dev = &whcrc->umc_dev->dev;
+ struct umc_dev *umc_dev = whcrc->umc_dev;
+
+ whcrc->area = umc_dev->resource.start;
+ whcrc->rc_len = umc_dev->resource.end - umc_dev->resource.start + 1;
+ result = -EBUSY;
+ if (request_mem_region(whcrc->area, whcrc->rc_len, KBUILD_MODNAME)
+ == NULL) {
+ dev_err(dev, "can't request URC region (%zu bytes @ 0x%lx): %d\n",
+ whcrc->rc_len, whcrc->area, result);
+ goto error_request_region;
+ }
+
+ whcrc->rc_base = ioremap_nocache(whcrc->area, whcrc->rc_len);
+ if (whcrc->rc_base == NULL) {
+ dev_err(dev, "can't ioremap registers (%zu bytes @ 0x%lx): %d\n",
+ whcrc->rc_len, whcrc->area, result);
+ goto error_ioremap_nocache;
+ }
+
+ result = request_irq(umc_dev->irq, whcrc_irq_cb, IRQF_SHARED,
+ KBUILD_MODNAME, whcrc);
+ if (result < 0) {
+ dev_err(dev, "can't allocate IRQ %d: %d\n",
+ umc_dev->irq, result);
+ goto error_request_irq;
+ }
+
+ result = -ENOMEM;
+ whcrc->cmd_buf = dma_alloc_coherent(&umc_dev->dev, PAGE_SIZE,
+ &whcrc->cmd_dma_buf, GFP_KERNEL);
+ if (whcrc->cmd_buf == NULL) {
+ dev_err(dev, "Can't allocate cmd transfer buffer\n");
+ goto error_cmd_buffer;
+ }
+
+ whcrc->evt_buf = dma_alloc_coherent(&umc_dev->dev, PAGE_SIZE,
+ &whcrc->evt_dma_buf, GFP_KERNEL);
+ if (whcrc->evt_buf == NULL) {
+ dev_err(dev, "Can't allocate evt transfer buffer\n");
+ goto error_evt_buffer;
+ }
+ d_printf(3, dev, "UWB RC Interface: %zu bytes at 0x%p, irq %u\n",
+ whcrc->rc_len, whcrc->rc_base, umc_dev->irq);
+ return 0;
+
+error_evt_buffer:
+ dma_free_coherent(&umc_dev->dev, PAGE_SIZE, whcrc->cmd_buf,
+ whcrc->cmd_dma_buf);
+error_cmd_buffer:
+ free_irq(umc_dev->irq, whcrc);
+error_request_irq:
+ iounmap(whcrc->rc_base);
+error_ioremap_nocache:
+ release_mem_region(whcrc->area, whcrc->rc_len);
+error_request_region:
+ return result;
+}
+
+
+/**
+ * Release RC's UMC resources
+ */
+static
+void whcrc_release_rc_umc(struct whcrc *whcrc)
+{
+ struct umc_dev *umc_dev = whcrc->umc_dev;
+
+ dma_free_coherent(&umc_dev->dev, PAGE_SIZE, whcrc->evt_buf,
+ whcrc->evt_dma_buf);
+ dma_free_coherent(&umc_dev->dev, PAGE_SIZE, whcrc->cmd_buf,
+ whcrc->cmd_dma_buf);
+ free_irq(umc_dev->irq, whcrc);
+ iounmap(whcrc->rc_base);
+ release_mem_region(whcrc->area, whcrc->rc_len);
+}
+
+
+/**
+ * whcrc_start_rc - start a WHCI radio controller
+ * @whcrc: the radio controller to start
+ *
+ * Reset the UMC device, start the radio controller, enable events and
+ * finally enable interrupts.
+ */
+static int whcrc_start_rc(struct whcrc *whcrc)
+{
+ int result = 0;
+ struct device *dev = &whcrc->umc_dev->dev;
+ unsigned long start, duration;
+
+ /* Reset the thing */
+ le_writel(URCCMD_RESET, whcrc->rc_base + URCCMD);
+ if (d_test(3))
+ start = jiffies;
+ if (whci_wait_for(dev, whcrc->rc_base + URCCMD, URCCMD_RESET, 0,
+ 5000, "device to reset at init") < 0) {
+ result = -EBUSY;
+ goto error;
+ } else if (d_test(3)) {
+ duration = jiffies - start;
+ if (duration > msecs_to_jiffies(40))
+ dev_err(dev, "Device took %ums to "
+ "reset. MAX expected: 40ms\n",
+ jiffies_to_msecs(duration));
+ }
+
+ /* Set the event buffer, start the controller (enable IRQs later) */
+ le_writel(0, whcrc->rc_base + URCINTR);
+ le_writel(URCCMD_RS, whcrc->rc_base + URCCMD);
+ result = -ETIMEDOUT;
+ if (d_test(3))
+ start = jiffies;
+ if (whci_wait_for(dev, whcrc->rc_base + URCSTS, URCSTS_HALTED, 0,
+ 5000, "device to start") < 0)
+ goto error;
+ if (d_test(3)) {
+ duration = jiffies - start;
+ if (duration > msecs_to_jiffies(40))
+ dev_err(dev, "Device took %ums to start. "
+ "MAX expected: 40ms\n",
+ jiffies_to_msecs(duration));
+ }
+ whcrc_enable_events(whcrc);
+ result = 0;
+ le_writel(URCINTR_EN_ALL, whcrc->rc_base + URCINTR);
+error:
+ return result;
+}
+
+
+/**
+ * whcrc_stop_rc - stop a WHCI radio controller
+ * @whcrc: the radio controller to stop
+ *
+ * Disable interrupts and cancel any pending event processing work
+ * before clearing the Run/Stop bit.
+ */
+static
+void whcrc_stop_rc(struct whcrc *whcrc)
+{
+ struct umc_dev *umc_dev = whcrc->umc_dev;
+
+ le_writel(0, whcrc->rc_base + URCINTR);
+ cancel_work_sync(&whcrc->event_work);
+
+ le_writel(0, whcrc->rc_base + URCCMD);
+ whci_wait_for(&umc_dev->dev, whcrc->rc_base + URCSTS,
+ URCSTS_HALTED, 0, 40, "URCSTS.HALTED");
+}
+
+static void whcrc_init(struct whcrc *whcrc)
+{
+ spin_lock_init(&whcrc->irq_lock);
+ init_waitqueue_head(&whcrc->cmd_wq);
+ INIT_WORK(&whcrc->event_work, whcrc_event_work);
+}
+
+/**
+ * Initialize the radio controller.
+ *
+ * NOTE: we setup whcrc->uwb_rc before calling uwb_rc_add(); in the
+ * IRQ handler we use that to determine if the hw is ready to
+ * handle events. Looks like a race condition, but it really is
+ * not.
+ */
+static
+int whcrc_probe(struct umc_dev *umc_dev)
+{
+ int result;
+ struct uwb_rc *uwb_rc;
+ struct whcrc *whcrc;
+ struct device *dev = &umc_dev->dev;
+
+ d_fnstart(3, dev, "(umc_dev %p)\n", umc_dev);
+ result = -ENOMEM;
+ uwb_rc = uwb_rc_alloc();
+ if (uwb_rc == NULL) {
+ dev_err(dev, "unable to allocate RC instance\n");
+ goto error_rc_alloc;
+ }
+ whcrc = kzalloc(sizeof(*whcrc), GFP_KERNEL);
+ if (whcrc == NULL) {
+ dev_err(dev, "unable to allocate WHC-RC instance\n");
+ goto error_alloc;
+ }
+ whcrc_init(whcrc);
+ whcrc->umc_dev = umc_dev;
+
+ result = whcrc_setup_rc_umc(whcrc);
+ if (result < 0) {
+ dev_err(dev, "Can't setup RC UMC interface: %d\n", result);
+ goto error_setup_rc_umc;
+ }
+ result = whcrc_start_rc(whcrc);
+ if (result < 0) {
+ dev_err(dev, "Can't setup RC interface: %d\n", result);
+ goto error_start_rc;
+ }
+ whcrc->uwb_rc = uwb_rc;
+ result = uwb_rc_add(uwb_rc, dev, whcrc, whcrc_cmd, NULL, NULL);
+ if (result < 0)
+ goto error_rc_add;
+ umc_set_drvdata(umc_dev, whcrc);
+ d_fnend(3, dev, "(umc_dev %p) = 0\n", umc_dev);
+ return 0;
+
+error_rc_add:
+ whcrc_stop_rc(whcrc);
+error_start_rc:
+ whcrc_release_rc_umc(whcrc);
+error_setup_rc_umc:
+ kfree(whcrc);
+error_alloc:
+ uwb_rc_put(uwb_rc);
+error_rc_alloc:
+ d_fnend(3, dev, "(umc_dev %p) = %d\n", umc_dev, result);
+ return result;
+}
+
+/**
+ * Clean up the radio control resources
+ *
+ * When we up the command semaphore, everybody possibly held trying to
+ * execute a command should be granted entry and then they'll see the
+ * host is quiescing and up it (so it will chain to the next waiter).
+ * This should not happen (in any case), as we can only remove when
+ * there are no handles open...
+ */
+static void whcrc_remove(struct umc_dev *umc_dev)
+{
+ struct whcrc *whcrc = umc_get_drvdata(umc_dev);
+ struct uwb_rc *uwb_rc = whcrc->uwb_rc;
+
+ umc_set_drvdata(umc_dev, NULL);
+ uwb_rc_rm(uwb_rc);
+ whcrc_stop_rc(whcrc);
+ whcrc_release_rc_umc(whcrc);
+ kfree(whcrc);
+ uwb_rc_put(uwb_rc);
+ d_printf(1, &umc_dev->dev, "freed whcrc %p\n", whcrc);
+}
+
+/* PCI device ID's that we handle [so it gets loaded] */
+static struct pci_device_id whcrc_id_table[] = {
+ { PCI_DEVICE_CLASS(PCI_CLASS_WIRELESS_WHCI, ~0) },
+ { /* empty last entry */ }
+};
+MODULE_DEVICE_TABLE(pci, whcrc_id_table);
+
+static struct umc_driver whcrc_driver = {
+ .name = "whc-rc",
+ .cap_id = UMC_CAP_ID_WHCI_RC,
+ .probe = whcrc_probe,
+ .remove = whcrc_remove,
+};
+
+static int __init whcrc_driver_init(void)
+{
+ return umc_driver_register(&whcrc_driver);
+}
+module_init(whcrc_driver_init);
+
+static void __exit whcrc_driver_exit(void)
+{
+ umc_driver_unregister(&whcrc_driver);
+}
+module_exit(whcrc_driver_exit);
+
+MODULE_AUTHOR("Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>");
+MODULE_DESCRIPTION("Wireless Host Controller Radio Control Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/uwb/whci.c b/drivers/uwb/whci.c
new file mode 100644
index 000000000000..d22c2cac8e4b
--- /dev/null
+++ b/drivers/uwb/whci.c
@@ -0,0 +1,274 @@
+/*
+ * WHCI UWB Multi-interface Controller enumerator.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ *
+ * This file is released under the GNU GPL v2.
+ */
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/uwb/whci.h>
+#include <linux/uwb/umc.h>
+#define D_LOCAL 0
+#include <linux/uwb/debug.h>
+
+
+struct whci_card {
+ struct pci_dev *pci;
+ void __iomem *uwbbase;
+ u8 n_caps;
+ struct umc_dev *devs[0];
+};
+
+
+/* Fix faulty HW :( */
+static
+u64 whci_capdata_quirks(struct whci_card *card, u64 capdata)
+{
+ u64 capdata_orig = capdata;
+ struct pci_dev *pci_dev = card->pci;
+ if (pci_dev->vendor == PCI_VENDOR_ID_INTEL
+ && (pci_dev->device == 0x0c3b || pci_dev->device == 0004)
+ && pci_dev->class == 0x0d1010) {
+ switch (UWBCAPDATA_TO_CAP_ID(capdata)) {
+ /* NIC capability has 0x100 bytes of aperture */
+ case 0x80: capdata |= 0x40 << 8; break;
+ /* WUSB capability has 0x80 bytes of aperture
+ * and ID is 1 */
+ case 0x02:
+ capdata &= ~0xffff;
+ capdata |= 0x2001;
+ break;
+ }
+ }
+ if (capdata_orig != capdata)
+ dev_warn(&pci_dev->dev,
+ "PCI v%04x d%04x c%06x#%02x: "
+ "corrected capdata from %016Lx to %016Lx\n",
+ pci_dev->vendor, pci_dev->device, pci_dev->class,
+ (unsigned)UWBCAPDATA_TO_CAP_ID(capdata),
+ (unsigned long long)capdata_orig,
+ (unsigned long long)capdata);
+ return capdata;
+}
+
+
+/**
+ * whci_wait_for - wait for a WHCI register to be set
+ *
+ * Polls (for at most @max_ms ms) until '*@reg & @mask == @result'.
+ */
+int whci_wait_for(struct device *dev, u32 __iomem *reg, u32 mask, u32 result,
+ unsigned long max_ms, const char *tag)
+{
+ unsigned t = 0;
+ u32 val;
+ for (;;) {
+ val = le_readl(reg);
+ if ((val & mask) == result)
+ break;
+ msleep(10);
+ if (t >= max_ms) {
+ dev_err(dev, "timed out waiting for %s ", tag);
+ return -ETIMEDOUT;
+ }
+ t += 10;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(whci_wait_for);
+
+
+/*
+ * NOTE: the capinfo and capdata registers are slightly different
+ * (size and cap-id fields). So for cap #0, we need to fill
+ * in. Size comes from the size of the register block
+ * (statically calculated); cap_id comes from nowhere, we use
+ * zero, that is reserved, for the radio controller, because
+ * none was defined at the spec level.
+ */
+static int whci_add_cap(struct whci_card *card, int n)
+{
+ struct umc_dev *umc;
+ u64 capdata;
+ int bar, err;
+
+ umc = umc_device_create(&card->pci->dev, n);
+ if (umc == NULL)
+ return -ENOMEM;
+
+ capdata = le_readq(card->uwbbase + UWBCAPDATA(n));
+
+ bar = UWBCAPDATA_TO_BAR(capdata) << 1;
+
+ capdata = whci_capdata_quirks(card, capdata);
+ /* Capability 0 is the radio controller. It's size is 32
+ * bytes (WHCI0.95[2.3, T2-9]). */
+ umc->version = UWBCAPDATA_TO_VERSION(capdata);
+ umc->cap_id = n == 0? 0 : UWBCAPDATA_TO_CAP_ID(capdata);
+ umc->bar = bar;
+ umc->resource.start = pci_resource_start(card->pci, bar)
+ + UWBCAPDATA_TO_OFFSET(capdata);
+ umc->resource.end = umc->resource.start
+ + (n == 0? 0x20 : UWBCAPDATA_TO_SIZE(capdata)) - 1;
+ umc->resource.name = dev_name(&umc->dev);
+ umc->resource.flags = card->pci->resource[bar].flags;
+ umc->resource.parent = &card->pci->resource[bar];
+ umc->irq = card->pci->irq;
+
+ err = umc_device_register(umc);
+ if (err < 0)
+ goto error;
+ card->devs[n] = umc;
+ return 0;
+
+error:
+ kfree(umc);
+ return err;
+}
+
+static void whci_del_cap(struct whci_card *card, int n)
+{
+ struct umc_dev *umc = card->devs[n];
+
+ d_fnstart(3, &card->pci->dev, "(card %p, n %d)\n", card, n);
+ if (umc != NULL)
+ umc_device_unregister(umc);
+ d_fnend(3, &card->pci->dev, "(card %p, n %d)\n", card, n);
+}
+
+static int whci_n_caps(struct pci_dev *pci)
+{
+ void __iomem *uwbbase;
+ u64 capinfo;
+
+ uwbbase = pci_iomap(pci, 0, 8);
+ if (!uwbbase)
+ return -ENOMEM;
+ capinfo = le_readq(uwbbase + UWBCAPINFO);
+ pci_iounmap(pci, uwbbase);
+
+ return UWBCAPINFO_TO_N_CAPS(capinfo);
+}
+
+static int whci_probe(struct pci_dev *pci, const struct pci_device_id *id)
+{
+ struct whci_card *card;
+ int err, n_caps, n;
+
+ err = pci_enable_device(pci);
+ if (err < 0)
+ goto error;
+ pci_enable_msi(pci);
+ pci_set_master(pci);
+ err = -ENXIO;
+ if (!pci_set_dma_mask(pci, DMA_64BIT_MASK))
+ pci_set_consistent_dma_mask(pci, DMA_64BIT_MASK);
+ else if (!pci_set_dma_mask(pci, DMA_32BIT_MASK))
+ pci_set_consistent_dma_mask(pci, DMA_32BIT_MASK);
+ else
+ goto error_dma;
+
+ err = n_caps = whci_n_caps(pci);
+ if (n_caps < 0)
+ goto error_ncaps;
+
+ err = -ENOMEM;
+ card = kzalloc(sizeof(struct whci_card)
+ + sizeof(struct whci_dev *) * (n_caps + 1),
+ GFP_KERNEL);
+ if (card == NULL)
+ goto error_kzalloc;
+ card->pci = pci;
+ card->n_caps = n_caps;
+
+ err = -EBUSY;
+ if (!request_mem_region(pci_resource_start(pci, 0),
+ UWBCAPDATA_SIZE(card->n_caps),
+ "whci (capability data)"))
+ goto error_request_memregion;
+ err = -ENOMEM;
+ card->uwbbase = pci_iomap(pci, 0, UWBCAPDATA_SIZE(card->n_caps));
+ if (!card->uwbbase)
+ goto error_iomap;
+
+ /* Add each capability. */
+ for (n = 0; n <= card->n_caps; n++) {
+ err = whci_add_cap(card, n);
+ if (err < 0 && n == 0) {
+ dev_err(&pci->dev, "cannot bind UWB radio controller:"
+ " %d\n", err);
+ goto error_bind;
+ }
+ if (err < 0)
+ dev_warn(&pci->dev, "warning: cannot bind capability "
+ "#%u: %d\n", n, err);
+ }
+ pci_set_drvdata(pci, card);
+ return 0;
+
+error_bind:
+ pci_iounmap(pci, card->uwbbase);
+error_iomap:
+ release_mem_region(pci_resource_start(pci, 0), UWBCAPDATA_SIZE(card->n_caps));
+error_request_memregion:
+ kfree(card);
+error_kzalloc:
+error_ncaps:
+error_dma:
+ pci_disable_device(pci);
+error:
+ return err;
+}
+
+static void whci_remove(struct pci_dev *pci)
+{
+ struct whci_card *card = pci_get_drvdata(pci);
+ int n;
+
+ d_fnstart(3, &pci->dev, "(pci %p)\n", pci);
+ pci_set_drvdata(pci, NULL);
+ /* Unregister each capability in reverse (so the master device
+ * is unregistered last). */
+ for (n = card->n_caps; n >= 0 ; n--)
+ whci_del_cap(card, n);
+ pci_iounmap(pci, card->uwbbase);
+ release_mem_region(pci_resource_start(pci, 0), UWBCAPDATA_SIZE(card->n_caps));
+ kfree(card);
+ pci_disable_msi(pci);
+ pci_disable_device(pci);
+ d_fnend(3, &pci->dev, "(pci %p)\n", pci);
+}
+
+static struct pci_device_id whci_id_table[] = {
+ { PCI_DEVICE_CLASS(PCI_CLASS_WIRELESS_WHCI, ~0) },
+ { 0 },
+};
+MODULE_DEVICE_TABLE(pci, whci_id_table);
+
+
+static struct pci_driver whci_driver = {
+ .name = "whci",
+ .id_table = whci_id_table,
+ .probe = whci_probe,
+ .remove = whci_remove,
+};
+
+static int __init whci_init(void)
+{
+ return pci_register_driver(&whci_driver);
+}
+
+static void __exit whci_exit(void)
+{
+ pci_unregister_driver(&whci_driver);
+}
+
+module_init(whci_init);
+module_exit(whci_exit);
+
+MODULE_DESCRIPTION("WHCI UWB Multi-interface Controller enumerator");
+MODULE_AUTHOR("Cambridge Silicon Radio Ltd.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/uwb/wlp/Makefile b/drivers/uwb/wlp/Makefile
new file mode 100644
index 000000000000..c72c11db5b1b
--- /dev/null
+++ b/drivers/uwb/wlp/Makefile
@@ -0,0 +1,10 @@
+obj-$(CONFIG_UWB_WLP) := wlp.o
+
+wlp-objs := \
+ driver.o \
+ eda.o \
+ messages.o \
+ sysfs.o \
+ txrx.o \
+ wlp-lc.o \
+ wss-lc.o
diff --git a/drivers/uwb/wlp/driver.c b/drivers/uwb/wlp/driver.c
new file mode 100644
index 000000000000..cb8d699b6a67
--- /dev/null
+++ b/drivers/uwb/wlp/driver.c
@@ -0,0 +1,43 @@
+/*
+ * WiMedia Logical Link Control Protocol (WLP)
+ *
+ * Copyright (C) 2007 Intel Corporation
+ * Reinette Chatre <reinette.chatre@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Life cycle of WLP substack
+ *
+ * FIXME: Docs
+ */
+
+#include <linux/module.h>
+
+static int __init wlp_subsys_init(void)
+{
+ return 0;
+}
+module_init(wlp_subsys_init);
+
+static void __exit wlp_subsys_exit(void)
+{
+ return;
+}
+module_exit(wlp_subsys_exit);
+
+MODULE_AUTHOR("Reinette Chatre <reinette.chatre@intel.com>");
+MODULE_DESCRIPTION("WiMedia Logical Link Control Protocol (WLP)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/uwb/wlp/eda.c b/drivers/uwb/wlp/eda.c
new file mode 100644
index 000000000000..b5bef564d57c
--- /dev/null
+++ b/drivers/uwb/wlp/eda.c
@@ -0,0 +1,449 @@
+/*
+ * WUSB Wire Adapter: WLP interface
+ * Ethernet to device address cache
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * We need to be able to map ethernet addresses to device addresses
+ * and back because there is not explicit relationship between the eth
+ * addresses used in the ETH frames and the device addresses (no, it
+ * would not have been simpler to force as ETH address the MBOA MAC
+ * address...no, not at all :).
+ *
+ * A device has one MBOA MAC address and one device address. It is possible
+ * for a device to have more than one virtual MAC address (although a
+ * virtual address can be the same as the MBOA MAC address). The device
+ * address is guaranteed to be unique among the devices in the extended
+ * beacon group (see ECMA 17.1.1). We thus use the device address as index
+ * to this cache. We do allow searching based on virtual address as this
+ * is how Ethernet frames will be addressed.
+ *
+ * We need to support virtual EUI-48. Although, right now the virtual
+ * EUI-48 will always be the same as the MAC SAP address. The EDA cache
+ * entry thus contains a MAC SAP address as well as the virtual address
+ * (used to map the network stack address to a neighbor). When we move
+ * to support more than one virtual MAC on a host then this organization
+ * will have to change. Perhaps a neighbor has a list of WSSs, each with a
+ * tag and virtual EUI-48.
+ *
+ * On data transmission
+ * it is used to determine if the neighbor is connected and what WSS it
+ * belongs to. With this we know what tag to add to the WLP frame. Storing
+ * the WSS in the EDA cache may be overkill because we only support one
+ * WSS. Hopefully we will support more than one WSS at some point.
+ * On data reception it is used to determine the WSS based on
+ * the tag and address of the transmitting neighbor.
+ */
+
+#define D_LOCAL 5
+#include <linux/netdevice.h>
+#include <linux/uwb/debug.h>
+#include <linux/etherdevice.h>
+#include <linux/wlp.h>
+#include "wlp-internal.h"
+
+
+/* FIXME: cache is not purged, only on device close */
+
+/* FIXME: does not scale, change to dynamic array */
+
+/*
+ * Initialize the EDA cache
+ *
+ * @returns 0 if ok, < 0 errno code on error
+ *
+ * Call when the interface is being brought up
+ *
+ * NOTE: Keep it as a separate function as the implementation will
+ * change and be more complex.
+ */
+void wlp_eda_init(struct wlp_eda *eda)
+{
+ INIT_LIST_HEAD(&eda->cache);
+ spin_lock_init(&eda->lock);
+}
+
+/*
+ * Release the EDA cache
+ *
+ * @returns 0 if ok, < 0 errno code on error
+ *
+ * Called when the interface is brought down
+ */
+void wlp_eda_release(struct wlp_eda *eda)
+{
+ unsigned long flags;
+ struct wlp_eda_node *itr, *next;
+
+ spin_lock_irqsave(&eda->lock, flags);
+ list_for_each_entry_safe(itr, next, &eda->cache, list_node) {
+ list_del(&itr->list_node);
+ kfree(itr);
+ }
+ spin_unlock_irqrestore(&eda->lock, flags);
+}
+
+/*
+ * Add an address mapping
+ *
+ * @returns 0 if ok, < 0 errno code on error
+ *
+ * An address mapping is initially created when the neighbor device is seen
+ * for the first time (it is "onair"). At this time the neighbor is not
+ * connected or associated with a WSS so we only populate the Ethernet and
+ * Device address fields.
+ *
+ */
+int wlp_eda_create_node(struct wlp_eda *eda,
+ const unsigned char eth_addr[ETH_ALEN],
+ const struct uwb_dev_addr *dev_addr)
+{
+ int result = 0;
+ struct wlp_eda_node *itr;
+ unsigned long flags;
+
+ BUG_ON(dev_addr == NULL || eth_addr == NULL);
+ spin_lock_irqsave(&eda->lock, flags);
+ list_for_each_entry(itr, &eda->cache, list_node) {
+ if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
+ printk(KERN_ERR "EDA cache already contains entry "
+ "for neighbor %02x:%02x\n",
+ dev_addr->data[1], dev_addr->data[0]);
+ result = -EEXIST;
+ goto out_unlock;
+ }
+ }
+ itr = kzalloc(sizeof(*itr), GFP_ATOMIC);
+ if (itr != NULL) {
+ memcpy(itr->eth_addr, eth_addr, sizeof(itr->eth_addr));
+ itr->dev_addr = *dev_addr;
+ list_add(&itr->list_node, &eda->cache);
+ } else
+ result = -ENOMEM;
+out_unlock:
+ spin_unlock_irqrestore(&eda->lock, flags);
+ return result;
+}
+
+/*
+ * Remove entry from EDA cache
+ *
+ * This is done when the device goes off air.
+ */
+void wlp_eda_rm_node(struct wlp_eda *eda, const struct uwb_dev_addr *dev_addr)
+{
+ struct wlp_eda_node *itr, *next;
+ unsigned long flags;
+
+ spin_lock_irqsave(&eda->lock, flags);
+ list_for_each_entry_safe(itr, next, &eda->cache, list_node) {
+ if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
+ list_del(&itr->list_node);
+ kfree(itr);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&eda->lock, flags);
+}
+
+/*
+ * Update an address mapping
+ *
+ * @returns 0 if ok, < 0 errno code on error
+ */
+int wlp_eda_update_node(struct wlp_eda *eda,
+ const struct uwb_dev_addr *dev_addr,
+ struct wlp_wss *wss,
+ const unsigned char virt_addr[ETH_ALEN],
+ const u8 tag, const enum wlp_wss_connect state)
+{
+ int result = -ENOENT;
+ struct wlp_eda_node *itr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&eda->lock, flags);
+ list_for_each_entry(itr, &eda->cache, list_node) {
+ if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
+ /* Found it, update it */
+ itr->wss = wss;
+ memcpy(itr->virt_addr, virt_addr,
+ sizeof(itr->virt_addr));
+ itr->tag = tag;
+ itr->state = state;
+ result = 0;
+ goto out_unlock;
+ }
+ }
+ /* Not found */
+out_unlock:
+ spin_unlock_irqrestore(&eda->lock, flags);
+ return result;
+}
+
+/*
+ * Update only state field of an address mapping
+ *
+ * @returns 0 if ok, < 0 errno code on error
+ */
+int wlp_eda_update_node_state(struct wlp_eda *eda,
+ const struct uwb_dev_addr *dev_addr,
+ const enum wlp_wss_connect state)
+{
+ int result = -ENOENT;
+ struct wlp_eda_node *itr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&eda->lock, flags);
+ list_for_each_entry(itr, &eda->cache, list_node) {
+ if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
+ /* Found it, update it */
+ itr->state = state;
+ result = 0;
+ goto out_unlock;
+ }
+ }
+ /* Not found */
+out_unlock:
+ spin_unlock_irqrestore(&eda->lock, flags);
+ return result;
+}
+
+/*
+ * Return contents of EDA cache entry
+ *
+ * @dev_addr: index to EDA cache
+ * @eda_entry: pointer to where contents of EDA cache will be copied
+ */
+int wlp_copy_eda_node(struct wlp_eda *eda, struct uwb_dev_addr *dev_addr,
+ struct wlp_eda_node *eda_entry)
+{
+ int result = -ENOENT;
+ struct wlp_eda_node *itr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&eda->lock, flags);
+ list_for_each_entry(itr, &eda->cache, list_node) {
+ if (!memcmp(&itr->dev_addr, dev_addr, sizeof(itr->dev_addr))) {
+ *eda_entry = *itr;
+ result = 0;
+ goto out_unlock;
+ }
+ }
+ /* Not found */
+out_unlock:
+ spin_unlock_irqrestore(&eda->lock, flags);
+ return result;
+}
+
+/*
+ * Execute function for every element in the cache
+ *
+ * @function: function to execute on element of cache (must be atomic)
+ * @priv: private data of function
+ * @returns: result of first function that failed, or last function
+ * executed if no function failed.
+ *
+ * Stop executing when function returns error for any element in cache.
+ *
+ * IMPORTANT: We are using a spinlock here: the function executed on each
+ * element has to be atomic.
+ */
+int wlp_eda_for_each(struct wlp_eda *eda, wlp_eda_for_each_f function,
+ void *priv)
+{
+ int result = 0;
+ struct wlp *wlp = container_of(eda, struct wlp, eda);
+ struct wlp_eda_node *entry;
+ unsigned long flags;
+
+ spin_lock_irqsave(&eda->lock, flags);
+ list_for_each_entry(entry, &eda->cache, list_node) {
+ result = (*function)(wlp, entry, priv);
+ if (result < 0)
+ break;
+ }
+ spin_unlock_irqrestore(&eda->lock, flags);
+ return result;
+}
+
+/*
+ * Execute function for single element in the cache (return dev addr)
+ *
+ * @virt_addr: index into EDA cache used to determine which element to
+ * execute the function on
+ * @dev_addr: device address of element in cache will be returned using
+ * @dev_addr
+ * @function: function to execute on element of cache (must be atomic)
+ * @priv: private data of function
+ * @returns: result of function
+ *
+ * IMPORTANT: We are using a spinlock here: the function executed on the
+ * element has to be atomic.
+ */
+int wlp_eda_for_virtual(struct wlp_eda *eda,
+ const unsigned char virt_addr[ETH_ALEN],
+ struct uwb_dev_addr *dev_addr,
+ wlp_eda_for_each_f function,
+ void *priv)
+{
+ int result = 0;
+ struct wlp *wlp = container_of(eda, struct wlp, eda);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_eda_node *itr;
+ unsigned long flags;
+ int found = 0;
+
+ spin_lock_irqsave(&eda->lock, flags);
+ list_for_each_entry(itr, &eda->cache, list_node) {
+ if (!memcmp(itr->virt_addr, virt_addr,
+ sizeof(itr->virt_addr))) {
+ d_printf(6, dev, "EDA: looking for "
+ "%02x:%02x:%02x:%02x:%02x:%02x hit %02x:%02x "
+ "wss %p tag 0x%02x state %u\n",
+ virt_addr[0], virt_addr[1],
+ virt_addr[2], virt_addr[3],
+ virt_addr[4], virt_addr[5],
+ itr->dev_addr.data[1],
+ itr->dev_addr.data[0], itr->wss,
+ itr->tag, itr->state);
+ result = (*function)(wlp, itr, priv);
+ *dev_addr = itr->dev_addr;
+ found = 1;
+ break;
+ } else
+ d_printf(6, dev, "EDA: looking for "
+ "%02x:%02x:%02x:%02x:%02x:%02x "
+ "against "
+ "%02x:%02x:%02x:%02x:%02x:%02x miss\n",
+ virt_addr[0], virt_addr[1],
+ virt_addr[2], virt_addr[3],
+ virt_addr[4], virt_addr[5],
+ itr->virt_addr[0], itr->virt_addr[1],
+ itr->virt_addr[2], itr->virt_addr[3],
+ itr->virt_addr[4], itr->virt_addr[5]);
+ }
+ if (!found) {
+ if (printk_ratelimit())
+ dev_err(dev, "EDA: Eth addr %02x:%02x:%02x"
+ ":%02x:%02x:%02x not found.\n",
+ virt_addr[0], virt_addr[1],
+ virt_addr[2], virt_addr[3],
+ virt_addr[4], virt_addr[5]);
+ result = -ENODEV;
+ }
+ spin_unlock_irqrestore(&eda->lock, flags);
+ return result;
+}
+
+static const char *__wlp_wss_connect_state[] = { "WLP_WSS_UNCONNECTED",
+ "WLP_WSS_CONNECTED",
+ "WLP_WSS_CONNECT_FAILED",
+};
+
+static const char *wlp_wss_connect_state_str(unsigned id)
+{
+ if (id >= ARRAY_SIZE(__wlp_wss_connect_state))
+ return "unknown WSS connection state";
+ return __wlp_wss_connect_state[id];
+}
+
+/*
+ * View EDA cache from user space
+ *
+ * A debugging feature to give user visibility into the EDA cache. Also
+ * used to display members of WSS to user (called from wlp_wss_members_show())
+ */
+ssize_t wlp_eda_show(struct wlp *wlp, char *buf)
+{
+ ssize_t result = 0;
+ struct wlp_eda_node *entry;
+ unsigned long flags;
+ struct wlp_eda *eda = &wlp->eda;
+ spin_lock_irqsave(&eda->lock, flags);
+ result = scnprintf(buf, PAGE_SIZE, "#eth_addr dev_addr wss_ptr "
+ "tag state virt_addr\n");
+ list_for_each_entry(entry, &eda->cache, list_node) {
+ result += scnprintf(buf + result, PAGE_SIZE - result,
+ "%02x:%02x:%02x:%02x:%02x:%02x %02x:%02x "
+ "%p 0x%02x %s "
+ "%02x:%02x:%02x:%02x:%02x:%02x\n",
+ entry->eth_addr[0], entry->eth_addr[1],
+ entry->eth_addr[2], entry->eth_addr[3],
+ entry->eth_addr[4], entry->eth_addr[5],
+ entry->dev_addr.data[1],
+ entry->dev_addr.data[0], entry->wss,
+ entry->tag,
+ wlp_wss_connect_state_str(entry->state),
+ entry->virt_addr[0], entry->virt_addr[1],
+ entry->virt_addr[2], entry->virt_addr[3],
+ entry->virt_addr[4], entry->virt_addr[5]);
+ if (result >= PAGE_SIZE)
+ break;
+ }
+ spin_unlock_irqrestore(&eda->lock, flags);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wlp_eda_show);
+
+/*
+ * Add new EDA cache entry based on user input in sysfs
+ *
+ * Should only be used for debugging.
+ *
+ * The WSS is assumed to be the only WSS supported. This needs to be
+ * redesigned when we support more than one WSS.
+ */
+ssize_t wlp_eda_store(struct wlp *wlp, const char *buf, size_t size)
+{
+ ssize_t result;
+ struct wlp_eda *eda = &wlp->eda;
+ u8 eth_addr[6];
+ struct uwb_dev_addr dev_addr;
+ u8 tag;
+ unsigned state;
+
+ result = sscanf(buf, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx "
+ "%02hhx:%02hhx %02hhx %u\n",
+ &eth_addr[0], &eth_addr[1],
+ &eth_addr[2], &eth_addr[3],
+ &eth_addr[4], &eth_addr[5],
+ &dev_addr.data[1], &dev_addr.data[0], &tag, &state);
+ switch (result) {
+ case 6: /* no dev addr specified -- remove entry NOT IMPLEMENTED */
+ /*result = wlp_eda_rm(eda, eth_addr, &dev_addr);*/
+ result = -ENOSYS;
+ break;
+ case 10:
+ state = state >= 1 ? 1 : 0;
+ result = wlp_eda_create_node(eda, eth_addr, &dev_addr);
+ if (result < 0 && result != -EEXIST)
+ goto error;
+ /* Set virtual addr to be same as MAC */
+ result = wlp_eda_update_node(eda, &dev_addr, &wlp->wss,
+ eth_addr, tag, state);
+ if (result < 0)
+ goto error;
+ break;
+ default: /* bad format */
+ result = -EINVAL;
+ }
+error:
+ return result < 0? result : size;
+}
+EXPORT_SYMBOL_GPL(wlp_eda_store);
diff --git a/drivers/uwb/wlp/messages.c b/drivers/uwb/wlp/messages.c
new file mode 100644
index 000000000000..a64cb8241713
--- /dev/null
+++ b/drivers/uwb/wlp/messages.c
@@ -0,0 +1,1946 @@
+/*
+ * WiMedia Logical Link Control Protocol (WLP)
+ * Message construction and parsing
+ *
+ * Copyright (C) 2007 Intel Corporation
+ * Reinette Chatre <reinette.chatre@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ */
+
+#include <linux/wlp.h>
+#define D_LOCAL 6
+#include <linux/uwb/debug.h>
+#include "wlp-internal.h"
+
+static
+const char *__wlp_assoc_frame[] = {
+ [WLP_ASSOC_D1] = "WLP_ASSOC_D1",
+ [WLP_ASSOC_D2] = "WLP_ASSOC_D2",
+ [WLP_ASSOC_M1] = "WLP_ASSOC_M1",
+ [WLP_ASSOC_M2] = "WLP_ASSOC_M2",
+ [WLP_ASSOC_M3] = "WLP_ASSOC_M3",
+ [WLP_ASSOC_M4] = "WLP_ASSOC_M4",
+ [WLP_ASSOC_M5] = "WLP_ASSOC_M5",
+ [WLP_ASSOC_M6] = "WLP_ASSOC_M6",
+ [WLP_ASSOC_M7] = "WLP_ASSOC_M7",
+ [WLP_ASSOC_M8] = "WLP_ASSOC_M8",
+ [WLP_ASSOC_F0] = "WLP_ASSOC_F0",
+ [WLP_ASSOC_E1] = "WLP_ASSOC_E1",
+ [WLP_ASSOC_E2] = "WLP_ASSOC_E2",
+ [WLP_ASSOC_C1] = "WLP_ASSOC_C1",
+ [WLP_ASSOC_C2] = "WLP_ASSOC_C2",
+ [WLP_ASSOC_C3] = "WLP_ASSOC_C3",
+ [WLP_ASSOC_C4] = "WLP_ASSOC_C4",
+};
+
+static const char *wlp_assoc_frame_str(unsigned id)
+{
+ if (id >= ARRAY_SIZE(__wlp_assoc_frame))
+ return "unknown association frame";
+ return __wlp_assoc_frame[id];
+}
+
+static const char *__wlp_assc_error[] = {
+ "none",
+ "Authenticator Failure",
+ "Rogue activity suspected",
+ "Device busy",
+ "Setup Locked",
+ "Registrar not ready",
+ "Invalid WSS selection",
+ "Message timeout",
+ "Enrollment session timeout",
+ "Device password invalid",
+ "Unsupported version",
+ "Internal error",
+ "Undefined error",
+ "Numeric comparison failure",
+ "Waiting for user input",
+};
+
+static const char *wlp_assc_error_str(unsigned id)
+{
+ if (id >= ARRAY_SIZE(__wlp_assc_error))
+ return "unknown WLP association error";
+ return __wlp_assc_error[id];
+}
+
+static inline void wlp_set_attr_hdr(struct wlp_attr_hdr *hdr, unsigned type,
+ size_t len)
+{
+ hdr->type = cpu_to_le16(type);
+ hdr->length = cpu_to_le16(len);
+}
+
+/*
+ * Populate fields of a constant sized attribute
+ *
+ * @returns: total size of attribute including size of new value
+ *
+ * We have two instances of this function (wlp_pset and wlp_set): one takes
+ * the value as a parameter, the other takes a pointer to the value as
+ * parameter. They thus only differ in how the value is assigned to the
+ * attribute.
+ *
+ * We use sizeof(*attr) - sizeof(struct wlp_attr_hdr) instead of
+ * sizeof(type) to be able to use this same code for the structures that
+ * contain 8bit enum values and be able to deal with pointer types.
+ */
+#define wlp_set(type, type_code, name) \
+static size_t wlp_set_##name(struct wlp_attr_##name *attr, type value) \
+{ \
+ d_fnstart(6, NULL, "(attribute %p)\n", attr); \
+ wlp_set_attr_hdr(&attr->hdr, type_code, \
+ sizeof(*attr) - sizeof(struct wlp_attr_hdr)); \
+ attr->name = value; \
+ d_dump(6, NULL, attr, sizeof(*attr)); \
+ d_fnend(6, NULL, "(attribute %p)\n", attr); \
+ return sizeof(*attr); \
+}
+
+#define wlp_pset(type, type_code, name) \
+static size_t wlp_set_##name(struct wlp_attr_##name *attr, type value) \
+{ \
+ d_fnstart(6, NULL, "(attribute %p)\n", attr); \
+ wlp_set_attr_hdr(&attr->hdr, type_code, \
+ sizeof(*attr) - sizeof(struct wlp_attr_hdr)); \
+ attr->name = *value; \
+ d_dump(6, NULL, attr, sizeof(*attr)); \
+ d_fnend(6, NULL, "(attribute %p)\n", attr); \
+ return sizeof(*attr); \
+}
+
+/**
+ * Populate fields of a variable attribute
+ *
+ * @returns: total size of attribute including size of new value
+ *
+ * Provided with a pointer to the memory area reserved for the
+ * attribute structure, the field is populated with the value. The
+ * reserved memory has to contain enough space for the value.
+ */
+#define wlp_vset(type, type_code, name) \
+static size_t wlp_set_##name(struct wlp_attr_##name *attr, type value, \
+ size_t len) \
+{ \
+ d_fnstart(6, NULL, "(attribute %p)\n", attr); \
+ wlp_set_attr_hdr(&attr->hdr, type_code, len); \
+ memcpy(attr->name, value, len); \
+ d_dump(6, NULL, attr, sizeof(*attr) + len); \
+ d_fnend(6, NULL, "(attribute %p)\n", attr); \
+ return sizeof(*attr) + len; \
+}
+
+wlp_vset(char *, WLP_ATTR_DEV_NAME, dev_name)
+wlp_vset(char *, WLP_ATTR_MANUF, manufacturer)
+wlp_set(enum wlp_assoc_type, WLP_ATTR_MSG_TYPE, msg_type)
+wlp_vset(char *, WLP_ATTR_MODEL_NAME, model_name)
+wlp_vset(char *, WLP_ATTR_MODEL_NR, model_nr)
+wlp_vset(char *, WLP_ATTR_SERIAL, serial)
+wlp_vset(char *, WLP_ATTR_WSS_NAME, wss_name)
+wlp_pset(struct wlp_uuid *, WLP_ATTR_UUID_E, uuid_e)
+wlp_pset(struct wlp_uuid *, WLP_ATTR_UUID_R, uuid_r)
+wlp_pset(struct wlp_uuid *, WLP_ATTR_WSSID, wssid)
+wlp_pset(struct wlp_dev_type *, WLP_ATTR_PRI_DEV_TYPE, prim_dev_type)
+/*wlp_pset(struct wlp_dev_type *, WLP_ATTR_SEC_DEV_TYPE, sec_dev_type)*/
+wlp_set(u8, WLP_ATTR_WLP_VER, version)
+wlp_set(enum wlp_assc_error, WLP_ATTR_WLP_ASSC_ERR, wlp_assc_err)
+wlp_set(enum wlp_wss_sel_mthd, WLP_ATTR_WSS_SEL_MTHD, wss_sel_mthd)
+wlp_set(u8, WLP_ATTR_ACC_ENRL, accept_enrl)
+wlp_set(u8, WLP_ATTR_WSS_SEC_STAT, wss_sec_status)
+wlp_pset(struct uwb_mac_addr *, WLP_ATTR_WSS_BCAST, wss_bcast)
+wlp_pset(struct wlp_nonce *, WLP_ATTR_ENRL_NONCE, enonce)
+wlp_pset(struct wlp_nonce *, WLP_ATTR_REG_NONCE, rnonce)
+wlp_set(u8, WLP_ATTR_WSS_TAG, wss_tag)
+wlp_pset(struct uwb_mac_addr *, WLP_ATTR_WSS_VIRT, wss_virt)
+
+/**
+ * Fill in the WSS information attributes
+ *
+ * We currently only support one WSS, and this is assumed in this function
+ * that can populate only one WSS information attribute.
+ */
+static size_t wlp_set_wss_info(struct wlp_attr_wss_info *attr,
+ struct wlp_wss *wss)
+{
+ size_t datalen;
+ void *ptr = attr->wss_info;
+ size_t used = sizeof(*attr);
+ d_fnstart(6, NULL, "(attribute %p)\n", attr);
+ datalen = sizeof(struct wlp_wss_info) + strlen(wss->name);
+ wlp_set_attr_hdr(&attr->hdr, WLP_ATTR_WSS_INFO, datalen);
+ used = wlp_set_wssid(ptr, &wss->wssid);
+ used += wlp_set_wss_name(ptr + used, wss->name, strlen(wss->name));
+ used += wlp_set_accept_enrl(ptr + used, wss->accept_enroll);
+ used += wlp_set_wss_sec_status(ptr + used, wss->secure_status);
+ used += wlp_set_wss_bcast(ptr + used, &wss->bcast);
+ d_dump(6, NULL, attr, sizeof(*attr) + datalen);
+ d_fnend(6, NULL, "(attribute %p, used %d)\n",
+ attr, (int)(sizeof(*attr) + used));
+ return sizeof(*attr) + used;
+}
+
+/**
+ * Verify attribute header
+ *
+ * @hdr: Pointer to attribute header that will be verified.
+ * @type: Expected attribute type.
+ * @len: Expected length of attribute value (excluding header).
+ *
+ * Most attribute values have a known length even when they do have a
+ * length field. This knowledge can be used via this function to verify
+ * that the length field matches the expected value.
+ */
+static int wlp_check_attr_hdr(struct wlp *wlp, struct wlp_attr_hdr *hdr,
+ enum wlp_attr_type type, unsigned len)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+
+ if (le16_to_cpu(hdr->type) != type) {
+ dev_err(dev, "WLP: unexpected header type. Expected "
+ "%u, got %u.\n", type, le16_to_cpu(hdr->type));
+ return -EINVAL;
+ }
+ if (le16_to_cpu(hdr->length) != len) {
+ dev_err(dev, "WLP: unexpected length in header. Expected "
+ "%u, got %u.\n", len, le16_to_cpu(hdr->length));
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
+ * Check if header of WSS information attribute valid
+ *
+ * @returns: length of WSS attributes (value of length attribute field) if
+ * valid WSS information attribute found
+ * -ENODATA if no WSS information attribute found
+ * -EIO other error occured
+ *
+ * The WSS information attribute is optional. The function will be provided
+ * with a pointer to data that could _potentially_ be a WSS information
+ * attribute. If a valid WSS information attribute is found it will return
+ * 0, if no WSS information attribute is found it will return -ENODATA, and
+ * another error will be returned if it is a WSS information attribute, but
+ * some parsing failure occured.
+ */
+static int wlp_check_wss_info_attr_hdr(struct wlp *wlp,
+ struct wlp_attr_hdr *hdr, size_t buflen)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ size_t len;
+ int result = 0;
+
+ if (buflen < sizeof(*hdr)) {
+ dev_err(dev, "WLP: Not enough space in buffer to parse"
+ " WSS information attribute header.\n");
+ result = -EIO;
+ goto out;
+ }
+ if (le16_to_cpu(hdr->type) != WLP_ATTR_WSS_INFO) {
+ /* WSS information is optional */
+ result = -ENODATA;
+ goto out;
+ }
+ len = le16_to_cpu(hdr->length);
+ if (buflen < sizeof(*hdr) + len) {
+ dev_err(dev, "WLP: Not enough space in buffer to parse "
+ "variable data. Got %d, expected %d.\n",
+ (int)buflen, (int)(sizeof(*hdr) + len));
+ result = -EIO;
+ goto out;
+ }
+ result = len;
+out:
+ return result;
+}
+
+
+/**
+ * Get value of attribute from fixed size attribute field.
+ *
+ * @attr: Pointer to attribute field.
+ * @value: Pointer to variable in which attribute value will be placed.
+ * @buflen: Size of buffer in which attribute field (including header)
+ * can be found.
+ * @returns: Amount of given buffer consumed by parsing for this attribute.
+ *
+ * The size and type of the value is known by the type of the attribute.
+ */
+#define wlp_get(type, type_code, name) \
+ssize_t wlp_get_##name(struct wlp *wlp, struct wlp_attr_##name *attr, \
+ type *value, ssize_t buflen) \
+{ \
+ struct device *dev = &wlp->rc->uwb_dev.dev; \
+ if (buflen < 0) \
+ return -EINVAL; \
+ if (buflen < sizeof(*attr)) { \
+ dev_err(dev, "WLP: Not enough space in buffer to parse" \
+ " attribute field. Need %d, received %zu\n", \
+ (int)sizeof(*attr), buflen); \
+ return -EIO; \
+ } \
+ if (wlp_check_attr_hdr(wlp, &attr->hdr, type_code, \
+ sizeof(attr->name)) < 0) { \
+ dev_err(dev, "WLP: Header verification failed. \n"); \
+ return -EINVAL; \
+ } \
+ *value = attr->name; \
+ return sizeof(*attr); \
+}
+
+#define wlp_get_sparse(type, type_code, name) \
+ static wlp_get(type, type_code, name)
+
+/**
+ * Get value of attribute from variable sized attribute field.
+ *
+ * @max: The maximum size of this attribute. This value is dictated by
+ * the maximum value from the WLP specification.
+ *
+ * @attr: Pointer to attribute field.
+ * @value: Pointer to variable that will contain the value. The memory
+ * must already have been allocated for this value.
+ * @buflen: Size of buffer in which attribute field (including header)
+ * can be found.
+ * @returns: Amount of given bufferconsumed by parsing for this attribute.
+ */
+#define wlp_vget(type_val, type_code, name, max) \
+static ssize_t wlp_get_##name(struct wlp *wlp, \
+ struct wlp_attr_##name *attr, \
+ type_val *value, ssize_t buflen) \
+{ \
+ struct device *dev = &wlp->rc->uwb_dev.dev; \
+ size_t len; \
+ if (buflen < 0) \
+ return -EINVAL; \
+ if (buflen < sizeof(*attr)) { \
+ dev_err(dev, "WLP: Not enough space in buffer to parse" \
+ " header.\n"); \
+ return -EIO; \
+ } \
+ if (le16_to_cpu(attr->hdr.type) != type_code) { \
+ dev_err(dev, "WLP: Unexpected attribute type. Got %u, " \
+ "expected %u.\n", le16_to_cpu(attr->hdr.type), \
+ type_code); \
+ return -EINVAL; \
+ } \
+ len = le16_to_cpu(attr->hdr.length); \
+ if (len > max) { \
+ dev_err(dev, "WLP: Attribute larger than maximum " \
+ "allowed. Received %zu, max is %d.\n", len, \
+ (int)max); \
+ return -EFBIG; \
+ } \
+ if (buflen < sizeof(*attr) + len) { \
+ dev_err(dev, "WLP: Not enough space in buffer to parse "\
+ "variable data.\n"); \
+ return -EIO; \
+ } \
+ memcpy(value, (void *) attr + sizeof(*attr), len); \
+ return sizeof(*attr) + len; \
+}
+
+wlp_get(u8, WLP_ATTR_WLP_VER, version)
+wlp_get_sparse(enum wlp_wss_sel_mthd, WLP_ATTR_WSS_SEL_MTHD, wss_sel_mthd)
+wlp_get_sparse(struct wlp_dev_type, WLP_ATTR_PRI_DEV_TYPE, prim_dev_type)
+wlp_get_sparse(enum wlp_assc_error, WLP_ATTR_WLP_ASSC_ERR, wlp_assc_err)
+wlp_get_sparse(struct wlp_uuid, WLP_ATTR_UUID_E, uuid_e)
+wlp_get_sparse(struct wlp_uuid, WLP_ATTR_UUID_R, uuid_r)
+wlp_get(struct wlp_uuid, WLP_ATTR_WSSID, wssid)
+wlp_get_sparse(u8, WLP_ATTR_ACC_ENRL, accept_enrl)
+wlp_get_sparse(u8, WLP_ATTR_WSS_SEC_STAT, wss_sec_status)
+wlp_get_sparse(struct uwb_mac_addr, WLP_ATTR_WSS_BCAST, wss_bcast)
+wlp_get_sparse(u8, WLP_ATTR_WSS_TAG, wss_tag)
+wlp_get_sparse(struct uwb_mac_addr, WLP_ATTR_WSS_VIRT, wss_virt)
+wlp_get_sparse(struct wlp_nonce, WLP_ATTR_ENRL_NONCE, enonce)
+wlp_get_sparse(struct wlp_nonce, WLP_ATTR_REG_NONCE, rnonce)
+
+/* The buffers for the device info attributes can be found in the
+ * wlp_device_info struct. These buffers contain one byte more than the
+ * max allowed by the spec - this is done to be able to add the
+ * terminating \0 for user display. This terminating byte is not required
+ * in the actual attribute field (because it has a length field) so the
+ * maximum allowed for this value is one less than its size in the
+ * structure.
+ */
+wlp_vget(char, WLP_ATTR_WSS_NAME, wss_name,
+ FIELD_SIZEOF(struct wlp_wss, name) - 1)
+wlp_vget(char, WLP_ATTR_DEV_NAME, dev_name,
+ FIELD_SIZEOF(struct wlp_device_info, name) - 1)
+wlp_vget(char, WLP_ATTR_MANUF, manufacturer,
+ FIELD_SIZEOF(struct wlp_device_info, manufacturer) - 1)
+wlp_vget(char, WLP_ATTR_MODEL_NAME, model_name,
+ FIELD_SIZEOF(struct wlp_device_info, model_name) - 1)
+wlp_vget(char, WLP_ATTR_MODEL_NR, model_nr,
+ FIELD_SIZEOF(struct wlp_device_info, model_nr) - 1)
+wlp_vget(char, WLP_ATTR_SERIAL, serial,
+ FIELD_SIZEOF(struct wlp_device_info, serial) - 1)
+
+/**
+ * Retrieve WSS Name, Accept enroll, Secure status, Broadcast from WSS info
+ *
+ * @attr: pointer to WSS name attribute in WSS information attribute field
+ * @info: structure that will be populated with data from WSS information
+ * field (WSS name, Accept enroll, secure status, broadcast address)
+ * @buflen: size of buffer
+ *
+ * Although the WSSID attribute forms part of the WSS info attribute it is
+ * retrieved separately and stored in a different location.
+ */
+static ssize_t wlp_get_wss_info_attrs(struct wlp *wlp,
+ struct wlp_attr_hdr *attr,
+ struct wlp_wss_tmp_info *info,
+ ssize_t buflen)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ void *ptr = attr;
+ size_t used = 0;
+ ssize_t result = -EINVAL;
+
+ d_printf(6, dev, "WLP: WSS info: Retrieving WSS name\n");
+ result = wlp_get_wss_name(wlp, ptr, info->name, buflen);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSS name from "
+ "WSS info in D2 message.\n");
+ goto error_parse;
+ }
+ used += result;
+ d_printf(6, dev, "WLP: WSS info: Retrieving accept enroll\n");
+ result = wlp_get_accept_enrl(wlp, ptr + used, &info->accept_enroll,
+ buflen - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain accepting "
+ "enrollment from WSS info in D2 message.\n");
+ goto error_parse;
+ }
+ if (info->accept_enroll != 0 && info->accept_enroll != 1) {
+ dev_err(dev, "WLP: invalid value for accepting "
+ "enrollment in D2 message.\n");
+ result = -EINVAL;
+ goto error_parse;
+ }
+ used += result;
+ d_printf(6, dev, "WLP: WSS info: Retrieving secure status\n");
+ result = wlp_get_wss_sec_status(wlp, ptr + used, &info->sec_status,
+ buflen - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain secure "
+ "status from WSS info in D2 message.\n");
+ goto error_parse;
+ }
+ if (info->sec_status != 0 && info->sec_status != 1) {
+ dev_err(dev, "WLP: invalid value for secure "
+ "status in D2 message.\n");
+ result = -EINVAL;
+ goto error_parse;
+ }
+ used += result;
+ d_printf(6, dev, "WLP: WSS info: Retrieving broadcast\n");
+ result = wlp_get_wss_bcast(wlp, ptr + used, &info->bcast,
+ buflen - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain broadcast "
+ "address from WSS info in D2 message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = used;
+error_parse:
+ return result;
+}
+
+/**
+ * Create a new WSSID entry for the neighbor, allocate temporary storage
+ *
+ * Each neighbor can have many WSS active. We maintain a list of WSSIDs
+ * advertised by neighbor. During discovery we also cache information about
+ * these WSS in temporary storage.
+ *
+ * The temporary storage will be removed after it has been used (eg.
+ * displayed to user), the wssid element will be removed from the list when
+ * the neighbor is rediscovered or when it disappears.
+ */
+static struct wlp_wssid_e *wlp_create_wssid_e(struct wlp *wlp,
+ struct wlp_neighbor_e *neighbor)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_wssid_e *wssid_e;
+
+ wssid_e = kzalloc(sizeof(*wssid_e), GFP_KERNEL);
+ if (wssid_e == NULL) {
+ dev_err(dev, "WLP: unable to allocate memory "
+ "for WSS information.\n");
+ goto error_alloc;
+ }
+ wssid_e->info = kzalloc(sizeof(struct wlp_wss_tmp_info), GFP_KERNEL);
+ if (wssid_e->info == NULL) {
+ dev_err(dev, "WLP: unable to allocate memory "
+ "for temporary WSS information.\n");
+ kfree(wssid_e);
+ wssid_e = NULL;
+ goto error_alloc;
+ }
+ list_add(&wssid_e->node, &neighbor->wssid);
+error_alloc:
+ return wssid_e;
+}
+
+/**
+ * Parse WSS information attribute
+ *
+ * @attr: pointer to WSS information attribute header
+ * @buflen: size of buffer in which WSS information attribute appears
+ * @wssid: will place wssid from WSS info attribute in this location
+ * @wss_info: will place other information from WSS information attribute
+ * in this location
+ *
+ * memory for @wssid and @wss_info must be allocated when calling this
+ */
+static ssize_t wlp_get_wss_info(struct wlp *wlp, struct wlp_attr_wss_info *attr,
+ size_t buflen, struct wlp_uuid *wssid,
+ struct wlp_wss_tmp_info *wss_info)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ ssize_t result;
+ size_t len;
+ size_t used = 0;
+ void *ptr;
+
+ result = wlp_check_wss_info_attr_hdr(wlp, (struct wlp_attr_hdr *)attr,
+ buflen);
+ if (result < 0)
+ goto out;
+ len = result;
+ used = sizeof(*attr);
+ ptr = attr;
+ d_printf(6, dev, "WLP: WSS info: Retrieving WSSID\n");
+ result = wlp_get_wssid(wlp, ptr + used, wssid, buflen - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSSID from WSS info.\n");
+ goto out;
+ }
+ used += result;
+ result = wlp_get_wss_info_attrs(wlp, ptr + used, wss_info,
+ buflen - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSS information "
+ "from WSS information attributes. \n");
+ goto out;
+ }
+ used += result;
+ if (len + sizeof(*attr) != used) {
+ dev_err(dev, "WLP: Amount of data parsed does not "
+ "match length field. Parsed %zu, length "
+ "field %zu. \n", used, len);
+ result = -EINVAL;
+ goto out;
+ }
+ result = used;
+ d_printf(6, dev, "WLP: Successfully parsed WLP information "
+ "attribute. used %zu bytes\n", used);
+out:
+ return result;
+}
+
+/**
+ * Retrieve WSS info from association frame
+ *
+ * @attr: pointer to WSS information attribute
+ * @neighbor: ptr to neighbor being discovered, NULL if enrollment in
+ * progress
+ * @wss: ptr to WSS being enrolled in, NULL if discovery in progress
+ * @buflen: size of buffer in which WSS information appears
+ *
+ * The WSS information attribute appears in the D2 association message.
+ * This message is used in two ways: to discover all neighbors or to enroll
+ * into a WSS activated by a neighbor. During discovery we only want to
+ * store the WSS info in a cache, to be deleted right after it has been
+ * used (eg. displayed to the user). During enrollment we store the WSS
+ * information for the lifetime of enrollment.
+ *
+ * During discovery we are interested in all WSS information, during
+ * enrollment we are only interested in the WSS being enrolled in. Even so,
+ * when in enrollment we keep parsing the message after finding the WSS of
+ * interest, this simplifies the calling routine in that it can be sure
+ * that all WSS information attributes have been parsed out of the message.
+ *
+ * Association frame is process with nbmutex held. The list access is safe.
+ */
+static ssize_t wlp_get_all_wss_info(struct wlp *wlp,
+ struct wlp_attr_wss_info *attr,
+ struct wlp_neighbor_e *neighbor,
+ struct wlp_wss *wss, ssize_t buflen)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ size_t used = 0;
+ ssize_t result = -EINVAL;
+ struct wlp_attr_wss_info *cur;
+ struct wlp_uuid wssid;
+ struct wlp_wss_tmp_info wss_info;
+ unsigned enroll; /* 0 - discovery to cache, 1 - enrollment */
+ struct wlp_wssid_e *wssid_e;
+ char buf[WLP_WSS_UUID_STRSIZE];
+
+ d_fnstart(6, dev, "wlp %p, attr %p, neighbor %p, wss %p, buflen %d \n",
+ wlp, attr, neighbor, wss, (int)buflen);
+ if (buflen < 0)
+ goto out;
+
+ if (neighbor != NULL && wss == NULL)
+ enroll = 0; /* discovery */
+ else if (wss != NULL && neighbor == NULL)
+ enroll = 1; /* enrollment */
+ else
+ goto out;
+
+ cur = attr;
+ while (buflen - used > 0) {
+ memset(&wss_info, 0, sizeof(wss_info));
+ cur = (void *)cur + used;
+ result = wlp_get_wss_info(wlp, cur, buflen - used, &wssid,
+ &wss_info);
+ if (result == -ENODATA) {
+ result = used;
+ goto out;
+ } else if (result < 0) {
+ dev_err(dev, "WLP: Unable to parse WSS information "
+ "from WSS information attribute. \n");
+ result = -EINVAL;
+ goto error_parse;
+ }
+ if (enroll && !memcmp(&wssid, &wss->wssid, sizeof(wssid))) {
+ if (wss_info.accept_enroll != 1) {
+ dev_err(dev, "WLP: Requested WSS does "
+ "not accept enrollment.\n");
+ result = -EINVAL;
+ goto out;
+ }
+ memcpy(wss->name, wss_info.name, sizeof(wss->name));
+ wss->bcast = wss_info.bcast;
+ wss->secure_status = wss_info.sec_status;
+ wss->accept_enroll = wss_info.accept_enroll;
+ wss->state = WLP_WSS_STATE_PART_ENROLLED;
+ wlp_wss_uuid_print(buf, sizeof(buf), &wssid);
+ d_printf(2, dev, "WLP: Found WSS %s. Enrolling.\n",
+ buf);
+ } else {
+ wssid_e = wlp_create_wssid_e(wlp, neighbor);
+ if (wssid_e == NULL) {
+ dev_err(dev, "WLP: Cannot create new WSSID "
+ "entry for neighbor %02x:%02x.\n",
+ neighbor->uwb_dev->dev_addr.data[1],
+ neighbor->uwb_dev->dev_addr.data[0]);
+ result = -ENOMEM;
+ goto out;
+ }
+ wssid_e->wssid = wssid;
+ *wssid_e->info = wss_info;
+ }
+ used += result;
+ }
+ result = used;
+error_parse:
+ if (result < 0 && !enroll) /* this was a discovery */
+ wlp_remove_neighbor_tmp_info(neighbor);
+out:
+ d_fnend(6, dev, "wlp %p, attr %p, neighbor %p, wss %p, buflen %d, "
+ "result %d \n", wlp, attr, neighbor, wss, (int)buflen,
+ (int)result);
+ return result;
+
+}
+
+/**
+ * Parse WSS information attributes into cache for discovery
+ *
+ * @attr: the first WSS information attribute in message
+ * @neighbor: the neighbor whose cache will be populated
+ * @buflen: size of the input buffer
+ */
+static ssize_t wlp_get_wss_info_to_cache(struct wlp *wlp,
+ struct wlp_attr_wss_info *attr,
+ struct wlp_neighbor_e *neighbor,
+ ssize_t buflen)
+{
+ return wlp_get_all_wss_info(wlp, attr, neighbor, NULL, buflen);
+}
+
+/**
+ * Parse WSS information attributes into WSS struct for enrollment
+ *
+ * @attr: the first WSS information attribute in message
+ * @wss: the WSS that will be enrolled
+ * @buflen: size of the input buffer
+ */
+static ssize_t wlp_get_wss_info_to_enroll(struct wlp *wlp,
+ struct wlp_attr_wss_info *attr,
+ struct wlp_wss *wss, ssize_t buflen)
+{
+ return wlp_get_all_wss_info(wlp, attr, NULL, wss, buflen);
+}
+
+/**
+ * Construct a D1 association frame
+ *
+ * We use the radio control functions to determine the values of the device
+ * properties. These are of variable length and the total space needed is
+ * tallied first before we start constructing the message. The radio
+ * control functions return strings that are terminated with \0. This
+ * character should not be included in the message (there is a length field
+ * accompanying it in the attribute).
+ */
+static int wlp_build_assoc_d1(struct wlp *wlp, struct wlp_wss *wss,
+ struct sk_buff **skb)
+{
+
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = 0;
+ struct wlp_device_info *info;
+ size_t used = 0;
+ struct wlp_frame_assoc *_d1;
+ struct sk_buff *_skb;
+ void *d1_itr;
+
+ d_fnstart(6, dev, "wlp %p\n", wlp);
+ if (wlp->dev_info == NULL) {
+ result = __wlp_setup_device_info(wlp);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to setup device "
+ "information for D1 message.\n");
+ goto error;
+ }
+ }
+ info = wlp->dev_info;
+ d_printf(6, dev, "Local properties:\n"
+ "Device name (%d bytes): %s\n"
+ "Model name (%d bytes): %s\n"
+ "Manufacturer (%d bytes): %s\n"
+ "Model number (%d bytes): %s\n"
+ "Serial number (%d bytes): %s\n"
+ "Primary device type: \n"
+ " Category: %d \n"
+ " OUI: %02x:%02x:%02x \n"
+ " OUI Subdivision: %u \n",
+ (int)strlen(info->name), info->name,
+ (int)strlen(info->model_name), info->model_name,
+ (int)strlen(info->manufacturer), info->manufacturer,
+ (int)strlen(info->model_nr), info->model_nr,
+ (int)strlen(info->serial), info->serial,
+ info->prim_dev_type.category,
+ info->prim_dev_type.OUI[0], info->prim_dev_type.OUI[1],
+ info->prim_dev_type.OUI[2], info->prim_dev_type.OUIsubdiv);
+ _skb = dev_alloc_skb(sizeof(*_d1)
+ + sizeof(struct wlp_attr_uuid_e)
+ + sizeof(struct wlp_attr_wss_sel_mthd)
+ + sizeof(struct wlp_attr_dev_name)
+ + strlen(info->name)
+ + sizeof(struct wlp_attr_manufacturer)
+ + strlen(info->manufacturer)
+ + sizeof(struct wlp_attr_model_name)
+ + strlen(info->model_name)
+ + sizeof(struct wlp_attr_model_nr)
+ + strlen(info->model_nr)
+ + sizeof(struct wlp_attr_serial)
+ + strlen(info->serial)
+ + sizeof(struct wlp_attr_prim_dev_type)
+ + sizeof(struct wlp_attr_wlp_assc_err));
+ if (_skb == NULL) {
+ dev_err(dev, "WLP: Cannot allocate memory for association "
+ "message.\n");
+ result = -ENOMEM;
+ goto error;
+ }
+ _d1 = (void *) _skb->data;
+ d_printf(6, dev, "D1 starts at %p \n", _d1);
+ _d1->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
+ _d1->hdr.type = WLP_FRAME_ASSOCIATION;
+ _d1->type = WLP_ASSOC_D1;
+
+ wlp_set_version(&_d1->version, WLP_VERSION);
+ wlp_set_msg_type(&_d1->msg_type, WLP_ASSOC_D1);
+ d1_itr = _d1->attr;
+ used = wlp_set_uuid_e(d1_itr, &wlp->uuid);
+ used += wlp_set_wss_sel_mthd(d1_itr + used, WLP_WSS_REG_SELECT);
+ used += wlp_set_dev_name(d1_itr + used, info->name,
+ strlen(info->name));
+ used += wlp_set_manufacturer(d1_itr + used, info->manufacturer,
+ strlen(info->manufacturer));
+ used += wlp_set_model_name(d1_itr + used, info->model_name,
+ strlen(info->model_name));
+ used += wlp_set_model_nr(d1_itr + used, info->model_nr,
+ strlen(info->model_nr));
+ used += wlp_set_serial(d1_itr + used, info->serial,
+ strlen(info->serial));
+ used += wlp_set_prim_dev_type(d1_itr + used, &info->prim_dev_type);
+ used += wlp_set_wlp_assc_err(d1_itr + used, WLP_ASSOC_ERROR_NONE);
+ skb_put(_skb, sizeof(*_d1) + used);
+ d_printf(6, dev, "D1 message:\n");
+ d_dump(6, dev, _d1, sizeof(*_d1)
+ + sizeof(struct wlp_attr_uuid_e)
+ + sizeof(struct wlp_attr_wss_sel_mthd)
+ + sizeof(struct wlp_attr_dev_name)
+ + strlen(info->name)
+ + sizeof(struct wlp_attr_manufacturer)
+ + strlen(info->manufacturer)
+ + sizeof(struct wlp_attr_model_name)
+ + strlen(info->model_name)
+ + sizeof(struct wlp_attr_model_nr)
+ + strlen(info->model_nr)
+ + sizeof(struct wlp_attr_serial)
+ + strlen(info->serial)
+ + sizeof(struct wlp_attr_prim_dev_type)
+ + sizeof(struct wlp_attr_wlp_assc_err));
+ *skb = _skb;
+error:
+ d_fnend(6, dev, "wlp %p, result = %d\n", wlp, result);
+ return result;
+}
+
+/**
+ * Construct a D2 association frame
+ *
+ * We use the radio control functions to determine the values of the device
+ * properties. These are of variable length and the total space needed is
+ * tallied first before we start constructing the message. The radio
+ * control functions return strings that are terminated with \0. This
+ * character should not be included in the message (there is a length field
+ * accompanying it in the attribute).
+ */
+static
+int wlp_build_assoc_d2(struct wlp *wlp, struct wlp_wss *wss,
+ struct sk_buff **skb, struct wlp_uuid *uuid_e)
+{
+
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = 0;
+ struct wlp_device_info *info;
+ size_t used = 0;
+ struct wlp_frame_assoc *_d2;
+ struct sk_buff *_skb;
+ void *d2_itr;
+ size_t mem_needed;
+
+ d_fnstart(6, dev, "wlp %p\n", wlp);
+ if (wlp->dev_info == NULL) {
+ result = __wlp_setup_device_info(wlp);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to setup device "
+ "information for D2 message.\n");
+ goto error;
+ }
+ }
+ info = wlp->dev_info;
+ d_printf(6, dev, "Local properties:\n"
+ "Device name (%d bytes): %s\n"
+ "Model name (%d bytes): %s\n"
+ "Manufacturer (%d bytes): %s\n"
+ "Model number (%d bytes): %s\n"
+ "Serial number (%d bytes): %s\n"
+ "Primary device type: \n"
+ " Category: %d \n"
+ " OUI: %02x:%02x:%02x \n"
+ " OUI Subdivision: %u \n",
+ (int)strlen(info->name), info->name,
+ (int)strlen(info->model_name), info->model_name,
+ (int)strlen(info->manufacturer), info->manufacturer,
+ (int)strlen(info->model_nr), info->model_nr,
+ (int)strlen(info->serial), info->serial,
+ info->prim_dev_type.category,
+ info->prim_dev_type.OUI[0], info->prim_dev_type.OUI[1],
+ info->prim_dev_type.OUI[2], info->prim_dev_type.OUIsubdiv);
+ mem_needed = sizeof(*_d2)
+ + sizeof(struct wlp_attr_uuid_e)
+ + sizeof(struct wlp_attr_uuid_r)
+ + sizeof(struct wlp_attr_dev_name)
+ + strlen(info->name)
+ + sizeof(struct wlp_attr_manufacturer)
+ + strlen(info->manufacturer)
+ + sizeof(struct wlp_attr_model_name)
+ + strlen(info->model_name)
+ + sizeof(struct wlp_attr_model_nr)
+ + strlen(info->model_nr)
+ + sizeof(struct wlp_attr_serial)
+ + strlen(info->serial)
+ + sizeof(struct wlp_attr_prim_dev_type)
+ + sizeof(struct wlp_attr_wlp_assc_err);
+ if (wlp->wss.state >= WLP_WSS_STATE_ACTIVE)
+ mem_needed += sizeof(struct wlp_attr_wss_info)
+ + sizeof(struct wlp_wss_info)
+ + strlen(wlp->wss.name);
+ _skb = dev_alloc_skb(mem_needed);
+ if (_skb == NULL) {
+ dev_err(dev, "WLP: Cannot allocate memory for association "
+ "message.\n");
+ result = -ENOMEM;
+ goto error;
+ }
+ _d2 = (void *) _skb->data;
+ d_printf(6, dev, "D2 starts at %p \n", _d2);
+ _d2->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
+ _d2->hdr.type = WLP_FRAME_ASSOCIATION;
+ _d2->type = WLP_ASSOC_D2;
+
+ wlp_set_version(&_d2->version, WLP_VERSION);
+ wlp_set_msg_type(&_d2->msg_type, WLP_ASSOC_D2);
+ d2_itr = _d2->attr;
+ used = wlp_set_uuid_e(d2_itr, uuid_e);
+ used += wlp_set_uuid_r(d2_itr + used, &wlp->uuid);
+ if (wlp->wss.state >= WLP_WSS_STATE_ACTIVE)
+ used += wlp_set_wss_info(d2_itr + used, &wlp->wss);
+ used += wlp_set_dev_name(d2_itr + used, info->name,
+ strlen(info->name));
+ used += wlp_set_manufacturer(d2_itr + used, info->manufacturer,
+ strlen(info->manufacturer));
+ used += wlp_set_model_name(d2_itr + used, info->model_name,
+ strlen(info->model_name));
+ used += wlp_set_model_nr(d2_itr + used, info->model_nr,
+ strlen(info->model_nr));
+ used += wlp_set_serial(d2_itr + used, info->serial,
+ strlen(info->serial));
+ used += wlp_set_prim_dev_type(d2_itr + used, &info->prim_dev_type);
+ used += wlp_set_wlp_assc_err(d2_itr + used, WLP_ASSOC_ERROR_NONE);
+ skb_put(_skb, sizeof(*_d2) + used);
+ d_printf(6, dev, "D2 message:\n");
+ d_dump(6, dev, _d2, mem_needed);
+ *skb = _skb;
+error:
+ d_fnend(6, dev, "wlp %p, result = %d\n", wlp, result);
+ return result;
+}
+
+/**
+ * Allocate memory for and populate fields of F0 association frame
+ *
+ * Currently (while focusing on unsecure enrollment) we ignore the
+ * nonce's that could be placed in the message. Only the error field is
+ * populated by the value provided by the caller.
+ */
+static
+int wlp_build_assoc_f0(struct wlp *wlp, struct sk_buff **skb,
+ enum wlp_assc_error error)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = -ENOMEM;
+ struct {
+ struct wlp_frame_assoc f0_hdr;
+ struct wlp_attr_enonce enonce;
+ struct wlp_attr_rnonce rnonce;
+ struct wlp_attr_wlp_assc_err assc_err;
+ } *f0;
+ struct sk_buff *_skb;
+ struct wlp_nonce tmp;
+
+ d_fnstart(6, dev, "wlp %p\n", wlp);
+ _skb = dev_alloc_skb(sizeof(*f0));
+ if (_skb == NULL) {
+ dev_err(dev, "WLP: Unable to allocate memory for F0 "
+ "association frame. \n");
+ goto error_alloc;
+ }
+ f0 = (void *) _skb->data;
+ d_printf(6, dev, "F0 starts at %p \n", f0);
+ f0->f0_hdr.hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
+ f0->f0_hdr.hdr.type = WLP_FRAME_ASSOCIATION;
+ f0->f0_hdr.type = WLP_ASSOC_F0;
+ wlp_set_version(&f0->f0_hdr.version, WLP_VERSION);
+ wlp_set_msg_type(&f0->f0_hdr.msg_type, WLP_ASSOC_F0);
+ memset(&tmp, 0, sizeof(tmp));
+ wlp_set_enonce(&f0->enonce, &tmp);
+ wlp_set_rnonce(&f0->rnonce, &tmp);
+ wlp_set_wlp_assc_err(&f0->assc_err, error);
+ skb_put(_skb, sizeof(*f0));
+ *skb = _skb;
+ result = 0;
+error_alloc:
+ d_fnend(6, dev, "wlp %p, result %d \n", wlp, result);
+ return result;
+}
+
+/**
+ * Parse F0 frame
+ *
+ * We just retrieve the values and print it as an error to the user.
+ * Calling function already knows an error occured (F0 indicates error), so
+ * we just parse the content as debug for higher layers.
+ */
+int wlp_parse_f0(struct wlp *wlp, struct sk_buff *skb)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_frame_assoc *f0 = (void *) skb->data;
+ void *ptr = skb->data;
+ size_t len = skb->len;
+ size_t used;
+ ssize_t result;
+ struct wlp_nonce enonce, rnonce;
+ enum wlp_assc_error assc_err;
+ char enonce_buf[WLP_WSS_NONCE_STRSIZE];
+ char rnonce_buf[WLP_WSS_NONCE_STRSIZE];
+
+ used = sizeof(*f0);
+ result = wlp_get_enonce(wlp, ptr + used, &enonce, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain Enrollee nonce "
+ "attribute from F0 message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_rnonce(wlp, ptr + used, &rnonce, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain Registrar nonce "
+ "attribute from F0 message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_wlp_assc_err(wlp, ptr + used, &assc_err, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WLP Association error "
+ "attribute from F0 message.\n");
+ goto error_parse;
+ }
+ wlp_wss_nonce_print(enonce_buf, sizeof(enonce_buf), &enonce);
+ wlp_wss_nonce_print(rnonce_buf, sizeof(rnonce_buf), &rnonce);
+ dev_err(dev, "WLP: Received F0 error frame from neighbor. Enrollee "
+ "nonce: %s, Registrar nonce: %s, WLP Association error: %s.\n",
+ enonce_buf, rnonce_buf, wlp_assc_error_str(assc_err));
+ result = 0;
+error_parse:
+ return result;
+}
+
+/**
+ * Retrieve variable device information from association message
+ *
+ * The device information parsed is not required in any message. This
+ * routine will thus not fail if an attribute is not present.
+ * The attributes are expected in a certain order, even if all are not
+ * present. The "attribute type" value is used to ensure the attributes
+ * are parsed in the correct order.
+ *
+ * If an error is encountered during parsing the function will return an
+ * error code, when this happens the given device_info structure may be
+ * partially filled.
+ */
+static
+int wlp_get_variable_info(struct wlp *wlp, void *data,
+ struct wlp_device_info *dev_info, ssize_t len)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ size_t used = 0;
+ struct wlp_attr_hdr *hdr;
+ ssize_t result = 0;
+ unsigned last = 0;
+
+ while (len - used > 0) {
+ if (len - used < sizeof(*hdr)) {
+ dev_err(dev, "WLP: Partial data in frame, cannot "
+ "parse. \n");
+ goto error_parse;
+ }
+ hdr = data + used;
+ switch (le16_to_cpu(hdr->type)) {
+ case WLP_ATTR_MANUF:
+ if (last >= WLP_ATTR_MANUF) {
+ dev_err(dev, "WLP: Incorrect order of "
+ "attribute values in D1 msg.\n");
+ goto error_parse;
+ }
+ result = wlp_get_manufacturer(wlp, data + used,
+ dev_info->manufacturer,
+ len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to obtain "
+ "Manufacturer attribute from D1 "
+ "message.\n");
+ goto error_parse;
+ }
+ last = WLP_ATTR_MANUF;
+ used += result;
+ break;
+ case WLP_ATTR_MODEL_NAME:
+ if (last >= WLP_ATTR_MODEL_NAME) {
+ dev_err(dev, "WLP: Incorrect order of "
+ "attribute values in D1 msg.\n");
+ goto error_parse;
+ }
+ result = wlp_get_model_name(wlp, data + used,
+ dev_info->model_name,
+ len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to obtain Model "
+ "name attribute from D1 message.\n");
+ goto error_parse;
+ }
+ last = WLP_ATTR_MODEL_NAME;
+ used += result;
+ break;
+ case WLP_ATTR_MODEL_NR:
+ if (last >= WLP_ATTR_MODEL_NR) {
+ dev_err(dev, "WLP: Incorrect order of "
+ "attribute values in D1 msg.\n");
+ goto error_parse;
+ }
+ result = wlp_get_model_nr(wlp, data + used,
+ dev_info->model_nr,
+ len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to obtain Model "
+ "number attribute from D1 message.\n");
+ goto error_parse;
+ }
+ last = WLP_ATTR_MODEL_NR;
+ used += result;
+ break;
+ case WLP_ATTR_SERIAL:
+ if (last >= WLP_ATTR_SERIAL) {
+ dev_err(dev, "WLP: Incorrect order of "
+ "attribute values in D1 msg.\n");
+ goto error_parse;
+ }
+ result = wlp_get_serial(wlp, data + used,
+ dev_info->serial, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to obtain Serial "
+ "number attribute from D1 message.\n");
+ goto error_parse;
+ }
+ last = WLP_ATTR_SERIAL;
+ used += result;
+ break;
+ case WLP_ATTR_PRI_DEV_TYPE:
+ if (last >= WLP_ATTR_PRI_DEV_TYPE) {
+ dev_err(dev, "WLP: Incorrect order of "
+ "attribute values in D1 msg.\n");
+ goto error_parse;
+ }
+ result = wlp_get_prim_dev_type(wlp, data + used,
+ &dev_info->prim_dev_type,
+ len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to obtain Primary "
+ "device type attribute from D1 "
+ "message.\n");
+ goto error_parse;
+ }
+ dev_info->prim_dev_type.category =
+ le16_to_cpu(dev_info->prim_dev_type.category);
+ dev_info->prim_dev_type.subID =
+ le16_to_cpu(dev_info->prim_dev_type.subID);
+ last = WLP_ATTR_PRI_DEV_TYPE;
+ used += result;
+ break;
+ default:
+ /* This is not variable device information. */
+ goto out;
+ break;
+ }
+ }
+out:
+ return used;
+error_parse:
+ return -EINVAL;
+}
+
+/**
+ * Parse incoming D1 frame, populate attribute values
+ *
+ * Caller provides pointers to memory already allocated for attributes
+ * expected in the D1 frame. These variables will be populated.
+ */
+static
+int wlp_parse_d1_frame(struct wlp *wlp, struct sk_buff *skb,
+ struct wlp_uuid *uuid_e,
+ enum wlp_wss_sel_mthd *sel_mthd,
+ struct wlp_device_info *dev_info,
+ enum wlp_assc_error *assc_err)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_frame_assoc *d1 = (void *) skb->data;
+ void *ptr = skb->data;
+ size_t len = skb->len;
+ size_t used;
+ ssize_t result;
+
+ used = sizeof(*d1);
+ result = wlp_get_uuid_e(wlp, ptr + used, uuid_e, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain UUID-E attribute from D1 "
+ "message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_wss_sel_mthd(wlp, ptr + used, sel_mthd, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSS selection method "
+ "from D1 message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_dev_name(wlp, ptr + used, dev_info->name,
+ len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain Device Name from D1 "
+ "message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_variable_info(wlp, ptr + used, dev_info, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain Device Information from "
+ "D1 message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_wlp_assc_err(wlp, ptr + used, assc_err, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WLP Association Error "
+ "Information from D1 message.\n");
+ goto error_parse;
+ }
+ result = 0;
+error_parse:
+ return result;
+}
+/**
+ * Handle incoming D1 frame
+ *
+ * The frame has already been verified to contain an Association header with
+ * the correct version number. Parse the incoming frame, construct and send
+ * a D2 frame in response.
+ *
+ * It is not clear what to do with most fields in the incoming D1 frame. We
+ * retrieve and discard the information here for now.
+ */
+void wlp_handle_d1_frame(struct work_struct *ws)
+{
+ struct wlp_assoc_frame_ctx *frame_ctx = container_of(ws,
+ struct wlp_assoc_frame_ctx,
+ ws);
+ struct wlp *wlp = frame_ctx->wlp;
+ struct wlp_wss *wss = &wlp->wss;
+ struct sk_buff *skb = frame_ctx->skb;
+ struct uwb_dev_addr *src = &frame_ctx->src;
+ int result;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_uuid uuid_e;
+ enum wlp_wss_sel_mthd sel_mthd = 0;
+ struct wlp_device_info dev_info;
+ enum wlp_assc_error assc_err;
+ char uuid[WLP_WSS_UUID_STRSIZE];
+ struct sk_buff *resp = NULL;
+
+ /* Parse D1 frame */
+ d_fnstart(6, dev, "WLP: handle D1 frame. wlp = %p, skb = %p\n",
+ wlp, skb);
+ mutex_lock(&wss->mutex);
+ mutex_lock(&wlp->mutex); /* to access wlp->uuid */
+ memset(&dev_info, 0, sizeof(dev_info));
+ result = wlp_parse_d1_frame(wlp, skb, &uuid_e, &sel_mthd, &dev_info,
+ &assc_err);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to parse incoming D1 frame.\n");
+ kfree_skb(skb);
+ goto out;
+ }
+ wlp_wss_uuid_print(uuid, sizeof(uuid), &uuid_e);
+ d_printf(6, dev, "From D1 frame:\n"
+ "UUID-E: %s\n"
+ "Selection method: %d\n"
+ "Device name (%d bytes): %s\n"
+ "Model name (%d bytes): %s\n"
+ "Manufacturer (%d bytes): %s\n"
+ "Model number (%d bytes): %s\n"
+ "Serial number (%d bytes): %s\n"
+ "Primary device type: \n"
+ " Category: %d \n"
+ " OUI: %02x:%02x:%02x \n"
+ " OUI Subdivision: %u \n",
+ uuid, sel_mthd,
+ (int)strlen(dev_info.name), dev_info.name,
+ (int)strlen(dev_info.model_name), dev_info.model_name,
+ (int)strlen(dev_info.manufacturer), dev_info.manufacturer,
+ (int)strlen(dev_info.model_nr), dev_info.model_nr,
+ (int)strlen(dev_info.serial), dev_info.serial,
+ dev_info.prim_dev_type.category,
+ dev_info.prim_dev_type.OUI[0],
+ dev_info.prim_dev_type.OUI[1],
+ dev_info.prim_dev_type.OUI[2],
+ dev_info.prim_dev_type.OUIsubdiv);
+
+ kfree_skb(skb);
+ if (!wlp_uuid_is_set(&wlp->uuid)) {
+ dev_err(dev, "WLP: UUID is not set. Set via sysfs to "
+ "proceed. Respong to D1 message with error F0.\n");
+ result = wlp_build_assoc_f0(wlp, &resp,
+ WLP_ASSOC_ERROR_NOT_READY);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to construct F0 message.\n");
+ goto out;
+ }
+ } else {
+ /* Construct D2 frame */
+ result = wlp_build_assoc_d2(wlp, wss, &resp, &uuid_e);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to construct D2 message.\n");
+ goto out;
+ }
+ }
+ /* Send D2 frame */
+ BUG_ON(wlp->xmit_frame == NULL);
+ result = wlp->xmit_frame(wlp, resp, src);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to transmit D2 association "
+ "message: %d\n", result);
+ if (result == -ENXIO)
+ dev_err(dev, "WLP: Is network interface up? \n");
+ /* We could try again ... */
+ dev_kfree_skb_any(resp); /* we need to free if tx fails */
+ }
+out:
+ kfree(frame_ctx);
+ mutex_unlock(&wlp->mutex);
+ mutex_unlock(&wss->mutex);
+ d_fnend(6, dev, "WLP: handle D1 frame. wlp = %p\n", wlp);
+}
+
+/**
+ * Parse incoming D2 frame, create and populate temporary cache
+ *
+ * @skb: socket buffer in which D2 frame can be found
+ * @neighbor: the neighbor that sent the D2 frame
+ *
+ * Will allocate memory for temporary storage of information learned during
+ * discovery.
+ */
+int wlp_parse_d2_frame_to_cache(struct wlp *wlp, struct sk_buff *skb,
+ struct wlp_neighbor_e *neighbor)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_frame_assoc *d2 = (void *) skb->data;
+ void *ptr = skb->data;
+ size_t len = skb->len;
+ size_t used;
+ ssize_t result;
+ struct wlp_uuid uuid_e;
+ struct wlp_device_info *nb_info;
+ enum wlp_assc_error assc_err;
+
+ used = sizeof(*d2);
+ result = wlp_get_uuid_e(wlp, ptr + used, &uuid_e, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain UUID-E attribute from D2 "
+ "message.\n");
+ goto error_parse;
+ }
+ if (memcmp(&uuid_e, &wlp->uuid, sizeof(uuid_e))) {
+ dev_err(dev, "WLP: UUID-E in incoming D2 does not match "
+ "local UUID sent in D1. \n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_uuid_r(wlp, ptr + used, &neighbor->uuid, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain UUID-R attribute from D2 "
+ "message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_wss_info_to_cache(wlp, ptr + used, neighbor,
+ len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSS information "
+ "from D2 message.\n");
+ goto error_parse;
+ }
+ used += result;
+ neighbor->info = kzalloc(sizeof(struct wlp_device_info), GFP_KERNEL);
+ if (neighbor->info == NULL) {
+ dev_err(dev, "WLP: cannot allocate memory to store device "
+ "info.\n");
+ result = -ENOMEM;
+ goto error_parse;
+ }
+ nb_info = neighbor->info;
+ result = wlp_get_dev_name(wlp, ptr + used, nb_info->name,
+ len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain Device Name from D2 "
+ "message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_variable_info(wlp, ptr + used, nb_info, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain Device Information from "
+ "D2 message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_wlp_assc_err(wlp, ptr + used, &assc_err, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WLP Association Error "
+ "Information from D2 message.\n");
+ goto error_parse;
+ }
+ if (assc_err != WLP_ASSOC_ERROR_NONE) {
+ dev_err(dev, "WLP: neighbor device returned association "
+ "error %d\n", assc_err);
+ result = -EINVAL;
+ goto error_parse;
+ }
+ result = 0;
+error_parse:
+ if (result < 0)
+ wlp_remove_neighbor_tmp_info(neighbor);
+ return result;
+}
+
+/**
+ * Parse incoming D2 frame, populate attribute values of WSS bein enrolled in
+ *
+ * @wss: our WSS that will be enrolled
+ * @skb: socket buffer in which D2 frame can be found
+ * @neighbor: the neighbor that sent the D2 frame
+ * @wssid: the wssid of the WSS in which we want to enroll
+ *
+ * Forms part of enrollment sequence. We are trying to enroll in WSS with
+ * @wssid by using @neighbor as registrar. A D1 message was sent to
+ * @neighbor and now we need to parse the D2 response. The neighbor's
+ * response is searched for the requested WSS and if found (and it accepts
+ * enrollment), we store the information.
+ */
+int wlp_parse_d2_frame_to_enroll(struct wlp_wss *wss, struct sk_buff *skb,
+ struct wlp_neighbor_e *neighbor,
+ struct wlp_uuid *wssid)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ void *ptr = skb->data;
+ size_t len = skb->len;
+ size_t used;
+ ssize_t result;
+ struct wlp_uuid uuid_e;
+ struct wlp_uuid uuid_r;
+ struct wlp_device_info nb_info;
+ enum wlp_assc_error assc_err;
+ char uuid_bufA[WLP_WSS_UUID_STRSIZE];
+ char uuid_bufB[WLP_WSS_UUID_STRSIZE];
+
+ used = sizeof(struct wlp_frame_assoc);
+ result = wlp_get_uuid_e(wlp, ptr + used, &uuid_e, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain UUID-E attribute from D2 "
+ "message.\n");
+ goto error_parse;
+ }
+ if (memcmp(&uuid_e, &wlp->uuid, sizeof(uuid_e))) {
+ dev_err(dev, "WLP: UUID-E in incoming D2 does not match "
+ "local UUID sent in D1. \n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_uuid_r(wlp, ptr + used, &uuid_r, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain UUID-R attribute from D2 "
+ "message.\n");
+ goto error_parse;
+ }
+ if (memcmp(&uuid_r, &neighbor->uuid, sizeof(uuid_r))) {
+ wlp_wss_uuid_print(uuid_bufA, sizeof(uuid_bufA),
+ &neighbor->uuid);
+ wlp_wss_uuid_print(uuid_bufB, sizeof(uuid_bufB), &uuid_r);
+ dev_err(dev, "WLP: UUID of neighbor does not match UUID "
+ "learned during discovery. Originally discovered: %s, "
+ "now from D2 message: %s\n", uuid_bufA, uuid_bufB);
+ result = -EINVAL;
+ goto error_parse;
+ }
+ used += result;
+ wss->wssid = *wssid;
+ result = wlp_get_wss_info_to_enroll(wlp, ptr + used, wss, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSS information "
+ "from D2 message.\n");
+ goto error_parse;
+ }
+ if (wss->state != WLP_WSS_STATE_PART_ENROLLED) {
+ dev_err(dev, "WLP: D2 message did not contain information "
+ "for successful enrollment. \n");
+ result = -EINVAL;
+ goto error_parse;
+ }
+ used += result;
+ /* Place device information on stack to continue parsing of message */
+ result = wlp_get_dev_name(wlp, ptr + used, nb_info.name,
+ len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain Device Name from D2 "
+ "message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_variable_info(wlp, ptr + used, &nb_info, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain Device Information from "
+ "D2 message.\n");
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_wlp_assc_err(wlp, ptr + used, &assc_err, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WLP Association Error "
+ "Information from D2 message.\n");
+ goto error_parse;
+ }
+ if (assc_err != WLP_ASSOC_ERROR_NONE) {
+ dev_err(dev, "WLP: neighbor device returned association "
+ "error %d\n", assc_err);
+ if (wss->state == WLP_WSS_STATE_PART_ENROLLED) {
+ dev_err(dev, "WLP: Enrolled in WSS (should not "
+ "happen according to spec). Undoing. \n");
+ wlp_wss_reset(wss);
+ }
+ result = -EINVAL;
+ goto error_parse;
+ }
+ result = 0;
+error_parse:
+ return result;
+}
+
+/**
+ * Parse C3/C4 frame into provided variables
+ *
+ * @wssid: will point to copy of wssid retrieved from C3/C4 frame
+ * @tag: will point to copy of tag retrieved from C3/C4 frame
+ * @virt_addr: will point to copy of virtual address retrieved from C3/C4
+ * frame.
+ *
+ * Calling function has to allocate memory for these values.
+ *
+ * skb contains a valid C3/C4 frame, return the individual fields of this
+ * frame in the provided variables.
+ */
+int wlp_parse_c3c4_frame(struct wlp *wlp, struct sk_buff *skb,
+ struct wlp_uuid *wssid, u8 *tag,
+ struct uwb_mac_addr *virt_addr)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result;
+ void *ptr = skb->data;
+ size_t len = skb->len;
+ size_t used;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ struct wlp_frame_assoc *assoc = ptr;
+
+ d_fnstart(6, dev, "wlp %p, skb %p \n", wlp, skb);
+ used = sizeof(*assoc);
+ result = wlp_get_wssid(wlp, ptr + used, wssid, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSSID attribute from "
+ "%s message.\n", wlp_assoc_frame_str(assoc->type));
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_wss_tag(wlp, ptr + used, tag, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSS tag attribute from "
+ "%s message.\n", wlp_assoc_frame_str(assoc->type));
+ goto error_parse;
+ }
+ used += result;
+ result = wlp_get_wss_virt(wlp, ptr + used, virt_addr, len - used);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSS virtual address "
+ "attribute from %s message.\n",
+ wlp_assoc_frame_str(assoc->type));
+ goto error_parse;
+ }
+ wlp_wss_uuid_print(buf, sizeof(buf), wssid);
+ d_printf(6, dev, "WLP: parsed: WSSID %s, tag 0x%02x, virt "
+ "%02x:%02x:%02x:%02x:%02x:%02x \n", buf, *tag,
+ virt_addr->data[0], virt_addr->data[1], virt_addr->data[2],
+ virt_addr->data[3], virt_addr->data[4], virt_addr->data[5]);
+
+error_parse:
+ d_fnend(6, dev, "wlp %p, skb %p, result = %d \n", wlp, skb, result);
+ return result;
+}
+
+/**
+ * Allocate memory for and populate fields of C1 or C2 association frame
+ *
+ * The C1 and C2 association frames appear identical - except for the type.
+ */
+static
+int wlp_build_assoc_c1c2(struct wlp *wlp, struct wlp_wss *wss,
+ struct sk_buff **skb, enum wlp_assoc_type type)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = -ENOMEM;
+ struct {
+ struct wlp_frame_assoc c_hdr;
+ struct wlp_attr_wssid wssid;
+ } *c;
+ struct sk_buff *_skb;
+
+ d_fnstart(6, dev, "wlp %p, wss %p \n", wlp, wss);
+ _skb = dev_alloc_skb(sizeof(*c));
+ if (_skb == NULL) {
+ dev_err(dev, "WLP: Unable to allocate memory for C1/C2 "
+ "association frame. \n");
+ goto error_alloc;
+ }
+ c = (void *) _skb->data;
+ d_printf(6, dev, "C1/C2 starts at %p \n", c);
+ c->c_hdr.hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
+ c->c_hdr.hdr.type = WLP_FRAME_ASSOCIATION;
+ c->c_hdr.type = type;
+ wlp_set_version(&c->c_hdr.version, WLP_VERSION);
+ wlp_set_msg_type(&c->c_hdr.msg_type, type);
+ wlp_set_wssid(&c->wssid, &wss->wssid);
+ skb_put(_skb, sizeof(*c));
+ d_printf(6, dev, "C1/C2 message:\n");
+ d_dump(6, dev, c, sizeof(*c));
+ *skb = _skb;
+ result = 0;
+error_alloc:
+ d_fnend(6, dev, "wlp %p, wss %p, result %d \n", wlp, wss, result);
+ return result;
+}
+
+
+static
+int wlp_build_assoc_c1(struct wlp *wlp, struct wlp_wss *wss,
+ struct sk_buff **skb)
+{
+ return wlp_build_assoc_c1c2(wlp, wss, skb, WLP_ASSOC_C1);
+}
+
+static
+int wlp_build_assoc_c2(struct wlp *wlp, struct wlp_wss *wss,
+ struct sk_buff **skb)
+{
+ return wlp_build_assoc_c1c2(wlp, wss, skb, WLP_ASSOC_C2);
+}
+
+
+/**
+ * Allocate memory for and populate fields of C3 or C4 association frame
+ *
+ * The C3 and C4 association frames appear identical - except for the type.
+ */
+static
+int wlp_build_assoc_c3c4(struct wlp *wlp, struct wlp_wss *wss,
+ struct sk_buff **skb, enum wlp_assoc_type type)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = -ENOMEM;
+ struct {
+ struct wlp_frame_assoc c_hdr;
+ struct wlp_attr_wssid wssid;
+ struct wlp_attr_wss_tag wss_tag;
+ struct wlp_attr_wss_virt wss_virt;
+ } *c;
+ struct sk_buff *_skb;
+
+ d_fnstart(6, dev, "wlp %p, wss %p \n", wlp, wss);
+ _skb = dev_alloc_skb(sizeof(*c));
+ if (_skb == NULL) {
+ dev_err(dev, "WLP: Unable to allocate memory for C3/C4 "
+ "association frame. \n");
+ goto error_alloc;
+ }
+ c = (void *) _skb->data;
+ d_printf(6, dev, "C3/C4 starts at %p \n", c);
+ c->c_hdr.hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
+ c->c_hdr.hdr.type = WLP_FRAME_ASSOCIATION;
+ c->c_hdr.type = type;
+ wlp_set_version(&c->c_hdr.version, WLP_VERSION);
+ wlp_set_msg_type(&c->c_hdr.msg_type, type);
+ wlp_set_wssid(&c->wssid, &wss->wssid);
+ wlp_set_wss_tag(&c->wss_tag, wss->tag);
+ wlp_set_wss_virt(&c->wss_virt, &wss->virtual_addr);
+ skb_put(_skb, sizeof(*c));
+ d_printf(6, dev, "C3/C4 message:\n");
+ d_dump(6, dev, c, sizeof(*c));
+ *skb = _skb;
+ result = 0;
+error_alloc:
+ d_fnend(6, dev, "wlp %p, wss %p, result %d \n", wlp, wss, result);
+ return result;
+}
+
+static
+int wlp_build_assoc_c3(struct wlp *wlp, struct wlp_wss *wss,
+ struct sk_buff **skb)
+{
+ return wlp_build_assoc_c3c4(wlp, wss, skb, WLP_ASSOC_C3);
+}
+
+static
+int wlp_build_assoc_c4(struct wlp *wlp, struct wlp_wss *wss,
+ struct sk_buff **skb)
+{
+ return wlp_build_assoc_c3c4(wlp, wss, skb, WLP_ASSOC_C4);
+}
+
+
+#define wlp_send_assoc(type, id) \
+static int wlp_send_assoc_##type(struct wlp *wlp, struct wlp_wss *wss, \
+ struct uwb_dev_addr *dev_addr) \
+{ \
+ struct device *dev = &wlp->rc->uwb_dev.dev; \
+ int result; \
+ struct sk_buff *skb = NULL; \
+ d_fnstart(6, dev, "wlp %p, wss %p, neighbor: %02x:%02x\n", \
+ wlp, wss, dev_addr->data[1], dev_addr->data[0]); \
+ d_printf(6, dev, "WLP: Constructing %s frame. \n", \
+ wlp_assoc_frame_str(id)); \
+ /* Build the frame */ \
+ result = wlp_build_assoc_##type(wlp, wss, &skb); \
+ if (result < 0) { \
+ dev_err(dev, "WLP: Unable to construct %s association " \
+ "frame: %d\n", wlp_assoc_frame_str(id), result);\
+ goto error_build_assoc; \
+ } \
+ /* Send the frame */ \
+ d_printf(6, dev, "Transmitting %s frame to %02x:%02x \n", \
+ wlp_assoc_frame_str(id), \
+ dev_addr->data[1], dev_addr->data[0]); \
+ BUG_ON(wlp->xmit_frame == NULL); \
+ result = wlp->xmit_frame(wlp, skb, dev_addr); \
+ if (result < 0) { \
+ dev_err(dev, "WLP: Unable to transmit %s association " \
+ "message: %d\n", wlp_assoc_frame_str(id), \
+ result); \
+ if (result == -ENXIO) \
+ dev_err(dev, "WLP: Is network interface " \
+ "up? \n"); \
+ goto error_xmit; \
+ } \
+ return 0; \
+error_xmit: \
+ /* We could try again ... */ \
+ dev_kfree_skb_any(skb);/*we need to free if tx fails*/ \
+error_build_assoc: \
+ d_fnend(6, dev, "wlp %p, wss %p, neighbor: %02x:%02x\n", \
+ wlp, wss, dev_addr->data[1], dev_addr->data[0]); \
+ return result; \
+}
+
+wlp_send_assoc(d1, WLP_ASSOC_D1)
+wlp_send_assoc(c1, WLP_ASSOC_C1)
+wlp_send_assoc(c3, WLP_ASSOC_C3)
+
+int wlp_send_assoc_frame(struct wlp *wlp, struct wlp_wss *wss,
+ struct uwb_dev_addr *dev_addr,
+ enum wlp_assoc_type type)
+{
+ int result = 0;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ switch (type) {
+ case WLP_ASSOC_D1:
+ result = wlp_send_assoc_d1(wlp, wss, dev_addr);
+ break;
+ case WLP_ASSOC_C1:
+ result = wlp_send_assoc_c1(wlp, wss, dev_addr);
+ break;
+ case WLP_ASSOC_C3:
+ result = wlp_send_assoc_c3(wlp, wss, dev_addr);
+ break;
+ default:
+ dev_err(dev, "WLP: Received request to send unknown "
+ "association message.\n");
+ result = -EINVAL;
+ break;
+ }
+ return result;
+}
+
+/**
+ * Handle incoming C1 frame
+ *
+ * The frame has already been verified to contain an Association header with
+ * the correct version number. Parse the incoming frame, construct and send
+ * a C2 frame in response.
+ */
+void wlp_handle_c1_frame(struct work_struct *ws)
+{
+ struct wlp_assoc_frame_ctx *frame_ctx = container_of(ws,
+ struct wlp_assoc_frame_ctx,
+ ws);
+ struct wlp *wlp = frame_ctx->wlp;
+ struct wlp_wss *wss = &wlp->wss;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_frame_assoc *c1 = (void *) frame_ctx->skb->data;
+ unsigned int len = frame_ctx->skb->len;
+ struct uwb_dev_addr *src = &frame_ctx->src;
+ int result;
+ struct wlp_uuid wssid;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ struct sk_buff *resp = NULL;
+
+ /* Parse C1 frame */
+ d_fnstart(6, dev, "WLP: handle C1 frame. wlp = %p, c1 = %p\n",
+ wlp, c1);
+ mutex_lock(&wss->mutex);
+ result = wlp_get_wssid(wlp, (void *)c1 + sizeof(*c1), &wssid,
+ len - sizeof(*c1));
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSSID from C1 frame.\n");
+ goto out;
+ }
+ wlp_wss_uuid_print(buf, sizeof(buf), &wssid);
+ d_printf(6, dev, "Received C1 frame with WSSID %s \n", buf);
+ if (!memcmp(&wssid, &wss->wssid, sizeof(wssid))
+ && wss->state == WLP_WSS_STATE_ACTIVE) {
+ d_printf(6, dev, "WSSID from C1 frame is known locally "
+ "and is active\n");
+ /* Construct C2 frame */
+ result = wlp_build_assoc_c2(wlp, wss, &resp);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to construct C2 message.\n");
+ goto out;
+ }
+ } else {
+ d_printf(6, dev, "WSSID from C1 frame is not known locally "
+ "or is not active\n");
+ /* Construct F0 frame */
+ result = wlp_build_assoc_f0(wlp, &resp, WLP_ASSOC_ERROR_INV);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to construct F0 message.\n");
+ goto out;
+ }
+ }
+ /* Send C2 frame */
+ d_printf(6, dev, "Transmitting response (C2/F0) frame to %02x:%02x \n",
+ src->data[1], src->data[0]);
+ BUG_ON(wlp->xmit_frame == NULL);
+ result = wlp->xmit_frame(wlp, resp, src);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to transmit response association "
+ "message: %d\n", result);
+ if (result == -ENXIO)
+ dev_err(dev, "WLP: Is network interface up? \n");
+ /* We could try again ... */
+ dev_kfree_skb_any(resp); /* we need to free if tx fails */
+ }
+out:
+ kfree_skb(frame_ctx->skb);
+ kfree(frame_ctx);
+ mutex_unlock(&wss->mutex);
+ d_fnend(6, dev, "WLP: handle C1 frame. wlp = %p\n", wlp);
+}
+
+/**
+ * Handle incoming C3 frame
+ *
+ * The frame has already been verified to contain an Association header with
+ * the correct version number. Parse the incoming frame, construct and send
+ * a C4 frame in response. If the C3 frame identifies a WSS that is locally
+ * active then we connect to this neighbor (add it to our EDA cache).
+ */
+void wlp_handle_c3_frame(struct work_struct *ws)
+{
+ struct wlp_assoc_frame_ctx *frame_ctx = container_of(ws,
+ struct wlp_assoc_frame_ctx,
+ ws);
+ struct wlp *wlp = frame_ctx->wlp;
+ struct wlp_wss *wss = &wlp->wss;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct sk_buff *skb = frame_ctx->skb;
+ struct uwb_dev_addr *src = &frame_ctx->src;
+ int result;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ struct sk_buff *resp = NULL;
+ struct wlp_uuid wssid;
+ u8 tag;
+ struct uwb_mac_addr virt_addr;
+
+ /* Parse C3 frame */
+ d_fnstart(6, dev, "WLP: handle C3 frame. wlp = %p, skb = %p\n",
+ wlp, skb);
+ mutex_lock(&wss->mutex);
+ result = wlp_parse_c3c4_frame(wlp, skb, &wssid, &tag, &virt_addr);
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain values from C3 frame.\n");
+ goto out;
+ }
+ wlp_wss_uuid_print(buf, sizeof(buf), &wssid);
+ d_printf(6, dev, "Received C3 frame with WSSID %s \n", buf);
+ if (!memcmp(&wssid, &wss->wssid, sizeof(wssid))
+ && wss->state >= WLP_WSS_STATE_ACTIVE) {
+ d_printf(6, dev, "WSSID from C3 frame is known locally "
+ "and is active\n");
+ result = wlp_eda_update_node(&wlp->eda, src, wss,
+ (void *) virt_addr.data, tag,
+ WLP_WSS_CONNECTED);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to update EDA cache "
+ "with new connected neighbor information.\n");
+ result = wlp_build_assoc_f0(wlp, &resp,
+ WLP_ASSOC_ERROR_INT);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to construct F0 "
+ "message.\n");
+ goto out;
+ }
+ } else {
+ wss->state = WLP_WSS_STATE_CONNECTED;
+ /* Construct C4 frame */
+ result = wlp_build_assoc_c4(wlp, wss, &resp);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to construct C4 "
+ "message.\n");
+ goto out;
+ }
+ }
+ } else {
+ d_printf(6, dev, "WSSID from C3 frame is not known locally "
+ "or is not active\n");
+ /* Construct F0 frame */
+ result = wlp_build_assoc_f0(wlp, &resp, WLP_ASSOC_ERROR_INV);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to construct F0 message.\n");
+ goto out;
+ }
+ }
+ /* Send C4 frame */
+ d_printf(6, dev, "Transmitting response (C4/F0) frame to %02x:%02x \n",
+ src->data[1], src->data[0]);
+ BUG_ON(wlp->xmit_frame == NULL);
+ result = wlp->xmit_frame(wlp, resp, src);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to transmit response association "
+ "message: %d\n", result);
+ if (result == -ENXIO)
+ dev_err(dev, "WLP: Is network interface up? \n");
+ /* We could try again ... */
+ dev_kfree_skb_any(resp); /* we need to free if tx fails */
+ }
+out:
+ kfree_skb(frame_ctx->skb);
+ kfree(frame_ctx);
+ mutex_unlock(&wss->mutex);
+ d_fnend(6, dev, "WLP: handle C3 frame. wlp = %p, skb = %p\n",
+ wlp, skb);
+}
+
+
diff --git a/drivers/uwb/wlp/sysfs.c b/drivers/uwb/wlp/sysfs.c
new file mode 100644
index 000000000000..cfbdfdddb74a
--- /dev/null
+++ b/drivers/uwb/wlp/sysfs.c
@@ -0,0 +1,715 @@
+/*
+ * WiMedia Logical Link Control Protocol (WLP)
+ * sysfs functions
+ *
+ * Copyright (C) 2007 Intel Corporation
+ * Reinette Chatre <reinette.chatre@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: Docs
+ *
+ */
+
+#include <linux/wlp.h>
+#define D_LOCAL 6
+#include <linux/uwb/debug.h>
+#include "wlp-internal.h"
+
+static
+size_t wlp_wss_wssid_e_print(char *buf, size_t bufsize,
+ struct wlp_wssid_e *wssid_e)
+{
+ size_t used = 0;
+ used += scnprintf(buf, bufsize, " WSS: ");
+ used += wlp_wss_uuid_print(buf + used, bufsize - used,
+ &wssid_e->wssid);
+
+ if (wssid_e->info != NULL) {
+ used += scnprintf(buf + used, bufsize - used, " ");
+ used += uwb_mac_addr_print(buf + used, bufsize - used,
+ &wssid_e->info->bcast);
+ used += scnprintf(buf + used, bufsize - used, " %u %u %s\n",
+ wssid_e->info->accept_enroll,
+ wssid_e->info->sec_status,
+ wssid_e->info->name);
+ }
+ return used;
+}
+
+/**
+ * Print out information learned from neighbor discovery
+ *
+ * Some fields being printed may not be included in the device discovery
+ * information (it is not mandatory). We are thus careful how the
+ * information is printed to ensure it is clear to the user what field is
+ * being referenced.
+ * The information being printed is for one time use - temporary storage is
+ * cleaned after it is printed.
+ *
+ * Ideally sysfs output should be on one line. The information printed here
+ * contain a few strings so it will be hard to parse if they are all
+ * printed on the same line - without agreeing on a standard field
+ * separator.
+ */
+static
+ssize_t wlp_wss_neighborhood_print_remove(struct wlp *wlp, char *buf,
+ size_t bufsize)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ size_t used = 0;
+ struct wlp_neighbor_e *neighb;
+ struct wlp_wssid_e *wssid_e;
+ d_fnstart(6, dev, "wlp %p\n", wlp);
+ mutex_lock(&wlp->nbmutex);
+ used = scnprintf(buf, bufsize, "#Neighbor information\n"
+ "#uuid dev_addr\n"
+ "# Device Name:\n# Model Name:\n# Manufacturer:\n"
+ "# Model Nr:\n# Serial:\n"
+ "# Pri Dev type: CategoryID OUI OUISubdiv "
+ "SubcategoryID\n"
+ "# WSS: WSSID WSS_name accept_enroll sec_status "
+ "bcast\n"
+ "# WSS: WSSID WSS_name accept_enroll sec_status "
+ "bcast\n\n");
+ list_for_each_entry(neighb, &wlp->neighbors, node) {
+ if (bufsize - used <= 0)
+ goto out;
+ used += wlp_wss_uuid_print(buf + used, bufsize - used,
+ &neighb->uuid);
+ buf[used++] = ' ';
+ used += uwb_dev_addr_print(buf + used, bufsize - used,
+ &neighb->uwb_dev->dev_addr);
+ if (neighb->info != NULL)
+ used += scnprintf(buf + used, bufsize - used,
+ "\n Device Name: %s\n"
+ " Model Name: %s\n"
+ " Manufacturer:%s \n"
+ " Model Nr: %s\n"
+ " Serial: %s\n"
+ " Pri Dev type: "
+ "%u %02x:%02x:%02x %u %u\n",
+ neighb->info->name,
+ neighb->info->model_name,
+ neighb->info->manufacturer,
+ neighb->info->model_nr,
+ neighb->info->serial,
+ neighb->info->prim_dev_type.category,
+ neighb->info->prim_dev_type.OUI[0],
+ neighb->info->prim_dev_type.OUI[1],
+ neighb->info->prim_dev_type.OUI[2],
+ neighb->info->prim_dev_type.OUIsubdiv,
+ neighb->info->prim_dev_type.subID);
+ list_for_each_entry(wssid_e, &neighb->wssid, node) {
+ used += wlp_wss_wssid_e_print(buf + used,
+ bufsize - used,
+ wssid_e);
+ }
+ buf[used++] = '\n';
+ wlp_remove_neighbor_tmp_info(neighb);
+ }
+
+
+out:
+ mutex_unlock(&wlp->nbmutex);
+ d_fnend(6, dev, "wlp %p\n", wlp);
+ return used;
+}
+
+
+/**
+ * Show properties of all WSS in neighborhood.
+ *
+ * Will trigger a complete discovery of WSS activated by this device and
+ * its neighbors.
+ */
+ssize_t wlp_neighborhood_show(struct wlp *wlp, char *buf)
+{
+ wlp_discover(wlp);
+ return wlp_wss_neighborhood_print_remove(wlp, buf, PAGE_SIZE);
+}
+EXPORT_SYMBOL_GPL(wlp_neighborhood_show);
+
+static
+ssize_t __wlp_wss_properties_show(struct wlp_wss *wss, char *buf,
+ size_t bufsize)
+{
+ ssize_t result;
+
+ result = wlp_wss_uuid_print(buf, bufsize, &wss->wssid);
+ result += scnprintf(buf + result, bufsize - result, " ");
+ result += uwb_mac_addr_print(buf + result, bufsize - result,
+ &wss->bcast);
+ result += scnprintf(buf + result, bufsize - result,
+ " 0x%02x %u ", wss->hash, wss->secure_status);
+ result += wlp_wss_key_print(buf + result, bufsize - result,
+ wss->master_key);
+ result += scnprintf(buf + result, bufsize - result, " 0x%02x ",
+ wss->tag);
+ result += uwb_mac_addr_print(buf + result, bufsize - result,
+ &wss->virtual_addr);
+ result += scnprintf(buf + result, bufsize - result, " %s", wss->name);
+ result += scnprintf(buf + result, bufsize - result,
+ "\n\n#WSSID\n#WSS broadcast address\n"
+ "#WSS hash\n#WSS secure status\n"
+ "#WSS master key\n#WSS local tag\n"
+ "#WSS local virtual EUI-48\n#WSS name\n");
+ return result;
+}
+
+/**
+ * Show which WSS is activated.
+ */
+ssize_t wlp_wss_activate_show(struct wlp_wss *wss, char *buf)
+{
+ int result = 0;
+
+ if (mutex_lock_interruptible(&wss->mutex))
+ goto out;
+ if (wss->state >= WLP_WSS_STATE_ACTIVE)
+ result = __wlp_wss_properties_show(wss, buf, PAGE_SIZE);
+ else
+ result = scnprintf(buf, PAGE_SIZE, "No local WSS active.\n");
+ result += scnprintf(buf + result, PAGE_SIZE - result,
+ "\n\n"
+ "# echo WSSID SECURE_STATUS ACCEPT_ENROLLMENT "
+ "NAME #create new WSS\n"
+ "# echo WSSID [DEV ADDR] #enroll in and activate "
+ "existing WSS, can request registrar\n"
+ "#\n"
+ "# WSSID is a 16 byte hex array. Eg. 12 A3 3B ... \n"
+ "# SECURE_STATUS 0 - unsecure, 1 - secure (default)\n"
+ "# ACCEPT_ENROLLMENT 0 - no, 1 - yes (default)\n"
+ "# NAME is the text string identifying the WSS\n"
+ "# DEV ADDR is the device address of neighbor "
+ "that should be registrar. Eg. 32:AB\n");
+
+ mutex_unlock(&wss->mutex);
+out:
+ return result;
+
+}
+EXPORT_SYMBOL_GPL(wlp_wss_activate_show);
+
+/**
+ * Create/activate a new WSS or enroll/activate in neighboring WSS
+ *
+ * The user can provide the WSSID of a WSS in which it wants to enroll.
+ * Only the WSSID is necessary if the WSS have been discovered before. If
+ * the WSS has not been discovered before, or the user wants to use a
+ * particular neighbor as its registrar, then the user can also provide a
+ * device address or the neighbor that will be used as registrar.
+ *
+ * A new WSS is created when the user provides a WSSID, secure status, and
+ * WSS name.
+ */
+ssize_t wlp_wss_activate_store(struct wlp_wss *wss,
+ const char *buf, size_t size)
+{
+ ssize_t result = -EINVAL;
+ struct wlp_uuid wssid;
+ struct uwb_dev_addr dev;
+ struct uwb_dev_addr bcast = {.data = {0xff, 0xff} };
+ char name[65];
+ unsigned sec_status, accept;
+ memset(name, 0, sizeof(name));
+ result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx:%02hhx",
+ &wssid.data[0] , &wssid.data[1],
+ &wssid.data[2] , &wssid.data[3],
+ &wssid.data[4] , &wssid.data[5],
+ &wssid.data[6] , &wssid.data[7],
+ &wssid.data[8] , &wssid.data[9],
+ &wssid.data[10], &wssid.data[11],
+ &wssid.data[12], &wssid.data[13],
+ &wssid.data[14], &wssid.data[15],
+ &dev.data[1], &dev.data[0]);
+ if (result == 16 || result == 17) {
+ result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%u %u %64c",
+ &wssid.data[0] , &wssid.data[1],
+ &wssid.data[2] , &wssid.data[3],
+ &wssid.data[4] , &wssid.data[5],
+ &wssid.data[6] , &wssid.data[7],
+ &wssid.data[8] , &wssid.data[9],
+ &wssid.data[10], &wssid.data[11],
+ &wssid.data[12], &wssid.data[13],
+ &wssid.data[14], &wssid.data[15],
+ &sec_status, &accept, name);
+ if (result == 16)
+ result = wlp_wss_enroll_activate(wss, &wssid, &bcast);
+ else if (result == 19) {
+ sec_status = sec_status == 0 ? 0 : 1;
+ accept = accept == 0 ? 0 : 1;
+ /* We read name using %c, so the newline needs to be
+ * removed */
+ if (strlen(name) != sizeof(name) - 1)
+ name[strlen(name) - 1] = '\0';
+ result = wlp_wss_create_activate(wss, &wssid, name,
+ sec_status, accept);
+ } else
+ result = -EINVAL;
+ } else if (result == 18)
+ result = wlp_wss_enroll_activate(wss, &wssid, &dev);
+ else
+ result = -EINVAL;
+ return result < 0? result : size;
+}
+EXPORT_SYMBOL_GPL(wlp_wss_activate_store);
+
+/**
+ * Show the UUID of this host
+ */
+ssize_t wlp_uuid_show(struct wlp *wlp, char *buf)
+{
+ ssize_t result = 0;
+
+ mutex_lock(&wlp->mutex);
+ result = wlp_wss_uuid_print(buf, PAGE_SIZE, &wlp->uuid);
+ buf[result++] = '\n';
+ mutex_unlock(&wlp->mutex);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wlp_uuid_show);
+
+/**
+ * Store a new UUID for this host
+ *
+ * According to the spec this should be encoded as an octet string in the
+ * order the octets are shown in string representation in RFC 4122 (WLP
+ * 0.99 [Table 6])
+ *
+ * We do not check value provided by user.
+ */
+ssize_t wlp_uuid_store(struct wlp *wlp, const char *buf, size_t size)
+{
+ ssize_t result;
+ struct wlp_uuid uuid;
+
+ mutex_lock(&wlp->mutex);
+ result = sscanf(buf, "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx "
+ "%02hhx %02hhx %02hhx %02hhx ",
+ &uuid.data[0] , &uuid.data[1],
+ &uuid.data[2] , &uuid.data[3],
+ &uuid.data[4] , &uuid.data[5],
+ &uuid.data[6] , &uuid.data[7],
+ &uuid.data[8] , &uuid.data[9],
+ &uuid.data[10], &uuid.data[11],
+ &uuid.data[12], &uuid.data[13],
+ &uuid.data[14], &uuid.data[15]);
+ if (result != 16) {
+ result = -EINVAL;
+ goto error;
+ }
+ wlp->uuid = uuid;
+error:
+ mutex_unlock(&wlp->mutex);
+ return result < 0? result : size;
+}
+EXPORT_SYMBOL_GPL(wlp_uuid_store);
+
+/**
+ * Show contents of members of device information structure
+ */
+#define wlp_dev_info_show(type) \
+ssize_t wlp_dev_##type##_show(struct wlp *wlp, char *buf) \
+{ \
+ ssize_t result = 0; \
+ mutex_lock(&wlp->mutex); \
+ if (wlp->dev_info == NULL) { \
+ result = __wlp_setup_device_info(wlp); \
+ if (result < 0) \
+ goto out; \
+ } \
+ result = scnprintf(buf, PAGE_SIZE, "%s\n", wlp->dev_info->type);\
+out: \
+ mutex_unlock(&wlp->mutex); \
+ return result; \
+} \
+EXPORT_SYMBOL_GPL(wlp_dev_##type##_show);
+
+wlp_dev_info_show(name)
+wlp_dev_info_show(model_name)
+wlp_dev_info_show(model_nr)
+wlp_dev_info_show(manufacturer)
+wlp_dev_info_show(serial)
+
+/**
+ * Store contents of members of device information structure
+ */
+#define wlp_dev_info_store(type, len) \
+ssize_t wlp_dev_##type##_store(struct wlp *wlp, const char *buf, size_t size)\
+{ \
+ ssize_t result; \
+ char format[10]; \
+ mutex_lock(&wlp->mutex); \
+ if (wlp->dev_info == NULL) { \
+ result = __wlp_alloc_device_info(wlp); \
+ if (result < 0) \
+ goto out; \
+ } \
+ memset(wlp->dev_info->type, 0, sizeof(wlp->dev_info->type)); \
+ sprintf(format, "%%%uc", len); \
+ result = sscanf(buf, format, wlp->dev_info->type); \
+out: \
+ mutex_unlock(&wlp->mutex); \
+ return result < 0? result : size; \
+} \
+EXPORT_SYMBOL_GPL(wlp_dev_##type##_store);
+
+wlp_dev_info_store(name, 32)
+wlp_dev_info_store(manufacturer, 64)
+wlp_dev_info_store(model_name, 32)
+wlp_dev_info_store(model_nr, 32)
+wlp_dev_info_store(serial, 32)
+
+static
+const char *__wlp_dev_category[] = {
+ [WLP_DEV_CAT_COMPUTER] = "Computer",
+ [WLP_DEV_CAT_INPUT] = "Input device",
+ [WLP_DEV_CAT_PRINT_SCAN_FAX_COPIER] = "Printer, scanner, FAX, or "
+ "Copier",
+ [WLP_DEV_CAT_CAMERA] = "Camera",
+ [WLP_DEV_CAT_STORAGE] = "Storage Network",
+ [WLP_DEV_CAT_INFRASTRUCTURE] = "Infrastructure",
+ [WLP_DEV_CAT_DISPLAY] = "Display",
+ [WLP_DEV_CAT_MULTIM] = "Multimedia device",
+ [WLP_DEV_CAT_GAMING] = "Gaming device",
+ [WLP_DEV_CAT_TELEPHONE] = "Telephone",
+ [WLP_DEV_CAT_OTHER] = "Other",
+};
+
+static
+const char *wlp_dev_category_str(unsigned cat)
+{
+ if ((cat >= WLP_DEV_CAT_COMPUTER && cat <= WLP_DEV_CAT_TELEPHONE)
+ || cat == WLP_DEV_CAT_OTHER)
+ return __wlp_dev_category[cat];
+ return "unknown category";
+}
+
+ssize_t wlp_dev_prim_category_show(struct wlp *wlp, char *buf)
+{
+ ssize_t result = 0;
+ mutex_lock(&wlp->mutex);
+ if (wlp->dev_info == NULL) {
+ result = __wlp_setup_device_info(wlp);
+ if (result < 0)
+ goto out;
+ }
+ result = scnprintf(buf, PAGE_SIZE, "%s\n",
+ wlp_dev_category_str(wlp->dev_info->prim_dev_type.category));
+out:
+ mutex_unlock(&wlp->mutex);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wlp_dev_prim_category_show);
+
+ssize_t wlp_dev_prim_category_store(struct wlp *wlp, const char *buf,
+ size_t size)
+{
+ ssize_t result;
+ u16 cat;
+ mutex_lock(&wlp->mutex);
+ if (wlp->dev_info == NULL) {
+ result = __wlp_alloc_device_info(wlp);
+ if (result < 0)
+ goto out;
+ }
+ result = sscanf(buf, "%hu", &cat);
+ if ((cat >= WLP_DEV_CAT_COMPUTER && cat <= WLP_DEV_CAT_TELEPHONE)
+ || cat == WLP_DEV_CAT_OTHER)
+ wlp->dev_info->prim_dev_type.category = cat;
+ else
+ result = -EINVAL;
+out:
+ mutex_unlock(&wlp->mutex);
+ return result < 0? result : size;
+}
+EXPORT_SYMBOL_GPL(wlp_dev_prim_category_store);
+
+ssize_t wlp_dev_prim_OUI_show(struct wlp *wlp, char *buf)
+{
+ ssize_t result = 0;
+ mutex_lock(&wlp->mutex);
+ if (wlp->dev_info == NULL) {
+ result = __wlp_setup_device_info(wlp);
+ if (result < 0)
+ goto out;
+ }
+ result = scnprintf(buf, PAGE_SIZE, "%02x:%02x:%02x\n",
+ wlp->dev_info->prim_dev_type.OUI[0],
+ wlp->dev_info->prim_dev_type.OUI[1],
+ wlp->dev_info->prim_dev_type.OUI[2]);
+out:
+ mutex_unlock(&wlp->mutex);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_show);
+
+ssize_t wlp_dev_prim_OUI_store(struct wlp *wlp, const char *buf, size_t size)
+{
+ ssize_t result;
+ u8 OUI[3];
+ mutex_lock(&wlp->mutex);
+ if (wlp->dev_info == NULL) {
+ result = __wlp_alloc_device_info(wlp);
+ if (result < 0)
+ goto out;
+ }
+ result = sscanf(buf, "%hhx:%hhx:%hhx",
+ &OUI[0], &OUI[1], &OUI[2]);
+ if (result != 3) {
+ result = -EINVAL;
+ goto out;
+ } else
+ memcpy(wlp->dev_info->prim_dev_type.OUI, OUI, sizeof(OUI));
+out:
+ mutex_unlock(&wlp->mutex);
+ return result < 0? result : size;
+}
+EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_store);
+
+
+ssize_t wlp_dev_prim_OUI_sub_show(struct wlp *wlp, char *buf)
+{
+ ssize_t result = 0;
+ mutex_lock(&wlp->mutex);
+ if (wlp->dev_info == NULL) {
+ result = __wlp_setup_device_info(wlp);
+ if (result < 0)
+ goto out;
+ }
+ result = scnprintf(buf, PAGE_SIZE, "%u\n",
+ wlp->dev_info->prim_dev_type.OUIsubdiv);
+out:
+ mutex_unlock(&wlp->mutex);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_sub_show);
+
+ssize_t wlp_dev_prim_OUI_sub_store(struct wlp *wlp, const char *buf,
+ size_t size)
+{
+ ssize_t result;
+ unsigned sub;
+ u8 max_sub = ~0;
+ mutex_lock(&wlp->mutex);
+ if (wlp->dev_info == NULL) {
+ result = __wlp_alloc_device_info(wlp);
+ if (result < 0)
+ goto out;
+ }
+ result = sscanf(buf, "%u", &sub);
+ if (sub <= max_sub)
+ /* unsigned to u8 */
+ wlp->dev_info->prim_dev_type.OUIsubdiv = sub;
+ else
+ result = -EINVAL;
+out:
+ mutex_unlock(&wlp->mutex);
+ return result < 0? result : size;
+}
+EXPORT_SYMBOL_GPL(wlp_dev_prim_OUI_sub_store);
+
+ssize_t wlp_dev_prim_subcat_show(struct wlp *wlp, char *buf)
+{
+ ssize_t result = 0;
+ mutex_lock(&wlp->mutex);
+ if (wlp->dev_info == NULL) {
+ result = __wlp_setup_device_info(wlp);
+ if (result < 0)
+ goto out;
+ }
+ result = scnprintf(buf, PAGE_SIZE, "%u\n",
+ wlp->dev_info->prim_dev_type.subID);
+out:
+ mutex_unlock(&wlp->mutex);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wlp_dev_prim_subcat_show);
+
+ssize_t wlp_dev_prim_subcat_store(struct wlp *wlp, const char *buf,
+ size_t size)
+{
+ ssize_t result;
+ unsigned sub;
+ __le16 max_sub = ~0;
+ mutex_lock(&wlp->mutex);
+ if (wlp->dev_info == NULL) {
+ result = __wlp_alloc_device_info(wlp);
+ if (result < 0)
+ goto out;
+ }
+ result = sscanf(buf, "%u", &sub);
+ if (sub <= max_sub)
+ /* unsigned to _le16 */
+ wlp->dev_info->prim_dev_type.subID = sub;
+ else
+ result = -EINVAL;
+out:
+ mutex_unlock(&wlp->mutex);
+ return result < 0? result : size;
+}
+EXPORT_SYMBOL_GPL(wlp_dev_prim_subcat_store);
+
+/**
+ * Subsystem implementation for interaction with individual WSS via sysfs
+ *
+ * Followed instructions for subsystem in Documentation/filesystems/sysfs.txt
+ */
+
+#define kobj_to_wlp_wss(obj) container_of(obj, struct wlp_wss, kobj)
+#define attr_to_wlp_wss_attr(_attr) \
+ container_of(_attr, struct wlp_wss_attribute, attr)
+
+/**
+ * Sysfs subsystem: forward read calls
+ *
+ * Sysfs operation for forwarding read call to the show method of the
+ * attribute owner
+ */
+static
+ssize_t wlp_wss_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct wlp_wss_attribute *wss_attr = attr_to_wlp_wss_attr(attr);
+ struct wlp_wss *wss = kobj_to_wlp_wss(kobj);
+ ssize_t ret = -EIO;
+
+ if (wss_attr->show)
+ ret = wss_attr->show(wss, buf);
+ return ret;
+}
+/**
+ * Sysfs subsystem: forward write calls
+ *
+ * Sysfs operation for forwarding write call to the store method of the
+ * attribute owner
+ */
+static
+ssize_t wlp_wss_attr_store(struct kobject *kobj, struct attribute *attr,
+ const char *buf, size_t count)
+{
+ struct wlp_wss_attribute *wss_attr = attr_to_wlp_wss_attr(attr);
+ struct wlp_wss *wss = kobj_to_wlp_wss(kobj);
+ ssize_t ret = -EIO;
+
+ if (wss_attr->store)
+ ret = wss_attr->store(wss, buf, count);
+ return ret;
+}
+
+static
+struct sysfs_ops wss_sysfs_ops = {
+ .show = wlp_wss_attr_show,
+ .store = wlp_wss_attr_store,
+};
+
+struct kobj_type wss_ktype = {
+ .release = wlp_wss_release,
+ .sysfs_ops = &wss_sysfs_ops,
+};
+
+
+/**
+ * Sysfs files for individual WSS
+ */
+
+/**
+ * Print static properties of this WSS
+ *
+ * The name of a WSS may not be null teminated. It's max size is 64 bytes
+ * so we copy it to a larger array just to make sure we print sane data.
+ */
+static ssize_t wlp_wss_properties_show(struct wlp_wss *wss, char *buf)
+{
+ int result = 0;
+
+ if (mutex_lock_interruptible(&wss->mutex))
+ goto out;
+ result = __wlp_wss_properties_show(wss, buf, PAGE_SIZE);
+ mutex_unlock(&wss->mutex);
+out:
+ return result;
+}
+WSS_ATTR(properties, S_IRUGO, wlp_wss_properties_show, NULL);
+
+/**
+ * Print all connected members of this WSS
+ * The EDA cache contains all members of WSS neighborhood.
+ */
+static ssize_t wlp_wss_members_show(struct wlp_wss *wss, char *buf)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ return wlp_eda_show(wlp, buf);
+}
+WSS_ATTR(members, S_IRUGO, wlp_wss_members_show, NULL);
+
+static
+const char *__wlp_strstate[] = {
+ "none",
+ "partially enrolled",
+ "enrolled",
+ "active",
+ "connected",
+};
+
+static const char *wlp_wss_strstate(unsigned state)
+{
+ if (state >= ARRAY_SIZE(__wlp_strstate))
+ return "unknown state";
+ return __wlp_strstate[state];
+}
+
+/*
+ * Print current state of this WSS
+ */
+static ssize_t wlp_wss_state_show(struct wlp_wss *wss, char *buf)
+{
+ int result = 0;
+
+ if (mutex_lock_interruptible(&wss->mutex))
+ goto out;
+ result = scnprintf(buf, PAGE_SIZE, "%s\n",
+ wlp_wss_strstate(wss->state));
+ mutex_unlock(&wss->mutex);
+out:
+ return result;
+}
+WSS_ATTR(state, S_IRUGO, wlp_wss_state_show, NULL);
+
+
+static
+struct attribute *wss_attrs[] = {
+ &wss_attr_properties.attr,
+ &wss_attr_members.attr,
+ &wss_attr_state.attr,
+ NULL,
+};
+
+struct attribute_group wss_attr_group = {
+ .name = NULL, /* we want them in the same directory */
+ .attrs = wss_attrs,
+};
diff --git a/drivers/uwb/wlp/txrx.c b/drivers/uwb/wlp/txrx.c
new file mode 100644
index 000000000000..c701bd1a2887
--- /dev/null
+++ b/drivers/uwb/wlp/txrx.c
@@ -0,0 +1,374 @@
+/*
+ * WiMedia Logical Link Control Protocol (WLP)
+ * Message exchange infrastructure
+ *
+ * Copyright (C) 2007 Intel Corporation
+ * Reinette Chatre <reinette.chatre@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: Docs
+ *
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/wlp.h>
+#define D_LOCAL 5
+#include <linux/uwb/debug.h>
+#include "wlp-internal.h"
+
+
+/**
+ * Direct incoming association msg to correct parsing routine
+ *
+ * We only expect D1, E1, C1, C3 messages as new. All other incoming
+ * association messages should form part of an established session that is
+ * handled elsewhere.
+ * The handling of these messages often require calling sleeping functions
+ * - this cannot be done in interrupt context. We use the kernel's
+ * workqueue to handle these messages.
+ */
+static
+void wlp_direct_assoc_frame(struct wlp *wlp, struct sk_buff *skb,
+ struct uwb_dev_addr *src)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_frame_assoc *assoc = (void *) skb->data;
+ struct wlp_assoc_frame_ctx *frame_ctx;
+ d_fnstart(5, dev, "wlp %p, skb %p\n", wlp, skb);
+ frame_ctx = kmalloc(sizeof(*frame_ctx), GFP_ATOMIC);
+ if (frame_ctx == NULL) {
+ dev_err(dev, "WLP: Unable to allocate memory for association "
+ "frame handling.\n");
+ kfree_skb(skb);
+ goto out;
+ }
+ frame_ctx->wlp = wlp;
+ frame_ctx->skb = skb;
+ frame_ctx->src = *src;
+ switch (assoc->type) {
+ case WLP_ASSOC_D1:
+ d_printf(5, dev, "Received a D1 frame.\n");
+ INIT_WORK(&frame_ctx->ws, wlp_handle_d1_frame);
+ schedule_work(&frame_ctx->ws);
+ break;
+ case WLP_ASSOC_E1:
+ d_printf(5, dev, "Received a E1 frame. FIXME?\n");
+ kfree_skb(skb); /* Temporary until we handle it */
+ kfree(frame_ctx); /* Temporary until we handle it */
+ break;
+ case WLP_ASSOC_C1:
+ d_printf(5, dev, "Received a C1 frame.\n");
+ INIT_WORK(&frame_ctx->ws, wlp_handle_c1_frame);
+ schedule_work(&frame_ctx->ws);
+ break;
+ case WLP_ASSOC_C3:
+ d_printf(5, dev, "Received a C3 frame.\n");
+ INIT_WORK(&frame_ctx->ws, wlp_handle_c3_frame);
+ schedule_work(&frame_ctx->ws);
+ break;
+ default:
+ dev_err(dev, "Received unexpected association frame. "
+ "Type = %d \n", assoc->type);
+ kfree_skb(skb);
+ kfree(frame_ctx);
+ break;
+ }
+out:
+ d_fnend(5, dev, "wlp %p\n", wlp);
+}
+
+/**
+ * Process incoming association frame
+ *
+ * Although it could be possible to deal with some incoming association
+ * messages without creating a new session we are keeping things simple. We
+ * do not accept new association messages if there is a session in progress
+ * and the messages do not belong to that session.
+ *
+ * If an association message arrives that causes the creation of a session
+ * (WLP_ASSOC_E1) while we are in the process of creating a session then we
+ * rely on the neighbor mutex to protect the data. That is, the new session
+ * will not be started until the previous is completed.
+ */
+static
+void wlp_receive_assoc_frame(struct wlp *wlp, struct sk_buff *skb,
+ struct uwb_dev_addr *src)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_frame_assoc *assoc = (void *) skb->data;
+ struct wlp_session *session = wlp->session;
+ u8 version;
+ d_fnstart(5, dev, "wlp %p, skb %p\n", wlp, skb);
+
+ if (wlp_get_version(wlp, &assoc->version, &version,
+ sizeof(assoc->version)) < 0)
+ goto error;
+ if (version != WLP_VERSION) {
+ dev_err(dev, "Unsupported WLP version in association "
+ "message.\n");
+ goto error;
+ }
+ if (session != NULL) {
+ /* Function that created this session is still holding the
+ * &wlp->mutex to protect this session. */
+ if (assoc->type == session->exp_message ||
+ assoc->type == WLP_ASSOC_F0) {
+ if (!memcmp(&session->neighbor_addr, src,
+ sizeof(*src))) {
+ session->data = skb;
+ (session->cb)(wlp);
+ } else {
+ dev_err(dev, "Received expected message from "
+ "unexpected source. Expected message "
+ "%d or F0 from %02x:%02x, but received "
+ "it from %02x:%02x. Dropping.\n",
+ session->exp_message,
+ session->neighbor_addr.data[1],
+ session->neighbor_addr.data[0],
+ src->data[1], src->data[0]);
+ goto error;
+ }
+ } else {
+ dev_err(dev, "Association already in progress. "
+ "Dropping.\n");
+ goto error;
+ }
+ } else {
+ wlp_direct_assoc_frame(wlp, skb, src);
+ }
+ d_fnend(5, dev, "wlp %p\n", wlp);
+ return;
+error:
+ kfree_skb(skb);
+ d_fnend(5, dev, "wlp %p\n", wlp);
+}
+
+/**
+ * Verify incoming frame is from connected neighbor, prep to pass to WLP client
+ *
+ * Verification proceeds according to WLP 0.99 [7.3.1]. The source address
+ * is used to determine which neighbor is sending the frame and the WSS tag
+ * is used to know to which WSS the frame belongs (we only support one WSS
+ * so this test is straight forward).
+ * With the WSS found we need to ensure that we are connected before
+ * allowing the exchange of data frames.
+ */
+static
+int wlp_verify_prep_rx_frame(struct wlp *wlp, struct sk_buff *skb,
+ struct uwb_dev_addr *src)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = -EINVAL;
+ struct wlp_eda_node eda_entry;
+ struct wlp_frame_std_abbrv_hdr *hdr = (void *) skb->data;
+
+ d_fnstart(6, dev, "wlp %p, skb %p \n", wlp, skb);
+ /*verify*/
+ result = wlp_copy_eda_node(&wlp->eda, src, &eda_entry);
+ if (result < 0) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Incoming frame is from unknown "
+ "neighbor %02x:%02x.\n", src->data[1],
+ src->data[0]);
+ goto out;
+ }
+ if (hdr->tag != eda_entry.tag) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Tag of incoming frame from "
+ "%02x:%02x does not match expected tag. "
+ "Received 0x%02x, expected 0x%02x. \n",
+ src->data[1], src->data[0], hdr->tag,
+ eda_entry.tag);
+ result = -EINVAL;
+ goto out;
+ }
+ if (eda_entry.state != WLP_WSS_CONNECTED) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Incoming frame from "
+ "%02x:%02x does is not from connected WSS.\n",
+ src->data[1], src->data[0]);
+ result = -EINVAL;
+ goto out;
+ }
+ /*prep*/
+ skb_pull(skb, sizeof(*hdr));
+out:
+ d_fnend(6, dev, "wlp %p, skb %p, result = %d \n", wlp, skb, result);
+ return result;
+}
+
+/**
+ * Receive a WLP frame from device
+ *
+ * @returns: 1 if calling function should free the skb
+ * 0 if it successfully handled skb and freed it
+ * 0 if error occured, will free skb in this case
+ */
+int wlp_receive_frame(struct device *dev, struct wlp *wlp, struct sk_buff *skb,
+ struct uwb_dev_addr *src)
+{
+ unsigned len = skb->len;
+ void *ptr = skb->data;
+ struct wlp_frame_hdr *hdr;
+ int result = 0;
+
+ d_fnstart(6, dev, "skb (%p), len (%u)\n", skb, len);
+ if (len < sizeof(*hdr)) {
+ dev_err(dev, "Not enough data to parse WLP header.\n");
+ result = -EINVAL;
+ goto out;
+ }
+ hdr = ptr;
+ d_dump(6, dev, hdr, sizeof(*hdr));
+ if (le16_to_cpu(hdr->mux_hdr) != WLP_PROTOCOL_ID) {
+ dev_err(dev, "Not a WLP frame type.\n");
+ result = -EINVAL;
+ goto out;
+ }
+ switch (hdr->type) {
+ case WLP_FRAME_STANDARD:
+ if (len < sizeof(struct wlp_frame_std_abbrv_hdr)) {
+ dev_err(dev, "Not enough data to parse Standard "
+ "WLP header.\n");
+ goto out;
+ }
+ result = wlp_verify_prep_rx_frame(wlp, skb, src);
+ if (result < 0) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Verification of frame "
+ "from neighbor %02x:%02x failed.\n",
+ src->data[1], src->data[0]);
+ goto out;
+ }
+ result = 1;
+ break;
+ case WLP_FRAME_ABBREVIATED:
+ dev_err(dev, "Abbreviated frame received. FIXME?\n");
+ kfree_skb(skb);
+ break;
+ case WLP_FRAME_CONTROL:
+ dev_err(dev, "Control frame received. FIXME?\n");
+ kfree_skb(skb);
+ break;
+ case WLP_FRAME_ASSOCIATION:
+ if (len < sizeof(struct wlp_frame_assoc)) {
+ dev_err(dev, "Not enough data to parse Association "
+ "WLP header.\n");
+ goto out;
+ }
+ d_printf(5, dev, "Association frame received.\n");
+ wlp_receive_assoc_frame(wlp, skb, src);
+ break;
+ default:
+ dev_err(dev, "Invalid frame received.\n");
+ result = -EINVAL;
+ break;
+ }
+out:
+ if (result < 0) {
+ kfree_skb(skb);
+ result = 0;
+ }
+ d_fnend(6, dev, "skb (%p)\n", skb);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wlp_receive_frame);
+
+
+/**
+ * Verify frame from network stack, prepare for further transmission
+ *
+ * @skb: the socket buffer that needs to be prepared for transmission (it
+ * is in need of a WLP header). If this is a broadcast frame we take
+ * over the entire transmission.
+ * If it is a unicast the WSS connection should already be established
+ * and transmission will be done by the calling function.
+ * @dst: On return this will contain the device address to which the
+ * frame is destined.
+ * @returns: 0 on success no tx : WLP header sucessfully applied to skb buffer,
+ * calling function can proceed with tx
+ * 1 on success with tx : WLP will take over transmission of this
+ * frame
+ * <0 on error
+ *
+ * The network stack (WLP client) is attempting to transmit a frame. We can
+ * only transmit data if a local WSS is at least active (connection will be
+ * done here if this is a broadcast frame and neighbor also has the WSS
+ * active).
+ *
+ * The frame can be either broadcast or unicast. Broadcast in a WSS is
+ * supported via multicast, but we don't support multicast yet (until
+ * devices start to support MAB IEs). If a broadcast frame needs to be
+ * transmitted it is treated as a unicast frame to each neighbor. In this
+ * case the WLP takes over transmission of the skb and returns 1
+ * to the caller to indicate so. Also, in this case, if a neighbor has the
+ * same WSS activated but is not connected then the WSS connection will be
+ * done at this time. The neighbor's virtual address will be learned at
+ * this time.
+ *
+ * The destination address in a unicast frame is the virtual address of the
+ * neighbor. This address only becomes known when a WSS connection is
+ * established. We thus rely on a broadcast frame to trigger the setup of
+ * WSS connections to all neighbors before we are able to send unicast
+ * frames to them. This seems reasonable as IP would usually use ARP first
+ * before any unicast frames are sent.
+ *
+ * If we are already connected to the neighbor (neighbor's virtual address
+ * is known) we just prepare the WLP header and the caller will continue to
+ * send the frame.
+ *
+ * A failure in this function usually indicates something that cannot be
+ * fixed automatically. So, if this function fails (@return < 0) the calling
+ * function should not retry to send the frame as it will very likely keep
+ * failing.
+ *
+ */
+int wlp_prepare_tx_frame(struct device *dev, struct wlp *wlp,
+ struct sk_buff *skb, struct uwb_dev_addr *dst)
+{
+ int result = -EINVAL;
+ struct ethhdr *eth_hdr = (void *) skb->data;
+
+ d_fnstart(6, dev, "wlp (%p), skb (%p) \n", wlp, skb);
+ if (is_broadcast_ether_addr(eth_hdr->h_dest)) {
+ d_printf(6, dev, "WLP: handling broadcast frame. \n");
+ result = wlp_eda_for_each(&wlp->eda, wlp_wss_send_copy, skb);
+ if (result < 0) {
+ if (printk_ratelimit())
+ dev_err(dev, "Unable to handle broadcast "
+ "frame from WLP client.\n");
+ goto out;
+ }
+ dev_kfree_skb_irq(skb);
+ result = 1;
+ /* Frame will be transmitted by WLP. */
+ } else {
+ d_printf(6, dev, "WLP: handling unicast frame. \n");
+ result = wlp_eda_for_virtual(&wlp->eda, eth_hdr->h_dest, dst,
+ wlp_wss_prep_hdr, skb);
+ if (unlikely(result < 0)) {
+ if (printk_ratelimit())
+ dev_err(dev, "Unable to prepare "
+ "skb for transmission. \n");
+ goto out;
+ }
+ }
+out:
+ d_fnend(6, dev, "wlp (%p), skb (%p). result = %d \n", wlp, skb, result);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wlp_prepare_tx_frame);
diff --git a/drivers/uwb/wlp/wlp-internal.h b/drivers/uwb/wlp/wlp-internal.h
new file mode 100644
index 000000000000..1c94fabfb1a7
--- /dev/null
+++ b/drivers/uwb/wlp/wlp-internal.h
@@ -0,0 +1,228 @@
+/*
+ * WiMedia Logical Link Control Protocol (WLP)
+ * Internal API
+ *
+ * Copyright (C) 2007 Intel Corporation
+ * Reinette Chatre <reinette.chatre@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ */
+
+#ifndef __WLP_INTERNAL_H__
+#define __WLP_INTERNAL_H__
+
+/**
+ * State of WSS connection
+ *
+ * A device needs to connect to a neighbor in an activated WSS before data
+ * can be transmitted. The spec also distinguishes between a new connection
+ * attempt and a connection attempt after previous connection attempts. The
+ * state WLP_WSS_CONNECT_FAILED is used for this scenario. See WLP 0.99
+ * [7.2.6]
+ */
+enum wlp_wss_connect {
+ WLP_WSS_UNCONNECTED = 0,
+ WLP_WSS_CONNECTED,
+ WLP_WSS_CONNECT_FAILED,
+};
+
+extern struct kobj_type wss_ktype;
+extern struct attribute_group wss_attr_group;
+
+extern int uwb_rc_ie_add(struct uwb_rc *, const struct uwb_ie_hdr *, size_t);
+extern int uwb_rc_ie_rm(struct uwb_rc *, enum uwb_ie);
+
+
+/* This should be changed to a dynamic array where entries are sorted
+ * by eth_addr and search is done in a binary form
+ *
+ * Although thinking twice about it: this technologie's maximum reach
+ * is 10 meters...unless you want to pack too much stuff in around
+ * your radio controller/WLP device, the list will probably not be
+ * too big.
+ *
+ * In any case, there is probably some data structure in the kernel
+ * than we could reused for that already.
+ *
+ * The below structure is really just good while we support one WSS per
+ * host.
+ */
+struct wlp_eda_node {
+ struct list_head list_node;
+ unsigned char eth_addr[ETH_ALEN];
+ struct uwb_dev_addr dev_addr;
+ struct wlp_wss *wss;
+ unsigned char virt_addr[ETH_ALEN];
+ u8 tag;
+ enum wlp_wss_connect state;
+};
+
+typedef int (*wlp_eda_for_each_f)(struct wlp *, struct wlp_eda_node *, void *);
+
+extern void wlp_eda_init(struct wlp_eda *);
+extern void wlp_eda_release(struct wlp_eda *);
+extern int wlp_eda_create_node(struct wlp_eda *,
+ const unsigned char eth_addr[ETH_ALEN],
+ const struct uwb_dev_addr *);
+extern void wlp_eda_rm_node(struct wlp_eda *, const struct uwb_dev_addr *);
+extern int wlp_eda_update_node(struct wlp_eda *,
+ const struct uwb_dev_addr *,
+ struct wlp_wss *,
+ const unsigned char virt_addr[ETH_ALEN],
+ const u8, const enum wlp_wss_connect);
+extern int wlp_eda_update_node_state(struct wlp_eda *,
+ const struct uwb_dev_addr *,
+ const enum wlp_wss_connect);
+
+extern int wlp_copy_eda_node(struct wlp_eda *, struct uwb_dev_addr *,
+ struct wlp_eda_node *);
+extern int wlp_eda_for_each(struct wlp_eda *, wlp_eda_for_each_f , void *);
+extern int wlp_eda_for_virtual(struct wlp_eda *,
+ const unsigned char eth_addr[ETH_ALEN],
+ struct uwb_dev_addr *,
+ wlp_eda_for_each_f , void *);
+
+
+extern void wlp_remove_neighbor_tmp_info(struct wlp_neighbor_e *);
+
+extern size_t wlp_wss_key_print(char *, size_t, u8 *);
+
+/* Function called when no more references to WSS exists */
+extern void wlp_wss_release(struct kobject *);
+
+extern void wlp_wss_reset(struct wlp_wss *);
+extern int wlp_wss_create_activate(struct wlp_wss *, struct wlp_uuid *,
+ char *, unsigned, unsigned);
+extern int wlp_wss_enroll_activate(struct wlp_wss *, struct wlp_uuid *,
+ struct uwb_dev_addr *);
+extern ssize_t wlp_discover(struct wlp *);
+
+extern int wlp_enroll_neighbor(struct wlp *, struct wlp_neighbor_e *,
+ struct wlp_wss *, struct wlp_uuid *);
+extern int wlp_wss_is_active(struct wlp *, struct wlp_wss *,
+ struct uwb_dev_addr *);
+
+struct wlp_assoc_conn_ctx {
+ struct work_struct ws;
+ struct wlp *wlp;
+ struct sk_buff *skb;
+ struct wlp_eda_node eda_entry;
+};
+
+
+extern int wlp_wss_connect_prep(struct wlp *, struct wlp_eda_node *, void *);
+extern int wlp_wss_send_copy(struct wlp *, struct wlp_eda_node *, void *);
+
+
+/* Message handling */
+struct wlp_assoc_frame_ctx {
+ struct work_struct ws;
+ struct wlp *wlp;
+ struct sk_buff *skb;
+ struct uwb_dev_addr src;
+};
+
+extern int wlp_wss_prep_hdr(struct wlp *, struct wlp_eda_node *, void *);
+extern void wlp_handle_d1_frame(struct work_struct *);
+extern int wlp_parse_d2_frame_to_cache(struct wlp *, struct sk_buff *,
+ struct wlp_neighbor_e *);
+extern int wlp_parse_d2_frame_to_enroll(struct wlp_wss *, struct sk_buff *,
+ struct wlp_neighbor_e *,
+ struct wlp_uuid *);
+extern void wlp_handle_c1_frame(struct work_struct *);
+extern void wlp_handle_c3_frame(struct work_struct *);
+extern int wlp_parse_c3c4_frame(struct wlp *, struct sk_buff *,
+ struct wlp_uuid *, u8 *,
+ struct uwb_mac_addr *);
+extern int wlp_parse_f0(struct wlp *, struct sk_buff *);
+extern int wlp_send_assoc_frame(struct wlp *, struct wlp_wss *,
+ struct uwb_dev_addr *, enum wlp_assoc_type);
+extern ssize_t wlp_get_version(struct wlp *, struct wlp_attr_version *,
+ u8 *, ssize_t);
+extern ssize_t wlp_get_wssid(struct wlp *, struct wlp_attr_wssid *,
+ struct wlp_uuid *, ssize_t);
+extern int __wlp_alloc_device_info(struct wlp *);
+extern int __wlp_setup_device_info(struct wlp *);
+
+extern struct wlp_wss_attribute wss_attribute_properties;
+extern struct wlp_wss_attribute wss_attribute_members;
+extern struct wlp_wss_attribute wss_attribute_state;
+
+static inline
+size_t wlp_wss_uuid_print(char *buf, size_t bufsize, struct wlp_uuid *uuid)
+{
+ size_t result;
+
+ result = scnprintf(buf, bufsize,
+ "%02x:%02x:%02x:%02x:%02x:%02x:"
+ "%02x:%02x:%02x:%02x:%02x:%02x:"
+ "%02x:%02x:%02x:%02x",
+ uuid->data[0], uuid->data[1],
+ uuid->data[2], uuid->data[3],
+ uuid->data[4], uuid->data[5],
+ uuid->data[6], uuid->data[7],
+ uuid->data[8], uuid->data[9],
+ uuid->data[10], uuid->data[11],
+ uuid->data[12], uuid->data[13],
+ uuid->data[14], uuid->data[15]);
+ return result;
+}
+
+/**
+ * FIXME: How should a nonce be displayed?
+ */
+static inline
+size_t wlp_wss_nonce_print(char *buf, size_t bufsize, struct wlp_nonce *nonce)
+{
+ size_t result;
+
+ result = scnprintf(buf, bufsize,
+ "%02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x",
+ nonce->data[0], nonce->data[1],
+ nonce->data[2], nonce->data[3],
+ nonce->data[4], nonce->data[5],
+ nonce->data[6], nonce->data[7],
+ nonce->data[8], nonce->data[9],
+ nonce->data[10], nonce->data[11],
+ nonce->data[12], nonce->data[13],
+ nonce->data[14], nonce->data[15]);
+ return result;
+}
+
+
+static inline
+void wlp_session_cb(struct wlp *wlp)
+{
+ struct completion *completion = wlp->session->cb_priv;
+ complete(completion);
+}
+
+static inline
+int wlp_uuid_is_set(struct wlp_uuid *uuid)
+{
+ struct wlp_uuid zero_uuid = { .data = { 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00} };
+
+ if (!memcmp(uuid, &zero_uuid, sizeof(*uuid)))
+ return 0;
+ return 1;
+}
+
+#endif /* __WLP_INTERNAL_H__ */
diff --git a/drivers/uwb/wlp/wlp-lc.c b/drivers/uwb/wlp/wlp-lc.c
new file mode 100644
index 000000000000..0bc636c021f4
--- /dev/null
+++ b/drivers/uwb/wlp/wlp-lc.c
@@ -0,0 +1,572 @@
+/*
+ * WiMedia Logical Link Control Protocol (WLP)
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Reinette Chatre <reinette.chatre@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ */
+
+#include <linux/wlp.h>
+#define D_LOCAL 6
+#include <linux/uwb/debug.h>
+#include "wlp-internal.h"
+
+
+static
+void wlp_neighbor_init(struct wlp_neighbor_e *neighbor)
+{
+ INIT_LIST_HEAD(&neighbor->wssid);
+}
+
+/**
+ * Create area for device information storage
+ *
+ * wlp->mutex must be held
+ */
+int __wlp_alloc_device_info(struct wlp *wlp)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ BUG_ON(wlp->dev_info != NULL);
+ wlp->dev_info = kzalloc(sizeof(struct wlp_device_info), GFP_KERNEL);
+ if (wlp->dev_info == NULL) {
+ dev_err(dev, "WLP: Unable to allocate memory for "
+ "device information.\n");
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+
+/**
+ * Fill in device information using function provided by driver
+ *
+ * wlp->mutex must be held
+ */
+static
+void __wlp_fill_device_info(struct wlp *wlp)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+
+ BUG_ON(wlp->fill_device_info == NULL);
+ d_printf(6, dev, "Retrieving device information "
+ "from device driver.\n");
+ wlp->fill_device_info(wlp, wlp->dev_info);
+}
+
+/**
+ * Setup device information
+ *
+ * Allocate area for device information and populate it.
+ *
+ * wlp->mutex must be held
+ */
+int __wlp_setup_device_info(struct wlp *wlp)
+{
+ int result;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+
+ result = __wlp_alloc_device_info(wlp);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to allocate area for "
+ "device information.\n");
+ return result;
+ }
+ __wlp_fill_device_info(wlp);
+ return 0;
+}
+
+/**
+ * Remove information about neighbor stored temporarily
+ *
+ * Information learned during discovey should only be stored when the
+ * device enrolls in the neighbor's WSS. We do need to store this
+ * information temporarily in order to present it to the user.
+ *
+ * We are only interested in keeping neighbor WSS information if that
+ * neighbor is accepting enrollment.
+ *
+ * should be called with wlp->nbmutex held
+ */
+void wlp_remove_neighbor_tmp_info(struct wlp_neighbor_e *neighbor)
+{
+ struct wlp_wssid_e *wssid_e, *next;
+ u8 keep;
+ if (!list_empty(&neighbor->wssid)) {
+ list_for_each_entry_safe(wssid_e, next, &neighbor->wssid,
+ node) {
+ if (wssid_e->info != NULL) {
+ keep = wssid_e->info->accept_enroll;
+ kfree(wssid_e->info);
+ wssid_e->info = NULL;
+ if (!keep) {
+ list_del(&wssid_e->node);
+ kfree(wssid_e);
+ }
+ }
+ }
+ }
+ if (neighbor->info != NULL) {
+ kfree(neighbor->info);
+ neighbor->info = NULL;
+ }
+}
+
+/**
+ * Populate WLP neighborhood cache with neighbor information
+ *
+ * A new neighbor is found. If it is discoverable then we add it to the
+ * neighborhood cache.
+ *
+ */
+static
+int wlp_add_neighbor(struct wlp *wlp, struct uwb_dev *dev)
+{
+ int result = 0;
+ int discoverable;
+ struct wlp_neighbor_e *neighbor;
+
+ d_fnstart(6, &dev->dev, "uwb %p \n", dev);
+ d_printf(6, &dev->dev, "Found neighbor device %02x:%02x \n",
+ dev->dev_addr.data[1], dev->dev_addr.data[0]);
+ /**
+ * FIXME:
+ * Use contents of WLP IE found in beacon cache to determine if
+ * neighbor is discoverable.
+ * The device does not support WLP IE yet so this still needs to be
+ * done. Until then we assume all devices are discoverable.
+ */
+ discoverable = 1; /* will be changed when FIXME disappears */
+ if (discoverable) {
+ /* Add neighbor to cache for discovery */
+ neighbor = kzalloc(sizeof(*neighbor), GFP_KERNEL);
+ if (neighbor == NULL) {
+ dev_err(&dev->dev, "Unable to create memory for "
+ "new neighbor. \n");
+ result = -ENOMEM;
+ goto error_no_mem;
+ }
+ wlp_neighbor_init(neighbor);
+ uwb_dev_get(dev);
+ neighbor->uwb_dev = dev;
+ list_add(&neighbor->node, &wlp->neighbors);
+ }
+error_no_mem:
+ d_fnend(6, &dev->dev, "uwb %p, result = %d \n", dev, result);
+ return result;
+}
+
+/**
+ * Remove one neighbor from cache
+ */
+static
+void __wlp_neighbor_release(struct wlp_neighbor_e *neighbor)
+{
+ struct wlp_wssid_e *wssid_e, *next_wssid_e;
+
+ list_for_each_entry_safe(wssid_e, next_wssid_e,
+ &neighbor->wssid, node) {
+ list_del(&wssid_e->node);
+ kfree(wssid_e);
+ }
+ uwb_dev_put(neighbor->uwb_dev);
+ list_del(&neighbor->node);
+ kfree(neighbor);
+}
+
+/**
+ * Clear entire neighborhood cache.
+ */
+static
+void __wlp_neighbors_release(struct wlp *wlp)
+{
+ struct wlp_neighbor_e *neighbor, *next;
+ if (list_empty(&wlp->neighbors))
+ return;
+ list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) {
+ __wlp_neighbor_release(neighbor);
+ }
+}
+
+static
+void wlp_neighbors_release(struct wlp *wlp)
+{
+ mutex_lock(&wlp->nbmutex);
+ __wlp_neighbors_release(wlp);
+ mutex_unlock(&wlp->nbmutex);
+}
+
+
+
+/**
+ * Send D1 message to neighbor, receive D2 message
+ *
+ * @neighbor: neighbor to which D1 message will be sent
+ * @wss: if not NULL, it is an enrollment request for this WSS
+ * @wssid: if wss not NULL, this is the wssid of the WSS in which we
+ * want to enroll
+ *
+ * A D1/D2 exchange is done for one of two reasons: discovery or
+ * enrollment. If done for discovery the D1 message is sent to the neighbor
+ * and the contents of the D2 response is stored in a temporary cache.
+ * If done for enrollment the @wss and @wssid are provided also. In this
+ * case the D1 message is sent to the neighbor, the D2 response is parsed
+ * for enrollment of the WSS with wssid.
+ *
+ * &wss->mutex is held
+ */
+static
+int wlp_d1d2_exchange(struct wlp *wlp, struct wlp_neighbor_e *neighbor,
+ struct wlp_wss *wss, struct wlp_uuid *wssid)
+{
+ int result;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ DECLARE_COMPLETION_ONSTACK(completion);
+ struct wlp_session session;
+ struct sk_buff *skb;
+ struct wlp_frame_assoc *resp;
+ struct uwb_dev_addr *dev_addr = &neighbor->uwb_dev->dev_addr;
+
+ mutex_lock(&wlp->mutex);
+ if (!wlp_uuid_is_set(&wlp->uuid)) {
+ dev_err(dev, "WLP: UUID is not set. Set via sysfs to "
+ "proceed.\n");
+ result = -ENXIO;
+ goto out;
+ }
+ /* Send D1 association frame */
+ result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_D1);
+ if (result < 0) {
+ dev_err(dev, "Unable to send D1 frame to neighbor "
+ "%02x:%02x (%d)\n", dev_addr->data[1],
+ dev_addr->data[0], result);
+ d_printf(6, dev, "Add placeholders into buffer next to "
+ "neighbor information we have (dev address).\n");
+ goto out;
+ }
+ /* Create session, wait for response */
+ session.exp_message = WLP_ASSOC_D2;
+ session.cb = wlp_session_cb;
+ session.cb_priv = &completion;
+ session.neighbor_addr = *dev_addr;
+ BUG_ON(wlp->session != NULL);
+ wlp->session = &session;
+ /* Wait for D2/F0 frame */
+ result = wait_for_completion_interruptible_timeout(&completion,
+ WLP_PER_MSG_TIMEOUT * HZ);
+ if (result == 0) {
+ result = -ETIMEDOUT;
+ dev_err(dev, "Timeout while sending D1 to neighbor "
+ "%02x:%02x.\n", dev_addr->data[1],
+ dev_addr->data[0]);
+ goto error_session;
+ }
+ if (result < 0) {
+ dev_err(dev, "Unable to discover/enroll neighbor %02x:%02x.\n",
+ dev_addr->data[1], dev_addr->data[0]);
+ goto error_session;
+ }
+ /* Parse message in session->data: it will be either D2 or F0 */
+ skb = session.data;
+ resp = (void *) skb->data;
+ d_printf(6, dev, "Received response to D1 frame. \n");
+ d_dump(6, dev, skb->data, skb->len > 72 ? 72 : skb->len);
+
+ if (resp->type == WLP_ASSOC_F0) {
+ result = wlp_parse_f0(wlp, skb);
+ if (result < 0)
+ dev_err(dev, "WLP: Unable to parse F0 from neighbor "
+ "%02x:%02x.\n", dev_addr->data[1],
+ dev_addr->data[0]);
+ result = -EINVAL;
+ goto error_resp_parse;
+ }
+ if (wss == NULL) {
+ /* Discovery */
+ result = wlp_parse_d2_frame_to_cache(wlp, skb, neighbor);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to parse D2 message from "
+ "neighbor %02x:%02x for discovery.\n",
+ dev_addr->data[1], dev_addr->data[0]);
+ goto error_resp_parse;
+ }
+ } else {
+ /* Enrollment */
+ result = wlp_parse_d2_frame_to_enroll(wss, skb, neighbor,
+ wssid);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to parse D2 message from "
+ "neighbor %02x:%02x for enrollment.\n",
+ dev_addr->data[1], dev_addr->data[0]);
+ goto error_resp_parse;
+ }
+ }
+error_resp_parse:
+ kfree_skb(skb);
+error_session:
+ wlp->session = NULL;
+out:
+ mutex_unlock(&wlp->mutex);
+ return result;
+}
+
+/**
+ * Enroll into WSS of provided WSSID by using neighbor as registrar
+ *
+ * &wss->mutex is held
+ */
+int wlp_enroll_neighbor(struct wlp *wlp, struct wlp_neighbor_e *neighbor,
+ struct wlp_wss *wss, struct wlp_uuid *wssid)
+{
+ int result = 0;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ struct uwb_dev_addr *dev_addr = &neighbor->uwb_dev->dev_addr;
+ wlp_wss_uuid_print(buf, sizeof(buf), wssid);
+ d_fnstart(6, dev, "wlp %p, neighbor %p, wss %p, wssid %p (%s)\n",
+ wlp, neighbor, wss, wssid, buf);
+ d_printf(6, dev, "Complete me.\n");
+ result = wlp_d1d2_exchange(wlp, neighbor, wss, wssid);
+ if (result < 0) {
+ dev_err(dev, "WLP: D1/D2 message exchange for enrollment "
+ "failed. result = %d \n", result);
+ goto out;
+ }
+ if (wss->state != WLP_WSS_STATE_PART_ENROLLED) {
+ dev_err(dev, "WLP: Unable to enroll into WSS %s using "
+ "neighbor %02x:%02x. \n", buf,
+ dev_addr->data[1], dev_addr->data[0]);
+ result = -EINVAL;
+ goto out;
+ }
+ if (wss->secure_status == WLP_WSS_SECURE) {
+ dev_err(dev, "FIXME: need to complete secure enrollment.\n");
+ result = -EINVAL;
+ goto error;
+ } else {
+ wss->state = WLP_WSS_STATE_ENROLLED;
+ d_printf(2, dev, "WLP: Success Enrollment into unsecure WSS "
+ "%s using neighbor %02x:%02x. \n", buf,
+ dev_addr->data[1], dev_addr->data[0]);
+ }
+
+ d_fnend(6, dev, "wlp %p, neighbor %p, wss %p, wssid %p (%s)\n",
+ wlp, neighbor, wss, wssid, buf);
+out:
+ return result;
+error:
+ wlp_wss_reset(wss);
+ return result;
+}
+
+/**
+ * Discover WSS information of neighbor's active WSS
+ */
+static
+int wlp_discover_neighbor(struct wlp *wlp,
+ struct wlp_neighbor_e *neighbor)
+{
+ return wlp_d1d2_exchange(wlp, neighbor, NULL, NULL);
+}
+
+
+/**
+ * Each neighbor in the neighborhood cache is discoverable. Discover it.
+ *
+ * Discovery is done through sending of D1 association frame and parsing
+ * the D2 association frame response. Only wssid from D2 will be included
+ * in neighbor cache, rest is just displayed to user and forgotten.
+ *
+ * The discovery is not done in parallel. This is simple and enables us to
+ * maintain only one association context.
+ *
+ * The discovery of one neighbor does not affect the other, but if the
+ * discovery of a neighbor fails it is removed from the neighborhood cache.
+ */
+static
+int wlp_discover_all_neighbors(struct wlp *wlp)
+{
+ int result = 0;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_neighbor_e *neighbor, *next;
+
+ list_for_each_entry_safe(neighbor, next, &wlp->neighbors, node) {
+ result = wlp_discover_neighbor(wlp, neighbor);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to discover neighbor "
+ "%02x:%02x, removing from neighborhood. \n",
+ neighbor->uwb_dev->dev_addr.data[1],
+ neighbor->uwb_dev->dev_addr.data[0]);
+ __wlp_neighbor_release(neighbor);
+ }
+ }
+ return result;
+}
+
+static int wlp_add_neighbor_helper(struct device *dev, void *priv)
+{
+ struct wlp *wlp = priv;
+ struct uwb_dev *uwb_dev = to_uwb_dev(dev);
+
+ return wlp_add_neighbor(wlp, uwb_dev);
+}
+
+/**
+ * Discover WLP neighborhood
+ *
+ * Will send D1 association frame to all devices in beacon group that have
+ * discoverable bit set in WLP IE. D2 frames will be received, information
+ * displayed to user in @buf. Partial information (from D2 association
+ * frame) will be cached to assist with future association
+ * requests.
+ *
+ * The discovery of the WLP neighborhood is triggered by the user. This
+ * should occur infrequently and we thus free current cache and re-allocate
+ * memory if needed.
+ *
+ * If one neighbor fails during initial discovery (determining if it is a
+ * neighbor or not), we fail all - note that interaction with neighbor has
+ * not occured at this point so if a failure occurs we know something went wrong
+ * locally. We thus undo everything.
+ */
+ssize_t wlp_discover(struct wlp *wlp)
+{
+ int result = 0;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+
+ d_fnstart(6, dev, "wlp %p \n", wlp);
+ mutex_lock(&wlp->nbmutex);
+ /* Clear current neighborhood cache. */
+ __wlp_neighbors_release(wlp);
+ /* Determine which devices in neighborhood. Repopulate cache. */
+ result = uwb_dev_for_each(wlp->rc, wlp_add_neighbor_helper, wlp);
+ if (result < 0) {
+ /* May have partial neighbor information, release all. */
+ __wlp_neighbors_release(wlp);
+ goto error_dev_for_each;
+ }
+ /* Discover the properties of devices in neighborhood. */
+ result = wlp_discover_all_neighbors(wlp);
+ /* In case of failure we still print our partial results. */
+ if (result < 0) {
+ dev_err(dev, "Unable to fully discover neighborhood. \n");
+ result = 0;
+ }
+error_dev_for_each:
+ mutex_unlock(&wlp->nbmutex);
+ d_fnend(6, dev, "wlp %p \n", wlp);
+ return result;
+}
+
+/**
+ * Handle events from UWB stack
+ *
+ * We handle events conservatively. If a neighbor goes off the air we
+ * remove it from the neighborhood. If an association process is in
+ * progress this function will block waiting for the nbmutex to become
+ * free. The association process will thus be allowed to complete before it
+ * is removed.
+ */
+static
+void wlp_uwb_notifs_cb(void *_wlp, struct uwb_dev *uwb_dev,
+ enum uwb_notifs event)
+{
+ struct wlp *wlp = _wlp;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_neighbor_e *neighbor, *next;
+ int result;
+ switch (event) {
+ case UWB_NOTIF_ONAIR:
+ d_printf(6, dev, "UWB device %02x:%02x is onair\n",
+ uwb_dev->dev_addr.data[1],
+ uwb_dev->dev_addr.data[0]);
+ result = wlp_eda_create_node(&wlp->eda,
+ uwb_dev->mac_addr.data,
+ &uwb_dev->dev_addr);
+ if (result < 0)
+ dev_err(dev, "WLP: Unable to add new neighbor "
+ "%02x:%02x to EDA cache.\n",
+ uwb_dev->dev_addr.data[1],
+ uwb_dev->dev_addr.data[0]);
+ break;
+ case UWB_NOTIF_OFFAIR:
+ d_printf(6, dev, "UWB device %02x:%02x is offair\n",
+ uwb_dev->dev_addr.data[1],
+ uwb_dev->dev_addr.data[0]);
+ wlp_eda_rm_node(&wlp->eda, &uwb_dev->dev_addr);
+ mutex_lock(&wlp->nbmutex);
+ list_for_each_entry_safe(neighbor, next, &wlp->neighbors,
+ node) {
+ if (neighbor->uwb_dev == uwb_dev) {
+ d_printf(6, dev, "Removing device from "
+ "neighborhood.\n");
+ __wlp_neighbor_release(neighbor);
+ }
+ }
+ mutex_unlock(&wlp->nbmutex);
+ break;
+ default:
+ dev_err(dev, "don't know how to handle event %d from uwb\n",
+ event);
+ }
+}
+
+int wlp_setup(struct wlp *wlp, struct uwb_rc *rc)
+{
+ struct device *dev = &rc->uwb_dev.dev;
+ int result;
+
+ d_fnstart(6, dev, "wlp %p\n", wlp);
+ BUG_ON(wlp->fill_device_info == NULL);
+ BUG_ON(wlp->xmit_frame == NULL);
+ BUG_ON(wlp->stop_queue == NULL);
+ BUG_ON(wlp->start_queue == NULL);
+ wlp->rc = rc;
+ wlp_eda_init(&wlp->eda);/* Set up address cache */
+ wlp->uwb_notifs_handler.cb = wlp_uwb_notifs_cb;
+ wlp->uwb_notifs_handler.data = wlp;
+ uwb_notifs_register(rc, &wlp->uwb_notifs_handler);
+
+ uwb_pal_init(&wlp->pal);
+ result = uwb_pal_register(rc, &wlp->pal);
+ if (result < 0)
+ uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler);
+
+ d_fnend(6, dev, "wlp %p, result = %d\n", wlp, result);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wlp_setup);
+
+void wlp_remove(struct wlp *wlp)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ d_fnstart(6, dev, "wlp %p\n", wlp);
+ wlp_neighbors_release(wlp);
+ uwb_pal_unregister(wlp->rc, &wlp->pal);
+ uwb_notifs_deregister(wlp->rc, &wlp->uwb_notifs_handler);
+ wlp_eda_release(&wlp->eda);
+ mutex_lock(&wlp->mutex);
+ if (wlp->dev_info != NULL)
+ kfree(wlp->dev_info);
+ mutex_unlock(&wlp->mutex);
+ wlp->rc = NULL;
+ /* We have to use NULL here because this function can be called
+ * when the device disappeared. */
+ d_fnend(6, NULL, "wlp %p\n", wlp);
+}
+EXPORT_SYMBOL_GPL(wlp_remove);
diff --git a/drivers/uwb/wlp/wss-lc.c b/drivers/uwb/wlp/wss-lc.c
new file mode 100644
index 000000000000..5ac1a38fdd43
--- /dev/null
+++ b/drivers/uwb/wlp/wss-lc.c
@@ -0,0 +1,1056 @@
+/*
+ * WiMedia Logical Link Control Protocol (WLP)
+ *
+ * Copyright (C) 2007 Intel Corporation
+ * Reinette Chatre <reinette.chatre@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * Implementation of the WLP association protocol.
+ *
+ * FIXME: Docs
+ *
+ * A UWB network interface will configure a WSS through wlp_wss_setup() after
+ * the interface has been assigned a MAC address, typically after
+ * "ifconfig" has been called. When the interface goes down it should call
+ * wlp_wss_remove().
+ *
+ * When the WSS is ready for use the user interacts via sysfs to create,
+ * discover, and activate WSS.
+ *
+ * wlp_wss_enroll_activate()
+ *
+ * wlp_wss_create_activate()
+ * wlp_wss_set_wssid_hash()
+ * wlp_wss_comp_wssid_hash()
+ * wlp_wss_sel_bcast_addr()
+ * wlp_wss_sysfs_add()
+ *
+ * Called when no more references to WSS exist:
+ * wlp_wss_release()
+ * wlp_wss_reset()
+ */
+
+#include <linux/etherdevice.h> /* for is_valid_ether_addr */
+#include <linux/skbuff.h>
+#include <linux/wlp.h>
+#define D_LOCAL 5
+#include <linux/uwb/debug.h>
+#include "wlp-internal.h"
+
+
+size_t wlp_wss_key_print(char *buf, size_t bufsize, u8 *key)
+{
+ size_t result;
+
+ result = scnprintf(buf, bufsize,
+ "%02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x",
+ key[0], key[1], key[2], key[3],
+ key[4], key[5], key[6], key[7],
+ key[8], key[9], key[10], key[11],
+ key[12], key[13], key[14], key[15]);
+ return result;
+}
+
+/**
+ * Compute WSSID hash
+ * WLP Draft 0.99 [7.2.1]
+ *
+ * The WSSID hash for a WSSID is the result of an octet-wise exclusive-OR
+ * of all octets in the WSSID.
+ */
+static
+u8 wlp_wss_comp_wssid_hash(struct wlp_uuid *wssid)
+{
+ return wssid->data[0] ^ wssid->data[1] ^ wssid->data[2]
+ ^ wssid->data[3] ^ wssid->data[4] ^ wssid->data[5]
+ ^ wssid->data[6] ^ wssid->data[7] ^ wssid->data[8]
+ ^ wssid->data[9] ^ wssid->data[10] ^ wssid->data[11]
+ ^ wssid->data[12] ^ wssid->data[13] ^ wssid->data[14]
+ ^ wssid->data[15];
+}
+
+/**
+ * Select a multicast EUI-48 for the WSS broadcast address.
+ * WLP Draft 0.99 [7.2.1]
+ *
+ * Selected based on the WiMedia Alliance OUI, 00-13-88, within the WLP
+ * range, [01-13-88-00-01-00, 01-13-88-00-01-FF] inclusive.
+ *
+ * This address is currently hardcoded.
+ * FIXME?
+ */
+static
+struct uwb_mac_addr wlp_wss_sel_bcast_addr(struct wlp_wss *wss)
+{
+ struct uwb_mac_addr bcast = {
+ .data = { 0x01, 0x13, 0x88, 0x00, 0x01, 0x00 }
+ };
+ return bcast;
+}
+
+/**
+ * Clear the contents of the WSS structure - all except kobj, mutex, virtual
+ *
+ * We do not want to reinitialize - the internal kobj should not change as
+ * it still points to the parent received during setup. The mutex should
+ * remain also. We thus just reset values individually.
+ * The virutal address assigned to WSS will remain the same for the
+ * lifetime of the WSS. We only reset the fields that can change during its
+ * lifetime.
+ */
+void wlp_wss_reset(struct wlp_wss *wss)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ d_fnstart(5, dev, "wss (%p) \n", wss);
+ memset(&wss->wssid, 0, sizeof(wss->wssid));
+ wss->hash = 0;
+ memset(&wss->name[0], 0, sizeof(wss->name));
+ memset(&wss->bcast, 0, sizeof(wss->bcast));
+ wss->secure_status = WLP_WSS_UNSECURE;
+ memset(&wss->master_key[0], 0, sizeof(wss->master_key));
+ wss->tag = 0;
+ wss->state = WLP_WSS_STATE_NONE;
+ d_fnend(5, dev, "wss (%p) \n", wss);
+}
+
+/**
+ * Create sysfs infrastructure for WSS
+ *
+ * The WSS is configured to have the interface as parent (see wlp_wss_setup())
+ * a new sysfs directory that includes wssid as its name is created in the
+ * interface's sysfs directory. The group of files interacting with WSS are
+ * created also.
+ */
+static
+int wlp_wss_sysfs_add(struct wlp_wss *wss, char *wssid_str)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result;
+
+ d_fnstart(5, dev, "wss (%p), wssid: %s\n", wss, wssid_str);
+ result = kobject_set_name(&wss->kobj, "wss-%s", wssid_str);
+ if (result < 0)
+ return result;
+ wss->kobj.ktype = &wss_ktype;
+ result = kobject_init_and_add(&wss->kobj,
+ &wss_ktype, wss->kobj.parent, "wlp");
+ if (result < 0) {
+ dev_err(dev, "WLP: Cannot register WSS kobject.\n");
+ goto error_kobject_register;
+ }
+ result = sysfs_create_group(&wss->kobj, &wss_attr_group);
+ if (result < 0) {
+ dev_err(dev, "WLP: Cannot register WSS attributes: %d\n",
+ result);
+ goto error_sysfs_create_group;
+ }
+ d_fnend(5, dev, "Completed. result = %d \n", result);
+ return 0;
+error_sysfs_create_group:
+
+ kobject_put(&wss->kobj); /* will free name if needed */
+ return result;
+error_kobject_register:
+ kfree(wss->kobj.name);
+ wss->kobj.name = NULL;
+ wss->kobj.ktype = NULL;
+ return result;
+}
+
+
+/**
+ * Release WSS
+ *
+ * No more references exist to this WSS. We should undo everything that was
+ * done in wlp_wss_create_activate() except removing the group. The group
+ * is not removed because an object can be unregistered before the group is
+ * created. We also undo any additional operations on the WSS after this
+ * (addition of members).
+ *
+ * If memory was allocated for the kobject's name then it will
+ * be freed by the kobject system during this time.
+ *
+ * The EDA cache is removed and reinitilized when the WSS is removed. We
+ * thus loose knowledge of members of this WSS at that time and need not do
+ * it here.
+ */
+void wlp_wss_release(struct kobject *kobj)
+{
+ struct wlp_wss *wss = container_of(kobj, struct wlp_wss, kobj);
+
+ wlp_wss_reset(wss);
+}
+
+/**
+ * Enroll into a WSS using provided neighbor as registrar
+ *
+ * First search the neighborhood information to learn which neighbor is
+ * referred to, next proceed with enrollment.
+ *
+ * &wss->mutex is held
+ */
+static
+int wlp_wss_enroll_target(struct wlp_wss *wss, struct wlp_uuid *wssid,
+ struct uwb_dev_addr *dest)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_neighbor_e *neighbor;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ int result = -ENXIO;
+ struct uwb_dev_addr *dev_addr;
+
+ wlp_wss_uuid_print(buf, sizeof(buf), wssid);
+ d_fnstart(5, dev, "wss %p, wssid %s, registrar %02x:%02x \n",
+ wss, buf, dest->data[1], dest->data[0]);
+ mutex_lock(&wlp->nbmutex);
+ list_for_each_entry(neighbor, &wlp->neighbors, node) {
+ dev_addr = &neighbor->uwb_dev->dev_addr;
+ if (!memcmp(dest, dev_addr, sizeof(*dest))) {
+ d_printf(5, dev, "Neighbor %02x:%02x is valid, "
+ "enrolling. \n",
+ dev_addr->data[1], dev_addr->data[0]);
+ result = wlp_enroll_neighbor(wlp, neighbor, wss,
+ wssid);
+ break;
+ }
+ }
+ if (result == -ENXIO)
+ dev_err(dev, "WLP: Cannot find neighbor %02x:%02x. \n",
+ dest->data[1], dest->data[0]);
+ mutex_unlock(&wlp->nbmutex);
+ d_fnend(5, dev, "wss %p, wssid %s, registrar %02x:%02x, result %d \n",
+ wss, buf, dest->data[1], dest->data[0], result);
+ return result;
+}
+
+/**
+ * Enroll into a WSS previously discovered
+ *
+ * User provides WSSID of WSS, search for neighbor that has this WSS
+ * activated and attempt to enroll.
+ *
+ * &wss->mutex is held
+ */
+static
+int wlp_wss_enroll_discovered(struct wlp_wss *wss, struct wlp_uuid *wssid)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct wlp_neighbor_e *neighbor;
+ struct wlp_wssid_e *wssid_e;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ int result = -ENXIO;
+
+ wlp_wss_uuid_print(buf, sizeof(buf), wssid);
+ d_fnstart(5, dev, "wss %p, wssid %s \n", wss, buf);
+ mutex_lock(&wlp->nbmutex);
+ list_for_each_entry(neighbor, &wlp->neighbors, node) {
+ list_for_each_entry(wssid_e, &neighbor->wssid, node) {
+ if (!memcmp(wssid, &wssid_e->wssid, sizeof(*wssid))) {
+ d_printf(5, dev, "Found WSSID %s in neighbor "
+ "%02x:%02x cache. \n", buf,
+ neighbor->uwb_dev->dev_addr.data[1],
+ neighbor->uwb_dev->dev_addr.data[0]);
+ result = wlp_enroll_neighbor(wlp, neighbor,
+ wss, wssid);
+ if (result == 0) /* enrollment success */
+ goto out;
+ break;
+ }
+ }
+ }
+out:
+ if (result == -ENXIO)
+ dev_err(dev, "WLP: Cannot find WSSID %s in cache. \n", buf);
+ mutex_unlock(&wlp->nbmutex);
+ d_fnend(5, dev, "wss %p, wssid %s, result %d \n", wss, buf, result);
+ return result;
+}
+
+/**
+ * Enroll into WSS with provided WSSID, registrar may be provided
+ *
+ * @wss: out WSS that will be enrolled
+ * @wssid: wssid of neighboring WSS that we want to enroll in
+ * @devaddr: registrar can be specified, will be broadcast (ff:ff) if any
+ * neighbor can be used as registrar.
+ *
+ * &wss->mutex is held
+ */
+static
+int wlp_wss_enroll(struct wlp_wss *wss, struct wlp_uuid *wssid,
+ struct uwb_dev_addr *devaddr)
+{
+ int result;
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ struct uwb_dev_addr bcast = {.data = {0xff, 0xff} };
+
+ wlp_wss_uuid_print(buf, sizeof(buf), wssid);
+ if (wss->state != WLP_WSS_STATE_NONE) {
+ dev_err(dev, "WLP: Already enrolled in WSS %s.\n", buf);
+ result = -EEXIST;
+ goto error;
+ }
+ if (!memcmp(&bcast, devaddr, sizeof(bcast))) {
+ d_printf(5, dev, "Request to enroll in discovered WSS "
+ "with WSSID %s \n", buf);
+ result = wlp_wss_enroll_discovered(wss, wssid);
+ } else {
+ d_printf(5, dev, "Request to enroll in WSSID %s with "
+ "registrar %02x:%02x\n", buf, devaddr->data[1],
+ devaddr->data[0]);
+ result = wlp_wss_enroll_target(wss, wssid, devaddr);
+ }
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to enroll into WSS %s, result %d \n",
+ buf, result);
+ goto error;
+ }
+ d_printf(2, dev, "Successfully enrolled into WSS %s \n", buf);
+ result = wlp_wss_sysfs_add(wss, buf);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to set up sysfs for WSS kobject.\n");
+ wlp_wss_reset(wss);
+ }
+error:
+ return result;
+
+}
+
+/**
+ * Activate given WSS
+ *
+ * Prior to activation a WSS must be enrolled. To activate a WSS a device
+ * includes the WSS hash in the WLP IE in its beacon in each superframe.
+ * WLP 0.99 [7.2.5].
+ *
+ * The WSS tag is also computed at this time. We only support one activated
+ * WSS so we can use the hash as a tag - there will never be a conflict.
+ *
+ * We currently only support one activated WSS so only one WSS hash is
+ * included in the WLP IE.
+ */
+static
+int wlp_wss_activate(struct wlp_wss *wss)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct uwb_rc *uwb_rc = wlp->rc;
+ int result;
+ struct {
+ struct wlp_ie wlp_ie;
+ u8 hash; /* only include one hash */
+ } ie_data;
+
+ d_fnstart(5, dev, "Activating WSS %p. \n", wss);
+ BUG_ON(wss->state != WLP_WSS_STATE_ENROLLED);
+ wss->hash = wlp_wss_comp_wssid_hash(&wss->wssid);
+ wss->tag = wss->hash;
+ memset(&ie_data, 0, sizeof(ie_data));
+ ie_data.wlp_ie.hdr.element_id = UWB_IE_WLP;
+ ie_data.wlp_ie.hdr.length = sizeof(ie_data) - sizeof(struct uwb_ie_hdr);
+ ie_data.wlp_ie.hash_length = sizeof(ie_data.hash);
+ ie_data.hash = wss->hash;
+ ie_data.wlp_ie.capabilities = cpu_to_le16(ie_data.wlp_ie.capabilities);
+ result = uwb_rc_ie_add(uwb_rc, &ie_data.wlp_ie.hdr,
+ sizeof(ie_data));
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to add WLP IE to beacon. "
+ "result = %d.\n", result);
+ goto error_wlp_ie;
+ }
+ wss->state = WLP_WSS_STATE_ACTIVE;
+ result = 0;
+error_wlp_ie:
+ d_fnend(5, dev, "Activating WSS %p, result = %d \n", wss, result);
+ return result;
+}
+
+/**
+ * Enroll in and activate WSS identified by provided WSSID
+ *
+ * The neighborhood cache should contain a list of all neighbors and the
+ * WSS they have activated. Based on that cache we search which neighbor we
+ * can perform the association process with. The user also has option to
+ * specify which neighbor it prefers as registrar.
+ * Successful enrollment is followed by activation.
+ * Successful activation will create the sysfs directory containing
+ * specific information regarding this WSS.
+ */
+int wlp_wss_enroll_activate(struct wlp_wss *wss, struct wlp_uuid *wssid,
+ struct uwb_dev_addr *devaddr)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = 0;
+ char buf[WLP_WSS_UUID_STRSIZE];
+
+ d_fnstart(5, dev, "Enrollment and activation requested. \n");
+ mutex_lock(&wss->mutex);
+ result = wlp_wss_enroll(wss, wssid, devaddr);
+ if (result < 0) {
+ wlp_wss_uuid_print(buf, sizeof(buf), &wss->wssid);
+ dev_err(dev, "WLP: Enrollment into WSS %s failed.\n", buf);
+ goto error_enroll;
+ }
+ result = wlp_wss_activate(wss);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to activate WSS. Undoing enrollment "
+ "result = %d \n", result);
+ /* Undo enrollment */
+ wlp_wss_reset(wss);
+ goto error_activate;
+ }
+error_activate:
+error_enroll:
+ mutex_unlock(&wss->mutex);
+ d_fnend(5, dev, "Completed. result = %d \n", result);
+ return result;
+}
+
+/**
+ * Create, enroll, and activate a new WSS
+ *
+ * @wssid: new wssid provided by user
+ * @name: WSS name requested by used.
+ * @sec_status: security status requested by user
+ *
+ * A user requested the creation of a new WSS. All operations are done
+ * locally. The new WSS will be stored locally, the hash will be included
+ * in the WLP IE, and the sysfs infrastructure for this WSS will be
+ * created.
+ */
+int wlp_wss_create_activate(struct wlp_wss *wss, struct wlp_uuid *wssid,
+ char *name, unsigned sec_status, unsigned accept)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = 0;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ d_fnstart(5, dev, "Request to create new WSS.\n");
+ result = wlp_wss_uuid_print(buf, sizeof(buf), wssid);
+ d_printf(5, dev, "Request to create WSS: WSSID=%s, name=%s, "
+ "sec_status=%u, accepting enrollment=%u \n",
+ buf, name, sec_status, accept);
+ if (!mutex_trylock(&wss->mutex)) {
+ dev_err(dev, "WLP: WLP association session in progress.\n");
+ return -EBUSY;
+ }
+ if (wss->state != WLP_WSS_STATE_NONE) {
+ dev_err(dev, "WLP: WSS already exists. Not creating new.\n");
+ result = -EEXIST;
+ goto out;
+ }
+ if (wss->kobj.parent == NULL) {
+ dev_err(dev, "WLP: WSS parent not ready. Is network interface "
+ "up?\n");
+ result = -ENXIO;
+ goto out;
+ }
+ if (sec_status == WLP_WSS_SECURE) {
+ dev_err(dev, "WLP: FIXME Creation of secure WSS not "
+ "supported yet.\n");
+ result = -EINVAL;
+ goto out;
+ }
+ wss->wssid = *wssid;
+ memcpy(wss->name, name, sizeof(wss->name));
+ wss->bcast = wlp_wss_sel_bcast_addr(wss);
+ wss->secure_status = sec_status;
+ wss->accept_enroll = accept;
+ /*wss->virtual_addr is initialized in call to wlp_wss_setup*/
+ /* sysfs infrastructure */
+ result = wlp_wss_sysfs_add(wss, buf);
+ if (result < 0) {
+ dev_err(dev, "Cannot set up sysfs for WSS kobject.\n");
+ wlp_wss_reset(wss);
+ goto out;
+ } else
+ result = 0;
+ wss->state = WLP_WSS_STATE_ENROLLED;
+ result = wlp_wss_activate(wss);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to activate WSS. Undoing "
+ "enrollment\n");
+ wlp_wss_reset(wss);
+ goto out;
+ }
+ result = 0;
+out:
+ mutex_unlock(&wss->mutex);
+ d_fnend(5, dev, "Completed. result = %d \n", result);
+ return result;
+}
+
+/**
+ * Determine if neighbor has WSS activated
+ *
+ * @returns: 1 if neighbor has WSS activated, zero otherwise
+ *
+ * This can be done in two ways:
+ * - send a C1 frame, parse C2/F0 response
+ * - examine the WLP IE sent by the neighbor
+ *
+ * The WLP IE is not fully supported in hardware so we use the C1/C2 frame
+ * exchange to determine if a WSS is activated. Using the WLP IE should be
+ * faster and should be used when it becomes possible.
+ */
+int wlp_wss_is_active(struct wlp *wlp, struct wlp_wss *wss,
+ struct uwb_dev_addr *dev_addr)
+{
+ int result = 0;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ DECLARE_COMPLETION_ONSTACK(completion);
+ struct wlp_session session;
+ struct sk_buff *skb;
+ struct wlp_frame_assoc *resp;
+ struct wlp_uuid wssid;
+
+ wlp_wss_uuid_print(buf, sizeof(buf), &wss->wssid);
+ d_fnstart(5, dev, "wlp %p, wss %p (wssid %s), neighbor %02x:%02x \n",
+ wlp, wss, buf, dev_addr->data[1], dev_addr->data[0]);
+ mutex_lock(&wlp->mutex);
+ /* Send C1 association frame */
+ result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_C1);
+ if (result < 0) {
+ dev_err(dev, "Unable to send C1 frame to neighbor "
+ "%02x:%02x (%d)\n", dev_addr->data[1],
+ dev_addr->data[0], result);
+ result = 0;
+ goto out;
+ }
+ /* Create session, wait for response */
+ session.exp_message = WLP_ASSOC_C2;
+ session.cb = wlp_session_cb;
+ session.cb_priv = &completion;
+ session.neighbor_addr = *dev_addr;
+ BUG_ON(wlp->session != NULL);
+ wlp->session = &session;
+ /* Wait for C2/F0 frame */
+ result = wait_for_completion_interruptible_timeout(&completion,
+ WLP_PER_MSG_TIMEOUT * HZ);
+ if (result == 0) {
+ dev_err(dev, "Timeout while sending C1 to neighbor "
+ "%02x:%02x.\n", dev_addr->data[1],
+ dev_addr->data[0]);
+ goto out;
+ }
+ if (result < 0) {
+ dev_err(dev, "Unable to send C1 to neighbor %02x:%02x.\n",
+ dev_addr->data[1], dev_addr->data[0]);
+ result = 0;
+ goto out;
+ }
+ /* Parse message in session->data: it will be either C2 or F0 */
+ skb = session.data;
+ resp = (void *) skb->data;
+ d_printf(5, dev, "Received response to C1 frame. \n");
+ d_dump(5, dev, skb->data, skb->len > 72 ? 72 : skb->len);
+ if (resp->type == WLP_ASSOC_F0) {
+ result = wlp_parse_f0(wlp, skb);
+ if (result < 0)
+ dev_err(dev, "WLP: unable to parse incoming F0 "
+ "frame from neighbor %02x:%02x.\n",
+ dev_addr->data[1], dev_addr->data[0]);
+ result = 0;
+ goto error_resp_parse;
+ }
+ /* WLP version and message type fields have already been parsed */
+ result = wlp_get_wssid(wlp, (void *)resp + sizeof(*resp), &wssid,
+ skb->len - sizeof(*resp));
+ if (result < 0) {
+ dev_err(dev, "WLP: unable to obtain WSSID from C2 frame.\n");
+ result = 0;
+ goto error_resp_parse;
+ }
+ if (!memcmp(&wssid, &wss->wssid, sizeof(wssid))) {
+ d_printf(5, dev, "WSSID in C2 frame matches local "
+ "active WSS.\n");
+ result = 1;
+ } else {
+ dev_err(dev, "WLP: Received a C2 frame without matching "
+ "WSSID.\n");
+ result = 0;
+ }
+error_resp_parse:
+ kfree_skb(skb);
+out:
+ wlp->session = NULL;
+ mutex_unlock(&wlp->mutex);
+ d_fnend(5, dev, "wlp %p, wss %p (wssid %s), neighbor %02x:%02x \n",
+ wlp, wss, buf, dev_addr->data[1], dev_addr->data[0]);
+ return result;
+}
+
+/**
+ * Activate connection with neighbor by updating EDA cache
+ *
+ * @wss: local WSS to which neighbor wants to connect
+ * @dev_addr: neighbor's address
+ * @wssid: neighbor's WSSID - must be same as our WSS's WSSID
+ * @tag: neighbor's WSS tag used to identify frames transmitted by it
+ * @virt_addr: neighbor's virtual EUI-48
+ */
+static
+int wlp_wss_activate_connection(struct wlp *wlp, struct wlp_wss *wss,
+ struct uwb_dev_addr *dev_addr,
+ struct wlp_uuid *wssid, u8 *tag,
+ struct uwb_mac_addr *virt_addr)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = 0;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ wlp_wss_uuid_print(buf, sizeof(buf), wssid);
+ d_fnstart(5, dev, "wlp %p, wss %p, wssid %s, tag %u, virtual "
+ "%02x:%02x:%02x:%02x:%02x:%02x \n", wlp, wss, buf, *tag,
+ virt_addr->data[0], virt_addr->data[1], virt_addr->data[2],
+ virt_addr->data[3], virt_addr->data[4], virt_addr->data[5]);
+
+ if (!memcmp(wssid, &wss->wssid, sizeof(*wssid))) {
+ d_printf(5, dev, "WSSID from neighbor frame matches local "
+ "active WSS.\n");
+ /* Update EDA cache */
+ result = wlp_eda_update_node(&wlp->eda, dev_addr, wss,
+ (void *) virt_addr->data, *tag,
+ WLP_WSS_CONNECTED);
+ if (result < 0)
+ dev_err(dev, "WLP: Unable to update EDA cache "
+ "with new connected neighbor information.\n");
+ } else {
+ dev_err(dev, "WLP: Neighbor does not have matching "
+ "WSSID.\n");
+ result = -EINVAL;
+ }
+
+ d_fnend(5, dev, "wlp %p, wss %p, wssid %s, tag %u, virtual "
+ "%02x:%02x:%02x:%02x:%02x:%02x, result = %d \n",
+ wlp, wss, buf, *tag,
+ virt_addr->data[0], virt_addr->data[1], virt_addr->data[2],
+ virt_addr->data[3], virt_addr->data[4], virt_addr->data[5],
+ result);
+
+ return result;
+}
+
+/**
+ * Connect to WSS neighbor
+ *
+ * Use C3/C4 exchange to determine if neighbor has WSS activated and
+ * retrieve the WSS tag and virtual EUI-48 of the neighbor.
+ */
+static
+int wlp_wss_connect_neighbor(struct wlp *wlp, struct wlp_wss *wss,
+ struct uwb_dev_addr *dev_addr)
+{
+ int result;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ char buf[WLP_WSS_UUID_STRSIZE];
+ struct wlp_uuid wssid;
+ u8 tag;
+ struct uwb_mac_addr virt_addr;
+ DECLARE_COMPLETION_ONSTACK(completion);
+ struct wlp_session session;
+ struct wlp_frame_assoc *resp;
+ struct sk_buff *skb;
+
+ wlp_wss_uuid_print(buf, sizeof(buf), &wss->wssid);
+ d_fnstart(5, dev, "wlp %p, wss %p (wssid %s), neighbor %02x:%02x \n",
+ wlp, wss, buf, dev_addr->data[1], dev_addr->data[0]);
+ mutex_lock(&wlp->mutex);
+ /* Send C3 association frame */
+ result = wlp_send_assoc_frame(wlp, wss, dev_addr, WLP_ASSOC_C3);
+ if (result < 0) {
+ dev_err(dev, "Unable to send C3 frame to neighbor "
+ "%02x:%02x (%d)\n", dev_addr->data[1],
+ dev_addr->data[0], result);
+ goto out;
+ }
+ /* Create session, wait for response */
+ session.exp_message = WLP_ASSOC_C4;
+ session.cb = wlp_session_cb;
+ session.cb_priv = &completion;
+ session.neighbor_addr = *dev_addr;
+ BUG_ON(wlp->session != NULL);
+ wlp->session = &session;
+ /* Wait for C4/F0 frame */
+ result = wait_for_completion_interruptible_timeout(&completion,
+ WLP_PER_MSG_TIMEOUT * HZ);
+ if (result == 0) {
+ dev_err(dev, "Timeout while sending C3 to neighbor "
+ "%02x:%02x.\n", dev_addr->data[1],
+ dev_addr->data[0]);
+ result = -ETIMEDOUT;
+ goto out;
+ }
+ if (result < 0) {
+ dev_err(dev, "Unable to send C3 to neighbor %02x:%02x.\n",
+ dev_addr->data[1], dev_addr->data[0]);
+ goto out;
+ }
+ /* Parse message in session->data: it will be either C4 or F0 */
+ skb = session.data;
+ resp = (void *) skb->data;
+ d_printf(5, dev, "Received response to C3 frame. \n");
+ d_dump(5, dev, skb->data, skb->len > 72 ? 72 : skb->len);
+ if (resp->type == WLP_ASSOC_F0) {
+ result = wlp_parse_f0(wlp, skb);
+ if (result < 0)
+ dev_err(dev, "WLP: unable to parse incoming F0 "
+ "frame from neighbor %02x:%02x.\n",
+ dev_addr->data[1], dev_addr->data[0]);
+ result = -EINVAL;
+ goto error_resp_parse;
+ }
+ result = wlp_parse_c3c4_frame(wlp, skb, &wssid, &tag, &virt_addr);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to parse C4 frame from neighbor.\n");
+ goto error_resp_parse;
+ }
+ result = wlp_wss_activate_connection(wlp, wss, dev_addr, &wssid, &tag,
+ &virt_addr);
+ if (result < 0) {
+ dev_err(dev, "WLP: Unable to activate connection to "
+ "neighbor %02x:%02x.\n", dev_addr->data[1],
+ dev_addr->data[0]);
+ goto error_resp_parse;
+ }
+error_resp_parse:
+ kfree_skb(skb);
+out:
+ /* Record that we unsuccessfully tried to connect to this neighbor */
+ if (result < 0)
+ wlp_eda_update_node_state(&wlp->eda, dev_addr,
+ WLP_WSS_CONNECT_FAILED);
+ wlp->session = NULL;
+ mutex_unlock(&wlp->mutex);
+ d_fnend(5, dev, "wlp %p, wss %p (wssid %s), neighbor %02x:%02x \n",
+ wlp, wss, buf, dev_addr->data[1], dev_addr->data[0]);
+ return result;
+}
+
+/**
+ * Connect to neighbor with common WSS, send pending frame
+ *
+ * This function is scheduled when a frame is destined to a neighbor with
+ * which we do not have a connection. A copy of the EDA cache entry is
+ * provided - not the actual cache entry (because it is protected by a
+ * spinlock).
+ *
+ * First determine if neighbor has the same WSS activated, connect if it
+ * does. The C3/C4 exchange is dual purpose to determine if neighbor has
+ * WSS activated and proceed with the connection.
+ *
+ * The frame that triggered the connection setup is sent after connection
+ * setup.
+ *
+ * network queue is stopped - we need to restart when done
+ *
+ */
+static
+void wlp_wss_connect_send(struct work_struct *ws)
+{
+ struct wlp_assoc_conn_ctx *conn_ctx = container_of(ws,
+ struct wlp_assoc_conn_ctx,
+ ws);
+ struct wlp *wlp = conn_ctx->wlp;
+ struct sk_buff *skb = conn_ctx->skb;
+ struct wlp_eda_node *eda_entry = &conn_ctx->eda_entry;
+ struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr;
+ struct wlp_wss *wss = &wlp->wss;
+ int result;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ char buf[WLP_WSS_UUID_STRSIZE];
+
+ mutex_lock(&wss->mutex);
+ wlp_wss_uuid_print(buf, sizeof(buf), &wss->wssid);
+ d_fnstart(5, dev, "wlp %p, wss %p (wssid %s), neighbor %02x:%02x \n",
+ wlp, wss, buf, dev_addr->data[1], dev_addr->data[0]);
+ if (wss->state < WLP_WSS_STATE_ACTIVE) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Attempting to connect with "
+ "WSS that is not active or connected.\n");
+ dev_kfree_skb(skb);
+ goto out;
+ }
+ /* Establish connection - send C3 rcv C4 */
+ result = wlp_wss_connect_neighbor(wlp, wss, dev_addr);
+ if (result < 0) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Unable to establish connection "
+ "with neighbor %02x:%02x.\n",
+ dev_addr->data[1], dev_addr->data[0]);
+ dev_kfree_skb(skb);
+ goto out;
+ }
+ /* EDA entry changed, update the local copy being used */
+ result = wlp_copy_eda_node(&wlp->eda, dev_addr, eda_entry);
+ if (result < 0) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Cannot find EDA entry for "
+ "neighbor %02x:%02x \n",
+ dev_addr->data[1], dev_addr->data[0]);
+ }
+ result = wlp_wss_prep_hdr(wlp, eda_entry, skb);
+ if (result < 0) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Unable to prepare frame header for "
+ "transmission (neighbor %02x:%02x). \n",
+ dev_addr->data[1], dev_addr->data[0]);
+ dev_kfree_skb(skb);
+ goto out;
+ }
+ BUG_ON(wlp->xmit_frame == NULL);
+ result = wlp->xmit_frame(wlp, skb, dev_addr);
+ if (result < 0) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Unable to transmit frame: %d\n",
+ result);
+ if (result == -ENXIO)
+ dev_err(dev, "WLP: Is network interface up? \n");
+ /* We could try again ... */
+ dev_kfree_skb(skb);/*we need to free if tx fails */
+ }
+out:
+ kfree(conn_ctx);
+ BUG_ON(wlp->start_queue == NULL);
+ wlp->start_queue(wlp);
+ mutex_unlock(&wss->mutex);
+ d_fnend(5, dev, "wlp %p, wss %p (wssid %s)\n", wlp, wss, buf);
+}
+
+/**
+ * Add WLP header to outgoing skb
+ *
+ * @eda_entry: pointer to neighbor's entry in the EDA cache
+ * @_skb: skb containing data destined to the neighbor
+ */
+int wlp_wss_prep_hdr(struct wlp *wlp, struct wlp_eda_node *eda_entry,
+ void *_skb)
+{
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = 0;
+ unsigned char *eth_addr = eda_entry->eth_addr;
+ struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr;
+ struct sk_buff *skb = _skb;
+ struct wlp_frame_std_abbrv_hdr *std_hdr;
+
+ d_fnstart(6, dev, "wlp %p \n", wlp);
+ if (eda_entry->state == WLP_WSS_CONNECTED) {
+ /* Add WLP header */
+ BUG_ON(skb_headroom(skb) < sizeof(*std_hdr));
+ std_hdr = (void *) __skb_push(skb, sizeof(*std_hdr));
+ std_hdr->hdr.mux_hdr = cpu_to_le16(WLP_PROTOCOL_ID);
+ std_hdr->hdr.type = WLP_FRAME_STANDARD;
+ std_hdr->tag = eda_entry->wss->tag;
+ } else {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Destination neighbor (Ethernet: "
+ "%02x:%02x:%02x:%02x:%02x:%02x, Dev: "
+ "%02x:%02x) is not connected. \n", eth_addr[0],
+ eth_addr[1], eth_addr[2], eth_addr[3],
+ eth_addr[4], eth_addr[5], dev_addr->data[1],
+ dev_addr->data[0]);
+ result = -EINVAL;
+ }
+ d_fnend(6, dev, "wlp %p \n", wlp);
+ return result;
+}
+
+
+/**
+ * Prepare skb for neighbor: connect if not already and prep WLP header
+ *
+ * This function is called in interrupt context, but it needs to sleep. We
+ * temporarily stop the net queue to establish the WLP connection.
+ * Setup of the WLP connection and restart of queue is scheduled
+ * on the default work queue.
+ *
+ * run with eda->lock held (spinlock)
+ */
+int wlp_wss_connect_prep(struct wlp *wlp, struct wlp_eda_node *eda_entry,
+ void *_skb)
+{
+ int result = 0;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr;
+ unsigned char *eth_addr = eda_entry->eth_addr;
+ struct sk_buff *skb = _skb;
+ struct wlp_assoc_conn_ctx *conn_ctx;
+
+ d_fnstart(5, dev, "wlp %p\n", wlp);
+ d_printf(5, dev, "To neighbor %02x:%02x with eth "
+ "%02x:%02x:%02x:%02x:%02x:%02x\n", dev_addr->data[1],
+ dev_addr->data[0], eth_addr[0], eth_addr[1], eth_addr[2],
+ eth_addr[3], eth_addr[4], eth_addr[5]);
+ if (eda_entry->state == WLP_WSS_UNCONNECTED) {
+ /* We don't want any more packets while we set up connection */
+ BUG_ON(wlp->stop_queue == NULL);
+ wlp->stop_queue(wlp);
+ conn_ctx = kmalloc(sizeof(*conn_ctx), GFP_ATOMIC);
+ if (conn_ctx == NULL) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Unable to allocate memory "
+ "for connection handling.\n");
+ result = -ENOMEM;
+ goto out;
+ }
+ conn_ctx->wlp = wlp;
+ conn_ctx->skb = skb;
+ conn_ctx->eda_entry = *eda_entry;
+ INIT_WORK(&conn_ctx->ws, wlp_wss_connect_send);
+ schedule_work(&conn_ctx->ws);
+ result = 1;
+ } else if (eda_entry->state == WLP_WSS_CONNECT_FAILED) {
+ /* Previous connection attempts failed, don't retry - see
+ * conditions for connection in WLP 0.99 [7.6.2] */
+ if (printk_ratelimit())
+ dev_err(dev, "Could not connect to neighbor "
+ "previously. Not retrying. \n");
+ result = -ENONET;
+ goto out;
+ } else { /* eda_entry->state == WLP_WSS_CONNECTED */
+ d_printf(5, dev, "Neighbor is connected, preparing frame.\n");
+ result = wlp_wss_prep_hdr(wlp, eda_entry, skb);
+ }
+out:
+ d_fnend(5, dev, "wlp %p, result = %d \n", wlp, result);
+ return result;
+}
+
+/**
+ * Emulate broadcast: copy skb, send copy to neighbor (connect if not already)
+ *
+ * We need to copy skbs in the case where we emulate broadcast through
+ * unicast. We copy instead of clone because we are modifying the data of
+ * the frame after copying ... clones share data so we cannot emulate
+ * broadcast using clones.
+ *
+ * run with eda->lock held (spinlock)
+ */
+int wlp_wss_send_copy(struct wlp *wlp, struct wlp_eda_node *eda_entry,
+ void *_skb)
+{
+ int result = -ENOMEM;
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ struct sk_buff *skb = _skb;
+ struct sk_buff *copy;
+ struct uwb_dev_addr *dev_addr = &eda_entry->dev_addr;
+
+ d_fnstart(5, dev, "to neighbor %02x:%02x, skb (%p) \n",
+ dev_addr->data[1], dev_addr->data[0], skb);
+ copy = skb_copy(skb, GFP_ATOMIC);
+ if (copy == NULL) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Unable to copy skb for "
+ "transmission.\n");
+ goto out;
+ }
+ result = wlp_wss_connect_prep(wlp, eda_entry, copy);
+ if (result < 0) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Unable to connect/send skb "
+ "to neighbor.\n");
+ dev_kfree_skb_irq(copy);
+ goto out;
+ } else if (result == 1)
+ /* Frame will be transmitted separately */
+ goto out;
+ BUG_ON(wlp->xmit_frame == NULL);
+ result = wlp->xmit_frame(wlp, copy, dev_addr);
+ if (result < 0) {
+ if (printk_ratelimit())
+ dev_err(dev, "WLP: Unable to transmit frame: %d\n",
+ result);
+ if ((result == -ENXIO) && printk_ratelimit())
+ dev_err(dev, "WLP: Is network interface up? \n");
+ /* We could try again ... */
+ dev_kfree_skb_irq(copy);/*we need to free if tx fails */
+ }
+out:
+ d_fnend(5, dev, "to neighbor %02x:%02x \n", dev_addr->data[1],
+ dev_addr->data[0]);
+ return result;
+}
+
+
+/**
+ * Setup WSS
+ *
+ * Should be called by network driver after the interface has been given a
+ * MAC address.
+ */
+int wlp_wss_setup(struct net_device *net_dev, struct wlp_wss *wss)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ int result = 0;
+ d_fnstart(5, dev, "wss (%p) \n", wss);
+ mutex_lock(&wss->mutex);
+ wss->kobj.parent = &net_dev->dev.kobj;
+ if (!is_valid_ether_addr(net_dev->dev_addr)) {
+ dev_err(dev, "WLP: Invalid MAC address. Cannot use for"
+ "virtual.\n");
+ result = -EINVAL;
+ goto out;
+ }
+ memcpy(wss->virtual_addr.data, net_dev->dev_addr,
+ sizeof(wss->virtual_addr.data));
+out:
+ mutex_unlock(&wss->mutex);
+ d_fnend(5, dev, "wss (%p) \n", wss);
+ return result;
+}
+EXPORT_SYMBOL_GPL(wlp_wss_setup);
+
+/**
+ * Remove WSS
+ *
+ * Called by client that configured WSS through wlp_wss_setup(). This
+ * function is called when client no longer needs WSS, eg. client shuts
+ * down.
+ *
+ * We remove the WLP IE from the beacon before initiating local cleanup.
+ */
+void wlp_wss_remove(struct wlp_wss *wss)
+{
+ struct wlp *wlp = container_of(wss, struct wlp, wss);
+ struct device *dev = &wlp->rc->uwb_dev.dev;
+ d_fnstart(5, dev, "wss (%p) \n", wss);
+ mutex_lock(&wss->mutex);
+ if (wss->state == WLP_WSS_STATE_ACTIVE)
+ uwb_rc_ie_rm(wlp->rc, UWB_IE_WLP);
+ if (wss->state != WLP_WSS_STATE_NONE) {
+ sysfs_remove_group(&wss->kobj, &wss_attr_group);
+ kobject_put(&wss->kobj);
+ }
+ wss->kobj.parent = NULL;
+ memset(&wss->virtual_addr, 0, sizeof(wss->virtual_addr));
+ /* Cleanup EDA cache */
+ wlp_eda_release(&wlp->eda);
+ wlp_eda_init(&wlp->eda);
+ mutex_unlock(&wss->mutex);
+ d_fnend(5, dev, "wss (%p) \n", wss);
+}
+EXPORT_SYMBOL_GPL(wlp_wss_remove);
diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c
index 24ee96c4e9e9..07b6addbb3c1 100644
--- a/drivers/video/aty/aty128fb.c
+++ b/drivers/video/aty/aty128fb.c
@@ -1872,7 +1872,7 @@ static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_i
struct fb_info *info = pci_get_drvdata(pdev);
struct aty128fb_par *par = info->par;
struct fb_var_screeninfo var;
- char video_card[DEVICE_NAME_SIZE];
+ char video_card[50];
u8 chip_rev;
u32 dac;
diff --git a/drivers/video/aty/radeonfb.h b/drivers/video/aty/radeonfb.h
index c347e38cd0b0..ccbfffd12805 100644
--- a/drivers/video/aty/radeonfb.h
+++ b/drivers/video/aty/radeonfb.h
@@ -289,7 +289,7 @@ struct radeonfb_info {
struct radeon_regs state;
struct radeon_regs init_state;
- char name[DEVICE_NAME_SIZE];
+ char name[50];
unsigned long mmio_base_phys;
unsigned long fb_base_phys;
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index 97aff8db10bf..4be3b46c069b 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -3586,7 +3586,8 @@ static int __init fb_console_init(void)
acquire_console_sem();
fb_register_client(&fbcon_event_notifier);
- fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), "fbcon");
+ fbcon_device = device_create_drvdata(fb_class, NULL, MKDEV(0, 0),
+ NULL, "fbcon");
if (IS_ERR(fbcon_device)) {
printk(KERN_WARNING "Unable to create device "
diff --git a/drivers/video/fb_ddc.c b/drivers/video/fb_ddc.c
index a0df63289b5f..0cf96eb8a60f 100644
--- a/drivers/video/fb_ddc.c
+++ b/drivers/video/fb_ddc.c
@@ -106,6 +106,7 @@ unsigned char *fb_ddc_read(struct i2c_adapter *adapter)
algo_data->setsda(algo_data->data, 1);
algo_data->setscl(algo_data->data, 1);
+ adapter->class |= I2C_CLASS_DDC;
return edid;
}
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index 776f7fcd2fbf..971ef551cfcd 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -1432,8 +1432,9 @@ register_framebuffer(struct fb_info *fb_info)
break;
fb_info->node = i;
- fb_info->dev = device_create(fb_class, fb_info->device,
- MKDEV(FB_MAJOR, i), "fb%d", i);
+ fb_info->dev = device_create_drvdata(fb_class, fb_info->device,
+ MKDEV(FB_MAJOR, i), NULL,
+ "fb%d", i);
if (IS_ERR(fb_info->dev)) {
/* Not fatal */
printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
diff --git a/drivers/video/fsl-diu-fb.c b/drivers/video/fsl-diu-fb.c
index b50bb03cb5ab..0a2785361ca3 100644
--- a/drivers/video/fsl-diu-fb.c
+++ b/drivers/video/fsl-diu-fb.c
@@ -1320,7 +1320,7 @@ static void free_irq_local(int irq)
* Power management hooks. Note that we won't be called from IRQ context,
* unlike the blank functions above, so we may sleep.
*/
-static int fsl_diu_suspend(struct of_device *dev, pm_message_t state)
+static int fsl_diu_suspend(struct of_device *ofdev, pm_message_t state)
{
struct fsl_diu_data *machine_data;
@@ -1330,7 +1330,7 @@ static int fsl_diu_suspend(struct of_device *dev, pm_message_t state)
return 0;
}
-static int fsl_diu_resume(struct of_device *dev)
+static int fsl_diu_resume(struct of_device *ofdev)
{
struct fsl_diu_data *machine_data;
diff --git a/drivers/video/intelfb/intelfb_i2c.c b/drivers/video/intelfb/intelfb_i2c.c
index ca95f09d8b43..fcf9fadbf572 100644
--- a/drivers/video/intelfb/intelfb_i2c.c
+++ b/drivers/video/intelfb/intelfb_i2c.c
@@ -100,7 +100,8 @@ static int intelfb_gpio_getsda(void *data)
static int intelfb_setup_i2c_bus(struct intelfb_info *dinfo,
struct intelfb_i2c_chan *chan,
- const u32 reg, const char *name)
+ const u32 reg, const char *name,
+ int class)
{
int rc;
@@ -108,6 +109,7 @@ static int intelfb_setup_i2c_bus(struct intelfb_info *dinfo,
chan->reg = reg;
snprintf(chan->adapter.name, sizeof(chan->adapter.name),
"intelfb %s", name);
+ chan->adapter.class = class;
chan->adapter.owner = THIS_MODULE;
chan->adapter.id = I2C_HW_B_INTELFB;
chan->adapter.algo_data = &chan->algo;
@@ -145,7 +147,7 @@ void intelfb_create_i2c_busses(struct intelfb_info *dinfo)
/* setup the DDC bus for analog output */
intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].ddc_bus, GPIOA,
- "CRTDDC_A");
+ "CRTDDC_A", I2C_CLASS_DDC);
i++;
/* need to add the output busses for each device
@@ -159,9 +161,9 @@ void intelfb_create_i2c_busses(struct intelfb_info *dinfo)
case INTEL_865G:
dinfo->output[i].type = INTELFB_OUTPUT_DVO;
intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].ddc_bus,
- GPIOD, "DVODDC_D");
+ GPIOD, "DVODDC_D", I2C_CLASS_DDC);
intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].i2c_bus,
- GPIOE, "DVOI2C_E");
+ GPIOE, "DVOI2C_E", 0);
i++;
break;
case INTEL_915G:
@@ -174,7 +176,7 @@ void intelfb_create_i2c_busses(struct intelfb_info *dinfo)
/* SDVO ports have a single control bus - 2 devices */
dinfo->output[i].type = INTELFB_OUTPUT_SDVO;
intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].i2c_bus,
- GPIOE, "SDVOCTRL_E");
+ GPIOE, "SDVOCTRL_E", 0);
/* TODO: initialize the SDVO */
/* I830SDVOInit(pScrn, i, DVOB); */
i++;
diff --git a/drivers/video/matrox/i2c-matroxfb.c b/drivers/video/matrox/i2c-matroxfb.c
index 4baab7be58de..75ee5a12e549 100644
--- a/drivers/video/matrox/i2c-matroxfb.c
+++ b/drivers/video/matrox/i2c-matroxfb.c
@@ -104,7 +104,9 @@ static struct i2c_algo_bit_data matrox_i2c_algo_template =
};
static int i2c_bus_reg(struct i2c_bit_adapter* b, struct matrox_fb_info* minfo,
- unsigned int data, unsigned int clock, const char* name) {
+ unsigned int data, unsigned int clock, const char *name,
+ int class)
+{
int err;
b->minfo = minfo;
@@ -114,6 +116,7 @@ static int i2c_bus_reg(struct i2c_bit_adapter* b, struct matrox_fb_info* minfo,
snprintf(b->adapter.name, sizeof(b->adapter.name), name,
minfo->fbcon.node);
i2c_set_adapdata(&b->adapter, b);
+ b->adapter.class = class;
b->adapter.algo_data = &b->bac;
b->adapter.dev.parent = &ACCESS_FBINFO(pcidev)->dev;
b->bac = matrox_i2c_algo_template;
@@ -159,22 +162,29 @@ static void* i2c_matroxfb_probe(struct matrox_fb_info* minfo) {
switch (ACCESS_FBINFO(chip)) {
case MGA_2064:
case MGA_2164:
- err = i2c_bus_reg(&m2info->ddc1, minfo, DDC1B_DATA, DDC1B_CLK, "DDC:fb%u #0");
+ err = i2c_bus_reg(&m2info->ddc1, minfo,
+ DDC1B_DATA, DDC1B_CLK,
+ "DDC:fb%u #0", I2C_CLASS_DDC);
break;
default:
- err = i2c_bus_reg(&m2info->ddc1, minfo, DDC1_DATA, DDC1_CLK, "DDC:fb%u #0");
+ err = i2c_bus_reg(&m2info->ddc1, minfo,
+ DDC1_DATA, DDC1_CLK,
+ "DDC:fb%u #0", I2C_CLASS_DDC);
break;
}
if (err)
goto fail_ddc1;
if (ACCESS_FBINFO(devflags.dualhead)) {
- err = i2c_bus_reg(&m2info->ddc2, minfo, DDC2_DATA, DDC2_CLK, "DDC:fb%u #1");
+ err = i2c_bus_reg(&m2info->ddc2, minfo,
+ DDC2_DATA, DDC2_CLK,
+ "DDC:fb%u #1", I2C_CLASS_DDC);
if (err == -ENODEV) {
printk(KERN_INFO "i2c-matroxfb: VGA->TV plug detected, DDC unavailable.\n");
} else if (err)
printk(KERN_INFO "i2c-matroxfb: Could not register secondary output i2c bus. Continuing anyway.\n");
/* Register maven bus even on G450/G550 */
- err = i2c_bus_reg(&m2info->maven, minfo, MAT_DATA, MAT_CLK, "MAVEN:fb%u");
+ err = i2c_bus_reg(&m2info->maven, minfo,
+ MAT_DATA, MAT_CLK, "MAVEN:fb%u", 0);
if (err)
printk(KERN_INFO "i2c-matroxfb: Could not register Maven i2c bus. Continuing anyway.\n");
}
diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c
index 274bc93ab7d8..7dcda187d9ba 100644
--- a/drivers/video/pxafb.c
+++ b/drivers/video/pxafb.c
@@ -573,8 +573,8 @@ static int setup_frame_dma(struct pxafb_info *fbi, int dma, int pal,
dma_desc->fdadr = fbi->dma_buff_phys + dma_desc_off;
fbi->fdadr[dma] = fbi->dma_buff_phys + dma_desc_off;
} else {
- pal_desc = &fbi->dma_buff->pal_desc[dma];
- pal_desc_off = offsetof(struct pxafb_dma_buff, dma_desc[pal]);
+ pal_desc = &fbi->dma_buff->pal_desc[pal];
+ pal_desc_off = offsetof(struct pxafb_dma_buff, pal_desc[pal]);
pal_desc->fsadr = fbi->dma_buff_phys + pal * PALETTE_SIZE;
pal_desc->fidr = 0;
@@ -1276,6 +1276,8 @@ static int __init pxafb_map_video_memory(struct pxafb_info *fbi)
fbi->dma_buff_phys = fbi->map_dma;
fbi->palette_cpu = (u16 *) fbi->dma_buff->palette;
+ pr_debug("pxafb: palette_mem_size = 0x%08lx\n", fbi->palette_size*sizeof(u16));
+
#ifdef CONFIG_FB_PXA_SMARTPANEL
fbi->smart_cmds = (uint16_t *) fbi->dma_buff->cmd_buff;
fbi->n_smart_cmds = 0;
diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c
index 619a6f8d65a2..47ed39b52f9c 100644
--- a/drivers/video/xen-fbfront.c
+++ b/drivers/video/xen-fbfront.c
@@ -18,6 +18,7 @@
* frame buffer.
*/
+#include <linux/console.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/fb.h>
@@ -42,37 +43,68 @@ struct xenfb_info {
struct xenfb_page *page;
unsigned long *mfns;
int update_wanted; /* XENFB_TYPE_UPDATE wanted */
+ int feature_resize; /* XENFB_TYPE_RESIZE ok */
+ struct xenfb_resize resize; /* protected by resize_lock */
+ int resize_dpy; /* ditto */
+ spinlock_t resize_lock;
struct xenbus_device *xbdev;
};
-static u32 xenfb_mem_len = XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH / 8;
+#define XENFB_DEFAULT_FB_LEN (XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH / 8)
+enum { KPARAM_MEM, KPARAM_WIDTH, KPARAM_HEIGHT, KPARAM_CNT };
+static int video[KPARAM_CNT] = { 2, XENFB_WIDTH, XENFB_HEIGHT };
+module_param_array(video, int, NULL, 0);
+MODULE_PARM_DESC(video,
+ "Video memory size in MB, width, height in pixels (default 2,800,600)");
+
+static void xenfb_make_preferred_console(void);
static int xenfb_remove(struct xenbus_device *);
-static void xenfb_init_shared_page(struct xenfb_info *);
+static void xenfb_init_shared_page(struct xenfb_info *, struct fb_info *);
static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *);
static void xenfb_disconnect_backend(struct xenfb_info *);
+static void xenfb_send_event(struct xenfb_info *info,
+ union xenfb_out_event *event)
+{
+ u32 prod;
+
+ prod = info->page->out_prod;
+ /* caller ensures !xenfb_queue_full() */
+ mb(); /* ensure ring space available */
+ XENFB_OUT_RING_REF(info->page, prod) = *event;
+ wmb(); /* ensure ring contents visible */
+ info->page->out_prod = prod + 1;
+
+ notify_remote_via_irq(info->irq);
+}
+
static void xenfb_do_update(struct xenfb_info *info,
int x, int y, int w, int h)
{
union xenfb_out_event event;
- u32 prod;
+ memset(&event, 0, sizeof(event));
event.type = XENFB_TYPE_UPDATE;
event.update.x = x;
event.update.y = y;
event.update.width = w;
event.update.height = h;
- prod = info->page->out_prod;
/* caller ensures !xenfb_queue_full() */
- mb(); /* ensure ring space available */
- XENFB_OUT_RING_REF(info->page, prod) = event;
- wmb(); /* ensure ring contents visible */
- info->page->out_prod = prod + 1;
+ xenfb_send_event(info, &event);
+}
- notify_remote_via_irq(info->irq);
+static void xenfb_do_resize(struct xenfb_info *info)
+{
+ union xenfb_out_event event;
+
+ memset(&event, 0, sizeof(event));
+ event.resize = info->resize;
+
+ /* caller ensures !xenfb_queue_full() */
+ xenfb_send_event(info, &event);
}
static int xenfb_queue_full(struct xenfb_info *info)
@@ -84,12 +116,28 @@ static int xenfb_queue_full(struct xenfb_info *info)
return prod - cons == XENFB_OUT_RING_LEN;
}
+static void xenfb_handle_resize_dpy(struct xenfb_info *info)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&info->resize_lock, flags);
+ if (info->resize_dpy) {
+ if (!xenfb_queue_full(info)) {
+ info->resize_dpy = 0;
+ xenfb_do_resize(info);
+ }
+ }
+ spin_unlock_irqrestore(&info->resize_lock, flags);
+}
+
static void xenfb_refresh(struct xenfb_info *info,
int x1, int y1, int w, int h)
{
unsigned long flags;
- int y2 = y1 + h - 1;
int x2 = x1 + w - 1;
+ int y2 = y1 + h - 1;
+
+ xenfb_handle_resize_dpy(info);
if (!info->update_wanted)
return;
@@ -222,6 +270,57 @@ static ssize_t xenfb_write(struct fb_info *p, const char __user *buf,
return res;
}
+static int
+xenfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ struct xenfb_info *xenfb_info;
+ int required_mem_len;
+
+ xenfb_info = info->par;
+
+ if (!xenfb_info->feature_resize) {
+ if (var->xres == video[KPARAM_WIDTH] &&
+ var->yres == video[KPARAM_HEIGHT] &&
+ var->bits_per_pixel == xenfb_info->page->depth) {
+ return 0;
+ }
+ return -EINVAL;
+ }
+
+ /* Can't resize past initial width and height */
+ if (var->xres > video[KPARAM_WIDTH] || var->yres > video[KPARAM_HEIGHT])
+ return -EINVAL;
+
+ required_mem_len = var->xres * var->yres * xenfb_info->page->depth / 8;
+ if (var->bits_per_pixel == xenfb_info->page->depth &&
+ var->xres <= info->fix.line_length / (XENFB_DEPTH / 8) &&
+ required_mem_len <= info->fix.smem_len) {
+ var->xres_virtual = var->xres;
+ var->yres_virtual = var->yres;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static int xenfb_set_par(struct fb_info *info)
+{
+ struct xenfb_info *xenfb_info;
+ unsigned long flags;
+
+ xenfb_info = info->par;
+
+ spin_lock_irqsave(&xenfb_info->resize_lock, flags);
+ xenfb_info->resize.type = XENFB_TYPE_RESIZE;
+ xenfb_info->resize.width = info->var.xres;
+ xenfb_info->resize.height = info->var.yres;
+ xenfb_info->resize.stride = info->fix.line_length;
+ xenfb_info->resize.depth = info->var.bits_per_pixel;
+ xenfb_info->resize.offset = 0;
+ xenfb_info->resize_dpy = 1;
+ spin_unlock_irqrestore(&xenfb_info->resize_lock, flags);
+ return 0;
+}
+
static struct fb_ops xenfb_fb_ops = {
.owner = THIS_MODULE,
.fb_read = fb_sys_read,
@@ -230,6 +329,8 @@ static struct fb_ops xenfb_fb_ops = {
.fb_fillrect = xenfb_fillrect,
.fb_copyarea = xenfb_copyarea,
.fb_imageblit = xenfb_imageblit,
+ .fb_check_var = xenfb_check_var,
+ .fb_set_par = xenfb_set_par,
};
static irqreturn_t xenfb_event_handler(int rq, void *dev_id)
@@ -258,6 +359,8 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
{
struct xenfb_info *info;
struct fb_info *fb_info;
+ int fb_size;
+ int val;
int ret;
info = kzalloc(sizeof(*info), GFP_KERNEL);
@@ -265,18 +368,35 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
return -ENOMEM;
}
+
+ /* Limit kernel param videoram amount to what is in xenstore */
+ if (xenbus_scanf(XBT_NIL, dev->otherend, "videoram", "%d", &val) == 1) {
+ if (val < video[KPARAM_MEM])
+ video[KPARAM_MEM] = val;
+ }
+
+ /* If requested res does not fit in available memory, use default */
+ fb_size = video[KPARAM_MEM] * 1024 * 1024;
+ if (video[KPARAM_WIDTH] * video[KPARAM_HEIGHT] * XENFB_DEPTH / 8
+ > fb_size) {
+ video[KPARAM_WIDTH] = XENFB_WIDTH;
+ video[KPARAM_HEIGHT] = XENFB_HEIGHT;
+ fb_size = XENFB_DEFAULT_FB_LEN;
+ }
+
dev->dev.driver_data = info;
info->xbdev = dev;
info->irq = -1;
info->x1 = info->y1 = INT_MAX;
spin_lock_init(&info->dirty_lock);
+ spin_lock_init(&info->resize_lock);
- info->fb = vmalloc(xenfb_mem_len);
+ info->fb = vmalloc(fb_size);
if (info->fb == NULL)
goto error_nomem;
- memset(info->fb, 0, xenfb_mem_len);
+ memset(info->fb, 0, fb_size);
- info->nr_pages = (xenfb_mem_len + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ info->nr_pages = (fb_size + PAGE_SIZE - 1) >> PAGE_SHIFT;
info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages);
if (!info->mfns)
@@ -287,8 +407,6 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
if (!info->page)
goto error_nomem;
- xenfb_init_shared_page(info);
-
/* abusing framebuffer_alloc() to allocate pseudo_palette */
fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL);
if (fb_info == NULL)
@@ -301,9 +419,9 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
fb_info->screen_base = info->fb;
fb_info->fbops = &xenfb_fb_ops;
- fb_info->var.xres_virtual = fb_info->var.xres = info->page->width;
- fb_info->var.yres_virtual = fb_info->var.yres = info->page->height;
- fb_info->var.bits_per_pixel = info->page->depth;
+ fb_info->var.xres_virtual = fb_info->var.xres = video[KPARAM_WIDTH];
+ fb_info->var.yres_virtual = fb_info->var.yres = video[KPARAM_HEIGHT];
+ fb_info->var.bits_per_pixel = XENFB_DEPTH;
fb_info->var.red = (struct fb_bitfield){16, 8, 0};
fb_info->var.green = (struct fb_bitfield){8, 8, 0};
@@ -315,9 +433,9 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
fb_info->var.vmode = FB_VMODE_NONINTERLACED;
fb_info->fix.visual = FB_VISUAL_TRUECOLOR;
- fb_info->fix.line_length = info->page->line_length;
+ fb_info->fix.line_length = fb_info->var.xres * XENFB_DEPTH / 8;
fb_info->fix.smem_start = 0;
- fb_info->fix.smem_len = xenfb_mem_len;
+ fb_info->fix.smem_len = fb_size;
strcpy(fb_info->fix.id, "xen");
fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
fb_info->fix.accel = FB_ACCEL_NONE;
@@ -334,6 +452,8 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
fb_info->fbdefio = &xenfb_defio;
fb_deferred_io_init(fb_info);
+ xenfb_init_shared_page(info, fb_info);
+
ret = register_framebuffer(fb_info);
if (ret) {
fb_deferred_io_cleanup(fb_info);
@@ -348,6 +468,7 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
if (ret < 0)
goto error;
+ xenfb_make_preferred_console();
return 0;
error_nomem:
@@ -358,12 +479,34 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
return ret;
}
+static __devinit void
+xenfb_make_preferred_console(void)
+{
+ struct console *c;
+
+ if (console_set_on_cmdline)
+ return;
+
+ acquire_console_sem();
+ for (c = console_drivers; c; c = c->next) {
+ if (!strcmp(c->name, "tty") && c->index == 0)
+ break;
+ }
+ release_console_sem();
+ if (c) {
+ unregister_console(c);
+ c->flags |= CON_CONSDEV;
+ c->flags &= ~CON_PRINTBUFFER; /* don't print again */
+ register_console(c);
+ }
+}
+
static int xenfb_resume(struct xenbus_device *dev)
{
struct xenfb_info *info = dev->dev.driver_data;
xenfb_disconnect_backend(info);
- xenfb_init_shared_page(info);
+ xenfb_init_shared_page(info, info->fb_info);
return xenfb_connect_backend(dev, info);
}
@@ -391,20 +534,23 @@ static unsigned long vmalloc_to_mfn(void *address)
return pfn_to_mfn(vmalloc_to_pfn(address));
}
-static void xenfb_init_shared_page(struct xenfb_info *info)
+static void xenfb_init_shared_page(struct xenfb_info *info,
+ struct fb_info *fb_info)
{
int i;
+ int epd = PAGE_SIZE / sizeof(info->mfns[0]);
for (i = 0; i < info->nr_pages; i++)
info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE);
- info->page->pd[0] = vmalloc_to_mfn(info->mfns);
- info->page->pd[1] = 0;
- info->page->width = XENFB_WIDTH;
- info->page->height = XENFB_HEIGHT;
- info->page->depth = XENFB_DEPTH;
- info->page->line_length = (info->page->depth / 8) * info->page->width;
- info->page->mem_length = xenfb_mem_len;
+ for (i = 0; i * epd < info->nr_pages; i++)
+ info->page->pd[i] = vmalloc_to_mfn(&info->mfns[i * epd]);
+
+ info->page->width = fb_info->var.xres;
+ info->page->height = fb_info->var.yres;
+ info->page->depth = fb_info->var.bits_per_pixel;
+ info->page->line_length = fb_info->fix.line_length;
+ info->page->mem_length = fb_info->fix.smem_len;
info->page->in_cons = info->page->in_prod = 0;
info->page->out_cons = info->page->out_prod = 0;
}
@@ -504,6 +650,11 @@ InitWait:
val = 0;
if (val)
info->update_wanted = 1;
+
+ if (xenbus_scanf(XBT_NIL, dev->otherend,
+ "feature-resize", "%d", &val) < 0)
+ val = 0;
+ info->feature_resize = val;
break;
case XenbusStateClosing:
@@ -547,4 +698,6 @@ static void __exit xenfb_cleanup(void)
module_init(xenfb_init);
module_exit(xenfb_cleanup);
+MODULE_DESCRIPTION("Xen virtual framebuffer device frontend");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("xen:vfb");
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile
index 37af04f1ffd9..363286c54290 100644
--- a/drivers/xen/Makefile
+++ b/drivers/xen/Makefile
@@ -1,4 +1,4 @@
-obj-y += grant-table.o features.o events.o
+obj-y += grant-table.o features.o events.o manage.o
obj-y += xenbus/
obj-$(CONFIG_XEN_XENCOMM) += xencomm.o
obj-$(CONFIG_XEN_BALLOON) += balloon.o
diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c
index ab25ba6cbbb9..591bc29b55f5 100644
--- a/drivers/xen/balloon.c
+++ b/drivers/xen/balloon.c
@@ -225,7 +225,7 @@ static int increase_reservation(unsigned long nr_pages)
page = balloon_next_page(page);
}
- reservation.extent_start = (unsigned long)frame_list;
+ set_xen_guest_handle(reservation.extent_start, frame_list);
reservation.nr_extents = nr_pages;
rc = HYPERVISOR_memory_op(
XENMEM_populate_physmap, &reservation);
@@ -321,7 +321,7 @@ static int decrease_reservation(unsigned long nr_pages)
balloon_append(pfn_to_page(pfn));
}
- reservation.extent_start = (unsigned long)frame_list;
+ set_xen_guest_handle(reservation.extent_start, frame_list);
reservation.nr_extents = nr_pages;
ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation);
BUG_ON(ret != nr_pages);
@@ -368,7 +368,7 @@ static void balloon_process(struct work_struct *work)
}
/* Resets the Xen limit, sets new target, and kicks off processing. */
-void balloon_set_new_target(unsigned long target)
+static void balloon_set_new_target(unsigned long target)
{
/* No need for lock. Not read-modify-write updates. */
balloon_stats.hard_limit = ~0UL;
@@ -483,7 +483,7 @@ static int dealloc_pte_fn(
.extent_order = 0,
.domid = DOMID_SELF
};
- reservation.extent_start = (unsigned long)&mfn;
+ set_xen_guest_handle(reservation.extent_start, &mfn);
set_pte_at(&init_mm, addr, pte, __pte_ma(0ull));
set_phys_to_machine(__pa(addr) >> PAGE_SHIFT, INVALID_P2M_ENTRY);
ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation);
@@ -519,7 +519,7 @@ static struct page **alloc_empty_pages_and_pagevec(int nr_pages)
.extent_order = 0,
.domid = DOMID_SELF
};
- reservation.extent_start = (unsigned long)&gmfn;
+ set_xen_guest_handle(reservation.extent_start, &gmfn);
ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation,
&reservation);
if (ret == 1)
diff --git a/drivers/xen/events.c b/drivers/xen/events.c
index 4f0f22b020ea..73d78dc9b875 100644
--- a/drivers/xen/events.c
+++ b/drivers/xen/events.c
@@ -355,7 +355,7 @@ static void unbind_from_irq(unsigned int irq)
spin_lock(&irq_mapping_update_lock);
- if (VALID_EVTCHN(evtchn) && (--irq_bindcount[irq] == 0)) {
+ if ((--irq_bindcount[irq] == 0) && VALID_EVTCHN(evtchn)) {
close.port = evtchn;
if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close) != 0)
BUG();
@@ -375,7 +375,7 @@ static void unbind_from_irq(unsigned int irq)
evtchn_to_irq[evtchn] = -1;
irq_info[irq] = IRQ_UNBOUND;
- dynamic_irq_init(irq);
+ dynamic_irq_cleanup(irq);
}
spin_unlock(&irq_mapping_update_lock);
@@ -557,6 +557,33 @@ out:
put_cpu();
}
+/* Rebind a new event channel to an existing irq. */
+void rebind_evtchn_irq(int evtchn, int irq)
+{
+ /* Make sure the irq is masked, since the new event channel
+ will also be masked. */
+ disable_irq(irq);
+
+ spin_lock(&irq_mapping_update_lock);
+
+ /* After resume the irq<->evtchn mappings are all cleared out */
+ BUG_ON(evtchn_to_irq[evtchn] != -1);
+ /* Expect irq to have been bound before,
+ so the bindcount should be non-0 */
+ BUG_ON(irq_bindcount[irq] == 0);
+
+ evtchn_to_irq[evtchn] = irq;
+ irq_info[irq] = mk_irq_info(IRQT_EVTCHN, 0, evtchn);
+
+ spin_unlock(&irq_mapping_update_lock);
+
+ /* new event channels are always bound to cpu 0 */
+ irq_set_affinity(irq, cpumask_of_cpu(0));
+
+ /* Unmask the event channel. */
+ enable_irq(irq);
+}
+
/* Rebind an evtchn so that it gets delivered to a specific cpu */
static void rebind_irq_to_cpu(unsigned irq, unsigned tcpu)
{
@@ -647,6 +674,89 @@ static int retrigger_dynirq(unsigned int irq)
return ret;
}
+static void restore_cpu_virqs(unsigned int cpu)
+{
+ struct evtchn_bind_virq bind_virq;
+ int virq, irq, evtchn;
+
+ for (virq = 0; virq < NR_VIRQS; virq++) {
+ if ((irq = per_cpu(virq_to_irq, cpu)[virq]) == -1)
+ continue;
+
+ BUG_ON(irq_info[irq].type != IRQT_VIRQ);
+ BUG_ON(irq_info[irq].index != virq);
+
+ /* Get a new binding from Xen. */
+ bind_virq.virq = virq;
+ bind_virq.vcpu = cpu;
+ if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq,
+ &bind_virq) != 0)
+ BUG();
+ evtchn = bind_virq.port;
+
+ /* Record the new mapping. */
+ evtchn_to_irq[evtchn] = irq;
+ irq_info[irq] = mk_irq_info(IRQT_VIRQ, virq, evtchn);
+ bind_evtchn_to_cpu(evtchn, cpu);
+
+ /* Ready for use. */
+ unmask_evtchn(evtchn);
+ }
+}
+
+static void restore_cpu_ipis(unsigned int cpu)
+{
+ struct evtchn_bind_ipi bind_ipi;
+ int ipi, irq, evtchn;
+
+ for (ipi = 0; ipi < XEN_NR_IPIS; ipi++) {
+ if ((irq = per_cpu(ipi_to_irq, cpu)[ipi]) == -1)
+ continue;
+
+ BUG_ON(irq_info[irq].type != IRQT_IPI);
+ BUG_ON(irq_info[irq].index != ipi);
+
+ /* Get a new binding from Xen. */
+ bind_ipi.vcpu = cpu;
+ if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_ipi,
+ &bind_ipi) != 0)
+ BUG();
+ evtchn = bind_ipi.port;
+
+ /* Record the new mapping. */
+ evtchn_to_irq[evtchn] = irq;
+ irq_info[irq] = mk_irq_info(IRQT_IPI, ipi, evtchn);
+ bind_evtchn_to_cpu(evtchn, cpu);
+
+ /* Ready for use. */
+ unmask_evtchn(evtchn);
+
+ }
+}
+
+void xen_irq_resume(void)
+{
+ unsigned int cpu, irq, evtchn;
+
+ init_evtchn_cpu_bindings();
+
+ /* New event-channel space is not 'live' yet. */
+ for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++)
+ mask_evtchn(evtchn);
+
+ /* No IRQ <-> event-channel mappings. */
+ for (irq = 0; irq < NR_IRQS; irq++)
+ irq_info[irq].evtchn = 0; /* zap event-channel binding */
+
+ for (evtchn = 0; evtchn < NR_EVENT_CHANNELS; evtchn++)
+ evtchn_to_irq[evtchn] = -1;
+
+ for_each_possible_cpu(cpu) {
+ restore_cpu_virqs(cpu);
+ restore_cpu_ipis(cpu);
+ }
+}
+
static struct irq_chip xen_dynamic_chip __read_mostly = {
.name = "xen-dyn",
.mask = disable_dynirq,
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index 52b6b41b909d..e9e11168616a 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -471,14 +471,14 @@ static int gnttab_map(unsigned int start_idx, unsigned int end_idx)
return 0;
}
-static int gnttab_resume(void)
+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)
+int gnttab_suspend(void)
{
arch_gnttab_unmap_shared(shared, nr_grant_frames);
return 0;
diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c
new file mode 100644
index 000000000000..5b546e365f00
--- /dev/null
+++ b/drivers/xen/manage.c
@@ -0,0 +1,252 @@
+/*
+ * 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 <linux/stop_machine.h>
+#include <linux/freezer.h>
+
+#include <xen/xenbus.h>
+#include <xen/grant_table.h>
+#include <xen/events.h>
+#include <xen/hvc-console.h>
+#include <xen/xen-ops.h>
+
+#include <asm/xen/hypercall.h>
+#include <asm/xen/page.h>
+
+enum shutdown_state {
+ SHUTDOWN_INVALID = -1,
+ SHUTDOWN_POWEROFF = 0,
+ 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. */
+ SHUTDOWN_HALT = 4,
+};
+
+/* Ignore multiple shutdown requests. */
+static enum shutdown_state shutting_down = SHUTDOWN_INVALID;
+
+#ifdef CONFIG_PM_SLEEP
+static int xen_suspend(void *data)
+{
+ int *cancelled = data;
+ int err;
+
+ BUG_ON(!irqs_disabled());
+
+ load_cr3(swapper_pg_dir);
+
+ err = device_power_down(PMSG_SUSPEND);
+ if (err) {
+ printk(KERN_ERR "xen_suspend: device_power_down failed: %d\n",
+ err);
+ return err;
+ }
+
+ xen_mm_pin_all();
+ gnttab_suspend();
+ xen_pre_suspend();
+
+ /*
+ * This hypercall returns 1 if suspend was cancelled
+ * or the domain was merely checkpointed, and 0 if it
+ * is resuming in a new domain.
+ */
+ *cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
+
+ xen_post_suspend(*cancelled);
+ gnttab_resume();
+ xen_mm_unpin_all();
+
+ device_power_up();
+
+ if (!*cancelled) {
+ xen_irq_resume();
+ xen_console_resume();
+ }
+
+ return 0;
+}
+
+static void do_suspend(void)
+{
+ int err;
+ int cancelled = 1;
+
+ shutting_down = SHUTDOWN_SUSPEND;
+
+#ifdef CONFIG_PREEMPT
+ /* If the kernel is preemptible, we need to freeze all the processes
+ to prevent them from being in the middle of a pagetable update
+ during suspend. */
+ err = freeze_processes();
+ if (err) {
+ printk(KERN_ERR "xen suspend: freeze failed %d\n", err);
+ return;
+ }
+#endif
+
+ err = device_suspend(PMSG_SUSPEND);
+ if (err) {
+ printk(KERN_ERR "xen suspend: device_suspend %d\n", err);
+ goto out;
+ }
+
+ printk("suspending xenbus...\n");
+ /* XXX use normal device tree? */
+ xenbus_suspend();
+
+ err = stop_machine_run(xen_suspend, &cancelled, 0);
+ if (err) {
+ printk(KERN_ERR "failed to start xen_suspend: %d\n", err);
+ goto out;
+ }
+
+ if (!cancelled)
+ xenbus_resume();
+ else
+ xenbus_suspend_cancel();
+
+ device_resume();
+
+ /* Make sure timer events get retriggered on all CPUs */
+ clock_was_set();
+out:
+#ifdef CONFIG_PREEMPT
+ thaw_processes();
+#endif
+ shutting_down = SHUTDOWN_INVALID;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+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) {
+ shutting_down = SHUTDOWN_POWEROFF;
+ orderly_poweroff(false);
+ } else if (strcmp(str, "reboot") == 0) {
+ shutting_down = SHUTDOWN_POWEROFF; /* ? */
+ ctrl_alt_del();
+#ifdef CONFIG_PM_SLEEP
+ } else if (strcmp(str, "suspend") == 0) {
+ do_suspend();
+#endif
+ } 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/drivers/xen/xenbus/xenbus_comms.c b/drivers/xen/xenbus/xenbus_comms.c
index 6efbe3f29ca5..090c61ee8fd0 100644
--- a/drivers/xen/xenbus/xenbus_comms.c
+++ b/drivers/xen/xenbus/xenbus_comms.c
@@ -203,7 +203,6 @@ int xb_read(void *data, unsigned len)
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 "
@@ -216,18 +215,20 @@ int xb_init_comms(void)
intf->rsp_cons = intf->rsp_prod;
}
- if (xenbus_irq)
- unbind_from_irqhandler(xenbus_irq, &xb_waitq);
+ if (xenbus_irq) {
+ /* Already have an irq; assume we're resuming */
+ rebind_evtchn_irq(xen_store_evtchn, xenbus_irq);
+ } else {
+ int err;
+ 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;
+ }
- 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;
}
- xenbus_irq = err;
-
return 0;
}
diff --git a/fs/Kconfig b/fs/Kconfig
index cf12c403b8c7..c264610ca27c 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -470,6 +470,14 @@ config OCFS2_FS_USERSPACE_CLUSTER
It is safe to say Y, as the clustering method is run-time
selectable.
+config OCFS2_FS_STATS
+ bool "OCFS2 statistics"
+ depends on OCFS2_FS
+ default y
+ help
+ This option allows some fs statistics to be captured. Enabling
+ this option may increase the memory consumption.
+
config OCFS2_DEBUG_MASKLOG
bool "OCFS2 logging support"
depends on OCFS2_FS
diff --git a/fs/Kconfig.binfmt b/fs/Kconfig.binfmt
index 3263084eef9e..4a551af6f3fc 100644
--- a/fs/Kconfig.binfmt
+++ b/fs/Kconfig.binfmt
@@ -30,7 +30,7 @@ config COMPAT_BINFMT_ELF
config BINFMT_ELF_FDPIC
bool "Kernel support for FDPIC ELF binaries"
default y
- depends on (FRV || BLACKFIN)
+ depends on (FRV || BLACKFIN || (SUPERH32 && !MMU))
help
ELF FDPIC binaries are based on ELF, but allow the individual load
segments of a binary to be located in memory independently of each
diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
index d051a32e6270..54a526e57f9f 100644
--- a/fs/binfmt_elf_fdpic.c
+++ b/fs/binfmt_elf_fdpic.c
@@ -477,6 +477,7 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
char __user *u_platform, *p;
long hwcap;
int loop;
+ int nr; /* reset for each csp adjustment */
/* we're going to shovel a whole load of stuff onto the stack */
#ifdef CONFIG_MMU
@@ -549,10 +550,7 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
/* force 16 byte _final_ alignment here for generality */
#define DLINFO_ITEMS 13
- nitems = 1 + DLINFO_ITEMS + (k_platform ? 1 : 0);
-#ifdef DLINFO_ARCH_ITEMS
- nitems += DLINFO_ARCH_ITEMS;
-#endif
+ nitems = 1 + DLINFO_ITEMS + (k_platform ? 1 : 0) + AT_VECTOR_SIZE_ARCH;
csp = sp;
sp -= nitems * 2 * sizeof(unsigned long);
@@ -564,39 +562,46 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
sp -= sp & 15UL;
/* put the ELF interpreter info on the stack */
-#define NEW_AUX_ENT(nr, id, val) \
+#define NEW_AUX_ENT(id, val) \
do { \
struct { unsigned long _id, _val; } __user *ent; \
\
ent = (void __user *) csp; \
__put_user((id), &ent[nr]._id); \
__put_user((val), &ent[nr]._val); \
+ nr++; \
} while (0)
+ nr = 0;
csp -= 2 * sizeof(unsigned long);
- NEW_AUX_ENT(0, AT_NULL, 0);
+ NEW_AUX_ENT(AT_NULL, 0);
if (k_platform) {
+ nr = 0;
csp -= 2 * sizeof(unsigned long);
- NEW_AUX_ENT(0, AT_PLATFORM,
+ NEW_AUX_ENT(AT_PLATFORM,
(elf_addr_t) (unsigned long) u_platform);
}
+ nr = 0;
csp -= DLINFO_ITEMS * 2 * sizeof(unsigned long);
- NEW_AUX_ENT( 0, AT_HWCAP, hwcap);
- NEW_AUX_ENT( 1, AT_PAGESZ, PAGE_SIZE);
- NEW_AUX_ENT( 2, AT_CLKTCK, CLOCKS_PER_SEC);
- NEW_AUX_ENT( 3, AT_PHDR, exec_params->ph_addr);
- NEW_AUX_ENT( 4, AT_PHENT, sizeof(struct elf_phdr));
- NEW_AUX_ENT( 5, AT_PHNUM, exec_params->hdr.e_phnum);
- NEW_AUX_ENT( 6, AT_BASE, interp_params->elfhdr_addr);
- NEW_AUX_ENT( 7, AT_FLAGS, 0);
- NEW_AUX_ENT( 8, AT_ENTRY, exec_params->entry_addr);
- NEW_AUX_ENT( 9, AT_UID, (elf_addr_t) current->uid);
- NEW_AUX_ENT(10, AT_EUID, (elf_addr_t) current->euid);
- NEW_AUX_ENT(11, AT_GID, (elf_addr_t) current->gid);
- NEW_AUX_ENT(12, AT_EGID, (elf_addr_t) current->egid);
+ NEW_AUX_ENT(AT_HWCAP, hwcap);
+ NEW_AUX_ENT(AT_PAGESZ, PAGE_SIZE);
+ NEW_AUX_ENT(AT_CLKTCK, CLOCKS_PER_SEC);
+ NEW_AUX_ENT(AT_PHDR, exec_params->ph_addr);
+ NEW_AUX_ENT(AT_PHENT, sizeof(struct elf_phdr));
+ NEW_AUX_ENT(AT_PHNUM, exec_params->hdr.e_phnum);
+ NEW_AUX_ENT(AT_BASE, interp_params->elfhdr_addr);
+ NEW_AUX_ENT(AT_FLAGS, 0);
+ NEW_AUX_ENT(AT_ENTRY, exec_params->entry_addr);
+ NEW_AUX_ENT(AT_UID, (elf_addr_t) current->uid);
+ NEW_AUX_ENT(AT_EUID, (elf_addr_t) current->euid);
+ NEW_AUX_ENT(AT_GID, (elf_addr_t) current->gid);
+ NEW_AUX_ENT(AT_EGID, (elf_addr_t) current->egid);
#ifdef ARCH_DLINFO
+ nr = 0;
+ csp -= AT_VECTOR_SIZE_ARCH * 2 * sizeof(unsigned long);
+
/* ARCH_DLINFO must come last so platform specific code can enforce
* special alignment requirements on the AUXV if necessary (eg. PPC).
*/
diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES
index 28e3d5c5fcac..1f3465201fdf 100644
--- a/fs/cifs/CHANGES
+++ b/fs/cifs/CHANGES
@@ -2,6 +2,11 @@ Version 1.53
------------
DFS support added (Microsoft Distributed File System client support needed
for referrals which enable a hierarchical name space among servers).
+Disable temporary caching of mode bits to servers which do not support
+storing of mode (e.g. Windows servers, when client mounts without cifsacl
+mount option) and add new "dynperm" mount option to enable temporary caching
+of mode (enable old behavior). Fix hang on mount caused when server crashes
+tcp session during negotiate protocol.
Version 1.52
------------
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 5df93fd6303f..86b4d5f405ae 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -97,9 +97,6 @@ cifs_read_super(struct super_block *sb, void *data,
{
struct inode *inode;
struct cifs_sb_info *cifs_sb;
-#ifdef CONFIG_CIFS_DFS_UPCALL
- int len;
-#endif
int rc = 0;
/* BB should we make this contingent on mount parm? */
@@ -117,15 +114,17 @@ cifs_read_super(struct super_block *sb, void *data,
* complex operation (mount), and in case of fail
* just exit instead of doing mount and attempting
* undo it if this copy fails?*/
- len = strlen(data);
- cifs_sb->mountdata = kzalloc(len + 1, GFP_KERNEL);
- if (cifs_sb->mountdata == NULL) {
- kfree(sb->s_fs_info);
- sb->s_fs_info = NULL;
- return -ENOMEM;
+ if (data) {
+ int len = strlen(data);
+ cifs_sb->mountdata = kzalloc(len + 1, GFP_KERNEL);
+ if (cifs_sb->mountdata == NULL) {
+ kfree(sb->s_fs_info);
+ sb->s_fs_info = NULL;
+ return -ENOMEM;
+ }
+ strncpy(cifs_sb->mountdata, data, len + 1);
+ cifs_sb->mountdata[len] = '\0';
}
- strncpy(cifs_sb->mountdata, data, len + 1);
- cifs_sb->mountdata[len] = '\0';
#endif
rc = cifs_mount(sb, cifs_sb, data, devname);
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 08914053242b..9cfcf326ead3 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -333,7 +333,6 @@ struct cifsFileInfo {
bool messageMode:1; /* for pipes: message vs byte mode */
atomic_t wrtPending; /* handle in use - defer close */
struct semaphore fh_sem; /* prevents reopen race after dead ses*/
- char *search_resume_name; /* BB removeme BB */
struct cifs_search_info srch_inf;
};
@@ -626,7 +625,7 @@ GLOBAL_EXTERN atomic_t tcpSesAllocCount;
GLOBAL_EXTERN atomic_t tcpSesReconnectCount;
GLOBAL_EXTERN atomic_t tconInfoReconnectCount;
-/* Various Debug counters to remove someday (BB) */
+/* Various Debug counters */
GLOBAL_EXTERN atomic_t bufAllocCount; /* current number allocated */
#ifdef CONFIG_CIFS_STATS2
GLOBAL_EXTERN atomic_t totBufAllocCount; /* total allocated over all time */
diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
index 65d58b4e6a61..0f327c224da3 100644
--- a/fs/cifs/cifspdu.h
+++ b/fs/cifs/cifspdu.h
@@ -79,6 +79,19 @@
#define TRANS2_GET_DFS_REFERRAL 0x10
#define TRANS2_REPORT_DFS_INCOSISTENCY 0x11
+/* SMB Transact (Named Pipe) subcommand codes */
+#define TRANS_SET_NMPIPE_STATE 0x0001
+#define TRANS_RAW_READ_NMPIPE 0x0011
+#define TRANS_QUERY_NMPIPE_STATE 0x0021
+#define TRANS_QUERY_NMPIPE_INFO 0x0022
+#define TRANS_PEEK_NMPIPE 0x0023
+#define TRANS_TRANSACT_NMPIPE 0x0026
+#define TRANS_RAW_WRITE_NMPIPE 0x0031
+#define TRANS_READ_NMPIPE 0x0036
+#define TRANS_WRITE_NMPIPE 0x0037
+#define TRANS_WAIT_NMPIPE 0x0053
+#define TRANS_CALL_NMPIPE 0x0054
+
/* NT Transact subcommand codes */
#define NT_TRANSACT_CREATE 0x01
#define NT_TRANSACT_IOCTL 0x02
@@ -328,12 +341,13 @@
#define CREATE_COMPLETE_IF_OPLK 0x00000100 /* should be zero */
#define CREATE_NO_EA_KNOWLEDGE 0x00000200
#define CREATE_EIGHT_DOT_THREE 0x00000400 /* doc says this is obsolete
- open for recovery flag - should
- be zero */
+ "open for recovery" flag - should
+ be zero in any case */
+#define CREATE_OPEN_FOR_RECOVERY 0x00000400
#define CREATE_RANDOM_ACCESS 0x00000800
#define CREATE_DELETE_ON_CLOSE 0x00001000
#define CREATE_OPEN_BY_ID 0x00002000
-#define CREATE_OPEN_BACKUP_INTN 0x00004000
+#define CREATE_OPEN_BACKUP_INTENT 0x00004000
#define CREATE_NO_COMPRESSION 0x00008000
#define CREATE_RESERVE_OPFILTER 0x00100000 /* should be zero */
#define OPEN_REPARSE_POINT 0x00200000
@@ -722,7 +736,6 @@ typedef struct smb_com_tconx_rsp_ext {
#define SMB_CSC_CACHE_AUTO_REINT 0x0004
#define SMB_CSC_CACHE_VDO 0x0008
#define SMB_CSC_NO_CACHING 0x000C
-
#define SMB_UNIQUE_FILE_NAME 0x0010
#define SMB_EXTENDED_SIGNATURES 0x0020
@@ -806,7 +819,7 @@ typedef struct smb_com_findclose_req {
#define ICOUNT_MASK 0x00FF
#define PIPE_READ_MODE 0x0100
#define NAMED_PIPE_TYPE 0x0400
-#define PIPE_END_POINT 0x0800
+#define PIPE_END_POINT 0x4000
#define BLOCKING_NAMED_PIPE 0x8000
typedef struct smb_com_open_req { /* also handles create */
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index fb655b4593c6..4511b708f0f3 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -1728,7 +1728,7 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
{
int rc = 0;
LOCK_REQ *pSMB = NULL;
- LOCK_RSP *pSMBr = NULL;
+/* LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */
int bytes_returned;
int timeout = 0;
__u16 count;
@@ -1739,8 +1739,6 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
if (rc)
return rc;
- pSMBr = (LOCK_RSP *)pSMB; /* BB removeme BB */
-
if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) {
timeout = CIFS_ASYNC_OP; /* no response expected */
pSMB->Timeout = 0;
@@ -1774,7 +1772,7 @@ CIFSSMBLock(const int xid, struct cifsTconInfo *tcon,
if (waitFlag) {
rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB,
- (struct smb_hdr *) pSMBr, &bytes_returned);
+ (struct smb_hdr *) pSMB, &bytes_returned);
cifs_small_buf_release(pSMB);
} else {
rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *)pSMB,
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 023434f72c15..e8fa46c7cff2 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -653,6 +653,7 @@ multi_t2_fnd:
spin_lock(&GlobalMid_Lock);
server->tcpStatus = CifsExiting;
spin_unlock(&GlobalMid_Lock);
+ wake_up_all(&server->response_q);
/* don't exit until kthread_stop is called */
set_current_state(TASK_UNINTERRUPTIBLE);
@@ -2120,6 +2121,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO;
}
+ if ((volume_info.cifs_acl) && (volume_info.dynperm))
+ cERROR(1, ("mount option dynperm ignored if cifsacl "
+ "mount option supported"));
+
tcon =
find_unc(sin_server.sin_addr.s_addr, volume_info.UNC,
volume_info.username);
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index f0b5b5f3dd2e..fb69c1fa85c9 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -260,7 +260,9 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
buf, inode->i_sb, xid,
&fileHandle);
if (newinode) {
- newinode->i_mode = mode;
+ if (cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_DYNPERM)
+ newinode->i_mode = mode;
if ((oplock & CIFS_CREATE_ACTION) &&
(cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_SET_UID)) {
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 8636cec2642c..0aac824371a5 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -546,7 +546,6 @@ int cifs_close(struct inode *inode, struct file *file)
msleep(timeout);
timeout *= 8;
}
- kfree(pSMBFile->search_resume_name);
kfree(file->private_data);
file->private_data = NULL;
} else
@@ -605,12 +604,6 @@ int cifs_closedir(struct inode *inode, struct file *file)
else
cifs_buf_release(ptmp);
}
- ptmp = pCFileStruct->search_resume_name;
- if (ptmp) {
- cFYI(1, ("closedir free resume name"));
- pCFileStruct->search_resume_name = NULL;
- kfree(ptmp);
- }
kfree(file->private_data);
file->private_data = NULL;
}
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 129dbfe4dca7..722be543ceec 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -418,6 +418,7 @@ int cifs_get_inode_info(struct inode **pinode,
char *buf = NULL;
bool adjustTZ = false;
bool is_dfs_referral = false;
+ umode_t default_mode;
pTcon = cifs_sb->tcon;
cFYI(1, ("Getting info on %s", full_path));
@@ -530,47 +531,42 @@ int cifs_get_inode_info(struct inode **pinode,
inode->i_mtime.tv_sec += pTcon->ses->server->timeAdj;
}
- /* set default mode. will override for dirs below */
- if (atomic_read(&cifsInfo->inUse) == 0)
- /* new inode, can safely set these fields */
- inode->i_mode = cifs_sb->mnt_file_mode;
- else /* since we set the inode type below we need to mask off
- to avoid strange results if type changes and both
- get orred in */
- inode->i_mode &= ~S_IFMT;
-/* if (attr & ATTR_REPARSE) */
- /* We no longer handle these as symlinks because we could not
- follow them due to the absolute path with drive letter */
- if (attr & ATTR_DIRECTORY) {
- /* override default perms since we do not do byte range locking
- on dirs */
- inode->i_mode = cifs_sb->mnt_dir_mode;
- inode->i_mode |= S_IFDIR;
- } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) &&
- (cifsInfo->cifsAttrs & ATTR_SYSTEM) &&
- /* No need to le64 convert size of zero */
- (pfindData->EndOfFile == 0)) {
- inode->i_mode = cifs_sb->mnt_file_mode;
- inode->i_mode |= S_IFIFO;
-/* BB Finish for SFU style symlinks and devices */
- } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) &&
- (cifsInfo->cifsAttrs & ATTR_SYSTEM)) {
- if (decode_sfu_inode(inode, le64_to_cpu(pfindData->EndOfFile),
- full_path, cifs_sb, xid))
- cFYI(1, ("Unrecognized sfu inode type"));
-
- cFYI(1, ("sfu mode 0%o", inode->i_mode));
+ /* get default inode mode */
+ if (attr & ATTR_DIRECTORY)
+ default_mode = cifs_sb->mnt_dir_mode;
+ else
+ default_mode = cifs_sb->mnt_file_mode;
+
+ /* set permission bits */
+ if (atomic_read(&cifsInfo->inUse) == 0 ||
+ (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0)
+ inode->i_mode = default_mode;
+ else {
+ /* just reenable write bits if !ATTR_READONLY */
+ if ((inode->i_mode & S_IWUGO) == 0 &&
+ (attr & ATTR_READONLY) == 0)
+ inode->i_mode |= (S_IWUGO & default_mode);
+ inode->i_mode &= ~S_IFMT;
+ }
+ /* clear write bits if ATTR_READONLY is set */
+ if (attr & ATTR_READONLY)
+ inode->i_mode &= ~S_IWUGO;
+
+ /* set inode type */
+ if ((attr & ATTR_SYSTEM) &&
+ (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) {
+ /* no need to fix endianness on 0 */
+ if (pfindData->EndOfFile == 0)
+ inode->i_mode |= S_IFIFO;
+ else if (decode_sfu_inode(inode,
+ le64_to_cpu(pfindData->EndOfFile),
+ full_path, cifs_sb, xid))
+ cFYI(1, ("unknown SFU file type\n"));
} else {
- inode->i_mode |= S_IFREG;
- /* treat dos attribute of read-only as read-only mode eg 555 */
- if (cifsInfo->cifsAttrs & ATTR_READONLY)
- inode->i_mode &= ~(S_IWUGO);
- else if ((inode->i_mode & S_IWUGO) == 0)
- /* the ATTR_READONLY flag may have been */
- /* changed on server -- set any w bits */
- /* allowed by mnt_file_mode */
- inode->i_mode |= (S_IWUGO & cifs_sb->mnt_file_mode);
- /* BB add code to validate if device or weird share or device type? */
+ if (attr & ATTR_DIRECTORY)
+ inode->i_mode |= S_IFDIR;
+ else
+ inode->i_mode |= S_IFREG;
}
spin_lock(&inode->i_lock);
@@ -1019,8 +1015,11 @@ mkdir_get_info:
CIFS_MOUNT_MAP_SPECIAL_CHR);
}
if (direntry->d_inode) {
- direntry->d_inode->i_mode = mode;
- direntry->d_inode->i_mode |= S_IFDIR;
+ if (cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_DYNPERM)
+ direntry->d_inode->i_mode =
+ (mode | S_IFDIR);
+
if (cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_SET_UID) {
direntry->d_inode->i_uid =
@@ -1547,13 +1546,26 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
} else
goto cifs_setattr_exit;
}
- if (attrs->ia_valid & ATTR_UID) {
- cFYI(1, ("UID changed to %d", attrs->ia_uid));
- uid = attrs->ia_uid;
- }
- if (attrs->ia_valid & ATTR_GID) {
- cFYI(1, ("GID changed to %d", attrs->ia_gid));
- gid = attrs->ia_gid;
+
+ /*
+ * Without unix extensions we can't send ownership changes to the
+ * server, so silently ignore them. This is consistent with how
+ * local DOS/Windows filesystems behave (VFAT, NTFS, etc). With
+ * CIFSACL support + proper Windows to Unix idmapping, we may be
+ * able to support this in the future.
+ */
+ if (!pTcon->unix_ext &&
+ !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) {
+ attrs->ia_valid &= ~(ATTR_UID | ATTR_GID);
+ } else {
+ if (attrs->ia_valid & ATTR_UID) {
+ cFYI(1, ("UID changed to %d", attrs->ia_uid));
+ uid = attrs->ia_uid;
+ }
+ if (attrs->ia_valid & ATTR_GID) {
+ cFYI(1, ("GID changed to %d", attrs->ia_gid));
+ gid = attrs->ia_gid;
+ }
}
time_buf.Attributes = 0;
@@ -1563,7 +1575,7 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
attrs->ia_valid &= ~ATTR_MODE;
if (attrs->ia_valid & ATTR_MODE) {
- cFYI(1, ("Mode changed to 0x%x", attrs->ia_mode));
+ cFYI(1, ("Mode changed to 0%o", attrs->ia_mode));
mode = attrs->ia_mode;
}
@@ -1578,18 +1590,18 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
#ifdef CONFIG_CIFS_EXPERIMENTAL
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL)
rc = mode_to_acl(inode, full_path, mode);
- else if ((mode & S_IWUGO) == 0) {
-#else
- if ((mode & S_IWUGO) == 0) {
+ else
#endif
- /* not writeable */
- if ((cifsInode->cifsAttrs & ATTR_READONLY) == 0) {
- set_dosattr = true;
- time_buf.Attributes =
- cpu_to_le32(cifsInode->cifsAttrs |
- ATTR_READONLY);
- }
- } else if (cifsInode->cifsAttrs & ATTR_READONLY) {
+ if (((mode & S_IWUGO) == 0) &&
+ (cifsInode->cifsAttrs & ATTR_READONLY) == 0) {
+ set_dosattr = true;
+ time_buf.Attributes = cpu_to_le32(cifsInode->cifsAttrs |
+ ATTR_READONLY);
+ /* fix up mode if we're not using dynperm */
+ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0)
+ attrs->ia_mode = inode->i_mode & ~S_IWUGO;
+ } else if ((mode & S_IWUGO) &&
+ (cifsInode->cifsAttrs & ATTR_READONLY)) {
/* If file is readonly on server, we would
not be able to write to it - so if any write
bit is enabled for user or group or other we
@@ -1600,6 +1612,20 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
/* Windows ignores set to zero */
if (time_buf.Attributes == 0)
time_buf.Attributes |= cpu_to_le32(ATTR_NORMAL);
+
+ /* reset local inode permissions to normal */
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) {
+ attrs->ia_mode &= ~(S_IALLUGO);
+ if (S_ISDIR(inode->i_mode))
+ attrs->ia_mode |=
+ cifs_sb->mnt_dir_mode;
+ else
+ attrs->ia_mode |=
+ cifs_sb->mnt_file_mode;
+ }
+ } else if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) {
+ /* ignore mode change - ATTR_READONLY hasn't changed */
+ attrs->ia_valid &= ~ATTR_MODE;
}
}
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 1d69b8014e0b..4b17f8fe3157 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -519,8 +519,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
pnotify = (struct file_notify_information *)
((char *)&pSMBr->hdr.Protocol + data_offset);
cFYI(1, ("dnotify on %s Action: 0x%x",
- pnotify->FileName,
- pnotify->Action)); /* BB removeme BB */
+ pnotify->FileName, pnotify->Action));
/* cifs_dump_mem("Rcvd notify Data: ",buf,
sizeof(struct smb_hdr)+60); */
return true;
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index 713c25110197..83f306954883 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -132,6 +132,7 @@ static void fill_in_inode(struct inode *tmp_inode, int new_buf_type,
__u32 attr;
__u64 allocation_size;
__u64 end_of_file;
+ umode_t default_mode;
/* save mtime and size */
local_mtime = tmp_inode->i_mtime;
@@ -187,48 +188,54 @@ static void fill_in_inode(struct inode *tmp_inode, int new_buf_type,
if (atomic_read(&cifsInfo->inUse) == 0) {
tmp_inode->i_uid = cifs_sb->mnt_uid;
tmp_inode->i_gid = cifs_sb->mnt_gid;
- /* set default mode. will override for dirs below */
- tmp_inode->i_mode = cifs_sb->mnt_file_mode;
- } else {
- /* mask off the type bits since it gets set
- below and we do not want to get two type
- bits set */
+ }
+
+ if (attr & ATTR_DIRECTORY)
+ default_mode = cifs_sb->mnt_dir_mode;
+ else
+ default_mode = cifs_sb->mnt_file_mode;
+
+ /* set initial permissions */
+ if ((atomic_read(&cifsInfo->inUse) == 0) ||
+ (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0)
+ tmp_inode->i_mode = default_mode;
+ else {
+ /* just reenable write bits if !ATTR_READONLY */
+ if ((tmp_inode->i_mode & S_IWUGO) == 0 &&
+ (attr & ATTR_READONLY) == 0)
+ tmp_inode->i_mode |= (S_IWUGO & default_mode);
+
tmp_inode->i_mode &= ~S_IFMT;
}
- if (attr & ATTR_DIRECTORY) {
- *pobject_type = DT_DIR;
- /* override default perms since we do not lock dirs */
- if (atomic_read(&cifsInfo->inUse) == 0)
- tmp_inode->i_mode = cifs_sb->mnt_dir_mode;
- tmp_inode->i_mode |= S_IFDIR;
- } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) &&
- (attr & ATTR_SYSTEM)) {
+ /* clear write bits if ATTR_READONLY is set */
+ if (attr & ATTR_READONLY)
+ tmp_inode->i_mode &= ~S_IWUGO;
+
+ /* set inode type */
+ if ((attr & ATTR_SYSTEM) &&
+ (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) {
if (end_of_file == 0) {
- *pobject_type = DT_FIFO;
tmp_inode->i_mode |= S_IFIFO;
+ *pobject_type = DT_FIFO;
} else {
- /* rather than get the type here, we mark the
- inode as needing revalidate and get the real type
- (blk vs chr vs. symlink) later ie in lookup */
- *pobject_type = DT_REG;
+ /*
+ * trying to get the type can be slow, so just call
+ * this a regular file for now, and mark for reval
+ */
tmp_inode->i_mode |= S_IFREG;
+ *pobject_type = DT_REG;
cifsInfo->time = 0;
}
-/* we no longer mark these because we could not follow them */
-/* } else if (attr & ATTR_REPARSE) {
- *pobject_type = DT_LNK;
- tmp_inode->i_mode |= S_IFLNK; */
} else {
- *pobject_type = DT_REG;
- tmp_inode->i_mode |= S_IFREG;
- if (attr & ATTR_READONLY)
- tmp_inode->i_mode &= ~(S_IWUGO);
- else if ((tmp_inode->i_mode & S_IWUGO) == 0)
- /* the ATTR_READONLY flag may have been changed on */
- /* server -- set any w bits allowed by mnt_file_mode */
- tmp_inode->i_mode |= (S_IWUGO & cifs_sb->mnt_file_mode);
- } /* could add code here - to validate if device or weird share type? */
+ if (attr & ATTR_DIRECTORY) {
+ tmp_inode->i_mode |= S_IFDIR;
+ *pobject_type = DT_DIR;
+ } else {
+ tmp_inode->i_mode |= S_IFREG;
+ *pobject_type = DT_REG;
+ }
+ }
/* can not fill in nlink here as in qpathinfo version and Unx search */
if (atomic_read(&cifsInfo->inUse) == 0)
@@ -675,8 +682,6 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
cifsFile->invalidHandle = true;
CIFSFindClose(xid, pTcon, cifsFile->netfid);
}
- kfree(cifsFile->search_resume_name);
- cifsFile->search_resume_name = NULL;
if (cifsFile->srch_inf.ntwrk_buf_start) {
cFYI(1, ("freeing SMB ff cache buf on search rewind"));
if (cifsFile->srch_inf.smallBuf)
@@ -1043,9 +1048,7 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
} /* else {
cifsFile->invalidHandle = true;
CIFSFindClose(xid, pTcon, cifsFile->netfid);
- }
- kfree(cifsFile->search_resume_name);
- cifsFile->search_resume_name = NULL; */
+ } */
rc = find_cifs_entry(xid, pTcon, file,
&current_entry, &num_to_fill);
diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c
index e3eb3556622b..40c36f7352a6 100644
--- a/fs/coda/psdev.c
+++ b/fs/coda/psdev.c
@@ -362,8 +362,9 @@ static int init_coda_psdev(void)
goto out_chrdev;
}
for (i = 0; i < MAX_CODADEVS; i++)
- device_create(coda_psdev_class, NULL,
- MKDEV(CODA_PSDEV_MAJOR,i), "cfs%d", i);
+ device_create_drvdata(coda_psdev_class, NULL,
+ MKDEV(CODA_PSDEV_MAJOR, i),
+ NULL, "cfs%d", i);
coda_sysctl_init();
goto out;
diff --git a/fs/dcache.c b/fs/dcache.c
index 3ee588d5f585..40e4d511c4ea 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1191,6 +1191,107 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
return new;
}
+/**
+ * d_add_ci - lookup or allocate new dentry with case-exact name
+ * @inode: the inode case-insensitive lookup has found
+ * @dentry: the negative dentry that was passed to the parent's lookup func
+ * @name: the case-exact name to be associated with the returned dentry
+ *
+ * This is to avoid filling the dcache with case-insensitive names to the
+ * same inode, only the actual correct case is stored in the dcache for
+ * case-insensitive filesystems.
+ *
+ * For a case-insensitive lookup match and if the the case-exact dentry
+ * already exists in in the dcache, use it and return it.
+ *
+ * If no entry exists with the exact case name, allocate new dentry with
+ * the exact case, and return the spliced entry.
+ */
+struct dentry *d_add_ci(struct inode *inode, struct dentry *dentry,
+ struct qstr *name)
+{
+ int error;
+ struct dentry *found;
+ struct dentry *new;
+
+ /* Does a dentry matching the name exist already? */
+ found = d_hash_and_lookup(dentry->d_parent, name);
+ /* If not, create it now and return */
+ if (!found) {
+ new = d_alloc(dentry->d_parent, name);
+ if (!new) {
+ error = -ENOMEM;
+ goto err_out;
+ }
+ found = d_splice_alias(inode, new);
+ if (found) {
+ dput(new);
+ return found;
+ }
+ return new;
+ }
+ /* Matching dentry exists, check if it is negative. */
+ if (found->d_inode) {
+ if (unlikely(found->d_inode != inode)) {
+ /* This can't happen because bad inodes are unhashed. */
+ BUG_ON(!is_bad_inode(inode));
+ BUG_ON(!is_bad_inode(found->d_inode));
+ }
+ /*
+ * Already have the inode and the dentry attached, decrement
+ * the reference count to balance the iget() done
+ * earlier on. We found the dentry using d_lookup() so it
+ * cannot be disconnected and thus we do not need to worry
+ * about any NFS/disconnectedness issues here.
+ */
+ iput(inode);
+ return found;
+ }
+ /*
+ * Negative dentry: instantiate it unless the inode is a directory and
+ * has a 'disconnected' dentry (i.e. IS_ROOT and DCACHE_DISCONNECTED),
+ * in which case d_move() that in place of the found dentry.
+ */
+ if (!S_ISDIR(inode->i_mode)) {
+ /* Not a directory; everything is easy. */
+ d_instantiate(found, inode);
+ return found;
+ }
+ spin_lock(&dcache_lock);
+ if (list_empty(&inode->i_dentry)) {
+ /*
+ * Directory without a 'disconnected' dentry; we need to do
+ * d_instantiate() by hand because it takes dcache_lock which
+ * we already hold.
+ */
+ list_add(&found->d_alias, &inode->i_dentry);
+ found->d_inode = inode;
+ spin_unlock(&dcache_lock);
+ security_d_instantiate(found, inode);
+ return found;
+ }
+ /*
+ * Directory with a 'disconnected' dentry; get a reference to the
+ * 'disconnected' dentry.
+ */
+ new = list_entry(inode->i_dentry.next, struct dentry, d_alias);
+ dget_locked(new);
+ spin_unlock(&dcache_lock);
+ /* Do security vodoo. */
+ security_d_instantiate(found, inode);
+ /* Move new in place of found. */
+ d_move(new, found);
+ /* Balance the iget() we did above. */
+ iput(inode);
+ /* Throw away found. */
+ dput(found);
+ /* Use new as the actual dentry. */
+ return new;
+
+err_out:
+ iput(inode);
+ return ERR_PTR(error);
+}
/**
* d_lookup - search for a dentry
@@ -2228,6 +2329,7 @@ EXPORT_SYMBOL(d_path);
EXPORT_SYMBOL(d_prune_aliases);
EXPORT_SYMBOL(d_rehash);
EXPORT_SYMBOL(d_splice_alias);
+EXPORT_SYMBOL(d_add_ci);
EXPORT_SYMBOL(d_validate);
EXPORT_SYMBOL(dget_locked);
EXPORT_SYMBOL(dput);
diff --git a/fs/dlm/lock.c b/fs/dlm/lock.c
index 2d3d1027ce2b..7ba9586a0943 100644
--- a/fs/dlm/lock.c
+++ b/fs/dlm/lock.c
@@ -1782,7 +1782,8 @@ static void grant_pending_locks(struct dlm_rsb *r)
list_for_each_entry_safe(lkb, s, &r->res_grantqueue, lkb_statequeue) {
if (lkb->lkb_bastfn && lock_requires_bast(lkb, high, cw)) {
- if (cw && high == DLM_LOCK_PR)
+ if (cw && high == DLM_LOCK_PR &&
+ lkb->lkb_grmode == DLM_LOCK_PR)
queue_bast(r, lkb, DLM_LOCK_CW);
else
queue_bast(r, lkb, high);
diff --git a/fs/dlm/user.c b/fs/dlm/user.c
index ebbcf38fd33b..1aa76b32d056 100644
--- a/fs/dlm/user.c
+++ b/fs/dlm/user.c
@@ -538,7 +538,7 @@ static ssize_t device_write(struct file *file, const char __user *buf,
/* do we really need this? can a write happen after a close? */
if ((kbuf->cmd == DLM_USER_LOCK || kbuf->cmd == DLM_USER_UNLOCK) &&
- test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags))
+ (proc && test_bit(DLM_PROC_FLAGS_CLOSING, &proc->flags)))
return -EINVAL;
sigfillset(&allsigs);
diff --git a/fs/jfs/jfs_debug.c b/fs/jfs/jfs_debug.c
index bf6ab19b86ee..6a73de84bcef 100644
--- a/fs/jfs/jfs_debug.c
+++ b/fs/jfs/jfs_debug.c
@@ -21,6 +21,7 @@
#include <linux/ctype.h>
#include <linux/module.h>
#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
#include <asm/uaccess.h>
#include "jfs_incore.h"
#include "jfs_filsys.h"
@@ -30,29 +31,19 @@
static struct proc_dir_entry *base;
#ifdef CONFIG_JFS_DEBUG
-static int loglevel_read(char *page, char **start, off_t off,
- int count, int *eof, void *data)
+static int jfs_loglevel_proc_show(struct seq_file *m, void *v)
{
- int len;
-
- len = sprintf(page, "%d\n", jfsloglevel);
-
- len -= off;
- *start = page + off;
-
- if (len > count)
- len = count;
- else
- *eof = 1;
-
- if (len < 0)
- len = 0;
+ seq_printf(m, "%d\n", jfsloglevel);
+ return 0;
+}
- return len;
+static int jfs_loglevel_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, jfs_loglevel_proc_show, NULL);
}
-static int loglevel_write(struct file *file, const char __user *buffer,
- unsigned long count, void *data)
+static ssize_t jfs_loglevel_proc_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *ppos)
{
char c;
@@ -65,22 +56,30 @@ static int loglevel_write(struct file *file, const char __user *buffer,
jfsloglevel = c - '0';
return count;
}
+
+static const struct file_operations jfs_loglevel_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = jfs_loglevel_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = jfs_loglevel_proc_write,
+};
#endif
static struct {
const char *name;
- read_proc_t *read_fn;
- write_proc_t *write_fn;
+ const struct file_operations *proc_fops;
} Entries[] = {
#ifdef CONFIG_JFS_STATISTICS
- { "lmstats", jfs_lmstats_read, },
- { "txstats", jfs_txstats_read, },
- { "xtstat", jfs_xtstat_read, },
- { "mpstat", jfs_mpstat_read, },
+ { "lmstats", &jfs_lmstats_proc_fops, },
+ { "txstats", &jfs_txstats_proc_fops, },
+ { "xtstat", &jfs_xtstat_proc_fops, },
+ { "mpstat", &jfs_mpstat_proc_fops, },
#endif
#ifdef CONFIG_JFS_DEBUG
- { "TxAnchor", jfs_txanchor_read, },
- { "loglevel", loglevel_read, loglevel_write }
+ { "TxAnchor", &jfs_txanchor_proc_fops, },
+ { "loglevel", &jfs_loglevel_proc_fops }
#endif
};
#define NPROCENT ARRAY_SIZE(Entries)
@@ -93,13 +92,8 @@ void jfs_proc_init(void)
return;
base->owner = THIS_MODULE;
- for (i = 0; i < NPROCENT; i++) {
- struct proc_dir_entry *p;
- if ((p = create_proc_entry(Entries[i].name, 0, base))) {
- p->read_proc = Entries[i].read_fn;
- p->write_proc = Entries[i].write_fn;
- }
- }
+ for (i = 0; i < NPROCENT; i++)
+ proc_create(Entries[i].name, 0, base, Entries[i].proc_fops);
}
void jfs_proc_clean(void)
diff --git a/fs/jfs/jfs_debug.h b/fs/jfs/jfs_debug.h
index 044c1e654cc0..eafd1300a00b 100644
--- a/fs/jfs/jfs_debug.h
+++ b/fs/jfs/jfs_debug.h
@@ -62,7 +62,7 @@ extern void jfs_proc_clean(void);
extern int jfsloglevel;
-extern int jfs_txanchor_read(char *, char **, off_t, int, int *, void *);
+extern const struct file_operations jfs_txanchor_proc_fops;
/* information message: e.g., configuration, major event */
#define jfs_info(fmt, arg...) do { \
@@ -105,10 +105,10 @@ extern int jfs_txanchor_read(char *, char **, off_t, int, int *, void *);
* ----------
*/
#ifdef CONFIG_JFS_STATISTICS
-extern int jfs_lmstats_read(char *, char **, off_t, int, int *, void *);
-extern int jfs_txstats_read(char *, char **, off_t, int, int *, void *);
-extern int jfs_mpstat_read(char *, char **, off_t, int, int *, void *);
-extern int jfs_xtstat_read(char *, char **, off_t, int, int *, void *);
+extern const struct file_operations jfs_lmstats_proc_fops;
+extern const struct file_operations jfs_txstats_proc_fops;
+extern const struct file_operations jfs_mpstat_proc_fops;
+extern const struct file_operations jfs_xtstat_proc_fops;
#define INCREMENT(x) ((x)++)
#define DECREMENT(x) ((x)--)
diff --git a/fs/jfs/jfs_dtree.h b/fs/jfs/jfs_dtree.h
index cdac2d5bafeb..2545bb317235 100644
--- a/fs/jfs/jfs_dtree.h
+++ b/fs/jfs/jfs_dtree.h
@@ -243,9 +243,6 @@ typedef union {
#define JFS_REMOVE 3
#define JFS_RENAME 4
-#define DIRENTSIZ(namlen) \
- ( (sizeof(struct dirent) - 2*(JFS_NAME_MAX+1) + 2*((namlen)+1) + 3) &~ 3 )
-
/*
* Maximum file offset for directories.
*/
diff --git a/fs/jfs/jfs_imap.c b/fs/jfs/jfs_imap.c
index 734ec916beaf..d6363d8309d0 100644
--- a/fs/jfs/jfs_imap.c
+++ b/fs/jfs/jfs_imap.c
@@ -1520,7 +1520,7 @@ int diAlloc(struct inode *pip, bool dir, struct inode *ip)
jfs_error(ip->i_sb,
"diAlloc: can't find free bit "
"in wmap");
- return EIO;
+ return -EIO;
}
/* determine the inode number within the
diff --git a/fs/jfs/jfs_logmgr.c b/fs/jfs/jfs_logmgr.c
index 325a9679b95a..cd2ec2988b59 100644
--- a/fs/jfs/jfs_logmgr.c
+++ b/fs/jfs/jfs_logmgr.c
@@ -69,6 +69,7 @@
#include <linux/freezer.h>
#include <linux/delay.h>
#include <linux/mutex.h>
+#include <linux/seq_file.h>
#include "jfs_incore.h"
#include "jfs_filsys.h"
#include "jfs_metapage.h"
@@ -2503,13 +2504,9 @@ exit:
}
#ifdef CONFIG_JFS_STATISTICS
-int jfs_lmstats_read(char *buffer, char **start, off_t offset, int length,
- int *eof, void *data)
+static int jfs_lmstats_proc_show(struct seq_file *m, void *v)
{
- int len = 0;
- off_t begin;
-
- len += sprintf(buffer,
+ seq_printf(m,
"JFS Logmgr stats\n"
"================\n"
"commits = %d\n"
@@ -2522,19 +2519,19 @@ int jfs_lmstats_read(char *buffer, char **start, off_t offset, int length,
lmStat.pagedone,
lmStat.full_page,
lmStat.partial_page);
+ return 0;
+}
- begin = offset;
- *start = buffer + begin;
- len -= begin;
-
- if (len > length)
- len = length;
- else
- *eof = 1;
-
- if (len < 0)
- len = 0;
-
- return len;
+static int jfs_lmstats_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, jfs_lmstats_proc_show, NULL);
}
+
+const struct file_operations jfs_lmstats_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = jfs_lmstats_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
#endif /* CONFIG_JFS_STATISTICS */
diff --git a/fs/jfs/jfs_metapage.c b/fs/jfs/jfs_metapage.c
index d1e64f2f2fcd..854ff0ec574f 100644
--- a/fs/jfs/jfs_metapage.c
+++ b/fs/jfs/jfs_metapage.c
@@ -19,10 +19,12 @@
#include <linux/fs.h>
#include <linux/mm.h>
+#include <linux/module.h>
#include <linux/bio.h>
#include <linux/init.h>
#include <linux/buffer_head.h>
#include <linux/mempool.h>
+#include <linux/seq_file.h>
#include "jfs_incore.h"
#include "jfs_superblock.h"
#include "jfs_filsys.h"
@@ -804,13 +806,9 @@ void __invalidate_metapages(struct inode *ip, s64 addr, int len)
}
#ifdef CONFIG_JFS_STATISTICS
-int jfs_mpstat_read(char *buffer, char **start, off_t offset, int length,
- int *eof, void *data)
+static int jfs_mpstat_proc_show(struct seq_file *m, void *v)
{
- int len = 0;
- off_t begin;
-
- len += sprintf(buffer,
+ seq_printf(m,
"JFS Metapage statistics\n"
"=======================\n"
"page allocations = %d\n"
@@ -819,19 +817,19 @@ int jfs_mpstat_read(char *buffer, char **start, off_t offset, int length,
mpStat.pagealloc,
mpStat.pagefree,
mpStat.lockwait);
+ return 0;
+}
- begin = offset;
- *start = buffer + begin;
- len -= begin;
-
- if (len > length)
- len = length;
- else
- *eof = 1;
-
- if (len < 0)
- len = 0;
-
- return len;
+static int jfs_mpstat_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, jfs_mpstat_proc_show, NULL);
}
+
+const struct file_operations jfs_mpstat_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = jfs_mpstat_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
#endif
diff --git a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c
index e7c60ae6b5b2..f26e4d03ada5 100644
--- a/fs/jfs/jfs_txnmgr.c
+++ b/fs/jfs/jfs_txnmgr.c
@@ -49,6 +49,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kthread.h>
+#include <linux/seq_file.h>
#include "jfs_incore.h"
#include "jfs_inode.h"
#include "jfs_filsys.h"
@@ -3009,11 +3010,8 @@ int jfs_sync(void *arg)
}
#if defined(CONFIG_PROC_FS) && defined(CONFIG_JFS_DEBUG)
-int jfs_txanchor_read(char *buffer, char **start, off_t offset, int length,
- int *eof, void *data)
+static int jfs_txanchor_proc_show(struct seq_file *m, void *v)
{
- int len = 0;
- off_t begin;
char *freewait;
char *freelockwait;
char *lowlockwait;
@@ -3025,7 +3023,7 @@ int jfs_txanchor_read(char *buffer, char **start, off_t offset, int length,
lowlockwait =
waitqueue_active(&TxAnchor.lowlockwait) ? "active" : "empty";
- len += sprintf(buffer,
+ seq_printf(m,
"JFS TxAnchor\n"
"============\n"
"freetid = %d\n"
@@ -3044,31 +3042,27 @@ int jfs_txanchor_read(char *buffer, char **start, off_t offset, int length,
TxAnchor.tlocksInUse,
jfs_tlocks_low,
list_empty(&TxAnchor.unlock_queue) ? "" : "not ");
+ return 0;
+}
- begin = offset;
- *start = buffer + begin;
- len -= begin;
-
- if (len > length)
- len = length;
- else
- *eof = 1;
-
- if (len < 0)
- len = 0;
-
- return len;
+static int jfs_txanchor_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, jfs_txanchor_proc_show, NULL);
}
+
+const struct file_operations jfs_txanchor_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = jfs_txanchor_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
#endif
#if defined(CONFIG_PROC_FS) && defined(CONFIG_JFS_STATISTICS)
-int jfs_txstats_read(char *buffer, char **start, off_t offset, int length,
- int *eof, void *data)
+static int jfs_txstats_proc_show(struct seq_file *m, void *v)
{
- int len = 0;
- off_t begin;
-
- len += sprintf(buffer,
+ seq_printf(m,
"JFS TxStats\n"
"===========\n"
"calls to txBegin = %d\n"
@@ -3089,19 +3083,19 @@ int jfs_txstats_read(char *buffer, char **start, off_t offset, int length,
TxStat.txBeginAnon_lockslow,
TxStat.txLockAlloc,
TxStat.txLockAlloc_freelock);
+ return 0;
+}
- begin = offset;
- *start = buffer + begin;
- len -= begin;
-
- if (len > length)
- len = length;
- else
- *eof = 1;
-
- if (len < 0)
- len = 0;
-
- return len;
+static int jfs_txstats_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, jfs_txstats_proc_show, NULL);
}
+
+const struct file_operations jfs_txstats_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = jfs_txstats_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
#endif
diff --git a/fs/jfs/jfs_xtree.c b/fs/jfs/jfs_xtree.c
index 5a61ebf2cbcc..ae3acafb447b 100644
--- a/fs/jfs/jfs_xtree.c
+++ b/fs/jfs/jfs_xtree.c
@@ -20,7 +20,9 @@
*/
#include <linux/fs.h>
+#include <linux/module.h>
#include <linux/quotaops.h>
+#include <linux/seq_file.h>
#include "jfs_incore.h"
#include "jfs_filsys.h"
#include "jfs_metapage.h"
@@ -4134,13 +4136,9 @@ s64 xtTruncate_pmap(tid_t tid, struct inode *ip, s64 committed_size)
}
#ifdef CONFIG_JFS_STATISTICS
-int jfs_xtstat_read(char *buffer, char **start, off_t offset, int length,
- int *eof, void *data)
+static int jfs_xtstat_proc_show(struct seq_file *m, void *v)
{
- int len = 0;
- off_t begin;
-
- len += sprintf(buffer,
+ seq_printf(m,
"JFS Xtree statistics\n"
"====================\n"
"searches = %d\n"
@@ -4149,19 +4147,19 @@ int jfs_xtstat_read(char *buffer, char **start, off_t offset, int length,
xtStat.search,
xtStat.fastSearch,
xtStat.split);
+ return 0;
+}
- begin = offset;
- *start = buffer + begin;
- len -= begin;
-
- if (len > length)
- len = length;
- else
- *eof = 1;
-
- if (len < 0)
- len = 0;
-
- return len;
+static int jfs_xtstat_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, jfs_xtstat_proc_show, NULL);
}
+
+const struct file_operations jfs_xtstat_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = jfs_xtstat_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
#endif
diff --git a/fs/jfs/namei.c b/fs/jfs/namei.c
index 0ba6778edaa2..2aba82386810 100644
--- a/fs/jfs/namei.c
+++ b/fs/jfs/namei.c
@@ -1455,7 +1455,7 @@ static struct dentry *jfs_lookup(struct inode *dip, struct dentry *dentry, struc
free_UCSname(&key);
if (rc == -ENOENT) {
d_add(dentry, NULL);
- return ERR_PTR(0);
+ return NULL;
} else if (rc) {
jfs_err("jfs_lookup: dtSearch returned %d", rc);
return ERR_PTR(rc);
diff --git a/fs/jfs/super.c b/fs/jfs/super.c
index 50ea65451732..0288e6d7936a 100644
--- a/fs/jfs/super.c
+++ b/fs/jfs/super.c
@@ -499,7 +499,7 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent)
inode = jfs_iget(sb, ROOT_I);
if (IS_ERR(inode)) {
ret = PTR_ERR(inode);
- goto out_no_root;
+ goto out_no_rw;
}
sb->s_root = d_alloc_root(inode);
if (!sb->s_root)
@@ -521,9 +521,8 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent)
return 0;
out_no_root:
- jfs_err("jfs_read_super: get root inode failed");
- if (inode)
- iput(inode);
+ jfs_err("jfs_read_super: get root dentry failed");
+ iput(inode);
out_no_rw:
rc = jfs_umount(sb);
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index c309c881bd4e..313484380a9b 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -952,6 +952,7 @@ encode_op:
out:
nfsd4_release_compoundargs(args);
cstate_free(cstate);
+ dprintk("nfsv4 compound returned %d\n", ntohl(status));
return status;
}
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 8799b8708188..bf11d6879ab4 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1579,8 +1579,8 @@ nfs4_upgrade_open(struct svc_rqst *rqstp, struct svc_fh *cur_fh, struct nfs4_sta
}
/* remember the open */
filp->f_mode |= open->op_share_access;
- set_bit(open->op_share_access, &stp->st_access_bmap);
- set_bit(open->op_share_deny, &stp->st_deny_bmap);
+ __set_bit(open->op_share_access, &stp->st_access_bmap);
+ __set_bit(open->op_share_deny, &stp->st_deny_bmap);
return nfs_ok;
}
@@ -3249,12 +3249,14 @@ nfs4_state_shutdown(void)
nfs4_unlock_state();
}
+/*
+ * user_recovery_dirname is protected by the nfsd_mutex since it's only
+ * accessed when nfsd is starting.
+ */
static void
nfs4_set_recdir(char *recdir)
{
- nfs4_lock_state();
strcpy(user_recovery_dirname, recdir);
- nfs4_unlock_state();
}
/*
@@ -3278,6 +3280,12 @@ nfs4_reset_recoverydir(char *recdir)
return status;
}
+char *
+nfs4_recoverydir(void)
+{
+ return user_recovery_dirname;
+}
+
/*
* Called when leasetime is changed.
*
@@ -3286,11 +3294,12 @@ nfs4_reset_recoverydir(char *recdir)
* we start to register any changes in lease time. If the administrator
* really wants to change the lease time *now*, they can go ahead and bring
* nfsd down and then back up again after changing the lease time.
+ *
+ * user_lease_time is protected by nfsd_mutex since it's only really accessed
+ * when nfsd is starting
*/
void
nfs4_reset_lease(time_t leasetime)
{
- lock_kernel();
user_lease_time = leasetime;
- unlock_kernel();
}
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index c513bbdf2d36..9547ab636274 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -1201,7 +1201,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
*p++ = htonl((u32)((n) >> 32)); \
*p++ = htonl((u32)(n)); \
} while (0)
-#define WRITEMEM(ptr,nbytes) do { \
+#define WRITEMEM(ptr,nbytes) do if (nbytes > 0) { \
*(p + XDR_QUADLEN(nbytes) -1) = 0; \
memcpy(p, ptr, nbytes); \
p += XDR_QUADLEN(nbytes); \
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index 5ac00c4fee91..2c2eb8796c10 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -450,22 +450,26 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
int i;
int rv;
int len;
- int npools = nfsd_nrpools();
+ int npools;
int *nthreads;
+ mutex_lock(&nfsd_mutex);
+ npools = nfsd_nrpools();
if (npools == 0) {
/*
* NFS is shut down. The admin can start it by
* writing to the threads file but NOT the pool_threads
* file, sorry. Report zero threads.
*/
+ mutex_unlock(&nfsd_mutex);
strcpy(buf, "0\n");
return strlen(buf);
}
nthreads = kcalloc(npools, sizeof(int), GFP_KERNEL);
+ rv = -ENOMEM;
if (nthreads == NULL)
- return -ENOMEM;
+ goto out_free;
if (size > 0) {
for (i = 0; i < npools; i++) {
@@ -496,14 +500,16 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
mesg += len;
}
+ mutex_unlock(&nfsd_mutex);
return (mesg-buf);
out_free:
kfree(nthreads);
+ mutex_unlock(&nfsd_mutex);
return rv;
}
-static ssize_t write_versions(struct file *file, char *buf, size_t size)
+static ssize_t __write_versions(struct file *file, char *buf, size_t size)
{
/*
* Format:
@@ -566,14 +572,23 @@ static ssize_t write_versions(struct file *file, char *buf, size_t size)
return len;
}
-static ssize_t write_ports(struct file *file, char *buf, size_t size)
+static ssize_t write_versions(struct file *file, char *buf, size_t size)
+{
+ ssize_t rv;
+
+ mutex_lock(&nfsd_mutex);
+ rv = __write_versions(file, buf, size);
+ mutex_unlock(&nfsd_mutex);
+ return rv;
+}
+
+static ssize_t __write_ports(struct file *file, char *buf, size_t size)
{
if (size == 0) {
int len = 0;
- lock_kernel();
+
if (nfsd_serv)
len = svc_xprt_names(nfsd_serv, buf, 0);
- unlock_kernel();
return len;
}
/* Either a single 'fd' number is written, in which
@@ -603,9 +618,7 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size)
/* Decrease the count, but don't shutdown the
* the service
*/
- lock_kernel();
nfsd_serv->sv_nrthreads--;
- unlock_kernel();
}
return err < 0 ? err : 0;
}
@@ -614,10 +627,8 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size)
int len = 0;
if (!toclose)
return -ENOMEM;
- lock_kernel();
if (nfsd_serv)
len = svc_sock_names(buf, nfsd_serv, toclose);
- unlock_kernel();
if (len >= 0)
lockd_down();
kfree(toclose);
@@ -655,7 +666,6 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size)
if (sscanf(&buf[1], "%15s %4d", transport, &port) == 2) {
if (port == 0)
return -EINVAL;
- lock_kernel();
if (nfsd_serv) {
xprt = svc_find_xprt(nfsd_serv, transport,
AF_UNSPEC, port);
@@ -666,13 +676,23 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size)
} else
err = -ENOTCONN;
}
- unlock_kernel();
return err < 0 ? err : 0;
}
}
return -EINVAL;
}
+static ssize_t write_ports(struct file *file, char *buf, size_t size)
+{
+ ssize_t rv;
+
+ mutex_lock(&nfsd_mutex);
+ rv = __write_ports(file, buf, size);
+ mutex_unlock(&nfsd_mutex);
+ return rv;
+}
+
+
int nfsd_max_blksize;
static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
@@ -691,13 +711,13 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
if (bsize > NFSSVC_MAXBLKSIZE)
bsize = NFSSVC_MAXBLKSIZE;
bsize &= ~(1024-1);
- lock_kernel();
+ mutex_lock(&nfsd_mutex);
if (nfsd_serv && nfsd_serv->sv_nrthreads) {
- unlock_kernel();
+ mutex_unlock(&nfsd_mutex);
return -EBUSY;
}
nfsd_max_blksize = bsize;
- unlock_kernel();
+ mutex_unlock(&nfsd_mutex);
}
return sprintf(buf, "%d\n", nfsd_max_blksize);
}
@@ -705,16 +725,17 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
#ifdef CONFIG_NFSD_V4
extern time_t nfs4_leasetime(void);
-static ssize_t write_leasetime(struct file *file, char *buf, size_t size)
+static ssize_t __write_leasetime(struct file *file, char *buf, size_t size)
{
/* if size > 10 seconds, call
* nfs4_reset_lease() then write out the new lease (seconds) as reply
*/
char *mesg = buf;
- int rv;
+ int rv, lease;
if (size > 0) {
- int lease;
+ if (nfsd_serv)
+ return -EBUSY;
rv = get_int(&mesg, &lease);
if (rv)
return rv;
@@ -726,24 +747,52 @@ static ssize_t write_leasetime(struct file *file, char *buf, size_t size)
return strlen(buf);
}
-static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
+static ssize_t write_leasetime(struct file *file, char *buf, size_t size)
+{
+ ssize_t rv;
+
+ mutex_lock(&nfsd_mutex);
+ rv = __write_leasetime(file, buf, size);
+ mutex_unlock(&nfsd_mutex);
+ return rv;
+}
+
+extern char *nfs4_recoverydir(void);
+
+static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size)
{
char *mesg = buf;
char *recdir;
int len, status;
- if (size == 0 || size > PATH_MAX || buf[size-1] != '\n')
- return -EINVAL;
- buf[size-1] = 0;
+ if (size > 0) {
+ if (nfsd_serv)
+ return -EBUSY;
+ if (size > PATH_MAX || buf[size-1] != '\n')
+ return -EINVAL;
+ buf[size-1] = 0;
- recdir = mesg;
- len = qword_get(&mesg, recdir, size);
- if (len <= 0)
- return -EINVAL;
+ recdir = mesg;
+ len = qword_get(&mesg, recdir, size);
+ if (len <= 0)
+ return -EINVAL;
- status = nfs4_reset_recoverydir(recdir);
+ status = nfs4_reset_recoverydir(recdir);
+ }
+ sprintf(buf, "%s\n", nfs4_recoverydir());
return strlen(buf);
}
+
+static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
+{
+ ssize_t rv;
+
+ mutex_lock(&nfsd_mutex);
+ rv = __write_recoverydir(file, buf, size);
+ mutex_unlock(&nfsd_mutex);
+ return rv;
+}
+
#endif
/*----------------------------------------------------------------------------*/
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 941041f4b136..26c81149d49a 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -21,6 +21,7 @@
#include <linux/smp_lock.h>
#include <linux/freezer.h>
#include <linux/fs_struct.h>
+#include <linux/kthread.h>
#include <linux/sunrpc/types.h>
#include <linux/sunrpc/stats.h>
@@ -44,20 +45,39 @@
* when not handling a request. i.e. when waiting
*/
#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGHUP) | sigmask(SIGINT) | sigmask(SIGQUIT))
-/* if the last thread dies with SIGHUP, then the exports table is
- * left unchanged ( like 2.4-{0-9} ). Any other signal will clear
- * the exports table (like 2.2).
- */
-#define SIG_NOCLEAN SIGHUP
extern struct svc_program nfsd_program;
-static void nfsd(struct svc_rqst *rqstp);
+static int nfsd(void *vrqstp);
struct timeval nfssvc_boot;
- struct svc_serv *nfsd_serv;
static atomic_t nfsd_busy;
static unsigned long nfsd_last_call;
static DEFINE_SPINLOCK(nfsd_call_lock);
+/*
+ * nfsd_mutex protects nfsd_serv -- both the pointer itself and the members
+ * of the svc_serv struct. In particular, ->sv_nrthreads but also to some
+ * extent ->sv_temp_socks and ->sv_permsocks. It also protects nfsdstats.th_cnt
+ *
+ * If (out side the lock) nfsd_serv is non-NULL, then it must point to a
+ * properly initialised 'struct svc_serv' with ->sv_nrthreads > 0. That number
+ * of nfsd threads must exist and each must listed in ->sp_all_threads in each
+ * entry of ->sv_pools[].
+ *
+ * Transitions of the thread count between zero and non-zero are of particular
+ * interest since the svc_serv needs to be created and initialized at that
+ * point, or freed.
+ *
+ * Finally, the nfsd_mutex also protects some of the global variables that are
+ * accessed when nfsd starts and that are settable via the write_* routines in
+ * nfsctl.c. In particular:
+ *
+ * user_recovery_dirname
+ * user_lease_time
+ * nfsd_versions
+ */
+DEFINE_MUTEX(nfsd_mutex);
+struct svc_serv *nfsd_serv;
+
#if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL)
static struct svc_stat nfsd_acl_svcstats;
static struct svc_version * nfsd_acl_version[] = {
@@ -151,7 +171,6 @@ int nfsd_nrthreads(void)
return nfsd_serv->sv_nrthreads;
}
-static int killsig; /* signal that was used to kill last nfsd */
static void nfsd_last_thread(struct svc_serv *serv)
{
/* When last nfsd thread exits we need to do some clean-up */
@@ -162,11 +181,9 @@ static void nfsd_last_thread(struct svc_serv *serv)
nfsd_racache_shutdown();
nfs4_state_shutdown();
- printk(KERN_WARNING "nfsd: last server has exited\n");
- if (killsig != SIG_NOCLEAN) {
- printk(KERN_WARNING "nfsd: unexporting all filesystems\n");
- nfsd_export_flush();
- }
+ printk(KERN_WARNING "nfsd: last server has exited, flushing export "
+ "cache\n");
+ nfsd_export_flush();
}
void nfsd_reset_versions(void)
@@ -190,13 +207,14 @@ void nfsd_reset_versions(void)
}
}
+
int nfsd_create_serv(void)
{
int err = 0;
- lock_kernel();
+
+ WARN_ON(!mutex_is_locked(&nfsd_mutex));
if (nfsd_serv) {
svc_get(nfsd_serv);
- unlock_kernel();
return 0;
}
if (nfsd_max_blksize == 0) {
@@ -217,13 +235,11 @@ int nfsd_create_serv(void)
}
atomic_set(&nfsd_busy, 0);
- nfsd_serv = svc_create_pooled(&nfsd_program,
- nfsd_max_blksize,
- nfsd_last_thread,
- nfsd, SIG_NOCLEAN, THIS_MODULE);
+ nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize,
+ nfsd_last_thread, nfsd, THIS_MODULE);
if (nfsd_serv == NULL)
err = -ENOMEM;
- unlock_kernel();
+
do_gettimeofday(&nfssvc_boot); /* record boot time */
return err;
}
@@ -282,6 +298,8 @@ int nfsd_set_nrthreads(int n, int *nthreads)
int tot = 0;
int err = 0;
+ WARN_ON(!mutex_is_locked(&nfsd_mutex));
+
if (nfsd_serv == NULL || n <= 0)
return 0;
@@ -316,7 +334,6 @@ int nfsd_set_nrthreads(int n, int *nthreads)
nthreads[0] = 1;
/* apply the new numbers */
- lock_kernel();
svc_get(nfsd_serv);
for (i = 0; i < n; i++) {
err = svc_set_num_threads(nfsd_serv, &nfsd_serv->sv_pools[i],
@@ -325,7 +342,6 @@ int nfsd_set_nrthreads(int n, int *nthreads)
break;
}
svc_destroy(nfsd_serv);
- unlock_kernel();
return err;
}
@@ -334,8 +350,8 @@ int
nfsd_svc(unsigned short port, int nrservs)
{
int error;
-
- lock_kernel();
+
+ mutex_lock(&nfsd_mutex);
dprintk("nfsd: creating service\n");
error = -EINVAL;
if (nrservs <= 0)
@@ -363,7 +379,7 @@ nfsd_svc(unsigned short port, int nrservs)
failure:
svc_destroy(nfsd_serv); /* Release server */
out:
- unlock_kernel();
+ mutex_unlock(&nfsd_mutex);
return error;
}
@@ -391,18 +407,19 @@ update_thread_usage(int busy_threads)
/*
* This is the NFS server kernel thread
*/
-static void
-nfsd(struct svc_rqst *rqstp)
+static int
+nfsd(void *vrqstp)
{
+ struct svc_rqst *rqstp = (struct svc_rqst *) vrqstp;
struct fs_struct *fsp;
- int err;
sigset_t shutdown_mask, allowed_mask;
+ int err, preverr = 0;
+ unsigned int signo;
/* Lock module and set up kernel thread */
- lock_kernel();
- daemonize("nfsd");
+ mutex_lock(&nfsd_mutex);
- /* After daemonize() this kernel thread shares current->fs
+ /* At this point, the thread shares current->fs
* with the init process. We need to create files with a
* umask of 0 instead of init's umask. */
fsp = copy_fs_struct(current->fs);
@@ -417,11 +434,17 @@ nfsd(struct svc_rqst *rqstp)
siginitsetinv(&shutdown_mask, SHUTDOWN_SIGS);
siginitsetinv(&allowed_mask, ALLOWED_SIGS);
- nfsdstats.th_cnt++;
-
- rqstp->rq_task = current;
+ /*
+ * thread is spawned with all signals set to SIG_IGN, re-enable
+ * the ones that matter
+ */
+ for (signo = 1; signo <= _NSIG; signo++) {
+ if (!sigismember(&shutdown_mask, signo))
+ allow_signal(signo);
+ }
- unlock_kernel();
+ nfsdstats.th_cnt++;
+ mutex_unlock(&nfsd_mutex);
/*
* We want less throttling in balance_dirty_pages() so that nfs to
@@ -444,15 +467,25 @@ nfsd(struct svc_rqst *rqstp)
*/
while ((err = svc_recv(rqstp, 60*60*HZ)) == -EAGAIN)
;
- if (err < 0)
+ if (err == -EINTR)
break;
+ else if (err < 0) {
+ if (err != preverr) {
+ printk(KERN_WARNING "%s: unexpected error "
+ "from svc_recv (%d)\n", __func__, -err);
+ preverr = err;
+ }
+ schedule_timeout_uninterruptible(HZ);
+ continue;
+ }
+
update_thread_usage(atomic_read(&nfsd_busy));
atomic_inc(&nfsd_busy);
/* Lock the export hash tables for reading. */
exp_readlock();
- /* Process request with signals blocked. */
+ /* Process request with signals blocked. */
sigprocmask(SIG_SETMASK, &allowed_mask, NULL);
svc_process(rqstp);
@@ -463,22 +496,10 @@ nfsd(struct svc_rqst *rqstp)
atomic_dec(&nfsd_busy);
}
- if (err != -EINTR) {
- printk(KERN_WARNING "nfsd: terminating on error %d\n", -err);
- } else {
- unsigned int signo;
-
- for (signo = 1; signo <= _NSIG; signo++)
- if (sigismember(&current->pending.signal, signo) &&
- !sigismember(&current->blocked, signo))
- break;
- killsig = signo;
- }
/* Clear signals before calling svc_exit_thread() */
flush_signals(current);
- lock_kernel();
-
+ mutex_lock(&nfsd_mutex);
nfsdstats.th_cnt --;
out:
@@ -486,8 +507,9 @@ out:
svc_exit_thread(rqstp);
/* Release module */
- unlock_kernel();
+ mutex_unlock(&nfsd_mutex);
module_put_and_exit(0);
+ return 0;
}
static __be32 map_new_errors(u32 vers, __be32 nfserr)
diff --git a/fs/ocfs2/cluster/netdebug.c b/fs/ocfs2/cluster/netdebug.c
index 7bf3c0ea7bd9..d8bfa0eb41b2 100644
--- a/fs/ocfs2/cluster/netdebug.c
+++ b/fs/ocfs2/cluster/netdebug.c
@@ -146,8 +146,10 @@ static int nst_seq_show(struct seq_file *seq, void *v)
nst->st_task->comm, nst->st_node,
nst->st_sc, nst->st_id, nst->st_msg_type,
nst->st_msg_key,
- nst->st_sock_time.tv_sec, nst->st_sock_time.tv_usec,
- nst->st_send_time.tv_sec, nst->st_send_time.tv_usec,
+ nst->st_sock_time.tv_sec,
+ (unsigned long)nst->st_sock_time.tv_usec,
+ nst->st_send_time.tv_sec,
+ (unsigned long)nst->st_send_time.tv_usec,
nst->st_status_time.tv_sec,
nst->st_status_time.tv_usec);
}
@@ -274,7 +276,7 @@ static void *sc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
return sc; /* unused, just needs to be null when done */
}
-#define TV_SEC_USEC(TV) TV.tv_sec, TV.tv_usec
+#define TV_SEC_USEC(TV) TV.tv_sec, (unsigned long)TV.tv_usec
static int sc_seq_show(struct seq_file *seq, void *v)
{
diff --git a/fs/ocfs2/cluster/nodemanager.c b/fs/ocfs2/cluster/nodemanager.c
index cf9401e8cd0b..cfdb08b484ed 100644
--- a/fs/ocfs2/cluster/nodemanager.c
+++ b/fs/ocfs2/cluster/nodemanager.c
@@ -21,7 +21,6 @@
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/sysctl.h>
#include <linux/configfs.h>
#include "tcp.h"
@@ -36,65 +35,6 @@
* cluster references throughout where nodes are looked up */
struct o2nm_cluster *o2nm_single_cluster = NULL;
-#define OCFS2_MAX_HB_CTL_PATH 256
-static char ocfs2_hb_ctl_path[OCFS2_MAX_HB_CTL_PATH] = "/sbin/ocfs2_hb_ctl";
-
-static ctl_table ocfs2_nm_table[] = {
- {
- .ctl_name = 1,
- .procname = "hb_ctl_path",
- .data = ocfs2_hb_ctl_path,
- .maxlen = OCFS2_MAX_HB_CTL_PATH,
- .mode = 0644,
- .proc_handler = &proc_dostring,
- .strategy = &sysctl_string,
- },
- { .ctl_name = 0 }
-};
-
-static ctl_table ocfs2_mod_table[] = {
- {
- .ctl_name = FS_OCFS2_NM,
- .procname = "nm",
- .data = NULL,
- .maxlen = 0,
- .mode = 0555,
- .child = ocfs2_nm_table
- },
- { .ctl_name = 0}
-};
-
-static ctl_table ocfs2_kern_table[] = {
- {
- .ctl_name = FS_OCFS2,
- .procname = "ocfs2",
- .data = NULL,
- .maxlen = 0,
- .mode = 0555,
- .child = ocfs2_mod_table
- },
- { .ctl_name = 0}
-};
-
-static ctl_table ocfs2_root_table[] = {
- {
- .ctl_name = CTL_FS,
- .procname = "fs",
- .data = NULL,
- .maxlen = 0,
- .mode = 0555,
- .child = ocfs2_kern_table
- },
- { .ctl_name = 0 }
-};
-
-static struct ctl_table_header *ocfs2_table_header = NULL;
-
-const char *o2nm_get_hb_ctl_path(void)
-{
- return ocfs2_hb_ctl_path;
-}
-EXPORT_SYMBOL_GPL(o2nm_get_hb_ctl_path);
struct o2nm_node *o2nm_get_node_by_num(u8 node_num)
{
@@ -941,9 +881,6 @@ void o2nm_undepend_this_node(void)
static void __exit exit_o2nm(void)
{
- if (ocfs2_table_header)
- unregister_sysctl_table(ocfs2_table_header);
-
/* XXX sync with hb callbacks and shut down hb? */
o2net_unregister_hb_callbacks();
configfs_unregister_subsystem(&o2nm_cluster_group.cs_subsys);
@@ -964,16 +901,9 @@ static int __init init_o2nm(void)
if (ret)
goto out;
- ocfs2_table_header = register_sysctl_table(ocfs2_root_table);
- if (!ocfs2_table_header) {
- printk(KERN_ERR "nodemanager: unable to register sysctl\n");
- ret = -ENOMEM; /* or something. */
- goto out_o2net;
- }
-
ret = o2net_register_hb_callbacks();
if (ret)
- goto out_sysctl;
+ goto out_o2net;
config_group_init(&o2nm_cluster_group.cs_subsys.su_group);
mutex_init(&o2nm_cluster_group.cs_subsys.su_mutex);
@@ -990,8 +920,6 @@ static int __init init_o2nm(void)
configfs_unregister_subsystem(&o2nm_cluster_group.cs_subsys);
out_callbacks:
o2net_unregister_hb_callbacks();
-out_sysctl:
- unregister_sysctl_table(ocfs2_table_header);
out_o2net:
o2net_exit();
out:
diff --git a/fs/ocfs2/cluster/nodemanager.h b/fs/ocfs2/cluster/nodemanager.h
index 7c860361b8dd..c992ea0da4ad 100644
--- a/fs/ocfs2/cluster/nodemanager.h
+++ b/fs/ocfs2/cluster/nodemanager.h
@@ -33,10 +33,6 @@
#include <linux/configfs.h>
#include <linux/rbtree.h>
-#define FS_OCFS2_NM 1
-
-const char *o2nm_get_hb_ctl_path(void);
-
struct o2nm_node {
spinlock_t nd_lock;
struct config_item nd_item;
diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c
index 394d25a131a5..1c6da696d213 100644
--- a/fs/ocfs2/dlmglue.c
+++ b/fs/ocfs2/dlmglue.c
@@ -31,6 +31,7 @@
#include <linux/pagemap.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <linux/time.h>
#define MLOG_MASK_PREFIX ML_DLM_GLUE
#include <cluster/masklog.h>
@@ -59,6 +60,9 @@ struct ocfs2_mask_waiter {
struct completion mw_complete;
unsigned long mw_mask;
unsigned long mw_goal;
+#ifdef CONFIG_OCFS2_FS_STATS
+ unsigned long long mw_lock_start;
+#endif
};
static struct ocfs2_super *ocfs2_get_dentry_osb(struct ocfs2_lock_res *lockres);
@@ -366,6 +370,75 @@ static void ocfs2_remove_lockres_tracking(struct ocfs2_lock_res *res)
spin_unlock(&ocfs2_dlm_tracking_lock);
}
+#ifdef CONFIG_OCFS2_FS_STATS
+static void ocfs2_init_lock_stats(struct ocfs2_lock_res *res)
+{
+ res->l_lock_num_prmode = 0;
+ res->l_lock_num_prmode_failed = 0;
+ res->l_lock_total_prmode = 0;
+ res->l_lock_max_prmode = 0;
+ res->l_lock_num_exmode = 0;
+ res->l_lock_num_exmode_failed = 0;
+ res->l_lock_total_exmode = 0;
+ res->l_lock_max_exmode = 0;
+ res->l_lock_refresh = 0;
+}
+
+static void ocfs2_update_lock_stats(struct ocfs2_lock_res *res, int level,
+ struct ocfs2_mask_waiter *mw, int ret)
+{
+ unsigned long long *num, *sum;
+ unsigned int *max, *failed;
+ struct timespec ts = current_kernel_time();
+ unsigned long long time = timespec_to_ns(&ts) - mw->mw_lock_start;
+
+ if (level == LKM_PRMODE) {
+ num = &res->l_lock_num_prmode;
+ sum = &res->l_lock_total_prmode;
+ max = &res->l_lock_max_prmode;
+ failed = &res->l_lock_num_prmode_failed;
+ } else if (level == LKM_EXMODE) {
+ num = &res->l_lock_num_exmode;
+ sum = &res->l_lock_total_exmode;
+ max = &res->l_lock_max_exmode;
+ failed = &res->l_lock_num_exmode_failed;
+ } else
+ return;
+
+ (*num)++;
+ (*sum) += time;
+ if (time > *max)
+ *max = time;
+ if (ret)
+ (*failed)++;
+}
+
+static inline void ocfs2_track_lock_refresh(struct ocfs2_lock_res *lockres)
+{
+ lockres->l_lock_refresh++;
+}
+
+static inline void ocfs2_init_start_time(struct ocfs2_mask_waiter *mw)
+{
+ struct timespec ts = current_kernel_time();
+ mw->mw_lock_start = timespec_to_ns(&ts);
+}
+#else
+static inline void ocfs2_init_lock_stats(struct ocfs2_lock_res *res)
+{
+}
+static inline void ocfs2_update_lock_stats(struct ocfs2_lock_res *res,
+ int level, struct ocfs2_mask_waiter *mw, int ret)
+{
+}
+static inline void ocfs2_track_lock_refresh(struct ocfs2_lock_res *lockres)
+{
+}
+static inline void ocfs2_init_start_time(struct ocfs2_mask_waiter *mw)
+{
+}
+#endif
+
static void ocfs2_lock_res_init_common(struct ocfs2_super *osb,
struct ocfs2_lock_res *res,
enum ocfs2_lock_type type,
@@ -385,6 +458,8 @@ static void ocfs2_lock_res_init_common(struct ocfs2_super *osb,
res->l_flags = OCFS2_LOCK_INITIALIZED;
ocfs2_add_lockres_tracking(res, osb->osb_dlm_debug);
+
+ ocfs2_init_lock_stats(res);
}
void ocfs2_lock_res_init_once(struct ocfs2_lock_res *res)
@@ -1048,6 +1123,7 @@ static void ocfs2_init_mask_waiter(struct ocfs2_mask_waiter *mw)
{
INIT_LIST_HEAD(&mw->mw_item);
init_completion(&mw->mw_complete);
+ ocfs2_init_start_time(mw);
}
static int ocfs2_wait_for_mask(struct ocfs2_mask_waiter *mw)
@@ -1254,6 +1330,7 @@ out:
goto again;
mlog_errno(ret);
}
+ ocfs2_update_lock_stats(lockres, level, &mw, ret);
mlog_exit(ret);
return ret;
@@ -1983,6 +2060,7 @@ static int ocfs2_inode_lock_update(struct inode *inode,
le32_to_cpu(fe->i_flags));
ocfs2_refresh_inode(inode, fe);
+ ocfs2_track_lock_refresh(lockres);
}
status = 0;
@@ -2267,6 +2345,7 @@ int ocfs2_super_lock(struct ocfs2_super *osb,
if (status < 0)
mlog_errno(status);
+ ocfs2_track_lock_refresh(lockres);
}
bail:
mlog_exit(status);
@@ -2461,7 +2540,7 @@ static void *ocfs2_dlm_seq_next(struct seq_file *m, void *v, loff_t *pos)
}
/* So that debugfs.ocfs2 can determine which format is being used */
-#define OCFS2_DLM_DEBUG_STR_VERSION 1
+#define OCFS2_DLM_DEBUG_STR_VERSION 2
static int ocfs2_dlm_seq_show(struct seq_file *m, void *v)
{
int i;
@@ -2502,6 +2581,47 @@ static int ocfs2_dlm_seq_show(struct seq_file *m, void *v)
for(i = 0; i < DLM_LVB_LEN; i++)
seq_printf(m, "0x%x\t", lvb[i]);
+#ifdef CONFIG_OCFS2_FS_STATS
+# define lock_num_prmode(_l) (_l)->l_lock_num_prmode
+# define lock_num_exmode(_l) (_l)->l_lock_num_exmode
+# define lock_num_prmode_failed(_l) (_l)->l_lock_num_prmode_failed
+# define lock_num_exmode_failed(_l) (_l)->l_lock_num_exmode_failed
+# define lock_total_prmode(_l) (_l)->l_lock_total_prmode
+# define lock_total_exmode(_l) (_l)->l_lock_total_exmode
+# define lock_max_prmode(_l) (_l)->l_lock_max_prmode
+# define lock_max_exmode(_l) (_l)->l_lock_max_exmode
+# define lock_refresh(_l) (_l)->l_lock_refresh
+#else
+# define lock_num_prmode(_l) (0ULL)
+# define lock_num_exmode(_l) (0ULL)
+# define lock_num_prmode_failed(_l) (0)
+# define lock_num_exmode_failed(_l) (0)
+# define lock_total_prmode(_l) (0ULL)
+# define lock_total_exmode(_l) (0ULL)
+# define lock_max_prmode(_l) (0)
+# define lock_max_exmode(_l) (0)
+# define lock_refresh(_l) (0)
+#endif
+ /* The following seq_print was added in version 2 of this output */
+ seq_printf(m, "%llu\t"
+ "%llu\t"
+ "%u\t"
+ "%u\t"
+ "%llu\t"
+ "%llu\t"
+ "%u\t"
+ "%u\t"
+ "%u\t",
+ lock_num_prmode(lockres),
+ lock_num_exmode(lockres),
+ lock_num_prmode_failed(lockres),
+ lock_num_exmode_failed(lockres),
+ lock_total_prmode(lockres),
+ lock_total_exmode(lockres),
+ lock_max_prmode(lockres),
+ lock_max_exmode(lockres),
+ lock_refresh(lockres));
+
/* End the line */
seq_printf(m, "\n");
return 0;
diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c
index 57e0d30cde98..e8514e8b6ce8 100644
--- a/fs/ocfs2/file.c
+++ b/fs/ocfs2/file.c
@@ -2202,7 +2202,7 @@ static ssize_t ocfs2_file_aio_read(struct kiocb *iocb,
ret = generic_file_aio_read(iocb, iov, nr_segs, iocb->ki_pos);
if (ret == -EINVAL)
- mlog(ML_ERROR, "generic_file_aio_read returned -EINVAL\n");
+ mlog(0, "generic_file_aio_read returned -EINVAL\n");
/* buffered aio wouldn't have proper lock coverage today */
BUG_ON(ret == -EIOCBQUEUED && !(filp->f_flags & O_DIRECT));
diff --git a/fs/ocfs2/ocfs2.h b/fs/ocfs2/ocfs2.h
index 31692379c170..1cb814be8ef1 100644
--- a/fs/ocfs2/ocfs2.h
+++ b/fs/ocfs2/ocfs2.h
@@ -132,6 +132,18 @@ struct ocfs2_lock_res {
wait_queue_head_t l_event;
struct list_head l_debug_list;
+
+#ifdef CONFIG_OCFS2_FS_STATS
+ unsigned long long l_lock_num_prmode; /* PR acquires */
+ unsigned long long l_lock_num_exmode; /* EX acquires */
+ unsigned int l_lock_num_prmode_failed; /* Failed PR gets */
+ unsigned int l_lock_num_exmode_failed; /* Failed EX gets */
+ unsigned long long l_lock_total_prmode; /* Tot wait for PR */
+ unsigned long long l_lock_total_exmode; /* Tot wait for EX */
+ unsigned int l_lock_max_prmode; /* Max wait for PR */
+ unsigned int l_lock_max_exmode; /* Max wait for EX */
+ unsigned int l_lock_refresh; /* Disk refreshes */
+#endif
};
struct ocfs2_dlm_debug {
diff --git a/fs/ocfs2/stack_o2cb.c b/fs/ocfs2/stack_o2cb.c
index bbd1667aa7d3..fcd120f1493a 100644
--- a/fs/ocfs2/stack_o2cb.c
+++ b/fs/ocfs2/stack_o2cb.c
@@ -317,8 +317,7 @@ out:
return rc;
}
-static int o2cb_cluster_disconnect(struct ocfs2_cluster_connection *conn,
- int hangup_pending)
+static int o2cb_cluster_disconnect(struct ocfs2_cluster_connection *conn)
{
struct dlm_ctxt *dlm = conn->cc_lockspace;
struct o2dlm_private *priv = conn->cc_private;
@@ -333,43 +332,6 @@ static int o2cb_cluster_disconnect(struct ocfs2_cluster_connection *conn,
return 0;
}
-static void o2hb_stop(const char *group)
-{
- int ret;
- char *argv[5], *envp[3];
-
- argv[0] = (char *)o2nm_get_hb_ctl_path();
- argv[1] = "-K";
- argv[2] = "-u";
- argv[3] = (char *)group;
- argv[4] = NULL;
-
- mlog(0, "Run: %s %s %s %s\n", argv[0], argv[1], argv[2], argv[3]);
-
- /* minimal command environment taken from cpu_run_sbin_hotplug */
- envp[0] = "HOME=/";
- envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
- envp[2] = NULL;
-
- ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
- if (ret < 0)
- mlog_errno(ret);
-}
-
-/*
- * Hangup is a hack for tools compatibility. Older ocfs2-tools software
- * expects the filesystem to call "ocfs2_hb_ctl" during unmount. This
- * happens regardless of whether the DLM got started, so we can't do it
- * in ocfs2_cluster_disconnect(). We bring the o2hb_stop() function into
- * the glue and provide a "hangup" API for super.c to call.
- *
- * Other stacks will eventually provide a NULL ->hangup() pointer.
- */
-static void o2cb_cluster_hangup(const char *group, int grouplen)
-{
- o2hb_stop(group);
-}
-
static int o2cb_cluster_this_node(unsigned int *node)
{
int node_num;
@@ -388,7 +350,6 @@ static int o2cb_cluster_this_node(unsigned int *node)
static struct ocfs2_stack_operations o2cb_stack_ops = {
.connect = o2cb_cluster_connect,
.disconnect = o2cb_cluster_disconnect,
- .hangup = o2cb_cluster_hangup,
.this_node = o2cb_cluster_this_node,
.dlm_lock = o2cb_dlm_lock,
.dlm_unlock = o2cb_dlm_unlock,
diff --git a/fs/ocfs2/stack_user.c b/fs/ocfs2/stack_user.c
index 6b97d11f6bf8..24e0b19c8b69 100644
--- a/fs/ocfs2/stack_user.c
+++ b/fs/ocfs2/stack_user.c
@@ -549,26 +549,17 @@ static ssize_t ocfs2_control_read(struct file *file,
size_t count,
loff_t *ppos)
{
- char *proto_string = OCFS2_CONTROL_PROTO;
- size_t to_write = 0;
-
- if (*ppos >= OCFS2_CONTROL_PROTO_LEN)
- return 0;
-
- to_write = OCFS2_CONTROL_PROTO_LEN - *ppos;
- if (to_write > count)
- to_write = count;
- if (copy_to_user(buf, proto_string + *ppos, to_write))
- return -EFAULT;
+ ssize_t ret;
- *ppos += to_write;
+ ret = simple_read_from_buffer(buf, count, ppos,
+ OCFS2_CONTROL_PROTO, OCFS2_CONTROL_PROTO_LEN);
/* Have we read the whole protocol list? */
- if (*ppos >= OCFS2_CONTROL_PROTO_LEN)
+ if (ret > 0 && *ppos >= OCFS2_CONTROL_PROTO_LEN)
ocfs2_control_set_handshake_state(file,
OCFS2_CONTROL_HANDSHAKE_READ);
- return to_write;
+ return ret;
}
static int ocfs2_control_release(struct inode *inode, struct file *file)
@@ -816,8 +807,7 @@ out:
return rc;
}
-static int user_cluster_disconnect(struct ocfs2_cluster_connection *conn,
- int hangup_pending)
+static int user_cluster_disconnect(struct ocfs2_cluster_connection *conn)
{
dlm_release_lockspace(conn->cc_lockspace, 2);
conn->cc_lockspace = NULL;
diff --git a/fs/ocfs2/stackglue.c b/fs/ocfs2/stackglue.c
index 119f60cea9cc..10e149ae5e3a 100644
--- a/fs/ocfs2/stackglue.c
+++ b/fs/ocfs2/stackglue.c
@@ -26,6 +26,7 @@
#include <linux/fs.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
+#include <linux/sysctl.h>
#include "ocfs2_fs.h"
@@ -33,11 +34,13 @@
#define OCFS2_STACK_PLUGIN_O2CB "o2cb"
#define OCFS2_STACK_PLUGIN_USER "user"
+#define OCFS2_MAX_HB_CTL_PATH 256
static struct ocfs2_locking_protocol *lproto;
static DEFINE_SPINLOCK(ocfs2_stack_lock);
static LIST_HEAD(ocfs2_stack_list);
static char cluster_stack_name[OCFS2_STACK_LABEL_LEN + 1];
+static char ocfs2_hb_ctl_path[OCFS2_MAX_HB_CTL_PATH] = "/sbin/ocfs2_hb_ctl";
/*
* The stack currently in use. If not null, active_stack->sp_count > 0,
@@ -349,7 +352,7 @@ int ocfs2_cluster_disconnect(struct ocfs2_cluster_connection *conn,
BUG_ON(conn == NULL);
- ret = active_stack->sp_ops->disconnect(conn, hangup_pending);
+ ret = active_stack->sp_ops->disconnect(conn);
/* XXX Should we free it anyway? */
if (!ret) {
@@ -362,13 +365,48 @@ int ocfs2_cluster_disconnect(struct ocfs2_cluster_connection *conn,
}
EXPORT_SYMBOL_GPL(ocfs2_cluster_disconnect);
+/*
+ * Leave the group for this filesystem. This is executed by a userspace
+ * program (stored in ocfs2_hb_ctl_path).
+ */
+static void ocfs2_leave_group(const char *group)
+{
+ int ret;
+ char *argv[5], *envp[3];
+
+ argv[0] = ocfs2_hb_ctl_path;
+ argv[1] = "-K";
+ argv[2] = "-u";
+ argv[3] = (char *)group;
+ argv[4] = NULL;
+
+ /* minimal command environment taken from cpu_run_sbin_hotplug */
+ envp[0] = "HOME=/";
+ envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+ envp[2] = NULL;
+
+ ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);
+ if (ret < 0) {
+ printk(KERN_ERR
+ "ocfs2: Error %d running user helper "
+ "\"%s %s %s %s\"\n",
+ ret, argv[0], argv[1], argv[2], argv[3]);
+ }
+}
+
+/*
+ * Hangup is a required post-umount. ocfs2-tools software expects the
+ * filesystem to call "ocfs2_hb_ctl" during unmount. This happens
+ * regardless of whether the DLM got started, so we can't do it
+ * in ocfs2_cluster_disconnect(). The ocfs2_leave_group() function does
+ * the actual work.
+ */
void ocfs2_cluster_hangup(const char *group, int grouplen)
{
BUG_ON(group == NULL);
BUG_ON(group[grouplen] != '\0');
- if (active_stack->sp_ops->hangup)
- active_stack->sp_ops->hangup(group, grouplen);
+ ocfs2_leave_group(group);
/* cluster_disconnect() was called with hangup_pending==1 */
ocfs2_stack_driver_put();
@@ -548,10 +586,83 @@ error:
return ret;
}
+/*
+ * Sysctl bits
+ *
+ * The sysctl lives at /proc/sys/fs/ocfs2/nm/hb_ctl_path. The 'nm' doesn't
+ * make as much sense in a multiple cluster stack world, but it's safer
+ * and easier to preserve the name.
+ */
+
+#define FS_OCFS2_NM 1
+
+static ctl_table ocfs2_nm_table[] = {
+ {
+ .ctl_name = 1,
+ .procname = "hb_ctl_path",
+ .data = ocfs2_hb_ctl_path,
+ .maxlen = OCFS2_MAX_HB_CTL_PATH,
+ .mode = 0644,
+ .proc_handler = &proc_dostring,
+ .strategy = &sysctl_string,
+ },
+ { .ctl_name = 0 }
+};
+
+static ctl_table ocfs2_mod_table[] = {
+ {
+ .ctl_name = FS_OCFS2_NM,
+ .procname = "nm",
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0555,
+ .child = ocfs2_nm_table
+ },
+ { .ctl_name = 0}
+};
+
+static ctl_table ocfs2_kern_table[] = {
+ {
+ .ctl_name = FS_OCFS2,
+ .procname = "ocfs2",
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0555,
+ .child = ocfs2_mod_table
+ },
+ { .ctl_name = 0}
+};
+
+static ctl_table ocfs2_root_table[] = {
+ {
+ .ctl_name = CTL_FS,
+ .procname = "fs",
+ .data = NULL,
+ .maxlen = 0,
+ .mode = 0555,
+ .child = ocfs2_kern_table
+ },
+ { .ctl_name = 0 }
+};
+
+static struct ctl_table_header *ocfs2_table_header = NULL;
+
+
+/*
+ * Initialization
+ */
+
static int __init ocfs2_stack_glue_init(void)
{
strcpy(cluster_stack_name, OCFS2_STACK_PLUGIN_O2CB);
+ ocfs2_table_header = register_sysctl_table(ocfs2_root_table);
+ if (!ocfs2_table_header) {
+ printk(KERN_ERR
+ "ocfs2 stack glue: unable to register sysctl\n");
+ return -ENOMEM; /* or something. */
+ }
+
return ocfs2_sysfs_init();
}
@@ -559,6 +670,8 @@ static void __exit ocfs2_stack_glue_exit(void)
{
lproto = NULL;
ocfs2_sysfs_exit();
+ if (ocfs2_table_header)
+ unregister_sysctl_table(ocfs2_table_header);
}
MODULE_AUTHOR("Oracle");
diff --git a/fs/ocfs2/stackglue.h b/fs/ocfs2/stackglue.h
index 005e4f170e0f..db56281dd1be 100644
--- a/fs/ocfs2/stackglue.h
+++ b/fs/ocfs2/stackglue.h
@@ -134,22 +134,10 @@ struct ocfs2_stack_operations {
* be freed. Thus, a stack must not return from ->disconnect()
* until it will no longer reference the conn pointer.
*
- * If hangup_pending is zero, ocfs2_cluster_disconnect() will also
- * be dropping the reference on the module.
+ * Once this call returns, the stack glue will be dropping this
+ * connection's reference on the module.
*/
- int (*disconnect)(struct ocfs2_cluster_connection *conn,
- int hangup_pending);
-
- /*
- * ocfs2_cluster_hangup() exists for compatibility with older
- * ocfs2 tools. Only the classic stack really needs it. As such
- * ->hangup() is not required of all stacks. See the comment by
- * ocfs2_cluster_hangup() for more details.
- *
- * Note that ocfs2_cluster_hangup() can only be called if
- * hangup_pending was passed to ocfs2_cluster_disconnect().
- */
- void (*hangup)(const char *group, int grouplen);
+ int (*disconnect)(struct ocfs2_cluster_connection *conn);
/*
* ->this_node() returns the cluster's unique identifier for the
@@ -258,4 +246,5 @@ void ocfs2_stack_glue_set_locking_protocol(struct ocfs2_locking_protocol *proto)
/* Used by stack plugins */
int ocfs2_stack_glue_register(struct ocfs2_stack_plugin *plugin);
void ocfs2_stack_glue_unregister(struct ocfs2_stack_plugin *plugin);
+
#endif /* STACKGLUE_H */
diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c
index df63ba20ae90..ccecfe5094fa 100644
--- a/fs/ocfs2/super.c
+++ b/fs/ocfs2/super.c
@@ -1703,7 +1703,11 @@ static int ocfs2_check_volume(struct ocfs2_super *osb)
local = ocfs2_mount_local(osb);
/* will play back anything left in the journal. */
- ocfs2_journal_load(osb->journal, local);
+ status = ocfs2_journal_load(osb->journal, local);
+ if (status < 0) {
+ mlog(ML_ERROR, "ocfs2 journal load failed! %d\n", status);
+ goto finally;
+ }
if (dirty) {
/* recover my local alloc if we didn't unmount cleanly. */
diff --git a/fs/partitions/check.c b/fs/partitions/check.c
index 6149e4b58c88..efef715135d3 100644
--- a/fs/partitions/check.c
+++ b/fs/partitions/check.c
@@ -401,7 +401,7 @@ void register_disk(struct gendisk *disk)
disk->dev.parent = disk->driverfs_dev;
disk->dev.devt = MKDEV(disk->major, disk->first_minor);
- strlcpy(disk->dev.bus_id, disk->disk_name, KOBJ_NAME_LEN);
+ strlcpy(disk->dev.bus_id, disk->disk_name, BUS_ID_SIZE);
/* ewww... some of these buggers have / in the name... */
s = strchr(disk->dev.bus_id, '/');
if (s)
diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c
index 7e277f2ad466..c652d469dc08 100644
--- a/fs/proc/proc_misc.c
+++ b/fs/proc/proc_misc.c
@@ -123,6 +123,11 @@ static int uptime_read_proc(char *page, char **start, off_t off,
return proc_calc_metrics(page, start, off, count, eof, len);
}
+int __attribute__((weak)) arch_report_meminfo(char *page)
+{
+ return 0;
+}
+
static int meminfo_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
@@ -221,6 +226,8 @@ static int meminfo_read_proc(char *page, char **start, off_t off,
len += hugetlb_report_meminfo(page + len);
+ len += arch_report_meminfo(page + len);
+
return proc_calc_metrics(page, start, off, count, eof, len);
#undef K
}
@@ -472,6 +479,13 @@ static const struct file_operations proc_vmalloc_operations = {
};
#endif
+#ifndef arch_irq_stat_cpu
+#define arch_irq_stat_cpu(cpu) 0
+#endif
+#ifndef arch_irq_stat
+#define arch_irq_stat() 0
+#endif
+
static int show_stat(struct seq_file *p, void *v)
{
int i;
@@ -509,7 +523,9 @@ static int show_stat(struct seq_file *p, void *v)
sum += temp;
per_irq_sum[j] += temp;
}
+ sum += arch_irq_stat_cpu(i);
}
+ sum += arch_irq_stat();
seq_printf(p, "cpu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
(unsigned long long)cputime64_to_clock_t(user),
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index e7735f643cd1..9db95a710e92 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -18,10 +18,18 @@
#include <linux/poll.h>
#include <linux/list.h>
#include <linux/mutex.h>
+#include <linux/limits.h>
#include <asm/uaccess.h>
#include "sysfs.h"
+/* used in crash dumps to help with debugging */
+static char last_sysfs_file[PATH_MAX];
+void sysfs_printk_last_file(void)
+{
+ printk(KERN_EMERG "last sysfs file: %s\n", last_sysfs_file);
+}
+
/*
* There's one sysfs_buffer for each open file and one
* sysfs_open_dirent for each sysfs_dirent with one or more open
@@ -327,6 +335,11 @@ static int sysfs_open_file(struct inode *inode, struct file *file)
struct sysfs_buffer *buffer;
struct sysfs_ops *ops;
int error = -EACCES;
+ char *p;
+
+ p = d_path(&file->f_path, last_sysfs_file, sizeof(last_sysfs_file));
+ if (p)
+ memmove(last_sysfs_file, p, strlen(p) + 1);
/* need attr_sd for attr and ops, its parent for kobj */
if (!sysfs_get_active_two(attr_sd))
diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c
index 14f0023984d7..9f328d2420e7 100644
--- a/fs/sysfs/mount.c
+++ b/fs/sysfs/mount.c
@@ -22,7 +22,7 @@
/* Random magic number */
#define SYSFS_MAGIC 0x62656572
-static struct vfsmount *sysfs_mount;
+struct vfsmount *sysfs_mount;
struct super_block * sysfs_sb = NULL;
struct kmem_cache *sysfs_dir_cachep;
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
index ce4e15f8aaeb..2915959a4499 100644
--- a/fs/sysfs/sysfs.h
+++ b/fs/sysfs/sysfs.h
@@ -91,6 +91,7 @@ struct sysfs_addrm_cxt {
extern struct sysfs_dirent sysfs_root;
extern struct super_block *sysfs_sb;
extern struct kmem_cache *sysfs_dir_cachep;
+extern struct vfsmount *sysfs_mount;
/*
* dir.c
diff --git a/fs/xfs/linux-2.6/kmem.c b/fs/xfs/linux-2.6/kmem.c
index 9b1bb17a0501..1cd3b55ee3d2 100644
--- a/fs/xfs/linux-2.6/kmem.c
+++ b/fs/xfs/linux-2.6/kmem.c
@@ -90,7 +90,7 @@ kmem_zalloc_greedy(size_t *size, size_t minsize, size_t maxsize,
}
void
-kmem_free(void *ptr, size_t size)
+kmem_free(const void *ptr)
{
if (!is_vmalloc_addr(ptr)) {
kfree(ptr);
@@ -100,7 +100,7 @@ kmem_free(void *ptr, size_t size)
}
void *
-kmem_realloc(void *ptr, size_t newsize, size_t oldsize,
+kmem_realloc(const void *ptr, size_t newsize, size_t oldsize,
unsigned int __nocast flags)
{
void *new;
@@ -110,7 +110,7 @@ kmem_realloc(void *ptr, size_t newsize, size_t oldsize,
if (new)
memcpy(new, ptr,
((oldsize < newsize) ? oldsize : newsize));
- kmem_free(ptr, oldsize);
+ kmem_free(ptr);
}
return new;
}
diff --git a/fs/xfs/linux-2.6/kmem.h b/fs/xfs/linux-2.6/kmem.h
index 5e9564902976..3c9910103c52 100644
--- a/fs/xfs/linux-2.6/kmem.h
+++ b/fs/xfs/linux-2.6/kmem.h
@@ -57,8 +57,8 @@ kmem_flags_convert(unsigned int __nocast flags)
extern void *kmem_alloc(size_t, unsigned int __nocast);
extern void *kmem_zalloc(size_t, unsigned int __nocast);
extern void *kmem_zalloc_greedy(size_t *, size_t, size_t, unsigned int __nocast);
-extern void *kmem_realloc(void *, size_t, size_t, unsigned int __nocast);
-extern void kmem_free(void *, size_t);
+extern void *kmem_realloc(const void *, size_t, size_t, unsigned int __nocast);
+extern void kmem_free(const void *);
/*
* Zone interfaces
diff --git a/fs/xfs/linux-2.6/xfs_aops.c b/fs/xfs/linux-2.6/xfs_aops.c
index a55c3b26d840..0b211cba1909 100644
--- a/fs/xfs/linux-2.6/xfs_aops.c
+++ b/fs/xfs/linux-2.6/xfs_aops.c
@@ -409,7 +409,6 @@ xfs_start_buffer_writeback(
STATIC void
xfs_start_page_writeback(
struct page *page,
- struct writeback_control *wbc,
int clear_dirty,
int buffers)
{
@@ -858,7 +857,7 @@ xfs_convert_page(
done = 1;
}
}
- xfs_start_page_writeback(page, wbc, !page_dirty, count);
+ xfs_start_page_writeback(page, !page_dirty, count);
}
return done;
@@ -1130,7 +1129,7 @@ xfs_page_state_convert(
SetPageUptodate(page);
if (startio)
- xfs_start_page_writeback(page, wbc, 1, count);
+ xfs_start_page_writeback(page, 1, count);
if (ioend && iomap_valid) {
offset = (iomap.iomap_offset + iomap.iomap_bsize - 1) >>
diff --git a/fs/xfs/linux-2.6/xfs_buf.c b/fs/xfs/linux-2.6/xfs_buf.c
index 98e0e86093b4..9cc8f0213095 100644
--- a/fs/xfs/linux-2.6/xfs_buf.c
+++ b/fs/xfs/linux-2.6/xfs_buf.c
@@ -310,8 +310,7 @@ _xfs_buf_free_pages(
xfs_buf_t *bp)
{
if (bp->b_pages != bp->b_page_array) {
- kmem_free(bp->b_pages,
- bp->b_page_count * sizeof(struct page *));
+ kmem_free(bp->b_pages);
}
}
@@ -1398,7 +1397,7 @@ STATIC void
xfs_free_bufhash(
xfs_buftarg_t *btp)
{
- kmem_free(btp->bt_hash, (1<<btp->bt_hashshift) * sizeof(xfs_bufhash_t));
+ kmem_free(btp->bt_hash);
btp->bt_hash = NULL;
}
@@ -1428,13 +1427,10 @@ xfs_unregister_buftarg(
void
xfs_free_buftarg(
- xfs_buftarg_t *btp,
- int external)
+ xfs_buftarg_t *btp)
{
xfs_flush_buftarg(btp, 1);
xfs_blkdev_issue_flush(btp);
- if (external)
- xfs_blkdev_put(btp->bt_bdev);
xfs_free_bufhash(btp);
iput(btp->bt_mapping->host);
@@ -1444,7 +1440,7 @@ xfs_free_buftarg(
xfs_unregister_buftarg(btp);
kthread_stop(btp->bt_task);
- kmem_free(btp, sizeof(*btp));
+ kmem_free(btp);
}
STATIC int
@@ -1575,7 +1571,7 @@ xfs_alloc_buftarg(
return btp;
error:
- kmem_free(btp, sizeof(*btp));
+ kmem_free(btp);
return NULL;
}
diff --git a/fs/xfs/linux-2.6/xfs_buf.h b/fs/xfs/linux-2.6/xfs_buf.h
index f948ec7ba9a4..29d1d4adc078 100644
--- a/fs/xfs/linux-2.6/xfs_buf.h
+++ b/fs/xfs/linux-2.6/xfs_buf.h
@@ -429,7 +429,7 @@ static inline void xfs_bdwrite(void *mp, xfs_buf_t *bp)
* Handling of buftargs.
*/
extern xfs_buftarg_t *xfs_alloc_buftarg(struct block_device *, int);
-extern void xfs_free_buftarg(xfs_buftarg_t *, int);
+extern void xfs_free_buftarg(xfs_buftarg_t *);
extern void xfs_wait_buftarg(xfs_buftarg_t *);
extern int xfs_setsize_buftarg(xfs_buftarg_t *, unsigned int, unsigned int);
extern int xfs_flush_buftarg(xfs_buftarg_t *, int);
diff --git a/fs/xfs/linux-2.6/xfs_export.c b/fs/xfs/linux-2.6/xfs_export.c
index c672b3238b14..987fe84f7b13 100644
--- a/fs/xfs/linux-2.6/xfs_export.c
+++ b/fs/xfs/linux-2.6/xfs_export.c
@@ -215,7 +215,7 @@ xfs_fs_get_parent(
struct xfs_inode *cip;
struct dentry *parent;
- error = xfs_lookup(XFS_I(child->d_inode), &xfs_name_dotdot, &cip);
+ error = xfs_lookup(XFS_I(child->d_inode), &xfs_name_dotdot, &cip, NULL);
if (unlikely(error))
return ERR_PTR(-error);
diff --git a/fs/xfs/linux-2.6/xfs_iops.c b/fs/xfs/linux-2.6/xfs_iops.c
index 2bf287ef5489..b022251114d9 100644
--- a/fs/xfs/linux-2.6/xfs_iops.c
+++ b/fs/xfs/linux-2.6/xfs_iops.c
@@ -382,7 +382,7 @@ xfs_vn_lookup(
return ERR_PTR(-ENAMETOOLONG);
xfs_dentry_to_name(&name, dentry);
- error = xfs_lookup(XFS_I(dir), &name, &cip);
+ error = xfs_lookup(XFS_I(dir), &name, &cip, NULL);
if (unlikely(error)) {
if (unlikely(error != ENOENT))
return ERR_PTR(-error);
@@ -393,6 +393,46 @@ xfs_vn_lookup(
return d_splice_alias(cip->i_vnode, dentry);
}
+STATIC struct dentry *
+xfs_vn_ci_lookup(
+ struct inode *dir,
+ struct dentry *dentry,
+ struct nameidata *nd)
+{
+ struct xfs_inode *ip;
+ struct xfs_name xname;
+ struct xfs_name ci_name;
+ struct qstr dname;
+ int error;
+
+ if (dentry->d_name.len >= MAXNAMELEN)
+ return ERR_PTR(-ENAMETOOLONG);
+
+ xfs_dentry_to_name(&xname, dentry);
+ error = xfs_lookup(XFS_I(dir), &xname, &ip, &ci_name);
+ if (unlikely(error)) {
+ if (unlikely(error != ENOENT))
+ return ERR_PTR(-error);
+ /*
+ * call d_add(dentry, NULL) here when d_drop_negative_children
+ * is called in xfs_vn_mknod (ie. allow negative dentries
+ * with CI filesystems).
+ */
+ return NULL;
+ }
+
+ /* if exact match, just splice and exit */
+ if (!ci_name.name)
+ return d_splice_alias(ip->i_vnode, dentry);
+
+ /* else case-insensitive match... */
+ dname.name = ci_name.name;
+ dname.len = ci_name.len;
+ dentry = d_add_ci(ip->i_vnode, dentry, &dname);
+ kmem_free(ci_name.name);
+ return dentry;
+}
+
STATIC int
xfs_vn_link(
struct dentry *old_dentry,
@@ -740,15 +780,11 @@ xfs_vn_setxattr(
char *attr = (char *)name;
attrnames_t *namesp;
int xflags = 0;
- int error;
namesp = attr_lookup_namespace(attr, attr_namespaces, ATTR_NAMECOUNT);
if (!namesp)
return -EOPNOTSUPP;
attr += namesp->attr_namelen;
- error = namesp->attr_capable(vp, NULL);
- if (error)
- return error;
/* Convert Linux syscall to XFS internal ATTR flags */
if (flags & XATTR_CREATE)
@@ -770,15 +806,11 @@ xfs_vn_getxattr(
char *attr = (char *)name;
attrnames_t *namesp;
int xflags = 0;
- ssize_t error;
namesp = attr_lookup_namespace(attr, attr_namespaces, ATTR_NAMECOUNT);
if (!namesp)
return -EOPNOTSUPP;
attr += namesp->attr_namelen;
- error = namesp->attr_capable(vp, NULL);
- if (error)
- return error;
/* Convert Linux syscall to XFS internal ATTR flags */
if (!size) {
@@ -818,15 +850,12 @@ xfs_vn_removexattr(
char *attr = (char *)name;
attrnames_t *namesp;
int xflags = 0;
- int error;
namesp = attr_lookup_namespace(attr, attr_namespaces, ATTR_NAMECOUNT);
if (!namesp)
return -EOPNOTSUPP;
attr += namesp->attr_namelen;
- error = namesp->attr_capable(vp, NULL);
- if (error)
- return error;
+
xflags |= namesp->attr_flag;
return namesp->attr_remove(vp, attr, xflags);
}
@@ -904,6 +933,25 @@ const struct inode_operations xfs_dir_inode_operations = {
.removexattr = xfs_vn_removexattr,
};
+const struct inode_operations xfs_dir_ci_inode_operations = {
+ .create = xfs_vn_create,
+ .lookup = xfs_vn_ci_lookup,
+ .link = xfs_vn_link,
+ .unlink = xfs_vn_unlink,
+ .symlink = xfs_vn_symlink,
+ .mkdir = xfs_vn_mkdir,
+ .rmdir = xfs_vn_rmdir,
+ .mknod = xfs_vn_mknod,
+ .rename = xfs_vn_rename,
+ .permission = xfs_vn_permission,
+ .getattr = xfs_vn_getattr,
+ .setattr = xfs_vn_setattr,
+ .setxattr = xfs_vn_setxattr,
+ .getxattr = xfs_vn_getxattr,
+ .listxattr = xfs_vn_listxattr,
+ .removexattr = xfs_vn_removexattr,
+};
+
const struct inode_operations xfs_symlink_inode_operations = {
.readlink = generic_readlink,
.follow_link = xfs_vn_follow_link,
diff --git a/fs/xfs/linux-2.6/xfs_iops.h b/fs/xfs/linux-2.6/xfs_iops.h
index 14d0deb7afff..3b4df5863e4a 100644
--- a/fs/xfs/linux-2.6/xfs_iops.h
+++ b/fs/xfs/linux-2.6/xfs_iops.h
@@ -20,6 +20,7 @@
extern const struct inode_operations xfs_inode_operations;
extern const struct inode_operations xfs_dir_inode_operations;
+extern const struct inode_operations xfs_dir_ci_inode_operations;
extern const struct inode_operations xfs_symlink_inode_operations;
extern const struct file_operations xfs_file_operations;
diff --git a/fs/xfs/linux-2.6/xfs_linux.h b/fs/xfs/linux-2.6/xfs_linux.h
index 4edc46915b57..aded57321b12 100644
--- a/fs/xfs/linux-2.6/xfs_linux.h
+++ b/fs/xfs/linux-2.6/xfs_linux.h
@@ -76,6 +76,7 @@
#include <linux/log2.h>
#include <linux/spinlock.h>
#include <linux/random.h>
+#include <linux/ctype.h>
#include <asm/page.h>
#include <asm/div64.h>
diff --git a/fs/xfs/linux-2.6/xfs_super.c b/fs/xfs/linux-2.6/xfs_super.c
index 742b2c7852c1..79c11aaf5423 100644
--- a/fs/xfs/linux-2.6/xfs_super.c
+++ b/fs/xfs/linux-2.6/xfs_super.c
@@ -52,6 +52,7 @@
#include "xfs_version.h"
#include "xfs_log_priv.h"
#include "xfs_trans_priv.h"
+#include "xfs_filestream.h"
#include <linux/namei.h>
#include <linux/init.h>
@@ -74,7 +75,10 @@ xfs_args_allocate(
{
struct xfs_mount_args *args;
- args = kmem_zalloc(sizeof(struct xfs_mount_args), KM_SLEEP);
+ args = kzalloc(sizeof(struct xfs_mount_args), GFP_KERNEL);
+ if (!args)
+ return NULL;
+
args->logbufs = args->logbufsize = -1;
strncpy(args->fsname, sb->s_id, MAXNAMELEN);
@@ -314,6 +318,7 @@ xfs_parseargs(
args->flags |= XFSMNT_ATTR2;
} else if (!strcmp(this_char, MNTOPT_NOATTR2)) {
args->flags &= ~XFSMNT_ATTR2;
+ args->flags |= XFSMNT_NOATTR2;
} else if (!strcmp(this_char, MNTOPT_FILESTREAM)) {
args->flags2 |= XFSMNT2_FILESTREAMS;
} else if (!strcmp(this_char, MNTOPT_NOQUOTA)) {
@@ -564,7 +569,10 @@ xfs_set_inodeops(
inode->i_mapping->a_ops = &xfs_address_space_operations;
break;
case S_IFDIR:
- inode->i_op = &xfs_dir_inode_operations;
+ if (xfs_sb_version_hasasciici(&XFS_M(inode->i_sb)->m_sb))
+ inode->i_op = &xfs_dir_ci_inode_operations;
+ else
+ inode->i_op = &xfs_dir_inode_operations;
inode->i_fop = &xfs_dir_file_operations;
break;
case S_IFLNK:
@@ -764,6 +772,137 @@ xfs_blkdev_issue_flush(
blkdev_issue_flush(buftarg->bt_bdev, NULL);
}
+STATIC void
+xfs_close_devices(
+ struct xfs_mount *mp)
+{
+ if (mp->m_logdev_targp && mp->m_logdev_targp != mp->m_ddev_targp) {
+ xfs_free_buftarg(mp->m_logdev_targp);
+ xfs_blkdev_put(mp->m_logdev_targp->bt_bdev);
+ }
+ if (mp->m_rtdev_targp) {
+ xfs_free_buftarg(mp->m_rtdev_targp);
+ xfs_blkdev_put(mp->m_rtdev_targp->bt_bdev);
+ }
+ xfs_free_buftarg(mp->m_ddev_targp);
+}
+
+/*
+ * The file system configurations are:
+ * (1) device (partition) with data and internal log
+ * (2) logical volume with data and log subvolumes.
+ * (3) logical volume with data, log, and realtime subvolumes.
+ *
+ * We only have to handle opening the log and realtime volumes here if
+ * they are present. The data subvolume has already been opened by
+ * get_sb_bdev() and is stored in sb->s_bdev.
+ */
+STATIC int
+xfs_open_devices(
+ struct xfs_mount *mp,
+ struct xfs_mount_args *args)
+{
+ struct block_device *ddev = mp->m_super->s_bdev;
+ struct block_device *logdev = NULL, *rtdev = NULL;
+ int error;
+
+ /*
+ * Open real time and log devices - order is important.
+ */
+ if (args->logname[0]) {
+ error = xfs_blkdev_get(mp, args->logname, &logdev);
+ if (error)
+ goto out;
+ }
+
+ if (args->rtname[0]) {
+ error = xfs_blkdev_get(mp, args->rtname, &rtdev);
+ if (error)
+ goto out_close_logdev;
+
+ if (rtdev == ddev || rtdev == logdev) {
+ cmn_err(CE_WARN,
+ "XFS: Cannot mount filesystem with identical rtdev and ddev/logdev.");
+ error = EINVAL;
+ goto out_close_rtdev;
+ }
+ }
+
+ /*
+ * Setup xfs_mount buffer target pointers
+ */
+ error = ENOMEM;
+ mp->m_ddev_targp = xfs_alloc_buftarg(ddev, 0);
+ if (!mp->m_ddev_targp)
+ goto out_close_rtdev;
+
+ if (rtdev) {
+ mp->m_rtdev_targp = xfs_alloc_buftarg(rtdev, 1);
+ if (!mp->m_rtdev_targp)
+ goto out_free_ddev_targ;
+ }
+
+ if (logdev && logdev != ddev) {
+ mp->m_logdev_targp = xfs_alloc_buftarg(logdev, 1);
+ if (!mp->m_logdev_targp)
+ goto out_free_rtdev_targ;
+ } else {
+ mp->m_logdev_targp = mp->m_ddev_targp;
+ }
+
+ return 0;
+
+ out_free_rtdev_targ:
+ if (mp->m_rtdev_targp)
+ xfs_free_buftarg(mp->m_rtdev_targp);
+ out_free_ddev_targ:
+ xfs_free_buftarg(mp->m_ddev_targp);
+ out_close_rtdev:
+ if (rtdev)
+ xfs_blkdev_put(rtdev);
+ out_close_logdev:
+ if (logdev && logdev != ddev)
+ xfs_blkdev_put(logdev);
+ out:
+ return error;
+}
+
+/*
+ * Setup xfs_mount buffer target pointers based on superblock
+ */
+STATIC int
+xfs_setup_devices(
+ struct xfs_mount *mp)
+{
+ int error;
+
+ error = xfs_setsize_buftarg(mp->m_ddev_targp, mp->m_sb.sb_blocksize,
+ mp->m_sb.sb_sectsize);
+ if (error)
+ return error;
+
+ if (mp->m_logdev_targp && mp->m_logdev_targp != mp->m_ddev_targp) {
+ unsigned int log_sector_size = BBSIZE;
+
+ if (xfs_sb_version_hassector(&mp->m_sb))
+ log_sector_size = mp->m_sb.sb_logsectsize;
+ error = xfs_setsize_buftarg(mp->m_logdev_targp,
+ mp->m_sb.sb_blocksize,
+ log_sector_size);
+ if (error)
+ return error;
+ }
+ if (mp->m_rtdev_targp) {
+ error = xfs_setsize_buftarg(mp->m_rtdev_targp,
+ mp->m_sb.sb_blocksize,
+ mp->m_sb.sb_sectsize);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
/*
* XFS AIL push thread support
*/
@@ -1074,7 +1213,7 @@ xfssyncd(
list_del(&work->w_list);
if (work == &mp->m_sync_work)
continue;
- kmem_free(work, sizeof(struct bhv_vfs_sync_work));
+ kmem_free(work);
}
}
@@ -1086,14 +1225,63 @@ xfs_fs_put_super(
struct super_block *sb)
{
struct xfs_mount *mp = XFS_M(sb);
+ struct xfs_inode *rip = mp->m_rootip;
+ int unmount_event_flags = 0;
int error;
kthread_stop(mp->m_sync_task);
xfs_sync(mp, SYNC_ATTR | SYNC_DELWRI);
- error = xfs_unmount(mp, 0, NULL);
- if (error)
- printk("XFS: unmount got error=%d\n", error);
+
+#ifdef HAVE_DMAPI
+ if (mp->m_flags & XFS_MOUNT_DMAPI) {
+ unmount_event_flags =
+ (mp->m_dmevmask & (1 << DM_EVENT_UNMOUNT)) ?
+ 0 : DM_FLAGS_UNWANTED;
+ /*
+ * Ignore error from dmapi here, first unmount is not allowed
+ * to fail anyway, and second we wouldn't want to fail a
+ * unmount because of dmapi.
+ */
+ XFS_SEND_PREUNMOUNT(mp, rip, DM_RIGHT_NULL, rip, DM_RIGHT_NULL,
+ NULL, NULL, 0, 0, unmount_event_flags);
+ }
+#endif
+
+ /*
+ * Blow away any referenced inode in the filestreams cache.
+ * This can and will cause log traffic as inodes go inactive
+ * here.
+ */
+ xfs_filestream_unmount(mp);
+
+ XFS_bflush(mp->m_ddev_targp);
+ error = xfs_unmount_flush(mp, 0);
+ WARN_ON(error);
+
+ IRELE(rip);
+
+ /*
+ * If we're forcing a shutdown, typically because of a media error,
+ * we want to make sure we invalidate dirty pages that belong to
+ * referenced vnodes as well.
+ */
+ if (XFS_FORCED_SHUTDOWN(mp)) {
+ error = xfs_sync(mp, SYNC_WAIT | SYNC_CLOSE);
+ ASSERT(error != EFSCORRUPTED);
+ }
+
+ if (mp->m_flags & XFS_MOUNT_DMAPI) {
+ XFS_SEND_UNMOUNT(mp, rip, DM_RIGHT_NULL, 0, 0,
+ unmount_event_flags);
+ }
+
+ xfs_unmountfs(mp);
+ xfs_icsb_destroy_counters(mp);
+ xfs_close_devices(mp);
+ xfs_qmops_put(mp);
+ xfs_dmops_put(mp);
+ kfree(mp);
}
STATIC void
@@ -1216,13 +1404,35 @@ xfs_fs_remount(
char *options)
{
struct xfs_mount *mp = XFS_M(sb);
- struct xfs_mount_args *args = xfs_args_allocate(sb, 0);
+ struct xfs_mount_args *args;
int error;
+ args = xfs_args_allocate(sb, 0);
+ if (!args)
+ return -ENOMEM;
+
error = xfs_parseargs(mp, options, args, 1);
- if (!error)
- error = xfs_mntupdate(mp, flags, args);
- kmem_free(args, sizeof(*args));
+ if (error)
+ goto out_free_args;
+
+ if (!(*flags & MS_RDONLY)) { /* rw/ro -> rw */
+ if (mp->m_flags & XFS_MOUNT_RDONLY)
+ mp->m_flags &= ~XFS_MOUNT_RDONLY;
+ if (args->flags & XFSMNT_BARRIER) {
+ mp->m_flags |= XFS_MOUNT_BARRIER;
+ xfs_mountfs_check_barriers(mp);
+ } else {
+ mp->m_flags &= ~XFS_MOUNT_BARRIER;
+ }
+ } else if (!(mp->m_flags & XFS_MOUNT_RDONLY)) { /* rw -> ro */
+ xfs_filestream_flush(mp);
+ xfs_sync(mp, SYNC_DATA_QUIESCE);
+ xfs_attr_quiesce(mp);
+ mp->m_flags |= XFS_MOUNT_RDONLY;
+ }
+
+ out_free_args:
+ kfree(args);
return -error;
}
@@ -1300,6 +1510,225 @@ xfs_fs_setxquota(
Q_XSETPQLIM), id, (caddr_t)fdq);
}
+/*
+ * This function fills in xfs_mount_t fields based on mount args.
+ * Note: the superblock has _not_ yet been read in.
+ */
+STATIC int
+xfs_start_flags(
+ struct xfs_mount_args *ap,
+ struct xfs_mount *mp)
+{
+ /* Values are in BBs */
+ if ((ap->flags & XFSMNT_NOALIGN) != XFSMNT_NOALIGN) {
+ /*
+ * At this point the superblock has not been read
+ * in, therefore we do not know the block size.
+ * Before the mount call ends we will convert
+ * these to FSBs.
+ */
+ mp->m_dalign = ap->sunit;
+ mp->m_swidth = ap->swidth;
+ }
+
+ if (ap->logbufs != -1 &&
+ ap->logbufs != 0 &&
+ (ap->logbufs < XLOG_MIN_ICLOGS ||
+ ap->logbufs > XLOG_MAX_ICLOGS)) {
+ cmn_err(CE_WARN,
+ "XFS: invalid logbufs value: %d [not %d-%d]",
+ ap->logbufs, XLOG_MIN_ICLOGS, XLOG_MAX_ICLOGS);
+ return XFS_ERROR(EINVAL);
+ }
+ mp->m_logbufs = ap->logbufs;
+ if (ap->logbufsize != -1 &&
+ ap->logbufsize != 0 &&
+ (ap->logbufsize < XLOG_MIN_RECORD_BSIZE ||
+ ap->logbufsize > XLOG_MAX_RECORD_BSIZE ||
+ !is_power_of_2(ap->logbufsize))) {
+ cmn_err(CE_WARN,
+ "XFS: invalid logbufsize: %d [not 16k,32k,64k,128k or 256k]",
+ ap->logbufsize);
+ return XFS_ERROR(EINVAL);
+ }
+ mp->m_logbsize = ap->logbufsize;
+ mp->m_fsname_len = strlen(ap->fsname) + 1;
+ mp->m_fsname = kmem_alloc(mp->m_fsname_len, KM_SLEEP);
+ strcpy(mp->m_fsname, ap->fsname);
+ if (ap->rtname[0]) {
+ mp->m_rtname = kmem_alloc(strlen(ap->rtname) + 1, KM_SLEEP);
+ strcpy(mp->m_rtname, ap->rtname);
+ }
+ if (ap->logname[0]) {
+ mp->m_logname = kmem_alloc(strlen(ap->logname) + 1, KM_SLEEP);
+ strcpy(mp->m_logname, ap->logname);
+ }
+
+ if (ap->flags & XFSMNT_WSYNC)
+ mp->m_flags |= XFS_MOUNT_WSYNC;
+#if XFS_BIG_INUMS
+ if (ap->flags & XFSMNT_INO64) {
+ mp->m_flags |= XFS_MOUNT_INO64;
+ mp->m_inoadd = XFS_INO64_OFFSET;
+ }
+#endif
+ if (ap->flags & XFSMNT_RETERR)
+ mp->m_flags |= XFS_MOUNT_RETERR;
+ if (ap->flags & XFSMNT_NOALIGN)
+ mp->m_flags |= XFS_MOUNT_NOALIGN;
+ if (ap->flags & XFSMNT_SWALLOC)
+ mp->m_flags |= XFS_MOUNT_SWALLOC;
+ if (ap->flags & XFSMNT_OSYNCISOSYNC)
+ mp->m_flags |= XFS_MOUNT_OSYNCISOSYNC;
+ if (ap->flags & XFSMNT_32BITINODES)
+ mp->m_flags |= XFS_MOUNT_32BITINODES;
+
+ if (ap->flags & XFSMNT_IOSIZE) {
+ if (ap->iosizelog > XFS_MAX_IO_LOG ||
+ ap->iosizelog < XFS_MIN_IO_LOG) {
+ cmn_err(CE_WARN,
+ "XFS: invalid log iosize: %d [not %d-%d]",
+ ap->iosizelog, XFS_MIN_IO_LOG,
+ XFS_MAX_IO_LOG);
+ return XFS_ERROR(EINVAL);
+ }
+
+ mp->m_flags |= XFS_MOUNT_DFLT_IOSIZE;
+ mp->m_readio_log = mp->m_writeio_log = ap->iosizelog;
+ }
+
+ if (ap->flags & XFSMNT_IKEEP)
+ mp->m_flags |= XFS_MOUNT_IKEEP;
+ if (ap->flags & XFSMNT_DIRSYNC)
+ mp->m_flags |= XFS_MOUNT_DIRSYNC;
+ if (ap->flags & XFSMNT_ATTR2)
+ mp->m_flags |= XFS_MOUNT_ATTR2;
+ if (ap->flags & XFSMNT_NOATTR2)
+ mp->m_flags |= XFS_MOUNT_NOATTR2;
+
+ if (ap->flags2 & XFSMNT2_COMPAT_IOSIZE)
+ mp->m_flags |= XFS_MOUNT_COMPAT_IOSIZE;
+
+ /*
+ * no recovery flag requires a read-only mount
+ */
+ if (ap->flags & XFSMNT_NORECOVERY) {
+ if (!(mp->m_flags & XFS_MOUNT_RDONLY)) {
+ cmn_err(CE_WARN,
+ "XFS: tried to mount a FS read-write without recovery!");
+ return XFS_ERROR(EINVAL);
+ }
+ mp->m_flags |= XFS_MOUNT_NORECOVERY;
+ }
+
+ if (ap->flags & XFSMNT_NOUUID)
+ mp->m_flags |= XFS_MOUNT_NOUUID;
+ if (ap->flags & XFSMNT_BARRIER)
+ mp->m_flags |= XFS_MOUNT_BARRIER;
+ else
+ mp->m_flags &= ~XFS_MOUNT_BARRIER;
+
+ if (ap->flags2 & XFSMNT2_FILESTREAMS)
+ mp->m_flags |= XFS_MOUNT_FILESTREAMS;
+
+ if (ap->flags & XFSMNT_DMAPI)
+ mp->m_flags |= XFS_MOUNT_DMAPI;
+ return 0;
+}
+
+/*
+ * This function fills in xfs_mount_t fields based on mount args.
+ * Note: the superblock _has_ now been read in.
+ */
+STATIC int
+xfs_finish_flags(
+ struct xfs_mount_args *ap,
+ struct xfs_mount *mp)
+{
+ int ronly = (mp->m_flags & XFS_MOUNT_RDONLY);
+
+ /* Fail a mount where the logbuf is smaller then the log stripe */
+ if (xfs_sb_version_haslogv2(&mp->m_sb)) {
+ if ((ap->logbufsize <= 0) &&
+ (mp->m_sb.sb_logsunit > XLOG_BIG_RECORD_BSIZE)) {
+ mp->m_logbsize = mp->m_sb.sb_logsunit;
+ } else if (ap->logbufsize > 0 &&
+ ap->logbufsize < mp->m_sb.sb_logsunit) {
+ cmn_err(CE_WARN,
+ "XFS: logbuf size must be greater than or equal to log stripe size");
+ return XFS_ERROR(EINVAL);
+ }
+ } else {
+ /* Fail a mount if the logbuf is larger than 32K */
+ if (ap->logbufsize > XLOG_BIG_RECORD_BSIZE) {
+ cmn_err(CE_WARN,
+ "XFS: logbuf size for version 1 logs must be 16K or 32K");
+ return XFS_ERROR(EINVAL);
+ }
+ }
+
+ /*
+ * mkfs'ed attr2 will turn on attr2 mount unless explicitly
+ * told by noattr2 to turn it off
+ */
+ if (xfs_sb_version_hasattr2(&mp->m_sb) &&
+ !(ap->flags & XFSMNT_NOATTR2))
+ mp->m_flags |= XFS_MOUNT_ATTR2;
+
+ /*
+ * prohibit r/w mounts of read-only filesystems
+ */
+ if ((mp->m_sb.sb_flags & XFS_SBF_READONLY) && !ronly) {
+ cmn_err(CE_WARN,
+ "XFS: cannot mount a read-only filesystem as read-write");
+ return XFS_ERROR(EROFS);
+ }
+
+ /*
+ * check for shared mount.
+ */
+ if (ap->flags & XFSMNT_SHARED) {
+ if (!xfs_sb_version_hasshared(&mp->m_sb))
+ return XFS_ERROR(EINVAL);
+
+ /*
+ * For IRIX 6.5, shared mounts must have the shared
+ * version bit set, have the persistent readonly
+ * field set, must be version 0 and can only be mounted
+ * read-only.
+ */
+ if (!ronly || !(mp->m_sb.sb_flags & XFS_SBF_READONLY) ||
+ (mp->m_sb.sb_shared_vn != 0))
+ return XFS_ERROR(EINVAL);
+
+ mp->m_flags |= XFS_MOUNT_SHARED;
+
+ /*
+ * Shared XFS V0 can't deal with DMI. Return EINVAL.
+ */
+ if (mp->m_sb.sb_shared_vn == 0 && (ap->flags & XFSMNT_DMAPI))
+ return XFS_ERROR(EINVAL);
+ }
+
+ if (ap->flags & XFSMNT_UQUOTA) {
+ mp->m_qflags |= (XFS_UQUOTA_ACCT | XFS_UQUOTA_ACTIVE);
+ if (ap->flags & XFSMNT_UQUOTAENF)
+ mp->m_qflags |= XFS_UQUOTA_ENFD;
+ }
+
+ if (ap->flags & XFSMNT_GQUOTA) {
+ mp->m_qflags |= (XFS_GQUOTA_ACCT | XFS_GQUOTA_ACTIVE);
+ if (ap->flags & XFSMNT_GQUOTAENF)
+ mp->m_qflags |= XFS_OQUOTA_ENFD;
+ } else if (ap->flags & XFSMNT_PQUOTA) {
+ mp->m_qflags |= (XFS_PQUOTA_ACCT | XFS_PQUOTA_ACTIVE);
+ if (ap->flags & XFSMNT_PQUOTAENF)
+ mp->m_qflags |= XFS_OQUOTA_ENFD;
+ }
+
+ return 0;
+}
+
STATIC int
xfs_fs_fill_super(
struct super_block *sb,
@@ -1308,11 +1737,21 @@ xfs_fs_fill_super(
{
struct inode *root;
struct xfs_mount *mp = NULL;
- struct xfs_mount_args *args = xfs_args_allocate(sb, silent);
- int error;
+ struct xfs_mount_args *args;
+ int flags = 0, error = ENOMEM;
+
+ args = xfs_args_allocate(sb, silent);
+ if (!args)
+ return -ENOMEM;
- mp = xfs_mount_init();
+ mp = kzalloc(sizeof(struct xfs_mount), GFP_KERNEL);
+ if (!mp)
+ goto out_free_args;
+ spin_lock_init(&mp->m_sb_lock);
+ mutex_init(&mp->m_ilock);
+ mutex_init(&mp->m_growlock);
+ atomic_set(&mp->m_active_trans, 0);
INIT_LIST_HEAD(&mp->m_sync_list);
spin_lock_init(&mp->m_sync_lock);
init_waitqueue_head(&mp->m_wait_single_sync_task);
@@ -1325,16 +1764,59 @@ xfs_fs_fill_super(
error = xfs_parseargs(mp, (char *)data, args, 0);
if (error)
- goto fail_vfsop;
+ goto out_free_mp;
sb_min_blocksize(sb, BBSIZE);
sb->s_export_op = &xfs_export_operations;
sb->s_qcop = &xfs_quotactl_operations;
sb->s_op = &xfs_super_operations;
- error = xfs_mount(mp, args, NULL);
+ error = xfs_dmops_get(mp, args);
+ if (error)
+ goto out_free_mp;
+ error = xfs_qmops_get(mp, args);
+ if (error)
+ goto out_put_dmops;
+
+ if (args->flags & XFSMNT_QUIET)
+ flags |= XFS_MFSI_QUIET;
+
+ error = xfs_open_devices(mp, args);
+ if (error)
+ goto out_put_qmops;
+
+ if (xfs_icsb_init_counters(mp))
+ mp->m_flags |= XFS_MOUNT_NO_PERCPU_SB;
+
+ /*
+ * Setup flags based on mount(2) options and then the superblock
+ */
+ error = xfs_start_flags(args, mp);
+ if (error)
+ goto out_destroy_counters;
+ error = xfs_readsb(mp, flags);
+ if (error)
+ goto out_destroy_counters;
+ error = xfs_finish_flags(args, mp);
+ if (error)
+ goto out_free_sb;
+
+ error = xfs_setup_devices(mp);
+ if (error)
+ goto out_free_sb;
+
+ if (mp->m_flags & XFS_MOUNT_BARRIER)
+ xfs_mountfs_check_barriers(mp);
+
+ error = xfs_filestream_mount(mp);
+ if (error)
+ goto out_free_sb;
+
+ error = xfs_mountfs(mp, flags);
if (error)
- goto fail_vfsop;
+ goto out_filestream_unmount;
+
+ XFS_SEND_MOUNT(mp, DM_RIGHT_NULL, args->mtpt, args->fsname);
sb->s_dirt = 1;
sb->s_magic = XFS_SB_MAGIC;
@@ -1369,10 +1851,27 @@ xfs_fs_fill_super(
xfs_itrace_exit(XFS_I(sb->s_root->d_inode));
- kmem_free(args, sizeof(*args));
+ kfree(args);
return 0;
-fail_vnrele:
+ out_filestream_unmount:
+ xfs_filestream_unmount(mp);
+ out_free_sb:
+ xfs_freesb(mp);
+ out_destroy_counters:
+ xfs_icsb_destroy_counters(mp);
+ xfs_close_devices(mp);
+ out_put_qmops:
+ xfs_qmops_put(mp);
+ out_put_dmops:
+ xfs_dmops_put(mp);
+ out_free_mp:
+ kfree(mp);
+ out_free_args:
+ kfree(args);
+ return -error;
+
+ fail_vnrele:
if (sb->s_root) {
dput(sb->s_root);
sb->s_root = NULL;
@@ -1380,12 +1879,22 @@ fail_vnrele:
iput(root);
}
-fail_unmount:
- xfs_unmount(mp, 0, NULL);
+ fail_unmount:
+ /*
+ * Blow away any referenced inode in the filestreams cache.
+ * This can and will cause log traffic as inodes go inactive
+ * here.
+ */
+ xfs_filestream_unmount(mp);
-fail_vfsop:
- kmem_free(args, sizeof(*args));
- return -error;
+ XFS_bflush(mp->m_ddev_targp);
+ error = xfs_unmount_flush(mp, 0);
+ WARN_ON(error);
+
+ IRELE(mp->m_rootip);
+
+ xfs_unmountfs(mp);
+ goto out_destroy_counters;
}
STATIC int
@@ -1396,8 +1905,17 @@ xfs_fs_get_sb(
void *data,
struct vfsmount *mnt)
{
- return get_sb_bdev(fs_type, flags, dev_name, data, xfs_fs_fill_super,
+ int error;
+
+ error = get_sb_bdev(fs_type, flags, dev_name, data, xfs_fs_fill_super,
mnt);
+ if (!error) {
+ xfs_mount_t *mp = XFS_M(mnt->mnt_sb);
+
+ mp->m_vfsmount = mnt;
+ }
+
+ return error;
}
static struct super_operations xfs_super_operations = {
diff --git a/fs/xfs/linux-2.6/xfs_super.h b/fs/xfs/linux-2.6/xfs_super.h
index 3efb7c6d3303..212bdc7a7897 100644
--- a/fs/xfs/linux-2.6/xfs_super.h
+++ b/fs/xfs/linux-2.6/xfs_super.h
@@ -107,9 +107,6 @@ extern void xfs_initialize_vnode(struct xfs_mount *mp, bhv_vnode_t *vp,
extern void xfs_flush_inode(struct xfs_inode *);
extern void xfs_flush_device(struct xfs_inode *);
-extern int xfs_blkdev_get(struct xfs_mount *, const char *,
- struct block_device **);
-extern void xfs_blkdev_put(struct block_device *);
extern void xfs_blkdev_issue_flush(struct xfs_buftarg *);
extern const struct export_operations xfs_export_operations;
diff --git a/fs/xfs/quota/xfs_dquot.c b/fs/xfs/quota/xfs_dquot.c
index 85df3288efd5..fc9f3fb39b7b 100644
--- a/fs/xfs/quota/xfs_dquot.c
+++ b/fs/xfs/quota/xfs_dquot.c
@@ -1435,8 +1435,7 @@ xfs_dqlock2(
/* ARGSUSED */
int
xfs_qm_dqpurge(
- xfs_dquot_t *dqp,
- uint flags)
+ xfs_dquot_t *dqp)
{
xfs_dqhash_t *thishash;
xfs_mount_t *mp = dqp->q_mount;
diff --git a/fs/xfs/quota/xfs_dquot.h b/fs/xfs/quota/xfs_dquot.h
index 5c371a92e3e2..f7393bba4e95 100644
--- a/fs/xfs/quota/xfs_dquot.h
+++ b/fs/xfs/quota/xfs_dquot.h
@@ -164,7 +164,7 @@ extern void xfs_qm_dqprint(xfs_dquot_t *);
extern void xfs_qm_dqdestroy(xfs_dquot_t *);
extern int xfs_qm_dqflush(xfs_dquot_t *, uint);
-extern int xfs_qm_dqpurge(xfs_dquot_t *, uint);
+extern int xfs_qm_dqpurge(xfs_dquot_t *);
extern void xfs_qm_dqunpin_wait(xfs_dquot_t *);
extern int xfs_qm_dqlock_nowait(xfs_dquot_t *);
extern int xfs_qm_dqflock_nowait(xfs_dquot_t *);
diff --git a/fs/xfs/quota/xfs_dquot_item.c b/fs/xfs/quota/xfs_dquot_item.c
index 36e05ca78412..08d2fc89e6a1 100644
--- a/fs/xfs/quota/xfs_dquot_item.c
+++ b/fs/xfs/quota/xfs_dquot_item.c
@@ -576,8 +576,8 @@ xfs_qm_qoffend_logitem_committed(
* xfs_trans_delete_ail() drops the AIL lock.
*/
xfs_trans_delete_ail(qfs->qql_item.li_mountp, (xfs_log_item_t *)qfs);
- kmem_free(qfs, sizeof(xfs_qoff_logitem_t));
- kmem_free(qfe, sizeof(xfs_qoff_logitem_t));
+ kmem_free(qfs);
+ kmem_free(qfe);
return (xfs_lsn_t)-1;
}
diff --git a/fs/xfs/quota/xfs_qm.c b/fs/xfs/quota/xfs_qm.c
index d31cce1165c5..26370a3128f5 100644
--- a/fs/xfs/quota/xfs_qm.c
+++ b/fs/xfs/quota/xfs_qm.c
@@ -192,8 +192,8 @@ xfs_qm_destroy(
xfs_qm_list_destroy(&(xqm->qm_usr_dqhtable[i]));
xfs_qm_list_destroy(&(xqm->qm_grp_dqhtable[i]));
}
- kmem_free(xqm->qm_usr_dqhtable, hsize * sizeof(xfs_dqhash_t));
- kmem_free(xqm->qm_grp_dqhtable, hsize * sizeof(xfs_dqhash_t));
+ kmem_free(xqm->qm_usr_dqhtable);
+ kmem_free(xqm->qm_grp_dqhtable);
xqm->qm_usr_dqhtable = NULL;
xqm->qm_grp_dqhtable = NULL;
xqm->qm_dqhashmask = 0;
@@ -201,7 +201,7 @@ xfs_qm_destroy(
#ifdef DEBUG
mutex_destroy(&qcheck_lock);
#endif
- kmem_free(xqm, sizeof(xfs_qm_t));
+ kmem_free(xqm);
}
/*
@@ -631,7 +631,7 @@ xfs_qm_dqpurge_int(
* freelist in INACTIVE state.
*/
nextdqp = dqp->MPL_NEXT;
- nmisses += xfs_qm_dqpurge(dqp, flags);
+ nmisses += xfs_qm_dqpurge(dqp);
dqp = nextdqp;
}
xfs_qm_mplist_unlock(mp);
@@ -1134,7 +1134,7 @@ xfs_qm_init_quotainfo(
* and change the superblock accordingly.
*/
if ((error = xfs_qm_init_quotainos(mp))) {
- kmem_free(qinf, sizeof(xfs_quotainfo_t));
+ kmem_free(qinf);
mp->m_quotainfo = NULL;
return error;
}
@@ -1248,7 +1248,7 @@ xfs_qm_destroy_quotainfo(
qi->qi_gquotaip = NULL;
}
mutex_destroy(&qi->qi_quotaofflock);
- kmem_free(qi, sizeof(xfs_quotainfo_t));
+ kmem_free(qi);
mp->m_quotainfo = NULL;
}
@@ -1623,7 +1623,7 @@ xfs_qm_dqiterate(
break;
} while (nmaps > 0);
- kmem_free(map, XFS_DQITER_MAP_SIZE * sizeof(*map));
+ kmem_free(map);
return error;
}
diff --git a/fs/xfs/quota/xfs_qm_syscalls.c b/fs/xfs/quota/xfs_qm_syscalls.c
index 768a3b27d2b6..413671523cb5 100644
--- a/fs/xfs/quota/xfs_qm_syscalls.c
+++ b/fs/xfs/quota/xfs_qm_syscalls.c
@@ -1449,14 +1449,14 @@ xfs_qm_internalqcheck(
for (d = (xfs_dqtest_t *) h1->qh_next; d != NULL; ) {
xfs_dqtest_cmp(d);
e = (xfs_dqtest_t *) d->HL_NEXT;
- kmem_free(d, sizeof(xfs_dqtest_t));
+ kmem_free(d);
d = e;
}
h1 = &qmtest_gdqtab[i];
for (d = (xfs_dqtest_t *) h1->qh_next; d != NULL; ) {
xfs_dqtest_cmp(d);
e = (xfs_dqtest_t *) d->HL_NEXT;
- kmem_free(d, sizeof(xfs_dqtest_t));
+ kmem_free(d);
d = e;
}
}
@@ -1467,8 +1467,8 @@ xfs_qm_internalqcheck(
} else {
cmn_err(CE_DEBUG, "******** quotacheck successful! ********");
}
- kmem_free(qmtest_udqtab, qmtest_hashmask * sizeof(xfs_dqhash_t));
- kmem_free(qmtest_gdqtab, qmtest_hashmask * sizeof(xfs_dqhash_t));
+ kmem_free(qmtest_udqtab);
+ kmem_free(qmtest_gdqtab);
mutex_unlock(&qcheck_lock);
return (qmtest_nfails);
}
diff --git a/fs/xfs/support/ktrace.c b/fs/xfs/support/ktrace.c
index 0b75d302508f..a34ef05489b1 100644
--- a/fs/xfs/support/ktrace.c
+++ b/fs/xfs/support/ktrace.c
@@ -89,7 +89,7 @@ ktrace_alloc(int nentries, unsigned int __nocast sleep)
if (sleep & KM_SLEEP)
panic("ktrace_alloc: NULL memory on KM_SLEEP request!");
- kmem_free(ktp, sizeof(*ktp));
+ kmem_free(ktp);
return NULL;
}
@@ -126,7 +126,7 @@ ktrace_free(ktrace_t *ktp)
} else {
entries_size = (int)(ktp->kt_nentries * sizeof(ktrace_entry_t));
- kmem_free(ktp->kt_entries, entries_size);
+ kmem_free(ktp->kt_entries);
}
kmem_zone_free(ktrace_hdr_zone, ktp);
diff --git a/fs/xfs/xfs_attr.c b/fs/xfs/xfs_attr.c
index df151a859186..557dad611de0 100644
--- a/fs/xfs/xfs_attr.c
+++ b/fs/xfs/xfs_attr.c
@@ -241,8 +241,7 @@ xfs_attr_set_int(xfs_inode_t *dp, struct xfs_name *name,
args.firstblock = &firstblock;
args.flist = &flist;
args.whichfork = XFS_ATTR_FORK;
- args.addname = 1;
- args.oknoent = 1;
+ args.op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT;
/*
* Determine space new attribute will use, and if it would be
@@ -974,7 +973,7 @@ xfs_attr_leaf_addname(xfs_da_args_t *args)
xfs_da_brelse(args->trans, bp);
return(retval);
}
- args->rename = 1; /* an atomic rename */
+ args->op_flags |= XFS_DA_OP_RENAME; /* an atomic rename */
args->blkno2 = args->blkno; /* set 2nd entry info*/
args->index2 = args->index;
args->rmtblkno2 = args->rmtblkno;
@@ -1054,7 +1053,7 @@ xfs_attr_leaf_addname(xfs_da_args_t *args)
* so that one disappears and one appears atomically. Then we
* must remove the "old" attribute/value pair.
*/
- if (args->rename) {
+ if (args->op_flags & XFS_DA_OP_RENAME) {
/*
* In a separate transaction, set the incomplete flag on the
* "old" attr and clear the incomplete flag on the "new" attr.
@@ -1307,7 +1306,7 @@ restart:
} else if (retval == EEXIST) {
if (args->flags & ATTR_CREATE)
goto out;
- args->rename = 1; /* atomic rename op */
+ args->op_flags |= XFS_DA_OP_RENAME; /* atomic rename op */
args->blkno2 = args->blkno; /* set 2nd entry info*/
args->index2 = args->index;
args->rmtblkno2 = args->rmtblkno;
@@ -1425,7 +1424,7 @@ restart:
* so that one disappears and one appears atomically. Then we
* must remove the "old" attribute/value pair.
*/
- if (args->rename) {
+ if (args->op_flags & XFS_DA_OP_RENAME) {
/*
* In a separate transaction, set the incomplete flag on the
* "old" attr and clear the incomplete flag on the "new" attr.
@@ -2300,23 +2299,7 @@ xfs_attr_rmtval_remove(xfs_da_args_t *args)
void
xfs_attr_trace_l_c(char *where, struct xfs_attr_list_context *context)
{
- xfs_attr_trace_enter(XFS_ATTR_KTRACE_L_C, where,
- (__psunsigned_t)context->dp,
- (__psunsigned_t)context->cursor->hashval,
- (__psunsigned_t)context->cursor->blkno,
- (__psunsigned_t)context->cursor->offset,
- (__psunsigned_t)context->alist,
- (__psunsigned_t)context->bufsize,
- (__psunsigned_t)context->count,
- (__psunsigned_t)context->firstu,
- (__psunsigned_t)
- ((context->count > 0) &&
- !(context->flags & (ATTR_KERNAMELS|ATTR_KERNOVAL)))
- ? (ATTR_ENTRY(context->alist,
- context->count-1)->a_valuelen)
- : 0,
- (__psunsigned_t)context->dupcnt,
- (__psunsigned_t)context->flags,
+ xfs_attr_trace_enter(XFS_ATTR_KTRACE_L_C, where, context,
(__psunsigned_t)NULL,
(__psunsigned_t)NULL,
(__psunsigned_t)NULL);
@@ -2329,23 +2312,7 @@ void
xfs_attr_trace_l_cn(char *where, struct xfs_attr_list_context *context,
struct xfs_da_intnode *node)
{
- xfs_attr_trace_enter(XFS_ATTR_KTRACE_L_CN, where,
- (__psunsigned_t)context->dp,
- (__psunsigned_t)context->cursor->hashval,
- (__psunsigned_t)context->cursor->blkno,
- (__psunsigned_t)context->cursor->offset,
- (__psunsigned_t)context->alist,
- (__psunsigned_t)context->bufsize,
- (__psunsigned_t)context->count,
- (__psunsigned_t)context->firstu,
- (__psunsigned_t)
- ((context->count > 0) &&
- !(context->flags & (ATTR_KERNAMELS|ATTR_KERNOVAL)))
- ? (ATTR_ENTRY(context->alist,
- context->count-1)->a_valuelen)
- : 0,
- (__psunsigned_t)context->dupcnt,
- (__psunsigned_t)context->flags,
+ xfs_attr_trace_enter(XFS_ATTR_KTRACE_L_CN, where, context,
(__psunsigned_t)be16_to_cpu(node->hdr.count),
(__psunsigned_t)be32_to_cpu(node->btree[0].hashval),
(__psunsigned_t)be32_to_cpu(node->btree[
@@ -2359,23 +2326,7 @@ void
xfs_attr_trace_l_cb(char *where, struct xfs_attr_list_context *context,
struct xfs_da_node_entry *btree)
{
- xfs_attr_trace_enter(XFS_ATTR_KTRACE_L_CB, where,
- (__psunsigned_t)context->dp,
- (__psunsigned_t)context->cursor->hashval,
- (__psunsigned_t)context->cursor->blkno,
- (__psunsigned_t)context->cursor->offset,
- (__psunsigned_t)context->alist,
- (__psunsigned_t)context->bufsize,
- (__psunsigned_t)context->count,
- (__psunsigned_t)context->firstu,
- (__psunsigned_t)
- ((context->count > 0) &&
- !(context->flags & (ATTR_KERNAMELS|ATTR_KERNOVAL)))
- ? (ATTR_ENTRY(context->alist,
- context->count-1)->a_valuelen)
- : 0,
- (__psunsigned_t)context->dupcnt,
- (__psunsigned_t)context->flags,
+ xfs_attr_trace_enter(XFS_ATTR_KTRACE_L_CB, where, context,
(__psunsigned_t)be32_to_cpu(btree->hashval),
(__psunsigned_t)be32_to_cpu(btree->before),
(__psunsigned_t)NULL);
@@ -2388,23 +2339,7 @@ void
xfs_attr_trace_l_cl(char *where, struct xfs_attr_list_context *context,
struct xfs_attr_leafblock *leaf)
{
- xfs_attr_trace_enter(XFS_ATTR_KTRACE_L_CL, where,
- (__psunsigned_t)context->dp,
- (__psunsigned_t)context->cursor->hashval,
- (__psunsigned_t)context->cursor->blkno,
- (__psunsigned_t)context->cursor->offset,
- (__psunsigned_t)context->alist,
- (__psunsigned_t)context->bufsize,
- (__psunsigned_t)context->count,
- (__psunsigned_t)context->firstu,
- (__psunsigned_t)
- ((context->count > 0) &&
- !(context->flags & (ATTR_KERNAMELS|ATTR_KERNOVAL)))
- ? (ATTR_ENTRY(context->alist,
- context->count-1)->a_valuelen)
- : 0,
- (__psunsigned_t)context->dupcnt,
- (__psunsigned_t)context->flags,
+ xfs_attr_trace_enter(XFS_ATTR_KTRACE_L_CL, where, context,
(__psunsigned_t)be16_to_cpu(leaf->hdr.count),
(__psunsigned_t)be32_to_cpu(leaf->entries[0].hashval),
(__psunsigned_t)be32_to_cpu(leaf->entries[
@@ -2417,22 +2352,30 @@ xfs_attr_trace_l_cl(char *where, struct xfs_attr_list_context *context,
*/
void
xfs_attr_trace_enter(int type, char *where,
- __psunsigned_t a2, __psunsigned_t a3,
- __psunsigned_t a4, __psunsigned_t a5,
- __psunsigned_t a6, __psunsigned_t a7,
- __psunsigned_t a8, __psunsigned_t a9,
- __psunsigned_t a10, __psunsigned_t a11,
- __psunsigned_t a12, __psunsigned_t a13,
- __psunsigned_t a14, __psunsigned_t a15)
+ struct xfs_attr_list_context *context,
+ __psunsigned_t a13, __psunsigned_t a14,
+ __psunsigned_t a15)
{
ASSERT(xfs_attr_trace_buf);
ktrace_enter(xfs_attr_trace_buf, (void *)((__psunsigned_t)type),
- (void *)where,
- (void *)a2, (void *)a3, (void *)a4,
- (void *)a5, (void *)a6, (void *)a7,
- (void *)a8, (void *)a9, (void *)a10,
- (void *)a11, (void *)a12, (void *)a13,
- (void *)a14, (void *)a15);
+ (void *)((__psunsigned_t)where),
+ (void *)((__psunsigned_t)context->dp),
+ (void *)((__psunsigned_t)context->cursor->hashval),
+ (void *)((__psunsigned_t)context->cursor->blkno),
+ (void *)((__psunsigned_t)context->cursor->offset),
+ (void *)((__psunsigned_t)context->alist),
+ (void *)((__psunsigned_t)context->bufsize),
+ (void *)((__psunsigned_t)context->count),
+ (void *)((__psunsigned_t)context->firstu),
+ (void *)((__psunsigned_t)
+ (((context->count > 0) &&
+ !(context->flags & (ATTR_KERNAMELS|ATTR_KERNOVAL)))
+ ? (ATTR_ENTRY(context->alist,
+ context->count-1)->a_valuelen)
+ : 0)),
+ (void *)((__psunsigned_t)context->dupcnt),
+ (void *)((__psunsigned_t)context->flags),
+ (void *)a13, (void *)a14, (void *)a15);
}
#endif /* XFS_ATTR_TRACE */
@@ -2622,43 +2565,6 @@ attr_lookup_namespace(
return NULL;
}
-/*
- * Some checks to prevent people abusing EAs to get over quota:
- * - Don't allow modifying user EAs on devices/symlinks;
- * - Don't allow modifying user EAs if sticky bit set;
- */
-STATIC int
-attr_user_capable(
- bhv_vnode_t *vp,
- cred_t *cred)
-{
- struct inode *inode = vn_to_inode(vp);
-
- if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
- return -EPERM;
- if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode) &&
- !capable(CAP_SYS_ADMIN))
- return -EPERM;
- if (S_ISDIR(inode->i_mode) && (inode->i_mode & S_ISVTX) &&
- (current_fsuid(cred) != inode->i_uid) && !capable(CAP_FOWNER))
- return -EPERM;
- return 0;
-}
-
-STATIC int
-attr_trusted_capable(
- bhv_vnode_t *vp,
- cred_t *cred)
-{
- struct inode *inode = vn_to_inode(vp);
-
- if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
- return -EPERM;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- return 0;
-}
-
STATIC int
attr_system_set(
bhv_vnode_t *vp, char *name, void *data, size_t size, int xflags)
@@ -2709,7 +2615,6 @@ struct attrnames attr_system = {
.attr_get = attr_system_get,
.attr_set = attr_system_set,
.attr_remove = attr_system_remove,
- .attr_capable = (attrcapable_t)fs_noerr,
};
struct attrnames attr_trusted = {
@@ -2719,7 +2624,6 @@ struct attrnames attr_trusted = {
.attr_get = attr_generic_get,
.attr_set = attr_generic_set,
.attr_remove = attr_generic_remove,
- .attr_capable = attr_trusted_capable,
};
struct attrnames attr_secure = {
@@ -2729,7 +2633,6 @@ struct attrnames attr_secure = {
.attr_get = attr_generic_get,
.attr_set = attr_generic_set,
.attr_remove = attr_generic_remove,
- .attr_capable = (attrcapable_t)fs_noerr,
};
struct attrnames attr_user = {
@@ -2738,7 +2641,6 @@ struct attrnames attr_user = {
.attr_get = attr_generic_get,
.attr_set = attr_generic_set,
.attr_remove = attr_generic_remove,
- .attr_capable = attr_user_capable,
};
struct attrnames *attr_namespaces[] =
diff --git a/fs/xfs/xfs_attr.h b/fs/xfs/xfs_attr.h
index 6cfc9384fe35..9b96d171b75c 100644
--- a/fs/xfs/xfs_attr.h
+++ b/fs/xfs/xfs_attr.h
@@ -42,7 +42,6 @@ typedef int (*attrset_t)(bhv_vnode_t *, char *, void *, size_t, int);
typedef int (*attrget_t)(bhv_vnode_t *, char *, void *, size_t, int);
typedef int (*attrremove_t)(bhv_vnode_t *, char *, int);
typedef int (*attrexists_t)(bhv_vnode_t *);
-typedef int (*attrcapable_t)(bhv_vnode_t *, struct cred *);
typedef struct attrnames {
char * attr_name;
@@ -52,7 +51,6 @@ typedef struct attrnames {
attrset_t attr_set;
attrremove_t attr_remove;
attrexists_t attr_exists;
- attrcapable_t attr_capable;
} attrnames_t;
#define ATTR_NAMECOUNT 4
diff --git a/fs/xfs/xfs_attr_leaf.c b/fs/xfs/xfs_attr_leaf.c
index 303d41e4217b..cb345e6e4850 100644
--- a/fs/xfs/xfs_attr_leaf.c
+++ b/fs/xfs/xfs_attr_leaf.c
@@ -369,9 +369,10 @@ xfs_attr_shortform_remove(xfs_da_args_t *args)
* Fix up the start offset of the attribute fork
*/
totsize -= size;
- if (totsize == sizeof(xfs_attr_sf_hdr_t) && !args->addname &&
- (mp->m_flags & XFS_MOUNT_ATTR2) &&
- (dp->i_d.di_format != XFS_DINODE_FMT_BTREE)) {
+ if (totsize == sizeof(xfs_attr_sf_hdr_t) &&
+ !(args->op_flags & XFS_DA_OP_ADDNAME) &&
+ (mp->m_flags & XFS_MOUNT_ATTR2) &&
+ (dp->i_d.di_format != XFS_DINODE_FMT_BTREE)) {
/*
* Last attribute now removed, revert to original
* inode format making all literal area available
@@ -389,9 +390,10 @@ xfs_attr_shortform_remove(xfs_da_args_t *args)
xfs_idata_realloc(dp, -size, XFS_ATTR_FORK);
dp->i_d.di_forkoff = xfs_attr_shortform_bytesfit(dp, totsize);
ASSERT(dp->i_d.di_forkoff);
- ASSERT(totsize > sizeof(xfs_attr_sf_hdr_t) || args->addname ||
- !(mp->m_flags & XFS_MOUNT_ATTR2) ||
- dp->i_d.di_format == XFS_DINODE_FMT_BTREE);
+ ASSERT(totsize > sizeof(xfs_attr_sf_hdr_t) ||
+ (args->op_flags & XFS_DA_OP_ADDNAME) ||
+ !(mp->m_flags & XFS_MOUNT_ATTR2) ||
+ dp->i_d.di_format == XFS_DINODE_FMT_BTREE);
dp->i_afp->if_ext_max =
XFS_IFORK_ASIZE(dp) / (uint)sizeof(xfs_bmbt_rec_t);
dp->i_df.if_ext_max =
@@ -531,7 +533,7 @@ xfs_attr_shortform_to_leaf(xfs_da_args_t *args)
nargs.total = args->total;
nargs.whichfork = XFS_ATTR_FORK;
nargs.trans = args->trans;
- nargs.oknoent = 1;
+ nargs.op_flags = XFS_DA_OP_OKNOENT;
sfe = &sf->list[0];
for (i = 0; i < sf->hdr.count; i++) {
@@ -555,7 +557,7 @@ xfs_attr_shortform_to_leaf(xfs_da_args_t *args)
out:
if(bp)
xfs_da_buf_done(bp);
- kmem_free(tmpbuffer, size);
+ kmem_free(tmpbuffer);
return(error);
}
@@ -676,7 +678,7 @@ xfs_attr_shortform_list(xfs_attr_list_context_t *context)
XFS_ERRLEVEL_LOW,
context->dp->i_mount, sfe);
xfs_attr_trace_l_c("sf corrupted", context);
- kmem_free(sbuf, sbsize);
+ kmem_free(sbuf);
return XFS_ERROR(EFSCORRUPTED);
}
if (!xfs_attr_namesp_match_overrides(context->flags, sfe->flags)) {
@@ -717,7 +719,7 @@ xfs_attr_shortform_list(xfs_attr_list_context_t *context)
}
}
if (i == nsbuf) {
- kmem_free(sbuf, sbsize);
+ kmem_free(sbuf);
xfs_attr_trace_l_c("blk end", context);
return(0);
}
@@ -747,7 +749,7 @@ xfs_attr_shortform_list(xfs_attr_list_context_t *context)
cursor->offset++;
}
- kmem_free(sbuf, sbsize);
+ kmem_free(sbuf);
xfs_attr_trace_l_c("sf E-O-F", context);
return(0);
}
@@ -853,7 +855,7 @@ xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args, int forkoff)
nargs.total = args->total;
nargs.whichfork = XFS_ATTR_FORK;
nargs.trans = args->trans;
- nargs.oknoent = 1;
+ nargs.op_flags = XFS_DA_OP_OKNOENT;
entry = &leaf->entries[0];
for (i = 0; i < be16_to_cpu(leaf->hdr.count); entry++, i++) {
if (entry->flags & XFS_ATTR_INCOMPLETE)
@@ -873,7 +875,7 @@ xfs_attr_leaf_to_shortform(xfs_dabuf_t *bp, xfs_da_args_t *args, int forkoff)
error = 0;
out:
- kmem_free(tmpbuffer, XFS_LBSIZE(dp->i_mount));
+ kmem_free(tmpbuffer);
return(error);
}
@@ -1155,7 +1157,7 @@ xfs_attr_leaf_add_work(xfs_dabuf_t *bp, xfs_da_args_t *args, int mapindex)
entry->hashval = cpu_to_be32(args->hashval);
entry->flags = tmp ? XFS_ATTR_LOCAL : 0;
entry->flags |= XFS_ATTR_NSP_ARGS_TO_ONDISK(args->flags);
- if (args->rename) {
+ if (args->op_flags & XFS_DA_OP_RENAME) {
entry->flags |= XFS_ATTR_INCOMPLETE;
if ((args->blkno2 == args->blkno) &&
(args->index2 <= args->index)) {
@@ -1271,7 +1273,7 @@ xfs_attr_leaf_compact(xfs_trans_t *trans, xfs_dabuf_t *bp)
be16_to_cpu(hdr_s->count), mp);
xfs_da_log_buf(trans, bp, 0, XFS_LBSIZE(mp) - 1);
- kmem_free(tmpbuffer, XFS_LBSIZE(mp));
+ kmem_free(tmpbuffer);
}
/*
@@ -1921,7 +1923,7 @@ xfs_attr_leaf_unbalance(xfs_da_state_t *state, xfs_da_state_blk_t *drop_blk,
be16_to_cpu(drop_hdr->count), mp);
}
memcpy((char *)save_leaf, (char *)tmp_leaf, state->blocksize);
- kmem_free(tmpbuffer, state->blocksize);
+ kmem_free(tmpbuffer);
}
xfs_da_log_buf(state->args->trans, save_blk->bp, 0,
@@ -2451,7 +2453,7 @@ xfs_attr_leaf_list_int(xfs_dabuf_t *bp, xfs_attr_list_context_t *context)
(int)name_rmt->namelen,
valuelen,
(char*)args.value);
- kmem_free(args.value, valuelen);
+ kmem_free(args.value);
}
else {
retval = context->put_listent(context,
@@ -2954,7 +2956,7 @@ xfs_attr_leaf_inactive(xfs_trans_t **trans, xfs_inode_t *dp, xfs_dabuf_t *bp)
error = tmp; /* save only the 1st errno */
}
- kmem_free((xfs_caddr_t)list, size);
+ kmem_free((xfs_caddr_t)list);
return(error);
}
diff --git a/fs/xfs/xfs_attr_sf.h b/fs/xfs/xfs_attr_sf.h
index f67f917803b1..ea22839caed2 100644
--- a/fs/xfs/xfs_attr_sf.h
+++ b/fs/xfs/xfs_attr_sf.h
@@ -97,13 +97,9 @@ void xfs_attr_trace_l_cb(char *where, struct xfs_attr_list_context *context,
void xfs_attr_trace_l_cl(char *where, struct xfs_attr_list_context *context,
struct xfs_attr_leafblock *leaf);
void xfs_attr_trace_enter(int type, char *where,
- __psunsigned_t a2, __psunsigned_t a3,
- __psunsigned_t a4, __psunsigned_t a5,
- __psunsigned_t a6, __psunsigned_t a7,
- __psunsigned_t a8, __psunsigned_t a9,
- __psunsigned_t a10, __psunsigned_t a11,
- __psunsigned_t a12, __psunsigned_t a13,
- __psunsigned_t a14, __psunsigned_t a15);
+ struct xfs_attr_list_context *context,
+ __psunsigned_t a13, __psunsigned_t a14,
+ __psunsigned_t a15);
#else
#define xfs_attr_trace_l_c(w,c)
#define xfs_attr_trace_l_cn(w,c,n)
diff --git a/fs/xfs/xfs_bmap.c b/fs/xfs/xfs_bmap.c
index 53c259f5a5af..a612a90aae4a 100644
--- a/fs/xfs/xfs_bmap.c
+++ b/fs/xfs/xfs_bmap.c
@@ -5970,7 +5970,7 @@ unlock_and_return:
xfs_iunlock_map_shared(ip, lock);
xfs_iunlock(ip, XFS_IOLOCK_SHARED);
- kmem_free(map, subnex * sizeof(*map));
+ kmem_free(map);
return error;
}
diff --git a/fs/xfs/xfs_buf_item.c b/fs/xfs/xfs_buf_item.c
index 53a71c62025d..d86ca2c03a70 100644
--- a/fs/xfs/xfs_buf_item.c
+++ b/fs/xfs/xfs_buf_item.c
@@ -889,9 +889,9 @@ xfs_buf_item_relse(
}
#ifdef XFS_TRANS_DEBUG
- kmem_free(bip->bli_orig, XFS_BUF_COUNT(bp));
+ kmem_free(bip->bli_orig);
bip->bli_orig = NULL;
- kmem_free(bip->bli_logged, XFS_BUF_COUNT(bp) / NBBY);
+ kmem_free(bip->bli_logged);
bip->bli_logged = NULL;
#endif /* XFS_TRANS_DEBUG */
@@ -1138,9 +1138,9 @@ xfs_buf_iodone(
xfs_trans_delete_ail(mp, (xfs_log_item_t *)bip);
#ifdef XFS_TRANS_DEBUG
- kmem_free(bip->bli_orig, XFS_BUF_COUNT(bp));
+ kmem_free(bip->bli_orig);
bip->bli_orig = NULL;
- kmem_free(bip->bli_logged, XFS_BUF_COUNT(bp) / NBBY);
+ kmem_free(bip->bli_logged);
bip->bli_logged = NULL;
#endif /* XFS_TRANS_DEBUG */
diff --git a/fs/xfs/xfs_clnt.h b/fs/xfs/xfs_clnt.h
index d5d1e60ee224..d2ce5dd70d87 100644
--- a/fs/xfs/xfs_clnt.h
+++ b/fs/xfs/xfs_clnt.h
@@ -78,6 +78,7 @@ struct xfs_mount_args {
#define XFSMNT_IOSIZE 0x00002000 /* optimize for I/O size */
#define XFSMNT_OSYNCISOSYNC 0x00004000 /* o_sync is REALLY o_sync */
/* (osyncisdsync is default) */
+#define XFSMNT_NOATTR2 0x00008000 /* turn off ATTR2 EA format */
#define XFSMNT_32BITINODES 0x00200000 /* restrict inodes to 32
* bits of address space */
#define XFSMNT_GQUOTA 0x00400000 /* group quota accounting */
diff --git a/fs/xfs/xfs_da_btree.c b/fs/xfs/xfs_da_btree.c
index 021a8f7e563f..edc0aef4e51e 100644
--- a/fs/xfs/xfs_da_btree.c
+++ b/fs/xfs/xfs_da_btree.c
@@ -1431,7 +1431,7 @@ xfs_da_path_shift(xfs_da_state_t *state, xfs_da_state_path_t *path,
}
if (level < 0) {
*result = XFS_ERROR(ENOENT); /* we're out of our tree */
- ASSERT(args->oknoent);
+ ASSERT(args->op_flags & XFS_DA_OP_OKNOENT);
return(0);
}
@@ -1530,6 +1530,28 @@ xfs_da_hashname(const uchar_t *name, int namelen)
}
}
+enum xfs_dacmp
+xfs_da_compname(
+ struct xfs_da_args *args,
+ const char *name,
+ int len)
+{
+ return (args->namelen == len && memcmp(args->name, name, len) == 0) ?
+ XFS_CMP_EXACT : XFS_CMP_DIFFERENT;
+}
+
+static xfs_dahash_t
+xfs_default_hashname(
+ struct xfs_name *name)
+{
+ return xfs_da_hashname(name->name, name->len);
+}
+
+const struct xfs_nameops xfs_default_nameops = {
+ .hashname = xfs_default_hashname,
+ .compname = xfs_da_compname
+};
+
/*
* Add a block to the btree ahead of the file.
* Return the new block number to the caller.
@@ -1598,7 +1620,7 @@ xfs_da_grow_inode(xfs_da_args_t *args, xfs_dablk_t *new_blkno)
args->firstblock, args->total,
&mapp[mapi], &nmap, args->flist,
NULL))) {
- kmem_free(mapp, sizeof(*mapp) * count);
+ kmem_free(mapp);
return error;
}
if (nmap < 1)
@@ -1620,11 +1642,11 @@ xfs_da_grow_inode(xfs_da_args_t *args, xfs_dablk_t *new_blkno)
mapp[mapi - 1].br_startoff + mapp[mapi - 1].br_blockcount !=
bno + count) {
if (mapp != &map)
- kmem_free(mapp, sizeof(*mapp) * count);
+ kmem_free(mapp);
return XFS_ERROR(ENOSPC);
}
if (mapp != &map)
- kmem_free(mapp, sizeof(*mapp) * count);
+ kmem_free(mapp);
*new_blkno = (xfs_dablk_t)bno;
return 0;
}
@@ -2090,10 +2112,10 @@ xfs_da_do_buf(
}
}
if (bplist) {
- kmem_free(bplist, sizeof(*bplist) * nmap);
+ kmem_free(bplist);
}
if (mapp != &map) {
- kmem_free(mapp, sizeof(*mapp) * nfsb);
+ kmem_free(mapp);
}
if (bpp)
*bpp = rbp;
@@ -2102,11 +2124,11 @@ exit1:
if (bplist) {
for (i = 0; i < nbplist; i++)
xfs_trans_brelse(trans, bplist[i]);
- kmem_free(bplist, sizeof(*bplist) * nmap);
+ kmem_free(bplist);
}
exit0:
if (mapp != &map)
- kmem_free(mapp, sizeof(*mapp) * nfsb);
+ kmem_free(mapp);
if (bpp)
*bpp = NULL;
return error;
@@ -2315,7 +2337,7 @@ xfs_da_buf_done(xfs_dabuf_t *dabuf)
if (dabuf->dirty)
xfs_da_buf_clean(dabuf);
if (dabuf->nbuf > 1)
- kmem_free(dabuf->data, BBTOB(dabuf->bbcount));
+ kmem_free(dabuf->data);
#ifdef XFS_DABUF_DEBUG
{
spin_lock(&xfs_dabuf_global_lock);
@@ -2332,7 +2354,7 @@ xfs_da_buf_done(xfs_dabuf_t *dabuf)
if (dabuf->nbuf == 1)
kmem_zone_free(xfs_dabuf_zone, dabuf);
else
- kmem_free(dabuf, XFS_DA_BUF_SIZE(dabuf->nbuf));
+ kmem_free(dabuf);
}
/*
@@ -2403,7 +2425,7 @@ xfs_da_brelse(xfs_trans_t *tp, xfs_dabuf_t *dabuf)
for (i = 0; i < nbuf; i++)
xfs_trans_brelse(tp, bplist[i]);
if (bplist != &bp)
- kmem_free(bplist, nbuf * sizeof(*bplist));
+ kmem_free(bplist);
}
/*
@@ -2429,7 +2451,7 @@ xfs_da_binval(xfs_trans_t *tp, xfs_dabuf_t *dabuf)
for (i = 0; i < nbuf; i++)
xfs_trans_binval(tp, bplist[i]);
if (bplist != &bp)
- kmem_free(bplist, nbuf * sizeof(*bplist));
+ kmem_free(bplist);
}
/*
diff --git a/fs/xfs/xfs_da_btree.h b/fs/xfs/xfs_da_btree.h
index 7facf86f74f9..8be0b00ede9a 100644
--- a/fs/xfs/xfs_da_btree.h
+++ b/fs/xfs/xfs_da_btree.h
@@ -99,6 +99,15 @@ typedef struct xfs_da_node_entry xfs_da_node_entry_t;
*========================================================================*/
/*
+ * Search comparison results
+ */
+enum xfs_dacmp {
+ XFS_CMP_DIFFERENT, /* names are completely different */
+ XFS_CMP_EXACT, /* names are exactly the same */
+ XFS_CMP_CASE /* names are same but differ in case */
+};
+
+/*
* Structure to ease passing around component names.
*/
typedef struct xfs_da_args {
@@ -123,13 +132,20 @@ typedef struct xfs_da_args {
int index2; /* index of 2nd attr in blk */
xfs_dablk_t rmtblkno2; /* remote attr value starting blkno */
int rmtblkcnt2; /* remote attr value block count */
- unsigned char justcheck; /* T/F: check for ok with no space */
- unsigned char rename; /* T/F: this is an atomic rename op */
- unsigned char addname; /* T/F: this is an add operation */
- unsigned char oknoent; /* T/F: ok to return ENOENT, else die */
+ int op_flags; /* operation flags */
+ enum xfs_dacmp cmpresult; /* name compare result for lookups */
} xfs_da_args_t;
/*
+ * Operation flags:
+ */
+#define XFS_DA_OP_JUSTCHECK 0x0001 /* check for ok with no space */
+#define XFS_DA_OP_RENAME 0x0002 /* this is an atomic rename op */
+#define XFS_DA_OP_ADDNAME 0x0004 /* this is an add operation */
+#define XFS_DA_OP_OKNOENT 0x0008 /* lookup/add op, ENOENT ok, else die */
+#define XFS_DA_OP_CILOOKUP 0x0010 /* lookup to return CI name if found */
+
+/*
* Structure to describe buffer(s) for a block.
* This is needed in the directory version 2 format case, when
* multiple non-contiguous fsblocks might be needed to cover one
@@ -201,6 +217,14 @@ typedef struct xfs_da_state {
(uint)(XFS_DA_LOGOFF(BASE, ADDR)), \
(uint)(XFS_DA_LOGOFF(BASE, ADDR)+(SIZE)-1)
+/*
+ * Name ops for directory and/or attr name operations
+ */
+struct xfs_nameops {
+ xfs_dahash_t (*hashname)(struct xfs_name *);
+ enum xfs_dacmp (*compname)(struct xfs_da_args *, const char *, int);
+};
+
#ifdef __KERNEL__
/*========================================================================
@@ -249,6 +273,10 @@ int xfs_da_shrink_inode(xfs_da_args_t *args, xfs_dablk_t dead_blkno,
xfs_dabuf_t *dead_buf);
uint xfs_da_hashname(const uchar_t *name_string, int name_length);
+enum xfs_dacmp xfs_da_compname(struct xfs_da_args *args,
+ const char *name, int len);
+
+
xfs_da_state_t *xfs_da_state_alloc(void);
void xfs_da_state_free(xfs_da_state_t *state);
diff --git a/fs/xfs/xfs_dfrag.c b/fs/xfs/xfs_dfrag.c
index 5f3647cb9885..2211e885ef24 100644
--- a/fs/xfs/xfs_dfrag.c
+++ b/fs/xfs/xfs_dfrag.c
@@ -116,7 +116,7 @@ xfs_swapext(
out_put_file:
fput(file);
out_free_sxp:
- kmem_free(sxp, sizeof(xfs_swapext_t));
+ kmem_free(sxp);
out:
return error;
}
@@ -381,6 +381,6 @@ xfs_swap_extents(
xfs_iunlock(tip, lock_flags);
}
if (tempifp != NULL)
- kmem_free(tempifp, sizeof(xfs_ifork_t));
+ kmem_free(tempifp);
return error;
}
diff --git a/fs/xfs/xfs_dir2.c b/fs/xfs/xfs_dir2.c
index 7cb26529766b..80e0dc51361c 100644
--- a/fs/xfs/xfs_dir2.c
+++ b/fs/xfs/xfs_dir2.c
@@ -46,6 +46,54 @@
struct xfs_name xfs_name_dotdot = {"..", 2};
+extern const struct xfs_nameops xfs_default_nameops;
+
+/*
+ * ASCII case-insensitive (ie. A-Z) support for directories that was
+ * used in IRIX.
+ */
+STATIC xfs_dahash_t
+xfs_ascii_ci_hashname(
+ struct xfs_name *name)
+{
+ xfs_dahash_t hash;
+ int i;
+
+ for (i = 0, hash = 0; i < name->len; i++)
+ hash = tolower(name->name[i]) ^ rol32(hash, 7);
+
+ return hash;
+}
+
+STATIC enum xfs_dacmp
+xfs_ascii_ci_compname(
+ struct xfs_da_args *args,
+ const char *name,
+ int len)
+{
+ enum xfs_dacmp result;
+ int i;
+
+ if (args->namelen != len)
+ return XFS_CMP_DIFFERENT;
+
+ result = XFS_CMP_EXACT;
+ for (i = 0; i < len; i++) {
+ if (args->name[i] == name[i])
+ continue;
+ if (tolower(args->name[i]) != tolower(name[i]))
+ return XFS_CMP_DIFFERENT;
+ result = XFS_CMP_CASE;
+ }
+
+ return result;
+}
+
+static struct xfs_nameops xfs_ascii_ci_nameops = {
+ .hashname = xfs_ascii_ci_hashname,
+ .compname = xfs_ascii_ci_compname,
+};
+
void
xfs_dir_mount(
xfs_mount_t *mp)
@@ -65,6 +113,10 @@ xfs_dir_mount(
(mp->m_dirblksize - (uint)sizeof(xfs_da_node_hdr_t)) /
(uint)sizeof(xfs_da_node_entry_t);
mp->m_dir_magicpct = (mp->m_dirblksize * 37) / 100;
+ if (xfs_sb_version_hasasciici(&mp->m_sb))
+ mp->m_dirnameops = &xfs_ascii_ci_nameops;
+ else
+ mp->m_dirnameops = &xfs_default_nameops;
}
/*
@@ -162,9 +214,10 @@ xfs_dir_createname(
return rval;
XFS_STATS_INC(xs_dir_create);
+ memset(&args, 0, sizeof(xfs_da_args_t));
args.name = name->name;
args.namelen = name->len;
- args.hashval = xfs_da_hashname(name->name, name->len);
+ args.hashval = dp->i_mount->m_dirnameops->hashname(name);
args.inumber = inum;
args.dp = dp;
args.firstblock = first;
@@ -172,8 +225,7 @@ xfs_dir_createname(
args.total = total;
args.whichfork = XFS_DATA_FORK;
args.trans = tp;
- args.justcheck = 0;
- args.addname = args.oknoent = 1;
+ args.op_flags = XFS_DA_OP_ADDNAME | XFS_DA_OP_OKNOENT;
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
rval = xfs_dir2_sf_addname(&args);
@@ -191,14 +243,43 @@ xfs_dir_createname(
}
/*
+ * If doing a CI lookup and case-insensitive match, dup actual name into
+ * args.value. Return EEXIST for success (ie. name found) or an error.
+ */
+int
+xfs_dir_cilookup_result(
+ struct xfs_da_args *args,
+ const char *name,
+ int len)
+{
+ if (args->cmpresult == XFS_CMP_DIFFERENT)
+ return ENOENT;
+ if (args->cmpresult != XFS_CMP_CASE ||
+ !(args->op_flags & XFS_DA_OP_CILOOKUP))
+ return EEXIST;
+
+ args->value = kmem_alloc(len, KM_MAYFAIL);
+ if (!args->value)
+ return ENOMEM;
+
+ memcpy(args->value, name, len);
+ args->valuelen = len;
+ return EEXIST;
+}
+
+/*
* Lookup a name in a directory, give back the inode number.
+ * If ci_name is not NULL, returns the actual name in ci_name if it differs
+ * to name, or ci_name->name is set to NULL for an exact match.
*/
+
int
xfs_dir_lookup(
xfs_trans_t *tp,
xfs_inode_t *dp,
struct xfs_name *name,
- xfs_ino_t *inum) /* out: inode number */
+ xfs_ino_t *inum, /* out: inode number */
+ struct xfs_name *ci_name) /* out: actual name if CI match */
{
xfs_da_args_t args;
int rval;
@@ -206,15 +287,17 @@ xfs_dir_lookup(
ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
XFS_STATS_INC(xs_dir_lookup);
- memset(&args, 0, sizeof(xfs_da_args_t));
+ memset(&args, 0, sizeof(xfs_da_args_t));
args.name = name->name;
args.namelen = name->len;
- args.hashval = xfs_da_hashname(name->name, name->len);
+ args.hashval = dp->i_mount->m_dirnameops->hashname(name);
args.dp = dp;
args.whichfork = XFS_DATA_FORK;
args.trans = tp;
- args.oknoent = 1;
+ args.op_flags = XFS_DA_OP_OKNOENT;
+ if (ci_name)
+ args.op_flags |= XFS_DA_OP_CILOOKUP;
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
rval = xfs_dir2_sf_lookup(&args);
@@ -230,8 +313,13 @@ xfs_dir_lookup(
rval = xfs_dir2_node_lookup(&args);
if (rval == EEXIST)
rval = 0;
- if (rval == 0)
+ if (!rval) {
*inum = args.inumber;
+ if (ci_name) {
+ ci_name->name = args.value;
+ ci_name->len = args.valuelen;
+ }
+ }
return rval;
}
@@ -255,9 +343,10 @@ xfs_dir_removename(
ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
XFS_STATS_INC(xs_dir_remove);
+ memset(&args, 0, sizeof(xfs_da_args_t));
args.name = name->name;
args.namelen = name->len;
- args.hashval = xfs_da_hashname(name->name, name->len);
+ args.hashval = dp->i_mount->m_dirnameops->hashname(name);
args.inumber = ino;
args.dp = dp;
args.firstblock = first;
@@ -265,7 +354,6 @@ xfs_dir_removename(
args.total = total;
args.whichfork = XFS_DATA_FORK;
args.trans = tp;
- args.justcheck = args.addname = args.oknoent = 0;
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
rval = xfs_dir2_sf_removename(&args);
@@ -338,9 +426,10 @@ xfs_dir_replace(
if ((rval = xfs_dir_ino_validate(tp->t_mountp, inum)))
return rval;
+ memset(&args, 0, sizeof(xfs_da_args_t));
args.name = name->name;
args.namelen = name->len;
- args.hashval = xfs_da_hashname(name->name, name->len);
+ args.hashval = dp->i_mount->m_dirnameops->hashname(name);
args.inumber = inum;
args.dp = dp;
args.firstblock = first;
@@ -348,7 +437,6 @@ xfs_dir_replace(
args.total = total;
args.whichfork = XFS_DATA_FORK;
args.trans = tp;
- args.justcheck = args.addname = args.oknoent = 0;
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
rval = xfs_dir2_sf_replace(&args);
@@ -384,15 +472,16 @@ xfs_dir_canenter(
return 0;
ASSERT((dp->i_d.di_mode & S_IFMT) == S_IFDIR);
- memset(&args, 0, sizeof(xfs_da_args_t));
+ memset(&args, 0, sizeof(xfs_da_args_t));
args.name = name->name;
args.namelen = name->len;
- args.hashval = xfs_da_hashname(name->name, name->len);
+ args.hashval = dp->i_mount->m_dirnameops->hashname(name);
args.dp = dp;
args.whichfork = XFS_DATA_FORK;
args.trans = tp;
- args.justcheck = args.addname = args.oknoent = 1;
+ args.op_flags = XFS_DA_OP_JUSTCHECK | XFS_DA_OP_ADDNAME |
+ XFS_DA_OP_OKNOENT;
if (dp->i_d.di_format == XFS_DINODE_FMT_LOCAL)
rval = xfs_dir2_sf_addname(&args);
@@ -493,7 +582,7 @@ xfs_dir2_grow_inode(
args->firstblock, args->total,
&mapp[mapi], &nmap, args->flist,
NULL))) {
- kmem_free(mapp, sizeof(*mapp) * count);
+ kmem_free(mapp);
return error;
}
if (nmap < 1)
@@ -525,14 +614,14 @@ xfs_dir2_grow_inode(
mapp[mapi - 1].br_startoff + mapp[mapi - 1].br_blockcount !=
bno + count) {
if (mapp != &map)
- kmem_free(mapp, sizeof(*mapp) * count);
+ kmem_free(mapp);
return XFS_ERROR(ENOSPC);
}
/*
* Done with the temporary mapping table.
*/
if (mapp != &map)
- kmem_free(mapp, sizeof(*mapp) * count);
+ kmem_free(mapp);
*dbp = xfs_dir2_da_to_db(mp, (xfs_dablk_t)bno);
/*
* Update file's size if this is the data space and it grew.
diff --git a/fs/xfs/xfs_dir2.h b/fs/xfs/xfs_dir2.h
index 6392f939029f..1d9ef96f33aa 100644
--- a/fs/xfs/xfs_dir2.h
+++ b/fs/xfs/xfs_dir2.h
@@ -74,7 +74,8 @@ extern int xfs_dir_createname(struct xfs_trans *tp, struct xfs_inode *dp,
xfs_fsblock_t *first,
struct xfs_bmap_free *flist, xfs_extlen_t tot);
extern int xfs_dir_lookup(struct xfs_trans *tp, struct xfs_inode *dp,
- struct xfs_name *name, xfs_ino_t *inum);
+ struct xfs_name *name, xfs_ino_t *inum,
+ struct xfs_name *ci_name);
extern int xfs_dir_removename(struct xfs_trans *tp, struct xfs_inode *dp,
struct xfs_name *name, xfs_ino_t ino,
xfs_fsblock_t *first,
@@ -99,4 +100,7 @@ extern int xfs_dir2_isleaf(struct xfs_trans *tp, struct xfs_inode *dp,
extern int xfs_dir2_shrink_inode(struct xfs_da_args *args, xfs_dir2_db_t db,
struct xfs_dabuf *bp);
+extern int xfs_dir_cilookup_result(struct xfs_da_args *args, const char *name,
+ int len);
+
#endif /* __XFS_DIR2_H__ */
diff --git a/fs/xfs/xfs_dir2_block.c b/fs/xfs/xfs_dir2_block.c
index fb5a556725b3..e2fa0a1d8e96 100644
--- a/fs/xfs/xfs_dir2_block.c
+++ b/fs/xfs/xfs_dir2_block.c
@@ -215,7 +215,7 @@ xfs_dir2_block_addname(
/*
* If this isn't a real add, we're done with the buffer.
*/
- if (args->justcheck)
+ if (args->op_flags & XFS_DA_OP_JUSTCHECK)
xfs_da_brelse(tp, bp);
/*
* If we don't have space for the new entry & leaf ...
@@ -225,7 +225,7 @@ xfs_dir2_block_addname(
* Not trying to actually do anything, or don't have
* a space reservation: return no-space.
*/
- if (args->justcheck || args->total == 0)
+ if ((args->op_flags & XFS_DA_OP_JUSTCHECK) || args->total == 0)
return XFS_ERROR(ENOSPC);
/*
* Convert to the next larger format.
@@ -240,7 +240,7 @@ xfs_dir2_block_addname(
/*
* Just checking, and it would work, so say so.
*/
- if (args->justcheck)
+ if (args->op_flags & XFS_DA_OP_JUSTCHECK)
return 0;
needlog = needscan = 0;
/*
@@ -610,14 +610,15 @@ xfs_dir2_block_lookup(
/*
* Get the offset from the leaf entry, to point to the data.
*/
- dep = (xfs_dir2_data_entry_t *)
- ((char *)block + xfs_dir2_dataptr_to_off(mp, be32_to_cpu(blp[ent].address)));
+ dep = (xfs_dir2_data_entry_t *)((char *)block +
+ xfs_dir2_dataptr_to_off(mp, be32_to_cpu(blp[ent].address)));
/*
- * Fill in inode number, release the block.
+ * Fill in inode number, CI name if appropriate, release the block.
*/
args->inumber = be64_to_cpu(dep->inumber);
+ error = xfs_dir_cilookup_result(args, dep->name, dep->namelen);
xfs_da_brelse(args->trans, bp);
- return XFS_ERROR(EEXIST);
+ return XFS_ERROR(error);
}
/*
@@ -643,6 +644,7 @@ xfs_dir2_block_lookup_int(
int mid; /* binary search current idx */
xfs_mount_t *mp; /* filesystem mount point */
xfs_trans_t *tp; /* transaction pointer */
+ enum xfs_dacmp cmp; /* comparison result */
dp = args->dp;
tp = args->trans;
@@ -673,7 +675,7 @@ xfs_dir2_block_lookup_int(
else
high = mid - 1;
if (low > high) {
- ASSERT(args->oknoent);
+ ASSERT(args->op_flags & XFS_DA_OP_OKNOENT);
xfs_da_brelse(tp, bp);
return XFS_ERROR(ENOENT);
}
@@ -697,20 +699,31 @@ xfs_dir2_block_lookup_int(
dep = (xfs_dir2_data_entry_t *)
((char *)block + xfs_dir2_dataptr_to_off(mp, addr));
/*
- * Compare, if it's right give back buffer & entry number.
+ * Compare name and if it's an exact match, return the index
+ * and buffer. If it's the first case-insensitive match, store
+ * the index and buffer and continue looking for an exact match.
*/
- if (dep->namelen == args->namelen &&
- dep->name[0] == args->name[0] &&
- memcmp(dep->name, args->name, args->namelen) == 0) {
+ cmp = mp->m_dirnameops->compname(args, dep->name, dep->namelen);
+ if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
+ args->cmpresult = cmp;
*bpp = bp;
*entno = mid;
- return 0;
+ if (cmp == XFS_CMP_EXACT)
+ return 0;
}
- } while (++mid < be32_to_cpu(btp->count) && be32_to_cpu(blp[mid].hashval) == hash);
+ } while (++mid < be32_to_cpu(btp->count) &&
+ be32_to_cpu(blp[mid].hashval) == hash);
+
+ ASSERT(args->op_flags & XFS_DA_OP_OKNOENT);
+ /*
+ * Here, we can only be doing a lookup (not a rename or replace).
+ * If a case-insensitive match was found earlier, return success.
+ */
+ if (args->cmpresult == XFS_CMP_CASE)
+ return 0;
/*
* No match, release the buffer and return ENOENT.
*/
- ASSERT(args->oknoent);
xfs_da_brelse(tp, bp);
return XFS_ERROR(ENOENT);
}
@@ -1033,6 +1046,7 @@ xfs_dir2_sf_to_block(
xfs_dir2_sf_t *sfp; /* shortform structure */
__be16 *tagp; /* end of data entry */
xfs_trans_t *tp; /* transaction pointer */
+ struct xfs_name name;
xfs_dir2_trace_args("sf_to_block", args);
dp = args->dp;
@@ -1071,7 +1085,7 @@ xfs_dir2_sf_to_block(
*/
error = xfs_dir2_grow_inode(args, XFS_DIR2_DATA_SPACE, &blkno);
if (error) {
- kmem_free(buf, buf_len);
+ kmem_free(buf);
return error;
}
/*
@@ -1079,7 +1093,7 @@ xfs_dir2_sf_to_block(
*/
error = xfs_dir2_data_init(args, blkno, &bp);
if (error) {
- kmem_free(buf, buf_len);
+ kmem_free(buf);
return error;
}
block = bp->data;
@@ -1187,8 +1201,10 @@ xfs_dir2_sf_to_block(
tagp = xfs_dir2_data_entry_tag_p(dep);
*tagp = cpu_to_be16((char *)dep - (char *)block);
xfs_dir2_data_log_entry(tp, bp, dep);
- blp[2 + i].hashval = cpu_to_be32(xfs_da_hashname(
- (char *)sfep->name, sfep->namelen));
+ name.name = sfep->name;
+ name.len = sfep->namelen;
+ blp[2 + i].hashval = cpu_to_be32(mp->m_dirnameops->
+ hashname(&name));
blp[2 + i].address = cpu_to_be32(xfs_dir2_byte_to_dataptr(mp,
(char *)dep - (char *)block));
offset = (int)((char *)(tagp + 1) - (char *)block);
@@ -1198,7 +1214,7 @@ xfs_dir2_sf_to_block(
sfep = xfs_dir2_sf_nextentry(sfp, sfep);
}
/* Done with the temporary buffer */
- kmem_free(buf, buf_len);
+ kmem_free(buf);
/*
* Sort the leaf entries by hash value.
*/
diff --git a/fs/xfs/xfs_dir2_data.c b/fs/xfs/xfs_dir2_data.c
index fb8c9e08b23d..498f8d694330 100644
--- a/fs/xfs/xfs_dir2_data.c
+++ b/fs/xfs/xfs_dir2_data.c
@@ -65,6 +65,7 @@ xfs_dir2_data_check(
xfs_mount_t *mp; /* filesystem mount point */
char *p; /* current data position */
int stale; /* count of stale leaves */
+ struct xfs_name name;
mp = dp->i_mount;
d = bp->data;
@@ -140,7 +141,9 @@ xfs_dir2_data_check(
addr = xfs_dir2_db_off_to_dataptr(mp, mp->m_dirdatablk,
(xfs_dir2_data_aoff_t)
((char *)dep - (char *)d));
- hash = xfs_da_hashname((char *)dep->name, dep->namelen);
+ name.name = dep->name;
+ name.len = dep->namelen;
+ hash = mp->m_dirnameops->hashname(&name);
for (i = 0; i < be32_to_cpu(btp->count); i++) {
if (be32_to_cpu(lep[i].address) == addr &&
be32_to_cpu(lep[i].hashval) == hash)
diff --git a/fs/xfs/xfs_dir2_leaf.c b/fs/xfs/xfs_dir2_leaf.c
index bc52b803d79b..f110242d6dfc 100644
--- a/fs/xfs/xfs_dir2_leaf.c
+++ b/fs/xfs/xfs_dir2_leaf.c
@@ -263,20 +263,21 @@ xfs_dir2_leaf_addname(
* If we don't have enough free bytes but we can make enough
* by compacting out stale entries, we'll do that.
*/
- if ((char *)bestsp - (char *)&leaf->ents[be16_to_cpu(leaf->hdr.count)] < needbytes &&
- be16_to_cpu(leaf->hdr.stale) > 1) {
+ if ((char *)bestsp - (char *)&leaf->ents[be16_to_cpu(leaf->hdr.count)] <
+ needbytes && be16_to_cpu(leaf->hdr.stale) > 1) {
compact = 1;
}
/*
* Otherwise if we don't have enough free bytes we need to
* convert to node form.
*/
- else if ((char *)bestsp - (char *)&leaf->ents[be16_to_cpu(leaf->hdr.count)] <
- needbytes) {
+ else if ((char *)bestsp - (char *)&leaf->ents[be16_to_cpu(
+ leaf->hdr.count)] < needbytes) {
/*
* Just checking or no space reservation, give up.
*/
- if (args->justcheck || args->total == 0) {
+ if ((args->op_flags & XFS_DA_OP_JUSTCHECK) ||
+ args->total == 0) {
xfs_da_brelse(tp, lbp);
return XFS_ERROR(ENOSPC);
}
@@ -301,7 +302,7 @@ xfs_dir2_leaf_addname(
* If just checking, then it will fit unless we needed to allocate
* a new data block.
*/
- if (args->justcheck) {
+ if (args->op_flags & XFS_DA_OP_JUSTCHECK) {
xfs_da_brelse(tp, lbp);
return use_block == -1 ? XFS_ERROR(ENOSPC) : 0;
}
@@ -1110,7 +1111,7 @@ xfs_dir2_leaf_getdents(
*offset = XFS_DIR2_MAX_DATAPTR;
else
*offset = xfs_dir2_byte_to_dataptr(mp, curoff);
- kmem_free(map, map_size * sizeof(*map));
+ kmem_free(map);
if (bp)
xfs_da_brelse(NULL, bp);
return error;
@@ -1298,12 +1299,13 @@ xfs_dir2_leaf_lookup(
((char *)dbp->data +
xfs_dir2_dataptr_to_off(dp->i_mount, be32_to_cpu(lep->address)));
/*
- * Return the found inode number.
+ * Return the found inode number & CI name if appropriate
*/
args->inumber = be64_to_cpu(dep->inumber);
+ error = xfs_dir_cilookup_result(args, dep->name, dep->namelen);
xfs_da_brelse(tp, dbp);
xfs_da_brelse(tp, lbp);
- return XFS_ERROR(EEXIST);
+ return XFS_ERROR(error);
}
/*
@@ -1331,6 +1333,8 @@ xfs_dir2_leaf_lookup_int(
xfs_mount_t *mp; /* filesystem mount point */
xfs_dir2_db_t newdb; /* new data block number */
xfs_trans_t *tp; /* transaction pointer */
+ xfs_dabuf_t *cbp; /* case match data buffer */
+ enum xfs_dacmp cmp; /* name compare result */
dp = args->dp;
tp = args->trans;
@@ -1354,9 +1358,11 @@ xfs_dir2_leaf_lookup_int(
* Loop over all the entries with the right hash value
* looking to match the name.
*/
+ cbp = NULL;
for (lep = &leaf->ents[index], dbp = NULL, curdb = -1;
- index < be16_to_cpu(leaf->hdr.count) && be32_to_cpu(lep->hashval) == args->hashval;
- lep++, index++) {
+ index < be16_to_cpu(leaf->hdr.count) &&
+ be32_to_cpu(lep->hashval) == args->hashval;
+ lep++, index++) {
/*
* Skip over stale leaf entries.
*/
@@ -1371,12 +1377,12 @@ xfs_dir2_leaf_lookup_int(
* need to pitch the old one and read the new one.
*/
if (newdb != curdb) {
- if (dbp)
+ if (dbp != cbp)
xfs_da_brelse(tp, dbp);
- if ((error =
- xfs_da_read_buf(tp, dp,
- xfs_dir2_db_to_da(mp, newdb), -1, &dbp,
- XFS_DATA_FORK))) {
+ error = xfs_da_read_buf(tp, dp,
+ xfs_dir2_db_to_da(mp, newdb),
+ -1, &dbp, XFS_DATA_FORK);
+ if (error) {
xfs_da_brelse(tp, lbp);
return error;
}
@@ -1386,24 +1392,46 @@ xfs_dir2_leaf_lookup_int(
/*
* Point to the data entry.
*/
- dep = (xfs_dir2_data_entry_t *)
- ((char *)dbp->data +
- xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
+ dep = (xfs_dir2_data_entry_t *)((char *)dbp->data +
+ xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
/*
- * If it matches then return it.
+ * Compare name and if it's an exact match, return the index
+ * and buffer. If it's the first case-insensitive match, store
+ * the index and buffer and continue looking for an exact match.
*/
- if (dep->namelen == args->namelen &&
- dep->name[0] == args->name[0] &&
- memcmp(dep->name, args->name, args->namelen) == 0) {
- *dbpp = dbp;
+ cmp = mp->m_dirnameops->compname(args, dep->name, dep->namelen);
+ if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
+ args->cmpresult = cmp;
*indexp = index;
- return 0;
+ /*
+ * case exact match: release the stored CI buffer if it
+ * exists and return the current buffer.
+ */
+ if (cmp == XFS_CMP_EXACT) {
+ if (cbp && cbp != dbp)
+ xfs_da_brelse(tp, cbp);
+ *dbpp = dbp;
+ return 0;
+ }
+ cbp = dbp;
}
}
+ ASSERT(args->op_flags & XFS_DA_OP_OKNOENT);
+ /*
+ * Here, we can only be doing a lookup (not a rename or replace).
+ * If a case-insensitive match was found earlier, release the current
+ * buffer and return the stored CI matching buffer.
+ */
+ if (args->cmpresult == XFS_CMP_CASE) {
+ if (cbp != dbp)
+ xfs_da_brelse(tp, dbp);
+ *dbpp = cbp;
+ return 0;
+ }
/*
* No match found, return ENOENT.
*/
- ASSERT(args->oknoent);
+ ASSERT(cbp == NULL);
if (dbp)
xfs_da_brelse(tp, dbp);
xfs_da_brelse(tp, lbp);
diff --git a/fs/xfs/xfs_dir2_node.c b/fs/xfs/xfs_dir2_node.c
index 8dade711f099..1b5430223461 100644
--- a/fs/xfs/xfs_dir2_node.c
+++ b/fs/xfs/xfs_dir2_node.c
@@ -226,7 +226,7 @@ xfs_dir2_leafn_add(
ASSERT(index == be16_to_cpu(leaf->hdr.count) ||
be32_to_cpu(leaf->ents[index].hashval) >= args->hashval);
- if (args->justcheck)
+ if (args->op_flags & XFS_DA_OP_JUSTCHECK)
return 0;
/*
@@ -387,28 +387,26 @@ xfs_dir2_leafn_lasthash(
}
/*
- * Look up a leaf entry in a node-format leaf block.
- * If this is an addname then the extrablk in state is a freespace block,
- * otherwise it's a data block.
+ * Look up a leaf entry for space to add a name in a node-format leaf block.
+ * The extrablk in state is a freespace block.
*/
-int
-xfs_dir2_leafn_lookup_int(
+STATIC int
+xfs_dir2_leafn_lookup_for_addname(
xfs_dabuf_t *bp, /* leaf buffer */
xfs_da_args_t *args, /* operation arguments */
int *indexp, /* out: leaf entry index */
xfs_da_state_t *state) /* state to fill in */
{
- xfs_dabuf_t *curbp; /* current data/free buffer */
- xfs_dir2_db_t curdb; /* current data block number */
- xfs_dir2_db_t curfdb; /* current free block number */
- xfs_dir2_data_entry_t *dep; /* data block entry */
+ xfs_dabuf_t *curbp = NULL; /* current data/free buffer */
+ xfs_dir2_db_t curdb = -1; /* current data block number */
+ xfs_dir2_db_t curfdb = -1; /* current free block number */
xfs_inode_t *dp; /* incore directory inode */
int error; /* error return value */
int fi; /* free entry index */
- xfs_dir2_free_t *free=NULL; /* free block structure */
+ xfs_dir2_free_t *free = NULL; /* free block structure */
int index; /* leaf entry index */
xfs_dir2_leaf_t *leaf; /* leaf structure */
- int length=0; /* length of new data entry */
+ int length; /* length of new data entry */
xfs_dir2_leaf_entry_t *lep; /* leaf entry */
xfs_mount_t *mp; /* filesystem mount point */
xfs_dir2_db_t newdb; /* new data block number */
@@ -431,33 +429,20 @@ xfs_dir2_leafn_lookup_int(
/*
* Do we have a buffer coming in?
*/
- if (state->extravalid)
+ if (state->extravalid) {
+ /* If so, it's a free block buffer, get the block number. */
curbp = state->extrablk.bp;
- else
- curbp = NULL;
- /*
- * For addname, it's a free block buffer, get the block number.
- */
- if (args->addname) {
- curfdb = curbp ? state->extrablk.blkno : -1;
- curdb = -1;
- length = xfs_dir2_data_entsize(args->namelen);
- if ((free = (curbp ? curbp->data : NULL)))
- ASSERT(be32_to_cpu(free->hdr.magic) == XFS_DIR2_FREE_MAGIC);
- }
- /*
- * For others, it's a data block buffer, get the block number.
- */
- else {
- curfdb = -1;
- curdb = curbp ? state->extrablk.blkno : -1;
+ curfdb = state->extrablk.blkno;
+ free = curbp->data;
+ ASSERT(be32_to_cpu(free->hdr.magic) == XFS_DIR2_FREE_MAGIC);
}
+ length = xfs_dir2_data_entsize(args->namelen);
/*
* Loop over leaf entries with the right hash value.
*/
- for (lep = &leaf->ents[index];
- index < be16_to_cpu(leaf->hdr.count) && be32_to_cpu(lep->hashval) == args->hashval;
- lep++, index++) {
+ for (lep = &leaf->ents[index]; index < be16_to_cpu(leaf->hdr.count) &&
+ be32_to_cpu(lep->hashval) == args->hashval;
+ lep++, index++) {
/*
* Skip stale leaf entries.
*/
@@ -471,158 +456,224 @@ xfs_dir2_leafn_lookup_int(
* For addname, we're looking for a place to put the new entry.
* We want to use a data block with an entry of equal
* hash value to ours if there is one with room.
+ *
+ * If this block isn't the data block we already have
+ * in hand, take a look at it.
*/
- if (args->addname) {
+ if (newdb != curdb) {
+ curdb = newdb;
/*
- * If this block isn't the data block we already have
- * in hand, take a look at it.
+ * Convert the data block to the free block
+ * holding its freespace information.
*/
- if (newdb != curdb) {
- curdb = newdb;
- /*
- * Convert the data block to the free block
- * holding its freespace information.
- */
- newfdb = xfs_dir2_db_to_fdb(mp, newdb);
- /*
- * If it's not the one we have in hand,
- * read it in.
- */
- if (newfdb != curfdb) {
- /*
- * If we had one before, drop it.
- */
- if (curbp)
- xfs_da_brelse(tp, curbp);
- /*
- * Read the free block.
- */
- if ((error = xfs_da_read_buf(tp, dp,
- xfs_dir2_db_to_da(mp,
- newfdb),
- -1, &curbp,
- XFS_DATA_FORK))) {
- return error;
- }
- free = curbp->data;
- ASSERT(be32_to_cpu(free->hdr.magic) ==
- XFS_DIR2_FREE_MAGIC);
- ASSERT((be32_to_cpu(free->hdr.firstdb) %
- XFS_DIR2_MAX_FREE_BESTS(mp)) ==
- 0);
- ASSERT(be32_to_cpu(free->hdr.firstdb) <= curdb);
- ASSERT(curdb <
- be32_to_cpu(free->hdr.firstdb) +
- be32_to_cpu(free->hdr.nvalid));
- }
- /*
- * Get the index for our entry.
- */
- fi = xfs_dir2_db_to_fdindex(mp, curdb);
- /*
- * If it has room, return it.
- */
- if (unlikely(be16_to_cpu(free->bests[fi]) == NULLDATAOFF)) {
- XFS_ERROR_REPORT("xfs_dir2_leafn_lookup_int",
- XFS_ERRLEVEL_LOW, mp);
- if (curfdb != newfdb)
- xfs_da_brelse(tp, curbp);
- return XFS_ERROR(EFSCORRUPTED);
- }
- curfdb = newfdb;
- if (be16_to_cpu(free->bests[fi]) >= length) {
- *indexp = index;
- state->extravalid = 1;
- state->extrablk.bp = curbp;
- state->extrablk.blkno = curfdb;
- state->extrablk.index = fi;
- state->extrablk.magic =
- XFS_DIR2_FREE_MAGIC;
- ASSERT(args->oknoent);
- return XFS_ERROR(ENOENT);
- }
- }
- }
- /*
- * Not adding a new entry, so we really want to find
- * the name given to us.
- */
- else {
+ newfdb = xfs_dir2_db_to_fdb(mp, newdb);
/*
- * If it's a different data block, go get it.
+ * If it's not the one we have in hand, read it in.
*/
- if (newdb != curdb) {
+ if (newfdb != curfdb) {
/*
- * If we had a block before, drop it.
+ * If we had one before, drop it.
*/
if (curbp)
xfs_da_brelse(tp, curbp);
/*
- * Read the data block.
+ * Read the free block.
*/
- if ((error =
- xfs_da_read_buf(tp, dp,
- xfs_dir2_db_to_da(mp, newdb), -1,
- &curbp, XFS_DATA_FORK))) {
+ error = xfs_da_read_buf(tp, dp,
+ xfs_dir2_db_to_da(mp, newfdb),
+ -1, &curbp, XFS_DATA_FORK);
+ if (error)
return error;
- }
- xfs_dir2_data_check(dp, curbp);
- curdb = newdb;
+ free = curbp->data;
+ ASSERT(be32_to_cpu(free->hdr.magic) ==
+ XFS_DIR2_FREE_MAGIC);
+ ASSERT((be32_to_cpu(free->hdr.firstdb) %
+ XFS_DIR2_MAX_FREE_BESTS(mp)) == 0);
+ ASSERT(be32_to_cpu(free->hdr.firstdb) <= curdb);
+ ASSERT(curdb < be32_to_cpu(free->hdr.firstdb) +
+ be32_to_cpu(free->hdr.nvalid));
}
/*
- * Point to the data entry.
+ * Get the index for our entry.
*/
- dep = (xfs_dir2_data_entry_t *)
- ((char *)curbp->data +
- xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
+ fi = xfs_dir2_db_to_fdindex(mp, curdb);
/*
- * Compare the entry, return it if it matches.
+ * If it has room, return it.
*/
- if (dep->namelen == args->namelen &&
- dep->name[0] == args->name[0] &&
- memcmp(dep->name, args->name, args->namelen) == 0) {
- args->inumber = be64_to_cpu(dep->inumber);
- *indexp = index;
- state->extravalid = 1;
- state->extrablk.bp = curbp;
- state->extrablk.blkno = curdb;
- state->extrablk.index =
- (int)((char *)dep -
- (char *)curbp->data);
- state->extrablk.magic = XFS_DIR2_DATA_MAGIC;
- return XFS_ERROR(EEXIST);
+ if (unlikely(be16_to_cpu(free->bests[fi]) == NULLDATAOFF)) {
+ XFS_ERROR_REPORT("xfs_dir2_leafn_lookup_int",
+ XFS_ERRLEVEL_LOW, mp);
+ if (curfdb != newfdb)
+ xfs_da_brelse(tp, curbp);
+ return XFS_ERROR(EFSCORRUPTED);
}
+ curfdb = newfdb;
+ if (be16_to_cpu(free->bests[fi]) >= length)
+ goto out;
}
}
+ /* Didn't find any space */
+ fi = -1;
+out:
+ ASSERT(args->op_flags & XFS_DA_OP_OKNOENT);
+ if (curbp) {
+ /* Giving back a free block. */
+ state->extravalid = 1;
+ state->extrablk.bp = curbp;
+ state->extrablk.index = fi;
+ state->extrablk.blkno = curfdb;
+ state->extrablk.magic = XFS_DIR2_FREE_MAGIC;
+ } else {
+ state->extravalid = 0;
+ }
/*
- * Didn't find a match.
- * If we are holding a buffer, give it back in case our caller
- * finds it useful.
+ * Return the index, that will be the insertion point.
*/
- if ((state->extravalid = (curbp != NULL))) {
- state->extrablk.bp = curbp;
- state->extrablk.index = -1;
+ *indexp = index;
+ return XFS_ERROR(ENOENT);
+}
+
+/*
+ * Look up a leaf entry in a node-format leaf block.
+ * The extrablk in state a data block.
+ */
+STATIC int
+xfs_dir2_leafn_lookup_for_entry(
+ xfs_dabuf_t *bp, /* leaf buffer */
+ xfs_da_args_t *args, /* operation arguments */
+ int *indexp, /* out: leaf entry index */
+ xfs_da_state_t *state) /* state to fill in */
+{
+ xfs_dabuf_t *curbp = NULL; /* current data/free buffer */
+ xfs_dir2_db_t curdb = -1; /* current data block number */
+ xfs_dir2_data_entry_t *dep; /* data block entry */
+ xfs_inode_t *dp; /* incore directory inode */
+ int error; /* error return value */
+ int di = -1; /* data entry index */
+ int index; /* leaf entry index */
+ xfs_dir2_leaf_t *leaf; /* leaf structure */
+ xfs_dir2_leaf_entry_t *lep; /* leaf entry */
+ xfs_mount_t *mp; /* filesystem mount point */
+ xfs_dir2_db_t newdb; /* new data block number */
+ xfs_trans_t *tp; /* transaction pointer */
+ enum xfs_dacmp cmp; /* comparison result */
+
+ dp = args->dp;
+ tp = args->trans;
+ mp = dp->i_mount;
+ leaf = bp->data;
+ ASSERT(be16_to_cpu(leaf->hdr.info.magic) == XFS_DIR2_LEAFN_MAGIC);
+#ifdef __KERNEL__
+ ASSERT(be16_to_cpu(leaf->hdr.count) > 0);
+#endif
+ xfs_dir2_leafn_check(dp, bp);
+ /*
+ * Look up the hash value in the leaf entries.
+ */
+ index = xfs_dir2_leaf_search_hash(args, bp);
+ /*
+ * Do we have a buffer coming in?
+ */
+ if (state->extravalid) {
+ curbp = state->extrablk.bp;
+ curdb = state->extrablk.blkno;
+ di = state->extrablk.index;
+ }
+ /*
+ * Loop over leaf entries with the right hash value.
+ */
+ for (lep = &leaf->ents[index]; index < be16_to_cpu(leaf->hdr.count) &&
+ be32_to_cpu(lep->hashval) == args->hashval;
+ lep++, index++) {
/*
- * For addname, giving back a free block.
+ * Skip stale leaf entries.
+ */
+ if (be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR)
+ continue;
+ /*
+ * Pull the data block number from the entry.
+ */
+ newdb = xfs_dir2_dataptr_to_db(mp, be32_to_cpu(lep->address));
+ /*
+ * Not adding a new entry, so we really want to find
+ * the name given to us.
+ *
+ * If it's a different data block, go get it.
*/
- if (args->addname) {
- state->extrablk.blkno = curfdb;
- state->extrablk.magic = XFS_DIR2_FREE_MAGIC;
+ if (newdb != curdb) {
+ /*
+ * If we had a block before, drop it.
+ */
+ if (curbp)
+ xfs_da_brelse(tp, curbp);
+ /*
+ * Read the data block.
+ */
+ error = xfs_da_read_buf(tp, dp, xfs_dir2_db_to_da(mp,
+ newdb), -1, &curbp, XFS_DATA_FORK);
+ if (error)
+ return error;
+ xfs_dir2_data_check(dp, curbp);
+ curdb = newdb;
}
/*
- * For other callers, giving back a data block.
+ * Point to the data entry.
*/
- else {
- state->extrablk.blkno = curdb;
- state->extrablk.magic = XFS_DIR2_DATA_MAGIC;
+ dep = (xfs_dir2_data_entry_t *)((char *)curbp->data +
+ xfs_dir2_dataptr_to_off(mp, be32_to_cpu(lep->address)));
+ /*
+ * Compare the entry and if it's an exact match, return
+ * EEXIST immediately. If it's the first case-insensitive
+ * match, store the inode number and continue looking.
+ */
+ cmp = mp->m_dirnameops->compname(args, dep->name, dep->namelen);
+ if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
+ args->cmpresult = cmp;
+ args->inumber = be64_to_cpu(dep->inumber);
+ di = (int)((char *)dep - (char *)curbp->data);
+ error = EEXIST;
+ if (cmp == XFS_CMP_EXACT)
+ goto out;
}
}
+ /* Didn't find an exact match. */
+ error = ENOENT;
+ ASSERT(index == be16_to_cpu(leaf->hdr.count) ||
+ (args->op_flags & XFS_DA_OP_OKNOENT));
+out:
+ if (curbp) {
+ /* Giving back a data block. */
+ state->extravalid = 1;
+ state->extrablk.bp = curbp;
+ state->extrablk.index = di;
+ state->extrablk.blkno = curdb;
+ state->extrablk.magic = XFS_DIR2_DATA_MAGIC;
+ } else {
+ state->extravalid = 0;
+ }
/*
- * Return the final index, that will be the insertion point.
+ * Return the index, that will be the deletion point for remove/replace.
*/
*indexp = index;
- ASSERT(index == be16_to_cpu(leaf->hdr.count) || args->oknoent);
- return XFS_ERROR(ENOENT);
+ return XFS_ERROR(error);
+}
+
+/*
+ * Look up a leaf entry in a node-format leaf block.
+ * If this is an addname then the extrablk in state is a freespace block,
+ * otherwise it's a data block.
+ */
+int
+xfs_dir2_leafn_lookup_int(
+ xfs_dabuf_t *bp, /* leaf buffer */
+ xfs_da_args_t *args, /* operation arguments */
+ int *indexp, /* out: leaf entry index */
+ xfs_da_state_t *state) /* state to fill in */
+{
+ if (args->op_flags & XFS_DA_OP_ADDNAME)
+ return xfs_dir2_leafn_lookup_for_addname(bp, args, indexp,
+ state);
+ return xfs_dir2_leafn_lookup_for_entry(bp, args, indexp, state);
}
/*
@@ -823,9 +874,10 @@ xfs_dir2_leafn_rebalance(
*/
if (!state->inleaf)
blk2->index = blk1->index - be16_to_cpu(leaf1->hdr.count);
-
- /*
- * Finally sanity check just to make sure we are not returning a negative index
+
+ /*
+ * Finally sanity check just to make sure we are not returning a
+ * negative index
*/
if(blk2->index < 0) {
state->inleaf = 1;
@@ -1332,7 +1384,7 @@ xfs_dir2_node_addname(
/*
* It worked, fix the hash values up the btree.
*/
- if (!args->justcheck)
+ if (!(args->op_flags & XFS_DA_OP_JUSTCHECK))
xfs_da_fixhashpath(state, &state->path);
} else {
/*
@@ -1515,7 +1567,8 @@ xfs_dir2_node_addname_int(
/*
* Not allowed to allocate, return failure.
*/
- if (args->justcheck || args->total == 0) {
+ if ((args->op_flags & XFS_DA_OP_JUSTCHECK) ||
+ args->total == 0) {
/*
* Drop the freespace buffer unless it came from our
* caller.
@@ -1661,7 +1714,7 @@ xfs_dir2_node_addname_int(
/*
* If just checking, we succeeded.
*/
- if (args->justcheck) {
+ if (args->op_flags & XFS_DA_OP_JUSTCHECK) {
if ((fblk == NULL || fblk->bp == NULL) && fbp != NULL)
xfs_da_buf_done(fbp);
return 0;
@@ -1767,6 +1820,14 @@ xfs_dir2_node_lookup(
error = xfs_da_node_lookup_int(state, &rval);
if (error)
rval = error;
+ else if (rval == ENOENT && args->cmpresult == XFS_CMP_CASE) {
+ /* If a CI match, dup the actual name and return EEXIST */
+ xfs_dir2_data_entry_t *dep;
+
+ dep = (xfs_dir2_data_entry_t *)((char *)state->extrablk.bp->
+ data + state->extrablk.index);
+ rval = xfs_dir_cilookup_result(args, dep->name, dep->namelen);
+ }
/*
* Release the btree blocks and leaf block.
*/
@@ -1810,9 +1871,8 @@ xfs_dir2_node_removename(
* Look up the entry we're deleting, set up the cursor.
*/
error = xfs_da_node_lookup_int(state, &rval);
- if (error) {
+ if (error)
rval = error;
- }
/*
* Didn't find it, upper layer screwed up.
*/
@@ -1829,9 +1889,8 @@ xfs_dir2_node_removename(
*/
error = xfs_dir2_leafn_remove(args, blk->bp, blk->index,
&state->extrablk, &rval);
- if (error) {
+ if (error)
return error;
- }
/*
* Fix the hash values up the btree.
*/
diff --git a/fs/xfs/xfs_dir2_sf.c b/fs/xfs/xfs_dir2_sf.c
index 919d275a1cef..b46af0013ec9 100644
--- a/fs/xfs/xfs_dir2_sf.c
+++ b/fs/xfs/xfs_dir2_sf.c
@@ -255,7 +255,7 @@ xfs_dir2_block_to_sf(
xfs_dir2_sf_check(args);
out:
xfs_trans_log_inode(args->trans, dp, logflags);
- kmem_free(block, mp->m_dirblksize);
+ kmem_free(block);
return error;
}
@@ -332,7 +332,7 @@ xfs_dir2_sf_addname(
/*
* Just checking or no space reservation, it doesn't fit.
*/
- if (args->justcheck || args->total == 0)
+ if ((args->op_flags & XFS_DA_OP_JUSTCHECK) || args->total == 0)
return XFS_ERROR(ENOSPC);
/*
* Convert to block form then add the name.
@@ -345,7 +345,7 @@ xfs_dir2_sf_addname(
/*
* Just checking, it fits.
*/
- if (args->justcheck)
+ if (args->op_flags & XFS_DA_OP_JUSTCHECK)
return 0;
/*
* Do it the easy way - just add it at the end.
@@ -512,7 +512,7 @@ xfs_dir2_sf_addname_hard(
sfep = xfs_dir2_sf_nextentry(sfp, sfep);
memcpy(sfep, oldsfep, old_isize - nbytes);
}
- kmem_free(buf, old_isize);
+ kmem_free(buf);
dp->i_d.di_size = new_isize;
xfs_dir2_sf_check(args);
}
@@ -812,8 +812,11 @@ xfs_dir2_sf_lookup(
{
xfs_inode_t *dp; /* incore directory inode */
int i; /* entry index */
+ int error;
xfs_dir2_sf_entry_t *sfep; /* shortform directory entry */
xfs_dir2_sf_t *sfp; /* shortform structure */
+ enum xfs_dacmp cmp; /* comparison result */
+ xfs_dir2_sf_entry_t *ci_sfep; /* case-insens. entry */
xfs_dir2_trace_args("sf_lookup", args);
xfs_dir2_sf_check(args);
@@ -836,6 +839,7 @@ xfs_dir2_sf_lookup(
*/
if (args->namelen == 1 && args->name[0] == '.') {
args->inumber = dp->i_ino;
+ args->cmpresult = XFS_CMP_EXACT;
return XFS_ERROR(EEXIST);
}
/*
@@ -844,28 +848,41 @@ xfs_dir2_sf_lookup(
if (args->namelen == 2 &&
args->name[0] == '.' && args->name[1] == '.') {
args->inumber = xfs_dir2_sf_get_inumber(sfp, &sfp->hdr.parent);
+ args->cmpresult = XFS_CMP_EXACT;
return XFS_ERROR(EEXIST);
}
/*
* Loop over all the entries trying to match ours.
*/
- for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp);
- i < sfp->hdr.count;
- i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
- if (sfep->namelen == args->namelen &&
- sfep->name[0] == args->name[0] &&
- memcmp(args->name, sfep->name, args->namelen) == 0) {
- args->inumber =
- xfs_dir2_sf_get_inumber(sfp,
- xfs_dir2_sf_inumberp(sfep));
- return XFS_ERROR(EEXIST);
+ ci_sfep = NULL;
+ for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->hdr.count;
+ i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
+ /*
+ * Compare name and if it's an exact match, return the inode
+ * number. If it's the first case-insensitive match, store the
+ * inode number and continue looking for an exact match.
+ */
+ cmp = dp->i_mount->m_dirnameops->compname(args, sfep->name,
+ sfep->namelen);
+ if (cmp != XFS_CMP_DIFFERENT && cmp != args->cmpresult) {
+ args->cmpresult = cmp;
+ args->inumber = xfs_dir2_sf_get_inumber(sfp,
+ xfs_dir2_sf_inumberp(sfep));
+ if (cmp == XFS_CMP_EXACT)
+ return XFS_ERROR(EEXIST);
+ ci_sfep = sfep;
}
}
+ ASSERT(args->op_flags & XFS_DA_OP_OKNOENT);
/*
- * Didn't find it.
+ * Here, we can only be doing a lookup (not a rename or replace).
+ * If a case-insensitive match was not found, return ENOENT.
*/
- ASSERT(args->oknoent);
- return XFS_ERROR(ENOENT);
+ if (!ci_sfep)
+ return XFS_ERROR(ENOENT);
+ /* otherwise process the CI match as required by the caller */
+ error = xfs_dir_cilookup_result(args, ci_sfep->name, ci_sfep->namelen);
+ return XFS_ERROR(error);
}
/*
@@ -904,24 +921,21 @@ xfs_dir2_sf_removename(
* Loop over the old directory entries.
* Find the one we're deleting.
*/
- for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp);
- i < sfp->hdr.count;
- i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
- if (sfep->namelen == args->namelen &&
- sfep->name[0] == args->name[0] &&
- memcmp(sfep->name, args->name, args->namelen) == 0) {
+ for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp); i < sfp->hdr.count;
+ i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
+ if (xfs_da_compname(args, sfep->name, sfep->namelen) ==
+ XFS_CMP_EXACT) {
ASSERT(xfs_dir2_sf_get_inumber(sfp,
- xfs_dir2_sf_inumberp(sfep)) ==
- args->inumber);
+ xfs_dir2_sf_inumberp(sfep)) ==
+ args->inumber);
break;
}
}
/*
* Didn't find it.
*/
- if (i == sfp->hdr.count) {
+ if (i == sfp->hdr.count)
return XFS_ERROR(ENOENT);
- }
/*
* Calculate sizes.
*/
@@ -1042,11 +1056,10 @@ xfs_dir2_sf_replace(
*/
else {
for (i = 0, sfep = xfs_dir2_sf_firstentry(sfp);
- i < sfp->hdr.count;
- i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
- if (sfep->namelen == args->namelen &&
- sfep->name[0] == args->name[0] &&
- memcmp(args->name, sfep->name, args->namelen) == 0) {
+ i < sfp->hdr.count;
+ i++, sfep = xfs_dir2_sf_nextentry(sfp, sfep)) {
+ if (xfs_da_compname(args, sfep->name, sfep->namelen) ==
+ XFS_CMP_EXACT) {
#if XFS_BIG_INUMS || defined(DEBUG)
ino = xfs_dir2_sf_get_inumber(sfp,
xfs_dir2_sf_inumberp(sfep));
@@ -1061,7 +1074,7 @@ xfs_dir2_sf_replace(
* Didn't find it.
*/
if (i == sfp->hdr.count) {
- ASSERT(args->oknoent);
+ ASSERT(args->op_flags & XFS_DA_OP_OKNOENT);
#if XFS_BIG_INUMS
if (i8elevated)
xfs_dir2_sf_toino4(args);
@@ -1174,7 +1187,7 @@ xfs_dir2_sf_toino4(
/*
* Clean up the inode.
*/
- kmem_free(buf, oldsize);
+ kmem_free(buf);
dp->i_d.di_size = newsize;
xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
}
@@ -1251,7 +1264,7 @@ xfs_dir2_sf_toino8(
/*
* Clean up the inode.
*/
- kmem_free(buf, oldsize);
+ kmem_free(buf);
dp->i_d.di_size = newsize;
xfs_trans_log_inode(args->trans, dp, XFS_ILOG_CORE | XFS_ILOG_DDATA);
}
diff --git a/fs/xfs/xfs_dir2_trace.c b/fs/xfs/xfs_dir2_trace.c
index f3fb2ffd6f5c..6cc7c0c681ac 100644
--- a/fs/xfs/xfs_dir2_trace.c
+++ b/fs/xfs/xfs_dir2_trace.c
@@ -85,7 +85,8 @@ xfs_dir2_trace_args(
(void *)((unsigned long)(args->inumber >> 32)),
(void *)((unsigned long)(args->inumber & 0xFFFFFFFF)),
(void *)args->dp, (void *)args->trans,
- (void *)(unsigned long)args->justcheck, NULL, NULL);
+ (void *)(unsigned long)(args->op_flags & XFS_DA_OP_JUSTCHECK),
+ NULL, NULL);
}
void
@@ -100,7 +101,7 @@ xfs_dir2_trace_args_b(
(void *)((unsigned long)(args->inumber >> 32)),
(void *)((unsigned long)(args->inumber & 0xFFFFFFFF)),
(void *)args->dp, (void *)args->trans,
- (void *)(unsigned long)args->justcheck,
+ (void *)(unsigned long)(args->op_flags & XFS_DA_OP_JUSTCHECK),
(void *)(bp ? bp->bps[0] : NULL), NULL);
}
@@ -117,7 +118,7 @@ xfs_dir2_trace_args_bb(
(void *)((unsigned long)(args->inumber >> 32)),
(void *)((unsigned long)(args->inumber & 0xFFFFFFFF)),
(void *)args->dp, (void *)args->trans,
- (void *)(unsigned long)args->justcheck,
+ (void *)(unsigned long)(args->op_flags & XFS_DA_OP_JUSTCHECK),
(void *)(lbp ? lbp->bps[0] : NULL),
(void *)(dbp ? dbp->bps[0] : NULL));
}
@@ -157,8 +158,8 @@ xfs_dir2_trace_args_db(
(void *)((unsigned long)(args->inumber >> 32)),
(void *)((unsigned long)(args->inumber & 0xFFFFFFFF)),
(void *)args->dp, (void *)args->trans,
- (void *)(unsigned long)args->justcheck, (void *)(long)db,
- (void *)dbp);
+ (void *)(unsigned long)(args->op_flags & XFS_DA_OP_JUSTCHECK),
+ (void *)(long)db, (void *)dbp);
}
void
@@ -173,7 +174,7 @@ xfs_dir2_trace_args_i(
(void *)((unsigned long)(args->inumber >> 32)),
(void *)((unsigned long)(args->inumber & 0xFFFFFFFF)),
(void *)args->dp, (void *)args->trans,
- (void *)(unsigned long)args->justcheck,
+ (void *)(unsigned long)(args->op_flags & XFS_DA_OP_JUSTCHECK),
(void *)((unsigned long)(i >> 32)),
(void *)((unsigned long)(i & 0xFFFFFFFF)));
}
@@ -190,7 +191,8 @@ xfs_dir2_trace_args_s(
(void *)((unsigned long)(args->inumber >> 32)),
(void *)((unsigned long)(args->inumber & 0xFFFFFFFF)),
(void *)args->dp, (void *)args->trans,
- (void *)(unsigned long)args->justcheck, (void *)(long)s, NULL);
+ (void *)(unsigned long)(args->op_flags & XFS_DA_OP_JUSTCHECK),
+ (void *)(long)s, NULL);
}
void
@@ -208,7 +210,7 @@ xfs_dir2_trace_args_sb(
(void *)((unsigned long)(args->inumber >> 32)),
(void *)((unsigned long)(args->inumber & 0xFFFFFFFF)),
(void *)args->dp, (void *)args->trans,
- (void *)(unsigned long)args->justcheck, (void *)(long)s,
- (void *)dbp);
+ (void *)(unsigned long)(args->op_flags & XFS_DA_OP_JUSTCHECK),
+ (void *)(long)s, (void *)dbp);
}
#endif /* XFS_DIR2_TRACE */
diff --git a/fs/xfs/xfs_error.c b/fs/xfs/xfs_error.c
index 05e5365d3c31..7380a00644c8 100644
--- a/fs/xfs/xfs_error.c
+++ b/fs/xfs/xfs_error.c
@@ -150,8 +150,7 @@ xfs_errortag_clearall(xfs_mount_t *mp, int loud)
xfs_etest[i]);
xfs_etest[i] = 0;
xfs_etest_fsid[i] = 0LL;
- kmem_free(xfs_etest_fsname[i],
- strlen(xfs_etest_fsname[i]) + 1);
+ kmem_free(xfs_etest_fsname[i]);
xfs_etest_fsname[i] = NULL;
}
}
@@ -175,7 +174,7 @@ xfs_fs_vcmn_err(int level, xfs_mount_t *mp, char *fmt, va_list ap)
newfmt = kmem_alloc(len, KM_SLEEP);
sprintf(newfmt, "Filesystem \"%s\": %s", mp->m_fsname, fmt);
icmn_err(level, newfmt, ap);
- kmem_free(newfmt, len);
+ kmem_free(newfmt);
} else {
icmn_err(level, fmt, ap);
}
diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c
index 132bd07b9bb8..8aa28f751b2a 100644
--- a/fs/xfs/xfs_extfree_item.c
+++ b/fs/xfs/xfs_extfree_item.c
@@ -41,8 +41,7 @@ xfs_efi_item_free(xfs_efi_log_item_t *efip)
int nexts = efip->efi_format.efi_nextents;
if (nexts > XFS_EFI_MAX_FAST_EXTENTS) {
- kmem_free(efip, sizeof(xfs_efi_log_item_t) +
- (nexts - 1) * sizeof(xfs_extent_t));
+ kmem_free(efip);
} else {
kmem_zone_free(xfs_efi_zone, efip);
}
@@ -374,8 +373,7 @@ xfs_efd_item_free(xfs_efd_log_item_t *efdp)
int nexts = efdp->efd_format.efd_nextents;
if (nexts > XFS_EFD_MAX_FAST_EXTENTS) {
- kmem_free(efdp, sizeof(xfs_efd_log_item_t) +
- (nexts - 1) * sizeof(xfs_extent_t));
+ kmem_free(efdp);
} else {
kmem_zone_free(xfs_efd_zone, efdp);
}
diff --git a/fs/xfs/xfs_fs.h b/fs/xfs/xfs_fs.h
index 3bed6433d050..6ca749897c58 100644
--- a/fs/xfs/xfs_fs.h
+++ b/fs/xfs/xfs_fs.h
@@ -239,6 +239,7 @@ typedef struct xfs_fsop_resblks {
#define XFS_FSOP_GEOM_FLAGS_LOGV2 0x0100 /* log format version 2 */
#define XFS_FSOP_GEOM_FLAGS_SECTOR 0x0200 /* sector sizes >1BB */
#define XFS_FSOP_GEOM_FLAGS_ATTR2 0x0400 /* inline attributes rework */
+#define XFS_FSOP_GEOM_FLAGS_DIRV2CI 0x1000 /* ASCII only CI names */
#define XFS_FSOP_GEOM_FLAGS_LAZYSB 0x4000 /* lazy superblock counters */
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index 381ebda4f7bc..84583cf73db3 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -95,6 +95,8 @@ xfs_fs_geometry(
XFS_FSOP_GEOM_FLAGS_DIRV2 : 0) |
(xfs_sb_version_hassector(&mp->m_sb) ?
XFS_FSOP_GEOM_FLAGS_SECTOR : 0) |
+ (xfs_sb_version_hasasciici(&mp->m_sb) ?
+ XFS_FSOP_GEOM_FLAGS_DIRV2CI : 0) |
(xfs_sb_version_haslazysbcount(&mp->m_sb) ?
XFS_FSOP_GEOM_FLAGS_LAZYSB : 0) |
(xfs_sb_version_hasattr2(&mp->m_sb) ?
@@ -625,7 +627,7 @@ xfs_fs_goingdown(
xfs_force_shutdown(mp, SHUTDOWN_FORCE_UMOUNT);
thaw_bdev(sb->s_bdev, sb);
}
-
+
break;
}
case XFS_FSOP_GOING_FLAGS_LOGFLUSH:
diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
index e569bf5d6cf0..199a36ac8e2d 100644
--- a/fs/xfs/xfs_inode.c
+++ b/fs/xfs/xfs_inode.c
@@ -1763,67 +1763,6 @@ xfs_itruncate_finish(
return 0;
}
-
-/*
- * xfs_igrow_start
- *
- * Do the first part of growing a file: zero any data in the last
- * block that is beyond the old EOF. We need to do this before
- * the inode is joined to the transaction to modify the i_size.
- * That way we can drop the inode lock and call into the buffer
- * cache to get the buffer mapping the EOF.
- */
-int
-xfs_igrow_start(
- xfs_inode_t *ip,
- xfs_fsize_t new_size,
- cred_t *credp)
-{
- ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_IOLOCK_EXCL));
- ASSERT(new_size > ip->i_size);
-
- /*
- * Zero any pages that may have been created by
- * xfs_write_file() beyond the end of the file
- * and any blocks between the old and new file sizes.
- */
- return xfs_zero_eof(ip, new_size, ip->i_size);
-}
-
-/*
- * xfs_igrow_finish
- *
- * This routine is called to extend the size of a file.
- * The inode must have both the iolock and the ilock locked
- * for update and it must be a part of the current transaction.
- * The xfs_igrow_start() function must have been called previously.
- * If the change_flag is not zero, the inode change timestamp will
- * be updated.
- */
-void
-xfs_igrow_finish(
- xfs_trans_t *tp,
- xfs_inode_t *ip,
- xfs_fsize_t new_size,
- int change_flag)
-{
- ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL|XFS_IOLOCK_EXCL));
- ASSERT(ip->i_transp == tp);
- ASSERT(new_size > ip->i_size);
-
- /*
- * Update the file size. Update the inode change timestamp
- * if change_flag set.
- */
- ip->i_d.di_size = new_size;
- ip->i_size = new_size;
- if (change_flag)
- xfs_ichgtime(ip, XFS_ICHGTIME_CHG);
- xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
-
-}
-
-
/*
* This is called when the inode's link count goes to 0.
* We place the on-disk inode on a list in the AGI. It
@@ -2258,7 +2197,7 @@ xfs_ifree_cluster(
xfs_trans_binval(tp, bp);
}
- kmem_free(ip_found, ninodes * sizeof(xfs_inode_t *));
+ kmem_free(ip_found);
xfs_put_perag(mp, pag);
}
@@ -2470,7 +2409,7 @@ xfs_iroot_realloc(
(int)new_size);
memcpy(np, op, new_max * (uint)sizeof(xfs_dfsbno_t));
}
- kmem_free(ifp->if_broot, ifp->if_broot_bytes);
+ kmem_free(ifp->if_broot);
ifp->if_broot = new_broot;
ifp->if_broot_bytes = (int)new_size;
ASSERT(ifp->if_broot_bytes <=
@@ -2514,7 +2453,7 @@ xfs_idata_realloc(
if (new_size == 0) {
if (ifp->if_u1.if_data != ifp->if_u2.if_inline_data) {
- kmem_free(ifp->if_u1.if_data, ifp->if_real_bytes);
+ kmem_free(ifp->if_u1.if_data);
}
ifp->if_u1.if_data = NULL;
real_size = 0;
@@ -2529,7 +2468,7 @@ xfs_idata_realloc(
ASSERT(ifp->if_real_bytes != 0);
memcpy(ifp->if_u2.if_inline_data, ifp->if_u1.if_data,
new_size);
- kmem_free(ifp->if_u1.if_data, ifp->if_real_bytes);
+ kmem_free(ifp->if_u1.if_data);
ifp->if_u1.if_data = ifp->if_u2.if_inline_data;
}
real_size = 0;
@@ -2636,7 +2575,7 @@ xfs_idestroy_fork(
ifp = XFS_IFORK_PTR(ip, whichfork);
if (ifp->if_broot != NULL) {
- kmem_free(ifp->if_broot, ifp->if_broot_bytes);
+ kmem_free(ifp->if_broot);
ifp->if_broot = NULL;
}
@@ -2650,7 +2589,7 @@ xfs_idestroy_fork(
if ((ifp->if_u1.if_data != ifp->if_u2.if_inline_data) &&
(ifp->if_u1.if_data != NULL)) {
ASSERT(ifp->if_real_bytes != 0);
- kmem_free(ifp->if_u1.if_data, ifp->if_real_bytes);
+ kmem_free(ifp->if_u1.if_data);
ifp->if_u1.if_data = NULL;
ifp->if_real_bytes = 0;
}
@@ -3058,7 +2997,7 @@ xfs_iflush_cluster(
out_free:
read_unlock(&pag->pag_ici_lock);
- kmem_free(ilist, ilist_size);
+ kmem_free(ilist);
return 0;
@@ -3102,7 +3041,7 @@ cluster_corrupt_out:
* Unlocks the flush lock
*/
xfs_iflush_abort(iq);
- kmem_free(ilist, ilist_size);
+ kmem_free(ilist);
return XFS_ERROR(EFSCORRUPTED);
}
@@ -3836,7 +3775,7 @@ xfs_iext_add_indirect_multi(
erp = xfs_iext_irec_new(ifp, erp_idx);
}
memmove(&erp->er_extbuf[i], nex2_ep, byte_diff);
- kmem_free(nex2_ep, byte_diff);
+ kmem_free(nex2_ep);
erp->er_extcount += nex2;
xfs_iext_irec_update_extoffs(ifp, erp_idx + 1, nex2);
}
@@ -4112,7 +4051,7 @@ xfs_iext_direct_to_inline(
*/
memcpy(ifp->if_u2.if_inline_ext, ifp->if_u1.if_extents,
nextents * sizeof(xfs_bmbt_rec_t));
- kmem_free(ifp->if_u1.if_extents, ifp->if_real_bytes);
+ kmem_free(ifp->if_u1.if_extents);
ifp->if_u1.if_extents = ifp->if_u2.if_inline_ext;
ifp->if_real_bytes = 0;
}
@@ -4186,7 +4125,7 @@ xfs_iext_indirect_to_direct(
ASSERT(ifp->if_real_bytes == XFS_IEXT_BUFSZ);
ep = ifp->if_u1.if_ext_irec->er_extbuf;
- kmem_free(ifp->if_u1.if_ext_irec, sizeof(xfs_ext_irec_t));
+ kmem_free(ifp->if_u1.if_ext_irec);
ifp->if_flags &= ~XFS_IFEXTIREC;
ifp->if_u1.if_extents = ep;
ifp->if_bytes = size;
@@ -4212,7 +4151,7 @@ xfs_iext_destroy(
}
ifp->if_flags &= ~XFS_IFEXTIREC;
} else if (ifp->if_real_bytes) {
- kmem_free(ifp->if_u1.if_extents, ifp->if_real_bytes);
+ kmem_free(ifp->if_u1.if_extents);
} else if (ifp->if_bytes) {
memset(ifp->if_u2.if_inline_ext, 0, XFS_INLINE_EXTS *
sizeof(xfs_bmbt_rec_t));
@@ -4483,7 +4422,7 @@ xfs_iext_irec_remove(
if (erp->er_extbuf) {
xfs_iext_irec_update_extoffs(ifp, erp_idx + 1,
-erp->er_extcount);
- kmem_free(erp->er_extbuf, XFS_IEXT_BUFSZ);
+ kmem_free(erp->er_extbuf);
}
/* Compact extent records */
erp = ifp->if_u1.if_ext_irec;
@@ -4501,8 +4440,7 @@ xfs_iext_irec_remove(
xfs_iext_realloc_indirect(ifp,
nlists * sizeof(xfs_ext_irec_t));
} else {
- kmem_free(ifp->if_u1.if_ext_irec,
- sizeof(xfs_ext_irec_t));
+ kmem_free(ifp->if_u1.if_ext_irec);
}
ifp->if_real_bytes = nlists * XFS_IEXT_BUFSZ;
}
@@ -4571,7 +4509,7 @@ xfs_iext_irec_compact_pages(
* so er_extoffs don't get modified in
* xfs_iext_irec_remove.
*/
- kmem_free(erp_next->er_extbuf, XFS_IEXT_BUFSZ);
+ kmem_free(erp_next->er_extbuf);
erp_next->er_extbuf = NULL;
xfs_iext_irec_remove(ifp, erp_idx + 1);
nlists = ifp->if_real_bytes / XFS_IEXT_BUFSZ;
@@ -4614,8 +4552,7 @@ xfs_iext_irec_compact_full(
* so er_extoffs don't get modified in
* xfs_iext_irec_remove.
*/
- kmem_free(erp_next->er_extbuf,
- erp_next->er_extcount * sizeof(xfs_bmbt_rec_t));
+ kmem_free(erp_next->er_extbuf);
erp_next->er_extbuf = NULL;
xfs_iext_irec_remove(ifp, erp_idx + 1);
erp = &ifp->if_u1.if_ext_irec[erp_idx];
diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h
index 0a999fee4f03..17a04b6321ed 100644
--- a/fs/xfs/xfs_inode.h
+++ b/fs/xfs/xfs_inode.h
@@ -507,9 +507,6 @@ int xfs_itruncate_start(xfs_inode_t *, uint, xfs_fsize_t);
int xfs_itruncate_finish(struct xfs_trans **, xfs_inode_t *,
xfs_fsize_t, int, int);
int xfs_iunlink(struct xfs_trans *, xfs_inode_t *);
-int xfs_igrow_start(xfs_inode_t *, xfs_fsize_t, struct cred *);
-void xfs_igrow_finish(struct xfs_trans *, xfs_inode_t *,
- xfs_fsize_t, int);
void xfs_idestroy_fork(xfs_inode_t *, int);
void xfs_idestroy(xfs_inode_t *);
diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c
index 167b33f15772..0eee08a32c26 100644
--- a/fs/xfs/xfs_inode_item.c
+++ b/fs/xfs/xfs_inode_item.c
@@ -686,7 +686,7 @@ xfs_inode_item_unlock(
ASSERT(ip->i_d.di_nextents > 0);
ASSERT(iip->ili_format.ilf_fields & XFS_ILOG_DEXT);
ASSERT(ip->i_df.if_bytes > 0);
- kmem_free(iip->ili_extents_buf, ip->i_df.if_bytes);
+ kmem_free(iip->ili_extents_buf);
iip->ili_extents_buf = NULL;
}
if (iip->ili_aextents_buf != NULL) {
@@ -694,7 +694,7 @@ xfs_inode_item_unlock(
ASSERT(ip->i_d.di_anextents > 0);
ASSERT(iip->ili_format.ilf_fields & XFS_ILOG_AEXT);
ASSERT(ip->i_afp->if_bytes > 0);
- kmem_free(iip->ili_aextents_buf, ip->i_afp->if_bytes);
+ kmem_free(iip->ili_aextents_buf);
iip->ili_aextents_buf = NULL;
}
@@ -957,8 +957,7 @@ xfs_inode_item_destroy(
{
#ifdef XFS_TRANS_DEBUG
if (ip->i_itemp->ili_root_size != 0) {
- kmem_free(ip->i_itemp->ili_orig_root,
- ip->i_itemp->ili_root_size);
+ kmem_free(ip->i_itemp->ili_orig_root);
}
#endif
kmem_zone_free(xfs_ili_zone, ip->i_itemp);
diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c
index 419de15aeb43..9a3ef9dcaeb9 100644
--- a/fs/xfs/xfs_itable.c
+++ b/fs/xfs/xfs_itable.c
@@ -257,7 +257,7 @@ xfs_bulkstat_one(
*ubused = error;
out_free:
- kmem_free(buf, sizeof(*buf));
+ kmem_free(buf);
return error;
}
@@ -708,7 +708,7 @@ xfs_bulkstat(
/*
* Done, we're either out of filesystem or space to put the data.
*/
- kmem_free(irbuf, irbsize);
+ kmem_free(irbuf);
*ubcountp = ubelem;
/*
* Found some inodes, return them now and return the error next time.
@@ -914,7 +914,7 @@ xfs_inumbers(
}
*lastino = XFS_AGINO_TO_INO(mp, agno, agino);
}
- kmem_free(buffer, bcount * sizeof(*buffer));
+ kmem_free(buffer);
if (cur)
xfs_btree_del_cursor(cur, (error ? XFS_BTREE_ERROR :
XFS_BTREE_NOERROR));
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
index afaee301b0ee..2497de885b0a 100644
--- a/fs/xfs/xfs_log.c
+++ b/fs/xfs/xfs_log.c
@@ -226,20 +226,24 @@ xlog_grant_sub_space(struct log *log, int bytes)
static void
xlog_grant_add_space_write(struct log *log, int bytes)
{
- log->l_grant_write_bytes += bytes;
- if (log->l_grant_write_bytes > log->l_logsize) {
- log->l_grant_write_bytes -= log->l_logsize;
+ int tmp = log->l_logsize - log->l_grant_write_bytes;
+ if (tmp > bytes)
+ log->l_grant_write_bytes += bytes;
+ else {
log->l_grant_write_cycle++;
+ log->l_grant_write_bytes = bytes - tmp;
}
}
static void
xlog_grant_add_space_reserve(struct log *log, int bytes)
{
- log->l_grant_reserve_bytes += bytes;
- if (log->l_grant_reserve_bytes > log->l_logsize) {
- log->l_grant_reserve_bytes -= log->l_logsize;
+ int tmp = log->l_logsize - log->l_grant_reserve_bytes;
+ if (tmp > bytes)
+ log->l_grant_reserve_bytes += bytes;
+ else {
log->l_grant_reserve_cycle++;
+ log->l_grant_reserve_bytes = bytes - tmp;
}
}
@@ -1228,7 +1232,7 @@ xlog_alloc_log(xfs_mount_t *mp,
spin_lock_init(&log->l_icloglock);
spin_lock_init(&log->l_grant_lock);
- initnsema(&log->l_flushsema, 0, "ic-flush");
+ sv_init(&log->l_flush_wait, 0, "flush_wait");
/* log record size must be multiple of BBSIZE; see xlog_rec_header_t */
ASSERT((XFS_BUF_SIZE(bp) & BBMASK) == 0);
@@ -1570,10 +1574,9 @@ xlog_dealloc_log(xlog_t *log)
}
#endif
next_iclog = iclog->ic_next;
- kmem_free(iclog, sizeof(xlog_in_core_t));
+ kmem_free(iclog);
iclog = next_iclog;
}
- freesema(&log->l_flushsema);
spinlock_destroy(&log->l_icloglock);
spinlock_destroy(&log->l_grant_lock);
@@ -1587,7 +1590,7 @@ xlog_dealloc_log(xlog_t *log)
}
#endif
log->l_mp->m_log = NULL;
- kmem_free(log, sizeof(xlog_t));
+ kmem_free(log);
} /* xlog_dealloc_log */
/*
@@ -2097,6 +2100,7 @@ xlog_state_do_callback(
int funcdidcallbacks; /* flag: function did callbacks */
int repeats; /* for issuing console warnings if
* looping too many times */
+ int wake = 0;
spin_lock(&log->l_icloglock);
first_iclog = iclog = log->l_iclog;
@@ -2278,15 +2282,13 @@ xlog_state_do_callback(
}
#endif
- flushcnt = 0;
- if (log->l_iclog->ic_state & (XLOG_STATE_ACTIVE|XLOG_STATE_IOERROR)) {
- flushcnt = log->l_flushcnt;
- log->l_flushcnt = 0;
- }
+ if (log->l_iclog->ic_state & (XLOG_STATE_ACTIVE|XLOG_STATE_IOERROR))
+ wake = 1;
spin_unlock(&log->l_icloglock);
- while (flushcnt--)
- vsema(&log->l_flushsema);
-} /* xlog_state_do_callback */
+
+ if (wake)
+ sv_broadcast(&log->l_flush_wait);
+}
/*
@@ -2384,16 +2386,15 @@ restart:
}
iclog = log->l_iclog;
- if (! (iclog->ic_state == XLOG_STATE_ACTIVE)) {
- log->l_flushcnt++;
- spin_unlock(&log->l_icloglock);
+ if (iclog->ic_state != XLOG_STATE_ACTIVE) {
xlog_trace_iclog(iclog, XLOG_TRACE_SLEEP_FLUSH);
XFS_STATS_INC(xs_log_noiclogs);
- /* Ensure that log writes happen */
- psema(&log->l_flushsema, PINOD);
+
+ /* Wait for log writes to have flushed */
+ sv_wait(&log->l_flush_wait, 0, &log->l_icloglock, 0);
goto restart;
}
- ASSERT(iclog->ic_state == XLOG_STATE_ACTIVE);
+
head = &iclog->ic_header;
atomic_inc(&iclog->ic_refcnt); /* prevents sync */
diff --git a/fs/xfs/xfs_log_priv.h b/fs/xfs/xfs_log_priv.h
index 8952a392b5f3..6245913196b4 100644
--- a/fs/xfs/xfs_log_priv.h
+++ b/fs/xfs/xfs_log_priv.h
@@ -423,10 +423,8 @@ typedef struct log {
int l_logBBsize; /* size of log in BB chunks */
/* The following block of fields are changed while holding icloglock */
- sema_t l_flushsema ____cacheline_aligned_in_smp;
- /* iclog flushing semaphore */
- int l_flushcnt; /* # of procs waiting on this
- * sema */
+ sv_t l_flush_wait ____cacheline_aligned_in_smp;
+ /* waiting for iclog flush */
int l_covered_state;/* state of "covering disk
* log entries" */
xlog_in_core_t *l_iclog; /* head log queue */
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
index e65ab4af0955..9eb722ec744e 100644
--- a/fs/xfs/xfs_log_recover.c
+++ b/fs/xfs/xfs_log_recover.c
@@ -1715,8 +1715,7 @@ xlog_check_buffer_cancelled(
} else {
prevp->bc_next = bcp->bc_next;
}
- kmem_free(bcp,
- sizeof(xfs_buf_cancel_t));
+ kmem_free(bcp);
}
}
return 1;
@@ -2519,7 +2518,7 @@ write_inode_buffer:
error:
if (need_free)
- kmem_free(in_f, sizeof(*in_f));
+ kmem_free(in_f);
return XFS_ERROR(error);
}
@@ -2830,16 +2829,14 @@ xlog_recover_free_trans(
item = item->ri_next;
/* Free the regions in the item. */
for (i = 0; i < free_item->ri_cnt; i++) {
- kmem_free(free_item->ri_buf[i].i_addr,
- free_item->ri_buf[i].i_len);
+ kmem_free(free_item->ri_buf[i].i_addr);
}
/* Free the item itself */
- kmem_free(free_item->ri_buf,
- (free_item->ri_total * sizeof(xfs_log_iovec_t)));
- kmem_free(free_item, sizeof(xlog_recover_item_t));
+ kmem_free(free_item->ri_buf);
+ kmem_free(free_item);
} while (first_item != item);
/* Free the transaction recover structure */
- kmem_free(trans, sizeof(xlog_recover_t));
+ kmem_free(trans);
}
STATIC int
@@ -3786,8 +3783,7 @@ xlog_do_log_recovery(
error = xlog_do_recovery_pass(log, head_blk, tail_blk,
XLOG_RECOVER_PASS1);
if (error != 0) {
- kmem_free(log->l_buf_cancel_table,
- XLOG_BC_TABLE_SIZE * sizeof(xfs_buf_cancel_t*));
+ kmem_free(log->l_buf_cancel_table);
log->l_buf_cancel_table = NULL;
return error;
}
@@ -3806,8 +3802,7 @@ xlog_do_log_recovery(
}
#endif /* DEBUG */
- kmem_free(log->l_buf_cancel_table,
- XLOG_BC_TABLE_SIZE * sizeof(xfs_buf_cancel_t*));
+ kmem_free(log->l_buf_cancel_table);
log->l_buf_cancel_table = NULL;
return error;
diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c
index da3988453b71..1bfaa204f689 100644
--- a/fs/xfs/xfs_mount.c
+++ b/fs/xfs/xfs_mount.c
@@ -47,12 +47,10 @@
STATIC int xfs_mount_log_sb(xfs_mount_t *, __int64_t);
STATIC int xfs_uuid_mount(xfs_mount_t *);
-STATIC void xfs_uuid_unmount(xfs_mount_t *mp);
STATIC void xfs_unmountfs_wait(xfs_mount_t *);
#ifdef HAVE_PERCPU_SB
-STATIC void xfs_icsb_destroy_counters(xfs_mount_t *);
STATIC void xfs_icsb_balance_counter(xfs_mount_t *, xfs_sb_field_t,
int);
STATIC void xfs_icsb_balance_counter_locked(xfs_mount_t *, xfs_sb_field_t,
@@ -63,7 +61,6 @@ STATIC void xfs_icsb_disable_counter(xfs_mount_t *, xfs_sb_field_t);
#else
-#define xfs_icsb_destroy_counters(mp) do { } while (0)
#define xfs_icsb_balance_counter(mp, a, b) do { } while (0)
#define xfs_icsb_balance_counter_locked(mp, a, b) do { } while (0)
#define xfs_icsb_modify_counters(mp, a, b, c) do { } while (0)
@@ -126,33 +123,11 @@ static const struct {
};
/*
- * Return a pointer to an initialized xfs_mount structure.
- */
-xfs_mount_t *
-xfs_mount_init(void)
-{
- xfs_mount_t *mp;
-
- mp = kmem_zalloc(sizeof(xfs_mount_t), KM_SLEEP);
-
- if (xfs_icsb_init_counters(mp)) {
- mp->m_flags |= XFS_MOUNT_NO_PERCPU_SB;
- }
-
- spin_lock_init(&mp->m_sb_lock);
- mutex_init(&mp->m_ilock);
- mutex_init(&mp->m_growlock);
- atomic_set(&mp->m_active_trans, 0);
-
- return mp;
-}
-
-/*
* Free up the resources associated with a mount structure. Assume that
* the structure was initially zeroed, so we can tell which fields got
* initialized.
*/
-void
+STATIC void
xfs_mount_free(
xfs_mount_t *mp)
{
@@ -161,11 +136,8 @@ xfs_mount_free(
for (agno = 0; agno < mp->m_maxagi; agno++)
if (mp->m_perag[agno].pagb_list)
- kmem_free(mp->m_perag[agno].pagb_list,
- sizeof(xfs_perag_busy_t) *
- XFS_PAGB_NUM_SLOTS);
- kmem_free(mp->m_perag,
- sizeof(xfs_perag_t) * mp->m_sb.sb_agcount);
+ kmem_free(mp->m_perag[agno].pagb_list);
+ kmem_free(mp->m_perag);
}
spinlock_destroy(&mp->m_ail_lock);
@@ -176,13 +148,11 @@ xfs_mount_free(
XFS_QM_DONE(mp);
if (mp->m_fsname != NULL)
- kmem_free(mp->m_fsname, mp->m_fsname_len);
+ kmem_free(mp->m_fsname);
if (mp->m_rtname != NULL)
- kmem_free(mp->m_rtname, strlen(mp->m_rtname) + 1);
+ kmem_free(mp->m_rtname);
if (mp->m_logname != NULL)
- kmem_free(mp->m_logname, strlen(mp->m_logname) + 1);
-
- xfs_icsb_destroy_counters(mp);
+ kmem_free(mp->m_logname);
}
/*
@@ -994,9 +964,19 @@ xfs_mountfs(
* Re-check for ATTR2 in case it was found in bad_features2
* slot.
*/
- if (xfs_sb_version_hasattr2(&mp->m_sb))
+ if (xfs_sb_version_hasattr2(&mp->m_sb) &&
+ !(mp->m_flags & XFS_MOUNT_NOATTR2))
mp->m_flags |= XFS_MOUNT_ATTR2;
+ }
+
+ if (xfs_sb_version_hasattr2(&mp->m_sb) &&
+ (mp->m_flags & XFS_MOUNT_NOATTR2)) {
+ xfs_sb_version_removeattr2(&mp->m_sb);
+ update_flags |= XFS_SB_FEATURES2;
+ /* update sb_versionnum for the clearing of the morebits */
+ if (!sbp->sb_features2)
+ update_flags |= XFS_SB_VERSIONNUM;
}
/*
@@ -1255,15 +1235,13 @@ xfs_mountfs(
error2:
for (agno = 0; agno < sbp->sb_agcount; agno++)
if (mp->m_perag[agno].pagb_list)
- kmem_free(mp->m_perag[agno].pagb_list,
- sizeof(xfs_perag_busy_t) * XFS_PAGB_NUM_SLOTS);
- kmem_free(mp->m_perag, sbp->sb_agcount * sizeof(xfs_perag_t));
+ kmem_free(mp->m_perag[agno].pagb_list);
+ kmem_free(mp->m_perag);
mp->m_perag = NULL;
/* FALLTHROUGH */
error1:
if (uuid_mounted)
- xfs_uuid_unmount(mp);
- xfs_freesb(mp);
+ uuid_table_remove(&mp->m_sb.sb_uuid);
return error;
}
@@ -1274,7 +1252,7 @@ xfs_mountfs(
* log and makes sure that incore structures are freed.
*/
int
-xfs_unmountfs(xfs_mount_t *mp, struct cred *cr)
+xfs_unmountfs(xfs_mount_t *mp)
{
__uint64_t resblks;
int error = 0;
@@ -1341,9 +1319,8 @@ xfs_unmountfs(xfs_mount_t *mp, struct cred *cr)
*/
ASSERT(mp->m_inodes == NULL);
- xfs_unmountfs_close(mp, cr);
if ((mp->m_flags & XFS_MOUNT_NOUUID) == 0)
- xfs_uuid_unmount(mp);
+ uuid_table_remove(&mp->m_sb.sb_uuid);
#if defined(DEBUG) || defined(INDUCE_IO_ERROR)
xfs_errortag_clearall(mp, 0);
@@ -1352,16 +1329,6 @@ xfs_unmountfs(xfs_mount_t *mp, struct cred *cr)
return 0;
}
-void
-xfs_unmountfs_close(xfs_mount_t *mp, struct cred *cr)
-{
- if (mp->m_logdev_targp && mp->m_logdev_targp != mp->m_ddev_targp)
- xfs_free_buftarg(mp->m_logdev_targp, 1);
- if (mp->m_rtdev_targp)
- xfs_free_buftarg(mp->m_rtdev_targp, 1);
- xfs_free_buftarg(mp->m_ddev_targp, 0);
-}
-
STATIC void
xfs_unmountfs_wait(xfs_mount_t *mp)
{
@@ -1905,16 +1872,6 @@ xfs_uuid_mount(
}
/*
- * Remove filesystem from the UUID table.
- */
-STATIC void
-xfs_uuid_unmount(
- xfs_mount_t *mp)
-{
- uuid_table_remove(&mp->m_sb.sb_uuid);
-}
-
-/*
* Used to log changes to the superblock unit and width fields which could
* be altered by the mount options, as well as any potential sb_features2
* fixup. Only the first superblock is updated.
@@ -1928,7 +1885,8 @@ xfs_mount_log_sb(
int error;
ASSERT(fields & (XFS_SB_UNIT | XFS_SB_WIDTH | XFS_SB_UUID |
- XFS_SB_FEATURES2 | XFS_SB_BAD_FEATURES2));
+ XFS_SB_FEATURES2 | XFS_SB_BAD_FEATURES2 |
+ XFS_SB_VERSIONNUM));
tp = xfs_trans_alloc(mp, XFS_TRANS_SB_UNIT);
error = xfs_trans_reserve(tp, 0, mp->m_sb.sb_sectsize + 128, 0, 0,
@@ -2109,7 +2067,7 @@ xfs_icsb_reinit_counters(
xfs_icsb_unlock(mp);
}
-STATIC void
+void
xfs_icsb_destroy_counters(
xfs_mount_t *mp)
{
diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h
index 63e0693a358a..dbba68f8c771 100644
--- a/fs/xfs/xfs_mount.h
+++ b/fs/xfs/xfs_mount.h
@@ -61,6 +61,7 @@ struct xfs_bmap_free;
struct xfs_extdelta;
struct xfs_swapext;
struct xfs_mru_cache;
+struct xfs_nameops;
/*
* Prototypes and functions for the Data Migration subsystem.
@@ -210,12 +211,14 @@ typedef struct xfs_icsb_cnts {
extern int xfs_icsb_init_counters(struct xfs_mount *);
extern void xfs_icsb_reinit_counters(struct xfs_mount *);
+extern void xfs_icsb_destroy_counters(struct xfs_mount *);
extern void xfs_icsb_sync_counters(struct xfs_mount *, int);
extern void xfs_icsb_sync_counters_locked(struct xfs_mount *, int);
#else
-#define xfs_icsb_init_counters(mp) (0)
-#define xfs_icsb_reinit_counters(mp) do { } while (0)
+#define xfs_icsb_init_counters(mp) (0)
+#define xfs_icsb_destroy_counters(mp) do { } while (0)
+#define xfs_icsb_reinit_counters(mp) do { } while (0)
#define xfs_icsb_sync_counters(mp, flags) do { } while (0)
#define xfs_icsb_sync_counters_locked(mp, flags) do { } while (0)
#endif
@@ -313,6 +316,7 @@ typedef struct xfs_mount {
__uint8_t m_inode_quiesce;/* call quiesce on new inodes.
field governed by m_ilock */
__uint8_t m_sectbb_log; /* sectlog - BBSHIFT */
+ const struct xfs_nameops *m_dirnameops; /* vector of dir name ops */
int m_dirblksize; /* directory block sz--bytes */
int m_dirblkfsbs; /* directory block sz--fsbs */
xfs_dablk_t m_dirdatablk; /* blockno of dir data v2 */
@@ -337,6 +341,7 @@ typedef struct xfs_mount {
spinlock_t m_sync_lock; /* work item list lock */
int m_sync_seq; /* sync thread generation no. */
wait_queue_head_t m_wait_single_sync_task;
+ struct vfsmount *m_vfsmount;
} xfs_mount_t;
/*
@@ -378,6 +383,7 @@ typedef struct xfs_mount {
counters */
#define XFS_MOUNT_FILESTREAMS (1ULL << 24) /* enable the filestreams
allocator */
+#define XFS_MOUNT_NOATTR2 (1ULL << 25) /* disable use of attr2 format */
/*
@@ -510,15 +516,12 @@ typedef struct xfs_mod_sb {
#define XFS_MOUNT_ILOCK(mp) mutex_lock(&((mp)->m_ilock))
#define XFS_MOUNT_IUNLOCK(mp) mutex_unlock(&((mp)->m_ilock))
-extern xfs_mount_t *xfs_mount_init(void);
extern void xfs_mod_sb(xfs_trans_t *, __int64_t);
extern int xfs_log_sbcount(xfs_mount_t *, uint);
-extern void xfs_mount_free(xfs_mount_t *mp);
extern int xfs_mountfs(xfs_mount_t *mp, int);
extern void xfs_mountfs_check_barriers(xfs_mount_t *mp);
-extern int xfs_unmountfs(xfs_mount_t *, struct cred *);
-extern void xfs_unmountfs_close(xfs_mount_t *, struct cred *);
+extern int xfs_unmountfs(xfs_mount_t *);
extern int xfs_unmountfs_writesb(xfs_mount_t *);
extern int xfs_unmount_flush(xfs_mount_t *, int);
extern int xfs_mod_incore_sb(xfs_mount_t *, xfs_sb_field_t, int64_t, int);
diff --git a/fs/xfs/xfs_mru_cache.c b/fs/xfs/xfs_mru_cache.c
index a0b2c0a2589a..26d14a1e0e14 100644
--- a/fs/xfs/xfs_mru_cache.c
+++ b/fs/xfs/xfs_mru_cache.c
@@ -382,9 +382,9 @@ xfs_mru_cache_create(
exit:
if (err && mru && mru->lists)
- kmem_free(mru->lists, mru->grp_count * sizeof(*mru->lists));
+ kmem_free(mru->lists);
if (err && mru)
- kmem_free(mru, sizeof(*mru));
+ kmem_free(mru);
return err;
}
@@ -424,8 +424,8 @@ xfs_mru_cache_destroy(
xfs_mru_cache_flush(mru);
- kmem_free(mru->lists, mru->grp_count * sizeof(*mru->lists));
- kmem_free(mru, sizeof(*mru));
+ kmem_free(mru->lists);
+ kmem_free(mru);
}
/*
diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c
index a0dc6e5bc5b9..bf87a5913504 100644
--- a/fs/xfs/xfs_rtalloc.c
+++ b/fs/xfs/xfs_rtalloc.c
@@ -2062,7 +2062,7 @@ xfs_growfs_rt(
/*
* Free the fake mp structure.
*/
- kmem_free(nmp, sizeof(*nmp));
+ kmem_free(nmp);
return error;
}
diff --git a/fs/xfs/xfs_sb.h b/fs/xfs/xfs_sb.h
index d904efe7f871..3f8cf1587f4c 100644
--- a/fs/xfs/xfs_sb.h
+++ b/fs/xfs/xfs_sb.h
@@ -46,10 +46,12 @@ struct xfs_mount;
#define XFS_SB_VERSION_SECTORBIT 0x0800
#define XFS_SB_VERSION_EXTFLGBIT 0x1000
#define XFS_SB_VERSION_DIRV2BIT 0x2000
+#define XFS_SB_VERSION_BORGBIT 0x4000 /* ASCII only case-insens. */
#define XFS_SB_VERSION_MOREBITSBIT 0x8000
#define XFS_SB_VERSION_OKSASHFBITS \
(XFS_SB_VERSION_EXTFLGBIT | \
- XFS_SB_VERSION_DIRV2BIT)
+ XFS_SB_VERSION_DIRV2BIT | \
+ XFS_SB_VERSION_BORGBIT)
#define XFS_SB_VERSION_OKREALFBITS \
(XFS_SB_VERSION_ATTRBIT | \
XFS_SB_VERSION_NLINKBIT | \
@@ -437,6 +439,12 @@ static inline int xfs_sb_version_hassector(xfs_sb_t *sbp)
((sbp)->sb_versionnum & XFS_SB_VERSION_SECTORBIT);
}
+static inline int xfs_sb_version_hasasciici(xfs_sb_t *sbp)
+{
+ return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
+ (sbp->sb_versionnum & XFS_SB_VERSION_BORGBIT);
+}
+
static inline int xfs_sb_version_hasmorebits(xfs_sb_t *sbp)
{
return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_4) && \
@@ -473,6 +481,13 @@ static inline void xfs_sb_version_addattr2(xfs_sb_t *sbp)
((sbp)->sb_features2 | XFS_SB_VERSION2_ATTR2BIT)));
}
+static inline void xfs_sb_version_removeattr2(xfs_sb_t *sbp)
+{
+ sbp->sb_features2 &= ~XFS_SB_VERSION2_ATTR2BIT;
+ if (!sbp->sb_features2)
+ sbp->sb_versionnum &= ~XFS_SB_VERSION_MOREBITSBIT;
+}
+
/*
* end of superblock version macros
*/
diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c
index 140386434aa3..e4ebddd3c500 100644
--- a/fs/xfs/xfs_trans.c
+++ b/fs/xfs/xfs_trans.c
@@ -889,7 +889,7 @@ shut_us_down:
tp->t_commit_lsn = commit_lsn;
if (nvec > XFS_TRANS_LOGVEC_COUNT) {
- kmem_free(log_vector, nvec * sizeof(xfs_log_iovec_t));
+ kmem_free(log_vector);
}
/*
@@ -1265,7 +1265,7 @@ xfs_trans_committed(
ASSERT(!XFS_LIC_ARE_ALL_FREE(licp));
xfs_trans_chunk_committed(licp, tp->t_lsn, abortflag);
next_licp = licp->lic_next;
- kmem_free(licp, sizeof(xfs_log_item_chunk_t));
+ kmem_free(licp);
licp = next_licp;
}
diff --git a/fs/xfs/xfs_trans_inode.c b/fs/xfs/xfs_trans_inode.c
index 4c70bf5e9985..2a1c0f071f91 100644
--- a/fs/xfs/xfs_trans_inode.c
+++ b/fs/xfs/xfs_trans_inode.c
@@ -291,7 +291,7 @@ xfs_trans_inode_broot_debug(
iip = ip->i_itemp;
if (iip->ili_root_size != 0) {
ASSERT(iip->ili_orig_root != NULL);
- kmem_free(iip->ili_orig_root, iip->ili_root_size);
+ kmem_free(iip->ili_orig_root);
iip->ili_root_size = 0;
iip->ili_orig_root = NULL;
}
diff --git a/fs/xfs/xfs_trans_item.c b/fs/xfs/xfs_trans_item.c
index 66a09f0d894b..db5c83595526 100644
--- a/fs/xfs/xfs_trans_item.c
+++ b/fs/xfs/xfs_trans_item.c
@@ -161,7 +161,7 @@ xfs_trans_free_item(xfs_trans_t *tp, xfs_log_item_desc_t *lidp)
licpp = &((*licpp)->lic_next);
}
*licpp = licp->lic_next;
- kmem_free(licp, sizeof(xfs_log_item_chunk_t));
+ kmem_free(licp);
tp->t_items_free -= XFS_LIC_NUM_SLOTS;
}
}
@@ -314,7 +314,7 @@ xfs_trans_free_items(
ASSERT(!XFS_LIC_ARE_ALL_FREE(licp));
(void) xfs_trans_unlock_chunk(licp, 1, abort, NULLCOMMITLSN);
next_licp = licp->lic_next;
- kmem_free(licp, sizeof(xfs_log_item_chunk_t));
+ kmem_free(licp);
licp = next_licp;
}
@@ -363,7 +363,7 @@ xfs_trans_unlock_items(xfs_trans_t *tp, xfs_lsn_t commit_lsn)
next_licp = licp->lic_next;
if (XFS_LIC_ARE_ALL_FREE(licp)) {
*licpp = next_licp;
- kmem_free(licp, sizeof(xfs_log_item_chunk_t));
+ kmem_free(licp);
freed -= XFS_LIC_NUM_SLOTS;
} else {
licpp = &(licp->lic_next);
@@ -530,7 +530,7 @@ xfs_trans_free_busy(xfs_trans_t *tp)
lbcp = tp->t_busy.lbc_next;
while (lbcp != NULL) {
lbcq = lbcp->lbc_next;
- kmem_free(lbcp, sizeof(xfs_log_busy_chunk_t));
+ kmem_free(lbcp);
lbcp = lbcq;
}
diff --git a/fs/xfs/xfs_vfsops.c b/fs/xfs/xfs_vfsops.c
index 30bacd8bb0e5..8b5a3376c2f7 100644
--- a/fs/xfs/xfs_vfsops.c
+++ b/fs/xfs/xfs_vfsops.c
@@ -189,455 +189,6 @@ xfs_cleanup(void)
kmem_zone_destroy(xfs_log_ticket_zone);
}
-/*
- * xfs_start_flags
- *
- * This function fills in xfs_mount_t fields based on mount args.
- * Note: the superblock has _not_ yet been read in.
- */
-STATIC int
-xfs_start_flags(
- struct xfs_mount_args *ap,
- struct xfs_mount *mp)
-{
- /* Values are in BBs */
- if ((ap->flags & XFSMNT_NOALIGN) != XFSMNT_NOALIGN) {
- /*
- * At this point the superblock has not been read
- * in, therefore we do not know the block size.
- * Before the mount call ends we will convert
- * these to FSBs.
- */
- mp->m_dalign = ap->sunit;
- mp->m_swidth = ap->swidth;
- }
-
- if (ap->logbufs != -1 &&
- ap->logbufs != 0 &&
- (ap->logbufs < XLOG_MIN_ICLOGS ||
- ap->logbufs > XLOG_MAX_ICLOGS)) {
- cmn_err(CE_WARN,
- "XFS: invalid logbufs value: %d [not %d-%d]",
- ap->logbufs, XLOG_MIN_ICLOGS, XLOG_MAX_ICLOGS);
- return XFS_ERROR(EINVAL);
- }
- mp->m_logbufs = ap->logbufs;
- if (ap->logbufsize != -1 &&
- ap->logbufsize != 0 &&
- (ap->logbufsize < XLOG_MIN_RECORD_BSIZE ||
- ap->logbufsize > XLOG_MAX_RECORD_BSIZE ||
- !is_power_of_2(ap->logbufsize))) {
- cmn_err(CE_WARN,
- "XFS: invalid logbufsize: %d [not 16k,32k,64k,128k or 256k]",
- ap->logbufsize);
- return XFS_ERROR(EINVAL);
- }
- mp->m_logbsize = ap->logbufsize;
- mp->m_fsname_len = strlen(ap->fsname) + 1;
- mp->m_fsname = kmem_alloc(mp->m_fsname_len, KM_SLEEP);
- strcpy(mp->m_fsname, ap->fsname);
- if (ap->rtname[0]) {
- mp->m_rtname = kmem_alloc(strlen(ap->rtname) + 1, KM_SLEEP);
- strcpy(mp->m_rtname, ap->rtname);
- }
- if (ap->logname[0]) {
- mp->m_logname = kmem_alloc(strlen(ap->logname) + 1, KM_SLEEP);
- strcpy(mp->m_logname, ap->logname);
- }
-
- if (ap->flags & XFSMNT_WSYNC)
- mp->m_flags |= XFS_MOUNT_WSYNC;
-#if XFS_BIG_INUMS
- if (ap->flags & XFSMNT_INO64) {
- mp->m_flags |= XFS_MOUNT_INO64;
- mp->m_inoadd = XFS_INO64_OFFSET;
- }
-#endif
- if (ap->flags & XFSMNT_RETERR)
- mp->m_flags |= XFS_MOUNT_RETERR;
- if (ap->flags & XFSMNT_NOALIGN)
- mp->m_flags |= XFS_MOUNT_NOALIGN;
- if (ap->flags & XFSMNT_SWALLOC)
- mp->m_flags |= XFS_MOUNT_SWALLOC;
- if (ap->flags & XFSMNT_OSYNCISOSYNC)
- mp->m_flags |= XFS_MOUNT_OSYNCISOSYNC;
- if (ap->flags & XFSMNT_32BITINODES)
- mp->m_flags |= XFS_MOUNT_32BITINODES;
-
- if (ap->flags & XFSMNT_IOSIZE) {
- if (ap->iosizelog > XFS_MAX_IO_LOG ||
- ap->iosizelog < XFS_MIN_IO_LOG) {
- cmn_err(CE_WARN,
- "XFS: invalid log iosize: %d [not %d-%d]",
- ap->iosizelog, XFS_MIN_IO_LOG,
- XFS_MAX_IO_LOG);
- return XFS_ERROR(EINVAL);
- }
-
- mp->m_flags |= XFS_MOUNT_DFLT_IOSIZE;
- mp->m_readio_log = mp->m_writeio_log = ap->iosizelog;
- }
-
- if (ap->flags & XFSMNT_IKEEP)
- mp->m_flags |= XFS_MOUNT_IKEEP;
- if (ap->flags & XFSMNT_DIRSYNC)
- mp->m_flags |= XFS_MOUNT_DIRSYNC;
- if (ap->flags & XFSMNT_ATTR2)
- mp->m_flags |= XFS_MOUNT_ATTR2;
-
- if (ap->flags2 & XFSMNT2_COMPAT_IOSIZE)
- mp->m_flags |= XFS_MOUNT_COMPAT_IOSIZE;
-
- /*
- * no recovery flag requires a read-only mount
- */
- if (ap->flags & XFSMNT_NORECOVERY) {
- if (!(mp->m_flags & XFS_MOUNT_RDONLY)) {
- cmn_err(CE_WARN,
- "XFS: tried to mount a FS read-write without recovery!");
- return XFS_ERROR(EINVAL);
- }
- mp->m_flags |= XFS_MOUNT_NORECOVERY;
- }
-
- if (ap->flags & XFSMNT_NOUUID)
- mp->m_flags |= XFS_MOUNT_NOUUID;
- if (ap->flags & XFSMNT_BARRIER)
- mp->m_flags |= XFS_MOUNT_BARRIER;
- else
- mp->m_flags &= ~XFS_MOUNT_BARRIER;
-
- if (ap->flags2 & XFSMNT2_FILESTREAMS)
- mp->m_flags |= XFS_MOUNT_FILESTREAMS;
-
- if (ap->flags & XFSMNT_DMAPI)
- mp->m_flags |= XFS_MOUNT_DMAPI;
- return 0;
-}
-
-/*
- * This function fills in xfs_mount_t fields based on mount args.
- * Note: the superblock _has_ now been read in.
- */
-STATIC int
-xfs_finish_flags(
- struct xfs_mount_args *ap,
- struct xfs_mount *mp)
-{
- int ronly = (mp->m_flags & XFS_MOUNT_RDONLY);
-
- /* Fail a mount where the logbuf is smaller then the log stripe */
- if (xfs_sb_version_haslogv2(&mp->m_sb)) {
- if ((ap->logbufsize <= 0) &&
- (mp->m_sb.sb_logsunit > XLOG_BIG_RECORD_BSIZE)) {
- mp->m_logbsize = mp->m_sb.sb_logsunit;
- } else if (ap->logbufsize > 0 &&
- ap->logbufsize < mp->m_sb.sb_logsunit) {
- cmn_err(CE_WARN,
- "XFS: logbuf size must be greater than or equal to log stripe size");
- return XFS_ERROR(EINVAL);
- }
- } else {
- /* Fail a mount if the logbuf is larger than 32K */
- if (ap->logbufsize > XLOG_BIG_RECORD_BSIZE) {
- cmn_err(CE_WARN,
- "XFS: logbuf size for version 1 logs must be 16K or 32K");
- return XFS_ERROR(EINVAL);
- }
- }
-
- if (xfs_sb_version_hasattr2(&mp->m_sb))
- mp->m_flags |= XFS_MOUNT_ATTR2;
-
- /*
- * prohibit r/w mounts of read-only filesystems
- */
- if ((mp->m_sb.sb_flags & XFS_SBF_READONLY) && !ronly) {
- cmn_err(CE_WARN,
- "XFS: cannot mount a read-only filesystem as read-write");
- return XFS_ERROR(EROFS);
- }
-
- /*
- * check for shared mount.
- */
- if (ap->flags & XFSMNT_SHARED) {
- if (!xfs_sb_version_hasshared(&mp->m_sb))
- return XFS_ERROR(EINVAL);
-
- /*
- * For IRIX 6.5, shared mounts must have the shared
- * version bit set, have the persistent readonly
- * field set, must be version 0 and can only be mounted
- * read-only.
- */
- if (!ronly || !(mp->m_sb.sb_flags & XFS_SBF_READONLY) ||
- (mp->m_sb.sb_shared_vn != 0))
- return XFS_ERROR(EINVAL);
-
- mp->m_flags |= XFS_MOUNT_SHARED;
-
- /*
- * Shared XFS V0 can't deal with DMI. Return EINVAL.
- */
- if (mp->m_sb.sb_shared_vn == 0 && (ap->flags & XFSMNT_DMAPI))
- return XFS_ERROR(EINVAL);
- }
-
- if (ap->flags & XFSMNT_UQUOTA) {
- mp->m_qflags |= (XFS_UQUOTA_ACCT | XFS_UQUOTA_ACTIVE);
- if (ap->flags & XFSMNT_UQUOTAENF)
- mp->m_qflags |= XFS_UQUOTA_ENFD;
- }
-
- if (ap->flags & XFSMNT_GQUOTA) {
- mp->m_qflags |= (XFS_GQUOTA_ACCT | XFS_GQUOTA_ACTIVE);
- if (ap->flags & XFSMNT_GQUOTAENF)
- mp->m_qflags |= XFS_OQUOTA_ENFD;
- } else if (ap->flags & XFSMNT_PQUOTA) {
- mp->m_qflags |= (XFS_PQUOTA_ACCT | XFS_PQUOTA_ACTIVE);
- if (ap->flags & XFSMNT_PQUOTAENF)
- mp->m_qflags |= XFS_OQUOTA_ENFD;
- }
-
- return 0;
-}
-
-/*
- * xfs_mount
- *
- * The file system configurations are:
- * (1) device (partition) with data and internal log
- * (2) logical volume with data and log subvolumes.
- * (3) logical volume with data, log, and realtime subvolumes.
- *
- * We only have to handle opening the log and realtime volumes here if
- * they are present. The data subvolume has already been opened by
- * get_sb_bdev() and is stored in vfsp->vfs_super->s_bdev.
- */
-int
-xfs_mount(
- struct xfs_mount *mp,
- struct xfs_mount_args *args,
- cred_t *credp)
-{
- struct block_device *ddev, *logdev, *rtdev;
- int flags = 0, error;
-
- ddev = mp->m_super->s_bdev;
- logdev = rtdev = NULL;
-
- error = xfs_dmops_get(mp, args);
- if (error)
- return error;
- error = xfs_qmops_get(mp, args);
- if (error)
- return error;
-
- if (args->flags & XFSMNT_QUIET)
- flags |= XFS_MFSI_QUIET;
-
- /*
- * Open real time and log devices - order is important.
- */
- if (args->logname[0]) {
- error = xfs_blkdev_get(mp, args->logname, &logdev);
- if (error)
- return error;
- }
- if (args->rtname[0]) {
- error = xfs_blkdev_get(mp, args->rtname, &rtdev);
- if (error) {
- xfs_blkdev_put(logdev);
- return error;
- }
-
- if (rtdev == ddev || rtdev == logdev) {
- cmn_err(CE_WARN,
- "XFS: Cannot mount filesystem with identical rtdev and ddev/logdev.");
- xfs_blkdev_put(logdev);
- xfs_blkdev_put(rtdev);
- return EINVAL;
- }
- }
-
- /*
- * Setup xfs_mount buffer target pointers
- */
- error = ENOMEM;
- mp->m_ddev_targp = xfs_alloc_buftarg(ddev, 0);
- if (!mp->m_ddev_targp) {
- xfs_blkdev_put(logdev);
- xfs_blkdev_put(rtdev);
- return error;
- }
- if (rtdev) {
- mp->m_rtdev_targp = xfs_alloc_buftarg(rtdev, 1);
- if (!mp->m_rtdev_targp) {
- xfs_blkdev_put(logdev);
- xfs_blkdev_put(rtdev);
- goto error0;
- }
- }
- mp->m_logdev_targp = (logdev && logdev != ddev) ?
- xfs_alloc_buftarg(logdev, 1) : mp->m_ddev_targp;
- if (!mp->m_logdev_targp) {
- xfs_blkdev_put(logdev);
- xfs_blkdev_put(rtdev);
- goto error0;
- }
-
- /*
- * Setup flags based on mount(2) options and then the superblock
- */
- error = xfs_start_flags(args, mp);
- if (error)
- goto error1;
- error = xfs_readsb(mp, flags);
- if (error)
- goto error1;
- error = xfs_finish_flags(args, mp);
- if (error)
- goto error2;
-
- /*
- * Setup xfs_mount buffer target pointers based on superblock
- */
- error = xfs_setsize_buftarg(mp->m_ddev_targp, mp->m_sb.sb_blocksize,
- mp->m_sb.sb_sectsize);
- if (!error && logdev && logdev != ddev) {
- unsigned int log_sector_size = BBSIZE;
-
- if (xfs_sb_version_hassector(&mp->m_sb))
- log_sector_size = mp->m_sb.sb_logsectsize;
- error = xfs_setsize_buftarg(mp->m_logdev_targp,
- mp->m_sb.sb_blocksize,
- log_sector_size);
- }
- if (!error && rtdev)
- error = xfs_setsize_buftarg(mp->m_rtdev_targp,
- mp->m_sb.sb_blocksize,
- mp->m_sb.sb_sectsize);
- if (error)
- goto error2;
-
- if (mp->m_flags & XFS_MOUNT_BARRIER)
- xfs_mountfs_check_barriers(mp);
-
- if ((error = xfs_filestream_mount(mp)))
- goto error2;
-
- error = xfs_mountfs(mp, flags);
- if (error)
- goto error2;
-
- XFS_SEND_MOUNT(mp, DM_RIGHT_NULL, args->mtpt, args->fsname);
-
- return 0;
-
-error2:
- if (mp->m_sb_bp)
- xfs_freesb(mp);
-error1:
- xfs_binval(mp->m_ddev_targp);
- if (logdev && logdev != ddev)
- xfs_binval(mp->m_logdev_targp);
- if (rtdev)
- xfs_binval(mp->m_rtdev_targp);
-error0:
- xfs_unmountfs_close(mp, credp);
- xfs_qmops_put(mp);
- xfs_dmops_put(mp);
- return error;
-}
-
-int
-xfs_unmount(
- xfs_mount_t *mp,
- int flags,
- cred_t *credp)
-{
- xfs_inode_t *rip;
- bhv_vnode_t *rvp;
- int unmount_event_wanted = 0;
- int unmount_event_flags = 0;
- int xfs_unmountfs_needed = 0;
- int error;
-
- rip = mp->m_rootip;
- rvp = XFS_ITOV(rip);
-
-#ifdef HAVE_DMAPI
- if (mp->m_flags & XFS_MOUNT_DMAPI) {
- error = XFS_SEND_PREUNMOUNT(mp,
- rip, DM_RIGHT_NULL, rip, DM_RIGHT_NULL,
- NULL, NULL, 0, 0,
- (mp->m_dmevmask & (1<<DM_EVENT_PREUNMOUNT))?
- 0:DM_FLAGS_UNWANTED);
- if (error)
- return XFS_ERROR(error);
- unmount_event_wanted = 1;
- unmount_event_flags = (mp->m_dmevmask & (1<<DM_EVENT_UNMOUNT))?
- 0 : DM_FLAGS_UNWANTED;
- }
-#endif
-
- /*
- * Blow away any referenced inode in the filestreams cache.
- * This can and will cause log traffic as inodes go inactive
- * here.
- */
- xfs_filestream_unmount(mp);
-
- XFS_bflush(mp->m_ddev_targp);
- error = xfs_unmount_flush(mp, 0);
- if (error)
- goto out;
-
- ASSERT(vn_count(rvp) == 1);
-
- /*
- * Drop the reference count
- */
- IRELE(rip);
-
- /*
- * If we're forcing a shutdown, typically because of a media error,
- * we want to make sure we invalidate dirty pages that belong to
- * referenced vnodes as well.
- */
- if (XFS_FORCED_SHUTDOWN(mp)) {
- error = xfs_sync(mp, SYNC_WAIT | SYNC_CLOSE);
- ASSERT(error != EFSCORRUPTED);
- }
- xfs_unmountfs_needed = 1;
-
-out:
- /* Send DMAPI event, if required.
- * Then do xfs_unmountfs() if needed.
- * Then return error (or zero).
- */
- if (unmount_event_wanted) {
- /* Note: mp structure must still exist for
- * XFS_SEND_UNMOUNT() call.
- */
- XFS_SEND_UNMOUNT(mp, error == 0 ? rip : NULL,
- DM_RIGHT_NULL, 0, error, unmount_event_flags);
- }
- if (xfs_unmountfs_needed) {
- /*
- * Call common unmount function to flush to disk
- * and free the super block buffer & mount structures.
- */
- xfs_unmountfs(mp, credp);
- xfs_qmops_put(mp);
- xfs_dmops_put(mp);
- kmem_free(mp, sizeof(xfs_mount_t));
- }
-
- return XFS_ERROR(error);
-}
-
STATIC void
xfs_quiesce_fs(
xfs_mount_t *mp)
@@ -694,30 +245,6 @@ xfs_attr_quiesce(
xfs_unmountfs_writesb(mp);
}
-int
-xfs_mntupdate(
- struct xfs_mount *mp,
- int *flags,
- struct xfs_mount_args *args)
-{
- if (!(*flags & MS_RDONLY)) { /* rw/ro -> rw */
- if (mp->m_flags & XFS_MOUNT_RDONLY)
- mp->m_flags &= ~XFS_MOUNT_RDONLY;
- if (args->flags & XFSMNT_BARRIER) {
- mp->m_flags |= XFS_MOUNT_BARRIER;
- xfs_mountfs_check_barriers(mp);
- } else {
- mp->m_flags &= ~XFS_MOUNT_BARRIER;
- }
- } else if (!(mp->m_flags & XFS_MOUNT_RDONLY)) { /* rw -> ro */
- xfs_filestream_flush(mp);
- xfs_sync(mp, SYNC_DATA_QUIESCE);
- xfs_attr_quiesce(mp);
- mp->m_flags |= XFS_MOUNT_RDONLY;
- }
- return 0;
-}
-
/*
* xfs_unmount_flush implements a set of flush operation on special
* inodes, which are needed as a separate set of operations so that
@@ -1048,7 +575,7 @@ xfs_sync_inodes(
if (XFS_FORCED_SHUTDOWN(mp) && !(flags & SYNC_CLOSE)) {
XFS_MOUNT_IUNLOCK(mp);
- kmem_free(ipointer, sizeof(xfs_iptr_t));
+ kmem_free(ipointer);
return 0;
}
@@ -1194,7 +721,7 @@ xfs_sync_inodes(
}
XFS_MOUNT_IUNLOCK(mp);
ASSERT(ipointer_in == B_FALSE);
- kmem_free(ipointer, sizeof(xfs_iptr_t));
+ kmem_free(ipointer);
return XFS_ERROR(error);
}
@@ -1224,7 +751,7 @@ xfs_sync_inodes(
ASSERT(ipointer_in == B_FALSE);
- kmem_free(ipointer, sizeof(xfs_iptr_t));
+ kmem_free(ipointer);
return XFS_ERROR(last_error);
}
diff --git a/fs/xfs/xfs_vfsops.h b/fs/xfs/xfs_vfsops.h
index 1688817c55ed..a74b05087da4 100644
--- a/fs/xfs/xfs_vfsops.h
+++ b/fs/xfs/xfs_vfsops.h
@@ -8,11 +8,6 @@ struct kstatfs;
struct xfs_mount;
struct xfs_mount_args;
-int xfs_mount(struct xfs_mount *mp, struct xfs_mount_args *args,
- struct cred *credp);
-int xfs_unmount(struct xfs_mount *mp, int flags, struct cred *credp);
-int xfs_mntupdate(struct xfs_mount *mp, int *flags,
- struct xfs_mount_args *args);
int xfs_sync(struct xfs_mount *mp, int flags);
void xfs_do_force_shutdown(struct xfs_mount *mp, int flags, char *fname,
int lnnum);
diff --git a/fs/xfs/xfs_vnodeops.c b/fs/xfs/xfs_vnodeops.c
index e475e3717eb3..b6a065eb25a5 100644
--- a/fs/xfs/xfs_vnodeops.c
+++ b/fs/xfs/xfs_vnodeops.c
@@ -444,7 +444,13 @@ xfs_setattr(
code = 0;
if ((vap->va_size > ip->i_size) &&
(flags & ATTR_NOSIZETOK) == 0) {
- code = xfs_igrow_start(ip, vap->va_size, credp);
+ /*
+ * Do the first part of growing a file: zero any data
+ * in the last block that is beyond the old EOF. We
+ * need to do this before the inode is joined to the
+ * transaction to modify the i_size.
+ */
+ code = xfs_zero_eof(ip, vap->va_size, ip->i_size);
}
xfs_iunlock(ip, XFS_ILOCK_EXCL);
@@ -512,8 +518,11 @@ xfs_setattr(
timeflags |= XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG;
if (vap->va_size > ip->i_size) {
- xfs_igrow_finish(tp, ip, vap->va_size,
- !(flags & ATTR_DMI));
+ ip->i_d.di_size = vap->va_size;
+ ip->i_size = vap->va_size;
+ if (!(flags & ATTR_DMI))
+ xfs_ichgtime(ip, XFS_ICHGTIME_CHG);
+ xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
} else if ((vap->va_size <= ip->i_size) ||
((vap->va_size == 0) && ip->i_d.di_nextents)) {
/*
@@ -1601,12 +1610,18 @@ xfs_inactive(
return VN_INACTIVE_CACHE;
}
-
+/*
+ * Lookups up an inode from "name". If ci_name is not NULL, then a CI match
+ * is allowed, otherwise it has to be an exact match. If a CI match is found,
+ * ci_name->name will point to a the actual name (caller must free) or
+ * will be set to NULL if an exact match is found.
+ */
int
xfs_lookup(
xfs_inode_t *dp,
struct xfs_name *name,
- xfs_inode_t **ipp)
+ xfs_inode_t **ipp,
+ struct xfs_name *ci_name)
{
xfs_ino_t inum;
int error;
@@ -1618,7 +1633,7 @@ xfs_lookup(
return XFS_ERROR(EIO);
lock_mode = xfs_ilock_map_shared(dp);
- error = xfs_dir_lookup(NULL, dp, name, &inum);
+ error = xfs_dir_lookup(NULL, dp, name, &inum, ci_name);
xfs_iunlock_map_shared(dp, lock_mode);
if (error)
@@ -1626,12 +1641,15 @@ xfs_lookup(
error = xfs_iget(dp->i_mount, NULL, inum, 0, 0, ipp, 0);
if (error)
- goto out;
+ goto out_free_name;
xfs_itrace_ref(*ipp);
return 0;
- out:
+out_free_name:
+ if (ci_name)
+ kmem_free(ci_name->name);
+out:
*ipp = NULL;
return error;
}
diff --git a/fs/xfs/xfs_vnodeops.h b/fs/xfs/xfs_vnodeops.h
index 57335ba4ce53..7e9a8b241f21 100644
--- a/fs/xfs/xfs_vnodeops.h
+++ b/fs/xfs/xfs_vnodeops.h
@@ -22,7 +22,7 @@ int xfs_fsync(struct xfs_inode *ip);
int xfs_release(struct xfs_inode *ip);
int xfs_inactive(struct xfs_inode *ip);
int xfs_lookup(struct xfs_inode *dp, struct xfs_name *name,
- struct xfs_inode **ipp);
+ struct xfs_inode **ipp, struct xfs_name *ci_name);
int xfs_create(struct xfs_inode *dp, struct xfs_name *name, mode_t mode,
xfs_dev_t rdev, struct xfs_inode **ipp, struct cred *credp);
int xfs_remove(struct xfs_inode *dp, struct xfs_name *name,
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index 2f1c68c7a727..db90a74f8714 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -376,9 +376,9 @@ acpi_handle acpi_get_pci_rootbridge_handle(unsigned int, unsigned int);
#define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)((dev)->archdata.acpi_handle))
#ifdef CONFIG_PM_SLEEP
-int acpi_pm_device_sleep_state(struct device *, int, int *);
+int acpi_pm_device_sleep_state(struct device *, int *);
#else /* !CONFIG_PM_SLEEP */
-static inline int acpi_pm_device_sleep_state(struct device *d, int w, int *p)
+static inline int acpi_pm_device_sleep_state(struct device *d, int *p)
{
if (p)
*p = ACPI_STATE_D0;
diff --git a/include/acpi/processor.h b/include/acpi/processor.h
index 06480bcabfdc..3795590e152a 100644
--- a/include/acpi/processor.h
+++ b/include/acpi/processor.h
@@ -255,7 +255,7 @@ extern void acpi_processor_unregister_performance(struct
int acpi_processor_notify_smm(struct module *calling_module);
/* for communication between multiple parts of the processor kernel module */
-extern struct acpi_processor *processors[NR_CPUS];
+DECLARE_PER_CPU(struct acpi_processor *, processors);
extern struct acpi_processor_errata errata;
void arch_acpi_processor_init_pdc(struct acpi_processor *pr);
@@ -319,6 +319,7 @@ static inline int acpi_processor_ppc_has_changed(struct acpi_processor *pr)
#endif /* CONFIG_CPU_FREQ */
/* in processor_throttling.c */
+int acpi_processor_tstate_has_changed(struct acpi_processor *pr);
int acpi_processor_get_throttling_info(struct acpi_processor *pr);
extern int acpi_processor_set_throttling(struct acpi_processor *pr, int state);
extern struct file_operations acpi_processor_throttling_fops;
diff --git a/include/asm-arm/arch-pxa/regs-lcd.h b/include/asm-arm/arch-pxa/regs-lcd.h
index f762493f5141..3ba464c913a5 100644
--- a/include/asm-arm/arch-pxa/regs-lcd.h
+++ b/include/asm-arm/arch-pxa/regs-lcd.h
@@ -1,5 +1,8 @@
#ifndef __ASM_ARCH_REGS_LCD_H
#define __ASM_ARCH_REGS_LCD_H
+
+#include <asm/arch/bitfield.h>
+
/*
* LCD Controller Registers and Bits Definitions
*/
@@ -69,7 +72,7 @@
#define LCCR0_QDM (1 << 11) /* LCD Quick Disable mask */
#define LCCR0_PDD (0xff << 12) /* Palette DMA request delay */
#define LCCR0_PDD_S 12
-#define LCCR0_BM (1 << 20) /* Branch mask */
+#define LCCR0_BM (1 << 20) /* Branch mask */
#define LCCR0_OUM (1 << 21) /* Output FIFO underrun mask */
#define LCCR0_LCDT (1 << 22) /* LCD panel type */
#define LCCR0_RDSTM (1 << 23) /* Read status interrupt mask */
diff --git a/include/asm-arm/kprobes.h b/include/asm-arm/kprobes.h
index c042194d3ab5..b1a37876942d 100644
--- a/include/asm-arm/kprobes.h
+++ b/include/asm-arm/kprobes.h
@@ -59,6 +59,7 @@ struct kprobe_ctlblk {
};
void arch_remove_kprobe(struct kprobe *);
+void kretprobe_trampoline(void);
int kprobe_trap_handler(struct pt_regs *regs, unsigned int instr);
int kprobe_fault_handler(struct pt_regs *regs, unsigned int fsr);
diff --git a/include/asm-avr32/arch-at32ap/board.h b/include/asm-avr32/arch-at32ap/board.h
index a4e2d28bfb58..b4cddfaca90e 100644
--- a/include/asm-avr32/arch-at32ap/board.h
+++ b/include/asm-avr32/arch-at32ap/board.h
@@ -8,6 +8,12 @@
#define GPIO_PIN_NONE (-1)
+/*
+ * Clock rates for various on-board oscillators. The number of entries
+ * in this array is chip-dependent.
+ */
+extern unsigned long at32_board_osc_rates[];
+
/* Add basic devices: system manager, interrupt controller, portmuxes, etc. */
void at32_add_system_devices(void);
@@ -36,7 +42,8 @@ at32_add_device_spi(unsigned int id, struct spi_board_info *b, unsigned int n);
struct atmel_lcdfb_info;
struct platform_device *
at32_add_device_lcdc(unsigned int id, struct atmel_lcdfb_info *data,
- unsigned long fbmem_start, unsigned long fbmem_len);
+ unsigned long fbmem_start, unsigned long fbmem_len,
+ unsigned int pin_config);
struct usba_platform_data;
struct platform_device *
@@ -73,6 +80,7 @@ struct platform_device *at32_add_device_twi(unsigned int id,
struct platform_device *at32_add_device_mci(unsigned int id);
struct platform_device *at32_add_device_ac97c(unsigned int id);
struct platform_device *at32_add_device_abdac(unsigned int id);
+struct platform_device *at32_add_device_psif(unsigned int id);
struct cf_platform_data {
int detect_pin;
diff --git a/include/asm-avr32/pci.h b/include/asm-avr32/pci.h
index 0f5f134b896a..a32a02372017 100644
--- a/include/asm-avr32/pci.h
+++ b/include/asm-avr32/pci.h
@@ -5,4 +5,6 @@
#define PCI_DMA_BUS_IS_PHYS (1)
+#include <asm-generic/pci-dma-compat.h>
+
#endif /* __ASM_AVR32_PCI_H__ */
diff --git a/include/asm-generic/percpu.h b/include/asm-generic/percpu.h
index b0e63c672ebd..36d188f47c98 100644
--- a/include/asm-generic/percpu.h
+++ b/include/asm-generic/percpu.h
@@ -45,7 +45,12 @@ extern unsigned long __per_cpu_offset[NR_CPUS];
* Only S390 provides its own means of moving the pointer.
*/
#ifndef SHIFT_PERCPU_PTR
-#define SHIFT_PERCPU_PTR(__p, __offset) RELOC_HIDE((__p), (__offset))
+# ifdef CONFIG_HAVE_ZERO_BASED_PER_CPU
+# define SHIFT_PERCPU_PTR(__p, __offset) \
+ ((__typeof(__p))(((void *)(__p)) + (__offset)))
+# else
+# define SHIFT_PERCPU_PTR(__p, __offset) RELOC_HIDE((__p), (__offset))
+# endif /* CONFIG_HAVE_ZERO_BASED_PER_CPU */
#endif
/*
@@ -70,6 +75,8 @@ extern void setup_per_cpu_areas(void);
#define per_cpu(var, cpu) (*((void)(cpu), &per_cpu_var(var)))
#define __get_cpu_var(var) per_cpu_var(var)
#define __raw_get_cpu_var(var) per_cpu_var(var)
+#define SHIFT_PERCPU_PTR(__p, __offset) (__p)
+#define per_cpu_offset(x) 0L
#endif /* SMP */
diff --git a/include/asm-generic/sections.h b/include/asm-generic/sections.h
index 8feeae1f2369..f697620c3e21 100644
--- a/include/asm-generic/sections.h
+++ b/include/asm-generic/sections.h
@@ -9,7 +9,17 @@ extern char __bss_start[], __bss_stop[];
extern char __init_begin[], __init_end[];
extern char _sinittext[], _einittext[];
extern char _end[];
+#ifdef CONFIG_HAVE_ZERO_BASED_PER_CPU
+extern char __per_cpu_load[];
+extern char ____per_cpu_size[];
+#define __per_cpu_size ((unsigned long)&____per_cpu_size)
+#define __per_cpu_start ((char *)0)
+#define __per_cpu_end ((char *)__per_cpu_size)
+#else
extern char __per_cpu_start[], __per_cpu_end[];
+#define __per_cpu_load __per_cpu_start
+#define __per_cpu_size (__per_cpu_end - __per_cpu_start)
+#endif
extern char __kprobes_text_start[], __kprobes_text_end[];
extern char __initdata_begin[], __initdata_end[];
extern char __start_rodata[], __end_rodata[];
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index f054778e916c..48fe38276d60 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -67,6 +67,8 @@
*(.rodata1) \
} \
\
+ BUG_TABLE \
+ \
/* PCI quirks */ \
.pci_fixup : AT(ADDR(.pci_fixup) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__start_pci_fixups_early) = .; \
@@ -84,6 +86,12 @@
VMLINUX_SYMBOL(__start_pci_fixups_resume) = .; \
*(.pci_fixup_resume) \
VMLINUX_SYMBOL(__end_pci_fixups_resume) = .; \
+ VMLINUX_SYMBOL(__start_pci_fixups_resume_early) = .; \
+ *(.pci_fixup_resume_early) \
+ VMLINUX_SYMBOL(__end_pci_fixups_resume_early) = .; \
+ VMLINUX_SYMBOL(__start_pci_fixups_suspend) = .; \
+ *(.pci_fixup_suspend) \
+ VMLINUX_SYMBOL(__end_pci_fixups_suspend) = .; \
} \
\
/* RapidIO route ops */ \
@@ -93,6 +101,8 @@
VMLINUX_SYMBOL(__end_rio_route_ops) = .; \
} \
\
+ TRACEDATA \
+ \
/* Kernel symbol table: Normal symbols */ \
__ksymtab : AT(ADDR(__ksymtab) - LOAD_OFFSET) { \
VMLINUX_SYMBOL(__start___ksymtab) = .; \
@@ -310,6 +320,7 @@
.stab.indexstr 0 : { *(.stab.indexstr) } \
.comment 0 : { *(.comment) }
+#ifdef CONFIG_GENERIC_BUG
#define BUG_TABLE \
. = ALIGN(8); \
__bug_table : AT(ADDR(__bug_table) - LOAD_OFFSET) { \
@@ -317,6 +328,21 @@
*(__bug_table) \
__stop___bug_table = .; \
}
+#else
+#define BUG_TABLE
+#endif
+
+#ifdef CONFIG_PM_TRACE
+#define TRACEDATA \
+ . = ALIGN(4); \
+ .tracedata : AT(ADDR(.tracedata) - LOAD_OFFSET) { \
+ __tracedata_start = .; \
+ *(.tracedata) \
+ __tracedata_end = .; \
+ }
+#else
+#define TRACEDATA
+#endif
#define NOTES \
.notes : AT(ADDR(.notes) - LOAD_OFFSET) { \
@@ -344,11 +370,28 @@
*(.initcall7.init) \
*(.initcall7s.init)
+#ifdef CONFIG_HAVE_ZERO_BASED_PER_CPU
+#define PERCPU(align) \
+ . = ALIGN(align); \
+ percpu : { } :percpu \
+ __per_cpu_load = .; \
+ .data.percpu 0 : AT(__per_cpu_load - LOAD_OFFSET) { \
+ *(.data.percpu.first) \
+ *(.data.percpu.shared_aligned) \
+ *(.data.percpu) \
+ *(.data.percpu.page_aligned) \
+ ____per_cpu_size = .; \
+ } \
+ . = __per_cpu_load + ____per_cpu_size; \
+ data : { } :data
+#else
#define PERCPU(align) \
. = ALIGN(align); \
__per_cpu_start = .; \
.data.percpu : AT(ADDR(.data.percpu) - LOAD_OFFSET) { \
+ *(.data.percpu.page_aligned) \
*(.data.percpu) \
*(.data.percpu.shared_aligned) \
} \
__per_cpu_end = .;
+#endif
diff --git a/include/asm-ia64/Kbuild b/include/asm-ia64/Kbuild
index eb24a3f47caa..ccbe8ae47a61 100644
--- a/include/asm-ia64/Kbuild
+++ b/include/asm-ia64/Kbuild
@@ -5,12 +5,12 @@ header-y += fpu.h
header-y += fpswa.h
header-y += ia64regs.h
header-y += intel_intrin.h
-header-y += intrinsics.h
header-y += perfmon_default_smpl.h
header-y += ptrace_offsets.h
header-y += rse.h
header-y += ucontext.h
unifdef-y += gcc_intrin.h
+unifdef-y += intrinsics.h
unifdef-y += perfmon.h
unifdef-y += ustack.h
diff --git a/include/asm-ia64/gcc_intrin.h b/include/asm-ia64/gcc_intrin.h
index 2fe292c275fe..0f5b55921758 100644
--- a/include/asm-ia64/gcc_intrin.h
+++ b/include/asm-ia64/gcc_intrin.h
@@ -32,7 +32,7 @@ extern void ia64_bad_param_for_getreg (void);
register unsigned long ia64_r13 asm ("r13") __used;
#endif
-#define ia64_setreg(regnum, val) \
+#define ia64_native_setreg(regnum, val) \
({ \
switch (regnum) { \
case _IA64_REG_PSR_L: \
@@ -61,7 +61,7 @@ register unsigned long ia64_r13 asm ("r13") __used;
} \
})
-#define ia64_getreg(regnum) \
+#define ia64_native_getreg(regnum) \
({ \
__u64 ia64_intri_res; \
\
@@ -385,7 +385,7 @@ register unsigned long ia64_r13 asm ("r13") __used;
#define ia64_invala() asm volatile ("invala" ::: "memory")
-#define ia64_thash(addr) \
+#define ia64_native_thash(addr) \
({ \
__u64 ia64_intri_res; \
asm volatile ("thash %0=%1" : "=r"(ia64_intri_res) : "r" (addr)); \
@@ -438,10 +438,10 @@ register unsigned long ia64_r13 asm ("r13") __used;
#define ia64_set_pmd(index, val) \
asm volatile ("mov pmd[%0]=%1" :: "r"(index), "r"(val) : "memory")
-#define ia64_set_rr(index, val) \
+#define ia64_native_set_rr(index, val) \
asm volatile ("mov rr[%0]=%1" :: "r"(index), "r"(val) : "memory");
-#define ia64_get_cpuid(index) \
+#define ia64_native_get_cpuid(index) \
({ \
__u64 ia64_intri_res; \
asm volatile ("mov %0=cpuid[%r1]" : "=r"(ia64_intri_res) : "rO"(index)); \
@@ -477,33 +477,33 @@ register unsigned long ia64_r13 asm ("r13") __used;
})
-#define ia64_get_pmd(index) \
+#define ia64_native_get_pmd(index) \
({ \
__u64 ia64_intri_res; \
asm volatile ("mov %0=pmd[%1]" : "=r"(ia64_intri_res) : "r"(index)); \
ia64_intri_res; \
})
-#define ia64_get_rr(index) \
+#define ia64_native_get_rr(index) \
({ \
__u64 ia64_intri_res; \
asm volatile ("mov %0=rr[%1]" : "=r"(ia64_intri_res) : "r" (index)); \
ia64_intri_res; \
})
-#define ia64_fc(addr) asm volatile ("fc %0" :: "r"(addr) : "memory")
+#define ia64_native_fc(addr) asm volatile ("fc %0" :: "r"(addr) : "memory")
#define ia64_sync_i() asm volatile (";; sync.i" ::: "memory")
-#define ia64_ssm(mask) asm volatile ("ssm %0":: "i"((mask)) : "memory")
-#define ia64_rsm(mask) asm volatile ("rsm %0":: "i"((mask)) : "memory")
+#define ia64_native_ssm(mask) asm volatile ("ssm %0":: "i"((mask)) : "memory")
+#define ia64_native_rsm(mask) asm volatile ("rsm %0":: "i"((mask)) : "memory")
#define ia64_sum(mask) asm volatile ("sum %0":: "i"((mask)) : "memory")
#define ia64_rum(mask) asm volatile ("rum %0":: "i"((mask)) : "memory")
#define ia64_ptce(addr) asm volatile ("ptc.e %0" :: "r"(addr))
-#define ia64_ptcga(addr, size) \
+#define ia64_native_ptcga(addr, size) \
do { \
asm volatile ("ptc.ga %0,%1" :: "r"(addr), "r"(size) : "memory"); \
ia64_dv_serialize_data(); \
@@ -608,7 +608,7 @@ do { \
} \
})
-#define ia64_intrin_local_irq_restore(x) \
+#define ia64_native_intrin_local_irq_restore(x) \
do { \
asm volatile (";; cmp.ne p6,p7=%0,r0;;" \
"(p6) ssm psr.i;" \
diff --git a/include/asm-ia64/hw_irq.h b/include/asm-ia64/hw_irq.h
index 76366dc9c1a0..5c99cbcb8a0d 100644
--- a/include/asm-ia64/hw_irq.h
+++ b/include/asm-ia64/hw_irq.h
@@ -15,7 +15,11 @@
#include <asm/ptrace.h>
#include <asm/smp.h>
+#ifndef CONFIG_PARAVIRT
typedef u8 ia64_vector;
+#else
+typedef u16 ia64_vector;
+#endif
/*
* 0 special
@@ -104,13 +108,24 @@ DECLARE_PER_CPU(int[IA64_NUM_VECTORS], vector_irq);
extern struct hw_interrupt_type irq_type_ia64_lsapic; /* CPU-internal interrupt controller */
+#ifdef CONFIG_PARAVIRT_GUEST
+#include <asm/paravirt.h>
+#else
+#define ia64_register_ipi ia64_native_register_ipi
+#define assign_irq_vector ia64_native_assign_irq_vector
+#define free_irq_vector ia64_native_free_irq_vector
+#define register_percpu_irq ia64_native_register_percpu_irq
+#define ia64_resend_irq ia64_native_resend_irq
+#endif
+
+extern void ia64_native_register_ipi(void);
extern int bind_irq_vector(int irq, int vector, cpumask_t domain);
-extern int assign_irq_vector (int irq); /* allocate a free vector */
-extern void free_irq_vector (int vector);
+extern int ia64_native_assign_irq_vector (int irq); /* allocate a free vector */
+extern void ia64_native_free_irq_vector (int vector);
extern int reserve_irq_vector (int vector);
extern void __setup_vector_irq(int cpu);
extern void ia64_send_ipi (int cpu, int vector, int delivery_mode, int redirect);
-extern void register_percpu_irq (ia64_vector vec, struct irqaction *action);
+extern void ia64_native_register_percpu_irq (ia64_vector vec, struct irqaction *action);
extern int check_irq_used (int irq);
extern void destroy_and_reserve_irq (unsigned int irq);
@@ -122,7 +137,7 @@ static inline int irq_prepare_move(int irq, int cpu) { return 0; }
static inline void irq_complete_move(unsigned int irq) {}
#endif
-static inline void ia64_resend_irq(unsigned int vector)
+static inline void ia64_native_resend_irq(unsigned int vector)
{
platform_send_ipi(smp_processor_id(), vector, IA64_IPI_DM_INT, 0);
}
diff --git a/include/asm-ia64/intel_intrin.h b/include/asm-ia64/intel_intrin.h
index a520d103d808..53cec577558a 100644
--- a/include/asm-ia64/intel_intrin.h
+++ b/include/asm-ia64/intel_intrin.h
@@ -16,8 +16,8 @@
* intrinsic
*/
-#define ia64_getreg __getReg
-#define ia64_setreg __setReg
+#define ia64_native_getreg __getReg
+#define ia64_native_setreg __setReg
#define ia64_hint __hint
#define ia64_hint_pause __hint_pause
@@ -39,10 +39,10 @@
#define ia64_invala_fr __invala_fr
#define ia64_nop __nop
#define ia64_sum __sum
-#define ia64_ssm __ssm
+#define ia64_native_ssm __ssm
#define ia64_rum __rum
-#define ia64_rsm __rsm
-#define ia64_fc __fc
+#define ia64_native_rsm __rsm
+#define ia64_native_fc __fc
#define ia64_ldfs __ldfs
#define ia64_ldfd __ldfd
@@ -88,16 +88,17 @@
__setIndReg(_IA64_REG_INDR_PMC, index, val)
#define ia64_set_pmd(index, val) \
__setIndReg(_IA64_REG_INDR_PMD, index, val)
-#define ia64_set_rr(index, val) \
+#define ia64_native_set_rr(index, val) \
__setIndReg(_IA64_REG_INDR_RR, index, val)
-#define ia64_get_cpuid(index) __getIndReg(_IA64_REG_INDR_CPUID, index)
-#define __ia64_get_dbr(index) __getIndReg(_IA64_REG_INDR_DBR, index)
-#define ia64_get_ibr(index) __getIndReg(_IA64_REG_INDR_IBR, index)
-#define ia64_get_pkr(index) __getIndReg(_IA64_REG_INDR_PKR, index)
-#define ia64_get_pmc(index) __getIndReg(_IA64_REG_INDR_PMC, index)
-#define ia64_get_pmd(index) __getIndReg(_IA64_REG_INDR_PMD, index)
-#define ia64_get_rr(index) __getIndReg(_IA64_REG_INDR_RR, index)
+#define ia64_native_get_cpuid(index) \
+ __getIndReg(_IA64_REG_INDR_CPUID, index)
+#define __ia64_get_dbr(index) __getIndReg(_IA64_REG_INDR_DBR, index)
+#define ia64_get_ibr(index) __getIndReg(_IA64_REG_INDR_IBR, index)
+#define ia64_get_pkr(index) __getIndReg(_IA64_REG_INDR_PKR, index)
+#define ia64_get_pmc(index) __getIndReg(_IA64_REG_INDR_PMC, index)
+#define ia64_native_get_pmd(index) __getIndReg(_IA64_REG_INDR_PMD, index)
+#define ia64_native_get_rr(index) __getIndReg(_IA64_REG_INDR_RR, index)
#define ia64_srlz_d __dsrlz
#define ia64_srlz_i __isrlz
@@ -119,16 +120,16 @@
#define ia64_ld8_acq __ld8_acq
#define ia64_sync_i __synci
-#define ia64_thash __thash
-#define ia64_ttag __ttag
+#define ia64_native_thash __thash
+#define ia64_native_ttag __ttag
#define ia64_itcd __itcd
#define ia64_itci __itci
#define ia64_itrd __itrd
#define ia64_itri __itri
#define ia64_ptce __ptce
#define ia64_ptcl __ptcl
-#define ia64_ptcg __ptcg
-#define ia64_ptcga __ptcga
+#define ia64_native_ptcg __ptcg
+#define ia64_native_ptcga __ptcga
#define ia64_ptri __ptri
#define ia64_ptrd __ptrd
#define ia64_dep_mi _m64_dep_mi
@@ -145,13 +146,13 @@
#define ia64_lfetch_fault __lfetch_fault
#define ia64_lfetch_fault_excl __lfetch_fault_excl
-#define ia64_intrin_local_irq_restore(x) \
+#define ia64_native_intrin_local_irq_restore(x) \
do { \
if ((x) != 0) { \
- ia64_ssm(IA64_PSR_I); \
+ ia64_native_ssm(IA64_PSR_I); \
ia64_srlz_d(); \
} else { \
- ia64_rsm(IA64_PSR_I); \
+ ia64_native_rsm(IA64_PSR_I); \
} \
} while (0)
diff --git a/include/asm-ia64/intrinsics.h b/include/asm-ia64/intrinsics.h
index f1135b5b94c3..47d686dba1eb 100644
--- a/include/asm-ia64/intrinsics.h
+++ b/include/asm-ia64/intrinsics.h
@@ -18,6 +18,17 @@
# include <asm/gcc_intrin.h>
#endif
+#define ia64_native_get_psr_i() (ia64_native_getreg(_IA64_REG_PSR) & IA64_PSR_I)
+
+#define ia64_native_set_rr0_to_rr4(val0, val1, val2, val3, val4) \
+do { \
+ ia64_native_set_rr(0x0000000000000000UL, (val0)); \
+ ia64_native_set_rr(0x2000000000000000UL, (val1)); \
+ ia64_native_set_rr(0x4000000000000000UL, (val2)); \
+ ia64_native_set_rr(0x6000000000000000UL, (val3)); \
+ ia64_native_set_rr(0x8000000000000000UL, (val4)); \
+} while (0)
+
/*
* Force an unresolved reference if someone tries to use
* ia64_fetch_and_add() with a bad value.
@@ -183,4 +194,48 @@ extern long ia64_cmpxchg_called_with_bad_pointer (void);
#endif /* !CONFIG_IA64_DEBUG_CMPXCHG */
#endif
+
+#ifdef __KERNEL__
+#include <asm/paravirt_privop.h>
+#endif
+
+#ifndef __ASSEMBLY__
+#if defined(CONFIG_PARAVIRT) && defined(__KERNEL__)
+#define IA64_INTRINSIC_API(name) pv_cpu_ops.name
+#define IA64_INTRINSIC_MACRO(name) paravirt_ ## name
+#else
+#define IA64_INTRINSIC_API(name) ia64_native_ ## name
+#define IA64_INTRINSIC_MACRO(name) ia64_native_ ## name
+#endif
+
+/************************************************/
+/* Instructions paravirtualized for correctness */
+/************************************************/
+/* fc, thash, get_cpuid, get_pmd, get_eflags, set_eflags */
+/* Note that "ttag" and "cover" are also privilege-sensitive; "ttag"
+ * is not currently used (though it may be in a long-format VHPT system!)
+ */
+#define ia64_fc IA64_INTRINSIC_API(fc)
+#define ia64_thash IA64_INTRINSIC_API(thash)
+#define ia64_get_cpuid IA64_INTRINSIC_API(get_cpuid)
+#define ia64_get_pmd IA64_INTRINSIC_API(get_pmd)
+
+
+/************************************************/
+/* Instructions paravirtualized for performance */
+/************************************************/
+#define ia64_ssm IA64_INTRINSIC_MACRO(ssm)
+#define ia64_rsm IA64_INTRINSIC_MACRO(rsm)
+#define ia64_getreg IA64_INTRINSIC_API(getreg)
+#define ia64_setreg IA64_INTRINSIC_API(setreg)
+#define ia64_set_rr IA64_INTRINSIC_API(set_rr)
+#define ia64_get_rr IA64_INTRINSIC_API(get_rr)
+#define ia64_ptcga IA64_INTRINSIC_API(ptcga)
+#define ia64_get_psr_i IA64_INTRINSIC_API(get_psr_i)
+#define ia64_intrin_local_irq_restore \
+ IA64_INTRINSIC_API(intrin_local_irq_restore)
+#define ia64_set_rr0_to_rr4 IA64_INTRINSIC_API(set_rr0_to_rr4)
+
+#endif /* !__ASSEMBLY__ */
+
#endif /* _ASM_IA64_INTRINSICS_H */
diff --git a/include/asm-ia64/iosapic.h b/include/asm-ia64/iosapic.h
index a3a4288daae8..b9c102e15f22 100644
--- a/include/asm-ia64/iosapic.h
+++ b/include/asm-ia64/iosapic.h
@@ -55,13 +55,27 @@
#define NR_IOSAPICS 256
-static inline unsigned int __iosapic_read(char __iomem *iosapic, unsigned int reg)
+#ifdef CONFIG_PARAVIRT_GUEST
+#include <asm/paravirt.h>
+#else
+#define iosapic_pcat_compat_init ia64_native_iosapic_pcat_compat_init
+#define __iosapic_read __ia64_native_iosapic_read
+#define __iosapic_write __ia64_native_iosapic_write
+#define iosapic_get_irq_chip ia64_native_iosapic_get_irq_chip
+#endif
+
+extern void __init ia64_native_iosapic_pcat_compat_init(void);
+extern struct irq_chip *ia64_native_iosapic_get_irq_chip(unsigned long trigger);
+
+static inline unsigned int
+__ia64_native_iosapic_read(char __iomem *iosapic, unsigned int reg)
{
writel(reg, iosapic + IOSAPIC_REG_SELECT);
return readl(iosapic + IOSAPIC_WINDOW);
}
-static inline void __iosapic_write(char __iomem *iosapic, unsigned int reg, u32 val)
+static inline void
+__ia64_native_iosapic_write(char __iomem *iosapic, unsigned int reg, u32 val)
{
writel(reg, iosapic + IOSAPIC_REG_SELECT);
writel(val, iosapic + IOSAPIC_WINDOW);
diff --git a/include/asm-ia64/irq.h b/include/asm-ia64/irq.h
index a66d26827cbb..3627116fb0e2 100644
--- a/include/asm-ia64/irq.h
+++ b/include/asm-ia64/irq.h
@@ -13,14 +13,7 @@
#include <linux/types.h>
#include <linux/cpumask.h>
-
-#define NR_VECTORS 256
-
-#if (NR_VECTORS + 32 * NR_CPUS) < 1024
-#define NR_IRQS (NR_VECTORS + 32 * NR_CPUS)
-#else
-#define NR_IRQS 1024
-#endif
+#include <asm-ia64/nr-irqs.h>
static __inline__ int
irq_canonicalize (int irq)
diff --git a/include/asm-ia64/kvm_host.h b/include/asm-ia64/kvm_host.h
index c082c208c1f3..1efe513a9941 100644
--- a/include/asm-ia64/kvm_host.h
+++ b/include/asm-ia64/kvm_host.h
@@ -38,6 +38,7 @@
/* memory slots that does not exposed to userspace */
#define KVM_PRIVATE_MEM_SLOTS 4
+#define KVM_COALESCED_MMIO_PAGE_OFFSET 1
/* define exit reasons from vmm to kvm*/
#define EXIT_REASON_VM_PANIC 0
@@ -521,4 +522,6 @@ int kvm_emulate_halt(struct kvm_vcpu *vcpu);
int kvm_pal_emul(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run);
void kvm_sal_emul(struct kvm_vcpu *vcpu);
+static inline void kvm_inject_nmi(struct kvm_vcpu *vcpu) {}
+
#endif
diff --git a/include/asm-ia64/mmu_context.h b/include/asm-ia64/mmu_context.h
index cef2400983fa..040bc87db930 100644
--- a/include/asm-ia64/mmu_context.h
+++ b/include/asm-ia64/mmu_context.h
@@ -152,11 +152,7 @@ reload_context (nv_mm_context_t context)
# endif
#endif
- ia64_set_rr(0x0000000000000000UL, rr0);
- ia64_set_rr(0x2000000000000000UL, rr1);
- ia64_set_rr(0x4000000000000000UL, rr2);
- ia64_set_rr(0x6000000000000000UL, rr3);
- ia64_set_rr(0x8000000000000000UL, rr4);
+ ia64_set_rr0_to_rr4(rr0, rr1, rr2, rr3, rr4);
ia64_srlz_i(); /* srlz.i implies srlz.d */
}
diff --git a/include/asm-ia64/native/inst.h b/include/asm-ia64/native/inst.h
new file mode 100644
index 000000000000..c953a2ca4fce
--- /dev/null
+++ b/include/asm-ia64/native/inst.h
@@ -0,0 +1,175 @@
+/******************************************************************************
+ * include/asm-ia64/native/inst.h
+ *
+ * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * 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
+ *
+ */
+
+#define DO_SAVE_MIN IA64_NATIVE_DO_SAVE_MIN
+
+#define __paravirt_switch_to ia64_native_switch_to
+#define __paravirt_leave_syscall ia64_native_leave_syscall
+#define __paravirt_work_processed_syscall ia64_native_work_processed_syscall
+#define __paravirt_leave_kernel ia64_native_leave_kernel
+#define __paravirt_pending_syscall_end ia64_work_pending_syscall_end
+#define __paravirt_work_processed_syscall_target \
+ ia64_work_processed_syscall
+
+#ifdef CONFIG_PARAVIRT_GUEST_ASM_CLOBBER_CHECK
+# define PARAVIRT_POISON 0xdeadbeefbaadf00d
+# define CLOBBER(clob) \
+ ;; \
+ movl clob = PARAVIRT_POISON; \
+ ;;
+#else
+# define CLOBBER(clob) /* nothing */
+#endif
+
+#define MOV_FROM_IFA(reg) \
+ mov reg = cr.ifa
+
+#define MOV_FROM_ITIR(reg) \
+ mov reg = cr.itir
+
+#define MOV_FROM_ISR(reg) \
+ mov reg = cr.isr
+
+#define MOV_FROM_IHA(reg) \
+ mov reg = cr.iha
+
+#define MOV_FROM_IPSR(pred, reg) \
+(pred) mov reg = cr.ipsr
+
+#define MOV_FROM_IIM(reg) \
+ mov reg = cr.iim
+
+#define MOV_FROM_IIP(reg) \
+ mov reg = cr.iip
+
+#define MOV_FROM_IVR(reg, clob) \
+ mov reg = cr.ivr \
+ CLOBBER(clob)
+
+#define MOV_FROM_PSR(pred, reg, clob) \
+(pred) mov reg = psr \
+ CLOBBER(clob)
+
+#define MOV_TO_IFA(reg, clob) \
+ mov cr.ifa = reg \
+ CLOBBER(clob)
+
+#define MOV_TO_ITIR(pred, reg, clob) \
+(pred) mov cr.itir = reg \
+ CLOBBER(clob)
+
+#define MOV_TO_IHA(pred, reg, clob) \
+(pred) mov cr.iha = reg \
+ CLOBBER(clob)
+
+#define MOV_TO_IPSR(pred, reg, clob) \
+(pred) mov cr.ipsr = reg \
+ CLOBBER(clob)
+
+#define MOV_TO_IFS(pred, reg, clob) \
+(pred) mov cr.ifs = reg \
+ CLOBBER(clob)
+
+#define MOV_TO_IIP(reg, clob) \
+ mov cr.iip = reg \
+ CLOBBER(clob)
+
+#define MOV_TO_KR(kr, reg, clob0, clob1) \
+ mov IA64_KR(kr) = reg \
+ CLOBBER(clob0) \
+ CLOBBER(clob1)
+
+#define ITC_I(pred, reg, clob) \
+(pred) itc.i reg \
+ CLOBBER(clob)
+
+#define ITC_D(pred, reg, clob) \
+(pred) itc.d reg \
+ CLOBBER(clob)
+
+#define ITC_I_AND_D(pred_i, pred_d, reg, clob) \
+(pred_i) itc.i reg; \
+(pred_d) itc.d reg \
+ CLOBBER(clob)
+
+#define THASH(pred, reg0, reg1, clob) \
+(pred) thash reg0 = reg1 \
+ CLOBBER(clob)
+
+#define SSM_PSR_IC_AND_DEFAULT_BITS_AND_SRLZ_I(clob0, clob1) \
+ ssm psr.ic | PSR_DEFAULT_BITS \
+ CLOBBER(clob0) \
+ CLOBBER(clob1) \
+ ;; \
+ srlz.i /* guarantee that interruption collectin is on */ \
+ ;;
+
+#define SSM_PSR_IC_AND_SRLZ_D(clob0, clob1) \
+ ssm psr.ic \
+ CLOBBER(clob0) \
+ CLOBBER(clob1) \
+ ;; \
+ srlz.d
+
+#define RSM_PSR_IC(clob) \
+ rsm psr.ic \
+ CLOBBER(clob)
+
+#define SSM_PSR_I(pred, pred_clob, clob) \
+(pred) ssm psr.i \
+ CLOBBER(clob)
+
+#define RSM_PSR_I(pred, clob0, clob1) \
+(pred) rsm psr.i \
+ CLOBBER(clob0) \
+ CLOBBER(clob1)
+
+#define RSM_PSR_I_IC(clob0, clob1, clob2) \
+ rsm psr.i | psr.ic \
+ CLOBBER(clob0) \
+ CLOBBER(clob1) \
+ CLOBBER(clob2)
+
+#define RSM_PSR_DT \
+ rsm psr.dt
+
+#define SSM_PSR_DT_AND_SRLZ_I \
+ ssm psr.dt \
+ ;; \
+ srlz.i
+
+#define BSW_0(clob0, clob1, clob2) \
+ bsw.0 \
+ CLOBBER(clob0) \
+ CLOBBER(clob1) \
+ CLOBBER(clob2)
+
+#define BSW_1(clob0, clob1) \
+ bsw.1 \
+ CLOBBER(clob0) \
+ CLOBBER(clob1)
+
+#define COVER \
+ cover
+
+#define RFI \
+ rfi
diff --git a/include/asm-ia64/native/irq.h b/include/asm-ia64/native/irq.h
new file mode 100644
index 000000000000..efe9ff74a3c4
--- /dev/null
+++ b/include/asm-ia64/native/irq.h
@@ -0,0 +1,35 @@
+/******************************************************************************
+ * include/asm-ia64/native/irq.h
+ *
+ * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * 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
+ *
+ * moved from linux/include/asm-ia64/irq.h.
+ */
+
+#ifndef _ASM_IA64_NATIVE_IRQ_H
+#define _ASM_IA64_NATIVE_IRQ_H
+
+#define NR_VECTORS 256
+
+#if (NR_VECTORS + 32 * NR_CPUS) < 1024
+#define IA64_NATIVE_NR_IRQS (NR_VECTORS + 32 * NR_CPUS)
+#else
+#define IA64_NATIVE_NR_IRQS 1024
+#endif
+
+#endif /* _ASM_IA64_NATIVE_IRQ_H */
diff --git a/include/asm-ia64/paravirt.h b/include/asm-ia64/paravirt.h
new file mode 100644
index 000000000000..1b4df129f579
--- /dev/null
+++ b/include/asm-ia64/paravirt.h
@@ -0,0 +1,255 @@
+/******************************************************************************
+ * include/asm-ia64/paravirt.h
+ *
+ * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * 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
+ *
+ */
+
+
+#ifndef __ASM_PARAVIRT_H
+#define __ASM_PARAVIRT_H
+
+#ifdef CONFIG_PARAVIRT_GUEST
+
+#define PARAVIRT_HYPERVISOR_TYPE_DEFAULT 0
+#define PARAVIRT_HYPERVISOR_TYPE_XEN 1
+
+#ifndef __ASSEMBLY__
+
+#include <asm/hw_irq.h>
+#include <asm/meminit.h>
+
+/******************************************************************************
+ * general info
+ */
+struct pv_info {
+ unsigned int kernel_rpl;
+ int paravirt_enabled;
+ const char *name;
+};
+
+extern struct pv_info pv_info;
+
+static inline int paravirt_enabled(void)
+{
+ return pv_info.paravirt_enabled;
+}
+
+static inline unsigned int get_kernel_rpl(void)
+{
+ return pv_info.kernel_rpl;
+}
+
+/******************************************************************************
+ * initialization hooks.
+ */
+struct rsvd_region;
+
+struct pv_init_ops {
+ void (*banner)(void);
+
+ int (*reserve_memory)(struct rsvd_region *region);
+
+ void (*arch_setup_early)(void);
+ void (*arch_setup_console)(char **cmdline_p);
+ int (*arch_setup_nomca)(void);
+
+ void (*post_smp_prepare_boot_cpu)(void);
+};
+
+extern struct pv_init_ops pv_init_ops;
+
+static inline void paravirt_banner(void)
+{
+ if (pv_init_ops.banner)
+ pv_init_ops.banner();
+}
+
+static inline int paravirt_reserve_memory(struct rsvd_region *region)
+{
+ if (pv_init_ops.reserve_memory)
+ return pv_init_ops.reserve_memory(region);
+ return 0;
+}
+
+static inline void paravirt_arch_setup_early(void)
+{
+ if (pv_init_ops.arch_setup_early)
+ pv_init_ops.arch_setup_early();
+}
+
+static inline void paravirt_arch_setup_console(char **cmdline_p)
+{
+ if (pv_init_ops.arch_setup_console)
+ pv_init_ops.arch_setup_console(cmdline_p);
+}
+
+static inline int paravirt_arch_setup_nomca(void)
+{
+ if (pv_init_ops.arch_setup_nomca)
+ return pv_init_ops.arch_setup_nomca();
+ return 0;
+}
+
+static inline void paravirt_post_smp_prepare_boot_cpu(void)
+{
+ if (pv_init_ops.post_smp_prepare_boot_cpu)
+ pv_init_ops.post_smp_prepare_boot_cpu();
+}
+
+/******************************************************************************
+ * replacement of iosapic operations.
+ */
+
+struct pv_iosapic_ops {
+ void (*pcat_compat_init)(void);
+
+ struct irq_chip *(*get_irq_chip)(unsigned long trigger);
+
+ unsigned int (*__read)(char __iomem *iosapic, unsigned int reg);
+ void (*__write)(char __iomem *iosapic, unsigned int reg, u32 val);
+};
+
+extern struct pv_iosapic_ops pv_iosapic_ops;
+
+static inline void
+iosapic_pcat_compat_init(void)
+{
+ if (pv_iosapic_ops.pcat_compat_init)
+ pv_iosapic_ops.pcat_compat_init();
+}
+
+static inline struct irq_chip*
+iosapic_get_irq_chip(unsigned long trigger)
+{
+ return pv_iosapic_ops.get_irq_chip(trigger);
+}
+
+static inline unsigned int
+__iosapic_read(char __iomem *iosapic, unsigned int reg)
+{
+ return pv_iosapic_ops.__read(iosapic, reg);
+}
+
+static inline void
+__iosapic_write(char __iomem *iosapic, unsigned int reg, u32 val)
+{
+ return pv_iosapic_ops.__write(iosapic, reg, val);
+}
+
+/******************************************************************************
+ * replacement of irq operations.
+ */
+
+struct pv_irq_ops {
+ void (*register_ipi)(void);
+
+ int (*assign_irq_vector)(int irq);
+ void (*free_irq_vector)(int vector);
+
+ void (*register_percpu_irq)(ia64_vector vec,
+ struct irqaction *action);
+
+ void (*resend_irq)(unsigned int vector);
+};
+
+extern struct pv_irq_ops pv_irq_ops;
+
+static inline void
+ia64_register_ipi(void)
+{
+ pv_irq_ops.register_ipi();
+}
+
+static inline int
+assign_irq_vector(int irq)
+{
+ return pv_irq_ops.assign_irq_vector(irq);
+}
+
+static inline void
+free_irq_vector(int vector)
+{
+ return pv_irq_ops.free_irq_vector(vector);
+}
+
+static inline void
+register_percpu_irq(ia64_vector vec, struct irqaction *action)
+{
+ pv_irq_ops.register_percpu_irq(vec, action);
+}
+
+static inline void
+ia64_resend_irq(unsigned int vector)
+{
+ pv_irq_ops.resend_irq(vector);
+}
+
+/******************************************************************************
+ * replacement of time operations.
+ */
+
+extern struct itc_jitter_data_t itc_jitter_data;
+extern volatile int time_keeper_id;
+
+struct pv_time_ops {
+ void (*init_missing_ticks_accounting)(int cpu);
+ int (*do_steal_accounting)(unsigned long *new_itm);
+
+ void (*clocksource_resume)(void);
+};
+
+extern struct pv_time_ops pv_time_ops;
+
+static inline void
+paravirt_init_missing_ticks_accounting(int cpu)
+{
+ if (pv_time_ops.init_missing_ticks_accounting)
+ pv_time_ops.init_missing_ticks_accounting(cpu);
+}
+
+static inline int
+paravirt_do_steal_accounting(unsigned long *new_itm)
+{
+ return pv_time_ops.do_steal_accounting(new_itm);
+}
+
+#endif /* !__ASSEMBLY__ */
+
+#else
+/* fallback for native case */
+
+#ifndef __ASSEMBLY__
+
+#define paravirt_banner() do { } while (0)
+#define paravirt_reserve_memory(region) 0
+
+#define paravirt_arch_setup_early() do { } while (0)
+#define paravirt_arch_setup_console(cmdline_p) do { } while (0)
+#define paravirt_arch_setup_nomca() 0
+#define paravirt_post_smp_prepare_boot_cpu() do { } while (0)
+
+#define paravirt_init_missing_ticks_accounting(cpu) do { } while (0)
+#define paravirt_do_steal_accounting(new_itm) 0
+
+#endif /* __ASSEMBLY__ */
+
+
+#endif /* CONFIG_PARAVIRT_GUEST */
+
+#endif /* __ASM_PARAVIRT_H */
diff --git a/include/asm-ia64/paravirt_privop.h b/include/asm-ia64/paravirt_privop.h
new file mode 100644
index 000000000000..52482e6940ac
--- /dev/null
+++ b/include/asm-ia64/paravirt_privop.h
@@ -0,0 +1,114 @@
+/******************************************************************************
+ * include/asm-ia64/paravirt_privops.h
+ *
+ * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp>
+ * VA Linux Systems Japan K.K.
+ *
+ * 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
+ *
+ */
+
+#ifndef _ASM_IA64_PARAVIRT_PRIVOP_H
+#define _ASM_IA64_PARAVIRT_PRIVOP_H
+
+#ifdef CONFIG_PARAVIRT
+
+#ifndef __ASSEMBLY__
+
+#include <linux/types.h>
+#include <asm/kregs.h> /* for IA64_PSR_I */
+
+/******************************************************************************
+ * replacement of intrinsics operations.
+ */
+
+struct pv_cpu_ops {
+ void (*fc)(unsigned long addr);
+ unsigned long (*thash)(unsigned long addr);
+ unsigned long (*get_cpuid)(int index);
+ unsigned long (*get_pmd)(int index);
+ unsigned long (*getreg)(int reg);
+ void (*setreg)(int reg, unsigned long val);
+ void (*ptcga)(unsigned long addr, unsigned long size);
+ unsigned long (*get_rr)(unsigned long index);
+ void (*set_rr)(unsigned long index, unsigned long val);
+ void (*set_rr0_to_rr4)(unsigned long val0, unsigned long val1,
+ unsigned long val2, unsigned long val3,
+ unsigned long val4);
+ void (*ssm_i)(void);
+ void (*rsm_i)(void);
+ unsigned long (*get_psr_i)(void);
+ void (*intrin_local_irq_restore)(unsigned long flags);
+};
+
+extern struct pv_cpu_ops pv_cpu_ops;
+
+extern void ia64_native_setreg_func(int regnum, unsigned long val);
+extern unsigned long ia64_native_getreg_func(int regnum);
+
+/************************************************/
+/* Instructions paravirtualized for performance */
+/************************************************/
+
+/* mask for ia64_native_ssm/rsm() must be constant.("i" constraing).
+ * static inline function doesn't satisfy it. */
+#define paravirt_ssm(mask) \
+ do { \
+ if ((mask) == IA64_PSR_I) \
+ pv_cpu_ops.ssm_i(); \
+ else \
+ ia64_native_ssm(mask); \
+ } while (0)
+
+#define paravirt_rsm(mask) \
+ do { \
+ if ((mask) == IA64_PSR_I) \
+ pv_cpu_ops.rsm_i(); \
+ else \
+ ia64_native_rsm(mask); \
+ } while (0)
+
+/******************************************************************************
+ * replacement of hand written assembly codes.
+ */
+struct pv_cpu_asm_switch {
+ unsigned long switch_to;
+ unsigned long leave_syscall;
+ unsigned long work_processed_syscall;
+ unsigned long leave_kernel;
+};
+void paravirt_cpu_asm_init(const struct pv_cpu_asm_switch *cpu_asm_switch);
+
+#endif /* __ASSEMBLY__ */
+
+#define IA64_PARAVIRT_ASM_FUNC(name) paravirt_ ## name
+
+#else
+
+/* fallback for native case */
+#define IA64_PARAVIRT_ASM_FUNC(name) ia64_native_ ## name
+
+#endif /* CONFIG_PARAVIRT */
+
+/* these routines utilize privilege-sensitive or performance-sensitive
+ * privileged instructions so the code must be replaced with
+ * paravirtualized versions */
+#define ia64_switch_to IA64_PARAVIRT_ASM_FUNC(switch_to)
+#define ia64_leave_syscall IA64_PARAVIRT_ASM_FUNC(leave_syscall)
+#define ia64_work_processed_syscall \
+ IA64_PARAVIRT_ASM_FUNC(work_processed_syscall)
+#define ia64_leave_kernel IA64_PARAVIRT_ASM_FUNC(leave_kernel)
+
+#endif /* _ASM_IA64_PARAVIRT_PRIVOP_H */
diff --git a/include/asm-ia64/smp.h b/include/asm-ia64/smp.h
index ec5f355fb7e3..2984e262fc6c 100644
--- a/include/asm-ia64/smp.h
+++ b/include/asm-ia64/smp.h
@@ -15,6 +15,7 @@
#include <linux/kernel.h>
#include <linux/cpumask.h>
#include <linux/bitops.h>
+#include <linux/irqreturn.h>
#include <asm/io.h>
#include <asm/param.h>
@@ -123,6 +124,7 @@ extern void __init smp_build_cpu_map(void);
extern void __init init_smp_config (void);
extern void smp_do_timer (struct pt_regs *regs);
+extern irqreturn_t handle_IPI(int irq, void *dev_id);
extern void smp_send_reschedule (int cpu);
extern void lock_ipi_calllock(void);
extern void unlock_ipi_calllock(void);
diff --git a/include/asm-ia64/system.h b/include/asm-ia64/system.h
index 26e250bfb912..927a381c20ca 100644
--- a/include/asm-ia64/system.h
+++ b/include/asm-ia64/system.h
@@ -26,6 +26,7 @@
*/
#define KERNEL_START (GATE_ADDR+__IA64_UL_CONST(0x100000000))
#define PERCPU_ADDR (-PERCPU_PAGE_SIZE)
+#define LOAD_OFFSET (KERNEL_START - KERNEL_TR_PAGE_SIZE)
#ifndef __ASSEMBLY__
@@ -122,10 +123,16 @@ extern struct ia64_boot_param {
* write a floating-point register right before reading the PSR
* and that writes to PSR.mfl
*/
+#ifdef CONFIG_PARAVIRT
+#define __local_save_flags() ia64_get_psr_i()
+#else
+#define __local_save_flags() ia64_getreg(_IA64_REG_PSR)
+#endif
+
#define __local_irq_save(x) \
do { \
ia64_stop(); \
- (x) = ia64_getreg(_IA64_REG_PSR); \
+ (x) = __local_save_flags(); \
ia64_stop(); \
ia64_rsm(IA64_PSR_I); \
} while (0)
@@ -173,7 +180,7 @@ do { \
#endif /* !CONFIG_IA64_DEBUG_IRQ */
#define local_irq_enable() ({ ia64_stop(); ia64_ssm(IA64_PSR_I); ia64_srlz_d(); })
-#define local_save_flags(flags) ({ ia64_stop(); (flags) = ia64_getreg(_IA64_REG_PSR); })
+#define local_save_flags(flags) ({ ia64_stop(); (flags) = __local_save_flags(); })
#define irqs_disabled() \
({ \
diff --git a/include/asm-powerpc/ftrace.h b/include/asm-powerpc/ftrace.h
new file mode 100644
index 000000000000..b1bfa704b6e5
--- /dev/null
+++ b/include/asm-powerpc/ftrace.h
@@ -0,0 +1,6 @@
+#ifndef _ASM_POWERPC_FTRACE
+#define _ASM_POWERPC_FTRACE
+
+extern void _mcount(void);
+
+#endif
diff --git a/include/asm-powerpc/hw_irq.h b/include/asm-powerpc/hw_irq.h
index ad8c9f7fd0e3..f75a5fc64d2e 100644
--- a/include/asm-powerpc/hw_irq.h
+++ b/include/asm-powerpc/hw_irq.h
@@ -59,6 +59,11 @@ extern void iseries_handle_interrupts(void);
get_paca()->hard_enabled = 0; \
} while(0)
+static inline int irqs_disabled_flags(unsigned long flags)
+{
+ return flags == 0;
+}
+
#else
#if defined(CONFIG_BOOKE)
@@ -113,6 +118,11 @@ static inline void local_irq_save_ptr(unsigned long *flags)
#define hard_irq_enable() local_irq_enable()
#define hard_irq_disable() local_irq_disable()
+static inline int irqs_disabled_flags(unsigned long flags)
+{
+ return (flags & MSR_EE) == 0;
+}
+
#endif /* CONFIG_PPC64 */
/*
diff --git a/include/asm-powerpc/kvm_host.h b/include/asm-powerpc/kvm_host.h
index 81a69d711017..2655e2a4831e 100644
--- a/include/asm-powerpc/kvm_host.h
+++ b/include/asm-powerpc/kvm_host.h
@@ -31,6 +31,8 @@
/* memory slots that does not exposed to userspace */
#define KVM_PRIVATE_MEM_SLOTS 4
+#define KVM_COALESCED_MMIO_PAGE_OFFSET 1
+
/* We don't currently support large pages. */
#define KVM_PAGES_PER_HPAGE (1<<31)
diff --git a/include/asm-powerpc/kvm_ppc.h b/include/asm-powerpc/kvm_ppc.h
index b35a7e3ef978..5a21115228af 100644
--- a/include/asm-powerpc/kvm_ppc.h
+++ b/include/asm-powerpc/kvm_ppc.h
@@ -57,6 +57,7 @@ extern int kvmppc_handle_store(struct kvm_run *run, struct kvm_vcpu *vcpu,
extern int kvmppc_emulate_instruction(struct kvm_run *run,
struct kvm_vcpu *vcpu);
+extern int kvmppc_emulate_mmio(struct kvm_run *run, struct kvm_vcpu *vcpu);
extern void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gfn_t gfn,
u64 asid, u32 flags);
diff --git a/include/asm-s390/airq.h b/include/asm-s390/airq.h
index 41d028cb52a4..1ac80d6b0588 100644
--- a/include/asm-s390/airq.h
+++ b/include/asm-s390/airq.h
@@ -13,7 +13,7 @@
typedef void (*adapter_int_handler_t)(void *, void *);
-void *s390_register_adapter_interrupt(adapter_int_handler_t, void *);
-void s390_unregister_adapter_interrupt(void *);
+void *s390_register_adapter_interrupt(adapter_int_handler_t, void *, u8);
+void s390_unregister_adapter_interrupt(void *, u8);
#endif /* _ASM_S390_AIRQ_H */
diff --git a/include/asm-s390/ccwdev.h b/include/asm-s390/ccwdev.h
index 066aa70518ce..ba007d8df941 100644
--- a/include/asm-s390/ccwdev.h
+++ b/include/asm-s390/ccwdev.h
@@ -12,6 +12,7 @@
#include <linux/device.h>
#include <linux/mod_devicetable.h>
+#include <asm/fcx.h>
/* structs from asm/cio.h */
struct irb;
@@ -157,6 +158,17 @@ extern int ccw_device_start_timeout_key(struct ccw_device *, struct ccw1 *,
extern int ccw_device_resume(struct ccw_device *);
extern int ccw_device_halt(struct ccw_device *, unsigned long);
extern int ccw_device_clear(struct ccw_device *, unsigned long);
+int ccw_device_tm_start_key(struct ccw_device *cdev, struct tcw *tcw,
+ unsigned long intparm, u8 lpm, u8 key);
+int ccw_device_tm_start_key(struct ccw_device *, struct tcw *,
+ unsigned long, u8, u8);
+int ccw_device_tm_start_timeout_key(struct ccw_device *, struct tcw *,
+ unsigned long, u8, u8, int);
+int ccw_device_tm_start(struct ccw_device *, struct tcw *,
+ unsigned long, u8);
+int ccw_device_tm_start_timeout(struct ccw_device *, struct tcw *,
+ unsigned long, u8, int);
+int ccw_device_tm_intrg(struct ccw_device *cdev);
extern int ccw_device_set_online(struct ccw_device *cdev);
extern int ccw_device_set_offline(struct ccw_device *cdev);
diff --git a/include/asm-s390/cio.h b/include/asm-s390/cio.h
index 0818ecd30ca6..6dccb071aec3 100644
--- a/include/asm-s390/cio.h
+++ b/include/asm-s390/cio.h
@@ -16,7 +16,7 @@
#define __MAX_CSSID 0
/**
- * struct scsw - subchannel status word
+ * struct cmd_scsw - command-mode subchannel status word
* @key: subchannel key
* @sctl: suspend control
* @eswf: esw format
@@ -38,7 +38,7 @@
* @cstat: subchannel status
* @count: residual count
*/
-struct scsw {
+struct cmd_scsw {
__u32 key : 4;
__u32 sctl : 1;
__u32 eswf : 1;
@@ -61,6 +61,114 @@ struct scsw {
__u32 count : 16;
} __attribute__ ((packed));
+/**
+ * struct tm_scsw - transport-mode subchannel status word
+ * @key: subchannel key
+ * @eswf: esw format
+ * @cc: deferred condition code
+ * @fmt: format
+ * @x: IRB-format control
+ * @q: interrogate-complete
+ * @ectl: extended control
+ * @pno: path not operational
+ * @fctl: function control
+ * @actl: activity control
+ * @stctl: status control
+ * @tcw: TCW address
+ * @dstat: device status
+ * @cstat: subchannel status
+ * @fcxs: FCX status
+ * @schxs: subchannel-extended status
+ */
+struct tm_scsw {
+ u32 key:4;
+ u32 :1;
+ u32 eswf:1;
+ u32 cc:2;
+ u32 fmt:3;
+ u32 x:1;
+ u32 q:1;
+ u32 :1;
+ u32 ectl:1;
+ u32 pno:1;
+ u32 :1;
+ u32 fctl:3;
+ u32 actl:7;
+ u32 stctl:5;
+ u32 tcw;
+ u32 dstat:8;
+ u32 cstat:8;
+ u32 fcxs:8;
+ u32 schxs:8;
+} __attribute__ ((packed));
+
+/**
+ * union scsw - subchannel status word
+ * @cmd: command-mode SCSW
+ * @tm: transport-mode SCSW
+ */
+union scsw {
+ struct cmd_scsw cmd;
+ struct tm_scsw tm;
+} __attribute__ ((packed));
+
+int scsw_is_tm(union scsw *scsw);
+u32 scsw_key(union scsw *scsw);
+u32 scsw_eswf(union scsw *scsw);
+u32 scsw_cc(union scsw *scsw);
+u32 scsw_ectl(union scsw *scsw);
+u32 scsw_pno(union scsw *scsw);
+u32 scsw_fctl(union scsw *scsw);
+u32 scsw_actl(union scsw *scsw);
+u32 scsw_stctl(union scsw *scsw);
+u32 scsw_dstat(union scsw *scsw);
+u32 scsw_cstat(union scsw *scsw);
+int scsw_is_solicited(union scsw *scsw);
+int scsw_is_valid_key(union scsw *scsw);
+int scsw_is_valid_eswf(union scsw *scsw);
+int scsw_is_valid_cc(union scsw *scsw);
+int scsw_is_valid_ectl(union scsw *scsw);
+int scsw_is_valid_pno(union scsw *scsw);
+int scsw_is_valid_fctl(union scsw *scsw);
+int scsw_is_valid_actl(union scsw *scsw);
+int scsw_is_valid_stctl(union scsw *scsw);
+int scsw_is_valid_dstat(union scsw *scsw);
+int scsw_is_valid_cstat(union scsw *scsw);
+int scsw_cmd_is_valid_key(union scsw *scsw);
+int scsw_cmd_is_valid_sctl(union scsw *scsw);
+int scsw_cmd_is_valid_eswf(union scsw *scsw);
+int scsw_cmd_is_valid_cc(union scsw *scsw);
+int scsw_cmd_is_valid_fmt(union scsw *scsw);
+int scsw_cmd_is_valid_pfch(union scsw *scsw);
+int scsw_cmd_is_valid_isic(union scsw *scsw);
+int scsw_cmd_is_valid_alcc(union scsw *scsw);
+int scsw_cmd_is_valid_ssi(union scsw *scsw);
+int scsw_cmd_is_valid_zcc(union scsw *scsw);
+int scsw_cmd_is_valid_ectl(union scsw *scsw);
+int scsw_cmd_is_valid_pno(union scsw *scsw);
+int scsw_cmd_is_valid_fctl(union scsw *scsw);
+int scsw_cmd_is_valid_actl(union scsw *scsw);
+int scsw_cmd_is_valid_stctl(union scsw *scsw);
+int scsw_cmd_is_valid_dstat(union scsw *scsw);
+int scsw_cmd_is_valid_cstat(union scsw *scsw);
+int scsw_cmd_is_solicited(union scsw *scsw);
+int scsw_tm_is_valid_key(union scsw *scsw);
+int scsw_tm_is_valid_eswf(union scsw *scsw);
+int scsw_tm_is_valid_cc(union scsw *scsw);
+int scsw_tm_is_valid_fmt(union scsw *scsw);
+int scsw_tm_is_valid_x(union scsw *scsw);
+int scsw_tm_is_valid_q(union scsw *scsw);
+int scsw_tm_is_valid_ectl(union scsw *scsw);
+int scsw_tm_is_valid_pno(union scsw *scsw);
+int scsw_tm_is_valid_fctl(union scsw *scsw);
+int scsw_tm_is_valid_actl(union scsw *scsw);
+int scsw_tm_is_valid_stctl(union scsw *scsw);
+int scsw_tm_is_valid_dstat(union scsw *scsw);
+int scsw_tm_is_valid_cstat(union scsw *scsw);
+int scsw_tm_is_valid_fcxs(union scsw *scsw);
+int scsw_tm_is_valid_schxs(union scsw *scsw);
+int scsw_tm_is_solicited(union scsw *scsw);
+
#define SCSW_FCTL_CLEAR_FUNC 0x1
#define SCSW_FCTL_HALT_FUNC 0x2
#define SCSW_FCTL_START_FUNC 0x4
@@ -303,7 +411,7 @@ struct esw3 {
* if applicable).
*/
struct irb {
- struct scsw scsw;
+ union scsw scsw;
union {
struct esw0 esw0;
struct esw1 esw1;
diff --git a/include/asm-s390/elf.h b/include/asm-s390/elf.h
index b3ac262c4582..3cad56923815 100644
--- a/include/asm-s390/elf.h
+++ b/include/asm-s390/elf.h
@@ -113,6 +113,9 @@
typedef s390_fp_regs elf_fpregset_t;
typedef s390_regs elf_gregset_t;
+typedef s390_fp_regs compat_elf_fpregset_t;
+typedef s390_compat_regs compat_elf_gregset_t;
+
#include <linux/sched.h> /* for task_struct */
#include <asm/system.h> /* for save_access_regs */
#include <asm/mmu_context.h>
@@ -123,6 +126,10 @@ typedef s390_regs elf_gregset_t;
#define elf_check_arch(x) \
(((x)->e_machine == EM_S390 || (x)->e_machine == EM_S390_OLD) \
&& (x)->e_ident[EI_CLASS] == ELF_CLASS)
+#define compat_elf_check_arch(x) \
+ (((x)->e_machine == EM_S390 || (x)->e_machine == EM_S390_OLD) \
+ && (x)->e_ident[EI_CLASS] == ELF_CLASS)
+#define compat_start_thread start_thread31
/* For SVR4/S390 the function pointer to be registered with `atexit` is
passed in R14. */
@@ -131,6 +138,7 @@ typedef s390_regs elf_gregset_t;
_r->gprs[14] = 0; \
} while (0)
+#define CORE_DUMP_USE_REGSET
#define USE_ELF_CORE_DUMP
#define ELF_EXEC_PAGESIZE 4096
@@ -140,44 +148,6 @@ typedef s390_regs elf_gregset_t;
that it will "exec", and that there is sufficient room for the brk. */
#define ELF_ET_DYN_BASE (STACK_TOP / 3 * 2)
-/* Wow, the "main" arch needs arch dependent functions too.. :) */
-
-/* regs is struct pt_regs, pr_reg is elf_gregset_t (which is
- now struct_user_regs, they are different) */
-
-static inline int dump_regs(struct pt_regs *ptregs, elf_gregset_t *regs)
-{
- memcpy(&regs->psw, &ptregs->psw, sizeof(regs->psw)+sizeof(regs->gprs));
- save_access_regs(regs->acrs);
- regs->orig_gpr2 = ptregs->orig_gpr2;
- return 1;
-}
-
-#define ELF_CORE_COPY_REGS(pr_reg, regs) dump_regs(regs, &pr_reg);
-
-static inline int dump_task_regs(struct task_struct *tsk, elf_gregset_t *regs)
-{
- struct pt_regs *ptregs = task_pt_regs(tsk);
- memcpy(&regs->psw, &ptregs->psw, sizeof(regs->psw)+sizeof(regs->gprs));
- memcpy(regs->acrs, tsk->thread.acrs, sizeof(regs->acrs));
- regs->orig_gpr2 = ptregs->orig_gpr2;
- return 1;
-}
-
-#define ELF_CORE_COPY_TASK_REGS(tsk, regs) dump_task_regs(tsk, regs)
-
-static inline int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpregs)
-{
- if (tsk == current)
- save_fp_regs(fpregs);
- else
- memcpy(fpregs, &tsk->thread.fp_regs, sizeof(elf_fpregset_t));
- return 1;
-}
-
-#define ELF_CORE_COPY_FPREGS(tsk, fpregs) dump_task_fpu(tsk, fpregs)
-
-
/* This yields a mask that user programs can use to figure out what
instruction set this CPU supports. */
@@ -204,7 +174,10 @@ do { \
set_personality(PER_SVR4); \
else if (current->personality != PER_LINUX32) \
set_personality(PER_LINUX); \
- clear_thread_flag(TIF_31BIT); \
+ if ((ex).e_ident[EI_CLASS] == ELFCLASS32) \
+ set_thread_flag(TIF_31BIT); \
+ else \
+ clear_thread_flag(TIF_31BIT); \
} while (0)
#endif /* __s390x__ */
diff --git a/include/asm-s390/etr.h b/include/asm-s390/etr.h
index b498f19bb9a7..80ef58c61970 100644
--- a/include/asm-s390/etr.h
+++ b/include/asm-s390/etr.h
@@ -122,7 +122,7 @@ struct etr_aib {
} __attribute__ ((packed,aligned(8)));
/* ETR interruption parameter */
-struct etr_interruption_parameter {
+struct etr_irq_parm {
unsigned int _pad0 : 8;
unsigned int pc0 : 1; /* port 0 state change */
unsigned int pc1 : 1; /* port 1 state change */
@@ -213,7 +213,46 @@ static inline int etr_ptff(void *ptff_block, unsigned int func)
#define ETR_PTFF_SGS 0x43 /* set gross steering rate */
/* Functions needed by the machine check handler */
-extern void etr_switch_to_local(void);
-extern void etr_sync_check(void);
+void etr_switch_to_local(void);
+void etr_sync_check(void);
+
+/* STP interruption parameter */
+struct stp_irq_parm {
+ unsigned int _pad0 : 14;
+ unsigned int tsc : 1; /* Timing status change */
+ unsigned int lac : 1; /* Link availability change */
+ unsigned int tcpc : 1; /* Time control parameter change */
+ unsigned int _pad2 : 15;
+} __attribute__ ((packed));
+
+#define STP_OP_SYNC 1
+#define STP_OP_CTRL 3
+
+struct stp_sstpi {
+ unsigned int rsvd0;
+ unsigned int rsvd1 : 8;
+ unsigned int stratum : 8;
+ unsigned int vbits : 16;
+ unsigned int leaps : 16;
+ unsigned int tmd : 4;
+ unsigned int ctn : 4;
+ unsigned int rsvd2 : 3;
+ unsigned int c : 1;
+ unsigned int tst : 4;
+ unsigned int tzo : 16;
+ unsigned int dsto : 16;
+ unsigned int ctrl : 16;
+ unsigned int rsvd3 : 16;
+ unsigned int tto;
+ unsigned int rsvd4;
+ unsigned int ctnid[3];
+ unsigned int rsvd5;
+ unsigned int todoff[4];
+ unsigned int rsvd6[48];
+} __attribute__ ((packed));
+
+/* Functions needed by the machine check handler */
+void stp_sync_check(void);
+void stp_island_check(void);
#endif /* __S390_ETR_H */
diff --git a/include/asm-s390/fcx.h b/include/asm-s390/fcx.h
new file mode 100644
index 000000000000..8be1f3a58042
--- /dev/null
+++ b/include/asm-s390/fcx.h
@@ -0,0 +1,311 @@
+/*
+ * Functions for assembling fcx enabled I/O control blocks.
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#ifndef _ASM_S390_FCX_H
+#define _ASM_S390_FCX_H _ASM_S390_FCX_H
+
+#include <linux/types.h>
+
+#define TCW_FORMAT_DEFAULT 0
+#define TCW_TIDAW_FORMAT_DEFAULT 0
+#define TCW_FLAGS_INPUT_TIDA 1 << (23 - 5)
+#define TCW_FLAGS_TCCB_TIDA 1 << (23 - 6)
+#define TCW_FLAGS_OUTPUT_TIDA 1 << (23 - 7)
+#define TCW_FLAGS_TIDAW_FORMAT(x) ((x) & 3) << (23 - 9)
+#define TCW_FLAGS_GET_TIDAW_FORMAT(x) (((x) >> (23 - 9)) & 3)
+
+/**
+ * struct tcw - Transport Control Word (TCW)
+ * @format: TCW format
+ * @flags: TCW flags
+ * @tccbl: Transport-Command-Control-Block Length
+ * @r: Read Operations
+ * @w: Write Operations
+ * @output: Output-Data Address
+ * @input: Input-Data Address
+ * @tsb: Transport-Status-Block Address
+ * @tccb: Transport-Command-Control-Block Address
+ * @output_count: Output Count
+ * @input_count: Input Count
+ * @intrg: Interrogate TCW Address
+ */
+struct tcw {
+ u32 format:2;
+ u32 :6;
+ u32 flags:24;
+ u32 :8;
+ u32 tccbl:6;
+ u32 r:1;
+ u32 w:1;
+ u32 :16;
+ u64 output;
+ u64 input;
+ u64 tsb;
+ u64 tccb;
+ u32 output_count;
+ u32 input_count;
+ u32 :32;
+ u32 :32;
+ u32 :32;
+ u32 intrg;
+} __attribute__ ((packed, aligned(64)));
+
+#define TIDAW_FLAGS_LAST 1 << (7 - 0)
+#define TIDAW_FLAGS_SKIP 1 << (7 - 1)
+#define TIDAW_FLAGS_DATA_INT 1 << (7 - 2)
+#define TIDAW_FLAGS_TTIC 1 << (7 - 3)
+#define TIDAW_FLAGS_INSERT_CBC 1 << (7 - 4)
+
+/**
+ * struct tidaw - Transport-Indirect-Addressing Word (TIDAW)
+ * @flags: TIDAW flags. Can be an arithmetic OR of the following constants:
+ * %TIDAW_FLAGS_LAST, %TIDAW_FLAGS_SKIP, %TIDAW_FLAGS_DATA_INT,
+ * %TIDAW_FLAGS_TTIC, %TIDAW_FLAGS_INSERT_CBC
+ * @count: Count
+ * @addr: Address
+ */
+struct tidaw {
+ u32 flags:8;
+ u32 :24;
+ u32 count;
+ u64 addr;
+} __attribute__ ((packed, aligned(16)));
+
+/**
+ * struct tsa_iostat - I/O-Status Transport-Status Area (IO-Stat TSA)
+ * @dev_time: Device Time
+ * @def_time: Defer Time
+ * @queue_time: Queue Time
+ * @dev_busy_time: Device-Busy Time
+ * @dev_act_time: Device-Active-Only Time
+ * @sense: Sense Data (if present)
+ */
+struct tsa_iostat {
+ u32 dev_time;
+ u32 def_time;
+ u32 queue_time;
+ u32 dev_busy_time;
+ u32 dev_act_time;
+ u8 sense[32];
+} __attribute__ ((packed));
+
+/**
+ * struct tsa_ddpcs - Device-Detected-Program-Check Transport-Status Area (DDPC TSA)
+ * @rc: Reason Code
+ * @rcq: Reason Code Qualifier
+ * @sense: Sense Data (if present)
+ */
+struct tsa_ddpc {
+ u32 :24;
+ u32 rc:8;
+ u8 rcq[16];
+ u8 sense[32];
+} __attribute__ ((packed));
+
+#define TSA_INTRG_FLAGS_CU_STATE_VALID 1 << (7 - 0)
+#define TSA_INTRG_FLAGS_DEV_STATE_VALID 1 << (7 - 1)
+#define TSA_INTRG_FLAGS_OP_STATE_VALID 1 << (7 - 2)
+
+/**
+ * struct tsa_intrg - Interrogate Transport-Status Area (Intrg. TSA)
+ * @format: Format
+ * @flags: Flags. Can be an arithmetic OR of the following constants:
+ * %TSA_INTRG_FLAGS_CU_STATE_VALID, %TSA_INTRG_FLAGS_DEV_STATE_VALID,
+ * %TSA_INTRG_FLAGS_OP_STATE_VALID
+ * @cu_state: Controle-Unit State
+ * @dev_state: Device State
+ * @op_state: Operation State
+ * @sd_info: State-Dependent Information
+ * @dl_id: Device-Level Identifier
+ * @dd_data: Device-Dependent Data
+ */
+struct tsa_intrg {
+ u32 format:8;
+ u32 flags:8;
+ u32 cu_state:8;
+ u32 dev_state:8;
+ u32 op_state:8;
+ u32 :24;
+ u8 sd_info[12];
+ u32 dl_id;
+ u8 dd_data[28];
+} __attribute__ ((packed));
+
+#define TSB_FORMAT_NONE 0
+#define TSB_FORMAT_IOSTAT 1
+#define TSB_FORMAT_DDPC 2
+#define TSB_FORMAT_INTRG 3
+
+#define TSB_FLAGS_DCW_OFFSET_VALID 1 << (7 - 0)
+#define TSB_FLAGS_COUNT_VALID 1 << (7 - 1)
+#define TSB_FLAGS_CACHE_MISS 1 << (7 - 2)
+#define TSB_FLAGS_TIME_VALID 1 << (7 - 3)
+#define TSB_FLAGS_FORMAT(x) ((x) & 7)
+#define TSB_FORMAT(t) ((t)->flags & 7)
+
+/**
+ * struct tsb - Transport-Status Block (TSB)
+ * @length: Length
+ * @flags: Flags. Can be an arithmetic OR of the following constants:
+ * %TSB_FLAGS_DCW_OFFSET_VALID, %TSB_FLAGS_COUNT_VALID, %TSB_FLAGS_CACHE_MISS,
+ * %TSB_FLAGS_TIME_VALID
+ * @dcw_offset: DCW Offset
+ * @count: Count
+ * @tsa: Transport-Status-Area
+ */
+struct tsb {
+ u32 length:8;
+ u32 flags:8;
+ u32 dcw_offset:16;
+ u32 count;
+ u32 :32;
+ union {
+ struct tsa_iostat iostat;
+ struct tsa_ddpc ddpc;
+ struct tsa_intrg intrg;
+ } __attribute__ ((packed)) tsa;
+} __attribute__ ((packed, aligned(8)));
+
+#define DCW_INTRG_FORMAT_DEFAULT 0
+
+#define DCW_INTRG_RC_UNSPECIFIED 0
+#define DCW_INTRG_RC_TIMEOUT 1
+
+#define DCW_INTRG_RCQ_UNSPECIFIED 0
+#define DCW_INTRG_RCQ_PRIMARY 1
+#define DCW_INTRG_RCQ_SECONDARY 2
+
+#define DCW_INTRG_FLAGS_MPM 1 < (7 - 0)
+#define DCW_INTRG_FLAGS_PPR 1 < (7 - 1)
+#define DCW_INTRG_FLAGS_CRIT 1 < (7 - 2)
+
+/**
+ * struct dcw_intrg_data - Interrogate DCW data
+ * @format: Format. Should be %DCW_INTRG_FORMAT_DEFAULT
+ * @rc: Reason Code. Can be one of %DCW_INTRG_RC_UNSPECIFIED,
+ * %DCW_INTRG_RC_TIMEOUT
+ * @rcq: Reason Code Qualifier: Can be one of %DCW_INTRG_RCQ_UNSPECIFIED,
+ * %DCW_INTRG_RCQ_PRIMARY, %DCW_INTRG_RCQ_SECONDARY
+ * @lpm: Logical-Path Mask
+ * @pam: Path-Available Mask
+ * @pim: Path-Installed Mask
+ * @timeout: Timeout
+ * @flags: Flags. Can be an arithmetic OR of %DCW_INTRG_FLAGS_MPM,
+ * %DCW_INTRG_FLAGS_PPR, %DCW_INTRG_FLAGS_CRIT
+ * @time: Time
+ * @prog_id: Program Identifier
+ * @prog_data: Program-Dependent Data
+ */
+struct dcw_intrg_data {
+ u32 format:8;
+ u32 rc:8;
+ u32 rcq:8;
+ u32 lpm:8;
+ u32 pam:8;
+ u32 pim:8;
+ u32 timeout:16;
+ u32 flags:8;
+ u32 :24;
+ u32 :32;
+ u64 time;
+ u64 prog_id;
+ u8 prog_data[0];
+} __attribute__ ((packed));
+
+#define DCW_FLAGS_CC 1 << (7 - 1)
+
+#define DCW_CMD_WRITE 0x01
+#define DCW_CMD_READ 0x02
+#define DCW_CMD_CONTROL 0x03
+#define DCW_CMD_SENSE 0x04
+#define DCW_CMD_SENSE_ID 0xe4
+#define DCW_CMD_INTRG 0x40
+
+/**
+ * struct dcw - Device-Command Word (DCW)
+ * @cmd: Command Code. Can be one of %DCW_CMD_WRITE, %DCW_CMD_READ,
+ * %DCW_CMD_CONTROL, %DCW_CMD_SENSE, %DCW_CMD_SENSE_ID, %DCW_CMD_INTRG
+ * @flags: Flags. Can be an arithmetic OR of %DCW_FLAGS_CC
+ * @cd_count: Control-Data Count
+ * @count: Count
+ * @cd: Control Data
+ */
+struct dcw {
+ u32 cmd:8;
+ u32 flags:8;
+ u32 :8;
+ u32 cd_count:8;
+ u32 count;
+ u8 cd[0];
+} __attribute__ ((packed));
+
+#define TCCB_FORMAT_DEFAULT 0x7f
+#define TCCB_MAX_DCW 30
+#define TCCB_MAX_SIZE (sizeof(struct tccb_tcah) + \
+ TCCB_MAX_DCW * sizeof(struct dcw) + \
+ sizeof(struct tccb_tcat))
+#define TCCB_SAC_DEFAULT 0xf901
+#define TCCB_SAC_INTRG 0xf902
+
+/**
+ * struct tccb_tcah - Transport-Command-Area Header (TCAH)
+ * @format: Format. Should be %TCCB_FORMAT_DEFAULT
+ * @tcal: Transport-Command-Area Length
+ * @sac: Service-Action Code. Can be one of %TCCB_SAC_DEFAULT, %TCCB_SAC_INTRG
+ * @prio: Priority
+ */
+struct tccb_tcah {
+ u32 format:8;
+ u32 :24;
+ u32 :24;
+ u32 tcal:8;
+ u32 sac:16;
+ u32 :8;
+ u32 prio:8;
+ u32 :32;
+} __attribute__ ((packed));
+
+/**
+ * struct tccb_tcat - Transport-Command-Area Trailer (TCAT)
+ * @count: Transport Count
+ */
+struct tccb_tcat {
+ u32 :32;
+ u32 count;
+} __attribute__ ((packed));
+
+/**
+ * struct tccb - (partial) Transport-Command-Control Block (TCCB)
+ * @tcah: TCAH
+ * @tca: Transport-Command Area
+ */
+struct tccb {
+ struct tccb_tcah tcah;
+ u8 tca[0];
+} __attribute__ ((packed, aligned(8)));
+
+struct tcw *tcw_get_intrg(struct tcw *tcw);
+void *tcw_get_data(struct tcw *tcw);
+struct tccb *tcw_get_tccb(struct tcw *tcw);
+struct tsb *tcw_get_tsb(struct tcw *tcw);
+
+void tcw_init(struct tcw *tcw, int r, int w);
+void tcw_finalize(struct tcw *tcw, int num_tidaws);
+
+void tcw_set_intrg(struct tcw *tcw, struct tcw *intrg_tcw);
+void tcw_set_data(struct tcw *tcw, void *data, int use_tidal);
+void tcw_set_tccb(struct tcw *tcw, struct tccb *tccb);
+void tcw_set_tsb(struct tcw *tcw, struct tsb *tsb);
+
+void tccb_init(struct tccb *tccb, size_t tccb_size, u32 sac);
+void tsb_init(struct tsb *tsb);
+struct dcw *tccb_add_dcw(struct tccb *tccb, size_t tccb_size, u8 cmd, u8 flags,
+ void *cd, u8 cd_count, u32 count);
+struct tidaw *tcw_add_tidaw(struct tcw *tcw, int num_tidaws, u8 flags,
+ void *addr, u32 count);
+
+#endif /* _ASM_S390_FCX_H */
diff --git a/include/asm-s390/isc.h b/include/asm-s390/isc.h
new file mode 100644
index 000000000000..fe56f7b445ea
--- /dev/null
+++ b/include/asm-s390/isc.h
@@ -0,0 +1,24 @@
+#ifndef _ASM_S390_ISC_H
+#define _ASM_S390_ISC_H
+
+#include <linux/types.h>
+
+/*
+ * I/O interruption subclasses used by drivers.
+ * Please add all used iscs here so that it is possible to distribute
+ * isc usage between drivers.
+ * Reminder: 0 is highest priority, 7 lowest.
+ */
+#define MAX_ISC 7
+
+/* Regular I/O interrupts. */
+#define IO_SCH_ISC 3 /* regular I/O subchannels */
+#define CONSOLE_ISC 1 /* console I/O subchannel */
+/* Adapter interrupts. */
+#define QDIO_AIRQ_ISC IO_SCH_ISC /* I/O subchannel in qdio mode */
+
+/* Functions for registration of I/O interruption subclasses */
+void isc_register(unsigned int isc);
+void isc_unregister(unsigned int isc);
+
+#endif /* _ASM_S390_ISC_H */
diff --git a/include/asm-s390/itcw.h b/include/asm-s390/itcw.h
new file mode 100644
index 000000000000..a9bc5c36b32a
--- /dev/null
+++ b/include/asm-s390/itcw.h
@@ -0,0 +1,30 @@
+/*
+ * Functions for incremental construction of fcx enabled I/O control blocks.
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#ifndef _ASM_S390_ITCW_H
+#define _ASM_S390_ITCW_H _ASM_S390_ITCW_H
+
+#include <linux/types.h>
+#include <asm/fcx.h>
+
+#define ITCW_OP_READ 0
+#define ITCW_OP_WRITE 1
+
+struct itcw;
+
+struct tcw *itcw_get_tcw(struct itcw *itcw);
+size_t itcw_calc_size(int intrg, int max_tidaws, int intrg_max_tidaws);
+struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg,
+ int max_tidaws, int intrg_max_tidaws);
+struct dcw *itcw_add_dcw(struct itcw *itcw, u8 cmd, u8 flags, void *cd,
+ u8 cd_count, u32 count);
+struct tidaw *itcw_add_tidaw(struct itcw *itcw, u8 flags, void *addr,
+ u32 count);
+void itcw_set_data(struct itcw *itcw, void *addr, int use_tidal);
+void itcw_finalize(struct itcw *itcw);
+
+#endif /* _ASM_S390_ITCW_H */
diff --git a/include/asm-s390/processor.h b/include/asm-s390/processor.h
index a00f79dd323b..4af80af2a88f 100644
--- a/include/asm-s390/processor.h
+++ b/include/asm-s390/processor.h
@@ -143,11 +143,19 @@ struct stack_frame {
/*
* Do necessary setup to start up a new thread.
*/
-#define start_thread(regs, new_psw, new_stackp) do { \
+#define start_thread(regs, new_psw, new_stackp) do { \
set_fs(USER_DS); \
regs->psw.mask = psw_user_bits; \
- regs->psw.addr = new_psw | PSW_ADDR_AMODE; \
- regs->gprs[15] = new_stackp ; \
+ regs->psw.addr = new_psw | PSW_ADDR_AMODE; \
+ regs->gprs[15] = new_stackp; \
+} while (0)
+
+#define start_thread31(regs, new_psw, new_stackp) do { \
+ set_fs(USER_DS); \
+ regs->psw.mask = psw_user32_bits; \
+ regs->psw.addr = new_psw | PSW_ADDR_AMODE; \
+ regs->gprs[15] = new_stackp; \
+ crst_table_downgrade(current->mm, 1UL << 31); \
} while (0)
/* Forward declaration, a strange C thing */
@@ -328,16 +336,6 @@ extern void (*s390_base_mcck_handler_fn)(void);
extern void (*s390_base_pgm_handler_fn)(void);
extern void (*s390_base_ext_handler_fn)(void);
-/*
- * CPU idle notifier chain.
- */
-#define S390_CPU_IDLE 0
-#define S390_CPU_NOT_IDLE 1
-
-struct notifier_block;
-int register_idle_notifier(struct notifier_block *nb);
-int unregister_idle_notifier(struct notifier_block *nb);
-
#define ARCH_LOW_ADDRESS_LIMIT 0x7fffffffUL
#endif
diff --git a/include/asm-s390/ptrace.h b/include/asm-s390/ptrace.h
index d7d4e2eb3e6f..af2c9ac28a07 100644
--- a/include/asm-s390/ptrace.h
+++ b/include/asm-s390/ptrace.h
@@ -215,6 +215,12 @@ typedef struct
unsigned long addr;
} __attribute__ ((aligned(8))) psw_t;
+typedef struct
+{
+ __u32 mask;
+ __u32 addr;
+} __attribute__ ((aligned(8))) psw_compat_t;
+
#ifndef __s390x__
#define PSW_MASK_PER 0x40000000UL
@@ -292,6 +298,15 @@ typedef struct
unsigned long orig_gpr2;
} s390_regs;
+typedef struct
+{
+ psw_compat_t psw;
+ __u32 gprs[NUM_GPRS];
+ __u32 acrs[NUM_ACRS];
+ __u32 orig_gpr2;
+} s390_compat_regs;
+
+
#ifdef __KERNEL__
#include <asm/setup.h>
#include <asm/page.h>
diff --git a/include/asm-s390/system.h b/include/asm-s390/system.h
index e0d4500d5f95..819e7d99ca0c 100644
--- a/include/asm-s390/system.h
+++ b/include/asm-s390/system.h
@@ -315,14 +315,14 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size)
asm volatile( \
" lctlg %1,%2,0(%0)\n" \
: : "a" (&array), "i" (low), "i" (high), \
- "m" (*(addrtype *)(array))); \
+ "m" (*(addrtype *)(&array))); \
})
#define __ctl_store(array, low, high) ({ \
typedef struct { char _[sizeof(array)]; } addrtype; \
asm volatile( \
" stctg %2,%3,0(%1)\n" \
- : "=m" (*(addrtype *)(array)) \
+ : "=m" (*(addrtype *)(&array)) \
: "a" (&array), "i" (low), "i" (high)); \
})
@@ -333,14 +333,14 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size)
asm volatile( \
" lctl %1,%2,0(%0)\n" \
: : "a" (&array), "i" (low), "i" (high), \
- "m" (*(addrtype *)(array))); \
+ "m" (*(addrtype *)(&array))); \
})
#define __ctl_store(array, low, high) ({ \
typedef struct { char _[sizeof(array)]; } addrtype; \
asm volatile( \
" stctl %2,%3,0(%1)\n" \
- : "=m" (*(addrtype *)(array)) \
+ : "=m" (*(addrtype *)(&array)) \
: "a" (&array), "i" (low), "i" (high)); \
})
diff --git a/include/asm-s390/timer.h b/include/asm-s390/timer.h
index adb34860a543..d98d79e35cd6 100644
--- a/include/asm-s390/timer.h
+++ b/include/asm-s390/timer.h
@@ -48,6 +48,18 @@ extern int del_virt_timer(struct vtimer_list *timer);
extern void init_cpu_vtimer(void);
extern void vtime_init(void);
+#ifdef CONFIG_VIRT_TIMER
+
+extern void vtime_start_cpu_timer(void);
+extern void vtime_stop_cpu_timer(void);
+
+#else
+
+static inline void vtime_start_cpu_timer(void) { }
+static inline void vtime_stop_cpu_timer(void) { }
+
+#endif /* CONFIG_VIRT_TIMER */
+
#endif /* __KERNEL__ */
#endif /* _ASM_S390_TIMER_H */
diff --git a/include/asm-sh/elf.h b/include/asm-sh/elf.h
index 05092da1aa59..f01449a8d378 100644
--- a/include/asm-sh/elf.h
+++ b/include/asm-sh/elf.h
@@ -1,10 +1,15 @@
#ifndef __ASM_SH_ELF_H
#define __ASM_SH_ELF_H
+#include <linux/utsname.h>
#include <asm/auxvec.h>
#include <asm/ptrace.h>
#include <asm/user.h>
+/* ELF header e_flags defines */
+#define EF_SH_PIC 0x100 /* -fpic */
+#define EF_SH_FDPIC 0x8000 /* -mfdpic */
+
/* SH (particularly SHcompact) relocation types */
#define R_SH_NONE 0
#define R_SH_DIR32 1
@@ -43,6 +48,28 @@
#define R_SH_RELATIVE 165
#define R_SH_GOTOFF 166
#define R_SH_GOTPC 167
+
+/* FDPIC relocs */
+#define R_SH_GOT20 70
+#define R_SH_GOTOFF20 71
+#define R_SH_GOTFUNCDESC 72
+#define R_SH_GOTFUNCDESC20 73
+#define R_SH_GOTOFFFUNCDESC 74
+#define R_SH_GOTOFFFUNCDESC20 75
+#define R_SH_FUNCDESC 76
+#define R_SH_FUNCDESC_VALUE 77
+
+#if 0 /* XXX - later .. */
+#define R_SH_GOT20 198
+#define R_SH_GOTOFF20 199
+#define R_SH_GOTFUNCDESC 200
+#define R_SH_GOTFUNCDESC20 201
+#define R_SH_GOTOFFFUNCDESC 202
+#define R_SH_GOTOFFFUNCDESC20 203
+#define R_SH_FUNCDESC 204
+#define R_SH_FUNCDESC_VALUE 205
+#endif
+
/* SHmedia relocs */
#define R_SH_IMM_LOW16 246
#define R_SH_IMM_LOW16_PCREL 247
@@ -77,9 +104,12 @@ typedef struct user_fpu_struct elf_fpregset_t;
/*
* This is used to ensure we don't load something for the wrong architecture.
*/
-#define elf_check_arch(x) ( (x)->e_machine == EM_SH )
+#define elf_check_arch(x) ((x)->e_machine == EM_SH)
+#define elf_check_fdpic(x) ((x)->e_flags & EF_SH_FDPIC)
+#define elf_check_const_displacement(x) ((x)->e_flags & EF_SH_PIC)
#define USE_ELF_CORE_DUMP
+#define ELF_FDPIC_CORE_EFLAGS EF_SH_FDPIC
#define ELF_EXEC_PAGESIZE PAGE_SIZE
/* This is the location that an ET_DYN program is loaded if exec'ed. Typical
@@ -136,6 +166,27 @@ typedef struct user_fpu_struct elf_fpregset_t;
_r->regs[8]=0; _r->regs[9]=0; _r->regs[10]=0; _r->regs[11]=0; \
_r->regs[12]=0; _r->regs[13]=0; _r->regs[14]=0; \
_r->sr = SR_FD; } while (0)
+
+#define ELF_FDPIC_PLAT_INIT(_r, _exec_map_addr, _interp_map_addr, \
+ _dynamic_addr) \
+do { \
+ _r->regs[0] = 0; \
+ _r->regs[1] = 0; \
+ _r->regs[2] = 0; \
+ _r->regs[3] = 0; \
+ _r->regs[4] = 0; \
+ _r->regs[5] = 0; \
+ _r->regs[6] = 0; \
+ _r->regs[7] = 0; \
+ _r->regs[8] = _exec_map_addr; \
+ _r->regs[9] = _interp_map_addr; \
+ _r->regs[10] = _dynamic_addr; \
+ _r->regs[11] = 0; \
+ _r->regs[12] = 0; \
+ _r->regs[13] = 0; \
+ _r->regs[14] = 0; \
+ _r->sr = SR_FD; \
+} while (0)
#endif
#define SET_PERSONALITY(ex, ibcs2) set_personality(PER_LINUX_32BIT)
diff --git a/include/asm-sh/mmu.h b/include/asm-sh/mmu.h
index eb0358c097d0..fdcb93bc6d11 100644
--- a/include/asm-sh/mmu.h
+++ b/include/asm-sh/mmu.h
@@ -12,6 +12,10 @@ typedef struct {
struct vm_list_struct *vmlist;
unsigned long end_brk;
#endif
+#ifdef CONFIG_BINFMT_ELF_FDPIC
+ unsigned long exec_fdpic_loadmap;
+ unsigned long interp_fdpic_loadmap;
+#endif
} mm_context_t;
/*
diff --git a/include/asm-sh/page.h b/include/asm-sh/page.h
index 304c30b5d947..3b305ca9a00f 100644
--- a/include/asm-sh/page.h
+++ b/include/asm-sh/page.h
@@ -12,6 +12,8 @@
# define PAGE_SHIFT 12
#elif defined(CONFIG_PAGE_SIZE_8KB)
# define PAGE_SHIFT 13
+#elif defined(CONFIG_PAGE_SIZE_16KB)
+# define PAGE_SHIFT 14
#elif defined(CONFIG_PAGE_SIZE_64KB)
# define PAGE_SHIFT 16
#else
diff --git a/include/asm-sh/pgtable_32.h b/include/asm-sh/pgtable_32.h
index cbc731d35c25..72ea209195bd 100644
--- a/include/asm-sh/pgtable_32.h
+++ b/include/asm-sh/pgtable_32.h
@@ -102,7 +102,9 @@
#define _PAGE_FLAGS_HARDWARE_MASK (PHYS_ADDR_MASK & ~(_PAGE_CLEAR_FLAGS))
/* Hardware flags, page size encoding */
-#if defined(CONFIG_X2TLB)
+#if !defined(CONFIG_MMU)
+# define _PAGE_FLAGS_HARD 0ULL
+#elif defined(CONFIG_X2TLB)
# if defined(CONFIG_PAGE_SIZE_4KB)
# define _PAGE_FLAGS_HARD _PAGE_EXT(_PAGE_EXT_ESZ0)
# elif defined(CONFIG_PAGE_SIZE_8KB)
diff --git a/include/asm-sh/processor.h b/include/asm-sh/processor.h
index b7c7ce80f03e..15d9f92ca383 100644
--- a/include/asm-sh/processor.h
+++ b/include/asm-sh/processor.h
@@ -2,6 +2,7 @@
#define __ASM_SH_PROCESSOR_H
#include <asm/cpu-features.h>
+#include <asm/segment.h>
#ifndef __ASSEMBLY__
/*
diff --git a/include/asm-sh/processor_32.h b/include/asm-sh/processor_32.h
index c09305d6a9d9..81628f144faf 100644
--- a/include/asm-sh/processor_32.h
+++ b/include/asm-sh/processor_32.h
@@ -113,10 +113,6 @@ struct thread_struct {
union sh_fpu_union fpu;
};
-typedef struct {
- unsigned long seg;
-} mm_segment_t;
-
/* Count of active tasks with UBC settings */
extern int ubc_usercnt;
diff --git a/include/asm-sh/processor_64.h b/include/asm-sh/processor_64.h
index 88a2edf8fa5d..fc7fc685ba27 100644
--- a/include/asm-sh/processor_64.h
+++ b/include/asm-sh/processor_64.h
@@ -166,10 +166,6 @@ struct thread_struct {
union sh_fpu_union fpu;
};
-typedef struct {
- unsigned long seg;
-} mm_segment_t;
-
#define INIT_MMAP \
{ &init_mm, 0, 0, NULL, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC, 1, NULL, NULL }
diff --git a/include/asm-sh/ptrace.h b/include/asm-sh/ptrace.h
index 8d6c92b3e770..a9f0c3f67f4a 100644
--- a/include/asm-sh/ptrace.h
+++ b/include/asm-sh/ptrace.h
@@ -87,6 +87,11 @@ struct pt_dspregs {
unsigned long mod;
};
+#define PTRACE_GETFDPIC 31 /* get the ELF fdpic loadmap address */
+
+#define PTRACE_GETFDPIC_EXEC 0 /* [addr] request the executable loadmap */
+#define PTRACE_GETFDPIC_INTERP 1 /* [addr] request the interpreter loadmap */
+
#define PTRACE_GETDSPREGS 55
#define PTRACE_SETDSPREGS 56
#endif
diff --git a/include/asm-sh/segment.h b/include/asm-sh/segment.h
index e417eab4c7d7..5e2725f4ac49 100644
--- a/include/asm-sh/segment.h
+++ b/include/asm-sh/segment.h
@@ -1,6 +1,34 @@
#ifndef __ASM_SH_SEGMENT_H
#define __ASM_SH_SEGMENT_H
-/* Only here because we have some old header files that expect it.. */
+#ifndef __ASSEMBLY__
+typedef struct {
+ unsigned long seg;
+} mm_segment_t;
+
+#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) })
+
+/*
+ * The fs value determines whether argument validity checking should be
+ * performed or not. If get_fs() == USER_DS, checking is performed, with
+ * get_fs() == KERNEL_DS, checking is bypassed.
+ *
+ * For historical reasons, these macros are grossly misnamed.
+ */
+#define KERNEL_DS MAKE_MM_SEG(0xFFFFFFFFUL)
+#ifdef CONFIG_MMU
+#define USER_DS MAKE_MM_SEG(PAGE_OFFSET)
+#else
+#define USER_DS KERNEL_DS
+#endif
+
+#define segment_eq(a,b) ((a).seg == (b).seg)
+
+#define get_ds() (KERNEL_DS)
+
+#define get_fs() (current_thread_info()->addr_limit)
+#define set_fs(x) (current_thread_info()->addr_limit = (x))
+
+#endif /* __ASSEMBLY__ */
#endif /* __ASM_SH_SEGMENT_H */
diff --git a/include/asm-sh/sh7763rdp.h b/include/asm-sh/sh7763rdp.h
new file mode 100644
index 000000000000..8750cc852977
--- /dev/null
+++ b/include/asm-sh/sh7763rdp.h
@@ -0,0 +1,54 @@
+#ifndef __ASM_SH_SH7763RDP_H
+#define __ASM_SH_SH7763RDP_H
+
+/*
+ * linux/include/asm-sh/sh7763drp.h
+ *
+ * Copyright (C) 2008 Renesas Solutions
+ * Copyright (C) 2008 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ */
+#include <asm/addrspace.h>
+
+/* clock control */
+#define MSTPCR1 0xFFC80038
+
+/* PORT */
+#define PORT_PSEL0 0xFFEF0070
+#define PORT_PSEL1 0xFFEF0072
+#define PORT_PSEL2 0xFFEF0074
+#define PORT_PSEL3 0xFFEF0076
+#define PORT_PSEL4 0xFFEF0078
+
+#define PORT_PACR 0xFFEF0000
+#define PORT_PCCR 0xFFEF0004
+#define PORT_PFCR 0xFFEF000A
+#define PORT_PGCR 0xFFEF000C
+#define PORT_PHCR 0xFFEF000E
+#define PORT_PICR 0xFFEF0010
+#define PORT_PJCR 0xFFEF0012
+#define PORT_PKCR 0xFFEF0014
+#define PORT_PLCR 0xFFEF0016
+#define PORT_PMCR 0xFFEF0018
+#define PORT_PNCR 0xFFEF001A
+
+/* FPGA */
+#define CPLD_BOARD_ID_ERV_REG 0xB1000000
+#define CPLD_CPLD_CMD_REG 0xB1000006
+
+/*
+ * USB SH7763RDP board can use Host only.
+ */
+#define USB_USBHSC 0xFFEC80f0
+
+/* arch/sh/boards/renesas/sh7763rdp/irq.c */
+void init_sh7763rdp_IRQ(void);
+int sh7763rdp_irq_demux(int irq);
+#define __IO_PREFIX sh7763rdp
+#include <asm/io_generic.h>
+
+#endif /* __ASM_SH_SH7763RDP_H */
diff --git a/include/asm-sh/thread_info.h b/include/asm-sh/thread_info.h
index c50e5d35fe84..0f69b4899309 100644
--- a/include/asm-sh/thread_info.h
+++ b/include/asm-sh/thread_info.h
@@ -38,6 +38,8 @@ struct thread_info {
#define THREAD_SIZE_ORDER (1)
#elif defined(CONFIG_PAGE_SIZE_8KB)
#define THREAD_SIZE_ORDER (1)
+#elif defined(CONFIG_PAGE_SIZE_16KB)
+#define THREAD_SIZE_ORDER (0)
#elif defined(CONFIG_PAGE_SIZE_64KB)
#define THREAD_SIZE_ORDER (0)
#else
diff --git a/include/asm-sh/uaccess.h b/include/asm-sh/uaccess.h
index b3440c305b5d..45c2c9b2993d 100644
--- a/include/asm-sh/uaccess.h
+++ b/include/asm-sh/uaccess.h
@@ -1,12 +1,171 @@
#ifndef __ASM_SH_UACCESS_H
#define __ASM_SH_UACCESS_H
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <asm/segment.h>
+
+#define VERIFY_READ 0
+#define VERIFY_WRITE 1
+
+#define __addr_ok(addr) \
+ ((unsigned long __force)(addr) < current_thread_info()->addr_limit.seg)
+
+/*
+ * __access_ok: Check if address with size is OK or not.
+ *
+ * Uhhuh, this needs 33-bit arithmetic. We have a carry..
+ *
+ * sum := addr + size; carry? --> flag = true;
+ * if (sum >= addr_limit) flag = true;
+ */
+#define __access_ok(addr, size) \
+ (__addr_ok((addr) + (size)))
+#define access_ok(type, addr, size) \
+ (__chk_user_ptr(addr), \
+ __access_ok((unsigned long __force)(addr), (size)))
+
+/*
+ * Uh, these should become the main single-value transfer routines ...
+ * They automatically use the right size if we just have the right
+ * pointer type ...
+ *
+ * As SuperH uses the same address space for kernel and user data, we
+ * can just do these as direct assignments.
+ *
+ * Careful to not
+ * (a) re-use the arguments for side effects (sizeof is ok)
+ * (b) require any knowledge of processes at this stage
+ */
+#define put_user(x,ptr) __put_user_check((x), (ptr), sizeof(*(ptr)))
+#define get_user(x,ptr) __get_user_check((x), (ptr), sizeof(*(ptr)))
+
+/*
+ * The "__xxx" versions do not do address space checking, useful when
+ * doing multiple accesses to the same area (the user has to do the
+ * checks by hand with "access_ok()")
+ */
+#define __put_user(x,ptr) __put_user_nocheck((x), (ptr), sizeof(*(ptr)))
+#define __get_user(x,ptr) __get_user_nocheck((x), (ptr), sizeof(*(ptr)))
+
+struct __large_struct { unsigned long buf[100]; };
+#define __m(x) (*(struct __large_struct __user *)(x))
+
+#define __get_user_nocheck(x,ptr,size) \
+({ \
+ long __gu_err; \
+ unsigned long __gu_val; \
+ const __typeof__(*(ptr)) __user *__gu_addr = (ptr); \
+ __chk_user_ptr(ptr); \
+ __get_user_size(__gu_val, __gu_addr, (size), __gu_err); \
+ (x) = (__typeof__(*(ptr)))__gu_val; \
+ __gu_err; \
+})
+
+#define __get_user_check(x,ptr,size) \
+({ \
+ long __gu_err = -EFAULT; \
+ unsigned long __gu_val = 0; \
+ const __typeof__(*(ptr)) *__gu_addr = (ptr); \
+ if (likely(access_ok(VERIFY_READ, __gu_addr, (size)))) \
+ __get_user_size(__gu_val, __gu_addr, (size), __gu_err); \
+ (x) = (__typeof__(*(ptr)))__gu_val; \
+ __gu_err; \
+})
+
+#define __put_user_nocheck(x,ptr,size) \
+({ \
+ long __pu_err; \
+ __typeof__(*(ptr)) __user *__pu_addr = (ptr); \
+ __chk_user_ptr(ptr); \
+ __put_user_size((x), __pu_addr, (size), __pu_err); \
+ __pu_err; \
+})
+
+#define __put_user_check(x,ptr,size) \
+({ \
+ long __pu_err = -EFAULT; \
+ __typeof__(*(ptr)) __user *__pu_addr = (ptr); \
+ if (likely(access_ok(VERIFY_WRITE, __pu_addr, size))) \
+ __put_user_size((x), __pu_addr, (size), \
+ __pu_err); \
+ __pu_err; \
+})
+
#ifdef CONFIG_SUPERH32
# include "uaccess_32.h"
#else
# include "uaccess_64.h"
#endif
+/* Generic arbitrary sized copy. */
+/* Return the number of bytes NOT copied */
+__kernel_size_t __copy_user(void *to, const void *from, __kernel_size_t n);
+
+static __always_inline unsigned long
+__copy_from_user(void *to, const void __user *from, unsigned long n)
+{
+ return __copy_user(to, (__force void *)from, n);
+}
+
+static __always_inline unsigned long __must_check
+__copy_to_user(void __user *to, const void *from, unsigned long n)
+{
+ return __copy_user((__force void *)to, from, n);
+}
+
+#define __copy_to_user_inatomic __copy_to_user
+#define __copy_from_user_inatomic __copy_from_user
+
+/*
+ * Clear the area and return remaining number of bytes
+ * (on failure. Usually it's 0.)
+ */
+__kernel_size_t __clear_user(void *addr, __kernel_size_t size);
+
+#define clear_user(addr,n) \
+({ \
+ void __user * __cl_addr = (addr); \
+ unsigned long __cl_size = (n); \
+ \
+ if (__cl_size && access_ok(VERIFY_WRITE, \
+ ((unsigned long)(__cl_addr)), __cl_size)) \
+ __cl_size = __clear_user(__cl_addr, __cl_size); \
+ \
+ __cl_size; \
+})
+
+/**
+ * strncpy_from_user: - Copy a NUL terminated string from userspace.
+ * @dst: Destination address, in kernel space. This buffer must be at
+ * least @count bytes long.
+ * @src: Source address, in user space.
+ * @count: Maximum number of bytes to copy, including the trailing NUL.
+ *
+ * Copies a NUL-terminated string from userspace to kernel space.
+ *
+ * On success, returns the length of the string (not including the trailing
+ * NUL).
+ *
+ * If access to userspace fails, returns -EFAULT (some data may have been
+ * copied).
+ *
+ * If @count is smaller than the length of the string, copies @count bytes
+ * and returns @count.
+ */
+#define strncpy_from_user(dest,src,count) \
+({ \
+ unsigned long __sfu_src = (unsigned long)(src); \
+ int __sfu_count = (int)(count); \
+ long __sfu_res = -EFAULT; \
+ \
+ if (__access_ok(__sfu_src, __sfu_count)) \
+ __sfu_res = __strncpy_from_user((unsigned long)(dest), \
+ __sfu_src, __sfu_count); \
+ \
+ __sfu_res; \
+})
+
static inline unsigned long
copy_from_user(void *to, const void __user *from, unsigned long n)
{
@@ -31,4 +190,67 @@ copy_to_user(void __user *to, const void *from, unsigned long n)
return __copy_size;
}
+/**
+ * strnlen_user: - Get the size of a string in user space.
+ * @s: The string to measure.
+ * @n: The maximum valid length
+ *
+ * Context: User context only. This function may sleep.
+ *
+ * Get the size of a NUL-terminated string in user space.
+ *
+ * Returns the size of the string INCLUDING the terminating NUL.
+ * On exception, returns 0.
+ * If the string is too long, returns a value greater than @n.
+ */
+static inline long strnlen_user(const char __user *s, long n)
+{
+ if (!__addr_ok(s))
+ return 0;
+ else
+ return __strnlen_user(s, n);
+}
+
+/**
+ * strlen_user: - Get the size of a string in user space.
+ * @str: The string to measure.
+ *
+ * Context: User context only. This function may sleep.
+ *
+ * Get the size of a NUL-terminated string in user space.
+ *
+ * Returns the size of the string INCLUDING the terminating NUL.
+ * On exception, returns 0.
+ *
+ * If there is a limit on the length of a valid string, you may wish to
+ * consider using strnlen_user() instead.
+ */
+#define strlen_user(str) strnlen_user(str, ~0UL >> 1)
+
+/*
+ * The exception table consists of pairs of addresses: the first is the
+ * address of an instruction that is allowed to fault, and the second is
+ * the address at which the program should continue. No registers are
+ * modified, so it is entirely up to the continuation code to figure out
+ * what to do.
+ *
+ * All the routines below use bits of fixup code that are out of line
+ * with the main instruction path. This means when everything is well,
+ * we don't even have to jump over them. Further, they do not intrude
+ * on our cache or tlb entries.
+ */
+struct exception_table_entry {
+ unsigned long insn, fixup;
+};
+
+#if defined(CONFIG_SUPERH64) && defined(CONFIG_MMU)
+#define ARCH_HAS_SEARCH_EXTABLE
+#endif
+
+int fixup_exception(struct pt_regs *regs);
+/* Returns 0 if exception not found and fixup.unit otherwise. */
+unsigned long search_exception_table(unsigned long addr);
+const struct exception_table_entry *search_exception_tables(unsigned long addr);
+
+
#endif /* __ASM_SH_UACCESS_H */
diff --git a/include/asm-sh/uaccess_32.h b/include/asm-sh/uaccess_32.h
index 1e41fda74bd3..ae0d24f6653f 100644
--- a/include/asm-sh/uaccess_32.h
+++ b/include/asm-sh/uaccess_32.h
@@ -1,9 +1,8 @@
-/* $Id: uaccess.h,v 1.11 2003/10/13 07:21:20 lethal Exp $
- *
+/*
* User space memory access functions
*
* Copyright (C) 1999, 2002 Niibe Yutaka
- * Copyright (C) 2003 Paul Mundt
+ * Copyright (C) 2003 - 2008 Paul Mundt
*
* Based on:
* MIPS implementation version 1.15 by
@@ -13,115 +12,6 @@
#ifndef __ASM_SH_UACCESS_32_H
#define __ASM_SH_UACCESS_32_H
-#include <linux/errno.h>
-#include <linux/sched.h>
-
-#define VERIFY_READ 0
-#define VERIFY_WRITE 1
-
-/*
- * The fs value determines whether argument validity checking should be
- * performed or not. If get_fs() == USER_DS, checking is performed, with
- * get_fs() == KERNEL_DS, checking is bypassed.
- *
- * For historical reasons (Data Segment Register?), these macros are misnamed.
- */
-
-#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) })
-
-#define KERNEL_DS MAKE_MM_SEG(0xFFFFFFFFUL)
-#define USER_DS MAKE_MM_SEG(PAGE_OFFSET)
-
-#define segment_eq(a,b) ((a).seg == (b).seg)
-
-#define get_ds() (KERNEL_DS)
-
-#if !defined(CONFIG_MMU)
-/* NOMMU is always true */
-#define __addr_ok(addr) (1)
-
-static inline mm_segment_t get_fs(void)
-{
- return USER_DS;
-}
-
-static inline void set_fs(mm_segment_t s)
-{
-}
-
-/*
- * __access_ok: Check if address with size is OK or not.
- *
- * If we don't have an MMU (or if its disabled) the only thing we really have
- * to look out for is if the address resides somewhere outside of what
- * available RAM we have.
- */
-static inline int __access_ok(unsigned long addr, unsigned long size)
-{
- return 1;
-}
-#else /* CONFIG_MMU */
-#define __addr_ok(addr) \
- ((unsigned long)(addr) < (current_thread_info()->addr_limit.seg))
-
-#define get_fs() (current_thread_info()->addr_limit)
-#define set_fs(x) (current_thread_info()->addr_limit = (x))
-
-/*
- * __access_ok: Check if address with size is OK or not.
- *
- * Uhhuh, this needs 33-bit arithmetic. We have a carry..
- *
- * sum := addr + size; carry? --> flag = true;
- * if (sum >= addr_limit) flag = true;
- */
-static inline int __access_ok(unsigned long addr, unsigned long size)
-{
- unsigned long flag, sum;
-
- __asm__("clrt\n\t"
- "addc %3, %1\n\t"
- "movt %0\n\t"
- "cmp/hi %4, %1\n\t"
- "rotcl %0"
- :"=&r" (flag), "=r" (sum)
- :"1" (addr), "r" (size),
- "r" (current_thread_info()->addr_limit.seg)
- :"t");
- return flag == 0;
-}
-#endif /* CONFIG_MMU */
-
-#define access_ok(type, addr, size) \
- (__chk_user_ptr(addr), \
- __access_ok((unsigned long __force)(addr), (size)))
-
-/*
- * Uh, these should become the main single-value transfer routines ...
- * They automatically use the right size if we just have the right
- * pointer type ...
- *
- * As SuperH uses the same address space for kernel and user data, we
- * can just do these as direct assignments.
- *
- * Careful to not
- * (a) re-use the arguments for side effects (sizeof is ok)
- * (b) require any knowledge of processes at this stage
- */
-#define put_user(x,ptr) __put_user_check((x), (ptr), sizeof(*(ptr)))
-#define get_user(x,ptr) __get_user_check((x), (ptr), sizeof(*(ptr)))
-
-/*
- * The "__xxx" versions do not do address space checking, useful when
- * doing multiple accesses to the same area (the user has to do the
- * checks by hand with "access_ok()")
- */
-#define __put_user(x,ptr) __put_user_nocheck((x), (ptr), sizeof(*(ptr)))
-#define __get_user(x,ptr) __get_user_nocheck((x), (ptr), sizeof(*(ptr)))
-
-struct __large_struct { unsigned long buf[100]; };
-#define __m(x) (*(struct __large_struct __user *)(x))
-
#define __get_user_size(x,ptr,size,retval) \
do { \
retval = 0; \
@@ -141,28 +31,7 @@ do { \
} \
} while (0)
-#define __get_user_nocheck(x,ptr,size) \
-({ \
- long __gu_err; \
- unsigned long __gu_val; \
- const __typeof__(*(ptr)) __user *__gu_addr = (ptr); \
- __chk_user_ptr(ptr); \
- __get_user_size(__gu_val, __gu_addr, (size), __gu_err); \
- (x) = (__typeof__(*(ptr)))__gu_val; \
- __gu_err; \
-})
-
-#define __get_user_check(x,ptr,size) \
-({ \
- long __gu_err = -EFAULT; \
- unsigned long __gu_val = 0; \
- const __typeof__(*(ptr)) *__gu_addr = (ptr); \
- if (likely(access_ok(VERIFY_READ, __gu_addr, (size)))) \
- __get_user_size(__gu_val, __gu_addr, (size), __gu_err); \
- (x) = (__typeof__(*(ptr)))__gu_val; \
- __gu_err; \
-})
-
+#ifdef CONFIG_MMU
#define __get_user_asm(x, addr, err, insn) \
({ \
__asm__ __volatile__( \
@@ -183,6 +52,16 @@ __asm__ __volatile__( \
".previous" \
:"=&r" (err), "=&r" (x) \
:"m" (__m(addr)), "i" (-EFAULT), "0" (err)); })
+#else
+#define __get_user_asm(x, addr, err, insn) \
+do { \
+ __asm__ __volatile__ ( \
+ "mov." insn " %1, %0\n\t" \
+ : "=&r" (x) \
+ : "m" (__m(addr)) \
+ ); \
+} while (0)
+#endif /* CONFIG_MMU */
extern void __get_user_unknown(void);
@@ -207,45 +86,41 @@ do { \
} \
} while (0)
-#define __put_user_nocheck(x,ptr,size) \
-({ \
- long __pu_err; \
- __typeof__(*(ptr)) __user *__pu_addr = (ptr); \
- __chk_user_ptr(ptr); \
- __put_user_size((x), __pu_addr, (size), __pu_err); \
- __pu_err; \
-})
-
-#define __put_user_check(x,ptr,size) \
-({ \
- long __pu_err = -EFAULT; \
- __typeof__(*(ptr)) __user *__pu_addr = (ptr); \
- if (likely(access_ok(VERIFY_WRITE, __pu_addr, size))) \
- __put_user_size((x), __pu_addr, (size), \
- __pu_err); \
- __pu_err; \
-})
-
-#define __put_user_asm(x, addr, err, insn) \
-({ \
-__asm__ __volatile__( \
- "1:\n\t" \
- "mov." insn " %1, %2\n\t" \
- "2:\n" \
- ".section .fixup,\"ax\"\n" \
- "3:\n\t" \
- "mov.l 4f, %0\n\t" \
- "jmp @%0\n\t" \
- " mov %3, %0\n\t" \
- ".balign 4\n" \
- "4: .long 2b\n\t" \
- ".previous\n" \
- ".section __ex_table,\"a\"\n\t" \
- ".long 1b, 3b\n\t" \
- ".previous" \
- :"=&r" (err) \
- :"r" (x), "m" (__m(addr)), "i" (-EFAULT), "0" (err) \
- :"memory"); })
+#ifdef CONFIG_MMU
+#define __put_user_asm(x, addr, err, insn) \
+do { \
+ __asm__ __volatile__ ( \
+ "1:\n\t" \
+ "mov." insn " %1, %2\n\t" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3:\n\t" \
+ "mov.l 4f, %0\n\t" \
+ "jmp @%0\n\t" \
+ " mov %3, %0\n\t" \
+ ".balign 4\n" \
+ "4: .long 2b\n\t" \
+ ".previous\n" \
+ ".section __ex_table,\"a\"\n\t" \
+ ".long 1b, 3b\n\t" \
+ ".previous" \
+ : "=&r" (err) \
+ : "r" (x), "m" (__m(addr)), "i" (-EFAULT), \
+ "0" (err) \
+ : "memory" \
+ ); \
+} while (0)
+#else
+#define __put_user_asm(x, addr, err, insn) \
+do { \
+ __asm__ __volatile__ ( \
+ "mov." insn " %0, %1\n\t" \
+ : /* no outputs */ \
+ : "r" (x), "m" (__m(addr)) \
+ : "memory" \
+ ); \
+} while (0)
+#endif /* CONFIG_MMU */
#if defined(CONFIG_CPU_LITTLE_ENDIAN)
#define __put_user_u64(val,addr,retval) \
@@ -295,40 +170,7 @@ __asm__ __volatile__( \
extern void __put_user_unknown(void);
-/* Generic arbitrary sized copy. */
-/* Return the number of bytes NOT copied */
-__kernel_size_t __copy_user(void *to, const void *from, __kernel_size_t n);
-
-
-static __always_inline unsigned long
-__copy_from_user(void *to, const void __user *from, unsigned long n)
-{
- return __copy_user(to, (__force void *)from, n);
-}
-
-static __always_inline unsigned long __must_check
-__copy_to_user(void __user *to, const void *from, unsigned long n)
-{
- return __copy_user((__force void *)to, from, n);
-}
-
-#define __copy_to_user_inatomic __copy_to_user
-#define __copy_from_user_inatomic __copy_from_user
-
-/*
- * Clear the area and return remaining number of bytes
- * (on failure. Usually it's 0.)
- */
-extern __kernel_size_t __clear_user(void *addr, __kernel_size_t size);
-
-#define clear_user(addr,n) ({ \
-void * __cl_addr = (addr); \
-unsigned long __cl_size = (n); \
-if (__cl_size && __access_ok(((unsigned long)(__cl_addr)), __cl_size)) \
-__cl_size = __clear_user(__cl_addr, __cl_size); \
-__cl_size; })
-
-static __inline__ int
+static inline int
__strncpy_from_user(unsigned long __dest, unsigned long __user __src, int __count)
{
__kernel_size_t res;
@@ -367,37 +209,11 @@ __strncpy_from_user(unsigned long __dest, unsigned long __user __src, int __coun
return res;
}
-/**
- * strncpy_from_user: - Copy a NUL terminated string from userspace.
- * @dst: Destination address, in kernel space. This buffer must be at
- * least @count bytes long.
- * @src: Source address, in user space.
- * @count: Maximum number of bytes to copy, including the trailing NUL.
- *
- * Copies a NUL-terminated string from userspace to kernel space.
- *
- * On success, returns the length of the string (not including the trailing
- * NUL).
- *
- * If access to userspace fails, returns -EFAULT (some data may have been
- * copied).
- *
- * If @count is smaller than the length of the string, copies @count bytes
- * and returns @count.
- */
-#define strncpy_from_user(dest,src,count) ({ \
-unsigned long __sfu_src = (unsigned long) (src); \
-int __sfu_count = (int) (count); \
-long __sfu_res = -EFAULT; \
-if(__access_ok(__sfu_src, __sfu_count)) { \
-__sfu_res = __strncpy_from_user((unsigned long) (dest), __sfu_src, __sfu_count); \
-} __sfu_res; })
-
/*
* Return the size of a string (including the ending 0 even when we have
* exceeded the maximum string length).
*/
-static __inline__ long __strnlen_user(const char __user *__s, long __n)
+static inline long __strnlen_user(const char __user *__s, long __n)
{
unsigned long res;
unsigned long __dummy;
@@ -429,61 +245,4 @@ static __inline__ long __strnlen_user(const char __user *__s, long __n)
return res;
}
-/**
- * strnlen_user: - Get the size of a string in user space.
- * @s: The string to measure.
- * @n: The maximum valid length
- *
- * Context: User context only. This function may sleep.
- *
- * Get the size of a NUL-terminated string in user space.
- *
- * Returns the size of the string INCLUDING the terminating NUL.
- * On exception, returns 0.
- * If the string is too long, returns a value greater than @n.
- */
-static __inline__ long strnlen_user(const char __user *s, long n)
-{
- if (!__addr_ok(s))
- return 0;
- else
- return __strnlen_user(s, n);
-}
-
-/**
- * strlen_user: - Get the size of a string in user space.
- * @str: The string to measure.
- *
- * Context: User context only. This function may sleep.
- *
- * Get the size of a NUL-terminated string in user space.
- *
- * Returns the size of the string INCLUDING the terminating NUL.
- * On exception, returns 0.
- *
- * If there is a limit on the length of a valid string, you may wish to
- * consider using strnlen_user() instead.
- */
-#define strlen_user(str) strnlen_user(str, ~0UL >> 1)
-
-/*
- * The exception table consists of pairs of addresses: the first is the
- * address of an instruction that is allowed to fault, and the second is
- * the address at which the program should continue. No registers are
- * modified, so it is entirely up to the continuation code to figure out
- * what to do.
- *
- * All the routines below use bits of fixup code that are out of line
- * with the main instruction path. This means when everything is well,
- * we don't even have to jump over them. Further, they do not intrude
- * on our cache or tlb entries.
- */
-
-struct exception_table_entry
-{
- unsigned long insn, fixup;
-};
-
-extern int fixup_exception(struct pt_regs *regs);
-
#endif /* __ASM_SH_UACCESS_32_H */
diff --git a/include/asm-sh/uaccess_64.h b/include/asm-sh/uaccess_64.h
index a9b68d094844..81b3d515fcb3 100644
--- a/include/asm-sh/uaccess_64.h
+++ b/include/asm-sh/uaccess_64.h
@@ -20,87 +20,6 @@
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
-#include <linux/errno.h>
-#include <linux/sched.h>
-
-#define VERIFY_READ 0
-#define VERIFY_WRITE 1
-
-/*
- * The fs value determines whether argument validity checking should be
- * performed or not. If get_fs() == USER_DS, checking is performed, with
- * get_fs() == KERNEL_DS, checking is bypassed.
- *
- * For historical reasons (Data Segment Register?), these macros are misnamed.
- */
-
-#define MAKE_MM_SEG(s) ((mm_segment_t) { (s) })
-
-#define KERNEL_DS MAKE_MM_SEG(0xFFFFFFFF)
-#define USER_DS MAKE_MM_SEG(0x80000000)
-
-#define get_ds() (KERNEL_DS)
-#define get_fs() (current_thread_info()->addr_limit)
-#define set_fs(x) (current_thread_info()->addr_limit=(x))
-
-#define segment_eq(a,b) ((a).seg == (b).seg)
-
-#define __addr_ok(addr) ((unsigned long)(addr) < (current_thread_info()->addr_limit.seg))
-
-/*
- * Uhhuh, this needs 33-bit arithmetic. We have a carry..
- *
- * sum := addr + size; carry? --> flag = true;
- * if (sum >= addr_limit) flag = true;
- */
-#define __range_ok(addr,size) (((unsigned long) (addr) + (size) < (current_thread_info()->addr_limit.seg)) ? 0 : 1)
-
-#define access_ok(type,addr,size) (__range_ok(addr,size) == 0)
-#define __access_ok(addr,size) (__range_ok(addr,size) == 0)
-
-/*
- * Uh, these should become the main single-value transfer routines ...
- * They automatically use the right size if we just have the right
- * pointer type ...
- *
- * As MIPS uses the same address space for kernel and user data, we
- * can just do these as direct assignments.
- *
- * Careful to not
- * (a) re-use the arguments for side effects (sizeof is ok)
- * (b) require any knowledge of processes at this stage
- */
-#define put_user(x,ptr) __put_user_check((x),(ptr),sizeof(*(ptr)))
-#define get_user(x,ptr) __get_user_check((x),(ptr),sizeof(*(ptr)))
-
-/*
- * The "__xxx" versions do not do address space checking, useful when
- * doing multiple accesses to the same area (the user has to do the
- * checks by hand with "access_ok()")
- */
-#define __put_user(x,ptr) __put_user_nocheck((x),(ptr),sizeof(*(ptr)))
-#define __get_user(x,ptr) __get_user_nocheck((x),(ptr),sizeof(*(ptr)))
-
-/*
- * The "xxx_ret" versions return constant specified in third argument, if
- * something bad happens. These macros can be optimized for the
- * case of just returning from the function xxx_ret is used.
- */
-
-#define put_user_ret(x,ptr,ret) ({ \
-if (put_user(x,ptr)) return ret; })
-
-#define get_user_ret(x,ptr,ret) ({ \
-if (get_user(x,ptr)) return ret; })
-
-#define __put_user_ret(x,ptr,ret) ({ \
-if (__put_user(x,ptr)) return ret; })
-
-#define __get_user_ret(x,ptr,ret) ({ \
-if (__get_user(x,ptr)) return ret; })
-
-struct __large_struct { unsigned long buf[100]; };
-#define __m(x) (*(struct __large_struct *)(x))
#define __get_user_size(x,ptr,size,retval) \
do { \
@@ -124,26 +43,6 @@ do { \
} \
} while (0)
-#define __get_user_nocheck(x,ptr,size) \
-({ \
- long __gu_err, __gu_val; \
- __get_user_size((void *)&__gu_val, (long)(ptr), \
- (size), __gu_err); \
- (x) = (__typeof__(*(ptr)))__gu_val; \
- __gu_err; \
-})
-
-#define __get_user_check(x,ptr,size) \
-({ \
- long __gu_addr = (long)(ptr); \
- long __gu_err = -EFAULT, __gu_val; \
- if (__access_ok(__gu_addr, (size))) \
- __get_user_size((void *)&__gu_val, __gu_addr, \
- (size), __gu_err); \
- (x) = (__typeof__(*(ptr))) __gu_val; \
- __gu_err; \
-})
-
extern long __get_user_asm_b(void *, long);
extern long __get_user_asm_w(void *, long);
extern long __get_user_asm_l(void *, long);
@@ -171,115 +70,10 @@ do { \
} \
} while (0)
-#define __put_user_nocheck(x,ptr,size) \
-({ \
- long __pu_err; \
- __typeof__(*(ptr)) __pu_val = (x); \
- __put_user_size((void *)&__pu_val, (long)(ptr), (size), __pu_err); \
- __pu_err; \
-})
-
-#define __put_user_check(x,ptr,size) \
-({ \
- long __pu_err = -EFAULT; \
- long __pu_addr = (long)(ptr); \
- __typeof__(*(ptr)) __pu_val = (x); \
- \
- if (__access_ok(__pu_addr, (size))) \
- __put_user_size((void *)&__pu_val, __pu_addr, (size), __pu_err);\
- __pu_err; \
-})
-
extern long __put_user_asm_b(void *, long);
extern long __put_user_asm_w(void *, long);
extern long __put_user_asm_l(void *, long);
extern long __put_user_asm_q(void *, long);
extern void __put_user_unknown(void);
-
-/* Generic arbitrary sized copy. */
-/* Return the number of bytes NOT copied */
-/* XXX: should be such that: 4byte and the rest. */
-extern __kernel_size_t __copy_user(void *__to, const void *__from, __kernel_size_t __n);
-
-#define copy_to_user_ret(to,from,n,retval) ({ \
-if (copy_to_user(to,from,n)) \
- return retval; \
-})
-
-#define __copy_to_user(to,from,n) \
- __copy_user((void *)(to), \
- (void *)(from), n)
-
-#define __copy_to_user_ret(to,from,n,retval) ({ \
-if (__copy_to_user(to,from,n)) \
- return retval; \
-})
-
-#define copy_from_user_ret(to,from,n,retval) ({ \
-if (copy_from_user(to,from,n)) \
- return retval; \
-})
-
-#define __copy_from_user(to,from,n) \
- __copy_user((void *)(to), \
- (void *)(from), n)
-
-#define __copy_from_user_ret(to,from,n,retval) ({ \
-if (__copy_from_user(to,from,n)) \
- return retval; \
-})
-
-#define __copy_to_user_inatomic __copy_to_user
-#define __copy_from_user_inatomic __copy_from_user
-
-/* XXX: Not sure it works well..
- should be such that: 4byte clear and the rest. */
-extern __kernel_size_t __clear_user(void *addr, __kernel_size_t size);
-
-#define clear_user(addr,n) ({ \
-void * __cl_addr = (addr); \
-unsigned long __cl_size = (n); \
-if (__cl_size && __access_ok(((unsigned long)(__cl_addr)), __cl_size)) \
-__cl_size = __clear_user(__cl_addr, __cl_size); \
-__cl_size; })
-
-extern int __strncpy_from_user(unsigned long __dest, unsigned long __src, int __count);
-
-#define strncpy_from_user(dest,src,count) ({ \
-unsigned long __sfu_src = (unsigned long) (src); \
-int __sfu_count = (int) (count); \
-long __sfu_res = -EFAULT; \
-if(__access_ok(__sfu_src, __sfu_count)) { \
-__sfu_res = __strncpy_from_user((unsigned long) (dest), __sfu_src, __sfu_count); \
-} __sfu_res; })
-
-#define strlen_user(str) strnlen_user(str, ~0UL >> 1)
-
-/*
- * Return the size of a string (including the ending 0!)
- */
-extern long __strnlen_user(const char *__s, long __n);
-
-static inline long strnlen_user(const char *s, long n)
-{
- if (!__addr_ok(s))
- return 0;
- else
- return __strnlen_user(s, n);
-}
-
-struct exception_table_entry
-{
- unsigned long insn, fixup;
-};
-
-#ifdef CONFIG_MMU
-#define ARCH_HAS_SEARCH_EXTABLE
-#endif
-
-/* Returns 0 if exception not found and fixup.unit otherwise. */
-extern unsigned long search_exception_table(unsigned long addr);
-extern const struct exception_table_entry *search_exception_tables (unsigned long addr);
-
#endif /* __ASM_SH_UACCESS_64_H */
diff --git a/include/asm-x86/acpi.h b/include/asm-x86/acpi.h
index 14411c9de46f..73ce5b32443f 100644
--- a/include/asm-x86/acpi.h
+++ b/include/asm-x86/acpi.h
@@ -28,6 +28,7 @@
#include <asm/numa.h>
#include <asm/processor.h>
#include <asm/mmu.h>
+#include <asm/mpspec.h>
#define COMPILER_DEPENDENT_INT64 long long
#define COMPILER_DEPENDENT_UINT64 unsigned long long
diff --git a/include/asm-x86/alternative.h b/include/asm-x86/alternative.h
index 1f6a9ca10126..f6aa18eadf71 100644
--- a/include/asm-x86/alternative.h
+++ b/include/asm-x86/alternative.h
@@ -72,6 +72,8 @@ static inline void alternatives_smp_module_del(struct module *mod) {}
static inline void alternatives_smp_switch(int smp) {}
#endif /* CONFIG_SMP */
+const unsigned char *const *find_nop_table(void);
+
/*
* Alternative instructions for different CPU types or capabilities.
*
diff --git a/include/asm-x86/apic.h b/include/asm-x86/apic.h
index be9639a9a186..313bcaf4b6c3 100644
--- a/include/asm-x86/apic.h
+++ b/include/asm-x86/apic.h
@@ -36,14 +36,10 @@ extern void generic_apic_probe(void);
#ifdef CONFIG_X86_LOCAL_APIC
extern int apic_verbosity;
-extern int timer_over_8254;
extern int local_apic_timer_c2_ok;
-extern int local_apic_timer_disabled;
-extern int apic_runs_main_timer;
extern int ioapic_force;
extern int disable_apic;
-extern int disable_apic_timer;
/*
* Basic functions accessing APICs.
diff --git a/include/asm-x86/atomic_64.h b/include/asm-x86/atomic_64.h
index 3e0cd7d38335..fe589c153db8 100644
--- a/include/asm-x86/atomic_64.h
+++ b/include/asm-x86/atomic_64.h
@@ -11,12 +11,6 @@
* resource counting etc..
*/
-#ifdef CONFIG_SMP
-#define LOCK "lock ; "
-#else
-#define LOCK ""
-#endif
-
/*
* Make sure gcc doesn't try to be clever and move things around
* on us. We need to use _exactly_ the address the user gave us,
diff --git a/include/asm-x86/bios_ebda.h b/include/asm-x86/bios_ebda.h
index b4a46b7be794..0033e50c13b2 100644
--- a/include/asm-x86/bios_ebda.h
+++ b/include/asm-x86/bios_ebda.h
@@ -14,4 +14,6 @@ static inline unsigned int get_bios_ebda(void)
return address; /* 0 means none */
}
+void reserve_ebda_region(void);
+
#endif /* _MACH_BIOS_EBDA_H */
diff --git a/include/asm-x86/bitops.h b/include/asm-x86/bitops.h
index ee4b3ead6a43..7d2494bdc660 100644
--- a/include/asm-x86/bitops.h
+++ b/include/asm-x86/bitops.h
@@ -43,7 +43,7 @@
* Note that @nr may be almost arbitrarily large; this function is not
* restricted to acting on a single-word quantity.
*/
-static inline void set_bit(int nr, volatile void *addr)
+static inline void set_bit(int nr, volatile unsigned long *addr)
{
asm volatile(LOCK_PREFIX "bts %1,%0" : ADDR : "Ir" (nr) : "memory");
}
@@ -57,7 +57,7 @@ static inline void set_bit(int nr, volatile void *addr)
* If it's called on the same region of memory simultaneously, the effect
* may be that only one operation succeeds.
*/
-static inline void __set_bit(int nr, volatile void *addr)
+static inline void __set_bit(int nr, volatile unsigned long *addr)
{
asm volatile("bts %1,%0" : ADDR : "Ir" (nr) : "memory");
}
@@ -72,7 +72,7 @@ static inline void __set_bit(int nr, volatile void *addr)
* you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit()
* in order to ensure changes are visible on other processors.
*/
-static inline void clear_bit(int nr, volatile void *addr)
+static inline void clear_bit(int nr, volatile unsigned long *addr)
{
asm volatile(LOCK_PREFIX "btr %1,%0" : ADDR : "Ir" (nr));
}
@@ -85,13 +85,13 @@ static inline void clear_bit(int nr, volatile void *addr)
* clear_bit() is atomic and implies release semantics before the memory
* operation. It can be used for an unlock.
*/
-static inline void clear_bit_unlock(unsigned nr, volatile void *addr)
+static inline void clear_bit_unlock(unsigned nr, volatile unsigned long *addr)
{
barrier();
clear_bit(nr, addr);
}
-static inline void __clear_bit(int nr, volatile void *addr)
+static inline void __clear_bit(int nr, volatile unsigned long *addr)
{
asm volatile("btr %1,%0" : ADDR : "Ir" (nr));
}
@@ -108,7 +108,7 @@ static inline void __clear_bit(int nr, volatile void *addr)
* No memory barrier is required here, because x86 cannot reorder stores past
* older loads. Same principle as spin_unlock.
*/
-static inline void __clear_bit_unlock(unsigned nr, volatile void *addr)
+static inline void __clear_bit_unlock(unsigned nr, volatile unsigned long *addr)
{
barrier();
__clear_bit(nr, addr);
@@ -126,7 +126,7 @@ static inline void __clear_bit_unlock(unsigned nr, volatile void *addr)
* If it's called on the same region of memory simultaneously, the effect
* may be that only one operation succeeds.
*/
-static inline void __change_bit(int nr, volatile void *addr)
+static inline void __change_bit(int nr, volatile unsigned long *addr)
{
asm volatile("btc %1,%0" : ADDR : "Ir" (nr));
}
@@ -140,7 +140,7 @@ static inline void __change_bit(int nr, volatile void *addr)
* Note that @nr may be almost arbitrarily large; this function is not
* restricted to acting on a single-word quantity.
*/
-static inline void change_bit(int nr, volatile void *addr)
+static inline void change_bit(int nr, volatile unsigned long *addr)
{
asm volatile(LOCK_PREFIX "btc %1,%0" : ADDR : "Ir" (nr));
}
@@ -153,7 +153,7 @@ static inline void change_bit(int nr, volatile void *addr)
* This operation is atomic and cannot be reordered.
* It also implies a memory barrier.
*/
-static inline int test_and_set_bit(int nr, volatile void *addr)
+static inline int test_and_set_bit(int nr, volatile unsigned long *addr)
{
int oldbit;
@@ -170,7 +170,7 @@ static inline int test_and_set_bit(int nr, volatile void *addr)
*
* This is the same as test_and_set_bit on x86.
*/
-static inline int test_and_set_bit_lock(int nr, volatile void *addr)
+static inline int test_and_set_bit_lock(int nr, volatile unsigned long *addr)
{
return test_and_set_bit(nr, addr);
}
@@ -184,7 +184,7 @@ static inline int test_and_set_bit_lock(int nr, volatile void *addr)
* If two examples of this operation race, one can appear to succeed
* but actually fail. You must protect multiple accesses with a lock.
*/
-static inline int __test_and_set_bit(int nr, volatile void *addr)
+static inline int __test_and_set_bit(int nr, volatile unsigned long *addr)
{
int oldbit;
@@ -203,7 +203,7 @@ static inline int __test_and_set_bit(int nr, volatile void *addr)
* This operation is atomic and cannot be reordered.
* It also implies a memory barrier.
*/
-static inline int test_and_clear_bit(int nr, volatile void *addr)
+static inline int test_and_clear_bit(int nr, volatile unsigned long *addr)
{
int oldbit;
@@ -223,7 +223,7 @@ static inline int test_and_clear_bit(int nr, volatile void *addr)
* If two examples of this operation race, one can appear to succeed
* but actually fail. You must protect multiple accesses with a lock.
*/
-static inline int __test_and_clear_bit(int nr, volatile void *addr)
+static inline int __test_and_clear_bit(int nr, volatile unsigned long *addr)
{
int oldbit;
@@ -235,7 +235,7 @@ static inline int __test_and_clear_bit(int nr, volatile void *addr)
}
/* WARNING: non atomic and it can be reordered! */
-static inline int __test_and_change_bit(int nr, volatile void *addr)
+static inline int __test_and_change_bit(int nr, volatile unsigned long *addr)
{
int oldbit;
@@ -255,7 +255,7 @@ static inline int __test_and_change_bit(int nr, volatile void *addr)
* This operation is atomic and cannot be reordered.
* It also implies a memory barrier.
*/
-static inline int test_and_change_bit(int nr, volatile void *addr)
+static inline int test_and_change_bit(int nr, volatile unsigned long *addr)
{
int oldbit;
@@ -266,13 +266,13 @@ static inline int test_and_change_bit(int nr, volatile void *addr)
return oldbit;
}
-static inline int constant_test_bit(int nr, const volatile void *addr)
+static inline int constant_test_bit(int nr, const volatile unsigned long *addr)
{
return ((1UL << (nr % BITS_PER_LONG)) &
(((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0;
}
-static inline int variable_test_bit(int nr, volatile const void *addr)
+static inline int variable_test_bit(int nr, volatile const unsigned long *addr)
{
int oldbit;
diff --git a/include/asm-x86/bootparam.h b/include/asm-x86/bootparam.h
index f62f4733606b..caeca78c2f0a 100644
--- a/include/asm-x86/bootparam.h
+++ b/include/asm-x86/bootparam.h
@@ -40,6 +40,7 @@ struct setup_header {
__u8 type_of_loader;
__u8 loadflags;
#define LOADED_HIGH (1<<0)
+#define QUIET_FLAG (1<<5)
#define KEEP_SEGMENTS (1<<6)
#define CAN_USE_HEAP (1<<7)
__u16 setup_move_size;
@@ -106,4 +107,7 @@ struct boot_params {
__u8 _pad9[276]; /* 0xeec */
} __attribute__((packed));
+void reserve_setup_data(void);
+void parse_setup_data(void);
+
#endif /* _ASM_BOOTPARAM_H */
diff --git a/include/asm-x86/cpufeature.h b/include/asm-x86/cpufeature.h
index 0d609c837a41..c30213703eb7 100644
--- a/include/asm-x86/cpufeature.h
+++ b/include/asm-x86/cpufeature.h
@@ -79,6 +79,7 @@
#define X86_FEATURE_REP_GOOD (3*32+16) /* rep microcode works well on this CPU */
#define X86_FEATURE_MFENCE_RDTSC (3*32+17) /* Mfence synchronizes RDTSC */
#define X86_FEATURE_LFENCE_RDTSC (3*32+18) /* Lfence synchronizes RDTSC */
+#define X86_FEATURE_PCI_EXT_CFG (3*32+19) /* PCI extended cfg access */
/* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */
#define X86_FEATURE_XMM3 (4*32+ 0) /* Streaming SIMD Extensions-3 */
@@ -142,11 +143,11 @@ extern const char * const x86_power_flags[32];
#define clear_cpu_cap(c, bit) clear_bit(bit, (unsigned long *)((c)->x86_capability))
#define setup_clear_cpu_cap(bit) do { \
clear_cpu_cap(&boot_cpu_data, bit); \
- set_bit(bit, cleared_cpu_caps); \
+ set_bit(bit, (unsigned long *)cleared_cpu_caps); \
} while (0)
#define setup_force_cpu_cap(bit) do { \
set_cpu_cap(&boot_cpu_data, bit); \
- clear_bit(bit, cleared_cpu_caps); \
+ clear_bit(bit, (unsigned long *)cleared_cpu_caps); \
} while (0)
#define cpu_has_fpu boot_cpu_has(X86_FEATURE_FPU)
@@ -187,6 +188,7 @@ extern const char * const x86_power_flags[32];
#define cpu_has_gbpages boot_cpu_has(X86_FEATURE_GBPAGES)
#define cpu_has_arch_perfmon boot_cpu_has(X86_FEATURE_ARCH_PERFMON)
#define cpu_has_pat boot_cpu_has(X86_FEATURE_PAT)
+#define cpu_has_pci_ext_cfg boot_cpu_has(X86_FEATURE_PCI_EXT_CFG)
#if defined(CONFIG_X86_INVLPG) || defined(CONFIG_X86_64)
# define cpu_has_invlpg 1
diff --git a/include/asm-x86/current.h b/include/asm-x86/current.h
index d2526d3f7346..7515c19d4988 100644
--- a/include/asm-x86/current.h
+++ b/include/asm-x86/current.h
@@ -1,5 +1,39 @@
+#ifndef _X86_CURRENT_H
+#define _X86_CURRENT_H
+
#ifdef CONFIG_X86_32
-# include "current_32.h"
-#else
-# include "current_64.h"
-#endif
+#include <linux/compiler.h>
+#include <asm/percpu.h>
+
+struct task_struct;
+
+DECLARE_PER_CPU(struct task_struct *, current_task);
+static __always_inline struct task_struct *get_current(void)
+{
+ return x86_read_percpu(current_task);
+}
+
+#else /* X86_32 */
+
+#ifndef __ASSEMBLY__
+#include <asm/pda.h>
+
+struct task_struct;
+
+static __always_inline struct task_struct *get_current(void)
+{
+ return read_pda(pcurrent);
+}
+
+#else /* __ASSEMBLY__ */
+
+#include <asm/asm-offsets.h>
+#define GET_CURRENT(reg) movq %gs:(pda_pcurrent),reg
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* X86_32 */
+
+#define current get_current()
+
+#endif /* X86_CURRENT_H */
diff --git a/include/asm-x86/current_32.h b/include/asm-x86/current_32.h
deleted file mode 100644
index 5af9bdb97a16..000000000000
--- a/include/asm-x86/current_32.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef _I386_CURRENT_H
-#define _I386_CURRENT_H
-
-#include <linux/compiler.h>
-#include <asm/percpu.h>
-
-struct task_struct;
-
-DECLARE_PER_CPU(struct task_struct *, current_task);
-static __always_inline struct task_struct *get_current(void)
-{
- return x86_read_percpu(current_task);
-}
-
-#define current get_current()
-
-#endif /* !(_I386_CURRENT_H) */
diff --git a/include/asm-x86/current_64.h b/include/asm-x86/current_64.h
deleted file mode 100644
index 2d368ede2fc1..000000000000
--- a/include/asm-x86/current_64.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef _X86_64_CURRENT_H
-#define _X86_64_CURRENT_H
-
-#if !defined(__ASSEMBLY__)
-struct task_struct;
-
-#include <asm/pda.h>
-
-static inline struct task_struct *get_current(void)
-{
- struct task_struct *t = read_pda(pcurrent);
- return t;
-}
-
-#define current get_current()
-
-#else
-
-#ifndef ASM_OFFSET_H
-#include <asm/asm-offsets.h>
-#endif
-
-#define GET_CURRENT(reg) movq %gs:(pda_pcurrent),reg
-
-#endif
-
-#endif /* !(_X86_64_CURRENT_H) */
diff --git a/include/asm-x86/desc.h b/include/asm-x86/desc.h
index 268a012bcd79..b3875d4b4fab 100644
--- a/include/asm-x86/desc.h
+++ b/include/asm-x86/desc.h
@@ -311,6 +311,28 @@ static inline void set_intr_gate(unsigned int n, void *addr)
_set_gate(n, GATE_INTERRUPT, addr, 0, 0, __KERNEL_CS);
}
+#define SYS_VECTOR_FREE 0
+#define SYS_VECTOR_ALLOCED 1
+
+extern int first_system_vector;
+extern char system_vectors[];
+
+static inline void alloc_system_vector(int vector)
+{
+ if (system_vectors[vector] == SYS_VECTOR_FREE) {
+ system_vectors[vector] = SYS_VECTOR_ALLOCED;
+ if (first_system_vector > vector)
+ first_system_vector = vector;
+ } else
+ BUG();
+}
+
+static inline void alloc_intr_gate(unsigned int n, void *addr)
+{
+ alloc_system_vector(n);
+ set_intr_gate(n, addr);
+}
+
/*
* This routine sets up an interrupt gate at directory privilege level 3.
*/
diff --git a/include/asm-x86/e820.h b/include/asm-x86/e820.h
index 7004251fc66b..8aa32323a182 100644
--- a/include/asm-x86/e820.h
+++ b/include/asm-x86/e820.h
@@ -2,6 +2,41 @@
#define __ASM_E820_H
#define E820MAP 0x2d0 /* our map */
#define E820MAX 128 /* number of entries in E820MAP */
+
+/*
+ * Legacy E820 BIOS limits us to 128 (E820MAX) nodes due to the
+ * constrained space in the zeropage. If we have more nodes than
+ * that, and if we've booted off EFI firmware, then the EFI tables
+ * passed us from the EFI firmware can list more nodes. Size our
+ * internal memory map tables to have room for these additional
+ * nodes, based on up to three entries per node for which the
+ * kernel was built: MAX_NUMNODES == (1 << CONFIG_NODES_SHIFT),
+ * plus E820MAX, allowing space for the possible duplicate E820
+ * entries that might need room in the same arrays, prior to the
+ * call to sanitize_e820_map() to remove duplicates. The allowance
+ * of three memory map entries per node is "enough" entries for
+ * the initial hardware platform motivating this mechanism to make
+ * use of additional EFI map entries. Future platforms may want
+ * to allow more than three entries per node or otherwise refine
+ * this size.
+ */
+
+/*
+ * Odd: 'make headers_check' complains about numa.h if I try
+ * to collapse the next two #ifdef lines to a single line:
+ * #if defined(__KERNEL__) && defined(CONFIG_EFI)
+ */
+#ifdef __KERNEL__
+#ifdef CONFIG_EFI
+#include <linux/numa.h>
+#define E820_X_MAX (E820MAX + 3 * MAX_NUMNODES)
+#else /* ! CONFIG_EFI */
+#define E820_X_MAX E820MAX
+#endif
+#else /* ! __KERNEL__ */
+#define E820_X_MAX E820MAX
+#endif
+
#define E820NR 0x1e8 /* # entries in E820MAP */
#define E820_RAM 1
@@ -18,8 +53,51 @@ struct e820entry {
struct e820map {
__u32 nr_map;
- struct e820entry map[E820MAX];
+ struct e820entry map[E820_X_MAX];
};
+
+extern struct e820map e820;
+
+extern int e820_any_mapped(u64 start, u64 end, unsigned type);
+extern int e820_all_mapped(u64 start, u64 end, unsigned type);
+extern void add_memory_region(u64 start, u64 size, int type);
+extern void e820_print_map(char *who);
+extern int
+sanitize_e820_map(struct e820entry *biosmap, int max_nr_map, int *pnr_map);
+extern int copy_e820_map(struct e820entry *biosmap, int nr_map);
+extern u64 update_memory_range(u64 start, u64 size, unsigned old_type,
+ unsigned new_type);
+extern void update_e820(void);
+extern void e820_setup_gap(void);
+
+#if defined(CONFIG_X86_64) || \
+ (defined(CONFIG_X86_32) && defined(CONFIG_HIBERNATION))
+extern void e820_mark_nosave_regions(unsigned long limit_pfn);
+#else
+static inline void e820_mark_nosave_regions(unsigned long limit_pfn)
+{
+}
+#endif
+
+extern unsigned long end_user_pfn;
+
+extern u64 find_e820_area(u64 start, u64 end, u64 size, u64 align);
+extern u64 find_e820_area_size(u64 start, u64 *sizep, u64 align);
+extern void reserve_early(u64 start, u64 end, char *name);
+extern void free_early(u64 start, u64 end);
+extern void early_res_to_bootmem(u64 start, u64 end);
+extern int page_is_reserved_early(unsigned long pagenr);
+extern u64 early_reserve_e820(u64 startt, u64 sizet, u64 align);
+
+extern unsigned long e820_end_of_ram(void);
+extern int e820_find_active_region(const struct e820entry *ei,
+ unsigned long start_pfn,
+ unsigned long last_pfn,
+ unsigned long *ei_startpfn,
+ unsigned long *ei_endpfn);
+extern void e820_register_active_regions(int nid, unsigned long start_pfn,
+ unsigned long end_pfn);
+extern u64 e820_hole_size(u64 start, u64 end);
#endif /* __ASSEMBLY__ */
#define ISA_START_ADDRESS 0xa0000
diff --git a/include/asm-x86/e820_32.h b/include/asm-x86/e820_32.h
index a9f7c6ec32bf..212b74c10efc 100644
--- a/include/asm-x86/e820_32.h
+++ b/include/asm-x86/e820_32.h
@@ -18,33 +18,13 @@
#ifndef __ASSEMBLY__
-extern struct e820map e820;
-extern void update_e820(void);
+extern void setup_memory_map(void);
+extern void finish_e820_parsing(void);
-extern int e820_all_mapped(unsigned long start, unsigned long end,
- unsigned type);
-extern int e820_any_mapped(u64 start, u64 end, unsigned type);
-extern void propagate_e820_map(void);
-extern void register_bootmem_low_pages(unsigned long max_low_pfn);
-extern void add_memory_region(unsigned long long start,
- unsigned long long size, int type);
-extern void update_memory_range(u64 start, u64 size, unsigned old_type,
- unsigned new_type);
-extern void e820_register_memory(void);
extern void limit_regions(unsigned long long size);
-extern void print_memory_map(char *who);
extern void init_iomem_resources(struct resource *code_resource,
struct resource *data_resource,
struct resource *bss_resource);
-#if defined(CONFIG_PM) && defined(CONFIG_HIBERNATION)
-extern void e820_mark_nosave_regions(void);
-#else
-static inline void e820_mark_nosave_regions(void)
-{
-}
-#endif
-
-
#endif/*!__ASSEMBLY__*/
#endif/*__E820_HEADER*/
diff --git a/include/asm-x86/e820_64.h b/include/asm-x86/e820_64.h
index 71c4d685d30d..368585daaa42 100644
--- a/include/asm-x86/e820_64.h
+++ b/include/asm-x86/e820_64.h
@@ -14,43 +14,16 @@
#include <linux/ioport.h>
#ifndef __ASSEMBLY__
-extern unsigned long find_e820_area(unsigned long start, unsigned long end,
- unsigned long size, unsigned long align);
-extern unsigned long find_e820_area_size(unsigned long start,
- unsigned long *sizep,
- unsigned long align);
-extern void add_memory_region(unsigned long start, unsigned long size,
- int type);
-extern void update_memory_range(u64 start, u64 size, unsigned old_type,
- unsigned new_type);
extern void setup_memory_region(void);
extern void contig_e820_setup(void);
-extern unsigned long e820_end_of_ram(void);
extern void e820_reserve_resources(void);
-extern void e820_mark_nosave_regions(void);
-extern int e820_any_mapped(unsigned long start, unsigned long end,
- unsigned type);
-extern int e820_all_mapped(unsigned long start, unsigned long end,
- unsigned type);
extern int e820_any_non_reserved(unsigned long start, unsigned long end);
extern int is_memory_any_valid(unsigned long start, unsigned long end);
extern int e820_all_non_reserved(unsigned long start, unsigned long end);
extern int is_memory_all_valid(unsigned long start, unsigned long end);
-extern unsigned long e820_hole_size(unsigned long start, unsigned long end);
-
-extern void e820_setup_gap(void);
-extern void e820_register_active_regions(int nid, unsigned long start_pfn,
- unsigned long end_pfn);
extern void finish_e820_parsing(void);
-extern struct e820map e820;
-extern void update_e820(void);
-
-extern void reserve_early(unsigned long start, unsigned long end, char *name);
-extern void free_early(unsigned long start, unsigned long end);
-extern void early_res_to_bootmem(unsigned long start, unsigned long end);
-
#endif/*!__ASSEMBLY__*/
#endif/*__E820_HEADER*/
diff --git a/include/asm-x86/efi.h b/include/asm-x86/efi.h
index d53004b855cc..7ed2bd7a7f51 100644
--- a/include/asm-x86/efi.h
+++ b/include/asm-x86/efi.h
@@ -90,7 +90,7 @@ extern void *efi_ioremap(unsigned long addr, unsigned long size);
#endif /* CONFIG_X86_32 */
-extern void efi_reserve_bootmem(void);
+extern void efi_reserve_early(void);
extern void efi_call_phys_prelog(void);
extern void efi_call_phys_epilog(void);
diff --git a/include/asm-x86/fixmap_32.h b/include/asm-x86/fixmap_32.h
index 4b96148e90c1..f0df7ee96816 100644
--- a/include/asm-x86/fixmap_32.h
+++ b/include/asm-x86/fixmap_32.h
@@ -79,10 +79,6 @@ enum fixed_addresses {
FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */
FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,
#endif
-#ifdef CONFIG_ACPI
- FIX_ACPI_BEGIN,
- FIX_ACPI_END = FIX_ACPI_BEGIN + FIX_ACPI_PAGES - 1,
-#endif
#ifdef CONFIG_PCI_MMCONFIG
FIX_PCIE_MCFG,
#endif
@@ -103,6 +99,10 @@ enum fixed_addresses {
(__end_of_permanent_fixed_addresses & 511),
FIX_BTMAP_BEGIN = FIX_BTMAP_END + NR_FIX_BTMAPS*FIX_BTMAPS_NESTING - 1,
FIX_WP_TEST,
+#ifdef CONFIG_ACPI
+ FIX_ACPI_BEGIN,
+ FIX_ACPI_END = FIX_ACPI_BEGIN + FIX_ACPI_PAGES - 1,
+#endif
#ifdef CONFIG_PROVIDE_OHCI1394_DMA_INIT
FIX_OHCI1394_BASE,
#endif
diff --git a/include/asm-x86/gart.h b/include/asm-x86/gart.h
index 90958ed993fa..c818b96f936b 100644
--- a/include/asm-x86/gart.h
+++ b/include/asm-x86/gart.h
@@ -1,10 +1,13 @@
#ifndef _ASM_X8664_IOMMU_H
#define _ASM_X8664_IOMMU_H 1
+#include <asm/e820.h>
+
extern void pci_iommu_shutdown(void);
extern void no_iommu_init(void);
extern int force_iommu, no_iommu;
extern int iommu_detected;
+extern int agp_amd64_init(void);
#ifdef CONFIG_GART_IOMMU
extern void gart_iommu_init(void);
extern void gart_iommu_shutdown(void);
@@ -31,4 +34,63 @@ static inline void gart_iommu_shutdown(void)
#endif
+/* PTE bits. */
+#define GPTE_VALID 1
+#define GPTE_COHERENT 2
+
+/* Aperture control register bits. */
+#define GARTEN (1<<0)
+#define DISGARTCPU (1<<4)
+#define DISGARTIO (1<<5)
+
+/* GART cache control register bits. */
+#define INVGART (1<<0)
+#define GARTPTEERR (1<<1)
+
+/* K8 On-cpu GART registers */
+#define AMD64_GARTAPERTURECTL 0x90
+#define AMD64_GARTAPERTUREBASE 0x94
+#define AMD64_GARTTABLEBASE 0x98
+#define AMD64_GARTCACHECTL 0x9c
+#define AMD64_GARTEN (1<<0)
+
+static inline void enable_gart_translation(struct pci_dev *dev, u64 addr)
+{
+ u32 tmp, ctl;
+
+ /* address of the mappings table */
+ addr >>= 12;
+ tmp = (u32) addr<<4;
+ tmp &= ~0xf;
+ pci_write_config_dword(dev, AMD64_GARTTABLEBASE, tmp);
+
+ /* Enable GART translation for this hammer. */
+ pci_read_config_dword(dev, AMD64_GARTAPERTURECTL, &ctl);
+ ctl |= GARTEN;
+ ctl &= ~(DISGARTCPU | DISGARTIO);
+ pci_write_config_dword(dev, AMD64_GARTAPERTURECTL, ctl);
+}
+
+static inline int aperture_valid(u64 aper_base, u32 aper_size, u32 min_size)
+{
+ if (!aper_base)
+ return 0;
+
+ if (aper_base + aper_size > 0x100000000ULL) {
+ printk(KERN_ERR "Aperture beyond 4GB. Ignoring.\n");
+ return 0;
+ }
+ if (e820_any_mapped(aper_base, aper_base + aper_size, E820_RAM)) {
+ printk(KERN_ERR "Aperture pointing to e820 RAM. Ignoring.\n");
+ return 0;
+ }
+ if (aper_size < min_size) {
+ printk(KERN_ERR "Aperture too small (%d MB) than (%d MB)\n",
+ aper_size>>20, min_size>>20);
+ return 0;
+ }
+
+ return 1;
+}
+
#endif
diff --git a/include/asm-x86/genapic_64.h b/include/asm-x86/genapic_64.h
index 1de931b263ce..0f8504627c41 100644
--- a/include/asm-x86/genapic_64.h
+++ b/include/asm-x86/genapic_64.h
@@ -44,4 +44,6 @@ DECLARE_PER_CPU(int, x2apic_extra_bits);
extern void uv_cpu_init(void);
extern int uv_wakeup_secondary(int phys_apicid, unsigned int start_rip);
+extern void setup_apic_routing(void);
+
#endif
diff --git a/include/asm-x86/hardirq.h b/include/asm-x86/hardirq.h
index 314434d664e7..000787df66e6 100644
--- a/include/asm-x86/hardirq.h
+++ b/include/asm-x86/hardirq.h
@@ -3,3 +3,9 @@
#else
# include "hardirq_64.h"
#endif
+
+extern u64 arch_irq_stat_cpu(unsigned int cpu);
+#define arch_irq_stat_cpu arch_irq_stat_cpu
+
+extern u64 arch_irq_stat(void);
+#define arch_irq_stat arch_irq_stat
diff --git a/include/asm-x86/hw_irq.h b/include/asm-x86/hw_irq.h
index bf025399d939..1428b41dcbb9 100644
--- a/include/asm-x86/hw_irq.h
+++ b/include/asm-x86/hw_irq.h
@@ -1,5 +1,106 @@
+#ifndef _ASM_HW_IRQ_H
+#define _ASM_HW_IRQ_H
+
+/*
+ * (C) 1992, 1993 Linus Torvalds, (C) 1997 Ingo Molnar
+ *
+ * moved some of the old arch/i386/kernel/irq.h to here. VY
+ *
+ * IRQ/IPI changes taken from work by Thomas Radke
+ * <tomsoft@informatik.tu-chemnitz.de>
+ *
+ * hacked by Andi Kleen for x86-64.
+ * unified by tglx
+ */
+
+#include <asm/irq_vectors.h>
+
+#ifndef __ASSEMBLY__
+
+#include <linux/percpu.h>
+#include <linux/profile.h>
+#include <linux/smp.h>
+
+#include <asm/atomic.h>
+#include <asm/irq.h>
+#include <asm/sections.h>
+
+#define platform_legacy_irq(irq) ((irq) < 16)
+
+/* Interrupt handlers registered during init_IRQ */
+extern void apic_timer_interrupt(void);
+extern void error_interrupt(void);
+extern void spurious_interrupt(void);
+extern void thermal_interrupt(void);
+extern void reschedule_interrupt(void);
+
+extern void invalidate_interrupt(void);
+extern void invalidate_interrupt0(void);
+extern void invalidate_interrupt1(void);
+extern void invalidate_interrupt2(void);
+extern void invalidate_interrupt3(void);
+extern void invalidate_interrupt4(void);
+extern void invalidate_interrupt5(void);
+extern void invalidate_interrupt6(void);
+extern void invalidate_interrupt7(void);
+
+extern void irq_move_cleanup_interrupt(void);
+extern void threshold_interrupt(void);
+
+extern void call_function_interrupt(void);
+
+/* PIC specific functions */
+extern void disable_8259A_irq(unsigned int irq);
+extern void enable_8259A_irq(unsigned int irq);
+extern int i8259A_irq_pending(unsigned int irq);
+extern void make_8259A_irq(unsigned int irq);
+extern void init_8259A(int aeoi);
+
+/* IOAPIC */
+#define IO_APIC_IRQ(x) (((x) >= 16) || ((1<<(x)) & io_apic_irqs))
+extern unsigned long io_apic_irqs;
+
+extern void init_VISWS_APIC_irqs(void);
+extern void setup_IO_APIC(void);
+extern void disable_IO_APIC(void);
+extern void print_IO_APIC(void);
+extern int IO_APIC_get_PCI_irq_vector(int bus, int slot, int fn);
+extern void setup_ioapic_dest(void);
+
+#ifdef CONFIG_X86_64
+extern void enable_IO_APIC(void);
+#endif
+
+/* IPI functions */
+extern void send_IPI_self(int vector);
+extern void send_IPI(int dest, int vector);
+
+/* Statistics */
+extern atomic_t irq_err_count;
+extern atomic_t irq_mis_count;
+
+/* EISA */
+extern void eisa_set_level_irq(unsigned int irq);
+
+/* Voyager functions */
+extern asmlinkage void vic_cpi_interrupt(void);
+extern asmlinkage void vic_sys_interrupt(void);
+extern asmlinkage void vic_cmn_interrupt(void);
+extern asmlinkage void qic_timer_interrupt(void);
+extern asmlinkage void qic_invalidate_interrupt(void);
+extern asmlinkage void qic_reschedule_interrupt(void);
+extern asmlinkage void qic_enable_irq_interrupt(void);
+extern asmlinkage void qic_call_function_interrupt(void);
+
#ifdef CONFIG_X86_32
-# include "hw_irq_32.h"
+extern void (*const interrupt[NR_IRQS])(void);
#else
-# include "hw_irq_64.h"
+typedef int vector_irq_t[NR_VECTORS];
+DECLARE_PER_CPU(vector_irq_t, vector_irq);
+extern void __setup_vector_irq(int cpu);
+extern spinlock_t vector_lock;
+#endif
+
+#endif /* !ASSEMBLY_ */
+
#endif
diff --git a/include/asm-x86/hw_irq_32.h b/include/asm-x86/hw_irq_32.h
deleted file mode 100644
index ea88054e03f3..000000000000
--- a/include/asm-x86/hw_irq_32.h
+++ /dev/null
@@ -1,66 +0,0 @@
-#ifndef _ASM_HW_IRQ_H
-#define _ASM_HW_IRQ_H
-
-/*
- * linux/include/asm/hw_irq.h
- *
- * (C) 1992, 1993 Linus Torvalds, (C) 1997 Ingo Molnar
- *
- * moved some of the old arch/i386/kernel/irq.h to here. VY
- *
- * IRQ/IPI changes taken from work by Thomas Radke
- * <tomsoft@informatik.tu-chemnitz.de>
- */
-
-#include <linux/profile.h>
-#include <asm/atomic.h>
-#include <asm/irq.h>
-#include <asm/sections.h>
-
-#define NMI_VECTOR 0x02
-
-/*
- * Various low-level irq details needed by irq.c, process.c,
- * time.c, io_apic.c and smp.c
- *
- * Interrupt entry/exit code at both C and assembly level
- */
-
-extern void (*const interrupt[NR_IRQS])(void);
-
-#ifdef CONFIG_SMP
-void reschedule_interrupt(void);
-void invalidate_interrupt(void);
-void call_function_interrupt(void);
-#endif
-
-#ifdef CONFIG_X86_LOCAL_APIC
-void apic_timer_interrupt(void);
-void error_interrupt(void);
-void spurious_interrupt(void);
-void thermal_interrupt(void);
-#define platform_legacy_irq(irq) ((irq) < 16)
-#endif
-
-void disable_8259A_irq(unsigned int irq);
-void enable_8259A_irq(unsigned int irq);
-int i8259A_irq_pending(unsigned int irq);
-void make_8259A_irq(unsigned int irq);
-void init_8259A(int aeoi);
-void send_IPI_self(int vector);
-void init_VISWS_APIC_irqs(void);
-void setup_IO_APIC(void);
-void disable_IO_APIC(void);
-void print_IO_APIC(void);
-int IO_APIC_get_PCI_irq_vector(int bus, int slot, int fn);
-void send_IPI(int dest, int vector);
-void setup_ioapic_dest(void);
-
-extern unsigned long io_apic_irqs;
-
-extern atomic_t irq_err_count;
-extern atomic_t irq_mis_count;
-
-#define IO_APIC_IRQ(x) (((x) >= 16) || ((1<<(x)) & io_apic_irqs))
-
-#endif /* _ASM_HW_IRQ_H */
diff --git a/include/asm-x86/hw_irq_64.h b/include/asm-x86/hw_irq_64.h
deleted file mode 100644
index 0062ef390f67..000000000000
--- a/include/asm-x86/hw_irq_64.h
+++ /dev/null
@@ -1,173 +0,0 @@
-#ifndef _ASM_HW_IRQ_H
-#define _ASM_HW_IRQ_H
-
-/*
- * linux/include/asm/hw_irq.h
- *
- * (C) 1992, 1993 Linus Torvalds, (C) 1997 Ingo Molnar
- *
- * moved some of the old arch/i386/kernel/irq.h to here. VY
- *
- * IRQ/IPI changes taken from work by Thomas Radke
- * <tomsoft@informatik.tu-chemnitz.de>
- *
- * hacked by Andi Kleen for x86-64.
- */
-
-#ifndef __ASSEMBLY__
-#include <asm/atomic.h>
-#include <asm/irq.h>
-#include <linux/profile.h>
-#include <linux/smp.h>
-#include <linux/percpu.h>
-#endif
-
-#define NMI_VECTOR 0x02
-/*
- * IDT vectors usable for external interrupt sources start
- * at 0x20:
- */
-#define FIRST_EXTERNAL_VECTOR 0x20
-
-#define IA32_SYSCALL_VECTOR 0x80
-
-
-/* Reserve the lowest usable priority level 0x20 - 0x2f for triggering
- * cleanup after irq migration.
- */
-#define IRQ_MOVE_CLEANUP_VECTOR FIRST_EXTERNAL_VECTOR
-
-/*
- * Vectors 0x30-0x3f are used for ISA interrupts.
- */
-#define IRQ0_VECTOR (FIRST_EXTERNAL_VECTOR + 0x10)
-#define IRQ1_VECTOR (IRQ0_VECTOR + 1)
-#define IRQ2_VECTOR (IRQ0_VECTOR + 2)
-#define IRQ3_VECTOR (IRQ0_VECTOR + 3)
-#define IRQ4_VECTOR (IRQ0_VECTOR + 4)
-#define IRQ5_VECTOR (IRQ0_VECTOR + 5)
-#define IRQ6_VECTOR (IRQ0_VECTOR + 6)
-#define IRQ7_VECTOR (IRQ0_VECTOR + 7)
-#define IRQ8_VECTOR (IRQ0_VECTOR + 8)
-#define IRQ9_VECTOR (IRQ0_VECTOR + 9)
-#define IRQ10_VECTOR (IRQ0_VECTOR + 10)
-#define IRQ11_VECTOR (IRQ0_VECTOR + 11)
-#define IRQ12_VECTOR (IRQ0_VECTOR + 12)
-#define IRQ13_VECTOR (IRQ0_VECTOR + 13)
-#define IRQ14_VECTOR (IRQ0_VECTOR + 14)
-#define IRQ15_VECTOR (IRQ0_VECTOR + 15)
-
-/*
- * Special IRQ vectors used by the SMP architecture, 0xf0-0xff
- *
- * some of the following vectors are 'rare', they are merged
- * into a single vector (CALL_FUNCTION_VECTOR) to save vector space.
- * TLB, reschedule and local APIC vectors are performance-critical.
- */
-#define SPURIOUS_APIC_VECTOR 0xff
-#define ERROR_APIC_VECTOR 0xfe
-#define RESCHEDULE_VECTOR 0xfd
-#define CALL_FUNCTION_VECTOR 0xfc
-/* fb free - please don't readd KDB here because it's useless
- (hint - think what a NMI bit does to a vector) */
-#define THERMAL_APIC_VECTOR 0xfa
-#define THRESHOLD_APIC_VECTOR 0xf9
-/* f8 free */
-#define INVALIDATE_TLB_VECTOR_END 0xf7
-#define INVALIDATE_TLB_VECTOR_START 0xf0 /* f0-f7 used for TLB flush */
-
-#define NUM_INVALIDATE_TLB_VECTORS 8
-
-/*
- * Local APIC timer IRQ vector is on a different priority level,
- * to work around the 'lost local interrupt if more than 2 IRQ
- * sources per level' errata.
- */
-#define LOCAL_TIMER_VECTOR 0xef
-
-/*
- * First APIC vector available to drivers: (vectors 0x30-0xee)
- * we start at 0x41 to spread out vectors evenly between priority
- * levels. (0x80 is the syscall vector)
- */
-#define FIRST_DEVICE_VECTOR (IRQ15_VECTOR + 2)
-#define FIRST_SYSTEM_VECTOR 0xef /* duplicated in irq.h */
-
-
-#ifndef __ASSEMBLY__
-
-/* Interrupt handlers registered during init_IRQ */
-void apic_timer_interrupt(void);
-void spurious_interrupt(void);
-void error_interrupt(void);
-void reschedule_interrupt(void);
-void call_function_interrupt(void);
-void irq_move_cleanup_interrupt(void);
-void invalidate_interrupt0(void);
-void invalidate_interrupt1(void);
-void invalidate_interrupt2(void);
-void invalidate_interrupt3(void);
-void invalidate_interrupt4(void);
-void invalidate_interrupt5(void);
-void invalidate_interrupt6(void);
-void invalidate_interrupt7(void);
-void thermal_interrupt(void);
-void threshold_interrupt(void);
-void i8254_timer_resume(void);
-
-typedef int vector_irq_t[NR_VECTORS];
-DECLARE_PER_CPU(vector_irq_t, vector_irq);
-extern void __setup_vector_irq(int cpu);
-extern spinlock_t vector_lock;
-
-/*
- * Various low-level irq details needed by irq.c, process.c,
- * time.c, io_apic.c and smp.c
- *
- * Interrupt entry/exit code at both C and assembly level
- */
-
-extern void disable_8259A_irq(unsigned int irq);
-extern void enable_8259A_irq(unsigned int irq);
-extern int i8259A_irq_pending(unsigned int irq);
-extern void make_8259A_irq(unsigned int irq);
-extern void init_8259A(int aeoi);
-extern void send_IPI_self(int vector);
-extern void init_VISWS_APIC_irqs(void);
-extern void setup_IO_APIC(void);
-extern void enable_IO_APIC(void);
-extern void disable_IO_APIC(void);
-extern void print_IO_APIC(void);
-extern int IO_APIC_get_PCI_irq_vector(int bus, int slot, int fn);
-extern void send_IPI(int dest, int vector);
-extern void setup_ioapic_dest(void);
-extern void native_init_IRQ(void);
-
-extern unsigned long io_apic_irqs;
-
-extern atomic_t irq_err_count;
-extern atomic_t irq_mis_count;
-
-#define IO_APIC_IRQ(x) (((x) >= 16) || ((1<<(x)) & io_apic_irqs))
-
-#include <asm/ptrace.h>
-
-#define IRQ_NAME2(nr) nr##_interrupt(void)
-#define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr)
-
-/*
- * SMP has a few special interrupts for IPI messages
- */
-
-#define BUILD_IRQ(nr) \
- asmlinkage void IRQ_NAME(nr); \
- asm("\n.p2align\n" \
- "IRQ" #nr "_interrupt:\n\t" \
- "push $~(" #nr ") ; " \
- "jmp common_interrupt");
-
-#define platform_legacy_irq(irq) ((irq) < 16)
-
-#endif
-
-#endif /* _ASM_HW_IRQ_H */
diff --git a/include/asm-x86/i8259.h b/include/asm-x86/i8259.h
index 45d4df3e51e6..2f98df91f1f2 100644
--- a/include/asm-x86/i8259.h
+++ b/include/asm-x86/i8259.h
@@ -55,4 +55,6 @@ static inline void outb_pic(unsigned char value, unsigned int port)
udelay(2);
}
+extern struct irq_chip i8259A_chip;
+
#endif /* __ASM_I8259_H__ */
diff --git a/include/asm-x86/io.h b/include/asm-x86/io.h
index d5b11f60dbd0..8e9eca93f9b9 100644
--- a/include/asm-x86/io.h
+++ b/include/asm-x86/io.h
@@ -3,6 +3,62 @@
#define ARCH_HAS_IOREMAP_WC
+#include <linux/compiler.h>
+
+#define build_mmio_read(name, size, type, reg, barrier) \
+static inline type name(const volatile void __iomem *addr) \
+{ type ret; asm volatile("mov" size " %1,%0":"=" reg (ret) \
+:"m" (*(volatile type __force *)addr) barrier); return ret; }
+
+#define build_mmio_write(name, size, type, reg, barrier) \
+static inline void name(type val, volatile void __iomem *addr) \
+{ asm volatile("mov" size " %0,%1": :reg (val), \
+"m" (*(volatile type __force *)addr) barrier); }
+
+build_mmio_read(readb, "b", unsigned char, "q", :"memory")
+build_mmio_read(readw, "w", unsigned short, "r", :"memory")
+build_mmio_read(readl, "l", unsigned int, "r", :"memory")
+
+build_mmio_read(__readb, "b", unsigned char, "q", )
+build_mmio_read(__readw, "w", unsigned short, "r", )
+build_mmio_read(__readl, "l", unsigned int, "r", )
+
+build_mmio_write(writeb, "b", unsigned char, "q", :"memory")
+build_mmio_write(writew, "w", unsigned short, "r", :"memory")
+build_mmio_write(writel, "l", unsigned int, "r", :"memory")
+
+build_mmio_write(__writeb, "b", unsigned char, "q", )
+build_mmio_write(__writew, "w", unsigned short, "r", )
+build_mmio_write(__writel, "l", unsigned int, "r", )
+
+#define readb_relaxed(a) __readb(a)
+#define readw_relaxed(a) __readw(a)
+#define readl_relaxed(a) __readl(a)
+#define __raw_readb __readb
+#define __raw_readw __readw
+#define __raw_readl __readl
+
+#define __raw_writeb __writeb
+#define __raw_writew __writew
+#define __raw_writel __writel
+
+#define mmiowb() barrier()
+
+#ifdef CONFIG_X86_64
+build_mmio_read(readq, "q", unsigned long, "r", :"memory")
+build_mmio_read(__readq, "q", unsigned long, "r", )
+build_mmio_write(writeq, "q", unsigned long, "r", :"memory")
+build_mmio_write(__writeq, "q", unsigned long, "r", )
+
+#define readq_relaxed(a) __readq(a)
+#define __raw_readq __readq
+#define __raw_writeq writeq
+
+/* Let people know we have them */
+#define readq readq
+#define writeq writeq
+#endif
+
#ifdef CONFIG_X86_32
# include "io_32.h"
#else
diff --git a/include/asm-x86/io_32.h b/include/asm-x86/io_32.h
index 049e81e797a0..d71be8df9797 100644
--- a/include/asm-x86/io_32.h
+++ b/include/asm-x86/io_32.h
@@ -149,55 +149,6 @@ extern void __iomem *fix_ioremap(unsigned idx, unsigned long phys);
#define virt_to_bus virt_to_phys
#define bus_to_virt phys_to_virt
-/*
- * readX/writeX() are used to access memory mapped devices. On some
- * architectures the memory mapped IO stuff needs to be accessed
- * differently. On the x86 architecture, we just read/write the
- * memory location directly.
- */
-
-static inline unsigned char readb(const volatile void __iomem *addr)
-{
- return *(volatile unsigned char __force *)addr;
-}
-
-static inline unsigned short readw(const volatile void __iomem *addr)
-{
- return *(volatile unsigned short __force *)addr;
-}
-
-static inline unsigned int readl(const volatile void __iomem *addr)
-{
- return *(volatile unsigned int __force *) addr;
-}
-
-#define readb_relaxed(addr) readb(addr)
-#define readw_relaxed(addr) readw(addr)
-#define readl_relaxed(addr) readl(addr)
-#define __raw_readb readb
-#define __raw_readw readw
-#define __raw_readl readl
-
-static inline void writeb(unsigned char b, volatile void __iomem *addr)
-{
- *(volatile unsigned char __force *)addr = b;
-}
-
-static inline void writew(unsigned short b, volatile void __iomem *addr)
-{
- *(volatile unsigned short __force *)addr = b;
-}
-
-static inline void writel(unsigned int b, volatile void __iomem *addr)
-{
- *(volatile unsigned int __force *)addr = b;
-}
-#define __raw_writeb writeb
-#define __raw_writew writew
-#define __raw_writel writel
-
-#define mmiowb()
-
static inline void
memset_io(volatile void __iomem *addr, unsigned char val, int count)
{
diff --git a/include/asm-x86/io_64.h b/include/asm-x86/io_64.h
index 0930bedf9e4d..ddd8058a5026 100644
--- a/include/asm-x86/io_64.h
+++ b/include/asm-x86/io_64.h
@@ -204,77 +204,6 @@ extern void __iomem *fix_ioremap(unsigned idx, unsigned long phys);
#define virt_to_bus virt_to_phys
#define bus_to_virt phys_to_virt
-/*
- * readX/writeX() are used to access memory mapped devices. On some
- * architectures the memory mapped IO stuff needs to be accessed
- * differently. On the x86 architecture, we just read/write the
- * memory location directly.
- */
-
-static inline __u8 __readb(const volatile void __iomem *addr)
-{
- return *(__force volatile __u8 *)addr;
-}
-
-static inline __u16 __readw(const volatile void __iomem *addr)
-{
- return *(__force volatile __u16 *)addr;
-}
-
-static __always_inline __u32 __readl(const volatile void __iomem *addr)
-{
- return *(__force volatile __u32 *)addr;
-}
-
-static inline __u64 __readq(const volatile void __iomem *addr)
-{
- return *(__force volatile __u64 *)addr;
-}
-
-#define readb(x) __readb(x)
-#define readw(x) __readw(x)
-#define readl(x) __readl(x)
-#define readq(x) __readq(x)
-#define readb_relaxed(a) readb(a)
-#define readw_relaxed(a) readw(a)
-#define readl_relaxed(a) readl(a)
-#define readq_relaxed(a) readq(a)
-#define __raw_readb readb
-#define __raw_readw readw
-#define __raw_readl readl
-#define __raw_readq readq
-
-#define mmiowb()
-
-static inline void __writel(__u32 b, volatile void __iomem *addr)
-{
- *(__force volatile __u32 *)addr = b;
-}
-
-static inline void __writeq(__u64 b, volatile void __iomem *addr)
-{
- *(__force volatile __u64 *)addr = b;
-}
-
-static inline void __writeb(__u8 b, volatile void __iomem *addr)
-{
- *(__force volatile __u8 *)addr = b;
-}
-
-static inline void __writew(__u16 b, volatile void __iomem *addr)
-{
- *(__force volatile __u16 *)addr = b;
-}
-
-#define writeq(val, addr) __writeq((val), (addr))
-#define writel(val, addr) __writel((val), (addr))
-#define writew(val, addr) __writew((val), (addr))
-#define writeb(val, addr) __writeb((val), (addr))
-#define __raw_writeb writeb
-#define __raw_writew writew
-#define __raw_writel writel
-#define __raw_writeq writeq
-
void __memcpy_fromio(void *, unsigned long, unsigned);
void __memcpy_toio(unsigned long, const void *, unsigned);
diff --git a/include/asm-x86/io_apic.h b/include/asm-x86/io_apic.h
index d593e14f0341..8b1f5684842e 100644
--- a/include/asm-x86/io_apic.h
+++ b/include/asm-x86/io_apic.h
@@ -11,6 +11,15 @@
* Copyright (C) 1997, 1998, 1999, 2000 Ingo Molnar
*/
+/* I/O Unit Redirection Table */
+#define IO_APIC_REDIR_VECTOR_MASK 0x000FF
+#define IO_APIC_REDIR_DEST_LOGICAL 0x00800
+#define IO_APIC_REDIR_DEST_PHYSICAL 0x00000
+#define IO_APIC_REDIR_SEND_PENDING (1 << 12)
+#define IO_APIC_REDIR_REMOTE_IRR (1 << 14)
+#define IO_APIC_REDIR_LEVEL_TRIGGER (1 << 15)
+#define IO_APIC_REDIR_MASKED (1 << 16)
+
/*
* The structure of the IO-APIC:
*/
@@ -112,21 +121,32 @@ extern int nr_ioapic_registers[MAX_IO_APICS];
#define MP_MAX_IOAPIC_PIN 127
-struct mp_ioapic_routing {
- int apic_id;
- int gsi_base;
- int gsi_end;
- DECLARE_BITMAP(pin_programmed, MP_MAX_IOAPIC_PIN + 1);
+struct mp_config_ioapic {
+ unsigned long mp_apicaddr;
+ unsigned int mp_apicid;
+ unsigned char mp_type;
+ unsigned char mp_apicver;
+ unsigned char mp_flags;
+};
+
+struct mp_config_intsrc {
+ unsigned int mp_dstapic;
+ unsigned char mp_type;
+ unsigned char mp_irqtype;
+ unsigned short mp_irqflag;
+ unsigned char mp_srcbus;
+ unsigned char mp_srcbusirq;
+ unsigned char mp_dstirq;
};
/* I/O APIC entries */
-extern struct mpc_config_ioapic mp_ioapics[MAX_IO_APICS];
+extern struct mp_config_ioapic mp_ioapics[MAX_IO_APICS];
/* # of MP IRQ source entries */
extern int mp_irq_entries;
/* MP IRQ source entries */
-extern struct mpc_config_intsrc mp_irqs[MAX_IRQ_SOURCES];
+extern struct mp_config_intsrc mp_irqs[MAX_IRQ_SOURCES];
/* non-0 if default (table-less) MP configuration */
extern int mpc_default_type;
@@ -137,6 +157,9 @@ extern int sis_apic_bug;
/* 1 if "noapic" boot option passed */
extern int skip_ioapic_setup;
+/* 1 if the timer IRQ uses the '8259A Virtual Wire' mode */
+extern int timer_through_8259;
+
static inline void disable_ioapic_setup(void)
{
skip_ioapic_setup = 1;
@@ -162,6 +185,7 @@ extern void ioapic_init_mappings(void);
#else /* !CONFIG_X86_IO_APIC */
#define io_apic_assign_pci_irqs 0
+static const int timer_through_8259 = 0;
#endif
#endif
diff --git a/include/asm-x86/ipi.h b/include/asm-x86/ipi.h
index ecc80f341f37..bb1c09f7a76c 100644
--- a/include/asm-x86/ipi.h
+++ b/include/asm-x86/ipi.h
@@ -20,6 +20,7 @@
#include <asm/hw_irq.h>
#include <asm/apic.h>
+#include <asm/smp.h>
/*
* the following functions deal with sending IPIs between CPUs.
@@ -121,7 +122,7 @@ static inline void send_IPI_mask_sequence(cpumask_t mask, int vector)
* - mbligh
*/
local_irq_save(flags);
- for_each_cpu_mask(query_cpu, mask) {
+ for_each_cpu_mask_nr(query_cpu, mask) {
__send_IPI_dest_field(per_cpu(x86_cpu_to_apicid, query_cpu),
vector, APIC_DEST_PHYSICAL);
}
diff --git a/include/asm-x86/irq.h b/include/asm-x86/irq.h
index 7ba905465a53..1a2925757317 100644
--- a/include/asm-x86/irq.h
+++ b/include/asm-x86/irq.h
@@ -1,5 +1,50 @@
-#ifdef CONFIG_X86_32
-# include "irq_32.h"
+#ifndef _ASM_IRQ_H
+#define _ASM_IRQ_H
+/*
+ * (C) 1992, 1993 Linus Torvalds, (C) 1997 Ingo Molnar
+ *
+ * IRQ/IPI changes taken from work by Thomas Radke
+ * <tomsoft@informatik.tu-chemnitz.de>
+ */
+
+#include <asm/apicdef.h>
+#include <asm/irq_vectors.h>
+
+static inline int irq_canonicalize(int irq)
+{
+ return ((irq == 2) ? 9 : irq);
+}
+
+#ifdef CONFIG_X86_LOCAL_APIC
+# define ARCH_HAS_NMI_WATCHDOG
+#endif
+
+#ifdef CONFIG_4KSTACKS
+ extern void irq_ctx_init(int cpu);
+ extern void irq_ctx_exit(int cpu);
+# define __ARCH_HAS_DO_SOFTIRQ
#else
-# include "irq_64.h"
+# define irq_ctx_init(cpu) do { } while (0)
+# define irq_ctx_exit(cpu) do { } while (0)
+# ifdef CONFIG_X86_64
+# define __ARCH_HAS_DO_SOFTIRQ
+# endif
+#endif
+
+#ifdef CONFIG_IRQBALANCE
+extern int irqbalance_disable(char *str);
+#endif
+
+#ifdef CONFIG_HOTPLUG_CPU
+#include <linux/cpumask.h>
+extern void fixup_irqs(cpumask_t map);
#endif
+
+extern unsigned int do_IRQ(struct pt_regs *regs);
+extern void init_IRQ(void);
+extern void native_init_IRQ(void);
+
+/* Interrupt vector management */
+extern DECLARE_BITMAP(used_vectors, NR_VECTORS);
+
+#endif /* _ASM_IRQ_H */
diff --git a/include/asm-x86/irq_32.h b/include/asm-x86/irq_32.h
deleted file mode 100644
index 0b79f3185243..000000000000
--- a/include/asm-x86/irq_32.h
+++ /dev/null
@@ -1,51 +0,0 @@
-#ifndef _ASM_IRQ_H
-#define _ASM_IRQ_H
-
-/*
- * linux/include/asm/irq.h
- *
- * (C) 1992, 1993 Linus Torvalds, (C) 1997 Ingo Molnar
- *
- * IRQ/IPI changes taken from work by Thomas Radke
- * <tomsoft@informatik.tu-chemnitz.de>
- */
-
-#include <linux/sched.h>
-/* include comes from machine specific directory */
-#include "irq_vectors.h"
-#include <asm/thread_info.h>
-
-static inline int irq_canonicalize(int irq)
-{
- return ((irq == 2) ? 9 : irq);
-}
-
-#ifdef CONFIG_X86_LOCAL_APIC
-# define ARCH_HAS_NMI_WATCHDOG /* See include/linux/nmi.h */
-#endif
-
-#ifdef CONFIG_4KSTACKS
- extern void irq_ctx_init(int cpu);
- extern void irq_ctx_exit(int cpu);
-# define __ARCH_HAS_DO_SOFTIRQ
-#else
-# define irq_ctx_init(cpu) do { } while (0)
-# define irq_ctx_exit(cpu) do { } while (0)
-#endif
-
-#ifdef CONFIG_IRQBALANCE
-extern int irqbalance_disable(char *str);
-#endif
-
-#ifdef CONFIG_HOTPLUG_CPU
-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);
-
-/* Interrupt vector management */
-extern DECLARE_BITMAP(used_vectors, NR_VECTORS);
-
-#endif /* _ASM_IRQ_H */
diff --git a/include/asm-x86/irq_64.h b/include/asm-x86/irq_64.h
deleted file mode 100644
index 083d35a62c94..000000000000
--- a/include/asm-x86/irq_64.h
+++ /dev/null
@@ -1,51 +0,0 @@
-#ifndef _ASM_IRQ_H
-#define _ASM_IRQ_H
-
-/*
- * linux/include/asm/irq.h
- *
- * (C) 1992, 1993 Linus Torvalds, (C) 1997 Ingo Molnar
- *
- * IRQ/IPI changes taken from work by Thomas Radke
- * <tomsoft@informatik.tu-chemnitz.de>
- */
-
-#define TIMER_IRQ 0
-
-/*
- * 16 8259A IRQ's, 208 potential APIC interrupt sources.
- * Right now the APIC is mostly only used for SMP.
- * 256 vectors is an architectural limit. (we can have
- * more than 256 devices theoretically, but they will
- * have to use shared interrupts)
- * Since vectors 0x00-0x1f are used/reserved for the CPU,
- * the usable vector space is 0x20-0xff (224 vectors)
- */
-
-/*
- * The maximum number of vectors supported by x86_64 processors
- * is limited to 256. For processors other than x86_64, NR_VECTORS
- * should be changed accordingly.
- */
-#define NR_VECTORS 256
-
-#define FIRST_SYSTEM_VECTOR 0xef /* duplicated in hw_irq.h */
-
-#define NR_IRQS (NR_VECTORS + (32 * NR_CPUS))
-#define NR_IRQ_VECTORS NR_IRQS
-
-static inline int irq_canonicalize(int irq)
-{
- return ((irq == 2) ? 9 : irq);
-}
-
-#define ARCH_HAS_NMI_WATCHDOG /* See include/linux/nmi.h */
-
-#ifdef CONFIG_HOTPLUG_CPU
-#include <linux/cpumask.h>
-extern void fixup_irqs(cpumask_t map);
-#endif
-
-#define __ARCH_HAS_DO_SOFTIRQ 1
-
-#endif /* _ASM_IRQ_H */
diff --git a/include/asm-x86/irq_vectors.h b/include/asm-x86/irq_vectors.h
new file mode 100644
index 000000000000..b58581e2e24e
--- /dev/null
+++ b/include/asm-x86/irq_vectors.h
@@ -0,0 +1,169 @@
+#ifndef _ASM_IRQ_VECTORS_H
+#define _ASM_IRQ_VECTORS_H
+
+#include <linux/threads.h>
+
+#define NMI_VECTOR 0x02
+
+/*
+ * IDT vectors usable for external interrupt sources start
+ * at 0x20:
+ */
+#define FIRST_EXTERNAL_VECTOR 0x20
+
+#ifdef CONFIG_X86_32
+# define SYSCALL_VECTOR 0x80
+#else
+# define IA32_SYSCALL_VECTOR 0x80
+#endif
+
+/*
+ * Reserve the lowest usable priority level 0x20 - 0x2f for triggering
+ * cleanup after irq migration on 64 bit.
+ */
+#define IRQ_MOVE_CLEANUP_VECTOR FIRST_EXTERNAL_VECTOR
+
+/*
+ * Vectors 0x20-0x2f are used for ISA interrupts on 32 bit.
+ * Vectors 0x30-0x3f are used for ISA interrupts on 64 bit.
+ */
+#ifdef CONFIG_X86_32
+#define IRQ0_VECTOR (FIRST_EXTERNAL_VECTOR)
+#else
+#define IRQ0_VECTOR (FIRST_EXTERNAL_VECTOR + 0x10)
+#endif
+#define IRQ1_VECTOR (IRQ0_VECTOR + 1)
+#define IRQ2_VECTOR (IRQ0_VECTOR + 2)
+#define IRQ3_VECTOR (IRQ0_VECTOR + 3)
+#define IRQ4_VECTOR (IRQ0_VECTOR + 4)
+#define IRQ5_VECTOR (IRQ0_VECTOR + 5)
+#define IRQ6_VECTOR (IRQ0_VECTOR + 6)
+#define IRQ7_VECTOR (IRQ0_VECTOR + 7)
+#define IRQ8_VECTOR (IRQ0_VECTOR + 8)
+#define IRQ9_VECTOR (IRQ0_VECTOR + 9)
+#define IRQ10_VECTOR (IRQ0_VECTOR + 10)
+#define IRQ11_VECTOR (IRQ0_VECTOR + 11)
+#define IRQ12_VECTOR (IRQ0_VECTOR + 12)
+#define IRQ13_VECTOR (IRQ0_VECTOR + 13)
+#define IRQ14_VECTOR (IRQ0_VECTOR + 14)
+#define IRQ15_VECTOR (IRQ0_VECTOR + 15)
+
+/*
+ * Special IRQ vectors used by the SMP architecture, 0xf0-0xff
+ *
+ * some of the following vectors are 'rare', they are merged
+ * into a single vector (CALL_FUNCTION_VECTOR) to save vector space.
+ * TLB, reschedule and local APIC vectors are performance-critical.
+ *
+ * Vectors 0xf0-0xfa are free (reserved for future Linux use).
+ */
+#ifdef CONFIG_X86_32
+
+# define SPURIOUS_APIC_VECTOR 0xff
+# define ERROR_APIC_VECTOR 0xfe
+# define INVALIDATE_TLB_VECTOR 0xfd
+# define RESCHEDULE_VECTOR 0xfc
+# define CALL_FUNCTION_VECTOR 0xfb
+# define THERMAL_APIC_VECTOR 0xf0
+
+#else
+
+#define SPURIOUS_APIC_VECTOR 0xff
+#define ERROR_APIC_VECTOR 0xfe
+#define RESCHEDULE_VECTOR 0xfd
+#define CALL_FUNCTION_VECTOR 0xfc
+#define THERMAL_APIC_VECTOR 0xfa
+#define THRESHOLD_APIC_VECTOR 0xf9
+#define INVALIDATE_TLB_VECTOR_END 0xf7
+#define INVALIDATE_TLB_VECTOR_START 0xf0 /* f0-f7 used for TLB flush */
+
+#define NUM_INVALIDATE_TLB_VECTORS 8
+
+#endif
+
+/*
+ * Local APIC timer IRQ vector is on a different priority level,
+ * to work around the 'lost local interrupt if more than 2 IRQ
+ * sources per level' errata.
+ */
+#define LOCAL_TIMER_VECTOR 0xef
+
+/*
+ * First APIC vector available to drivers: (vectors 0x30-0xee) we
+ * start at 0x31(0x41) to spread out vectors evenly between priority
+ * levels. (0x80 is the syscall vector)
+ */
+#ifdef CONFIG_X86_32
+# define FIRST_DEVICE_VECTOR 0x31
+#else
+# define FIRST_DEVICE_VECTOR (IRQ15_VECTOR + 2)
+#endif
+
+#define NR_VECTORS 256
+
+#define FPU_IRQ 13
+
+#define FIRST_VM86_IRQ 3
+#define LAST_VM86_IRQ 15
+#define invalid_vm86_irq(irq) ((irq) < 3 || (irq) > 15)
+
+#if !defined(CONFIG_X86_VISWS) && !defined(CONFIG_X86_VOYAGER)
+
+# if defined(CONFIG_X86_IO_APIC) || defined(CONFIG_PARAVIRT)
+
+# define NR_IRQS 224
+
+# if (224 >= 32 * NR_CPUS)
+# define NR_IRQ_VECTORS NR_IRQS
+# else
+# define NR_IRQ_VECTORS (32 * NR_CPUS)
+# endif
+
+# else /* IO_APIC || PARAVIRT */
+
+# define NR_IRQS 16
+# define NR_IRQ_VECTORS NR_IRQS
+
+# endif
+
+#else /* !VISWS && !VOYAGER */
+
+# define NR_IRQS 224
+# define NR_IRQ_VECTORS NR_IRQS
+
+#endif /* VISWS */
+
+/* Voyager specific defines */
+/* These define the CPIs we use in linux */
+#define VIC_CPI_LEVEL0 0
+#define VIC_CPI_LEVEL1 1
+/* now the fake CPIs */
+#define VIC_TIMER_CPI 2
+#define VIC_INVALIDATE_CPI 3
+#define VIC_RESCHEDULE_CPI 4
+#define VIC_ENABLE_IRQ_CPI 5
+#define VIC_CALL_FUNCTION_CPI 6
+
+/* Now the QIC CPIs: Since we don't need the two initial levels,
+ * these are 2 less than the VIC CPIs */
+#define QIC_CPI_OFFSET 1
+#define QIC_TIMER_CPI (VIC_TIMER_CPI - QIC_CPI_OFFSET)
+#define QIC_INVALIDATE_CPI (VIC_INVALIDATE_CPI - QIC_CPI_OFFSET)
+#define QIC_RESCHEDULE_CPI (VIC_RESCHEDULE_CPI - QIC_CPI_OFFSET)
+#define QIC_ENABLE_IRQ_CPI (VIC_ENABLE_IRQ_CPI - QIC_CPI_OFFSET)
+#define QIC_CALL_FUNCTION_CPI (VIC_CALL_FUNCTION_CPI - QIC_CPI_OFFSET)
+
+#define VIC_START_FAKE_CPI VIC_TIMER_CPI
+#define VIC_END_FAKE_CPI VIC_CALL_FUNCTION_CPI
+
+/* this is the SYS_INT CPI. */
+#define VIC_SYS_INT 8
+#define VIC_CMN_INT 15
+
+/* This is the boot CPI for alternate processors. It gets overwritten
+ * by the above once the system has activated all available processors */
+#define VIC_CPU_BOOT_CPI VIC_CPI_LEVEL0
+#define VIC_CPU_BOOT_ERRATA_CPI (VIC_CPI_LEVEL0 + 8)
+
+
+#endif /* _ASM_IRQ_VECTORS_H */
diff --git a/include/asm-x86/irqflags.h b/include/asm-x86/irqflags.h
index c242527f970e..24d71b1eb189 100644
--- a/include/asm-x86/irqflags.h
+++ b/include/asm-x86/irqflags.h
@@ -179,8 +179,6 @@ static inline void trace_hardirqs_fixup(void)
* have a reliable stack. x86_64 only.
*/
#define SWAPGS_UNSAFE_STACK swapgs
-#define ARCH_TRACE_IRQS_ON call trace_hardirqs_on_thunk
-#define ARCH_TRACE_IRQS_OFF call trace_hardirqs_off_thunk
#define ARCH_LOCKDEP_SYS_EXIT call lockdep_sys_exit_thunk
#define ARCH_LOCKDEP_SYS_EXIT_IRQ \
TRACE_IRQS_ON; \
@@ -192,24 +190,6 @@ static inline void trace_hardirqs_fixup(void)
TRACE_IRQS_OFF;
#else
-#define ARCH_TRACE_IRQS_ON \
- pushl %eax; \
- pushl %ecx; \
- pushl %edx; \
- call trace_hardirqs_on; \
- popl %edx; \
- popl %ecx; \
- popl %eax;
-
-#define ARCH_TRACE_IRQS_OFF \
- pushl %eax; \
- pushl %ecx; \
- pushl %edx; \
- call trace_hardirqs_off; \
- popl %edx; \
- popl %ecx; \
- popl %eax;
-
#define ARCH_LOCKDEP_SYS_EXIT \
pushl %eax; \
pushl %ecx; \
@@ -223,8 +203,8 @@ static inline void trace_hardirqs_fixup(void)
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
-# define TRACE_IRQS_ON ARCH_TRACE_IRQS_ON
-# define TRACE_IRQS_OFF ARCH_TRACE_IRQS_OFF
+# define TRACE_IRQS_ON call trace_hardirqs_on_thunk;
+# define TRACE_IRQS_OFF call trace_hardirqs_off_thunk;
#else
# define TRACE_IRQS_ON
# define TRACE_IRQS_OFF
diff --git a/include/asm-x86/kvm.h b/include/asm-x86/kvm.h
index 80eefef2cc76..6f1840812e59 100644
--- a/include/asm-x86/kvm.h
+++ b/include/asm-x86/kvm.h
@@ -228,5 +228,6 @@ struct kvm_pit_state {
#define KVM_TRC_CLTS (KVM_TRC_HANDLER + 0x12)
#define KVM_TRC_LMSW (KVM_TRC_HANDLER + 0x13)
#define KVM_TRC_APIC_ACCESS (KVM_TRC_HANDLER + 0x14)
+#define KVM_TRC_TDP_FAULT (KVM_TRC_HANDLER + 0x15)
#endif
diff --git a/include/asm-x86/kvm_host.h b/include/asm-x86/kvm_host.h
index 1d8cd01fa514..851184ddff81 100644
--- a/include/asm-x86/kvm_host.h
+++ b/include/asm-x86/kvm_host.h
@@ -26,6 +26,7 @@
#define KVM_PRIVATE_MEM_SLOTS 4
#define KVM_PIO_PAGE_OFFSET 1
+#define KVM_COALESCED_MMIO_PAGE_OFFSET 2
#define CR3_PAE_RESERVED_BITS ((X86_CR3_PWT | X86_CR3_PCD) - 1)
#define CR3_NONPAE_RESERVED_BITS ((PAGE_SIZE-1) & ~(X86_CR3_PWT | X86_CR3_PCD))
@@ -78,6 +79,7 @@
#define KVM_MIN_FREE_MMU_PAGES 5
#define KVM_REFILL_PAGES 25
#define KVM_MAX_CPUID_ENTRIES 40
+#define KVM_NR_VAR_MTRR 8
extern spinlock_t kvm_lock;
extern struct list_head vm_list;
@@ -108,12 +110,12 @@ enum {
};
enum {
+ VCPU_SREG_ES,
VCPU_SREG_CS,
+ VCPU_SREG_SS,
VCPU_SREG_DS,
- VCPU_SREG_ES,
VCPU_SREG_FS,
VCPU_SREG_GS,
- VCPU_SREG_SS,
VCPU_SREG_TR,
VCPU_SREG_LDTR,
};
@@ -242,6 +244,7 @@ struct kvm_vcpu_arch {
gfn_t last_pt_write_gfn;
int last_pt_write_count;
u64 *last_pte_updated;
+ gfn_t last_pte_gfn;
struct {
gfn_t gfn; /* presumed gfn during guest pte update */
@@ -285,6 +288,10 @@ struct kvm_vcpu_arch {
struct kvm_vcpu_time_info hv_clock;
unsigned int time_offset;
struct page *time_page;
+
+ bool nmi_pending;
+
+ u64 mtrr[0x100];
};
struct kvm_mem_alias {
@@ -342,6 +349,7 @@ struct kvm_vcpu_stat {
u32 mmio_exits;
u32 signal_exits;
u32 irq_window_exits;
+ u32 nmi_window_exits;
u32 halt_exits;
u32 halt_wakeup;
u32 request_irq_exits;
@@ -377,7 +385,6 @@ struct kvm_x86_ops {
void (*prepare_guest_switch)(struct kvm_vcpu *vcpu);
void (*vcpu_load)(struct kvm_vcpu *vcpu, int cpu);
void (*vcpu_put)(struct kvm_vcpu *vcpu);
- void (*vcpu_decache)(struct kvm_vcpu *vcpu);
int (*set_guest_debug)(struct kvm_vcpu *vcpu,
struct kvm_debug_guest *dbg);
@@ -495,6 +502,10 @@ int emulator_get_dr(struct x86_emulate_ctxt *ctxt, int dr,
int emulator_set_dr(struct x86_emulate_ctxt *ctxt, int dr,
unsigned long value);
+void kvm_get_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg);
+int kvm_load_segment_descriptor(struct kvm_vcpu *vcpu, u16 selector,
+ int type_bits, int seg);
+
int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason);
void kvm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0);
@@ -513,6 +524,8 @@ void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code);
void kvm_inject_page_fault(struct kvm_vcpu *vcpu, unsigned long cr2,
u32 error_code);
+void kvm_inject_nmi(struct kvm_vcpu *vcpu);
+
void fx_init(struct kvm_vcpu *vcpu);
int emulator_read_std(unsigned long addr,
@@ -689,4 +702,28 @@ enum {
trace_mark(kvm_trace_##name, "%u %p %u %u %u %u %u %u", KVM_TRC_##evt, \
vcpu, 0, 0, 0, 0, 0, 0)
+#ifdef CONFIG_64BIT
+#define KVM_EX_ENTRY ".quad"
+#else
+#define KVM_EX_ENTRY ".long"
+#endif
+
+/*
+ * Hardware virtualization extension instructions may fault if a
+ * reboot turns off virtualization while processes are running.
+ * Trap the fault and ignore the instruction if that happens.
+ */
+asmlinkage void kvm_handle_fault_on_reboot(void);
+
+#define __kvm_handle_fault_on_reboot(insn) \
+ "666: " insn "\n\t" \
+ ".pushsection .text.fixup, \"ax\" \n" \
+ "667: \n\t" \
+ "push $666b \n\t" \
+ "jmp kvm_handle_fault_on_reboot \n\t" \
+ ".popsection \n\t" \
+ ".pushsection __ex_table, \"a\" \n\t" \
+ KVM_EX_ENTRY " 666b, 667b \n\t" \
+ ".popsection"
+
#endif
diff --git a/include/asm-x86/mach-default/irq_vectors.h b/include/asm-x86/mach-default/irq_vectors.h
deleted file mode 100644
index 881c63ca61ad..000000000000
--- a/include/asm-x86/mach-default/irq_vectors.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * This file should contain #defines for all of the interrupt vector
- * numbers used by this architecture.
- *
- * In addition, there are some standard defines:
- *
- * FIRST_EXTERNAL_VECTOR:
- * The first free place for external interrupts
- *
- * SYSCALL_VECTOR:
- * The IRQ vector a syscall makes the user to kernel transition
- * under.
- *
- * TIMER_IRQ:
- * The IRQ number the timer interrupt comes in at.
- *
- * NR_IRQS:
- * The total number of interrupt vectors (including all the
- * architecture specific interrupts) needed.
- *
- */
-#ifndef _ASM_IRQ_VECTORS_H
-#define _ASM_IRQ_VECTORS_H
-
-/*
- * IDT vectors usable for external interrupt sources start
- * at 0x20:
- */
-#define FIRST_EXTERNAL_VECTOR 0x20
-
-#define SYSCALL_VECTOR 0x80
-
-/*
- * Vectors 0x20-0x2f are used for ISA interrupts.
- */
-
-/*
- * Special IRQ vectors used by the SMP architecture, 0xf0-0xff
- *
- * some of the following vectors are 'rare', they are merged
- * into a single vector (CALL_FUNCTION_VECTOR) to save vector space.
- * TLB, reschedule and local APIC vectors are performance-critical.
- *
- * Vectors 0xf0-0xfa are free (reserved for future Linux use).
- */
-#define SPURIOUS_APIC_VECTOR 0xff
-#define ERROR_APIC_VECTOR 0xfe
-#define INVALIDATE_TLB_VECTOR 0xfd
-#define RESCHEDULE_VECTOR 0xfc
-#define CALL_FUNCTION_VECTOR 0xfb
-
-#define THERMAL_APIC_VECTOR 0xf0
-/*
- * Local APIC timer IRQ vector is on a different priority level,
- * to work around the 'lost local interrupt if more than 2 IRQ
- * sources per level' errata.
- */
-#define LOCAL_TIMER_VECTOR 0xef
-
-/*
- * First APIC vector available to drivers: (vectors 0x30-0xee)
- * we start at 0x31 to spread out vectors evenly between priority
- * levels. (0x80 is the syscall vector)
- */
-#define FIRST_DEVICE_VECTOR 0x31
-#define FIRST_SYSTEM_VECTOR 0xef
-
-#define TIMER_IRQ 0
-
-/*
- * 16 8259A IRQ's, 208 potential APIC interrupt sources.
- * Right now the APIC is mostly only used for SMP.
- * 256 vectors is an architectural limit. (we can have
- * more than 256 devices theoretically, but they will
- * have to use shared interrupts)
- * Since vectors 0x00-0x1f are used/reserved for the CPU,
- * the usable vector space is 0x20-0xff (224 vectors)
- */
-
-/*
- * The maximum number of vectors supported by i386 processors
- * is limited to 256. For processors other than i386, NR_VECTORS
- * should be changed accordingly.
- */
-#define NR_VECTORS 256
-
-#include "irq_vectors_limits.h"
-
-#define FPU_IRQ 13
-
-#define FIRST_VM86_IRQ 3
-#define LAST_VM86_IRQ 15
-#define invalid_vm86_irq(irq) ((irq) < 3 || (irq) > 15)
-
-
-#endif /* _ASM_IRQ_VECTORS_H */
diff --git a/include/asm-x86/mach-default/irq_vectors_limits.h b/include/asm-x86/mach-default/irq_vectors_limits.h
deleted file mode 100644
index a90c7a60109f..000000000000
--- a/include/asm-x86/mach-default/irq_vectors_limits.h
+++ /dev/null
@@ -1,16 +0,0 @@
-#ifndef _ASM_IRQ_VECTORS_LIMITS_H
-#define _ASM_IRQ_VECTORS_LIMITS_H
-
-#if defined(CONFIG_X86_IO_APIC) || defined(CONFIG_PARAVIRT)
-#define NR_IRQS 224
-# if (224 >= 32 * NR_CPUS)
-# define NR_IRQ_VECTORS NR_IRQS
-# else
-# define NR_IRQ_VECTORS (32 * NR_CPUS)
-# endif
-#else
-#define NR_IRQS 16
-#define NR_IRQ_VECTORS NR_IRQS
-#endif
-
-#endif /* _ASM_IRQ_VECTORS_LIMITS_H */
diff --git a/include/asm-x86/mach-default/smpboot_hooks.h b/include/asm-x86/mach-default/smpboot_hooks.h
index 56d0e1fa0258..b63c52182006 100644
--- a/include/asm-x86/mach-default/smpboot_hooks.h
+++ b/include/asm-x86/mach-default/smpboot_hooks.h
@@ -41,8 +41,10 @@ static inline void __init smpboot_setup_io_apic(void)
*/
if (!skip_ioapic_setup && nr_ioapics)
setup_IO_APIC();
- else
+ else {
nr_ioapics = 0;
+ localise_nmi_watchdog();
+ }
}
static inline void smpboot_clear_io_apic(void)
diff --git a/include/asm-x86/mach-generic/mach_mpparse.h b/include/asm-x86/mach-generic/mach_mpparse.h
index 0d0b5ba2e9d1..586cadbf3787 100644
--- a/include/asm-x86/mach-generic/mach_mpparse.h
+++ b/include/asm-x86/mach-generic/mach_mpparse.h
@@ -1,7 +1,10 @@
#ifndef _MACH_MPPARSE_H
#define _MACH_MPPARSE_H 1
-int mps_oem_check(struct mp_config_table *mpc, char *oem, char *productid);
-int acpi_madt_oem_check(char *oem_id, char *oem_table_id);
+
+extern int mps_oem_check(struct mp_config_table *mpc, char *oem,
+ char *productid);
+
+extern int acpi_madt_oem_check(char *oem_id, char *oem_table_id);
#endif
diff --git a/include/asm-x86/mach-numaq/mach_apic.h b/include/asm-x86/mach-numaq/mach_apic.h
index 75a56e5afbe7..d802465e026a 100644
--- a/include/asm-x86/mach-numaq/mach_apic.h
+++ b/include/asm-x86/mach-numaq/mach_apic.h
@@ -20,8 +20,14 @@ static inline cpumask_t target_cpus(void)
#define INT_DELIVERY_MODE dest_LowestPrio
#define INT_DEST_MODE 0 /* physical delivery on LOCAL quad */
-#define check_apicid_used(bitmap, apicid) physid_isset(apicid, bitmap)
-#define check_apicid_present(bit) physid_isset(bit, phys_cpu_present_map)
+static inline unsigned long check_apicid_used(physid_mask_t bitmap, int apicid)
+{
+ return physid_isset(apicid, bitmap);
+}
+static inline unsigned long check_apicid_present(int bit)
+{
+ return physid_isset(bit, phys_cpu_present_map);
+}
#define apicid_cluster(apicid) (apicid & 0xF0)
static inline int apic_id_registered(void)
@@ -77,11 +83,6 @@ static inline int cpu_present_to_apicid(int mps_cpu)
return BAD_APICID;
}
-static inline int generate_logical_apicid(int quad, int phys_apicid)
-{
- return (quad << 4) + (phys_apicid ? phys_apicid << 1 : 1);
-}
-
static inline int apicid_to_node(int logical_apicid)
{
return logical_apicid >> 4;
@@ -95,30 +96,6 @@ static inline physid_mask_t apicid_to_cpu_present(int logical_apicid)
return physid_mask_of_physid(cpu + 4*node);
}
-struct mpc_config_translation {
- unsigned char mpc_type;
- unsigned char trans_len;
- unsigned char trans_type;
- unsigned char trans_quad;
- unsigned char trans_global;
- unsigned char trans_local;
- unsigned short trans_reserved;
-};
-
-static inline int mpc_apic_id(struct mpc_config_processor *m,
- struct mpc_config_translation *translation_record)
-{
- int quad = translation_record->trans_quad;
- int logical_apicid = generate_logical_apicid(quad, m->mpc_apicid);
-
- printk("Processor #%d %u:%u APIC version %d (quad %d, apic %d)\n",
- m->mpc_apicid,
- (m->mpc_cpufeature & CPU_FAMILY_MASK) >> 8,
- (m->mpc_cpufeature & CPU_MODEL_MASK) >> 4,
- m->mpc_apicver, quad, logical_apicid);
- return logical_apicid;
-}
-
extern void *xquad_portio;
static inline void setup_portio_remap(void)
diff --git a/include/asm-x86/mach-numaq/mach_mpparse.h b/include/asm-x86/mach-numaq/mach_mpparse.h
index 459b12401187..626aef6b155f 100644
--- a/include/asm-x86/mach-numaq/mach_mpparse.h
+++ b/include/asm-x86/mach-numaq/mach_mpparse.h
@@ -1,14 +1,7 @@
#ifndef __ASM_MACH_MPPARSE_H
#define __ASM_MACH_MPPARSE_H
-extern void mpc_oem_bus_info(struct mpc_config_bus *m, char *name,
- struct mpc_config_translation *translation);
-extern void mpc_oem_pci_bus(struct mpc_config_bus *m,
- struct mpc_config_translation *translation);
-
-/* Hook from generic ACPI tables.c */
-static inline void acpi_madt_oem_check(char *oem_id, char *oem_table_id)
-{
-}
+extern void numaq_mps_oem_check(struct mp_config_table *mpc, char *oem,
+ char *productid);
#endif /* __ASM_MACH_MPPARSE_H */
diff --git a/include/asm-x86/mach-visws/irq_vectors.h b/include/asm-x86/mach-visws/irq_vectors.h
deleted file mode 100644
index cb572d8db505..000000000000
--- a/include/asm-x86/mach-visws/irq_vectors.h
+++ /dev/null
@@ -1,62 +0,0 @@
-#ifndef _ASM_IRQ_VECTORS_H
-#define _ASM_IRQ_VECTORS_H
-
-/*
- * IDT vectors usable for external interrupt sources start
- * at 0x20:
- */
-#define FIRST_EXTERNAL_VECTOR 0x20
-
-#define SYSCALL_VECTOR 0x80
-
-/*
- * Vectors 0x20-0x2f are used for ISA interrupts.
- */
-
-/*
- * Special IRQ vectors used by the SMP architecture, 0xf0-0xff
- *
- * some of the following vectors are 'rare', they are merged
- * into a single vector (CALL_FUNCTION_VECTOR) to save vector space.
- * TLB, reschedule and local APIC vectors are performance-critical.
- *
- * Vectors 0xf0-0xfa are free (reserved for future Linux use).
- */
-#define SPURIOUS_APIC_VECTOR 0xff
-#define ERROR_APIC_VECTOR 0xfe
-#define INVALIDATE_TLB_VECTOR 0xfd
-#define RESCHEDULE_VECTOR 0xfc
-#define CALL_FUNCTION_VECTOR 0xfb
-
-#define THERMAL_APIC_VECTOR 0xf0
-/*
- * Local APIC timer IRQ vector is on a different priority level,
- * to work around the 'lost local interrupt if more than 2 IRQ
- * sources per level' errata.
- */
-#define LOCAL_TIMER_VECTOR 0xef
-
-/*
- * First APIC vector available to drivers: (vectors 0x30-0xee)
- * we start at 0x31 to spread out vectors evenly between priority
- * levels. (0x80 is the syscall vector)
- */
-#define FIRST_DEVICE_VECTOR 0x31
-#define FIRST_SYSTEM_VECTOR 0xef
-
-#define TIMER_IRQ 0
-
-/*
- * IRQ definitions
- */
-#define NR_VECTORS 256
-#define NR_IRQS 224
-#define NR_IRQ_VECTORS NR_IRQS
-
-#define FPU_IRQ 13
-
-#define FIRST_VM86_IRQ 3
-#define LAST_VM86_IRQ 15
-#define invalid_vm86_irq(irq) ((irq) < 3 || (irq) > 15)
-
-#endif /* _ASM_IRQ_VECTORS_H */
diff --git a/include/asm-x86/mach-voyager/irq_vectors.h b/include/asm-x86/mach-voyager/irq_vectors.h
deleted file mode 100644
index 165421f5821c..000000000000
--- a/include/asm-x86/mach-voyager/irq_vectors.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/* -*- mode: c; c-basic-offset: 8 -*- */
-
-/* Copyright (C) 2002
- *
- * Author: James.Bottomley@HansenPartnership.com
- *
- * linux/arch/i386/voyager/irq_vectors.h
- *
- * This file provides definitions for the VIC and QIC CPIs
- */
-
-#ifndef _ASM_IRQ_VECTORS_H
-#define _ASM_IRQ_VECTORS_H
-
-/*
- * IDT vectors usable for external interrupt sources start
- * at 0x20:
- */
-#define FIRST_EXTERNAL_VECTOR 0x20
-
-#define SYSCALL_VECTOR 0x80
-
-/*
- * Vectors 0x20-0x2f are used for ISA interrupts.
- */
-
-/* These define the CPIs we use in linux */
-#define VIC_CPI_LEVEL0 0
-#define VIC_CPI_LEVEL1 1
-/* now the fake CPIs */
-#define VIC_TIMER_CPI 2
-#define VIC_INVALIDATE_CPI 3
-#define VIC_RESCHEDULE_CPI 4
-#define VIC_ENABLE_IRQ_CPI 5
-#define VIC_CALL_FUNCTION_CPI 6
-
-/* Now the QIC CPIs: Since we don't need the two initial levels,
- * these are 2 less than the VIC CPIs */
-#define QIC_CPI_OFFSET 1
-#define QIC_TIMER_CPI (VIC_TIMER_CPI - QIC_CPI_OFFSET)
-#define QIC_INVALIDATE_CPI (VIC_INVALIDATE_CPI - QIC_CPI_OFFSET)
-#define QIC_RESCHEDULE_CPI (VIC_RESCHEDULE_CPI - QIC_CPI_OFFSET)
-#define QIC_ENABLE_IRQ_CPI (VIC_ENABLE_IRQ_CPI - QIC_CPI_OFFSET)
-#define QIC_CALL_FUNCTION_CPI (VIC_CALL_FUNCTION_CPI - QIC_CPI_OFFSET)
-
-#define VIC_START_FAKE_CPI VIC_TIMER_CPI
-#define VIC_END_FAKE_CPI VIC_CALL_FUNCTION_CPI
-
-/* this is the SYS_INT CPI. */
-#define VIC_SYS_INT 8
-#define VIC_CMN_INT 15
-
-/* This is the boot CPI for alternate processors. It gets overwritten
- * by the above once the system has activated all available processors */
-#define VIC_CPU_BOOT_CPI VIC_CPI_LEVEL0
-#define VIC_CPU_BOOT_ERRATA_CPI (VIC_CPI_LEVEL0 + 8)
-
-#define NR_VECTORS 256
-#define NR_IRQS 224
-#define NR_IRQ_VECTORS NR_IRQS
-
-#define FPU_IRQ 13
-
-#define FIRST_VM86_IRQ 3
-#define LAST_VM86_IRQ 15
-#define invalid_vm86_irq(irq) ((irq) < 3 || (irq) > 15)
-
-#ifndef __ASSEMBLY__
-extern asmlinkage void vic_cpi_interrupt(void);
-extern asmlinkage void vic_sys_interrupt(void);
-extern asmlinkage void vic_cmn_interrupt(void);
-extern asmlinkage void qic_timer_interrupt(void);
-extern asmlinkage void qic_invalidate_interrupt(void);
-extern asmlinkage void qic_reschedule_interrupt(void);
-extern asmlinkage void qic_enable_irq_interrupt(void);
-extern asmlinkage void qic_call_function_interrupt(void);
-#endif /* !__ASSEMBLY__ */
-
-#endif /* _ASM_IRQ_VECTORS_H */
diff --git a/include/asm-x86/mmconfig.h b/include/asm-x86/mmconfig.h
new file mode 100644
index 000000000000..95beda07c6fa
--- /dev/null
+++ b/include/asm-x86/mmconfig.h
@@ -0,0 +1,12 @@
+#ifndef _ASM_MMCONFIG_H
+#define _ASM_MMCONFIG_H
+
+#ifdef CONFIG_PCI_MMCONFIG
+extern void __cpuinit fam10h_check_enable_mmcfg(void);
+extern void __init check_enable_amd_mmconf_dmi(void);
+#else
+static inline void fam10h_check_enable_mmcfg(void) { }
+static inline void check_enable_amd_mmconf_dmi(void) { }
+#endif
+
+#endif
diff --git a/include/asm-x86/mmzone_32.h b/include/asm-x86/mmzone_32.h
index cb2cad0b65a7..b2298a227567 100644
--- a/include/asm-x86/mmzone_32.h
+++ b/include/asm-x86/mmzone_32.h
@@ -12,11 +12,9 @@
extern struct pglist_data *node_data[];
#define NODE_DATA(nid) (node_data[nid])
-#ifdef CONFIG_X86_NUMAQ
- #include <asm/numaq.h>
-#elif defined(CONFIG_ACPI_SRAT)/* summit or generic arch */
- #include <asm/srat.h>
-#endif
+#include <asm/numaq.h>
+/* summit or generic arch */
+#include <asm/srat.h>
extern int get_memcfg_numa_flat(void);
/*
@@ -26,28 +24,20 @@ extern int get_memcfg_numa_flat(void);
*/
static inline void get_memcfg_numa(void)
{
-#ifdef CONFIG_X86_NUMAQ
+
if (get_memcfg_numaq())
return;
-#elif defined(CONFIG_ACPI_SRAT)
if (get_memcfg_from_srat())
return;
-#endif
-
get_memcfg_numa_flat();
}
extern int early_pfn_to_nid(unsigned long pfn);
-extern void numa_kva_reserve(void);
#else /* !CONFIG_NUMA */
#define get_memcfg_numa get_memcfg_numa_flat
-#define get_zholes_size(n) (0)
-static inline void numa_kva_reserve(void)
-{
-}
#endif /* CONFIG_NUMA */
#ifdef CONFIG_DISCONTIGMEM
@@ -55,14 +45,14 @@ static inline void numa_kva_reserve(void)
/*
* generic node memory support, the following assumptions apply:
*
- * 1) memory comes in 256Mb contigious chunks which are either present or not
+ * 1) memory comes in 64Mb contigious chunks which are either present or not
* 2) we will not have more than 64Gb in total
*
* for now assume that 64Gb is max amount of RAM for whole system
* 64Gb / 4096bytes/page = 16777216 pages
*/
#define MAX_NR_PAGES 16777216
-#define MAX_ELEMENTS 256
+#define MAX_ELEMENTS 1024
#define PAGES_PER_ELEMENT (MAX_NR_PAGES/MAX_ELEMENTS)
extern s8 physnode_map[];
@@ -87,9 +77,6 @@ static inline int pfn_to_nid(unsigned long pfn)
__pgdat->node_start_pfn + __pgdat->node_spanned_pages; \
})
-#ifdef CONFIG_X86_NUMAQ /* we have contiguous memory on NUMA-Q */
-#define pfn_valid(pfn) ((pfn) < num_physpages)
-#else
static inline int pfn_valid(int pfn)
{
int nid = pfn_to_nid(pfn);
@@ -98,7 +85,6 @@ static inline int pfn_valid(int pfn)
return (pfn < node_end_pfn(nid));
return 0;
}
-#endif /* CONFIG_X86_NUMAQ */
#endif /* CONFIG_DISCONTIGMEM */
diff --git a/include/asm-x86/mpspec.h b/include/asm-x86/mpspec.h
index 57a991b9c053..b8ba37496e2d 100644
--- a/include/asm-x86/mpspec.h
+++ b/include/asm-x86/mpspec.h
@@ -13,6 +13,12 @@ extern int apic_version[MAX_APICS];
extern u8 apicid_2_node[];
extern int pic_mode;
+#ifdef CONFIG_X86_NUMAQ
+extern int mp_bus_id_to_node[MAX_MP_BUSSES];
+extern int mp_bus_id_to_local[MAX_MP_BUSSES];
+extern int quad_local_to_mp_bus_id [NR_CPUS/4][4];
+#endif
+
#define MAX_APICID 256
#else
@@ -21,26 +27,30 @@ extern int pic_mode;
/* Each PCI slot may be a combo card with its own bus. 4 IRQ pins per slot. */
#define MAX_IRQ_SOURCES (MAX_MP_BUSSES * 4)
+#endif
+
extern void early_find_smp_config(void);
extern void early_get_smp_config(void);
-#endif
-
#if defined(CONFIG_MCA) || defined(CONFIG_EISA)
extern int mp_bus_id_to_type[MAX_MP_BUSSES];
#endif
extern DECLARE_BITMAP(mp_bus_not_pci, MAX_MP_BUSSES);
-extern int mp_bus_id_to_pci_bus[MAX_MP_BUSSES];
-
extern unsigned int boot_cpu_physical_apicid;
+extern unsigned int max_physical_apicid;
extern int smp_found_config;
extern int mpc_default_type;
extern unsigned long mp_lapic_addr;
extern void find_smp_config(void);
extern void get_smp_config(void);
+#ifdef CONFIG_X86_MPPARSE
+extern void early_reserve_e820_mpc_new(void);
+#else
+static inline void early_reserve_e820_mpc_new(void) { }
+#endif
void __cpuinit generic_processor_info(int apicid, int version);
#ifdef CONFIG_ACPI
@@ -49,6 +59,18 @@ extern void mp_override_legacy_irq(u8 bus_irq, u8 polarity, u8 trigger,
u32 gsi);
extern void mp_config_acpi_legacy_irqs(void);
extern int mp_register_gsi(u32 gsi, int edge_level, int active_high_low);
+extern void MP_intsrc_info(struct mpc_config_intsrc *m);
+#ifdef CONFIG_X86_IO_APIC
+extern int mp_config_acpi_gsi(unsigned char number, unsigned int devfn, u8 pin,
+ u32 gsi, int triggering, int polarity);
+#else
+static inline int
+mp_config_acpi_gsi(unsigned char number, unsigned int devfn, u8 pin,
+ u32 gsi, int triggering, int polarity)
+{
+ return 0;
+}
+#endif
#endif /* CONFIG_ACPI */
#define PHYSID_ARRAY_SIZE BITS_TO_LONGS(MAX_APICS)
diff --git a/include/asm-x86/msr-index.h b/include/asm-x86/msr-index.h
index 09413ad39d3c..44bce773012e 100644
--- a/include/asm-x86/msr-index.h
+++ b/include/asm-x86/msr-index.h
@@ -111,7 +111,9 @@
#define MSR_K8_TOP_MEM2 0xc001001d
#define MSR_K8_SYSCFG 0xc0010010
#define MSR_K8_HWCR 0xc0010015
-#define MSR_K8_ENABLE_C1E 0xc0010055
+#define MSR_K8_INT_PENDING_MSG 0xc0010055
+/* C1E active bits in int pending message */
+#define K8_INTP_C1E_ACTIVE_MASK 0x18000000
#define MSR_K8_TSEG_ADDR 0xc0010112
#define K8_MTRRFIXRANGE_DRAM_ENABLE 0x00040000 /* MtrrFixDramEn bit */
#define K8_MTRRFIXRANGE_DRAM_MODIFY 0x00080000 /* MtrrFixDramModEn bit */
diff --git a/include/asm-x86/nmi.h b/include/asm-x86/nmi.h
index 1e363021e72f..28a7f4e4fd4a 100644
--- a/include/asm-x86/nmi.h
+++ b/include/asm-x86/nmi.h
@@ -38,15 +38,12 @@ static inline void unset_nmi_pm_callback(struct pm_dev *dev)
#ifdef CONFIG_X86_64
extern void default_do_nmi(struct pt_regs *);
-extern void die_nmi(char *str, struct pt_regs *regs, int do_panic);
-extern void nmi_watchdog_default(void);
-#else
-#define nmi_watchdog_default() do {} while (0)
#endif
+extern void die_nmi(char *str, struct pt_regs *regs, int do_panic);
+extern void nmi_watchdog_default(void);
extern int check_nmi_watchdog(void);
extern int nmi_watchdog_enabled;
-extern int unknown_nmi_panic;
extern int avail_to_resrv_perfctr_nmi_bit(unsigned int);
extern int avail_to_resrv_perfctr_nmi(unsigned int);
extern int reserve_perfctr_nmi(unsigned int);
@@ -78,6 +75,11 @@ extern int unknown_nmi_panic;
void __trigger_all_cpu_backtrace(void);
#define trigger_all_cpu_backtrace() __trigger_all_cpu_backtrace()
+static inline void localise_nmi_watchdog(void)
+{
+ if (nmi_watchdog == NMI_IO_APIC)
+ nmi_watchdog = NMI_LOCAL_APIC;
+}
#endif
void lapic_watchdog_stop(void);
diff --git a/include/asm-x86/numa_64.h b/include/asm-x86/numa_64.h
index 22e87c9f6a80..b510daf4f4d8 100644
--- a/include/asm-x86/numa_64.h
+++ b/include/asm-x86/numa_64.h
@@ -14,11 +14,9 @@ extern int compute_hash_shift(struct bootnode *nodes, int numblks,
#define ZONE_ALIGN (1UL << (MAX_ORDER+PAGE_SHIFT))
-extern void numa_add_cpu(int cpu);
extern void numa_init_array(void);
extern int numa_off;
-extern void numa_set_node(int cpu, int node);
extern void srat_reserve_add_area(int nodeid);
extern int hotadd_percent;
@@ -31,15 +29,16 @@ extern void setup_node_bootmem(int nodeid, unsigned long start,
#ifdef CONFIG_NUMA
extern void __init init_cpu_to_node(void);
-
-static inline void clear_node_cpumask(int cpu)
-{
- clear_bit(cpu, (unsigned long *)&node_to_cpumask_map[cpu_to_node(cpu)]);
-}
-
+extern void __cpuinit numa_set_node(int cpu, int node);
+extern void __cpuinit numa_clear_node(int cpu);
+extern void __cpuinit numa_add_cpu(int cpu);
+extern void __cpuinit numa_remove_cpu(int cpu);
#else
-#define init_cpu_to_node() do {} while (0)
-#define clear_node_cpumask(cpu) do {} while (0)
+static inline void init_cpu_to_node(void) { }
+static inline void numa_set_node(int cpu, int node) { }
+static inline void numa_clear_node(int cpu) { }
+static inline void numa_add_cpu(int cpu, int node) { }
+static inline void numa_remove_cpu(int cpu) { }
#endif
#endif
diff --git a/include/asm-x86/numaq.h b/include/asm-x86/numaq.h
index 94b86c31239a..ef068d2465d6 100644
--- a/include/asm-x86/numaq.h
+++ b/include/asm-x86/numaq.h
@@ -28,6 +28,7 @@
#ifdef CONFIG_X86_NUMAQ
+extern int found_numaq;
extern int get_memcfg_numaq(void);
/*
@@ -156,9 +157,10 @@ struct sys_cfg_data {
struct eachquadmem eq[MAX_NUMNODES]; /* indexed by quad id */
};
-static inline unsigned long *get_zholes_size(int nid)
+#else
+static inline int get_memcfg_numaq(void)
{
- return NULL;
+ return 0;
}
#endif /* CONFIG_X86_NUMAQ */
#endif /* NUMAQ_H */
diff --git a/include/asm-x86/page.h b/include/asm-x86/page.h
index dc936dddf161..b52ed85f32f5 100644
--- a/include/asm-x86/page.h
+++ b/include/asm-x86/page.h
@@ -51,8 +51,15 @@
#ifndef __ASSEMBLY__
+typedef struct { pgdval_t pgd; } pgd_t;
+typedef struct { pgprotval_t pgprot; } pgprot_t;
+
extern int page_is_ram(unsigned long pagenr);
extern int devmem_is_allowed(unsigned long pagenr);
+extern void map_devmem(unsigned long pfn, unsigned long size,
+ pgprot_t vma_prot);
+extern void unmap_devmem(unsigned long pfn, unsigned long size,
+ pgprot_t vma_prot);
extern unsigned long max_pfn_mapped;
@@ -74,9 +81,6 @@ static inline void copy_user_page(void *to, void *from, unsigned long vaddr,
alloc_page_vma(GFP_HIGHUSER | __GFP_ZERO | movableflags, vma, vaddr)
#define __HAVE_ARCH_ALLOC_ZEROED_USER_HIGHPAGE
-typedef struct { pgdval_t pgd; } pgd_t;
-typedef struct { pgprotval_t pgprot; } pgprot_t;
-
static inline pgd_t native_make_pgd(pgdval_t val)
{
return (pgd_t) { val };
@@ -160,6 +164,7 @@ static inline pteval_t native_pte_val(pte_t pte)
#endif
#define pte_val(x) native_pte_val(x)
+#define pte_flags(x) native_pte_val(x)
#define __pte(x) native_make_pte(x)
#endif /* CONFIG_PARAVIRT */
diff --git a/include/asm-x86/page_32.h b/include/asm-x86/page_32.h
index 424e82f8ae27..73ed2e4ebf95 100644
--- a/include/asm-x86/page_32.h
+++ b/include/asm-x86/page_32.h
@@ -13,8 +13,17 @@
*/
#define __PAGE_OFFSET _AC(CONFIG_PAGE_OFFSET, UL)
+#ifdef CONFIG_4KSTACKS
+#define THREAD_ORDER 0
+#else
+#define THREAD_ORDER 1
+#endif
+#define THREAD_SIZE (PAGE_SIZE << THREAD_ORDER)
+
+
#ifdef CONFIG_X86_PAE
-#define __PHYSICAL_MASK_SHIFT 36
+/* 44=32+12, the limit we can fit into an unsigned long pfn */
+#define __PHYSICAL_MASK_SHIFT 44
#define __VIRTUAL_MASK_SHIFT 32
#define PAGETABLE_LEVELS 3
diff --git a/include/asm-x86/paravirt.h b/include/asm-x86/paravirt.h
index 0f13b945e240..5ea37a48eecb 100644
--- a/include/asm-x86/paravirt.h
+++ b/include/asm-x86/paravirt.h
@@ -239,6 +239,7 @@ struct pv_mmu_ops {
unsigned long addr, pte_t *ptep);
pteval_t (*pte_val)(pte_t);
+ pteval_t (*pte_flags)(pte_t);
pte_t (*make_pte)(pteval_t pte);
pgdval_t (*pgd_val)(pgd_t);
@@ -996,6 +997,20 @@ static inline pteval_t pte_val(pte_t pte)
return ret;
}
+static inline pteval_t pte_flags(pte_t pte)
+{
+ pteval_t ret;
+
+ if (sizeof(pteval_t) > sizeof(long))
+ ret = PVOP_CALL2(pteval_t, pv_mmu_ops.pte_flags,
+ pte.pte, (u64)pte.pte >> 32);
+ else
+ ret = PVOP_CALL1(pteval_t, pv_mmu_ops.pte_flags,
+ pte.pte);
+
+ return ret;
+}
+
static inline pgd_t __pgd(pgdval_t val)
{
pgdval_t ret;
diff --git a/include/asm-x86/pci-direct.h b/include/asm-x86/pci-direct.h
index 5b21485be573..80c775d9fe20 100644
--- a/include/asm-x86/pci-direct.h
+++ b/include/asm-x86/pci-direct.h
@@ -11,7 +11,11 @@ extern u8 read_pci_config_byte(u8 bus, u8 slot, u8 func, u8 offset);
extern u16 read_pci_config_16(u8 bus, u8 slot, u8 func, u8 offset);
extern void write_pci_config(u8 bus, u8 slot, u8 func, u8 offset, u32 val);
extern void write_pci_config_byte(u8 bus, u8 slot, u8 func, u8 offset, u8 val);
+extern void write_pci_config_16(u8 bus, u8 slot, u8 func, u8 offset, u16 val);
extern int early_pci_allowed(void);
+extern unsigned int pci_early_dump_regs;
+extern void early_dump_pci_device(u8 bus, u8 slot, u8 func);
+extern void early_dump_pci_devices(void);
#endif
diff --git a/include/asm-x86/pci.h b/include/asm-x86/pci.h
index 30bbde0cb34b..2db14cf17db8 100644
--- a/include/asm-x86/pci.h
+++ b/include/asm-x86/pci.h
@@ -18,6 +18,8 @@ struct pci_sysdata {
#endif
};
+extern int pci_routeirq;
+
/* scan a bus after allocating a pci_sysdata for it */
extern struct pci_bus *pci_scan_bus_on_node(int busno, struct pci_ops *ops,
int node);
diff --git a/include/asm-x86/pda.h b/include/asm-x86/pda.h
index 101fb9e11954..385ffbe4c169 100644
--- a/include/asm-x86/pda.h
+++ b/include/asm-x86/pda.h
@@ -16,12 +16,12 @@ struct x8664_pda {
unsigned long oldrsp; /* 24 user rsp for system call */
int irqcount; /* 32 Irq nesting counter. Starts -1 */
unsigned int cpunumber; /* 36 Logical CPU number */
-#ifdef CONFIG_CC_STACKPROTECTOR
unsigned long stack_canary; /* 40 stack canary value */
/* gcc-ABI: this canary MUST be at
offset 40!!! */
-#endif
char *irqstackptr;
+ short nodenumber; /* number of current node (32k max) */
+ short in_bootmem; /* pda lives in bootmem */
unsigned int __softirq_pending;
unsigned int __nmi_count; /* number of NMI on this CPUs */
short mmu_state;
@@ -37,8 +37,7 @@ struct x8664_pda {
unsigned irq_spurious_count;
} ____cacheline_aligned_in_smp;
-extern struct x8664_pda *_cpu_pda[];
-extern struct x8664_pda boot_cpu_pda[];
+extern struct x8664_pda **_cpu_pda;
extern void pda_init(int);
#define cpu_pda(i) (_cpu_pda[i])
diff --git a/include/asm-x86/percpu.h b/include/asm-x86/percpu.h
index 736fc3bb8e1e..47d7c7bbe62c 100644
--- a/include/asm-x86/percpu.h
+++ b/include/asm-x86/percpu.h
@@ -108,6 +108,11 @@ do { \
: "+m" (var) \
: "ri" ((T__)val)); \
break; \
+ case 8: \
+ asm(op "q %1,"__percpu_seg"%0" \
+ : "+m" (var) \
+ : "ri" ((T__)val)); \
+ break; \
default: __bad_percpu_size(); \
} \
} while (0)
@@ -131,16 +136,130 @@ do { \
: "=r" (ret__) \
: "m" (var)); \
break; \
+ case 8: \
+ asm(op "q "__percpu_seg"%1,%0" \
+ : "=r" (ret__) \
+ : "m" (var)); \
+ break; \
default: __bad_percpu_size(); \
} \
ret__; \
})
-#define x86_read_percpu(var) percpu_from_op("mov", per_cpu__##var)
-#define x86_write_percpu(var, val) percpu_to_op("mov", per_cpu__##var, val)
-#define x86_add_percpu(var, val) percpu_to_op("add", per_cpu__##var, val)
-#define x86_sub_percpu(var, val) percpu_to_op("sub", per_cpu__##var, val)
-#define x86_or_percpu(var, val) percpu_to_op("or", per_cpu__##var, val)
+#define percpu_addr_op(op, var) \
+({ \
+ switch (sizeof(var)) { \
+ case 1: \
+ asm(op "b "__percpu_seg"%0" \
+ : : "m"(var)); \
+ break; \
+ case 2: \
+ asm(op "w "__percpu_seg"%0" \
+ : : "m"(var)); \
+ break; \
+ case 4: \
+ asm(op "l "__percpu_seg"%0" \
+ : : "m"(var)); \
+ break; \
+ case 8: \
+ asm(op "q "__percpu_seg"%0" \
+ : : "m"(var)); \
+ break; \
+ default: __bad_percpu_size(); \
+ } \
+})
+
+#define percpu_cmpxchg_op(var, old, new) \
+({ \
+ typeof(var) prev; \
+ switch (sizeof(var)) { \
+ case 1: \
+ asm("cmpxchgb %b1, "__percpu_seg"%2" \
+ : "=a"(prev) \
+ : "q"(new), "m"(var), "0"(old) \
+ : "memory"); \
+ break; \
+ case 2: \
+ asm("cmpxchgw %w1, "__percpu_seg"%2" \
+ : "=a"(prev) \
+ : "r"(new), "m"(var), "0"(old) \
+ : "memory"); \
+ break; \
+ case 4: \
+ asm("cmpxchgl %k1, "__percpu_seg"%2" \
+ : "=a"(prev) \
+ : "r"(new), "m"(var), "0"(old) \
+ : "memory"); \
+ break; \
+ case 8: \
+ asm("cmpxchgq %1, "__percpu_seg"%2" \
+ : "=a"(prev) \
+ : "r"(new), "m"(var), "0"(old) \
+ : "memory"); \
+ break; \
+ default: \
+ __bad_percpu_size(); \
+ } \
+ return prev; \
+})
+
+#define x86_read_percpu(var) percpu_from_op("mov", per_cpu_var(var))
+#define x86_write_percpu(var, val) percpu_to_op("mov", per_cpu_var(var), val)
+#define x86_add_percpu(var, val) percpu_to_op("add", per_cpu_var(var), val)
+#define x86_sub_percpu(var, val) percpu_to_op("sub", per_cpu_var(var), val)
+#define x86_inc_percpu(var) percpu_addr_op("inc", per_cpu_var(var))
+#define x86_dec_percpu(var) percpu_addr_op("dec", per_cpu_var(var))
+#define x86_or_percpu(var, val) percpu_to_op("or", per_cpu_var(var), val)
+#define x86_xchg_percpu(var, val) percpu_to_op("xchg", per_cpu_var(var), val)
+#define x86_cmpxchg_percpu(var, old, new) \
+ percpu_cmpxchg_op(per_cpu_var(var), old, new)
+
#endif /* !__ASSEMBLY__ */
#endif /* !CONFIG_X86_64 */
+
+#ifdef CONFIG_SMP
+
+/*
+ * Define the "EARLY_PER_CPU" macros. These are used for some per_cpu
+ * variables that are initialized and accessed before there are per_cpu
+ * areas allocated.
+ */
+
+#define DEFINE_EARLY_PER_CPU(_type, _name, _initvalue) \
+ DEFINE_PER_CPU(_type, _name) = _initvalue; \
+ __typeof__(_type) _name##_early_map[NR_CPUS] __initdata = \
+ { [0 ... NR_CPUS-1] = _initvalue }; \
+ __typeof__(_type) *_name##_early_ptr = _name##_early_map
+
+#define EXPORT_EARLY_PER_CPU_SYMBOL(_name) \
+ EXPORT_PER_CPU_SYMBOL(_name)
+
+#define DECLARE_EARLY_PER_CPU(_type, _name) \
+ DECLARE_PER_CPU(_type, _name); \
+ extern __typeof__(_type) *_name##_early_ptr; \
+ extern __typeof__(_type) _name##_early_map[]
+
+#define early_per_cpu_ptr(_name) (_name##_early_ptr)
+#define early_per_cpu_map(_name, _idx) (_name##_early_map[_idx])
+#define early_per_cpu(_name, _cpu) \
+ (early_per_cpu_ptr(_name) ? \
+ early_per_cpu_ptr(_name)[_cpu] : \
+ per_cpu(_name, _cpu))
+
+#else /* !CONFIG_SMP */
+#define DEFINE_EARLY_PER_CPU(_type, _name, _initvalue) \
+ DEFINE_PER_CPU(_type, _name) = _initvalue
+
+#define EXPORT_EARLY_PER_CPU_SYMBOL(_name) \
+ EXPORT_PER_CPU_SYMBOL(_name)
+
+#define DECLARE_EARLY_PER_CPU(_type, _name) \
+ DECLARE_PER_CPU(_type, _name)
+
+#define early_per_cpu(_name, _cpu) per_cpu(_name, _cpu)
+#define early_per_cpu_ptr(_name) NULL
+/* no early_per_cpu_map() */
+
+#endif /* !CONFIG_SMP */
+
#endif /* _ASM_X86_PERCPU_H_ */
diff --git a/include/asm-x86/pgtable.h b/include/asm-x86/pgtable.h
index 97c271b2910b..bcb5446a08d1 100644
--- a/include/asm-x86/pgtable.h
+++ b/include/asm-x86/pgtable.h
@@ -20,30 +20,25 @@
#define _PAGE_BIT_PAT_LARGE 12 /* On 2MB or 1GB pages */
#define _PAGE_BIT_NX 63 /* No execute: only valid after cpuid check */
-/*
- * Note: we use _AC(1, L) instead of _AC(1, UL) so that we get a
- * sign-extended value on 32-bit with all 1's in the upper word,
- * which preserves the upper pte values on 64-bit ptes:
- */
-#define _PAGE_PRESENT (_AC(1, L)<<_PAGE_BIT_PRESENT)
-#define _PAGE_RW (_AC(1, L)<<_PAGE_BIT_RW)
-#define _PAGE_USER (_AC(1, L)<<_PAGE_BIT_USER)
-#define _PAGE_PWT (_AC(1, L)<<_PAGE_BIT_PWT)
-#define _PAGE_PCD (_AC(1, L)<<_PAGE_BIT_PCD)
-#define _PAGE_ACCESSED (_AC(1, L)<<_PAGE_BIT_ACCESSED)
-#define _PAGE_DIRTY (_AC(1, L)<<_PAGE_BIT_DIRTY)
-#define _PAGE_PSE (_AC(1, L)<<_PAGE_BIT_PSE) /* 2MB page */
-#define _PAGE_GLOBAL (_AC(1, L)<<_PAGE_BIT_GLOBAL) /* Global TLB entry */
-#define _PAGE_UNUSED1 (_AC(1, L)<<_PAGE_BIT_UNUSED1)
-#define _PAGE_UNUSED2 (_AC(1, L)<<_PAGE_BIT_UNUSED2)
-#define _PAGE_UNUSED3 (_AC(1, L)<<_PAGE_BIT_UNUSED3)
-#define _PAGE_PAT (_AC(1, L)<<_PAGE_BIT_PAT)
-#define _PAGE_PAT_LARGE (_AC(1, L)<<_PAGE_BIT_PAT_LARGE)
+#define _PAGE_PRESENT (_AT(pteval_t, 1) << _PAGE_BIT_PRESENT)
+#define _PAGE_RW (_AT(pteval_t, 1) << _PAGE_BIT_RW)
+#define _PAGE_USER (_AT(pteval_t, 1) << _PAGE_BIT_USER)
+#define _PAGE_PWT (_AT(pteval_t, 1) << _PAGE_BIT_PWT)
+#define _PAGE_PCD (_AT(pteval_t, 1) << _PAGE_BIT_PCD)
+#define _PAGE_ACCESSED (_AT(pteval_t, 1) << _PAGE_BIT_ACCESSED)
+#define _PAGE_DIRTY (_AT(pteval_t, 1) << _PAGE_BIT_DIRTY)
+#define _PAGE_PSE (_AT(pteval_t, 1) << _PAGE_BIT_PSE)
+#define _PAGE_GLOBAL (_AT(pteval_t, 1) << _PAGE_BIT_GLOBAL)
+#define _PAGE_UNUSED1 (_AT(pteval_t, 1) << _PAGE_BIT_UNUSED1)
+#define _PAGE_UNUSED2 (_AT(pteval_t, 1) << _PAGE_BIT_UNUSED2)
+#define _PAGE_UNUSED3 (_AT(pteval_t, 1) << _PAGE_BIT_UNUSED3)
+#define _PAGE_PAT (_AT(pteval_t, 1) << _PAGE_BIT_PAT)
+#define _PAGE_PAT_LARGE (_AT(pteval_t, 1) << _PAGE_BIT_PAT_LARGE)
#if defined(CONFIG_X86_64) || defined(CONFIG_X86_PAE)
-#define _PAGE_NX (_AC(1, ULL) << _PAGE_BIT_NX)
+#define _PAGE_NX (_AT(pteval_t, 1) << _PAGE_BIT_NX)
#else
-#define _PAGE_NX 0
+#define _PAGE_NX (_AT(pteval_t, 0))
#endif
/* If _PAGE_PRESENT is clear, we use these: */
@@ -164,37 +159,37 @@ extern struct list_head pgd_list;
*/
static inline int pte_dirty(pte_t pte)
{
- return pte_val(pte) & _PAGE_DIRTY;
+ return pte_flags(pte) & _PAGE_DIRTY;
}
static inline int pte_young(pte_t pte)
{
- return pte_val(pte) & _PAGE_ACCESSED;
+ return pte_flags(pte) & _PAGE_ACCESSED;
}
static inline int pte_write(pte_t pte)
{
- return pte_val(pte) & _PAGE_RW;
+ return pte_flags(pte) & _PAGE_RW;
}
static inline int pte_file(pte_t pte)
{
- return pte_val(pte) & _PAGE_FILE;
+ return pte_flags(pte) & _PAGE_FILE;
}
static inline int pte_huge(pte_t pte)
{
- return pte_val(pte) & _PAGE_PSE;
+ return pte_flags(pte) & _PAGE_PSE;
}
static inline int pte_global(pte_t pte)
{
- return pte_val(pte) & _PAGE_GLOBAL;
+ return pte_flags(pte) & _PAGE_GLOBAL;
}
static inline int pte_exec(pte_t pte)
{
- return !(pte_val(pte) & _PAGE_NX);
+ return !(pte_flags(pte) & _PAGE_NX);
}
static inline int pte_special(pte_t pte)
@@ -210,22 +205,22 @@ static inline int pmd_large(pmd_t pte)
static inline pte_t pte_mkclean(pte_t pte)
{
- return __pte(pte_val(pte) & ~(pteval_t)_PAGE_DIRTY);
+ return __pte(pte_val(pte) & ~_PAGE_DIRTY);
}
static inline pte_t pte_mkold(pte_t pte)
{
- return __pte(pte_val(pte) & ~(pteval_t)_PAGE_ACCESSED);
+ return __pte(pte_val(pte) & ~_PAGE_ACCESSED);
}
static inline pte_t pte_wrprotect(pte_t pte)
{
- return __pte(pte_val(pte) & ~(pteval_t)_PAGE_RW);
+ return __pte(pte_val(pte) & ~_PAGE_RW);
}
static inline pte_t pte_mkexec(pte_t pte)
{
- return __pte(pte_val(pte) & ~(pteval_t)_PAGE_NX);
+ return __pte(pte_val(pte) & ~_PAGE_NX);
}
static inline pte_t pte_mkdirty(pte_t pte)
@@ -250,7 +245,7 @@ static inline pte_t pte_mkhuge(pte_t pte)
static inline pte_t pte_clrhuge(pte_t pte)
{
- return __pte(pte_val(pte) & ~(pteval_t)_PAGE_PSE);
+ return __pte(pte_val(pte) & ~_PAGE_PSE);
}
static inline pte_t pte_mkglobal(pte_t pte)
@@ -260,7 +255,7 @@ static inline pte_t pte_mkglobal(pte_t pte)
static inline pte_t pte_clrglobal(pte_t pte)
{
- return __pte(pte_val(pte) & ~(pteval_t)_PAGE_GLOBAL);
+ return __pte(pte_val(pte) & ~_PAGE_GLOBAL);
}
static inline pte_t pte_mkspecial(pte_t pte)
@@ -305,7 +300,7 @@ static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot)
return __pgprot(preservebits | addbits);
}
-#define pte_pgprot(x) __pgprot(pte_val(x) & ~PTE_MASK)
+#define pte_pgprot(x) __pgprot(pte_flags(x) & ~PTE_MASK)
#define canon_pgprot(p) __pgprot(pgprot_val(p) & __supported_pte_mask)
@@ -369,8 +364,15 @@ enum {
PG_LEVEL_4K,
PG_LEVEL_2M,
PG_LEVEL_1G,
+ PG_LEVEL_NUM
};
+#ifdef CONFIG_PROC_FS
+extern void update_page_count(int level, unsigned long pages);
+#else
+static inline void update_page_count(int level, unsigned long pages) { }
+#endif
+
/*
* Helper function that returns the kernel pagetable entry controlling
* the virtual address 'address'. NULL means no pagetable entry present.
diff --git a/include/asm-x86/processor-flags.h b/include/asm-x86/processor-flags.h
index 199cab107d85..092b39b3a7e6 100644
--- a/include/asm-x86/processor-flags.h
+++ b/include/asm-x86/processor-flags.h
@@ -88,4 +88,10 @@
#define CX86_ARR_BASE 0xc4
#define CX86_RCR_BASE 0xdc
+#ifdef CONFIG_VM86
+#define X86_VM_MASK X86_EFLAGS_VM
+#else
+#define X86_VM_MASK 0 /* No VM86 support */
+#endif
+
#endif /* __ASM_I386_PROCESSOR_FLAGS_H */
diff --git a/include/asm-x86/proto.h b/include/asm-x86/proto.h
index 6c8b41b03f6d..a9f51472521e 100644
--- a/include/asm-x86/proto.h
+++ b/include/asm-x86/proto.h
@@ -14,7 +14,7 @@ extern void ia32_syscall(void);
extern void ia32_cstar_target(void);
extern void ia32_sysenter_target(void);
-extern void reserve_bootmem_generic(unsigned long phys, unsigned len);
+extern int reserve_bootmem_generic(unsigned long phys, unsigned len, int flags);
extern void syscall32_cpu_init(void);
diff --git a/include/asm-x86/ptrace.h b/include/asm-x86/ptrace.h
index 9f922b0b95d6..8a71db803da6 100644
--- a/include/asm-x86/ptrace.h
+++ b/include/asm-x86/ptrace.h
@@ -3,7 +3,12 @@
#include <linux/compiler.h> /* For __user */
#include <asm/ptrace-abi.h>
+#include <asm/processor-flags.h>
+#ifdef __KERNEL__
+#include <asm/ds.h> /* the DS BTS struct is used for ptrace too */
+#include <asm/segment.h>
+#endif
#ifndef __ASSEMBLY__
@@ -55,9 +60,6 @@ struct pt_regs {
unsigned long ss;
};
-#include <asm/vm86.h>
-#include <asm/segment.h>
-
#endif /* __KERNEL__ */
#else /* __i386__ */
diff --git a/include/asm-x86/reboot.h b/include/asm-x86/reboot.h
index e63741f19392..206f355786dc 100644
--- a/include/asm-x86/reboot.h
+++ b/include/asm-x86/reboot.h
@@ -14,8 +14,8 @@ struct machine_ops {
extern struct machine_ops machine_ops;
-void machine_real_restart(unsigned char *code, int length);
void native_machine_crash_shutdown(struct pt_regs *regs);
void native_machine_shutdown(void);
+void machine_real_restart(const unsigned char *code, int length);
#endif /* _ASM_REBOOT_H */
diff --git a/include/asm-x86/required-features.h b/include/asm-x86/required-features.h
index 7400d3ad75c6..8c387198ca88 100644
--- a/include/asm-x86/required-features.h
+++ b/include/asm-x86/required-features.h
@@ -19,9 +19,13 @@
#if defined(CONFIG_X86_PAE) || defined(CONFIG_X86_64)
# define NEED_PAE (1<<(X86_FEATURE_PAE & 31))
-# define NEED_CX8 (1<<(X86_FEATURE_CX8 & 31))
#else
# define NEED_PAE 0
+#endif
+
+#ifdef CONFIG_X86_CMPXCHG64
+# define NEED_CX8 (1<<(X86_FEATURE_CX8 & 31))
+#else
# define NEED_CX8 0
#endif
diff --git a/include/asm-x86/resume-trace.h b/include/asm-x86/resume-trace.h
index 2557514d7ef6..8d9f0b41ee86 100644
--- a/include/asm-x86/resume-trace.h
+++ b/include/asm-x86/resume-trace.h
@@ -6,7 +6,7 @@
#define TRACE_RESUME(user) \
do { \
if (pm_trace_enabled) { \
- void *tracedata; \
+ const void *tracedata; \
asm volatile(_ASM_MOV_UL " $1f,%0\n" \
".section .tracedata,\"a\"\n" \
"1:\t.word %c1\n\t" \
diff --git a/include/asm-x86/seccomp_32.h b/include/asm-x86/seccomp_32.h
index 18da19e89bff..36e71c5f306f 100644
--- a/include/asm-x86/seccomp_32.h
+++ b/include/asm-x86/seccomp_32.h
@@ -1,4 +1,5 @@
#ifndef _ASM_SECCOMP_H
+#define _ASM_SECCOMP_H
#include <linux/thread_info.h>
diff --git a/include/asm-x86/setup.h b/include/asm-x86/setup.h
index fa6763af8d26..9e163fc3e984 100644
--- a/include/asm-x86/setup.h
+++ b/include/asm-x86/setup.h
@@ -50,19 +50,14 @@ extern struct boot_params boot_params;
*/
#define LOWMEMSIZE() (0x9f000)
-struct e820entry;
-
char * __init machine_specific_memory_setup(void);
char *memory_setup(void);
-int __init copy_e820_map(struct e820entry *biosmap, int nr_map);
-int __init sanitize_e820_map(struct e820entry *biosmap, char *pnr_map);
-void __init add_memory_region(unsigned long long start,
- unsigned long long size, int type);
-
-extern unsigned long init_pg_tables_end;
+void __init i386_start_kernel(void);
+extern unsigned long init_pg_tables_start;
+extern unsigned long init_pg_tables_end;
#endif /* __i386__ */
#endif /* _SETUP */
diff --git a/include/asm-x86/smp.h b/include/asm-x86/smp.h
index 1ebaa5cd3112..fc1007321ef6 100644
--- a/include/asm-x86/smp.h
+++ b/include/asm-x86/smp.h
@@ -29,21 +29,12 @@ extern int smp_num_siblings;
extern unsigned int num_processors;
extern cpumask_t cpu_initialized;
-#ifdef CONFIG_SMP
-extern u16 x86_cpu_to_apicid_init[];
-extern u16 x86_bios_cpu_apicid_init[];
-extern void *x86_cpu_to_apicid_early_ptr;
-extern void *x86_bios_cpu_apicid_early_ptr;
-#else
-#define x86_cpu_to_apicid_early_ptr NULL
-#define x86_bios_cpu_apicid_early_ptr NULL
-#endif
-
DECLARE_PER_CPU(cpumask_t, cpu_sibling_map);
DECLARE_PER_CPU(cpumask_t, cpu_core_map);
DECLARE_PER_CPU(u16, cpu_llc_id);
-DECLARE_PER_CPU(u16, x86_cpu_to_apicid);
-DECLARE_PER_CPU(u16, x86_bios_cpu_apicid);
+
+DECLARE_EARLY_PER_CPU(u16, x86_cpu_to_apicid);
+DECLARE_EARLY_PER_CPU(u16, x86_bios_cpu_apicid);
/* Static state in head.S used to set up a CPU */
extern struct {
@@ -201,7 +192,6 @@ extern void cpu_exit_clear(void);
extern void cpu_uninit(void);
#endif
-extern void smp_alloc_memory(void);
extern void lock_ipi_call_lock(void);
extern void unlock_ipi_call_lock(void);
#endif /* __ASSEMBLY__ */
diff --git a/include/asm-x86/srat.h b/include/asm-x86/srat.h
index f4bba131d068..456fe0b5a921 100644
--- a/include/asm-x86/srat.h
+++ b/include/asm-x86/srat.h
@@ -27,11 +27,13 @@
#ifndef _ASM_SRAT_H_
#define _ASM_SRAT_H_
-#ifndef CONFIG_ACPI_SRAT
-#error CONFIG_ACPI_SRAT not defined, and srat.h header has been included
-#endif
-
+#ifdef CONFIG_ACPI_SRAT
extern int get_memcfg_from_srat(void);
-extern unsigned long *get_zholes_size(int);
+#else
+static inline int get_memcfg_from_srat(void)
+{
+ return 0;
+}
+#endif
#endif /* _ASM_SRAT_H_ */
diff --git a/include/asm-x86/stackprotector.h b/include/asm-x86/stackprotector.h
new file mode 100644
index 000000000000..3baf7ad89be1
--- /dev/null
+++ b/include/asm-x86/stackprotector.h
@@ -0,0 +1,38 @@
+#ifndef _ASM_STACKPROTECTOR_H
+#define _ASM_STACKPROTECTOR_H 1
+
+#include <asm/tsc.h>
+
+/*
+ * Initialize the stackprotector canary value.
+ *
+ * NOTE: this must only be called from functions that never return,
+ * and it must always be inlined.
+ */
+static __always_inline void boot_init_stack_canary(void)
+{
+ u64 canary;
+ u64 tsc;
+
+ /*
+ * If we're the non-boot CPU, nothing set the PDA stack
+ * canary up for us - and if we are the boot CPU we have
+ * a 0 stack canary. This is a good place for updating
+ * it, as we wont ever return from this function (so the
+ * invalid canaries already on the stack wont ever
+ * trigger).
+ *
+ * We both use the random pool and the current TSC as a source
+ * of randomness. The TSC only matters for very early init,
+ * there it already has some randomness on most systems. Later
+ * on during the bootup the random pool has true entropy too.
+ */
+ get_random_bytes(&canary, sizeof(canary));
+ tsc = __native_read_tsc();
+ canary += tsc + (tsc << 32UL);
+
+ current->stack_canary = canary;
+ write_pda(stack_canary, canary);
+}
+
+#endif
diff --git a/include/asm-x86/string_32.h b/include/asm-x86/string_32.h
index b49369ad9a61..193578cd1fd9 100644
--- a/include/asm-x86/string_32.h
+++ b/include/asm-x86/string_32.h
@@ -29,81 +29,116 @@ extern char *strchr(const char *s, int c);
#define __HAVE_ARCH_STRLEN
extern size_t strlen(const char *s);
-static __always_inline void * __memcpy(void * to, const void * from, size_t n)
+static __always_inline void *__memcpy(void *to, const void *from, size_t n)
{
-int d0, d1, d2;
-__asm__ __volatile__(
- "rep ; movsl\n\t"
- "movl %4,%%ecx\n\t"
- "andl $3,%%ecx\n\t"
- "jz 1f\n\t"
- "rep ; movsb\n\t"
- "1:"
- : "=&c" (d0), "=&D" (d1), "=&S" (d2)
- : "0" (n/4), "g" (n), "1" ((long) to), "2" ((long) from)
- : "memory");
-return (to);
+ int d0, d1, d2;
+ asm volatile("rep ; movsl\n\t"
+ "movl %4,%%ecx\n\t"
+ "andl $3,%%ecx\n\t"
+ "jz 1f\n\t"
+ "rep ; movsb\n\t"
+ "1:"
+ : "=&c" (d0), "=&D" (d1), "=&S" (d2)
+ : "0" (n / 4), "g" (n), "1" ((long)to), "2" ((long)from)
+ : "memory");
+ return to;
}
/*
* This looks ugly, but the compiler can optimize it totally,
* as the count is constant.
*/
-static __always_inline void * __constant_memcpy(void * to, const void * from, size_t n)
+static __always_inline void *__constant_memcpy(void *to, const void *from,
+ size_t n)
{
long esi, edi;
- if (!n) return to;
-#if 1 /* want to do small copies with non-string ops? */
+ if (!n)
+ return to;
+
switch (n) {
- case 1: *(char*)to = *(char*)from; return to;
- case 2: *(short*)to = *(short*)from; return to;
- case 4: *(int*)to = *(int*)from; return to;
-#if 1 /* including those doable with two moves? */
- case 3: *(short*)to = *(short*)from;
- *((char*)to+2) = *((char*)from+2); return to;
- case 5: *(int*)to = *(int*)from;
- *((char*)to+4) = *((char*)from+4); return to;
- case 6: *(int*)to = *(int*)from;
- *((short*)to+2) = *((short*)from+2); return to;
- case 8: *(int*)to = *(int*)from;
- *((int*)to+1) = *((int*)from+1); return to;
-#endif
+ case 1:
+ *(char *)to = *(char *)from;
+ return to;
+ case 2:
+ *(short *)to = *(short *)from;
+ return to;
+ case 4:
+ *(int *)to = *(int *)from;
+ return to;
+
+ case 3:
+ *(short *)to = *(short *)from;
+ *((char *)to + 2) = *((char *)from + 2);
+ return to;
+ case 5:
+ *(int *)to = *(int *)from;
+ *((char *)to + 4) = *((char *)from + 4);
+ return to;
+ case 6:
+ *(int *)to = *(int *)from;
+ *((short *)to + 2) = *((short *)from + 2);
+ return to;
+ case 8:
+ *(int *)to = *(int *)from;
+ *((int *)to + 1) = *((int *)from + 1);
+ return to;
}
-#endif
- esi = (long) from;
- edi = (long) to;
- if (n >= 5*4) {
+
+ esi = (long)from;
+ edi = (long)to;
+ if (n >= 5 * 4) {
/* large block: use rep prefix */
int ecx;
- __asm__ __volatile__(
- "rep ; movsl"
- : "=&c" (ecx), "=&D" (edi), "=&S" (esi)
- : "0" (n/4), "1" (edi),"2" (esi)
- : "memory"
+ asm volatile("rep ; movsl"
+ : "=&c" (ecx), "=&D" (edi), "=&S" (esi)
+ : "0" (n / 4), "1" (edi), "2" (esi)
+ : "memory"
);
} else {
/* small block: don't clobber ecx + smaller code */
- if (n >= 4*4) __asm__ __volatile__("movsl"
- :"=&D"(edi),"=&S"(esi):"0"(edi),"1"(esi):"memory");
- if (n >= 3*4) __asm__ __volatile__("movsl"
- :"=&D"(edi),"=&S"(esi):"0"(edi),"1"(esi):"memory");
- if (n >= 2*4) __asm__ __volatile__("movsl"
- :"=&D"(edi),"=&S"(esi):"0"(edi),"1"(esi):"memory");
- if (n >= 1*4) __asm__ __volatile__("movsl"
- :"=&D"(edi),"=&S"(esi):"0"(edi),"1"(esi):"memory");
+ if (n >= 4 * 4)
+ asm volatile("movsl"
+ : "=&D"(edi), "=&S"(esi)
+ : "0"(edi), "1"(esi)
+ : "memory");
+ if (n >= 3 * 4)
+ asm volatile("movsl"
+ : "=&D"(edi), "=&S"(esi)
+ : "0"(edi), "1"(esi)
+ : "memory");
+ if (n >= 2 * 4)
+ asm volatile("movsl"
+ : "=&D"(edi), "=&S"(esi)
+ : "0"(edi), "1"(esi)
+ : "memory");
+ if (n >= 1 * 4)
+ asm volatile("movsl"
+ : "=&D"(edi), "=&S"(esi)
+ : "0"(edi), "1"(esi)
+ : "memory");
}
switch (n % 4) {
/* tail */
- case 0: return to;
- case 1: __asm__ __volatile__("movsb"
- :"=&D"(edi),"=&S"(esi):"0"(edi),"1"(esi):"memory");
- return to;
- case 2: __asm__ __volatile__("movsw"
- :"=&D"(edi),"=&S"(esi):"0"(edi),"1"(esi):"memory");
- return to;
- default: __asm__ __volatile__("movsw\n\tmovsb"
- :"=&D"(edi),"=&S"(esi):"0"(edi),"1"(esi):"memory");
- return to;
+ case 0:
+ return to;
+ case 1:
+ asm volatile("movsb"
+ : "=&D"(edi), "=&S"(esi)
+ : "0"(edi), "1"(esi)
+ : "memory");
+ return to;
+ case 2:
+ asm volatile("movsw"
+ : "=&D"(edi), "=&S"(esi)
+ : "0"(edi), "1"(esi)
+ : "memory");
+ return to;
+ default:
+ asm volatile("movsw\n\tmovsb"
+ : "=&D"(edi), "=&S"(esi)
+ : "0"(edi), "1"(esi)
+ : "memory");
+ return to;
}
}
@@ -117,87 +152,86 @@ static __always_inline void * __constant_memcpy(void * to, const void * from, si
* This CPU favours 3DNow strongly (eg AMD Athlon)
*/
-static inline void * __constant_memcpy3d(void * to, const void * from, size_t len)
+static inline void *__constant_memcpy3d(void *to, const void *from, size_t len)
{
if (len < 512)
return __constant_memcpy(to, from, len);
return _mmx_memcpy(to, from, len);
}
-static __inline__ void *__memcpy3d(void *to, const void *from, size_t len)
+static inline void *__memcpy3d(void *to, const void *from, size_t len)
{
if (len < 512)
return __memcpy(to, from, len);
return _mmx_memcpy(to, from, len);
}
-#define memcpy(t, f, n) \
-(__builtin_constant_p(n) ? \
- __constant_memcpy3d((t),(f),(n)) : \
- __memcpy3d((t),(f),(n)))
+#define memcpy(t, f, n) \
+ (__builtin_constant_p((n)) \
+ ? __constant_memcpy3d((t), (f), (n)) \
+ : __memcpy3d((t), (f), (n)))
#else
/*
* No 3D Now!
*/
-
-#define memcpy(t, f, n) \
-(__builtin_constant_p(n) ? \
- __constant_memcpy((t),(f),(n)) : \
- __memcpy((t),(f),(n)))
+
+#define memcpy(t, f, n) \
+ (__builtin_constant_p((n)) \
+ ? __constant_memcpy((t), (f), (n)) \
+ : __memcpy((t), (f), (n)))
#endif
#define __HAVE_ARCH_MEMMOVE
-void *memmove(void * dest,const void * src, size_t n);
+void *memmove(void *dest, const void *src, size_t n);
#define memcmp __builtin_memcmp
#define __HAVE_ARCH_MEMCHR
-extern void *memchr(const void * cs,int c,size_t count);
+extern void *memchr(const void *cs, int c, size_t count);
-static inline void * __memset_generic(void * s, char c,size_t count)
+static inline void *__memset_generic(void *s, char c, size_t count)
{
-int d0, d1;
-__asm__ __volatile__(
- "rep\n\t"
- "stosb"
- : "=&c" (d0), "=&D" (d1)
- :"a" (c),"1" (s),"0" (count)
- :"memory");
-return s;
+ int d0, d1;
+ asm volatile("rep\n\t"
+ "stosb"
+ : "=&c" (d0), "=&D" (d1)
+ : "a" (c), "1" (s), "0" (count)
+ : "memory");
+ return s;
}
/* we might want to write optimized versions of these later */
-#define __constant_count_memset(s,c,count) __memset_generic((s),(c),(count))
+#define __constant_count_memset(s, c, count) __memset_generic((s), (c), (count))
/*
- * memset(x,0,y) is a reasonably common thing to do, so we want to fill
+ * memset(x, 0, y) is a reasonably common thing to do, so we want to fill
* things 32 bits at a time even when we don't know the size of the
* area at compile-time..
*/
-static __always_inline void * __constant_c_memset(void * s, unsigned long c, size_t count)
+static __always_inline
+void *__constant_c_memset(void *s, unsigned long c, size_t count)
{
-int d0, d1;
-__asm__ __volatile__(
- "rep ; stosl\n\t"
- "testb $2,%b3\n\t"
- "je 1f\n\t"
- "stosw\n"
- "1:\ttestb $1,%b3\n\t"
- "je 2f\n\t"
- "stosb\n"
- "2:"
- :"=&c" (d0), "=&D" (d1)
- :"a" (c), "q" (count), "0" (count/4), "1" ((long) s)
- :"memory");
-return (s);
+ int d0, d1;
+ asm volatile("rep ; stosl\n\t"
+ "testb $2,%b3\n\t"
+ "je 1f\n\t"
+ "stosw\n"
+ "1:\ttestb $1,%b3\n\t"
+ "je 2f\n\t"
+ "stosb\n"
+ "2:"
+ : "=&c" (d0), "=&D" (d1)
+ : "a" (c), "q" (count), "0" (count/4), "1" ((long)s)
+ : "memory");
+ return s;
}
/* Added by Gertjan van Wingerde to make minix and sysv module work */
#define __HAVE_ARCH_STRNLEN
-extern size_t strnlen(const char * s, size_t count);
+extern size_t strnlen(const char *s, size_t count);
/* end of additional stuff */
#define __HAVE_ARCH_STRSTR
@@ -207,66 +241,85 @@ extern char *strstr(const char *cs, const char *ct);
* This looks horribly ugly, but the compiler can optimize it totally,
* as we by now know that both pattern and count is constant..
*/
-static __always_inline void * __constant_c_and_count_memset(void * s, unsigned long pattern, size_t count)
+static __always_inline
+void *__constant_c_and_count_memset(void *s, unsigned long pattern,
+ size_t count)
{
switch (count) {
+ case 0:
+ return s;
+ case 1:
+ *(unsigned char *)s = pattern & 0xff;
+ return s;
+ case 2:
+ *(unsigned short *)s = pattern & 0xffff;
+ return s;
+ case 3:
+ *(unsigned short *)s = pattern & 0xffff;
+ *((unsigned char *)s + 2) = pattern & 0xff;
+ return s;
+ case 4:
+ *(unsigned long *)s = pattern;
+ return s;
+ }
+
+#define COMMON(x) \
+ asm volatile("rep ; stosl" \
+ x \
+ : "=&c" (d0), "=&D" (d1) \
+ : "a" (eax), "0" (count/4), "1" ((long)s) \
+ : "memory")
+
+ {
+ int d0, d1;
+#if __GNUC__ == 4 && __GNUC_MINOR__ == 0
+ /* Workaround for broken gcc 4.0 */
+ register unsigned long eax asm("%eax") = pattern;
+#else
+ unsigned long eax = pattern;
+#endif
+
+ switch (count % 4) {
case 0:
+ COMMON("");
return s;
case 1:
- *(unsigned char *)s = pattern & 0xff;
+ COMMON("\n\tstosb");
return s;
case 2:
- *(unsigned short *)s = pattern & 0xffff;
+ COMMON("\n\tstosw");
return s;
- case 3:
- *(unsigned short *)s = pattern & 0xffff;
- *(2+(unsigned char *)s) = pattern & 0xff;
- return s;
- case 4:
- *(unsigned long *)s = pattern;
+ default:
+ COMMON("\n\tstosw\n\tstosb");
return s;
+ }
}
-#define COMMON(x) \
-__asm__ __volatile__( \
- "rep ; stosl" \
- x \
- : "=&c" (d0), "=&D" (d1) \
- : "a" (pattern),"0" (count/4),"1" ((long) s) \
- : "memory")
-{
- int d0, d1;
- switch (count % 4) {
- case 0: COMMON(""); return s;
- case 1: COMMON("\n\tstosb"); return s;
- case 2: COMMON("\n\tstosw"); return s;
- default: COMMON("\n\tstosw\n\tstosb"); return s;
- }
-}
-
+
#undef COMMON
}
-#define __constant_c_x_memset(s, c, count) \
-(__builtin_constant_p(count) ? \
- __constant_c_and_count_memset((s),(c),(count)) : \
- __constant_c_memset((s),(c),(count)))
+#define __constant_c_x_memset(s, c, count) \
+ (__builtin_constant_p(count) \
+ ? __constant_c_and_count_memset((s), (c), (count)) \
+ : __constant_c_memset((s), (c), (count)))
-#define __memset(s, c, count) \
-(__builtin_constant_p(count) ? \
- __constant_count_memset((s),(c),(count)) : \
- __memset_generic((s),(c),(count)))
+#define __memset(s, c, count) \
+ (__builtin_constant_p(count) \
+ ? __constant_count_memset((s), (c), (count)) \
+ : __memset_generic((s), (c), (count)))
#define __HAVE_ARCH_MEMSET
-#define memset(s, c, count) \
-(__builtin_constant_p(c) ? \
- __constant_c_x_memset((s),(0x01010101UL*(unsigned char)(c)),(count)) : \
- __memset((s),(c),(count)))
+#define memset(s, c, count) \
+ (__builtin_constant_p(c) \
+ ? __constant_c_x_memset((s), (0x01010101UL * (unsigned char)(c)), \
+ (count)) \
+ : __memset((s), (c), (count)))
/*
* find the first occurrence of byte 'c', or 1 past the area if none
*/
#define __HAVE_ARCH_MEMSCAN
-extern void *memscan(void * addr, int c, size_t size);
+extern void *memscan(void *addr, int c, size_t size);
#endif /* __KERNEL__ */
diff --git a/include/asm-x86/system.h b/include/asm-x86/system.h
index a2f04cd79b29..91f3ab98dbaa 100644
--- a/include/asm-x86/system.h
+++ b/include/asm-x86/system.h
@@ -92,6 +92,8 @@ do { \
".globl thread_return\n" \
"thread_return:\n\t" \
"movq %%gs:%P[pda_pcurrent],%%rsi\n\t" \
+ "movq %P[task_canary](%%rsi),%%r8\n\t" \
+ "movq %%r8,%%gs:%P[pda_canary]\n\t" \
"movq %P[thread_info](%%rsi),%%r8\n\t" \
LOCK_PREFIX "btr %[tif_fork],%P[ti_flags](%%r8)\n\t" \
"movq %%rax,%%rdi\n\t" \
@@ -103,7 +105,9 @@ do { \
[ti_flags] "i" (offsetof(struct thread_info, flags)), \
[tif_fork] "i" (TIF_FORK), \
[thread_info] "i" (offsetof(struct task_struct, stack)), \
- [pda_pcurrent] "i" (offsetof(struct x8664_pda, pcurrent)) \
+ [task_canary] "i" (offsetof(struct task_struct, stack_canary)),\
+ [pda_pcurrent] "i" (offsetof(struct x8664_pda, pcurrent)), \
+ [pda_canary] "i" (offsetof(struct x8664_pda, stack_canary))\
: "memory", "cc" __EXTRA_CLOBBER)
#endif
@@ -289,7 +293,7 @@ static inline void native_wbinvd(void)
#endif/* CONFIG_PARAVIRT */
-#define stts() write_cr0(8 | read_cr0())
+#define stts() write_cr0(read_cr0() | X86_CR0_TS)
#endif /* __KERNEL__ */
@@ -303,7 +307,6 @@ static inline void clflush(volatile void *__p)
void disable_hlt(void);
void enable_hlt(void);
-extern int es7000_plat;
void cpu_idle_wait(void);
extern unsigned long arch_align_stack(unsigned long sp);
diff --git a/include/asm-x86/thread_info.h b/include/asm-x86/thread_info.h
index 77244f17993f..895339d2bc0b 100644
--- a/include/asm-x86/thread_info.h
+++ b/include/asm-x86/thread_info.h
@@ -1,9 +1,253 @@
+/* thread_info.h: low-level thread information
+ *
+ * Copyright (C) 2002 David Howells (dhowells@redhat.com)
+ * - Incorporating suggestions made by Linus Torvalds and Dave Miller
+ */
+
#ifndef _ASM_X86_THREAD_INFO_H
+#define _ASM_X86_THREAD_INFO_H
+
+#include <linux/compiler.h>
+#include <asm/page.h>
+#include <asm/types.h>
+
+/*
+ * low level task data that entry.S needs immediate access to
+ * - this struct should fit entirely inside of one cache line
+ * - this struct shares the supervisor stack pages
+ */
+#ifndef __ASSEMBLY__
+struct task_struct;
+struct exec_domain;
+#include <asm/processor.h>
+
+struct thread_info {
+ struct task_struct *task; /* main task structure */
+ struct exec_domain *exec_domain; /* execution domain */
+ unsigned long flags; /* low level flags */
+ __u32 status; /* thread synchronous flags */
+ __u32 cpu; /* current CPU */
+ int preempt_count; /* 0 => preemptable,
+ <0 => BUG */
+ mm_segment_t addr_limit;
+ struct restart_block restart_block;
+ void __user *sysenter_return;
+#ifdef CONFIG_X86_32
+ unsigned long previous_esp; /* ESP of the previous stack in
+ case of nested (IRQ) stacks
+ */
+ __u8 supervisor_stack[0];
+#endif
+};
+
+#define INIT_THREAD_INFO(tsk) \
+{ \
+ .task = &tsk, \
+ .exec_domain = &default_exec_domain, \
+ .flags = 0, \
+ .cpu = 0, \
+ .preempt_count = 1, \
+ .addr_limit = KERNEL_DS, \
+ .restart_block = { \
+ .fn = do_no_restart_syscall, \
+ }, \
+}
+
+#define init_thread_info (init_thread_union.thread_info)
+#define init_stack (init_thread_union.stack)
+
+#else /* !__ASSEMBLY__ */
+
+#include <asm/asm-offsets.h>
+
+#endif
+
+/*
+ * thread information flags
+ * - these are process state flags that various assembly files
+ * may need to access
+ * - pending work-to-be-done flags are in LSW
+ * - other flags in MSW
+ * Warning: layout of LSW is hardcoded in entry.S
+ */
+#define TIF_SYSCALL_TRACE 0 /* syscall trace active */
+#define TIF_SIGPENDING 2 /* signal pending */
+#define TIF_NEED_RESCHED 3 /* rescheduling necessary */
+#define TIF_SINGLESTEP 4 /* reenable singlestep on user return*/
+#define TIF_IRET 5 /* force IRET */
#ifdef CONFIG_X86_32
-# include "thread_info_32.h"
+#define TIF_SYSCALL_EMU 6 /* syscall emulation active */
+#endif
+#define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */
+#define TIF_SECCOMP 8 /* secure computing */
+#define TIF_MCE_NOTIFY 10 /* notify userspace of an MCE */
+#define TIF_HRTICK_RESCHED 11 /* reprogram hrtick timer */
+#define TIF_NOTSC 16 /* TSC is not accessible in userland */
+#define TIF_IA32 17 /* 32bit process */
+#define TIF_FORK 18 /* ret_from_fork */
+#define TIF_ABI_PENDING 19
+#define TIF_MEMDIE 20
+#define TIF_DEBUG 21 /* uses debug registers */
+#define TIF_IO_BITMAP 22 /* uses I/O bitmap */
+#define TIF_FREEZE 23 /* is freezing for suspend */
+#define TIF_FORCED_TF 24 /* true if TF in eflags artificially */
+#define TIF_DEBUGCTLMSR 25 /* uses thread_struct.debugctlmsr */
+#define TIF_DS_AREA_MSR 26 /* uses thread_struct.ds_area_msr */
+#define TIF_BTS_TRACE_TS 27 /* record scheduling event timestamps */
+
+#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
+#define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
+#define _TIF_SINGLESTEP (1 << TIF_SINGLESTEP)
+#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED)
+#define _TIF_IRET (1 << TIF_IRET)
+#ifdef CONFIG_X86_32
+#define _TIF_SYSCALL_EMU (1 << TIF_SYSCALL_EMU)
#else
-# include "thread_info_64.h"
+#define _TIF_SYSCALL_EMU 0
#endif
+#define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT)
+#define _TIF_SECCOMP (1 << TIF_SECCOMP)
+#define _TIF_MCE_NOTIFY (1 << TIF_MCE_NOTIFY)
+#define _TIF_HRTICK_RESCHED (1 << TIF_HRTICK_RESCHED)
+#define _TIF_NOTSC (1 << TIF_NOTSC)
+#define _TIF_IA32 (1 << TIF_IA32)
+#define _TIF_FORK (1 << TIF_FORK)
+#define _TIF_ABI_PENDING (1 << TIF_ABI_PENDING)
+#define _TIF_DEBUG (1 << TIF_DEBUG)
+#define _TIF_IO_BITMAP (1 << TIF_IO_BITMAP)
+#define _TIF_FREEZE (1 << TIF_FREEZE)
+#define _TIF_FORCED_TF (1 << TIF_FORCED_TF)
+#define _TIF_DEBUGCTLMSR (1 << TIF_DEBUGCTLMSR)
+#define _TIF_DS_AREA_MSR (1 << TIF_DS_AREA_MSR)
+#define _TIF_BTS_TRACE_TS (1 << TIF_BTS_TRACE_TS)
+
+/* work to do on interrupt/exception return */
+#define _TIF_WORK_MASK \
+ (0x0000FFFF & \
+ ~(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SINGLESTEP| \
+ _TIF_SECCOMP|_TIF_SYSCALL_EMU))
+
+/* work to do on any return to user space */
+#define _TIF_ALLWORK_MASK (0x0000FFFF & ~_TIF_SECCOMP)
+
+/* Only used for 64 bit */
+#define _TIF_DO_NOTIFY_MASK \
+ (_TIF_SIGPENDING|_TIF_SINGLESTEP|_TIF_MCE_NOTIFY|_TIF_HRTICK_RESCHED)
+
+/* flags to check in __switch_to() */
+#define _TIF_WORK_CTXSW \
+ (_TIF_IO_BITMAP|_TIF_DEBUGCTLMSR|_TIF_DS_AREA_MSR|_TIF_BTS_TRACE_TS| \
+ _TIF_NOTSC)
+
+#define _TIF_WORK_CTXSW_PREV _TIF_WORK_CTXSW
+#define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW|_TIF_DEBUG)
+
+#define PREEMPT_ACTIVE 0x10000000
+
+/* thread information allocation */
+#ifdef CONFIG_DEBUG_STACK_USAGE
+#define THREAD_FLAGS (GFP_KERNEL | __GFP_ZERO)
+#else
+#define THREAD_FLAGS GFP_KERNEL
+#endif
+
+#define alloc_thread_info(tsk) \
+ ((struct thread_info *)__get_free_pages(THREAD_FLAGS, THREAD_ORDER))
+
+#ifdef CONFIG_X86_32
+
+#define STACK_WARN (THREAD_SIZE/8)
+/*
+ * macros/functions for gaining access to the thread information structure
+ *
+ * preempt_count needs to be 1 initially, until the scheduler is functional.
+ */
+#ifndef __ASSEMBLY__
+
+
+/* how to get the current stack pointer from C */
+register unsigned long current_stack_pointer asm("esp") __used;
+
+/* how to get the thread information struct from C */
+static inline struct thread_info *current_thread_info(void)
+{
+ return (struct thread_info *)
+ (current_stack_pointer & ~(THREAD_SIZE - 1));
+}
+
+#else /* !__ASSEMBLY__ */
+
+/* how to get the thread information struct from ASM */
+#define GET_THREAD_INFO(reg) \
+ movl $-THREAD_SIZE, reg; \
+ andl %esp, reg
+
+/* use this one if reg already contains %esp */
+#define GET_THREAD_INFO_WITH_ESP(reg) \
+ andl $-THREAD_SIZE, reg
+
+#endif
+
+#else /* X86_32 */
+
+#include <asm/pda.h>
+
+/*
+ * macros/functions for gaining access to the thread information structure
+ * preempt_count needs to be 1 initially, until the scheduler is functional.
+ */
+#ifndef __ASSEMBLY__
+static inline struct thread_info *current_thread_info(void)
+{
+ struct thread_info *ti;
+ ti = (void *)(read_pda(kernelstack) + PDA_STACKOFFSET - THREAD_SIZE);
+ return ti;
+}
+
+/* do not use in interrupt context */
+static inline struct thread_info *stack_thread_info(void)
+{
+ struct thread_info *ti;
+ asm("andq %%rsp,%0; " : "=r" (ti) : "0" (~(THREAD_SIZE - 1)));
+ return ti;
+}
+
+#else /* !__ASSEMBLY__ */
+
+/* how to get the thread information struct from ASM */
+#define GET_THREAD_INFO(reg) \
+ movq %gs:pda_kernelstack,reg ; \
+ subq $(THREAD_SIZE-PDA_STACKOFFSET),reg
+
+#endif
+
+#endif /* !X86_32 */
+
+/*
+ * Thread-synchronous status.
+ *
+ * This is different from the flags in that nobody else
+ * ever touches our thread-synchronous status, so we don't
+ * have to worry about atomic accesses.
+ */
+#define TS_USEDFPU 0x0001 /* FPU was used by this task
+ this quantum (SMP) */
+#define TS_COMPAT 0x0002 /* 32bit syscall active (64BIT)*/
+#define TS_POLLING 0x0004 /* true if in idle loop
+ and not sleeping */
+#define TS_RESTORE_SIGMASK 0x0008 /* restore signal mask in do_signal() */
+
+#define tsk_is_polling(t) (task_thread_info(t)->status & TS_POLLING)
+
+#ifndef __ASSEMBLY__
+#define HAVE_SET_RESTORE_SIGMASK 1
+static inline void set_restore_sigmask(void)
+{
+ struct thread_info *ti = current_thread_info();
+ ti->status |= TS_RESTORE_SIGMASK;
+ set_bit(TIF_SIGPENDING, (unsigned long *)&ti->flags);
+}
+#endif /* !__ASSEMBLY__ */
#ifndef __ASSEMBLY__
extern void arch_task_cache_init(void);
diff --git a/include/asm-x86/thread_info_32.h b/include/asm-x86/thread_info_32.h
deleted file mode 100644
index b6338829d1a8..000000000000
--- a/include/asm-x86/thread_info_32.h
+++ /dev/null
@@ -1,205 +0,0 @@
-/* thread_info.h: i386 low-level thread information
- *
- * Copyright (C) 2002 David Howells (dhowells@redhat.com)
- * - Incorporating suggestions made by Linus Torvalds and Dave Miller
- */
-
-#ifndef _ASM_THREAD_INFO_H
-#define _ASM_THREAD_INFO_H
-
-#ifdef __KERNEL__
-
-#include <linux/compiler.h>
-#include <asm/page.h>
-
-#ifndef __ASSEMBLY__
-#include <asm/processor.h>
-#endif
-
-/*
- * low level task data that entry.S needs immediate access to
- * - this struct should fit entirely inside of one cache line
- * - this struct shares the supervisor stack pages
- * - if the contents of this structure are changed,
- * the assembly constants must also be changed
- */
-#ifndef __ASSEMBLY__
-
-struct thread_info {
- struct task_struct *task; /* main task structure */
- struct exec_domain *exec_domain; /* execution domain */
- unsigned long flags; /* low level flags */
- unsigned long status; /* thread-synchronous flags */
- __u32 cpu; /* current CPU */
- int preempt_count; /* 0 => preemptable,
- <0 => BUG */
- mm_segment_t addr_limit; /* thread address space:
- 0-0xBFFFFFFF user-thread
- 0-0xFFFFFFFF kernel-thread
- */
- void *sysenter_return;
- struct restart_block restart_block;
- unsigned long previous_esp; /* ESP of the previous stack in
- case of nested (IRQ) stacks
- */
- __u8 supervisor_stack[0];
-};
-
-#else /* !__ASSEMBLY__ */
-
-#include <asm/asm-offsets.h>
-
-#endif
-
-#define PREEMPT_ACTIVE 0x10000000
-#ifdef CONFIG_4KSTACKS
-#define THREAD_SIZE (4096)
-#else
-#define THREAD_SIZE (8192)
-#endif
-
-#define STACK_WARN (THREAD_SIZE/8)
-/*
- * macros/functions for gaining access to the thread information structure
- *
- * preempt_count needs to be 1 initially, until the scheduler is functional.
- */
-#ifndef __ASSEMBLY__
-
-#define INIT_THREAD_INFO(tsk) \
-{ \
- .task = &tsk, \
- .exec_domain = &default_exec_domain, \
- .flags = 0, \
- .cpu = 0, \
- .preempt_count = 1, \
- .addr_limit = KERNEL_DS, \
- .restart_block = { \
- .fn = do_no_restart_syscall, \
- }, \
-}
-
-#define init_thread_info (init_thread_union.thread_info)
-#define init_stack (init_thread_union.stack)
-
-
-/* how to get the current stack pointer from C */
-register unsigned long current_stack_pointer asm("esp") __used;
-
-/* how to get the thread information struct from C */
-static inline struct thread_info *current_thread_info(void)
-{
- return (struct thread_info *)
- (current_stack_pointer & ~(THREAD_SIZE - 1));
-}
-
-/* thread information allocation */
-#ifdef CONFIG_DEBUG_STACK_USAGE
-#define alloc_thread_info(tsk) ((struct thread_info *) \
- __get_free_pages(GFP_KERNEL | __GFP_ZERO, get_order(THREAD_SIZE)))
-#else
-#define alloc_thread_info(tsk) ((struct thread_info *) \
- __get_free_pages(GFP_KERNEL, get_order(THREAD_SIZE)))
-#endif
-
-#else /* !__ASSEMBLY__ */
-
-/* how to get the thread information struct from ASM */
-#define GET_THREAD_INFO(reg) \
- movl $-THREAD_SIZE, reg; \
- andl %esp, reg
-
-/* use this one if reg already contains %esp */
-#define GET_THREAD_INFO_WITH_ESP(reg) \
- andl $-THREAD_SIZE, reg
-
-#endif
-
-/*
- * thread information flags
- * - these are process state flags that various
- * assembly files may need to access
- * - pending work-to-be-done flags are in LSW
- * - other flags in MSW
- */
-#define TIF_SYSCALL_TRACE 0 /* syscall trace active */
-#define TIF_SIGPENDING 1 /* signal pending */
-#define TIF_NEED_RESCHED 2 /* rescheduling necessary */
-#define TIF_SINGLESTEP 3 /* restore singlestep on return to
- user mode */
-#define TIF_IRET 4 /* return with iret */
-#define TIF_SYSCALL_EMU 5 /* syscall emulation active */
-#define TIF_SYSCALL_AUDIT 6 /* syscall auditing active */
-#define TIF_SECCOMP 7 /* secure computing */
-#define TIF_HRTICK_RESCHED 9 /* reprogram hrtick timer */
-#define TIF_MEMDIE 16
-#define TIF_DEBUG 17 /* uses debug registers */
-#define TIF_IO_BITMAP 18 /* uses I/O bitmap */
-#define TIF_FREEZE 19 /* is freezing for suspend */
-#define TIF_NOTSC 20 /* TSC is not accessible in userland */
-#define TIF_FORCED_TF 21 /* true if TF in eflags artificially */
-#define TIF_DEBUGCTLMSR 22 /* uses thread_struct.debugctlmsr */
-#define TIF_DS_AREA_MSR 23 /* uses thread_struct.ds_area_msr */
-#define TIF_BTS_TRACE_TS 24 /* record scheduling event timestamps */
-
-#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
-#define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
-#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED)
-#define _TIF_SINGLESTEP (1 << TIF_SINGLESTEP)
-#define _TIF_IRET (1 << TIF_IRET)
-#define _TIF_SYSCALL_EMU (1 << TIF_SYSCALL_EMU)
-#define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT)
-#define _TIF_SECCOMP (1 << TIF_SECCOMP)
-#define _TIF_HRTICK_RESCHED (1 << TIF_HRTICK_RESCHED)
-#define _TIF_DEBUG (1 << TIF_DEBUG)
-#define _TIF_IO_BITMAP (1 << TIF_IO_BITMAP)
-#define _TIF_FREEZE (1 << TIF_FREEZE)
-#define _TIF_NOTSC (1 << TIF_NOTSC)
-#define _TIF_FORCED_TF (1 << TIF_FORCED_TF)
-#define _TIF_DEBUGCTLMSR (1 << TIF_DEBUGCTLMSR)
-#define _TIF_DS_AREA_MSR (1 << TIF_DS_AREA_MSR)
-#define _TIF_BTS_TRACE_TS (1 << TIF_BTS_TRACE_TS)
-
-/* work to do on interrupt/exception return */
-#define _TIF_WORK_MASK \
- (0x0000FFFF & ~(_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \
- _TIF_SECCOMP | _TIF_SYSCALL_EMU))
-/* work to do on any return to u-space */
-#define _TIF_ALLWORK_MASK (0x0000FFFF & ~_TIF_SECCOMP)
-
-/* flags to check in __switch_to() */
-#define _TIF_WORK_CTXSW \
- (_TIF_IO_BITMAP | _TIF_NOTSC | _TIF_DEBUGCTLMSR | \
- _TIF_DS_AREA_MSR | _TIF_BTS_TRACE_TS)
-#define _TIF_WORK_CTXSW_PREV _TIF_WORK_CTXSW
-#define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW | _TIF_DEBUG)
-
-
-/*
- * Thread-synchronous status.
- *
- * This is different from the flags in that nobody else
- * ever touches our thread-synchronous status, so we don't
- * have to worry about atomic accesses.
- */
-#define TS_USEDFPU 0x0001 /* FPU was used by this task
- this quantum (SMP) */
-#define TS_POLLING 0x0002 /* True if in idle loop
- and not sleeping */
-#define TS_RESTORE_SIGMASK 0x0004 /* restore signal mask in do_signal() */
-
-#define tsk_is_polling(t) (task_thread_info(t)->status & TS_POLLING)
-
-#ifndef __ASSEMBLY__
-#define HAVE_SET_RESTORE_SIGMASK 1
-static inline void set_restore_sigmask(void)
-{
- struct thread_info *ti = current_thread_info();
- ti->status |= TS_RESTORE_SIGMASK;
- set_bit(TIF_SIGPENDING, &ti->flags);
-}
-#endif /* !__ASSEMBLY__ */
-
-#endif /* __KERNEL__ */
-
-#endif /* _ASM_THREAD_INFO_H */
diff --git a/include/asm-x86/thread_info_64.h b/include/asm-x86/thread_info_64.h
deleted file mode 100644
index cb69f70abba1..000000000000
--- a/include/asm-x86/thread_info_64.h
+++ /dev/null
@@ -1,195 +0,0 @@
-/* thread_info.h: x86_64 low-level thread information
- *
- * Copyright (C) 2002 David Howells (dhowells@redhat.com)
- * - Incorporating suggestions made by Linus Torvalds and Dave Miller
- */
-
-#ifndef _ASM_THREAD_INFO_H
-#define _ASM_THREAD_INFO_H
-
-#ifdef __KERNEL__
-
-#include <asm/page.h>
-#include <asm/types.h>
-#include <asm/pda.h>
-
-/*
- * low level task data that entry.S needs immediate access to
- * - this struct should fit entirely inside of one cache line
- * - this struct shares the supervisor stack pages
- */
-#ifndef __ASSEMBLY__
-struct task_struct;
-struct exec_domain;
-#include <asm/processor.h>
-
-struct thread_info {
- struct task_struct *task; /* main task structure */
- struct exec_domain *exec_domain; /* execution domain */
- __u32 flags; /* low level flags */
- __u32 status; /* thread synchronous flags */
- __u32 cpu; /* current CPU */
- int preempt_count; /* 0 => preemptable,
- <0 => BUG */
- mm_segment_t addr_limit;
- struct restart_block restart_block;
-#ifdef CONFIG_IA32_EMULATION
- void __user *sysenter_return;
-#endif
-};
-#endif
-
-/*
- * macros/functions for gaining access to the thread information structure
- * preempt_count needs to be 1 initially, until the scheduler is functional.
- */
-#ifndef __ASSEMBLY__
-#define INIT_THREAD_INFO(tsk) \
-{ \
- .task = &tsk, \
- .exec_domain = &default_exec_domain, \
- .flags = 0, \
- .cpu = 0, \
- .preempt_count = 1, \
- .addr_limit = KERNEL_DS, \
- .restart_block = { \
- .fn = do_no_restart_syscall, \
- }, \
-}
-
-#define init_thread_info (init_thread_union.thread_info)
-#define init_stack (init_thread_union.stack)
-
-static inline struct thread_info *current_thread_info(void)
-{
- struct thread_info *ti;
- ti = (void *)(read_pda(kernelstack) + PDA_STACKOFFSET - THREAD_SIZE);
- return ti;
-}
-
-/* do not use in interrupt context */
-static inline struct thread_info *stack_thread_info(void)
-{
- struct thread_info *ti;
- asm("andq %%rsp,%0; " : "=r" (ti) : "0" (~(THREAD_SIZE - 1)));
- return ti;
-}
-
-/* thread information allocation */
-#ifdef CONFIG_DEBUG_STACK_USAGE
-#define THREAD_FLAGS (GFP_KERNEL | __GFP_ZERO)
-#else
-#define THREAD_FLAGS GFP_KERNEL
-#endif
-
-#define alloc_thread_info(tsk) \
- ((struct thread_info *)__get_free_pages(THREAD_FLAGS, THREAD_ORDER))
-
-#else /* !__ASSEMBLY__ */
-
-/* how to get the thread information struct from ASM */
-#define GET_THREAD_INFO(reg) \
- movq %gs:pda_kernelstack,reg ; \
- subq $(THREAD_SIZE-PDA_STACKOFFSET),reg
-
-#endif
-
-/*
- * thread information flags
- * - these are process state flags that various assembly files
- * may need to access
- * - pending work-to-be-done flags are in LSW
- * - other flags in MSW
- * Warning: layout of LSW is hardcoded in entry.S
- */
-#define TIF_SYSCALL_TRACE 0 /* syscall trace active */
-#define TIF_SIGPENDING 2 /* signal pending */
-#define TIF_NEED_RESCHED 3 /* rescheduling necessary */
-#define TIF_SINGLESTEP 4 /* reenable singlestep on user return*/
-#define TIF_IRET 5 /* force IRET */
-#define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */
-#define TIF_SECCOMP 8 /* secure computing */
-#define TIF_MCE_NOTIFY 10 /* notify userspace of an MCE */
-#define TIF_HRTICK_RESCHED 11 /* reprogram hrtick timer */
-/* 16 free */
-#define TIF_IA32 17 /* 32bit process */
-#define TIF_FORK 18 /* ret_from_fork */
-#define TIF_ABI_PENDING 19
-#define TIF_MEMDIE 20
-#define TIF_DEBUG 21 /* uses debug registers */
-#define TIF_IO_BITMAP 22 /* uses I/O bitmap */
-#define TIF_FREEZE 23 /* is freezing for suspend */
-#define TIF_FORCED_TF 24 /* true if TF in eflags artificially */
-#define TIF_DEBUGCTLMSR 25 /* uses thread_struct.debugctlmsr */
-#define TIF_DS_AREA_MSR 26 /* uses thread_struct.ds_area_msr */
-#define TIF_BTS_TRACE_TS 27 /* record scheduling event timestamps */
-#define TIF_NOTSC 28 /* TSC is not accessible in userland */
-
-#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
-#define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
-#define _TIF_SINGLESTEP (1 << TIF_SINGLESTEP)
-#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED)
-#define _TIF_IRET (1 << TIF_IRET)
-#define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT)
-#define _TIF_SECCOMP (1 << TIF_SECCOMP)
-#define _TIF_MCE_NOTIFY (1 << TIF_MCE_NOTIFY)
-#define _TIF_HRTICK_RESCHED (1 << TIF_HRTICK_RESCHED)
-#define _TIF_IA32 (1 << TIF_IA32)
-#define _TIF_FORK (1 << TIF_FORK)
-#define _TIF_ABI_PENDING (1 << TIF_ABI_PENDING)
-#define _TIF_DEBUG (1 << TIF_DEBUG)
-#define _TIF_IO_BITMAP (1 << TIF_IO_BITMAP)
-#define _TIF_FREEZE (1 << TIF_FREEZE)
-#define _TIF_FORCED_TF (1 << TIF_FORCED_TF)
-#define _TIF_DEBUGCTLMSR (1 << TIF_DEBUGCTLMSR)
-#define _TIF_DS_AREA_MSR (1 << TIF_DS_AREA_MSR)
-#define _TIF_BTS_TRACE_TS (1 << TIF_BTS_TRACE_TS)
-#define _TIF_NOTSC (1 << TIF_NOTSC)
-
-/* work to do on interrupt/exception return */
-#define _TIF_WORK_MASK \
- (0x0000FFFF & \
- ~(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SINGLESTEP|_TIF_SECCOMP))
-/* work to do on any return to user space */
-#define _TIF_ALLWORK_MASK (0x0000FFFF & ~_TIF_SECCOMP)
-
-#define _TIF_DO_NOTIFY_MASK \
- (_TIF_SIGPENDING|_TIF_SINGLESTEP|_TIF_MCE_NOTIFY|_TIF_HRTICK_RESCHED)
-
-/* flags to check in __switch_to() */
-#define _TIF_WORK_CTXSW \
- (_TIF_IO_BITMAP|_TIF_DEBUGCTLMSR|_TIF_DS_AREA_MSR|_TIF_BTS_TRACE_TS|_TIF_NOTSC)
-#define _TIF_WORK_CTXSW_PREV _TIF_WORK_CTXSW
-#define _TIF_WORK_CTXSW_NEXT (_TIF_WORK_CTXSW|_TIF_DEBUG)
-
-#define PREEMPT_ACTIVE 0x10000000
-
-/*
- * Thread-synchronous status.
- *
- * This is different from the flags in that nobody else
- * ever touches our thread-synchronous status, so we don't
- * have to worry about atomic accesses.
- */
-#define TS_USEDFPU 0x0001 /* FPU was used by this task
- this quantum (SMP) */
-#define TS_COMPAT 0x0002 /* 32bit syscall active */
-#define TS_POLLING 0x0004 /* true if in idle loop
- and not sleeping */
-#define TS_RESTORE_SIGMASK 0x0008 /* restore signal mask in do_signal() */
-
-#define tsk_is_polling(t) (task_thread_info(t)->status & TS_POLLING)
-
-#ifndef __ASSEMBLY__
-#define HAVE_SET_RESTORE_SIGMASK 1
-static inline void set_restore_sigmask(void)
-{
- struct thread_info *ti = current_thread_info();
- ti->status |= TS_RESTORE_SIGMASK;
- set_bit(TIF_SIGPENDING, &ti->flags);
-}
-#endif /* !__ASSEMBLY__ */
-
-#endif /* __KERNEL__ */
-
-#endif /* _ASM_THREAD_INFO_H */
diff --git a/include/asm-x86/topology.h b/include/asm-x86/topology.h
index dcf3f8131d6b..1f97758de4ab 100644
--- a/include/asm-x86/topology.h
+++ b/include/asm-x86/topology.h
@@ -35,79 +35,88 @@
# endif
#endif
+/* Node not present */
+#define NUMA_NO_NODE (-1)
+
#ifdef CONFIG_NUMA
#include <linux/cpumask.h>
#include <asm/mpspec.h>
-/* Mappings between logical cpu number and node number */
#ifdef CONFIG_X86_32
-extern int cpu_to_node_map[];
-#else
-/* Returns the number of the current Node. */
-#define numa_node_id() (early_cpu_to_node(raw_smp_processor_id()))
-#endif
-
-DECLARE_PER_CPU(int, x86_cpu_to_node_map);
-
-#ifdef CONFIG_SMP
-extern int x86_cpu_to_node_map_init[];
-extern void *x86_cpu_to_node_map_early_ptr;
-#else
-#define x86_cpu_to_node_map_early_ptr NULL
-#endif
+/* Mappings between node number and cpus on that node. */
extern cpumask_t node_to_cpumask_map[];
-#define NUMA_NO_NODE (-1)
+/* Mappings between logical cpu number and node number */
+extern int cpu_to_node_map[];
/* Returns the number of the node containing CPU 'cpu' */
-#ifdef CONFIG_X86_32
-#define early_cpu_to_node(cpu) cpu_to_node(cpu)
static inline int cpu_to_node(int cpu)
{
return cpu_to_node_map[cpu];
}
+#define early_cpu_to_node(cpu) cpu_to_node(cpu)
-#else /* CONFIG_X86_64 */
-
-#ifdef CONFIG_SMP
-static inline int early_cpu_to_node(int cpu)
+/* Returns a bitmask of CPUs on Node 'node'. */
+static inline cpumask_t node_to_cpumask(int node)
{
- int *cpu_to_node_map = x86_cpu_to_node_map_early_ptr;
-
- if (cpu_to_node_map)
- return cpu_to_node_map[cpu];
- else if (per_cpu_offset(cpu))
- return per_cpu(x86_cpu_to_node_map, cpu);
- else
- return NUMA_NO_NODE;
+ return node_to_cpumask_map[node];
}
-#else
-#define early_cpu_to_node(cpu) cpu_to_node(cpu)
-#endif
+#else /* CONFIG_X86_64 */
+
+/* Mappings between node number and cpus on that node. */
+extern cpumask_t *node_to_cpumask_map;
+
+/* Mappings between logical cpu number and node number */
+DECLARE_EARLY_PER_CPU(int, x86_cpu_to_node_map);
+
+/* Returns the number of the current Node. */
+#define numa_node_id() read_pda(nodenumber)
+
+#ifdef CONFIG_DEBUG_PER_CPU_MAPS
+extern int cpu_to_node(int cpu);
+extern int early_cpu_to_node(int cpu);
+extern cpumask_t *_node_to_cpumask_ptr(int node);
+extern cpumask_t node_to_cpumask(int node);
+
+#else /* !CONFIG_DEBUG_PER_CPU_MAPS */
+
+/* Returns the number of the node containing CPU 'cpu' */
static inline int cpu_to_node(int cpu)
{
-#ifdef CONFIG_DEBUG_PER_CPU_MAPS
- if (x86_cpu_to_node_map_early_ptr) {
- printk("KERN_NOTICE cpu_to_node(%d): usage too early!\n",
- (int)cpu);
- dump_stack();
- return ((int *)x86_cpu_to_node_map_early_ptr)[cpu];
- }
-#endif
return per_cpu(x86_cpu_to_node_map, cpu);
}
-#ifdef CONFIG_NUMA
+/* Same function but used if called before per_cpu areas are setup */
+static inline int early_cpu_to_node(int cpu)
+{
+ if (early_per_cpu_ptr(x86_cpu_to_node_map))
+ return early_per_cpu_ptr(x86_cpu_to_node_map)[cpu];
+
+ return per_cpu(x86_cpu_to_node_map, cpu);
+}
/* Returns a pointer to the cpumask of CPUs on Node 'node'. */
+static inline cpumask_t *_node_to_cpumask_ptr(int node)
+{
+ return &node_to_cpumask_map[node];
+}
+
+/* Returns a bitmask of CPUs on Node 'node'. */
+static inline cpumask_t node_to_cpumask(int node)
+{
+ return node_to_cpumask_map[node];
+}
+
+#endif /* !CONFIG_DEBUG_PER_CPU_MAPS */
+
+/* Replace default node_to_cpumask_ptr with optimized version */
#define node_to_cpumask_ptr(v, node) \
- cpumask_t *v = &(node_to_cpumask_map[node])
+ cpumask_t *v = _node_to_cpumask_ptr(node)
#define node_to_cpumask_ptr_next(v, node) \
- v = &(node_to_cpumask_map[node])
-#endif
+ v = _node_to_cpumask_ptr(node)
#endif /* CONFIG_X86_64 */
@@ -117,20 +126,6 @@ static inline int cpu_to_node(int cpu)
*/
#define parent_node(node) (node)
-/* Returns a bitmask of CPUs on Node 'node'. */
-static inline cpumask_t node_to_cpumask(int node)
-{
- return node_to_cpumask_map[node];
-}
-
-/* Returns the number of the first CPU on Node 'node'. */
-static inline int node_to_first_cpu(int node)
-{
- cpumask_t mask = node_to_cpumask(node);
-
- return first_cpu(mask);
-}
-
#define pcibus_to_node(bus) __pcibus_to_node(bus)
#define pcibus_to_cpumask(bus) __pcibus_to_cpumask(bus)
@@ -180,12 +175,44 @@ extern int __node_distance(int, int);
#define node_distance(a, b) __node_distance(a, b)
#endif
-#else /* CONFIG_NUMA */
+#else /* !CONFIG_NUMA */
+#define numa_node_id() 0
+#define cpu_to_node(cpu) 0
+#define early_cpu_to_node(cpu) 0
+
+static inline cpumask_t *_node_to_cpumask_ptr(int node)
+{
+ return &cpu_online_map;
+}
+static inline cpumask_t node_to_cpumask(int node)
+{
+ return cpu_online_map;
+}
+static inline int node_to_first_cpu(int node)
+{
+ return first_cpu(cpu_online_map);
+}
+
+/* Replace default node_to_cpumask_ptr with optimized version */
+#define node_to_cpumask_ptr(v, node) \
+ cpumask_t *v = _node_to_cpumask_ptr(node)
+
+#define node_to_cpumask_ptr_next(v, node) \
+ v = _node_to_cpumask_ptr(node)
#endif
#include <asm-generic/topology.h>
+#ifdef CONFIG_NUMA
+/* Returns the number of the first CPU on Node 'node'. */
+static inline int node_to_first_cpu(int node)
+{
+ node_to_cpumask_ptr(mask, node);
+ return first_cpu(*mask);
+}
+#endif
+
extern cpumask_t cpu_coregroup_map(int cpu);
#ifdef ENABLE_TOPO_DEFINES
@@ -193,6 +220,9 @@ extern cpumask_t cpu_coregroup_map(int cpu);
#define topology_core_id(cpu) (cpu_data(cpu).cpu_core_id)
#define topology_core_siblings(cpu) (per_cpu(cpu_core_map, cpu))
#define topology_thread_siblings(cpu) (per_cpu(cpu_sibling_map, cpu))
+
+/* indicates that pointers to the topology cpumask_t maps are valid */
+#define arch_provides_topology_pointers yes
#endif
static inline void arch_fix_phys_package_id(int num, u32 slot)
@@ -220,4 +250,4 @@ static inline void set_mp_bus_to_node(int busnum, int node)
}
#endif
-#endif
+#endif /* _ASM_X86_TOPOLOGY_H */
diff --git a/include/asm-x86/uv/uv_hub.h b/include/asm-x86/uv/uv_hub.h
index 26b9240d1e23..65004881de5f 100644
--- a/include/asm-x86/uv/uv_hub.h
+++ b/include/asm-x86/uv/uv_hub.h
@@ -5,7 +5,7 @@
*
* SGI UV architectural definitions
*
- * Copyright (C) 2007 Silicon Graphics, Inc. All rights reserved.
+ * Copyright (C) 2007-2008 Silicon Graphics, Inc. All rights reserved.
*/
#ifndef __ASM_X86_UV_HUB_H__
@@ -20,26 +20,49 @@
/*
* Addressing Terminology
*
- * NASID - network ID of a router, Mbrick or Cbrick. Nasid values of
- * routers always have low bit of 1, C/MBricks have low bit
- * equal to 0. Most addressing macros that target UV hub chips
- * right shift the NASID by 1 to exclude the always-zero bit.
+ * M - The low M bits of a physical address represent the offset
+ * into the blade local memory. RAM memory on a blade is physically
+ * contiguous (although various IO spaces may punch holes in
+ * it)..
*
- * SNASID - NASID right shifted by 1 bit.
+ * N - Number of bits in the node portion of a socket physical
+ * address.
+ *
+ * NASID - network ID of a router, Mbrick or Cbrick. Nasid values of
+ * routers always have low bit of 1, C/MBricks have low bit
+ * equal to 0. Most addressing macros that target UV hub chips
+ * right shift the NASID by 1 to exclude the always-zero bit.
+ * NASIDs contain up to 15 bits.
+ *
+ * GNODE - NASID right shifted by 1 bit. Most mmrs contain gnodes instead
+ * of nasids.
+ *
+ * PNODE - the low N bits of the GNODE. The PNODE is the most useful variant
+ * of the nasid for socket usage.
+ *
+ *
+ * NumaLink Global Physical Address Format:
+ * +--------------------------------+---------------------+
+ * |00..000| GNODE | NodeOffset |
+ * +--------------------------------+---------------------+
+ * |<-------53 - M bits --->|<--------M bits ----->
+ *
+ * M - number of node offset bits (35 .. 40)
*
*
* Memory/UV-HUB Processor Socket Address Format:
- * +--------+---------------+---------------------+
- * |00..0000| SNASID | NodeOffset |
- * +--------+---------------+---------------------+
- * <--- N bits --->|<--------M bits ----->
+ * +----------------+---------------+---------------------+
+ * |00..000000000000| PNODE | NodeOffset |
+ * +----------------+---------------+---------------------+
+ * <--- N bits --->|<--------M bits ----->
*
- * M number of node offset bits (35 .. 40)
- * N number of SNASID bits (0 .. 10)
+ * M - number of node offset bits (35 .. 40)
+ * N - number of PNODE bits (0 .. 10)
*
* Note: M + N cannot currently exceed 44 (x86_64) or 46 (IA64).
* The actual values are configuration dependent and are set at
- * boot time
+ * boot time. M & N values are set by the hardware/BIOS at boot.
+ *
*
* APICID format
* NOTE!!!!!! This is the current format of the APICID. However, code
@@ -48,14 +71,14 @@
*
* 1111110000000000
* 5432109876543210
- * nnnnnnnnnnlc0cch
+ * pppppppppplc0cch
* sssssssssss
*
- * n = snasid bits
+ * p = pnode bits
* l = socket number on board
* c = core
* h = hyperthread
- * s = bits that are in the socket CSR
+ * s = bits that are in the SOCKET_ID CSR
*
* Note: Processor only supports 12 bits in the APICID register. The ACPI
* tables hold all 16 bits. Software needs to be aware of this.
@@ -74,7 +97,7 @@
* This value is also the value of the maximum number of non-router NASIDs
* in the numalink fabric.
*
- * NOTE: a brick may be 1 or 2 OS nodes. Don't get these confused.
+ * NOTE: a brick may contain 1 or 2 OS nodes. Don't get these confused.
*/
#define UV_MAX_NUMALINK_BLADES 16384
@@ -96,8 +119,12 @@
*/
struct uv_hub_info_s {
unsigned long global_mmr_base;
- unsigned short local_nasid;
- unsigned short gnode_upper;
+ unsigned long gpa_mask;
+ unsigned long gnode_upper;
+ unsigned long lowmem_remap_top;
+ unsigned long lowmem_remap_base;
+ unsigned short pnode;
+ unsigned short pnode_mask;
unsigned short coherency_domain_number;
unsigned short numa_blade_id;
unsigned char blade_processor_id;
@@ -112,83 +139,124 @@ DECLARE_PER_CPU(struct uv_hub_info_s, __uv_hub_info);
* Local & Global MMR space macros.
* Note: macros are intended to be used ONLY by inline functions
* in this file - not by other kernel code.
+ * n - NASID (full 15-bit global nasid)
+ * g - GNODE (full 15-bit global nasid, right shifted 1)
+ * p - PNODE (local part of nsids, right shifted 1)
*/
-#define UV_SNASID(n) ((n) >> 1)
-#define UV_NASID(n) ((n) << 1)
+#define UV_NASID_TO_PNODE(n) (((n) >> 1) & uv_hub_info->pnode_mask)
+#define UV_PNODE_TO_NASID(p) (((p) << 1) | uv_hub_info->gnode_upper)
#define UV_LOCAL_MMR_BASE 0xf4000000UL
#define UV_GLOBAL_MMR32_BASE 0xf8000000UL
#define UV_GLOBAL_MMR64_BASE (uv_hub_info->global_mmr_base)
-#define UV_GLOBAL_MMR32_SNASID_MASK 0x3ff
-#define UV_GLOBAL_MMR32_SNASID_SHIFT 15
-#define UV_GLOBAL_MMR64_SNASID_SHIFT 26
+#define UV_GLOBAL_MMR32_PNODE_SHIFT 15
+#define UV_GLOBAL_MMR64_PNODE_SHIFT 26
-#define UV_GLOBAL_MMR32_NASID_BITS(n) \
- (((UV_SNASID(n) & UV_GLOBAL_MMR32_SNASID_MASK)) << \
- (UV_GLOBAL_MMR32_SNASID_SHIFT))
+#define UV_GLOBAL_MMR32_PNODE_BITS(p) ((p) << (UV_GLOBAL_MMR32_PNODE_SHIFT))
-#define UV_GLOBAL_MMR64_NASID_BITS(n) \
- ((unsigned long)UV_SNASID(n) << UV_GLOBAL_MMR64_SNASID_SHIFT)
+#define UV_GLOBAL_MMR64_PNODE_BITS(p) \
+ ((unsigned long)(p) << UV_GLOBAL_MMR64_PNODE_SHIFT)
+
+#define UV_APIC_PNODE_SHIFT 6
+
+/*
+ * Macros for converting between kernel virtual addresses, socket local physical
+ * addresses, and UV global physical addresses.
+ * Note: use the standard __pa() & __va() macros for converting
+ * between socket virtual and socket physical addresses.
+ */
+
+/* socket phys RAM --> UV global physical address */
+static inline unsigned long uv_soc_phys_ram_to_gpa(unsigned long paddr)
+{
+ if (paddr < uv_hub_info->lowmem_remap_top)
+ paddr += uv_hub_info->lowmem_remap_base;
+ return paddr | uv_hub_info->gnode_upper;
+}
+
+
+/* socket virtual --> UV global physical address */
+static inline unsigned long uv_gpa(void *v)
+{
+ return __pa(v) | uv_hub_info->gnode_upper;
+}
+
+/* socket virtual --> UV global physical address */
+static inline void *uv_vgpa(void *v)
+{
+ return (void *)uv_gpa(v);
+}
+
+/* UV global physical address --> socket virtual */
+static inline void *uv_va(unsigned long gpa)
+{
+ return __va(gpa & uv_hub_info->gpa_mask);
+}
+
+/* pnode, offset --> socket virtual */
+static inline void *uv_pnode_offset_to_vaddr(int pnode, unsigned long offset)
+{
+ return __va(((unsigned long)pnode << uv_hub_info->m_val) | offset);
+}
-#define UV_APIC_NASID_SHIFT 6
/*
- * Extract a NASID from an APICID (full apicid, not processor subset)
+ * Extract a PNODE from an APICID (full apicid, not processor subset)
*/
-static inline int uv_apicid_to_nasid(int apicid)
+static inline int uv_apicid_to_pnode(int apicid)
{
- return (UV_NASID(apicid >> UV_APIC_NASID_SHIFT));
+ return (apicid >> UV_APIC_PNODE_SHIFT);
}
/*
* Access global MMRs using the low memory MMR32 space. This region supports
* faster MMR access but not all MMRs are accessible in this space.
*/
-static inline unsigned long *uv_global_mmr32_address(int nasid,
+static inline unsigned long *uv_global_mmr32_address(int pnode,
unsigned long offset)
{
return __va(UV_GLOBAL_MMR32_BASE |
- UV_GLOBAL_MMR32_NASID_BITS(nasid) | offset);
+ UV_GLOBAL_MMR32_PNODE_BITS(pnode) | offset);
}
-static inline void uv_write_global_mmr32(int nasid, unsigned long offset,
+static inline void uv_write_global_mmr32(int pnode, unsigned long offset,
unsigned long val)
{
- *uv_global_mmr32_address(nasid, offset) = val;
+ *uv_global_mmr32_address(pnode, offset) = val;
}
-static inline unsigned long uv_read_global_mmr32(int nasid,
+static inline unsigned long uv_read_global_mmr32(int pnode,
unsigned long offset)
{
- return *uv_global_mmr32_address(nasid, offset);
+ return *uv_global_mmr32_address(pnode, offset);
}
/*
* Access Global MMR space using the MMR space located at the top of physical
* memory.
*/
-static inline unsigned long *uv_global_mmr64_address(int nasid,
+static inline unsigned long *uv_global_mmr64_address(int pnode,
unsigned long offset)
{
return __va(UV_GLOBAL_MMR64_BASE |
- UV_GLOBAL_MMR64_NASID_BITS(nasid) | offset);
+ UV_GLOBAL_MMR64_PNODE_BITS(pnode) | offset);
}
-static inline void uv_write_global_mmr64(int nasid, unsigned long offset,
+static inline void uv_write_global_mmr64(int pnode, unsigned long offset,
unsigned long val)
{
- *uv_global_mmr64_address(nasid, offset) = val;
+ *uv_global_mmr64_address(pnode, offset) = val;
}
-static inline unsigned long uv_read_global_mmr64(int nasid,
+static inline unsigned long uv_read_global_mmr64(int pnode,
unsigned long offset)
{
- return *uv_global_mmr64_address(nasid, offset);
+ return *uv_global_mmr64_address(pnode, offset);
}
/*
- * Access node local MMRs. Faster than using global space but only local MMRs
+ * Access hub local MMRs. Faster than using global space but only local MMRs
* are accessible.
*/
static inline unsigned long *uv_local_mmr_address(unsigned long offset)
@@ -207,15 +275,15 @@ static inline void uv_write_local_mmr(unsigned long offset, unsigned long val)
}
/*
- * Structures and definitions for converting between cpu, node, and blade
+ * Structures and definitions for converting between cpu, node, pnode, and blade
* numbers.
*/
struct uv_blade_info {
- unsigned short nr_posible_cpus;
+ unsigned short nr_possible_cpus;
unsigned short nr_online_cpus;
- unsigned short nasid;
+ unsigned short pnode;
};
-struct uv_blade_info *uv_blade_info;
+extern struct uv_blade_info *uv_blade_info;
extern short *uv_node_to_blade;
extern short *uv_cpu_to_blade;
extern short uv_possible_blades;
@@ -244,16 +312,16 @@ static inline int uv_node_to_blade_id(int nid)
return uv_node_to_blade[nid];
}
-/* Convert a blade id to the NASID of the blade */
-static inline int uv_blade_to_nasid(int bid)
+/* Convert a blade id to the PNODE of the blade */
+static inline int uv_blade_to_pnode(int bid)
{
- return uv_blade_info[bid].nasid;
+ return uv_blade_info[bid].pnode;
}
/* Determine the number of possible cpus on a blade */
static inline int uv_blade_nr_possible_cpus(int bid)
{
- return uv_blade_info[bid].nr_posible_cpus;
+ return uv_blade_info[bid].nr_possible_cpus;
}
/* Determine the number of online cpus on a blade */
@@ -262,16 +330,16 @@ static inline int uv_blade_nr_online_cpus(int bid)
return uv_blade_info[bid].nr_online_cpus;
}
-/* Convert a cpu id to the NASID of the blade containing the cpu */
-static inline int uv_cpu_to_nasid(int cpu)
+/* Convert a cpu id to the PNODE of the blade containing the cpu */
+static inline int uv_cpu_to_pnode(int cpu)
{
- return uv_blade_info[uv_cpu_to_blade_id(cpu)].nasid;
+ return uv_blade_info[uv_cpu_to_blade_id(cpu)].pnode;
}
-/* Convert a node number to the NASID of the blade */
-static inline int uv_node_to_nasid(int nid)
+/* Convert a linux node number to the PNODE of the blade */
+static inline int uv_node_to_pnode(int nid)
{
- return uv_blade_info[uv_node_to_blade_id(nid)].nasid;
+ return uv_blade_info[uv_node_to_blade_id(nid)].pnode;
}
/* Maximum possible number of blades */
diff --git a/include/asm-x86/uv/uv_mmrs.h b/include/asm-x86/uv/uv_mmrs.h
index 3b69fe6b6376..ac9846076521 100644
--- a/include/asm-x86/uv/uv_mmrs.h
+++ b/include/asm-x86/uv/uv_mmrs.h
@@ -11,11 +11,46 @@
#ifndef __ASM_X86_UV_MMRS__
#define __ASM_X86_UV_MMRS__
-/*
- * AUTO GENERATED - Do not edit
- */
+#define UV_MMR_ENABLE (1UL << 63)
- #define UV_MMR_ENABLE (1UL << 63)
+/* ========================================================================= */
+/* UVH_BAU_DATA_CONFIG */
+/* ========================================================================= */
+#define UVH_BAU_DATA_CONFIG 0x61680UL
+#define UVH_BAU_DATA_CONFIG_32 0x0450
+
+#define UVH_BAU_DATA_CONFIG_VECTOR_SHFT 0
+#define UVH_BAU_DATA_CONFIG_VECTOR_MASK 0x00000000000000ffUL
+#define UVH_BAU_DATA_CONFIG_DM_SHFT 8
+#define UVH_BAU_DATA_CONFIG_DM_MASK 0x0000000000000700UL
+#define UVH_BAU_DATA_CONFIG_DESTMODE_SHFT 11
+#define UVH_BAU_DATA_CONFIG_DESTMODE_MASK 0x0000000000000800UL
+#define UVH_BAU_DATA_CONFIG_STATUS_SHFT 12
+#define UVH_BAU_DATA_CONFIG_STATUS_MASK 0x0000000000001000UL
+#define UVH_BAU_DATA_CONFIG_P_SHFT 13
+#define UVH_BAU_DATA_CONFIG_P_MASK 0x0000000000002000UL
+#define UVH_BAU_DATA_CONFIG_T_SHFT 15
+#define UVH_BAU_DATA_CONFIG_T_MASK 0x0000000000008000UL
+#define UVH_BAU_DATA_CONFIG_M_SHFT 16
+#define UVH_BAU_DATA_CONFIG_M_MASK 0x0000000000010000UL
+#define UVH_BAU_DATA_CONFIG_APIC_ID_SHFT 32
+#define UVH_BAU_DATA_CONFIG_APIC_ID_MASK 0xffffffff00000000UL
+
+union uvh_bau_data_config_u {
+ unsigned long v;
+ struct uvh_bau_data_config_s {
+ unsigned long vector_ : 8; /* RW */
+ unsigned long dm : 3; /* RW */
+ unsigned long destmode : 1; /* RW */
+ unsigned long status : 1; /* RO */
+ unsigned long p : 1; /* RO */
+ unsigned long rsvd_14 : 1; /* */
+ unsigned long t : 1; /* RO */
+ unsigned long m : 1; /* RW */
+ unsigned long rsvd_17_31: 15; /* */
+ unsigned long apic_id : 32; /* RW */
+ } s;
+};
/* ========================================================================= */
/* UVH_IPI_INT */
@@ -109,6 +144,7 @@ union uvh_lb_bau_intd_payload_queue_tail_u {
/* UVH_LB_BAU_INTD_SOFTWARE_ACKNOWLEDGE */
/* ========================================================================= */
#define UVH_LB_BAU_INTD_SOFTWARE_ACKNOWLEDGE 0x320080UL
+#define UVH_LB_BAU_INTD_SOFTWARE_ACKNOWLEDGE_32 0x0aa0
#define UVH_LB_BAU_INTD_SOFTWARE_ACKNOWLEDGE_PENDING_0_SHFT 0
#define UVH_LB_BAU_INTD_SOFTWARE_ACKNOWLEDGE_PENDING_0_MASK 0x0000000000000001UL
@@ -169,6 +205,7 @@ union uvh_lb_bau_intd_software_acknowledge_u {
/* UVH_LB_BAU_INTD_SOFTWARE_ACKNOWLEDGE_ALIAS */
/* ========================================================================= */
#define UVH_LB_BAU_INTD_SOFTWARE_ACKNOWLEDGE_ALIAS 0x0000000000320088UL
+#define UVH_LB_BAU_INTD_SOFTWARE_ACKNOWLEDGE_ALIAS_32 0x0aa8
/* ========================================================================= */
/* UVH_LB_BAU_SB_ACTIVATION_CONTROL */
@@ -248,6 +285,331 @@ union uvh_lb_bau_sb_descriptor_base_u {
};
/* ========================================================================= */
+/* UVH_LB_MCAST_AOERR0_RPT_ENABLE */
+/* ========================================================================= */
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE 0x50b20UL
+
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MCAST_OBESE_MSG_SHFT 0
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MCAST_OBESE_MSG_MASK 0x0000000000000001UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MCAST_DATA_SB_ERR_SHFT 1
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MCAST_DATA_SB_ERR_MASK 0x0000000000000002UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MCAST_NACK_BUFF_PARITY_SHFT 2
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MCAST_NACK_BUFF_PARITY_MASK 0x0000000000000004UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MCAST_TIMEOUT_SHFT 3
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MCAST_TIMEOUT_MASK 0x0000000000000008UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MCAST_INACTIVE_REPLY_SHFT 4
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MCAST_INACTIVE_REPLY_MASK 0x0000000000000010UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MCAST_UPGRADE_ERROR_SHFT 5
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MCAST_UPGRADE_ERROR_MASK 0x0000000000000020UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MCAST_REG_COUNT_UNDERFLOW_SHFT 6
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MCAST_REG_COUNT_UNDERFLOW_MASK 0x0000000000000040UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MCAST_REP_OBESE_MSG_SHFT 7
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MCAST_REP_OBESE_MSG_MASK 0x0000000000000080UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_UCACHE_REQ_RUNT_MSG_SHFT 8
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_UCACHE_REQ_RUNT_MSG_MASK 0x0000000000000100UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_UCACHE_REQ_OBESE_MSG_SHFT 9
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_UCACHE_REQ_OBESE_MSG_MASK 0x0000000000000200UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_UCACHE_REQ_DATA_SB_ERR_SHFT 10
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_UCACHE_REQ_DATA_SB_ERR_MASK 0x0000000000000400UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_UCACHE_REP_RUNT_MSG_SHFT 11
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_UCACHE_REP_RUNT_MSG_MASK 0x0000000000000800UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_UCACHE_REP_OBESE_MSG_SHFT 12
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_UCACHE_REP_OBESE_MSG_MASK 0x0000000000001000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_UCACHE_REP_DATA_SB_ERR_SHFT 13
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_UCACHE_REP_DATA_SB_ERR_MASK 0x0000000000002000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_UCACHE_REP_COMMAND_ERR_SHFT 14
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_UCACHE_REP_COMMAND_ERR_MASK 0x0000000000004000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_UCACHE_PEND_TIMEOUT_SHFT 15
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_UCACHE_PEND_TIMEOUT_MASK 0x0000000000008000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MACC_REQ_RUNT_MSG_SHFT 16
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MACC_REQ_RUNT_MSG_MASK 0x0000000000010000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MACC_REQ_OBESE_MSG_SHFT 17
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MACC_REQ_OBESE_MSG_MASK 0x0000000000020000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MACC_REQ_DATA_SB_ERR_SHFT 18
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MACC_REQ_DATA_SB_ERR_MASK 0x0000000000040000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MACC_REP_RUNT_MSG_SHFT 19
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MACC_REP_RUNT_MSG_MASK 0x0000000000080000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MACC_REP_OBESE_MSG_SHFT 20
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MACC_REP_OBESE_MSG_MASK 0x0000000000100000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MACC_REP_DATA_SB_ERR_SHFT 21
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MACC_REP_DATA_SB_ERR_MASK 0x0000000000200000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MACC_TIMEOUT_SHFT 22
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MACC_TIMEOUT_MASK 0x0000000000400000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MACC_SPURIOUS_EVENT_SHFT 23
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_MACC_SPURIOUS_EVENT_MASK 0x0000000000800000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_IOH_DESTINATION_TABLE_PARITY_SHFT 24
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_IOH_DESTINATION_TABLE_PARITY_MASK 0x0000000001000000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_GET_HAD_ERROR_REPLY_SHFT 25
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_GET_HAD_ERROR_REPLY_MASK 0x0000000002000000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_GET_TIMEOUT_SHFT 26
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_GET_TIMEOUT_MASK 0x0000000004000000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_LOCK_MANAGER_HAD_ERROR_REPLY_SHFT 27
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_LOCK_MANAGER_HAD_ERROR_REPLY_MASK 0x0000000008000000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_PUT_HAD_ERROR_REPLY_SHFT 28
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_PUT_HAD_ERROR_REPLY_MASK 0x0000000010000000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_PUT_TIMEOUT_SHFT 29
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_PUT_TIMEOUT_MASK 0x0000000020000000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_SB_ACTIVATION_OVERRUN_SHFT 30
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_SB_ACTIVATION_OVERRUN_MASK 0x0000000040000000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_COMPLETED_GB_ACTIVATION_HAD_ERROR_REPLY_SHFT 31
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_COMPLETED_GB_ACTIVATION_HAD_ERROR_REPLY_MASK 0x0000000080000000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_COMPLETED_GB_ACTIVATION_TIMEOUT_SHFT 32
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_COMPLETED_GB_ACTIVATION_TIMEOUT_MASK 0x0000000100000000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_DESCRIPTOR_BUFFER_0_PARITY_SHFT 33
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_DESCRIPTOR_BUFFER_0_PARITY_MASK 0x0000000200000000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_DESCRIPTOR_BUFFER_1_PARITY_SHFT 34
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_DESCRIPTOR_BUFFER_1_PARITY_MASK 0x0000000400000000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_SOCKET_DESTINATION_TABLE_PARITY_SHFT 35
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_SOCKET_DESTINATION_TABLE_PARITY_MASK 0x0000000800000000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_BAU_REPLY_PAYLOAD_CORRUPTION_SHFT 36
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_BAU_REPLY_PAYLOAD_CORRUPTION_MASK 0x0000001000000000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_IO_PORT_DESTINATION_TABLE_PARITY_SHFT 37
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_IO_PORT_DESTINATION_TABLE_PARITY_MASK 0x0000002000000000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_INTD_SOFT_ACK_TIMEOUT_SHFT 38
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_INTD_SOFT_ACK_TIMEOUT_MASK 0x0000004000000000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_INT_REP_OBESE_MSG_SHFT 39
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_INT_REP_OBESE_MSG_MASK 0x0000008000000000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_INT_REP_COMMAND_ERR_SHFT 40
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_INT_REP_COMMAND_ERR_MASK 0x0000010000000000UL
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_INT_TIMEOUT_SHFT 41
+#define UVH_LB_MCAST_AOERR0_RPT_ENABLE_INT_TIMEOUT_MASK 0x0000020000000000UL
+
+union uvh_lb_mcast_aoerr0_rpt_enable_u {
+ unsigned long v;
+ struct uvh_lb_mcast_aoerr0_rpt_enable_s {
+ unsigned long mcast_obese_msg : 1; /* RW */
+ unsigned long mcast_data_sb_err : 1; /* RW */
+ unsigned long mcast_nack_buff_parity : 1; /* RW */
+ unsigned long mcast_timeout : 1; /* RW */
+ unsigned long mcast_inactive_reply : 1; /* RW */
+ unsigned long mcast_upgrade_error : 1; /* RW */
+ unsigned long mcast_reg_count_underflow : 1; /* RW */
+ unsigned long mcast_rep_obese_msg : 1; /* RW */
+ unsigned long ucache_req_runt_msg : 1; /* RW */
+ unsigned long ucache_req_obese_msg : 1; /* RW */
+ unsigned long ucache_req_data_sb_err : 1; /* RW */
+ unsigned long ucache_rep_runt_msg : 1; /* RW */
+ unsigned long ucache_rep_obese_msg : 1; /* RW */
+ unsigned long ucache_rep_data_sb_err : 1; /* RW */
+ unsigned long ucache_rep_command_err : 1; /* RW */
+ unsigned long ucache_pend_timeout : 1; /* RW */
+ unsigned long macc_req_runt_msg : 1; /* RW */
+ unsigned long macc_req_obese_msg : 1; /* RW */
+ unsigned long macc_req_data_sb_err : 1; /* RW */
+ unsigned long macc_rep_runt_msg : 1; /* RW */
+ unsigned long macc_rep_obese_msg : 1; /* RW */
+ unsigned long macc_rep_data_sb_err : 1; /* RW */
+ unsigned long macc_timeout : 1; /* RW */
+ unsigned long macc_spurious_event : 1; /* RW */
+ unsigned long ioh_destination_table_parity : 1; /* RW */
+ unsigned long get_had_error_reply : 1; /* RW */
+ unsigned long get_timeout : 1; /* RW */
+ unsigned long lock_manager_had_error_reply : 1; /* RW */
+ unsigned long put_had_error_reply : 1; /* RW */
+ unsigned long put_timeout : 1; /* RW */
+ unsigned long sb_activation_overrun : 1; /* RW */
+ unsigned long completed_gb_activation_had_error_reply : 1; /* RW */
+ unsigned long completed_gb_activation_timeout : 1; /* RW */
+ unsigned long descriptor_buffer_0_parity : 1; /* RW */
+ unsigned long descriptor_buffer_1_parity : 1; /* RW */
+ unsigned long socket_destination_table_parity : 1; /* RW */
+ unsigned long bau_reply_payload_corruption : 1; /* RW */
+ unsigned long io_port_destination_table_parity : 1; /* RW */
+ unsigned long intd_soft_ack_timeout : 1; /* RW */
+ unsigned long int_rep_obese_msg : 1; /* RW */
+ unsigned long int_rep_command_err : 1; /* RW */
+ unsigned long int_timeout : 1; /* RW */
+ unsigned long rsvd_42_63 : 22; /* */
+ } s;
+};
+
+/* ========================================================================= */
+/* UVH_LOCAL_INT0_CONFIG */
+/* ========================================================================= */
+#define UVH_LOCAL_INT0_CONFIG 0x61000UL
+
+#define UVH_LOCAL_INT0_CONFIG_VECTOR_SHFT 0
+#define UVH_LOCAL_INT0_CONFIG_VECTOR_MASK 0x00000000000000ffUL
+#define UVH_LOCAL_INT0_CONFIG_DM_SHFT 8
+#define UVH_LOCAL_INT0_CONFIG_DM_MASK 0x0000000000000700UL
+#define UVH_LOCAL_INT0_CONFIG_DESTMODE_SHFT 11
+#define UVH_LOCAL_INT0_CONFIG_DESTMODE_MASK 0x0000000000000800UL
+#define UVH_LOCAL_INT0_CONFIG_STATUS_SHFT 12
+#define UVH_LOCAL_INT0_CONFIG_STATUS_MASK 0x0000000000001000UL
+#define UVH_LOCAL_INT0_CONFIG_P_SHFT 13
+#define UVH_LOCAL_INT0_CONFIG_P_MASK 0x0000000000002000UL
+#define UVH_LOCAL_INT0_CONFIG_T_SHFT 15
+#define UVH_LOCAL_INT0_CONFIG_T_MASK 0x0000000000008000UL
+#define UVH_LOCAL_INT0_CONFIG_M_SHFT 16
+#define UVH_LOCAL_INT0_CONFIG_M_MASK 0x0000000000010000UL
+#define UVH_LOCAL_INT0_CONFIG_APIC_ID_SHFT 32
+#define UVH_LOCAL_INT0_CONFIG_APIC_ID_MASK 0xffffffff00000000UL
+
+union uvh_local_int0_config_u {
+ unsigned long v;
+ struct uvh_local_int0_config_s {
+ unsigned long vector_ : 8; /* RW */
+ unsigned long dm : 3; /* RW */
+ unsigned long destmode : 1; /* RW */
+ unsigned long status : 1; /* RO */
+ unsigned long p : 1; /* RO */
+ unsigned long rsvd_14 : 1; /* */
+ unsigned long t : 1; /* RO */
+ unsigned long m : 1; /* RW */
+ unsigned long rsvd_17_31: 15; /* */
+ unsigned long apic_id : 32; /* RW */
+ } s;
+};
+
+/* ========================================================================= */
+/* UVH_LOCAL_INT0_ENABLE */
+/* ========================================================================= */
+#define UVH_LOCAL_INT0_ENABLE 0x65000UL
+
+#define UVH_LOCAL_INT0_ENABLE_LB_HCERR_SHFT 0
+#define UVH_LOCAL_INT0_ENABLE_LB_HCERR_MASK 0x0000000000000001UL
+#define UVH_LOCAL_INT0_ENABLE_GR0_HCERR_SHFT 1
+#define UVH_LOCAL_INT0_ENABLE_GR0_HCERR_MASK 0x0000000000000002UL
+#define UVH_LOCAL_INT0_ENABLE_GR1_HCERR_SHFT 2
+#define UVH_LOCAL_INT0_ENABLE_GR1_HCERR_MASK 0x0000000000000004UL
+#define UVH_LOCAL_INT0_ENABLE_LH_HCERR_SHFT 3
+#define UVH_LOCAL_INT0_ENABLE_LH_HCERR_MASK 0x0000000000000008UL
+#define UVH_LOCAL_INT0_ENABLE_RH_HCERR_SHFT 4
+#define UVH_LOCAL_INT0_ENABLE_RH_HCERR_MASK 0x0000000000000010UL
+#define UVH_LOCAL_INT0_ENABLE_XN_HCERR_SHFT 5
+#define UVH_LOCAL_INT0_ENABLE_XN_HCERR_MASK 0x0000000000000020UL
+#define UVH_LOCAL_INT0_ENABLE_SI_HCERR_SHFT 6
+#define UVH_LOCAL_INT0_ENABLE_SI_HCERR_MASK 0x0000000000000040UL
+#define UVH_LOCAL_INT0_ENABLE_LB_AOERR0_SHFT 7
+#define UVH_LOCAL_INT0_ENABLE_LB_AOERR0_MASK 0x0000000000000080UL
+#define UVH_LOCAL_INT0_ENABLE_GR0_AOERR0_SHFT 8
+#define UVH_LOCAL_INT0_ENABLE_GR0_AOERR0_MASK 0x0000000000000100UL
+#define UVH_LOCAL_INT0_ENABLE_GR1_AOERR0_SHFT 9
+#define UVH_LOCAL_INT0_ENABLE_GR1_AOERR0_MASK 0x0000000000000200UL
+#define UVH_LOCAL_INT0_ENABLE_LH_AOERR0_SHFT 10
+#define UVH_LOCAL_INT0_ENABLE_LH_AOERR0_MASK 0x0000000000000400UL
+#define UVH_LOCAL_INT0_ENABLE_RH_AOERR0_SHFT 11
+#define UVH_LOCAL_INT0_ENABLE_RH_AOERR0_MASK 0x0000000000000800UL
+#define UVH_LOCAL_INT0_ENABLE_XN_AOERR0_SHFT 12
+#define UVH_LOCAL_INT0_ENABLE_XN_AOERR0_MASK 0x0000000000001000UL
+#define UVH_LOCAL_INT0_ENABLE_SI_AOERR0_SHFT 13
+#define UVH_LOCAL_INT0_ENABLE_SI_AOERR0_MASK 0x0000000000002000UL
+#define UVH_LOCAL_INT0_ENABLE_LB_AOERR1_SHFT 14
+#define UVH_LOCAL_INT0_ENABLE_LB_AOERR1_MASK 0x0000000000004000UL
+#define UVH_LOCAL_INT0_ENABLE_GR0_AOERR1_SHFT 15
+#define UVH_LOCAL_INT0_ENABLE_GR0_AOERR1_MASK 0x0000000000008000UL
+#define UVH_LOCAL_INT0_ENABLE_GR1_AOERR1_SHFT 16
+#define UVH_LOCAL_INT0_ENABLE_GR1_AOERR1_MASK 0x0000000000010000UL
+#define UVH_LOCAL_INT0_ENABLE_LH_AOERR1_SHFT 17
+#define UVH_LOCAL_INT0_ENABLE_LH_AOERR1_MASK 0x0000000000020000UL
+#define UVH_LOCAL_INT0_ENABLE_RH_AOERR1_SHFT 18
+#define UVH_LOCAL_INT0_ENABLE_RH_AOERR1_MASK 0x0000000000040000UL
+#define UVH_LOCAL_INT0_ENABLE_XN_AOERR1_SHFT 19
+#define UVH_LOCAL_INT0_ENABLE_XN_AOERR1_MASK 0x0000000000080000UL
+#define UVH_LOCAL_INT0_ENABLE_SI_AOERR1_SHFT 20
+#define UVH_LOCAL_INT0_ENABLE_SI_AOERR1_MASK 0x0000000000100000UL
+#define UVH_LOCAL_INT0_ENABLE_RH_VPI_INT_SHFT 21
+#define UVH_LOCAL_INT0_ENABLE_RH_VPI_INT_MASK 0x0000000000200000UL
+#define UVH_LOCAL_INT0_ENABLE_SYSTEM_SHUTDOWN_INT_SHFT 22
+#define UVH_LOCAL_INT0_ENABLE_SYSTEM_SHUTDOWN_INT_MASK 0x0000000000400000UL
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_0_SHFT 23
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_0_MASK 0x0000000000800000UL
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_1_SHFT 24
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_1_MASK 0x0000000001000000UL
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_2_SHFT 25
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_2_MASK 0x0000000002000000UL
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_3_SHFT 26
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_3_MASK 0x0000000004000000UL
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_4_SHFT 27
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_4_MASK 0x0000000008000000UL
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_5_SHFT 28
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_5_MASK 0x0000000010000000UL
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_6_SHFT 29
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_6_MASK 0x0000000020000000UL
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_7_SHFT 30
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_7_MASK 0x0000000040000000UL
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_8_SHFT 31
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_8_MASK 0x0000000080000000UL
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_9_SHFT 32
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_9_MASK 0x0000000100000000UL
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_10_SHFT 33
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_10_MASK 0x0000000200000000UL
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_11_SHFT 34
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_11_MASK 0x0000000400000000UL
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_12_SHFT 35
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_12_MASK 0x0000000800000000UL
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_13_SHFT 36
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_13_MASK 0x0000001000000000UL
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_14_SHFT 37
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_14_MASK 0x0000002000000000UL
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_15_SHFT 38
+#define UVH_LOCAL_INT0_ENABLE_LB_IRQ_INT_15_MASK 0x0000004000000000UL
+#define UVH_LOCAL_INT0_ENABLE_L1_NMI_INT_SHFT 39
+#define UVH_LOCAL_INT0_ENABLE_L1_NMI_INT_MASK 0x0000008000000000UL
+#define UVH_LOCAL_INT0_ENABLE_STOP_CLOCK_SHFT 40
+#define UVH_LOCAL_INT0_ENABLE_STOP_CLOCK_MASK 0x0000010000000000UL
+#define UVH_LOCAL_INT0_ENABLE_ASIC_TO_L1_SHFT 41
+#define UVH_LOCAL_INT0_ENABLE_ASIC_TO_L1_MASK 0x0000020000000000UL
+#define UVH_LOCAL_INT0_ENABLE_L1_TO_ASIC_SHFT 42
+#define UVH_LOCAL_INT0_ENABLE_L1_TO_ASIC_MASK 0x0000040000000000UL
+#define UVH_LOCAL_INT0_ENABLE_LTC_INT_SHFT 43
+#define UVH_LOCAL_INT0_ENABLE_LTC_INT_MASK 0x0000080000000000UL
+#define UVH_LOCAL_INT0_ENABLE_LA_SEQ_TRIGGER_SHFT 44
+#define UVH_LOCAL_INT0_ENABLE_LA_SEQ_TRIGGER_MASK 0x0000100000000000UL
+
+union uvh_local_int0_enable_u {
+ unsigned long v;
+ struct uvh_local_int0_enable_s {
+ unsigned long lb_hcerr : 1; /* RW */
+ unsigned long gr0_hcerr : 1; /* RW */
+ unsigned long gr1_hcerr : 1; /* RW */
+ unsigned long lh_hcerr : 1; /* RW */
+ unsigned long rh_hcerr : 1; /* RW */
+ unsigned long xn_hcerr : 1; /* RW */
+ unsigned long si_hcerr : 1; /* RW */
+ unsigned long lb_aoerr0 : 1; /* RW */
+ unsigned long gr0_aoerr0 : 1; /* RW */
+ unsigned long gr1_aoerr0 : 1; /* RW */
+ unsigned long lh_aoerr0 : 1; /* RW */
+ unsigned long rh_aoerr0 : 1; /* RW */
+ unsigned long xn_aoerr0 : 1; /* RW */
+ unsigned long si_aoerr0 : 1; /* RW */
+ unsigned long lb_aoerr1 : 1; /* RW */
+ unsigned long gr0_aoerr1 : 1; /* RW */
+ unsigned long gr1_aoerr1 : 1; /* RW */
+ unsigned long lh_aoerr1 : 1; /* RW */
+ unsigned long rh_aoerr1 : 1; /* RW */
+ unsigned long xn_aoerr1 : 1; /* RW */
+ unsigned long si_aoerr1 : 1; /* RW */
+ unsigned long rh_vpi_int : 1; /* RW */
+ unsigned long system_shutdown_int : 1; /* RW */
+ unsigned long lb_irq_int_0 : 1; /* RW */
+ unsigned long lb_irq_int_1 : 1; /* RW */
+ unsigned long lb_irq_int_2 : 1; /* RW */
+ unsigned long lb_irq_int_3 : 1; /* RW */
+ unsigned long lb_irq_int_4 : 1; /* RW */
+ unsigned long lb_irq_int_5 : 1; /* RW */
+ unsigned long lb_irq_int_6 : 1; /* RW */
+ unsigned long lb_irq_int_7 : 1; /* RW */
+ unsigned long lb_irq_int_8 : 1; /* RW */
+ unsigned long lb_irq_int_9 : 1; /* RW */
+ unsigned long lb_irq_int_10 : 1; /* RW */
+ unsigned long lb_irq_int_11 : 1; /* RW */
+ unsigned long lb_irq_int_12 : 1; /* RW */
+ unsigned long lb_irq_int_13 : 1; /* RW */
+ unsigned long lb_irq_int_14 : 1; /* RW */
+ unsigned long lb_irq_int_15 : 1; /* RW */
+ unsigned long l1_nmi_int : 1; /* RW */
+ unsigned long stop_clock : 1; /* RW */
+ unsigned long asic_to_l1 : 1; /* RW */
+ unsigned long l1_to_asic : 1; /* RW */
+ unsigned long ltc_int : 1; /* RW */
+ unsigned long la_seq_trigger : 1; /* RW */
+ unsigned long rsvd_45_63 : 19; /* */
+ } s;
+};
+
+/* ========================================================================= */
/* UVH_NODE_ID */
/* ========================================================================= */
#define UVH_NODE_ID 0x0UL
@@ -284,6 +646,73 @@ union uvh_node_id_u {
};
/* ========================================================================= */
+/* UVH_NODE_PRESENT_TABLE */
+/* ========================================================================= */
+#define UVH_NODE_PRESENT_TABLE 0x1400UL
+#define UVH_NODE_PRESENT_TABLE_DEPTH 16
+
+#define UVH_NODE_PRESENT_TABLE_NODES_SHFT 0
+#define UVH_NODE_PRESENT_TABLE_NODES_MASK 0xffffffffffffffffUL
+
+union uvh_node_present_table_u {
+ unsigned long v;
+ struct uvh_node_present_table_s {
+ unsigned long nodes : 64; /* RW */
+ } s;
+};
+
+/* ========================================================================= */
+/* UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_0_MMR */
+/* ========================================================================= */
+#define UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_0_MMR 0x16000d0UL
+
+#define UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_0_MMR_DEST_BASE_SHFT 24
+#define UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_0_MMR_DEST_BASE_MASK 0x00003fffff000000UL
+
+union uvh_rh_gam_alias210_redirect_config_0_mmr_u {
+ unsigned long v;
+ struct uvh_rh_gam_alias210_redirect_config_0_mmr_s {
+ unsigned long rsvd_0_23 : 24; /* */
+ unsigned long dest_base : 22; /* RW */
+ unsigned long rsvd_46_63: 18; /* */
+ } s;
+};
+
+/* ========================================================================= */
+/* UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_1_MMR */
+/* ========================================================================= */
+#define UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_1_MMR 0x16000e0UL
+
+#define UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_1_MMR_DEST_BASE_SHFT 24
+#define UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_1_MMR_DEST_BASE_MASK 0x00003fffff000000UL
+
+union uvh_rh_gam_alias210_redirect_config_1_mmr_u {
+ unsigned long v;
+ struct uvh_rh_gam_alias210_redirect_config_1_mmr_s {
+ unsigned long rsvd_0_23 : 24; /* */
+ unsigned long dest_base : 22; /* RW */
+ unsigned long rsvd_46_63: 18; /* */
+ } s;
+};
+
+/* ========================================================================= */
+/* UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_2_MMR */
+/* ========================================================================= */
+#define UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_2_MMR 0x16000f0UL
+
+#define UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_2_MMR_DEST_BASE_SHFT 24
+#define UVH_RH_GAM_ALIAS210_REDIRECT_CONFIG_2_MMR_DEST_BASE_MASK 0x00003fffff000000UL
+
+union uvh_rh_gam_alias210_redirect_config_2_mmr_u {
+ unsigned long v;
+ struct uvh_rh_gam_alias210_redirect_config_2_mmr_s {
+ unsigned long rsvd_0_23 : 24; /* */
+ unsigned long dest_base : 22; /* RW */
+ unsigned long rsvd_46_63: 18; /* */
+ } s;
+};
+
+/* ========================================================================= */
/* UVH_RH_GAM_GRU_OVERLAY_CONFIG_MMR */
/* ========================================================================= */
#define UVH_RH_GAM_GRU_OVERLAY_CONFIG_MMR 0x1600010UL
@@ -369,5 +798,77 @@ union uvh_si_addr_map_config_u {
} s;
};
+/* ========================================================================= */
+/* UVH_SI_ALIAS0_OVERLAY_CONFIG */
+/* ========================================================================= */
+#define UVH_SI_ALIAS0_OVERLAY_CONFIG 0xc80008UL
+
+#define UVH_SI_ALIAS0_OVERLAY_CONFIG_BASE_SHFT 24
+#define UVH_SI_ALIAS0_OVERLAY_CONFIG_BASE_MASK 0x00000000ff000000UL
+#define UVH_SI_ALIAS0_OVERLAY_CONFIG_M_ALIAS_SHFT 48
+#define UVH_SI_ALIAS0_OVERLAY_CONFIG_M_ALIAS_MASK 0x001f000000000000UL
+#define UVH_SI_ALIAS0_OVERLAY_CONFIG_ENABLE_SHFT 63
+#define UVH_SI_ALIAS0_OVERLAY_CONFIG_ENABLE_MASK 0x8000000000000000UL
+
+union uvh_si_alias0_overlay_config_u {
+ unsigned long v;
+ struct uvh_si_alias0_overlay_config_s {
+ unsigned long rsvd_0_23: 24; /* */
+ unsigned long base : 8; /* RW */
+ unsigned long rsvd_32_47: 16; /* */
+ unsigned long m_alias : 5; /* RW */
+ unsigned long rsvd_53_62: 10; /* */
+ unsigned long enable : 1; /* RW */
+ } s;
+};
+
+/* ========================================================================= */
+/* UVH_SI_ALIAS1_OVERLAY_CONFIG */
+/* ========================================================================= */
+#define UVH_SI_ALIAS1_OVERLAY_CONFIG 0xc80010UL
+
+#define UVH_SI_ALIAS1_OVERLAY_CONFIG_BASE_SHFT 24
+#define UVH_SI_ALIAS1_OVERLAY_CONFIG_BASE_MASK 0x00000000ff000000UL
+#define UVH_SI_ALIAS1_OVERLAY_CONFIG_M_ALIAS_SHFT 48
+#define UVH_SI_ALIAS1_OVERLAY_CONFIG_M_ALIAS_MASK 0x001f000000000000UL
+#define UVH_SI_ALIAS1_OVERLAY_CONFIG_ENABLE_SHFT 63
+#define UVH_SI_ALIAS1_OVERLAY_CONFIG_ENABLE_MASK 0x8000000000000000UL
+
+union uvh_si_alias1_overlay_config_u {
+ unsigned long v;
+ struct uvh_si_alias1_overlay_config_s {
+ unsigned long rsvd_0_23: 24; /* */
+ unsigned long base : 8; /* RW */
+ unsigned long rsvd_32_47: 16; /* */
+ unsigned long m_alias : 5; /* RW */
+ unsigned long rsvd_53_62: 10; /* */
+ unsigned long enable : 1; /* RW */
+ } s;
+};
+
+/* ========================================================================= */
+/* UVH_SI_ALIAS2_OVERLAY_CONFIG */
+/* ========================================================================= */
+#define UVH_SI_ALIAS2_OVERLAY_CONFIG 0xc80018UL
+
+#define UVH_SI_ALIAS2_OVERLAY_CONFIG_BASE_SHFT 24
+#define UVH_SI_ALIAS2_OVERLAY_CONFIG_BASE_MASK 0x00000000ff000000UL
+#define UVH_SI_ALIAS2_OVERLAY_CONFIG_M_ALIAS_SHFT 48
+#define UVH_SI_ALIAS2_OVERLAY_CONFIG_M_ALIAS_MASK 0x001f000000000000UL
+#define UVH_SI_ALIAS2_OVERLAY_CONFIG_ENABLE_SHFT 63
+#define UVH_SI_ALIAS2_OVERLAY_CONFIG_ENABLE_MASK 0x8000000000000000UL
+
+union uvh_si_alias2_overlay_config_u {
+ unsigned long v;
+ struct uvh_si_alias2_overlay_config_s {
+ unsigned long rsvd_0_23: 24; /* */
+ unsigned long base : 8; /* RW */
+ unsigned long rsvd_32_47: 16; /* */
+ unsigned long m_alias : 5; /* RW */
+ unsigned long rsvd_53_62: 10; /* */
+ unsigned long enable : 1; /* RW */
+ } s;
+};
+
#endif /* __ASM_X86_UV_MMRS__ */
diff --git a/include/asm-x86/vm86.h b/include/asm-x86/vm86.h
index 074b357146df..5ce351325e01 100644
--- a/include/asm-x86/vm86.h
+++ b/include/asm-x86/vm86.h
@@ -14,12 +14,6 @@
#include <asm/processor-flags.h>
-#ifdef CONFIG_VM86
-#define X86_VM_MASK X86_EFLAGS_VM
-#else
-#define X86_VM_MASK 0 /* No VM86 support */
-#endif
-
#define BIOSSEG 0x0f000
#define CPU_086 0
@@ -121,7 +115,6 @@ struct vm86plus_info_struct {
unsigned long is_vm86pus:1; /* for vm86 internal use */
unsigned char vm86dbg_intxxtab[32]; /* for debugger */
};
-
struct vm86plus_struct {
struct vm86_regs regs;
unsigned long flags;
@@ -133,6 +126,9 @@ struct vm86plus_struct {
};
#ifdef __KERNEL__
+
+#include <asm/ptrace.h>
+
/*
* This is the (kernel) stack-layout when we have done a "SAVE_ALL" from vm86
* mode - the main change is that the old segment descriptors aren't
@@ -141,7 +137,6 @@ struct vm86plus_struct {
* at the end of the structure. Look at ptrace.h to see the "normal"
* setup. For user space layout see 'struct vm86_regs' above.
*/
-#include <asm/ptrace.h>
struct kernel_vm86_regs {
/*
diff --git a/include/asm-x86/vsyscall.h b/include/asm-x86/vsyscall.h
index 17b3700949bf..6b66ff905af0 100644
--- a/include/asm-x86/vsyscall.h
+++ b/include/asm-x86/vsyscall.h
@@ -24,7 +24,8 @@ enum vsyscall_num {
((unused, __section__ (".vsyscall_gtod_data"),aligned(16)))
#define __section_vsyscall_clock __attribute__ \
((unused, __section__ (".vsyscall_clock"),aligned(16)))
-#define __vsyscall_fn __attribute__ ((unused,__section__(".vsyscall_fn")))
+#define __vsyscall_fn \
+ __attribute__ ((unused, __section__(".vsyscall_fn"))) notrace
#define VGETCPU_RDTSCP 1
#define VGETCPU_LSL 2
diff --git a/include/asm-x86/xen/hypercall.h b/include/asm-x86/xen/hypercall.h
index c2ccd997ed35..2a4f9b41d684 100644
--- a/include/asm-x86/xen/hypercall.h
+++ b/include/asm-x86/xen/hypercall.h
@@ -176,9 +176,9 @@ HYPERVISOR_fpu_taskswitch(int set)
}
static inline int
-HYPERVISOR_sched_op(int cmd, unsigned long arg)
+HYPERVISOR_sched_op(int cmd, void *arg)
{
- return _hypercall2(int, sched_op, cmd, arg);
+ return _hypercall2(int, sched_op_new, cmd, arg);
}
static inline long
@@ -315,6 +315,13 @@ HYPERVISOR_nmi_op(unsigned long op, unsigned long arg)
}
static inline void
+MULTI_fpu_taskswitch(struct multicall_entry *mcl, int set)
+{
+ mcl->op = __HYPERVISOR_fpu_taskswitch;
+ mcl->args[0] = set;
+}
+
+static inline void
MULTI_update_va_mapping(struct multicall_entry *mcl, unsigned long va,
pte_t new_val, unsigned long flags)
{
diff --git a/include/asm-x86/xen/page.h b/include/asm-x86/xen/page.h
index baf3a4dce28c..377c04591c15 100644
--- a/include/asm-x86/xen/page.h
+++ b/include/asm-x86/xen/page.h
@@ -26,15 +26,20 @@ typedef struct xpaddr {
#define FOREIGN_FRAME_BIT (1UL<<31)
#define FOREIGN_FRAME(m) ((m) | FOREIGN_FRAME_BIT)
-extern unsigned long *phys_to_machine_mapping;
+/* Maximum amount of memory we can handle in a domain in pages */
+#define MAX_DOMAIN_PAGES \
+ ((unsigned long)((u64)CONFIG_XEN_MAX_DOMAIN_MEMORY * 1024 * 1024 * 1024 / PAGE_SIZE))
+
+
+extern unsigned long get_phys_to_machine(unsigned long pfn);
+extern void set_phys_to_machine(unsigned long pfn, unsigned long mfn);
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;
+ return get_phys_to_machine(pfn) & ~FOREIGN_FRAME_BIT;
}
static inline int phys_to_machine_mapping_valid(unsigned long pfn)
@@ -42,7 +47,7 @@ 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);
+ return get_phys_to_machine(pfn) != INVALID_P2M_ENTRY;
}
static inline unsigned long mfn_to_pfn(unsigned long mfn)
@@ -106,20 +111,12 @@ static inline unsigned long mfn_to_local_pfn(unsigned long mfn)
unsigned long pfn = mfn_to_pfn(mfn);
if ((pfn < max_mapnr)
&& !xen_feature(XENFEAT_auto_translated_physmap)
- && (phys_to_machine_mapping[pfn] != mfn))
+ && (get_phys_to_machine(pfn) != mfn))
return max_mapnr; /* force !pfn_valid() */
+ /* XXX fixme; not true with sparsemem */
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))))
@@ -150,13 +147,9 @@ static inline pte_t __pte_ma(pteval_t x)
return (pte_t) { .pte = x };
}
-#ifdef CONFIG_X86_PAE
#define pmd_val_ma(v) ((v).pmd)
#define pud_val_ma(v) ((v).pgd.pgd)
#define __pmd_ma(x) ((pmd_t) { (x) } )
-#else /* !X86_PAE */
-#define pmd_val_ma(v) ((v).pud.pgd.pgd)
-#endif /* CONFIG_X86_PAE */
#define pgd_val_ma(x) ((x).pgd)
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 41f7ce7edd7a..33adcf91ef41 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -234,6 +234,9 @@ int acpi_check_region(resource_size_t start, resource_size_t n,
int acpi_check_mem_region(resource_size_t start, resource_size_t n,
const char *name);
+#ifdef CONFIG_PM_SLEEP
+void __init acpi_old_suspend_ordering(void);
+#endif /* CONFIG_PM_SLEEP */
#else /* CONFIG_ACPI */
static inline int early_acpi_boot_init(void)
diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h
index 1abfe664c444..a08c33a26ca9 100644
--- a/include/linux/bitmap.h
+++ b/include/linux/bitmap.h
@@ -129,6 +129,7 @@ extern void bitmap_fold(unsigned long *dst, const unsigned long *orig,
extern int bitmap_find_free_region(unsigned long *bitmap, int bits, int order);
extern void bitmap_release_region(unsigned long *bitmap, int pos, int order);
extern int bitmap_allocate_region(unsigned long *bitmap, int pos, int order);
+extern void bitmap_copy_le(void *dst, const unsigned long *src, int nbits);
#define BITMAP_LAST_WORD_MASK(nbits) \
( \
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index d2a1b71e93c3..1171abd7eb17 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -623,7 +623,6 @@ extern void generic_make_request(struct bio *bio);
extern void blk_rq_init(struct request_queue *q, struct request *rq);
extern void blk_put_request(struct request *);
extern void __blk_put_request(struct request_queue *, struct request *);
-extern void blk_end_sync_rq(struct request *rq, int error);
extern struct request *blk_get_request(struct request_queue *, int, gfp_t);
extern void blk_insert_request(struct request_queue *, struct request *, int, void *);
extern void blk_requeue_request(struct request_queue *, struct request *);
diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h
index 6a5dbdc8a7dc..686895bacd9d 100644
--- a/include/linux/bootmem.h
+++ b/include/linux/bootmem.h
@@ -94,7 +94,7 @@ extern unsigned long init_bootmem_node(pg_data_t *pgdat,
unsigned long freepfn,
unsigned long startpfn,
unsigned long endpfn);
-extern void reserve_bootmem_node(pg_data_t *pgdat,
+extern int reserve_bootmem_node(pg_data_t *pgdat,
unsigned long physaddr,
unsigned long size,
int flags);
diff --git a/include/linux/byteorder/bitfields.h b/include/linux/byteorder/bitfields.h
new file mode 100644
index 000000000000..5a8c2f8a46b1
--- /dev/null
+++ b/include/linux/byteorder/bitfields.h
@@ -0,0 +1,114 @@
+/*
+ * Helpers for defining little-endian bitfields
+ *
+ * Copyright (C) 2007 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef __LINUX__BYTEORDER__BITFIELDS_H__
+#define __LINUX__BYTEORDER__BITFIELDS_H__
+
+#include <asm/byteorder.h>
+
+/*
+ * Declare Little Endian bitfields
+ *
+ * These macros are used to switch the order of the bitfields when
+ * packing with Little Endian in mind (ie: for mapping to a hw type
+ * that you have to send or read from a device).
+ *
+ * NOTE: When using multibyte bitfields, you need to convert the data
+ * from Little Endian to CPU before you can access the bitfield
+ * (to make it simpler):
+ *
+ * union something {
+ * le16 value;
+ * DECL_BF_LE3(
+ * u16 bf0:3,
+ * u16 bf1:10,
+ * u16 bf2:3
+ * ) __attribute__((packed));
+ * };
+ *
+ * ...
+ *
+ * union something s;
+ *
+ * s.value = le16_to_cpu(something LE read from hw);
+ * frame_count = s.bf1;
+ *
+ */
+#ifdef __LITTLE_ENDIAN_BITFIELD
+#define DECL_BF_LE2(a, b) struct { a; b; }
+#define DECL_BF_LE3(a, b, c) struct { a; b; c; }
+#define DECL_BF_LE4(a, b, c, d) struct { a; b; c; d ; }
+#define DECL_BF_LE5(a, b, c, d, e) struct { a; b; c; d; e; }
+#define DECL_BF_LE6(a, b, c, d, e, f) struct { a; b; c; d; e; f; }
+#define DECL_BF_LE7(a, b, c, d, e, f, g) struct { a; b; c; d; e; f; g; }
+#define DECL_BF_LE8(a, b, c, d, e, f, g, h) struct { a; b; c; d; e; f; g; h; }
+#define DECL_BF_LE9(a, b, c, d, e, f, g, h, i) \
+ struct { a; b; c; d; e; f; g; h; i; }
+#define DECL_BF_LE10(a, b, c, d, e, f, g, h, i, j) \
+ struct { a; b; c; d; e; f; g; h; i; j; }
+#define DECL_BF_LE11(a, b, c, d, e, f, g, h, i, j, k) \
+ struct { a; b; c; d; e; f; g; h; i; j; k; }
+#define DECL_BF_LE12(a, b, c, d, e, f, g, h, i, j, k, l) \
+ struct { a; b; c; d; e; f; g; h; i; j; k; l; }
+#define DECL_BF_LE13(a, b, c, d, e, f, g, h, i, j, k, l, m) \
+ struct { a; b; c; d; e; f; g; h; i; j; k; l; m; }
+#define DECL_BF_LE14(a, b, c, d, e, f, g, h, i, j, k, l, m, n) \
+ struct { a; b; c; d; e; f; g; h; i; j; k; l; m; n; }
+#define DECL_BF_LE15(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) \
+ struct { a; b; c; d; e; f; g; h; i; j; k; l; m; n; o; }
+#define DECL_BF_LE16(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
+ struct { a; b; c; d; e; f; g; h; i; j; k; l; m; n; o; p; }
+
+#else
+#ifdef __BIG_ENDIAN_BITFIELD
+
+#define DECL_BF_LE2(a, b) struct { b; a; }
+#define DECL_BF_LE3(a, b, c) struct { c; b; a; }
+#define DECL_BF_LE4(a, b, c, d) struct { d; c; b; a; }
+#define DECL_BF_LE5(a, b, c, d, e) struct { e; d; c; b; a; }
+#define DECL_BF_LE6(a, b, c, d, e, f) struct { f; e; d; c; b; a; }
+#define DECL_BF_LE7(a, b, c, d, e, f, g) struct { g; f; e; d; c; b; a; }
+#define DECL_BF_LE8(a, b, c, d, e, f, g, h) struct { h; g; f; e; d; c; b; a; }
+#define DECL_BF_LE9(a, b, c, d, e, f, g, h, i) \
+ struct { i; h; g; f; e; d; c; b; a; }
+#define DECL_BF_LE10(a, b, c, d, e, f, g, h, i, j) \
+ struct { j; i; h; g; f; e; d; c; b; a; }
+#define DECL_BF_LE11(a, b, c, d, e, f, g, h, i, j, k) \
+ struct { k; j; i; h; g; f; e; d; c; b; a; }
+#define DECL_BF_LE12(a, b, c, d, e, f, g, h, i, j, k, l) \
+ struct { l; k; j; i; h; g; f; e; d; c; b; a; }
+#define DECL_BF_LE13(a, b, c, d, e, f, g, h, i, j, k, l, m) \
+ struct { m; l; k; j; i; h; g; f; e; d; c; b; a; }
+#define DECL_BF_LE14(a, b, c, d, e, f, g, h, i, j, k, l, m, n) \
+ struct { n; m; l; k; j; i; h; g; f; e; d; c; b; a; }
+#define DECL_BF_LE15(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) \
+ struct { o; n; m; l; k; j; i; h; g; f; e; d; c; b; a; }
+#define DECL_BF_LE16(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
+ struct { p; o; n; m; l; k; j; i; h; g; f; e; d; c; b; a; }
+
+#else
+
+#error Unknown endianess for Little Endian bitfield definition
+
+#endif
+#endif
+
+#endif /* #ifndef __LINUX__BYTEORDER__BITFIELDS_H__ */
diff --git a/include/linux/console.h b/include/linux/console.h
index a4f27fbdf549..248e6e3b9b73 100644
--- a/include/linux/console.h
+++ b/include/linux/console.h
@@ -108,6 +108,8 @@ struct console {
struct console *next;
};
+extern int console_set_on_cmdline;
+
extern int add_preferred_console(char *name, int idx, char *options);
extern int update_console_cmdline(char *name, int idx, char *name_new, int idx_new, char *options);
extern void register_console(struct console *);
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index 51e6b1e520e6..dcf77fa826b5 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -82,6 +82,7 @@ struct cpuidle_state_kobj {
};
struct cpuidle_device {
+ unsigned int registered:1;
unsigned int enabled:1;
unsigned int cpu;
diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h
index 5df3db58fcc6..80226e776143 100644
--- a/include/linux/cpumask.h
+++ b/include/linux/cpumask.h
@@ -17,6 +17,20 @@
* For details of cpus_onto(), see bitmap_onto in lib/bitmap.c.
* For details of cpus_fold(), see bitmap_fold in lib/bitmap.c.
*
+ * . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+ * Note: The alternate operations with the suffix "_nr" are used
+ * to limit the range of the loop to nr_cpu_ids instead of
+ * NR_CPUS when NR_CPUS > 64 for performance reasons.
+ * If NR_CPUS is <= 64 then most assembler bitmask
+ * operators execute faster with a constant range, so
+ * the operator will continue to use NR_CPUS.
+ *
+ * Another consideration is that nr_cpu_ids is initialized
+ * to NR_CPUS and isn't lowered until the possible cpus are
+ * discovered (including any disabled cpus). So early uses
+ * will span the entire range of NR_CPUS.
+ * . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
+ *
* The available cpumask operations are:
*
* void cpu_set(cpu, mask) turn on bit 'cpu' in mask
@@ -38,12 +52,14 @@
* int cpus_empty(mask) Is mask empty (no bits sets)?
* int cpus_full(mask) Is mask full (all bits sets)?
* int cpus_weight(mask) Hamming weigh - number of set bits
+ * int cpus_weight_nr(mask) Same using nr_cpu_ids instead of NR_CPUS
*
* void cpus_shift_right(dst, src, n) Shift right
* void cpus_shift_left(dst, src, n) Shift left
*
* int first_cpu(mask) Number lowest set bit, or NR_CPUS
* int next_cpu(cpu, mask) Next cpu past 'cpu', or NR_CPUS
+ * int next_cpu_nr(cpu, mask) Next cpu past 'cpu', or nr_cpu_ids
*
* cpumask_t cpumask_of_cpu(cpu) Return cpumask with bit 'cpu' set
* CPU_MASK_ALL Initializer - all bits set
@@ -59,7 +75,8 @@
* void cpus_onto(dst, orig, relmap) *dst = orig relative to relmap
* void cpus_fold(dst, orig, sz) dst bits = orig bits mod sz
*
- * for_each_cpu_mask(cpu, mask) for-loop cpu over mask
+ * for_each_cpu_mask(cpu, mask) for-loop cpu over mask using NR_CPUS
+ * for_each_cpu_mask_nr(cpu, mask) for-loop cpu over mask using nr_cpu_ids
*
* int num_online_cpus() Number of online CPUs
* int num_possible_cpus() Number of all possible CPUs
@@ -216,15 +233,6 @@ static inline void __cpus_shift_left(cpumask_t *dstp,
bitmap_shift_left(dstp->bits, srcp->bits, n, nbits);
}
-#ifdef CONFIG_SMP
-int __first_cpu(const cpumask_t *srcp);
-#define first_cpu(src) __first_cpu(&(src))
-int __next_cpu(int n, const cpumask_t *srcp);
-#define next_cpu(n, src) __next_cpu((n), &(src))
-#else
-#define first_cpu(src) ({ (void)(src); 0; })
-#define next_cpu(n, src) ({ (void)(src); 1; })
-#endif
#ifdef CONFIG_HAVE_CPUMASK_OF_CPU_MAP
extern cpumask_t *cpumask_of_cpu_map;
@@ -343,15 +351,48 @@ static inline void __cpus_fold(cpumask_t *dstp, const cpumask_t *origp,
bitmap_fold(dstp->bits, origp->bits, sz, nbits);
}
-#if NR_CPUS > 1
-#define for_each_cpu_mask(cpu, mask) \
- for ((cpu) = first_cpu(mask); \
- (cpu) < NR_CPUS; \
- (cpu) = next_cpu((cpu), (mask)))
-#else /* NR_CPUS == 1 */
-#define for_each_cpu_mask(cpu, mask) \
+#if NR_CPUS == 1
+
+#define nr_cpu_ids 1
+#define first_cpu(src) ({ (void)(src); 0; })
+#define next_cpu(n, src) ({ (void)(src); 1; })
+#define any_online_cpu(mask) 0
+#define for_each_cpu_mask(cpu, mask) \
for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask)
-#endif /* NR_CPUS */
+
+#else /* NR_CPUS > 1 */
+
+extern int nr_cpu_ids;
+int __first_cpu(const cpumask_t *srcp);
+int __next_cpu(int n, const cpumask_t *srcp);
+int __any_online_cpu(const cpumask_t *mask);
+
+#define first_cpu(src) __first_cpu(&(src))
+#define next_cpu(n, src) __next_cpu((n), &(src))
+#define any_online_cpu(mask) __any_online_cpu(&(mask))
+#define for_each_cpu_mask(cpu, mask) \
+ for ((cpu) = -1; \
+ (cpu) = next_cpu((cpu), (mask)), \
+ (cpu) < NR_CPUS; )
+#endif
+
+#if NR_CPUS <= 64
+
+#define next_cpu_nr(n, src) next_cpu(n, src)
+#define cpus_weight_nr(cpumask) cpus_weight(cpumask)
+#define for_each_cpu_mask_nr(cpu, mask) for_each_cpu_mask(cpu, mask)
+
+#else /* NR_CPUS > 64 */
+
+int __next_cpu_nr(int n, const cpumask_t *srcp);
+#define next_cpu_nr(n, src) __next_cpu_nr((n), &(src))
+#define cpus_weight_nr(cpumask) __cpus_weight(&(cpumask), nr_cpu_ids)
+#define for_each_cpu_mask_nr(cpu, mask) \
+ for ((cpu) = -1; \
+ (cpu) = next_cpu_nr((cpu), (mask)), \
+ (cpu) < nr_cpu_ids; )
+
+#endif /* NR_CPUS > 64 */
/*
* The following particular system cpumasks and operations manage
@@ -414,9 +455,9 @@ extern cpumask_t cpu_online_map;
extern cpumask_t cpu_present_map;
#if NR_CPUS > 1
-#define num_online_cpus() cpus_weight(cpu_online_map)
-#define num_possible_cpus() cpus_weight(cpu_possible_map)
-#define num_present_cpus() cpus_weight(cpu_present_map)
+#define num_online_cpus() cpus_weight_nr(cpu_online_map)
+#define num_possible_cpus() cpus_weight_nr(cpu_possible_map)
+#define num_present_cpus() cpus_weight_nr(cpu_present_map)
#define cpu_online(cpu) cpu_isset((cpu), cpu_online_map)
#define cpu_possible(cpu) cpu_isset((cpu), cpu_possible_map)
#define cpu_present(cpu) cpu_isset((cpu), cpu_present_map)
@@ -431,17 +472,8 @@ extern cpumask_t cpu_present_map;
#define cpu_is_offline(cpu) unlikely(!cpu_online(cpu))
-#ifdef CONFIG_SMP
-extern int nr_cpu_ids;
-#define any_online_cpu(mask) __any_online_cpu(&(mask))
-int __any_online_cpu(const cpumask_t *mask);
-#else
-#define nr_cpu_ids 1
-#define any_online_cpu(mask) 0
-#endif
-
-#define for_each_possible_cpu(cpu) for_each_cpu_mask((cpu), cpu_possible_map)
-#define for_each_online_cpu(cpu) for_each_cpu_mask((cpu), cpu_online_map)
-#define for_each_present_cpu(cpu) for_each_cpu_mask((cpu), cpu_present_map)
+#define for_each_possible_cpu(cpu) for_each_cpu_mask_nr((cpu), cpu_possible_map)
+#define for_each_online_cpu(cpu) for_each_cpu_mask_nr((cpu), cpu_online_map)
+#define for_each_present_cpu(cpu) for_each_cpu_mask_nr((cpu), cpu_present_map)
#endif /* __LINUX_CPUMASK_H */
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 2a6639407c80..7f16b96e7660 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -3,6 +3,7 @@
#include <asm/atomic.h>
#include <linux/list.h>
+#include <linux/rculist.h>
#include <linux/spinlock.h>
#include <linux/cache.h>
#include <linux/rcupdate.h>
@@ -229,6 +230,7 @@ extern void d_delete(struct dentry *);
extern struct dentry * d_alloc(struct dentry *, const struct qstr *);
extern struct dentry * d_alloc_anon(struct inode *);
extern struct dentry * d_splice_alias(struct inode *, struct dentry *);
+extern struct dentry * d_add_ci(struct inode *, struct dentry *, struct qstr *);
extern void shrink_dcache_sb(struct super_block *);
extern void shrink_dcache_parent(struct dentry *);
extern void shrink_dcache_for_umount(struct super_block *);
diff --git a/include/linux/device.h b/include/linux/device.h
index 6a2d04c011bc..ab755debf683 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -16,6 +16,7 @@
#include <linux/kobject.h>
#include <linux/klist.h>
#include <linux/list.h>
+#include <linux/lockdep.h>
#include <linux/compiler.h>
#include <linux/types.h>
#include <linux/module.h>
@@ -24,17 +25,13 @@
#include <asm/atomic.h>
#include <asm/device.h>
-#define DEVICE_NAME_SIZE 50
-/* DEVICE_NAME_HALF is really less than half to accommodate slop */
-#define DEVICE_NAME_HALF __stringify(20)
-#define DEVICE_ID_SIZE 32
-#define BUS_ID_SIZE KOBJ_NAME_LEN
-
+#define BUS_ID_SIZE 20
struct device;
struct device_driver;
struct driver_private;
struct class;
+struct class_private;
struct bus_type;
struct bus_type_private;
@@ -68,6 +65,8 @@ struct bus_type {
int (*resume_early)(struct device *dev);
int (*resume)(struct device *dev);
+ struct pm_ext_ops *pm;
+
struct bus_type_private *p;
};
@@ -131,6 +130,8 @@ struct device_driver {
int (*resume) (struct device *dev);
struct attribute_group **groups;
+ struct pm_ops *pm;
+
struct driver_private *p;
};
@@ -182,13 +183,9 @@ struct class {
const char *name;
struct module *owner;
- struct kset subsys;
- struct list_head devices;
- struct list_head interfaces;
- struct kset class_dirs;
- struct semaphore sem; /* locks children, devices, interfaces */
struct class_attribute *class_attrs;
struct device_attribute *dev_attrs;
+ struct kobject *dev_kobj;
int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
@@ -197,13 +194,30 @@ struct class {
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
+
+ struct pm_ops *pm;
+ struct class_private *p;
};
-extern int __must_check class_register(struct class *class);
+extern struct kobject *sysfs_dev_block_kobj;
+extern struct kobject *sysfs_dev_char_kobj;
+extern int __must_check __class_register(struct class *class,
+ struct lock_class_key *key);
extern void class_unregister(struct class *class);
-extern int class_for_each_device(struct class *class, void *data,
+
+/* This is a #define to keep the compiler from merging different
+ * instances of the __key variable */
+#define class_register(class) \
+({ \
+ static struct lock_class_key __key; \
+ __class_register(class, &__key); \
+})
+
+extern int class_for_each_device(struct class *class, struct device *start,
+ void *data,
int (*fn)(struct device *dev, void *data));
-extern struct device *class_find_device(struct class *class, void *data,
+extern struct device *class_find_device(struct class *class,
+ struct device *start, void *data,
int (*match)(struct device *, void *));
struct class_attribute {
@@ -231,9 +245,19 @@ struct class_interface {
extern int __must_check class_interface_register(struct class_interface *);
extern void class_interface_unregister(struct class_interface *);
-extern struct class *class_create(struct module *owner, const char *name);
+extern struct class * __must_check __class_create(struct module *owner,
+ const char *name,
+ struct lock_class_key *key);
extern void class_destroy(struct class *cls);
+/* This is a #define to keep the compiler from merging different
+ * instances of the __key variable */
+#define class_create(owner, name) \
+({ \
+ static struct lock_class_key __key; \
+ __class_create(owner, name, &__key); \
+})
+
/*
* The type of device, "struct device" is embedded in. A class
* or bus can contain devices of different types
@@ -248,8 +272,11 @@ struct device_type {
struct attribute_group **groups;
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
void (*release)(struct device *dev);
+
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
+
+ struct pm_ops *pm;
};
/* interface for exporting device attributes */
@@ -458,9 +485,6 @@ extern struct device *device_create_vargs(struct class *cls,
void *drvdata,
const char *fmt,
va_list vargs);
-extern struct device *device_create(struct class *cls, struct device *parent,
- dev_t devt, const char *fmt, ...)
- __attribute__((format(printf, 4, 5)));
extern struct device *device_create_drvdata(struct class *cls,
struct device *parent,
dev_t devt,
diff --git a/include/linux/efi.h b/include/linux/efi.h
index a5f359a7ad0e..807373d467f7 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -287,7 +287,6 @@ efi_guid_unparse(efi_guid_t *guid, char *out)
extern void efi_init (void);
extern void *efi_get_pal_addr (void);
extern void efi_map_pal_code (void);
-extern void efi_map_memmap(void);
extern void efi_memmap_walk (efi_freemem_callback_t callback, void *arg);
extern void efi_gettimeofday (struct timespec *ts);
extern void efi_enter_virtual_mode (void); /* switch EFI to virtual mode, if possible */
@@ -295,14 +294,11 @@ extern u64 efi_get_iobase (void);
extern u32 efi_mem_type (unsigned long phys_addr);
extern u64 efi_mem_attributes (unsigned long phys_addr);
extern u64 efi_mem_attribute (unsigned long phys_addr, unsigned long size);
-extern int efi_mem_attribute_range (unsigned long phys_addr, unsigned long size,
- u64 attr);
extern int __init efi_uart_console_only (void);
extern void efi_initialize_iomem_resources(struct resource *code_resource,
struct resource *data_resource, struct resource *bss_resource);
extern unsigned long efi_get_time(void);
extern int efi_set_rtc_mmss(unsigned long nowtime);
-extern int is_available_memory(efi_memory_desc_t * md);
extern struct efi_memory_map memmap;
/**
diff --git a/include/linux/eisa.h b/include/linux/eisa.h
index fe806b6f030d..e61c0be2a459 100644
--- a/include/linux/eisa.h
+++ b/include/linux/eisa.h
@@ -40,7 +40,7 @@ struct eisa_device {
u64 dma_mask;
struct device dev; /* generic device */
#ifdef CONFIG_EISA_NAMES
- char pretty_name[DEVICE_NAME_SIZE];
+ char pretty_name[50];
#endif
};
diff --git a/include/linux/firmware.h b/include/linux/firmware.h
index 4d10c7328d2d..5e73f3f896e8 100644
--- a/include/linux/firmware.h
+++ b/include/linux/firmware.h
@@ -8,7 +8,7 @@
struct firmware {
size_t size;
- u8 *data;
+ const u8 *data;
};
struct device;
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
new file mode 100644
index 000000000000..20e14d0093c7
--- /dev/null
+++ b/include/linux/ftrace.h
@@ -0,0 +1,140 @@
+#ifndef _LINUX_FTRACE_H
+#define _LINUX_FTRACE_H
+
+#ifdef CONFIG_FTRACE
+
+#include <linux/linkage.h>
+#include <linux/fs.h>
+
+extern int ftrace_enabled;
+extern int
+ftrace_enable_sysctl(struct ctl_table *table, int write,
+ struct file *filp, void __user *buffer, size_t *lenp,
+ loff_t *ppos);
+
+typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip);
+
+struct ftrace_ops {
+ ftrace_func_t func;
+ struct ftrace_ops *next;
+};
+
+/*
+ * The ftrace_ops must be a static and should also
+ * be read_mostly. These functions do modify read_mostly variables
+ * so use them sparely. Never free an ftrace_op or modify the
+ * next pointer after it has been registered. Even after unregistering
+ * it, the next pointer may still be used internally.
+ */
+int register_ftrace_function(struct ftrace_ops *ops);
+int unregister_ftrace_function(struct ftrace_ops *ops);
+void clear_ftrace_function(void);
+
+extern void ftrace_stub(unsigned long a0, unsigned long a1);
+extern void mcount(void);
+
+#else /* !CONFIG_FTRACE */
+# define register_ftrace_function(ops) do { } while (0)
+# define unregister_ftrace_function(ops) do { } while (0)
+# define clear_ftrace_function(ops) do { } while (0)
+#endif /* CONFIG_FTRACE */
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+# define FTRACE_HASHBITS 10
+# define FTRACE_HASHSIZE (1<<FTRACE_HASHBITS)
+
+enum {
+ FTRACE_FL_FREE = (1 << 0),
+ FTRACE_FL_FAILED = (1 << 1),
+ FTRACE_FL_FILTER = (1 << 2),
+ FTRACE_FL_ENABLED = (1 << 3),
+ FTRACE_FL_NOTRACE = (1 << 4),
+ FTRACE_FL_CONVERTED = (1 << 5),
+};
+
+struct dyn_ftrace {
+ struct hlist_node node;
+ unsigned long ip;
+ unsigned long flags;
+};
+
+int ftrace_force_update(void);
+void ftrace_set_filter(unsigned char *buf, int len, int reset);
+
+/* defined in arch */
+extern int ftrace_ip_converted(unsigned long ip);
+extern unsigned char *ftrace_nop_replace(void);
+extern unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr);
+extern int ftrace_dyn_arch_init(void *data);
+extern int ftrace_mcount_set(unsigned long *data);
+extern int ftrace_modify_code(unsigned long ip, unsigned char *old_code,
+ unsigned char *new_code);
+extern int ftrace_update_ftrace_func(ftrace_func_t func);
+extern void ftrace_caller(void);
+extern void ftrace_call(void);
+extern void mcount_call(void);
+
+void ftrace_disable_daemon(void);
+void ftrace_enable_daemon(void);
+
+#else
+# define ftrace_force_update() ({ 0; })
+# define ftrace_set_filter(buf, len, reset) do { } while (0)
+# define ftrace_disable_daemon() do { } while (0)
+# define ftrace_enable_daemon() do { } while (0)
+#endif
+
+/* totally disable ftrace - can not re-enable after this */
+void ftrace_kill(void);
+
+static inline void tracer_disable(void)
+{
+#ifdef CONFIG_FTRACE
+ ftrace_enabled = 0;
+#endif
+}
+
+#ifdef CONFIG_FRAME_POINTER
+/* TODO: need to fix this for ARM */
+# define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0))
+# define CALLER_ADDR1 ((unsigned long)__builtin_return_address(1))
+# define CALLER_ADDR2 ((unsigned long)__builtin_return_address(2))
+# define CALLER_ADDR3 ((unsigned long)__builtin_return_address(3))
+# define CALLER_ADDR4 ((unsigned long)__builtin_return_address(4))
+# define CALLER_ADDR5 ((unsigned long)__builtin_return_address(5))
+# define CALLER_ADDR6 ((unsigned long)__builtin_return_address(6))
+#else
+# define CALLER_ADDR0 ((unsigned long)__builtin_return_address(0))
+# define CALLER_ADDR1 0UL
+# define CALLER_ADDR2 0UL
+# define CALLER_ADDR3 0UL
+# define CALLER_ADDR4 0UL
+# define CALLER_ADDR5 0UL
+# define CALLER_ADDR6 0UL
+#endif
+
+#ifdef CONFIG_IRQSOFF_TRACER
+ extern void time_hardirqs_on(unsigned long a0, unsigned long a1);
+ extern void time_hardirqs_off(unsigned long a0, unsigned long a1);
+#else
+# define time_hardirqs_on(a0, a1) do { } while (0)
+# define time_hardirqs_off(a0, a1) do { } while (0)
+#endif
+
+#ifdef CONFIG_PREEMPT_TRACER
+ extern void trace_preempt_on(unsigned long a0, unsigned long a1);
+ extern void trace_preempt_off(unsigned long a0, unsigned long a1);
+#else
+# define trace_preempt_on(a0, a1) do { } while (0)
+# define trace_preempt_off(a0, a1) do { } while (0)
+#endif
+
+#ifdef CONFIG_TRACING
+extern void
+ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3);
+#else
+static inline void
+ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3) { }
+#endif
+
+#endif /* _LINUX_FTRACE_H */
diff --git a/include/linux/hpet.h b/include/linux/hpet.h
index 2dc29ce6c8e4..6d2626b63a9a 100644
--- a/include/linux/hpet.h
+++ b/include/linux/hpet.h
@@ -37,6 +37,7 @@ struct hpet {
#define hpet_compare _u1._hpet_compare
#define HPET_MAX_TIMERS (32)
+#define HPET_MAX_IRQ (32)
/*
* HPET general capabilities register
@@ -64,7 +65,7 @@ struct hpet {
*/
#define Tn_INT_ROUTE_CAP_MASK (0xffffffff00000000ULL)
-#define Tn_INI_ROUTE_CAP_SHIFT (32UL)
+#define Tn_INT_ROUTE_CAP_SHIFT (32UL)
#define Tn_FSB_INT_DELCAP_MASK (0x8000UL)
#define Tn_FSB_INT_DELCAP_SHIFT (15)
#define Tn_FSB_EN_CNF_MASK (0x4000UL)
diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h
index 580acc93903e..988e566d3ed5 100644
--- a/include/linux/i2c-id.h
+++ b/include/linux/i2c-id.h
@@ -111,7 +111,6 @@
#define I2C_HW_B_RIVA 0x010010 /* Riva based graphics cards */
#define I2C_HW_B_IOC 0x010011 /* IOC bit-wiggling */
#define I2C_HW_B_IXP2000 0x010016 /* GPIO on IXP2000 systems */
-#define I2C_HW_B_S3VIA 0x010018 /* S3Via ProSavage adapter */
#define I2C_HW_B_ZR36067 0x010019 /* Zoran-36057/36067 based boards */
#define I2C_HW_B_PCILYNX 0x01001a /* TI PCILynx I2C adapter */
#define I2C_HW_B_CX2388x 0x01001b /* connexant 2388x based tv cards */
diff --git a/include/linux/i2c.h b/include/linux/i2c.h
index fb9af6a0fe9c..0e332ed991e8 100644
--- a/include/linux/i2c.h
+++ b/include/linux/i2c.h
@@ -69,9 +69,8 @@ extern s32 i2c_smbus_xfer (struct i2c_adapter * adapter, u16 addr,
union i2c_smbus_data * data);
/* Now follow the 'nice' access routines. These also document the calling
- conventions of smbus_access. */
+ conventions of i2c_smbus_xfer. */
-extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value);
extern s32 i2c_smbus_read_byte(struct i2c_client * client);
extern s32 i2c_smbus_write_byte(struct i2c_client * client, u8 value);
extern s32 i2c_smbus_read_byte_data(struct i2c_client * client, u8 command);
@@ -350,10 +349,11 @@ static inline void i2c_set_adapdata (struct i2c_adapter *dev, void *data)
#define I2C_CLASS_HWMON (1<<0) /* lm_sensors, ... */
#define I2C_CLASS_TV_ANALOG (1<<1) /* bttv + friends */
#define I2C_CLASS_TV_DIGITAL (1<<2) /* dvb cards */
-#define I2C_CLASS_DDC (1<<3) /* i2c-matroxfb ? */
+#define I2C_CLASS_DDC (1<<3) /* DDC bus on graphics adapters */
#define I2C_CLASS_CAM_ANALOG (1<<4) /* camera with analog CCD */
#define I2C_CLASS_CAM_DIGITAL (1<<5) /* most webcams */
#define I2C_CLASS_SOUND (1<<6) /* sound devices */
+#define I2C_CLASS_SPD (1<<7) /* SPD EEPROMs and similar */
#define I2C_CLASS_ALL (UINT_MAX) /* all of the above */
/* i2c_client_address_data is the struct for holding default client
@@ -537,7 +537,7 @@ union i2c_smbus_data {
/* and one more for user-space compatibility */
};
-/* smbus_access read or write markers */
+/* i2c_smbus_xfer read or write markers */
#define I2C_SMBUS_READ 1
#define I2C_SMBUS_WRITE 0
diff --git a/include/linux/ide.h b/include/linux/ide.h
index f8f195c20da2..5ddae9b55b8d 100644
--- a/include/linux/ide.h
+++ b/include/linux/ide.h
@@ -153,7 +153,7 @@ enum { ide_unknown, ide_generic, ide_pci,
ide_qd65xx, ide_umc8672, ide_ht6560b,
ide_rz1000, ide_trm290,
ide_cmd646, ide_cy82c693, ide_4drives,
- ide_pmac, ide_etrax100, ide_acorn,
+ ide_pmac, ide_acorn,
ide_au1xxx, ide_palm3710
};
@@ -171,7 +171,7 @@ typedef struct hw_regs_s {
int irq; /* our irq number */
ide_ack_intr_t *ack_intr; /* acknowledge interrupt */
hwif_chipset_t chipset;
- struct device *dev;
+ struct device *dev, *parent;
} hw_regs_t;
void ide_init_port_data(struct hwif_s *, unsigned int);
@@ -349,7 +349,6 @@ typedef struct ide_drive_s {
u8 wcache; /* status of write cache */
u8 acoustic; /* acoustic management */
u8 media; /* disk, cdrom, tape, floppy, ... */
- u8 ctl; /* "normal" value for Control register */
u8 ready_stat; /* min status value for drive ready */
u8 mult_count; /* current multiple sector setting */
u8 mult_req; /* requested multiple sector setting */
@@ -391,8 +390,8 @@ typedef struct ide_drive_s {
struct ide_port_info;
struct ide_port_ops {
- /* host specific initialization of devices on a port */
- void (*port_init_devs)(struct hwif_s *);
+ /* host specific initialization of a device */
+ void (*init_dev)(ide_drive_t *);
/* routine to program host for PIO mode */
void (*set_pio_mode)(ide_drive_t *, const u8);
/* routine to program host for DMA mode */
@@ -478,7 +477,7 @@ typedef struct hwif_s {
void (*ide_dma_clear_irq)(ide_drive_t *drive);
void (*OUTB)(u8 addr, unsigned long port);
- void (*OUTBSYNC)(ide_drive_t *drive, u8 addr, unsigned long port);
+ void (*OUTBSYNC)(struct hwif_s *hwif, u8 addr, unsigned long port);
u8 (*INB)(unsigned long port);
@@ -517,7 +516,6 @@ typedef struct hwif_s {
unsigned serialized : 1; /* serialized all channel operation */
unsigned sharing_irq: 1; /* 1 = sharing irq with another hwif */
unsigned sg_mapped : 1; /* sg_table and sg_nents are ready */
- unsigned mmio : 1; /* host uses MMIO */
struct device gendev;
struct device *portdev;
@@ -589,12 +587,13 @@ enum {
PC_FLAG_SUPPRESS_ERROR = (1 << 1),
PC_FLAG_WAIT_FOR_DSC = (1 << 2),
PC_FLAG_DMA_OK = (1 << 3),
- PC_FLAG_DMA_RECOMMENDED = (1 << 4),
- PC_FLAG_DMA_IN_PROGRESS = (1 << 5),
- PC_FLAG_DMA_ERROR = (1 << 6),
- PC_FLAG_WRITING = (1 << 7),
+ PC_FLAG_DMA_IN_PROGRESS = (1 << 4),
+ PC_FLAG_DMA_ERROR = (1 << 5),
+ PC_FLAG_WRITING = (1 << 6),
/* command timed out */
- PC_FLAG_TIMEDOUT = (1 << 8),
+ PC_FLAG_TIMEDOUT = (1 << 7),
+ PC_FLAG_ZIP_DRIVE = (1 << 8),
+ PC_FLAG_DRQ_INTERRUPT = (1 << 9),
};
struct ide_atapi_pc {
@@ -627,8 +626,8 @@ struct ide_atapi_pc {
* to change/removal later.
*/
u8 pc_buf[256];
- void (*idefloppy_callback) (ide_drive_t *);
- ide_startstop_t (*idetape_callback) (ide_drive_t *);
+
+ void (*callback)(ide_drive_t *);
/* idetape only */
struct idetape_bh *bh;
@@ -787,22 +786,6 @@ struct ide_driver_s {
int generic_ide_ioctl(ide_drive_t *, struct file *, struct block_device *, unsigned, unsigned long);
-/*
- * ide_hwifs[] is the master data structure used to keep track
- * of just about everything in ide.c. Whenever possible, routines
- * should be using pointers to a drive (ide_drive_t *) or
- * pointers to a hwif (ide_hwif_t *), rather than indexing this
- * structure directly (the allocation/layout may change!).
- *
- */
-#ifndef _IDE_C
-extern ide_hwif_t ide_hwifs[]; /* master data repository */
-#endif
-extern int ide_noacpi;
-extern int ide_acpigtf;
-extern int ide_acpionboot;
-extern int noautodma;
-
extern int ide_vlb_clk;
extern int ide_pci_clk;
@@ -842,25 +825,12 @@ int ide_wait_stat(ide_startstop_t *, ide_drive_t *, u8, u8, unsigned long);
extern ide_startstop_t ide_do_reset (ide_drive_t *);
-extern void ide_init_drive_cmd (struct request *rq);
-
-/*
- * "action" parameter type for ide_do_drive_cmd() below.
- */
-typedef enum {
- ide_wait, /* insert rq at end of list, and wait for it */
- ide_preempt, /* insert rq in front of current request */
- ide_head_wait, /* insert rq in front of current request and wait for it */
- ide_end /* insert rq at end of list, but don't wait for it */
-} ide_action_t;
-
-extern int ide_do_drive_cmd(ide_drive_t *, struct request *, ide_action_t);
+extern void ide_do_drive_cmd(ide_drive_t *, struct request *);
extern void ide_end_drive_cmd(ide_drive_t *, u8, u8);
enum {
IDE_TFLAG_LBA48 = (1 << 0),
- IDE_TFLAG_NO_SELECT_MASK = (1 << 1),
IDE_TFLAG_FLAGGED = (1 << 2),
IDE_TFLAG_OUT_DATA = (1 << 3),
IDE_TFLAG_OUT_HOB_FEATURE = (1 << 4),
@@ -965,11 +935,23 @@ typedef struct ide_task_s {
void ide_tf_dump(const char *, struct ide_taskfile *);
extern void SELECT_DRIVE(ide_drive_t *);
+void SELECT_MASK(ide_drive_t *, int);
extern int drive_is_ready(ide_drive_t *);
void ide_pktcmd_tf_load(ide_drive_t *, u32, u16, u8);
+ide_startstop_t ide_pc_intr(ide_drive_t *drive, struct ide_atapi_pc *pc,
+ ide_handler_t *handler, unsigned int timeout, ide_expiry_t *expiry,
+ void (*update_buffers)(ide_drive_t *, struct ide_atapi_pc *),
+ void (*retry_pc)(ide_drive_t *), void (*dsc_handle)(ide_drive_t *),
+ void (*io_buffers)(ide_drive_t *, struct ide_atapi_pc *, unsigned int,
+ int));
+ide_startstop_t ide_transfer_pc(ide_drive_t *, struct ide_atapi_pc *,
+ ide_handler_t *, unsigned int, ide_expiry_t *);
+ide_startstop_t ide_issue_pc(ide_drive_t *, struct ide_atapi_pc *,
+ ide_handler_t *, unsigned int, ide_expiry_t *);
+
ide_startstop_t do_rw_taskfile(ide_drive_t *, ide_task_t *);
void task_end_request(ide_drive_t *, struct request *, u8);
@@ -981,8 +963,6 @@ int ide_taskfile_ioctl(ide_drive_t *, unsigned int, unsigned long);
int ide_cmd_ioctl(ide_drive_t *, unsigned int, unsigned long);
int ide_task_ioctl(ide_drive_t *, unsigned int, unsigned long);
-extern int system_bus_clock(void);
-
extern int ide_driveid_update(ide_drive_t *);
extern int ide_config_drive_speed(ide_drive_t *, u8);
extern u8 eighty_ninty_three (ide_drive_t *);
@@ -1264,16 +1244,43 @@ static inline int ide_dev_is_sata(struct hd_driveid *id)
u64 ide_get_lba_addr(struct ide_taskfile *, int);
u8 ide_dump_status(ide_drive_t *, const char *, u8);
-typedef struct ide_pio_timings_s {
- int setup_time; /* Address setup (ns) minimum */
- int active_time; /* Active pulse (ns) minimum */
- int cycle_time; /* Cycle time (ns) minimum = */
- /* active + recovery (+ setup for some chips) */
-} ide_pio_timings_t;
+struct ide_timing {
+ u8 mode;
+ u8 setup; /* t1 */
+ u16 act8b; /* t2 for 8-bit io */
+ u16 rec8b; /* t2i for 8-bit io */
+ u16 cyc8b; /* t0 for 8-bit io */
+ u16 active; /* t2 or tD */
+ u16 recover; /* t2i or tK */
+ u16 cycle; /* t0 */
+ u16 udma; /* t2CYCTYP/2 */
+};
+
+enum {
+ IDE_TIMING_SETUP = (1 << 0),
+ IDE_TIMING_ACT8B = (1 << 1),
+ IDE_TIMING_REC8B = (1 << 2),
+ IDE_TIMING_CYC8B = (1 << 3),
+ IDE_TIMING_8BIT = IDE_TIMING_ACT8B | IDE_TIMING_REC8B |
+ IDE_TIMING_CYC8B,
+ IDE_TIMING_ACTIVE = (1 << 4),
+ IDE_TIMING_RECOVER = (1 << 5),
+ IDE_TIMING_CYCLE = (1 << 6),
+ IDE_TIMING_UDMA = (1 << 7),
+ IDE_TIMING_ALL = IDE_TIMING_SETUP | IDE_TIMING_8BIT |
+ IDE_TIMING_ACTIVE | IDE_TIMING_RECOVER |
+ IDE_TIMING_CYCLE | IDE_TIMING_UDMA,
+};
+
+struct ide_timing *ide_timing_find_mode(u8);
+u16 ide_pio_cycle_time(ide_drive_t *, u8);
+void ide_timing_merge(struct ide_timing *, struct ide_timing *,
+ struct ide_timing *, unsigned int);
+int ide_timing_compute(ide_drive_t *, u8, struct ide_timing *, int, int);
+
+int ide_scan_pio_blacklist(char *);
-unsigned int ide_pio_cycle_time(ide_drive_t *, u8);
u8 ide_get_best_pio_mode(ide_drive_t *, u8, u8);
-extern const ide_pio_timings_t ide_pio_timings[6];
int ide_set_pio_mode(ide_drive_t *, u8);
int ide_set_dma_mode(ide_drive_t *, u8);
@@ -1334,7 +1341,8 @@ static inline void ide_set_irq(ide_drive_t *drive, int on)
{
ide_hwif_t *hwif = drive->hwif;
- hwif->OUTB(drive->ctl | (on ? 0 : 2), hwif->io_ports.ctl_addr);
+ hwif->OUTBSYNC(hwif, ATA_DEVCTL_OBS | (on ? 0 : 2),
+ hwif->io_ports.ctl_addr);
}
static inline u8 ide_read_status(ide_drive_t *drive)
diff --git a/include/linux/init.h b/include/linux/init.h
index 21d658cdfa27..b1ed48ddf73f 100644
--- a/include/linux/init.h
+++ b/include/linux/init.h
@@ -142,6 +142,7 @@ extern initcall_t __security_initcall_start[], __security_initcall_end[];
extern char __initdata boot_command_line[];
extern char *saved_command_line;
extern unsigned int reset_devices;
+extern int initmem_now_dynamic;
/* used by init/main.c */
void setup_arch(char **);
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index f1fc7470d26c..62aa4f895abe 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -104,8 +104,11 @@ extern void enable_irq(unsigned int irq);
#if defined(CONFIG_SMP) && defined(CONFIG_GENERIC_HARDIRQS)
+extern cpumask_t irq_default_affinity;
+
extern int irq_set_affinity(unsigned int irq, cpumask_t cpumask);
extern int irq_can_set_affinity(unsigned int irq);
+extern int irq_select_affinity(unsigned int irq);
#else /* CONFIG_SMP */
@@ -119,6 +122,8 @@ static inline int irq_can_set_affinity(unsigned int irq)
return 0;
}
+static inline int irq_select_affinity(unsigned int irq) { return 0; }
+
#endif /* CONFIG_SMP && CONFIG_GENERIC_HARDIRQS */
#ifdef CONFIG_GENERIC_HARDIRQS
@@ -285,12 +290,11 @@ enum
struct softirq_action
{
void (*action)(struct softirq_action *);
- void *data;
};
asmlinkage void do_softirq(void);
asmlinkage void __do_softirq(void);
-extern void open_softirq(int nr, void (*action)(struct softirq_action*), void *data);
+extern void open_softirq(int nr, void (*action)(struct softirq_action *));
extern void softirq_init(void);
#define __raise_softirq_irqoff(nr) do { or_softirq_pending(1UL << (nr)); } while (0)
extern void raise_softirq_irqoff(unsigned int nr);
diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index d5d40a9f7929..c6801bffe76d 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -53,14 +53,14 @@ struct resource_list {
#define IORESOURCE_AUTO 0x40000000
#define IORESOURCE_BUSY 0x80000000 /* Driver has marked this resource busy */
-/* ISA PnP IRQ specific bits (IORESOURCE_BITS) */
+/* PnP IRQ specific bits (IORESOURCE_BITS) */
#define IORESOURCE_IRQ_HIGHEDGE (1<<0)
#define IORESOURCE_IRQ_LOWEDGE (1<<1)
#define IORESOURCE_IRQ_HIGHLEVEL (1<<2)
#define IORESOURCE_IRQ_LOWLEVEL (1<<3)
#define IORESOURCE_IRQ_SHAREABLE (1<<4)
-/* ISA PnP DMA specific bits (IORESOURCE_BITS) */
+/* PnP DMA specific bits (IORESOURCE_BITS) */
#define IORESOURCE_DMA_TYPE_MASK (3<<0)
#define IORESOURCE_DMA_8BIT (0<<0)
#define IORESOURCE_DMA_8AND16BIT (1<<0)
@@ -76,7 +76,7 @@ struct resource_list {
#define IORESOURCE_DMA_TYPEB (2<<6)
#define IORESOURCE_DMA_TYPEF (3<<6)
-/* ISA PnP memory I/O specific bits (IORESOURCE_BITS) */
+/* PnP memory I/O specific bits (IORESOURCE_BITS) */
#define IORESOURCE_MEM_WRITEABLE (1<<0) /* dup: IORESOURCE_READONLY */
#define IORESOURCE_MEM_CACHEABLE (1<<1) /* dup: IORESOURCE_CACHEABLE */
#define IORESOURCE_MEM_RANGELENGTH (1<<2) /* dup: IORESOURCE_RANGELENGTH */
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 10b666b61add..cde056e08181 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -396,8 +396,10 @@ static inline struct request_sock *inet6_reqsk_alloc(struct request_sock_ops *op
{
struct request_sock *req = reqsk_alloc(ops);
- if (req != NULL)
+ if (req != NULL) {
inet_rsk(req)->inet6_rsk_offset = inet6_rsk_offset(req);
+ inet6_rsk(req)->pktopts = NULL;
+ }
return req;
}
diff --git a/include/linux/irq.h b/include/linux/irq.h
index 552e0ec269c9..8ccb462ea42c 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -244,15 +244,6 @@ static inline void set_balance_irq_affinity(unsigned int irq, cpumask_t mask)
}
#endif
-#ifdef CONFIG_AUTO_IRQ_AFFINITY
-extern int select_smp_affinity(unsigned int irq);
-#else
-static inline int select_smp_affinity(unsigned int irq)
-{
- return 1;
-}
-#endif
-
extern int no_irq_affinity;
static inline int irq_balancing_disabled(unsigned int irq)
diff --git a/include/linux/irqflags.h b/include/linux/irqflags.h
index e600c4e9b8c5..2b1c2e58566e 100644
--- a/include/linux/irqflags.h
+++ b/include/linux/irqflags.h
@@ -12,10 +12,10 @@
#define _LINUX_TRACE_IRQFLAGS_H
#ifdef CONFIG_TRACE_IRQFLAGS
- extern void trace_hardirqs_on(void);
- extern void trace_hardirqs_off(void);
extern void trace_softirqs_on(unsigned long ip);
extern void trace_softirqs_off(unsigned long ip);
+ extern void trace_hardirqs_on(void);
+ extern void trace_hardirqs_off(void);
# define trace_hardirq_context(p) ((p)->hardirq_context)
# define trace_softirq_context(p) ((p)->softirq_context)
# define trace_hardirqs_enabled(p) ((p)->hardirqs_enabled)
@@ -41,6 +41,15 @@
# define INIT_TRACE_IRQFLAGS
#endif
+#if defined(CONFIG_IRQSOFF_TRACER) || \
+ defined(CONFIG_PREEMPT_TRACER)
+ extern void stop_critical_timings(void);
+ extern void start_critical_timings(void);
+#else
+# define stop_critical_timings() do { } while (0)
+# define start_critical_timings() do { } while (0)
+#endif
+
#ifdef CONFIG_TRACE_IRQFLAGS_SUPPORT
#include <asm/irqflags.h>
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 792bf0aa779b..f2a668c195bf 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -184,9 +184,6 @@ asmlinkage int vprintk(const char *fmt, va_list args)
__attribute__ ((format (printf, 1, 0)));
asmlinkage int printk(const char * fmt, ...)
__attribute__ ((format (printf, 1, 2))) __cold;
-extern int log_buf_get_len(void);
-extern int log_buf_read(int idx);
-extern int log_buf_copy(char *dest, int idx, int len);
extern int printk_ratelimit_jiffies;
extern int printk_ratelimit_burst;
@@ -202,9 +199,6 @@ static inline int vprintk(const char *s, va_list args) { return 0; }
static inline int printk(const char *s, ...)
__attribute__ ((format (printf, 1, 2)));
static inline int __cold printk(const char *s, ...) { return 0; }
-static inline int log_buf_get_len(void) { return 0; }
-static inline int log_buf_read(int idx) { return 0; }
-static inline int log_buf_copy(char *dest, int idx, int len) { return 0; }
static inline int printk_ratelimit(void) { return 0; }
static inline int __printk_ratelimit(int ratelimit_jiffies, \
int ratelimit_burst) { return 0; }
diff --git a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h
index e8ffce898bf9..cf9f40a91c9c 100644
--- a/include/linux/kernel_stat.h
+++ b/include/linux/kernel_stat.h
@@ -1,11 +1,11 @@
#ifndef _LINUX_KERNEL_STAT_H
#define _LINUX_KERNEL_STAT_H
-#include <asm/irq.h>
#include <linux/smp.h>
#include <linux/threads.h>
#include <linux/percpu.h>
#include <linux/cpumask.h>
+#include <asm/irq.h>
#include <asm/cputime.h>
/*
diff --git a/include/linux/kobject.h b/include/linux/kobject.h
index 39e709f88aa0..60f0d418ae32 100644
--- a/include/linux/kobject.h
+++ b/include/linux/kobject.h
@@ -26,7 +26,6 @@
#include <linux/wait.h>
#include <asm/atomic.h>
-#define KOBJ_NAME_LEN 20
#define UEVENT_HELPER_PATH_LEN 256
#define UEVENT_NUM_ENVP 32 /* number of env pointers */
#define UEVENT_BUFFER_SIZE 2048 /* buffer for the variables */
@@ -59,12 +58,12 @@ enum kobject_action {
struct kobject {
const char *name;
- struct kref kref;
struct list_head entry;
struct kobject *parent;
struct kset *kset;
struct kobj_type *ktype;
struct sysfs_dirent *sd;
+ struct kref kref;
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
diff --git a/include/linux/kvm.h b/include/linux/kvm.h
index a281afeddfbb..0ea064cbfbc8 100644
--- a/include/linux/kvm.h
+++ b/include/linux/kvm.h
@@ -173,6 +173,30 @@ struct kvm_run {
};
};
+/* for KVM_REGISTER_COALESCED_MMIO / KVM_UNREGISTER_COALESCED_MMIO */
+
+struct kvm_coalesced_mmio_zone {
+ __u64 addr;
+ __u32 size;
+ __u32 pad;
+};
+
+struct kvm_coalesced_mmio {
+ __u64 phys_addr;
+ __u32 len;
+ __u32 pad;
+ __u8 data[8];
+};
+
+struct kvm_coalesced_mmio_ring {
+ __u32 first, last;
+ struct kvm_coalesced_mmio coalesced_mmio[0];
+};
+
+#define KVM_COALESCED_MMIO_MAX \
+ ((PAGE_SIZE - sizeof(struct kvm_coalesced_mmio_ring)) / \
+ sizeof(struct kvm_coalesced_mmio))
+
/* for KVM_TRANSLATE */
struct kvm_translation {
/* in */
@@ -294,14 +318,14 @@ struct kvm_trace_rec {
__u32 vcpu_id;
union {
struct {
- __u32 cycle_lo, cycle_hi;
+ __u64 cycle_u64;
__u32 extra_u32[KVM_TRC_EXTRA_MAX];
} cycle;
struct {
__u32 extra_u32[KVM_TRC_EXTRA_MAX];
} nocycle;
} u;
-};
+} __attribute__((packed));
#define KVMIO 0xAE
@@ -346,6 +370,7 @@ struct kvm_trace_rec {
#define KVM_CAP_NOP_IO_DELAY 12
#define KVM_CAP_PV_MMU 13
#define KVM_CAP_MP_STATE 14
+#define KVM_CAP_COALESCED_MMIO 15
/*
* ioctls for VM fds
@@ -371,6 +396,10 @@ struct kvm_trace_rec {
#define KVM_CREATE_PIT _IO(KVMIO, 0x64)
#define KVM_GET_PIT _IOWR(KVMIO, 0x65, struct kvm_pit_state)
#define KVM_SET_PIT _IOR(KVMIO, 0x66, struct kvm_pit_state)
+#define KVM_REGISTER_COALESCED_MMIO \
+ _IOW(KVMIO, 0x67, struct kvm_coalesced_mmio_zone)
+#define KVM_UNREGISTER_COALESCED_MMIO \
+ _IOW(KVMIO, 0x68, struct kvm_coalesced_mmio_zone)
/*
* ioctls for vcpu fds
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 398978972b7a..d220b4926c4a 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -33,6 +33,7 @@
#define KVM_REQ_REPORT_TPR_ACCESS 2
#define KVM_REQ_MMU_RELOAD 3
#define KVM_REQ_TRIPLE_FAULT 4
+#define KVM_REQ_PENDING_TIMER 5
struct kvm_vcpu;
extern struct kmem_cache *kvm_vcpu_cache;
@@ -51,7 +52,8 @@ struct kvm_io_bus {
void kvm_io_bus_init(struct kvm_io_bus *bus);
void kvm_io_bus_destroy(struct kvm_io_bus *bus);
-struct kvm_io_device *kvm_io_bus_find_dev(struct kvm_io_bus *bus, gpa_t addr);
+struct kvm_io_device *kvm_io_bus_find_dev(struct kvm_io_bus *bus,
+ gpa_t addr, int len, int is_write);
void kvm_io_bus_register_dev(struct kvm_io_bus *bus,
struct kvm_io_device *dev);
@@ -115,6 +117,10 @@ struct kvm {
struct kvm_vm_stat stat;
struct kvm_arch arch;
atomic_t users_count;
+#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET
+ struct kvm_coalesced_mmio_dev *coalesced_mmio_dev;
+ struct kvm_coalesced_mmio_ring *coalesced_mmio_ring;
+#endif
};
/* The guest did something we don't support. */
@@ -134,9 +140,6 @@ void kvm_vcpu_uninit(struct kvm_vcpu *vcpu);
void vcpu_load(struct kvm_vcpu *vcpu);
void vcpu_put(struct kvm_vcpu *vcpu);
-void decache_vcpus_on_cpu(int cpu);
-
-
int kvm_init(void *opaque, unsigned int vcpu_size,
struct module *module);
void kvm_exit(void);
@@ -297,7 +300,7 @@ static inline gpa_t gfn_to_gpa(gfn_t gfn)
return (gpa_t)gfn << PAGE_SHIFT;
}
-static inline void kvm_migrate_apic_timer(struct kvm_vcpu *vcpu)
+static inline void kvm_migrate_timers(struct kvm_vcpu *vcpu)
{
set_bit(KVM_REQ_MIGRATE_TIMER, &vcpu->requests);
}
diff --git a/include/linux/linkage.h b/include/linux/linkage.h
index 2119610b24f8..14f329c64ba8 100644
--- a/include/linux/linkage.h
+++ b/include/linux/linkage.h
@@ -3,6 +3,8 @@
#include <asm/linkage.h>
+#define notrace __attribute__((no_instrument_function))
+
#ifdef __cplusplus
#define CPP_ASMLINKAGE extern "C"
#else
diff --git a/include/linux/list.h b/include/linux/list.h
index 08cf4f651889..139ec41d9c2e 100644
--- a/include/linux/list.h
+++ b/include/linux/list.h
@@ -85,65 +85,6 @@ static inline void list_add_tail(struct list_head *new, struct list_head *head)
}
/*
- * Insert a new entry between two known consecutive entries.
- *
- * This is only for internal list manipulation where we know
- * the prev/next entries already!
- */
-static inline void __list_add_rcu(struct list_head * new,
- struct list_head * prev, struct list_head * next)
-{
- new->next = next;
- new->prev = prev;
- smp_wmb();
- next->prev = new;
- prev->next = new;
-}
-
-/**
- * list_add_rcu - add a new entry to rcu-protected list
- * @new: new entry to be added
- * @head: list head to add it after
- *
- * Insert a new entry after the specified head.
- * This is good for implementing stacks.
- *
- * The caller must take whatever precautions are necessary
- * (such as holding appropriate locks) to avoid racing
- * with another list-mutation primitive, such as list_add_rcu()
- * or list_del_rcu(), running on this same list.
- * However, it is perfectly legal to run concurrently with
- * the _rcu list-traversal primitives, such as
- * list_for_each_entry_rcu().
- */
-static inline void list_add_rcu(struct list_head *new, struct list_head *head)
-{
- __list_add_rcu(new, head, head->next);
-}
-
-/**
- * list_add_tail_rcu - add a new entry to rcu-protected list
- * @new: new entry to be added
- * @head: list head to add it before
- *
- * Insert a new entry before the specified head.
- * This is useful for implementing queues.
- *
- * The caller must take whatever precautions are necessary
- * (such as holding appropriate locks) to avoid racing
- * with another list-mutation primitive, such as list_add_tail_rcu()
- * or list_del_rcu(), running on this same list.
- * However, it is perfectly legal to run concurrently with
- * the _rcu list-traversal primitives, such as
- * list_for_each_entry_rcu().
- */
-static inline void list_add_tail_rcu(struct list_head *new,
- struct list_head *head)
-{
- __list_add_rcu(new, head->prev, head);
-}
-
-/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
@@ -174,36 +115,6 @@ extern void list_del(struct list_head *entry);
#endif
/**
- * list_del_rcu - deletes entry from list without re-initialization
- * @entry: the element to delete from the list.
- *
- * Note: list_empty() on entry does not return true after this,
- * the entry is in an undefined state. It is useful for RCU based
- * lockfree traversal.
- *
- * In particular, it means that we can not poison the forward
- * pointers that may still be used for walking the list.
- *
- * The caller must take whatever precautions are necessary
- * (such as holding appropriate locks) to avoid racing
- * with another list-mutation primitive, such as list_del_rcu()
- * or list_add_rcu(), running on this same list.
- * However, it is perfectly legal to run concurrently with
- * the _rcu list-traversal primitives, such as
- * list_for_each_entry_rcu().
- *
- * Note that the caller is not permitted to immediately free
- * the newly deleted entry. Instead, either synchronize_rcu()
- * or call_rcu() must be used to defer freeing until an RCU
- * grace period has elapsed.
- */
-static inline void list_del_rcu(struct list_head *entry)
-{
- __list_del(entry->prev, entry->next);
- entry->prev = LIST_POISON2;
-}
-
-/**
* list_replace - replace old entry by new one
* @old : the element to be replaced
* @new : the new element to insert
@@ -227,25 +138,6 @@ static inline void list_replace_init(struct list_head *old,
}
/**
- * list_replace_rcu - replace old entry by new one
- * @old : the element to be replaced
- * @new : the new element to insert
- *
- * The @old entry will be replaced with the @new entry atomically.
- * Note: @old should not be empty.
- */
-static inline void list_replace_rcu(struct list_head *old,
- struct list_head *new)
-{
- new->next = old->next;
- new->prev = old->prev;
- smp_wmb();
- new->next->prev = new;
- new->prev->next = new;
- old->prev = LIST_POISON2;
-}
-
-/**
* list_del_init - deletes entry from list and reinitialize it.
* @entry: the element to delete from the list.
*/
@@ -369,62 +261,6 @@ static inline void list_splice_init(struct list_head *list,
}
/**
- * list_splice_init_rcu - splice an RCU-protected list into an existing list.
- * @list: the RCU-protected list to splice
- * @head: the place in the list to splice the first list into
- * @sync: function to sync: synchronize_rcu(), synchronize_sched(), ...
- *
- * @head can be RCU-read traversed concurrently with this function.
- *
- * Note that this function blocks.
- *
- * Important note: the caller must take whatever action is necessary to
- * prevent any other updates to @head. In principle, it is possible
- * to modify the list as soon as sync() begins execution.
- * If this sort of thing becomes necessary, an alternative version
- * based on call_rcu() could be created. But only if -really-
- * needed -- there is no shortage of RCU API members.
- */
-static inline void list_splice_init_rcu(struct list_head *list,
- struct list_head *head,
- void (*sync)(void))
-{
- struct list_head *first = list->next;
- struct list_head *last = list->prev;
- struct list_head *at = head->next;
-
- if (list_empty(head))
- return;
-
- /* "first" and "last" tracking list, so initialize it. */
-
- INIT_LIST_HEAD(list);
-
- /*
- * At this point, the list body still points to the source list.
- * Wait for any readers to finish using the list before splicing
- * the list body into the new list. Any new readers will see
- * an empty list.
- */
-
- sync();
-
- /*
- * Readers are finished with the source list, so perform splice.
- * The order is important if the new list is global and accessible
- * to concurrent RCU readers. Note that RCU readers are not
- * permitted to traverse the prev pointers without excluding
- * this function.
- */
-
- last->next = at;
- smp_wmb();
- head->next = first;
- first->prev = head;
- at->prev = last;
-}
-
-/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
@@ -629,57 +465,6 @@ static inline void list_splice_init_rcu(struct list_head *list,
&pos->member != (head); \
pos = n, n = list_entry(n->member.prev, typeof(*n), member))
-/**
- * list_for_each_rcu - iterate over an rcu-protected list
- * @pos: the &struct list_head to use as a loop cursor.
- * @head: the head for your list.
- *
- * This list-traversal primitive may safely run concurrently with
- * the _rcu list-mutation primitives such as list_add_rcu()
- * as long as the traversal is guarded by rcu_read_lock().
- */
-#define list_for_each_rcu(pos, head) \
- for (pos = rcu_dereference((head)->next); \
- prefetch(pos->next), pos != (head); \
- pos = rcu_dereference(pos->next))
-
-#define __list_for_each_rcu(pos, head) \
- for (pos = rcu_dereference((head)->next); \
- pos != (head); \
- pos = rcu_dereference(pos->next))
-
-/**
- * list_for_each_entry_rcu - iterate over rcu list of given type
- * @pos: the type * to use as a loop cursor.
- * @head: the head for your list.
- * @member: the name of the list_struct within the struct.
- *
- * This list-traversal primitive may safely run concurrently with
- * the _rcu list-mutation primitives such as list_add_rcu()
- * as long as the traversal is guarded by rcu_read_lock().
- */
-#define list_for_each_entry_rcu(pos, head, member) \
- for (pos = list_entry(rcu_dereference((head)->next), typeof(*pos), member); \
- prefetch(pos->member.next), &pos->member != (head); \
- pos = list_entry(rcu_dereference(pos->member.next), typeof(*pos), member))
-
-
-/**
- * list_for_each_continue_rcu
- * @pos: the &struct list_head to use as a loop cursor.
- * @head: the head for your list.
- *
- * Iterate over an rcu-protected list, continuing after current point.
- *
- * This list-traversal primitive may safely run concurrently with
- * the _rcu list-mutation primitives such as list_add_rcu()
- * as long as the traversal is guarded by rcu_read_lock().
- */
-#define list_for_each_continue_rcu(pos, head) \
- for ((pos) = rcu_dereference((pos)->next); \
- prefetch((pos)->next), (pos) != (head); \
- (pos) = rcu_dereference((pos)->next))
-
/*
* Double linked lists with a single pointer list head.
* Mostly useful for hash tables where the two pointer list head is
@@ -730,31 +515,6 @@ static inline void hlist_del(struct hlist_node *n)
n->pprev = LIST_POISON2;
}
-/**
- * hlist_del_rcu - deletes entry from hash list without re-initialization
- * @n: the element to delete from the hash list.
- *
- * Note: list_unhashed() on entry does not return true after this,
- * the entry is in an undefined state. It is useful for RCU based
- * lockfree traversal.
- *
- * In particular, it means that we can not poison the forward
- * pointers that may still be used for walking the hash list.
- *
- * The caller must take whatever precautions are necessary
- * (such as holding appropriate locks) to avoid racing
- * with another list-mutation primitive, such as hlist_add_head_rcu()
- * or hlist_del_rcu(), running on this same list.
- * However, it is perfectly legal to run concurrently with
- * the _rcu list-traversal primitives, such as
- * hlist_for_each_entry().
- */
-static inline void hlist_del_rcu(struct hlist_node *n)
-{
- __hlist_del(n);
- n->pprev = LIST_POISON2;
-}
-
static inline void hlist_del_init(struct hlist_node *n)
{
if (!hlist_unhashed(n)) {
@@ -763,27 +523,6 @@ static inline void hlist_del_init(struct hlist_node *n)
}
}
-/**
- * hlist_replace_rcu - replace old entry by new one
- * @old : the element to be replaced
- * @new : the new element to insert
- *
- * The @old entry will be replaced with the @new entry atomically.
- */
-static inline void hlist_replace_rcu(struct hlist_node *old,
- struct hlist_node *new)
-{
- struct hlist_node *next = old->next;
-
- new->next = next;
- new->pprev = old->pprev;
- smp_wmb();
- if (next)
- new->next->pprev = &new->next;
- *new->pprev = new;
- old->pprev = LIST_POISON2;
-}
-
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
struct hlist_node *first = h->first;
@@ -794,38 +533,6 @@ static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
n->pprev = &h->first;
}
-
-/**
- * hlist_add_head_rcu
- * @n: the element to add to the hash list.
- * @h: the list to add to.
- *
- * Description:
- * Adds the specified element to the specified hlist,
- * while permitting racing traversals.
- *
- * The caller must take whatever precautions are necessary
- * (such as holding appropriate locks) to avoid racing
- * with another list-mutation primitive, such as hlist_add_head_rcu()
- * or hlist_del_rcu(), running on this same list.
- * However, it is perfectly legal to run concurrently with
- * the _rcu list-traversal primitives, such as
- * hlist_for_each_entry_rcu(), used to prevent memory-consistency
- * problems on Alpha CPUs. Regardless of the type of CPU, the
- * list-traversal primitive must be guarded by rcu_read_lock().
- */
-static inline void hlist_add_head_rcu(struct hlist_node *n,
- struct hlist_head *h)
-{
- struct hlist_node *first = h->first;
- n->next = first;
- n->pprev = &h->first;
- smp_wmb();
- if (first)
- first->pprev = &n->next;
- h->first = n;
-}
-
/* next must be != NULL */
static inline void hlist_add_before(struct hlist_node *n,
struct hlist_node *next)
@@ -847,63 +554,6 @@ static inline void hlist_add_after(struct hlist_node *n,
next->next->pprev = &next->next;
}
-/**
- * hlist_add_before_rcu
- * @n: the new element to add to the hash list.
- * @next: the existing element to add the new element before.
- *
- * Description:
- * Adds the specified element to the specified hlist
- * before the specified node while permitting racing traversals.
- *
- * The caller must take whatever precautions are necessary
- * (such as holding appropriate locks) to avoid racing
- * with another list-mutation primitive, such as hlist_add_head_rcu()
- * or hlist_del_rcu(), running on this same list.
- * However, it is perfectly legal to run concurrently with
- * the _rcu list-traversal primitives, such as
- * hlist_for_each_entry_rcu(), used to prevent memory-consistency
- * problems on Alpha CPUs.
- */
-static inline void hlist_add_before_rcu(struct hlist_node *n,
- struct hlist_node *next)
-{
- n->pprev = next->pprev;
- n->next = next;
- smp_wmb();
- next->pprev = &n->next;
- *(n->pprev) = n;
-}
-
-/**
- * hlist_add_after_rcu
- * @prev: the existing element to add the new element after.
- * @n: the new element to add to the hash list.
- *
- * Description:
- * Adds the specified element to the specified hlist
- * after the specified node while permitting racing traversals.
- *
- * The caller must take whatever precautions are necessary
- * (such as holding appropriate locks) to avoid racing
- * with another list-mutation primitive, such as hlist_add_head_rcu()
- * or hlist_del_rcu(), running on this same list.
- * However, it is perfectly legal to run concurrently with
- * the _rcu list-traversal primitives, such as
- * hlist_for_each_entry_rcu(), used to prevent memory-consistency
- * problems on Alpha CPUs.
- */
-static inline void hlist_add_after_rcu(struct hlist_node *prev,
- struct hlist_node *n)
-{
- n->next = prev->next;
- n->pprev = &prev->next;
- smp_wmb();
- prev->next = n;
- if (n->next)
- n->next->pprev = &n->next;
-}
-
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
#define hlist_for_each(pos, head) \
@@ -964,21 +614,4 @@ static inline void hlist_add_after_rcu(struct hlist_node *prev,
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = n)
-/**
- * hlist_for_each_entry_rcu - iterate over rcu list of given type
- * @tpos: the type * to use as a loop cursor.
- * @pos: the &struct hlist_node to use as a loop cursor.
- * @head: the head for your list.
- * @member: the name of the hlist_node within the struct.
- *
- * This list-traversal primitive may safely run concurrently with
- * the _rcu list-mutation primitives such as hlist_add_head_rcu()
- * as long as the traversal is guarded by rcu_read_lock().
- */
-#define hlist_for_each_entry_rcu(tpos, pos, head, member) \
- for (pos = rcu_dereference((head)->first); \
- pos && ({ prefetch(pos->next); 1;}) && \
- ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
- pos = rcu_dereference(pos->next))
-
#endif
diff --git a/include/linux/magic.h b/include/linux/magic.h
index 1fa0c2ce4dec..74e68e201166 100644
--- a/include/linux/magic.h
+++ b/include/linux/magic.h
@@ -42,4 +42,5 @@
#define FUTEXFS_SUPER_MAGIC 0xBAD1DEA
#define INOTIFYFS_SUPER_MAGIC 0x2BAD1DEA
+#define STACK_END_MAGIC 0x57AC6E9D
#endif /* __LINUX_MAGIC_H__ */
diff --git a/include/linux/marker.h b/include/linux/marker.h
index 430f6adf9762..1290653f9241 100644
--- a/include/linux/marker.h
+++ b/include/linux/marker.h
@@ -44,8 +44,8 @@ struct marker {
*/
char state; /* Marker state. */
char ptype; /* probe type : 0 : single, 1 : multi */
- void (*call)(const struct marker *mdata, /* Probe wrapper */
- void *call_private, const char *fmt, ...);
+ /* Probe wrapper */
+ void (*call)(const struct marker *mdata, void *call_private, ...);
struct marker_probe_closure single;
struct marker_probe_closure *multi;
} __attribute__((aligned(8)));
@@ -58,8 +58,12 @@ struct marker {
* Make sure the alignment of the structure in the __markers section will
* not add unwanted padding between the beginning of the section and the
* structure. Force alignment to the same alignment as the section start.
+ *
+ * The "generic" argument controls which marker enabling mechanism must be used.
+ * If generic is true, a variable read is used.
+ * If generic is false, immediate values are used.
*/
-#define __trace_mark(name, call_private, format, args...) \
+#define __trace_mark(generic, name, call_private, format, args...) \
do { \
static const char __mstrtab_##name[] \
__attribute__((section("__markers_strings"))) \
@@ -72,15 +76,14 @@ struct marker {
__mark_check_format(format, ## args); \
if (unlikely(__mark_##name.state)) { \
(*__mark_##name.call) \
- (&__mark_##name, call_private, \
- format, ## args); \
+ (&__mark_##name, call_private, ## args);\
} \
} while (0)
extern void marker_update_probe_range(struct marker *begin,
struct marker *end);
#else /* !CONFIG_MARKERS */
-#define __trace_mark(name, call_private, format, args...) \
+#define __trace_mark(generic, name, call_private, format, args...) \
__mark_check_format(format, ## args)
static inline void marker_update_probe_range(struct marker *begin,
struct marker *end)
@@ -88,15 +91,30 @@ static inline void marker_update_probe_range(struct marker *begin,
#endif /* CONFIG_MARKERS */
/**
- * trace_mark - Marker
+ * trace_mark - Marker using code patching
* @name: marker name, not quoted.
* @format: format string
* @args...: variable argument list
*
- * Places a marker.
+ * Places a marker using optimized code patching technique (imv_read())
+ * to be enabled when immediate values are present.
*/
#define trace_mark(name, format, args...) \
- __trace_mark(name, NULL, format, ## args)
+ __trace_mark(0, name, NULL, format, ## args)
+
+/**
+ * _trace_mark - Marker using variable read
+ * @name: marker name, not quoted.
+ * @format: format string
+ * @args...: variable argument list
+ *
+ * Places a marker using a standard memory read (_imv_read()) to be
+ * enabled. Should be used for markers in code paths where instruction
+ * modification based enabling is not welcome. (__init and __exit functions,
+ * lockdep, some traps, printk).
+ */
+#define _trace_mark(name, format, args...) \
+ __trace_mark(1, name, NULL, format, ## args)
/**
* MARK_NOARGS - Format string for a marker with no argument.
@@ -117,9 +135,9 @@ static inline void __printf(1, 2) ___mark_check_format(const char *fmt, ...)
extern marker_probe_func __mark_empty_function;
extern void marker_probe_cb(const struct marker *mdata,
- void *call_private, const char *fmt, ...);
+ void *call_private, ...);
extern void marker_probe_cb_noarg(const struct marker *mdata,
- void *call_private, const char *fmt, ...);
+ void *call_private, ...);
/*
* Connect a probe to a marker.
diff --git a/include/linux/math64.h b/include/linux/math64.h
index c1a5f81501ff..c87f1528703a 100644
--- a/include/linux/math64.h
+++ b/include/linux/math64.h
@@ -81,4 +81,25 @@ static inline s64 div_s64(s64 dividend, s32 divisor)
}
#endif
+u32 iter_div_u64_rem(u64 dividend, u32 divisor, u64 *remainder);
+
+static __always_inline u32
+__iter_div_u64_rem(u64 dividend, u32 divisor, u64 *remainder)
+{
+ u32 ret = 0;
+
+ while (dividend >= divisor) {
+ /* The following asm() prevents the compiler from
+ optimising this loop into a modulo operation. */
+ asm("" : "+rm"(dividend));
+
+ dividend -= divisor;
+ ret++;
+ }
+
+ *remainder = dividend;
+
+ return ret;
+}
+
#endif /* _LINUX_MATH64_H */
diff --git a/include/linux/mm.h b/include/linux/mm.h
index c31a9cd2a30e..444022f00cc9 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -997,8 +997,7 @@ extern void free_area_init_node(int nid, pg_data_t *pgdat,
extern void free_area_init_nodes(unsigned long *max_zone_pfn);
extern void add_active_range(unsigned int nid, unsigned long start_pfn,
unsigned long end_pfn);
-extern void shrink_active_range(unsigned int nid, unsigned long old_end_pfn,
- unsigned long new_end_pfn);
+extern void shrink_active_range(unsigned int nid, unsigned long new_end_pfn);
extern void push_node_boundaries(unsigned int nid, unsigned long start_pfn,
unsigned long end_pfn);
extern void remove_all_active_ranges(void);
@@ -1023,6 +1022,7 @@ extern void mem_init(void);
extern void show_mem(void);
extern void si_meminfo(struct sysinfo * val);
extern void si_meminfo_node(struct sysinfo *val, int nid);
+extern int after_bootmem;
#ifdef CONFIG_NUMA
extern void setup_per_cpu_pageset(void);
diff --git a/include/linux/mmiotrace.h b/include/linux/mmiotrace.h
new file mode 100644
index 000000000000..61d19e1b7a0b
--- /dev/null
+++ b/include/linux/mmiotrace.h
@@ -0,0 +1,85 @@
+#ifndef MMIOTRACE_H
+#define MMIOTRACE_H
+
+#include <linux/types.h>
+#include <linux/list.h>
+
+struct kmmio_probe;
+struct pt_regs;
+
+typedef void (*kmmio_pre_handler_t)(struct kmmio_probe *,
+ struct pt_regs *, unsigned long addr);
+typedef void (*kmmio_post_handler_t)(struct kmmio_probe *,
+ unsigned long condition, struct pt_regs *);
+
+struct kmmio_probe {
+ struct list_head list; /* kmmio internal list */
+ unsigned long addr; /* start location of the probe point */
+ unsigned long len; /* length of the probe region */
+ kmmio_pre_handler_t pre_handler; /* Called before addr is executed. */
+ kmmio_post_handler_t post_handler; /* Called after addr is executed */
+ void *private;
+};
+
+/* kmmio is active by some kmmio_probes? */
+static inline int is_kmmio_active(void)
+{
+ extern unsigned int kmmio_count;
+ return kmmio_count;
+}
+
+extern int register_kmmio_probe(struct kmmio_probe *p);
+extern void unregister_kmmio_probe(struct kmmio_probe *p);
+
+/* Called from page fault handler. */
+extern int kmmio_handler(struct pt_regs *regs, unsigned long addr);
+
+/* Called from ioremap.c */
+#ifdef CONFIG_MMIOTRACE
+extern void mmiotrace_ioremap(resource_size_t offset, unsigned long size,
+ void __iomem *addr);
+extern void mmiotrace_iounmap(volatile void __iomem *addr);
+#else
+static inline void mmiotrace_ioremap(resource_size_t offset,
+ unsigned long size, void __iomem *addr)
+{
+}
+
+static inline void mmiotrace_iounmap(volatile void __iomem *addr)
+{
+}
+#endif /* CONFIG_MMIOTRACE_HOOKS */
+
+enum mm_io_opcode {
+ MMIO_READ = 0x1, /* struct mmiotrace_rw */
+ MMIO_WRITE = 0x2, /* struct mmiotrace_rw */
+ MMIO_PROBE = 0x3, /* struct mmiotrace_map */
+ MMIO_UNPROBE = 0x4, /* struct mmiotrace_map */
+ MMIO_MARKER = 0x5, /* raw char data */
+ MMIO_UNKNOWN_OP = 0x6, /* struct mmiotrace_rw */
+};
+
+struct mmiotrace_rw {
+ resource_size_t phys; /* PCI address of register */
+ unsigned long value;
+ unsigned long pc; /* optional program counter */
+ int map_id;
+ unsigned char opcode; /* one of MMIO_{READ,WRITE,UNKNOWN_OP} */
+ unsigned char width; /* size of register access in bytes */
+};
+
+struct mmiotrace_map {
+ resource_size_t phys; /* base address in PCI space */
+ unsigned long virt; /* base virtual address */
+ unsigned long len; /* mapping size */
+ int map_id;
+ unsigned char opcode; /* MMIO_PROBE or MMIO_UNPROBE */
+};
+
+/* in kernel/trace/trace_mmiotrace.c */
+extern void enable_mmiotrace(void);
+extern void disable_mmiotrace(void);
+extern void mmio_trace_rw(struct mmiotrace_rw *rw);
+extern void mmio_trace_mapping(struct mmiotrace_map *map);
+
+#endif /* MMIOTRACE_H */
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index 69b2342d5ebb..1fd03e732e07 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -159,6 +159,15 @@ struct ap_device_id {
#define AP_DEVICE_ID_MATCH_DEVICE_TYPE 0x01
+/* s390 css bus devices (subchannels) */
+struct css_device_id {
+ __u8 type; /* subchannel type */
+ __u8 pad1;
+ __u16 pad2;
+ __u32 pad3;
+ kernel_ulong_t driver_data;
+};
+
#define ACPI_ID_LEN 16 /* only 9 bytes needed here, 16 bytes are used */
/* to workaround crosscompile issues */
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index 8726491de154..ea0366769484 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -65,9 +65,6 @@
#define NFS4_ACE_SUCCESSFUL_ACCESS_ACE_FLAG 0x00000010
#define NFS4_ACE_FAILED_ACCESS_ACE_FLAG 0x00000020
#define NFS4_ACE_IDENTIFIER_GROUP 0x00000040
-#define NFS4_ACE_OWNER 0x00000080
-#define NFS4_ACE_GROUP 0x00000100
-#define NFS4_ACE_EVERYONE 0x00000200
#define NFS4_ACE_READ_DATA 0x00000001
#define NFS4_ACE_LIST_DIRECTORY 0x00000001
diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h
index 41d30c9c9de6..88d85b964429 100644
--- a/include/linux/nfsd/nfsd.h
+++ b/include/linux/nfsd/nfsd.h
@@ -54,6 +54,7 @@ typedef int (*nfsd_dirop_t)(struct inode *, struct dentry *, int, int);
extern struct svc_program nfsd_program;
extern struct svc_version nfsd_version2, nfsd_version3,
nfsd_version4;
+extern struct mutex nfsd_mutex;
extern struct svc_serv *nfsd_serv;
extern struct seq_operations nfs_exports_op;
diff --git a/include/linux/nfsd/state.h b/include/linux/nfsd/state.h
index db348f749376..d0fe2e378452 100644
--- a/include/linux/nfsd/state.h
+++ b/include/linux/nfsd/state.h
@@ -98,8 +98,6 @@ struct nfs4_callback {
u32 cb_ident;
/* RPC client info */
atomic_t cb_set; /* successful CB_NULL call */
- struct rpc_program cb_program;
- struct rpc_stat cb_stat;
struct rpc_clnt * cb_client;
};
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index f31debfac926..0d2a4e7012aa 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -157,6 +157,7 @@ PAGEFLAG(Active, active) __CLEARPAGEFLAG(Active, active)
__PAGEFLAG(Slab, slab)
PAGEFLAG(Checked, owner_priv_1) /* Used by some filesystems */
PAGEFLAG(Pinned, owner_priv_1) TESTSCFLAG(Pinned, owner_priv_1) /* Xen */
+PAGEFLAG(SavePinned, dirty); /* Xen */
PAGEFLAG(Reserved, reserved) __CLEARPAGEFLAG(Reserved, reserved)
PAGEFLAG(Private, private) __CLEARPAGEFLAG(Private, private)
__SETPAGEFLAG(Private, private)
diff --git a/include/linux/pageblock-flags.h b/include/linux/pageblock-flags.h
index e875905f7b12..e8c06122be36 100644
--- a/include/linux/pageblock-flags.h
+++ b/include/linux/pageblock-flags.h
@@ -25,13 +25,11 @@
#include <linux/types.h>
-/* Macro to aid the definition of ranges of bits */
-#define PB_range(name, required_bits) \
- name, name ## _end = (name + required_bits) - 1
-
/* Bit indices that affect a whole block of pages */
enum pageblock_bits {
- PB_range(PB_migrate, 3), /* 3 bits required for migrate types */
+ PB_migrate,
+ PB_migrate_end = PB_migrate + 3 - 1,
+ /* 3 bits required for migrate types */
NR_PAGEBLOCK_BITS
};
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 509159bcd4e7..f9d070b62d43 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -17,8 +17,7 @@
#ifndef LINUX_PCI_H
#define LINUX_PCI_H
-/* Include the pci register defines */
-#include <linux/pci_regs.h>
+#include <linux/pci_regs.h> /* The pci register defines */
/*
* The PCI interface treats multi-function devices as independent
@@ -49,12 +48,22 @@
#include <linux/list.h>
#include <linux/compiler.h>
#include <linux/errno.h>
+#include <linux/kobject.h>
#include <asm/atomic.h>
#include <linux/device.h>
/* Include the ID list */
#include <linux/pci_ids.h>
+/* pci_slot represents a physical slot */
+struct pci_slot {
+ struct pci_bus *bus; /* The bus this slot is on */
+ struct list_head list; /* node in list of slots on this bus */
+ struct hotplug_slot *hotplug; /* Hotplug info (migrate over time) */
+ unsigned char number; /* PCI_SLOT(pci_dev->devfn) */
+ struct kobject kobj;
+};
+
/* File state for mmap()s on /proc/bus/pci/X/Y */
enum pci_mmap_state {
pci_mmap_io,
@@ -142,6 +151,7 @@ struct pci_dev {
void *sysdata; /* hook for sys-specific extension */
struct proc_dir_entry *procent; /* device entry in /proc/bus/pci */
+ struct pci_slot *slot; /* Physical slot this device is in */
unsigned int devfn; /* encoded device & function index */
unsigned short vendor;
@@ -206,6 +216,7 @@ struct pci_dev {
struct bin_attribute *rom_attr; /* attribute descriptor for sysfs ROM entry */
int rom_attr_enabled; /* has display of the rom attribute been enabled? */
struct bin_attribute *res_attr[DEVICE_COUNT_RESOURCE]; /* sysfs file for resources */
+ struct bin_attribute *res_attr_wc[DEVICE_COUNT_RESOURCE]; /* sysfs file for WC mapping of resources */
#ifdef CONFIG_PCI_MSI
struct list_head msi_list;
#endif
@@ -266,6 +277,7 @@ struct pci_bus {
struct list_head children; /* list of child buses */
struct list_head devices; /* list of devices on this bus */
struct pci_dev *self; /* bridge device as seen by parent */
+ struct list_head slots; /* list of slots on this bus */
struct resource *resource[PCI_BUS_NUM_RESOURCES];
/* address space routed to this bus */
@@ -389,7 +401,7 @@ struct pci_driver {
int (*resume_early) (struct pci_dev *dev);
int (*resume) (struct pci_dev *dev); /* Device woken up */
void (*shutdown) (struct pci_dev *dev);
-
+ struct pm_ext_ops *pm;
struct pci_error_handlers *err_handler;
struct device_driver driver;
struct pci_dynids dynids;
@@ -488,6 +500,10 @@ struct pci_bus *pci_create_bus(struct device *parent, int bus,
struct pci_ops *ops, void *sysdata);
struct pci_bus *pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev,
int busnr);
+struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr,
+ const char *name);
+void pci_destroy_slot(struct pci_slot *slot);
+void pci_update_slot_number(struct pci_slot *slot, int slot_nr);
int pci_scan_slot(struct pci_bus *bus, int devfn);
struct pci_dev *pci_scan_single_device(struct pci_bus *bus, int devfn);
void pci_device_add(struct pci_dev *dev, struct pci_bus *bus);
@@ -976,9 +992,9 @@ static inline void pci_set_drvdata(struct pci_dev *pdev, void *data)
/* If you want to know what to call your pci_dev, ask this function.
* Again, it's a wrapper around the generic device.
*/
-static inline char *pci_name(struct pci_dev *pdev)
+static inline const char *pci_name(struct pci_dev *pdev)
{
- return pdev->dev.bus_id;
+ return dev_name(&pdev->dev);
}
@@ -1013,7 +1029,9 @@ enum pci_fixup_pass {
pci_fixup_header, /* After reading configuration header */
pci_fixup_final, /* Final phase of device fixups */
pci_fixup_enable, /* pci_enable_device() time */
- pci_fixup_resume, /* pci_enable_device() time */
+ pci_fixup_resume, /* pci_device_resume() */
+ pci_fixup_suspend, /* pci_device_suspend */
+ pci_fixup_resume_early, /* pci_device_resume_early() */
};
/* Anonymous variables would be nice... */
@@ -1035,6 +1053,12 @@ enum pci_fixup_pass {
#define DECLARE_PCI_FIXUP_RESUME(vendor, device, hook) \
DECLARE_PCI_FIXUP_SECTION(.pci_fixup_resume, \
resume##vendor##device##hook, vendor, device, hook)
+#define DECLARE_PCI_FIXUP_RESUME_EARLY(vendor, device, hook) \
+ DECLARE_PCI_FIXUP_SECTION(.pci_fixup_resume_early, \
+ resume_early##vendor##device##hook, vendor, device, hook)
+#define DECLARE_PCI_FIXUP_SUSPEND(vendor, device, hook) \
+ DECLARE_PCI_FIXUP_SECTION(.pci_fixup_suspend, \
+ suspend##vendor##device##hook, vendor, device, hook)
void pci_fixup_device(enum pci_fixup_pass pass, struct pci_dev *dev);
@@ -1059,7 +1083,10 @@ extern int pci_pci_problems;
extern unsigned long pci_cardbus_io_size;
extern unsigned long pci_cardbus_mem_size;
-extern int pcibios_add_platform_entries(struct pci_dev *dev);
+int pcibios_add_platform_entries(struct pci_dev *dev);
+void pcibios_disable_device(struct pci_dev *dev);
+int pcibios_set_pcie_reset_state(struct pci_dev *dev,
+ enum pcie_reset_state state);
#ifdef CONFIG_PCI_MMCONFIG
extern void __init pci_mmcfg_early_init(void);
diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h
index 8f67e8f2a3cc..a08cd06b541a 100644
--- a/include/linux/pci_hotplug.h
+++ b/include/linux/pci_hotplug.h
@@ -95,9 +95,6 @@ struct hotplug_slot_attribute {
* @get_adapter_status: Called to get see if an adapter is present in the slot or not.
* If this field is NULL, the value passed in the struct hotplug_slot_info
* will be used when this value is requested by a user.
- * @get_address: Called to get pci address of a slot.
- * If this field is NULL, the value passed in the struct hotplug_slot_info
- * will be used when this value is requested by a user.
* @get_max_bus_speed: Called to get the max bus speed for a slot.
* If this field is NULL, the value passed in the struct hotplug_slot_info
* will be used when this value is requested by a user.
@@ -120,7 +117,6 @@ struct hotplug_slot_ops {
int (*get_attention_status) (struct hotplug_slot *slot, u8 *value);
int (*get_latch_status) (struct hotplug_slot *slot, u8 *value);
int (*get_adapter_status) (struct hotplug_slot *slot, u8 *value);
- int (*get_address) (struct hotplug_slot *slot, u32 *value);
int (*get_max_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value);
int (*get_cur_bus_speed) (struct hotplug_slot *slot, enum pci_bus_speed *value);
};
@@ -140,7 +136,6 @@ struct hotplug_slot_info {
u8 attention_status;
u8 latch_status;
u8 adapter_status;
- u32 address;
enum pci_bus_speed max_bus_speed;
enum pci_bus_speed cur_bus_speed;
};
@@ -166,15 +161,14 @@ struct hotplug_slot {
/* Variables below this are for use only by the hotplug pci core. */
struct list_head slot_list;
- struct kobject kobj;
+ struct pci_slot *pci_slot;
};
#define to_hotplug_slot(n) container_of(n, struct hotplug_slot, kobj)
-extern int pci_hp_register (struct hotplug_slot *slot);
-extern int pci_hp_deregister (struct hotplug_slot *slot);
+extern int pci_hp_register(struct hotplug_slot *, struct pci_bus *, int nr);
+extern int pci_hp_deregister(struct hotplug_slot *slot);
extern int __must_check pci_hp_change_slot_info (struct hotplug_slot *slot,
struct hotplug_slot_info *info);
-extern struct kset *pci_hotplug_slots_kset;
/* PCI Setting Record (Type 0) */
struct hpp_type0 {
@@ -227,9 +221,9 @@ struct hotplug_params {
#include <acpi/acpi.h>
#include <acpi/acpi_bus.h>
#include <acpi/actypes.h>
-extern acpi_status acpi_run_oshp(acpi_handle handle);
extern acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus,
struct hotplug_params *hpp);
+int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags);
int acpi_root_bridge(acpi_handle handle);
#endif
#endif
diff --git a/include/linux/percpu.h b/include/linux/percpu.h
index 4cdd393e71e1..3d1a7f6cc738 100644
--- a/include/linux/percpu.h
+++ b/include/linux/percpu.h
@@ -23,13 +23,35 @@
__attribute__((__section__(SHARED_ALIGNED_SECTION))) \
PER_CPU_ATTRIBUTES __typeof__(type) per_cpu__##name \
____cacheline_aligned_in_smp
+
+#define DEFINE_PER_CPU_PAGE_ALIGNED(type, name) \
+ __attribute__((__section__(".data.percpu.page_aligned"))) \
+ PER_CPU_ATTRIBUTES __typeof__(type) per_cpu__##name
+
+#ifdef CONFIG_HAVE_ZERO_BASED_PER_CPU
+#define DEFINE_PER_CPU_FIRST(type, name) \
+ __attribute__((__section__(".data.percpu.first"))) \
+ PER_CPU_ATTRIBUTES __typeof__(type) per_cpu__##name
#else
+#define DEFINE_PER_CPU_FIRST(type, name) \
+ DEFINE_PER_CPU(type, name)
+#endif
+
+#else /* !CONFIG_SMP */
+
#define DEFINE_PER_CPU(type, name) \
PER_CPU_ATTRIBUTES __typeof__(type) per_cpu__##name
#define DEFINE_PER_CPU_SHARED_ALIGNED(type, name) \
DEFINE_PER_CPU(type, name)
-#endif
+
+#define DEFINE_PER_CPU_PAGE_ALIGNED(type, name) \
+ DEFINE_PER_CPU(type, name)
+
+#define DEFINE_PER_CPU_FIRST(type, name) \
+ DEFINE_PER_CPU(type, name)
+
+#endif /* !CONFIG_SMP */
#define EXPORT_PER_CPU_SYMBOL(var) EXPORT_SYMBOL(per_cpu__##var)
#define EXPORT_PER_CPU_SYMBOL_GPL(var) EXPORT_SYMBOL_GPL(per_cpu__##var)
diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h
index 3261681c82a4..95ac21ab3a09 100644
--- a/include/linux/platform_device.h
+++ b/include/linux/platform_device.h
@@ -53,6 +53,7 @@ struct platform_driver {
int (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
+ struct pm_ext_ops *pm;
struct device_driver driver;
};
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 39a7ee859b67..4ad9de94449a 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -112,7 +112,9 @@ typedef struct pm_message {
int event;
} pm_message_t;
-/*
+/**
+ * struct pm_ops - device PM callbacks
+ *
* Several driver power state transitions are externally visible, affecting
* the state of pending I/O queues and (for drivers that touch hardware)
* interrupts, wakeups, DMA, and other hardware state. There may also be
@@ -120,6 +122,284 @@ typedef struct pm_message {
* to the rest of the driver stack (such as a driver that's ON gating off
* clocks which are not in active use).
*
+ * The externally visible transitions are handled with the help of the following
+ * callbacks included in this structure:
+ *
+ * @prepare: Prepare the device for the upcoming transition, but do NOT change
+ * its hardware state. Prevent new children of the device from being
+ * registered after @prepare() returns (the driver's subsystem and
+ * generally the rest of the kernel is supposed to prevent new calls to the
+ * probe method from being made too once @prepare() has succeeded). If
+ * @prepare() detects a situation it cannot handle (e.g. registration of a
+ * child already in progress), it may return -EAGAIN, so that the PM core
+ * can execute it once again (e.g. after the new child has been registered)
+ * to recover from the race condition. This method is executed for all
+ * kinds of suspend transitions and is followed by one of the suspend
+ * callbacks: @suspend(), @freeze(), or @poweroff().
+ * The PM core executes @prepare() for all devices before starting to
+ * execute suspend callbacks for any of them, so drivers may assume all of
+ * the other devices to be present and functional while @prepare() is being
+ * executed. In particular, it is safe to make GFP_KERNEL memory
+ * allocations from within @prepare(). However, drivers may NOT assume
+ * anything about the availability of the user space at that time and it
+ * is not correct to request firmware from within @prepare() (it's too
+ * late to do that). [To work around this limitation, drivers may
+ * register suspend and hibernation notifiers that are executed before the
+ * freezing of tasks.]
+ *
+ * @complete: Undo the changes made by @prepare(). This method is executed for
+ * all kinds of resume transitions, following one of the resume callbacks:
+ * @resume(), @thaw(), @restore(). Also called if the state transition
+ * fails before the driver's suspend callback (@suspend(), @freeze(),
+ * @poweroff()) can be executed (e.g. if the suspend callback fails for one
+ * of the other devices that the PM core has unsuccessfully attempted to
+ * suspend earlier).
+ * The PM core executes @complete() after it has executed the appropriate
+ * resume callback for all devices.
+ *
+ * @suspend: Executed before putting the system into a sleep state in which the
+ * contents of main memory are preserved. Quiesce the device, put it into
+ * a low power state appropriate for the upcoming system state (such as
+ * PCI_D3hot), and enable wakeup events as appropriate.
+ *
+ * @resume: Executed after waking the system up from a sleep state in which the
+ * contents of main memory were preserved. Put the device into the
+ * appropriate state, according to the information saved in memory by the
+ * preceding @suspend(). The driver starts working again, responding to
+ * hardware events and software requests. The hardware may have gone
+ * through a power-off reset, or it may have maintained state from the
+ * previous suspend() which the driver may rely on while resuming. On most
+ * platforms, there are no restrictions on availability of resources like
+ * clocks during @resume().
+ *
+ * @freeze: Hibernation-specific, executed before creating a hibernation image.
+ * Quiesce operations so that a consistent image can be created, but do NOT
+ * otherwise put the device into a low power device state and do NOT emit
+ * system wakeup events. Save in main memory the device settings to be
+ * used by @restore() during the subsequent resume from hibernation or by
+ * the subsequent @thaw(), if the creation of the image or the restoration
+ * of main memory contents from it fails.
+ *
+ * @thaw: Hibernation-specific, executed after creating a hibernation image OR
+ * if the creation of the image fails. Also executed after a failing
+ * attempt to restore the contents of main memory from such an image.
+ * Undo the changes made by the preceding @freeze(), so the device can be
+ * operated in the same way as immediately before the call to @freeze().
+ *
+ * @poweroff: Hibernation-specific, executed after saving a hibernation image.
+ * Quiesce the device, put it into a low power state appropriate for the
+ * upcoming system state (such as PCI_D3hot), and enable wakeup events as
+ * appropriate.
+ *
+ * @restore: Hibernation-specific, executed after restoring the contents of main
+ * memory from a hibernation image. Driver starts working again,
+ * responding to hardware events and software requests. Drivers may NOT
+ * make ANY assumptions about the hardware state right prior to @restore().
+ * On most platforms, there are no restrictions on availability of
+ * resources like clocks during @restore().
+ *
+ * All of the above callbacks, except for @complete(), return error codes.
+ * However, the error codes returned by the resume operations, @resume(),
+ * @thaw(), and @restore(), do not cause the PM core to abort the resume
+ * transition during which they are returned. The error codes returned in
+ * that cases are only printed by the PM core to the system logs for debugging
+ * purposes. Still, it is recommended that drivers only return error codes
+ * from their resume methods in case of an unrecoverable failure (i.e. when the
+ * device being handled refuses to resume and becomes unusable) to allow us to
+ * modify the PM core in the future, so that it can avoid attempting to handle
+ * devices that failed to resume and their children.
+ *
+ * It is allowed to unregister devices while the above callbacks are being
+ * executed. However, it is not allowed to unregister a device from within any
+ * of its own callbacks.
+ */
+
+struct pm_ops {
+ int (*prepare)(struct device *dev);
+ void (*complete)(struct device *dev);
+ int (*suspend)(struct device *dev);
+ int (*resume)(struct device *dev);
+ int (*freeze)(struct device *dev);
+ int (*thaw)(struct device *dev);
+ int (*poweroff)(struct device *dev);
+ int (*restore)(struct device *dev);
+};
+
+/**
+ * struct pm_ext_ops - extended device PM callbacks
+ *
+ * Some devices require certain operations related to suspend and hibernation
+ * to be carried out with interrupts disabled. Thus, 'struct pm_ext_ops' below
+ * is defined, adding callbacks to be executed with interrupts disabled to
+ * 'struct pm_ops'.
+ *
+ * The following callbacks included in 'struct pm_ext_ops' are executed with
+ * the nonboot CPUs switched off and with interrupts disabled on the only
+ * functional CPU. They also are executed with the PM core list of devices
+ * locked, so they must NOT unregister any devices.
+ *
+ * @suspend_noirq: Complete the operations of ->suspend() by carrying out any
+ * actions required for suspending the device that need interrupts to be
+ * disabled
+ *
+ * @resume_noirq: Prepare for the execution of ->resume() by carrying out any
+ * actions required for resuming the device that need interrupts to be
+ * disabled
+ *
+ * @freeze_noirq: Complete the operations of ->freeze() by carrying out any
+ * actions required for freezing the device that need interrupts to be
+ * disabled
+ *
+ * @thaw_noirq: Prepare for the execution of ->thaw() by carrying out any
+ * actions required for thawing the device that need interrupts to be
+ * disabled
+ *
+ * @poweroff_noirq: Complete the operations of ->poweroff() by carrying out any
+ * actions required for handling the device that need interrupts to be
+ * disabled
+ *
+ * @restore_noirq: Prepare for the execution of ->restore() by carrying out any
+ * actions required for restoring the operations of the device that need
+ * interrupts to be disabled
+ *
+ * All of the above callbacks return error codes, but the error codes returned
+ * by the resume operations, @resume_noirq(), @thaw_noirq(), and
+ * @restore_noirq(), do not cause the PM core to abort the resume transition
+ * during which they are returned. The error codes returned in that cases are
+ * only printed by the PM core to the system logs for debugging purposes.
+ * Still, as stated above, it is recommended that drivers only return error
+ * codes from their resume methods if the device being handled fails to resume
+ * and is not usable any more.
+ */
+
+struct pm_ext_ops {
+ struct pm_ops base;
+ int (*suspend_noirq)(struct device *dev);
+ int (*resume_noirq)(struct device *dev);
+ int (*freeze_noirq)(struct device *dev);
+ int (*thaw_noirq)(struct device *dev);
+ int (*poweroff_noirq)(struct device *dev);
+ int (*restore_noirq)(struct device *dev);
+};
+
+/**
+ * PM_EVENT_ messages
+ *
+ * The following PM_EVENT_ messages are defined for the internal use of the PM
+ * core, in order to provide a mechanism allowing the high level suspend and
+ * hibernation code to convey the necessary information to the device PM core
+ * code:
+ *
+ * ON No transition.
+ *
+ * FREEZE System is going to hibernate, call ->prepare() and ->freeze()
+ * for all devices.
+ *
+ * SUSPEND System is going to suspend, call ->prepare() and ->suspend()
+ * for all devices.
+ *
+ * HIBERNATE Hibernation image has been saved, call ->prepare() and
+ * ->poweroff() for all devices.
+ *
+ * QUIESCE Contents of main memory are going to be restored from a (loaded)
+ * hibernation image, call ->prepare() and ->freeze() for all
+ * devices.
+ *
+ * RESUME System is resuming, call ->resume() and ->complete() for all
+ * devices.
+ *
+ * THAW Hibernation image has been created, call ->thaw() and
+ * ->complete() for all devices.
+ *
+ * RESTORE Contents of main memory have been restored from a hibernation
+ * image, call ->restore() and ->complete() for all devices.
+ *
+ * RECOVER Creation of a hibernation image or restoration of the main
+ * memory contents from a hibernation image has failed, call
+ * ->thaw() and ->complete() for all devices.
+ */
+
+#define PM_EVENT_ON 0x0000
+#define PM_EVENT_FREEZE 0x0001
+#define PM_EVENT_SUSPEND 0x0002
+#define PM_EVENT_HIBERNATE 0x0004
+#define PM_EVENT_QUIESCE 0x0008
+#define PM_EVENT_RESUME 0x0010
+#define PM_EVENT_THAW 0x0020
+#define PM_EVENT_RESTORE 0x0040
+#define PM_EVENT_RECOVER 0x0080
+
+#define PM_EVENT_SLEEP (PM_EVENT_SUSPEND | PM_EVENT_HIBERNATE)
+
+#define PMSG_FREEZE ((struct pm_message){ .event = PM_EVENT_FREEZE, })
+#define PMSG_QUIESCE ((struct pm_message){ .event = PM_EVENT_QUIESCE, })
+#define PMSG_SUSPEND ((struct pm_message){ .event = PM_EVENT_SUSPEND, })
+#define PMSG_HIBERNATE ((struct pm_message){ .event = PM_EVENT_HIBERNATE, })
+#define PMSG_RESUME ((struct pm_message){ .event = PM_EVENT_RESUME, })
+#define PMSG_THAW ((struct pm_message){ .event = PM_EVENT_THAW, })
+#define PMSG_RESTORE ((struct pm_message){ .event = PM_EVENT_RESTORE, })
+#define PMSG_RECOVER ((struct pm_message){ .event = PM_EVENT_RECOVER, })
+#define PMSG_ON ((struct pm_message){ .event = PM_EVENT_ON, })
+
+/**
+ * Device power management states
+ *
+ * These state labels are used internally by the PM core to indicate the current
+ * status of a device with respect to the PM core operations.
+ *
+ * DPM_ON Device is regarded as operational. Set this way
+ * initially and when ->complete() is about to be called.
+ * Also set when ->prepare() fails.
+ *
+ * DPM_PREPARING Device is going to be prepared for a PM transition. Set
+ * when ->prepare() is about to be called.
+ *
+ * DPM_RESUMING Device is going to be resumed. Set when ->resume(),
+ * ->thaw(), or ->restore() is about to be called.
+ *
+ * DPM_SUSPENDING Device has been prepared for a power transition. Set
+ * when ->prepare() has just succeeded.
+ *
+ * DPM_OFF Device is regarded as inactive. Set immediately after
+ * ->suspend(), ->freeze(), or ->poweroff() has succeeded.
+ * Also set when ->resume()_noirq, ->thaw_noirq(), or
+ * ->restore_noirq() is about to be called.
+ *
+ * DPM_OFF_IRQ Device is in a "deep sleep". Set immediately after
+ * ->suspend_noirq(), ->freeze_noirq(), or
+ * ->poweroff_noirq() has just succeeded.
+ */
+
+enum dpm_state {
+ DPM_INVALID,
+ DPM_ON,
+ DPM_PREPARING,
+ DPM_RESUMING,
+ DPM_SUSPENDING,
+ DPM_OFF,
+ DPM_OFF_IRQ,
+};
+
+struct dev_pm_info {
+ pm_message_t power_state;
+ unsigned can_wakeup:1;
+ unsigned should_wakeup:1;
+ enum dpm_state status; /* Owned by the PM core */
+#ifdef CONFIG_PM_SLEEP
+ struct list_head entry;
+#endif
+};
+
+/*
+ * The PM_EVENT_ messages are also used by drivers implementing the legacy
+ * suspend framework, based on the ->suspend() and ->resume() callbacks common
+ * for suspend and hibernation transitions, according to the rules below.
+ */
+
+/* Necessary, because several drivers use PM_EVENT_PRETHAW */
+#define PM_EVENT_PRETHAW PM_EVENT_QUIESCE
+
+/*
* One transition is triggered by resume(), after a suspend() call; the
* message is implicit:
*
@@ -164,35 +444,13 @@ typedef struct pm_message {
* or from system low-power states such as standby or suspend-to-RAM.
*/
-#define PM_EVENT_ON 0
-#define PM_EVENT_FREEZE 1
-#define PM_EVENT_SUSPEND 2
-#define PM_EVENT_HIBERNATE 4
-#define PM_EVENT_PRETHAW 8
-
-#define PM_EVENT_SLEEP (PM_EVENT_SUSPEND | PM_EVENT_HIBERNATE)
-
-#define PMSG_FREEZE ((struct pm_message){ .event = PM_EVENT_FREEZE, })
-#define PMSG_PRETHAW ((struct pm_message){ .event = PM_EVENT_PRETHAW, })
-#define PMSG_SUSPEND ((struct pm_message){ .event = PM_EVENT_SUSPEND, })
-#define PMSG_HIBERNATE ((struct pm_message){ .event = PM_EVENT_HIBERNATE, })
-#define PMSG_ON ((struct pm_message){ .event = PM_EVENT_ON, })
-
-struct dev_pm_info {
- pm_message_t power_state;
- unsigned can_wakeup:1;
- unsigned should_wakeup:1;
- bool sleeping:1; /* Owned by the PM core */
-#ifdef CONFIG_PM_SLEEP
- struct list_head entry;
-#endif
-};
+#ifdef CONFIG_PM_SLEEP
+extern void device_pm_lock(void);
+extern void device_power_up(pm_message_t state);
+extern void device_resume(pm_message_t state);
+extern void device_pm_unlock(void);
extern int device_power_down(pm_message_t state);
-extern void device_power_up(void);
-extern void device_resume(void);
-
-#ifdef CONFIG_PM_SLEEP
extern int device_suspend(pm_message_t state);
extern int device_prepare_suspend(pm_message_t state);
diff --git a/include/linux/poison.h b/include/linux/poison.h
index 9f31683728fd..0d105a56e668 100644
--- a/include/linux/poison.h
+++ b/include/linux/poison.h
@@ -1,14 +1,20 @@
#ifndef _LINUX_POISON_H
#define _LINUX_POISON_H
+#ifdef CONFIG_ILLEGAL_POINTER_VALUE
+#define POISON_POINTER_DELTA CONFIG_ILLEGAL_POINTER_VALUE
+#else
+#define POISON_POINTER_DELTA 0L
+#endif
+
/********** include/linux/list.h **********/
/*
* These are non-NULL pointers that will result in page faults
* under normal circumstances, used to verify that nobody uses
* non-initialized list entries.
*/
-#define LIST_POISON1 ((void *) 0x00100100)
-#define LIST_POISON2 ((void *) 0x00200200)
+#define LIST_POISON1 ((void *) 0x00100100 + POISON_POINTER_DELTA)
+#define LIST_POISON2 ((void *) 0x00200200 + POISON_POINTER_DELTA)
/********** include/linux/timer.h **********/
/*
diff --git a/include/linux/preempt.h b/include/linux/preempt.h
index 23f0c54175cd..72b1a10a59b6 100644
--- a/include/linux/preempt.h
+++ b/include/linux/preempt.h
@@ -10,7 +10,7 @@
#include <linux/linkage.h>
#include <linux/list.h>
-#ifdef CONFIG_DEBUG_PREEMPT
+#if defined(CONFIG_DEBUG_PREEMPT) || defined(CONFIG_PREEMPT_TRACER)
extern void add_preempt_count(int val);
extern void sub_preempt_count(int val);
#else
@@ -52,6 +52,34 @@ do { \
preempt_check_resched(); \
} while (0)
+/* For debugging and tracer internals only! */
+#define add_preempt_count_notrace(val) \
+ do { preempt_count() += (val); } while (0)
+#define sub_preempt_count_notrace(val) \
+ do { preempt_count() -= (val); } while (0)
+#define inc_preempt_count_notrace() add_preempt_count_notrace(1)
+#define dec_preempt_count_notrace() sub_preempt_count_notrace(1)
+
+#define preempt_disable_notrace() \
+do { \
+ inc_preempt_count_notrace(); \
+ barrier(); \
+} while (0)
+
+#define preempt_enable_no_resched_notrace() \
+do { \
+ barrier(); \
+ dec_preempt_count_notrace(); \
+} while (0)
+
+/* preempt_check_resched is OK to trace */
+#define preempt_enable_notrace() \
+do { \
+ preempt_enable_no_resched_notrace(); \
+ barrier(); \
+ preempt_check_resched(); \
+} while (0)
+
#else
#define preempt_disable() do { } while (0)
@@ -59,6 +87,10 @@ do { \
#define preempt_enable() do { } while (0)
#define preempt_check_resched() do { } while (0)
+#define preempt_disable_notrace() do { } while (0)
+#define preempt_enable_no_resched_notrace() do { } while (0)
+#define preempt_enable_notrace() do { } while (0)
+
#endif
#ifdef CONFIG_PREEMPT_NOTIFIERS
diff --git a/include/linux/rcuclassic.h b/include/linux/rcuclassic.h
index b3aa05baab8a..8c774905dcfe 100644
--- a/include/linux/rcuclassic.h
+++ b/include/linux/rcuclassic.h
@@ -151,7 +151,10 @@ extern struct lockdep_map rcu_lock_map;
#define __synchronize_sched() synchronize_rcu()
+#define call_rcu_sched(head, func) call_rcu(head, func)
+
extern void __rcu_init(void);
+#define rcu_init_sched() do { } while (0)
extern void rcu_check_callbacks(int cpu, int user);
extern void rcu_restart_cpu(int cpu);
diff --git a/include/linux/rculist.h b/include/linux/rculist.h
new file mode 100644
index 000000000000..b0f39be08b6c
--- /dev/null
+++ b/include/linux/rculist.h
@@ -0,0 +1,373 @@
+#ifndef _LINUX_RCULIST_H
+#define _LINUX_RCULIST_H
+
+#ifdef __KERNEL__
+
+/*
+ * RCU-protected list version
+ */
+#include <linux/list.h>
+#include <linux/rcupdate.h>
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add_rcu(struct list_head *new,
+ struct list_head *prev, struct list_head *next)
+{
+ new->next = next;
+ new->prev = prev;
+ rcu_assign_pointer(prev->next, new);
+ next->prev = new;
+}
+
+/**
+ * list_add_rcu - add a new entry to rcu-protected list
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_add_rcu()
+ * or list_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ */
+static inline void list_add_rcu(struct list_head *new, struct list_head *head)
+{
+ __list_add_rcu(new, head, head->next);
+}
+
+/**
+ * list_add_tail_rcu - add a new entry to rcu-protected list
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_add_tail_rcu()
+ * or list_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ */
+static inline void list_add_tail_rcu(struct list_head *new,
+ struct list_head *head)
+{
+ __list_add_rcu(new, head->prev, head);
+}
+
+/**
+ * list_del_rcu - deletes entry from list without re-initialization
+ * @entry: the element to delete from the list.
+ *
+ * Note: list_empty() on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as list_del_rcu()
+ * or list_add_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * list_for_each_entry_rcu().
+ *
+ * Note that the caller is not permitted to immediately free
+ * the newly deleted entry. Instead, either synchronize_rcu()
+ * or call_rcu() must be used to defer freeing until an RCU
+ * grace period has elapsed.
+ */
+static inline void list_del_rcu(struct list_head *entry)
+{
+ __list_del(entry->prev, entry->next);
+ entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_replace_rcu - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * The @old entry will be replaced with the @new entry atomically.
+ * Note: @old should not be empty.
+ */
+static inline void list_replace_rcu(struct list_head *old,
+ struct list_head *new)
+{
+ new->next = old->next;
+ new->prev = old->prev;
+ rcu_assign_pointer(new->prev->next, new);
+ new->next->prev = new;
+ old->prev = LIST_POISON2;
+}
+
+/**
+ * list_splice_init_rcu - splice an RCU-protected list into an existing list.
+ * @list: the RCU-protected list to splice
+ * @head: the place in the list to splice the first list into
+ * @sync: function to sync: synchronize_rcu(), synchronize_sched(), ...
+ *
+ * @head can be RCU-read traversed concurrently with this function.
+ *
+ * Note that this function blocks.
+ *
+ * Important note: the caller must take whatever action is necessary to
+ * prevent any other updates to @head. In principle, it is possible
+ * to modify the list as soon as sync() begins execution.
+ * If this sort of thing becomes necessary, an alternative version
+ * based on call_rcu() could be created. But only if -really-
+ * needed -- there is no shortage of RCU API members.
+ */
+static inline void list_splice_init_rcu(struct list_head *list,
+ struct list_head *head,
+ void (*sync)(void))
+{
+ struct list_head *first = list->next;
+ struct list_head *last = list->prev;
+ struct list_head *at = head->next;
+
+ if (list_empty(head))
+ return;
+
+ /* "first" and "last" tracking list, so initialize it. */
+
+ INIT_LIST_HEAD(list);
+
+ /*
+ * At this point, the list body still points to the source list.
+ * Wait for any readers to finish using the list before splicing
+ * the list body into the new list. Any new readers will see
+ * an empty list.
+ */
+
+ sync();
+
+ /*
+ * Readers are finished with the source list, so perform splice.
+ * The order is important if the new list is global and accessible
+ * to concurrent RCU readers. Note that RCU readers are not
+ * permitted to traverse the prev pointers without excluding
+ * this function.
+ */
+
+ last->next = at;
+ rcu_assign_pointer(head->next, first);
+ first->prev = head;
+ at->prev = last;
+}
+
+/**
+ * list_for_each_rcu - iterate over an rcu-protected list
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_rcu(pos, head) \
+ for (pos = rcu_dereference((head)->next); \
+ prefetch(pos->next), pos != (head); \
+ pos = rcu_dereference(pos->next))
+
+#define __list_for_each_rcu(pos, head) \
+ for (pos = rcu_dereference((head)->next); \
+ pos != (head); \
+ pos = rcu_dereference(pos->next))
+
+/**
+ * list_for_each_entry_rcu - iterate over rcu list of given type
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the list_struct within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_entry_rcu(pos, head, member) \
+ for (pos = list_entry(rcu_dereference((head)->next), typeof(*pos), member); \
+ prefetch(pos->member.next), &pos->member != (head); \
+ pos = list_entry(rcu_dereference(pos->member.next), typeof(*pos), member))
+
+
+/**
+ * list_for_each_continue_rcu
+ * @pos: the &struct list_head to use as a loop cursor.
+ * @head: the head for your list.
+ *
+ * Iterate over an rcu-protected list, continuing after current point.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as list_add_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define list_for_each_continue_rcu(pos, head) \
+ for ((pos) = rcu_dereference((pos)->next); \
+ prefetch((pos)->next), (pos) != (head); \
+ (pos) = rcu_dereference((pos)->next))
+
+/**
+ * hlist_del_rcu - deletes entry from hash list without re-initialization
+ * @n: the element to delete from the hash list.
+ *
+ * Note: list_unhashed() on entry does not return true after this,
+ * the entry is in an undefined state. It is useful for RCU based
+ * lockfree traversal.
+ *
+ * In particular, it means that we can not poison the forward
+ * pointers that may still be used for walking the hash list.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry().
+ */
+static inline void hlist_del_rcu(struct hlist_node *n)
+{
+ __hlist_del(n);
+ n->pprev = LIST_POISON2;
+}
+
+/**
+ * hlist_replace_rcu - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * The @old entry will be replaced with the @new entry atomically.
+ */
+static inline void hlist_replace_rcu(struct hlist_node *old,
+ struct hlist_node *new)
+{
+ struct hlist_node *next = old->next;
+
+ new->next = next;
+ new->pprev = old->pprev;
+ rcu_assign_pointer(*new->pprev, new);
+ if (next)
+ new->next->pprev = &new->next;
+ old->pprev = LIST_POISON2;
+}
+
+/**
+ * hlist_add_head_rcu
+ * @n: the element to add to the hash list.
+ * @h: the list to add to.
+ *
+ * Description:
+ * Adds the specified element to the specified hlist,
+ * while permitting racing traversals.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry_rcu(), used to prevent memory-consistency
+ * problems on Alpha CPUs. Regardless of the type of CPU, the
+ * list-traversal primitive must be guarded by rcu_read_lock().
+ */
+static inline void hlist_add_head_rcu(struct hlist_node *n,
+ struct hlist_head *h)
+{
+ struct hlist_node *first = h->first;
+
+ n->next = first;
+ n->pprev = &h->first;
+ rcu_assign_pointer(h->first, n);
+ if (first)
+ first->pprev = &n->next;
+}
+
+/**
+ * hlist_add_before_rcu
+ * @n: the new element to add to the hash list.
+ * @next: the existing element to add the new element before.
+ *
+ * Description:
+ * Adds the specified element to the specified hlist
+ * before the specified node while permitting racing traversals.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry_rcu(), used to prevent memory-consistency
+ * problems on Alpha CPUs.
+ */
+static inline void hlist_add_before_rcu(struct hlist_node *n,
+ struct hlist_node *next)
+{
+ n->pprev = next->pprev;
+ n->next = next;
+ rcu_assign_pointer(*(n->pprev), n);
+ next->pprev = &n->next;
+}
+
+/**
+ * hlist_add_after_rcu
+ * @prev: the existing element to add the new element after.
+ * @n: the new element to add to the hash list.
+ *
+ * Description:
+ * Adds the specified element to the specified hlist
+ * after the specified node while permitting racing traversals.
+ *
+ * The caller must take whatever precautions are necessary
+ * (such as holding appropriate locks) to avoid racing
+ * with another list-mutation primitive, such as hlist_add_head_rcu()
+ * or hlist_del_rcu(), running on this same list.
+ * However, it is perfectly legal to run concurrently with
+ * the _rcu list-traversal primitives, such as
+ * hlist_for_each_entry_rcu(), used to prevent memory-consistency
+ * problems on Alpha CPUs.
+ */
+static inline void hlist_add_after_rcu(struct hlist_node *prev,
+ struct hlist_node *n)
+{
+ n->next = prev->next;
+ n->pprev = &prev->next;
+ rcu_assign_pointer(prev->next, n);
+ if (n->next)
+ n->next->pprev = &n->next;
+}
+
+/**
+ * hlist_for_each_entry_rcu - iterate over rcu list of given type
+ * @tpos: the type * to use as a loop cursor.
+ * @pos: the &struct hlist_node to use as a loop cursor.
+ * @head: the head for your list.
+ * @member: the name of the hlist_node within the struct.
+ *
+ * This list-traversal primitive may safely run concurrently with
+ * the _rcu list-mutation primitives such as hlist_add_head_rcu()
+ * as long as the traversal is guarded by rcu_read_lock().
+ */
+#define hlist_for_each_entry_rcu(tpos, pos, head, member) \
+ for (pos = rcu_dereference((head)->first); \
+ pos && ({ prefetch(pos->next); 1; }) && \
+ ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1; }); \
+ pos = rcu_dereference(pos->next))
+
+#endif /* __KERNEL__ */
+#endif
diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h
index d42dbec06083..e8b4039cfb2f 100644
--- a/include/linux/rcupdate.h
+++ b/include/linux/rcupdate.h
@@ -40,6 +40,7 @@
#include <linux/cpumask.h>
#include <linux/seqlock.h>
#include <linux/lockdep.h>
+#include <linux/completion.h>
/**
* struct rcu_head - callback structure for use with RCU
@@ -168,6 +169,27 @@ struct rcu_head {
(p) = (v); \
})
+/* Infrastructure to implement the synchronize_() primitives. */
+
+struct rcu_synchronize {
+ struct rcu_head head;
+ struct completion completion;
+};
+
+extern void wakeme_after_rcu(struct rcu_head *head);
+
+#define synchronize_rcu_xxx(name, func) \
+void name(void) \
+{ \
+ struct rcu_synchronize rcu; \
+ \
+ init_completion(&rcu.completion); \
+ /* Will wake me after RCU finished. */ \
+ func(&rcu.head, wakeme_after_rcu); \
+ /* Wait for it. */ \
+ wait_for_completion(&rcu.completion); \
+}
+
/**
* synchronize_sched - block until all CPUs have exited any non-preemptive
* kernel code sequences.
@@ -224,8 +246,8 @@ extern void call_rcu_bh(struct rcu_head *head,
/* Exported common interfaces */
extern void synchronize_rcu(void);
extern void rcu_barrier(void);
-extern long rcu_batches_completed(void);
-extern long rcu_batches_completed_bh(void);
+extern void rcu_barrier_bh(void);
+extern void rcu_barrier_sched(void);
/* Internal to kernel */
extern void rcu_init(void);
diff --git a/include/linux/rcupreempt.h b/include/linux/rcupreempt.h
index 8a05c7e20bc4..f04b64eca636 100644
--- a/include/linux/rcupreempt.h
+++ b/include/linux/rcupreempt.h
@@ -40,10 +40,39 @@
#include <linux/cpumask.h>
#include <linux/seqlock.h>
-#define rcu_qsctr_inc(cpu)
+struct rcu_dyntick_sched {
+ int dynticks;
+ int dynticks_snap;
+ int sched_qs;
+ int sched_qs_snap;
+ int sched_dynticks_snap;
+};
+
+DECLARE_PER_CPU(struct rcu_dyntick_sched, rcu_dyntick_sched);
+
+static inline void rcu_qsctr_inc(int cpu)
+{
+ struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);
+
+ rdssp->sched_qs++;
+}
#define rcu_bh_qsctr_inc(cpu)
#define call_rcu_bh(head, rcu) call_rcu(head, rcu)
+/**
+ * call_rcu_sched - Queue RCU callback for invocation after sched grace period.
+ * @head: structure to be used for queueing the RCU updates.
+ * @func: actual update function to be invoked after the grace period
+ *
+ * The update function will be invoked some time after a full
+ * synchronize_sched()-style grace period elapses, in other words after
+ * all currently executing preempt-disabled sections of code (including
+ * hardirq handlers, NMI handlers, and local_irq_save() blocks) have
+ * completed.
+ */
+extern void call_rcu_sched(struct rcu_head *head,
+ void (*func)(struct rcu_head *head));
+
extern void __rcu_read_lock(void) __acquires(RCU);
extern void __rcu_read_unlock(void) __releases(RCU);
extern int rcu_pending(int cpu);
@@ -55,6 +84,7 @@ extern int rcu_needs_cpu(int cpu);
extern void __synchronize_sched(void);
extern void __rcu_init(void);
+extern void rcu_init_sched(void);
extern void rcu_check_callbacks(int cpu, int user);
extern void rcu_restart_cpu(int cpu);
extern long rcu_batches_completed(void);
@@ -81,20 +111,20 @@ extern struct rcupreempt_trace *rcupreempt_trace_cpu(int cpu);
struct softirq_action;
#ifdef CONFIG_NO_HZ
-DECLARE_PER_CPU(long, dynticks_progress_counter);
+DECLARE_PER_CPU(struct rcu_dyntick_sched, rcu_dyntick_sched);
static inline void rcu_enter_nohz(void)
{
smp_mb(); /* CPUs seeing ++ must see prior RCU read-side crit sects */
- __get_cpu_var(dynticks_progress_counter)++;
- WARN_ON(__get_cpu_var(dynticks_progress_counter) & 0x1);
+ __get_cpu_var(rcu_dyntick_sched).dynticks++;
+ WARN_ON(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1);
}
static inline void rcu_exit_nohz(void)
{
- __get_cpu_var(dynticks_progress_counter)++;
smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */
- WARN_ON(!(__get_cpu_var(dynticks_progress_counter) & 0x1));
+ __get_cpu_var(rcu_dyntick_sched).dynticks++;
+ WARN_ON(!(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1));
}
#else /* CONFIG_NO_HZ */
diff --git a/include/linux/resume-trace.h b/include/linux/resume-trace.h
index f3f4f28c6960..c9ba2fdf807d 100644
--- a/include/linux/resume-trace.h
+++ b/include/linux/resume-trace.h
@@ -8,7 +8,7 @@ extern int pm_trace_enabled;
struct device;
extern void set_trace_device(struct device *);
-extern void generate_resume_trace(void *tracedata, unsigned int user);
+extern void generate_resume_trace(const void *tracedata, unsigned int user);
#define TRACE_DEVICE(dev) do { \
if (pm_trace_enabled) \
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index a2aec2c0cfb5..b358c704d102 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -246,6 +246,7 @@ enum rt_class_t
{
RT_TABLE_UNSPEC=0,
/* User defined values */
+ RT_TABLE_COMPAT=252,
RT_TABLE_DEFAULT=253,
RT_TABLE_MAIN=254,
RT_TABLE_LOCAL=255,
diff --git a/include/linux/sched.h b/include/linux/sched.h
index ae0be3c62375..2f6d361d6d86 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -134,7 +134,6 @@ extern unsigned long nr_running(void);
extern unsigned long nr_uninterruptible(void);
extern unsigned long nr_active(void);
extern unsigned long nr_iowait(void);
-extern unsigned long weighted_cpuload(const int cpu);
struct seq_file;
struct cfs_rq;
@@ -246,6 +245,8 @@ extern asmlinkage void schedule_tail(struct task_struct *prev);
extern void init_idle(struct task_struct *idle, int cpu);
extern void init_idle_bootup_task(struct task_struct *idle);
+extern int runqueue_is_locked(void);
+
extern cpumask_t nohz_cpu_mask;
#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ)
extern int select_nohz_load_balancer(int cpu);
@@ -294,10 +295,11 @@ extern void softlockup_tick(void);
extern void spawn_softlockup_task(void);
extern void touch_softlockup_watchdog(void);
extern void touch_all_softlockup_watchdogs(void);
-extern unsigned long softlockup_thresh;
+extern unsigned int softlockup_panic;
extern unsigned long sysctl_hung_task_check_count;
extern unsigned long sysctl_hung_task_timeout_secs;
extern unsigned long sysctl_hung_task_warnings;
+extern int softlockup_thresh;
#else
static inline void softlockup_tick(void)
{
@@ -823,23 +825,6 @@ extern int arch_reinit_sched_domains(void);
#endif /* CONFIG_SMP */
-/*
- * A runqueue laden with a single nice 0 task scores a weighted_cpuload of
- * SCHED_LOAD_SCALE. This function returns 1 if any cpu is laden with a
- * task of nice 0 or enough lower priority tasks to bring up the
- * weighted_cpuload
- */
-static inline int above_background_load(void)
-{
- unsigned long cpu;
-
- for_each_online_cpu(cpu) {
- if (weighted_cpuload(cpu) >= SCHED_LOAD_SCALE)
- return 1;
- }
- return 0;
-}
-
struct io_context; /* See blkdev.h */
#define NGROUPS_SMALL 32
#define NGROUPS_PER_BLOCK ((unsigned int)(PAGE_SIZE / sizeof(gid_t)))
@@ -921,8 +906,8 @@ struct sched_class {
void (*set_cpus_allowed)(struct task_struct *p,
const cpumask_t *newmask);
- void (*join_domain)(struct rq *rq);
- void (*leave_domain)(struct rq *rq);
+ void (*rq_online)(struct rq *rq);
+ void (*rq_offline)(struct rq *rq);
void (*switched_from) (struct rq *this_rq, struct task_struct *task,
int running);
@@ -1039,6 +1024,7 @@ struct task_struct {
#endif
int prio, static_prio, normal_prio;
+ unsigned int rt_priority;
const struct sched_class *sched_class;
struct sched_entity se;
struct sched_rt_entity rt;
@@ -1095,10 +1081,9 @@ struct task_struct {
pid_t pid;
pid_t tgid;
-#ifdef CONFIG_CC_STACKPROTECTOR
/* Canary value for the -fstack-protector gcc feature */
unsigned long stack_canary;
-#endif
+
/*
* pointers to (original) parent process, youngest child, younger sibling,
* older sibling, respectively. (p->father can be replaced with
@@ -1122,7 +1107,6 @@ struct task_struct {
int __user *set_child_tid; /* CLONE_CHILD_SETTID */
int __user *clear_child_tid; /* CLONE_CHILD_CLEARTID */
- unsigned int rt_priority;
cputime_t utime, stime, utimescaled, stimescaled;
cputime_t gtime;
cputime_t prev_utime, prev_stime;
@@ -1141,12 +1125,12 @@ struct task_struct {
gid_t gid,egid,sgid,fsgid;
struct group_info *group_info;
kernel_cap_t cap_effective, cap_inheritable, cap_permitted, cap_bset;
- unsigned securebits;
struct user_struct *user;
+ unsigned securebits;
#ifdef CONFIG_KEYS
+ unsigned char jit_keyring; /* default keyring to attach requested keys to */
struct key *request_key_auth; /* assumed request_key authority */
struct key *thread_keyring; /* keyring private to this thread */
- unsigned char jit_keyring; /* default keyring to attach requested keys to */
#endif
char comm[TASK_COMM_LEN]; /* executable name excluding path
- access with [gs]et_task_comm (which lock
@@ -1233,8 +1217,8 @@ struct task_struct {
# define MAX_LOCK_DEPTH 48UL
u64 curr_chain_key;
int lockdep_depth;
- struct held_lock held_locks[MAX_LOCK_DEPTH];
unsigned int lockdep_recursion;
+ struct held_lock held_locks[MAX_LOCK_DEPTH];
#endif
/* journalling filesystem info */
@@ -1262,10 +1246,6 @@ struct task_struct {
u64 acct_vm_mem1; /* accumulated virtual memory usage */
cputime_t acct_stimexpd;/* stime since last update */
#endif
-#ifdef CONFIG_NUMA
- struct mempolicy *mempolicy;
- short il_next;
-#endif
#ifdef CONFIG_CPUSETS
nodemask_t mems_allowed;
int cpuset_mems_generation;
@@ -1285,6 +1265,10 @@ struct task_struct {
struct list_head pi_state_list;
struct futex_pi_state *pi_state_cache;
#endif
+#ifdef CONFIG_NUMA
+ struct mempolicy *mempolicy;
+ short il_next;
+#endif
atomic_t fs_excl; /* holding fs exclusive resources */
struct rcu_head rcu;
@@ -1504,6 +1488,7 @@ static inline void put_task_struct(struct task_struct *t)
#define PF_SWAPWRITE 0x00800000 /* Allowed to write to swap */
#define PF_SPREAD_PAGE 0x01000000 /* Spread page cache over cpuset */
#define PF_SPREAD_SLAB 0x02000000 /* Spread some slab caches over cpuset */
+#define PF_THREAD_BOUND 0x04000000 /* Thread bound to specific cpu */
#define PF_MEMPOLICY 0x10000000 /* Non-default NUMA mempolicy */
#define PF_MUTEX_TESTER 0x20000000 /* Thread belongs to the rt mutex tester */
#define PF_FREEZER_SKIP 0x40000000 /* Freezer should not count it as freezeable */
@@ -1971,6 +1956,19 @@ static inline unsigned long *end_of_stack(struct task_struct *p)
extern void thread_info_cache_init(void);
+#ifdef CONFIG_DEBUG_STACK_USAGE
+static inline unsigned long stack_not_used(struct task_struct *p)
+{
+ unsigned long *n = end_of_stack(p);
+
+ do { /* Skip over canary */
+ n++;
+ } while (!*n);
+
+ return (unsigned long)n - (unsigned long)end_of_stack(p);
+}
+#endif
+
/* set thread flags in other task's structures
* - see asm/thread_info.h for TIF_xxxx flags available
*/
@@ -2026,6 +2024,19 @@ static inline int fatal_signal_pending(struct task_struct *p)
return signal_pending(p) && __fatal_signal_pending(p);
}
+static inline int signal_pending_state(long state, struct task_struct *p)
+{
+ if (!(state & (TASK_INTERRUPTIBLE | TASK_WAKEKILL)))
+ return 0;
+ if (!signal_pending(p))
+ return 0;
+
+ if (state & (__TASK_STOPPED | __TASK_TRACED))
+ return 0;
+
+ return (state & TASK_INTERRUPTIBLE) || __fatal_signal_pending(p);
+}
+
static inline int need_resched(void)
{
return unlikely(test_thread_flag(TIF_NEED_RESCHED));
@@ -2118,6 +2129,18 @@ static inline void arch_pick_mmap_layout(struct mm_struct *mm)
}
#endif
+#ifdef CONFIG_TRACING
+extern void
+__trace_special(void *__tr, void *__data,
+ unsigned long arg1, unsigned long arg2, unsigned long arg3);
+#else
+static inline void
+__trace_special(void *__tr, void *__data,
+ unsigned long arg1, unsigned long arg2, unsigned long arg3)
+{
+}
+#endif
+
extern long sched_setaffinity(pid_t pid, const cpumask_t *new_mask);
extern long sched_getaffinity(pid_t pid, cpumask_t *mask);
@@ -2212,6 +2235,8 @@ static inline void mm_init_owner(struct mm_struct *mm, struct task_struct *p)
}
#endif /* CONFIG_MM_OWNER */
+#define TASK_STATE_TO_CHAR_STR "RSDTtZX"
+
#endif /* __KERNEL__ */
#endif
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 387e428f1cdf..b9a76c972084 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -733,7 +733,7 @@ struct spi_board_info {
* controller_data goes to spi_device.controller_data,
* irq is copied too
*/
- char modalias[KOBJ_NAME_LEN];
+ char modalias[32];
const void *platform_data;
void *controller_data;
int irq;
diff --git a/include/linux/ssb/ssb_driver_gige.h b/include/linux/ssb/ssb_driver_gige.h
index 01fbdf5fef22..942e38736901 100644
--- a/include/linux/ssb/ssb_driver_gige.h
+++ b/include/linux/ssb/ssb_driver_gige.h
@@ -100,7 +100,7 @@ extern char * nvram_get(const char *name);
/* Get the device MAC address */
static inline void ssb_gige_get_macaddr(struct pci_dev *pdev, u8 *macaddr)
{
-#ifdef CONFIG_BCM947XX
+#ifdef CONFIG_BCM47XX
char *res = nvram_get("et0macaddr");
if (res)
memcpy(macaddr, res, 6);
diff --git a/include/linux/stackprotector.h b/include/linux/stackprotector.h
new file mode 100644
index 000000000000..6f3e54c704c0
--- /dev/null
+++ b/include/linux/stackprotector.h
@@ -0,0 +1,16 @@
+#ifndef _LINUX_STACKPROTECTOR_H
+#define _LINUX_STACKPROTECTOR_H 1
+
+#include <linux/compiler.h>
+#include <linux/sched.h>
+#include <linux/random.h>
+
+#ifdef CONFIG_CC_STACKPROTECTOR
+# include <asm/stackprotector.h>
+#else
+static inline void boot_init_stack_canary(void)
+{
+}
+#endif
+
+#endif
diff --git a/include/linux/sunrpc/gss_krb5.h b/include/linux/sunrpc/gss_krb5.h
index a10f1fb0bf7c..5bb227e00b41 100644
--- a/include/linux/sunrpc/gss_krb5.h
+++ b/include/linux/sunrpc/gss_krb5.h
@@ -40,6 +40,12 @@
#include <linux/sunrpc/gss_err.h>
#include <linux/sunrpc/gss_asn1.h>
+/* Maximum checksum function output for the supported crypto algorithms */
+#define GSS_KRB5_MAX_CKSUM_LEN (20)
+
+/* Maximum blocksize for the supported crypto algorithms */
+#define GSS_KRB5_MAX_BLOCKSIZE (16)
+
struct krb5_ctx {
int initiate; /* 1 = initiating, 0 = accepting */
struct crypto_blkcipher *enc;
@@ -51,6 +57,9 @@ struct krb5_ctx {
extern spinlock_t krb5_seq_lock;
+/* The length of the Kerberos GSS token header */
+#define GSS_KRB5_TOK_HDR_LEN (16)
+
#define KG_TOK_MIC_MSG 0x0101
#define KG_TOK_WRAP_MSG 0x0201
@@ -110,6 +119,22 @@ enum seal_alg {
#define ENCTYPE_DES3_CBC_SHA1 0x0010
#define ENCTYPE_UNKNOWN 0x01ff
+/*
+ * This compile-time check verifies that we will not exceed the
+ * slack space allotted by the client and server auth_gss code
+ * before they call gss_wrap().
+ */
+#define GSS_KRB5_SLACK_CHECK \
+ BUILD_BUG_ON(GSS_KRB5_TOK_HDR_LEN /* gss token header */ \
+ + GSS_KRB5_MAX_CKSUM_LEN /* gss token checksum */ \
+ + GSS_KRB5_MAX_BLOCKSIZE /* confounder */ \
+ + GSS_KRB5_MAX_BLOCKSIZE /* possible padding */ \
+ + GSS_KRB5_TOK_HDR_LEN /* encrypted hdr in v2 token */\
+ + GSS_KRB5_MAX_CKSUM_LEN /* encryption hmac */ \
+ + 4 + 4 /* RPC verifier */ \
+ + GSS_KRB5_TOK_HDR_LEN \
+ + GSS_KRB5_MAX_CKSUM_LEN > RPC_MAX_AUTH_SIZE)
+
s32
make_checksum(char *, char *header, int hdrlen, struct xdr_buf *body,
int body_offset, struct xdr_netobj *cksum);
@@ -154,3 +179,6 @@ s32
krb5_get_seq_num(struct crypto_blkcipher *key,
unsigned char *cksum,
unsigned char *buf, int *direction, u32 *seqnum);
+
+int
+shift_head_data(struct xdr_buf *buf, unsigned int base, unsigned int shiftlen);
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
index 4b54c5fdcfd9..dc69068d94c7 100644
--- a/include/linux/sunrpc/svc.h
+++ b/include/linux/sunrpc/svc.h
@@ -22,7 +22,7 @@
/*
* This is the RPC server thread function prototype
*/
-typedef void (*svc_thread_fn)(struct svc_rqst *);
+typedef int (*svc_thread_fn)(void *);
/*
*
@@ -80,7 +80,6 @@ struct svc_serv {
struct module * sv_module; /* optional module to count when
* adding threads */
svc_thread_fn sv_function; /* main function for threads */
- int sv_kill_signal; /* signal to kill threads */
};
/*
@@ -388,8 +387,8 @@ struct svc_rqst *svc_prepare_thread(struct svc_serv *serv,
struct svc_pool *pool);
void svc_exit_thread(struct svc_rqst *);
struct svc_serv * svc_create_pooled(struct svc_program *, unsigned int,
- void (*shutdown)(struct svc_serv*),
- svc_thread_fn, int sig, struct module *);
+ void (*shutdown)(struct svc_serv*), svc_thread_fn,
+ struct module *);
int svc_set_num_threads(struct svc_serv *, struct svc_pool *, int);
void svc_destroy(struct svc_serv *);
int svc_process(struct svc_rqst *);
diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h
index 05eb4664d0dd..ef2e3a20bf3b 100644
--- a/include/linux/sunrpc/svc_rdma.h
+++ b/include/linux/sunrpc/svc_rdma.h
@@ -72,7 +72,7 @@ extern atomic_t rdma_stat_sq_prod;
*/
struct svc_rdma_op_ctxt {
struct svc_rdma_op_ctxt *read_hdr;
- struct list_head free_list;
+ int hdr_count;
struct xdr_buf arg;
struct list_head dto_q;
enum ib_wr_opcode wr_op;
@@ -86,6 +86,31 @@ struct svc_rdma_op_ctxt {
struct page *pages[RPCSVC_MAXPAGES];
};
+/*
+ * NFS_ requests are mapped on the client side by the chunk lists in
+ * the RPCRDMA header. During the fetching of the RPC from the client
+ * and the writing of the reply to the client, the memory in the
+ * client and the memory in the server must be mapped as contiguous
+ * vaddr/len for access by the hardware. These data strucures keep
+ * these mappings.
+ *
+ * For an RDMA_WRITE, the 'sge' maps the RPC REPLY. For RDMA_READ, the
+ * 'sge' in the svc_rdma_req_map maps the server side RPC reply and the
+ * 'ch' field maps the read-list of the RPCRDMA header to the 'sge'
+ * mapping of the reply.
+ */
+struct svc_rdma_chunk_sge {
+ int start; /* sge no for this chunk */
+ int count; /* sge count for this chunk */
+};
+struct svc_rdma_req_map {
+ unsigned long count;
+ union {
+ struct kvec sge[RPCSVC_MAXPAGES];
+ struct svc_rdma_chunk_sge ch[RPCSVC_MAXPAGES];
+ };
+};
+
#define RDMACTXT_F_LAST_CTXT 2
struct svcxprt_rdma {
@@ -93,7 +118,6 @@ struct svcxprt_rdma {
struct rdma_cm_id *sc_cm_id; /* RDMA connection id */
struct list_head sc_accept_q; /* Conn. waiting accept */
int sc_ord; /* RDMA read limit */
- wait_queue_head_t sc_read_wait;
int sc_max_sge;
int sc_sq_depth; /* Depth of SQ */
@@ -104,12 +128,8 @@ struct svcxprt_rdma {
struct ib_pd *sc_pd;
+ atomic_t sc_dma_used;
atomic_t sc_ctxt_used;
- struct list_head sc_ctxt_free;
- int sc_ctxt_cnt;
- int sc_ctxt_bump;
- int sc_ctxt_max;
- spinlock_t sc_ctxt_lock;
struct list_head sc_rq_dto_q;
spinlock_t sc_rq_dto_lock;
struct ib_qp *sc_qp;
@@ -173,6 +193,8 @@ extern int svc_rdma_post_recv(struct svcxprt_rdma *);
extern int svc_rdma_create_listen(struct svc_serv *, int, struct sockaddr *);
extern struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *);
extern void svc_rdma_put_context(struct svc_rdma_op_ctxt *, int);
+extern struct svc_rdma_req_map *svc_rdma_get_req_map(void);
+extern void svc_rdma_put_req_map(struct svc_rdma_req_map *);
extern void svc_sq_reap(struct svcxprt_rdma *);
extern void svc_rq_reap(struct svcxprt_rdma *);
extern struct svc_xprt_class svc_rdma_class;
diff --git a/include/linux/suspend.h b/include/linux/suspend.h
index a6977423baf7..e8e69159af71 100644
--- a/include/linux/suspend.h
+++ b/include/linux/suspend.h
@@ -86,6 +86,11 @@ typedef int __bitwise suspend_state_t;
* that implement @begin(), but platforms implementing @begin() should
* also provide a @end() which cleans up transitions aborted before
* @enter().
+ *
+ * @recover: Recover the platform from a suspend failure.
+ * Called by the PM core if the suspending of devices fails.
+ * This callback is optional and should only be implemented by platforms
+ * which require special recovery actions in that situation.
*/
struct platform_suspend_ops {
int (*valid)(suspend_state_t state);
@@ -94,6 +99,7 @@ struct platform_suspend_ops {
int (*enter)(suspend_state_t state);
void (*finish)(void);
void (*end)(void);
+ void (*recover)(void);
};
#ifdef CONFIG_SUSPEND
@@ -149,7 +155,7 @@ extern void mark_free_pages(struct zone *zone);
* The methods in this structure allow a platform to carry out special
* operations required by it during a hibernation transition.
*
- * All the methods below must be implemented.
+ * All the methods below, except for @recover(), must be implemented.
*
* @begin: Tell the platform driver that we're starting hibernation.
* Called right after shrinking memory and before freezing devices.
@@ -189,6 +195,11 @@ extern void mark_free_pages(struct zone *zone);
* @restore_cleanup: Clean up after a failing image restoration.
* Called right after the nonboot CPUs have been enabled and before
* thawing devices (runs with IRQs on).
+ *
+ * @recover: Recover the platform from a failure to suspend devices.
+ * Called by the PM core if the suspending of devices during hibernation
+ * fails. This callback is optional and should only be implemented by
+ * platforms which require special recovery actions in that situation.
*/
struct platform_hibernation_ops {
int (*begin)(void);
@@ -200,6 +211,7 @@ struct platform_hibernation_ops {
void (*leave)(void);
int (*pre_restore)(void);
void (*restore_cleanup)(void);
+ void (*recover)(void);
};
#ifdef CONFIG_HIBERNATION
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h
index 7858eac40aa7..84d92bb06333 100644
--- a/include/linux/sysfs.h
+++ b/include/linux/sysfs.h
@@ -20,6 +20,8 @@
struct kobject;
struct module;
+extern int kobject_set_name(struct kobject *kobj, const char *name, ...)
+ __attribute__((format(printf, 2, 3)));
/* FIXME
* The *owner field is no longer used, but leave around
* until the tree gets cleaned up fully.
@@ -115,6 +117,7 @@ void sysfs_remove_file_from_group(struct kobject *kobj,
const struct attribute *attr, const char *group);
void sysfs_notify(struct kobject *kobj, char *dir, char *attr);
+void sysfs_printk_last_file(void);
extern int __must_check sysfs_init(void);
@@ -137,7 +140,7 @@ static inline void sysfs_remove_dir(struct kobject *kobj)
static inline int sysfs_rename_dir(struct kobject *kobj, const char *new_name)
{
- return 0;
+ return kobject_set_name(kobj, "%s", new_name);
}
static inline int sysfs_move_dir(struct kobject *kobj,
@@ -221,6 +224,10 @@ static inline int __must_check sysfs_init(void)
return 0;
}
+static inline void sysfs_printk_last_file(void)
+{
+}
+
#endif /* CONFIG_SYSFS */
#endif /* _SYSFS_H_ */
diff --git a/include/linux/time.h b/include/linux/time.h
index d32ef0ad4c0a..e15206a7e82e 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -6,6 +6,7 @@
#ifdef __KERNEL__
# include <linux/cache.h>
# include <linux/seqlock.h>
+# include <linux/math64.h>
#endif
#ifndef _STRUCT_TIMESPEC
@@ -169,18 +170,13 @@ extern struct timeval ns_to_timeval(const s64 nsec);
* timespec_add_ns - Adds nanoseconds to a timespec
* @a: pointer to timespec to be incremented
* @ns: unsigned nanoseconds value to be added
+ *
+ * This must always be inlined because its used from the x86-64 vdso,
+ * which cannot call other kernel functions.
*/
-static inline void timespec_add_ns(struct timespec *a, u64 ns)
+static __always_inline void timespec_add_ns(struct timespec *a, u64 ns)
{
- ns += a->tv_nsec;
- while(unlikely(ns >= NSEC_PER_SEC)) {
- /* The following asm() prevents the compiler from
- * optimising this loop into a modulo operation. */
- asm("" : "+r"(ns));
-
- ns -= NSEC_PER_SEC;
- a->tv_sec++;
- }
+ a->tv_sec += __iter_div_u64_rem(a->tv_nsec + ns, NSEC_PER_SEC, &ns);
a->tv_nsec = ns;
}
#endif /* __KERNEL__ */
diff --git a/include/linux/topology.h b/include/linux/topology.h
index 24f3d2282e11..2158fc0d5a56 100644
--- a/include/linux/topology.h
+++ b/include/linux/topology.h
@@ -179,4 +179,17 @@ void arch_update_cpu_topology(void);
#endif
#endif /* CONFIG_NUMA */
+#ifndef topology_physical_package_id
+#define topology_physical_package_id(cpu) ((void)(cpu), -1)
+#endif
+#ifndef topology_core_id
+#define topology_core_id(cpu) ((void)(cpu), 0)
+#endif
+#ifndef topology_thread_siblings
+#define topology_thread_siblings(cpu) cpumask_of_cpu(cpu)
+#endif
+#ifndef topology_core_siblings
+#define topology_core_siblings(cpu) cpumask_of_cpu(cpu)
+#endif
+
#endif /* _LINUX_TOPOLOGY_H */
diff --git a/include/linux/uio_driver.h b/include/linux/uio_driver.h
index 973386d439da..cf65e964102b 100644
--- a/include/linux/uio_driver.h
+++ b/include/linux/uio_driver.h
@@ -53,6 +53,7 @@ struct uio_device;
* @mmap: mmap operation for this uio device
* @open: open operation for this uio device
* @release: release operation for this uio device
+ * @irqcontrol: disable/enable irqs when 0/1 is written to /dev/uioX
*/
struct uio_info {
struct uio_device *uio_dev;
@@ -66,6 +67,7 @@ struct uio_info {
int (*mmap)(struct uio_info *info, struct vm_area_struct *vma);
int (*open)(struct uio_info *info, struct inode *inode);
int (*release)(struct uio_info *info, struct inode *inode);
+ int (*irqcontrol)(struct uio_info *info, s32 irq_on);
};
extern int __must_check
diff --git a/include/linux/usb.h b/include/linux/usb.h
index c08689ea9b4b..3f29c1f577c7 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -293,7 +293,7 @@ struct usb_devmap {
struct usb_bus {
struct device *controller; /* host/master side hardware */
int busnum; /* Bus number (in order of reg) */
- char *bus_name; /* stable id (PCI slot_name etc) */
+ const char *bus_name; /* stable id (PCI slot_name etc) */
u8 uses_dma; /* Does the host controller use DMA? */
u8 otg_port; /* 0, or number of OTG/HNP port */
unsigned is_b_host:1; /* true during some HNP roleswitches */
@@ -375,6 +375,7 @@ struct usb_tt;
* FIXME -- complete doc
* @authenticated: Crypto authentication passed
* @wusb: device is Wireless USB
+ * @delayed_reset: delayed reset is in progress, or not
* @string_langid: language ID for strings
* @product: iProduct string, if present (static)
* @manufacturer: iManufacturer string, if present (static)
@@ -442,6 +443,7 @@ struct usb_device {
unsigned authorized:1;
unsigned authenticated:1;
unsigned wusb:1;
+ unsigned delayed_reset:1;
int string_langid;
/* static strings from the device */
@@ -499,6 +501,7 @@ extern int usb_lock_device_for_reset(struct usb_device *udev,
extern int usb_reset_device(struct usb_device *dev);
extern int usb_reset_composite_device(struct usb_device *dev,
struct usb_interface *iface);
+extern void usb_dev_reset_delayed(struct usb_device *usb_dev);
extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id);
@@ -972,6 +975,8 @@ struct usbdrv_wrap {
* added to this driver by preventing the sysfs file from being created.
* @supports_autosuspend: if set to 0, the USB core will not allow autosuspend
* for interfaces bound to this driver.
+ * @soft_unbind: if set to 1, the USB core will not kill URBs and disable
+ * endpoints before calling the driver's disconnect method.
*
* USB interface drivers must provide a name, probe() and disconnect()
* methods, and an id_table. Other driver fields are optional.
@@ -1012,6 +1017,7 @@ struct usb_driver {
struct usbdrv_wrap drvwrap;
unsigned int no_dynamic_id:1;
unsigned int supports_autosuspend:1;
+ unsigned int soft_unbind:1;
};
#define to_usb_driver(d) container_of(d, struct usb_driver, drvwrap.driver)
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
new file mode 100644
index 000000000000..747c3a49cdc9
--- /dev/null
+++ b/include/linux/usb/composite.h
@@ -0,0 +1,338 @@
+/*
+ * composite.h -- framework for usb gadgets which are composite devices
+ *
+ * Copyright (C) 2006-2008 David Brownell
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __LINUX_USB_COMPOSITE_H
+#define __LINUX_USB_COMPOSITE_H
+
+/*
+ * This framework is an optional layer on top of the USB Gadget interface,
+ * making it easier to build (a) Composite devices, supporting multiple
+ * functions within any single configuration, and (b) Multi-configuration
+ * devices, also supporting multiple functions but without necessarily
+ * having more than one function per configuration.
+ *
+ * Example: a device with a single configuration supporting both network
+ * link and mass storage functions is a composite device. Those functions
+ * might alternatively be packaged in individual configurations, but in
+ * the composite model the host can use both functions at the same time.
+ */
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+
+struct usb_configuration;
+
+/**
+ * struct usb_function - describes one function of a configuration
+ * @name: For diagnostics, identifies the function.
+ * @strings: tables of strings, keyed by identifiers assigned during bind()
+ * and by language IDs provided in control requests
+ * @descriptors: Table of full (or low) speed descriptors, using interface and
+ * string identifiers assigned during @bind(). If this pointer is null,
+ * the function will not be available at full speed (or at low speed).
+ * @hs_descriptors: Table of high speed descriptors, using interface and
+ * string identifiers assigned during @bind(). If this pointer is null,
+ * the function will not be available at high speed.
+ * @config: assigned when @usb_add_function() is called; this is the
+ * configuration with which this function is associated.
+ * @bind: Before the gadget can register, all of its functions bind() to the
+ * available resources including string and interface identifiers used
+ * in interface or class descriptors; endpoints; I/O buffers; and so on.
+ * @unbind: Reverses @bind; called as a side effect of unregistering the
+ * driver which added this function.
+ * @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may
+ * initialize usb_ep.driver data at this time (when it is used).
+ * Note that setting an interface to its current altsetting resets
+ * interface state, and that all interfaces have a disabled state.
+ * @get_alt: Returns the active altsetting. If this is not provided,
+ * then only altsetting zero is supported.
+ * @disable: (REQUIRED) Indicates the function should be disabled. Reasons
+ * include host resetting or reconfiguring the gadget, and disconnection.
+ * @setup: Used for interface-specific control requests.
+ * @suspend: Notifies functions when the host stops sending USB traffic.
+ * @resume: Notifies functions when the host restarts USB traffic.
+ *
+ * A single USB function uses one or more interfaces, and should in most
+ * cases support operation at both full and high speeds. Each function is
+ * associated by @usb_add_function() with a one configuration; that function
+ * causes @bind() to be called so resources can be allocated as part of
+ * setting up a gadget driver. Those resources include endpoints, which
+ * should be allocated using @usb_ep_autoconfig().
+ *
+ * To support dual speed operation, a function driver provides descriptors
+ * for both high and full speed operation. Except in rare cases that don't
+ * involve bulk endpoints, each speed needs different endpoint descriptors.
+ *
+ * Function drivers choose their own strategies for managing instance data.
+ * The simplest strategy just declares it "static', which means the function
+ * can only be activated once. If the function needs to be exposed in more
+ * than one configuration at a given speed, it needs to support multiple
+ * usb_function structures (one for each configuration).
+ *
+ * A more complex strategy might encapsulate a @usb_function structure inside
+ * a driver-specific instance structure to allows multiple activations. An
+ * example of multiple activations might be a CDC ACM function that supports
+ * two or more distinct instances within the same configuration, providing
+ * several independent logical data links to a USB host.
+ */
+struct usb_function {
+ const char *name;
+ struct usb_gadget_strings **strings;
+ struct usb_descriptor_header **descriptors;
+ struct usb_descriptor_header **hs_descriptors;
+
+ struct usb_configuration *config;
+
+ /* REVISIT: bind() functions can be marked __init, which
+ * makes trouble for section mismatch analysis. See if
+ * we can't restructure things to avoid mismatching.
+ * Related: unbind() may kfree() but bind() won't...
+ */
+
+ /* configuration management: bind/unbind */
+ int (*bind)(struct usb_configuration *,
+ struct usb_function *);
+ void (*unbind)(struct usb_configuration *,
+ struct usb_function *);
+
+ /* runtime state management */
+ int (*set_alt)(struct usb_function *,
+ unsigned interface, unsigned alt);
+ int (*get_alt)(struct usb_function *,
+ unsigned interface);
+ void (*disable)(struct usb_function *);
+ int (*setup)(struct usb_function *,
+ const struct usb_ctrlrequest *);
+ void (*suspend)(struct usb_function *);
+ void (*resume)(struct usb_function *);
+
+ /* internals */
+ struct list_head list;
+};
+
+int usb_add_function(struct usb_configuration *, struct usb_function *);
+
+int usb_interface_id(struct usb_configuration *, struct usb_function *);
+
+/**
+ * ep_choose - select descriptor endpoint at current device speed
+ * @g: gadget, connected and running at some speed
+ * @hs: descriptor to use for high speed operation
+ * @fs: descriptor to use for full or low speed operation
+ */
+static inline struct usb_endpoint_descriptor *
+ep_choose(struct usb_gadget *g, struct usb_endpoint_descriptor *hs,
+ struct usb_endpoint_descriptor *fs)
+{
+ if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+ return hs;
+ return fs;
+}
+
+#define MAX_CONFIG_INTERFACES 16 /* arbitrary; max 255 */
+
+/**
+ * struct usb_configuration - represents one gadget configuration
+ * @label: For diagnostics, describes the configuration.
+ * @strings: Tables of strings, keyed by identifiers assigned during @bind()
+ * and by language IDs provided in control requests.
+ * @descriptors: Table of descriptors preceding all function descriptors.
+ * Examples include OTG and vendor-specific descriptors.
+ * @bind: Called from @usb_add_config() to allocate resources unique to this
+ * configuration and to call @usb_add_function() for each function used.
+ * @unbind: Reverses @bind; called as a side effect of unregistering the
+ * driver which added this configuration.
+ * @setup: Used to delegate control requests that aren't handled by standard
+ * device infrastructure or directed at a specific interface.
+ * @bConfigurationValue: Copied into configuration descriptor.
+ * @iConfiguration: Copied into configuration descriptor.
+ * @bmAttributes: Copied into configuration descriptor.
+ * @bMaxPower: Copied into configuration descriptor.
+ * @cdev: assigned by @usb_add_config() before calling @bind(); this is
+ * the device associated with this configuration.
+ *
+ * Configurations are building blocks for gadget drivers structured around
+ * function drivers. Simple USB gadgets require only one function and one
+ * configuration, and handle dual-speed hardware by always providing the same
+ * functionality. Slightly more complex gadgets may have more than one
+ * single-function configuration at a given speed; or have configurations
+ * that only work at one speed.
+ *
+ * Composite devices are, by definition, ones with configurations which
+ * include more than one function.
+ *
+ * The lifecycle of a usb_configuration includes allocation, initialization
+ * of the fields described above, and calling @usb_add_config() to set up
+ * internal data and bind it to a specific device. The configuration's
+ * @bind() method is then used to initialize all the functions and then
+ * call @usb_add_function() for them.
+ *
+ * Those functions would normally be independant of each other, but that's
+ * not mandatory. CDC WMC devices are an example where functions often
+ * depend on other functions, with some functions subsidiary to others.
+ * Such interdependency may be managed in any way, so long as all of the
+ * descriptors complete by the time the composite driver returns from
+ * its bind() routine.
+ */
+struct usb_configuration {
+ const char *label;
+ struct usb_gadget_strings **strings;
+ const struct usb_descriptor_header **descriptors;
+
+ /* REVISIT: bind() functions can be marked __init, which
+ * makes trouble for section mismatch analysis. See if
+ * we can't restructure things to avoid mismatching...
+ */
+
+ /* configuration management: bind/unbind */
+ int (*bind)(struct usb_configuration *);
+ void (*unbind)(struct usb_configuration *);
+ int (*setup)(struct usb_configuration *,
+ const struct usb_ctrlrequest *);
+
+ /* fields in the config descriptor */
+ u8 bConfigurationValue;
+ u8 iConfiguration;
+ u8 bmAttributes;
+ u8 bMaxPower;
+
+ struct usb_composite_dev *cdev;
+
+ /* internals */
+ struct list_head list;
+ struct list_head functions;
+ u8 next_interface_id;
+ unsigned highspeed:1;
+ unsigned fullspeed:1;
+ struct usb_function *interface[MAX_CONFIG_INTERFACES];
+};
+
+int usb_add_config(struct usb_composite_dev *,
+ struct usb_configuration *);
+
+/**
+ * struct usb_composite_driver - groups configurations into a gadget
+ * @name: For diagnostics, identifies the driver.
+ * @dev: Template descriptor for the device, including default device
+ * identifiers.
+ * @strings: tables of strings, keyed by identifiers assigned during bind()
+ * and language IDs provided in control requests
+ * @bind: (REQUIRED) Used to allocate resources that are shared across the
+ * whole device, such as string IDs, and add its configurations using
+ * @usb_add_config(). This may fail by returning a negative errno
+ * value; it should return zero on successful initialization.
+ * @unbind: Reverses @bind(); called as a side effect of unregistering
+ * this driver.
+ *
+ * Devices default to reporting self powered operation. Devices which rely
+ * on bus powered operation should report this in their @bind() method.
+ *
+ * Before returning from @bind, various fields in the template descriptor
+ * may be overridden. These include the idVendor/idProduct/bcdDevice values
+ * normally to bind the appropriate host side driver, and the three strings
+ * (iManufacturer, iProduct, iSerialNumber) normally used to provide user
+ * meaningful device identifiers. (The strings will not be defined unless
+ * they are defined in @dev and @strings.) The correct ep0 maxpacket size
+ * is also reported, as defined by the underlying controller driver.
+ */
+struct usb_composite_driver {
+ const char *name;
+ const struct usb_device_descriptor *dev;
+ struct usb_gadget_strings **strings;
+
+ /* REVISIT: bind() functions can be marked __init, which
+ * makes trouble for section mismatch analysis. See if
+ * we can't restructure things to avoid mismatching...
+ */
+
+ int (*bind)(struct usb_composite_dev *);
+ int (*unbind)(struct usb_composite_dev *);
+};
+
+extern int usb_composite_register(struct usb_composite_driver *);
+extern void usb_composite_unregister(struct usb_composite_driver *);
+
+
+/**
+ * struct usb_composite_device - represents one composite usb gadget
+ * @gadget: read-only, abstracts the gadget's usb peripheral controller
+ * @req: used for control responses; buffer is pre-allocated
+ * @bufsiz: size of buffer pre-allocated in @req
+ * @config: the currently active configuration
+ *
+ * One of these devices is allocated and initialized before the
+ * associated device driver's bind() is called.
+ *
+ * OPEN ISSUE: it appears that some WUSB devices will need to be
+ * built by combining a normal (wired) gadget with a wireless one.
+ * This revision of the gadget framework should probably try to make
+ * sure doing that won't hurt too much.
+ *
+ * One notion for how to handle Wireless USB devices involves:
+ * (a) a second gadget here, discovery mechanism TBD, but likely
+ * needing separate "register/unregister WUSB gadget" calls;
+ * (b) updates to usb_gadget to include flags "is it wireless",
+ * "is it wired", plus (presumably in a wrapper structure)
+ * bandgroup and PHY info;
+ * (c) presumably a wireless_ep wrapping a usb_ep, and reporting
+ * wireless-specific parameters like maxburst and maxsequence;
+ * (d) configurations that are specific to wireless links;
+ * (e) function drivers that understand wireless configs and will
+ * support wireless for (additional) function instances;
+ * (f) a function to support association setup (like CBAF), not
+ * necessarily requiring a wireless adapter;
+ * (g) composite device setup that can create one or more wireless
+ * configs, including appropriate association setup support;
+ * (h) more, TBD.
+ */
+struct usb_composite_dev {
+ struct usb_gadget *gadget;
+ struct usb_request *req;
+ unsigned bufsiz;
+
+ struct usb_configuration *config;
+
+ /* internals */
+ struct usb_device_descriptor desc;
+ struct list_head configs;
+ struct usb_composite_driver *driver;
+ u8 next_string_id;
+
+ spinlock_t lock;
+
+ /* REVISIT use and existence of lock ... */
+};
+
+extern int usb_string_id(struct usb_composite_dev *c);
+
+/* messaging utils */
+#define DBG(d, fmt, args...) \
+ dev_dbg(&(d)->gadget->dev , fmt , ## args)
+#define VDBG(d, fmt, args...) \
+ dev_vdbg(&(d)->gadget->dev , fmt , ## args)
+#define ERROR(d, fmt, args...) \
+ dev_err(&(d)->gadget->dev , fmt , ## args)
+#define WARN(d, fmt, args...) \
+ dev_warn(&(d)->gadget->dev , fmt , ## args)
+#define INFO(d, fmt, args...) \
+ dev_info(&(d)->gadget->dev , fmt , ## args)
+
+#endif /* __LINUX_USB_COMPOSITE_H */
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index cf468fbdbf8e..0ebedaec075d 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -858,6 +858,25 @@ int usb_descriptor_fillbuf(void *, unsigned,
int usb_gadget_config_buf(const struct usb_config_descriptor *config,
void *buf, unsigned buflen, const struct usb_descriptor_header **desc);
+/* copy a NULL-terminated vector of descriptors */
+struct usb_descriptor_header **usb_copy_descriptors(
+ struct usb_descriptor_header **);
+
+/* return copy of endpoint descriptor given original descriptor set */
+struct usb_endpoint_descriptor *usb_find_endpoint(
+ struct usb_descriptor_header **src,
+ struct usb_descriptor_header **copy,
+ struct usb_endpoint_descriptor *match);
+
+/**
+ * usb_free_descriptors - free descriptors returned by usb_copy_descriptors()
+ * @v: vector of descriptors
+ */
+static inline void usb_free_descriptors(struct usb_descriptor_header **v)
+{
+ kfree(v);
+}
+
/*-------------------------------------------------------------------------*/
/* utility wrapping a simple endpoint selection policy */
diff --git a/include/linux/usb/wusb-wa.h b/include/linux/usb/wusb-wa.h
new file mode 100644
index 000000000000..c0743a2b69f8
--- /dev/null
+++ b/include/linux/usb/wusb-wa.h
@@ -0,0 +1,281 @@
+/*
+ * Wireless USB Wire Adapter constants and structures.
+ *
+ * Copyright (C) 2005-2006 Intel Corporation.
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ * FIXME: organize properly, group logically
+ *
+ * All the event structures are defined in uwb/spec.h, as they are
+ * common to the WHCI and WUSB radio control interfaces.
+ *
+ * References:
+ * [WUSB] Wireless Universal Serial Bus Specification, revision 1.0, ch8
+ */
+#ifndef __LINUX_USB_WUSB_WA_H
+#define __LINUX_USB_WUSB_WA_H
+
+/**
+ * Radio Command Request for the Radio Control Interface
+ *
+ * Radio Control Interface command and event codes are the same as
+ * WHCI, and listed in include/linux/uwb.h:UWB_RC_{CMD,EVT}_*
+ */
+enum {
+ WA_EXEC_RC_CMD = 40, /* Radio Control command Request */
+};
+
+/* Wireless Adapter Requests ([WUSB] table 8-51) */
+enum {
+ WUSB_REQ_ADD_MMC_IE = 20,
+ WUSB_REQ_REMOVE_MMC_IE = 21,
+ WUSB_REQ_SET_NUM_DNTS = 22,
+ WUSB_REQ_SET_CLUSTER_ID = 23,
+ WUSB_REQ_SET_DEV_INFO = 24,
+ WUSB_REQ_GET_TIME = 25,
+ WUSB_REQ_SET_STREAM_IDX = 26,
+ WUSB_REQ_SET_WUSB_MAS = 27,
+};
+
+
+/* Wireless Adapter WUSB Channel Time types ([WUSB] table 8-52) */
+enum {
+ WUSB_TIME_ADJ = 0,
+ WUSB_TIME_BPST = 1,
+ WUSB_TIME_WUSB = 2,
+};
+
+enum {
+ WA_ENABLE = 0x01,
+ WA_RESET = 0x02,
+ RPIPE_PAUSE = 0x1,
+};
+
+/* Responses from Get Status request ([WUSB] section 8.3.1.6) */
+enum {
+ WA_STATUS_ENABLED = 0x01,
+ WA_STATUS_RESETTING = 0x02
+};
+
+enum rpipe_crs {
+ RPIPE_CRS_CTL = 0x01,
+ RPIPE_CRS_ISO = 0x02,
+ RPIPE_CRS_BULK = 0x04,
+ RPIPE_CRS_INTR = 0x08
+};
+
+/**
+ * RPipe descriptor ([WUSB] section 8.5.2.11)
+ *
+ * FIXME: explain rpipes
+ */
+struct usb_rpipe_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ __le16 wRPipeIndex;
+ __le16 wRequests;
+ __le16 wBlocks; /* rw if 0 */
+ __le16 wMaxPacketSize; /* rw? */
+ u8 bHSHubAddress; /* reserved: 0 */
+ u8 bHSHubPort; /* ??? FIXME ??? */
+ u8 bSpeed; /* rw: xfer rate 'enum uwb_phy_rate' */
+ u8 bDeviceAddress; /* rw: Target device address */
+ u8 bEndpointAddress; /* rw: Target EP address */
+ u8 bDataSequence; /* ro: Current Data sequence */
+ __le32 dwCurrentWindow; /* ro */
+ u8 bMaxDataSequence; /* ro?: max supported seq */
+ u8 bInterval; /* rw: */
+ u8 bOverTheAirInterval; /* rw: */
+ u8 bmAttribute; /* ro? */
+ u8 bmCharacteristics; /* ro? enum rpipe_attr, supported xsactions */
+ u8 bmRetryOptions; /* rw? */
+ __le16 wNumTransactionErrors; /* rw */
+} __attribute__ ((packed));
+
+/**
+ * Wire Adapter Notification types ([WUSB] sections 8.4.5 & 8.5.4)
+ *
+ * These are the notifications coming on the notification endpoint of
+ * an HWA and a DWA.
+ */
+enum wa_notif_type {
+ DWA_NOTIF_RWAKE = 0x91,
+ DWA_NOTIF_PORTSTATUS = 0x92,
+ WA_NOTIF_TRANSFER = 0x93,
+ HWA_NOTIF_BPST_ADJ = 0x94,
+ HWA_NOTIF_DN = 0x95,
+};
+
+/**
+ * Wire Adapter notification header
+ *
+ * Notifications coming from a wire adapter use a common header
+ * defined in [WUSB] sections 8.4.5 & 8.5.4.
+ */
+struct wa_notif_hdr {
+ u8 bLength;
+ u8 bNotifyType; /* enum wa_notif_type */
+} __attribute__((packed));
+
+/**
+ * HWA DN Received notification [(WUSB] section 8.5.4.2)
+ *
+ * The DNData is specified in WUSB1.0[7.6]. For each device
+ * notification we received, we just need to dispatch it. However,
+ * note a WUSB1.0 errata from Dec'06 (page 24) says that the
+ * notification data doesn't have to include the WUSB header. As there
+ * are still firmwares out there that don't comply w/ the errata, we
+ * leave the option to have it.
+ *
+ * @dndata: this is really an array of notifications, but all start
+ * with the same header.
+ */
+struct hwa_notif_dn {
+ struct wa_notif_hdr hdr;
+ u8 bSourceDeviceAddr; /* from errata 2005/07 */
+ union {
+ u8 bmAttributes;
+ DECL_BF_LE2(
+ u8 reserved:7,
+ u8 secure:1
+ ) __attribute__((packed));
+ };
+ struct wusb_dn_hdr dndata[];
+} __attribute__((packed));
+
+/* [WUSB] section 8.3.3 */
+enum wa_xfer_type {
+ WA_XFER_TYPE_CTL = 0x80,
+ WA_XFER_TYPE_BI = 0x81, /* bulk/interrupt */
+ WA_XFER_TYPE_ISO = 0x82,
+ WA_XFER_RESULT = 0x83,
+ WA_XFER_ABORT = 0x84,
+};
+
+/* [WUSB] section 8.3.3 */
+struct wa_xfer_hdr {
+ u8 bLength; /* 0x18 */
+ u8 bRequestType; /* 0x80 WA_REQUEST_TYPE_CTL */
+ __le16 wRPipe; /* RPipe index */
+ __le32 dwTransferID; /* Host-assigned ID */
+ __le32 dwTransferLength; /* Length of data to xfer */
+ u8 bTransferSegment;
+} __attribute__((packed));
+
+struct wa_xfer_ctl {
+ struct wa_xfer_hdr hdr;
+ u8 bmAttribute;
+ __le16 wReserved;
+ struct usb_ctrlrequest baSetupData;
+} __attribute__((packed));
+
+struct wa_xfer_bi {
+ struct wa_xfer_hdr hdr;
+ u8 bReserved;
+ __le16 wReserved;
+} __attribute__((packed));
+
+struct wa_xfer_hwaiso {
+ struct wa_xfer_hdr hdr;
+ u8 bReserved;
+ __le16 wPresentationTime;
+ __le32 dwNumOfPackets;
+ /* FIXME: u8 pktdata[]? */
+} __attribute__((packed));
+
+/* [WUSB] section 8.3.3.5 */
+struct wa_xfer_abort {
+ u8 bLength;
+ u8 bRequestType;
+ __le16 wRPipe; /* RPipe index */
+ __le32 dwTransferID; /* Host-assigned ID */
+} __attribute__((packed));
+
+/**
+ * WA Transfer Complete notification ([WUSB] section 8.3.3.3)
+ *
+ */
+struct wa_notif_xfer {
+ struct wa_notif_hdr hdr;
+ u8 bEndpoint;
+ u8 Reserved;
+} __attribute__((packed));
+
+/** Transfer result basic codes [WUSB] table 8-15 */
+enum {
+ WA_XFER_STATUS_SUCCESS,
+ WA_XFER_STATUS_HALTED,
+ WA_XFER_STATUS_DATA_BUFFER_ERROR,
+ WA_XFER_STATUS_BABBLE,
+ WA_XFER_RESERVED,
+ WA_XFER_STATUS_NOT_FOUND,
+ WA_XFER_STATUS_INSUFFICIENT_RESOURCE,
+ WA_XFER_STATUS_TRANSACTION_ERROR,
+ WA_XFER_STATUS_ABORTED,
+ WA_XFER_STATUS_RPIPE_NOT_READY,
+ WA_XFER_INVALID_FORMAT,
+ WA_XFER_UNEXPECTED_SEGMENT_NUMBER,
+ WA_XFER_STATUS_RPIPE_TYPE_MISMATCH,
+};
+
+/** [WUSB] section 8.3.3.4 */
+struct wa_xfer_result {
+ struct wa_notif_hdr hdr;
+ __le32 dwTransferID;
+ __le32 dwTransferLength;
+ u8 bTransferSegment;
+ u8 bTransferStatus;
+ __le32 dwNumOfPackets;
+} __attribute__((packed));
+
+/**
+ * Wire Adapter Class Descriptor ([WUSB] section 8.5.2.7).
+ *
+ * NOTE: u16 fields are read Little Endian from the hardware.
+ *
+ * @bNumPorts is the original max number of devices that the host can
+ * connect; we might chop this so the stack can handle
+ * it. In case you need to access it, use wusbhc->ports_max
+ * if it is a Wireless USB WA.
+ */
+struct usb_wa_descriptor {
+ u8 bLength;
+ u8 bDescriptorType;
+ u16 bcdWAVersion;
+ u8 bNumPorts; /* don't use!! */
+ u8 bmAttributes; /* Reserved == 0 */
+ u16 wNumRPipes;
+ u16 wRPipeMaxBlock;
+ u8 bRPipeBlockSize;
+ u8 bPwrOn2PwrGood;
+ u8 bNumMMCIEs;
+ u8 DeviceRemovable; /* FIXME: in DWA this is up to 16 bytes */
+} __attribute__((packed));
+
+/**
+ * HWA Device Information Buffer (WUSB1.0[T8.54])
+ */
+struct hwa_dev_info {
+ u8 bmDeviceAvailability[32]; /* FIXME: ignored for now */
+ u8 bDeviceAddress;
+ __le16 wPHYRates;
+ u8 bmDeviceAttribute;
+} __attribute__((packed));
+
+#endif /* #ifndef __LINUX_USB_WUSB_WA_H */
diff --git a/include/linux/usb/wusb.h b/include/linux/usb/wusb.h
new file mode 100644
index 000000000000..248354ab48ac
--- /dev/null
+++ b/include/linux/usb/wusb.h
@@ -0,0 +1,419 @@
+/*
+ * Wireless USB Standard Definitions
+ * Event Size Tables
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ * FIXME: organize properly, group logically
+ *
+ * All the event structures are defined in uwb/spec.h, as they are
+ * common to the WHCI and WUSB radio control interfaces.
+ */
+
+#ifndef __WUSB_H__
+#define __WUSB_H__
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/uwb/spec.h>
+#include <linux/usb/ch9.h>
+#include <linux/param.h>
+
+/**
+ * WUSB Information Element header
+ *
+ * I don't know why, they decided to make it different to the MBOA MAC
+ * IE Header; beats me.
+ */
+struct wuie_hdr {
+ u8 bLength;
+ u8 bIEIdentifier;
+} __attribute__((packed));
+
+enum {
+ WUIE_ID_WCTA = 0x80,
+ WUIE_ID_CONNECTACK,
+ WUIE_ID_HOST_INFO,
+ WUIE_ID_CHANGE_ANNOUNCE,
+ WUIE_ID_DEVICE_DISCONNECT,
+ WUIE_ID_HOST_DISCONNECT,
+ WUIE_ID_KEEP_ALIVE = 0x89,
+ WUIE_ID_ISOCH_DISCARD,
+ WUIE_ID_RESET_DEVICE,
+};
+
+/**
+ * Maximum number of array elements in a WUSB IE.
+ *
+ * WUSB1.0[7.5 before table 7-38] says that in WUSB IEs that
+ * are "arrays" have to limited to 4 elements. So we define it
+ * like that to ease up and submit only the neeed size.
+ */
+#define WUIE_ELT_MAX 4
+
+/**
+ * Wrapper for the data that defines a CHID, a CDID or a CK
+ *
+ * WUSB defines that CHIDs, CDIDs and CKs are a 16 byte string of
+ * data. In order to avoid confusion and enforce types, we wrap it.
+ *
+ * Make it packed, as we use it in some hw defintions.
+ */
+struct wusb_ckhdid {
+ u8 data[16];
+} __attribute__((packed));
+
+const static
+struct wusb_ckhdid wusb_ckhdid_zero = { .data = { 0 } };
+
+#define WUSB_CKHDID_STRSIZE (3 * sizeof(struct wusb_ckhdid) + 1)
+
+/**
+ * WUSB IE: Host Information (WUSB1.0[7.5.2])
+ *
+ * Used to provide information about the host to the Wireless USB
+ * devices in range (CHID can be used as an ASCII string).
+ *
+ * NOTE that because so far, bits 6 to 15 of bmAttributes are
+ * reserved, I just declare 8 bits and play with that so we don't have
+ * to do endianess conversion.
+ */
+struct wuie_host_info {
+ struct wuie_hdr hdr;
+ DECL_BF_LE5(
+ u8 connect_avail:2,
+ u8 p2p_drd:1,
+ u8 stream_index:3,
+ u8 reserved1:2,
+ ) __attribute__((packed));
+ u8 reserved2; /* See NOTE */
+ struct wusb_ckhdid CHID;
+} __attribute__((packed));
+
+/**
+ * WUSB IE: Connect Ack (WUSB1.0[7.5.1])
+ *
+ * Used to acknowledge device connect requests. See note for
+ * WUIE_ELT_MAX.
+ */
+struct wuie_connect_ack {
+ struct wuie_hdr hdr;
+ struct {
+ struct wusb_ckhdid CDID;
+ u8 bDeviceAddress; /* 0 means unused */
+ u8 bReserved;
+ } blk[WUIE_ELT_MAX];
+} __attribute__((packed));
+
+/**
+ * WUSB IE Host Information Element, Connect Availability
+ *
+ * WUSB1.0[7.5.2], bmAttributes description
+ */
+enum {
+ WUIE_HI_CAP_RECONNECT = 0,
+ WUIE_HI_CAP_LIMITED,
+ WUIE_HI_CAP_RESERVED,
+ WUIE_HI_CAP_ALL,
+};
+
+/**
+ * WUSB IE: Channel Stop (WUSB1.0[7.5.8])
+ *
+ * Tells devices the host is going to stop sending MMCs and will dissapear.
+ */
+struct wuie_channel_stop {
+ struct wuie_hdr hdr;
+ union {
+ u8 bmAttributes;
+ DECL_BF_LE2(
+ u8 RemoteWakeup:1,
+ u8 reserved:1
+ ) __attribute__((packed));
+ };
+ u8 timestamp[3];
+} __attribute__((packed));
+
+/**
+ * WUSB IE: Keepalive (WUSB1.0[7.5.9])
+ *
+ * Ask device(s) to send keepalives.
+ */
+struct wuie_keep_alive {
+ struct wuie_hdr hdr;
+ u8 bDeviceAddress[WUIE_ELT_MAX];
+} __attribute__((packed));
+
+/**
+ * WUSB IE: Reset device (WUSB1.0[7.5.11])
+ *
+ * Tell device to reset; in all truth, we can fit 4 CDIDs, but we only
+ * use it for one at the time...
+ *
+ * In any case, this request is a wee bit silly: why don't they target
+ * by address??
+ */
+struct wuie_reset {
+ struct wuie_hdr hdr;
+ struct wusb_ckhdid CDID;
+} __attribute__((packed));
+
+/**
+ * WUSB IE: Disconnect device (WUSB1.0[7.5.11])
+ *
+ * Tell device to disconnect; we can fit 4 addresses, but we only use
+ * it for one at the time...
+ */
+struct wuie_disconnect {
+ struct wuie_hdr hdr;
+ u8 bDeviceAddress;
+ u8 padding;
+} __attribute__((packed));
+
+/**
+ * WUSB IE: Host disconnect ([WUSB] section 7.5.5)
+ *
+ * Tells all connected devices to disconnect.
+ */
+struct wuie_host_disconnect {
+ struct wuie_hdr hdr;
+} __attribute__((packed));
+
+/**
+ * Device notification header
+ *
+ * WUSB1.0[5.1] and [7.6]
+ *
+ * This is the most common parts of the header; depending on the value
+ * of @pid, some other stuff might be added.
+ *
+ * This is a WIRE FORMAT header--check 'struct wa_notif_hdr'
+ */
+struct wusb_pkt_hdr {
+ union {
+ u8 bmAttributes;
+ DECL_BF_LE3(
+ u8 endp_number:4,
+ u8 pid:3,
+ u8 direction:1
+ ) __attribute__((packed));
+ };
+ union {
+ u8 bmStatus;
+ DECL_BF_LE2(
+ u8 seq_number:5,
+ u8 flags_hshk:3
+ ) __attribute__((packed));
+ };
+} __attribute__((packed));
+
+/**
+ * WUSB Device Notification header (WUSB1.0[7.6])
+ */
+struct wusb_dn_hdr {
+ u8 bType;
+ u8 notifdata[];
+} __attribute__((packed));
+
+/** Device Notification codes (WUSB1.0[Table 7-54]) */
+enum WUSB_DN {
+ WUSB_DN_CONNECT = 0x01,
+ WUSB_DN_DISCONNECT = 0x02,
+ WUSB_DN_EPRDY = 0x03,
+ WUSB_DN_MASAVAILCHANGED = 0x04,
+ WUSB_DN_RWAKE = 0x05,
+ WUSB_DN_SLEEP = 0x06,
+ WUSB_DN_ALIVE = 0x07,
+};
+
+/** WUSB Device Notification Connect */
+struct wusb_dn_connect {
+ struct wusb_dn_hdr hdr;
+ union {
+ u16 bmAttributes;
+ __le16 bmAttributes_le;
+ DECL_BF_LE4(
+ u8 prev_dev_addr:8,
+ u8 new_connection:1,
+ u8 beacon_behaviour:2,
+ u8 reserved:5
+ ) __attribute__((packed));
+ };
+ struct wusb_ckhdid CDID;
+} __attribute__((packed));
+
+/** Device is alive (aka: pong) (WUSB1.0[7.6.7]) */
+struct wusb_dn_alive {
+ struct wusb_dn_hdr hdr;
+} __attribute__((packed));
+
+/** Device is disconnecting (WUSB1.0[7.6.2]) */
+struct wusb_dn_disconnect {
+ struct wusb_dn_hdr hdr;
+} __attribute__((packed));
+
+/* General constants */
+enum {
+ WUSB_TRUST_TIMEOUT_MS = 4000, /* [WUSB] section 4.15.1 */
+};
+
+static inline size_t ckhdid_printf(char *pr_ckhdid, size_t size,
+ const struct wusb_ckhdid *ckhdid)
+{
+ return scnprintf(pr_ckhdid, size,
+ "%02hx %02hx %02hx %02hx %02hx %02hx %02hx %02hx "
+ "%02hx %02hx %02hx %02hx %02hx %02hx %02hx %02hx",
+ ckhdid->data[0], ckhdid->data[1],
+ ckhdid->data[2], ckhdid->data[3],
+ ckhdid->data[4], ckhdid->data[5],
+ ckhdid->data[6], ckhdid->data[7],
+ ckhdid->data[8], ckhdid->data[9],
+ ckhdid->data[10], ckhdid->data[11],
+ ckhdid->data[12], ckhdid->data[13],
+ ckhdid->data[14], ckhdid->data[15]);
+}
+
+/*
+ * WUSB Crypto stuff (WUSB1.0[6])
+ */
+
+extern const char *wusb_et_name(u8);
+
+/**
+ * WUSB key index WUSB1.0[7.3.2.4], for usage when setting keys for
+ * the host or the device.
+ */
+union wusb_key_index {
+ u8 value;
+ DECL_BF_LE4(
+ u8 index:4,
+ u8 type:2,
+ u8 originator:1,
+ u8 reserved:1
+ );
+} __attribute__((packed));
+
+#define WUSB_KEY_INDEX_TYPE_PTK 0 /* for HWA only */
+#define WUSB_KEY_INDEX_TYPE_ASSOC 1
+#define WUSB_KEY_INDEX_TYPE_GTK 2
+#define WUSB_KEY_INDEX_ORIGINATOR_HOST 0
+#define WUSB_KEY_INDEX_ORIGINATOR_DEVICE 1
+
+/* A CCM Nonce, defined in WUSB1.0[6.4.1] */
+struct aes_ccm_nonce {
+ u8 sfn[6]; /* Little Endian */
+ u8 tkid[3]; /* LE */
+ struct uwb_dev_addr dest_addr;
+ struct uwb_dev_addr src_addr;
+} __attribute__((packed));
+
+/* A CCM operation label, defined on WUSB1.0[6.5.x] */
+struct aes_ccm_label {
+ u8 data[14];
+} __attribute__((packed));
+
+/*
+ * Input to the key derivation sequence defined in
+ * WUSB1.0[6.5.1]. Rest of the data is in the CCM Nonce passed to the
+ * PRF function.
+ */
+struct wusb_keydvt_in {
+ u8 hnonce[16];
+ u8 dnonce[16];
+} __attribute__((packed));
+
+/*
+ * Output from the key derivation sequence defined in
+ * WUSB1.0[6.5.1].
+ */
+struct wusb_keydvt_out {
+ u8 kck[16];
+ u8 ptk[16];
+} __attribute__((packed));
+
+/* Pseudo Random Function WUSB1.0[6.5] */
+extern int wusb_crypto_init(void);
+extern void wusb_crypto_exit(void);
+extern ssize_t wusb_prf(void *out, size_t out_size,
+ const u8 key[16], const struct aes_ccm_nonce *_n,
+ const struct aes_ccm_label *a,
+ const void *b, size_t blen, size_t len);
+
+static inline int wusb_prf_64(void *out, size_t out_size, const u8 key[16],
+ const struct aes_ccm_nonce *n,
+ const struct aes_ccm_label *a,
+ const void *b, size_t blen)
+{
+ return wusb_prf(out, out_size, key, n, a, b, blen, 64);
+}
+
+static inline int wusb_prf_128(void *out, size_t out_size, const u8 key[16],
+ const struct aes_ccm_nonce *n,
+ const struct aes_ccm_label *a,
+ const void *b, size_t blen)
+{
+ return wusb_prf(out, out_size, key, n, a, b, blen, 128);
+}
+
+static inline int wusb_prf_256(void *out, size_t out_size, const u8 key[16],
+ const struct aes_ccm_nonce *n,
+ const struct aes_ccm_label *a,
+ const void *b, size_t blen)
+{
+ return wusb_prf(out, out_size, key, n, a, b, blen, 256);
+}
+
+/* Key derivation WUSB1.0[6.5.1] */
+static inline int wusb_key_derive(struct wusb_keydvt_out *keydvt_out,
+ const u8 key[16],
+ const struct aes_ccm_nonce *n,
+ const struct wusb_keydvt_in *keydvt_in)
+{
+ const struct aes_ccm_label a = { .data = "Pair-wise keys" };
+ return wusb_prf_256(keydvt_out, sizeof(*keydvt_out), key, n, &a,
+ keydvt_in, sizeof(*keydvt_in));
+}
+
+/*
+ * Out-of-band MIC Generation WUSB1.0[6.5.2]
+ *
+ * Compute the MIC over @key, @n and @hs and place it in @mic_out.
+ *
+ * @mic_out: Where to place the 8 byte MIC tag
+ * @key: KCK from the derivation process
+ * @n: CCM nonce, n->sfn == 0, TKID as established in the
+ * process.
+ * @hs: Handshake struct for phase 2 of the 4-way.
+ * hs->bStatus and hs->bReserved are zero.
+ * hs->bMessageNumber is 2 (WUSB1.0[7.3.2.5.2]
+ * hs->dest_addr is the device's USB address padded with 0
+ * hs->src_addr is the hosts's UWB device address
+ * hs->mic is ignored (as we compute that value).
+ */
+static inline int wusb_oob_mic(u8 mic_out[8], const u8 key[16],
+ const struct aes_ccm_nonce *n,
+ const struct usb_handshake *hs)
+{
+ const struct aes_ccm_label a = { .data = "out-of-bandMIC" };
+ return wusb_prf_64(mic_out, 8, key, n, &a,
+ hs, sizeof(*hs) - sizeof(hs->MIC));
+}
+
+#endif /* #ifndef __WUSB_H__ */
diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h
index 3118ede2c67b..0044d9b4cb85 100644
--- a/include/linux/usbdevice_fs.h
+++ b/include/linux/usbdevice_fs.h
@@ -22,8 +22,6 @@
*
* History:
* 0.1 04.01.2000 Created
- *
- * $Id: usbdevice_fs.h,v 1.1 2000/01/06 18:40:41 tom Exp $
*/
/*****************************************************************************/
diff --git a/include/linux/uwb.h b/include/linux/uwb.h
new file mode 100644
index 000000000000..c1562d7dc304
--- /dev/null
+++ b/include/linux/uwb.h
@@ -0,0 +1,759 @@
+/*
+ * Ultra Wide Band
+ * UWB API
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: doc: overview of the API, different parts and pointers
+ */
+
+#ifndef __LINUX__UWB_H__
+#define __LINUX__UWB_H__
+
+#include <linux/limits.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <linux/uwb/spec.h>
+
+struct uwb_dev;
+struct uwb_beca_e;
+struct uwb_rc;
+struct uwb_rsv;
+struct uwb_dbg;
+
+/**
+ * UWB Device
+ *
+ * Each device is part of a beacon group.
+ *
+ * @rc: UWB Radio Controller that discovered the device (kind
+ * of its parent).
+ *
+ * @bce: protected by @uwb_beca->mutex. Always alive as long as
+ * uwb_dev is alive. Note that a local radio controller might or
+ * might not have a bce entry (if there is another radio
+ * controller that saw him, then it might--FIXME).
+ *
+ * @streams: bitmap of streams allocated to reservations targeted at
+ * this device. For an RC, this is the streams allocated
+ * for reservations targeted at DevAddrs.
+ *
+ * It's possible that we have in a same machine more than one radio
+ * controller
+ */
+struct uwb_dev {
+ struct mutex mutex;
+ struct list_head list_node;
+ struct device dev;
+ struct uwb_rc *rc; /* radio controller */
+ struct uwb_beca_e *bce; /* Beacon Cache Entry */
+
+ struct uwb_mac_addr mac_addr;
+ struct uwb_dev_addr dev_addr;
+ DECLARE_BITMAP(streams, UWB_NUM_STREAMS);
+};
+#define to_uwb_dev(d) container_of(d, struct uwb_dev, dev)
+
+/**
+ * UWB HWA/WHCI Radio Control {Command|Event} Block context IDs
+ *
+ * RC[CE]Bs have a 'context ID' field that matches the command with
+ * the event received to confirm it.
+ *
+ * Maximum number of context IDs
+ */
+enum { UWB_RC_CTX_MAX = 256 };
+
+
+/** Notification chain head for UWB generated events to listeners */
+struct uwb_notifs_chain {
+ struct list_head list;
+ struct mutex mutex;
+};
+
+/**
+ * struct uwb_mas_bm - a bitmap of all MAS in a superframe
+ * @bm: a bitmap of length #UWB_NUM_MAS
+ */
+struct uwb_mas_bm {
+ DECLARE_BITMAP(bm, UWB_NUM_MAS);
+};
+
+/**
+ * uwb_rsv_state - UWB Reservation state.
+ *
+ * NONE - reservation is not active (no DRP IE being transmitted).
+ *
+ * Owner reservation states:
+ *
+ * INITIATED - owner has sent an initial DRP request.
+ * PENDING - target responded with pending Reason Code.
+ * MODIFIED - reservation manager is modifying an established
+ * reservation with a different MAS allocation.
+ * ESTABLISHED - the reservation has been successfully negotiated.
+ *
+ * Target reservation states:
+ *
+ * DENIED - request is denied.
+ * ACCEPTED - request is accepted.
+ * PENDING - PAL has yet to make a decision to whether to accept or
+ * deny.
+ *
+ * FIXME: further target states TBD.
+ */
+enum uwb_rsv_state {
+ UWB_RSV_STATE_NONE,
+ UWB_RSV_STATE_O_INITIATED,
+ UWB_RSV_STATE_O_PENDING,
+ UWB_RSV_STATE_O_MODIFIED,
+ UWB_RSV_STATE_O_ESTABLISHED,
+ UWB_RSV_STATE_T_ACCEPTED,
+ UWB_RSV_STATE_T_DENIED,
+ UWB_RSV_STATE_T_PENDING,
+
+ UWB_RSV_STATE_LAST,
+};
+
+enum uwb_rsv_target_type {
+ UWB_RSV_TARGET_DEV,
+ UWB_RSV_TARGET_DEVADDR,
+};
+
+/**
+ * struct uwb_rsv_target - the target of a reservation.
+ *
+ * Reservations unicast and targeted at a single device
+ * (UWB_RSV_TARGET_DEV); or (e.g., in the case of WUSB) targeted at a
+ * specific (private) DevAddr (UWB_RSV_TARGET_DEVADDR).
+ */
+struct uwb_rsv_target {
+ enum uwb_rsv_target_type type;
+ union {
+ struct uwb_dev *dev;
+ struct uwb_dev_addr devaddr;
+ };
+};
+
+/*
+ * Number of streams reserved for reservations targeted at DevAddrs.
+ */
+#define UWB_NUM_GLOBAL_STREAMS 1
+
+typedef void (*uwb_rsv_cb_f)(struct uwb_rsv *rsv);
+
+/**
+ * struct uwb_rsv - a DRP reservation
+ *
+ * Data structure management:
+ *
+ * @rc: the radio controller this reservation is for
+ * (as target or owner)
+ * @rc_node: a list node for the RC
+ * @pal_node: a list node for the PAL
+ *
+ * Owner and target parameters:
+ *
+ * @owner: the UWB device owning this reservation
+ * @target: the target UWB device
+ * @type: reservation type
+ *
+ * Owner parameters:
+ *
+ * @max_mas: maxiumum number of MAS
+ * @min_mas: minimum number of MAS
+ * @sparsity: owner selected sparsity
+ * @is_multicast: true iff multicast
+ *
+ * @callback: callback function when the reservation completes
+ * @pal_priv: private data for the PAL making the reservation
+ *
+ * Reservation status:
+ *
+ * @status: negotiation status
+ * @stream: stream index allocated for this reservation
+ * @mas: reserved MAS
+ * @drp_ie: the DRP IE
+ * @ie_valid: true iff the DRP IE matches the reservation parameters
+ *
+ * DRP reservations are uniquely identified by the owner, target and
+ * stream index. However, when using a DevAddr as a target (e.g., for
+ * a WUSB cluster reservation) the responses may be received from
+ * devices with different DevAddrs. In this case, reservations are
+ * uniquely identified by just the stream index. A number of stream
+ * indexes (UWB_NUM_GLOBAL_STREAMS) are reserved for this.
+ */
+struct uwb_rsv {
+ struct uwb_rc *rc;
+ struct list_head rc_node;
+ struct list_head pal_node;
+
+ struct uwb_dev *owner;
+ struct uwb_rsv_target target;
+ enum uwb_drp_type type;
+ int max_mas;
+ int min_mas;
+ int sparsity;
+ bool is_multicast;
+
+ uwb_rsv_cb_f callback;
+ void *pal_priv;
+
+ enum uwb_rsv_state state;
+ u8 stream;
+ struct uwb_mas_bm mas;
+ struct uwb_ie_drp *drp_ie;
+ bool ie_valid;
+ struct timer_list timer;
+ bool expired;
+};
+
+static const
+struct uwb_mas_bm uwb_mas_bm_zero = { .bm = { 0 } };
+
+static inline void uwb_mas_bm_copy_le(void *dst, const struct uwb_mas_bm *mas)
+{
+ bitmap_copy_le(dst, mas->bm, UWB_NUM_MAS);
+}
+
+/**
+ * struct uwb_drp_avail - a radio controller's view of MAS usage
+ * @global: MAS unused by neighbors (excluding reservations targetted
+ * or owned by the local radio controller) or the beaon period
+ * @local: MAS unused by local established reservations
+ * @pending: MAS unused by local pending reservations
+ * @ie: DRP Availability IE to be included in the beacon
+ * @ie_valid: true iff @ie is valid and does not need to regenerated from
+ * @global and @local
+ *
+ * Each radio controller maintains a view of MAS usage or
+ * availability. MAS available for a new reservation are determined
+ * from the intersection of @global, @local, and @pending.
+ *
+ * The radio controller must transmit a DRP Availability IE that's the
+ * intersection of @global and @local.
+ *
+ * A set bit indicates the MAS is unused and available.
+ *
+ * rc->rsvs_mutex should be held before accessing this data structure.
+ *
+ * [ECMA-368] section 17.4.3.
+ */
+struct uwb_drp_avail {
+ DECLARE_BITMAP(global, UWB_NUM_MAS);
+ DECLARE_BITMAP(local, UWB_NUM_MAS);
+ DECLARE_BITMAP(pending, UWB_NUM_MAS);
+ struct uwb_ie_drp_avail ie;
+ bool ie_valid;
+};
+
+
+const char *uwb_rsv_state_str(enum uwb_rsv_state state);
+const char *uwb_rsv_type_str(enum uwb_drp_type type);
+
+struct uwb_rsv *uwb_rsv_create(struct uwb_rc *rc, uwb_rsv_cb_f cb,
+ void *pal_priv);
+void uwb_rsv_destroy(struct uwb_rsv *rsv);
+
+int uwb_rsv_establish(struct uwb_rsv *rsv);
+int uwb_rsv_modify(struct uwb_rsv *rsv,
+ int max_mas, int min_mas, int sparsity);
+void uwb_rsv_terminate(struct uwb_rsv *rsv);
+
+void uwb_rsv_accept(struct uwb_rsv *rsv, uwb_rsv_cb_f cb, void *pal_priv);
+
+/**
+ * Radio Control Interface instance
+ *
+ *
+ * Life cycle rules: those of the UWB Device.
+ *
+ * FIXME: move most fields that are not continuously accessed to
+ * bitfields to pack the info a wee bit. Channels go from 0 to
+ * 46, for example.
+ *
+ * @version: version of protocol supported by this device
+ * @priv: Backend implementation; rw with uwb_dev.dev.sem taken.
+ * @cmd: Backend implementation to execute commands; rw and call
+ * only with uwb_dev.dev.sem taken.
+ * @filter: Backend implementation to manipulate data to and from device
+ * to be compliant to specification assumed by driver (WHCI
+ * 0.95).
+ *
+ * uwb_dev.dev.mutex is used to execute commands and update
+ * the corresponding structures; can't use a spinlock
+ * because rc->cmd() can sleep.
+ * @ies: This is a dynamically allocated array cacheing the
+ * IEs (settable by the host) that the beacon of this
+ * radio controller is currently sending.
+ *
+ * In reality, we store here the full command we set to
+ * the radio controller (which is basically a command
+ * prefix followed by all the IEs the beacon currently
+ * contains). This way we don't have to realloc and
+ * memcpy when setting it.
+ *
+ * We set this up in uwb_rc_ie_setup(), where we alloc
+ * this struct, call get_ie() [so we know which IEs are
+ * currently being sent, if any].
+ *
+ * @ies_capacity:Amount of space (in bytes) allocated in @ies. The
+ * amount used is given by sizeof(*ies) plus ies->wIELength
+ * (which is a little endian quantity all the time).
+ * @ies_mutex: protect the IE cache
+ * @dbg: information for the debug interface
+ */
+struct uwb_rc {
+ struct uwb_dev uwb_dev;
+ u16 version;
+
+ void *priv;
+ int (*cmd)(struct uwb_rc *, const struct uwb_rccb *, size_t);
+ int (*filter_cmd)(struct uwb_rc *, struct uwb_rccb **, size_t *);
+ int (*filter_event)(struct uwb_rc *, struct uwb_rceb **, const size_t,
+ size_t *, size_t *);
+
+ spinlock_t neh_lock; /* protects neh_* and ctx_* */
+ struct list_head neh_list; /* Open NE handles */
+ unsigned long ctx_bm[UWB_RC_CTX_MAX / 8 / sizeof(unsigned long)];
+ u8 ctx_roll;
+
+ int beaconing; /* Beaconing state [channel number] */
+ int scanning;
+ enum uwb_scan_type scan_type:3;
+ unsigned ready:1;
+ struct uwb_notifs_chain notifs_chain;
+
+ struct uwb_drp_avail drp_avail;
+ struct list_head reservations;
+ struct mutex rsvs_mutex;
+ struct workqueue_struct *rsv_workq;
+ struct work_struct rsv_update_work;
+
+ struct mutex ies_mutex;
+ struct uwb_rc_cmd_set_ie *ies;
+ size_t ies_capacity;
+
+ spinlock_t pal_lock;
+ struct list_head pals;
+
+ struct uwb_dbg *dbg;
+};
+
+
+/**
+ * struct uwb_pal - a UWB PAL
+ * @new_rsv: called when a peer requests a reservation (may be NULL if
+ * the PAL cannot accept reservation requests).
+ *
+ * A Protocol Adaptation Layer (PAL) is a user of the WiMedia UWB
+ * radio platform (e.g., WUSB, WLP or Bluetooth UWB AMP).
+ *
+ * The PALs using a radio controller must register themselves to
+ * permit the UWB stack to coordinate usage of the radio between the
+ * various PALs or to allow PALs to response to certain requests from
+ * peers.
+ *
+ * A struct uwb_pal should be embedded in a containing structure
+ * belonging to the PAL and initialized with uwb_pal_init()). Fields
+ * should be set appropriately by the PAL before registering the PAL
+ * with uwb_pal_register().
+ */
+struct uwb_pal {
+ struct list_head node;
+
+ void (*new_rsv)(struct uwb_rsv *rsv);
+};
+
+void uwb_pal_init(struct uwb_pal *pal);
+int uwb_pal_register(struct uwb_rc *rc, struct uwb_pal *pal);
+void uwb_pal_unregister(struct uwb_rc *rc, struct uwb_pal *pal);
+
+/*
+ * General public API
+ *
+ * This API can be used by UWB device drivers or by those implementing
+ * UWB Radio Controllers
+ */
+struct uwb_dev *uwb_dev_get_by_devaddr(struct uwb_rc *rc,
+ const struct uwb_dev_addr *devaddr);
+extern struct uwb_dev *uwb_dev_get_by_rc(struct uwb_dev *, struct uwb_rc *);
+static inline void uwb_dev_get(struct uwb_dev *uwb_dev)
+{
+ get_device(&uwb_dev->dev);
+}
+static inline void uwb_dev_put(struct uwb_dev *uwb_dev)
+{
+ put_device(&uwb_dev->dev);
+}
+struct uwb_dev *uwb_dev_try_get(struct uwb_rc *rc, struct uwb_dev *uwb_dev);
+
+/**
+ * Callback function for 'uwb_{dev,rc}_foreach()'.
+ *
+ * @dev: Linux device instance
+ * 'uwb_dev = container_of(dev, struct uwb_dev, dev)'
+ * @priv: Data passed by the caller to 'uwb_{dev,rc}_foreach()'.
+ *
+ * @returns: 0 to continue the iterations, any other val to stop
+ * iterating and return the value to the caller of
+ * _foreach().
+ */
+typedef int (*uwb_dev_for_each_f)(struct device *dev, void *priv);
+int uwb_dev_for_each(struct uwb_rc *rc, uwb_dev_for_each_f func, void *priv);
+
+extern struct uwb_rc *uwb_rc_alloc(void);
+extern struct uwb_rc *uwb_rc_get_by_dev(const struct uwb_dev_addr *);
+extern struct uwb_rc *uwb_rc_get_by_grandpa(const struct device *);
+extern void uwb_rc_put(struct uwb_rc *rc);
+
+
+extern ssize_t uwb_rc_cmd(struct uwb_rc *rc, const char *cmd_name,
+ struct uwb_rccb *cmd, size_t cmd_size,
+ struct uwb_rceb *reply, size_t reply_size);
+extern ssize_t uwb_rc_vcmd(struct uwb_rc *rc, const char *cmd_name,
+ struct uwb_rccb *cmd, size_t cmd_size,
+ u8 expected_type, u16 expected_event,
+ struct uwb_rceb **preply);
+extern ssize_t uwb_rc_get_ie(struct uwb_rc *, struct uwb_rc_evt_get_ie **);
+extern int uwb_bg_joined(struct uwb_rc *rc);
+
+extern size_t __uwb_addr_print(char *, size_t, const unsigned char *, int);
+
+extern int uwb_rc_dev_addr_set(struct uwb_rc *, const struct uwb_dev_addr *);
+extern int uwb_rc_dev_addr_get(struct uwb_rc *, struct uwb_dev_addr *);
+extern int uwb_rc_mac_addr_set(struct uwb_rc *, const struct uwb_mac_addr *);
+extern int uwb_rc_mac_addr_get(struct uwb_rc *, struct uwb_mac_addr *);
+extern int __uwb_mac_addr_assigned_check(struct device *, void *);
+extern int __uwb_dev_addr_assigned_check(struct device *, void *);
+
+/* Print in @buf a pretty repr of @addr */
+static inline size_t uwb_dev_addr_print(char *buf, size_t buf_size,
+ const struct uwb_dev_addr *addr)
+{
+ return __uwb_addr_print(buf, buf_size, addr->data, 0);
+}
+
+/* Print in @buf a pretty repr of @addr */
+static inline size_t uwb_mac_addr_print(char *buf, size_t buf_size,
+ const struct uwb_mac_addr *addr)
+{
+ return __uwb_addr_print(buf, buf_size, addr->data, 1);
+}
+
+/* @returns 0 if device addresses @addr2 and @addr1 are equal */
+static inline int uwb_dev_addr_cmp(const struct uwb_dev_addr *addr1,
+ const struct uwb_dev_addr *addr2)
+{
+ return memcmp(addr1, addr2, sizeof(*addr1));
+}
+
+/* @returns 0 if MAC addresses @addr2 and @addr1 are equal */
+static inline int uwb_mac_addr_cmp(const struct uwb_mac_addr *addr1,
+ const struct uwb_mac_addr *addr2)
+{
+ return memcmp(addr1, addr2, sizeof(*addr1));
+}
+
+/* @returns !0 if a MAC @addr is a broadcast address */
+static inline int uwb_mac_addr_bcast(const struct uwb_mac_addr *addr)
+{
+ struct uwb_mac_addr bcast = {
+ .data = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }
+ };
+ return !uwb_mac_addr_cmp(addr, &bcast);
+}
+
+/* @returns !0 if a MAC @addr is all zeroes*/
+static inline int uwb_mac_addr_unset(const struct uwb_mac_addr *addr)
+{
+ struct uwb_mac_addr unset = {
+ .data = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+ };
+ return !uwb_mac_addr_cmp(addr, &unset);
+}
+
+/* @returns !0 if the address is in use. */
+static inline unsigned __uwb_dev_addr_assigned(struct uwb_rc *rc,
+ struct uwb_dev_addr *addr)
+{
+ return uwb_dev_for_each(rc, __uwb_dev_addr_assigned_check, addr);
+}
+
+/*
+ * UWB Radio Controller API
+ *
+ * This API is used (in addition to the general API) to implement UWB
+ * Radio Controllers.
+ */
+extern void uwb_rc_init(struct uwb_rc *);
+extern int uwb_rc_add(struct uwb_rc *, struct device *dev, void *rc_priv,
+ int (*cmd)(struct uwb_rc *, const struct uwb_rccb *,
+ size_t),
+ int (*filter_cmd)(struct uwb_rc *, struct uwb_rccb **,
+ size_t *),
+ int (*filter_event)(struct uwb_rc *, struct uwb_rceb **,
+ const size_t, size_t *, size_t *));
+extern void uwb_rc_rm(struct uwb_rc *);
+extern void uwb_rc_neh_grok(struct uwb_rc *, void *, size_t);
+extern void uwb_rc_neh_error(struct uwb_rc *, int);
+
+/**
+ * uwb_rsv_is_owner - is the owner of this reservation the RC?
+ * @rsv: the reservation
+ */
+static inline bool uwb_rsv_is_owner(struct uwb_rsv *rsv)
+{
+ return rsv->owner == &rsv->rc->uwb_dev;
+}
+
+/**
+ * Events generated by UWB that can be passed to any listeners
+ *
+ * Higher layers can register callback functions with the radio
+ * controller using uwb_notifs_register(). The radio controller
+ * maintains a list of all registered handlers and will notify all
+ * nodes when an event occurs.
+ */
+enum uwb_notifs {
+ UWB_NOTIF_BG_JOIN = 0, /* radio controller joined a beacon group */
+ UWB_NOTIF_BG_LEAVE = 1, /* radio controller left a beacon group */
+ UWB_NOTIF_ONAIR,
+ UWB_NOTIF_OFFAIR,
+};
+
+/* Callback function registered with UWB */
+struct uwb_notifs_handler {
+ struct list_head list_node;
+ void (*cb)(void *, struct uwb_dev *, enum uwb_notifs);
+ void *data;
+};
+
+extern int uwb_notifs_register(struct uwb_rc *, struct uwb_notifs_handler *);
+extern int uwb_notifs_deregister(struct uwb_rc *, struct uwb_notifs_handler *);
+
+
+/**
+ * UWB radio controller Event Size Entry (for creating entry tables)
+ *
+ * WUSB and WHCI define events and notifications, and they might have
+ * fixed or variable size.
+ *
+ * Each event/notification has a size which is not necessarily known
+ * in advance based on the event code. As well, vendor specific
+ * events/notifications will have a size impossible to determine
+ * unless we know about the device's specific details.
+ *
+ * It was way too smart of the spec writers not to think that it would
+ * be impossible for a generic driver to skip over vendor specific
+ * events/notifications if there are no LENGTH fields in the HEADER of
+ * each message...the transaction size cannot be counted on as the
+ * spec does not forbid to pack more than one event in a single
+ * transaction.
+ *
+ * Thus, we guess sizes with tables (or for events, when you know the
+ * size ahead of time you can use uwb_rc_neh_extra_size*()). We
+ * register tables with the known events and their sizes, and then we
+ * traverse those tables. For those with variable length, we provide a
+ * way to lookup the size inside the event/notification's
+ * payload. This allows device-specific event size tables to be
+ * registered.
+ *
+ * @size: Size of the payload
+ *
+ * @offset: if != 0, at offset @offset-1 starts a field with a length
+ * that has to be added to @size. The format of the field is
+ * given by @type.
+ *
+ * @type: Type and length of the offset field. Most common is LE 16
+ * bits (that's why that is zero); others are there mostly to
+ * cover for bugs and weirdos.
+ */
+struct uwb_est_entry {
+ size_t size;
+ unsigned offset;
+ enum { UWB_EST_16 = 0, UWB_EST_8 = 1 } type;
+};
+
+extern int uwb_est_register(u8 type, u8 code_high, u16 vendor, u16 product,
+ const struct uwb_est_entry *, size_t entries);
+extern int uwb_est_unregister(u8 type, u8 code_high, u16 vendor, u16 product,
+ const struct uwb_est_entry *, size_t entries);
+extern ssize_t uwb_est_find_size(struct uwb_rc *, const struct uwb_rceb *,
+ size_t);
+
+/* -- Misc */
+
+enum {
+ EDC_MAX_ERRORS = 10,
+ EDC_ERROR_TIMEFRAME = HZ,
+};
+
+/* error density counter */
+struct edc {
+ unsigned long timestart;
+ u16 errorcount;
+};
+
+static inline
+void edc_init(struct edc *edc)
+{
+ edc->timestart = jiffies;
+}
+
+/* Called when an error occured.
+ * This is way to determine if the number of acceptable errors per time
+ * period has been exceeded. It is not accurate as there are cases in which
+ * this scheme will not work, for example if there are periodic occurences
+ * of errors that straddle updates to the start time. This scheme is
+ * sufficient for our usage.
+ *
+ * @returns 1 if maximum acceptable errors per timeframe has been exceeded.
+ */
+static inline int edc_inc(struct edc *err_hist, u16 max_err, u16 timeframe)
+{
+ unsigned long now;
+
+ now = jiffies;
+ if (now - err_hist->timestart > timeframe) {
+ err_hist->errorcount = 1;
+ err_hist->timestart = now;
+ } else if (++err_hist->errorcount > max_err) {
+ err_hist->errorcount = 0;
+ err_hist->timestart = now;
+ return 1;
+ }
+ return 0;
+}
+
+
+/* Information Element handling */
+
+/* For representing the state of writing to a buffer when iterating */
+struct uwb_buf_ctx {
+ char *buf;
+ size_t bytes, size;
+};
+
+typedef int (*uwb_ie_f)(struct uwb_dev *, const struct uwb_ie_hdr *,
+ size_t, void *);
+struct uwb_ie_hdr *uwb_ie_next(void **ptr, size_t *len);
+ssize_t uwb_ie_for_each(struct uwb_dev *uwb_dev, uwb_ie_f fn, void *data,
+ const void *buf, size_t size);
+int uwb_ie_dump_hex(struct uwb_dev *, const struct uwb_ie_hdr *,
+ size_t, void *);
+int uwb_rc_set_ie(struct uwb_rc *, struct uwb_rc_cmd_set_ie *);
+struct uwb_ie_hdr *uwb_ie_next(void **ptr, size_t *len);
+
+
+/*
+ * Transmission statistics
+ *
+ * UWB uses LQI and RSSI (one byte values) for reporting radio signal
+ * strength and line quality indication. We do quick and dirty
+ * averages of those. They are signed values, btw.
+ *
+ * For 8 bit quantities, we keep the min, the max, an accumulator
+ * (@sigma) and a # of samples. When @samples gets to 255, we compute
+ * the average (@sigma / @samples), place it in @sigma and reset
+ * @samples to 1 (so we use it as the first sample).
+ *
+ * Now, statistically speaking, probably I am kicking the kidneys of
+ * some books I have in my shelves collecting dust, but I just want to
+ * get an approx, not the Nobel.
+ *
+ * LOCKING: there is no locking per se, but we try to keep a lockless
+ * schema. Only _add_samples() modifies the values--as long as you
+ * have other locking on top that makes sure that no two calls of
+ * _add_sample() happen at the same time, then we are fine. Now, for
+ * resetting the values we just set @samples to 0 and that makes the
+ * next _add_sample() to start with defaults. Reading the values in
+ * _show() currently can race, so you need to make sure the calls are
+ * under the same lock that protects calls to _add_sample(). FIXME:
+ * currently unlocked (It is not ultraprecise but does the trick. Bite
+ * me).
+ */
+struct stats {
+ s8 min, max;
+ s16 sigma;
+ atomic_t samples;
+};
+
+static inline
+void stats_init(struct stats *stats)
+{
+ atomic_set(&stats->samples, 0);
+ wmb();
+}
+
+static inline
+void stats_add_sample(struct stats *stats, s8 sample)
+{
+ s8 min, max;
+ s16 sigma;
+ unsigned samples = atomic_read(&stats->samples);
+ if (samples == 0) { /* it was zero before, so we initialize */
+ min = 127;
+ max = -128;
+ sigma = 0;
+ } else {
+ min = stats->min;
+ max = stats->max;
+ sigma = stats->sigma;
+ }
+
+ if (sample < min) /* compute new values */
+ min = sample;
+ else if (sample > max)
+ max = sample;
+ sigma += sample;
+
+ stats->min = min; /* commit */
+ stats->max = max;
+ stats->sigma = sigma;
+ if (atomic_add_return(1, &stats->samples) > 255) {
+ /* wrapped around! reset */
+ stats->sigma = sigma / 256;
+ atomic_set(&stats->samples, 1);
+ }
+}
+
+static inline ssize_t stats_show(struct stats *stats, char *buf)
+{
+ int min, max, avg;
+ int samples = atomic_read(&stats->samples);
+ if (samples == 0)
+ min = max = avg = 0;
+ else {
+ min = stats->min;
+ max = stats->max;
+ avg = stats->sigma / samples;
+ }
+ return scnprintf(buf, PAGE_SIZE, "%d %d %d\n", min, max, avg);
+}
+
+static inline ssize_t stats_store(struct stats *stats, const char *buf,
+ size_t size)
+{
+ stats_init(stats);
+ return size;
+}
+
+#endif /* #ifndef __LINUX__UWB_H__ */
diff --git a/include/linux/uwb/debug-cmd.h b/include/linux/uwb/debug-cmd.h
new file mode 100644
index 000000000000..1141f41bab5c
--- /dev/null
+++ b/include/linux/uwb/debug-cmd.h
@@ -0,0 +1,57 @@
+/*
+ * Ultra Wide Band
+ * Debug interface commands
+ *
+ * Copyright (C) 2008 Cambridge Silicon Radio 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.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __LINUX__UWB__DEBUG_CMD_H__
+#define __LINUX__UWB__DEBUG_CMD_H__
+
+#include <linux/types.h>
+
+/*
+ * Debug interface commands
+ *
+ * UWB_DBG_CMD_RSV_ESTABLISH: Establish a new unicast reservation.
+ *
+ * UWB_DBG_CMD_RSV_TERMINATE: Terminate the Nth reservation.
+ */
+
+enum uwb_dbg_cmd_type {
+ UWB_DBG_CMD_RSV_ESTABLISH = 1,
+ UWB_DBG_CMD_RSV_TERMINATE = 2,
+};
+
+struct uwb_dbg_cmd_rsv_establish {
+ __u8 target[6];
+ __u8 type;
+ __u16 max_mas;
+ __u16 min_mas;
+ __u8 sparsity;
+};
+
+struct uwb_dbg_cmd_rsv_terminate {
+ int index;
+};
+
+struct uwb_dbg_cmd {
+ __u32 type;
+ union {
+ struct uwb_dbg_cmd_rsv_establish rsv_establish;
+ struct uwb_dbg_cmd_rsv_terminate rsv_terminate;
+ };
+};
+
+#endif /* #ifndef __LINUX__UWB__DEBUG_CMD_H__ */
diff --git a/include/linux/uwb/debug.h b/include/linux/uwb/debug.h
new file mode 100644
index 000000000000..6a898bda4359
--- /dev/null
+++ b/include/linux/uwb/debug.h
@@ -0,0 +1,82 @@
+/*
+ * Ultra Wide Band
+ * Debug Support
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: doc
+ * Invoke like:
+ *
+ * #define D_LOCAL 4
+ * #include <linux/uwb/debug.h>
+ *
+ * At the end of your include files.
+ */
+#include <linux/types.h>
+
+struct device;
+extern void dump_bytes(struct device *dev, const void *_buf, size_t rsize);
+
+/* Master debug switch; !0 enables, 0 disables */
+#define D_MASTER !0
+
+/* Local (per-file) debug switch; #define before #including */
+#ifndef D_LOCAL
+#define D_LOCAL 0
+#endif
+
+#undef __d_printf
+#undef d_fnstart
+#undef d_fnend
+#undef d_printf
+#undef d_dump
+
+#define __d_printf(l, _tag, _dev, f, a...) \
+do { \
+ struct device *__dev = (_dev); \
+ if (D_MASTER && D_LOCAL >= (l)) { \
+ char __head[64] = ""; \
+ if (_dev != NULL) { \
+ if ((unsigned long)__dev < 4096) \
+ printk(KERN_ERR "E: Corrupt dev %p\n", \
+ __dev); \
+ else \
+ snprintf(__head, sizeof(__head), \
+ "%s %s: ", \
+ dev_driver_string(__dev), \
+ dev_name(__dev)); \
+ } \
+ printk(KERN_ERR "%s%s" _tag ": " f, __head, \
+ __func__, ## a); \
+ } \
+} while (0 && _dev)
+
+#define d_fnstart(l, _dev, f, a...) \
+ __d_printf(l, " FNSTART", _dev, f, ## a)
+#define d_fnend(l, _dev, f, a...) \
+ __d_printf(l, " FNEND", _dev, f, ## a)
+#define d_printf(l, _dev, f, a...) \
+ __d_printf(l, "", _dev, f, ## a)
+#define d_dump(l, _dev, ptr, size) \
+do { \
+ struct device *__dev = _dev; \
+ if (D_MASTER && D_LOCAL >= (l)) \
+ dump_bytes(__dev, ptr, size); \
+} while (0 && _dev)
+#define d_test(l) (D_MASTER && D_LOCAL >= (l))
diff --git a/include/linux/uwb/spec.h b/include/linux/uwb/spec.h
new file mode 100644
index 000000000000..102425903090
--- /dev/null
+++ b/include/linux/uwb/spec.h
@@ -0,0 +1,625 @@
+/*
+ * Ultra Wide Band
+ * UWB Standard definitions
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * All these definitions are based on the ECMA-368 standard.
+ *
+ * Note all definitions are Little Endian in the wire, and we will
+ * convert them to host order before operating on the bitfields (that
+ * yes, we use extensively).
+ */
+
+#ifndef __LINUX__UWB_SPEC_H__
+#define __LINUX__UWB_SPEC_H__
+
+#include <linux/types.h>
+#include <linux/bitmap.h>
+#include <linux/byteorder/bitfields.h>
+
+#define i1480_FW 0x00000303
+/* #define i1480_FW 0x00000302 */
+
+/**
+ * Number of Medium Access Slots in a superframe.
+ *
+ * UWB divides time in SuperFrames, each one divided in 256 pieces, or
+ * Medium Access Slots. See MBOA MAC[5.4.5] for details. The MAS is the
+ * basic bandwidth allocation unit in UWB.
+ */
+enum { UWB_NUM_MAS = 256 };
+
+/**
+ * Number of Zones in superframe.
+ *
+ * UWB divides the superframe into zones with numbering starting from BPST.
+ * See MBOA MAC[16.8.6]
+ */
+enum { UWB_NUM_ZONES = 16 };
+
+/*
+ * Number of MAS in a zone.
+ */
+#define UWB_MAS_PER_ZONE (UWB_NUM_MAS / UWB_NUM_ZONES)
+
+/*
+ * Number of streams per DRP reservation between a pair of devices.
+ *
+ * [ECMA-368] section 16.8.6.
+ */
+enum { UWB_NUM_STREAMS = 8 };
+
+/*
+ * mMasLength
+ *
+ * The length of a MAS in microseconds.
+ *
+ * [ECMA-368] section 17.16.
+ */
+enum { UWB_MAS_LENGTH_US = 256 };
+
+/*
+ * mBeaconSlotLength
+ *
+ * The length of the beacon slot in microseconds.
+ *
+ * [ECMA-368] section 17.16
+ */
+enum { UWB_BEACON_SLOT_LENGTH_US = 85 };
+
+/*
+ * mMaxLostBeacons
+ *
+ * The number beacons missing in consecutive superframes before a
+ * device can be considered as unreachable.
+ *
+ * [ECMA-368] section 17.16
+ */
+enum { UWB_MAX_LOST_BEACONS = 3 };
+
+/*
+ * Length of a superframe in microseconds.
+ */
+#define UWB_SUPERFRAME_LENGTH_US (UWB_MAS_LENGTH_US * UWB_NUM_MAS)
+
+/**
+ * UWB MAC address
+ *
+ * It is *imperative* that this struct is exactly 6 packed bytes (as
+ * it is also used to define headers sent down and up the wire/radio).
+ */
+struct uwb_mac_addr {
+ u8 data[6];
+} __attribute__((packed));
+
+
+/**
+ * UWB device address
+ *
+ * It is *imperative* that this struct is exactly 6 packed bytes (as
+ * it is also used to define headers sent down and up the wire/radio).
+ */
+struct uwb_dev_addr {
+ u8 data[2];
+} __attribute__((packed));
+
+
+/**
+ * Types of UWB addresses
+ *
+ * Order matters (by size).
+ */
+enum uwb_addr_type {
+ UWB_ADDR_DEV = 0,
+ UWB_ADDR_MAC = 1,
+};
+
+
+/** Size of a char buffer for printing a MAC/device address */
+enum { UWB_ADDR_STRSIZE = 32 };
+
+
+/** UWB WiMedia protocol IDs. */
+enum uwb_prid {
+ UWB_PRID_WLP_RESERVED = 0x0000,
+ UWB_PRID_WLP = 0x0001,
+ UWB_PRID_WUSB_BOT = 0x0010,
+ UWB_PRID_WUSB = 0x0010,
+ UWB_PRID_WUSB_TOP = 0x001F,
+};
+
+
+/** PHY Rate (MBOA MAC[7.8.12, Table 61]) */
+enum uwb_phy_rate {
+ UWB_PHY_RATE_53 = 0,
+ UWB_PHY_RATE_80,
+ UWB_PHY_RATE_106,
+ UWB_PHY_RATE_160,
+ UWB_PHY_RATE_200,
+ UWB_PHY_RATE_320,
+ UWB_PHY_RATE_400,
+ UWB_PHY_RATE_480,
+ UWB_PHY_RATE_INVALID
+};
+
+
+/**
+ * Different ways to scan (MBOA MAC[6.2.2, Table 8], WUSB[Table 8-78])
+ */
+enum uwb_scan_type {
+ UWB_SCAN_ONLY = 0,
+ UWB_SCAN_OUTSIDE_BP,
+ UWB_SCAN_WHILE_INACTIVE,
+ UWB_SCAN_DISABLED,
+ UWB_SCAN_ONLY_STARTTIME,
+ UWB_SCAN_TOP
+};
+
+
+/** ACK Policy types (MBOA MAC[7.2.1.3]) */
+enum uwb_ack_pol {
+ UWB_ACK_NO = 0,
+ UWB_ACK_INM = 1,
+ UWB_ACK_B = 2,
+ UWB_ACK_B_REQ = 3,
+};
+
+
+/** DRP reservation types ([ECMA-368 table 106) */
+enum uwb_drp_type {
+ UWB_DRP_TYPE_ALIEN_BP = 0,
+ UWB_DRP_TYPE_HARD,
+ UWB_DRP_TYPE_SOFT,
+ UWB_DRP_TYPE_PRIVATE,
+ UWB_DRP_TYPE_PCA,
+};
+
+
+/** DRP Reason Codes ([ECMA-368] table 107) */
+enum uwb_drp_reason {
+ UWB_DRP_REASON_ACCEPTED = 0,
+ UWB_DRP_REASON_CONFLICT,
+ UWB_DRP_REASON_PENDING,
+ UWB_DRP_REASON_DENIED,
+ UWB_DRP_REASON_MODIFIED,
+};
+
+/**
+ * DRP Notification Reason Codes (WHCI 0.95 [3.1.4.9])
+ */
+enum uwb_drp_notif_reason {
+ UWB_DRP_NOTIF_DRP_IE_RCVD = 0,
+ UWB_DRP_NOTIF_CONFLICT,
+ UWB_DRP_NOTIF_TERMINATE,
+};
+
+
+/** Allocation of MAS slots in a DRP request MBOA MAC[7.8.7] */
+struct uwb_drp_alloc {
+ __le16 zone_bm;
+ __le16 mas_bm;
+} __attribute__((packed));
+
+
+/** General MAC Header format (ECMA-368[16.2]) */
+struct uwb_mac_frame_hdr {
+ union {
+ __le16 Frame_Control;
+ DECL_BF_LE7(
+ u16 Protocol_Version:3,
+ u16 Secure:1,
+ u16 ACK_Policy:2,
+ u16 Frame_Type:2,
+ u16 Frame_Subtype:4,
+ u16 Retry:1,
+ u16 Reserved:2
+ ) __attribute__((packed));
+ };
+ struct uwb_dev_addr DestAddr;
+ struct uwb_dev_addr SrcAddr;
+ union {
+ __le16 Sequence_Control;
+ DECL_BF_LE4(
+ u16 Fragment_Number:3,
+ u16 Sequence_Number:11,
+ u16 More_Fragments:1,
+ u16 Reserved:1
+ ) __attribute__((packed));
+ };
+ union {
+ __le16 Access_Information;
+ DECL_BF_LE3(
+ u16 Duration:14,
+ u16 More_Frames:1,
+ u16 Access_Method:1
+ ) __attribute__((packed));
+ };
+} __attribute__((packed));
+
+
+/**
+ * uwb_beacon_frame - a beacon frame including MAC headers
+ *
+ * [ECMA] section 16.3.
+ */
+struct uwb_beacon_frame {
+ struct uwb_mac_frame_hdr hdr;
+ struct uwb_mac_addr Device_Identifier; /* may be a NULL EUI-48 */
+ u8 Beacon_Slot_Number;
+ union {
+ u8 Device_Control;
+ DECL_BF_LE3(
+ u8 Movable:1,
+ u8 Security_Mode:2,
+ u8 Reserved:5
+ ) __attribute__((packed));
+ };
+ u8 IEData[];
+} __attribute__((packed));
+
+
+/** Information Element codes (MBOA MAC[T54]) */
+enum uwb_ie {
+ UWB_PCA_AVAILABILITY = 2,
+ UWB_IE_DRP_AVAILABILITY = 8,
+ UWB_IE_DRP = 9,
+ UWB_BP_SWITCH_IE = 11,
+ UWB_MAC_CAPABILITIES_IE = 12,
+ UWB_PHY_CAPABILITIES_IE = 13,
+ UWB_APP_SPEC_PROBE_IE = 15,
+ UWB_IDENTIFICATION_IE = 19,
+ UWB_MASTER_KEY_ID_IE = 20,
+ UWB_IE_WLP = 250, /* WiMedia Logical Link Control Protocol WLP 0.99 */
+ UWB_APP_SPEC_IE = 255,
+};
+
+
+/**
+ * Header common to all Information Elements (IEs)
+ */
+struct uwb_ie_hdr {
+ u8 element_id; /* enum uwb_ie */
+ u8 length;
+} __attribute__((packed));
+
+
+/** Dynamic Reservation Protocol IE (MBOA MAC[7.8.6]) */
+struct uwb_ie_drp {
+ struct uwb_ie_hdr hdr;
+ union {
+ __le16 DRP_Control;
+ DECL_BF_LE8(
+ enum uwb_drp_type type:3,
+ u8 stream_index:3,
+ enum uwb_drp_reason reason_code:3,
+ u8 status:1,
+ u8 owner:1,
+ u8 tiebreaker:1,
+ u8 unsafe:1,
+ u8 reserved:3
+ ) __attribute__((packed));
+ };
+ struct uwb_dev_addr dev_addr;
+ struct uwb_drp_alloc allocs[];
+} __attribute__((packed));
+
+/** Dynamic Reservation Protocol IE (MBOA MAC[7.8.7]) */
+struct uwb_ie_drp_avail {
+ struct uwb_ie_hdr hdr;
+ DECLARE_BITMAP(bmp, UWB_NUM_MAS);
+} __attribute__((packed));
+
+/**
+ * The Vendor ID is set to an OUI that indicates the vendor of the device.
+ * ECMA-368 [16.8.10]
+ */
+struct uwb_vendor_id {
+ u8 data[3];
+} __attribute__((packed));
+
+/**
+ * The device type ID
+ * FIXME: clarify what this means
+ * ECMA-368 [16.8.10]
+ */
+struct uwb_device_type_id {
+ u8 data[3];
+} __attribute__((packed));
+
+
+/**
+ * UWB device information types
+ * ECMA-368 [16.8.10]
+ */
+enum uwb_dev_info_type {
+ UWB_DEV_INFO_VENDOR_ID = 0,
+ UWB_DEV_INFO_VENDOR_TYPE,
+ UWB_DEV_INFO_NAME,
+};
+
+/**
+ * UWB device information found in Identification IE
+ * ECMA-368 [16.8.10]
+ */
+struct uwb_dev_info {
+ u8 type; /* enum uwb_dev_info_type */
+ u8 length;
+ u8 data[];
+} __attribute__((packed));
+
+/**
+ * UWB Identification IE
+ * ECMA-368 [16.8.10]
+ */
+struct uwb_identification_ie {
+ struct uwb_ie_hdr hdr;
+ struct uwb_dev_info info[];
+} __attribute__((packed));
+
+/*
+ * UWB Radio Controller
+ *
+ * These definitions are common to the Radio Control layers as
+ * exported by the WUSB1.0 HWA and WHCI interfaces.
+ */
+
+/** Radio Control Command Block (WUSB1.0[Table 8-65] and WHCI 0.95) */
+struct uwb_rccb {
+ u8 bCommandType; /* enum hwa_cet */
+ __le16 wCommand; /* Command code */
+ u8 bCommandContext; /* Context ID */
+} __attribute__((packed));
+
+
+/** Radio Control Event Block (WUSB[table 8-66], WHCI 0.95) */
+struct uwb_rceb {
+ u8 bEventType; /* enum hwa_cet */
+ __le16 wEvent; /* Event code */
+ u8 bEventContext; /* Context ID */
+} __attribute__((packed));
+
+
+enum {
+ UWB_RC_CET_GENERAL = 0, /* General Command/Event type */
+};
+
+/* Commands to the radio controller */
+enum uwb_rc_cmd {
+ UWB_RC_CMD_CHANNEL_CHANGE = 16,
+ UWB_RC_CMD_DEV_ADDR_MGMT = 17, /* Device Address Management */
+ UWB_RC_CMD_GET_IE = 18, /* GET Information Elements */
+ UWB_RC_CMD_RESET = 19,
+ UWB_RC_CMD_SCAN = 20, /* Scan management */
+ UWB_RC_CMD_SET_BEACON_FILTER = 21,
+ UWB_RC_CMD_SET_DRP_IE = 22, /* Dynamic Reservation Protocol IEs */
+ UWB_RC_CMD_SET_IE = 23, /* Information Element management */
+ UWB_RC_CMD_START_BEACON = 27,
+ UWB_RC_CMD_STOP_BEACON = 28,
+ UWB_RC_CMD_BP_MERGE = 29,
+ UWB_RC_CMD_SEND_COMMAND_FRAME = 30,
+ UWB_RC_CMD_SET_ASIE_NOTIF = 31,
+};
+
+
+/* Notifications from the radio controller */
+enum uwb_rc_evt {
+ UWB_RC_EVT_BEACON = 1,
+ UWB_RC_EVT_BEACON_SIZE = 2,
+ UWB_RC_EVT_BPOIE_CHANGE = 3,
+ UWB_RC_EVT_BP_SLOT_CHANGE = 4,
+ UWB_RC_EVT_DEV_ADDR_CONFLICT = 6,
+ UWB_RC_EVT_DRP_AVAIL = 7,
+ UWB_RC_EVT_DRP = 8,
+ UWB_RC_EVT_BP_SWITCH_STATUS = 9,
+ UWB_RC_EVT_CMD_FRAME_RCV = 10,
+ UWB_RC_EVT_CHANNEL_CHANGE_IE_RCV = 11,
+ /* Events (command responses) use the same code as the command */
+};
+
+
+/** Confirm event structure */
+struct uwb_rc_evt_confirm {
+ struct uwb_rceb rceb;
+ u8 bResultCode;
+} __attribute__((packed));
+
+
+/** Device Address Management event */
+struct uwb_rc_evt_dev_addr_mgmt {
+ struct uwb_rceb rceb;
+ u8 baAddr[6];
+ u8 bResultCode;
+} __attribute__((packed));
+
+
+/** Get IE Event structure (response to Get IE command). */
+struct uwb_rc_evt_get_ie {
+ struct uwb_rceb rceb;
+ __le16 wIELength;
+ u8 IEData[];
+} __attribute__((packed));
+
+/**
+ * SET DRP IE Event structure
+ *
+ * WHCI 0.95 [3.1.3.7]
+ */
+struct uwb_rc_evt_set_drp_ie {
+ struct uwb_rceb rceb;
+ __le16 wRemainingSpace;
+ u8 bResultCode;
+} __attribute__((packed));
+
+/**
+ * SET IE Event structure (response to SET IE command).
+ *
+ * WHCI 0.95 [3.1.3.8]
+ */
+struct uwb_rc_evt_set_ie {
+ struct uwb_rceb rceb;
+ __le16 RemainingSpace;
+ u8 bResultCode;
+} __attribute__((packed));
+
+
+/**
+ * Type of the received beacon. WHCI 0.95 [3.1.4.2]
+ */
+enum uwb_rc_beacon_type {
+ UWB_RC_BEACON_TYPE_SCAN = 0,
+ UWB_RC_BEACON_TYPE_NEIGHBOR,
+ UWB_RC_BEACON_TYPE_OL_ALIEN,
+ UWB_RC_BEACON_TYPE_NOL_ALIEN,
+};
+
+/** Radio Control Result Code (WHCI 0.95[Table 3-3])*/
+enum {
+ UWB_RC_RES_SUCCESS = 0,
+ UWB_RC_RES_FAIL,
+ UWB_RC_RES_FAIL_HARDWARE,
+ UWB_RC_RES_FAIL_NO_SLOTS,
+ UWB_RC_RES_FAIL_BEACON_TOO_LARGE,
+ UWB_RC_RES_FAIL_INVALID_PARAMETER,
+ UWB_RC_RES_FAIL_UNSUPPORTED_PWR_LEVEL,
+ UWB_RC_RES_FAIL_INVALID_IE_DATA,
+ UWB_RC_RES_FAIL_BEACON_SIZE_EXCEEDED,
+ UWB_RC_RES_FAIL_CANCELLED,
+ UWB_RC_RES_FAIL_INVALID_STATE,
+ UWB_RC_RES_FAIL_INVALID_SIZE,
+ UWB_RC_RES_FAIL_ACK_NOT_RECEIVED,
+ UWB_RC_RES_FAIL_NO_MORE_ASIE_NOTIF,
+ UWB_RC_RES_FAIL_TIME_OUT = 255,
+};
+/**
+ * Beacon received notification
+ * WHCI 0.95 [3.1.4.2]
+ */
+struct uwb_rc_evt_beacon {
+ struct uwb_rceb rceb;
+ u8 bChannelNumber;
+ u8 bBeaconType;
+ __le16 wBPSTOffset;
+ u8 bLQI;
+ u8 bRSSI;
+ __le16 wBeaconInfoLength;
+ u8 BeaconInfo[];
+} __attribute__((packed));
+
+
+/** Beacon Size notification */
+struct uwb_rc_evt_beacon_size {
+ struct uwb_rceb rceb;
+ __le16 wNewBeaconSize;
+} __attribute__((packed));
+
+
+/** BPOIE Change notification */
+struct uwb_rc_evt_bpoie_change {
+ struct uwb_rceb rceb;
+ __le16 wBPOIELength;
+ u8 BPOIE[];
+} __attribute__((packed));
+
+
+/**
+ * Beacon slot change notification
+ * WHCI 0.95 [3.1.4.5]
+ */
+struct uwb_rc_evt_bp_slot_change {
+ struct uwb_rceb rceb;
+ DECL_BF_LE2(
+ u8 bSlotNumber:7,
+ u8 bNoSlot:1
+ ) __attribute__((packed));
+} __attribute__((packed));
+
+
+/**
+ * DRP notification
+ * WHCI 0.95 [3.1.4.9]
+ */
+struct uwb_rc_evt_drp {
+ struct uwb_rceb rceb;
+ struct uwb_dev_addr wSrcAddr;
+ DECL_BF_LE2(
+ u8 Reserved:4,
+ enum uwb_drp_notif_reason Reason:4
+ )__attribute__((packed));
+ u8 bBeaconSlotNumber;
+ __le16 wIELength;
+ u8 IEData[];
+} __attribute__((packed));
+
+
+/**
+ * DRP Availability change notification
+ * WHCI 0.95 [3.1.4.8]
+ */
+struct uwb_rc_evt_drp_avail {
+ struct uwb_rceb rceb;
+ DECLARE_BITMAP(bmp, UWB_NUM_MAS);
+} __attribute__((packed));
+
+/**
+ * Scan Command
+ * WHCI 0.95 [3.1.3.5]
+ */
+struct uwb_rc_cmd_scan {
+ struct uwb_rccb rccb;
+ u8 bChannelNumber;
+ u8 bScanState;
+ __le16 wStartTime;
+} __attribute__((packed));
+
+/**
+ * SET DRP IE Command structure
+ *
+ * WHCI 0.95 [3.1.3.7]
+ */
+struct uwb_rc_cmd_set_drp_ie {
+ struct uwb_rccb rccb;
+ __le16 wIELength;
+ struct uwb_ie_drp IEData[];
+} __attribute__((packed));
+
+/**
+ * SET IE Command structure
+ *
+ * WHCI 0.95 [3.1.3.8]
+ */
+struct uwb_rc_cmd_set_ie {
+ struct uwb_rccb rccb;
+ __le16 wIELength;
+ u8 IEData[];
+} __attribute__((packed));
+
+
+/**
+ * Radio Control Interface Class Descriptor
+ *
+ * WUSB 1.0 [8.6.1.2]
+ */
+struct uwb_rc_control_intf_class_desc {
+ u8 bLength;
+ u8 bDescriptorType;
+ __le16 bcdRCIVersion;
+} __attribute__((packed));
+
+#endif /* #ifndef __LINUX__UWB_SPEC_H__ */
diff --git a/include/linux/uwb/umc.h b/include/linux/uwb/umc.h
new file mode 100644
index 000000000000..071224df9347
--- /dev/null
+++ b/include/linux/uwb/umc.h
@@ -0,0 +1,194 @@
+/*
+ * UWB Multi-interface Controller support.
+ *
+ * Copyright (C) 2007 Cambridge Silicon Radio Ltd.
+ *
+ * This file is released under the GPLv2
+ *
+ * UMC (UWB Multi-interface Controller) capabilities (e.g., radio
+ * controller, host controller) are presented as devices on the "umc"
+ * bus.
+ *
+ * The radio controller is not strictly a UMC capability but it's
+ * useful to present it as such.
+ *
+ * References:
+ *
+ * [WHCI] Wireless Host Controller Interface Specification for
+ * Certified Wireless Universal Serial Bus, revision 0.95.
+ *
+ * How this works is kind of convoluted but simple. The whci.ko driver
+ * loads when WHCI devices are detected. These WHCI devices expose
+ * many devices in the same PCI function (they couldn't have reused
+ * functions, no), so for each PCI function that exposes these many
+ * devices, whci ceates a umc_dev [whci_probe() -> whci_add_cap()]
+ * with umc_device_create() and adds it to the bus with
+ * umc_device_register().
+ *
+ * umc_device_register() calls device_register() which will push the
+ * bus management code to load your UMC driver's somehting_probe()
+ * that you have registered for that capability code.
+ *
+ * Now when the WHCI device is removed, whci_remove() will go over
+ * each umc_dev assigned to each of the PCI function's capabilities
+ * and through whci_del_cap() call umc_device_unregister() each
+ * created umc_dev. Of course, if you are bound to the device, your
+ * driver's something_remove() will be called.
+ */
+
+#ifndef _LINUX_UWB_UMC_H_
+#define _LINUX_UWB_UMC_H_
+
+#include <linux/device.h>
+#include <linux/pci.h>
+
+/*
+ * UMC capability IDs.
+ *
+ * 0x00 is reserved so use it for the radio controller device.
+ *
+ * [WHCI] table 2-8
+ */
+#define UMC_CAP_ID_WHCI_RC 0x00 /* radio controller */
+#define UMC_CAP_ID_WHCI_WUSB_HC 0x01 /* WUSB host controller */
+
+/**
+ * struct umc_dev - UMC capability device
+ *
+ * @version: version of the specification this capability conforms to.
+ * @cap_id: capability ID.
+ * @bar: PCI Bar (64 bit) where the resource lies
+ * @resource: register space resource.
+ * @irq: interrupt line.
+ */
+struct umc_dev {
+ u16 version;
+ u8 cap_id;
+ u8 bar;
+ struct resource resource;
+ unsigned irq;
+ struct device dev;
+};
+
+#define to_umc_dev(d) container_of(d, struct umc_dev, dev)
+
+/**
+ * struct umc_driver - UMC capability driver
+ * @cap_id: supported capability ID.
+ * @match: driver specific capability matching function.
+ * @match_data: driver specific data for match() (e.g., a
+ * table of pci_device_id's if umc_match_pci_id() is used).
+ */
+struct umc_driver {
+ char *name;
+ u8 cap_id;
+ int (*match)(struct umc_driver *, struct umc_dev *);
+ const void *match_data;
+
+ int (*probe)(struct umc_dev *);
+ void (*remove)(struct umc_dev *);
+ int (*suspend)(struct umc_dev *, pm_message_t state);
+ int (*resume)(struct umc_dev *);
+
+ struct device_driver driver;
+};
+
+#define to_umc_driver(d) container_of(d, struct umc_driver, driver)
+
+extern struct bus_type umc_bus_type;
+
+struct umc_dev *umc_device_create(struct device *parent, int n);
+int __must_check umc_device_register(struct umc_dev *umc);
+void umc_device_unregister(struct umc_dev *umc);
+
+int __must_check __umc_driver_register(struct umc_driver *umc_drv,
+ struct module *mod,
+ const char *mod_name);
+
+/**
+ * umc_driver_register - register a UMC capabiltity driver.
+ * @umc_drv: pointer to the driver.
+ */
+static inline int __must_check umc_driver_register(struct umc_driver *umc_drv)
+{
+ return __umc_driver_register(umc_drv, THIS_MODULE, KBUILD_MODNAME);
+}
+void umc_driver_unregister(struct umc_driver *umc_drv);
+
+/*
+ * Utility function you can use to match (umc_driver->match) against a
+ * null-terminated array of 'struct pci_device_id' in
+ * umc_driver->match_data.
+ */
+int umc_match_pci_id(struct umc_driver *umc_drv, struct umc_dev *umc);
+
+/**
+ * umc_parent_pci_dev - return the UMC's parent PCI device or NULL if none
+ * @umc_dev: UMC device whose parent PCI device we are looking for
+ *
+ * DIRTY!!! DON'T RELY ON THIS
+ *
+ * FIXME: This is as dirty as it gets, but we need some way to check
+ * the correct type of umc_dev->parent (so that for example, we can
+ * cast to pci_dev). Casting to pci_dev is necesary because at some
+ * point we need to request resources from the device. Mapping is
+ * easily over come (ioremap and stuff are bus agnostic), but hooking
+ * up to some error handlers (such as pci error handlers) might need
+ * this.
+ *
+ * THIS might (probably will) be removed in the future, so don't count
+ * on it.
+ */
+static inline struct pci_dev *umc_parent_pci_dev(struct umc_dev *umc_dev)
+{
+ struct pci_dev *pci_dev = NULL;
+ if (umc_dev->dev.parent->bus == &pci_bus_type)
+ pci_dev = to_pci_dev(umc_dev->dev.parent);
+ return pci_dev;
+}
+
+/**
+ * umc_dev_get() - reference a UMC device.
+ * @umc_dev: Pointer to UMC device.
+ *
+ * NOTE: we are assuming in this whole scheme that the parent device
+ * is referenced at _probe() time and unreferenced at _remove()
+ * time by the parent's subsystem.
+ */
+static inline struct umc_dev *umc_dev_get(struct umc_dev *umc_dev)
+{
+ get_device(&umc_dev->dev);
+ return umc_dev;
+}
+
+/**
+ * umc_dev_put() - unreference a UMC device.
+ * @umc_dev: Pointer to UMC device.
+ */
+static inline void umc_dev_put(struct umc_dev *umc_dev)
+{
+ put_device(&umc_dev->dev);
+}
+
+/**
+ * umc_set_drvdata - set UMC device's driver data.
+ * @umc_dev: Pointer to UMC device.
+ * @data: Data to set.
+ */
+static inline void umc_set_drvdata(struct umc_dev *umc_dev, void *data)
+{
+ dev_set_drvdata(&umc_dev->dev, data);
+}
+
+/**
+ * umc_get_drvdata - recover UMC device's driver data.
+ * @umc_dev: Pointer to UMC device.
+ */
+static inline void *umc_get_drvdata(struct umc_dev *umc_dev)
+{
+ return dev_get_drvdata(&umc_dev->dev);
+}
+
+int umc_match_pci_id(struct umc_driver *umc_drv, struct umc_dev *umc);
+
+#endif /* #ifndef _LINUX_UWB_UMC_H_ */
diff --git a/include/linux/uwb/whci.h b/include/linux/uwb/whci.h
new file mode 100644
index 000000000000..915ec23042d4
--- /dev/null
+++ b/include/linux/uwb/whci.h
@@ -0,0 +1,117 @@
+/*
+ * Wireless Host Controller Interface for Ultra-Wide-Band and Wireless USB
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ *
+ * References:
+ * [WHCI] Wireless Host Controller Interface Specification for
+ * Certified Wireless Universal Serial Bus, revision 0.95.
+ */
+#ifndef _LINUX_UWB_WHCI_H_
+#define _LINUX_UWB_WHCI_H_
+
+#include <linux/pci.h>
+
+/*
+ * UWB interface capability registers (offsets from UWBBASE)
+ *
+ * [WHCI] section 2.2
+ */
+#define UWBCAPINFO 0x00 /* == UWBCAPDATA(0) */
+# define UWBCAPINFO_TO_N_CAPS(c) (((c) >> 0) & 0xFull)
+#define UWBCAPDATA(n) (8*(n))
+# define UWBCAPDATA_TO_VERSION(c) (((c) >> 32) & 0xFFFFull)
+# define UWBCAPDATA_TO_OFFSET(c) (((c) >> 18) & 0x3FFFull)
+# define UWBCAPDATA_TO_BAR(c) (((c) >> 16) & 0x3ull)
+# define UWBCAPDATA_TO_SIZE(c) ((((c) >> 8) & 0xFFull) * sizeof(u32))
+# define UWBCAPDATA_TO_CAP_ID(c) (((c) >> 0) & 0xFFull)
+
+/* Size of the WHCI capability data (including the RC capability) for
+ a device with n capabilities. */
+#define UWBCAPDATA_SIZE(n) (8 + 8*(n))
+
+
+/*
+ * URC registers (offsets from URCBASE)
+ *
+ * [WHCI] section 2.3
+ */
+#define URCCMD 0x00
+# define URCCMD_RESET (1 << 31) /* UMC Hardware reset */
+# define URCCMD_RS (1 << 30) /* Run/Stop */
+# define URCCMD_EARV (1 << 29) /* Event Address Register Valid */
+# define URCCMD_ACTIVE (1 << 15) /* Command is active */
+# define URCCMD_IWR (1 << 14) /* Interrupt When Ready */
+# define URCCMD_SIZE_MASK 0x00000fff /* Command size mask */
+#define URCSTS 0x04
+# define URCSTS_EPS (1 << 17) /* Event Processing Status */
+# define URCSTS_HALTED (1 << 16) /* RC halted */
+# define URCSTS_HSE (1 << 10) /* Host System Error...fried */
+# define URCSTS_ER (1 << 9) /* Event Ready */
+# define URCSTS_RCI (1 << 8) /* Ready for Command Interrupt */
+# define URCSTS_INT_MASK 0x00000700 /* URC interrupt sources */
+# define URCSTS_ISI 0x000000ff /* Interrupt Source Identification */
+#define URCINTR 0x08
+# define URCINTR_EN_ALL 0x000007ff /* Enable all interrupt sources */
+#define URCCMDADDR 0x10
+#define URCEVTADDR 0x18
+# define URCEVTADDR_OFFSET_MASK 0xfff /* Event pointer offset mask */
+
+
+/** Write 32 bit @value to little endian register at @addr */
+static inline
+void le_writel(u32 value, void __iomem *addr)
+{
+ iowrite32(value, addr);
+}
+
+
+/** Read from 32 bit little endian register at @addr */
+static inline
+u32 le_readl(void __iomem *addr)
+{
+ return ioread32(addr);
+}
+
+
+/** Write 64 bit @value to little endian register at @addr */
+static inline
+void le_writeq(u64 value, void __iomem *addr)
+{
+ iowrite32(value, addr);
+ iowrite32(value >> 32, addr + 4);
+}
+
+
+/** Read from 64 bit little endian register at @addr */
+static inline
+u64 le_readq(void __iomem *addr)
+{
+ u64 value;
+ value = ioread32(addr);
+ value |= (u64)ioread32(addr + 4) << 32;
+ return value;
+}
+
+extern int whci_wait_for(struct device *dev, u32 __iomem *reg,
+ u32 mask, u32 result,
+ unsigned long max_ms, const char *tag);
+
+#endif /* #ifndef _LINUX_UWB_WHCI_H_ */
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 4a535ea1e123..9385c823a978 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -246,6 +246,7 @@ struct v4l2_capability
#define V4L2_CAP_SLICED_VBI_OUTPUT 0x00000080 /* Is a sliced VBI output device */
#define V4L2_CAP_RDS_CAPTURE 0x00000100 /* RDS data capture */
#define V4L2_CAP_VIDEO_OUTPUT_OVERLAY 0x00000200 /* Can do video output overlay */
+#define V4L2_CAP_HW_FREQ_SEEK 0x00000400 /* Can do hardware frequency seek */
#define V4L2_CAP_TUNER 0x00010000 /* has a tuner */
#define V4L2_CAP_AUDIO 0x00020000 /* has audio support */
@@ -1156,6 +1157,14 @@ struct v4l2_frequency
__u32 reserved[8];
};
+struct v4l2_hw_freq_seek {
+ __u32 tuner;
+ enum v4l2_tuner_type type;
+ __u32 seek_upward;
+ __u32 wrap_around;
+ __u32 reserved[8];
+};
+
/*
* A U D I O
*/
@@ -1441,6 +1450,7 @@ struct v4l2_chip_ident {
#define VIDIOC_G_CHIP_IDENT _IOWR ('V', 81, struct v4l2_chip_ident)
#endif
+#define VIDIOC_S_HW_FREQ_SEEK _IOW ('V', 82, struct v4l2_hw_freq_seek)
#ifdef __OLD_VIDIOC_
/* for compatibility, will go away some day */
diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h
index 9405aa6cdf26..38c0571820fb 100644
--- a/include/linux/virtio_net.h
+++ b/include/linux/virtio_net.h
@@ -38,7 +38,7 @@ struct virtio_net_hdr
#define VIRTIO_NET_HDR_GSO_ECN 0x80 // TCP has ECN set
__u8 gso_type;
__u16 hdr_len; /* Ethernet + IP + tcp/udp hdrs */
- __u16 gso_size; /* Bytes to append to gso_hdr_len per frame */
+ __u16 gso_size; /* Bytes to append to hdr_len per frame */
__u16 csum_start; /* Position to start checksumming from */
__u16 csum_offset; /* Offset after that to place checksum */
};
diff --git a/include/linux/wlp.h b/include/linux/wlp.h
new file mode 100644
index 000000000000..62d7cf443b41
--- /dev/null
+++ b/include/linux/wlp.h
@@ -0,0 +1,728 @@
+/*
+ * WiMedia Logical Link Control Protocol (WLP)
+ *
+ * Copyright (C) 2005-2006 Intel Corporation
+ * Reinette Chatre <reinette.chatre@intel.com>
+ *
+ * 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.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ *
+ * FIXME: docs
+ *
+ * - Does not (yet) include support for WLP control frames
+ * WLP Draft 0.99 [6.5].
+ *
+ * A visual representation of the data structures.
+ *
+ * wssidB wssidB
+ * ^ ^
+ * | |
+ * wssidA wssidA
+ * wlp interface { ^ ^
+ * ... | |
+ * ... ... wssid wssid ...
+ * wlp --- ... | |
+ * }; neighbors --> neighbA --> neighbB
+ * ...
+ * wss
+ * ...
+ * eda cache --> neighborA --> neighborB --> neighborC ...
+ */
+
+#ifndef __LINUX__WLP_H_
+#define __LINUX__WLP_H_
+
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/list.h>
+#include <linux/uwb.h>
+
+/**
+ * WLP Protocol ID
+ * WLP Draft 0.99 [6.2]
+ *
+ * The MUX header for all WLP frames
+ */
+#define WLP_PROTOCOL_ID 0x0100
+
+/**
+ * WLP Version
+ * WLP version placed in the association frames (WLP 0.99 [6.6])
+ */
+#define WLP_VERSION 0x10
+
+/**
+ * Bytes needed to print UUID as string
+ */
+#define WLP_WSS_UUID_STRSIZE 48
+
+/**
+ * Bytes needed to print nonce as string
+ */
+#define WLP_WSS_NONCE_STRSIZE 48
+
+
+/**
+ * Size used for WLP name size
+ *
+ * The WSS name is set to 65 bytes, 1 byte larger than the maximum
+ * allowed by the WLP spec. This is to have a null terminated string
+ * for display to the user. A maximum of 64 bytes will still be used
+ * when placing the WSS name field in association frames.
+ */
+#define WLP_WSS_NAME_SIZE 65
+
+/**
+ * Number of bytes added by WLP to data frame
+ *
+ * A data frame transmitted from a host will be placed in a Standard or
+ * Abbreviated WLP frame. These have an extra 4 bytes of header (struct
+ * wlp_frame_std_abbrv_hdr).
+ * When the stack sends this data frame for transmission it needs to ensure
+ * there is enough headroom for this header.
+ */
+#define WLP_DATA_HLEN 4
+
+/**
+ * State of device regarding WLP Service Set
+ *
+ * WLP_WSS_STATE_NONE: the host does not participate in any WSS
+ * WLP_WSS_STATE_PART_ENROLLED: used as part of the enrollment sequence
+ * ("Partial Enroll"). This state is used to
+ * indicate the first part of enrollment that is
+ * unsecure. If the WSS is unsecure then the
+ * state will promptly go to WLP_WSS_STATE_ENROLLED,
+ * if the WSS is not secure then the enrollment
+ * procedure is a few more steps before we are
+ * enrolled.
+ * WLP_WSS_STATE_ENROLLED: the host is enrolled in a WSS
+ * WLP_WSS_STATE_ACTIVE: WSS is activated
+ * WLP_WSS_STATE_CONNECTED: host is connected to neighbor in WSS
+ *
+ */
+enum wlp_wss_state {
+ WLP_WSS_STATE_NONE = 0,
+ WLP_WSS_STATE_PART_ENROLLED,
+ WLP_WSS_STATE_ENROLLED,
+ WLP_WSS_STATE_ACTIVE,
+ WLP_WSS_STATE_CONNECTED,
+};
+
+/**
+ * WSS Secure status
+ * WLP 0.99 Table 6
+ *
+ * Set to one if the WSS is secure, zero if it is not secure
+ */
+enum wlp_wss_sec_status {
+ WLP_WSS_UNSECURE = 0,
+ WLP_WSS_SECURE,
+};
+
+/**
+ * WLP frame type
+ * WLP Draft 0.99 [6.2 Table 1]
+ */
+enum wlp_frame_type {
+ WLP_FRAME_STANDARD = 0,
+ WLP_FRAME_ABBREVIATED,
+ WLP_FRAME_CONTROL,
+ WLP_FRAME_ASSOCIATION,
+};
+
+/**
+ * WLP Association Message Type
+ * WLP Draft 0.99 [6.6.1.2 Table 8]
+ */
+enum wlp_assoc_type {
+ WLP_ASSOC_D1 = 2,
+ WLP_ASSOC_D2 = 3,
+ WLP_ASSOC_M1 = 4,
+ WLP_ASSOC_M2 = 5,
+ WLP_ASSOC_M3 = 7,
+ WLP_ASSOC_M4 = 8,
+ WLP_ASSOC_M5 = 9,
+ WLP_ASSOC_M6 = 10,
+ WLP_ASSOC_M7 = 11,
+ WLP_ASSOC_M8 = 12,
+ WLP_ASSOC_F0 = 14,
+ WLP_ASSOC_E1 = 32,
+ WLP_ASSOC_E2 = 33,
+ WLP_ASSOC_C1 = 34,
+ WLP_ASSOC_C2 = 35,
+ WLP_ASSOC_C3 = 36,
+ WLP_ASSOC_C4 = 37,
+};
+
+/**
+ * WLP Attribute Type
+ * WLP Draft 0.99 [6.6.1 Table 6]
+ */
+enum wlp_attr_type {
+ WLP_ATTR_AUTH = 0x1005, /* Authenticator */
+ WLP_ATTR_DEV_NAME = 0x1011, /* Device Name */
+ WLP_ATTR_DEV_PWD_ID = 0x1012, /* Device Password ID */
+ WLP_ATTR_E_HASH1 = 0x1014, /* E-Hash1 */
+ WLP_ATTR_E_HASH2 = 0x1015, /* E-Hash2 */
+ WLP_ATTR_E_SNONCE1 = 0x1016, /* E-SNonce1 */
+ WLP_ATTR_E_SNONCE2 = 0x1017, /* E-SNonce2 */
+ WLP_ATTR_ENCR_SET = 0x1018, /* Encrypted Settings */
+ WLP_ATTR_ENRL_NONCE = 0x101A, /* Enrollee Nonce */
+ WLP_ATTR_KEYWRAP_AUTH = 0x101E, /* Key Wrap Authenticator */
+ WLP_ATTR_MANUF = 0x1021, /* Manufacturer */
+ WLP_ATTR_MSG_TYPE = 0x1022, /* Message Type */
+ WLP_ATTR_MODEL_NAME = 0x1023, /* Model Name */
+ WLP_ATTR_MODEL_NR = 0x1024, /* Model Number */
+ WLP_ATTR_PUB_KEY = 0x1032, /* Public Key */
+ WLP_ATTR_REG_NONCE = 0x1039, /* Registrar Nonce */
+ WLP_ATTR_R_HASH1 = 0x103D, /* R-Hash1 */
+ WLP_ATTR_R_HASH2 = 0x103E, /* R-Hash2 */
+ WLP_ATTR_R_SNONCE1 = 0x103F, /* R-SNonce1 */
+ WLP_ATTR_R_SNONCE2 = 0x1040, /* R-SNonce2 */
+ WLP_ATTR_SERIAL = 0x1042, /* Serial number */
+ WLP_ATTR_UUID_E = 0x1047, /* UUID-E */
+ WLP_ATTR_UUID_R = 0x1048, /* UUID-R */
+ WLP_ATTR_PRI_DEV_TYPE = 0x1054, /* Primary Device Type */
+ WLP_ATTR_SEC_DEV_TYPE = 0x1055, /* Secondary Device Type */
+ WLP_ATTR_PORT_DEV = 0x1056, /* Portable Device */
+ WLP_ATTR_APP_EXT = 0x1058, /* Application Extension */
+ WLP_ATTR_WLP_VER = 0x2000, /* WLP Version */
+ WLP_ATTR_WSSID = 0x2001, /* WSSID */
+ WLP_ATTR_WSS_NAME = 0x2002, /* WSS Name */
+ WLP_ATTR_WSS_SEC_STAT = 0x2003, /* WSS Secure Status */
+ WLP_ATTR_WSS_BCAST = 0x2004, /* WSS Broadcast Address */
+ WLP_ATTR_WSS_M_KEY = 0x2005, /* WSS Master Key */
+ WLP_ATTR_ACC_ENRL = 0x2006, /* Accepting Enrollment */
+ WLP_ATTR_WSS_INFO = 0x2007, /* WSS Information */
+ WLP_ATTR_WSS_SEL_MTHD = 0x2008, /* WSS Selection Method */
+ WLP_ATTR_ASSC_MTHD_LIST = 0x2009, /* Association Methods List */
+ WLP_ATTR_SEL_ASSC_MTHD = 0x200A, /* Selected Association Method */
+ WLP_ATTR_ENRL_HASH_COMM = 0x200B, /* Enrollee Hash Commitment */
+ WLP_ATTR_WSS_TAG = 0x200C, /* WSS Tag */
+ WLP_ATTR_WSS_VIRT = 0x200D, /* WSS Virtual EUI-48 */
+ WLP_ATTR_WLP_ASSC_ERR = 0x200E, /* WLP Association Error */
+ WLP_ATTR_VNDR_EXT = 0x200F, /* Vendor Extension */
+};
+
+/**
+ * WLP Category ID of primary/secondary device
+ * WLP Draft 0.99 [6.6.1.8 Table 12]
+ */
+enum wlp_dev_category_id {
+ WLP_DEV_CAT_COMPUTER = 1,
+ WLP_DEV_CAT_INPUT,
+ WLP_DEV_CAT_PRINT_SCAN_FAX_COPIER,
+ WLP_DEV_CAT_CAMERA,
+ WLP_DEV_CAT_STORAGE,
+ WLP_DEV_CAT_INFRASTRUCTURE,
+ WLP_DEV_CAT_DISPLAY,
+ WLP_DEV_CAT_MULTIM,
+ WLP_DEV_CAT_GAMING,
+ WLP_DEV_CAT_TELEPHONE,
+ WLP_DEV_CAT_OTHER = 65535,
+};
+
+/**
+ * WLP WSS selection method
+ * WLP Draft 0.99 [6.6.1.6 Table 10]
+ */
+enum wlp_wss_sel_mthd {
+ WLP_WSS_ENRL_SELECT = 1, /* Enrollee selects */
+ WLP_WSS_REG_SELECT, /* Registrar selects */
+};
+
+/**
+ * WLP association error values
+ * WLP Draft 0.99 [6.6.1.5 Table 9]
+ */
+enum wlp_assc_error {
+ WLP_ASSOC_ERROR_NONE,
+ WLP_ASSOC_ERROR_AUTH, /* Authenticator Failure */
+ WLP_ASSOC_ERROR_ROGUE, /* Rogue activity suspected */
+ WLP_ASSOC_ERROR_BUSY, /* Device busy */
+ WLP_ASSOC_ERROR_LOCK, /* Setup Locked */
+ WLP_ASSOC_ERROR_NOT_READY, /* Registrar not ready */
+ WLP_ASSOC_ERROR_INV, /* Invalid WSS selection */
+ WLP_ASSOC_ERROR_MSG_TIME, /* Message timeout */
+ WLP_ASSOC_ERROR_ENR_TIME, /* Enrollment session timeout */
+ WLP_ASSOC_ERROR_PW, /* Device password invalid */
+ WLP_ASSOC_ERROR_VER, /* Unsupported version */
+ WLP_ASSOC_ERROR_INT, /* Internal error */
+ WLP_ASSOC_ERROR_UNDEF, /* Undefined error */
+ WLP_ASSOC_ERROR_NUM, /* Numeric comparison failure */
+ WLP_ASSOC_ERROR_WAIT, /* Waiting for user input */
+};
+
+/**
+ * WLP Parameters
+ * WLP 0.99 [7.7]
+ */
+enum wlp_parameters {
+ WLP_PER_MSG_TIMEOUT = 15, /* Seconds to wait for response to
+ association message. */
+};
+
+/**
+ * WLP IE
+ *
+ * The WLP IE should be included in beacons by all devices.
+ *
+ * The driver can set only a few of the fields in this information element,
+ * most fields are managed by the device self. When the driver needs to set
+ * a field it will only provide values for the fields of interest, the rest
+ * will be filled with zeroes. The fields of interest are:
+ *
+ * Element ID
+ * Length
+ * Capabilities (only to include WSSID Hash list length)
+ * WSSID Hash List fields
+ *
+ * WLP 0.99 [6.7]
+ *
+ * Only the fields that will be used are detailed in this structure, rest
+ * are not detailed or marked as "notused".
+ */
+struct wlp_ie {
+ struct uwb_ie_hdr hdr;
+ union {
+ __le16 capabilities;
+ DECL_BF_LE2(
+ u16 notused:12,
+ u16 hash_length:4
+ ) __attribute__((packed));
+ };
+ __le16 cycle_param;
+ __le16 acw_anchor_addr;
+ u8 wssid_hash_list[];
+} __attribute__((packed));
+
+/**
+ * WLP nonce
+ * WLP Draft 0.99 [6.6.1 Table 6]
+ *
+ * A 128-bit random number often used (E-SNonce1, E-SNonce2, Enrollee
+ * Nonce, Registrar Nonce, R-SNonce1, R-SNonce2). It is passed to HW so
+ * it is packed.
+ */
+struct wlp_nonce {
+ u8 data[16];
+} __attribute__((packed));
+
+/**
+ * WLP UUID
+ * WLP Draft 0.99 [6.6.1 Table 6]
+ *
+ * Universally Unique Identifier (UUID) encoded as an octet string in the
+ * order the octets are shown in string representation in RFC4122. A UUID
+ * is often used (UUID-E, UUID-R, WSSID). It is passed to HW so it is packed.
+ */
+struct wlp_uuid {
+ u8 data[16];
+} __attribute__((packed));
+
+
+/**
+ * Primary and secondary device type attributes
+ * WLP Draft 0.99 [6.6.1.8]
+ */
+struct wlp_dev_type {
+ enum wlp_dev_category_id category:16;
+ u8 OUI[3];
+ u8 OUIsubdiv;
+ __le16 subID;
+} __attribute__((packed));
+
+/**
+ * WLP frame header
+ * WLP Draft 0.99 [6.2]
+ */
+struct wlp_frame_hdr {
+ __le16 mux_hdr; /* WLP_PROTOCOL_ID */
+ enum wlp_frame_type type:8;
+} __attribute__((packed));
+
+/**
+ * WLP attribute field header
+ * WLP Draft 0.99 [6.6.1]
+ *
+ * Header of each attribute found in an association frame
+ */
+struct wlp_attr_hdr {
+ __le16 type;
+ __le16 length;
+} __attribute__((packed));
+
+/**
+ * Device information commonly used together
+ *
+ * Each of these device information elements has a specified range in which it
+ * should fit (WLP 0.99 [Table 6]). This range provided in the spec does not
+ * include the termination null '\0' character (when used in the
+ * association protocol the attribute fields are accompanied
+ * with a "length" field so the full range from the spec can be used for
+ * the value). We thus allocate an extra byte to be able to store a string
+ * of max length with a terminating '\0'.
+ */
+struct wlp_device_info {
+ char name[33];
+ char model_name[33];
+ char manufacturer[65];
+ char model_nr[33];
+ char serial[33];
+ struct wlp_dev_type prim_dev_type;
+};
+
+/**
+ * Macros for the WLP attributes
+ *
+ * There are quite a few attributes (total is 43). The attribute layout can be
+ * in one of three categories: one value, an array, an enum forced to 8 bits.
+ * These macros help with their definitions.
+ */
+#define wlp_attr(type, name) \
+struct wlp_attr_##name { \
+ struct wlp_attr_hdr hdr; \
+ type name; \
+} __attribute__((packed));
+
+#define wlp_attr_array(type, name) \
+struct wlp_attr_##name { \
+ struct wlp_attr_hdr hdr; \
+ type name[]; \
+} __attribute__((packed));
+
+/**
+ * WLP association attribute fields
+ * WLP Draft 0.99 [6.6.1 Table 6]
+ *
+ * Attributes appear in same order as the Table in the spec
+ * FIXME Does not define all attributes yet
+ */
+
+/* Device name: Friendly name of sending device */
+wlp_attr_array(u8, dev_name)
+
+/* Enrollee Nonce: Random number generated by enrollee for an enrollment
+ * session */
+wlp_attr(struct wlp_nonce, enonce)
+
+/* Manufacturer name: Name of manufacturer of the sending device */
+wlp_attr_array(u8, manufacturer)
+
+/* WLP Message Type */
+wlp_attr(u8, msg_type)
+
+/* WLP Model name: Model name of sending device */
+wlp_attr_array(u8, model_name)
+
+/* WLP Model number: Model number of sending device */
+wlp_attr_array(u8, model_nr)
+
+/* Registrar Nonce: Random number generated by registrar for an enrollment
+ * session */
+wlp_attr(struct wlp_nonce, rnonce)
+
+/* Serial number of device */
+wlp_attr_array(u8, serial)
+
+/* UUID of enrollee */
+wlp_attr(struct wlp_uuid, uuid_e)
+
+/* UUID of registrar */
+wlp_attr(struct wlp_uuid, uuid_r)
+
+/* WLP Primary device type */
+wlp_attr(struct wlp_dev_type, prim_dev_type)
+
+/* WLP Secondary device type */
+wlp_attr(struct wlp_dev_type, sec_dev_type)
+
+/* WLP protocol version */
+wlp_attr(u8, version)
+
+/* WLP service set identifier */
+wlp_attr(struct wlp_uuid, wssid)
+
+/* WLP WSS name */
+wlp_attr_array(u8, wss_name)
+
+/* WLP WSS Secure Status */
+wlp_attr(u8, wss_sec_status)
+
+/* WSS Broadcast Address */
+wlp_attr(struct uwb_mac_addr, wss_bcast)
+
+/* WLP Accepting Enrollment */
+wlp_attr(u8, accept_enrl)
+
+/**
+ * WSS information attributes
+ * WLP Draft 0.99 [6.6.3 Table 15]
+ */
+struct wlp_wss_info {
+ struct wlp_attr_wssid wssid;
+ struct wlp_attr_wss_name name;
+ struct wlp_attr_accept_enrl accept;
+ struct wlp_attr_wss_sec_status sec_stat;
+ struct wlp_attr_wss_bcast bcast;
+} __attribute__((packed));
+
+/* WLP WSS Information */
+wlp_attr_array(struct wlp_wss_info, wss_info)
+
+/* WLP WSS Selection method */
+wlp_attr(u8, wss_sel_mthd)
+
+/* WLP WSS tag */
+wlp_attr(u8, wss_tag)
+
+/* WSS Virtual Address */
+wlp_attr(struct uwb_mac_addr, wss_virt)
+
+/* WLP association error */
+wlp_attr(u8, wlp_assc_err)
+
+/**
+ * WLP standard and abbreviated frames
+ *
+ * WLP Draft 0.99 [6.3] and [6.4]
+ *
+ * The difference between the WLP standard frame and the WLP
+ * abbreviated frame is that the standard frame includes the src
+ * and dest addresses from the Ethernet header, the abbreviated frame does
+ * not.
+ * The src/dest (as well as the type/length and client data) are already
+ * defined as part of the Ethernet header, we do not do this here.
+ * From this perspective the standard and abbreviated frames appear the
+ * same - they will be treated differently though.
+ *
+ * The size of this header is also captured in WLP_DATA_HLEN to enable
+ * interfaces to prepare their headroom.
+ */
+struct wlp_frame_std_abbrv_hdr {
+ struct wlp_frame_hdr hdr;
+ u8 tag;
+} __attribute__((packed));
+
+/**
+ * WLP association frames
+ *
+ * WLP Draft 0.99 [6.6]
+ */
+struct wlp_frame_assoc {
+ struct wlp_frame_hdr hdr;
+ enum wlp_assoc_type type:8;
+ struct wlp_attr_version version;
+ struct wlp_attr_msg_type msg_type;
+ u8 attr[];
+} __attribute__((packed));
+
+/* Ethernet to dev address mapping */
+struct wlp_eda {
+ spinlock_t lock;
+ struct list_head cache; /* Eth<->Dev Addr cache */
+};
+
+/**
+ * WSS information temporary storage
+ *
+ * This information is only stored temporarily during discovery. It should
+ * not be stored unless the device is enrolled in the advertised WSS. This
+ * is done mainly because we follow the letter of the spec in this regard.
+ * See WLP 0.99 [7.2.3].
+ * When the device does become enrolled in a WSS the WSS information will
+ * be stored as part of the more comprehensive struct wlp_wss.
+ */
+struct wlp_wss_tmp_info {
+ char name[WLP_WSS_NAME_SIZE];
+ u8 accept_enroll;
+ u8 sec_status;
+ struct uwb_mac_addr bcast;
+};
+
+struct wlp_wssid_e {
+ struct list_head node;
+ struct wlp_uuid wssid;
+ struct wlp_wss_tmp_info *info;
+};
+
+/**
+ * A cache entry of WLP neighborhood
+ *
+ * @node: head of list is wlp->neighbors
+ * @wssid: list of wssids of this neighbor, element is wlp_wssid_e
+ * @info: temporary storage for information learned during discovery. This
+ * storage is used together with the wssid_e temporary storage
+ * during discovery.
+ */
+struct wlp_neighbor_e {
+ struct list_head node;
+ struct wlp_uuid uuid;
+ struct uwb_dev *uwb_dev;
+ struct list_head wssid; /* Elements are wlp_wssid_e */
+ struct wlp_device_info *info;
+};
+
+struct wlp;
+/**
+ * Information for an association session in progress.
+ *
+ * @exp_message: The type of the expected message. Both this message and a
+ * F0 message (which can be sent in response to any
+ * association frame) will be accepted as a valid message for
+ * this session.
+ * @cb: The function that will be called upon receipt of this
+ * message.
+ * @cb_priv: Private data of callback
+ * @data: Data used in association process (always a sk_buff?)
+ * @neighbor: Address of neighbor with which association session is in
+ * progress.
+ */
+struct wlp_session {
+ enum wlp_assoc_type exp_message;
+ void (*cb)(struct wlp *);
+ void *cb_priv;
+ void *data;
+ struct uwb_dev_addr neighbor_addr;
+};
+
+/**
+ * WLP Service Set
+ *
+ * @mutex: used to protect entire WSS structure.
+ *
+ * @name: The WSS name is set to 65 bytes, 1 byte larger than the maximum
+ * allowed by the WLP spec. This is to have a null terminated string
+ * for display to the user. A maximum of 64 bytes will still be used
+ * when placing the WSS name field in association frames.
+ *
+ * @accept_enroll: Accepting enrollment: Set to one if registrar is
+ * accepting enrollment in WSS, or zero otherwise.
+ *
+ * Global and local information for each WSS in which we are enrolled.
+ * WLP 0.99 Section 7.2.1 and Section 7.2.2
+ */
+struct wlp_wss {
+ struct mutex mutex;
+ struct kobject kobj;
+ /* Global properties. */
+ struct wlp_uuid wssid;
+ u8 hash;
+ char name[WLP_WSS_NAME_SIZE];
+ struct uwb_mac_addr bcast;
+ u8 secure_status:1;
+ u8 master_key[16];
+ /* Local properties. */
+ u8 tag;
+ struct uwb_mac_addr virtual_addr;
+ /* Extra */
+ u8 accept_enroll:1;
+ enum wlp_wss_state state;
+};
+
+/**
+ * WLP main structure
+ * @mutex: protect changes to WLP structure. We only allow changes to the
+ * uuid, so currently this mutex only protects this field.
+ */
+struct wlp {
+ struct mutex mutex;
+ struct uwb_rc *rc; /* UWB radio controller */
+ struct uwb_pal pal;
+ struct wlp_eda eda;
+ struct wlp_uuid uuid;
+ struct wlp_session *session;
+ struct wlp_wss wss;
+ struct mutex nbmutex; /* Neighbor mutex protects neighbors list */
+ struct list_head neighbors; /* Elements are wlp_neighbor_e */
+ struct uwb_notifs_handler uwb_notifs_handler;
+ struct wlp_device_info *dev_info;
+ void (*fill_device_info)(struct wlp *wlp, struct wlp_device_info *info);
+ int (*xmit_frame)(struct wlp *, struct sk_buff *,
+ struct uwb_dev_addr *);
+ void (*stop_queue)(struct wlp *);
+ void (*start_queue)(struct wlp *);
+};
+
+/* sysfs */
+
+
+struct wlp_wss_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct wlp_wss *wss, char *buf);
+ ssize_t (*store)(struct wlp_wss *wss, const char *buf, size_t count);
+};
+
+#define WSS_ATTR(_name, _mode, _show, _store) \
+static struct wlp_wss_attribute wss_attr_##_name = __ATTR(_name, _mode, \
+ _show, _store)
+
+extern int wlp_setup(struct wlp *, struct uwb_rc *);
+extern void wlp_remove(struct wlp *);
+extern ssize_t wlp_neighborhood_show(struct wlp *, char *);
+extern int wlp_wss_setup(struct net_device *, struct wlp_wss *);
+extern void wlp_wss_remove(struct wlp_wss *);
+extern ssize_t wlp_wss_activate_show(struct wlp_wss *, char *);
+extern ssize_t wlp_wss_activate_store(struct wlp_wss *, const char *, size_t);
+extern ssize_t wlp_eda_show(struct wlp *, char *);
+extern ssize_t wlp_eda_store(struct wlp *, const char *, size_t);
+extern ssize_t wlp_uuid_show(struct wlp *, char *);
+extern ssize_t wlp_uuid_store(struct wlp *, const char *, size_t);
+extern ssize_t wlp_dev_name_show(struct wlp *, char *);
+extern ssize_t wlp_dev_name_store(struct wlp *, const char *, size_t);
+extern ssize_t wlp_dev_manufacturer_show(struct wlp *, char *);
+extern ssize_t wlp_dev_manufacturer_store(struct wlp *, const char *, size_t);
+extern ssize_t wlp_dev_model_name_show(struct wlp *, char *);
+extern ssize_t wlp_dev_model_name_store(struct wlp *, const char *, size_t);
+extern ssize_t wlp_dev_model_nr_show(struct wlp *, char *);
+extern ssize_t wlp_dev_model_nr_store(struct wlp *, const char *, size_t);
+extern ssize_t wlp_dev_serial_show(struct wlp *, char *);
+extern ssize_t wlp_dev_serial_store(struct wlp *, const char *, size_t);
+extern ssize_t wlp_dev_prim_category_show(struct wlp *, char *);
+extern ssize_t wlp_dev_prim_category_store(struct wlp *, const char *,
+ size_t);
+extern ssize_t wlp_dev_prim_OUI_show(struct wlp *, char *);
+extern ssize_t wlp_dev_prim_OUI_store(struct wlp *, const char *, size_t);
+extern ssize_t wlp_dev_prim_OUI_sub_show(struct wlp *, char *);
+extern ssize_t wlp_dev_prim_OUI_sub_store(struct wlp *, const char *,
+ size_t);
+extern ssize_t wlp_dev_prim_subcat_show(struct wlp *, char *);
+extern ssize_t wlp_dev_prim_subcat_store(struct wlp *, const char *,
+ size_t);
+extern int wlp_receive_frame(struct device *, struct wlp *, struct sk_buff *,
+ struct uwb_dev_addr *);
+extern int wlp_prepare_tx_frame(struct device *, struct wlp *,
+ struct sk_buff *, struct uwb_dev_addr *);
+
+/**
+ * Initialize WSS
+ */
+static inline
+void wlp_wss_init(struct wlp_wss *wss)
+{
+ mutex_init(&wss->mutex);
+}
+
+static inline
+void wlp_init(struct wlp *wlp)
+{
+ INIT_LIST_HEAD(&wlp->neighbors);
+ mutex_init(&wlp->mutex);
+ mutex_init(&wlp->nbmutex);
+ wlp_wss_init(&wlp->wss);
+}
+
+
+#endif /* #ifndef __LINUX__WLP_H_ */
diff --git a/include/linux/writeback.h b/include/linux/writeback.h
index f462439cc288..bd91987c065f 100644
--- a/include/linux/writeback.h
+++ b/include/linux/writeback.h
@@ -105,6 +105,8 @@ extern int vm_highmem_is_dirtyable;
extern int block_dump;
extern int laptop_mode;
+extern unsigned long determine_dirtyable_memory(void);
+
extern int dirty_ratio_handler(struct ctl_table *table, int write,
struct file *filp, void __user *buffer, size_t *lenp,
loff_t *ppos);
diff --git a/include/media/cx25840.h b/include/media/cx25840.h
index cd599ad29fb2..db431d513f2f 100644
--- a/include/media/cx25840.h
+++ b/include/media/cx25840.h
@@ -32,12 +32,16 @@ enum cx25840_video_input {
CX25840_COMPOSITE7,
CX25840_COMPOSITE8,
- /* S-Video inputs consist of one luma input (In1-In4) ORed with one
+ /* S-Video inputs consist of one luma input (In1-In8) ORed with one
chroma input (In5-In8) */
CX25840_SVIDEO_LUMA1 = 0x10,
CX25840_SVIDEO_LUMA2 = 0x20,
CX25840_SVIDEO_LUMA3 = 0x30,
CX25840_SVIDEO_LUMA4 = 0x40,
+ CX25840_SVIDEO_LUMA5 = 0x50,
+ CX25840_SVIDEO_LUMA6 = 0x60,
+ CX25840_SVIDEO_LUMA7 = 0x70,
+ CX25840_SVIDEO_LUMA8 = 0x80,
CX25840_SVIDEO_CHROMA4 = 0x400,
CX25840_SVIDEO_CHROMA5 = 0x500,
CX25840_SVIDEO_CHROMA6 = 0x600,
diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h
index 33f01ae08f76..3beba8b9177c 100644
--- a/include/media/v4l2-dev.h
+++ b/include/media/v4l2-dev.h
@@ -118,74 +118,76 @@ struct video_device
enum v4l2_priority p);
/* VIDIOC_ENUM_FMT handlers */
- int (*vidioc_enum_fmt_cap) (struct file *file, void *fh,
+ int (*vidioc_enum_fmt_vid_cap) (struct file *file, void *fh,
struct v4l2_fmtdesc *f);
- int (*vidioc_enum_fmt_overlay) (struct file *file, void *fh,
+ int (*vidioc_enum_fmt_vid_overlay) (struct file *file, void *fh,
struct v4l2_fmtdesc *f);
- int (*vidioc_enum_fmt_vbi) (struct file *file, void *fh,
+ int (*vidioc_enum_fmt_vid_out) (struct file *file, void *fh,
struct v4l2_fmtdesc *f);
- int (*vidioc_enum_fmt_vbi_capture) (struct file *file, void *fh,
- struct v4l2_fmtdesc *f);
- int (*vidioc_enum_fmt_video_output)(struct file *file, void *fh,
- struct v4l2_fmtdesc *f);
- int (*vidioc_enum_fmt_output_overlay) (struct file *file, void *fh,
- struct v4l2_fmtdesc *f);
- int (*vidioc_enum_fmt_vbi_output) (struct file *file, void *fh,
+#if 1
+ /* deprecated, will be removed in 2.6.28 */
+ int (*vidioc_enum_fmt_vbi_cap) (struct file *file, void *fh,
struct v4l2_fmtdesc *f);
+#endif
int (*vidioc_enum_fmt_type_private)(struct file *file, void *fh,
struct v4l2_fmtdesc *f);
/* VIDIOC_G_FMT handlers */
- int (*vidioc_g_fmt_cap) (struct file *file, void *fh,
+ int (*vidioc_g_fmt_vid_cap) (struct file *file, void *fh,
struct v4l2_format *f);
- int (*vidioc_g_fmt_overlay) (struct file *file, void *fh,
+ int (*vidioc_g_fmt_vid_overlay)(struct file *file, void *fh,
struct v4l2_format *f);
- int (*vidioc_g_fmt_vbi) (struct file *file, void *fh,
+ int (*vidioc_g_fmt_vid_out) (struct file *file, void *fh,
struct v4l2_format *f);
- int (*vidioc_g_fmt_vbi_output) (struct file *file, void *fh,
+ int (*vidioc_g_fmt_vid_out_overlay)(struct file *file, void *fh,
struct v4l2_format *f);
- int (*vidioc_g_fmt_vbi_capture)(struct file *file, void *fh,
+ int (*vidioc_g_fmt_vbi_cap) (struct file *file, void *fh,
struct v4l2_format *f);
- int (*vidioc_g_fmt_video_output)(struct file *file, void *fh,
+ int (*vidioc_g_fmt_vbi_out) (struct file *file, void *fh,
struct v4l2_format *f);
- int (*vidioc_g_fmt_output_overlay) (struct file *file, void *fh,
+ int (*vidioc_g_fmt_sliced_vbi_cap)(struct file *file, void *fh,
+ struct v4l2_format *f);
+ int (*vidioc_g_fmt_sliced_vbi_out)(struct file *file, void *fh,
struct v4l2_format *f);
int (*vidioc_g_fmt_type_private)(struct file *file, void *fh,
struct v4l2_format *f);
/* VIDIOC_S_FMT handlers */
- int (*vidioc_s_fmt_cap) (struct file *file, void *fh,
+ int (*vidioc_s_fmt_vid_cap) (struct file *file, void *fh,
struct v4l2_format *f);
-
- int (*vidioc_s_fmt_overlay) (struct file *file, void *fh,
+ int (*vidioc_s_fmt_vid_overlay)(struct file *file, void *fh,
struct v4l2_format *f);
- int (*vidioc_s_fmt_vbi) (struct file *file, void *fh,
+ int (*vidioc_s_fmt_vid_out) (struct file *file, void *fh,
struct v4l2_format *f);
- int (*vidioc_s_fmt_vbi_output) (struct file *file, void *fh,
+ int (*vidioc_s_fmt_vid_out_overlay)(struct file *file, void *fh,
struct v4l2_format *f);
- int (*vidioc_s_fmt_vbi_capture)(struct file *file, void *fh,
+ int (*vidioc_s_fmt_vbi_cap) (struct file *file, void *fh,
struct v4l2_format *f);
- int (*vidioc_s_fmt_video_output)(struct file *file, void *fh,
+ int (*vidioc_s_fmt_vbi_out) (struct file *file, void *fh,
struct v4l2_format *f);
- int (*vidioc_s_fmt_output_overlay) (struct file *file, void *fh,
+ int (*vidioc_s_fmt_sliced_vbi_cap)(struct file *file, void *fh,
+ struct v4l2_format *f);
+ int (*vidioc_s_fmt_sliced_vbi_out)(struct file *file, void *fh,
struct v4l2_format *f);
int (*vidioc_s_fmt_type_private)(struct file *file, void *fh,
struct v4l2_format *f);
/* VIDIOC_TRY_FMT handlers */
- int (*vidioc_try_fmt_cap) (struct file *file, void *fh,
+ int (*vidioc_try_fmt_vid_cap) (struct file *file, void *fh,
+ struct v4l2_format *f);
+ int (*vidioc_try_fmt_vid_overlay)(struct file *file, void *fh,
struct v4l2_format *f);
- int (*vidioc_try_fmt_overlay) (struct file *file, void *fh,
+ int (*vidioc_try_fmt_vid_out) (struct file *file, void *fh,
struct v4l2_format *f);
- int (*vidioc_try_fmt_vbi) (struct file *file, void *fh,
+ int (*vidioc_try_fmt_vid_out_overlay)(struct file *file, void *fh,
struct v4l2_format *f);
- int (*vidioc_try_fmt_vbi_output) (struct file *file, void *fh,
+ int (*vidioc_try_fmt_vbi_cap) (struct file *file, void *fh,
struct v4l2_format *f);
- int (*vidioc_try_fmt_vbi_capture)(struct file *file, void *fh,
+ int (*vidioc_try_fmt_vbi_out) (struct file *file, void *fh,
struct v4l2_format *f);
- int (*vidioc_try_fmt_video_output)(struct file *file, void *fh,
+ int (*vidioc_try_fmt_sliced_vbi_cap)(struct file *file, void *fh,
struct v4l2_format *f);
- int (*vidioc_try_fmt_output_overlay)(struct file *file, void *fh,
+ int (*vidioc_try_fmt_sliced_vbi_out)(struct file *file, void *fh,
struct v4l2_format *f);
int (*vidioc_try_fmt_type_private)(struct file *file, void *fh,
struct v4l2_format *f);
@@ -212,8 +214,9 @@ struct video_device
int (*vidioc_streamoff)(struct file *file, void *fh, enum v4l2_buf_type i);
/* Standard handling
- G_STD and ENUMSTD are handled by videodev.c
+ ENUMSTD is handled by videodev.c
*/
+ int (*vidioc_g_std) (struct file *file, void *fh, v4l2_std_id *norm);
int (*vidioc_s_std) (struct file *file, void *fh, v4l2_std_id *norm);
int (*vidioc_querystd) (struct file *file, void *fh, v4l2_std_id *a);
@@ -224,7 +227,7 @@ struct video_device
int (*vidioc_s_input) (struct file *file, void *fh, unsigned int i);
/* Output handling */
- int (*vidioc_enumoutput) (struct file *file, void *fh,
+ int (*vidioc_enum_output) (struct file *file, void *fh,
struct v4l2_output *a);
int (*vidioc_g_output) (struct file *file, void *fh, unsigned int *i);
int (*vidioc_s_output) (struct file *file, void *fh, unsigned int i);
@@ -306,6 +309,8 @@ struct video_device
/* Log status ioctl */
int (*vidioc_log_status) (struct file *file, void *fh);
+ int (*vidioc_s_hw_freq_seek) (struct file *file, void *fh,
+ struct v4l2_hw_freq_seek *a);
/* Debugging ioctls */
#ifdef CONFIG_VIDEO_ADV_DEBUG
diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h
index a42cd63d241a..9fabe5b38912 100644
--- a/include/net/inet_sock.h
+++ b/include/net/inet_sock.h
@@ -197,4 +197,14 @@ static inline int inet_iif(const struct sk_buff *skb)
return skb->rtable->rt_iif;
}
+static inline struct request_sock *inet_reqsk_alloc(struct request_sock_ops *ops)
+{
+ struct request_sock *req = reqsk_alloc(ops);
+
+ if (req != NULL)
+ inet_rsk(req)->opt = NULL;
+
+ return req;
+}
+
#endif /* _INET_SOCK_H */
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 633147cb6bbc..d448310c82c1 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -433,7 +433,6 @@ extern struct sk_buff * tcp_make_synack(struct sock *sk,
extern int tcp_disconnect(struct sock *sk, int flags);
-extern void tcp_unhash(struct sock *sk);
/* From syncookies.c */
extern __u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS];
diff --git a/include/rdma/ib_addr.h b/include/rdma/ib_addr.h
index c36750ff6ae8..b42bdd000419 100644
--- a/include/rdma/ib_addr.h
+++ b/include/rdma/ib_addr.h
@@ -2,29 +2,33 @@
* Copyright (c) 2005 Voltaire Inc. All rights reserved.
* Copyright (c) 2005 Intel Corporation. All rights reserved.
*
- * This Software is licensed under one of the following licenses:
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
*
- * 1) under the terms of the "Common Public License 1.0" a copy of which is
- * available from the Open Source Initiative, see
- * http://www.opensource.org/licenses/cpl.php.
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
*
- * 2) under the terms of the "The BSD License" a copy of which is
- * available from the Open Source Initiative, see
- * http://www.opensource.org/licenses/bsd-license.php.
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
*
- * 3) under the terms of the "GNU General Public License (GPL) Version 2" a
- * copy of which is available from the Open Source Initiative, see
- * http://www.opensource.org/licenses/gpl-license.php.
- *
- * Licensee has the right to choose one of the above licenses.
- *
- * Redistributions of source code must retain the above copyright
- * notice and one of the license notices.
- *
- * Redistributions in binary form must reproduce both the above copyright
- * notice, one of the license notices in the documentation
- * and/or other materials provided with the distribution.
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
*
+ * 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.
*/
#if !defined(IB_ADDR_H)
diff --git a/include/rdma/ib_cache.h b/include/rdma/ib_cache.h
index f179d233ffc3..00a2b8ec327f 100644
--- a/include/rdma/ib_cache.h
+++ b/include/rdma/ib_cache.h
@@ -30,8 +30,6 @@
* 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.
- *
- * $Id: ib_cache.h 1349 2004-12-16 21:09:43Z roland $
*/
#ifndef _IB_CACHE_H
diff --git a/include/rdma/ib_cm.h b/include/rdma/ib_cm.h
index a627c8682d2f..ec7c6d99ed3f 100644
--- a/include/rdma/ib_cm.h
+++ b/include/rdma/ib_cm.h
@@ -31,8 +31,6 @@
* 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.
- *
- * $Id: ib_cm.h 4311 2005-12-05 18:42:01Z sean.hefty $
*/
#if !defined(IB_CM_H)
#define IB_CM_H
diff --git a/include/rdma/ib_fmr_pool.h b/include/rdma/ib_fmr_pool.h
index 00dadbf94e1d..f62b842e6596 100644
--- a/include/rdma/ib_fmr_pool.h
+++ b/include/rdma/ib_fmr_pool.h
@@ -29,8 +29,6 @@
* 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.
- *
- * $Id: ib_fmr_pool.h 2730 2005-06-28 16:43:03Z sean.hefty $
*/
#if !defined(IB_FMR_POOL_H)
@@ -61,7 +59,7 @@ struct ib_fmr_pool_param {
int pool_size;
int dirty_watermark;
void (*flush_function)(struct ib_fmr_pool *pool,
- void * arg);
+ void *arg);
void *flush_arg;
unsigned cache:1;
};
diff --git a/include/rdma/ib_mad.h b/include/rdma/ib_mad.h
index 7228c056b9e9..5f6c40fffcf4 100644
--- a/include/rdma/ib_mad.h
+++ b/include/rdma/ib_mad.h
@@ -32,11 +32,9 @@
* 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.
- *
- * $Id: ib_mad.h 5596 2006-03-03 01:00:07Z sean.hefty $
*/
-#if !defined( IB_MAD_H )
+#if !defined(IB_MAD_H)
#define IB_MAD_H
#include <linux/list.h>
@@ -194,8 +192,7 @@ struct ib_vendor_mad {
u8 data[IB_MGMT_VENDOR_DATA];
};
-struct ib_class_port_info
-{
+struct ib_class_port_info {
u8 base_version;
u8 class_version;
__be16 capability_mask;
@@ -614,11 +611,11 @@ int ib_process_mad_wc(struct ib_mad_agent *mad_agent,
* any class specific header, and MAD data area.
* If @rmpp_active is set, the RMPP header will be initialized for sending.
*/
-struct ib_mad_send_buf * ib_create_send_mad(struct ib_mad_agent *mad_agent,
- u32 remote_qpn, u16 pkey_index,
- int rmpp_active,
- int hdr_len, int data_len,
- gfp_t gfp_mask);
+struct ib_mad_send_buf *ib_create_send_mad(struct ib_mad_agent *mad_agent,
+ u32 remote_qpn, u16 pkey_index,
+ int rmpp_active,
+ int hdr_len, int data_len,
+ gfp_t gfp_mask);
/**
* ib_is_mad_class_rmpp - returns whether given management class
diff --git a/include/rdma/ib_pack.h b/include/rdma/ib_pack.h
index f926020d6331..d7fc45c4eba9 100644
--- a/include/rdma/ib_pack.h
+++ b/include/rdma/ib_pack.h
@@ -28,8 +28,6 @@
* 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.
- *
- * $Id: ib_pack.h 1349 2004-12-16 21:09:43Z roland $
*/
#ifndef IB_PACK_H
diff --git a/include/rdma/ib_sa.h b/include/rdma/ib_sa.h
index 942692b0b92e..3841c1aff692 100644
--- a/include/rdma/ib_sa.h
+++ b/include/rdma/ib_sa.h
@@ -30,8 +30,6 @@
* 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.
- *
- * $Id: ib_sa.h 2811 2005-07-06 18:11:43Z halr $
*/
#ifndef IB_SA_H
diff --git a/include/rdma/ib_smi.h b/include/rdma/ib_smi.h
index f29af135ba83..aaca0878668f 100644
--- a/include/rdma/ib_smi.h
+++ b/include/rdma/ib_smi.h
@@ -32,11 +32,9 @@
* 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.
- *
- * $Id: ib_smi.h 1389 2004-12-27 22:56:47Z roland $
*/
-#if !defined( IB_SMI_H )
+#if !defined(IB_SMI_H)
#define IB_SMI_H
#include <rdma/ib_mad.h>
diff --git a/include/rdma/ib_user_cm.h b/include/rdma/ib_user_cm.h
index 37650afb982c..bd3d380781e0 100644
--- a/include/rdma/ib_user_cm.h
+++ b/include/rdma/ib_user_cm.h
@@ -29,8 +29,6 @@
* 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.
- *
- * $Id: ib_user_cm.h 4019 2005-11-11 00:33:09Z sean.hefty $
*/
#ifndef IB_USER_CM_H
diff --git a/include/rdma/ib_user_mad.h b/include/rdma/ib_user_mad.h
index 29d2c7205a90..d6fce1cbdb90 100644
--- a/include/rdma/ib_user_mad.h
+++ b/include/rdma/ib_user_mad.h
@@ -29,8 +29,6 @@
* 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.
- *
- * $Id: ib_user_mad.h 2814 2005-07-06 19:14:09Z halr $
*/
#ifndef IB_USER_MAD_H
diff --git a/include/rdma/ib_user_verbs.h b/include/rdma/ib_user_verbs.h
index 8d65bf0a625b..a17f77106149 100644
--- a/include/rdma/ib_user_verbs.h
+++ b/include/rdma/ib_user_verbs.h
@@ -31,8 +31,6 @@
* 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.
- *
- * $Id: ib_user_verbs.h 4019 2005-11-11 00:33:09Z sean.hefty $
*/
#ifndef IB_USER_VERBS_H
@@ -291,7 +289,10 @@ struct ib_uverbs_wc {
__u32 opcode;
__u32 vendor_err;
__u32 byte_len;
- __u32 imm_data;
+ union {
+ __u32 imm_data;
+ __u32 invalidate_rkey;
+ } ex;
__u32 qp_num;
__u32 src_qp;
__u32 wc_flags;
diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h
index 31d30b1852e8..4c3c76b03c8c 100644
--- a/include/rdma/ib_verbs.h
+++ b/include/rdma/ib_verbs.h
@@ -34,8 +34,6 @@
* 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.
- *
- * $Id: ib_verbs.h 1349 2004-12-16 21:09:43Z roland $
*/
#if !defined(IB_VERBS_H)
@@ -105,6 +103,7 @@ enum ib_device_cap_flags {
*/
IB_DEVICE_UD_IP_CSUM = (1<<18),
IB_DEVICE_UD_TSO = (1<<19),
+ IB_DEVICE_MEM_MGT_EXTENSIONS = (1<<21),
};
enum ib_atomic_cap {
@@ -150,6 +149,7 @@ struct ib_device_attr {
int max_srq;
int max_srq_wr;
int max_srq_sge;
+ unsigned int max_fast_reg_page_list_len;
u16 max_pkeys;
u8 local_ca_ack_delay;
};
@@ -413,6 +413,8 @@ enum ib_wc_opcode {
IB_WC_FETCH_ADD,
IB_WC_BIND_MW,
IB_WC_LSO,
+ IB_WC_LOCAL_INV,
+ IB_WC_FAST_REG_MR,
/*
* Set value of IB_WC_RECV so consumers can test if a completion is a
* receive by testing (opcode & IB_WC_RECV).
@@ -423,7 +425,8 @@ enum ib_wc_opcode {
enum ib_wc_flags {
IB_WC_GRH = 1,
- IB_WC_WITH_IMM = (1<<1)
+ IB_WC_WITH_IMM = (1<<1),
+ IB_WC_WITH_INVALIDATE = (1<<2),
};
struct ib_wc {
@@ -433,7 +436,10 @@ struct ib_wc {
u32 vendor_err;
u32 byte_len;
struct ib_qp *qp;
- __be32 imm_data;
+ union {
+ __be32 imm_data;
+ u32 invalidate_rkey;
+ } ex;
u32 src_qp;
int wc_flags;
u16 pkey_index;
@@ -627,6 +633,9 @@ enum ib_wr_opcode {
IB_WR_ATOMIC_FETCH_AND_ADD,
IB_WR_LSO,
IB_WR_SEND_WITH_INV,
+ IB_WR_RDMA_READ_WITH_INV,
+ IB_WR_LOCAL_INV,
+ IB_WR_FAST_REG_MR,
};
enum ib_send_flags {
@@ -643,6 +652,12 @@ struct ib_sge {
u32 lkey;
};
+struct ib_fast_reg_page_list {
+ struct ib_device *device;
+ u64 *page_list;
+ unsigned int max_page_list_len;
+};
+
struct ib_send_wr {
struct ib_send_wr *next;
u64 wr_id;
@@ -675,6 +690,16 @@ struct ib_send_wr {
u16 pkey_index; /* valid for GSI only */
u8 port_num; /* valid for DR SMPs on switch only */
} ud;
+ struct {
+ u64 iova_start;
+ struct ib_fast_reg_page_list *page_list;
+ unsigned int page_shift;
+ unsigned int page_list_len;
+ unsigned int first_byte_offset;
+ u32 length;
+ int access_flags;
+ u32 rkey;
+ } fast_reg;
} wr;
};
@@ -777,7 +802,7 @@ struct ib_cq {
struct ib_uobject *uobject;
ib_comp_handler comp_handler;
void (*event_handler)(struct ib_event *, void *);
- void * cq_context;
+ void *cq_context;
int cqe;
atomic_t usecnt; /* count number of work queues */
};
@@ -883,7 +908,7 @@ struct ib_dma_mapping_ops {
void (*sync_single_for_cpu)(struct ib_device *dev,
u64 dma_handle,
size_t size,
- enum dma_data_direction dir);
+ enum dma_data_direction dir);
void (*sync_single_for_device)(struct ib_device *dev,
u64 dma_handle,
size_t size,
@@ -1013,6 +1038,11 @@ struct ib_device {
int (*query_mr)(struct ib_mr *mr,
struct ib_mr_attr *mr_attr);
int (*dereg_mr)(struct ib_mr *mr);
+ struct ib_mr * (*alloc_fast_reg_mr)(struct ib_pd *pd,
+ int max_page_list_len);
+ struct ib_fast_reg_page_list * (*alloc_fast_reg_page_list)(struct ib_device *device,
+ int page_list_len);
+ void (*free_fast_reg_page_list)(struct ib_fast_reg_page_list *page_list);
int (*rereg_phys_mr)(struct ib_mr *mr,
int mr_rereg_mask,
struct ib_pd *pd,
@@ -1807,6 +1837,54 @@ int ib_query_mr(struct ib_mr *mr, struct ib_mr_attr *mr_attr);
int ib_dereg_mr(struct ib_mr *mr);
/**
+ * ib_alloc_fast_reg_mr - Allocates memory region usable with the
+ * IB_WR_FAST_REG_MR send work request.
+ * @pd: The protection domain associated with the region.
+ * @max_page_list_len: requested max physical buffer list length to be
+ * used with fast register work requests for this MR.
+ */
+struct ib_mr *ib_alloc_fast_reg_mr(struct ib_pd *pd, int max_page_list_len);
+
+/**
+ * ib_alloc_fast_reg_page_list - Allocates a page list array
+ * @device - ib device pointer.
+ * @page_list_len - size of the page list array to be allocated.
+ *
+ * This allocates and returns a struct ib_fast_reg_page_list * and a
+ * page_list array that is at least page_list_len in size. The actual
+ * size is returned in max_page_list_len. The caller is responsible
+ * for initializing the contents of the page_list array before posting
+ * a send work request with the IB_WC_FAST_REG_MR opcode.
+ *
+ * The page_list array entries must be translated using one of the
+ * ib_dma_*() functions just like the addresses passed to
+ * ib_map_phys_fmr(). Once the ib_post_send() is issued, the struct
+ * ib_fast_reg_page_list must not be modified by the caller until the
+ * IB_WC_FAST_REG_MR work request completes.
+ */
+struct ib_fast_reg_page_list *ib_alloc_fast_reg_page_list(
+ struct ib_device *device, int page_list_len);
+
+/**
+ * ib_free_fast_reg_page_list - Deallocates a previously allocated
+ * page list array.
+ * @page_list - struct ib_fast_reg_page_list pointer to be deallocated.
+ */
+void ib_free_fast_reg_page_list(struct ib_fast_reg_page_list *page_list);
+
+/**
+ * ib_update_fast_reg_key - updates the key portion of the fast_reg MR
+ * R_Key and L_Key.
+ * @mr - struct ib_mr pointer to be updated.
+ * @newkey - new key to be used.
+ */
+static inline void ib_update_fast_reg_key(struct ib_mr *mr, u8 newkey)
+{
+ mr->lkey = (mr->lkey & 0xffffff00) | newkey;
+ mr->rkey = (mr->rkey & 0xffffff00) | newkey;
+}
+
+/**
* ib_alloc_mw - Allocates a memory window.
* @pd: The protection domain associated with the memory window.
*/
diff --git a/include/rdma/iw_cm.h b/include/rdma/iw_cm.h
index aeefa9b740dc..cbb822e8d791 100644
--- a/include/rdma/iw_cm.h
+++ b/include/rdma/iw_cm.h
@@ -62,7 +62,7 @@ struct iw_cm_event {
struct sockaddr_in remote_addr;
void *private_data;
u8 private_data_len;
- void* provider_data;
+ void *provider_data;
};
/**
diff --git a/include/rdma/rdma_cm.h b/include/rdma/rdma_cm.h
index 010f876f41d8..22bb2e7bab1a 100644
--- a/include/rdma/rdma_cm.h
+++ b/include/rdma/rdma_cm.h
@@ -2,29 +2,33 @@
* Copyright (c) 2005 Voltaire Inc. All rights reserved.
* Copyright (c) 2005 Intel Corporation. All rights reserved.
*
- * This Software is licensed under one of the following licenses:
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
*
- * 1) under the terms of the "Common Public License 1.0" a copy of which is
- * available from the Open Source Initiative, see
- * http://www.opensource.org/licenses/cpl.php.
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
*
- * 2) under the terms of the "The BSD License" a copy of which is
- * available from the Open Source Initiative, see
- * http://www.opensource.org/licenses/bsd-license.php.
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
*
- * 3) under the terms of the "GNU General Public License (GPL) Version 2" a
- * copy of which is available from the Open Source Initiative, see
- * http://www.opensource.org/licenses/gpl-license.php.
- *
- * Licensee has the right to choose one of the above licenses.
- *
- * Redistributions of source code must retain the above copyright
- * notice and one of the license notices.
- *
- * Redistributions in binary form must reproduce both the above copyright
- * notice, one of the license notices in the documentation
- * and/or other materials provided with the distribution.
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
*
+ * 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.
*/
#if !defined(RDMA_CM_H)
@@ -57,11 +61,11 @@ enum rdma_cm_event_type {
};
enum rdma_port_space {
- RDMA_PS_SDP = 0x0001,
- RDMA_PS_IPOIB= 0x0002,
- RDMA_PS_TCP = 0x0106,
- RDMA_PS_UDP = 0x0111,
- RDMA_PS_SCTP = 0x0183
+ RDMA_PS_SDP = 0x0001,
+ RDMA_PS_IPOIB = 0x0002,
+ RDMA_PS_TCP = 0x0106,
+ RDMA_PS_UDP = 0x0111,
+ RDMA_PS_SCTP = 0x0183
};
struct rdma_addr {
diff --git a/include/rdma/rdma_cm_ib.h b/include/rdma/rdma_cm_ib.h
index 950424b38f16..2389c3b45404 100644
--- a/include/rdma/rdma_cm_ib.h
+++ b/include/rdma/rdma_cm_ib.h
@@ -1,29 +1,33 @@
/*
* Copyright (c) 2006 Intel Corporation. All rights reserved.
*
- * This Software is licensed under one of the following licenses:
- *
- * 1) under the terms of the "Common Public License 1.0" a copy of which is
- * available from the Open Source Initiative, see
- * http://www.opensource.org/licenses/cpl.php.
- *
- * 2) under the terms of the "The BSD License" a copy of which is
- * available from the Open Source Initiative, see
- * http://www.opensource.org/licenses/bsd-license.php.
- *
- * 3) under the terms of the "GNU General Public License (GPL) Version 2" a
- * copy of which is available from the Open Source Initiative, see
- * http://www.opensource.org/licenses/gpl-license.php.
- *
- * Licensee has the right to choose one of the above licenses.
- *
- * Redistributions of source code must retain the above copyright
- * notice and one of the license notices.
- *
- * Redistributions in binary form must reproduce both the above copyright
- * notice, one of the license notices in the documentation
- * and/or other materials provided with the distribution.
- *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 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.
*/
#if !defined(RDMA_CM_IB_H)
diff --git a/include/scsi/iscsi_if.h b/include/scsi/iscsi_if.h
index e19e58423166..16be12f1cbe8 100644
--- a/include/scsi/iscsi_if.h
+++ b/include/scsi/iscsi_if.h
@@ -50,6 +50,7 @@ enum iscsi_uevent_e {
ISCSI_UEVENT_TGT_DSCVR = UEVENT_BASE + 15,
ISCSI_UEVENT_SET_HOST_PARAM = UEVENT_BASE + 16,
ISCSI_UEVENT_UNBIND_SESSION = UEVENT_BASE + 17,
+ ISCSI_UEVENT_CREATE_BOUND_SESSION = UEVENT_BASE + 18,
/* up events */
ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1,
@@ -78,6 +79,12 @@ struct iscsi_uevent {
uint16_t cmds_max;
uint16_t queue_depth;
} c_session;
+ struct msg_create_bound_session {
+ uint64_t ep_handle;
+ uint32_t initial_cmdsn;
+ uint16_t cmds_max;
+ uint16_t queue_depth;
+ } c_bound_session;
struct msg_destroy_session {
uint32_t sid;
} d_session;
@@ -250,42 +257,49 @@ enum iscsi_param {
ISCSI_PARAM_PING_TMO,
ISCSI_PARAM_RECV_TMO,
+
+ ISCSI_PARAM_IFACE_NAME,
+ ISCSI_PARAM_ISID,
+ ISCSI_PARAM_INITIATOR_NAME,
/* must always be last */
ISCSI_PARAM_MAX,
};
-#define ISCSI_MAX_RECV_DLENGTH (1 << ISCSI_PARAM_MAX_RECV_DLENGTH)
-#define ISCSI_MAX_XMIT_DLENGTH (1 << ISCSI_PARAM_MAX_XMIT_DLENGTH)
-#define ISCSI_HDRDGST_EN (1 << ISCSI_PARAM_HDRDGST_EN)
-#define ISCSI_DATADGST_EN (1 << ISCSI_PARAM_DATADGST_EN)
-#define ISCSI_INITIAL_R2T_EN (1 << ISCSI_PARAM_INITIAL_R2T_EN)
-#define ISCSI_MAX_R2T (1 << ISCSI_PARAM_MAX_R2T)
-#define ISCSI_IMM_DATA_EN (1 << ISCSI_PARAM_IMM_DATA_EN)
-#define ISCSI_FIRST_BURST (1 << ISCSI_PARAM_FIRST_BURST)
-#define ISCSI_MAX_BURST (1 << ISCSI_PARAM_MAX_BURST)
-#define ISCSI_PDU_INORDER_EN (1 << ISCSI_PARAM_PDU_INORDER_EN)
-#define ISCSI_DATASEQ_INORDER_EN (1 << ISCSI_PARAM_DATASEQ_INORDER_EN)
-#define ISCSI_ERL (1 << ISCSI_PARAM_ERL)
-#define ISCSI_IFMARKER_EN (1 << ISCSI_PARAM_IFMARKER_EN)
-#define ISCSI_OFMARKER_EN (1 << ISCSI_PARAM_OFMARKER_EN)
-#define ISCSI_EXP_STATSN (1 << ISCSI_PARAM_EXP_STATSN)
-#define ISCSI_TARGET_NAME (1 << ISCSI_PARAM_TARGET_NAME)
-#define ISCSI_TPGT (1 << ISCSI_PARAM_TPGT)
-#define ISCSI_PERSISTENT_ADDRESS (1 << ISCSI_PARAM_PERSISTENT_ADDRESS)
-#define ISCSI_PERSISTENT_PORT (1 << ISCSI_PARAM_PERSISTENT_PORT)
-#define ISCSI_SESS_RECOVERY_TMO (1 << ISCSI_PARAM_SESS_RECOVERY_TMO)
-#define ISCSI_CONN_PORT (1 << ISCSI_PARAM_CONN_PORT)
-#define ISCSI_CONN_ADDRESS (1 << ISCSI_PARAM_CONN_ADDRESS)
-#define ISCSI_USERNAME (1 << ISCSI_PARAM_USERNAME)
-#define ISCSI_USERNAME_IN (1 << ISCSI_PARAM_USERNAME_IN)
-#define ISCSI_PASSWORD (1 << ISCSI_PARAM_PASSWORD)
-#define ISCSI_PASSWORD_IN (1 << ISCSI_PARAM_PASSWORD_IN)
-#define ISCSI_FAST_ABORT (1 << ISCSI_PARAM_FAST_ABORT)
-#define ISCSI_ABORT_TMO (1 << ISCSI_PARAM_ABORT_TMO)
-#define ISCSI_LU_RESET_TMO (1 << ISCSI_PARAM_LU_RESET_TMO)
-#define ISCSI_HOST_RESET_TMO (1 << ISCSI_PARAM_HOST_RESET_TMO)
-#define ISCSI_PING_TMO (1 << ISCSI_PARAM_PING_TMO)
-#define ISCSI_RECV_TMO (1 << ISCSI_PARAM_RECV_TMO)
+#define ISCSI_MAX_RECV_DLENGTH (1ULL << ISCSI_PARAM_MAX_RECV_DLENGTH)
+#define ISCSI_MAX_XMIT_DLENGTH (1ULL << ISCSI_PARAM_MAX_XMIT_DLENGTH)
+#define ISCSI_HDRDGST_EN (1ULL << ISCSI_PARAM_HDRDGST_EN)
+#define ISCSI_DATADGST_EN (1ULL << ISCSI_PARAM_DATADGST_EN)
+#define ISCSI_INITIAL_R2T_EN (1ULL << ISCSI_PARAM_INITIAL_R2T_EN)
+#define ISCSI_MAX_R2T (1ULL << ISCSI_PARAM_MAX_R2T)
+#define ISCSI_IMM_DATA_EN (1ULL << ISCSI_PARAM_IMM_DATA_EN)
+#define ISCSI_FIRST_BURST (1ULL << ISCSI_PARAM_FIRST_BURST)
+#define ISCSI_MAX_BURST (1ULL << ISCSI_PARAM_MAX_BURST)
+#define ISCSI_PDU_INORDER_EN (1ULL << ISCSI_PARAM_PDU_INORDER_EN)
+#define ISCSI_DATASEQ_INORDER_EN (1ULL << ISCSI_PARAM_DATASEQ_INORDER_EN)
+#define ISCSI_ERL (1ULL << ISCSI_PARAM_ERL)
+#define ISCSI_IFMARKER_EN (1ULL << ISCSI_PARAM_IFMARKER_EN)
+#define ISCSI_OFMARKER_EN (1ULL << ISCSI_PARAM_OFMARKER_EN)
+#define ISCSI_EXP_STATSN (1ULL << ISCSI_PARAM_EXP_STATSN)
+#define ISCSI_TARGET_NAME (1ULL << ISCSI_PARAM_TARGET_NAME)
+#define ISCSI_TPGT (1ULL << ISCSI_PARAM_TPGT)
+#define ISCSI_PERSISTENT_ADDRESS (1ULL << ISCSI_PARAM_PERSISTENT_ADDRESS)
+#define ISCSI_PERSISTENT_PORT (1ULL << ISCSI_PARAM_PERSISTENT_PORT)
+#define ISCSI_SESS_RECOVERY_TMO (1ULL << ISCSI_PARAM_SESS_RECOVERY_TMO)
+#define ISCSI_CONN_PORT (1ULL << ISCSI_PARAM_CONN_PORT)
+#define ISCSI_CONN_ADDRESS (1ULL << ISCSI_PARAM_CONN_ADDRESS)
+#define ISCSI_USERNAME (1ULL << ISCSI_PARAM_USERNAME)
+#define ISCSI_USERNAME_IN (1ULL << ISCSI_PARAM_USERNAME_IN)
+#define ISCSI_PASSWORD (1ULL << ISCSI_PARAM_PASSWORD)
+#define ISCSI_PASSWORD_IN (1ULL << ISCSI_PARAM_PASSWORD_IN)
+#define ISCSI_FAST_ABORT (1ULL << ISCSI_PARAM_FAST_ABORT)
+#define ISCSI_ABORT_TMO (1ULL << ISCSI_PARAM_ABORT_TMO)
+#define ISCSI_LU_RESET_TMO (1ULL << ISCSI_PARAM_LU_RESET_TMO)
+#define ISCSI_HOST_RESET_TMO (1ULL << ISCSI_PARAM_HOST_RESET_TMO)
+#define ISCSI_PING_TMO (1ULL << ISCSI_PARAM_PING_TMO)
+#define ISCSI_RECV_TMO (1ULL << ISCSI_PARAM_RECV_TMO)
+#define ISCSI_IFACE_NAME (1ULL << ISCSI_PARAM_IFACE_NAME)
+#define ISCSI_ISID (1ULL << ISCSI_PARAM_ISID)
+#define ISCSI_INITIATOR_NAME (1ULL << ISCSI_PARAM_INITIATOR_NAME)
/* iSCSI HBA params */
enum iscsi_host_param {
@@ -296,20 +310,13 @@ enum iscsi_host_param {
ISCSI_HOST_PARAM_MAX,
};
-#define ISCSI_HOST_HWADDRESS (1 << ISCSI_HOST_PARAM_HWADDRESS)
-#define ISCSI_HOST_INITIATOR_NAME (1 << ISCSI_HOST_PARAM_INITIATOR_NAME)
-#define ISCSI_HOST_NETDEV_NAME (1 << ISCSI_HOST_PARAM_NETDEV_NAME)
-#define ISCSI_HOST_IPADDRESS (1 << ISCSI_HOST_PARAM_IPADDRESS)
+#define ISCSI_HOST_HWADDRESS (1ULL << ISCSI_HOST_PARAM_HWADDRESS)
+#define ISCSI_HOST_INITIATOR_NAME (1ULL << ISCSI_HOST_PARAM_INITIATOR_NAME)
+#define ISCSI_HOST_NETDEV_NAME (1ULL << ISCSI_HOST_PARAM_NETDEV_NAME)
+#define ISCSI_HOST_IPADDRESS (1ULL << ISCSI_HOST_PARAM_IPADDRESS)
#define iscsi_ptr(_handle) ((void*)(unsigned long)_handle)
#define iscsi_handle(_ptr) ((uint64_t)(unsigned long)_ptr)
-#define hostdata_session(_hostdata) (iscsi_ptr(*(unsigned long *)_hostdata))
-
-/**
- * iscsi_hostdata - get LLD hostdata from scsi_host
- * @_hostdata: pointer to scsi host's hostdata
- **/
-#define iscsi_hostdata(_hostdata) ((void*)_hostdata + sizeof(unsigned long))
/*
* These flags presents iSCSI Data-Path capabilities.
diff --git a/include/scsi/iscsi_proto.h b/include/scsi/iscsi_proto.h
index e0593bfae622..f2a2c1169486 100644
--- a/include/scsi/iscsi_proto.h
+++ b/include/scsi/iscsi_proto.h
@@ -22,6 +22,7 @@
#define ISCSI_PROTO_H
#include <linux/types.h>
+#include <scsi/scsi.h>
#define ISCSI_DRAFT20_VERSION 0x00
@@ -156,7 +157,7 @@ struct iscsi_ecdb_ahdr {
uint8_t ahstype;
uint8_t reserved;
/* 4-byte aligned extended CDB spillover */
- uint8_t ecdb[260 - ISCSI_CDB_SIZE];
+ uint8_t ecdb[SCSI_MAX_VARLEN_CDB_SIZE - ISCSI_CDB_SIZE];
};
/* SCSI Response Header */
diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h
index cd3ca63d4fb1..5e75bb7f311c 100644
--- a/include/scsi/libiscsi.h
+++ b/include/scsi/libiscsi.h
@@ -24,6 +24,7 @@
#define LIBISCSI_H
#include <linux/types.h>
+#include <linux/wait.h>
#include <linux/mutex.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
@@ -31,6 +32,7 @@
#include <scsi/iscsi_if.h>
struct scsi_transport_template;
+struct scsi_host_template;
struct scsi_device;
struct Scsi_Host;
struct scsi_cmnd;
@@ -40,6 +42,7 @@ struct iscsi_cls_session;
struct iscsi_cls_conn;
struct iscsi_session;
struct iscsi_nopin;
+struct device;
/* #define DEBUG_SCSI */
#ifdef DEBUG_SCSI
@@ -49,9 +52,7 @@ struct iscsi_nopin;
#endif
#define ISCSI_DEF_XMIT_CMDS_MAX 128 /* must be power of 2 */
-#define ISCSI_MGMT_CMDS_MAX 16 /* must be power of 2 */
-
-#define ISCSI_MGMT_ITT_OFFSET 0xa00
+#define ISCSI_MGMT_CMDS_MAX 15
#define ISCSI_DEF_CMD_PER_LUN 32
#define ISCSI_MAX_CMD_PER_LUN 128
@@ -69,7 +70,10 @@ enum {
/* Connection suspend "bit" */
#define ISCSI_SUSPEND_BIT 1
-#define ISCSI_ITT_MASK (0xfff)
+#define ISCSI_ITT_MASK (0x1fff)
+#define ISCSI_TOTAL_CMDS_MAX 4096
+/* this must be a power of two greater than ISCSI_MGMT_CMDS_MAX */
+#define ISCSI_TOTAL_CMDS_MIN 16
#define ISCSI_AGE_SHIFT 28
#define ISCSI_AGE_MASK (0xf << ISCSI_AGE_SHIFT)
@@ -82,18 +86,6 @@ enum {
ISCSI_DIGEST_SIZE = sizeof(__u32),
};
-struct iscsi_mgmt_task {
- /*
- * Becuae LLDs allocate their hdr differently, this is a pointer to
- * that storage. It must be setup at session creation time.
- */
- struct iscsi_hdr *hdr;
- char *data; /* mgmt payload */
- unsigned data_count; /* counts data to be sent */
- uint32_t itt; /* this ITT */
- void *dd_data; /* driver/transport data */
- struct list_head running;
-};
enum {
ISCSI_TASK_COMPLETED,
@@ -101,7 +93,7 @@ enum {
ISCSI_TASK_RUNNING,
};
-struct iscsi_cmd_task {
+struct iscsi_task {
/*
* Because LLDs allocate their hdr differently, this is a pointer
* and length to that storage. It must be setup at session
@@ -118,6 +110,7 @@ struct iscsi_cmd_task {
/* offset in unsolicited stream (bytes); */
unsigned unsol_offset;
unsigned data_count; /* remaining Data-Out */
+ char *data; /* mgmt payload */
struct scsi_cmnd *sc; /* associated SCSI cmd*/
struct iscsi_conn *conn; /* used connection */
@@ -128,9 +121,9 @@ struct iscsi_cmd_task {
void *dd_data; /* driver/transport data */
};
-static inline void* iscsi_next_hdr(struct iscsi_cmd_task *ctask)
+static inline void* iscsi_next_hdr(struct iscsi_task *task)
{
- return (void*)ctask->hdr + ctask->hdr_len;
+ return (void*)task->hdr + task->hdr_len;
}
/* Connection's states */
@@ -146,11 +139,6 @@ struct iscsi_conn {
void *dd_data; /* iscsi_transport data */
struct iscsi_session *session; /* parent session */
/*
- * LLDs should set this lock. It protects the transport recv
- * code
- */
- rwlock_t *recv_lock;
- /*
* conn_stop() flag: stop to recover, stop to terminate
*/
int stop_stage;
@@ -159,7 +147,7 @@ struct iscsi_conn {
unsigned long last_ping;
int ping_timeout;
int recv_timeout;
- struct iscsi_mgmt_task *ping_mtask;
+ struct iscsi_task *ping_task;
/* iSCSI connection-wide sequencing */
uint32_t exp_statsn;
@@ -175,9 +163,8 @@ struct iscsi_conn {
* should always fit in this buffer
*/
char *data;
- struct iscsi_mgmt_task *login_mtask; /* mtask used for login/text */
- struct iscsi_mgmt_task *mtask; /* xmit mtask in progress */
- struct iscsi_cmd_task *ctask; /* xmit ctask in progress */
+ struct iscsi_task *login_task; /* mtask used for login/text */
+ struct iscsi_task *task; /* xmit task in progress */
/* xmit */
struct list_head mgmtqueue; /* mgmt (control) xmit queue */
@@ -208,9 +195,6 @@ struct iscsi_conn {
/* remote portal currently connected to */
int portal_port;
char portal_address[ISCSI_ADDRESS_BUF_LEN];
- /* local address */
- int local_port;
- char local_address[ISCSI_ADDRESS_BUF_LEN];
/* MIB-statistics */
uint64_t txdata_octets;
@@ -246,6 +230,7 @@ enum {
};
struct iscsi_session {
+ struct iscsi_cls_session *cls_session;
/*
* Syncs up the scsi eh thread with the iscsi eh thread when sending
* task management functions. This must be taken before the session
@@ -281,10 +266,8 @@ struct iscsi_session {
char *password;
char *password_in;
char *targetname;
+ char *ifacename;
char *initiatorname;
- /* hw address or netdev iscsi connection is bound to */
- char *hwaddress;
- char *netdev;
/* control data */
struct iscsi_transport *tt;
struct Scsi_Host *host;
@@ -298,12 +281,20 @@ struct iscsi_session {
int state; /* session state */
int age; /* counts session re-opens */
+ int scsi_cmds_max; /* max scsi commands */
int cmds_max; /* size of cmds array */
- struct iscsi_cmd_task **cmds; /* Original Cmds arr */
+ struct iscsi_task **cmds; /* Original Cmds arr */
struct iscsi_pool cmdpool; /* PDU's pool */
- int mgmtpool_max; /* size of mgmt array */
- struct iscsi_mgmt_task **mgmt_cmds; /* Original mgmt arr */
- struct iscsi_pool mgmtpool; /* Mgmt PDU's pool */
+};
+
+struct iscsi_host {
+ char *initiatorname;
+ /* hw address or netdev iscsi connection is bound to */
+ char *hwaddress;
+ char *netdev;
+ /* local address */
+ int local_port;
+ char local_address[ISCSI_ADDRESS_BUF_LEN];
};
/*
@@ -316,42 +307,44 @@ extern int iscsi_eh_device_reset(struct scsi_cmnd *sc);
extern int iscsi_queuecommand(struct scsi_cmnd *sc,
void (*done)(struct scsi_cmnd *));
-
/*
* iSCSI host helpers.
*/
+#define iscsi_host_priv(_shost) \
+ (shost_priv(_shost) + sizeof(struct iscsi_host))
+
extern int iscsi_host_set_param(struct Scsi_Host *shost,
enum iscsi_host_param param, char *buf,
int buflen);
extern int iscsi_host_get_param(struct Scsi_Host *shost,
enum iscsi_host_param param, char *buf);
+extern int iscsi_host_add(struct Scsi_Host *shost, struct device *pdev);
+extern struct Scsi_Host *iscsi_host_alloc(struct scsi_host_template *sht,
+ int dd_data_size, uint16_t qdepth);
+extern void iscsi_host_remove(struct Scsi_Host *shost);
+extern void iscsi_host_free(struct Scsi_Host *shost);
/*
* session management
*/
extern struct iscsi_cls_session *
-iscsi_session_setup(struct iscsi_transport *, struct scsi_transport_template *,
- uint16_t, uint16_t, int, int, uint32_t, uint32_t *);
+iscsi_session_setup(struct iscsi_transport *, struct Scsi_Host *shost,
+ uint16_t, int, uint32_t, unsigned int);
extern void iscsi_session_teardown(struct iscsi_cls_session *);
-extern struct iscsi_session *class_to_transport_session(struct iscsi_cls_session *);
extern void iscsi_session_recovery_timedout(struct iscsi_cls_session *);
extern int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf, int buflen);
extern int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
enum iscsi_param param, char *buf);
-#define session_to_cls(_sess) \
- hostdata_session(_sess->host->hostdata)
-
#define iscsi_session_printk(prefix, _sess, fmt, a...) \
- iscsi_cls_session_printk(prefix, \
- (struct iscsi_cls_session *)session_to_cls(_sess), fmt, ##a)
+ iscsi_cls_session_printk(prefix, _sess->cls_session, fmt, ##a)
/*
* connection management
*/
extern struct iscsi_cls_conn *iscsi_conn_setup(struct iscsi_cls_session *,
- uint32_t);
+ int, uint32_t);
extern void iscsi_conn_teardown(struct iscsi_cls_conn *);
extern int iscsi_conn_start(struct iscsi_cls_conn *);
extern void iscsi_conn_stop(struct iscsi_cls_conn *, int);
@@ -360,25 +353,29 @@ extern int iscsi_conn_bind(struct iscsi_cls_session *, struct iscsi_cls_conn *,
extern void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err);
extern int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf);
+extern void iscsi_suspend_tx(struct iscsi_conn *conn);
#define iscsi_conn_printk(prefix, _c, fmt, a...) \
- iscsi_cls_conn_printk(prefix, _c->cls_conn, fmt, ##a)
+ iscsi_cls_conn_printk(prefix, ((struct iscsi_conn *)_c)->cls_conn, \
+ fmt, ##a)
/*
* pdu and task processing
*/
extern void iscsi_update_cmdsn(struct iscsi_session *, struct iscsi_nopin *);
-extern void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *,
+extern void iscsi_prep_unsolicit_data_pdu(struct iscsi_task *,
struct iscsi_data *hdr);
extern int iscsi_conn_send_pdu(struct iscsi_cls_conn *, struct iscsi_hdr *,
char *, uint32_t);
extern int iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *,
char *, int);
-extern int iscsi_verify_itt(struct iscsi_conn *, struct iscsi_hdr *,
- uint32_t *);
-extern void iscsi_requeue_ctask(struct iscsi_cmd_task *ctask);
-extern void iscsi_free_mgmt_task(struct iscsi_conn *conn,
- struct iscsi_mgmt_task *mtask);
+extern int __iscsi_complete_pdu(struct iscsi_conn *, struct iscsi_hdr *,
+ char *, int);
+extern int iscsi_verify_itt(struct iscsi_conn *, itt_t);
+extern struct iscsi_task *iscsi_itt_to_ctask(struct iscsi_conn *, itt_t);
+extern void iscsi_requeue_task(struct iscsi_task *task);
+extern void iscsi_put_task(struct iscsi_task *task);
+extern void __iscsi_get_task(struct iscsi_task *task);
/*
* generic helpers
diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h
index 32742c4563de..2b5b9356c314 100644
--- a/include/scsi/scsi.h
+++ b/include/scsi/scsi.h
@@ -400,6 +400,7 @@ struct scsi_lun {
#define SOFT_ERROR 0x2005
#define ADD_TO_MLQUEUE 0x2006
#define TIMEOUT_ERROR 0x2007
+#define SCSI_RETURN_NOT_HANDLED 0x2008
/*
* Midlevel queue return values.
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index f6a9fe0ef09c..6467f78b191f 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -134,6 +134,7 @@ struct scsi_device {
unsigned no_start_on_add:1; /* do not issue start on add */
unsigned allow_restart:1; /* issue START_UNIT in error handler */
unsigned manage_start_stop:1; /* Let HLD (sd) manage start/stop */
+ unsigned start_stop_pwr_cond:1; /* Set power cond. in START_STOP_UNIT */
unsigned no_uld_attach:1; /* disable connecting to upper level drivers */
unsigned select_no_atn:1;
unsigned fix_capacity:1; /* READ_CAPACITY is too high by 1 */
@@ -161,9 +162,29 @@ struct scsi_device {
struct execute_work ew; /* used to get process context on put */
+ struct scsi_dh_data *scsi_dh_data;
enum scsi_device_state sdev_state;
unsigned long sdev_data[0];
} __attribute__((aligned(sizeof(unsigned long))));
+
+struct scsi_device_handler {
+ /* Used by the infrastructure */
+ struct list_head list; /* list of scsi_device_handlers */
+ struct notifier_block nb;
+
+ /* Filled by the hardware handler */
+ struct module *module;
+ const char *name;
+ int (*check_sense)(struct scsi_device *, struct scsi_sense_hdr *);
+ int (*activate)(struct scsi_device *);
+ int (*prep_fn)(struct scsi_device *, struct request *);
+};
+
+struct scsi_dh_data {
+ struct scsi_device_handler *scsi_dh;
+ char buf[0];
+};
+
#define to_scsi_device(d) \
container_of(d, struct scsi_device, sdev_gendev)
#define class_to_sdev(d) \
@@ -230,7 +251,9 @@ extern struct scsi_device *__scsi_add_device(struct Scsi_Host *,
uint, uint, uint, void *hostdata);
extern int scsi_add_device(struct Scsi_Host *host, uint channel,
uint target, uint lun);
+extern int scsi_register_device_handler(struct scsi_device_handler *scsi_dh);
extern void scsi_remove_device(struct scsi_device *);
+extern int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh);
extern int scsi_device_get(struct scsi_device *);
extern void scsi_device_put(struct scsi_device *);
diff --git a/include/scsi/scsi_dh.h b/include/scsi/scsi_dh.h
new file mode 100644
index 000000000000..04d0d8495c83
--- /dev/null
+++ b/include/scsi/scsi_dh.h
@@ -0,0 +1,59 @@
+/*
+ * Header file for SCSI device handler infrastruture.
+ *
+ * Modified version of patches posted by Mike Christie <michaelc@cs.wisc.edu>
+ *
+ * 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.
+ *
+ * Copyright IBM Corporation, 2007
+ * Authors:
+ * Chandra Seetharaman <sekharan@us.ibm.com>
+ * Mike Anderson <andmike@linux.vnet.ibm.com>
+ */
+
+#include <scsi/scsi_device.h>
+
+enum {
+ SCSI_DH_OK = 0,
+ /*
+ * device errors
+ */
+ SCSI_DH_DEV_FAILED, /* generic device error */
+ SCSI_DH_DEV_TEMP_BUSY,
+ SCSI_DH_DEVICE_MAX, /* max device blkerr definition */
+
+ /*
+ * transport errors
+ */
+ SCSI_DH_NOTCONN = SCSI_DH_DEVICE_MAX + 1,
+ SCSI_DH_CONN_FAILURE,
+ SCSI_DH_TRANSPORT_MAX, /* max transport blkerr definition */
+
+ /*
+ * driver and generic errors
+ */
+ SCSI_DH_IO = SCSI_DH_TRANSPORT_MAX + 1, /* generic error */
+ SCSI_DH_INVALID_IO,
+ SCSI_DH_RETRY, /* retry the req, but not immediately */
+ SCSI_DH_IMM_RETRY, /* immediately retry the req */
+ SCSI_DH_TIMED_OUT,
+ SCSI_DH_RES_TEMP_UNAVAIL,
+ SCSI_DH_DEV_OFFLINED,
+ SCSI_DH_NOSYS,
+ SCSI_DH_DRIVER_MAX,
+};
+
+extern int scsi_dh_activate(struct request_queue *);
+extern int scsi_dh_handler_exist(const char *);
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
index 1834fdfe82a7..a594bac4a77d 100644
--- a/include/scsi/scsi_host.h
+++ b/include/scsi/scsi_host.h
@@ -623,7 +623,7 @@ struct Scsi_Host {
/*
* Optional work queue to be utilized by the transport
*/
- char work_q_name[KOBJ_NAME_LEN];
+ char work_q_name[20];
struct workqueue_struct *work_q;
/*
diff --git a/include/scsi/scsi_transport_fc.h b/include/scsi/scsi_transport_fc.h
index 06f72bab9df0..878373c32ef7 100644
--- a/include/scsi/scsi_transport_fc.h
+++ b/include/scsi/scsi_transport_fc.h
@@ -489,9 +489,9 @@ struct fc_host_attrs {
u16 npiv_vports_inuse;
/* work queues for rport state manipulation */
- char work_q_name[KOBJ_NAME_LEN];
+ char work_q_name[20];
struct workqueue_struct *work_q;
- char devloss_work_q_name[KOBJ_NAME_LEN];
+ char devloss_work_q_name[20];
struct workqueue_struct *devloss_work_q;
};
diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h
index aab1eae2ec4c..8b6c91df4c7a 100644
--- a/include/scsi/scsi_transport_iscsi.h
+++ b/include/scsi/scsi_transport_iscsi.h
@@ -30,11 +30,11 @@
struct scsi_transport_template;
struct iscsi_transport;
+struct iscsi_endpoint;
struct Scsi_Host;
struct iscsi_cls_conn;
struct iscsi_conn;
-struct iscsi_cmd_task;
-struct iscsi_mgmt_task;
+struct iscsi_task;
struct sockaddr;
/**
@@ -58,19 +58,22 @@ struct sockaddr;
* @stop_conn: suspend/recover/terminate connection
* @send_pdu: send iSCSI PDU, Login, Logout, NOP-Out, Reject, Text.
* @session_recovery_timedout: notify LLD a block during recovery timed out
- * @init_cmd_task: Initialize a iscsi_cmd_task and any internal structs.
- * Called from queuecommand with session lock held.
- * @init_mgmt_task: Initialize a iscsi_mgmt_task and any internal structs.
- * Called from iscsi_conn_send_generic with xmitmutex.
- * @xmit_cmd_task: Requests LLD to transfer cmd task. Returns 0 or the
+ * @init_task: Initialize a iscsi_task and any internal structs.
+ * When offloading the data path, this is called from
+ * queuecommand with the session lock, or from the
+ * iscsi_conn_send_pdu context with the session lock.
+ * When not offloading the data path, this is called
+ * from the scsi work queue without the session lock.
+ * @xmit_task Requests LLD to transfer cmd task. Returns 0 or the
* the number of bytes transferred on success, and -Exyz
- * value on error.
- * @xmit_mgmt_task: Requests LLD to transfer mgmt task. Returns 0 or the
- * the number of bytes transferred on success, and -Exyz
- * value on error.
- * @cleanup_cmd_task: requests LLD to fail cmd task. Called with xmitmutex
- * and session->lock after the connection has been
- * suspended and terminated during recovery. If called
+ * value on error. When offloading the data path, this
+ * is called from queuecommand with the session lock, or
+ * from the iscsi_conn_send_pdu context with the session
+ * lock. When not offloading the data path, this is called
+ * from the scsi work queue without the session lock.
+ * @cleanup_task: requests LLD to fail task. Called with session lock
+ * and after the connection has been suspended and
+ * terminated during recovery. If called
* from abort task then connection is not suspended
* or terminated but sk_callback_lock is held
*
@@ -83,17 +86,9 @@ struct iscsi_transport {
/* LLD sets this to indicate what values it can export to sysfs */
uint64_t param_mask;
uint64_t host_param_mask;
- struct scsi_host_template *host_template;
- /* LLD connection data size */
- int conndata_size;
- /* LLD session data size */
- int sessiondata_size;
- int max_lun;
- unsigned int max_conn;
- unsigned int max_cmd_len;
- struct iscsi_cls_session *(*create_session) (struct iscsi_transport *it,
- struct scsi_transport_template *t, uint16_t, uint16_t,
- uint32_t sn, uint32_t *hn);
+ struct iscsi_cls_session *(*create_session) (struct iscsi_endpoint *ep,
+ uint16_t cmds_max, uint16_t qdepth,
+ uint32_t sn, uint32_t *hn);
void (*destroy_session) (struct iscsi_cls_session *session);
struct iscsi_cls_conn *(*create_conn) (struct iscsi_cls_session *sess,
uint32_t cid);
@@ -118,20 +113,15 @@ struct iscsi_transport {
char *data, uint32_t data_size);
void (*get_stats) (struct iscsi_cls_conn *conn,
struct iscsi_stats *stats);
- int (*init_cmd_task) (struct iscsi_cmd_task *ctask);
- void (*init_mgmt_task) (struct iscsi_conn *conn,
- struct iscsi_mgmt_task *mtask);
- int (*xmit_cmd_task) (struct iscsi_conn *conn,
- struct iscsi_cmd_task *ctask);
- void (*cleanup_cmd_task) (struct iscsi_conn *conn,
- struct iscsi_cmd_task *ctask);
- int (*xmit_mgmt_task) (struct iscsi_conn *conn,
- struct iscsi_mgmt_task *mtask);
+ int (*init_task) (struct iscsi_task *task);
+ int (*xmit_task) (struct iscsi_task *task);
+ void (*cleanup_task) (struct iscsi_conn *conn,
+ struct iscsi_task *task);
void (*session_recovery_timedout) (struct iscsi_cls_session *session);
- int (*ep_connect) (struct sockaddr *dst_addr, int non_blocking,
- uint64_t *ep_handle);
- int (*ep_poll) (uint64_t ep_handle, int timeout_ms);
- void (*ep_disconnect) (uint64_t ep_handle);
+ struct iscsi_endpoint *(*ep_connect) (struct sockaddr *dst_addr,
+ int non_blocking);
+ int (*ep_poll) (struct iscsi_endpoint *ep, int timeout_ms);
+ void (*ep_disconnect) (struct iscsi_endpoint *ep);
int (*tgt_dscvr) (struct Scsi_Host *shost, enum iscsi_tgt_dscvr type,
uint32_t enable, struct sockaddr *dst_addr);
};
@@ -172,9 +162,10 @@ enum {
ISCSI_SESSION_FREE,
};
+#define ISCSI_MAX_TARGET -1
+
struct iscsi_cls_session {
struct list_head sess_list; /* item in session_list */
- struct list_head host_list;
struct iscsi_transport *transport;
spinlock_t lock;
struct work_struct block_work;
@@ -186,7 +177,7 @@ struct iscsi_cls_session {
int recovery_tmo;
struct delayed_work recovery_work;
- int target_id;
+ unsigned int target_id;
int state;
int sid; /* session id */
@@ -203,12 +194,20 @@ struct iscsi_cls_session {
#define starget_to_session(_stgt) \
iscsi_dev_to_session(_stgt->dev.parent)
-struct iscsi_host {
- struct list_head sessions;
+struct iscsi_cls_host {
atomic_t nr_scans;
struct mutex mutex;
struct workqueue_struct *scan_workq;
- char scan_workq_name[KOBJ_NAME_LEN];
+ char scan_workq_name[20];
+};
+
+extern void iscsi_host_for_each_session(struct Scsi_Host *shost,
+ void (*fn)(struct iscsi_cls_session *));
+
+struct iscsi_endpoint {
+ void *dd_data; /* LLD private data */
+ struct device dev;
+ unsigned int id;
};
/*
@@ -222,22 +221,26 @@ struct iscsi_host {
extern int iscsi_session_chkready(struct iscsi_cls_session *session);
extern struct iscsi_cls_session *iscsi_alloc_session(struct Scsi_Host *shost,
- struct iscsi_transport *transport);
+ struct iscsi_transport *transport, int dd_size);
extern int iscsi_add_session(struct iscsi_cls_session *session,
unsigned int target_id);
extern int iscsi_session_event(struct iscsi_cls_session *session,
enum iscsi_uevent_e event);
extern struct iscsi_cls_session *iscsi_create_session(struct Scsi_Host *shost,
struct iscsi_transport *t,
+ int dd_size,
unsigned int target_id);
extern void iscsi_remove_session(struct iscsi_cls_session *session);
extern void iscsi_free_session(struct iscsi_cls_session *session);
extern int iscsi_destroy_session(struct iscsi_cls_session *session);
extern struct iscsi_cls_conn *iscsi_create_conn(struct iscsi_cls_session *sess,
- uint32_t cid);
+ int dd_size, uint32_t cid);
extern int iscsi_destroy_conn(struct iscsi_cls_conn *conn);
extern void iscsi_unblock_session(struct iscsi_cls_session *session);
extern void iscsi_block_session(struct iscsi_cls_session *session);
extern int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time);
+extern struct iscsi_endpoint *iscsi_create_endpoint(int dd_size);
+extern void iscsi_destroy_endpoint(struct iscsi_endpoint *ep);
+extern struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle);
#endif
diff --git a/include/xen/events.h b/include/xen/events.h
index acd8e062c85f..67c4436554a9 100644
--- a/include/xen/events.h
+++ b/include/xen/events.h
@@ -32,6 +32,7 @@ void unbind_from_irqhandler(unsigned int irq, void *dev_id);
void xen_send_IPI_one(unsigned int cpu, enum ipi_vector vector);
int resend_irq_on_evtchn(unsigned int irq);
+void rebind_evtchn_irq(int evtchn, int irq);
static inline void notify_remote_via_evtchn(int port)
{
@@ -40,4 +41,7 @@ static inline void notify_remote_via_evtchn(int port)
}
extern void notify_remote_via_irq(int irq);
+
+extern void xen_irq_resume(void);
+
#endif /* _XEN_EVENTS_H */
diff --git a/include/xen/grant_table.h b/include/xen/grant_table.h
index 466204846121..a40f1cd91be1 100644
--- a/include/xen/grant_table.h
+++ b/include/xen/grant_table.h
@@ -51,6 +51,9 @@ struct gnttab_free_callback {
u16 count;
};
+int gnttab_suspend(void);
+int gnttab_resume(void);
+
int gnttab_grant_foreign_access(domid_t domid, unsigned long frame,
int readonly);
diff --git a/include/xen/hvc-console.h b/include/xen/hvc-console.h
index 21c0ecfd786d..98b79bc404dd 100644
--- a/include/xen/hvc-console.h
+++ b/include/xen/hvc-console.h
@@ -3,4 +3,13 @@
extern struct console xenboot_console;
+#ifdef CONFIG_HVC_XEN
+void xen_console_resume(void);
+#else
+static inline void xen_console_resume(void) { }
+#endif
+
+void xen_raw_console_write(const char *str);
+void xen_raw_printk(const char *fmt, ...);
+
#endif /* XEN_HVC_CONSOLE_H */
diff --git a/include/xen/interface/elfnote.h b/include/xen/interface/elfnote.h
index a64d3df5bd95..7a8262c375cc 100644
--- a/include/xen/interface/elfnote.h
+++ b/include/xen/interface/elfnote.h
@@ -120,6 +120,26 @@
*/
#define XEN_ELFNOTE_BSD_SYMTAB 11
+/*
+ * The lowest address the hypervisor hole can begin at (numeric).
+ *
+ * This must not be set higher than HYPERVISOR_VIRT_START. Its presence
+ * also indicates to the hypervisor that the kernel can deal with the
+ * hole starting at a higher address.
+ */
+#define XEN_ELFNOTE_HV_START_LOW 12
+
+/*
+ * List of maddr_t-sized mask/value pairs describing how to recognize
+ * (non-present) L1 page table entries carrying valid MFNs (numeric).
+ */
+#define XEN_ELFNOTE_L1_MFN_VALID 13
+
+/*
+ * Whether or not the guest supports cooperative suspend cancellation.
+ */
+#define XEN_ELFNOTE_SUSPEND_CANCEL 14
+
#endif /* __XEN_PUBLIC_ELFNOTE_H__ */
/*
diff --git a/include/xen/interface/io/fbif.h b/include/xen/interface/io/fbif.h
index 5a934dd7796d..974a51ed9165 100644
--- a/include/xen/interface/io/fbif.h
+++ b/include/xen/interface/io/fbif.h
@@ -49,11 +49,27 @@ struct xenfb_update {
int32_t height; /* rect height */
};
+/*
+ * Framebuffer resize notification event
+ * Capable backend sets feature-resize in xenstore.
+ */
+#define XENFB_TYPE_RESIZE 3
+
+struct xenfb_resize {
+ uint8_t type; /* XENFB_TYPE_RESIZE */
+ int32_t width; /* width in pixels */
+ int32_t height; /* height in pixels */
+ int32_t stride; /* stride in bytes */
+ int32_t depth; /* depth in bits */
+ int32_t offset; /* start offset within framebuffer */
+};
+
#define XENFB_OUT_EVENT_SIZE 40
union xenfb_out_event {
uint8_t type;
struct xenfb_update update;
+ struct xenfb_resize resize;
char pad[XENFB_OUT_EVENT_SIZE];
};
@@ -105,15 +121,18 @@ struct xenfb_page {
* Each directory page holds PAGE_SIZE / sizeof(*pd)
* framebuffer pages, and can thus map up to PAGE_SIZE *
* PAGE_SIZE / sizeof(*pd) bytes. With PAGE_SIZE == 4096 and
- * sizeof(unsigned long) == 4, that's 4 Megs. Two directory
- * pages should be enough for a while.
+ * sizeof(unsigned long) == 4/8, that's 4 Megs 32 bit and 2
+ * Megs 64 bit. 256 directories give enough room for a 512
+ * Meg framebuffer with a max resolution of 12,800x10,240.
+ * Should be enough for a while with room leftover for
+ * expansion.
*/
- unsigned long pd[2];
+ unsigned long pd[256];
};
/*
- * Wart: xenkbd needs to know resolution. Put it here until a better
- * solution is found, but don't leak it to the backend.
+ * Wart: xenkbd needs to know default resolution. Put it here until a
+ * better solution is found, but don't leak it to the backend.
*/
#ifdef __KERNEL__
#define XENFB_WIDTH 800
diff --git a/include/xen/interface/io/kbdif.h b/include/xen/interface/io/kbdif.h
index fb97f4284ffd..8066c7849fbe 100644
--- a/include/xen/interface/io/kbdif.h
+++ b/include/xen/interface/io/kbdif.h
@@ -49,6 +49,7 @@ struct xenkbd_motion {
uint8_t type; /* XENKBD_TYPE_MOTION */
int32_t rel_x; /* relative X motion */
int32_t rel_y; /* relative Y motion */
+ int32_t rel_z; /* relative Z motion (wheel) */
};
struct xenkbd_key {
@@ -61,6 +62,7 @@ struct xenkbd_position {
uint8_t type; /* XENKBD_TYPE_POS */
int32_t abs_x; /* absolute X position (in FB pixels) */
int32_t abs_y; /* absolute Y position (in FB pixels) */
+ int32_t rel_z; /* relative Z motion (wheel) */
};
#define XENKBD_IN_EVENT_SIZE 40
diff --git a/include/xen/interface/memory.h b/include/xen/interface/memory.h
index da768469aa92..af36ead16817 100644
--- a/include/xen/interface/memory.h
+++ b/include/xen/interface/memory.h
@@ -29,7 +29,7 @@ struct xen_memory_reservation {
* OUT: GMFN bases of extents that were allocated
* (NB. This command also updates the mach_to_phys translation table)
*/
- ulong extent_start;
+ GUEST_HANDLE(ulong) extent_start;
/* Number of extents, and size/alignment of each (2^extent_order pages). */
unsigned long nr_extents;
@@ -50,6 +50,7 @@ struct xen_memory_reservation {
domid_t domid;
};
+DEFINE_GUEST_HANDLE_STRUCT(xen_memory_reservation);
/*
* Returns the maximum machine frame number of mapped RAM in this system.
@@ -85,7 +86,7 @@ struct xen_machphys_mfn_list {
* any large discontiguities in the machine address space, 2MB gaps in
* the machphys table will be represented by an MFN base of zero.
*/
- ulong extent_start;
+ GUEST_HANDLE(ulong) extent_start;
/*
* Number of extents written to the above array. This will be smaller
@@ -93,6 +94,7 @@ struct xen_machphys_mfn_list {
*/
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
@@ -115,6 +117,7 @@ struct xen_add_to_physmap {
/* 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
@@ -129,13 +132,14 @@ struct xen_translate_gpfn_list {
unsigned long nr_gpfns;
/* List of GPFNs to translate. */
- ulong gpfn_list;
+ 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).
*/
- ulong mfn_list;
+ GUEST_HANDLE(ulong) mfn_list;
};
+DEFINE_GUEST_HANDLE_STRUCT(xen_translate_gpfn_list);
#endif /* __XEN_PUBLIC_MEMORY_H__ */
diff --git a/include/xen/xen-ops.h b/include/xen/xen-ops.h
index 10ddfe0142d0..a706d6a78960 100644
--- a/include/xen/xen-ops.h
+++ b/include/xen/xen-ops.h
@@ -5,4 +5,10 @@
DECLARE_PER_CPU(struct vcpu_info *, xen_vcpu);
+void xen_pre_suspend(void);
+void xen_post_suspend(int suspend_cancelled);
+
+void xen_mm_pin_all(void);
+void xen_mm_unpin_all(void);
+
#endif /* INCLUDE_XEN_OPS_H */
diff --git a/init/main.c b/init/main.c
index f7fb20021d48..51f6b5fe52d2 100644
--- a/init/main.c
+++ b/init/main.c
@@ -14,6 +14,7 @@
#include <linux/proc_fs.h>
#include <linux/kernel.h>
#include <linux/syscalls.h>
+#include <linux/stackprotector.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/delay.h>
@@ -545,6 +546,12 @@ asmlinkage void __init start_kernel(void)
unwind_init();
lockdep_init();
debug_objects_early_init();
+
+ /*
+ * Set up the the initial canary ASAP:
+ */
+ boot_init_stack_canary();
+
cgroup_init_early();
local_irq_disable();
@@ -758,6 +765,7 @@ static void __init do_initcalls(void)
*/
static void __init do_basic_setup(void)
{
+ rcu_init_sched(); /* needed by module_init stage. */
/* drivers will send hotplug events */
init_workqueues();
usermodehelper_init();
@@ -791,12 +799,21 @@ static void run_init_process(char *init_filename)
kernel_execve(init_filename, argv_init, envp_init);
}
+/*
+ * __init/__init_data sections are turned into normal
+ * dynamically allocated memory later in boot. When
+ * this is 0, the memory is for the __init purposes,
+ * when it it some other value, the memory is dynamic.
+ */
+int initmem_now_dynamic;
+
/* This is a non __init function. Force it to be noinline otherwise gcc
* makes it inline to init() and it becomes part of init.text section
*/
static int noinline init_post(void)
{
free_initmem();
+ initmem_now_dynamic = 1;
unlock_kernel();
mark_rodata_ro();
system_state = SYSTEM_RUNNING;
diff --git a/ipc/shm.c b/ipc/shm.c
index 554429ade079..d05f6b564998 100644
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -894,8 +894,6 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr)
if (!sfd)
goto out_put_dentry;
- err = -ENOMEM;
-
file = alloc_file(path.mnt, path.dentry, f_mode, &shm_file_operations);
if (!file)
goto out_free;
diff --git a/kernel/Makefile b/kernel/Makefile
index 1c9938addb9d..ed052b804525 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -3,7 +3,7 @@
#
obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \
- exit.o itimer.o time.o softirq.o resource.o \
+ cpu.o exit.o itimer.o time.o softirq.o resource.o \
sysctl.o capability.o ptrace.o timer.o user.o \
signal.o sys.o kmod.o workqueue.o pid.o \
rcupdate.o extable.o params.o posix-timers.o \
@@ -11,6 +11,18 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \
hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \
notifier.o ksysfs.o pm_qos_params.o sched_clock.o
+CFLAGS_REMOVE_sched.o = -pg -mno-spe
+
+ifdef CONFIG_FTRACE
+# Do not trace debug files and internal ftrace files
+CFLAGS_REMOVE_lockdep.o = -pg
+CFLAGS_REMOVE_lockdep_proc.o = -pg
+CFLAGS_REMOVE_mutex-debug.o = -pg
+CFLAGS_REMOVE_rtmutex-debug.o = -pg
+CFLAGS_REMOVE_cgroup-debug.o = -pg
+CFLAGS_REMOVE_sched_clock.o = -pg
+endif
+
obj-$(CONFIG_SYSCTL_SYSCALL_CHECK) += sysctl_check.o
obj-$(CONFIG_STACKTRACE) += stacktrace.o
obj-y += time/
@@ -25,9 +37,8 @@ obj-$(CONFIG_FUTEX) += futex_compat.o
endif
obj-$(CONFIG_RT_MUTEXES) += rtmutex.o
obj-$(CONFIG_DEBUG_RT_MUTEXES) += rtmutex-debug.o
-obj-$(CONFIG_RT_MUTEX_TESTER) += rtmutex-tester.o
obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
-obj-$(CONFIG_SMP) += cpu.o spinlock.o
+obj-$(CONFIG_SMP) += spinlock.o
obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o
obj-$(CONFIG_PROVE_LOCKING) += spinlock.o
obj-$(CONFIG_UID16) += uid16.o
@@ -36,7 +47,6 @@ obj-$(CONFIG_KALLSYMS) += kallsyms.o
obj-$(CONFIG_PM) += power/
obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
obj-$(CONFIG_KEXEC) += kexec.o
-obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
obj-$(CONFIG_COMPAT) += compat.o
obj-$(CONFIG_CGROUPS) += cgroup.o
obj-$(CONFIG_CGROUP_DEBUG) += cgroup_debug.o
@@ -48,7 +58,6 @@ obj-$(CONFIG_PID_NS) += pid_namespace.o
obj-$(CONFIG_IKCONFIG) += configs.o
obj-$(CONFIG_RESOURCE_COUNTERS) += res_counter.o
obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
-obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o
obj-$(CONFIG_AUDIT) += audit.o auditfilter.o
obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
obj-$(CONFIG_AUDIT_TREE) += audit_tree.o
@@ -57,7 +66,6 @@ obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_DETECT_SOFTLOCKUP) += softlockup.o
obj-$(CONFIG_GENERIC_HARDIRQS) += irq/
obj-$(CONFIG_SECCOMP) += seccomp.o
-obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o
obj-$(CONFIG_CLASSIC_RCU) += rcuclassic.o
obj-$(CONFIG_PREEMPT_RCU) += rcupreempt.o
ifeq ($(CONFIG_PREEMPT_RCU),y)
@@ -69,6 +77,9 @@ obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o
obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o
obj-$(CONFIG_MARKERS) += marker.o
obj-$(CONFIG_LATENCYTOP) += latencytop.o
+obj-$(CONFIG_FTRACE) += trace/
+obj-$(CONFIG_TRACING) += trace/
+obj-$(CONFIG_SMP) += sched_cpupri.o
ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y)
# According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
diff --git a/kernel/cpu.c b/kernel/cpu.c
index c77bc3a1c722..d26d0b095b3b 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -15,6 +15,28 @@
#include <linux/stop_machine.h>
#include <linux/mutex.h>
+/*
+ * Represents all cpu's present in the system
+ * In systems capable of hotplug, this map could dynamically grow
+ * as new cpu's are detected in the system via any platform specific
+ * method, such as ACPI for e.g.
+ */
+cpumask_t cpu_present_map __read_mostly;
+EXPORT_SYMBOL(cpu_present_map);
+
+#ifndef CONFIG_SMP
+
+/*
+ * Represents all cpu's that are currently online.
+ */
+cpumask_t cpu_online_map __read_mostly = CPU_MASK_ALL;
+EXPORT_SYMBOL(cpu_online_map);
+
+cpumask_t cpu_possible_map __read_mostly = CPU_MASK_ALL;
+EXPORT_SYMBOL(cpu_possible_map);
+
+#else /* CONFIG_SMP */
+
/* Serializes the updates to cpu_online_map, cpu_present_map */
static DEFINE_MUTEX(cpu_add_remove_lock);
@@ -277,6 +299,7 @@ int __ref cpu_down(unsigned int cpu)
cpu_maps_update_done();
return err;
}
+EXPORT_SYMBOL(cpu_down);
#endif /*CONFIG_HOTPLUG_CPU*/
/* Requires cpu_add_remove_lock to be held */
@@ -390,7 +413,7 @@ void __ref enable_nonboot_cpus(void)
goto out;
printk("Enabling non-boot CPUs ...\n");
- for_each_cpu_mask(cpu, frozen_cpus) {
+ for_each_cpu_mask_nr(cpu, frozen_cpus) {
error = _cpu_up(cpu, 1);
if (!error) {
printk("CPU%d is up\n", cpu);
@@ -403,3 +426,5 @@ out:
cpu_maps_update_done();
}
#endif /* CONFIG_PM_SLEEP_SMP */
+
+#endif /* CONFIG_SMP */
diff --git a/kernel/cpuset.c b/kernel/cpuset.c
index 039baa4cd90c..64a05da9bc4c 100644
--- a/kernel/cpuset.c
+++ b/kernel/cpuset.c
@@ -1037,8 +1037,8 @@ int current_cpuset_is_being_rebound(void)
static int update_relax_domain_level(struct cpuset *cs, s64 val)
{
- if ((int)val < 0)
- val = -1;
+ if (val < -1 || val >= SD_LV_MAX)
+ return -EINVAL;
if (val != cs->relax_domain_level) {
cs->relax_domain_level = val;
@@ -1194,6 +1194,15 @@ static int cpuset_can_attach(struct cgroup_subsys *ss,
if (cpus_empty(cs->cpus_allowed) || nodes_empty(cs->mems_allowed))
return -ENOSPC;
+ if (tsk->flags & PF_THREAD_BOUND) {
+ cpumask_t mask;
+
+ mutex_lock(&callback_mutex);
+ mask = cs->cpus_allowed;
+ mutex_unlock(&callback_mutex);
+ if (!cpus_equal(tsk->cpus_allowed, mask))
+ return -EINVAL;
+ }
return security_task_setscheduler(tsk, 0, NULL);
}
@@ -1207,11 +1216,14 @@ static void cpuset_attach(struct cgroup_subsys *ss,
struct mm_struct *mm;
struct cpuset *cs = cgroup_cs(cont);
struct cpuset *oldcs = cgroup_cs(oldcont);
+ int err;
mutex_lock(&callback_mutex);
guarantee_online_cpus(cs, &cpus);
- set_cpus_allowed_ptr(tsk, &cpus);
+ err = set_cpus_allowed_ptr(tsk, &cpus);
mutex_unlock(&callback_mutex);
+ if (err)
+ return;
from = oldcs->mems_allowed;
to = cs->mems_allowed;
@@ -1890,6 +1902,12 @@ static void common_cpu_mem_hotplug_unplug(void)
top_cpuset.mems_allowed = node_states[N_HIGH_MEMORY];
scan_for_empty_cpusets(&top_cpuset);
+ /*
+ * Scheduler destroys domains on hotplug events.
+ * Rebuild them based on the current settings.
+ */
+ rebuild_sched_domains();
+
cgroup_unlock();
}
diff --git a/kernel/exit.c b/kernel/exit.c
index 8f6185e69b69..fb8de6cbf2c7 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -899,12 +899,9 @@ static void check_stack_usage(void)
{
static DEFINE_SPINLOCK(low_water_lock);
static int lowest_to_date = THREAD_SIZE;
- unsigned long *n = end_of_stack(current);
unsigned long free;
- while (*n == 0)
- n++;
- free = (unsigned long)n - (unsigned long)end_of_stack(current);
+ free = stack_not_used(current);
if (free >= lowest_to_date)
return;
diff --git a/kernel/fork.c b/kernel/fork.c
index 19908b26cf80..b98b08aecee0 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -54,6 +54,7 @@
#include <linux/tty.h>
#include <linux/proc_fs.h>
#include <linux/blkdev.h>
+#include <linux/magic.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
@@ -186,6 +187,8 @@ static struct task_struct *dup_task_struct(struct task_struct *orig)
{
struct task_struct *tsk;
struct thread_info *ti;
+ unsigned long *stackend;
+
int err;
prepare_to_copy(orig);
@@ -211,6 +214,8 @@ static struct task_struct *dup_task_struct(struct task_struct *orig)
goto out;
setup_thread_stack(tsk, orig);
+ stackend = end_of_stack(tsk);
+ *stackend = STACK_END_MAGIC; /* for overflow detection */
#ifdef CONFIG_CC_STACKPROTECTOR
tsk->stack_canary = get_random_int();
@@ -909,7 +914,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
rt_mutex_init_task(p);
-#ifdef CONFIG_TRACE_IRQFLAGS
+#if defined(CONFIG_TRACE_IRQFLAGS) && defined(CONFIG_LOCKDEP)
DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled);
DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled);
#endif
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c
index 421be5fe5cc7..66c36056b6cb 100644
--- a/kernel/hrtimer.c
+++ b/kernel/hrtimer.c
@@ -300,11 +300,10 @@ EXPORT_SYMBOL_GPL(ktime_sub_ns);
*/
u64 ktime_divns(const ktime_t kt, s64 div)
{
- u64 dclc, inc, dns;
+ u64 dclc;
int sft = 0;
- dclc = dns = ktime_to_ns(kt);
- inc = div;
+ dclc = ktime_to_ns(kt);
/* Make sure the divisor is less than 2^32: */
while (div >> 32) {
sft++;
@@ -632,8 +631,6 @@ void clock_was_set(void)
*/
void hres_timers_resume(void)
{
- WARN_ON_ONCE(num_online_cpus() > 1);
-
/* Retrigger the CPU local events: */
retrigger_next_event(NULL);
}
@@ -1669,7 +1666,7 @@ void __init hrtimers_init(void)
(void *)(long)smp_processor_id());
register_cpu_notifier(&hrtimers_nb);
#ifdef CONFIG_HIGH_RES_TIMERS
- open_softirq(HRTIMER_SOFTIRQ, run_hrtimer_softirq, NULL);
+ open_softirq(HRTIMER_SOFTIRQ, run_hrtimer_softirq);
#endif
}
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 46d6611a33bb..469814e9b9ee 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -17,6 +17,8 @@
#ifdef CONFIG_SMP
+cpumask_t irq_default_affinity = CPU_MASK_ALL;
+
/**
* synchronize_irq - wait for pending IRQ handlers (on other CPUs)
* @irq: interrupt number to wait for
@@ -95,6 +97,27 @@ int irq_set_affinity(unsigned int irq, cpumask_t cpumask)
return 0;
}
+#ifndef CONFIG_AUTO_IRQ_AFFINITY
+/*
+ * Generic version of the affinity autoselector.
+ */
+int irq_select_affinity(unsigned int irq)
+{
+ cpumask_t mask;
+
+ if (!irq_can_set_affinity(irq))
+ return 0;
+
+ cpus_and(mask, cpu_online_map, irq_default_affinity);
+
+ irq_desc[irq].affinity = mask;
+ irq_desc[irq].chip->set_affinity(irq, mask);
+
+ set_balance_irq_affinity(irq, mask);
+ return 0;
+}
+#endif
+
#endif
/**
@@ -382,6 +405,9 @@ int setup_irq(unsigned int irq, struct irqaction *new)
} else
/* Undo nested disables: */
desc->depth = 1;
+
+ /* Set default affinity mask once everything is setup */
+ irq_select_affinity(irq);
}
/* Reset broken irq detection when installing new handler */
desc->irq_count = 0;
@@ -571,8 +597,6 @@ int request_irq(unsigned int irq, irq_handler_t handler,
action->next = NULL;
action->dev_id = dev_id;
- select_smp_affinity(irq);
-
#ifdef CONFIG_DEBUG_SHIRQ
if (irqflags & IRQF_SHARED) {
/*
diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c
index c2f2ccb0549a..6c6d35d68ee9 100644
--- a/kernel/irq/proc.c
+++ b/kernel/irq/proc.c
@@ -44,7 +44,7 @@ static int irq_affinity_write_proc(struct file *file, const char __user *buffer,
unsigned long count, void *data)
{
unsigned int irq = (int)(long)data, full_count = count, err;
- cpumask_t new_value, tmp;
+ cpumask_t new_value;
if (!irq_desc[irq].chip->set_affinity || no_irq_affinity ||
irq_balancing_disabled(irq))
@@ -62,17 +62,51 @@ static int irq_affinity_write_proc(struct file *file, const char __user *buffer,
* way to make the system unusable accidentally :-) At least
* one online CPU still has to be targeted.
*/
- cpus_and(tmp, new_value, cpu_online_map);
- if (cpus_empty(tmp))
+ if (!cpus_intersects(new_value, cpu_online_map))
/* Special case for empty set - allow the architecture
code to set default SMP affinity. */
- return select_smp_affinity(irq) ? -EINVAL : full_count;
+ return irq_select_affinity(irq) ? -EINVAL : full_count;
irq_set_affinity(irq, new_value);
return full_count;
}
+static int default_affinity_read(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = cpumask_scnprintf(page, count, irq_default_affinity);
+ if (count - len < 2)
+ return -EINVAL;
+ len += sprintf(page + len, "\n");
+ return len;
+}
+
+static int default_affinity_write(struct file *file, const char __user *buffer,
+ unsigned long count, void *data)
+{
+ unsigned int full_count = count, err;
+ cpumask_t new_value;
+
+ err = cpumask_parse_user(buffer, count, new_value);
+ if (err)
+ return err;
+
+ if (!is_affinity_mask_valid(new_value))
+ return -EINVAL;
+
+ /*
+ * Do not allow disabling IRQs completely - it's a too easy
+ * way to make the system unusable accidentally :-) At least
+ * one online CPU still has to be targeted.
+ */
+ if (!cpus_intersects(new_value, cpu_online_map))
+ return -EINVAL;
+
+ irq_default_affinity = new_value;
+
+ return full_count;
+}
#endif
static int irq_spurious_read(char *page, char **start, off_t off,
@@ -171,6 +205,21 @@ void unregister_handler_proc(unsigned int irq, struct irqaction *action)
remove_proc_entry(action->dir->name, irq_desc[irq].dir);
}
+void register_default_affinity_proc(void)
+{
+#ifdef CONFIG_SMP
+ struct proc_dir_entry *entry;
+
+ /* create /proc/irq/default_smp_affinity */
+ entry = create_proc_entry("default_smp_affinity", 0600, root_irq_dir);
+ if (entry) {
+ entry->data = NULL;
+ entry->read_proc = default_affinity_read;
+ entry->write_proc = default_affinity_write;
+ }
+#endif
+}
+
void init_irq_proc(void)
{
int i;
@@ -180,6 +229,8 @@ void init_irq_proc(void)
if (!root_irq_dir)
return;
+ register_default_affinity_proc();
+
/*
* Create entries for all existing IRQs.
*/
diff --git a/kernel/kthread.c b/kernel/kthread.c
index bd1b9ea024e1..97747cdd37c9 100644
--- a/kernel/kthread.c
+++ b/kernel/kthread.c
@@ -180,6 +180,7 @@ void kthread_bind(struct task_struct *k, unsigned int cpu)
set_task_cpu(k, cpu);
k->cpus_allowed = cpumask_of_cpu(cpu);
k->rt.nr_cpus_allowed = 1;
+ k->flags |= PF_THREAD_BOUND;
}
EXPORT_SYMBOL(kthread_bind);
diff --git a/kernel/lockdep.c b/kernel/lockdep.c
index 81a4e4a3f087..21242a3ecf30 100644
--- a/kernel/lockdep.c
+++ b/kernel/lockdep.c
@@ -39,6 +39,7 @@
#include <linux/irqflags.h>
#include <linux/utsname.h>
#include <linux/hash.h>
+#include <linux/ftrace.h>
#include <asm/sections.h>
@@ -81,6 +82,8 @@ static int graph_lock(void)
__raw_spin_unlock(&lockdep_lock);
return 0;
}
+ /* prevent any recursions within lockdep from causing deadlocks */
+ current->lockdep_recursion++;
return 1;
}
@@ -89,6 +92,7 @@ static inline int graph_unlock(void)
if (debug_locks && !__raw_spin_is_locked(&lockdep_lock))
return DEBUG_LOCKS_WARN_ON(1);
+ current->lockdep_recursion--;
__raw_spin_unlock(&lockdep_lock);
return 0;
}
@@ -609,8 +613,8 @@ static int static_obj(void *obj)
* percpu var?
*/
for_each_possible_cpu(i) {
- start = (unsigned long) &__per_cpu_start + per_cpu_offset(i);
- end = (unsigned long) &__per_cpu_start + PERCPU_ENOUGH_ROOM
+ start = (unsigned long) __per_cpu_start + per_cpu_offset(i);
+ end = (unsigned long) __per_cpu_start + PERCPU_ENOUGH_ROOM
+ per_cpu_offset(i);
if ((addr >= start) && (addr < end))
@@ -982,7 +986,7 @@ check_noncircular(struct lock_class *source, unsigned int depth)
return 1;
}
-#ifdef CONFIG_TRACE_IRQFLAGS
+#if defined(CONFIG_TRACE_IRQFLAGS) && defined(CONFIG_PROVE_LOCKING)
/*
* Forwards and backwards subgraph searching, for the purposes of
* proving that two subgraphs can be connected by a new dependency
@@ -1680,7 +1684,7 @@ valid_state(struct task_struct *curr, struct held_lock *this,
static int mark_lock(struct task_struct *curr, struct held_lock *this,
enum lock_usage_bit new_bit);
-#ifdef CONFIG_TRACE_IRQFLAGS
+#if defined(CONFIG_TRACE_IRQFLAGS) && defined(CONFIG_PROVE_LOCKING)
/*
* print irq inversion bug:
@@ -2013,11 +2017,13 @@ void early_boot_irqs_on(void)
/*
* Hardirqs will be enabled:
*/
-void trace_hardirqs_on(void)
+void trace_hardirqs_on_caller(unsigned long a0)
{
struct task_struct *curr = current;
unsigned long ip;
+ time_hardirqs_on(CALLER_ADDR0, a0);
+
if (unlikely(!debug_locks || current->lockdep_recursion))
return;
@@ -2055,16 +2061,23 @@ void trace_hardirqs_on(void)
curr->hardirq_enable_event = ++curr->irq_events;
debug_atomic_inc(&hardirqs_on_events);
}
+EXPORT_SYMBOL(trace_hardirqs_on_caller);
+void trace_hardirqs_on(void)
+{
+ trace_hardirqs_on_caller(CALLER_ADDR0);
+}
EXPORT_SYMBOL(trace_hardirqs_on);
/*
* Hardirqs were disabled:
*/
-void trace_hardirqs_off(void)
+void trace_hardirqs_off_caller(unsigned long a0)
{
struct task_struct *curr = current;
+ time_hardirqs_off(CALLER_ADDR0, a0);
+
if (unlikely(!debug_locks || current->lockdep_recursion))
return;
@@ -2082,7 +2095,12 @@ void trace_hardirqs_off(void)
} else
debug_atomic_inc(&redundant_hardirqs_off);
}
+EXPORT_SYMBOL(trace_hardirqs_off_caller);
+void trace_hardirqs_off(void)
+{
+ trace_hardirqs_off_caller(CALLER_ADDR0);
+}
EXPORT_SYMBOL(trace_hardirqs_off);
/*
@@ -2246,7 +2264,7 @@ static inline int separate_irq_context(struct task_struct *curr,
* Mark a lock with a usage bit, and validate the state transition:
*/
static int mark_lock(struct task_struct *curr, struct held_lock *this,
- enum lock_usage_bit new_bit)
+ enum lock_usage_bit new_bit)
{
unsigned int new_mask = 1 << new_bit, ret = 1;
@@ -2686,7 +2704,7 @@ static void check_flags(unsigned long flags)
* and also avoid lockdep recursion:
*/
void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
- int trylock, int read, int check, unsigned long ip)
+ int trylock, int read, int check, unsigned long ip)
{
unsigned long flags;
@@ -2708,7 +2726,8 @@ void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
EXPORT_SYMBOL_GPL(lock_acquire);
-void lock_release(struct lockdep_map *lock, int nested, unsigned long ip)
+void lock_release(struct lockdep_map *lock, int nested,
+ unsigned long ip)
{
unsigned long flags;
diff --git a/kernel/marker.c b/kernel/marker.c
index b5a9fe1d50d5..1abfb923b761 100644
--- a/kernel/marker.c
+++ b/kernel/marker.c
@@ -55,8 +55,8 @@ static DEFINE_MUTEX(markers_mutex);
struct marker_entry {
struct hlist_node hlist;
char *format;
- void (*call)(const struct marker *mdata, /* Probe wrapper */
- void *call_private, const char *fmt, ...);
+ /* Probe wrapper */
+ void (*call)(const struct marker *mdata, void *call_private, ...);
struct marker_probe_closure single;
struct marker_probe_closure *multi;
int refcount; /* Number of times armed. 0 if disarmed. */
@@ -91,15 +91,13 @@ EXPORT_SYMBOL_GPL(__mark_empty_function);
* marker_probe_cb Callback that prepares the variable argument list for probes.
* @mdata: pointer of type struct marker
* @call_private: caller site private data
- * @fmt: format string
* @...: Variable argument list.
*
* Since we do not use "typical" pointer based RCU in the 1 argument case, we
* need to put a full smp_rmb() in this branch. This is why we do not use
* rcu_dereference() for the pointer read.
*/
-void marker_probe_cb(const struct marker *mdata, void *call_private,
- const char *fmt, ...)
+void marker_probe_cb(const struct marker *mdata, void *call_private, ...)
{
va_list args;
char ptype;
@@ -120,8 +118,9 @@ void marker_probe_cb(const struct marker *mdata, void *call_private,
/* Must read the ptr before private data. They are not data
* dependant, so we put an explicit smp_rmb() here. */
smp_rmb();
- va_start(args, fmt);
- func(mdata->single.probe_private, call_private, fmt, &args);
+ va_start(args, call_private);
+ func(mdata->single.probe_private, call_private, mdata->format,
+ &args);
va_end(args);
} else {
struct marker_probe_closure *multi;
@@ -136,9 +135,9 @@ void marker_probe_cb(const struct marker *mdata, void *call_private,
smp_read_barrier_depends();
multi = mdata->multi;
for (i = 0; multi[i].func; i++) {
- va_start(args, fmt);
- multi[i].func(multi[i].probe_private, call_private, fmt,
- &args);
+ va_start(args, call_private);
+ multi[i].func(multi[i].probe_private, call_private,
+ mdata->format, &args);
va_end(args);
}
}
@@ -150,13 +149,11 @@ EXPORT_SYMBOL_GPL(marker_probe_cb);
* marker_probe_cb Callback that does not prepare the variable argument list.
* @mdata: pointer of type struct marker
* @call_private: caller site private data
- * @fmt: format string
* @...: Variable argument list.
*
* Should be connected to markers "MARK_NOARGS".
*/
-void marker_probe_cb_noarg(const struct marker *mdata,
- void *call_private, const char *fmt, ...)
+void marker_probe_cb_noarg(const struct marker *mdata, void *call_private, ...)
{
va_list args; /* not initialized */
char ptype;
@@ -172,7 +169,8 @@ void marker_probe_cb_noarg(const struct marker *mdata,
/* Must read the ptr before private data. They are not data
* dependant, so we put an explicit smp_rmb() here. */
smp_rmb();
- func(mdata->single.probe_private, call_private, fmt, &args);
+ func(mdata->single.probe_private, call_private, mdata->format,
+ &args);
} else {
struct marker_probe_closure *multi;
int i;
@@ -186,8 +184,8 @@ void marker_probe_cb_noarg(const struct marker *mdata,
smp_read_barrier_depends();
multi = mdata->multi;
for (i = 0; multi[i].func; i++)
- multi[i].func(multi[i].probe_private, call_private, fmt,
- &args);
+ multi[i].func(multi[i].probe_private, call_private,
+ mdata->format, &args);
}
preempt_enable();
}
diff --git a/kernel/module.c b/kernel/module.c
index 5f80478b746d..f525015dd65d 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -44,6 +44,7 @@
#include <linux/unwind.h>
#include <asm/uaccess.h>
#include <asm/cacheflush.h>
+#include <asm/sections.h>
#include <linux/license.h>
#include <asm/sections.h>
@@ -365,7 +366,7 @@ static void *percpu_modalloc(unsigned long size, unsigned long align,
align = PAGE_SIZE;
}
- ptr = __per_cpu_start;
+ ptr = __per_cpu_load;
for (i = 0; i < pcpu_num_used; ptr += block_size(pcpu_size[i]), i++) {
/* Extra for alignment requirement. */
extra = ALIGN((unsigned long)ptr, align) - (unsigned long)ptr;
@@ -400,7 +401,7 @@ static void *percpu_modalloc(unsigned long size, unsigned long align,
static void percpu_modfree(void *freeme)
{
unsigned int i;
- void *ptr = __per_cpu_start + block_size(pcpu_size[0]);
+ void *ptr = __per_cpu_load + block_size(pcpu_size[0]);
/* First entry is core kernel percpu data. */
for (i = 1; i < pcpu_num_used; ptr += block_size(pcpu_size[i]), i++) {
@@ -451,7 +452,7 @@ static int percpu_modinit(void)
pcpu_size = kmalloc(sizeof(pcpu_size[0]) * pcpu_num_allocated,
GFP_KERNEL);
/* Static in-kernel percpu data (used). */
- pcpu_size[0] = -(__per_cpu_end-__per_cpu_start);
+ pcpu_size[0] = -__per_cpu_size;
/* Free room. */
pcpu_size[1] = PERCPU_ENOUGH_ROOM + pcpu_size[0];
if (pcpu_size[1] < 0) {
diff --git a/kernel/mutex-debug.c b/kernel/mutex-debug.c
index 3aaa06c561de..1d94160eb532 100644
--- a/kernel/mutex-debug.c
+++ b/kernel/mutex-debug.c
@@ -79,8 +79,8 @@ void debug_mutex_unlock(struct mutex *lock)
if (unlikely(!debug_locks))
return;
- DEBUG_LOCKS_WARN_ON(lock->owner != current_thread_info());
DEBUG_LOCKS_WARN_ON(lock->magic != lock);
+ DEBUG_LOCKS_WARN_ON(lock->owner != current_thread_info());
DEBUG_LOCKS_WARN_ON(!lock->wait_list.prev && !lock->wait_list.next);
DEBUG_LOCKS_WARN_ON(lock->owner != current_thread_info());
}
diff --git a/kernel/mutex.c b/kernel/mutex.c
index d046a345d365..bcdc9ac8ef60 100644
--- a/kernel/mutex.c
+++ b/kernel/mutex.c
@@ -165,10 +165,7 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
* got a signal? (This code gets eliminated in the
* TASK_UNINTERRUPTIBLE case.)
*/
- if (unlikely((state == TASK_INTERRUPTIBLE &&
- signal_pending(task)) ||
- (state == TASK_KILLABLE &&
- fatal_signal_pending(task)))) {
+ if (unlikely(signal_pending_state(state, task))) {
mutex_remove_waiter(lock, &waiter,
task_thread_info(task));
mutex_release(&lock->dep_map, 1, ip);
diff --git a/kernel/panic.c b/kernel/panic.c
index 425567f45b9f..6729e3f4ebcb 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -80,6 +80,9 @@ NORET_TYPE void panic(const char * fmt, ...)
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
printk(KERN_EMERG "Kernel panic - not syncing: %s\n",buf);
+#ifdef CONFIG_DEBUG_BUGVERBOSE
+ dump_stack();
+#endif
bust_spinlocks(0);
/*
@@ -321,13 +324,85 @@ EXPORT_SYMBOL(warn_on_slowpath);
#endif
#ifdef CONFIG_CC_STACKPROTECTOR
+
+#ifndef GCC_HAS_SP
+#warning You have selected the CONFIG_CC_STACKPROTECTOR option, but the gcc used does not support this.
+#endif
+static unsigned long __stack_check_testing;
+/*
+ * Self test function for the stack-protector feature.
+ * This test requires that the local variable absolutely has
+ * a stack slot, hence the barrier()s.
+ */
+static noinline void __stack_chk_test_func(void)
+{
+ unsigned long foo;
+ barrier();
+ /*
+ * we need to make sure we're not about to clobber the return address,
+ * while real exploits do this, it's unhealthy on a running system.
+ * Besides, if we would, the test is already failed anyway so
+ * time to pull the emergency brake on it.
+ */
+ if ((unsigned long)__builtin_return_address(0) ==
+ *(((unsigned long *)&foo)+1)) {
+ printk(KERN_ERR "No -fstack-protector-stack-frame!\n");
+ return;
+ }
+#ifdef CONFIG_FRAME_POINTER
+ /* We also don't want to clobber the frame pointer */
+ if ((unsigned long)__builtin_return_address(0) ==
+ *(((unsigned long *)&foo)+2)) {
+ printk(KERN_ERR "No -fstack-protector-stack-frame!\n");
+ return;
+ }
+#endif
+ barrier();
+ if (current->stack_canary == *(((unsigned long *)&foo)+1))
+ *(((unsigned long *)&foo)+1) = 0;
+ else
+ printk(KERN_ERR "No -fstack-protector canary found\n");
+ barrier();
+}
+
+static int __stack_chk_test(void)
+{
+ printk(KERN_INFO "Testing -fstack-protector-all feature\n");
+ __stack_check_testing = (unsigned long)&__stack_chk_test_func;
+ __stack_chk_test_func();
+ if (__stack_check_testing) {
+ printk(KERN_ERR "-fstack-protector-all test failed\n");
+ WARN_ON(1);
+ }
+ return 0;
+}
/*
* Called when gcc's -fstack-protector feature is used, and
* gcc detects corruption of the on-stack canary value
*/
void __stack_chk_fail(void)
{
- panic("stack-protector: Kernel stack is corrupted");
+ if (__stack_check_testing == (unsigned long)&__stack_chk_test_func) {
+ long delta;
+
+ delta = (unsigned long)__builtin_return_address(0) -
+ __stack_check_testing;
+ /*
+ * The test needs to happen inside the test function, so
+ * check if the return address is close to that function.
+ * The function is only 2 dozen bytes long, but keep a wide
+ * safety margin to avoid panic()s for normal users regardless
+ * of the quality of the compiler.
+ */
+ if (delta >= 0 && delta <= 400) {
+ __stack_check_testing = 0;
+ return;
+ }
+ }
+ panic("stack-protector: Kernel stack is corrupted in: %p\n",
+ __builtin_return_address(0));
}
EXPORT_SYMBOL(__stack_chk_fail);
+
+late_initcall(__stack_chk_test);
#endif
diff --git a/kernel/pid.c b/kernel/pid.c
index 20d59fa2d493..30bd5d4b2ac7 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -30,6 +30,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
+#include <linux/rculist.h>
#include <linux/bootmem.h>
#include <linux/hash.h>
#include <linux/pid_namespace.h>
diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c
index f1525ad06cb3..c42a03aef36f 100644
--- a/kernel/posix-cpu-timers.c
+++ b/kernel/posix-cpu-timers.c
@@ -1037,6 +1037,9 @@ static void check_thread_timers(struct task_struct *tsk,
sig->rlim[RLIMIT_RTTIME].rlim_cur +=
USEC_PER_SEC;
}
+ printk(KERN_INFO
+ "RT Watchdog Timeout: %s[%d]\n",
+ tsk->comm, task_pid_nr(tsk));
__group_send_sig_info(SIGXCPU, SEND_SIG_PRIV, tsk);
}
}
diff --git a/kernel/power/disk.c b/kernel/power/disk.c
index 14a656cdc652..f011e0870b52 100644
--- a/kernel/power/disk.c
+++ b/kernel/power/disk.c
@@ -180,6 +180,17 @@ static void platform_restore_cleanup(int platform_mode)
}
/**
+ * platform_recover - recover the platform from a failure to suspend
+ * devices.
+ */
+
+static void platform_recover(int platform_mode)
+{
+ if (platform_mode && hibernation_ops && hibernation_ops->recover)
+ hibernation_ops->recover();
+}
+
+/**
* create_image - freeze devices that need to be frozen with interrupts
* off, create the hibernation image and thaw those devices. Control
* reappears in this routine after a restore.
@@ -193,6 +204,7 @@ static int create_image(int platform_mode)
if (error)
return error;
+ device_pm_lock();
local_irq_disable();
/* At this point, device_suspend() has been called, but *not*
* device_power_down(). We *must* call device_power_down() now.
@@ -224,9 +236,11 @@ static int create_image(int platform_mode)
/* NOTE: device_power_up() is just a resume() for devices
* that suspended with irqs off ... no overall powerup.
*/
- device_power_up();
+ device_power_up(in_suspend ?
+ (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
Enable_irqs:
local_irq_enable();
+ device_pm_unlock();
return error;
}
@@ -255,10 +269,10 @@ int hibernation_snapshot(int platform_mode)
suspend_console();
error = device_suspend(PMSG_FREEZE);
if (error)
- goto Resume_console;
+ goto Recover_platform;
if (hibernation_test(TEST_DEVICES))
- goto Resume_devices;
+ goto Recover_platform;
error = platform_pre_snapshot(platform_mode);
if (error || hibernation_test(TEST_PLATFORM))
@@ -280,12 +294,16 @@ int hibernation_snapshot(int platform_mode)
Finish:
platform_finish(platform_mode);
Resume_devices:
- device_resume();
- Resume_console:
+ device_resume(in_suspend ?
+ (error ? PMSG_RECOVER : PMSG_THAW) : PMSG_RESTORE);
resume_console();
Close:
platform_end(platform_mode);
return error;
+
+ Recover_platform:
+ platform_recover(platform_mode);
+ goto Resume_devices;
}
/**
@@ -300,8 +318,9 @@ static int resume_target_kernel(void)
{
int error;
+ device_pm_lock();
local_irq_disable();
- error = device_power_down(PMSG_PRETHAW);
+ error = device_power_down(PMSG_QUIESCE);
if (error) {
printk(KERN_ERR "PM: Some devices failed to power down, "
"aborting resume\n");
@@ -329,9 +348,10 @@ static int resume_target_kernel(void)
swsusp_free();
restore_processor_state();
touch_softlockup_watchdog();
- device_power_up();
+ device_power_up(PMSG_RECOVER);
Enable_irqs:
local_irq_enable();
+ device_pm_unlock();
return error;
}
@@ -350,7 +370,7 @@ int hibernation_restore(int platform_mode)
pm_prepare_console();
suspend_console();
- error = device_suspend(PMSG_PRETHAW);
+ error = device_suspend(PMSG_QUIESCE);
if (error)
goto Finish;
@@ -362,7 +382,7 @@ int hibernation_restore(int platform_mode)
enable_nonboot_cpus();
}
platform_restore_cleanup(platform_mode);
- device_resume();
+ device_resume(PMSG_RECOVER);
Finish:
resume_console();
pm_restore_console();
@@ -392,8 +412,11 @@ int hibernation_platform_enter(void)
suspend_console();
error = device_suspend(PMSG_HIBERNATE);
- if (error)
- goto Resume_console;
+ if (error) {
+ if (hibernation_ops->recover)
+ hibernation_ops->recover();
+ goto Resume_devices;
+ }
error = hibernation_ops->prepare();
if (error)
@@ -403,6 +426,7 @@ int hibernation_platform_enter(void)
if (error)
goto Finish;
+ device_pm_lock();
local_irq_disable();
error = device_power_down(PMSG_HIBERNATE);
if (!error) {
@@ -411,6 +435,7 @@ int hibernation_platform_enter(void)
while (1);
}
local_irq_enable();
+ device_pm_unlock();
/*
* We don't need to reenable the nonboot CPUs or resume consoles, since
@@ -419,8 +444,7 @@ int hibernation_platform_enter(void)
Finish:
hibernation_ops->finish();
Resume_devices:
- device_resume();
- Resume_console:
+ device_resume(PMSG_RESTORE);
resume_console();
Close:
hibernation_ops->end();
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 6a6d5eb3524e..3398f4651aa1 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -228,6 +228,7 @@ static int suspend_enter(suspend_state_t state)
{
int error = 0;
+ device_pm_lock();
arch_suspend_disable_irqs();
BUG_ON(!irqs_disabled());
@@ -239,10 +240,11 @@ static int suspend_enter(suspend_state_t state)
if (!suspend_test(TEST_CORE))
error = suspend_ops->enter(state);
- device_power_up();
+ device_power_up(PMSG_RESUME);
Done:
arch_suspend_enable_irqs();
BUG_ON(irqs_disabled());
+ device_pm_unlock();
return error;
}
@@ -267,11 +269,11 @@ int suspend_devices_and_enter(suspend_state_t state)
error = device_suspend(PMSG_SUSPEND);
if (error) {
printk(KERN_ERR "PM: Some devices failed to suspend\n");
- goto Resume_console;
+ goto Recover_platform;
}
if (suspend_test(TEST_DEVICES))
- goto Resume_devices;
+ goto Recover_platform;
if (suspend_ops->prepare) {
error = suspend_ops->prepare();
@@ -291,13 +293,17 @@ int suspend_devices_and_enter(suspend_state_t state)
if (suspend_ops->finish)
suspend_ops->finish();
Resume_devices:
- device_resume();
- Resume_console:
+ device_resume(PMSG_RESUME);
resume_console();
Close:
if (suspend_ops->end)
suspend_ops->end();
return error;
+
+ Recover_platform:
+ if (suspend_ops->recover)
+ suspend_ops->recover();
+ goto Resume_devices;
}
/**
diff --git a/kernel/printk.c b/kernel/printk.c
index 8fb01c32aa3b..84d640530bfe 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -75,6 +75,8 @@ EXPORT_SYMBOL(oops_in_progress);
static DECLARE_MUTEX(console_sem);
static DECLARE_MUTEX(secondary_console_sem);
struct console *console_drivers;
+EXPORT_SYMBOL_GPL(console_drivers);
+
/*
* This is used for debugging the mess that is the VT code by
* keeping track if we have the console semaphore held. It's
@@ -121,6 +123,8 @@ struct console_cmdline
static struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES];
static int selected_console = -1;
static int preferred_console = -1;
+int console_set_on_cmdline;
+EXPORT_SYMBOL(console_set_on_cmdline);
/* Flag: console code may call schedule() */
static int console_may_schedule;
@@ -231,7 +235,7 @@ static inline void boot_delay_msec(void)
/*
* Return the number of unread characters in the log buffer.
*/
-int log_buf_get_len(void)
+static int log_buf_get_len(void)
{
return logged_chars;
}
@@ -268,19 +272,6 @@ int log_buf_copy(char *dest, int idx, int len)
}
/*
- * Extract a single character from the log buffer.
- */
-int log_buf_read(int idx)
-{
- char ret;
-
- if (log_buf_copy(&ret, idx, 1) == 1)
- return ret;
- else
- return -1;
-}
-
-/*
* Commands to do_syslog:
*
* 0 -- Close the log. Currently a NOP.
@@ -665,18 +656,17 @@ static int acquire_console_semaphore_for_printk(unsigned int cpu)
spin_unlock(&logbuf_lock);
return retval;
}
-
-const char printk_recursion_bug_msg [] =
- KERN_CRIT "BUG: recent printk recursion!\n";
-static int printk_recursion_bug;
+static const char recursion_bug_msg [] =
+ KERN_CRIT "BUG: recent printk recursion!\n";
+static int recursion_bug;
+ static int new_text_line = 1;
+static char printk_buf[1024];
asmlinkage int vprintk(const char *fmt, va_list args)
{
- static int log_level_unknown = 1;
- static char printk_buf[1024];
-
- unsigned long flags;
int printed_len = 0;
+ int current_log_level = default_message_loglevel;
+ unsigned long flags;
int this_cpu;
char *p;
@@ -699,7 +689,7 @@ asmlinkage int vprintk(const char *fmt, va_list args)
* it can be printed at the next appropriate moment:
*/
if (!oops_in_progress) {
- printk_recursion_bug = 1;
+ recursion_bug = 1;
goto out_restore_irqs;
}
zap_locks();
@@ -709,70 +699,62 @@ asmlinkage int vprintk(const char *fmt, va_list args)
spin_lock(&logbuf_lock);
printk_cpu = this_cpu;
- if (printk_recursion_bug) {
- printk_recursion_bug = 0;
- strcpy(printk_buf, printk_recursion_bug_msg);
- printed_len = sizeof(printk_recursion_bug_msg);
+ if (recursion_bug) {
+ recursion_bug = 0;
+ strcpy(printk_buf, recursion_bug_msg);
+ printed_len = sizeof(recursion_bug_msg);
}
/* Emit the output into the temporary buffer */
printed_len += vscnprintf(printk_buf + printed_len,
sizeof(printk_buf) - printed_len, fmt, args);
+
/*
* Copy the output into log_buf. If the caller didn't provide
* appropriate log level tags, we insert them here
*/
for (p = printk_buf; *p; p++) {
- if (log_level_unknown) {
- /* log_level_unknown signals the start of a new line */
+ if (new_text_line) {
+ /* If a token, set current_log_level and skip over */
+ if (p[0] == '<' && p[1] >= '0' && p[1] <= '7' &&
+ p[2] == '>') {
+ current_log_level = p[1] - '0';
+ p += 3;
+ printed_len -= 3;
+ }
+
+ /* Always output the token */
+ emit_log_char('<');
+ emit_log_char(current_log_level + '0');
+ emit_log_char('>');
+ printed_len += 3;
+ new_text_line = 0;
+
if (printk_time) {
- int loglev_char;
+ /* Follow the token with the time */
char tbuf[50], *tp;
unsigned tlen;
unsigned long long t;
unsigned long nanosec_rem;
- /*
- * force the log level token to be
- * before the time output.
- */
- if (p[0] == '<' && p[1] >='0' &&
- p[1] <= '7' && p[2] == '>') {
- loglev_char = p[1];
- p += 3;
- printed_len -= 3;
- } else {
- loglev_char = default_message_loglevel
- + '0';
- }
t = cpu_clock(printk_cpu);
nanosec_rem = do_div(t, 1000000000);
- tlen = sprintf(tbuf,
- "<%c>[%5lu.%06lu] ",
- loglev_char,
- (unsigned long)t,
- nanosec_rem/1000);
+ tlen = sprintf(tbuf, "[%5lu.%06lu] ",
+ (unsigned long) t,
+ nanosec_rem / 1000);
for (tp = tbuf; tp < tbuf + tlen; tp++)
emit_log_char(*tp);
printed_len += tlen;
- } else {
- if (p[0] != '<' || p[1] < '0' ||
- p[1] > '7' || p[2] != '>') {
- emit_log_char('<');
- emit_log_char(default_message_loglevel
- + '0');
- emit_log_char('>');
- printed_len += 3;
- }
}
- log_level_unknown = 0;
+
if (!*p)
break;
}
+
emit_log_char(*p);
if (*p == '\n')
- log_level_unknown = 1;
+ new_text_line = 1;
}
/*
@@ -890,6 +872,7 @@ static int __init console_setup(char *str)
*s = 0;
__add_preferred_console(buf, idx, options, brl_options);
+ console_set_on_cmdline = 1;
return 1;
}
__setup("console=", console_setup);
@@ -1041,7 +1024,9 @@ void release_console_sem(void)
_log_end = log_end;
con_start = log_end; /* Flush */
spin_unlock(&logbuf_lock);
+ stop_critical_timings(); /* don't trace print latency */
call_console_drivers(_con_start, _log_end);
+ start_critical_timings();
local_irq_restore(flags);
}
console_locked = 0;
@@ -1172,8 +1157,11 @@ void register_console(struct console *console)
console->index = 0;
if (console->setup == NULL ||
console->setup(console, NULL) == 0) {
- console->flags |= CON_ENABLED | CON_CONSDEV;
- preferred_console = 0;
+ console->flags |= CON_ENABLED;
+ if (console->device) {
+ console->flags |= CON_CONSDEV;
+ preferred_console = 0;
+ }
}
}
diff --git a/kernel/rcuclassic.c b/kernel/rcuclassic.c
index f4ffbd0f306f..b5b90b25e49f 100644
--- a/kernel/rcuclassic.c
+++ b/kernel/rcuclassic.c
@@ -92,7 +92,7 @@ static void force_quiescent_state(struct rcu_data *rdp,
*/
cpumask = rcp->cpumask;
cpu_clear(rdp->cpu, cpumask);
- for_each_cpu_mask(cpu, cpumask)
+ for_each_cpu_mask_nr(cpu, cpumask)
smp_send_reschedule(cpu);
}
}
@@ -502,10 +502,38 @@ void rcu_check_callbacks(int cpu, int user)
if (user ||
(idle_cpu(cpu) && !in_softirq() &&
hardirq_count() <= (1 << HARDIRQ_SHIFT))) {
+
+ /*
+ * Get here if this CPU took its interrupt from user
+ * mode or from the idle loop, and if this is not a
+ * nested interrupt. In this case, the CPU is in
+ * a quiescent state, so count it.
+ *
+ * Also do a memory barrier. This is needed to handle
+ * the case where writes from a preempt-disable section
+ * of code get reordered into schedule() by this CPU's
+ * write buffer. The memory barrier makes sure that
+ * the rcu_qsctr_inc() and rcu_bh_qsctr_inc() are see
+ * by other CPUs to happen after any such write.
+ */
+
+ smp_mb(); /* See above block comment. */
rcu_qsctr_inc(cpu);
rcu_bh_qsctr_inc(cpu);
- } else if (!in_softirq())
+
+ } else if (!in_softirq()) {
+
+ /*
+ * Get here if this CPU did not take its interrupt from
+ * softirq, in other words, if it is not interrupting
+ * a rcu_bh read-side critical section. This is an _bh
+ * critical section, so count it. The memory barrier
+ * is needed for the same reason as is the above one.
+ */
+
+ smp_mb(); /* See above block comment. */
rcu_bh_qsctr_inc(cpu);
+ }
raise_rcu_softirq();
}
@@ -529,7 +557,7 @@ static void __cpuinit rcu_online_cpu(int cpu)
rcu_init_percpu_data(cpu, &rcu_ctrlblk, rdp);
rcu_init_percpu_data(cpu, &rcu_bh_ctrlblk, bh_rdp);
- open_softirq(RCU_SOFTIRQ, rcu_process_callbacks, NULL);
+ open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);
}
static int __cpuinit rcu_cpu_notify(struct notifier_block *self,
diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c
index c09605f8d16c..4a74b8d48d90 100644
--- a/kernel/rcupdate.c
+++ b/kernel/rcupdate.c
@@ -39,16 +39,16 @@
#include <linux/sched.h>
#include <asm/atomic.h>
#include <linux/bitops.h>
-#include <linux/completion.h>
#include <linux/percpu.h>
#include <linux/notifier.h>
#include <linux/cpu.h>
#include <linux/mutex.h>
#include <linux/module.h>
-struct rcu_synchronize {
- struct rcu_head head;
- struct completion completion;
+enum rcu_barrier {
+ RCU_BARRIER_STD,
+ RCU_BARRIER_BH,
+ RCU_BARRIER_SCHED,
};
static DEFINE_PER_CPU(struct rcu_head, rcu_barrier_head) = {NULL};
@@ -60,7 +60,7 @@ static struct completion rcu_barrier_completion;
* Awaken the corresponding synchronize_rcu() instance now that a
* grace period has elapsed.
*/
-static void wakeme_after_rcu(struct rcu_head *head)
+void wakeme_after_rcu(struct rcu_head *head)
{
struct rcu_synchronize *rcu;
@@ -77,17 +77,7 @@ static void wakeme_after_rcu(struct rcu_head *head)
* sections are delimited by rcu_read_lock() and rcu_read_unlock(),
* and may be nested.
*/
-void synchronize_rcu(void)
-{
- struct rcu_synchronize rcu;
-
- init_completion(&rcu.completion);
- /* Will wake me after RCU finished */
- call_rcu(&rcu.head, wakeme_after_rcu);
-
- /* Wait for it */
- wait_for_completion(&rcu.completion);
-}
+synchronize_rcu_xxx(synchronize_rcu, call_rcu)
EXPORT_SYMBOL_GPL(synchronize_rcu);
static void rcu_barrier_callback(struct rcu_head *notused)
@@ -99,19 +89,30 @@ static void rcu_barrier_callback(struct rcu_head *notused)
/*
* Called with preemption disabled, and from cross-cpu IRQ context.
*/
-static void rcu_barrier_func(void *notused)
+static void rcu_barrier_func(void *type)
{
int cpu = smp_processor_id();
struct rcu_head *head = &per_cpu(rcu_barrier_head, cpu);
atomic_inc(&rcu_barrier_cpu_count);
- call_rcu(head, rcu_barrier_callback);
+ switch ((enum rcu_barrier)type) {
+ case RCU_BARRIER_STD:
+ call_rcu(head, rcu_barrier_callback);
+ break;
+ case RCU_BARRIER_BH:
+ call_rcu_bh(head, rcu_barrier_callback);
+ break;
+ case RCU_BARRIER_SCHED:
+ call_rcu_sched(head, rcu_barrier_callback);
+ break;
+ }
}
-/**
- * rcu_barrier - Wait until all the in-flight RCUs are complete.
+/*
+ * Orchestrate the specified type of RCU barrier, waiting for all
+ * RCU callbacks of the specified type to complete.
*/
-void rcu_barrier(void)
+static void _rcu_barrier(enum rcu_barrier type)
{
BUG_ON(in_interrupt());
/* Take cpucontrol mutex to protect against CPU hotplug */
@@ -127,13 +128,39 @@ void rcu_barrier(void)
* until all the callbacks are queued.
*/
rcu_read_lock();
- on_each_cpu(rcu_barrier_func, NULL, 0, 1);
+ on_each_cpu(rcu_barrier_func, (void *)type, 0, 1);
rcu_read_unlock();
wait_for_completion(&rcu_barrier_completion);
mutex_unlock(&rcu_barrier_mutex);
}
+
+/**
+ * rcu_barrier - Wait until all in-flight call_rcu() callbacks complete.
+ */
+void rcu_barrier(void)
+{
+ _rcu_barrier(RCU_BARRIER_STD);
+}
EXPORT_SYMBOL_GPL(rcu_barrier);
+/**
+ * rcu_barrier_bh - Wait until all in-flight call_rcu_bh() callbacks complete.
+ */
+void rcu_barrier_bh(void)
+{
+ _rcu_barrier(RCU_BARRIER_BH);
+}
+EXPORT_SYMBOL_GPL(rcu_barrier_bh);
+
+/**
+ * rcu_barrier_sched - Wait for in-flight call_rcu_sched() callbacks.
+ */
+void rcu_barrier_sched(void)
+{
+ _rcu_barrier(RCU_BARRIER_SCHED);
+}
+EXPORT_SYMBOL_GPL(rcu_barrier_sched);
+
void __init rcu_init(void)
{
__rcu_init();
diff --git a/kernel/rcupreempt.c b/kernel/rcupreempt.c
index e1cdf196a515..e66167decb8f 100644
--- a/kernel/rcupreempt.c
+++ b/kernel/rcupreempt.c
@@ -46,11 +46,11 @@
#include <asm/atomic.h>
#include <linux/bitops.h>
#include <linux/module.h>
+#include <linux/kthread.h>
#include <linux/completion.h>
#include <linux/moduleparam.h>
#include <linux/percpu.h>
#include <linux/notifier.h>
-#include <linux/rcupdate.h>
#include <linux/cpu.h>
#include <linux/random.h>
#include <linux/delay.h>
@@ -87,9 +87,14 @@ struct rcu_data {
struct rcu_head **nexttail;
struct rcu_head *waitlist[GP_STAGES];
struct rcu_head **waittail[GP_STAGES];
- struct rcu_head *donelist;
+ struct rcu_head *donelist; /* from waitlist & waitschedlist */
struct rcu_head **donetail;
long rcu_flipctr[2];
+ struct rcu_head *nextschedlist;
+ struct rcu_head **nextschedtail;
+ struct rcu_head *waitschedlist;
+ struct rcu_head **waitschedtail;
+ int rcu_sched_sleeping;
#ifdef CONFIG_RCU_TRACE
struct rcupreempt_trace trace;
#endif /* #ifdef CONFIG_RCU_TRACE */
@@ -131,11 +136,24 @@ enum rcu_try_flip_states {
rcu_try_flip_waitmb_state,
};
+/*
+ * States for rcu_ctrlblk.rcu_sched_sleep.
+ */
+
+enum rcu_sched_sleep_states {
+ rcu_sched_not_sleeping, /* Not sleeping, callbacks need GP. */
+ rcu_sched_sleep_prep, /* Thinking of sleeping, rechecking. */
+ rcu_sched_sleeping, /* Sleeping, awaken if GP needed. */
+};
+
struct rcu_ctrlblk {
spinlock_t fliplock; /* Protect state-machine transitions. */
long completed; /* Number of last completed batch. */
enum rcu_try_flip_states rcu_try_flip_state; /* The current state of
the rcu state machine */
+ spinlock_t schedlock; /* Protect rcu_sched sleep state. */
+ enum rcu_sched_sleep_states sched_sleep; /* rcu_sched state. */
+ wait_queue_head_t sched_wq; /* Place for rcu_sched to sleep. */
};
static DEFINE_PER_CPU(struct rcu_data, rcu_data);
@@ -143,8 +161,12 @@ static struct rcu_ctrlblk rcu_ctrlblk = {
.fliplock = __SPIN_LOCK_UNLOCKED(rcu_ctrlblk.fliplock),
.completed = 0,
.rcu_try_flip_state = rcu_try_flip_idle_state,
+ .schedlock = __SPIN_LOCK_UNLOCKED(rcu_ctrlblk.schedlock),
+ .sched_sleep = rcu_sched_not_sleeping,
+ .sched_wq = __WAIT_QUEUE_HEAD_INITIALIZER(rcu_ctrlblk.sched_wq),
};
+static struct task_struct *rcu_sched_grace_period_task;
#ifdef CONFIG_RCU_TRACE
static char *rcu_try_flip_state_names[] =
@@ -207,6 +229,8 @@ static DEFINE_PER_CPU_SHARED_ALIGNED(enum rcu_mb_flag_values, rcu_mb_flag)
*/
#define RCU_TRACE_RDP(f, rdp) RCU_TRACE(f, &((rdp)->trace));
+#define RCU_SCHED_BATCH_TIME (HZ / 50)
+
/*
* Return the number of RCU batches processed thus far. Useful
* for debug and statistics.
@@ -217,8 +241,6 @@ long rcu_batches_completed(void)
}
EXPORT_SYMBOL_GPL(rcu_batches_completed);
-EXPORT_SYMBOL_GPL(rcu_batches_completed_bh);
-
void __rcu_read_lock(void)
{
int idx;
@@ -413,32 +435,34 @@ static void __rcu_advance_callbacks(struct rcu_data *rdp)
}
}
-#ifdef CONFIG_NO_HZ
+DEFINE_PER_CPU_SHARED_ALIGNED(struct rcu_dyntick_sched, rcu_dyntick_sched) = {
+ .dynticks = 1,
+};
-DEFINE_PER_CPU(long, dynticks_progress_counter) = 1;
-static DEFINE_PER_CPU(long, rcu_dyntick_snapshot);
+#ifdef CONFIG_NO_HZ
static DEFINE_PER_CPU(int, rcu_update_flag);
/**
* rcu_irq_enter - Called from Hard irq handlers and NMI/SMI.
*
* If the CPU was idle with dynamic ticks active, this updates the
- * dynticks_progress_counter to let the RCU handling know that the
+ * rcu_dyntick_sched.dynticks to let the RCU handling know that the
* CPU is active.
*/
void rcu_irq_enter(void)
{
int cpu = smp_processor_id();
+ struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);
if (per_cpu(rcu_update_flag, cpu))
per_cpu(rcu_update_flag, cpu)++;
/*
* Only update if we are coming from a stopped ticks mode
- * (dynticks_progress_counter is even).
+ * (rcu_dyntick_sched.dynticks is even).
*/
if (!in_interrupt() &&
- (per_cpu(dynticks_progress_counter, cpu) & 0x1) == 0) {
+ (rdssp->dynticks & 0x1) == 0) {
/*
* The following might seem like we could have a race
* with NMI/SMIs. But this really isn't a problem.
@@ -461,12 +485,12 @@ void rcu_irq_enter(void)
* RCU read-side critical sections on this CPU would
* have already completed.
*/
- per_cpu(dynticks_progress_counter, cpu)++;
+ rdssp->dynticks++;
/*
* The following memory barrier ensures that any
* rcu_read_lock() primitives in the irq handler
* are seen by other CPUs to follow the above
- * increment to dynticks_progress_counter. This is
+ * increment to rcu_dyntick_sched.dynticks. This is
* required in order for other CPUs to correctly
* determine when it is safe to advance the RCU
* grace-period state machine.
@@ -474,7 +498,7 @@ void rcu_irq_enter(void)
smp_mb(); /* see above block comment. */
/*
* Since we can't determine the dynamic tick mode from
- * the dynticks_progress_counter after this routine,
+ * the rcu_dyntick_sched.dynticks after this routine,
* we use a second flag to acknowledge that we came
* from an idle state with ticks stopped.
*/
@@ -482,7 +506,7 @@ void rcu_irq_enter(void)
/*
* If we take an NMI/SMI now, they will also increment
* the rcu_update_flag, and will not update the
- * dynticks_progress_counter on exit. That is for
+ * rcu_dyntick_sched.dynticks on exit. That is for
* this IRQ to do.
*/
}
@@ -492,12 +516,13 @@ void rcu_irq_enter(void)
* rcu_irq_exit - Called from exiting Hard irq context.
*
* If the CPU was idle with dynamic ticks active, update the
- * dynticks_progress_counter to put let the RCU handling be
+ * rcu_dyntick_sched.dynticks to put let the RCU handling be
* aware that the CPU is going back to idle with no ticks.
*/
void rcu_irq_exit(void)
{
int cpu = smp_processor_id();
+ struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);
/*
* rcu_update_flag is set if we interrupted the CPU
@@ -505,7 +530,7 @@ void rcu_irq_exit(void)
* Once this occurs, we keep track of interrupt nesting
* because a NMI/SMI could also come in, and we still
* only want the IRQ that started the increment of the
- * dynticks_progress_counter to be the one that modifies
+ * rcu_dyntick_sched.dynticks to be the one that modifies
* it on exit.
*/
if (per_cpu(rcu_update_flag, cpu)) {
@@ -517,28 +542,29 @@ void rcu_irq_exit(void)
/*
* If an NMI/SMI happens now we are still
- * protected by the dynticks_progress_counter being odd.
+ * protected by the rcu_dyntick_sched.dynticks being odd.
*/
/*
* The following memory barrier ensures that any
* rcu_read_unlock() primitives in the irq handler
* are seen by other CPUs to preceed the following
- * increment to dynticks_progress_counter. This
+ * increment to rcu_dyntick_sched.dynticks. This
* is required in order for other CPUs to determine
* when it is safe to advance the RCU grace-period
* state machine.
*/
smp_mb(); /* see above block comment. */
- per_cpu(dynticks_progress_counter, cpu)++;
- WARN_ON(per_cpu(dynticks_progress_counter, cpu) & 0x1);
+ rdssp->dynticks++;
+ WARN_ON(rdssp->dynticks & 0x1);
}
}
static void dyntick_save_progress_counter(int cpu)
{
- per_cpu(rcu_dyntick_snapshot, cpu) =
- per_cpu(dynticks_progress_counter, cpu);
+ struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);
+
+ rdssp->dynticks_snap = rdssp->dynticks;
}
static inline int
@@ -546,9 +572,10 @@ rcu_try_flip_waitack_needed(int cpu)
{
long curr;
long snap;
+ struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);
- curr = per_cpu(dynticks_progress_counter, cpu);
- snap = per_cpu(rcu_dyntick_snapshot, cpu);
+ curr = rdssp->dynticks;
+ snap = rdssp->dynticks_snap;
smp_mb(); /* force ordering with cpu entering/leaving dynticks. */
/*
@@ -569,7 +596,7 @@ rcu_try_flip_waitack_needed(int cpu)
* that this CPU already acknowledged the counter.
*/
- if ((curr - snap) > 2 || (snap & 0x1) == 0)
+ if ((curr - snap) > 2 || (curr & 0x1) == 0)
return 0;
/* We need this CPU to explicitly acknowledge the counter flip. */
@@ -582,9 +609,10 @@ rcu_try_flip_waitmb_needed(int cpu)
{
long curr;
long snap;
+ struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);
- curr = per_cpu(dynticks_progress_counter, cpu);
- snap = per_cpu(rcu_dyntick_snapshot, cpu);
+ curr = rdssp->dynticks;
+ snap = rdssp->dynticks_snap;
smp_mb(); /* force ordering with cpu entering/leaving dynticks. */
/*
@@ -611,14 +639,86 @@ rcu_try_flip_waitmb_needed(int cpu)
return 1;
}
+static void dyntick_save_progress_counter_sched(int cpu)
+{
+ struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);
+
+ rdssp->sched_dynticks_snap = rdssp->dynticks;
+}
+
+static int rcu_qsctr_inc_needed_dyntick(int cpu)
+{
+ long curr;
+ long snap;
+ struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);
+
+ curr = rdssp->dynticks;
+ snap = rdssp->sched_dynticks_snap;
+ smp_mb(); /* force ordering with cpu entering/leaving dynticks. */
+
+ /*
+ * If the CPU remained in dynticks mode for the entire time
+ * and didn't take any interrupts, NMIs, SMIs, or whatever,
+ * then it cannot be in the middle of an rcu_read_lock(), so
+ * the next rcu_read_lock() it executes must use the new value
+ * of the counter. Therefore, this CPU has been in a quiescent
+ * state the entire time, and we don't need to wait for it.
+ */
+
+ if ((curr == snap) && ((curr & 0x1) == 0))
+ return 0;
+
+ /*
+ * If the CPU passed through or entered a dynticks idle phase with
+ * no active irq handlers, then, as above, this CPU has already
+ * passed through a quiescent state.
+ */
+
+ if ((curr - snap) > 2 || (snap & 0x1) == 0)
+ return 0;
+
+ /* We need this CPU to go through a quiescent state. */
+
+ return 1;
+}
+
#else /* !CONFIG_NO_HZ */
-# define dyntick_save_progress_counter(cpu) do { } while (0)
-# define rcu_try_flip_waitack_needed(cpu) (1)
-# define rcu_try_flip_waitmb_needed(cpu) (1)
+# define dyntick_save_progress_counter(cpu) do { } while (0)
+# define rcu_try_flip_waitack_needed(cpu) (1)
+# define rcu_try_flip_waitmb_needed(cpu) (1)
+
+# define dyntick_save_progress_counter_sched(cpu) do { } while (0)
+# define rcu_qsctr_inc_needed_dyntick(cpu) (1)
#endif /* CONFIG_NO_HZ */
+static void save_qsctr_sched(int cpu)
+{
+ struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);
+
+ rdssp->sched_qs_snap = rdssp->sched_qs;
+}
+
+static inline int rcu_qsctr_inc_needed(int cpu)
+{
+ struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);
+
+ /*
+ * If there has been a quiescent state, no more need to wait
+ * on this CPU.
+ */
+
+ if (rdssp->sched_qs != rdssp->sched_qs_snap) {
+ smp_mb(); /* force ordering with cpu entering schedule(). */
+ return 0;
+ }
+
+ /* We need this CPU to go through a quiescent state. */
+
+ return 1;
+}
+
/*
* Get here when RCU is idle. Decide whether we need to
* move out of idle state, and return non-zero if so.
@@ -657,7 +757,7 @@ rcu_try_flip_idle(void)
/* Now ask each CPU for acknowledgement of the flip. */
- for_each_cpu_mask(cpu, rcu_cpu_online_map) {
+ for_each_cpu_mask_nr(cpu, rcu_cpu_online_map) {
per_cpu(rcu_flip_flag, cpu) = rcu_flipped;
dyntick_save_progress_counter(cpu);
}
@@ -675,7 +775,7 @@ rcu_try_flip_waitack(void)
int cpu;
RCU_TRACE_ME(rcupreempt_trace_try_flip_a1);
- for_each_cpu_mask(cpu, rcu_cpu_online_map)
+ for_each_cpu_mask_nr(cpu, rcu_cpu_online_map)
if (rcu_try_flip_waitack_needed(cpu) &&
per_cpu(rcu_flip_flag, cpu) != rcu_flip_seen) {
RCU_TRACE_ME(rcupreempt_trace_try_flip_ae1);
@@ -707,7 +807,7 @@ rcu_try_flip_waitzero(void)
/* Check to see if the sum of the "last" counters is zero. */
RCU_TRACE_ME(rcupreempt_trace_try_flip_z1);
- for_each_cpu_mask(cpu, rcu_cpu_online_map)
+ for_each_cpu_mask_nr(cpu, rcu_cpu_online_map)
sum += RCU_DATA_CPU(cpu)->rcu_flipctr[lastidx];
if (sum != 0) {
RCU_TRACE_ME(rcupreempt_trace_try_flip_ze1);
@@ -722,7 +822,7 @@ rcu_try_flip_waitzero(void)
smp_mb(); /* ^^^^^^^^^^^^ */
/* Call for a memory barrier from each CPU. */
- for_each_cpu_mask(cpu, rcu_cpu_online_map) {
+ for_each_cpu_mask_nr(cpu, rcu_cpu_online_map) {
per_cpu(rcu_mb_flag, cpu) = rcu_mb_needed;
dyntick_save_progress_counter(cpu);
}
@@ -742,7 +842,7 @@ rcu_try_flip_waitmb(void)
int cpu;
RCU_TRACE_ME(rcupreempt_trace_try_flip_m1);
- for_each_cpu_mask(cpu, rcu_cpu_online_map)
+ for_each_cpu_mask_nr(cpu, rcu_cpu_online_map)
if (rcu_try_flip_waitmb_needed(cpu) &&
per_cpu(rcu_mb_flag, cpu) != rcu_mb_done) {
RCU_TRACE_ME(rcupreempt_trace_try_flip_me1);
@@ -821,6 +921,26 @@ void rcu_check_callbacks(int cpu, int user)
unsigned long flags;
struct rcu_data *rdp = RCU_DATA_CPU(cpu);
+ /*
+ * If this CPU took its interrupt from user mode or from the
+ * idle loop, and this is not a nested interrupt, then
+ * this CPU has to have exited all prior preept-disable
+ * sections of code. So increment the counter to note this.
+ *
+ * The memory barrier is needed to handle the case where
+ * writes from a preempt-disable section of code get reordered
+ * into schedule() by this CPU's write buffer. So the memory
+ * barrier makes sure that the rcu_qsctr_inc() is seen by other
+ * CPUs to happen after any such write.
+ */
+
+ if (user ||
+ (idle_cpu(cpu) && !in_softirq() &&
+ hardirq_count() <= (1 << HARDIRQ_SHIFT))) {
+ smp_mb(); /* Guard against aggressive schedule(). */
+ rcu_qsctr_inc(cpu);
+ }
+
rcu_check_mb(cpu);
if (rcu_ctrlblk.completed == rdp->completed)
rcu_try_flip();
@@ -871,6 +991,8 @@ void rcu_offline_cpu(int cpu)
struct rcu_head *list = NULL;
unsigned long flags;
struct rcu_data *rdp = RCU_DATA_CPU(cpu);
+ struct rcu_head *schedlist = NULL;
+ struct rcu_head **schedtail = &schedlist;
struct rcu_head **tail = &list;
/*
@@ -884,6 +1006,11 @@ void rcu_offline_cpu(int cpu)
rcu_offline_cpu_enqueue(rdp->waitlist[i], rdp->waittail[i],
list, tail);
rcu_offline_cpu_enqueue(rdp->nextlist, rdp->nexttail, list, tail);
+ rcu_offline_cpu_enqueue(rdp->waitschedlist, rdp->waitschedtail,
+ schedlist, schedtail);
+ rcu_offline_cpu_enqueue(rdp->nextschedlist, rdp->nextschedtail,
+ schedlist, schedtail);
+ rdp->rcu_sched_sleeping = 0;
spin_unlock_irqrestore(&rdp->lock, flags);
rdp->waitlistcount = 0;
@@ -918,22 +1045,40 @@ void rcu_offline_cpu(int cpu)
* fix.
*/
- local_irq_save(flags);
+ local_irq_save(flags); /* disable preempt till we know what lock. */
rdp = RCU_DATA_ME();
spin_lock(&rdp->lock);
*rdp->nexttail = list;
if (list)
rdp->nexttail = tail;
+ *rdp->nextschedtail = schedlist;
+ if (schedlist)
+ rdp->nextschedtail = schedtail;
spin_unlock_irqrestore(&rdp->lock, flags);
}
void __devinit rcu_online_cpu(int cpu)
{
unsigned long flags;
+ struct rcu_data *rdp;
spin_lock_irqsave(&rcu_ctrlblk.fliplock, flags);
cpu_set(cpu, rcu_cpu_online_map);
spin_unlock_irqrestore(&rcu_ctrlblk.fliplock, flags);
+
+ /*
+ * The rcu_sched grace-period processing might have bypassed
+ * this CPU, given that it was not in the rcu_cpu_online_map
+ * when the grace-period scan started. This means that the
+ * grace-period task might sleep. So make sure that if this
+ * should happen, the first callback posted to this CPU will
+ * wake up the grace-period task if need be.
+ */
+
+ rdp = RCU_DATA_CPU(cpu);
+ spin_lock_irqsave(&rdp->lock, flags);
+ rdp->rcu_sched_sleeping = 1;
+ spin_unlock_irqrestore(&rdp->lock, flags);
}
#else /* #ifdef CONFIG_HOTPLUG_CPU */
@@ -988,31 +1133,196 @@ void call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
*rdp->nexttail = head;
rdp->nexttail = &head->next;
RCU_TRACE_RDP(rcupreempt_trace_next_add, rdp);
- spin_unlock(&rdp->lock);
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&rdp->lock, flags);
}
EXPORT_SYMBOL_GPL(call_rcu);
+void call_rcu_sched(struct rcu_head *head, void (*func)(struct rcu_head *rcu))
+{
+ unsigned long flags;
+ struct rcu_data *rdp;
+ int wake_gp = 0;
+
+ head->func = func;
+ head->next = NULL;
+ local_irq_save(flags);
+ rdp = RCU_DATA_ME();
+ spin_lock(&rdp->lock);
+ *rdp->nextschedtail = head;
+ rdp->nextschedtail = &head->next;
+ if (rdp->rcu_sched_sleeping) {
+
+ /* Grace-period processing might be sleeping... */
+
+ rdp->rcu_sched_sleeping = 0;
+ wake_gp = 1;
+ }
+ spin_unlock_irqrestore(&rdp->lock, flags);
+ if (wake_gp) {
+
+ /* Wake up grace-period processing, unless someone beat us. */
+
+ spin_lock_irqsave(&rcu_ctrlblk.schedlock, flags);
+ if (rcu_ctrlblk.sched_sleep != rcu_sched_sleeping)
+ wake_gp = 0;
+ rcu_ctrlblk.sched_sleep = rcu_sched_not_sleeping;
+ spin_unlock_irqrestore(&rcu_ctrlblk.schedlock, flags);
+ if (wake_gp)
+ wake_up_interruptible(&rcu_ctrlblk.sched_wq);
+ }
+}
+EXPORT_SYMBOL_GPL(call_rcu_sched);
+
/*
* Wait until all currently running preempt_disable() code segments
* (including hardware-irq-disable segments) complete. Note that
* in -rt this does -not- necessarily result in all currently executing
* interrupt -handlers- having completed.
*/
-void __synchronize_sched(void)
+synchronize_rcu_xxx(__synchronize_sched, call_rcu_sched)
+EXPORT_SYMBOL_GPL(__synchronize_sched);
+
+/*
+ * kthread function that manages call_rcu_sched grace periods.
+ */
+static int rcu_sched_grace_period(void *arg)
{
- cpumask_t oldmask;
+ int couldsleep; /* might sleep after current pass. */
+ int couldsleepnext = 0; /* might sleep after next pass. */
int cpu;
+ unsigned long flags;
+ struct rcu_data *rdp;
+ int ret;
- if (sched_getaffinity(0, &oldmask) < 0)
- oldmask = cpu_possible_map;
- for_each_online_cpu(cpu) {
- sched_setaffinity(0, &cpumask_of_cpu(cpu));
- schedule();
- }
- sched_setaffinity(0, &oldmask);
+ /*
+ * Each pass through the following loop handles one
+ * rcu_sched grace period cycle.
+ */
+ do {
+ /* Save each CPU's current state. */
+
+ for_each_online_cpu(cpu) {
+ dyntick_save_progress_counter_sched(cpu);
+ save_qsctr_sched(cpu);
+ }
+
+ /*
+ * Sleep for about an RCU grace-period's worth to
+ * allow better batching and to consume less CPU.
+ */
+ schedule_timeout_interruptible(RCU_SCHED_BATCH_TIME);
+
+ /*
+ * If there was nothing to do last time, prepare to
+ * sleep at the end of the current grace period cycle.
+ */
+ couldsleep = couldsleepnext;
+ couldsleepnext = 1;
+ if (couldsleep) {
+ spin_lock_irqsave(&rcu_ctrlblk.schedlock, flags);
+ rcu_ctrlblk.sched_sleep = rcu_sched_sleep_prep;
+ spin_unlock_irqrestore(&rcu_ctrlblk.schedlock, flags);
+ }
+
+ /*
+ * Wait on each CPU in turn to have either visited
+ * a quiescent state or been in dynticks-idle mode.
+ */
+ for_each_online_cpu(cpu) {
+ while (rcu_qsctr_inc_needed(cpu) &&
+ rcu_qsctr_inc_needed_dyntick(cpu)) {
+ /* resched_cpu(cpu); @@@ */
+ schedule_timeout_interruptible(1);
+ }
+ }
+
+ /* Advance callbacks for each CPU. */
+
+ for_each_online_cpu(cpu) {
+
+ rdp = RCU_DATA_CPU(cpu);
+ spin_lock_irqsave(&rdp->lock, flags);
+
+ /*
+ * We are running on this CPU irq-disabled, so no
+ * CPU can go offline until we re-enable irqs.
+ * The current CPU might have already gone
+ * offline (between the for_each_offline_cpu and
+ * the spin_lock_irqsave), but in that case all its
+ * callback lists will be empty, so no harm done.
+ *
+ * Advance the callbacks! We share normal RCU's
+ * donelist, since callbacks are invoked the
+ * same way in either case.
+ */
+ if (rdp->waitschedlist != NULL) {
+ *rdp->donetail = rdp->waitschedlist;
+ rdp->donetail = rdp->waitschedtail;
+
+ /*
+ * Next rcu_check_callbacks() will
+ * do the required raise_softirq().
+ */
+ }
+ if (rdp->nextschedlist != NULL) {
+ rdp->waitschedlist = rdp->nextschedlist;
+ rdp->waitschedtail = rdp->nextschedtail;
+ couldsleep = 0;
+ couldsleepnext = 0;
+ } else {
+ rdp->waitschedlist = NULL;
+ rdp->waitschedtail = &rdp->waitschedlist;
+ }
+ rdp->nextschedlist = NULL;
+ rdp->nextschedtail = &rdp->nextschedlist;
+
+ /* Mark sleep intention. */
+
+ rdp->rcu_sched_sleeping = couldsleep;
+
+ spin_unlock_irqrestore(&rdp->lock, flags);
+ }
+
+ /* If we saw callbacks on the last scan, go deal with them. */
+
+ if (!couldsleep)
+ continue;
+
+ /* Attempt to block... */
+
+ spin_lock_irqsave(&rcu_ctrlblk.schedlock, flags);
+ if (rcu_ctrlblk.sched_sleep != rcu_sched_sleep_prep) {
+
+ /*
+ * Someone posted a callback after we scanned.
+ * Go take care of it.
+ */
+ spin_unlock_irqrestore(&rcu_ctrlblk.schedlock, flags);
+ couldsleepnext = 0;
+ continue;
+ }
+
+ /* Block until the next person posts a callback. */
+
+ rcu_ctrlblk.sched_sleep = rcu_sched_sleeping;
+ spin_unlock_irqrestore(&rcu_ctrlblk.schedlock, flags);
+ ret = 0;
+ __wait_event_interruptible(rcu_ctrlblk.sched_wq,
+ rcu_ctrlblk.sched_sleep != rcu_sched_sleeping,
+ ret);
+
+ /*
+ * Signals would prevent us from sleeping, and we cannot
+ * do much with them in any case. So flush them.
+ */
+ if (ret)
+ flush_signals(current);
+ couldsleepnext = 0;
+
+ } while (!kthread_should_stop());
+
+ return (0);
}
-EXPORT_SYMBOL_GPL(__synchronize_sched);
/*
* Check to see if any future RCU-related work will need to be done
@@ -1029,7 +1339,9 @@ int rcu_needs_cpu(int cpu)
return (rdp->donelist != NULL ||
!!rdp->waitlistcount ||
- rdp->nextlist != NULL);
+ rdp->nextlist != NULL ||
+ rdp->nextschedlist != NULL ||
+ rdp->waitschedlist != NULL);
}
int rcu_pending(int cpu)
@@ -1040,7 +1352,9 @@ int rcu_pending(int cpu)
if (rdp->donelist != NULL ||
!!rdp->waitlistcount ||
- rdp->nextlist != NULL)
+ rdp->nextlist != NULL ||
+ rdp->nextschedlist != NULL ||
+ rdp->waitschedlist != NULL)
return 1;
/* The RCU core needs an acknowledgement from this CPU. */
@@ -1107,6 +1421,11 @@ void __init __rcu_init(void)
rdp->donetail = &rdp->donelist;
rdp->rcu_flipctr[0] = 0;
rdp->rcu_flipctr[1] = 0;
+ rdp->nextschedlist = NULL;
+ rdp->nextschedtail = &rdp->nextschedlist;
+ rdp->waitschedlist = NULL;
+ rdp->waitschedtail = &rdp->waitschedlist;
+ rdp->rcu_sched_sleeping = 0;
}
register_cpu_notifier(&rcu_nb);
@@ -1125,15 +1444,19 @@ void __init __rcu_init(void)
for_each_online_cpu(cpu)
rcu_cpu_notify(&rcu_nb, CPU_UP_PREPARE, (void *)(long) cpu);
- open_softirq(RCU_SOFTIRQ, rcu_process_callbacks, NULL);
+ open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);
}
/*
- * Deprecated, use synchronize_rcu() or synchronize_sched() instead.
+ * Late-boot-time RCU initialization that must wait until after scheduler
+ * has been initialized.
*/
-void synchronize_kernel(void)
+void __init rcu_init_sched(void)
{
- synchronize_rcu();
+ rcu_sched_grace_period_task = kthread_run(rcu_sched_grace_period,
+ NULL,
+ "rcu_sched_grace_period");
+ WARN_ON(IS_ERR(rcu_sched_grace_period_task));
}
#ifdef CONFIG_RCU_TRACE
diff --git a/kernel/rcupreempt_trace.c b/kernel/rcupreempt_trace.c
index 49ac4947af24..5edf82c34bbc 100644
--- a/kernel/rcupreempt_trace.c
+++ b/kernel/rcupreempt_trace.c
@@ -38,7 +38,6 @@
#include <linux/moduleparam.h>
#include <linux/percpu.h>
#include <linux/notifier.h>
-#include <linux/rcupdate.h>
#include <linux/cpu.h>
#include <linux/mutex.h>
#include <linux/rcupreempt_trace.h>
diff --git a/kernel/sched.c b/kernel/sched.c
index bfb8ad8ed171..13d9a7b931e4 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -70,10 +70,13 @@
#include <linux/bootmem.h>
#include <linux/debugfs.h>
#include <linux/ctype.h>
+#include <linux/ftrace.h>
#include <asm/tlb.h>
#include <asm/irq_regs.h>
+#include "sched_cpupri.h"
+
/*
* Convert user-nice values [ -20 ... 0 ... 19 ]
* to static priority [ MAX_RT_PRIO..MAX_PRIO-1 ],
@@ -151,7 +154,8 @@ static inline int task_has_rt_policy(struct task_struct *p)
*/
struct rt_prio_array {
DECLARE_BITMAP(bitmap, MAX_RT_PRIO+1); /* include 1 bit for delimiter */
- struct list_head queue[MAX_RT_PRIO];
+ struct list_head xqueue[MAX_RT_PRIO]; /* exclusive queue */
+ struct list_head squeue[MAX_RT_PRIO]; /* shared queue */
};
struct rt_bandwidth {
@@ -289,15 +293,15 @@ struct task_group root_task_group;
static DEFINE_PER_CPU(struct sched_entity, init_sched_entity);
/* Default task group's cfs_rq on each cpu */
static DEFINE_PER_CPU(struct cfs_rq, init_cfs_rq) ____cacheline_aligned_in_smp;
-#endif
+#endif /* CONFIG_FAIR_GROUP_SCHED */
#ifdef CONFIG_RT_GROUP_SCHED
static DEFINE_PER_CPU(struct sched_rt_entity, init_sched_rt_entity);
static DEFINE_PER_CPU(struct rt_rq, init_rt_rq) ____cacheline_aligned_in_smp;
-#endif
-#else
+#endif /* CONFIG_RT_GROUP_SCHED */
+#else /* !CONFIG_FAIR_GROUP_SCHED */
#define root_task_group init_task_group
-#endif
+#endif /* CONFIG_FAIR_GROUP_SCHED */
/* task_group_lock serializes add/remove of task groups and also changes to
* a task group's cpu shares.
@@ -307,17 +311,20 @@ static DEFINE_SPINLOCK(task_group_lock);
#ifdef CONFIG_FAIR_GROUP_SCHED
#ifdef CONFIG_USER_SCHED
# define INIT_TASK_GROUP_LOAD (2*NICE_0_LOAD)
-#else
+#else /* !CONFIG_USER_SCHED */
# define INIT_TASK_GROUP_LOAD NICE_0_LOAD
-#endif
+#endif /* CONFIG_USER_SCHED */
/*
- * A weight of 0, 1 or ULONG_MAX can cause arithmetics problems.
+ * A weight of 0 or 1 can cause arithmetics problems.
+ * A weight of a cfs_rq is the sum of weights of which entities
+ * are queued on this cfs_rq, so a weight of a entity should not be
+ * too large, so as the shares value of a task group.
* (The default weight is 1024 - so there's no practical
* limitation from this.)
*/
#define MIN_SHARES 2
-#define MAX_SHARES (ULONG_MAX - 1)
+#define MAX_SHARES (1UL << 18)
static int init_task_group_load = INIT_TASK_GROUP_LOAD;
#endif
@@ -449,6 +456,9 @@ struct root_domain {
*/
cpumask_t rto_mask;
atomic_t rto_count;
+#ifdef CONFIG_SMP
+ struct cpupri cpupri;
+#endif
};
/*
@@ -523,6 +533,7 @@ struct rq {
int push_cpu;
/* cpu of this runqueue: */
int cpu;
+ int online;
struct task_struct *migration_thread;
struct list_head migration_queue;
@@ -595,6 +606,8 @@ static inline void update_rq_clock(struct rq *rq)
rq->clock = sched_clock_cpu(cpu_of(rq));
}
+#include "sched_trace.h"
+
/*
* Tunables that become constants when CONFIG_SCHED_DEBUG is off:
*/
@@ -604,6 +617,24 @@ static inline void update_rq_clock(struct rq *rq)
# define const_debug static const
#endif
+/**
+ * runqueue_is_locked
+ *
+ * Returns true if the current cpu runqueue is locked.
+ * This interface allows printk to be called with the runqueue lock
+ * held and know whether or not it is OK to wake up the klogd.
+ */
+int runqueue_is_locked(void)
+{
+ int cpu = get_cpu();
+ struct rq *rq = cpu_rq(cpu);
+ int ret;
+
+ ret = spin_is_locked(&rq->lock);
+ put_cpu();
+ return ret;
+}
+
/*
* Debugging: various feature bits
*/
@@ -828,7 +859,7 @@ static unsigned long long __cpu_clock(int cpu)
* For kernel-internal use: high-speed (but slightly incorrect) per-cpu
* clock constructed from sched_clock():
*/
-unsigned long long cpu_clock(int cpu)
+unsigned long long notrace cpu_clock(int cpu)
{
unsigned long long prev_cpu_time, time, delta_time;
unsigned long flags;
@@ -1124,6 +1155,7 @@ static enum hrtimer_restart hrtick(struct hrtimer *timer)
return HRTIMER_NORESTART;
}
+#ifdef CONFIG_SMP
static void hotplug_hrtick_disable(int cpu)
{
struct rq *rq = cpu_rq(cpu);
@@ -1179,6 +1211,7 @@ static void init_hrtick(void)
{
hotcpu_notifier(hotplug_hrtick, 0);
}
+#endif /* CONFIG_SMP */
static void init_rq_hrtick(struct rq *rq)
{
@@ -1308,15 +1341,15 @@ void wake_up_idle_cpu(int cpu)
if (!tsk_is_polling(rq->idle))
smp_send_reschedule(cpu);
}
-#endif
+#endif /* CONFIG_NO_HZ */
-#else
+#else /* !CONFIG_SMP */
static void __resched_task(struct task_struct *p, int tif_bit)
{
assert_spin_locked(&task_rq(p)->lock);
set_tsk_thread_flag(p, tif_bit);
}
-#endif
+#endif /* CONFIG_SMP */
#if BITS_PER_LONG == 32
# define WMULT_CONST (~0UL)
@@ -1337,8 +1370,13 @@ calc_delta_mine(unsigned long delta_exec, unsigned long weight,
{
u64 tmp;
- if (!lw->inv_weight)
- lw->inv_weight = 1 + (WMULT_CONST-lw->weight/2)/(lw->weight+1);
+ if (!lw->inv_weight) {
+ if (BITS_PER_LONG > 32 && unlikely(lw->weight >= WMULT_CONST))
+ lw->inv_weight = 1;
+ else
+ lw->inv_weight = 1 + (WMULT_CONST-lw->weight/2)
+ / (lw->weight+1);
+ }
tmp = (u64)delta_exec * weight;
/*
@@ -1471,16 +1509,8 @@ static unsigned long source_load(int cpu, int type);
static unsigned long target_load(int cpu, int type);
static unsigned long cpu_avg_load_per_task(int cpu);
static int task_hot(struct task_struct *p, u64 now, struct sched_domain *sd);
-#else /* CONFIG_SMP */
-
-#ifdef CONFIG_FAIR_GROUP_SCHED
-static void cfs_rq_set_shares(struct cfs_rq *cfs_rq, unsigned long shares)
-{
-}
#endif
-#endif /* CONFIG_SMP */
-
#include "sched_stats.h"
#include "sched_idletask.c"
#include "sched_fair.c"
@@ -1490,6 +1520,8 @@ static void cfs_rq_set_shares(struct cfs_rq *cfs_rq, unsigned long shares)
#endif
#define sched_class_highest (&rt_sched_class)
+#define for_each_class(class) \
+ for (class = sched_class_highest; class; class = class->next)
static inline void inc_load(struct rq *rq, const struct task_struct *p)
{
@@ -1626,12 +1658,6 @@ inline int task_curr(const struct task_struct *p)
return cpu_curr(task_cpu(p)) == p;
}
-/* Used instead of source_load when we know the type == 0 */
-unsigned long weighted_cpuload(const int cpu)
-{
- return cpu_rq(cpu)->load.weight;
-}
-
static inline void __set_task_cpu(struct task_struct *p, unsigned int cpu)
{
set_task_rq(p, cpu);
@@ -1660,6 +1686,12 @@ static inline void check_class_changed(struct rq *rq, struct task_struct *p,
#ifdef CONFIG_SMP
+/* Used instead of source_load when we know the type == 0 */
+static unsigned long weighted_cpuload(const int cpu)
+{
+ return cpu_rq(cpu)->load.weight;
+}
+
/*
* Is this task likely cache-hot:
*/
@@ -1796,6 +1828,7 @@ void wait_task_inactive(struct task_struct *p)
* just go back and repeat.
*/
rq = task_rq_lock(p, &flags);
+ trace_kernel_sched_wait(p);
running = task_running(rq, p);
on_rq = p->se.on_rq;
task_rq_unlock(rq, &flags);
@@ -1929,7 +1962,7 @@ find_idlest_group(struct sched_domain *sd, struct task_struct *p, int this_cpu)
/* Tally up the load of all CPUs in the group */
avg_load = 0;
- for_each_cpu_mask(i, group->cpumask) {
+ for_each_cpu_mask_nr(i, group->cpumask) {
/* Bias balancing toward cpus of our domain */
if (local_group)
load = source_load(i, load_idx);
@@ -1971,7 +2004,7 @@ find_idlest_cpu(struct sched_group *group, struct task_struct *p, int this_cpu,
/* Traverse only the allowed CPUs */
cpus_and(*tmp, group->cpumask, p->cpus_allowed);
- for_each_cpu_mask(i, *tmp) {
+ for_each_cpu_mask_nr(i, *tmp) {
load = weighted_cpuload(i);
if (load < min_load || (load == min_load && i == this_cpu)) {
@@ -2121,7 +2154,7 @@ static int try_to_wake_up(struct task_struct *p, unsigned int state, int sync)
}
}
}
-#endif
+#endif /* CONFIG_SCHEDSTATS */
out_activate:
#endif /* CONFIG_SMP */
@@ -2139,6 +2172,7 @@ out_activate:
success = 1;
out_running:
+ trace_kernel_sched_wakeup(rq, p);
check_preempt_curr(rq, p);
p->state = TASK_RUNNING;
@@ -2269,6 +2303,7 @@ void wake_up_new_task(struct task_struct *p, unsigned long clone_flags)
p->sched_class->task_new(rq, p);
inc_nr_running(p, rq);
}
+ trace_kernel_sched_wakeup_new(rq, p);
check_preempt_curr(rq, p);
#ifdef CONFIG_SMP
if (p->sched_class->task_wake_up)
@@ -2321,7 +2356,7 @@ fire_sched_out_preempt_notifiers(struct task_struct *curr,
notifier->ops->sched_out(notifier, next);
}
-#else
+#else /* !CONFIG_PREEMPT_NOTIFIERS */
static void fire_sched_in_preempt_notifiers(struct task_struct *curr)
{
@@ -2333,7 +2368,7 @@ fire_sched_out_preempt_notifiers(struct task_struct *curr,
{
}
-#endif
+#endif /* CONFIG_PREEMPT_NOTIFIERS */
/**
* prepare_task_switch - prepare to switch tasks
@@ -2441,6 +2476,8 @@ context_switch(struct rq *rq, struct task_struct *prev,
struct mm_struct *mm, *oldmm;
prepare_task_switch(rq, prev, next);
+
+ trace_kernel_sched_switch(rq, prev, next);
mm = next->mm;
oldmm = prev->active_mm;
/*
@@ -2673,6 +2710,7 @@ static void sched_migrate_task(struct task_struct *p, int dest_cpu)
|| unlikely(cpu_is_offline(dest_cpu)))
goto out;
+ trace_kernel_sched_migrate_task(p, cpu_of(rq), dest_cpu);
/* force the process onto the specified CPU */
if (migrate_task(p, dest_cpu, &req)) {
/* Need to wait for migration thread (might exit: take ref). */
@@ -2954,7 +2992,7 @@ find_busiest_group(struct sched_domain *sd, int this_cpu,
max_cpu_load = 0;
min_cpu_load = ~0UL;
- for_each_cpu_mask(i, group->cpumask) {
+ for_each_cpu_mask_nr(i, group->cpumask) {
struct rq *rq;
if (!cpu_isset(i, *cpus))
@@ -3218,7 +3256,7 @@ find_busiest_queue(struct sched_group *group, enum cpu_idle_type idle,
unsigned long max_load = 0;
int i;
- for_each_cpu_mask(i, group->cpumask) {
+ for_each_cpu_mask_nr(i, group->cpumask) {
unsigned long wl;
if (!cpu_isset(i, *cpus))
@@ -3662,6 +3700,7 @@ static void rebalance_domains(int cpu, enum cpu_idle_type idle)
/* Earliest time when we have to do rebalance again */
unsigned long next_balance = jiffies + 60*HZ;
int update_next_balance = 0;
+ int need_serialize;
cpumask_t tmp;
for_each_domain(cpu, sd) {
@@ -3679,8 +3718,9 @@ static void rebalance_domains(int cpu, enum cpu_idle_type idle)
if (interval > HZ*NR_CPUS/10)
interval = HZ*NR_CPUS/10;
+ need_serialize = sd->flags & SD_SERIALIZE;
- if (sd->flags & SD_SERIALIZE) {
+ if (need_serialize) {
if (!spin_trylock(&balancing))
goto out;
}
@@ -3696,7 +3736,7 @@ static void rebalance_domains(int cpu, enum cpu_idle_type idle)
}
sd->last_balance = jiffies;
}
- if (sd->flags & SD_SERIALIZE)
+ if (need_serialize)
spin_unlock(&balancing);
out:
if (time_after(next_balance, sd->last_balance + interval)) {
@@ -3749,7 +3789,7 @@ static void run_rebalance_domains(struct softirq_action *h)
int balance_cpu;
cpu_clear(this_cpu, cpus);
- for_each_cpu_mask(balance_cpu, cpus) {
+ for_each_cpu_mask_nr(balance_cpu, cpus) {
/*
* If this cpu gets work to do, stop the load balancing
* work being done for other cpus. Next load
@@ -4011,26 +4051,44 @@ void scheduler_tick(void)
#endif
}
-#if defined(CONFIG_PREEMPT) && defined(CONFIG_DEBUG_PREEMPT)
+#if defined(CONFIG_PREEMPT) && (defined(CONFIG_DEBUG_PREEMPT) || \
+ defined(CONFIG_PREEMPT_TRACER))
+
+static inline unsigned long get_parent_ip(unsigned long addr)
+{
+ if (in_lock_functions(addr)) {
+ addr = CALLER_ADDR2;
+ if (in_lock_functions(addr))
+ addr = CALLER_ADDR3;
+ }
+ return addr;
+}
void __kprobes add_preempt_count(int val)
{
+#ifdef CONFIG_DEBUG_PREEMPT
/*
* Underflow?
*/
if (DEBUG_LOCKS_WARN_ON((preempt_count() < 0)))
return;
+#endif
preempt_count() += val;
+#ifdef CONFIG_DEBUG_PREEMPT
/*
* Spinlock count overflowing soon?
*/
DEBUG_LOCKS_WARN_ON((preempt_count() & PREEMPT_MASK) >=
PREEMPT_MASK - 10);
+#endif
+ if (preempt_count() == val)
+ trace_preempt_off(CALLER_ADDR0, get_parent_ip(CALLER_ADDR1));
}
EXPORT_SYMBOL(add_preempt_count);
void __kprobes sub_preempt_count(int val)
{
+#ifdef CONFIG_DEBUG_PREEMPT
/*
* Underflow?
*/
@@ -4042,7 +4100,10 @@ void __kprobes sub_preempt_count(int val)
if (DEBUG_LOCKS_WARN_ON((val < PREEMPT_MASK) &&
!(preempt_count() & PREEMPT_MASK)))
return;
+#endif
+ if (preempt_count() == val)
+ trace_preempt_on(CALLER_ADDR0, get_parent_ip(CALLER_ADDR1));
preempt_count() -= val;
}
EXPORT_SYMBOL(sub_preempt_count);
@@ -4060,6 +4121,7 @@ static noinline void __schedule_bug(struct task_struct *prev)
prev->comm, prev->pid, preempt_count());
debug_show_held_locks(prev);
+ print_modules();
if (irqs_disabled())
print_irqtrace_events(prev);
@@ -4133,7 +4195,7 @@ asmlinkage void __sched schedule(void)
struct task_struct *prev, *next;
unsigned long *switch_count;
struct rq *rq;
- int cpu;
+ int cpu, hrtick = sched_feat(HRTICK);
need_resched:
preempt_disable();
@@ -4148,7 +4210,8 @@ need_resched_nonpreemptible:
schedule_debug(prev);
- hrtick_clear(rq);
+ if (hrtick)
+ hrtick_clear(rq);
/*
* Do the rq-clock update outside the rq lock:
@@ -4159,12 +4222,10 @@ need_resched_nonpreemptible:
clear_tsk_need_resched(prev);
if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {
- if (unlikely((prev->state & TASK_INTERRUPTIBLE) &&
- signal_pending(prev))) {
+ if (unlikely(signal_pending_state(prev->state, prev)))
prev->state = TASK_RUNNING;
- } else {
+ else
deactivate_task(rq, prev, 1);
- }
switch_count = &prev->nvcsw;
}
@@ -4196,7 +4257,8 @@ need_resched_nonpreemptible:
} else
spin_unlock_irq(&rq->lock);
- hrtick_set(rq);
+ if (hrtick)
+ hrtick_set(rq);
if (unlikely(reacquire_kernel_lock(current) < 0))
goto need_resched_nonpreemptible;
@@ -5064,24 +5126,6 @@ asmlinkage long sys_sched_setaffinity(pid_t pid, unsigned int len,
return sched_setaffinity(pid, &new_mask);
}
-/*
- * Represents all cpu's present in the system
- * In systems capable of hotplug, this map could dynamically grow
- * as new cpu's are detected in the system via any platform specific
- * method, such as ACPI for e.g.
- */
-
-cpumask_t cpu_present_map __read_mostly;
-EXPORT_SYMBOL(cpu_present_map);
-
-#ifndef CONFIG_SMP
-cpumask_t cpu_online_map __read_mostly = CPU_MASK_ALL;
-EXPORT_SYMBOL(cpu_online_map);
-
-cpumask_t cpu_possible_map __read_mostly = CPU_MASK_ALL;
-EXPORT_SYMBOL(cpu_possible_map);
-#endif
-
long sched_getaffinity(pid_t pid, cpumask_t *mask)
{
struct task_struct *p;
@@ -5378,7 +5422,7 @@ out_unlock:
return retval;
}
-static const char stat_nam[] = "RSDTtZX";
+static const char stat_nam[] = TASK_STATE_TO_CHAR_STR;
void sched_show_task(struct task_struct *p)
{
@@ -5400,12 +5444,7 @@ void sched_show_task(struct task_struct *p)
printk(KERN_CONT " %016lx ", thread_saved_pc(p));
#endif
#ifdef CONFIG_DEBUG_STACK_USAGE
- {
- unsigned long *n = end_of_stack(p);
- while (!*n)
- n++;
- free = (unsigned long)n - (unsigned long)end_of_stack(p);
- }
+ free = stack_not_used(p);
#endif
printk(KERN_CONT "%5lu %5d %6d\n", free,
task_pid_nr(p), task_pid_nr(p->real_parent));
@@ -5565,6 +5604,12 @@ int set_cpus_allowed_ptr(struct task_struct *p, const cpumask_t *new_mask)
goto out;
}
+ if (unlikely((p->flags & PF_THREAD_BOUND) && p != current &&
+ !cpus_equal(p->cpus_allowed, *new_mask))) {
+ ret = -EINVAL;
+ goto out;
+ }
+
if (p->sched_class->set_cpus_allowed)
p->sched_class->set_cpus_allowed(p, new_mask);
else {
@@ -6052,6 +6097,36 @@ static void unregister_sched_domain_sysctl(void)
}
#endif
+static void set_rq_online(struct rq *rq)
+{
+ if (!rq->online) {
+ const struct sched_class *class;
+
+ cpu_set(rq->cpu, rq->rd->online);
+ rq->online = 1;
+
+ for_each_class(class) {
+ if (class->rq_online)
+ class->rq_online(rq);
+ }
+ }
+}
+
+static void set_rq_offline(struct rq *rq)
+{
+ if (rq->online) {
+ const struct sched_class *class;
+
+ for_each_class(class) {
+ if (class->rq_offline)
+ class->rq_offline(rq);
+ }
+
+ cpu_clear(rq->cpu, rq->rd->online);
+ rq->online = 0;
+ }
+}
+
/*
* migration_call - callback that gets triggered when a CPU is added.
* Here we can start up the necessary migration thread for the new CPU.
@@ -6089,7 +6164,8 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu)
spin_lock_irqsave(&rq->lock, flags);
if (rq->rd) {
BUG_ON(!cpu_isset(cpu, rq->rd->span));
- cpu_set(cpu, rq->rd->online);
+
+ set_rq_online(rq);
}
spin_unlock_irqrestore(&rq->lock, flags);
break;
@@ -6150,7 +6226,7 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu)
spin_lock_irqsave(&rq->lock, flags);
if (rq->rd) {
BUG_ON(!cpu_isset(cpu, rq->rd->span));
- cpu_clear(cpu, rq->rd->online);
+ set_rq_offline(rq);
}
spin_unlock_irqrestore(&rq->lock, flags);
break;
@@ -6184,6 +6260,28 @@ void __init migration_init(void)
#ifdef CONFIG_SCHED_DEBUG
+static inline const char *sd_level_to_string(enum sched_domain_level lvl)
+{
+ switch (lvl) {
+ case SD_LV_NONE:
+ return "NONE";
+ case SD_LV_SIBLING:
+ return "SIBLING";
+ case SD_LV_MC:
+ return "MC";
+ case SD_LV_CPU:
+ return "CPU";
+ case SD_LV_NODE:
+ return "NODE";
+ case SD_LV_ALLNODES:
+ return "ALLNODES";
+ case SD_LV_MAX:
+ return "MAX";
+
+ }
+ return "MAX";
+}
+
static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level,
cpumask_t *groupmask)
{
@@ -6203,7 +6301,8 @@ static int sched_domain_debug_one(struct sched_domain *sd, int cpu, int level,
return -1;
}
- printk(KERN_CONT "span %s\n", str);
+ printk(KERN_CONT "span %s level %s\n",
+ str, sd_level_to_string(sd->level));
if (!cpu_isset(cpu, sd->span)) {
printk(KERN_ERR "ERROR: domain->span does not contain "
@@ -6287,9 +6386,9 @@ static void sched_domain_debug(struct sched_domain *sd, int cpu)
}
kfree(groupmask);
}
-#else
+#else /* !CONFIG_SCHED_DEBUG */
# define sched_domain_debug(sd, cpu) do { } while (0)
-#endif
+#endif /* CONFIG_SCHED_DEBUG */
static int sd_degenerate(struct sched_domain *sd)
{
@@ -6349,20 +6448,16 @@ sd_parent_degenerate(struct sched_domain *sd, struct sched_domain *parent)
static void rq_attach_root(struct rq *rq, struct root_domain *rd)
{
unsigned long flags;
- const struct sched_class *class;
spin_lock_irqsave(&rq->lock, flags);
if (rq->rd) {
struct root_domain *old_rd = rq->rd;
- for (class = sched_class_highest; class; class = class->next) {
- if (class->leave_domain)
- class->leave_domain(rq);
- }
+ if (cpu_isset(rq->cpu, old_rd->online))
+ set_rq_offline(rq);
cpu_clear(rq->cpu, old_rd->span);
- cpu_clear(rq->cpu, old_rd->online);
if (atomic_dec_and_test(&old_rd->refcount))
kfree(old_rd);
@@ -6373,12 +6468,7 @@ static void rq_attach_root(struct rq *rq, struct root_domain *rd)
cpu_set(rq->cpu, rd->span);
if (cpu_isset(rq->cpu, cpu_online_map))
- cpu_set(rq->cpu, rd->online);
-
- for (class = sched_class_highest; class; class = class->next) {
- if (class->join_domain)
- class->join_domain(rq);
- }
+ set_rq_online(rq);
spin_unlock_irqrestore(&rq->lock, flags);
}
@@ -6389,6 +6479,8 @@ static void init_rootdomain(struct root_domain *rd)
cpus_clear(rd->span);
cpus_clear(rd->online);
+
+ cpupri_init(&rd->cpupri);
}
static void init_defrootdomain(void)
@@ -6484,7 +6576,7 @@ init_sched_build_groups(const cpumask_t *span, const cpumask_t *cpu_map,
cpus_clear(*covered);
- for_each_cpu_mask(i, *span) {
+ for_each_cpu_mask_nr(i, *span) {
struct sched_group *sg;
int group = group_fn(i, cpu_map, &sg, tmpmask);
int j;
@@ -6495,7 +6587,7 @@ init_sched_build_groups(const cpumask_t *span, const cpumask_t *cpu_map,
cpus_clear(sg->cpumask);
sg->__cpu_power = 0;
- for_each_cpu_mask(j, *span) {
+ for_each_cpu_mask_nr(j, *span) {
if (group_fn(j, cpu_map, NULL, tmpmask) != group)
continue;
@@ -6531,9 +6623,9 @@ static int find_next_best_node(int node, nodemask_t *used_nodes)
min_val = INT_MAX;
- for (i = 0; i < MAX_NUMNODES; i++) {
+ for (i = 0; i < nr_node_ids; i++) {
/* Start at @node */
- n = (node + i) % MAX_NUMNODES;
+ n = (node + i) % nr_node_ids;
if (!nr_cpus_node(n))
continue;
@@ -6583,7 +6675,7 @@ static void sched_domain_node_span(int node, cpumask_t *span)
cpus_or(*span, *span, *nodemask);
}
}
-#endif
+#endif /* CONFIG_NUMA */
int sched_smt_power_savings = 0, sched_mc_power_savings = 0;
@@ -6602,7 +6694,7 @@ cpu_to_cpu_group(int cpu, const cpumask_t *cpu_map, struct sched_group **sg,
*sg = &per_cpu(sched_group_cpus, cpu);
return cpu;
}
-#endif
+#endif /* CONFIG_SCHED_SMT */
/*
* multi-core sched-domains:
@@ -6610,7 +6702,7 @@ cpu_to_cpu_group(int cpu, const cpumask_t *cpu_map, struct sched_group **sg,
#ifdef CONFIG_SCHED_MC
static DEFINE_PER_CPU(struct sched_domain, core_domains);
static DEFINE_PER_CPU(struct sched_group, sched_group_core);
-#endif
+#endif /* CONFIG_SCHED_MC */
#if defined(CONFIG_SCHED_MC) && defined(CONFIG_SCHED_SMT)
static int
@@ -6695,7 +6787,7 @@ static void init_numa_sched_groups_power(struct sched_group *group_head)
if (!sg)
return;
do {
- for_each_cpu_mask(j, sg->cpumask) {
+ for_each_cpu_mask_nr(j, sg->cpumask) {
struct sched_domain *sd;
sd = &per_cpu(phys_domains, j);
@@ -6712,7 +6804,7 @@ static void init_numa_sched_groups_power(struct sched_group *group_head)
sg = sg->next;
} while (sg != group_head);
}
-#endif
+#endif /* CONFIG_NUMA */
#ifdef CONFIG_NUMA
/* Free memory allocated for various sched_group structures */
@@ -6720,14 +6812,14 @@ static void free_sched_groups(const cpumask_t *cpu_map, cpumask_t *nodemask)
{
int cpu, i;
- for_each_cpu_mask(cpu, *cpu_map) {
+ for_each_cpu_mask_nr(cpu, *cpu_map) {
struct sched_group **sched_group_nodes
= sched_group_nodes_bycpu[cpu];
if (!sched_group_nodes)
continue;
- for (i = 0; i < MAX_NUMNODES; i++) {
+ for (i = 0; i < nr_node_ids; i++) {
struct sched_group *oldsg, *sg = sched_group_nodes[i];
*nodemask = node_to_cpumask(i);
@@ -6749,11 +6841,11 @@ next_sg:
sched_group_nodes_bycpu[cpu] = NULL;
}
}
-#else
+#else /* !CONFIG_NUMA */
static void free_sched_groups(const cpumask_t *cpu_map, cpumask_t *nodemask)
{
}
-#endif
+#endif /* CONFIG_NUMA */
/*
* Initialize sched groups cpu_power.
@@ -6871,7 +6963,12 @@ static int default_relax_domain_level = -1;
static int __init setup_relax_domain_level(char *str)
{
- default_relax_domain_level = simple_strtoul(str, NULL, 0);
+ unsigned long val;
+
+ val = simple_strtoul(str, NULL, 0);
+ if (val < SD_LV_MAX)
+ default_relax_domain_level = val;
+
return 1;
}
__setup("relax_domain_level=", setup_relax_domain_level);
@@ -6915,7 +7012,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map,
/*
* Allocate the per-node list of sched groups
*/
- sched_group_nodes = kcalloc(MAX_NUMNODES, sizeof(struct sched_group *),
+ sched_group_nodes = kcalloc(nr_node_ids, sizeof(struct sched_group *),
GFP_KERNEL);
if (!sched_group_nodes) {
printk(KERN_WARNING "Can not alloc sched group node list\n");
@@ -6954,7 +7051,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map,
/*
* Set up domains for cpus specified by the cpu_map.
*/
- for_each_cpu_mask(i, *cpu_map) {
+ for_each_cpu_mask_nr(i, *cpu_map) {
struct sched_domain *sd = NULL, *p;
SCHED_CPUMASK_VAR(nodemask, allmasks);
@@ -7021,7 +7118,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map,
#ifdef CONFIG_SCHED_SMT
/* Set up CPU (sibling) groups */
- for_each_cpu_mask(i, *cpu_map) {
+ for_each_cpu_mask_nr(i, *cpu_map) {
SCHED_CPUMASK_VAR(this_sibling_map, allmasks);
SCHED_CPUMASK_VAR(send_covered, allmasks);
@@ -7038,7 +7135,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map,
#ifdef CONFIG_SCHED_MC
/* Set up multi-core groups */
- for_each_cpu_mask(i, *cpu_map) {
+ for_each_cpu_mask_nr(i, *cpu_map) {
SCHED_CPUMASK_VAR(this_core_map, allmasks);
SCHED_CPUMASK_VAR(send_covered, allmasks);
@@ -7054,7 +7151,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map,
#endif
/* Set up physical groups */
- for (i = 0; i < MAX_NUMNODES; i++) {
+ for (i = 0; i < nr_node_ids; i++) {
SCHED_CPUMASK_VAR(nodemask, allmasks);
SCHED_CPUMASK_VAR(send_covered, allmasks);
@@ -7078,7 +7175,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map,
send_covered, tmpmask);
}
- for (i = 0; i < MAX_NUMNODES; i++) {
+ for (i = 0; i < nr_node_ids; i++) {
/* Set up node groups */
struct sched_group *sg, *prev;
SCHED_CPUMASK_VAR(nodemask, allmasks);
@@ -7105,7 +7202,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map,
goto error;
}
sched_group_nodes[i] = sg;
- for_each_cpu_mask(j, *nodemask) {
+ for_each_cpu_mask_nr(j, *nodemask) {
struct sched_domain *sd;
sd = &per_cpu(node_domains, j);
@@ -7117,9 +7214,9 @@ static int __build_sched_domains(const cpumask_t *cpu_map,
cpus_or(*covered, *covered, *nodemask);
prev = sg;
- for (j = 0; j < MAX_NUMNODES; j++) {
+ for (j = 0; j < nr_node_ids; j++) {
SCHED_CPUMASK_VAR(notcovered, allmasks);
- int n = (i + j) % MAX_NUMNODES;
+ int n = (i + j) % nr_node_ids;
node_to_cpumask_ptr(pnodemask, n);
cpus_complement(*notcovered, *covered);
@@ -7151,28 +7248,28 @@ static int __build_sched_domains(const cpumask_t *cpu_map,
/* Calculate CPU power for physical packages and nodes */
#ifdef CONFIG_SCHED_SMT
- for_each_cpu_mask(i, *cpu_map) {
+ for_each_cpu_mask_nr(i, *cpu_map) {
struct sched_domain *sd = &per_cpu(cpu_domains, i);
init_sched_groups_power(i, sd);
}
#endif
#ifdef CONFIG_SCHED_MC
- for_each_cpu_mask(i, *cpu_map) {
+ for_each_cpu_mask_nr(i, *cpu_map) {
struct sched_domain *sd = &per_cpu(core_domains, i);
init_sched_groups_power(i, sd);
}
#endif
- for_each_cpu_mask(i, *cpu_map) {
+ for_each_cpu_mask_nr(i, *cpu_map) {
struct sched_domain *sd = &per_cpu(phys_domains, i);
init_sched_groups_power(i, sd);
}
#ifdef CONFIG_NUMA
- for (i = 0; i < MAX_NUMNODES; i++)
+ for (i = 0; i < nr_node_ids; i++)
init_numa_sched_groups_power(sched_group_nodes[i]);
if (sd_allnodes) {
@@ -7185,7 +7282,7 @@ static int __build_sched_domains(const cpumask_t *cpu_map,
#endif
/* Attach the domains */
- for_each_cpu_mask(i, *cpu_map) {
+ for_each_cpu_mask_nr(i, *cpu_map) {
struct sched_domain *sd;
#ifdef CONFIG_SCHED_SMT
sd = &per_cpu(cpu_domains, i);
@@ -7230,6 +7327,18 @@ void __attribute__((weak)) arch_update_cpu_topology(void)
}
/*
+ * Free current domain masks.
+ * Called after all cpus are attached to NULL domain.
+ */
+static void free_sched_domains(void)
+{
+ ndoms_cur = 0;
+ if (doms_cur != &fallback_doms)
+ kfree(doms_cur);
+ doms_cur = &fallback_doms;
+}
+
+/*
* Set up scheduler domains and groups. Callers must hold the hotplug lock.
* For now this just excludes isolated cpus, but could be used to
* exclude other special cases in the future.
@@ -7268,7 +7377,7 @@ static void detach_destroy_domains(const cpumask_t *cpu_map)
unregister_sched_domain_sysctl();
- for_each_cpu_mask(i, *cpu_map)
+ for_each_cpu_mask_nr(i, *cpu_map)
cpu_attach_domain(NULL, &def_root_domain, i);
synchronize_sched();
arch_destroy_sched_domains(cpu_map, &tmpmask);
@@ -7376,6 +7485,7 @@ int arch_reinit_sched_domains(void)
get_online_cpus();
mutex_lock(&sched_domains_mutex);
detach_destroy_domains(&cpu_online_map);
+ free_sched_domains();
err = arch_init_sched_domains(&cpu_online_map);
mutex_unlock(&sched_domains_mutex);
put_online_cpus();
@@ -7444,7 +7554,7 @@ int sched_create_sysfs_power_savings_entries(struct sysdev_class *cls)
#endif
return err;
}
-#endif
+#endif /* CONFIG_SCHED_MC || CONFIG_SCHED_SMT */
/*
* Force a reinitialization of the sched domains hierarchy. The domains
@@ -7455,20 +7565,28 @@ int sched_create_sysfs_power_savings_entries(struct sysdev_class *cls)
static int update_sched_domains(struct notifier_block *nfb,
unsigned long action, void *hcpu)
{
+ int cpu = (int)(long)hcpu;
+
switch (action) {
- case CPU_UP_PREPARE:
- case CPU_UP_PREPARE_FROZEN:
case CPU_DOWN_PREPARE:
case CPU_DOWN_PREPARE_FROZEN:
+ disable_runtime(cpu_rq(cpu));
+ /* fall-through */
+ case CPU_UP_PREPARE:
+ case CPU_UP_PREPARE_FROZEN:
detach_destroy_domains(&cpu_online_map);
+ free_sched_domains();
return NOTIFY_OK;
- case CPU_UP_CANCELED:
- case CPU_UP_CANCELED_FROZEN:
+
case CPU_DOWN_FAILED:
case CPU_DOWN_FAILED_FROZEN:
case CPU_ONLINE:
case CPU_ONLINE_FROZEN:
+ enable_runtime(cpu_rq(cpu));
+ /* fall-through */
+ case CPU_UP_CANCELED:
+ case CPU_UP_CANCELED_FROZEN:
case CPU_DEAD:
case CPU_DEAD_FROZEN:
/*
@@ -7479,8 +7597,16 @@ static int update_sched_domains(struct notifier_block *nfb,
return NOTIFY_DONE;
}
+#ifndef CONFIG_CPUSETS
+ /*
+ * Create default domain partitioning if cpusets are disabled.
+ * Otherwise we let cpusets rebuild the domains based on the
+ * current setup.
+ */
+
/* The hotplug lock is already held by cpu_up/cpu_down */
arch_init_sched_domains(&cpu_online_map);
+#endif
return NOTIFY_OK;
}
@@ -7542,7 +7668,8 @@ static void init_rt_rq(struct rt_rq *rt_rq, struct rq *rq)
array = &rt_rq->active;
for (i = 0; i < MAX_RT_PRIO; i++) {
- INIT_LIST_HEAD(array->queue + i);
+ INIT_LIST_HEAD(array->xqueue + i);
+ INIT_LIST_HEAD(array->squeue + i);
__clear_bit(i, array->bitmap);
}
/* delimiter for bitsearch: */
@@ -7661,8 +7788,8 @@ void __init sched_init(void)
root_task_group.cfs_rq = (struct cfs_rq **)ptr;
ptr += nr_cpu_ids * sizeof(void **);
-#endif
-#endif
+#endif /* CONFIG_USER_SCHED */
+#endif /* CONFIG_FAIR_GROUP_SCHED */
#ifdef CONFIG_RT_GROUP_SCHED
init_task_group.rt_se = (struct sched_rt_entity **)ptr;
ptr += nr_cpu_ids * sizeof(void **);
@@ -7676,8 +7803,8 @@ void __init sched_init(void)
root_task_group.rt_rq = (struct rt_rq **)ptr;
ptr += nr_cpu_ids * sizeof(void **);
-#endif
-#endif
+#endif /* CONFIG_USER_SCHED */
+#endif /* CONFIG_RT_GROUP_SCHED */
}
#ifdef CONFIG_SMP
@@ -7693,8 +7820,8 @@ void __init sched_init(void)
#ifdef CONFIG_USER_SCHED
init_rt_bandwidth(&root_task_group.rt_bandwidth,
global_rt_period(), RUNTIME_INF);
-#endif
-#endif
+#endif /* CONFIG_USER_SCHED */
+#endif /* CONFIG_RT_GROUP_SCHED */
#ifdef CONFIG_GROUP_SCHED
list_add(&init_task_group.list, &task_groups);
@@ -7704,8 +7831,8 @@ void __init sched_init(void)
INIT_LIST_HEAD(&root_task_group.children);
init_task_group.parent = &root_task_group;
list_add(&init_task_group.siblings, &root_task_group.children);
-#endif
-#endif
+#endif /* CONFIG_USER_SCHED */
+#endif /* CONFIG_GROUP_SCHED */
for_each_possible_cpu(i) {
struct rq *rq;
@@ -7785,6 +7912,7 @@ void __init sched_init(void)
rq->next_balance = jiffies;
rq->push_cpu = 0;
rq->cpu = i;
+ rq->online = 0;
rq->migration_thread = NULL;
INIT_LIST_HEAD(&rq->migration_queue);
rq_attach_root(rq, &def_root_domain);
@@ -7800,7 +7928,7 @@ void __init sched_init(void)
#endif
#ifdef CONFIG_SMP
- open_softirq(SCHED_SOFTIRQ, run_rebalance_domains, NULL);
+ open_softirq(SCHED_SOFTIRQ, run_rebalance_domains);
#endif
#ifdef CONFIG_RT_MUTEXES
@@ -8024,7 +8152,7 @@ static inline void unregister_fair_sched_group(struct task_group *tg, int cpu)
{
list_del_rcu(&tg->cfs_rq[cpu]->leaf_cfs_rq_list);
}
-#else
+#else /* !CONFG_FAIR_GROUP_SCHED */
static inline void free_fair_sched_group(struct task_group *tg)
{
}
@@ -8042,7 +8170,7 @@ static inline void register_fair_sched_group(struct task_group *tg, int cpu)
static inline void unregister_fair_sched_group(struct task_group *tg, int cpu)
{
}
-#endif
+#endif /* CONFIG_FAIR_GROUP_SCHED */
#ifdef CONFIG_RT_GROUP_SCHED
static void free_rt_sched_group(struct task_group *tg)
@@ -8113,7 +8241,7 @@ static inline void unregister_rt_sched_group(struct task_group *tg, int cpu)
{
list_del_rcu(&tg->rt_rq[cpu]->leaf_rt_rq_list);
}
-#else
+#else /* !CONFIG_RT_GROUP_SCHED */
static inline void free_rt_sched_group(struct task_group *tg)
{
}
@@ -8131,7 +8259,7 @@ static inline void register_rt_sched_group(struct task_group *tg, int cpu)
static inline void unregister_rt_sched_group(struct task_group *tg, int cpu)
{
}
-#endif
+#endif /* CONFIG_RT_GROUP_SCHED */
#ifdef CONFIG_GROUP_SCHED
static void free_sched_group(struct task_group *tg)
@@ -8242,7 +8370,7 @@ void sched_move_task(struct task_struct *tsk)
task_rq_unlock(rq, &flags);
}
-#endif
+#endif /* CONFIG_GROUP_SCHED */
#ifdef CONFIG_FAIR_GROUP_SCHED
static void set_se_shares(struct sched_entity *se, unsigned long shares)
@@ -8492,7 +8620,7 @@ static int sched_rt_global_constraints(void)
return ret;
}
-#else
+#else /* !CONFIG_RT_GROUP_SCHED */
static int sched_rt_global_constraints(void)
{
unsigned long flags;
@@ -8510,7 +8638,7 @@ static int sched_rt_global_constraints(void)
return 0;
}
-#endif
+#endif /* CONFIG_RT_GROUP_SCHED */
int sched_rt_handler(struct ctl_table *table, int write,
struct file *filp, void __user *buffer, size_t *lenp,
@@ -8618,7 +8746,7 @@ static u64 cpu_shares_read_u64(struct cgroup *cgrp, struct cftype *cft)
return (u64) tg->shares;
}
-#endif
+#endif /* CONFIG_FAIR_GROUP_SCHED */
#ifdef CONFIG_RT_GROUP_SCHED
static int cpu_rt_runtime_write(struct cgroup *cgrp, struct cftype *cft,
@@ -8642,7 +8770,7 @@ static u64 cpu_rt_period_read_uint(struct cgroup *cgrp, struct cftype *cft)
{
return sched_group_rt_period(cgroup_tg(cgrp));
}
-#endif
+#endif /* CONFIG_RT_GROUP_SCHED */
static struct cftype cpu_files[] = {
#ifdef CONFIG_FAIR_GROUP_SCHED
diff --git a/kernel/sched_cpupri.c b/kernel/sched_cpupri.c
new file mode 100644
index 000000000000..52154fefab7e
--- /dev/null
+++ b/kernel/sched_cpupri.c
@@ -0,0 +1,174 @@
+/*
+ * kernel/sched_cpupri.c
+ *
+ * CPU priority management
+ *
+ * Copyright (C) 2007-2008 Novell
+ *
+ * Author: Gregory Haskins <ghaskins@novell.com>
+ *
+ * This code tracks the priority of each CPU so that global migration
+ * decisions are easy to calculate. Each CPU can be in a state as follows:
+ *
+ * (INVALID), IDLE, NORMAL, RT1, ... RT99
+ *
+ * going from the lowest priority to the highest. CPUs in the INVALID state
+ * are not eligible for routing. The system maintains this state with
+ * a 2 dimensional bitmap (the first for priority class, the second for cpus
+ * in that class). Therefore a typical application without affinity
+ * restrictions can find a suitable CPU with O(1) complexity (e.g. two bit
+ * searches). For tasks with affinity restrictions, the algorithm has a
+ * worst case complexity of O(min(102, nr_domcpus)), though the scenario that
+ * yields the worst case search is fairly contrived.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#include "sched_cpupri.h"
+
+/* Convert between a 140 based task->prio, and our 102 based cpupri */
+static int convert_prio(int prio)
+{
+ int cpupri;
+
+ if (prio == CPUPRI_INVALID)
+ cpupri = CPUPRI_INVALID;
+ else if (prio == MAX_PRIO)
+ cpupri = CPUPRI_IDLE;
+ else if (prio >= MAX_RT_PRIO)
+ cpupri = CPUPRI_NORMAL;
+ else
+ cpupri = MAX_RT_PRIO - prio + 1;
+
+ return cpupri;
+}
+
+#define for_each_cpupri_active(array, idx) \
+ for (idx = find_first_bit(array, CPUPRI_NR_PRIORITIES); \
+ idx < CPUPRI_NR_PRIORITIES; \
+ idx = find_next_bit(array, CPUPRI_NR_PRIORITIES, idx+1))
+
+/**
+ * cpupri_find - find the best (lowest-pri) CPU in the system
+ * @cp: The cpupri context
+ * @p: The task
+ * @lowest_mask: A mask to fill in with selected CPUs
+ *
+ * Note: This function returns the recommended CPUs as calculated during the
+ * current invokation. By the time the call returns, the CPUs may have in
+ * fact changed priorities any number of times. While not ideal, it is not
+ * an issue of correctness since the normal rebalancer logic will correct
+ * any discrepancies created by racing against the uncertainty of the current
+ * priority configuration.
+ *
+ * Returns: (int)bool - CPUs were found
+ */
+int cpupri_find(struct cpupri *cp, struct task_struct *p,
+ cpumask_t *lowest_mask)
+{
+ int idx = 0;
+ int task_pri = convert_prio(p->prio);
+
+ for_each_cpupri_active(cp->pri_active, idx) {
+ struct cpupri_vec *vec = &cp->pri_to_cpu[idx];
+ cpumask_t mask;
+
+ if (idx >= task_pri)
+ break;
+
+ cpus_and(mask, p->cpus_allowed, vec->mask);
+
+ if (cpus_empty(mask))
+ continue;
+
+ *lowest_mask = mask;
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * cpupri_set - update the cpu priority setting
+ * @cp: The cpupri context
+ * @cpu: The target cpu
+ * @pri: The priority (INVALID-RT99) to assign to this CPU
+ *
+ * Note: Assumes cpu_rq(cpu)->lock is locked
+ *
+ * Returns: (void)
+ */
+void cpupri_set(struct cpupri *cp, int cpu, int newpri)
+{
+ int *currpri = &cp->cpu_to_pri[cpu];
+ int oldpri = *currpri;
+ unsigned long flags;
+
+ newpri = convert_prio(newpri);
+
+ BUG_ON(newpri >= CPUPRI_NR_PRIORITIES);
+
+ if (newpri == oldpri)
+ return;
+
+ /*
+ * If the cpu was currently mapped to a different value, we
+ * first need to unmap the old value
+ */
+ if (likely(oldpri != CPUPRI_INVALID)) {
+ struct cpupri_vec *vec = &cp->pri_to_cpu[oldpri];
+
+ spin_lock_irqsave(&vec->lock, flags);
+
+ vec->count--;
+ if (!vec->count)
+ clear_bit(oldpri, cp->pri_active);
+ cpu_clear(cpu, vec->mask);
+
+ spin_unlock_irqrestore(&vec->lock, flags);
+ }
+
+ if (likely(newpri != CPUPRI_INVALID)) {
+ struct cpupri_vec *vec = &cp->pri_to_cpu[newpri];
+
+ spin_lock_irqsave(&vec->lock, flags);
+
+ cpu_set(cpu, vec->mask);
+ vec->count++;
+ if (vec->count == 1)
+ set_bit(newpri, cp->pri_active);
+
+ spin_unlock_irqrestore(&vec->lock, flags);
+ }
+
+ *currpri = newpri;
+}
+
+/**
+ * cpupri_init - initialize the cpupri structure
+ * @cp: The cpupri context
+ *
+ * Returns: (void)
+ */
+void cpupri_init(struct cpupri *cp)
+{
+ int i;
+
+ memset(cp, 0, sizeof(*cp));
+
+ for (i = 0; i < CPUPRI_NR_PRIORITIES; i++) {
+ struct cpupri_vec *vec = &cp->pri_to_cpu[i];
+
+ spin_lock_init(&vec->lock);
+ vec->count = 0;
+ cpus_clear(vec->mask);
+ }
+
+ for_each_possible_cpu(i)
+ cp->cpu_to_pri[i] = CPUPRI_INVALID;
+}
+
+
diff --git a/kernel/sched_cpupri.h b/kernel/sched_cpupri.h
new file mode 100644
index 000000000000..f25811b0f931
--- /dev/null
+++ b/kernel/sched_cpupri.h
@@ -0,0 +1,36 @@
+#ifndef _LINUX_CPUPRI_H
+#define _LINUX_CPUPRI_H
+
+#include <linux/sched.h>
+
+#define CPUPRI_NR_PRIORITIES (MAX_RT_PRIO + 2)
+#define CPUPRI_NR_PRI_WORDS BITS_TO_LONGS(CPUPRI_NR_PRIORITIES)
+
+#define CPUPRI_INVALID -1
+#define CPUPRI_IDLE 0
+#define CPUPRI_NORMAL 1
+/* values 2-101 are RT priorities 0-99 */
+
+struct cpupri_vec {
+ spinlock_t lock;
+ int count;
+ cpumask_t mask;
+};
+
+struct cpupri {
+ struct cpupri_vec pri_to_cpu[CPUPRI_NR_PRIORITIES];
+ long pri_active[CPUPRI_NR_PRI_WORDS];
+ int cpu_to_pri[NR_CPUS];
+};
+
+#ifdef CONFIG_SMP
+int cpupri_find(struct cpupri *cp,
+ struct task_struct *p, cpumask_t *lowest_mask);
+void cpupri_set(struct cpupri *cp, int cpu, int pri);
+void cpupri_init(struct cpupri *cp);
+#else
+#define cpupri_set(cp, cpu, pri) do { } while (0)
+#define cpupri_init() do { } while (0)
+#endif
+
+#endif /* _LINUX_CPUPRI_H */
diff --git a/kernel/sched_fair.c b/kernel/sched_fair.c
index 08ae848b71d4..0152dbd0b77a 100644
--- a/kernel/sched_fair.c
+++ b/kernel/sched_fair.c
@@ -961,7 +961,7 @@ static int wake_idle(int cpu, struct task_struct *p)
|| ((sd->flags & SD_WAKE_IDLE_FAR)
&& !task_hot(p, task_rq(p)->clock, sd))) {
cpus_and(tmp, sd->span, p->cpus_allowed);
- for_each_cpu_mask(i, tmp) {
+ for_each_cpu_mask_nr(i, tmp) {
if (idle_cpu(i)) {
if (i != task_cpu(p)) {
schedstat_inc(p,
@@ -1275,23 +1275,18 @@ __load_balance_iterator(struct cfs_rq *cfs_rq, struct list_head *next)
struct task_struct *p = NULL;
struct sched_entity *se;
- if (next == &cfs_rq->tasks)
- return NULL;
-
- /* Skip over entities that are not tasks */
- do {
+ while (next != &cfs_rq->tasks) {
se = list_entry(next, struct sched_entity, group_node);
next = next->next;
- } while (next != &cfs_rq->tasks && !entity_is_task(se));
- if (next == &cfs_rq->tasks)
- return NULL;
+ /* Skip over entities that are not tasks */
+ if (entity_is_task(se)) {
+ p = task_of(se);
+ break;
+ }
+ }
cfs_rq->balance_iterator = next;
-
- if (entity_is_task(se))
- p = task_of(se);
-
return p;
}
diff --git a/kernel/sched_features.h b/kernel/sched_features.h
index 1c7283cb9581..62b39ca92ebd 100644
--- a/kernel/sched_features.h
+++ b/kernel/sched_features.h
@@ -6,5 +6,3 @@ SCHED_FEAT(CACHE_HOT_BUDDY, 1)
SCHED_FEAT(SYNC_WAKEUPS, 1)
SCHED_FEAT(HRTICK, 1)
SCHED_FEAT(DOUBLE_TICK, 0)
-SCHED_FEAT(NORMALIZED_SLEEPER, 1)
-SCHED_FEAT(DEADLINE, 1)
diff --git a/kernel/sched_rt.c b/kernel/sched_rt.c
index 3432d573205d..49a225e2729e 100644
--- a/kernel/sched_rt.c
+++ b/kernel/sched_rt.c
@@ -12,6 +12,9 @@ static inline int rt_overloaded(struct rq *rq)
static inline void rt_set_overload(struct rq *rq)
{
+ if (!rq->online)
+ return;
+
cpu_set(rq->cpu, rq->rd->rto_mask);
/*
* Make sure the mask is visible before we set
@@ -26,6 +29,9 @@ static inline void rt_set_overload(struct rq *rq)
static inline void rt_clear_overload(struct rq *rq)
{
+ if (!rq->online)
+ return;
+
/* the order here really doesn't matter */
atomic_dec(&rq->rd->rto_count);
cpu_clear(rq->cpu, rq->rd->rto_mask);
@@ -231,7 +237,7 @@ static int do_sched_rt_period_timer(struct rt_bandwidth *rt_b, int overrun)
return 1;
span = sched_rt_period_mask();
- for_each_cpu_mask(i, span) {
+ for_each_cpu_mask_nr(i, span) {
int enqueue = 0;
struct rt_rq *rt_rq = sched_rt_period_rt_rq(rt_b, i);
struct rq *rq = rq_of_rt_rq(rt_rq);
@@ -272,7 +278,7 @@ static int balance_runtime(struct rt_rq *rt_rq)
spin_lock(&rt_b->rt_runtime_lock);
rt_period = ktime_to_ns(rt_b->rt_period);
- for_each_cpu_mask(i, rd->span) {
+ for_each_cpu_mask_nr(i, rd->span) {
struct rt_rq *iter = sched_rt_period_rt_rq(rt_b, i);
s64 diff;
@@ -280,6 +286,9 @@ static int balance_runtime(struct rt_rq *rt_rq)
continue;
spin_lock(&iter->rt_runtime_lock);
+ if (iter->rt_runtime == RUNTIME_INF)
+ goto next;
+
diff = iter->rt_runtime - iter->rt_time;
if (diff > 0) {
do_div(diff, weight);
@@ -293,12 +302,105 @@ static int balance_runtime(struct rt_rq *rt_rq)
break;
}
}
+next:
spin_unlock(&iter->rt_runtime_lock);
}
spin_unlock(&rt_b->rt_runtime_lock);
return more;
}
+
+static void __disable_runtime(struct rq *rq)
+{
+ struct root_domain *rd = rq->rd;
+ struct rt_rq *rt_rq;
+
+ if (unlikely(!scheduler_running))
+ return;
+
+ for_each_leaf_rt_rq(rt_rq, rq) {
+ struct rt_bandwidth *rt_b = sched_rt_bandwidth(rt_rq);
+ s64 want;
+ int i;
+
+ spin_lock(&rt_b->rt_runtime_lock);
+ spin_lock(&rt_rq->rt_runtime_lock);
+ if (rt_rq->rt_runtime == RUNTIME_INF ||
+ rt_rq->rt_runtime == rt_b->rt_runtime)
+ goto balanced;
+ spin_unlock(&rt_rq->rt_runtime_lock);
+
+ want = rt_b->rt_runtime - rt_rq->rt_runtime;
+
+ for_each_cpu_mask(i, rd->span) {
+ struct rt_rq *iter = sched_rt_period_rt_rq(rt_b, i);
+ s64 diff;
+
+ if (iter == rt_rq)
+ continue;
+
+ spin_lock(&iter->rt_runtime_lock);
+ if (want > 0) {
+ diff = min_t(s64, iter->rt_runtime, want);
+ iter->rt_runtime -= diff;
+ want -= diff;
+ } else {
+ iter->rt_runtime -= want;
+ want -= want;
+ }
+ spin_unlock(&iter->rt_runtime_lock);
+
+ if (!want)
+ break;
+ }
+
+ spin_lock(&rt_rq->rt_runtime_lock);
+ BUG_ON(want);
+balanced:
+ rt_rq->rt_runtime = RUNTIME_INF;
+ spin_unlock(&rt_rq->rt_runtime_lock);
+ spin_unlock(&rt_b->rt_runtime_lock);
+ }
+}
+
+static void disable_runtime(struct rq *rq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&rq->lock, flags);
+ __disable_runtime(rq);
+ spin_unlock_irqrestore(&rq->lock, flags);
+}
+
+static void __enable_runtime(struct rq *rq)
+{
+ struct root_domain *rd = rq->rd;
+ struct rt_rq *rt_rq;
+
+ if (unlikely(!scheduler_running))
+ return;
+
+ for_each_leaf_rt_rq(rt_rq, rq) {
+ struct rt_bandwidth *rt_b = sched_rt_bandwidth(rt_rq);
+
+ spin_lock(&rt_b->rt_runtime_lock);
+ spin_lock(&rt_rq->rt_runtime_lock);
+ rt_rq->rt_runtime = rt_b->rt_runtime;
+ rt_rq->rt_time = 0;
+ spin_unlock(&rt_rq->rt_runtime_lock);
+ spin_unlock(&rt_b->rt_runtime_lock);
+ }
+}
+
+static void enable_runtime(struct rq *rq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&rq->lock, flags);
+ __enable_runtime(rq);
+ spin_unlock_irqrestore(&rq->lock, flags);
+}
+
#endif
static inline int rt_se_prio(struct sched_rt_entity *rt_se)
@@ -328,14 +430,13 @@ static int sched_rt_runtime_exceeded(struct rt_rq *rt_rq)
#ifdef CONFIG_SMP
if (rt_rq->rt_time > runtime) {
- int more;
-
spin_unlock(&rt_rq->rt_runtime_lock);
- more = balance_runtime(rt_rq);
+ balance_runtime(rt_rq);
spin_lock(&rt_rq->rt_runtime_lock);
- if (more)
- runtime = sched_rt_runtime(rt_rq);
+ runtime = sched_rt_runtime(rt_rq);
+ if (runtime == RUNTIME_INF)
+ return 0;
}
#endif
@@ -391,12 +492,21 @@ void inc_rt_tasks(struct sched_rt_entity *rt_se, struct rt_rq *rt_rq)
WARN_ON(!rt_prio(rt_se_prio(rt_se)));
rt_rq->rt_nr_running++;
#if defined CONFIG_SMP || defined CONFIG_RT_GROUP_SCHED
- if (rt_se_prio(rt_se) < rt_rq->highest_prio)
+ if (rt_se_prio(rt_se) < rt_rq->highest_prio) {
+ struct rq *rq = rq_of_rt_rq(rt_rq);
+
rt_rq->highest_prio = rt_se_prio(rt_se);
+#ifdef CONFIG_SMP
+ if (rq->online)
+ cpupri_set(&rq->rd->cpupri, rq->cpu,
+ rt_se_prio(rt_se));
+#endif
+ }
#endif
#ifdef CONFIG_SMP
if (rt_se->nr_cpus_allowed > 1) {
struct rq *rq = rq_of_rt_rq(rt_rq);
+
rq->rt.rt_nr_migratory++;
}
@@ -416,6 +526,10 @@ void inc_rt_tasks(struct sched_rt_entity *rt_se, struct rt_rq *rt_rq)
static inline
void dec_rt_tasks(struct sched_rt_entity *rt_se, struct rt_rq *rt_rq)
{
+#ifdef CONFIG_SMP
+ int highest_prio = rt_rq->highest_prio;
+#endif
+
WARN_ON(!rt_prio(rt_se_prio(rt_se)));
WARN_ON(!rt_rq->rt_nr_running);
rt_rq->rt_nr_running--;
@@ -439,6 +553,14 @@ void dec_rt_tasks(struct sched_rt_entity *rt_se, struct rt_rq *rt_rq)
rq->rt.rt_nr_migratory--;
}
+ if (rt_rq->highest_prio != highest_prio) {
+ struct rq *rq = rq_of_rt_rq(rt_rq);
+
+ if (rq->online)
+ cpupri_set(&rq->rd->cpupri, rq->cpu,
+ rt_rq->highest_prio);
+ }
+
update_rt_migration(rq_of_rt_rq(rt_rq));
#endif /* CONFIG_SMP */
#ifdef CONFIG_RT_GROUP_SCHED
@@ -458,7 +580,13 @@ static void enqueue_rt_entity(struct sched_rt_entity *rt_se)
if (group_rq && rt_rq_throttled(group_rq))
return;
- list_add_tail(&rt_se->run_list, array->queue + rt_se_prio(rt_se));
+ if (rt_se->nr_cpus_allowed == 1)
+ list_add_tail(&rt_se->run_list,
+ array->xqueue + rt_se_prio(rt_se));
+ else
+ list_add_tail(&rt_se->run_list,
+ array->squeue + rt_se_prio(rt_se));
+
__set_bit(rt_se_prio(rt_se), array->bitmap);
inc_rt_tasks(rt_se, rt_rq);
@@ -470,7 +598,8 @@ static void dequeue_rt_entity(struct sched_rt_entity *rt_se)
struct rt_prio_array *array = &rt_rq->active;
list_del_init(&rt_se->run_list);
- if (list_empty(array->queue + rt_se_prio(rt_se)))
+ if (list_empty(array->squeue + rt_se_prio(rt_se))
+ && list_empty(array->xqueue + rt_se_prio(rt_se)))
__clear_bit(rt_se_prio(rt_se), array->bitmap);
dec_rt_tasks(rt_se, rt_rq);
@@ -537,13 +666,19 @@ static void dequeue_task_rt(struct rq *rq, struct task_struct *p, int sleep)
/*
* Put task to the end of the run list without the overhead of dequeue
* followed by enqueue.
+ *
+ * Note: We always enqueue the task to the shared-queue, regardless of its
+ * previous position w.r.t. exclusive vs shared. This is so that exclusive RR
+ * tasks fairly round-robin with all tasks on the runqueue, not just other
+ * exclusive tasks.
*/
static
void requeue_rt_entity(struct rt_rq *rt_rq, struct sched_rt_entity *rt_se)
{
struct rt_prio_array *array = &rt_rq->active;
- list_move_tail(&rt_se->run_list, array->queue + rt_se_prio(rt_se));
+ list_del_init(&rt_se->run_list);
+ list_add_tail(&rt_se->run_list, array->squeue + rt_se_prio(rt_se));
}
static void requeue_task_rt(struct rq *rq, struct task_struct *p)
@@ -601,13 +736,46 @@ static int select_task_rq_rt(struct task_struct *p, int sync)
}
#endif /* CONFIG_SMP */
+static struct sched_rt_entity *pick_next_rt_entity(struct rq *rq,
+ struct rt_rq *rt_rq);
+
/*
* Preempt the current task with a newly woken task if needed:
*/
static void check_preempt_curr_rt(struct rq *rq, struct task_struct *p)
{
- if (p->prio < rq->curr->prio)
+ if (p->prio < rq->curr->prio) {
resched_task(rq->curr);
+ return;
+ }
+
+#ifdef CONFIG_SMP
+ /*
+ * If:
+ *
+ * - the newly woken task is of equal priority to the current task
+ * - the newly woken task is non-migratable while current is migratable
+ * - current will be preempted on the next reschedule
+ *
+ * we should check to see if current can readily move to a different
+ * cpu. If so, we will reschedule to allow the push logic to try
+ * to move current somewhere else, making room for our non-migratable
+ * task.
+ */
+ if((p->prio == rq->curr->prio)
+ && p->rt.nr_cpus_allowed == 1
+ && rq->curr->rt.nr_cpus_allowed != 1
+ && pick_next_rt_entity(rq, &rq->rt) != &rq->curr->rt) {
+ cpumask_t mask;
+
+ if (cpupri_find(&rq->rd->cpupri, rq->curr, &mask))
+ /*
+ * There appears to be other cpus that can accept
+ * current, so lets reschedule to try and push it away
+ */
+ resched_task(rq->curr);
+ }
+#endif
}
static struct sched_rt_entity *pick_next_rt_entity(struct rq *rq,
@@ -621,8 +789,15 @@ static struct sched_rt_entity *pick_next_rt_entity(struct rq *rq,
idx = sched_find_first_bit(array->bitmap);
BUG_ON(idx >= MAX_RT_PRIO);
- queue = array->queue + idx;
- next = list_entry(queue->next, struct sched_rt_entity, run_list);
+ queue = array->xqueue + idx;
+ if (!list_empty(queue))
+ next = list_entry(queue->next, struct sched_rt_entity,
+ run_list);
+ else {
+ queue = array->squeue + idx;
+ next = list_entry(queue->next, struct sched_rt_entity,
+ run_list);
+ }
return next;
}
@@ -692,7 +867,7 @@ static struct task_struct *pick_next_highest_task_rt(struct rq *rq, int cpu)
continue;
if (next && next->prio < idx)
continue;
- list_for_each_entry(rt_se, array->queue + idx, run_list) {
+ list_for_each_entry(rt_se, array->squeue + idx, run_list) {
struct task_struct *p = rt_task_of(rt_se);
if (pick_rt_task(rq, p, cpu)) {
next = p;
@@ -710,73 +885,6 @@ static struct task_struct *pick_next_highest_task_rt(struct rq *rq, int cpu)
static DEFINE_PER_CPU(cpumask_t, local_cpu_mask);
-static int find_lowest_cpus(struct task_struct *task, cpumask_t *lowest_mask)
-{
- int lowest_prio = -1;
- int lowest_cpu = -1;
- int count = 0;
- int cpu;
-
- cpus_and(*lowest_mask, task_rq(task)->rd->online, task->cpus_allowed);
-
- /*
- * Scan each rq for the lowest prio.
- */
- for_each_cpu_mask(cpu, *lowest_mask) {
- struct rq *rq = cpu_rq(cpu);
-
- /* We look for lowest RT prio or non-rt CPU */
- if (rq->rt.highest_prio >= MAX_RT_PRIO) {
- /*
- * if we already found a low RT queue
- * and now we found this non-rt queue
- * clear the mask and set our bit.
- * Otherwise just return the queue as is
- * and the count==1 will cause the algorithm
- * to use the first bit found.
- */
- if (lowest_cpu != -1) {
- cpus_clear(*lowest_mask);
- cpu_set(rq->cpu, *lowest_mask);
- }
- return 1;
- }
-
- /* no locking for now */
- if ((rq->rt.highest_prio > task->prio)
- && (rq->rt.highest_prio >= lowest_prio)) {
- if (rq->rt.highest_prio > lowest_prio) {
- /* new low - clear old data */
- lowest_prio = rq->rt.highest_prio;
- lowest_cpu = cpu;
- count = 0;
- }
- count++;
- } else
- cpu_clear(cpu, *lowest_mask);
- }
-
- /*
- * Clear out all the set bits that represent
- * runqueues that were of higher prio than
- * the lowest_prio.
- */
- if (lowest_cpu > 0) {
- /*
- * Perhaps we could add another cpumask op to
- * zero out bits. Like cpu_zero_bits(cpumask, nrbits);
- * Then that could be optimized to use memset and such.
- */
- for_each_cpu_mask(cpu, *lowest_mask) {
- if (cpu >= lowest_cpu)
- break;
- cpu_clear(cpu, *lowest_mask);
- }
- }
-
- return count;
-}
-
static inline int pick_optimal_cpu(int this_cpu, cpumask_t *mask)
{
int first;
@@ -798,17 +906,12 @@ static int find_lowest_rq(struct task_struct *task)
cpumask_t *lowest_mask = &__get_cpu_var(local_cpu_mask);
int this_cpu = smp_processor_id();
int cpu = task_cpu(task);
- int count = find_lowest_cpus(task, lowest_mask);
- if (!count)
- return -1; /* No targets found */
+ if (task->rt.nr_cpus_allowed == 1)
+ return -1; /* No other targets possible */
- /*
- * There is no sense in performing an optimal search if only one
- * target is found.
- */
- if (count == 1)
- return first_cpu(*lowest_mask);
+ if (!cpupri_find(&task_rq(task)->rd->cpupri, task, lowest_mask))
+ return -1; /* No targets found */
/*
* At this point we have built a mask of cpus representing the
@@ -996,7 +1099,7 @@ static int pull_rt_task(struct rq *this_rq)
next = pick_next_task_rt(this_rq);
- for_each_cpu_mask(cpu, this_rq->rd->rto_mask) {
+ for_each_cpu_mask_nr(cpu, this_rq->rd->rto_mask) {
if (this_cpu == cpu)
continue;
@@ -1146,6 +1249,14 @@ static void set_cpus_allowed_rt(struct task_struct *p,
}
update_rt_migration(rq);
+
+ if (unlikely(weight == 1 || p->rt.nr_cpus_allowed == 1))
+ /*
+ * If either the new or old weight is a "1", we need
+ * to requeue to properly move between shared and
+ * exclusive queues.
+ */
+ requeue_task_rt(rq, p);
}
p->cpus_allowed = *new_mask;
@@ -1153,17 +1264,25 @@ static void set_cpus_allowed_rt(struct task_struct *p,
}
/* Assumes rq->lock is held */
-static void join_domain_rt(struct rq *rq)
+static void rq_online_rt(struct rq *rq)
{
if (rq->rt.overloaded)
rt_set_overload(rq);
+
+ __enable_runtime(rq);
+
+ cpupri_set(&rq->rd->cpupri, rq->cpu, rq->rt.highest_prio);
}
/* Assumes rq->lock is held */
-static void leave_domain_rt(struct rq *rq)
+static void rq_offline_rt(struct rq *rq)
{
if (rq->rt.overloaded)
rt_clear_overload(rq);
+
+ __disable_runtime(rq);
+
+ cpupri_set(&rq->rd->cpupri, rq->cpu, CPUPRI_INVALID);
}
/*
@@ -1326,8 +1445,8 @@ static const struct sched_class rt_sched_class = {
.load_balance = load_balance_rt,
.move_one_task = move_one_task_rt,
.set_cpus_allowed = set_cpus_allowed_rt,
- .join_domain = join_domain_rt,
- .leave_domain = leave_domain_rt,
+ .rq_online = rq_online_rt,
+ .rq_offline = rq_offline_rt,
.pre_schedule = pre_schedule_rt,
.post_schedule = post_schedule_rt,
.task_wake_up = task_wake_up_rt,
diff --git a/kernel/sched_trace.h b/kernel/sched_trace.h
new file mode 100644
index 000000000000..29b48f34fd02
--- /dev/null
+++ b/kernel/sched_trace.h
@@ -0,0 +1,41 @@
+#include <linux/marker.h>
+
+static inline void trace_kernel_sched_wait(struct task_struct *p)
+{
+ trace_mark(kernel_sched_wait_task, "pid %d state %ld",
+ p->pid, p->state);
+}
+
+static inline
+void trace_kernel_sched_wakeup(struct rq *rq, struct task_struct *p)
+{
+ trace_mark(kernel_sched_wakeup,
+ "pid %d state %ld ## rq %p task %p rq->curr %p",
+ p->pid, p->state, rq, p, rq->curr);
+}
+
+static inline
+void trace_kernel_sched_wakeup_new(struct rq *rq, struct task_struct *p)
+{
+ trace_mark(kernel_sched_wakeup_new,
+ "pid %d state %ld ## rq %p task %p rq->curr %p",
+ p->pid, p->state, rq, p, rq->curr);
+}
+
+static inline void trace_kernel_sched_switch(struct rq *rq,
+ struct task_struct *prev, struct task_struct *next)
+{
+ trace_mark(kernel_sched_schedule,
+ "prev_pid %d next_pid %d prev_state %ld "
+ "## rq %p prev %p next %p",
+ prev->pid, next->pid, prev->state,
+ rq, prev, next);
+}
+
+static inline void
+trace_kernel_sched_migrate_task(struct task_struct *p, int src, int dst)
+{
+ trace_mark(kernel_sched_migrate_task,
+ "pid %d state %ld dest_cpu %d",
+ p->pid, p->state, dst);
+}
diff --git a/kernel/semaphore.c b/kernel/semaphore.c
index 5c2942e768cd..1a064adab658 100644
--- a/kernel/semaphore.c
+++ b/kernel/semaphore.c
@@ -31,6 +31,7 @@
#include <linux/sched.h>
#include <linux/semaphore.h>
#include <linux/spinlock.h>
+#include <linux/ftrace.h>
static noinline void __down(struct semaphore *sem);
static noinline int __down_interruptible(struct semaphore *sem);
@@ -53,6 +54,7 @@ void down(struct semaphore *sem)
{
unsigned long flags;
+ ftrace_special(sem->count, 0, __LINE__);
spin_lock_irqsave(&sem->lock, flags);
if (likely(sem->count > 0))
sem->count--;
diff --git a/kernel/softirq.c b/kernel/softirq.c
index 36e061740047..059256874e9b 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -347,9 +347,8 @@ void raise_softirq(unsigned int nr)
local_irq_restore(flags);
}
-void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
+void open_softirq(int nr, void (*action)(struct softirq_action *))
{
- softirq_vec[nr].data = data;
softirq_vec[nr].action = action;
}
@@ -503,8 +502,8 @@ void __init softirq_init(void)
&per_cpu(tasklet_hi_vec, cpu).head;
}
- open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
- open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
+ open_softirq(TASKLET_SOFTIRQ, tasklet_action);
+ open_softirq(HI_SOFTIRQ, tasklet_hi_action);
}
static int ksoftirqd(void * __bind_cpu)
diff --git a/kernel/softlockup.c b/kernel/softlockup.c
index 01b6522fd92b..6b682d86bddf 100644
--- a/kernel/softlockup.c
+++ b/kernel/softlockup.c
@@ -25,7 +25,22 @@ static DEFINE_PER_CPU(unsigned long, print_timestamp);
static DEFINE_PER_CPU(struct task_struct *, watchdog_task);
static int __read_mostly did_panic;
-unsigned long __read_mostly softlockup_thresh = 60;
+int __read_mostly softlockup_thresh = 60;
+
+/*
+ * Should we panic (and reboot, if panic_timeout= is set) when a
+ * soft-lockup occurs:
+ */
+unsigned int __read_mostly softlockup_panic =
+ CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE;
+
+static int __init softlockup_panic_setup(char *str)
+{
+ softlockup_panic = simple_strtoul(str, NULL, 0);
+
+ return 1;
+}
+__setup("softlockup_panic=", softlockup_panic_setup);
static int
softlock_panic(struct notifier_block *this, unsigned long event, void *ptr)
@@ -49,12 +64,17 @@ static unsigned long get_timestamp(int this_cpu)
return cpu_clock(this_cpu) >> 30LL; /* 2^30 ~= 10^9 */
}
-void touch_softlockup_watchdog(void)
+static void __touch_softlockup_watchdog(void)
{
int this_cpu = raw_smp_processor_id();
__raw_get_cpu_var(touch_timestamp) = get_timestamp(this_cpu);
}
+
+void touch_softlockup_watchdog(void)
+{
+ __raw_get_cpu_var(touch_timestamp) = 0;
+}
EXPORT_SYMBOL(touch_softlockup_watchdog);
void touch_all_softlockup_watchdogs(void)
@@ -79,8 +99,16 @@ void softlockup_tick(void)
struct pt_regs *regs = get_irq_regs();
unsigned long now;
+ /* Is detection switched off? */
+ if (!per_cpu(watchdog_task, this_cpu) || softlockup_thresh <= 0) {
+ /* Be sure we don't false trigger if switched back on */
+ if (touch_timestamp)
+ per_cpu(touch_timestamp, this_cpu) = 0;
+ return;
+ }
+
if (touch_timestamp == 0) {
- touch_softlockup_watchdog();
+ __touch_softlockup_watchdog();
return;
}
@@ -89,13 +117,13 @@ void softlockup_tick(void)
/* report at most once a second */
if ((print_timestamp >= touch_timestamp &&
print_timestamp < (touch_timestamp + 1)) ||
- did_panic || !per_cpu(watchdog_task, this_cpu)) {
+ did_panic) {
return;
}
/* do not print during early bootup: */
if (unlikely(system_state != SYSTEM_RUNNING)) {
- touch_softlockup_watchdog();
+ __touch_softlockup_watchdog();
return;
}
@@ -120,6 +148,9 @@ void softlockup_tick(void)
else
dump_stack();
spin_unlock(&print_lock);
+
+ if (softlockup_panic)
+ panic("softlockup: hung tasks");
}
/*
@@ -172,6 +203,9 @@ static void check_hung_task(struct task_struct *t, unsigned long now)
t->last_switch_timestamp = now;
touch_nmi_watchdog();
+
+ if (softlockup_panic)
+ panic("softlockup: blocked tasks");
}
/*
@@ -214,7 +248,7 @@ static int watchdog(void *__bind_cpu)
sched_setscheduler(current, SCHED_FIFO, &param);
/* initialize timestamp */
- touch_softlockup_watchdog();
+ __touch_softlockup_watchdog();
set_current_state(TASK_INTERRUPTIBLE);
/*
@@ -223,7 +257,7 @@ static int watchdog(void *__bind_cpu)
* debug-printout triggers in softlockup_tick().
*/
while (!kthread_should_stop()) {
- touch_softlockup_watchdog();
+ __touch_softlockup_watchdog();
schedule();
if (kthread_should_stop())
diff --git a/kernel/spinlock.c b/kernel/spinlock.c
index ae28c8245123..a1fb54c93cdd 100644
--- a/kernel/spinlock.c
+++ b/kernel/spinlock.c
@@ -436,7 +436,7 @@ int __lockfunc _spin_trylock_bh(spinlock_t *lock)
}
EXPORT_SYMBOL(_spin_trylock_bh);
-int in_lock_functions(unsigned long addr)
+notrace int in_lock_functions(unsigned long addr)
{
/* Linker adds these: start and end of __lockfunc functions */
extern char __lock_text_start[], __lock_text_end[];
diff --git a/kernel/stacktrace.c b/kernel/stacktrace.c
index b71816e47a30..0914d0cbc83c 100644
--- a/kernel/stacktrace.c
+++ b/kernel/stacktrace.c
@@ -13,6 +13,9 @@ void print_stack_trace(struct stack_trace *trace, int spaces)
{
int i, j;
+ if (WARN_ON(!trace->entries))
+ return;
+
for (i = 0; i < trace->nr_entries; i++) {
unsigned long ip = trace->entries[i];
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 29116652dca8..2f3fcdac325b 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -46,6 +46,7 @@
#include <linux/nfs_fs.h>
#include <linux/acpi.h>
#include <linux/reboot.h>
+#include <linux/ftrace.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
@@ -84,12 +85,13 @@ extern int latencytop_enabled;
extern int sysctl_nr_open_min, sysctl_nr_open_max;
/* Constants used for minimum and maximum */
-#if defined(CONFIG_DETECT_SOFTLOCKUP) || defined(CONFIG_HIGHMEM)
+#if defined(CONFIG_HIGHMEM) || defined(CONFIG_DETECT_SOFTLOCKUP)
static int one = 1;
#endif
#ifdef CONFIG_DETECT_SOFTLOCKUP
static int sixty = 60;
+static int neg_one = -1;
#endif
#ifdef CONFIG_MMU
@@ -455,6 +457,16 @@ static struct ctl_table kern_table[] = {
.mode = 0644,
.proc_handler = &proc_dointvec,
},
+#ifdef CONFIG_FTRACE
+ {
+ .ctl_name = CTL_UNNUMBERED,
+ .procname = "ftrace_enabled",
+ .data = &ftrace_enabled,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &ftrace_enable_sysctl,
+ },
+#endif
#ifdef CONFIG_KMOD
{
.ctl_name = KERN_MODPROBE,
@@ -729,13 +741,24 @@ static struct ctl_table kern_table[] = {
#ifdef CONFIG_DETECT_SOFTLOCKUP
{
.ctl_name = CTL_UNNUMBERED,
+ .procname = "softlockup_panic",
+ .data = &softlockup_panic,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = &proc_doulongvec_minmax,
+ .strategy = &sysctl_intvec,
+ .extra1 = &zero,
+ .extra2 = &one,
+ },
+ {
+ .ctl_name = CTL_UNNUMBERED,
.procname = "softlockup_thresh",
.data = &softlockup_thresh,
- .maxlen = sizeof(unsigned long),
+ .maxlen = sizeof(int),
.mode = 0644,
- .proc_handler = &proc_doulongvec_minmax,
+ .proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
- .extra1 = &one,
+ .extra1 = &neg_one,
.extra2 = &sixty,
},
{
diff --git a/kernel/taskstats.c b/kernel/taskstats.c
index 4a23517169a6..06b17547f4e7 100644
--- a/kernel/taskstats.c
+++ b/kernel/taskstats.c
@@ -301,7 +301,7 @@ static int add_del_listener(pid_t pid, cpumask_t *maskp, int isadd)
return -EINVAL;
if (isadd == REGISTER) {
- for_each_cpu_mask(cpu, mask) {
+ for_each_cpu_mask_nr(cpu, mask) {
s = kmalloc_node(sizeof(struct listener), GFP_KERNEL,
cpu_to_node(cpu));
if (!s)
@@ -320,7 +320,7 @@ static int add_del_listener(pid_t pid, cpumask_t *maskp, int isadd)
/* Deregister or cleanup */
cleanup:
- for_each_cpu_mask(cpu, mask) {
+ for_each_cpu_mask_nr(cpu, mask) {
listeners = &per_cpu(listener_array, cpu);
down_write(&listeners->sem);
list_for_each_entry_safe(s, tmp, &listeners->list, list) {
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c
index dadde5361f32..60ceabd53f2e 100644
--- a/kernel/time/clocksource.c
+++ b/kernel/time/clocksource.c
@@ -145,9 +145,9 @@ static void clocksource_watchdog(unsigned long data)
* Cycle through CPUs to check if the CPUs stay
* synchronized to each other.
*/
- int next_cpu = next_cpu(raw_smp_processor_id(), cpu_online_map);
+ int next_cpu = next_cpu_nr(raw_smp_processor_id(), cpu_online_map);
- if (next_cpu >= NR_CPUS)
+ if (next_cpu >= nr_cpu_ids)
next_cpu = first_cpu(cpu_online_map);
watchdog_timer.expires += WATCHDOG_INTERVAL;
add_timer_on(&watchdog_timer, next_cpu);
diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c
index 57a1f02e5ec0..963dd30a2e0b 100644
--- a/kernel/time/tick-broadcast.c
+++ b/kernel/time/tick-broadcast.c
@@ -30,6 +30,7 @@
struct tick_device tick_broadcast_device;
static cpumask_t tick_broadcast_mask;
static DEFINE_SPINLOCK(tick_broadcast_lock);
+static int tick_broadcast_force;
#ifdef CONFIG_TICK_ONESHOT
static void tick_broadcast_clear_oneshot(int cpu);
@@ -232,10 +233,11 @@ static void tick_do_broadcast_on_off(void *why)
CLOCK_EVT_MODE_SHUTDOWN);
}
if (*reason == CLOCK_EVT_NOTIFY_BROADCAST_FORCE)
- dev->features |= CLOCK_EVT_FEAT_DUMMY;
+ tick_broadcast_force = 1;
break;
case CLOCK_EVT_NOTIFY_BROADCAST_OFF:
- if (cpu_isset(cpu, tick_broadcast_mask)) {
+ if (!tick_broadcast_force &&
+ cpu_isset(cpu, tick_broadcast_mask)) {
cpu_clear(cpu, tick_broadcast_mask);
if (td->mode == TICKDEV_MODE_PERIODIC)
tick_setup_periodic(dev, 0);
@@ -397,8 +399,7 @@ again:
mask = CPU_MASK_NONE;
now = ktime_get();
/* Find all expired events */
- for (cpu = first_cpu(tick_broadcast_oneshot_mask); cpu != NR_CPUS;
- cpu = next_cpu(cpu, tick_broadcast_oneshot_mask)) {
+ for_each_cpu_mask_nr(cpu, tick_broadcast_oneshot_mask) {
td = &per_cpu(tick_cpu_device, cpu);
if (td->evtdev->next_event.tv64 <= now.tv64)
cpu_set(cpu, mask);
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index b854a895591e..76050c9fb6af 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -48,6 +48,13 @@ static void tick_do_update_jiffies64(ktime_t now)
unsigned long ticks = 0;
ktime_t delta;
+ /*
+ * Do a quick check without holding xtime_lock:
+ */
+ delta = ktime_sub(now, last_jiffies_update);
+ if (delta.tv64 < tick_period.tv64)
+ return;
+
/* Reevalute with xtime_lock held */
write_seqlock(&xtime_lock);
@@ -133,8 +140,6 @@ void tick_nohz_update_jiffies(void)
if (!ts->tick_stopped)
return;
- touch_softlockup_watchdog();
-
cpu_clear(cpu, nohz_cpu_mask);
now = ktime_get();
ts->idle_waketime = now;
@@ -142,6 +147,8 @@ void tick_nohz_update_jiffies(void)
local_irq_save(flags);
tick_do_update_jiffies64(now);
local_irq_restore(flags);
+
+ touch_softlockup_watchdog();
}
void tick_nohz_stop_idle(int cpu)
diff --git a/kernel/timer.c b/kernel/timer.c
index ceacc6626572..b4da888497fa 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -1502,7 +1502,7 @@ void __init init_timers(void)
BUG_ON(err == NOTIFY_BAD);
register_cpu_notifier(&timers_nb);
- open_softirq(TIMER_SOFTIRQ, run_timer_softirq, NULL);
+ open_softirq(TIMER_SOFTIRQ, run_timer_softirq);
}
/**
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
new file mode 100644
index 000000000000..263e9e6bbd60
--- /dev/null
+++ b/kernel/trace/Kconfig
@@ -0,0 +1,135 @@
+#
+# Architectures that offer an FTRACE implementation should select HAVE_FTRACE:
+#
+config HAVE_FTRACE
+ bool
+
+config HAVE_DYNAMIC_FTRACE
+ bool
+
+config TRACER_MAX_TRACE
+ bool
+
+config TRACING
+ bool
+ select DEBUG_FS
+ select STACKTRACE
+
+config FTRACE
+ bool "Kernel Function Tracer"
+ depends on HAVE_FTRACE
+ select FRAME_POINTER
+ select TRACING
+ select CONTEXT_SWITCH_TRACER
+ help
+ Enable the kernel to trace every kernel function. This is done
+ by using a compiler feature to insert a small, 5-byte No-Operation
+ instruction to the beginning of every kernel function, which NOP
+ sequence is then dynamically patched into a tracer call when
+ tracing is enabled by the administrator. If it's runtime disabled
+ (the bootup default), then the overhead of the instructions is very
+ small and not measurable even in micro-benchmarks.
+
+config IRQSOFF_TRACER
+ bool "Interrupts-off Latency Tracer"
+ default n
+ depends on TRACE_IRQFLAGS_SUPPORT
+ depends on GENERIC_TIME
+ depends on HAVE_FTRACE
+ select TRACE_IRQFLAGS
+ select TRACING
+ select TRACER_MAX_TRACE
+ help
+ This option measures the time spent in irqs-off critical
+ sections, with microsecond accuracy.
+
+ The default measurement method is a maximum search, which is
+ disabled by default and can be runtime (re-)started
+ via:
+
+ echo 0 > /debugfs/tracing/tracing_max_latency
+
+ (Note that kernel size and overhead increases with this option
+ enabled. This option and the preempt-off timing option can be
+ used together or separately.)
+
+config PREEMPT_TRACER
+ bool "Preemption-off Latency Tracer"
+ default n
+ depends on GENERIC_TIME
+ depends on PREEMPT
+ depends on HAVE_FTRACE
+ select TRACING
+ select TRACER_MAX_TRACE
+ help
+ This option measures the time spent in preemption off critical
+ sections, with microsecond accuracy.
+
+ The default measurement method is a maximum search, which is
+ disabled by default and can be runtime (re-)started
+ via:
+
+ echo 0 > /debugfs/tracing/tracing_max_latency
+
+ (Note that kernel size and overhead increases with this option
+ enabled. This option and the irqs-off timing option can be
+ used together or separately.)
+
+config SYSPROF_TRACER
+ bool "Sysprof Tracer"
+ depends on X86
+ select TRACING
+ help
+ This tracer provides the trace needed by the 'Sysprof' userspace
+ tool.
+
+config SCHED_TRACER
+ bool "Scheduling Latency Tracer"
+ depends on HAVE_FTRACE
+ select TRACING
+ select CONTEXT_SWITCH_TRACER
+ select TRACER_MAX_TRACE
+ help
+ This tracer tracks the latency of the highest priority task
+ to be scheduled in, starting from the point it has woken up.
+
+config CONTEXT_SWITCH_TRACER
+ bool "Trace process context switches"
+ depends on HAVE_FTRACE
+ select TRACING
+ select MARKERS
+ help
+ This tracer gets called from the context switch and records
+ all switching of tasks.
+
+config DYNAMIC_FTRACE
+ bool "enable/disable ftrace tracepoints dynamically"
+ depends on FTRACE
+ depends on HAVE_DYNAMIC_FTRACE
+ default y
+ help
+ This option will modify all the calls to ftrace dynamically
+ (will patch them out of the binary image and replaces them
+ with a No-Op instruction) as they are called. A table is
+ created to dynamically enable them again.
+
+ This way a CONFIG_FTRACE kernel is slightly larger, but otherwise
+ has native performance as long as no tracing is active.
+
+ The changes to the code are done by a kernel thread that
+ wakes up once a second and checks to see if any ftrace calls
+ were made. If so, it runs stop_machine (stops all CPUS)
+ and modifies the code to jump over the call to ftrace.
+
+config FTRACE_SELFTEST
+ bool
+
+config FTRACE_STARTUP_TEST
+ bool "Perform a startup test on ftrace"
+ depends on TRACING
+ select FTRACE_SELFTEST
+ help
+ This option performs a series of startup tests on ftrace. On bootup
+ a series of tests are made to verify that the tracer is
+ functioning properly. It will do tests on all the configured
+ tracers of ftrace.
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
new file mode 100644
index 000000000000..71d17de17288
--- /dev/null
+++ b/kernel/trace/Makefile
@@ -0,0 +1,24 @@
+
+# Do not instrument the tracer itself:
+
+ifdef CONFIG_FTRACE
+ORIG_CFLAGS := $(KBUILD_CFLAGS)
+KBUILD_CFLAGS = $(subst -pg,,$(ORIG_CFLAGS))
+
+# selftest needs instrumentation
+CFLAGS_trace_selftest_dynamic.o = -pg
+obj-y += trace_selftest_dynamic.o
+endif
+
+obj-$(CONFIG_FTRACE) += libftrace.o
+
+obj-$(CONFIG_TRACING) += trace.o
+obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o
+obj-$(CONFIG_SYSPROF_TRACER) += trace_sysprof.o
+obj-$(CONFIG_FTRACE) += trace_functions.o
+obj-$(CONFIG_IRQSOFF_TRACER) += trace_irqsoff.o
+obj-$(CONFIG_PREEMPT_TRACER) += trace_irqsoff.o
+obj-$(CONFIG_SCHED_TRACER) += trace_sched_wakeup.o
+obj-$(CONFIG_MMIOTRACE) += trace_mmiotrace.o
+
+libftrace-y := ftrace.o
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
new file mode 100644
index 000000000000..0118979e211f
--- /dev/null
+++ b/kernel/trace/ftrace.c
@@ -0,0 +1,1599 @@
+/*
+ * Infrastructure for profiling code inserted by 'gcc -pg'.
+ *
+ * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
+ * Copyright (C) 2004-2008 Ingo Molnar <mingo@redhat.com>
+ *
+ * Originally ported from the -rt patch by:
+ * Copyright (C) 2007 Arnaldo Carvalho de Melo <acme@redhat.com>
+ *
+ * Based on code in the latency_tracer, that is:
+ *
+ * Copyright (C) 2004-2006 Ingo Molnar
+ * Copyright (C) 2004 William Lee Irwin III
+ */
+
+#include <linux/stop_machine.h>
+#include <linux/clocksource.h>
+#include <linux/kallsyms.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/hardirq.h>
+#include <linux/kthread.h>
+#include <linux/uaccess.h>
+#include <linux/ftrace.h>
+#include <linux/sysctl.h>
+#include <linux/ctype.h>
+#include <linux/hash.h>
+#include <linux/list.h>
+
+#include "trace.h"
+
+/* ftrace_enabled is a method to turn ftrace on or off */
+int ftrace_enabled __read_mostly;
+static int last_ftrace_enabled;
+
+/*
+ * ftrace_disabled is set when an anomaly is discovered.
+ * ftrace_disabled is much stronger than ftrace_enabled.
+ */
+static int ftrace_disabled __read_mostly;
+
+static DEFINE_SPINLOCK(ftrace_lock);
+static DEFINE_MUTEX(ftrace_sysctl_lock);
+
+static struct ftrace_ops ftrace_list_end __read_mostly =
+{
+ .func = ftrace_stub,
+};
+
+static struct ftrace_ops *ftrace_list __read_mostly = &ftrace_list_end;
+ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub;
+
+void ftrace_list_func(unsigned long ip, unsigned long parent_ip)
+{
+ struct ftrace_ops *op = ftrace_list;
+
+ /* in case someone actually ports this to alpha! */
+ read_barrier_depends();
+
+ while (op != &ftrace_list_end) {
+ /* silly alpha */
+ read_barrier_depends();
+ op->func(ip, parent_ip);
+ op = op->next;
+ };
+}
+
+/**
+ * clear_ftrace_function - reset the ftrace function
+ *
+ * This NULLs the ftrace function and in essence stops
+ * tracing. There may be lag
+ */
+void clear_ftrace_function(void)
+{
+ ftrace_trace_function = ftrace_stub;
+}
+
+static int __register_ftrace_function(struct ftrace_ops *ops)
+{
+ /* Should never be called by interrupts */
+ spin_lock(&ftrace_lock);
+
+ ops->next = ftrace_list;
+ /*
+ * We are entering ops into the ftrace_list but another
+ * CPU might be walking that list. We need to make sure
+ * the ops->next pointer is valid before another CPU sees
+ * the ops pointer included into the ftrace_list.
+ */
+ smp_wmb();
+ ftrace_list = ops;
+
+ if (ftrace_enabled) {
+ /*
+ * For one func, simply call it directly.
+ * For more than one func, call the chain.
+ */
+ if (ops->next == &ftrace_list_end)
+ ftrace_trace_function = ops->func;
+ else
+ ftrace_trace_function = ftrace_list_func;
+ }
+
+ spin_unlock(&ftrace_lock);
+
+ return 0;
+}
+
+static int __unregister_ftrace_function(struct ftrace_ops *ops)
+{
+ struct ftrace_ops **p;
+ int ret = 0;
+
+ spin_lock(&ftrace_lock);
+
+ /*
+ * If we are removing the last function, then simply point
+ * to the ftrace_stub.
+ */
+ if (ftrace_list == ops && ops->next == &ftrace_list_end) {
+ ftrace_trace_function = ftrace_stub;
+ ftrace_list = &ftrace_list_end;
+ goto out;
+ }
+
+ for (p = &ftrace_list; *p != &ftrace_list_end; p = &(*p)->next)
+ if (*p == ops)
+ break;
+
+ if (*p != ops) {
+ ret = -1;
+ goto out;
+ }
+
+ *p = (*p)->next;
+
+ if (ftrace_enabled) {
+ /* If we only have one func left, then call that directly */
+ if (ftrace_list == &ftrace_list_end ||
+ ftrace_list->next == &ftrace_list_end)
+ ftrace_trace_function = ftrace_list->func;
+ }
+
+ out:
+ spin_unlock(&ftrace_lock);
+
+ return ret;
+}
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+
+static struct task_struct *ftraced_task;
+
+enum {
+ FTRACE_ENABLE_CALLS = (1 << 0),
+ FTRACE_DISABLE_CALLS = (1 << 1),
+ FTRACE_UPDATE_TRACE_FUNC = (1 << 2),
+ FTRACE_ENABLE_MCOUNT = (1 << 3),
+ FTRACE_DISABLE_MCOUNT = (1 << 4),
+};
+
+static int ftrace_filtered;
+
+static struct hlist_head ftrace_hash[FTRACE_HASHSIZE];
+
+static DEFINE_PER_CPU(int, ftrace_shutdown_disable_cpu);
+
+static DEFINE_SPINLOCK(ftrace_shutdown_lock);
+static DEFINE_MUTEX(ftraced_lock);
+static DEFINE_MUTEX(ftrace_regex_lock);
+
+struct ftrace_page {
+ struct ftrace_page *next;
+ unsigned long index;
+ struct dyn_ftrace records[];
+};
+
+#define ENTRIES_PER_PAGE \
+ ((PAGE_SIZE - sizeof(struct ftrace_page)) / sizeof(struct dyn_ftrace))
+
+/* estimate from running different kernels */
+#define NR_TO_INIT 10000
+
+static struct ftrace_page *ftrace_pages_start;
+static struct ftrace_page *ftrace_pages;
+
+static int ftraced_trigger;
+static int ftraced_suspend;
+static int ftraced_stop;
+
+static int ftrace_record_suspend;
+
+static struct dyn_ftrace *ftrace_free_records;
+
+static inline int
+ftrace_ip_in_hash(unsigned long ip, unsigned long key)
+{
+ struct dyn_ftrace *p;
+ struct hlist_node *t;
+ int found = 0;
+
+ hlist_for_each_entry_rcu(p, t, &ftrace_hash[key], node) {
+ if (p->ip == ip) {
+ found = 1;
+ break;
+ }
+ }
+
+ return found;
+}
+
+static inline void
+ftrace_add_hash(struct dyn_ftrace *node, unsigned long key)
+{
+ hlist_add_head_rcu(&node->node, &ftrace_hash[key]);
+}
+
+/* called from kstop_machine */
+static inline void ftrace_del_hash(struct dyn_ftrace *node)
+{
+ hlist_del(&node->node);
+}
+
+static void ftrace_free_rec(struct dyn_ftrace *rec)
+{
+ /* no locking, only called from kstop_machine */
+
+ rec->ip = (unsigned long)ftrace_free_records;
+ ftrace_free_records = rec;
+ rec->flags |= FTRACE_FL_FREE;
+}
+
+static struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip)
+{
+ struct dyn_ftrace *rec;
+
+ /* First check for freed records */
+ if (ftrace_free_records) {
+ rec = ftrace_free_records;
+
+ if (unlikely(!(rec->flags & FTRACE_FL_FREE))) {
+ WARN_ON_ONCE(1);
+ ftrace_free_records = NULL;
+ ftrace_disabled = 1;
+ ftrace_enabled = 0;
+ return NULL;
+ }
+
+ ftrace_free_records = (void *)rec->ip;
+ memset(rec, 0, sizeof(*rec));
+ return rec;
+ }
+
+ if (ftrace_pages->index == ENTRIES_PER_PAGE) {
+ if (!ftrace_pages->next)
+ return NULL;
+ ftrace_pages = ftrace_pages->next;
+ }
+
+ return &ftrace_pages->records[ftrace_pages->index++];
+}
+
+static void
+ftrace_record_ip(unsigned long ip)
+{
+ struct dyn_ftrace *node;
+ unsigned long flags;
+ unsigned long key;
+ int resched;
+ int atomic;
+ int cpu;
+
+ if (!ftrace_enabled || ftrace_disabled)
+ return;
+
+ resched = need_resched();
+ preempt_disable_notrace();
+
+ /*
+ * We simply need to protect against recursion.
+ * Use the the raw version of smp_processor_id and not
+ * __get_cpu_var which can call debug hooks that can
+ * cause a recursive crash here.
+ */
+ cpu = raw_smp_processor_id();
+ per_cpu(ftrace_shutdown_disable_cpu, cpu)++;
+ if (per_cpu(ftrace_shutdown_disable_cpu, cpu) != 1)
+ goto out;
+
+ if (unlikely(ftrace_record_suspend))
+ goto out;
+
+ key = hash_long(ip, FTRACE_HASHBITS);
+
+ WARN_ON_ONCE(key >= FTRACE_HASHSIZE);
+
+ if (ftrace_ip_in_hash(ip, key))
+ goto out;
+
+ atomic = irqs_disabled();
+
+ spin_lock_irqsave(&ftrace_shutdown_lock, flags);
+
+ /* This ip may have hit the hash before the lock */
+ if (ftrace_ip_in_hash(ip, key))
+ goto out_unlock;
+
+ node = ftrace_alloc_dyn_node(ip);
+ if (!node)
+ goto out_unlock;
+
+ node->ip = ip;
+
+ ftrace_add_hash(node, key);
+
+ ftraced_trigger = 1;
+
+ out_unlock:
+ spin_unlock_irqrestore(&ftrace_shutdown_lock, flags);
+ out:
+ per_cpu(ftrace_shutdown_disable_cpu, cpu)--;
+
+ /* prevent recursion with scheduler */
+ if (resched)
+ preempt_enable_no_resched_notrace();
+ else
+ preempt_enable_notrace();
+}
+
+#define FTRACE_ADDR ((long)(ftrace_caller))
+#define MCOUNT_ADDR ((long)(mcount))
+
+static int
+__ftrace_replace_code(struct dyn_ftrace *rec,
+ unsigned char *old, unsigned char *new, int enable)
+{
+ unsigned long ip, fl;
+
+ ip = rec->ip;
+
+ if (ftrace_filtered && enable) {
+ /*
+ * If filtering is on:
+ *
+ * If this record is set to be filtered and
+ * is enabled then do nothing.
+ *
+ * If this record is set to be filtered and
+ * it is not enabled, enable it.
+ *
+ * If this record is not set to be filtered
+ * and it is not enabled do nothing.
+ *
+ * If this record is set not to trace then
+ * do nothing.
+ *
+ * If this record is not set to be filtered and
+ * it is enabled, disable it.
+ */
+ fl = rec->flags & (FTRACE_FL_FILTER | FTRACE_FL_ENABLED);
+
+ if ((fl == (FTRACE_FL_FILTER | FTRACE_FL_ENABLED)) ||
+ (fl == 0) || (rec->flags & FTRACE_FL_NOTRACE))
+ return 0;
+
+ /*
+ * If it is enabled disable it,
+ * otherwise enable it!
+ */
+ if (fl == FTRACE_FL_ENABLED) {
+ /* swap new and old */
+ new = old;
+ old = ftrace_call_replace(ip, FTRACE_ADDR);
+ rec->flags &= ~FTRACE_FL_ENABLED;
+ } else {
+ new = ftrace_call_replace(ip, FTRACE_ADDR);
+ rec->flags |= FTRACE_FL_ENABLED;
+ }
+ } else {
+
+ if (enable) {
+ /*
+ * If this record is set not to trace and is
+ * not enabled, do nothing.
+ */
+ fl = rec->flags & (FTRACE_FL_NOTRACE | FTRACE_FL_ENABLED);
+ if (fl == FTRACE_FL_NOTRACE)
+ return 0;
+
+ new = ftrace_call_replace(ip, FTRACE_ADDR);
+ } else
+ old = ftrace_call_replace(ip, FTRACE_ADDR);
+
+ if (enable) {
+ if (rec->flags & FTRACE_FL_ENABLED)
+ return 0;
+ rec->flags |= FTRACE_FL_ENABLED;
+ } else {
+ if (!(rec->flags & FTRACE_FL_ENABLED))
+ return 0;
+ rec->flags &= ~FTRACE_FL_ENABLED;
+ }
+ }
+
+ return ftrace_modify_code(ip, old, new);
+}
+
+static void ftrace_replace_code(int enable)
+{
+ int i, failed;
+ unsigned char *new = NULL, *old = NULL;
+ struct dyn_ftrace *rec;
+ struct ftrace_page *pg;
+
+ if (enable)
+ old = ftrace_nop_replace();
+ else
+ new = ftrace_nop_replace();
+
+ for (pg = ftrace_pages_start; pg; pg = pg->next) {
+ for (i = 0; i < pg->index; i++) {
+ rec = &pg->records[i];
+
+ /* don't modify code that has already faulted */
+ if (rec->flags & FTRACE_FL_FAILED)
+ continue;
+
+ failed = __ftrace_replace_code(rec, old, new, enable);
+ if (failed && (rec->flags & FTRACE_FL_CONVERTED)) {
+ rec->flags |= FTRACE_FL_FAILED;
+ if ((system_state == SYSTEM_BOOTING) ||
+ !core_kernel_text(rec->ip)) {
+ ftrace_del_hash(rec);
+ ftrace_free_rec(rec);
+ }
+ }
+ }
+ }
+}
+
+static void ftrace_shutdown_replenish(void)
+{
+ if (ftrace_pages->next)
+ return;
+
+ /* allocate another page */
+ ftrace_pages->next = (void *)get_zeroed_page(GFP_KERNEL);
+}
+
+static int
+ftrace_code_disable(struct dyn_ftrace *rec)
+{
+ unsigned long ip;
+ unsigned char *nop, *call;
+ int failed;
+
+ ip = rec->ip;
+
+ nop = ftrace_nop_replace();
+ call = ftrace_call_replace(ip, MCOUNT_ADDR);
+
+ failed = ftrace_modify_code(ip, call, nop);
+ if (failed) {
+ rec->flags |= FTRACE_FL_FAILED;
+ return 0;
+ }
+ return 1;
+}
+
+static int __ftrace_update_code(void *ignore);
+
+static int __ftrace_modify_code(void *data)
+{
+ unsigned long addr;
+ int *command = data;
+
+ if (*command & FTRACE_ENABLE_CALLS) {
+ /*
+ * Update any recorded ips now that we have the
+ * machine stopped
+ */
+ __ftrace_update_code(NULL);
+ ftrace_replace_code(1);
+ } else if (*command & FTRACE_DISABLE_CALLS)
+ ftrace_replace_code(0);
+
+ if (*command & FTRACE_UPDATE_TRACE_FUNC)
+ ftrace_update_ftrace_func(ftrace_trace_function);
+
+ if (*command & FTRACE_ENABLE_MCOUNT) {
+ addr = (unsigned long)ftrace_record_ip;
+ ftrace_mcount_set(&addr);
+ } else if (*command & FTRACE_DISABLE_MCOUNT) {
+ addr = (unsigned long)ftrace_stub;
+ ftrace_mcount_set(&addr);
+ }
+
+ return 0;
+}
+
+static void ftrace_run_update_code(int command)
+{
+ stop_machine_run(__ftrace_modify_code, &command, NR_CPUS);
+}
+
+void ftrace_disable_daemon(void)
+{
+ /* Stop the daemon from calling kstop_machine */
+ mutex_lock(&ftraced_lock);
+ ftraced_stop = 1;
+ mutex_unlock(&ftraced_lock);
+
+ ftrace_force_update();
+}
+
+void ftrace_enable_daemon(void)
+{
+ mutex_lock(&ftraced_lock);
+ ftraced_stop = 0;
+ mutex_unlock(&ftraced_lock);
+
+ ftrace_force_update();
+}
+
+static ftrace_func_t saved_ftrace_func;
+
+static void ftrace_startup(void)
+{
+ int command = 0;
+
+ if (unlikely(ftrace_disabled))
+ return;
+
+ mutex_lock(&ftraced_lock);
+ ftraced_suspend++;
+ if (ftraced_suspend == 1)
+ command |= FTRACE_ENABLE_CALLS;
+
+ if (saved_ftrace_func != ftrace_trace_function) {
+ saved_ftrace_func = ftrace_trace_function;
+ command |= FTRACE_UPDATE_TRACE_FUNC;
+ }
+
+ if (!command || !ftrace_enabled)
+ goto out;
+
+ ftrace_run_update_code(command);
+ out:
+ mutex_unlock(&ftraced_lock);
+}
+
+static void ftrace_shutdown(void)
+{
+ int command = 0;
+
+ if (unlikely(ftrace_disabled))
+ return;
+
+ mutex_lock(&ftraced_lock);
+ ftraced_suspend--;
+ if (!ftraced_suspend)
+ command |= FTRACE_DISABLE_CALLS;
+
+ if (saved_ftrace_func != ftrace_trace_function) {
+ saved_ftrace_func = ftrace_trace_function;
+ command |= FTRACE_UPDATE_TRACE_FUNC;
+ }
+
+ if (!command || !ftrace_enabled)
+ goto out;
+
+ ftrace_run_update_code(command);
+ out:
+ mutex_unlock(&ftraced_lock);
+}
+
+static void ftrace_startup_sysctl(void)
+{
+ int command = FTRACE_ENABLE_MCOUNT;
+
+ if (unlikely(ftrace_disabled))
+ return;
+
+ mutex_lock(&ftraced_lock);
+ /* Force update next time */
+ saved_ftrace_func = NULL;
+ /* ftraced_suspend is true if we want ftrace running */
+ if (ftraced_suspend)
+ command |= FTRACE_ENABLE_CALLS;
+
+ ftrace_run_update_code(command);
+ mutex_unlock(&ftraced_lock);
+}
+
+static void ftrace_shutdown_sysctl(void)
+{
+ int command = FTRACE_DISABLE_MCOUNT;
+
+ if (unlikely(ftrace_disabled))
+ return;
+
+ mutex_lock(&ftraced_lock);
+ /* ftraced_suspend is true if ftrace is running */
+ if (ftraced_suspend)
+ command |= FTRACE_DISABLE_CALLS;
+
+ ftrace_run_update_code(command);
+ mutex_unlock(&ftraced_lock);
+}
+
+static cycle_t ftrace_update_time;
+static unsigned long ftrace_update_cnt;
+unsigned long ftrace_update_tot_cnt;
+
+static int __ftrace_update_code(void *ignore)
+{
+ struct dyn_ftrace *p;
+ struct hlist_node *t, *n;
+ int save_ftrace_enabled;
+ cycle_t start, stop;
+ int i;
+
+ /* Don't be recording funcs now */
+ ftrace_record_suspend++;
+ save_ftrace_enabled = ftrace_enabled;
+ ftrace_enabled = 0;
+
+ start = ftrace_now(raw_smp_processor_id());
+ ftrace_update_cnt = 0;
+
+ /* No locks needed, the machine is stopped! */
+ for (i = 0; i < FTRACE_HASHSIZE; i++) {
+ /* all CPUS are stopped, we are safe to modify code */
+ hlist_for_each_entry_safe(p, t, n, &ftrace_hash[i], node) {
+ /* Skip over failed records which have not been
+ * freed. */
+ if (p->flags & FTRACE_FL_FAILED)
+ continue;
+
+ /* Unconverted records are always at the head of the
+ * hash bucket. Once we encounter a converted record,
+ * simply skip over to the next bucket. Saves ftraced
+ * some processor cycles (ftrace does its bid for
+ * global warming :-p ). */
+ if (p->flags & (FTRACE_FL_CONVERTED))
+ break;
+
+ if (ftrace_code_disable(p)) {
+ p->flags |= FTRACE_FL_CONVERTED;
+ ftrace_update_cnt++;
+ } else {
+ if ((system_state == SYSTEM_BOOTING) ||
+ !core_kernel_text(p->ip)) {
+ ftrace_del_hash(p);
+ ftrace_free_rec(p);
+ }
+ }
+ }
+ }
+
+ stop = ftrace_now(raw_smp_processor_id());
+ ftrace_update_time = stop - start;
+ ftrace_update_tot_cnt += ftrace_update_cnt;
+ ftraced_trigger = 0;
+
+ ftrace_enabled = save_ftrace_enabled;
+ ftrace_record_suspend--;
+
+ return 0;
+}
+
+static int ftrace_update_code(void)
+{
+ if (unlikely(ftrace_disabled) ||
+ !ftrace_enabled || !ftraced_trigger)
+ return 0;
+
+ stop_machine_run(__ftrace_update_code, NULL, NR_CPUS);
+
+ return 1;
+}
+
+static int ftraced(void *ignore)
+{
+ unsigned long usecs;
+
+ while (!kthread_should_stop()) {
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ /* check once a second */
+ schedule_timeout(HZ);
+
+ if (unlikely(ftrace_disabled))
+ continue;
+
+ mutex_lock(&ftrace_sysctl_lock);
+ mutex_lock(&ftraced_lock);
+ if (!ftraced_suspend && !ftraced_stop &&
+ ftrace_update_code()) {
+ usecs = nsecs_to_usecs(ftrace_update_time);
+ if (ftrace_update_tot_cnt > 100000) {
+ ftrace_update_tot_cnt = 0;
+ pr_info("hm, dftrace overflow: %lu change%s"
+ " (%lu total) in %lu usec%s\n",
+ ftrace_update_cnt,
+ ftrace_update_cnt != 1 ? "s" : "",
+ ftrace_update_tot_cnt,
+ usecs, usecs != 1 ? "s" : "");
+ ftrace_disabled = 1;
+ WARN_ON_ONCE(1);
+ }
+ }
+ mutex_unlock(&ftraced_lock);
+ mutex_unlock(&ftrace_sysctl_lock);
+
+ ftrace_shutdown_replenish();
+ }
+ __set_current_state(TASK_RUNNING);
+ return 0;
+}
+
+static int __init ftrace_dyn_table_alloc(void)
+{
+ struct ftrace_page *pg;
+ int cnt;
+ int i;
+
+ /* allocate a few pages */
+ ftrace_pages_start = (void *)get_zeroed_page(GFP_KERNEL);
+ if (!ftrace_pages_start)
+ return -1;
+
+ /*
+ * Allocate a few more pages.
+ *
+ * TODO: have some parser search vmlinux before
+ * final linking to find all calls to ftrace.
+ * Then we can:
+ * a) know how many pages to allocate.
+ * and/or
+ * b) set up the table then.
+ *
+ * The dynamic code is still necessary for
+ * modules.
+ */
+
+ pg = ftrace_pages = ftrace_pages_start;
+
+ cnt = NR_TO_INIT / ENTRIES_PER_PAGE;
+
+ for (i = 0; i < cnt; i++) {
+ pg->next = (void *)get_zeroed_page(GFP_KERNEL);
+
+ /* If we fail, we'll try later anyway */
+ if (!pg->next)
+ break;
+
+ pg = pg->next;
+ }
+
+ return 0;
+}
+
+enum {
+ FTRACE_ITER_FILTER = (1 << 0),
+ FTRACE_ITER_CONT = (1 << 1),
+ FTRACE_ITER_NOTRACE = (1 << 2),
+ FTRACE_ITER_FAILURES = (1 << 3),
+};
+
+#define FTRACE_BUFF_MAX (KSYM_SYMBOL_LEN+4) /* room for wildcards */
+
+struct ftrace_iterator {
+ loff_t pos;
+ struct ftrace_page *pg;
+ unsigned idx;
+ unsigned flags;
+ unsigned char buffer[FTRACE_BUFF_MAX+1];
+ unsigned buffer_idx;
+ unsigned filtered;
+};
+
+static void *
+t_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ struct ftrace_iterator *iter = m->private;
+ struct dyn_ftrace *rec = NULL;
+
+ (*pos)++;
+
+ retry:
+ if (iter->idx >= iter->pg->index) {
+ if (iter->pg->next) {
+ iter->pg = iter->pg->next;
+ iter->idx = 0;
+ goto retry;
+ }
+ } else {
+ rec = &iter->pg->records[iter->idx++];
+ if ((!(iter->flags & FTRACE_ITER_FAILURES) &&
+ (rec->flags & FTRACE_FL_FAILED)) ||
+
+ ((iter->flags & FTRACE_ITER_FAILURES) &&
+ (!(rec->flags & FTRACE_FL_FAILED) ||
+ (rec->flags & FTRACE_FL_FREE))) ||
+
+ ((iter->flags & FTRACE_ITER_FILTER) &&
+ !(rec->flags & FTRACE_FL_FILTER)) ||
+
+ ((iter->flags & FTRACE_ITER_NOTRACE) &&
+ !(rec->flags & FTRACE_FL_NOTRACE))) {
+ rec = NULL;
+ goto retry;
+ }
+ }
+
+ iter->pos = *pos;
+
+ return rec;
+}
+
+static void *t_start(struct seq_file *m, loff_t *pos)
+{
+ struct ftrace_iterator *iter = m->private;
+ void *p = NULL;
+ loff_t l = -1;
+
+ if (*pos != iter->pos) {
+ for (p = t_next(m, p, &l); p && l < *pos; p = t_next(m, p, &l))
+ ;
+ } else {
+ l = *pos;
+ p = t_next(m, p, &l);
+ }
+
+ return p;
+}
+
+static void t_stop(struct seq_file *m, void *p)
+{
+}
+
+static int t_show(struct seq_file *m, void *v)
+{
+ struct dyn_ftrace *rec = v;
+ char str[KSYM_SYMBOL_LEN];
+
+ if (!rec)
+ return 0;
+
+ kallsyms_lookup(rec->ip, NULL, NULL, NULL, str);
+
+ seq_printf(m, "%s\n", str);
+
+ return 0;
+}
+
+static struct seq_operations show_ftrace_seq_ops = {
+ .start = t_start,
+ .next = t_next,
+ .stop = t_stop,
+ .show = t_show,
+};
+
+static int
+ftrace_avail_open(struct inode *inode, struct file *file)
+{
+ struct ftrace_iterator *iter;
+ int ret;
+
+ if (unlikely(ftrace_disabled))
+ return -ENODEV;
+
+ iter = kzalloc(sizeof(*iter), GFP_KERNEL);
+ if (!iter)
+ return -ENOMEM;
+
+ iter->pg = ftrace_pages_start;
+ iter->pos = -1;
+
+ ret = seq_open(file, &show_ftrace_seq_ops);
+ if (!ret) {
+ struct seq_file *m = file->private_data;
+
+ m->private = iter;
+ } else {
+ kfree(iter);
+ }
+
+ return ret;
+}
+
+int ftrace_avail_release(struct inode *inode, struct file *file)
+{
+ struct seq_file *m = (struct seq_file *)file->private_data;
+ struct ftrace_iterator *iter = m->private;
+
+ seq_release(inode, file);
+ kfree(iter);
+
+ return 0;
+}
+
+static int
+ftrace_failures_open(struct inode *inode, struct file *file)
+{
+ int ret;
+ struct seq_file *m;
+ struct ftrace_iterator *iter;
+
+ ret = ftrace_avail_open(inode, file);
+ if (!ret) {
+ m = (struct seq_file *)file->private_data;
+ iter = (struct ftrace_iterator *)m->private;
+ iter->flags = FTRACE_ITER_FAILURES;
+ }
+
+ return ret;
+}
+
+
+static void ftrace_filter_reset(int enable)
+{
+ struct ftrace_page *pg;
+ struct dyn_ftrace *rec;
+ unsigned long type = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE;
+ unsigned i;
+
+ /* keep kstop machine from running */
+ preempt_disable();
+ if (enable)
+ ftrace_filtered = 0;
+ pg = ftrace_pages_start;
+ while (pg) {
+ for (i = 0; i < pg->index; i++) {
+ rec = &pg->records[i];
+ if (rec->flags & FTRACE_FL_FAILED)
+ continue;
+ rec->flags &= ~type;
+ }
+ pg = pg->next;
+ }
+ preempt_enable();
+}
+
+static int
+ftrace_regex_open(struct inode *inode, struct file *file, int enable)
+{
+ struct ftrace_iterator *iter;
+ int ret = 0;
+
+ if (unlikely(ftrace_disabled))
+ return -ENODEV;
+
+ iter = kzalloc(sizeof(*iter), GFP_KERNEL);
+ if (!iter)
+ return -ENOMEM;
+
+ mutex_lock(&ftrace_regex_lock);
+ if ((file->f_mode & FMODE_WRITE) &&
+ !(file->f_flags & O_APPEND))
+ ftrace_filter_reset(enable);
+
+ if (file->f_mode & FMODE_READ) {
+ iter->pg = ftrace_pages_start;
+ iter->pos = -1;
+ iter->flags = enable ? FTRACE_ITER_FILTER :
+ FTRACE_ITER_NOTRACE;
+
+ ret = seq_open(file, &show_ftrace_seq_ops);
+ if (!ret) {
+ struct seq_file *m = file->private_data;
+ m->private = iter;
+ } else
+ kfree(iter);
+ } else
+ file->private_data = iter;
+ mutex_unlock(&ftrace_regex_lock);
+
+ return ret;
+}
+
+static int
+ftrace_filter_open(struct inode *inode, struct file *file)
+{
+ return ftrace_regex_open(inode, file, 1);
+}
+
+static int
+ftrace_notrace_open(struct inode *inode, struct file *file)
+{
+ return ftrace_regex_open(inode, file, 0);
+}
+
+static ssize_t
+ftrace_regex_read(struct file *file, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ if (file->f_mode & FMODE_READ)
+ return seq_read(file, ubuf, cnt, ppos);
+ else
+ return -EPERM;
+}
+
+static loff_t
+ftrace_regex_lseek(struct file *file, loff_t offset, int origin)
+{
+ loff_t ret;
+
+ if (file->f_mode & FMODE_READ)
+ ret = seq_lseek(file, offset, origin);
+ else
+ file->f_pos = ret = 1;
+
+ return ret;
+}
+
+enum {
+ MATCH_FULL,
+ MATCH_FRONT_ONLY,
+ MATCH_MIDDLE_ONLY,
+ MATCH_END_ONLY,
+};
+
+static void
+ftrace_match(unsigned char *buff, int len, int enable)
+{
+ char str[KSYM_SYMBOL_LEN];
+ char *search = NULL;
+ struct ftrace_page *pg;
+ struct dyn_ftrace *rec;
+ int type = MATCH_FULL;
+ unsigned long flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE;
+ unsigned i, match = 0, search_len = 0;
+
+ for (i = 0; i < len; i++) {
+ if (buff[i] == '*') {
+ if (!i) {
+ search = buff + i + 1;
+ type = MATCH_END_ONLY;
+ search_len = len - (i + 1);
+ } else {
+ if (type == MATCH_END_ONLY) {
+ type = MATCH_MIDDLE_ONLY;
+ } else {
+ match = i;
+ type = MATCH_FRONT_ONLY;
+ }
+ buff[i] = 0;
+ break;
+ }
+ }
+ }
+
+ /* keep kstop machine from running */
+ preempt_disable();
+ if (enable)
+ ftrace_filtered = 1;
+ pg = ftrace_pages_start;
+ while (pg) {
+ for (i = 0; i < pg->index; i++) {
+ int matched = 0;
+ char *ptr;
+
+ rec = &pg->records[i];
+ if (rec->flags & FTRACE_FL_FAILED)
+ continue;
+ kallsyms_lookup(rec->ip, NULL, NULL, NULL, str);
+ switch (type) {
+ case MATCH_FULL:
+ if (strcmp(str, buff) == 0)
+ matched = 1;
+ break;
+ case MATCH_FRONT_ONLY:
+ if (memcmp(str, buff, match) == 0)
+ matched = 1;
+ break;
+ case MATCH_MIDDLE_ONLY:
+ if (strstr(str, search))
+ matched = 1;
+ break;
+ case MATCH_END_ONLY:
+ ptr = strstr(str, search);
+ if (ptr && (ptr[search_len] == 0))
+ matched = 1;
+ break;
+ }
+ if (matched)
+ rec->flags |= flag;
+ }
+ pg = pg->next;
+ }
+ preempt_enable();
+}
+
+static ssize_t
+ftrace_regex_write(struct file *file, const char __user *ubuf,
+ size_t cnt, loff_t *ppos, int enable)
+{
+ struct ftrace_iterator *iter;
+ char ch;
+ size_t read = 0;
+ ssize_t ret;
+
+ if (!cnt || cnt < 0)
+ return 0;
+
+ mutex_lock(&ftrace_regex_lock);
+
+ if (file->f_mode & FMODE_READ) {
+ struct seq_file *m = file->private_data;
+ iter = m->private;
+ } else
+ iter = file->private_data;
+
+ if (!*ppos) {
+ iter->flags &= ~FTRACE_ITER_CONT;
+ iter->buffer_idx = 0;
+ }
+
+ ret = get_user(ch, ubuf++);
+ if (ret)
+ goto out;
+ read++;
+ cnt--;
+
+ if (!(iter->flags & ~FTRACE_ITER_CONT)) {
+ /* skip white space */
+ while (cnt && isspace(ch)) {
+ ret = get_user(ch, ubuf++);
+ if (ret)
+ goto out;
+ read++;
+ cnt--;
+ }
+
+ if (isspace(ch)) {
+ file->f_pos += read;
+ ret = read;
+ goto out;
+ }
+
+ iter->buffer_idx = 0;
+ }
+
+ while (cnt && !isspace(ch)) {
+ if (iter->buffer_idx < FTRACE_BUFF_MAX)
+ iter->buffer[iter->buffer_idx++] = ch;
+ else {
+ ret = -EINVAL;
+ goto out;
+ }
+ ret = get_user(ch, ubuf++);
+ if (ret)
+ goto out;
+ read++;
+ cnt--;
+ }
+
+ if (isspace(ch)) {
+ iter->filtered++;
+ iter->buffer[iter->buffer_idx] = 0;
+ ftrace_match(iter->buffer, iter->buffer_idx, enable);
+ iter->buffer_idx = 0;
+ } else
+ iter->flags |= FTRACE_ITER_CONT;
+
+
+ file->f_pos += read;
+
+ ret = read;
+ out:
+ mutex_unlock(&ftrace_regex_lock);
+
+ return ret;
+}
+
+static ssize_t
+ftrace_filter_write(struct file *file, const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ return ftrace_regex_write(file, ubuf, cnt, ppos, 1);
+}
+
+static ssize_t
+ftrace_notrace_write(struct file *file, const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ return ftrace_regex_write(file, ubuf, cnt, ppos, 0);
+}
+
+static void
+ftrace_set_regex(unsigned char *buf, int len, int reset, int enable)
+{
+ if (unlikely(ftrace_disabled))
+ return;
+
+ mutex_lock(&ftrace_regex_lock);
+ if (reset)
+ ftrace_filter_reset(enable);
+ if (buf)
+ ftrace_match(buf, len, enable);
+ mutex_unlock(&ftrace_regex_lock);
+}
+
+/**
+ * ftrace_set_filter - set a function to filter on in ftrace
+ * @buf - the string that holds the function filter text.
+ * @len - the length of the string.
+ * @reset - non zero to reset all filters before applying this filter.
+ *
+ * Filters denote which functions should be enabled when tracing is enabled.
+ * If @buf is NULL and reset is set, all functions will be enabled for tracing.
+ */
+void ftrace_set_filter(unsigned char *buf, int len, int reset)
+{
+ ftrace_set_regex(buf, len, reset, 1);
+}
+
+/**
+ * ftrace_set_notrace - set a function to not trace in ftrace
+ * @buf - the string that holds the function notrace text.
+ * @len - the length of the string.
+ * @reset - non zero to reset all filters before applying this filter.
+ *
+ * Notrace Filters denote which functions should not be enabled when tracing
+ * is enabled. If @buf is NULL and reset is set, all functions will be enabled
+ * for tracing.
+ */
+void ftrace_set_notrace(unsigned char *buf, int len, int reset)
+{
+ ftrace_set_regex(buf, len, reset, 0);
+}
+
+static int
+ftrace_regex_release(struct inode *inode, struct file *file, int enable)
+{
+ struct seq_file *m = (struct seq_file *)file->private_data;
+ struct ftrace_iterator *iter;
+
+ mutex_lock(&ftrace_regex_lock);
+ if (file->f_mode & FMODE_READ) {
+ iter = m->private;
+
+ seq_release(inode, file);
+ } else
+ iter = file->private_data;
+
+ if (iter->buffer_idx) {
+ iter->filtered++;
+ iter->buffer[iter->buffer_idx] = 0;
+ ftrace_match(iter->buffer, iter->buffer_idx, enable);
+ }
+
+ mutex_lock(&ftrace_sysctl_lock);
+ mutex_lock(&ftraced_lock);
+ if (iter->filtered && ftraced_suspend && ftrace_enabled)
+ ftrace_run_update_code(FTRACE_ENABLE_CALLS);
+ mutex_unlock(&ftraced_lock);
+ mutex_unlock(&ftrace_sysctl_lock);
+
+ kfree(iter);
+ mutex_unlock(&ftrace_regex_lock);
+ return 0;
+}
+
+static int
+ftrace_filter_release(struct inode *inode, struct file *file)
+{
+ return ftrace_regex_release(inode, file, 1);
+}
+
+static int
+ftrace_notrace_release(struct inode *inode, struct file *file)
+{
+ return ftrace_regex_release(inode, file, 0);
+}
+
+static ssize_t
+ftraced_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ /* don't worry about races */
+ char *buf = ftraced_stop ? "disabled\n" : "enabled\n";
+ int r = strlen(buf);
+
+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
+}
+
+static ssize_t
+ftraced_write(struct file *filp, const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ char buf[64];
+ long val;
+ int ret;
+
+ if (cnt >= sizeof(buf))
+ return -EINVAL;
+
+ if (copy_from_user(&buf, ubuf, cnt))
+ return -EFAULT;
+
+ if (strncmp(buf, "enable", 6) == 0)
+ val = 1;
+ else if (strncmp(buf, "disable", 7) == 0)
+ val = 0;
+ else {
+ buf[cnt] = 0;
+
+ ret = strict_strtoul(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ val = !!val;
+ }
+
+ if (val)
+ ftrace_enable_daemon();
+ else
+ ftrace_disable_daemon();
+
+ filp->f_pos += cnt;
+
+ return cnt;
+}
+
+static struct file_operations ftrace_avail_fops = {
+ .open = ftrace_avail_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = ftrace_avail_release,
+};
+
+static struct file_operations ftrace_failures_fops = {
+ .open = ftrace_failures_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = ftrace_avail_release,
+};
+
+static struct file_operations ftrace_filter_fops = {
+ .open = ftrace_filter_open,
+ .read = ftrace_regex_read,
+ .write = ftrace_filter_write,
+ .llseek = ftrace_regex_lseek,
+ .release = ftrace_filter_release,
+};
+
+static struct file_operations ftrace_notrace_fops = {
+ .open = ftrace_notrace_open,
+ .read = ftrace_regex_read,
+ .write = ftrace_notrace_write,
+ .llseek = ftrace_regex_lseek,
+ .release = ftrace_notrace_release,
+};
+
+static struct file_operations ftraced_fops = {
+ .open = tracing_open_generic,
+ .read = ftraced_read,
+ .write = ftraced_write,
+};
+
+/**
+ * ftrace_force_update - force an update to all recording ftrace functions
+ */
+int ftrace_force_update(void)
+{
+ int ret = 0;
+
+ if (unlikely(ftrace_disabled))
+ return -ENODEV;
+
+ mutex_lock(&ftrace_sysctl_lock);
+ mutex_lock(&ftraced_lock);
+
+ /*
+ * If ftraced_trigger is not set, then there is nothing
+ * to update.
+ */
+ if (ftraced_trigger && !ftrace_update_code())
+ ret = -EBUSY;
+
+ mutex_unlock(&ftraced_lock);
+ mutex_unlock(&ftrace_sysctl_lock);
+
+ return ret;
+}
+
+static void ftrace_force_shutdown(void)
+{
+ struct task_struct *task;
+ int command = FTRACE_DISABLE_CALLS | FTRACE_UPDATE_TRACE_FUNC;
+
+ mutex_lock(&ftraced_lock);
+ task = ftraced_task;
+ ftraced_task = NULL;
+ ftraced_suspend = -1;
+ ftrace_run_update_code(command);
+ mutex_unlock(&ftraced_lock);
+
+ if (task)
+ kthread_stop(task);
+}
+
+static __init int ftrace_init_debugfs(void)
+{
+ struct dentry *d_tracer;
+ struct dentry *entry;
+
+ d_tracer = tracing_init_dentry();
+
+ entry = debugfs_create_file("available_filter_functions", 0444,
+ d_tracer, NULL, &ftrace_avail_fops);
+ if (!entry)
+ pr_warning("Could not create debugfs "
+ "'available_filter_functions' entry\n");
+
+ entry = debugfs_create_file("failures", 0444,
+ d_tracer, NULL, &ftrace_failures_fops);
+ if (!entry)
+ pr_warning("Could not create debugfs 'failures' entry\n");
+
+ entry = debugfs_create_file("set_ftrace_filter", 0644, d_tracer,
+ NULL, &ftrace_filter_fops);
+ if (!entry)
+ pr_warning("Could not create debugfs "
+ "'set_ftrace_filter' entry\n");
+
+ entry = debugfs_create_file("set_ftrace_notrace", 0644, d_tracer,
+ NULL, &ftrace_notrace_fops);
+ if (!entry)
+ pr_warning("Could not create debugfs "
+ "'set_ftrace_notrace' entry\n");
+
+ entry = debugfs_create_file("ftraced_enabled", 0644, d_tracer,
+ NULL, &ftraced_fops);
+ if (!entry)
+ pr_warning("Could not create debugfs "
+ "'ftraced_enabled' entry\n");
+ return 0;
+}
+
+fs_initcall(ftrace_init_debugfs);
+
+static int __init ftrace_dynamic_init(void)
+{
+ struct task_struct *p;
+ unsigned long addr;
+ int ret;
+
+ addr = (unsigned long)ftrace_record_ip;
+
+ stop_machine_run(ftrace_dyn_arch_init, &addr, NR_CPUS);
+
+ /* ftrace_dyn_arch_init places the return code in addr */
+ if (addr) {
+ ret = (int)addr;
+ goto failed;
+ }
+
+ ret = ftrace_dyn_table_alloc();
+ if (ret)
+ goto failed;
+
+ p = kthread_run(ftraced, NULL, "ftraced");
+ if (IS_ERR(p)) {
+ ret = -1;
+ goto failed;
+ }
+
+ last_ftrace_enabled = ftrace_enabled = 1;
+ ftraced_task = p;
+
+ return 0;
+
+ failed:
+ ftrace_disabled = 1;
+ return ret;
+}
+
+core_initcall(ftrace_dynamic_init);
+#else
+# define ftrace_startup() do { } while (0)
+# define ftrace_shutdown() do { } while (0)
+# define ftrace_startup_sysctl() do { } while (0)
+# define ftrace_shutdown_sysctl() do { } while (0)
+# define ftrace_force_shutdown() do { } while (0)
+#endif /* CONFIG_DYNAMIC_FTRACE */
+
+/**
+ * ftrace_kill - totally shutdown ftrace
+ *
+ * This is a safety measure. If something was detected that seems
+ * wrong, calling this function will keep ftrace from doing
+ * any more modifications, and updates.
+ * used when something went wrong.
+ */
+void ftrace_kill(void)
+{
+ mutex_lock(&ftrace_sysctl_lock);
+ ftrace_disabled = 1;
+ ftrace_enabled = 0;
+
+ clear_ftrace_function();
+ mutex_unlock(&ftrace_sysctl_lock);
+
+ /* Try to totally disable ftrace */
+ ftrace_force_shutdown();
+}
+
+/**
+ * register_ftrace_function - register a function for profiling
+ * @ops - ops structure that holds the function for profiling.
+ *
+ * Register a function to be called by all functions in the
+ * kernel.
+ *
+ * Note: @ops->func and all the functions it calls must be labeled
+ * with "notrace", otherwise it will go into a
+ * recursive loop.
+ */
+int register_ftrace_function(struct ftrace_ops *ops)
+{
+ int ret;
+
+ if (unlikely(ftrace_disabled))
+ return -1;
+
+ mutex_lock(&ftrace_sysctl_lock);
+ ret = __register_ftrace_function(ops);
+ ftrace_startup();
+ mutex_unlock(&ftrace_sysctl_lock);
+
+ return ret;
+}
+
+/**
+ * unregister_ftrace_function - unresgister a function for profiling.
+ * @ops - ops structure that holds the function to unregister
+ *
+ * Unregister a function that was added to be called by ftrace profiling.
+ */
+int unregister_ftrace_function(struct ftrace_ops *ops)
+{
+ int ret;
+
+ mutex_lock(&ftrace_sysctl_lock);
+ ret = __unregister_ftrace_function(ops);
+ ftrace_shutdown();
+ mutex_unlock(&ftrace_sysctl_lock);
+
+ return ret;
+}
+
+int
+ftrace_enable_sysctl(struct ctl_table *table, int write,
+ struct file *file, void __user *buffer, size_t *lenp,
+ loff_t *ppos)
+{
+ int ret;
+
+ if (unlikely(ftrace_disabled))
+ return -ENODEV;
+
+ mutex_lock(&ftrace_sysctl_lock);
+
+ ret = proc_dointvec(table, write, file, buffer, lenp, ppos);
+
+ if (ret || !write || (last_ftrace_enabled == ftrace_enabled))
+ goto out;
+
+ last_ftrace_enabled = ftrace_enabled;
+
+ if (ftrace_enabled) {
+
+ ftrace_startup_sysctl();
+
+ /* we are starting ftrace again */
+ if (ftrace_list != &ftrace_list_end) {
+ if (ftrace_list->next == &ftrace_list_end)
+ ftrace_trace_function = ftrace_list->func;
+ else
+ ftrace_trace_function = ftrace_list_func;
+ }
+
+ } else {
+ /* stopping ftrace calls (just send to ftrace_stub) */
+ ftrace_trace_function = ftrace_stub;
+
+ ftrace_shutdown_sysctl();
+ }
+
+ out:
+ mutex_unlock(&ftrace_sysctl_lock);
+ return ret;
+}
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
new file mode 100644
index 000000000000..2c6ffd6aad47
--- /dev/null
+++ b/kernel/trace/trace.c
@@ -0,0 +1,3143 @@
+/*
+ * ring buffer based function tracer
+ *
+ * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
+ * Copyright (C) 2008 Ingo Molnar <mingo@redhat.com>
+ *
+ * Originally taken from the RT patch by:
+ * Arnaldo Carvalho de Melo <acme@redhat.com>
+ *
+ * Based on code from the latency_tracer, that is:
+ * Copyright (C) 2004-2006 Ingo Molnar
+ * Copyright (C) 2004 William Lee Irwin III
+ */
+#include <linux/utsrelease.h>
+#include <linux/kallsyms.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+#include <linux/pagemap.h>
+#include <linux/hardirq.h>
+#include <linux/linkage.h>
+#include <linux/uaccess.h>
+#include <linux/ftrace.h>
+#include <linux/module.h>
+#include <linux/percpu.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/gfp.h>
+#include <linux/fs.h>
+#include <linux/kprobes.h>
+#include <linux/writeback.h>
+
+#include <linux/stacktrace.h>
+
+#include "trace.h"
+
+unsigned long __read_mostly tracing_max_latency = (cycle_t)ULONG_MAX;
+unsigned long __read_mostly tracing_thresh;
+
+static unsigned long __read_mostly tracing_nr_buffers;
+static cpumask_t __read_mostly tracing_buffer_mask;
+
+#define for_each_tracing_cpu(cpu) \
+ for_each_cpu_mask(cpu, tracing_buffer_mask)
+
+static int trace_alloc_page(void);
+static int trace_free_page(void);
+
+static int tracing_disabled = 1;
+
+static unsigned long tracing_pages_allocated;
+
+long
+ns2usecs(cycle_t nsec)
+{
+ nsec += 500;
+ do_div(nsec, 1000);
+ return nsec;
+}
+
+cycle_t ftrace_now(int cpu)
+{
+ return cpu_clock(cpu);
+}
+
+/*
+ * The global_trace is the descriptor that holds the tracing
+ * buffers for the live tracing. For each CPU, it contains
+ * a link list of pages that will store trace entries. The
+ * page descriptor of the pages in the memory is used to hold
+ * the link list by linking the lru item in the page descriptor
+ * to each of the pages in the buffer per CPU.
+ *
+ * For each active CPU there is a data field that holds the
+ * pages for the buffer for that CPU. Each CPU has the same number
+ * of pages allocated for its buffer.
+ */
+static struct trace_array global_trace;
+
+static DEFINE_PER_CPU(struct trace_array_cpu, global_trace_cpu);
+
+/*
+ * The max_tr is used to snapshot the global_trace when a maximum
+ * latency is reached. Some tracers will use this to store a maximum
+ * trace while it continues examining live traces.
+ *
+ * The buffers for the max_tr are set up the same as the global_trace.
+ * When a snapshot is taken, the link list of the max_tr is swapped
+ * with the link list of the global_trace and the buffers are reset for
+ * the global_trace so the tracing can continue.
+ */
+static struct trace_array max_tr;
+
+static DEFINE_PER_CPU(struct trace_array_cpu, max_data);
+
+/* tracer_enabled is used to toggle activation of a tracer */
+static int tracer_enabled = 1;
+
+/*
+ * trace_nr_entries is the number of entries that is allocated
+ * for a buffer. Note, the number of entries is always rounded
+ * to ENTRIES_PER_PAGE.
+ */
+static unsigned long trace_nr_entries = 65536UL;
+
+/* trace_types holds a link list of available tracers. */
+static struct tracer *trace_types __read_mostly;
+
+/* current_trace points to the tracer that is currently active */
+static struct tracer *current_trace __read_mostly;
+
+/*
+ * max_tracer_type_len is used to simplify the allocating of
+ * buffers to read userspace tracer names. We keep track of
+ * the longest tracer name registered.
+ */
+static int max_tracer_type_len;
+
+/*
+ * trace_types_lock is used to protect the trace_types list.
+ * This lock is also used to keep user access serialized.
+ * Accesses from userspace will grab this lock while userspace
+ * activities happen inside the kernel.
+ */
+static DEFINE_MUTEX(trace_types_lock);
+
+/* trace_wait is a waitqueue for tasks blocked on trace_poll */
+static DECLARE_WAIT_QUEUE_HEAD(trace_wait);
+
+/* trace_flags holds iter_ctrl options */
+unsigned long trace_flags = TRACE_ITER_PRINT_PARENT;
+
+static notrace void no_trace_init(struct trace_array *tr)
+{
+ int cpu;
+
+ if(tr->ctrl)
+ for_each_online_cpu(cpu)
+ tracing_reset(tr->data[cpu]);
+ tracer_enabled = 0;
+}
+
+/* dummy trace to disable tracing */
+static struct tracer no_tracer __read_mostly = {
+ .name = "none",
+ .init = no_trace_init
+};
+
+
+/**
+ * trace_wake_up - wake up tasks waiting for trace input
+ *
+ * Simply wakes up any task that is blocked on the trace_wait
+ * queue. These is used with trace_poll for tasks polling the trace.
+ */
+void trace_wake_up(void)
+{
+ /*
+ * The runqueue_is_locked() can fail, but this is the best we
+ * have for now:
+ */
+ if (!(trace_flags & TRACE_ITER_BLOCK) && !runqueue_is_locked())
+ wake_up(&trace_wait);
+}
+
+#define ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(struct trace_entry))
+
+static int __init set_nr_entries(char *str)
+{
+ unsigned long nr_entries;
+ int ret;
+
+ if (!str)
+ return 0;
+ ret = strict_strtoul(str, 0, &nr_entries);
+ /* nr_entries can not be zero */
+ if (ret < 0 || nr_entries == 0)
+ return 0;
+ trace_nr_entries = nr_entries;
+ return 1;
+}
+__setup("trace_entries=", set_nr_entries);
+
+unsigned long nsecs_to_usecs(unsigned long nsecs)
+{
+ return nsecs / 1000;
+}
+
+/*
+ * trace_flag_type is an enumeration that holds different
+ * states when a trace occurs. These are:
+ * IRQS_OFF - interrupts were disabled
+ * NEED_RESCED - reschedule is requested
+ * HARDIRQ - inside an interrupt handler
+ * SOFTIRQ - inside a softirq handler
+ */
+enum trace_flag_type {
+ TRACE_FLAG_IRQS_OFF = 0x01,
+ TRACE_FLAG_NEED_RESCHED = 0x02,
+ TRACE_FLAG_HARDIRQ = 0x04,
+ TRACE_FLAG_SOFTIRQ = 0x08,
+};
+
+/*
+ * TRACE_ITER_SYM_MASK masks the options in trace_flags that
+ * control the output of kernel symbols.
+ */
+#define TRACE_ITER_SYM_MASK \
+ (TRACE_ITER_PRINT_PARENT|TRACE_ITER_SYM_OFFSET|TRACE_ITER_SYM_ADDR)
+
+/* These must match the bit postions in trace_iterator_flags */
+static const char *trace_options[] = {
+ "print-parent",
+ "sym-offset",
+ "sym-addr",
+ "verbose",
+ "raw",
+ "hex",
+ "bin",
+ "block",
+ "stacktrace",
+ "sched-tree",
+ NULL
+};
+
+/*
+ * ftrace_max_lock is used to protect the swapping of buffers
+ * when taking a max snapshot. The buffers themselves are
+ * protected by per_cpu spinlocks. But the action of the swap
+ * needs its own lock.
+ *
+ * This is defined as a raw_spinlock_t in order to help
+ * with performance when lockdep debugging is enabled.
+ */
+static raw_spinlock_t ftrace_max_lock =
+ (raw_spinlock_t)__RAW_SPIN_LOCK_UNLOCKED;
+
+/*
+ * Copy the new maximum trace into the separate maximum-trace
+ * structure. (this way the maximum trace is permanently saved,
+ * for later retrieval via /debugfs/tracing/latency_trace)
+ */
+static void
+__update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu)
+{
+ struct trace_array_cpu *data = tr->data[cpu];
+
+ max_tr.cpu = cpu;
+ max_tr.time_start = data->preempt_timestamp;
+
+ data = max_tr.data[cpu];
+ data->saved_latency = tracing_max_latency;
+
+ memcpy(data->comm, tsk->comm, TASK_COMM_LEN);
+ data->pid = tsk->pid;
+ data->uid = tsk->uid;
+ data->nice = tsk->static_prio - 20 - MAX_RT_PRIO;
+ data->policy = tsk->policy;
+ data->rt_priority = tsk->rt_priority;
+
+ /* record this tasks comm */
+ tracing_record_cmdline(current);
+}
+
+#define CHECK_COND(cond) \
+ if (unlikely(cond)) { \
+ tracing_disabled = 1; \
+ WARN_ON(1); \
+ return -1; \
+ }
+
+/**
+ * check_pages - integrity check of trace buffers
+ *
+ * As a safty measure we check to make sure the data pages have not
+ * been corrupted.
+ */
+int check_pages(struct trace_array_cpu *data)
+{
+ struct page *page, *tmp;
+
+ CHECK_COND(data->trace_pages.next->prev != &data->trace_pages);
+ CHECK_COND(data->trace_pages.prev->next != &data->trace_pages);
+
+ list_for_each_entry_safe(page, tmp, &data->trace_pages, lru) {
+ CHECK_COND(page->lru.next->prev != &page->lru);
+ CHECK_COND(page->lru.prev->next != &page->lru);
+ }
+
+ return 0;
+}
+
+/**
+ * head_page - page address of the first page in per_cpu buffer.
+ *
+ * head_page returns the page address of the first page in
+ * a per_cpu buffer. This also preforms various consistency
+ * checks to make sure the buffer has not been corrupted.
+ */
+void *head_page(struct trace_array_cpu *data)
+{
+ struct page *page;
+
+ if (list_empty(&data->trace_pages))
+ return NULL;
+
+ page = list_entry(data->trace_pages.next, struct page, lru);
+ BUG_ON(&page->lru == &data->trace_pages);
+
+ return page_address(page);
+}
+
+/**
+ * trace_seq_printf - sequence printing of trace information
+ * @s: trace sequence descriptor
+ * @fmt: printf format string
+ *
+ * The tracer may use either sequence operations or its own
+ * copy to user routines. To simplify formating of a trace
+ * trace_seq_printf is used to store strings into a special
+ * buffer (@s). Then the output may be either used by
+ * the sequencer or pulled into another buffer.
+ */
+int
+trace_seq_printf(struct trace_seq *s, const char *fmt, ...)
+{
+ int len = (PAGE_SIZE - 1) - s->len;
+ va_list ap;
+ int ret;
+
+ if (!len)
+ return 0;
+
+ va_start(ap, fmt);
+ ret = vsnprintf(s->buffer + s->len, len, fmt, ap);
+ va_end(ap);
+
+ /* If we can't write it all, don't bother writing anything */
+ if (ret >= len)
+ return 0;
+
+ s->len += ret;
+
+ return len;
+}
+
+/**
+ * trace_seq_puts - trace sequence printing of simple string
+ * @s: trace sequence descriptor
+ * @str: simple string to record
+ *
+ * The tracer may use either the sequence operations or its own
+ * copy to user routines. This function records a simple string
+ * into a special buffer (@s) for later retrieval by a sequencer
+ * or other mechanism.
+ */
+static int
+trace_seq_puts(struct trace_seq *s, const char *str)
+{
+ int len = strlen(str);
+
+ if (len > ((PAGE_SIZE - 1) - s->len))
+ return 0;
+
+ memcpy(s->buffer + s->len, str, len);
+ s->len += len;
+
+ return len;
+}
+
+static int
+trace_seq_putc(struct trace_seq *s, unsigned char c)
+{
+ if (s->len >= (PAGE_SIZE - 1))
+ return 0;
+
+ s->buffer[s->len++] = c;
+
+ return 1;
+}
+
+static int
+trace_seq_putmem(struct trace_seq *s, void *mem, size_t len)
+{
+ if (len > ((PAGE_SIZE - 1) - s->len))
+ return 0;
+
+ memcpy(s->buffer + s->len, mem, len);
+ s->len += len;
+
+ return len;
+}
+
+#define HEX_CHARS 17
+static const char hex2asc[] = "0123456789abcdef";
+
+static int
+trace_seq_putmem_hex(struct trace_seq *s, void *mem, size_t len)
+{
+ unsigned char hex[HEX_CHARS];
+ unsigned char *data = mem;
+ unsigned char byte;
+ int i, j;
+
+ BUG_ON(len >= HEX_CHARS);
+
+#ifdef __BIG_ENDIAN
+ for (i = 0, j = 0; i < len; i++) {
+#else
+ for (i = len-1, j = 0; i >= 0; i--) {
+#endif
+ byte = data[i];
+
+ hex[j++] = hex2asc[byte & 0x0f];
+ hex[j++] = hex2asc[byte >> 4];
+ }
+ hex[j++] = ' ';
+
+ return trace_seq_putmem(s, hex, j);
+}
+
+static void
+trace_seq_reset(struct trace_seq *s)
+{
+ s->len = 0;
+ s->readpos = 0;
+}
+
+ssize_t trace_seq_to_user(struct trace_seq *s, char __user *ubuf, size_t cnt)
+{
+ int len;
+ int ret;
+
+ if (s->len <= s->readpos)
+ return -EBUSY;
+
+ len = s->len - s->readpos;
+ if (cnt > len)
+ cnt = len;
+ ret = copy_to_user(ubuf, s->buffer + s->readpos, cnt);
+ if (ret)
+ return -EFAULT;
+
+ s->readpos += len;
+ return cnt;
+}
+
+static void
+trace_print_seq(struct seq_file *m, struct trace_seq *s)
+{
+ int len = s->len >= PAGE_SIZE ? PAGE_SIZE - 1 : s->len;
+
+ s->buffer[len] = 0;
+ seq_puts(m, s->buffer);
+
+ trace_seq_reset(s);
+}
+
+/*
+ * flip the trace buffers between two trace descriptors.
+ * This usually is the buffers between the global_trace and
+ * the max_tr to record a snapshot of a current trace.
+ *
+ * The ftrace_max_lock must be held.
+ */
+static void
+flip_trace(struct trace_array_cpu *tr1, struct trace_array_cpu *tr2)
+{
+ struct list_head flip_pages;
+
+ INIT_LIST_HEAD(&flip_pages);
+
+ memcpy(&tr1->trace_head_idx, &tr2->trace_head_idx,
+ sizeof(struct trace_array_cpu) -
+ offsetof(struct trace_array_cpu, trace_head_idx));
+
+ check_pages(tr1);
+ check_pages(tr2);
+ list_splice_init(&tr1->trace_pages, &flip_pages);
+ list_splice_init(&tr2->trace_pages, &tr1->trace_pages);
+ list_splice_init(&flip_pages, &tr2->trace_pages);
+ BUG_ON(!list_empty(&flip_pages));
+ check_pages(tr1);
+ check_pages(tr2);
+}
+
+/**
+ * update_max_tr - snapshot all trace buffers from global_trace to max_tr
+ * @tr: tracer
+ * @tsk: the task with the latency
+ * @cpu: The cpu that initiated the trace.
+ *
+ * Flip the buffers between the @tr and the max_tr and record information
+ * about which task was the cause of this latency.
+ */
+void
+update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu)
+{
+ struct trace_array_cpu *data;
+ int i;
+
+ WARN_ON_ONCE(!irqs_disabled());
+ __raw_spin_lock(&ftrace_max_lock);
+ /* clear out all the previous traces */
+ for_each_tracing_cpu(i) {
+ data = tr->data[i];
+ flip_trace(max_tr.data[i], data);
+ tracing_reset(data);
+ }
+
+ __update_max_tr(tr, tsk, cpu);
+ __raw_spin_unlock(&ftrace_max_lock);
+}
+
+/**
+ * update_max_tr_single - only copy one trace over, and reset the rest
+ * @tr - tracer
+ * @tsk - task with the latency
+ * @cpu - the cpu of the buffer to copy.
+ *
+ * Flip the trace of a single CPU buffer between the @tr and the max_tr.
+ */
+void
+update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu)
+{
+ struct trace_array_cpu *data = tr->data[cpu];
+ int i;
+
+ WARN_ON_ONCE(!irqs_disabled());
+ __raw_spin_lock(&ftrace_max_lock);
+ for_each_tracing_cpu(i)
+ tracing_reset(max_tr.data[i]);
+
+ flip_trace(max_tr.data[cpu], data);
+ tracing_reset(data);
+
+ __update_max_tr(tr, tsk, cpu);
+ __raw_spin_unlock(&ftrace_max_lock);
+}
+
+/**
+ * register_tracer - register a tracer with the ftrace system.
+ * @type - the plugin for the tracer
+ *
+ * Register a new plugin tracer.
+ */
+int register_tracer(struct tracer *type)
+{
+ struct tracer *t;
+ int len;
+ int ret = 0;
+
+ if (!type->name) {
+ pr_info("Tracer must have a name\n");
+ return -1;
+ }
+
+ mutex_lock(&trace_types_lock);
+ for (t = trace_types; t; t = t->next) {
+ if (strcmp(type->name, t->name) == 0) {
+ /* already found */
+ pr_info("Trace %s already registered\n",
+ type->name);
+ ret = -1;
+ goto out;
+ }
+ }
+
+#ifdef CONFIG_FTRACE_STARTUP_TEST
+ if (type->selftest) {
+ struct tracer *saved_tracer = current_trace;
+ struct trace_array_cpu *data;
+ struct trace_array *tr = &global_trace;
+ int saved_ctrl = tr->ctrl;
+ int i;
+ /*
+ * Run a selftest on this tracer.
+ * Here we reset the trace buffer, and set the current
+ * tracer to be this tracer. The tracer can then run some
+ * internal tracing to verify that everything is in order.
+ * If we fail, we do not register this tracer.
+ */
+ for_each_tracing_cpu(i) {
+ data = tr->data[i];
+ if (!head_page(data))
+ continue;
+ tracing_reset(data);
+ }
+ current_trace = type;
+ tr->ctrl = 0;
+ /* the test is responsible for initializing and enabling */
+ pr_info("Testing tracer %s: ", type->name);
+ ret = type->selftest(type, tr);
+ /* the test is responsible for resetting too */
+ current_trace = saved_tracer;
+ tr->ctrl = saved_ctrl;
+ if (ret) {
+ printk(KERN_CONT "FAILED!\n");
+ goto out;
+ }
+ /* Only reset on passing, to avoid touching corrupted buffers */
+ for_each_tracing_cpu(i) {
+ data = tr->data[i];
+ if (!head_page(data))
+ continue;
+ tracing_reset(data);
+ }
+ printk(KERN_CONT "PASSED\n");
+ }
+#endif
+
+ type->next = trace_types;
+ trace_types = type;
+ len = strlen(type->name);
+ if (len > max_tracer_type_len)
+ max_tracer_type_len = len;
+
+ out:
+ mutex_unlock(&trace_types_lock);
+
+ return ret;
+}
+
+void unregister_tracer(struct tracer *type)
+{
+ struct tracer **t;
+ int len;
+
+ mutex_lock(&trace_types_lock);
+ for (t = &trace_types; *t; t = &(*t)->next) {
+ if (*t == type)
+ goto found;
+ }
+ pr_info("Trace %s not registered\n", type->name);
+ goto out;
+
+ found:
+ *t = (*t)->next;
+ if (strlen(type->name) != max_tracer_type_len)
+ goto out;
+
+ max_tracer_type_len = 0;
+ for (t = &trace_types; *t; t = &(*t)->next) {
+ len = strlen((*t)->name);
+ if (len > max_tracer_type_len)
+ max_tracer_type_len = len;
+ }
+ out:
+ mutex_unlock(&trace_types_lock);
+}
+
+void tracing_reset(struct trace_array_cpu *data)
+{
+ data->trace_idx = 0;
+ data->overrun = 0;
+ data->trace_head = data->trace_tail = head_page(data);
+ data->trace_head_idx = 0;
+ data->trace_tail_idx = 0;
+}
+
+#define SAVED_CMDLINES 128
+static unsigned map_pid_to_cmdline[PID_MAX_DEFAULT+1];
+static unsigned map_cmdline_to_pid[SAVED_CMDLINES];
+static char saved_cmdlines[SAVED_CMDLINES][TASK_COMM_LEN];
+static int cmdline_idx;
+static DEFINE_SPINLOCK(trace_cmdline_lock);
+
+/* temporary disable recording */
+atomic_t trace_record_cmdline_disabled __read_mostly;
+
+static void trace_init_cmdlines(void)
+{
+ memset(&map_pid_to_cmdline, -1, sizeof(map_pid_to_cmdline));
+ memset(&map_cmdline_to_pid, -1, sizeof(map_cmdline_to_pid));
+ cmdline_idx = 0;
+}
+
+void trace_stop_cmdline_recording(void);
+
+static void trace_save_cmdline(struct task_struct *tsk)
+{
+ unsigned map;
+ unsigned idx;
+
+ if (!tsk->pid || unlikely(tsk->pid > PID_MAX_DEFAULT))
+ return;
+
+ /*
+ * It's not the end of the world if we don't get
+ * the lock, but we also don't want to spin
+ * nor do we want to disable interrupts,
+ * so if we miss here, then better luck next time.
+ */
+ if (!spin_trylock(&trace_cmdline_lock))
+ return;
+
+ idx = map_pid_to_cmdline[tsk->pid];
+ if (idx >= SAVED_CMDLINES) {
+ idx = (cmdline_idx + 1) % SAVED_CMDLINES;
+
+ map = map_cmdline_to_pid[idx];
+ if (map <= PID_MAX_DEFAULT)
+ map_pid_to_cmdline[map] = (unsigned)-1;
+
+ map_pid_to_cmdline[tsk->pid] = idx;
+
+ cmdline_idx = idx;
+ }
+
+ memcpy(&saved_cmdlines[idx], tsk->comm, TASK_COMM_LEN);
+
+ spin_unlock(&trace_cmdline_lock);
+}
+
+static char *trace_find_cmdline(int pid)
+{
+ char *cmdline = "<...>";
+ unsigned map;
+
+ if (!pid)
+ return "<idle>";
+
+ if (pid > PID_MAX_DEFAULT)
+ goto out;
+
+ map = map_pid_to_cmdline[pid];
+ if (map >= SAVED_CMDLINES)
+ goto out;
+
+ cmdline = saved_cmdlines[map];
+
+ out:
+ return cmdline;
+}
+
+void tracing_record_cmdline(struct task_struct *tsk)
+{
+ if (atomic_read(&trace_record_cmdline_disabled))
+ return;
+
+ trace_save_cmdline(tsk);
+}
+
+static inline struct list_head *
+trace_next_list(struct trace_array_cpu *data, struct list_head *next)
+{
+ /*
+ * Roundrobin - but skip the head (which is not a real page):
+ */
+ next = next->next;
+ if (unlikely(next == &data->trace_pages))
+ next = next->next;
+ BUG_ON(next == &data->trace_pages);
+
+ return next;
+}
+
+static inline void *
+trace_next_page(struct trace_array_cpu *data, void *addr)
+{
+ struct list_head *next;
+ struct page *page;
+
+ page = virt_to_page(addr);
+
+ next = trace_next_list(data, &page->lru);
+ page = list_entry(next, struct page, lru);
+
+ return page_address(page);
+}
+
+static inline struct trace_entry *
+tracing_get_trace_entry(struct trace_array *tr, struct trace_array_cpu *data)
+{
+ unsigned long idx, idx_next;
+ struct trace_entry *entry;
+
+ data->trace_idx++;
+ idx = data->trace_head_idx;
+ idx_next = idx + 1;
+
+ BUG_ON(idx * TRACE_ENTRY_SIZE >= PAGE_SIZE);
+
+ entry = data->trace_head + idx * TRACE_ENTRY_SIZE;
+
+ if (unlikely(idx_next >= ENTRIES_PER_PAGE)) {
+ data->trace_head = trace_next_page(data, data->trace_head);
+ idx_next = 0;
+ }
+
+ if (data->trace_head == data->trace_tail &&
+ idx_next == data->trace_tail_idx) {
+ /* overrun */
+ data->overrun++;
+ data->trace_tail_idx++;
+ if (data->trace_tail_idx >= ENTRIES_PER_PAGE) {
+ data->trace_tail =
+ trace_next_page(data, data->trace_tail);
+ data->trace_tail_idx = 0;
+ }
+ }
+
+ data->trace_head_idx = idx_next;
+
+ return entry;
+}
+
+static inline void
+tracing_generic_entry_update(struct trace_entry *entry, unsigned long flags)
+{
+ struct task_struct *tsk = current;
+ unsigned long pc;
+
+ pc = preempt_count();
+
+ entry->preempt_count = pc & 0xff;
+ entry->pid = (tsk) ? tsk->pid : 0;
+ entry->t = ftrace_now(raw_smp_processor_id());
+ entry->flags = (irqs_disabled_flags(flags) ? TRACE_FLAG_IRQS_OFF : 0) |
+ ((pc & HARDIRQ_MASK) ? TRACE_FLAG_HARDIRQ : 0) |
+ ((pc & SOFTIRQ_MASK) ? TRACE_FLAG_SOFTIRQ : 0) |
+ (need_resched() ? TRACE_FLAG_NEED_RESCHED : 0);
+}
+
+void
+trace_function(struct trace_array *tr, struct trace_array_cpu *data,
+ unsigned long ip, unsigned long parent_ip, unsigned long flags)
+{
+ struct trace_entry *entry;
+ unsigned long irq_flags;
+
+ raw_local_irq_save(irq_flags);
+ __raw_spin_lock(&data->lock);
+ entry = tracing_get_trace_entry(tr, data);
+ tracing_generic_entry_update(entry, flags);
+ entry->type = TRACE_FN;
+ entry->fn.ip = ip;
+ entry->fn.parent_ip = parent_ip;
+ __raw_spin_unlock(&data->lock);
+ raw_local_irq_restore(irq_flags);
+}
+
+void
+ftrace(struct trace_array *tr, struct trace_array_cpu *data,
+ unsigned long ip, unsigned long parent_ip, unsigned long flags)
+{
+ if (likely(!atomic_read(&data->disabled)))
+ trace_function(tr, data, ip, parent_ip, flags);
+}
+
+#ifdef CONFIG_MMIOTRACE
+void __trace_mmiotrace_rw(struct trace_array *tr, struct trace_array_cpu *data,
+ struct mmiotrace_rw *rw)
+{
+ struct trace_entry *entry;
+ unsigned long irq_flags;
+
+ raw_local_irq_save(irq_flags);
+ __raw_spin_lock(&data->lock);
+
+ entry = tracing_get_trace_entry(tr, data);
+ tracing_generic_entry_update(entry, 0);
+ entry->type = TRACE_MMIO_RW;
+ entry->mmiorw = *rw;
+
+ __raw_spin_unlock(&data->lock);
+ raw_local_irq_restore(irq_flags);
+
+ trace_wake_up();
+}
+
+void __trace_mmiotrace_map(struct trace_array *tr, struct trace_array_cpu *data,
+ struct mmiotrace_map *map)
+{
+ struct trace_entry *entry;
+ unsigned long irq_flags;
+
+ raw_local_irq_save(irq_flags);
+ __raw_spin_lock(&data->lock);
+
+ entry = tracing_get_trace_entry(tr, data);
+ tracing_generic_entry_update(entry, 0);
+ entry->type = TRACE_MMIO_MAP;
+ entry->mmiomap = *map;
+
+ __raw_spin_unlock(&data->lock);
+ raw_local_irq_restore(irq_flags);
+
+ trace_wake_up();
+}
+#endif
+
+void __trace_stack(struct trace_array *tr,
+ struct trace_array_cpu *data,
+ unsigned long flags,
+ int skip)
+{
+ struct trace_entry *entry;
+ struct stack_trace trace;
+
+ if (!(trace_flags & TRACE_ITER_STACKTRACE))
+ return;
+
+ entry = tracing_get_trace_entry(tr, data);
+ tracing_generic_entry_update(entry, flags);
+ entry->type = TRACE_STACK;
+
+ memset(&entry->stack, 0, sizeof(entry->stack));
+
+ trace.nr_entries = 0;
+ trace.max_entries = FTRACE_STACK_ENTRIES;
+ trace.skip = skip;
+ trace.entries = entry->stack.caller;
+
+ save_stack_trace(&trace);
+}
+
+void
+__trace_special(void *__tr, void *__data,
+ unsigned long arg1, unsigned long arg2, unsigned long arg3)
+{
+ struct trace_array_cpu *data = __data;
+ struct trace_array *tr = __tr;
+ struct trace_entry *entry;
+ unsigned long irq_flags;
+
+ raw_local_irq_save(irq_flags);
+ __raw_spin_lock(&data->lock);
+ entry = tracing_get_trace_entry(tr, data);
+ tracing_generic_entry_update(entry, 0);
+ entry->type = TRACE_SPECIAL;
+ entry->special.arg1 = arg1;
+ entry->special.arg2 = arg2;
+ entry->special.arg3 = arg3;
+ __trace_stack(tr, data, irq_flags, 4);
+ __raw_spin_unlock(&data->lock);
+ raw_local_irq_restore(irq_flags);
+
+ trace_wake_up();
+}
+
+void
+tracing_sched_switch_trace(struct trace_array *tr,
+ struct trace_array_cpu *data,
+ struct task_struct *prev,
+ struct task_struct *next,
+ unsigned long flags)
+{
+ struct trace_entry *entry;
+ unsigned long irq_flags;
+
+ raw_local_irq_save(irq_flags);
+ __raw_spin_lock(&data->lock);
+ entry = tracing_get_trace_entry(tr, data);
+ tracing_generic_entry_update(entry, flags);
+ entry->type = TRACE_CTX;
+ entry->ctx.prev_pid = prev->pid;
+ entry->ctx.prev_prio = prev->prio;
+ entry->ctx.prev_state = prev->state;
+ entry->ctx.next_pid = next->pid;
+ entry->ctx.next_prio = next->prio;
+ entry->ctx.next_state = next->state;
+ __trace_stack(tr, data, flags, 5);
+ __raw_spin_unlock(&data->lock);
+ raw_local_irq_restore(irq_flags);
+}
+
+void
+tracing_sched_wakeup_trace(struct trace_array *tr,
+ struct trace_array_cpu *data,
+ struct task_struct *wakee,
+ struct task_struct *curr,
+ unsigned long flags)
+{
+ struct trace_entry *entry;
+ unsigned long irq_flags;
+
+ raw_local_irq_save(irq_flags);
+ __raw_spin_lock(&data->lock);
+ entry = tracing_get_trace_entry(tr, data);
+ tracing_generic_entry_update(entry, flags);
+ entry->type = TRACE_WAKE;
+ entry->ctx.prev_pid = curr->pid;
+ entry->ctx.prev_prio = curr->prio;
+ entry->ctx.prev_state = curr->state;
+ entry->ctx.next_pid = wakee->pid;
+ entry->ctx.next_prio = wakee->prio;
+ entry->ctx.next_state = wakee->state;
+ __trace_stack(tr, data, flags, 6);
+ __raw_spin_unlock(&data->lock);
+ raw_local_irq_restore(irq_flags);
+
+ trace_wake_up();
+}
+
+void
+ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3)
+{
+ struct trace_array *tr = &global_trace;
+ struct trace_array_cpu *data;
+ unsigned long flags;
+ long disabled;
+ int cpu;
+
+ if (tracing_disabled || current_trace == &no_tracer || !tr->ctrl)
+ return;
+
+ local_irq_save(flags);
+ cpu = raw_smp_processor_id();
+ data = tr->data[cpu];
+ disabled = atomic_inc_return(&data->disabled);
+
+ if (likely(disabled == 1))
+ __trace_special(tr, data, arg1, arg2, arg3);
+
+ atomic_dec(&data->disabled);
+ local_irq_restore(flags);
+}
+
+#ifdef CONFIG_FTRACE
+static void
+function_trace_call(unsigned long ip, unsigned long parent_ip)
+{
+ struct trace_array *tr = &global_trace;
+ struct trace_array_cpu *data;
+ unsigned long flags;
+ long disabled;
+ int cpu;
+
+ if (unlikely(!tracer_enabled))
+ return;
+
+ local_irq_save(flags);
+ cpu = raw_smp_processor_id();
+ data = tr->data[cpu];
+ disabled = atomic_inc_return(&data->disabled);
+
+ if (likely(disabled == 1))
+ trace_function(tr, data, ip, parent_ip, flags);
+
+ atomic_dec(&data->disabled);
+ local_irq_restore(flags);
+}
+
+static struct ftrace_ops trace_ops __read_mostly =
+{
+ .func = function_trace_call,
+};
+
+void tracing_start_function_trace(void)
+{
+ register_ftrace_function(&trace_ops);
+}
+
+void tracing_stop_function_trace(void)
+{
+ unregister_ftrace_function(&trace_ops);
+}
+#endif
+
+enum trace_file_type {
+ TRACE_FILE_LAT_FMT = 1,
+};
+
+static struct trace_entry *
+trace_entry_idx(struct trace_array *tr, struct trace_array_cpu *data,
+ struct trace_iterator *iter, int cpu)
+{
+ struct page *page;
+ struct trace_entry *array;
+
+ if (iter->next_idx[cpu] >= tr->entries ||
+ iter->next_idx[cpu] >= data->trace_idx ||
+ (data->trace_head == data->trace_tail &&
+ data->trace_head_idx == data->trace_tail_idx))
+ return NULL;
+
+ if (!iter->next_page[cpu]) {
+ /* Initialize the iterator for this cpu trace buffer */
+ WARN_ON(!data->trace_tail);
+ page = virt_to_page(data->trace_tail);
+ iter->next_page[cpu] = &page->lru;
+ iter->next_page_idx[cpu] = data->trace_tail_idx;
+ }
+
+ page = list_entry(iter->next_page[cpu], struct page, lru);
+ BUG_ON(&data->trace_pages == &page->lru);
+
+ array = page_address(page);
+
+ WARN_ON(iter->next_page_idx[cpu] >= ENTRIES_PER_PAGE);
+ return &array[iter->next_page_idx[cpu]];
+}
+
+static struct trace_entry *
+find_next_entry(struct trace_iterator *iter, int *ent_cpu)
+{
+ struct trace_array *tr = iter->tr;
+ struct trace_entry *ent, *next = NULL;
+ int next_cpu = -1;
+ int cpu;
+
+ for_each_tracing_cpu(cpu) {
+ if (!head_page(tr->data[cpu]))
+ continue;
+ ent = trace_entry_idx(tr, tr->data[cpu], iter, cpu);
+ /*
+ * Pick the entry with the smallest timestamp:
+ */
+ if (ent && (!next || ent->t < next->t)) {
+ next = ent;
+ next_cpu = cpu;
+ }
+ }
+
+ if (ent_cpu)
+ *ent_cpu = next_cpu;
+
+ return next;
+}
+
+static void trace_iterator_increment(struct trace_iterator *iter)
+{
+ iter->idx++;
+ iter->next_idx[iter->cpu]++;
+ iter->next_page_idx[iter->cpu]++;
+
+ if (iter->next_page_idx[iter->cpu] >= ENTRIES_PER_PAGE) {
+ struct trace_array_cpu *data = iter->tr->data[iter->cpu];
+
+ iter->next_page_idx[iter->cpu] = 0;
+ iter->next_page[iter->cpu] =
+ trace_next_list(data, iter->next_page[iter->cpu]);
+ }
+}
+
+static void trace_consume(struct trace_iterator *iter)
+{
+ struct trace_array_cpu *data = iter->tr->data[iter->cpu];
+
+ data->trace_tail_idx++;
+ if (data->trace_tail_idx >= ENTRIES_PER_PAGE) {
+ data->trace_tail = trace_next_page(data, data->trace_tail);
+ data->trace_tail_idx = 0;
+ }
+
+ /* Check if we empty it, then reset the index */
+ if (data->trace_head == data->trace_tail &&
+ data->trace_head_idx == data->trace_tail_idx)
+ data->trace_idx = 0;
+}
+
+static void *find_next_entry_inc(struct trace_iterator *iter)
+{
+ struct trace_entry *next;
+ int next_cpu = -1;
+
+ next = find_next_entry(iter, &next_cpu);
+
+ iter->prev_ent = iter->ent;
+ iter->prev_cpu = iter->cpu;
+
+ iter->ent = next;
+ iter->cpu = next_cpu;
+
+ if (next)
+ trace_iterator_increment(iter);
+
+ return next ? iter : NULL;
+}
+
+static void *s_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ struct trace_iterator *iter = m->private;
+ void *last_ent = iter->ent;
+ int i = (int)*pos;
+ void *ent;
+
+ (*pos)++;
+
+ /* can't go backwards */
+ if (iter->idx > i)
+ return NULL;
+
+ if (iter->idx < 0)
+ ent = find_next_entry_inc(iter);
+ else
+ ent = iter;
+
+ while (ent && iter->idx < i)
+ ent = find_next_entry_inc(iter);
+
+ iter->pos = *pos;
+
+ if (last_ent && !ent)
+ seq_puts(m, "\n\nvim:ft=help\n");
+
+ return ent;
+}
+
+static void *s_start(struct seq_file *m, loff_t *pos)
+{
+ struct trace_iterator *iter = m->private;
+ void *p = NULL;
+ loff_t l = 0;
+ int i;
+
+ mutex_lock(&trace_types_lock);
+
+ if (!current_trace || current_trace != iter->trace) {
+ mutex_unlock(&trace_types_lock);
+ return NULL;
+ }
+
+ atomic_inc(&trace_record_cmdline_disabled);
+
+ /* let the tracer grab locks here if needed */
+ if (current_trace->start)
+ current_trace->start(iter);
+
+ if (*pos != iter->pos) {
+ iter->ent = NULL;
+ iter->cpu = 0;
+ iter->idx = -1;
+ iter->prev_ent = NULL;
+ iter->prev_cpu = -1;
+
+ for_each_tracing_cpu(i) {
+ iter->next_idx[i] = 0;
+ iter->next_page[i] = NULL;
+ }
+
+ for (p = iter; p && l < *pos; p = s_next(m, p, &l))
+ ;
+
+ } else {
+ l = *pos - 1;
+ p = s_next(m, p, &l);
+ }
+
+ return p;
+}
+
+static void s_stop(struct seq_file *m, void *p)
+{
+ struct trace_iterator *iter = m->private;
+
+ atomic_dec(&trace_record_cmdline_disabled);
+
+ /* let the tracer release locks here if needed */
+ if (current_trace && current_trace == iter->trace && iter->trace->stop)
+ iter->trace->stop(iter);
+
+ mutex_unlock(&trace_types_lock);
+}
+
+#define KRETPROBE_MSG "[unknown/kretprobe'd]"
+
+#ifdef CONFIG_KRETPROBES
+static inline int kretprobed(unsigned long addr)
+{
+ return addr == (unsigned long)kretprobe_trampoline;
+}
+#else
+static inline int kretprobed(unsigned long addr)
+{
+ return 0;
+}
+#endif /* CONFIG_KRETPROBES */
+
+static int
+seq_print_sym_short(struct trace_seq *s, const char *fmt, unsigned long address)
+{
+#ifdef CONFIG_KALLSYMS
+ char str[KSYM_SYMBOL_LEN];
+
+ kallsyms_lookup(address, NULL, NULL, NULL, str);
+
+ return trace_seq_printf(s, fmt, str);
+#endif
+ return 1;
+}
+
+static int
+seq_print_sym_offset(struct trace_seq *s, const char *fmt,
+ unsigned long address)
+{
+#ifdef CONFIG_KALLSYMS
+ char str[KSYM_SYMBOL_LEN];
+
+ sprint_symbol(str, address);
+ return trace_seq_printf(s, fmt, str);
+#endif
+ return 1;
+}
+
+#ifndef CONFIG_64BIT
+# define IP_FMT "%08lx"
+#else
+# define IP_FMT "%016lx"
+#endif
+
+static int
+seq_print_ip_sym(struct trace_seq *s, unsigned long ip, unsigned long sym_flags)
+{
+ int ret;
+
+ if (!ip)
+ return trace_seq_printf(s, "0");
+
+ if (sym_flags & TRACE_ITER_SYM_OFFSET)
+ ret = seq_print_sym_offset(s, "%s", ip);
+ else
+ ret = seq_print_sym_short(s, "%s", ip);
+
+ if (!ret)
+ return 0;
+
+ if (sym_flags & TRACE_ITER_SYM_ADDR)
+ ret = trace_seq_printf(s, " <" IP_FMT ">", ip);
+ return ret;
+}
+
+static void print_lat_help_header(struct seq_file *m)
+{
+ seq_puts(m, "# _------=> CPU# \n");
+ seq_puts(m, "# / _-----=> irqs-off \n");
+ seq_puts(m, "# | / _----=> need-resched \n");
+ seq_puts(m, "# || / _---=> hardirq/softirq \n");
+ seq_puts(m, "# ||| / _--=> preempt-depth \n");
+ seq_puts(m, "# |||| / \n");
+ seq_puts(m, "# ||||| delay \n");
+ seq_puts(m, "# cmd pid ||||| time | caller \n");
+ seq_puts(m, "# \\ / ||||| \\ | / \n");
+}
+
+static void print_func_help_header(struct seq_file *m)
+{
+ seq_puts(m, "# TASK-PID CPU# TIMESTAMP FUNCTION\n");
+ seq_puts(m, "# | | | | |\n");
+}
+
+
+static void
+print_trace_header(struct seq_file *m, struct trace_iterator *iter)
+{
+ unsigned long sym_flags = (trace_flags & TRACE_ITER_SYM_MASK);
+ struct trace_array *tr = iter->tr;
+ struct trace_array_cpu *data = tr->data[tr->cpu];
+ struct tracer *type = current_trace;
+ unsigned long total = 0;
+ unsigned long entries = 0;
+ int cpu;
+ const char *name = "preemption";
+
+ if (type)
+ name = type->name;
+
+ for_each_tracing_cpu(cpu) {
+ if (head_page(tr->data[cpu])) {
+ total += tr->data[cpu]->trace_idx;
+ if (tr->data[cpu]->trace_idx > tr->entries)
+ entries += tr->entries;
+ else
+ entries += tr->data[cpu]->trace_idx;
+ }
+ }
+
+ seq_printf(m, "%s latency trace v1.1.5 on %s\n",
+ name, UTS_RELEASE);
+ seq_puts(m, "-----------------------------------"
+ "---------------------------------\n");
+ seq_printf(m, " latency: %lu us, #%lu/%lu, CPU#%d |"
+ " (M:%s VP:%d, KP:%d, SP:%d HP:%d",
+ nsecs_to_usecs(data->saved_latency),
+ entries,
+ total,
+ tr->cpu,
+#if defined(CONFIG_PREEMPT_NONE)
+ "server",
+#elif defined(CONFIG_PREEMPT_VOLUNTARY)
+ "desktop",
+#elif defined(CONFIG_PREEMPT_DESKTOP)
+ "preempt",
+#else
+ "unknown",
+#endif
+ /* These are reserved for later use */
+ 0, 0, 0, 0);
+#ifdef CONFIG_SMP
+ seq_printf(m, " #P:%d)\n", num_online_cpus());
+#else
+ seq_puts(m, ")\n");
+#endif
+ seq_puts(m, " -----------------\n");
+ seq_printf(m, " | task: %.16s-%d "
+ "(uid:%d nice:%ld policy:%ld rt_prio:%ld)\n",
+ data->comm, data->pid, data->uid, data->nice,
+ data->policy, data->rt_priority);
+ seq_puts(m, " -----------------\n");
+
+ if (data->critical_start) {
+ seq_puts(m, " => started at: ");
+ seq_print_ip_sym(&iter->seq, data->critical_start, sym_flags);
+ trace_print_seq(m, &iter->seq);
+ seq_puts(m, "\n => ended at: ");
+ seq_print_ip_sym(&iter->seq, data->critical_end, sym_flags);
+ trace_print_seq(m, &iter->seq);
+ seq_puts(m, "\n");
+ }
+
+ seq_puts(m, "\n");
+}
+
+static void
+lat_print_generic(struct trace_seq *s, struct trace_entry *entry, int cpu)
+{
+ int hardirq, softirq;
+ char *comm;
+
+ comm = trace_find_cmdline(entry->pid);
+
+ trace_seq_printf(s, "%8.8s-%-5d ", comm, entry->pid);
+ trace_seq_printf(s, "%d", cpu);
+ trace_seq_printf(s, "%c%c",
+ (entry->flags & TRACE_FLAG_IRQS_OFF) ? 'd' : '.',
+ ((entry->flags & TRACE_FLAG_NEED_RESCHED) ? 'N' : '.'));
+
+ hardirq = entry->flags & TRACE_FLAG_HARDIRQ;
+ softirq = entry->flags & TRACE_FLAG_SOFTIRQ;
+ if (hardirq && softirq) {
+ trace_seq_putc(s, 'H');
+ } else {
+ if (hardirq) {
+ trace_seq_putc(s, 'h');
+ } else {
+ if (softirq)
+ trace_seq_putc(s, 's');
+ else
+ trace_seq_putc(s, '.');
+ }
+ }
+
+ if (entry->preempt_count)
+ trace_seq_printf(s, "%x", entry->preempt_count);
+ else
+ trace_seq_puts(s, ".");
+}
+
+unsigned long preempt_mark_thresh = 100;
+
+static void
+lat_print_timestamp(struct trace_seq *s, unsigned long long abs_usecs,
+ unsigned long rel_usecs)
+{
+ trace_seq_printf(s, " %4lldus", abs_usecs);
+ if (rel_usecs > preempt_mark_thresh)
+ trace_seq_puts(s, "!: ");
+ else if (rel_usecs > 1)
+ trace_seq_puts(s, "+: ");
+ else
+ trace_seq_puts(s, " : ");
+}
+
+static const char state_to_char[] = TASK_STATE_TO_CHAR_STR;
+
+static int
+print_lat_fmt(struct trace_iterator *iter, unsigned int trace_idx, int cpu)
+{
+ struct trace_seq *s = &iter->seq;
+ unsigned long sym_flags = (trace_flags & TRACE_ITER_SYM_MASK);
+ struct trace_entry *next_entry = find_next_entry(iter, NULL);
+ unsigned long verbose = (trace_flags & TRACE_ITER_VERBOSE);
+ struct trace_entry *entry = iter->ent;
+ unsigned long abs_usecs;
+ unsigned long rel_usecs;
+ char *comm;
+ int S, T;
+ int i;
+ unsigned state;
+
+ if (!next_entry)
+ next_entry = entry;
+ rel_usecs = ns2usecs(next_entry->t - entry->t);
+ abs_usecs = ns2usecs(entry->t - iter->tr->time_start);
+
+ if (verbose) {
+ comm = trace_find_cmdline(entry->pid);
+ trace_seq_printf(s, "%16s %5d %d %d %08x %08x [%08lx]"
+ " %ld.%03ldms (+%ld.%03ldms): ",
+ comm,
+ entry->pid, cpu, entry->flags,
+ entry->preempt_count, trace_idx,
+ ns2usecs(entry->t),
+ abs_usecs/1000,
+ abs_usecs % 1000, rel_usecs/1000,
+ rel_usecs % 1000);
+ } else {
+ lat_print_generic(s, entry, cpu);
+ lat_print_timestamp(s, abs_usecs, rel_usecs);
+ }
+ switch (entry->type) {
+ case TRACE_FN:
+ seq_print_ip_sym(s, entry->fn.ip, sym_flags);
+ trace_seq_puts(s, " (");
+ if (kretprobed(entry->fn.parent_ip))
+ trace_seq_puts(s, KRETPROBE_MSG);
+ else
+ seq_print_ip_sym(s, entry->fn.parent_ip, sym_flags);
+ trace_seq_puts(s, ")\n");
+ break;
+ case TRACE_CTX:
+ case TRACE_WAKE:
+ T = entry->ctx.next_state < sizeof(state_to_char) ?
+ state_to_char[entry->ctx.next_state] : 'X';
+
+ state = entry->ctx.prev_state ? __ffs(entry->ctx.prev_state) + 1 : 0;
+ S = state < sizeof(state_to_char) - 1 ? state_to_char[state] : 'X';
+ comm = trace_find_cmdline(entry->ctx.next_pid);
+ trace_seq_printf(s, " %5d:%3d:%c %s %5d:%3d:%c %s\n",
+ entry->ctx.prev_pid,
+ entry->ctx.prev_prio,
+ S, entry->type == TRACE_CTX ? "==>" : " +",
+ entry->ctx.next_pid,
+ entry->ctx.next_prio,
+ T, comm);
+ break;
+ case TRACE_SPECIAL:
+ trace_seq_printf(s, "# %ld %ld %ld\n",
+ entry->special.arg1,
+ entry->special.arg2,
+ entry->special.arg3);
+ break;
+ case TRACE_STACK:
+ for (i = 0; i < FTRACE_STACK_ENTRIES; i++) {
+ if (i)
+ trace_seq_puts(s, " <= ");
+ seq_print_ip_sym(s, entry->stack.caller[i], sym_flags);
+ }
+ trace_seq_puts(s, "\n");
+ break;
+ default:
+ trace_seq_printf(s, "Unknown type %d\n", entry->type);
+ }
+ return 1;
+}
+
+static int print_trace_fmt(struct trace_iterator *iter)
+{
+ struct trace_seq *s = &iter->seq;
+ unsigned long sym_flags = (trace_flags & TRACE_ITER_SYM_MASK);
+ struct trace_entry *entry;
+ unsigned long usec_rem;
+ unsigned long long t;
+ unsigned long secs;
+ char *comm;
+ int ret;
+ int S, T;
+ int i;
+
+ entry = iter->ent;
+
+ comm = trace_find_cmdline(iter->ent->pid);
+
+ t = ns2usecs(entry->t);
+ usec_rem = do_div(t, 1000000ULL);
+ secs = (unsigned long)t;
+
+ ret = trace_seq_printf(s, "%16s-%-5d ", comm, entry->pid);
+ if (!ret)
+ return 0;
+ ret = trace_seq_printf(s, "[%02d] ", iter->cpu);
+ if (!ret)
+ return 0;
+ ret = trace_seq_printf(s, "%5lu.%06lu: ", secs, usec_rem);
+ if (!ret)
+ return 0;
+
+ switch (entry->type) {
+ case TRACE_FN:
+ ret = seq_print_ip_sym(s, entry->fn.ip, sym_flags);
+ if (!ret)
+ return 0;
+ if ((sym_flags & TRACE_ITER_PRINT_PARENT) &&
+ entry->fn.parent_ip) {
+ ret = trace_seq_printf(s, " <-");
+ if (!ret)
+ return 0;
+ if (kretprobed(entry->fn.parent_ip))
+ ret = trace_seq_puts(s, KRETPROBE_MSG);
+ else
+ ret = seq_print_ip_sym(s, entry->fn.parent_ip,
+ sym_flags);
+ if (!ret)
+ return 0;
+ }
+ ret = trace_seq_printf(s, "\n");
+ if (!ret)
+ return 0;
+ break;
+ case TRACE_CTX:
+ case TRACE_WAKE:
+ S = entry->ctx.prev_state < sizeof(state_to_char) ?
+ state_to_char[entry->ctx.prev_state] : 'X';
+ T = entry->ctx.next_state < sizeof(state_to_char) ?
+ state_to_char[entry->ctx.next_state] : 'X';
+ ret = trace_seq_printf(s, " %5d:%3d:%c %s %5d:%3d:%c\n",
+ entry->ctx.prev_pid,
+ entry->ctx.prev_prio,
+ S,
+ entry->type == TRACE_CTX ? "==>" : " +",
+ entry->ctx.next_pid,
+ entry->ctx.next_prio,
+ T);
+ if (!ret)
+ return 0;
+ break;
+ case TRACE_SPECIAL:
+ ret = trace_seq_printf(s, "# %ld %ld %ld\n",
+ entry->special.arg1,
+ entry->special.arg2,
+ entry->special.arg3);
+ if (!ret)
+ return 0;
+ break;
+ case TRACE_STACK:
+ for (i = 0; i < FTRACE_STACK_ENTRIES; i++) {
+ if (i) {
+ ret = trace_seq_puts(s, " <= ");
+ if (!ret)
+ return 0;
+ }
+ ret = seq_print_ip_sym(s, entry->stack.caller[i],
+ sym_flags);
+ if (!ret)
+ return 0;
+ }
+ ret = trace_seq_puts(s, "\n");
+ if (!ret)
+ return 0;
+ break;
+ }
+ return 1;
+}
+
+static int print_raw_fmt(struct trace_iterator *iter)
+{
+ struct trace_seq *s = &iter->seq;
+ struct trace_entry *entry;
+ int ret;
+ int S, T;
+
+ entry = iter->ent;
+
+ ret = trace_seq_printf(s, "%d %d %llu ",
+ entry->pid, iter->cpu, entry->t);
+ if (!ret)
+ return 0;
+
+ switch (entry->type) {
+ case TRACE_FN:
+ ret = trace_seq_printf(s, "%x %x\n",
+ entry->fn.ip, entry->fn.parent_ip);
+ if (!ret)
+ return 0;
+ break;
+ case TRACE_CTX:
+ case TRACE_WAKE:
+ S = entry->ctx.prev_state < sizeof(state_to_char) ?
+ state_to_char[entry->ctx.prev_state] : 'X';
+ T = entry->ctx.next_state < sizeof(state_to_char) ?
+ state_to_char[entry->ctx.next_state] : 'X';
+ if (entry->type == TRACE_WAKE)
+ S = '+';
+ ret = trace_seq_printf(s, "%d %d %c %d %d %c\n",
+ entry->ctx.prev_pid,
+ entry->ctx.prev_prio,
+ S,
+ entry->ctx.next_pid,
+ entry->ctx.next_prio,
+ T);
+ if (!ret)
+ return 0;
+ break;
+ case TRACE_SPECIAL:
+ case TRACE_STACK:
+ ret = trace_seq_printf(s, "# %ld %ld %ld\n",
+ entry->special.arg1,
+ entry->special.arg2,
+ entry->special.arg3);
+ if (!ret)
+ return 0;
+ break;
+ }
+ return 1;
+}
+
+#define SEQ_PUT_FIELD_RET(s, x) \
+do { \
+ if (!trace_seq_putmem(s, &(x), sizeof(x))) \
+ return 0; \
+} while (0)
+
+#define SEQ_PUT_HEX_FIELD_RET(s, x) \
+do { \
+ if (!trace_seq_putmem_hex(s, &(x), sizeof(x))) \
+ return 0; \
+} while (0)
+
+static int print_hex_fmt(struct trace_iterator *iter)
+{
+ struct trace_seq *s = &iter->seq;
+ unsigned char newline = '\n';
+ struct trace_entry *entry;
+ int S, T;
+
+ entry = iter->ent;
+
+ SEQ_PUT_HEX_FIELD_RET(s, entry->pid);
+ SEQ_PUT_HEX_FIELD_RET(s, iter->cpu);
+ SEQ_PUT_HEX_FIELD_RET(s, entry->t);
+
+ switch (entry->type) {
+ case TRACE_FN:
+ SEQ_PUT_HEX_FIELD_RET(s, entry->fn.ip);
+ SEQ_PUT_HEX_FIELD_RET(s, entry->fn.parent_ip);
+ break;
+ case TRACE_CTX:
+ case TRACE_WAKE:
+ S = entry->ctx.prev_state < sizeof(state_to_char) ?
+ state_to_char[entry->ctx.prev_state] : 'X';
+ T = entry->ctx.next_state < sizeof(state_to_char) ?
+ state_to_char[entry->ctx.next_state] : 'X';
+ if (entry->type == TRACE_WAKE)
+ S = '+';
+ SEQ_PUT_HEX_FIELD_RET(s, entry->ctx.prev_pid);
+ SEQ_PUT_HEX_FIELD_RET(s, entry->ctx.prev_prio);
+ SEQ_PUT_HEX_FIELD_RET(s, S);
+ SEQ_PUT_HEX_FIELD_RET(s, entry->ctx.next_pid);
+ SEQ_PUT_HEX_FIELD_RET(s, entry->ctx.next_prio);
+ SEQ_PUT_HEX_FIELD_RET(s, entry->fn.parent_ip);
+ SEQ_PUT_HEX_FIELD_RET(s, T);
+ break;
+ case TRACE_SPECIAL:
+ case TRACE_STACK:
+ SEQ_PUT_HEX_FIELD_RET(s, entry->special.arg1);
+ SEQ_PUT_HEX_FIELD_RET(s, entry->special.arg2);
+ SEQ_PUT_HEX_FIELD_RET(s, entry->special.arg3);
+ break;
+ }
+ SEQ_PUT_FIELD_RET(s, newline);
+
+ return 1;
+}
+
+static int print_bin_fmt(struct trace_iterator *iter)
+{
+ struct trace_seq *s = &iter->seq;
+ struct trace_entry *entry;
+
+ entry = iter->ent;
+
+ SEQ_PUT_FIELD_RET(s, entry->pid);
+ SEQ_PUT_FIELD_RET(s, entry->cpu);
+ SEQ_PUT_FIELD_RET(s, entry->t);
+
+ switch (entry->type) {
+ case TRACE_FN:
+ SEQ_PUT_FIELD_RET(s, entry->fn.ip);
+ SEQ_PUT_FIELD_RET(s, entry->fn.parent_ip);
+ break;
+ case TRACE_CTX:
+ SEQ_PUT_FIELD_RET(s, entry->ctx.prev_pid);
+ SEQ_PUT_FIELD_RET(s, entry->ctx.prev_prio);
+ SEQ_PUT_FIELD_RET(s, entry->ctx.prev_state);
+ SEQ_PUT_FIELD_RET(s, entry->ctx.next_pid);
+ SEQ_PUT_FIELD_RET(s, entry->ctx.next_prio);
+ SEQ_PUT_FIELD_RET(s, entry->ctx.next_state);
+ break;
+ case TRACE_SPECIAL:
+ case TRACE_STACK:
+ SEQ_PUT_FIELD_RET(s, entry->special.arg1);
+ SEQ_PUT_FIELD_RET(s, entry->special.arg2);
+ SEQ_PUT_FIELD_RET(s, entry->special.arg3);
+ break;
+ }
+ return 1;
+}
+
+static int trace_empty(struct trace_iterator *iter)
+{
+ struct trace_array_cpu *data;
+ int cpu;
+
+ for_each_tracing_cpu(cpu) {
+ data = iter->tr->data[cpu];
+
+ if (head_page(data) && data->trace_idx &&
+ (data->trace_tail != data->trace_head ||
+ data->trace_tail_idx != data->trace_head_idx))
+ return 0;
+ }
+ return 1;
+}
+
+static int print_trace_line(struct trace_iterator *iter)
+{
+ if (iter->trace && iter->trace->print_line)
+ return iter->trace->print_line(iter);
+
+ if (trace_flags & TRACE_ITER_BIN)
+ return print_bin_fmt(iter);
+
+ if (trace_flags & TRACE_ITER_HEX)
+ return print_hex_fmt(iter);
+
+ if (trace_flags & TRACE_ITER_RAW)
+ return print_raw_fmt(iter);
+
+ if (iter->iter_flags & TRACE_FILE_LAT_FMT)
+ return print_lat_fmt(iter, iter->idx, iter->cpu);
+
+ return print_trace_fmt(iter);
+}
+
+static int s_show(struct seq_file *m, void *v)
+{
+ struct trace_iterator *iter = v;
+
+ if (iter->ent == NULL) {
+ if (iter->tr) {
+ seq_printf(m, "# tracer: %s\n", iter->trace->name);
+ seq_puts(m, "#\n");
+ }
+ if (iter->iter_flags & TRACE_FILE_LAT_FMT) {
+ /* print nothing if the buffers are empty */
+ if (trace_empty(iter))
+ return 0;
+ print_trace_header(m, iter);
+ if (!(trace_flags & TRACE_ITER_VERBOSE))
+ print_lat_help_header(m);
+ } else {
+ if (!(trace_flags & TRACE_ITER_VERBOSE))
+ print_func_help_header(m);
+ }
+ } else {
+ print_trace_line(iter);
+ trace_print_seq(m, &iter->seq);
+ }
+
+ return 0;
+}
+
+static struct seq_operations tracer_seq_ops = {
+ .start = s_start,
+ .next = s_next,
+ .stop = s_stop,
+ .show = s_show,
+};
+
+static struct trace_iterator *
+__tracing_open(struct inode *inode, struct file *file, int *ret)
+{
+ struct trace_iterator *iter;
+
+ if (tracing_disabled) {
+ *ret = -ENODEV;
+ return NULL;
+ }
+
+ iter = kzalloc(sizeof(*iter), GFP_KERNEL);
+ if (!iter) {
+ *ret = -ENOMEM;
+ goto out;
+ }
+
+ mutex_lock(&trace_types_lock);
+ if (current_trace && current_trace->print_max)
+ iter->tr = &max_tr;
+ else
+ iter->tr = inode->i_private;
+ iter->trace = current_trace;
+ iter->pos = -1;
+
+ /* TODO stop tracer */
+ *ret = seq_open(file, &tracer_seq_ops);
+ if (!*ret) {
+ struct seq_file *m = file->private_data;
+ m->private = iter;
+
+ /* stop the trace while dumping */
+ if (iter->tr->ctrl)
+ tracer_enabled = 0;
+
+ if (iter->trace && iter->trace->open)
+ iter->trace->open(iter);
+ } else {
+ kfree(iter);
+ iter = NULL;
+ }
+ mutex_unlock(&trace_types_lock);
+
+ out:
+ return iter;
+}
+
+int tracing_open_generic(struct inode *inode, struct file *filp)
+{
+ if (tracing_disabled)
+ return -ENODEV;
+
+ filp->private_data = inode->i_private;
+ return 0;
+}
+
+int tracing_release(struct inode *inode, struct file *file)
+{
+ struct seq_file *m = (struct seq_file *)file->private_data;
+ struct trace_iterator *iter = m->private;
+
+ mutex_lock(&trace_types_lock);
+ if (iter->trace && iter->trace->close)
+ iter->trace->close(iter);
+
+ /* reenable tracing if it was previously enabled */
+ if (iter->tr->ctrl)
+ tracer_enabled = 1;
+ mutex_unlock(&trace_types_lock);
+
+ seq_release(inode, file);
+ kfree(iter);
+ return 0;
+}
+
+static int tracing_open(struct inode *inode, struct file *file)
+{
+ int ret;
+
+ __tracing_open(inode, file, &ret);
+
+ return ret;
+}
+
+static int tracing_lt_open(struct inode *inode, struct file *file)
+{
+ struct trace_iterator *iter;
+ int ret;
+
+ iter = __tracing_open(inode, file, &ret);
+
+ if (!ret)
+ iter->iter_flags |= TRACE_FILE_LAT_FMT;
+
+ return ret;
+}
+
+
+static void *
+t_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ struct tracer *t = m->private;
+
+ (*pos)++;
+
+ if (t)
+ t = t->next;
+
+ m->private = t;
+
+ return t;
+}
+
+static void *t_start(struct seq_file *m, loff_t *pos)
+{
+ struct tracer *t = m->private;
+ loff_t l = 0;
+
+ mutex_lock(&trace_types_lock);
+ for (; t && l < *pos; t = t_next(m, t, &l))
+ ;
+
+ return t;
+}
+
+static void t_stop(struct seq_file *m, void *p)
+{
+ mutex_unlock(&trace_types_lock);
+}
+
+static int t_show(struct seq_file *m, void *v)
+{
+ struct tracer *t = v;
+
+ if (!t)
+ return 0;
+
+ seq_printf(m, "%s", t->name);
+ if (t->next)
+ seq_putc(m, ' ');
+ else
+ seq_putc(m, '\n');
+
+ return 0;
+}
+
+static struct seq_operations show_traces_seq_ops = {
+ .start = t_start,
+ .next = t_next,
+ .stop = t_stop,
+ .show = t_show,
+};
+
+static int show_traces_open(struct inode *inode, struct file *file)
+{
+ int ret;
+
+ if (tracing_disabled)
+ return -ENODEV;
+
+ ret = seq_open(file, &show_traces_seq_ops);
+ if (!ret) {
+ struct seq_file *m = file->private_data;
+ m->private = trace_types;
+ }
+
+ return ret;
+}
+
+static struct file_operations tracing_fops = {
+ .open = tracing_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = tracing_release,
+};
+
+static struct file_operations tracing_lt_fops = {
+ .open = tracing_lt_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = tracing_release,
+};
+
+static struct file_operations show_traces_fops = {
+ .open = show_traces_open,
+ .read = seq_read,
+ .release = seq_release,
+};
+
+/*
+ * Only trace on a CPU if the bitmask is set:
+ */
+static cpumask_t tracing_cpumask = CPU_MASK_ALL;
+
+/*
+ * When tracing/tracing_cpu_mask is modified then this holds
+ * the new bitmask we are about to install:
+ */
+static cpumask_t tracing_cpumask_new;
+
+/*
+ * The tracer itself will not take this lock, but still we want
+ * to provide a consistent cpumask to user-space:
+ */
+static DEFINE_MUTEX(tracing_cpumask_update_lock);
+
+/*
+ * Temporary storage for the character representation of the
+ * CPU bitmask (and one more byte for the newline):
+ */
+static char mask_str[NR_CPUS + 1];
+
+static ssize_t
+tracing_cpumask_read(struct file *filp, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ int len;
+
+ mutex_lock(&tracing_cpumask_update_lock);
+
+ len = cpumask_scnprintf(mask_str, count, tracing_cpumask);
+ if (count - len < 2) {
+ count = -EINVAL;
+ goto out_err;
+ }
+ len += sprintf(mask_str + len, "\n");
+ count = simple_read_from_buffer(ubuf, count, ppos, mask_str, NR_CPUS+1);
+
+out_err:
+ mutex_unlock(&tracing_cpumask_update_lock);
+
+ return count;
+}
+
+static ssize_t
+tracing_cpumask_write(struct file *filp, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ int err, cpu;
+
+ mutex_lock(&tracing_cpumask_update_lock);
+ err = cpumask_parse_user(ubuf, count, tracing_cpumask_new);
+ if (err)
+ goto err_unlock;
+
+ raw_local_irq_disable();
+ __raw_spin_lock(&ftrace_max_lock);
+ for_each_tracing_cpu(cpu) {
+ /*
+ * Increase/decrease the disabled counter if we are
+ * about to flip a bit in the cpumask:
+ */
+ if (cpu_isset(cpu, tracing_cpumask) &&
+ !cpu_isset(cpu, tracing_cpumask_new)) {
+ atomic_inc(&global_trace.data[cpu]->disabled);
+ }
+ if (!cpu_isset(cpu, tracing_cpumask) &&
+ cpu_isset(cpu, tracing_cpumask_new)) {
+ atomic_dec(&global_trace.data[cpu]->disabled);
+ }
+ }
+ __raw_spin_unlock(&ftrace_max_lock);
+ raw_local_irq_enable();
+
+ tracing_cpumask = tracing_cpumask_new;
+
+ mutex_unlock(&tracing_cpumask_update_lock);
+
+ return count;
+
+err_unlock:
+ mutex_unlock(&tracing_cpumask_update_lock);
+
+ return err;
+}
+
+static struct file_operations tracing_cpumask_fops = {
+ .open = tracing_open_generic,
+ .read = tracing_cpumask_read,
+ .write = tracing_cpumask_write,
+};
+
+static ssize_t
+tracing_iter_ctrl_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ char *buf;
+ int r = 0;
+ int len = 0;
+ int i;
+
+ /* calulate max size */
+ for (i = 0; trace_options[i]; i++) {
+ len += strlen(trace_options[i]);
+ len += 3; /* "no" and space */
+ }
+
+ /* +2 for \n and \0 */
+ buf = kmalloc(len + 2, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ for (i = 0; trace_options[i]; i++) {
+ if (trace_flags & (1 << i))
+ r += sprintf(buf + r, "%s ", trace_options[i]);
+ else
+ r += sprintf(buf + r, "no%s ", trace_options[i]);
+ }
+
+ r += sprintf(buf + r, "\n");
+ WARN_ON(r >= len + 2);
+
+ r = simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
+
+ kfree(buf);
+
+ return r;
+}
+
+static ssize_t
+tracing_iter_ctrl_write(struct file *filp, const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ char buf[64];
+ char *cmp = buf;
+ int neg = 0;
+ int i;
+
+ if (cnt >= sizeof(buf))
+ return -EINVAL;
+
+ if (copy_from_user(&buf, ubuf, cnt))
+ return -EFAULT;
+
+ buf[cnt] = 0;
+
+ if (strncmp(buf, "no", 2) == 0) {
+ neg = 1;
+ cmp += 2;
+ }
+
+ for (i = 0; trace_options[i]; i++) {
+ int len = strlen(trace_options[i]);
+
+ if (strncmp(cmp, trace_options[i], len) == 0) {
+ if (neg)
+ trace_flags &= ~(1 << i);
+ else
+ trace_flags |= (1 << i);
+ break;
+ }
+ }
+ /*
+ * If no option could be set, return an error:
+ */
+ if (!trace_options[i])
+ return -EINVAL;
+
+ filp->f_pos += cnt;
+
+ return cnt;
+}
+
+static struct file_operations tracing_iter_fops = {
+ .open = tracing_open_generic,
+ .read = tracing_iter_ctrl_read,
+ .write = tracing_iter_ctrl_write,
+};
+
+static const char readme_msg[] =
+ "tracing mini-HOWTO:\n\n"
+ "# mkdir /debug\n"
+ "# mount -t debugfs nodev /debug\n\n"
+ "# cat /debug/tracing/available_tracers\n"
+ "wakeup preemptirqsoff preemptoff irqsoff ftrace sched_switch none\n\n"
+ "# cat /debug/tracing/current_tracer\n"
+ "none\n"
+ "# echo sched_switch > /debug/tracing/current_tracer\n"
+ "# cat /debug/tracing/current_tracer\n"
+ "sched_switch\n"
+ "# cat /debug/tracing/iter_ctrl\n"
+ "noprint-parent nosym-offset nosym-addr noverbose\n"
+ "# echo print-parent > /debug/tracing/iter_ctrl\n"
+ "# echo 1 > /debug/tracing/tracing_enabled\n"
+ "# cat /debug/tracing/trace > /tmp/trace.txt\n"
+ "echo 0 > /debug/tracing/tracing_enabled\n"
+;
+
+static ssize_t
+tracing_readme_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ return simple_read_from_buffer(ubuf, cnt, ppos,
+ readme_msg, strlen(readme_msg));
+}
+
+static struct file_operations tracing_readme_fops = {
+ .open = tracing_open_generic,
+ .read = tracing_readme_read,
+};
+
+static ssize_t
+tracing_ctrl_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct trace_array *tr = filp->private_data;
+ char buf[64];
+ int r;
+
+ r = sprintf(buf, "%ld\n", tr->ctrl);
+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
+}
+
+static ssize_t
+tracing_ctrl_write(struct file *filp, const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct trace_array *tr = filp->private_data;
+ char buf[64];
+ long val;
+ int ret;
+
+ if (cnt >= sizeof(buf))
+ return -EINVAL;
+
+ if (copy_from_user(&buf, ubuf, cnt))
+ return -EFAULT;
+
+ buf[cnt] = 0;
+
+ ret = strict_strtoul(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ val = !!val;
+
+ mutex_lock(&trace_types_lock);
+ if (tr->ctrl ^ val) {
+ if (val)
+ tracer_enabled = 1;
+ else
+ tracer_enabled = 0;
+
+ tr->ctrl = val;
+
+ if (current_trace && current_trace->ctrl_update)
+ current_trace->ctrl_update(tr);
+ }
+ mutex_unlock(&trace_types_lock);
+
+ filp->f_pos += cnt;
+
+ return cnt;
+}
+
+static ssize_t
+tracing_set_trace_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ char buf[max_tracer_type_len+2];
+ int r;
+
+ mutex_lock(&trace_types_lock);
+ if (current_trace)
+ r = sprintf(buf, "%s\n", current_trace->name);
+ else
+ r = sprintf(buf, "\n");
+ mutex_unlock(&trace_types_lock);
+
+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
+}
+
+static ssize_t
+tracing_set_trace_write(struct file *filp, const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct trace_array *tr = &global_trace;
+ struct tracer *t;
+ char buf[max_tracer_type_len+1];
+ int i;
+
+ if (cnt > max_tracer_type_len)
+ cnt = max_tracer_type_len;
+
+ if (copy_from_user(&buf, ubuf, cnt))
+ return -EFAULT;
+
+ buf[cnt] = 0;
+
+ /* strip ending whitespace. */
+ for (i = cnt - 1; i > 0 && isspace(buf[i]); i--)
+ buf[i] = 0;
+
+ mutex_lock(&trace_types_lock);
+ for (t = trace_types; t; t = t->next) {
+ if (strcmp(t->name, buf) == 0)
+ break;
+ }
+ if (!t || t == current_trace)
+ goto out;
+
+ if (current_trace && current_trace->reset)
+ current_trace->reset(tr);
+
+ current_trace = t;
+ if (t->init)
+ t->init(tr);
+
+ out:
+ mutex_unlock(&trace_types_lock);
+
+ filp->f_pos += cnt;
+
+ return cnt;
+}
+
+static ssize_t
+tracing_max_lat_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ unsigned long *ptr = filp->private_data;
+ char buf[64];
+ int r;
+
+ r = snprintf(buf, sizeof(buf), "%ld\n",
+ *ptr == (unsigned long)-1 ? -1 : nsecs_to_usecs(*ptr));
+ if (r > sizeof(buf))
+ r = sizeof(buf);
+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
+}
+
+static ssize_t
+tracing_max_lat_write(struct file *filp, const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ long *ptr = filp->private_data;
+ char buf[64];
+ long val;
+ int ret;
+
+ if (cnt >= sizeof(buf))
+ return -EINVAL;
+
+ if (copy_from_user(&buf, ubuf, cnt))
+ return -EFAULT;
+
+ buf[cnt] = 0;
+
+ ret = strict_strtoul(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ *ptr = val * 1000;
+
+ return cnt;
+}
+
+static atomic_t tracing_reader;
+
+static int tracing_open_pipe(struct inode *inode, struct file *filp)
+{
+ struct trace_iterator *iter;
+
+ if (tracing_disabled)
+ return -ENODEV;
+
+ /* We only allow for reader of the pipe */
+ if (atomic_inc_return(&tracing_reader) != 1) {
+ atomic_dec(&tracing_reader);
+ return -EBUSY;
+ }
+
+ /* create a buffer to store the information to pass to userspace */
+ iter = kzalloc(sizeof(*iter), GFP_KERNEL);
+ if (!iter)
+ return -ENOMEM;
+
+ mutex_lock(&trace_types_lock);
+ iter->tr = &global_trace;
+ iter->trace = current_trace;
+ filp->private_data = iter;
+
+ if (iter->trace->pipe_open)
+ iter->trace->pipe_open(iter);
+ mutex_unlock(&trace_types_lock);
+
+ return 0;
+}
+
+static int tracing_release_pipe(struct inode *inode, struct file *file)
+{
+ struct trace_iterator *iter = file->private_data;
+
+ kfree(iter);
+ atomic_dec(&tracing_reader);
+
+ return 0;
+}
+
+static unsigned int
+tracing_poll_pipe(struct file *filp, poll_table *poll_table)
+{
+ struct trace_iterator *iter = filp->private_data;
+
+ if (trace_flags & TRACE_ITER_BLOCK) {
+ /*
+ * Always select as readable when in blocking mode
+ */
+ return POLLIN | POLLRDNORM;
+ } else {
+ if (!trace_empty(iter))
+ return POLLIN | POLLRDNORM;
+ poll_wait(filp, &trace_wait, poll_table);
+ if (!trace_empty(iter))
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+ }
+}
+
+/*
+ * Consumer reader.
+ */
+static ssize_t
+tracing_read_pipe(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct trace_iterator *iter = filp->private_data;
+ struct trace_array_cpu *data;
+ static cpumask_t mask;
+ unsigned long flags;
+#ifdef CONFIG_FTRACE
+ int ftrace_save;
+#endif
+ int cpu;
+ ssize_t sret;
+
+ /* return any leftover data */
+ sret = trace_seq_to_user(&iter->seq, ubuf, cnt);
+ if (sret != -EBUSY)
+ return sret;
+ sret = 0;
+
+ trace_seq_reset(&iter->seq);
+
+ mutex_lock(&trace_types_lock);
+ if (iter->trace->read) {
+ sret = iter->trace->read(iter, filp, ubuf, cnt, ppos);
+ if (sret)
+ goto out;
+ }
+
+ while (trace_empty(iter)) {
+
+ if ((filp->f_flags & O_NONBLOCK)) {
+ sret = -EAGAIN;
+ goto out;
+ }
+
+ /*
+ * This is a make-shift waitqueue. The reason we don't use
+ * an actual wait queue is because:
+ * 1) we only ever have one waiter
+ * 2) the tracing, traces all functions, we don't want
+ * the overhead of calling wake_up and friends
+ * (and tracing them too)
+ * Anyway, this is really very primitive wakeup.
+ */
+ set_current_state(TASK_INTERRUPTIBLE);
+ iter->tr->waiter = current;
+
+ mutex_unlock(&trace_types_lock);
+
+ /* sleep for 100 msecs, and try again. */
+ schedule_timeout(HZ/10);
+
+ mutex_lock(&trace_types_lock);
+
+ iter->tr->waiter = NULL;
+
+ if (signal_pending(current)) {
+ sret = -EINTR;
+ goto out;
+ }
+
+ if (iter->trace != current_trace)
+ goto out;
+
+ /*
+ * We block until we read something and tracing is disabled.
+ * We still block if tracing is disabled, but we have never
+ * read anything. This allows a user to cat this file, and
+ * then enable tracing. But after we have read something,
+ * we give an EOF when tracing is again disabled.
+ *
+ * iter->pos will be 0 if we haven't read anything.
+ */
+ if (!tracer_enabled && iter->pos)
+ break;
+
+ continue;
+ }
+
+ /* stop when tracing is finished */
+ if (trace_empty(iter))
+ goto out;
+
+ if (cnt >= PAGE_SIZE)
+ cnt = PAGE_SIZE - 1;
+
+ /* reset all but tr, trace, and overruns */
+ memset(&iter->seq, 0,
+ sizeof(struct trace_iterator) -
+ offsetof(struct trace_iterator, seq));
+ iter->pos = -1;
+
+ /*
+ * We need to stop all tracing on all CPUS to read the
+ * the next buffer. This is a bit expensive, but is
+ * not done often. We fill all what we can read,
+ * and then release the locks again.
+ */
+
+ cpus_clear(mask);
+ local_irq_save(flags);
+#ifdef CONFIG_FTRACE
+ ftrace_save = ftrace_enabled;
+ ftrace_enabled = 0;
+#endif
+ smp_wmb();
+ for_each_tracing_cpu(cpu) {
+ data = iter->tr->data[cpu];
+
+ if (!head_page(data) || !data->trace_idx)
+ continue;
+
+ atomic_inc(&data->disabled);
+ cpu_set(cpu, mask);
+ }
+
+ for_each_cpu_mask(cpu, mask) {
+ data = iter->tr->data[cpu];
+ __raw_spin_lock(&data->lock);
+
+ if (data->overrun > iter->last_overrun[cpu])
+ iter->overrun[cpu] +=
+ data->overrun - iter->last_overrun[cpu];
+ iter->last_overrun[cpu] = data->overrun;
+ }
+
+ while (find_next_entry_inc(iter) != NULL) {
+ int ret;
+ int len = iter->seq.len;
+
+ ret = print_trace_line(iter);
+ if (!ret) {
+ /* don't print partial lines */
+ iter->seq.len = len;
+ break;
+ }
+
+ trace_consume(iter);
+
+ if (iter->seq.len >= cnt)
+ break;
+ }
+
+ for_each_cpu_mask(cpu, mask) {
+ data = iter->tr->data[cpu];
+ __raw_spin_unlock(&data->lock);
+ }
+
+ for_each_cpu_mask(cpu, mask) {
+ data = iter->tr->data[cpu];
+ atomic_dec(&data->disabled);
+ }
+#ifdef CONFIG_FTRACE
+ ftrace_enabled = ftrace_save;
+#endif
+ local_irq_restore(flags);
+
+ /* Now copy what we have to the user */
+ sret = trace_seq_to_user(&iter->seq, ubuf, cnt);
+ if (iter->seq.readpos >= iter->seq.len)
+ trace_seq_reset(&iter->seq);
+ if (sret == -EBUSY)
+ sret = 0;
+
+out:
+ mutex_unlock(&trace_types_lock);
+
+ return sret;
+}
+
+static ssize_t
+tracing_entries_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ struct trace_array *tr = filp->private_data;
+ char buf[64];
+ int r;
+
+ r = sprintf(buf, "%lu\n", tr->entries);
+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
+}
+
+static ssize_t
+tracing_entries_write(struct file *filp, const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ unsigned long val;
+ char buf[64];
+ int i, ret;
+
+ if (cnt >= sizeof(buf))
+ return -EINVAL;
+
+ if (copy_from_user(&buf, ubuf, cnt))
+ return -EFAULT;
+
+ buf[cnt] = 0;
+
+ ret = strict_strtoul(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ /* must have at least 1 entry */
+ if (!val)
+ return -EINVAL;
+
+ mutex_lock(&trace_types_lock);
+
+ if (current_trace != &no_tracer) {
+ cnt = -EBUSY;
+ pr_info("ftrace: set current_tracer to none"
+ " before modifying buffer size\n");
+ goto out;
+ }
+
+ if (val > global_trace.entries) {
+ long pages_requested;
+ unsigned long freeable_pages;
+
+ /* make sure we have enough memory before mapping */
+ pages_requested =
+ (val + (ENTRIES_PER_PAGE-1)) / ENTRIES_PER_PAGE;
+
+ /* account for each buffer (and max_tr) */
+ pages_requested *= tracing_nr_buffers * 2;
+
+ /* Check for overflow */
+ if (pages_requested < 0) {
+ cnt = -ENOMEM;
+ goto out;
+ }
+
+ freeable_pages = determine_dirtyable_memory();
+
+ /* we only allow to request 1/4 of useable memory */
+ if (pages_requested >
+ ((freeable_pages + tracing_pages_allocated) / 4)) {
+ cnt = -ENOMEM;
+ goto out;
+ }
+
+ while (global_trace.entries < val) {
+ if (trace_alloc_page()) {
+ cnt = -ENOMEM;
+ goto out;
+ }
+ /* double check that we don't go over the known pages */
+ if (tracing_pages_allocated > pages_requested)
+ break;
+ }
+
+ } else {
+ /* include the number of entries in val (inc of page entries) */
+ while (global_trace.entries > val + (ENTRIES_PER_PAGE - 1))
+ trace_free_page();
+ }
+
+ /* check integrity */
+ for_each_tracing_cpu(i)
+ check_pages(global_trace.data[i]);
+
+ filp->f_pos += cnt;
+
+ /* If check pages failed, return ENOMEM */
+ if (tracing_disabled)
+ cnt = -ENOMEM;
+ out:
+ max_tr.entries = global_trace.entries;
+ mutex_unlock(&trace_types_lock);
+
+ return cnt;
+}
+
+static struct file_operations tracing_max_lat_fops = {
+ .open = tracing_open_generic,
+ .read = tracing_max_lat_read,
+ .write = tracing_max_lat_write,
+};
+
+static struct file_operations tracing_ctrl_fops = {
+ .open = tracing_open_generic,
+ .read = tracing_ctrl_read,
+ .write = tracing_ctrl_write,
+};
+
+static struct file_operations set_tracer_fops = {
+ .open = tracing_open_generic,
+ .read = tracing_set_trace_read,
+ .write = tracing_set_trace_write,
+};
+
+static struct file_operations tracing_pipe_fops = {
+ .open = tracing_open_pipe,
+ .poll = tracing_poll_pipe,
+ .read = tracing_read_pipe,
+ .release = tracing_release_pipe,
+};
+
+static struct file_operations tracing_entries_fops = {
+ .open = tracing_open_generic,
+ .read = tracing_entries_read,
+ .write = tracing_entries_write,
+};
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+
+static ssize_t
+tracing_read_long(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ unsigned long *p = filp->private_data;
+ char buf[64];
+ int r;
+
+ r = sprintf(buf, "%ld\n", *p);
+
+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
+}
+
+static struct file_operations tracing_read_long_fops = {
+ .open = tracing_open_generic,
+ .read = tracing_read_long,
+};
+#endif
+
+static struct dentry *d_tracer;
+
+struct dentry *tracing_init_dentry(void)
+{
+ static int once;
+
+ if (d_tracer)
+ return d_tracer;
+
+ d_tracer = debugfs_create_dir("tracing", NULL);
+
+ if (!d_tracer && !once) {
+ once = 1;
+ pr_warning("Could not create debugfs directory 'tracing'\n");
+ return NULL;
+ }
+
+ return d_tracer;
+}
+
+#ifdef CONFIG_FTRACE_SELFTEST
+/* Let selftest have access to static functions in this file */
+#include "trace_selftest.c"
+#endif
+
+static __init void tracer_init_debugfs(void)
+{
+ struct dentry *d_tracer;
+ struct dentry *entry;
+
+ d_tracer = tracing_init_dentry();
+
+ entry = debugfs_create_file("tracing_enabled", 0644, d_tracer,
+ &global_trace, &tracing_ctrl_fops);
+ if (!entry)
+ pr_warning("Could not create debugfs 'tracing_enabled' entry\n");
+
+ entry = debugfs_create_file("iter_ctrl", 0644, d_tracer,
+ NULL, &tracing_iter_fops);
+ if (!entry)
+ pr_warning("Could not create debugfs 'iter_ctrl' entry\n");
+
+ entry = debugfs_create_file("tracing_cpumask", 0644, d_tracer,
+ NULL, &tracing_cpumask_fops);
+ if (!entry)
+ pr_warning("Could not create debugfs 'tracing_cpumask' entry\n");
+
+ entry = debugfs_create_file("latency_trace", 0444, d_tracer,
+ &global_trace, &tracing_lt_fops);
+ if (!entry)
+ pr_warning("Could not create debugfs 'latency_trace' entry\n");
+
+ entry = debugfs_create_file("trace", 0444, d_tracer,
+ &global_trace, &tracing_fops);
+ if (!entry)
+ pr_warning("Could not create debugfs 'trace' entry\n");
+
+ entry = debugfs_create_file("available_tracers", 0444, d_tracer,
+ &global_trace, &show_traces_fops);
+ if (!entry)
+ pr_warning("Could not create debugfs 'trace' entry\n");
+
+ entry = debugfs_create_file("current_tracer", 0444, d_tracer,
+ &global_trace, &set_tracer_fops);
+ if (!entry)
+ pr_warning("Could not create debugfs 'trace' entry\n");
+
+ entry = debugfs_create_file("tracing_max_latency", 0644, d_tracer,
+ &tracing_max_latency,
+ &tracing_max_lat_fops);
+ if (!entry)
+ pr_warning("Could not create debugfs "
+ "'tracing_max_latency' entry\n");
+
+ entry = debugfs_create_file("tracing_thresh", 0644, d_tracer,
+ &tracing_thresh, &tracing_max_lat_fops);
+ if (!entry)
+ pr_warning("Could not create debugfs "
+ "'tracing_threash' entry\n");
+ entry = debugfs_create_file("README", 0644, d_tracer,
+ NULL, &tracing_readme_fops);
+ if (!entry)
+ pr_warning("Could not create debugfs 'README' entry\n");
+
+ entry = debugfs_create_file("trace_pipe", 0644, d_tracer,
+ NULL, &tracing_pipe_fops);
+ if (!entry)
+ pr_warning("Could not create debugfs "
+ "'tracing_threash' entry\n");
+
+ entry = debugfs_create_file("trace_entries", 0644, d_tracer,
+ &global_trace, &tracing_entries_fops);
+ if (!entry)
+ pr_warning("Could not create debugfs "
+ "'tracing_threash' entry\n");
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+ entry = debugfs_create_file("dyn_ftrace_total_info", 0444, d_tracer,
+ &ftrace_update_tot_cnt,
+ &tracing_read_long_fops);
+ if (!entry)
+ pr_warning("Could not create debugfs "
+ "'dyn_ftrace_total_info' entry\n");
+#endif
+#ifdef CONFIG_SYSPROF_TRACER
+ init_tracer_sysprof_debugfs(d_tracer);
+#endif
+}
+
+static int trace_alloc_page(void)
+{
+ struct trace_array_cpu *data;
+ struct page *page, *tmp;
+ LIST_HEAD(pages);
+ void *array;
+ unsigned pages_allocated = 0;
+ int i;
+
+ /* first allocate a page for each CPU */
+ for_each_tracing_cpu(i) {
+ array = (void *)__get_free_page(GFP_KERNEL);
+ if (array == NULL) {
+ printk(KERN_ERR "tracer: failed to allocate page"
+ "for trace buffer!\n");
+ goto free_pages;
+ }
+
+ pages_allocated++;
+ page = virt_to_page(array);
+ list_add(&page->lru, &pages);
+
+/* Only allocate if we are actually using the max trace */
+#ifdef CONFIG_TRACER_MAX_TRACE
+ array = (void *)__get_free_page(GFP_KERNEL);
+ if (array == NULL) {
+ printk(KERN_ERR "tracer: failed to allocate page"
+ "for trace buffer!\n");
+ goto free_pages;
+ }
+ pages_allocated++;
+ page = virt_to_page(array);
+ list_add(&page->lru, &pages);
+#endif
+ }
+
+ /* Now that we successfully allocate a page per CPU, add them */
+ for_each_tracing_cpu(i) {
+ data = global_trace.data[i];
+ page = list_entry(pages.next, struct page, lru);
+ list_del_init(&page->lru);
+ list_add_tail(&page->lru, &data->trace_pages);
+ ClearPageLRU(page);
+
+#ifdef CONFIG_TRACER_MAX_TRACE
+ data = max_tr.data[i];
+ page = list_entry(pages.next, struct page, lru);
+ list_del_init(&page->lru);
+ list_add_tail(&page->lru, &data->trace_pages);
+ SetPageLRU(page);
+#endif
+ }
+ tracing_pages_allocated += pages_allocated;
+ global_trace.entries += ENTRIES_PER_PAGE;
+
+ return 0;
+
+ free_pages:
+ list_for_each_entry_safe(page, tmp, &pages, lru) {
+ list_del_init(&page->lru);
+ __free_page(page);
+ }
+ return -ENOMEM;
+}
+
+static int trace_free_page(void)
+{
+ struct trace_array_cpu *data;
+ struct page *page;
+ struct list_head *p;
+ int i;
+ int ret = 0;
+
+ /* free one page from each buffer */
+ for_each_tracing_cpu(i) {
+ data = global_trace.data[i];
+ p = data->trace_pages.next;
+ if (p == &data->trace_pages) {
+ /* should never happen */
+ WARN_ON(1);
+ tracing_disabled = 1;
+ ret = -1;
+ break;
+ }
+ page = list_entry(p, struct page, lru);
+ ClearPageLRU(page);
+ list_del(&page->lru);
+ tracing_pages_allocated--;
+ tracing_pages_allocated--;
+ __free_page(page);
+
+ tracing_reset(data);
+
+#ifdef CONFIG_TRACER_MAX_TRACE
+ data = max_tr.data[i];
+ p = data->trace_pages.next;
+ if (p == &data->trace_pages) {
+ /* should never happen */
+ WARN_ON(1);
+ tracing_disabled = 1;
+ ret = -1;
+ break;
+ }
+ page = list_entry(p, struct page, lru);
+ ClearPageLRU(page);
+ list_del(&page->lru);
+ __free_page(page);
+
+ tracing_reset(data);
+#endif
+ }
+ global_trace.entries -= ENTRIES_PER_PAGE;
+
+ return ret;
+}
+
+__init static int tracer_alloc_buffers(void)
+{
+ struct trace_array_cpu *data;
+ void *array;
+ struct page *page;
+ int pages = 0;
+ int ret = -ENOMEM;
+ int i;
+
+ /* TODO: make the number of buffers hot pluggable with CPUS */
+ tracing_nr_buffers = num_possible_cpus();
+ tracing_buffer_mask = cpu_possible_map;
+
+ /* Allocate the first page for all buffers */
+ for_each_tracing_cpu(i) {
+ data = global_trace.data[i] = &per_cpu(global_trace_cpu, i);
+ max_tr.data[i] = &per_cpu(max_data, i);
+
+ array = (void *)__get_free_page(GFP_KERNEL);
+ if (array == NULL) {
+ printk(KERN_ERR "tracer: failed to allocate page"
+ "for trace buffer!\n");
+ goto free_buffers;
+ }
+
+ /* set the array to the list */
+ INIT_LIST_HEAD(&data->trace_pages);
+ page = virt_to_page(array);
+ list_add(&page->lru, &data->trace_pages);
+ /* use the LRU flag to differentiate the two buffers */
+ ClearPageLRU(page);
+
+ data->lock = (raw_spinlock_t)__RAW_SPIN_LOCK_UNLOCKED;
+ max_tr.data[i]->lock = (raw_spinlock_t)__RAW_SPIN_LOCK_UNLOCKED;
+
+/* Only allocate if we are actually using the max trace */
+#ifdef CONFIG_TRACER_MAX_TRACE
+ array = (void *)__get_free_page(GFP_KERNEL);
+ if (array == NULL) {
+ printk(KERN_ERR "tracer: failed to allocate page"
+ "for trace buffer!\n");
+ goto free_buffers;
+ }
+
+ INIT_LIST_HEAD(&max_tr.data[i]->trace_pages);
+ page = virt_to_page(array);
+ list_add(&page->lru, &max_tr.data[i]->trace_pages);
+ SetPageLRU(page);
+#endif
+ }
+
+ /*
+ * Since we allocate by orders of pages, we may be able to
+ * round up a bit.
+ */
+ global_trace.entries = ENTRIES_PER_PAGE;
+ pages++;
+
+ while (global_trace.entries < trace_nr_entries) {
+ if (trace_alloc_page())
+ break;
+ pages++;
+ }
+ max_tr.entries = global_trace.entries;
+
+ pr_info("tracer: %d pages allocated for %ld",
+ pages, trace_nr_entries);
+ pr_info(" entries of %ld bytes\n", (long)TRACE_ENTRY_SIZE);
+ pr_info(" actual entries %ld\n", global_trace.entries);
+
+ tracer_init_debugfs();
+
+ trace_init_cmdlines();
+
+ register_tracer(&no_tracer);
+ current_trace = &no_tracer;
+
+ /* All seems OK, enable tracing */
+ global_trace.ctrl = tracer_enabled;
+ tracing_disabled = 0;
+
+ return 0;
+
+ free_buffers:
+ for (i-- ; i >= 0; i--) {
+ struct page *page, *tmp;
+ struct trace_array_cpu *data = global_trace.data[i];
+
+ if (data) {
+ list_for_each_entry_safe(page, tmp,
+ &data->trace_pages, lru) {
+ list_del_init(&page->lru);
+ __free_page(page);
+ }
+ }
+
+#ifdef CONFIG_TRACER_MAX_TRACE
+ data = max_tr.data[i];
+ if (data) {
+ list_for_each_entry_safe(page, tmp,
+ &data->trace_pages, lru) {
+ list_del_init(&page->lru);
+ __free_page(page);
+ }
+ }
+#endif
+ }
+ return ret;
+}
+fs_initcall(tracer_alloc_buffers);
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
new file mode 100644
index 000000000000..8cb215b239d5
--- /dev/null
+++ b/kernel/trace/trace.h
@@ -0,0 +1,333 @@
+#ifndef _LINUX_KERNEL_TRACE_H
+#define _LINUX_KERNEL_TRACE_H
+
+#include <linux/fs.h>
+#include <asm/atomic.h>
+#include <linux/sched.h>
+#include <linux/clocksource.h>
+#include <linux/mmiotrace.h>
+
+enum trace_type {
+ __TRACE_FIRST_TYPE = 0,
+
+ TRACE_FN,
+ TRACE_CTX,
+ TRACE_WAKE,
+ TRACE_STACK,
+ TRACE_SPECIAL,
+ TRACE_MMIO_RW,
+ TRACE_MMIO_MAP,
+
+ __TRACE_LAST_TYPE
+};
+
+/*
+ * Function trace entry - function address and parent function addres:
+ */
+struct ftrace_entry {
+ unsigned long ip;
+ unsigned long parent_ip;
+};
+
+/*
+ * Context switch trace entry - which task (and prio) we switched from/to:
+ */
+struct ctx_switch_entry {
+ unsigned int prev_pid;
+ unsigned char prev_prio;
+ unsigned char prev_state;
+ unsigned int next_pid;
+ unsigned char next_prio;
+ unsigned char next_state;
+};
+
+/*
+ * Special (free-form) trace entry:
+ */
+struct special_entry {
+ unsigned long arg1;
+ unsigned long arg2;
+ unsigned long arg3;
+};
+
+/*
+ * Stack-trace entry:
+ */
+
+#define FTRACE_STACK_ENTRIES 8
+
+struct stack_entry {
+ unsigned long caller[FTRACE_STACK_ENTRIES];
+};
+
+/*
+ * The trace entry - the most basic unit of tracing. This is what
+ * is printed in the end as a single line in the trace output, such as:
+ *
+ * bash-15816 [01] 235.197585: idle_cpu <- irq_enter
+ */
+struct trace_entry {
+ char type;
+ char cpu;
+ char flags;
+ char preempt_count;
+ int pid;
+ cycle_t t;
+ union {
+ struct ftrace_entry fn;
+ struct ctx_switch_entry ctx;
+ struct special_entry special;
+ struct stack_entry stack;
+ struct mmiotrace_rw mmiorw;
+ struct mmiotrace_map mmiomap;
+ };
+};
+
+#define TRACE_ENTRY_SIZE sizeof(struct trace_entry)
+
+/*
+ * The CPU trace array - it consists of thousands of trace entries
+ * plus some other descriptor data: (for example which task started
+ * the trace, etc.)
+ */
+struct trace_array_cpu {
+ struct list_head trace_pages;
+ atomic_t disabled;
+ raw_spinlock_t lock;
+ struct lock_class_key lock_key;
+
+ /* these fields get copied into max-trace: */
+ unsigned trace_head_idx;
+ unsigned trace_tail_idx;
+ void *trace_head; /* producer */
+ void *trace_tail; /* consumer */
+ unsigned long trace_idx;
+ unsigned long overrun;
+ unsigned long saved_latency;
+ unsigned long critical_start;
+ unsigned long critical_end;
+ unsigned long critical_sequence;
+ unsigned long nice;
+ unsigned long policy;
+ unsigned long rt_priority;
+ cycle_t preempt_timestamp;
+ pid_t pid;
+ uid_t uid;
+ char comm[TASK_COMM_LEN];
+};
+
+struct trace_iterator;
+
+/*
+ * The trace array - an array of per-CPU trace arrays. This is the
+ * highest level data structure that individual tracers deal with.
+ * They have on/off state as well:
+ */
+struct trace_array {
+ unsigned long entries;
+ long ctrl;
+ int cpu;
+ cycle_t time_start;
+ struct task_struct *waiter;
+ struct trace_array_cpu *data[NR_CPUS];
+};
+
+/*
+ * A specific tracer, represented by methods that operate on a trace array:
+ */
+struct tracer {
+ const char *name;
+ void (*init)(struct trace_array *tr);
+ void (*reset)(struct trace_array *tr);
+ void (*open)(struct trace_iterator *iter);
+ void (*pipe_open)(struct trace_iterator *iter);
+ void (*close)(struct trace_iterator *iter);
+ void (*start)(struct trace_iterator *iter);
+ void (*stop)(struct trace_iterator *iter);
+ ssize_t (*read)(struct trace_iterator *iter,
+ struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos);
+ void (*ctrl_update)(struct trace_array *tr);
+#ifdef CONFIG_FTRACE_STARTUP_TEST
+ int (*selftest)(struct tracer *trace,
+ struct trace_array *tr);
+#endif
+ int (*print_line)(struct trace_iterator *iter);
+ struct tracer *next;
+ int print_max;
+};
+
+struct trace_seq {
+ unsigned char buffer[PAGE_SIZE];
+ unsigned int len;
+ unsigned int readpos;
+};
+
+/*
+ * Trace iterator - used by printout routines who present trace
+ * results to users and which routines might sleep, etc:
+ */
+struct trace_iterator {
+ struct trace_array *tr;
+ struct tracer *trace;
+ void *private;
+ long last_overrun[NR_CPUS];
+ long overrun[NR_CPUS];
+
+ /* The below is zeroed out in pipe_read */
+ struct trace_seq seq;
+ struct trace_entry *ent;
+ int cpu;
+
+ struct trace_entry *prev_ent;
+ int prev_cpu;
+
+ unsigned long iter_flags;
+ loff_t pos;
+ unsigned long next_idx[NR_CPUS];
+ struct list_head *next_page[NR_CPUS];
+ unsigned next_page_idx[NR_CPUS];
+ long idx;
+};
+
+void tracing_reset(struct trace_array_cpu *data);
+int tracing_open_generic(struct inode *inode, struct file *filp);
+struct dentry *tracing_init_dentry(void);
+void init_tracer_sysprof_debugfs(struct dentry *d_tracer);
+
+void ftrace(struct trace_array *tr,
+ struct trace_array_cpu *data,
+ unsigned long ip,
+ unsigned long parent_ip,
+ unsigned long flags);
+void tracing_sched_switch_trace(struct trace_array *tr,
+ struct trace_array_cpu *data,
+ struct task_struct *prev,
+ struct task_struct *next,
+ unsigned long flags);
+void tracing_record_cmdline(struct task_struct *tsk);
+
+void tracing_sched_wakeup_trace(struct trace_array *tr,
+ struct trace_array_cpu *data,
+ struct task_struct *wakee,
+ struct task_struct *cur,
+ unsigned long flags);
+void trace_special(struct trace_array *tr,
+ struct trace_array_cpu *data,
+ unsigned long arg1,
+ unsigned long arg2,
+ unsigned long arg3);
+void trace_function(struct trace_array *tr,
+ struct trace_array_cpu *data,
+ unsigned long ip,
+ unsigned long parent_ip,
+ unsigned long flags);
+
+void tracing_start_function_trace(void);
+void tracing_stop_function_trace(void);
+void tracing_start_cmdline_record(void);
+void tracing_stop_cmdline_record(void);
+int register_tracer(struct tracer *type);
+void unregister_tracer(struct tracer *type);
+
+extern unsigned long nsecs_to_usecs(unsigned long nsecs);
+
+extern unsigned long tracing_max_latency;
+extern unsigned long tracing_thresh;
+
+void update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu);
+void update_max_tr_single(struct trace_array *tr,
+ struct task_struct *tsk, int cpu);
+
+extern cycle_t ftrace_now(int cpu);
+
+#ifdef CONFIG_CONTEXT_SWITCH_TRACER
+typedef void
+(*tracer_switch_func_t)(void *private,
+ void *__rq,
+ struct task_struct *prev,
+ struct task_struct *next);
+
+struct tracer_switch_ops {
+ tracer_switch_func_t func;
+ void *private;
+ struct tracer_switch_ops *next;
+};
+
+#endif /* CONFIG_CONTEXT_SWITCH_TRACER */
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+extern unsigned long ftrace_update_tot_cnt;
+#define DYN_FTRACE_TEST_NAME trace_selftest_dynamic_test_func
+extern int DYN_FTRACE_TEST_NAME(void);
+#endif
+
+#ifdef CONFIG_MMIOTRACE
+extern void __trace_mmiotrace_rw(struct trace_array *tr,
+ struct trace_array_cpu *data,
+ struct mmiotrace_rw *rw);
+extern void __trace_mmiotrace_map(struct trace_array *tr,
+ struct trace_array_cpu *data,
+ struct mmiotrace_map *map);
+#endif
+
+#ifdef CONFIG_FTRACE_STARTUP_TEST
+#ifdef CONFIG_FTRACE
+extern int trace_selftest_startup_function(struct tracer *trace,
+ struct trace_array *tr);
+#endif
+#ifdef CONFIG_IRQSOFF_TRACER
+extern int trace_selftest_startup_irqsoff(struct tracer *trace,
+ struct trace_array *tr);
+#endif
+#ifdef CONFIG_PREEMPT_TRACER
+extern int trace_selftest_startup_preemptoff(struct tracer *trace,
+ struct trace_array *tr);
+#endif
+#if defined(CONFIG_IRQSOFF_TRACER) && defined(CONFIG_PREEMPT_TRACER)
+extern int trace_selftest_startup_preemptirqsoff(struct tracer *trace,
+ struct trace_array *tr);
+#endif
+#ifdef CONFIG_SCHED_TRACER
+extern int trace_selftest_startup_wakeup(struct tracer *trace,
+ struct trace_array *tr);
+#endif
+#ifdef CONFIG_CONTEXT_SWITCH_TRACER
+extern int trace_selftest_startup_sched_switch(struct tracer *trace,
+ struct trace_array *tr);
+#endif
+#ifdef CONFIG_SYSPROF_TRACER
+extern int trace_selftest_startup_sysprof(struct tracer *trace,
+ struct trace_array *tr);
+#endif
+#endif /* CONFIG_FTRACE_STARTUP_TEST */
+
+extern void *head_page(struct trace_array_cpu *data);
+extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...);
+extern ssize_t trace_seq_to_user(struct trace_seq *s, char __user *ubuf,
+ size_t cnt);
+extern long ns2usecs(cycle_t nsec);
+
+extern unsigned long trace_flags;
+
+/*
+ * trace_iterator_flags is an enumeration that defines bit
+ * positions into trace_flags that controls the output.
+ *
+ * NOTE: These bits must match the trace_options array in
+ * trace.c.
+ */
+enum trace_iterator_flags {
+ TRACE_ITER_PRINT_PARENT = 0x01,
+ TRACE_ITER_SYM_OFFSET = 0x02,
+ TRACE_ITER_SYM_ADDR = 0x04,
+ TRACE_ITER_VERBOSE = 0x08,
+ TRACE_ITER_RAW = 0x10,
+ TRACE_ITER_HEX = 0x20,
+ TRACE_ITER_BIN = 0x40,
+ TRACE_ITER_BLOCK = 0x80,
+ TRACE_ITER_STACKTRACE = 0x100,
+ TRACE_ITER_SCHED_TREE = 0x200,
+};
+
+#endif /* _LINUX_KERNEL_TRACE_H */
diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c
new file mode 100644
index 000000000000..7ee7dcd76b7d
--- /dev/null
+++ b/kernel/trace/trace_functions.c
@@ -0,0 +1,78 @@
+/*
+ * ring buffer based function tracer
+ *
+ * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
+ * Copyright (C) 2008 Ingo Molnar <mingo@redhat.com>
+ *
+ * Based on code from the latency_tracer, that is:
+ *
+ * Copyright (C) 2004-2006 Ingo Molnar
+ * Copyright (C) 2004 William Lee Irwin III
+ */
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/ftrace.h>
+#include <linux/fs.h>
+
+#include "trace.h"
+
+static void function_reset(struct trace_array *tr)
+{
+ int cpu;
+
+ tr->time_start = ftrace_now(tr->cpu);
+
+ for_each_online_cpu(cpu)
+ tracing_reset(tr->data[cpu]);
+}
+
+static void start_function_trace(struct trace_array *tr)
+{
+ function_reset(tr);
+ tracing_start_cmdline_record();
+ tracing_start_function_trace();
+}
+
+static void stop_function_trace(struct trace_array *tr)
+{
+ tracing_stop_function_trace();
+ tracing_stop_cmdline_record();
+}
+
+static void function_trace_init(struct trace_array *tr)
+{
+ if (tr->ctrl)
+ start_function_trace(tr);
+}
+
+static void function_trace_reset(struct trace_array *tr)
+{
+ if (tr->ctrl)
+ stop_function_trace(tr);
+}
+
+static void function_trace_ctrl_update(struct trace_array *tr)
+{
+ if (tr->ctrl)
+ start_function_trace(tr);
+ else
+ stop_function_trace(tr);
+}
+
+static struct tracer function_trace __read_mostly =
+{
+ .name = "ftrace",
+ .init = function_trace_init,
+ .reset = function_trace_reset,
+ .ctrl_update = function_trace_ctrl_update,
+#ifdef CONFIG_FTRACE_SELFTEST
+ .selftest = trace_selftest_startup_function,
+#endif
+};
+
+static __init int init_function_trace(void)
+{
+ return register_tracer(&function_trace);
+}
+
+device_initcall(init_function_trace);
diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
new file mode 100644
index 000000000000..421d6fe3650e
--- /dev/null
+++ b/kernel/trace/trace_irqsoff.c
@@ -0,0 +1,486 @@
+/*
+ * trace irqs off criticall timings
+ *
+ * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
+ * Copyright (C) 2008 Ingo Molnar <mingo@redhat.com>
+ *
+ * From code in the latency_tracer, that is:
+ *
+ * Copyright (C) 2004-2006 Ingo Molnar
+ * Copyright (C) 2004 William Lee Irwin III
+ */
+#include <linux/kallsyms.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/ftrace.h>
+#include <linux/fs.h>
+
+#include "trace.h"
+
+static struct trace_array *irqsoff_trace __read_mostly;
+static int tracer_enabled __read_mostly;
+
+static DEFINE_PER_CPU(int, tracing_cpu);
+
+static DEFINE_SPINLOCK(max_trace_lock);
+
+enum {
+ TRACER_IRQS_OFF = (1 << 1),
+ TRACER_PREEMPT_OFF = (1 << 2),
+};
+
+static int trace_type __read_mostly;
+
+#ifdef CONFIG_PREEMPT_TRACER
+static inline int
+preempt_trace(void)
+{
+ return ((trace_type & TRACER_PREEMPT_OFF) && preempt_count());
+}
+#else
+# define preempt_trace() (0)
+#endif
+
+#ifdef CONFIG_IRQSOFF_TRACER
+static inline int
+irq_trace(void)
+{
+ return ((trace_type & TRACER_IRQS_OFF) &&
+ irqs_disabled());
+}
+#else
+# define irq_trace() (0)
+#endif
+
+/*
+ * Sequence count - we record it when starting a measurement and
+ * skip the latency if the sequence has changed - some other section
+ * did a maximum and could disturb our measurement with serial console
+ * printouts, etc. Truly coinciding maximum latencies should be rare
+ * and what happens together happens separately as well, so this doesnt
+ * decrease the validity of the maximum found:
+ */
+static __cacheline_aligned_in_smp unsigned long max_sequence;
+
+#ifdef CONFIG_FTRACE
+/*
+ * irqsoff uses its own tracer function to keep the overhead down:
+ */
+static void
+irqsoff_tracer_call(unsigned long ip, unsigned long parent_ip)
+{
+ struct trace_array *tr = irqsoff_trace;
+ struct trace_array_cpu *data;
+ unsigned long flags;
+ long disabled;
+ int cpu;
+
+ /*
+ * Does not matter if we preempt. We test the flags
+ * afterward, to see if irqs are disabled or not.
+ * If we preempt and get a false positive, the flags
+ * test will fail.
+ */
+ cpu = raw_smp_processor_id();
+ if (likely(!per_cpu(tracing_cpu, cpu)))
+ return;
+
+ local_save_flags(flags);
+ /* slight chance to get a false positive on tracing_cpu */
+ if (!irqs_disabled_flags(flags))
+ return;
+
+ data = tr->data[cpu];
+ disabled = atomic_inc_return(&data->disabled);
+
+ if (likely(disabled == 1))
+ trace_function(tr, data, ip, parent_ip, flags);
+
+ atomic_dec(&data->disabled);
+}
+
+static struct ftrace_ops trace_ops __read_mostly =
+{
+ .func = irqsoff_tracer_call,
+};
+#endif /* CONFIG_FTRACE */
+
+/*
+ * Should this new latency be reported/recorded?
+ */
+static int report_latency(cycle_t delta)
+{
+ if (tracing_thresh) {
+ if (delta < tracing_thresh)
+ return 0;
+ } else {
+ if (delta <= tracing_max_latency)
+ return 0;
+ }
+ return 1;
+}
+
+static void
+check_critical_timing(struct trace_array *tr,
+ struct trace_array_cpu *data,
+ unsigned long parent_ip,
+ int cpu)
+{
+ unsigned long latency, t0, t1;
+ cycle_t T0, T1, delta;
+ unsigned long flags;
+
+ /*
+ * usecs conversion is slow so we try to delay the conversion
+ * as long as possible:
+ */
+ T0 = data->preempt_timestamp;
+ T1 = ftrace_now(cpu);
+ delta = T1-T0;
+
+ local_save_flags(flags);
+
+ if (!report_latency(delta))
+ goto out;
+
+ spin_lock_irqsave(&max_trace_lock, flags);
+
+ /* check if we are still the max latency */
+ if (!report_latency(delta))
+ goto out_unlock;
+
+ trace_function(tr, data, CALLER_ADDR0, parent_ip, flags);
+
+ latency = nsecs_to_usecs(delta);
+
+ if (data->critical_sequence != max_sequence)
+ goto out_unlock;
+
+ tracing_max_latency = delta;
+ t0 = nsecs_to_usecs(T0);
+ t1 = nsecs_to_usecs(T1);
+
+ data->critical_end = parent_ip;
+
+ update_max_tr_single(tr, current, cpu);
+
+ max_sequence++;
+
+out_unlock:
+ spin_unlock_irqrestore(&max_trace_lock, flags);
+
+out:
+ data->critical_sequence = max_sequence;
+ data->preempt_timestamp = ftrace_now(cpu);
+ tracing_reset(data);
+ trace_function(tr, data, CALLER_ADDR0, parent_ip, flags);
+}
+
+static inline void
+start_critical_timing(unsigned long ip, unsigned long parent_ip)
+{
+ int cpu;
+ struct trace_array *tr = irqsoff_trace;
+ struct trace_array_cpu *data;
+ unsigned long flags;
+
+ if (likely(!tracer_enabled))
+ return;
+
+ cpu = raw_smp_processor_id();
+
+ if (per_cpu(tracing_cpu, cpu))
+ return;
+
+ data = tr->data[cpu];
+
+ if (unlikely(!data) || atomic_read(&data->disabled))
+ return;
+
+ atomic_inc(&data->disabled);
+
+ data->critical_sequence = max_sequence;
+ data->preempt_timestamp = ftrace_now(cpu);
+ data->critical_start = parent_ip ? : ip;
+ tracing_reset(data);
+
+ local_save_flags(flags);
+
+ trace_function(tr, data, ip, parent_ip, flags);
+
+ per_cpu(tracing_cpu, cpu) = 1;
+
+ atomic_dec(&data->disabled);
+}
+
+static inline void
+stop_critical_timing(unsigned long ip, unsigned long parent_ip)
+{
+ int cpu;
+ struct trace_array *tr = irqsoff_trace;
+ struct trace_array_cpu *data;
+ unsigned long flags;
+
+ cpu = raw_smp_processor_id();
+ /* Always clear the tracing cpu on stopping the trace */
+ if (unlikely(per_cpu(tracing_cpu, cpu)))
+ per_cpu(tracing_cpu, cpu) = 0;
+ else
+ return;
+
+ if (!tracer_enabled)
+ return;
+
+ data = tr->data[cpu];
+
+ if (unlikely(!data) || unlikely(!head_page(data)) ||
+ !data->critical_start || atomic_read(&data->disabled))
+ return;
+
+ atomic_inc(&data->disabled);
+
+ local_save_flags(flags);
+ trace_function(tr, data, ip, parent_ip, flags);
+ check_critical_timing(tr, data, parent_ip ? : ip, cpu);
+ data->critical_start = 0;
+ atomic_dec(&data->disabled);
+}
+
+/* start and stop critical timings used to for stoppage (in idle) */
+void start_critical_timings(void)
+{
+ if (preempt_trace() || irq_trace())
+ start_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
+}
+
+void stop_critical_timings(void)
+{
+ if (preempt_trace() || irq_trace())
+ stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
+}
+
+#ifdef CONFIG_IRQSOFF_TRACER
+#ifdef CONFIG_PROVE_LOCKING
+void time_hardirqs_on(unsigned long a0, unsigned long a1)
+{
+ if (!preempt_trace() && irq_trace())
+ stop_critical_timing(a0, a1);
+}
+
+void time_hardirqs_off(unsigned long a0, unsigned long a1)
+{
+ if (!preempt_trace() && irq_trace())
+ start_critical_timing(a0, a1);
+}
+
+#else /* !CONFIG_PROVE_LOCKING */
+
+/*
+ * Stubs:
+ */
+
+void early_boot_irqs_off(void)
+{
+}
+
+void early_boot_irqs_on(void)
+{
+}
+
+void trace_softirqs_on(unsigned long ip)
+{
+}
+
+void trace_softirqs_off(unsigned long ip)
+{
+}
+
+inline void print_irqtrace_events(struct task_struct *curr)
+{
+}
+
+/*
+ * We are only interested in hardirq on/off events:
+ */
+void trace_hardirqs_on(void)
+{
+ if (!preempt_trace() && irq_trace())
+ stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
+}
+EXPORT_SYMBOL(trace_hardirqs_on);
+
+void trace_hardirqs_off(void)
+{
+ if (!preempt_trace() && irq_trace())
+ start_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
+}
+EXPORT_SYMBOL(trace_hardirqs_off);
+
+void trace_hardirqs_on_caller(unsigned long caller_addr)
+{
+ if (!preempt_trace() && irq_trace())
+ stop_critical_timing(CALLER_ADDR0, caller_addr);
+}
+EXPORT_SYMBOL(trace_hardirqs_on_caller);
+
+void trace_hardirqs_off_caller(unsigned long caller_addr)
+{
+ if (!preempt_trace() && irq_trace())
+ start_critical_timing(CALLER_ADDR0, caller_addr);
+}
+EXPORT_SYMBOL(trace_hardirqs_off_caller);
+
+#endif /* CONFIG_PROVE_LOCKING */
+#endif /* CONFIG_IRQSOFF_TRACER */
+
+#ifdef CONFIG_PREEMPT_TRACER
+void trace_preempt_on(unsigned long a0, unsigned long a1)
+{
+ stop_critical_timing(a0, a1);
+}
+
+void trace_preempt_off(unsigned long a0, unsigned long a1)
+{
+ start_critical_timing(a0, a1);
+}
+#endif /* CONFIG_PREEMPT_TRACER */
+
+static void start_irqsoff_tracer(struct trace_array *tr)
+{
+ register_ftrace_function(&trace_ops);
+ tracer_enabled = 1;
+}
+
+static void stop_irqsoff_tracer(struct trace_array *tr)
+{
+ tracer_enabled = 0;
+ unregister_ftrace_function(&trace_ops);
+}
+
+static void __irqsoff_tracer_init(struct trace_array *tr)
+{
+ irqsoff_trace = tr;
+ /* make sure that the tracer is visible */
+ smp_wmb();
+
+ if (tr->ctrl)
+ start_irqsoff_tracer(tr);
+}
+
+static void irqsoff_tracer_reset(struct trace_array *tr)
+{
+ if (tr->ctrl)
+ stop_irqsoff_tracer(tr);
+}
+
+static void irqsoff_tracer_ctrl_update(struct trace_array *tr)
+{
+ if (tr->ctrl)
+ start_irqsoff_tracer(tr);
+ else
+ stop_irqsoff_tracer(tr);
+}
+
+static void irqsoff_tracer_open(struct trace_iterator *iter)
+{
+ /* stop the trace while dumping */
+ if (iter->tr->ctrl)
+ stop_irqsoff_tracer(iter->tr);
+}
+
+static void irqsoff_tracer_close(struct trace_iterator *iter)
+{
+ if (iter->tr->ctrl)
+ start_irqsoff_tracer(iter->tr);
+}
+
+#ifdef CONFIG_IRQSOFF_TRACER
+static void irqsoff_tracer_init(struct trace_array *tr)
+{
+ trace_type = TRACER_IRQS_OFF;
+
+ __irqsoff_tracer_init(tr);
+}
+static struct tracer irqsoff_tracer __read_mostly =
+{
+ .name = "irqsoff",
+ .init = irqsoff_tracer_init,
+ .reset = irqsoff_tracer_reset,
+ .open = irqsoff_tracer_open,
+ .close = irqsoff_tracer_close,
+ .ctrl_update = irqsoff_tracer_ctrl_update,
+ .print_max = 1,
+#ifdef CONFIG_FTRACE_SELFTEST
+ .selftest = trace_selftest_startup_irqsoff,
+#endif
+};
+# define register_irqsoff(trace) register_tracer(&trace)
+#else
+# define register_irqsoff(trace) do { } while (0)
+#endif
+
+#ifdef CONFIG_PREEMPT_TRACER
+static void preemptoff_tracer_init(struct trace_array *tr)
+{
+ trace_type = TRACER_PREEMPT_OFF;
+
+ __irqsoff_tracer_init(tr);
+}
+
+static struct tracer preemptoff_tracer __read_mostly =
+{
+ .name = "preemptoff",
+ .init = preemptoff_tracer_init,
+ .reset = irqsoff_tracer_reset,
+ .open = irqsoff_tracer_open,
+ .close = irqsoff_tracer_close,
+ .ctrl_update = irqsoff_tracer_ctrl_update,
+ .print_max = 1,
+#ifdef CONFIG_FTRACE_SELFTEST
+ .selftest = trace_selftest_startup_preemptoff,
+#endif
+};
+# define register_preemptoff(trace) register_tracer(&trace)
+#else
+# define register_preemptoff(trace) do { } while (0)
+#endif
+
+#if defined(CONFIG_IRQSOFF_TRACER) && \
+ defined(CONFIG_PREEMPT_TRACER)
+
+static void preemptirqsoff_tracer_init(struct trace_array *tr)
+{
+ trace_type = TRACER_IRQS_OFF | TRACER_PREEMPT_OFF;
+
+ __irqsoff_tracer_init(tr);
+}
+
+static struct tracer preemptirqsoff_tracer __read_mostly =
+{
+ .name = "preemptirqsoff",
+ .init = preemptirqsoff_tracer_init,
+ .reset = irqsoff_tracer_reset,
+ .open = irqsoff_tracer_open,
+ .close = irqsoff_tracer_close,
+ .ctrl_update = irqsoff_tracer_ctrl_update,
+ .print_max = 1,
+#ifdef CONFIG_FTRACE_SELFTEST
+ .selftest = trace_selftest_startup_preemptirqsoff,
+#endif
+};
+
+# define register_preemptirqsoff(trace) register_tracer(&trace)
+#else
+# define register_preemptirqsoff(trace) do { } while (0)
+#endif
+
+__init static int init_irqsoff_tracer(void)
+{
+ register_irqsoff(irqsoff_tracer);
+ register_preemptoff(preemptoff_tracer);
+ register_preemptirqsoff(preemptirqsoff_tracer);
+
+ return 0;
+}
+device_initcall(init_irqsoff_tracer);
diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c
new file mode 100644
index 000000000000..b13dc19dcbb4
--- /dev/null
+++ b/kernel/trace/trace_mmiotrace.c
@@ -0,0 +1,295 @@
+/*
+ * Memory mapped I/O tracing
+ *
+ * Copyright (C) 2008 Pekka Paalanen <pq@iki.fi>
+ */
+
+#define DEBUG 1
+
+#include <linux/kernel.h>
+#include <linux/mmiotrace.h>
+#include <linux/pci.h>
+
+#include "trace.h"
+
+struct header_iter {
+ struct pci_dev *dev;
+};
+
+static struct trace_array *mmio_trace_array;
+static bool overrun_detected;
+
+static void mmio_reset_data(struct trace_array *tr)
+{
+ int cpu;
+
+ overrun_detected = false;
+ tr->time_start = ftrace_now(tr->cpu);
+
+ for_each_online_cpu(cpu)
+ tracing_reset(tr->data[cpu]);
+}
+
+static void mmio_trace_init(struct trace_array *tr)
+{
+ pr_debug("in %s\n", __func__);
+ mmio_trace_array = tr;
+ if (tr->ctrl) {
+ mmio_reset_data(tr);
+ enable_mmiotrace();
+ }
+}
+
+static void mmio_trace_reset(struct trace_array *tr)
+{
+ pr_debug("in %s\n", __func__);
+ if (tr->ctrl)
+ disable_mmiotrace();
+ mmio_reset_data(tr);
+ mmio_trace_array = NULL;
+}
+
+static void mmio_trace_ctrl_update(struct trace_array *tr)
+{
+ pr_debug("in %s\n", __func__);
+ if (tr->ctrl) {
+ mmio_reset_data(tr);
+ enable_mmiotrace();
+ } else {
+ disable_mmiotrace();
+ }
+}
+
+static int mmio_print_pcidev(struct trace_seq *s, const struct pci_dev *dev)
+{
+ int ret = 0;
+ int i;
+ resource_size_t start, end;
+ const struct pci_driver *drv = pci_dev_driver(dev);
+
+ /* XXX: incomplete checks for trace_seq_printf() return value */
+ ret += trace_seq_printf(s, "PCIDEV %02x%02x %04x%04x %x",
+ dev->bus->number, dev->devfn,
+ dev->vendor, dev->device, dev->irq);
+ /*
+ * XXX: is pci_resource_to_user() appropriate, since we are
+ * supposed to interpret the __ioremap() phys_addr argument based on
+ * these printed values?
+ */
+ for (i = 0; i < 7; i++) {
+ pci_resource_to_user(dev, i, &dev->resource[i], &start, &end);
+ ret += trace_seq_printf(s, " %llx",
+ (unsigned long long)(start |
+ (dev->resource[i].flags & PCI_REGION_FLAG_MASK)));
+ }
+ for (i = 0; i < 7; i++) {
+ pci_resource_to_user(dev, i, &dev->resource[i], &start, &end);
+ ret += trace_seq_printf(s, " %llx",
+ dev->resource[i].start < dev->resource[i].end ?
+ (unsigned long long)(end - start) + 1 : 0);
+ }
+ if (drv)
+ ret += trace_seq_printf(s, " %s\n", drv->name);
+ else
+ ret += trace_seq_printf(s, " \n");
+ return ret;
+}
+
+static void destroy_header_iter(struct header_iter *hiter)
+{
+ if (!hiter)
+ return;
+ pci_dev_put(hiter->dev);
+ kfree(hiter);
+}
+
+static void mmio_pipe_open(struct trace_iterator *iter)
+{
+ struct header_iter *hiter;
+ struct trace_seq *s = &iter->seq;
+
+ trace_seq_printf(s, "VERSION 20070824\n");
+
+ hiter = kzalloc(sizeof(*hiter), GFP_KERNEL);
+ if (!hiter)
+ return;
+
+ hiter->dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
+ iter->private = hiter;
+}
+
+/* XXX: This is not called when the pipe is closed! */
+static void mmio_close(struct trace_iterator *iter)
+{
+ struct header_iter *hiter = iter->private;
+ destroy_header_iter(hiter);
+ iter->private = NULL;
+}
+
+static unsigned long count_overruns(struct trace_iterator *iter)
+{
+ int cpu;
+ unsigned long cnt = 0;
+ for_each_online_cpu(cpu) {
+ cnt += iter->overrun[cpu];
+ iter->overrun[cpu] = 0;
+ }
+ return cnt;
+}
+
+static ssize_t mmio_read(struct trace_iterator *iter, struct file *filp,
+ char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+ ssize_t ret;
+ struct header_iter *hiter = iter->private;
+ struct trace_seq *s = &iter->seq;
+ unsigned long n;
+
+ n = count_overruns(iter);
+ if (n) {
+ /* XXX: This is later than where events were lost. */
+ trace_seq_printf(s, "MARK 0.000000 Lost %lu events.\n", n);
+ if (!overrun_detected)
+ pr_warning("mmiotrace has lost events.\n");
+ overrun_detected = true;
+ goto print_out;
+ }
+
+ if (!hiter)
+ return 0;
+
+ mmio_print_pcidev(s, hiter->dev);
+ hiter->dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, hiter->dev);
+
+ if (!hiter->dev) {
+ destroy_header_iter(hiter);
+ iter->private = NULL;
+ }
+
+print_out:
+ ret = trace_seq_to_user(s, ubuf, cnt);
+ return (ret == -EBUSY) ? 0 : ret;
+}
+
+static int mmio_print_rw(struct trace_iterator *iter)
+{
+ struct trace_entry *entry = iter->ent;
+ struct mmiotrace_rw *rw = &entry->mmiorw;
+ struct trace_seq *s = &iter->seq;
+ unsigned long long t = ns2usecs(entry->t);
+ unsigned long usec_rem = do_div(t, 1000000ULL);
+ unsigned secs = (unsigned long)t;
+ int ret = 1;
+
+ switch (entry->mmiorw.opcode) {
+ case MMIO_READ:
+ ret = trace_seq_printf(s,
+ "R %d %lu.%06lu %d 0x%llx 0x%lx 0x%lx %d\n",
+ rw->width, secs, usec_rem, rw->map_id,
+ (unsigned long long)rw->phys,
+ rw->value, rw->pc, 0);
+ break;
+ case MMIO_WRITE:
+ ret = trace_seq_printf(s,
+ "W %d %lu.%06lu %d 0x%llx 0x%lx 0x%lx %d\n",
+ rw->width, secs, usec_rem, rw->map_id,
+ (unsigned long long)rw->phys,
+ rw->value, rw->pc, 0);
+ break;
+ case MMIO_UNKNOWN_OP:
+ ret = trace_seq_printf(s,
+ "UNKNOWN %lu.%06lu %d 0x%llx %02x,%02x,%02x 0x%lx %d\n",
+ secs, usec_rem, rw->map_id,
+ (unsigned long long)rw->phys,
+ (rw->value >> 16) & 0xff, (rw->value >> 8) & 0xff,
+ (rw->value >> 0) & 0xff, rw->pc, 0);
+ break;
+ default:
+ ret = trace_seq_printf(s, "rw what?\n");
+ break;
+ }
+ if (ret)
+ return 1;
+ return 0;
+}
+
+static int mmio_print_map(struct trace_iterator *iter)
+{
+ struct trace_entry *entry = iter->ent;
+ struct mmiotrace_map *m = &entry->mmiomap;
+ struct trace_seq *s = &iter->seq;
+ unsigned long long t = ns2usecs(entry->t);
+ unsigned long usec_rem = do_div(t, 1000000ULL);
+ unsigned secs = (unsigned long)t;
+ int ret = 1;
+
+ switch (entry->mmiorw.opcode) {
+ case MMIO_PROBE:
+ ret = trace_seq_printf(s,
+ "MAP %lu.%06lu %d 0x%llx 0x%lx 0x%lx 0x%lx %d\n",
+ secs, usec_rem, m->map_id,
+ (unsigned long long)m->phys, m->virt, m->len,
+ 0UL, 0);
+ break;
+ case MMIO_UNPROBE:
+ ret = trace_seq_printf(s,
+ "UNMAP %lu.%06lu %d 0x%lx %d\n",
+ secs, usec_rem, m->map_id, 0UL, 0);
+ break;
+ default:
+ ret = trace_seq_printf(s, "map what?\n");
+ break;
+ }
+ if (ret)
+ return 1;
+ return 0;
+}
+
+/* return 0 to abort printing without consuming current entry in pipe mode */
+static int mmio_print_line(struct trace_iterator *iter)
+{
+ switch (iter->ent->type) {
+ case TRACE_MMIO_RW:
+ return mmio_print_rw(iter);
+ case TRACE_MMIO_MAP:
+ return mmio_print_map(iter);
+ default:
+ return 1; /* ignore unknown entries */
+ }
+}
+
+static struct tracer mmio_tracer __read_mostly =
+{
+ .name = "mmiotrace",
+ .init = mmio_trace_init,
+ .reset = mmio_trace_reset,
+ .pipe_open = mmio_pipe_open,
+ .close = mmio_close,
+ .read = mmio_read,
+ .ctrl_update = mmio_trace_ctrl_update,
+ .print_line = mmio_print_line,
+};
+
+__init static int init_mmio_trace(void)
+{
+ return register_tracer(&mmio_tracer);
+}
+device_initcall(init_mmio_trace);
+
+void mmio_trace_rw(struct mmiotrace_rw *rw)
+{
+ struct trace_array *tr = mmio_trace_array;
+ struct trace_array_cpu *data = tr->data[smp_processor_id()];
+ __trace_mmiotrace_rw(tr, data, rw);
+}
+
+void mmio_trace_mapping(struct mmiotrace_map *map)
+{
+ struct trace_array *tr = mmio_trace_array;
+ struct trace_array_cpu *data;
+
+ preempt_disable();
+ data = tr->data[smp_processor_id()];
+ __trace_mmiotrace_map(tr, data, map);
+ preempt_enable();
+}
diff --git a/kernel/trace/trace_sched_switch.c b/kernel/trace/trace_sched_switch.c
new file mode 100644
index 000000000000..c16935d3bc5c
--- /dev/null
+++ b/kernel/trace/trace_sched_switch.c
@@ -0,0 +1,286 @@
+/*
+ * trace context switch
+ *
+ * Copyright (C) 2007 Steven Rostedt <srostedt@redhat.com>
+ *
+ */
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/kallsyms.h>
+#include <linux/uaccess.h>
+#include <linux/marker.h>
+#include <linux/ftrace.h>
+
+#include "trace.h"
+
+static struct trace_array *ctx_trace;
+static int __read_mostly tracer_enabled;
+static atomic_t sched_ref;
+
+static void
+sched_switch_func(void *private, void *__rq, struct task_struct *prev,
+ struct task_struct *next)
+{
+ struct trace_array **ptr = private;
+ struct trace_array *tr = *ptr;
+ struct trace_array_cpu *data;
+ unsigned long flags;
+ long disabled;
+ int cpu;
+
+ tracing_record_cmdline(prev);
+ tracing_record_cmdline(next);
+
+ if (!tracer_enabled)
+ return;
+
+ local_irq_save(flags);
+ cpu = raw_smp_processor_id();
+ data = tr->data[cpu];
+ disabled = atomic_inc_return(&data->disabled);
+
+ if (likely(disabled == 1))
+ tracing_sched_switch_trace(tr, data, prev, next, flags);
+
+ atomic_dec(&data->disabled);
+ local_irq_restore(flags);
+}
+
+static notrace void
+sched_switch_callback(void *probe_data, void *call_data,
+ const char *format, va_list *args)
+{
+ struct task_struct *prev;
+ struct task_struct *next;
+ struct rq *__rq;
+
+ if (!atomic_read(&sched_ref))
+ return;
+
+ /* skip prev_pid %d next_pid %d prev_state %ld */
+ (void)va_arg(*args, int);
+ (void)va_arg(*args, int);
+ (void)va_arg(*args, long);
+ __rq = va_arg(*args, typeof(__rq));
+ prev = va_arg(*args, typeof(prev));
+ next = va_arg(*args, typeof(next));
+
+ /*
+ * If tracer_switch_func only points to the local
+ * switch func, it still needs the ptr passed to it.
+ */
+ sched_switch_func(probe_data, __rq, prev, next);
+}
+
+static void
+wakeup_func(void *private, void *__rq, struct task_struct *wakee, struct
+ task_struct *curr)
+{
+ struct trace_array **ptr = private;
+ struct trace_array *tr = *ptr;
+ struct trace_array_cpu *data;
+ unsigned long flags;
+ long disabled;
+ int cpu;
+
+ if (!tracer_enabled)
+ return;
+
+ tracing_record_cmdline(curr);
+
+ local_irq_save(flags);
+ cpu = raw_smp_processor_id();
+ data = tr->data[cpu];
+ disabled = atomic_inc_return(&data->disabled);
+
+ if (likely(disabled == 1))
+ tracing_sched_wakeup_trace(tr, data, wakee, curr, flags);
+
+ atomic_dec(&data->disabled);
+ local_irq_restore(flags);
+}
+
+static notrace void
+wake_up_callback(void *probe_data, void *call_data,
+ const char *format, va_list *args)
+{
+ struct task_struct *curr;
+ struct task_struct *task;
+ struct rq *__rq;
+
+ if (likely(!tracer_enabled))
+ return;
+
+ /* Skip pid %d state %ld */
+ (void)va_arg(*args, int);
+ (void)va_arg(*args, long);
+ /* now get the meat: "rq %p task %p rq->curr %p" */
+ __rq = va_arg(*args, typeof(__rq));
+ task = va_arg(*args, typeof(task));
+ curr = va_arg(*args, typeof(curr));
+
+ tracing_record_cmdline(task);
+ tracing_record_cmdline(curr);
+
+ wakeup_func(probe_data, __rq, task, curr);
+}
+
+static void sched_switch_reset(struct trace_array *tr)
+{
+ int cpu;
+
+ tr->time_start = ftrace_now(tr->cpu);
+
+ for_each_online_cpu(cpu)
+ tracing_reset(tr->data[cpu]);
+}
+
+static int tracing_sched_register(void)
+{
+ int ret;
+
+ ret = marker_probe_register("kernel_sched_wakeup",
+ "pid %d state %ld ## rq %p task %p rq->curr %p",
+ wake_up_callback,
+ &ctx_trace);
+ if (ret) {
+ pr_info("wakeup trace: Couldn't add marker"
+ " probe to kernel_sched_wakeup\n");
+ return ret;
+ }
+
+ ret = marker_probe_register("kernel_sched_wakeup_new",
+ "pid %d state %ld ## rq %p task %p rq->curr %p",
+ wake_up_callback,
+ &ctx_trace);
+ if (ret) {
+ pr_info("wakeup trace: Couldn't add marker"
+ " probe to kernel_sched_wakeup_new\n");
+ goto fail_deprobe;
+ }
+
+ ret = marker_probe_register("kernel_sched_schedule",
+ "prev_pid %d next_pid %d prev_state %ld "
+ "## rq %p prev %p next %p",
+ sched_switch_callback,
+ &ctx_trace);
+ if (ret) {
+ pr_info("sched trace: Couldn't add marker"
+ " probe to kernel_sched_schedule\n");
+ goto fail_deprobe_wake_new;
+ }
+
+ return ret;
+fail_deprobe_wake_new:
+ marker_probe_unregister("kernel_sched_wakeup_new",
+ wake_up_callback,
+ &ctx_trace);
+fail_deprobe:
+ marker_probe_unregister("kernel_sched_wakeup",
+ wake_up_callback,
+ &ctx_trace);
+ return ret;
+}
+
+static void tracing_sched_unregister(void)
+{
+ marker_probe_unregister("kernel_sched_schedule",
+ sched_switch_callback,
+ &ctx_trace);
+ marker_probe_unregister("kernel_sched_wakeup_new",
+ wake_up_callback,
+ &ctx_trace);
+ marker_probe_unregister("kernel_sched_wakeup",
+ wake_up_callback,
+ &ctx_trace);
+}
+
+void tracing_start_sched_switch(void)
+{
+ long ref;
+
+ ref = atomic_inc_return(&sched_ref);
+ if (ref == 1)
+ tracing_sched_register();
+}
+
+void tracing_stop_sched_switch(void)
+{
+ long ref;
+
+ ref = atomic_dec_and_test(&sched_ref);
+ if (ref)
+ tracing_sched_unregister();
+}
+
+void tracing_start_cmdline_record(void)
+{
+ tracing_start_sched_switch();
+}
+
+void tracing_stop_cmdline_record(void)
+{
+ tracing_stop_sched_switch();
+}
+
+static void start_sched_trace(struct trace_array *tr)
+{
+ sched_switch_reset(tr);
+ tracer_enabled = 1;
+ tracing_start_cmdline_record();
+}
+
+static void stop_sched_trace(struct trace_array *tr)
+{
+ tracing_stop_cmdline_record();
+ tracer_enabled = 0;
+}
+
+static void sched_switch_trace_init(struct trace_array *tr)
+{
+ ctx_trace = tr;
+
+ if (tr->ctrl)
+ start_sched_trace(tr);
+}
+
+static void sched_switch_trace_reset(struct trace_array *tr)
+{
+ if (tr->ctrl)
+ stop_sched_trace(tr);
+}
+
+static void sched_switch_trace_ctrl_update(struct trace_array *tr)
+{
+ /* When starting a new trace, reset the buffers */
+ if (tr->ctrl)
+ start_sched_trace(tr);
+ else
+ stop_sched_trace(tr);
+}
+
+static struct tracer sched_switch_trace __read_mostly =
+{
+ .name = "sched_switch",
+ .init = sched_switch_trace_init,
+ .reset = sched_switch_trace_reset,
+ .ctrl_update = sched_switch_trace_ctrl_update,
+#ifdef CONFIG_FTRACE_SELFTEST
+ .selftest = trace_selftest_startup_sched_switch,
+#endif
+};
+
+__init static int init_sched_switch_trace(void)
+{
+ int ret = 0;
+
+ if (atomic_read(&sched_ref))
+ ret = tracing_sched_register();
+ if (ret) {
+ pr_info("error registering scheduler trace\n");
+ return ret;
+ }
+ return register_tracer(&sched_switch_trace);
+}
+device_initcall(init_sched_switch_trace);
diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c
new file mode 100644
index 000000000000..bf7e91caef57
--- /dev/null
+++ b/kernel/trace/trace_sched_wakeup.c
@@ -0,0 +1,447 @@
+/*
+ * trace task wakeup timings
+ *
+ * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com>
+ * Copyright (C) 2008 Ingo Molnar <mingo@redhat.com>
+ *
+ * Based on code from the latency_tracer, that is:
+ *
+ * Copyright (C) 2004-2006 Ingo Molnar
+ * Copyright (C) 2004 William Lee Irwin III
+ */
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/kallsyms.h>
+#include <linux/uaccess.h>
+#include <linux/ftrace.h>
+#include <linux/marker.h>
+
+#include "trace.h"
+
+static struct trace_array *wakeup_trace;
+static int __read_mostly tracer_enabled;
+
+static struct task_struct *wakeup_task;
+static int wakeup_cpu;
+static unsigned wakeup_prio = -1;
+
+static DEFINE_SPINLOCK(wakeup_lock);
+
+static void __wakeup_reset(struct trace_array *tr);
+
+#ifdef CONFIG_FTRACE
+/*
+ * irqsoff uses its own tracer function to keep the overhead down:
+ */
+static void
+wakeup_tracer_call(unsigned long ip, unsigned long parent_ip)
+{
+ struct trace_array *tr = wakeup_trace;
+ struct trace_array_cpu *data;
+ unsigned long flags;
+ long disabled;
+ int resched;
+ int cpu;
+
+ if (likely(!wakeup_task))
+ return;
+
+ resched = need_resched();
+ preempt_disable_notrace();
+
+ cpu = raw_smp_processor_id();
+ data = tr->data[cpu];
+ disabled = atomic_inc_return(&data->disabled);
+ if (unlikely(disabled != 1))
+ goto out;
+
+ spin_lock_irqsave(&wakeup_lock, flags);
+
+ if (unlikely(!wakeup_task))
+ goto unlock;
+
+ /*
+ * The task can't disappear because it needs to
+ * wake up first, and we have the wakeup_lock.
+ */
+ if (task_cpu(wakeup_task) != cpu)
+ goto unlock;
+
+ trace_function(tr, data, ip, parent_ip, flags);
+
+ unlock:
+ spin_unlock_irqrestore(&wakeup_lock, flags);
+
+ out:
+ atomic_dec(&data->disabled);
+
+ /*
+ * To prevent recursion from the scheduler, if the
+ * resched flag was set before we entered, then
+ * don't reschedule.
+ */
+ if (resched)
+ preempt_enable_no_resched_notrace();
+ else
+ preempt_enable_notrace();
+}
+
+static struct ftrace_ops trace_ops __read_mostly =
+{
+ .func = wakeup_tracer_call,
+};
+#endif /* CONFIG_FTRACE */
+
+/*
+ * Should this new latency be reported/recorded?
+ */
+static int report_latency(cycle_t delta)
+{
+ if (tracing_thresh) {
+ if (delta < tracing_thresh)
+ return 0;
+ } else {
+ if (delta <= tracing_max_latency)
+ return 0;
+ }
+ return 1;
+}
+
+static void notrace
+wakeup_sched_switch(void *private, void *rq, struct task_struct *prev,
+ struct task_struct *next)
+{
+ unsigned long latency = 0, t0 = 0, t1 = 0;
+ struct trace_array **ptr = private;
+ struct trace_array *tr = *ptr;
+ struct trace_array_cpu *data;
+ cycle_t T0, T1, delta;
+ unsigned long flags;
+ long disabled;
+ int cpu;
+
+ if (unlikely(!tracer_enabled))
+ return;
+
+ /*
+ * When we start a new trace, we set wakeup_task to NULL
+ * and then set tracer_enabled = 1. We want to make sure
+ * that another CPU does not see the tracer_enabled = 1
+ * and the wakeup_task with an older task, that might
+ * actually be the same as next.
+ */
+ smp_rmb();
+
+ if (next != wakeup_task)
+ return;
+
+ /* The task we are waiting for is waking up */
+ data = tr->data[wakeup_cpu];
+
+ /* disable local data, not wakeup_cpu data */
+ cpu = raw_smp_processor_id();
+ disabled = atomic_inc_return(&tr->data[cpu]->disabled);
+ if (likely(disabled != 1))
+ goto out;
+
+ spin_lock_irqsave(&wakeup_lock, flags);
+
+ /* We could race with grabbing wakeup_lock */
+ if (unlikely(!tracer_enabled || next != wakeup_task))
+ goto out_unlock;
+
+ trace_function(tr, data, CALLER_ADDR1, CALLER_ADDR2, flags);
+
+ /*
+ * usecs conversion is slow so we try to delay the conversion
+ * as long as possible:
+ */
+ T0 = data->preempt_timestamp;
+ T1 = ftrace_now(cpu);
+ delta = T1-T0;
+
+ if (!report_latency(delta))
+ goto out_unlock;
+
+ latency = nsecs_to_usecs(delta);
+
+ tracing_max_latency = delta;
+ t0 = nsecs_to_usecs(T0);
+ t1 = nsecs_to_usecs(T1);
+
+ update_max_tr(tr, wakeup_task, wakeup_cpu);
+
+out_unlock:
+ __wakeup_reset(tr);
+ spin_unlock_irqrestore(&wakeup_lock, flags);
+out:
+ atomic_dec(&tr->data[cpu]->disabled);
+}
+
+static notrace void
+sched_switch_callback(void *probe_data, void *call_data,
+ const char *format, va_list *args)
+{
+ struct task_struct *prev;
+ struct task_struct *next;
+ struct rq *__rq;
+
+ /* skip prev_pid %d next_pid %d prev_state %ld */
+ (void)va_arg(*args, int);
+ (void)va_arg(*args, int);
+ (void)va_arg(*args, long);
+ __rq = va_arg(*args, typeof(__rq));
+ prev = va_arg(*args, typeof(prev));
+ next = va_arg(*args, typeof(next));
+
+ tracing_record_cmdline(prev);
+
+ /*
+ * If tracer_switch_func only points to the local
+ * switch func, it still needs the ptr passed to it.
+ */
+ wakeup_sched_switch(probe_data, __rq, prev, next);
+}
+
+static void __wakeup_reset(struct trace_array *tr)
+{
+ struct trace_array_cpu *data;
+ int cpu;
+
+ assert_spin_locked(&wakeup_lock);
+
+ for_each_possible_cpu(cpu) {
+ data = tr->data[cpu];
+ tracing_reset(data);
+ }
+
+ wakeup_cpu = -1;
+ wakeup_prio = -1;
+
+ if (wakeup_task)
+ put_task_struct(wakeup_task);
+
+ wakeup_task = NULL;
+}
+
+static void wakeup_reset(struct trace_array *tr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&wakeup_lock, flags);
+ __wakeup_reset(tr);
+ spin_unlock_irqrestore(&wakeup_lock, flags);
+}
+
+static void
+wakeup_check_start(struct trace_array *tr, struct task_struct *p,
+ struct task_struct *curr)
+{
+ int cpu = smp_processor_id();
+ unsigned long flags;
+ long disabled;
+
+ if (likely(!rt_task(p)) ||
+ p->prio >= wakeup_prio ||
+ p->prio >= curr->prio)
+ return;
+
+ disabled = atomic_inc_return(&tr->data[cpu]->disabled);
+ if (unlikely(disabled != 1))
+ goto out;
+
+ /* interrupts should be off from try_to_wake_up */
+ spin_lock(&wakeup_lock);
+
+ /* check for races. */
+ if (!tracer_enabled || p->prio >= wakeup_prio)
+ goto out_locked;
+
+ /* reset the trace */
+ __wakeup_reset(tr);
+
+ wakeup_cpu = task_cpu(p);
+ wakeup_prio = p->prio;
+
+ wakeup_task = p;
+ get_task_struct(wakeup_task);
+
+ local_save_flags(flags);
+
+ tr->data[wakeup_cpu]->preempt_timestamp = ftrace_now(cpu);
+ trace_function(tr, tr->data[wakeup_cpu],
+ CALLER_ADDR1, CALLER_ADDR2, flags);
+
+out_locked:
+ spin_unlock(&wakeup_lock);
+out:
+ atomic_dec(&tr->data[cpu]->disabled);
+}
+
+static notrace void
+wake_up_callback(void *probe_data, void *call_data,
+ const char *format, va_list *args)
+{
+ struct trace_array **ptr = probe_data;
+ struct trace_array *tr = *ptr;
+ struct task_struct *curr;
+ struct task_struct *task;
+ struct rq *__rq;
+
+ if (likely(!tracer_enabled))
+ return;
+
+ /* Skip pid %d state %ld */
+ (void)va_arg(*args, int);
+ (void)va_arg(*args, long);
+ /* now get the meat: "rq %p task %p rq->curr %p" */
+ __rq = va_arg(*args, typeof(__rq));
+ task = va_arg(*args, typeof(task));
+ curr = va_arg(*args, typeof(curr));
+
+ tracing_record_cmdline(task);
+ tracing_record_cmdline(curr);
+
+ wakeup_check_start(tr, task, curr);
+}
+
+static void start_wakeup_tracer(struct trace_array *tr)
+{
+ int ret;
+
+ ret = marker_probe_register("kernel_sched_wakeup",
+ "pid %d state %ld ## rq %p task %p rq->curr %p",
+ wake_up_callback,
+ &wakeup_trace);
+ if (ret) {
+ pr_info("wakeup trace: Couldn't add marker"
+ " probe to kernel_sched_wakeup\n");
+ return;
+ }
+
+ ret = marker_probe_register("kernel_sched_wakeup_new",
+ "pid %d state %ld ## rq %p task %p rq->curr %p",
+ wake_up_callback,
+ &wakeup_trace);
+ if (ret) {
+ pr_info("wakeup trace: Couldn't add marker"
+ " probe to kernel_sched_wakeup_new\n");
+ goto fail_deprobe;
+ }
+
+ ret = marker_probe_register("kernel_sched_schedule",
+ "prev_pid %d next_pid %d prev_state %ld "
+ "## rq %p prev %p next %p",
+ sched_switch_callback,
+ &wakeup_trace);
+ if (ret) {
+ pr_info("sched trace: Couldn't add marker"
+ " probe to kernel_sched_schedule\n");
+ goto fail_deprobe_wake_new;
+ }
+
+ wakeup_reset(tr);
+
+ /*
+ * Don't let the tracer_enabled = 1 show up before
+ * the wakeup_task is reset. This may be overkill since
+ * wakeup_reset does a spin_unlock after setting the
+ * wakeup_task to NULL, but I want to be safe.
+ * This is a slow path anyway.
+ */
+ smp_wmb();
+
+ tracer_enabled = 1;
+ register_ftrace_function(&trace_ops);
+
+ return;
+fail_deprobe_wake_new:
+ marker_probe_unregister("kernel_sched_wakeup_new",
+ wake_up_callback,
+ &wakeup_trace);
+fail_deprobe:
+ marker_probe_unregister("kernel_sched_wakeup",
+ wake_up_callback,
+ &wakeup_trace);
+}
+
+static void stop_wakeup_tracer(struct trace_array *tr)
+{
+ tracer_enabled = 0;
+ unregister_ftrace_function(&trace_ops);
+ marker_probe_unregister("kernel_sched_schedule",
+ sched_switch_callback,
+ &wakeup_trace);
+ marker_probe_unregister("kernel_sched_wakeup_new",
+ wake_up_callback,
+ &wakeup_trace);
+ marker_probe_unregister("kernel_sched_wakeup",
+ wake_up_callback,
+ &wakeup_trace);
+}
+
+static void wakeup_tracer_init(struct trace_array *tr)
+{
+ wakeup_trace = tr;
+
+ if (tr->ctrl)
+ start_wakeup_tracer(tr);
+}
+
+static void wakeup_tracer_reset(struct trace_array *tr)
+{
+ if (tr->ctrl) {
+ stop_wakeup_tracer(tr);
+ /* make sure we put back any tasks we are tracing */
+ wakeup_reset(tr);
+ }
+}
+
+static void wakeup_tracer_ctrl_update(struct trace_array *tr)
+{
+ if (tr->ctrl)
+ start_wakeup_tracer(tr);
+ else
+ stop_wakeup_tracer(tr);
+}
+
+static void wakeup_tracer_open(struct trace_iterator *iter)
+{
+ /* stop the trace while dumping */
+ if (iter->tr->ctrl)
+ stop_wakeup_tracer(iter->tr);
+}
+
+static void wakeup_tracer_close(struct trace_iterator *iter)
+{
+ /* forget about any processes we were recording */
+ if (iter->tr->ctrl)
+ start_wakeup_tracer(iter->tr);
+}
+
+static struct tracer wakeup_tracer __read_mostly =
+{
+ .name = "wakeup",
+ .init = wakeup_tracer_init,
+ .reset = wakeup_tracer_reset,
+ .open = wakeup_tracer_open,
+ .close = wakeup_tracer_close,
+ .ctrl_update = wakeup_tracer_ctrl_update,
+ .print_max = 1,
+#ifdef CONFIG_FTRACE_SELFTEST
+ .selftest = trace_selftest_startup_wakeup,
+#endif
+};
+
+__init static int init_wakeup_tracer(void)
+{
+ int ret;
+
+ ret = register_tracer(&wakeup_tracer);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+device_initcall(init_wakeup_tracer);
diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c
new file mode 100644
index 000000000000..0911b7e073bf
--- /dev/null
+++ b/kernel/trace/trace_selftest.c
@@ -0,0 +1,563 @@
+/* Include in trace.c */
+
+#include <linux/kthread.h>
+#include <linux/delay.h>
+
+static inline int trace_valid_entry(struct trace_entry *entry)
+{
+ switch (entry->type) {
+ case TRACE_FN:
+ case TRACE_CTX:
+ case TRACE_WAKE:
+ case TRACE_STACK:
+ case TRACE_SPECIAL:
+ return 1;
+ }
+ return 0;
+}
+
+static int
+trace_test_buffer_cpu(struct trace_array *tr, struct trace_array_cpu *data)
+{
+ struct trace_entry *entries;
+ struct page *page;
+ int idx = 0;
+ int i;
+
+ BUG_ON(list_empty(&data->trace_pages));
+ page = list_entry(data->trace_pages.next, struct page, lru);
+ entries = page_address(page);
+
+ check_pages(data);
+ if (head_page(data) != entries)
+ goto failed;
+
+ /*
+ * The starting trace buffer always has valid elements,
+ * if any element exists.
+ */
+ entries = head_page(data);
+
+ for (i = 0; i < tr->entries; i++) {
+
+ if (i < data->trace_idx && !trace_valid_entry(&entries[idx])) {
+ printk(KERN_CONT ".. invalid entry %d ",
+ entries[idx].type);
+ goto failed;
+ }
+
+ idx++;
+ if (idx >= ENTRIES_PER_PAGE) {
+ page = virt_to_page(entries);
+ if (page->lru.next == &data->trace_pages) {
+ if (i != tr->entries - 1) {
+ printk(KERN_CONT ".. entries buffer mismatch");
+ goto failed;
+ }
+ } else {
+ page = list_entry(page->lru.next, struct page, lru);
+ entries = page_address(page);
+ }
+ idx = 0;
+ }
+ }
+
+ page = virt_to_page(entries);
+ if (page->lru.next != &data->trace_pages) {
+ printk(KERN_CONT ".. too many entries");
+ goto failed;
+ }
+
+ return 0;
+
+ failed:
+ /* disable tracing */
+ tracing_disabled = 1;
+ printk(KERN_CONT ".. corrupted trace buffer .. ");
+ return -1;
+}
+
+/*
+ * Test the trace buffer to see if all the elements
+ * are still sane.
+ */
+static int trace_test_buffer(struct trace_array *tr, unsigned long *count)
+{
+ unsigned long flags, cnt = 0;
+ int cpu, ret = 0;
+
+ /* Don't allow flipping of max traces now */
+ raw_local_irq_save(flags);
+ __raw_spin_lock(&ftrace_max_lock);
+ for_each_possible_cpu(cpu) {
+ if (!head_page(tr->data[cpu]))
+ continue;
+
+ cnt += tr->data[cpu]->trace_idx;
+
+ ret = trace_test_buffer_cpu(tr, tr->data[cpu]);
+ if (ret)
+ break;
+ }
+ __raw_spin_unlock(&ftrace_max_lock);
+ raw_local_irq_restore(flags);
+
+ if (count)
+ *count = cnt;
+
+ return ret;
+}
+
+#ifdef CONFIG_FTRACE
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+
+#define __STR(x) #x
+#define STR(x) __STR(x)
+
+/* Test dynamic code modification and ftrace filters */
+int trace_selftest_startup_dynamic_tracing(struct tracer *trace,
+ struct trace_array *tr,
+ int (*func)(void))
+{
+ unsigned long count;
+ int ret;
+ int save_ftrace_enabled = ftrace_enabled;
+ int save_tracer_enabled = tracer_enabled;
+ char *func_name;
+
+ /* The ftrace test PASSED */
+ printk(KERN_CONT "PASSED\n");
+ pr_info("Testing dynamic ftrace: ");
+
+ /* enable tracing, and record the filter function */
+ ftrace_enabled = 1;
+ tracer_enabled = 1;
+
+ /* passed in by parameter to fool gcc from optimizing */
+ func();
+
+ /* update the records */
+ ret = ftrace_force_update();
+ if (ret) {
+ printk(KERN_CONT ".. ftraced failed .. ");
+ return ret;
+ }
+
+ /*
+ * Some archs *cough*PowerPC*cough* add charachters to the
+ * start of the function names. We simply put a '*' to
+ * accomodate them.
+ */
+ func_name = "*" STR(DYN_FTRACE_TEST_NAME);
+
+ /* filter only on our function */
+ ftrace_set_filter(func_name, strlen(func_name), 1);
+
+ /* enable tracing */
+ tr->ctrl = 1;
+ trace->init(tr);
+ /* Sleep for a 1/10 of a second */
+ msleep(100);
+
+ /* we should have nothing in the buffer */
+ ret = trace_test_buffer(tr, &count);
+ if (ret)
+ goto out;
+
+ if (count) {
+ ret = -1;
+ printk(KERN_CONT ".. filter did not filter .. ");
+ goto out;
+ }
+
+ /* call our function again */
+ func();
+
+ /* sleep again */
+ msleep(100);
+
+ /* stop the tracing. */
+ tr->ctrl = 0;
+ trace->ctrl_update(tr);
+ ftrace_enabled = 0;
+
+ /* check the trace buffer */
+ ret = trace_test_buffer(tr, &count);
+ trace->reset(tr);
+
+ /* we should only have one item */
+ if (!ret && count != 1) {
+ printk(KERN_CONT ".. filter failed count=%ld ..", count);
+ ret = -1;
+ goto out;
+ }
+ out:
+ ftrace_enabled = save_ftrace_enabled;
+ tracer_enabled = save_tracer_enabled;
+
+ /* Enable tracing on all functions again */
+ ftrace_set_filter(NULL, 0, 1);
+
+ return ret;
+}
+#else
+# define trace_selftest_startup_dynamic_tracing(trace, tr, func) ({ 0; })
+#endif /* CONFIG_DYNAMIC_FTRACE */
+/*
+ * Simple verification test of ftrace function tracer.
+ * Enable ftrace, sleep 1/10 second, and then read the trace
+ * buffer to see if all is in order.
+ */
+int
+trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr)
+{
+ unsigned long count;
+ int ret;
+ int save_ftrace_enabled = ftrace_enabled;
+ int save_tracer_enabled = tracer_enabled;
+
+ /* make sure msleep has been recorded */
+ msleep(1);
+
+ /* force the recorded functions to be traced */
+ ret = ftrace_force_update();
+ if (ret) {
+ printk(KERN_CONT ".. ftraced failed .. ");
+ return ret;
+ }
+
+ /* start the tracing */
+ ftrace_enabled = 1;
+ tracer_enabled = 1;
+
+ tr->ctrl = 1;
+ trace->init(tr);
+ /* Sleep for a 1/10 of a second */
+ msleep(100);
+ /* stop the tracing. */
+ tr->ctrl = 0;
+ trace->ctrl_update(tr);
+ ftrace_enabled = 0;
+
+ /* check the trace buffer */
+ ret = trace_test_buffer(tr, &count);
+ trace->reset(tr);
+
+ if (!ret && !count) {
+ printk(KERN_CONT ".. no entries found ..");
+ ret = -1;
+ goto out;
+ }
+
+ ret = trace_selftest_startup_dynamic_tracing(trace, tr,
+ DYN_FTRACE_TEST_NAME);
+
+ out:
+ ftrace_enabled = save_ftrace_enabled;
+ tracer_enabled = save_tracer_enabled;
+
+ /* kill ftrace totally if we failed */
+ if (ret)
+ ftrace_kill();
+
+ return ret;
+}
+#endif /* CONFIG_FTRACE */
+
+#ifdef CONFIG_IRQSOFF_TRACER
+int
+trace_selftest_startup_irqsoff(struct tracer *trace, struct trace_array *tr)
+{
+ unsigned long save_max = tracing_max_latency;
+ unsigned long count;
+ int ret;
+
+ /* start the tracing */
+ tr->ctrl = 1;
+ trace->init(tr);
+ /* reset the max latency */
+ tracing_max_latency = 0;
+ /* disable interrupts for a bit */
+ local_irq_disable();
+ udelay(100);
+ local_irq_enable();
+ /* stop the tracing. */
+ tr->ctrl = 0;
+ trace->ctrl_update(tr);
+ /* check both trace buffers */
+ ret = trace_test_buffer(tr, NULL);
+ if (!ret)
+ ret = trace_test_buffer(&max_tr, &count);
+ trace->reset(tr);
+
+ if (!ret && !count) {
+ printk(KERN_CONT ".. no entries found ..");
+ ret = -1;
+ }
+
+ tracing_max_latency = save_max;
+
+ return ret;
+}
+#endif /* CONFIG_IRQSOFF_TRACER */
+
+#ifdef CONFIG_PREEMPT_TRACER
+int
+trace_selftest_startup_preemptoff(struct tracer *trace, struct trace_array *tr)
+{
+ unsigned long save_max = tracing_max_latency;
+ unsigned long count;
+ int ret;
+
+ /* start the tracing */
+ tr->ctrl = 1;
+ trace->init(tr);
+ /* reset the max latency */
+ tracing_max_latency = 0;
+ /* disable preemption for a bit */
+ preempt_disable();
+ udelay(100);
+ preempt_enable();
+ /* stop the tracing. */
+ tr->ctrl = 0;
+ trace->ctrl_update(tr);
+ /* check both trace buffers */
+ ret = trace_test_buffer(tr, NULL);
+ if (!ret)
+ ret = trace_test_buffer(&max_tr, &count);
+ trace->reset(tr);
+
+ if (!ret && !count) {
+ printk(KERN_CONT ".. no entries found ..");
+ ret = -1;
+ }
+
+ tracing_max_latency = save_max;
+
+ return ret;
+}
+#endif /* CONFIG_PREEMPT_TRACER */
+
+#if defined(CONFIG_IRQSOFF_TRACER) && defined(CONFIG_PREEMPT_TRACER)
+int
+trace_selftest_startup_preemptirqsoff(struct tracer *trace, struct trace_array *tr)
+{
+ unsigned long save_max = tracing_max_latency;
+ unsigned long count;
+ int ret;
+
+ /* start the tracing */
+ tr->ctrl = 1;
+ trace->init(tr);
+
+ /* reset the max latency */
+ tracing_max_latency = 0;
+
+ /* disable preemption and interrupts for a bit */
+ preempt_disable();
+ local_irq_disable();
+ udelay(100);
+ preempt_enable();
+ /* reverse the order of preempt vs irqs */
+ local_irq_enable();
+
+ /* stop the tracing. */
+ tr->ctrl = 0;
+ trace->ctrl_update(tr);
+ /* check both trace buffers */
+ ret = trace_test_buffer(tr, NULL);
+ if (ret)
+ goto out;
+
+ ret = trace_test_buffer(&max_tr, &count);
+ if (ret)
+ goto out;
+
+ if (!ret && !count) {
+ printk(KERN_CONT ".. no entries found ..");
+ ret = -1;
+ goto out;
+ }
+
+ /* do the test by disabling interrupts first this time */
+ tracing_max_latency = 0;
+ tr->ctrl = 1;
+ trace->ctrl_update(tr);
+ preempt_disable();
+ local_irq_disable();
+ udelay(100);
+ preempt_enable();
+ /* reverse the order of preempt vs irqs */
+ local_irq_enable();
+
+ /* stop the tracing. */
+ tr->ctrl = 0;
+ trace->ctrl_update(tr);
+ /* check both trace buffers */
+ ret = trace_test_buffer(tr, NULL);
+ if (ret)
+ goto out;
+
+ ret = trace_test_buffer(&max_tr, &count);
+
+ if (!ret && !count) {
+ printk(KERN_CONT ".. no entries found ..");
+ ret = -1;
+ goto out;
+ }
+
+ out:
+ trace->reset(tr);
+ tracing_max_latency = save_max;
+
+ return ret;
+}
+#endif /* CONFIG_IRQSOFF_TRACER && CONFIG_PREEMPT_TRACER */
+
+#ifdef CONFIG_SCHED_TRACER
+static int trace_wakeup_test_thread(void *data)
+{
+ /* Make this a RT thread, doesn't need to be too high */
+ struct sched_param param = { .sched_priority = 5 };
+ struct completion *x = data;
+
+ sched_setscheduler(current, SCHED_FIFO, &param);
+
+ /* Make it know we have a new prio */
+ complete(x);
+
+ /* now go to sleep and let the test wake us up */
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+
+ /* we are awake, now wait to disappear */
+ while (!kthread_should_stop()) {
+ /*
+ * This is an RT task, do short sleeps to let
+ * others run.
+ */
+ msleep(100);
+ }
+
+ return 0;
+}
+
+int
+trace_selftest_startup_wakeup(struct tracer *trace, struct trace_array *tr)
+{
+ unsigned long save_max = tracing_max_latency;
+ struct task_struct *p;
+ struct completion isrt;
+ unsigned long count;
+ int ret;
+
+ init_completion(&isrt);
+
+ /* create a high prio thread */
+ p = kthread_run(trace_wakeup_test_thread, &isrt, "ftrace-test");
+ if (IS_ERR(p)) {
+ printk(KERN_CONT "Failed to create ftrace wakeup test thread ");
+ return -1;
+ }
+
+ /* make sure the thread is running at an RT prio */
+ wait_for_completion(&isrt);
+
+ /* start the tracing */
+ tr->ctrl = 1;
+ trace->init(tr);
+ /* reset the max latency */
+ tracing_max_latency = 0;
+
+ /* sleep to let the RT thread sleep too */
+ msleep(100);
+
+ /*
+ * Yes this is slightly racy. It is possible that for some
+ * strange reason that the RT thread we created, did not
+ * call schedule for 100ms after doing the completion,
+ * and we do a wakeup on a task that already is awake.
+ * But that is extremely unlikely, and the worst thing that
+ * happens in such a case, is that we disable tracing.
+ * Honestly, if this race does happen something is horrible
+ * wrong with the system.
+ */
+
+ wake_up_process(p);
+
+ /* stop the tracing. */
+ tr->ctrl = 0;
+ trace->ctrl_update(tr);
+ /* check both trace buffers */
+ ret = trace_test_buffer(tr, NULL);
+ if (!ret)
+ ret = trace_test_buffer(&max_tr, &count);
+
+
+ trace->reset(tr);
+
+ tracing_max_latency = save_max;
+
+ /* kill the thread */
+ kthread_stop(p);
+
+ if (!ret && !count) {
+ printk(KERN_CONT ".. no entries found ..");
+ ret = -1;
+ }
+
+ return ret;
+}
+#endif /* CONFIG_SCHED_TRACER */
+
+#ifdef CONFIG_CONTEXT_SWITCH_TRACER
+int
+trace_selftest_startup_sched_switch(struct tracer *trace, struct trace_array *tr)
+{
+ unsigned long count;
+ int ret;
+
+ /* start the tracing */
+ tr->ctrl = 1;
+ trace->init(tr);
+ /* Sleep for a 1/10 of a second */
+ msleep(100);
+ /* stop the tracing. */
+ tr->ctrl = 0;
+ trace->ctrl_update(tr);
+ /* check the trace buffer */
+ ret = trace_test_buffer(tr, &count);
+ trace->reset(tr);
+
+ if (!ret && !count) {
+ printk(KERN_CONT ".. no entries found ..");
+ ret = -1;
+ }
+
+ return ret;
+}
+#endif /* CONFIG_CONTEXT_SWITCH_TRACER */
+
+#ifdef CONFIG_SYSPROF_TRACER
+int
+trace_selftest_startup_sysprof(struct tracer *trace, struct trace_array *tr)
+{
+ unsigned long count;
+ int ret;
+
+ /* start the tracing */
+ tr->ctrl = 1;
+ trace->init(tr);
+ /* Sleep for a 1/10 of a second */
+ msleep(100);
+ /* stop the tracing. */
+ tr->ctrl = 0;
+ trace->ctrl_update(tr);
+ /* check the trace buffer */
+ ret = trace_test_buffer(tr, &count);
+ trace->reset(tr);
+
+ return ret;
+}
+#endif /* CONFIG_SYSPROF_TRACER */
diff --git a/kernel/trace/trace_selftest_dynamic.c b/kernel/trace/trace_selftest_dynamic.c
new file mode 100644
index 000000000000..54dd77cce5bf
--- /dev/null
+++ b/kernel/trace/trace_selftest_dynamic.c
@@ -0,0 +1,7 @@
+#include "trace.h"
+
+int DYN_FTRACE_TEST_NAME(void)
+{
+ /* used to call mcount */
+ return 0;
+}
diff --git a/kernel/trace/trace_sysprof.c b/kernel/trace/trace_sysprof.c
new file mode 100644
index 000000000000..2301e1e7c606
--- /dev/null
+++ b/kernel/trace/trace_sysprof.c
@@ -0,0 +1,363 @@
+/*
+ * trace stack traces
+ *
+ * Copyright (C) 2004-2008, Soeren Sandmann
+ * Copyright (C) 2007 Steven Rostedt <srostedt@redhat.com>
+ * Copyright (C) 2008 Ingo Molnar <mingo@redhat.com>
+ */
+#include <linux/kallsyms.h>
+#include <linux/debugfs.h>
+#include <linux/hrtimer.h>
+#include <linux/uaccess.h>
+#include <linux/ftrace.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/fs.h>
+
+#include <asm/stacktrace.h>
+
+#include "trace.h"
+
+static struct trace_array *sysprof_trace;
+static int __read_mostly tracer_enabled;
+
+/*
+ * 1 msec sample interval by default:
+ */
+static unsigned long sample_period = 1000000;
+static const unsigned int sample_max_depth = 512;
+
+static DEFINE_MUTEX(sample_timer_lock);
+/*
+ * Per CPU hrtimers that do the profiling:
+ */
+static DEFINE_PER_CPU(struct hrtimer, stack_trace_hrtimer);
+
+struct stack_frame {
+ const void __user *next_fp;
+ unsigned long return_address;
+};
+
+static int copy_stack_frame(const void __user *fp, struct stack_frame *frame)
+{
+ int ret;
+
+ if (!access_ok(VERIFY_READ, fp, sizeof(*frame)))
+ return 0;
+
+ ret = 1;
+ pagefault_disable();
+ if (__copy_from_user_inatomic(frame, fp, sizeof(*frame)))
+ ret = 0;
+ pagefault_enable();
+
+ return ret;
+}
+
+struct backtrace_info {
+ struct trace_array_cpu *data;
+ struct trace_array *tr;
+ int pos;
+};
+
+static void
+backtrace_warning_symbol(void *data, char *msg, unsigned long symbol)
+{
+ /* Ignore warnings */
+}
+
+static void backtrace_warning(void *data, char *msg)
+{
+ /* Ignore warnings */
+}
+
+static int backtrace_stack(void *data, char *name)
+{
+ /* Don't bother with IRQ stacks for now */
+ return -1;
+}
+
+static void backtrace_address(void *data, unsigned long addr, int reliable)
+{
+ struct backtrace_info *info = data;
+
+ if (info->pos < sample_max_depth && reliable) {
+ __trace_special(info->tr, info->data, 1, addr, 0);
+
+ info->pos++;
+ }
+}
+
+const static struct stacktrace_ops backtrace_ops = {
+ .warning = backtrace_warning,
+ .warning_symbol = backtrace_warning_symbol,
+ .stack = backtrace_stack,
+ .address = backtrace_address,
+};
+
+static int
+trace_kernel(struct pt_regs *regs, struct trace_array *tr,
+ struct trace_array_cpu *data)
+{
+ struct backtrace_info info;
+ unsigned long bp;
+ char *stack;
+
+ info.tr = tr;
+ info.data = data;
+ info.pos = 1;
+
+ __trace_special(info.tr, info.data, 1, regs->ip, 0);
+
+ stack = ((char *)regs + sizeof(struct pt_regs));
+#ifdef CONFIG_FRAME_POINTER
+ bp = regs->bp;
+#else
+ bp = 0;
+#endif
+
+ dump_trace(NULL, regs, (void *)stack, bp, &backtrace_ops, &info);
+
+ return info.pos;
+}
+
+static void timer_notify(struct pt_regs *regs, int cpu)
+{
+ struct trace_array_cpu *data;
+ struct stack_frame frame;
+ struct trace_array *tr;
+ const void __user *fp;
+ int is_user;
+ int i;
+
+ if (!regs)
+ return;
+
+ tr = sysprof_trace;
+ data = tr->data[cpu];
+ is_user = user_mode(regs);
+
+ if (!current || current->pid == 0)
+ return;
+
+ if (is_user && current->state != TASK_RUNNING)
+ return;
+
+ __trace_special(tr, data, 0, 0, current->pid);
+
+ if (!is_user)
+ i = trace_kernel(regs, tr, data);
+ else
+ i = 0;
+
+ /*
+ * Trace user stack if we are not a kernel thread
+ */
+ if (current->mm && i < sample_max_depth) {
+ regs = (struct pt_regs *)current->thread.sp0 - 1;
+
+ fp = (void __user *)regs->bp;
+
+ __trace_special(tr, data, 2, regs->ip, 0);
+
+ while (i < sample_max_depth) {
+ frame.next_fp = 0;
+ frame.return_address = 0;
+ if (!copy_stack_frame(fp, &frame))
+ break;
+ if ((unsigned long)fp < regs->sp)
+ break;
+
+ __trace_special(tr, data, 2, frame.return_address,
+ (unsigned long)fp);
+ fp = frame.next_fp;
+
+ i++;
+ }
+
+ }
+
+ /*
+ * Special trace entry if we overflow the max depth:
+ */
+ if (i == sample_max_depth)
+ __trace_special(tr, data, -1, -1, -1);
+
+ __trace_special(tr, data, 3, current->pid, i);
+}
+
+static enum hrtimer_restart stack_trace_timer_fn(struct hrtimer *hrtimer)
+{
+ /* trace here */
+ timer_notify(get_irq_regs(), smp_processor_id());
+
+ hrtimer_forward_now(hrtimer, ns_to_ktime(sample_period));
+
+ return HRTIMER_RESTART;
+}
+
+static void start_stack_timer(int cpu)
+{
+ struct hrtimer *hrtimer = &per_cpu(stack_trace_hrtimer, cpu);
+
+ hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hrtimer->function = stack_trace_timer_fn;
+ hrtimer->cb_mode = HRTIMER_CB_IRQSAFE_NO_SOFTIRQ;
+
+ hrtimer_start(hrtimer, ns_to_ktime(sample_period), HRTIMER_MODE_REL);
+}
+
+static void start_stack_timers(void)
+{
+ cpumask_t saved_mask = current->cpus_allowed;
+ int cpu;
+
+ for_each_online_cpu(cpu) {
+ set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
+ start_stack_timer(cpu);
+ }
+ set_cpus_allowed_ptr(current, &saved_mask);
+}
+
+static void stop_stack_timer(int cpu)
+{
+ struct hrtimer *hrtimer = &per_cpu(stack_trace_hrtimer, cpu);
+
+ hrtimer_cancel(hrtimer);
+}
+
+static void stop_stack_timers(void)
+{
+ int cpu;
+
+ for_each_online_cpu(cpu)
+ stop_stack_timer(cpu);
+}
+
+static void stack_reset(struct trace_array *tr)
+{
+ int cpu;
+
+ tr->time_start = ftrace_now(tr->cpu);
+
+ for_each_online_cpu(cpu)
+ tracing_reset(tr->data[cpu]);
+}
+
+static void start_stack_trace(struct trace_array *tr)
+{
+ mutex_lock(&sample_timer_lock);
+ stack_reset(tr);
+ start_stack_timers();
+ tracer_enabled = 1;
+ mutex_unlock(&sample_timer_lock);
+}
+
+static void stop_stack_trace(struct trace_array *tr)
+{
+ mutex_lock(&sample_timer_lock);
+ stop_stack_timers();
+ tracer_enabled = 0;
+ mutex_unlock(&sample_timer_lock);
+}
+
+static void stack_trace_init(struct trace_array *tr)
+{
+ sysprof_trace = tr;
+
+ if (tr->ctrl)
+ start_stack_trace(tr);
+}
+
+static void stack_trace_reset(struct trace_array *tr)
+{
+ if (tr->ctrl)
+ stop_stack_trace(tr);
+}
+
+static void stack_trace_ctrl_update(struct trace_array *tr)
+{
+ /* When starting a new trace, reset the buffers */
+ if (tr->ctrl)
+ start_stack_trace(tr);
+ else
+ stop_stack_trace(tr);
+}
+
+static struct tracer stack_trace __read_mostly =
+{
+ .name = "sysprof",
+ .init = stack_trace_init,
+ .reset = stack_trace_reset,
+ .ctrl_update = stack_trace_ctrl_update,
+#ifdef CONFIG_FTRACE_SELFTEST
+ .selftest = trace_selftest_startup_sysprof,
+#endif
+};
+
+__init static int init_stack_trace(void)
+{
+ return register_tracer(&stack_trace);
+}
+device_initcall(init_stack_trace);
+
+#define MAX_LONG_DIGITS 22
+
+static ssize_t
+sysprof_sample_read(struct file *filp, char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ char buf[MAX_LONG_DIGITS];
+ int r;
+
+ r = sprintf(buf, "%ld\n", nsecs_to_usecs(sample_period));
+
+ return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
+}
+
+static ssize_t
+sysprof_sample_write(struct file *filp, const char __user *ubuf,
+ size_t cnt, loff_t *ppos)
+{
+ char buf[MAX_LONG_DIGITS];
+ unsigned long val;
+
+ if (cnt > MAX_LONG_DIGITS-1)
+ cnt = MAX_LONG_DIGITS-1;
+
+ if (copy_from_user(&buf, ubuf, cnt))
+ return -EFAULT;
+
+ buf[cnt] = 0;
+
+ val = simple_strtoul(buf, NULL, 10);
+ /*
+ * Enforce a minimum sample period of 100 usecs:
+ */
+ if (val < 100)
+ val = 100;
+
+ mutex_lock(&sample_timer_lock);
+ stop_stack_timers();
+ sample_period = val * 1000;
+ start_stack_timers();
+ mutex_unlock(&sample_timer_lock);
+
+ return cnt;
+}
+
+static struct file_operations sysprof_sample_fops = {
+ .read = sysprof_sample_read,
+ .write = sysprof_sample_write,
+};
+
+void init_tracer_sysprof_debugfs(struct dentry *d_tracer)
+{
+ struct dentry *entry;
+
+ entry = debugfs_create_file("sysprof_sample_period", 0644,
+ d_tracer, NULL, &sysprof_sample_fops);
+ if (entry)
+ return;
+ pr_warning("Could not create debugfs 'dyn_ftrace_total_info' entry\n");
+}
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 29fc39f1029c..28c2b2c96ac7 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -397,7 +397,7 @@ void flush_workqueue(struct workqueue_struct *wq)
might_sleep();
lock_acquire(&wq->lockdep_map, 0, 0, 0, 2, _THIS_IP_);
lock_release(&wq->lockdep_map, 1, _THIS_IP_);
- for_each_cpu_mask(cpu, *cpu_map)
+ for_each_cpu_mask_nr(cpu, *cpu_map)
flush_cpu_workqueue(per_cpu_ptr(wq->cpu_wq, cpu));
}
EXPORT_SYMBOL_GPL(flush_workqueue);
@@ -477,7 +477,7 @@ static void wait_on_work(struct work_struct *work)
wq = cwq->wq;
cpu_map = wq_cpu_map(wq);
- for_each_cpu_mask(cpu, *cpu_map)
+ for_each_cpu_mask_nr(cpu, *cpu_map)
wait_on_cpu_work(per_cpu_ptr(wq->cpu_wq, cpu), work);
}
@@ -813,7 +813,7 @@ void destroy_workqueue(struct workqueue_struct *wq)
list_del(&wq->list);
spin_unlock(&workqueue_lock);
- for_each_cpu_mask(cpu, *cpu_map)
+ for_each_cpu_mask_nr(cpu, *cpu_map)
cleanup_workqueue_thread(per_cpu_ptr(wq->cpu_wq, cpu));
put_online_cpus();
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index d2099f41aa1e..0ff4cf7e98f1 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -74,6 +74,9 @@ config DEBUG_FS
debugging files into. Enable this option to be able to read and
write to these files.
+ For detailed documentation on the debugfs API, see
+ Documentation/DocBook/filesystems.
+
If unsure, say N.
config HEADERS_CHECK
@@ -147,7 +150,7 @@ config DETECT_SOFTLOCKUP
help
Say Y here to enable the kernel to detect "soft lockups",
which are bugs that cause the kernel to loop in kernel
- mode for more than 10 seconds, without giving other tasks a
+ mode for more than 60 seconds, without giving other tasks a
chance to run.
When a soft-lockup is detected, the kernel will print the
@@ -159,6 +162,30 @@ config DETECT_SOFTLOCKUP
can be detected via the NMI-watchdog, on platforms that
support it.)
+config BOOTPARAM_SOFTLOCKUP_PANIC
+ bool "Panic (Reboot) On Soft Lockups"
+ depends on DETECT_SOFTLOCKUP
+ help
+ Say Y here to enable the kernel to panic on "soft lockups",
+ which are bugs that cause the kernel to loop in kernel
+ mode for more than 60 seconds, without giving other tasks a
+ chance to run.
+
+ The panic can be used in combination with panic_timeout,
+ to cause the system to reboot automatically after a
+ lockup has been detected. This feature is useful for
+ high-availability systems that have uptime guarantees and
+ where a lockup must be resolved ASAP.
+
+ Say N if unsure.
+
+config BOOTPARAM_SOFTLOCKUP_PANIC_VALUE
+ int
+ depends on DETECT_SOFTLOCKUP
+ range 0 1
+ default 0 if !BOOTPARAM_SOFTLOCKUP_PANIC
+ default 1 if BOOTPARAM_SOFTLOCKUP_PANIC
+
config SCHED_DEBUG
bool "Collect scheduler debugging info"
depends on DEBUG_KERNEL && PROC_FS
@@ -285,12 +312,6 @@ config DEBUG_PI_LIST
default y
depends on DEBUG_RT_MUTEXES
-config RT_MUTEX_TESTER
- bool "Built-in scriptable tester for rt-mutexes"
- depends on DEBUG_KERNEL && RT_MUTEXES
- help
- This option enables a rt-mutex tester.
-
config DEBUG_SPINLOCK
bool "Spinlock and rw-lock debugging: basic checks"
depends on DEBUG_KERNEL
@@ -406,20 +427,8 @@ config DEBUG_SPINLOCK_SLEEP
If you say Y here, various routines which may sleep will become very
noisy if they are called with a spinlock held.
-config DEBUG_LOCKING_API_SELFTESTS
- bool "Locking API boot-time self-tests"
- depends on DEBUG_KERNEL
- help
- Say Y here if you want the kernel to run a short self-test during
- bootup. The self-test checks whether common types of locking bugs
- are detected by debugging mechanisms or not. (if you disable
- lock debugging then those bugs wont be detected of course.)
- The following locking APIs are covered: spinlocks, rwlocks,
- mutexes and rwsems.
-
config STACKTRACE
bool
- depends on DEBUG_KERNEL
depends on STACKTRACE_SUPPORT
config DEBUG_KOBJECT
@@ -528,59 +537,6 @@ config BOOT_PRINTK_DELAY
BOOT_PRINTK_DELAY also may cause DETECT_SOFTLOCKUP to detect
what it believes to be lockup conditions.
-config RCU_TORTURE_TEST
- tristate "torture tests for RCU"
- depends on DEBUG_KERNEL
- depends on m
- default n
- help
- This option provides a kernel module that runs torture tests
- on the RCU infrastructure. The kernel module may be built
- after the fact on the running kernel to be tested, if desired.
-
- Say M if you want the RCU torture tests to build as a module.
- Say N if you are unsure.
-
-config KPROBES_SANITY_TEST
- bool "Kprobes sanity tests"
- depends on DEBUG_KERNEL
- depends on KPROBES
- default n
- help
- This option provides for testing basic kprobes functionality on
- boot. A sample kprobe, jprobe and kretprobe are inserted and
- verified for functionality.
-
- Say N if you are unsure.
-
-config BACKTRACE_SELF_TEST
- tristate "Self test for the backtrace code"
- depends on DEBUG_KERNEL
- default n
- help
- This option provides a kernel module that can be used to test
- the kernel stack backtrace code. This option is not useful
- for distributions or general kernels, but only for kernel
- developers working on architecture code.
-
- Say N if you are unsure.
-
-config LKDTM
- tristate "Linux Kernel Dump Test Tool Module"
- depends on DEBUG_KERNEL
- depends on KPROBES
- depends on BLOCK
- default n
- help
- This module enables testing of the different dumping mechanisms by
- inducing system failures at predefined crash points.
- If you don't need it: say N
- Choose M here to compile this code as a module. The module will be
- called lkdtm.
-
- Documentation on how to use the module can be found in
- drivers/misc/lkdtm.c
-
config FAULT_INJECTION
bool "Fault-injection framework"
depends on DEBUG_KERNEL
@@ -634,6 +590,8 @@ config LATENCYTOP
Enable this option if you want to use the LatencyTOP tool
to find out which userspace is blocking on what kernel operations.
+source kernel/trace/Kconfig
+
config PROVIDE_OHCI1394_DMA_INIT
bool "Remote debugging over FireWire early on boot"
depends on PCI && X86
@@ -677,3 +635,5 @@ config FIREWIRE_OHCI_REMOTE_DMA
source "samples/Kconfig"
source "lib/Kconfig.kgdb"
+
+source "tests/Kconfig"
diff --git a/lib/Makefile b/lib/Makefile
index 74b0cfb1fcc3..1605c5ec8eb3 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -8,6 +8,15 @@ lib-y := ctype.o string.o vsprintf.o cmdline.o \
sha1.o irq_regs.o reciprocal_div.o argv_split.o \
proportions.o prio_heap.o ratelimit.o
+ifdef CONFIG_FTRACE
+# Do not profile string.o, since it may be used in early boot or vdso
+CFLAGS_REMOVE_string.o = -pg
+# Also do not profile any debug utilities
+CFLAGS_REMOVE_spinlock_debug.o = -pg
+CFLAGS_REMOVE_list_debug.o = -pg
+CFLAGS_REMOVE_debugobjects.o = -pg
+endif
+
lib-$(CONFIG_MMU) += ioremap.o
lib-$(CONFIG_SMP) += cpumask.o
@@ -25,7 +34,6 @@ lib-$(CONFIG_HOTPLUG) += kobject_uevent.o
obj-$(CONFIG_GENERIC_IOMAP) += iomap.o
obj-$(CONFIG_HAS_IOMEM) += iomap_copy.o devres.o
obj-$(CONFIG_CHECK_SIGNATURE) += check_signature.o
-obj-$(CONFIG_DEBUG_LOCKING_API_SELFTESTS) += locking-selftest.o
obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock_debug.o
lib-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
lib-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
diff --git a/lib/bitmap.c b/lib/bitmap.c
index 482df94ea21e..1338469ac849 100644
--- a/lib/bitmap.c
+++ b/lib/bitmap.c
@@ -996,3 +996,25 @@ int bitmap_allocate_region(unsigned long *bitmap, int pos, int order)
return 0;
}
EXPORT_SYMBOL(bitmap_allocate_region);
+
+/**
+ * bitmap_copy_le - copy a bitmap, putting the bits into little-endian order.
+ * @dst: destination buffer
+ * @src: bitmap to copy
+ * @nbits: number of bits in the bitmap
+ *
+ * Require nbits % BITS_PER_LONG == 0.
+ */
+void bitmap_copy_le(void *dst, const unsigned long *src, int nbits)
+{
+ unsigned long *d = dst;
+ int i;
+
+ for (i = 0; i < nbits/BITS_PER_LONG; i++) {
+ if (BITS_PER_LONG == 64)
+ d[i] = cpu_to_le64(src[i]);
+ else
+ d[i] = cpu_to_le32(src[i]);
+ }
+}
+EXPORT_SYMBOL(bitmap_copy_le);
diff --git a/lib/cpumask.c b/lib/cpumask.c
index bb4f76d3c3e7..5f97dc25ef9c 100644
--- a/lib/cpumask.c
+++ b/lib/cpumask.c
@@ -15,6 +15,15 @@ int __next_cpu(int n, const cpumask_t *srcp)
}
EXPORT_SYMBOL(__next_cpu);
+#if NR_CPUS > 64
+int __next_cpu_nr(int n, const cpumask_t *srcp)
+{
+ return min_t(int, nr_cpu_ids,
+ find_next_bit(srcp->bits, nr_cpu_ids, n+1));
+}
+EXPORT_SYMBOL(__next_cpu_nr);
+#endif
+
int __any_online_cpu(const cpumask_t *mask)
{
int cpu;
diff --git a/lib/div64.c b/lib/div64.c
index bb5bd0c0f030..a111eb8de9cf 100644
--- a/lib/div64.c
+++ b/lib/div64.c
@@ -98,3 +98,13 @@ EXPORT_SYMBOL(div64_u64);
#endif
#endif /* BITS_PER_LONG == 32 */
+
+/*
+ * Iterative div/mod for use when dividend is not expected to be much
+ * bigger than divisor.
+ */
+u32 iter_div_u64_rem(u64 dividend, u32 divisor, u64 *remainder)
+{
+ return __iter_div_u64_rem(dividend, divisor, remainder);
+}
+EXPORT_SYMBOL(iter_div_u64_rem);
diff --git a/lib/kobject.c b/lib/kobject.c
index 718e5101c263..b61ead8ab400 100644
--- a/lib/kobject.c
+++ b/lib/kobject.c
@@ -17,6 +17,57 @@
#include <linux/module.h>
#include <linux/stat.h>
#include <linux/slab.h>
+#include <linux/kallsyms.h>
+#include <asm-generic/sections.h>
+
+#ifdef CONFIG_X86_32
+static int ptr_in_range(void *ptr, void *start, void *end)
+{
+ /*
+ * This should hopefully get rid of causing warnings
+ * if the architecture did not set one of the section
+ * variables up.
+ */
+ if (start >= end)
+ return 0;
+
+ if ((ptr >= start) && (ptr < end))
+ return 1;
+ return 0;
+}
+
+static void verify_dynamic_kobject_allocation(struct kobject *kobj)
+{
+ char *namebuf;
+ const char *ret;
+
+ namebuf = kzalloc(KSYM_NAME_LEN, GFP_KERNEL);
+ ret = kallsyms_lookup((unsigned long)kobj, NULL, NULL, NULL,
+ namebuf);
+ /*
+ * This is the X86_32-only part of this function.
+ * This is here because it is valid to have a kobject
+ * in an __init section, but only after those
+ * sections have been freed back to the dynamic pool.
+ */
+ if (!initmem_now_dynamic &&
+ ptr_in_range(kobj, __init_begin, __init_end))
+ goto out;
+ if (!ret || !strlen(ret))
+ goto out;
+ pr_debug("---- begin silly warning ----\n");
+ pr_debug("This is a janitorial warning, not a kernel bug.\n");
+ pr_debug("The kobject '%s', at, or inside '%s'@(0x%p) is not "
+ "dynamically allocated.\n", kobject_name(kobj), namebuf, kobj);
+ pr_debug("kobjects must be dynamically allocated, not static\n");
+ /* dump_stack(); */
+ pr_debug("---- end silly warning ----\n");
+out:
+ kfree(namebuf);
+}
+#else
+static void verify_dynamic_kobject_allocation(struct kobject *kobj) { }
+#endif
/*
* populate_dir - populate directory with attributes.
@@ -216,13 +267,19 @@ static int kobject_add_internal(struct kobject *kobj)
static int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
va_list vargs)
{
- /* Free the old name, if necessary. */
- kfree(kobj->name);
+ const char *old_name = kobj->name;
+ char *s;
kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
if (!kobj->name)
return -ENOMEM;
+ /* ewww... some of these buggers have '/' in the name ... */
+ s = strchr(kobj->name, '/');
+ if (s)
+ s[0] = '!';
+
+ kfree(old_name);
return 0;
}
@@ -278,6 +335,7 @@ void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
"object, something is seriously wrong.\n", kobj);
dump_stack();
}
+ verify_dynamic_kobject_allocation(kobj);
kobject_init_internal(kobj);
kobj->ktype = ktype;
@@ -383,6 +441,11 @@ EXPORT_SYMBOL_GPL(kobject_init_and_add);
* kobject_rename - change the name of an object
* @kobj: object in question.
* @new_name: object's new name
+ *
+ * It is the responsibility of the caller to provide mutual
+ * exclusion between two different calls of kobject_rename
+ * on the same kobject and to ensure that new_name is valid and
+ * won't conflict with other kobjects.
*/
int kobject_rename(struct kobject *kobj, const char *new_name)
{
@@ -397,19 +460,6 @@ int kobject_rename(struct kobject *kobj, const char *new_name)
if (!kobj->parent)
return -EINVAL;
- /* see if this name is already in use */
- if (kobj->kset) {
- struct kobject *temp_kobj;
- temp_kobj = kset_find_obj(kobj->kset, new_name);
- if (temp_kobj) {
- printk(KERN_WARNING "kobject '%s' cannot be renamed "
- "to '%s' as '%s' is already in existence.\n",
- kobject_name(kobj), new_name, new_name);
- kobject_put(temp_kobj);
- return -EINVAL;
- }
- }
-
devpath = kobject_get_path(kobj, GFP_KERNEL);
if (!devpath) {
error = -ENOMEM;
@@ -439,6 +489,7 @@ out:
return error;
}
+EXPORT_SYMBOL_GPL(kobject_rename);
/**
* kobject_move - move object to another parent
diff --git a/lib/smp_processor_id.c b/lib/smp_processor_id.c
index 6c90fb90e19c..3b4dc098181e 100644
--- a/lib/smp_processor_id.c
+++ b/lib/smp_processor_id.c
@@ -7,7 +7,7 @@
#include <linux/kallsyms.h>
#include <linux/sched.h>
-unsigned int debug_smp_processor_id(void)
+notrace unsigned int debug_smp_processor_id(void)
{
unsigned long preempt_count = preempt_count();
int this_cpu = raw_smp_processor_id();
@@ -37,7 +37,7 @@ unsigned int debug_smp_processor_id(void)
/*
* Avoid recursion:
*/
- preempt_disable();
+ preempt_disable_notrace();
if (!printk_ratelimit())
goto out_enable;
@@ -49,7 +49,7 @@ unsigned int debug_smp_processor_id(void)
dump_stack();
out_enable:
- preempt_enable_no_resched();
+ preempt_enable_no_resched_notrace();
out:
return this_cpu;
}
diff --git a/lib/textsearch.c b/lib/textsearch.c
index be8bda3862f5..a3e500ad51d7 100644
--- a/lib/textsearch.c
+++ b/lib/textsearch.c
@@ -97,6 +97,7 @@
#include <linux/types.h>
#include <linux/string.h>
#include <linux/init.h>
+#include <linux/rculist.h>
#include <linux/rcupdate.h>
#include <linux/err.h>
#include <linux/textsearch.h>
diff --git a/mm/allocpercpu.c b/mm/allocpercpu.c
index f4026bae6eed..b5411c2c47d4 100644
--- a/mm/allocpercpu.c
+++ b/mm/allocpercpu.c
@@ -35,7 +35,7 @@ EXPORT_SYMBOL_GPL(percpu_depopulate);
void __percpu_depopulate_mask(void *__pdata, cpumask_t *mask)
{
int cpu;
- for_each_cpu_mask(cpu, *mask)
+ for_each_cpu_mask_nr(cpu, *mask)
percpu_depopulate(__pdata, cpu);
}
EXPORT_SYMBOL_GPL(__percpu_depopulate_mask);
@@ -86,7 +86,7 @@ int __percpu_populate_mask(void *__pdata, size_t size, gfp_t gfp,
int cpu;
cpus_clear(populated);
- for_each_cpu_mask(cpu, *mask)
+ for_each_cpu_mask_nr(cpu, *mask)
if (unlikely(!percpu_populate(__pdata, size, gfp, cpu))) {
__percpu_depopulate_mask(__pdata, &populated);
return -ENOMEM;
diff --git a/mm/bootmem.c b/mm/bootmem.c
index e8fb927392b9..8d9f60e06f62 100644
--- a/mm/bootmem.c
+++ b/mm/bootmem.c
@@ -442,15 +442,17 @@ unsigned long __init init_bootmem_node(pg_data_t *pgdat, unsigned long freepfn,
return init_bootmem_core(pgdat, freepfn, startpfn, endpfn);
}
-void __init reserve_bootmem_node(pg_data_t *pgdat, unsigned long physaddr,
+int __init reserve_bootmem_node(pg_data_t *pgdat, unsigned long physaddr,
unsigned long size, int flags)
{
int ret;
ret = can_reserve_bootmem_core(pgdat->bdata, physaddr, size, flags);
if (ret < 0)
- return;
+ return -ENOMEM;
reserve_bootmem_core(pgdat->bdata, physaddr, size, flags);
+
+ return 0;
}
void __init free_bootmem_node(pg_data_t *pgdat, unsigned long physaddr,
diff --git a/mm/filemap.c b/mm/filemap.c
index 1e6a7d34874f..72a953682912 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -1000,8 +1000,9 @@ page_ok:
page_not_up_to_date:
/* Get exclusive access to the page ... */
- if (lock_page_killable(page))
- goto readpage_eio;
+ error = lock_page_killable(page);
+ if (unlikely(error))
+ goto readpage_error;
/* Did it get truncated before we got the lock? */
if (!page->mapping) {
@@ -1029,8 +1030,9 @@ readpage:
}
if (!PageUptodate(page)) {
- if (lock_page_killable(page))
- goto readpage_eio;
+ error = lock_page_killable(page);
+ if (unlikely(error))
+ goto readpage_error;
if (!PageUptodate(page)) {
if (page->mapping == NULL) {
/*
@@ -1042,15 +1044,14 @@ readpage:
}
unlock_page(page);
shrink_readahead_size_eio(filp, ra);
- goto readpage_eio;
+ error = -EIO;
+ goto readpage_error;
}
unlock_page(page);
}
goto page_ok;
-readpage_eio:
- error = -EIO;
readpage_error:
/* UHHUH! A synchronous read error occurred. Report it */
desc->error = error;
diff --git a/mm/nommu.c b/mm/nommu.c
index 3abd0845bda4..4462b6a3fcb9 100644
--- a/mm/nommu.c
+++ b/mm/nommu.c
@@ -104,21 +104,15 @@ EXPORT_SYMBOL(vmtruncate);
unsigned int kobjsize(const void *objp)
{
struct page *page;
- int order = 0;
/*
* If the object we have should not have ksize performed on it,
* return size of 0
*/
- if (!objp)
- return 0;
-
- if ((unsigned long)objp >= memory_end)
+ if (!objp || !virt_addr_valid(objp))
return 0;
page = virt_to_head_page(objp);
- if (!page)
- return 0;
/*
* If the allocator sets PageSlab, we know the pointer came from
@@ -129,18 +123,9 @@ unsigned int kobjsize(const void *objp)
/*
* The ksize() function is only guaranteed to work for pointers
- * returned by kmalloc(). So handle arbitrary pointers, that we expect
- * always to be compound pages, here.
- */
- if (PageCompound(page))
- order = compound_order(page);
-
- /*
- * Finally, handle arbitrary pointers that don't set PageSlab.
- * Default to 0-order in the case when we're unable to ksize()
- * the object.
+ * returned by kmalloc(). So handle arbitrary pointers here.
*/
- return PAGE_SIZE << order;
+ return PAGE_SIZE << compound_order(page);
}
/*
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index 789b6adbef37..b38f700825fc 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -126,8 +126,6 @@ static void background_writeout(unsigned long _min_pages);
static struct prop_descriptor vm_completions;
static struct prop_descriptor vm_dirties;
-static unsigned long determine_dirtyable_memory(void);
-
/*
* couple the period to the dirty_ratio:
*
@@ -347,7 +345,13 @@ static unsigned long highmem_dirtyable_memory(unsigned long total)
#endif
}
-static unsigned long determine_dirtyable_memory(void)
+/**
+ * determine_dirtyable_memory - amount of memory that may be used
+ *
+ * Returns the numebr of pages that can currently be freed and used
+ * by the kernel for direct mappings.
+ */
+unsigned long determine_dirtyable_memory(void)
{
unsigned long x;
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 2f552955a02f..983eac506050 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -3462,6 +3462,11 @@ void __paginginit free_area_init_node(int nid, struct pglist_data *pgdat,
calculate_node_totalpages(pgdat, zones_size, zholes_size);
alloc_node_mem_map(pgdat);
+#ifdef CONFIG_FLAT_NODE_MEM_MAP
+ printk(KERN_DEBUG "free_area_init_node: node %d, pgdat %08lx, node_mem_map %08lx\n",
+ nid, (unsigned long)pgdat,
+ (unsigned long)pgdat->node_mem_map);
+#endif
free_area_init_core(pgdat, zones_size, zholes_size);
}
@@ -3550,25 +3555,49 @@ void __init add_active_range(unsigned int nid, unsigned long start_pfn,
/**
* shrink_active_range - Shrink an existing registered range of PFNs
* @nid: The node id the range is on that should be shrunk
- * @old_end_pfn: The old end PFN of the range
* @new_end_pfn: The new PFN of the range
*
* i386 with NUMA use alloc_remap() to store a node_mem_map on a local node.
- * The map is kept at the end physical page range that has already been
- * registered with add_active_range(). This function allows an arch to shrink
- * an existing registered range.
+ * The map is kept near the end physical page range that has already been
+ * registered. This function allows an arch to shrink an existing registered
+ * range.
*/
-void __init shrink_active_range(unsigned int nid, unsigned long old_end_pfn,
- unsigned long new_end_pfn)
+void __init shrink_active_range(unsigned int nid, unsigned long new_end_pfn)
{
- int i;
+ int i, j;
+ int removed = 0;
/* Find the old active region end and shrink */
- for_each_active_range_index_in_nid(i, nid)
- if (early_node_map[i].end_pfn == old_end_pfn) {
+ for_each_active_range_index_in_nid(i, nid) {
+ if (early_node_map[i].start_pfn >= new_end_pfn) {
+ /* clear it */
+ early_node_map[i].end_pfn = 0;
+ removed = 1;
+ continue;
+ }
+ if (early_node_map[i].end_pfn > new_end_pfn) {
early_node_map[i].end_pfn = new_end_pfn;
- break;
+ continue;
}
+ }
+
+ if (!removed)
+ return;
+
+ /* remove the blank ones */
+ for (i = nr_nodemap_entries - 1; i > 0; i--) {
+ if (early_node_map[i].nid != nid)
+ continue;
+ if (early_node_map[i].end_pfn)
+ continue;
+ /* we found it, get rid of it */
+ for (j = i; j < nr_nodemap_entries - 1; j++)
+ memcpy(&early_node_map[j], &early_node_map[j+1],
+ sizeof(early_node_map[j]));
+ j = nr_nodemap_entries - 1;
+ memset(&early_node_map[j], 0, sizeof(early_node_map[j]));
+ nr_nodemap_entries--;
+ }
}
/**
diff --git a/mm/vmstat.c b/mm/vmstat.c
index db9eabb2c5b3..c3d4a781802f 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -26,7 +26,7 @@ static void sum_vm_events(unsigned long *ret, cpumask_t *cpumask)
memset(ret, 0, NR_VM_EVENT_ITEMS * sizeof(unsigned long));
- for_each_cpu_mask(cpu, *cpumask) {
+ for_each_cpu_mask_nr(cpu, *cpumask) {
struct vm_event_state *this = &per_cpu(vm_event_states, cpu);
for (i = 0; i < NR_VM_EVENT_ITEMS; i++)
diff --git a/net/802/psnap.c b/net/802/psnap.c
index 31128cb92a23..ea4643931446 100644
--- a/net/802/psnap.c
+++ b/net/802/psnap.c
@@ -20,6 +20,7 @@
#include <linux/mm.h>
#include <linux/in.h>
#include <linux/init.h>
+#include <linux/rculist.h>
static LIST_HEAD(snap_list);
static DEFINE_SPINLOCK(snap_lock);
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index ab2225da0ee2..08f14f6c5fd6 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -27,6 +27,7 @@
#include <linux/mm.h>
#include <linux/in.h>
#include <linux/init.h>
+#include <linux/rculist.h>
#include <net/p8022.h>
#include <net/arp.h>
#include <linux/rtnetlink.h>
diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c
index 84360c117d4e..e13600106c6c 100644
--- a/net/bluetooth/hci_sysfs.c
+++ b/net/bluetooth/hci_sysfs.c
@@ -387,10 +387,6 @@ int hci_register_sysfs(struct hci_dev *hdev)
if (device_create_file(dev, bt_attrs[i]) < 0)
BT_ERR("Failed to create device attribute");
- if (sysfs_create_link(&bt_class->subsys.kobj,
- &dev->kobj, kobject_name(&dev->kobj)) < 0)
- BT_ERR("Failed to create class symlink");
-
return 0;
}
@@ -398,9 +394,6 @@ void hci_unregister_sysfs(struct hci_dev *hdev)
{
BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
- sysfs_remove_link(&bt_class->subsys.kobj,
- kobject_name(&hdev->dev.kobj));
-
device_del(&hdev->dev);
}
diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c
index 72c5976a5ce3..142060f02054 100644
--- a/net/bridge/br_fdb.c
+++ b/net/bridge/br_fdb.c
@@ -15,6 +15,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/rculist.h>
#include <linux/spinlock.h>
#include <linux/times.h>
#include <linux/netdevice.h>
diff --git a/net/bridge/br_stp.c b/net/bridge/br_stp.c
index e38034aa56f5..9e96ffcd29a3 100644
--- a/net/bridge/br_stp.c
+++ b/net/bridge/br_stp.c
@@ -13,6 +13,7 @@
* 2 of the License, or (at your option) any later version.
*/
#include <linux/kernel.h>
+#include <linux/rculist.h>
#include "br_private.h"
#include "br_private_stp.h"
diff --git a/net/core/dev.c b/net/core/dev.c
index 582963077877..305eaabb26f2 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2239,7 +2239,7 @@ out:
*/
if (!cpus_empty(net_dma.channel_mask)) {
int chan_idx;
- for_each_cpu_mask(chan_idx, net_dma.channel_mask) {
+ for_each_cpu_mask_nr(chan_idx, net_dma.channel_mask) {
struct dma_chan *chan = net_dma.channels[chan_idx];
if (chan)
dma_async_memcpy_issue_pending(chan);
@@ -4300,7 +4300,7 @@ static void net_dma_rebalance(struct net_dma *net_dma)
i = 0;
cpu = first_cpu(cpu_online_map);
- for_each_cpu_mask(chan_idx, net_dma->channel_mask) {
+ for_each_cpu_mask_nr(chan_idx, net_dma->channel_mask) {
chan = net_dma->channels[chan_idx];
n = ((num_online_cpus() / cpus_weight(net_dma->channel_mask))
@@ -4563,8 +4563,8 @@ static int __init net_dev_init(void)
dev_boot_phase = 0;
- open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);
- open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);
+ open_softirq(NET_TX_SOFTIRQ, net_tx_action);
+ open_softirq(NET_RX_SOFTIRQ, net_rx_action);
hotcpu_notifier(dev_cpu_callback, 0);
dst_init();
diff --git a/net/dccp/ackvec.c b/net/dccp/ackvec.c
index 6de4bd195d28..1e8be246ad15 100644
--- a/net/dccp/ackvec.c
+++ b/net/dccp/ackvec.c
@@ -290,12 +290,12 @@ int dccp_ackvec_add(struct dccp_ackvec *av, const struct sock *sk,
while (1) {
const u8 len = dccp_ackvec_len(av, index);
- const u8 state = dccp_ackvec_state(av, index);
+ const u8 av_state = dccp_ackvec_state(av, index);
/*
* valid packets not yet in av_buf have a reserved
* entry, with a len equal to 0.
*/
- if (state == DCCP_ACKVEC_STATE_NOT_RECEIVED &&
+ if (av_state == DCCP_ACKVEC_STATE_NOT_RECEIVED &&
len == 0 && delta == 0) { /* Found our
reserved seat! */
dccp_pr_debug("Found %llu reserved seat!\n",
@@ -325,31 +325,6 @@ out_duplicate:
return -EILSEQ;
}
-#ifdef CONFIG_IP_DCCP_DEBUG
-void dccp_ackvector_print(const u64 ackno, const unsigned char *vector, int len)
-{
- dccp_pr_debug_cat("ACK vector len=%d, ackno=%llu |", len,
- (unsigned long long)ackno);
-
- while (len--) {
- const u8 state = (*vector & DCCP_ACKVEC_STATE_MASK) >> 6;
- const u8 rl = *vector & DCCP_ACKVEC_LEN_MASK;
-
- dccp_pr_debug_cat("%d,%d|", state, rl);
- ++vector;
- }
-
- dccp_pr_debug_cat("\n");
-}
-
-void dccp_ackvec_print(const struct dccp_ackvec *av)
-{
- dccp_ackvector_print(av->av_buf_ackno,
- av->av_buf + av->av_buf_head,
- av->av_vec_len);
-}
-#endif
-
static void dccp_ackvec_throw_record(struct dccp_ackvec *av,
struct dccp_ackvec_record *avr)
{
diff --git a/net/dccp/ccids/ccid3.c b/net/dccp/ccids/ccid3.c
index f813077234b7..a1929f33d703 100644
--- a/net/dccp/ccids/ccid3.c
+++ b/net/dccp/ccids/ccid3.c
@@ -159,8 +159,8 @@ static void ccid3_hc_tx_update_x(struct sock *sk, ktime_t *stamp)
} else if (ktime_us_delta(now, hctx->ccid3hctx_t_ld)
- (s64)hctx->ccid3hctx_rtt >= 0) {
- hctx->ccid3hctx_x =
- max(min(2 * hctx->ccid3hctx_x, min_rate),
+ hctx->ccid3hctx_x = min(2 * hctx->ccid3hctx_x, min_rate);
+ hctx->ccid3hctx_x = max(hctx->ccid3hctx_x,
scaled_div(((__u64)hctx->ccid3hctx_s) << 6,
hctx->ccid3hctx_rtt));
hctx->ccid3hctx_t_ld = now;
@@ -329,8 +329,14 @@ static int ccid3_hc_tx_send_packet(struct sock *sk, struct sk_buff *skb)
hctx->ccid3hctx_x = rfc3390_initial_rate(sk);
hctx->ccid3hctx_t_ld = now;
} else {
- /* Sender does not have RTT sample: X_pps = 1 pkt/sec */
- hctx->ccid3hctx_x = hctx->ccid3hctx_s;
+ /*
+ * Sender does not have RTT sample:
+ * - set fallback RTT (RFC 4340, 3.4) since a RTT value
+ * is needed in several parts (e.g. window counter);
+ * - set sending rate X_pps = 1pps as per RFC 3448, 4.2.
+ */
+ hctx->ccid3hctx_rtt = DCCP_FALLBACK_RTT;
+ hctx->ccid3hctx_x = hctx->ccid3hctx_s;
hctx->ccid3hctx_x <<= 6;
}
ccid3_update_send_interval(hctx);
diff --git a/net/dccp/ccids/lib/tfrc.c b/net/dccp/ccids/lib/tfrc.c
index d1dfbb8de64c..97ecec0a8e76 100644
--- a/net/dccp/ccids/lib/tfrc.c
+++ b/net/dccp/ccids/lib/tfrc.c
@@ -14,14 +14,6 @@ module_param(tfrc_debug, bool, 0444);
MODULE_PARM_DESC(tfrc_debug, "Enable debug messages");
#endif
-extern int tfrc_tx_packet_history_init(void);
-extern void tfrc_tx_packet_history_exit(void);
-extern int tfrc_rx_packet_history_init(void);
-extern void tfrc_rx_packet_history_exit(void);
-
-extern int tfrc_li_init(void);
-extern void tfrc_li_exit(void);
-
static int __init tfrc_module_init(void)
{
int rc = tfrc_li_init();
diff --git a/net/dccp/ccids/lib/tfrc.h b/net/dccp/ccids/lib/tfrc.h
index 1fb1187bbf1c..ed9857527acf 100644
--- a/net/dccp/ccids/lib/tfrc.h
+++ b/net/dccp/ccids/lib/tfrc.h
@@ -15,7 +15,7 @@
* (at your option) any later version.
*/
#include <linux/types.h>
-#include <asm/div64.h>
+#include <linux/math64.h>
#include "../../dccp.h"
/* internal includes that this module exports: */
#include "loss_interval.h"
@@ -29,21 +29,19 @@ extern int tfrc_debug;
#endif
/* integer-arithmetic divisions of type (a * 1000000)/b */
-static inline u64 scaled_div(u64 a, u32 b)
+static inline u64 scaled_div(u64 a, u64 b)
{
BUG_ON(b==0);
- a *= 1000000;
- do_div(a, b);
- return a;
+ return div64_u64(a * 1000000, b);
}
-static inline u32 scaled_div32(u64 a, u32 b)
+static inline u32 scaled_div32(u64 a, u64 b)
{
u64 result = scaled_div(a, b);
if (result > UINT_MAX) {
- DCCP_CRIT("Overflow: a(%llu)/b(%u) > ~0U",
- (unsigned long long)a, b);
+ DCCP_CRIT("Overflow: %llu/%llu > UINT_MAX",
+ (unsigned long long)a, (unsigned long long)b);
return UINT_MAX;
}
return result;
@@ -58,7 +56,14 @@ static inline u32 tfrc_ewma(const u32 avg, const u32 newval, const u8 weight)
return avg ? (weight * avg + (10 - weight) * newval) / 10 : newval;
}
-extern u32 tfrc_calc_x(u16 s, u32 R, u32 p);
-extern u32 tfrc_calc_x_reverse_lookup(u32 fvalue);
+extern u32 tfrc_calc_x(u16 s, u32 R, u32 p);
+extern u32 tfrc_calc_x_reverse_lookup(u32 fvalue);
+extern int tfrc_tx_packet_history_init(void);
+extern void tfrc_tx_packet_history_exit(void);
+extern int tfrc_rx_packet_history_init(void);
+extern void tfrc_rx_packet_history_exit(void);
+
+extern int tfrc_li_init(void);
+extern void tfrc_li_exit(void);
#endif /* _TFRC_H_ */
diff --git a/net/dccp/ccids/lib/tfrc_equation.c b/net/dccp/ccids/lib/tfrc_equation.c
index e4e64b76c10c..2f20a29cffe4 100644
--- a/net/dccp/ccids/lib/tfrc_equation.c
+++ b/net/dccp/ccids/lib/tfrc_equation.c
@@ -661,7 +661,7 @@ u32 tfrc_calc_x(u16 s, u32 R, u32 p)
EXPORT_SYMBOL_GPL(tfrc_calc_x);
-/*
+/**
* tfrc_calc_x_reverse_lookup - try to find p given f(p)
*
* @fvalue: function value to match, scaled by 1000000
@@ -676,11 +676,11 @@ u32 tfrc_calc_x_reverse_lookup(u32 fvalue)
/* Error cases. */
if (fvalue < tfrc_calc_x_lookup[0][1]) {
- DCCP_WARN("fvalue %d smaller than resolution\n", fvalue);
- return tfrc_calc_x_lookup[0][1];
+ DCCP_WARN("fvalue %u smaller than resolution\n", fvalue);
+ return TFRC_SMALLEST_P;
}
if (fvalue > tfrc_calc_x_lookup[TFRC_CALC_X_ARRSIZE - 1][0]) {
- DCCP_WARN("fvalue %d exceeds bounds!\n", fvalue);
+ DCCP_WARN("fvalue %u exceeds bounds!\n", fvalue);
return 1000000;
}
diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c
index c22a3780c14e..37d27bcb361f 100644
--- a/net/dccp/ipv4.c
+++ b/net/dccp/ipv4.c
@@ -589,7 +589,7 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
goto drop;
- req = reqsk_alloc(&dccp_request_sock_ops);
+ req = inet_reqsk_alloc(&dccp_request_sock_ops);
if (req == NULL)
goto drop;
@@ -605,7 +605,6 @@ int dccp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
ireq = inet_rsk(req);
ireq->loc_addr = ip_hdr(skb)->daddr;
ireq->rmt_addr = ip_hdr(skb)->saddr;
- ireq->opt = NULL;
/*
* Step 3: Process LISTEN state
diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
index 9b1129bb7ece..f7fe2a572d7b 100644
--- a/net/dccp/ipv6.c
+++ b/net/dccp/ipv6.c
@@ -421,7 +421,6 @@ static int dccp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
ireq6 = inet6_rsk(req);
ipv6_addr_copy(&ireq6->rmt_addr, &ipv6_hdr(skb)->saddr);
ipv6_addr_copy(&ireq6->loc_addr, &ipv6_hdr(skb)->daddr);
- ireq6->pktopts = NULL;
if (ipv6_opt_accepted(sk, skb) ||
np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo ||
diff --git a/net/dccp/minisocks.c b/net/dccp/minisocks.c
index 33ad48321b08..66dca5bba858 100644
--- a/net/dccp/minisocks.c
+++ b/net/dccp/minisocks.c
@@ -165,12 +165,12 @@ out_free:
/* See dccp_v4_conn_request */
newdmsk->dccpms_sequence_window = req->rcv_wnd;
- newdp->dccps_gar = newdp->dccps_isr = dreq->dreq_isr;
- dccp_update_gsr(newsk, dreq->dreq_isr);
-
- newdp->dccps_iss = dreq->dreq_iss;
+ newdp->dccps_gar = newdp->dccps_iss = dreq->dreq_iss;
dccp_update_gss(newsk, dreq->dreq_iss);
+ newdp->dccps_isr = dreq->dreq_isr;
+ dccp_update_gsr(newsk, dreq->dreq_isr);
+
/*
* SWL and AWL are initially adjusted so that they are not less than
* the initial Sequence Numbers received and sent, respectively:
diff --git a/net/dccp/options.c b/net/dccp/options.c
index d2a84a2fecee..43bc24e761d0 100644
--- a/net/dccp/options.c
+++ b/net/dccp/options.c
@@ -107,9 +107,11 @@ int dccp_parse_options(struct sock *sk, struct dccp_request_sock *dreq,
*
* CCID-specific options are ignored during connection setup, as
* negotiation may still be in progress (see RFC 4340, 10.3).
+ * The same applies to Ack Vectors, as these depend on the CCID.
*
*/
- if (dreq != NULL && opt >= 128)
+ if (dreq != NULL && (opt >= 128 ||
+ opt == DCCPO_ACK_VECTOR_0 || opt == DCCPO_ACK_VECTOR_1))
goto ignore_option;
switch (opt) {
diff --git a/net/dccp/output.c b/net/dccp/output.c
index 1f8a9b64c083..fe20068c5d8e 100644
--- a/net/dccp/output.c
+++ b/net/dccp/output.c
@@ -508,6 +508,7 @@ void dccp_send_ack(struct sock *sk)
EXPORT_SYMBOL_GPL(dccp_send_ack);
+#if 0
/* FIXME: Is this still necessary (11.3) - currently nowhere used by DCCP. */
void dccp_send_delayed_ack(struct sock *sk)
{
@@ -538,6 +539,7 @@ void dccp_send_delayed_ack(struct sock *sk)
icsk->icsk_ack.timeout = timeout;
sk_reset_timer(sk, &icsk->icsk_delack_timer, timeout);
}
+#endif
void dccp_send_sync(struct sock *sk, const u64 ackno,
const enum dccp_pkt_type pkt_type)
diff --git a/net/dccp/probe.c b/net/dccp/probe.c
index 0bcdc9250279..81368a7f5379 100644
--- a/net/dccp/probe.c
+++ b/net/dccp/probe.c
@@ -42,7 +42,7 @@ static int bufsize = 64 * 1024;
static const char procname[] = "dccpprobe";
-struct {
+static struct {
struct kfifo *fifo;
spinlock_t lock;
wait_queue_head_t wait;
diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c
index 3b83c34019fc..0d4d72827e4b 100644
--- a/net/ipv4/fib_semantics.c
+++ b/net/ipv4/fib_semantics.c
@@ -960,7 +960,10 @@ int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event,
rtm->rtm_dst_len = dst_len;
rtm->rtm_src_len = 0;
rtm->rtm_tos = tos;
- rtm->rtm_table = tb_id;
+ if (tb_id < 256)
+ rtm->rtm_table = tb_id;
+ else
+ rtm->rtm_table = RT_TABLE_COMPAT;
NLA_PUT_U32(skb, RTA_TABLE, tb_id);
rtm->rtm_type = type;
rtm->rtm_flags = fi->fib_flags;
diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c
index 73ba98921d64..d182a2a26291 100644
--- a/net/ipv4/syncookies.c
+++ b/net/ipv4/syncookies.c
@@ -285,7 +285,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
cookie_check_timestamp(&tcp_opt);
ret = NULL;
- req = reqsk_alloc(&tcp_request_sock_ops); /* for safety */
+ req = inet_reqsk_alloc(&tcp_request_sock_ops); /* for safety */
if (!req)
goto out;
@@ -301,7 +301,6 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb,
ireq->rmt_port = th->source;
ireq->loc_addr = ip_hdr(skb)->daddr;
ireq->rmt_addr = ip_hdr(skb)->saddr;
- ireq->opt = NULL;
ireq->snd_wscale = tcp_opt.snd_wscale;
ireq->rcv_wscale = tcp_opt.rcv_wscale;
ireq->sack_ok = tcp_opt.sack_ok;
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index cd601a866c2f..4f8485c67d1a 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1285,7 +1285,7 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb)
if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1)
goto drop;
- req = reqsk_alloc(&tcp_request_sock_ops);
+ req = inet_reqsk_alloc(&tcp_request_sock_ops);
if (!req)
goto drop;
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 3c6aafb02183..e84b3fd17fb4 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -191,7 +191,7 @@ lookup_protocol:
np->mcast_hops = -1;
np->mc_loop = 1;
np->pmtudisc = IPV6_PMTUDISC_WANT;
- np->ipv6only = init_net.ipv6.sysctl.bindv6only;
+ np->ipv6only = net->ipv6.sysctl.bindv6only;
/* Init the ipv4 part of the socket since we can have sockets
* using v6 API for ipv4.
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index b9c2de84a8a2..0f0f94a40335 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -705,6 +705,11 @@ int datagram_send_ctl(struct net *net,
}
*hlimit = *(int *)CMSG_DATA(cmsg);
+ if (*hlimit < -1 || *hlimit > 0xff) {
+ err = -EINVAL;
+ goto exit_f;
+ }
+
break;
case IPV6_TCLASS:
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
index 26b83e512a09..c042ce19bd14 100644
--- a/net/ipv6/ipv6_sockglue.c
+++ b/net/ipv6/ipv6_sockglue.c
@@ -67,7 +67,7 @@ int ip6_ra_control(struct sock *sk, int sel, void (*destructor)(struct sock *))
/* RA packet may be delivered ONLY to IPPROTO_RAW socket */
if (sk->sk_type != SOCK_RAW || inet_sk(sk)->num != IPPROTO_RAW)
- return -EINVAL;
+ return -ENOPROTOOPT;
new_ra = (sel>=0) ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;
@@ -446,7 +446,7 @@ done:
case IPV6_MULTICAST_HOPS:
if (sk->sk_type == SOCK_STREAM)
- goto e_inval;
+ break;
if (optlen < sizeof(int))
goto e_inval;
if (val > 255 || val < -1)
@@ -458,13 +458,15 @@ done:
case IPV6_MULTICAST_LOOP:
if (optlen < sizeof(int))
goto e_inval;
+ if (val != valbool)
+ goto e_inval;
np->mc_loop = valbool;
retv = 0;
break;
case IPV6_MULTICAST_IF:
if (sk->sk_type == SOCK_STREAM)
- goto e_inval;
+ break;
if (optlen < sizeof(int))
goto e_inval;
@@ -860,7 +862,7 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
if (sk->sk_protocol != IPPROTO_UDP &&
sk->sk_protocol != IPPROTO_UDPLITE &&
sk->sk_protocol != IPPROTO_TCP)
- return -EINVAL;
+ return -ENOPROTOOPT;
if (sk->sk_state != TCP_ESTABLISHED)
return -ENOTCONN;
val = sk->sk_family;
@@ -874,6 +876,8 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname,
return -EINVAL;
if (copy_from_user(&gsf, optval, GROUP_FILTER_SIZE(0)))
return -EFAULT;
+ if (gsf.gf_group.ss_family != AF_INET6)
+ return -EADDRNOTAVAIL;
lock_sock(sk);
err = ip6_mc_msfget(sk, &gsf,
(struct group_filter __user *)optval, optlen);
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 220cffe9e63b..d1f3e19b06c7 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -2196,8 +2196,12 @@ static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt,
NLA_PUT_U32(skb, RTA_PRIORITY, rt->rt6i_metric);
- expires = (rt->rt6i_flags & RTF_EXPIRES) ?
- rt->rt6i_expires - jiffies : 0;
+ if (!(rt->rt6i_flags & RTF_EXPIRES))
+ expires = 0;
+ else if (rt->rt6i_expires - jiffies < INT_MAX)
+ expires = rt->rt6i_expires - jiffies;
+ else
+ expires = INT_MAX;
if (rtnl_put_cacheinfo(skb, &rt->u.dst, 0, 0, 0,
expires, rt->u.dst.error) < 0)
diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c
index 938ce4ecde55..3ecc1157994e 100644
--- a/net/ipv6/syncookies.c
+++ b/net/ipv6/syncookies.c
@@ -198,7 +198,6 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb)
ireq = inet_rsk(req);
ireq6 = inet6_rsk(req);
treq = tcp_rsk(req);
- ireq6->pktopts = NULL;
if (security_inet_conn_request(sk, skb, req)) {
reqsk_free(req);
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 715965f0fac0..cb46749d4c32 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1299,7 +1299,6 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb)
treq = inet6_rsk(req);
ipv6_addr_copy(&treq->rmt_addr, &ipv6_hdr(skb)->saddr);
ipv6_addr_copy(&treq->loc_addr, &ipv6_hdr(skb)->daddr);
- treq->pktopts = NULL;
if (!want_cookie)
TCP_ECN_create_request(req, tcp_hdr(skb));
diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c
index 918970762131..8de511070593 100644
--- a/net/iucv/iucv.c
+++ b/net/iucv/iucv.c
@@ -497,7 +497,7 @@ static void iucv_setmask_up(void)
/* Disable all cpu but the first in cpu_irq_cpumask. */
cpumask = iucv_irq_cpumask;
cpu_clear(first_cpu(iucv_irq_cpumask), cpumask);
- for_each_cpu_mask(cpu, cpumask)
+ for_each_cpu_mask_nr(cpu, cpumask)
smp_call_function_single(cpu, iucv_block_cpu, NULL, 0, 1);
}
diff --git a/net/key/af_key.c b/net/key/af_key.c
index 9bba7ac5fee0..7470e367272b 100644
--- a/net/key/af_key.c
+++ b/net/key/af_key.c
@@ -3030,6 +3030,9 @@ static int key_notify_sa_expire(struct xfrm_state *x, struct km_event *c)
static int pfkey_send_notify(struct xfrm_state *x, struct km_event *c)
{
+ if (atomic_read(&pfkey_socks_nr) == 0)
+ return 0;
+
switch (c->event) {
case XFRM_MSG_EXPIRE:
return key_notify_sa_expire(x, c);
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index c7314bf4bec2..006486b26726 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -899,7 +899,7 @@ extern const struct iw_handler_def ieee80211_iw_handler_def;
/* ieee80211_ioctl.c */
-int ieee80211_set_freq(struct ieee80211_local *local, int freq);
+int ieee80211_set_freq(struct net_device *dev, int freq);
/* ieee80211_sta.c */
void ieee80211_sta_timer(unsigned long data);
void ieee80211_sta_work(struct work_struct *work);
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 5c876450b14c..98c0b5e56ecc 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -511,6 +511,7 @@ static int ieee80211_stop(struct net_device *dev)
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_IBSS:
sdata->u.sta.state = IEEE80211_DISABLED;
+ memset(sdata->u.sta.bssid, 0, ETH_ALEN);
del_timer_sync(&sdata->u.sta.timer);
/*
* When we get here, the interface is marked down.
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 841278f1df8e..4d2b582dd055 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -44,7 +44,7 @@
#define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ)
#define IEEE80211_SCAN_INTERVAL (2 * HZ)
#define IEEE80211_SCAN_INTERVAL_SLOW (15 * HZ)
-#define IEEE80211_IBSS_JOIN_TIMEOUT (20 * HZ)
+#define IEEE80211_IBSS_JOIN_TIMEOUT (7 * HZ)
#define IEEE80211_PROBE_DELAY (HZ / 33)
#define IEEE80211_CHANNEL_TIME (HZ / 33)
@@ -2336,6 +2336,7 @@ static int ieee80211_sta_join_ibss(struct net_device *dev,
u8 *pos;
struct ieee80211_sub_if_data *sdata;
struct ieee80211_supported_band *sband;
+ union iwreq_data wrqu;
sband = local->hw.wiphy->bands[local->hw.conf.channel->band];
@@ -2358,13 +2359,10 @@ static int ieee80211_sta_join_ibss(struct net_device *dev,
sdata->drop_unencrypted = bss->capability &
WLAN_CAPABILITY_PRIVACY ? 1 : 0;
- res = ieee80211_set_freq(local, bss->freq);
+ res = ieee80211_set_freq(dev, bss->freq);
- if (local->oper_channel->flags & IEEE80211_CHAN_NO_IBSS) {
- printk(KERN_DEBUG "%s: IBSS not allowed on frequency "
- "%d MHz\n", dev->name, local->oper_channel->center_freq);
- return -1;
- }
+ if (res)
+ return res;
/* Set beacon template */
skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
@@ -2479,6 +2477,10 @@ static int ieee80211_sta_join_ibss(struct net_device *dev,
ifsta->state = IEEE80211_IBSS_JOINED;
mod_timer(&ifsta->timer, jiffies + IEEE80211_IBSS_MERGE_INTERVAL);
+ memset(&wrqu, 0, sizeof(wrqu));
+ memcpy(wrqu.ap_addr.sa_data, bss->bssid, ETH_ALEN);
+ wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
+
return res;
}
@@ -3486,7 +3488,7 @@ static int ieee80211_sta_config_auth(struct net_device *dev,
spin_unlock_bh(&local->sta_bss_lock);
if (selected) {
- ieee80211_set_freq(local, selected->freq);
+ ieee80211_set_freq(dev, selected->freq);
if (!(ifsta->flags & IEEE80211_STA_SSID_SET))
ieee80211_sta_set_ssid(dev, selected->ssid,
selected->ssid_len);
diff --git a/net/mac80211/wext.c b/net/mac80211/wext.c
index 8311bb24f9f3..a8bb8e31b1ec 100644
--- a/net/mac80211/wext.c
+++ b/net/mac80211/wext.c
@@ -290,14 +290,22 @@ static int ieee80211_ioctl_giwmode(struct net_device *dev,
return 0;
}
-int ieee80211_set_freq(struct ieee80211_local *local, int freqMHz)
+int ieee80211_set_freq(struct net_device *dev, int freqMHz)
{
int ret = -EINVAL;
struct ieee80211_channel *chan;
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
chan = ieee80211_get_channel(local->hw.wiphy, freqMHz);
if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) {
+ if (sdata->vif.type == IEEE80211_IF_TYPE_IBSS &&
+ chan->flags & IEEE80211_CHAN_NO_IBSS) {
+ printk(KERN_DEBUG "%s: IBSS not allowed on frequency "
+ "%d MHz\n", dev->name, chan->center_freq);
+ return ret;
+ }
local->oper_channel = chan;
if (local->sta_sw_scanning || local->sta_hw_scanning)
@@ -315,7 +323,6 @@ static int ieee80211_ioctl_siwfreq(struct net_device *dev,
struct iw_request_info *info,
struct iw_freq *freq, char *extra)
{
- struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (sdata->vif.type == IEEE80211_IF_TYPE_STA)
@@ -329,14 +336,14 @@ static int ieee80211_ioctl_siwfreq(struct net_device *dev,
IEEE80211_STA_AUTO_CHANNEL_SEL;
return 0;
} else
- return ieee80211_set_freq(local,
+ return ieee80211_set_freq(dev,
ieee80211_channel_to_frequency(freq->m));
} else {
int i, div = 1000000;
for (i = 0; i < freq->e; i++)
div /= 10;
if (div > 0)
- return ieee80211_set_freq(local, freq->m / div);
+ return ieee80211_set_freq(dev, freq->m / div);
else
return -EINVAL;
}
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index c4b1799da5d7..662c1ccfee26 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -196,8 +196,6 @@ destroy_conntrack(struct nf_conntrack *nfct)
if (l4proto && l4proto->destroy)
l4proto->destroy(ct);
- nf_ct_ext_destroy(ct);
-
rcu_read_unlock();
spin_lock_bh(&nf_conntrack_lock);
@@ -520,6 +518,7 @@ static void nf_conntrack_free_rcu(struct rcu_head *head)
void nf_conntrack_free(struct nf_conn *ct)
{
+ nf_ct_ext_destroy(ct);
call_rcu(&ct->rcu, nf_conntrack_free_rcu);
}
EXPORT_SYMBOL_GPL(nf_conntrack_free);
diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c
index 7d1b11703741..8e0b4c8f62a8 100644
--- a/net/netfilter/nf_conntrack_helper.c
+++ b/net/netfilter/nf_conntrack_helper.c
@@ -20,6 +20,7 @@
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/netdevice.h>
+#include <linux/rculist.h>
#include <net/netfilter/nf_conntrack.h>
#include <net/netfilter/nf_conntrack_l3proto.h>
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 0edefcfc5949..077bcd228799 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -18,6 +18,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
+#include <linux/rculist.h>
#include <linux/types.h>
#include <linux/timer.h>
#include <linux/skbuff.h>
diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c
index bc11d7092032..9fda6ee95a31 100644
--- a/net/netfilter/nf_log.c
+++ b/net/netfilter/nf_log.c
@@ -92,10 +92,6 @@ void nf_log_packet(int pf,
vsnprintf(prefix, sizeof(prefix), fmt, args);
va_end(args);
logger->logfn(pf, hooknum, skb, in, out, loginfo, prefix);
- } else if (net_ratelimit()) {
- printk(KERN_WARNING "nf_log_packet: can\'t log since "
- "no backend logging module loaded in! Please either "
- "load one, or disable logging explicitly\n");
}
rcu_read_unlock();
}
diff --git a/net/netlabel/netlabel_domainhash.c b/net/netlabel/netlabel_domainhash.c
index 02c2f7c0b255..643c032a3a57 100644
--- a/net/netlabel/netlabel_domainhash.c
+++ b/net/netlabel/netlabel_domainhash.c
@@ -30,8 +30,7 @@
*/
#include <linux/types.h>
-#include <linux/rcupdate.h>
-#include <linux/list.h>
+#include <linux/rculist.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/string.h>
diff --git a/net/sunrpc/auth_gss/Makefile b/net/sunrpc/auth_gss/Makefile
index f3431a7e33da..4de8bcf26fa7 100644
--- a/net/sunrpc/auth_gss/Makefile
+++ b/net/sunrpc/auth_gss/Makefile
@@ -5,12 +5,12 @@
obj-$(CONFIG_SUNRPC_GSS) += auth_rpcgss.o
auth_rpcgss-objs := auth_gss.o gss_generic_token.o \
- gss_mech_switch.o svcauth_gss.o gss_krb5_crypto.o
+ gss_mech_switch.o svcauth_gss.o
obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o
rpcsec_gss_krb5-objs := gss_krb5_mech.o gss_krb5_seal.o gss_krb5_unseal.o \
- gss_krb5_seqnum.o gss_krb5_wrap.o
+ gss_krb5_seqnum.o gss_krb5_wrap.o gss_krb5_crypto.o
obj-$(CONFIG_RPCSEC_GSS_SPKM3) += rpcsec_gss_spkm3.o
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index cc12d5f5d5da..53e027e7cb4d 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -65,7 +65,7 @@ static const struct rpc_credops gss_nullops;
#define NFS_NGROUPS 16
-#define GSS_CRED_SLACK 1024 /* XXX: unused */
+#define GSS_CRED_SLACK (RPC_MAX_AUTH_SIZE * 2)
/* length of a krb5 verifier (48), plus data added before arguments when
* using integrity (two 4-byte integers): */
#define GSS_VERF_SLACK 100
@@ -1137,15 +1137,21 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
inpages = snd_buf->pages + first;
snd_buf->pages = rqstp->rq_enc_pages;
snd_buf->page_base -= first << PAGE_CACHE_SHIFT;
- /* Give the tail its own page, in case we need extra space in the
- * head when wrapping: */
+ /*
+ * Give the tail its own page, in case we need extra space in the
+ * head when wrapping:
+ *
+ * call_allocate() allocates twice the slack space required
+ * by the authentication flavor to rq_callsize.
+ * For GSS, slack is GSS_CRED_SLACK.
+ */
if (snd_buf->page_len || snd_buf->tail[0].iov_len) {
tmp = page_address(rqstp->rq_enc_pages[rqstp->rq_enc_pages_num - 1]);
memcpy(tmp, snd_buf->tail[0].iov_base, snd_buf->tail[0].iov_len);
snd_buf->tail[0].iov_base = tmp;
}
maj_stat = gss_wrap(ctx->gc_gss_ctx, offset, snd_buf, inpages);
- /* RPC_SLACK_SPACE should prevent this ever happening: */
+ /* slack space should prevent this ever happening: */
BUG_ON(snd_buf->len > snd_buf->buflen);
status = -EIO;
/* We're assuming that when GSS_S_CONTEXT_EXPIRED, the encryption was
diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c
index 1d52308ca324..d0f3371c62e2 100644
--- a/net/sunrpc/auth_gss/gss_krb5_crypto.c
+++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c
@@ -83,8 +83,6 @@ out:
return ret;
}
-EXPORT_SYMBOL(krb5_encrypt);
-
u32
krb5_decrypt(
struct crypto_blkcipher *tfm,
@@ -118,8 +116,6 @@ out:
return ret;
}
-EXPORT_SYMBOL(krb5_decrypt);
-
static int
checksummer(struct scatterlist *sg, void *data)
{
@@ -161,8 +157,6 @@ out:
return err ? GSS_S_FAILURE : 0;
}
-EXPORT_SYMBOL(make_checksum);
-
struct encryptor_desc {
u8 iv[8]; /* XXX hard-coded blocksize */
struct blkcipher_desc desc;
@@ -262,8 +256,6 @@ gss_encrypt_xdr_buf(struct crypto_blkcipher *tfm, struct xdr_buf *buf,
return ret;
}
-EXPORT_SYMBOL(gss_encrypt_xdr_buf);
-
struct decryptor_desc {
u8 iv[8]; /* XXX hard-coded blocksize */
struct blkcipher_desc desc;
@@ -335,4 +327,58 @@ gss_decrypt_xdr_buf(struct crypto_blkcipher *tfm, struct xdr_buf *buf,
return xdr_process_buf(buf, offset, buf->len - offset, decryptor, &desc);
}
-EXPORT_SYMBOL(gss_decrypt_xdr_buf);
+/*
+ * This function makes the assumption that it was ultimately called
+ * from gss_wrap().
+ *
+ * The client auth_gss code moves any existing tail data into a
+ * separate page before calling gss_wrap.
+ * The server svcauth_gss code ensures that both the head and the
+ * tail have slack space of RPC_MAX_AUTH_SIZE before calling gss_wrap.
+ *
+ * Even with that guarantee, this function may be called more than
+ * once in the processing of gss_wrap(). The best we can do is
+ * verify at compile-time (see GSS_KRB5_SLACK_CHECK) that the
+ * largest expected shift will fit within RPC_MAX_AUTH_SIZE.
+ * At run-time we can verify that a single invocation of this
+ * function doesn't attempt to use more the RPC_MAX_AUTH_SIZE.
+ */
+
+int
+shift_head_data(struct xdr_buf *buf, unsigned int base, unsigned int shiftlen)
+{
+ u8 *p;
+
+ if (shiftlen == 0)
+ return 0;
+
+ GSS_KRB5_SLACK_CHECK;
+ BUG_ON(shiftlen > RPC_MAX_AUTH_SIZE);
+
+ /*
+ * If there is a tail, and it shares a page with the head,
+ * make sure we don't clobber the tail. This is a just a
+ * defensive check.
+ */
+ if (buf->tail[0].iov_base != NULL) {
+ if ((((long)buf->tail[0].iov_base >> PAGE_CACHE_SHIFT) ==
+ ((long)buf->head[0].iov_base >> PAGE_CACHE_SHIFT)) &&
+ buf->tail[0].iov_base - buf->head[0].iov_base < shiftlen) {
+ dprintk("%s: collision: head %p:%zu, tail %p:%zu, "
+ "shiftlen %u\n",
+ __func__, buf->head[0].iov_base,
+ buf->head[0].iov_len, buf->tail[0].iov_base,
+ buf->tail[0].iov_len, shiftlen);
+ return 1;
+ }
+ }
+
+ p = buf->head[0].iov_base + base;
+
+ memmove(p + shiftlen, p, buf->head[0].iov_len - base);
+
+ buf->head[0].iov_len += shiftlen;
+ buf->len += shiftlen;
+
+ return 0;
+}
diff --git a/net/sunrpc/auth_gss/gss_krb5_seal.c b/net/sunrpc/auth_gss/gss_krb5_seal.c
index 5f1d36dfbcf7..b8f42ef7178e 100644
--- a/net/sunrpc/auth_gss/gss_krb5_seal.c
+++ b/net/sunrpc/auth_gss/gss_krb5_seal.c
@@ -78,7 +78,7 @@ gss_get_mic_kerberos(struct gss_ctx *gss_ctx, struct xdr_buf *text,
struct krb5_ctx *ctx = gss_ctx->internal_ctx_id;
char cksumdata[16];
struct xdr_netobj md5cksum = {.len = 0, .data = cksumdata};
- unsigned char *ptr, *krb5_hdr, *msg_start;
+ unsigned char *ptr, *msg_start;
s32 now;
u32 seq_send;
@@ -87,36 +87,36 @@ gss_get_mic_kerberos(struct gss_ctx *gss_ctx, struct xdr_buf *text,
now = get_seconds();
- token->len = g_token_size(&ctx->mech_used, 24);
+ token->len = g_token_size(&ctx->mech_used, GSS_KRB5_TOK_HDR_LEN + 8);
ptr = token->data;
- g_make_token_header(&ctx->mech_used, 24, &ptr);
+ g_make_token_header(&ctx->mech_used, GSS_KRB5_TOK_HDR_LEN + 8, &ptr);
- *ptr++ = (unsigned char) ((KG_TOK_MIC_MSG>>8)&0xff);
- *ptr++ = (unsigned char) (KG_TOK_MIC_MSG&0xff);
+ /* ptr now at header described in rfc 1964, section 1.2.1: */
+ ptr[0] = (unsigned char) ((KG_TOK_MIC_MSG >> 8) & 0xff);
+ ptr[1] = (unsigned char) (KG_TOK_MIC_MSG & 0xff);
- /* ptr now at byte 2 of header described in rfc 1964, section 1.2.1: */
- krb5_hdr = ptr - 2;
- msg_start = krb5_hdr + 24;
+ msg_start = ptr + GSS_KRB5_TOK_HDR_LEN + 8;
- *(__be16 *)(krb5_hdr + 2) = htons(SGN_ALG_DES_MAC_MD5);
- memset(krb5_hdr + 4, 0xff, 4);
+ *(__be16 *)(ptr + 2) = htons(SGN_ALG_DES_MAC_MD5);
+ memset(ptr + 4, 0xff, 4);
- if (make_checksum("md5", krb5_hdr, 8, text, 0, &md5cksum))
+ if (make_checksum("md5", ptr, 8, text, 0, &md5cksum))
return GSS_S_FAILURE;
if (krb5_encrypt(ctx->seq, NULL, md5cksum.data,
md5cksum.data, md5cksum.len))
return GSS_S_FAILURE;
- memcpy(krb5_hdr + 16, md5cksum.data + md5cksum.len - 8, 8);
+ memcpy(ptr + GSS_KRB5_TOK_HDR_LEN, md5cksum.data + md5cksum.len - 8, 8);
spin_lock(&krb5_seq_lock);
seq_send = ctx->seq_send++;
spin_unlock(&krb5_seq_lock);
if (krb5_make_seq_num(ctx->seq, ctx->initiate ? 0 : 0xff,
- seq_send, krb5_hdr + 16, krb5_hdr + 8))
+ seq_send, ptr + GSS_KRB5_TOK_HDR_LEN,
+ ptr + 8))
return GSS_S_FAILURE;
return (ctx->endtime < now) ? GSS_S_CONTEXT_EXPIRED : GSS_S_COMPLETE;
diff --git a/net/sunrpc/auth_gss/gss_krb5_unseal.c b/net/sunrpc/auth_gss/gss_krb5_unseal.c
index d91a5d004803..066ec73c84d6 100644
--- a/net/sunrpc/auth_gss/gss_krb5_unseal.c
+++ b/net/sunrpc/auth_gss/gss_krb5_unseal.c
@@ -92,30 +92,30 @@ gss_verify_mic_kerberos(struct gss_ctx *gss_ctx,
read_token->len))
return GSS_S_DEFECTIVE_TOKEN;
- if ((*ptr++ != ((KG_TOK_MIC_MSG>>8)&0xff)) ||
- (*ptr++ != ( KG_TOK_MIC_MSG &0xff)) )
+ if ((ptr[0] != ((KG_TOK_MIC_MSG >> 8) & 0xff)) ||
+ (ptr[1] != (KG_TOK_MIC_MSG & 0xff)))
return GSS_S_DEFECTIVE_TOKEN;
/* XXX sanity-check bodysize?? */
- signalg = ptr[0] + (ptr[1] << 8);
+ signalg = ptr[2] + (ptr[3] << 8);
if (signalg != SGN_ALG_DES_MAC_MD5)
return GSS_S_DEFECTIVE_TOKEN;
- sealalg = ptr[2] + (ptr[3] << 8);
+ sealalg = ptr[4] + (ptr[5] << 8);
if (sealalg != SEAL_ALG_NONE)
return GSS_S_DEFECTIVE_TOKEN;
- if ((ptr[4] != 0xff) || (ptr[5] != 0xff))
+ if ((ptr[6] != 0xff) || (ptr[7] != 0xff))
return GSS_S_DEFECTIVE_TOKEN;
- if (make_checksum("md5", ptr - 2, 8, message_buffer, 0, &md5cksum))
+ if (make_checksum("md5", ptr, 8, message_buffer, 0, &md5cksum))
return GSS_S_FAILURE;
if (krb5_encrypt(ctx->seq, NULL, md5cksum.data, md5cksum.data, 16))
return GSS_S_FAILURE;
- if (memcmp(md5cksum.data + 8, ptr + 14, 8))
+ if (memcmp(md5cksum.data + 8, ptr + GSS_KRB5_TOK_HDR_LEN, 8))
return GSS_S_BAD_SIG;
/* it got through unscathed. Make sure the context is unexpired */
@@ -127,7 +127,7 @@ gss_verify_mic_kerberos(struct gss_ctx *gss_ctx,
/* do sequencing checks */
- if (krb5_get_seq_num(ctx->seq, ptr + 14, ptr + 6, &direction, &seqnum))
+ if (krb5_get_seq_num(ctx->seq, ptr + GSS_KRB5_TOK_HDR_LEN, ptr + 8, &direction, &seqnum))
return GSS_S_FAILURE;
if ((ctx->initiate && direction != 0xff) ||
diff --git a/net/sunrpc/auth_gss/gss_krb5_wrap.c b/net/sunrpc/auth_gss/gss_krb5_wrap.c
index b00b1b426301..a0660f5c3220 100644
--- a/net/sunrpc/auth_gss/gss_krb5_wrap.c
+++ b/net/sunrpc/auth_gss/gss_krb5_wrap.c
@@ -87,8 +87,8 @@ out:
return 0;
}
-static inline void
-make_confounder(char *p, int blocksize)
+static void
+make_confounder(char *p, u32 conflen)
{
static u64 i = 0;
u64 *q = (u64 *)p;
@@ -102,8 +102,22 @@ make_confounder(char *p, int blocksize)
* uniqueness would mean worrying about atomicity and rollover, and I
* don't care enough. */
- BUG_ON(blocksize != 8);
- *q = i++;
+ /* initialize to random value */
+ if (i == 0) {
+ i = random32();
+ i = (i << 32) | random32();
+ }
+
+ switch (conflen) {
+ case 16:
+ *q++ = i++;
+ /* fall through */
+ case 8:
+ *q++ = i++;
+ break;
+ default:
+ BUG();
+ }
}
/* Assumptions: the head and tail of inbuf are ours to play with.
@@ -122,7 +136,7 @@ gss_wrap_kerberos(struct gss_ctx *ctx, int offset,
char cksumdata[16];
struct xdr_netobj md5cksum = {.len = 0, .data = cksumdata};
int blocksize = 0, plainlen;
- unsigned char *ptr, *krb5_hdr, *msg_start;
+ unsigned char *ptr, *msg_start;
s32 now;
int headlen;
struct page **tmp_pages;
@@ -130,6 +144,7 @@ gss_wrap_kerberos(struct gss_ctx *ctx, int offset,
dprintk("RPC: gss_wrap_kerberos\n");
+ GSS_KRB5_SLACK_CHECK;
now = get_seconds();
blocksize = crypto_blkcipher_blocksize(kctx->enc);
@@ -142,33 +157,31 @@ gss_wrap_kerberos(struct gss_ctx *ctx, int offset,
ptr = buf->head[0].iov_base + offset;
/* shift data to make room for header. */
+ shift_head_data(buf, offset, headlen);
+
/* XXX Would be cleverer to encrypt while copying. */
- /* XXX bounds checking, slack, etc. */
- memmove(ptr + headlen, ptr, buf->head[0].iov_len - offset);
- buf->head[0].iov_len += headlen;
- buf->len += headlen;
BUG_ON((buf->len - offset - headlen) % blocksize);
- g_make_token_header(&kctx->mech_used, 24 + plainlen, &ptr);
+ g_make_token_header(&kctx->mech_used,
+ GSS_KRB5_TOK_HDR_LEN + 8 + plainlen, &ptr);
- *ptr++ = (unsigned char) ((KG_TOK_WRAP_MSG>>8)&0xff);
- *ptr++ = (unsigned char) (KG_TOK_WRAP_MSG&0xff);
+ /* ptr now at header described in rfc 1964, section 1.2.1: */
+ ptr[0] = (unsigned char) ((KG_TOK_WRAP_MSG >> 8) & 0xff);
+ ptr[1] = (unsigned char) (KG_TOK_WRAP_MSG & 0xff);
- /* ptr now at byte 2 of header described in rfc 1964, section 1.2.1: */
- krb5_hdr = ptr - 2;
- msg_start = krb5_hdr + 24;
+ msg_start = ptr + 24;
- *(__be16 *)(krb5_hdr + 2) = htons(SGN_ALG_DES_MAC_MD5);
- memset(krb5_hdr + 4, 0xff, 4);
- *(__be16 *)(krb5_hdr + 4) = htons(SEAL_ALG_DES);
+ *(__be16 *)(ptr + 2) = htons(SGN_ALG_DES_MAC_MD5);
+ memset(ptr + 4, 0xff, 4);
+ *(__be16 *)(ptr + 4) = htons(SEAL_ALG_DES);
make_confounder(msg_start, blocksize);
/* XXXJBF: UGH!: */
tmp_pages = buf->pages;
buf->pages = pages;
- if (make_checksum("md5", krb5_hdr, 8, buf,
+ if (make_checksum("md5", ptr, 8, buf,
offset + headlen - blocksize, &md5cksum))
return GSS_S_FAILURE;
buf->pages = tmp_pages;
@@ -176,7 +189,7 @@ gss_wrap_kerberos(struct gss_ctx *ctx, int offset,
if (krb5_encrypt(kctx->seq, NULL, md5cksum.data,
md5cksum.data, md5cksum.len))
return GSS_S_FAILURE;
- memcpy(krb5_hdr + 16, md5cksum.data + md5cksum.len - 8, 8);
+ memcpy(ptr + GSS_KRB5_TOK_HDR_LEN, md5cksum.data + md5cksum.len - 8, 8);
spin_lock(&krb5_seq_lock);
seq_send = kctx->seq_send++;
@@ -185,7 +198,7 @@ gss_wrap_kerberos(struct gss_ctx *ctx, int offset,
/* XXX would probably be more efficient to compute checksum
* and encrypt at the same time: */
if ((krb5_make_seq_num(kctx->seq, kctx->initiate ? 0 : 0xff,
- seq_send, krb5_hdr + 16, krb5_hdr + 8)))
+ seq_send, ptr + GSS_KRB5_TOK_HDR_LEN, ptr + 8)))
return GSS_S_FAILURE;
if (gss_encrypt_xdr_buf(kctx->enc, buf, offset + headlen - blocksize,
@@ -219,38 +232,38 @@ gss_unwrap_kerberos(struct gss_ctx *ctx, int offset, struct xdr_buf *buf)
buf->len - offset))
return GSS_S_DEFECTIVE_TOKEN;
- if ((*ptr++ != ((KG_TOK_WRAP_MSG>>8)&0xff)) ||
- (*ptr++ != (KG_TOK_WRAP_MSG &0xff)) )
+ if ((ptr[0] != ((KG_TOK_WRAP_MSG >> 8) & 0xff)) ||
+ (ptr[1] != (KG_TOK_WRAP_MSG & 0xff)))
return GSS_S_DEFECTIVE_TOKEN;
/* XXX sanity-check bodysize?? */
/* get the sign and seal algorithms */
- signalg = ptr[0] + (ptr[1] << 8);
+ signalg = ptr[2] + (ptr[3] << 8);
if (signalg != SGN_ALG_DES_MAC_MD5)
return GSS_S_DEFECTIVE_TOKEN;
- sealalg = ptr[2] + (ptr[3] << 8);
+ sealalg = ptr[4] + (ptr[5] << 8);
if (sealalg != SEAL_ALG_DES)
return GSS_S_DEFECTIVE_TOKEN;
- if ((ptr[4] != 0xff) || (ptr[5] != 0xff))
+ if ((ptr[6] != 0xff) || (ptr[7] != 0xff))
return GSS_S_DEFECTIVE_TOKEN;
if (gss_decrypt_xdr_buf(kctx->enc, buf,
- ptr + 22 - (unsigned char *)buf->head[0].iov_base))
+ ptr + GSS_KRB5_TOK_HDR_LEN + 8 - (unsigned char *)buf->head[0].iov_base))
return GSS_S_DEFECTIVE_TOKEN;
- if (make_checksum("md5", ptr - 2, 8, buf,
- ptr + 22 - (unsigned char *)buf->head[0].iov_base, &md5cksum))
+ if (make_checksum("md5", ptr, 8, buf,
+ ptr + GSS_KRB5_TOK_HDR_LEN + 8 - (unsigned char *)buf->head[0].iov_base, &md5cksum))
return GSS_S_FAILURE;
if (krb5_encrypt(kctx->seq, NULL, md5cksum.data,
md5cksum.data, md5cksum.len))
return GSS_S_FAILURE;
- if (memcmp(md5cksum.data + 8, ptr + 14, 8))
+ if (memcmp(md5cksum.data + 8, ptr + GSS_KRB5_TOK_HDR_LEN, 8))
return GSS_S_BAD_SIG;
/* it got through unscathed. Make sure the context is unexpired */
@@ -262,8 +275,8 @@ gss_unwrap_kerberos(struct gss_ctx *ctx, int offset, struct xdr_buf *buf)
/* do sequencing checks */
- if (krb5_get_seq_num(kctx->seq, ptr + 14, ptr + 6, &direction,
- &seqnum))
+ if (krb5_get_seq_num(kctx->seq, ptr + GSS_KRB5_TOK_HDR_LEN, ptr + 8,
+ &direction, &seqnum))
return GSS_S_BAD_SIG;
if ((kctx->initiate && direction != 0xff) ||
@@ -274,7 +287,7 @@ gss_unwrap_kerberos(struct gss_ctx *ctx, int offset, struct xdr_buf *buf)
* better to copy and encrypt at the same time. */
blocksize = crypto_blkcipher_blocksize(kctx->enc);
- data_start = ptr + 22 + blocksize;
+ data_start = ptr + GSS_KRB5_TOK_HDR_LEN + 8 + blocksize;
orig_start = buf->head[0].iov_base + offset;
data_len = (buf->head[0].iov_base + buf->head[0].iov_len) - data_start;
memmove(orig_start, data_start, data_len);
diff --git a/net/sunrpc/auth_gss/gss_mech_switch.c b/net/sunrpc/auth_gss/gss_mech_switch.c
index bce9d527af08..3cfc197b6a36 100644
--- a/net/sunrpc/auth_gss/gss_mech_switch.c
+++ b/net/sunrpc/auth_gss/gss_mech_switch.c
@@ -285,6 +285,20 @@ gss_verify_mic(struct gss_ctx *context_handle,
mic_token);
}
+/*
+ * This function is called from both the client and server code.
+ * Each makes guarantees about how much "slack" space is available
+ * for the underlying function in "buf"'s head and tail while
+ * performing the wrap.
+ *
+ * The client and server code allocate RPC_MAX_AUTH_SIZE extra
+ * space in both the head and tail which is available for use by
+ * the wrap function.
+ *
+ * Underlying functions should verify they do not use more than
+ * RPC_MAX_AUTH_SIZE of extra space in either the head or tail
+ * when performing the wrap.
+ */
u32
gss_wrap(struct gss_ctx *ctx_id,
int offset,
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index 5905d56737d6..675adeb10df4 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -1287,6 +1287,14 @@ svcauth_gss_wrap_resp_priv(struct svc_rqst *rqstp)
inpages = resbuf->pages;
/* XXX: Would be better to write some xdr helper functions for
* nfs{2,3,4}xdr.c that place the data right, instead of copying: */
+
+ /*
+ * If there is currently tail data, make sure there is
+ * room for the head, tail, and 2 * RPC_MAX_AUTH_SIZE in
+ * the page, and move the current tail data such that
+ * there is RPC_MAX_AUTH_SIZE slack space available in
+ * both the head and tail.
+ */
if (resbuf->tail[0].iov_base) {
BUG_ON(resbuf->tail[0].iov_base >= resbuf->head[0].iov_base
+ PAGE_SIZE);
@@ -1299,6 +1307,13 @@ svcauth_gss_wrap_resp_priv(struct svc_rqst *rqstp)
resbuf->tail[0].iov_len);
resbuf->tail[0].iov_base += RPC_MAX_AUTH_SIZE;
}
+ /*
+ * If there is no current tail data, make sure there is
+ * room for the head data, and 2 * RPC_MAX_AUTH_SIZE in the
+ * allotted page, and set up tail information such that there
+ * is RPC_MAX_AUTH_SIZE slack space available in both the
+ * head and tail.
+ */
if (resbuf->tail[0].iov_base == NULL) {
if (resbuf->head[0].iov_len + 2*RPC_MAX_AUTH_SIZE > PAGE_SIZE)
return -ENOMEM;
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c
index 01c7e311b904..5a32cb7c4bb4 100644
--- a/net/sunrpc/svc.c
+++ b/net/sunrpc/svc.c
@@ -18,6 +18,7 @@
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/module.h>
+#include <linux/kthread.h>
#include <linux/sunrpc/types.h>
#include <linux/sunrpc/xdr.h>
@@ -291,15 +292,14 @@ svc_pool_map_put(void)
/*
- * Set the current thread's cpus_allowed mask so that it
+ * Set the given thread's cpus_allowed mask so that it
* will only run on cpus in the given pool.
- *
- * Returns 1 and fills in oldmask iff a cpumask was applied.
*/
-static inline int
-svc_pool_map_set_cpumask(unsigned int pidx, cpumask_t *oldmask)
+static inline void
+svc_pool_map_set_cpumask(struct task_struct *task, unsigned int pidx)
{
struct svc_pool_map *m = &svc_pool_map;
+ unsigned int node = m->pool_to[pidx];
/*
* The caller checks for sv_nrpools > 1, which
@@ -307,26 +307,17 @@ svc_pool_map_set_cpumask(unsigned int pidx, cpumask_t *oldmask)
*/
BUG_ON(m->count == 0);
- switch (m->mode)
- {
- default:
- return 0;
+ switch (m->mode) {
case SVC_POOL_PERCPU:
{
- unsigned int cpu = m->pool_to[pidx];
-
- *oldmask = current->cpus_allowed;
- set_cpus_allowed_ptr(current, &cpumask_of_cpu(cpu));
- return 1;
+ set_cpus_allowed_ptr(task, &cpumask_of_cpu(node));
+ break;
}
case SVC_POOL_PERNODE:
{
- unsigned int node = m->pool_to[pidx];
node_to_cpumask_ptr(nodecpumask, node);
-
- *oldmask = current->cpus_allowed;
- set_cpus_allowed_ptr(current, nodecpumask);
- return 1;
+ set_cpus_allowed_ptr(task, nodecpumask);
+ break;
}
}
}
@@ -443,7 +434,7 @@ EXPORT_SYMBOL(svc_create);
struct svc_serv *
svc_create_pooled(struct svc_program *prog, unsigned int bufsize,
void (*shutdown)(struct svc_serv *serv),
- svc_thread_fn func, int sig, struct module *mod)
+ svc_thread_fn func, struct module *mod)
{
struct svc_serv *serv;
unsigned int npools = svc_pool_map_get();
@@ -452,7 +443,6 @@ svc_create_pooled(struct svc_program *prog, unsigned int bufsize,
if (serv != NULL) {
serv->sv_function = func;
- serv->sv_kill_signal = sig;
serv->sv_module = mod;
}
@@ -461,7 +451,8 @@ svc_create_pooled(struct svc_program *prog, unsigned int bufsize,
EXPORT_SYMBOL(svc_create_pooled);
/*
- * Destroy an RPC service. Should be called with the BKL held
+ * Destroy an RPC service. Should be called with appropriate locking to
+ * protect the sv_nrthreads, sv_permsocks and sv_tempsocks.
*/
void
svc_destroy(struct svc_serv *serv)
@@ -578,46 +569,6 @@ out_enomem:
EXPORT_SYMBOL(svc_prepare_thread);
/*
- * Create a thread in the given pool. Caller must hold BKL.
- * On a NUMA or SMP machine, with a multi-pool serv, the thread
- * will be restricted to run on the cpus belonging to the pool.
- */
-static int
-__svc_create_thread(svc_thread_fn func, struct svc_serv *serv,
- struct svc_pool *pool)
-{
- struct svc_rqst *rqstp;
- int error = -ENOMEM;
- int have_oldmask = 0;
- cpumask_t uninitialized_var(oldmask);
-
- rqstp = svc_prepare_thread(serv, pool);
- if (IS_ERR(rqstp)) {
- error = PTR_ERR(rqstp);
- goto out;
- }
-
- if (serv->sv_nrpools > 1)
- have_oldmask = svc_pool_map_set_cpumask(pool->sp_id, &oldmask);
-
- error = kernel_thread((int (*)(void *)) func, rqstp, 0);
-
- if (have_oldmask)
- set_cpus_allowed(current, oldmask);
-
- if (error < 0)
- goto out_thread;
- svc_sock_update_bufs(serv);
- error = 0;
-out:
- return error;
-
-out_thread:
- svc_exit_thread(rqstp);
- goto out;
-}
-
-/*
* Choose a pool in which to create a new thread, for svc_set_num_threads
*/
static inline struct svc_pool *
@@ -674,7 +625,7 @@ found_pool:
* of threads the given number. If `pool' is non-NULL, applies
* only to threads in that pool, otherwise round-robins between
* all pools. Must be called with a svc_get() reference and
- * the BKL held.
+ * the BKL or another lock to protect access to svc_serv fields.
*
* Destroying threads relies on the service threads filling in
* rqstp->rq_task, which only the nfs ones do. Assumes the serv
@@ -686,7 +637,9 @@ found_pool:
int
svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
{
- struct task_struct *victim;
+ struct svc_rqst *rqstp;
+ struct task_struct *task;
+ struct svc_pool *chosen_pool;
int error = 0;
unsigned int state = serv->sv_nrthreads-1;
@@ -702,18 +655,34 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
/* create new threads */
while (nrservs > 0) {
nrservs--;
+ chosen_pool = choose_pool(serv, pool, &state);
+
+ rqstp = svc_prepare_thread(serv, chosen_pool);
+ if (IS_ERR(rqstp)) {
+ error = PTR_ERR(rqstp);
+ break;
+ }
+
__module_get(serv->sv_module);
- error = __svc_create_thread(serv->sv_function, serv,
- choose_pool(serv, pool, &state));
- if (error < 0) {
+ task = kthread_create(serv->sv_function, rqstp, serv->sv_name);
+ if (IS_ERR(task)) {
+ error = PTR_ERR(task);
module_put(serv->sv_module);
+ svc_exit_thread(rqstp);
break;
}
+
+ rqstp->rq_task = task;
+ if (serv->sv_nrpools > 1)
+ svc_pool_map_set_cpumask(task, chosen_pool->sp_id);
+
+ svc_sock_update_bufs(serv);
+ wake_up_process(task);
}
/* destroy old threads */
while (nrservs < 0 &&
- (victim = choose_victim(serv, pool, &state)) != NULL) {
- send_sig(serv->sv_kill_signal, victim, 1);
+ (task = choose_victim(serv, pool, &state)) != NULL) {
+ send_sig(SIGINT, task, 1);
nrservs++;
}
@@ -722,7 +691,8 @@ svc_set_num_threads(struct svc_serv *serv, struct svc_pool *pool, int nrservs)
EXPORT_SYMBOL(svc_set_num_threads);
/*
- * Called from a server thread as it's exiting. Caller must hold BKL.
+ * Called from a server thread as it's exiting. Caller must hold the BKL or
+ * the "service mutex", whichever is appropriate for the service.
*/
void
svc_exit_thread(struct svc_rqst *rqstp)
diff --git a/net/sunrpc/xprtrdma/svc_rdma.c b/net/sunrpc/xprtrdma/svc_rdma.c
index 88c0ca20bb1e..8a7d34b84e39 100644
--- a/net/sunrpc/xprtrdma/svc_rdma.c
+++ b/net/sunrpc/xprtrdma/svc_rdma.c
@@ -69,6 +69,10 @@ atomic_t rdma_stat_rq_prod;
atomic_t rdma_stat_sq_poll;
atomic_t rdma_stat_sq_prod;
+/* Temporary NFS request map and context caches */
+struct kmem_cache *svc_rdma_map_cachep = NULL;
+struct kmem_cache *svc_rdma_ctxt_cachep = NULL;
+
/*
* This function implements reading and resetting an atomic_t stat
* variable through read/write to a proc file. Any write to the file
@@ -236,11 +240,16 @@ static ctl_table svcrdma_root_table[] = {
void svc_rdma_cleanup(void)
{
dprintk("SVCRDMA Module Removed, deregister RPC RDMA transport\n");
+ flush_scheduled_work();
if (svcrdma_table_header) {
unregister_sysctl_table(svcrdma_table_header);
svcrdma_table_header = NULL;
}
svc_unreg_xprt_class(&svc_rdma_class);
+ if (svc_rdma_map_cachep)
+ kmem_cache_destroy(svc_rdma_map_cachep);
+ if (svc_rdma_ctxt_cachep)
+ kmem_cache_destroy(svc_rdma_ctxt_cachep);
}
int svc_rdma_init(void)
@@ -255,9 +264,40 @@ int svc_rdma_init(void)
svcrdma_table_header =
register_sysctl_table(svcrdma_root_table);
+ /* Create the temporary map cache */
+ svc_rdma_map_cachep = kmem_cache_create("svc_rdma_map_cache",
+ sizeof(struct svc_rdma_req_map),
+ 0,
+ SLAB_HWCACHE_ALIGN,
+ NULL);
+ if (!svc_rdma_map_cachep) {
+ printk(KERN_INFO "Could not allocate map cache.\n");
+ goto err0;
+ }
+
+ /* Create the temporary context cache */
+ svc_rdma_ctxt_cachep =
+ kmem_cache_create("svc_rdma_ctxt_cache",
+ sizeof(struct svc_rdma_op_ctxt),
+ 0,
+ SLAB_HWCACHE_ALIGN,
+ NULL);
+ if (!svc_rdma_ctxt_cachep) {
+ printk(KERN_INFO "Could not allocate WR ctxt cache.\n");
+ goto err1;
+ }
+
/* Register RDMA with the SVC transport switch */
svc_reg_xprt_class(&svc_rdma_class);
return 0;
+ err1:
+ kmem_cache_destroy(svc_rdma_map_cachep);
+ err0:
+ if (svcrdma_table_header) {
+ unregister_sysctl_table(svcrdma_table_header);
+ svcrdma_table_header = NULL;
+ }
+ return -ENOMEM;
}
MODULE_AUTHOR("Tom Tucker <tom@opengridcomputing.com>");
MODULE_DESCRIPTION("SVC RDMA Transport");
diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
index 06ab4841537b..b4b17f44cb29 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c
@@ -112,11 +112,6 @@ static void rdma_build_arg_xdr(struct svc_rqst *rqstp,
rqstp->rq_arg.tail[0].iov_len = 0;
}
-struct chunk_sge {
- int start; /* sge no for this chunk */
- int count; /* sge count for this chunk */
-};
-
/* Encode a read-chunk-list as an array of IB SGE
*
* Assumptions:
@@ -134,8 +129,8 @@ static int rdma_rcl_to_sge(struct svcxprt_rdma *xprt,
struct svc_rqst *rqstp,
struct svc_rdma_op_ctxt *head,
struct rpcrdma_msg *rmsgp,
- struct ib_sge *sge,
- struct chunk_sge *ch_sge_ary,
+ struct svc_rdma_req_map *rpl_map,
+ struct svc_rdma_req_map *chl_map,
int ch_count,
int byte_count)
{
@@ -156,22 +151,18 @@ static int rdma_rcl_to_sge(struct svcxprt_rdma *xprt,
head->arg.head[0] = rqstp->rq_arg.head[0];
head->arg.tail[0] = rqstp->rq_arg.tail[0];
head->arg.pages = &head->pages[head->count];
- head->sge[0].length = head->count; /* save count of hdr pages */
+ head->hdr_count = head->count; /* save count of hdr pages */
head->arg.page_base = 0;
head->arg.page_len = ch_bytes;
head->arg.len = rqstp->rq_arg.len + ch_bytes;
head->arg.buflen = rqstp->rq_arg.buflen + ch_bytes;
head->count++;
- ch_sge_ary[0].start = 0;
+ chl_map->ch[0].start = 0;
while (byte_count) {
+ rpl_map->sge[sge_no].iov_base =
+ page_address(rqstp->rq_arg.pages[page_no]) + page_off;
sge_bytes = min_t(int, PAGE_SIZE-page_off, ch_bytes);
- sge[sge_no].addr =
- ib_dma_map_page(xprt->sc_cm_id->device,
- rqstp->rq_arg.pages[page_no],
- page_off, sge_bytes,
- DMA_FROM_DEVICE);
- sge[sge_no].length = sge_bytes;
- sge[sge_no].lkey = xprt->sc_phys_mr->lkey;
+ rpl_map->sge[sge_no].iov_len = sge_bytes;
/*
* Don't bump head->count here because the same page
* may be used by multiple SGE.
@@ -187,11 +178,11 @@ static int rdma_rcl_to_sge(struct svcxprt_rdma *xprt,
* SGE, move to the next SGE
*/
if (ch_bytes == 0) {
- ch_sge_ary[ch_no].count =
- sge_no - ch_sge_ary[ch_no].start;
+ chl_map->ch[ch_no].count =
+ sge_no - chl_map->ch[ch_no].start;
ch_no++;
ch++;
- ch_sge_ary[ch_no].start = sge_no;
+ chl_map->ch[ch_no].start = sge_no;
ch_bytes = ch->rc_target.rs_length;
/* If bytes remaining account for next chunk */
if (byte_count) {
@@ -220,18 +211,25 @@ static int rdma_rcl_to_sge(struct svcxprt_rdma *xprt,
return sge_no;
}
-static void rdma_set_ctxt_sge(struct svc_rdma_op_ctxt *ctxt,
- struct ib_sge *sge,
+static void rdma_set_ctxt_sge(struct svcxprt_rdma *xprt,
+ struct svc_rdma_op_ctxt *ctxt,
+ struct kvec *vec,
u64 *sgl_offset,
int count)
{
int i;
ctxt->count = count;
+ ctxt->direction = DMA_FROM_DEVICE;
for (i = 0; i < count; i++) {
- ctxt->sge[i].addr = sge[i].addr;
- ctxt->sge[i].length = sge[i].length;
- *sgl_offset = *sgl_offset + sge[i].length;
+ atomic_inc(&xprt->sc_dma_used);
+ ctxt->sge[i].addr =
+ ib_dma_map_single(xprt->sc_cm_id->device,
+ vec[i].iov_base, vec[i].iov_len,
+ DMA_FROM_DEVICE);
+ ctxt->sge[i].length = vec[i].iov_len;
+ ctxt->sge[i].lkey = xprt->sc_phys_mr->lkey;
+ *sgl_offset = *sgl_offset + vec[i].iov_len;
}
}
@@ -282,34 +280,29 @@ static int rdma_read_xdr(struct svcxprt_rdma *xprt,
struct ib_send_wr read_wr;
int err = 0;
int ch_no;
- struct ib_sge *sge;
int ch_count;
int byte_count;
int sge_count;
u64 sgl_offset;
struct rpcrdma_read_chunk *ch;
struct svc_rdma_op_ctxt *ctxt = NULL;
- struct svc_rdma_op_ctxt *tmp_sge_ctxt;
- struct svc_rdma_op_ctxt *tmp_ch_ctxt;
- struct chunk_sge *ch_sge_ary;
+ struct svc_rdma_req_map *rpl_map;
+ struct svc_rdma_req_map *chl_map;
/* If no read list is present, return 0 */
ch = svc_rdma_get_read_chunk(rmsgp);
if (!ch)
return 0;
- /* Allocate temporary contexts to keep SGE */
- BUG_ON(sizeof(struct ib_sge) < sizeof(struct chunk_sge));
- tmp_sge_ctxt = svc_rdma_get_context(xprt);
- sge = tmp_sge_ctxt->sge;
- tmp_ch_ctxt = svc_rdma_get_context(xprt);
- ch_sge_ary = (struct chunk_sge *)tmp_ch_ctxt->sge;
+ /* Allocate temporary reply and chunk maps */
+ rpl_map = svc_rdma_get_req_map();
+ chl_map = svc_rdma_get_req_map();
svc_rdma_rcl_chunk_counts(ch, &ch_count, &byte_count);
if (ch_count > RPCSVC_MAXPAGES)
return -EINVAL;
sge_count = rdma_rcl_to_sge(xprt, rqstp, hdr_ctxt, rmsgp,
- sge, ch_sge_ary,
+ rpl_map, chl_map,
ch_count, byte_count);
sgl_offset = 0;
ch_no = 0;
@@ -331,14 +324,15 @@ next_sge:
read_wr.wr.rdma.remote_addr =
get_unaligned(&(ch->rc_target.rs_offset)) +
sgl_offset;
- read_wr.sg_list = &sge[ch_sge_ary[ch_no].start];
+ read_wr.sg_list = ctxt->sge;
read_wr.num_sge =
- rdma_read_max_sge(xprt, ch_sge_ary[ch_no].count);
- rdma_set_ctxt_sge(ctxt, &sge[ch_sge_ary[ch_no].start],
+ rdma_read_max_sge(xprt, chl_map->ch[ch_no].count);
+ rdma_set_ctxt_sge(xprt, ctxt,
+ &rpl_map->sge[chl_map->ch[ch_no].start],
&sgl_offset,
read_wr.num_sge);
if (((ch+1)->rc_discrim == 0) &&
- (read_wr.num_sge == ch_sge_ary[ch_no].count)) {
+ (read_wr.num_sge == chl_map->ch[ch_no].count)) {
/*
* Mark the last RDMA_READ with a bit to
* indicate all RPC data has been fetched from
@@ -358,9 +352,9 @@ next_sge:
}
atomic_inc(&rdma_stat_read);
- if (read_wr.num_sge < ch_sge_ary[ch_no].count) {
- ch_sge_ary[ch_no].count -= read_wr.num_sge;
- ch_sge_ary[ch_no].start += read_wr.num_sge;
+ if (read_wr.num_sge < chl_map->ch[ch_no].count) {
+ chl_map->ch[ch_no].count -= read_wr.num_sge;
+ chl_map->ch[ch_no].start += read_wr.num_sge;
goto next_sge;
}
sgl_offset = 0;
@@ -368,8 +362,8 @@ next_sge:
}
out:
- svc_rdma_put_context(tmp_sge_ctxt, 0);
- svc_rdma_put_context(tmp_ch_ctxt, 0);
+ svc_rdma_put_req_map(rpl_map);
+ svc_rdma_put_req_map(chl_map);
/* Detach arg pages. svc_recv will replenish them */
for (ch_no = 0; &rqstp->rq_pages[ch_no] < rqstp->rq_respages; ch_no++)
@@ -399,7 +393,7 @@ static int rdma_read_complete(struct svc_rqst *rqstp,
rqstp->rq_pages[page_no] = head->pages[page_no];
}
/* Point rq_arg.pages past header */
- rqstp->rq_arg.pages = &rqstp->rq_pages[head->sge[0].length];
+ rqstp->rq_arg.pages = &rqstp->rq_pages[head->hdr_count];
rqstp->rq_arg.page_len = head->arg.page_len;
rqstp->rq_arg.page_base = head->arg.page_base;
diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c
index fb82b1b683f8..015ac988a149 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c
@@ -64,10 +64,9 @@
* SGE[sge_count-1] data from xdr->tail.
*
*/
-static struct ib_sge *xdr_to_sge(struct svcxprt_rdma *xprt,
- struct xdr_buf *xdr,
- struct ib_sge *sge,
- int *sge_count)
+static void xdr_to_sge(struct svcxprt_rdma *xprt,
+ struct xdr_buf *xdr,
+ struct svc_rdma_req_map *vec)
{
/* Max we need is the length of the XDR / pagesize + one for
* head + one for tail + one for RPCRDMA header
@@ -84,14 +83,10 @@ static struct ib_sge *xdr_to_sge(struct svcxprt_rdma *xprt,
sge_no = 1;
/* Head SGE */
- sge[sge_no].addr = ib_dma_map_single(xprt->sc_cm_id->device,
- xdr->head[0].iov_base,
- xdr->head[0].iov_len,
- DMA_TO_DEVICE);
+ vec->sge[sge_no].iov_base = xdr->head[0].iov_base;
sge_bytes = min_t(u32, byte_count, xdr->head[0].iov_len);
byte_count -= sge_bytes;
- sge[sge_no].length = sge_bytes;
- sge[sge_no].lkey = xprt->sc_phys_mr->lkey;
+ vec->sge[sge_no].iov_len = sge_bytes;
sge_no++;
/* pages SGE */
@@ -99,16 +94,13 @@ static struct ib_sge *xdr_to_sge(struct svcxprt_rdma *xprt,
page_bytes = xdr->page_len;
page_off = xdr->page_base;
while (byte_count && page_bytes) {
+ vec->sge[sge_no].iov_base =
+ page_address(xdr->pages[page_no]) + page_off;
sge_bytes = min_t(u32, byte_count, (PAGE_SIZE-page_off));
- sge[sge_no].addr =
- ib_dma_map_page(xprt->sc_cm_id->device,
- xdr->pages[page_no], page_off,
- sge_bytes, DMA_TO_DEVICE);
sge_bytes = min(sge_bytes, page_bytes);
byte_count -= sge_bytes;
page_bytes -= sge_bytes;
- sge[sge_no].length = sge_bytes;
- sge[sge_no].lkey = xprt->sc_phys_mr->lkey;
+ vec->sge[sge_no].iov_len = sge_bytes;
sge_no++;
page_no++;
@@ -117,23 +109,17 @@ static struct ib_sge *xdr_to_sge(struct svcxprt_rdma *xprt,
/* Tail SGE */
if (byte_count && xdr->tail[0].iov_len) {
- sge[sge_no].addr =
- ib_dma_map_single(xprt->sc_cm_id->device,
- xdr->tail[0].iov_base,
- xdr->tail[0].iov_len,
- DMA_TO_DEVICE);
+ vec->sge[sge_no].iov_base = xdr->tail[0].iov_base;
sge_bytes = min_t(u32, byte_count, xdr->tail[0].iov_len);
byte_count -= sge_bytes;
- sge[sge_no].length = sge_bytes;
- sge[sge_no].lkey = xprt->sc_phys_mr->lkey;
+ vec->sge[sge_no].iov_len = sge_bytes;
sge_no++;
}
BUG_ON(sge_no > sge_max);
BUG_ON(byte_count != 0);
- *sge_count = sge_no;
- return sge;
+ vec->count = sge_no;
}
@@ -143,9 +129,8 @@ static struct ib_sge *xdr_to_sge(struct svcxprt_rdma *xprt,
static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp,
u32 rmr, u64 to,
u32 xdr_off, int write_len,
- struct ib_sge *xdr_sge, int sge_count)
+ struct svc_rdma_req_map *vec)
{
- struct svc_rdma_op_ctxt *tmp_sge_ctxt;
struct ib_send_wr write_wr;
struct ib_sge *sge;
int xdr_sge_no;
@@ -156,23 +141,22 @@ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp,
struct svc_rdma_op_ctxt *ctxt;
int ret = 0;
- BUG_ON(sge_count > RPCSVC_MAXPAGES);
+ BUG_ON(vec->count > RPCSVC_MAXPAGES);
dprintk("svcrdma: RDMA_WRITE rmr=%x, to=%llx, xdr_off=%d, "
- "write_len=%d, xdr_sge=%p, sge_count=%d\n",
+ "write_len=%d, vec->sge=%p, vec->count=%lu\n",
rmr, (unsigned long long)to, xdr_off,
- write_len, xdr_sge, sge_count);
+ write_len, vec->sge, vec->count);
ctxt = svc_rdma_get_context(xprt);
- ctxt->count = 0;
- tmp_sge_ctxt = svc_rdma_get_context(xprt);
- sge = tmp_sge_ctxt->sge;
+ ctxt->direction = DMA_TO_DEVICE;
+ sge = ctxt->sge;
/* Find the SGE associated with xdr_off */
- for (bc = xdr_off, xdr_sge_no = 1; bc && xdr_sge_no < sge_count;
+ for (bc = xdr_off, xdr_sge_no = 1; bc && xdr_sge_no < vec->count;
xdr_sge_no++) {
- if (xdr_sge[xdr_sge_no].length > bc)
+ if (vec->sge[xdr_sge_no].iov_len > bc)
break;
- bc -= xdr_sge[xdr_sge_no].length;
+ bc -= vec->sge[xdr_sge_no].iov_len;
}
sge_off = bc;
@@ -180,21 +164,28 @@ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp,
sge_no = 0;
/* Copy the remaining SGE */
- while (bc != 0 && xdr_sge_no < sge_count) {
- sge[sge_no].addr = xdr_sge[xdr_sge_no].addr + sge_off;
- sge[sge_no].lkey = xdr_sge[xdr_sge_no].lkey;
+ while (bc != 0 && xdr_sge_no < vec->count) {
+ sge[sge_no].lkey = xprt->sc_phys_mr->lkey;
sge_bytes = min((size_t)bc,
- (size_t)(xdr_sge[xdr_sge_no].length-sge_off));
+ (size_t)(vec->sge[xdr_sge_no].iov_len-sge_off));
sge[sge_no].length = sge_bytes;
-
+ atomic_inc(&xprt->sc_dma_used);
+ sge[sge_no].addr =
+ ib_dma_map_single(xprt->sc_cm_id->device,
+ (void *)
+ vec->sge[xdr_sge_no].iov_base + sge_off,
+ sge_bytes, DMA_TO_DEVICE);
+ if (dma_mapping_error(sge[sge_no].addr))
+ return -EINVAL;
sge_off = 0;
sge_no++;
+ ctxt->count++;
xdr_sge_no++;
bc -= sge_bytes;
}
BUG_ON(bc != 0);
- BUG_ON(xdr_sge_no > sge_count);
+ BUG_ON(xdr_sge_no > vec->count);
/* Prepare WRITE WR */
memset(&write_wr, 0, sizeof write_wr);
@@ -210,11 +201,10 @@ static int send_write(struct svcxprt_rdma *xprt, struct svc_rqst *rqstp,
/* Post It */
atomic_inc(&rdma_stat_write);
if (svc_rdma_send(xprt, &write_wr)) {
- svc_rdma_put_context(ctxt, 1);
+ svc_rdma_put_context(ctxt, 0);
/* Fatal error, close transport */
ret = -EIO;
}
- svc_rdma_put_context(tmp_sge_ctxt, 0);
return ret;
}
@@ -222,8 +212,7 @@ static int send_write_chunks(struct svcxprt_rdma *xprt,
struct rpcrdma_msg *rdma_argp,
struct rpcrdma_msg *rdma_resp,
struct svc_rqst *rqstp,
- struct ib_sge *sge,
- int sge_count)
+ struct svc_rdma_req_map *vec)
{
u32 xfer_len = rqstp->rq_res.page_len + rqstp->rq_res.tail[0].iov_len;
int write_len;
@@ -269,8 +258,7 @@ static int send_write_chunks(struct svcxprt_rdma *xprt,
rs_offset + chunk_off,
xdr_off,
this_write,
- sge,
- sge_count);
+ vec);
if (ret) {
dprintk("svcrdma: RDMA_WRITE failed, ret=%d\n",
ret);
@@ -292,8 +280,7 @@ static int send_reply_chunks(struct svcxprt_rdma *xprt,
struct rpcrdma_msg *rdma_argp,
struct rpcrdma_msg *rdma_resp,
struct svc_rqst *rqstp,
- struct ib_sge *sge,
- int sge_count)
+ struct svc_rdma_req_map *vec)
{
u32 xfer_len = rqstp->rq_res.len;
int write_len;
@@ -341,8 +328,7 @@ static int send_reply_chunks(struct svcxprt_rdma *xprt,
rs_offset + chunk_off,
xdr_off,
this_write,
- sge,
- sge_count);
+ vec);
if (ret) {
dprintk("svcrdma: RDMA_WRITE failed, ret=%d\n",
ret);
@@ -380,7 +366,7 @@ static int send_reply(struct svcxprt_rdma *rdma,
struct page *page,
struct rpcrdma_msg *rdma_resp,
struct svc_rdma_op_ctxt *ctxt,
- int sge_count,
+ struct svc_rdma_req_map *vec,
int byte_count)
{
struct ib_send_wr send_wr;
@@ -405,6 +391,7 @@ static int send_reply(struct svcxprt_rdma *rdma,
ctxt->count = 1;
/* Prepare the SGE for the RPCRDMA Header */
+ atomic_inc(&rdma->sc_dma_used);
ctxt->sge[0].addr =
ib_dma_map_page(rdma->sc_cm_id->device,
page, 0, PAGE_SIZE, DMA_TO_DEVICE);
@@ -413,10 +400,16 @@ static int send_reply(struct svcxprt_rdma *rdma,
ctxt->sge[0].lkey = rdma->sc_phys_mr->lkey;
/* Determine how many of our SGE are to be transmitted */
- for (sge_no = 1; byte_count && sge_no < sge_count; sge_no++) {
- sge_bytes = min((size_t)ctxt->sge[sge_no].length,
- (size_t)byte_count);
+ for (sge_no = 1; byte_count && sge_no < vec->count; sge_no++) {
+ sge_bytes = min_t(size_t, vec->sge[sge_no].iov_len, byte_count);
byte_count -= sge_bytes;
+ atomic_inc(&rdma->sc_dma_used);
+ ctxt->sge[sge_no].addr =
+ ib_dma_map_single(rdma->sc_cm_id->device,
+ vec->sge[sge_no].iov_base,
+ sge_bytes, DMA_TO_DEVICE);
+ ctxt->sge[sge_no].length = sge_bytes;
+ ctxt->sge[sge_no].lkey = rdma->sc_phys_mr->lkey;
}
BUG_ON(byte_count != 0);
@@ -428,8 +421,10 @@ static int send_reply(struct svcxprt_rdma *rdma,
ctxt->pages[page_no+1] = rqstp->rq_respages[page_no];
ctxt->count++;
rqstp->rq_respages[page_no] = NULL;
+ /* If there are more pages than SGE, terminate SGE list */
+ if (page_no+1 >= sge_no)
+ ctxt->sge[page_no+1].length = 0;
}
-
BUG_ON(sge_no > rdma->sc_max_sge);
memset(&send_wr, 0, sizeof send_wr);
ctxt->wr_op = IB_WR_SEND;
@@ -473,20 +468,20 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
enum rpcrdma_proc reply_type;
int ret;
int inline_bytes;
- struct ib_sge *sge;
- int sge_count = 0;
struct page *res_page;
struct svc_rdma_op_ctxt *ctxt;
+ struct svc_rdma_req_map *vec;
dprintk("svcrdma: sending response for rqstp=%p\n", rqstp);
/* Get the RDMA request header. */
rdma_argp = xdr_start(&rqstp->rq_arg);
- /* Build an SGE for the XDR */
+ /* Build an req vec for the XDR */
ctxt = svc_rdma_get_context(rdma);
ctxt->direction = DMA_TO_DEVICE;
- sge = xdr_to_sge(rdma, &rqstp->rq_res, ctxt->sge, &sge_count);
+ vec = svc_rdma_get_req_map();
+ xdr_to_sge(rdma, &rqstp->rq_res, vec);
inline_bytes = rqstp->rq_res.len;
@@ -503,7 +498,7 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
/* Send any write-chunk data and build resp write-list */
ret = send_write_chunks(rdma, rdma_argp, rdma_resp,
- rqstp, sge, sge_count);
+ rqstp, vec);
if (ret < 0) {
printk(KERN_ERR "svcrdma: failed to send write chunks, rc=%d\n",
ret);
@@ -513,7 +508,7 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
/* Send any reply-list data and update resp reply-list */
ret = send_reply_chunks(rdma, rdma_argp, rdma_resp,
- rqstp, sge, sge_count);
+ rqstp, vec);
if (ret < 0) {
printk(KERN_ERR "svcrdma: failed to send reply chunks, rc=%d\n",
ret);
@@ -521,11 +516,13 @@ int svc_rdma_sendto(struct svc_rqst *rqstp)
}
inline_bytes -= ret;
- ret = send_reply(rdma, rqstp, res_page, rdma_resp, ctxt, sge_count,
+ ret = send_reply(rdma, rqstp, res_page, rdma_resp, ctxt, vec,
inline_bytes);
+ svc_rdma_put_req_map(vec);
dprintk("svcrdma: send_reply returns %d\n", ret);
return ret;
error:
+ svc_rdma_put_req_map(vec);
svc_rdma_put_context(ctxt, 0);
put_page(res_page);
return ret;
diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c
index e132509d1db0..19ddc382b777 100644
--- a/net/sunrpc/xprtrdma/svc_rdma_transport.c
+++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c
@@ -84,70 +84,37 @@ struct svc_xprt_class svc_rdma_class = {
.xcl_max_payload = RPCSVC_MAXPAYLOAD_TCP,
};
-static int rdma_bump_context_cache(struct svcxprt_rdma *xprt)
+/* WR context cache. Created in svc_rdma.c */
+extern struct kmem_cache *svc_rdma_ctxt_cachep;
+
+struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *xprt)
{
- int target;
- int at_least_one = 0;
struct svc_rdma_op_ctxt *ctxt;
- target = min(xprt->sc_ctxt_cnt + xprt->sc_ctxt_bump,
- xprt->sc_ctxt_max);
-
- spin_lock_bh(&xprt->sc_ctxt_lock);
- while (xprt->sc_ctxt_cnt < target) {
- xprt->sc_ctxt_cnt++;
- spin_unlock_bh(&xprt->sc_ctxt_lock);
-
- ctxt = kmalloc(sizeof(*ctxt), GFP_KERNEL);
-
- spin_lock_bh(&xprt->sc_ctxt_lock);
- if (ctxt) {
- at_least_one = 1;
- INIT_LIST_HEAD(&ctxt->free_list);
- list_add(&ctxt->free_list, &xprt->sc_ctxt_free);
- } else {
- /* kmalloc failed...give up for now */
- xprt->sc_ctxt_cnt--;
+ while (1) {
+ ctxt = kmem_cache_alloc(svc_rdma_ctxt_cachep, GFP_KERNEL);
+ if (ctxt)
break;
- }
+ schedule_timeout_uninterruptible(msecs_to_jiffies(500));
}
- spin_unlock_bh(&xprt->sc_ctxt_lock);
- dprintk("svcrdma: sc_ctxt_max=%d, sc_ctxt_cnt=%d\n",
- xprt->sc_ctxt_max, xprt->sc_ctxt_cnt);
- return at_least_one;
+ ctxt->xprt = xprt;
+ INIT_LIST_HEAD(&ctxt->dto_q);
+ ctxt->count = 0;
+ atomic_inc(&xprt->sc_ctxt_used);
+ return ctxt;
}
-struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *xprt)
+static void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt)
{
- struct svc_rdma_op_ctxt *ctxt;
-
- while (1) {
- spin_lock_bh(&xprt->sc_ctxt_lock);
- if (unlikely(list_empty(&xprt->sc_ctxt_free))) {
- /* Try to bump my cache. */
- spin_unlock_bh(&xprt->sc_ctxt_lock);
-
- if (rdma_bump_context_cache(xprt))
- continue;
-
- printk(KERN_INFO "svcrdma: sleeping waiting for "
- "context memory on xprt=%p\n",
- xprt);
- schedule_timeout_uninterruptible(msecs_to_jiffies(500));
- continue;
- }
- ctxt = list_entry(xprt->sc_ctxt_free.next,
- struct svc_rdma_op_ctxt,
- free_list);
- list_del_init(&ctxt->free_list);
- spin_unlock_bh(&xprt->sc_ctxt_lock);
- ctxt->xprt = xprt;
- INIT_LIST_HEAD(&ctxt->dto_q);
- ctxt->count = 0;
- atomic_inc(&xprt->sc_ctxt_used);
- break;
+ struct svcxprt_rdma *xprt = ctxt->xprt;
+ int i;
+ for (i = 0; i < ctxt->count && ctxt->sge[i].length; i++) {
+ atomic_dec(&xprt->sc_dma_used);
+ ib_dma_unmap_single(xprt->sc_cm_id->device,
+ ctxt->sge[i].addr,
+ ctxt->sge[i].length,
+ ctxt->direction);
}
- return ctxt;
}
void svc_rdma_put_context(struct svc_rdma_op_ctxt *ctxt, int free_pages)
@@ -161,18 +128,36 @@ void svc_rdma_put_context(struct svc_rdma_op_ctxt *ctxt, int free_pages)
for (i = 0; i < ctxt->count; i++)
put_page(ctxt->pages[i]);
- for (i = 0; i < ctxt->count; i++)
- ib_dma_unmap_single(xprt->sc_cm_id->device,
- ctxt->sge[i].addr,
- ctxt->sge[i].length,
- ctxt->direction);
-
- spin_lock_bh(&xprt->sc_ctxt_lock);
- list_add(&ctxt->free_list, &xprt->sc_ctxt_free);
- spin_unlock_bh(&xprt->sc_ctxt_lock);
+ kmem_cache_free(svc_rdma_ctxt_cachep, ctxt);
atomic_dec(&xprt->sc_ctxt_used);
}
+/* Temporary NFS request map cache. Created in svc_rdma.c */
+extern struct kmem_cache *svc_rdma_map_cachep;
+
+/*
+ * Temporary NFS req mappings are shared across all transport
+ * instances. These are short lived and should be bounded by the number
+ * of concurrent server threads * depth of the SQ.
+ */
+struct svc_rdma_req_map *svc_rdma_get_req_map(void)
+{
+ struct svc_rdma_req_map *map;
+ while (1) {
+ map = kmem_cache_alloc(svc_rdma_map_cachep, GFP_KERNEL);
+ if (map)
+ break;
+ schedule_timeout_uninterruptible(msecs_to_jiffies(500));
+ }
+ map->count = 0;
+ return map;
+}
+
+void svc_rdma_put_req_map(struct svc_rdma_req_map *map)
+{
+ kmem_cache_free(svc_rdma_map_cachep, map);
+}
+
/* ib_cq event handler */
static void cq_event_handler(struct ib_event *event, void *context)
{
@@ -302,6 +287,7 @@ static void rq_cq_reap(struct svcxprt_rdma *xprt)
ctxt = (struct svc_rdma_op_ctxt *)(unsigned long)wc.wr_id;
ctxt->wc_status = wc.status;
ctxt->byte_len = wc.byte_len;
+ svc_rdma_unmap_dma(ctxt);
if (wc.status != IB_WC_SUCCESS) {
/* Close the transport */
dprintk("svcrdma: transport closing putting ctxt %p\n", ctxt);
@@ -351,6 +337,7 @@ static void sq_cq_reap(struct svcxprt_rdma *xprt)
ctxt = (struct svc_rdma_op_ctxt *)(unsigned long)wc.wr_id;
xprt = ctxt->xprt;
+ svc_rdma_unmap_dma(ctxt);
if (wc.status != IB_WC_SUCCESS)
/* Close the transport */
set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags);
@@ -361,10 +348,13 @@ static void sq_cq_reap(struct svcxprt_rdma *xprt)
switch (ctxt->wr_op) {
case IB_WR_SEND:
- case IB_WR_RDMA_WRITE:
svc_rdma_put_context(ctxt, 1);
break;
+ case IB_WR_RDMA_WRITE:
+ svc_rdma_put_context(ctxt, 0);
+ break;
+
case IB_WR_RDMA_READ:
if (test_bit(RDMACTXT_F_LAST_CTXT, &ctxt->flags)) {
struct svc_rdma_op_ctxt *read_hdr = ctxt->read_hdr;
@@ -423,40 +413,6 @@ static void sq_comp_handler(struct ib_cq *cq, void *cq_context)
tasklet_schedule(&dto_tasklet);
}
-static void create_context_cache(struct svcxprt_rdma *xprt,
- int ctxt_count, int ctxt_bump, int ctxt_max)
-{
- struct svc_rdma_op_ctxt *ctxt;
- int i;
-
- xprt->sc_ctxt_max = ctxt_max;
- xprt->sc_ctxt_bump = ctxt_bump;
- xprt->sc_ctxt_cnt = 0;
- atomic_set(&xprt->sc_ctxt_used, 0);
-
- INIT_LIST_HEAD(&xprt->sc_ctxt_free);
- for (i = 0; i < ctxt_count; i++) {
- ctxt = kmalloc(sizeof(*ctxt), GFP_KERNEL);
- if (ctxt) {
- INIT_LIST_HEAD(&ctxt->free_list);
- list_add(&ctxt->free_list, &xprt->sc_ctxt_free);
- xprt->sc_ctxt_cnt++;
- }
- }
-}
-
-static void destroy_context_cache(struct svcxprt_rdma *xprt)
-{
- while (!list_empty(&xprt->sc_ctxt_free)) {
- struct svc_rdma_op_ctxt *ctxt;
- ctxt = list_entry(xprt->sc_ctxt_free.next,
- struct svc_rdma_op_ctxt,
- free_list);
- list_del_init(&ctxt->free_list);
- kfree(ctxt);
- }
-}
-
static struct svcxprt_rdma *rdma_create_xprt(struct svc_serv *serv,
int listener)
{
@@ -473,7 +429,6 @@ static struct svcxprt_rdma *rdma_create_xprt(struct svc_serv *serv,
spin_lock_init(&cma_xprt->sc_lock);
spin_lock_init(&cma_xprt->sc_read_complete_lock);
- spin_lock_init(&cma_xprt->sc_ctxt_lock);
spin_lock_init(&cma_xprt->sc_rq_dto_lock);
cma_xprt->sc_ord = svcrdma_ord;
@@ -482,21 +437,9 @@ static struct svcxprt_rdma *rdma_create_xprt(struct svc_serv *serv,
cma_xprt->sc_max_requests = svcrdma_max_requests;
cma_xprt->sc_sq_depth = svcrdma_max_requests * RPCRDMA_SQ_DEPTH_MULT;
atomic_set(&cma_xprt->sc_sq_count, 0);
+ atomic_set(&cma_xprt->sc_ctxt_used, 0);
- if (!listener) {
- int reqs = cma_xprt->sc_max_requests;
- create_context_cache(cma_xprt,
- reqs << 1, /* starting size */
- reqs, /* bump amount */
- reqs +
- cma_xprt->sc_sq_depth +
- RPCRDMA_MAX_THREADS + 1); /* max */
- if (list_empty(&cma_xprt->sc_ctxt_free)) {
- kfree(cma_xprt);
- return NULL;
- }
- clear_bit(XPT_LISTENER, &cma_xprt->sc_xprt.xpt_flags);
- } else
+ if (listener)
set_bit(XPT_LISTENER, &cma_xprt->sc_xprt.xpt_flags);
return cma_xprt;
@@ -532,6 +475,7 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt)
BUG_ON(sge_no >= xprt->sc_max_sge);
page = svc_rdma_get_page();
ctxt->pages[sge_no] = page;
+ atomic_inc(&xprt->sc_dma_used);
pa = ib_dma_map_page(xprt->sc_cm_id->device,
page, 0, PAGE_SIZE,
DMA_FROM_DEVICE);
@@ -566,7 +510,7 @@ int svc_rdma_post_recv(struct svcxprt_rdma *xprt)
* will call the recvfrom method on the listen xprt which will accept the new
* connection.
*/
-static void handle_connect_req(struct rdma_cm_id *new_cma_id)
+static void handle_connect_req(struct rdma_cm_id *new_cma_id, size_t client_ird)
{
struct svcxprt_rdma *listen_xprt = new_cma_id->context;
struct svcxprt_rdma *newxprt;
@@ -583,6 +527,9 @@ static void handle_connect_req(struct rdma_cm_id *new_cma_id)
dprintk("svcrdma: Creating newxprt=%p, cm_id=%p, listenxprt=%p\n",
newxprt, newxprt->sc_cm_id, listen_xprt);
+ /* Save client advertised inbound read limit for use later in accept. */
+ newxprt->sc_ord = client_ird;
+
/* Set the local and remote addresses in the transport */
sa = (struct sockaddr *)&newxprt->sc_cm_id->route.addr.dst_addr;
svc_xprt_set_remote(&newxprt->sc_xprt, sa, svc_addr_len(sa));
@@ -619,7 +566,8 @@ static int rdma_listen_handler(struct rdma_cm_id *cma_id,
case RDMA_CM_EVENT_CONNECT_REQUEST:
dprintk("svcrdma: Connect request on cma_id=%p, xprt = %p, "
"event=%d\n", cma_id, cma_id->context, event->event);
- handle_connect_req(cma_id);
+ handle_connect_req(cma_id,
+ event->param.conn.responder_resources);
break;
case RDMA_CM_EVENT_ESTABLISHED:
@@ -793,8 +741,12 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt)
(size_t)svcrdma_max_requests);
newxprt->sc_sq_depth = RPCRDMA_SQ_DEPTH_MULT * newxprt->sc_max_requests;
- newxprt->sc_ord = min((size_t)devattr.max_qp_rd_atom,
- (size_t)svcrdma_ord);
+ /*
+ * Limit ORD based on client limit, local device limit, and
+ * configured svcrdma limit.
+ */
+ newxprt->sc_ord = min_t(size_t, devattr.max_qp_rd_atom, newxprt->sc_ord);
+ newxprt->sc_ord = min_t(size_t, svcrdma_ord, newxprt->sc_ord);
newxprt->sc_pd = ib_alloc_pd(newxprt->sc_cm_id->device);
if (IS_ERR(newxprt->sc_pd)) {
@@ -987,7 +939,6 @@ static void __svc_rdma_free(struct work_struct *work)
* cm_id because the device ptr is needed to unmap the dma in
* svc_rdma_put_context.
*/
- spin_lock_bh(&rdma->sc_read_complete_lock);
while (!list_empty(&rdma->sc_read_complete_q)) {
struct svc_rdma_op_ctxt *ctxt;
ctxt = list_entry(rdma->sc_read_complete_q.next,
@@ -996,10 +947,8 @@ static void __svc_rdma_free(struct work_struct *work)
list_del_init(&ctxt->dto_q);
svc_rdma_put_context(ctxt, 1);
}
- spin_unlock_bh(&rdma->sc_read_complete_lock);
/* Destroy queued, but not processed recv completions */
- spin_lock_bh(&rdma->sc_rq_dto_lock);
while (!list_empty(&rdma->sc_rq_dto_q)) {
struct svc_rdma_op_ctxt *ctxt;
ctxt = list_entry(rdma->sc_rq_dto_q.next,
@@ -1008,10 +957,10 @@ static void __svc_rdma_free(struct work_struct *work)
list_del_init(&ctxt->dto_q);
svc_rdma_put_context(ctxt, 1);
}
- spin_unlock_bh(&rdma->sc_rq_dto_lock);
/* Warn if we leaked a resource or under-referenced */
WARN_ON(atomic_read(&rdma->sc_ctxt_used) != 0);
+ WARN_ON(atomic_read(&rdma->sc_dma_used) != 0);
/* Destroy the QP if present (not a listener) */
if (rdma->sc_qp && !IS_ERR(rdma->sc_qp))
@@ -1032,7 +981,6 @@ static void __svc_rdma_free(struct work_struct *work)
/* Destroy the CM ID */
rdma_destroy_id(rdma->sc_cm_id);
- destroy_context_cache(rdma);
kfree(rdma);
}
@@ -1132,6 +1080,7 @@ void svc_rdma_send_error(struct svcxprt_rdma *xprt, struct rpcrdma_msg *rmsgp,
length = svc_rdma_xdr_encode_error(xprt, rmsgp, err, va);
/* Prepare SGE for local address */
+ atomic_inc(&xprt->sc_dma_used);
sge.addr = ib_dma_map_page(xprt->sc_cm_id->device,
p, 0, PAGE_SIZE, DMA_FROM_DEVICE);
sge.lkey = xprt->sc_phys_mr->lkey;
diff --git a/samples/firmware_class/firmware_sample_firmware_class.c b/samples/firmware_class/firmware_sample_firmware_class.c
index 9392116e47b0..e6cf7a43a297 100644
--- a/samples/firmware_class/firmware_sample_firmware_class.c
+++ b/samples/firmware_class/firmware_sample_firmware_class.c
@@ -124,7 +124,7 @@ static int fw_setup_class_device(struct class_device *class_dev,
class_dev->class_id[BUS_ID_SIZE-1] = '\0';
class_dev->dev = device;
- class_dev->class = &firmware_class,
+ class_dev->class = &firmware_class;
class_set_devdata(class_dev, fw_priv);
retval = class_device_register(class_dev);
if (retval) {
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 8e440233c27d..ea48b82a3707 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -96,7 +96,8 @@ basename_flags = -D"KBUILD_BASENAME=KBUILD_STR($(call name-fix,$(basetarget)))"
modname_flags = $(if $(filter 1,$(words $(modname))),\
-D"KBUILD_MODNAME=KBUILD_STR($(call name-fix,$(modname)))")
-_c_flags = $(KBUILD_CFLAGS) $(ccflags-y) $(CFLAGS_$(basetarget).o)
+orig_c_flags = $(KBUILD_CFLAGS) $(ccflags-y) $(CFLAGS_$(basetarget).o)
+_c_flags = $(filter-out $(CFLAGS_REMOVE_$(basetarget).o), $(orig_c_flags))
_a_flags = $(KBUILD_AFLAGS) $(asflags-y) $(AFLAGS_$(basetarget).o)
_cpp_flags = $(KBUILD_CPPFLAGS) $(cppflags-y) $(CPPFLAGS_$(@F))
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index cea4a790e1e9..1fcaf3284a6a 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -304,6 +304,14 @@ static int do_ap_entry(const char *filename,
return 1;
}
+/* looks like: "css:tN" */
+static int do_css_entry(const char *filename,
+ struct css_device_id *id, char *alias)
+{
+ sprintf(alias, "css:t%01X", id->type);
+ return 1;
+}
+
/* Looks like: "serio:tyNprNidNexN" */
static int do_serio_entry(const char *filename,
struct serio_device_id *id, char *alias)
@@ -332,11 +340,18 @@ static int do_acpi_entry(const char *filename,
}
/* looks like: "pnp:dD" */
-static int do_pnp_entry(const char *filename,
- struct pnp_device_id *id, char *alias)
+static void do_pnp_device_entry(void *symval, unsigned long size,
+ struct module *mod)
{
- sprintf(alias, "pnp:d%s*", id->id);
- return 1;
+ const unsigned long id_size = sizeof(struct pnp_device_id);
+ const struct pnp_device_id *id = symval;
+
+ device_id_check(mod->name, "pnp", size, id_size, symval);
+
+ buf_printf(&mod->dev_table_buf,
+ "MODULE_ALIAS(\"pnp:d%s*\");\n", id->id);
+ buf_printf(&mod->dev_table_buf,
+ "MODULE_ALIAS(\"acpi*:%s:*\");\n", id->id);
}
/* looks like: "pnp:dD" for every device of the card */
@@ -380,9 +395,12 @@ static void do_pnp_card_entries(void *symval, unsigned long size,
}
/* add an individual alias for every device entry */
- if (!dup)
+ if (!dup) {
buf_printf(&mod->dev_table_buf,
"MODULE_ALIAS(\"pnp:d%s*\");\n", id);
+ buf_printf(&mod->dev_table_buf,
+ "MODULE_ALIAS(\"acpi*:%s:*\");\n", id);
+ }
}
}
}
@@ -680,6 +698,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
do_table(symval, sym->st_size,
sizeof(struct ap_device_id), "ap",
do_ap_entry, mod);
+ else if (sym_is(symname, "__mod_css_device_table"))
+ do_table(symval, sym->st_size,
+ sizeof(struct css_device_id), "css",
+ do_css_entry, mod);
else if (sym_is(symname, "__mod_serio_device_table"))
do_table(symval, sym->st_size,
sizeof(struct serio_device_id), "serio",
@@ -689,9 +711,7 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
sizeof(struct acpi_device_id), "acpi",
do_acpi_entry, mod);
else if (sym_is(symname, "__mod_pnp_device_table"))
- do_table(symval, sym->st_size,
- sizeof(struct pnp_device_id), "pnp",
- do_pnp_entry, mod);
+ do_pnp_device_entry(symval, sym->st_size, mod);
else if (sym_is(symname, "__mod_pnp_card_device_table"))
do_pnp_card_entries(symval, sym->st_size, mod);
else if (sym_is(symname, "__mod_pcmcia_device_table"))
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index 508c5895c680..a07f91aac920 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -467,6 +467,25 @@ static void parse_elf_finish(struct elf_info *info)
release_file(info->hdr, info->size);
}
+static int ignore_undef_symbol(struct elf_info *info, const char *symname)
+{
+ /* ignore __this_module, it will be resolved shortly */
+ if (strcmp(symname, MODULE_SYMBOL_PREFIX "__this_module") == 0)
+ return 1;
+ /* ignore global offset table */
+ if (strcmp(symname, "_GLOBAL_OFFSET_TABLE_") == 0)
+ return 1;
+ if (info->hdr->e_machine == EM_PPC)
+ /* Special register function linked on all modules during final link of .ko */
+ if (strncmp(symname, "_restgpr_", sizeof("_restgpr_") - 1) == 0 ||
+ strncmp(symname, "_savegpr_", sizeof("_savegpr_") - 1) == 0 ||
+ strncmp(symname, "_rest32gpr_", sizeof("_rest32gpr_") - 1) == 0 ||
+ strncmp(symname, "_save32gpr_", sizeof("_save32gpr_") - 1) == 0)
+ return 1;
+ /* Do not ignore this symbol */
+ return 0;
+}
+
#define CRC_PFX MODULE_SYMBOL_PREFIX "__crc_"
#define KSYMTAB_PFX MODULE_SYMBOL_PREFIX "__ksymtab_"
@@ -493,11 +512,7 @@ static void handle_modversions(struct module *mod, struct elf_info *info,
if (ELF_ST_BIND(sym->st_info) != STB_GLOBAL &&
ELF_ST_BIND(sym->st_info) != STB_WEAK)
break;
- /* ignore global offset table */
- if (strcmp(symname, "_GLOBAL_OFFSET_TABLE_") == 0)
- break;
- /* ignore __this_module, it will be resolved shortly */
- if (strcmp(symname, MODULE_SYMBOL_PREFIX "__this_module") == 0)
+ if (ignore_undef_symbol(info, symname))
break;
/* cope with newer glibc (2.3.4 or higher) STT_ definition in elf.h */
#if defined(STT_REGISTER) || defined(STT_SPARC_REGISTER)
diff --git a/sound/core/init.c b/sound/core/init.c
index ac0573416130..7722c0dcff98 100644
--- a/sound/core/init.c
+++ b/sound/core/init.c
@@ -535,8 +535,9 @@ int snd_card_register(struct snd_card *card)
snd_assert(card != NULL, return -EINVAL);
#ifndef CONFIG_SYSFS_DEPRECATED
if (!card->card_dev) {
- card->card_dev = device_create(sound_class, card->dev, 0,
- "card%i", card->number);
+ card->card_dev = device_create_drvdata(sound_class, card->dev,
+ MKDEV(0, 0), NULL,
+ "card%i", card->number);
if (IS_ERR(card->card_dev))
card->card_dev = NULL;
}
diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c
index 99538862e342..585af2eb1438 100644
--- a/sound/drivers/vx/vx_core.c
+++ b/sound/drivers/vx/vx_core.c
@@ -453,7 +453,7 @@ int snd_vx_load_boot_image(struct vx_core *chip, const struct firmware *boot)
vx_outb(chip, TXM, 0);
vx_outb(chip, TXL, 0);
} else {
- unsigned char *image = boot->data + i;
+ const unsigned char *image = boot->data + i;
if (vx_wait_isr_bit(chip, ISR_TX_EMPTY) < 0) {
snd_printk(KERN_ERR "dsp boot failed at %d\n", i);
return -EIO;
@@ -671,7 +671,7 @@ int snd_vx_dsp_load(struct vx_core *chip, const struct firmware *dsp)
unsigned int i;
int err;
unsigned int csum = 0;
- unsigned char *image, *cptr;
+ const unsigned char *image, *cptr;
snd_assert(dsp->size % 3 == 0, return -EINVAL);
diff --git a/sound/oss/soundcard.c b/sound/oss/soundcard.c
index a9c23b2502ad..7d89c081a086 100644
--- a/sound/oss/soundcard.c
+++ b/sound/oss/soundcard.c
@@ -560,17 +560,19 @@ static int __init oss_init(void)
sound_dmap_flag = (dmabuf > 0 ? 1 : 0);
for (i = 0; i < ARRAY_SIZE(dev_list); i++) {
- device_create(sound_class, NULL,
- MKDEV(SOUND_MAJOR, dev_list[i].minor),
- "%s", dev_list[i].name);
+ device_create_drvdata(sound_class, NULL,
+ MKDEV(SOUND_MAJOR, dev_list[i].minor),
+ NULL, "%s", dev_list[i].name);
if (!dev_list[i].num)
continue;
for (j = 1; j < *dev_list[i].num; j++)
- device_create(sound_class, NULL,
- MKDEV(SOUND_MAJOR, dev_list[i].minor + (j*0x10)),
- "%s%d", dev_list[i].name, j);
+ device_create_drvdata(sound_class, NULL,
+ MKDEV(SOUND_MAJOR,
+ dev_list[i].minor + (j*0x10)),
+ NULL,
+ "%s%d", dev_list[i].name, j);
}
if (sound_nblocks >= 1024)
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index a536c59fbea1..9dfba6eff858 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -2240,18 +2240,16 @@ static const struct firmware assp_minisrc = {
.size = sizeof assp_minisrc_image
};
-#else /* CONFIG_SND_MAESTRO3_FIRMWARE_IN_KERNEL */
-
#ifdef __LITTLE_ENDIAN
-static inline void snd_m3_convert_from_le(const struct firmware *fw) { }
+static inline void snd_m3_convert_to_le(const struct firmware *fw) { }
#else
-static void snd_m3_convert_from_le(const struct firmware *fw)
+static void snd_m3_convert_to_le(const struct firmware *fw)
{
int i;
u16 *data = (u16 *)fw->data;
for (i = 0; i < fw->size / 2; ++i)
- le16_to_cpus(&data[i]);
+ cpu_to_le16s(&data[i]);
}
#endif
@@ -2271,7 +2269,7 @@ static const u16 minisrc_lpf[MINISRC_LPF_LEN] = {
static void snd_m3_assp_init(struct snd_m3 *chip)
{
unsigned int i;
- u16 *data;
+ const u16 *data;
/* zero kernel data */
for (i = 0; i < (REV_B_DATA_MEMORY_UNIT_LENGTH * NUM_UNITS_KERNEL_DATA) / 2; i++)
@@ -2289,10 +2287,11 @@ static void snd_m3_assp_init(struct snd_m3 *chip)
KDATA_DMA_XFER0);
/* write kernel into code memory.. */
- data = (u16 *)chip->assp_kernel_image->data;
+ data = (const u16 *)chip->assp_kernel_image->data;
for (i = 0 ; i * 2 < chip->assp_kernel_image->size; i++) {
snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE,
- REV_B_CODE_MEMORY_BEGIN + i, data[i]);
+ REV_B_CODE_MEMORY_BEGIN + i,
+ le16_to_cpu(data[i]));
}
/*
@@ -2301,10 +2300,10 @@ static void snd_m3_assp_init(struct snd_m3 *chip)
* drop it there. It seems that the minisrc doesn't
* need vectors, so we won't bother with them..
*/
- data = (u16 *)chip->assp_minisrc_image->data;
+ data = (const u16 *)chip->assp_minisrc_image->data;
for (i = 0; i * 2 < chip->assp_minisrc_image->size; i++) {
snd_m3_assp_write(chip, MEMTYPE_INTERNAL_CODE,
- 0x400 + i, data[i]);
+ 0x400 + i, le16_to_cpu(data[i]));
}
/*
@@ -2749,8 +2748,7 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
if (err < 0) {
snd_m3_free(chip);
return err;
- } else
- snd_m3_convert_from_le(chip->assp_kernel_image);
+ }
#endif
#ifdef CONFIG_SND_MAESTRO3_FIRMWARE_IN_KERNEL
@@ -2761,8 +2759,7 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
if (err < 0) {
snd_m3_free(chip);
return err;
- } else
- snd_m3_convert_from_le(chip->assp_minisrc_image);
+ }
#endif
if ((err = pci_request_regions(pci, card->driver)) < 0) {
@@ -2915,6 +2912,10 @@ static struct pci_driver driver = {
static int __init alsa_card_m3_init(void)
{
+#ifdef CONFIG_SND_MAESTRO3_FIRMWARE_IN_KERNEL
+ snd_m3_convert_to_le(&assp_kernel);
+ snd_m3_convert_to_le(&assp_minisrc);
+#endif
return pci_register_driver(&driver);
}
diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c
index 78aa81feaa4a..957e6afe94f1 100644
--- a/sound/pci/pcxhr/pcxhr_core.c
+++ b/sound/pci/pcxhr/pcxhr_core.c
@@ -269,7 +269,7 @@ int pcxhr_load_xilinx_binary(struct pcxhr_mgr *mgr, const struct firmware *xilin
unsigned int chipsc;
unsigned char data;
unsigned char mask;
- unsigned char *image;
+ const unsigned char *image;
/* test first xilinx */
chipsc = PCXHR_INPL(mgr, PCXHR_PLX_CHIPSC);
@@ -316,7 +316,7 @@ static int pcxhr_download_dsp(struct pcxhr_mgr *mgr, const struct firmware *dsp)
int err;
unsigned int i;
unsigned int len;
- unsigned char *data;
+ const unsigned char *data;
unsigned char dummy;
/* check the length of boot image */
snd_assert(dsp->size > 0, return -EINVAL);
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index 979f7da641ce..6a3596247348 100644
--- a/sound/pci/riptide/riptide.c
+++ b/sound/pci/riptide/riptide.c
@@ -682,7 +682,7 @@ static union firmware_version firmware_versions[] = {
},
};
-static u32 atoh(unsigned char *in, unsigned int len)
+static u32 atoh(const unsigned char *in, unsigned int len)
{
u32 sum = 0;
unsigned int mult = 1;
@@ -702,12 +702,12 @@ static u32 atoh(unsigned char *in, unsigned int len)
return sum;
}
-static int senddata(struct cmdif *cif, unsigned char *in, u32 offset)
+static int senddata(struct cmdif *cif, const unsigned char *in, u32 offset)
{
u32 addr;
u32 data;
u32 i;
- unsigned char *p;
+ const unsigned char *p;
i = atoh(&in[1], 2);
addr = offset + atoh(&in[3], 4);
@@ -726,10 +726,10 @@ static int senddata(struct cmdif *cif, unsigned char *in, u32 offset)
return 0;
}
-static int loadfirmware(struct cmdif *cif, unsigned char *img,
+static int loadfirmware(struct cmdif *cif, const unsigned char *img,
unsigned int size)
{
- unsigned char *in;
+ const unsigned char *in;
u32 laddr, saddr, t, val;
int err = 0;
diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c
index b4bfc1acde88..631f3a639993 100644
--- a/sound/pci/vx222/vx222_ops.c
+++ b/sound/pci/vx222/vx222_ops.c
@@ -359,7 +359,7 @@ static int vx2_load_xilinx_binary(struct vx_core *chip, const struct firmware *x
{
unsigned int i;
unsigned int port;
- unsigned char *image;
+ const unsigned char *image;
/* XILINX reset (wait at least 1 milisecond between reset on and off). */
vx_outl(chip, CNTRL, VX_CNTRL_REGISTER_VALUE | VX_XILINX_RESET_MASK);
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
index 29b3056c5109..6298b29c66bb 100644
--- a/sound/pci/ymfpci/ymfpci_main.c
+++ b/sound/pci/ymfpci/ymfpci_main.c
@@ -26,6 +26,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
+#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/control.h>
@@ -2009,11 +2010,34 @@ static struct firmware snd_ymfpci_controller_1e_microcode = {
.size = YDSXG_CTRLLENGTH,
.data = (u8 *)CntrlInst1E,
};
+
+#ifdef __BIG_ENDIAN
+static int microcode_swapped;
+static DEFINE_MUTEX(microcode_swap);
+
+static void snd_ymfpci_convert_to_le(const struct firmware *fw)
+{
+ int i;
+ u32 *data = (u32 *)fw->data;
+
+ for (i = 0; i < fw->size / 4; ++i)
+ cpu_to_le32s(&data[i]);
+}
#endif
-#ifdef CONFIG_SND_YMFPCI_FIRMWARE_IN_KERNEL
static int snd_ymfpci_request_firmware(struct snd_ymfpci *chip)
{
+#ifdef __BIG_ENDIAN
+ mutex_lock(&microcode_swap);
+ if (!microcode_swapped) {
+ snd_ymfpci_convert_to_le(&snd_ymfpci_dsp_microcode);
+ snd_ymfpci_convert_to_le(&snd_ymfpci_controller_1e_microcode);
+ snd_ymfpci_convert_to_le(&snd_ymfpci_controller_microcode);
+ microcode_swapped = 1;
+ }
+ mutex_unlock(&microcode_swap);
+#endif
+
chip->dsp_microcode = &snd_ymfpci_dsp_microcode;
if (chip->device_id == PCI_DEVICE_ID_YAMAHA_724F ||
chip->device_id == PCI_DEVICE_ID_YAMAHA_740C ||
@@ -2029,19 +2053,6 @@ static int snd_ymfpci_request_firmware(struct snd_ymfpci *chip)
#else /* use fw_loader */
-#ifdef __LITTLE_ENDIAN
-static inline void snd_ymfpci_convert_from_le(const struct firmware *fw) { }
-#else
-static void snd_ymfpci_convert_from_le(const struct firmware *fw)
-{
- int i;
- u32 *data = (u32 *)fw->data;
-
- for (i = 0; i < fw->size / 4; ++i)
- le32_to_cpus(&data[i]);
-}
-#endif
-
static int snd_ymfpci_request_firmware(struct snd_ymfpci *chip)
{
int err, is_1e;
@@ -2050,9 +2061,7 @@ static int snd_ymfpci_request_firmware(struct snd_ymfpci *chip)
err = request_firmware(&chip->dsp_microcode, "yamaha/ds1_dsp.fw",
&chip->pci->dev);
if (err >= 0) {
- if (chip->dsp_microcode->size == YDSXG_DSPLENGTH)
- snd_ymfpci_convert_from_le(chip->dsp_microcode);
- else {
+ if (chip->dsp_microcode->size != YDSXG_DSPLENGTH) {
snd_printk(KERN_ERR "DSP microcode has wrong size\n");
err = -EINVAL;
}
@@ -2067,9 +2076,7 @@ static int snd_ymfpci_request_firmware(struct snd_ymfpci *chip)
err = request_firmware(&chip->controller_microcode, name,
&chip->pci->dev);
if (err >= 0) {
- if (chip->controller_microcode->size == YDSXG_CTRLLENGTH)
- snd_ymfpci_convert_from_le(chip->controller_microcode);
- else {
+ if (chip->controller_microcode->size != YDSXG_CTRLLENGTH) {
snd_printk(KERN_ERR "controller microcode"
" has wrong size\n");
err = -EINVAL;
@@ -2090,7 +2097,7 @@ static void snd_ymfpci_download_image(struct snd_ymfpci *chip)
{
int i;
u16 ctrl;
- u32 *inst;
+ const __le32 *inst;
snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x00000000);
snd_ymfpci_disable_dsp(chip);
@@ -2105,14 +2112,16 @@ static void snd_ymfpci_download_image(struct snd_ymfpci *chip)
snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007);
/* setup DSP instruction code */
- inst = (u32 *)chip->dsp_microcode->data;
+ inst = (const __le32 *)chip->dsp_microcode->data;
for (i = 0; i < YDSXG_DSPLENGTH / 4; i++)
- snd_ymfpci_writel(chip, YDSXGR_DSPINSTRAM + (i << 2), inst[i]);
+ snd_ymfpci_writel(chip, YDSXGR_DSPINSTRAM + (i << 2),
+ le32_to_cpu(inst[i]));
/* setup control instruction code */
- inst = (u32 *)chip->controller_microcode->data;
+ inst = (const __le32 *)chip->controller_microcode->data;
for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++)
- snd_ymfpci_writel(chip, YDSXGR_CTRLINSTRAM + (i << 2), inst[i]);
+ snd_ymfpci_writel(chip, YDSXGR_CTRLINSTRAM + (i << 2),
+ le32_to_cpu(inst[i]));
snd_ymfpci_enable_dsp(chip);
}
diff --git a/sound/sound_core.c b/sound/sound_core.c
index 46daca175502..fb13ba2946ef 100644
--- a/sound/sound_core.c
+++ b/sound/sound_core.c
@@ -170,8 +170,9 @@ static int sound_insert_unit(struct sound_unit **list, const struct file_operati
else
sprintf(s->name, "sound/%s%d", name, r / SOUND_STEP);
- device_create(sound_class, dev, MKDEV(SOUND_MAJOR, s->unit_minor),
- s->name+6);
+ device_create_drvdata(sound_class, dev,
+ MKDEV(SOUND_MAJOR, s->unit_minor),
+ NULL, s->name+6);
return r;
fail:
diff --git a/tests/Kconfig b/tests/Kconfig
new file mode 100644
index 000000000000..cbfbd7aea906
--- /dev/null
+++ b/tests/Kconfig
@@ -0,0 +1,80 @@
+# tests/Kconfig
+
+menuconfig KERNEL_TESTS
+ bool "Kernel subsystem tests"
+ help
+ You can build kernel subsystem specific tests.
+
+if KERNEL_TESTS
+
+config DEBUG_LOCKING_API_SELFTESTS
+ bool "Locking API boot-time self-tests"
+ depends on DEBUG_KERNEL
+ help
+ Say Y here if you want the kernel to run a short self-test during
+ bootup. The self-test checks whether common types of locking bugs
+ are detected by debugging mechanisms or not. (if you disable
+ lock debugging then those bugs wont be detected of course.)
+ The following locking APIs are covered: spinlocks, rwlocks,
+ mutexes and rwsems.
+
+config RCU_TORTURE_TEST
+ tristate "torture tests for RCU"
+ depends on DEBUG_KERNEL
+ depends on m
+ default n
+ help
+ This option provides a kernel module that runs torture tests
+ on the RCU infrastructure. The kernel module may be built
+ after the fact on the running kernel to be tested, if desired.
+
+ Say M if you want the RCU torture tests to build as a module.
+ Say N if you are unsure.
+
+config RT_MUTEX_TESTER
+ bool "Built-in scriptable tester for rt-mutexes"
+ depends on DEBUG_KERNEL && RT_MUTEXES
+ help
+ This option enables a rt-mutex tester.
+
+config LKDTM
+ tristate "Linux Kernel Dump Test Tool Module"
+ depends on DEBUG_KERNEL
+ depends on KPROBES
+ depends on BLOCK
+ default n
+ help
+ This module enables testing of the different dumping mechanisms by
+ inducing system failures at predefined crash points.
+ If you don't need it: say N
+ Choose M here to compile this code as a module. The module will be
+ called lkdtm.
+
+ Documentation on how to use the module can be found in
+ tests/lkdtm.c
+
+config KPROBES_SANITY_TEST
+ bool "Kprobes sanity tests"
+ depends on DEBUG_KERNEL
+ depends on KPROBES
+ default n
+ help
+ This option provides for testing basic kprobes functionality on
+ boot. A sample kprobe, jprobe and kretprobe are inserted and
+ verified for functionality.
+
+ Say N if you are unsure.
+
+config BACKTRACE_SELF_TEST
+ tristate "Self test for the backtrace code"
+ depends on DEBUG_KERNEL
+ default n
+ help
+ This option provides a kernel module that can be used to test
+ the kernel stack backtrace code. This option is not useful
+ for distributions or general kernels, but only for kernel
+ developers working on architecture code.
+
+ Say N if you are unsure.
+
+endif # KERNEL_TESTS
diff --git a/tests/Makefile b/tests/Makefile
new file mode 100644
index 000000000000..1d56a9ea5993
--- /dev/null
+++ b/tests/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for kernel subsystem specific tests
+#
+
+obj-$(CONFIG_DEBUG_LOCKING_API_SELFTESTS) += locking-selftest.o
+obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o
+obj-$(CONFIG_RT_MUTEX_TESTER) += rtmutex-tester.o
+obj-$(CONFIG_LKDTM) += lkdtm.o
+obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o
+obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
diff --git a/kernel/backtracetest.c b/tests/backtracetest.c
index d1a7605c5b8f..d1a7605c5b8f 100644
--- a/kernel/backtracetest.c
+++ b/tests/backtracetest.c
diff --git a/drivers/misc/lkdtm.c b/tests/lkdtm.c
index 1bfe5d16963b..5b4f5fd6799f 100644
--- a/drivers/misc/lkdtm.c
+++ b/tests/lkdtm.c
@@ -216,7 +216,7 @@ static int lkdtm_parse_commandline(void)
}
if (cpoint == INVALID || cptype == NONE)
- return -EINVAL;
+ return -EINVAL;
count = cpoint_count;
@@ -232,7 +232,7 @@ static int recursive_loop(int a)
if (!recur_count)
return 0;
else
- return recursive_loop(a);
+ return recursive_loop(a);
}
void lkdtm_handler(void)
@@ -324,8 +324,8 @@ static int __init lkdtm_module_init(void)
}
if ((ret = register_jprobe(&lkdtm)) < 0) {
- printk(KERN_INFO "lkdtm : Couldn't register jprobe\n");
- return ret;
+ printk(KERN_INFO "lkdtm : Couldn't register jprobe\n");
+ return ret;
}
printk(KERN_INFO "lkdtm : Crash point %s of type %s registered\n",
@@ -335,8 +335,8 @@ static int __init lkdtm_module_init(void)
static void __exit lkdtm_module_exit(void)
{
- unregister_jprobe(&lkdtm);
- printk(KERN_INFO "lkdtm : Crash point unregistered\n");
+ unregister_jprobe(&lkdtm);
+ printk(KERN_INFO "lkdtm : Crash point unregistered\n");
}
module_init(lkdtm_module_init);
diff --git a/lib/locking-selftest-hardirq.h b/tests/locking-selftest-hardirq.h
index 10d4a150b259..10d4a150b259 100644
--- a/lib/locking-selftest-hardirq.h
+++ b/tests/locking-selftest-hardirq.h
diff --git a/lib/locking-selftest-mutex.h b/tests/locking-selftest-mutex.h
index 68601b6f584b..68601b6f584b 100644
--- a/lib/locking-selftest-mutex.h
+++ b/tests/locking-selftest-mutex.h
diff --git a/lib/locking-selftest-rlock-hardirq.h b/tests/locking-selftest-rlock-hardirq.h
index 9f517ebcb786..9f517ebcb786 100644
--- a/lib/locking-selftest-rlock-hardirq.h
+++ b/tests/locking-selftest-rlock-hardirq.h
diff --git a/lib/locking-selftest-rlock-softirq.h b/tests/locking-selftest-rlock-softirq.h
index 981455db7ff0..981455db7ff0 100644
--- a/lib/locking-selftest-rlock-softirq.h
+++ b/tests/locking-selftest-rlock-softirq.h
diff --git a/lib/locking-selftest-rlock.h b/tests/locking-selftest-rlock.h
index 6789044f4d0e..6789044f4d0e 100644
--- a/lib/locking-selftest-rlock.h
+++ b/tests/locking-selftest-rlock.h
diff --git a/lib/locking-selftest-rsem.h b/tests/locking-selftest-rsem.h
index 62da886680c7..62da886680c7 100644
--- a/lib/locking-selftest-rsem.h
+++ b/tests/locking-selftest-rsem.h
diff --git a/lib/locking-selftest-softirq.h b/tests/locking-selftest-softirq.h
index a83de2a04ace..a83de2a04ace 100644
--- a/lib/locking-selftest-softirq.h
+++ b/tests/locking-selftest-softirq.h
diff --git a/lib/locking-selftest-spin-hardirq.h b/tests/locking-selftest-spin-hardirq.h
index 693198dce30a..693198dce30a 100644
--- a/lib/locking-selftest-spin-hardirq.h
+++ b/tests/locking-selftest-spin-hardirq.h
diff --git a/lib/locking-selftest-spin-softirq.h b/tests/locking-selftest-spin-softirq.h
index c472e2a87ffc..c472e2a87ffc 100644
--- a/lib/locking-selftest-spin-softirq.h
+++ b/tests/locking-selftest-spin-softirq.h
diff --git a/lib/locking-selftest-spin.h b/tests/locking-selftest-spin.h
index ccd1b4b09757..ccd1b4b09757 100644
--- a/lib/locking-selftest-spin.h
+++ b/tests/locking-selftest-spin.h
diff --git a/lib/locking-selftest-wlock-hardirq.h b/tests/locking-selftest-wlock-hardirq.h
index 2dd2e5122caa..2dd2e5122caa 100644
--- a/lib/locking-selftest-wlock-hardirq.h
+++ b/tests/locking-selftest-wlock-hardirq.h
diff --git a/lib/locking-selftest-wlock-softirq.h b/tests/locking-selftest-wlock-softirq.h
index cb80d1cb944e..cb80d1cb944e 100644
--- a/lib/locking-selftest-wlock-softirq.h
+++ b/tests/locking-selftest-wlock-softirq.h
diff --git a/lib/locking-selftest-wlock.h b/tests/locking-selftest-wlock.h
index 0815322d99ed..0815322d99ed 100644
--- a/lib/locking-selftest-wlock.h
+++ b/tests/locking-selftest-wlock.h
diff --git a/lib/locking-selftest-wsem.h b/tests/locking-selftest-wsem.h
index b88c5f2dc5f0..b88c5f2dc5f0 100644
--- a/lib/locking-selftest-wsem.h
+++ b/tests/locking-selftest-wsem.h
diff --git a/lib/locking-selftest.c b/tests/locking-selftest.c
index 280332c1827c..280332c1827c 100644
--- a/lib/locking-selftest.c
+++ b/tests/locking-selftest.c
diff --git a/kernel/rcutorture.c b/tests/rcutorture.c
index 33acc424667e..0334b6a8baca 100644
--- a/kernel/rcutorture.c
+++ b/tests/rcutorture.c
@@ -192,6 +192,7 @@ struct rcu_torture_ops {
int (*completed)(void);
void (*deferredfree)(struct rcu_torture *p);
void (*sync)(void);
+ void (*cb_barrier)(void);
int (*stats)(char *page);
char *name;
};
@@ -265,6 +266,7 @@ static struct rcu_torture_ops rcu_ops = {
.completed = rcu_torture_completed,
.deferredfree = rcu_torture_deferred_free,
.sync = synchronize_rcu,
+ .cb_barrier = rcu_barrier,
.stats = NULL,
.name = "rcu"
};
@@ -304,6 +306,7 @@ static struct rcu_torture_ops rcu_sync_ops = {
.completed = rcu_torture_completed,
.deferredfree = rcu_sync_torture_deferred_free,
.sync = synchronize_rcu,
+ .cb_barrier = NULL,
.stats = NULL,
.name = "rcu_sync"
};
@@ -364,6 +367,7 @@ static struct rcu_torture_ops rcu_bh_ops = {
.completed = rcu_bh_torture_completed,
.deferredfree = rcu_bh_torture_deferred_free,
.sync = rcu_bh_torture_synchronize,
+ .cb_barrier = rcu_barrier_bh,
.stats = NULL,
.name = "rcu_bh"
};
@@ -377,6 +381,7 @@ static struct rcu_torture_ops rcu_bh_sync_ops = {
.completed = rcu_bh_torture_completed,
.deferredfree = rcu_sync_torture_deferred_free,
.sync = rcu_bh_torture_synchronize,
+ .cb_barrier = NULL,
.stats = NULL,
.name = "rcu_bh_sync"
};
@@ -458,6 +463,7 @@ static struct rcu_torture_ops srcu_ops = {
.completed = srcu_torture_completed,
.deferredfree = rcu_sync_torture_deferred_free,
.sync = srcu_torture_synchronize,
+ .cb_barrier = NULL,
.stats = srcu_torture_stats,
.name = "srcu"
};
@@ -482,6 +488,11 @@ static int sched_torture_completed(void)
return 0;
}
+static void rcu_sched_torture_deferred_free(struct rcu_torture *p)
+{
+ call_rcu_sched(&p->rtort_rcu, rcu_torture_cb);
+}
+
static void sched_torture_synchronize(void)
{
synchronize_sched();
@@ -494,12 +505,27 @@ static struct rcu_torture_ops sched_ops = {
.readdelay = rcu_read_delay, /* just reuse rcu's version. */
.readunlock = sched_torture_read_unlock,
.completed = sched_torture_completed,
- .deferredfree = rcu_sync_torture_deferred_free,
+ .deferredfree = rcu_sched_torture_deferred_free,
.sync = sched_torture_synchronize,
+ .cb_barrier = rcu_barrier_sched,
.stats = NULL,
.name = "sched"
};
+static struct rcu_torture_ops sched_ops_sync = {
+ .init = rcu_sync_torture_init,
+ .cleanup = NULL,
+ .readlock = sched_torture_read_lock,
+ .readdelay = rcu_read_delay, /* just reuse rcu's version. */
+ .readunlock = sched_torture_read_unlock,
+ .completed = sched_torture_completed,
+ .deferredfree = rcu_sync_torture_deferred_free,
+ .sync = sched_torture_synchronize,
+ .cb_barrier = NULL,
+ .stats = NULL,
+ .name = "sched_sync"
+};
+
/*
* RCU torture writer kthread. Repeatedly substitutes a new structure
* for that pointed to by rcu_torture_current, freeing the old structure
@@ -848,7 +874,9 @@ rcu_torture_cleanup(void)
stats_task = NULL;
/* Wait for all RCU callbacks to fire. */
- rcu_barrier();
+
+ if (cur_ops->cb_barrier != NULL)
+ cur_ops->cb_barrier();
rcu_torture_stats_print(); /* -After- the stats thread is stopped! */
@@ -868,7 +896,7 @@ rcu_torture_init(void)
int firsterr = 0;
static struct rcu_torture_ops *torture_ops[] =
{ &rcu_ops, &rcu_sync_ops, &rcu_bh_ops, &rcu_bh_sync_ops,
- &srcu_ops, &sched_ops, };
+ &srcu_ops, &sched_ops, &sched_ops_sync, };
/* Process args and tell the world that the torturer is on the job. */
for (i = 0; i < ARRAY_SIZE(torture_ops); i++) {
diff --git a/kernel/rtmutex-tester.c b/tests/rtmutex-tester.c
index 092e4c620af9..f0200bd21217 100644
--- a/kernel/rtmutex-tester.c
+++ b/tests/rtmutex-tester.c
@@ -15,7 +15,7 @@
#include <linux/timer.h>
#include <linux/freezer.h>
-#include "rtmutex.h"
+#include "../kernel/rtmutex.h"
#define MAX_RT_TEST_THREADS 8
#define MAX_RT_TEST_MUTEXES 8
diff --git a/kernel/test_kprobes.c b/tests/test_kprobes.c
index 06b6395b45b2..06b6395b45b2 100644
--- a/kernel/test_kprobes.c
+++ b/tests/test_kprobes.c
diff --git a/virt/kvm/coalesced_mmio.c b/virt/kvm/coalesced_mmio.c
new file mode 100644
index 000000000000..5ae620d32fac
--- /dev/null
+++ b/virt/kvm/coalesced_mmio.c
@@ -0,0 +1,156 @@
+/*
+ * KVM coalesced MMIO
+ *
+ * Copyright (c) 2008 Bull S.A.S.
+ *
+ * Author: Laurent Vivier <Laurent.Vivier@bull.net>
+ *
+ */
+
+#include "iodev.h"
+
+#include <linux/kvm_host.h>
+#include <linux/kvm.h>
+
+#include "coalesced_mmio.h"
+
+static int coalesced_mmio_in_range(struct kvm_io_device *this,
+ gpa_t addr, int len, int is_write)
+{
+ struct kvm_coalesced_mmio_dev *dev =
+ (struct kvm_coalesced_mmio_dev*)this->private;
+ struct kvm_coalesced_mmio_zone *zone;
+ int next;
+ int i;
+
+ if (!is_write)
+ return 0;
+
+ /* kvm->lock is taken by the caller and must be not released before
+ * dev.read/write
+ */
+
+ /* Are we able to batch it ? */
+
+ /* last is the first free entry
+ * check if we don't meet the first used entry
+ * there is always one unused entry in the buffer
+ */
+
+ next = (dev->kvm->coalesced_mmio_ring->last + 1) %
+ KVM_COALESCED_MMIO_MAX;
+ if (next == dev->kvm->coalesced_mmio_ring->first) {
+ /* full */
+ return 0;
+ }
+
+ /* is it in a batchable area ? */
+
+ for (i = 0; i < dev->nb_zones; i++) {
+ zone = &dev->zone[i];
+
+ /* (addr,len) is fully included in
+ * (zone->addr, zone->size)
+ */
+
+ if (zone->addr <= addr &&
+ addr + len <= zone->addr + zone->size)
+ return 1;
+ }
+ return 0;
+}
+
+static void coalesced_mmio_write(struct kvm_io_device *this,
+ gpa_t addr, int len, const void *val)
+{
+ struct kvm_coalesced_mmio_dev *dev =
+ (struct kvm_coalesced_mmio_dev*)this->private;
+ struct kvm_coalesced_mmio_ring *ring = dev->kvm->coalesced_mmio_ring;
+
+ /* kvm->lock must be taken by caller before call to in_range()*/
+
+ /* copy data in first free entry of the ring */
+
+ ring->coalesced_mmio[ring->last].phys_addr = addr;
+ ring->coalesced_mmio[ring->last].len = len;
+ memcpy(ring->coalesced_mmio[ring->last].data, val, len);
+ smp_wmb();
+ ring->last = (ring->last + 1) % KVM_COALESCED_MMIO_MAX;
+}
+
+static void coalesced_mmio_destructor(struct kvm_io_device *this)
+{
+ kfree(this);
+}
+
+int kvm_coalesced_mmio_init(struct kvm *kvm)
+{
+ struct kvm_coalesced_mmio_dev *dev;
+
+ dev = kzalloc(sizeof(struct kvm_coalesced_mmio_dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+ dev->dev.write = coalesced_mmio_write;
+ dev->dev.in_range = coalesced_mmio_in_range;
+ dev->dev.destructor = coalesced_mmio_destructor;
+ dev->dev.private = dev;
+ dev->kvm = kvm;
+ kvm->coalesced_mmio_dev = dev;
+ kvm_io_bus_register_dev(&kvm->mmio_bus, &dev->dev);
+
+ return 0;
+}
+
+int kvm_vm_ioctl_register_coalesced_mmio(struct kvm *kvm,
+ struct kvm_coalesced_mmio_zone *zone)
+{
+ struct kvm_coalesced_mmio_dev *dev = kvm->coalesced_mmio_dev;
+
+ if (dev == NULL)
+ return -EINVAL;
+
+ mutex_lock(&kvm->lock);
+ if (dev->nb_zones >= KVM_COALESCED_MMIO_ZONE_MAX) {
+ mutex_unlock(&kvm->lock);
+ return -ENOBUFS;
+ }
+
+ dev->zone[dev->nb_zones] = *zone;
+ dev->nb_zones++;
+
+ mutex_unlock(&kvm->lock);
+ return 0;
+}
+
+int kvm_vm_ioctl_unregister_coalesced_mmio(struct kvm *kvm,
+ struct kvm_coalesced_mmio_zone *zone)
+{
+ int i;
+ struct kvm_coalesced_mmio_dev *dev = kvm->coalesced_mmio_dev;
+ struct kvm_coalesced_mmio_zone *z;
+
+ if (dev == NULL)
+ return -EINVAL;
+
+ mutex_lock(&kvm->lock);
+
+ i = dev->nb_zones;
+ while(i) {
+ z = &dev->zone[i - 1];
+
+ /* unregister all zones
+ * included in (zone->addr, zone->size)
+ */
+
+ if (zone->addr <= z->addr &&
+ z->addr + z->size <= zone->addr + zone->size) {
+ dev->nb_zones--;
+ *z = dev->zone[dev->nb_zones];
+ }
+ i--;
+ }
+
+ mutex_unlock(&kvm->lock);
+
+ return 0;
+}
diff --git a/virt/kvm/coalesced_mmio.h b/virt/kvm/coalesced_mmio.h
new file mode 100644
index 000000000000..5ac0ec628461
--- /dev/null
+++ b/virt/kvm/coalesced_mmio.h
@@ -0,0 +1,23 @@
+/*
+ * KVM coalesced MMIO
+ *
+ * Copyright (c) 2008 Bull S.A.S.
+ *
+ * Author: Laurent Vivier <Laurent.Vivier@bull.net>
+ *
+ */
+
+#define KVM_COALESCED_MMIO_ZONE_MAX 100
+
+struct kvm_coalesced_mmio_dev {
+ struct kvm_io_device dev;
+ struct kvm *kvm;
+ int nb_zones;
+ struct kvm_coalesced_mmio_zone zone[KVM_COALESCED_MMIO_ZONE_MAX];
+};
+
+int kvm_coalesced_mmio_init(struct kvm *kvm);
+int kvm_vm_ioctl_register_coalesced_mmio(struct kvm *kvm,
+ struct kvm_coalesced_mmio_zone *zone);
+int kvm_vm_ioctl_unregister_coalesced_mmio(struct kvm *kvm,
+ struct kvm_coalesced_mmio_zone *zone);
diff --git a/virt/kvm/ioapic.c b/virt/kvm/ioapic.c
index 4232fd75dd20..8afc074ae51f 100644
--- a/virt/kvm/ioapic.c
+++ b/virt/kvm/ioapic.c
@@ -45,7 +45,7 @@
#else
#define ioapic_debug(fmt, arg...)
#endif
-static void ioapic_deliver(struct kvm_ioapic *vioapic, int irq);
+static int ioapic_deliver(struct kvm_ioapic *vioapic, int irq);
static unsigned long ioapic_read_indirect(struct kvm_ioapic *ioapic,
unsigned long addr,
@@ -89,8 +89,8 @@ static void ioapic_service(struct kvm_ioapic *ioapic, unsigned int idx)
pent = &ioapic->redirtbl[idx];
if (!pent->fields.mask) {
- ioapic_deliver(ioapic, idx);
- if (pent->fields.trig_mode == IOAPIC_LEVEL_TRIG)
+ int injected = ioapic_deliver(ioapic, idx);
+ if (injected && pent->fields.trig_mode == IOAPIC_LEVEL_TRIG)
pent->fields.remote_irr = 1;
}
if (!pent->fields.trig_mode)
@@ -133,7 +133,7 @@ static void ioapic_write_indirect(struct kvm_ioapic *ioapic, u32 val)
}
}
-static void ioapic_inj_irq(struct kvm_ioapic *ioapic,
+static int ioapic_inj_irq(struct kvm_ioapic *ioapic,
struct kvm_vcpu *vcpu,
u8 vector, u8 trig_mode, u8 delivery_mode)
{
@@ -143,7 +143,12 @@ static void ioapic_inj_irq(struct kvm_ioapic *ioapic,
ASSERT((delivery_mode == IOAPIC_FIXED) ||
(delivery_mode == IOAPIC_LOWEST_PRIORITY));
- kvm_apic_set_irq(vcpu, vector, trig_mode);
+ return kvm_apic_set_irq(vcpu, vector, trig_mode);
+}
+
+static void ioapic_inj_nmi(struct kvm_vcpu *vcpu)
+{
+ kvm_inject_nmi(vcpu);
}
static u32 ioapic_get_delivery_bitmask(struct kvm_ioapic *ioapic, u8 dest,
@@ -186,7 +191,7 @@ static u32 ioapic_get_delivery_bitmask(struct kvm_ioapic *ioapic, u8 dest,
return mask;
}
-static void ioapic_deliver(struct kvm_ioapic *ioapic, int irq)
+static int ioapic_deliver(struct kvm_ioapic *ioapic, int irq)
{
u8 dest = ioapic->redirtbl[irq].fields.dest_id;
u8 dest_mode = ioapic->redirtbl[irq].fields.dest_mode;
@@ -195,7 +200,7 @@ static void ioapic_deliver(struct kvm_ioapic *ioapic, int irq)
u8 trig_mode = ioapic->redirtbl[irq].fields.trig_mode;
u32 deliver_bitmask;
struct kvm_vcpu *vcpu;
- int vcpu_id;
+ int vcpu_id, r = 0;
ioapic_debug("dest=%x dest_mode=%x delivery_mode=%x "
"vector=%x trig_mode=%x\n",
@@ -204,7 +209,7 @@ static void ioapic_deliver(struct kvm_ioapic *ioapic, int irq)
deliver_bitmask = ioapic_get_delivery_bitmask(ioapic, dest, dest_mode);
if (!deliver_bitmask) {
ioapic_debug("no target on destination\n");
- return;
+ return 0;
}
switch (delivery_mode) {
@@ -216,7 +221,7 @@ static void ioapic_deliver(struct kvm_ioapic *ioapic, int irq)
vcpu = ioapic->kvm->vcpus[0];
#endif
if (vcpu != NULL)
- ioapic_inj_irq(ioapic, vcpu, vector,
+ r = ioapic_inj_irq(ioapic, vcpu, vector,
trig_mode, delivery_mode);
else
ioapic_debug("null lowest prio vcpu: "
@@ -234,18 +239,30 @@ static void ioapic_deliver(struct kvm_ioapic *ioapic, int irq)
deliver_bitmask &= ~(1 << vcpu_id);
vcpu = ioapic->kvm->vcpus[vcpu_id];
if (vcpu) {
- ioapic_inj_irq(ioapic, vcpu, vector,
+ r = ioapic_inj_irq(ioapic, vcpu, vector,
trig_mode, delivery_mode);
}
}
break;
-
- /* TODO: NMI */
+ case IOAPIC_NMI:
+ for (vcpu_id = 0; deliver_bitmask != 0; vcpu_id++) {
+ if (!(deliver_bitmask & (1 << vcpu_id)))
+ continue;
+ deliver_bitmask &= ~(1 << vcpu_id);
+ vcpu = ioapic->kvm->vcpus[vcpu_id];
+ if (vcpu)
+ ioapic_inj_nmi(vcpu);
+ else
+ ioapic_debug("NMI to vcpu %d failed\n",
+ vcpu->vcpu_id);
+ }
+ break;
default:
printk(KERN_WARNING "Unsupported delivery mode %d\n",
delivery_mode);
break;
}
+ return r;
}
void kvm_ioapic_set_irq(struct kvm_ioapic *ioapic, int irq, int level)
@@ -299,7 +316,8 @@ void kvm_ioapic_update_eoi(struct kvm *kvm, int vector)
ioapic_deliver(ioapic, gsi);
}
-static int ioapic_in_range(struct kvm_io_device *this, gpa_t addr)
+static int ioapic_in_range(struct kvm_io_device *this, gpa_t addr,
+ int len, int is_write)
{
struct kvm_ioapic *ioapic = (struct kvm_ioapic *)this->private;
diff --git a/virt/kvm/iodev.h b/virt/kvm/iodev.h
index c14e642027b2..55e8846ac3a6 100644
--- a/virt/kvm/iodev.h
+++ b/virt/kvm/iodev.h
@@ -27,7 +27,8 @@ struct kvm_io_device {
gpa_t addr,
int len,
const void *val);
- int (*in_range)(struct kvm_io_device *this, gpa_t addr);
+ int (*in_range)(struct kvm_io_device *this, gpa_t addr, int len,
+ int is_write);
void (*destructor)(struct kvm_io_device *this);
void *private;
@@ -49,9 +50,10 @@ static inline void kvm_iodevice_write(struct kvm_io_device *dev,
dev->write(dev, addr, len, val);
}
-static inline int kvm_iodevice_inrange(struct kvm_io_device *dev, gpa_t addr)
+static inline int kvm_iodevice_inrange(struct kvm_io_device *dev,
+ gpa_t addr, int len, int is_write)
{
- return dev->in_range(dev, addr);
+ return dev->in_range(dev, addr, len, is_write);
}
static inline void kvm_iodevice_destructor(struct kvm_io_device *dev)
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 2d29e260da3d..4f38f5ca3594 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -47,6 +47,10 @@
#include <asm/uaccess.h>
#include <asm/pgtable.h>
+#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET
+#include "coalesced_mmio.h"
+#endif
+
MODULE_AUTHOR("Qumranet");
MODULE_LICENSE("GPL");
@@ -65,6 +69,8 @@ struct dentry *kvm_debugfs_dir;
static long kvm_vcpu_ioctl(struct file *file, unsigned int ioctl,
unsigned long arg);
+bool kvm_rebooting;
+
static inline int valid_vcpu(int n)
{
return likely(n >= 0 && n < KVM_MAX_VCPUS);
@@ -183,10 +189,23 @@ EXPORT_SYMBOL_GPL(kvm_vcpu_uninit);
static struct kvm *kvm_create_vm(void)
{
struct kvm *kvm = kvm_arch_create_vm();
+#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET
+ struct page *page;
+#endif
if (IS_ERR(kvm))
goto out;
+#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET
+ page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+ if (!page) {
+ kfree(kvm);
+ return ERR_PTR(-ENOMEM);
+ }
+ kvm->coalesced_mmio_ring =
+ (struct kvm_coalesced_mmio_ring *)page_address(page);
+#endif
+
kvm->mm = current->mm;
atomic_inc(&kvm->mm->mm_count);
spin_lock_init(&kvm->mmu_lock);
@@ -198,6 +217,9 @@ static struct kvm *kvm_create_vm(void)
spin_lock(&kvm_lock);
list_add(&kvm->vm_list, &vm_list);
spin_unlock(&kvm_lock);
+#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET
+ kvm_coalesced_mmio_init(kvm);
+#endif
out:
return kvm;
}
@@ -241,6 +263,10 @@ static void kvm_destroy_vm(struct kvm *kvm)
kvm_io_bus_destroy(&kvm->pio_bus);
kvm_io_bus_destroy(&kvm->mmio_bus);
kvm_arch_destroy_vm(kvm);
+#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET
+ if (kvm->coalesced_mmio_ring != NULL)
+ free_page((unsigned long)kvm->coalesced_mmio_ring);
+#endif
mmdrop(mm);
}
@@ -532,6 +558,7 @@ pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn)
struct page *page[1];
unsigned long addr;
int npages;
+ pfn_t pfn;
might_sleep();
@@ -544,19 +571,38 @@ pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn)
npages = get_user_pages(current, current->mm, addr, 1, 1, 1, page,
NULL);
- if (npages != 1) {
- get_page(bad_page);
- return page_to_pfn(bad_page);
- }
+ if (unlikely(npages != 1)) {
+ struct vm_area_struct *vma;
- return page_to_pfn(page[0]);
+ vma = find_vma(current->mm, addr);
+ if (vma == NULL || addr < vma->vm_start ||
+ !(vma->vm_flags & VM_PFNMAP)) {
+ get_page(bad_page);
+ return page_to_pfn(bad_page);
+ }
+
+ pfn = ((addr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
+ BUG_ON(pfn_valid(pfn));
+ } else
+ pfn = page_to_pfn(page[0]);
+
+ return pfn;
}
EXPORT_SYMBOL_GPL(gfn_to_pfn);
struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn)
{
- return pfn_to_page(gfn_to_pfn(kvm, gfn));
+ pfn_t pfn;
+
+ pfn = gfn_to_pfn(kvm, gfn);
+ if (pfn_valid(pfn))
+ return pfn_to_page(pfn);
+
+ WARN_ON(!pfn_valid(pfn));
+
+ get_page(bad_page);
+ return bad_page;
}
EXPORT_SYMBOL_GPL(gfn_to_page);
@@ -569,7 +615,8 @@ EXPORT_SYMBOL_GPL(kvm_release_page_clean);
void kvm_release_pfn_clean(pfn_t pfn)
{
- put_page(pfn_to_page(pfn));
+ if (pfn_valid(pfn))
+ put_page(pfn_to_page(pfn));
}
EXPORT_SYMBOL_GPL(kvm_release_pfn_clean);
@@ -594,21 +641,25 @@ EXPORT_SYMBOL_GPL(kvm_set_page_dirty);
void kvm_set_pfn_dirty(pfn_t pfn)
{
- struct page *page = pfn_to_page(pfn);
- if (!PageReserved(page))
- SetPageDirty(page);
+ if (pfn_valid(pfn)) {
+ struct page *page = pfn_to_page(pfn);
+ if (!PageReserved(page))
+ SetPageDirty(page);
+ }
}
EXPORT_SYMBOL_GPL(kvm_set_pfn_dirty);
void kvm_set_pfn_accessed(pfn_t pfn)
{
- mark_page_accessed(pfn_to_page(pfn));
+ if (pfn_valid(pfn))
+ mark_page_accessed(pfn_to_page(pfn));
}
EXPORT_SYMBOL_GPL(kvm_set_pfn_accessed);
void kvm_get_pfn(pfn_t pfn)
{
- get_page(pfn_to_page(pfn));
+ if (pfn_valid(pfn))
+ get_page(pfn_to_page(pfn));
}
EXPORT_SYMBOL_GPL(kvm_get_pfn);
@@ -799,6 +850,10 @@ static int kvm_vcpu_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
else if (vmf->pgoff == KVM_PIO_PAGE_OFFSET)
page = virt_to_page(vcpu->arch.pio_data);
#endif
+#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET
+ else if (vmf->pgoff == KVM_COALESCED_MMIO_PAGE_OFFSET)
+ page = virt_to_page(vcpu->kvm->coalesced_mmio_ring);
+#endif
else
return VM_FAULT_SIGBUS;
get_page(page);
@@ -1121,6 +1176,32 @@ static long kvm_vm_ioctl(struct file *filp,
goto out;
break;
}
+#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET
+ case KVM_REGISTER_COALESCED_MMIO: {
+ struct kvm_coalesced_mmio_zone zone;
+ r = -EFAULT;
+ if (copy_from_user(&zone, argp, sizeof zone))
+ goto out;
+ r = -ENXIO;
+ r = kvm_vm_ioctl_register_coalesced_mmio(kvm, &zone);
+ if (r)
+ goto out;
+ r = 0;
+ break;
+ }
+ case KVM_UNREGISTER_COALESCED_MMIO: {
+ struct kvm_coalesced_mmio_zone zone;
+ r = -EFAULT;
+ if (copy_from_user(&zone, argp, sizeof zone))
+ goto out;
+ r = -ENXIO;
+ r = kvm_vm_ioctl_unregister_coalesced_mmio(kvm, &zone);
+ if (r)
+ goto out;
+ r = 0;
+ break;
+ }
+#endif
default:
r = kvm_arch_vm_ioctl(filp, ioctl, arg);
}
@@ -1179,7 +1260,6 @@ static int kvm_dev_ioctl_create_vm(void)
static long kvm_dev_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg)
{
- void __user *argp = (void __user *)arg;
long r = -EINVAL;
switch (ioctl) {
@@ -1196,7 +1276,7 @@ static long kvm_dev_ioctl(struct file *filp,
r = kvm_dev_ioctl_create_vm();
break;
case KVM_CHECK_EXTENSION:
- r = kvm_dev_ioctl_check_extension((long)argp);
+ r = kvm_dev_ioctl_check_extension(arg);
break;
case KVM_GET_VCPU_MMAP_SIZE:
r = -EINVAL;
@@ -1206,6 +1286,9 @@ static long kvm_dev_ioctl(struct file *filp,
#ifdef CONFIG_X86
r += PAGE_SIZE; /* pio data page */
#endif
+#ifdef KVM_COALESCED_MMIO_PAGE_OFFSET
+ r += PAGE_SIZE; /* coalesced mmio ring page */
+#endif
break;
case KVM_TRACE_ENABLE:
case KVM_TRACE_PAUSE:
@@ -1247,7 +1330,6 @@ static void hardware_disable(void *junk)
if (!cpu_isset(cpu, cpus_hardware_enabled))
return;
cpu_clear(cpu, cpus_hardware_enabled);
- decache_vcpus_on_cpu(cpu);
kvm_arch_hardware_disable(NULL);
}
@@ -1277,6 +1359,18 @@ static int kvm_cpu_hotplug(struct notifier_block *notifier, unsigned long val,
return NOTIFY_OK;
}
+
+asmlinkage void kvm_handle_fault_on_reboot(void)
+{
+ if (kvm_rebooting)
+ /* spin while reset goes on */
+ while (true)
+ ;
+ /* Fault while not rebooting. We want the trace. */
+ BUG();
+}
+EXPORT_SYMBOL_GPL(kvm_handle_fault_on_reboot);
+
static int kvm_reboot(struct notifier_block *notifier, unsigned long val,
void *v)
{
@@ -1286,6 +1380,7 @@ static int kvm_reboot(struct notifier_block *notifier, unsigned long val,
* in vmx root mode.
*/
printk(KERN_INFO "kvm: exiting hardware virtualization\n");
+ kvm_rebooting = true;
on_each_cpu(hardware_disable, NULL, 0, 1);
}
return NOTIFY_OK;
@@ -1312,14 +1407,15 @@ void kvm_io_bus_destroy(struct kvm_io_bus *bus)
}
}
-struct kvm_io_device *kvm_io_bus_find_dev(struct kvm_io_bus *bus, gpa_t addr)
+struct kvm_io_device *kvm_io_bus_find_dev(struct kvm_io_bus *bus,
+ gpa_t addr, int len, int is_write)
{
int i;
for (i = 0; i < bus->dev_count; i++) {
struct kvm_io_device *pos = bus->devs[i];
- if (pos->in_range(pos, addr))
+ if (pos->in_range(pos, addr, len, is_write))
return pos;
}
diff --git a/virt/kvm/kvm_trace.c b/virt/kvm/kvm_trace.c
index 0e495470788d..58141f31ea8f 100644
--- a/virt/kvm/kvm_trace.c
+++ b/virt/kvm/kvm_trace.c
@@ -72,11 +72,7 @@ static void kvm_add_trace(void *probe_private, void *call_data,
rec.cycle_in = p->cycle_in;
if (rec.cycle_in) {
- u64 cycle = 0;
-
- cycle = get_cycles();
- rec.u.cycle.cycle_lo = (u32)cycle;
- rec.u.cycle.cycle_hi = (u32)(cycle >> 32);
+ rec.u.cycle.cycle_u64 = get_cycles();
for (i = 0; i < rec.extra_u32; i++)
rec.u.cycle.extra_u32[i] = va_arg(*args, u32);
@@ -114,8 +110,18 @@ static int kvm_subbuf_start_callback(struct rchan_buf *buf, void *subbuf,
{
struct kvm_trace *kt;
- if (!relay_buf_full(buf))
+ if (!relay_buf_full(buf)) {
+ if (!prev_subbuf) {
+ /*
+ * executed only once when the channel is opened
+ * save metadata as first record
+ */
+ subbuf_start_reserve(buf, sizeof(u32));
+ *(u32 *)subbuf = 0x12345678;
+ }
+
return 1;
+ }
kt = buf->chan->private_data;
atomic_inc(&kt->lost_records);