summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Keating <jkeating@redhat.com>2010-07-29 16:46:31 -0700
committerJesse Keating <jkeating@redhat.com>2010-07-29 16:46:31 -0700
commit7a32965a104c3363e8505fe566531fcf071cced7 (patch)
treee45cb84552b2b022f49047bbddd9d887753c35bc
parent64ba2e5ffde5f2418eb26c700cb0ab62b04e5013 (diff)
downloadkernel-7a32965a104c3363e8505fe566531fcf071cced7.tar.gz
kernel-7a32965a104c3363e8505fe566531fcf071cced7.tar.xz
kernel-7a32965a104c3363e8505fe566531fcf071cced7.zip
initial srpm import
-rw-r--r--.gitignore3
-rw-r--r--Makefile.config105
-rw-r--r--acpi-ec-add-delay-before-write.patch52
-rw-r--r--config-arm115
-rw-r--r--config-debug91
-rw-r--r--config-generic4293
-rw-r--r--config-i686-PAE5
-rw-r--r--config-ia64-generic206
-rw-r--r--config-nodebug91
-rw-r--r--config-powerpc-generic334
-rw-r--r--config-powerpc32-generic183
-rw-r--r--config-powerpc32-smp4
-rw-r--r--config-powerpc64184
-rw-r--r--config-rhel-generic205
-rw-r--r--config-s390x228
-rw-r--r--config-sparc64-generic203
-rw-r--r--config-x86-generic484
-rw-r--r--config-x86_64-generic409
-rw-r--r--die-floppy-die.patch30
-rw-r--r--disable-i8042-check-on-apple-mac.patch59
-rw-r--r--drm-intel-big-hammer.patch16
-rw-r--r--drm-intel-make-lvds-work.patch19
-rw-r--r--drm-intel-next.patch1
-rw-r--r--drm-nouveau-updates.patch6997
-rw-r--r--drm-revert-drm-fbdev-rework-output-polling-to-be-back-in-core.patch958
-rwxr-xr-xfind-provides44
-rw-r--r--fix_xen_guest_on_old_EC2.patch34
-rw-r--r--genkey7
-rw-r--r--git-linus.diff0
-rw-r--r--git-utrace.patch6326
-rw-r--r--hda_intel-prealloc-4mb-dmabuffer.patch47
-rw-r--r--hdpvr-ir-enable.patch220
-rw-r--r--kernel.spec2195
-rw-r--r--linux-2.6-32bit-mmap-exec-randomization.patch216
-rw-r--r--linux-2.6-acpi-debug-infinite-loop.patch24
-rw-r--r--linux-2.6-acpi-video-dos.patch17
-rw-r--r--linux-2.6-build-nonintconfig.patch128
-rw-r--r--linux-2.6-compile-fixes.patch6
-rw-r--r--linux-2.6-crash-driver.patch385
-rw-r--r--linux-2.6-debug-always-inline-kzalloc.patch25
-rw-r--r--linux-2.6-debug-nmi-timeout.patch45
-rw-r--r--linux-2.6-debug-sizeof-structs.patch31
-rw-r--r--linux-2.6-debug-taint-vm.patch65
-rw-r--r--linux-2.6-debug-vm-would-have-oomkilled.patch64
-rw-r--r--linux-2.6-defaults-acpi-video.patch13
-rw-r--r--linux-2.6-defaults-aspm.patch12
-rw-r--r--linux-2.6-defaults-pci_no_msi.patch110
-rw-r--r--linux-2.6-ext4-fix-freeze-deadlock.patch46
-rw-r--r--linux-2.6-firewire-git-pending.patch4
-rw-r--r--linux-2.6-firewire-git-update.patch3682
-rw-r--r--linux-2.6-hotfixes.patch15
-rw-r--r--linux-2.6-i386-nx-emulation.patch628
-rw-r--r--linux-2.6-input-kill-stupid-messages.patch32
-rw-r--r--linux-2.6-intel-iommu-igfx.patch78
-rw-r--r--linux-2.6-makefile-after_link.patch57
-rw-r--r--linux-2.6-selinux-mprotect-checks.patch124
-rw-r--r--linux-2.6-serial-460800.patch70
-rw-r--r--linux-2.6-silence-acpi-blacklist.patch25
-rw-r--r--linux-2.6-silence-fbcon-logo.patch42
-rw-r--r--linux-2.6-silence-noise.patch89
-rw-r--r--linux-2.6-sparc-selinux-mprotect-checks.patch35
-rw-r--r--linux-2.6-upstream-reverts.patch1
-rw-r--r--linux-2.6-v4l-dvb-add-kworld-a340-support.patch161
-rw-r--r--linux-2.6-v4l-dvb-add-lgdt3304-support.patch350
-rw-r--r--linux-2.6-v4l-dvb-experimental.patch0
-rw-r--r--linux-2.6-v4l-dvb-fixes.patch0
-rw-r--r--linux-2.6-v4l-dvb-ir-core-update.patch6741
-rw-r--r--linux-2.6-v4l-dvb-update.patch0
-rw-r--r--linux-2.6-v4l-dvb-uvcvideo-update.patch362
-rw-r--r--linux-2.6.29-sparc-IOC_TYPECHECK.patch21
-rw-r--r--linux-2.6.30-no-pcspkr-modalias.patch11
-rw-r--r--lirc-staging-2.6.36.patch12190
-rwxr-xr-xmerge.pl66
-rw-r--r--neuter_intel_microcode_load.patch24
-rw-r--r--only-use-alpha2-regulatory-information-from-country-IE.patch788
-rw-r--r--pci-acpi-disable-aspm-if-no-osc.patch53
-rw-r--r--pci-aspm-dont-enable-too-early.patch50
-rw-r--r--prevent-runtime-conntrack-changes.patch74
-rw-r--r--revert-drm-kms-toggle-poll-around-switcheroo.patch65
-rw-r--r--sources3
-rw-r--r--ssb_check_for_sprom.patch155
-rw-r--r--thinkpad-acpi-fix-backlight.patch56
-rw-r--r--utrace-ptrace-fix-build.patch29
-rw-r--r--utrace-remove-use-of-kref_set.patch32
84 files changed, 51448 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index e69de29bb..7ad97c908 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1,3 @@
+linux-2.6.34.tar.bz2
+patch-2.6.35-rc6-git1.bz2
+patch-2.6.35-rc6.bz2
diff --git a/Makefile.config b/Makefile.config
new file mode 100644
index 000000000..53812fa9b
--- /dev/null
+++ b/Makefile.config
@@ -0,0 +1,105 @@
+# Make rules for configuration files.
+#
+# $Id$
+
+CFG = kernel-$(VERSION)
+
+CONFIGFILES = \
+ $(CFG)-i686.config $(CFG)-i686-debug.config \
+ $(CFG)-i686-PAE.config $(CFG)-i686-PAEdebug.config \
+ $(CFG)-x86_64.config $(CFG)-x86_64-debug.config \
+ $(CFG)-s390x.config $(CFG)-arm.config \
+ $(CFG)-ppc.config $(CFG)-ppc-smp.config \
+ $(CFG)-sparc64.config \
+ $(CFG)-ppc64.config $(CFG)-ppc64-debug.config \
+ $(CFG)-ia64.config
+
+PLATFORMS = x86 x86_64 powerpc powerpc32 powerpc64 s390x ia64 sparc64
+TEMPFILES = $(addprefix temp-, $(addsuffix -generic, $(PLATFORMS)))
+
+configs: $(CONFIGFILES)
+ @rm -f kernel-*-config
+ @rm -f $(TEMPFILES)
+ @rm -f temp-generic temp-*-generic temp-*-generic-tmp
+
+# Augment the clean target to clean up our own cruft
+clean ::
+ @rm -fv $(CONFIGFILES) $(TEMPFILES) temp-generic kernel-$(VERSION)*config
+
+temp-generic: config-generic
+ cat config-generic config-nodebug > temp-generic
+
+temp-debug-generic: config-generic
+ cat config-generic config-debug > temp-debug-generic
+
+temp-x86-generic: config-x86-generic temp-generic
+ perl merge.pl $^ > $@
+
+temp-x86-debug-generic: config-x86-generic temp-debug-generic
+ perl merge.pl $^ > $@
+
+temp-x86_64-generic: config-x86_64-generic temp-generic
+ perl merge.pl $^ > $@
+
+temp-x86_64-debug-generic: config-x86_64-generic temp-debug-generic
+ perl merge.pl $^ > $@
+
+temp-sparc64-generic: config-sparc64-generic temp-generic
+ perl merge.pl $^ > $@
+
+temp-powerpc-generic: config-powerpc-generic temp-generic
+ perl merge.pl $^ > $@
+
+temp-powerpc-debug-generic: config-powerpc-generic temp-debug-generic
+ perl merge.pl $^ > $@
+
+temp-powerpc32-generic: config-powerpc32-generic temp-powerpc-generic
+ perl merge.pl $^ > $@
+
+temp-s390-generic: config-s390x temp-generic
+ perl merge.pl $^ > $@
+
+temp-ia64-generic: config-ia64-generic temp-generic
+ perl merge.pl $^ > $@
+
+kernel-$(VERSION)-i686-PAE.config: config-i686-PAE temp-x86-generic
+ perl merge.pl $^ i386 > $@
+
+kernel-$(VERSION)-i686-PAEdebug.config: config-i686-PAE temp-x86-debug-generic
+ perl merge.pl $^ i386 > $@
+
+kernel-$(VERSION)-i686.config: /dev/null temp-x86-generic
+ perl merge.pl $^ i386 > $@
+
+kernel-$(VERSION)-i686-debug.config: /dev/null temp-x86-debug-generic
+ perl merge.pl $^ i386 > $@
+
+kernel-$(VERSION)-x86_64.config: /dev/null temp-x86_64-generic
+ perl merge.pl $^ x86_64 > $@
+
+kernel-$(VERSION)-x86_64-debug.config: /dev/null temp-x86_64-debug-generic
+ perl merge.pl $^ x86_64 > $@
+
+kernel-$(VERSION)-sparc64.config: /dev/null temp-sparc64-generic
+ perl merge.pl $^ sparc64 > $@
+
+kernel-$(VERSION)-ppc64.config: config-powerpc64 temp-powerpc-generic
+ perl merge.pl $^ powerpc > $@
+
+kernel-$(VERSION)-ppc64-debug.config: config-powerpc64 temp-powerpc-debug-generic
+ perl merge.pl $^ powerpc > $@
+
+kernel-$(VERSION)-s390x.config: config-s390x temp-s390-generic
+ perl merge.pl $^ s390 > $@
+
+kernel-$(VERSION)-arm.config: config-arm temp-generic
+ perl merge.pl $^ arm > $@
+
+kernel-$(VERSION)-ppc.config: /dev/null temp-powerpc32-generic
+ perl merge.pl $^ powerpc > $@
+
+kernel-$(VERSION)-ppc-smp.config: config-powerpc32-smp temp-powerpc32-generic
+ perl merge.pl $^ powerpc > $@
+
+kernel-$(VERSION)-ia64.config: /dev/null temp-ia64-generic
+ perl merge.pl $^ ia64 > $@
diff --git a/acpi-ec-add-delay-before-write.patch b/acpi-ec-add-delay-before-write.patch
new file mode 100644
index 000000000..af49cccbd
--- /dev/null
+++ b/acpi-ec-add-delay-before-write.patch
@@ -0,0 +1,52 @@
+https://bugzilla.kernel.org/show_bug.cgi?id=14733#c41
+
+diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
+index 27e0b92..09fbb69 100644
+--- a/drivers/acpi/ec.c
++++ b/drivers/acpi/ec.c
+@@ -226,6 +226,7 @@ static int ec_poll(struct acpi_ec *ec)
+ if (ec_transaction_done(ec))
+ return 0;
+ } else {
++ msleep(1);
+ if (wait_event_timeout(ec->wait,
+ ec_transaction_done(ec),
+ msecs_to_jiffies(1)))
+@@ -233,8 +234,8 @@ static int ec_poll(struct acpi_ec *ec)
+ }
+ advance_transaction(ec, acpi_ec_read_status(ec));
+ } while (time_before(jiffies, delay));
+- if (acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF)
+- break;
++// if (acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF)
++// break;
+ pr_debug(PREFIX "controller reset, restart transaction\n");
+ spin_lock_irqsave(&ec->curr_lock, flags);
+ start_transaction(ec);
+@@ -271,15 +272,25 @@ static int ec_check_ibf0(struct acpi_ec *ec)
+ return (status & ACPI_EC_FLAG_IBF) == 0;
+ }
+
++/* try to clean input buffer with burst_disable transaction */
++static int acpi_ec_clean_buffer(struct acpi_ec *ec)
++{
++ struct transaction t = {.command = ACPI_EC_BURST_DISABLE,
++ .wdata = NULL, .rdata = NULL,
++ .wlen = 0, .rlen = 0};
++ return acpi_ec_transaction_unlocked(ec, &t);
++}
++
+ static int ec_wait_ibf0(struct acpi_ec *ec)
+ {
++
+ unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY);
+ /* interrupt wait manually if GPE mode is not active */
+ while (time_before(jiffies, delay))
+ if (wait_event_timeout(ec->wait, ec_check_ibf0(ec),
+ msecs_to_jiffies(1)))
+ return 0;
+- return -ETIME;
++ return acpi_ec_clean_buffer(ec);
+ }
+
+ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t)
diff --git a/config-arm b/config-arm
new file mode 100644
index 000000000..7b13a8279
--- /dev/null
+++ b/config-arm
@@ -0,0 +1,115 @@
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+# CONFIG_SMP is not set
+
+# CONFIG_CMDLINE_FORCE is not set
+
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+
+CONFIG_ARCH_VERSATILE=y
+CONFIG_ARCH_VERSATILE_PB=y
+CONFIG_MACH_VERSATILE_AB=y
+
+CONFIG_HIGHMEM=y
+# CONFIG_HIGHPTE is not set
+
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
+# CONFIG_CPU_CACHE_ROUND_ROBIN is not set
+
+CONFIG_ZBOOT_ROM_TEXT=0
+CONFIG_ZBOOT_ROM_BSS=0
+
+# CONFIG_XIP_KERNEL is not set
+
+CONFIG_ATAGS_PROC=y
+
+# CONFIG_FPE_NWFPE is not set
+CONFIG_FPE_FASTFPE=y
+CONFIG_VFP=y
+
+CONFIG_PM=y
+# CONFIG_PM_DEBUG is not set
+# CONFIG_PM_TRACE is not set
+CONFIG_SUSPEND=y
+# CONFIG_PM_TEST_SUSPEND is not set
+CONFIG_APM_EMULATION=y
+
+CONFIG_ARM_THUMB=y
+
+CONFIG_AEABI=y
+CONFIG_OABI_COMPAT=y
+
+# CONFIG_UACCESS_WITH_MEMCPY is not set
+
+CONFIG_CMDLINE="console=ttyAM0,115200 root=/dev/sda1 rootdelay=20"
+
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+
+# CONFIG_CPU_IDLE is not set
+
+CONFIG_LEDS=y
+CONFIG_LEDS_CPU=y
+
+CONFIG_MTD_AFS_PARTS=y
+CONFIG_MTD_ARM_INTEGRATOR=y
+CONFIG_MTD_IMPA7=y
+
+CONFIG_AX88796=m
+CONFIG_AX88796_93CX6=y
+CONFIG_SMC91X=m
+CONFIG_DM9000=m
+CONFIG_DM9000_DEBUGLEVEL=4
+# CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL is not set
+CONFIG_SMC911X=m
+CONFIG_SMSC911X=m
+
+CONFIG_SERIO_AMBAKMI=m
+
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+
+CONFIG_I2C_VERSATILE=y
+
+CONFIG_THERMAL=y
+
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+
+CONFIG_FB_ARMCLCD=m
+
+CONFIG_SND_ARM=y
+CONFIG_SND_ARMAACI=m
+
+CONFIG_USB_MUSB_HDRC=m
+# CONFIG_MUSB_PIO_ONLY is not set
+CONFIG_USB_TUSB6010=y
+# CONFIG_USB_MUSB_DEBUG is not set
+
+CONFIG_MMC_ARMMMCI=m
+
+CONFIG_RTC_DRV_PL030=m
+CONFIG_RTC_DRV_PL031=m
+
+# CONFIG_SGI_IOC4 is not set
+
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_ERRORS is not set
+# CONFIG_DEBUG_LL is not set
+
+CONFIG_ARM_UNWIND=y
+
+CONFIG_RCU_FANOUT=32
+
+# CONFIG_USB_ULPI is not set
+# CONFIG_OC_ETM is not set
+
+# CONFIG_MTD_PISMO is not set
+
+CONFIG_PERF_EVENTS=y
+CONFIG_PERF_COUNTERS=y
+
+# CONFIG_MG_DISK is not set
+# CONFIG_GPIO_PL061 is not set
diff --git a/config-debug b/config-debug
new file mode 100644
index 000000000..f8c7c00b1
--- /dev/null
+++ b/config-debug
@@ -0,0 +1,91 @@
+CONFIG_SND_VERBOSE_PRINTK=y
+CONFIG_SND_DEBUG=y
+CONFIG_SND_PCM_XRUN_DEBUG=y
+
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_RT_MUTEXES=y
+CONFIG_DEBUG_LOCK_ALLOC=y
+CONFIG_PROVE_LOCKING=y
+CONFIG_DEBUG_VM=y
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_PROVE_RCU=y
+# CONFIG_PROVE_RCU_REPEATEDLY is not set
+CONFIG_DEBUG_PER_CPU_MAPS=y
+CONFIG_CPUMASK_OFFSTACK=y
+
+CONFIG_CPU_NOTIFIER_ERROR_INJECT=m
+
+CONFIG_FAULT_INJECTION=y
+CONFIG_FAILSLAB=y
+CONFIG_FAIL_PAGE_ALLOC=y
+CONFIG_FAIL_MAKE_REQUEST=y
+CONFIG_FAULT_INJECTION_DEBUG_FS=y
+CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
+CONFIG_FAIL_IO_TIMEOUT=y
+
+CONFIG_SLUB_DEBUG_ON=y
+
+CONFIG_LOCK_STAT=y
+
+CONFIG_DEBUG_STACK_USAGE=y
+
+CONFIG_ACPI_DEBUG=y
+# CONFIG_ACPI_DEBUG_FUNC_TRACE is not set
+
+CONFIG_DEBUG_SG=y
+
+# CONFIG_DEBUG_PAGEALLOC is not set
+
+CONFIG_DEBUG_WRITECOUNT=y
+CONFIG_DEBUG_OBJECTS=y
+# CONFIG_DEBUG_OBJECTS_SELFTEST is not set
+CONFIG_DEBUG_OBJECTS_FREE=y
+CONFIG_DEBUG_OBJECTS_TIMERS=y
+CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT=1
+
+CONFIG_X86_PTDUMP=y
+
+CONFIG_CAN_DEBUG_DEVICES=y
+
+CONFIG_MODULE_FORCE_UNLOAD=y
+
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+
+CONFIG_DEBUG_NOTIFIERS=y
+
+CONFIG_DMA_API_DEBUG=y
+
+CONFIG_MMIOTRACE=y
+
+CONFIG_DEBUG_CREDENTIALS=y
+
+CONFIG_EXT4_DEBUG=y
+
+CONFIG_DEBUG_PERF_USE_VMALLOC=y
+
+# off in both production debug and nodebug builds,
+# on in rawhide nodebug builds
+# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
+
+CONFIG_JBD2_DEBUG=y
+
+CONFIG_DEBUG_CFQ_IOSCHED=y
+
+CONFIG_DRBD_FAULT_INJECTION=y
+
+CONFIG_ATH_DEBUG=y
+CONFIG_IWLWIFI_DEVICE_TRACING=y
+
+CONFIG_DEBUG_OBJECTS_WORK=y
+# CONFIG_DEBUG_STRICT_USER_COPY_CHECKS is not set
+
+CONFIG_DMADEVICES_DEBUG=y
+CONFIG_DMADEVICES_VDEBUG=y
+
+CONFIG_PM_ADVANCED_DEBUG=y
+
+CONFIG_CEPH_FS_PRETTYDEBUG=y
+CONFIG_QUOTA_DEBUG=y
+
+CONFIG_KGDB_KDB=y
+CONFIG_KDB_KEYBOARD=y
diff --git a/config-generic b/config-generic
new file mode 100644
index 000000000..0e1c3cb57
--- /dev/null
+++ b/config-generic
@@ -0,0 +1,4293 @@
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_MMU=y
+CONFIG_SMP=y
+CONFIG_HOTPLUG_CPU=y
+CONFIG_LOCALVERSION=""
+CONFIG_CROSS_COMPILE=""
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_HOTPLUG=y
+CONFIG_UEVENT_HELPER_PATH=""
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+
+CONFIG_BUILD_DOCSRC=y
+
+#
+# General setup
+#
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=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_SYSCTL=y
+CONFIG_LOG_BUF_SHIFT=17
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_KALLSYMS_EXTRA_PASS=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+CONFIG_CFQ_GROUP_IOSCHED=y
+CONFIG_DEFAULT_CFQ=y
+CONFIG_USER_NS=y
+CONFIG_PID_NS=y
+CONFIG_UTS_NS=y
+CONFIG_IPC_NS=y
+CONFIG_NET_NS=y
+
+CONFIG_POSIX_MQUEUE=y
+# CONFIG_PREEMPT_NONE is not set
+CONFIG_PREEMPT_VOLUNTARY=y
+# CONFIG_PREEMPT is not set
+
+CONFIG_SLUB=y
+# CONFIG_SLUB_STATS is not set
+
+CONFIG_MISC_DEVICES=y
+# CONFIG_AD525X_DPOT is not set
+CONFIG_IWMC3200TOP=m
+# CONFIG_IWMC3200TOP_DEBUG is not set
+CONFIG_IWMC3200TOP_DEBUGFS=y
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+# -- MODULE_FORCE_UNLOAD is controlled by config-debug/nodebug
+# CONFIG_MODVERSIONS is not set
+CONFIG_MODULE_SRCVERSION_ALL=y
+
+#
+# Bus options (PCI, PCMCIA, EISA, MCA, ISA)
+#
+CONFIG_PCI=y
+# CONFIG_PCI_DEBUG is not set
+CONFIG_PCI_STUB=y
+CONFIG_PCI_IOV=y
+CONFIG_HT_IRQ=y
+CONFIG_PCI_MSI=y
+CONFIG_PCI_MSI_DEFAULT_ON=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_PCIEAER=y
+CONFIG_PCIEASPM=y
+# CONFIG_PCIEASPM_DEBUG is not set
+CONFIG_PCIE_ECRC=y
+CONFIG_PCIEAER_INJECT=m
+CONFIG_HOTPLUG_PCI_PCIE=y
+CONFIG_HOTPLUG_PCI_FAKE=m
+CONFIG_PCI_LEGACY=y
+
+CONFIG_ISA=y
+# CONFIG_EISA is not set
+# CONFIG_MCA is not set
+# CONFIG_SCx200 is not set
+
+#
+# PCMCIA/CardBus support
+#
+CONFIG_PCMCIA=y
+CONFIG_PCMCIA_LOAD_CIS=y
+# CONFIG_PCMCIA_DEBUG is not set
+CONFIG_YENTA=m
+CONFIG_CARDBUS=y
+CONFIG_I82092=m
+CONFIG_PD6729=m
+CONFIG_PCMCIA_IOCTL=y
+
+CONFIG_PCCARD=y
+CONFIG_MMC=m
+CONFIG_MMC_BLOCK_BOUNCE=y
+CONFIG_SDIO_UART=m
+# CONFIG_MMC_TEST is not set
+# CONFIG_MMC_DEBUG is not set
+# CONFIG_MMC_UNSAFE_RESUME is not set
+CONFIG_MMC_BLOCK=m
+CONFIG_MMC_SDHCI=m
+CONFIG_MMC_SDHCI_PCI=m
+CONFIG_MMC_SDRICOH_CS=m
+CONFIG_MMC_TIFM_SD=m
+CONFIG_MMC_WBSD=m
+CONFIG_MMC_VIA_SDMMC=m
+CONFIG_MMC_SDHCI_PLTFM=m
+CONFIG_MMC_CB710=m
+CONFIG_MMC_RICOH_MMC=y
+
+CONFIG_CB710_CORE=m
+# CONFIG_CB710_DEBUG is not set
+
+CONFIG_INFINIBAND=m
+CONFIG_INFINIBAND_MTHCA=m
+# CONFIG_INFINIBAND_MTHCA_DEBUG is not set
+CONFIG_INFINIBAND_IPOIB=m
+CONFIG_INFINIBAND_IPOIB_DEBUG=y
+CONFIG_INFINIBAND_IPOIB_DEBUG_DATA=y
+CONFIG_INFINIBAND_IPOIB_CM=y
+CONFIG_INFINIBAND_SRP=m
+CONFIG_INFINIBAND_USER_MAD=m
+CONFIG_INFINIBAND_USER_ACCESS=m
+CONFIG_INFINIBAND_IPATH=m
+CONFIG_INFINIBAND_ISER=m
+CONFIG_INFINIBAND_AMSO1100=m
+# CONFIG_INFINIBAND_AMSO1100_DEBUG is not set
+CONFIG_INFINIBAND_CXGB3=m
+CONFIG_INFINIBAND_CXGB4=m
+# CONFIG_INFINIBAND_CXGB3_DEBUG is not set
+CONFIG_MLX4_INFINIBAND=m
+CONFIG_INFINIBAND_NES=m
+# CONFIG_INFINIBAND_NES_DEBUG is not set
+CONFIG_INFINIBAND_QIB=m
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
+# CONFIG_BINFMT_AOUT is not set
+CONFIG_BINFMT_MISC=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_FW_LOADER=y
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_EXTRA_FIRMWARE=""
+
+# CONFIG_SPI is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+CONFIG_MTD=m
+# CONFIG_MTD_DEBUG is not set
+CONFIG_MTD_PARTITIONS=y
+CONFIG_MTD_AR7_PARTS=m
+CONFIG_MTD_CONCAT=m
+CONFIG_MTD_CMDLINE_PARTS=y
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=m
+CONFIG_MTD_BLOCK=m
+CONFIG_MTD_BLOCK_RO=m
+CONFIG_MTD_BLOCK2MTD=m
+
+CONFIG_MTD_OOPS=m
+# CONFIG_MTD_INTEL_VR_NOR is not set
+CONFIG_MTD_ALAUDA=m
+
+CONFIG_FTL=m
+CONFIG_NFTL=m
+CONFIG_NFTL_RW=y
+CONFIG_INFTL=m
+CONFIG_RFD_FTL=m
+CONFIG_SSFDC=m
+# CONFIG_SM_FTL is not set
+
+CONFIG_MTD_UBI=m
+CONFIG_MTD_UBI_WL_THRESHOLD=4096
+CONFIG_MTD_UBI_BEB_RESERVE=1
+# CONFIG_MTD_UBI_GLUEBI is not set
+# CONFIG_MTD_UBI_DEBUG is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=m
+CONFIG_MTD_JEDECPROBE=m
+CONFIG_MTD_GEN_PROBE=m
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_CFI_INTELEXT=m
+CONFIG_MTD_CFI_AMDSTD=m
+CONFIG_MTD_CFI_STAA=m
+CONFIG_MTD_RAM=m
+CONFIG_MTD_ROM=m
+CONFIG_MTD_ABSENT=m
+
+#
+# Mapping drivers for chip access
+#
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+# CONFIG_MTD_PHYSMAP is not set
+CONFIG_MTD_SC520CDP=m
+CONFIG_MTD_NETSC520=m
+# CONFIG_MTD_SBC_GXX is not set
+# CONFIG_MTD_SCx200_DOCFLASH is not set
+# CONFIG_MTD_AMD76XROM is not set
+CONFIG_MTD_SCB2_FLASH=m
+# CONFIG_MTD_NETtel is not set
+# CONFIG_MTD_DILNETPC is not set
+# CONFIG_MTD_L440GX is not set
+CONFIG_MTD_PCI=m
+CONFIG_MTD_TS5500=m
+# CONFIG_MTD_GPIO_ADDR is not set
+# CONFIG_MTD_PCMCIA is not set
+
+#
+# Self-contained MTD device drivers
+#
+CONFIG_MTD_PMC551=m
+# CONFIG_MTD_PMC551_BUGFIX is not set
+# CONFIG_MTD_PMC551_DEBUG is not set
+# CONFIG_MTD_SLRAM is not set
+CONFIG_MTD_MTDRAM=m
+CONFIG_MTDRAM_TOTAL_SIZE=4096
+CONFIG_MTDRAM_ERASE_SIZE=128
+
+#
+# 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_DOCPROBE is not set
+# CONFIG_MTD_DOCPROBE_ADVANCED is not set
+# CONFIG_MTD_DOCPROBE_ADDRESS is not set
+
+#
+# NAND Flash Device Drivers
+#
+CONFIG_MTD_NAND=m
+# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_NAND_VERIFY_WRITE is not set
+# CONFIG_MTD_NAND_CAFE is not set
+CONFIG_MTD_NAND_IDS=m
+CONFIG_MTD_NAND_NANDSIM=m
+# CONFIG_MTD_ONENAND is not set
+CONFIG_MTD_NAND_ECC_SMC=y
+CONFIG_MTD_NAND_CS553X=m
+# CONFIG_MTD_NAND_DENALI is not set
+CONFIG_MTD_NAND_DENALI_SCRATCH_REG_ADDR=0xFF108018
+# CONFIG_MTD_NAND_GPIO is not set
+CONFIG_MTD_NAND_RICOH=m
+
+CONFIG_MTD_REDBOOT_PARTS=m
+# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set
+# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set
+CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1
+# CONFIG_MTD_XIP is not set
+# CONFIG_MTD_ICHXROM is not set
+# CONFIG_MTD_PHRAM is not set
+CONFIG_MTD_NAND_DISKONCHIP=m
+# CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADVANCED is not set
+CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS=0
+# CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE is not set
+# CONFIG_MTD_PLATRAM is not set
+
+# CONFIG_MTD_TESTS is not set
+CONFIG_MTD_LPDDR=m
+CONFIG_MTD_QINFO_PROBE=m
+
+#
+# Parallel port support
+#
+CONFIG_PARPORT=m
+CONFIG_PARPORT_PC=m
+CONFIG_PARPORT_SERIAL=m
+# CONFIG_PARPORT_PC_FIFO is not set
+# CONFIG_PARPORT_PC_SUPERIO is not set
+CONFIG_PARPORT_PC_PCMCIA=m
+CONFIG_PARPORT_1284=y
+# CONFIG_PARPORT_AX88796 is not set
+
+#
+# Plug and Play support
+#
+CONFIG_PNP=y
+# CONFIG_PNP_DEBUG is not set
+# CONFIG_PNP_DEBUG_MESSAGES is not set
+
+#
+# Protocols
+#
+CONFIG_ISAPNP=y
+# CONFIG_PNPBIOS is not set
+
+CONFIG_ACPI_PCI_SLOT=y
+CONFIG_HOTPLUG_PCI_ACPI=y
+CONFIG_HOTPLUG_PCI_ACPI_IBM=m
+
+#
+# Block devices
+#
+CONFIG_BLK_DEV=y
+CONFIG_BLK_DEV_FD=m
+# CONFIG_BLK_DEV_XD is not set
+CONFIG_PARIDE=m
+CONFIG_PARIDE_PD=m
+CONFIG_PARIDE_PCD=m
+CONFIG_PARIDE_PF=m
+CONFIG_PARIDE_PT=m
+CONFIG_PARIDE_PG=m
+CONFIG_PARIDE_ATEN=m
+CONFIG_PARIDE_BPCK=m
+CONFIG_PARIDE_BPCK6=m
+CONFIG_PARIDE_COMM=m
+CONFIG_PARIDE_DSTR=m
+CONFIG_PARIDE_FIT2=m
+CONFIG_PARIDE_FIT3=m
+CONFIG_PARIDE_EPAT=m
+CONFIG_PARIDE_EPATC8=y
+CONFIG_PARIDE_EPIA=m
+CONFIG_PARIDE_FRIQ=m
+CONFIG_PARIDE_FRPW=m
+CONFIG_PARIDE_KBIC=m
+CONFIG_PARIDE_KTTI=m
+CONFIG_PARIDE_ON20=m
+CONFIG_PARIDE_ON26=m
+CONFIG_BLK_CPQ_DA=m
+CONFIG_BLK_CPQ_CISS_DA=m
+CONFIG_CISS_SCSI_TAPE=y
+CONFIG_BLK_DEV_DAC960=m
+CONFIG_BLK_DEV_UMEM=m
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_CRYPTOLOOP=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_OSD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=16384
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_BLK_DEV_ATIIXP=y
+CONFIG_LBD=y
+CONFIG_BLK_DEV_IO_TRACE=y
+
+CONFIG_BLK_DEV_DELKIN=m
+# CONFIG_BLK_DEV_IT8213 is not set
+# CONFIG_BLK_DEV_TC86C001 is not set
+CONFIG_LBDAF=y
+CONFIG_BLK_DEV_BSG=y
+CONFIG_BLK_DEV_INTEGRITY=y
+
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+CONFIG_BLK_DEV_IDECS=m
+CONFIG_BLK_DEV_IDECD=m
+# CONFIG_BLK_DEV_IDETAPE is not set
+CONFIG_IDE_TASK_IOCTL=y
+# CONFIG_BLK_DEV_IDE_SATA is not set
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_BLK_DEV_CMD640=y
+CONFIG_BLK_DEV_CMD640_ENHANCED=y
+CONFIG_BLK_DEV_IDEPNP=y
+CONFIG_BLK_DEV_IDEPCI=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+CONFIG_BLK_DEV_GENERIC=y
+# CONFIG_BLK_DEV_OPTI621 is not set
+CONFIG_BLK_DEV_RZ1000=y
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+CONFIG_BLK_DEV_AEC62XX=y
+CONFIG_BLK_DEV_ALI15X3=y
+# CONFIG_BLK_DEV_AMD74XX is not set
+CONFIG_BLK_DEV_CMD64X=y
+CONFIG_BLK_DEV_TRIFLEX=y
+# CONFIG_BLK_DEV_CY82C693 is not set
+CONFIG_BLK_DEV_CS5520=y
+CONFIG_BLK_DEV_CS5530=y
+CONFIG_BLK_DEV_CS5535=y
+CONFIG_BLK_DEV_HPT366=y
+CONFIG_BLK_DEV_IT821X=y
+CONFIG_BLK_DEV_JMICRON=y
+# CONFIG_BLK_DEV_SC1200 is not set
+CONFIG_BLK_DEV_PIIX=y
+# CONFIG_BLK_DEV_NS87415 is not set
+CONFIG_BLK_DEV_PDC202XX_OLD=y
+CONFIG_BLK_DEV_PDC202XX_NEW=y
+CONFIG_BLK_DEV_SVWKS=y
+CONFIG_BLK_DEV_SIIMAGE=y
+CONFIG_BLK_DEV_SIS5513=y
+# CONFIG_BLK_DEV_SLC90E66 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+CONFIG_BLK_DEV_VIA82CXXX=y
+CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_BLK_DEV_HD is not set
+
+CONFIG_VIRTIO=m
+CONFIG_VIRTIO_BLK=m
+CONFIG_VIRTIO_RING=m
+CONFIG_VIRTIO_PCI=m
+CONFIG_VIRTIO_BALLOON=m
+CONFIG_VIRTIO_NET=m
+CONFIG_VMXNET3=m
+CONFIG_HW_RANDOM_VIRTIO=m
+CONFIG_VIRTIO_CONSOLE=m
+CONFIG_VHOST_NET=m
+
+#
+# SCSI device support
+#
+CONFIG_SCSI=y
+
+CONFIG_SCSI_ENCLOSURE=m
+CONFIG_SCSI_PROC_FS=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_SCSI_SRP=m
+CONFIG_SCSI_SRP_ATTRS=m
+CONFIG_SCSI_TGT=m
+
+CONFIG_SCSI_DH=y
+CONFIG_SCSI_DH_RDAC=m
+CONFIG_SCSI_DH_HP_SW=m
+CONFIG_SCSI_DH_EMC=m
+CONFIG_SCSI_DH_ALUA=m
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_ST=m
+CONFIG_CHR_DEV_OSST=m
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SPI_ATTRS=m
+CONFIG_SCSI_FC_ATTRS=m
+CONFIG_SCSI_FC_TGT_ATTRS=y
+CONFIG_SCSI_ISCSI_ATTRS=m
+CONFIG_SCSI_SAS_ATTRS=m
+CONFIG_SCSI_SRP_TGT_ATTRS=y
+CONFIG_SCSI_SAS_LIBSAS=m
+CONFIG_SCSI_SAS_ATA=y
+# CONFIG_SCSI_SAS_LIBSAS_DEBUG is not set
+CONFIG_SCSI_SAS_HOST_SMP=y
+CONFIG_RAID_ATTRS=m
+
+CONFIG_ISCSI_TCP=m
+
+#
+# SCSI low-level drivers
+#
+CONFIG_SCSI_LOWLEVEL=y
+CONFIG_BLK_DEV_3W_XXXX_RAID=m
+CONFIG_SCSI_3W_9XXX=m
+# CONFIG_SCSI_7000FASST is not set
+CONFIG_SCSI_ACARD=m
+CONFIG_SCSI_AACRAID=m
+CONFIG_SCSI_AIC7XXX=m
+CONFIG_SCSI_AIC94XX=m
+# CONFIG_AIC94XX_DEBUG is not set
+CONFIG_AIC7XXX_CMDS_PER_DEVICE=4
+CONFIG_AIC7XXX_RESET_DELAY_MS=15000
+# CONFIG_AIC7XXX_BUILD_FIRMWARE is not set
+# CONFIG_AIC7XXX_DEBUG_ENABLE is not set
+CONFIG_AIC7XXX_DEBUG_MASK=0
+# CONFIG_AIC7XXX_REG_PRETTY_PRINT is not set
+CONFIG_SCSI_AIC7XXX_OLD=m
+CONFIG_SCSI_AIC79XX=m
+CONFIG_AIC79XX_CMDS_PER_DEVICE=4
+CONFIG_AIC79XX_RESET_DELAY_MS=15000
+# CONFIG_AIC79XX_BUILD_FIRMWARE is not set
+# CONFIG_AIC79XX_DEBUG_ENABLE is not set
+CONFIG_AIC79XX_DEBUG_MASK=0
+# CONFIG_AIC79XX_REG_PRETTY_PRINT is not set
+# CONFIG_SCSI_ADVANSYS is not set
+CONFIG_SCSI_BFA_FC=m
+CONFIG_MEGARAID_NEWGEN=y
+CONFIG_MEGARAID_MM=m
+CONFIG_MEGARAID_MAILBOX=m
+CONFIG_MEGARAID_LEGACY=m
+CONFIG_MEGARAID_SAS=m
+CONFIG_SCSI_MVSAS=m
+# CONFIG_SCSI_MVSAS_DEBUG is not set
+CONFIG_SCSI_MPT2SAS=m
+CONFIG_SCSI_MPT2SAS_MAX_SGE=128
+CONFIG_SCSI_MPT2SAS_LOGGING=y
+
+CONFIG_SCSI_OSD_INITIATOR=m
+CONFIG_SCSI_OSD_ULD=m
+CONFIG_SCSI_OSD_DPRINT_SENSE=1
+# CONFIG_SCSI_OSD_DEBUG is not set
+
+CONFIG_SCSI_BNX2_ISCSI=m
+CONFIG_BE2ISCSI=m
+CONFIG_SCSI_PMCRAID=m
+
+CONFIG_SCSI_HPSA=m
+CONFIG_SCSI_3W_SAS=m
+CONFIG_SCSI_PM8001=m
+CONFIG_VMWARE_PVSCSI=m
+CONFIG_VMWARE_BALLOON=m
+
+CONFIG_ATA=y
+CONFIG_ATA_BMDMA=y
+CONFIG_ATA_VERBOSE_ERROR=y
+CONFIG_ATA_SFF=y
+CONFIG_ATA_PIIX=y
+CONFIG_ATA_ACPI=y
+CONFIG_BLK_DEV_SX8=m
+CONFIG_PDC_ADMA=m
+CONFIG_SATA_AHCI=y
+CONFIG_SATA_AHCI_PLATFORM=m
+CONFIG_SATA_INIC162X=m
+CONFIG_SATA_MV=m
+CONFIG_SATA_NV=m
+CONFIG_SATA_PMP=y
+CONFIG_SATA_PROMISE=m
+CONFIG_SATA_QSTOR=m
+CONFIG_SATA_SIL=m
+CONFIG_SATA_SIL24=m
+CONFIG_SATA_SIS=m
+CONFIG_SATA_SVW=m
+CONFIG_SATA_SX4=m
+CONFIG_SATA_ULI=m
+CONFIG_SATA_VIA=m
+CONFIG_SATA_VITESSE=m
+
+CONFIG_PATA_ACPI=m
+CONFIG_PATA_ALI=m
+CONFIG_PATA_AMD=m
+CONFIG_PATA_ARTOP=m
+CONFIG_PATA_ATIIXP=m
+CONFIG_PATA_CMD640_PCI=m
+CONFIG_PATA_CMD64X=m
+CONFIG_PATA_CS5520=m
+CONFIG_PATA_CS5530=m
+CONFIG_PATA_CS5535=m
+CONFIG_PATA_CS5536=m
+CONFIG_PATA_CYPRESS=m
+CONFIG_PATA_EFAR=m
+CONFIG_ATA_GENERIC=m
+CONFIG_PATA_HPT366=m
+CONFIG_PATA_HPT37X=m
+CONFIG_PATA_HPT3X2N=m
+CONFIG_PATA_HPT3X3=m
+# CONFIG_PATA_HPT3X3_DMA is not set
+# CONFIG_PATA_ISAPNP is not set
+CONFIG_PATA_IT821X=m
+CONFIG_PATA_IT8213=m
+CONFIG_PATA_JMICRON=m
+# CONFIG_PATA_LEGACY is not set
+CONFIG_PATA_NINJA32=m
+CONFIG_PATA_MARVELL=m
+# CONFIG_PATA_WINBOND_VLB is not set
+CONFIG_PATA_MPIIX=m
+CONFIG_PATA_NETCELL=m
+CONFIG_PATA_NS87410=m
+CONFIG_PATA_NS87415=m
+CONFIG_PATA_OLDPIIX=m
+CONFIG_PATA_OPTI=m
+CONFIG_PATA_OPTIDMA=m
+CONFIG_PATA_PCMCIA=m
+CONFIG_PATA_PDC_OLD=m
+CONFIG_PATA_QDI=m
+# CONFIG_PATA_RADISYS is not set
+CONFIG_PATA_RDC=m
+# CONFIG_PATA_RZ1000 is not set
+# CONFIG_PATA_SC1200 is not set
+CONFIG_PATA_SERVERWORKS=m
+CONFIG_PATA_PDC2027X=m
+CONFIG_PATA_SCH=m
+CONFIG_PATA_SIL680=m
+CONFIG_PATA_SIS=m
+CONFIG_PATA_TOSHIBA=m
+CONFIG_PATA_TRIFLEX=m
+CONFIG_PATA_VIA=m
+CONFIG_PATA_WINBOND=m
+CONFIG_PATA_ATP867X=m
+
+CONFIG_SCSI_BUSLOGIC=m
+CONFIG_SCSI_INITIO=m
+CONFIG_SCSI_FLASHPOINT=y
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+CONFIG_SCSI_GDTH=m
+CONFIG_SCSI_HPTIOP=m
+CONFIG_SCSI_IPS=m
+CONFIG_SCSI_INIA100=m
+CONFIG_SCSI_PPA=m
+CONFIG_SCSI_IMM=m
+# CONFIG_SCSI_IZIP_EPP16 is not set
+# CONFIG_SCSI_IZIP_SLOW_CTR is not set
+CONFIG_SCSI_STEX=m
+CONFIG_SCSI_SYM53C8XX_2=m
+CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1
+CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
+CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
+CONFIG_SCSI_SYM53C8XX_MMIO=y
+CONFIG_SCSI_QLOGIC_1280=m
+CONFIG_SCSI_DC395x=m
+# CONFIG_SCSI_NSP32 is not set
+CONFIG_SCSI_DEBUG=m
+CONFIG_SCSI_DC390T=m
+CONFIG_SCSI_QLA_FC=m
+CONFIG_SCSI_QLA_ISCSI=m
+# CONFIG_SCSI_IPR is not set
+# CONFIG_SCSI_DPT_I2O is not set
+CONFIG_SCSI_LPFC=m
+
+#
+# PCMCIA SCSI adapter support
+#
+CONFIG_SCSI_LOWLEVEL_PCMCIA=y
+CONFIG_PCMCIA_AHA152X=m
+# CONFIG_PCMCIA_FDOMAIN is not set
+CONFIG_PCMCIA_NINJA_SCSI=m
+CONFIG_PCMCIA_QLOGIC=m
+CONFIG_PCMCIA_SYM53C500=m
+
+
+#
+# Multi-device support (RAID and LVM)
+#
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=y
+CONFIG_MD_AUTODETECT=y
+CONFIG_MD_FAULTY=m
+CONFIG_MD_LINEAR=m
+CONFIG_MD_MULTIPATH=m
+CONFIG_MD_RAID0=m
+CONFIG_MD_RAID1=m
+CONFIG_MD_RAID10=m
+CONFIG_MD_RAID456=m
+CONFIG_MULTICORE_RAID456=y
+CONFIG_ASYNC_RAID6_TEST=m
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=m
+CONFIG_DM_DEBUG=y
+# CONFIG_DM_DELAY is not set
+CONFIG_DM_MIRROR=y
+CONFIG_DM_MULTIPATH=m
+CONFIG_DM_SNAPSHOT=y
+CONFIG_DM_UEVENT=y
+CONFIG_DM_ZERO=y
+CONFIG_DM_LOG_USERSPACE=m
+CONFIG_DM_MULTIPATH_QL=m
+CONFIG_DM_MULTIPATH_ST=m
+
+#
+# Fusion MPT device support
+#
+CONFIG_FUSION=y
+CONFIG_FUSION_SPI=m
+CONFIG_FUSION_FC=m
+CONFIG_FUSION_MAX_SGE=40
+CONFIG_FUSION_CTL=m
+CONFIG_FUSION_LAN=m
+CONFIG_FUSION_SAS=m
+CONFIG_FUSION_LOGGING=y
+
+#
+# IEEE 1394 (FireWire) support (JUJU alternative stack)
+#
+CONFIG_FIREWIRE=m
+CONFIG_FIREWIRE_OHCI=m
+CONFIG_FIREWIRE_SBP2=m
+CONFIG_FIREWIRE_NET=m
+CONFIG_FIREWIRE_OHCI_DEBUG=y
+# CONFIG_FIREWIRE_OHCI_REMOTE_DMA is not set
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_IEEE1394 is not set
+
+#
+# I2O device support
+#
+# CONFIG_I2O is not set
+# CONFIG_I2O_LCT_NOTIFY_ON_CHANGES is not set
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_BIC=m
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_TCP_CONG_HTCP=m
+CONFIG_TCP_CONG_HSTCP=m
+CONFIG_TCP_CONG_HYBLA=m
+CONFIG_TCP_CONG_ILLINOIS=m
+CONFIG_TCP_CONG_LP=m
+CONFIG_TCP_CONG_SCALABLE=m
+CONFIG_TCP_CONG_VEGAS=m
+CONFIG_TCP_CONG_VENO=m
+CONFIG_TCP_CONG_WESTWOOD=m
+CONFIG_TCP_CONG_YEAH=m
+
+CONFIG_TCP_MD5SIG=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+CONFIG_PACKET_MMAP=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=m
+CONFIG_NET_KEY_MIGRATE=y
+CONFIG_INET=y
+CONFIG_INET_LRO=y
+CONFIG_INET_TUNNEL=m
+CONFIG_INET_DIAG=m
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_NF_SECURITY=m
+# CONFIG_IP_PNP is not set
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE=m
+CONFIG_NET_IPGRE_BROADCAST=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_ARPD=y
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_NETCONSOLE=m
+CONFIG_NETCONSOLE_DYNAMIC=y
+CONFIG_NETPOLL_TRAP=y
+CONFIG_NET_POLL_CONTROLLER=y
+
+#
+# IP: Virtual Server Configuration
+#
+CONFIG_IP_VS=m
+# CONFIG_IP_VS_DEBUG is not set
+CONFIG_IP_VS_TAB_BITS=12
+CONFIG_IP_VS_PROTO_TCP=y
+CONFIG_IP_VS_PROTO_UDP=y
+CONFIG_IP_VS_PROTO_ESP=y
+CONFIG_IP_VS_PROTO_AH=y
+CONFIG_IP_VS_PROTO_SCTP=y
+CONFIG_IP_VS_RR=m
+CONFIG_IP_VS_WRR=m
+CONFIG_IP_VS_LC=m
+CONFIG_IP_VS_WLC=m
+CONFIG_IP_VS_LBLC=m
+CONFIG_IP_VS_LBLCR=m
+CONFIG_IP_VS_DH=m
+CONFIG_IP_VS_SH=m
+CONFIG_IP_VS_SED=m
+CONFIG_IP_VS_NQ=m
+CONFIG_IP_VS_FTP=m
+
+CONFIG_IPV6=m
+CONFIG_IPV6_PRIVACY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_SIT=m
+CONFIG_IPV6_SIT_6RD=y
+CONFIG_IPV6_TUNNEL=m
+CONFIG_IPV6_SUBTREES=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_MROUTE=y
+CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y
+CONFIG_IPV6_PIMSM_V2=y
+
+CONFIG_RDS=m
+# CONFIG_RDS_DEBUG is not set
+CONFIG_RDS_RDMA=m
+CONFIG_RDS_TCP=m
+
+CONFIG_NET_9P=m
+CONFIG_NET_9P_VIRTIO=m
+# CONFIG_NET_9P_DEBUG is not set
+CONFIG_NET_9P_RDMA=m
+
+CONFIG_DECNET=m
+CONFIG_DECNET_ROUTER=y
+# CONFIG_DECNET_NF_GRABULATOR is not set
+CONFIG_BRIDGE=m
+CONFIG_BRIDGE_IGMP_SNOOPING=y
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_ADVANCED=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NETFILTER_NETLINK=m
+CONFIG_NETFILTER_NETLINK_QUEUE=m
+CONFIG_NETFILTER_NETLINK_LOG=m
+CONFIG_NETFILTER_XTABLES=y
+CONFIG_NETFILTER_XT_MARK=m
+CONFIG_NETFILTER_XT_CONNMARK=m
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=m
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=m
+CONFIG_NETFILTER_XT_TARGET_DSCP=m
+CONFIG_NETFILTER_XT_TARGET_MARK=m
+CONFIG_NETFILTER_XT_TARGET_NFLOG=m
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
+CONFIG_NETFILTER_XT_TARGET_NOTRACK=m
+CONFIG_NETFILTER_XT_TARGET_RATEEST=m
+CONFIG_NETFILTER_XT_TARGET_SECMARK=m
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
+CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m
+CONFIG_NETFILTER_XT_TARGET_TRACE=m
+CONFIG_NETFILTER_XT_TARGET_TEE=m
+CONFIG_NETFILTER_XT_TARGET_LED=m
+CONFIG_NETFILTER_XT_TARGET_CT=m
+CONFIG_NETFILTER_XT_MATCH_COMMENT=m
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_DCCP=m
+CONFIG_NETFILTER_XT_MATCH_DSCP=m
+CONFIG_NETFILTER_XT_MATCH_ESP=m
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_HELPER=m
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=m
+CONFIG_NETFILTER_XT_MATCH_LENGTH=m
+CONFIG_NETFILTER_XT_MATCH_LIMIT=m
+CONFIG_NETFILTER_XT_MATCH_MAC=m
+CONFIG_NETFILTER_XT_MATCH_MARK=m
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
+CONFIG_NETFILTER_XT_MATCH_OWNER=m
+CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
+CONFIG_NETFILTER_XT_MATCH_POLICY=m
+CONFIG_NETFILTER_XT_MATCH_QUOTA=m
+CONFIG_NETFILTER_XT_MATCH_RATEEST=m
+CONFIG_NETFILTER_XT_MATCH_REALM=m
+CONFIG_NETFILTER_XT_MATCH_SCTP=m
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
+CONFIG_NETFILTER_XT_MATCH_STRING=m
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_TIME=m
+CONFIG_NETFILTER_XT_MATCH_U32=m
+CONFIG_NETFILTER_XT_MATCH_CLUSTER=m
+CONFIG_NETFILTER_XT_MATCH_HL=m
+CONFIG_NETFILTER_XT_MATCH_OSF=m
+
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_BRIDGE_NETFILTER=y
+
+#
+# IP: Netfilter Configuration
+#
+
+CONFIG_NF_CT_ACCT=y
+CONFIG_NF_CONNTRACK_MARK=y
+CONFIG_NF_CONNTRACK_SECMARK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CONNTRACK_ZONES=y
+# CONFIG_NF_CONNTRACK_PROC_COMPAT is not set
+CONFIG_NF_CONNTRACK_AMANDA=m
+CONFIG_NF_CONNTRACK_FTP=m
+CONFIG_NF_CONNTRACK_H323=m
+CONFIG_NF_CONNTRACK_IRC=m
+CONFIG_NF_CONNTRACK_NETBIOS_NS=m
+CONFIG_NF_CONNTRACK_PPTP=m
+CONFIG_NF_CONNTRACK_SANE=m
+CONFIG_NF_CONNTRACK_SIP=m
+CONFIG_NF_CONNTRACK_TFTP=m
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_NF_NAT=m
+CONFIG_NF_NAT_SNMP_BASIC=m
+CONFIG_NF_CT_PROTO_DCCP=m
+CONFIG_NF_CT_PROTO_SCTP=m
+CONFIG_NF_CT_NETLINK=m
+CONFIG_NF_CT_PROTO_UDPLITE=m
+
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_TARGET_CLUSTERIP=m
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_IP_NF_TARGET_NETMAP=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_RAW=m
+
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_FILTER=y
+
+#
+# IPv6: Netfilter Configuration
+#
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_MATCH_AH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_MH=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_QUEUE=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_IP6_NF_SECURITY=m
+CONFIG_IP6_NF_TARGET_LOG=m
+CONFIG_IP6_NF_TARGET_REJECT=m
+CONFIG_IP6_NF_TARGET_HL=m
+
+#
+# Bridge: Netfilter Configuration
+#
+CONFIG_BRIDGE_NF_EBTABLES=m
+CONFIG_BRIDGE_EBT_802_3=m
+CONFIG_BRIDGE_EBT_AMONG=m
+CONFIG_BRIDGE_EBT_ARP=m
+CONFIG_BRIDGE_EBT_ARPREPLY=m
+CONFIG_BRIDGE_EBT_BROUTE=m
+CONFIG_BRIDGE_EBT_DNAT=m
+CONFIG_BRIDGE_EBT_IP=m
+CONFIG_BRIDGE_EBT_IP6=m
+CONFIG_BRIDGE_EBT_LIMIT=m
+CONFIG_BRIDGE_EBT_LOG=m
+CONFIG_BRIDGE_EBT_MARK=m
+CONFIG_BRIDGE_EBT_MARK_T=m
+CONFIG_BRIDGE_EBT_NFLOG=m
+CONFIG_BRIDGE_EBT_PKTTYPE=m
+CONFIG_BRIDGE_EBT_REDIRECT=m
+CONFIG_BRIDGE_EBT_SNAT=m
+CONFIG_BRIDGE_EBT_STP=m
+CONFIG_BRIDGE_EBT_T_FILTER=m
+CONFIG_BRIDGE_EBT_T_NAT=m
+CONFIG_BRIDGE_EBT_ULOG=m
+CONFIG_BRIDGE_EBT_VLAN=m
+CONFIG_XFRM=y
+CONFIG_XFRM_MIGRATE=y
+CONFIG_XFRM_SUB_POLICY=y
+CONFIG_XFRM_STATISTICS=y
+CONFIG_XFRM_USER=y
+CONFIG_INET_XFRM_MODE_TRANSPORT=m
+CONFIG_INET_XFRM_MODE_TUNNEL=m
+CONFIG_INET_XFRM_MODE_BEET=m
+CONFIG_INET6_XFRM_MODE_TRANSPORT=m
+CONFIG_INET6_XFRM_MODE_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=m
+CONFIG_INET6_XFRM_MODE_BEET=m
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+CONFIG_IP_SCTP=m
+CONFIG_NET_SCTPPROBE=m
+# CONFIG_SCTP_DBG_MSG is not set
+# CONFIG_SCTP_DBG_OBJCNT is not set
+# CONFIG_SCTP_HMAC_NONE is not set
+CONFIG_SCTP_HMAC_SHA1=y
+# CONFIG_SCTP_HMAC_MD5 is not set
+CONFIG_ATM=m
+CONFIG_VLAN_8021Q=m
+CONFIG_VLAN_8021Q_GVRP=y
+CONFIG_LLC=m
+# CONFIG_LLC2 is not set
+CONFIG_IPX=m
+# CONFIG_IPX_INTERN is not set
+CONFIG_ATALK=m
+CONFIG_DEV_APPLETALK=y
+CONFIG_IPDDP=m
+CONFIG_IPDDP_ENCAP=y
+CONFIG_IPDDP_DECAP=y
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+CONFIG_WAN_ROUTER=m
+CONFIG_IP_DCCP=m
+CONFIG_IP_DCCP_CCID2=m
+# CONFIG_IP_DCCP_CCID2_DEBUG is not set
+CONFIG_IP_DCCP_CCID3=y
+# CONFIG_IP_DCCP_CCID3_DEBUG is not set
+CONFIG_IP_DCCP_CCID3_RTO=100
+# CONFIG_IP_DCCP_DEBUG is not set
+CONFIG_NET_DCCPPROBE=m
+
+#
+# TIPC Configuration (EXPERIMENTAL)
+#
+# CONFIG_TIPC is not set
+# CONFIG_TIPC_ADVANCED is not set
+# CONFIG_TIPC_DEBUG is not set
+
+CONFIG_NETLABEL=y
+
+#
+# QoS and/or fair queueing
+#
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_CBQ=m
+CONFIG_NET_SCH_DSMARK=m
+CONFIG_NET_SCH_DRR=m
+CONFIG_NET_SCH_GRED=m
+CONFIG_NET_SCH_HFSC=m
+CONFIG_NET_SCH_HTB=m
+CONFIG_NET_SCH_INGRESS=m
+CONFIG_NET_SCH_NETEM=m
+CONFIG_NET_SCH_PRIO=m
+CONFIG_NET_SCH_RED=m
+CONFIG_NET_SCH_SFQ=m
+CONFIG_NET_SCH_TBF=m
+CONFIG_NET_SCH_TEQL=m
+CONFIG_NET_CLS=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_CLS_BASIC=m
+CONFIG_NET_CLS_CGROUP=y
+CONFIG_NET_CLS_FLOW=m
+CONFIG_NET_CLS_FW=m
+CONFIG_NET_CLS_IND=y
+CONFIG_NET_CLS_ROUTE4=m
+CONFIG_NET_CLS_ROUTE=y
+CONFIG_NET_CLS_RSVP=m
+CONFIG_NET_CLS_RSVP6=m
+CONFIG_NET_CLS_TCINDEX=m
+CONFIG_NET_CLS_U32=m
+CONFIG_CLS_U32_MARK=y
+CONFIG_CLS_U32_PERF=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_CMP=m
+CONFIG_NET_EMATCH_META=m
+CONFIG_NET_EMATCH_NBYTE=m
+CONFIG_NET_EMATCH_STACK=32
+CONFIG_NET_EMATCH_TEXT=m
+CONFIG_NET_EMATCH_U32=m
+
+CONFIG_NET_ACT_GACT=m
+CONFIG_GACT_PROB=y
+CONFIG_NET_ACT_IPT=m
+CONFIG_NET_ACT_MIRRED=m
+CONFIG_NET_ACT_NAT=m
+CONFIG_NET_ACT_PEDIT=m
+CONFIG_NET_ACT_POLICE=m
+CONFIG_NET_ACT_SIMP=m
+
+CONFIG_DCB=y
+
+#
+# Network testing
+#
+CONFIG_NET_PKTGEN=m
+# CONFIG_NET_TCPPROBE is not set
+CONFIG_NET_DROP_MONITOR=y
+CONFIG_NETDEVICES=y
+
+# disable later --kyle
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+CONFIG_IFB=m
+CONFIG_DUMMY=m
+CONFIG_BONDING=m
+CONFIG_MACVLAN=m
+CONFIG_MACVTAP=m
+CONFIG_EQUALIZER=m
+CONFIG_TUN=m
+CONFIG_VETH=m
+CONFIG_NET_SB1000=m
+
+#
+# ATM
+#
+CONFIG_ATM_DRIVERS=y
+# CONFIG_ATM_DUMMY is not set
+CONFIG_ATM_CLIP=m
+CONFIG_ATM_LANE=m
+CONFIG_ATM_BR2684=m
+CONFIG_NET_SCH_ATM=m
+CONFIG_ATM_TCP=m
+# CONFIG_ATM_LANAI is not set
+CONFIG_ATM_ENI=m
+CONFIG_ATM_FIRESTREAM=m
+# CONFIG_ATM_ZATM is not set
+# CONFIG_ATM_IDT77252 is not set
+# CONFIG_ATM_AMBASSADOR is not set
+# CONFIG_ATM_HORIZON is not set
+# CONFIG_ATM_FORE200E is not set
+# CONFIG_ATM_FORE200E_USE_TASKLET is not set
+CONFIG_ATM_FORE200E_TX_RETRY=16
+CONFIG_ATM_FORE200E_DEBUG=0
+
+CONFIG_ATM_HE=m
+CONFIG_PPPOATM=m
+CONFIG_PPPOL2TP=m
+CONFIG_ATM_NICSTAR=m
+# CONFIG_ATM_IA is not set
+# CONFIG_ATM_CLIP_NO_ICMP is not set
+# CONFIG_ATM_MPOA is not set
+# CONFIG_ATM_BR2684_IPFILTER is not set
+# CONFIG_ATM_ENI_DEBUG is not set
+# CONFIG_ATM_ENI_TUNE_BURST is not set
+# CONFIG_ATM_ZATM_DEBUG is not set
+# CONFIG_ATM_IDT77252_DEBUG is not set
+# CONFIG_ATM_IDT77252_RCV_ALL is not set
+# CONFIG_ATM_AMBASSADOR_DEBUG is not set
+# CONFIG_ATM_HORIZON_DEBUG is not set
+# CONFIG_ATM_HE_USE_SUNI is not set
+# CONFIG_ATM_NICSTAR_USE_SUNI is not set
+# CONFIG_ATM_NICSTAR_USE_IDT77105 is not set
+# CONFIG_ATM_IA_DEBUG is not set
+CONFIG_ATM_SOLOS=m
+
+CONFIG_L2TP=m
+CONFIG_L2TP_DEBUGFS=m
+CONFIG_L2TP_V3=y
+CONFIG_L2TP_IP=m
+CONFIG_L2TP_ETH=m
+
+# CONFIG_CAIF is not set
+
+CONFIG_RFKILL=m
+CONFIG_RFKILL_INPUT=y
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NETDEV_1000=y
+CONFIG_NETDEV_10000=y
+CONFIG_NET_ETHERNET=y
+CONFIG_PHYLIB=m
+CONFIG_BROADCOM_PHY=m
+CONFIG_CICADA_PHY=m
+CONFIG_DAVICOM_PHY=m
+CONFIG_FIXED_PHY=y
+CONFIG_MDIO_BITBANG=m
+CONFIG_NATIONAL_PHY=m
+CONFIG_ICPLUS_PHY=m
+CONFIG_LSI_ET1011C_PHY=m
+CONFIG_LXT_PHY=m
+CONFIG_MARVELL_PHY=m
+CONFIG_QSEMI_PHY=m
+CONFIG_REALTEK_PHY=m
+CONFIG_SMSC_PHY=m
+CONFIG_STE10XP=m
+CONFIG_VITESSE_PHY=m
+CONFIG_MICREL_PHY=m
+
+CONFIG_MII=m
+CONFIG_HAPPYMEAL=m
+CONFIG_SUNGEM=m
+CONFIG_NET_VENDOR_3COM=y
+CONFIG_VORTEX=m
+CONFIG_TYPHOON=m
+CONFIG_DNET=m
+
+#
+# Tulip family network device support
+#
+CONFIG_NET_TULIP=y
+CONFIG_DE2104X=m
+CONFIG_DE2104X_DSL=0
+CONFIG_TULIP=m
+# CONFIG_TULIP_NAPI is not set
+# CONFIG_TULIP_MWI is not set
+CONFIG_TULIP_MMIO=y
+# CONFIG_NI5010 is not set
+CONFIG_DE4X5=m
+CONFIG_WINBOND_840=m
+CONFIG_DM9102=m
+CONFIG_PCMCIA_XIRCOM=m
+CONFIG_ULI526X=m
+# CONFIG_HP100 is not set
+CONFIG_LNE390=m
+CONFIG_NE3210=m
+CONFIG_ES3210=m
+CONFIG_NET_PCI=y
+CONFIG_PCNET32=m
+CONFIG_AMD8111_ETH=m
+CONFIG_ADAPTEC_STARFIRE=m
+CONFIG_KSZ884X_PCI=m
+CONFIG_B44=m
+CONFIG_B44_PCI=y
+CONFIG_BNX2=m
+CONFIG_CNIC=m
+CONFIG_QLA3XXX=m
+CONFIG_ATL1=m
+CONFIG_ATL1C=m
+CONFIG_ATL2=m
+CONFIG_ATL1E=m
+CONFIG_E100=m
+CONFIG_FEALNX=m
+CONFIG_FORCEDETH=m
+CONFIG_FORCEDETH_NAPI=y
+CONFIG_NATSEMI=m
+CONFIG_NE2K_PCI=m
+CONFIG_8139CP=m
+CONFIG_8139TOO=m
+# CONFIG_8139TOO_PIO is not set
+# CONFIG_8139TOO_TUNE_TWISTER is not set
+CONFIG_8139TOO_8129=y
+# CONFIG_8139_OLD_RX_RESET is not set
+CONFIG_SIS900=m
+CONFIG_SIS190=m
+CONFIG_EPIC100=m
+CONFIG_SC92031=m
+CONFIG_SMSC9420=m
+CONFIG_SUNDANCE=m
+# CONFIG_SUNDANCE_MMIO is not set
+CONFIG_TLAN=m
+CONFIG_VIA_RHINE=m
+CONFIG_VIA_RHINE_MMIO=y
+CONFIG_VIA_VELOCITY=m
+CONFIG_NET_POCKET=y
+CONFIG_ATP=m
+CONFIG_DE600=m
+CONFIG_DE620=m
+CONFIG_CASSINI=m
+CONFIG_ETHOC=m
+# CONFIG_KS8842 is not set
+# CONFIG_KS8851_MLL is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+CONFIG_ACENIC=m
+# CONFIG_ACENIC_OMIT_TIGON_I is not set
+CONFIG_DL2K=m
+CONFIG_E1000=m
+CONFIG_E1000E=m
+CONFIG_IGB=m
+CONFIG_IGB_DCA=y
+CONFIG_IGBVF=m
+CONFIG_NS83820=m
+CONFIG_HAMACHI=m
+CONFIG_YELLOWFIN=m
+CONFIG_R8169=m
+CONFIG_R8169_VLAN=y
+CONFIG_SKGE=m
+# CONFIG_SKGE_DEBUG is not set
+CONFIG_TIGON3=m
+CONFIG_SKY2=m
+# CONFIG_SKY2_DEBUG is not set
+CONFIG_JME=m
+
+#
+# Ethernet (10000 Mbit)
+#
+CONFIG_CHELSIO_T1=m
+CONFIG_CHELSIO_T1_1G=y
+CONFIG_CHELSIO_T3=m
+CONFIG_CHELSIO_T4=m
+CONFIG_IP1000=m
+CONFIG_IXGB=m
+CONFIG_IXGBEVF=m
+CONFIG_IXGBE=m
+CONFIG_IXGBE_DCA=y
+CONFIG_IXGBE_DCB=y
+CONFIG_MYRI10GE=m
+CONFIG_MYRI10GE_DCA=y
+CONFIG_NETXEN_NIC=m
+CONFIG_NIU=m
+CONFIG_S2IO=m
+CONFIG_VXGE=m
+# CONFIG_VXGE_DEBUG_TRACE_ALL is not set
+CONFIG_TEHUTI=m
+CONFIG_ENIC=m
+CONFIG_MLX4_EN=m
+# CONFIG_MLX4_DEBUG is not set
+CONFIG_QLCNIC=m
+CONFIG_QLGE=m
+CONFIG_SFC=m
+CONFIG_SFC_MTD=y
+CONFIG_BE2NET=m
+
+CONFIG_FDDI=y
+# CONFIG_DEFXX is not set
+CONFIG_SKFP=m
+# CONFIG_HIPPI is not set
+CONFIG_PLIP=m
+CONFIG_PPP=m
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_IPPP_FILTER=y
+# CONFIG_PPP_BSDCOMP is not set
+CONFIG_PPPOE=m
+CONFIG_PPP_MPPE=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+# CONFIG_SLIP_MODE_SLIP6 is not set
+
+#
+# Wireless LAN
+#
+#
+CONFIG_WLAN=y
+# CONFIG_STRIP is not set
+# CONFIG_ARLAN is not set
+CONFIG_PCMCIA_WAVELAN=m
+CONFIG_PCMCIA_NETWAVE=m
+# CONFIG_PCMCIA_RAYCS is not set
+
+CONFIG_WIRELESS=y
+CONFIG_CFG80211=m
+CONFIG_CFG80211_WEXT=y
+# CONFIG_CFG80211_REG_DEBUG is not set
+CONFIG_CFG80211_DEBUGFS=y
+# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set
+CONFIG_CFG80211_DEFAULT_PS=y
+CONFIG_NL80211=y
+# CONFIG_NL80211_TESTMODE is not set
+# CONFIG_WIRELESS_OLD_REGULATORY is not set
+CONFIG_WIRELESS_EXT=y
+CONFIG_WIRELESS_EXT_SYSFS=y
+CONFIG_LIB80211=m
+CONFIG_LIB80211_CRYPT_WEP=m
+CONFIG_LIB80211_CRYPT_CCMP=m
+CONFIG_LIB80211_CRYPT_TKIP=m
+# CONFIG_LIB80211_DEBUG is not set
+
+CONFIG_MAC80211=m
+CONFIG_MAC80211_RC_MINSTREL=y
+# CONFIG_MAC80211_RC_DEFAULT_PID is not set
+CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y
+CONFIG_MAC80211_RC_DEFAULT="minstrel"
+CONFIG_MAC80211_MESH=y
+CONFIG_MAC80211_LEDS=y
+CONFIG_MAC80211_DEBUGFS=y
+# CONFIG_MAC80211_DEBUG_MENU is not set
+
+CONFIG_WIMAX=m
+CONFIG_WIMAX_DEBUG_LEVEL=8
+CONFIG_WIMAX_I2400M_USB=m
+CONFIG_WIMAX_I2400M_SDIO=m
+CONFIG_WIMAX_I2400M_DEBUG_LEVEL=8
+CONFIG_WIMAX_IWMC3200_SDIO=y
+
+CONFIG_ADM8211=m
+CONFIG_ATH_COMMON=m
+CONFIG_ATH5K=m
+CONFIG_ATH5K_DEBUG=y
+CONFIG_ATH9K=m
+# CONFIG_ATH9K_DEBUG is not set
+CONFIG_ATH9K_DEBUGFS=y
+CONFIG_ATH9K_HTC=m
+# CONFIG_ATH9K_HTC_DEBUGFS is not set
+CONFIG_AT76C50X_USB=m
+CONFIG_AIRO=m
+CONFIG_AIRO_CS=m
+CONFIG_ATMEL=m
+CONFIG_B43=m
+CONFIG_B43_PCMCIA=y
+CONFIG_B43_SDIO=y
+CONFIG_B43_DEBUG=y
+CONFIG_B43_PHY_LP=y
+# CONFIG_B43_FORCE_PIO is not set
+CONFIG_B43LEGACY=m
+CONFIG_B43LEGACY_DEBUG=y
+CONFIG_B43LEGACY_DMA=y
+CONFIG_B43LEGACY_PIO=y
+CONFIG_B43LEGACY_DMA_AND_PIO_MODE=y
+# CONFIG_B43LEGACY_DMA_MODE is not set
+# CONFIG_B43LEGACY_PIO_MODE is not set
+CONFIG_HERMES=m
+CONFIG_HERMES_CACHE_FW_ON_INIT=y
+# CONFIG_HERMES_PRISM is not set
+CONFIG_NORTEL_HERMES=m
+CONFIG_PCI_HERMES=m
+CONFIG_PLX_HERMES=m
+CONFIG_PCMCIA_HERMES=m
+CONFIG_ORINOCO_USB=m
+CONFIG_HOSTAP=m
+CONFIG_HOSTAP_PCI=m
+CONFIG_HOSTAP_PLX=m
+CONFIG_HOSTAP_FIRMWARE=y
+CONFIG_HOSTAP_FIRMWARE_NVRAM=y
+CONFIG_HOSTAP_CS=m
+# CONFIG_IPW2100 is not set
+# CONFIG_IPW2200 is not set
+# CONFIG_IPW2100_DEBUG is not set
+# CONFIG_IPW2200_DEBUG is not set
+# CONFIG_LIBIPW_DEBUG is not set
+CONFIG_LIBERTAS=m
+CONFIG_LIBERTAS_USB=m
+CONFIG_LIBERTAS_CS=m
+CONFIG_LIBERTAS_SDIO=m
+# CONFIG_LIBERTAS_DEBUG is not set
+CONFIG_LIBERTAS_THINFIRM=m
+CONFIG_LIBERTAS_THINFIRM_USB=m
+# CONFIG_LIBERTAS_THINFIRM_DEBUG is not set
+CONFIG_LIBERTAS_MESH=y
+CONFIG_IWLWIFI=m
+CONFIG_IWLWIFI_DEBUG=y
+CONFIG_IWLWIFI_DEBUGFS=y
+CONFIG_IWLWIFI_SPECTRUM_MEASUREMENT=y
+CONFIG_IWLAGN=m
+CONFIG_IWL4965=y
+CONFIG_IWL5000=y
+CONFIG_IWL3945=m
+CONFIG_IWL3945_SPECTRUM_MEASUREMENT=y
+CONFIG_IWM=m
+# CONFIG_IWM_DEBUG is not set
+# CONFIG_IWM_TRACING is not set
+CONFIG_MAC80211_HWSIM=m
+CONFIG_P54_COMMON=m
+CONFIG_P54_USB=m
+CONFIG_P54_PCI=m
+CONFIG_PCI_ATMEL=m
+CONFIG_MWL8K=m
+# CONFIG_PRISM54 is not set
+CONFIG_PCMCIA_SPECTRUM=m
+CONFIG_PCMCIA_ATMEL=m
+CONFIG_PCMCIA_WL3501=m
+CONFIG_RT2X00=m
+CONFIG_RT2X00_LIB_DEBUGFS=y
+# CONFIG_RT2X00_DEBUG is not set
+CONFIG_RT2400PCI=m
+CONFIG_RT2500PCI=m
+CONFIG_RT61PCI=m
+CONFIG_RT2500USB=m
+CONFIG_RT2800USB=m
+# CONFIG_RT2800USB_RT30XX is not set
+# CONFIG_RT2800USB_RT35XX is not set
+# CONFIG_RT2800USB_UNKNOWN is not set
+CONFIG_RT2800PCI=m
+# CONFIG_RT2800PCI_RT30XX is not set
+# CONFIG_RT2800PCI_RT35XX is not set
+CONFIG_RT73USB=m
+CONFIG_RTL8180=m
+CONFIG_RTL8187=m
+CONFIG_TMD_HERMES=m
+CONFIG_USB_ZD1201=m
+CONFIG_USB_NET_RNDIS_WLAN=m
+CONFIG_USB_NET_SMSC75XX=m
+CONFIG_ZD1211RW=m
+# CONFIG_ZD1211RW_DEBUG is not set
+CONFIG_AR9170_USB=m
+
+CONFIG_WL12XX=y
+CONFIG_WL1251=m
+CONFIG_WL1251_SPI=m
+CONFIG_WL1251_SDIO=m
+CONFIG_WL1271=m
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_IBMOL is not set
+# CONFIG_3C359 is not set
+# Broken with gcc4.1
+# CONFIG_TMS380TR is not set
+# CONFIG_TMSPCI is not set
+# CONFIG_ABYSS is not set
+# CONFIG_IBMLS is not set
+# CONFIG_PCMCIA_IBMTR is not set
+
+
+CONFIG_NET_FC=y
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# PCMCIA network device support
+#
+CONFIG_NET_PCMCIA=y
+CONFIG_PCMCIA_3C589=m
+CONFIG_PCMCIA_3C574=m
+CONFIG_PCMCIA_FMVJ18X=m
+CONFIG_PCMCIA_PCNET=m
+CONFIG_PCMCIA_NMCLAN=m
+CONFIG_PCMCIA_SMC91C92=m
+CONFIG_PCMCIA_XIRC2PS=m
+CONFIG_PCMCIA_AXNET=m
+
+#
+# Amateur Radio support
+#
+CONFIG_HAMRADIO=y
+CONFIG_AX25=m
+CONFIG_AX25_DAMA_SLAVE=y
+CONFIG_CAN=m
+CONFIG_CAN_RAW=m
+CONFIG_CAN_BCM=m
+CONFIG_CAN_VCAN=m
+CONFIG_CAN_DEV=m
+CONFIG_CAN_CALC_BITTIMING=y
+CONFIG_CAN_SJA1000=m
+CONFIG_CAN_SJA1000_ISA=m
+CONFIG_CAN_SJA1000_PLATFORM=m
+CONFIG_CAN_EMS_PCI=m
+CONFIG_CAN_EMS_USB=m
+CONFIG_CAN_KVASER_PCI=m
+CONFIG_CAN_PLX_PCI=m
+CONFIG_NETROM=m
+CONFIG_ROSE=m
+CONFIG_MKISS=m
+CONFIG_6PACK=m
+CONFIG_BPQETHER=m
+CONFIG_SCC=m
+CONFIG_DMASCC=m
+# CONFIG_SCC_DELAY is not set
+CONFIG_SCC_TRXECHO=y
+CONFIG_BAYCOM_SER_FDX=m
+CONFIG_BAYCOM_SER_HDX=m
+CONFIG_BAYCOM_PAR=m
+CONFIG_BAYCOM_EPP=m
+CONFIG_YAM=m
+
+#
+# IrDA (infrared) support
+#
+CONFIG_IRDA=m
+# CONFIG_IRDA_DEBUG is not set
+CONFIG_IRLAN=m
+CONFIG_IRNET=m
+CONFIG_IRCOMM=m
+# CONFIG_IRDA_ULTRA is not set
+CONFIG_IRDA_CACHE_LAST_LSAP=y
+CONFIG_IRDA_FAST_RR=y
+CONFIG_IRTTY_SIR=m
+CONFIG_DONGLE=y
+CONFIG_ACTISYS_DONGLE=m
+CONFIG_ACT200L_DONGLE=m
+CONFIG_ESI_DONGLE=m
+CONFIG_GIRBIL_DONGLE=m
+CONFIG_KINGSUN_DONGLE=m
+CONFIG_KSDAZZLE_DONGLE=m
+CONFIG_KS959_DONGLE=m
+CONFIG_LITELINK_DONGLE=m
+CONFIG_MA600_DONGLE=m
+CONFIG_MCP2120_DONGLE=m
+CONFIG_OLD_BELKIN_DONGLE=m
+CONFIG_TEKRAM_DONGLE=m
+CONFIG_TOIM3232_DONGLE=m
+
+CONFIG_ALI_FIR=m
+CONFIG_MCS_FIR=m
+CONFIG_NSC_FIR=m
+CONFIG_SIGMATEL_FIR=m
+CONFIG_SMC_IRCC_FIR=m
+# CONFIG_TOSHIBA_FIR is not set
+CONFIG_USB_IRDA=m
+CONFIG_VLSI_FIR=m
+CONFIG_VIA_FIR=m
+CONFIG_WINBOND_FIR=m
+
+#
+# Bluetooth support
+#
+CONFIG_BT=m
+CONFIG_BT_L2CAP=m
+CONFIG_BT_L2CAP_EXT_FEATURES=y
+CONFIG_BT_SCO=m
+CONFIG_BT_CMTP=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+
+#
+# Bluetooth device drivers
+#
+CONFIG_BT_HCIBTUSB=m
+# Disable the BT_HCIUSB driver.
+# It sucks more power than BT_HCIBTUSB which has the same functionality.
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+CONFIG_BT_HCIDTL1=m
+CONFIG_BT_HCIBT3C=m
+CONFIG_BT_HCIBLUECARD=m
+CONFIG_BT_HCIBTUART=m
+CONFIG_BT_HCIVHCI=m
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBTSDIO=m
+CONFIG_BT_HCIUART_LL=y
+CONFIG_BT_MRVL=m
+CONFIG_BT_MRVL_SDIO=m
+CONFIG_BT_ATH3K=m
+
+#
+# ISDN subsystem
+#
+CONFIG_ISDN=y
+CONFIG_MISDN=m
+CONFIG_MISDN_DSP=m
+CONFIG_MISDN_L1OIP=m
+CONFIG_MISDN_AVMFRITZ=m
+CONFIG_MISDN_SPEEDFAX=m
+CONFIG_MISDN_INFINEON=m
+CONFIG_MISDN_W6692=m
+CONFIG_MISDN_NETJET=m
+
+#
+# mISDN hardware drivers
+#
+CONFIG_MISDN_HFCPCI=m
+CONFIG_MISDN_HFCMULTI=m
+CONFIG_ISDN_I4L=m
+CONFIG_ISDN_DRV_AVMB1_B1PCI=m
+CONFIG_ISDN_DRV_AVMB1_B1PCMCIA=m
+CONFIG_ISDN_DRV_AVMB1_T1PCI=m
+CONFIG_ISDN_DRV_AVMB1_C4=m
+
+CONFIG_MISDN_HFCUSB=m
+
+CONFIG_ISDN_PPP=y
+CONFIG_ISDN_PPP_VJ=y
+CONFIG_ISDN_MPP=y
+# CONFIG_ISDN_PPP_BSDCOMP is not set
+CONFIG_ISDN_TTY_FAX=y
+CONFIG_DE_AOC=y
+
+CONFIG_ISDN_AUDIO=y
+
+CONFIG_ISDN_DRV_HISAX=m
+CONFIG_ISDN_DRV_AVMB1_B1PCIV4=y
+CONFIG_ISDN_DRV_AVMB1_AVM_CS=m
+
+CONFIG_ISDN_CAPI_CAPIDRV=m
+CONFIG_ISDN_DIVERSION=m
+
+CONFIG_HISAX_EURO=y
+CONFIG_HISAX_1TR6=y
+CONFIG_HISAX_NI1=y
+CONFIG_HISAX_MAX_CARDS=8
+CONFIG_HISAX_16_3=y
+CONFIG_HISAX_TELESPCI=y
+CONFIG_HISAX_S0BOX=y
+CONFIG_HISAX_FRITZPCI=y
+CONFIG_HISAX_AVM_A1_PCMCIA=y
+CONFIG_HISAX_ELSA=y
+CONFIG_HISAX_DIEHLDIVA=y
+CONFIG_HISAX_SEDLBAUER=y
+CONFIG_HISAX_NETJET=y
+CONFIG_HISAX_NETJET_U=y
+CONFIG_HISAX_NICCY=y
+CONFIG_HISAX_BKM_A4T=y
+CONFIG_HISAX_SCT_QUADRO=y
+CONFIG_HISAX_GAZEL=y
+CONFIG_HISAX_HFC_PCI=y
+CONFIG_HISAX_W6692=y
+CONFIG_HISAX_HFC_SX=y
+CONFIG_HISAX_ENTERNOW_PCI=y
+# CONFIG_HISAX_DEBUG is not set
+CONFIG_HISAX_AVM_A1_CS=m
+CONFIG_HISAX_ST5481=m
+# CONFIG_HISAX_HFCUSB is not set
+CONFIG_HISAX_FRITZ_PCIPNP=m
+CONFIG_HISAX_NO_SENDCOMPLETE=y
+CONFIG_HISAX_NO_LLC=y
+CONFIG_HISAX_NO_KEYPAD=y
+CONFIG_HISAX_SEDLBAUER_CS=m
+CONFIG_HISAX_ELSA_CS=m
+CONFIG_HISAX_TELES_CS=m
+CONFIG_HISAX_HFC4S8S=m
+
+CONFIG_ISDN_DRV_LOOP=m
+CONFIG_HYSDN=m
+CONFIG_HYSDN_CAPI=y
+
+
+#
+# CAPI subsystem
+#
+CONFIG_ISDN_CAPI=m
+# CONFIG_CAPI_TRACE is not set
+CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON=y
+CONFIG_ISDN_CAPI_MIDDLEWARE=y
+CONFIG_ISDN_CAPI_CAPI20=m
+CONFIG_ISDN_CAPI_CAPIFS_BOOL=y
+CONFIG_ISDN_CAPI_CAPIFS=m
+
+#
+# CAPI hardware drivers
+#
+
+#
+# Active AVM cards
+#
+CONFIG_CAPI_AVM=y
+
+#
+# Active Eicon DIVA Server cards
+#
+# CONFIG_CAPI_EICON is not set
+CONFIG_ISDN_DIVAS=m
+CONFIG_ISDN_DIVAS_BRIPCI=y
+CONFIG_ISDN_DIVAS_PRIPCI=y
+CONFIG_ISDN_DIVAS_DIVACAPI=m
+CONFIG_ISDN_DIVAS_USERIDI=m
+CONFIG_ISDN_DIVAS_MAINT=m
+
+CONFIG_ISDN_DRV_GIGASET=m
+CONFIG_GIGASET_CAPI=y
+CONFIG_GIGASET_BASE=m
+CONFIG_GIGASET_M101=m
+CONFIG_GIGASET_M105=m
+# CONFIG_GIGASET_DEBUG is not set
+
+#
+# Telephony Support
+#
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+CONFIG_INPUT_FF_MEMLESS=m
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+CONFIG_INPUT_JOYDEV=m
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+
+CONFIG_INPUT_TABLET=y
+CONFIG_TABLET_USB_ACECAD=m
+CONFIG_TABLET_USB_AIPTEK=m
+CONFIG_TABLET_USB_GTCO=m
+CONFIG_TABLET_USB_KBTAB=m
+CONFIG_TABLET_USB_WACOM=m
+
+CONFIG_INPUT_POWERMATE=m
+CONFIG_INPUT_YEALINK=m
+CONFIG_INPUT_CM109=m
+CONFIG_INPUT_POLLDEV=m
+CONFIG_INPUT_SPARSEKMAP=m
+
+#
+# Input I/O drivers
+#
+CONFIG_GAMEPORT=m
+CONFIG_GAMEPORT_NS558=m
+CONFIG_GAMEPORT_L4=m
+CONFIG_GAMEPORT_EMU10K1=m
+CONFIG_GAMEPORT_FM801=m
+CONFIG_SERIO=y
+CONFIG_SERIO_I8042=y
+CONFIG_SERIO_SERPORT=y
+CONFIG_SERIO_RAW=m
+CONFIG_SERIO_ALTERA_PS2=m
+
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_PARKBD is not set
+# CONFIG_SERIO_PCIPS2 is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+CONFIG_KEYBOARD_ATKBD=y
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_MATRIX is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_LM8323 is not set
+# FIXME: Do we really need these keyboards enabled ?
+CONFIG_KEYBOARD_ADP5588=m
+CONFIG_KEYBOARD_MAX7359=m
+CONFIG_KEYBOARD_OPENCORES=m
+CONFIG_KEYBOARD_QT2160=m
+# CONFIG_KEYBOARD_TCA6416 is not set
+CONFIG_INPUT_MOUSE=y
+CONFIG_MOUSE_PS2=y
+# CONFIG_MOUSE_PS2_TOUCHKIT is not set
+CONFIG_MOUSE_PS2_ELANTECH=y
+CONFIG_MOUSE_PS2_SENTELIC=y
+CONFIG_MOUSE_SERIAL=m
+CONFIG_MOUSE_VSXXXAA=m
+CONFIG_MOUSE_APPLETOUCH=m
+CONFIG_MOUSE_BCM5974=m
+CONFIG_MOUSE_SYNAPTICS_I2C=m
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_JOYSTICK_ANALOG=m
+CONFIG_JOYSTICK_A3D=m
+CONFIG_JOYSTICK_ADI=m
+CONFIG_JOYSTICK_COBRA=m
+CONFIG_JOYSTICK_GF2K=m
+CONFIG_JOYSTICK_GRIP=m
+CONFIG_JOYSTICK_GRIP_MP=m
+CONFIG_JOYSTICK_GUILLEMOT=m
+CONFIG_JOYSTICK_INTERACT=m
+CONFIG_JOYSTICK_SIDEWINDER=m
+CONFIG_JOYSTICK_TMDC=m
+CONFIG_JOYSTICK_IFORCE=m
+CONFIG_JOYSTICK_IFORCE_USB=y
+CONFIG_JOYSTICK_IFORCE_232=y
+CONFIG_JOYSTICK_WARRIOR=m
+CONFIG_JOYSTICK_MAGELLAN=m
+CONFIG_JOYSTICK_SPACEORB=m
+CONFIG_JOYSTICK_SPACEBALL=m
+CONFIG_JOYSTICK_STINGER=m
+CONFIG_JOYSTICK_DB9=m
+CONFIG_JOYSTICK_GAMECON=m
+CONFIG_JOYSTICK_TURBOGRAFX=m
+CONFIG_JOYSTICK_JOYDUMP=m
+CONFIG_JOYSTICK_TWIDJOY=m
+CONFIG_JOYSTICK_WALKERA0701=m
+CONFIG_JOYSTICK_XPAD=m
+CONFIG_JOYSTICK_XPAD_FF=y
+CONFIG_JOYSTICK_XPAD_LEDS=y
+CONFIG_JOYSTICK_ZHENHUA=m
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_GUNZE=m
+CONFIG_TOUCHSCREEN_ELO=m
+CONFIG_TOUCHSCREEN_FUJITSU=m
+CONFIG_TOUCHSCREEN_HTCPEN=m
+CONFIG_TOUCHSCREEN_INEXIO=m
+CONFIG_TOUCHSCREEN_MTOUCH=m
+CONFIG_TOUCHSCREEN_MK712=m
+CONFIG_TOUCHSCREEN_PENMOUNT=m
+CONFIG_TOUCHSCREEN_TSC2007=m
+CONFIG_TOUCHSCREEN_AD7879_I2C=m
+CONFIG_TOUCHSCREEN_TOUCHIT213=m
+CONFIG_TOUCHSCREEN_TOUCHRIGHT=m
+CONFIG_TOUCHSCREEN_TOUCHWIN=m
+CONFIG_TOUCHSCREEN_UCB1400=m
+CONFIG_TOUCHSCREEN_WACOM_W8001=m
+CONFIG_TOUCHSCREEN_USB_E2I=y
+CONFIG_TOUCHSCREEN_USB_COMPOSITE=m
+CONFIG_TOUCHSCREEN_DYNAPRO=m
+# CONFIG_TOUCHSCREEN_WM97XX is not set
+CONFIG_TOUCHSCREEN_EETI=m
+CONFIG_TOUCHSCREEN_W90X900=m
+CONFIG_TOUCHSCREEN_MCS5000=m
+# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set
+# CONFIG_TOUCHSCREEN_TPS6507X is not set
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_PCSPKR=m
+CONFIG_INPUT_UINPUT=m
+CONFIG_INPUT_WISTRON_BTNS=m
+CONFIG_INPUT_ATLAS_BTNS=m
+
+CONFIG_INPUT_ATI_REMOTE=m
+CONFIG_INPUT_ATI_REMOTE2=m
+CONFIG_INPUT_KEYSPAN_REMOTE=m
+
+CONFIG_MAC_EMUMOUSEBTN=y
+
+CONFIG_INPUT_WM831X_ON=m
+
+CONFIG_INPUT_APPLEIR=m
+
+# CONFIG_INPUT_AD714X is not set
+# CONFIG_INPUT_PCF8574 is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_SERIAL_NONSTANDARD=y
+CONFIG_ROCKETPORT=m
+CONFIG_SYNCLINK=m
+CONFIG_SYNCLINKMP=m
+CONFIG_SYNCLINK_GT=m
+CONFIG_N_HDLC=m
+CONFIG_N_GSM=m
+# CONFIG_STALDRV is not set
+# CONFIG_IBM_ASM is not set
+CONFIG_TIFM_CORE=m
+CONFIG_TIFM_7XX1=m
+CONFIG_TCG_TPM=m
+CONFIG_TCG_TIS=m
+CONFIG_TCG_NSC=m
+CONFIG_TCG_ATMEL=m
+# CONFIG_TCG_INFINEON is not set
+CONFIG_TELCLOCK=m
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_CS=m
+CONFIG_SERIAL_8250_NR_UARTS=32
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+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
+# CONFIG_COMPUTONE is not set
+CONFIG_CYCLADES=m
+# CONFIG_CYZ_INTR 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_RISCOM8 is not set
+# CONFIG_SPECIALIX is not set
+# CONFIG_SX is not set
+# CONFIG_RIO is not set
+# CONFIG_STALLION is not set
+# CONFIG_ISTALLION is not set
+CONFIG_SERIAL_JSM=m
+
+# CONFIG_SERIAL_ALTERA_JTAGUART is not set
+# CONFIG_SERIAL_ALTERA_UART is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_TIMBERDALE is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_DEVPTS_MULTIPLE_INSTANCES=y
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_PRINTER=m
+CONFIG_LP_CONSOLE=y
+CONFIG_PPDEV=m
+
+#
+# I2C support
+#
+CONFIG_I2C=m
+CONFIG_I2C_COMPAT=y
+CONFIG_I2C_CHARDEV=m
+
+#
+# I2C Algorithms
+#
+# CONFIG_I2C_DEBUG_ALGO is not set
+CONFIG_I2C_HELPER_AUTO=y
+CONFIG_I2C_ALGOBIT=m
+CONFIG_I2C_ALGOPCF=m
+
+#
+# I2C Hardware Bus support
+#
+
+CONFIG_I2C_ALGOPCA=m
+# 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_AMD756_S4882 is not set
+# CONFIG_I2C_AMD8111 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+# CONFIG_I2C_ELEKTOR is not set
+# CONFIG_I2C_I801 is not set
+# CONFIG_I2C_ISCH is not set
+# CONFIG_I2C_NFORCE2_S4985 is not set
+
+CONFIG_EEPROM_AT24=m
+CONFIG_EEPROM_LEGACY=m
+CONFIG_EEPROM_93CX6=m
+CONFIG_EEPROM_MAX6875=m
+
+CONFIG_I2C_NFORCE2=m
+# CONFIG_I2C_OCORES is not set
+CONFIG_I2C_PARPORT=m
+CONFIG_I2C_PARPORT_LIGHT=m
+CONFIG_I2C_PASEMI=m
+CONFIG_I2C_PCA_ISA=m
+CONFIG_I2C_PCA_PLATFORM=m
+# CONFIG_I2C_PIIX4 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_SIMTEC=m
+CONFIG_I2C_STUB=m
+CONFIG_I2C_TINY_USB=m
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_VIA is not set
+# CONFIG_I2C_VIAPRO is not set
+# CONFIG_I2C_DESIGNWARE is not set
+# CONFIG_I2C_XILINX is not set
+
+#
+# I2C Hardware Sensors Chip support
+#
+CONFIG_SENSORS_ATK0110=m
+CONFIG_SENSORS_ABITUGURU=m
+CONFIG_SENSORS_ABITUGURU3=m
+CONFIG_SENSORS_AD7414=m
+CONFIG_SENSORS_AD7418=m
+CONFIG_SENSORS_ADM1021=m
+CONFIG_SENSORS_ADM1025=m
+CONFIG_SENSORS_ADM1026=m
+CONFIG_SENSORS_ADM1029=m
+CONFIG_SENSORS_ADM1031=m
+CONFIG_SENSORS_ADM9240=m
+CONFIG_SENSORS_ADS7828=m
+CONFIG_SENSORS_ADT7462=m
+CONFIG_SENSORS_ADT7470=m
+CONFIG_SENSORS_ADT7473=m
+CONFIG_SENSORS_ADT7475=m
+CONFIG_SENSORS_APPLESMC=m
+CONFIG_SENSORS_ASB100=m
+CONFIG_SENSORS_ATXP1=m
+CONFIG_SENSORS_CORETEMP=m
+CONFIG_SENSORS_DME1737=m
+CONFIG_SENSORS_DS1621=m
+# CONFIG_DS1682 is not set
+CONFIG_SENSORS_F71805F=m
+CONFIG_SENSORS_F71882FG=m
+CONFIG_SENSORS_F75375S=m
+CONFIG_SENSORS_FSCHMD=m
+CONFIG_SENSORS_G760A=m
+CONFIG_SENSORS_GL518SM=m
+CONFIG_SENSORS_GL520SM=m
+CONFIG_SENSORS_HDAPS=m
+# CONFIG_SENSORS_I5K_AMB is not set
+# FIXME: IBMAEM x86 only?
+CONFIG_SENSORS_IBMAEM=m
+CONFIG_SENSORS_IBMPEX=m
+CONFIG_SENSORS_IT87=m
+CONFIG_SENSORS_K8TEMP=m
+CONFIG_SENSORS_K10TEMP=m
+CONFIG_SENSORS_LIS3LV02D=m
+CONFIG_SENSORS_LIS3_I2C=m
+CONFIG_SENSORS_LM63=m
+CONFIG_SENSORS_LM75=m
+CONFIG_SENSORS_LM77=m
+CONFIG_SENSORS_LM78=m
+CONFIG_SENSORS_LM80=m
+CONFIG_SENSORS_LM83=m
+CONFIG_SENSORS_LM85=m
+CONFIG_SENSORS_LM87=m
+CONFIG_SENSORS_LM90=m
+CONFIG_SENSORS_LM92=m
+CONFIG_SENSORS_LM93=m
+CONFIG_SENSORS_LTC4245=m
+CONFIG_SENSORS_MAX1619=m
+CONFIG_SENSORS_MAX6650=m
+CONFIG_SENSORS_PC87360=m
+CONFIG_SENSORS_PC87427=m
+CONFIG_SENSORS_PCF8591=m
+CONFIG_SENSORS_SHT15=m
+CONFIG_SENSORS_SIS5595=m
+CONFIG_SENSORS_SMSC47M1=m
+CONFIG_SENSORS_SMSC47M192=m
+CONFIG_SENSORS_SMSC47B397=m
+CONFIG_SENSORS_THMC50=m
+CONFIG_SENSORS_TMP401=m
+CONFIG_SENSORS_TSL2550=m
+CONFIG_SENSORS_VIA686A=m
+CONFIG_SENSORS_VIA_CPUTEMP=m
+CONFIG_SENSORS_VT1211=m
+CONFIG_SENSORS_VT8231=m
+CONFIG_SENSORS_W83627HF=m
+CONFIG_SENSORS_W83781D=m
+CONFIG_SENSORS_W83L785TS=m
+CONFIG_SENSORS_W83L786NG=m
+CONFIG_SENSORS_W83627EHF=m
+CONFIG_SENSORS_W83791D=m
+CONFIG_SENSORS_W83792D=m
+CONFIG_SENSORS_W83793=m
+CONFIG_SENSORS_LTC4215=m
+CONFIG_SENSORS_LM95241=m
+CONFIG_SENSORS_TMP421=m
+CONFIG_SENSORS_WM8350=m
+CONFIG_SENSORS_WM831X=m
+CONFIG_SENSORS_LM73=m
+CONFIG_SENSORS_AMC6821=m
+CONFIG_SENSORS_ADT7411=m
+CONFIG_SENSORS_ASC7621=m
+CONFIG_SENSORS_EMC1403=m
+CONFIG_SENSORS_TMP102=m
+
+CONFIG_W1=m
+CONFIG_W1_CON=y
+# This is busted.
+# If we enable it, it steals Matrox cards, and the
+# framebuffer drivers stop working.
+# CONFIG_W1_MASTER_MATROX is not set
+CONFIG_W1_MASTER_DS2482=m
+CONFIG_W1_MASTER_DS2490=m
+CONFIG_W1_MASTER_DS1WM=m
+CONFIG_W1_SLAVE_THERM=m
+CONFIG_W1_SLAVE_SMEM=m
+CONFIG_W1_SLAVE_DS2431=m
+CONFIG_W1_SLAVE_DS2433=m
+CONFIG_W1_SLAVE_DS2433_CRC=y
+CONFIG_W1_SLAVE_DS2760=m
+#
+# Mice
+#
+
+#
+# IPMI
+#
+CONFIG_IPMI_HANDLER=m
+# CONFIG_IPMI_PANIC_EVENT is not set
+CONFIG_IPMI_DEVICE_INTERFACE=m
+CONFIG_IPMI_WATCHDOG=m
+CONFIG_IPMI_SI=m
+CONFIG_IPMI_POWEROFF=m
+
+#
+# Watchdog Cards
+#
+CONFIG_WATCHDOG=y
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+CONFIG_SOFT_WATCHDOG=m
+CONFIG_WDTPCI=m
+# CONFIG_ACQUIRE_WDT is not set
+# CONFIG_ADVANTECH_WDT is not set
+# CONFIG_EUROTECH_WDT is not set
+# CONFIG_IB700_WDT is not set
+# CONFIG_MIXCOMWD is not set
+# CONFIG_SCx200_WDT is not set
+# CONFIG_60XX_WDT is not set
+CONFIG_W83877F_WDT=m
+CONFIG_W83627HF_WDT=m
+CONFIG_MACHZ_WDT=m
+# CONFIG_SC520_WDT is not set
+CONFIG_ALIM7101_WDT=m
+CONFIG_ALIM1535_WDT=m
+CONFIG_ITCO_WDT=m
+CONFIG_ITCO_VENDOR_SUPPORT=y
+# CONFIG_SC1200_WDT is not set
+# CONFIG_PC87413_WDT is not set
+# CONFIG_WAFER_WDT is not set
+# CONFIG_CPU5_WDT is not set
+CONFIG_I6300ESB_WDT=m
+CONFIG_IT8712F_WDT=m
+# CONFIG_SBC8360_WDT is not set
+# CONFIG_SBC7240_WDT is not set
+CONFIG_SMSC_SCH311X_WDT=m
+CONFIG_W83977F_WDT=m
+CONFIG_PCIPCWATCHDOG=m
+CONFIG_USBPCWATCHDOG=m
+# CONFIG_SBC_EPX_C3_WATCHDOG is not set
+CONFIG_WM8350_WATCHDOG=m
+CONFIG_WM831X_WATCHDOG=m
+# CONFIG_MAX63XX_WATCHDOG is not set
+
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_TIMERIOMEM=m
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+# CONFIG_RTC_DEBUG is not set
+# CONFIG_GEN_RTC is not set
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+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_CMOS=y
+CONFIG_RTC_DRV_DS1307=m
+CONFIG_RTC_DRV_DS1553=m
+CONFIG_RTC_DRV_DS1672=m
+CONFIG_RTC_DRV_DS1742=m
+CONFIG_RTC_DRV_DS1374=m
+# CONFIG_RTC_DRV_EP93XX is not set
+CONFIG_RTC_DRV_FM3130=m
+CONFIG_RTC_DRV_ISL1208=m
+CONFIG_RTC_DRV_M41T80=m
+CONFIG_RTC_DRV_M41T80_WDT=y
+CONFIG_RTC_DRV_M48T59=m
+CONFIG_RTC_DRV_MAX6900=m
+# CONFIG_RTC_DRV_M48T86 is not set
+CONFIG_RTC_DRV_PCF8563=m
+CONFIG_RTC_DRV_PCF8583=m
+CONFIG_RTC_DRV_RS5C372=m
+# CONFIG_RTC_DRV_SA1100 is not set
+# CONFIG_RTC_DRV_TEST is not set
+CONFIG_RTC_DRV_X1205=m
+CONFIG_RTC_DRV_V3020=m
+CONFIG_RTC_DRV_STK17TA8=m
+# CONFIG_RTC_DRV_S35390A is not set
+CONFIG_RTC_DRV_RX8581=m
+CONFIG_RTC_DRV_RX8025=m
+CONFIG_RTC_DRV_DS1286=m
+CONFIG_RTC_DRV_M48T35=m
+CONFIG_RTC_DRV_BQ4802=m
+CONFIG_RTC_DRV_WM8350=m
+# CONFIG_RTC_DRV_AB3100 is not set
+CONFIG_RTC_DRV_WM831X=m
+CONFIG_RTC_DRV_BQ32K=m
+CONFIG_RTC_DRV_MSM6242=m
+CONFIG_RTC_DRV_RP5C01=m
+
+CONFIG_DTLK=m
+CONFIG_R3964=m
+# CONFIG_APPLICOM is not set
+# CONFIG_SONYPI is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+CONFIG_AGP=y
+CONFIG_AGP_ALI=y
+CONFIG_AGP_ATI=y
+CONFIG_AGP_AMD=y
+CONFIG_AGP_AMD64=y
+CONFIG_AGP_INTEL=y
+CONFIG_AGP_NVIDIA=y
+CONFIG_AGP_SIS=y
+CONFIG_AGP_SWORKS=y
+CONFIG_AGP_VIA=y
+CONFIG_AGP_EFFICEON=y
+CONFIG_VGA_ARB=y
+CONFIG_VGA_ARB_MAX_GPUS=16
+CONFIG_DRM=m
+CONFIG_DRM_TDFX=m
+CONFIG_DRM_R128=m
+CONFIG_DRM_RADEON=m
+CONFIG_DRM_RADEON_KMS=y
+CONFIG_DRM_I810=m
+# CONFIG_DRM_I830 is not set
+CONFIG_DRM_MGA=m
+CONFIG_DRM_SIS=m
+CONFIG_DRM_SAVAGE=m
+CONFIG_DRM_I915=m
+CONFIG_DRM_I915_KMS=y
+CONFIG_DRM_VIA=m
+CONFIG_DRM_NOUVEAU=m
+CONFIG_DRM_NOUVEAU_BACKLIGHT=y
+CONFIG_DRM_NOUVEAU_DEBUG=y
+CONFIG_DRM_I2C_CH7006=m
+CONFIG_DRM_VMWGFX=m
+
+#
+# PCMCIA character devices
+#
+# CONFIG_SYNCLINK_CS is not set
+
+CONFIG_CARDMAN_4000=m
+CONFIG_CARDMAN_4040=m
+
+CONFIG_MWAVE=m
+CONFIG_RAW_DRIVER=y
+CONFIG_MAX_RAW_DEVS=8192
+CONFIG_HANGCHECK_TIMER=m
+
+#
+# Multimedia devices
+#
+CONFIG_MEDIA_SUPPORT=m
+CONFIG_VIDEO_DEV=m
+# CONFIG_VIDEO_ADV_DEBUG is not set
+CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
+CONFIG_VIDEO_ALLOW_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
+CONFIG_VIDEO_V4L2=y
+# CONFIG_VIDEO_VIVI is not set
+
+#
+# Video For Linux
+#
+
+#
+# Video Adapters
+#
+CONFIG_V4L_USB_DRIVERS=y
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+CONFIG_VIDEO_AU0828=m
+CONFIG_VIDEO_BT848=m
+CONFIG_VIDEO_BT848_DVB=y
+CONFIG_VIDEO_BWQCAM=m
+# CONFIG_VIDEO_CAFE_CCIC is not set
+# CONFIG_VIDEO_CPIA is not set
+CONFIG_VIDEO_CPIA2=m
+CONFIG_VIDEO_CQCAM=m
+CONFIG_VIDEO_CX23885=m
+CONFIG_VIDEO_CX18=m
+CONFIG_VIDEO_CX18_ALSA=m
+CONFIG_VIDEO_CX88=m
+CONFIG_VIDEO_CX88_DVB=m
+CONFIG_VIDEO_CX88_ALSA=m
+CONFIG_VIDEO_CX88_BLACKBIRD=m
+CONFIG_VIDEO_CX88_VP3054=m
+CONFIG_VIDEO_EM28XX=m
+CONFIG_VIDEO_EM28XX_ALSA=m
+CONFIG_VIDEO_EM28XX_DVB=m
+CONFIG_VIDEO_CX231XX=m
+CONFIG_VIDEO_CX231XX_ALSA=m
+CONFIG_VIDEO_CX231XX_DVB=m
+CONFIG_VIDEO_HEXIUM_ORION=m
+CONFIG_VIDEO_HEXIUM_GEMINI=m
+CONFIG_VIDEO_IVTV=m
+CONFIG_VIDEO_MEYE=m
+CONFIG_VIDEO_MXB=m
+# CONFIG_VIDEO_OVCAMCHIP is not set
+CONFIG_VIDEO_PVRUSB2_DVB=y
+CONFIG_VIDEO_HDPVR=m
+CONFIG_VIDEO_SAA5246A=m
+CONFIG_VIDEO_SAA5249=m
+CONFIG_VIDEO_SAA6588=m
+CONFIG_VIDEO_SAA7134=m
+CONFIG_VIDEO_SAA7134_ALSA=m
+CONFIG_VIDEO_SAA7134_DVB=m
+CONFIG_VIDEO_STRADIS=m
+CONFIG_VIDEO_USBVISION=m
+CONFIG_VIDEO_W9966=m
+CONFIG_VIDEO_ZORAN=m
+CONFIG_VIDEO_ZORAN_AVS6EYES=m
+CONFIG_VIDEO_ZORAN_BUZ=m
+CONFIG_VIDEO_ZORAN_DC10=m
+CONFIG_VIDEO_ZORAN_DC30=m
+CONFIG_VIDEO_ZORAN_LML33=m
+CONFIG_VIDEO_ZORAN_LML33R10=m
+CONFIG_VIDEO_ZORAN_ZR36060=m
+CONFIG_VIDEO_FB_IVTV=m
+CONFIG_VIDEO_SAA7164=m
+CONFIG_VIDEO_TLG2300=m
+
+CONFIG_USB_VIDEO_CLASS=m
+CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y
+
+#
+# Radio Adapters
+#
+CONFIG_RADIO_GEMTEK_PCI=m
+CONFIG_RADIO_MAXIRADIO=m
+CONFIG_RADIO_MAESTRO=m
+
+CONFIG_MEDIA_ATTACH=y
+CONFIG_MEDIA_TUNER_CUSTOMISE=y
+CONFIG_MEDIA_TUNER_SIMPLE=m
+CONFIG_MEDIA_TUNER_TDA8290=m
+CONFIG_MEDIA_TUNER_TEA5761=m
+CONFIG_MEDIA_TUNER_TEA5767=m
+CONFIG_MEDIA_TUNER_MT20XX=m
+CONFIG_MEDIA_TUNER_MT2060=m
+CONFIG_MEDIA_TUNER_MT2266=m
+CONFIG_MEDIA_TUNER_MT2131=m
+CONFIG_MEDIA_TUNER_QT1010=m
+CONFIG_MEDIA_TUNER_XC2028=m
+CONFIG_MEDIA_TUNER_XC5000=m
+CONFIG_MEDIA_TUNER_MXL5005S=m
+CONFIG_MEDIA_TUNER_MXL5007T=m
+CONFIG_MEDIA_TUNER_MC44S803=m
+CONFIG_MEDIA_TUNER_MAX2165=m
+
+#
+# Digital Video Broadcasting Devices
+#
+CONFIG_DVB_CAPTURE_DRIVERS=y
+CONFIG_DVB_CORE=m
+CONFIG_DVB_MAX_ADAPTERS=8
+CONFIG_DVB_DYNAMIC_MINORS=y
+
+CONFIG_DVB_FE_CUSTOMISE=y
+CONFIG_DVB_STB0899=m
+CONFIG_DVB_STB6100=m
+CONFIG_DVB_STV090x=m
+CONFIG_DVB_STV6110x=m
+CONFIG_DVB_CX24110=m
+CONFIG_DVB_CX24123=m
+CONFIG_DVB_MT312=m
+CONFIG_DVB_ZL10036=m
+CONFIG_DVB_ZL10039=m
+CONFIG_DVB_S5H1420=m
+CONFIG_DVB_STV0288=m
+CONFIG_DVB_STB6000=m
+CONFIG_DVB_STV6110=m
+CONFIG_DVB_STV0900=m
+CONFIG_DVB_TDA8083=m
+CONFIG_DVB_TDA10086=m
+CONFIG_DVB_TDA8261=m
+CONFIG_DVB_VES1X93=m
+CONFIG_DVB_TUNER_ITD1000=m
+CONFIG_DVB_TUNER_CX24113=m
+CONFIG_DVB_TDA826X=m
+CONFIG_DVB_TUA6100=m
+CONFIG_DVB_CX24116=m
+CONFIG_DVB_SI21XX=m
+CONFIG_DVB_DS3000=m
+CONFIG_DVB_SP8870=m
+CONFIG_DVB_SP887X=m
+CONFIG_DVB_CX22700=m
+CONFIG_DVB_CX22702=m
+CONFIG_DVB_L64781=m
+CONFIG_DVB_NXT6000=m
+CONFIG_DVB_MT352=m
+CONFIG_DVB_DIB7000M=m
+CONFIG_DVB_DIB7000P=m
+CONFIG_DVB_TDA10048=m
+CONFIG_DVB_VES1820=m
+CONFIG_DVB_TDA10021=m
+CONFIG_DVB_TDA10023=m
+CONFIG_DVB_STV0297=m
+CONFIG_DVB_NXT200X=m
+CONFIG_DVB_OR51211=m
+CONFIG_DVB_OR51132=m
+CONFIG_DVB_BCM3510=m
+CONFIG_DVB_LGDT330X=m
+CONFIG_DVB_LGDT3305=m
+CONFIG_DVB_S5H1409=m
+CONFIG_DVB_AU8522=m
+CONFIG_DVB_S5H1411=m
+CONFIG_DVB_DIB8000=m
+CONFIG_DVB_TUNER_DIB0070=m
+CONFIG_DVB_TUNER_DIB0090=m
+CONFIG_DVB_LNBP21=m
+CONFIG_DVB_ISL6421=m
+CONFIG_DVB_ISL6423=m
+CONFIG_DVB_LGS8GXX=m
+CONFIG_DVB_ATBM8830=m
+CONFIG_DVB_TDA665x=m
+CONFIG_DVB_STV0299=m
+CONFIG_DVB_MB86A16=m
+
+#
+# Supported Frontend Modules
+#
+CONFIG_DVB_BT8XX=m
+CONFIG_DVB_BUDGET_CORE=m
+CONFIG_DVB_PLUTO2=m
+CONFIG_SMS_SIANO_MDTV=m
+CONFIG_SMS_USB_DRV=m
+CONFIG_SMS_SDIO_DRV=m
+CONFIG_DVB_TTUSB_DEC=m
+CONFIG_DVB_USB_DTV5100=m
+CONFIG_DVB_USB_AF9015=m
+CONFIG_DVB_USB_ANYSEE=m
+CONFIG_DVB_USB_DW2102=m
+CONFIG_DVB_USB_FRIIO=m
+CONFIG_DVB_USB_EC168=m
+CONFIG_DVB_DM1105=m
+CONFIG_DVB_DRX397XD=m
+CONFIG_DVB_LGDT3304=m
+CONFIG_DVB_S921=m
+CONFIG_DVB_ISL6405=m
+CONFIG_DVB_LGS8GL5=m
+CONFIG_DVB_DUMMY_FE=m
+CONFIG_DVB_FIREDTV=m
+CONFIG_DVB_NGENE=m
+
+#
+# Supported SAA7146 based PCI Adapters
+#
+CONFIG_DVB_AV7110=m
+CONFIG_DVB_AV7110_OSD=y
+CONFIG_DVB_BUDGET=m
+CONFIG_DVB_BUDGET_CI=m
+CONFIG_DVB_BUDGET_AV=m
+CONFIG_DVB_BUDGET_PATCH=m
+
+#
+# Supported USB Adapters
+#
+CONFIG_DVB_TTUSB_BUDGET=m
+
+#
+# Supported FlexCopII (B2C2) Adapters
+#
+CONFIG_DVB_USB_CINERGY_T2=m
+CONFIG_DVB_B2C2_FLEXCOP=m
+CONFIG_DVB_B2C2_FLEXCOP_PCI=m
+CONFIG_DVB_B2C2_FLEXCOP_USB=m
+# CONFIG_DVB_B2C2_FLEXCOP_DEBUG is not set
+CONFIG_DVB_USB=m
+# CONFIG_DVB_USB_DEBUG is not set
+CONFIG_DVB_USB_A800=m
+CONFIG_DVB_USB_AF9005=m
+CONFIG_DVB_USB_AF9005_REMOTE=m
+CONFIG_DVB_USB_AU6610=m
+CONFIG_DVB_USB_CXUSB=m
+CONFIG_DVB_USB_DIBUSB_MB=m
+# CONFIG_DVB_USB_DIBUSB_MB_FAULTY is not set
+CONFIG_DVB_USB_DIBUSB_MC=m
+CONFIG_DVB_USB_DIB0700=m
+CONFIG_DVB_USB_DIGITV=m
+CONFIG_DVB_USB_DTT200U=m
+CONFIG_DVB_USB_GL861=m
+CONFIG_DVB_USB_GP8PSK=m
+CONFIG_DVB_USB_M920X=m
+CONFIG_DVB_USB_NOVA_T_USB2=m
+CONFIG_DVB_USB_CE6230=m
+CONFIG_DVB_USB_OPERA1=m
+CONFIG_DVB_USB_TTUSB2=m
+CONFIG_DVB_USB_UMT_010=m
+CONFIG_DVB_USB_VP702X=m
+CONFIG_DVB_USB_VP7045=m
+CONFIG_DVB_USB_AZ6027=m
+
+CONFIG_DVB_PT1=m
+
+CONFIG_MANTIS_CORE=m
+CONFIG_DVB_MANTIS=m
+CONFIG_DVB_HOPPER=m
+
+CONFIG_VIDEO_SAA7146=m
+CONFIG_VIDEO_SAA7146_VV=m
+CONFIG_VIDEO_TUNER=m
+CONFIG_VIDEO_BTCX=m
+CONFIG_VIDEO_PVRUSB2=m
+CONFIG_VIDEO_PVRUSB2_SYSFS=y
+# CONFIG_VIDEO_PVRUSB2_DEBUGIFC is not set
+
+CONFIG_RC_MAP=m
+CONFIG_IR_NEC_DECODER=m
+CONFIG_IR_RC5_DECODER=m
+CONFIG_IR_RC6_DECODER=m
+CONFIG_IR_JVC_DECODER=m
+CONFIG_IR_SONY_DECODER=m
+CONFIG_IR_LIRC_CODEC=m
+CONFIG_IR_IMON=m
+CONFIG_IR_MCEUSB=m
+
+CONFIG_V4L_MEM2MEM_DRIVERS=y
+# CONFIG_VIDEO_MEM2MEM_TESTDEV is not set
+
+#
+# Broadcom Crystal HD video decoder driver
+#
+CONFIG_CRYSTALHD=m
+
+#
+# Graphics support
+#
+
+CONFIG_DISPLAY_SUPPORT=m
+CONFIG_VIDEO_OUTPUT_CONTROL=m
+
+CONFIG_FB=y
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+CONFIG_FB_3DFX=m
+CONFIG_FB_3DFX_ACCEL=y
+CONFIG_FB_3DFX_I2C=y
+# CONFIG_FB_ARC is not set
+# CONFIG_FB_ARK is not set
+CONFIG_FB_ATY128=m
+CONFIG_FB_ATY=m
+CONFIG_FB_ATY_CT=y
+CONFIG_FB_ATY_GX=y
+CONFIG_FB_ATY_GENERIC_LCD=y
+# CONFIG_FB_ASILIANT is not set
+# CONFIG_FB_CARMINE is not set
+CONFIG_FB_CIRRUS=m
+# CONFIG_FB_CYBER2000 is not set
+# CONFIG_FB_GEODE is not set
+# CONFIG_FB_HECUBA is not set
+# CONFIG_FB_HGA is not set
+CONFIG_FB_I810=m
+CONFIG_FB_I810_GTF=y
+CONFIG_FB_I810_I2C=y
+# CONFIG_FB_IMSTT is not set
+# CONFIG_FB_INTEL is not set
+# CONFIG_FB_INTEL_DEBUG is not set
+# CONFIG_FB_INTEL_I2C is not set
+CONFIG_FB_KYRO=m
+# CONFIG_FB_LE80578 is not set
+CONFIG_FB_MATROX=m
+CONFIG_FB_MATROX_MILLENIUM=y
+CONFIG_FB_MATROX_MYSTIQUE=y
+CONFIG_FB_MATROX_G=y
+CONFIG_FB_MATROX_I2C=m
+CONFIG_FB_MATROX_MAVEN=m
+CONFIG_FB_NEOMAGIC=m
+CONFIG_FB_NVIDIA=m
+# CONFIG_FB_NVIDIA_DEBUG is not set
+CONFIG_FB_NVIDIA_I2C=y
+# CONFIG_FB_PM2 is not set
+# CONFIG_FB_PM2_FIFO_DISCONNECT is not set
+# CONFIG_FB_PM3 is not set
+CONFIG_FB_RADEON=m
+# CONFIG_FB_RADEON_DEBUG is not set
+CONFIG_FB_RADEON_I2C=y
+CONFIG_FB_RIVA=m
+# CONFIG_FB_RIVA_DEBUG is not set
+# CONFIG_FB_RIVA_I2C is not set
+# CONFIG_FB_S1D13XXX is not set
+CONFIG_FB_S3=m
+CONFIG_FB_SAVAGE=m
+CONFIG_FB_SAVAGE_I2C=y
+CONFIG_FB_SAVAGE_ACCEL=y
+# CONFIG_FB_SIS is not set
+CONFIG_FB_SIS_300=y
+CONFIG_FB_SIS_315=y
+CONFIG_FB_SM501=m
+CONFIG_FB_TILEBLITTING=y
+CONFIG_FB_TRIDENT=m
+# CONFIG_FB_UVESA is not set
+CONFIG_FB_VESA=y
+CONFIG_FB_VGA16=m
+CONFIG_FB_VIRTUAL=m
+CONFIG_FB_VOODOO1=m
+# CONFIG_FB_VT8623 is not set
+CONFIG_FB_EFI=y
+CONFIG_FB_VIA=m
+# CONFIG_FB_VIA_DIRECT_PROCFS is not set
+CONFIG_FB_METRONOME=m
+CONFIG_FB_MB862XX=m
+CONFIG_FB_MB862XX_PCI_GDC=y
+CONFIG_FB_MB862XX_LIME=y
+# CONFIG_FB_PRE_INIT_FB is not set
+# CONFIG_FB_TMIO is not set
+# CONFIG_FB_BROADSHEET is not set
+
+# CONFIG_FIRMWARE_EDID is not set
+
+#
+# Console display driver support
+#
+CONFIG_VGA_CONSOLE=y
+CONFIG_VGACON_SOFT_SCROLLBACK=y
+CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=64
+# CONFIG_MDA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
+CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
+# CONFIG_FONTS is not set
+
+#
+# Logo configuration
+#
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_LOGO_LINUX_CLUT224=y
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+
+#
+# Advanced Linux Sound Architecture
+#
+CONFIG_SND=y
+CONFIG_SOUND_OSS_CORE_PRECLAIM=y
+# CONFIG_SND_DEBUG_VERBOSE is not set
+CONFIG_SND_VERBOSE_PROCFS=y
+CONFIG_SND_SEQUENCER=y
+CONFIG_SND_HRTIMER=y
+CONFIG_SND_SEQ_HRTIMER_DEFAULT=y
+CONFIG_SND_SEQ_DUMMY=m
+CONFIG_SND_SEQUENCER_OSS=y
+CONFIG_SND_SEQ_RTCTIMER_DEFAULT=y
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=y
+CONFIG_SND_PCM_OSS=y
+CONFIG_SND_PCM_OSS_PLUGINS=y
+CONFIG_SND_RTCTIMER=y
+CONFIG_SND_DYNAMIC_MINORS=y
+# CONFIG_SND_SUPPORT_OLD_API is not set
+
+#
+# Generic devices
+#
+CONFIG_SND_DUMMY=m
+CONFIG_SND_VIRMIDI=m
+CONFIG_SND_MTPAV=m
+CONFIG_SND_MTS64=m
+CONFIG_SND_SERIAL_U16550=m
+CONFIG_SND_MPU401=m
+CONFIG_SND_PORTMAN2X4=m
+CONFIG_SND_AC97_POWER_SAVE=y
+CONFIG_SND_AC97_POWER_SAVE_DEFAULT=0
+
+CONFIG_SND_DRIVERS=y
+
+#
+# ISA devices
+#
+CONFIG_SND_AD1889=m
+# CONFIG_SND_WAVEFRONT is not set
+# CONFIG_SND_MSND_PINNACLE is not set
+# CONFIG_SND_MSND_CLASSIC is not set
+
+#
+# PCI devices
+#
+CONFIG_SND_PCI=y
+CONFIG_SND_ADLIB=m
+CONFIG_SND_ALI5451=m
+CONFIG_SND_ALS300=m
+CONFIG_SND_ALS4000=m
+CONFIG_SND_ATIIXP=m
+CONFIG_SND_ATIIXP_MODEM=m
+CONFIG_SND_AU8810=m
+CONFIG_SND_AU8820=m
+CONFIG_SND_AU8830=m
+# CONFIG_SND_AW2 is not set
+CONFIG_SND_AZT3328=m
+CONFIG_SND_BT87X=m
+# CONFIG_SND_BT87X_OVERCLOCK is not set
+CONFIG_SND_CA0106=m
+CONFIG_SND_CMIPCI=m
+CONFIG_SND_CS46XX=m
+CONFIG_SND_CS46XX_NEW_DSP=y
+CONFIG_SND_CS4281=m
+CONFIG_SND_CS5530=m
+CONFIG_SND_CS5535AUDIO=m
+CONFIG_SND_EMU10K1=m
+CONFIG_SND_EMU10K1X=m
+CONFIG_SND_ENS1370=m
+CONFIG_SND_ENS1371=m
+CONFIG_SND_ES1938=m
+CONFIG_SND_ES1968=m
+CONFIG_SND_ES1968_INPUT=y
+CONFIG_SND_FM801=m
+CONFIG_SND_FM801_TEA575X_BOOL=y
+CONFIG_SND_CTXFI=m
+CONFIG_SND_LX6464ES=m
+CONFIG_SND_HDA_INTEL=y
+CONFIG_SND_HDA_INPUT_BEEP=y
+CONFIG_SND_HDA_INPUT_BEEP_MODE=0
+CONFIG_SND_HDA_INPUT_JACK=y
+CONFIG_SND_HDA_PATCH_LOADER=y
+CONFIG_SND_HDA_HWDEP=y
+CONFIG_SND_HDA_CODEC_REALTEK=y
+CONFIG_SND_HDA_CODEC_CA0110=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_CIRRUS=y
+CONFIG_SND_HDA_CODEC_CONEXANT=y
+CONFIG_SND_HDA_CODEC_CMEDIA=y
+CONFIG_SND_HDA_CODEC_INTELHDMI=y
+CONFIG_SND_HDA_CODEC_SI3054=y
+CONFIG_SND_HDA_CODEC_NVHDMI=y
+CONFIG_SND_HDA_GENERIC=y
+CONFIG_SND_HDA_POWER_SAVE=y
+CONFIG_SND_HDA_POWER_SAVE_DEFAULT=0
+CONFIG_SND_HDA_RECONFIG=y
+CONFIG_SND_HDSPM=m
+CONFIG_SND_HIFIER=m
+CONFIG_SND_ICE1712=m
+CONFIG_SND_ICE1724=m
+CONFIG_SND_INTEL8X0=y
+CONFIG_SND_INTEL8X0M=m
+CONFIG_SND_KORG1212=m
+CONFIG_SND_MAESTRO3=m
+CONFIG_SND_MAESTRO3_INPUT=y
+CONFIG_SND_MIRO=m
+CONFIG_SND_MIXART=m
+CONFIG_SND_NM256=m
+CONFIG_SND_OXYGEN=m
+CONFIG_SND_RME32=m
+CONFIG_SND_PCSP=m
+CONFIG_SND_PCXHR=m
+CONFIG_SND_RIPTIDE=m
+CONFIG_SND_RME96=m
+CONFIG_SND_RME9652=m
+CONFIG_SND_SC6000=m
+CONFIG_SND_SIS7019=m
+CONFIG_SND_SONICVIBES=m
+CONFIG_SND_HDSP=m
+CONFIG_SND_TRIDENT=m
+CONFIG_SND_VIA82XX=m
+CONFIG_SND_VIA82XX_MODEM=m
+CONFIG_SND_VIRTUOSO=m
+CONFIG_SND_VX222=m
+CONFIG_SND_YMFPCI=m
+CONFIG_SND_ASIHPI=m
+
+#
+# ALSA USB devices
+#
+CONFIG_SND_USB=y
+CONFIG_SND_USB_AUDIO=m
+CONFIG_SND_USB_CAIAQ=m
+CONFIG_SND_USB_CAIAQ_INPUT=y
+CONFIG_SND_USB_USX2Y=m
+CONFIG_SND_USB_US122L=m
+CONFIG_SND_USB_UA101=m
+
+#
+# PCMCIA devices
+#
+CONFIG_SND_PCMCIA=y
+CONFIG_SND_VXPOCKET=m
+CONFIG_SND_PDAUDIOCF=m
+
+#
+# Open Sound System
+#
+# CONFIG_SOUND_PRIME is not set
+
+#
+# USB support
+#
+CONFIG_USB=y
+CONFIG_USB_SUPPORT=y
+# CONFIG_USB_DEBUG is not set
+
+# DEPRECATED: See bug 362221. Fix udev.
+# CONFIG_USB_DEVICE_CLASS is not set
+
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+CONFIG_USB_SUSPEND=y
+
+#
+# USB Host Controller Drivers
+#
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+CONFIG_USB_EHCI_TT_NEWSCHED=y
+CONFIG_USB_OHCI_HCD=y
+# CONFIG_USB_OHCI_HCD_SSB is not set
+CONFIG_USB_UHCI_HCD=y
+# CONFIG_USB_SL811_CS is not set
+# CONFIG_USB_R8A66597_HCD is not set
+CONFIG_USB_XHCI_HCD=m
+# CONFIG_USB_XHCI_HCD_DEBUGGING is not set
+CONFIG_USB_ISP1362_HCD=m
+
+#
+# USB Device Class drivers
+#
+
+#
+# USB Bluetooth TTY can only be used with disabled Bluetooth subsystem
+#
+CONFIG_USB_ACM=m
+CONFIG_USB_PRINTER=m
+CONFIG_USB_WDM=m
+CONFIG_USB_TMC=m
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_USB_STORAGE=m
+# CONFIG_USB_STORAGE_DEBUG is not set
+CONFIG_USB_STORAGE_CYPRESS_ATACB=y
+CONFIG_USB_STORAGE_DATAFAB=y
+CONFIG_USB_STORAGE_FREECOM=y
+CONFIG_USB_STORAGE_ISD200=y
+CONFIG_USB_STORAGE_SDDR09=y
+CONFIG_USB_STORAGE_SDDR55=y
+CONFIG_USB_STORAGE_JUMPSHOT=y
+CONFIG_USB_STORAGE_USBAT=y
+CONFIG_USB_STORAGE_ONETOUCH=y
+CONFIG_USB_STORAGE_ALAUDA=y
+CONFIG_USB_STORAGE_KARMA=y
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Human Interface Devices (HID)
+#
+CONFIG_USB_HID=y
+
+CONFIG_HID_SUPPORT=y
+
+CONFIG_HID=m
+# debugging default is y upstream now
+CONFIG_HIDRAW=y
+CONFIG_HID_PID=y
+CONFIG_LOGITECH_FF=y
+CONFIG_LOGIRUMBLEPAD2_FF=y
+CONFIG_PANTHERLORD_FF=y
+CONFIG_THRUSTMASTER_FF=y
+CONFIG_HID_WACOM=m
+CONFIG_HID_WACOM_POWER_SUPPLY=y
+CONFIG_ZEROPLUS_FF=y
+CONFIG_USB_HIDDEV=y
+CONFIG_USB_IDMOUSE=m
+CONFIG_DRAGONRISE_FF=y
+CONFIG_GREENASIA_FF=y
+CONFIG_SMARTJOYPLUS_FF=y
+CONFIG_HID_3M_PCT=y
+CONFIG_LOGIG940_FF=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MOSART=y
+CONFIG_HID_NTRIG=y
+CONFIG_HID_QUANTA=y
+CONFIG_HID_STANTUM=y
+CONFIG_HID_CANDO=m
+CONFIG_HID_PRODIKEYS=m
+CONFIG_HID_DRAGONRISE=m
+CONFIG_HID_EGALAX=m
+CONFIG_HID_GYRATION=m
+CONFIG_HID_TWINHAN=m
+CONFIG_HID_ORTEK=m
+CONFIG_HID_PANTHERLORD=m
+CONFIG_HID_PETALYNX=m
+CONFIG_HID_PICOLCD=m
+CONFIG_HID_ROCCAT=m
+CONFIG_HID_ROCCAT_KONE=m
+CONFIG_HID_SAMSUNG=m
+CONFIG_HID_SONY=m
+CONFIG_HID_SUNPLUS=m
+CONFIG_HID_GREENASIA=m
+CONFIG_HID_SMARTJOYPLUS=m
+CONFIG_HID_TOPSEED=m
+CONFIG_HID_THRUSTMASTER=m
+CONFIG_HID_ZEROPLUS=m
+CONFIG_HID_ZYDACRON=m
+
+
+#
+# USB Imaging devices
+#
+CONFIG_USB_MDC800=m
+CONFIG_USB_MICROTEK=m
+
+#
+# USB Multimedia devices
+#
+CONFIG_DAB=y
+CONFIG_USB_DABUSB=m
+
+CONFIG_USB_VICAM=m
+CONFIG_USB_DSBR=m
+# CONFIG_USB_ET61X251 is not set
+CONFIG_USB_M5602=m
+CONFIG_USB_STV06XX=m
+CONFIG_USB_GSPCA=m
+CONFIG_USB_GSPCA_MR97310A=m
+CONFIG_USB_GSPCA_BENQ=m
+CONFIG_USB_GSPCA_CONEX=m
+CONFIG_USB_GSPCA_CPIA1=m
+CONFIG_USB_GSPCA_ETOMS=m
+CONFIG_USB_GSPCA_FINEPIX=m
+CONFIG_USB_GSPCA_MARS=m
+CONFIG_USB_GSPCA_OV519=m
+CONFIG_USB_GSPCA_OV534=m
+CONFIG_USB_GSPCA_OV534_9=m
+CONFIG_USB_GSPCA_PAC207=m
+CONFIG_USB_GSPCA_PAC7311=m
+CONFIG_USB_GSPCA_SN9C2028=m
+CONFIG_USB_GSPCA_SN9C20X=m
+CONFIG_USB_GSPCA_SN9C20X_EVDEV=y
+CONFIG_USB_GSPCA_SONIXB=m
+CONFIG_USB_GSPCA_SONIXJ=m
+CONFIG_USB_GSPCA_SPCA500=m
+CONFIG_USB_GSPCA_SPCA501=m
+CONFIG_USB_GSPCA_SPCA505=m
+CONFIG_USB_GSPCA_SPCA506=m
+CONFIG_USB_GSPCA_SPCA508=m
+CONFIG_USB_GSPCA_SPCA561=m
+CONFIG_USB_GSPCA_STK014=m
+CONFIG_USB_GSPCA_SUNPLUS=m
+CONFIG_USB_GSPCA_T613=m
+CONFIG_USB_GSPCA_TV8532=m
+CONFIG_USB_GSPCA_VC032X=m
+CONFIG_USB_GSPCA_ZC3XX=m
+CONFIG_USB_GSPCA_SQ905=m
+CONFIG_USB_GSPCA_SQ905C=m
+CONFIG_USB_GSPCA_PAC7302=m
+CONFIG_USB_GSPCA_STV0680=m
+CONFIG_USB_GL860=m
+CONFIG_USB_GSPCA_JEILINJ=m
+CONFIG_USB_GSPCA_SPCA1528=m
+CONFIG_USB_GSPCA_SQ930X=m
+
+CONFIG_USB_IBMCAM=m
+CONFIG_USB_KONICAWC=m
+# CONFIG_USB_OV511 is not set
+CONFIG_USB_S2255=m
+CONFIG_USB_SE401=m
+# CONFIG_VIDEO_SH_MOBILE_CEU is not set
+# CONFIG_USB_STV680 is not set
+# CONFIG_USB_SN9C102 is not set
+CONFIG_USB_ZR364XX=m
+CONFIG_SOC_CAMERA=m
+CONFIG_SOC_CAMERA_MT9M001=m
+CONFIG_SOC_CAMERA_MT9V022=m
+CONFIG_SOC_CAMERA_PLATFORM=m
+CONFIG_SOC_CAMERA_MT9M111=m
+CONFIG_SOC_CAMERA_MT9T031=m
+CONFIG_SOC_CAMERA_TW9910=m
+CONFIG_SOC_CAMERA_OV772X=m
+CONFIG_SOC_CAMERA_MT9T112=m
+CONFIG_SOC_CAMERA_RJ54N1=m
+CONFIG_SOC_CAMERA_OV9640=m
+
+#
+# USB Network adaptors
+#
+CONFIG_USB_CATC=m
+CONFIG_USB_HSO=m
+CONFIG_USB_KAWETH=m
+CONFIG_USB_PEGASUS=m
+CONFIG_USB_RTL8150=m
+CONFIG_USB_USBNET=m
+CONFIG_USB_SPEEDTOUCH=m
+CONFIG_USB_NET_AX8817X=m
+CONFIG_USB_NET_DM9601=m
+CONFIG_USB_NET_SMSC95XX=m
+CONFIG_USB_NET_GL620A=m
+CONFIG_USB_NET_NET1080=m
+CONFIG_USB_NET_PLUSB=m
+CONFIG_USB_NET_MCS7830=m
+CONFIG_USB_NET_RNDIS_HOST=m
+CONFIG_USB_NET_CDC_SUBSET=m
+CONFIG_USB_NET_CDC_EEM=m
+CONFIG_USB_NET_ZAURUS=m
+CONFIG_USB_NET_INT51X1=m
+CONFIG_USB_CDC_PHONET=m
+CONFIG_USB_IPHETH=m
+CONFIG_USB_SIERRA_NET=m
+
+#
+# USB Host-to-Host Cables
+#
+CONFIG_USB_AN2720=y
+CONFIG_USB_BELKIN=y
+
+#
+# Intelligent USB Devices/Gadgets
+#
+CONFIG_USB_ARMLINUX=y
+CONFIG_USB_EPSON2888=y
+CONFIG_USB_KC2190=y
+
+# CONFIG_USB_MUSB_HDRC is not set
+
+#
+# USB port drivers
+#
+CONFIG_USB_USS720=m
+
+#
+# USB Serial Converter support
+#
+CONFIG_USB_SERIAL=m
+CONFIG_USB_SERIAL_GENERIC=y
+CONFIG_USB_SERIAL_AIRCABLE=m
+CONFIG_USB_SERIAL_ARK3116=m
+CONFIG_USB_SERIAL_BELKIN=m
+CONFIG_USB_SERIAL_CH341=m
+CONFIG_USB_SERIAL_CYPRESS_M8=m
+CONFIG_USB_SERIAL_CYBERJACK=m
+CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
+CONFIG_USB_SERIAL_CP210X=m
+CONFIG_USB_SERIAL_QUALCOMM=m
+CONFIG_USB_SERIAL_SYMBOL=m
+CONFIG_USB_SERIAL_EDGEPORT=m
+CONFIG_USB_SERIAL_EDGEPORT_TI=m
+CONFIG_USB_SERIAL_EMPEG=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+CONFIG_USB_SERIAL_FUNSOFT=m
+CONFIG_USB_SERIAL_GARMIN=m
+CONFIG_USB_SERIAL_HP4X=m
+CONFIG_USB_SERIAL_IPAQ=m
+CONFIG_USB_SERIAL_IPW=m
+CONFIG_USB_SERIAL_IR=m
+CONFIG_USB_SERIAL_IUU=m
+CONFIG_USB_SERIAL_KEYSPAN_PDA=m
+CONFIG_USB_SERIAL_KEYSPAN=m
+CONFIG_USB_SERIAL_KEYSPAN_MPR=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28X=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19=y
+CONFIG_USB_SERIAL_KEYSPAN_USA18X=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19W=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y
+CONFIG_USB_SERIAL_KEYSPAN_USA49W=y
+CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y
+CONFIG_USB_SERIAL_KLSI=m
+CONFIG_USB_SERIAL_KOBIL_SCT=m
+CONFIG_USB_SERIAL_MCT_U232=m
+CONFIG_USB_SERIAL_MOS7720=m
+CONFIG_USB_SERIAL_MOS7715_PARPORT=y
+# CONFIG_USB_SERIAL_ZIO is not set
+CONFIG_USB_SERIAL_MOS7840=m
+CONFIG_USB_SERIAL_MOTOROLA=m
+CONFIG_USB_SERIAL_NAVMAN=m
+CONFIG_USB_SERIAL_OPTION=y
+CONFIG_USB_SERIAL_OTI6858=m
+CONFIG_USB_SERIAL_OPTICON=m
+CONFIG_USB_SERIAL_OMNINET=m
+CONFIG_USB_SERIAL_PL2303=m
+CONFIG_USB_SERIAL_SAFE=m
+CONFIG_USB_SERIAL_SAFE_PADDED=y
+CONFIG_USB_SERIAL_SIERRAWIRELESS=m
+CONFIG_USB_SERIAL_SIEMENS_MPI=m
+CONFIG_USB_SERIAL_SPCP8X5=m
+CONFIG_USB_SERIAL_TI=m
+CONFIG_USB_SERIAL_VISOR=m
+CONFIG_USB_SERIAL_WHITEHEAT=m
+CONFIG_USB_SERIAL_XIRCOM=m
+CONFIG_USB_SERIAL_QCAUX=m
+CONFIG_USB_SERIAL_VIVOPAY_SERIAL=m
+CONFIG_USB_SERIAL_DEBUG=m
+
+CONFIG_USB_EZUSB=y
+CONFIG_USB_EMI62=m
+CONFIG_USB_LED=m
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+CONFIG_USB_G_SERIAL=m
+
+#
+# USB Miscellaneous drivers
+#
+
+CONFIG_USB_ADUTUX=m
+CONFIG_USB_SEVSEG=m
+CONFIG_USB_ALI_M5632=y
+CONFIG_USB_APPLEDISPLAY=m
+CONFIG_USB_ATM=m
+CONFIG_USB_BERRY_CHARGE=m
+CONFIG_USB_CXACRU=m
+# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_CYTHERM is not set
+CONFIG_USB_EMI26=m
+CONFIG_USB_ETH=m
+CONFIG_USB_FTDI_ELAN=m
+CONFIG_USB_FILE_STORAGE=m
+# CONFIG_USB_FILE_STORAGE_TEST is not set
+# CONFIG_USB_GADGET is not set
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGETFS is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
+# CONFIG_USB_OXU210HP_HCD is not set
+CONFIG_USB_IOWARRIOR=m
+CONFIG_USB_ISIGHTFW=m
+CONFIG_USB_VST=m
+CONFIG_USB_LCD=m
+CONFIG_USB_LD=m
+CONFIG_USB_LEGOTOWER=m
+CONFIG_USB_MON=y
+CONFIG_USB_PWC=m
+CONFIG_USB_PWC_INPUT_EVDEV=y
+# CONFIG_USB_PWC_DEBUG is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_QUICKCAM_MESSENGER is not set
+CONFIG_USB_SL811_HCD=m
+CONFIG_USB_SISUSBVGA=m
+CONFIG_USB_SISUSBVGA_CON=y
+CONFIG_RADIO_SI470X=y
+CONFIG_USB_SI470X=m
+CONFIG_I2C_SI470X=m
+CONFIG_RADIO_SI4713=m
+# CONFIG_RADIO_TEF6862 is not set
+CONFIG_USB_MR800=m
+CONFIG_USB_STKWEBCAM=m
+# CONFIG_USB_TEST is not set
+CONFIG_USB_TRANCEVIBRATOR=m
+CONFIG_USB_U132_HCD=m
+CONFIG_USB_UEAGLEATM=m
+CONFIG_USB_XUSBATM=m
+# CONFIG_USB_ZC0301 is not set
+CONFIG_USB_ZERO=m
+
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB=m
+CONFIG_SSB_PCIHOST=y
+CONFIG_SSB_SDIOHOST=y
+CONFIG_SSB_PCMCIAHOST=y
+# CONFIG_SSB_SILENT is not set
+# CONFIG_SSB_DEBUG is not set
+CONFIG_SSB_DRIVER_PCICORE=y
+
+# Multifunction USB devices
+# CONFIG_MFD_PCF50633 is not set
+CONFIG_PCF50633_ADC=m
+CONFIG_PCF50633_GPIO=m
+# CONFIG_AB3100_CORE is not set
+CONFIG_INPUT_PCF50633_PMU=m
+CONFIG_INPUT_GPIO_ROTARY_ENCODER=m
+CONFIG_CHARGER_PCF50633=m
+CONFIG_RTC_DRV_PCF50633=m
+
+CONFIG_MFD_SUPPORT=y
+CONFIG_MFD_SM501=m
+CONFIG_MFD_SM501_GPIO=y
+# CONFIG_MFD_TC6393XB is not set
+CONFIG_MFD_WM8400=m
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_WM8350 is not set
+# CONFIG_MFD_WM831X is not set
+# CONFIG_AB3100_OTP is not set
+# CONFIG_MFD_TIMBERDALE is not set
+# CONFIG_MFD_WM8994 is not set
+# CONFIG_MFD_88PM860X is not set
+# CONFIG_LPC_SCH is not set
+# CONFIG_HTC_I2CPLD is not set
+# CONFIG_MFD_MAX8925 is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_TPS6507X is not set
+# CONFIG_ABX500_CORE is not set
+# CONFIG_MFD_RDC321X is not set
+# CONFIG_MFD_JANZ_CMODIO is not set
+
+#
+# File systems
+#
+CONFIG_MISC_FILESYSTEMS=y
+
+CONFIG_EXT2_FS=m
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT2_FS_XIP=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_DEFAULTS_TO_ORDERED=y
+CONFIG_EXT3_FS_XATTR=y
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+CONFIG_JBD=y
+# CONFIG_JBD_DEBUG is not set
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_XATTR=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_JBD2=y
+CONFIG_FS_MBCACHE=y
+CONFIG_REISERFS_FS=m
+# CONFIG_REISERFS_CHECK is not set
+CONFIG_REISERFS_PROC_INFO=y
+CONFIG_REISERFS_FS_XATTR=y
+CONFIG_REISERFS_FS_POSIX_ACL=y
+CONFIG_REISERFS_FS_SECURITY=y
+CONFIG_JFS_FS=m
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_STATISTICS is not set
+CONFIG_JFS_POSIX_ACL=y
+CONFIG_JFS_SECURITY=y
+CONFIG_XFS_FS=m
+# CONFIG_XFS_DEBUG is not set
+# CONFIG_XFS_RT is not set
+CONFIG_XFS_QUOTA=y
+CONFIG_XFS_POSIX_ACL=y
+CONFIG_MINIX_FS=m
+CONFIG_ROMFS_FS=m
+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_DNOTIFY=y
+# Autofsv3 is obsolete.
+# CONFIG_AUTOFS_FS is not set
+CONFIG_AUTOFS4_FS=m
+CONFIG_EXOFS_FS=m
+# CONFIG_EXOFS_DEBUG is not set
+CONFIG_NILFS2_FS=m
+CONFIG_LOGFS=m
+CONFIG_CEPH_FS=m
+
+CONFIG_FSCACHE=m
+CONFIG_FSCACHE_STATS=y
+# CONFIG_FSCACHE_HISTOGRAM is not set
+# CONFIG_FSCACHE_DEBUG is not set
+CONFIG_FSCACHE_OBJECT_LIST=y
+
+CONFIG_CACHEFILES=m
+# CONFIG_CACHEFILES_DEBUG is not set
+# CONFIG_CACHEFILES_HISTOGRAM is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_UDF_FS=m
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="ascii"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROC_VMCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_HUGETLBFS=y
+CONFIG_HUGETLB_PAGE=y
+CONFIG_DEBUG_FS=y
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+CONFIG_AFFS_FS=m
+CONFIG_ECRYPT_FS=m
+CONFIG_HFS_FS=m
+CONFIG_HFSPLUS_FS=m
+CONFIG_BEFS_FS=m
+# CONFIG_BEFS_DEBUG is not set
+CONFIG_BFS_FS=m
+CONFIG_EFS_FS=m
+CONFIG_JFFS2_FS=m
+CONFIG_JFFS2_FS_DEBUG=0
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+CONFIG_JFFS2_SUMMARY=y
+CONFIG_JFFS2_FS_XATTR=y
+CONFIG_JFFS2_FS_POSIX_ACL=y
+CONFIG_JFFS2_FS_SECURITY=y
+CONFIG_CRAMFS=m
+CONFIG_SQUASHFS=m
+CONFIG_SQUASHFS_XATTRS=y
+# CONFIG_SQUASHFS_EMBEDDED is not set
+CONFIG_VXFS_FS=m
+# CONFIG_HPFS_FS is not set
+CONFIG_QNX4FS_FS=m
+CONFIG_SYSV_FS=m
+CONFIG_UFS_FS=m
+# CONFIG_UFS_FS_WRITE is not set
+# CONFIG_UFS_DEBUG is not set
+CONFIG_9P_FS=m
+CONFIG_9P_FSCACHE=y
+CONFIG_FUSE_FS=m
+CONFIG_OMFS_FS=m
+CONFIG_CUSE=m
+
+#
+# Network File Systems
+#
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=m
+CONFIG_NFS_V3=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_V4_1=y
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+CONFIG_NFSD_V3_ACL=y
+CONFIG_NFSD_V4=y
+CONFIG_NFS_FSCACHE=y
+CONFIG_LOCKD=m
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_SUNRPC=m
+CONFIG_SUNRPC_GSS=m
+CONFIG_SUNRPC_XPRT_RDMA=m
+CONFIG_RPCSEC_GSS_KRB5=m
+CONFIG_RPCSEC_GSS_SPKM3=m
+# CONFIG_SMB_FS is not set
+# CONFIG_SMB_NLS_DEFAULT is not set
+CONFIG_CIFS=m
+CONFIG_CIFS_STATS=y
+# CONFIG_CIFS_STATS2 is not set
+CONFIG_CIFS_EXPERIMENTAL=y
+CONFIG_CIFS_UPCALL=y
+CONFIG_CIFS_XATTR=y
+CONFIG_CIFS_POSIX=y
+CONFIG_CIFS_WEAK_PW_HASH=y
+# CONFIG_CIFS_DEBUG2 is not set
+CONFIG_CIFS_DFS_UPCALL=y
+CONFIG_NCP_FS=m
+CONFIG_NCPFS_PACKET_SIGNING=y
+CONFIG_NCPFS_IOCTL_LOCKING=y
+CONFIG_NCPFS_STRONG=y
+CONFIG_NCPFS_NFS_NS=y
+CONFIG_NCPFS_OS2_NS=y
+CONFIG_NCPFS_SMALLDOS=y
+CONFIG_NCPFS_NLS=y
+CONFIG_NCPFS_EXTRAS=y
+CONFIG_CODA_FS=m
+# CONFIG_AFS_FS is not set
+# CONFIG_AF_RXRPC is not set
+
+CONFIG_OCFS2_FS=m
+# CONFIG_OCFS2_DEBUG_FS is not set
+# CONFIG_OCFS2_DEBUG_MASKLOG is not set
+CONFIG_OCFS2_FS_O2CB=m
+CONFIG_OCFS2_FS_USERSPACE_CLUSTER=m
+# CONFIG_OCFS2_FS_STATS is not set
+
+CONFIG_BTRFS_FS=m
+CONFIG_BTRFS_FS_POSIX_ACL=y
+
+CONFIG_CONFIGFS_FS=m
+
+CONFIG_DLM=m
+CONFIG_DLM_DEBUG=y
+CONFIG_GFS2_FS=m
+CONFIG_GFS2_FS_LOCKING_DLM=y
+
+CONFIG_UBIFS_FS=m
+CONFIG_UBIFS_FS_XATTR=y
+# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set
+# CONFIG_UBIFS_FS_DEBUG is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+CONFIG_AMIGA_PARTITION=y
+# CONFIG_ATARI_PARTITION is not set
+CONFIG_BSD_DISKLABEL=y
+CONFIG_EFI_PARTITION=y
+CONFIG_KARMA_PARTITION=y
+# CONFIG_LDM_PARTITION is not set
+CONFIG_MAC_PARTITION=y
+CONFIG_MSDOS_PARTITION=y
+CONFIG_MINIX_SUBPARTITION=y
+CONFIG_OSF_PARTITION=y
+CONFIG_SGI_PARTITION=y
+CONFIG_SOLARIS_X86_PARTITION=y
+CONFIG_SUN_PARTITION=y
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_UNIXWARE_DISKLABEL=y
+# CONFIG_ULTRIX_PARTITION is not set
+
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=m
+CONFIG_NLS_ASCII=y
+
+#
+# Profiling support
+#
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=m
+CONFIG_OPROFILE_EVENT_MULTIPLEX=y
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+CONFIG_FRAME_WARN=1024
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_INFO=y
+CONFIG_FRAME_POINTER=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+# CONFIG_DEBUG_DRIVER is not set
+CONFIG_HEADERS_CHECK=y
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_RCU_TRACE is not set
+# CONFIG_LKDTM is not set
+
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_LOCKDEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+
+CONFIG_KGDB=y
+CONFIG_KGDB_SERIAL_CONSOLE=y
+CONFIG_KGDB_TESTS=y
+CONFIG_KGDB_LOW_LEVEL_TRAP=y
+# CONFIG_KGDB_TESTS_ON_BOOT is not set
+
+#
+# Security options
+#
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_SECURITY_NETWORK_XFRM=y
+# CONFIG_SECURITY_PATH is not set
+CONFIG_SECURITY_SELINUX=y
+CONFIG_SECURITY_SELINUX_BOOTPARAM=y
+CONFIG_SECURITY_SELINUX_DISABLE=y
+CONFIG_SECURITY_SELINUX_DEVELOP=y
+CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1
+CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1
+CONFIG_SECURITY_SELINUX_AVC_STATS=y
+# CONFIG_SECURITY_SMACK is not set
+# CONFIG_SECURITY_TOMOYO is not set
+CONFIG_AUDIT=y
+CONFIG_AUDITSYSCALL=y
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_FIPS=y
+CONFIG_CRYPTO_HW=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_MANAGER=m
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_AUTHENC=m
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_CAMELLIA=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_CBC=m
+CONFIG_CRYPTO_CCM=m
+CONFIG_CRYPTO_CRC32C=y
+CONFIG_CRYPTO_CTR=m
+CONFIG_CRYPTO_CTS=m
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_DES=m
+CONFIG_CRYPTO_ECB=m
+CONFIG_CRYPTO_FCRYPT=m
+CONFIG_CRYPTO_GCM=m
+CONFIG_CRYPTO_GF128MUL=m
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_LRW=m
+CONFIG_CRYPTO_LZO=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_PCBC=m
+CONFIG_CRYPTO_RMD128=m
+CONFIG_CRYPTO_RMD160=m
+CONFIG_CRYPTO_RMD256=m
+CONFIG_CRYPTO_RMD320=m
+CONFIG_CRYPTO_SALSA20=m
+CONFIG_CRYPTO_SALSA20_586=m
+CONFIG_CRYPTO_SEED=m
+CONFIG_CRYPTO_SEQIV=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_SHA1=y
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_TGR192=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_WP512=m
+CONFIG_CRYPTO_XCBC=m
+CONFIG_CRYPTO_VMAC=m
+CONFIG_CRYPTO_XTS=m
+CONFIG_CRYPTO_TEST=m
+CONFIG_LIBCRC32C=m
+CONFIG_CRYPTO_CRC32C_INTEL=m
+CONFIG_CRYPTO_GHASH=m
+CONFIG_CRYPTO_ANSI_CPRNG=m
+CONFIG_CRYPTO_DEV_HIFN_795X=m
+CONFIG_CRYPTO_DEV_HIFN_795X_RNG=y
+CONFIG_CRYPTO_PCRYPT=m
+
+# Random number generation
+
+#
+# Library routines
+#
+CONFIG_CRC16=y
+CONFIG_CRC32=m
+CONFIG_CRC_CCITT=m
+CONFIG_CRC_ITU_T=m
+CONFIG_CRC_T10DIF=m
+
+CONFIG_CRYPTO_ZLIB=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=m
+
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_KEYS=y
+CONFIG_KEYS_DEBUG_PROC_KEYS=y
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_CDROM_PKTCDVD_BUFFERS=8
+# CONFIG_CDROM_PKTCDVD_WCACHE is not set
+
+CONFIG_ATA_OVER_ETH=m
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=m
+# CONFIG_BACKLIGHT_GENERIC is not set
+CONFIG_BACKLIGHT_PROGEAR=m
+# CONFIG_BACKLIGHT_ADP8860 is not set
+CONFIG_FB_NVIDIA_BACKLIGHT=y
+CONFIG_FB_RIVA_BACKLIGHT=y
+CONFIG_FB_RADEON_BACKLIGHT=y
+CONFIG_FB_ATY128_BACKLIGHT=y
+CONFIG_FB_ATY_BACKLIGHT=y
+# CONFIG_BACKLIGHT_SAHARA is not set
+CONFIG_BACKLIGHT_WM831X=m
+
+CONFIG_LCD_CLASS_DEVICE=m
+CONFIG_LCD_PLATFORM=m
+
+CONFIG_SCHEDSTATS=y
+CONFIG_SCHED_DEBUG=y
+CONFIG_FAIR_GROUP_SCHED=y
+CONFIG_SCHED_OMIT_FRAME_POINTER=y
+CONFIG_GROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+
+CONFIG_CPUSETS=y
+CONFIG_PROC_PID_CPUSET=y
+
+CONFIG_CGROUPS=y
+# CONFIG_CGROUP_DEBUG is not set
+CONFIG_CGROUP_NS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_CGROUP_MEM_RES_CTLR=y
+CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y
+CONFIG_BLK_CGROUP=y
+# CONFIG_DEBUG_BLK_CGROUP is not set
+
+# CONFIG_SYSFS_DEPRECATED_V2 is not set
+
+CONFIG_RELAY=y
+# CONFIG_PRINTK_TIME is not set
+
+CONFIG_ENABLE_MUST_CHECK=y
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+
+CONFIG_KEXEC=y
+
+CONFIG_HWMON=y
+# CONFIG_HWMON_DEBUG_CHIP is not set
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+
+CONFIG_CONNECTOR=y
+CONFIG_PROC_EVENTS=y
+
+CONFIG_IBMASR=m
+
+CONFIG_PM_DEBUG=y
+CONFIG_PM_TRACE=y
+# CONFIG_PM_VERBOSE is not set
+CONFIG_PM_TEST_SUSPEND=y
+CONFIG_PM_RUNTIME=y
+
+## BEGIN ISA Junk.
+
+CONFIG_I82365=m
+# CONFIG_TCIC is not set
+# CONFIG_PCMCIA_PROBE is not set
+# CONFIG_LTPC is not set
+# CONFIG_COPS is not set
+
+CONFIG_SCSI_AHA152X=m
+CONFIG_SCSI_AHA1542=m
+# CONFIG_SCSI_IN2000 is not set
+CONFIG_SCSI_ARCMSR=m
+CONFIG_SCSI_ARCMSR_AER=y
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+
+# CONFIG_EL1 is not set
+# CONFIG_EL2 is not set
+# CONFIG_ELPLUS is not set
+# CONFIG_EL16 is not set
+CONFIG_EL3=m
+# CONFIG_3C515 is not set
+# CONFIG_LANCE is not set
+CONFIG_NET_VENDOR_SMC=y
+# CONFIG_WD80x3 is not set
+CONFIG_ULTRA=m
+# CONFIG_SMC9194 is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_NI52 is not set
+# CONFIG_NI65 is not set
+# CONFIG_AT1700 is not set
+# CONFIG_DEPCA is not set
+CONFIG_NET_ISA=y
+CONFIG_NE2000=m
+# CONFIG_E2100 is not set
+CONFIG_EWRK3=m
+# CONFIG_EEXPRESS is not set
+# CONFIG_EEXPRESS_PRO is not set
+# CONFIG_HPLAN_PLUS is not set
+# CONFIG_HPLAN is not set
+# CONFIG_LP486E is not set
+# CONFIG_ETH16I is not set
+# CONFIG_ZNET is not set
+# CONFIG_SEEQ8005 is not set
+# CONFIG_AC3200 is not set
+# CONFIG_APRICOT is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_IBMTR is not set
+# CONFIG_SKISA is not set
+# CONFIG_PROTEON is not set
+# CONFIG_SMCTR is not set
+# CONFIG_WAVELAN is not set
+# CONFIG_HISAX_16_0 is not set
+# CONFIG_HISAX_AVM_A1 is not set
+# CONFIG_HISAX_IX1MICROR2 is not set
+# CONFIG_HISAX_ASUSCOM is not set
+# CONFIG_HISAX_TELEINT is not set
+# CONFIG_HISAX_HFCS is not set
+# CONFIG_HISAX_SPORTSTER is not set
+# CONFIG_HISAX_MIC is not set
+# CONFIG_HISAX_ISURF is not set
+# CONFIG_HISAX_HSTSAPHIR is not set
+# CONFIG_ISDN_DRV_ICN is not set
+# CONFIG_ISDN_DRV_PCBIT is not set
+# CONFIG_ISDN_DRV_SC is not set
+# CONFIG_ISDN_DRV_ACT2000 is not set
+# CONFIG_ISDN_DRV_AVMB1_B1ISA is not set
+# CONFIG_ISDN_DRV_AVMB1_T1ISA is not set
+
+# CONFIG_MOUSE_INPORT is not set
+# CONFIG_MOUSE_ATIXL is not set
+# CONFIG_MOUSE_LOGIBM is not set
+# CONFIG_MOUSE_PC110PAD is not set
+
+# CONFIG_SERIAL_8250_FOURPORT is not set
+# CONFIG_SERIAL_8250_ACCENT is not set
+# CONFIG_SERIAL_8250_BOCA is not set
+# CONFIG_SERIAL_8250_HUB6 is not set
+# CONFIG_SERIAL_8250_EXAR_ST16C554 is not set
+
+# CONFIG_PCWATCHDOG is not set
+# CONFIG_WDT is not set
+
+# CONFIG_VIDEO_PMS is not set
+CONFIG_RADIO_ADAPTERS=y
+# CONFIG_RADIO_CADET is not set
+# CONFIG_RADIO_RTRACK is not set
+# CONFIG_RADIO_RTRACK2 is not set
+# CONFIG_RADIO_AZTECH is not set
+# CONFIG_RADIO_GEMTEK is not set
+# CONFIG_RADIO_SF16FMI is not set
+# CONFIG_RADIO_SF16FMR2 is not set
+# CONFIG_RADIO_TERRATEC is not set
+# CONFIG_RADIO_TRUST is not set
+# CONFIG_RADIO_TEA5764 is not set
+# CONFIG_RADIO_TYPHOON is not set
+# CONFIG_RADIO_ZOLTRIX is not set
+# CONFIG_RADIO_SAA7706H is not set
+
+# CONFIG_SND_OPL4_LIB is not set
+# CONFIG_SND_AD1816A is not set
+# CONFIG_SND_AD1848 is not set
+# CONFIG_SND_CS4231 is not set
+CONFIG_SND_CS4236=m
+# CONFIG_SND_ES968 is not set
+# CONFIG_SND_ES1688 is not set
+# CONFIG_SND_ES18XX is not set
+# CONFIG_SND_GUSCLASSIC is not set
+# CONFIG_SND_GUSEXTREME is not set
+# CONFIG_SND_GUSMAX is not set
+# CONFIG_SND_INTERWAVE is not set
+# CONFIG_SND_JAZZ16 is not set
+# CONFIG_SND_INTERWAVE_STB is not set
+# CONFIG_SND_OPTI92X_AD1848 is not set
+# CONFIG_SND_OPTI92X_CS4231 is not set
+# CONFIG_SND_OPTI93X is not set
+# CONFIG_SND_SB8 is not set
+CONFIG_SND_SB16=m
+CONFIG_SND_SBAWE=m
+# CONFIG_SND_SB16_CSP is not set
+# CONFIG_SND_ALS100 is not set
+# CONFIG_SND_AZT2320 is not set
+# CONFIG_SND_CMI8330 is not set
+# CONFIG_SND_DT019X is not set
+CONFIG_SND_OPL3SA2=m
+# CONFIG_SND_SGALAXY is not set
+# CONFIG_SND_SSCAPE is not set
+CONFIG_SND_DARLA20=m
+CONFIG_SND_GINA20=m
+CONFIG_SND_LAYLA20=m
+CONFIG_SND_DARLA24=m
+CONFIG_SND_GINA24=m
+CONFIG_SND_LAYLA24=m
+CONFIG_SND_MONA=m
+CONFIG_SND_MIA=m
+CONFIG_SND_ECHO3G=m
+CONFIG_SND_INDIGO=m
+CONFIG_SND_INDIGOIO=m
+CONFIG_SND_INDIGODJ=m
+CONFIG_SND_INDIGOIOX=m
+CONFIG_SND_INDIGODJX=m
+# CONFIG_SND_SOC is not set
+
+## END of ISA options.
+
+CONFIG_COMPACTION=y
+CONFIG_MIGRATION=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+# CONFIG_LEDS_AMS_DELTA is not set
+# CONFIG_LEDS_LOCOMO is not set
+# CONFIG_LEDS_NET48XX is not set
+# CONFIG_LEDS_PCA9532 is not set
+# CONFIG_LEDS_PCA955X is not set
+# CONFIG_LEDS_BD2802 is not set
+# CONFIG_LEDS_S3C24XX is not set
+CONFIG_LEDS_DELL_NETBOOKS=m
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=m
+CONFIG_LEDS_TRIGGER_IDE_DISK=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=m
+CONFIG_LEDS_TRIGGER_BACKLIGHT=m
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=m
+CONFIG_LEDS_ALIX2=m
+CONFIG_LEDS_WM8350=m
+CONFIG_LEDS_LP3944=m
+CONFIG_LEDS_WM831X_STATUS=m
+CONFIG_LEDS_REGULATOR=m
+CONFIG_LEDS_LT3593=m
+CONFIG_LEDS_TRIGGER_GPIO=m
+CONFIG_LEDS_INTEL_SS4200=m
+
+CONFIG_DMADEVICES=y
+CONFIG_DMA_ENGINE=y
+# CONFIG_TIMB_DMA is not set
+CONFIG_NET_DMA=y
+# CONFIG_DMATEST is not set
+CONFIG_ASYNC_TX_DMA=y
+
+CONFIG_UNUSED_SYMBOLS=y
+
+CONFIG_UTRACE=y
+
+CONFIG_FTRACE=y
+CONFIG_DYNAMIC_FTRACE=y
+# CONFIG_IRQSOFF_TRACER is not set
+CONFIG_SCHED_TRACER=y
+CONFIG_CONTEXT_SWITCH_TRACER=y
+CONFIG_WORKQUEUE_TRACER=y
+CONFIG_FTRACE_SYSCALLS=y
+CONFIG_KMEMTRACE=y
+CONFIG_FTRACE_MCOUNT_RECORD=y
+# CONFIG_FTRACE_STARTUP_TEST is not set
+# CONFIG_TRACE_BRANCH_PROFILING is not set
+CONFIG_FUNCTION_PROFILER=y
+CONFIG_RING_BUFFER_BENCHMARK=m
+CONFIG_FUNCTION_TRACER=y
+CONFIG_STACK_TRACER=y
+
+CONFIG_KPROBES=y
+CONFIG_OPTPROBES=y
+
+# CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX is not set
+
+CONFIG_HZ_1000=y
+
+CONFIG_TIMER_STATS=y
+
+# Auxillary displays
+CONFIG_KS0108=m
+CONFIG_KS0108_PORT=0x378
+CONFIG_KS0108_DELAY=2
+CONFIG_CFAG12864B=y
+CONFIG_CFAG12864B_RATE=20
+
+# CONFIG_PHANTOM is not set
+
+CONFIG_POWER_SUPPLY=m
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+# CONFIG_TEST_POWER is not set
+CONFIG_APM_POWER=m
+CONFIG_WM831X_POWER=m
+# CONFIG_BATTERY_DS2760 is not set
+# CONFIG_BATTERY_DS2782 is not set
+CONFIG_BATTERY_PMU=m
+CONFIG_BATTERY_BQ27x00=m
+CONFIG_BATTERY_MAX17040=m
+# CONFIG_PDA_POWER is not set
+
+CONFIG_AUXDISPLAY=y
+
+CONFIG_UIO=m
+CONFIG_UIO_CIF=m
+CONFIG_UIO_SMX=m
+CONFIG_UIO_PDRV=m
+CONFIG_UIO_PDRV_GENIRQ=m
+CONFIG_UIO_AEC=m
+CONFIG_UIO_SERCOS3=m
+CONFIG_UIO_PCI_GENERIC=m
+# CONFIG_UIO_NETX is not set
+
+# CONFIG_CRC7 is not set
+
+
+# LIRC
+CONFIG_LIRC_STAGING=y
+CONFIG_LIRC_BT829=m
+CONFIG_LIRC_ENE0100=m
+CONFIG_LIRC_I2C=m
+CONFIG_LIRC_IGORPLUGUSB=m
+CONFIG_LIRC_IMON=m
+CONFIG_LIRC_IT87=m
+CONFIG_LIRC_ITE8709=m
+CONFIG_LIRC_MCEUSB=m
+CONFIG_LIRC_ZILOG=m
+CONFIG_LIRC_PARALLEL=m
+CONFIG_LIRC_SERIAL=m
+CONFIG_LIRC_SERIAL_TRANSMITTER=y
+CONFIG_LIRC_SASEM=m
+CONFIG_LIRC_SIR=m
+CONFIG_LIRC_STREAMZAP=m
+CONFIG_LIRC_TTUSBIR=m
+
+# CONFIG_SAMPLES is not set
+
+# CONFIG_DEVKMEM is not set
+
+CONFIG_PM_TRACE_RTC=y
+CONFIG_R6040=m
+
+CONFIG_BNX2X=m
+CONFIG_NOZOMI=m
+# CONFIG_TPS65010 is not set
+# CONFIG_DEBUG_SECTION_MISMATCH is not set
+# CONFIG_KPROBES_SANITY_TEST is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+CONFIG_LATENCYTOP=y
+CONFIG_RESOURCE_COUNTERS=y
+# CONFIG_COMPAT_BRK is not set
+
+
+#FIXME: x86 generic?
+CONFIG_LEDS_CLEVO_MAIL=m
+CONFIG_I8K=m
+CONFIG_EDAC_I3000=m
+CONFIG_EDAC_X38=m
+CONFIG_INPUT_APANEL=m
+
+# CONFIG_INTEL_MENLOW is not set
+CONFIG_ENCLOSURE_SERVICES=m
+CONFIG_ISL29003=m
+CONFIG_IPWIRELESS=m
+CONFIG_RTC_DRV_DS1511=m
+
+# CONFIG_BLK_DEV_XIP is not set
+CONFIG_MEMSTICK=m
+# CONFIG_MEMSTICK_DEBUG is not set
+# CONFIG_MEMSTICK_UNSAFE_RESUME is not set
+CONFIG_MSPRO_BLOCK=m
+CONFIG_MEMSTICK_TIFM_MS=m
+CONFIG_MEMSTICK_JMICRON_38X=m
+
+CONFIG_ACCESSIBILITY=y
+CONFIG_A11Y_BRAILLE_CONSOLE=y
+
+# CONFIG_HTC_PASIC3 is not set
+
+# MT9V022_PCA9536_SWITCH is not set
+
+CONFIG_THERMAL_HWMON=y
+
+CONFIG_OPTIMIZE_INLINING=y
+
+# FIXME: This should be x86/ia64 only
+# CONFIG_HP_ILO is not set
+
+# CONFIG_GPIOLIB is not set
+
+
+CONFIG_NETFILTER_TPROXY=m
+CONFIG_NETFILTER_XT_TARGET_TPROXY=m
+CONFIG_NETFILTER_XT_MATCH_RECENT=m
+# CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT is not set
+CONFIG_NETFILTER_XT_MATCH_SOCKET=m
+
+# CONFIG_IP_VS_IPV6 is not set
+
+CONFIG_NET_DSA=y
+CONFIG_NET_DSA_MV88E6060=y
+CONFIG_NET_DSA_MV88E6131=y
+CONFIG_NET_DSA_MV88E6123_61_65=y
+
+CONFIG_NET_SCH_MULTIQ=m
+CONFIG_NET_ACT_SKBEDIT=m
+
+CONFIG_PHONET=m
+
+CONFIG_ICS932S401=m
+# CONFIG_C2PORT is not set
+CONFIG_W1_SLAVE_BQ27000=m
+
+
+CONFIG_IT87_WDT=m
+CONFIG_W83697UG_WDT=m
+
+# CONFIG_REGULATOR is not set
+# CONFIG_REGULATOR_DEBUG is not set
+
+CONFIG_WM8350_POWER=m
+
+# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set
+
+CONFIG_USB_WUSB=m
+CONFIG_USB_WUSB_CBAF=m
+# CONFIG_USB_WUSB_CBAF_DEBUG is not set
+CONFIG_USB_WHCI_HCD=m
+CONFIG_USB_HWA_HCD=m
+
+CONFIG_UWB=m
+CONFIG_UWB_HWA=m
+CONFIG_UWB_WHCI=m
+CONFIG_UWB_WLP=m
+CONFIG_UWB_I1480U=m
+CONFIG_UWB_I1480U_WLP=m
+
+CONFIG_STAGING=y
+# CONFIG_STAGING_EXCLUDE_BUILD is not set
+# CONFIG_ET131X is not set
+# CONFIG_SLICOSS is not set
+# CONFIG_VIDEO_TM6000 is not set
+# CONFIG_WLAGS49_H2 is not set
+# CONFIG_WLAGS49_H25 is not set
+# CONFIG_VIDEO_DT3155 is not set
+# CONFIG_TI_ST is not set
+# CONFIG_ST_BT is not set
+# CONFIG_FB_XGI is not set
+# CONFIG_VIDEO_GO7007 is not set
+# CONFIG_USB_IP_COMMON is not set
+# CONFIG_DT3155 is not set
+# CONFIG_W35UND is not set
+# CONFIG_PRISM2_USB is not set
+# CONFIG_ECHO is not set
+CONFIG_USB_ATMEL=m
+# CONFIG_POCH is not set
+# CONFIG_OTUS is not set
+# CONFIG_RT2860 is not set
+# CONFIG_RT2870 is not set
+# CONFIG_COMEDI is not set
+# CONFIG_ASUS_OLED is not set
+# CONFIG_PANEL is not set
+# CONFIG_ALTERA_PCIE_CHDMA is not set
+# CONFIG_INPUT_MIMIO is not set
+# CONFIG_TRANZPORT is not set
+# CONFIG_POHMELFS is not set
+# CONFIG_B3DFG is not set
+# CONFIG_IDE_PHISON is not set
+# CONFIG_PLAN9AUTH is not set
+# CONFIG_LINE6_USB is not set
+# CONFIG_RTL8192SU is not set
+# CONFIG_IIO is not set
+# CONFIG_VME_BUS is not set
+# CONFIG_RAR_REGISTER is not set
+# CONFIG_VT6656 is not set
+# CONFIG_USB_SERIAL_QUATECH_USB2 is not set
+# CONFIG_RTL8192E is not set
+# CONFIG_INPUT_GPIO is not set
+# CONFIG_VIDEO_CX25821 is not set
+# CONFIG_HYPERV is not set
+# CONFIG_R8187SE is not set
+# CONFIG_RTL8192U is not set
+# CONFIG_RAMZSWAP is not set
+# CONFIG_BATMAN_ADV is not set
+# CONFIG_FB_SM7XX is not set
+
+#
+# Android
+#
+
+# CONFIG_DEBUG_VIRTUAL is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_FUNCTION_GRAPH_TRACER is not set
+CONFIG_BOOT_TRACER=y
+CONFIG_EARLY_PRINTK_DBGP=y
+
+CONFIG_SECURITYFS=y
+
+CONFIG_SCSI_CXGB3_ISCSI=m
+CONFIG_LIBFC=m
+CONFIG_LIBFCOE=m
+CONFIG_FCOE=m
+CONFIG_FCOE_FNIC=m
+# CONFIG_SCSI_LPFC_DEBUG_FS is not set
+
+CONFIG_NOP_USB_XCEIV=m
+
+CONFIG_IMA=y
+CONFIG_IMA_MEASURE_PCR_IDX=10
+CONFIG_IMA_AUDIT=y
+CONFIG_IMA_LSM_RULES=y
+
+CONFIG_LSM_MMAP_MIN_ADDR=65536
+
+# CONFIG_PAGE_POISONING is not set
+
+CONFIG_SLOW_WORK=y
+CONFIG_SLOW_WORK_DEBUG=y
+
+# CONFIG_CRASH_DUMP is not set
+# CONFIG_CRASH is not set
+
+CONFIG_STRIP_ASM_SYMS=y
+
+# CONFIG_RCU_FANOUT_EXACT is not set
+CONFIG_RCU_FAST_NO_HZ=y
+
+CONFIG_KSM=y
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+
+CONFIG_FSNOTIFY=y
+
+CONFIG_IEEE802154=m
+CONFIG_IEEE802154_DRIVERS=m
+CONFIG_IEEE802154_FAKEHARD=m
+
+# CONFIG_GCOV_KERNEL is not set
+
+### is this generally useful?
+# CONFIG_PPS is not set
+# CONFIG_PPS_CLIENT_KTIMER is not set
+# CONFIG_PPS_CLIENT_LDISC is not set
+# CONFIG_PPS_DEBUG is not set
+
+# CONFIG_USB_SERIAL_QUATECH2 is not set
+# CONFIG_VT6655 is not set
+# CONFIG_FB_UDL is not set
+
+# DEBUG options that don't get enabled/disabled with 'make debug/release'
+#
+# Kmemleak still produces a lot of false positives.
+# CONFIG_DEBUG_KMEMLEAK is not set
+#
+# This generates a huge amount of dmesg spew
+# CONFIG_DEBUG_KOBJECT is not set
+#
+#
+# These debug options are deliberatly left on (even in 'make release' kernels).
+# They aren't that much of a performance impact, and the value
+# from getting useful bug-reports makes it worth leaving them on.
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_HIGHMEM=y
+CONFIG_DEBUG_SPINLOCK_SLEEP=y
+CONFIG_BOOT_PRINTK_DELAY=y
+CONFIG_DEBUG_LIST=y
+CONFIG_DEBUG_SHIRQ=y
+CONFIG_DEBUG_DEVRES=y
+CONFIG_DEBUG_RODATA_TEST=y
+CONFIG_DEBUG_NX_TEST=m
+CONFIG_DEBUG_BOOT_PARAMS=y
+CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_DETECT_HUNG_TASK=y
+# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set
+CONFIG_ATOMIC64_SELFTEST=y
+
+CONFIG_MEMORY_FAILURE=y
+CONFIG_HWPOISON_INJECT=m
+
+CONFIG_BLK_DEV_DRBD=m
+
+# CONFIG_MDIO_GPIO is not set
+# CONFIG_KEYBOARD_GPIO is not set
+# CONFIG_MOUSE_GPIO is not set
+# CONFIG_I2C_GPIO is not set
+# CONFIG_DEBUG_GPIO is not set
+# CONFIG_W1_MASTER_GPIO is not set
+# CONFIG_LEDS_GPIO is not set
+# CONFIG_GPIO_SYSFS is not set
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+# CONFIG_GPIO_CS5535 is not set
+# CONFIG_GPIO_ADP5588 is not set
+# CONFIG_GPIO_IT8761E is not set
+# CONFIG_GPIO_MAX7300 is not set
+# CONFIG_UCB1400_CORE is not set
+# CONFIG_RADIO_MIROPCM20 is not set
+# CONFIG_USB_GPIO_VBUS is not set
+# CONFIG_GPIO_SCH is not set
+# CONFIG_GPIO_LANGWELL is not set
+# CONFIG_GPIO_RDC321X is not set
+
+
+CONFIG_KSYM_TRACER=y
+CONFIG_PROFILE_KSYM_TRACER=y
+CONFIG_KPROBE_EVENT=y
+
+# CONFIG_RAMOOPS is not set
diff --git a/config-i686-PAE b/config-i686-PAE
new file mode 100644
index 000000000..1e58e65b2
--- /dev/null
+++ b/config-i686-PAE
@@ -0,0 +1,5 @@
+# CONFIG_HIGHMEM4G is not set
+CONFIG_HIGHMEM64G=y
+
+CONFIG_XEN_DEV_EVTCHN=m
+CONFIG_XEN_SYS_HYPERVISOR=y
diff --git a/config-ia64-generic b/config-ia64-generic
new file mode 100644
index 000000000..f1581b40c
--- /dev/null
+++ b/config-ia64-generic
@@ -0,0 +1,206 @@
+#
+# Automatically generated make config: don't edit
+#
+
+#
+# Processor type and features
+#
+CONFIG_IA64=y
+CONFIG_64BIT=y
+# CONFIG_XEN is not set
+CONFIG_MMU=y
+CONFIG_EFI=y
+# CONFIG_ITANIUM is not set
+CONFIG_MCKINLEY=y
+CONFIG_IA64_GENERIC=y
+# CONFIG_IA64_DIG is not set
+# CONFIG_IA64_HP_ZX1 is not set
+# CONFIG_IA64_SGI_SN2 is not set
+CONFIG_IA64_ESI=y
+CONFIG_IA64_HP_AML_NFW=y
+CONFIG_MSPEC=y
+# CONFIG_IA64_HP_SIM is not set
+# CONFIG_IA64_PAGE_SIZE_4KB is not set
+# CONFIG_IA64_PAGE_SIZE_8KB is not set
+CONFIG_IA64_PAGE_SIZE_16KB=y
+# CONFIG_IA64_PAGE_SIZE_64KB is not set
+CONFIG_IA64_L1_CACHE_SHIFT=7
+CONFIG_NUMA=y
+# CONFIG_VIRTUAL_MEM_MAP is not set
+CONFIG_SPARSEMEM_MANUAL=y
+CONFIG_SPARSEMEM=y
+CONFIG_MEMORY_HOTPLUG=y
+CONFIG_MEMORY_HOTREMOVE=y
+CONFIG_IA64_MCA_RECOVERY=m
+CONFIG_IA64_CYCLONE=y
+CONFIG_MMTIMER=y
+CONFIG_IOSAPIC=y
+CONFIG_FORCE_MAX_ZONEORDER=18
+CONFIG_NR_CPUS=1024
+# CONFIG_IA32_SUPPORT is not set
+# CONFIG_COMPAT is not set
+CONFIG_PERFMON=y
+CONFIG_IA64_PALINFO=y
+CONFIG_EFI_VARS=y
+CONFIG_SERIAL_8250_RUNTIME_UARTS=16
+CONFIG_EFI_PCDP=y
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_BLK_DEV_SGIIOC4=y
+
+#
+# Character devices
+#
+CONFIG_TCG_INFINEON=m
+
+#
+# Watchdog Cards
+#
+# CONFIG_HW_RANDOM is not set
+# CONFIG_GEN_RTC is not set
+CONFIG_EFI_RTC=y
+CONFIG_RTC_DRV_EFI=y
+
+
+#
+# AGP
+#
+CONFIG_AGP_I460=y
+CONFIG_AGP_HP_ZX1=y
+CONFIG_AGP_SGI_TIOCA=y
+
+#
+# HP Simulator drivers
+#
+# CONFIG_HP_SIMETH is not set
+# CONFIG_HP_SIMSERIAL is not set
+# CONFIG_HP_SIMSCSI is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_IA64_PRINT_HAZARDS is not set
+# CONFIG_DISABLE_VHPT is not set
+# CONFIG_IA64_DEBUG_CMPXCHG is not set
+# CONFIG_IA64_DEBUG_IRQ is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# SGI
+#
+CONFIG_SGI_SNSC=y
+CONFIG_SGI_TIOCX=y
+CONFIG_SGI_MBCS=m
+CONFIG_SGI_IOC3=m
+CONFIG_SGI_IOC4=y
+CONFIG_SGI_XP=m
+CONFIG_SGI_GRU=m
+# CONFIG_SGI_GRU_DEBUG is not set
+CONFIG_SERIAL_SGI_L1_CONSOLE=y
+CONFIG_SERIAL_SGI_IOC3=m
+CONFIG_SERIAL_SGI_IOC4=m
+
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_BUSLOGIC is not set
+
+#
+CONFIG_ACPI=y
+CONFIG_ACPI_AC=y
+# CONFIG_ACPI_ASUS is not set
+CONFIG_ACPI_PROCFS_POWER=y
+CONFIG_ACPI_SYSFS_POWER=y
+# CONFIG_ACPI_BATTERY is not set
+CONFIG_ACPI_BLACKLIST_YEAR=0
+CONFIG_ACPI_BUTTON=y
+# CONFIG_ACPI_DOCK is not set
+CONFIG_ACPI_FAN=y
+CONFIG_ACPI_HOTPLUG_MEMORY=y
+CONFIG_ACPI_NUMA=y
+CONFIG_ACPI_POWER=y
+CONFIG_ACPI_PROCESSOR=y
+CONFIG_ACPI_PROCFS=y
+CONFIG_ACPI_SLEEP=y
+CONFIG_ACPI_THERMAL=y
+# CONFIG_ACPI_TOSHIBA is not set
+CONFIG_ACPI_VIDEO=m
+# CONFIG_ACPI_PROC_EVENT is not set
+CONFIG_ACPI_HED=m
+
+CONFIG_PM=y
+CONFIG_HOTPLUG_PCI=y
+# CONFIG_HPET is not set
+# CONFIG_HOTPLUG_PCI_CPCI is not set
+CONFIG_HOTPLUG_PCI_SHPC=m
+CONFIG_HOTPLUG_PCI_SGI=m
+CONFIG_PNPACPI=y
+
+CONFIG_SCHED_SMT=y
+
+CONFIG_ARCH_DISCONTIGMEM_ENABLE=y
+
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEBUG=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=m
+CONFIG_CPU_FREQ_GOV_USERSPACE=m
+CONFIG_CPU_FREQ_GOV_ONDEMAND=m
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m
+CONFIG_CPU_FREQ_STAT=m
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+
+CONFIG_IA64_ACPI_CPUFREQ=m
+
+# CONFIG_PERMIT_BSP_REMOVE is not set
+# CONFIG_FORCE_CPEI_RETARGET is not set
+
+CONFIG_NODES_SHIFT=10
+
+
+CONFIG_HW_RANDOM_INTEL=m
+
+CONFIG_CRASH_DUMP=y
+CONFIG_PROC_VMCORE=y
+
+# drivers/media/video/usbvision/usbvision-i2c.c:64:39: error: macro "outb" passed 4 arguments, but takes just 2
+# CONFIG_VIDEO_USBVISION is not set
+
+# CONFIG_IA64_MC_ERR_INJECT is not set
+
+CONFIG_DMIID=y
+
+CONFIG_SENSORS_I5K_AMB=m
+
+CONFIG_SPARSEMEM_VMEMMAP=y
+
+CONFIG_FRAME_WARN=2048
+
+CONFIG_VIRT_CPU_ACCOUNTING=y
+CONFIG_VIRTUALIZATION=y
+CONFIG_KVM=m
+CONFIG_KVM_INTEL=m
+
+CONFIG_HP_ILO=m
+
+CONFIG_PARAVIRT_GUEST=y
+CONFIG_PARAVIRT=y
+
+CONFIG_DMAR_DEFAULT_ON=y
+
+CONFIG_RCU_FANOUT=64
+
+CONFIG_ACPI_POWER_METER=m
+CONFIG_I2C_SCMI=m
diff --git a/config-nodebug b/config-nodebug
new file mode 100644
index 000000000..5c7f4364e
--- /dev/null
+++ b/config-nodebug
@@ -0,0 +1,91 @@
+CONFIG_SND_VERBOSE_PRINTK=y
+CONFIG_SND_DEBUG=y
+CONFIG_SND_PCM_XRUN_DEBUG=y
+
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_RT_MUTEXES=y
+CONFIG_DEBUG_LOCK_ALLOC=y
+CONFIG_PROVE_LOCKING=y
+CONFIG_DEBUG_VM=y
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_PROVE_RCU=y
+# CONFIG_PROVE_RCU_REPEATEDLY is not set
+CONFIG_DEBUG_PER_CPU_MAPS=y
+CONFIG_CPUMASK_OFFSTACK=y
+
+CONFIG_CPU_NOTIFIER_ERROR_INJECT=m
+
+CONFIG_FAULT_INJECTION=y
+CONFIG_FAILSLAB=y
+CONFIG_FAIL_PAGE_ALLOC=y
+CONFIG_FAIL_MAKE_REQUEST=y
+CONFIG_FAULT_INJECTION_DEBUG_FS=y
+CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
+CONFIG_FAIL_IO_TIMEOUT=y
+
+CONFIG_SLUB_DEBUG_ON=y
+
+CONFIG_LOCK_STAT=y
+
+CONFIG_DEBUG_STACK_USAGE=y
+
+CONFIG_ACPI_DEBUG=y
+# CONFIG_ACPI_DEBUG_FUNC_TRACE is not set
+
+CONFIG_DEBUG_SG=y
+
+# CONFIG_DEBUG_PAGEALLOC is not set
+
+CONFIG_DEBUG_WRITECOUNT=y
+CONFIG_DEBUG_OBJECTS=y
+# CONFIG_DEBUG_OBJECTS_SELFTEST is not set
+CONFIG_DEBUG_OBJECTS_FREE=y
+CONFIG_DEBUG_OBJECTS_TIMERS=y
+CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT=1
+
+CONFIG_X86_PTDUMP=y
+
+CONFIG_CAN_DEBUG_DEVICES=y
+
+CONFIG_MODULE_FORCE_UNLOAD=y
+
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+
+CONFIG_DEBUG_NOTIFIERS=y
+
+CONFIG_DMA_API_DEBUG=y
+
+CONFIG_MMIOTRACE=y
+
+CONFIG_DEBUG_CREDENTIALS=y
+
+# off in both production debug and nodebug builds,
+# on in rawhide nodebug builds
+CONFIG_DEBUG_FORCE_WEAK_PER_CPU=y
+
+CONFIG_EXT4_DEBUG=y
+
+CONFIG_DEBUG_PERF_USE_VMALLOC=y
+
+CONFIG_JBD2_DEBUG=y
+
+CONFIG_DEBUG_CFQ_IOSCHED=y
+
+CONFIG_DRBD_FAULT_INJECTION=y
+
+CONFIG_ATH_DEBUG=y
+CONFIG_IWLWIFI_DEVICE_TRACING=y
+
+CONFIG_DEBUG_OBJECTS_WORK=y
+CONFIG_DEBUG_STRICT_USER_COPY_CHECKS=y
+
+CONFIG_DMADEVICES_DEBUG=y
+CONFIG_DMADEVICES_VDEBUG=y
+
+CONFIG_PM_ADVANCED_DEBUG=y
+
+CONFIG_CEPH_FS_PRETTYDEBUG=y
+CONFIG_QUOTA_DEBUG=y
+
+CONFIG_KGDB_KDB=y
+CONFIG_KDB_KEYBOARD=y
diff --git a/config-powerpc-generic b/config-powerpc-generic
new file mode 100644
index 000000000..430396dfc
--- /dev/null
+++ b/config-powerpc-generic
@@ -0,0 +1,334 @@
+# Most PowerPC kernels we build are SMP
+CONFIG_SMP=y
+CONFIG_IRQ_ALL_CPUS=y
+CONFIG_PPC=y
+CONFIG_WATCHDOG_RTAS=m
+CONFIG_DEBUGGER=y
+CONFIG_GENERIC_NVRAM=y
+CONFIG_ALTIVEC=y
+
+CONFIG_TAU=y
+# CONFIG_TAU_INT is not set
+CONFIG_TAU_AVERAGE=y
+
+CONFIG_SECCOMP=y
+
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEBUG=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=m
+CONFIG_CPU_FREQ_GOV_USERSPACE=m
+CONFIG_CPU_FREQ_GOV_ONDEMAND=m
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m
+CONFIG_CPU_FREQ_TABLE=y
+CONFIG_CPU_FREQ_STAT=m
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+
+CONFIG_PM=y
+
+CONFIG_PM_STD_PARTITION=""
+
+CONFIG_SUSPEND=y
+CONFIG_HIBERNATION=y
+# CONFIG_RTC is not set
+# CONFIG_GEN_RTC is not set
+# CONFIG_GEN_RTC_X is not set
+CONFIG_RTC_DRV_GENERIC=y
+CONFIG_PROC_DEVICETREE=y
+# CONFIG_CMDLINE_BOOL is not set
+
+CONFIG_ADB=y
+CONFIG_ADB_PMU=y
+CONFIG_WINDFARM=y
+CONFIG_WINDFARM_PM112=y
+CONFIG_I2C_POWERMAC=y
+CONFIG_APPLE_AIRPORT=m
+CONFIG_SERIAL_PMACZILOG=m
+# CONFIG_SERIAL_PMACZILOG_TTYS is not set
+CONFIG_AGP_UNINORTH=y
+CONFIG_FB_OF=y
+# CONFIG_FB_CONTROL is not set
+CONFIG_FB_IBM_GXT4500=y
+CONFIG_FB_RADEON=y
+CONFIG_FB_MATROX=y
+CONFIG_FB_NVIDIA=m
+# CONFIG_FB_VGA16 is not set
+CONFIG_FB_ATY128_BACKLIGHT=y
+CONFIG_FB_ATY_BACKLIGHT=y
+CONFIG_FB_RADEON_BACKLIGHT=y
+CONFIG_FB_RIVA_BACKLIGHT=y
+CONFIG_FB_NVIDIA_BACKLIGHT=y
+
+CONFIG_SND_POWERMAC=m
+CONFIG_SND_POWERMAC_AUTO_DRC=y
+CONFIG_SND_AOA=m
+CONFIG_SND_AOA_SOUNDBUS=m
+CONFIG_SND_AOA_FABRIC_LAYOUT=m
+CONFIG_SND_AOA_ONYX=m
+CONFIG_SND_AOA_TAS=m
+CONFIG_SND_AOA_TOONIE=m
+CONFIG_SND_AOA_SOUNDBUS_I2S=m
+
+CONFIG_XMON=y
+# CONFIG_XMON_DEFAULT is not set
+CONFIG_XMON_DISASSEMBLY=y
+
+CONFIG_BOOTX_TEXT=y
+CONFIG_MAC_EMUMOUSEBTN=y
+CONFIG_CAPI_EICON=y
+
+CONFIG_NVRAM=y
+
+# CONFIG_PCMCIA_M8XX is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_IPS is not set
+# CONFIG_NI52 is not set
+# CONFIG_NI65 is not set
+# CONFIG_LANCE is not set
+# CONFIG_3C515 is not set
+# CONFIG_ELPLUS is not set
+
+CONFIG_MEMORY_HOTPLUG=y
+
+# Stuff which wants bus_to_virt() or virt_to_bus()
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_VIDEO_STRADIS is not set
+# CONFIG_VIDEO_ZORAN is not set
+# CONFIG_ATM_HORIZON is not set
+# CONFIG_ATM_FIRESTREAM is not set
+# CONFIG_ATM_AMBASSADOR is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+
+
+# CONFIG_PPC_EARLY_DEBUG is not set
+
+# CONFIG_PMAC_BACKLIGHT_LEGACY is not set
+CONFIG_LEDS_TRIGGER_TIMER=m
+CONFIG_LEDS_TRIGGER_HEARTBEAT=m
+CONFIG_LEDS_TRIGGER_GPIO=m
+
+# FIXME: Should depend on IA64/x86
+# CONFIG_SGI_IOC4 is not set
+
+CONFIG_PPC_EFIKA=y
+CONFIG_PPC_MEDIA5200=y
+
+# CONFIG_PPC_LITE5200 is not set
+CONFIG_PPC_BESTCOMM=y
+CONFIG_PMAC_RACKMETER=m
+CONFIG_USB_OHCI_HCD_PPC_SOC=y
+CONFIG_USB_OHCI_HCD_PCI=y
+CONFIG_USB_OHCI_HCD_PPC_OF=y
+CONFIG_USB_OHCI_HCD_PPC_OF_BE=y
+CONFIG_USB_OHCI_HCD_PPC_OF_LE=y
+
+CONFIG_SERIAL_UARTLITE=m
+CONFIG_SERIAL_UARTLITE_CONSOLE=y
+
+CONFIG_SENSORS_AMS=m
+CONFIG_SENSORS_AMS_PMU=y
+CONFIG_SENSORS_AMS_I2C=y
+
+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_IDECS is not set
+CONFIG_BLK_DEV_IDECD=m
+# CONFIG_BLK_DEV_IDETAPE is not set
+CONFIG_IDE_TASK_IOCTL=y
+#
+# IDE chipset support/bugfixes
+#
+# CONFIG_IDE_GENERIC is not set
+# CONFIG_BLK_DEV_IDEPNP is not set
+# CONFIG_BLK_DEV_IDEPCI is not set
+# CONFIG_BLK_DEV_AEC62XX is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD74XX 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_HPT366 is not set
+# CONFIG_BLK_DEV_JMICRON is not set
+# CONFIG_BLK_DEV_SC1200 is not set
+# CONFIG_BLK_DEV_PIIX 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_SL82C105 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_IDE_PMAC=y
+CONFIG_BLK_DEV_IDE_PMAC_ATA100FIRST=y
+CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_BLK_DEV_HD is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+CONFIG_MTD_PHYSMAP_OF=m
+CONFIG_IDE_PROC_FS=y
+CONFIG_MACINTOSH_DRIVERS=y
+
+CONFIG_PPC_PASEMI_MDIO=m
+CONFIG_SPU_FS_64K_LS=y
+CONFIG_PPC_PASEMI_CPUFREQ=y
+CONFIG_PMAC_APM_EMU=m
+CONFIG_HW_RANDOM_PASEMI=m
+
+CONFIG_EDAC=y
+# CONFIG_EDAC_DEBUG is not set
+CONFIG_EDAC_MM_EDAC=m
+CONFIG_EDAC_PASEMI=m
+CONFIG_EDAC_AMD8131=m
+CONFIG_EDAC_AMD8111=m
+
+CONFIG_AXON_RAM=m
+CONFIG_OPROFILE_CELL=y
+
+CONFIG_SUSPEND_FREEZER=y
+# CONFIG_IDEPCI_PCIBUS_ORDER is not set
+CONFIG_PATA_PLATFORM=m
+CONFIG_PATA_OF_PLATFORM=m
+CONFIG_USB_EHCI_HCD_PPC_OF=y
+
+# CONFIG_MPC5121_ADS is not set
+# CONFIG_MPC5121_GENERIC is not set
+CONFIG_MTD_OF_PARTS=m
+# CONFIG_MTD_NAND_FSL_ELBC is not set
+CONFIG_THERMAL=y
+
+# CONFIG_MEMORY_HOTREMOVE is not set
+
+CONFIG_DMADEVICES=y
+# CONFIG_FSL_DMA is not set
+
+CONFIG_SND_PPC=y
+
+CONFIG_PPC_82xx=y
+CONFIG_PPC_83xx=y
+CONFIG_PPC_86xx=y
+CONFIG_EXTRA_TARGETS=""
+# CONFIG_CODE_PATCHING_SELFTEST is not set
+# CONFIG_FTR_FIXUP_SELFTEST is not set
+
+# CONFIG_MATH_EMULATION is not set
+# CONFIG_RAPIDIO is not set
+# CONFIG_FS_ENET is not set
+# CONFIG_UCC_GETH is not set
+# CONFIG_KEYBOARD_MATRIX is not set
+# CONFIG_SERIAL_CPM is not set
+# CONFIG_SERIAL_QE is not set
+# CONFIG_I2C_CPM is not set
+
+
+CONFIG_SERIO_XILINX_XPS_PS2=m
+
+# CONFIG_PPC_SMLPAR is not set
+
+CONFIG_MGCOGE=y
+CONFIG_GEF_SBC610=y
+CONFIG_GEF_PPC9A=y
+CONFIG_GEF_SBC310=y
+
+CONFIG_QUICC_ENGINE=y
+CONFIG_QE_GPIO=y
+CONFIG_MPC8xxx_GPIO=y
+
+CONFIG_IDE_GD=y
+CONFIG_IDE_GD_ATA=y
+CONFIG_IDE_GD_ATAPI=y
+
+CONFIG_MCU_MPC8349EMITX=m
+
+CONFIG_GPIO_XILINX=y
+
+CONFIG_PMIC_DA903X=y
+CONFIG_BACKLIGHT_DA903X=m
+CONFIG_LEDS_DA903X=m
+
+CONFIG_MSI_BITMAP_SELFTEST=y
+
+CONFIG_RELOCATABLE=y
+
+# CONFIG_HVC_UDBG is not set
+CONFIG_PRINT_STACK_DEPTH=64
+
+CONFIG_BATTERY_DA9030=m
+# CONFIG_TWL4030_CORE is not set
+
+CONFIG_BLK_DEV_IT8172=m
+CONFIG_TOUCHSCREEN_DA9034=m
+
+CONFIG_SIMPLE_GPIO=y
+
+CONFIG_FSL_PQ_MDIO=m
+
+CONFIG_PS3_VRAM=m
+CONFIG_MDIO_GPIO=m
+CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL=m
+# CONFIG_DEBUG_GPIO is not set
+CONFIG_GPIO_PCA953X=m
+CONFIG_GPIO_PCF857X=m
+
+# CONFIG_USB_FHCI_HCD is not set
+# CONFIG_FHCI_DEBUG is not set
+
+# CONFIG_DRM_RADEON_KMS is not set
+
+# CONFIG_AMIGAONE is not set
+
+CONFIG_PPC_OF_BOOT_TRAMPOLINE=y
+
+CONFIG_DTL=y
+
+CONFIG_MMC_SDHCI_OF=m
+
+# CONFIG_CONSISTENT_SIZE_BOOL is not set
+
+CONFIG_CAN_SJA1000_OF_PLATFORM=m
+
+CONFIG_PPC_EMULATED_STATS=y
+
+CONFIG_SWIOTLB=y
+
+# CONFIG_RDS is not set
+
+CONFIG_PPC_DISABLE_WERROR=y
+
+CONFIG_XILINX_LL_TEMAC=m
+CONFIG_XILINX_EMACLITE=m
+
+CONFIG_GPIO_WM831X=m
+# CONFIG_GPIO_LANGWELL is not set
+# CONFIG_GPIO_UCB1400 is not set
+CONFIG_EDAC_MPC85XX=m
+
+CONFIG_NR_IRQS=512
+CONFIG_SPARSE_IRQ=y
+
+CONFIG_PPC_MPC5200_LPBFIFO=m
+CONFIG_CAN_MSCAN=m
+CONFIG_CAN_MPC5XXX=m
+CONFIG_PATA_MACIO=m
+CONFIG_SERIAL_GRLIB_GAISLER_APBUART=m
+# CONFIG_PMIC_ADP5520 is not set
+# CONFIG_MFD_88PM8607 is not set
+# CONFIG_XPS_USB_HCD_XILINX is not set
+# CONFIG_MMC_SDHCI_OF_ESDHC is not set
+# CONFIG_MMC_SDHCI_OF_HLWD is not set
+
+# CONFIG_MFD_TC35892 is not set
+
+# CONFIG_GPIO_SCH is not set
diff --git a/config-powerpc32-generic b/config-powerpc32-generic
new file mode 100644
index 000000000..07b450ad3
--- /dev/null
+++ b/config-powerpc32-generic
@@ -0,0 +1,183 @@
+# CONFIG_SMP is not set
+CONFIG_PPC32=y
+# CONFIG_PPC64 is not set
+# CONFIG_RTAS_PROC is not set
+# CONFIG_PCMCIA_M8XX is not set
+# CONFIG_HOTPLUG_PCI is not set
+CONFIG_CPU_FREQ_PMAC=y
+CONFIG_PPC_CHRP=y
+CONFIG_PPC_PMAC=y
+CONFIG_PPC_MPC52xx=y
+CONFIG_PPC_PREP=y
+
+# CONFIG_PPC_MPC5200_SIMPLE is not set
+CONFIG_SATA_FSL=m
+# CONFIG_SATA_NV is not set
+
+# busted in .28git1
+# ERROR: "cacheable_memzero" [drivers/net/gianfar_driver.ko] undefined!
+# CONFIG_GIANFAR is not set
+CONFIG_USB_EHCI_FSL=y
+
+CONFIG_PMAC_APM_EMU=y
+CONFIG_PMAC_BACKLIGHT=y
+
+CONFIG_HIGHMEM=y
+# CONFIG_HIGHMEM_START_BOOL is not set
+# CONFIG_LOWMEM_SIZE_BOOL is not set
+# CONFIG_TASK_SIZE_BOOL is not set
+# CONFIG_KERNEL_START_BOOL is not set
+# CONFIG_PPC601_SYNC_FIX is not set
+CONFIG_ADVANCED_OPTIONS=y
+CONFIG_SCSI_MESH=m
+CONFIG_SCSI_MESH_SYNC_RATE=5
+CONFIG_SCSI_MESH_RESET_DELAY_MS=4000
+
+CONFIG_SCSI_MAC53C94=m
+CONFIG_ADB_CUDA=y
+CONFIG_ADB_MACIO=y
+CONFIG_INPUT_ADBHID=y
+CONFIG_ADB_PMU_LED=y
+CONFIG_ADB_PMU_LED_IDE=y
+
+CONFIG_PMAC_MEDIABAY=y
+CONFIG_BMAC=m
+CONFIG_MACE=m
+# CONFIG_MACE_AAUI_PORT is not set
+CONFIG_MV643XX_ETH=m
+CONFIG_I2C_HYDRA=m
+CONFIG_I2C_MPC=m
+CONFIG_THERM_WINDTUNNEL=m
+CONFIG_THERM_ADT746X=m
+# CONFIG_ANSLCD is not set
+
+CONFIG_FB_PLATINUM=y
+CONFIG_FB_VALKYRIE=y
+CONFIG_FB_CT65550=y
+# CONFIG_BDI_SWITCH is not set
+
+CONFIG_MAC_FLOPPY=m
+# CONFIG_BLK_DEV_FD is not set
+
+CONFIG_FB_ATY128=y
+CONFIG_FB_ATY=y
+CONFIG_FB_MATROX=y
+# CONFIG_KEXEC is not set
+
+# CONFIG_HVC_RTAS is not set
+
+# CONFIG_UDBG_RTAS_CONSOLE is not set
+CONFIG_BRIQ_PANEL=m
+
+# CONFIG_ATA_PIIX is not set
+# CONFIG_PATA_AMD is not set
+# CONFIG_PATA_ATIIXP is not set
+CONFIG_PATA_MPC52xx=m
+# CONFIG_PATA_MPIIX is not set
+# CONFIG_PATA_OLDPIIX is not set
+# CONFIG_PATA_OPTI is not set
+# CONFIG_PATA_SERVERWORKS is not set
+
+CONFIG_SERIAL_MPC52xx=y
+CONFIG_SERIAL_MPC52xx_CONSOLE=y
+CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD=115200
+# CONFIG_MPC5200_WDT is not set
+CONFIG_8xxx_WDT=m
+CONFIG_GEF_WDT=m
+
+CONFIG_PPC_MPC5200_BUGFIX=y
+CONFIG_FEC_MPC52xx=m
+#CHECK: This may later become a tristate.
+CONFIG_FEC_MPC52xx_MDIO=y
+CONFIG_PPC_MPC5200_GPIO=y
+CONFIG_MDIO_GPIO=m
+
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_DEBUG_STACKOVERFLOW=y
+
+# CONFIG_EMBEDDED6xx is not set
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+
+# CONFIG_BLK_DEV_PLATFORM is not set
+# CONFIG_BLK_DEV_4DRIVES is not set
+# CONFIG_BLK_DEV_ALI14XX is not set
+# CONFIG_BLK_DEV_DTC2278 is not set
+# CONFIG_BLK_DEV_HT6560B is not set
+# CONFIG_BLK_DEV_QD65XX is not set
+# CONFIG_BLK_DEV_UMC8672 is not set
+
+# CONFIG_VIRQ_DEBUG is not set
+
+CONFIG_PPC_BESTCOMM_ATA=m
+CONFIG_PPC_BESTCOMM_FEC=m
+CONFIG_PPC_BESTCOMM_GEN_BD=m
+
+CONFIG_FORCE_MAX_ZONEORDER=11
+# CONFIG_PAGE_OFFSET_BOOL is not set
+# CONFIG_FB_FSL_DIU is not set
+CONFIG_IRQSTACKS=y
+CONFIG_VIRTUALIZATION=y
+
+# CONFIG_DEBUG_GPIO is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+# CONFIG_HTC_EGPIO is not set
+
+# CONFIG_TIFM_CORE is not set
+
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_CISS_SCSI_TAPE is not set
+
+# CONFIG_I2C_NFORCE2 is not set
+
+# CONFIG_SND_INTEL8X0 is not set
+# CONFIG_SND_INTEL8X0M is not set
+
+# CONFIG_MEMSTICK is not set
+
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_TCG_TPM is not set
+
+# PPC gets sad with debug alloc (bz 448598)
+# CONFIG_DEBUG_PAGEALLOC is not set
+
+CONFIG_SND_ISA=y
+CONFIG_CRYPTO_DEV_TALITOS=m
+
+CONFIG_FSL_EMB_PERFMON=y
+CONFIG_MPC8272_ADS=y
+CONFIG_PQ2FADS=y
+CONFIG_EP8248E=y
+CONFIG_MPC831x_RDB=y
+CONFIG_MPC832x_MDS=y
+CONFIG_MPC832x_RDB=y
+CONFIG_MPC834x_MDS=y
+CONFIG_MPC834x_ITX=y
+CONFIG_MPC836x_MDS=y
+CONFIG_MPC836x_RDK=y
+CONFIG_MPC837x_MDS=y
+CONFIG_MPC837x_RDB=y
+CONFIG_SBC834x=y
+CONFIG_ASP834x=y
+CONFIG_KMETER1=y
+CONFIG_MPC8641_HPCN=y
+CONFIG_SBC8641D=y
+CONFIG_MPC8610_HPCD=y
+
+# CONFIG_USB_MUSB_HDRC is not set
+
+# busted in 2.6.27
+# drivers/mtd/maps/sbc8240.c: In function 'init_sbc8240_mtd':
+# drivers/mtd/maps/sbc8240.c:172: warning: passing argument 1 of 'simple_map_init' from incompatible pointer type
+# drivers/mtd/maps/sbc8240.c:177: error: 'struct mtd_info' has no member named 'module'
+
+CONFIG_MTD_NAND_FSL_UPM=m
+
+CONFIG_RCU_FANOUT=32
+
+CONFIG_PERF_COUNTERS=y
+CONFIG_PERF_EVENTS=y
+CONFIG_EVENT_PROFILE=y
+
+CONFIG_KVM_BOOK3S_32=m
diff --git a/config-powerpc32-smp b/config-powerpc32-smp
new file mode 100644
index 000000000..e60f59cdf
--- /dev/null
+++ b/config-powerpc32-smp
@@ -0,0 +1,4 @@
+CONFIG_SMP=y
+# CONFIG_HOTPLUG_CPU is not set
+CONFIG_NR_CPUS=4
+# CONFIG_BATTERY_PMU is not set
diff --git a/config-powerpc64 b/config-powerpc64
new file mode 100644
index 000000000..e2e8f99f0
--- /dev/null
+++ b/config-powerpc64
@@ -0,0 +1,184 @@
+CONFIG_WINDFARM_PM81=y
+CONFIG_WINDFARM_PM91=y
+CONFIG_WINDFARM_PM121=y
+CONFIG_PPC_PMAC64=y
+CONFIG_PPC_MAPLE=y
+CONFIG_PPC_CELL=y
+CONFIG_PPC_IBM_CELL_BLADE=y
+CONFIG_PPC_ISERIES=y
+CONFIG_PPC_PSERIES=y
+CONFIG_PPC_PMAC=y
+CONFIG_PPC_PASEMI=y
+# CONFIG_PPC_PASEMI_IOMMU_DMA_FORCE is not set
+CONFIG_PPC_PS3=y
+CONFIG_PPC_CELLEB=y
+CONFIG_PPC_CELL_QPACE=y
+CONFIG_PS3_HTAB_SIZE=20
+# CONFIG_PS3_DYNAMIC_DMA is not set
+CONFIG_PS3_ADVANCED=y
+CONFIG_PS3_HTAB_SIZE=20
+# CONFIG_PS3_DYNAMIC_DMA is not set
+CONFIG_PS3_VUART=y
+CONFIG_PS3_PS3AV=y
+CONFIG_PS3_STORAGE=m
+CONFIG_PS3_DISK=m
+CONFIG_PS3_ROM=m
+CONFIG_PS3_FLASH=m
+CONFIG_PS3_LPM=y
+CONFIG_SND_PS3=m
+CONFIG_SND_PS3_DEFAULT_START_DELAY=1000
+CONFIG_GELIC_NET=m
+CONFIG_GELIC_WIRELESS=y
+CONFIG_GELIC_WIRELESS_OLD_PSK_INTERFACE=y
+CONFIG_CBE_THERM=m
+CONFIG_CBE_CPUFREQ=m
+CONFIG_CBE_CPUFREQ_PMI=m
+CONFIG_CBE_CPUFREQ_PMI_ENABLE=y
+CONFIG_PMAC_RACKMETER=m
+CONFIG_IBMEBUS=y
+CONFIG_SPU_FS=m
+CONFIG_RTAS_FLASH=y
+CONFIG_PPC_SPLPAR=y
+CONFIG_SCANLOG=y
+CONFIG_LPARCFG=y
+CONFIG_SERIAL_ICOM=m
+CONFIG_HVCS=m
+CONFIG_HVC_CONSOLE=y
+CONFIG_HOTPLUG_PCI=y
+CONFIG_THERM_PM72=y
+CONFIG_IBMVETH=m
+CONFIG_SCSI_IBMVSCSI=m
+# CONFIG_HOTPLUG_PCI_CPCI is not set
+CONFIG_HOTPLUG_PCI_SHPC=m
+CONFIG_HOTPLUG_PCI_RPA=m
+CONFIG_HOTPLUG_PCI_RPA_DLPAR=y
+CONFIG_ADB_PMU_LED=y
+CONFIG_ADB_PMU_LED_IDE=y
+CONFIG_PMAC_SMU=y
+CONFIG_CPU_FREQ_PMAC64=y
+CONFIG_SCSI_IPR=m
+CONFIG_SCSI_IPR_TRACE=y
+CONFIG_SCSI_IPR_DUMP=y
+CONFIG_SPIDER_NET=m
+CONFIG_HVC_RTAS=y
+CONFIG_HVC_ISERIES=y
+CONFIG_CBE_RAS=y
+
+# iSeries device drivers
+#
+CONFIG_ISERIES_VETH=m
+CONFIG_VIODASD=m
+CONFIG_VIOCD=m
+CONFIG_VIOTAPE=m
+
+CONFIG_PASEMI_MAC=m
+CONFIG_SERIAL_OF_PLATFORM=m
+
+CONFIG_PPC_PASEMI_IOMMU=y
+CONFIG_SERIAL_TXX9=y
+CONFIG_SERIAL_TXX9_NR_UARTS=6
+CONFIG_SERIAL_TXX9_CONSOLE=y
+
+CONFIG_HVC_BEAT=y
+
+CONFIG_FB_PS3=y
+CONFIG_FB_PS3_DEFAULT_SIZE_M=18
+
+CONFIG_PPC_PMI=m
+CONFIG_PS3_SYS_MANAGER=y
+# CONFIG_BLK_DEV_CELLEB is not set
+
+CONFIG_PATA_SCC=m
+
+CONFIG_APM_EMULATION=m
+
+CONFIG_PPC64=y
+CONFIG_VIRT_CPU_ACCOUNTING=y
+CONFIG_NR_CPUS=128
+# CONFIG_FB_PLATINUM is not set
+# CONFIG_FB_VALKYRIE is not set
+# CONFIG_FB_CT65550 is not set
+# CONFIG_FB_VGA16 is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_ATY is not set
+
+# CONFIG_POWER4_ONLY is not set
+
+CONFIG_RTAS_PROC=y
+CONFIG_IOMMU_VMERGE=y
+CONFIG_NUMA=y
+# CONFIG_PPC_64K_PAGES is not set
+CONFIG_SCHED_SMT=y
+
+# CONFIG_MV643XX_ETH is not set
+CONFIG_IRQSTACKS=y
+CONFIG_DEBUG_STACKOVERFLOW=y
+# CONFIG_INPUT_PCSPKR is not set
+
+CONFIG_EHEA=m
+CONFIG_INFINIBAND_EHCA=m
+
+CONFIG_HCALL_STATS=y
+
+CONFIG_XMON_DISASSEMBLY=y
+
+CONFIG_SCSI_IBMVSCSIS=m
+
+CONFIG_SECCOMP=y
+
+CONFIG_TUNE_CELL=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+# CONFIG_BLK_DEV_PLATFORM is not set
+CONFIG_IBM_NEW_EMAC=m
+CONFIG_IBM_NEW_EMAC_RXB=128
+CONFIG_IBM_NEW_EMAC_TXB=64
+CONFIG_IBM_NEW_EMAC_POLL_WEIGHT=32
+CONFIG_IBM_NEW_EMAC_RX_COPY_THRESHOLD=256
+CONFIG_IBM_NEW_EMAC_RX_SKB_HEADROOM=0
+# CONFIG_IBM_NEW_EMAC_DEBUG is not set
+
+# CONFIG_VIRQ_DEBUG is not set
+CONFIG_ELECTRA_CF=m
+
+CONFIG_MTD_NAND_PASEMI=m
+CONFIG_EDAC_CELL=m
+CONFIG_EDAC_CPC925=m
+CONFIG_FRAME_WARN=2048
+
+CONFIG_PHYP_DUMP=y
+CONFIG_FORCE_MAX_ZONEORDER=13
+CONFIG_VIRTUALIZATION=y
+
+CONFIG_VSX=y
+
+CONFIG_SCSI_IBMVFC=m
+# CONFIG_SCSI_IBMVFC_TRACE is not set
+CONFIG_IBM_BSR=m
+
+CONFIG_SERIO_XILINX_XPS_PS2=m
+
+CONFIG_PPC_IBM_CELL_RESETBUTTON=y
+CONFIG_PPC_IBM_CELL_POWERBUTTON=m
+CONFIG_CBE_CPUFREQ_SPU_GOVERNOR=m
+
+CONFIG_RTC_DRV_PS3=y
+
+CONFIG_CRASH_DUMP=y
+CONFIG_RELOCATABLE=y
+
+CONFIG_RCU_FANOUT=64
+
+CONFIG_PERF_COUNTERS=y
+CONFIG_PERF_EVENTS=y
+CONFIG_EVENT_PROFILE=y
+
+CONFIG_KVM_BOOK3S_64=m
+# CONFIG_KVM_EXIT_TIMING is not set
+
+#-- bz#607175
+#-- active memory sharing
+CONFIG_PPC_SMLPAR=y
+CONFIG_CMM=y
+#-- DLPAR memory remove
+# CONFIG_SPARSEMEM_VMEMMAP is not set
diff --git a/config-rhel-generic b/config-rhel-generic
new file mode 100644
index 000000000..09dbf8121
--- /dev/null
+++ b/config-rhel-generic
@@ -0,0 +1,205 @@
+# CONFIG_ISA is not set
+# CONFIG_ISAPNP is not set
+# CONFIG_I2C_PCA_ISA is not set
+
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_DC390T is not set
+
+# CONFIG_ATALK is not set
+# CONFIG_DEV_APPLETALK is not set
+# CONFIG_LTPC is not set
+# CONFIG_COPS is not set
+# CONFIG_IPX is not set
+# CONFIG_IPDDP is not set
+# CONFIG_DECNET is not set
+# CONFIG_PLIP is not set
+
+# CONFIG_PCMCIA_AHA152X is not set
+# CONFIG_PCMCIA_NINJA_SCSI is not set
+# CONFIG_PCMCIA_QLOGIC is not set
+# CONFIG_PCMCIA_SYM53C500 is not set
+
+# CONFIG_EL2 is not set
+# CONFIG_ELPLUS is not set
+# CONFIG_WD80x3 is not set
+# CONFIG_I82092 is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_SUNDANCE is not set
+# CONFIG_ULTRA is not set
+# CONFIG_SKFP is not set
+# CONFIG_DE600 is not set
+# CONFIG_DE620 is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_AC3200 is not set
+# CONFIG_NI52 is not set
+# CONFIG_NI65 is not set
+# CONFIG_LANCE is not set
+# CONFIG_EL16 is not set
+# CONFIG_EL3 is not set
+# CONFIG_3C515 is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_HP100 is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_NET_SB1000 is not set
+# CONFIG_DEPCA is not set
+# CONFIG_ATP is not set
+
+# CONFIG_TR is not set
+
+# CONFIG_GAMEPORT is not set
+
+# CONFIG_SND_AD1816A is not set
+# CONFIG_SND_AD1848 is not set
+# CONFIG_SND_CS4231 is not set
+# CONFIG_SND_CS4236 is not set
+# CONFIG_SND_ES968 is not set
+# CONFIG_SND_ES1688 is not set
+# CONFIG_SND_ES18XX is not set
+# CONFIG_SND_GUSCLASSIC is not set
+# CONFIG_SND_GUSEXTREME is not set
+# CONFIG_SND_GUSMAX is not set
+# CONFIG_SND_INTERWAVE is not set
+# CONFIG_SND_INTERWAVE_STB is not set
+# CONFIG_SND_OPTI92X_AD1848 is not set
+# CONFIG_SND_OPTI92X_CS4231 is not set
+# CONFIG_SND_OPTI93X is not set
+# CONFIG_SND_MIRO is not set
+# CONFIG_SND_SB8 is not set
+# CONFIG_SND_SB16 is not set
+# CONFIG_SND_SBAWE is not set
+# CONFIG_SND_SB16_CSP is not set
+# CONFIG_SND_WAVEFRONT is not set
+# CONFIG_SND_ALS100 is not set
+# CONFIG_SND_AZT2320 is not set
+# CONFIG_SND_CMI8330 is not set
+# CONFIG_SND_DT019X is not set
+# CONFIG_SND_OPL3SA2 is not set
+# CONFIG_SND_SGALAXY is not set
+# CONFIG_SND_SSCAPE is not set
+
+# CONFIG_WAN_ROUTER is not set
+
+# CONFIG_BINFMT_AOUT is not set
+
+# CONFIG_DRM_TDFX is not set
+# CONFIG_DRM_SIS is not set
+
+# CONFIG_BLK_DEV_UMEM is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+
+# CONFIG_PARIDE is not set
+
+# CONFIG_I2O is not set
+
+# CONFIG_MWAVE is not set
+
+# CONFIG_ROCKETPORT is not set
+# CONFIG_R3964 is not set
+
+# 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_DB9 is not set
+# CONFIG_JOYSTICK_GAMECON is not set
+# CONFIG_JOYSTICK_TURBOGRAFX is not set
+
+# CONFIG_RADIO_CADET is not set
+# CONFIG_RADIO_RTRACK is not set
+# CONFIG_RADIO_RTRACK2 is not set
+# CONFIG_RADIO_AZTECH is not set
+# CONFIG_RADIO_GEMTEK is not set
+# CONFIG_RADIO_GEMTEK_PCI is not set
+# CONFIG_RADIO_MAXIRADIO is not set
+# CONFIG_RADIO_MAESTRO is not set
+# CONFIG_RADIO_MIROPCM20 is not set
+# CONFIG_RADIO_SF16FMI is not set
+# CONFIG_RADIO_SF16FMR2 is not set
+# CONFIG_RADIO_TERRATEC is not set
+# CONFIG_RADIO_TRUST is not set
+# CONFIG_RADIO_TYPHOON is not set
+# CONFIG_RADIO_ZOLTRIX is not set
+
+
+# CONFIG_VIDEO_PMS is not set
+# CONFIG_VIDEO_BWQCAM is not set
+# CONFIG_VIDEO_CQCAM is not set
+# CONFIG_VIDEO_W9966 is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA_PP is not set
+# CONFIG_VIDEO_CPIA_USB is not set
+# CONFIG_VIDEO_SAA5249 is not set
+# CONFIG_VIDEO_STRADIS is not set
+# CONFIG_VIDEO_ZORAN is not set
+# CONFIG_VIDEO_ZORAN_BUZ is not set
+# CONFIG_VIDEO_ZORAN_DC10 is not set
+# CONFIG_VIDEO_ZORAN_DC30 is not set
+# CONFIG_VIDEO_ZORAN_LML33 is not set
+# CONFIG_VIDEO_ZORAN_LML33R10 is not set
+# CONFIG_VIDEO_MEYE is not set
+# CONFIG_VIDEO_SAA7134 is not set
+# CONFIG_VIDEO_MXB is not set
+# CONFIG_VIDEO_HEXIUM_ORION is not set
+# CONFIG_VIDEO_HEXIUM_GEMINI is not set
+# CONFIG_VIDEO_CX88 is not set
+# CONFIG_VIDEO_SAA5246A is not set
+
+# CONFIG_INFTL is not set
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_PMC551 is not set
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_PCI is not set
+
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_HGA is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_TRIDENT is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_RADEON is not set
+# CONFIG_FB_NEOMAGIC is not set
+# CONFIG_FB_ASILIANT is not set
+# CONFIG_FB_HGA_ACCEL is not set
+# CONFIG_FB_3DFX_ACCEL is not set
+
+# CONFIG_JFS_FS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_9P_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+
diff --git a/config-s390x b/config-s390x
new file mode 100644
index 000000000..0d3a14b06
--- /dev/null
+++ b/config-s390x
@@ -0,0 +1,228 @@
+CONFIG_64BIT=y
+# CONFIG_MARCH_G5 is not set
+# CONFIG_MARCH_Z900 is not set
+CONFIG_MARCH_Z9_109=y
+# CONFIG_MARCH_Z990 is not set
+
+CONFIG_NR_CPUS=64
+CONFIG_COMPAT=y
+
+# See bug 496596
+CONFIG_HZ_100=y
+# CONFIG_HZ_1000 is not set
+# See bug 496605
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+
+CONFIG_MMU=y
+
+CONFIG_LOG_BUF_SHIFT=16
+CONFIG_NO_IDLE_HZ=y
+
+CONFIG_SMP=y
+
+#
+# I/O subsystem configuration
+#
+CONFIG_QDIO=m
+
+#
+# Misc
+#
+CONFIG_IPL=y
+# CONFIG_IPL_TAPE is not set
+CONFIG_IPL_VM=y
+# CONFIG_PROCESS_DEBUG is not set
+CONFIG_PFAULT=y
+CONFIG_SHARED_KERNEL=y
+CONFIG_CMM=m
+CONFIG_CMM_PROC=y
+# CONFIG_NETIUCV is not set
+CONFIG_SMSGIUCV=m
+
+#
+# SCSI low-level drivers
+#
+CONFIG_ZFCP=m
+CONFIG_ZFCPDUMP=y
+CONFIG_CCW=y
+
+#
+# S/390 block device drivers
+#
+CONFIG_DCSSBLK=m
+CONFIG_BLK_DEV_XPRAM=m
+CONFIG_DASD=m
+CONFIG_DASD_PROFILE=y
+CONFIG_DASD_ECKD=m
+CONFIG_DASD_FBA=m
+CONFIG_DASD_DIAG=m
+CONFIG_DASD_EER=y
+
+#
+# S/390 character device drivers
+#
+CONFIG_TN3270=y
+CONFIG_TN3270_CONSOLE=y
+CONFIG_TN3215=y
+CONFIG_TN3215_CONSOLE=y
+CONFIG_CCW_CONSOLE=y
+CONFIG_SCLP_TTY=y
+CONFIG_SCLP_CONSOLE=y
+CONFIG_SCLP_VT220_TTY=y
+CONFIG_SCLP_VT220_CONSOLE=y
+CONFIG_SCLP_CPI=m
+CONFIG_SCLP_ASYNC=m
+CONFIG_S390_TAPE=m
+CONFIG_S390_TAPE_3590=m
+
+CONFIG_APPLDATA_BASE=y
+CONFIG_APPLDATA_MEM=m
+CONFIG_APPLDATA_OS=m
+CONFIG_APPLDATA_NET_SUM=m
+CONFIG_TN3270_TTY=y
+CONFIG_TN3270_FS=m
+
+
+#
+# S/390 tape interface support
+#
+CONFIG_S390_TAPE_BLOCK=y
+
+#
+# S/390 tape hardware support
+#
+CONFIG_S390_TAPE_34XX=m
+
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+
+#
+# Token Ring devices
+#
+CONFIG_TR=y
+CONFIG_NETCONSOLE=m
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# S/390 network device drivers
+#
+CONFIG_LCS=m
+CONFIG_CTC=m
+CONFIG_IUCV=m
+CONFIG_QETH=m
+CONFIG_QETH_IPV6=y
+CONFIG_CCWGROUP=m
+
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_WIRELESS_EXT is not set
+# CONFIG_MAC80211 is not set
+# CONFIG_B44 is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_OSF_PARTITION is not set
+CONFIG_IBM_PARTITION=y
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+
+
+#
+# S390 crypto hw
+#
+CONFIG_CRYPTO_SHA1_S390=m
+CONFIG_CRYPTO_SHA256_S390=m
+CONFIG_CRYPTO_DES_S390=m
+CONFIG_CRYPTO_AES_S390=m
+
+#
+# Kernel hacking
+#
+
+#
+# S390 specific stack options; needs gcc 3.5 so off for now
+#
+CONFIG_PACK_STACK=y
+CONFIG_CHECK_STACK=y
+# CONFIG_WARN_STACK is not set
+# CONFIG_SMALL_STACK is not set
+
+CONFIG_ZVM_WATCHDOG=m
+CONFIG_VMLOGRDR=m
+CONFIG_MONREADER=m
+
+CONFIG_VIRT_CPU_ACCOUNTING=y
+
+# CONFIG_CLAW is not set
+
+# CONFIG_ATMEL is not set
+
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_MII is not set
+
+
+CONFIG_STACK_GUARD=256
+CONFIG_CMM_IUCV=y
+
+# CONFIG_DETECT_SOFTLOCKUP is not set
+
+CONFIG_S390_HYPFS_FS=y
+
+CONFIG_MONWRITER=m
+CONFIG_ZCRYPT=m
+CONFIG_ZCRYPT_MONOLITHIC=y
+
+CONFIG_S390_EXEC_PROTECT=y
+CONFIG_AFIUCV=m
+CONFIG_S390_PRNG=m
+
+CONFIG_S390_VMUR=m
+
+# CONFIG_THERMAL is not set
+
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_CTCM=m
+CONFIG_QETH_L2=m
+CONFIG_QETH_L3=m
+CONFIG_CRYPTO_SHA512_S390=m
+CONFIG_VIRTUALIZATION=y
+CONFIG_KVM=m
+CONFIG_S390_GUEST=y
+
+
+CONFIG_MEMORY_HOTPLUG=y
+CONFIG_MEMORY_HOTREMOVE=y
+CONFIG_CHSC_SCH=m
+
+# drivers/isdn/hardware/mISDN/hfcmulti.c:5255:2: error: #error "not running on big endian machines now"
+# CONFIG_MISDN_HFCMULTI is not set
+
+CONFIG_HVC_IUCV=y
+
+CONFIG_RCU_FANOUT=64
+
+CONFIG_SECCOMP=y
+
+CONFIG_PM=y
+CONFIG_HIBERNATION=y
+CONFIG_PM_STD_PARTITION="/dev/jokes"
+
+CONFIG_PERF_COUNTERS=y
+CONFIG_PERF_EVENTS=y
+CONFIG_EVENT_PROFILE=y
+
+CONFIG_SMSGIUCV_EVENT=m
+
+# CONFIG_PREEMPT_TRACER is not set
+
+CONFIG_VMCP=y
diff --git a/config-sparc64-generic b/config-sparc64-generic
new file mode 100644
index 000000000..1d21fa781
--- /dev/null
+++ b/config-sparc64-generic
@@ -0,0 +1,203 @@
+CONFIG_SMP=y
+CONFIG_SPARC=y
+CONFIG_SPARC64=y
+CONFIG_SECCOMP=y
+CONFIG_HZ_100=y
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=100
+
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=m
+CONFIG_CPU_FREQ_DEBUG=y
+# CONFIG_CPU_FREQ_STAT is not set
+# 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_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=m
+CONFIG_CPU_FREQ_GOV_USERSPACE=m
+CONFIG_CPU_FREQ_GOV_ONDEMAND=m
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m
+CONFIG_US3_FREQ=m
+CONFIG_US2E_FREQ=m
+
+CONFIG_SUN_LDOMS=y
+CONFIG_SCHED_SMT=y
+CONFIG_SCHED_MC=y
+CONFIG_64BIT=y
+# CONFIG_BBC_I2C is not set
+CONFIG_HUGETLB_PAGE_SIZE_4MB=y
+# CONFIG_HUGETLB_PAGE_SIZE_512K is not set
+# CONFIG_HUGETLB_PAGE_SIZE_64K is not set
+CONFIG_NR_CPUS=256
+CONFIG_US3_FREQ=m
+CONFIG_US2E_FREQ=m
+CONFIG_SUN_OPENPROMFS=m
+CONFIG_COMPAT=y
+CONFIG_UID16=y
+CONFIG_BINFMT_ELF32=y
+CONFIG_ENVCTRL=m
+CONFIG_DISPLAY7SEG=m
+CONFIG_WATCHDOG_CP1XXX=m
+CONFIG_WATCHDOG_RIO=m
+# CONFIG_CMDLINE_BOOL is not set
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+# CONFIG_PARPORT is not set
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_LIRC_PARALLEL is not set
+# CONFIG_I2C_NFORCE2 is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_SIMTEC is not set
+CONFIG_I2C_ALI1535=m
+# CONFIG_VGASTATE is not set
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_BW2 is not set
+CONFIG_FB_CG3=y
+CONFIG_FB_CG6=y
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_RADEON is not set
+CONFIG_FB_ATY=y
+# 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_3DFX is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_TRIDENT is not set
+CONFIG_FB_SBUS=y
+CONFIG_FB_FFB=y
+# CONFIG_FB_TCX is not set
+# CONFIG_FB_CG14 is not set
+CONFIG_FB_PM2=y
+CONFIG_FB_P9100=y
+# CONFIG_FB_LEO is not set
+CONFIG_FB_XVR500=y
+CONFIG_FB_XVR2500=y
+# CONFIG_VGASTATE is not set
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_CIRRUS is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_KYRO is not set
+# CONFIG_AGP is not set
+# CONFIG_DRM_NOUVEAU is not set
+# CONFIG_MDA_CONSOLE is not set
+CONFIG_FONTS=y
+# CONFIG_FONT_8x8 is not set
+# CONFIG_FONT_8x16 is not set
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_10x18 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+CONFIG_FONT_SUN8x16=y
+CONFIG_FONT_SUN12x22=y
+# CONFIG_LOGO_LINUX_CLUT224 is not set
+# CONFIG_SERIAL_8250 is not set
+CONFIG_SERIAL_SUNZILOG=y
+CONFIG_SERIAL_SUNZILOG_CONSOLE=y
+CONFIG_SERIAL_SUNSU=y
+CONFIG_SERIAL_SUNSU_CONSOLE=y
+CONFIG_SERIAL_SUNSAB=y
+CONFIG_SERIAL_SUNSAB_CONSOLE=y
+CONFIG_SERIAL_SUNHV=y
+CONFIG_SUN_OPENPROMIO=y
+CONFIG_OBP_FLASH=m
+# CONFIG_SERIO_SERPORT is not set
+CONFIG_BLK_DEV_FD=y
+CONFIG_SUNVDC=m
+CONFIG_SUNVNET=m
+# CONFIG_BLK_DEV_AEC62XX is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_BLK_DEV_PDC202XX_OLD is not set
+# CONFIG_BLK_DEV_PDC202XX_NEW is not set
+# CONFIG_BLK_DEV_SIIMAGE is not set
+# CONFIG_BLK_DEV_SLC90E66 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+CONFIG_SCSI_QLOGICPTI=m
+CONFIG_SCSI_SUNESP=m
+CONFIG_SUNLANCE=m
+CONFIG_SUNBMAC=m
+CONFIG_SUNQE=m
+# CONFIG_DM9102 is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_R8169 is not set
+CONFIG_ATM_FORE200E_USE_TASKLET=y
+CONFIG_ATM_FORE200E_DEBUG=0
+CONFIG_ATM_FORE200E_TX_RETRY=16
+# CONFIG_DRM_TDFX is not set
+CONFIG_KEYBOARD_ATKBD=y
+CONFIG_KEYBOARD_SUNKBD=y
+# CONFIG_INPUT_PCSPKR is not set
+CONFIG_INPUT_SPARCSPKR=m
+# CONFIG_SOUND_PRIME is not set
+# CONFIG_SND_SUN_AMD7930 is not set
+CONFIG_SND_SUN_CS4231=m
+# CONFIG_SND_SUN_DBRI is not set
+CONFIG_PARPORT_SUNBPP=m
+CONFIG_LOGO_SUN_CLUT224=y
+CONFIG_MTD_SUN_UFLASH=m
+CONFIG_MYRI_SBUS=m
+# CONFIG_SGI_IOC4 is not set
+# CONFIG_VIDEO_ZORAN is not set
+# CONFIG_VIDEO_STRADIS is not set
+# CONFIG_IEEE1394_SBP2 is not set
+# CONFIG_USB_NET2280 is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_DCFLUSH is not set
+# CONFIG_DEBUG_BOOTMEM is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_LOCKDEP is not set
+# CONFIG_STACK_DEBUG is not set
+
+CONFIG_SPARSEMEM_VMEMMAP=y
+
+# CONFIG_THERMAL is not set
+
+CONFIG_FRAME_WARN=2048
+
+CONFIG_NUMA=y
+
+CONFIG_SND_SPARC=y
+
+CONFIG_HW_RANDOM_N2RNG=m
+
+# drivers/isdn/hardware/mISDN/hfcmulti.c:5255:2: error: #error "not running on big endian machines now"
+# CONFIG_MISDN_HFCMULTI is not set
+
+CONFIG_US3_MC=y
+CONFIG_SENSORS_ULTRA45=m
+CONFIG_LEDS_SUNFIRE=m
+CONFIG_TADPOLE_TS102_UCTRL=m
+
+CONFIG_RCU_FANOUT=64
+
+CONFIG_LIRC_ENE0100=m
+# CONFIG_BATTERY_DS2782 is not set
+CONFIG_USB_GSPCA_SN9C20X=m
+CONFIG_USB_GSPCA_SN9C20X_EVDEV=y
+CONFIG_LSM_MMAP_MIN_ADDR=65536
+
+CONFIG_PERF_COUNTERS=y
+CONFIG_PERF_EVENTS=y
+CONFIG_EVENT_PROFILE=y
+
+CONFIG_EARLYFB=y
+CONFIG_SERIAL_GRLIB_GAISLER_APBUART=m
+
+CONFIG_GRETH=m
+CONFIG_FB_XVR1000=y
+
+CONFIG_CRYPTO_DEV_NIAGARA2=y
diff --git a/config-x86-generic b/config-x86-generic
new file mode 100644
index 000000000..58a8cf11b
--- /dev/null
+++ b/config-x86-generic
@@ -0,0 +1,484 @@
+CONFIG_UID16=y
+# CONFIG_64BIT is not set
+# CONFIG_KERNEL_LZMA is not set
+
+#
+# Processor type and features
+#
+#
+# Enable summit and co via the generic arch
+#
+CONFIG_X86_EXTENDED_PLATFORM=y
+CONFIG_X86_32_NON_STANDARD=y
+
+# CONFIG_X86_ELAN is not set
+# CONFIG_X86_NUMAQ is not set
+# CONFIG_X86_SUMMIT is not set
+CONFIG_X86_BIGSMP=y
+# CONFIG_X86_VISWS is not set
+# CONFIG_X86_RDC321X is not set
+# CONFIG_X86_ES7000 is not set
+# 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=y
+# 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_MWINCHIPC6 is not set
+# CONFIG_MWINCHIP3D is not set
+# CONFIG_MCYRIXIII is not set
+# CONFIG_MVIAC3_2 is not set
+CONFIG_SMP=y
+CONFIG_NR_CPUS=32
+CONFIG_X86_GENERIC=y
+# CONFIG_X86_PPRO_FENCE is not set
+CONFIG_HPET=y
+CONFIG_HPET_TIMER=y
+# CONFIG_HPET_MMAP is not set
+CONFIG_X86_MCE=y
+CONFIG_TOSHIBA=m
+CONFIG_I8K=m
+CONFIG_SONYPI=m
+CONFIG_SONYPI_COMPAT=y
+CONFIG_MICROCODE=m
+CONFIG_X86_MSR=y
+CONFIG_X86_CPUID=y
+CONFIG_EDD=m
+# CONFIG_EDD_OFF is not set
+# CONFIG_NUMA is not set
+
+# CONFIG_NOHIGHMEM is not set
+CONFIG_HIGHMEM4G=y
+# CONFIG_HIGHMEM64G is not set
+CONFIG_HIGHMEM=y
+CONFIG_HIGHPTE=y
+
+# CONFIG_MATH_EMULATION is not set
+CONFIG_MTRR=y
+CONFIG_X86_PAT=y
+CONFIG_X86_PM_TIMER=y
+
+CONFIG_EFI=y
+CONFIG_EFI_VARS=y
+CONFIG_EFI_PCDP=y
+CONFIG_FB_EFI=y
+# CONFIG_FB_N411 is not set
+
+CONFIG_DMAR=y
+CONFIG_DMAR_BROKEN_GFX_WA=y
+CONFIG_DMAR_FLOPPY_WA=y
+CONFIG_DMAR_DEFAULT_ON=y
+
+CONFIG_FB_GEODE=y
+CONFIG_FB_GEODE_LX=y
+CONFIG_FB_GEODE_GX=y
+# CONFIG_FB_GEODE_GX1 is not set
+
+# CONFIG_PCI_GOBIOS is not set
+# CONFIG_PCI_GODIRECT is not set
+# CONFIG_PCI_GOMMCONFIG is not set
+CONFIG_PCI_GOANY=y
+
+#
+# x86 specific drivers
+#
+CONFIG_PCMCIA_FDOMAIN=m
+CONFIG_SCSI_FUTURE_DOMAIN=m
+CONFIG_SCSI_ADVANSYS=m
+
+CONFIG_CC_STACKPROTECTOR=y
+
+CONFIG_SECCOMP=y
+
+CONFIG_CAPI_EICON=y
+
+#
+# APM (Advanced Power Management) BIOS Support
+#
+CONFIG_APM=y
+# CONFIG_APM_IGNORE_USER_SUSPEND is not set
+# CONFIG_APM_DO_ENABLE is not set
+CONFIG_APM_CPU_IDLE=y
+# CONFIG_APM_DISPLAY_BLANK is not set
+# CONFIG_APM_ALLOW_INTS is not set
+
+#
+# Kernel debugging
+#
+CONFIG_X86_MPPARSE=y
+
+CONFIG_ACPI=y
+CONFIG_ACPI_AC=y
+# CONFIG_ACPI_ASUS is not set
+CONFIG_ACPI_PROCFS_POWER=y
+CONFIG_ACPI_SYSFS_POWER=y
+CONFIG_ACPI_BATTERY=y
+CONFIG_ACPI_BLACKLIST_YEAR=1999
+CONFIG_ACPI_BUTTON=y
+CONFIG_ACPI_CONTAINER=m
+CONFIG_ACPI_DOCK=y
+CONFIG_ACPI_FAN=y
+CONFIG_ACPI_NUMA=y
+CONFIG_ACPI_PROCESSOR=y
+CONFIG_ACPI_POWER=y
+CONFIG_ACPI_PROCFS=y
+CONFIG_ACPI_SBS=m
+CONFIG_ACPI_SLEEP=y
+CONFIG_ACPI_THERMAL=y
+CONFIG_TOPSTAR_LAPTOP=m
+CONFIG_ACPI_TOSHIBA=m
+CONFIG_ACPI_VIDEO=m
+# CONFIG_ACPI_PROC_EVENT is not set
+CONFIG_PNPACPI=y
+CONFIG_ACPI_POWER_METER=m
+CONFIG_ACPI_PROCESSOR_AGGREGATOR=m
+CONFIG_ACPI_HED=m
+CONFIG_ACPI_APEI=y
+CONFIG_ACPI_APEI_GHES=m
+# CONFIG_ACPI_APEI_EINJ is not set
+
+#
+# CPUFreq processor drivers
+#
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEBUG=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=m
+CONFIG_CPU_FREQ_GOV_USERSPACE=m
+CONFIG_CPU_FREQ_GOV_ONDEMAND=m
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m
+CONFIG_CPU_FREQ_TABLE=y
+CONFIG_CPU_FREQ_STAT=m
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+
+CONFIG_X86_ACPI_CPUFREQ=m
+CONFIG_X86_PCC_CPUFREQ=m
+# CONFIG_X86_POWERNOW_K6 is not set
+CONFIG_X86_POWERNOW_K7=y
+CONFIG_X86_POWERNOW_K8=m
+# CONFIG_X86_GX_SUSPMOD is not set
+# CONFIG_X86_SPEEDSTEP_CENTRINO is not set
+CONFIG_X86_SPEEDSTEP_ICH=y
+CONFIG_X86_SPEEDSTEP_SMI=y
+CONFIG_X86_SPEEDSTEP_LIB=y
+# CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK is not set
+CONFIG_X86_P4_CLOCKMOD=m
+CONFIG_X86_LONGRUN=y
+# CONFIG_X86_LONGHAUL is not set
+# CONFIG_X86_CPUFREQ_NFORCE2 is not set
+# e_powersaver is dangerous
+# CONFIG_X86_E_POWERSAVER is not set
+
+CONFIG_X86_HT=y
+CONFIG_X86_TRAMPOLINE=y
+
+#
+# various x86 specific drivers
+#
+CONFIG_NVRAM=y
+CONFIG_IBM_ASM=m
+CONFIG_CRYPTO_AES_586=m
+CONFIG_CRYPTO_TWOFISH_586=m
+CONFIG_CRYPTO_DEV_PADLOCK=m
+CONFIG_CRYPTO_DEV_PADLOCK_AES=m
+CONFIG_CRYPTO_DEV_PADLOCK_SHA=m
+
+CONFIG_GENERIC_ISA_DMA=y
+CONFIG_SCHED_SMT=y
+CONFIG_SUSPEND=y
+CONFIG_HIBERNATION=y
+CONFIG_PM_STD_PARTITION=""
+
+CONFIG_DEBUG_RODATA=y
+CONFIG_DEBUG_STACKOVERFLOW=y
+# CONFIG_4KSTACKS is not set
+CONFIG_DEBUG_NMI_TIMEOUT=5
+
+CONFIG_PCI_DIRECT=y
+CONFIG_PCI_MMCONFIG=y
+CONFIG_PCI_BIOS=y
+
+CONFIG_HOTPLUG_PCI=y
+CONFIG_HOTPLUG_PCI_COMPAQ=m
+# CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set
+CONFIG_HOTPLUG_PCI_IBM=m
+# CONFIG_HOTPLUG_PCI_CPCI is not set
+# SHPC has half-arsed PCI probing, which makes it load on too many systems
+# CONFIG_HOTPLUG_PCI_SHPC is not set
+CONFIG_PM=y
+
+CONFIG_IPW2100=m
+CONFIG_IPW2100_MONITOR=y
+CONFIG_IPW2200=m
+CONFIG_IPW2200_MONITOR=y
+CONFIG_IPW2200_RADIOTAP=y
+CONFIG_IPW2200_PROMISCUOUS=y
+CONFIG_IPW2200_QOS=y
+
+CONFIG_BLK_DEV_AMD74XX=y
+
+CONFIG_I2C_ALI1535=m
+CONFIG_I2C_ALI15X3=m
+CONFIG_I2C_ALI1563=m
+CONFIG_I2C_AMD756=m
+CONFIG_I2C_AMD756_S4882=m
+CONFIG_I2C_AMD8111=m
+CONFIG_I2C_I801=m
+CONFIG_I2C_ISCH=m
+CONFIG_I2C_NFORCE2=m
+CONFIG_I2C_NFORCE2_S4985=m
+CONFIG_I2C_PIIX4=m
+CONFIG_I2C_SIS5595=m
+CONFIG_I2C_SIS630=m
+CONFIG_I2C_SIS96X=m
+
+CONFIG_I2C_VIA=m
+CONFIG_I2C_VIAPRO=m
+
+CONFIG_SCx200_ACB=m
+
+# CONFIG_X86_REBOOTFIXUPS is not set
+
+CONFIG_DELL_RBU=m
+CONFIG_DCDBAS=m
+
+CONFIG_GPIO_SCH=m
+CONFIG_PC8736x_GPIO=m
+# CONFIG_NSC_GPIO is not set
+CONFIG_CS5535_GPIO=m
+
+CONFIG_EDAC=y
+# CONFIG_EDAC_DEBUG is not set
+CONFIG_EDAC_MM_EDAC=m
+CONFIG_EDAC_AMD76X=m
+CONFIG_EDAC_E7XXX=m
+CONFIG_EDAC_E752X=m
+CONFIG_EDAC_I82860=m
+CONFIG_EDAC_I82875P=m
+CONFIG_EDAC_I82975X=m
+CONFIG_EDAC_I3000=m
+CONFIG_EDAC_I5000=m
+CONFIG_EDAC_I5100=m
+CONFIG_EDAC_I5400=m
+CONFIG_EDAC_R82600=m
+CONFIG_EDAC_AMD8131=m
+CONFIG_EDAC_AMD8111=m
+CONFIG_EDAC_I7CORE=m
+
+CONFIG_SCHED_MC=y
+
+CONFIG_SND_ISA=y
+CONFIG_SND_ES18XX=m
+
+CONFIG_TCG_INFINEON=m
+
+CONFIG_HW_RANDOM_INTEL=m
+CONFIG_HW_RANDOM_AMD=m
+CONFIG_HW_RANDOM_GEODE=m
+CONFIG_HW_RANDOM_VIA=m
+
+
+# CONFIG_COMPAT_VDSO is not set
+
+# CONFIG_SGI_IOC4 is not set
+
+CONFIG_X86_PLATFORM_DEVICES=y
+CONFIG_ASUS_LAPTOP=m
+CONFIG_COMPAL_LAPTOP=m
+CONFIG_EEEPC_LAPTOP=m
+CONFIG_EEEPC_WMI=m
+CONFIG_FUJITSU_LAPTOP=m
+# CONFIG_FUJITSU_LAPTOP_DEBUG is not set
+CONFIG_MSI_LAPTOP=m
+CONFIG_SONY_LAPTOP=m
+CONFIG_DELL_LAPTOP=m
+CONFIG_ACPI_WMI=m
+CONFIG_ACER_WMI=m
+CONFIG_ACERHDF=m
+CONFIG_TC1100_WMI=m
+CONFIG_HP_WMI=m
+# CONFIG_INTEL_SCU_IPC is not set
+CONFIG_DELL_WMI=m
+
+# CONFIG_TOUCHSCREEN_INTEL_MID is not set
+
+# CONFIG_SMSC37B787_WDT is not set
+CONFIG_W83697HF_WDT=m
+CONFIG_IB700_WDT=m
+
+CONFIG_RELOCATABLE=y
+CONFIG_PHYSICAL_ALIGN=0x400000
+CONFIG_PHYSICAL_START=0x400000
+CONFIG_CRASH_DUMP=y
+# CONFIG_KEXEC_JUMP is not set
+CONFIG_PROC_VMCORE=y
+CONFIG_CRASH=m
+
+CONFIG_CRYPTO_DEV_GEODE=m
+
+CONFIG_VIDEO_CAFE_CCIC=m
+
+CONFIG_VIRTUALIZATION=y
+CONFIG_KVM=m
+CONFIG_KVM_INTEL=m
+CONFIG_KVM_AMD=m
+CONFIG_LGUEST=m
+
+CONFIG_PARAVIRT_GUEST=y
+CONFIG_PARAVIRT=y
+# CONFIG_PARAVIRT_DEBUG is not set
+
+# PARAVIRT_SPINLOCKS has a 5% perf hit
+# CONFIG_PARAVIRT_SPINLOCKS is not set
+CONFIG_KVM_CLOCK=y
+CONFIG_KVM_GUEST=y
+CONFIG_LGUEST_GUEST=y
+CONFIG_VMI=y
+
+CONFIG_XEN=y
+CONFIG_XEN_MAX_DOMAIN_MEMORY=8
+CONFIG_XEN_BALLOON=y
+CONFIG_XEN_SCRUB_PAGES=y
+CONFIG_XEN_SAVE_RESTORE=y
+CONFIG_HVC_XEN=y
+CONFIG_XEN_FBDEV_FRONTEND=y
+CONFIG_XEN_KBDDEV_FRONTEND=y
+CONFIG_XEN_BLKDEV_FRONTEND=m
+CONFIG_XEN_NETDEV_FRONTEND=m
+CONFIG_XENFS=m
+CONFIG_XEN_COMPAT_XENFS=y
+
+CONFIG_MTD_ESB2ROM=m
+CONFIG_MTD_CK804XROM=m
+CONFIG_MTD_NAND_CAFE=m
+
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_CPU_IDLE=y
+# CONFIG_CPU_IDLE_GOV_LADDER is not set
+CONFIG_CPU_IDLE_GOV_MENU=y
+
+CONFIG_THINKPAD_ACPI=m
+# CONFIG_THINKPAD_ACPI_DEBUG is not set
+# CONFIG_THINKPAD_ACPI_DEBUGFACILITIES is not set
+CONFIG_THINKPAD_ACPI_HOTKEY_POLL=y
+CONFIG_THINKPAD_ACPI_VIDEO=y
+CONFIG_THINKPAD_ACPI_ALSA_SUPPORT=y
+# CONFIG_THINKPAD_ACPI_UNSAFE_LEDS is not set
+
+CONFIG_MACINTOSH_DRIVERS=y
+
+CONFIG_DMIID=y
+CONFIG_ISCSI_IBFT_FIND=y
+CONFIG_ISCSI_IBFT=m
+
+CONFIG_DMADEVICES=y
+CONFIG_INTEL_IOATDMA=m
+
+CONFIG_SENSORS_I5K_AMB=m
+
+# CONFIG_CPA_DEBUG is not set
+# CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set
+
+CONFIG_HP_WATCHDOG=m
+
+CONFIG_OLPC=y
+CONFIG_BATTERY_OLPC=y
+CONFIG_MOUSE_PS2_OLPC=y
+
+CONFIG_STRICT_DEVMEM=y
+
+# CONFIG_NO_BOOTMEM is not set
+
+# CONFIG_MEMTEST is not set
+# CONFIG_MAXSMP is not set
+CONFIG_MTRR_SANITIZER=y
+CONFIG_MTRR_SANITIZER_ENABLE_DEFAULT=1
+CONFIG_MTRR_SANITIZER_SPARE_REG_NR_DEFAULT=1
+CONFIG_SYSPROF_TRACER=y
+
+# CONFIG_X86_VERBOSE_BOOTUP is not set
+# CONFIG_MMIOTRACE_TEST is not set
+
+# CONFIG_DEBUG_PER_CPU_MAPS is not set
+
+CONFIG_HP_ILO=m
+
+CONFIG_BACKLIGHT_MBP_NVIDIA=m
+
+CONFIG_OPROFILE_IBS=y
+CONFIG_MICROCODE_INTEL=y
+CONFIG_MICROCODE_AMD=y
+
+# CONFIG_X86_CHECK_BIOS_CORRUPTION is not set
+CONFIG_X86_RESERVE_LOW_64K=y
+
+# CONFIG_CMDLINE_BOOL is not set
+
+CONFIG_PANASONIC_LAPTOP=m
+
+CONFIG_XEN_DEBUG_FS=y
+CONFIG_X86_PTRACE_BTS=y
+
+CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS=y
+
+CONFIG_POWER_TRACER=y
+CONFIG_HW_BRANCH_TRACER=y
+
+# CONFIG_SPARSE_IRQ is not set
+
+CONFIG_RCU_FANOUT=32
+
+# CONFIG_IOMMU_STRESS is not set
+
+CONFIG_PERF_COUNTERS=y
+CONFIG_PERF_EVENTS=y
+CONFIG_EVENT_PROFILE=y
+
+CONFIG_X86_MCE_INTEL=y
+CONFIG_X86_MCE_AMD=y
+# CONFIG_X86_ANCIENT_MCE is not set
+# CONFIG_X86_MCE_INJECT is not set
+
+# CONFIG_X86_MRST is not set
+CONFIG_SFI=y
+
+CONFIG_INPUT_WINBOND_CIR=m
+CONFIG_I2C_SCMI=m
+CONFIG_SBC_FITPC2_WATCHDOG=m
+CONFIG_EDAC_I3200=m
+CONFIG_EDAC_DECODE_MCE=m
+
+CONFIG_GPIO_LANGWELL=y
+
+# CONFIG_INTEL_TXT is not set
+
+CONFIG_CS5535_MFGPT=m
+CONFIG_GEODE_WDT=m
+CONFIG_CS5535_CLOCK_EVENT_SRC=m
+
+CONFIG_LEDS_INTEL_SS4200=m
+
+CONFIG_X86_DECODER_SELFTEST=y
+
+CONFIG_ACPI_CMPC=m
+CONFIG_MSI_WMI=m
+CONFIG_TOSHIBA_BT_RFKILL=m
+# CONFIG_SAMSUNG_LAPTOP is not set
+
+CONFIG_VGA_SWITCHEROO=y
+CONFIG_LPC_SCH=m
+
+CONFIG_INTEL_IDLE=m
+
+CONFIG_PCI_CNB20LE_QUIRK=y
diff --git a/config-x86_64-generic b/config-x86_64-generic
new file mode 100644
index 000000000..204dfff62
--- /dev/null
+++ b/config-x86_64-generic
@@ -0,0 +1,409 @@
+CONFIG_64BIT=y
+CONFIG_UID16=y
+# CONFIG_KERNEL_LZMA is not set
+
+# CONFIG_MK8 is not set
+# CONFIG_MPSC is not set
+CONFIG_GENERIC_CPU=y
+CONFIG_X86_EXTENDED_PLATFORM=y
+# CONFIG_X86_VSMP is not set
+# CONFIG_X86_UV is not set
+CONFIG_X86_MSR=y
+CONFIG_X86_CPUID=y
+CONFIG_MTRR=y
+CONFIG_NUMA=y
+CONFIG_K8_NUMA=y
+CONFIG_X86_64_ACPI_NUMA=y
+# CONFIG_NUMA_EMU is not set
+CONFIG_NR_CPUS=512
+CONFIG_X86_POWERNOW_K8=m
+CONFIG_X86_P4_CLOCKMOD=m
+CONFIG_IA32_EMULATION=y
+# CONFIG_IA32_AOUT is not set
+# CONFIG_IOMMU_DEBUG is not set
+CONFIG_DEBUG_RODATA=y
+CONFIG_MICROCODE=m
+CONFIG_SWIOTLB=y
+CONFIG_CALGARY_IOMMU=y
+CONFIG_CALGARY_IOMMU_ENABLED_BY_DEFAULT=y
+CONFIG_X86_PM_TIMER=y
+CONFIG_EDD=m
+# CONFIG_EDD_OFF is not set
+CONFIG_PCI_BIOS=y
+CONFIG_PCI_MMCONFIG=y
+CONFIG_DMAR=y
+CONFIG_DMAR_BROKEN_GFX_WA=y
+CONFIG_DMAR_FLOPPY_WA=y
+CONFIG_DMAR_DEFAULT_ON=y
+
+CONFIG_KEXEC_JUMP=y
+
+CONFIG_EFI=y
+CONFIG_EFI_VARS=y
+CONFIG_EFI_PCDP=y
+CONFIG_FB_EFI=y
+
+CONFIG_SECCOMP=y
+
+CONFIG_CAPI_EICON=y
+
+CONFIG_GENERIC_ISA_DMA=y
+CONFIG_SCHED_SMT=y
+CONFIG_SUSPEND=y
+CONFIG_HIBERNATION=y
+CONFIG_PM_STD_PARTITION=""
+
+CONFIG_CPU_FREQ=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=m
+CONFIG_CPU_FREQ_GOV_USERSPACE=m
+CONFIG_CPU_FREQ_GOV_ONDEMAND=m
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m
+CONFIG_CPU_FREQ_TABLE=y
+CONFIG_CPU_FREQ_DEBUG=y
+# CONFIG_X86_SPEEDSTEP_CENTRINO is not set
+CONFIG_X86_PCC_CPUFREQ=m
+CONFIG_X86_ACPI_CPUFREQ=m
+CONFIG_CPU_FREQ_STAT=m
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+
+CONFIG_ACPI=y
+CONFIG_ACPI_AC=y
+# CONFIG_ACPI_ASUS is not set
+CONFIG_ACPI_PROCFS_POWER=y
+CONFIG_ACPI_SYSFS_POWER=y
+CONFIG_ACPI_BATTERY=y
+CONFIG_ACPI_BLACKLIST_YEAR=0
+CONFIG_ACPI_BUTTON=y
+CONFIG_ACPI_CONTAINER=m
+CONFIG_ACPI_DOCK=y
+CONFIG_ACPI_FAN=y
+CONFIG_ACPI_HOTPLUG_MEMORY=m
+CONFIG_ACPI_NUMA=y
+CONFIG_ACPI_PROCESSOR=y
+CONFIG_ACPI_PROCFS=y
+CONFIG_ACPI_SBS=m
+CONFIG_ACPI_SLEEP=y
+CONFIG_ACPI_THERMAL=y
+CONFIG_ACPI_TOSHIBA=m
+CONFIG_ACPI_POWER=y
+CONFIG_ACPI_VIDEO=m
+# CONFIG_ACPI_PROC_EVENT is not set
+CONFIG_ACPI_POWER_METER=m
+CONFIG_ACPI_PROCESSOR_AGGREGATOR=m
+CONFIG_ACPI_HED=m
+CONFIG_ACPI_APEI=y
+CONFIG_ACPI_APEI_GHES=m
+# CONFIG_ACPI_APEI_EINJ is not set
+
+CONFIG_X86_PLATFORM_DEVICES=y
+CONFIG_ASUS_LAPTOP=m
+CONFIG_COMPAL_LAPTOP=m
+CONFIG_FUJITSU_LAPTOP=m
+# CONFIG_FUJITSU_LAPTOP_DEBUG is not set
+CONFIG_MSI_LAPTOP=m
+CONFIG_SONY_LAPTOP=m
+CONFIG_SONYPI_COMPAT=y
+CONFIG_EEEPC_LAPTOP=m
+CONFIG_EEEPC_WMI=m
+CONFIG_DELL_LAPTOP=m
+CONFIG_ACPI_WMI=m
+CONFIG_ACER_WMI=m
+CONFIG_ACERHDF=m
+CONFIG_HP_WMI=m
+CONFIG_DELL_WMI=m
+# CONFIG_INTEL_SCU_IPC is not set
+
+# CONFIG_TOUCHSCREEN_INTEL_MID is not set
+
+CONFIG_THINKPAD_ACPI=m
+# CONFIG_THINKPAD_ACPI_DEBUG is not set
+# CONFIG_THINKPAD_ACPI_DEBUGFACILITIES is not set
+CONFIG_THINKPAD_ACPI_HOTKEY_POLL=y
+CONFIG_THINKPAD_ACPI_VIDEO=y
+CONFIG_THINKPAD_ACPI_ALSA_SUPPORT=y
+# CONFIG_THINKPAD_ACPI_UNSAFE_LEDS is not set
+
+CONFIG_HOTPLUG_PCI=y
+CONFIG_HOTPLUG_PCI_COMPAQ=m
+# CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set
+CONFIG_HOTPLUG_PCI_IBM=m
+# CONFIG_HOTPLUG_PCI_CPCI is not set
+# SHPC has half-arsed PCI probing, which makes it load on too many systems
+CONFIG_HOTPLUG_PCI_SHPC=m
+
+CONFIG_HPET=y
+# CONFIG_HPET_MMAP is not set
+CONFIG_PM=y
+
+CONFIG_IPW2100=m
+CONFIG_IPW2100_MONITOR=y
+CONFIG_IPW2200=m
+CONFIG_IPW2200_MONITOR=y
+CONFIG_IPW2200_RADIOTAP=y
+CONFIG_IPW2200_PROMISCUOUS=y
+CONFIG_IPW2200_QOS=y
+
+CONFIG_PNP=y
+CONFIG_PNPACPI=y
+
+CONFIG_BLK_DEV_AMD74XX=y
+CONFIG_CRYPTO_DEV_PADLOCK=m
+CONFIG_CRYPTO_DEV_PADLOCK_AES=m
+CONFIG_CRYPTO_DEV_PADLOCK_SHA=m
+# CONFIG_CRYPTO_AES is not set
+CONFIG_CRYPTO_AES_X86_64=m
+# CONFIG_CRYPTO_TWOFISH is not set
+CONFIG_CRYPTO_TWOFISH_X86_64=m
+# CONFIG_CRYPTO_SALSA20 is not set
+CONFIG_CRYPTO_SALSA20_X86_64=m
+CONFIG_CRYPTO_AES_NI_INTEL=m
+
+CONFIG_X86_MCE=y
+CONFIG_X86_MCE_INTEL=y
+CONFIG_X86_MCE_AMD=y
+
+# CONFIG_I2C_ALI1535 is not set
+# CONFIG_I2C_ALI1563 is not set
+# CONFIG_I2C_ALI15X3 is not set
+CONFIG_I2C_AMD756=m
+CONFIG_I2C_AMD756_S4882=m
+CONFIG_I2C_AMD8111=m
+CONFIG_I2C_I801=m
+CONFIG_I2C_ISCH=m
+CONFIG_I2C_NFORCE2_S4985=m
+CONFIG_I2C_PIIX4=m
+# CONFIG_I2C_SIS5595 is not set
+# CONFIG_I2C_SIS630 is not set
+
+CONFIG_I2C_SIS96X=m
+CONFIG_I2C_VIA=m
+CONFIG_I2C_VIAPRO=m
+
+CONFIG_DELL_RBU=m
+CONFIG_DCDBAS=m
+
+CONFIG_NVRAM=y
+
+CONFIG_EDAC=y
+# CONFIG_EDAC_DEBUG is not set
+CONFIG_EDAC_MM_EDAC=m
+CONFIG_EDAC_AMD76X=m
+CONFIG_EDAC_E7XXX=m
+CONFIG_EDAC_E752X=m
+CONFIG_EDAC_I5000=m
+CONFIG_EDAC_I5100=m
+CONFIG_EDAC_I5400=m
+CONFIG_EDAC_I82875P=m
+CONFIG_EDAC_I82860=m
+CONFIG_EDAC_I82975X=m
+CONFIG_EDAC_R82600=m
+CONFIG_EDAC_AMD8131=m
+CONFIG_EDAC_AMD8111=m
+CONFIG_EDAC_AMD64=m
+# CONFIG_EDAC_AMD64_ERROR_INJECTION is not set
+CONFIG_EDAC_DECODE_MCE=m
+CONFIG_EDAC_I7CORE=m
+
+CONFIG_SCHED_MC=y
+
+CONFIG_TCG_INFINEON=m
+
+CONFIG_HW_RANDOM_INTEL=m
+CONFIG_HW_RANDOM_AMD=m
+CONFIG_HW_RANDOM_VIA=m
+
+# CONFIG_HW_RANDOM_GEODE is not set
+
+
+CONFIG_DEBUG_STACKOVERFLOW=y
+CONFIG_DEBUG_NMI_TIMEOUT=5
+
+CONFIG_GPIO_SCH=m
+# CONFIG_PC8736x_GPIO is not set
+
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+CONFIG_SPARSEMEM_MANUAL=y
+CONFIG_SPARSEMEM=y
+CONFIG_HAVE_MEMORY_PRESENT=y
+CONFIG_SPARSEMEM_EXTREME=y
+CONFIG_SPARSEMEM_VMEMMAP=y
+CONFIG_MEMORY_HOTPLUG=y
+CONFIG_MEMORY_HOTREMOVE=y
+
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+# CONFIG_BLK_DEV_TRIFLEX 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_CC_STACKPROTECTOR=y
+
+CONFIG_SGI_IOC4=m
+CONFIG_SGI_XP=m
+CONFIG_SGI_GRU=m
+# CONFIG_SGI_GRU_DEBUG is not set
+
+# CONFIG_SMSC37B787_WDT is not set
+CONFIG_W83697HF_WDT=m
+
+# CONFIG_VIDEO_CAFE_CCIC is not set
+
+CONFIG_MTD_ESB2ROM=m
+CONFIG_MTD_CK804XROM=m
+
+CONFIG_RELOCATABLE=y
+CONFIG_MACINTOSH_DRIVERS=y
+
+CONFIG_CRASH_DUMP=y
+CONFIG_PHYSICAL_START=0x1000000
+CONFIG_PROC_VMCORE=y
+CONFIG_CRASH=m
+
+CONFIG_DMIID=y
+CONFIG_ISCSI_IBFT_FIND=y
+CONFIG_ISCSI_IBFT=m
+
+
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_CPU_IDLE=y
+# CONFIG_CPU_IDLE_GOV_LADDER is not set
+CONFIG_CPU_IDLE_GOV_MENU=y
+
+CONFIG_VIRTUALIZATION=y
+CONFIG_KVM=m
+CONFIG_KVM_INTEL=m
+CONFIG_KVM_AMD=m
+
+CONFIG_PARAVIRT_GUEST=y
+CONFIG_PARAVIRT=y
+# CONFIG_PARAVIRT_DEBUG is not set
+# PARAVIRT_SPINLOCKS has a 5% perf hit
+# CONFIG_PARAVIRT_SPINLOCKS is not set
+CONFIG_KVM_CLOCK=y
+CONFIG_KVM_GUEST=y
+
+CONFIG_XEN=y
+CONFIG_XEN_MAX_DOMAIN_MEMORY=32
+CONFIG_XEN_BALLOON=y
+CONFIG_XEN_SCRUB_PAGES=y
+CONFIG_XEN_SAVE_RESTORE=y
+CONFIG_HVC_XEN=y
+CONFIG_XEN_FBDEV_FRONTEND=y
+CONFIG_XEN_KBDDEV_FRONTEND=y
+CONFIG_XEN_BLKDEV_FRONTEND=m
+CONFIG_XEN_NETDEV_FRONTEND=m
+CONFIG_XENFS=m
+CONFIG_XEN_COMPAT_XENFS=y
+CONFIG_XEN_DEV_EVTCHN=m
+CONFIG_XEN_SYS_HYPERVISOR=y
+
+CONFIG_DMADEVICES=y
+CONFIG_INTEL_IOATDMA=m
+
+CONFIG_SENSORS_I5K_AMB=m
+
+# CONFIG_COMPAT_VDSO is not set
+CONFIG_PROVIDE_OHCI1394_DMA_INIT=y
+# CONFIG_DEBUG_PER_CPU_MAPS is not set
+# CONFIG_CPA_DEBUG is not set
+
+CONFIG_HP_WATCHDOG=m
+
+CONFIG_FRAME_WARN=2048
+
+CONFIG_NODES_SHIFT=9
+CONFIG_X86_PAT=y
+# FIXME: These should be 32bit only
+# CONFIG_FB_N411 is not set
+CONFIG_STRICT_DEVMEM=y
+
+CONFIG_DIRECT_GBPAGES=y
+
+# CONFIG_NO_BOOTMEM is not set
+
+# CONFIG_MEMTEST is not set
+CONFIG_AMD_IOMMU=y
+CONFIG_AMD_IOMMU_STATS=y
+# CONFIG_MAXSMP is not set
+CONFIG_MTRR_SANITIZER=y
+CONFIG_MTRR_SANITIZER_ENABLE_DEFAULT=1
+CONFIG_MTRR_SANITIZER_SPARE_REG_NR_DEFAULT=1
+CONFIG_SYSPROF_TRACER=y
+# CONFIG_X86_VERBOSE_BOOTUP is not set
+# CONFIG_MMIOTRACE_TEST is not set
+
+CONFIG_X86_MPPARSE=y
+
+CONFIG_BACKLIGHT_MBP_NVIDIA=m
+
+CONFIG_OPROFILE_IBS=y
+CONFIG_MICROCODE_INTEL=y
+CONFIG_MICROCODE_AMD=y
+
+# CONFIG_X86_CHECK_BIOS_CORRUPTION is not set
+CONFIG_X86_RESERVE_LOW_64K=y
+
+# CONFIG_CMDLINE_BOOL is not set
+
+CONFIG_PANASONIC_LAPTOP=m
+
+CONFIG_XEN_DEBUG_FS=y
+CONFIG_X86_PTRACE_BTS=y
+
+CONFIG_I7300_IDLE=m
+CONFIG_INTR_REMAP=y
+
+CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS=y
+
+CONFIG_POWER_TRACER=y
+CONFIG_HW_BRANCH_TRACER=y
+
+CONFIG_X86_X2APIC=y
+CONFIG_SPARSE_IRQ=y
+
+CONFIG_RCU_FANOUT=64
+
+# CONFIG_IOMMU_STRESS is not set
+
+CONFIG_PERF_COUNTERS=y
+CONFIG_PERF_EVENTS=y
+CONFIG_EVENT_PROFILE=y
+
+# CONFIG_X86_MCE_INJECT is not set
+
+CONFIG_SFI=y
+CONFIG_INPUT_WINBOND_CIR=m
+CONFIG_I2C_SCMI=m
+CONFIG_SBC_FITPC2_WATCHDOG=m
+CONFIG_EDAC_I3200=m
+CONFIG_TOPSTAR_LAPTOP=m
+CONFIG_INTEL_TXT=y
+CONFIG_GPIO_LANGWELL=y
+
+CONFIG_FUNCTION_GRAPH_TRACER=y
+
+CONFIG_ACPI_CMPC=m
+CONFIG_MSI_WMI=m
+CONFIG_TOSHIBA_BT_RFKILL=m
+# CONFIG_SAMSUNG_LAPTOP is not set
+
+CONFIG_CS5535_MFGPT=m
+CONFIG_GEODE_WDT=m
+CONFIG_CS5535_CLOCK_EVENT_SRC=m
+
+CONFIG_X86_DECODER_SELFTEST=y
+
+CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL=m
+
+CONFIG_VGA_SWITCHEROO=y
+CONFIG_LPC_SCH=m
+
+CONFIG_INTEL_IDLE=m
+CONFIG_I7300_IDLE=m
+
+CONFIG_PCI_CNB20LE_QUIRK=y
diff --git a/die-floppy-die.patch b/die-floppy-die.patch
new file mode 100644
index 000000000..76db31218
--- /dev/null
+++ b/die-floppy-die.patch
@@ -0,0 +1,30 @@
+From 4ff58b642f80dedb20533978123d89b5ac9b1ed5 Mon Sep 17 00:00:00 2001
+From: Kyle McMartin <kyle@phobos.i.jkkm.org>
+Date: Tue, 30 Mar 2010 00:04:29 -0400
+Subject: die-floppy-die
+
+Kill the floppy.ko pnp modalias. We were surviving just fine without
+autoloading floppy drivers, tyvm.
+
+Please feel free to register all complaints in the wastepaper bin.
+---
+ drivers/block/floppy.c | 3 +--
+ 1 files changed, 1 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
+index 90c4038..f4a0b90 100644
+--- a/drivers/block/floppy.c
++++ b/drivers/block/floppy.c
+@@ -4619,8 +4619,7 @@ static const struct pnp_device_id floppy_pnpids[] = {
+ {"PNP0700", 0},
+ {}
+ };
+-
+-MODULE_DEVICE_TABLE(pnp, floppy_pnpids);
++/* MODULE_DEVICE_TABLE(pnp, floppy_pnpids); */
+
+ #else
+
+--
+1.7.0.1
+
diff --git a/disable-i8042-check-on-apple-mac.patch b/disable-i8042-check-on-apple-mac.patch
new file mode 100644
index 000000000..f99d0f900
--- /dev/null
+++ b/disable-i8042-check-on-apple-mac.patch
@@ -0,0 +1,59 @@
+From 2a79554c864ac58fa2ad982f0fcee2cc2aa33eb5 Mon Sep 17 00:00:00 2001
+From: Bastien Nocera <hadess@hadess.net>
+Date: Thu, 20 May 2010 10:30:31 -0400
+Subject: Disable i8042 checks on Intel Apple Macs
+
+As those computers never had any i8042 controllers, and the
+current lookup code could potentially lock up/hang/wait for
+timeout for long periods of time.
+
+Fixes intermittent hangs on boot on a MacbookAir1,1
+
+Signed-off-by: Bastien Nocera <hadess@hadess.net>
+---
+ drivers/input/serio/i8042.c | 22 ++++++++++++++++++++++
+ 1 files changed, 22 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
+index 6440a8f..4d7cf98 100644
+--- a/drivers/input/serio/i8042.c
++++ b/drivers/input/serio/i8042.c
+@@ -1451,6 +1451,22 @@ static struct platform_driver i8042_driver = {
+ .shutdown = i8042_shutdown,
+ };
+
++#ifdef CONFIG_DMI
++static struct dmi_system_id __initdata dmi_system_table[] = {
++ {
++ .matches = {
++ DMI_MATCH(DMI_BIOS_VENDOR, "Apple Computer, Inc.")
++ },
++ },
++ {
++ .matches = {
++ DMI_MATCH(DMI_BIOS_VENDOR, "Apple Inc.")
++ },
++ },
++ {}
++};
++#endif /*CONFIG_DMI*/
++
+ static int __init i8042_init(void)
+ {
+ struct platform_device *pdev;
+@@ -1458,6 +1474,12 @@ static int __init i8042_init(void)
+
+ dbg_init();
+
++#ifdef CONFIG_DMI
++ /* Intel Apple Macs never have an i8042 controller */
++ if (dmi_check_system(dmi_system_table) > 0)
++ return -ENODEV;
++#endif /*CONFIG_DMI*/
++
+ err = i8042_platform_init();
+ if (err)
+ return err;
+--
+1.7.0.1
+
diff --git a/drm-intel-big-hammer.patch b/drm-intel-big-hammer.patch
new file mode 100644
index 000000000..63dc016b1
--- /dev/null
+++ b/drm-intel-big-hammer.patch
@@ -0,0 +1,16 @@
+diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
+index 37427e4..08af9db 100644
+--- a/drivers/gpu/drm/i915/i915_gem.c
++++ b/drivers/gpu/drm/i915/i915_gem.c
+@@ -2553,6 +2553,11 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
+
+ mutex_lock(&dev->struct_mutex);
+
++ /* We don't get the flushing right for these chipsets, use the
++ * big hamer for now to avoid random crashiness. */
++ if (IS_I85X(dev) || IS_I865G(dev))
++ wbinvd();
++
+ i915_verify_inactive(dev, __FILE__, __LINE__);
+
+ if (dev_priv->mm.wedged) {
diff --git a/drm-intel-make-lvds-work.patch b/drm-intel-make-lvds-work.patch
new file mode 100644
index 000000000..5ca0152da
--- /dev/null
+++ b/drm-intel-make-lvds-work.patch
@@ -0,0 +1,19 @@
+diff -up linux-2.6.33.noarch/drivers/gpu/drm/i915/intel_display.c.orig linux-2.6.33.noarch/drivers/gpu/drm/i915/intel_display.c
+--- linux-2.6.33.noarch/drivers/gpu/drm/i915/intel_display.c.orig 2010-03-31 16:59:39.901995671 -0400
++++ linux-2.6.33.noarch/drivers/gpu/drm/i915/intel_display.c 2010-03-31 17:01:05.416996744 -0400
+@@ -3757,7 +3757,6 @@ struct drm_crtc *intel_get_load_detect_p
+ void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder, int dpms_mode)
+ {
+ struct drm_encoder *encoder = &intel_encoder->enc;
+- struct drm_device *dev = encoder->dev;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+@@ -3767,7 +3766,6 @@ void intel_release_load_detect_pipe(stru
+ intel_encoder->base.encoder = NULL;
+ intel_encoder->load_detect_temp = false;
+ crtc->enabled = drm_helper_crtc_in_use(crtc);
+- drm_helper_disable_unused_functions(dev);
+ }
+
+ /* Switch crtc and output back off if necessary */
diff --git a/drm-intel-next.patch b/drm-intel-next.patch
new file mode 100644
index 000000000..c6cac6926
--- /dev/null
+++ b/drm-intel-next.patch
@@ -0,0 +1 @@
+empty
diff --git a/drm-nouveau-updates.patch b/drm-nouveau-updates.patch
new file mode 100644
index 000000000..1b704ff0a
--- /dev/null
+++ b/drm-nouveau-updates.patch
@@ -0,0 +1,6997 @@
+ drivers/gpu/drm/drm_crtc_helper.c | 22 +-
+ drivers/gpu/drm/i2c/ch7006_drv.c | 22 +-
+ drivers/gpu/drm/i2c/ch7006_priv.h | 2 +-
+ drivers/gpu/drm/nouveau/Makefile | 2 +-
+ drivers/gpu/drm/nouveau/nouveau_acpi.c | 38 ++-
+ drivers/gpu/drm/nouveau/nouveau_bios.c | 636 +++++++++++++++++++++------
+ drivers/gpu/drm/nouveau/nouveau_bios.h | 4 +-
+ drivers/gpu/drm/nouveau/nouveau_bo.c | 9 +-
+ drivers/gpu/drm/nouveau/nouveau_calc.c | 4 +-
+ drivers/gpu/drm/nouveau/nouveau_channel.c | 5 -
+ drivers/gpu/drm/nouveau/nouveau_connector.c | 404 ++++++++----------
+ drivers/gpu/drm/nouveau/nouveau_connector.h | 7 +-
+ drivers/gpu/drm/nouveau/nouveau_dma.c | 8 +-
+ drivers/gpu/drm/nouveau/nouveau_dp.c | 24 +-
+ drivers/gpu/drm/nouveau/nouveau_drv.c | 31 +-
+ drivers/gpu/drm/nouveau/nouveau_drv.h | 90 ++---
+ drivers/gpu/drm/nouveau/nouveau_encoder.h | 10 +-
+ drivers/gpu/drm/nouveau/nouveau_fbcon.c | 2 +-
+ drivers/gpu/drm/nouveau/nouveau_fence.c | 31 +-
+ drivers/gpu/drm/nouveau/nouveau_gem.c | 11 +-
+ drivers/gpu/drm/nouveau/nouveau_grctx.c | 160 -------
+ drivers/gpu/drm/nouveau/nouveau_i2c.c | 34 ++
+ drivers/gpu/drm/nouveau/nouveau_i2c.h | 3 +
+ drivers/gpu/drm/nouveau/nouveau_mem.c | 275 ++----------
+ drivers/gpu/drm/nouveau/nouveau_notifier.c | 30 +-
+ drivers/gpu/drm/nouveau/nouveau_object.c | 105 ++---
+ drivers/gpu/drm/nouveau/nouveau_reg.h | 91 +++--
+ drivers/gpu/drm/nouveau/nouveau_sgdma.c | 46 +--
+ drivers/gpu/drm/nouveau/nouveau_state.c | 172 +++-----
+ drivers/gpu/drm/nouveau/nv04_crtc.c | 5 +
+ drivers/gpu/drm/nouveau/nv04_dac.c | 37 ++-
+ drivers/gpu/drm/nouveau/nv04_dfp.c | 12 +-
+ drivers/gpu/drm/nouveau/nv04_display.c | 64 ++--
+ drivers/gpu/drm/nouveau/nv04_fifo.c | 20 +-
+ drivers/gpu/drm/nouveau/nv04_graph.c | 5 +-
+ drivers/gpu/drm/nouveau/nv04_instmem.c | 21 +-
+ drivers/gpu/drm/nouveau/nv04_mc.c | 4 +
+ drivers/gpu/drm/nouveau/nv04_tv.c | 125 ++----
+ drivers/gpu/drm/nouveau/nv10_fifo.c | 10 -
+ drivers/gpu/drm/nouveau/nv17_tv.c | 46 ++-
+ drivers/gpu/drm/nouveau/nv20_graph.c | 96 +++--
+ drivers/gpu/drm/nouveau/nv40_fifo.c | 8 -
+ drivers/gpu/drm/nouveau/nv40_graph.c | 58 +--
+ drivers/gpu/drm/nouveau/nv40_mc.c | 2 +-
+ drivers/gpu/drm/nouveau/nv50_crtc.c | 42 +--
+ drivers/gpu/drm/nouveau/nv50_dac.c | 43 ++-
+ drivers/gpu/drm/nouveau/nv50_display.c | 385 ++++++++++-------
+ drivers/gpu/drm/nouveau/nv50_fifo.c | 126 ++----
+ drivers/gpu/drm/nouveau/nv50_graph.c | 86 ++---
+ drivers/gpu/drm/nouveau/nv50_instmem.c | 67 +--
+ drivers/gpu/drm/nouveau/nv50_sor.c | 105 +++---
+ drivers/gpu/drm/nouveau/nvreg.h | 22 -
+ 52 files changed, 1748 insertions(+), 1919 deletions(-)
+
+diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
+index 9b2a541..1eaa315 100644
+--- a/drivers/gpu/drm/drm_crtc_helper.c
++++ b/drivers/gpu/drm/drm_crtc_helper.c
+@@ -201,6 +201,17 @@ bool drm_helper_crtc_in_use(struct drm_crtc *crtc)
+ }
+ EXPORT_SYMBOL(drm_helper_crtc_in_use);
+
++static void
++drm_encoder_disable(struct drm_encoder *encoder)
++{
++ struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
++
++ if (encoder_funcs->disable)
++ (*encoder_funcs->disable)(encoder);
++ else
++ (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
++}
++
+ /**
+ * drm_helper_disable_unused_functions - disable unused objects
+ * @dev: DRM device
+@@ -215,7 +226,6 @@ void drm_helper_disable_unused_functions(struct drm_device *dev)
+ {
+ struct drm_encoder *encoder;
+ struct drm_connector *connector;
+- struct drm_encoder_helper_funcs *encoder_funcs;
+ struct drm_crtc *crtc;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+@@ -226,12 +236,8 @@ void drm_helper_disable_unused_functions(struct drm_device *dev)
+ }
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+- encoder_funcs = encoder->helper_private;
+ if (!drm_helper_encoder_in_use(encoder)) {
+- if (encoder_funcs->disable)
+- (*encoder_funcs->disable)(encoder);
+- else
+- (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
++ drm_encoder_disable(encoder);
+ /* disconnector encoder from any connector */
+ encoder->crtc = NULL;
+ }
+@@ -292,11 +298,11 @@ drm_crtc_prepare_encoders(struct drm_device *dev)
+ encoder_funcs = encoder->helper_private;
+ /* Disable unused encoders */
+ if (encoder->crtc == NULL)
+- (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
++ drm_encoder_disable(encoder);
+ /* Disable encoders whose CRTC is about to change */
+ if (encoder_funcs->get_crtc &&
+ encoder->crtc != (*encoder_funcs->get_crtc)(encoder))
+- (*encoder_funcs->dpms)(encoder, DRM_MODE_DPMS_OFF);
++ drm_encoder_disable(encoder);
+ }
+ }
+
+diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c
+index 81681a0..833b35f 100644
+--- a/drivers/gpu/drm/i2c/ch7006_drv.c
++++ b/drivers/gpu/drm/i2c/ch7006_drv.c
+@@ -33,7 +33,7 @@ static void ch7006_encoder_set_config(struct drm_encoder *encoder,
+ {
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+
+- priv->params = params;
++ priv->params = *(struct ch7006_encoder_params *)params;
+ }
+
+ static void ch7006_encoder_destroy(struct drm_encoder *encoder)
+@@ -114,7 +114,7 @@ static void ch7006_encoder_mode_set(struct drm_encoder *encoder,
+ {
+ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
+ struct ch7006_priv *priv = to_ch7006_priv(encoder);
+- struct ch7006_encoder_params *params = priv->params;
++ struct ch7006_encoder_params *params = &priv->params;
+ struct ch7006_state *state = &priv->state;
+ uint8_t *regs = state->regs;
+ struct ch7006_mode *mode = priv->mode;
+@@ -428,6 +428,22 @@ static int ch7006_remove(struct i2c_client *client)
+ return 0;
+ }
+
++static int ch7006_suspend(struct i2c_client *client, pm_message_t mesg)
++{
++ ch7006_dbg(client, "\n");
++
++ return 0;
++}
++
++static int ch7006_resume(struct i2c_client *client)
++{
++ ch7006_dbg(client, "\n");
++
++ ch7006_write(client, 0x3d, 0x0);
++
++ return 0;
++}
++
+ static int ch7006_encoder_init(struct i2c_client *client,
+ struct drm_device *dev,
+ struct drm_encoder_slave *encoder)
+@@ -487,6 +503,8 @@ static struct drm_i2c_encoder_driver ch7006_driver = {
+ .i2c_driver = {
+ .probe = ch7006_probe,
+ .remove = ch7006_remove,
++ .suspend = ch7006_suspend,
++ .resume = ch7006_resume,
+
+ .driver = {
+ .name = "ch7006",
+diff --git a/drivers/gpu/drm/i2c/ch7006_priv.h b/drivers/gpu/drm/i2c/ch7006_priv.h
+index b06d3d9..1c6d2e3 100644
+--- a/drivers/gpu/drm/i2c/ch7006_priv.h
++++ b/drivers/gpu/drm/i2c/ch7006_priv.h
+@@ -77,7 +77,7 @@ struct ch7006_state {
+ };
+
+ struct ch7006_priv {
+- struct ch7006_encoder_params *params;
++ struct ch7006_encoder_params params;
+ struct ch7006_mode *mode;
+
+ struct ch7006_state state;
+diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
+index acd31ed..4a1db73 100644
+--- a/drivers/gpu/drm/nouveau/Makefile
++++ b/drivers/gpu/drm/nouveau/Makefile
+@@ -9,7 +9,7 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
+ nouveau_bo.o nouveau_fence.o nouveau_gem.o nouveau_ttm.o \
+ nouveau_hw.o nouveau_calc.o nouveau_bios.o nouveau_i2c.o \
+ nouveau_display.o nouveau_connector.o nouveau_fbcon.o \
+- nouveau_dp.o nouveau_grctx.o \
++ nouveau_dp.o \
+ nv04_timer.o \
+ nv04_mc.o nv40_mc.o nv50_mc.o \
+ nv04_fb.o nv10_fb.o nv40_fb.o nv50_fb.o \
+diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c
+index d4bcca8..c17a055 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_acpi.c
++++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c
+@@ -3,6 +3,7 @@
+ #include <linux/slab.h>
+ #include <acpi/acpi_drivers.h>
+ #include <acpi/acpi_bus.h>
++#include <acpi/video.h>
+
+ #include "drmP.h"
+ #include "drm.h"
+@@ -11,6 +12,7 @@
+ #include "nouveau_drv.h"
+ #include "nouveau_drm.h"
+ #include "nv50_display.h"
++#include "nouveau_connector.h"
+
+ #include <linux/vga_switcheroo.h>
+
+@@ -42,7 +44,7 @@ static const char nouveau_dsm_muid[] = {
+ 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,
+ };
+
+-static int nouveau_dsm(acpi_handle handle, int func, int arg, int *result)
++static int nouveau_dsm(acpi_handle handle, int func, int arg, uint32_t *result)
+ {
+ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_object_list input;
+@@ -259,3 +261,37 @@ int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len)
+ {
+ return nouveau_rom_call(nouveau_dsm_priv.rom_handle, bios, offset, len);
+ }
++
++int
++nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)
++{
++ struct nouveau_connector *nv_connector = nouveau_connector(connector);
++ struct acpi_device *acpidev;
++ acpi_handle handle;
++ int type, ret;
++ void *edid;
++
++ switch (connector->connector_type) {
++ case DRM_MODE_CONNECTOR_LVDS:
++ case DRM_MODE_CONNECTOR_eDP:
++ type = ACPI_VIDEO_DISPLAY_LCD;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev);
++ if (!handle)
++ return -ENODEV;
++
++ ret = acpi_bus_get_device(handle, &acpidev);
++ if (ret)
++ return -ENODEV;
++
++ ret = acpi_video_get_edid(acpidev, type, -1, &edid);
++ if (ret < 0)
++ return ret;
++
++ nv_connector->edid = edid;
++ return 0;
++}
+diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
+index e492919..aae29cc 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
++++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
+@@ -28,6 +28,8 @@
+ #include "nouveau_hw.h"
+ #include "nouveau_encoder.h"
+
++#include <linux/io-mapping.h>
++
+ /* these defines are made up */
+ #define NV_CIO_CRE_44_HEADA 0x0
+ #define NV_CIO_CRE_44_HEADB 0x3
+@@ -209,20 +211,20 @@ static struct methods shadow_methods[] = {
+ { "PCIROM", load_vbios_pci, true },
+ { "ACPI", load_vbios_acpi, true },
+ };
++#define NUM_SHADOW_METHODS ARRAY_SIZE(shadow_methods)
+
+ static bool NVShadowVBIOS(struct drm_device *dev, uint8_t *data)
+ {
+- const int nr_methods = ARRAY_SIZE(shadow_methods);
+ struct methods *methods = shadow_methods;
+ int testscore = 3;
+- int scores[nr_methods], i;
++ int scores[NUM_SHADOW_METHODS], i;
+
+ if (nouveau_vbios) {
+- for (i = 0; i < nr_methods; i++)
++ for (i = 0; i < NUM_SHADOW_METHODS; i++)
+ if (!strcasecmp(nouveau_vbios, methods[i].desc))
+ break;
+
+- if (i < nr_methods) {
++ if (i < NUM_SHADOW_METHODS) {
+ NV_INFO(dev, "Attempting to use BIOS image from %s\n",
+ methods[i].desc);
+
+@@ -234,7 +236,7 @@ static bool NVShadowVBIOS(struct drm_device *dev, uint8_t *data)
+ NV_ERROR(dev, "VBIOS source \'%s\' invalid\n", nouveau_vbios);
+ }
+
+- for (i = 0; i < nr_methods; i++) {
++ for (i = 0; i < NUM_SHADOW_METHODS; i++) {
+ NV_TRACE(dev, "Attempting to load BIOS image from %s\n",
+ methods[i].desc);
+ data[0] = data[1] = 0; /* avoid reuse of previous image */
+@@ -245,7 +247,7 @@ static bool NVShadowVBIOS(struct drm_device *dev, uint8_t *data)
+ }
+
+ while (--testscore > 0) {
+- for (i = 0; i < nr_methods; i++) {
++ for (i = 0; i < NUM_SHADOW_METHODS; i++) {
+ if (scores[i] == testscore) {
+ NV_TRACE(dev, "Using BIOS image from %s\n",
+ methods[i].desc);
+@@ -920,7 +922,7 @@ init_io_restrict_prog(struct nvbios *bios, uint16_t offset,
+ NV_ERROR(bios->dev,
+ "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n",
+ offset, config, count);
+- return -EINVAL;
++ return len;
+ }
+
+ configval = ROM32(bios->data[offset + 11 + config * 4]);
+@@ -1022,7 +1024,7 @@ init_io_restrict_pll(struct nvbios *bios, uint16_t offset,
+ NV_ERROR(bios->dev,
+ "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n",
+ offset, config, count);
+- return -EINVAL;
++ return len;
+ }
+
+ freq = ROM16(bios->data[offset + 12 + config * 2]);
+@@ -1194,7 +1196,7 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ dpe = nouveau_bios_dp_table(dev, dcb, &dummy);
+ if (!dpe) {
+ NV_ERROR(dev, "0x%04X: INIT_3A: no encoder table!!\n", offset);
+- return -EINVAL;
++ return 3;
+ }
+
+ switch (cond) {
+@@ -1218,12 +1220,16 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ int ret;
+
+ auxch = nouveau_i2c_find(dev, bios->display.output->i2c_index);
+- if (!auxch)
+- return -ENODEV;
++ if (!auxch) {
++ NV_ERROR(dev, "0x%04X: couldn't get auxch\n", offset);
++ return 3;
++ }
+
+ ret = nouveau_dp_auxch(auxch, 9, 0xd, &cond, 1);
+- if (ret)
+- return ret;
++ if (ret) {
++ NV_ERROR(dev, "0x%04X: auxch rd fail: %d\n", offset, ret);
++ return 3;
++ }
+
+ if (cond & 1)
+ iexec->execute = false;
+@@ -1392,7 +1398,7 @@ init_io_restrict_pll2(struct nvbios *bios, uint16_t offset,
+ NV_ERROR(bios->dev,
+ "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n",
+ offset, config, count);
+- return -EINVAL;
++ return len;
+ }
+
+ freq = ROM32(bios->data[offset + 11 + config * 4]);
+@@ -1452,6 +1458,7 @@ init_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ * "mask n" and OR it with "data n" before writing it back to the device
+ */
+
++ struct drm_device *dev = bios->dev;
+ uint8_t i2c_index = bios->data[offset + 1];
+ uint8_t i2c_address = bios->data[offset + 2] >> 1;
+ uint8_t count = bios->data[offset + 3];
+@@ -1466,9 +1473,11 @@ init_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ "Count: 0x%02X\n",
+ offset, i2c_index, i2c_address, count);
+
+- chan = init_i2c_device_find(bios->dev, i2c_index);
+- if (!chan)
+- return -ENODEV;
++ chan = init_i2c_device_find(dev, i2c_index);
++ if (!chan) {
++ NV_ERROR(dev, "0x%04X: i2c bus not found\n", offset);
++ return len;
++ }
+
+ for (i = 0; i < count; i++) {
+ uint8_t reg = bios->data[offset + 4 + i * 3];
+@@ -1479,8 +1488,10 @@ init_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ ret = i2c_smbus_xfer(&chan->adapter, i2c_address, 0,
+ I2C_SMBUS_READ, reg,
+ I2C_SMBUS_BYTE_DATA, &val);
+- if (ret < 0)
+- return ret;
++ if (ret < 0) {
++ NV_ERROR(dev, "0x%04X: i2c rd fail: %d\n", offset, ret);
++ return len;
++ }
+
+ BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Value: 0x%02X, "
+ "Mask: 0x%02X, Data: 0x%02X\n",
+@@ -1494,8 +1505,10 @@ init_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ ret = i2c_smbus_xfer(&chan->adapter, i2c_address, 0,
+ I2C_SMBUS_WRITE, reg,
+ I2C_SMBUS_BYTE_DATA, &val);
+- if (ret < 0)
+- return ret;
++ if (ret < 0) {
++ NV_ERROR(dev, "0x%04X: i2c wr fail: %d\n", offset, ret);
++ return len;
++ }
+ }
+
+ return len;
+@@ -1520,6 +1533,7 @@ init_zm_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ * "DCB I2C table entry index", set the register to "data n"
+ */
+
++ struct drm_device *dev = bios->dev;
+ uint8_t i2c_index = bios->data[offset + 1];
+ uint8_t i2c_address = bios->data[offset + 2] >> 1;
+ uint8_t count = bios->data[offset + 3];
+@@ -1534,9 +1548,11 @@ init_zm_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ "Count: 0x%02X\n",
+ offset, i2c_index, i2c_address, count);
+
+- chan = init_i2c_device_find(bios->dev, i2c_index);
+- if (!chan)
+- return -ENODEV;
++ chan = init_i2c_device_find(dev, i2c_index);
++ if (!chan) {
++ NV_ERROR(dev, "0x%04X: i2c bus not found\n", offset);
++ return len;
++ }
+
+ for (i = 0; i < count; i++) {
+ uint8_t reg = bios->data[offset + 4 + i * 2];
+@@ -1553,8 +1569,10 @@ init_zm_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ ret = i2c_smbus_xfer(&chan->adapter, i2c_address, 0,
+ I2C_SMBUS_WRITE, reg,
+ I2C_SMBUS_BYTE_DATA, &val);
+- if (ret < 0)
+- return ret;
++ if (ret < 0) {
++ NV_ERROR(dev, "0x%04X: i2c wr fail: %d\n", offset, ret);
++ return len;
++ }
+ }
+
+ return len;
+@@ -1577,6 +1595,7 @@ init_zm_i2c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ * address" on the I2C bus given by "DCB I2C table entry index"
+ */
+
++ struct drm_device *dev = bios->dev;
+ uint8_t i2c_index = bios->data[offset + 1];
+ uint8_t i2c_address = bios->data[offset + 2] >> 1;
+ uint8_t count = bios->data[offset + 3];
+@@ -1584,7 +1603,7 @@ init_zm_i2c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ struct nouveau_i2c_chan *chan;
+ struct i2c_msg msg;
+ uint8_t data[256];
+- int i;
++ int ret, i;
+
+ if (!iexec->execute)
+ return len;
+@@ -1593,9 +1612,11 @@ init_zm_i2c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ "Count: 0x%02X\n",
+ offset, i2c_index, i2c_address, count);
+
+- chan = init_i2c_device_find(bios->dev, i2c_index);
+- if (!chan)
+- return -ENODEV;
++ chan = init_i2c_device_find(dev, i2c_index);
++ if (!chan) {
++ NV_ERROR(dev, "0x%04X: i2c bus not found\n", offset);
++ return len;
++ }
+
+ for (i = 0; i < count; i++) {
+ data[i] = bios->data[offset + 4 + i];
+@@ -1608,8 +1629,11 @@ init_zm_i2c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ msg.flags = 0;
+ msg.len = count;
+ msg.buf = data;
+- if (i2c_transfer(&chan->adapter, &msg, 1) != 1)
+- return -EIO;
++ ret = i2c_transfer(&chan->adapter, &msg, 1);
++ if (ret != 1) {
++ NV_ERROR(dev, "0x%04X: i2c wr fail: %d\n", offset, ret);
++ return len;
++ }
+ }
+
+ return len;
+@@ -1633,6 +1657,7 @@ init_tmds(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ * used -- see get_tmds_index_reg()
+ */
+
++ struct drm_device *dev = bios->dev;
+ uint8_t mlv = bios->data[offset + 1];
+ uint32_t tmdsaddr = bios->data[offset + 2];
+ uint8_t mask = bios->data[offset + 3];
+@@ -1647,8 +1672,10 @@ init_tmds(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ offset, mlv, tmdsaddr, mask, data);
+
+ reg = get_tmds_index_reg(bios->dev, mlv);
+- if (!reg)
+- return -EINVAL;
++ if (!reg) {
++ NV_ERROR(dev, "0x%04X: no tmds_index_reg\n", offset);
++ return 5;
++ }
+
+ bios_wr32(bios, reg,
+ tmdsaddr | NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE);
+@@ -1678,6 +1705,7 @@ init_zm_tmds_group(struct nvbios *bios, uint16_t offset,
+ * register is used -- see get_tmds_index_reg()
+ */
+
++ struct drm_device *dev = bios->dev;
+ uint8_t mlv = bios->data[offset + 1];
+ uint8_t count = bios->data[offset + 2];
+ int len = 3 + count * 2;
+@@ -1691,8 +1719,10 @@ init_zm_tmds_group(struct nvbios *bios, uint16_t offset,
+ offset, mlv, count);
+
+ reg = get_tmds_index_reg(bios->dev, mlv);
+- if (!reg)
+- return -EINVAL;
++ if (!reg) {
++ NV_ERROR(dev, "0x%04X: no tmds_index_reg\n", offset);
++ return len;
++ }
+
+ for (i = 0; i < count; i++) {
+ uint8_t tmdsaddr = bios->data[offset + 3 + i * 2];
+@@ -2039,6 +2069,323 @@ init_zm_index_io(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ return 5;
+ }
+
++static inline void
++bios_md32(struct nvbios *bios, uint32_t reg,
++ uint32_t mask, uint32_t val)
++{
++ bios_wr32(bios, reg, (bios_rd32(bios, reg) & ~mask) | val);
++}
++
++static uint32_t
++peek_fb(struct drm_device *dev, struct io_mapping *fb,
++ uint32_t off)
++{
++ uint32_t val = 0;
++
++ if (off < pci_resource_len(dev->pdev, 1)) {
++ uint32_t __iomem *p = io_mapping_map_atomic_wc(fb, off);
++
++ val = ioread32(p);
++
++ io_mapping_unmap_atomic(p);
++ }
++
++ return val;
++}
++
++static void
++poke_fb(struct drm_device *dev, struct io_mapping *fb,
++ uint32_t off, uint32_t val)
++{
++ if (off < pci_resource_len(dev->pdev, 1)) {
++ uint32_t __iomem *p = io_mapping_map_atomic_wc(fb, off);
++
++ iowrite32(val, p);
++ wmb();
++
++ io_mapping_unmap_atomic(p);
++ }
++}
++
++static inline bool
++read_back_fb(struct drm_device *dev, struct io_mapping *fb,
++ uint32_t off, uint32_t val)
++{
++ poke_fb(dev, fb, off, val);
++ return val == peek_fb(dev, fb, off);
++}
++
++static int
++nv04_init_compute_mem(struct nvbios *bios)
++{
++ struct drm_device *dev = bios->dev;
++ uint32_t patt = 0xdeadbeef;
++ struct io_mapping *fb;
++ int i;
++
++ /* Map the framebuffer aperture */
++ fb = io_mapping_create_wc(pci_resource_start(dev->pdev, 1),
++ pci_resource_len(dev->pdev, 1));
++ if (!fb)
++ return -ENOMEM;
++
++ /* Sequencer and refresh off */
++ NVWriteVgaSeq(dev, 0, 1, NVReadVgaSeq(dev, 0, 1) | 0x20);
++ bios_md32(bios, NV04_PFB_DEBUG_0, 0, NV04_PFB_DEBUG_0_REFRESH_OFF);
++
++ bios_md32(bios, NV04_PFB_BOOT_0, ~0,
++ NV04_PFB_BOOT_0_RAM_AMOUNT_16MB |
++ NV04_PFB_BOOT_0_RAM_WIDTH_128 |
++ NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT);
++
++ for (i = 0; i < 4; i++)
++ poke_fb(dev, fb, 4 * i, patt);
++
++ poke_fb(dev, fb, 0x400000, patt + 1);
++
++ if (peek_fb(dev, fb, 0) == patt + 1) {
++ bios_md32(bios, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_TYPE,
++ NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT);
++ bios_md32(bios, NV04_PFB_DEBUG_0,
++ NV04_PFB_DEBUG_0_REFRESH_OFF, 0);
++
++ for (i = 0; i < 4; i++)
++ poke_fb(dev, fb, 4 * i, patt);
++
++ if ((peek_fb(dev, fb, 0xc) & 0xffff) != (patt & 0xffff))
++ bios_md32(bios, NV04_PFB_BOOT_0,
++ NV04_PFB_BOOT_0_RAM_WIDTH_128 |
++ NV04_PFB_BOOT_0_RAM_AMOUNT,
++ NV04_PFB_BOOT_0_RAM_AMOUNT_8MB);
++
++ } else if ((peek_fb(dev, fb, 0xc) & 0xffff0000) !=
++ (patt & 0xffff0000)) {
++ bios_md32(bios, NV04_PFB_BOOT_0,
++ NV04_PFB_BOOT_0_RAM_WIDTH_128 |
++ NV04_PFB_BOOT_0_RAM_AMOUNT,
++ NV04_PFB_BOOT_0_RAM_AMOUNT_4MB);
++
++ } else if (peek_fb(dev, fb, 0) == patt) {
++ if (read_back_fb(dev, fb, 0x800000, patt))
++ bios_md32(bios, NV04_PFB_BOOT_0,
++ NV04_PFB_BOOT_0_RAM_AMOUNT,
++ NV04_PFB_BOOT_0_RAM_AMOUNT_8MB);
++ else
++ bios_md32(bios, NV04_PFB_BOOT_0,
++ NV04_PFB_BOOT_0_RAM_AMOUNT,
++ NV04_PFB_BOOT_0_RAM_AMOUNT_4MB);
++
++ bios_md32(bios, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_TYPE,
++ NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT);
++
++ } else if (!read_back_fb(dev, fb, 0x800000, patt)) {
++ bios_md32(bios, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_AMOUNT,
++ NV04_PFB_BOOT_0_RAM_AMOUNT_8MB);
++
++ }
++
++ /* Refresh on, sequencer on */
++ bios_md32(bios, NV04_PFB_DEBUG_0, NV04_PFB_DEBUG_0_REFRESH_OFF, 0);
++ NVWriteVgaSeq(dev, 0, 1, NVReadVgaSeq(dev, 0, 1) & ~0x20);
++
++ io_mapping_free(fb);
++ return 0;
++}
++
++static const uint8_t *
++nv05_memory_config(struct nvbios *bios)
++{
++ /* Defaults for BIOSes lacking a memory config table */
++ static const uint8_t default_config_tab[][2] = {
++ { 0x24, 0x00 },
++ { 0x28, 0x00 },
++ { 0x24, 0x01 },
++ { 0x1f, 0x00 },
++ { 0x0f, 0x00 },
++ { 0x17, 0x00 },
++ { 0x06, 0x00 },
++ { 0x00, 0x00 }
++ };
++ int i = (bios_rd32(bios, NV_PEXTDEV_BOOT_0) &
++ NV_PEXTDEV_BOOT_0_RAMCFG) >> 2;
++
++ if (bios->legacy.mem_init_tbl_ptr)
++ return &bios->data[bios->legacy.mem_init_tbl_ptr + 2 * i];
++ else
++ return default_config_tab[i];
++}
++
++static int
++nv05_init_compute_mem(struct nvbios *bios)
++{
++ struct drm_device *dev = bios->dev;
++ const uint8_t *ramcfg = nv05_memory_config(bios);
++ uint32_t patt = 0xdeadbeef;
++ struct io_mapping *fb;
++ int i, v;
++
++ /* Map the framebuffer aperture */
++ fb = io_mapping_create_wc(pci_resource_start(dev->pdev, 1),
++ pci_resource_len(dev->pdev, 1));
++ if (!fb)
++ return -ENOMEM;
++
++ /* Sequencer off */
++ NVWriteVgaSeq(dev, 0, 1, NVReadVgaSeq(dev, 0, 1) | 0x20);
++
++ if (bios_rd32(bios, NV04_PFB_BOOT_0) & NV04_PFB_BOOT_0_UMA_ENABLE)
++ goto out;
++
++ bios_md32(bios, NV04_PFB_DEBUG_0, NV04_PFB_DEBUG_0_REFRESH_OFF, 0);
++
++ /* If present load the hardcoded scrambling table */
++ if (bios->legacy.mem_init_tbl_ptr) {
++ uint32_t *scramble_tab = (uint32_t *)&bios->data[
++ bios->legacy.mem_init_tbl_ptr + 0x10];
++
++ for (i = 0; i < 8; i++)
++ bios_wr32(bios, NV04_PFB_SCRAMBLE(i),
++ ROM32(scramble_tab[i]));
++ }
++
++ /* Set memory type/width/length defaults depending on the straps */
++ bios_md32(bios, NV04_PFB_BOOT_0, 0x3f, ramcfg[0]);
++
++ if (ramcfg[1] & 0x80)
++ bios_md32(bios, NV04_PFB_CFG0, 0, NV04_PFB_CFG0_SCRAMBLE);
++
++ bios_md32(bios, NV04_PFB_CFG1, 0x700001, (ramcfg[1] & 1) << 20);
++ bios_md32(bios, NV04_PFB_CFG1, 0, 1);
++
++ /* Probe memory bus width */
++ for (i = 0; i < 4; i++)
++ poke_fb(dev, fb, 4 * i, patt);
++
++ if (peek_fb(dev, fb, 0xc) != patt)
++ bios_md32(bios, NV04_PFB_BOOT_0,
++ NV04_PFB_BOOT_0_RAM_WIDTH_128, 0);
++
++ /* Probe memory length */
++ v = bios_rd32(bios, NV04_PFB_BOOT_0) & NV04_PFB_BOOT_0_RAM_AMOUNT;
++
++ if (v == NV04_PFB_BOOT_0_RAM_AMOUNT_32MB &&
++ (!read_back_fb(dev, fb, 0x1000000, ++patt) ||
++ !read_back_fb(dev, fb, 0, ++patt)))
++ bios_md32(bios, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_AMOUNT,
++ NV04_PFB_BOOT_0_RAM_AMOUNT_16MB);
++
++ if (v == NV04_PFB_BOOT_0_RAM_AMOUNT_16MB &&
++ !read_back_fb(dev, fb, 0x800000, ++patt))
++ bios_md32(bios, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_AMOUNT,
++ NV04_PFB_BOOT_0_RAM_AMOUNT_8MB);
++
++ if (!read_back_fb(dev, fb, 0x400000, ++patt))
++ bios_md32(bios, NV04_PFB_BOOT_0, NV04_PFB_BOOT_0_RAM_AMOUNT,
++ NV04_PFB_BOOT_0_RAM_AMOUNT_4MB);
++
++out:
++ /* Sequencer on */
++ NVWriteVgaSeq(dev, 0, 1, NVReadVgaSeq(dev, 0, 1) & ~0x20);
++
++ io_mapping_free(fb);
++ return 0;
++}
++
++static int
++nv10_init_compute_mem(struct nvbios *bios)
++{
++ struct drm_device *dev = bios->dev;
++ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
++ const int mem_width[] = { 0x10, 0x00, 0x20 };
++ const int mem_width_count = (dev_priv->chipset >= 0x17 ? 3 : 2);
++ uint32_t patt = 0xdeadbeef;
++ struct io_mapping *fb;
++ int i, j, k;
++
++ /* Map the framebuffer aperture */
++ fb = io_mapping_create_wc(pci_resource_start(dev->pdev, 1),
++ pci_resource_len(dev->pdev, 1));
++ if (!fb)
++ return -ENOMEM;
++
++ bios_wr32(bios, NV10_PFB_REFCTRL, NV10_PFB_REFCTRL_VALID_1);
++
++ /* Probe memory bus width */
++ for (i = 0; i < mem_width_count; i++) {
++ bios_md32(bios, NV04_PFB_CFG0, 0x30, mem_width[i]);
++
++ for (j = 0; j < 4; j++) {
++ for (k = 0; k < 4; k++)
++ poke_fb(dev, fb, 0x1c, 0);
++
++ poke_fb(dev, fb, 0x1c, patt);
++ poke_fb(dev, fb, 0x3c, 0);
++
++ if (peek_fb(dev, fb, 0x1c) == patt)
++ goto mem_width_found;
++ }
++ }
++
++mem_width_found:
++ patt <<= 1;
++
++ /* Probe amount of installed memory */
++ for (i = 0; i < 4; i++) {
++ int off = bios_rd32(bios, NV04_PFB_FIFO_DATA) - 0x100000;
++
++ poke_fb(dev, fb, off, patt);
++ poke_fb(dev, fb, 0, 0);
++
++ peek_fb(dev, fb, 0);
++ peek_fb(dev, fb, 0);
++ peek_fb(dev, fb, 0);
++ peek_fb(dev, fb, 0);
++
++ if (peek_fb(dev, fb, off) == patt)
++ goto amount_found;
++ }
++
++ /* IC missing - disable the upper half memory space. */
++ bios_md32(bios, NV04_PFB_CFG0, 0x1000, 0);
++
++amount_found:
++ io_mapping_free(fb);
++ return 0;
++}
++
++static int
++nv20_init_compute_mem(struct nvbios *bios)
++{
++ struct drm_device *dev = bios->dev;
++ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
++ uint32_t mask = (dev_priv->chipset >= 0x25 ? 0x300 : 0x900);
++ uint32_t amount, off;
++ struct io_mapping *fb;
++
++ /* Map the framebuffer aperture */
++ fb = io_mapping_create_wc(pci_resource_start(dev->pdev, 1),
++ pci_resource_len(dev->pdev, 1));
++ if (!fb)
++ return -ENOMEM;
++
++ bios_wr32(bios, NV10_PFB_REFCTRL, NV10_PFB_REFCTRL_VALID_1);
++
++ /* Allow full addressing */
++ bios_md32(bios, NV04_PFB_CFG0, 0, mask);
++
++ amount = bios_rd32(bios, NV04_PFB_FIFO_DATA);
++ for (off = amount; off > 0x2000000; off -= 0x2000000)
++ poke_fb(dev, fb, off - 4, off);
++
++ amount = bios_rd32(bios, NV04_PFB_FIFO_DATA);
++ if (amount != peek_fb(dev, fb, amount - 4))
++ /* IC missing - disable the upper half memory space. */
++ bios_md32(bios, NV04_PFB_CFG0, mask, 0);
++
++ io_mapping_free(fb);
++ return 0;
++}
++
+ static int
+ init_compute_mem(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ {
+@@ -2047,64 +2394,57 @@ init_compute_mem(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ *
+ * offset (8 bit): opcode
+ *
+- * This opcode is meant to set NV_PFB_CFG0 (0x100200) appropriately so
+- * that the hardware can correctly calculate how much VRAM it has
+- * (and subsequently report that value in NV_PFB_CSTATUS (0x10020C))
++ * This opcode is meant to set the PFB memory config registers
++ * appropriately so that we can correctly calculate how much VRAM it
++ * has (on nv10 and better chipsets the amount of installed VRAM is
++ * subsequently reported in NV_PFB_CSTATUS (0x10020C)).
+ *
+- * The implementation of this opcode in general consists of two parts:
+- * 1) determination of the memory bus width
+- * 2) determination of how many of the card's RAM pads have ICs attached
++ * The implementation of this opcode in general consists of several
++ * parts:
+ *
+- * 1) is done by a cunning combination of writes to offsets 0x1c and
+- * 0x3c in the framebuffer, and seeing whether the written values are
+- * read back correctly. This then affects bits 4-7 of NV_PFB_CFG0
++ * 1) Determination of memory type and density. Only necessary for
++ * really old chipsets, the memory type reported by the strap bits
++ * (0x101000) is assumed to be accurate on nv05 and newer.
+ *
+- * 2) is done by a cunning combination of writes to an offset slightly
+- * less than the maximum memory reported by NV_PFB_CSTATUS, then seeing
+- * if the test pattern can be read back. This then affects bits 12-15 of
+- * NV_PFB_CFG0
++ * 2) Determination of the memory bus width. Usually done by a cunning
++ * combination of writes to offsets 0x1c and 0x3c in the fb, and
++ * seeing whether the written values are read back correctly.
+ *
+- * In this context a "cunning combination" may include multiple reads
+- * and writes to varying locations, often alternating the test pattern
+- * and 0, doubtless to make sure buffers are filled, residual charges
+- * on tracks are removed etc.
++ * Only necessary on nv0x-nv1x and nv34, on the other cards we can
++ * trust the straps.
+ *
+- * Unfortunately, the "cunning combination"s mentioned above, and the
+- * changes to the bits in NV_PFB_CFG0 differ with nearly every bios
+- * trace I have.
++ * 3) Determination of how many of the card's RAM pads have ICs
++ * attached, usually done by a cunning combination of writes to an
++ * offset slightly less than the maximum memory reported by
++ * NV_PFB_CSTATUS, then seeing if the test pattern can be read back.
+ *
+- * Therefore, we cheat and assume the value of NV_PFB_CFG0 with which
+- * we started was correct, and use that instead
++ * This appears to be a NOP on IGPs and NV4x or newer chipsets, both io
++ * logs of the VBIOS and kmmio traces of the binary driver POSTing the
++ * card show nothing being done for this opcode. Why is it still listed
++ * in the table?!
+ */
+
+ /* no iexec->execute check by design */
+
+- /*
+- * This appears to be a NOP on G8x chipsets, both io logs of the VBIOS
+- * and kmmio traces of the binary driver POSTing the card show nothing
+- * being done for this opcode. why is it still listed in the table?!
+- */
+-
+ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
++ int ret;
+
+- if (dev_priv->card_type >= NV_40)
+- return 1;
+-
+- /*
+- * On every card I've seen, this step gets done for us earlier in
+- * the init scripts
+- uint8_t crdata = bios_idxprt_rd(dev, NV_VIO_SRX, 0x01);
+- bios_idxprt_wr(dev, NV_VIO_SRX, 0x01, crdata | 0x20);
+- */
+-
+- /*
+- * This also has probably been done in the scripts, but an mmio trace of
+- * s3 resume shows nvidia doing it anyway (unlike the NV_VIO_SRX write)
+- */
+- bios_wr32(bios, NV_PFB_REFCTRL, NV_PFB_REFCTRL_VALID_1);
++ if (dev_priv->chipset >= 0x40 ||
++ dev_priv->chipset == 0x1a ||
++ dev_priv->chipset == 0x1f)
++ ret = 0;
++ else if (dev_priv->chipset >= 0x20 &&
++ dev_priv->chipset != 0x34)
++ ret = nv20_init_compute_mem(bios);
++ else if (dev_priv->chipset >= 0x10)
++ ret = nv10_init_compute_mem(bios);
++ else if (dev_priv->chipset >= 0x5)
++ ret = nv05_init_compute_mem(bios);
++ else
++ ret = nv04_init_compute_mem(bios);
+
+- /* write back the saved configuration value */
+- bios_wr32(bios, NV_PFB_CFG0, bios->state.saved_nv_pfb_cfg0);
++ if (ret)
++ return ret;
+
+ return 1;
+ }
+@@ -2131,7 +2471,8 @@ init_reset(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ /* no iexec->execute check by design */
+
+ pci_nv_19 = bios_rd32(bios, NV_PBUS_PCI_NV_19);
+- bios_wr32(bios, NV_PBUS_PCI_NV_19, 0);
++ bios_wr32(bios, NV_PBUS_PCI_NV_19, pci_nv_19 & ~0xf00);
++
+ bios_wr32(bios, reg, value1);
+
+ udelay(10);
+@@ -2167,7 +2508,7 @@ init_configure_mem(struct nvbios *bios, uint16_t offset,
+ uint32_t reg, data;
+
+ if (bios->major_version > 2)
+- return -ENODEV;
++ return 0;
+
+ bios_idxprt_wr(bios, NV_VIO_SRX, NV_VIO_SR_CLOCK_INDEX, bios_idxprt_rd(
+ bios, NV_VIO_SRX, NV_VIO_SR_CLOCK_INDEX) | 0x20);
+@@ -2180,14 +2521,14 @@ init_configure_mem(struct nvbios *bios, uint16_t offset,
+ reg = ROM32(bios->data[seqtbloffs += 4])) {
+
+ switch (reg) {
+- case NV_PFB_PRE:
+- data = NV_PFB_PRE_CMD_PRECHARGE;
++ case NV04_PFB_PRE:
++ data = NV04_PFB_PRE_CMD_PRECHARGE;
+ break;
+- case NV_PFB_PAD:
+- data = NV_PFB_PAD_CKE_NORMAL;
++ case NV04_PFB_PAD:
++ data = NV04_PFB_PAD_CKE_NORMAL;
+ break;
+- case NV_PFB_REF:
+- data = NV_PFB_REF_CMD_REFRESH;
++ case NV04_PFB_REF:
++ data = NV04_PFB_REF_CMD_REFRESH;
+ break;
+ default:
+ data = ROM32(bios->data[meminitdata]);
+@@ -2222,7 +2563,7 @@ init_configure_clk(struct nvbios *bios, uint16_t offset,
+ int clock;
+
+ if (bios->major_version > 2)
+- return -ENODEV;
++ return 0;
+
+ clock = ROM16(bios->data[meminitoffs + 4]) * 10;
+ setPLL(bios, NV_PRAMDAC_NVPLL_COEFF, clock);
+@@ -2255,7 +2596,7 @@ init_configure_preinit(struct nvbios *bios, uint16_t offset,
+ uint8_t cr3c = ((straps << 2) & 0xf0) | (straps & (1 << 6));
+
+ if (bios->major_version > 2)
+- return -ENODEV;
++ return 0;
+
+ bios_idxprt_wr(bios, NV_CIO_CRX__COLOR,
+ NV_CIO_CRE_SCRATCH4__INDEX, cr3c);
+@@ -2389,7 +2730,7 @@ init_ram_condition(struct nvbios *bios, uint16_t offset,
+ * offset + 1 (8 bit): mask
+ * offset + 2 (8 bit): cmpval
+ *
+- * Test if (NV_PFB_BOOT_0 & "mask") equals "cmpval".
++ * Test if (NV04_PFB_BOOT_0 & "mask") equals "cmpval".
+ * If condition not met skip subsequent opcodes until condition is
+ * inverted (INIT_NOT), or we hit INIT_RESUME
+ */
+@@ -2401,7 +2742,7 @@ init_ram_condition(struct nvbios *bios, uint16_t offset,
+ if (!iexec->execute)
+ return 3;
+
+- data = bios_rd32(bios, NV_PFB_BOOT_0) & mask;
++ data = bios_rd32(bios, NV04_PFB_BOOT_0) & mask;
+
+ BIOSLOG(bios, "0x%04X: Checking if 0x%08X equals 0x%08X\n",
+ offset, data, cmpval);
+@@ -2800,7 +3141,7 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+
+ if (dev_priv->card_type != NV_50) {
+ NV_ERROR(bios->dev, "INIT_GPIO on unsupported chipset\n");
+- return -ENODEV;
++ return 1;
+ }
+
+ if (!iexec->execute)
+@@ -2872,10 +3213,7 @@ init_ram_restrict_zm_reg_group(struct nvbios *bios, uint16_t offset,
+ uint8_t index;
+ int i;
+
+-
+- if (!iexec->execute)
+- return len;
+-
++ /* critical! to know the length of the opcode */;
+ if (!blocklen) {
+ NV_ERROR(bios->dev,
+ "0x%04X: Zero block length - has the M table "
+@@ -2883,6 +3221,9 @@ init_ram_restrict_zm_reg_group(struct nvbios *bios, uint16_t offset,
+ return -EINVAL;
+ }
+
++ if (!iexec->execute)
++ return len;
++
+ strap_ramcfg = (bios_rd32(bios, NV_PEXTDEV_BOOT_0) >> 2) & 0xf;
+ index = bios->data[bios->ram_restrict_tbl_ptr + strap_ramcfg];
+
+@@ -3064,14 +3405,14 @@ init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+
+ if (!bios->display.output) {
+ NV_ERROR(dev, "INIT_AUXCH: no active output\n");
+- return -EINVAL;
++ return len;
+ }
+
+ auxch = init_i2c_device_find(dev, bios->display.output->i2c_index);
+ if (!auxch) {
+ NV_ERROR(dev, "INIT_AUXCH: couldn't get auxch %d\n",
+ bios->display.output->i2c_index);
+- return -ENODEV;
++ return len;
+ }
+
+ if (!iexec->execute)
+@@ -3084,7 +3425,7 @@ init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ ret = nouveau_dp_auxch(auxch, 9, addr, &data, 1);
+ if (ret) {
+ NV_ERROR(dev, "INIT_AUXCH: rd auxch fail %d\n", ret);
+- return ret;
++ return len;
+ }
+
+ data &= bios->data[offset + 0];
+@@ -3093,7 +3434,7 @@ init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ ret = nouveau_dp_auxch(auxch, 8, addr, &data, 1);
+ if (ret) {
+ NV_ERROR(dev, "INIT_AUXCH: wr auxch fail %d\n", ret);
+- return ret;
++ return len;
+ }
+ }
+
+@@ -3123,14 +3464,14 @@ init_zm_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+
+ if (!bios->display.output) {
+ NV_ERROR(dev, "INIT_ZM_AUXCH: no active output\n");
+- return -EINVAL;
++ return len;
+ }
+
+ auxch = init_i2c_device_find(dev, bios->display.output->i2c_index);
+ if (!auxch) {
+ NV_ERROR(dev, "INIT_ZM_AUXCH: couldn't get auxch %d\n",
+ bios->display.output->i2c_index);
+- return -ENODEV;
++ return len;
+ }
+
+ if (!iexec->execute)
+@@ -3141,7 +3482,7 @@ init_zm_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+ ret = nouveau_dp_auxch(auxch, 8, addr, &bios->data[offset], 1);
+ if (ret) {
+ NV_ERROR(dev, "INIT_ZM_AUXCH: wr auxch fail %d\n", ret);
+- return ret;
++ return len;
+ }
+ }
+
+@@ -5151,10 +5492,14 @@ static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsi
+ bios->legacy.i2c_indices.crt = bios->data[legacy_i2c_offset];
+ bios->legacy.i2c_indices.tv = bios->data[legacy_i2c_offset + 1];
+ bios->legacy.i2c_indices.panel = bios->data[legacy_i2c_offset + 2];
+- bios->dcb.i2c[0].write = bios->data[legacy_i2c_offset + 4];
+- bios->dcb.i2c[0].read = bios->data[legacy_i2c_offset + 5];
+- bios->dcb.i2c[1].write = bios->data[legacy_i2c_offset + 6];
+- bios->dcb.i2c[1].read = bios->data[legacy_i2c_offset + 7];
++ if (bios->data[legacy_i2c_offset + 4])
++ bios->dcb.i2c[0].write = bios->data[legacy_i2c_offset + 4];
++ if (bios->data[legacy_i2c_offset + 5])
++ bios->dcb.i2c[0].read = bios->data[legacy_i2c_offset + 5];
++ if (bios->data[legacy_i2c_offset + 6])
++ bios->dcb.i2c[1].write = bios->data[legacy_i2c_offset + 6];
++ if (bios->data[legacy_i2c_offset + 7])
++ bios->dcb.i2c[1].read = bios->data[legacy_i2c_offset + 7];
+
+ if (bmplength > 74) {
+ bios->fmaxvco = ROM32(bmp[67]);
+@@ -5589,9 +5934,12 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
+ if (conf & 0x4 || conf & 0x8)
+ entry->lvdsconf.use_power_scripts = true;
+ } else {
+- mask = ~0x5;
++ mask = ~0x7;
++ if (conf & 0x2)
++ entry->lvdsconf.use_acpi_for_edid = true;
+ if (conf & 0x4)
+ entry->lvdsconf.use_power_scripts = true;
++ entry->lvdsconf.sor.link = (conf & 0x00000030) >> 4;
+ }
+ if (conf & mask) {
+ /*
+@@ -5706,13 +6054,6 @@ parse_dcb15_entry(struct drm_device *dev, struct dcb_table *dcb,
+ case OUTPUT_TV:
+ entry->tvconf.has_component_output = false;
+ break;
+- case OUTPUT_TMDS:
+- /*
+- * Invent a DVI-A output, by copying the fields of the DVI-D
+- * output; reported to work by math_b on an NV20(!).
+- */
+- fabricate_vga_output(dcb, entry->i2c_index, entry->heads);
+- break;
+ case OUTPUT_LVDS:
+ if ((conn & 0x00003f00) != 0x10)
+ entry->lvdsconf.use_straps_for_mode = true;
+@@ -5793,6 +6134,31 @@ void merge_like_dcb_entries(struct drm_device *dev, struct dcb_table *dcb)
+ dcb->entries = newentries;
+ }
+
++static bool
++apply_dcb_encoder_quirks(struct drm_device *dev, int idx, u32 *conn, u32 *conf)
++{
++ /* Dell Precision M6300
++ * DCB entry 2: 02025312 00000010
++ * DCB entry 3: 02026312 00000020
++ *
++ * Identical, except apparently a different connector on a
++ * different SOR link. Not a clue how we're supposed to know
++ * which one is in use if it even shares an i2c line...
++ *
++ * Ignore the connector on the second SOR link to prevent
++ * nasty problems until this is sorted (assuming it's not a
++ * VBIOS bug).
++ */
++ if ((dev->pdev->device == 0x040d) &&
++ (dev->pdev->subsystem_vendor == 0x1028) &&
++ (dev->pdev->subsystem_device == 0x019b)) {
++ if (*conn == 0x02026312 && *conf == 0x00000020)
++ return false;
++ }
++
++ return true;
++}
++
+ static int
+ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
+ {
+@@ -5926,6 +6292,9 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
+ if ((connection & 0x0000000f) == 0x0000000f)
+ continue;
+
++ if (!apply_dcb_encoder_quirks(dev, i, &connection, &config))
++ continue;
++
+ NV_TRACEWARN(dev, "Raw DCB entry %d: %08x %08x\n",
+ dcb->entries, connection, config);
+
+@@ -6182,8 +6551,10 @@ nouveau_run_vbios_init(struct drm_device *dev)
+ int i, ret = 0;
+
+ NVLockVgaCrtcs(dev, false);
+- if (nv_two_heads(dev))
+- NVSetOwner(dev, bios->state.crtchead);
++ if (nv_two_heads(dev)) {
++ bios->state.crtchead = 0;
++ NVSetOwner(dev, 0);
++ }
+
+ if (bios->major_version < 5) /* BMP only */
+ load_nv17_hw_sequencer_ucode(dev, bios);
+@@ -6238,7 +6609,6 @@ static bool
+ nouveau_bios_posted(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- bool was_locked;
+ unsigned htotal;
+
+ if (dev_priv->chipset >= NV_50) {
+@@ -6248,13 +6618,14 @@ nouveau_bios_posted(struct drm_device *dev)
+ return true;
+ }
+
+- was_locked = NVLockVgaCrtcs(dev, false);
++ NVLockVgaCrtcs(dev, false);
+ htotal = NVReadVgaCrtc(dev, 0, 0x06);
+ htotal |= (NVReadVgaCrtc(dev, 0, 0x07) & 0x01) << 8;
+ htotal |= (NVReadVgaCrtc(dev, 0, 0x07) & 0x20) << 4;
+ htotal |= (NVReadVgaCrtc(dev, 0, 0x25) & 0x01) << 10;
+ htotal |= (NVReadVgaCrtc(dev, 0, 0x41) & 0x01) << 11;
+- NVLockVgaCrtcs(dev, was_locked);
++ NVLockVgaCrtcs(dev, true);
++
+ return (htotal != 0);
+ }
+
+@@ -6263,8 +6634,6 @@ nouveau_bios_init(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nvbios *bios = &dev_priv->vbios;
+- uint32_t saved_nv_pextdev_boot_0;
+- bool was_locked;
+ int ret;
+
+ if (!NVInitVBIOS(dev))
+@@ -6284,40 +6653,29 @@ nouveau_bios_init(struct drm_device *dev)
+ if (!bios->major_version) /* we don't run version 0 bios */
+ return 0;
+
+- /* these will need remembering across a suspend */
+- saved_nv_pextdev_boot_0 = bios_rd32(bios, NV_PEXTDEV_BOOT_0);
+- bios->state.saved_nv_pfb_cfg0 = bios_rd32(bios, NV_PFB_CFG0);
+-
+ /* init script execution disabled */
+ bios->execute = false;
+
+ /* ... unless card isn't POSTed already */
+ if (!nouveau_bios_posted(dev)) {
+- NV_INFO(dev, "Adaptor not initialised\n");
+- if (dev_priv->card_type < NV_40) {
+- NV_ERROR(dev, "Unable to POST this chipset\n");
+- return -ENODEV;
+- }
+-
+- NV_INFO(dev, "Running VBIOS init tables\n");
++ NV_INFO(dev, "Adaptor not initialised, "
++ "running VBIOS init tables.\n");
+ bios->execute = true;
+ }
+
+- bios_wr32(bios, NV_PEXTDEV_BOOT_0, saved_nv_pextdev_boot_0);
+-
+ ret = nouveau_run_vbios_init(dev);
+ if (ret)
+ return ret;
+
+ /* feature_byte on BMP is poor, but init always sets CR4B */
+- was_locked = NVLockVgaCrtcs(dev, false);
++ NVLockVgaCrtcs(dev, false);
+ if (bios->major_version < 5)
+ bios->is_mobile = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_4B) & 0x40;
+
+ /* all BIT systems need p_f_m_t for digital_min_front_porch */
+ if (bios->is_mobile || bios->major_version >= 5)
+ ret = parse_fp_mode_table(dev, bios);
+- NVLockVgaCrtcs(dev, was_locked);
++ NVLockVgaCrtcs(dev, true);
+
+ /* allow subsequent scripts to execute */
+ bios->execute = true;
+diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h
+index adf4ec2..024458a 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_bios.h
++++ b/drivers/gpu/drm/nouveau/nouveau_bios.h
+@@ -81,6 +81,7 @@ struct dcb_connector_table_entry {
+ enum dcb_connector_type type;
+ uint8_t index2;
+ uint8_t gpio_tag;
++ void *drm;
+ };
+
+ struct dcb_connector_table {
+@@ -117,6 +118,7 @@ struct dcb_entry {
+ struct {
+ struct sor_conf sor;
+ bool use_straps_for_mode;
++ bool use_acpi_for_edid;
+ bool use_power_scripts;
+ } lvdsconf;
+ struct {
+@@ -249,8 +251,6 @@ struct nvbios {
+
+ struct {
+ int crtchead;
+- /* these need remembering across suspend */
+- uint32_t saved_nv_pfb_cfg0;
+ } state;
+
+ struct {
+diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c
+index 6f3c195..d8c341d 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_bo.c
++++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
+@@ -461,9 +461,9 @@ nouveau_bo_move_accel_cleanup(struct nouveau_channel *chan,
+ return ret;
+
+ ret = ttm_bo_move_accel_cleanup(&nvbo->bo, fence, NULL,
+- evict, no_wait_reserve, no_wait_gpu, new_mem);
+- if (nvbo->channel && nvbo->channel != chan)
+- ret = nouveau_fence_wait(fence, NULL, false, false);
++ evict || (nvbo->channel &&
++ nvbo->channel != chan),
++ no_wait_reserve, no_wait_gpu, new_mem);
+ nouveau_fence_unref((void *)&fence);
+ return ret;
+ }
+@@ -711,8 +711,7 @@ nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr,
+ return ret;
+
+ /* Software copy if the card isn't up and running yet. */
+- if (dev_priv->init_state != NOUVEAU_CARD_INIT_DONE ||
+- !dev_priv->channel) {
++ if (!dev_priv->channel) {
+ ret = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, new_mem);
+ goto out;
+ }
+diff --git a/drivers/gpu/drm/nouveau/nouveau_calc.c b/drivers/gpu/drm/nouveau/nouveau_calc.c
+index 88f9bc0..ca85da7 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_calc.c
++++ b/drivers/gpu/drm/nouveau/nouveau_calc.c
+@@ -200,7 +200,7 @@ nv04_update_arb(struct drm_device *dev, int VClk, int bpp,
+ struct nv_sim_state sim_data;
+ int MClk = nouveau_hw_get_clock(dev, MPLL);
+ int NVClk = nouveau_hw_get_clock(dev, NVPLL);
+- uint32_t cfg1 = nvReadFB(dev, NV_PFB_CFG1);
++ uint32_t cfg1 = nvReadFB(dev, NV04_PFB_CFG1);
+
+ sim_data.pclk_khz = VClk;
+ sim_data.mclk_khz = MClk;
+@@ -218,7 +218,7 @@ nv04_update_arb(struct drm_device *dev, int VClk, int bpp,
+ sim_data.mem_latency = 3;
+ sim_data.mem_page_miss = 10;
+ } else {
+- sim_data.memory_type = nvReadFB(dev, NV_PFB_CFG0) & 0x1;
++ sim_data.memory_type = nvReadFB(dev, NV04_PFB_CFG0) & 0x1;
+ sim_data.memory_width = (nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) & 0x10) ? 128 : 64;
+ sim_data.mem_latency = cfg1 & 0xf;
+ sim_data.mem_page_miss = ((cfg1 >> 4) & 0xf) + ((cfg1 >> 31) & 0x1);
+diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c
+index 1fc57ef..e952c3b 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_channel.c
++++ b/drivers/gpu/drm/nouveau/nouveau_channel.c
+@@ -257,9 +257,7 @@ nouveau_channel_free(struct nouveau_channel *chan)
+ nouveau_debugfs_channel_fini(chan);
+
+ /* Give outstanding push buffers a chance to complete */
+- spin_lock_irqsave(&chan->fence.lock, flags);
+ nouveau_fence_update(chan);
+- spin_unlock_irqrestore(&chan->fence.lock, flags);
+ if (chan->fence.sequence != chan->fence.sequence_ack) {
+ struct nouveau_fence *fence = NULL;
+
+@@ -368,8 +366,6 @@ nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data,
+ struct nouveau_channel *chan;
+ int ret;
+
+- NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+-
+ if (dev_priv->engine.graph.accel_blocked)
+ return -ENODEV;
+
+@@ -418,7 +414,6 @@ nouveau_ioctl_fifo_free(struct drm_device *dev, void *data,
+ struct drm_nouveau_channel_free *cfree = data;
+ struct nouveau_channel *chan;
+
+- NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+ NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(cfree->channel, file_priv, chan);
+
+ nouveau_channel_free(chan);
+diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
+index 149ed22..734e926 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
++++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
+@@ -102,63 +102,15 @@ nouveau_connector_destroy(struct drm_connector *drm_connector)
+ kfree(drm_connector);
+ }
+
+-static void
+-nouveau_connector_ddc_prepare(struct drm_connector *connector, int *flags)
+-{
+- struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
+-
+- if (dev_priv->card_type >= NV_50)
+- return;
+-
+- *flags = 0;
+- if (NVLockVgaCrtcs(dev_priv->dev, false))
+- *flags |= 1;
+- if (nv_heads_tied(dev_priv->dev))
+- *flags |= 2;
+-
+- if (*flags & 2)
+- NVSetOwner(dev_priv->dev, 0); /* necessary? */
+-}
+-
+-static void
+-nouveau_connector_ddc_finish(struct drm_connector *connector, int flags)
+-{
+- struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
+-
+- if (dev_priv->card_type >= NV_50)
+- return;
+-
+- if (flags & 2)
+- NVSetOwner(dev_priv->dev, 4);
+- if (flags & 1)
+- NVLockVgaCrtcs(dev_priv->dev, true);
+-}
+-
+ static struct nouveau_i2c_chan *
+ nouveau_connector_ddc_detect(struct drm_connector *connector,
+ struct nouveau_encoder **pnv_encoder)
+ {
+ struct drm_device *dev = connector->dev;
+- uint8_t out_buf[] = { 0x0, 0x0}, buf[2];
+- int ret, flags, i;
+-
+- struct i2c_msg msgs[] = {
+- {
+- .addr = 0x50,
+- .flags = 0,
+- .len = 1,
+- .buf = out_buf,
+- },
+- {
+- .addr = 0x50,
+- .flags = I2C_M_RD,
+- .len = 1,
+- .buf = buf,
+- }
+- };
++ int i;
+
+ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
+- struct nouveau_i2c_chan *i2c = NULL;
++ struct nouveau_i2c_chan *i2c;
+ struct nouveau_encoder *nv_encoder;
+ struct drm_mode_object *obj;
+ int id;
+@@ -171,17 +123,9 @@ nouveau_connector_ddc_detect(struct drm_connector *connector,
+ if (!obj)
+ continue;
+ nv_encoder = nouveau_encoder(obj_to_encoder(obj));
++ i2c = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
+
+- if (nv_encoder->dcb->i2c_index < 0xf)
+- i2c = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
+- if (!i2c)
+- continue;
+-
+- nouveau_connector_ddc_prepare(connector, &flags);
+- ret = i2c_transfer(&i2c->adapter, msgs, 2);
+- nouveau_connector_ddc_finish(connector, flags);
+-
+- if (ret == 2) {
++ if (i2c && nouveau_probe_i2c_addr(i2c, 0x50)) {
+ *pnv_encoder = nv_encoder;
+ return i2c;
+ }
+@@ -234,21 +178,7 @@ nouveau_connector_detect(struct drm_connector *connector)
+ struct nouveau_connector *nv_connector = nouveau_connector(connector);
+ struct nouveau_encoder *nv_encoder = NULL;
+ struct nouveau_i2c_chan *i2c;
+- int type, flags;
+-
+- if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS)
+- nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS);
+- if (nv_encoder && nv_connector->native_mode) {
+- unsigned status = connector_status_connected;
+-
+-#if defined(CONFIG_ACPI_BUTTON) || \
+- (defined(CONFIG_ACPI_BUTTON_MODULE) && defined(MODULE))
+- if (!nouveau_ignorelid && !acpi_lid_open())
+- status = connector_status_unknown;
+-#endif
+- nouveau_connector_set_encoder(connector, nv_encoder);
+- return status;
+- }
++ int type;
+
+ /* Cleanup the previous EDID block. */
+ if (nv_connector->edid) {
+@@ -259,9 +189,7 @@ nouveau_connector_detect(struct drm_connector *connector)
+
+ i2c = nouveau_connector_ddc_detect(connector, &nv_encoder);
+ if (i2c) {
+- nouveau_connector_ddc_prepare(connector, &flags);
+ nv_connector->edid = drm_get_edid(connector, &i2c->adapter);
+- nouveau_connector_ddc_finish(connector, flags);
+ drm_mode_connector_update_edid_property(connector,
+ nv_connector->edid);
+ if (!nv_connector->edid) {
+@@ -321,6 +249,85 @@ detect_analog:
+ return connector_status_disconnected;
+ }
+
++static enum drm_connector_status
++nouveau_connector_detect_lvds(struct drm_connector *connector)
++{
++ struct drm_device *dev = connector->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_connector *nv_connector = nouveau_connector(connector);
++ struct nouveau_encoder *nv_encoder = NULL;
++ enum drm_connector_status status = connector_status_disconnected;
++
++ /* Cleanup the previous EDID block. */
++ if (nv_connector->edid) {
++ drm_mode_connector_update_edid_property(connector, NULL);
++ kfree(nv_connector->edid);
++ nv_connector->edid = NULL;
++ }
++
++ nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS);
++ if (!nv_encoder)
++ return connector_status_disconnected;
++
++ /* Try retrieving EDID via DDC */
++ if (!dev_priv->vbios.fp_no_ddc) {
++ status = nouveau_connector_detect(connector);
++ if (status == connector_status_connected)
++ goto out;
++ }
++
++ /* On some laptops (Sony, i'm looking at you) there appears to
++ * be no direct way of accessing the panel's EDID. The only
++ * option available to us appears to be to ask ACPI for help..
++ *
++ * It's important this check's before trying straps, one of the
++ * said manufacturer's laptops are configured in such a way
++ * the nouveau decides an entry in the VBIOS FP mode table is
++ * valid - it's not (rh#613284)
++ */
++ if (nv_encoder->dcb->lvdsconf.use_acpi_for_edid) {
++ if (!nouveau_acpi_edid(dev, connector)) {
++ status = connector_status_connected;
++ goto out;
++ }
++ }
++
++ /* If no EDID found above, and the VBIOS indicates a hardcoded
++ * modeline is avalilable for the panel, set it as the panel's
++ * native mode and exit.
++ */
++ if (nouveau_bios_fp_mode(dev, NULL) && (dev_priv->vbios.fp_no_ddc ||
++ nv_encoder->dcb->lvdsconf.use_straps_for_mode)) {
++ status = connector_status_connected;
++ goto out;
++ }
++
++ /* Still nothing, some VBIOS images have a hardcoded EDID block
++ * stored for the panel stored in them.
++ */
++ if (!dev_priv->vbios.fp_no_ddc) {
++ struct edid *edid =
++ (struct edid *)nouveau_bios_embedded_edid(dev);
++ if (edid) {
++ nv_connector->edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
++ *(nv_connector->edid) = *edid;
++ status = connector_status_connected;
++ }
++ }
++
++out:
++#if defined(CONFIG_ACPI_BUTTON) || \
++ (defined(CONFIG_ACPI_BUTTON_MODULE) && defined(MODULE))
++ if (status == connector_status_connected &&
++ !nouveau_ignorelid && !acpi_lid_open())
++ status = connector_status_unknown;
++#endif
++
++ drm_mode_connector_update_edid_property(connector, nv_connector->edid);
++ nouveau_connector_set_encoder(connector, nv_encoder);
++ return status;
++}
++
+ static void
+ nouveau_connector_force(struct drm_connector *connector)
+ {
+@@ -441,7 +448,8 @@ nouveau_connector_native_mode(struct drm_connector *connector)
+ int high_w = 0, high_h = 0, high_v = 0;
+
+ list_for_each_entry(mode, &nv_connector->base.probed_modes, head) {
+- if (helper->mode_valid(connector, mode) != MODE_OK)
++ if (helper->mode_valid(connector, mode) != MODE_OK ||
++ (mode->flags & DRM_MODE_FLAG_INTERLACE))
+ continue;
+
+ /* Use preferred mode if there is one.. */
+@@ -534,21 +542,27 @@ static int
+ nouveau_connector_get_modes(struct drm_connector *connector)
+ {
+ struct drm_device *dev = connector->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_connector *nv_connector = nouveau_connector(connector);
+ struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
+ int ret = 0;
+
+- /* If we're not LVDS, destroy the previous native mode, the attached
+- * monitor could have changed.
++ /* destroy the native mode, the attached monitor could have changed.
+ */
+- if (nv_connector->dcb->type != DCB_CONNECTOR_LVDS &&
+- nv_connector->native_mode) {
++ if (nv_connector->native_mode) {
+ drm_mode_destroy(dev, nv_connector->native_mode);
+ nv_connector->native_mode = NULL;
+ }
+
+ if (nv_connector->edid)
+ ret = drm_add_edid_modes(connector, nv_connector->edid);
++ else
++ if (nv_encoder->dcb->type == OUTPUT_LVDS &&
++ (nv_encoder->dcb->lvdsconf.use_straps_for_mode ||
++ dev_priv->vbios.fp_no_ddc) && nouveau_bios_fp_mode(dev, NULL)) {
++ nv_connector->native_mode = drm_mode_create(dev);
++ nouveau_bios_fp_mode(dev, nv_connector->native_mode);
++ }
+
+ /* Find the native mode if this is a digital panel, if we didn't
+ * find any modes through DDC previously add the native mode to
+@@ -569,7 +583,8 @@ nouveau_connector_get_modes(struct drm_connector *connector)
+ ret = get_slave_funcs(nv_encoder)->
+ get_modes(to_drm_encoder(nv_encoder), connector);
+
+- if (nv_encoder->dcb->type == OUTPUT_LVDS)
++ if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS ||
++ nv_connector->dcb->type == DCB_CONNECTOR_eDP)
+ ret += nouveau_connector_scaler_modes_add(connector);
+
+ return ret;
+@@ -643,6 +658,44 @@ nouveau_connector_best_encoder(struct drm_connector *connector)
+ return NULL;
+ }
+
++void
++nouveau_connector_set_polling(struct drm_connector *connector)
++{
++ struct drm_device *dev = connector->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct drm_crtc *crtc;
++ bool spare_crtc = false;
++
++ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
++ spare_crtc |= !crtc->enabled;
++
++ connector->polled = 0;
++
++ switch (connector->connector_type) {
++ case DRM_MODE_CONNECTOR_VGA:
++ case DRM_MODE_CONNECTOR_TV:
++ if (dev_priv->card_type >= NV_50 ||
++ (nv_gf4_disp_arch(dev) && spare_crtc))
++ connector->polled = DRM_CONNECTOR_POLL_CONNECT;
++ break;
++
++ case DRM_MODE_CONNECTOR_DVII:
++ case DRM_MODE_CONNECTOR_DVID:
++ case DRM_MODE_CONNECTOR_HDMIA:
++ case DRM_MODE_CONNECTOR_DisplayPort:
++ case DRM_MODE_CONNECTOR_eDP:
++ if (dev_priv->card_type >= NV_50)
++ connector->polled = DRM_CONNECTOR_POLL_HPD;
++ else if (connector->connector_type == DRM_MODE_CONNECTOR_DVID ||
++ spare_crtc)
++ connector->polled = DRM_CONNECTOR_POLL_CONNECT;
++ break;
++
++ default:
++ break;
++ }
++}
++
+ static const struct drm_connector_helper_funcs
+ nouveau_connector_helper_funcs = {
+ .get_modes = nouveau_connector_get_modes,
+@@ -662,148 +715,74 @@ nouveau_connector_funcs = {
+ .force = nouveau_connector_force
+ };
+
+-static int
+-nouveau_connector_create_lvds(struct drm_device *dev,
+- struct drm_connector *connector)
+-{
+- struct nouveau_connector *nv_connector = nouveau_connector(connector);
+- struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nouveau_i2c_chan *i2c = NULL;
+- struct nouveau_encoder *nv_encoder;
+- struct drm_display_mode native, *mode, *temp;
+- bool dummy, if_is_24bit = false;
+- int ret, flags;
+-
+- nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS);
+- if (!nv_encoder)
+- return -ENODEV;
+-
+- ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &if_is_24bit);
+- if (ret) {
+- NV_ERROR(dev, "Error parsing LVDS table, disabling LVDS\n");
+- return ret;
+- }
+- nv_connector->use_dithering = !if_is_24bit;
+-
+- /* Firstly try getting EDID over DDC, if allowed and I2C channel
+- * is available.
+- */
+- if (!dev_priv->vbios.fp_no_ddc && nv_encoder->dcb->i2c_index < 0xf)
+- i2c = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
+-
+- if (i2c) {
+- nouveau_connector_ddc_prepare(connector, &flags);
+- nv_connector->edid = drm_get_edid(connector, &i2c->adapter);
+- nouveau_connector_ddc_finish(connector, flags);
+- }
+-
+- /* If no EDID found above, and the VBIOS indicates a hardcoded
+- * modeline is avalilable for the panel, set it as the panel's
+- * native mode and exit.
+- */
+- if (!nv_connector->edid && nouveau_bios_fp_mode(dev, &native) &&
+- (nv_encoder->dcb->lvdsconf.use_straps_for_mode ||
+- dev_priv->vbios.fp_no_ddc)) {
+- nv_connector->native_mode = drm_mode_duplicate(dev, &native);
+- goto out;
+- }
+-
+- /* Still nothing, some VBIOS images have a hardcoded EDID block
+- * stored for the panel stored in them.
+- */
+- if (!nv_connector->edid && !nv_connector->native_mode &&
+- !dev_priv->vbios.fp_no_ddc) {
+- struct edid *edid =
+- (struct edid *)nouveau_bios_embedded_edid(dev);
+- if (edid) {
+- nv_connector->edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
+- *(nv_connector->edid) = *edid;
+- }
+- }
+-
+- if (!nv_connector->edid)
+- goto out;
+-
+- /* We didn't find/use a panel mode from the VBIOS, so parse the EDID
+- * block and look for the preferred mode there.
+- */
+- ret = drm_add_edid_modes(connector, nv_connector->edid);
+- if (ret == 0)
+- goto out;
+- nv_connector->detected_encoder = nv_encoder;
+- nv_connector->native_mode = nouveau_connector_native_mode(connector);
+- list_for_each_entry_safe(mode, temp, &connector->probed_modes, head)
+- drm_mode_remove(connector, mode);
+-
+-out:
+- if (!nv_connector->native_mode) {
+- NV_ERROR(dev, "LVDS present in DCB table, but couldn't "
+- "determine its native mode. Disabling.\n");
+- return -ENODEV;
+- }
+-
+- drm_mode_connector_update_edid_property(connector, nv_connector->edid);
+- return 0;
+-}
++static const struct drm_connector_funcs
++nouveau_connector_funcs_lvds = {
++ .dpms = drm_helper_connector_dpms,
++ .save = NULL,
++ .restore = NULL,
++ .detect = nouveau_connector_detect_lvds,
++ .destroy = nouveau_connector_destroy,
++ .fill_modes = drm_helper_probe_single_connector_modes,
++ .set_property = nouveau_connector_set_property,
++ .force = nouveau_connector_force
++};
+
+-int
+-nouveau_connector_create(struct drm_device *dev,
+- struct dcb_connector_table_entry *dcb)
++struct drm_connector *
++nouveau_connector_create(struct drm_device *dev, int index)
+ {
++ const struct drm_connector_funcs *funcs = &nouveau_connector_funcs;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_connector *nv_connector = NULL;
++ struct dcb_connector_table_entry *dcb = NULL;
+ struct drm_connector *connector;
+- struct drm_encoder *encoder;
+- int ret, type;
++ int type, ret = 0;
+
+ NV_DEBUG_KMS(dev, "\n");
+
++ if (index >= dev_priv->vbios.dcb.connector.entries)
++ return ERR_PTR(-EINVAL);
++
++ dcb = &dev_priv->vbios.dcb.connector.entry[index];
++ if (dcb->drm)
++ return dcb->drm;
++
+ switch (dcb->type) {
+- case DCB_CONNECTOR_NONE:
+- return 0;
+ case DCB_CONNECTOR_VGA:
+- NV_INFO(dev, "Detected a VGA connector\n");
+ type = DRM_MODE_CONNECTOR_VGA;
+ break;
+ case DCB_CONNECTOR_TV_0:
+ case DCB_CONNECTOR_TV_1:
+ case DCB_CONNECTOR_TV_3:
+- NV_INFO(dev, "Detected a TV connector\n");
+ type = DRM_MODE_CONNECTOR_TV;
+ break;
+ case DCB_CONNECTOR_DVI_I:
+- NV_INFO(dev, "Detected a DVI-I connector\n");
+ type = DRM_MODE_CONNECTOR_DVII;
+ break;
+ case DCB_CONNECTOR_DVI_D:
+- NV_INFO(dev, "Detected a DVI-D connector\n");
+ type = DRM_MODE_CONNECTOR_DVID;
+ break;
+ case DCB_CONNECTOR_HDMI_0:
+ case DCB_CONNECTOR_HDMI_1:
+- NV_INFO(dev, "Detected a HDMI connector\n");
+ type = DRM_MODE_CONNECTOR_HDMIA;
+ break;
+ case DCB_CONNECTOR_LVDS:
+- NV_INFO(dev, "Detected a LVDS connector\n");
+ type = DRM_MODE_CONNECTOR_LVDS;
++ funcs = &nouveau_connector_funcs_lvds;
+ break;
+ case DCB_CONNECTOR_DP:
+- NV_INFO(dev, "Detected a DisplayPort connector\n");
+ type = DRM_MODE_CONNECTOR_DisplayPort;
+ break;
+ case DCB_CONNECTOR_eDP:
+- NV_INFO(dev, "Detected an eDP connector\n");
+ type = DRM_MODE_CONNECTOR_eDP;
+ break;
+ default:
+ NV_ERROR(dev, "unknown connector type: 0x%02x!!\n", dcb->type);
+- return -EINVAL;
++ return ERR_PTR(-EINVAL);
+ }
+
+ nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL);
+ if (!nv_connector)
+- return -ENOMEM;
++ return ERR_PTR(-ENOMEM);
+ nv_connector->dcb = dcb;
+ connector = &nv_connector->base;
+
+@@ -811,27 +790,21 @@ nouveau_connector_create(struct drm_device *dev,
+ connector->interlace_allowed = false;
+ connector->doublescan_allowed = false;
+
+- drm_connector_init(dev, connector, &nouveau_connector_funcs, type);
++ drm_connector_init(dev, connector, funcs, type);
+ drm_connector_helper_add(connector, &nouveau_connector_helper_funcs);
+
+- /* attach encoders */
+- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+-
+- if (nv_encoder->dcb->connector != dcb->index)
+- continue;
+-
+- if (get_slave_funcs(nv_encoder))
+- get_slave_funcs(nv_encoder)->create_resources(encoder, connector);
++ /* Check if we need dithering enabled */
++ if (dcb->type == DCB_CONNECTOR_LVDS) {
++ bool dummy, is_24bit = false;
+
+- drm_mode_connector_attach_encoder(connector, encoder);
+- }
++ ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &is_24bit);
++ if (ret) {
++ NV_ERROR(dev, "Error parsing LVDS table, disabling "
++ "LVDS\n");
++ goto fail;
++ }
+
+- if (!connector->encoder_ids[0]) {
+- NV_WARN(dev, " no encoders, ignoring\n");
+- drm_connector_cleanup(connector);
+- kfree(connector);
+- return 0;
++ nv_connector->use_dithering = !is_24bit;
+ }
+
+ /* Init DVI-I specific properties */
+@@ -841,12 +814,8 @@ nouveau_connector_create(struct drm_device *dev,
+ drm_connector_attach_property(connector, dev->mode_config.dvi_i_select_subconnector_property, 0);
+ }
+
+- if (dcb->type != DCB_CONNECTOR_LVDS)
+- nv_connector->use_dithering = false;
+-
+ switch (dcb->type) {
+ case DCB_CONNECTOR_VGA:
+- connector->polled = DRM_CONNECTOR_POLL_CONNECT;
+ if (dev_priv->card_type >= NV_50) {
+ drm_connector_attach_property(connector,
+ dev->mode_config.scaling_mode_property,
+@@ -858,17 +827,6 @@ nouveau_connector_create(struct drm_device *dev,
+ case DCB_CONNECTOR_TV_3:
+ nv_connector->scaling_mode = DRM_MODE_SCALE_NONE;
+ break;
+- case DCB_CONNECTOR_DP:
+- case DCB_CONNECTOR_eDP:
+- case DCB_CONNECTOR_HDMI_0:
+- case DCB_CONNECTOR_HDMI_1:
+- case DCB_CONNECTOR_DVI_I:
+- case DCB_CONNECTOR_DVI_D:
+- if (dev_priv->card_type >= NV_50)
+- connector->polled = DRM_CONNECTOR_POLL_HPD;
+- else
+- connector->polled = DRM_CONNECTOR_POLL_CONNECT;
+- /* fall-through */
+ default:
+ nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN;
+
+@@ -882,15 +840,15 @@ nouveau_connector_create(struct drm_device *dev,
+ break;
+ }
+
++ nouveau_connector_set_polling(connector);
++
+ drm_sysfs_connector_add(connector);
++ dcb->drm = connector;
++ return dcb->drm;
+
+- if (dcb->type == DCB_CONNECTOR_LVDS) {
+- ret = nouveau_connector_create_lvds(dev, connector);
+- if (ret) {
+- connector->funcs->destroy(connector);
+- return ret;
+- }
+- }
++fail:
++ drm_connector_cleanup(connector);
++ kfree(connector);
++ return ERR_PTR(ret);
+
+- return 0;
+ }
+diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h
+index 4ef38ab..0d2e668 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_connector.h
++++ b/drivers/gpu/drm/nouveau/nouveau_connector.h
+@@ -49,7 +49,10 @@ static inline struct nouveau_connector *nouveau_connector(
+ return container_of(con, struct nouveau_connector, base);
+ }
+
+-int nouveau_connector_create(struct drm_device *,
+- struct dcb_connector_table_entry *);
++struct drm_connector *
++nouveau_connector_create(struct drm_device *, int index);
++
++void
++nouveau_connector_set_polling(struct drm_connector *);
+
+ #endif /* __NOUVEAU_CONNECTOR_H__ */
+diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.c b/drivers/gpu/drm/nouveau/nouveau_dma.c
+index 65c441a..2e3c6ca 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_dma.c
++++ b/drivers/gpu/drm/nouveau/nouveau_dma.c
+@@ -92,11 +92,9 @@ nouveau_dma_init(struct nouveau_channel *chan)
+ return ret;
+
+ /* Map M2MF notifier object - fbcon. */
+- if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+- ret = nouveau_bo_map(chan->notifier_bo);
+- if (ret)
+- return ret;
+- }
++ ret = nouveau_bo_map(chan->notifier_bo);
++ if (ret)
++ return ret;
+
+ /* Insert NOPS for NOUVEAU_DMA_SKIPS */
+ ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS);
+diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
+index deeb21c..184bc95 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_dp.c
++++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
+@@ -271,12 +271,26 @@ nouveau_dp_link_train(struct drm_encoder *encoder)
+ {
+ struct drm_device *dev = encoder->dev;
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+- uint8_t config[4];
+- uint8_t status[3];
++ struct bit_displayport_encoder_table *dpe;
++ int dpe_headerlen;
++ uint8_t config[4], status[3];
+ bool cr_done, cr_max_vs, eq_done;
+ int ret = 0, i, tries, voltage;
+
+ NV_DEBUG_KMS(dev, "link training!!\n");
++
++ dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
++ if (!dpe) {
++ NV_ERROR(dev, "SOR-%d: no DP encoder table!\n", nv_encoder->or);
++ return false;
++ }
++
++ if (dpe->script0) {
++ NV_DEBUG_KMS(dev, "SOR-%d: running DP script 0\n", nv_encoder->or);
++ nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script0),
++ nv_encoder->dcb);
++ }
++
+ train:
+ cr_done = eq_done = false;
+
+@@ -403,6 +417,12 @@ stop:
+ }
+ }
+
++ if (dpe->script1) {
++ NV_DEBUG_KMS(dev, "SOR-%d: running DP script 1\n", nv_encoder->or);
++ nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script1),
++ nv_encoder->dcb);
++ }
++
+ return eq_done;
+ }
+
+diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c
+index 2737704..203d0b6 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_drv.c
++++ b/drivers/gpu/drm/nouveau/nouveau_drv.c
+@@ -35,10 +35,6 @@
+
+ #include "drm_pciids.h"
+
+-MODULE_PARM_DESC(ctxfw, "Use external firmware blob for grctx init (NV40)");
+-int nouveau_ctxfw = 0;
+-module_param_named(ctxfw, nouveau_ctxfw, int, 0400);
+-
+ MODULE_PARM_DESC(noagp, "Disable AGP");
+ int nouveau_noagp;
+ module_param_named(noagp, nouveau_noagp, int, 0400);
+@@ -56,7 +52,7 @@ int nouveau_vram_pushbuf;
+ module_param_named(vram_pushbuf, nouveau_vram_pushbuf, int, 0400);
+
+ MODULE_PARM_DESC(vram_notify, "Force DMA notifiers to be in VRAM");
+-int nouveau_vram_notify = 1;
++int nouveau_vram_notify = 0;
+ module_param_named(vram_notify, nouveau_vram_notify, int, 0400);
+
+ MODULE_PARM_DESC(duallink, "Allow dual-link TMDS (>=GeForce 8)");
+@@ -155,9 +151,6 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state)
+ struct drm_crtc *crtc;
+ int ret, i;
+
+- if (!drm_core_check_feature(dev, DRIVER_MODESET))
+- return -ENODEV;
+-
+ if (pm_state.event == PM_EVENT_PRETHAW)
+ return 0;
+
+@@ -257,9 +250,6 @@ nouveau_pci_resume(struct pci_dev *pdev)
+ struct drm_crtc *crtc;
+ int ret, i;
+
+- if (!drm_core_check_feature(dev, DRIVER_MODESET))
+- return -ENODEV;
+-
+ nouveau_fbcon_save_disable_accel(dev);
+
+ NV_INFO(dev, "We're back, enabling device...\n");
+@@ -323,7 +313,6 @@ nouveau_pci_resume(struct pci_dev *pdev)
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+- int ret;
+
+ ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM);
+ if (!ret)
+@@ -332,10 +321,9 @@ nouveau_pci_resume(struct pci_dev *pdev)
+ NV_ERROR(dev, "Could not pin/map cursor.\n");
+ }
+
+- if (dev_priv->card_type < NV_50) {
++ if (dev_priv->card_type < NV_50)
+ nv04_display_restore(dev);
+- NVLockVgaCrtcs(dev, false);
+- } else
++ else
+ nv50_display_init(dev);
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+@@ -371,7 +359,8 @@ nouveau_pci_resume(struct pci_dev *pdev)
+ static struct drm_driver driver = {
+ .driver_features =
+ DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_SG |
+- DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM,
++ DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM |
++ DRIVER_MODESET,
+ .load = nouveau_load,
+ .firstopen = nouveau_firstopen,
+ .lastclose = nouveau_lastclose,
+@@ -438,16 +427,18 @@ static int __init nouveau_init(void)
+ nouveau_modeset = 1;
+ }
+
+- if (nouveau_modeset == 1) {
+- driver.driver_features |= DRIVER_MODESET;
+- nouveau_register_dsm_handler();
+- }
++ if (!nouveau_modeset)
++ return 0;
+
++ nouveau_register_dsm_handler();
+ return drm_init(&driver);
+ }
+
+ static void __exit nouveau_exit(void)
+ {
++ if (!nouveau_modeset)
++ return;
++
+ drm_exit(&driver);
+ nouveau_unregister_dsm_handler();
+ }
+diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
+index c697191..20ca5b8 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
++++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
+@@ -123,14 +123,6 @@ nvbo_kmap_obj_iovirtual(struct nouveau_bo *nvbo)
+ return ioptr;
+ }
+
+-struct mem_block {
+- struct mem_block *next;
+- struct mem_block *prev;
+- uint64_t start;
+- uint64_t size;
+- struct drm_file *file_priv; /* NULL: free, -1: heap, other: real files */
+-};
+-
+ enum nouveau_flags {
+ NV_NFORCE = 0x10000000,
+ NV_NFORCE2 = 0x20000000
+@@ -149,7 +141,7 @@ struct nouveau_gpuobj {
+ struct list_head list;
+
+ struct nouveau_channel *im_channel;
+- struct mem_block *im_pramin;
++ struct drm_mm_node *im_pramin;
+ struct nouveau_bo *im_backing;
+ uint32_t im_backing_start;
+ uint32_t *im_backing_suspend;
+@@ -196,7 +188,7 @@ struct nouveau_channel {
+ struct list_head pending;
+ uint32_t sequence;
+ uint32_t sequence_ack;
+- uint32_t last_sequence_irq;
++ atomic_t last_sequence_irq;
+ } fence;
+
+ /* DMA push buffer */
+@@ -206,7 +198,7 @@ struct nouveau_channel {
+
+ /* Notifier memory */
+ struct nouveau_bo *notifier_bo;
+- struct mem_block *notifier_heap;
++ struct drm_mm notifier_heap;
+
+ /* PFIFO context */
+ struct nouveau_gpuobj_ref *ramfc;
+@@ -224,7 +216,7 @@ struct nouveau_channel {
+
+ /* Objects */
+ struct nouveau_gpuobj_ref *ramin; /* Private instmem */
+- struct mem_block *ramin_heap; /* Private PRAMIN heap */
++ struct drm_mm ramin_heap; /* Private PRAMIN heap */
+ struct nouveau_gpuobj_ref *ramht; /* Hash table */
+ struct list_head ramht_refs; /* Objects referenced by RAMHT */
+
+@@ -277,8 +269,7 @@ struct nouveau_instmem_engine {
+ void (*clear)(struct drm_device *, struct nouveau_gpuobj *);
+ int (*bind)(struct drm_device *, struct nouveau_gpuobj *);
+ int (*unbind)(struct drm_device *, struct nouveau_gpuobj *);
+- void (*prepare_access)(struct drm_device *, bool write);
+- void (*finish_access)(struct drm_device *);
++ void (*flush)(struct drm_device *);
+ };
+
+ struct nouveau_mc_engine {
+@@ -303,10 +294,11 @@ struct nouveau_fb_engine {
+ };
+
+ struct nouveau_fifo_engine {
+- void *priv;
+-
+ int channels;
+
++ struct nouveau_gpuobj_ref *playlist[2];
++ int cur_playlist;
++
+ int (*init)(struct drm_device *);
+ void (*takedown)(struct drm_device *);
+
+@@ -339,10 +331,11 @@ struct nouveau_pgraph_object_class {
+ struct nouveau_pgraph_engine {
+ struct nouveau_pgraph_object_class *grclass;
+ bool accel_blocked;
+- void *ctxprog;
+- void *ctxvals;
+ int grctx_size;
+
++ /* NV2x/NV3x context table (0x400780) */
++ struct nouveau_gpuobj_ref *ctx_table;
++
+ int (*init)(struct drm_device *);
+ void (*takedown)(struct drm_device *);
+
+@@ -500,11 +493,6 @@ enum nouveau_card_type {
+
+ struct drm_nouveau_private {
+ struct drm_device *dev;
+- enum {
+- NOUVEAU_CARD_INIT_DOWN,
+- NOUVEAU_CARD_INIT_DONE,
+- NOUVEAU_CARD_INIT_FAILED
+- } init_state;
+
+ /* the card type, takes NV_* as values */
+ enum nouveau_card_type card_type;
+@@ -533,8 +521,6 @@ struct drm_nouveau_private {
+ atomic_t validate_sequence;
+ } ttm;
+
+- struct fb_info *fbdev_info;
+-
+ int fifo_alloc_count;
+ struct nouveau_channel *fifos[NOUVEAU_MAX_CHANNEL_NR];
+
+@@ -595,11 +581,7 @@ struct drm_nouveau_private {
+ struct nouveau_gpuobj *vm_vram_pt[NV50_VM_VRAM_NR];
+ int vm_vram_pt_nr;
+
+- struct mem_block *ramin_heap;
+-
+- /* context table pointed to be NV_PGRAPH_CHANNEL_CTX_TABLE (0x400780) */
+- uint32_t ctx_table_size;
+- struct nouveau_gpuobj_ref *ctx_table;
++ struct drm_mm ramin_heap;
+
+ struct list_head gpuobj_list;
+
+@@ -618,6 +600,11 @@ struct drm_nouveau_private {
+ struct backlight_device *backlight;
+
+ struct nouveau_channel *evo;
++ struct {
++ struct dcb_entry *dcb;
++ u16 script;
++ u32 pclk;
++ } evo_irq;
+
+ struct {
+ struct dentry *channel_root;
+@@ -652,14 +639,6 @@ nouveau_bo_ref(struct nouveau_bo *ref, struct nouveau_bo **pnvbo)
+ return 0;
+ }
+
+-#define NOUVEAU_CHECK_INITIALISED_WITH_RETURN do { \
+- struct drm_nouveau_private *nv = dev->dev_private; \
+- if (nv->init_state != NOUVEAU_CARD_INIT_DONE) { \
+- NV_ERROR(dev, "called without init\n"); \
+- return -EINVAL; \
+- } \
+-} while (0)
+-
+ #define NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(id, cl, ch) do { \
+ struct drm_nouveau_private *nv = dev->dev_private; \
+ if (!nouveau_channel_owner(dev, (cl), (id))) { \
+@@ -682,7 +661,6 @@ extern int nouveau_tv_disable;
+ extern char *nouveau_tv_norm;
+ extern int nouveau_reg_debug;
+ extern char *nouveau_vbios;
+-extern int nouveau_ctxfw;
+ extern int nouveau_ignorelid;
+ extern int nouveau_nofbaccel;
+ extern int nouveau_noaccel;
+@@ -707,15 +685,7 @@ extern bool nouveau_wait_for_idle(struct drm_device *);
+ extern int nouveau_card_init(struct drm_device *);
+
+ /* nouveau_mem.c */
+-extern int nouveau_mem_init_heap(struct mem_block **, uint64_t start,
+- uint64_t size);
+-extern struct mem_block *nouveau_mem_alloc_block(struct mem_block *,
+- uint64_t size, int align2,
+- struct drm_file *, int tail);
+-extern void nouveau_mem_takedown(struct mem_block **heap);
+-extern void nouveau_mem_free_block(struct mem_block *);
+ extern int nouveau_mem_detect(struct drm_device *dev);
+-extern void nouveau_mem_release(struct drm_file *, struct mem_block *heap);
+ extern int nouveau_mem_init(struct drm_device *);
+ extern int nouveau_mem_init_agp(struct drm_device *);
+ extern void nouveau_mem_close(struct drm_device *);
+@@ -857,11 +827,13 @@ void nouveau_register_dsm_handler(void);
+ void nouveau_unregister_dsm_handler(void);
+ int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len);
+ bool nouveau_acpi_rom_supported(struct pci_dev *pdev);
++int nouveau_acpi_edid(struct drm_device *, struct drm_connector *);
+ #else
+ static inline void nouveau_register_dsm_handler(void) {}
+ static inline void nouveau_unregister_dsm_handler(void) {}
+ static inline bool nouveau_acpi_rom_supported(struct pci_dev *pdev) { return false; }
+ static inline int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len) { return -EINVAL; }
++static inline int nouveau_acpi_edid(struct drm_device *, struct drm_connector *) { return -EINVAL; }
+ #endif
+
+ /* nouveau_backlight.c */
+@@ -1035,12 +1007,6 @@ extern int nv50_graph_unload_context(struct drm_device *);
+ extern void nv50_graph_context_switch(struct drm_device *);
+ extern int nv50_grctx_init(struct nouveau_grctx *);
+
+-/* nouveau_grctx.c */
+-extern int nouveau_grctx_prog_load(struct drm_device *);
+-extern void nouveau_grctx_vals_load(struct drm_device *,
+- struct nouveau_gpuobj *);
+-extern void nouveau_grctx_fini(struct drm_device *);
+-
+ /* nv04_instmem.c */
+ extern int nv04_instmem_init(struct drm_device *);
+ extern void nv04_instmem_takedown(struct drm_device *);
+@@ -1051,8 +1017,7 @@ extern int nv04_instmem_populate(struct drm_device *, struct nouveau_gpuobj *,
+ extern void nv04_instmem_clear(struct drm_device *, struct nouveau_gpuobj *);
+ extern int nv04_instmem_bind(struct drm_device *, struct nouveau_gpuobj *);
+ extern int nv04_instmem_unbind(struct drm_device *, struct nouveau_gpuobj *);
+-extern void nv04_instmem_prepare_access(struct drm_device *, bool write);
+-extern void nv04_instmem_finish_access(struct drm_device *);
++extern void nv04_instmem_flush(struct drm_device *);
+
+ /* nv50_instmem.c */
+ extern int nv50_instmem_init(struct drm_device *);
+@@ -1064,8 +1029,9 @@ extern int nv50_instmem_populate(struct drm_device *, struct nouveau_gpuobj *,
+ extern void nv50_instmem_clear(struct drm_device *, struct nouveau_gpuobj *);
+ extern int nv50_instmem_bind(struct drm_device *, struct nouveau_gpuobj *);
+ extern int nv50_instmem_unbind(struct drm_device *, struct nouveau_gpuobj *);
+-extern void nv50_instmem_prepare_access(struct drm_device *, bool write);
+-extern void nv50_instmem_finish_access(struct drm_device *);
++extern void nv50_instmem_flush(struct drm_device *);
++extern void nv84_instmem_flush(struct drm_device *);
++extern void nv50_vm_flush(struct drm_device *, int engine);
+
+ /* nv04_mc.c */
+ extern int nv04_mc_init(struct drm_device *);
+@@ -1088,13 +1054,14 @@ extern long nouveau_compat_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg);
+
+ /* nv04_dac.c */
+-extern int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry);
++extern int nv04_dac_create(struct drm_connector *, struct dcb_entry *);
+ extern uint32_t nv17_dac_sample_load(struct drm_encoder *encoder);
+ extern int nv04_dac_output_offset(struct drm_encoder *encoder);
+ extern void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable);
++extern bool nv04_dac_in_use(struct drm_encoder *encoder);
+
+ /* nv04_dfp.c */
+-extern int nv04_dfp_create(struct drm_device *dev, struct dcb_entry *entry);
++extern int nv04_dfp_create(struct drm_connector *, struct dcb_entry *);
+ extern int nv04_dfp_get_bound_head(struct drm_device *dev, struct dcb_entry *dcbent);
+ extern void nv04_dfp_bind_head(struct drm_device *dev, struct dcb_entry *dcbent,
+ int head, bool dl);
+@@ -1103,10 +1070,10 @@ extern void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode);
+
+ /* nv04_tv.c */
+ extern int nv04_tv_identify(struct drm_device *dev, int i2c_index);
+-extern int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry);
++extern int nv04_tv_create(struct drm_connector *, struct dcb_entry *);
+
+ /* nv17_tv.c */
+-extern int nv17_tv_create(struct drm_device *dev, struct dcb_entry *entry);
++extern int nv17_tv_create(struct drm_connector *, struct dcb_entry *);
+
+ /* nv04_display.c */
+ extern int nv04_display_create(struct drm_device *);
+@@ -1147,7 +1114,6 @@ extern int nouveau_fence_wait(void *obj, void *arg, bool lazy, bool intr);
+ extern int nouveau_fence_flush(void *obj, void *arg);
+ extern void nouveau_fence_unref(void **obj);
+ extern void *nouveau_fence_ref(void *obj);
+-extern void nouveau_fence_handler(struct drm_device *dev, int channel);
+
+ /* nouveau_gem.c */
+ extern int nouveau_gem_new(struct drm_device *, struct nouveau_channel *,
+diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h
+index e1df820..a1a0d48 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_encoder.h
++++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h
+@@ -38,13 +38,15 @@ struct nouveau_encoder {
+ struct dcb_entry *dcb;
+ int or;
+
++ /* different to drm_encoder.crtc, this reflects what's
++ * actually programmed on the hw, not the proposed crtc */
++ struct drm_crtc *crtc;
++
+ struct drm_display_mode mode;
+ int last_dpms;
+
+ struct nv04_output_reg restore;
+
+- void (*disconnect)(struct nouveau_encoder *encoder);
+-
+ union {
+ struct {
+ int mc_unknown;
+@@ -71,8 +73,8 @@ static inline struct drm_encoder *to_drm_encoder(struct nouveau_encoder *enc)
+
+ struct nouveau_connector *
+ nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
+-int nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry);
+-int nv50_dac_create(struct drm_device *dev, struct dcb_entry *entry);
++int nv50_sor_create(struct drm_connector *, struct dcb_entry *);
++int nv50_dac_create(struct drm_connector *, struct dcb_entry *);
+
+ struct bit_displayport_encoder_table {
+ uint32_t match;
+diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+index 257ea13..2fb2444 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
++++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+@@ -333,7 +333,7 @@ nouveau_fbcon_output_poll_changed(struct drm_device *dev)
+ drm_fb_helper_hotplug_event(&dev_priv->nfbdev->helper);
+ }
+
+-int
++static int
+ nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *nfbdev)
+ {
+ struct nouveau_framebuffer *nouveau_fb = &nfbdev->nouveau_fb;
+diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
+index faddf53..813d853 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_fence.c
++++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
+@@ -67,12 +67,13 @@ nouveau_fence_update(struct nouveau_channel *chan)
+ if (USE_REFCNT)
+ sequence = nvchan_rd32(chan, 0x48);
+ else
+- sequence = chan->fence.last_sequence_irq;
++ sequence = atomic_read(&chan->fence.last_sequence_irq);
+
+ if (chan->fence.sequence_ack == sequence)
+ return;
+ chan->fence.sequence_ack = sequence;
+
++ spin_lock(&chan->fence.lock);
+ list_for_each_safe(entry, tmp, &chan->fence.pending) {
+ fence = list_entry(entry, struct nouveau_fence, entry);
+
+@@ -84,6 +85,7 @@ nouveau_fence_update(struct nouveau_channel *chan)
+ if (sequence == chan->fence.sequence_ack)
+ break;
+ }
++ spin_unlock(&chan->fence.lock);
+ }
+
+ int
+@@ -119,7 +121,6 @@ nouveau_fence_emit(struct nouveau_fence *fence)
+ {
+ struct drm_nouveau_private *dev_priv = fence->channel->dev->dev_private;
+ struct nouveau_channel *chan = fence->channel;
+- unsigned long flags;
+ int ret;
+
+ ret = RING_SPACE(chan, 2);
+@@ -127,9 +128,7 @@ nouveau_fence_emit(struct nouveau_fence *fence)
+ return ret;
+
+ if (unlikely(chan->fence.sequence == chan->fence.sequence_ack - 1)) {
+- spin_lock_irqsave(&chan->fence.lock, flags);
+ nouveau_fence_update(chan);
+- spin_unlock_irqrestore(&chan->fence.lock, flags);
+
+ BUG_ON(chan->fence.sequence ==
+ chan->fence.sequence_ack - 1);
+@@ -138,9 +137,9 @@ nouveau_fence_emit(struct nouveau_fence *fence)
+ fence->sequence = ++chan->fence.sequence;
+
+ kref_get(&fence->refcount);
+- spin_lock_irqsave(&chan->fence.lock, flags);
++ spin_lock(&chan->fence.lock);
+ list_add_tail(&fence->entry, &chan->fence.pending);
+- spin_unlock_irqrestore(&chan->fence.lock, flags);
++ spin_unlock(&chan->fence.lock);
+
+ BEGIN_RING(chan, NvSubSw, USE_REFCNT ? 0x0050 : 0x0150, 1);
+ OUT_RING(chan, fence->sequence);
+@@ -173,14 +172,11 @@ nouveau_fence_signalled(void *sync_obj, void *sync_arg)
+ {
+ struct nouveau_fence *fence = nouveau_fence(sync_obj);
+ struct nouveau_channel *chan = fence->channel;
+- unsigned long flags;
+
+ if (fence->signalled)
+ return true;
+
+- spin_lock_irqsave(&chan->fence.lock, flags);
+ nouveau_fence_update(chan);
+- spin_unlock_irqrestore(&chan->fence.lock, flags);
+ return fence->signalled;
+ }
+
+@@ -221,27 +217,12 @@ nouveau_fence_flush(void *sync_obj, void *sync_arg)
+ return 0;
+ }
+
+-void
+-nouveau_fence_handler(struct drm_device *dev, int channel)
+-{
+- struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nouveau_channel *chan = NULL;
+-
+- if (channel >= 0 && channel < dev_priv->engine.fifo.channels)
+- chan = dev_priv->fifos[channel];
+-
+- if (chan) {
+- spin_lock_irq(&chan->fence.lock);
+- nouveau_fence_update(chan);
+- spin_unlock_irq(&chan->fence.lock);
+- }
+-}
+-
+ int
+ nouveau_fence_init(struct nouveau_channel *chan)
+ {
+ INIT_LIST_HEAD(&chan->fence.pending);
+ spin_lock_init(&chan->fence.lock);
++ atomic_set(&chan->fence.last_sequence_irq, 0);
+ return 0;
+ }
+
+diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
+index 69c76cf..547f2c2 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
++++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
+@@ -137,8 +137,6 @@ nouveau_gem_ioctl_new(struct drm_device *dev, void *data,
+ uint32_t flags = 0;
+ int ret = 0;
+
+- NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+-
+ if (unlikely(dev_priv->ttm.bdev.dev_mapping == NULL))
+ dev_priv->ttm.bdev.dev_mapping = dev_priv->dev->dev_mapping;
+
+@@ -577,10 +575,9 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
+ struct drm_nouveau_gem_pushbuf_bo *bo;
+ struct nouveau_channel *chan;
+ struct validate_op op;
+- struct nouveau_fence *fence = 0;
++ struct nouveau_fence *fence = NULL;
+ int i, j, ret = 0, do_reloc = 0;
+
+- NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+ NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(req->channel, file_priv, chan);
+
+ req->vram_available = dev_priv->fb_aper_free;
+@@ -760,8 +757,6 @@ nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data,
+ bool no_wait = !!(req->flags & NOUVEAU_GEM_CPU_PREP_NOWAIT);
+ int ret = -EINVAL;
+
+- NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+-
+ gem = drm_gem_object_lookup(dev, file_priv, req->handle);
+ if (!gem)
+ return ret;
+@@ -800,8 +795,6 @@ nouveau_gem_ioctl_cpu_fini(struct drm_device *dev, void *data,
+ struct nouveau_bo *nvbo;
+ int ret = -EINVAL;
+
+- NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+-
+ gem = drm_gem_object_lookup(dev, file_priv, req->handle);
+ if (!gem)
+ return ret;
+@@ -827,8 +820,6 @@ nouveau_gem_ioctl_info(struct drm_device *dev, void *data,
+ struct drm_gem_object *gem;
+ int ret;
+
+- NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+-
+ gem = drm_gem_object_lookup(dev, file_priv, req->handle);
+ if (!gem)
+ return -EINVAL;
+diff --git a/drivers/gpu/drm/nouveau/nouveau_grctx.c b/drivers/gpu/drm/nouveau/nouveau_grctx.c
+deleted file mode 100644
+index f731c5f..0000000
+--- a/drivers/gpu/drm/nouveau/nouveau_grctx.c
++++ /dev/null
+@@ -1,160 +0,0 @@
+-/*
+- * Copyright 2009 Red Hat Inc.
+- *
+- * Permission is hereby granted, free of charge, to any person obtaining a
+- * copy of this software and associated documentation files (the "Software"),
+- * to deal in the Software without restriction, including without limitation
+- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+- * and/or sell copies of the Software, and to permit persons to whom the
+- * Software is furnished to do so, subject to the following conditions:
+- *
+- * The above copyright notice and this permission notice shall be included in
+- * all copies or substantial portions of the Software.
+- *
+- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+- *
+- * Authors: Ben Skeggs
+- */
+-
+-#include <linux/firmware.h>
+-#include <linux/slab.h>
+-
+-#include "drmP.h"
+-#include "nouveau_drv.h"
+-
+-struct nouveau_ctxprog {
+- uint32_t signature;
+- uint8_t version;
+- uint16_t length;
+- uint32_t data[];
+-} __attribute__ ((packed));
+-
+-struct nouveau_ctxvals {
+- uint32_t signature;
+- uint8_t version;
+- uint32_t length;
+- struct {
+- uint32_t offset;
+- uint32_t value;
+- } data[];
+-} __attribute__ ((packed));
+-
+-int
+-nouveau_grctx_prog_load(struct drm_device *dev)
+-{
+- struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+- const int chipset = dev_priv->chipset;
+- const struct firmware *fw;
+- const struct nouveau_ctxprog *cp;
+- const struct nouveau_ctxvals *cv;
+- char name[32];
+- int ret, i;
+-
+- if (pgraph->accel_blocked)
+- return -ENODEV;
+-
+- if (!pgraph->ctxprog) {
+- sprintf(name, "nouveau/nv%02x.ctxprog", chipset);
+- ret = request_firmware(&fw, name, &dev->pdev->dev);
+- if (ret) {
+- NV_ERROR(dev, "No ctxprog for NV%02x\n", chipset);
+- return ret;
+- }
+-
+- pgraph->ctxprog = kmemdup(fw->data, fw->size, GFP_KERNEL);
+- if (!pgraph->ctxprog) {
+- NV_ERROR(dev, "OOM copying ctxprog\n");
+- release_firmware(fw);
+- return -ENOMEM;
+- }
+-
+- cp = pgraph->ctxprog;
+- if (le32_to_cpu(cp->signature) != 0x5043564e ||
+- cp->version != 0 ||
+- le16_to_cpu(cp->length) != ((fw->size - 7) / 4)) {
+- NV_ERROR(dev, "ctxprog invalid\n");
+- release_firmware(fw);
+- nouveau_grctx_fini(dev);
+- return -EINVAL;
+- }
+- release_firmware(fw);
+- }
+-
+- if (!pgraph->ctxvals) {
+- sprintf(name, "nouveau/nv%02x.ctxvals", chipset);
+- ret = request_firmware(&fw, name, &dev->pdev->dev);
+- if (ret) {
+- NV_ERROR(dev, "No ctxvals for NV%02x\n", chipset);
+- nouveau_grctx_fini(dev);
+- return ret;
+- }
+-
+- pgraph->ctxvals = kmemdup(fw->data, fw->size, GFP_KERNEL);
+- if (!pgraph->ctxvals) {
+- NV_ERROR(dev, "OOM copying ctxvals\n");
+- release_firmware(fw);
+- nouveau_grctx_fini(dev);
+- return -ENOMEM;
+- }
+-
+- cv = (void *)pgraph->ctxvals;
+- if (le32_to_cpu(cv->signature) != 0x5643564e ||
+- cv->version != 0 ||
+- le32_to_cpu(cv->length) != ((fw->size - 9) / 8)) {
+- NV_ERROR(dev, "ctxvals invalid\n");
+- release_firmware(fw);
+- nouveau_grctx_fini(dev);
+- return -EINVAL;
+- }
+- release_firmware(fw);
+- }
+-
+- cp = pgraph->ctxprog;
+-
+- nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0);
+- for (i = 0; i < le16_to_cpu(cp->length); i++)
+- nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA,
+- le32_to_cpu(cp->data[i]));
+-
+- return 0;
+-}
+-
+-void
+-nouveau_grctx_fini(struct drm_device *dev)
+-{
+- struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+-
+- if (pgraph->ctxprog) {
+- kfree(pgraph->ctxprog);
+- pgraph->ctxprog = NULL;
+- }
+-
+- if (pgraph->ctxvals) {
+- kfree(pgraph->ctxprog);
+- pgraph->ctxvals = NULL;
+- }
+-}
+-
+-void
+-nouveau_grctx_vals_load(struct drm_device *dev, struct nouveau_gpuobj *ctx)
+-{
+- struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+- struct nouveau_ctxvals *cv = pgraph->ctxvals;
+- int i;
+-
+- if (!cv)
+- return;
+-
+- for (i = 0; i < le32_to_cpu(cv->length); i++)
+- nv_wo32(dev, ctx, le32_to_cpu(cv->data[i].offset),
+- le32_to_cpu(cv->data[i].value));
+-}
+diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c
+index 316a3c7..97ba89e 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_i2c.c
++++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c
+@@ -278,3 +278,37 @@ nouveau_i2c_find(struct drm_device *dev, int index)
+ return i2c->chan;
+ }
+
++bool
++nouveau_probe_i2c_addr(struct nouveau_i2c_chan *i2c, int addr)
++{
++ struct i2c_msg msg = {
++ .addr = addr,
++ .len = 0,
++ };
++
++ return i2c_transfer(&i2c->adapter, &msg, 1) == 1;
++}
++
++int
++nouveau_i2c_identify(struct drm_device *dev, const char *what,
++ struct i2c_board_info *info, int index)
++{
++ struct nouveau_i2c_chan *i2c = nouveau_i2c_find(dev, index);
++ int was_locked, i;
++
++ was_locked = NVLockVgaCrtcs(dev, false);
++ NV_DEBUG(dev, "Probing %ss on I2C bus: %d\n", what, index);
++
++ for (i = 0; info[i].addr; i++) {
++ if (nouveau_probe_i2c_addr(i2c, info[i].addr)) {
++ NV_INFO(dev, "Detected %s: %s\n", what, info[i].type);
++ goto out;
++ }
++ }
++
++ NV_DEBUG(dev, "No devices found.\n");
++out:
++ NVLockVgaCrtcs(dev, was_locked);
++
++ return info[i].addr ? i : -ENODEV;
++}
+diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.h b/drivers/gpu/drm/nouveau/nouveau_i2c.h
+index c8eaf7a..6dd2f87 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_i2c.h
++++ b/drivers/gpu/drm/nouveau/nouveau_i2c.h
+@@ -45,6 +45,9 @@ struct nouveau_i2c_chan {
+ int nouveau_i2c_init(struct drm_device *, struct dcb_i2c_entry *, int index);
+ void nouveau_i2c_fini(struct drm_device *, struct dcb_i2c_entry *);
+ struct nouveau_i2c_chan *nouveau_i2c_find(struct drm_device *, int index);
++bool nouveau_probe_i2c_addr(struct nouveau_i2c_chan *i2c, int addr);
++int nouveau_i2c_identify(struct drm_device *dev, const char *what,
++ struct i2c_board_info *info, int index);
+
+ int nouveau_dp_i2c_aux_ch(struct i2c_adapter *, int mode, uint8_t write_byte,
+ uint8_t *read_byte);
+diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
+index c1fd42b..adf5ac4 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_mem.c
++++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
+@@ -35,162 +35,6 @@
+ #include "drm_sarea.h"
+ #include "nouveau_drv.h"
+
+-static struct mem_block *
+-split_block(struct mem_block *p, uint64_t start, uint64_t size,
+- struct drm_file *file_priv)
+-{
+- /* Maybe cut off the start of an existing block */
+- if (start > p->start) {
+- struct mem_block *newblock =
+- kmalloc(sizeof(*newblock), GFP_KERNEL);
+- if (!newblock)
+- goto out;
+- newblock->start = start;
+- newblock->size = p->size - (start - p->start);
+- newblock->file_priv = NULL;
+- newblock->next = p->next;
+- newblock->prev = p;
+- p->next->prev = newblock;
+- p->next = newblock;
+- p->size -= newblock->size;
+- p = newblock;
+- }
+-
+- /* Maybe cut off the end of an existing block */
+- if (size < p->size) {
+- struct mem_block *newblock =
+- kmalloc(sizeof(*newblock), GFP_KERNEL);
+- if (!newblock)
+- goto out;
+- newblock->start = start + size;
+- newblock->size = p->size - size;
+- newblock->file_priv = NULL;
+- newblock->next = p->next;
+- newblock->prev = p;
+- p->next->prev = newblock;
+- p->next = newblock;
+- p->size = size;
+- }
+-
+-out:
+- /* Our block is in the middle */
+- p->file_priv = file_priv;
+- return p;
+-}
+-
+-struct mem_block *
+-nouveau_mem_alloc_block(struct mem_block *heap, uint64_t size,
+- int align2, struct drm_file *file_priv, int tail)
+-{
+- struct mem_block *p;
+- uint64_t mask = (1 << align2) - 1;
+-
+- if (!heap)
+- return NULL;
+-
+- if (tail) {
+- list_for_each_prev(p, heap) {
+- uint64_t start = ((p->start + p->size) - size) & ~mask;
+-
+- if (p->file_priv == NULL && start >= p->start &&
+- start + size <= p->start + p->size)
+- return split_block(p, start, size, file_priv);
+- }
+- } else {
+- list_for_each(p, heap) {
+- uint64_t start = (p->start + mask) & ~mask;
+-
+- if (p->file_priv == NULL &&
+- start + size <= p->start + p->size)
+- return split_block(p, start, size, file_priv);
+- }
+- }
+-
+- return NULL;
+-}
+-
+-void nouveau_mem_free_block(struct mem_block *p)
+-{
+- p->file_priv = NULL;
+-
+- /* Assumes a single contiguous range. Needs a special file_priv in
+- * 'heap' to stop it being subsumed.
+- */
+- if (p->next->file_priv == NULL) {
+- struct mem_block *q = p->next;
+- p->size += q->size;
+- p->next = q->next;
+- p->next->prev = p;
+- kfree(q);
+- }
+-
+- if (p->prev->file_priv == NULL) {
+- struct mem_block *q = p->prev;
+- q->size += p->size;
+- q->next = p->next;
+- q->next->prev = q;
+- kfree(p);
+- }
+-}
+-
+-/* Initialize. How to check for an uninitialized heap?
+- */
+-int nouveau_mem_init_heap(struct mem_block **heap, uint64_t start,
+- uint64_t size)
+-{
+- struct mem_block *blocks = kmalloc(sizeof(*blocks), GFP_KERNEL);
+-
+- if (!blocks)
+- return -ENOMEM;
+-
+- *heap = kmalloc(sizeof(**heap), GFP_KERNEL);
+- if (!*heap) {
+- kfree(blocks);
+- return -ENOMEM;
+- }
+-
+- blocks->start = start;
+- blocks->size = size;
+- blocks->file_priv = NULL;
+- blocks->next = blocks->prev = *heap;
+-
+- memset(*heap, 0, sizeof(**heap));
+- (*heap)->file_priv = (struct drm_file *) -1;
+- (*heap)->next = (*heap)->prev = blocks;
+- return 0;
+-}
+-
+-/*
+- * Free all blocks associated with the releasing file_priv
+- */
+-void nouveau_mem_release(struct drm_file *file_priv, struct mem_block *heap)
+-{
+- struct mem_block *p;
+-
+- if (!heap || !heap->next)
+- return;
+-
+- list_for_each(p, heap) {
+- if (p->file_priv == file_priv)
+- p->file_priv = NULL;
+- }
+-
+- /* Assumes a single contiguous range. Needs a special file_priv in
+- * 'heap' to stop it being subsumed.
+- */
+- list_for_each(p, heap) {
+- while ((p->file_priv == NULL) &&
+- (p->next->file_priv == NULL) &&
+- (p->next != heap)) {
+- struct mem_block *q = p->next;
+- p->size += q->size;
+- p->next = q->next;
+- p->next->prev = p;
+- kfree(q);
+- }
+- }
+-}
+-
+ /*
+ * NV10-NV40 tiling helpers
+ */
+@@ -299,7 +143,6 @@ nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size,
+ phys |= 0x30;
+ }
+
+- dev_priv->engine.instmem.prepare_access(dev, true);
+ while (size) {
+ unsigned offset_h = upper_32_bits(phys);
+ unsigned offset_l = lower_32_bits(phys);
+@@ -331,36 +174,12 @@ nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size,
+ }
+ }
+ }
+- dev_priv->engine.instmem.finish_access(dev);
+-
+- nv_wr32(dev, 0x100c80, 0x00050001);
+- if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+- NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+- NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
+- return -EBUSY;
+- }
+-
+- nv_wr32(dev, 0x100c80, 0x00000001);
+- if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+- NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+- NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
+- return -EBUSY;
+- }
+-
+- nv_wr32(dev, 0x100c80, 0x00040001);
+- if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+- NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+- NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
+- return -EBUSY;
+- }
+-
+- nv_wr32(dev, 0x100c80, 0x00060001);
+- if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+- NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+- NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
+- return -EBUSY;
+- }
++ dev_priv->engine.instmem.flush(dev);
+
++ nv50_vm_flush(dev, 5);
++ nv50_vm_flush(dev, 0);
++ nv50_vm_flush(dev, 4);
++ nv50_vm_flush(dev, 6);
+ return 0;
+ }
+
+@@ -374,7 +193,6 @@ nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size)
+ virt -= dev_priv->vm_vram_base;
+ pages = (size >> 16) << 1;
+
+- dev_priv->engine.instmem.prepare_access(dev, true);
+ while (pages) {
+ pgt = dev_priv->vm_vram_pt[virt >> 29];
+ pte = (virt & 0x1ffe0000ULL) >> 15;
+@@ -388,57 +206,19 @@ nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size)
+ while (pte < end)
+ nv_wo32(dev, pgt, pte++, 0);
+ }
+- dev_priv->engine.instmem.finish_access(dev);
+-
+- nv_wr32(dev, 0x100c80, 0x00050001);
+- if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+- NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+- NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
+- return;
+- }
+-
+- nv_wr32(dev, 0x100c80, 0x00000001);
+- if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+- NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+- NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
+- return;
+- }
+-
+- nv_wr32(dev, 0x100c80, 0x00040001);
+- if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+- NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+- NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
+- return;
+- }
++ dev_priv->engine.instmem.flush(dev);
+
+- nv_wr32(dev, 0x100c80, 0x00060001);
+- if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+- NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+- NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
+- }
++ nv50_vm_flush(dev, 5);
++ nv50_vm_flush(dev, 0);
++ nv50_vm_flush(dev, 4);
++ nv50_vm_flush(dev, 6);
+ }
+
+ /*
+ * Cleanup everything
+ */
+-void nouveau_mem_takedown(struct mem_block **heap)
+-{
+- struct mem_block *p;
+-
+- if (!*heap)
+- return;
+-
+- for (p = (*heap)->next; p != *heap;) {
+- struct mem_block *q = p;
+- p = p->next;
+- kfree(q);
+- }
+-
+- kfree(*heap);
+- *heap = NULL;
+-}
+-
+-void nouveau_mem_close(struct drm_device *dev)
++void
++nouveau_mem_close(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+@@ -449,8 +229,7 @@ void nouveau_mem_close(struct drm_device *dev)
+
+ nouveau_ttm_global_release(dev_priv);
+
+- if (drm_core_has_AGP(dev) && dev->agp &&
+- drm_core_check_feature(dev, DRIVER_MODESET)) {
++ if (drm_core_has_AGP(dev) && dev->agp) {
+ struct drm_agp_mem *entry, *tempe;
+
+ /* Remove AGP resources, but leave dev->agp
+@@ -470,29 +249,29 @@ void nouveau_mem_close(struct drm_device *dev)
+ dev->agp->enabled = 0;
+ }
+
+- if (dev_priv->fb_mtrr) {
++ if (dev_priv->fb_mtrr >= 0) {
+ drm_mtrr_del(dev_priv->fb_mtrr, drm_get_resource_start(dev, 1),
+ drm_get_resource_len(dev, 1), DRM_MTRR_WC);
+- dev_priv->fb_mtrr = 0;
++ dev_priv->fb_mtrr = -1;
+ }
+ }
+
+ static uint32_t
+ nouveau_mem_detect_nv04(struct drm_device *dev)
+ {
+- uint32_t boot0 = nv_rd32(dev, NV03_BOOT_0);
++ uint32_t boot0 = nv_rd32(dev, NV04_PFB_BOOT_0);
+
+ if (boot0 & 0x00000100)
+ return (((boot0 >> 12) & 0xf) * 2 + 2) * 1024 * 1024;
+
+- switch (boot0 & NV03_BOOT_0_RAM_AMOUNT) {
+- case NV04_BOOT_0_RAM_AMOUNT_32MB:
++ switch (boot0 & NV04_PFB_BOOT_0_RAM_AMOUNT) {
++ case NV04_PFB_BOOT_0_RAM_AMOUNT_32MB:
+ return 32 * 1024 * 1024;
+- case NV04_BOOT_0_RAM_AMOUNT_16MB:
++ case NV04_PFB_BOOT_0_RAM_AMOUNT_16MB:
+ return 16 * 1024 * 1024;
+- case NV04_BOOT_0_RAM_AMOUNT_8MB:
++ case NV04_PFB_BOOT_0_RAM_AMOUNT_8MB:
+ return 8 * 1024 * 1024;
+- case NV04_BOOT_0_RAM_AMOUNT_4MB:
++ case NV04_PFB_BOOT_0_RAM_AMOUNT_4MB:
+ return 4 * 1024 * 1024;
+ }
+
+@@ -536,12 +315,18 @@ nouveau_mem_detect(struct drm_device *dev)
+ } else
+ if (dev_priv->flags & (NV_NFORCE | NV_NFORCE2)) {
+ dev_priv->vram_size = nouveau_mem_detect_nforce(dev);
++ } else
++ if (dev_priv->card_type < NV_50) {
++ dev_priv->vram_size = nv_rd32(dev, NV04_PFB_FIFO_DATA);
++ dev_priv->vram_size &= NV10_PFB_FIFO_DATA_RAM_AMOUNT_MB_MASK;
+ } else {
+- dev_priv->vram_size = nv_rd32(dev, NV04_FIFO_DATA);
+- dev_priv->vram_size &= NV10_FIFO_DATA_RAM_AMOUNT_MB_MASK;
+- if (dev_priv->chipset == 0xaa || dev_priv->chipset == 0xac)
++ dev_priv->vram_size = nv_rd32(dev, NV04_PFB_FIFO_DATA);
++ dev_priv->vram_size |= (dev_priv->vram_size & 0xff) << 32;
++ dev_priv->vram_size &= 0xffffffff00ll;
++ if (dev_priv->chipset == 0xaa || dev_priv->chipset == 0xac) {
+ dev_priv->vram_sys_base = nv_rd32(dev, 0x100e10);
+ dev_priv->vram_sys_base <<= 12;
++ }
+ }
+
+ NV_INFO(dev, "Detected %dMiB VRAM\n", (int)(dev_priv->vram_size >> 20));
+diff --git a/drivers/gpu/drm/nouveau/nouveau_notifier.c b/drivers/gpu/drm/nouveau/nouveau_notifier.c
+index 9537f3e..3ec181f 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_notifier.c
++++ b/drivers/gpu/drm/nouveau/nouveau_notifier.c
+@@ -55,7 +55,7 @@ nouveau_notifier_init_channel(struct nouveau_channel *chan)
+ if (ret)
+ goto out_err;
+
+- ret = nouveau_mem_init_heap(&chan->notifier_heap, 0, ntfy->bo.mem.size);
++ ret = drm_mm_init(&chan->notifier_heap, 0, ntfy->bo.mem.size);
+ if (ret)
+ goto out_err;
+
+@@ -80,7 +80,7 @@ nouveau_notifier_takedown_channel(struct nouveau_channel *chan)
+ nouveau_bo_unpin(chan->notifier_bo);
+ mutex_unlock(&dev->struct_mutex);
+ drm_gem_object_unreference_unlocked(chan->notifier_bo->gem);
+- nouveau_mem_takedown(&chan->notifier_heap);
++ drm_mm_takedown(&chan->notifier_heap);
+ }
+
+ static void
+@@ -90,7 +90,7 @@ nouveau_notifier_gpuobj_dtor(struct drm_device *dev,
+ NV_DEBUG(dev, "\n");
+
+ if (gpuobj->priv)
+- nouveau_mem_free_block(gpuobj->priv);
++ drm_mm_put_block(gpuobj->priv);
+ }
+
+ int
+@@ -100,18 +100,13 @@ nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle,
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_gpuobj *nobj = NULL;
+- struct mem_block *mem;
++ struct drm_mm_node *mem;
+ uint32_t offset;
+ int target, ret;
+
+- if (!chan->notifier_heap) {
+- NV_ERROR(dev, "Channel %d doesn't have a notifier heap!\n",
+- chan->id);
+- return -EINVAL;
+- }
+-
+- mem = nouveau_mem_alloc_block(chan->notifier_heap, size, 0,
+- (struct drm_file *)-2, 0);
++ mem = drm_mm_search_free(&chan->notifier_heap, size, 0, 0);
++ if (mem)
++ mem = drm_mm_get_block(mem, size, 0);
+ if (!mem) {
+ NV_ERROR(dev, "Channel %d notifier block full\n", chan->id);
+ return -ENOMEM;
+@@ -144,17 +139,17 @@ nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle,
+ mem->size, NV_DMA_ACCESS_RW, target,
+ &nobj);
+ if (ret) {
+- nouveau_mem_free_block(mem);
++ drm_mm_put_block(mem);
+ NV_ERROR(dev, "Error creating notifier ctxdma: %d\n", ret);
+ return ret;
+ }
+- nobj->dtor = nouveau_notifier_gpuobj_dtor;
+- nobj->priv = mem;
++ nobj->dtor = nouveau_notifier_gpuobj_dtor;
++ nobj->priv = mem;
+
+ ret = nouveau_gpuobj_ref_add(dev, chan, handle, nobj, NULL);
+ if (ret) {
+ nouveau_gpuobj_del(dev, &nobj);
+- nouveau_mem_free_block(mem);
++ drm_mm_put_block(mem);
+ NV_ERROR(dev, "Error referencing notifier ctxdma: %d\n", ret);
+ return ret;
+ }
+@@ -170,7 +165,7 @@ nouveau_notifier_offset(struct nouveau_gpuobj *nobj, uint32_t *poffset)
+ return -EINVAL;
+
+ if (poffset) {
+- struct mem_block *mem = nobj->priv;
++ struct drm_mm_node *mem = nobj->priv;
+
+ if (*poffset >= mem->size)
+ return false;
+@@ -189,7 +184,6 @@ nouveau_ioctl_notifier_alloc(struct drm_device *dev, void *data,
+ struct nouveau_channel *chan;
+ int ret;
+
+- NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+ NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(na->channel, file_priv, chan);
+
+ ret = nouveau_notifier_alloc(chan, na->handle, na->size, &na->offset);
+diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c
+index e7c100b..4bf6b33 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_object.c
++++ b/drivers/gpu/drm/nouveau/nouveau_object.c
+@@ -132,7 +132,6 @@ nouveau_ramht_insert(struct drm_device *dev, struct nouveau_gpuobj_ref *ref)
+ }
+ }
+
+- instmem->prepare_access(dev, true);
+ co = ho = nouveau_ramht_hash_handle(dev, chan->id, ref->handle);
+ do {
+ if (!nouveau_ramht_entry_valid(dev, ramht, co)) {
+@@ -143,7 +142,7 @@ nouveau_ramht_insert(struct drm_device *dev, struct nouveau_gpuobj_ref *ref)
+ nv_wo32(dev, ramht, (co + 4)/4, ctx);
+
+ list_add_tail(&ref->list, &chan->ramht_refs);
+- instmem->finish_access(dev);
++ instmem->flush(dev);
+ return 0;
+ }
+ NV_DEBUG(dev, "collision ch%d 0x%08x: h=0x%08x\n",
+@@ -153,7 +152,6 @@ nouveau_ramht_insert(struct drm_device *dev, struct nouveau_gpuobj_ref *ref)
+ if (co >= dev_priv->ramht_size)
+ co = 0;
+ } while (co != ho);
+- instmem->finish_access(dev);
+
+ NV_ERROR(dev, "RAMHT space exhausted. ch=%d\n", chan->id);
+ return -ENOMEM;
+@@ -173,7 +171,6 @@ nouveau_ramht_remove(struct drm_device *dev, struct nouveau_gpuobj_ref *ref)
+ return;
+ }
+
+- instmem->prepare_access(dev, true);
+ co = ho = nouveau_ramht_hash_handle(dev, chan->id, ref->handle);
+ do {
+ if (nouveau_ramht_entry_valid(dev, ramht, co) &&
+@@ -186,7 +183,7 @@ nouveau_ramht_remove(struct drm_device *dev, struct nouveau_gpuobj_ref *ref)
+ nv_wo32(dev, ramht, (co + 4)/4, 0x00000000);
+
+ list_del(&ref->list);
+- instmem->finish_access(dev);
++ instmem->flush(dev);
+ return;
+ }
+
+@@ -195,7 +192,6 @@ nouveau_ramht_remove(struct drm_device *dev, struct nouveau_gpuobj_ref *ref)
+ co = 0;
+ } while (co != ho);
+ list_del(&ref->list);
+- instmem->finish_access(dev);
+
+ NV_ERROR(dev, "RAMHT entry not found. ch=%d, handle=0x%08x\n",
+ chan->id, ref->handle);
+@@ -209,7 +205,7 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan,
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_engine *engine = &dev_priv->engine;
+ struct nouveau_gpuobj *gpuobj;
+- struct mem_block *pramin = NULL;
++ struct drm_mm *pramin = NULL;
+ int ret;
+
+ NV_DEBUG(dev, "ch%d size=%u align=%d flags=0x%08x\n",
+@@ -233,25 +229,12 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan,
+ * available.
+ */
+ if (chan) {
+- if (chan->ramin_heap) {
+- NV_DEBUG(dev, "private heap\n");
+- pramin = chan->ramin_heap;
+- } else
+- if (dev_priv->card_type < NV_50) {
+- NV_DEBUG(dev, "global heap fallback\n");
+- pramin = dev_priv->ramin_heap;
+- }
++ NV_DEBUG(dev, "channel heap\n");
++ pramin = &chan->ramin_heap;
+ } else {
+ NV_DEBUG(dev, "global heap\n");
+- pramin = dev_priv->ramin_heap;
+- }
+-
+- if (!pramin) {
+- NV_ERROR(dev, "No PRAMIN heap!\n");
+- return -EINVAL;
+- }
++ pramin = &dev_priv->ramin_heap;
+
+- if (!chan) {
+ ret = engine->instmem.populate(dev, gpuobj, &size);
+ if (ret) {
+ nouveau_gpuobj_del(dev, &gpuobj);
+@@ -260,9 +243,10 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan,
+ }
+
+ /* Allocate a chunk of the PRAMIN aperture */
+- gpuobj->im_pramin = nouveau_mem_alloc_block(pramin, size,
+- drm_order(align),
+- (struct drm_file *)-2, 0);
++ gpuobj->im_pramin = drm_mm_search_free(pramin, size, align, 0);
++ if (gpuobj->im_pramin)
++ gpuobj->im_pramin = drm_mm_get_block(gpuobj->im_pramin, size, align);
++
+ if (!gpuobj->im_pramin) {
+ nouveau_gpuobj_del(dev, &gpuobj);
+ return -ENOMEM;
+@@ -279,10 +263,9 @@ nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan,
+ if (gpuobj->flags & NVOBJ_FLAG_ZERO_ALLOC) {
+ int i;
+
+- engine->instmem.prepare_access(dev, true);
+ for (i = 0; i < gpuobj->im_pramin->size; i += 4)
+ nv_wo32(dev, gpuobj, i/4, 0);
+- engine->instmem.finish_access(dev);
++ engine->instmem.flush(dev);
+ }
+
+ *gpuobj_ret = gpuobj;
+@@ -370,10 +353,9 @@ nouveau_gpuobj_del(struct drm_device *dev, struct nouveau_gpuobj **pgpuobj)
+ }
+
+ if (gpuobj->im_pramin && (gpuobj->flags & NVOBJ_FLAG_ZERO_FREE)) {
+- engine->instmem.prepare_access(dev, true);
+ for (i = 0; i < gpuobj->im_pramin->size; i += 4)
+ nv_wo32(dev, gpuobj, i/4, 0);
+- engine->instmem.finish_access(dev);
++ engine->instmem.flush(dev);
+ }
+
+ if (gpuobj->dtor)
+@@ -386,7 +368,7 @@ nouveau_gpuobj_del(struct drm_device *dev, struct nouveau_gpuobj **pgpuobj)
+ if (gpuobj->flags & NVOBJ_FLAG_FAKE)
+ kfree(gpuobj->im_pramin);
+ else
+- nouveau_mem_free_block(gpuobj->im_pramin);
++ drm_mm_put_block(gpuobj->im_pramin);
+ }
+
+ list_del(&gpuobj->list);
+@@ -589,7 +571,7 @@ nouveau_gpuobj_new_fake(struct drm_device *dev, uint32_t p_offset,
+ list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list);
+
+ if (p_offset != ~0) {
+- gpuobj->im_pramin = kzalloc(sizeof(struct mem_block),
++ gpuobj->im_pramin = kzalloc(sizeof(struct drm_mm_node),
+ GFP_KERNEL);
+ if (!gpuobj->im_pramin) {
+ nouveau_gpuobj_del(dev, &gpuobj);
+@@ -605,10 +587,9 @@ nouveau_gpuobj_new_fake(struct drm_device *dev, uint32_t p_offset,
+ }
+
+ if (gpuobj->flags & NVOBJ_FLAG_ZERO_ALLOC) {
+- dev_priv->engine.instmem.prepare_access(dev, true);
+ for (i = 0; i < gpuobj->im_pramin->size; i += 4)
+ nv_wo32(dev, gpuobj, i/4, 0);
+- dev_priv->engine.instmem.finish_access(dev);
++ dev_priv->engine.instmem.flush(dev);
+ }
+
+ if (pref) {
+@@ -696,8 +677,6 @@ nouveau_gpuobj_dma_new(struct nouveau_channel *chan, int class,
+ return ret;
+ }
+
+- instmem->prepare_access(dev, true);
+-
+ if (dev_priv->card_type < NV_50) {
+ uint32_t frame, adjust, pte_flags = 0;
+
+@@ -734,7 +713,7 @@ nouveau_gpuobj_dma_new(struct nouveau_channel *chan, int class,
+ nv_wo32(dev, *gpuobj, 5, flags5);
+ }
+
+- instmem->finish_access(dev);
++ instmem->flush(dev);
+
+ (*gpuobj)->engine = NVOBJ_ENGINE_SW;
+ (*gpuobj)->class = class;
+@@ -849,7 +828,6 @@ nouveau_gpuobj_gr_new(struct nouveau_channel *chan, int class,
+ return ret;
+ }
+
+- dev_priv->engine.instmem.prepare_access(dev, true);
+ if (dev_priv->card_type >= NV_50) {
+ nv_wo32(dev, *gpuobj, 0, class);
+ nv_wo32(dev, *gpuobj, 5, 0x00010000);
+@@ -874,7 +852,7 @@ nouveau_gpuobj_gr_new(struct nouveau_channel *chan, int class,
+ }
+ }
+ }
+- dev_priv->engine.instmem.finish_access(dev);
++ dev_priv->engine.instmem.flush(dev);
+
+ (*gpuobj)->engine = NVOBJ_ENGINE_GR;
+ (*gpuobj)->class = class;
+@@ -920,6 +898,7 @@ nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan)
+ base = 0;
+
+ /* PGRAPH context */
++ size += dev_priv->engine.graph.grctx_size;
+
+ if (dev_priv->card_type == NV_50) {
+ /* Various fixed table thingos */
+@@ -930,12 +909,8 @@ nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan)
+ size += 0x8000;
+ /* RAMFC */
+ size += 0x1000;
+- /* PGRAPH context */
+- size += 0x70000;
+ }
+
+- NV_DEBUG(dev, "ch%d PRAMIN size: 0x%08x bytes, base alloc=0x%08x\n",
+- chan->id, size, base);
+ ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, size, 0x1000, 0,
+ &chan->ramin);
+ if (ret) {
+@@ -944,8 +919,7 @@ nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan)
+ }
+ pramin = chan->ramin->gpuobj;
+
+- ret = nouveau_mem_init_heap(&chan->ramin_heap,
+- pramin->im_pramin->start + base, size);
++ ret = drm_mm_init(&chan->ramin_heap, pramin->im_pramin->start + base, size);
+ if (ret) {
+ NV_ERROR(dev, "Error creating PRAMIN heap: %d\n", ret);
+ nouveau_gpuobj_ref_del(dev, &chan->ramin);
+@@ -969,15 +943,11 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
+
+ NV_DEBUG(dev, "ch%d vram=0x%08x tt=0x%08x\n", chan->id, vram_h, tt_h);
+
+- /* Reserve a block of PRAMIN for the channel
+- *XXX: maybe on <NV50 too at some point
+- */
+- if (0 || dev_priv->card_type == NV_50) {
+- ret = nouveau_gpuobj_channel_init_pramin(chan);
+- if (ret) {
+- NV_ERROR(dev, "init pramin\n");
+- return ret;
+- }
++ /* Allocate a chunk of memory for per-channel object storage */
++ ret = nouveau_gpuobj_channel_init_pramin(chan);
++ if (ret) {
++ NV_ERROR(dev, "init pramin\n");
++ return ret;
+ }
+
+ /* NV50 VM
+@@ -988,17 +958,13 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
+ if (dev_priv->card_type >= NV_50) {
+ uint32_t vm_offset, pde;
+
+- instmem->prepare_access(dev, true);
+-
+ vm_offset = (dev_priv->chipset & 0xf0) == 0x50 ? 0x1400 : 0x200;
+ vm_offset += chan->ramin->gpuobj->im_pramin->start;
+
+ ret = nouveau_gpuobj_new_fake(dev, vm_offset, ~0, 0x4000,
+ 0, &chan->vm_pd, NULL);
+- if (ret) {
+- instmem->finish_access(dev);
++ if (ret)
+ return ret;
+- }
+ for (i = 0; i < 0x4000; i += 8) {
+ nv_wo32(dev, chan->vm_pd, (i+0)/4, 0x00000000);
+ nv_wo32(dev, chan->vm_pd, (i+4)/4, 0xdeadcafe);
+@@ -1008,10 +974,8 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
+ ret = nouveau_gpuobj_ref_add(dev, NULL, 0,
+ dev_priv->gart_info.sg_ctxdma,
+ &chan->vm_gart_pt);
+- if (ret) {
+- instmem->finish_access(dev);
++ if (ret)
+ return ret;
+- }
+ nv_wo32(dev, chan->vm_pd, pde++,
+ chan->vm_gart_pt->instance | 0x03);
+ nv_wo32(dev, chan->vm_pd, pde++, 0x00000000);
+@@ -1021,17 +985,15 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
+ ret = nouveau_gpuobj_ref_add(dev, NULL, 0,
+ dev_priv->vm_vram_pt[i],
+ &chan->vm_vram_pt[i]);
+- if (ret) {
+- instmem->finish_access(dev);
++ if (ret)
+ return ret;
+- }
+
+ nv_wo32(dev, chan->vm_pd, pde++,
+ chan->vm_vram_pt[i]->instance | 0x61);
+ nv_wo32(dev, chan->vm_pd, pde++, 0x00000000);
+ }
+
+- instmem->finish_access(dev);
++ instmem->flush(dev);
+ }
+
+ /* RAMHT */
+@@ -1130,8 +1092,8 @@ nouveau_gpuobj_channel_takedown(struct nouveau_channel *chan)
+ for (i = 0; i < dev_priv->vm_vram_pt_nr; i++)
+ nouveau_gpuobj_ref_del(dev, &chan->vm_vram_pt[i]);
+
+- if (chan->ramin_heap)
+- nouveau_mem_takedown(&chan->ramin_heap);
++ if (chan->ramin_heap.fl_entry.next)
++ drm_mm_takedown(&chan->ramin_heap);
+ if (chan->ramin)
+ nouveau_gpuobj_ref_del(dev, &chan->ramin);
+
+@@ -1164,10 +1126,8 @@ nouveau_gpuobj_suspend(struct drm_device *dev)
+ return -ENOMEM;
+ }
+
+- dev_priv->engine.instmem.prepare_access(dev, false);
+ for (i = 0; i < gpuobj->im_pramin->size / 4; i++)
+ gpuobj->im_backing_suspend[i] = nv_ro32(dev, gpuobj, i);
+- dev_priv->engine.instmem.finish_access(dev);
+ }
+
+ return 0;
+@@ -1212,10 +1172,9 @@ nouveau_gpuobj_resume(struct drm_device *dev)
+ if (!gpuobj->im_backing_suspend)
+ continue;
+
+- dev_priv->engine.instmem.prepare_access(dev, true);
+ for (i = 0; i < gpuobj->im_pramin->size / 4; i++)
+ nv_wo32(dev, gpuobj, i, gpuobj->im_backing_suspend[i]);
+- dev_priv->engine.instmem.finish_access(dev);
++ dev_priv->engine.instmem.flush(dev);
+ }
+
+ nouveau_gpuobj_suspend_cleanup(dev);
+@@ -1232,7 +1191,6 @@ int nouveau_ioctl_grobj_alloc(struct drm_device *dev, void *data,
+ struct nouveau_channel *chan;
+ int ret;
+
+- NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+ NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(init->channel, file_priv, chan);
+
+ if (init->handle == ~0)
+@@ -1283,7 +1241,6 @@ int nouveau_ioctl_gpuobj_free(struct drm_device *dev, void *data,
+ struct nouveau_channel *chan;
+ int ret;
+
+- NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+ NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(objfree->channel, file_priv, chan);
+
+ ret = nouveau_gpuobj_ref_find(chan, objfree->handle, &ref);
+diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h
+index 6ca80a3..9c1056c 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_reg.h
++++ b/drivers/gpu/drm/nouveau/nouveau_reg.h
+@@ -1,19 +1,64 @@
+
++#define NV04_PFB_BOOT_0 0x00100000
++# define NV04_PFB_BOOT_0_RAM_AMOUNT 0x00000003
++# define NV04_PFB_BOOT_0_RAM_AMOUNT_32MB 0x00000000
++# define NV04_PFB_BOOT_0_RAM_AMOUNT_4MB 0x00000001
++# define NV04_PFB_BOOT_0_RAM_AMOUNT_8MB 0x00000002
++# define NV04_PFB_BOOT_0_RAM_AMOUNT_16MB 0x00000003
++# define NV04_PFB_BOOT_0_RAM_WIDTH_128 0x00000004
++# define NV04_PFB_BOOT_0_RAM_TYPE 0x00000028
++# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_8MBIT 0x00000000
++# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT 0x00000008
++# define NV04_PFB_BOOT_0_RAM_TYPE_SGRAM_16MBIT_4BANK 0x00000010
++# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_16MBIT 0x00000018
++# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBIT 0x00000020
++# define NV04_PFB_BOOT_0_RAM_TYPE_SDRAM_64MBITX16 0x00000028
++# define NV04_PFB_BOOT_0_UMA_ENABLE 0x00000100
++# define NV04_PFB_BOOT_0_UMA_SIZE 0x0000f000
++#define NV04_PFB_DEBUG_0 0x00100080
++# define NV04_PFB_DEBUG_0_PAGE_MODE 0x00000001
++# define NV04_PFB_DEBUG_0_REFRESH_OFF 0x00000010
++# define NV04_PFB_DEBUG_0_REFRESH_COUNTX64 0x00003f00
++# define NV04_PFB_DEBUG_0_REFRESH_SLOW_CLK 0x00004000
++# define NV04_PFB_DEBUG_0_SAFE_MODE 0x00008000
++# define NV04_PFB_DEBUG_0_ALOM_ENABLE 0x00010000
++# define NV04_PFB_DEBUG_0_CASOE 0x00100000
++# define NV04_PFB_DEBUG_0_CKE_INVERT 0x10000000
++# define NV04_PFB_DEBUG_0_REFINC 0x20000000
++# define NV04_PFB_DEBUG_0_SAVE_POWER_OFF 0x40000000
++#define NV04_PFB_CFG0 0x00100200
++# define NV04_PFB_CFG0_SCRAMBLE 0x20000000
++#define NV04_PFB_CFG1 0x00100204
++#define NV04_PFB_FIFO_DATA 0x0010020c
++# define NV10_PFB_FIFO_DATA_RAM_AMOUNT_MB_MASK 0xfff00000
++# define NV10_PFB_FIFO_DATA_RAM_AMOUNT_MB_SHIFT 20
++#define NV10_PFB_REFCTRL 0x00100210
++# define NV10_PFB_REFCTRL_VALID_1 (1 << 31)
++#define NV04_PFB_PAD 0x0010021c
++# define NV04_PFB_PAD_CKE_NORMAL (1 << 0)
++#define NV10_PFB_TILE(i) (0x00100240 + (i*16))
++#define NV10_PFB_TILE__SIZE 8
++#define NV10_PFB_TLIMIT(i) (0x00100244 + (i*16))
++#define NV10_PFB_TSIZE(i) (0x00100248 + (i*16))
++#define NV10_PFB_TSTATUS(i) (0x0010024c + (i*16))
++#define NV04_PFB_REF 0x001002d0
++# define NV04_PFB_REF_CMD_REFRESH (1 << 0)
++#define NV04_PFB_PRE 0x001002d4
++# define NV04_PFB_PRE_CMD_PRECHARGE (1 << 0)
++#define NV10_PFB_CLOSE_PAGE2 0x0010033c
++#define NV04_PFB_SCRAMBLE(i) (0x00100400 + 4 * (i))
++#define NV40_PFB_TILE(i) (0x00100600 + (i*16))
++#define NV40_PFB_TILE__SIZE_0 12
++#define NV40_PFB_TILE__SIZE_1 15
++#define NV40_PFB_TLIMIT(i) (0x00100604 + (i*16))
++#define NV40_PFB_TSIZE(i) (0x00100608 + (i*16))
++#define NV40_PFB_TSTATUS(i) (0x0010060c + (i*16))
++#define NV40_PFB_UNK_800 0x00100800
+
+-#define NV03_BOOT_0 0x00100000
+-# define NV03_BOOT_0_RAM_AMOUNT 0x00000003
+-# define NV03_BOOT_0_RAM_AMOUNT_8MB 0x00000000
+-# define NV03_BOOT_0_RAM_AMOUNT_2MB 0x00000001
+-# define NV03_BOOT_0_RAM_AMOUNT_4MB 0x00000002
+-# define NV03_BOOT_0_RAM_AMOUNT_8MB_SDRAM 0x00000003
+-# define NV04_BOOT_0_RAM_AMOUNT_32MB 0x00000000
+-# define NV04_BOOT_0_RAM_AMOUNT_4MB 0x00000001
+-# define NV04_BOOT_0_RAM_AMOUNT_8MB 0x00000002
+-# define NV04_BOOT_0_RAM_AMOUNT_16MB 0x00000003
+-
+-#define NV04_FIFO_DATA 0x0010020c
+-# define NV10_FIFO_DATA_RAM_AMOUNT_MB_MASK 0xfff00000
+-# define NV10_FIFO_DATA_RAM_AMOUNT_MB_SHIFT 20
++#define NV_PEXTDEV_BOOT_0 0x00101000
++#define NV_PEXTDEV_BOOT_0_RAMCFG 0x0000003c
++# define NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT (8 << 12)
++#define NV_PEXTDEV_BOOT_3 0x0010100c
+
+ #define NV_RAMIN 0x00700000
+
+@@ -131,23 +176,6 @@
+ #define NV04_PTIMER_TIME_1 0x00009410
+ #define NV04_PTIMER_ALARM_0 0x00009420
+
+-#define NV04_PFB_CFG0 0x00100200
+-#define NV04_PFB_CFG1 0x00100204
+-#define NV40_PFB_020C 0x0010020C
+-#define NV10_PFB_TILE(i) (0x00100240 + (i*16))
+-#define NV10_PFB_TILE__SIZE 8
+-#define NV10_PFB_TLIMIT(i) (0x00100244 + (i*16))
+-#define NV10_PFB_TSIZE(i) (0x00100248 + (i*16))
+-#define NV10_PFB_TSTATUS(i) (0x0010024C + (i*16))
+-#define NV10_PFB_CLOSE_PAGE2 0x0010033C
+-#define NV40_PFB_TILE(i) (0x00100600 + (i*16))
+-#define NV40_PFB_TILE__SIZE_0 12
+-#define NV40_PFB_TILE__SIZE_1 15
+-#define NV40_PFB_TLIMIT(i) (0x00100604 + (i*16))
+-#define NV40_PFB_TSIZE(i) (0x00100608 + (i*16))
+-#define NV40_PFB_TSTATUS(i) (0x0010060C + (i*16))
+-#define NV40_PFB_UNK_800 0x00100800
+-
+ #define NV04_PGRAPH_DEBUG_0 0x00400080
+ #define NV04_PGRAPH_DEBUG_1 0x00400084
+ #define NV04_PGRAPH_DEBUG_2 0x00400088
+@@ -814,6 +842,7 @@
+ #define NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE 0x80000000
+ #define NV50_PDISPLAY_SOR_BACKLIGHT_LEVEL 0x00000fff
+ #define NV50_SOR_DP_CTRL(i,l) (0x0061c10c + (i) * 0x800 + (l) * 0x80)
++#define NV50_SOR_DP_CTRL_ENABLED 0x00000001
+ #define NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED 0x00004000
+ #define NV50_SOR_DP_CTRL_LANE_MASK 0x001f0000
+ #define NV50_SOR_DP_CTRL_LANE_0_ENABLED 0x00010000
+diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
+index 1d6ee8b..491767f 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c
++++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
+@@ -97,7 +97,6 @@ nouveau_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem)
+
+ NV_DEBUG(dev, "pg=0x%lx\n", mem->mm_node->start);
+
+- dev_priv->engine.instmem.prepare_access(nvbe->dev, true);
+ pte = nouveau_sgdma_pte(nvbe->dev, mem->mm_node->start << PAGE_SHIFT);
+ nvbe->pte_start = pte;
+ for (i = 0; i < nvbe->nr_pages; i++) {
+@@ -116,24 +115,11 @@ nouveau_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem)
+ dma_offset += NV_CTXDMA_PAGE_SIZE;
+ }
+ }
+- dev_priv->engine.instmem.finish_access(nvbe->dev);
++ dev_priv->engine.instmem.flush(nvbe->dev);
+
+ if (dev_priv->card_type == NV_50) {
+- nv_wr32(dev, 0x100c80, 0x00050001);
+- if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+- NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+- NV_ERROR(dev, "0x100c80 = 0x%08x\n",
+- nv_rd32(dev, 0x100c80));
+- return -EBUSY;
+- }
+-
+- nv_wr32(dev, 0x100c80, 0x00000001);
+- if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+- NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+- NV_ERROR(dev, "0x100c80 = 0x%08x\n",
+- nv_rd32(dev, 0x100c80));
+- return -EBUSY;
+- }
++ nv50_vm_flush(dev, 5); /* PGRAPH */
++ nv50_vm_flush(dev, 0); /* PFIFO */
+ }
+
+ nvbe->bound = true;
+@@ -154,7 +140,6 @@ nouveau_sgdma_unbind(struct ttm_backend *be)
+ if (!nvbe->bound)
+ return 0;
+
+- dev_priv->engine.instmem.prepare_access(nvbe->dev, true);
+ pte = nvbe->pte_start;
+ for (i = 0; i < nvbe->nr_pages; i++) {
+ dma_addr_t dma_offset = dev_priv->gart_info.sg_dummy_bus;
+@@ -170,24 +155,11 @@ nouveau_sgdma_unbind(struct ttm_backend *be)
+ dma_offset += NV_CTXDMA_PAGE_SIZE;
+ }
+ }
+- dev_priv->engine.instmem.finish_access(nvbe->dev);
++ dev_priv->engine.instmem.flush(nvbe->dev);
+
+ if (dev_priv->card_type == NV_50) {
+- nv_wr32(dev, 0x100c80, 0x00050001);
+- if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+- NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+- NV_ERROR(dev, "0x100c80 = 0x%08x\n",
+- nv_rd32(dev, 0x100c80));
+- return -EBUSY;
+- }
+-
+- nv_wr32(dev, 0x100c80, 0x00000001);
+- if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+- NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+- NV_ERROR(dev, "0x100c80 = 0x%08x\n",
+- nv_rd32(dev, 0x100c80));
+- return -EBUSY;
+- }
++ nv50_vm_flush(dev, 5);
++ nv50_vm_flush(dev, 0);
+ }
+
+ nvbe->bound = false;
+@@ -272,7 +244,6 @@ nouveau_sgdma_init(struct drm_device *dev)
+ pci_map_page(dev->pdev, dev_priv->gart_info.sg_dummy_page, 0,
+ PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
+
+- dev_priv->engine.instmem.prepare_access(dev, true);
+ if (dev_priv->card_type < NV_50) {
+ /* Maybe use NV_DMA_TARGET_AGP for PCIE? NVIDIA do this, and
+ * confirmed to work on c51. Perhaps means NV_DMA_TARGET_PCIE
+@@ -294,7 +265,7 @@ nouveau_sgdma_init(struct drm_device *dev)
+ nv_wo32(dev, gpuobj, (i+4)/4, 0);
+ }
+ }
+- dev_priv->engine.instmem.finish_access(dev);
++ dev_priv->engine.instmem.flush(dev);
+
+ dev_priv->gart_info.type = NOUVEAU_GART_SGDMA;
+ dev_priv->gart_info.aper_base = 0;
+@@ -325,14 +296,11 @@ nouveau_sgdma_get_page(struct drm_device *dev, uint32_t offset, uint32_t *page)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma;
+- struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
+ int pte;
+
+ pte = (offset >> NV_CTXDMA_PAGE_SHIFT);
+ if (dev_priv->card_type < NV_50) {
+- instmem->prepare_access(dev, false);
+ *page = nv_ro32(dev, gpuobj, (pte + 2)) & ~NV_CTXDMA_PAGE_MASK;
+- instmem->finish_access(dev);
+ return 0;
+ }
+
+diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
+index b02a231..621e080 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_state.c
++++ b/drivers/gpu/drm/nouveau/nouveau_state.c
+@@ -54,8 +54,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
+ engine->instmem.clear = nv04_instmem_clear;
+ engine->instmem.bind = nv04_instmem_bind;
+ engine->instmem.unbind = nv04_instmem_unbind;
+- engine->instmem.prepare_access = nv04_instmem_prepare_access;
+- engine->instmem.finish_access = nv04_instmem_finish_access;
++ engine->instmem.flush = nv04_instmem_flush;
+ engine->mc.init = nv04_mc_init;
+ engine->mc.takedown = nv04_mc_takedown;
+ engine->timer.init = nv04_timer_init;
+@@ -95,8 +94,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
+ engine->instmem.clear = nv04_instmem_clear;
+ engine->instmem.bind = nv04_instmem_bind;
+ engine->instmem.unbind = nv04_instmem_unbind;
+- engine->instmem.prepare_access = nv04_instmem_prepare_access;
+- engine->instmem.finish_access = nv04_instmem_finish_access;
++ engine->instmem.flush = nv04_instmem_flush;
+ engine->mc.init = nv04_mc_init;
+ engine->mc.takedown = nv04_mc_takedown;
+ engine->timer.init = nv04_timer_init;
+@@ -138,8 +136,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
+ engine->instmem.clear = nv04_instmem_clear;
+ engine->instmem.bind = nv04_instmem_bind;
+ engine->instmem.unbind = nv04_instmem_unbind;
+- engine->instmem.prepare_access = nv04_instmem_prepare_access;
+- engine->instmem.finish_access = nv04_instmem_finish_access;
++ engine->instmem.flush = nv04_instmem_flush;
+ engine->mc.init = nv04_mc_init;
+ engine->mc.takedown = nv04_mc_takedown;
+ engine->timer.init = nv04_timer_init;
+@@ -181,8 +178,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
+ engine->instmem.clear = nv04_instmem_clear;
+ engine->instmem.bind = nv04_instmem_bind;
+ engine->instmem.unbind = nv04_instmem_unbind;
+- engine->instmem.prepare_access = nv04_instmem_prepare_access;
+- engine->instmem.finish_access = nv04_instmem_finish_access;
++ engine->instmem.flush = nv04_instmem_flush;
+ engine->mc.init = nv04_mc_init;
+ engine->mc.takedown = nv04_mc_takedown;
+ engine->timer.init = nv04_timer_init;
+@@ -225,8 +221,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
+ engine->instmem.clear = nv04_instmem_clear;
+ engine->instmem.bind = nv04_instmem_bind;
+ engine->instmem.unbind = nv04_instmem_unbind;
+- engine->instmem.prepare_access = nv04_instmem_prepare_access;
+- engine->instmem.finish_access = nv04_instmem_finish_access;
++ engine->instmem.flush = nv04_instmem_flush;
+ engine->mc.init = nv40_mc_init;
+ engine->mc.takedown = nv40_mc_takedown;
+ engine->timer.init = nv04_timer_init;
+@@ -271,8 +266,10 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
+ engine->instmem.clear = nv50_instmem_clear;
+ engine->instmem.bind = nv50_instmem_bind;
+ engine->instmem.unbind = nv50_instmem_unbind;
+- engine->instmem.prepare_access = nv50_instmem_prepare_access;
+- engine->instmem.finish_access = nv50_instmem_finish_access;
++ if (dev_priv->chipset == 0x50)
++ engine->instmem.flush = nv50_instmem_flush;
++ else
++ engine->instmem.flush = nv84_instmem_flush;
+ engine->mc.init = nv50_mc_init;
+ engine->mc.takedown = nv50_mc_takedown;
+ engine->timer.init = nv04_timer_init;
+@@ -407,11 +404,6 @@ nouveau_card_init(struct drm_device *dev)
+ struct nouveau_engine *engine;
+ int ret;
+
+- NV_DEBUG(dev, "prev state = %d\n", dev_priv->init_state);
+-
+- if (dev_priv->init_state == NOUVEAU_CARD_INIT_DONE)
+- return 0;
+-
+ vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode);
+ vga_switcheroo_register_client(dev->pdev, nouveau_switcheroo_set_state,
+ nouveau_switcheroo_can_switch);
+@@ -421,15 +413,12 @@ nouveau_card_init(struct drm_device *dev)
+ if (ret)
+ goto out;
+ engine = &dev_priv->engine;
+- dev_priv->init_state = NOUVEAU_CARD_INIT_FAILED;
+ spin_lock_init(&dev_priv->context_switch_lock);
+
+ /* Parse BIOS tables / Run init tables if card not POSTed */
+- if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+- ret = nouveau_bios_init(dev);
+- if (ret)
+- goto out;
+- }
++ ret = nouveau_bios_init(dev);
++ if (ret)
++ goto out;
+
+ ret = nouveau_mem_detect(dev);
+ if (ret)
+@@ -485,12 +474,19 @@ nouveau_card_init(struct drm_device *dev)
+ goto out_graph;
+ }
+
++ if (dev_priv->card_type >= NV_50)
++ ret = nv50_display_create(dev);
++ else
++ ret = nv04_display_create(dev);
++ if (ret)
++ goto out_fifo;
++
+ /* this call irq_preinstall, register irq handler and
+ * call irq_postinstall
+ */
+ ret = drm_irq_install(dev);
+ if (ret)
+- goto out_fifo;
++ goto out_display;
+
+ ret = drm_vblank_init(dev, 0);
+ if (ret)
+@@ -504,35 +500,21 @@ nouveau_card_init(struct drm_device *dev)
+ goto out_irq;
+ }
+
+- if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+- if (dev_priv->card_type >= NV_50)
+- ret = nv50_display_create(dev);
+- else
+- ret = nv04_display_create(dev);
+- if (ret)
+- goto out_channel;
+- }
+-
+ ret = nouveau_backlight_init(dev);
+ if (ret)
+ NV_ERROR(dev, "Error %d registering backlight\n", ret);
+
+- dev_priv->init_state = NOUVEAU_CARD_INIT_DONE;
+-
+- if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+- nouveau_fbcon_init(dev);
+- drm_kms_helper_poll_init(dev);
+- }
+-
++ nouveau_fbcon_init(dev);
++ drm_kms_helper_poll_init(dev);
+ return 0;
+
+-out_channel:
+- if (dev_priv->channel) {
+- nouveau_channel_free(dev_priv->channel);
+- dev_priv->channel = NULL;
+- }
+ out_irq:
+ drm_irq_uninstall(dev);
++out_display:
++ if (dev_priv->card_type >= NV_50)
++ nv50_display_destroy(dev);
++ else
++ nv04_display_destroy(dev);
+ out_fifo:
+ if (!nouveau_noaccel)
+ engine->fifo.takedown(dev);
+@@ -566,45 +548,37 @@ static void nouveau_card_takedown(struct drm_device *dev)
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_engine *engine = &dev_priv->engine;
+
+- NV_DEBUG(dev, "prev state = %d\n", dev_priv->init_state);
++ nouveau_backlight_exit(dev);
+
+- if (dev_priv->init_state != NOUVEAU_CARD_INIT_DOWN) {
+-
+- nouveau_backlight_exit(dev);
+-
+- if (dev_priv->channel) {
+- nouveau_channel_free(dev_priv->channel);
+- dev_priv->channel = NULL;
+- }
+-
+- if (!nouveau_noaccel) {
+- engine->fifo.takedown(dev);
+- engine->graph.takedown(dev);
+- }
+- engine->fb.takedown(dev);
+- engine->timer.takedown(dev);
+- engine->mc.takedown(dev);
++ if (dev_priv->channel) {
++ nouveau_channel_free(dev_priv->channel);
++ dev_priv->channel = NULL;
++ }
+
+- mutex_lock(&dev->struct_mutex);
+- ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM);
+- ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_TT);
+- mutex_unlock(&dev->struct_mutex);
+- nouveau_sgdma_takedown(dev);
++ if (!nouveau_noaccel) {
++ engine->fifo.takedown(dev);
++ engine->graph.takedown(dev);
++ }
++ engine->fb.takedown(dev);
++ engine->timer.takedown(dev);
++ engine->mc.takedown(dev);
+
+- nouveau_gpuobj_takedown(dev);
+- nouveau_mem_close(dev);
+- engine->instmem.takedown(dev);
++ mutex_lock(&dev->struct_mutex);
++ ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM);
++ ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_TT);
++ mutex_unlock(&dev->struct_mutex);
++ nouveau_sgdma_takedown(dev);
+
+- if (drm_core_check_feature(dev, DRIVER_MODESET))
+- drm_irq_uninstall(dev);
++ nouveau_gpuobj_takedown(dev);
++ nouveau_mem_close(dev);
++ engine->instmem.takedown(dev);
+
+- nouveau_gpuobj_late_takedown(dev);
+- nouveau_bios_takedown(dev);
++ drm_irq_uninstall(dev);
+
+- vga_client_register(dev->pdev, NULL, NULL, NULL);
++ nouveau_gpuobj_late_takedown(dev);
++ nouveau_bios_takedown(dev);
+
+- dev_priv->init_state = NOUVEAU_CARD_INIT_DOWN;
+- }
++ vga_client_register(dev->pdev, NULL, NULL, NULL);
+ }
+
+ /* here a client dies, release the stuff that was allocated for its
+@@ -691,6 +665,7 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
+ struct drm_nouveau_private *dev_priv;
+ uint32_t reg0;
+ resource_size_t mmio_start_offs;
++ int ret;
+
+ dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL);
+ if (!dev_priv)
+@@ -699,7 +674,6 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
+ dev_priv->dev = dev;
+
+ dev_priv->flags = flags & NOUVEAU_FLAGS;
+- dev_priv->init_state = NOUVEAU_CARD_INIT_DOWN;
+
+ NV_DEBUG(dev, "vendor: 0x%X device: 0x%X class: 0x%X\n",
+ dev->pci_vendor, dev->pci_device, dev->pdev->class);
+@@ -812,46 +786,28 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
+ dev_priv->flags |= NV_NFORCE2;
+
+ /* For kernel modesetting, init card now and bring up fbcon */
+- if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+- int ret = nouveau_card_init(dev);
+- if (ret)
+- return ret;
+- }
++ ret = nouveau_card_init(dev);
++ if (ret)
++ return ret;
+
+ return 0;
+ }
+
+-static void nouveau_close(struct drm_device *dev)
+-{
+- struct drm_nouveau_private *dev_priv = dev->dev_private;
+-
+- /* In the case of an error dev_priv may not be allocated yet */
+- if (dev_priv)
+- nouveau_card_takedown(dev);
+-}
+-
+-/* KMS: we need mmio at load time, not when the first drm client opens. */
+ void nouveau_lastclose(struct drm_device *dev)
+ {
+- if (drm_core_check_feature(dev, DRIVER_MODESET))
+- return;
+-
+- nouveau_close(dev);
+ }
+
+ int nouveau_unload(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+- if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+- drm_kms_helper_poll_fini(dev);
+- nouveau_fbcon_fini(dev);
+- if (dev_priv->card_type >= NV_50)
+- nv50_display_destroy(dev);
+- else
+- nv04_display_destroy(dev);
+- nouveau_close(dev);
+- }
++ drm_kms_helper_poll_fini(dev);
++ nouveau_fbcon_fini(dev);
++ if (dev_priv->card_type >= NV_50)
++ nv50_display_destroy(dev);
++ else
++ nv04_display_destroy(dev);
++ nouveau_card_takedown(dev);
+
+ iounmap(dev_priv->mmio);
+ iounmap(dev_priv->ramin);
+@@ -867,8 +823,6 @@ int nouveau_ioctl_getparam(struct drm_device *dev, void *data,
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct drm_nouveau_getparam *getparam = data;
+
+- NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+-
+ switch (getparam->param) {
+ case NOUVEAU_GETPARAM_CHIPSET_ID:
+ getparam->value = dev_priv->chipset;
+@@ -937,8 +891,6 @@ nouveau_ioctl_setparam(struct drm_device *dev, void *data,
+ {
+ struct drm_nouveau_setparam *setparam = data;
+
+- NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
+-
+ switch (setparam->param) {
+ default:
+ NV_ERROR(dev, "unknown parameter %lld\n", setparam->param);
+diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c
+index eba687f..1c20c08 100644
+--- a/drivers/gpu/drm/nouveau/nv04_crtc.c
++++ b/drivers/gpu/drm/nouveau/nv04_crtc.c
+@@ -157,6 +157,7 @@ nv_crtc_dpms(struct drm_crtc *crtc, int mode)
+ {
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+ struct drm_device *dev = crtc->dev;
++ struct drm_connector *connector;
+ unsigned char seq1 = 0, crtc17 = 0;
+ unsigned char crtc1A;
+
+@@ -211,6 +212,10 @@ nv_crtc_dpms(struct drm_crtc *crtc, int mode)
+ NVVgaSeqReset(dev, nv_crtc->index, false);
+
+ NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RPC1_INDEX, crtc1A);
++
++ /* Update connector polling modes */
++ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
++ nouveau_connector_set_polling(connector);
+ }
+
+ static bool
+diff --git a/drivers/gpu/drm/nouveau/nv04_dac.c b/drivers/gpu/drm/nouveau/nv04_dac.c
+index 1cb19e3..2d0fee5 100644
+--- a/drivers/gpu/drm/nouveau/nv04_dac.c
++++ b/drivers/gpu/drm/nouveau/nv04_dac.c
+@@ -261,12 +261,11 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder)
+
+ saved_routput = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
+ head = (saved_routput & 0x100) >> 8;
+-#if 0
+- /* if there's a spare crtc, using it will minimise flicker for the case
+- * where the in-use crtc is in use by an off-chip tmds encoder */
+- if (xf86_config->crtc[head]->enabled && !xf86_config->crtc[head ^ 1]->enabled)
++
++ /* if there's a spare crtc, using it will minimise flicker */
++ if (!(NVReadVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX) & 0xC0))
+ head ^= 1;
+-#endif
++
+ /* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */
+ routput = (saved_routput & 0xfffffece) | head << 8;
+
+@@ -315,9 +314,12 @@ nv17_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector)
+ {
+ struct drm_device *dev = encoder->dev;
+ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
+- uint32_t sample = nv17_dac_sample_load(encoder);
+
+- if (sample & NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI) {
++ if (nv04_dac_in_use(encoder))
++ return connector_status_disconnected;
++
++ if (nv17_dac_sample_load(encoder) &
++ NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI) {
+ NV_INFO(dev, "Load detected on output %c\n",
+ '@' + ffs(dcb->or));
+ return connector_status_connected;
+@@ -330,6 +332,9 @@ static bool nv04_dac_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+ {
++ if (nv04_dac_in_use(encoder))
++ return false;
++
+ return true;
+ }
+
+@@ -428,6 +433,17 @@ void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable)
+ }
+ }
+
++/* Check if the DAC corresponding to 'encoder' is being used by
++ * someone else. */
++bool nv04_dac_in_use(struct drm_encoder *encoder)
++{
++ struct drm_nouveau_private *dev_priv = encoder->dev->dev_private;
++ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
++
++ return nv_gf4_disp_arch(encoder->dev) &&
++ (dev_priv->dac_users[ffs(dcb->or) - 1] & ~(1 << dcb->index));
++}
++
+ static void nv04_dac_dpms(struct drm_encoder *encoder, int mode)
+ {
+ struct drm_device *dev = encoder->dev;
+@@ -501,11 +517,13 @@ static const struct drm_encoder_funcs nv04_dac_funcs = {
+ .destroy = nv04_dac_destroy,
+ };
+
+-int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry)
++int
++nv04_dac_create(struct drm_connector *connector, struct dcb_entry *entry)
+ {
+ const struct drm_encoder_helper_funcs *helper;
+- struct drm_encoder *encoder;
+ struct nouveau_encoder *nv_encoder = NULL;
++ struct drm_device *dev = connector->dev;
++ struct drm_encoder *encoder;
+
+ nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
+ if (!nv_encoder)
+@@ -527,5 +545,6 @@ int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry)
+ encoder->possible_crtcs = entry->heads;
+ encoder->possible_clones = 0;
+
++ drm_mode_connector_attach_encoder(connector, encoder);
+ return 0;
+ }
+diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c
+index 41634d4..3311f3a 100644
+--- a/drivers/gpu/drm/nouveau/nv04_dfp.c
++++ b/drivers/gpu/drm/nouveau/nv04_dfp.c
+@@ -413,10 +413,6 @@ static void nv04_dfp_commit(struct drm_encoder *encoder)
+ struct dcb_entry *dcbe = nv_encoder->dcb;
+ int head = nouveau_crtc(encoder->crtc)->index;
+
+- NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
+- drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
+- nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
+-
+ if (dcbe->type == OUTPUT_TMDS)
+ run_tmds_table(dev, dcbe, head, nv_encoder->mode.clock);
+ else if (dcbe->type == OUTPUT_LVDS)
+@@ -584,11 +580,12 @@ static const struct drm_encoder_funcs nv04_dfp_funcs = {
+ .destroy = nv04_dfp_destroy,
+ };
+
+-int nv04_dfp_create(struct drm_device *dev, struct dcb_entry *entry)
++int
++nv04_dfp_create(struct drm_connector *connector, struct dcb_entry *entry)
+ {
+ const struct drm_encoder_helper_funcs *helper;
+- struct drm_encoder *encoder;
+ struct nouveau_encoder *nv_encoder = NULL;
++ struct drm_encoder *encoder;
+ int type;
+
+ switch (entry->type) {
+@@ -613,11 +610,12 @@ int nv04_dfp_create(struct drm_device *dev, struct dcb_entry *entry)
+ nv_encoder->dcb = entry;
+ nv_encoder->or = ffs(entry->or) - 1;
+
+- drm_encoder_init(dev, encoder, &nv04_dfp_funcs, type);
++ drm_encoder_init(connector->dev, encoder, &nv04_dfp_funcs, type);
+ drm_encoder_helper_add(encoder, helper);
+
+ encoder->possible_crtcs = entry->heads;
+ encoder->possible_clones = 0;
+
++ drm_mode_connector_attach_encoder(connector, encoder);
+ return 0;
+ }
+diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c
+index c7898b4..c6df391 100644
+--- a/drivers/gpu/drm/nouveau/nv04_display.c
++++ b/drivers/gpu/drm/nouveau/nv04_display.c
+@@ -32,8 +32,6 @@
+ #include "nouveau_encoder.h"
+ #include "nouveau_connector.h"
+
+-#define MULTIPLE_ENCODERS(e) (e & (e - 1))
+-
+ static void
+ nv04_display_store_initial_head_owner(struct drm_device *dev)
+ {
+@@ -41,7 +39,7 @@ nv04_display_store_initial_head_owner(struct drm_device *dev)
+
+ if (dev_priv->chipset != 0x11) {
+ dev_priv->crtc_owner = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_44);
+- goto ownerknown;
++ return;
+ }
+
+ /* reading CR44 is broken on nv11, so we attempt to infer it */
+@@ -52,8 +50,6 @@ nv04_display_store_initial_head_owner(struct drm_device *dev)
+ bool tvA = false;
+ bool tvB = false;
+
+- NVLockVgaCrtcs(dev, false);
+-
+ slaved_on_B = NVReadVgaCrtc(dev, 1, NV_CIO_CRE_PIXEL_INDEX) &
+ 0x80;
+ if (slaved_on_B)
+@@ -66,8 +62,6 @@ nv04_display_store_initial_head_owner(struct drm_device *dev)
+ tvA = !(NVReadVgaCrtc(dev, 0, NV_CIO_CRE_LCD__INDEX) &
+ MASK(NV_CIO_CRE_LCD_LCD_SELECT));
+
+- NVLockVgaCrtcs(dev, true);
+-
+ if (slaved_on_A && !tvA)
+ dev_priv->crtc_owner = 0x0;
+ else if (slaved_on_B && !tvB)
+@@ -79,14 +73,6 @@ nv04_display_store_initial_head_owner(struct drm_device *dev)
+ else
+ dev_priv->crtc_owner = 0x0;
+ }
+-
+-ownerknown:
+- NV_INFO(dev, "Initial CRTC_OWNER is %d\n", dev_priv->crtc_owner);
+-
+- /* we need to ensure the heads are not tied henceforth, or reading any
+- * 8 bit reg on head B will fail
+- * setting a single arbitrary head solves that */
+- NVSetOwner(dev, 0);
+ }
+
+ int
+@@ -94,14 +80,20 @@ nv04_display_create(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct dcb_table *dcb = &dev_priv->vbios.dcb;
++ struct drm_connector *connector, *ct;
+ struct drm_encoder *encoder;
+ struct drm_crtc *crtc;
+ int i, ret;
+
+ NV_DEBUG_KMS(dev, "\n");
+
+- if (nv_two_heads(dev))
++ NVLockVgaCrtcs(dev, false);
++
++ if (nv_two_heads(dev)) {
+ nv04_display_store_initial_head_owner(dev);
++ NVSetOwner(dev, 0);
++ }
++
+ nouveau_hw_save_vga_fonts(dev, 1);
+
+ drm_mode_config_init(dev);
+@@ -132,19 +124,23 @@ nv04_display_create(struct drm_device *dev)
+ for (i = 0; i < dcb->entries; i++) {
+ struct dcb_entry *dcbent = &dcb->entry[i];
+
++ connector = nouveau_connector_create(dev, dcbent->connector);
++ if (IS_ERR(connector))
++ continue;
++
+ switch (dcbent->type) {
+ case OUTPUT_ANALOG:
+- ret = nv04_dac_create(dev, dcbent);
++ ret = nv04_dac_create(connector, dcbent);
+ break;
+ case OUTPUT_LVDS:
+ case OUTPUT_TMDS:
+- ret = nv04_dfp_create(dev, dcbent);
++ ret = nv04_dfp_create(connector, dcbent);
+ break;
+ case OUTPUT_TV:
+ if (dcbent->location == DCB_LOC_ON_CHIP)
+- ret = nv17_tv_create(dev, dcbent);
++ ret = nv17_tv_create(connector, dcbent);
+ else
+- ret = nv04_tv_create(dev, dcbent);
++ ret = nv04_tv_create(connector, dcbent);
+ break;
+ default:
+ NV_WARN(dev, "DCB type %d not known\n", dcbent->type);
+@@ -155,12 +151,16 @@ nv04_display_create(struct drm_device *dev)
+ continue;
+ }
+
+- for (i = 0; i < dcb->connector.entries; i++)
+- nouveau_connector_create(dev, &dcb->connector.entry[i]);
++ list_for_each_entry_safe(connector, ct,
++ &dev->mode_config.connector_list, head) {
++ if (!connector->encoder_ids[0]) {
++ NV_WARN(dev, "%s has no encoders, removing\n",
++ drm_get_connector_name(connector));
++ connector->funcs->destroy(connector);
++ }
++ }
+
+ /* Save previous state */
+- NVLockVgaCrtcs(dev, false);
+-
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+ crtc->funcs->save(crtc);
+
+@@ -176,6 +176,7 @@ nv04_display_create(struct drm_device *dev)
+ void
+ nv04_display_destroy(struct drm_device *dev)
+ {
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct drm_encoder *encoder;
+ struct drm_crtc *crtc;
+
+@@ -191,8 +192,6 @@ nv04_display_destroy(struct drm_device *dev)
+ }
+
+ /* Restore state */
+- NVLockVgaCrtcs(dev, false);
+-
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ struct drm_encoder_helper_funcs *func = encoder->helper_private;
+
+@@ -205,12 +204,15 @@ nv04_display_destroy(struct drm_device *dev)
+ drm_mode_config_cleanup(dev);
+
+ nouveau_hw_save_vga_fonts(dev, 0);
++
++ if (nv_two_heads(dev))
++ NVSetOwner(dev, dev_priv->crtc_owner);
++ NVLockVgaCrtcs(dev, true);
+ }
+
+ void
+ nv04_display_restore(struct drm_device *dev)
+ {
+- struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct drm_encoder *encoder;
+ struct drm_crtc *crtc;
+
+@@ -232,13 +234,5 @@ nv04_display_restore(struct drm_device *dev)
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+ crtc->funcs->restore(crtc);
+-
+- if (nv_two_heads(dev)) {
+- NV_INFO(dev, "Restoring CRTC_OWNER to %d.\n",
+- dev_priv->crtc_owner);
+- NVSetOwner(dev, dev_priv->crtc_owner);
+- }
+-
+- NVLockVgaCrtcs(dev, true);
+ }
+
+diff --git a/drivers/gpu/drm/nouveau/nv04_fifo.c b/drivers/gpu/drm/nouveau/nv04_fifo.c
+index 66fe559..06cedd9 100644
+--- a/drivers/gpu/drm/nouveau/nv04_fifo.c
++++ b/drivers/gpu/drm/nouveau/nv04_fifo.c
+@@ -112,6 +112,12 @@ nv04_fifo_channel_id(struct drm_device *dev)
+ NV03_PFIFO_CACHE1_PUSH1_CHID_MASK;
+ }
+
++#ifdef __BIG_ENDIAN
++#define DMA_FETCH_ENDIANNESS NV_PFIFO_CACHE1_BIG_ENDIAN
++#else
++#define DMA_FETCH_ENDIANNESS 0
++#endif
++
+ int
+ nv04_fifo_create_context(struct nouveau_channel *chan)
+ {
+@@ -131,18 +137,13 @@ nv04_fifo_create_context(struct nouveau_channel *chan)
+ spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
+
+ /* Setup initial state */
+- dev_priv->engine.instmem.prepare_access(dev, true);
+ RAMFC_WR(DMA_PUT, chan->pushbuf_base);
+ RAMFC_WR(DMA_GET, chan->pushbuf_base);
+ RAMFC_WR(DMA_INSTANCE, chan->pushbuf->instance >> 4);
+ RAMFC_WR(DMA_FETCH, (NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
+ NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES |
+ NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 |
+-#ifdef __BIG_ENDIAN
+- NV_PFIFO_CACHE1_BIG_ENDIAN |
+-#endif
+- 0));
+- dev_priv->engine.instmem.finish_access(dev);
++ DMA_FETCH_ENDIANNESS));
+
+ /* enable the fifo dma operation */
+ nv_wr32(dev, NV04_PFIFO_MODE,
+@@ -169,8 +170,6 @@ nv04_fifo_do_load_context(struct drm_device *dev, int chid)
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ uint32_t fc = NV04_RAMFC(chid), tmp;
+
+- dev_priv->engine.instmem.prepare_access(dev, false);
+-
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUT, nv_ri32(dev, fc + 0));
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET, nv_ri32(dev, fc + 4));
+ tmp = nv_ri32(dev, fc + 8);
+@@ -181,8 +180,6 @@ nv04_fifo_do_load_context(struct drm_device *dev, int chid)
+ nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_ri32(dev, fc + 20));
+ nv_wr32(dev, NV04_PFIFO_CACHE1_PULL1, nv_ri32(dev, fc + 24));
+
+- dev_priv->engine.instmem.finish_access(dev);
+-
+ nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0);
+ }
+@@ -223,7 +220,6 @@ nv04_fifo_unload_context(struct drm_device *dev)
+ return -EINVAL;
+ }
+
+- dev_priv->engine.instmem.prepare_access(dev, true);
+ RAMFC_WR(DMA_PUT, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT));
+ RAMFC_WR(DMA_GET, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
+ tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT) << 16;
+@@ -233,7 +229,6 @@ nv04_fifo_unload_context(struct drm_device *dev)
+ RAMFC_WR(DMA_FETCH, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_FETCH));
+ RAMFC_WR(ENGINE, nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE));
+ RAMFC_WR(PULL1_ENGINE, nv_rd32(dev, NV04_PFIFO_CACHE1_PULL1));
+- dev_priv->engine.instmem.finish_access(dev);
+
+ nv04_fifo_do_load_context(dev, pfifo->channels - 1);
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
+@@ -297,6 +292,7 @@ nv04_fifo_init(struct drm_device *dev)
+
+ nv04_fifo_init_intr(dev);
+ pfifo->enable(dev);
++ pfifo->reassign(dev, true);
+
+ for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
+ if (dev_priv->fifos[i]) {
+diff --git a/drivers/gpu/drm/nouveau/nv04_graph.c b/drivers/gpu/drm/nouveau/nv04_graph.c
+index 618355e..c897342 100644
+--- a/drivers/gpu/drm/nouveau/nv04_graph.c
++++ b/drivers/gpu/drm/nouveau/nv04_graph.c
+@@ -342,7 +342,7 @@ static uint32_t nv04_graph_ctx_regs[] = {
+ };
+
+ struct graph_state {
+- int nv04[ARRAY_SIZE(nv04_graph_ctx_regs)];
++ uint32_t nv04[ARRAY_SIZE(nv04_graph_ctx_regs)];
+ };
+
+ struct nouveau_channel *
+@@ -527,8 +527,7 @@ static int
+ nv04_graph_mthd_set_ref(struct nouveau_channel *chan, int grclass,
+ int mthd, uint32_t data)
+ {
+- chan->fence.last_sequence_irq = data;
+- nouveau_fence_handler(chan->dev, chan->id);
++ atomic_set(&chan->fence.last_sequence_irq, data);
+ return 0;
+ }
+
+diff --git a/drivers/gpu/drm/nouveau/nv04_instmem.c b/drivers/gpu/drm/nouveau/nv04_instmem.c
+index a3b9563..4408232 100644
+--- a/drivers/gpu/drm/nouveau/nv04_instmem.c
++++ b/drivers/gpu/drm/nouveau/nv04_instmem.c
+@@ -49,10 +49,8 @@ nv04_instmem_determine_amount(struct drm_device *dev)
+ NV_DEBUG(dev, "RAMIN size: %dKiB\n", dev_priv->ramin_rsvd_vram >> 10);
+
+ /* Clear all of it, except the BIOS image that's in the first 64KiB */
+- dev_priv->engine.instmem.prepare_access(dev, true);
+ for (i = 64 * 1024; i < dev_priv->ramin_rsvd_vram; i += 4)
+ nv_wi32(dev, i, 0x00000000);
+- dev_priv->engine.instmem.finish_access(dev);
+ }
+
+ static void
+@@ -106,7 +104,7 @@ int nv04_instmem_init(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ uint32_t offset;
+- int ret = 0;
++ int ret;
+
+ nv04_instmem_determine_amount(dev);
+ nv04_instmem_configure_fixed_tables(dev);
+@@ -129,14 +127,14 @@ int nv04_instmem_init(struct drm_device *dev)
+ offset = 0x40000;
+ }
+
+- ret = nouveau_mem_init_heap(&dev_priv->ramin_heap,
+- offset, dev_priv->ramin_rsvd_vram - offset);
++ ret = drm_mm_init(&dev_priv->ramin_heap, offset,
++ dev_priv->ramin_rsvd_vram - offset);
+ if (ret) {
+- dev_priv->ramin_heap = NULL;
+- NV_ERROR(dev, "Failed to init RAMIN heap\n");
++ NV_ERROR(dev, "Failed to init RAMIN heap: %d\n", ret);
++ return ret;
+ }
+
+- return ret;
++ return 0;
+ }
+
+ void
+@@ -186,12 +184,7 @@ nv04_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
+ }
+
+ void
+-nv04_instmem_prepare_access(struct drm_device *dev, bool write)
+-{
+-}
+-
+-void
+-nv04_instmem_finish_access(struct drm_device *dev)
++nv04_instmem_flush(struct drm_device *dev)
+ {
+ }
+
+diff --git a/drivers/gpu/drm/nouveau/nv04_mc.c b/drivers/gpu/drm/nouveau/nv04_mc.c
+index 617ed1e..2af43a1 100644
+--- a/drivers/gpu/drm/nouveau/nv04_mc.c
++++ b/drivers/gpu/drm/nouveau/nv04_mc.c
+@@ -11,6 +11,10 @@ nv04_mc_init(struct drm_device *dev)
+ */
+
+ nv_wr32(dev, NV03_PMC_ENABLE, 0xFFFFFFFF);
++
++ /* Disable PROM access. */
++ nv_wr32(dev, NV_PBUS_PCI_NV_20, NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED);
++
+ return 0;
+ }
+
+diff --git a/drivers/gpu/drm/nouveau/nv04_tv.c b/drivers/gpu/drm/nouveau/nv04_tv.c
+index c4e3404..94e299c 100644
+--- a/drivers/gpu/drm/nouveau/nv04_tv.c
++++ b/drivers/gpu/drm/nouveau/nv04_tv.c
+@@ -34,69 +34,26 @@
+
+ #include "i2c/ch7006.h"
+
+-static struct {
+- struct i2c_board_info board_info;
+- struct drm_encoder_funcs funcs;
+- struct drm_encoder_helper_funcs hfuncs;
+- void *params;
+-
+-} nv04_tv_encoder_info[] = {
++static struct i2c_board_info nv04_tv_encoder_info[] = {
+ {
+- .board_info = { I2C_BOARD_INFO("ch7006", 0x75) },
+- .params = &(struct ch7006_encoder_params) {
++ I2C_BOARD_INFO("ch7006", 0x75),
++ .platform_data = &(struct ch7006_encoder_params) {
+ CH7006_FORMAT_RGB24m12I, CH7006_CLOCK_MASTER,
+ 0, 0, 0,
+ CH7006_SYNC_SLAVE, CH7006_SYNC_SEPARATED,
+ CH7006_POUT_3_3V, CH7006_ACTIVE_HSYNC
+- },
++ }
+ },
++ { }
+ };
+
+-static bool probe_i2c_addr(struct i2c_adapter *adapter, int addr)
+-{
+- struct i2c_msg msg = {
+- .addr = addr,
+- .len = 0,
+- };
+-
+- return i2c_transfer(adapter, &msg, 1) == 1;
+-}
+-
+ int nv04_tv_identify(struct drm_device *dev, int i2c_index)
+ {
+- struct nouveau_i2c_chan *i2c;
+- bool was_locked;
+- int i, ret;
+-
+- NV_TRACE(dev, "Probing TV encoders on I2C bus: %d\n", i2c_index);
+-
+- i2c = nouveau_i2c_find(dev, i2c_index);
+- if (!i2c)
+- return -ENODEV;
+-
+- was_locked = NVLockVgaCrtcs(dev, false);
+-
+- for (i = 0; i < ARRAY_SIZE(nv04_tv_encoder_info); i++) {
+- if (probe_i2c_addr(&i2c->adapter,
+- nv04_tv_encoder_info[i].board_info.addr)) {
+- ret = i;
+- break;
+- }
+- }
+-
+- if (i < ARRAY_SIZE(nv04_tv_encoder_info)) {
+- NV_TRACE(dev, "Detected TV encoder: %s\n",
+- nv04_tv_encoder_info[i].board_info.type);
+-
+- } else {
+- NV_TRACE(dev, "No TV encoders found.\n");
+- i = -ENODEV;
+- }
+-
+- NVLockVgaCrtcs(dev, was_locked);
+- return i;
++ return nouveau_i2c_identify(dev, "TV encoder",
++ nv04_tv_encoder_info, i2c_index);
+ }
+
++
+ #define PLLSEL_TV_CRTC1_MASK \
+ (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \
+ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1)
+@@ -214,30 +171,32 @@ static void nv04_tv_commit(struct drm_encoder *encoder)
+
+ static void nv04_tv_destroy(struct drm_encoder *encoder)
+ {
+- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+-
+ to_encoder_slave(encoder)->slave_funcs->destroy(encoder);
+
+ drm_encoder_cleanup(encoder);
+
+- kfree(nv_encoder);
++ kfree(encoder->helper_private);
++ kfree(nouveau_encoder(encoder));
+ }
+
+-int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry)
++static const struct drm_encoder_funcs nv04_tv_funcs = {
++ .destroy = nv04_tv_destroy,
++};
++
++int
++nv04_tv_create(struct drm_connector *connector, struct dcb_entry *entry)
+ {
+ struct nouveau_encoder *nv_encoder;
+ struct drm_encoder *encoder;
+- struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct i2c_adapter *adap;
+- struct drm_encoder_funcs *funcs = NULL;
+- struct drm_encoder_helper_funcs *hfuncs = NULL;
+- struct drm_encoder_slave_funcs *sfuncs = NULL;
+- int i2c_index = entry->i2c_index;
++ struct drm_device *dev = connector->dev;
++ struct drm_encoder_helper_funcs *hfuncs;
++ struct drm_encoder_slave_funcs *sfuncs;
++ struct nouveau_i2c_chan *i2c =
++ nouveau_i2c_find(dev, entry->i2c_index);
+ int type, ret;
+- bool was_locked;
+
+ /* Ensure that we can talk to this encoder */
+- type = nv04_tv_identify(dev, i2c_index);
++ type = nv04_tv_identify(dev, entry->i2c_index);
+ if (type < 0)
+ return type;
+
+@@ -246,41 +205,32 @@ int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry)
+ if (!nv_encoder)
+ return -ENOMEM;
+
++ hfuncs = kzalloc(sizeof(*hfuncs), GFP_KERNEL);
++ if (!hfuncs) {
++ ret = -ENOMEM;
++ goto fail_free;
++ }
++
+ /* Initialize the common members */
+ encoder = to_drm_encoder(nv_encoder);
+
+- funcs = &nv04_tv_encoder_info[type].funcs;
+- hfuncs = &nv04_tv_encoder_info[type].hfuncs;
+-
+- drm_encoder_init(dev, encoder, funcs, DRM_MODE_ENCODER_TVDAC);
++ drm_encoder_init(dev, encoder, &nv04_tv_funcs, DRM_MODE_ENCODER_TVDAC);
+ drm_encoder_helper_add(encoder, hfuncs);
+
+ encoder->possible_crtcs = entry->heads;
+ encoder->possible_clones = 0;
+-
+ nv_encoder->dcb = entry;
+ nv_encoder->or = ffs(entry->or) - 1;
+
+ /* Run the slave-specific initialization */
+- adap = &dev_priv->vbios.dcb.i2c[i2c_index].chan->adapter;
+-
+- was_locked = NVLockVgaCrtcs(dev, false);
+-
+- ret = drm_i2c_encoder_init(encoder->dev, to_encoder_slave(encoder), adap,
+- &nv04_tv_encoder_info[type].board_info);
+-
+- NVLockVgaCrtcs(dev, was_locked);
+-
++ ret = drm_i2c_encoder_init(dev, to_encoder_slave(encoder),
++ &i2c->adapter, &nv04_tv_encoder_info[type]);
+ if (ret < 0)
+- goto fail;
++ goto fail_cleanup;
+
+ /* Fill the function pointers */
+ sfuncs = to_encoder_slave(encoder)->slave_funcs;
+
+- *funcs = (struct drm_encoder_funcs) {
+- .destroy = nv04_tv_destroy,
+- };
+-
+ *hfuncs = (struct drm_encoder_helper_funcs) {
+ .dpms = nv04_tv_dpms,
+ .save = sfuncs->save,
+@@ -292,14 +242,17 @@ int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry)
+ .detect = sfuncs->detect,
+ };
+
+- /* Set the slave encoder configuration */
+- sfuncs->set_config(encoder, nv04_tv_encoder_info[type].params);
++ /* Attach it to the specified connector. */
++ sfuncs->set_config(encoder, nv04_tv_encoder_info[type].platform_data);
++ sfuncs->create_resources(encoder, connector);
++ drm_mode_connector_attach_encoder(connector, encoder);
+
+ return 0;
+
+-fail:
++fail_cleanup:
+ drm_encoder_cleanup(encoder);
+-
++ kfree(hfuncs);
++fail_free:
+ kfree(nv_encoder);
+ return ret;
+ }
+diff --git a/drivers/gpu/drm/nouveau/nv10_fifo.c b/drivers/gpu/drm/nouveau/nv10_fifo.c
+index 7aeabf2..7a4069c 100644
+--- a/drivers/gpu/drm/nouveau/nv10_fifo.c
++++ b/drivers/gpu/drm/nouveau/nv10_fifo.c
+@@ -55,7 +55,6 @@ nv10_fifo_create_context(struct nouveau_channel *chan)
+ /* Fill entries that are seen filled in dumps of nvidia driver just
+ * after channel's is put into DMA mode
+ */
+- dev_priv->engine.instmem.prepare_access(dev, true);
+ nv_wi32(dev, fc + 0, chan->pushbuf_base);
+ nv_wi32(dev, fc + 4, chan->pushbuf_base);
+ nv_wi32(dev, fc + 12, chan->pushbuf->instance >> 4);
+@@ -66,7 +65,6 @@ nv10_fifo_create_context(struct nouveau_channel *chan)
+ NV_PFIFO_CACHE1_BIG_ENDIAN |
+ #endif
+ 0);
+- dev_priv->engine.instmem.finish_access(dev);
+
+ /* enable the fifo dma operation */
+ nv_wr32(dev, NV04_PFIFO_MODE,
+@@ -91,8 +89,6 @@ nv10_fifo_do_load_context(struct drm_device *dev, int chid)
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ uint32_t fc = NV10_RAMFC(chid), tmp;
+
+- dev_priv->engine.instmem.prepare_access(dev, false);
+-
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUT, nv_ri32(dev, fc + 0));
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET, nv_ri32(dev, fc + 4));
+ nv_wr32(dev, NV10_PFIFO_CACHE1_REF_CNT, nv_ri32(dev, fc + 8));
+@@ -117,8 +113,6 @@ nv10_fifo_do_load_context(struct drm_device *dev, int chid)
+ nv_wr32(dev, NV10_PFIFO_CACHE1_DMA_SUBROUTINE, nv_ri32(dev, fc + 48));
+
+ out:
+- dev_priv->engine.instmem.finish_access(dev);
+-
+ nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0);
+ }
+@@ -155,8 +149,6 @@ nv10_fifo_unload_context(struct drm_device *dev)
+ return 0;
+ fc = NV10_RAMFC(chid);
+
+- dev_priv->engine.instmem.prepare_access(dev, true);
+-
+ nv_wi32(dev, fc + 0, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT));
+ nv_wi32(dev, fc + 4, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
+ nv_wi32(dev, fc + 8, nv_rd32(dev, NV10_PFIFO_CACHE1_REF_CNT));
+@@ -179,8 +171,6 @@ nv10_fifo_unload_context(struct drm_device *dev)
+ nv_wi32(dev, fc + 48, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
+
+ out:
+- dev_priv->engine.instmem.finish_access(dev);
+-
+ nv10_fifo_do_load_context(dev, pfifo->channels - 1);
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
+ return 0;
+diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c b/drivers/gpu/drm/nouveau/nv17_tv.c
+index 74c8803..bb3a284 100644
+--- a/drivers/gpu/drm/nouveau/nv17_tv.c
++++ b/drivers/gpu/drm/nouveau/nv17_tv.c
+@@ -116,6 +116,21 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder)
+ return sample;
+ }
+
++static bool
++get_tv_detect_quirks(struct drm_device *dev, uint32_t *pin_mask)
++{
++ /* Zotac FX5200 */
++ if (dev->pdev->device == 0x0322 &&
++ dev->pdev->subsystem_vendor == 0x19da &&
++ (dev->pdev->subsystem_device == 0x1035 ||
++ dev->pdev->subsystem_device == 0x2035)) {
++ *pin_mask = 0xc;
++ return false;
++ }
++
++ return true;
++}
++
+ static enum drm_connector_status
+ nv17_tv_detect(struct drm_encoder *encoder, struct drm_connector *connector)
+ {
+@@ -124,12 +139,20 @@ nv17_tv_detect(struct drm_encoder *encoder, struct drm_connector *connector)
+ struct drm_mode_config *conf = &dev->mode_config;
+ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
+ struct dcb_entry *dcb = tv_enc->base.dcb;
++ bool reliable = get_tv_detect_quirks(dev, &tv_enc->pin_mask);
+
+- if (dev_priv->chipset == 0x42 ||
+- dev_priv->chipset == 0x43)
+- tv_enc->pin_mask = nv42_tv_sample_load(encoder) >> 28 & 0xe;
+- else
+- tv_enc->pin_mask = nv17_dac_sample_load(encoder) >> 28 & 0xe;
++ if (nv04_dac_in_use(encoder))
++ return connector_status_disconnected;
++
++ if (reliable) {
++ if (dev_priv->chipset == 0x42 ||
++ dev_priv->chipset == 0x43)
++ tv_enc->pin_mask =
++ nv42_tv_sample_load(encoder) >> 28 & 0xe;
++ else
++ tv_enc->pin_mask =
++ nv17_dac_sample_load(encoder) >> 28 & 0xe;
++ }
+
+ switch (tv_enc->pin_mask) {
+ case 0x2:
+@@ -154,7 +177,9 @@ nv17_tv_detect(struct drm_encoder *encoder, struct drm_connector *connector)
+ conf->tv_subconnector_property,
+ tv_enc->subconnector);
+
+- if (tv_enc->subconnector) {
++ if (!reliable) {
++ return connector_status_unknown;
++ } else if (tv_enc->subconnector) {
+ NV_INFO(dev, "Load detected on output %c\n",
+ '@' + ffs(dcb->or));
+ return connector_status_connected;
+@@ -296,6 +321,9 @@ static bool nv17_tv_mode_fixup(struct drm_encoder *encoder,
+ {
+ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
+
++ if (nv04_dac_in_use(encoder))
++ return false;
++
+ if (tv_norm->kind == CTV_ENC_MODE)
+ adjusted_mode->clock = tv_norm->ctv_enc_mode.mode.clock;
+ else
+@@ -744,8 +772,10 @@ static struct drm_encoder_funcs nv17_tv_funcs = {
+ .destroy = nv17_tv_destroy,
+ };
+
+-int nv17_tv_create(struct drm_device *dev, struct dcb_entry *entry)
++int
++nv17_tv_create(struct drm_connector *connector, struct dcb_entry *entry)
+ {
++ struct drm_device *dev = connector->dev;
+ struct drm_encoder *encoder;
+ struct nv17_tv_encoder *tv_enc = NULL;
+
+@@ -774,5 +804,7 @@ int nv17_tv_create(struct drm_device *dev, struct dcb_entry *entry)
+ encoder->possible_crtcs = entry->heads;
+ encoder->possible_clones = 0;
+
++ nv17_tv_create_resources(encoder, connector);
++ drm_mode_connector_attach_encoder(connector, encoder);
+ return 0;
+ }
+diff --git a/drivers/gpu/drm/nouveau/nv20_graph.c b/drivers/gpu/drm/nouveau/nv20_graph.c
+index d6fc0a8..191c15c 100644
+--- a/drivers/gpu/drm/nouveau/nv20_graph.c
++++ b/drivers/gpu/drm/nouveau/nv20_graph.c
+@@ -370,68 +370,54 @@ nv20_graph_create_context(struct nouveau_channel *chan)
+ {
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+ void (*ctx_init)(struct drm_device *, struct nouveau_gpuobj *);
+- unsigned int ctx_size;
+ unsigned int idoffs = 0x28/4;
+ int ret;
+
+ switch (dev_priv->chipset) {
+ case 0x20:
+- ctx_size = NV20_GRCTX_SIZE;
+ ctx_init = nv20_graph_context_init;
+ idoffs = 0;
+ break;
+ case 0x25:
+ case 0x28:
+- ctx_size = NV25_GRCTX_SIZE;
+ ctx_init = nv25_graph_context_init;
+ break;
+ case 0x2a:
+- ctx_size = NV2A_GRCTX_SIZE;
+ ctx_init = nv2a_graph_context_init;
+ idoffs = 0;
+ break;
+ case 0x30:
+ case 0x31:
+- ctx_size = NV30_31_GRCTX_SIZE;
+ ctx_init = nv30_31_graph_context_init;
+ break;
+ case 0x34:
+- ctx_size = NV34_GRCTX_SIZE;
+ ctx_init = nv34_graph_context_init;
+ break;
+ case 0x35:
+ case 0x36:
+- ctx_size = NV35_36_GRCTX_SIZE;
+ ctx_init = nv35_36_graph_context_init;
+ break;
+ default:
+- ctx_size = 0;
+- ctx_init = nv35_36_graph_context_init;
+- NV_ERROR(dev, "Please contact the devs if you want your NV%x"
+- " card to work\n", dev_priv->chipset);
+- return -ENOSYS;
+- break;
++ BUG_ON(1);
+ }
+
+- ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, ctx_size, 16,
+- NVOBJ_FLAG_ZERO_ALLOC,
+- &chan->ramin_grctx);
++ ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pgraph->grctx_size,
++ 16, NVOBJ_FLAG_ZERO_ALLOC,
++ &chan->ramin_grctx);
+ if (ret)
+ return ret;
+
+ /* Initialise default context values */
+- dev_priv->engine.instmem.prepare_access(dev, true);
+ ctx_init(dev, chan->ramin_grctx->gpuobj);
+
+ /* nv20: nv_wo32(dev, chan->ramin_grctx->gpuobj, 10, chan->id<<24); */
+ nv_wo32(dev, chan->ramin_grctx->gpuobj, idoffs,
+ (chan->id << 24) | 0x1); /* CTX_USER */
+
+- nv_wo32(dev, dev_priv->ctx_table->gpuobj, chan->id,
+- chan->ramin_grctx->instance >> 4);
+-
+- dev_priv->engine.instmem.finish_access(dev);
++ nv_wo32(dev, pgraph->ctx_table->gpuobj, chan->id,
++ chan->ramin_grctx->instance >> 4);
+ return 0;
+ }
+
+@@ -440,13 +426,12 @@ nv20_graph_destroy_context(struct nouveau_channel *chan)
+ {
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+
+ if (chan->ramin_grctx)
+ nouveau_gpuobj_ref_del(dev, &chan->ramin_grctx);
+
+- dev_priv->engine.instmem.prepare_access(dev, true);
+- nv_wo32(dev, dev_priv->ctx_table->gpuobj, chan->id, 0);
+- dev_priv->engine.instmem.finish_access(dev);
++ nv_wo32(dev, pgraph->ctx_table->gpuobj, chan->id, 0);
+ }
+
+ int
+@@ -538,29 +523,44 @@ nv20_graph_set_region_tiling(struct drm_device *dev, int i, uint32_t addr,
+ int
+ nv20_graph_init(struct drm_device *dev)
+ {
+- struct drm_nouveau_private *dev_priv =
+- (struct drm_nouveau_private *)dev->dev_private;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+ uint32_t tmp, vramsz;
+ int ret, i;
+
++ switch (dev_priv->chipset) {
++ case 0x20:
++ pgraph->grctx_size = NV20_GRCTX_SIZE;
++ break;
++ case 0x25:
++ case 0x28:
++ pgraph->grctx_size = NV25_GRCTX_SIZE;
++ break;
++ case 0x2a:
++ pgraph->grctx_size = NV2A_GRCTX_SIZE;
++ break;
++ default:
++ NV_ERROR(dev, "unknown chipset, disabling acceleration\n");
++ pgraph->accel_blocked = true;
++ return 0;
++ }
++
+ nv_wr32(dev, NV03_PMC_ENABLE,
+ nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PGRAPH);
+ nv_wr32(dev, NV03_PMC_ENABLE,
+ nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PGRAPH);
+
+- if (!dev_priv->ctx_table) {
++ if (!pgraph->ctx_table) {
+ /* Create Context Pointer Table */
+- dev_priv->ctx_table_size = 32 * 4;
+- ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0,
+- dev_priv->ctx_table_size, 16,
++ ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 32 * 4, 16,
+ NVOBJ_FLAG_ZERO_ALLOC,
+- &dev_priv->ctx_table);
++ &pgraph->ctx_table);
+ if (ret)
+ return ret;
+ }
+
+ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE,
+- dev_priv->ctx_table->instance >> 4);
++ pgraph->ctx_table->instance >> 4);
+
+ nv20_graph_rdi(dev);
+
+@@ -644,34 +644,52 @@ void
+ nv20_graph_takedown(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+
+- nouveau_gpuobj_ref_del(dev, &dev_priv->ctx_table);
++ nouveau_gpuobj_ref_del(dev, &pgraph->ctx_table);
+ }
+
+ int
+ nv30_graph_init(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
+ int ret, i;
+
++ switch (dev_priv->chipset) {
++ case 0x30:
++ case 0x31:
++ pgraph->grctx_size = NV30_31_GRCTX_SIZE;
++ break;
++ case 0x34:
++ pgraph->grctx_size = NV34_GRCTX_SIZE;
++ break;
++ case 0x35:
++ case 0x36:
++ pgraph->grctx_size = NV35_36_GRCTX_SIZE;
++ break;
++ default:
++ NV_ERROR(dev, "unknown chipset, disabling acceleration\n");
++ pgraph->accel_blocked = true;
++ return 0;
++ }
++
+ nv_wr32(dev, NV03_PMC_ENABLE,
+ nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PGRAPH);
+ nv_wr32(dev, NV03_PMC_ENABLE,
+ nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PGRAPH);
+
+- if (!dev_priv->ctx_table) {
++ if (!pgraph->ctx_table) {
+ /* Create Context Pointer Table */
+- dev_priv->ctx_table_size = 32 * 4;
+- ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0,
+- dev_priv->ctx_table_size, 16,
++ ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 32 * 4, 16,
+ NVOBJ_FLAG_ZERO_ALLOC,
+- &dev_priv->ctx_table);
++ &pgraph->ctx_table);
+ if (ret)
+ return ret;
+ }
+
+ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE,
+- dev_priv->ctx_table->instance >> 4);
++ pgraph->ctx_table->instance >> 4);
+
+ nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF);
+ nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
+diff --git a/drivers/gpu/drm/nouveau/nv40_fifo.c b/drivers/gpu/drm/nouveau/nv40_fifo.c
+index 500ccfd..2b67f18 100644
+--- a/drivers/gpu/drm/nouveau/nv40_fifo.c
++++ b/drivers/gpu/drm/nouveau/nv40_fifo.c
+@@ -48,7 +48,6 @@ nv40_fifo_create_context(struct nouveau_channel *chan)
+
+ spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
+
+- dev_priv->engine.instmem.prepare_access(dev, true);
+ nv_wi32(dev, fc + 0, chan->pushbuf_base);
+ nv_wi32(dev, fc + 4, chan->pushbuf_base);
+ nv_wi32(dev, fc + 12, chan->pushbuf->instance >> 4);
+@@ -61,7 +60,6 @@ nv40_fifo_create_context(struct nouveau_channel *chan)
+ 0x30000000 /* no idea.. */);
+ nv_wi32(dev, fc + 56, chan->ramin_grctx->instance >> 4);
+ nv_wi32(dev, fc + 60, 0x0001FFFF);
+- dev_priv->engine.instmem.finish_access(dev);
+
+ /* enable the fifo dma operation */
+ nv_wr32(dev, NV04_PFIFO_MODE,
+@@ -89,8 +87,6 @@ nv40_fifo_do_load_context(struct drm_device *dev, int chid)
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ uint32_t fc = NV40_RAMFC(chid), tmp, tmp2;
+
+- dev_priv->engine.instmem.prepare_access(dev, false);
+-
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUT, nv_ri32(dev, fc + 0));
+ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET, nv_ri32(dev, fc + 4));
+ nv_wr32(dev, NV10_PFIFO_CACHE1_REF_CNT, nv_ri32(dev, fc + 8));
+@@ -127,8 +123,6 @@ nv40_fifo_do_load_context(struct drm_device *dev, int chid)
+ nv_wr32(dev, 0x2088, nv_ri32(dev, fc + 76));
+ nv_wr32(dev, 0x3300, nv_ri32(dev, fc + 80));
+
+- dev_priv->engine.instmem.finish_access(dev);
+-
+ nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0);
+ }
+@@ -166,7 +160,6 @@ nv40_fifo_unload_context(struct drm_device *dev)
+ return 0;
+ fc = NV40_RAMFC(chid);
+
+- dev_priv->engine.instmem.prepare_access(dev, true);
+ nv_wi32(dev, fc + 0, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT));
+ nv_wi32(dev, fc + 4, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
+ nv_wi32(dev, fc + 8, nv_rd32(dev, NV10_PFIFO_CACHE1_REF_CNT));
+@@ -200,7 +193,6 @@ nv40_fifo_unload_context(struct drm_device *dev)
+ tmp |= (nv_rd32(dev, NV04_PFIFO_CACHE1_PUT) << 16);
+ nv_wi32(dev, fc + 72, tmp);
+ #endif
+- dev_priv->engine.instmem.finish_access(dev);
+
+ nv40_fifo_do_load_context(dev, pfifo->channels - 1);
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1,
+diff --git a/drivers/gpu/drm/nouveau/nv40_graph.c b/drivers/gpu/drm/nouveau/nv40_graph.c
+index 704a25d..ef550ce 100644
+--- a/drivers/gpu/drm/nouveau/nv40_graph.c
++++ b/drivers/gpu/drm/nouveau/nv40_graph.c
+@@ -58,6 +58,7 @@ nv40_graph_create_context(struct nouveau_channel *chan)
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
++ struct nouveau_grctx ctx = {};
+ int ret;
+
+ ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pgraph->grctx_size,
+@@ -67,20 +68,13 @@ nv40_graph_create_context(struct nouveau_channel *chan)
+ return ret;
+
+ /* Initialise default context values */
+- dev_priv->engine.instmem.prepare_access(dev, true);
+- if (!pgraph->ctxprog) {
+- struct nouveau_grctx ctx = {};
+-
+- ctx.dev = chan->dev;
+- ctx.mode = NOUVEAU_GRCTX_VALS;
+- ctx.data = chan->ramin_grctx->gpuobj;
+- nv40_grctx_init(&ctx);
+- } else {
+- nouveau_grctx_vals_load(dev, chan->ramin_grctx->gpuobj);
+- }
++ ctx.dev = chan->dev;
++ ctx.mode = NOUVEAU_GRCTX_VALS;
++ ctx.data = chan->ramin_grctx->gpuobj;
++ nv40_grctx_init(&ctx);
++
+ nv_wo32(dev, chan->ramin_grctx->gpuobj, 0,
+ chan->ramin_grctx->gpuobj->im_pramin->start);
+- dev_priv->engine.instmem.finish_access(dev);
+ return 0;
+ }
+
+@@ -238,7 +232,8 @@ nv40_graph_init(struct drm_device *dev)
+ struct drm_nouveau_private *dev_priv =
+ (struct drm_nouveau_private *)dev->dev_private;
+ struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
+- uint32_t vramsz;
++ struct nouveau_grctx ctx = {};
++ uint32_t vramsz, *cp;
+ int i, j;
+
+ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) &
+@@ -246,32 +241,22 @@ nv40_graph_init(struct drm_device *dev)
+ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) |
+ NV_PMC_ENABLE_PGRAPH);
+
+- if (nouveau_ctxfw) {
+- nouveau_grctx_prog_load(dev);
+- dev_priv->engine.graph.grctx_size = 175 * 1024;
+- }
++ cp = kmalloc(sizeof(*cp) * 256, GFP_KERNEL);
++ if (!cp)
++ return -ENOMEM;
+
+- if (!dev_priv->engine.graph.ctxprog) {
+- struct nouveau_grctx ctx = {};
+- uint32_t *cp;
++ ctx.dev = dev;
++ ctx.mode = NOUVEAU_GRCTX_PROG;
++ ctx.data = cp;
++ ctx.ctxprog_max = 256;
++ nv40_grctx_init(&ctx);
++ dev_priv->engine.graph.grctx_size = ctx.ctxvals_pos * 4;
+
+- cp = kmalloc(sizeof(*cp) * 256, GFP_KERNEL);
+- if (!cp)
+- return -ENOMEM;
++ nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0);
++ for (i = 0; i < ctx.ctxprog_len; i++)
++ nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA, cp[i]);
+
+- ctx.dev = dev;
+- ctx.mode = NOUVEAU_GRCTX_PROG;
+- ctx.data = cp;
+- ctx.ctxprog_max = 256;
+- nv40_grctx_init(&ctx);
+- dev_priv->engine.graph.grctx_size = ctx.ctxvals_pos * 4;
+-
+- nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0);
+- for (i = 0; i < ctx.ctxprog_len; i++)
+- nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA, cp[i]);
+-
+- kfree(cp);
+- }
++ kfree(cp);
+
+ /* No context present currently */
+ nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0x00000000);
+@@ -407,7 +392,6 @@ nv40_graph_init(struct drm_device *dev)
+
+ void nv40_graph_takedown(struct drm_device *dev)
+ {
+- nouveau_grctx_fini(dev);
+ }
+
+ struct nouveau_pgraph_object_class nv40_graph_grclass[] = {
+diff --git a/drivers/gpu/drm/nouveau/nv40_mc.c b/drivers/gpu/drm/nouveau/nv40_mc.c
+index 2a3495e..e4e72c1 100644
+--- a/drivers/gpu/drm/nouveau/nv40_mc.c
++++ b/drivers/gpu/drm/nouveau/nv40_mc.c
+@@ -19,7 +19,7 @@ nv40_mc_init(struct drm_device *dev)
+ case 0x46: /* G72 */
+ case 0x4e:
+ case 0x4c: /* C51_G7X */
+- tmp = nv_rd32(dev, NV40_PFB_020C);
++ tmp = nv_rd32(dev, NV04_PFB_FIFO_DATA);
+ nv_wr32(dev, NV40_PMC_1700, tmp);
+ nv_wr32(dev, NV40_PMC_1704, 0);
+ nv_wr32(dev, NV40_PMC_1708, 0);
+diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c
+index b4e4a3b..5d11ea1 100644
+--- a/drivers/gpu/drm/nouveau/nv50_crtc.c
++++ b/drivers/gpu/drm/nouveau/nv50_crtc.c
+@@ -440,47 +440,15 @@ nv50_crtc_prepare(struct drm_crtc *crtc)
+ {
+ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+ struct drm_device *dev = crtc->dev;
+- struct drm_encoder *encoder;
+- uint32_t dac = 0, sor = 0;
+
+ NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
+
+- /* Disconnect all unused encoders. */
+- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+-
+- if (!drm_helper_encoder_in_use(encoder))
+- continue;
+-
+- if (nv_encoder->dcb->type == OUTPUT_ANALOG ||
+- nv_encoder->dcb->type == OUTPUT_TV)
+- dac |= (1 << nv_encoder->or);
+- else
+- sor |= (1 << nv_encoder->or);
+- }
+-
+- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+-
+- if (nv_encoder->dcb->type == OUTPUT_ANALOG ||
+- nv_encoder->dcb->type == OUTPUT_TV) {
+- if (dac & (1 << nv_encoder->or))
+- continue;
+- } else {
+- if (sor & (1 << nv_encoder->or))
+- continue;
+- }
+-
+- nv_encoder->disconnect(nv_encoder);
+- }
+-
+ nv50_crtc_blank(nv_crtc, true);
+ }
+
+ static void
+ nv50_crtc_commit(struct drm_crtc *crtc)
+ {
+- struct drm_crtc *crtc2;
+ struct drm_device *dev = crtc->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_channel *evo = dev_priv->evo;
+@@ -491,20 +459,14 @@ nv50_crtc_commit(struct drm_crtc *crtc)
+
+ nv50_crtc_blank(nv_crtc, false);
+
+- /* Explicitly blank all unused crtc's. */
+- list_for_each_entry(crtc2, &dev->mode_config.crtc_list, head) {
+- if (!drm_helper_crtc_in_use(crtc2))
+- nv50_crtc_blank(nouveau_crtc(crtc2), true);
+- }
+-
+ ret = RING_SPACE(evo, 2);
+ if (ret) {
+ NV_ERROR(dev, "no space while committing crtc\n");
+ return;
+ }
+ BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
+- OUT_RING(evo, 0);
+- FIRE_RING(evo);
++ OUT_RING (evo, 0);
++ FIRE_RING (evo);
+ }
+
+ static bool
+diff --git a/drivers/gpu/drm/nouveau/nv50_dac.c b/drivers/gpu/drm/nouveau/nv50_dac.c
+index 1fd9537..1bc0859 100644
+--- a/drivers/gpu/drm/nouveau/nv50_dac.c
++++ b/drivers/gpu/drm/nouveau/nv50_dac.c
+@@ -37,22 +37,31 @@
+ #include "nv50_display.h"
+
+ static void
+-nv50_dac_disconnect(struct nouveau_encoder *nv_encoder)
++nv50_dac_disconnect(struct drm_encoder *encoder)
+ {
+- struct drm_device *dev = to_drm_encoder(nv_encoder)->dev;
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct drm_device *dev = encoder->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_channel *evo = dev_priv->evo;
+ int ret;
+
++ if (!nv_encoder->crtc)
++ return;
++ nv50_crtc_blank(nouveau_crtc(nv_encoder->crtc), true);
++
+ NV_DEBUG_KMS(dev, "Disconnecting DAC %d\n", nv_encoder->or);
+
+- ret = RING_SPACE(evo, 2);
++ ret = RING_SPACE(evo, 4);
+ if (ret) {
+ NV_ERROR(dev, "no space while disconnecting DAC\n");
+ return;
+ }
+ BEGIN_RING(evo, 0, NV50_EVO_DAC(nv_encoder->or, MODE_CTRL), 1);
+- OUT_RING(evo, 0);
++ OUT_RING (evo, 0);
++ BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
++ OUT_RING (evo, 0);
++
++ nv_encoder->crtc = NULL;
+ }
+
+ static enum drm_connector_status
+@@ -213,7 +222,8 @@ nv50_dac_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
+ uint32_t mode_ctl = 0, mode_ctl2 = 0;
+ int ret;
+
+- NV_DEBUG_KMS(dev, "or %d\n", nv_encoder->or);
++ NV_DEBUG_KMS(dev, "or %d type %d crtc %d\n",
++ nv_encoder->or, nv_encoder->dcb->type, crtc->index);
+
+ nv50_dac_dpms(encoder, DRM_MODE_DPMS_ON);
+
+@@ -243,6 +253,14 @@ nv50_dac_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
+ BEGIN_RING(evo, 0, NV50_EVO_DAC(nv_encoder->or, MODE_CTRL), 2);
+ OUT_RING(evo, mode_ctl);
+ OUT_RING(evo, mode_ctl2);
++
++ nv_encoder->crtc = encoder->crtc;
++}
++
++static struct drm_crtc *
++nv50_dac_crtc_get(struct drm_encoder *encoder)
++{
++ return nouveau_encoder(encoder)->crtc;
+ }
+
+ static const struct drm_encoder_helper_funcs nv50_dac_helper_funcs = {
+@@ -253,7 +271,9 @@ static const struct drm_encoder_helper_funcs nv50_dac_helper_funcs = {
+ .prepare = nv50_dac_prepare,
+ .commit = nv50_dac_commit,
+ .mode_set = nv50_dac_mode_set,
+- .detect = nv50_dac_detect
++ .get_crtc = nv50_dac_crtc_get,
++ .detect = nv50_dac_detect,
++ .disable = nv50_dac_disconnect
+ };
+
+ static void
+@@ -275,14 +295,11 @@ static const struct drm_encoder_funcs nv50_dac_encoder_funcs = {
+ };
+
+ int
+-nv50_dac_create(struct drm_device *dev, struct dcb_entry *entry)
++nv50_dac_create(struct drm_connector *connector, struct dcb_entry *entry)
+ {
+ struct nouveau_encoder *nv_encoder;
+ struct drm_encoder *encoder;
+
+- NV_DEBUG_KMS(dev, "\n");
+- NV_INFO(dev, "Detected a DAC output\n");
+-
+ nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
+ if (!nv_encoder)
+ return -ENOMEM;
+@@ -291,14 +308,14 @@ nv50_dac_create(struct drm_device *dev, struct dcb_entry *entry)
+ nv_encoder->dcb = entry;
+ nv_encoder->or = ffs(entry->or) - 1;
+
+- nv_encoder->disconnect = nv50_dac_disconnect;
+-
+- drm_encoder_init(dev, encoder, &nv50_dac_encoder_funcs,
++ drm_encoder_init(connector->dev, encoder, &nv50_dac_encoder_funcs,
+ DRM_MODE_ENCODER_DAC);
+ drm_encoder_helper_add(encoder, &nv50_dac_helper_funcs);
+
+ encoder->possible_crtcs = entry->heads;
+ encoder->possible_clones = 0;
++
++ drm_mode_connector_attach_encoder(connector, encoder);
+ return 0;
+ }
+
+diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
+index 580a5d1..c19ed8c 100644
+--- a/drivers/gpu/drm/nouveau/nv50_display.c
++++ b/drivers/gpu/drm/nouveau/nv50_display.c
+@@ -71,14 +71,13 @@ nv50_evo_dmaobj_new(struct nouveau_channel *evo, uint32_t class, uint32_t name,
+ return ret;
+ }
+
+- dev_priv->engine.instmem.prepare_access(dev, true);
+ nv_wo32(dev, obj, 0, (tile_flags << 22) | (magic_flags << 16) | class);
+ nv_wo32(dev, obj, 1, limit);
+ nv_wo32(dev, obj, 2, offset);
+ nv_wo32(dev, obj, 3, 0x00000000);
+ nv_wo32(dev, obj, 4, 0x00000000);
+ nv_wo32(dev, obj, 5, 0x00010000);
+- dev_priv->engine.instmem.finish_access(dev);
++ dev_priv->engine.instmem.flush(dev);
+
+ return 0;
+ }
+@@ -110,8 +109,8 @@ nv50_evo_channel_new(struct drm_device *dev, struct nouveau_channel **pchan)
+ return ret;
+ }
+
+- ret = nouveau_mem_init_heap(&chan->ramin_heap, chan->ramin->gpuobj->
+- im_pramin->start, 32768);
++ ret = drm_mm_init(&chan->ramin_heap,
++ chan->ramin->gpuobj->im_pramin->start, 32768);
+ if (ret) {
+ NV_ERROR(dev, "Error initialising EVO PRAMIN heap: %d\n", ret);
+ nv50_evo_channel_del(pchan);
+@@ -465,6 +464,7 @@ int nv50_display_create(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct dcb_table *dcb = &dev_priv->vbios.dcb;
++ struct drm_connector *connector, *ct;
+ int ret, i;
+
+ NV_DEBUG_KMS(dev, "\n");
+@@ -507,14 +507,18 @@ int nv50_display_create(struct drm_device *dev)
+ continue;
+ }
+
++ connector = nouveau_connector_create(dev, entry->connector);
++ if (IS_ERR(connector))
++ continue;
++
+ switch (entry->type) {
+ case OUTPUT_TMDS:
+ case OUTPUT_LVDS:
+ case OUTPUT_DP:
+- nv50_sor_create(dev, entry);
++ nv50_sor_create(connector, entry);
+ break;
+ case OUTPUT_ANALOG:
+- nv50_dac_create(dev, entry);
++ nv50_dac_create(connector, entry);
+ break;
+ default:
+ NV_WARN(dev, "DCB encoder %d unknown\n", entry->type);
+@@ -522,11 +526,13 @@ int nv50_display_create(struct drm_device *dev)
+ }
+ }
+
+- for (i = 0 ; i < dcb->connector.entries; i++) {
+- if (i != 0 && dcb->connector.entry[i].index2 ==
+- dcb->connector.entry[i - 1].index2)
+- continue;
+- nouveau_connector_create(dev, &dcb->connector.entry[i]);
++ list_for_each_entry_safe(connector, ct,
++ &dev->mode_config.connector_list, head) {
++ if (!connector->encoder_ids[0]) {
++ NV_WARN(dev, "%s has no encoders, removing\n",
++ drm_get_connector_name(connector));
++ connector->funcs->destroy(connector);
++ }
+ }
+
+ ret = nv50_display_init(dev);
+@@ -552,131 +558,28 @@ int nv50_display_destroy(struct drm_device *dev)
+ return 0;
+ }
+
+-static inline uint32_t
+-nv50_display_mode_ctrl(struct drm_device *dev, bool sor, int or)
+-{
+- struct drm_nouveau_private *dev_priv = dev->dev_private;
+- uint32_t mc;
+-
+- if (sor) {
+- if (dev_priv->chipset < 0x90 ||
+- dev_priv->chipset == 0x92 || dev_priv->chipset == 0xa0)
+- mc = nv_rd32(dev, NV50_PDISPLAY_SOR_MODE_CTRL_P(or));
+- else
+- mc = nv_rd32(dev, NV90_PDISPLAY_SOR_MODE_CTRL_P(or));
+- } else {
+- mc = nv_rd32(dev, NV50_PDISPLAY_DAC_MODE_CTRL_P(or));
+- }
+-
+- return mc;
+-}
+-
+-static int
+-nv50_display_irq_head(struct drm_device *dev, int *phead,
+- struct dcb_entry **pdcbent)
+-{
+- struct drm_nouveau_private *dev_priv = dev->dev_private;
+- uint32_t unk30 = nv_rd32(dev, NV50_PDISPLAY_UNK30_CTRL);
+- uint32_t dac = 0, sor = 0;
+- int head, i, or = 0, type = OUTPUT_ANY;
+-
+- /* We're assuming that head 0 *or* head 1 will be active here,
+- * and not both. I'm not sure if the hw will even signal both
+- * ever, but it definitely shouldn't for us as we commit each
+- * CRTC separately, and submission will be blocked by the GPU
+- * until we handle each in turn.
+- */
+- NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30);
+- head = ffs((unk30 >> 9) & 3) - 1;
+- if (head < 0)
+- return -EINVAL;
+-
+- /* This assumes CRTCs are never bound to multiple encoders, which
+- * should be the case.
+- */
+- for (i = 0; i < 3 && type == OUTPUT_ANY; i++) {
+- uint32_t mc = nv50_display_mode_ctrl(dev, false, i);
+- if (!(mc & (1 << head)))
+- continue;
+-
+- switch ((mc >> 8) & 0xf) {
+- case 0: type = OUTPUT_ANALOG; break;
+- case 1: type = OUTPUT_TV; break;
+- default:
+- NV_ERROR(dev, "unknown dac mode_ctrl: 0x%08x\n", dac);
+- return -1;
+- }
+-
+- or = i;
+- }
+-
+- for (i = 0; i < 4 && type == OUTPUT_ANY; i++) {
+- uint32_t mc = nv50_display_mode_ctrl(dev, true, i);
+- if (!(mc & (1 << head)))
+- continue;
+-
+- switch ((mc >> 8) & 0xf) {
+- case 0: type = OUTPUT_LVDS; break;
+- case 1: type = OUTPUT_TMDS; break;
+- case 2: type = OUTPUT_TMDS; break;
+- case 5: type = OUTPUT_TMDS; break;
+- case 8: type = OUTPUT_DP; break;
+- case 9: type = OUTPUT_DP; break;
+- default:
+- NV_ERROR(dev, "unknown sor mode_ctrl: 0x%08x\n", sor);
+- return -1;
+- }
+-
+- or = i;
+- }
+-
+- NV_DEBUG_KMS(dev, "type %d, or %d\n", type, or);
+- if (type == OUTPUT_ANY) {
+- NV_ERROR(dev, "unknown encoder!!\n");
+- return -1;
+- }
+-
+- for (i = 0; i < dev_priv->vbios.dcb.entries; i++) {
+- struct dcb_entry *dcbent = &dev_priv->vbios.dcb.entry[i];
+-
+- if (dcbent->type != type)
+- continue;
+-
+- if (!(dcbent->or & (1 << or)))
+- continue;
+-
+- *phead = head;
+- *pdcbent = dcbent;
+- return 0;
+- }
+-
+- NV_ERROR(dev, "no DCB entry for %d %d\n", dac != 0, or);
+- return 0;
+-}
+-
+-static uint32_t
+-nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcbent,
+- int pxclk)
++static u16
++nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcb,
++ u32 mc, int pxclk)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_connector *nv_connector = NULL;
+ struct drm_encoder *encoder;
+ struct nvbios *bios = &dev_priv->vbios;
+- uint32_t mc, script = 0, or;
++ u32 script = 0, or;
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+- if (nv_encoder->dcb != dcbent)
++ if (nv_encoder->dcb != dcb)
+ continue;
+
+ nv_connector = nouveau_encoder_connector_get(nv_encoder);
+ break;
+ }
+
+- or = ffs(dcbent->or) - 1;
+- mc = nv50_display_mode_ctrl(dev, dcbent->type != OUTPUT_ANALOG, or);
+- switch (dcbent->type) {
++ or = ffs(dcb->or) - 1;
++ switch (dcb->type) {
+ case OUTPUT_LVDS:
+ script = (mc >> 8) & 0xf;
+ if (bios->fp_no_ddc) {
+@@ -767,17 +670,88 @@ nv50_display_vblank_handler(struct drm_device *dev, uint32_t intr)
+ static void
+ nv50_display_unk10_handler(struct drm_device *dev)
+ {
+- struct dcb_entry *dcbent;
+- int head, ret;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ u32 unk30 = nv_rd32(dev, 0x610030), mc;
++ int i, crtc, or, type = OUTPUT_ANY;
+
+- ret = nv50_display_irq_head(dev, &head, &dcbent);
+- if (ret)
+- goto ack;
++ NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30);
++ dev_priv->evo_irq.dcb = NULL;
+
+ nv_wr32(dev, 0x619494, nv_rd32(dev, 0x619494) & ~8);
+
+- nouveau_bios_run_display_table(dev, dcbent, 0, -1);
++ /* Determine which CRTC we're dealing with, only 1 ever will be
++ * signalled at the same time with the current nouveau code.
++ */
++ crtc = ffs((unk30 & 0x00000060) >> 5) - 1;
++ if (crtc < 0)
++ goto ack;
++
++ /* Nothing needs to be done for the encoder */
++ crtc = ffs((unk30 & 0x00000180) >> 7) - 1;
++ if (crtc < 0)
++ goto ack;
++
++ /* Find which encoder was connected to the CRTC */
++ for (i = 0; type == OUTPUT_ANY && i < 3; i++) {
++ mc = nv_rd32(dev, NV50_PDISPLAY_DAC_MODE_CTRL_C(i));
++ NV_DEBUG_KMS(dev, "DAC-%d mc: 0x%08x\n", i, mc);
++ if (!(mc & (1 << crtc)))
++ continue;
++
++ switch ((mc & 0x00000f00) >> 8) {
++ case 0: type = OUTPUT_ANALOG; break;
++ case 1: type = OUTPUT_TV; break;
++ default:
++ NV_ERROR(dev, "invalid mc, DAC-%d: 0x%08x\n", i, mc);
++ goto ack;
++ }
++
++ or = i;
++ }
++
++ for (i = 0; type == OUTPUT_ANY && i < 4; i++) {
++ if (dev_priv->chipset < 0x90 ||
++ dev_priv->chipset == 0x92 ||
++ dev_priv->chipset == 0xa0)
++ mc = nv_rd32(dev, NV50_PDISPLAY_SOR_MODE_CTRL_C(i));
++ else
++ mc = nv_rd32(dev, NV90_PDISPLAY_SOR_MODE_CTRL_C(i));
+
++ NV_DEBUG_KMS(dev, "SOR-%d mc: 0x%08x\n", i, mc);
++ if (!(mc & (1 << crtc)))
++ continue;
++
++ switch ((mc & 0x00000f00) >> 8) {
++ case 0: type = OUTPUT_LVDS; break;
++ case 1: type = OUTPUT_TMDS; break;
++ case 2: type = OUTPUT_TMDS; break;
++ case 5: type = OUTPUT_TMDS; break;
++ case 8: type = OUTPUT_DP; break;
++ case 9: type = OUTPUT_DP; break;
++ default:
++ NV_ERROR(dev, "invalid mc, SOR-%d: 0x%08x\n", i, mc);
++ goto ack;
++ }
++
++ or = i;
++ }
++
++ /* There was no encoder to disable */
++ if (type == OUTPUT_ANY)
++ goto ack;
++
++ /* Disable the encoder */
++ for (i = 0; i < dev_priv->vbios.dcb.entries; i++) {
++ struct dcb_entry *dcb = &dev_priv->vbios.dcb.entry[i];
++
++ if (dcb->type == type && (dcb->or & (1 << or))) {
++ nouveau_bios_run_display_table(dev, dcb, 0, -1);
++ dev_priv->evo_irq.dcb = dcb;
++ goto ack;
++ }
++ }
++
++ NV_ERROR(dev, "no dcb for %d %d 0x%08x\n", or, type, mc);
+ ack:
+ nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK10);
+ nv_wr32(dev, 0x610030, 0x80000000);
+@@ -817,33 +791,103 @@ nv50_display_unk20_dp_hack(struct drm_device *dev, struct dcb_entry *dcb)
+ static void
+ nv50_display_unk20_handler(struct drm_device *dev)
+ {
+- struct dcb_entry *dcbent;
+- uint32_t tmp, pclk, script;
+- int head, or, ret;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ u32 unk30 = nv_rd32(dev, 0x610030), tmp, pclk, script, mc;
++ struct dcb_entry *dcb;
++ int i, crtc, or, type = OUTPUT_ANY;
+
+- ret = nv50_display_irq_head(dev, &head, &dcbent);
+- if (ret)
++ NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30);
++ dcb = dev_priv->evo_irq.dcb;
++ if (dcb) {
++ nouveau_bios_run_display_table(dev, dcb, 0, -2);
++ dev_priv->evo_irq.dcb = NULL;
++ }
++
++ /* CRTC clock change requested? */
++ crtc = ffs((unk30 & 0x00000600) >> 9) - 1;
++ if (crtc >= 0) {
++ pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(crtc, CLOCK));
++ pclk &= 0x003fffff;
++
++ nv50_crtc_set_clock(dev, crtc, pclk);
++
++ tmp = nv_rd32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(crtc));
++ tmp &= ~0x000000f;
++ nv_wr32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(crtc), tmp);
++ }
++
++ /* Nothing needs to be done for the encoder */
++ crtc = ffs((unk30 & 0x00000180) >> 7) - 1;
++ if (crtc < 0)
+ goto ack;
+- or = ffs(dcbent->or) - 1;
+- pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(head, CLOCK)) & 0x3fffff;
+- script = nv50_display_script_select(dev, dcbent, pclk);
++ pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(crtc, CLOCK)) & 0x003fffff;
+
+- NV_DEBUG_KMS(dev, "head %d pxclk: %dKHz\n", head, pclk);
++ /* Find which encoder is connected to the CRTC */
++ for (i = 0; type == OUTPUT_ANY && i < 3; i++) {
++ mc = nv_rd32(dev, NV50_PDISPLAY_DAC_MODE_CTRL_P(i));
++ NV_DEBUG_KMS(dev, "DAC-%d mc: 0x%08x\n", i, mc);
++ if (!(mc & (1 << crtc)))
++ continue;
+
+- if (dcbent->type != OUTPUT_DP)
+- nouveau_bios_run_display_table(dev, dcbent, 0, -2);
++ switch ((mc & 0x00000f00) >> 8) {
++ case 0: type = OUTPUT_ANALOG; break;
++ case 1: type = OUTPUT_TV; break;
++ default:
++ NV_ERROR(dev, "invalid mc, DAC-%d: 0x%08x\n", i, mc);
++ goto ack;
++ }
+
+- nv50_crtc_set_clock(dev, head, pclk);
++ or = i;
++ }
+
+- nouveau_bios_run_display_table(dev, dcbent, script, pclk);
++ for (i = 0; type == OUTPUT_ANY && i < 4; i++) {
++ if (dev_priv->chipset < 0x90 ||
++ dev_priv->chipset == 0x92 ||
++ dev_priv->chipset == 0xa0)
++ mc = nv_rd32(dev, NV50_PDISPLAY_SOR_MODE_CTRL_P(i));
++ else
++ mc = nv_rd32(dev, NV90_PDISPLAY_SOR_MODE_CTRL_P(i));
+
+- nv50_display_unk20_dp_hack(dev, dcbent);
++ NV_DEBUG_KMS(dev, "SOR-%d mc: 0x%08x\n", i, mc);
++ if (!(mc & (1 << crtc)))
++ continue;
+
+- tmp = nv_rd32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(head));
+- tmp &= ~0x000000f;
+- nv_wr32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(head), tmp);
++ switch ((mc & 0x00000f00) >> 8) {
++ case 0: type = OUTPUT_LVDS; break;
++ case 1: type = OUTPUT_TMDS; break;
++ case 2: type = OUTPUT_TMDS; break;
++ case 5: type = OUTPUT_TMDS; break;
++ case 8: type = OUTPUT_DP; break;
++ case 9: type = OUTPUT_DP; break;
++ default:
++ NV_ERROR(dev, "invalid mc, SOR-%d: 0x%08x\n", i, mc);
++ goto ack;
++ }
++
++ or = i;
++ }
++
++ if (type == OUTPUT_ANY)
++ goto ack;
++
++ /* Enable the encoder */
++ for (i = 0; i < dev_priv->vbios.dcb.entries; i++) {
++ dcb = &dev_priv->vbios.dcb.entry[i];
++ if (dcb->type == type && (dcb->or & (1 << or)))
++ break;
++ }
++
++ if (i == dev_priv->vbios.dcb.entries) {
++ NV_ERROR(dev, "no dcb for %d %d 0x%08x\n", or, type, mc);
++ goto ack;
++ }
++
++ script = nv50_display_script_select(dev, dcb, mc, pclk);
++ nouveau_bios_run_display_table(dev, dcb, script, pclk);
++
++ nv50_display_unk20_dp_hack(dev, dcb);
+
+- if (dcbent->type != OUTPUT_ANALOG) {
++ if (dcb->type != OUTPUT_ANALOG) {
+ tmp = nv_rd32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or));
+ tmp &= ~0x00000f0f;
+ if (script & 0x0100)
+@@ -853,24 +897,61 @@ nv50_display_unk20_handler(struct drm_device *dev)
+ nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL2(or), 0);
+ }
+
++ dev_priv->evo_irq.dcb = dcb;
++ dev_priv->evo_irq.pclk = pclk;
++ dev_priv->evo_irq.script = script;
++
+ ack:
+ nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK20);
+ nv_wr32(dev, 0x610030, 0x80000000);
+ }
+
++/* If programming a TMDS output on a SOR that can also be configured for
++ * DisplayPort, make sure NV50_SOR_DP_CTRL_ENABLE is forced off.
++ *
++ * It looks like the VBIOS TMDS scripts make an attempt at this, however,
++ * the VBIOS scripts on at least one board I have only switch it off on
++ * link 0, causing a blank display if the output has previously been
++ * programmed for DisplayPort.
++ */
++static void
++nv50_display_unk40_dp_set_tmds(struct drm_device *dev, struct dcb_entry *dcb)
++{
++ int or = ffs(dcb->or) - 1, link = !(dcb->dpconf.sor.link & 1);
++ struct drm_encoder *encoder;
++ u32 tmp;
++
++ if (dcb->type != OUTPUT_TMDS)
++ return;
++
++ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++
++ if (nv_encoder->dcb->type == OUTPUT_DP &&
++ nv_encoder->dcb->or & (1 << or)) {
++ tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
++ tmp &= ~NV50_SOR_DP_CTRL_ENABLED;
++ nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp);
++ break;
++ }
++ }
++}
++
+ static void
+ nv50_display_unk40_handler(struct drm_device *dev)
+ {
+- struct dcb_entry *dcbent;
+- int head, pclk, script, ret;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct dcb_entry *dcb = dev_priv->evo_irq.dcb;
++ u16 script = dev_priv->evo_irq.script;
++ u32 unk30 = nv_rd32(dev, 0x610030), pclk = dev_priv->evo_irq.pclk;
+
+- ret = nv50_display_irq_head(dev, &head, &dcbent);
+- if (ret)
++ NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30);
++ dev_priv->evo_irq.dcb = NULL;
++ if (!dcb)
+ goto ack;
+- pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(head, CLOCK)) & 0x3fffff;
+- script = nv50_display_script_select(dev, dcbent, pclk);
+
+- nouveau_bios_run_display_table(dev, dcbent, script, -pclk);
++ nouveau_bios_run_display_table(dev, dcb, script, -pclk);
++ nv50_display_unk40_dp_set_tmds(dev, dcb);
+
+ ack:
+ nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK40);
+diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c
+index e20c0e2..fb0281a 100644
+--- a/drivers/gpu/drm/nouveau/nv50_fifo.c
++++ b/drivers/gpu/drm/nouveau/nv50_fifo.c
+@@ -28,41 +28,33 @@
+ #include "drm.h"
+ #include "nouveau_drv.h"
+
+-struct nv50_fifo_priv {
+- struct nouveau_gpuobj_ref *thingo[2];
+- int cur_thingo;
+-};
+-
+-#define IS_G80 ((dev_priv->chipset & 0xf0) == 0x50)
+-
+ static void
+-nv50_fifo_init_thingo(struct drm_device *dev)
++nv50_fifo_playlist_update(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nv50_fifo_priv *priv = dev_priv->engine.fifo.priv;
++ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
+ struct nouveau_gpuobj_ref *cur;
+ int i, nr;
+
+ NV_DEBUG(dev, "\n");
+
+- cur = priv->thingo[priv->cur_thingo];
+- priv->cur_thingo = !priv->cur_thingo;
++ cur = pfifo->playlist[pfifo->cur_playlist];
++ pfifo->cur_playlist = !pfifo->cur_playlist;
+
+ /* We never schedule channel 0 or 127 */
+- dev_priv->engine.instmem.prepare_access(dev, true);
+ for (i = 1, nr = 0; i < 127; i++) {
+ if (dev_priv->fifos[i] && dev_priv->fifos[i]->ramfc)
+ nv_wo32(dev, cur->gpuobj, nr++, i);
+ }
+- dev_priv->engine.instmem.finish_access(dev);
++ dev_priv->engine.instmem.flush(dev);
+
+ nv_wr32(dev, 0x32f4, cur->instance >> 12);
+ nv_wr32(dev, 0x32ec, nr);
+ nv_wr32(dev, 0x2500, 0x101);
+ }
+
+-static int
+-nv50_fifo_channel_enable(struct drm_device *dev, int channel, bool nt)
++static void
++nv50_fifo_channel_enable(struct drm_device *dev, int channel)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_channel *chan = dev_priv->fifos[channel];
+@@ -70,37 +62,28 @@ nv50_fifo_channel_enable(struct drm_device *dev, int channel, bool nt)
+
+ NV_DEBUG(dev, "ch%d\n", channel);
+
+- if (!chan->ramfc)
+- return -EINVAL;
+-
+- if (IS_G80)
++ if (dev_priv->chipset == 0x50)
+ inst = chan->ramfc->instance >> 12;
+ else
+ inst = chan->ramfc->instance >> 8;
+- nv_wr32(dev, NV50_PFIFO_CTX_TABLE(channel),
+- inst | NV50_PFIFO_CTX_TABLE_CHANNEL_ENABLED);
+
+- if (!nt)
+- nv50_fifo_init_thingo(dev);
+- return 0;
++ nv_wr32(dev, NV50_PFIFO_CTX_TABLE(channel), inst |
++ NV50_PFIFO_CTX_TABLE_CHANNEL_ENABLED);
+ }
+
+ static void
+-nv50_fifo_channel_disable(struct drm_device *dev, int channel, bool nt)
++nv50_fifo_channel_disable(struct drm_device *dev, int channel)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ uint32_t inst;
+
+- NV_DEBUG(dev, "ch%d, nt=%d\n", channel, nt);
++ NV_DEBUG(dev, "ch%d\n", channel);
+
+- if (IS_G80)
++ if (dev_priv->chipset == 0x50)
+ inst = NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G80;
+ else
+ inst = NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G84;
+ nv_wr32(dev, NV50_PFIFO_CTX_TABLE(channel), inst);
+-
+- if (!nt)
+- nv50_fifo_init_thingo(dev);
+ }
+
+ static void
+@@ -133,12 +116,12 @@ nv50_fifo_init_context_table(struct drm_device *dev)
+
+ for (i = 0; i < NV50_PFIFO_CTX_TABLE__SIZE; i++) {
+ if (dev_priv->fifos[i])
+- nv50_fifo_channel_enable(dev, i, true);
++ nv50_fifo_channel_enable(dev, i);
+ else
+- nv50_fifo_channel_disable(dev, i, true);
++ nv50_fifo_channel_disable(dev, i);
+ }
+
+- nv50_fifo_init_thingo(dev);
++ nv50_fifo_playlist_update(dev);
+ }
+
+ static void
+@@ -162,41 +145,38 @@ nv50_fifo_init_regs(struct drm_device *dev)
+ nv_wr32(dev, 0x3270, 0);
+
+ /* Enable dummy channels setup by nv50_instmem.c */
+- nv50_fifo_channel_enable(dev, 0, true);
+- nv50_fifo_channel_enable(dev, 127, true);
++ nv50_fifo_channel_enable(dev, 0);
++ nv50_fifo_channel_enable(dev, 127);
+ }
+
+ int
+ nv50_fifo_init(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nv50_fifo_priv *priv;
++ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
+ int ret;
+
+ NV_DEBUG(dev, "\n");
+
+- priv = dev_priv->engine.fifo.priv;
+- if (priv) {
+- priv->cur_thingo = !priv->cur_thingo;
++ if (pfifo->playlist[0]) {
++ pfifo->cur_playlist = !pfifo->cur_playlist;
+ goto just_reset;
+ }
+
+- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+- if (!priv)
+- return -ENOMEM;
+- dev_priv->engine.fifo.priv = priv;
+-
+ ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 128*4, 0x1000,
+- NVOBJ_FLAG_ZERO_ALLOC, &priv->thingo[0]);
++ NVOBJ_FLAG_ZERO_ALLOC,
++ &pfifo->playlist[0]);
+ if (ret) {
+- NV_ERROR(dev, "error creating thingo0: %d\n", ret);
++ NV_ERROR(dev, "error creating playlist 0: %d\n", ret);
+ return ret;
+ }
+
+ ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 128*4, 0x1000,
+- NVOBJ_FLAG_ZERO_ALLOC, &priv->thingo[1]);
++ NVOBJ_FLAG_ZERO_ALLOC,
++ &pfifo->playlist[1]);
+ if (ret) {
+- NV_ERROR(dev, "error creating thingo1: %d\n", ret);
++ nouveau_gpuobj_ref_del(dev, &pfifo->playlist[0]);
++ NV_ERROR(dev, "error creating playlist 1: %d\n", ret);
+ return ret;
+ }
+
+@@ -216,18 +196,15 @@ void
+ nv50_fifo_takedown(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nv50_fifo_priv *priv = dev_priv->engine.fifo.priv;
++ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
+
+ NV_DEBUG(dev, "\n");
+
+- if (!priv)
++ if (!pfifo->playlist[0])
+ return;
+
+- nouveau_gpuobj_ref_del(dev, &priv->thingo[0]);
+- nouveau_gpuobj_ref_del(dev, &priv->thingo[1]);
+-
+- dev_priv->engine.fifo.priv = NULL;
+- kfree(priv);
++ nouveau_gpuobj_ref_del(dev, &pfifo->playlist[0]);
++ nouveau_gpuobj_ref_del(dev, &pfifo->playlist[1]);
+ }
+
+ int
+@@ -248,7 +225,7 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
+
+ NV_DEBUG(dev, "ch%d\n", chan->id);
+
+- if (IS_G80) {
++ if (dev_priv->chipset == 0x50) {
+ uint32_t ramin_poffset = chan->ramin->gpuobj->im_pramin->start;
+ uint32_t ramin_voffset = chan->ramin->gpuobj->im_backing_start;
+
+@@ -281,10 +258,10 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
+
+ spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
+
+- dev_priv->engine.instmem.prepare_access(dev, true);
+-
+ nv_wo32(dev, ramfc, 0x48/4, chan->pushbuf->instance >> 4);
+- nv_wo32(dev, ramfc, 0x80/4, (0xc << 24) | (chan->ramht->instance >> 4));
++ nv_wo32(dev, ramfc, 0x80/4, (0 << 27) /* 4KiB */ |
++ (4 << 24) /* SEARCH_FULL */ |
++ (chan->ramht->instance >> 4));
+ nv_wo32(dev, ramfc, 0x44/4, 0x2101ffff);
+ nv_wo32(dev, ramfc, 0x60/4, 0x7fffffff);
+ nv_wo32(dev, ramfc, 0x40/4, 0x00000000);
+@@ -295,7 +272,7 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
+ chan->dma.ib_base * 4);
+ nv_wo32(dev, ramfc, 0x54/4, drm_order(chan->dma.ib_max + 1) << 16);
+
+- if (!IS_G80) {
++ if (dev_priv->chipset != 0x50) {
+ nv_wo32(dev, chan->ramin->gpuobj, 0, chan->id);
+ nv_wo32(dev, chan->ramin->gpuobj, 1,
+ chan->ramfc->instance >> 8);
+@@ -304,16 +281,10 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
+ nv_wo32(dev, ramfc, 0x98/4, chan->ramin->instance >> 12);
+ }
+
+- dev_priv->engine.instmem.finish_access(dev);
+-
+- ret = nv50_fifo_channel_enable(dev, chan->id, false);
+- if (ret) {
+- NV_ERROR(dev, "error enabling ch%d: %d\n", chan->id, ret);
+- spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
+- nouveau_gpuobj_ref_del(dev, &chan->ramfc);
+- return ret;
+- }
++ dev_priv->engine.instmem.flush(dev);
+
++ nv50_fifo_channel_enable(dev, chan->id);
++ nv50_fifo_playlist_update(dev);
+ spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
+ return 0;
+ }
+@@ -328,11 +299,12 @@ nv50_fifo_destroy_context(struct nouveau_channel *chan)
+
+ /* This will ensure the channel is seen as disabled. */
+ chan->ramfc = NULL;
+- nv50_fifo_channel_disable(dev, chan->id, false);
++ nv50_fifo_channel_disable(dev, chan->id);
+
+ /* Dummy channel, also used on ch 127 */
+ if (chan->id == 0)
+- nv50_fifo_channel_disable(dev, 127, false);
++ nv50_fifo_channel_disable(dev, 127);
++ nv50_fifo_playlist_update(dev);
+
+ nouveau_gpuobj_ref_del(dev, &ramfc);
+ nouveau_gpuobj_ref_del(dev, &chan->cache);
+@@ -349,8 +321,6 @@ nv50_fifo_load_context(struct nouveau_channel *chan)
+
+ NV_DEBUG(dev, "ch%d\n", chan->id);
+
+- dev_priv->engine.instmem.prepare_access(dev, false);
+-
+ nv_wr32(dev, 0x3330, nv_ro32(dev, ramfc, 0x00/4));
+ nv_wr32(dev, 0x3334, nv_ro32(dev, ramfc, 0x04/4));
+ nv_wr32(dev, 0x3240, nv_ro32(dev, ramfc, 0x08/4));
+@@ -396,7 +366,7 @@ nv50_fifo_load_context(struct nouveau_channel *chan)
+ nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
+
+ /* guessing that all the 0x34xx regs aren't on NV50 */
+- if (!IS_G80) {
++ if (dev_priv->chipset != 0x50) {
+ nv_wr32(dev, 0x340c, nv_ro32(dev, ramfc, 0x88/4));
+ nv_wr32(dev, 0x3400, nv_ro32(dev, ramfc, 0x8c/4));
+ nv_wr32(dev, 0x3404, nv_ro32(dev, ramfc, 0x90/4));
+@@ -404,8 +374,6 @@ nv50_fifo_load_context(struct nouveau_channel *chan)
+ nv_wr32(dev, 0x3410, nv_ro32(dev, ramfc, 0x98/4));
+ }
+
+- dev_priv->engine.instmem.finish_access(dev);
+-
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, chan->id | (1<<16));
+ return 0;
+ }
+@@ -434,8 +402,6 @@ nv50_fifo_unload_context(struct drm_device *dev)
+ ramfc = chan->ramfc->gpuobj;
+ cache = chan->cache->gpuobj;
+
+- dev_priv->engine.instmem.prepare_access(dev, true);
+-
+ nv_wo32(dev, ramfc, 0x00/4, nv_rd32(dev, 0x3330));
+ nv_wo32(dev, ramfc, 0x04/4, nv_rd32(dev, 0x3334));
+ nv_wo32(dev, ramfc, 0x08/4, nv_rd32(dev, 0x3240));
+@@ -482,7 +448,7 @@ nv50_fifo_unload_context(struct drm_device *dev)
+ }
+
+ /* guessing that all the 0x34xx regs aren't on NV50 */
+- if (!IS_G80) {
++ if (dev_priv->chipset != 0x50) {
+ nv_wo32(dev, ramfc, 0x84/4, ptr >> 1);
+ nv_wo32(dev, ramfc, 0x88/4, nv_rd32(dev, 0x340c));
+ nv_wo32(dev, ramfc, 0x8c/4, nv_rd32(dev, 0x3400));
+@@ -491,7 +457,7 @@ nv50_fifo_unload_context(struct drm_device *dev)
+ nv_wo32(dev, ramfc, 0x98/4, nv_rd32(dev, 0x3410));
+ }
+
+- dev_priv->engine.instmem.finish_access(dev);
++ dev_priv->engine.instmem.flush(dev);
+
+ /*XXX: probably reload ch127 (NULL) state back too */
+ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, 127);
+diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c
+index b203d06..1413028 100644
+--- a/drivers/gpu/drm/nouveau/nv50_graph.c
++++ b/drivers/gpu/drm/nouveau/nv50_graph.c
+@@ -30,8 +30,6 @@
+
+ #include "nouveau_grctx.h"
+
+-#define IS_G80 ((dev_priv->chipset & 0xf0) == 0x50)
+-
+ static void
+ nv50_graph_init_reset(struct drm_device *dev)
+ {
+@@ -103,37 +101,33 @@ static int
+ nv50_graph_init_ctxctl(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_grctx ctx = {};
++ uint32_t *cp;
++ int i;
+
+ NV_DEBUG(dev, "\n");
+
+- if (nouveau_ctxfw) {
+- nouveau_grctx_prog_load(dev);
+- dev_priv->engine.graph.grctx_size = 0x70000;
++ cp = kmalloc(512 * 4, GFP_KERNEL);
++ if (!cp) {
++ NV_ERROR(dev, "failed to allocate ctxprog\n");
++ dev_priv->engine.graph.accel_blocked = true;
++ return 0;
+ }
+- if (!dev_priv->engine.graph.ctxprog) {
+- struct nouveau_grctx ctx = {};
+- uint32_t *cp = kmalloc(512 * 4, GFP_KERNEL);
+- int i;
+- if (!cp) {
+- NV_ERROR(dev, "Couldn't alloc ctxprog! Disabling acceleration.\n");
+- dev_priv->engine.graph.accel_blocked = true;
+- return 0;
+- }
+- ctx.dev = dev;
+- ctx.mode = NOUVEAU_GRCTX_PROG;
+- ctx.data = cp;
+- ctx.ctxprog_max = 512;
+- if (!nv50_grctx_init(&ctx)) {
+- dev_priv->engine.graph.grctx_size = ctx.ctxvals_pos * 4;
+-
+- nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0);
+- for (i = 0; i < ctx.ctxprog_len; i++)
+- nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA, cp[i]);
+- } else {
+- dev_priv->engine.graph.accel_blocked = true;
+- }
+- kfree(cp);
++
++ ctx.dev = dev;
++ ctx.mode = NOUVEAU_GRCTX_PROG;
++ ctx.data = cp;
++ ctx.ctxprog_max = 512;
++ if (!nv50_grctx_init(&ctx)) {
++ dev_priv->engine.graph.grctx_size = ctx.ctxvals_pos * 4;
++
++ nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0);
++ for (i = 0; i < ctx.ctxprog_len; i++)
++ nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA, cp[i]);
++ } else {
++ dev_priv->engine.graph.accel_blocked = true;
+ }
++ kfree(cp);
+
+ nv_wr32(dev, 0x400320, 4);
+ nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0);
+@@ -164,7 +158,6 @@ void
+ nv50_graph_takedown(struct drm_device *dev)
+ {
+ NV_DEBUG(dev, "\n");
+- nouveau_grctx_fini(dev);
+ }
+
+ void
+@@ -212,8 +205,9 @@ nv50_graph_create_context(struct nouveau_channel *chan)
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_gpuobj *ramin = chan->ramin->gpuobj;
+- struct nouveau_gpuobj *ctx;
++ struct nouveau_gpuobj *obj;
+ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
++ struct nouveau_grctx ctx = {};
+ int hdr, ret;
+
+ NV_DEBUG(dev, "ch%d\n", chan->id);
+@@ -223,10 +217,9 @@ nv50_graph_create_context(struct nouveau_channel *chan)
+ NVOBJ_FLAG_ZERO_FREE, &chan->ramin_grctx);
+ if (ret)
+ return ret;
+- ctx = chan->ramin_grctx->gpuobj;
++ obj = chan->ramin_grctx->gpuobj;
+
+- hdr = IS_G80 ? 0x200 : 0x20;
+- dev_priv->engine.instmem.prepare_access(dev, true);
++ hdr = (dev_priv->chipset == 0x50) ? 0x200 : 0x20;
+ nv_wo32(dev, ramin, (hdr + 0x00)/4, 0x00190002);
+ nv_wo32(dev, ramin, (hdr + 0x04)/4, chan->ramin_grctx->instance +
+ pgraph->grctx_size - 1);
+@@ -234,21 +227,15 @@ nv50_graph_create_context(struct nouveau_channel *chan)
+ nv_wo32(dev, ramin, (hdr + 0x0c)/4, 0);
+ nv_wo32(dev, ramin, (hdr + 0x10)/4, 0);
+ nv_wo32(dev, ramin, (hdr + 0x14)/4, 0x00010000);
+- dev_priv->engine.instmem.finish_access(dev);
+-
+- dev_priv->engine.instmem.prepare_access(dev, true);
+- if (!pgraph->ctxprog) {
+- struct nouveau_grctx ctx = {};
+- ctx.dev = chan->dev;
+- ctx.mode = NOUVEAU_GRCTX_VALS;
+- ctx.data = chan->ramin_grctx->gpuobj;
+- nv50_grctx_init(&ctx);
+- } else {
+- nouveau_grctx_vals_load(dev, ctx);
+- }
+- nv_wo32(dev, ctx, 0x00000/4, chan->ramin->instance >> 12);
+- dev_priv->engine.instmem.finish_access(dev);
+
++ ctx.dev = chan->dev;
++ ctx.mode = NOUVEAU_GRCTX_VALS;
++ ctx.data = obj;
++ nv50_grctx_init(&ctx);
++
++ nv_wo32(dev, obj, 0x00000/4, chan->ramin->instance >> 12);
++
++ dev_priv->engine.instmem.flush(dev);
+ return 0;
+ }
+
+@@ -257,17 +244,16 @@ nv50_graph_destroy_context(struct nouveau_channel *chan)
+ {
+ struct drm_device *dev = chan->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- int i, hdr = IS_G80 ? 0x200 : 0x20;
++ int i, hdr = (dev_priv->chipset == 0x50) ? 0x200 : 0x20;
+
+ NV_DEBUG(dev, "ch%d\n", chan->id);
+
+ if (!chan->ramin || !chan->ramin->gpuobj)
+ return;
+
+- dev_priv->engine.instmem.prepare_access(dev, true);
+ for (i = hdr; i < hdr + 24; i += 4)
+ nv_wo32(dev, chan->ramin->gpuobj, i/4, 0);
+- dev_priv->engine.instmem.finish_access(dev);
++ dev_priv->engine.instmem.flush(dev);
+
+ nouveau_gpuobj_ref_del(dev, &chan->ramin_grctx);
+ }
+diff --git a/drivers/gpu/drm/nouveau/nv50_instmem.c b/drivers/gpu/drm/nouveau/nv50_instmem.c
+index 5f21df3..b7ad258 100644
+--- a/drivers/gpu/drm/nouveau/nv50_instmem.c
++++ b/drivers/gpu/drm/nouveau/nv50_instmem.c
+@@ -35,8 +35,6 @@ struct nv50_instmem_priv {
+ struct nouveau_gpuobj_ref *pramin_pt;
+ struct nouveau_gpuobj_ref *pramin_bar;
+ struct nouveau_gpuobj_ref *fb_bar;
+-
+- bool last_access_wr;
+ };
+
+ #define NV50_INSTMEM_PAGE_SHIFT 12
+@@ -147,7 +145,7 @@ nv50_instmem_init(struct drm_device *dev)
+ if (ret)
+ return ret;
+
+- if (nouveau_mem_init_heap(&chan->ramin_heap, c_base, c_size - c_base))
++ if (drm_mm_init(&chan->ramin_heap, c_base, c_size - c_base))
+ return -ENOMEM;
+
+ /* RAMFC + zero channel's PRAMIN up to start of VM pagedir */
+@@ -262,23 +260,18 @@ nv50_instmem_init(struct drm_device *dev)
+
+ /* Assume that praying isn't enough, check that we can re-read the
+ * entire fake channel back from the PRAMIN BAR */
+- dev_priv->engine.instmem.prepare_access(dev, false);
+ for (i = 0; i < c_size; i += 4) {
+ if (nv_rd32(dev, NV_RAMIN + i) != nv_ri32(dev, i)) {
+ NV_ERROR(dev, "Error reading back PRAMIN at 0x%08x\n",
+ i);
+- dev_priv->engine.instmem.finish_access(dev);
+ return -EINVAL;
+ }
+ }
+- dev_priv->engine.instmem.finish_access(dev);
+
+ nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, save_nv001700);
+
+ /* Global PRAMIN heap */
+- if (nouveau_mem_init_heap(&dev_priv->ramin_heap,
+- c_size, dev_priv->ramin_size - c_size)) {
+- dev_priv->ramin_heap = NULL;
++ if (drm_mm_init(&dev_priv->ramin_heap, c_size, dev_priv->ramin_size - c_size)) {
+ NV_ERROR(dev, "Failed to init RAMIN heap\n");
+ }
+
+@@ -321,7 +314,7 @@ nv50_instmem_takedown(struct drm_device *dev)
+ nouveau_gpuobj_del(dev, &chan->vm_pd);
+ nouveau_gpuobj_ref_del(dev, &chan->ramfc);
+ nouveau_gpuobj_ref_del(dev, &chan->ramin);
+- nouveau_mem_takedown(&chan->ramin_heap);
++ drm_mm_takedown(&chan->ramin_heap);
+
+ dev_priv->fifos[0] = dev_priv->fifos[127] = NULL;
+ kfree(chan);
+@@ -436,14 +429,14 @@ nv50_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
+ if (!gpuobj->im_backing || !gpuobj->im_pramin || gpuobj->im_bound)
+ return -EINVAL;
+
+- NV_DEBUG(dev, "st=0x%0llx sz=0x%0llx\n",
++ NV_DEBUG(dev, "st=0x%lx sz=0x%lx\n",
+ gpuobj->im_pramin->start, gpuobj->im_pramin->size);
+
+ pte = (gpuobj->im_pramin->start >> 12) << 1;
+ pte_end = ((gpuobj->im_pramin->size >> 12) << 1) + pte;
+ vram = gpuobj->im_backing_start;
+
+- NV_DEBUG(dev, "pramin=0x%llx, pte=%d, pte_end=%d\n",
++ NV_DEBUG(dev, "pramin=0x%lx, pte=%d, pte_end=%d\n",
+ gpuobj->im_pramin->start, pte, pte_end);
+ NV_DEBUG(dev, "first vram page: 0x%08x\n", gpuobj->im_backing_start);
+
+@@ -453,27 +446,15 @@ nv50_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
+ vram |= 0x30;
+ }
+
+- dev_priv->engine.instmem.prepare_access(dev, true);
+ while (pte < pte_end) {
+ nv_wo32(dev, pramin_pt, pte++, lower_32_bits(vram));
+ nv_wo32(dev, pramin_pt, pte++, upper_32_bits(vram));
+ vram += NV50_INSTMEM_PAGE_SIZE;
+ }
+- dev_priv->engine.instmem.finish_access(dev);
+-
+- nv_wr32(dev, 0x100c80, 0x00040001);
+- if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+- NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (1)\n");
+- NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
+- return -EBUSY;
+- }
++ dev_priv->engine.instmem.flush(dev);
+
+- nv_wr32(dev, 0x100c80, 0x00060001);
+- if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
+- NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
+- NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
+- return -EBUSY;
+- }
++ nv50_vm_flush(dev, 4);
++ nv50_vm_flush(dev, 6);
+
+ gpuobj->im_bound = 1;
+ return 0;
+@@ -492,36 +473,36 @@ nv50_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
+ pte = (gpuobj->im_pramin->start >> 12) << 1;
+ pte_end = ((gpuobj->im_pramin->size >> 12) << 1) + pte;
+
+- dev_priv->engine.instmem.prepare_access(dev, true);
+ while (pte < pte_end) {
+ nv_wo32(dev, priv->pramin_pt->gpuobj, pte++, 0x00000000);
+ nv_wo32(dev, priv->pramin_pt->gpuobj, pte++, 0x00000000);
+ }
+- dev_priv->engine.instmem.finish_access(dev);
++ dev_priv->engine.instmem.flush(dev);
+
+ gpuobj->im_bound = 0;
+ return 0;
+ }
+
+ void
+-nv50_instmem_prepare_access(struct drm_device *dev, bool write)
++nv50_instmem_flush(struct drm_device *dev)
+ {
+- struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
+-
+- priv->last_access_wr = write;
++ nv_wr32(dev, 0x00330c, 0x00000001);
++ if (!nv_wait(0x00330c, 0x00000001, 0x00000000))
++ NV_ERROR(dev, "PRAMIN flush timeout\n");
+ }
+
+ void
+-nv50_instmem_finish_access(struct drm_device *dev)
++nv84_instmem_flush(struct drm_device *dev)
+ {
+- struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
+-
+- if (priv->last_access_wr) {
+- nv_wr32(dev, 0x070000, 0x00000001);
+- if (!nv_wait(0x070000, 0x00000001, 0x00000000))
+- NV_ERROR(dev, "PRAMIN flush timeout\n");
+- }
++ nv_wr32(dev, 0x070000, 0x00000001);
++ if (!nv_wait(0x070000, 0x00000001, 0x00000000))
++ NV_ERROR(dev, "PRAMIN flush timeout\n");
+ }
+
++void
++nv50_vm_flush(struct drm_device *dev, int engine)
++{
++ nv_wr32(dev, 0x100c80, (engine << 16) | 1);
++ if (!nv_wait(0x100c80, 0x00000001, 0x00000000))
++ NV_ERROR(dev, "vm flush timeout: engine %d\n", engine);
++}
+diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c
+index 812778d..bcd4cf8 100644
+--- a/drivers/gpu/drm/nouveau/nv50_sor.c
++++ b/drivers/gpu/drm/nouveau/nv50_sor.c
+@@ -37,52 +37,32 @@
+ #include "nv50_display.h"
+
+ static void
+-nv50_sor_disconnect(struct nouveau_encoder *nv_encoder)
++nv50_sor_disconnect(struct drm_encoder *encoder)
+ {
+- struct drm_device *dev = to_drm_encoder(nv_encoder)->dev;
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct drm_device *dev = encoder->dev;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_channel *evo = dev_priv->evo;
+ int ret;
+
++ if (!nv_encoder->crtc)
++ return;
++ nv50_crtc_blank(nouveau_crtc(nv_encoder->crtc), true);
++
+ NV_DEBUG_KMS(dev, "Disconnecting SOR %d\n", nv_encoder->or);
+
+- ret = RING_SPACE(evo, 2);
++ ret = RING_SPACE(evo, 4);
+ if (ret) {
+ NV_ERROR(dev, "no space while disconnecting SOR\n");
+ return;
+ }
+ BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1);
+- OUT_RING(evo, 0);
+-}
+-
+-static void
+-nv50_sor_dp_link_train(struct drm_encoder *encoder)
+-{
+- struct drm_device *dev = encoder->dev;
+- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+- struct bit_displayport_encoder_table *dpe;
+- int dpe_headerlen;
+-
+- dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
+- if (!dpe) {
+- NV_ERROR(dev, "SOR-%d: no DP encoder table!\n", nv_encoder->or);
+- return;
+- }
++ OUT_RING (evo, 0);
++ BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
++ OUT_RING (evo, 0);
+
+- if (dpe->script0) {
+- NV_DEBUG_KMS(dev, "SOR-%d: running DP script 0\n", nv_encoder->or);
+- nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script0),
+- nv_encoder->dcb);
+- }
+-
+- if (!nouveau_dp_link_train(encoder))
+- NV_ERROR(dev, "SOR-%d: link training failed\n", nv_encoder->or);
+-
+- if (dpe->script1) {
+- NV_DEBUG_KMS(dev, "SOR-%d: running DP script 1\n", nv_encoder->or);
+- nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script1),
+- nv_encoder->dcb);
+- }
++ nv_encoder->crtc = NULL;
++ nv_encoder->last_dpms = DRM_MODE_DPMS_OFF;
+ }
+
+ static void
+@@ -94,14 +74,16 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode)
+ uint32_t val;
+ int or = nv_encoder->or;
+
+- NV_DEBUG_KMS(dev, "or %d mode %d\n", or, mode);
++ NV_DEBUG_KMS(dev, "or %d type %d mode %d\n", or, nv_encoder->dcb->type, mode);
+
+ nv_encoder->last_dpms = mode;
+ list_for_each_entry(enc, &dev->mode_config.encoder_list, head) {
+ struct nouveau_encoder *nvenc = nouveau_encoder(enc);
+
+ if (nvenc == nv_encoder ||
+- nvenc->disconnect != nv50_sor_disconnect ||
++ (nvenc->dcb->type != OUTPUT_TMDS &&
++ nvenc->dcb->type != OUTPUT_LVDS &&
++ nvenc->dcb->type != OUTPUT_DP) ||
+ nvenc->dcb->or != nv_encoder->dcb->or)
+ continue;
+
+@@ -133,8 +115,22 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode)
+ nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_STATE(or)));
+ }
+
+- if (nv_encoder->dcb->type == OUTPUT_DP && mode == DRM_MODE_DPMS_ON)
+- nv50_sor_dp_link_train(encoder);
++ if (nv_encoder->dcb->type == OUTPUT_DP) {
++ struct nouveau_i2c_chan *auxch;
++
++ auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
++ if (!auxch)
++ return;
++
++ if (mode == DRM_MODE_DPMS_ON) {
++ u8 status = DP_SET_POWER_D0;
++ nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1);
++ nouveau_dp_link_train(encoder);
++ } else {
++ u8 status = DP_SET_POWER_D3;
++ nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1);
++ }
++ }
+ }
+
+ static void
+@@ -196,7 +192,8 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
+ uint32_t mode_ctl = 0;
+ int ret;
+
+- NV_DEBUG_KMS(dev, "or %d\n", nv_encoder->or);
++ NV_DEBUG_KMS(dev, "or %d type %d -> crtc %d\n",
++ nv_encoder->or, nv_encoder->dcb->type, crtc->index);
+
+ nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON);
+
+@@ -239,6 +236,14 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
+ }
+ BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1);
+ OUT_RING(evo, mode_ctl);
++
++ nv_encoder->crtc = encoder->crtc;
++}
++
++static struct drm_crtc *
++nv50_sor_crtc_get(struct drm_encoder *encoder)
++{
++ return nouveau_encoder(encoder)->crtc;
+ }
+
+ static const struct drm_encoder_helper_funcs nv50_sor_helper_funcs = {
+@@ -249,7 +254,9 @@ static const struct drm_encoder_helper_funcs nv50_sor_helper_funcs = {
+ .prepare = nv50_sor_prepare,
+ .commit = nv50_sor_commit,
+ .mode_set = nv50_sor_mode_set,
+- .detect = NULL
++ .get_crtc = nv50_sor_crtc_get,
++ .detect = NULL,
++ .disable = nv50_sor_disconnect
+ };
+
+ static void
+@@ -272,32 +279,22 @@ static const struct drm_encoder_funcs nv50_sor_encoder_funcs = {
+ };
+
+ int
+-nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry)
++nv50_sor_create(struct drm_connector *connector, struct dcb_entry *entry)
+ {
+ struct nouveau_encoder *nv_encoder = NULL;
++ struct drm_device *dev = connector->dev;
+ struct drm_encoder *encoder;
+- bool dum;
+ int type;
+
+ NV_DEBUG_KMS(dev, "\n");
+
+ switch (entry->type) {
+ case OUTPUT_TMDS:
+- NV_INFO(dev, "Detected a TMDS output\n");
++ case OUTPUT_DP:
+ type = DRM_MODE_ENCODER_TMDS;
+ break;
+ case OUTPUT_LVDS:
+- NV_INFO(dev, "Detected a LVDS output\n");
+ type = DRM_MODE_ENCODER_LVDS;
+-
+- if (nouveau_bios_parse_lvds_table(dev, 0, &dum, &dum)) {
+- NV_ERROR(dev, "Failed parsing LVDS table\n");
+- return -EINVAL;
+- }
+- break;
+- case OUTPUT_DP:
+- NV_INFO(dev, "Detected a DP output\n");
+- type = DRM_MODE_ENCODER_TMDS;
+ break;
+ default:
+ return -EINVAL;
+@@ -310,8 +307,7 @@ nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry)
+
+ nv_encoder->dcb = entry;
+ nv_encoder->or = ffs(entry->or) - 1;
+-
+- nv_encoder->disconnect = nv50_sor_disconnect;
++ nv_encoder->last_dpms = DRM_MODE_DPMS_OFF;
+
+ drm_encoder_init(dev, encoder, &nv50_sor_encoder_funcs, type);
+ drm_encoder_helper_add(encoder, &nv50_sor_helper_funcs);
+@@ -342,5 +338,6 @@ nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry)
+ nv_encoder->dp.mc_unknown = 5;
+ }
+
++ drm_mode_connector_attach_encoder(connector, encoder);
+ return 0;
+ }
+diff --git a/drivers/gpu/drm/nouveau/nvreg.h b/drivers/gpu/drm/nouveau/nvreg.h
+index 5998c35..ad64673 100644
+--- a/drivers/gpu/drm/nouveau/nvreg.h
++++ b/drivers/gpu/drm/nouveau/nvreg.h
+@@ -147,28 +147,6 @@
+ # define NV_VIO_GX_DONT_CARE_INDEX 0x07
+ # define NV_VIO_GX_BIT_MASK_INDEX 0x08
+
+-#define NV_PFB_BOOT_0 0x00100000
+-#define NV_PFB_CFG0 0x00100200
+-#define NV_PFB_CFG1 0x00100204
+-#define NV_PFB_CSTATUS 0x0010020C
+-#define NV_PFB_REFCTRL 0x00100210
+-# define NV_PFB_REFCTRL_VALID_1 (1 << 31)
+-#define NV_PFB_PAD 0x0010021C
+-# define NV_PFB_PAD_CKE_NORMAL (1 << 0)
+-#define NV_PFB_TILE_NV10 0x00100240
+-#define NV_PFB_TILE_SIZE_NV10 0x00100244
+-#define NV_PFB_REF 0x001002D0
+-# define NV_PFB_REF_CMD_REFRESH (1 << 0)
+-#define NV_PFB_PRE 0x001002D4
+-# define NV_PFB_PRE_CMD_PRECHARGE (1 << 0)
+-#define NV_PFB_CLOSE_PAGE2 0x0010033C
+-#define NV_PFB_TILE_NV40 0x00100600
+-#define NV_PFB_TILE_SIZE_NV40 0x00100604
+-
+-#define NV_PEXTDEV_BOOT_0 0x00101000
+-# define NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT (8 << 12)
+-#define NV_PEXTDEV_BOOT_3 0x0010100c
+-
+ #define NV_PCRTC_INTR_0 0x00600100
+ # define NV_PCRTC_INTR_0_VBLANK (1 << 0)
+ #define NV_PCRTC_INTR_EN_0 0x00600140
diff --git a/drm-revert-drm-fbdev-rework-output-polling-to-be-back-in-core.patch b/drm-revert-drm-fbdev-rework-output-polling-to-be-back-in-core.patch
new file mode 100644
index 000000000..481a08fdc
--- /dev/null
+++ b/drm-revert-drm-fbdev-rework-output-polling-to-be-back-in-core.patch
@@ -0,0 +1,958 @@
+From 5b904034b0ab5195d971b139d0c0b67ab21b063c Mon Sep 17 00:00:00 2001
+From: Kyle McMartin <kyle@dreadnought.i.jkkm.org>
+Date: Mon, 21 Jun 2010 20:33:16 +0100
+Subject: Revert "drm/fbdev: rework output polling to be back in the core. (v4)"
+
+This reverts commit eb1f8e4f3be898df808e2dfc131099f5831d491d.
+
+Conflicts:
+
+ drivers/gpu/drm/drm_crtc_helper.c
+ drivers/gpu/drm/i915/i915_dma.c
+ drivers/gpu/drm/i915/intel_fb.c
+ drivers/gpu/drm/nouveau/nouveau_fbcon.c
+ drivers/gpu/drm/radeon/radeon_fb.c
+ include/drm/drm_crtc_helper.h
+---
+ drivers/gpu/drm/Kconfig | 2 +-
+ drivers/gpu/drm/drm_crtc_helper.c | 111 ------------------------
+ drivers/gpu/drm/drm_fb_helper.c | 123 +++++++++++++++++++++++----
+ drivers/gpu/drm/i915/i915_dma.c | 1 -
+ drivers/gpu/drm/i915/i915_irq.c | 3 +-
+ drivers/gpu/drm/i915/intel_crt.c | 5 -
+ drivers/gpu/drm/i915/intel_display.c | 2 -
+ drivers/gpu/drm/i915/intel_dp.c | 2 -
+ drivers/gpu/drm/i915/intel_drv.h | 2 +-
+ drivers/gpu/drm/i915/intel_fb.c | 14 ++--
+ drivers/gpu/drm/i915/intel_hdmi.c | 1 -
+ drivers/gpu/drm/i915/intel_sdvo.c | 2 -
+ drivers/gpu/drm/nouveau/nouveau_connector.c | 12 ---
+ drivers/gpu/drm/nouveau/nouveau_display.c | 1 -
+ drivers/gpu/drm/nouveau/nouveau_fbcon.c | 13 ++-
+ drivers/gpu/drm/nouveau/nouveau_fbcon.h | 2 +-
+ drivers/gpu/drm/nouveau/nouveau_state.c | 5 +-
+ drivers/gpu/drm/nouveau/nv50_display.c | 2 +-
+ drivers/gpu/drm/radeon/radeon_connectors.c | 13 ---
+ drivers/gpu/drm/radeon/radeon_display.c | 10 --
+ drivers/gpu/drm/radeon/radeon_fb.c | 15 +++-
+ drivers/gpu/drm/radeon/radeon_irq_kms.c | 5 +-
+ drivers/gpu/drm/radeon/radeon_mode.h | 3 +-
+ include/drm/drm_crtc.h | 17 ----
+ include/drm/drm_crtc_helper.h | 6 --
+ include/drm/drm_fb_helper.h | 13 +++-
+ 26 files changed, 155 insertions(+), 230 deletions(-)
+
+diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
+index c2711c6..a51a1e4 100644
+--- a/drivers/gpu/drm/Kconfig
++++ b/drivers/gpu/drm/Kconfig
+@@ -9,7 +9,6 @@ menuconfig DRM
+ depends on (AGP || AGP=n) && PCI && !EMULATED_CMPXCHG && MMU
+ select I2C
+ select I2C_ALGOBIT
+- select SLOW_WORK
+ help
+ Kernel-level support for the Direct Rendering Infrastructure (DRI)
+ introduced in XFree86 4.0. If you say Y here, you need to select
+@@ -24,6 +23,7 @@ config DRM_KMS_HELPER
+ depends on DRM
+ select FB
+ select FRAMEBUFFER_CONSOLE if !EMBEDDED
++ select SLOW_WORK
+ help
+ FB and CRTC helpers for KMS drivers.
+
+diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
+index 9b2a541..b142ac2 100644
+--- a/drivers/gpu/drm/drm_crtc_helper.c
++++ b/drivers/gpu/drm/drm_crtc_helper.c
+@@ -807,114 +807,3 @@ int drm_helper_resume_force_mode(struct drm_device *dev)
+ return 0;
+ }
+ EXPORT_SYMBOL(drm_helper_resume_force_mode);
+-
+-static struct slow_work_ops output_poll_ops;
+-
+-#define DRM_OUTPUT_POLL_PERIOD (10*HZ)
+-static void output_poll_execute(struct slow_work *work)
+-{
+- struct delayed_slow_work *delayed_work = container_of(work, struct delayed_slow_work, work);
+- struct drm_device *dev = container_of(delayed_work, struct drm_device, mode_config.output_poll_slow_work);
+- struct drm_connector *connector;
+- enum drm_connector_status old_status, status;
+- bool repoll = false, changed = false;
+- int ret;
+-
+- mutex_lock(&dev->mode_config.mutex);
+- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+-
+- /* if this is HPD or polled don't check it -
+- TV out for instance */
+- if (!connector->polled)
+- continue;
+-
+- else if (connector->polled & (DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT))
+- repoll = true;
+-
+- old_status = connector->status;
+- /* if we are connected and don't want to poll for disconnect
+- skip it */
+- if (old_status == connector_status_connected &&
+- !(connector->polled & DRM_CONNECTOR_POLL_DISCONNECT) &&
+- !(connector->polled & DRM_CONNECTOR_POLL_HPD))
+- continue;
+-
+- status = connector->funcs->detect(connector);
+- if (old_status != status)
+- changed = true;
+- }
+-
+- mutex_unlock(&dev->mode_config.mutex);
+-
+- if (changed) {
+- /* send a uevent + call fbdev */
+- drm_sysfs_hotplug_event(dev);
+- if (dev->mode_config.funcs->output_poll_changed)
+- dev->mode_config.funcs->output_poll_changed(dev);
+- }
+-
+- if (repoll) {
+- ret = delayed_slow_work_enqueue(delayed_work, DRM_OUTPUT_POLL_PERIOD);
+- if (ret)
+- DRM_ERROR("delayed enqueue failed %d\n", ret);
+- }
+-}
+-
+-void drm_kms_helper_poll_disable(struct drm_device *dev)
+-{
+- if (!dev->mode_config.poll_enabled)
+- return;
+- delayed_slow_work_cancel(&dev->mode_config.output_poll_slow_work);
+-}
+-EXPORT_SYMBOL(drm_kms_helper_poll_disable);
+-
+-void drm_kms_helper_poll_enable(struct drm_device *dev)
+-{
+- bool poll = false;
+- struct drm_connector *connector;
+- int ret;
+-
+- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+- if (connector->polled)
+- poll = true;
+- }
+-
+- if (poll) {
+- ret = delayed_slow_work_enqueue(&dev->mode_config.output_poll_slow_work, DRM_OUTPUT_POLL_PERIOD);
+- if (ret)
+- DRM_ERROR("delayed enqueue failed %d\n", ret);
+- }
+-}
+-EXPORT_SYMBOL(drm_kms_helper_poll_enable);
+-
+-void drm_kms_helper_poll_init(struct drm_device *dev)
+-{
+- slow_work_register_user(THIS_MODULE);
+- delayed_slow_work_init(&dev->mode_config.output_poll_slow_work,
+- &output_poll_ops);
+- dev->mode_config.poll_enabled = true;
+-
+- drm_kms_helper_poll_enable(dev);
+-}
+-EXPORT_SYMBOL(drm_kms_helper_poll_init);
+-
+-void drm_kms_helper_poll_fini(struct drm_device *dev)
+-{
+- drm_kms_helper_poll_disable(dev);
+- slow_work_unregister_user(THIS_MODULE);
+-}
+-EXPORT_SYMBOL(drm_kms_helper_poll_fini);
+-
+-void drm_helper_hpd_irq_event(struct drm_device *dev)
+-{
+- if (!dev->mode_config.poll_enabled)
+- return;
+- delayed_slow_work_cancel(&dev->mode_config.output_poll_slow_work);
+- /* schedule a slow work asap */
+- delayed_slow_work_enqueue(&dev->mode_config.output_poll_slow_work, 0);
+-}
+-EXPORT_SYMBOL(drm_helper_hpd_irq_event);
+-
+-static struct slow_work_ops output_poll_ops = {
+- .execute = output_poll_execute,
+-};
+diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
+index 08c4c92..dcc6601 100644
+--- a/drivers/gpu/drm/drm_fb_helper.c
++++ b/drivers/gpu/drm/drm_fb_helper.c
+@@ -42,6 +42,8 @@ MODULE_LICENSE("GPL and additional rights");
+
+ static LIST_HEAD(kernel_fb_helper_list);
+
++static struct slow_work_ops output_status_change_ops;
++
+ /* simple single crtc case helper function */
+ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
+ {
+@@ -423,13 +425,19 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
+
+ int drm_fb_helper_init(struct drm_device *dev,
+ struct drm_fb_helper *fb_helper,
+- int crtc_count, int max_conn_count)
++ int crtc_count, int max_conn_count,
++ bool polled)
+ {
+ struct drm_crtc *crtc;
+ int ret = 0;
+ int i;
+
+ fb_helper->dev = dev;
++ fb_helper->poll_enabled = polled;
++
++ slow_work_register_user(THIS_MODULE);
++ delayed_slow_work_init(&fb_helper->output_status_change_slow_work,
++ &output_status_change_ops);
+
+ INIT_LIST_HEAD(&fb_helper->kernel_fb_list);
+
+@@ -486,6 +494,8 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
+
+ drm_fb_helper_crtc_free(fb_helper);
+
++ delayed_slow_work_cancel(&fb_helper->output_status_change_slow_work);
++ slow_work_unregister_user(THIS_MODULE);
+ }
+ EXPORT_SYMBOL(drm_fb_helper_fini);
+
+@@ -703,7 +713,7 @@ int drm_fb_helper_set_par(struct fb_info *info)
+
+ if (fb_helper->delayed_hotplug) {
+ fb_helper->delayed_hotplug = false;
+- drm_fb_helper_hotplug_event(fb_helper);
++ delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, 0);
+ }
+ return 0;
+ }
+@@ -816,7 +826,7 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
+ if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) {
+ /* hmm everyone went away - assume VGA cable just fell out
+ and will come back later. */
+- DRM_INFO("Cannot find any crtc or sizes - going 1024x768\n");
++ DRM_ERROR("Cannot find any crtc or sizes - going 1024x768\n");
+ sizes.fb_width = sizes.surface_width = 1024;
+ sizes.fb_height = sizes.surface_height = 768;
+ }
+@@ -1362,7 +1372,12 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
+ * we shouldn't end up with no modes here.
+ */
+ if (count == 0) {
+- printk(KERN_INFO "No connectors reported connected with modes\n");
++ if (fb_helper->poll_enabled) {
++ delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work,
++ 5*HZ);
++ printk(KERN_INFO "No connectors reported connected with modes - started polling\n");
++ } else
++ printk(KERN_INFO "No connectors reported connected with modes\n");
+ }
+ drm_setup_crtcs(fb_helper);
+
+@@ -1370,16 +1385,71 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
+ }
+ EXPORT_SYMBOL(drm_fb_helper_initial_config);
+
+-bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
++/* we got a hotplug irq - need to update fbcon */
++void drm_helper_fb_hpd_irq_event(struct drm_fb_helper *fb_helper)
++{
++ /* if we don't have the fbdev registered yet do nothing */
++ if (!fb_helper->fbdev)
++ return;
++
++ /* schedule a slow work asap */
++ delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, 0);
++}
++EXPORT_SYMBOL(drm_helper_fb_hpd_irq_event);
++
++bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper, bool polled)
+ {
+ int count = 0;
++ int ret;
+ u32 max_width, max_height, bpp_sel;
+- bool bound = false, crtcs_bound = false;
+- struct drm_crtc *crtc;
+
+ if (!fb_helper->fb)
+ return false;
++ DRM_DEBUG_KMS("\n");
++
++ max_width = fb_helper->fb->width;
++ max_height = fb_helper->fb->height;
++ bpp_sel = fb_helper->fb->bits_per_pixel;
++
++ count = drm_fb_helper_probe_connector_modes(fb_helper, max_width,
++ max_height);
++ if (fb_helper->poll_enabled && !polled) {
++ if (count) {
++ delayed_slow_work_cancel(&fb_helper->output_status_change_slow_work);
++ } else {
++ ret = delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, 5*HZ);
++ }
++ }
++ drm_setup_crtcs(fb_helper);
++
++ return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
++}
++EXPORT_SYMBOL(drm_helper_fb_hotplug_event);
++
++/*
++ * delayed work queue execution function
++ * - check if fbdev is actually in use on the gpu
++ * - if not set delayed flag and repoll if necessary
++ * - check for connector status change
++ * - repoll if 0 modes found
++ *- call driver output status changed notifier
++ */
++static void output_status_change_execute(struct slow_work *work)
++{
++ struct delayed_slow_work *delayed_work = container_of(work, struct delayed_slow_work, work);
++ struct drm_fb_helper *fb_helper = container_of(delayed_work, struct drm_fb_helper, output_status_change_slow_work);
++ struct drm_connector *connector;
++ enum drm_connector_status old_status, status;
++ bool repoll, changed = false;
++ int ret;
++ int i;
++ bool bound = false, crtcs_bound = false;
++ struct drm_crtc *crtc;
+
++ repoll = fb_helper->poll_enabled;
++
++ /* first of all check the fbcon framebuffer is actually bound to any crtc */
++ /* take into account that no crtc at all maybe bound */
+ list_for_each_entry(crtc, &fb_helper->dev->mode_config.crtc_list, head) {
+ if (crtc->fb)
+ crtcs_bound = true;
+@@ -1387,21 +1457,38 @@ bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
+ bound = true;
+ }
+
+- if (!bound && crtcs_bound) {
++ if (bound == false && crtcs_bound) {
+ fb_helper->delayed_hotplug = true;
+- return false;
++ goto requeue;
+ }
+- DRM_DEBUG_KMS("\n");
+
+- max_width = fb_helper->fb->width;
+- max_height = fb_helper->fb->height;
+- bpp_sel = fb_helper->fb->bits_per_pixel;
++ for (i = 0; i < fb_helper->connector_count; i++) {
++ connector = fb_helper->connector_info[i]->connector;
++ old_status = connector->status;
++ status = connector->funcs->detect(connector);
++ if (old_status != status) {
++ changed = true;
++ }
++ if (status == connector_status_connected && repoll) {
++ DRM_DEBUG("%s is connected - stop polling\n", drm_get_connector_name(connector));
++ repoll = false;
++ }
++ }
+
+- count = drm_fb_helper_probe_connector_modes(fb_helper, max_width,
+- max_height);
+- drm_setup_crtcs(fb_helper);
++ if (changed) {
++ if (fb_helper->funcs->fb_output_status_changed)
++ fb_helper->funcs->fb_output_status_changed(fb_helper);
++ }
+
+- return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
++requeue:
++ if (repoll) {
++ ret = delayed_slow_work_enqueue(delayed_work, 5*HZ);
++ if (ret)
++ DRM_ERROR("delayed enqueue failed %d\n", ret);
++ }
+ }
+-EXPORT_SYMBOL(drm_fb_helper_hotplug_event);
++
++static struct slow_work_ops output_status_change_ops = {
++ .execute = output_status_change_execute,
++};
+
+diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
+index 59a2bf8..76ace2d 100644
+--- a/drivers/gpu/drm/i915/i915_dma.c
++++ b/drivers/gpu/drm/i915/i915_dma.c
+@@ -1430,7 +1430,6 @@ static int i915_load_modeset_init(struct drm_device *dev,
+ if (ret)
+ goto cleanup_irq;
+
+- drm_kms_helper_poll_init(dev);
+ return 0;
+
+ cleanup_irq:
+diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
+index 2479be0..6350bd3 100644
+--- a/drivers/gpu/drm/i915/i915_irq.c
++++ b/drivers/gpu/drm/i915/i915_irq.c
+@@ -271,7 +271,8 @@ static void i915_hotplug_work_func(struct work_struct *work)
+ }
+ }
+ /* Just fire off a uevent and let userspace tell us what to do */
+- drm_helper_hpd_irq_event(dev);
++ intelfb_hotplug(dev, false);
++ drm_sysfs_hotplug_event(dev);
+ }
+
+ static void i915_handle_rps_change(struct drm_device *dev)
+diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
+index 22ff384..125eded 100644
+--- a/drivers/gpu/drm/i915/intel_crt.c
++++ b/drivers/gpu/drm/i915/intel_crt.c
+@@ -584,10 +584,5 @@ void intel_crt_init(struct drm_device *dev)
+
+ drm_sysfs_connector_add(connector);
+
+- if (I915_HAS_HOTPLUG(dev))
+- connector->polled = DRM_CONNECTOR_POLL_HPD;
+- else
+- connector->polled = DRM_CONNECTOR_POLL_CONNECT;
+-
+ dev_priv->hotplug_supported_mask |= CRT_HOTPLUG_INT_STATUS;
+ }
+diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
+index d753257..70537cf 100644
+--- a/drivers/gpu/drm/i915/intel_display.c
++++ b/drivers/gpu/drm/i915/intel_display.c
+@@ -5036,7 +5036,6 @@ intel_user_framebuffer_create(struct drm_device *dev,
+
+ static const struct drm_mode_config_funcs intel_mode_funcs = {
+ .fb_create = intel_user_framebuffer_create,
+- .output_poll_changed = intel_fb_output_poll_changed,
+ };
+
+ static struct drm_gem_object *
+@@ -5538,7 +5537,6 @@ void intel_modeset_cleanup(struct drm_device *dev)
+
+ mutex_lock(&dev->struct_mutex);
+
+- drm_kms_helper_poll_fini(dev);
+ intel_fbdev_fini(dev);
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
+index 49b54f0..1815df5 100644
+--- a/drivers/gpu/drm/i915/intel_dp.c
++++ b/drivers/gpu/drm/i915/intel_dp.c
+@@ -1393,8 +1393,6 @@ intel_dp_init(struct drm_device *dev, int output_reg)
+ DRM_MODE_CONNECTOR_DisplayPort);
+ drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs);
+
+- connector->polled = DRM_CONNECTOR_POLL_HPD;
+-
+ if (output_reg == DP_A)
+ intel_encoder->type = INTEL_OUTPUT_EDP;
+ else
+diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
+index df931f7..3230e8d 100644
+--- a/drivers/gpu/drm/i915/intel_drv.h
++++ b/drivers/gpu/drm/i915/intel_drv.h
+@@ -235,5 +235,5 @@ extern int intel_overlay_put_image(struct drm_device *dev, void *data,
+ extern int intel_overlay_attrs(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+
+-extern void intel_fb_output_poll_changed(struct drm_device *dev);
++void intelfb_hotplug(struct drm_device *dev, bool polled);
+ #endif /* __INTEL_DRV_H__ */
+diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c
+index c3c5052..79098b3 100644
+--- a/drivers/gpu/drm/i915/intel_fb.c
++++ b/drivers/gpu/drm/i915/intel_fb.c
+@@ -211,6 +211,12 @@ static int intel_fb_find_or_create_single(struct drm_fb_helper *helper,
+ return new_fb;
+ }
+
++void intelfb_hotplug(struct drm_device *dev, bool polled)
++{
++ drm_i915_private_t *dev_priv = dev->dev_private;
++ drm_helper_fb_hpd_irq_event(&dev_priv->fbdev->helper);
++}
++
+ static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
+ .gamma_set = intel_crtc_fb_gamma_set,
+ .gamma_get = intel_crtc_fb_gamma_get,
+@@ -256,7 +262,7 @@ int intel_fbdev_init(struct drm_device *dev)
+
+ ret = drm_fb_helper_init(dev, &ifbdev->helper,
+ dev_priv->num_pipe,
+- INTELFB_CONN_LIMIT);
++ INTELFB_CONN_LIMIT, false);
+ if (ret) {
+ kfree(ifbdev);
+ return ret;
+@@ -278,9 +284,3 @@ void intel_fbdev_fini(struct drm_device *dev)
+ dev_priv->fbdev = NULL;
+ }
+ MODULE_LICENSE("GPL and additional rights");
+-
+-void intel_fb_output_poll_changed(struct drm_device *dev)
+-{
+- drm_i915_private_t *dev_priv = dev->dev_private;
+- drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper);
+-}
+diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
+index 83bd764..acaca07 100644
+--- a/drivers/gpu/drm/i915/intel_hdmi.c
++++ b/drivers/gpu/drm/i915/intel_hdmi.c
+@@ -240,7 +240,6 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
+
+ intel_encoder->type = INTEL_OUTPUT_HDMI;
+
+- connector->polled = DRM_CONNECTOR_POLL_HPD;
+ connector->interlace_allowed = 0;
+ connector->doublescan_allowed = 0;
+ intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
+diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
+index 76993ac..1c716b5 100644
+--- a/drivers/gpu/drm/i915/intel_sdvo.c
++++ b/drivers/gpu/drm/i915/intel_sdvo.c
+@@ -2218,7 +2218,6 @@ intel_sdvo_dvi_init(struct intel_encoder *intel_encoder, int device)
+ }
+
+ connector = &intel_connector->base;
+- connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
+ encoder->encoder_type = DRM_MODE_ENCODER_TMDS;
+ connector->connector_type = DRM_MODE_CONNECTOR_DVID;
+
+@@ -2285,7 +2284,6 @@ intel_sdvo_analog_init(struct intel_encoder *intel_encoder, int device)
+ return false;
+
+ connector = &intel_connector->base;
+- connector->polled = DRM_CONNECTOR_POLL_CONNECT;
+ encoder->encoder_type = DRM_MODE_ENCODER_DAC;
+ connector->connector_type = DRM_MODE_CONNECTOR_VGA;
+ sdvo_connector = intel_connector->dev_priv;
+diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
+index 149ed22..9a61f3c 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
++++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
+@@ -846,7 +846,6 @@ nouveau_connector_create(struct drm_device *dev,
+
+ switch (dcb->type) {
+ case DCB_CONNECTOR_VGA:
+- connector->polled = DRM_CONNECTOR_POLL_CONNECT;
+ if (dev_priv->card_type >= NV_50) {
+ drm_connector_attach_property(connector,
+ dev->mode_config.scaling_mode_property,
+@@ -858,17 +857,6 @@ nouveau_connector_create(struct drm_device *dev,
+ case DCB_CONNECTOR_TV_3:
+ nv_connector->scaling_mode = DRM_MODE_SCALE_NONE;
+ break;
+- case DCB_CONNECTOR_DP:
+- case DCB_CONNECTOR_eDP:
+- case DCB_CONNECTOR_HDMI_0:
+- case DCB_CONNECTOR_HDMI_1:
+- case DCB_CONNECTOR_DVI_I:
+- case DCB_CONNECTOR_DVI_D:
+- if (dev_priv->card_type >= NV_50)
+- connector->polled = DRM_CONNECTOR_POLL_HPD;
+- else
+- connector->polled = DRM_CONNECTOR_POLL_CONNECT;
+- /* fall-through */
+ default:
+ nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN;
+
+diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
+index 74e6b4e..9d7928f 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_display.c
++++ b/drivers/gpu/drm/nouveau/nouveau_display.c
+@@ -101,6 +101,5 @@ nouveau_user_framebuffer_create(struct drm_device *dev,
+
+ const struct drm_mode_config_funcs nouveau_mode_config_funcs = {
+ .fb_create = nouveau_user_framebuffer_create,
+- .output_poll_changed = nouveau_fbcon_output_poll_changed,
+ };
+
+diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+index c9a4a0d..0a59f96 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
++++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+@@ -326,11 +326,15 @@ nouveau_fbcon_find_or_create_single(struct drm_fb_helper *helper,
+ return new_fb;
+ }
+
+-void
+-nouveau_fbcon_output_poll_changed(struct drm_device *dev)
++void nouveau_fbcon_hotplug(struct drm_device *dev)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- drm_fb_helper_hotplug_event(&dev_priv->nfbdev->helper);
++ drm_helper_fb_hpd_irq_event(&dev_priv->nfbdev->helper);
++}
++
++static void nouveau_fbcon_output_status_changed(struct drm_fb_helper *fb_helper)
++{
++ drm_helper_fb_hotplug_event(fb_helper, true);
+ }
+
+ int
+@@ -370,6 +374,7 @@ static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
+ .gamma_set = nouveau_fbcon_gamma_set,
+ .gamma_get = nouveau_fbcon_gamma_get,
+ .fb_probe = nouveau_fbcon_find_or_create_single,
++ .fb_output_status_changed = nouveau_fbcon_output_status_changed,
+ };
+
+
+@@ -387,7 +392,7 @@ int nouveau_fbcon_init(struct drm_device *dev)
+ dev_priv->nfbdev = nfbdev;
+ nfbdev->helper.funcs = &nouveau_fbcon_helper_funcs;
+
+- ret = drm_fb_helper_init(dev, &nfbdev->helper, 2, 4);
++ ret = drm_fb_helper_init(dev, &nfbdev->helper, 2, 4, true);
+ if (ret) {
+ kfree(nfbdev);
+ return ret;
+diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.h b/drivers/gpu/drm/nouveau/nouveau_fbcon.h
+index e7e1268..bf8e00d 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.h
++++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.h
+@@ -58,6 +58,6 @@ void nouveau_fbcon_zfill_all(struct drm_device *dev);
+ void nouveau_fbcon_save_disable_accel(struct drm_device *dev);
+ void nouveau_fbcon_restore_accel(struct drm_device *dev);
+
+-void nouveau_fbcon_output_poll_changed(struct drm_device *dev);
++void nouveau_fbcon_hotplug(struct drm_device *dev);
+ #endif /* __NV50_FBCON_H__ */
+
+diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
+index b02a231..4dcb976 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_state.c
++++ b/drivers/gpu/drm/nouveau/nouveau_state.c
+@@ -519,10 +519,8 @@ nouveau_card_init(struct drm_device *dev)
+
+ dev_priv->init_state = NOUVEAU_CARD_INIT_DONE;
+
+- if (drm_core_check_feature(dev, DRIVER_MODESET)) {
++ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ nouveau_fbcon_init(dev);
+- drm_kms_helper_poll_init(dev);
+- }
+
+ return 0;
+
+@@ -844,7 +842,6 @@ int nouveau_unload(struct drm_device *dev)
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+- drm_kms_helper_poll_fini(dev);
+ nouveau_fbcon_fini(dev);
+ if (dev_priv->card_type >= NV_50)
+ nv50_display_destroy(dev);
+diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
+index 580a5d1..e6a44af 100644
+--- a/drivers/gpu/drm/nouveau/nv50_display.c
++++ b/drivers/gpu/drm/nouveau/nv50_display.c
+@@ -980,7 +980,7 @@ nv50_display_irq_hotplug_bh(struct work_struct *work)
+ if (dev_priv->chipset >= 0x90)
+ nv_wr32(dev, 0xe074, nv_rd32(dev, 0xe074));
+
+- drm_helper_hpd_irq_event(dev);
++ nouveau_fbcon_hotplug(dev);
+ }
+
+ void
+diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
+index 0c7ccc6..40a24c9 100644
+--- a/drivers/gpu/drm/radeon/radeon_connectors.c
++++ b/drivers/gpu/drm/radeon/radeon_connectors.c
+@@ -1085,7 +1085,6 @@ radeon_add_atom_connector(struct drm_device *dev,
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.load_detect_property,
+ 1);
+- connector->polled = DRM_CONNECTOR_POLL_CONNECT;
+ break;
+ case DRM_MODE_CONNECTOR_DVIA:
+ drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
+@@ -1212,12 +1211,6 @@ radeon_add_atom_connector(struct drm_device *dev,
+ break;
+ }
+
+- if (hpd->hpd == RADEON_HPD_NONE) {
+- if (i2c_bus->valid)
+- connector->polled = DRM_CONNECTOR_POLL_CONNECT;
+- } else
+- connector->polled = DRM_CONNECTOR_POLL_HPD;
+-
+ connector->display_info.subpixel_order = subpixel_order;
+ drm_sysfs_connector_add(connector);
+ return;
+@@ -1279,7 +1272,6 @@ radeon_add_legacy_connector(struct drm_device *dev,
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.load_detect_property,
+ 1);
+- connector->polled = DRM_CONNECTOR_POLL_CONNECT;
+ break;
+ case DRM_MODE_CONNECTOR_DVIA:
+ drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
+@@ -1348,11 +1340,6 @@ radeon_add_legacy_connector(struct drm_device *dev,
+ break;
+ }
+
+- if (hpd->hpd == RADEON_HPD_NONE) {
+- if (i2c_bus->valid)
+- connector->polled = DRM_CONNECTOR_POLL_CONNECT;
+- } else
+- connector->polled = DRM_CONNECTOR_POLL_HPD;
+ connector->display_info.subpixel_order = subpixel_order;
+ drm_sysfs_connector_add(connector);
+ return;
+diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
+index c73444a..ed756be 100644
+--- a/drivers/gpu/drm/radeon/radeon_display.c
++++ b/drivers/gpu/drm/radeon/radeon_display.c
+@@ -887,15 +887,8 @@ radeon_user_framebuffer_create(struct drm_device *dev,
+ return &radeon_fb->base;
+ }
+
+-static void radeon_output_poll_changed(struct drm_device *dev)
+-{
+- struct radeon_device *rdev = dev->dev_private;
+- radeon_fb_output_poll_changed(rdev);
+-}
+-
+ static const struct drm_mode_config_funcs radeon_mode_funcs = {
+ .fb_create = radeon_user_framebuffer_create,
+- .output_poll_changed = radeon_output_poll_changed
+ };
+
+ struct drm_prop_enum_list {
+@@ -1044,8 +1037,6 @@ int radeon_modeset_init(struct radeon_device *rdev)
+ radeon_pm_init(rdev);
+
+ radeon_fbdev_init(rdev);
+- drm_kms_helper_poll_init(rdev->ddev);
+-
+ return 0;
+ }
+
+@@ -1058,7 +1049,6 @@ void radeon_modeset_fini(struct radeon_device *rdev)
+ radeon_pm_fini(rdev);
+
+ if (rdev->mode_info.mode_config_initialized) {
+- drm_kms_helper_poll_fini(rdev->ddev);
+ radeon_hpd_fini(rdev);
+ drm_mode_config_cleanup(rdev->ddev);
+ rdev->mode_info.mode_config_initialized = false;
+diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
+index dc1634b..7dc38f6 100644
+--- a/drivers/gpu/drm/radeon/radeon_fb.c
++++ b/drivers/gpu/drm/radeon/radeon_fb.c
+@@ -316,9 +316,16 @@ int radeon_parse_options(char *options)
+ return 0;
+ }
+
+-void radeon_fb_output_poll_changed(struct radeon_device *rdev)
++void radeonfb_hotplug(struct drm_device *dev, bool polled)
+ {
+- drm_fb_helper_hotplug_event(&rdev->mode_info.rfbdev->helper);
++ struct radeon_device *rdev = dev->dev_private;
++
++ drm_helper_fb_hpd_irq_event(&rdev->mode_info.rfbdev->helper);
++}
++
++static void radeon_fb_output_status_changed(struct drm_fb_helper *fb_helper)
++{
++ drm_helper_fb_hotplug_event(fb_helper, true);
+ }
+
+ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfbdev)
+@@ -357,6 +364,7 @@ static struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
+ .gamma_set = radeon_crtc_fb_gamma_set,
+ .gamma_get = radeon_crtc_fb_gamma_get,
+ .fb_probe = radeon_fb_find_or_create_single,
++ .fb_output_status_changed = radeon_fb_output_status_changed,
+ };
+
+ int radeon_fbdev_init(struct radeon_device *rdev)
+@@ -379,7 +387,7 @@ int radeon_fbdev_init(struct radeon_device *rdev)
+
+ ret = drm_fb_helper_init(rdev->ddev, &rfbdev->helper,
+ rdev->num_crtc,
+- RADEONFB_CONN_LIMIT);
++ RADEONFB_CONN_LIMIT, true);
+ if (ret) {
+ kfree(rfbdev);
+ return ret;
+@@ -388,6 +396,7 @@ int radeon_fbdev_init(struct radeon_device *rdev)
+ drm_fb_helper_single_add_all_connectors(&rfbdev->helper);
+ drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel);
+ return 0;
++
+ }
+
+ void radeon_fbdev_fini(struct radeon_device *rdev)
+diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c
+index 059bfa4..b0178de 100644
+--- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
++++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
+@@ -26,7 +26,6 @@
+ * Jerome Glisse
+ */
+ #include "drmP.h"
+-#include "drm_crtc_helper.h"
+ #include "radeon_drm.h"
+ #include "radeon_reg.h"
+ #include "radeon.h"
+@@ -56,7 +55,9 @@ static void radeon_hotplug_work_func(struct work_struct *work)
+ radeon_connector_hotplug(connector);
+ }
+ /* Just fire off a uevent and let userspace tell us what to do */
+- drm_helper_hpd_irq_event(dev);
++ radeonfb_hotplug(dev, false);
++
++ drm_sysfs_hotplug_event(dev);
+ }
+
+ void radeon_driver_irq_preinstall_kms(struct drm_device *dev)
+diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
+index 67358ba..fdd1611 100644
+--- a/drivers/gpu/drm/radeon/radeon_mode.h
++++ b/drivers/gpu/drm/radeon/radeon_mode.h
+@@ -588,6 +588,5 @@ void radeon_fbdev_fini(struct radeon_device *rdev);
+ void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state);
+ int radeon_fbdev_total_size(struct radeon_device *rdev);
+ bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj);
+-
+-void radeon_fb_output_poll_changed(struct radeon_device *rdev);
++void radeonfb_hotplug(struct drm_device *dev, bool polled);
+ #endif
+diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
+index 93a1a31..a7148d2 100644
+--- a/include/drm/drm_crtc.h
++++ b/include/drm/drm_crtc.h
+@@ -31,7 +31,6 @@
+ #include <linux/idr.h>
+
+ #include <linux/fb.h>
+-#include <linux/slow-work.h>
+
+ struct drm_device;
+ struct drm_mode_set;
+@@ -461,15 +460,6 @@ enum drm_connector_force {
+ DRM_FORCE_ON_DIGITAL, /* for DVI-I use digital connector */
+ };
+
+-/* should we poll this connector for connects and disconnects */
+-/* hot plug detectable */
+-#define DRM_CONNECTOR_POLL_HPD (1 << 0)
+-/* poll for connections */
+-#define DRM_CONNECTOR_POLL_CONNECT (1 << 1)
+-/* can cleanly poll for disconnections without flickering the screen */
+-/* DACs should rarely do this without a lot of testing */
+-#define DRM_CONNECTOR_POLL_DISCONNECT (1 << 2)
+-
+ /**
+ * drm_connector - central DRM connector control structure
+ * @crtc: CRTC this connector is currently connected to, NULL if none
+@@ -514,8 +504,6 @@ struct drm_connector {
+ u32 property_ids[DRM_CONNECTOR_MAX_PROPERTY];
+ uint64_t property_values[DRM_CONNECTOR_MAX_PROPERTY];
+
+- uint8_t polled; /* DRM_CONNECTOR_POLL_* */
+-
+ /* requested DPMS state */
+ int dpms;
+
+@@ -555,7 +543,6 @@ struct drm_mode_set {
+ */
+ struct drm_mode_config_funcs {
+ struct drm_framebuffer *(*fb_create)(struct drm_device *dev, struct drm_file *file_priv, struct drm_mode_fb_cmd *mode_cmd);
+- void (*output_poll_changed)(struct drm_device *dev);
+ };
+
+ struct drm_mode_group {
+@@ -593,10 +580,6 @@ struct drm_mode_config {
+ struct drm_mode_config_funcs *funcs;
+ resource_size_t fb_base;
+
+- /* output poll support */
+- bool poll_enabled;
+- struct delayed_slow_work output_poll_slow_work;
+-
+ /* pointers to standard properties */
+ struct list_head property_blob_list;
+ struct drm_property *edid_property;
+diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h
+index 1121f77..b1fa0f8 100644
+--- a/include/drm/drm_crtc_helper.h
++++ b/include/drm/drm_crtc_helper.h
+@@ -127,10 +127,4 @@ static inline void drm_connector_helper_add(struct drm_connector *connector,
+ }
+
+ extern int drm_helper_resume_force_mode(struct drm_device *dev);
+-extern void drm_kms_helper_poll_init(struct drm_device *dev);
+-extern void drm_kms_helper_poll_fini(struct drm_device *dev);
+-extern void drm_helper_hpd_irq_event(struct drm_device *dev);
+-
+-extern void drm_kms_helper_poll_disable(struct drm_device *dev);
+-extern void drm_kms_helper_poll_enable(struct drm_device *dev);
+ #endif
+diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
+index f0a6afc..9b55a94 100644
+--- a/include/drm/drm_fb_helper.h
++++ b/include/drm/drm_fb_helper.h
+@@ -30,6 +30,8 @@
+ #ifndef DRM_FB_HELPER_H
+ #define DRM_FB_HELPER_H
+
++#include <linux/slow-work.h>
++
+ struct drm_fb_helper;
+
+ struct drm_fb_helper_crtc {
+@@ -69,6 +71,9 @@ struct drm_fb_helper_funcs {
+
+ int (*fb_probe)(struct drm_fb_helper *helper,
+ struct drm_fb_helper_surface_size *sizes);
++
++ void (*fb_output_status_changed)(struct drm_fb_helper *helper);
++
+ };
+
+ struct drm_fb_helper_connector {
+@@ -90,6 +95,8 @@ struct drm_fb_helper {
+ u32 pseudo_palette[17];
+ struct list_head kernel_fb_list;
+
++ struct delayed_slow_work output_status_change_slow_work;
++ bool poll_enabled;
+ /* we got a hotplug but fbdev wasn't running the console
+ delay until next set_par */
+ bool delayed_hotplug;
+@@ -100,7 +107,7 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *helper,
+
+ int drm_fb_helper_init(struct drm_device *dev,
+ struct drm_fb_helper *helper, int crtc_count,
+- int max_conn);
++ int max_conn, bool polled);
+ void drm_fb_helper_fini(struct drm_fb_helper *helper);
+ int drm_fb_helper_blank(int blank, struct fb_info *info);
+ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
+@@ -123,8 +130,10 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
+
+ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info);
+
+-bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper);
++bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper,
++ bool polled);
+ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel);
+ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper);
+
++void drm_helper_fb_hpd_irq_event(struct drm_fb_helper *fb_helper);
+ #endif
+--
+1.7.0.1
+
diff --git a/find-provides b/find-provides
new file mode 100755
index 000000000..b28d1028f
--- /dev/null
+++ b/find-provides
@@ -0,0 +1,44 @@
+#!/usr/bin/python
+#
+# find-provides: munge the provides dependencies from the kabideps file
+#
+# This software may be freely redistributed under the terms of the GNU
+# General Public License (GPL).
+#
+# Takes a directory prefix, then outputs the kabideps file contents.
+
+__author__ = "Jon Masters <jcm@redhat.com>"
+__version__ = "1.0"
+__date__ = "Tue 25 Jul 2006 04:00 GMT"
+__copyright__ = "Copyright (C) 2006 Red Hat, Inc"
+__license__ = "GPL"
+
+import os
+import re
+import string
+import sys
+
+false = 0
+true = 1
+
+kabideps=""
+
+p = re.compile('^(.*)/symvers-(.*).gz$')
+while true:
+ foo = sys.stdin.readline()
+ if foo == "":
+ break
+ string.split(foo)
+ m = p.match(foo)
+ if m:
+ kabideps=sys.argv[1] + "/kernel-" + m.group(2) + "-kabideps"
+
+if kabideps == "":
+ sys.exit(0)
+
+if not (os.path.isfile(kabideps)):
+ sys.stderr.write(sys.argv[0] + ": cannot locate kabideps file: " + kabideps + "\n")
+ sys.exit(1)
+
+sys.stderr.write(sys.argv[0] + ": processing kABI: " + kabideps)
+os.system("cat " + kabideps)
diff --git a/fix_xen_guest_on_old_EC2.patch b/fix_xen_guest_on_old_EC2.patch
new file mode 100644
index 000000000..e86200295
--- /dev/null
+++ b/fix_xen_guest_on_old_EC2.patch
@@ -0,0 +1,34 @@
+
+Legacy hypervisors (RHEL 5.0 and RHEL 5.1) do not handle guest writes to
+cr4 gracefully. If a guest attempts to write a bit of cr4 that is
+unsupported, then the HV is so offended it crashes the domain. While
+later guest kernels (such as RHEL6) don't assume the HV supports all
+features, they do expect nicer responses. That assumption introduced
+code that probes whether or not xsave is supported early in the boot. So
+now when attempting to boot a RHEL6 guest on RHEL5.0 or RHEL5.1 an early
+crash will occur.
+
+This patch is quite obviously an undesirable hack. The real fix for this
+problem should be in the HV, and is, in later HVs. However, to support
+running on old HVs, RHEL6 can take this small change. No impact will
+occur for running on any RHEL HV (not even RHEL 5.5 supports xsave).
+There is only potential for guest performance loss on upstream Xen.
+
+---
+ arch/x86/xen/enlighten.c | 1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+
+diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
+index 52f8e19..6db3d67 100644
+--- a/arch/x86/xen/enlighten.c
++++ b/arch/x86/xen/enlighten.c
+@@ -802,6 +802,7 @@ static void xen_write_cr4(unsigned long cr4)
+ {
+ cr4 &= ~X86_CR4_PGE;
+ cr4 &= ~X86_CR4_PSE;
++ cr4 &= ~X86_CR4_OSXSAVE;
+
+ native_write_cr4(cr4);
+ }
+--
+1.6.6.1
diff --git a/genkey b/genkey
new file mode 100644
index 000000000..49c6ce8be
--- /dev/null
+++ b/genkey
@@ -0,0 +1,7 @@
+%pubring kernel.pub
+%secring kernel.sec
+Key-Type: DSA
+Key-Length: 512
+Name-Real: Red Hat, Inc.
+Name-Comment: Kernel Module GPG key
+%commit
diff --git a/git-linus.diff b/git-linus.diff
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/git-linus.diff
diff --git a/git-utrace.patch b/git-utrace.patch
new file mode 100644
index 000000000..77b5fa749
--- /dev/null
+++ b/git-utrace.patch
@@ -0,0 +1,6326 @@
+From c2f1645ae87d5b7fc5e5973c3a93a4ae1684a76b Mon Sep 17 00:00:00 2001
+From: Kyle McMartin <kyle@dreadnought.i.jkkm.org>
+Date: Tue, 22 Jun 2010 11:31:13 +0100
+Subject: Merge remote branch 'utrace/utrace-ptrace' into rawhide
+
+% git log --oneline --no-merges 7e27d6e..a91f6b7
+f979955 utrace-ptrace: fix compiling ptrace_regset under CONFIG_UTRACE
+b5f196b utrace-ptrace: copy PTRACE_GETREGSET code to utrace-ptrace
+d83135e utrace: fix utrace_maybe_reap() vs find_matching_engine() race
+9a2c607 utrace: move CONFIG_UTRACE after AUDITSYSCALL in init/Kconfig
+62f4621 utrace: s/rmb/mb/ in tracehook_notify_resume()
+65f5e9d utrace: fix utrace_maybe_reap logic
+ed1f9c2 utrace: fix syntax nit for !CONFIG_UTRACE
+71e3f39 ptrace: add utrace comment
+e7afc73 utrace: use WARN with text
+a8ced33 utrace: cosmetic restructure
+4330b80 utrace: remove some inline keywords
+d4be40a utrace: remove report_clone special priority for utrace_attach_task on child
+8c56566 ptrace: updates for utrace API changes
+1900135 utrace: streamline callback API
+97662d3 utrace: more cosmetic trivia
+fd414cd utrace: more cosmetic cleanup
+f30f068 utrace: cosmetic trivia
+cfebda7 utrace: fix the comments about rmb() in task_utrace_struct()
+875858a utrace: improve the comment in tracehook_notify_resume()
+76b49a5 utrace: fix the ->cloning check in utrace_attach_delay()
+e0755bb utrace: kill mb() in tracehook_report_death()
+9fdc988 fix __must_check warnings
+3e02499 kill suppress_sigtrap()
+f872e69 utrace: don't set ->ops = utrace_detached_ops lockless
+938482e utrace: fix doc typo
+7fae049 utrace: avoid BUG_ON when engine leaves bogus si_signo
+71b7a85 utrace: trivial, move CONFIG_UTRACE into "General setup"
+9c8dbe0 utrace: reset report action for UTRACE_SYSCALL_RESUMED iteration
+4c7514e join PTRACE_EVENT_SYSCALL_XXX states
+a8f782e export __ptrace_detach() and do_notify_parent_cldstop()
+c3473e1 ptrace_signal: check PT_PTRACED before reporting a signal
+b396f5e tracehooks: check PT_PTRACED before reporting the single-step
+45667dd tracehooks: kill some PT_PTRACED checks
+e8a2f23 ptrace: cleanup ptrace_init_task()->ptrace_link() path
+611dab8 kill CONFIG_UTRACE_PTRACE
+8d3833e rm kernel/ptrace-common.h
+494deb7 export __ptrace_detach(), add "ifndef CONFIG_UTRACE" into ptrace.c
+05cb325 (upstream) reorder the code in kernel/ptrace.c
+eb10f13 restore the old kernel/ptrace.c
+ddcc525 utrace_resume: Avoid finish_resume_report() for UTRACE_RESUME
+47852f9 mv kernel/ptrace.c kernel/ptrace-utrace.c
+de5a46e utrace: fix UTRACE_SYSCALL_RESUMED nits
+3bd4be9 stepping, accommodate to utrace-cleanup changes
+679be9e Revert "utrace: synthesize SIGTRAP for single-stepping at syscall-exit"
+23ab966 utrace: barrier nits
+d3800b8 utrace: tracehook_init_task
+64daf14 utrace: task_utrace_struct() barrier
+f19442c utrace: synthesize SIGTRAP for single-stepping at syscall-exit
+2583b32 utrace: nit for utrace-ptrace
+a88b467 ptrace: x86: change syscall_trace_leave() to rely on tracehook when stepping
+e01acf4 ptrace: x86: implement user_single_step_siginfo()
+462a57b ptrace: change tracehook_report_syscall_exit() to handle stepping
+172590d ptrace: powerpc: implement user_single_step_siginfo()
+d63b43d ptrace: introduce user_single_step_siginfo() helper
+c575558 utrace: barriers for initializing struct utrace
+89df3c7 utrace: utrace_attach_task() forgets to return when ->utrace == NULL
+4d17e95 utrace: finish_report() must never set ->resume = UTRACE_STOP
+212f67e utrace: utrace_get_signal() must check ->pending_attach
+eff6ca8 change ptrace_report_signal() to use user_single_step_siginfo()
+cba1272 don't send the unnecessary SIGTRAP after SYSCALL_EXIT
+8aa71a6 revert "turn PTRACE_EVENT_SIGTRAP into PTRACE_EVENT_SIGNAL"
+90c8237 utrace-ptrace: minimally handle UTRACE_SYSCALL_RESUMED
+a7e9198 utrace: clean up resume-action handling
+962eb2f utrace: update after merge
+e2ced71 re-introduce utrace_finish_stop() to fix the race with SIGKILL
+603e19c turn PTRACE_EVENT_SIGTRAP into PTRACE_EVENT_SIGNAL
+ff87f65 introduce suppress_sigtrap() to prevent unwanted send_sigtrap()
+6505e3c move ptrace_resume()->send_sigtrap() logic into ptrace_report_signal()
+5261baa prepare ptrace_report_signal() to synthesize SIGTRAP
+ef9534b ptrace_request: turn ptrace_resume() into default case
+f50c776 s/context/ctx/
+228b2e3 ptrace_notify_stop: kill the temporary WARN_ON()
+93e866a ptrace_request(PTRACE_KILL) should not(?) return -ESRCH
+26fefca utrace: sticky resume action
+28b2774b utrace: remove ->stopped field
+9e0f357 utrace_set_events: nit clean up
+6d0bad3 nits
+48bab07 (utrace) utrace_get_signal: don't dequeue_signal() if ->group_stop_count
+d4ef551 (upstream) signals: check ->group_stop_count after tracehook_get_signal()
+6292daa ptrace_detach_task: don't use valid_signal()
+c5a6a82 cosmetic, renames
+e422a3f cosmetic, relocate some code in ptrace.c
+b96e4db (upstream) introduce kernel/ptrace.h
+7665564 (upstream) tracehook_signal_handler: check PT_PTRACED
+7d708ca tracehooks: revert utrace-ptrace changes
+4104e29 (upstream) ptrace_init_task: cleanup the usage of ptrace_link()
+d04ccb7 revert all utrace-ptrace changes in ptrace.h
+80786ce revert utrace-ptrace changes in kernel/signal.c
+0b02f9e introduce PT_UTRACED to replace PT_PTRACED inside ptrace.c
+030ce35 tracehooks: remove some PT_PTRACED checks
+4b7b15a revert the clone() related changes in tracehook.h
+769030e hack ptrace_check_attach() to make it almost correct
+7aa5c3a cosmetic, fold do_ptrace_resume() into ptrace_resume()
+d27ebc1 cosmetic, introduce ptrace_resume_action()
+35fbca4 turn context->sysemu into PTRACE_O_SYSEMU
+38a8c1f PTRACE_SYSEMU_SINGLESTEP support
+4367836 PTRACE_SYSEMU support
+16819db ptrace_report_clone: minor cleanups + comments
+ac1afd8 ptrace_resume: rewrite request processing
+6b0d4f6 do_ptrace_resume: always use ptrace_wake_up()
+fa92ce3 do_ptrace_resume: consolidate multiple switch stmts
+135d780 uglify the code again to report VFORK_DONE after VFORK
+4e3f362 fix PTRACE_SYSCALL after PTRACE_EVENT_VFORK_DONE stop
+3f95189 ptrace_report_clone: uglify even more to handle TRACEVFORKDONE without TRACEVFORK
+66ca8b6 ptrace_report_clone: uglify CLONE_PTRACE/CLONE_UNTRACED behaviour to match upstream
+fc82b2c pretend PTRACE_O_TRACEVFORKDONE doesn't exist
+28aa15a utrace_set_events: never return -EINPROGRESS unless clearing some event bits
+a7f4350 utrace_stop: do ptrace_notify_stop() unconditionally
+cb78492 ptrace_report_exit: fix WARN_ON() condition
+bb941c3 do_ptrace_notify_stop: document the usage of tracee->exit_code
+383ba85 ptrace_wake_up: don't clear tracee->exit_code + update comments
+3d5c221 ptrace_wake_up: add "bool force_wakeup" argument for implicit detach
+be6862e ptrace_wake_up: clear context->stop_code
+bfb40c8 detach: use ptrace_wake_up() instead of utrace_control()
+7de148a shift context re-initialization from detach to reuse
+464def3 cleanup/optimize reuse/attch in ptrace_attach_task()
+50f56b9 ptrace_attach_task: rely on utrace_barrier(), don't check ->ops
+03376fd use set_stop_code() in ptrace_report_signal(UTRACE_SIGNAL_HANDLER)
+85f8b3a detach should reset the context of self-detaching engine
+a27233a attach: try to re-use the self-detaching engine
+8667615 ptrace_notify_stop: fix engine leak
+3d5d053 ptrace_detach_task: don't use engine ptr before IS_ERR(engine)
+01875c7 fold detach_signal() into ptrace_detach_task()
+464c2b7 don't detach the engine with the parting signal
+97b345c implement the basic detach-with-signal logic
+a158247 rework access to context->siginfo
+20ea83b introduce set_stop_code() helper
+eb222ed cosmetic, misc renames
+f83b2ca move "event << 8" into syscall_code()
+4c99287 kill context->ev_name
+df7c8f2 encode internal stop events in ->ev_code too
+3f48297 introduce get_stop_code(context) helper
+313bad1 introduce syscall_code(context) helper
+47b5e2c don't clear context->ev_code for debugging
+4e09fe3 convert ptrace_setsiginfo() to use ptrace_rw_siginfo()
+53187be convert ptrace_getsiginfo() to use ptrace_rw_siginfo()
+e7ac055 introduce ptrace_rw_siginfo() helper
+c625793 move "resume signal" logic into the tracee's context
+0768d89 UTRACE_SIGNAL_HANDLER should never see ->siginfo != NULL
+e90cb71 don't use task_struct->ptrace_message
+842684f do_ptrace_notify_stop: fix the race with SIGKILL
+d0ed18d do_ptrace_notify_stop: backport the "sync wakeup" logic
+08f4a21 fix the stepping over syscall
+a55d174 implement the stacked SYSCALL_EXIT event
+ba73824 ptrace_resume: don't ignore "data" argument
+fbd4368 kill context->ev_array[]
+3c6f822 Revert "ptrace_resume_signal() should use context->siginfo under ->siglock"
+ee31432 Revert "UTRACE_SIGNAL_HANDLER should never see ->siginfo != NULL"
+a4e5af1 Revert "introduce context_siginfo() helper"
+9bc939a revert merge w/s change
+6752625 introduce context_siginfo() helper
+d43a453 UTRACE_SIGNAL_HANDLER should never see ->siginfo != NULL
+e4e48df ptrace_resume_signal() should use context->siginfo under ->siglock
+4492770 implement UTRACE_SIGNAL_HANDLER stepping
+5f926a5 implement PTRACE_SINGLESTEP/PTRACE_SINGLEBLOCK
+8b70ae1 ptrace_request: use ptrace_lookup_engine()
+abd580d change ptrace_resume() to have the single "return"
+85878ae introduce ptrace_lookup_engine()
+74904f1 mv task_struct->last_siginfo ptrace_context->siginfo
+2b17f4a pretens ptrace_detach(sig) works
+075db41 ptrace_report_quiesce() can't trust fatal_signal_pending()
+d583c87 remove the now unneeded code
+69a6c83 break ptrace_report_signal()
+d6a31ee do_ptrace_notify_stop: kill "->ev_message != 0" check
+e194687 convert ptrace_report_exit()
+8bf8304 PTRACE_EVENT_VFORK_DONE: set ev_options = PTRACE_O_TRACEVFORKDONE
+b8f5e2a make sure PTRACE_SYSCALL reports SYSCALL_EXIT
+258b27d make sure PTRACE_CONT "disables" SYSCALL_EXIT report
+d26b659 introduce ptrace_event->ev_options
+03a0fe3 convert ptrace_report_exec()
+bea6139 convert ptrace_report_syscall_entry()
+17dd96d cleanup/simplify stop/resume mess
+97fc962 utrace: comments
+c661ddb utrace: move struct utrace back where it belongs
+95dcdee implement stacked stop events
+8608da6 ptrace_report_syscall_exit: do not WARN() if killed
+95a6b6b ptrace_report_clone: rework the stop/resume logic
+25dd723 remove the current PTRACE_EVENT_VFORK_DONE logic
+7d8900a ptrace_wake_up: fix the "compatibility bug" logic
+9a50d27 ptrace_report_syscall_exit: return UTRACE_STOP, not UTRACE_RESUME
+c07370d simplify utrace_add_engine() vs utrace_reap() protection
+0f4d918 utrace_add_engine: cleanup
+a24e891 fix utrace_reset() vs release_task() theoretical race
+dfc0917 change attach/release to avoid unnecessary utrace_reap()
+cbed668 utrace_attach_task: do no check ->exit_state
+9d114a6 utrace_wakeup: do not check target->state
+9368f18 utrace_wakeup: lock ->siglock directly
+e9b58e9 convert ptrace_report_syscall_exit() to use ptrace_context
+1d47e4d introduce context->resume_stopped()
+c34d813 introduce context->stopped_code
+b7edb5e introduce ptrace_notify_stop()
+93b2e7e utrace_release_task: cosmetic
+ac6e19c utrace_reap: loop lockless, do not clear ->ops and ->flags early
+7852d10 utrace: slow_path -> pending_attach
+c827b15 utrace_add_engine() should set ->utrace_flags |= REAP
+2e12892 utrace_reap: fix missing callback
+04852f3 utrace: do not force report on attach
+37b68f7 kill ptrace_setoptions() and ptrace_update_utrace()
+f1b39f3 use context->options instead of "->ptrace & PT_"
+d05bf8e ptrace_set_options: use PTRACE_O_ instead of PT_
+167b56a "disable" tracehook_prepare_clone()
+5e526f3 introduce ptrace_set_options()
+4a50ac1 introduce ptrace_context->options
+0457aa8 introduce the empty struct ptrace_context
+a2bca6f utrace_reset: do not use "unsafe mode"
+eac91f4 utrace_control: don't mark_engine_detached() before engine_wants_stop()
+c2916fb utrace_control: fix utrace_reset(safe) usage when ->exit_state != 0
+c36a311 utrace_reset fix
+8d2fc04 utrace: remove unused inline
+64a8ca3 utrace_reset cleanup
+d1a14ce utrace: change UTRACE_STOP bookkeeping
+96fe3cc Revert "utrace_stop: fix UTRACE_DETACH race"
+ceaae71 utrace: check QUIESCE before reporting UTRACE_SIGNAL_REPORT/HANDLER
+fc30d20 utrace_do_stop: move "if (exit_state)" logic to the caller
+9b655f7 utrace_do_stop: don't set ->stopped when ->exit_state
+9ed6a39 utrace_set_events: never return -EINPROGRESS on a zombie
+592d977 utrace_do_stop: cleanup the usage of ->siglock
+7f51e58 utrace: fix utrace->signal_handler "leakage"
+be5e266 utrace: utrace_finish_vfork: check ->vfork_stop lockless
+c3580f1 utrace-ptrace: fix conditions in ptrace_do_detach
+00932db utrace_stop: fix UTRACE_DETACH race
+b032859 utrace: move utrace_stop down
+a62ed15 utrace: consolidate utrace_reset callers
+c8315d3 ptrace_do_detach: Fiddle code to avoid warnings.
+e3635f1 utrace-ptrace: use WARN_ON(), suppress __must_check warning
+8ba59d7 ptrace_attach_task: kill ->ptrace != 0 check
+a18378e exit_ptrace: use ptrace_do_detach()
+371c69c ptrace_detach: do ptrace_unlink() first
+096f3ed ptrace_detach: kill the unconditional wakeup
+d999521 ptrace_report_clone: rework auto-attaching
+8cefebf move ->ptrace == 0 checks to ptrace_attach_task()
+471d6f4 utrace_engine_ops: add release hook
+78ca7e7 utrace_control: return -EINVAL for missing UTRACE_EVENT(QUIESCE)
+fcb8fa0 change ptrace_traceme() to use the new helpers, kill prepare/finish attach
+e82feff rework prepare_ptrace_attach/finish_ptrace_attach
+3bea38f do not use engine->data
+57cedd0 ptrace_detach_task: always do UTRACE_DETACH
+2093f3a shift ptrace_utrace_exit() from tracehook_report_exit() to exit_ptrace()
+33fb930 ptrace_resume()->send_sig() can crash
+a7b05fd ptrace_check_attach: check child->parent
+5ed4eff remove (almost all) !CONFIG_UTRACE_PTRACE code
+fb9379c change utrace_stop() to return void
+5bbbb41 kill utrace_report->killed
+0b57f74 finish_utrace_stop: use __fatal_signal_pending(), dont take ->siglock
+113a07e utrace: rework finish_report flag logic
+8ad60bb utrace_stop: preserve report/interrupt requests across stop/resume
+af3eb44 get_utrace_lock: do not check EXIT_DEAD
+d87e8c4 finish_utrace_stop: check ->stopped lockless
+3e0a686 utrace_report_jctl/utrace_get_signal: do not play with ->stopped
+7d97118 utrace_do_stop: s/STOPPED/TRACED/ to protect against SIGCONT
+ad2497a use tracehook_finish_jctl() to clear ->stopped
+f99db9f utrace_report_jctl: do not play with the group-stop state
+fd89498 introduce tracehook_finish_jctl() helper
+ff6be89 do_signal_stop: do not call tracehook_notify_jctl() in TASK_STOPPED state
+66e0705 utrace_stop: don't forget about SIGNAL_STOP_STOPPED
+2edad7d utrace_wakeup: take ->group_stop_count into account
+d4bcb57 utrace_reap: clear engine->flags when finishing detach
+cf890ad utrace: fix utrace->reporting left set for no callback
+cbe5188 More than one user has hit the -EEXIST problem when using utrace_attach_task and UTRACE_ATTACH_EXCLUSIVE without UTRACE_ATTACH_MATCH_DATA|_OPS. Document that a bit more.
+52db080 UTRACE_SYSCALL_RESUMED repeat callback
+5e67e22 utrace docbook: s/first/last/ braino
+4bd78f8 utrace: reverse engine callback order for report_syscall_entry
+1757088 utrace: WARN instead of BUG on misuse of UTRACE_*STEP without arch_has_*_step() check
+5d4e97b utrace: restore tracehook_report_death comment misplaced in merges
+cb49dcd utrace_report_syscall_entry: remove unnecessary recalc_sigpending() check
+c0909b5 utrace_resume: fix potential TIF_SIGPENDING race
+f0a1c64 utrace: use \t separator in /proc/pid/status
+13a5838 utrace: init_task syntax nit
+715d2a1 utrace: cosmetic
+42de707 utrace_report_jctl: do splice_attaching
+622013d utrace_resume: remove racy BUG_ON
+282d685 whitespace fix
+bec92f8 signals: tracehook_notify_jctl change
+a7181aa utrace: simplify death report condition
+4d8a6fd utrace: barrier between TIF_NOTIFY_RESUME check and utrace_flags/utrace->report checks
+ae3096f utrace-ptrace: remove unsafe_exec and tracer_task hooks
+325fecc utrace: get rid of tracer_task and unsafe_exec hooks
+0084fc2 utrace: ensure UTRACE_REPORT callback return leads to callback after utrace_stop
+5bdc6f1 utrace: cosmetic: DEAD_FLAGS_MASK macro
+5c5bdbe utrace: cosmetic: _UTRACE_DEATH_EVENTS macro
+f067223 utrace: make sure utrace_flags is nonzero before set_notify_resume at attach
+e2d293e utrace: drop racy unlocked check in utrace_do_stop
+68f3899 utrace: fix ->report_jctl @notify argument
+c743327 utrace: avoid unnecessary list_for_each_safe
+acd516b utrace_stop: trivial, kill the unnecessary assignment
+81ed517 utrace_add_engine: add missing 'else' after 'if (utrace->reap)'
+215a076 utrace: tracehook.h comment
+a584c66 utrace: fix utrace_attach_delay() creator test
+827ec3b utrace: comment ->reporting implementation
+07732b4 utrace-ptrace: handle -ERESTARTNOINTR from utrace_attach_task
+2233b06 utrace: finish utrace_reap conversion after indirect->direct struct utrace
+dd30e86 utrace: fix utrace_attach_delay() to loop, remove struct utrace.cloning field
+be4f357 get_utrace_lock: kill the bogus engine->kref.refcount check
+c367207 utrace: clear struct in utrace_init_task
+94f168c utrace: define UTRACE_API_VERSION
+742f120 utrace: place struct utrace directly in task_struct
+cb25a58 utrace: comment fixes
+2b834a5 utrace-ptrace: struct utrace_attached_engine -> struct utrace_engine
+6b8306a utrace: struct utrace_attached_engine -> struct utrace_engine
+9fe3bac utrace-ptrace: Kconfig doc update
+5bb0052 utrace: cosmetic changes
+556a7e7 utrace-ptrace: fix resuming with blocked signal
+3a9f4c8 utrace: order utrace_control() after callback return value processing
+269150d Cosmetic reorganization to further simplify utrace pointer vs embedded-struct.
+ea30176 Use task_utrace_struct() helper in utrace_interrupt_pending().
+ed2098a Use task_utrace_struct() helper
+97d5cde cosmetic code reorganization
+4e8a7ca Remove UTRACE_DEBUG hacks
+25fb674 utrace: exclude PTRACE_TRACEME
+f286be7 utrace-ptrace: remove utrace_engine_put stub
+e0c36bd Disable mutual exclusion if CONFIG_UTRACE_PTRACE
+c93d704 utrace/ptrace mutual exclusion
+594f22c cond_resched() before race-restart in utrace_attach_task
+0da72f3 Clean up utrace_attach_task code.
+fd3d457 utrace: ptrace cooperation
+f357a74 utrace core
+---
+ Documentation/DocBook/Makefile | 2 +-
+ Documentation/DocBook/utrace.tmpl | 590 +++++++++
+ fs/proc/array.c | 3 +
+ include/linux/ptrace.h | 3 +-
+ include/linux/sched.h | 6 +
+ include/linux/tracehook.h | 97 ++-
+ include/linux/utrace.h | 692 +++++++++++
+ init/Kconfig | 9 +
+ kernel/Makefile | 2 +
+ kernel/fork.c | 3 +
+ kernel/ptrace-utrace.c | 1127 +++++++++++++++++
+ kernel/ptrace.c | 620 +++++-----
+ kernel/signal.c | 4 +-
+ kernel/utrace.c | 2452 +++++++++++++++++++++++++++++++++++++
+ 14 files changed, 5291 insertions(+), 319 deletions(-)
+ create mode 100644 Documentation/DocBook/utrace.tmpl
+ create mode 100644 include/linux/utrace.h
+ create mode 100644 kernel/ptrace-utrace.c
+ create mode 100644 kernel/utrace.c
+
+diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile
+index c7e5dc7..e63f889 100644
+--- a/Documentation/DocBook/Makefile
++++ b/Documentation/DocBook/Makefile
+@@ -14,7 +14,7 @@ DOCBOOKS := z8530book.xml mcabook.xml device-drivers.xml \
+ genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \
+ mac80211.xml debugobjects.xml sh.xml regulator.xml \
+ alsa-driver-api.xml writing-an-alsa-driver.xml \
+- tracepoint.xml media.xml drm.xml
++ tracepoint.xml utrace.xml media.xml drm.xml
+
+ ###
+ # The build process is as follows (targets):
+diff --git a/Documentation/DocBook/utrace.tmpl b/Documentation/DocBook/utrace.tmpl
+new file mode 100644
+index 0000000..e149f49
+--- /dev/null
++++ b/Documentation/DocBook/utrace.tmpl
+@@ -0,0 +1,590 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
++"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
++
++<book id="utrace">
++ <bookinfo>
++ <title>The utrace User Debugging Infrastructure</title>
++ </bookinfo>
++
++ <toc></toc>
++
++ <chapter id="concepts"><title>utrace concepts</title>
++
++ <sect1 id="intro"><title>Introduction</title>
++
++ <para>
++ <application>utrace</application> is infrastructure code for tracing
++ and controlling user threads. This is the foundation for writing
++ tracing engines, which can be loadable kernel modules.
++ </para>
++
++ <para>
++ The basic actors in <application>utrace</application> are the thread
++ and the tracing engine. A tracing engine is some body of code that
++ calls into the <filename>&lt;linux/utrace.h&gt;</filename>
++ interfaces, represented by a <structname>struct
++ utrace_engine_ops</structname>. (Usually it's a kernel module,
++ though the legacy <function>ptrace</function> support is a tracing
++ engine that is not in a kernel module.) The interface operates on
++ individual threads (<structname>struct task_struct</structname>).
++ If an engine wants to treat several threads as a group, that is up
++ to its higher-level code.
++ </para>
++
++ <para>
++ Tracing begins by attaching an engine to a thread, using
++ <function>utrace_attach_task</function> or
++ <function>utrace_attach_pid</function>. If successful, it returns a
++ pointer that is the handle used in all other calls.
++ </para>
++
++ </sect1>
++
++ <sect1 id="callbacks"><title>Events and Callbacks</title>
++
++ <para>
++ An attached engine does nothing by default. An engine makes something
++ happen by requesting callbacks via <function>utrace_set_events</function>
++ and poking the thread with <function>utrace_control</function>.
++ The synchronization issues related to these two calls
++ are discussed further below in <xref linkend="teardown"/>.
++ </para>
++
++ <para>
++ Events are specified using the macro
++ <constant>UTRACE_EVENT(<replaceable>type</replaceable>)</constant>.
++ Each event type is associated with a callback in <structname>struct
++ utrace_engine_ops</structname>. A tracing engine can leave unused
++ callbacks <constant>NULL</constant>. The only callbacks required
++ are those used by the event flags it sets.
++ </para>
++
++ <para>
++ Many engines can be attached to each thread. When a thread has an
++ event, each engine gets a callback if it has set the event flag for
++ that event type. For most events, engines are called in the order they
++ attached. Engines that attach after the event has occurred do not get
++ callbacks for that event. This includes any new engines just attached
++ by an existing engine's callback function. Once the sequence of
++ callbacks for that one event has completed, such new engines are then
++ eligible in the next sequence that starts when there is another event.
++ </para>
++
++ <para>
++ Event reporting callbacks have details particular to the event type,
++ but are all called in similar environments and have the same
++ constraints. Callbacks are made from safe points, where no locks
++ are held, no special resources are pinned (usually), and the
++ user-mode state of the thread is accessible. So, callback code has
++ a pretty free hand. But to be a good citizen, callback code should
++ never block for long periods. It is fine to block in
++ <function>kmalloc</function> and the like, but never wait for i/o or
++ for user mode to do something. If you need the thread to wait, use
++ <constant>UTRACE_STOP</constant> and return from the callback
++ quickly. When your i/o finishes or whatever, you can use
++ <function>utrace_control</function> to resume the thread.
++ </para>
++
++ <para>
++ The <constant>UTRACE_EVENT(SYSCALL_ENTRY)</constant> event is a special
++ case. While other events happen in the kernel when it will return to
++ user mode soon, this event happens when entering the kernel before it
++ will proceed with the work requested from user mode. Because of this
++ difference, the <function>report_syscall_entry</function> callback is
++ special in two ways. For this event, engines are called in reverse of
++ the normal order (this includes the <function>report_quiesce</function>
++ call that precedes a <function>report_syscall_entry</function> call).
++ This preserves the semantics that the last engine to attach is called
++ "closest to user mode"--the engine that is first to see a thread's user
++ state when it enters the kernel is also the last to see that state when
++ the thread returns to user mode. For the same reason, if these
++ callbacks use <constant>UTRACE_STOP</constant> (see the next section),
++ the thread stops immediately after callbacks rather than only when it's
++ ready to return to user mode; when allowed to resume, it will actually
++ attempt the system call indicated by the register values at that time.
++ </para>
++
++ </sect1>
++
++ <sect1 id="safely"><title>Stopping Safely</title>
++
++ <sect2 id="well-behaved"><title>Writing well-behaved callbacks</title>
++
++ <para>
++ Well-behaved callbacks are important to maintain two essential
++ properties of the interface. The first of these is that unrelated
++ tracing engines should not interfere with each other. If your engine's
++ event callback does not return quickly, then another engine won't get
++ the event notification in a timely manner. The second important
++ property is that tracing should be as noninvasive as possible to the
++ normal operation of the system overall and of the traced thread in
++ particular. That is, attached tracing engines should not perturb a
++ thread's behavior, except to the extent that changing its user-visible
++ state is explicitly what you want to do. (Obviously some perturbation
++ is unavoidable, primarily timing changes, ranging from small delays due
++ to the overhead of tracing, to arbitrary pauses in user code execution
++ when a user stops a thread with a debugger for examination.) Even when
++ you explicitly want the perturbation of making the traced thread block,
++ just blocking directly in your callback has more unwanted effects. For
++ example, the <constant>CLONE</constant> event callbacks are called when
++ the new child thread has been created but not yet started running; the
++ child can never be scheduled until the <constant>CLONE</constant>
++ tracing callbacks return. (This allows engines tracing the parent to
++ attach to the child.) If a <constant>CLONE</constant> event callback
++ blocks the parent thread, it also prevents the child thread from
++ running (even to process a <constant>SIGKILL</constant>). If what you
++ want is to make both the parent and child block, then use
++ <function>utrace_attach_task</function> on the child and then use
++ <constant>UTRACE_STOP</constant> on both threads. A more crucial
++ problem with blocking in callbacks is that it can prevent
++ <constant>SIGKILL</constant> from working. A thread that is blocking
++ due to <constant>UTRACE_STOP</constant> will still wake up and die
++ immediately when sent a <constant>SIGKILL</constant>, as all threads
++ should. Relying on the <application>utrace</application>
++ infrastructure rather than on private synchronization calls in event
++ callbacks is an important way to help keep tracing robustly
++ noninvasive.
++ </para>
++
++ </sect2>
++
++ <sect2 id="UTRACE_STOP"><title>Using <constant>UTRACE_STOP</constant></title>
++
++ <para>
++ To control another thread and access its state, it must be stopped
++ with <constant>UTRACE_STOP</constant>. This means that it is
++ stopped and won't start running again while we access it. When a
++ thread is not already stopped, <function>utrace_control</function>
++ returns <constant>-EINPROGRESS</constant> and an engine must wait
++ for an event callback when the thread is ready to stop. The thread
++ may be running on another CPU or may be blocked. When it is ready
++ to be examined, it will make callbacks to engines that set the
++ <constant>UTRACE_EVENT(QUIESCE)</constant> event bit. To wake up an
++ interruptible wait, use <constant>UTRACE_INTERRUPT</constant>.
++ </para>
++
++ <para>
++ As long as some engine has used <constant>UTRACE_STOP</constant> and
++ not called <function>utrace_control</function> to resume the thread,
++ then the thread will remain stopped. <constant>SIGKILL</constant>
++ will wake it up, but it will not run user code. When the stop is
++ cleared with <function>utrace_control</function> or a callback
++ return value, the thread starts running again.
++ (See also <xref linkend="teardown"/>.)
++ </para>
++
++ </sect2>
++
++ </sect1>
++
++ <sect1 id="teardown"><title>Tear-down Races</title>
++
++ <sect2 id="SIGKILL"><title>Primacy of <constant>SIGKILL</constant></title>
++ <para>
++ Ordinarily synchronization issues for tracing engines are kept fairly
++ straightforward by using <constant>UTRACE_STOP</constant>. You ask a
++ thread to stop, and then once it makes the
++ <function>report_quiesce</function> callback it cannot do anything else
++ that would result in another callback, until you let it with a
++ <function>utrace_control</function> call. This simple arrangement
++ avoids complex and error-prone code in each one of a tracing engine's
++ event callbacks to keep them serialized with the engine's other
++ operations done on that thread from another thread of control.
++ However, giving tracing engines complete power to keep a traced thread
++ stuck in place runs afoul of a more important kind of simplicity that
++ the kernel overall guarantees: nothing can prevent or delay
++ <constant>SIGKILL</constant> from making a thread die and release its
++ resources. To preserve this important property of
++ <constant>SIGKILL</constant>, it as a special case can break
++ <constant>UTRACE_STOP</constant> like nothing else normally can. This
++ includes both explicit <constant>SIGKILL</constant> signals and the
++ implicit <constant>SIGKILL</constant> sent to each other thread in the
++ same thread group by a thread doing an exec, or processing a fatal
++ signal, or making an <function>exit_group</function> system call. A
++ tracing engine can prevent a thread from beginning the exit or exec or
++ dying by signal (other than <constant>SIGKILL</constant>) if it is
++ attached to that thread, but once the operation begins, no tracing
++ engine can prevent or delay all other threads in the same thread group
++ dying.
++ </para>
++ </sect2>
++
++ <sect2 id="reap"><title>Final callbacks</title>
++ <para>
++ The <function>report_reap</function> callback is always the final event
++ in the life cycle of a traced thread. Tracing engines can use this as
++ the trigger to clean up their own data structures. The
++ <function>report_death</function> callback is always the penultimate
++ event a tracing engine might see; it's seen unless the thread was
++ already in the midst of dying when the engine attached. Many tracing
++ engines will have no interest in when a parent reaps a dead process,
++ and nothing they want to do with a zombie thread once it dies; for
++ them, the <function>report_death</function> callback is the natural
++ place to clean up data structures and detach. To facilitate writing
++ such engines robustly, given the asynchrony of
++ <constant>SIGKILL</constant>, and without error-prone manual
++ implementation of synchronization schemes, the
++ <application>utrace</application> infrastructure provides some special
++ guarantees about the <function>report_death</function> and
++ <function>report_reap</function> callbacks. It still takes some care
++ to be sure your tracing engine is robust to tear-down races, but these
++ rules make it reasonably straightforward and concise to handle a lot of
++ corner cases correctly.
++ </para>
++ </sect2>
++
++ <sect2 id="refcount"><title>Engine and task pointers</title>
++ <para>
++ The first sort of guarantee concerns the core data structures
++ themselves. <structname>struct utrace_engine</structname> is
++ a reference-counted data structure. While you hold a reference, an
++ engine pointer will always stay valid so that you can safely pass it to
++ any <application>utrace</application> call. Each call to
++ <function>utrace_attach_task</function> or
++ <function>utrace_attach_pid</function> returns an engine pointer with a
++ reference belonging to the caller. You own that reference until you
++ drop it using <function>utrace_engine_put</function>. There is an
++ implicit reference on the engine while it is attached. So if you drop
++ your only reference, and then use
++ <function>utrace_attach_task</function> without
++ <constant>UTRACE_ATTACH_CREATE</constant> to look up that same engine,
++ you will get the same pointer with a new reference to replace the one
++ you dropped, just like calling <function>utrace_engine_get</function>.
++ When an engine has been detached, either explicitly with
++ <constant>UTRACE_DETACH</constant> or implicitly after
++ <function>report_reap</function>, then any references you hold are all
++ that keep the old engine pointer alive.
++ </para>
++
++ <para>
++ There is nothing a kernel module can do to keep a <structname>struct
++ task_struct</structname> alive outside of
++ <function>rcu_read_lock</function>. When the task dies and is reaped
++ by its parent (or itself), that structure can be freed so that any
++ dangling pointers you have stored become invalid.
++ <application>utrace</application> will not prevent this, but it can
++ help you detect it safely. By definition, a task that has been reaped
++ has had all its engines detached. All
++ <application>utrace</application> calls can be safely called on a
++ detached engine if the caller holds a reference on that engine pointer,
++ even if the task pointer passed in the call is invalid. All calls
++ return <constant>-ESRCH</constant> for a detached engine, which tells
++ you that the task pointer you passed could be invalid now. Since
++ <function>utrace_control</function> and
++ <function>utrace_set_events</function> do not block, you can call those
++ inside a <function>rcu_read_lock</function> section and be sure after
++ they don't return <constant>-ESRCH</constant> that the task pointer is
++ still valid until <function>rcu_read_unlock</function>. The
++ infrastructure never holds task references of its own. Though neither
++ <function>rcu_read_lock</function> nor any other lock is held while
++ making a callback, it's always guaranteed that the <structname>struct
++ task_struct</structname> and the <structname>struct
++ utrace_engine</structname> passed as arguments remain valid
++ until the callback function returns.
++ </para>
++
++ <para>
++ The common means for safely holding task pointers that is available to
++ kernel modules is to use <structname>struct pid</structname>, which
++ permits <function>put_pid</function> from kernel modules. When using
++ that, the calls <function>utrace_attach_pid</function>,
++ <function>utrace_control_pid</function>,
++ <function>utrace_set_events_pid</function>, and
++ <function>utrace_barrier_pid</function> are available.
++ </para>
++ </sect2>
++
++ <sect2 id="reap-after-death">
++ <title>
++ Serialization of <constant>DEATH</constant> and <constant>REAP</constant>
++ </title>
++ <para>
++ The second guarantee is the serialization of
++ <constant>DEATH</constant> and <constant>REAP</constant> event
++ callbacks for a given thread. The actual reaping by the parent
++ (<function>release_task</function> call) can occur simultaneously
++ while the thread is still doing the final steps of dying, including
++ the <function>report_death</function> callback. If a tracing engine
++ has requested both <constant>DEATH</constant> and
++ <constant>REAP</constant> event reports, it's guaranteed that the
++ <function>report_reap</function> callback will not be made until
++ after the <function>report_death</function> callback has returned.
++ If the <function>report_death</function> callback itself detaches
++ from the thread, then the <function>report_reap</function> callback
++ will never be made. Thus it is safe for a
++ <function>report_death</function> callback to clean up data
++ structures and detach.
++ </para>
++ </sect2>
++
++ <sect2 id="interlock"><title>Interlock with final callbacks</title>
++ <para>
++ The final sort of guarantee is that a tracing engine will know for sure
++ whether or not the <function>report_death</function> and/or
++ <function>report_reap</function> callbacks will be made for a certain
++ thread. These tear-down races are disambiguated by the error return
++ values of <function>utrace_set_events</function> and
++ <function>utrace_control</function>. Normally
++ <function>utrace_control</function> called with
++ <constant>UTRACE_DETACH</constant> returns zero, and this means that no
++ more callbacks will be made. If the thread is in the midst of dying,
++ it returns <constant>-EALREADY</constant> to indicate that the
++ <constant>report_death</constant> callback may already be in progress;
++ when you get this error, you know that any cleanup your
++ <function>report_death</function> callback does is about to happen or
++ has just happened--note that if the <function>report_death</function>
++ callback does not detach, the engine remains attached until the thread
++ gets reaped. If the thread is in the midst of being reaped,
++ <function>utrace_control</function> returns <constant>-ESRCH</constant>
++ to indicate that the <function>report_reap</function> callback may
++ already be in progress; this means the engine is implicitly detached
++ when the callback completes. This makes it possible for a tracing
++ engine that has decided asynchronously to detach from a thread to
++ safely clean up its data structures, knowing that no
++ <function>report_death</function> or <function>report_reap</function>
++ callback will try to do the same. <constant>utrace_detach</constant>
++ returns <constant>-ESRCH</constant> when the <structname>struct
++ utrace_engine</structname> has already been detached, but is
++ still a valid pointer because of its reference count. A tracing engine
++ can use this to safely synchronize its own independent multiple threads
++ of control with each other and with its event callbacks that detach.
++ </para>
++
++ <para>
++ In the same vein, <function>utrace_set_events</function> normally
++ returns zero; if the target thread was stopped before the call, then
++ after a successful call, no event callbacks not requested in the new
++ flags will be made. It fails with <constant>-EALREADY</constant> if
++ you try to clear <constant>UTRACE_EVENT(DEATH)</constant> when the
++ <function>report_death</function> callback may already have begun, if
++ you try to clear <constant>UTRACE_EVENT(REAP)</constant> when the
++ <function>report_reap</function> callback may already have begun, or if
++ you try to newly set <constant>UTRACE_EVENT(DEATH)</constant> or
++ <constant>UTRACE_EVENT(QUIESCE)</constant> when the target is already
++ dead or dying. Like <function>utrace_control</function>, it returns
++ <constant>-ESRCH</constant> when the thread has already been detached
++ (including forcible detach on reaping). This lets the tracing engine
++ know for sure which event callbacks it will or won't see after
++ <function>utrace_set_events</function> has returned. By checking for
++ errors, it can know whether to clean up its data structures immediately
++ or to let its callbacks do the work.
++ </para>
++ </sect2>
++
++ <sect2 id="barrier"><title>Using <function>utrace_barrier</function></title>
++ <para>
++ When a thread is safely stopped, calling
++ <function>utrace_control</function> with <constant>UTRACE_DETACH</constant>
++ or calling <function>utrace_set_events</function> to disable some events
++ ensures synchronously that your engine won't get any more of the callbacks
++ that have been disabled (none at all when detaching). But these can also
++ be used while the thread is not stopped, when it might be simultaneously
++ making a callback to your engine. For this situation, these calls return
++ <constant>-EINPROGRESS</constant> when it's possible a callback is in
++ progress. If you are not prepared to have your old callbacks still run,
++ then you can synchronize to be sure all the old callbacks are finished,
++ using <function>utrace_barrier</function>. This is necessary if the
++ kernel module containing your callback code is going to be unloaded.
++ </para>
++ <para>
++ After using <constant>UTRACE_DETACH</constant> once, further calls to
++ <function>utrace_control</function> with the same engine pointer will
++ return <constant>-ESRCH</constant>. In contrast, after getting
++ <constant>-EINPROGRESS</constant> from
++ <function>utrace_set_events</function>, you can call
++ <function>utrace_set_events</function> again later and if it returns zero
++ then know the old callbacks have finished.
++ </para>
++ <para>
++ Unlike all other calls, <function>utrace_barrier</function> (and
++ <function>utrace_barrier_pid</function>) will accept any engine pointer you
++ hold a reference on, even if <constant>UTRACE_DETACH</constant> has already
++ been used. After any <function>utrace_control</function> or
++ <function>utrace_set_events</function> call (these do not block), you can
++ call <function>utrace_barrier</function> to block until callbacks have
++ finished. This returns <constant>-ESRCH</constant> only if the engine is
++ completely detached (finished all callbacks). Otherwise it waits
++ until the thread is definitely not in the midst of a callback to this
++ engine and then returns zero, but can return
++ <constant>-ERESTARTSYS</constant> if its wait is interrupted.
++ </para>
++ </sect2>
++
++</sect1>
++
++</chapter>
++
++<chapter id="core"><title>utrace core API</title>
++
++<para>
++ The utrace API is declared in <filename>&lt;linux/utrace.h&gt;</filename>.
++</para>
++
++!Iinclude/linux/utrace.h
++!Ekernel/utrace.c
++
++</chapter>
++
++<chapter id="machine"><title>Machine State</title>
++
++<para>
++ The <function>task_current_syscall</function> function can be used on any
++ valid <structname>struct task_struct</structname> at any time, and does
++ not even require that <function>utrace_attach_task</function> was used at all.
++</para>
++
++<para>
++ The other ways to access the registers and other machine-dependent state of
++ a task can only be used on a task that is at a known safe point. The safe
++ points are all the places where <function>utrace_set_events</function> can
++ request callbacks (except for the <constant>DEATH</constant> and
++ <constant>REAP</constant> events). So at any event callback, it is safe to
++ examine <varname>current</varname>.
++</para>
++
++<para>
++ One task can examine another only after a callback in the target task that
++ returns <constant>UTRACE_STOP</constant> so that task will not return to user
++ mode after the safe point. This guarantees that the task will not resume
++ until the same engine uses <function>utrace_control</function>, unless the
++ task dies suddenly. To examine safely, one must use a pair of calls to
++ <function>utrace_prepare_examine</function> and
++ <function>utrace_finish_examine</function> surrounding the calls to
++ <structname>struct user_regset</structname> functions or direct examination
++ of task data structures. <function>utrace_prepare_examine</function> returns
++ an error if the task is not properly stopped, or is dead. After a
++ successful examination, the paired <function>utrace_finish_examine</function>
++ call returns an error if the task ever woke up during the examination. If
++ so, any data gathered may be scrambled and should be discarded. This means
++ there was a spurious wake-up (which should not happen), or a sudden death.
++</para>
++
++<sect1 id="regset"><title><structname>struct user_regset</structname></title>
++
++<para>
++ The <structname>struct user_regset</structname> API
++ is declared in <filename>&lt;linux/regset.h&gt;</filename>.
++</para>
++
++!Finclude/linux/regset.h
++
++</sect1>
++
++<sect1 id="task_current_syscall">
++ <title><filename>System Call Information</filename></title>
++
++<para>
++ This function is declared in <filename>&lt;linux/ptrace.h&gt;</filename>.
++</para>
++
++!Elib/syscall.c
++
++</sect1>
++
++<sect1 id="syscall"><title><filename>System Call Tracing</filename></title>
++
++<para>
++ The arch API for system call information is declared in
++ <filename>&lt;asm/syscall.h&gt;</filename>.
++ Each of these calls can be used only at system call entry tracing,
++ or can be used only at system call exit and the subsequent safe points
++ before returning to user mode.
++ At system call entry tracing means either during a
++ <structfield>report_syscall_entry</structfield> callback,
++ or any time after that callback has returned <constant>UTRACE_STOP</constant>.
++</para>
++
++!Finclude/asm-generic/syscall.h
++
++</sect1>
++
++</chapter>
++
++<chapter id="internals"><title>Kernel Internals</title>
++
++<para>
++ This chapter covers the interface to the tracing infrastructure
++ from the core of the kernel and the architecture-specific code.
++ This is for maintainers of the kernel and arch code, and not relevant
++ to using the tracing facilities described in preceding chapters.
++</para>
++
++<sect1 id="tracehook"><title>Core Calls In</title>
++
++<para>
++ These calls are declared in <filename>&lt;linux/tracehook.h&gt;</filename>.
++ The core kernel calls these functions at various important places.
++</para>
++
++!Finclude/linux/tracehook.h
++
++</sect1>
++
++<sect1 id="arch"><title>Architecture Calls Out</title>
++
++<para>
++ An arch that has done all these things sets
++ <constant>CONFIG_HAVE_ARCH_TRACEHOOK</constant>.
++ This is required to enable the <application>utrace</application> code.
++</para>
++
++<sect2 id="arch-ptrace"><title><filename>&lt;asm/ptrace.h&gt;</filename></title>
++
++<para>
++ An arch defines these in <filename>&lt;asm/ptrace.h&gt;</filename>
++ if it supports hardware single-step or block-step features.
++</para>
++
++!Finclude/linux/ptrace.h arch_has_single_step arch_has_block_step
++!Finclude/linux/ptrace.h user_enable_single_step user_enable_block_step
++!Finclude/linux/ptrace.h user_disable_single_step
++
++</sect2>
++
++<sect2 id="arch-syscall">
++ <title><filename>&lt;asm/syscall.h&gt;</filename></title>
++
++ <para>
++ An arch provides <filename>&lt;asm/syscall.h&gt;</filename> that
++ defines these as inlines, or declares them as exported functions.
++ These interfaces are described in <xref linkend="syscall"/>.
++ </para>
++
++</sect2>
++
++<sect2 id="arch-tracehook">
++ <title><filename>&lt;linux/tracehook.h&gt;</filename></title>
++
++ <para>
++ An arch must define <constant>TIF_NOTIFY_RESUME</constant>
++ and <constant>TIF_SYSCALL_TRACE</constant>
++ in its <filename>&lt;asm/thread_info.h&gt;</filename>.
++ The arch code must call the following functions, all declared
++ in <filename>&lt;linux/tracehook.h&gt;</filename> and
++ described in <xref linkend="tracehook"/>:
++
++ <itemizedlist>
++ <listitem>
++ <para><function>tracehook_notify_resume</function></para>
++ </listitem>
++ <listitem>
++ <para><function>tracehook_report_syscall_entry</function></para>
++ </listitem>
++ <listitem>
++ <para><function>tracehook_report_syscall_exit</function></para>
++ </listitem>
++ <listitem>
++ <para><function>tracehook_signal_handler</function></para>
++ </listitem>
++ </itemizedlist>
++
++ </para>
++
++</sect2>
++
++</sect1>
++
++</chapter>
++
++</book>
+diff --git a/fs/proc/array.c b/fs/proc/array.c
+index 9b58d38..c7c7881 100644
+--- a/fs/proc/array.c
++++ b/fs/proc/array.c
+@@ -81,6 +81,7 @@
+ #include <linux/pid_namespace.h>
+ #include <linux/ptrace.h>
+ #include <linux/tracehook.h>
++#include <linux/utrace.h>
+
+ #include <asm/pgtable.h>
+ #include <asm/processor.h>
+@@ -192,6 +193,8 @@ static inline void task_state(struct seq_file *m, struct pid_namespace *ns,
+ cred->uid, cred->euid, cred->suid, cred->fsuid,
+ cred->gid, cred->egid, cred->sgid, cred->fsgid);
+
++ task_utrace_proc_status(m, p);
++
+ task_lock(p);
+ if (p->files)
+ fdt = files_fdtable(p->files);
+diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
+index 4272521..235c1b0 100644
+--- a/include/linux/ptrace.h
++++ b/include/linux/ptrace.h
+@@ -99,12 +99,13 @@
+ #include <linux/compiler.h> /* For unlikely. */
+ #include <linux/sched.h> /* For struct task_struct. */
+
+-
++extern void ptrace_notify_stop(struct task_struct *tracee);
+ extern long arch_ptrace(struct task_struct *child, long request, long addr, long data);
+ extern int ptrace_traceme(void);
+ extern int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len);
+ extern int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len);
+ extern int ptrace_attach(struct task_struct *tsk);
++extern bool __ptrace_detach(struct task_struct *tracer, struct task_struct *tracee);
+ extern int ptrace_detach(struct task_struct *, unsigned int);
+ extern void ptrace_disable(struct task_struct *);
+ extern int ptrace_check_attach(struct task_struct *task, int kill);
+diff --git a/include/linux/sched.h b/include/linux/sched.h
+index f118809..d3fef7a 100644
+--- a/include/linux/sched.h
++++ b/include/linux/sched.h
+@@ -1348,6 +1348,11 @@ struct task_struct {
+ #endif
+ seccomp_t seccomp;
+
++#ifdef CONFIG_UTRACE
++ struct utrace *utrace;
++ unsigned long utrace_flags;
++#endif
++
+ /* Thread group tracking */
+ u32 parent_exec_id;
+ u32 self_exec_id;
+@@ -2033,6 +2038,7 @@ extern int kill_pgrp(struct pid *pid, int sig, int priv);
+ extern int kill_pid(struct pid *pid, int sig, int priv);
+ extern int kill_proc_info(int, struct siginfo *, pid_t);
+ extern int do_notify_parent(struct task_struct *, int);
++extern void do_notify_parent_cldstop(struct task_struct *, int);
+ extern void __wake_up_parent(struct task_struct *p, struct task_struct *parent);
+ extern void force_sig(int, struct task_struct *);
+ extern int send_sig(int, struct task_struct *, int);
+diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h
+index 10db010..71fa250 100644
+--- a/include/linux/tracehook.h
++++ b/include/linux/tracehook.h
+@@ -49,6 +49,7 @@
+ #include <linux/sched.h>
+ #include <linux/ptrace.h>
+ #include <linux/security.h>
++#include <linux/utrace.h>
+ struct linux_binprm;
+
+ /**
+@@ -63,6 +64,8 @@ struct linux_binprm;
+ */
+ static inline int tracehook_expect_breakpoints(struct task_struct *task)
+ {
++ if (unlikely(task_utrace_flags(task) & UTRACE_EVENT(SIGNAL_CORE)))
++ return 1;
+ return (task_ptrace(task) & PT_PTRACED) != 0;
+ }
+
+@@ -111,6 +114,9 @@ static inline void ptrace_report_syscall(struct pt_regs *regs)
+ static inline __must_check int tracehook_report_syscall_entry(
+ struct pt_regs *regs)
+ {
++ if ((task_utrace_flags(current) & UTRACE_EVENT(SYSCALL_ENTRY)) &&
++ utrace_report_syscall_entry(regs))
++ return 1;
+ ptrace_report_syscall(regs);
+ return 0;
+ }
+@@ -134,7 +140,10 @@ static inline __must_check int tracehook_report_syscall_entry(
+ */
+ static inline void tracehook_report_syscall_exit(struct pt_regs *regs, int step)
+ {
+- if (step) {
++ if (task_utrace_flags(current) & UTRACE_EVENT(SYSCALL_EXIT))
++ utrace_report_syscall_exit(regs);
++
++ if (step && (task_ptrace(current) & PT_PTRACED)) {
+ siginfo_t info;
+ user_single_step_siginfo(current, regs, &info);
+ force_sig_info(SIGTRAP, &info, current);
+@@ -156,7 +165,7 @@ static inline int tracehook_unsafe_exec(struct task_struct *task)
+ {
+ int unsafe = 0;
+ int ptrace = task_ptrace(task);
+- if (ptrace & PT_PTRACED) {
++ if (ptrace) {
+ if (ptrace & PT_PTRACE_CAP)
+ unsafe |= LSM_UNSAFE_PTRACE_CAP;
+ else
+@@ -178,7 +187,7 @@ static inline int tracehook_unsafe_exec(struct task_struct *task)
+ */
+ static inline struct task_struct *tracehook_tracer_task(struct task_struct *tsk)
+ {
+- if (task_ptrace(tsk) & PT_PTRACED)
++ if (task_ptrace(tsk))
+ return rcu_dereference(tsk->parent);
+ return NULL;
+ }
+@@ -201,6 +210,8 @@ static inline void tracehook_report_exec(struct linux_binfmt *fmt,
+ struct linux_binprm *bprm,
+ struct pt_regs *regs)
+ {
++ if (unlikely(task_utrace_flags(current) & UTRACE_EVENT(EXEC)))
++ utrace_report_exec(fmt, bprm, regs);
+ if (!ptrace_event(PT_TRACE_EXEC, PTRACE_EVENT_EXEC, 0) &&
+ unlikely(task_ptrace(current) & PT_PTRACED))
+ send_sig(SIGTRAP, current, 0);
+@@ -218,10 +229,37 @@ static inline void tracehook_report_exec(struct linux_binfmt *fmt,
+ */
+ static inline void tracehook_report_exit(long *exit_code)
+ {
++ if (unlikely(task_utrace_flags(current) & UTRACE_EVENT(EXIT)))
++ utrace_report_exit(exit_code);
+ ptrace_event(PT_TRACE_EXIT, PTRACE_EVENT_EXIT, *exit_code);
+ }
+
+ /**
++ * tracehook_init_task - task_struct has just been copied
++ * @task: new &struct task_struct just copied from parent
++ *
++ * Called from do_fork() when @task has just been duplicated.
++ * After this, @task will be passed to tracehook_free_task()
++ * even if the rest of its setup fails before it is fully created.
++ */
++static inline void tracehook_init_task(struct task_struct *task)
++{
++ utrace_init_task(task);
++}
++
++/**
++ * tracehook_free_task - task_struct is being freed
++ * @task: dead &struct task_struct being freed
++ *
++ * Called from free_task() when @task is no longer in use.
++ */
++static inline void tracehook_free_task(struct task_struct *task)
++{
++ if (task_utrace_struct(task))
++ utrace_free_task(task);
++}
++
++/**
+ * tracehook_prepare_clone - prepare for new child to be cloned
+ * @clone_flags: %CLONE_* flags from clone/fork/vfork system call
+ *
+@@ -285,6 +323,8 @@ static inline void tracehook_report_clone(struct pt_regs *regs,
+ unsigned long clone_flags,
+ pid_t pid, struct task_struct *child)
+ {
++ if (unlikely(task_utrace_flags(current) & UTRACE_EVENT(CLONE)))
++ utrace_report_clone(clone_flags, child);
+ if (unlikely(task_ptrace(child))) {
+ /*
+ * It doesn't matter who attached/attaching to this
+@@ -317,6 +357,9 @@ static inline void tracehook_report_clone_complete(int trace,
+ pid_t pid,
+ struct task_struct *child)
+ {
++ if (unlikely(task_utrace_flags(current) & UTRACE_EVENT(CLONE)) &&
++ (clone_flags & CLONE_VFORK))
++ utrace_finish_vfork(current);
+ if (unlikely(trace))
+ ptrace_event(0, trace, pid);
+ }
+@@ -351,6 +394,10 @@ static inline void tracehook_report_vfork_done(struct task_struct *child,
+ */
+ static inline void tracehook_prepare_release_task(struct task_struct *task)
+ {
++ /* see utrace_add_engine() about this barrier */
++ smp_mb();
++ if (task_utrace_flags(task))
++ utrace_maybe_reap(task, task_utrace_struct(task), true);
+ }
+
+ /**
+@@ -365,6 +412,7 @@ static inline void tracehook_prepare_release_task(struct task_struct *task)
+ static inline void tracehook_finish_release_task(struct task_struct *task)
+ {
+ ptrace_release_task(task);
++ BUG_ON(task->exit_state != EXIT_DEAD);
+ }
+
+ /**
+@@ -386,7 +434,9 @@ static inline void tracehook_signal_handler(int sig, siginfo_t *info,
+ const struct k_sigaction *ka,
+ struct pt_regs *regs, int stepping)
+ {
+- if (stepping)
++ if (task_utrace_flags(current))
++ utrace_signal_handler(current, stepping);
++ if (stepping && (task_ptrace(current) & PT_PTRACED))
+ ptrace_notify(SIGTRAP);
+ }
+
+@@ -403,6 +453,8 @@ static inline void tracehook_signal_handler(int sig, siginfo_t *info,
+ static inline int tracehook_consider_ignored_signal(struct task_struct *task,
+ int sig)
+ {
++ if (unlikely(task_utrace_flags(task) & UTRACE_EVENT(SIGNAL_IGN)))
++ return 1;
+ return (task_ptrace(task) & PT_PTRACED) != 0;
+ }
+
+@@ -422,6 +474,9 @@ static inline int tracehook_consider_ignored_signal(struct task_struct *task,
+ static inline int tracehook_consider_fatal_signal(struct task_struct *task,
+ int sig)
+ {
++ if (unlikely(task_utrace_flags(task) & (UTRACE_EVENT(SIGNAL_TERM) |
++ UTRACE_EVENT(SIGNAL_CORE))))
++ return 1;
+ return (task_ptrace(task) & PT_PTRACED) != 0;
+ }
+
+@@ -436,6 +491,8 @@ static inline int tracehook_consider_fatal_signal(struct task_struct *task,
+ */
+ static inline int tracehook_force_sigpending(void)
+ {
++ if (unlikely(task_utrace_flags(current)))
++ return utrace_interrupt_pending();
+ return 0;
+ }
+
+@@ -465,6 +522,8 @@ static inline int tracehook_get_signal(struct task_struct *task,
+ siginfo_t *info,
+ struct k_sigaction *return_ka)
+ {
++ if (unlikely(task_utrace_flags(task)))
++ return utrace_get_signal(task, regs, info, return_ka);
+ return 0;
+ }
+
+@@ -492,7 +551,9 @@ static inline int tracehook_get_signal(struct task_struct *task,
+ */
+ static inline int tracehook_notify_jctl(int notify, int why)
+ {
+- return notify ?: (current->ptrace & PT_PTRACED) ? why : 0;
++ if (task_utrace_flags(current) & UTRACE_EVENT(JCTL))
++ utrace_report_jctl(notify, why);
++ return notify ?: task_ptrace(current) ? why : 0;
+ }
+
+ /**
+@@ -502,6 +563,8 @@ static inline int tracehook_notify_jctl(int notify, int why)
+ */
+ static inline void tracehook_finish_jctl(void)
+ {
++ if (task_utrace_flags(current))
++ utrace_finish_stop();
+ }
+
+ #define DEATH_REAP -1
+@@ -524,6 +587,8 @@ static inline void tracehook_finish_jctl(void)
+ static inline int tracehook_notify_death(struct task_struct *task,
+ void **death_cookie, int group_dead)
+ {
++ *death_cookie = task_utrace_struct(task);
++
+ if (task_detached(task))
+ return task->ptrace ? SIGCHLD : DEATH_REAP;
+
+@@ -560,6 +625,15 @@ static inline void tracehook_report_death(struct task_struct *task,
+ int signal, void *death_cookie,
+ int group_dead)
+ {
++ /*
++ * If utrace_set_events() was just called to enable
++ * UTRACE_EVENT(DEATH), then we are obliged to call
++ * utrace_report_death() and not miss it. utrace_set_events()
++ * checks @task->exit_state under tasklist_lock to synchronize
++ * with exit_notify(), the caller.
++ */
++ if (task_utrace_flags(task) & _UTRACE_DEATH_EVENTS)
++ utrace_report_death(task, death_cookie, group_dead, signal);
+ }
+
+ #ifdef TIF_NOTIFY_RESUME
+@@ -589,10 +663,21 @@ static inline void set_notify_resume(struct task_struct *task)
+ * asynchronously, this will be called again before we return to
+ * user mode.
+ *
+- * Called without locks.
++ * Called without locks. However, on some machines this may be
++ * called with interrupts disabled.
+ */
+ static inline void tracehook_notify_resume(struct pt_regs *regs)
+ {
++ struct task_struct *task = current;
++ /*
++ * Prevent the following store/load from getting ahead of the
++ * caller which clears TIF_NOTIFY_RESUME. This pairs with the
++ * implicit mb() before setting TIF_NOTIFY_RESUME in
++ * set_notify_resume().
++ */
++ smp_mb();
++ if (task_utrace_flags(task))
++ utrace_resume(task, regs);
+ }
+ #endif /* TIF_NOTIFY_RESUME */
+
+diff --git a/include/linux/utrace.h b/include/linux/utrace.h
+new file mode 100644
+index 0000000..f251efe
+--- /dev/null
++++ b/include/linux/utrace.h
+@@ -0,0 +1,692 @@
++/*
++ * utrace infrastructure interface for debugging user processes
++ *
++ * Copyright (C) 2006-2009 Red Hat, Inc. All rights reserved.
++ *
++ * This copyrighted material is made available to anyone wishing to use,
++ * modify, copy, or redistribute it subject to the terms and conditions
++ * of the GNU General Public License v.2.
++ *
++ * Red Hat Author: Roland McGrath.
++ *
++ * This interface allows for notification of interesting events in a
++ * thread. It also mediates access to thread state such as registers.
++ * Multiple unrelated users can be associated with a single thread.
++ * We call each of these a tracing engine.
++ *
++ * A tracing engine starts by calling utrace_attach_task() or
++ * utrace_attach_pid() on the chosen thread, passing in a set of hooks
++ * (&struct utrace_engine_ops), and some associated data. This produces a
++ * &struct utrace_engine, which is the handle used for all other
++ * operations. An attached engine has its ops vector, its data, and an
++ * event mask controlled by utrace_set_events().
++ *
++ * For each event bit that is set, that engine will get the
++ * appropriate ops->report_*() callback when the event occurs. The
++ * &struct utrace_engine_ops need not provide callbacks for an event
++ * unless the engine sets one of the associated event bits.
++ */
++
++#ifndef _LINUX_UTRACE_H
++#define _LINUX_UTRACE_H 1
++
++#include <linux/list.h>
++#include <linux/kref.h>
++#include <linux/signal.h>
++#include <linux/sched.h>
++
++struct linux_binprm;
++struct pt_regs;
++struct utrace;
++struct user_regset;
++struct user_regset_view;
++
++/*
++ * Event bits passed to utrace_set_events().
++ * These appear in &struct task_struct.@utrace_flags
++ * and &struct utrace_engine.@flags.
++ */
++enum utrace_events {
++ _UTRACE_EVENT_QUIESCE, /* Thread is available for examination. */
++ _UTRACE_EVENT_REAP, /* Zombie reaped, no more tracing possible. */
++ _UTRACE_EVENT_CLONE, /* Successful clone/fork/vfork just done. */
++ _UTRACE_EVENT_EXEC, /* Successful execve just completed. */
++ _UTRACE_EVENT_EXIT, /* Thread exit in progress. */
++ _UTRACE_EVENT_DEATH, /* Thread has died. */
++ _UTRACE_EVENT_SYSCALL_ENTRY, /* User entered kernel for system call. */
++ _UTRACE_EVENT_SYSCALL_EXIT, /* Returning to user after system call. */
++ _UTRACE_EVENT_SIGNAL, /* Signal delivery will run a user handler. */
++ _UTRACE_EVENT_SIGNAL_IGN, /* No-op signal to be delivered. */
++ _UTRACE_EVENT_SIGNAL_STOP, /* Signal delivery will suspend. */
++ _UTRACE_EVENT_SIGNAL_TERM, /* Signal delivery will terminate. */
++ _UTRACE_EVENT_SIGNAL_CORE, /* Signal delivery will dump core. */
++ _UTRACE_EVENT_JCTL, /* Job control stop or continue completed. */
++ _UTRACE_NEVENTS
++};
++#define UTRACE_EVENT(type) (1UL << _UTRACE_EVENT_##type)
++
++/*
++ * All the kinds of signal events.
++ * These all use the @report_signal() callback.
++ */
++#define UTRACE_EVENT_SIGNAL_ALL (UTRACE_EVENT(SIGNAL) \
++ | UTRACE_EVENT(SIGNAL_IGN) \
++ | UTRACE_EVENT(SIGNAL_STOP) \
++ | UTRACE_EVENT(SIGNAL_TERM) \
++ | UTRACE_EVENT(SIGNAL_CORE))
++/*
++ * Both kinds of syscall events; these call the @report_syscall_entry()
++ * and @report_syscall_exit() callbacks, respectively.
++ */
++#define UTRACE_EVENT_SYSCALL \
++ (UTRACE_EVENT(SYSCALL_ENTRY) | UTRACE_EVENT(SYSCALL_EXIT))
++
++/*
++ * The event reports triggered synchronously by task death.
++ */
++#define _UTRACE_DEATH_EVENTS (UTRACE_EVENT(DEATH) | UTRACE_EVENT(QUIESCE))
++
++/*
++ * Hooks in <linux/tracehook.h> call these entry points to the utrace dispatch.
++ */
++void utrace_free_task(struct task_struct *);
++bool utrace_interrupt_pending(void);
++void utrace_resume(struct task_struct *, struct pt_regs *);
++void utrace_finish_stop(void);
++void utrace_maybe_reap(struct task_struct *, struct utrace *, bool);
++int utrace_get_signal(struct task_struct *, struct pt_regs *,
++ siginfo_t *, struct k_sigaction *);
++void utrace_report_clone(unsigned long, struct task_struct *);
++void utrace_finish_vfork(struct task_struct *);
++void utrace_report_exit(long *exit_code);
++void utrace_report_death(struct task_struct *, struct utrace *, bool, int);
++void utrace_report_jctl(int notify, int type);
++void utrace_report_exec(struct linux_binfmt *, struct linux_binprm *,
++ struct pt_regs *regs);
++bool utrace_report_syscall_entry(struct pt_regs *);
++void utrace_report_syscall_exit(struct pt_regs *);
++void utrace_signal_handler(struct task_struct *, int);
++
++#ifndef CONFIG_UTRACE
++
++/*
++ * <linux/tracehook.h> uses these accessors to avoid #ifdef CONFIG_UTRACE.
++ */
++static inline unsigned long task_utrace_flags(struct task_struct *task)
++{
++ return 0;
++}
++static inline struct utrace *task_utrace_struct(struct task_struct *task)
++{
++ return NULL;
++}
++static inline void utrace_init_task(struct task_struct *child)
++{
++}
++
++static inline void task_utrace_proc_status(struct seq_file *m,
++ struct task_struct *p)
++{
++}
++
++#else /* CONFIG_UTRACE */
++
++static inline unsigned long task_utrace_flags(struct task_struct *task)
++{
++ return task->utrace_flags;
++}
++
++static inline struct utrace *task_utrace_struct(struct task_struct *task)
++{
++ struct utrace *utrace;
++
++ /*
++ * This barrier ensures that any prior load of task->utrace_flags
++ * is ordered before this load of task->utrace. We use those
++ * utrace_flags checks in the hot path to decide to call into
++ * the utrace code. The first attach installs task->utrace before
++ * setting task->utrace_flags nonzero with implicit barrier in
++ * between, see utrace_add_engine().
++ */
++ smp_rmb();
++ utrace = task->utrace;
++
++ smp_read_barrier_depends(); /* See utrace_task_alloc(). */
++ return utrace;
++}
++
++static inline void utrace_init_task(struct task_struct *task)
++{
++ task->utrace_flags = 0;
++ task->utrace = NULL;
++}
++
++void task_utrace_proc_status(struct seq_file *m, struct task_struct *p);
++
++
++/*
++ * Version number of the API defined in this file. This will change
++ * whenever a tracing engine's code would need some updates to keep
++ * working. We maintain this here for the benefit of tracing engine code
++ * that is developed concurrently with utrace API improvements before they
++ * are merged into the kernel, making LINUX_VERSION_CODE checks unwieldy.
++ */
++#define UTRACE_API_VERSION 20091216
++
++/**
++ * enum utrace_resume_action - engine's choice of action for a traced task
++ * @UTRACE_STOP: Stay quiescent after callbacks.
++ * @UTRACE_INTERRUPT: Make @report_signal() callback soon.
++ * @UTRACE_REPORT: Make some callback soon.
++ * @UTRACE_SINGLESTEP: Resume in user mode for one instruction.
++ * @UTRACE_BLOCKSTEP: Resume in user mode until next branch.
++ * @UTRACE_RESUME: Resume normally in user mode.
++ * @UTRACE_DETACH: Detach my engine (implies %UTRACE_RESUME).
++ *
++ * See utrace_control() for detailed descriptions of each action. This is
++ * encoded in the @action argument and the return value for every callback
++ * with a &u32 return value.
++ *
++ * The order of these is important. When there is more than one engine,
++ * each supplies its choice and the smallest value prevails.
++ */
++enum utrace_resume_action {
++ UTRACE_STOP,
++ UTRACE_INTERRUPT,
++ UTRACE_REPORT,
++ UTRACE_SINGLESTEP,
++ UTRACE_BLOCKSTEP,
++ UTRACE_RESUME,
++ UTRACE_DETACH,
++ UTRACE_RESUME_MAX
++};
++#define UTRACE_RESUME_BITS (ilog2(UTRACE_RESUME_MAX) + 1)
++#define UTRACE_RESUME_MASK ((1 << UTRACE_RESUME_BITS) - 1)
++
++/**
++ * utrace_resume_action - &enum utrace_resume_action from callback action
++ * @action: &u32 callback @action argument or return value
++ *
++ * This extracts the &enum utrace_resume_action from @action,
++ * which is the @action argument to a &struct utrace_engine_ops
++ * callback or the return value from one.
++ */
++static inline enum utrace_resume_action utrace_resume_action(u32 action)
++{
++ return action & UTRACE_RESUME_MASK;
++}
++
++/**
++ * enum utrace_signal_action - disposition of signal
++ * @UTRACE_SIGNAL_DELIVER: Deliver according to sigaction.
++ * @UTRACE_SIGNAL_IGN: Ignore the signal.
++ * @UTRACE_SIGNAL_TERM: Terminate the process.
++ * @UTRACE_SIGNAL_CORE: Terminate with core dump.
++ * @UTRACE_SIGNAL_STOP: Deliver as absolute stop.
++ * @UTRACE_SIGNAL_TSTP: Deliver as job control stop.
++ * @UTRACE_SIGNAL_REPORT: Reporting before pending signals.
++ * @UTRACE_SIGNAL_HANDLER: Reporting after signal handler setup.
++ *
++ * This is encoded in the @action argument and the return value for
++ * a @report_signal() callback. It says what will happen to the
++ * signal described by the &siginfo_t parameter to the callback.
++ *
++ * The %UTRACE_SIGNAL_REPORT value is used in an @action argument when
++ * a tracing report is being made before dequeuing any pending signal.
++ * If this is immediately after a signal handler has been set up, then
++ * %UTRACE_SIGNAL_HANDLER is used instead. A @report_signal callback
++ * that uses %UTRACE_SIGNAL_DELIVER|%UTRACE_SINGLESTEP will ensure
++ * it sees a %UTRACE_SIGNAL_HANDLER report.
++ */
++enum utrace_signal_action {
++ UTRACE_SIGNAL_DELIVER = 0x00,
++ UTRACE_SIGNAL_IGN = 0x10,
++ UTRACE_SIGNAL_TERM = 0x20,
++ UTRACE_SIGNAL_CORE = 0x30,
++ UTRACE_SIGNAL_STOP = 0x40,
++ UTRACE_SIGNAL_TSTP = 0x50,
++ UTRACE_SIGNAL_REPORT = 0x60,
++ UTRACE_SIGNAL_HANDLER = 0x70
++};
++#define UTRACE_SIGNAL_MASK 0xf0
++#define UTRACE_SIGNAL_HOLD 0x100 /* Flag, push signal back on queue. */
++
++/**
++ * utrace_signal_action - &enum utrace_signal_action from callback action
++ * @action: @report_signal callback @action argument or return value
++ *
++ * This extracts the &enum utrace_signal_action from @action, which
++ * is the @action argument to a @report_signal callback or the
++ * return value from one.
++ */
++static inline enum utrace_signal_action utrace_signal_action(u32 action)
++{
++ return action & UTRACE_SIGNAL_MASK;
++}
++
++/**
++ * enum utrace_syscall_action - disposition of system call attempt
++ * @UTRACE_SYSCALL_RUN: Run the system call.
++ * @UTRACE_SYSCALL_ABORT: Don't run the system call.
++ *
++ * This is encoded in the @action argument and the return value for
++ * a @report_syscall_entry callback.
++ */
++enum utrace_syscall_action {
++ UTRACE_SYSCALL_RUN = 0x00,
++ UTRACE_SYSCALL_ABORT = 0x10
++};
++#define UTRACE_SYSCALL_MASK 0xf0
++#define UTRACE_SYSCALL_RESUMED 0x100 /* Flag, report_syscall_entry() repeats */
++
++/**
++ * utrace_syscall_action - &enum utrace_syscall_action from callback action
++ * @action: @report_syscall_entry callback @action or return value
++ *
++ * This extracts the &enum utrace_syscall_action from @action, which
++ * is the @action argument to a @report_syscall_entry callback or the
++ * return value from one.
++ */
++static inline enum utrace_syscall_action utrace_syscall_action(u32 action)
++{
++ return action & UTRACE_SYSCALL_MASK;
++}
++
++/*
++ * Flags for utrace_attach_task() and utrace_attach_pid().
++ */
++#define UTRACE_ATTACH_MATCH_OPS 0x0001 /* Match engines on ops. */
++#define UTRACE_ATTACH_MATCH_DATA 0x0002 /* Match engines on data. */
++#define UTRACE_ATTACH_MATCH_MASK 0x000f
++#define UTRACE_ATTACH_CREATE 0x0010 /* Attach a new engine. */
++#define UTRACE_ATTACH_EXCLUSIVE 0x0020 /* Refuse if existing match. */
++
++/**
++ * struct utrace_engine - per-engine structure
++ * @ops: &struct utrace_engine_ops pointer passed to utrace_attach_task()
++ * @data: engine-private &void * passed to utrace_attach_task()
++ * @flags: event mask set by utrace_set_events() plus internal flag bits
++ *
++ * The task itself never has to worry about engines detaching while
++ * it's doing event callbacks. These structures are removed from the
++ * task's active list only when it's stopped, or by the task itself.
++ *
++ * utrace_engine_get() and utrace_engine_put() maintain a reference count.
++ * When it drops to zero, the structure is freed. One reference is held
++ * implicitly while the engine is attached to its task.
++ */
++struct utrace_engine {
++/* private: */
++ struct kref kref;
++ void (*release)(void *);
++ struct list_head entry;
++
++/* public: */
++ const struct utrace_engine_ops *ops;
++ void *data;
++
++ unsigned long flags;
++};
++
++/**
++ * utrace_engine_get - acquire a reference on a &struct utrace_engine
++ * @engine: &struct utrace_engine pointer
++ *
++ * You must hold a reference on @engine, and you get another.
++ */
++static inline void utrace_engine_get(struct utrace_engine *engine)
++{
++ kref_get(&engine->kref);
++}
++
++void __utrace_engine_release(struct kref *);
++
++/**
++ * utrace_engine_put - release a reference on a &struct utrace_engine
++ * @engine: &struct utrace_engine pointer
++ *
++ * You must hold a reference on @engine, and you lose that reference.
++ * If it was the last one, @engine becomes an invalid pointer.
++ */
++static inline void utrace_engine_put(struct utrace_engine *engine)
++{
++ kref_put(&engine->kref, __utrace_engine_release);
++}
++
++/**
++ * struct utrace_engine_ops - tracing engine callbacks
++ *
++ * Each @report_*() callback corresponds to an %UTRACE_EVENT(*) bit.
++ * utrace_set_events() calls on @engine choose which callbacks will
++ * be made to @engine from @task.
++ *
++ * Most callbacks take an @action argument, giving the resume action
++ * chosen by other tracing engines. All callbacks take an @engine
++ * argument. The @report_reap callback takes a @task argument that
++ * might or might not be @current. All other @report_* callbacks
++ * report an event in the @current task.
++ *
++ * For some calls, @action also includes bits specific to that event
++ * and utrace_resume_action() is used to extract the resume action.
++ * This shows what would happen if @engine wasn't there, or will if
++ * the callback's return value uses %UTRACE_RESUME. This always
++ * starts as %UTRACE_RESUME when no other tracing is being done on
++ * this task.
++ *
++ * All return values contain &enum utrace_resume_action bits. For
++ * some calls, other bits specific to that kind of event are added to
++ * the resume action bits with OR. These are the same bits used in
++ * the @action argument. The resume action returned by a callback
++ * does not override previous engines' choices, it only says what
++ * @engine wants done. What @current actually does is the action that's
++ * most constrained among the choices made by all attached engines.
++ * See utrace_control() for more information on the actions.
++ *
++ * When %UTRACE_STOP is used in @report_syscall_entry, then @current
++ * stops before attempting the system call. In this case, another
++ * @report_syscall_entry callback will follow after @current resumes if
++ * %UTRACE_REPORT or %UTRACE_INTERRUPT was returned by some callback
++ * or passed to utrace_control(). In a second or later callback,
++ * %UTRACE_SYSCALL_RESUMED is set in the @action argument to indicate
++ * a repeat callback still waiting to attempt the same system call
++ * invocation. This repeat callback gives each engine an opportunity
++ * to reexamine registers another engine might have changed while
++ * @current was held in %UTRACE_STOP.
++ *
++ * In other cases, the resume action does not take effect until @current
++ * is ready to check for signals and return to user mode. If there
++ * are more callbacks to be made, the last round of calls determines
++ * the final action. A @report_quiesce callback with @event zero, or
++ * a @report_signal callback, will always be the last one made before
++ * @current resumes. Only %UTRACE_STOP is "sticky"--if @engine returned
++ * %UTRACE_STOP then @current stays stopped unless @engine returns
++ * different from a following callback.
++ *
++ * The report_death() and report_reap() callbacks do not take @action
++ * arguments, and only %UTRACE_DETACH is meaningful in the return value
++ * from a report_death() callback. None of the resume actions applies
++ * to a dead thread.
++ *
++ * All @report_*() hooks are called with no locks held, in a generally
++ * safe environment when we will be returning to user mode soon (or just
++ * entered the kernel). It is fine to block for memory allocation and
++ * the like, but all hooks are asynchronous and must not block on
++ * external events! If you want the thread to block, use %UTRACE_STOP
++ * in your hook's return value; then later wake it up with utrace_control().
++ *
++ * @report_quiesce:
++ * Requested by %UTRACE_EVENT(%QUIESCE).
++ * This does not indicate any event, but just that @current is in a
++ * safe place for examination. This call is made before each specific
++ * event callback, except for @report_reap. The @event argument gives
++ * the %UTRACE_EVENT(@which) value for the event occurring. This
++ * callback might be made for events @engine has not requested, if
++ * some other engine is tracing the event; calling utrace_set_events()
++ * call here can request the immediate callback for this occurrence of
++ * @event. @event is zero when there is no other event, @current is
++ * now ready to check for signals and return to user mode, and some
++ * engine has used %UTRACE_REPORT or %UTRACE_INTERRUPT to request this
++ * callback. For this case, if @report_signal is not %NULL, the
++ * @report_quiesce callback may be replaced with a @report_signal
++ * callback passing %UTRACE_SIGNAL_REPORT in its @action argument,
++ * whenever @current is entering the signal-check path anyway.
++ *
++ * @report_signal:
++ * Requested by %UTRACE_EVENT(%SIGNAL_*) or %UTRACE_EVENT(%QUIESCE).
++ * Use utrace_signal_action() and utrace_resume_action() on @action.
++ * The signal action is %UTRACE_SIGNAL_REPORT when some engine has
++ * used %UTRACE_REPORT or %UTRACE_INTERRUPT; the callback can choose
++ * to stop or to deliver an artificial signal, before pending signals.
++ * It's %UTRACE_SIGNAL_HANDLER instead when signal handler setup just
++ * finished (after a previous %UTRACE_SIGNAL_DELIVER return); this
++ * serves in lieu of any %UTRACE_SIGNAL_REPORT callback requested by
++ * %UTRACE_REPORT or %UTRACE_INTERRUPT, and is also implicitly
++ * requested by %UTRACE_SINGLESTEP or %UTRACE_BLOCKSTEP into the
++ * signal delivery. The other signal actions indicate a signal about
++ * to be delivered; the previous engine's return value sets the signal
++ * action seen by the the following engine's callback. The @info data
++ * can be changed at will, including @info->si_signo. The settings in
++ * @return_ka determines what %UTRACE_SIGNAL_DELIVER does. @orig_ka
++ * is what was in force before other tracing engines intervened, and
++ * it's %NULL when this report began as %UTRACE_SIGNAL_REPORT or
++ * %UTRACE_SIGNAL_HANDLER. For a report without a new signal, @info
++ * is left uninitialized and must be set completely by an engine that
++ * chooses to deliver a signal; if there was a previous @report_signal
++ * callback ending in %UTRACE_STOP and it was just resumed using
++ * %UTRACE_REPORT or %UTRACE_INTERRUPT, then @info is left unchanged
++ * from the previous callback. In this way, the original signal can
++ * be left in @info while returning %UTRACE_STOP|%UTRACE_SIGNAL_IGN
++ * and then found again when resuming with %UTRACE_INTERRUPT.
++ * The %UTRACE_SIGNAL_HOLD flag bit can be OR'd into the return value,
++ * and might be in @action if the previous engine returned it. This
++ * flag asks that the signal in @info be pushed back on @current's queue
++ * so that it will be seen again after whatever action is taken now.
++ *
++ * @report_clone:
++ * Requested by %UTRACE_EVENT(%CLONE).
++ * Event reported for parent, before the new task @child might run.
++ * @clone_flags gives the flags used in the clone system call, or
++ * equivalent flags for a fork() or vfork() system call. This
++ * function can use utrace_attach_task() on @child. Then passing
++ * %UTRACE_STOP to utrace_control() on @child here keeps the child
++ * stopped before it ever runs in user mode, %UTRACE_REPORT or
++ * %UTRACE_INTERRUPT ensures a callback from @child before it
++ * starts in user mode.
++ *
++ * @report_jctl:
++ * Requested by %UTRACE_EVENT(%JCTL).
++ * Job control event; @type is %CLD_STOPPED or %CLD_CONTINUED,
++ * indicating whether we are stopping or resuming now. If @notify
++ * is nonzero, @current is the last thread to stop and so will send
++ * %SIGCHLD to its parent after this callback; @notify reflects
++ * what the parent's %SIGCHLD has in @si_code, which can sometimes
++ * be %CLD_STOPPED even when @type is %CLD_CONTINUED.
++ *
++ * @report_exec:
++ * Requested by %UTRACE_EVENT(%EXEC).
++ * An execve system call has succeeded and the new program is about to
++ * start running. The initial user register state is handy to be tweaked
++ * directly in @regs. @fmt and @bprm gives the details of this exec.
++ *
++ * @report_syscall_entry:
++ * Requested by %UTRACE_EVENT(%SYSCALL_ENTRY).
++ * Thread has entered the kernel to request a system call.
++ * The user register state is handy to be tweaked directly in @regs.
++ * The @action argument contains an &enum utrace_syscall_action,
++ * use utrace_syscall_action() to extract it. The return value
++ * overrides the last engine's action for the system call.
++ * If the final action is %UTRACE_SYSCALL_ABORT, no system call
++ * is made. The details of the system call being attempted can
++ * be fetched here with syscall_get_nr() and syscall_get_arguments().
++ * The parameter registers can be changed with syscall_set_arguments().
++ * See above about the %UTRACE_SYSCALL_RESUMED flag in @action.
++ * Use %UTRACE_REPORT in the return value to guarantee you get
++ * another callback (with %UTRACE_SYSCALL_RESUMED flag) in case
++ * @current stops with %UTRACE_STOP before attempting the system call.
++ *
++ * @report_syscall_exit:
++ * Requested by %UTRACE_EVENT(%SYSCALL_EXIT).
++ * Thread is about to leave the kernel after a system call request.
++ * The user register state is handy to be tweaked directly in @regs.
++ * The results of the system call attempt can be examined here using
++ * syscall_get_error() and syscall_get_return_value(). It is safe
++ * here to call syscall_set_return_value() or syscall_rollback().
++ *
++ * @report_exit:
++ * Requested by %UTRACE_EVENT(%EXIT).
++ * Thread is exiting and cannot be prevented from doing so,
++ * but all its state is still live. The @code value will be
++ * the wait result seen by the parent, and can be changed by
++ * this engine or others. The @orig_code value is the real
++ * status, not changed by any tracing engine. Returning %UTRACE_STOP
++ * here keeps @current stopped before it cleans up its state and dies,
++ * so it can be examined by other processes. When @current is allowed
++ * to run, it will die and get to the @report_death callback.
++ *
++ * @report_death:
++ * Requested by %UTRACE_EVENT(%DEATH).
++ * Thread is really dead now. It might be reaped by its parent at
++ * any time, or self-reap immediately. Though the actual reaping
++ * may happen in parallel, a report_reap() callback will always be
++ * ordered after a report_death() callback.
++ *
++ * @report_reap:
++ * Requested by %UTRACE_EVENT(%REAP).
++ * Called when someone reaps the dead task (parent, init, or self).
++ * This means the parent called wait, or else this was a detached
++ * thread or a process whose parent ignores SIGCHLD.
++ * No more callbacks are made after this one.
++ * The engine is always detached.
++ * There is nothing more a tracing engine can do about this thread.
++ * After this callback, the @engine pointer will become invalid.
++ * The @task pointer may become invalid if get_task_struct() hasn't
++ * been used to keep it alive.
++ * An engine should always request this callback if it stores the
++ * @engine pointer or stores any pointer in @engine->data, so it
++ * can clean up its data structures.
++ * Unlike other callbacks, this can be called from the parent's context
++ * rather than from the traced thread itself--it must not delay the
++ * parent by blocking.
++ *
++ * @release:
++ * If not %NULL, this is called after the last utrace_engine_put()
++ * call for a &struct utrace_engine, which could be implicit after
++ * a %UTRACE_DETACH return from another callback. Its argument is
++ * the engine's @data member.
++ */
++struct utrace_engine_ops {
++ u32 (*report_quiesce)(u32 action, struct utrace_engine *engine,
++ unsigned long event);
++ u32 (*report_signal)(u32 action, struct utrace_engine *engine,
++ struct pt_regs *regs,
++ siginfo_t *info,
++ const struct k_sigaction *orig_ka,
++ struct k_sigaction *return_ka);
++ u32 (*report_clone)(u32 action, struct utrace_engine *engine,
++ unsigned long clone_flags,
++ struct task_struct *child);
++ u32 (*report_jctl)(u32 action, struct utrace_engine *engine,
++ int type, int notify);
++ u32 (*report_exec)(u32 action, struct utrace_engine *engine,
++ const struct linux_binfmt *fmt,
++ const struct linux_binprm *bprm,
++ struct pt_regs *regs);
++ u32 (*report_syscall_entry)(u32 action, struct utrace_engine *engine,
++ struct pt_regs *regs);
++ u32 (*report_syscall_exit)(u32 action, struct utrace_engine *engine,
++ struct pt_regs *regs);
++ u32 (*report_exit)(u32 action, struct utrace_engine *engine,
++ long orig_code, long *code);
++ u32 (*report_death)(struct utrace_engine *engine,
++ bool group_dead, int signal);
++ void (*report_reap)(struct utrace_engine *engine,
++ struct task_struct *task);
++ void (*release)(void *data);
++};
++
++/**
++ * struct utrace_examiner - private state for using utrace_prepare_examine()
++ *
++ * The members of &struct utrace_examiner are private to the implementation.
++ * This data type holds the state from a call to utrace_prepare_examine()
++ * to be used by a call to utrace_finish_examine().
++ */
++struct utrace_examiner {
++/* private: */
++ long state;
++ unsigned long ncsw;
++};
++
++/*
++ * These are the exported entry points for tracing engines to use.
++ * See kernel/utrace.c for their kerneldoc comments with interface details.
++ */
++struct utrace_engine *utrace_attach_task(struct task_struct *, int,
++ const struct utrace_engine_ops *,
++ void *);
++struct utrace_engine *utrace_attach_pid(struct pid *, int,
++ const struct utrace_engine_ops *,
++ void *);
++int __must_check utrace_control(struct task_struct *,
++ struct utrace_engine *,
++ enum utrace_resume_action);
++int __must_check utrace_set_events(struct task_struct *,
++ struct utrace_engine *,
++ unsigned long eventmask);
++int __must_check utrace_barrier(struct task_struct *,
++ struct utrace_engine *);
++int __must_check utrace_prepare_examine(struct task_struct *,
++ struct utrace_engine *,
++ struct utrace_examiner *);
++int __must_check utrace_finish_examine(struct task_struct *,
++ struct utrace_engine *,
++ struct utrace_examiner *);
++
++/**
++ * utrace_control_pid - control a thread being traced by a tracing engine
++ * @pid: thread to affect
++ * @engine: attached engine to affect
++ * @action: &enum utrace_resume_action for thread to do
++ *
++ * This is the same as utrace_control(), but takes a &struct pid
++ * pointer rather than a &struct task_struct pointer. The caller must
++ * hold a ref on @pid, but does not need to worry about the task
++ * staying valid. If it's been reaped so that @pid points nowhere,
++ * then this call returns -%ESRCH.
++ */
++static inline __must_check int utrace_control_pid(
++ struct pid *pid, struct utrace_engine *engine,
++ enum utrace_resume_action action)
++{
++ /*
++ * We don't bother with rcu_read_lock() here to protect the
++ * task_struct pointer, because utrace_control will return
++ * -ESRCH without looking at that pointer if the engine is
++ * already detached. A task_struct pointer can't die before
++ * all the engines are detached in release_task() first.
++ */
++ struct task_struct *task = pid_task(pid, PIDTYPE_PID);
++ return unlikely(!task) ? -ESRCH : utrace_control(task, engine, action);
++}
++
++/**
++ * utrace_set_events_pid - choose which event reports a tracing engine gets
++ * @pid: thread to affect
++ * @engine: attached engine to affect
++ * @eventmask: new event mask
++ *
++ * This is the same as utrace_set_events(), but takes a &struct pid
++ * pointer rather than a &struct task_struct pointer. The caller must
++ * hold a ref on @pid, but does not need to worry about the task
++ * staying valid. If it's been reaped so that @pid points nowhere,
++ * then this call returns -%ESRCH.
++ */
++static inline __must_check int utrace_set_events_pid(
++ struct pid *pid, struct utrace_engine *engine, unsigned long eventmask)
++{
++ struct task_struct *task = pid_task(pid, PIDTYPE_PID);
++ return unlikely(!task) ? -ESRCH :
++ utrace_set_events(task, engine, eventmask);
++}
++
++/**
++ * utrace_barrier_pid - synchronize with simultaneous tracing callbacks
++ * @pid: thread to affect
++ * @engine: engine to affect (can be detached)
++ *
++ * This is the same as utrace_barrier(), but takes a &struct pid
++ * pointer rather than a &struct task_struct pointer. The caller must
++ * hold a ref on @pid, but does not need to worry about the task
++ * staying valid. If it's been reaped so that @pid points nowhere,
++ * then this call returns -%ESRCH.
++ */
++static inline __must_check int utrace_barrier_pid(struct pid *pid,
++ struct utrace_engine *engine)
++{
++ struct task_struct *task = pid_task(pid, PIDTYPE_PID);
++ return unlikely(!task) ? -ESRCH : utrace_barrier(task, engine);
++}
++
++#endif /* CONFIG_UTRACE */
++
++#endif /* linux/utrace.h */
+diff --git a/init/Kconfig b/init/Kconfig
+index 5cff9a9..c0b7f81 100644
+--- a/init/Kconfig
++++ b/init/Kconfig
+@@ -328,6 +328,15 @@ config AUDIT_TREE
+ depends on AUDITSYSCALL
+ select INOTIFY
+
++config UTRACE
++ bool "Infrastructure for tracing and debugging user processes"
++ depends on EXPERIMENTAL
++ depends on HAVE_ARCH_TRACEHOOK
++ help
++ Enable the utrace process tracing interface. This is an internal
++ kernel interface exported to kernel modules, to track events in
++ user threads, extract and change user thread state.
++
+ menu "RCU Subsystem"
+
+ choice
+diff --git a/kernel/Makefile b/kernel/Makefile
+index 057472f..dfdc01c 100644
+--- a/kernel/Makefile
++++ b/kernel/Makefile
+@@ -70,6 +70,8 @@ obj-$(CONFIG_IKCONFIG) += configs.o
+ obj-$(CONFIG_RESOURCE_COUNTERS) += res_counter.o
+ obj-$(CONFIG_SMP) += stop_machine.o
+ obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o
++obj-$(CONFIG_UTRACE) += utrace.o
++obj-$(CONFIG_UTRACE) += ptrace-utrace.o
+ obj-$(CONFIG_AUDIT) += audit.o auditfilter.o audit_watch.o
+ obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
+ obj-$(CONFIG_GCOV_KERNEL) += gcov/
+diff --git a/kernel/fork.c b/kernel/fork.c
+index b6cce14..ac4a6ec 100644
+--- a/kernel/fork.c
++++ b/kernel/fork.c
+@@ -161,6 +161,7 @@ void free_task(struct task_struct *tsk)
+ free_thread_info(tsk->stack);
+ rt_mutex_debug_task_free(tsk);
+ ftrace_graph_exit_task(tsk);
++ tracehook_free_task(tsk);
+ free_task_struct(tsk);
+ }
+ EXPORT_SYMBOL(free_task);
+@@ -1007,6 +1008,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
+ if (!p)
+ goto fork_out;
+
++ tracehook_init_task(p);
++
+ ftrace_graph_init_task(p);
+
+ rt_mutex_init_task(p);
+diff --git a/kernel/ptrace-utrace.c b/kernel/ptrace-utrace.c
+new file mode 100644
+index 0000000..86234ee
+--- /dev/null
++++ b/kernel/ptrace-utrace.c
+@@ -0,0 +1,1127 @@
++/*
++ * linux/kernel/ptrace.c
++ *
++ * (C) Copyright 1999 Linus Torvalds
++ *
++ * Common interfaces for "ptrace()" which we do not want
++ * to continually duplicate across every architecture.
++ */
++
++#include <linux/capability.h>
++#include <linux/module.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/mm.h>
++#include <linux/highmem.h>
++#include <linux/pagemap.h>
++#include <linux/smp_lock.h>
++#include <linux/ptrace.h>
++#include <linux/utrace.h>
++#include <linux/security.h>
++#include <linux/signal.h>
++#include <linux/audit.h>
++#include <linux/pid_namespace.h>
++#include <linux/syscalls.h>
++#include <linux/uaccess.h>
++
++/*
++ * ptrace a task: make the debugger its new parent and
++ * move it to the ptrace list.
++ *
++ * Must be called with the tasklist lock write-held.
++ */
++void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
++{
++ BUG_ON(!list_empty(&child->ptrace_entry));
++ list_add(&child->ptrace_entry, &new_parent->ptraced);
++ child->parent = new_parent;
++}
++
++/*
++ * unptrace a task: move it back to its original parent and
++ * remove it from the ptrace list.
++ *
++ * Must be called with the tasklist lock write-held.
++ */
++void __ptrace_unlink(struct task_struct *child)
++{
++ BUG_ON(!child->ptrace);
++
++ child->ptrace = 0;
++ child->parent = child->real_parent;
++ list_del_init(&child->ptrace_entry);
++
++ arch_ptrace_untrace(child);
++}
++
++struct ptrace_context {
++ int options;
++
++ int signr;
++ siginfo_t *siginfo;
++
++ int stop_code;
++ unsigned long eventmsg;
++
++ enum utrace_resume_action resume;
++};
++
++#define PT_UTRACED 0x00001000
++
++#define PTRACE_O_SYSEMU 0x100
++
++#define PTRACE_EVENT_SYSCALL (1 << 16)
++#define PTRACE_EVENT_SIGTRAP (2 << 16)
++#define PTRACE_EVENT_SIGNAL (3 << 16)
++/* events visible to user-space */
++#define PTRACE_EVENT_MASK 0xFFFF
++
++static inline bool ptrace_event_pending(struct ptrace_context *ctx)
++{
++ return ctx->stop_code != 0;
++}
++
++static inline int get_stop_event(struct ptrace_context *ctx)
++{
++ return ctx->stop_code >> 8;
++}
++
++static inline void set_stop_code(struct ptrace_context *ctx, int event)
++{
++ ctx->stop_code = (event << 8) | SIGTRAP;
++}
++
++static inline struct ptrace_context *
++ptrace_context(struct utrace_engine *engine)
++{
++ return engine->data;
++}
++
++static const struct utrace_engine_ops ptrace_utrace_ops; /* forward decl */
++
++static struct utrace_engine *ptrace_lookup_engine(struct task_struct *tracee)
++{
++ return utrace_attach_task(tracee, UTRACE_ATTACH_MATCH_OPS,
++ &ptrace_utrace_ops, NULL);
++}
++
++static struct utrace_engine *
++ptrace_reuse_engine(struct task_struct *tracee)
++{
++ struct utrace_engine *engine;
++ struct ptrace_context *ctx;
++ int err = -EPERM;
++
++ engine = ptrace_lookup_engine(tracee);
++ if (IS_ERR(engine))
++ return engine;
++
++ ctx = ptrace_context(engine);
++ if (unlikely(ctx->resume == UTRACE_DETACH)) {
++ /*
++ * Try to reuse this self-detaching engine.
++ * The only caller which can hit this case is ptrace_attach(),
++ * it holds ->cred_guard_mutex.
++ */
++ ctx->options = 0;
++ ctx->eventmsg = 0;
++
++ /* make sure we don't get unwanted reports */
++ err = utrace_set_events(tracee, engine, UTRACE_EVENT(QUIESCE));
++ if (!err || err == -EINPROGRESS) {
++ ctx->resume = UTRACE_RESUME;
++ /* synchronize with ptrace_report_signal() */
++ err = utrace_barrier(tracee, engine);
++ }
++ WARN_ON(!err != (engine->ops == &ptrace_utrace_ops));
++
++ if (!err)
++ return engine;
++ }
++
++ utrace_engine_put(engine);
++ return ERR_PTR(err);
++}
++
++static struct utrace_engine *
++ptrace_attach_engine(struct task_struct *tracee)
++{
++ struct utrace_engine *engine;
++ struct ptrace_context *ctx;
++
++ if (unlikely(task_utrace_flags(tracee))) {
++ engine = ptrace_reuse_engine(tracee);
++ if (!IS_ERR(engine) || IS_ERR(engine) == -EPERM)
++ return engine;
++ }
++
++ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
++ if (unlikely(!ctx))
++ return ERR_PTR(-ENOMEM);
++
++ ctx->resume = UTRACE_RESUME;
++
++ engine = utrace_attach_task(tracee, UTRACE_ATTACH_CREATE |
++ UTRACE_ATTACH_EXCLUSIVE |
++ UTRACE_ATTACH_MATCH_OPS,
++ &ptrace_utrace_ops, ctx);
++ if (unlikely(IS_ERR(engine))) {
++ if (engine != ERR_PTR(-ESRCH) &&
++ engine != ERR_PTR(-ERESTARTNOINTR))
++ engine = ERR_PTR(-EPERM);
++ kfree(ctx);
++ }
++
++ return engine;
++}
++
++static inline int ptrace_set_events(struct task_struct *target,
++ struct utrace_engine *engine,
++ unsigned long options)
++{
++ struct ptrace_context *ctx = ptrace_context(engine);
++ /*
++ * We need QUIESCE for resume handling, CLONE to check
++ * for CLONE_PTRACE, other events are always reported.
++ */
++ unsigned long events = UTRACE_EVENT(QUIESCE) | UTRACE_EVENT(CLONE) |
++ UTRACE_EVENT(EXEC) | UTRACE_EVENT_SIGNAL_ALL;
++
++ ctx->options = options;
++ if (options & PTRACE_O_TRACEEXIT)
++ events |= UTRACE_EVENT(EXIT);
++
++ return utrace_set_events(target, engine, events);
++}
++
++/*
++ * Attach a utrace engine for ptrace and set up its event mask.
++ * Returns error code or 0 on success.
++ */
++static int ptrace_attach_task(struct task_struct *tracee, int options)
++{
++ struct utrace_engine *engine;
++ int err;
++
++ engine = ptrace_attach_engine(tracee);
++ if (IS_ERR(engine))
++ return PTR_ERR(engine);
++ /*
++ * It can fail only if the tracee is dead, the caller
++ * must notice this before setting PT_UTRACED.
++ */
++ err = ptrace_set_events(tracee, engine, options);
++ WARN_ON(err && !tracee->exit_state);
++ utrace_engine_put(engine);
++ return 0;
++}
++
++static int ptrace_wake_up(struct task_struct *tracee,
++ struct utrace_engine *engine,
++ enum utrace_resume_action action,
++ bool force_wakeup)
++{
++ if (force_wakeup) {
++ unsigned long flags;
++ /*
++ * Preserve the compatibility bug. Historically ptrace
++ * wakes up the tracee even if it should not. Clear
++ * SIGNAL_STOP_STOPPED for utrace_wakeup().
++ */
++ if (lock_task_sighand(tracee, &flags)) {
++ tracee->signal->flags &= ~SIGNAL_STOP_STOPPED;
++ unlock_task_sighand(tracee, &flags);
++ }
++ }
++
++ if (action != UTRACE_REPORT)
++ ptrace_context(engine)->stop_code = 0;
++
++ return utrace_control(tracee, engine, action);
++}
++
++static void ptrace_detach_task(struct task_struct *tracee, int sig)
++{
++ /*
++ * If true, the caller is PTRACE_DETACH, otherwise
++ * the tracer detaches implicitly during exit.
++ */
++ bool voluntary = (sig >= 0);
++ struct utrace_engine *engine = ptrace_lookup_engine(tracee);
++ enum utrace_resume_action action = UTRACE_DETACH;
++
++ if (unlikely(IS_ERR(engine)))
++ return;
++
++ if (sig) {
++ struct ptrace_context *ctx = ptrace_context(engine);
++
++ switch (get_stop_event(ctx)) {
++ case PTRACE_EVENT_SYSCALL:
++ if (voluntary)
++ send_sig_info(sig, SEND_SIG_PRIV, tracee);
++ break;
++
++ case PTRACE_EVENT_SIGNAL:
++ if (voluntary)
++ ctx->signr = sig;
++ ctx->resume = UTRACE_DETACH;
++ action = UTRACE_RESUME;
++ break;
++ }
++ }
++
++ ptrace_wake_up(tracee, engine, action, voluntary);
++ utrace_engine_put(engine);
++}
++
++static void ptrace_abort_attach(struct task_struct *tracee)
++{
++ ptrace_detach_task(tracee, 0);
++}
++
++static u32 ptrace_report_exit(u32 action, struct utrace_engine *engine,
++ long orig_code, long *code)
++{
++ struct ptrace_context *ctx = ptrace_context(engine);
++
++ WARN_ON(ptrace_event_pending(ctx) &&
++ !signal_group_exit(current->signal));
++
++ set_stop_code(ctx, PTRACE_EVENT_EXIT);
++ ctx->eventmsg = *code;
++
++ return UTRACE_STOP;
++}
++
++static void ptrace_clone_attach(struct task_struct *child,
++ int options)
++{
++ struct task_struct *parent = current;
++ struct task_struct *tracer;
++ bool abort = true;
++
++ if (unlikely(ptrace_attach_task(child, options))) {
++ WARN_ON(1);
++ return;
++ }
++
++ write_lock_irq(&tasklist_lock);
++ tracer = parent->parent;
++ if (!(tracer->flags & PF_EXITING) && parent->ptrace) {
++ child->ptrace = parent->ptrace;
++ __ptrace_link(child, tracer);
++ abort = false;
++ }
++ write_unlock_irq(&tasklist_lock);
++ if (unlikely(abort)) {
++ ptrace_abort_attach(child);
++ return;
++ }
++
++ sigaddset(&child->pending.signal, SIGSTOP);
++ set_tsk_thread_flag(child, TIF_SIGPENDING);
++}
++
++static u32 ptrace_report_clone(u32 action, struct utrace_engine *engine,
++ unsigned long clone_flags,
++ struct task_struct *child)
++{
++ struct ptrace_context *ctx = ptrace_context(engine);
++ int event = 0;
++
++ WARN_ON(ptrace_event_pending(ctx));
++
++ if (clone_flags & CLONE_UNTRACED) {
++ /* no events reported */
++ } else if (clone_flags & CLONE_VFORK) {
++ if (ctx->options & PTRACE_O_TRACEVFORK)
++ event = PTRACE_EVENT_VFORK;
++ else if (ctx->options & PTRACE_O_TRACEVFORKDONE)
++ event = PTRACE_EVENT_VFORK_DONE;
++ } else if ((clone_flags & CSIGNAL) != SIGCHLD) {
++ if (ctx->options & PTRACE_O_TRACECLONE)
++ event = PTRACE_EVENT_CLONE;
++ } else if (ctx->options & PTRACE_O_TRACEFORK) {
++ event = PTRACE_EVENT_FORK;
++ }
++ /*
++ * Any of these reports implies auto-attaching the new child.
++ * So does CLONE_PTRACE, even with no event to report.
++ */
++ if ((event && event != PTRACE_EVENT_VFORK_DONE) ||
++ (clone_flags & CLONE_PTRACE))
++ ptrace_clone_attach(child, ctx->options);
++
++ if (!event)
++ return UTRACE_RESUME;
++
++ set_stop_code(ctx, event);
++ ctx->eventmsg = child->pid;
++ /*
++ * We shouldn't stop now, inside the do_fork() path.
++ * We will stop later, before return to user-mode.
++ */
++ if (event == PTRACE_EVENT_VFORK_DONE)
++ return UTRACE_REPORT;
++ else
++ return UTRACE_STOP;
++}
++
++static inline void set_syscall_code(struct ptrace_context *ctx)
++{
++ set_stop_code(ctx, PTRACE_EVENT_SYSCALL);
++ if (ctx->options & PTRACE_O_TRACESYSGOOD)
++ ctx->stop_code |= 0x80;
++}
++
++static u32 ptrace_report_syscall_entry(u32 action, struct utrace_engine *engine,
++ struct pt_regs *regs)
++{
++ struct ptrace_context *ctx = ptrace_context(engine);
++
++ if (action & UTRACE_SYSCALL_RESUMED) {
++ /*
++ * We already reported the first time.
++ * Nothing more to do now.
++ */
++ if (unlikely(ctx->options & PTRACE_O_SYSEMU))
++ return UTRACE_SYSCALL_ABORT | UTRACE_REPORT;
++ return utrace_syscall_action(action) | UTRACE_RESUME;
++ }
++
++ WARN_ON(ptrace_event_pending(ctx));
++
++ set_syscall_code(ctx);
++
++ if (unlikely(ctx->options & PTRACE_O_SYSEMU))
++ return UTRACE_SYSCALL_ABORT | UTRACE_REPORT;
++ /*
++ * Stop now to report. We will get another callback after
++ * we resume, with the UTRACE_SYSCALL_RESUMED flag set.
++ */
++ return UTRACE_SYSCALL_RUN | UTRACE_STOP;
++}
++
++static u32 ptrace_report_syscall_exit(u32 action, struct utrace_engine *engine,
++ struct pt_regs *regs)
++{
++ struct ptrace_context *ctx = ptrace_context(engine);
++
++ if (ptrace_event_pending(ctx))
++ return UTRACE_STOP;
++
++ if (ctx->resume != UTRACE_RESUME) {
++ WARN_ON(ctx->resume != UTRACE_BLOCKSTEP &&
++ ctx->resume != UTRACE_SINGLESTEP);
++ ctx->resume = UTRACE_RESUME;
++
++ ctx->signr = SIGTRAP;
++ return UTRACE_INTERRUPT;
++ }
++
++ set_syscall_code(ctx);
++ return UTRACE_STOP;
++}
++
++static u32 ptrace_report_exec(u32 action, struct utrace_engine *engine,
++ const struct linux_binfmt *fmt,
++ const struct linux_binprm *bprm,
++ struct pt_regs *regs)
++{
++ struct ptrace_context *ctx = ptrace_context(engine);
++
++ WARN_ON(ptrace_event_pending(ctx));
++
++ if (!(ctx->options & PTRACE_O_TRACEEXEC)) {
++ /*
++ * Old-fashioned ptrace'd exec just posts a plain signal.
++ */
++ send_sig(SIGTRAP, current, 0);
++ return UTRACE_RESUME;
++ }
++
++ set_stop_code(ctx, PTRACE_EVENT_EXEC);
++ return UTRACE_STOP;
++}
++
++static enum utrace_signal_action resume_signal(struct ptrace_context *ctx,
++ struct k_sigaction *return_ka)
++{
++ siginfo_t *info = ctx->siginfo;
++ int signr = ctx->signr;
++
++ ctx->siginfo = NULL;
++ ctx->signr = 0;
++
++ /* Did the debugger cancel the sig? */
++ if (!signr)
++ return UTRACE_SIGNAL_IGN;
++ /*
++ * Update the siginfo structure if the signal has changed.
++ * If the debugger wanted something specific in the siginfo
++ * then it should have updated *info via PTRACE_SETSIGINFO.
++ */
++ if (info->si_signo != signr) {
++ info->si_signo = signr;
++ info->si_errno = 0;
++ info->si_code = SI_USER;
++ info->si_pid = task_pid_vnr(current->parent);
++ info->si_uid = task_uid(current->parent);
++ }
++
++ /* If the (new) signal is now blocked, requeue it. */
++ if (sigismember(&current->blocked, signr)) {
++ send_sig_info(signr, info, current);
++ return UTRACE_SIGNAL_IGN;
++ }
++
++ spin_lock_irq(&current->sighand->siglock);
++ *return_ka = current->sighand->action[signr - 1];
++ spin_unlock_irq(&current->sighand->siglock);
++
++ return UTRACE_SIGNAL_DELIVER;
++}
++
++static u32 ptrace_report_signal(u32 action, struct utrace_engine *engine,
++ struct pt_regs *regs,
++ siginfo_t *info,
++ const struct k_sigaction *orig_ka,
++ struct k_sigaction *return_ka)
++{
++ struct ptrace_context *ctx = ptrace_context(engine);
++ enum utrace_resume_action resume = ctx->resume;
++
++ if (ptrace_event_pending(ctx)) {
++ action = utrace_signal_action(action);
++ WARN_ON(action != UTRACE_SIGNAL_REPORT);
++ return action | UTRACE_STOP;
++ }
++
++ switch (utrace_signal_action(action)) {
++ case UTRACE_SIGNAL_HANDLER:
++ if (WARN_ON(ctx->siginfo))
++ ctx->siginfo = NULL;
++
++ if (resume != UTRACE_RESUME) {
++ WARN_ON(resume != UTRACE_BLOCKSTEP &&
++ resume != UTRACE_SINGLESTEP);
++
++ set_stop_code(ctx, PTRACE_EVENT_SIGTRAP);
++ return UTRACE_STOP | UTRACE_SIGNAL_IGN;
++ }
++
++ case UTRACE_SIGNAL_REPORT:
++ if (!ctx->siginfo) {
++ if (ctx->signr) {
++ /* set by ptrace_resume(SYSCALL_EXIT) */
++ WARN_ON(ctx->signr != SIGTRAP);
++ user_single_step_siginfo(current, regs, info);
++ force_sig_info(SIGTRAP, info, current);
++ }
++
++ return resume | UTRACE_SIGNAL_IGN;
++ }
++
++ if (WARN_ON(ctx->siginfo != info))
++ return resume | UTRACE_SIGNAL_IGN;
++
++ return resume | resume_signal(ctx, return_ka);
++
++ default:
++ break;
++ }
++
++ WARN_ON(ctx->siginfo);
++ ctx->siginfo = info;
++ /*
++ * ctx->siginfo points to the caller's stack.
++ * Make sure the subsequent UTRACE_SIGNAL_REPORT clears
++ * ->siginfo before return from get_signal_to_deliver().
++ */
++ if (utrace_control(current, engine, UTRACE_INTERRUPT))
++ WARN_ON(1);
++
++ ctx->signr = info->si_signo;
++ ctx->stop_code = (PTRACE_EVENT_SIGNAL << 8) | ctx->signr;
++
++ return UTRACE_STOP | UTRACE_SIGNAL_IGN;
++}
++
++static u32 ptrace_report_quiesce(u32 action, struct utrace_engine *engine,
++ unsigned long event)
++{
++ struct ptrace_context *ctx = ptrace_context(engine);
++
++ if (ptrace_event_pending(ctx))
++ return UTRACE_STOP;
++
++ return event ? UTRACE_RESUME : ctx->resume;
++}
++
++static void ptrace_release(void *data)
++{
++ kfree(data);
++}
++
++static const struct utrace_engine_ops ptrace_utrace_ops = {
++ .report_signal = ptrace_report_signal,
++ .report_quiesce = ptrace_report_quiesce,
++ .report_exec = ptrace_report_exec,
++ .report_exit = ptrace_report_exit,
++ .report_clone = ptrace_report_clone,
++ .report_syscall_entry = ptrace_report_syscall_entry,
++ .report_syscall_exit = ptrace_report_syscall_exit,
++ .release = ptrace_release,
++};
++
++int ptrace_check_attach(struct task_struct *child, int kill)
++{
++ struct utrace_engine *engine;
++ struct utrace_examiner exam;
++ int ret = -ESRCH;
++
++ engine = ptrace_lookup_engine(child);
++ if (IS_ERR(engine))
++ return ret;
++
++ if (child->parent != current)
++ goto out;
++
++ if (unlikely(kill))
++ ret = 0;
++
++ if (!task_is_stopped_or_traced(child))
++ goto out;
++ /*
++ * Make sure our engine has already stopped the child.
++ * Then wait for it to be off the CPU.
++ */
++ if (!utrace_control(child, engine, UTRACE_STOP) &&
++ !utrace_prepare_examine(child, engine, &exam))
++ ret = 0;
++out:
++ utrace_engine_put(engine);
++ return ret;
++}
++
++int ptrace_attach(struct task_struct *task)
++{
++ int retval;
++
++ audit_ptrace(task);
++
++ retval = -EPERM;
++ if (unlikely(task->flags & PF_KTHREAD))
++ goto out;
++ if (same_thread_group(task, current))
++ goto out;
++
++ /*
++ * Protect exec's credential calculations against our interference;
++ * interference; SUID, SGID and LSM creds get determined differently
++ * under ptrace.
++ */
++ retval = -ERESTARTNOINTR;
++ if (mutex_lock_interruptible(&task->cred_guard_mutex))
++ goto out;
++
++ task_lock(task);
++ retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
++ task_unlock(task);
++ if (retval)
++ goto unlock_creds;
++
++ retval = ptrace_attach_task(task, 0);
++ if (unlikely(retval))
++ goto unlock_creds;
++
++ write_lock_irq(&tasklist_lock);
++ retval = -EPERM;
++ if (unlikely(task->exit_state))
++ goto unlock_tasklist;
++
++ BUG_ON(task->ptrace);
++ task->ptrace = PT_UTRACED;
++ if (capable(CAP_SYS_PTRACE))
++ task->ptrace |= PT_PTRACE_CAP;
++
++ __ptrace_link(task, current);
++ send_sig_info(SIGSTOP, SEND_SIG_FORCED, task);
++
++ retval = 0;
++unlock_tasklist:
++ write_unlock_irq(&tasklist_lock);
++unlock_creds:
++ mutex_unlock(&task->cred_guard_mutex);
++out:
++ return retval;
++}
++
++/*
++ * Performs checks and sets PT_UTRACED.
++ * Should be used by all ptrace implementations for PTRACE_TRACEME.
++ */
++int ptrace_traceme(void)
++{
++ bool detach = true;
++ int ret = ptrace_attach_task(current, 0);
++
++ if (unlikely(ret))
++ return ret;
++
++ ret = -EPERM;
++ write_lock_irq(&tasklist_lock);
++ BUG_ON(current->ptrace);
++ ret = security_ptrace_traceme(current->parent);
++ /*
++ * Check PF_EXITING to ensure ->real_parent has not passed
++ * exit_ptrace(). Otherwise we don't report the error but
++ * pretend ->real_parent untraces us right after return.
++ */
++ if (!ret && !(current->real_parent->flags & PF_EXITING)) {
++ current->ptrace = PT_UTRACED;
++ __ptrace_link(current, current->real_parent);
++ detach = false;
++ }
++ write_unlock_irq(&tasklist_lock);
++
++ if (detach)
++ ptrace_abort_attach(current);
++ return ret;
++}
++
++static void ptrace_do_detach(struct task_struct *tracee, unsigned int data)
++{
++ bool detach, release;
++
++ write_lock_irq(&tasklist_lock);
++ /*
++ * This tracee can be already killed. Make sure de_thread() or
++ * our sub-thread doing do_wait() didn't do release_task() yet.
++ */
++ detach = tracee->ptrace != 0;
++ release = false;
++ if (likely(detach))
++ release = __ptrace_detach(current, tracee);
++ write_unlock_irq(&tasklist_lock);
++
++ if (unlikely(release))
++ release_task(tracee);
++ else if (likely(detach))
++ ptrace_detach_task(tracee, data);
++}
++
++int ptrace_detach(struct task_struct *child, unsigned int data)
++{
++ if (!valid_signal(data))
++ return -EIO;
++
++ ptrace_do_detach(child, data);
++
++ return 0;
++}
++
++/*
++ * Detach all tasks we were using ptrace on.
++ */
++void exit_ptrace(struct task_struct *tracer)
++{
++ for (;;) {
++ struct task_struct *tracee = NULL;
++
++ read_lock(&tasklist_lock);
++ if (!list_empty(&tracer->ptraced)) {
++ tracee = list_first_entry(&tracer->ptraced,
++ struct task_struct, ptrace_entry);
++ get_task_struct(tracee);
++ }
++ read_unlock(&tasklist_lock);
++ if (!tracee)
++ break;
++
++ ptrace_do_detach(tracee, -1);
++ put_task_struct(tracee);
++ }
++}
++
++static int ptrace_set_options(struct task_struct *tracee,
++ struct utrace_engine *engine, long data)
++{
++ BUILD_BUG_ON(PTRACE_O_MASK & PTRACE_O_SYSEMU);
++
++ ptrace_set_events(tracee, engine, data & PTRACE_O_MASK);
++ return (data & ~PTRACE_O_MASK) ? -EINVAL : 0;
++}
++
++static int ptrace_rw_siginfo(struct task_struct *tracee,
++ struct ptrace_context *ctx,
++ siginfo_t *info, bool write)
++{
++ unsigned long flags;
++ int err;
++
++ switch (get_stop_event(ctx)) {
++ case 0: /* jctl stop */
++ return -EINVAL;
++
++ case PTRACE_EVENT_SIGNAL:
++ err = -ESRCH;
++ if (lock_task_sighand(tracee, &flags)) {
++ if (likely(task_is_traced(tracee))) {
++ if (write)
++ *ctx->siginfo = *info;
++ else
++ *info = *ctx->siginfo;
++ err = 0;
++ }
++ unlock_task_sighand(tracee, &flags);
++ }
++
++ return err;
++
++ default:
++ if (!write) {
++ memset(info, 0, sizeof(*info));
++ info->si_signo = SIGTRAP;
++ info->si_code = ctx->stop_code & PTRACE_EVENT_MASK;
++ info->si_pid = task_pid_vnr(tracee);
++ info->si_uid = task_uid(tracee);
++ }
++
++ return 0;
++ }
++}
++
++static void do_ptrace_notify_stop(struct ptrace_context *ctx,
++ struct task_struct *tracee)
++{
++ /*
++ * This can race with SIGKILL, but we borrow this race from
++ * the old ptrace implementation. ->exit_code is only needed
++ * for wait_task_stopped()->task_stopped_code(), we should
++ * change it to use ptrace_context.
++ */
++ tracee->exit_code = ctx->stop_code & PTRACE_EVENT_MASK;
++ WARN_ON(!tracee->exit_code);
++
++ read_lock(&tasklist_lock);
++ /*
++ * Don't want to allow preemption here, because
++ * sys_ptrace() needs this task to be inactive.
++ */
++ preempt_disable();
++ /*
++ * It can be killed and then released by our subthread,
++ * or ptrace_attach() has not completed yet.
++ */
++ if (task_ptrace(tracee))
++ do_notify_parent_cldstop(tracee, CLD_TRAPPED);
++ read_unlock(&tasklist_lock);
++ preempt_enable_no_resched();
++}
++
++void ptrace_notify_stop(struct task_struct *tracee)
++{
++ struct utrace_engine *engine = ptrace_lookup_engine(tracee);
++
++ if (IS_ERR(engine))
++ return;
++
++ do_ptrace_notify_stop(ptrace_context(engine), tracee);
++ utrace_engine_put(engine);
++}
++
++static int ptrace_resume_action(struct task_struct *tracee,
++ struct utrace_engine *engine, long request)
++{
++ struct ptrace_context *ctx = ptrace_context(engine);
++ unsigned long events;
++ int action;
++
++ ctx->options &= ~PTRACE_O_SYSEMU;
++ events = engine->flags & ~UTRACE_EVENT_SYSCALL;
++ action = UTRACE_RESUME;
++
++ switch (request) {
++#ifdef PTRACE_SINGLEBLOCK
++ case PTRACE_SINGLEBLOCK:
++ if (unlikely(!arch_has_block_step()))
++ return -EIO;
++ action = UTRACE_BLOCKSTEP;
++ events |= UTRACE_EVENT(SYSCALL_EXIT);
++ break;
++#endif
++
++#ifdef PTRACE_SINGLESTEP
++ case PTRACE_SINGLESTEP:
++ if (unlikely(!arch_has_single_step()))
++ return -EIO;
++ action = UTRACE_SINGLESTEP;
++ events |= UTRACE_EVENT(SYSCALL_EXIT);
++ break;
++#endif
++
++#ifdef PTRACE_SYSEMU
++ case PTRACE_SYSEMU_SINGLESTEP:
++ if (unlikely(!arch_has_single_step()))
++ return -EIO;
++ action = UTRACE_SINGLESTEP;
++ case PTRACE_SYSEMU:
++ ctx->options |= PTRACE_O_SYSEMU;
++ events |= UTRACE_EVENT(SYSCALL_ENTRY);
++ break;
++#endif
++
++ case PTRACE_SYSCALL:
++ events |= UTRACE_EVENT_SYSCALL;
++ break;
++
++ case PTRACE_CONT:
++ break;
++ default:
++ return -EIO;
++ }
++
++ if (events != engine->flags &&
++ utrace_set_events(tracee, engine, events))
++ return -ESRCH;
++
++ return action;
++}
++
++static int ptrace_resume(struct task_struct *tracee,
++ struct utrace_engine *engine,
++ long request, long data)
++{
++ struct ptrace_context *ctx = ptrace_context(engine);
++ int action;
++
++ if (!valid_signal(data))
++ return -EIO;
++
++ action = ptrace_resume_action(tracee, engine, request);
++ if (action < 0)
++ return action;
++
++ switch (get_stop_event(ctx)) {
++ case PTRACE_EVENT_VFORK:
++ if (ctx->options & PTRACE_O_TRACEVFORKDONE) {
++ set_stop_code(ctx, PTRACE_EVENT_VFORK_DONE);
++ action = UTRACE_REPORT;
++ }
++ break;
++
++ case PTRACE_EVENT_EXEC:
++ case PTRACE_EVENT_FORK:
++ case PTRACE_EVENT_CLONE:
++ case PTRACE_EVENT_VFORK_DONE:
++ if (request == PTRACE_SYSCALL) {
++ set_syscall_code(ctx);
++ do_ptrace_notify_stop(ctx, tracee);
++ return 0;
++ }
++
++ if (action != UTRACE_RESUME) {
++ /*
++ * single-stepping. UTRACE_SIGNAL_REPORT will
++ * synthesize a trap to follow the syscall insn.
++ */
++ ctx->signr = SIGTRAP;
++ action = UTRACE_INTERRUPT;
++ }
++ break;
++
++ case PTRACE_EVENT_SYSCALL:
++ if (data)
++ send_sig_info(data, SEND_SIG_PRIV, tracee);
++ break;
++
++ case PTRACE_EVENT_SIGNAL:
++ ctx->signr = data;
++ break;
++ }
++
++ ctx->resume = action;
++ ptrace_wake_up(tracee, engine, action, true);
++ return 0;
++}
++
++extern int ptrace_regset(struct task_struct *task, int req, unsigned int type,
++ struct iovec *kiov);
++
++int ptrace_request(struct task_struct *child, long request,
++ long addr, long data)
++{
++ struct utrace_engine *engine = ptrace_lookup_engine(child);
++ siginfo_t siginfo;
++ int ret;
++
++ if (unlikely(IS_ERR(engine)))
++ return -ESRCH;
++
++ switch (request) {
++ case PTRACE_PEEKTEXT:
++ case PTRACE_PEEKDATA:
++ ret = generic_ptrace_peekdata(child, addr, data);
++ break;
++ case PTRACE_POKETEXT:
++ case PTRACE_POKEDATA:
++ ret = generic_ptrace_pokedata(child, addr, data);
++ break;
++
++#ifdef PTRACE_OLDSETOPTIONS
++ case PTRACE_OLDSETOPTIONS:
++#endif
++ case PTRACE_SETOPTIONS:
++ ret = ptrace_set_options(child, engine, data);
++ break;
++ case PTRACE_GETEVENTMSG:
++ ret = put_user(ptrace_context(engine)->eventmsg,
++ (unsigned long __user *) data);
++ break;
++
++ case PTRACE_GETSIGINFO:
++ ret = ptrace_rw_siginfo(child, ptrace_context(engine),
++ &siginfo, false);
++ if (!ret)
++ ret = copy_siginfo_to_user((siginfo_t __user *) data,
++ &siginfo);
++ break;
++
++ case PTRACE_SETSIGINFO:
++ if (copy_from_user(&siginfo, (siginfo_t __user *) data,
++ sizeof siginfo))
++ ret = -EFAULT;
++ else
++ ret = ptrace_rw_siginfo(child, ptrace_context(engine),
++ &siginfo, true);
++ break;
++
++ case PTRACE_DETACH: /* detach a process that was attached. */
++ ret = ptrace_detach(child, data);
++ break;
++
++ case PTRACE_KILL:
++ /* Ugly historical behaviour. */
++ if (task_is_traced(child))
++ ptrace_resume(child, engine, PTRACE_CONT, SIGKILL);
++ ret = 0;
++ break;
++
++ case PTRACE_GETREGSET:
++ case PTRACE_SETREGSET:
++ {
++ struct iovec kiov;
++ struct iovec __user *uiov = (struct iovec __user *) data;
++
++ if (!access_ok(VERIFY_WRITE, uiov, sizeof(*uiov)))
++ return -EFAULT;
++
++ if (__get_user(kiov.iov_base, &uiov->iov_base) ||
++ __get_user(kiov.iov_len, &uiov->iov_len))
++ return -EFAULT;
++
++ ret = ptrace_regset(child, request, addr, &kiov);
++ if (!ret)
++ ret = __put_user(kiov.iov_len, &uiov->iov_len);
++ break;
++ }
++
++ default:
++ ret = ptrace_resume(child, engine, request, data);
++ break;
++ }
++
++ utrace_engine_put(engine);
++ return ret;
++}
++
++#if defined CONFIG_COMPAT
++#include <linux/compat.h>
++
++int compat_ptrace_request(struct task_struct *child, compat_long_t request,
++ compat_ulong_t addr, compat_ulong_t data)
++{
++ struct utrace_engine *engine = ptrace_lookup_engine(child);
++ compat_ulong_t __user *datap = compat_ptr(data);
++ compat_ulong_t word;
++ siginfo_t siginfo;
++ int ret;
++
++ if (unlikely(IS_ERR(engine)))
++ return -ESRCH;
++
++ switch (request) {
++ case PTRACE_PEEKTEXT:
++ case PTRACE_PEEKDATA:
++ ret = access_process_vm(child, addr, &word, sizeof(word), 0);
++ if (ret != sizeof(word))
++ ret = -EIO;
++ else
++ ret = put_user(word, datap);
++ break;
++
++ case PTRACE_POKETEXT:
++ case PTRACE_POKEDATA:
++ ret = access_process_vm(child, addr, &data, sizeof(data), 1);
++ ret = (ret != sizeof(data) ? -EIO : 0);
++ break;
++
++ case PTRACE_GETEVENTMSG:
++ ret = put_user((compat_ulong_t)ptrace_context(engine)->eventmsg,
++ datap);
++ break;
++
++ case PTRACE_GETSIGINFO:
++ ret = ptrace_rw_siginfo(child, ptrace_context(engine),
++ &siginfo, false);
++ if (!ret)
++ ret = copy_siginfo_to_user32(
++ (struct compat_siginfo __user *) datap,
++ &siginfo);
++ break;
++
++ case PTRACE_SETSIGINFO:
++ memset(&siginfo, 0, sizeof siginfo);
++ if (copy_siginfo_from_user32(
++ &siginfo, (struct compat_siginfo __user *) datap))
++ ret = -EFAULT;
++ else
++ ret = ptrace_rw_siginfo(child, ptrace_context(engine),
++ &siginfo, true);
++ break;
++
++ case PTRACE_GETREGSET:
++ case PTRACE_SETREGSET:
++ {
++ struct iovec kiov;
++ struct compat_iovec __user *uiov =
++ (struct compat_iovec __user *) datap;
++ compat_uptr_t ptr;
++ compat_size_t len;
++
++ if (!access_ok(VERIFY_WRITE, uiov, sizeof(*uiov)))
++ return -EFAULT;
++
++ if (__get_user(ptr, &uiov->iov_base) ||
++ __get_user(len, &uiov->iov_len))
++ return -EFAULT;
++
++ kiov.iov_base = compat_ptr(ptr);
++ kiov.iov_len = len;
++
++ ret = ptrace_regset(child, request, addr, &kiov);
++ if (!ret)
++ ret = __put_user(kiov.iov_len, &uiov->iov_len);
++ break;
++ }
++
++ default:
++ ret = ptrace_request(child, request, addr, data);
++ }
++
++ utrace_engine_put(engine);
++ return ret;
++}
++#endif /* CONFIG_COMPAT */
+diff --git a/kernel/ptrace.c b/kernel/ptrace.c
+index 74a3d69..c77f9bf 100644
+--- a/kernel/ptrace.c
++++ b/kernel/ptrace.c
+@@ -23,7 +23,317 @@
+ #include <linux/uaccess.h>
+ #include <linux/regset.h>
+
++int __ptrace_may_access(struct task_struct *task, unsigned int mode)
++{
++ const struct cred *cred = current_cred(), *tcred;
++
++ /* May we inspect the given task?
++ * This check is used both for attaching with ptrace
++ * and for allowing access to sensitive information in /proc.
++ *
++ * ptrace_attach denies several cases that /proc allows
++ * because setting up the necessary parent/child relationship
++ * or halting the specified task is impossible.
++ */
++ int dumpable = 0;
++ /* Don't let security modules deny introspection */
++ if (task == current)
++ return 0;
++ rcu_read_lock();
++ tcred = __task_cred(task);
++ if ((cred->uid != tcred->euid ||
++ cred->uid != tcred->suid ||
++ cred->uid != tcred->uid ||
++ cred->gid != tcred->egid ||
++ cred->gid != tcred->sgid ||
++ cred->gid != tcred->gid) &&
++ !capable(CAP_SYS_PTRACE)) {
++ rcu_read_unlock();
++ return -EPERM;
++ }
++ rcu_read_unlock();
++ smp_rmb();
++ if (task->mm)
++ dumpable = get_dumpable(task->mm);
++ if (!dumpable && !capable(CAP_SYS_PTRACE))
++ return -EPERM;
++
++ return security_ptrace_access_check(task, mode);
++}
++
++bool ptrace_may_access(struct task_struct *task, unsigned int mode)
++{
++ int err;
++ task_lock(task);
++ err = __ptrace_may_access(task, mode);
++ task_unlock(task);
++ return !err;
++}
++
++/*
++ * Called with irqs disabled, returns true if childs should reap themselves.
++ */
++static int ignoring_children(struct sighand_struct *sigh)
++{
++ int ret;
++ spin_lock(&sigh->siglock);
++ ret = (sigh->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) ||
++ (sigh->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT);
++ spin_unlock(&sigh->siglock);
++ return ret;
++}
++
++/*
++ * Called with tasklist_lock held for writing.
++ * Unlink a traced task, and clean it up if it was a traced zombie.
++ * Return true if it needs to be reaped with release_task().
++ * (We can't call release_task() here because we already hold tasklist_lock.)
++ *
++ * If it's a zombie, our attachedness prevented normal parent notification
++ * or self-reaping. Do notification now if it would have happened earlier.
++ * If it should reap itself, return true.
++ *
++ * If it's our own child, there is no notification to do. But if our normal
++ * children self-reap, then this child was prevented by ptrace and we must
++ * reap it now, in that case we must also wake up sub-threads sleeping in
++ * do_wait().
++ */
++bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p)
++{
++ __ptrace_unlink(p);
++
++ if (p->exit_state == EXIT_ZOMBIE) {
++ if (!task_detached(p) && thread_group_empty(p)) {
++ if (!same_thread_group(p->real_parent, tracer))
++ do_notify_parent(p, p->exit_signal);
++ else if (ignoring_children(tracer->sighand)) {
++ __wake_up_parent(p, tracer);
++ p->exit_signal = -1;
++ }
++ }
++ if (task_detached(p)) {
++ /* Mark it as in the process of being reaped. */
++ p->exit_state = EXIT_DEAD;
++ return true;
++ }
++ }
++
++ return false;
++}
++
++int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len)
++{
++ int copied = 0;
++
++ while (len > 0) {
++ char buf[128];
++ int this_len, retval;
++
++ this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
++ retval = access_process_vm(tsk, src, buf, this_len, 0);
++ if (!retval) {
++ if (copied)
++ break;
++ return -EIO;
++ }
++ if (copy_to_user(dst, buf, retval))
++ return -EFAULT;
++ copied += retval;
++ src += retval;
++ dst += retval;
++ len -= retval;
++ }
++ return copied;
++}
++
++int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len)
++{
++ int copied = 0;
++
++ while (len > 0) {
++ char buf[128];
++ int this_len, retval;
++
++ this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
++ if (copy_from_user(buf, src, this_len))
++ return -EFAULT;
++ retval = access_process_vm(tsk, dst, buf, this_len, 1);
++ if (!retval) {
++ if (copied)
++ break;
++ return -EIO;
++ }
++ copied += retval;
++ src += retval;
++ dst += retval;
++ len -= retval;
++ }
++ return copied;
++}
++
++#ifdef CONFIG_HAVE_ARCH_TRACEHOOK
++
++static const struct user_regset *
++find_regset(const struct user_regset_view *view, unsigned int type)
++{
++ const struct user_regset *regset;
++ int n;
++
++ for (n = 0; n < view->n; ++n) {
++ regset = view->regsets + n;
++ if (regset->core_note_type == type)
++ return regset;
++ }
++
++ return NULL;
++}
++
++int ptrace_regset(struct task_struct *task, int req, unsigned int type,
++ struct iovec *kiov)
++{
++ const struct user_regset_view *view = task_user_regset_view(task);
++ const struct user_regset *regset = find_regset(view, type);
++ int regset_no;
++
++ if (!regset || (kiov->iov_len % regset->size) != 0)
++ return -EINVAL;
++
++ regset_no = regset - view->regsets;
++ kiov->iov_len = min(kiov->iov_len,
++ (__kernel_size_t) (regset->n * regset->size));
++
++ if (req == PTRACE_GETREGSET)
++ return copy_regset_to_user(task, view, regset_no, 0,
++ kiov->iov_len, kiov->iov_base);
++ else
++ return copy_regset_from_user(task, view, regset_no, 0,
++ kiov->iov_len, kiov->iov_base);
++}
++
++#endif
++
++static struct task_struct *ptrace_get_task_struct(pid_t pid)
++{
++ struct task_struct *child;
++
++ rcu_read_lock();
++ child = find_task_by_vpid(pid);
++ if (child)
++ get_task_struct(child);
++ rcu_read_unlock();
++
++ if (!child)
++ return ERR_PTR(-ESRCH);
++ return child;
++}
++
++#ifndef arch_ptrace_attach
++#define arch_ptrace_attach(child) do { } while (0)
++#endif
++
++SYSCALL_DEFINE4(ptrace, long, request, long, pid, long, addr, long, data)
++{
++ struct task_struct *child;
++ long ret;
++
++ if (request == PTRACE_TRACEME) {
++ ret = ptrace_traceme();
++ if (!ret)
++ arch_ptrace_attach(current);
++ goto out;
++ }
++
++ child = ptrace_get_task_struct(pid);
++ if (IS_ERR(child)) {
++ ret = PTR_ERR(child);
++ goto out;
++ }
++
++ if (request == PTRACE_ATTACH) {
++ ret = ptrace_attach(child);
++ /*
++ * Some architectures need to do book-keeping after
++ * a ptrace attach.
++ */
++ if (!ret)
++ arch_ptrace_attach(child);
++ goto out_put_task_struct;
++ }
++
++ ret = ptrace_check_attach(child, request == PTRACE_KILL);
++ if (ret < 0)
++ goto out_put_task_struct;
++
++ ret = arch_ptrace(child, request, addr, data);
++
++ out_put_task_struct:
++ put_task_struct(child);
++ out:
++ return ret;
++}
++
++int generic_ptrace_peekdata(struct task_struct *tsk, long addr, long data)
++{
++ unsigned long tmp;
++ int copied;
++
++ copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), 0);
++ if (copied != sizeof(tmp))
++ return -EIO;
++ return put_user(tmp, (unsigned long __user *)data);
++}
++
++int generic_ptrace_pokedata(struct task_struct *tsk, long addr, long data)
++{
++ int copied;
++
++ copied = access_process_vm(tsk, addr, &data, sizeof(data), 1);
++ return (copied == sizeof(data)) ? 0 : -EIO;
++}
++
++#if defined CONFIG_COMPAT
++#include <linux/compat.h>
++
++asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
++ compat_long_t addr, compat_long_t data)
++{
++ struct task_struct *child;
++ long ret;
++
++ if (request == PTRACE_TRACEME) {
++ ret = ptrace_traceme();
++ goto out;
++ }
++
++ child = ptrace_get_task_struct(pid);
++ if (IS_ERR(child)) {
++ ret = PTR_ERR(child);
++ goto out;
++ }
++
++ if (request == PTRACE_ATTACH) {
++ ret = ptrace_attach(child);
++ /*
++ * Some architectures need to do book-keeping after
++ * a ptrace attach.
++ */
++ if (!ret)
++ arch_ptrace_attach(child);
++ goto out_put_task_struct;
++ }
++
++ ret = ptrace_check_attach(child, request == PTRACE_KILL);
++ if (!ret)
++ ret = compat_arch_ptrace(child, request, addr, data);
++
++ out_put_task_struct:
++ put_task_struct(child);
++ out:
++ return ret;
++}
++#endif /* CONFIG_COMPAT */
+
++#ifndef CONFIG_UTRACE
+ /*
+ * ptrace a task: make the debugger its new parent and
+ * move it to the ptrace list.
+@@ -116,53 +426,6 @@ int ptrace_check_attach(struct task_struct *child, int kill)
+ return ret;
+ }
+
+-int __ptrace_may_access(struct task_struct *task, unsigned int mode)
+-{
+- const struct cred *cred = current_cred(), *tcred;
+-
+- /* May we inspect the given task?
+- * This check is used both for attaching with ptrace
+- * and for allowing access to sensitive information in /proc.
+- *
+- * ptrace_attach denies several cases that /proc allows
+- * because setting up the necessary parent/child relationship
+- * or halting the specified task is impossible.
+- */
+- int dumpable = 0;
+- /* Don't let security modules deny introspection */
+- if (task == current)
+- return 0;
+- rcu_read_lock();
+- tcred = __task_cred(task);
+- if ((cred->uid != tcred->euid ||
+- cred->uid != tcred->suid ||
+- cred->uid != tcred->uid ||
+- cred->gid != tcred->egid ||
+- cred->gid != tcred->sgid ||
+- cred->gid != tcred->gid) &&
+- !capable(CAP_SYS_PTRACE)) {
+- rcu_read_unlock();
+- return -EPERM;
+- }
+- rcu_read_unlock();
+- smp_rmb();
+- if (task->mm)
+- dumpable = get_dumpable(task->mm);
+- if (!dumpable && !capable(CAP_SYS_PTRACE))
+- return -EPERM;
+-
+- return security_ptrace_access_check(task, mode);
+-}
+-
+-bool ptrace_may_access(struct task_struct *task, unsigned int mode)
+-{
+- int err;
+- task_lock(task);
+- err = __ptrace_may_access(task, mode);
+- task_unlock(task);
+- return !err;
+-}
+-
+ int ptrace_attach(struct task_struct *task)
+ {
+ int retval;
+@@ -242,57 +505,6 @@ int ptrace_traceme(void)
+ return ret;
+ }
+
+-/*
+- * Called with irqs disabled, returns true if childs should reap themselves.
+- */
+-static int ignoring_children(struct sighand_struct *sigh)
+-{
+- int ret;
+- spin_lock(&sigh->siglock);
+- ret = (sigh->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) ||
+- (sigh->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT);
+- spin_unlock(&sigh->siglock);
+- return ret;
+-}
+-
+-/*
+- * Called with tasklist_lock held for writing.
+- * Unlink a traced task, and clean it up if it was a traced zombie.
+- * Return true if it needs to be reaped with release_task().
+- * (We can't call release_task() here because we already hold tasklist_lock.)
+- *
+- * If it's a zombie, our attachedness prevented normal parent notification
+- * or self-reaping. Do notification now if it would have happened earlier.
+- * If it should reap itself, return true.
+- *
+- * If it's our own child, there is no notification to do. But if our normal
+- * children self-reap, then this child was prevented by ptrace and we must
+- * reap it now, in that case we must also wake up sub-threads sleeping in
+- * do_wait().
+- */
+-static bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p)
+-{
+- __ptrace_unlink(p);
+-
+- if (p->exit_state == EXIT_ZOMBIE) {
+- if (!task_detached(p) && thread_group_empty(p)) {
+- if (!same_thread_group(p->real_parent, tracer))
+- do_notify_parent(p, p->exit_signal);
+- else if (ignoring_children(tracer->sighand)) {
+- __wake_up_parent(p, tracer);
+- p->exit_signal = -1;
+- }
+- }
+- if (task_detached(p)) {
+- /* Mark it as in the process of being reaped. */
+- p->exit_state = EXIT_DEAD;
+- return true;
+- }
+- }
+-
+- return false;
+-}
+-
+ int ptrace_detach(struct task_struct *child, unsigned int data)
+ {
+ bool dead = false;
+@@ -346,56 +558,6 @@ void exit_ptrace(struct task_struct *tracer)
+ }
+ }
+
+-int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len)
+-{
+- int copied = 0;
+-
+- while (len > 0) {
+- char buf[128];
+- int this_len, retval;
+-
+- this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
+- retval = access_process_vm(tsk, src, buf, this_len, 0);
+- if (!retval) {
+- if (copied)
+- break;
+- return -EIO;
+- }
+- if (copy_to_user(dst, buf, retval))
+- return -EFAULT;
+- copied += retval;
+- src += retval;
+- dst += retval;
+- len -= retval;
+- }
+- return copied;
+-}
+-
+-int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len)
+-{
+- int copied = 0;
+-
+- while (len > 0) {
+- char buf[128];
+- int this_len, retval;
+-
+- this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
+- if (copy_from_user(buf, src, this_len))
+- return -EFAULT;
+- retval = access_process_vm(tsk, dst, buf, this_len, 1);
+- if (!retval) {
+- if (copied)
+- break;
+- return -EIO;
+- }
+- copied += retval;
+- src += retval;
+- dst += retval;
+- len -= retval;
+- }
+- return copied;
+-}
+-
+ static int ptrace_setoptions(struct task_struct *child, long data)
+ {
+ child->ptrace &= ~PT_TRACE_MASK;
+@@ -456,7 +618,6 @@ static int ptrace_setsiginfo(struct task_struct *child, const siginfo_t *info)
+ return error;
+ }
+
+-
+ #ifdef PTRACE_SINGLESTEP
+ #define is_singlestep(request) ((request) == PTRACE_SINGLESTEP)
+ #else
+@@ -510,47 +671,6 @@ static int ptrace_resume(struct task_struct *child, long request, long data)
+ return 0;
+ }
+
+-#ifdef CONFIG_HAVE_ARCH_TRACEHOOK
+-
+-static const struct user_regset *
+-find_regset(const struct user_regset_view *view, unsigned int type)
+-{
+- const struct user_regset *regset;
+- int n;
+-
+- for (n = 0; n < view->n; ++n) {
+- regset = view->regsets + n;
+- if (regset->core_note_type == type)
+- return regset;
+- }
+-
+- return NULL;
+-}
+-
+-static int ptrace_regset(struct task_struct *task, int req, unsigned int type,
+- struct iovec *kiov)
+-{
+- const struct user_regset_view *view = task_user_regset_view(task);
+- const struct user_regset *regset = find_regset(view, type);
+- int regset_no;
+-
+- if (!regset || (kiov->iov_len % regset->size) != 0)
+- return -EINVAL;
+-
+- regset_no = regset - view->regsets;
+- kiov->iov_len = min(kiov->iov_len,
+- (__kernel_size_t) (regset->n * regset->size));
+-
+- if (req == PTRACE_GETREGSET)
+- return copy_regset_to_user(task, view, regset_no, 0,
+- kiov->iov_len, kiov->iov_base);
+- else
+- return copy_regset_from_user(task, view, regset_no, 0,
+- kiov->iov_len, kiov->iov_base);
+-}
+-
+-#endif
+-
+ int ptrace_request(struct task_struct *child, long request,
+ long addr, long data)
+ {
+@@ -666,88 +786,7 @@ int ptrace_request(struct task_struct *child, long request,
+ return ret;
+ }
+
+-static struct task_struct *ptrace_get_task_struct(pid_t pid)
+-{
+- struct task_struct *child;
+-
+- rcu_read_lock();
+- child = find_task_by_vpid(pid);
+- if (child)
+- get_task_struct(child);
+- rcu_read_unlock();
+-
+- if (!child)
+- return ERR_PTR(-ESRCH);
+- return child;
+-}
+-
+-#ifndef arch_ptrace_attach
+-#define arch_ptrace_attach(child) do { } while (0)
+-#endif
+-
+-SYSCALL_DEFINE4(ptrace, long, request, long, pid, long, addr, long, data)
+-{
+- struct task_struct *child;
+- long ret;
+-
+- if (request == PTRACE_TRACEME) {
+- ret = ptrace_traceme();
+- if (!ret)
+- arch_ptrace_attach(current);
+- goto out;
+- }
+-
+- child = ptrace_get_task_struct(pid);
+- if (IS_ERR(child)) {
+- ret = PTR_ERR(child);
+- goto out;
+- }
+-
+- if (request == PTRACE_ATTACH) {
+- ret = ptrace_attach(child);
+- /*
+- * Some architectures need to do book-keeping after
+- * a ptrace attach.
+- */
+- if (!ret)
+- arch_ptrace_attach(child);
+- goto out_put_task_struct;
+- }
+-
+- ret = ptrace_check_attach(child, request == PTRACE_KILL);
+- if (ret < 0)
+- goto out_put_task_struct;
+-
+- ret = arch_ptrace(child, request, addr, data);
+-
+- out_put_task_struct:
+- put_task_struct(child);
+- out:
+- return ret;
+-}
+-
+-int generic_ptrace_peekdata(struct task_struct *tsk, long addr, long data)
+-{
+- unsigned long tmp;
+- int copied;
+-
+- copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), 0);
+- if (copied != sizeof(tmp))
+- return -EIO;
+- return put_user(tmp, (unsigned long __user *)data);
+-}
+-
+-int generic_ptrace_pokedata(struct task_struct *tsk, long addr, long data)
+-{
+- int copied;
+-
+- copied = access_process_vm(tsk, addr, &data, sizeof(data), 1);
+- return (copied == sizeof(data)) ? 0 : -EIO;
+-}
+-
+ #if defined CONFIG_COMPAT
+-#include <linux/compat.h>
+-
+ int compat_ptrace_request(struct task_struct *child, compat_long_t request,
+ compat_ulong_t addr, compat_ulong_t data)
+ {
+@@ -825,42 +864,5 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request,
+
+ return ret;
+ }
+-
+-asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
+- compat_long_t addr, compat_long_t data)
+-{
+- struct task_struct *child;
+- long ret;
+-
+- if (request == PTRACE_TRACEME) {
+- ret = ptrace_traceme();
+- goto out;
+- }
+-
+- child = ptrace_get_task_struct(pid);
+- if (IS_ERR(child)) {
+- ret = PTR_ERR(child);
+- goto out;
+- }
+-
+- if (request == PTRACE_ATTACH) {
+- ret = ptrace_attach(child);
+- /*
+- * Some architectures need to do book-keeping after
+- * a ptrace attach.
+- */
+- if (!ret)
+- arch_ptrace_attach(child);
+- goto out_put_task_struct;
+- }
+-
+- ret = ptrace_check_attach(child, request == PTRACE_KILL);
+- if (!ret)
+- ret = compat_arch_ptrace(child, request, addr, data);
+-
+- out_put_task_struct:
+- put_task_struct(child);
+- out:
+- return ret;
+-}
+ #endif /* CONFIG_COMPAT */
++#endif /* CONFIG_UTRACE */
+diff --git a/kernel/signal.c b/kernel/signal.c
+index 906ae5a..8087f13 100644
+--- a/kernel/signal.c
++++ b/kernel/signal.c
+@@ -1518,7 +1518,7 @@ int do_notify_parent(struct task_struct *tsk, int sig)
+ return ret;
+ }
+
+-static void do_notify_parent_cldstop(struct task_struct *tsk, int why)
++void do_notify_parent_cldstop(struct task_struct *tsk, int why)
+ {
+ struct siginfo info;
+ unsigned long flags;
+@@ -1788,7 +1788,7 @@ static int do_signal_stop(int signr)
+ static int ptrace_signal(int signr, siginfo_t *info,
+ struct pt_regs *regs, void *cookie)
+ {
+- if (!task_ptrace(current))
++ if (!(task_ptrace(current) & PT_PTRACED))
+ return signr;
+
+ ptrace_signal_deliver(regs, cookie);
+diff --git a/kernel/utrace.c b/kernel/utrace.c
+new file mode 100644
+index 0000000..f5a9e2c
+--- /dev/null
++++ b/kernel/utrace.c
+@@ -0,0 +1,2452 @@
++/*
++ * utrace infrastructure interface for debugging user processes
++ *
++ * Copyright (C) 2006-2010 Red Hat, Inc. All rights reserved.
++ *
++ * This copyrighted material is made available to anyone wishing to use,
++ * modify, copy, or redistribute it subject to the terms and conditions
++ * of the GNU General Public License v.2.
++ *
++ * Red Hat Author: Roland McGrath.
++ */
++
++#include <linux/utrace.h>
++#include <linux/tracehook.h>
++#include <linux/regset.h>
++#include <asm/syscall.h>
++#include <linux/ptrace.h>
++#include <linux/err.h>
++#include <linux/sched.h>
++#include <linux/freezer.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/seq_file.h>
++
++
++/*
++ * Per-thread structure private to utrace implementation.
++ * If task_struct.utrace_flags is nonzero, task_struct.utrace
++ * has always been allocated first. Once allocated, it is
++ * never freed until free_task().
++ *
++ * The common event reporting loops are done by the task making the
++ * report without ever taking any locks. To facilitate this, the two
++ * lists @attached and @attaching work together for smooth asynchronous
++ * attaching with low overhead. Modifying either list requires @lock.
++ * The @attaching list can be modified any time while holding @lock.
++ * New engines being attached always go on this list.
++ *
++ * The @attached list is what the task itself uses for its reporting
++ * loops. When the task itself is not quiescent, it can use the
++ * @attached list without taking any lock. Nobody may modify the list
++ * when the task is not quiescent. When it is quiescent, that means
++ * that it won't run again without taking @lock itself before using
++ * the list.
++ *
++ * At each place where we know the task is quiescent (or it's current),
++ * while holding @lock, we call splice_attaching(), below. This moves
++ * the @attaching list members on to the end of the @attached list.
++ * Since this happens at the start of any reporting pass, any new
++ * engines attached asynchronously go on the stable @attached list
++ * in time to have their callbacks seen.
++ */
++struct utrace {
++ spinlock_t lock;
++ struct list_head attached, attaching;
++
++ struct task_struct *cloning;
++
++ struct utrace_engine *reporting;
++
++ enum utrace_resume_action resume:UTRACE_RESUME_BITS;
++ unsigned int signal_handler:1;
++ unsigned int vfork_stop:1; /* need utrace_stop() before vfork wait */
++ unsigned int death:1; /* in utrace_report_death() now */
++ unsigned int reap:1; /* release_task() has run */
++ unsigned int pending_attach:1; /* need splice_attaching() */
++};
++
++static struct kmem_cache *utrace_cachep;
++static struct kmem_cache *utrace_engine_cachep;
++static const struct utrace_engine_ops utrace_detached_ops; /* forward decl */
++
++static int __init utrace_init(void)
++{
++ utrace_cachep = KMEM_CACHE(utrace, SLAB_PANIC);
++ utrace_engine_cachep = KMEM_CACHE(utrace_engine, SLAB_PANIC);
++ return 0;
++}
++module_init(utrace_init);
++
++/*
++ * Set up @task.utrace for the first time. We can have races
++ * between two utrace_attach_task() calls here. The task_lock()
++ * governs installing the new pointer. If another one got in first,
++ * we just punt the new one we allocated.
++ *
++ * This returns false only in case of a memory allocation failure.
++ */
++static bool utrace_task_alloc(struct task_struct *task)
++{
++ struct utrace *utrace = kmem_cache_zalloc(utrace_cachep, GFP_KERNEL);
++ if (unlikely(!utrace))
++ return false;
++ spin_lock_init(&utrace->lock);
++ INIT_LIST_HEAD(&utrace->attached);
++ INIT_LIST_HEAD(&utrace->attaching);
++ utrace->resume = UTRACE_RESUME;
++ task_lock(task);
++ if (likely(!task->utrace)) {
++ /*
++ * This barrier makes sure the initialization of the struct
++ * precedes the installation of the pointer. This pairs
++ * with smp_read_barrier_depends() in task_utrace_struct().
++ */
++ smp_wmb();
++ task->utrace = utrace;
++ }
++ task_unlock(task);
++
++ if (unlikely(task->utrace != utrace))
++ kmem_cache_free(utrace_cachep, utrace);
++ return true;
++}
++
++/*
++ * This is called via tracehook_free_task() from free_task()
++ * when @task is being deallocated.
++ */
++void utrace_free_task(struct task_struct *task)
++{
++ kmem_cache_free(utrace_cachep, task->utrace);
++}
++
++/*
++ * This is calledwhen the task is safely quiescent, i.e. it won't consult
++ * utrace->attached without the lock. Move any engines attached
++ * asynchronously from @utrace->attaching onto the @utrace->attached list.
++ */
++static void splice_attaching(struct utrace *utrace)
++{
++ lockdep_assert_held(&utrace->lock);
++ list_splice_tail_init(&utrace->attaching, &utrace->attached);
++ utrace->pending_attach = 0;
++}
++
++/*
++ * This is the exported function used by the utrace_engine_put() inline.
++ */
++void __utrace_engine_release(struct kref *kref)
++{
++ struct utrace_engine *engine = container_of(kref, struct utrace_engine,
++ kref);
++ BUG_ON(!list_empty(&engine->entry));
++ if (engine->release)
++ (*engine->release)(engine->data);
++ kmem_cache_free(utrace_engine_cachep, engine);
++}
++EXPORT_SYMBOL_GPL(__utrace_engine_release);
++
++static bool engine_matches(struct utrace_engine *engine, int flags,
++ const struct utrace_engine_ops *ops, void *data)
++{
++ if ((flags & UTRACE_ATTACH_MATCH_OPS) && engine->ops != ops)
++ return false;
++ if ((flags & UTRACE_ATTACH_MATCH_DATA) && engine->data != data)
++ return false;
++ return engine->ops && engine->ops != &utrace_detached_ops;
++}
++
++static struct utrace_engine *find_matching_engine(
++ struct utrace *utrace, int flags,
++ const struct utrace_engine_ops *ops, void *data)
++{
++ struct utrace_engine *engine;
++ list_for_each_entry(engine, &utrace->attached, entry)
++ if (engine_matches(engine, flags, ops, data))
++ return engine;
++ list_for_each_entry(engine, &utrace->attaching, entry)
++ if (engine_matches(engine, flags, ops, data))
++ return engine;
++ return NULL;
++}
++
++/*
++ * Enqueue @engine, or maybe don't if UTRACE_ATTACH_EXCLUSIVE.
++ */
++static int utrace_add_engine(struct task_struct *target,
++ struct utrace *utrace,
++ struct utrace_engine *engine,
++ int flags,
++ const struct utrace_engine_ops *ops,
++ void *data)
++{
++ int ret;
++
++ spin_lock(&utrace->lock);
++
++ ret = -EEXIST;
++ if ((flags & UTRACE_ATTACH_EXCLUSIVE) &&
++ unlikely(find_matching_engine(utrace, flags, ops, data)))
++ goto unlock;
++
++ /*
++ * In case we had no engines before, make sure that
++ * utrace_flags is not zero. Since we did unlock+lock
++ * at least once after utrace_task_alloc() installed
++ * ->utrace, we have the necessary barrier which pairs
++ * with rmb() in task_utrace_struct().
++ */
++ ret = -ESRCH;
++ if (!target->utrace_flags) {
++ target->utrace_flags = UTRACE_EVENT(REAP);
++ /*
++ * If we race with tracehook_prepare_release_task()
++ * make sure that either it sees utrace_flags != 0
++ * or we see exit_state == EXIT_DEAD.
++ */
++ smp_mb();
++ if (unlikely(target->exit_state == EXIT_DEAD)) {
++ target->utrace_flags = 0;
++ goto unlock;
++ }
++ }
++
++ /*
++ * Put the new engine on the pending ->attaching list.
++ * Make sure it gets onto the ->attached list by the next
++ * time it's examined. Setting ->pending_attach ensures
++ * that start_report() takes the lock and splices the lists
++ * before the next new reporting pass.
++ *
++ * When target == current, it would be safe just to call
++ * splice_attaching() right here. But if we're inside a
++ * callback, that would mean the new engine also gets
++ * notified about the event that precipitated its own
++ * creation. This is not what the user wants.
++ */
++ list_add_tail(&engine->entry, &utrace->attaching);
++ utrace->pending_attach = 1;
++ ret = 0;
++unlock:
++ spin_unlock(&utrace->lock);
++
++ return ret;
++}
++
++/**
++ * utrace_attach_task - attach new engine, or look up an attached engine
++ * @target: thread to attach to
++ * @flags: flag bits combined with OR, see below
++ * @ops: callback table for new engine
++ * @data: engine private data pointer
++ *
++ * The caller must ensure that the @target thread does not get freed,
++ * i.e. hold a ref or be its parent. It is always safe to call this
++ * on @current, or on the @child pointer in a @report_clone callback.
++ * For most other cases, it's easier to use utrace_attach_pid() instead.
++ *
++ * UTRACE_ATTACH_CREATE:
++ * Create a new engine. If %UTRACE_ATTACH_CREATE is not specified, you
++ * only look up an existing engine already attached to the thread.
++ *
++ * UTRACE_ATTACH_EXCLUSIVE:
++ * Attempting to attach a second (matching) engine fails with -%EEXIST.
++ *
++ * UTRACE_ATTACH_MATCH_OPS: Only consider engines matching @ops.
++ * UTRACE_ATTACH_MATCH_DATA: Only consider engines matching @data.
++ *
++ * Calls with neither %UTRACE_ATTACH_MATCH_OPS nor %UTRACE_ATTACH_MATCH_DATA
++ * match the first among any engines attached to @target. That means that
++ * %UTRACE_ATTACH_EXCLUSIVE in such a call fails with -%EEXIST if there
++ * are any engines on @target at all.
++ */
++struct utrace_engine *utrace_attach_task(
++ struct task_struct *target, int flags,
++ const struct utrace_engine_ops *ops, void *data)
++{
++ struct utrace *utrace = task_utrace_struct(target);
++ struct utrace_engine *engine;
++ int ret;
++
++ if (!(flags & UTRACE_ATTACH_CREATE)) {
++ if (unlikely(!utrace))
++ return ERR_PTR(-ENOENT);
++ spin_lock(&utrace->lock);
++ engine = find_matching_engine(utrace, flags, ops, data);
++ if (engine)
++ utrace_engine_get(engine);
++ spin_unlock(&utrace->lock);
++ return engine ?: ERR_PTR(-ENOENT);
++ }
++
++ if (unlikely(!ops) || unlikely(ops == &utrace_detached_ops))
++ return ERR_PTR(-EINVAL);
++
++ if (unlikely(target->flags & PF_KTHREAD))
++ /*
++ * Silly kernel, utrace is for users!
++ */
++ return ERR_PTR(-EPERM);
++
++ if (!utrace) {
++ if (unlikely(!utrace_task_alloc(target)))
++ return ERR_PTR(-ENOMEM);
++ utrace = task_utrace_struct(target);
++ }
++
++ engine = kmem_cache_alloc(utrace_engine_cachep, GFP_KERNEL);
++ if (unlikely(!engine))
++ return ERR_PTR(-ENOMEM);
++
++ /*
++ * Initialize the new engine structure. It starts out with two
++ * refs: one ref to return, and one ref for being attached.
++ */
++ kref_set(&engine->kref, 2);
++ engine->flags = 0;
++ engine->ops = ops;
++ engine->data = data;
++ engine->release = ops->release;
++
++ ret = utrace_add_engine(target, utrace, engine, flags, ops, data);
++
++ if (unlikely(ret)) {
++ kmem_cache_free(utrace_engine_cachep, engine);
++ engine = ERR_PTR(ret);
++ }
++
++ return engine;
++}
++EXPORT_SYMBOL_GPL(utrace_attach_task);
++
++/**
++ * utrace_attach_pid - attach new engine, or look up an attached engine
++ * @pid: &struct pid pointer representing thread to attach to
++ * @flags: flag bits combined with OR, see utrace_attach_task()
++ * @ops: callback table for new engine
++ * @data: engine private data pointer
++ *
++ * This is the same as utrace_attach_task(), but takes a &struct pid
++ * pointer rather than a &struct task_struct pointer. The caller must
++ * hold a ref on @pid, but does not need to worry about the task
++ * staying valid. If it's been reaped so that @pid points nowhere,
++ * then this call returns -%ESRCH.
++ */
++struct utrace_engine *utrace_attach_pid(
++ struct pid *pid, int flags,
++ const struct utrace_engine_ops *ops, void *data)
++{
++ struct utrace_engine *engine = ERR_PTR(-ESRCH);
++ struct task_struct *task = get_pid_task(pid, PIDTYPE_PID);
++ if (task) {
++ engine = utrace_attach_task(task, flags, ops, data);
++ put_task_struct(task);
++ }
++ return engine;
++}
++EXPORT_SYMBOL_GPL(utrace_attach_pid);
++
++/*
++ * When an engine is detached, the target thread may still see it and
++ * make callbacks until it quiesces. We install a special ops vector
++ * with these two callbacks. When the target thread quiesces, it can
++ * safely free the engine itself. For any event we will always get
++ * the report_quiesce() callback first, so we only need this one
++ * pointer to be set. The only exception is report_reap(), so we
++ * supply that callback too.
++ */
++static u32 utrace_detached_quiesce(u32 action, struct utrace_engine *engine,
++ unsigned long event)
++{
++ return UTRACE_DETACH;
++}
++
++static void utrace_detached_reap(struct utrace_engine *engine,
++ struct task_struct *task)
++{
++}
++
++static const struct utrace_engine_ops utrace_detached_ops = {
++ .report_quiesce = &utrace_detached_quiesce,
++ .report_reap = &utrace_detached_reap
++};
++
++/*
++ * The caller has to hold a ref on the engine. If the attached flag is
++ * true (all but utrace_barrier() calls), the engine is supposed to be
++ * attached. If the attached flag is false (utrace_barrier() only),
++ * then return -ERESTARTSYS for an engine marked for detach but not yet
++ * fully detached. The task pointer can be invalid if the engine is
++ * detached.
++ *
++ * Get the utrace lock for the target task.
++ * Returns the struct if locked, or ERR_PTR(-errno).
++ *
++ * This has to be robust against races with:
++ * utrace_control(target, UTRACE_DETACH) calls
++ * UTRACE_DETACH after reports
++ * utrace_report_death
++ * utrace_release_task
++ */
++static struct utrace *get_utrace_lock(struct task_struct *target,
++ struct utrace_engine *engine,
++ bool attached)
++ __acquires(utrace->lock)
++{
++ struct utrace *utrace;
++
++ rcu_read_lock();
++
++ /*
++ * If this engine was already detached, bail out before we look at
++ * the task_struct pointer at all. If it's detached after this
++ * check, then RCU is still keeping this task_struct pointer valid.
++ *
++ * The ops pointer is NULL when the engine is fully detached.
++ * It's &utrace_detached_ops when it's marked detached but still
++ * on the list. In the latter case, utrace_barrier() still works,
++ * since the target might be in the middle of an old callback.
++ */
++ if (unlikely(!engine->ops)) {
++ rcu_read_unlock();
++ return ERR_PTR(-ESRCH);
++ }
++
++ if (unlikely(engine->ops == &utrace_detached_ops)) {
++ rcu_read_unlock();
++ return attached ? ERR_PTR(-ESRCH) : ERR_PTR(-ERESTARTSYS);
++ }
++
++ utrace = task_utrace_struct(target);
++ spin_lock(&utrace->lock);
++ if (unlikely(!engine->ops) ||
++ unlikely(engine->ops == &utrace_detached_ops)) {
++ /*
++ * By the time we got the utrace lock,
++ * it had been reaped or detached already.
++ */
++ spin_unlock(&utrace->lock);
++ utrace = ERR_PTR(-ESRCH);
++ if (!attached && engine->ops == &utrace_detached_ops)
++ utrace = ERR_PTR(-ERESTARTSYS);
++ }
++ rcu_read_unlock();
++
++ return utrace;
++}
++
++/*
++ * Now that we don't hold any locks, run through any
++ * detached engines and free their references. Each
++ * engine had one implicit ref while it was attached.
++ */
++static void put_detached_list(struct list_head *list)
++{
++ struct utrace_engine *engine, *next;
++ list_for_each_entry_safe(engine, next, list, entry) {
++ list_del_init(&engine->entry);
++ utrace_engine_put(engine);
++ }
++}
++
++/*
++ * We use an extra bit in utrace_engine.flags past the event bits,
++ * to record whether the engine is keeping the target thread stopped.
++ *
++ * This bit is set in task_struct.utrace_flags whenever it is set in any
++ * engine's flags. Only utrace_reset() resets it in utrace_flags.
++ */
++#define ENGINE_STOP (1UL << _UTRACE_NEVENTS)
++
++static void mark_engine_wants_stop(struct task_struct *task,
++ struct utrace_engine *engine)
++{
++ engine->flags |= ENGINE_STOP;
++ task->utrace_flags |= ENGINE_STOP;
++}
++
++static void clear_engine_wants_stop(struct utrace_engine *engine)
++{
++ engine->flags &= ~ENGINE_STOP;
++}
++
++static bool engine_wants_stop(struct utrace_engine *engine)
++{
++ return (engine->flags & ENGINE_STOP) != 0;
++}
++
++/**
++ * utrace_set_events - choose which event reports a tracing engine gets
++ * @target: thread to affect
++ * @engine: attached engine to affect
++ * @events: new event mask
++ *
++ * This changes the set of events for which @engine wants callbacks made.
++ *
++ * This fails with -%EALREADY and does nothing if you try to clear
++ * %UTRACE_EVENT(%DEATH) when the @report_death callback may already have
++ * begun, if you try to clear %UTRACE_EVENT(%REAP) when the @report_reap
++ * callback may already have begun, or if you try to newly set
++ * %UTRACE_EVENT(%DEATH) or %UTRACE_EVENT(%QUIESCE) when @target is
++ * already dead or dying.
++ *
++ * This can fail with -%ESRCH when @target has already been detached,
++ * including forcible detach on reaping.
++ *
++ * If @target was stopped before the call, then after a successful call,
++ * no event callbacks not requested in @events will be made; if
++ * %UTRACE_EVENT(%QUIESCE) is included in @events, then a
++ * @report_quiesce callback will be made when @target resumes.
++ *
++ * If @target was not stopped and @events excludes some bits that were
++ * set before, this can return -%EINPROGRESS to indicate that @target
++ * may have been making some callback to @engine. When this returns
++ * zero, you can be sure that no event callbacks you've disabled in
++ * @events can be made. If @events only sets new bits that were not set
++ * before on @engine, then -%EINPROGRESS will never be returned.
++ *
++ * To synchronize after an -%EINPROGRESS return, see utrace_barrier().
++ *
++ * When @target is @current, -%EINPROGRESS is not returned. But note
++ * that a newly-created engine will not receive any callbacks related to
++ * an event notification already in progress. This call enables @events
++ * callbacks to be made as soon as @engine becomes eligible for any
++ * callbacks, see utrace_attach_task().
++ *
++ * These rules provide for coherent synchronization based on %UTRACE_STOP,
++ * even when %SIGKILL is breaking its normal simple rules.
++ */
++int utrace_set_events(struct task_struct *target,
++ struct utrace_engine *engine,
++ unsigned long events)
++{
++ struct utrace *utrace;
++ unsigned long old_flags, old_utrace_flags;
++ int ret;
++
++ /*
++ * We just ignore the internal bit, so callers can use
++ * engine->flags to seed bitwise ops for our argument.
++ */
++ events &= ~ENGINE_STOP;
++
++ utrace = get_utrace_lock(target, engine, true);
++ if (unlikely(IS_ERR(utrace)))
++ return PTR_ERR(utrace);
++
++ old_utrace_flags = target->utrace_flags;
++ old_flags = engine->flags & ~ENGINE_STOP;
++
++ if (target->exit_state &&
++ (((events & ~old_flags) & _UTRACE_DEATH_EVENTS) ||
++ (utrace->death &&
++ ((old_flags & ~events) & _UTRACE_DEATH_EVENTS)) ||
++ (utrace->reap && ((old_flags & ~events) & UTRACE_EVENT(REAP))))) {
++ spin_unlock(&utrace->lock);
++ return -EALREADY;
++ }
++
++ /*
++ * When setting these flags, it's essential that we really
++ * synchronize with exit_notify(). They cannot be set after
++ * exit_notify() takes the tasklist_lock. By holding the read
++ * lock here while setting the flags, we ensure that the calls
++ * to tracehook_notify_death() and tracehook_report_death() will
++ * see the new flags. This ensures that utrace_release_task()
++ * knows positively that utrace_report_death() will be called or
++ * that it won't.
++ */
++ if ((events & ~old_utrace_flags) & _UTRACE_DEATH_EVENTS) {
++ read_lock(&tasklist_lock);
++ if (unlikely(target->exit_state)) {
++ read_unlock(&tasklist_lock);
++ spin_unlock(&utrace->lock);
++ return -EALREADY;
++ }
++ target->utrace_flags |= events;
++ read_unlock(&tasklist_lock);
++ }
++
++ engine->flags = events | (engine->flags & ENGINE_STOP);
++ target->utrace_flags |= events;
++
++ if ((events & UTRACE_EVENT_SYSCALL) &&
++ !(old_utrace_flags & UTRACE_EVENT_SYSCALL))
++ set_tsk_thread_flag(target, TIF_SYSCALL_TRACE);
++
++ ret = 0;
++ if ((old_flags & ~events) && target != current &&
++ !task_is_stopped_or_traced(target) && !target->exit_state) {
++ /*
++ * This barrier ensures that our engine->flags changes
++ * have hit before we examine utrace->reporting,
++ * pairing with the barrier in start_callback(). If
++ * @target has not yet hit finish_callback() to clear
++ * utrace->reporting, we might be in the middle of a
++ * callback to @engine.
++ */
++ smp_mb();
++ if (utrace->reporting == engine)
++ ret = -EINPROGRESS;
++ }
++
++ spin_unlock(&utrace->lock);
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(utrace_set_events);
++
++/*
++ * Asynchronously mark an engine as being detached.
++ *
++ * This must work while the target thread races with us doing
++ * start_callback(), defined below. It uses smp_rmb() between checking
++ * @engine->flags and using @engine->ops. Here we change @engine->ops
++ * first, then use smp_wmb() before changing @engine->flags. This ensures
++ * it can check the old flags before using the old ops, or check the old
++ * flags before using the new ops, or check the new flags before using the
++ * new ops, but can never check the new flags before using the old ops.
++ * Hence, utrace_detached_ops might be used with any old flags in place.
++ * It has report_quiesce() and report_reap() callbacks to handle all cases.
++ */
++static void mark_engine_detached(struct utrace_engine *engine)
++{
++ engine->ops = &utrace_detached_ops;
++ smp_wmb();
++ engine->flags = UTRACE_EVENT(QUIESCE);
++}
++
++/*
++ * Get @target to stop and return true if it is already stopped now.
++ * If we return false, it will make some event callback soonish.
++ * Called with @utrace locked.
++ */
++static bool utrace_do_stop(struct task_struct *target, struct utrace *utrace)
++{
++ if (task_is_stopped(target)) {
++ /*
++ * Stopped is considered quiescent; when it wakes up, it will
++ * go through utrace_finish_stop() before doing anything else.
++ */
++ spin_lock_irq(&target->sighand->siglock);
++ if (likely(task_is_stopped(target)))
++ __set_task_state(target, TASK_TRACED);
++ spin_unlock_irq(&target->sighand->siglock);
++ } else if (utrace->resume > UTRACE_REPORT) {
++ utrace->resume = UTRACE_REPORT;
++ set_notify_resume(target);
++ }
++
++ return task_is_traced(target);
++}
++
++/*
++ * If the target is not dead it should not be in tracing
++ * stop any more. Wake it unless it's in job control stop.
++ */
++static void utrace_wakeup(struct task_struct *target, struct utrace *utrace)
++{
++ lockdep_assert_held(&utrace->lock);
++ spin_lock_irq(&target->sighand->siglock);
++ if (target->signal->flags & SIGNAL_STOP_STOPPED ||
++ target->signal->group_stop_count)
++ target->state = TASK_STOPPED;
++ else
++ wake_up_state(target, __TASK_TRACED);
++ spin_unlock_irq(&target->sighand->siglock);
++}
++
++/*
++ * This is called when there might be some detached engines on the list or
++ * some stale bits in @task->utrace_flags. Clean them up and recompute the
++ * flags. Returns true if we're now fully detached.
++ *
++ * Called with @utrace->lock held, returns with it released.
++ * After this returns, @utrace might be freed if everything detached.
++ */
++static bool utrace_reset(struct task_struct *task, struct utrace *utrace)
++ __releases(utrace->lock)
++{
++ struct utrace_engine *engine, *next;
++ unsigned long flags = 0;
++ LIST_HEAD(detached);
++
++ splice_attaching(utrace);
++
++ /*
++ * Update the set of events of interest from the union
++ * of the interests of the remaining tracing engines.
++ * For any engine marked detached, remove it from the list.
++ * We'll collect them on the detached list.
++ */
++ list_for_each_entry_safe(engine, next, &utrace->attached, entry) {
++ if (engine->ops == &utrace_detached_ops) {
++ engine->ops = NULL;
++ list_move(&engine->entry, &detached);
++ } else {
++ flags |= engine->flags | UTRACE_EVENT(REAP);
++ }
++ }
++
++ if (task->exit_state) {
++ /*
++ * Once it's already dead, we never install any flags
++ * except REAP. When ->exit_state is set and events
++ * like DEATH are not set, then they never can be set.
++ * This ensures that utrace_release_task() knows
++ * positively that utrace_report_death() can never run.
++ */
++ BUG_ON(utrace->death);
++ flags &= UTRACE_EVENT(REAP);
++ } else if (!(flags & UTRACE_EVENT_SYSCALL) &&
++ test_tsk_thread_flag(task, TIF_SYSCALL_TRACE)) {
++ clear_tsk_thread_flag(task, TIF_SYSCALL_TRACE);
++ }
++
++ if (!flags) {
++ /*
++ * No more engines, cleared out the utrace.
++ */
++ utrace->resume = UTRACE_RESUME;
++ utrace->signal_handler = 0;
++ }
++
++ /*
++ * If no more engines want it stopped, wake it up.
++ */
++ if (task_is_traced(task) && !(flags & ENGINE_STOP))
++ utrace_wakeup(task, utrace);
++
++ /*
++ * In theory spin_lock() doesn't imply rcu_read_lock().
++ * Once we clear ->utrace_flags this task_struct can go away
++ * because tracehook_prepare_release_task() path does not take
++ * utrace->lock when ->utrace_flags == 0.
++ */
++ rcu_read_lock();
++ task->utrace_flags = flags;
++ spin_unlock(&utrace->lock);
++ rcu_read_unlock();
++
++ put_detached_list(&detached);
++
++ return !flags;
++}
++
++void utrace_finish_stop(void)
++{
++ /*
++ * If we were task_is_traced() and then SIGKILL'ed, make
++ * sure we do nothing until the tracer drops utrace->lock.
++ */
++ if (unlikely(__fatal_signal_pending(current))) {
++ struct utrace *utrace = task_utrace_struct(current);
++ spin_unlock_wait(&utrace->lock);
++ }
++}
++
++/*
++ * Perform %UTRACE_STOP, i.e. block in TASK_TRACED until woken up.
++ * @task == current, @utrace == current->utrace, which is not locked.
++ * Return true if we were woken up by SIGKILL even though some utrace
++ * engine may still want us to stay stopped.
++ */
++static void utrace_stop(struct task_struct *task, struct utrace *utrace,
++ enum utrace_resume_action action)
++{
++relock:
++ spin_lock(&utrace->lock);
++
++ if (action < utrace->resume) {
++ /*
++ * Ensure a reporting pass when we're resumed.
++ */
++ utrace->resume = action;
++ if (action == UTRACE_INTERRUPT)
++ set_thread_flag(TIF_SIGPENDING);
++ else
++ set_thread_flag(TIF_NOTIFY_RESUME);
++ }
++
++ /*
++ * If the ENGINE_STOP bit is clear in utrace_flags, that means
++ * utrace_reset() ran after we processed some UTRACE_STOP return
++ * values from callbacks to get here. If all engines have detached
++ * or resumed us, we don't stop. This check doesn't require
++ * siglock, but it should follow the interrupt/report bookkeeping
++ * steps (this can matter for UTRACE_RESUME but not UTRACE_DETACH).
++ */
++ if (unlikely(!(task->utrace_flags & ENGINE_STOP))) {
++ utrace_reset(task, utrace);
++ if (task->utrace_flags & ENGINE_STOP)
++ goto relock;
++ return;
++ }
++
++ /*
++ * The siglock protects us against signals. As well as SIGKILL
++ * waking us up, we must synchronize with the signal bookkeeping
++ * for stop signals and SIGCONT.
++ */
++ spin_lock_irq(&task->sighand->siglock);
++
++ if (unlikely(__fatal_signal_pending(task))) {
++ spin_unlock_irq(&task->sighand->siglock);
++ spin_unlock(&utrace->lock);
++ return;
++ }
++
++ __set_current_state(TASK_TRACED);
++
++ /*
++ * If there is a group stop in progress,
++ * we must participate in the bookkeeping.
++ */
++ if (unlikely(task->signal->group_stop_count) &&
++ !--task->signal->group_stop_count)
++ task->signal->flags = SIGNAL_STOP_STOPPED;
++
++ spin_unlock_irq(&task->sighand->siglock);
++ spin_unlock(&utrace->lock);
++
++ /*
++ * If ptrace is among the reasons for this stop, do its
++ * notification now. This could not just be done in
++ * ptrace's own event report callbacks because it has to
++ * be done after we are in TASK_TRACED. This makes the
++ * synchronization with ptrace_do_wait() work right.
++ *
++ * It's only because of the bad old overloading of the do_wait()
++ * logic for handling ptrace stops that we need this special case
++ * here. One day we will clean up ptrace so it does not need to
++ * work this way. New things that are designed sensibly don't need
++ * a wakeup that synchronizes with tasklist_lock and ->state, so
++ * the proper utrace API does not try to support this weirdness.
++ */
++ ptrace_notify_stop(task);
++
++ schedule();
++
++ utrace_finish_stop();
++
++ /*
++ * While in TASK_TRACED, we were considered "frozen enough".
++ * Now that we woke up, it's crucial if we're supposed to be
++ * frozen that we freeze now before running anything substantial.
++ */
++ try_to_freeze();
++
++ /*
++ * While we were in TASK_TRACED, complete_signal() considered
++ * us "uninterested" in signal wakeups. Now make sure our
++ * TIF_SIGPENDING state is correct for normal running.
++ */
++ spin_lock_irq(&task->sighand->siglock);
++ recalc_sigpending();
++ spin_unlock_irq(&task->sighand->siglock);
++}
++
++/*
++ * Called by release_task() with @reap set to true.
++ * Called by utrace_report_death() with @reap set to false.
++ * On reap, make report_reap callbacks and clean out @utrace
++ * unless still making callbacks. On death, update bookkeeping
++ * and handle the reap work if release_task() came in first.
++ */
++void utrace_maybe_reap(struct task_struct *target, struct utrace *utrace,
++ bool reap)
++{
++ struct utrace_engine *engine, *next;
++ struct list_head attached;
++
++ spin_lock(&utrace->lock);
++
++ if (reap) {
++ /*
++ * If the target will do some final callbacks but hasn't
++ * finished them yet, we know because it clears these event
++ * bits after it's done. Instead of cleaning up here and
++ * requiring utrace_report_death() to cope with it, we
++ * delay the REAP report and the teardown until after the
++ * target finishes its death reports.
++ */
++ utrace->reap = 1;
++
++ if (target->utrace_flags & _UTRACE_DEATH_EVENTS) {
++ spin_unlock(&utrace->lock);
++ return;
++ }
++ } else {
++ /*
++ * After we unlock with this flag clear, any competing
++ * utrace_control/utrace_set_events calls know that we've
++ * finished our callbacks and any detach bookkeeping.
++ */
++ utrace->death = 0;
++
++ if (!utrace->reap) {
++ /*
++ * We're just dead, not reaped yet. This will
++ * reset @target->utrace_flags so the later call
++ * with @reap set won't hit the check above.
++ */
++ utrace_reset(target, utrace);
++ return;
++ }
++ }
++
++ /*
++ * utrace_add_engine() checks ->utrace_flags != 0. Since
++ * @utrace->reap is set, nobody can set or clear UTRACE_EVENT(REAP)
++ * in @engine->flags or change @engine->ops and nobody can change
++ * @utrace->attached after we drop the lock.
++ */
++ target->utrace_flags = 0;
++
++ /*
++ * We clear out @utrace->attached before we drop the lock so
++ * that find_matching_engine() can't come across any old engine
++ * while we are busy tearing it down.
++ */
++ list_replace_init(&utrace->attached, &attached);
++ list_splice_tail_init(&utrace->attaching, &attached);
++
++ spin_unlock(&utrace->lock);
++
++ list_for_each_entry_safe(engine, next, &attached, entry) {
++ if (engine->flags & UTRACE_EVENT(REAP))
++ engine->ops->report_reap(engine, target);
++
++ engine->ops = NULL;
++ engine->flags = 0;
++ list_del_init(&engine->entry);
++
++ utrace_engine_put(engine);
++ }
++}
++
++/*
++ * You can't do anything to a dead task but detach it.
++ * If release_task() has been called, you can't do that.
++ *
++ * On the exit path, DEATH and QUIESCE event bits are set only
++ * before utrace_report_death() has taken the lock. At that point,
++ * the death report will come soon, so disallow detach until it's
++ * done. This prevents us from racing with it detaching itself.
++ *
++ * Called only when @target->exit_state is nonzero.
++ */
++static inline int utrace_control_dead(struct task_struct *target,
++ struct utrace *utrace,
++ enum utrace_resume_action action)
++{
++ lockdep_assert_held(&utrace->lock);
++
++ if (action != UTRACE_DETACH || unlikely(utrace->reap))
++ return -ESRCH;
++
++ if (unlikely(utrace->death))
++ /*
++ * We have already started the death report. We can't
++ * prevent the report_death and report_reap callbacks,
++ * so tell the caller they will happen.
++ */
++ return -EALREADY;
++
++ return 0;
++}
++
++/**
++ * utrace_control - control a thread being traced by a tracing engine
++ * @target: thread to affect
++ * @engine: attached engine to affect
++ * @action: &enum utrace_resume_action for thread to do
++ *
++ * This is how a tracing engine asks a traced thread to do something.
++ * This call is controlled by the @action argument, which has the
++ * same meaning as the &enum utrace_resume_action value returned by
++ * event reporting callbacks.
++ *
++ * If @target is already dead (@target->exit_state nonzero),
++ * all actions except %UTRACE_DETACH fail with -%ESRCH.
++ *
++ * The following sections describe each option for the @action argument.
++ *
++ * UTRACE_DETACH:
++ *
++ * After this, the @engine data structure is no longer accessible,
++ * and the thread might be reaped. The thread will start running
++ * again if it was stopped and no longer has any attached engines
++ * that want it stopped.
++ *
++ * If the @report_reap callback may already have begun, this fails
++ * with -%ESRCH. If the @report_death callback may already have
++ * begun, this fails with -%EALREADY.
++ *
++ * If @target is not already stopped, then a callback to this engine
++ * might be in progress or about to start on another CPU. If so,
++ * then this returns -%EINPROGRESS; the detach happens as soon as
++ * the pending callback is finished. To synchronize after an
++ * -%EINPROGRESS return, see utrace_barrier().
++ *
++ * If @target is properly stopped before utrace_control() is called,
++ * then after successful return it's guaranteed that no more callbacks
++ * to the @engine->ops vector will be made.
++ *
++ * The only exception is %SIGKILL (and exec or group-exit by another
++ * thread in the group), which can cause asynchronous @report_death
++ * and/or @report_reap callbacks even when %UTRACE_STOP was used.
++ * (In that event, this fails with -%ESRCH or -%EALREADY, see above.)
++ *
++ * UTRACE_STOP:
++ *
++ * This asks that @target stop running. This returns 0 only if
++ * @target is already stopped, either for tracing or for job
++ * control. Then @target will remain stopped until another
++ * utrace_control() call is made on @engine; @target can be woken
++ * only by %SIGKILL (or equivalent, such as exec or termination by
++ * another thread in the same thread group).
++ *
++ * This returns -%EINPROGRESS if @target is not already stopped.
++ * Then the effect is like %UTRACE_REPORT. A @report_quiesce or
++ * @report_signal callback will be made soon. Your callback can
++ * then return %UTRACE_STOP to keep @target stopped.
++ *
++ * This does not interrupt system calls in progress, including ones
++ * that sleep for a long time. For that, use %UTRACE_INTERRUPT.
++ * To interrupt system calls and then keep @target stopped, your
++ * @report_signal callback can return %UTRACE_STOP.
++ *
++ * UTRACE_RESUME:
++ *
++ * Just let @target continue running normally, reversing the effect
++ * of a previous %UTRACE_STOP. If another engine is keeping @target
++ * stopped, then it remains stopped until all engines let it resume.
++ * If @target was not stopped, this has no effect.
++ *
++ * UTRACE_REPORT:
++ *
++ * This is like %UTRACE_RESUME, but also ensures that there will be
++ * a @report_quiesce or @report_signal callback made soon. If
++ * @target had been stopped, then there will be a callback before it
++ * resumes running normally. If another engine is keeping @target
++ * stopped, then there might be no callbacks until all engines let
++ * it resume.
++ *
++ * Since this is meaningless unless @report_quiesce callbacks will
++ * be made, it returns -%EINVAL if @engine lacks %UTRACE_EVENT(%QUIESCE).
++ *
++ * UTRACE_INTERRUPT:
++ *
++ * This is like %UTRACE_REPORT, but ensures that @target will make a
++ * @report_signal callback before it resumes or delivers signals.
++ * If @target was in a system call or about to enter one, work in
++ * progress will be interrupted as if by %SIGSTOP. If another
++ * engine is keeping @target stopped, then there might be no
++ * callbacks until all engines let it resume.
++ *
++ * This gives @engine an opportunity to introduce a forced signal
++ * disposition via its @report_signal callback.
++ *
++ * UTRACE_SINGLESTEP:
++ *
++ * It's invalid to use this unless arch_has_single_step() returned true.
++ * This is like %UTRACE_RESUME, but resumes for one user instruction only.
++ *
++ * Note that passing %UTRACE_SINGLESTEP or %UTRACE_BLOCKSTEP to
++ * utrace_control() or returning it from an event callback alone does
++ * not necessarily ensure that stepping will be enabled. If there are
++ * more callbacks made to any engine before returning to user mode,
++ * then the resume action is chosen only by the last set of callbacks.
++ * To be sure, enable %UTRACE_EVENT(%QUIESCE) and look for the
++ * @report_quiesce callback with a zero event mask, or the
++ * @report_signal callback with %UTRACE_SIGNAL_REPORT.
++ *
++ * Since this is not robust unless @report_quiesce callbacks will
++ * be made, it returns -%EINVAL if @engine lacks %UTRACE_EVENT(%QUIESCE).
++ *
++ * UTRACE_BLOCKSTEP:
++ *
++ * It's invalid to use this unless arch_has_block_step() returned true.
++ * This is like %UTRACE_SINGLESTEP, but resumes for one whole basic
++ * block of user instructions.
++ *
++ * Since this is not robust unless @report_quiesce callbacks will
++ * be made, it returns -%EINVAL if @engine lacks %UTRACE_EVENT(%QUIESCE).
++ *
++ * %UTRACE_BLOCKSTEP devolves to %UTRACE_SINGLESTEP when another
++ * tracing engine is using %UTRACE_SINGLESTEP at the same time.
++ */
++int utrace_control(struct task_struct *target,
++ struct utrace_engine *engine,
++ enum utrace_resume_action action)
++{
++ struct utrace *utrace;
++ bool reset;
++ int ret;
++
++ if (unlikely(action >= UTRACE_RESUME_MAX)) {
++ WARN(1, "invalid action argument to utrace_control()!");
++ return -EINVAL;
++ }
++
++ /*
++ * This is a sanity check for a programming error in the caller.
++ * Their request can only work properly in all cases by relying on
++ * a follow-up callback, but they didn't set one up! This check
++ * doesn't do locking, but it shouldn't matter. The caller has to
++ * be synchronously sure the callback is set up to be operating the
++ * interface properly.
++ */
++ if (action >= UTRACE_REPORT && action < UTRACE_RESUME &&
++ unlikely(!(engine->flags & UTRACE_EVENT(QUIESCE)))) {
++ WARN(1, "utrace_control() with no QUIESCE callback in place!");
++ return -EINVAL;
++ }
++
++ utrace = get_utrace_lock(target, engine, true);
++ if (unlikely(IS_ERR(utrace)))
++ return PTR_ERR(utrace);
++
++ reset = task_is_traced(target);
++ ret = 0;
++
++ /*
++ * ->exit_state can change under us, this doesn't matter.
++ * We do not care about ->exit_state in fact, but we do
++ * care about ->reap and ->death. If either flag is set,
++ * we must also see ->exit_state != 0.
++ */
++ if (unlikely(target->exit_state)) {
++ ret = utrace_control_dead(target, utrace, action);
++ if (ret) {
++ spin_unlock(&utrace->lock);
++ return ret;
++ }
++ reset = true;
++ }
++
++ switch (action) {
++ case UTRACE_STOP:
++ mark_engine_wants_stop(target, engine);
++ if (!reset && !utrace_do_stop(target, utrace))
++ ret = -EINPROGRESS;
++ reset = false;
++ break;
++
++ case UTRACE_DETACH:
++ if (engine_wants_stop(engine))
++ target->utrace_flags &= ~ENGINE_STOP;
++ mark_engine_detached(engine);
++ reset = reset || utrace_do_stop(target, utrace);
++ if (!reset) {
++ /*
++ * As in utrace_set_events(), this barrier ensures
++ * that our engine->flags changes have hit before we
++ * examine utrace->reporting, pairing with the barrier
++ * in start_callback(). If @target has not yet hit
++ * finish_callback() to clear utrace->reporting, we
++ * might be in the middle of a callback to @engine.
++ */
++ smp_mb();
++ if (utrace->reporting == engine)
++ ret = -EINPROGRESS;
++ }
++ break;
++
++ case UTRACE_RESUME:
++ /*
++ * This and all other cases imply resuming if stopped.
++ * There might not be another report before it just
++ * resumes, so make sure single-step is not left set.
++ */
++ clear_engine_wants_stop(engine);
++ if (likely(reset))
++ user_disable_single_step(target);
++ break;
++
++ case UTRACE_BLOCKSTEP:
++ /*
++ * Resume from stopped, step one block.
++ * We fall through to treat it like UTRACE_SINGLESTEP.
++ */
++ if (unlikely(!arch_has_block_step())) {
++ WARN(1, "UTRACE_BLOCKSTEP when !arch_has_block_step()");
++ action = UTRACE_SINGLESTEP;
++ }
++
++ case UTRACE_SINGLESTEP:
++ /*
++ * Resume from stopped, step one instruction.
++ * We fall through to the UTRACE_REPORT case.
++ */
++ if (unlikely(!arch_has_single_step())) {
++ WARN(1,
++ "UTRACE_SINGLESTEP when !arch_has_single_step()");
++ reset = false;
++ ret = -EOPNOTSUPP;
++ break;
++ }
++
++ case UTRACE_REPORT:
++ /*
++ * Make the thread call tracehook_notify_resume() soon.
++ * But don't bother if it's already been interrupted.
++ * In that case, utrace_get_signal() will be reporting soon.
++ */
++ clear_engine_wants_stop(engine);
++ if (action < utrace->resume) {
++ utrace->resume = action;
++ set_notify_resume(target);
++ }
++ break;
++
++ case UTRACE_INTERRUPT:
++ /*
++ * Make the thread call tracehook_get_signal() soon.
++ */
++ clear_engine_wants_stop(engine);
++ if (utrace->resume == UTRACE_INTERRUPT)
++ break;
++ utrace->resume = UTRACE_INTERRUPT;
++
++ /*
++ * If it's not already stopped, interrupt it now. We need
++ * the siglock here in case it calls recalc_sigpending()
++ * and clears its own TIF_SIGPENDING. By taking the lock,
++ * we've serialized any later recalc_sigpending() after our
++ * setting of utrace->resume to force it on.
++ */
++ if (reset) {
++ /*
++ * This is really just to keep the invariant that
++ * TIF_SIGPENDING is set with UTRACE_INTERRUPT.
++ * When it's stopped, we know it's always going
++ * through utrace_get_signal() and will recalculate.
++ */
++ set_tsk_thread_flag(target, TIF_SIGPENDING);
++ } else {
++ struct sighand_struct *sighand;
++ unsigned long irqflags;
++ sighand = lock_task_sighand(target, &irqflags);
++ if (likely(sighand)) {
++ signal_wake_up(target, 0);
++ unlock_task_sighand(target, &irqflags);
++ }
++ }
++ break;
++
++ default:
++ BUG(); /* We checked it on entry. */
++ }
++
++ /*
++ * Let the thread resume running. If it's not stopped now,
++ * there is nothing more we need to do.
++ */
++ if (reset)
++ utrace_reset(target, utrace);
++ else
++ spin_unlock(&utrace->lock);
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(utrace_control);
++
++/**
++ * utrace_barrier - synchronize with simultaneous tracing callbacks
++ * @target: thread to affect
++ * @engine: engine to affect (can be detached)
++ *
++ * This blocks while @target might be in the midst of making a callback to
++ * @engine. It can be interrupted by signals and will return -%ERESTARTSYS.
++ * A return value of zero means no callback from @target to @engine was
++ * in progress. Any effect of its return value (such as %UTRACE_STOP) has
++ * already been applied to @engine.
++ *
++ * It's not necessary to keep the @target pointer alive for this call.
++ * It's only necessary to hold a ref on @engine. This will return
++ * safely even if @target has been reaped and has no task refs.
++ *
++ * A successful return from utrace_barrier() guarantees its ordering
++ * with respect to utrace_set_events() and utrace_control() calls. If
++ * @target was not properly stopped, event callbacks just disabled might
++ * still be in progress; utrace_barrier() waits until there is no chance
++ * an unwanted callback can be in progress.
++ */
++int utrace_barrier(struct task_struct *target, struct utrace_engine *engine)
++{
++ struct utrace *utrace;
++ int ret = -ERESTARTSYS;
++
++ if (unlikely(target == current))
++ return 0;
++
++ do {
++ utrace = get_utrace_lock(target, engine, false);
++ if (unlikely(IS_ERR(utrace))) {
++ ret = PTR_ERR(utrace);
++ if (ret != -ERESTARTSYS)
++ break;
++ } else {
++ /*
++ * All engine state changes are done while
++ * holding the lock, i.e. before we get here.
++ * Since we have the lock, we only need to
++ * worry about @target making a callback.
++ * When it has entered start_callback() but
++ * not yet gotten to finish_callback(), we
++ * will see utrace->reporting == @engine.
++ * When @target doesn't take the lock, it uses
++ * barriers to order setting utrace->reporting
++ * before it examines the engine state.
++ */
++ if (utrace->reporting != engine)
++ ret = 0;
++ spin_unlock(&utrace->lock);
++ if (!ret)
++ break;
++ }
++ schedule_timeout_interruptible(1);
++ } while (!signal_pending(current));
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(utrace_barrier);
++
++/*
++ * This is local state used for reporting loops, perhaps optimized away.
++ */
++struct utrace_report {
++ u32 result;
++ enum utrace_resume_action action;
++ enum utrace_resume_action resume_action;
++ bool detaches;
++ bool spurious;
++};
++
++#define INIT_REPORT(var) \
++ struct utrace_report var = { \
++ .action = UTRACE_RESUME, \
++ .resume_action = UTRACE_RESUME, \
++ .spurious = true \
++ }
++
++/*
++ * We are now making the report, so clear the flag saying we need one.
++ * When there is a new attach, ->pending_attach is set just so we will
++ * know to do splice_attaching() here before the callback loop.
++ */
++static enum utrace_resume_action start_report(struct utrace *utrace)
++{
++ enum utrace_resume_action resume = utrace->resume;
++ if (utrace->pending_attach ||
++ (resume > UTRACE_INTERRUPT && resume < UTRACE_RESUME)) {
++ spin_lock(&utrace->lock);
++ splice_attaching(utrace);
++ resume = utrace->resume;
++ if (resume > UTRACE_INTERRUPT)
++ utrace->resume = UTRACE_RESUME;
++ spin_unlock(&utrace->lock);
++ }
++ return resume;
++}
++
++static inline void finish_report_reset(struct task_struct *task,
++ struct utrace *utrace,
++ struct utrace_report *report)
++{
++ if (unlikely(report->spurious || report->detaches)) {
++ spin_lock(&utrace->lock);
++ if (utrace_reset(task, utrace))
++ report->action = UTRACE_RESUME;
++ }
++}
++
++/*
++ * Complete a normal reporting pass, pairing with a start_report() call.
++ * This handles any UTRACE_DETACH or UTRACE_REPORT or UTRACE_INTERRUPT
++ * returns from engine callbacks. If @will_not_stop is true and any
++ * engine's last callback used UTRACE_STOP, we do UTRACE_REPORT here to
++ * ensure we stop before user mode. If there were no callbacks made, it
++ * will recompute @task->utrace_flags to avoid another false-positive.
++ */
++static void finish_report(struct task_struct *task, struct utrace *utrace,
++ struct utrace_report *report, bool will_not_stop)
++{
++ enum utrace_resume_action resume = report->action;
++
++ if (resume == UTRACE_STOP)
++ resume = will_not_stop ? UTRACE_REPORT : UTRACE_RESUME;
++
++ if (resume < utrace->resume) {
++ spin_lock(&utrace->lock);
++ utrace->resume = resume;
++ if (resume == UTRACE_INTERRUPT)
++ set_tsk_thread_flag(task, TIF_SIGPENDING);
++ else
++ set_tsk_thread_flag(task, TIF_NOTIFY_RESUME);
++ spin_unlock(&utrace->lock);
++ }
++
++ finish_report_reset(task, utrace, report);
++}
++
++static void finish_callback_report(struct task_struct *task,
++ struct utrace *utrace,
++ struct utrace_report *report,
++ struct utrace_engine *engine,
++ enum utrace_resume_action action)
++{
++ if (action == UTRACE_DETACH) {
++ /*
++ * By holding the lock here, we make sure that
++ * utrace_barrier() (really get_utrace_lock()) sees the
++ * effect of this detach. Otherwise utrace_barrier() could
++ * return 0 after this callback had returned UTRACE_DETACH.
++ * This way, a 0 return is an unambiguous indicator that any
++ * callback returning UTRACE_DETACH has indeed caused detach.
++ */
++ spin_lock(&utrace->lock);
++ engine->ops = &utrace_detached_ops;
++ spin_unlock(&utrace->lock);
++ }
++
++ /*
++ * If utrace_control() was used, treat that like UTRACE_DETACH here.
++ */
++ if (engine->ops == &utrace_detached_ops) {
++ report->detaches = true;
++ return;
++ }
++
++ if (action < report->action)
++ report->action = action;
++
++ if (action != UTRACE_STOP) {
++ if (action < report->resume_action)
++ report->resume_action = action;
++
++ if (engine_wants_stop(engine)) {
++ spin_lock(&utrace->lock);
++ clear_engine_wants_stop(engine);
++ spin_unlock(&utrace->lock);
++ }
++
++ return;
++ }
++
++ if (!engine_wants_stop(engine)) {
++ spin_lock(&utrace->lock);
++ /*
++ * If utrace_control() came in and detached us
++ * before we got the lock, we must not stop now.
++ */
++ if (unlikely(engine->ops == &utrace_detached_ops))
++ report->detaches = true;
++ else
++ mark_engine_wants_stop(task, engine);
++ spin_unlock(&utrace->lock);
++ }
++}
++
++/*
++ * Apply the return value of one engine callback to @report.
++ * Returns true if @engine detached and should not get any more callbacks.
++ */
++static bool finish_callback(struct task_struct *task, struct utrace *utrace,
++ struct utrace_report *report,
++ struct utrace_engine *engine,
++ u32 ret)
++{
++ report->result = ret & ~UTRACE_RESUME_MASK;
++ finish_callback_report(task, utrace, report, engine,
++ utrace_resume_action(ret));
++
++ /*
++ * Now that we have applied the effect of the return value,
++ * clear this so that utrace_barrier() can stop waiting.
++ * A subsequent utrace_control() can stop or resume @engine
++ * and know this was ordered after its callback's action.
++ *
++ * We don't need any barriers here because utrace_barrier()
++ * takes utrace->lock. If we touched engine->flags above,
++ * the lock guaranteed this change was before utrace_barrier()
++ * examined utrace->reporting.
++ */
++ utrace->reporting = NULL;
++
++ /*
++ * We've just done an engine callback. These are allowed to sleep,
++ * though all well-behaved ones restrict that to blocking kalloc()
++ * or quickly-acquired mutex_lock() and the like. This is a good
++ * place to make sure tracing engines don't introduce too much
++ * latency under voluntary preemption.
++ */
++ might_sleep();
++
++ return engine->ops == &utrace_detached_ops;
++}
++
++/*
++ * Start the callbacks for @engine to consider @event (a bit mask).
++ * This makes the report_quiesce() callback first. If @engine wants
++ * a specific callback for @event, we return the ops vector to use.
++ * If not, we return NULL. The return value from the ops->callback
++ * function called should be passed to finish_callback().
++ */
++static const struct utrace_engine_ops *start_callback(
++ struct utrace *utrace, struct utrace_report *report,
++ struct utrace_engine *engine, struct task_struct *task,
++ unsigned long event)
++{
++ const struct utrace_engine_ops *ops;
++ unsigned long want;
++
++ /*
++ * This barrier ensures that we've set utrace->reporting before
++ * we examine engine->flags or engine->ops. utrace_barrier()
++ * relies on this ordering to indicate that the effect of any
++ * utrace_control() and utrace_set_events() calls is in place
++ * by the time utrace->reporting can be seen to be NULL.
++ */
++ utrace->reporting = engine;
++ smp_mb();
++
++ /*
++ * This pairs with the barrier in mark_engine_detached().
++ * It makes sure that we never see the old ops vector with
++ * the new flags, in case the original vector had no report_quiesce.
++ */
++ want = engine->flags;
++ smp_rmb();
++ ops = engine->ops;
++
++ if (want & UTRACE_EVENT(QUIESCE)) {
++ if (finish_callback(task, utrace, report, engine,
++ (*ops->report_quiesce)(report->action,
++ engine, event)))
++ return NULL;
++
++ /*
++ * finish_callback() reset utrace->reporting after the
++ * quiesce callback. Now we set it again (as above)
++ * before re-examining engine->flags, which could have
++ * been changed synchronously by ->report_quiesce or
++ * asynchronously by utrace_control() or utrace_set_events().
++ */
++ utrace->reporting = engine;
++ smp_mb();
++ want = engine->flags;
++ }
++
++ if (want & ENGINE_STOP)
++ report->action = UTRACE_STOP;
++
++ if (want & event) {
++ report->spurious = false;
++ return ops;
++ }
++
++ utrace->reporting = NULL;
++ return NULL;
++}
++
++/*
++ * Do a normal reporting pass for engines interested in @event.
++ * @callback is the name of the member in the ops vector, and remaining
++ * args are the extras it takes after the standard three args.
++ */
++#define REPORT_CALLBACKS(rev, task, utrace, report, event, callback, ...) \
++ do { \
++ struct utrace_engine *engine; \
++ const struct utrace_engine_ops *ops; \
++ list_for_each_entry##rev(engine, &utrace->attached, entry) { \
++ ops = start_callback(utrace, report, engine, task, \
++ event); \
++ if (!ops) \
++ continue; \
++ finish_callback(task, utrace, report, engine, \
++ (*ops->callback)(__VA_ARGS__)); \
++ } \
++ } while (0)
++#define REPORT(task, utrace, report, event, callback, ...) \
++ do { \
++ start_report(utrace); \
++ REPORT_CALLBACKS(, task, utrace, report, event, callback, \
++ (report)->action, engine, ## __VA_ARGS__); \
++ finish_report(task, utrace, report, true); \
++ } while (0)
++
++/*
++ * Called iff UTRACE_EVENT(EXEC) flag is set.
++ */
++void utrace_report_exec(struct linux_binfmt *fmt, struct linux_binprm *bprm,
++ struct pt_regs *regs)
++{
++ struct task_struct *task = current;
++ struct utrace *utrace = task_utrace_struct(task);
++ INIT_REPORT(report);
++
++ REPORT(task, utrace, &report, UTRACE_EVENT(EXEC),
++ report_exec, fmt, bprm, regs);
++}
++
++static u32 do_report_syscall_entry(struct pt_regs *regs,
++ struct task_struct *task,
++ struct utrace *utrace,
++ struct utrace_report *report,
++ u32 resume_report)
++{
++ start_report(utrace);
++ REPORT_CALLBACKS(_reverse, task, utrace, report,
++ UTRACE_EVENT(SYSCALL_ENTRY), report_syscall_entry,
++ resume_report | report->result | report->action,
++ engine, regs);
++ finish_report(task, utrace, report, false);
++
++ if (report->action != UTRACE_STOP)
++ return 0;
++
++ utrace_stop(task, utrace, report->resume_action);
++
++ if (fatal_signal_pending(task)) {
++ /*
++ * We are continuing despite UTRACE_STOP because of a
++ * SIGKILL. Don't let the system call actually proceed.
++ */
++ report->result = UTRACE_SYSCALL_ABORT;
++ } else if (utrace->resume <= UTRACE_REPORT) {
++ /*
++ * If we've been asked for another report after our stop,
++ * go back to report (and maybe stop) again before we run
++ * the system call. The second (and later) reports are
++ * marked with the UTRACE_SYSCALL_RESUMED flag so that
++ * engines know this is a second report at the same
++ * entry. This gives them the chance to examine the
++ * registers anew after they might have been changed
++ * while we were stopped.
++ */
++ report->detaches = false;
++ report->spurious = true;
++ report->action = report->resume_action = UTRACE_RESUME;
++ return UTRACE_SYSCALL_RESUMED;
++ }
++
++ return 0;
++}
++
++/*
++ * Called iff UTRACE_EVENT(SYSCALL_ENTRY) flag is set.
++ * Return true to prevent the system call.
++ */
++bool utrace_report_syscall_entry(struct pt_regs *regs)
++{
++ struct task_struct *task = current;
++ struct utrace *utrace = task_utrace_struct(task);
++ INIT_REPORT(report);
++ u32 resume_report = 0;
++
++ do {
++ resume_report = do_report_syscall_entry(regs, task, utrace,
++ &report, resume_report);
++ } while (resume_report);
++
++ return utrace_syscall_action(report.result) == UTRACE_SYSCALL_ABORT;
++}
++
++/*
++ * Called iff UTRACE_EVENT(SYSCALL_EXIT) flag is set.
++ */
++void utrace_report_syscall_exit(struct pt_regs *regs)
++{
++ struct task_struct *task = current;
++ struct utrace *utrace = task_utrace_struct(task);
++ INIT_REPORT(report);
++
++ REPORT(task, utrace, &report, UTRACE_EVENT(SYSCALL_EXIT),
++ report_syscall_exit, regs);
++}
++
++/*
++ * Called iff UTRACE_EVENT(CLONE) flag is set.
++ * This notification call blocks the wake_up_new_task call on the child.
++ * So we must not quiesce here. tracehook_report_clone_complete will do
++ * a quiescence check momentarily.
++ */
++void utrace_report_clone(unsigned long clone_flags, struct task_struct *child)
++{
++ struct task_struct *task = current;
++ struct utrace *utrace = task_utrace_struct(task);
++ INIT_REPORT(report);
++
++ /*
++ * We don't use the REPORT() macro here, because we need
++ * to clear utrace->cloning before finish_report().
++ * After finish_report(), utrace can be a stale pointer
++ * in cases when report.action is still UTRACE_RESUME.
++ */
++ start_report(utrace);
++ utrace->cloning = child;
++
++ REPORT_CALLBACKS(, task, utrace, &report,
++ UTRACE_EVENT(CLONE), report_clone,
++ report.action, engine, clone_flags, child);
++
++ utrace->cloning = NULL;
++ finish_report(task, utrace, &report, !(clone_flags & CLONE_VFORK));
++
++ /*
++ * For a vfork, we will go into an uninterruptible block waiting
++ * for the child. We need UTRACE_STOP to happen before this, not
++ * after. For CLONE_VFORK, utrace_finish_vfork() will be called.
++ */
++ if (report.action == UTRACE_STOP && (clone_flags & CLONE_VFORK)) {
++ spin_lock(&utrace->lock);
++ utrace->vfork_stop = 1;
++ spin_unlock(&utrace->lock);
++ }
++}
++
++/*
++ * We're called after utrace_report_clone() for a CLONE_VFORK.
++ * If UTRACE_STOP was left from the clone report, we stop here.
++ * After this, we'll enter the uninterruptible wait_for_completion()
++ * waiting for the child.
++ */
++void utrace_finish_vfork(struct task_struct *task)
++{
++ struct utrace *utrace = task_utrace_struct(task);
++
++ if (utrace->vfork_stop) {
++ spin_lock(&utrace->lock);
++ utrace->vfork_stop = 0;
++ spin_unlock(&utrace->lock);
++ utrace_stop(task, utrace, UTRACE_RESUME); /* XXX */
++ }
++}
++
++/*
++ * Called iff UTRACE_EVENT(JCTL) flag is set.
++ *
++ * Called with siglock held.
++ */
++void utrace_report_jctl(int notify, int what)
++{
++ struct task_struct *task = current;
++ struct utrace *utrace = task_utrace_struct(task);
++ INIT_REPORT(report);
++
++ spin_unlock_irq(&task->sighand->siglock);
++
++ REPORT(task, utrace, &report, UTRACE_EVENT(JCTL),
++ report_jctl, what, notify);
++
++ spin_lock_irq(&task->sighand->siglock);
++}
++
++/*
++ * Called iff UTRACE_EVENT(EXIT) flag is set.
++ */
++void utrace_report_exit(long *exit_code)
++{
++ struct task_struct *task = current;
++ struct utrace *utrace = task_utrace_struct(task);
++ INIT_REPORT(report);
++ long orig_code = *exit_code;
++
++ REPORT(task, utrace, &report, UTRACE_EVENT(EXIT),
++ report_exit, orig_code, exit_code);
++
++ if (report.action == UTRACE_STOP)
++ utrace_stop(task, utrace, report.resume_action);
++}
++
++/*
++ * Called iff UTRACE_EVENT(DEATH) or UTRACE_EVENT(QUIESCE) flag is set.
++ *
++ * It is always possible that we are racing with utrace_release_task here.
++ * For this reason, utrace_release_task checks for the event bits that get
++ * us here, and delays its cleanup for us to do.
++ */
++void utrace_report_death(struct task_struct *task, struct utrace *utrace,
++ bool group_dead, int signal)
++{
++ INIT_REPORT(report);
++
++ BUG_ON(!task->exit_state);
++
++ /*
++ * We are presently considered "quiescent"--which is accurate
++ * inasmuch as we won't run any more user instructions ever again.
++ * But for utrace_control and utrace_set_events to be robust, they
++ * must be sure whether or not we will run any more callbacks. If
++ * a call comes in before we do, taking the lock here synchronizes
++ * us so we don't run any callbacks just disabled. Calls that come
++ * in while we're running the callbacks will see the exit.death
++ * flag and know that we are not yet fully quiescent for purposes
++ * of detach bookkeeping.
++ */
++ spin_lock(&utrace->lock);
++ BUG_ON(utrace->death);
++ utrace->death = 1;
++ utrace->resume = UTRACE_RESUME;
++ splice_attaching(utrace);
++ spin_unlock(&utrace->lock);
++
++ REPORT_CALLBACKS(, task, utrace, &report, UTRACE_EVENT(DEATH),
++ report_death, engine, group_dead, signal);
++
++ utrace_maybe_reap(task, utrace, false);
++}
++
++/*
++ * Finish the last reporting pass before returning to user mode.
++ */
++static void finish_resume_report(struct task_struct *task,
++ struct utrace *utrace,
++ struct utrace_report *report)
++{
++ finish_report_reset(task, utrace, report);
++
++ switch (report->action) {
++ case UTRACE_STOP:
++ utrace_stop(task, utrace, report->resume_action);
++ break;
++
++ case UTRACE_INTERRUPT:
++ if (!signal_pending(task))
++ set_tsk_thread_flag(task, TIF_SIGPENDING);
++ break;
++
++ case UTRACE_BLOCKSTEP:
++ if (likely(arch_has_block_step())) {
++ user_enable_block_step(task);
++ break;
++ }
++
++ /*
++ * This means some callback is to blame for failing
++ * to check arch_has_block_step() itself. Warn and
++ * then fall through to treat it as SINGLESTEP.
++ */
++ WARN(1, "UTRACE_BLOCKSTEP when !arch_has_block_step()");
++
++ case UTRACE_SINGLESTEP:
++ if (likely(arch_has_single_step())) {
++ user_enable_single_step(task);
++ } else {
++ /*
++ * This means some callback is to blame for failing
++ * to check arch_has_single_step() itself. Spew
++ * about it so the loser will fix his module.
++ */
++ WARN(1,
++ "UTRACE_SINGLESTEP when !arch_has_single_step()");
++ }
++ break;
++
++ case UTRACE_REPORT:
++ case UTRACE_RESUME:
++ default:
++ user_disable_single_step(task);
++ break;
++ }
++}
++
++/*
++ * This is called when TIF_NOTIFY_RESUME had been set (and is now clear).
++ * We are close to user mode, and this is the place to report or stop.
++ * When we return, we're going to user mode or into the signals code.
++ */
++void utrace_resume(struct task_struct *task, struct pt_regs *regs)
++{
++ struct utrace *utrace = task_utrace_struct(task);
++ INIT_REPORT(report);
++ struct utrace_engine *engine;
++
++ /*
++ * Some machines get here with interrupts disabled. The same arch
++ * code path leads to calling into get_signal_to_deliver(), which
++ * implicitly reenables them by virtue of spin_unlock_irq.
++ */
++ local_irq_enable();
++
++ /*
++ * If this flag is still set it's because there was a signal
++ * handler setup done but no report_signal following it. Clear
++ * the flag before we get to user so it doesn't confuse us later.
++ */
++ if (unlikely(utrace->signal_handler)) {
++ spin_lock(&utrace->lock);
++ utrace->signal_handler = 0;
++ spin_unlock(&utrace->lock);
++ }
++
++ /*
++ * Update our bookkeeping even if there are no callbacks made here.
++ */
++ report.action = start_report(utrace);
++
++ switch (report.action) {
++ case UTRACE_RESUME:
++ /*
++ * Anything we might have done was already handled by
++ * utrace_get_signal(), or this is an entirely spurious
++ * call. (The arch might use TIF_NOTIFY_RESUME for other
++ * purposes as well as calling us.)
++ */
++ return;
++ case UTRACE_REPORT:
++ if (unlikely(!(task->utrace_flags & UTRACE_EVENT(QUIESCE))))
++ break;
++ /*
++ * Do a simple reporting pass, with no specific
++ * callback after report_quiesce.
++ */
++ report.action = UTRACE_RESUME;
++ list_for_each_entry(engine, &utrace->attached, entry)
++ start_callback(utrace, &report, engine, task, 0);
++ break;
++ default:
++ /*
++ * Even if this report was truly spurious, there is no need
++ * for utrace_reset() now. TIF_NOTIFY_RESUME was already
++ * cleared--it doesn't stay spuriously set.
++ */
++ report.spurious = false;
++ break;
++ }
++
++ /*
++ * Finish the report and either stop or get ready to resume.
++ * If utrace->resume was not UTRACE_REPORT, this applies its
++ * effect now (i.e. step or interrupt).
++ */
++ finish_resume_report(task, utrace, &report);
++}
++
++/*
++ * Return true if current has forced signal_pending().
++ *
++ * This is called only when current->utrace_flags is nonzero, so we know
++ * that current->utrace must be set. It's not inlined in tracehook.h
++ * just so that struct utrace can stay opaque outside this file.
++ */
++bool utrace_interrupt_pending(void)
++{
++ return task_utrace_struct(current)->resume == UTRACE_INTERRUPT;
++}
++
++/*
++ * Take the siglock and push @info back on our queue.
++ * Returns with @task->sighand->siglock held.
++ */
++static void push_back_signal(struct task_struct *task, siginfo_t *info)
++ __acquires(task->sighand->siglock)
++{
++ struct sigqueue *q;
++
++ if (unlikely(!info->si_signo)) { /* Oh, a wise guy! */
++ spin_lock_irq(&task->sighand->siglock);
++ return;
++ }
++
++ q = sigqueue_alloc();
++ if (likely(q)) {
++ q->flags = 0;
++ copy_siginfo(&q->info, info);
++ }
++
++ spin_lock_irq(&task->sighand->siglock);
++
++ sigaddset(&task->pending.signal, info->si_signo);
++ if (likely(q))
++ list_add(&q->list, &task->pending.list);
++
++ set_tsk_thread_flag(task, TIF_SIGPENDING);
++}
++
++/*
++ * This is the hook from the signals code, called with the siglock held.
++ * Here is the ideal place to stop. We also dequeue and intercept signals.
++ */
++int utrace_get_signal(struct task_struct *task, struct pt_regs *regs,
++ siginfo_t *info, struct k_sigaction *return_ka)
++ __releases(task->sighand->siglock)
++ __acquires(task->sighand->siglock)
++{
++ struct utrace *utrace;
++ struct k_sigaction *ka;
++ INIT_REPORT(report);
++ struct utrace_engine *engine;
++ const struct utrace_engine_ops *ops;
++ unsigned long event, want;
++ u32 ret;
++ int signr;
++
++ utrace = task_utrace_struct(task);
++ if (utrace->resume < UTRACE_RESUME ||
++ utrace->pending_attach || utrace->signal_handler) {
++ enum utrace_resume_action resume;
++
++ /*
++ * We've been asked for an explicit report before we
++ * even check for pending signals.
++ */
++
++ spin_unlock_irq(&task->sighand->siglock);
++
++ spin_lock(&utrace->lock);
++
++ splice_attaching(utrace);
++
++ report.result = utrace->signal_handler ?
++ UTRACE_SIGNAL_HANDLER : UTRACE_SIGNAL_REPORT;
++ utrace->signal_handler = 0;
++
++ resume = utrace->resume;
++ utrace->resume = UTRACE_RESUME;
++
++ spin_unlock(&utrace->lock);
++
++ /*
++ * Make sure signal_pending() only returns true
++ * if there are real signals pending.
++ */
++ if (signal_pending(task)) {
++ spin_lock_irq(&task->sighand->siglock);
++ recalc_sigpending();
++ spin_unlock_irq(&task->sighand->siglock);
++ }
++
++ if (resume > UTRACE_REPORT) {
++ /*
++ * We only got here to process utrace->resume.
++ * Despite no callbacks, this report is not spurious.
++ */
++ report.action = resume;
++ report.spurious = false;
++ finish_resume_report(task, utrace, &report);
++ return -1;
++ } else if (!(task->utrace_flags & UTRACE_EVENT(QUIESCE))) {
++ /*
++ * We only got here to clear utrace->signal_handler.
++ */
++ return -1;
++ }
++
++ /*
++ * Do a reporting pass for no signal, just for EVENT(QUIESCE).
++ * The engine callbacks can fill in *info and *return_ka.
++ * We'll pass NULL for the @orig_ka argument to indicate
++ * that there was no original signal.
++ */
++ event = 0;
++ ka = NULL;
++ memset(return_ka, 0, sizeof *return_ka);
++ } else if (!(task->utrace_flags & UTRACE_EVENT_SIGNAL_ALL) ||
++ unlikely(task->signal->group_stop_count)) {
++ /*
++ * If no engine is interested in intercepting signals or
++ * we must stop, let the caller just dequeue them normally
++ * or participate in group-stop.
++ */
++ return 0;
++ } else {
++ /*
++ * Steal the next signal so we can let tracing engines
++ * examine it. From the signal number and sigaction,
++ * determine what normal delivery would do. If no
++ * engine perturbs it, we'll do that by returning the
++ * signal number after setting *return_ka.
++ */
++ signr = dequeue_signal(task, &task->blocked, info);
++ if (signr == 0)
++ return signr;
++ BUG_ON(signr != info->si_signo);
++
++ ka = &task->sighand->action[signr - 1];
++ *return_ka = *ka;
++
++ /*
++ * We are never allowed to interfere with SIGKILL.
++ * Just punt after filling in *return_ka for our caller.
++ */
++ if (signr == SIGKILL)
++ return signr;
++
++ if (ka->sa.sa_handler == SIG_IGN) {
++ event = UTRACE_EVENT(SIGNAL_IGN);
++ report.result = UTRACE_SIGNAL_IGN;
++ } else if (ka->sa.sa_handler != SIG_DFL) {
++ event = UTRACE_EVENT(SIGNAL);
++ report.result = UTRACE_SIGNAL_DELIVER;
++ } else if (sig_kernel_coredump(signr)) {
++ event = UTRACE_EVENT(SIGNAL_CORE);
++ report.result = UTRACE_SIGNAL_CORE;
++ } else if (sig_kernel_ignore(signr)) {
++ event = UTRACE_EVENT(SIGNAL_IGN);
++ report.result = UTRACE_SIGNAL_IGN;
++ } else if (signr == SIGSTOP) {
++ event = UTRACE_EVENT(SIGNAL_STOP);
++ report.result = UTRACE_SIGNAL_STOP;
++ } else if (sig_kernel_stop(signr)) {
++ event = UTRACE_EVENT(SIGNAL_STOP);
++ report.result = UTRACE_SIGNAL_TSTP;
++ } else {
++ event = UTRACE_EVENT(SIGNAL_TERM);
++ report.result = UTRACE_SIGNAL_TERM;
++ }
++
++ /*
++ * Now that we know what event type this signal is, we
++ * can short-circuit if no engines care about those.
++ */
++ if ((task->utrace_flags & (event | UTRACE_EVENT(QUIESCE))) == 0)
++ return signr;
++
++ /*
++ * We have some interested engines, so tell them about
++ * the signal and let them change its disposition.
++ */
++ spin_unlock_irq(&task->sighand->siglock);
++ }
++
++ /*
++ * This reporting pass chooses what signal disposition we'll act on.
++ */
++ list_for_each_entry(engine, &utrace->attached, entry) {
++ /*
++ * See start_callback() comment about this barrier.
++ */
++ utrace->reporting = engine;
++ smp_mb();
++
++ /*
++ * This pairs with the barrier in mark_engine_detached(),
++ * see start_callback() comments.
++ */
++ want = engine->flags;
++ smp_rmb();
++ ops = engine->ops;
++
++ if ((want & (event | UTRACE_EVENT(QUIESCE))) == 0) {
++ utrace->reporting = NULL;
++ continue;
++ }
++
++ if (ops->report_signal)
++ ret = (*ops->report_signal)(
++ report.result | report.action, engine,
++ regs, info, ka, return_ka);
++ else
++ ret = (report.result | (*ops->report_quiesce)(
++ report.action, engine, event));
++
++ /*
++ * Avoid a tight loop reporting again and again if some
++ * engine is too stupid.
++ */
++ switch (utrace_resume_action(ret)) {
++ default:
++ break;
++ case UTRACE_INTERRUPT:
++ case UTRACE_REPORT:
++ ret = (ret & ~UTRACE_RESUME_MASK) | UTRACE_RESUME;
++ break;
++ }
++
++ finish_callback(task, utrace, &report, engine, ret);
++ }
++
++ /*
++ * We express the chosen action to the signals code in terms
++ * of a representative signal whose default action does it.
++ * Our caller uses our return value (signr) to decide what to
++ * do, but uses info->si_signo as the signal number to report.
++ */
++ switch (utrace_signal_action(report.result)) {
++ case UTRACE_SIGNAL_TERM:
++ signr = SIGTERM;
++ break;
++
++ case UTRACE_SIGNAL_CORE:
++ signr = SIGQUIT;
++ break;
++
++ case UTRACE_SIGNAL_STOP:
++ signr = SIGSTOP;
++ break;
++
++ case UTRACE_SIGNAL_TSTP:
++ signr = SIGTSTP;
++ break;
++
++ case UTRACE_SIGNAL_DELIVER:
++ signr = info->si_signo;
++
++ if (return_ka->sa.sa_handler == SIG_DFL) {
++ /*
++ * We'll do signr's normal default action.
++ * For ignore, we'll fall through below.
++ * For stop/death, break locks and returns it.
++ */
++ if (likely(signr) && !sig_kernel_ignore(signr))
++ break;
++ } else if (return_ka->sa.sa_handler != SIG_IGN &&
++ likely(signr)) {
++ /*
++ * Complete the bookkeeping after the report.
++ * The handler will run. If an engine wanted to
++ * stop or step, then make sure we do another
++ * report after signal handler setup.
++ */
++ if (report.action != UTRACE_RESUME)
++ report.action = UTRACE_INTERRUPT;
++ finish_report(task, utrace, &report, true);
++
++ if (unlikely(report.result & UTRACE_SIGNAL_HOLD))
++ push_back_signal(task, info);
++ else
++ spin_lock_irq(&task->sighand->siglock);
++
++ /*
++ * We do the SA_ONESHOT work here since the
++ * normal path will only touch *return_ka now.
++ */
++ if (unlikely(return_ka->sa.sa_flags & SA_ONESHOT)) {
++ return_ka->sa.sa_flags &= ~SA_ONESHOT;
++ if (likely(valid_signal(signr))) {
++ ka = &task->sighand->action[signr - 1];
++ ka->sa.sa_handler = SIG_DFL;
++ }
++ }
++
++ return signr;
++ }
++
++ /* Fall through for an ignored signal. */
++
++ case UTRACE_SIGNAL_IGN:
++ case UTRACE_SIGNAL_REPORT:
++ default:
++ /*
++ * If the signal is being ignored, then we are on the way
++ * directly back to user mode. We can stop here, or step,
++ * as in utrace_resume(), above. After we've dealt with that,
++ * our caller will relock and come back through here.
++ */
++ finish_resume_report(task, utrace, &report);
++
++ if (unlikely(fatal_signal_pending(task))) {
++ /*
++ * The only reason we woke up now was because of a
++ * SIGKILL. Don't do normal dequeuing in case it
++ * might get a signal other than SIGKILL. That would
++ * perturb the death state so it might differ from
++ * what the debugger would have allowed to happen.
++ * Instead, pluck out just the SIGKILL to be sure
++ * we'll die immediately with nothing else different
++ * from the quiescent state the debugger wanted us in.
++ */
++ sigset_t sigkill_only;
++ siginitsetinv(&sigkill_only, sigmask(SIGKILL));
++ spin_lock_irq(&task->sighand->siglock);
++ signr = dequeue_signal(task, &sigkill_only, info);
++ BUG_ON(signr != SIGKILL);
++ *return_ka = task->sighand->action[SIGKILL - 1];
++ return signr;
++ }
++
++ if (unlikely(report.result & UTRACE_SIGNAL_HOLD)) {
++ push_back_signal(task, info);
++ spin_unlock_irq(&task->sighand->siglock);
++ }
++
++ return -1;
++ }
++
++ /*
++ * Complete the bookkeeping after the report.
++ * This sets utrace->resume if UTRACE_STOP was used.
++ */
++ finish_report(task, utrace, &report, true);
++
++ return_ka->sa.sa_handler = SIG_DFL;
++
++ /*
++ * If this signal is fatal, si_signo gets through as exit_code.
++ * We can't allow a completely bogus value there or else core
++ * kernel code can freak out. (If an engine wants to control
++ * the exit_code value exactly, it can do so in report_exit.)
++ * We'll produce a big complaint in dmesg, but won't crash.
++ * That's nicer for debugging your utrace engine.
++ */
++ if (unlikely(info->si_signo & 0x80)) {
++ WARN(1, "utrace engine left bogus si_signo value!");
++ info->si_signo = SIGTRAP;
++ }
++
++ if (unlikely(report.result & UTRACE_SIGNAL_HOLD))
++ push_back_signal(task, info);
++ else
++ spin_lock_irq(&task->sighand->siglock);
++
++ if (sig_kernel_stop(signr))
++ task->signal->flags |= SIGNAL_STOP_DEQUEUED;
++
++ return signr;
++}
++
++/*
++ * This gets called after a signal handler has been set up.
++ * We set a flag so the next report knows it happened.
++ * If we're already stepping, make sure we do a report_signal.
++ * If not, make sure we get into utrace_resume() where we can
++ * clear the signal_handler flag before resuming.
++ */
++void utrace_signal_handler(struct task_struct *task, int stepping)
++{
++ struct utrace *utrace = task_utrace_struct(task);
++
++ spin_lock(&utrace->lock);
++
++ utrace->signal_handler = 1;
++ if (utrace->resume > UTRACE_INTERRUPT) {
++ if (stepping) {
++ utrace->resume = UTRACE_INTERRUPT;
++ set_tsk_thread_flag(task, TIF_SIGPENDING);
++ } else if (utrace->resume == UTRACE_RESUME) {
++ set_tsk_thread_flag(task, TIF_NOTIFY_RESUME);
++ }
++ }
++
++ spin_unlock(&utrace->lock);
++}
++
++/**
++ * utrace_prepare_examine - prepare to examine thread state
++ * @target: thread of interest, a &struct task_struct pointer
++ * @engine: engine pointer returned by utrace_attach_task()
++ * @exam: temporary state, a &struct utrace_examiner pointer
++ *
++ * This call prepares to safely examine the thread @target using
++ * &struct user_regset calls, or direct access to thread-synchronous fields.
++ *
++ * When @target is current, this call is superfluous. When @target is
++ * another thread, it must be held stopped via %UTRACE_STOP by @engine.
++ *
++ * This call may block the caller until @target stays stopped, so it must
++ * be called only after the caller is sure @target is about to unschedule.
++ * This means a zero return from a utrace_control() call on @engine giving
++ * %UTRACE_STOP, or a report_quiesce() or report_signal() callback to
++ * @engine that used %UTRACE_STOP in its return value.
++ *
++ * Returns -%ESRCH if @target is dead or -%EINVAL if %UTRACE_STOP was
++ * not used. If @target has started running again despite %UTRACE_STOP
++ * (for %SIGKILL or a spurious wakeup), this call returns -%EAGAIN.
++ *
++ * When this call returns zero, it's safe to use &struct user_regset
++ * calls and task_user_regset_view() on @target and to examine some of
++ * its fields directly. When the examination is complete, a
++ * utrace_finish_examine() call must follow to check whether it was
++ * completed safely.
++ */
++int utrace_prepare_examine(struct task_struct *target,
++ struct utrace_engine *engine,
++ struct utrace_examiner *exam)
++{
++ int ret = 0;
++
++ if (unlikely(target == current))
++ return 0;
++
++ rcu_read_lock();
++ if (unlikely(!engine_wants_stop(engine)))
++ ret = -EINVAL;
++ else if (unlikely(target->exit_state))
++ ret = -ESRCH;
++ else {
++ exam->state = target->state;
++ if (unlikely(exam->state == TASK_RUNNING))
++ ret = -EAGAIN;
++ else
++ get_task_struct(target);
++ }
++ rcu_read_unlock();
++
++ if (likely(!ret)) {
++ exam->ncsw = wait_task_inactive(target, exam->state);
++ put_task_struct(target);
++ if (unlikely(!exam->ncsw))
++ ret = -EAGAIN;
++ }
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(utrace_prepare_examine);
++
++/**
++ * utrace_finish_examine - complete an examination of thread state
++ * @target: thread of interest, a &struct task_struct pointer
++ * @engine: engine pointer returned by utrace_attach_task()
++ * @exam: pointer passed to utrace_prepare_examine() call
++ *
++ * This call completes an examination on the thread @target begun by a
++ * paired utrace_prepare_examine() call with the same arguments that
++ * returned success (zero).
++ *
++ * When @target is current, this call is superfluous. When @target is
++ * another thread, this returns zero if @target has remained unscheduled
++ * since the paired utrace_prepare_examine() call returned zero.
++ *
++ * When this returns an error, any examination done since the paired
++ * utrace_prepare_examine() call is unreliable and the data extracted
++ * should be discarded. The error is -%EINVAL if @engine is not
++ * keeping @target stopped, or -%EAGAIN if @target woke up unexpectedly.
++ */
++int utrace_finish_examine(struct task_struct *target,
++ struct utrace_engine *engine,
++ struct utrace_examiner *exam)
++{
++ int ret = 0;
++
++ if (unlikely(target == current))
++ return 0;
++
++ rcu_read_lock();
++ if (unlikely(!engine_wants_stop(engine)))
++ ret = -EINVAL;
++ else if (unlikely(target->state != exam->state))
++ ret = -EAGAIN;
++ else
++ get_task_struct(target);
++ rcu_read_unlock();
++
++ if (likely(!ret)) {
++ unsigned long ncsw = wait_task_inactive(target, exam->state);
++ if (unlikely(ncsw != exam->ncsw))
++ ret = -EAGAIN;
++ put_task_struct(target);
++ }
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(utrace_finish_examine);
++
++/*
++ * This is declared in linux/regset.h and defined in machine-dependent
++ * code. We put the export here to ensure no machine forgets it.
++ */
++EXPORT_SYMBOL_GPL(task_user_regset_view);
++
++/*
++ * Called with rcu_read_lock() held.
++ */
++void task_utrace_proc_status(struct seq_file *m, struct task_struct *p)
++{
++ seq_printf(m, "Utrace:\t%lx\n", p->utrace_flags);
++}
+--
+1.7.0.1
+
diff --git a/hda_intel-prealloc-4mb-dmabuffer.patch b/hda_intel-prealloc-4mb-dmabuffer.patch
new file mode 100644
index 000000000..36e6aca4f
--- /dev/null
+++ b/hda_intel-prealloc-4mb-dmabuffer.patch
@@ -0,0 +1,47 @@
+From c69fcbd1f60b0842f7c1ad2c95692ffd19c4932b Mon Sep 17 00:00:00 2001
+From: Kyle McMartin <kyle@phobos.i.jkkm.org>
+Date: Mon, 29 Mar 2010 23:56:08 -0400
+Subject: hda_intel-prealloc-4mb-dmabuffer
+
+---
+ sound/pci/hda/hda_intel.c | 14 +++++++++++++-
+ 1 files changed, 13 insertions(+), 1 deletions(-)
+
+diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
+index 4bb9067..37db515 100644
+--- a/sound/pci/hda/hda_intel.c
++++ b/sound/pci/hda/hda_intel.c
+@@ -1986,6 +1986,7 @@ azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
+ struct azx_pcm *apcm;
+ int pcm_dev = cpcm->device;
+ int s, err;
++ size_t prealloc_min = 64*1024; /* 64KB */
+
+ if (pcm_dev >= HDA_MAX_PCMS) {
+ snd_printk(KERN_ERR SFX "Invalid PCM device number %d\n",
+@@ -2019,10 +2020,21 @@ azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
+ if (cpcm->stream[s].substreams)
+ snd_pcm_set_ops(pcm, s, &azx_pcm_ops);
+ }
++
+ /* buffer pre-allocation */
++
++ /* subtle, don't allocate a big buffer for modems...
++ * also, don't just test 32BIT_MASK, since azx supports
++ * 64-bit DMA in some cases.
++ */
++ /* lennart wants a 2.2MB buffer for 2sec of 48khz */
++ if (pcm->dev_class == SNDRV_PCM_CLASS_GENERIC &&
++ chip->pci->dma_mask >= DMA_32BIT_MASK)
++ prealloc_min = 4 * 1024 * 1024; /* 4MB */
++
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(chip->pci),
+- 1024 * 64, 32 * 1024 * 1024);
++ prealloc_min, 32 * 1024 * 1024);
+ return 0;
+ }
+
+--
+1.7.0.1
+
diff --git a/hdpvr-ir-enable.patch b/hdpvr-ir-enable.patch
new file mode 100644
index 000000000..787c8f84c
--- /dev/null
+++ b/hdpvr-ir-enable.patch
@@ -0,0 +1,220 @@
+diff --git a/drivers/media/video/hdpvr/Makefile b/drivers/media/video/hdpvr/Makefile
+index e0230fc..3baa9f6 100644
+--- a/drivers/media/video/hdpvr/Makefile
++++ b/drivers/media/video/hdpvr/Makefile
+@@ -1,6 +1,4 @@
+-hdpvr-objs := hdpvr-control.o hdpvr-core.o hdpvr-video.o
+-
+-hdpvr-$(CONFIG_I2C) += hdpvr-i2c.o
++hdpvr-objs := hdpvr-control.o hdpvr-core.o hdpvr-video.o hdpvr-i2c.o
+
+ obj-$(CONFIG_VIDEO_HDPVR) += hdpvr.o
+
+diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c
+index 830d47b..70cfdc8 100644
+--- a/drivers/media/video/hdpvr/hdpvr-core.c
++++ b/drivers/media/video/hdpvr/hdpvr-core.c
+@@ -364,14 +364,13 @@ static int hdpvr_probe(struct usb_interface *interface,
+ goto error;
+ }
+
+-#ifdef CONFIG_I2C
+- /* until i2c is working properly */
+- retval = 0; /* hdpvr_register_i2c_adapter(dev); */
++#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
++ retval = hdpvr_register_i2c_adapter(dev);
+ if (retval < 0) {
+ v4l2_err(&dev->v4l2_dev, "registering i2c adapter failed\n");
+ goto error;
+ }
+-#endif /* CONFIG_I2C */
++#endif
+
+ /* let the user know what node this device is now attached to */
+ v4l2_info(&dev->v4l2_dev, "device now attached to %s\n",
+diff --git a/drivers/media/video/hdpvr/hdpvr-i2c.c b/drivers/media/video/hdpvr/hdpvr-i2c.c
+index 463b81b..60cdc06 100644
+--- a/drivers/media/video/hdpvr/hdpvr-i2c.c
++++ b/drivers/media/video/hdpvr/hdpvr-i2c.c
+@@ -10,6 +10,8 @@
+ *
+ */
+
++#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
++
+ #include <linux/i2c.h>
+ #include <linux/slab.h>
+
+@@ -22,8 +24,11 @@
+ #define REQTYPE_I2C_WRITE 0xb0
+ #define REQTYPE_I2C_WRITE_STATT 0xd0
+
+-static int hdpvr_i2c_read(struct hdpvr_device *dev, unsigned char addr,
+- char *data, int len)
++#define HDPVR_HW_Z8F0811_IR_TX_I2C_ADDR 0x70
++#define HDPVR_HW_Z8F0811_IR_RX_I2C_ADDR 0x71
++
++static int hdpvr_i2c_read(struct hdpvr_device *dev, int bus,
++ unsigned char addr, char *data, int len)
+ {
+ int ret;
+ char *buf = kmalloc(len, GFP_KERNEL);
+@@ -33,7 +38,7 @@ static int hdpvr_i2c_read(struct hdpvr_device *dev, unsigned char addr,
+ ret = usb_control_msg(dev->udev,
+ usb_rcvctrlpipe(dev->udev, 0),
+ REQTYPE_I2C_READ, CTRL_READ_REQUEST,
+- 0x100|addr, 0, buf, len, 1000);
++ (bus << 8) | addr, 0, buf, len, 1000);
+
+ if (ret == len) {
+ memcpy(data, buf, len);
+@@ -46,8 +51,8 @@ static int hdpvr_i2c_read(struct hdpvr_device *dev, unsigned char addr,
+ return ret;
+ }
+
+-static int hdpvr_i2c_write(struct hdpvr_device *dev, unsigned char addr,
+- char *data, int len)
++static int hdpvr_i2c_write(struct hdpvr_device *dev, int bus,
++ unsigned char addr, char *data, int len)
+ {
+ int ret;
+ char *buf = kmalloc(len, GFP_KERNEL);
+@@ -58,7 +63,7 @@ static int hdpvr_i2c_write(struct hdpvr_device *dev, unsigned char addr,
+ ret = usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST,
+- 0x100|addr, 0, buf, len, 1000);
++ (bus << 8) | addr, 0, buf, len, 1000);
+
+ if (ret < 0)
+ goto error;
+@@ -68,7 +73,7 @@ static int hdpvr_i2c_write(struct hdpvr_device *dev, unsigned char addr,
+ REQTYPE_I2C_WRITE_STATT, CTRL_READ_REQUEST,
+ 0, 0, buf, 2, 1000);
+
+- if (ret == 2)
++ if ((ret == 2) && (buf[1] == (len - 1)))
+ ret = 0;
+ else if (ret >= 0)
+ ret = -EIO;
+@@ -93,10 +98,10 @@ static int hdpvr_transfer(struct i2c_adapter *i2c_adapter, struct i2c_msg *msgs,
+ addr = msgs[i].addr << 1;
+
+ if (msgs[i].flags & I2C_M_RD)
+- retval = hdpvr_i2c_read(dev, addr, msgs[i].buf,
++ retval = hdpvr_i2c_read(dev, 1, addr, msgs[i].buf,
+ msgs[i].len);
+ else
+- retval = hdpvr_i2c_write(dev, addr, msgs[i].buf,
++ retval = hdpvr_i2c_write(dev, 1, addr, msgs[i].buf,
+ msgs[i].len);
+ }
+
+@@ -115,31 +120,59 @@ static struct i2c_algorithm hdpvr_algo = {
+ .functionality = hdpvr_functionality,
+ };
+
++static struct i2c_adapter hdpvr_i2c_adapter_template = {
++ .name = "Hauppage HD PVR I2C",
++ .owner = THIS_MODULE,
++ .id = I2C_HW_B_HDPVR,
++ .algo = &hdpvr_algo,
++ .class = I2C_CLASS_TV_ANALOG,
++};
++
++static struct i2c_board_info hdpvr_i2c_board_info = {
++ I2C_BOARD_INFO("ir_tx_z8f0811_haup", HDPVR_HW_Z8F0811_IR_TX_I2C_ADDR),
++ I2C_BOARD_INFO("ir_rx_z8f0811_haup", HDPVR_HW_Z8F0811_IR_RX_I2C_ADDR),
++};
++
++static int hdpvr_activate_ir(struct hdpvr_device *dev)
++{
++ char buffer[8];
++
++ mutex_lock(&dev->i2c_mutex);
++
++ hdpvr_i2c_read(dev, 0, 0x54, buffer, 1);
++
++ buffer[0] = 0;
++ buffer[1] = 0x8;
++ hdpvr_i2c_write(dev, 1, 0x54, buffer, 2);
++
++ buffer[1] = 0x18;
++ hdpvr_i2c_write(dev, 1, 0x54, buffer, 2);
++
++ mutex_unlock(&dev->i2c_mutex);
++
++ return 0;
++}
++
+ int hdpvr_register_i2c_adapter(struct hdpvr_device *dev)
+ {
+- struct i2c_adapter *i2c_adap;
+ int retval = -ENOMEM;
+
+- i2c_adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
+- if (i2c_adap == NULL)
+- goto error;
++ hdpvr_activate_ir(dev);
+
+- strlcpy(i2c_adap->name, "Hauppauge HD PVR I2C",
+- sizeof(i2c_adap->name));
+- i2c_adap->algo = &hdpvr_algo;
+- i2c_adap->class = I2C_CLASS_TV_ANALOG;
+- i2c_adap->owner = THIS_MODULE;
+- i2c_adap->dev.parent = &dev->udev->dev;
++ memcpy(&dev->i2c_adapter, &hdpvr_i2c_adapter_template,
++ sizeof(struct i2c_adapter));
++ dev->i2c_adapter.dev.parent = &dev->udev->dev;
+
+- i2c_set_adapdata(i2c_adap, dev);
++ i2c_set_adapdata(&dev->i2c_adapter, dev);
+
+- retval = i2c_add_adapter(i2c_adap);
++ retval = i2c_add_adapter(&dev->i2c_adapter);
++ if (retval)
++ goto error;
+
+- if (!retval)
+- dev->i2c_adapter = i2c_adap;
+- else
+- kfree(i2c_adap);
++ i2c_new_device(&dev->i2c_adapter, &hdpvr_i2c_board_info);
+
+ error:
+ return retval;
+ }
++
++#endif
+diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c
+index c338f3f..26fd9bf 100644
+--- a/drivers/media/video/hdpvr/hdpvr-video.c
++++ b/drivers/media/video/hdpvr/hdpvr-video.c
+@@ -1221,12 +1221,9 @@ static void hdpvr_device_release(struct video_device *vdev)
+ v4l2_device_unregister(&dev->v4l2_dev);
+
+ /* deregister I2C adapter */
+-#ifdef CONFIG_I2C
++#if defined(CONFIG_I2C) || (CONFIG_I2C_MODULE)
+ mutex_lock(&dev->i2c_mutex);
+- if (dev->i2c_adapter)
+- i2c_del_adapter(dev->i2c_adapter);
+- kfree(dev->i2c_adapter);
+- dev->i2c_adapter = NULL;
++ i2c_del_adapter(&dev->i2c_adapter);
+ mutex_unlock(&dev->i2c_mutex);
+ #endif /* CONFIG_I2C */
+
+diff --git a/drivers/media/video/hdpvr/hdpvr.h b/drivers/media/video/hdpvr/hdpvr.h
+index b0f046d..2107055 100644
+--- a/drivers/media/video/hdpvr/hdpvr.h
++++ b/drivers/media/video/hdpvr/hdpvr.h
+@@ -102,7 +102,7 @@ struct hdpvr_device {
+ struct work_struct worker;
+
+ /* I2C adapter */
+- struct i2c_adapter *i2c_adapter;
++ struct i2c_adapter i2c_adapter;
+ /* I2C lock */
+ struct mutex i2c_mutex;
+
diff --git a/kernel.spec b/kernel.spec
new file mode 100644
index 000000000..2f7fe1f9e
--- /dev/null
+++ b/kernel.spec
@@ -0,0 +1,2195 @@
+# We have to override the new %%install behavior because, well... the kernel is special.
+%global __spec_install_pre %{___build_pre}
+
+Summary: The Linux kernel
+
+# For a stable, released kernel, released_kernel should be 1. For rawhide
+# and/or a kernel built from an rc or git snapshot, released_kernel should
+# be 0.
+%global released_kernel 0
+
+# Save original buildid for later if it's defined
+%if 0%{?buildid:1}
+%global orig_buildid %{buildid}
+%undefine buildid
+%endif
+
+###################################################################
+# Polite request for people who spin their own kernel rpms:
+# please modify the "buildid" define in a way that identifies
+# that the kernel isn't the stock distribution kernel, for example,
+# by setting the define to ".local" or ".bz123456". This will be
+# appended to the full kernel version.
+#
+# (Uncomment the '#' and both spaces below to set the buildid.)
+#
+# % define buildid .local
+###################################################################
+
+# The buildid can also be specified on the rpmbuild command line
+# by adding --define="buildid .whatever". If both the specfile and
+# the environment define a buildid they will be concatenated together.
+%if 0%{?orig_buildid:1}
+%if 0%{?buildid:1}
+%global srpm_buildid %{buildid}
+%define buildid %{srpm_buildid}%{orig_buildid}
+%else
+%define buildid %{orig_buildid}
+%endif
+%endif
+
+# fedora_build defines which build revision of this kernel version we're
+# building. Rather than incrementing forever, as with the prior versioning
+# setup, we set fedora_cvs_origin to the current cvs revision s/1.// of the
+# kernel spec when the kernel is rebased, so fedora_build automatically
+# works out to the offset from the rebase, so it doesn't get too ginormous.
+#
+# If you're building on a branch, the RCS revision will be something like
+# 1.1205.1.1. In this case we drop the initial 1, subtract fedora_cvs_origin
+# from the second number, and then append the rest of the RCS string as is.
+# Don't stare at the awk too long, you'll go blind.
+%define fedora_cvs_origin 2037
+%define fedora_cvs_revision() %2
+%global fedora_build %(echo %{fedora_cvs_origin}.%{fedora_cvs_revision $Revision: 1.2094 $} | awk -F . '{ OFS = "."; ORS = ""; print $3 - $1 ; i = 4 ; OFS = ""; while (i <= NF) { print ".", $i ; i++} }')
+
+# base_sublevel is the kernel version we're starting with and patching
+# on top of -- for example, 2.6.22-rc7-git1 starts with a 2.6.21 base,
+# which yields a base_sublevel of 21.
+%define base_sublevel 34
+
+## If this is a released kernel ##
+%if 0%{?released_kernel}
+
+# Do we have a -stable update to apply?
+%define stable_update 0
+# Is it a -stable RC?
+%define stable_rc 0
+# Set rpm version accordingly
+%if 0%{?stable_update}
+%define stablerev .%{stable_update}
+%define stable_base %{stable_update}
+%if 0%{?stable_rc}
+# stable RCs are incremental patches, so we need the previous stable patch
+%define stable_base %(echo $((%{stable_update} - 1)))
+%endif
+%endif
+%define rpmversion 2.6.%{base_sublevel}%{?stablerev}
+
+## The not-released-kernel case ##
+%else
+# The next upstream release sublevel (base_sublevel+1)
+%define upstream_sublevel %(echo $((%{base_sublevel} + 1)))
+# The rc snapshot level
+%define rcrev 6
+# The git snapshot level
+%define gitrev 1
+# Set rpm version accordingly
+%define rpmversion 2.6.%{upstream_sublevel}
+%endif
+# Nb: The above rcrev and gitrev values automagically define Patch00 and Patch01 below.
+
+# What parts do we want to build? We must build at least one kernel.
+# These are the kernels that are built IF the architecture allows it.
+# All should default to 1 (enabled) and be flipped to 0 (disabled)
+# by later arch-specific checks.
+
+# The following build options are enabled by default.
+# Use either --without <opt> in your rpmbuild command or force values
+# to 0 in here to disable them.
+#
+# standard kernel
+%define with_up %{?_without_up: 0} %{?!_without_up: 1}
+# kernel-smp (only valid for ppc 32-bit)
+%define with_smp %{?_without_smp: 0} %{?!_without_smp: 1}
+# kernel-debug
+%define with_debug %{?_without_debug: 0} %{?!_without_debug: 1}
+# kernel-doc
+%define with_doc %{?_without_doc: 0} %{?!_without_doc: 1}
+# kernel-headers
+%define with_headers %{?_without_headers: 0} %{?!_without_headers: 1}
+# kernel-firmware
+%define with_firmware %{?_with_firmware: 1} %{?!_with_firmware: 0}
+# tools/perf
+%define with_perf %{?_without_perf: 0} %{?!_without_perf: 1}
+# kernel-debuginfo
+%define with_debuginfo %{?_without_debuginfo: 0} %{?!_without_debuginfo: 1}
+# kernel-bootwrapper (for creating zImages from kernel + initrd)
+%define with_bootwrapper %{?_without_bootwrapper: 0} %{?!_without_bootwrapper: 1}
+# Want to build a the vsdo directories installed
+%define with_vdso_install %{?_without_vdso_install: 0} %{?!_without_vdso_install: 1}
+
+# Build the kernel-doc package, but don't fail the build if it botches.
+# Here "true" means "continue" and "false" means "fail the build".
+%if 0%{?released_kernel}
+%define doc_build_fail false
+%else
+%define doc_build_fail true
+%endif
+
+%define rawhide_skip_docs 1
+%if 0%{?rawhide_skip_docs}
+%define with_doc 0
+%define doc_build_fail true
+%endif
+
+# Additional options for user-friendly one-off kernel building:
+#
+# Only build the base kernel (--with baseonly):
+%define with_baseonly %{?_with_baseonly: 1} %{?!_with_baseonly: 0}
+# Only build the smp kernel (--with smponly):
+%define with_smponly %{?_with_smponly: 1} %{?!_with_smponly: 0}
+# Only build the debug kernel (--with dbgonly):
+%define with_dbgonly %{?_with_dbgonly: 1} %{?!_with_dbgonly: 0}
+
+# should we do C=1 builds with sparse
+%define with_sparse %{?_with_sparse: 1} %{?!_with_sparse: 0}
+
+# Set debugbuildsenabled to 1 for production (build separate debug kernels)
+# and 0 for rawhide (all kernels are debug kernels).
+# See also 'make debug' and 'make release'.
+%define debugbuildsenabled 0
+
+# Want to build a vanilla kernel build without any non-upstream patches?
+# (well, almost none, we need nonintconfig for build purposes). Default to 0 (off).
+%define with_vanilla %{?_with_vanilla: 1} %{?!_with_vanilla: 0}
+
+# pkg_release is what we'll fill in for the rpm Release: field
+%if 0%{?released_kernel}
+
+%if 0%{?stable_rc}
+%define stable_rctag .rc%{stable_rc}
+%endif
+%define pkg_release %{fedora_build}%{?stable_rctag}%{?buildid}%{?dist}
+
+%else
+
+# non-released_kernel
+%if 0%{?rcrev}
+%define rctag .rc%rcrev
+%else
+%define rctag .rc0
+%endif
+%if 0%{?gitrev}
+%define gittag .git%gitrev
+%else
+%define gittag .git0
+%endif
+%define pkg_release 0.%{fedora_build}%{?rctag}%{?gittag}%{?buildid}%{?dist}
+
+%endif
+
+# The kernel tarball/base version
+%define kversion 2.6.%{base_sublevel}
+
+%define make_target bzImage
+
+%define KVERREL %{version}-%{release}.%{_target_cpu}
+%define hdrarch %_target_cpu
+%define asmarch %_target_cpu
+
+%if 0%{!?nopatches:1}
+%define nopatches 0
+%endif
+
+%if %{with_vanilla}
+%define nopatches 1
+%endif
+
+%if %{nopatches}
+%define with_bootwrapper 0
+%define variant -vanilla
+%else
+%define variant_fedora -fedora
+%endif
+
+%define using_upstream_branch 0
+%if 0%{?upstream_branch:1}
+%define stable_update 0
+%define using_upstream_branch 1
+%define variant -%{upstream_branch}%{?variant_fedora}
+%define pkg_release 0.%{fedora_build}%{upstream_branch_tag}%{?buildid}%{?dist}
+%endif
+
+%if !%{debugbuildsenabled}
+%define with_debug 0
+%endif
+
+%if !%{with_debuginfo}
+%define _enable_debug_packages 0
+%endif
+%define debuginfodir /usr/lib/debug
+
+# kernel-PAE is only built on i686.
+%ifarch i686
+%define with_pae 1
+%else
+%define with_pae 0
+%endif
+
+# if requested, only build base kernel
+%if %{with_baseonly}
+%define with_smp 0
+%define with_debug 0
+%endif
+
+# if requested, only build smp kernel
+%if %{with_smponly}
+%define with_up 0
+%define with_debug 0
+%endif
+
+# if requested, only build debug kernel
+%if %{with_dbgonly}
+%if %{debugbuildsenabled}
+%define with_up 0
+%endif
+%define with_smp 0
+%define with_pae 0
+%define with_perf 0
+%endif
+
+%define all_x86 i386 i686
+
+%if %{with_vdso_install}
+# These arches install vdso/ directories.
+%define vdso_arches %{all_x86} x86_64 ppc ppc64
+%endif
+
+# Overrides for generic default options
+
+# only ppc and alphav56 need separate smp kernels
+%ifnarch ppc alphaev56
+%define with_smp 0
+%endif
+
+# don't do debug builds on anything but i686 and x86_64
+%ifnarch i686 x86_64
+%define with_debug 0
+%endif
+
+# only package docs noarch
+%ifnarch noarch
+%define with_doc 0
+%endif
+
+# don't build noarch kernels or headers (duh)
+%ifarch noarch
+%define with_up 0
+%define with_headers 0
+%define with_perf 0
+%define all_arch_configs kernel-%{version}-*.config
+%define with_firmware %{?_with_firmware: 1} %{?!_with_firmware: 0}
+%endif
+
+# bootwrapper is only on ppc
+%ifnarch ppc ppc64
+%define with_bootwrapper 0
+%endif
+
+# sparse blows up on ppc64 alpha and sparc64
+%ifarch ppc64 ppc alpha sparc64
+%define with_sparse 0
+%endif
+
+# Per-arch tweaks
+
+%ifarch %{all_x86}
+%define asmarch x86
+%define hdrarch i386
+%define all_arch_configs kernel-%{version}-i?86*.config
+%define image_install_path boot
+%define kernel_image arch/x86/boot/bzImage
+%endif
+
+%ifarch x86_64
+%define asmarch x86
+%define all_arch_configs kernel-%{version}-x86_64*.config
+%define image_install_path boot
+%define kernel_image arch/x86/boot/bzImage
+%endif
+
+%ifarch ppc64
+%define asmarch powerpc
+%define hdrarch powerpc
+%define all_arch_configs kernel-%{version}-ppc64*.config
+%define image_install_path boot
+%define make_target vmlinux
+%define kernel_image vmlinux
+%define kernel_image_elf 1
+%endif
+
+%ifarch s390x
+%define asmarch s390
+%define hdrarch s390
+%define all_arch_configs kernel-%{version}-s390x.config
+%define image_install_path boot
+%define make_target image
+%define kernel_image arch/s390/boot/image
+%endif
+
+%ifarch sparc64
+%define asmarch sparc
+%define all_arch_configs kernel-%{version}-sparc64*.config
+%define make_target image
+%define kernel_image arch/sparc/boot/image
+%define image_install_path boot
+%define with_perf 0
+%endif
+
+%ifarch ppc
+%define asmarch powerpc
+%define hdrarch powerpc
+%define all_arch_configs kernel-%{version}-ppc{-,.}*config
+%define image_install_path boot
+%define make_target vmlinux
+%define kernel_image vmlinux
+%define kernel_image_elf 1
+%endif
+
+%ifarch ia64
+%define all_arch_configs kernel-%{version}-ia64*.config
+%define image_install_path boot/efi/EFI/redhat
+%define make_target compressed
+%define kernel_image vmlinux.gz
+%endif
+
+%ifarch alpha alphaev56
+%define all_arch_configs kernel-%{version}-alpha*.config
+%define image_install_path boot
+%define make_target vmlinux
+%define kernel_image vmlinux
+%endif
+
+%ifarch %{arm}
+%define all_arch_configs kernel-%{version}-arm*.config
+%define image_install_path boot
+%define hdrarch arm
+%define make_target vmlinux
+%define kernel_image vmlinux
+%endif
+
+%if %{nopatches}
+# XXX temporary until last vdso patches are upstream
+%define vdso_arches ppc ppc64
+%endif
+
+%if %{nopatches}%{using_upstream_branch}
+# Ignore unknown options in our config-* files.
+# Some options go with patches we're not applying.
+%define oldconfig_target loose_nonint_oldconfig
+%else
+%define oldconfig_target nonint_oldconfig
+%endif
+
+# To temporarily exclude an architecture from being built, add it to
+# %nobuildarches. Do _NOT_ use the ExclusiveArch: line, because if we
+# don't build kernel-headers then the new build system will no longer let
+# us use the previous build of that package -- it'll just be completely AWOL.
+# Which is a BadThing(tm).
+
+# We only build kernel-headers on the following...
+%define nobuildarches i386 s390 sparc %{arm}
+
+%ifarch %nobuildarches
+%define with_up 0
+%define with_smp 0
+%define with_pae 0
+%define with_debuginfo 0
+%define with_perf 0
+%define _enable_debug_packages 0
+%endif
+
+%define with_pae_debug 0
+%if %{with_pae}
+%define with_pae_debug %{with_debug}
+%endif
+
+#
+# Three sets of minimum package version requirements in the form of Conflicts:
+# to versions below the minimum
+#
+
+#
+# First the general kernel 2.6 required versions as per
+# Documentation/Changes
+#
+%define kernel_dot_org_conflicts ppp < 2.4.3-3, isdn4k-utils < 3.2-32, nfs-utils < 1.0.7-12, e2fsprogs < 1.37-4, util-linux < 2.12, jfsutils < 1.1.7-2, reiserfs-utils < 3.6.19-2, xfsprogs < 2.6.13-4, procps < 3.2.5-6.3, oprofile < 0.9.1-2
+
+#
+# Then a series of requirements that are distribution specific, either
+# because we add patches for something, or the older versions have
+# problems with the newer kernel or lack certain things that make
+# integration in the distro harder than needed.
+#
+%define package_conflicts initscripts < 7.23, udev < 063-6, iptables < 1.3.2-1, ipw2200-firmware < 2.4, iwl4965-firmware < 228.57.2, selinux-policy-targeted < 1.25.3-14, squashfs-tools < 4.0, wireless-tools < 29-3
+
+# We moved the drm include files into kernel-headers, make sure there's
+# a recent enough libdrm-devel on the system that doesn't have those.
+%define kernel_headers_conflicts libdrm-devel < 2.4.0-0.15
+
+#
+# Packages that need to be installed before the kernel is, because the %post
+# scripts use them.
+#
+%define kernel_prereq fileutils, module-init-tools, initscripts >= 8.11.1-1, grubby >= 7.0.10-1
+%define initrd_prereq dracut >= 001-7
+
+#
+# This macro does requires, provides, conflicts, obsoletes for a kernel package.
+# %%kernel_reqprovconf <subpackage>
+# It uses any kernel_<subpackage>_conflicts and kernel_<subpackage>_obsoletes
+# macros defined above.
+#
+%define kernel_reqprovconf \
+Provides: kernel = %{rpmversion}-%{pkg_release}\
+Provides: kernel-%{_target_cpu} = %{rpmversion}-%{pkg_release}%{?1:.%{1}}\
+Provides: kernel-drm = 4.3.0\
+Provides: kernel-drm-nouveau = 16\
+Provides: kernel-modeset = 1\
+Provides: kernel-uname-r = %{KVERREL}%{?1:.%{1}}\
+Requires(pre): %{kernel_prereq}\
+Requires(pre): %{initrd_prereq}\
+%if %{with_firmware}\
+Requires(pre): kernel-firmware >= %{rpmversion}-%{pkg_release}\
+%else\
+Requires(pre): linux-firmware\
+%endif\
+Requires(post): /sbin/new-kernel-pkg\
+Requires(preun): /sbin/new-kernel-pkg\
+Conflicts: %{kernel_dot_org_conflicts}\
+Conflicts: %{package_conflicts}\
+%{expand:%%{?kernel%{?1:_%{1}}_conflicts:Conflicts: %%{kernel%{?1:_%{1}}_conflicts}}}\
+%{expand:%%{?kernel%{?1:_%{1}}_obsoletes:Obsoletes: %%{kernel%{?1:_%{1}}_obsoletes}}}\
+%{expand:%%{?kernel%{?1:_%{1}}_provides:Provides: %%{kernel%{?1:_%{1}}_provides}}}\
+# We can't let RPM do the dependencies automatic because it'll then pick up\
+# a correct but undesirable perl dependency from the module headers which\
+# isn't required for the kernel proper to function\
+AutoReq: no\
+AutoProv: yes\
+%{nil}
+
+Name: kernel%{?variant}
+Group: System Environment/Kernel
+License: GPLv2
+URL: http://www.kernel.org/
+Version: %{rpmversion}
+Release: %{pkg_release}
+# DO NOT CHANGE THE 'ExclusiveArch' LINE TO TEMPORARILY EXCLUDE AN ARCHITECTURE BUILD.
+# SET %%nobuildarches (ABOVE) INSTEAD
+ExclusiveArch: noarch %{all_x86} x86_64 ppc ppc64 ia64 sparc sparc64 s390 s390x alpha alphaev56 %{arm}
+ExclusiveOS: Linux
+
+%kernel_reqprovconf
+%ifarch x86_64 sparc64
+Obsoletes: kernel-smp
+%endif
+
+
+#
+# List the packages used during the kernel build
+#
+BuildRequires: module-init-tools, patch >= 2.5.4, bash >= 2.03, sh-utils, tar
+BuildRequires: bzip2, findutils, gzip, m4, perl, make >= 3.78, diffutils, gawk
+BuildRequires: gcc >= 3.4.2, binutils >= 2.12, redhat-rpm-config
+BuildRequires: net-tools
+BuildRequires: xmlto, asciidoc
+%if %{with_sparse}
+BuildRequires: sparse >= 0.4.1
+%endif
+%if %{with_perf}
+BuildRequires: elfutils-devel zlib-devel binutils-devel
+%endif
+BuildConflicts: rhbuildsys(DiskFree) < 500Mb
+
+%define fancy_debuginfo 0
+%if %{with_debuginfo}
+%if 0%{?fedora} >= 8 || 0%{?rhel} >= 6
+%define fancy_debuginfo 1
+%endif
+%endif
+
+%if %{fancy_debuginfo}
+# Fancy new debuginfo generation introduced in Fedora 8.
+BuildRequires: rpm-build >= 4.4.2.1-4
+%define debuginfo_args --strict-build-id
+%endif
+
+Source0: ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-%{kversion}.tar.bz2
+
+Source11: genkey
+Source14: find-provides
+Source15: merge.pl
+
+Source20: Makefile.config
+Source21: config-debug
+Source22: config-nodebug
+Source23: config-generic
+Source24: config-rhel-generic
+
+Source30: config-x86-generic
+Source31: config-i686-PAE
+
+Source40: config-x86_64-generic
+
+Source50: config-powerpc-generic
+Source51: config-powerpc32-generic
+Source52: config-powerpc32-smp
+Source53: config-powerpc64
+
+Source60: config-ia64-generic
+
+Source70: config-s390x
+
+Source90: config-sparc64-generic
+
+Source100: config-arm
+
+# Here should be only the patches up to the upstream canonical Linus tree.
+
+# For a stable release kernel
+%if 0%{?stable_update}
+%if 0%{?stable_base}
+%define stable_patch_00 patch-2.6.%{base_sublevel}.%{stable_base}.bz2
+Patch00: %{stable_patch_00}
+%endif
+%if 0%{?stable_rc}
+%define stable_patch_01 patch-2.6.%{base_sublevel}.%{stable_update}-rc%{stable_rc}.bz2
+Patch01: %{stable_patch_01}
+%endif
+
+# non-released_kernel case
+# These are automagically defined by the rcrev and gitrev values set up
+# near the top of this spec file.
+%else
+%if 0%{?rcrev}
+Patch00: patch-2.6.%{upstream_sublevel}-rc%{rcrev}.bz2
+%if 0%{?gitrev}
+Patch01: patch-2.6.%{upstream_sublevel}-rc%{rcrev}-git%{gitrev}.bz2
+%endif
+%else
+# pre-{base_sublevel+1}-rc1 case
+%if 0%{?gitrev}
+Patch00: patch-2.6.%{base_sublevel}-git%{gitrev}.bz2
+%endif
+%endif
+%endif
+
+%if %{using_upstream_branch}
+### BRANCH PATCH ###
+%endif
+
+Patch02: git-linus.diff
+
+# we always need nonintconfig, even for -vanilla kernels
+Patch03: linux-2.6-build-nonintconfig.patch
+
+# we also need compile fixes for -vanilla
+Patch04: linux-2.6-compile-fixes.patch
+
+# build tweak for build ID magic, even for -vanilla
+Patch05: linux-2.6-makefile-after_link.patch
+
+%if !%{nopatches}
+
+# revert upstream patches we get via other methods
+Patch09: linux-2.6-upstream-reverts.patch
+# Git trees.
+
+# Standalone patches
+Patch20: linux-2.6-hotfixes.patch
+
+Patch30: git-utrace.patch
+Patch31: utrace-ptrace-fix-build.patch
+Patch32: utrace-remove-use-of-kref_set.patch
+
+Patch150: linux-2.6.29-sparc-IOC_TYPECHECK.patch
+
+Patch160: linux-2.6-32bit-mmap-exec-randomization.patch
+Patch161: linux-2.6-i386-nx-emulation.patch
+
+Patch200: linux-2.6-debug-sizeof-structs.patch
+Patch201: linux-2.6-debug-nmi-timeout.patch
+Patch202: linux-2.6-debug-taint-vm.patch
+Patch203: linux-2.6-debug-vm-would-have-oomkilled.patch
+Patch204: linux-2.6-debug-always-inline-kzalloc.patch
+
+Patch380: linux-2.6-defaults-pci_no_msi.patch
+Patch383: linux-2.6-defaults-aspm.patch
+Patch384: pci-acpi-disable-aspm-if-no-osc.patch
+Patch385: pci-aspm-dont-enable-too-early.patch
+
+Patch390: linux-2.6-defaults-acpi-video.patch
+Patch391: linux-2.6-acpi-video-dos.patch
+Patch393: acpi-ec-add-delay-before-write.patch
+Patch394: linux-2.6-acpi-debug-infinite-loop.patch
+
+Patch450: linux-2.6-input-kill-stupid-messages.patch
+Patch452: linux-2.6.30-no-pcspkr-modalias.patch
+Patch454: thinkpad-acpi-fix-backlight.patch
+
+Patch460: linux-2.6-serial-460800.patch
+
+Patch470: die-floppy-die.patch
+
+Patch510: linux-2.6-silence-noise.patch
+Patch530: linux-2.6-silence-fbcon-logo.patch
+Patch570: linux-2.6-selinux-mprotect-checks.patch
+Patch580: linux-2.6-sparc-selinux-mprotect-checks.patch
+
+Patch610: hda_intel-prealloc-4mb-dmabuffer.patch
+
+Patch800: linux-2.6-crash-driver.patch
+
+# crypto/
+
+# virt + ksm patches
+Patch1555: fix_xen_guest_on_old_EC2.patch
+
+# DRM
+Patch1801: drm-revert-drm-fbdev-rework-output-polling-to-be-back-in-core.patch
+Patch1802: revert-drm-kms-toggle-poll-around-switcheroo.patch
+# nouveau + drm fixes
+Patch1810: drm-nouveau-updates.patch
+Patch1819: drm-intel-big-hammer.patch
+# intel drm is all merged upstream
+Patch1824: drm-intel-next.patch
+# make sure the lvds comes back on lid open
+Patch1825: drm-intel-make-lvds-work.patch
+Patch1900: linux-2.6-intel-iommu-igfx.patch
+
+# linux1394 git patches
+Patch2200: linux-2.6-firewire-git-update.patch
+Patch2201: linux-2.6-firewire-git-pending.patch
+
+# Quiet boot fixes
+# silence the ACPI blacklist code
+Patch2802: linux-2.6-silence-acpi-blacklist.patch
+
+# media patches
+Patch2899: linux-2.6-v4l-dvb-fixes.patch
+Patch2900: linux-2.6-v4l-dvb-update.patch
+Patch2901: linux-2.6-v4l-dvb-experimental.patch
+Patch2902: linux-2.6-v4l-dvb-uvcvideo-update.patch
+
+Patch2910: linux-2.6-v4l-dvb-add-lgdt3304-support.patch
+Patch2911: linux-2.6-v4l-dvb-add-kworld-a340-support.patch
+Patch2912: linux-2.6-v4l-dvb-ir-core-update.patch
+
+Patch2915: lirc-staging-2.6.36.patch
+#Patch2916: lirc-staging-2.6.36-fixes.patch
+Patch2917: hdpvr-ir-enable.patch
+
+# fs fixes
+Patch3000: linux-2.6-ext4-fix-freeze-deadlock.patch
+
+# NFSv4
+
+# VIA Nano / VX8xx updates
+
+# patches headed upstream
+
+Patch12016: disable-i8042-check-on-apple-mac.patch
+
+Patch12017: prevent-runtime-conntrack-changes.patch
+
+Patch12018: neuter_intel_microcode_load.patch
+
+Patch12030: ssb_check_for_sprom.patch
+
+Patch12040: only-use-alpha2-regulatory-information-from-country-IE.patch
+
+%endif
+
+BuildRoot: %{_tmppath}/kernel-%{KVERREL}-root
+
+%description
+The kernel package contains the Linux kernel (vmlinuz), the core of any
+Linux operating system. The kernel handles the basic functions
+of the operating system: memory allocation, process allocation, device
+input and output, etc.
+
+
+%package doc
+Summary: Various documentation bits found in the kernel source
+Group: Documentation
+%description doc
+This package contains documentation files from the kernel
+source. Various bits of information about the Linux kernel and the
+device drivers shipped with it are documented in these files.
+
+You'll want to install this package if you need a reference to the
+options that can be passed to Linux kernel modules at load time.
+
+
+%package headers
+Summary: Header files for the Linux kernel for use by glibc
+Group: Development/System
+Obsoletes: glibc-kernheaders
+Provides: glibc-kernheaders = 3.0-46
+%description headers
+Kernel-headers includes the C header files that specify the interface
+between the Linux kernel and userspace libraries and programs. The
+header files define structures and constants that are needed for
+building most standard programs and are also needed for rebuilding the
+glibc package.
+
+%package firmware
+Summary: Firmware files used by the Linux kernel
+Group: Development/System
+# This is... complicated.
+# Look at the WHENCE file.
+License: GPL+ and GPLv2+ and MIT and Redistributable, no modification permitted
+%if "x%{?variant}" != "x"
+Provides: kernel-firmware = %{rpmversion}-%{pkg_release}
+%endif
+%description firmware
+Kernel-firmware includes firmware files required for some devices to
+operate.
+
+%package bootwrapper
+Summary: Boot wrapper files for generating combined kernel + initrd images
+Group: Development/System
+Requires: gzip
+%description bootwrapper
+Kernel-bootwrapper contains the wrapper code which makes bootable "zImage"
+files combining both kernel and initial ramdisk.
+
+%package debuginfo-common-%{_target_cpu}
+Summary: Kernel source files used by %{name}-debuginfo packages
+Group: Development/Debug
+%description debuginfo-common-%{_target_cpu}
+This package is required by %{name}-debuginfo subpackages.
+It provides the kernel source files common to all builds.
+
+%if %{with_perf}
+%package -n perf
+Summary: Performance monitoring for the Linux kernel
+Group: Development/System
+License: GPLv2
+%description -n perf
+This package provides the perf tool and the supporting documentation.
+%endif
+
+
+#
+# This macro creates a kernel-<subpackage>-debuginfo package.
+# %%kernel_debuginfo_package <subpackage>
+#
+%define kernel_debuginfo_package() \
+%package %{?1:%{1}-}debuginfo\
+Summary: Debug information for package %{name}%{?1:-%{1}}\
+Group: Development/Debug\
+Requires: %{name}-debuginfo-common-%{_target_cpu} = %{version}-%{release}\
+Provides: %{name}%{?1:-%{1}}-debuginfo-%{_target_cpu} = %{version}-%{release}\
+AutoReqProv: no\
+%description -n %{name}%{?1:-%{1}}-debuginfo\
+This package provides debug information for package %{name}%{?1:-%{1}}.\
+This is required to use SystemTap with %{name}%{?1:-%{1}}-%{KVERREL}.\
+%{expand:%%global debuginfo_args %{?debuginfo_args} -p '/.*/%%{KVERREL}%{?1:\.%{1}}/.*|/.*%%{KVERREL}%{?1:\.%{1}}(\.debug)?' -o debuginfo%{?1}.list}\
+%{nil}
+
+#
+# This macro creates a kernel-<subpackage>-devel package.
+# %%kernel_devel_package <subpackage> <pretty-name>
+#
+%define kernel_devel_package() \
+%package %{?1:%{1}-}devel\
+Summary: Development package for building kernel modules to match the %{?2:%{2} }kernel\
+Group: System Environment/Kernel\
+Provides: kernel%{?1:-%{1}}-devel-%{_target_cpu} = %{version}-%{release}\
+Provides: kernel-devel-%{_target_cpu} = %{version}-%{release}%{?1:.%{1}}\
+Provides: kernel-devel = %{version}-%{release}%{?1:.%{1}}\
+Provides: kernel-devel-uname-r = %{KVERREL}%{?1:.%{1}}\
+AutoReqProv: no\
+Requires(pre): /usr/bin/find\
+Requires: perl\
+%description -n kernel%{?variant}%{?1:-%{1}}-devel\
+This package provides kernel headers and makefiles sufficient to build modules\
+against the %{?2:%{2} }kernel package.\
+%{nil}
+
+#
+# This macro creates a kernel-<subpackage> and its -devel and -debuginfo too.
+# %%define variant_summary The Linux kernel compiled for <configuration>
+# %%kernel_variant_package [-n <pretty-name>] <subpackage>
+#
+%define kernel_variant_package(n:) \
+%package %1\
+Summary: %{variant_summary}\
+Group: System Environment/Kernel\
+%kernel_reqprovconf\
+%{expand:%%kernel_devel_package %1 %{!?-n:%1}%{?-n:%{-n*}}}\
+%{expand:%%kernel_debuginfo_package %1}\
+%{nil}
+
+
+# First the auxiliary packages of the main kernel package.
+%kernel_devel_package
+%kernel_debuginfo_package
+
+
+# Now, each variant package.
+
+%define variant_summary The Linux kernel compiled for SMP machines
+%kernel_variant_package -n SMP smp
+%description smp
+This package includes a SMP version of the Linux kernel. It is
+required only on machines with two or more CPUs as well as machines with
+hyperthreading technology.
+
+Install the kernel-smp package if your machine uses two or more CPUs.
+
+
+%define variant_summary The Linux kernel compiled for PAE capable machines
+%kernel_variant_package PAE
+%description PAE
+This package includes a version of the Linux kernel with support for up to
+64GB of high memory. It requires a CPU with Physical Address Extensions (PAE).
+The non-PAE kernel can only address up to 4GB of memory.
+Install the kernel-PAE package if your machine has more than 4GB of memory.
+
+
+%define variant_summary The Linux kernel compiled with extra debugging enabled for PAE capable machines
+%kernel_variant_package PAEdebug
+Obsoletes: kernel-PAE-debug
+%description PAEdebug
+This package includes a version of the Linux kernel with support for up to
+64GB of high memory. It requires a CPU with Physical Address Extensions (PAE).
+The non-PAE kernel can only address up to 4GB of memory.
+Install the kernel-PAE package if your machine has more than 4GB of memory.
+
+This variant of the kernel has numerous debugging options enabled.
+It should only be installed when trying to gather additional information
+on kernel bugs, as some of these options impact performance noticably.
+
+
+%define variant_summary The Linux kernel compiled with extra debugging enabled
+%kernel_variant_package debug
+%description debug
+The kernel package contains the Linux kernel (vmlinuz), the core of any
+Linux operating system. The kernel handles the basic functions
+of the operating system: memory allocation, process allocation, device
+input and output, etc.
+
+This variant of the kernel has numerous debugging options enabled.
+It should only be installed when trying to gather additional information
+on kernel bugs, as some of these options impact performance noticably.
+
+
+%prep
+# do a few sanity-checks for --with *only builds
+%if %{with_baseonly}
+%if !%{with_up}%{with_pae}
+echo "Cannot build --with baseonly, up build is disabled"
+exit 1
+%endif
+%endif
+
+%if %{with_smponly}
+%if !%{with_smp}
+echo "Cannot build --with smponly, smp build is disabled"
+exit 1
+%endif
+%endif
+
+# more sanity checking; do it quietly
+if [ "%{patches}" != "%%{patches}" ] ; then
+ for patch in %{patches} ; do
+ if [ ! -f $patch ] ; then
+ echo "ERROR: Patch ${patch##/*/} listed in specfile but is missing"
+ exit 1
+ fi
+ done
+fi 2>/dev/null
+
+patch_command='patch -p1 -F1 -s'
+ApplyPatch()
+{
+ local patch=$1
+ shift
+ if [ ! -f $RPM_SOURCE_DIR/$patch ]; then
+ exit 1
+ fi
+%if !%{using_upstream_branch}
+ if ! egrep "^Patch[0-9]+: $patch\$" %{_specdir}/${RPM_PACKAGE_NAME%%%%%{?variant}}.spec ; then
+ if [ "${patch:0:10}" != "patch-2.6." ] ; then
+ echo "ERROR: Patch $patch not listed as a source patch in specfile"
+ exit 1
+ fi
+ fi 2>/dev/null
+%endif
+ case "$patch" in
+ *.bz2) bunzip2 < "$RPM_SOURCE_DIR/$patch" | $patch_command ${1+"$@"} ;;
+ *.gz) gunzip < "$RPM_SOURCE_DIR/$patch" | $patch_command ${1+"$@"} ;;
+ *) $patch_command ${1+"$@"} < "$RPM_SOURCE_DIR/$patch" ;;
+ esac
+}
+
+# don't apply patch if it's empty
+ApplyOptionalPatch()
+{
+ local patch=$1
+ shift
+ if [ ! -f $RPM_SOURCE_DIR/$patch ]; then
+ exit 1
+ fi
+ local C=$(wc -l $RPM_SOURCE_DIR/$patch | awk '{print $1}')
+ if [ "$C" -gt 9 ]; then
+ ApplyPatch $patch ${1+"$@"}
+ fi
+}
+
+# we don't want a .config file when building firmware: it just confuses the build system
+%define build_firmware \
+ mv .config .config.firmware_save \
+ make INSTALL_FW_PATH=$RPM_BUILD_ROOT/lib/firmware firmware_install \
+ mv .config.firmware_save .config
+
+# First we unpack the kernel tarball.
+# If this isn't the first make prep, we use links to the existing clean tarball
+# which speeds things up quite a bit.
+
+# Update to latest upstream.
+%if 0%{?released_kernel}
+%define vanillaversion 2.6.%{base_sublevel}
+# non-released_kernel case
+%else
+%if 0%{?rcrev}
+%define vanillaversion 2.6.%{upstream_sublevel}-rc%{rcrev}
+%if 0%{?gitrev}
+%define vanillaversion 2.6.%{upstream_sublevel}-rc%{rcrev}-git%{gitrev}
+%endif
+%else
+# pre-{base_sublevel+1}-rc1 case
+%if 0%{?gitrev}
+%define vanillaversion 2.6.%{base_sublevel}-git%{gitrev}
+%else
+%define vanillaversion 2.6.%{base_sublevel}
+%endif
+%endif
+%endif
+
+# We can share hardlinked source trees by putting a list of
+# directory names of the CVS checkouts that we want to share
+# with in .shared-srctree. (Full pathnames are required.)
+[ -f .shared-srctree ] && sharedirs=$(cat .shared-srctree)
+
+if [ ! -d kernel-%{kversion}/vanilla-%{vanillaversion} ]; then
+
+ if [ -d kernel-%{kversion}/vanilla-%{kversion} ]; then
+
+ cd kernel-%{kversion}
+
+ # Any vanilla-* directories other than the base one are stale.
+ for dir in vanilla-*; do
+ [ "$dir" = vanilla-%{kversion} ] || rm -rf $dir &
+ done
+
+ else
+
+ # Ok, first time we do a make prep.
+ rm -f pax_global_header
+ for sharedir in $sharedirs ; do
+ if [[ ! -z $sharedir && -d $sharedir/kernel-%{kversion}/vanilla-%{kversion} ]] ; then
+ break
+ fi
+ done
+ if [[ ! -z $sharedir && -d $sharedir/kernel-%{kversion}/vanilla-%{kversion} ]] ; then
+%setup -q -n kernel-%{kversion} -c -T
+ cp -rl $sharedir/kernel-%{kversion}/vanilla-%{kversion} .
+ else
+%setup -q -n kernel-%{kversion} -c
+ mv linux-%{kversion} vanilla-%{kversion}
+ fi
+
+ fi
+
+%if "%{kversion}" != "%{vanillaversion}"
+
+ for sharedir in $sharedirs ; do
+ if [[ ! -z $sharedir && -d $sharedir/kernel-%{kversion}/vanilla-%{vanillaversion} ]] ; then
+ break
+ fi
+ done
+ if [[ ! -z $sharedir && -d $sharedir/kernel-%{kversion}/vanilla-%{vanillaversion} ]] ; then
+
+ cp -rl $sharedir/kernel-%{kversion}/vanilla-%{vanillaversion} .
+
+ else
+
+ cp -rl vanilla-%{kversion} vanilla-%{vanillaversion}
+ cd vanilla-%{vanillaversion}
+
+# Update vanilla to the latest upstream.
+# (non-released_kernel case only)
+%if 0%{?rcrev}
+ ApplyPatch patch-2.6.%{upstream_sublevel}-rc%{rcrev}.bz2
+%if 0%{?gitrev}
+ ApplyPatch patch-2.6.%{upstream_sublevel}-rc%{rcrev}-git%{gitrev}.bz2
+%endif
+%else
+# pre-{base_sublevel+1}-rc1 case
+%if 0%{?gitrev}
+ ApplyPatch patch-2.6.%{base_sublevel}-git%{gitrev}.bz2
+%endif
+%endif
+
+ cd ..
+
+ fi
+
+%endif
+
+else
+ # We already have a vanilla dir.
+ cd kernel-%{kversion}
+fi
+
+if [ -d linux-%{kversion}.%{_target_cpu} ]; then
+ # Just in case we ctrl-c'd a prep already
+ rm -rf deleteme.%{_target_cpu}
+ # Move away the stale away, and delete in background.
+ mv linux-%{kversion}.%{_target_cpu} deleteme.%{_target_cpu}
+ rm -rf deleteme.%{_target_cpu} &
+fi
+
+cp -rl vanilla-%{vanillaversion} linux-%{kversion}.%{_target_cpu}
+
+cd linux-%{kversion}.%{_target_cpu}
+
+# released_kernel with possible stable updates
+%if 0%{?stable_base}
+ApplyPatch %{stable_patch_00}
+%endif
+%if 0%{?stable_rc}
+ApplyPatch %{stable_patch_01}
+%endif
+
+%if %{using_upstream_branch}
+### BRANCH APPLY ###
+%endif
+
+# Drop some necessary files from the source dir into the buildroot
+cp $RPM_SOURCE_DIR/config-* .
+cp %{SOURCE15} .
+
+# Dynamically generate kernel .config files from config-* files
+make -f %{SOURCE20} VERSION=%{version} configs
+
+#if a rhel kernel, apply the rhel config options
+%if 0%{?rhel}
+ for i in %{all_arch_configs}
+ do
+ mv $i $i.tmp
+ ./merge.pl config-rhel-generic $i.tmp > $i
+ rm $i.tmp
+ done
+%endif
+
+ApplyOptionalPatch git-linus.diff
+
+# This patch adds a "make nonint_oldconfig" which is non-interactive and
+# also gives a list of missing options at the end. Useful for automated
+# builds (as used in the buildsystem).
+ApplyPatch linux-2.6-build-nonintconfig.patch
+
+ApplyPatch linux-2.6-makefile-after_link.patch
+
+#
+# misc small stuff to make things compile
+#
+ApplyOptionalPatch linux-2.6-compile-fixes.patch
+
+%if !%{nopatches}
+
+# revert patches from upstream that conflict or that we get via other means
+ApplyOptionalPatch linux-2.6-upstream-reverts.patch -R
+
+ApplyPatch linux-2.6-hotfixes.patch
+
+# Roland's utrace ptrace replacement.
+ApplyPatch git-utrace.patch
+ApplyPatch utrace-ptrace-fix-build.patch
+ApplyPatch utrace-remove-use-of-kref_set.patch
+
+# Architecture patches
+# x86(-64)
+
+#
+# Intel IOMMU
+#
+
+#
+# PowerPC
+#
+
+#
+# SPARC64
+#
+ApplyPatch linux-2.6.29-sparc-IOC_TYPECHECK.patch
+
+#
+# Exec shield
+#
+ApplyPatch linux-2.6-i386-nx-emulation.patch
+ApplyPatch linux-2.6-32bit-mmap-exec-randomization.patch
+
+#
+# bugfixes to drivers and filesystems
+#
+
+# ext4
+ApplyPatch linux-2.6-ext4-fix-freeze-deadlock.patch
+
+# xfs
+
+# btrfs
+
+
+# eCryptfs
+
+# NFSv4
+
+# USB
+
+# WMI
+
+# ACPI
+ApplyPatch linux-2.6-defaults-acpi-video.patch
+ApplyPatch linux-2.6-acpi-video-dos.patch
+ApplyPatch acpi-ec-add-delay-before-write.patch
+ApplyPatch linux-2.6-acpi-debug-infinite-loop.patch
+
+# Various low-impact patches to aid debugging.
+ApplyPatch linux-2.6-debug-sizeof-structs.patch
+ApplyPatch linux-2.6-debug-nmi-timeout.patch
+ApplyPatch linux-2.6-debug-taint-vm.patch
+ApplyPatch linux-2.6-debug-vm-would-have-oomkilled.patch
+ApplyPatch linux-2.6-debug-always-inline-kzalloc.patch
+
+#
+# PCI
+#
+# make default state of PCI MSI a config option
+ApplyPatch linux-2.6-defaults-pci_no_msi.patch
+# enable ASPM by default on hardware we expect to work
+ApplyPatch linux-2.6-defaults-aspm.patch
+# disable aspm if acpi doesn't provide an _OSC method
+ApplyPatch pci-acpi-disable-aspm-if-no-osc.patch
+# allow drivers to disable aspm at load time
+ApplyPatch pci-aspm-dont-enable-too-early.patch
+
+#
+# SCSI Bits.
+#
+
+# ACPI
+
+# ALSA
+ApplyPatch hda_intel-prealloc-4mb-dmabuffer.patch
+
+# Networking
+
+# Misc fixes
+# The input layer spews crap no-one cares about.
+ApplyPatch linux-2.6-input-kill-stupid-messages.patch
+
+# stop floppy.ko from autoloading during udev...
+ApplyPatch die-floppy-die.patch
+
+ApplyPatch linux-2.6.30-no-pcspkr-modalias.patch
+
+#ApplyPatch thinkpad-acpi-fix-backlight.patch
+
+# Allow to use 480600 baud on 16C950 UARTs
+ApplyPatch linux-2.6-serial-460800.patch
+
+# Silence some useless messages that still get printed with 'quiet'
+ApplyPatch linux-2.6-silence-noise.patch
+
+# Make fbcon not show the penguins with 'quiet'
+ApplyPatch linux-2.6-silence-fbcon-logo.patch
+
+# Fix the SELinux mprotect checks on executable mappings
+#ApplyPatch linux-2.6-selinux-mprotect-checks.patch
+# Fix SELinux for sparc
+# FIXME: Can we drop this now? See updated linux-2.6-selinux-mprotect-checks.patch
+#ApplyPatch linux-2.6-sparc-selinux-mprotect-checks.patch
+
+# Changes to upstream defaults.
+
+
+# /dev/crash driver.
+ApplyPatch linux-2.6-crash-driver.patch
+
+# crypto/
+
+# Assorted Virt Fixes
+ApplyPatch fix_xen_guest_on_old_EC2.patch
+
+#ApplyPatch drm-revert-drm-fbdev-rework-output-polling-to-be-back-in-core.patch
+#ApplyPatch revert-drm-kms-toggle-poll-around-switcheroo.patch
+
+# Nouveau DRM + drm fixes
+ApplyPatch drm-nouveau-updates.patch
+ApplyPatch drm-intel-big-hammer.patch
+ApplyOptionalPatch drm-intel-next.patch
+ApplyPatch drm-intel-make-lvds-work.patch
+ApplyPatch linux-2.6-intel-iommu-igfx.patch
+
+# linux1394 git patches
+#ApplyPatch linux-2.6-firewire-git-update.patch
+#ApplyOptionalPatch linux-2.6-firewire-git-pending.patch
+
+# silence the ACPI blacklist code
+ApplyPatch linux-2.6-silence-acpi-blacklist.patch
+
+# V4L/DVB updates/fixes/experimental drivers
+# apply if non-empty
+ApplyOptionalPatch linux-2.6-v4l-dvb-fixes.patch
+ApplyOptionalPatch linux-2.6-v4l-dvb-update.patch
+ApplyOptionalPatch linux-2.6-v4l-dvb-experimental.patch
+ApplyPatch linux-2.6-v4l-dvb-uvcvideo-update.patch
+
+ApplyPatch linux-2.6-v4l-dvb-ir-core-update.patch
+ApplyPatch linux-2.6-v4l-dvb-add-lgdt3304-support.patch
+ApplyPatch linux-2.6-v4l-dvb-add-kworld-a340-support.patch
+
+# http://www.lirc.org/
+ApplyPatch lirc-staging-2.6.36.patch
+#ApplyOptionalPatch lirc-staging-2.6.36-fixes.patch
+# enable IR receiver on Hauppauge HD PVR (v4l-dvb merge pending)
+ApplyPatch hdpvr-ir-enable.patch
+
+# Patches headed upstream
+ApplyPatch disable-i8042-check-on-apple-mac.patch
+
+ApplyPatch neuter_intel_microcode_load.patch
+
+# rhbz#533746
+#ApplyPatch ssb_check_for_sprom.patch
+
+ApplyPatch only-use-alpha2-regulatory-information-from-country-IE.patch
+
+# END OF PATCH APPLICATIONS
+
+%endif
+
+# Any further pre-build tree manipulations happen here.
+
+chmod +x scripts/checkpatch.pl
+
+# only deal with configs if we are going to build for the arch
+%ifnarch %nobuildarches
+
+mkdir configs
+
+# Remove configs not for the buildarch
+for cfg in kernel-%{version}-*.config; do
+ if [ `echo %{all_arch_configs} | grep -c $cfg` -eq 0 ]; then
+ rm -f $cfg
+ fi
+done
+
+%if !%{debugbuildsenabled}
+rm -f kernel-%{version}-*debug.config
+%endif
+
+# now run oldconfig over all the config files
+for i in *.config
+do
+ mv $i .config
+ Arch=`head -1 .config | cut -b 3-`
+ make ARCH=$Arch %{oldconfig_target} > /dev/null
+ echo "# $Arch" > configs/$i
+ cat .config >> configs/$i
+done
+# end of kernel config
+%endif
+
+# get rid of unwanted files resulting from patch fuzz
+find . \( -name "*.orig" -o -name "*~" \) -exec rm -f {} \; >/dev/null
+
+cd ..
+
+###
+### build
+###
+%build
+
+%if %{with_sparse}
+%define sparse_mflags C=1
+%endif
+
+%if %{fancy_debuginfo}
+# This override tweaks the kernel makefiles so that we run debugedit on an
+# object before embedding it. When we later run find-debuginfo.sh, it will
+# run debugedit again. The edits it does change the build ID bits embedded
+# in the stripped object, but repeating debugedit is a no-op. We do it
+# beforehand to get the proper final build ID bits into the embedded image.
+# This affects the vDSO images in vmlinux, and the vmlinux image in bzImage.
+export AFTER_LINK=\
+'sh -xc "/usr/lib/rpm/debugedit -b $$RPM_BUILD_DIR -d /usr/src/debug \
+ -i $@ > $@.id"'
+%endif
+
+cp_vmlinux()
+{
+ eu-strip --remove-comment -o "$2" "$1"
+}
+
+BuildKernel() {
+ MakeTarget=$1
+ KernelImage=$2
+ Flavour=$3
+ InstallName=${4:-vmlinuz}
+
+ # Pick the right config file for the kernel we're building
+ Config=kernel-%{version}-%{_target_cpu}${Flavour:+-${Flavour}}.config
+ DevelDir=/usr/src/kernels/%{KVERREL}${Flavour:+.${Flavour}}
+
+ # When the bootable image is just the ELF kernel, strip it.
+ # We already copy the unstripped file into the debuginfo package.
+ if [ "$KernelImage" = vmlinux ]; then
+ CopyKernel=cp_vmlinux
+ else
+ CopyKernel=cp
+ fi
+
+ KernelVer=%{version}-%{release}.%{_target_cpu}${Flavour:+.${Flavour}}
+ echo BUILDING A KERNEL FOR ${Flavour} %{_target_cpu}...
+
+ # make sure EXTRAVERSION says what we want it to say
+ perl -p -i -e "s/^EXTRAVERSION.*/EXTRAVERSION = %{?stablerev}-%{release}.%{_target_cpu}${Flavour:+.${Flavour}}/" Makefile
+
+ # if pre-rc1 devel kernel, must fix up SUBLEVEL for our versioning scheme
+ %if !0%{?rcrev}
+ %if 0%{?gitrev}
+ perl -p -i -e 's/^SUBLEVEL.*/SUBLEVEL = %{upstream_sublevel}/' Makefile
+ %endif
+ %endif
+
+ # and now to start the build process
+
+ make -s mrproper
+ cp configs/$Config .config
+
+ Arch=`head -1 .config | cut -b 3-`
+ echo USING ARCH=$Arch
+
+ make -s ARCH=$Arch %{oldconfig_target} > /dev/null
+ make -s ARCH=$Arch V=1 %{?_smp_mflags} $MakeTarget %{?sparse_mflags}
+ make -s ARCH=$Arch V=1 %{?_smp_mflags} modules %{?sparse_mflags} || exit 1
+
+ # Start installing the results
+%if %{with_debuginfo}
+ mkdir -p $RPM_BUILD_ROOT%{debuginfodir}/boot
+ mkdir -p $RPM_BUILD_ROOT%{debuginfodir}/%{image_install_path}
+%endif
+ mkdir -p $RPM_BUILD_ROOT/%{image_install_path}
+ install -m 644 .config $RPM_BUILD_ROOT/boot/config-$KernelVer
+ install -m 644 System.map $RPM_BUILD_ROOT/boot/System.map-$KernelVer
+
+ # We estimate the size of the initramfs because rpm needs to take this size
+ # into consideration when performing disk space calculations. (See bz #530778)
+ dd if=/dev/zero of=$RPM_BUILD_ROOT/boot/initramfs-$KernelVer.img bs=1M count=20
+
+ if [ -f arch/$Arch/boot/zImage.stub ]; then
+ cp arch/$Arch/boot/zImage.stub $RPM_BUILD_ROOT/%{image_install_path}/zImage.stub-$KernelVer || :
+ fi
+ $CopyKernel $KernelImage \
+ $RPM_BUILD_ROOT/%{image_install_path}/$InstallName-$KernelVer
+ chmod 755 $RPM_BUILD_ROOT/%{image_install_path}/$InstallName-$KernelVer
+
+ mkdir -p $RPM_BUILD_ROOT/lib/modules/$KernelVer
+ # Override $(mod-fw) because we don't want it to install any firmware
+ # We'll do that ourselves with 'make firmware_install'
+ make -s ARCH=$Arch INSTALL_MOD_PATH=$RPM_BUILD_ROOT modules_install KERNELRELEASE=$KernelVer mod-fw=
+%ifarch %{vdso_arches}
+ make -s ARCH=$Arch INSTALL_MOD_PATH=$RPM_BUILD_ROOT vdso_install KERNELRELEASE=$KernelVer
+ if [ ! -s ldconfig-kernel.conf ]; then
+ echo > ldconfig-kernel.conf "\
+# Placeholder file, no vDSO hwcap entries used in this kernel."
+ fi
+ %{__install} -D -m 444 ldconfig-kernel.conf \
+ $RPM_BUILD_ROOT/etc/ld.so.conf.d/kernel-$KernelVer.conf
+%endif
+
+ # And save the headers/makefiles etc for building modules against
+ #
+ # This all looks scary, but the end result is supposed to be:
+ # * all arch relevant include/ files
+ # * all Makefile/Kconfig files
+ # * all script/ files
+
+ rm -f $RPM_BUILD_ROOT/lib/modules/$KernelVer/build
+ rm -f $RPM_BUILD_ROOT/lib/modules/$KernelVer/source
+ mkdir -p $RPM_BUILD_ROOT/lib/modules/$KernelVer/build
+ (cd $RPM_BUILD_ROOT/lib/modules/$KernelVer ; ln -s build source)
+ # dirs for additional modules per module-init-tools, kbuild/modules.txt
+ mkdir -p $RPM_BUILD_ROOT/lib/modules/$KernelVer/extra
+ mkdir -p $RPM_BUILD_ROOT/lib/modules/$KernelVer/updates
+ # first copy everything
+ cp --parents `find -type f -name "Makefile*" -o -name "Kconfig*"` $RPM_BUILD_ROOT/lib/modules/$KernelVer/build
+ cp Module.symvers $RPM_BUILD_ROOT/lib/modules/$KernelVer/build
+ cp System.map $RPM_BUILD_ROOT/lib/modules/$KernelVer/build
+ if [ -s Module.markers ]; then
+ cp Module.markers $RPM_BUILD_ROOT/lib/modules/$KernelVer/build
+ fi
+ # then drop all but the needed Makefiles/Kconfig files
+ rm -rf $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/Documentation
+ rm -rf $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/scripts
+ rm -rf $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/include
+ cp .config $RPM_BUILD_ROOT/lib/modules/$KernelVer/build
+ cp -a scripts $RPM_BUILD_ROOT/lib/modules/$KernelVer/build
+ if [ -d arch/$Arch/scripts ]; then
+ cp -a arch/$Arch/scripts $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/arch/%{_arch} || :
+ fi
+ if [ -f arch/$Arch/*lds ]; then
+ cp -a arch/$Arch/*lds $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/arch/%{_arch}/ || :
+ fi
+ rm -f $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/scripts/*.o
+ rm -f $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/scripts/*/*.o
+%ifarch ppc
+ cp -a --parents arch/powerpc/lib/crtsavres.[So] $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/
+%endif
+ if [ -d arch/%{asmarch}/include ]; then
+ cp -a --parents arch/%{asmarch}/include $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/
+ fi
+ cp -a include $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/include
+
+ # Make sure the Makefile and version.h have a matching timestamp so that
+ # external modules can be built
+ touch -r $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/Makefile $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/include/linux/version.h
+ touch -r $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/.config $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/include/linux/autoconf.h
+ # Copy .config to include/config/auto.conf so "make prepare" is unnecessary.
+ cp $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/.config $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/include/config/auto.conf
+
+ if test -s vmlinux.id; then
+ cp vmlinux.id $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/vmlinux.id
+ else
+ echo >&2 "*** WARNING *** no vmlinux build ID! ***"
+ fi
+
+ #
+ # save the vmlinux file for kernel debugging into the kernel-debuginfo rpm
+ #
+%if %{with_debuginfo}
+ mkdir -p $RPM_BUILD_ROOT%{debuginfodir}/lib/modules/$KernelVer
+ cp vmlinux $RPM_BUILD_ROOT%{debuginfodir}/lib/modules/$KernelVer
+%endif
+
+ find $RPM_BUILD_ROOT/lib/modules/$KernelVer -name "*.ko" -type f >modnames
+
+ # mark modules executable so that strip-to-file can strip them
+ xargs --no-run-if-empty chmod u+x < modnames
+
+ # Generate a list of modules for block and networking.
+
+ fgrep /drivers/ modnames | xargs --no-run-if-empty nm -upA |
+ sed -n 's,^.*/\([^/]*\.ko\): *U \(.*\)$,\1 \2,p' > drivers.undef
+
+ collect_modules_list()
+ {
+ sed -r -n -e "s/^([^ ]+) \\.?($2)\$/\\1/p" drivers.undef |
+ LC_ALL=C sort -u > $RPM_BUILD_ROOT/lib/modules/$KernelVer/modules.$1
+ }
+
+ collect_modules_list networking \
+ 'register_netdev|ieee80211_register_hw|usbnet_probe|phy_driver_register'
+ collect_modules_list block \
+ 'ata_scsi_ioctl|scsi_add_host|scsi_add_host_with_dma|blk_init_queue|register_mtd_blktrans|scsi_esp_register|scsi_register_device_handler'
+ collect_modules_list drm \
+ 'drm_open|drm_init'
+ collect_modules_list modesetting \
+ 'drm_crtc_init'
+
+ # detect missing or incorrect license tags
+ rm -f modinfo
+ while read i
+ do
+ echo -n "${i#$RPM_BUILD_ROOT/lib/modules/$KernelVer/} " >> modinfo
+ /sbin/modinfo -l $i >> modinfo
+ done < modnames
+
+ egrep -v \
+ 'GPL( v2)?$|Dual BSD/GPL$|Dual MPL/GPL$|GPL and additional rights$' \
+ modinfo && exit 1
+
+ rm -f modinfo modnames
+
+ # remove files that will be auto generated by depmod at rpm -i time
+ for i in alias alias.bin ccwmap dep dep.bin ieee1394map inputmap isapnpmap ofmap pcimap seriomap symbols symbols.bin usbmap
+ do
+ rm -f $RPM_BUILD_ROOT/lib/modules/$KernelVer/modules.$i
+ done
+
+ # Move the devel headers out of the root file system
+ mkdir -p $RPM_BUILD_ROOT/usr/src/kernels
+ mv $RPM_BUILD_ROOT/lib/modules/$KernelVer/build $RPM_BUILD_ROOT/$DevelDir
+ ln -sf ../../..$DevelDir $RPM_BUILD_ROOT/lib/modules/$KernelVer/build
+}
+
+###
+# DO it...
+###
+
+# prepare directories
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/boot
+mkdir -p $RPM_BUILD_ROOT%{_libexecdir}
+
+cd linux-%{kversion}.%{_target_cpu}
+
+%if %{with_debug}
+BuildKernel %make_target %kernel_image debug
+%endif
+
+%if %{with_pae_debug}
+BuildKernel %make_target %kernel_image PAEdebug
+%endif
+
+%if %{with_pae}
+BuildKernel %make_target %kernel_image PAE
+%endif
+
+%if %{with_up}
+BuildKernel %make_target %kernel_image
+%endif
+
+%if %{with_smp}
+BuildKernel %make_target %kernel_image smp
+%endif
+
+%global perf_make \
+ make %{?_smp_mflags} -C tools/perf -s V=1 NO_DEMANGLE=1 prefix=%{_prefix}
+%if %{with_perf}
+%{perf_make} all
+%{perf_make} man || %{doc_build_fail}
+%endif
+
+%if %{with_doc}
+# Make the HTML and man pages.
+make %{?_smp_mflags} htmldocs mandocs || %{doc_build_fail}
+
+# sometimes non-world-readable files sneak into the kernel source tree
+chmod -R a=rX Documentation
+find Documentation -type d | xargs chmod u+w
+%endif
+
+###
+### Special hacks for debuginfo subpackages.
+###
+
+# This macro is used by %%install, so we must redefine it before that.
+%define debug_package %{nil}
+
+%if %{fancy_debuginfo}
+%define __debug_install_post \
+ /usr/lib/rpm/find-debuginfo.sh %{debuginfo_args} %{_builddir}/%{?buildsubdir}\
+%{nil}
+%endif
+
+%if %{with_debuginfo}
+%ifnarch noarch
+%global __debug_package 1
+%files -f debugfiles.list debuginfo-common-%{_target_cpu}
+%defattr(-,root,root)
+%endif
+%endif
+
+###
+### install
+###
+
+%install
+
+cd linux-%{kversion}.%{_target_cpu}
+
+%if %{with_doc}
+docdir=$RPM_BUILD_ROOT%{_datadir}/doc/kernel-doc-%{rpmversion}
+man9dir=$RPM_BUILD_ROOT%{_datadir}/man/man9
+
+# copy the source over
+mkdir -p $docdir
+tar -f - --exclude=man --exclude='.*' -c Documentation | tar xf - -C $docdir
+
+# Install man pages for the kernel API.
+mkdir -p $man9dir
+find Documentation/DocBook/man -name '*.9.gz' -print0 |
+xargs -0 --no-run-if-empty %{__install} -m 444 -t $man9dir $m
+ls $man9dir | grep -q '' || > $man9dir/BROKEN
+%endif # with_doc
+
+%if %{with_perf}
+# perf tool binary and supporting scripts/binaries
+%{perf_make} DESTDIR=$RPM_BUILD_ROOT install
+
+# perf man pages (note: implicit rpm magic compresses them later)
+%{perf_make} DESTDIR=$RPM_BUILD_ROOT install-man || %{doc_build_fail}
+%endif
+
+%if %{with_headers}
+# Install kernel headers
+make ARCH=%{hdrarch} INSTALL_HDR_PATH=$RPM_BUILD_ROOT/usr headers_install
+
+# Do headers_check but don't die if it fails.
+make ARCH=%{hdrarch} INSTALL_HDR_PATH=$RPM_BUILD_ROOT/usr headers_check \
+ > hdrwarnings.txt || :
+if grep -q exist hdrwarnings.txt; then
+ sed s:^$RPM_BUILD_ROOT/usr/include/:: hdrwarnings.txt
+ # Temporarily cause a build failure if header inconsistencies.
+ # exit 1
+fi
+
+find $RPM_BUILD_ROOT/usr/include \
+ \( -name .install -o -name .check -o \
+ -name ..install.cmd -o -name ..check.cmd \) | xargs rm -f
+
+# glibc provides scsi headers for itself, for now
+rm -rf $RPM_BUILD_ROOT/usr/include/scsi
+rm -f $RPM_BUILD_ROOT/usr/include/asm*/atomic.h
+rm -f $RPM_BUILD_ROOT/usr/include/asm*/io.h
+rm -f $RPM_BUILD_ROOT/usr/include/asm*/irq.h
+%endif
+
+%if %{with_firmware}
+%{build_firmware}
+%endif
+
+%if %{with_bootwrapper}
+make DESTDIR=$RPM_BUILD_ROOT bootwrapper_install WRAPPER_OBJDIR=%{_libdir}/kernel-wrapper WRAPPER_DTSDIR=%{_libdir}/kernel-wrapper/dts
+%endif
+
+
+###
+### clean
+###
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+###
+### scripts
+###
+
+#
+# This macro defines a %%post script for a kernel*-devel package.
+# %%kernel_devel_post [<subpackage>]
+#
+%define kernel_devel_post() \
+%{expand:%%post %{?1:%{1}-}devel}\
+if [ -f /etc/sysconfig/kernel ]\
+then\
+ . /etc/sysconfig/kernel || exit $?\
+fi\
+if [ "$HARDLINK" != "no" -a -x /usr/sbin/hardlink ]\
+then\
+ (cd /usr/src/kernels/%{KVERREL}%{?1:.%{1}} &&\
+ /usr/bin/find . -type f | while read f; do\
+ hardlink -c /usr/src/kernels/*.fc*.*/$f $f\
+ done)\
+fi\
+%{nil}
+
+# This macro defines a %%posttrans script for a kernel package.
+# %%kernel_variant_posttrans [<subpackage>]
+# More text can follow to go at the end of this variant's %%post.
+#
+%define kernel_variant_posttrans() \
+%{expand:%%posttrans %{?1}}\
+/sbin/new-kernel-pkg --package kernel%{?-v:-%{-v*}} --mkinitrd --dracut --depmod --update %{KVERREL}%{?-v:.%{-v*}} || exit $?\
+/sbin/new-kernel-pkg --package kernel%{?1:-%{1}} --rpmposttrans %{KVERREL}%{?1:.%{1}} || exit $?\
+%{nil}
+
+#
+# This macro defines a %%post script for a kernel package and its devel package.
+# %%kernel_variant_post [-v <subpackage>] [-r <replace>]
+# More text can follow to go at the end of this variant's %%post.
+#
+%define kernel_variant_post(v:r:) \
+%{expand:%%kernel_devel_post %{?-v*}}\
+%{expand:%%kernel_variant_posttrans %{?-v*}}\
+%{expand:%%post %{?-v*}}\
+%{-r:\
+if [ `uname -i` == "x86_64" -o `uname -i` == "i386" ] &&\
+ [ -f /etc/sysconfig/kernel ]; then\
+ /bin/sed -r -i -e 's/^DEFAULTKERNEL=%{-r*}$/DEFAULTKERNEL=kernel%{?-v:-%{-v*}}/' /etc/sysconfig/kernel || exit $?\
+fi}\
+%{expand:\
+/sbin/new-kernel-pkg --package kernel%{?-v:-%{-v*}} --install %{KVERREL}%{?-v:.%{-v*}} || exit $?\
+}\
+%{nil}
+
+#
+# This macro defines a %%preun script for a kernel package.
+# %%kernel_variant_preun <subpackage>
+#
+%define kernel_variant_preun() \
+%{expand:%%preun %{?1}}\
+/sbin/new-kernel-pkg --rminitrd --rmmoddep --remove %{KVERREL}%{?1:.%{1}} || exit $?\
+%{nil}
+
+%kernel_variant_preun
+%kernel_variant_post -r kernel-smp
+
+%kernel_variant_preun smp
+%kernel_variant_post -v smp
+
+%kernel_variant_preun PAE
+%kernel_variant_post -v PAE -r (kernel|kernel-smp)
+
+%kernel_variant_preun debug
+%kernel_variant_post -v debug
+
+%kernel_variant_post -v PAEdebug -r (kernel|kernel-smp)
+%kernel_variant_preun PAEdebug
+
+if [ -x /sbin/ldconfig ]
+then
+ /sbin/ldconfig -X || exit $?
+fi
+
+###
+### file lists
+###
+
+%if %{with_headers}
+%files headers
+%defattr(-,root,root)
+/usr/include/*
+%endif
+
+%if %{with_firmware}
+%files firmware
+%defattr(-,root,root)
+/lib/firmware/*
+%doc linux-%{kversion}.%{_target_cpu}/firmware/WHENCE
+%endif
+
+%if %{with_bootwrapper}
+%files bootwrapper
+%defattr(-,root,root)
+/usr/sbin/*
+%{_libdir}/kernel-wrapper
+%endif
+
+# only some architecture builds need kernel-doc
+%if %{with_doc}
+%files doc
+%defattr(-,root,root)
+%{_datadir}/doc/kernel-doc-%{rpmversion}/Documentation/*
+%dir %{_datadir}/doc/kernel-doc-%{rpmversion}/Documentation
+%dir %{_datadir}/doc/kernel-doc-%{rpmversion}
+%{_datadir}/man/man9/*
+%endif
+
+%if %{with_perf}
+%files -n perf
+%defattr(-,root,root)
+%{_bindir}/perf
+%dir %{_libexecdir}/perf-core
+%{_libexecdir}/perf-core/*
+%{_mandir}/man[1-8]/*
+%endif
+
+# This is %{image_install_path} on an arch where that includes ELF files,
+# or empty otherwise.
+%define elf_image_install_path %{?kernel_image_elf:%{image_install_path}}
+
+#
+# This macro defines the %%files sections for a kernel package
+# and its devel and debuginfo packages.
+# %%kernel_variant_files [-k vmlinux] <condition> <subpackage>
+#
+%define kernel_variant_files(k:) \
+%if %{1}\
+%{expand:%%files %{?2}}\
+%defattr(-,root,root)\
+/%{image_install_path}/%{?-k:%{-k*}}%{!?-k:vmlinuz}-%{KVERREL}%{?2:.%{2}}\
+/boot/System.map-%{KVERREL}%{?2:.%{2}}\
+/boot/config-%{KVERREL}%{?2:.%{2}}\
+%dir /lib/modules/%{KVERREL}%{?2:.%{2}}\
+/lib/modules/%{KVERREL}%{?2:.%{2}}/kernel\
+/lib/modules/%{KVERREL}%{?2:.%{2}}/build\
+/lib/modules/%{KVERREL}%{?2:.%{2}}/source\
+/lib/modules/%{KVERREL}%{?2:.%{2}}/extra\
+/lib/modules/%{KVERREL}%{?2:.%{2}}/updates\
+%ifarch %{vdso_arches}\
+/lib/modules/%{KVERREL}%{?2:.%{2}}/vdso\
+/etc/ld.so.conf.d/kernel-%{KVERREL}%{?2:.%{2}}.conf\
+%endif\
+/lib/modules/%{KVERREL}%{?2:.%{2}}/modules.*\
+%ghost /boot/initramfs-%{KVERREL}%{?2:.%{2}}.img\
+%{expand:%%files %{?2:%{2}-}devel}\
+%defattr(-,root,root)\
+/usr/src/kernels/%{KVERREL}%{?2:.%{2}}\
+%if %{with_debuginfo}\
+%ifnarch noarch\
+%if %{fancy_debuginfo}\
+%{expand:%%files -f debuginfo%{?2}.list %{?2:%{2}-}debuginfo}\
+%else\
+%{expand:%%files %{?2:%{2}-}debuginfo}\
+%endif\
+%defattr(-,root,root)\
+%if !%{fancy_debuginfo}\
+%if "%{elf_image_install_path}" != ""\
+%{debuginfodir}/%{elf_image_install_path}/*-%{KVERREL}%{?2:.%{2}}.debug\
+%endif\
+%{debuginfodir}/lib/modules/%{KVERREL}%{?2:.%{2}}\
+%{debuginfodir}/usr/src/kernels/%{KVERREL}%{?2:.%{2}}\
+%endif\
+%endif\
+%endif\
+%endif\
+%{nil}
+
+
+%kernel_variant_files %{with_up}
+%kernel_variant_files %{with_smp} smp
+%kernel_variant_files %{with_debug} debug
+%kernel_variant_files %{with_pae} PAE
+%kernel_variant_files %{with_pae_debug} PAEdebug
+
+# plz don't put in a version string unless you're going to tag
+# and build.
+
+# ___________________________________________________________
+# / This branch is for Fedora 14. You probably want to commit \
+# \ to the F-13 branch instead, or in addition to this one. /
+# -----------------------------------------------------------
+# \ ^__^
+# \ (@@)\_______
+# (__)\ )\/\
+# ||----w |
+# || ||
+
+%changelog
+* Mon Jul 27 2010 Kyle McMartin <kyle@redhat.com>
+- Patch from linville to only use the country code to set band limits.
+ (Fixes Apple Airport base stations from limiting you from associating
+ with other channels.)
+
+* Fri Jul 23 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.35-0.56.rc6.git1
+- Linux 2.6.35-rc6-git1
+
+* Thu Jul 22 2010 Dave Jones <davej@redhat.com>
+- 2.6.35-rc6
+
+* Thu Jul 22 2010 Ben Skeggsb <bskeggs@redhat.com>
+- drm-nouveau-updates: bring back, most patches *weren't* upstream yet,
+ they're queued for 2.6.36.
+
+* Wed Jul 21 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.35-0.53.rc5.git7
+- Linux 2.6.35-rc5-git7
+
+* Wed Jul 21 2010 Dave Jones <davej@redhat.com>
+- Remove the %verify (no mtime) on kernel-devel's files.
+ If they got modified, they should fail rpm verify.
+
+* Wed Jul 21 2010 Dave Jones <davej@redhat.com>
+- Linux 2.6.35-rc5-git6
+- Removed drm-nouveau-updates.patch (upstreamed)
+
+* Mon Jul 19 2010 Chuck Ebbert <cebbert@redhat.com>
+- Linux 2.6.35-rc5-git4
+
+* Mon Jul 19 2010 Jarod Wilson <jarod@redhat.com> 2.6.35-0.49.rc5.git2
+- Fix from Kyle for input_set_key oops introduced by ir-core patches (#615707)
+- Update lirc-staging patches to match what's about to be submitted upstream,
+ complete with updated copy_from_user overflow check fixages
+
+* Sun Jul 18 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.35-0.47.rc5.git2
+- Linux 2.6.35-rc5-git2
+
+* Sun Jul 18 2010 Chuck Ebbert <cebbert@redhat.com>
+- lirc-staging-2.6.36-fixes.patch: Fix up buffer size checking in
+ lirc code, found by CONFIG_DEBUG_STRICT_USER_COPY_CHECKS
+
+* Sun Jul 18 2010 Hans de Goede <hdegoede@redhat.com>
+- Add support for dynamic menu controls to the uvcvideo driver (#576023)
+
+* Sun Jul 18 2010 Jarod Wilson <jarod@redhat.com> 2.6.35-0.44.rc5.git1
+- Oops, minor oversight when moving lirc bits into staging resulted
+ in the modules themselves not getting built. Fix that.
+
+* Fri Jul 16 2010 Dave Jones <davej@redhat.com>
+- Limit some alsa spew that the nuforce udac makes happen a lot.
+
+* Fri Jul 16 2010 Jarod Wilson <jarod@redhat.com> 2.6.35-0.41.rc5.git1
+- Pull in ir-core update from v4l/dvb staging for 2.6.36, which includes
+ new in-kernel lirc_dev and ir-core mceusb driver
+- Move not-yet-merged/ported lirc bits to drivers/staging/, patch to be
+ sent upstream for 2.6.36 staging tree RSN
+
+* Thu Jul 15 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.35-0.40.rc5.git1
+- Replace pci-acpi-disable-aspm-if-no-osc.patch with
+ updated version from F-13
+
+* Thu Jul 15 2010 Chuck Ebbert <cebbert@redhat.com>
+- Linux 2.6.35-rc5-git1
+
+* Tue Jul 13 2010 Chuck Ebbert <cebbert@redhat.com>
+- Linux 2.6.35-rc5
+
+* Tue Jul 13 2010 Ben Skeggs <bskeggs@redhat.com>
+- nouveau: miscellanous fixes
+
+* Mon Jul 12 2010 Dave Jones <davej@redhat.com>
+- 2.6.35-rc4-git5
+
+* Mon Jul 12 2010 Dave Jones <davej@redhat.com>
+- Remove a bunch of x86 options from config files that get set
+ automatically, and can't be overridden.
+
+* Fri Jul 9 2010 Roland McGrath <roland@redhat.com>
+- Split execshield into two patches.
+
+* Thu Jul 08 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.35-0.31.rc4.git4
+- Linux 2.6.35-rc4-git4
+
+* Thu Jul 8 2010 Roland McGrath <roland@redhat.com>
+- Remove exec-shield=2 setting, some other cruft from execshield.
+
+* Wed Jul 7 2010 Roland McGrath <roland@redhat.com> 2.6.35-0.29.rc4.git0.fc14
+- Revamp perf packaging.
+
+* Wed Jul 07 2010 Chuck Ebbert <cebbert@redhat.com>
+- pci-acpi-disable-aspm-if-no-osc.patch, pci-aspm-dont-enable-too-early.patch
+ PCI layer fixes for problems with hardware that doesn't support ASPM.
+
+* Wed Jul 07 2010 Ben Skeggs <bskeggs@redhat.com>
+- nouveau: bring in nouveau upstream
+
+* Mon Jul 05 2010 Dave Jones <davej@redhat.com>
+- 2.6.35-rc4
+
+* Fri Jul 02 2010 Dave Jones <davej@redhat.com>
+- 2.6.35-rc3-git7
+ dropped: i915-fix-crt-hotplug-regression.patch (upstream)
+ dropped: drm-i915-fix-edp-panels.patch (upstream)
+
+* Fri Jul 02 2010 Dave Jones <davej@redhat.com>
+- 2.6.35-rc3-git6
+
+* Thu Jul 01 2010 Dave Jones <davej@redhat.com>
+- Add a patch to debug an infinite loop warning from acpi.
+
+* Thu Jul 01 2010 Dave Jones <davej@redhat.com>
+- 2.6.35-rc3-git5
+
+* Thu Jul 01 2010 Chuck Ebbert <cebbert@redhat.com>
+- Copy fix for BZ#220892 from F-13.
+
+* Wed Jun 30 2010 Kyle McMartin <kyle@redhat.com> 2.6.35-0.19.rc3.git4
+- 2.6.35-rc3-git4
+
+* Tue Jun 29 2010 Chuck Ebbert <cebbert@redhat.com>
+- Disable Intel Moorestown support -- it breaks PC keyboard controllers.
+
+* Tue Jun 29 2010 Dave Jones <davej@redhat.com>
+- Building external modules requires perl. (#608525)
+
+* Tue Jun 29 2010 Kyle McMartin <kyle@redhat.com> 2.6.35-0.15.rc3.git3
+- 2.6.35-rc3-git3
+- i915-fix-crt-hotplug-regression.patch: attempt to solve the gm45 hotplug
+ irq storm.
+
+* Mon Jun 28 2010 Chuck Ebbert <cebbert@redhat.com>
+- ppc64: enable active memory sharing and DLPAR memory remove (#607175)
+
+* Mon Jun 28 2010 Dave Jones <davej@redhat.com>
+- 2.6.35-rc3-git2
+
+* Mon Jun 28 2010 Dave Jones <davej@redhat.com>
+- 2.6.35-rc3-git1
+
+* Mon Jun 28 2010 Dave Airlie <airlied@redhat.com>
+- drm-i915-fix-edp-panels.patch: update to newer version
+
+* Fri Jun 25 2010 Kyle McMartin <kyle@redhat.com>
+- drm-i915-fix-edp-panels.patch: copy from rawhide.
+
+* Wed Jun 23 2010 Eric Sandeen <sandeen@redhat.com>
+- Fix ext4 freeze deadlock (#607245)
+
+* Wed Jun 23 2010 Kyle McMartin <kyle@redhat.com>
+- Override generic's CONFIG_HZ_1000 on s390.
+
+* Tue Jun 22 2010 Dave Jones <davej@redhat.com>
+- Fix localhost networking.
+
+* Tue Jun 22 2010 Kyle McMartin <kyle@redhat.com> 2.6.35-0.1.rc3.git0
+- Putting the raw back into rawhide... Yeehaw.
+- 2.6.35-rc3
+- Drop a tonne of patches that were merged upstream, or were backports.
+- Rebase execshield, utrace.
+- Fix up a bunch of rejects, build failures.
+- Fix up lirc to build with strict copy_from_user checking.
+
+* Mon Jun 21 2010 Dave Jones <davej@redhat.com>
+- Disable workaround for obscure SMP pentium pro errata.
+ I miss the 1990s too, but it's time to move on.
+ If anyone actually needs this it would be better done using
+ the apply_alternatives infrastructure.
+
+* Mon Jun 21 2010 Kyle McMartin <kyle@redhat.com> 2.6.34-45
+- drm-revert-drm-fbdev-rework-output-polling-to-be-back-in-core.patch
+ Revert eb1f8e4f, bisected by Nicolas Kaiser. Thanks! (rhbz#599190)
+ (If this works, will try to root-cause.)
+- rebase previous patch on top of above reversion
+
+* Mon Jun 21 2010 Kyle McMartin <kyle@redhat.com> 2.6.34-44
+- revert-drm-kms-toggle-poll-around-switcheroo.patch (rhbz#599190)
+
+* Thu Jun 17 2010 Kyle McMartin <kyle@redhat.com> 2.6.34-43
+- Suck in patch from Dave Miller in 2.6.35 to add async hash testing,
+ hopefully fixes error from previous commit. (But making it modular
+ is still a good idea.)
+
+* Thu Jun 17 2010 Kyle McMartin <kyle@redhat.com>
+- make ghash-clmulni modular to get rid of early boot noise (rhbz#586954)
+ (not a /fix/ but it should at least quiet boot down a bit if you have
+ the cpu support)
+
+* Wed Jun 16 2010 Kyle McMartin <kyle@redhat.com> 2.6.34-40
+- Snag some more DRM commits into drm-next.patch that I missed the first
+ time.
+- Fix up radeon_pm toggle to work with the upstream code.
+
+* Tue Jun 15 2010 Prarit Bhargava <prarit@redhat.com>
+- Turn off CONFIG_I2O on x86.
+ It is broken on 64-bit address spaces (i686/PAE, x86_64), and frankly, I'm
+ having trouble finding anyone who actually uses it.
+
+* Tue Jun 15 2010 Kyle McMartin <kyle@redhat.com> 2.6.34-38
+- Fix build by nuking superfluous "%{expand" which was missing a
+ trailing '}'. You may now reward me with an array of alcoholic
+ beverages, I so richly deserve for spending roughly a full
+ day staring at the diff of the spec.
+
+* Mon Jun 14 2010 Kyle McMartin <kyle@redhat.com> 2.6.34-37
+- btrfs ACL fixes from CVE-2010-2071.
+
+* Sun Jun 13 2010 Kyle McMartin <kyle@redhat.com> 2.6.34-36
+- remunge and reapply hdpvr-ir-enable
+
+* Sun Jun 13 2010 Kyle McMartin <kyle@redhat.com> 2.6.34-35
+- mac80211/iwlwifi fix connections to some APs (rhbz#558002)
+ patches from sgruszka@.
+
+* Sun Jun 13 2010 Kyle McMartin <kyle@redhat.com> 2.6.34-34
+- Provide a knob to enable radeon_pm to allow users to test
+ that functionality. Add radeon.pm=1 to your kernel cmdline
+ in order to enable it. (It still defaults to off though.)
+
+* Sun Jun 13 2010 Kyle McMartin <kyle@redhat.com> 2.6.34-33
+- Update drm-next to include fixes since 2.6.35-rc1.
+
+* Fri Jun 11 2010 Justin M. Forbes <jforbes@redhat.com>
+- Disable xsave for so that kernel will boot on ancient EC2 hosts.
+
+* Wed Jun 09 2010 John W. Linville <linville@redhat.com>
+- Disable rt20xx and rt35xx chipset support in rt2800 drivers (#570869)
+
+* Wed Jun 09 2010 David Woodhouse <David.Woodhouse@intel.com>
+- Include PHY modules in modules.networking (#602155)
+
+* Tue Jun 08 2010 Dave Jones <davej@redhat.com>
+- Remove useless -kdump kernel support
+
+* Tue Jun 08 2010 Dave Jones <davej@redhat.com>
+- Remove ia64 ata quirk which had no explanation, and still
+ isn't upstream. No-one cares.
+
+* Tue Jun 08 2010 Dave Jones <davej@redhat.com>
+- Drop linux-2.6-vio-modalias.patch
+ Two years should have been long enough to get upstream if this is important.
+
+* Tue Jun 08 2010 Dave Jones <davej@redhat.com>
+- Remove crufty Xen remnants from specfile.
+
+* Tue Jun 08 2010 Dave Jones <davej@redhat.com>
+- Remove mkinitrd ifdefs. Dracut or GTFO.
+
+* Thu Jun 03 2010 Kyle McMartin <kyle@redhat.com>
+- Build kernel headers on s390.
+
+* Wed Jun 02 2010 Kyle McMartin <kyle@redhat.com> 2.6.34-20
+- Disable doc_build_fail because xmlto et al. are crud.
+
+* Wed Jun 02 2010 Kyle McMartin <kyle@redhat.com> 2.6.34-19
+- Enable -debug flavour builds, until we branch for 2.6.35-rcX.
+
+* Wed Jun 02 2010 Kyle McMartin <kyle@redhat.com>
+- revert writeback fixes for now, there appear to be dragons
+ lurking there still.
+
+* Tue Jun 01 2010 Kyle McMartin <kyle@redhat.com> 2.6.34-18
+- fix mismerge in i915_gem.c, drm_gem_object_alloc is now
+ i915_gem_alloc_object.
+- add a hunk to rcu_read{,un}lock in sched_fair too.
+
+* Tue Jun 01 2010 Kyle McMartin <kyle@redhat.com> 2.6.34-17
+- backport writeback fixes from Jens until stable@ picks them up.
+
+* Tue Jun 01 2010 Kyle McMartin <kyle@redhat.com> 2.6.34-16
+- quiet-prove_RCU-in-cgroups.patch: shut RCU lockdep up
+ as in 8b08ca52f5942c21564bbb90ccfb61053f2c26a1.
+
+* Tue Jun 01 2010 Kyle McMartin <kyle@redhat.com>
+- disable radeon_pm for now.
+
+* Mon May 31 2010 Kyle McMartin <kyle@redhat.com> 2.6.34-14
+- re-add drm-next.patch, should be in sync with 2.6.35 and what
+ was backported to Fedora 13.
+- drop patches merged in drm-next to 2.6.35
+- rebase relevant iwl fixes on top of 2.6.34 from the ones committed
+ to F-13 by linville.
+
+* Wed May 26 2010 Adam Jackson <ajax@redhat.com>
+- config-generic: Stop building i830.ko
+
+* Wed May 26 2010 Kyle McMartin <kyle@redhat.com>
+- iwlwifi-recover_from_tx_stall.patch: copy from F-13.
+
+* Fri May 21 2010 Roland McGrath <roland@redhat.com> 2.6.34-11
+- utrace update
+
+* Fri May 21 2010 Dave Jones <davej@redhat.com>
+- Update the SELinux mprotect patch with a newer version from Stephen
+
+* Fri May 21 2010 Roland McGrath <roland@redhat.com>
+- perf requires libdw now, not libdwarf
+
+* Fri May 21 2010 Kyle McMartin <kyle@redhat.com> 2.6.34-6
+- Fixups for virt_console from Amit Shah, thanks!
+
+* Thu May 20 2010 Kyle McMartin <kyle@redhat.com> 2.6.34-5
+- disable intel sdvo fixes until dependent code is backported.
+
+* Thu May 20 2010 Kyle McMartin <kyle@redhat.com> 2.6.34-4
+- resync a lot of stuff with F-13...
+- linux-2.6-acpi-video-export-edid.patch: rebase & copy from F-13
+- acpi-ec-add-delay-before-write.patch: copy from F-13
+- ... and a whole lot more that I can't be bothered typing.
+
+* Mon May 17 2010 Matthew Garrett <mjg@redhat.com>
+- thinkpad-acpi-fix-backlight.patch: Fix backlight support on some recent
+ Thinkpads
+
+* Sun May 16 2010 Kyle McMartin <kyle@redhat.com> 2.6.34-2
+- Disable strict copy_from_user checking until lirc is fixed.
+
+* Sun May 16 2010 Kyle McMartin <kyle@redhat.com> 2.6.34-1
+- Linux 2.6.34
+
+* Sun May 16 2010 Kyle McMartin <kyle@redhat.com>
+- Trimmed changelog, see CVS.
+
+###
+# The following Emacs magic makes C-c C-e use UTC dates.
+# Local Variables:
+# rpm-change-log-uses-utc: t
+# End:
+###
diff --git a/linux-2.6-32bit-mmap-exec-randomization.patch b/linux-2.6-32bit-mmap-exec-randomization.patch
new file mode 100644
index 000000000..fe91e0abf
--- /dev/null
+++ b/linux-2.6-32bit-mmap-exec-randomization.patch
@@ -0,0 +1,216 @@
+--- b/include/linux/sched.h
++++ b/include/linux/sched.h
+@@ -397,6 +397,10 @@
+ extern unsigned long
+ arch_get_unmapped_area(struct file *, unsigned long, unsigned long,
+ unsigned long, unsigned long);
++
++extern unsigned long
++arch_get_unmapped_exec_area(struct file *, unsigned long, unsigned long,
++ unsigned long, unsigned long);
+ extern unsigned long
+ arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr,
+ unsigned long len, unsigned long pgoff,
+--- b/mm/mmap.c
++++ b/mm/mmap.c
+@@ -28,6 +28,7 @@
+ #include <linux/rmap.h>
+ #include <linux/mmu_notifier.h>
+ #include <linux/perf_event.h>
++#include <linux/random.h>
+
+ #include <asm/uaccess.h>
+ #include <asm/cacheflush.h>
+@@ -1000,7 +1001,8 @@
+ /* Obtain the address to map to. we verify (or select) it and ensure
+ * that it represents a valid section of the address space.
+ */
+- addr = get_unmapped_area(file, addr, len, pgoff, flags);
++ addr = get_unmapped_area_prot(file, addr, len, pgoff, flags,
++ prot & PROT_EXEC);
+ if (addr & ~PAGE_MASK)
+ return addr;
+
+@@ -1552,8 +1554,8 @@
+ }
+
+ unsigned long
+-get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
+- unsigned long pgoff, unsigned long flags)
++get_unmapped_area_prot(struct file *file, unsigned long addr, unsigned long len,
++ unsigned long pgoff, unsigned long flags, int exec)
+ {
+ unsigned long (*get_area)(struct file *, unsigned long,
+ unsigned long, unsigned long, unsigned long);
+@@ -1566,7 +1568,11 @@
+ if (len > TASK_SIZE)
+ return -ENOMEM;
+
+- get_area = current->mm->get_unmapped_area;
++ if (exec && current->mm->get_unmapped_exec_area)
++ get_area = current->mm->get_unmapped_exec_area;
++ else
++ get_area = current->mm->get_unmapped_area;
++
+ if (file && file->f_op && file->f_op->get_unmapped_area)
+ get_area = file->f_op->get_unmapped_area;
+ addr = get_area(file, addr, len, pgoff, flags);
+@@ -1580,8 +1586,83 @@
+
+ return arch_rebalance_pgtables(addr, len);
+ }
++EXPORT_SYMBOL(get_unmapped_area_prot);
++
++static bool should_randomize(void)
++{
++ return (current->flags & PF_RANDOMIZE) &&
++ !(current->personality & ADDR_NO_RANDOMIZE);
++}
++
++#define SHLIB_BASE 0x00110000
++
++unsigned long
++arch_get_unmapped_exec_area(struct file *filp, unsigned long addr0,
++ unsigned long len0, unsigned long pgoff, unsigned long flags)
++{
++ unsigned long addr = addr0, len = len0;
++ struct mm_struct *mm = current->mm;
++ struct vm_area_struct *vma;
++ unsigned long tmp;
++
++ if (len > TASK_SIZE)
++ return -ENOMEM;
++
++ if (flags & MAP_FIXED)
++ return addr;
++
++ if (!addr)
++ addr = !should_randomize() ? SHLIB_BASE :
++ randomize_range(SHLIB_BASE, 0x01000000, len);
++
++ if (addr) {
++ addr = PAGE_ALIGN(addr);
++ vma = find_vma(mm, addr);
++ if (TASK_SIZE - len >= addr &&
++ (!vma || addr + len <= vma->vm_start))
++ return addr;
++ }
++
++ addr = SHLIB_BASE;
++ for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
++ /* At this point: (!vma || addr < vma->vm_end). */
++ if (TASK_SIZE - len < addr)
++ return -ENOMEM;
++
++ if (!vma || addr + len <= vma->vm_start) {
++ /*
++ * Must not let a PROT_EXEC mapping get into the
++ * brk area:
++ */
++ if (addr + len > mm->brk)
++ goto failed;
++
++ /*
++ * Up until the brk area we randomize addresses
++ * as much as possible:
++ */
++ if (addr >= 0x01000000 && should_randomize()) {
++ tmp = randomize_range(0x01000000,
++ PAGE_ALIGN(max(mm->start_brk,
++ (unsigned long)0x08000000)), len);
++ vma = find_vma(mm, tmp);
++ if (TASK_SIZE - len >= tmp &&
++ (!vma || tmp + len <= vma->vm_start))
++ return tmp;
++ }
++ /*
++ * Ok, randomization didnt work out - return
++ * the result of the linear search:
++ */
++ return addr;
++ }
++ addr = vma->vm_end;
++ }
++
++failed:
++ return current->mm->get_unmapped_area(filp, addr0, len0, pgoff, flags);
++}
+
+-EXPORT_SYMBOL(get_unmapped_area);
+
+ /* Look up the first VMA which satisfies addr < vm_end, NULL if none. */
+ struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr)
+--- a/arch/x86/mm/mmap.c
++++ b/arch/x86/mm/mmap.c
+@@ -124,13 +124,16 @@ static unsigned long mmap_legacy_base(void)
+ */
+ void arch_pick_mmap_layout(struct mm_struct *mm)
+ {
+ if (mmap_is_legacy()) {
+ mm->mmap_base = mmap_legacy_base();
+ mm->get_unmapped_area = arch_get_unmapped_area;
+ mm->unmap_area = arch_unmap_area;
+ } else {
+ mm->mmap_base = mmap_base();
+ mm->get_unmapped_area = arch_get_unmapped_area_topdown;
++ if (!(current->personality & READ_IMPLIES_EXEC)
++ && mmap_is_ia32())
++ mm->get_unmapped_exec_area = arch_get_unmapped_exec_area;
+ mm->unmap_area = arch_unmap_area_topdown;
+ }
+ }
+--- a/arch/x86/vdso/vdso32-setup.c
++++ b/arch/x86/vdso/vdso32-setup.c
+@@ -331,7 +331,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
+ if (compat)
+ addr = VDSO_HIGH_BASE;
+ else {
+- addr = get_unmapped_area(NULL, 0, PAGE_SIZE, 0, 0);
++ addr = get_unmapped_area_prot(NULL, 0, PAGE_SIZE, 0, 0, 1);
+ if (IS_ERR_VALUE(addr)) {
+ ret = addr;
+ goto up_fail;
+--- a/include/linux/mm.h
++++ b/include/linux/mm.h
+@@ -1263,7 +1263,13 @@ extern int install_special_mapping(struct mm_struct *mm,
+ unsigned long addr, unsigned long len,
+ unsigned long flags, struct page **pages);
+
+-extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
++extern unsigned long get_unmapped_area_prot(struct file *, unsigned long, unsigned long, unsigned long, unsigned long, int);
++
++static inline unsigned long get_unmapped_area(struct file *file, unsigned long addr,
++ unsigned long len, unsigned long pgoff, unsigned long flags)
++{
++ return get_unmapped_area_prot(file, addr, len, pgoff, flags, 0);
++}
+
+ extern unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
+ unsigned long len, unsigned long prot,
+--- a/include/linux/mm_types.h
++++ b/include/linux/mm_types.h
+@@ -227,6 +227,9 @@ struct mm_struct {
+ unsigned long (*get_unmapped_area) (struct file *filp,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags);
++ unsigned long (*get_unmapped_exec_area) (struct file *filp,
++ unsigned long addr, unsigned long len,
++ unsigned long pgoff, unsigned long flags);
+ void (*unmap_area) (struct mm_struct *mm, unsigned long addr);
+ #endif
+ unsigned long mmap_base; /* base of mmap area */
+--- a/mm/mremap.c
++++ b/mm/mremap.c
+@@ -487,10 +487,10 @@ unsigned long do_mremap(unsigned long addr,
+ if (vma->vm_flags & VM_MAYSHARE)
+ map_flags |= MAP_SHARED;
+
+- new_addr = get_unmapped_area(vma->vm_file, 0, new_len,
++ new_addr = get_unmapped_area_prot(vma->vm_file, 0, new_len,
+ vma->vm_pgoff +
+ ((addr - vma->vm_start) >> PAGE_SHIFT),
+- map_flags);
++ map_flags, vma->vm_flags & VM_EXEC);
+ if (new_addr & ~PAGE_MASK) {
+ ret = new_addr;
+ goto out;
diff --git a/linux-2.6-acpi-debug-infinite-loop.patch b/linux-2.6-acpi-debug-infinite-loop.patch
new file mode 100644
index 000000000..45cb05165
--- /dev/null
+++ b/linux-2.6-acpi-debug-infinite-loop.patch
@@ -0,0 +1,24 @@
+--- linux-2.6.34.noarch/drivers/acpi/acpica/dsopcode.c~ 2010-07-01 14:40:44.000000000 -0400
++++ linux-2.6.34.noarch/drivers/acpi/acpica/dsopcode.c 2010-07-01 14:48:56.000000000 -0400
+@@ -1276,6 +1276,10 @@ acpi_ds_exec_end_control_op(struct acpi_
+ * loop does not implement a timeout.
+ */
+ control_state->control.loop_count++;
++ if ((control_state->control.loop_count > 1) && (control_state->control.loop_count % 0xffff == 0))
++ printk("ACPI: While loop taking a really long time. loop_count=0x%x\n",
++ control_state->control.loop_count);
++
+ if (control_state->control.loop_count >
+ ACPI_MAX_LOOP_ITERATIONS) {
+ status = AE_AML_INFINITE_LOOP;
+--- linux-2.6.34.noarch/drivers/acpi/acpica/acconfig.h~ 2010-07-01 14:49:03.000000000 -0400
++++ linux-2.6.34.noarch/drivers/acpi/acpica/acconfig.h 2010-07-01 14:49:17.000000000 -0400
+@@ -117,7 +117,7 @@
+
+ /* Maximum number of While() loop iterations before forced abort */
+
+-#define ACPI_MAX_LOOP_ITERATIONS 0xFFFF
++#define ACPI_MAX_LOOP_ITERATIONS 0xFFFFFF
+
+ /* Maximum sleep allowed via Sleep() operator */
+
diff --git a/linux-2.6-acpi-video-dos.patch b/linux-2.6-acpi-video-dos.patch
new file mode 100644
index 000000000..3e2085193
--- /dev/null
+++ b/linux-2.6-acpi-video-dos.patch
@@ -0,0 +1,17 @@
+Disable firmware video brightness change on AC/Battery switch by default
+
+-- mjg59
+
+diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
+index bac2901..93b1a9e 100644
+--- a/drivers/acpi/video.c
++++ b/drivers/acpi/video.c
+@@ -1818,7 +1818,7 @@ static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
+
+ static int acpi_video_bus_start_devices(struct acpi_video_bus *video)
+ {
+- return acpi_video_bus_DOS(video, 0, 0);
++ return acpi_video_bus_DOS(video, 0, 1);
+ }
+
+ static int acpi_video_bus_stop_devices(struct acpi_video_bus *video)
diff --git a/linux-2.6-build-nonintconfig.patch b/linux-2.6-build-nonintconfig.patch
new file mode 100644
index 000000000..e88e0ea1e
--- /dev/null
+++ b/linux-2.6-build-nonintconfig.patch
@@ -0,0 +1,128 @@
+diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
+index 6d69c7c..ff84d12 100644
+--- a/scripts/kconfig/Makefile
++++ b/scripts/kconfig/Makefile
+@@ -58,6 +58,11 @@ localyesconfig: $(obj)/streamline_config.pl $(obj)/conf
+ fi
+ $(Q)rm -f .tmp.config
+
++nonint_oldconfig: $(obj)/conf
++ $< -b $(Kconfig)
++loose_nonint_oldconfig: $(obj)/conf
++ $< -B $(Kconfig)
++
+ # Create new linux.pot file
+ # Adjust charset to UTF-8 in .po file to accept UTF-8 in Kconfig files
+ # The symlink is used to repair a deficiency in arch/um
+diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
+index 9960d1c..ac8d455 100644
+--- a/scripts/kconfig/conf.c
++++ b/scripts/kconfig/conf.c
+@@ -23,6 +23,8 @@ enum {
+ ask_all,
+ ask_new,
+ ask_silent,
++ dont_ask,
++ dont_ask_dont_tell,
+ set_default,
+ set_yes,
+ set_mod,
+@@ -360,7 +362,10 @@ static void conf(struct menu *menu)
+
+ switch (prop->type) {
+ case P_MENU:
+- if (input_mode == ask_silent && rootEntry != menu) {
++ if ((input_mode == ask_silent ||
++ input_mode == dont_ask ||
++ input_mode == dont_ask_dont_tell) &&
++ rootEntry != menu) {
+ check_conf(menu);
+ return;
+ }
+@@ -406,6 +411,8 @@ conf_childs:
+ indent -= 2;
+ }
+
++static int return_value;
++
+ static void check_conf(struct menu *menu)
+ {
+ struct symbol *sym;
+@@ -418,12 +425,21 @@ static void check_conf(struct menu *menu)
+ if (sym && !sym_has_value(sym)) {
+ if (sym_is_changable(sym) ||
+ (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)) {
++ if (input_mode == dont_ask ||
++ input_mode == dont_ask_dont_tell) {
++ if (input_mode == dont_ask &&
++ sym->name && !sym_is_choice_value(sym)) {
++ fprintf(stderr,"CONFIG_%s\n",sym->name);
++ ++return_value;
++ }
++ } else {
+ if (!conf_cnt++)
+ printf(_("*\n* Restart config...\n*\n"));
+ rootEntry = menu_get_parent_menu(menu);
+ conf(rootEntry);
+ }
+ }
++ }
+
+ for (child = menu->list; child; child = child->next)
+ check_conf(child);
+@@ -439,7 +455,7 @@ int main(int ac, char **av)
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+- while ((opt = getopt(ac, av, "osdD:nmyrh")) != -1) {
++ while ((opt = getopt(ac, av, "osbBdD:nmyrh")) != -1) {
+ switch (opt) {
+ case 'o':
+ input_mode = ask_silent;
+@@ -448,6 +464,12 @@ int main(int ac, char **av)
+ input_mode = ask_silent;
+ sync_kconfig = 1;
+ break;
++ case 'b':
++ input_mode = dont_ask;
++ break;
++ case 'B':
++ input_mode = dont_ask_dont_tell;
++ break;
+ case 'd':
+ input_mode = set_default;
+ break;
+@@ -525,6 +547,8 @@ int main(int ac, char **av)
+ case ask_silent:
+ case ask_all:
+ case ask_new:
++ case dont_ask:
++ case dont_ask_dont_tell:
+ conf_read(NULL);
+ break;
+ case set_no:
+@@ -586,12 +610,16 @@ int main(int ac, char **av)
+ conf(&rootmenu);
+ input_mode = ask_silent;
+ /* fall through */
++ case dont_ask:
++ case dont_ask_dont_tell:
+ case ask_silent:
+ /* Update until a loop caused no more changes */
+ do {
+ conf_cnt = 0;
+ check_conf(&rootmenu);
+- } while (conf_cnt);
++ } while (conf_cnt &&
++ (input_mode != dont_ask &&
++ input_mode != dont_ask_dont_tell));
+ break;
+ }
+
+@@ -613,5 +641,5 @@ int main(int ac, char **av)
+ exit(1);
+ }
+ }
+- return 0;
++ return return_value;
+ }
diff --git a/linux-2.6-compile-fixes.patch b/linux-2.6-compile-fixes.patch
new file mode 100644
index 000000000..34c08ce47
--- /dev/null
+++ b/linux-2.6-compile-fixes.patch
@@ -0,0 +1,6 @@
+#
+# Small compile fixes (For more involved fixes, please use a separate patch).
+#
+# Please add the errors from gcc before the diffs to save others having
+# to do a compile to figure out what your diff is fixing. Thanks.
+#
diff --git a/linux-2.6-crash-driver.patch b/linux-2.6-crash-driver.patch
new file mode 100644
index 000000000..7b518bb88
--- /dev/null
+++ b/linux-2.6-crash-driver.patch
@@ -0,0 +1,385 @@
+From df42d15cd28f468ecd4c30465b98a53cce90617c Mon Sep 17 00:00:00 2001
+From: Kyle McMartin <kyle@phobos.i.jkkm.org>
+Date: Tue, 30 Mar 2010 00:16:25 -0400
+Subject: dev-crash-driver.patch
+
+---
+ arch/ia64/include/asm/crash.h | 90 +++++++++++++++++++++++++++++
+ arch/ia64/kernel/ia64_ksyms.c | 3 +
+ arch/x86/include/asm/crash.h | 75 ++++++++++++++++++++++++
+ arch/x86/mm/ioremap.c | 2 +
+ drivers/char/Kconfig | 2 +
+ drivers/char/Makefile | 2 +
+ drivers/char/crash.c | 128 +++++++++++++++++++++++++++++++++++++++++
+ 7 files changed, 302 insertions(+), 0 deletions(-)
+ create mode 100644 arch/ia64/include/asm/crash.h
+ create mode 100644 arch/x86/include/asm/crash.h
+ create mode 100644 drivers/char/crash.c
+
+diff --git a/arch/ia64/include/asm/crash.h b/arch/ia64/include/asm/crash.h
+new file mode 100644
+index 0000000..541af84
+--- /dev/null
++++ b/arch/ia64/include/asm/crash.h
+@@ -0,0 +1,90 @@
++#ifndef _ASM_IA64_CRASH_H
++#define _ASM_IA64_CRASH_H
++
++/*
++ * linux/include/asm-ia64/crash.h
++ *
++ * Copyright (c) 2004 Red Hat, 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 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; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ */
++
++#ifdef __KERNEL__
++
++#include <linux/efi.h>
++#include <linux/mm.h>
++#include <asm/mmzone.h>
++
++static inline void *
++map_virtual(u64 offset, struct page **pp)
++{
++ struct page *page;
++ unsigned long pfn;
++ u32 type;
++
++ if (REGION_NUMBER(offset) == 5) {
++ char byte;
++
++ if (__get_user(byte, (char *)offset) == 0)
++ return (void *)offset;
++ else
++ return NULL;
++ }
++
++ switch (type = efi_mem_type(offset))
++ {
++ case EFI_LOADER_CODE:
++ case EFI_LOADER_DATA:
++ case EFI_BOOT_SERVICES_CODE:
++ case EFI_BOOT_SERVICES_DATA:
++ case EFI_CONVENTIONAL_MEMORY:
++ break;
++
++ default:
++ printk(KERN_INFO
++ "crash memory driver: invalid memory type for %lx: %d\n",
++ offset, type);
++ return NULL;
++ }
++
++ pfn = offset >> PAGE_SHIFT;
++
++ if (!pfn_valid(pfn)) {
++ printk(KERN_INFO
++ "crash memory driver: invalid pfn: %lx )\n", pfn);
++ return NULL;
++ }
++
++ page = pfn_to_page(pfn);
++
++ if (!page->virtual) {
++ printk(KERN_INFO
++ "crash memory driver: offset: %lx page: %lx page->virtual: NULL\n",
++ offset, (unsigned long)page);
++ return NULL;
++ }
++
++ return (page->virtual + (offset & (PAGE_SIZE-1)));
++}
++
++static inline void unmap_virtual(struct page *page)
++{
++ return;
++}
++
++#endif /* __KERNEL__ */
++
++#endif /* _ASM_IA64_CRASH_H */
+diff --git a/arch/ia64/kernel/ia64_ksyms.c b/arch/ia64/kernel/ia64_ksyms.c
+index 7f4a0ed..552fe24 100644
+--- a/arch/ia64/kernel/ia64_ksyms.c
++++ b/arch/ia64/kernel/ia64_ksyms.c
+@@ -84,6 +84,9 @@ EXPORT_SYMBOL(ia64_save_scratch_fpregs);
+ #include <asm/unwind.h>
+ EXPORT_SYMBOL(unw_init_running);
+
++#include <linux/efi.h>
++EXPORT_SYMBOL_GPL(efi_mem_type);
++
+ #if defined(CONFIG_IA64_ESI) || defined(CONFIG_IA64_ESI_MODULE)
+ extern void esi_call_phys (void);
+ EXPORT_SYMBOL_GPL(esi_call_phys);
+diff --git a/arch/x86/include/asm/crash.h b/arch/x86/include/asm/crash.h
+new file mode 100644
+index 0000000..dfcc006
+--- /dev/null
++++ b/arch/x86/include/asm/crash.h
+@@ -0,0 +1,75 @@
++#ifndef _ASM_I386_CRASH_H
++#define _ASM_I386_CRASH_H
++
++/*
++ * linux/include/asm-i386/crash.h
++ *
++ * Copyright (c) 2004 Red Hat, 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 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; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ */
++
++#ifdef __KERNEL__
++
++#include <linux/mm.h>
++#include <linux/highmem.h>
++#include <asm/mmzone.h>
++
++extern int page_is_ram(unsigned long);
++
++static inline void *
++map_virtual(u64 offset, struct page **pp)
++{
++ struct page *page;
++ unsigned long pfn;
++ void *vaddr;
++
++ pfn = (unsigned long)(offset >> PAGE_SHIFT);
++
++ if (!page_is_ram(pfn)) {
++ printk(KERN_INFO
++ "crash memory driver: !page_is_ram(pfn: %lx)\n", pfn);
++ return NULL;
++ }
++
++ if (!pfn_valid(pfn)) {
++ printk(KERN_INFO
++ "crash memory driver: invalid pfn: %lx )\n", pfn);
++ return NULL;
++ }
++
++ page = pfn_to_page(pfn);
++
++ vaddr = kmap(page);
++ if (!vaddr) {
++ printk(KERN_INFO
++ "crash memory driver: pfn: %lx kmap(page: %lx) failed\n",
++ pfn, (unsigned long)page);
++ return NULL;
++ }
++
++ *pp = page;
++ return (vaddr + (offset & (PAGE_SIZE-1)));
++}
++
++static inline void unmap_virtual(struct page *page)
++{
++ kunmap(page);
++}
++
++#endif /* __KERNEL__ */
++
++#endif /* _ASM_I386_CRASH_H */
+diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c
+index 5eb1ba7..3e525d2 100644
+--- a/arch/x86/mm/ioremap.c
++++ b/arch/x86/mm/ioremap.c
+@@ -24,6 +24,8 @@
+
+ #include "physaddr.h"
+
++EXPORT_SYMBOL_GPL(page_is_ram);
++
+ /*
+ * Fix up the linear direct mapping of the kernel to avoid cache attribute
+ * conflicts.
+diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
+index 3141dd3..153658c 100644
+--- a/drivers/char/Kconfig
++++ b/drivers/char/Kconfig
+@@ -471,6 +471,8 @@ config LEGACY_PTYS
+ security. This option enables these legacy devices; on most
+ systems, it is safe to say N.
+
++config CRASH
++ tristate "Crash Utility memory driver"
+
+ config LEGACY_PTY_COUNT
+ int "Maximum number of legacy PTY in use"
+diff --git a/drivers/char/Makefile b/drivers/char/Makefile
+index f957edf..604c418 100644
+--- a/drivers/char/Makefile
++++ b/drivers/char/Makefile
+@@ -111,6 +111,8 @@ obj-$(CONFIG_PS3_FLASH) += ps3flash.o
+ obj-$(CONFIG_JS_RTC) += js-rtc.o
+ js-rtc-y = rtc.o
+
++obj-$(CONFIG_CRASH) += crash.o
++
+ # Files generated that shall be removed upon make clean
+ clean-files := consolemap_deftbl.c defkeymap.c
+
+diff --git a/drivers/char/crash.c b/drivers/char/crash.c
+new file mode 100644
+index 0000000..e5437de
+--- /dev/null
++++ b/drivers/char/crash.c
+@@ -0,0 +1,128 @@
++/*
++ * linux/drivers/char/crash.c
++ *
++ * Copyright (C) 2004 Dave Anderson <anderson@redhat.com>
++ * Copyright (C) 2004 Red Hat, Inc.
++ */
++
++/******************************************************************************
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2, 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/module.h>
++#include <linux/types.h>
++#include <linux/miscdevice.h>
++#include <linux/init.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include <asm/types.h>
++#include <asm/crash.h>
++
++#define CRASH_VERSION "1.0"
++
++/*
++ * These are the file operation functions that allow crash utility
++ * access to physical memory.
++ */
++
++static loff_t
++crash_llseek(struct file * file, loff_t offset, int orig)
++{
++ switch (orig) {
++ case 0:
++ file->f_pos = offset;
++ return file->f_pos;
++ case 1:
++ file->f_pos += offset;
++ return file->f_pos;
++ default:
++ return -EINVAL;
++ }
++}
++
++/*
++ * Determine the page address for an address offset value,
++ * get a virtual address for it, and copy it out.
++ * Accesses must fit within a page.
++ */
++static ssize_t
++crash_read(struct file *file, char *buf, size_t count, loff_t *poff)
++{
++ void *vaddr;
++ struct page *page;
++ u64 offset;
++ ssize_t read;
++
++ offset = *poff;
++ if (offset >> PAGE_SHIFT != (offset+count-1) >> PAGE_SHIFT)
++ return -EINVAL;
++
++ vaddr = map_virtual(offset, &page);
++ if (!vaddr)
++ return -EFAULT;
++
++ if (copy_to_user(buf, vaddr, count)) {
++ unmap_virtual(page);
++ return -EFAULT;
++ }
++ unmap_virtual(page);
++
++ read = count;
++ *poff += read;
++ return read;
++}
++
++static struct file_operations crash_fops = {
++ .owner = THIS_MODULE,
++ .llseek = crash_llseek,
++ .read = crash_read,
++};
++
++static struct miscdevice crash_dev = {
++ MISC_DYNAMIC_MINOR,
++ "crash",
++ &crash_fops
++};
++
++static int __init
++crash_init(void)
++{
++ int ret;
++
++ ret = misc_register(&crash_dev);
++ if (ret) {
++ printk(KERN_ERR
++ "crash memory driver: cannot misc_register (MISC_DYNAMIC_MINOR)\n");
++ goto out;
++ }
++
++ ret = 0;
++ printk(KERN_INFO "crash memory driver: version %s\n", CRASH_VERSION);
++out:
++ return ret;
++}
++
++static void __exit
++crash_cleanup_module(void)
++{
++ misc_deregister(&crash_dev);
++}
++
++module_init(crash_init);
++module_exit(crash_cleanup_module);
++
++MODULE_LICENSE("GPL");
+--
+1.7.0.1
+
diff --git a/linux-2.6-debug-always-inline-kzalloc.patch b/linux-2.6-debug-always-inline-kzalloc.patch
new file mode 100644
index 000000000..24f665ca6
--- /dev/null
+++ b/linux-2.6-debug-always-inline-kzalloc.patch
@@ -0,0 +1,25 @@
+From 76ec0e2e6d6edf81abc0331d5e7873ef7b2f6019 Mon Sep 17 00:00:00 2001
+From: Kyle McMartin <kyle@phobos.i.jkkm.org>
+Date: Wed, 8 Jul 2009 13:06:01 -0400
+Subject: [PATCH 6/6] fedora: linux-2.6-debug-always-inline-kzalloc.patch
+
+---
+ include/linux/slab.h | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/include/linux/slab.h b/include/linux/slab.h
+index 2da8372..d4ef74f 100644
+--- a/include/linux/slab.h
++++ b/include/linux/slab.h
+@@ -310,7 +310,7 @@ static inline void *kmem_cache_zalloc(struct kmem_cache *k, gfp_t flags)
+ * @size: how many bytes of memory are required.
+ * @flags: the type of memory to allocate (see kmalloc).
+ */
+-static inline void *kzalloc(size_t size, gfp_t flags)
++static __always_inline void *kzalloc(size_t size, gfp_t flags)
+ {
+ return kmalloc(size, flags | __GFP_ZERO);
+ }
+--
+1.6.2.5
+
diff --git a/linux-2.6-debug-nmi-timeout.patch b/linux-2.6-debug-nmi-timeout.patch
new file mode 100644
index 000000000..f54d26ae9
--- /dev/null
+++ b/linux-2.6-debug-nmi-timeout.patch
@@ -0,0 +1,45 @@
+From 542dee6f43067fa0101b53925aadf1d08c997cd4 Mon Sep 17 00:00:00 2001
+From: Kyle McMartin <kyle@phobos.i.jkkm.org>
+Date: Mon, 29 Mar 2010 23:40:27 -0400
+Subject: linux-2.6-debug-nmi-timeout
+
+---
+ arch/x86/kernel/apic/nmi.c | 2 +-
+ lib/Kconfig.debug | 8 ++++++++
+ 2 files changed, 9 insertions(+), 1 deletions(-)
+
+diff --git a/arch/x86/kernel/apic/nmi.c b/arch/x86/kernel/apic/nmi.c
+index 8aa65ad..ba7d55e 100644
+--- a/arch/x86/kernel/apic/nmi.c
++++ b/arch/x86/kernel/apic/nmi.c
+@@ -439,7 +439,7 @@ nmi_watchdog_tick(struct pt_regs *regs, unsigned reason)
+ * wait a few IRQs (5 seconds) before doing the oops ...
+ */
+ __this_cpu_inc(alert_counter);
+- if (__this_cpu_read(alert_counter) == 5 * nmi_hz)
++ if (__this_cpu_read(alert_counter) == CONFIG_DEBUG_NMI_TIMEOUT * nmi_hz)
+ /*
+ * die_nmi will return ONLY if NOTIFY_STOP happens..
+ */
+diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
+index 1fafb4b..963e78b 100644
+--- a/lib/Kconfig.debug
++++ b/lib/Kconfig.debug
+@@ -254,6 +254,14 @@ config SCHEDSTATS
+ application, you can say N to avoid the very slight overhead
+ this adds.
+
++config DEBUG_NMI_TIMEOUT
++ int "Number of seconds before NMI timeout"
++ depends on X86
++ default 5
++ help
++ This value is the number of seconds the NMI watchdog will tick
++ before it decides the machine has hung.
++
+ config TIMER_STATS
+ bool "Collect kernel timers statistics"
+ depends on DEBUG_KERNEL && PROC_FS
+--
+1.7.0.1
+
diff --git a/linux-2.6-debug-sizeof-structs.patch b/linux-2.6-debug-sizeof-structs.patch
new file mode 100644
index 000000000..cc7747d1f
--- /dev/null
+++ b/linux-2.6-debug-sizeof-structs.patch
@@ -0,0 +1,31 @@
+diff --git a/init/main.c b/init/main.c
+index 7449819..98cfaae 100644
+--- a/init/main.c
++++ b/init/main.c
+@@ -369,6 +369,10 @@ static void __init setup_nr_cpu_ids(void)
+ nr_cpu_ids = find_last_bit(cpumask_bits(cpu_possible_mask),NR_CPUS) + 1;
+ }
+
++#include <linux/ext3_fs_i.h>
++#include <linux/skbuff.h>
++#include <linux/sched.h>
++
+ /* Called by boot processor to activate the rest. */
+ static void __init smp_init(void)
+ {
+@@ -391,6 +395,15 @@ static void __init smp_init(void)
+ /* Any cleanup work */
+ printk(KERN_INFO "Brought up %ld CPUs\n", (long)num_online_cpus());
+ smp_cpus_done(setup_max_cpus);
++
++ printk(KERN_DEBUG "sizeof(vma)=%u bytes\n", (unsigned int) sizeof(struct vm_area_struct));
++ printk(KERN_DEBUG "sizeof(page)=%u bytes\n", (unsigned int) sizeof(struct page));
++ printk(KERN_DEBUG "sizeof(inode)=%u bytes\n", (unsigned int) sizeof(struct inode));
++ printk(KERN_DEBUG "sizeof(dentry)=%u bytes\n", (unsigned int) sizeof(struct dentry));
++ printk(KERN_DEBUG "sizeof(ext3inode)=%u bytes\n", (unsigned int) sizeof(struct ext3_inode_info));
++ printk(KERN_DEBUG "sizeof(buffer_head)=%u bytes\n", (unsigned int) sizeof(struct buffer_head));
++ printk(KERN_DEBUG "sizeof(skbuff)=%u bytes\n", (unsigned int) sizeof(struct sk_buff));
++ printk(KERN_DEBUG "sizeof(task_struct)=%u bytes\n", (unsigned int) sizeof(struct task_struct));
+ }
+
+ #endif
diff --git a/linux-2.6-debug-taint-vm.patch b/linux-2.6-debug-taint-vm.patch
new file mode 100644
index 000000000..ee367d45a
--- /dev/null
+++ b/linux-2.6-debug-taint-vm.patch
@@ -0,0 +1,65 @@
+From b04c57d9dc889462951312be2ac81ff6c702e954 Mon Sep 17 00:00:00 2001
+From: Kyle McMartin <kyle@phobos.i.jkkm.org>
+Date: Wed, 8 Jul 2009 13:05:09 -0400
+Subject: [PATCH 3/6] fedora: linux-2.6-debug-taint-vm.patch
+
+---
+ kernel/panic.c | 4 +++-
+ mm/slab.c | 8 ++++----
+ mm/slub.c | 2 +-
+ 4 files changed, 11 insertions(+), 8 deletions(-)
+
+diff --git a/kernel/panic.c b/kernel/panic.c
+index 984b3ec..6d1c3be 100644
+--- a/kernel/panic.c
++++ b/kernel/panic.c
+@@ -199,6 +199,7 @@ const char *print_tainted(void)
+
+ return buf;
+ }
++EXPORT_SYMBOL(print_tainted);
+
+ int test_taint(unsigned flag)
+ {
+diff --git a/mm/slab.c b/mm/slab.c
+index e74a16e..7bc287e 100644
+--- a/mm/slab.c
++++ b/mm/slab.c
+@@ -1803,8 +1803,8 @@ static void check_poison_obj(struct kmem_cache *cachep, void *objp)
+ /* Print header */
+ if (lines == 0) {
+ printk(KERN_ERR
+- "Slab corruption: %s start=%p, len=%d\n",
+- cachep->name, realobj, size);
++ "Slab corruption (%s): %s start=%p, len=%d\n",
++ print_tainted(), cachep->name, realobj, size);
+ print_objinfo(cachep, objp, 0);
+ }
+ /* Hexdump the affected line */
+@@ -2902,8 +2902,8 @@ static void check_slabp(struct kmem_cache *cachep, struct slab *slabp)
+ if (entries != cachep->num - slabp->inuse) {
+ bad:
+ printk(KERN_ERR "slab: Internal list corruption detected in "
+- "cache '%s'(%d), slabp %p(%d). Hexdump:\n",
+- cachep->name, cachep->num, slabp, slabp->inuse);
++ "cache '%s'(%d), slabp %p(%d). Tainted(%s). Hexdump:\n",
++ cachep->name, cachep->num, slabp, slabp->inuse, print_tainted());
+ for (i = 0;
+ i < sizeof(*slabp) + cachep->num * sizeof(kmem_bufctl_t);
+ i++) {
+diff --git a/mm/slub.c b/mm/slub.c
+index 819f056..8eff0f4 100644
+--- a/mm/slub.c
++++ b/mm/slub.c
+@@ -433,7 +433,7 @@ static void slab_bug(struct kmem_cache *s, char *fmt, ...)
+ va_end(args);
+ printk(KERN_ERR "========================================"
+ "=====================================\n");
+- printk(KERN_ERR "BUG %s: %s\n", s->name, buf);
++ printk(KERN_ERR "BUG %s (%s): %s\n", s->name, print_tainted(), buf);
+ printk(KERN_ERR "----------------------------------------"
+ "-------------------------------------\n\n");
+ }
+--
+1.6.2.5
+
diff --git a/linux-2.6-debug-vm-would-have-oomkilled.patch b/linux-2.6-debug-vm-would-have-oomkilled.patch
new file mode 100644
index 000000000..dd8ba3f0a
--- /dev/null
+++ b/linux-2.6-debug-vm-would-have-oomkilled.patch
@@ -0,0 +1,64 @@
+From 03657519851cd180983db4bd0c38eaeed4aa2962 Mon Sep 17 00:00:00 2001
+From: Kyle McMartin <kyle@treachery.i.jkkm.org>
+Date: Mon, 11 Jan 2010 08:25:12 -0500
+Subject: linux-2.6-debug-vm-would-have-oomkilled.patch
+
+---
+ kernel/sysctl.c | 8 ++++++++
+ mm/oom_kill.c | 7 +++++++
+ 2 files changed, 15 insertions(+), 0 deletions(-)
+
+diff --git a/kernel/sysctl.c b/kernel/sysctl.c
+index 8a68b24..72a4ff1 100644
+--- a/kernel/sysctl.c
++++ b/kernel/sysctl.c
+@@ -71,6 +71,7 @@ extern int sysctl_overcommit_ratio;
+ extern int sysctl_panic_on_oom;
+ extern int sysctl_oom_kill_allocating_task;
+ extern int sysctl_oom_dump_tasks;
++extern int sysctl_would_have_oomkilled;
+ extern int max_threads;
+ extern int core_uses_pid;
+ extern int suid_dumpable;
+@@ -973,6 +974,13 @@ static struct ctl_table vm_table[] = {
+ .proc_handler = proc_dointvec,
+ },
+ {
++ .procname = "would_have_oomkilled",
++ .data = &sysctl_would_have_oomkilled,
++ .maxlen = sizeof(sysctl_would_have_oomkilled),
++ .mode = 0644,
++ .proc_handler = &proc_dointvec,
++ },
++ {
+ .procname = "overcommit_ratio",
+ .data = &sysctl_overcommit_ratio,
+ .maxlen = sizeof(sysctl_overcommit_ratio),
+diff --git a/mm/oom_kill.c b/mm/oom_kill.c
+index f52481b..a892f07 100644
+--- a/mm/oom_kill.c
++++ b/mm/oom_kill.c
+@@ -31,6 +31,7 @@
+ int sysctl_panic_on_oom;
+ int sysctl_oom_kill_allocating_task;
+ int sysctl_oom_dump_tasks;
++int sysctl_would_have_oomkilled;
+ static DEFINE_SPINLOCK(zone_scan_lock);
+ /* #define DEBUG */
+
+@@ -396,6 +397,12 @@ static void __oom_kill_task(struct task_struct *p, int verbose)
+ return;
+ }
+
++ if (sysctl_would_have_oomkilled == 1) {
++ printk(KERN_ERR "Would have killed process %d (%s). But continuing instead.\n",
++ task_pid_nr(p), p->comm);
++ return;
++ }
++
+ if (verbose)
+ printk(KERN_ERR "Killed process %d (%s) "
+ "vsz:%lukB, anon-rss:%lukB, file-rss:%lukB\n",
+--
+1.6.5.2
+
diff --git a/linux-2.6-defaults-acpi-video.patch b/linux-2.6-defaults-acpi-video.patch
new file mode 100644
index 000000000..af883b0d3
--- /dev/null
+++ b/linux-2.6-defaults-acpi-video.patch
@@ -0,0 +1,13 @@
+diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
+index d8d7596..a1b7117 100644
+--- a/drivers/acpi/video.c
++++ b/drivers/acpi/video.c
+@@ -71,7 +71,7 @@ MODULE_AUTHOR("Bruno Ducrot");
+ MODULE_DESCRIPTION("ACPI Video Driver");
+ MODULE_LICENSE("GPL");
+
+-static int brightness_switch_enabled = 1;
++static int brightness_switch_enabled = 0;
+ module_param(brightness_switch_enabled, bool, 0644);
+
+ static int acpi_video_bus_add(struct acpi_device *device);
diff --git a/linux-2.6-defaults-aspm.patch b/linux-2.6-defaults-aspm.patch
new file mode 100644
index 000000000..49b832d2c
--- /dev/null
+++ b/linux-2.6-defaults-aspm.patch
@@ -0,0 +1,12 @@
+diff -up linux-2.6.30.noarch/drivers/pci/pcie/aspm.c.mjg linux-2.6.30.noarch/drivers/pci/pcie/aspm.c
+--- linux-2.6.30.noarch/drivers/pci/pcie/aspm.c.mjg 2009-07-16 22:01:11.000000000 +0100
++++ linux-2.6.30.noarch/drivers/pci/pcie/aspm.c 2009-07-16 22:01:30.000000000 +0100
+@@ -65,7 +65,7 @@ static LIST_HEAD(link_list);
+ #define POLICY_DEFAULT 0 /* BIOS default setting */
+ #define POLICY_PERFORMANCE 1 /* high performance */
+ #define POLICY_POWERSAVE 2 /* high power saving */
+-static int aspm_policy;
++static int aspm_policy = POLICY_POWERSAVE;
+ static const char *policy_str[] = {
+ [POLICY_DEFAULT] = "default",
+ [POLICY_PERFORMANCE] = "performance",
diff --git a/linux-2.6-defaults-pci_no_msi.patch b/linux-2.6-defaults-pci_no_msi.patch
new file mode 100644
index 000000000..29a84e8d2
--- /dev/null
+++ b/linux-2.6-defaults-pci_no_msi.patch
@@ -0,0 +1,110 @@
+From 14bdd0d36f5284108468bb73afd50726b07c7a84 Mon Sep 17 00:00:00 2001
+From: Kyle McMartin <kyle@phobos.i.jkkm.org>
+Date: Mon, 29 Mar 2010 23:43:49 -0400
+Subject: linux-2.6-defaults-pci_no_msi
+
+---
+ Documentation/kernel-parameters.txt | 3 +++
+ drivers/pci/Kconfig | 12 ++++++++++++
+ drivers/pci/msi.c | 9 +++++++++
+ drivers/pci/pci.c | 2 ++
+ drivers/pci/pci.h | 2 ++
+ 5 files changed, 28 insertions(+), 0 deletions(-)
+
+diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
+index e4cbca5..8154a0f 100644
+--- a/Documentation/kernel-parameters.txt
++++ b/Documentation/kernel-parameters.txt
+@@ -1911,6 +1911,9 @@ and is between 256 and 4096 characters. It is defined in the file
+ check_enable_amd_mmconf [X86] check for and enable
+ properly configured MMIO access to PCI
+ config space on AMD family 10h CPU
++ msi [MSI] If the PCI_MSI kernel config parameter is
++ enabled, this kernel boot option can be used to
++ enable the use of MSI interrupts system-wide.
+ nomsi [MSI] If the PCI_MSI kernel config parameter is
+ enabled, this kernel boot option can be used to
+ disable the use of MSI interrupts system-wide.
+diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
+index 7858a11..b12fcad 100644
+--- a/drivers/pci/Kconfig
++++ b/drivers/pci/Kconfig
+@@ -21,6 +21,18 @@ config PCI_MSI
+
+ If you don't know what to do here, say Y.
+
++config PCI_MSI_DEFAULT_ON
++ def_bool y
++ prompt "Use Message Signaled Interrupts by default"
++ depends on PCI_MSI
++ help
++ Selecting this option will enable use of PCI MSI where applicable
++ by default. Support for MSI can be disabled through the use of the
++ pci=nomsi boot flag. Conversely, if this option is not selected,
++ support for PCI MSI can be enabled by passing the pci=msi flag.
++
++ If you don't know what to do here, say N.
++
+ config PCI_DEBUG
+ bool "PCI Debugging"
+ depends on PCI && DEBUG_KERNEL
+diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
+index f9cf317..6b0539a 100644
+--- a/drivers/pci/msi.c
++++ b/drivers/pci/msi.c
+@@ -22,7 +22,11 @@
+ #include "pci.h"
+ #include "msi.h"
+
++#ifdef CONFIG_PCI_MSI_DEFAULT_ON
+ static int pci_msi_enable = 1;
++#else
++static int pci_msi_enable = 0;
++#endif /*CONFIG_PCI_MSI_DEFAULT_ON*/
+
+ /* Arch hooks */
+
+@@ -836,6 +840,11 @@ int pci_msi_enabled(void)
+ }
+ EXPORT_SYMBOL(pci_msi_enabled);
+
++void pci_yes_msi(void)
++{
++ pci_msi_enable = 1;
++}
++
+ void pci_msi_init_pci_dev(struct pci_dev *dev)
+ {
+ INIT_LIST_HEAD(&dev->msi_list);
+diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
+index 1531f3a..3cb332b 100644
+--- a/drivers/pci/pci.c
++++ b/drivers/pci/pci.c
+@@ -2983,6 +2983,8 @@ static int __init pci_setup(char *str)
+ if (*str && (str = pcibios_setup(str)) && *str) {
+ if (!strcmp(str, "nomsi")) {
+ pci_no_msi();
++ } else if (!strcmp(str, "msi")) {
++ pci_yes_msi();
+ } else if (!strcmp(str, "noaer")) {
+ pci_no_aer();
+ } else if (!strcmp(str, "nodomains")) {
+diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
+index 4eb10f4..caa051e 100644
+--- a/drivers/pci/pci.h
++++ b/drivers/pci/pci.h
+@@ -122,9 +122,11 @@ extern unsigned int pci_pm_d3_delay;
+
+ #ifdef CONFIG_PCI_MSI
+ void pci_no_msi(void);
++void pci_yes_msi(void);
+ extern void pci_msi_init_pci_dev(struct pci_dev *dev);
+ #else
+ static inline void pci_no_msi(void) { }
++static inline void pci_yes_msi(void) { }
+ static inline void pci_msi_init_pci_dev(struct pci_dev *dev) { }
+ #endif
+
+--
+1.7.0.1
+
diff --git a/linux-2.6-ext4-fix-freeze-deadlock.patch b/linux-2.6-ext4-fix-freeze-deadlock.patch
new file mode 100644
index 000000000..e7eb817af
--- /dev/null
+++ b/linux-2.6-ext4-fix-freeze-deadlock.patch
@@ -0,0 +1,46 @@
+[PATCH] ext4: fix freeze deadlock under IO
+
+Commit 6b0310fbf087ad6 caused a regression resulting in deadlocks
+when freezing a filesystem which had active IO; the vfs_check_frozen
+level (SB_FREEZE_WRITE) did not let the freeze-related IO syncing
+through. Duh.
+
+Changing the test to FREEZE_TRANS should let the normal freeze
+syncing get through the fs, but still block any transactions from
+starting once the fs is completely frozen.
+
+I tested this by running fsstress in the background while periodically
+snapshotting the fs and running fsck on the result. I ran into
+occasional deadlocks, but different ones. I think this is a
+fine fix for the problem at hand, and the other deadlocky things
+will need more investigation.
+
+Reported-by: Phillip Susi <psusi@cfl.rr.com>
+Signed-off-by: Eric Sandeen <sandeen@redhat.com>
+---
+
+diff --git a/fs/ext4/super.c b/fs/ext4/super.c
+index 4e8983a..a45ced9 100644
+--- a/fs/ext4/super.c
++++ b/fs/ext4/super.c
+@@ -241,7 +241,7 @@ handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks)
+ if (sb->s_flags & MS_RDONLY)
+ return ERR_PTR(-EROFS);
+
+- vfs_check_frozen(sb, SB_FREEZE_WRITE);
++ vfs_check_frozen(sb, SB_FREEZE_TRANS);
+ /* Special case here: if the journal has aborted behind our
+ * backs (eg. EIO in the commit thread), then we still need to
+ * take the FS itself readonly cleanly. */
+@@ -3491,7 +3491,7 @@ int ext4_force_commit(struct super_block *sb)
+
+ journal = EXT4_SB(sb)->s_journal;
+ if (journal) {
+- vfs_check_frozen(sb, SB_FREEZE_WRITE);
++ vfs_check_frozen(sb, SB_FREEZE_TRANS);
+ ret = ext4_journal_force_commit(journal);
+ }
+
+
+
+
diff --git a/linux-2.6-firewire-git-pending.patch b/linux-2.6-firewire-git-pending.patch
new file mode 100644
index 000000000..e05471f1b
--- /dev/null
+++ b/linux-2.6-firewire-git-pending.patch
@@ -0,0 +1,4 @@
+#
+# Patches under review and/or pending inclusion in the linux1394-git
+# tree (and/or in by the time your read this), which we want...
+#
diff --git a/linux-2.6-firewire-git-update.patch b/linux-2.6-firewire-git-update.patch
new file mode 100644
index 000000000..685808133
--- /dev/null
+++ b/linux-2.6-firewire-git-update.patch
@@ -0,0 +1,3682 @@
+linux1394-2.6.git tree vs. linus v2.6.29-rc3-git1 on 20090130 by jarod
+
+---
+ firewire-git/drivers/firewire/fw-card.c | 68 -
+ firewire-git/drivers/firewire/fw-cdev.c | 1014 +++++++++++++++++--------
+ firewire-git/drivers/firewire/fw-device.c | 43 -
+ firewire-git/drivers/firewire/fw-device.h | 7
+ firewire-git/drivers/firewire/fw-iso.c | 225 ++++-
+ firewire-git/drivers/firewire/fw-ohci.c | 236 ++---
+ firewire-git/drivers/firewire/fw-sbp2.c | 57 -
+ firewire-git/drivers/firewire/fw-topology.c | 28
+ firewire-git/drivers/firewire/fw-topology.h | 19
+ firewire-git/drivers/firewire/fw-transaction.c | 151 +--
+ firewire-git/drivers/firewire/fw-transaction.h | 125 ---
+ include/linux/firewire-cdev.h | 170 +++-
+ 12 files changed, 1359 insertions(+), 784 deletions(-)
+
+diff -Naurp linux-2.6-git/drivers/firewire/fw-card.c firewire-git/drivers/firewire/fw-card.c
+--- linux-2.6-git/drivers/firewire/fw-card.c 2009-01-30 13:39:02.989651512 -0500
++++ firewire-git/drivers/firewire/fw-card.c 2009-01-30 13:35:51.859771884 -0500
+@@ -63,8 +63,7 @@ static int descriptor_count;
+ #define BIB_CMC ((1) << 30)
+ #define BIB_IMC ((1) << 31)
+
+-static u32 *
+-generate_config_rom(struct fw_card *card, size_t *config_rom_length)
++static u32 *generate_config_rom(struct fw_card *card, size_t *config_rom_length)
+ {
+ struct fw_descriptor *desc;
+ static u32 config_rom[256];
+@@ -128,8 +127,7 @@ generate_config_rom(struct fw_card *card
+ return config_rom;
+ }
+
+-static void
+-update_config_roms(void)
++static void update_config_roms(void)
+ {
+ struct fw_card *card;
+ u32 *config_rom;
+@@ -141,8 +139,7 @@ update_config_roms(void)
+ }
+ }
+
+-int
+-fw_core_add_descriptor(struct fw_descriptor *desc)
++int fw_core_add_descriptor(struct fw_descriptor *desc)
+ {
+ size_t i;
+
+@@ -171,8 +168,7 @@ fw_core_add_descriptor(struct fw_descrip
+ return 0;
+ }
+
+-void
+-fw_core_remove_descriptor(struct fw_descriptor *desc)
++void fw_core_remove_descriptor(struct fw_descriptor *desc)
+ {
+ mutex_lock(&card_mutex);
+
+@@ -189,8 +185,7 @@ static const char gap_count_table[] = {
+ 63, 5, 7, 8, 10, 13, 16, 18, 21, 24, 26, 29, 32, 35, 37, 40
+ };
+
+-void
+-fw_schedule_bm_work(struct fw_card *card, unsigned long delay)
++void fw_schedule_bm_work(struct fw_card *card, unsigned long delay)
+ {
+ int scheduled;
+
+@@ -200,8 +195,7 @@ fw_schedule_bm_work(struct fw_card *card
+ fw_card_put(card);
+ }
+
+-static void
+-fw_card_bm_work(struct work_struct *work)
++static void fw_card_bm_work(struct work_struct *work)
+ {
+ struct fw_card *card = container_of(work, struct fw_card, work.work);
+ struct fw_device *root_device;
+@@ -371,17 +365,16 @@ fw_card_bm_work(struct work_struct *work
+ fw_card_put(card);
+ }
+
+-static void
+-flush_timer_callback(unsigned long data)
++static void flush_timer_callback(unsigned long data)
+ {
+ struct fw_card *card = (struct fw_card *)data;
+
+ fw_flush_transactions(card);
+ }
+
+-void
+-fw_card_initialize(struct fw_card *card, const struct fw_card_driver *driver,
+- struct device *device)
++void fw_card_initialize(struct fw_card *card,
++ const struct fw_card_driver *driver,
++ struct device *device)
+ {
+ static atomic_t index = ATOMIC_INIT(-1);
+
+@@ -406,9 +399,8 @@ fw_card_initialize(struct fw_card *card,
+ }
+ EXPORT_SYMBOL(fw_card_initialize);
+
+-int
+-fw_card_add(struct fw_card *card,
+- u32 max_receive, u32 link_speed, u64 guid)
++int fw_card_add(struct fw_card *card,
++ u32 max_receive, u32 link_speed, u64 guid)
+ {
+ u32 *config_rom;
+ size_t length;
+@@ -435,23 +427,20 @@ EXPORT_SYMBOL(fw_card_add);
+ * dummy driver just fails all IO.
+ */
+
+-static int
+-dummy_enable(struct fw_card *card, u32 *config_rom, size_t length)
++static int dummy_enable(struct fw_card *card, u32 *config_rom, size_t length)
+ {
+ BUG();
+ return -1;
+ }
+
+-static int
+-dummy_update_phy_reg(struct fw_card *card, int address,
+- int clear_bits, int set_bits)
++static int dummy_update_phy_reg(struct fw_card *card, int address,
++ int clear_bits, int set_bits)
+ {
+ return -ENODEV;
+ }
+
+-static int
+-dummy_set_config_rom(struct fw_card *card,
+- u32 *config_rom, size_t length)
++static int dummy_set_config_rom(struct fw_card *card,
++ u32 *config_rom, size_t length)
+ {
+ /*
+ * We take the card out of card_list before setting the dummy
+@@ -461,27 +450,23 @@ dummy_set_config_rom(struct fw_card *car
+ return -1;
+ }
+
+-static void
+-dummy_send_request(struct fw_card *card, struct fw_packet *packet)
++static void dummy_send_request(struct fw_card *card, struct fw_packet *packet)
+ {
+ packet->callback(packet, card, -ENODEV);
+ }
+
+-static void
+-dummy_send_response(struct fw_card *card, struct fw_packet *packet)
++static void dummy_send_response(struct fw_card *card, struct fw_packet *packet)
+ {
+ packet->callback(packet, card, -ENODEV);
+ }
+
+-static int
+-dummy_cancel_packet(struct fw_card *card, struct fw_packet *packet)
++static int dummy_cancel_packet(struct fw_card *card, struct fw_packet *packet)
+ {
+ return -ENOENT;
+ }
+
+-static int
+-dummy_enable_phys_dma(struct fw_card *card,
+- int node_id, int generation)
++static int dummy_enable_phys_dma(struct fw_card *card,
++ int node_id, int generation)
+ {
+ return -ENODEV;
+ }
+@@ -496,16 +481,14 @@ static struct fw_card_driver dummy_drive
+ .enable_phys_dma = dummy_enable_phys_dma,
+ };
+
+-void
+-fw_card_release(struct kref *kref)
++void fw_card_release(struct kref *kref)
+ {
+ struct fw_card *card = container_of(kref, struct fw_card, kref);
+
+ complete(&card->done);
+ }
+
+-void
+-fw_core_remove_card(struct fw_card *card)
++void fw_core_remove_card(struct fw_card *card)
+ {
+ card->driver->update_phy_reg(card, 4,
+ PHY_LINK_ACTIVE | PHY_CONTENDER, 0);
+@@ -529,8 +512,7 @@ fw_core_remove_card(struct fw_card *card
+ }
+ EXPORT_SYMBOL(fw_core_remove_card);
+
+-int
+-fw_core_initiate_bus_reset(struct fw_card *card, int short_reset)
++int fw_core_initiate_bus_reset(struct fw_card *card, int short_reset)
+ {
+ int reg = short_reset ? 5 : 1;
+ int bit = short_reset ? PHY_BUS_SHORT_RESET : PHY_BUS_RESET;
+diff -Naurp linux-2.6-git/drivers/firewire/fw-cdev.c firewire-git/drivers/firewire/fw-cdev.c
+--- linux-2.6-git/drivers/firewire/fw-cdev.c 2008-11-04 11:19:19.000000000 -0500
++++ firewire-git/drivers/firewire/fw-cdev.c 2009-01-30 13:35:51.860646788 -0500
+@@ -18,87 +18,162 @@
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+-#include <linux/module.h>
+-#include <linux/kernel.h>
+-#include <linux/wait.h>
+-#include <linux/errno.h>
++#include <linux/compat.h>
++#include <linux/delay.h>
+ #include <linux/device.h>
+-#include <linux/vmalloc.h>
++#include <linux/errno.h>
++#include <linux/firewire-cdev.h>
++#include <linux/idr.h>
++#include <linux/jiffies.h>
++#include <linux/kernel.h>
++#include <linux/kref.h>
++#include <linux/mm.h>
++#include <linux/module.h>
++#include <linux/mutex.h>
+ #include <linux/poll.h>
+ #include <linux/preempt.h>
++#include <linux/spinlock.h>
+ #include <linux/time.h>
+-#include <linux/delay.h>
+-#include <linux/mm.h>
+-#include <linux/idr.h>
+-#include <linux/compat.h>
+-#include <linux/firewire-cdev.h>
++#include <linux/vmalloc.h>
++#include <linux/wait.h>
++#include <linux/workqueue.h>
++
+ #include <asm/system.h>
+ #include <asm/uaccess.h>
+-#include "fw-transaction.h"
+-#include "fw-topology.h"
++
+ #include "fw-device.h"
++#include "fw-topology.h"
++#include "fw-transaction.h"
++
++struct client {
++ u32 version;
++ struct fw_device *device;
++
++ spinlock_t lock;
++ bool in_shutdown;
++ struct idr resource_idr;
++ struct list_head event_list;
++ wait_queue_head_t wait;
++ u64 bus_reset_closure;
++
++ struct fw_iso_context *iso_context;
++ u64 iso_closure;
++ struct fw_iso_buffer buffer;
++ unsigned long vm_start;
+
+-struct client;
+-struct client_resource {
+ struct list_head link;
+- void (*release)(struct client *client, struct client_resource *r);
+- u32 handle;
++ struct kref kref;
++};
++
++static inline void client_get(struct client *client)
++{
++ kref_get(&client->kref);
++}
++
++static void client_release(struct kref *kref)
++{
++ struct client *client = container_of(kref, struct client, kref);
++
++ fw_device_put(client->device);
++ kfree(client);
++}
++
++static void client_put(struct client *client)
++{
++ kref_put(&client->kref, client_release);
++}
++
++struct client_resource;
++typedef void (*client_resource_release_fn_t)(struct client *,
++ struct client_resource *);
++struct client_resource {
++ client_resource_release_fn_t release;
++ int handle;
++};
++
++struct address_handler_resource {
++ struct client_resource resource;
++ struct fw_address_handler handler;
++ __u64 closure;
++ struct client *client;
++};
++
++struct outbound_transaction_resource {
++ struct client_resource resource;
++ struct fw_transaction transaction;
++};
++
++struct inbound_transaction_resource {
++ struct client_resource resource;
++ struct fw_request *request;
++ void *data;
++ size_t length;
+ };
+
++struct descriptor_resource {
++ struct client_resource resource;
++ struct fw_descriptor descriptor;
++ u32 data[0];
++};
++
++struct iso_resource {
++ struct client_resource resource;
++ struct client *client;
++ /* Schedule work and access todo only with client->lock held. */
++ struct delayed_work work;
++ enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC,
++ ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo;
++ int generation;
++ u64 channels;
++ s32 bandwidth;
++ struct iso_resource_event *e_alloc, *e_dealloc;
++};
++
++static void schedule_iso_resource(struct iso_resource *);
++static void release_iso_resource(struct client *, struct client_resource *);
++
+ /*
+ * dequeue_event() just kfree()'s the event, so the event has to be
+- * the first field in the struct.
++ * the first field in a struct XYZ_event.
+ */
+-
+ struct event {
+ struct { void *data; size_t size; } v[2];
+ struct list_head link;
+ };
+
+-struct bus_reset {
++struct bus_reset_event {
+ struct event event;
+ struct fw_cdev_event_bus_reset reset;
+ };
+
+-struct response {
++struct outbound_transaction_event {
+ struct event event;
+- struct fw_transaction transaction;
+ struct client *client;
+- struct client_resource resource;
++ struct outbound_transaction_resource r;
+ struct fw_cdev_event_response response;
+ };
+
+-struct iso_interrupt {
++struct inbound_transaction_event {
+ struct event event;
+- struct fw_cdev_event_iso_interrupt interrupt;
++ struct fw_cdev_event_request request;
+ };
+
+-struct client {
+- u32 version;
+- struct fw_device *device;
+- spinlock_t lock;
+- u32 resource_handle;
+- struct list_head resource_list;
+- struct list_head event_list;
+- wait_queue_head_t wait;
+- u64 bus_reset_closure;
+-
+- struct fw_iso_context *iso_context;
+- u64 iso_closure;
+- struct fw_iso_buffer buffer;
+- unsigned long vm_start;
++struct iso_interrupt_event {
++ struct event event;
++ struct fw_cdev_event_iso_interrupt interrupt;
++};
+
+- struct list_head link;
++struct iso_resource_event {
++ struct event event;
++ struct fw_cdev_event_iso_resource resource;
+ };
+
+-static inline void __user *
+-u64_to_uptr(__u64 value)
++static inline void __user *u64_to_uptr(__u64 value)
+ {
+ return (void __user *)(unsigned long)value;
+ }
+
+-static inline __u64
+-uptr_to_u64(void __user *ptr)
++static inline __u64 uptr_to_u64(void __user *ptr)
+ {
+ return (__u64)(unsigned long)ptr;
+ }
+@@ -107,7 +182,6 @@ static int fw_device_op_open(struct inod
+ {
+ struct fw_device *device;
+ struct client *client;
+- unsigned long flags;
+
+ device = fw_device_get_by_devt(inode->i_rdev);
+ if (device == NULL)
+@@ -125,16 +199,17 @@ static int fw_device_op_open(struct inod
+ }
+
+ client->device = device;
+- INIT_LIST_HEAD(&client->event_list);
+- INIT_LIST_HEAD(&client->resource_list);
+ spin_lock_init(&client->lock);
++ idr_init(&client->resource_idr);
++ INIT_LIST_HEAD(&client->event_list);
+ init_waitqueue_head(&client->wait);
++ kref_init(&client->kref);
+
+ file->private_data = client;
+
+- spin_lock_irqsave(&device->card->lock, flags);
++ mutex_lock(&device->client_list_mutex);
+ list_add_tail(&client->link, &device->client_list);
+- spin_unlock_irqrestore(&device->card->lock, flags);
++ mutex_unlock(&device->client_list_mutex);
+
+ return 0;
+ }
+@@ -150,68 +225,69 @@ static void queue_event(struct client *c
+ event->v[1].size = size1;
+
+ spin_lock_irqsave(&client->lock, flags);
+- list_add_tail(&event->link, &client->event_list);
++ if (client->in_shutdown)
++ kfree(event);
++ else
++ list_add_tail(&event->link, &client->event_list);
+ spin_unlock_irqrestore(&client->lock, flags);
+
+ wake_up_interruptible(&client->wait);
+ }
+
+-static int
+-dequeue_event(struct client *client, char __user *buffer, size_t count)
++static int dequeue_event(struct client *client,
++ char __user *buffer, size_t count)
+ {
+- unsigned long flags;
+ struct event *event;
+ size_t size, total;
+- int i, retval;
++ int i, ret;
+
+- retval = wait_event_interruptible(client->wait,
+- !list_empty(&client->event_list) ||
+- fw_device_is_shutdown(client->device));
+- if (retval < 0)
+- return retval;
++ ret = wait_event_interruptible(client->wait,
++ !list_empty(&client->event_list) ||
++ fw_device_is_shutdown(client->device));
++ if (ret < 0)
++ return ret;
+
+ if (list_empty(&client->event_list) &&
+ fw_device_is_shutdown(client->device))
+ return -ENODEV;
+
+- spin_lock_irqsave(&client->lock, flags);
+- event = container_of(client->event_list.next, struct event, link);
++ spin_lock_irq(&client->lock);
++ event = list_first_entry(&client->event_list, struct event, link);
+ list_del(&event->link);
+- spin_unlock_irqrestore(&client->lock, flags);
++ spin_unlock_irq(&client->lock);
+
+ total = 0;
+ for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) {
+ size = min(event->v[i].size, count - total);
+ if (copy_to_user(buffer + total, event->v[i].data, size)) {
+- retval = -EFAULT;
++ ret = -EFAULT;
+ goto out;
+ }
+ total += size;
+ }
+- retval = total;
++ ret = total;
+
+ out:
+ kfree(event);
+
+- return retval;
++ return ret;
+ }
+
+-static ssize_t
+-fw_device_op_read(struct file *file,
+- char __user *buffer, size_t count, loff_t *offset)
++static ssize_t fw_device_op_read(struct file *file, char __user *buffer,
++ size_t count, loff_t *offset)
+ {
+ struct client *client = file->private_data;
+
+ return dequeue_event(client, buffer, count);
+ }
+
+-/* caller must hold card->lock so that node pointers can be dereferenced here */
+-static void
+-fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
+- struct client *client)
++static void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
++ struct client *client)
+ {
+ struct fw_card *card = client->device->card;
+
++ spin_lock_irq(&card->lock);
++
+ event->closure = client->bus_reset_closure;
+ event->type = FW_CDEV_EVENT_BUS_RESET;
+ event->generation = client->device->generation;
+@@ -220,39 +296,49 @@ fill_bus_reset_event(struct fw_cdev_even
+ event->bm_node_id = 0; /* FIXME: We don't track the BM. */
+ event->irm_node_id = card->irm_node->node_id;
+ event->root_node_id = card->root_node->node_id;
++
++ spin_unlock_irq(&card->lock);
+ }
+
+-static void
+-for_each_client(struct fw_device *device,
+- void (*callback)(struct client *client))
++static void for_each_client(struct fw_device *device,
++ void (*callback)(struct client *client))
+ {
+- struct fw_card *card = device->card;
+ struct client *c;
+- unsigned long flags;
+-
+- spin_lock_irqsave(&card->lock, flags);
+
++ mutex_lock(&device->client_list_mutex);
+ list_for_each_entry(c, &device->client_list, link)
+ callback(c);
++ mutex_unlock(&device->client_list_mutex);
++}
++
++static int schedule_reallocations(int id, void *p, void *data)
++{
++ struct client_resource *r = p;
+
+- spin_unlock_irqrestore(&card->lock, flags);
++ if (r->release == release_iso_resource)
++ schedule_iso_resource(container_of(r,
++ struct iso_resource, resource));
++ return 0;
+ }
+
+-static void
+-queue_bus_reset_event(struct client *client)
++static void queue_bus_reset_event(struct client *client)
+ {
+- struct bus_reset *bus_reset;
++ struct bus_reset_event *e;
+
+- bus_reset = kzalloc(sizeof(*bus_reset), GFP_ATOMIC);
+- if (bus_reset == NULL) {
++ e = kzalloc(sizeof(*e), GFP_KERNEL);
++ if (e == NULL) {
+ fw_notify("Out of memory when allocating bus reset event\n");
+ return;
+ }
+
+- fill_bus_reset_event(&bus_reset->reset, client);
++ fill_bus_reset_event(&e->reset, client);
++
++ queue_event(client, &e->event,
++ &e->reset, sizeof(e->reset), NULL, 0);
+
+- queue_event(client, &bus_reset->event,
+- &bus_reset->reset, sizeof(bus_reset->reset), NULL, 0);
++ spin_lock_irq(&client->lock);
++ idr_for_each(&client->resource_idr, schedule_reallocations, client);
++ spin_unlock_irq(&client->lock);
+ }
+
+ void fw_device_cdev_update(struct fw_device *device)
+@@ -274,11 +360,11 @@ static int ioctl_get_info(struct client
+ {
+ struct fw_cdev_get_info *get_info = buffer;
+ struct fw_cdev_event_bus_reset bus_reset;
+- struct fw_card *card = client->device->card;
+ unsigned long ret = 0;
+
+ client->version = get_info->version;
+ get_info->version = FW_CDEV_VERSION;
++ get_info->card = client->device->card->index;
+
+ down_read(&fw_device_rwsem);
+
+@@ -300,49 +386,61 @@ static int ioctl_get_info(struct client
+ client->bus_reset_closure = get_info->bus_reset_closure;
+ if (get_info->bus_reset != 0) {
+ void __user *uptr = u64_to_uptr(get_info->bus_reset);
+- unsigned long flags;
+
+- spin_lock_irqsave(&card->lock, flags);
+ fill_bus_reset_event(&bus_reset, client);
+- spin_unlock_irqrestore(&card->lock, flags);
+-
+ if (copy_to_user(uptr, &bus_reset, sizeof(bus_reset)))
+ return -EFAULT;
+ }
+
+- get_info->card = card->index;
+-
+ return 0;
+ }
+
+-static void
+-add_client_resource(struct client *client, struct client_resource *resource)
++static int add_client_resource(struct client *client,
++ struct client_resource *resource, gfp_t gfp_mask)
+ {
+ unsigned long flags;
++ int ret;
++
++ retry:
++ if (idr_pre_get(&client->resource_idr, gfp_mask | __GFP_ZERO) == 0)
++ return -ENOMEM;
+
+ spin_lock_irqsave(&client->lock, flags);
+- list_add_tail(&resource->link, &client->resource_list);
+- resource->handle = client->resource_handle++;
++ if (client->in_shutdown)
++ ret = -ECANCELED;
++ else
++ ret = idr_get_new(&client->resource_idr, resource,
++ &resource->handle);
++ if (ret >= 0) {
++ client_get(client);
++ if (resource->release == release_iso_resource)
++ schedule_iso_resource(container_of(resource,
++ struct iso_resource, resource));
++ }
+ spin_unlock_irqrestore(&client->lock, flags);
++
++ if (ret == -EAGAIN)
++ goto retry;
++
++ return ret < 0 ? ret : 0;
+ }
+
+-static int
+-release_client_resource(struct client *client, u32 handle,
+- struct client_resource **resource)
++static int release_client_resource(struct client *client, u32 handle,
++ client_resource_release_fn_t release,
++ struct client_resource **resource)
+ {
+ struct client_resource *r;
+- unsigned long flags;
+
+- spin_lock_irqsave(&client->lock, flags);
+- list_for_each_entry(r, &client->resource_list, link) {
+- if (r->handle == handle) {
+- list_del(&r->link);
+- break;
+- }
+- }
+- spin_unlock_irqrestore(&client->lock, flags);
++ spin_lock_irq(&client->lock);
++ if (client->in_shutdown)
++ r = NULL;
++ else
++ r = idr_find(&client->resource_idr, handle);
++ if (r && r->release == release)
++ idr_remove(&client->resource_idr, handle);
++ spin_unlock_irq(&client->lock);
+
+- if (&r->link == &client->resource_list)
++ if (!(r && r->release == release))
+ return -EINVAL;
+
+ if (resource)
+@@ -350,203 +448,242 @@ release_client_resource(struct client *c
+ else
+ r->release(client, r);
+
++ client_put(client);
++
+ return 0;
+ }
+
+-static void
+-release_transaction(struct client *client, struct client_resource *resource)
++static void release_transaction(struct client *client,
++ struct client_resource *resource)
+ {
+- struct response *response =
+- container_of(resource, struct response, resource);
++ struct outbound_transaction_resource *r = container_of(resource,
++ struct outbound_transaction_resource, resource);
+
+- fw_cancel_transaction(client->device->card, &response->transaction);
++ fw_cancel_transaction(client->device->card, &r->transaction);
+ }
+
+-static void
+-complete_transaction(struct fw_card *card, int rcode,
+- void *payload, size_t length, void *data)
++static void complete_transaction(struct fw_card *card, int rcode,
++ void *payload, size_t length, void *data)
+ {
+- struct response *response = data;
+- struct client *client = response->client;
++ struct outbound_transaction_event *e = data;
++ struct fw_cdev_event_response *rsp = &e->response;
++ struct client *client = e->client;
+ unsigned long flags;
+- struct fw_cdev_event_response *r = &response->response;
+
+- if (length < r->length)
+- r->length = length;
++ if (length < rsp->length)
++ rsp->length = length;
+ if (rcode == RCODE_COMPLETE)
+- memcpy(r->data, payload, r->length);
++ memcpy(rsp->data, payload, rsp->length);
+
+ spin_lock_irqsave(&client->lock, flags);
+- list_del(&response->resource.link);
++ /*
++ * 1. If called while in shutdown, the idr tree must be left untouched.
++ * The idr handle will be removed and the client reference will be
++ * dropped later.
++ * 2. If the call chain was release_client_resource ->
++ * release_transaction -> complete_transaction (instead of a normal
++ * conclusion of the transaction), i.e. if this resource was already
++ * unregistered from the idr, the client reference will be dropped
++ * by release_client_resource and we must not drop it here.
++ */
++ if (!client->in_shutdown &&
++ idr_find(&client->resource_idr, e->r.resource.handle)) {
++ idr_remove(&client->resource_idr, e->r.resource.handle);
++ /* Drop the idr's reference */
++ client_put(client);
++ }
+ spin_unlock_irqrestore(&client->lock, flags);
+
+- r->type = FW_CDEV_EVENT_RESPONSE;
+- r->rcode = rcode;
++ rsp->type = FW_CDEV_EVENT_RESPONSE;
++ rsp->rcode = rcode;
+
+ /*
+- * In the case that sizeof(*r) doesn't align with the position of the
++ * In the case that sizeof(*rsp) doesn't align with the position of the
+ * data, and the read is short, preserve an extra copy of the data
+ * to stay compatible with a pre-2.6.27 bug. Since the bug is harmless
+ * for short reads and some apps depended on it, this is both safe
+ * and prudent for compatibility.
+ */
+- if (r->length <= sizeof(*r) - offsetof(typeof(*r), data))
+- queue_event(client, &response->event, r, sizeof(*r),
+- r->data, r->length);
++ if (rsp->length <= sizeof(*rsp) - offsetof(typeof(*rsp), data))
++ queue_event(client, &e->event, rsp, sizeof(*rsp),
++ rsp->data, rsp->length);
+ else
+- queue_event(client, &response->event, r, sizeof(*r) + r->length,
++ queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length,
+ NULL, 0);
++
++ /* Drop the transaction callback's reference */
++ client_put(client);
+ }
+
+-static int ioctl_send_request(struct client *client, void *buffer)
++static int init_request(struct client *client,
++ struct fw_cdev_send_request *request,
++ int destination_id, int speed)
+ {
+- struct fw_device *device = client->device;
+- struct fw_cdev_send_request *request = buffer;
+- struct response *response;
++ struct outbound_transaction_event *e;
++ int ret;
+
+- /* What is the biggest size we'll accept, really? */
+- if (request->length > 4096)
+- return -EINVAL;
++ if (request->length > 4096 || request->length > 512 << speed)
++ return -EIO;
+
+- response = kmalloc(sizeof(*response) + request->length, GFP_KERNEL);
+- if (response == NULL)
++ e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL);
++ if (e == NULL)
+ return -ENOMEM;
+
+- response->client = client;
+- response->response.length = request->length;
+- response->response.closure = request->closure;
++ e->client = client;
++ e->response.length = request->length;
++ e->response.closure = request->closure;
+
+ if (request->data &&
+- copy_from_user(response->response.data,
++ copy_from_user(e->response.data,
+ u64_to_uptr(request->data), request->length)) {
+- kfree(response);
+- return -EFAULT;
++ ret = -EFAULT;
++ goto failed;
+ }
+
+- response->resource.release = release_transaction;
+- add_client_resource(client, &response->resource);
+-
+- fw_send_request(device->card, &response->transaction,
+- request->tcode & 0x1f,
+- device->node->node_id,
+- request->generation,
+- device->max_speed,
+- request->offset,
+- response->response.data, request->length,
+- complete_transaction, response);
++ e->r.resource.release = release_transaction;
++ ret = add_client_resource(client, &e->r.resource, GFP_KERNEL);
++ if (ret < 0)
++ goto failed;
++
++ /* Get a reference for the transaction callback */
++ client_get(client);
++
++ fw_send_request(client->device->card, &e->r.transaction,
++ request->tcode & 0x1f, destination_id,
++ request->generation, speed, request->offset,
++ e->response.data, request->length,
++ complete_transaction, e);
+
+ if (request->data)
+ return sizeof(request) + request->length;
+ else
+ return sizeof(request);
++ failed:
++ kfree(e);
++
++ return ret;
+ }
+
+-struct address_handler {
+- struct fw_address_handler handler;
+- __u64 closure;
+- struct client *client;
+- struct client_resource resource;
+-};
++static int ioctl_send_request(struct client *client, void *buffer)
++{
++ struct fw_cdev_send_request *request = buffer;
+
+-struct request {
+- struct fw_request *request;
+- void *data;
+- size_t length;
+- struct client_resource resource;
+-};
++ switch (request->tcode) {
++ case TCODE_WRITE_QUADLET_REQUEST:
++ case TCODE_WRITE_BLOCK_REQUEST:
++ case TCODE_READ_QUADLET_REQUEST:
++ case TCODE_READ_BLOCK_REQUEST:
++ case TCODE_LOCK_MASK_SWAP:
++ case TCODE_LOCK_COMPARE_SWAP:
++ case TCODE_LOCK_FETCH_ADD:
++ case TCODE_LOCK_LITTLE_ADD:
++ case TCODE_LOCK_BOUNDED_ADD:
++ case TCODE_LOCK_WRAP_ADD:
++ case TCODE_LOCK_VENDOR_DEPENDENT:
++ break;
++ default:
++ return -EINVAL;
++ }
+
+-struct request_event {
+- struct event event;
+- struct fw_cdev_event_request request;
+-};
++ return init_request(client, request, client->device->node->node_id,
++ client->device->max_speed);
++}
+
+-static void
+-release_request(struct client *client, struct client_resource *resource)
++static void release_request(struct client *client,
++ struct client_resource *resource)
+ {
+- struct request *request =
+- container_of(resource, struct request, resource);
++ struct inbound_transaction_resource *r = container_of(resource,
++ struct inbound_transaction_resource, resource);
+
+- fw_send_response(client->device->card, request->request,
++ fw_send_response(client->device->card, r->request,
+ RCODE_CONFLICT_ERROR);
+- kfree(request);
++ kfree(r);
+ }
+
+-static void
+-handle_request(struct fw_card *card, struct fw_request *r,
+- int tcode, int destination, int source,
+- int generation, int speed,
+- unsigned long long offset,
+- void *payload, size_t length, void *callback_data)
+-{
+- struct address_handler *handler = callback_data;
+- struct request *request;
+- struct request_event *e;
+- struct client *client = handler->client;
++static void handle_request(struct fw_card *card, struct fw_request *request,
++ int tcode, int destination, int source,
++ int generation, int speed,
++ unsigned long long offset,
++ void *payload, size_t length, void *callback_data)
++{
++ struct address_handler_resource *handler = callback_data;
++ struct inbound_transaction_resource *r;
++ struct inbound_transaction_event *e;
++ int ret;
+
+- request = kmalloc(sizeof(*request), GFP_ATOMIC);
++ r = kmalloc(sizeof(*r), GFP_ATOMIC);
+ e = kmalloc(sizeof(*e), GFP_ATOMIC);
+- if (request == NULL || e == NULL) {
+- kfree(request);
+- kfree(e);
+- fw_send_response(card, r, RCODE_CONFLICT_ERROR);
+- return;
+- }
+-
+- request->request = r;
+- request->data = payload;
+- request->length = length;
++ if (r == NULL || e == NULL)
++ goto failed;
+
+- request->resource.release = release_request;
+- add_client_resource(client, &request->resource);
++ r->request = request;
++ r->data = payload;
++ r->length = length;
++
++ r->resource.release = release_request;
++ ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC);
++ if (ret < 0)
++ goto failed;
+
+ e->request.type = FW_CDEV_EVENT_REQUEST;
+ e->request.tcode = tcode;
+ e->request.offset = offset;
+ e->request.length = length;
+- e->request.handle = request->resource.handle;
++ e->request.handle = r->resource.handle;
+ e->request.closure = handler->closure;
+
+- queue_event(client, &e->event,
++ queue_event(handler->client, &e->event,
+ &e->request, sizeof(e->request), payload, length);
++ return;
++
++ failed:
++ kfree(r);
++ kfree(e);
++ fw_send_response(card, request, RCODE_CONFLICT_ERROR);
+ }
+
+-static void
+-release_address_handler(struct client *client,
+- struct client_resource *resource)
++static void release_address_handler(struct client *client,
++ struct client_resource *resource)
+ {
+- struct address_handler *handler =
+- container_of(resource, struct address_handler, resource);
++ struct address_handler_resource *r =
++ container_of(resource, struct address_handler_resource, resource);
+
+- fw_core_remove_address_handler(&handler->handler);
+- kfree(handler);
++ fw_core_remove_address_handler(&r->handler);
++ kfree(r);
+ }
+
+ static int ioctl_allocate(struct client *client, void *buffer)
+ {
+ struct fw_cdev_allocate *request = buffer;
+- struct address_handler *handler;
++ struct address_handler_resource *r;
+ struct fw_address_region region;
++ int ret;
+
+- handler = kmalloc(sizeof(*handler), GFP_KERNEL);
+- if (handler == NULL)
++ r = kmalloc(sizeof(*r), GFP_KERNEL);
++ if (r == NULL)
+ return -ENOMEM;
+
+ region.start = request->offset;
+ region.end = request->offset + request->length;
+- handler->handler.length = request->length;
+- handler->handler.address_callback = handle_request;
+- handler->handler.callback_data = handler;
+- handler->closure = request->closure;
+- handler->client = client;
+-
+- if (fw_core_add_address_handler(&handler->handler, &region) < 0) {
+- kfree(handler);
+- return -EBUSY;
++ r->handler.length = request->length;
++ r->handler.address_callback = handle_request;
++ r->handler.callback_data = r;
++ r->closure = request->closure;
++ r->client = client;
++
++ ret = fw_core_add_address_handler(&r->handler, &region);
++ if (ret < 0) {
++ kfree(r);
++ return ret;
+ }
+
+- handler->resource.release = release_address_handler;
+- add_client_resource(client, &handler->resource);
+- request->handle = handler->resource.handle;
++ r->resource.release = release_address_handler;
++ ret = add_client_resource(client, &r->resource, GFP_KERNEL);
++ if (ret < 0) {
++ release_address_handler(client, &r->resource);
++ return ret;
++ }
++ request->handle = r->resource.handle;
+
+ return 0;
+ }
+@@ -555,18 +692,22 @@ static int ioctl_deallocate(struct clien
+ {
+ struct fw_cdev_deallocate *request = buffer;
+
+- return release_client_resource(client, request->handle, NULL);
++ return release_client_resource(client, request->handle,
++ release_address_handler, NULL);
+ }
+
+ static int ioctl_send_response(struct client *client, void *buffer)
+ {
+ struct fw_cdev_send_response *request = buffer;
+ struct client_resource *resource;
+- struct request *r;
++ struct inbound_transaction_resource *r;
+
+- if (release_client_resource(client, request->handle, &resource) < 0)
++ if (release_client_resource(client, request->handle,
++ release_request, &resource) < 0)
+ return -EINVAL;
+- r = container_of(resource, struct request, resource);
++
++ r = container_of(resource, struct inbound_transaction_resource,
++ resource);
+ if (request->length < r->length)
+ r->length = request->length;
+ if (copy_from_user(r->data, u64_to_uptr(request->data), r->length))
+@@ -588,85 +729,84 @@ static int ioctl_initiate_bus_reset(stru
+ return fw_core_initiate_bus_reset(client->device->card, short_reset);
+ }
+
+-struct descriptor {
+- struct fw_descriptor d;
+- struct client_resource resource;
+- u32 data[0];
+-};
+-
+ static void release_descriptor(struct client *client,
+ struct client_resource *resource)
+ {
+- struct descriptor *descriptor =
+- container_of(resource, struct descriptor, resource);
++ struct descriptor_resource *r =
++ container_of(resource, struct descriptor_resource, resource);
+
+- fw_core_remove_descriptor(&descriptor->d);
+- kfree(descriptor);
++ fw_core_remove_descriptor(&r->descriptor);
++ kfree(r);
+ }
+
+ static int ioctl_add_descriptor(struct client *client, void *buffer)
+ {
+ struct fw_cdev_add_descriptor *request = buffer;
+- struct descriptor *descriptor;
+- int retval;
++ struct descriptor_resource *r;
++ int ret;
+
+ if (request->length > 256)
+ return -EINVAL;
+
+- descriptor =
+- kmalloc(sizeof(*descriptor) + request->length * 4, GFP_KERNEL);
+- if (descriptor == NULL)
++ r = kmalloc(sizeof(*r) + request->length * 4, GFP_KERNEL);
++ if (r == NULL)
+ return -ENOMEM;
+
+- if (copy_from_user(descriptor->data,
++ if (copy_from_user(r->data,
+ u64_to_uptr(request->data), request->length * 4)) {
+- kfree(descriptor);
+- return -EFAULT;
++ ret = -EFAULT;
++ goto failed;
+ }
+
+- descriptor->d.length = request->length;
+- descriptor->d.immediate = request->immediate;
+- descriptor->d.key = request->key;
+- descriptor->d.data = descriptor->data;
+-
+- retval = fw_core_add_descriptor(&descriptor->d);
+- if (retval < 0) {
+- kfree(descriptor);
+- return retval;
++ r->descriptor.length = request->length;
++ r->descriptor.immediate = request->immediate;
++ r->descriptor.key = request->key;
++ r->descriptor.data = r->data;
++
++ ret = fw_core_add_descriptor(&r->descriptor);
++ if (ret < 0)
++ goto failed;
++
++ r->resource.release = release_descriptor;
++ ret = add_client_resource(client, &r->resource, GFP_KERNEL);
++ if (ret < 0) {
++ fw_core_remove_descriptor(&r->descriptor);
++ goto failed;
+ }
+-
+- descriptor->resource.release = release_descriptor;
+- add_client_resource(client, &descriptor->resource);
+- request->handle = descriptor->resource.handle;
++ request->handle = r->resource.handle;
+
+ return 0;
++ failed:
++ kfree(r);
++
++ return ret;
+ }
+
+ static int ioctl_remove_descriptor(struct client *client, void *buffer)
+ {
+ struct fw_cdev_remove_descriptor *request = buffer;
+
+- return release_client_resource(client, request->handle, NULL);
++ return release_client_resource(client, request->handle,
++ release_descriptor, NULL);
+ }
+
+-static void
+-iso_callback(struct fw_iso_context *context, u32 cycle,
+- size_t header_length, void *header, void *data)
++static void iso_callback(struct fw_iso_context *context, u32 cycle,
++ size_t header_length, void *header, void *data)
+ {
+ struct client *client = data;
+- struct iso_interrupt *irq;
++ struct iso_interrupt_event *e;
+
+- irq = kzalloc(sizeof(*irq) + header_length, GFP_ATOMIC);
+- if (irq == NULL)
++ e = kzalloc(sizeof(*e) + header_length, GFP_ATOMIC);
++ if (e == NULL)
+ return;
+
+- irq->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT;
+- irq->interrupt.closure = client->iso_closure;
+- irq->interrupt.cycle = cycle;
+- irq->interrupt.header_length = header_length;
+- memcpy(irq->interrupt.header, header, header_length);
+- queue_event(client, &irq->event, &irq->interrupt,
+- sizeof(irq->interrupt) + header_length, NULL, 0);
++ e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT;
++ e->interrupt.closure = client->iso_closure;
++ e->interrupt.cycle = cycle;
++ e->interrupt.header_length = header_length;
++ memcpy(e->interrupt.header, header, header_length);
++ queue_event(client, &e->event, &e->interrupt,
++ sizeof(e->interrupt) + header_length, NULL, 0);
+ }
+
+ static int ioctl_create_iso_context(struct client *client, void *buffer)
+@@ -871,6 +1011,237 @@ static int ioctl_get_cycle_timer(struct
+ return 0;
+ }
+
++static void iso_resource_work(struct work_struct *work)
++{
++ struct iso_resource_event *e;
++ struct iso_resource *r =
++ container_of(work, struct iso_resource, work.work);
++ struct client *client = r->client;
++ int generation, channel, bandwidth, todo;
++ bool skip, free, success;
++
++ spin_lock_irq(&client->lock);
++ generation = client->device->generation;
++ todo = r->todo;
++ /* Allow 1000ms grace period for other reallocations. */
++ if (todo == ISO_RES_ALLOC &&
++ time_is_after_jiffies(client->device->card->reset_jiffies + HZ)) {
++ if (schedule_delayed_work(&r->work, DIV_ROUND_UP(HZ, 3)))
++ client_get(client);
++ skip = true;
++ } else {
++ /* We could be called twice within the same generation. */
++ skip = todo == ISO_RES_REALLOC &&
++ r->generation == generation;
++ }
++ free = todo == ISO_RES_DEALLOC ||
++ todo == ISO_RES_ALLOC_ONCE ||
++ todo == ISO_RES_DEALLOC_ONCE;
++ r->generation = generation;
++ spin_unlock_irq(&client->lock);
++
++ if (skip)
++ goto out;
++
++ bandwidth = r->bandwidth;
++
++ fw_iso_resource_manage(client->device->card, generation,
++ r->channels, &channel, &bandwidth,
++ todo == ISO_RES_ALLOC ||
++ todo == ISO_RES_REALLOC ||
++ todo == ISO_RES_ALLOC_ONCE);
++ /*
++ * Is this generation outdated already? As long as this resource sticks
++ * in the idr, it will be scheduled again for a newer generation or at
++ * shutdown.
++ */
++ if (channel == -EAGAIN &&
++ (todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC))
++ goto out;
++
++ success = channel >= 0 || bandwidth > 0;
++
++ spin_lock_irq(&client->lock);
++ /*
++ * Transit from allocation to reallocation, except if the client
++ * requested deallocation in the meantime.
++ */
++ if (r->todo == ISO_RES_ALLOC)
++ r->todo = ISO_RES_REALLOC;
++ /*
++ * Allocation or reallocation failure? Pull this resource out of the
++ * idr and prepare for deletion, unless the client is shutting down.
++ */
++ if (r->todo == ISO_RES_REALLOC && !success &&
++ !client->in_shutdown &&
++ idr_find(&client->resource_idr, r->resource.handle)) {
++ idr_remove(&client->resource_idr, r->resource.handle);
++ client_put(client);
++ free = true;
++ }
++ spin_unlock_irq(&client->lock);
++
++ if (todo == ISO_RES_ALLOC && channel >= 0)
++ r->channels = 1ULL << channel;
++
++ if (todo == ISO_RES_REALLOC && success)
++ goto out;
++
++ if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) {
++ e = r->e_alloc;
++ r->e_alloc = NULL;
++ } else {
++ e = r->e_dealloc;
++ r->e_dealloc = NULL;
++ }
++ e->resource.handle = r->resource.handle;
++ e->resource.channel = channel;
++ e->resource.bandwidth = bandwidth;
++
++ queue_event(client, &e->event,
++ &e->resource, sizeof(e->resource), NULL, 0);
++
++ if (free) {
++ cancel_delayed_work(&r->work);
++ kfree(r->e_alloc);
++ kfree(r->e_dealloc);
++ kfree(r);
++ }
++ out:
++ client_put(client);
++}
++
++static void schedule_iso_resource(struct iso_resource *r)
++{
++ client_get(r->client);
++ if (!schedule_delayed_work(&r->work, 0))
++ client_put(r->client);
++}
++
++static void release_iso_resource(struct client *client,
++ struct client_resource *resource)
++{
++ struct iso_resource *r =
++ container_of(resource, struct iso_resource, resource);
++
++ spin_lock_irq(&client->lock);
++ r->todo = ISO_RES_DEALLOC;
++ schedule_iso_resource(r);
++ spin_unlock_irq(&client->lock);
++}
++
++static int init_iso_resource(struct client *client,
++ struct fw_cdev_allocate_iso_resource *request, int todo)
++{
++ struct iso_resource_event *e1, *e2;
++ struct iso_resource *r;
++ int ret;
++
++ if ((request->channels == 0 && request->bandwidth == 0) ||
++ request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL ||
++ request->bandwidth < 0)
++ return -EINVAL;
++
++ r = kmalloc(sizeof(*r), GFP_KERNEL);
++ e1 = kmalloc(sizeof(*e1), GFP_KERNEL);
++ e2 = kmalloc(sizeof(*e2), GFP_KERNEL);
++ if (r == NULL || e1 == NULL || e2 == NULL) {
++ ret = -ENOMEM;
++ goto fail;
++ }
++
++ INIT_DELAYED_WORK(&r->work, iso_resource_work);
++ r->client = client;
++ r->todo = todo;
++ r->generation = -1;
++ r->channels = request->channels;
++ r->bandwidth = request->bandwidth;
++ r->e_alloc = e1;
++ r->e_dealloc = e2;
++
++ e1->resource.closure = request->closure;
++ e1->resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED;
++ e2->resource.closure = request->closure;
++ e2->resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED;
++
++ if (todo == ISO_RES_ALLOC) {
++ r->resource.release = release_iso_resource;
++ ret = add_client_resource(client, &r->resource, GFP_KERNEL);
++ if (ret < 0)
++ goto fail;
++ } else {
++ r->resource.release = NULL;
++ r->resource.handle = -1;
++ schedule_iso_resource(r);
++ }
++ request->handle = r->resource.handle;
++
++ return 0;
++ fail:
++ kfree(r);
++ kfree(e1);
++ kfree(e2);
++
++ return ret;
++}
++
++static int ioctl_allocate_iso_resource(struct client *client, void *buffer)
++{
++ struct fw_cdev_allocate_iso_resource *request = buffer;
++
++ return init_iso_resource(client, request, ISO_RES_ALLOC);
++}
++
++static int ioctl_deallocate_iso_resource(struct client *client, void *buffer)
++{
++ struct fw_cdev_deallocate *request = buffer;
++
++ return release_client_resource(client, request->handle,
++ release_iso_resource, NULL);
++}
++
++static int ioctl_allocate_iso_resource_once(struct client *client, void *buffer)
++{
++ struct fw_cdev_allocate_iso_resource *request = buffer;
++
++ return init_iso_resource(client, request, ISO_RES_ALLOC_ONCE);
++}
++
++static int ioctl_deallocate_iso_resource_once(struct client *client, void *buffer)
++{
++ struct fw_cdev_allocate_iso_resource *request = buffer;
++
++ return init_iso_resource(client, request, ISO_RES_DEALLOC_ONCE);
++}
++
++static int ioctl_get_speed(struct client *client, void *buffer)
++{
++ struct fw_cdev_get_speed *request = buffer;
++
++ request->max_speed = client->device->max_speed;
++
++ return 0;
++}
++
++static int ioctl_send_broadcast_request(struct client *client, void *buffer)
++{
++ struct fw_cdev_send_request *request = buffer;
++
++ switch (request->tcode) {
++ case TCODE_WRITE_QUADLET_REQUEST:
++ case TCODE_WRITE_BLOCK_REQUEST:
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* Security policy: Only allow accesses to Units Space. */
++ if (request->offset < CSR_REGISTER_BASE + CSR_CONFIG_ROM_END)
++ return -EACCES;
++
++ return init_request(client, request, LOCAL_BUS | 0x3f, SCODE_100);
++}
++
+ static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
+ ioctl_get_info,
+ ioctl_send_request,
+@@ -885,13 +1256,19 @@ static int (* const ioctl_handlers[])(st
+ ioctl_start_iso,
+ ioctl_stop_iso,
+ ioctl_get_cycle_timer,
++ ioctl_allocate_iso_resource,
++ ioctl_deallocate_iso_resource,
++ ioctl_allocate_iso_resource_once,
++ ioctl_deallocate_iso_resource_once,
++ ioctl_get_speed,
++ ioctl_send_broadcast_request,
+ };
+
+-static int
+-dispatch_ioctl(struct client *client, unsigned int cmd, void __user *arg)
++static int dispatch_ioctl(struct client *client,
++ unsigned int cmd, void __user *arg)
+ {
+ char buffer[256];
+- int retval;
++ int ret;
+
+ if (_IOC_TYPE(cmd) != '#' ||
+ _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers))
+@@ -903,9 +1280,9 @@ dispatch_ioctl(struct client *client, un
+ return -EFAULT;
+ }
+
+- retval = ioctl_handlers[_IOC_NR(cmd)](client, buffer);
+- if (retval < 0)
+- return retval;
++ ret = ioctl_handlers[_IOC_NR(cmd)](client, buffer);
++ if (ret < 0)
++ return ret;
+
+ if (_IOC_DIR(cmd) & _IOC_READ) {
+ if (_IOC_SIZE(cmd) > sizeof(buffer) ||
+@@ -913,12 +1290,11 @@ dispatch_ioctl(struct client *client, un
+ return -EFAULT;
+ }
+
+- return retval;
++ return ret;
+ }
+
+-static long
+-fw_device_op_ioctl(struct file *file,
+- unsigned int cmd, unsigned long arg)
++static long fw_device_op_ioctl(struct file *file,
++ unsigned int cmd, unsigned long arg)
+ {
+ struct client *client = file->private_data;
+
+@@ -929,9 +1305,8 @@ fw_device_op_ioctl(struct file *file,
+ }
+
+ #ifdef CONFIG_COMPAT
+-static long
+-fw_device_op_compat_ioctl(struct file *file,
+- unsigned int cmd, unsigned long arg)
++static long fw_device_op_compat_ioctl(struct file *file,
++ unsigned int cmd, unsigned long arg)
+ {
+ struct client *client = file->private_data;
+
+@@ -947,7 +1322,7 @@ static int fw_device_op_mmap(struct file
+ struct client *client = file->private_data;
+ enum dma_data_direction direction;
+ unsigned long size;
+- int page_count, retval;
++ int page_count, ret;
+
+ if (fw_device_is_shutdown(client->device))
+ return -ENODEV;
+@@ -973,48 +1348,57 @@ static int fw_device_op_mmap(struct file
+ else
+ direction = DMA_FROM_DEVICE;
+
+- retval = fw_iso_buffer_init(&client->buffer, client->device->card,
+- page_count, direction);
+- if (retval < 0)
+- return retval;
++ ret = fw_iso_buffer_init(&client->buffer, client->device->card,
++ page_count, direction);
++ if (ret < 0)
++ return ret;
+
+- retval = fw_iso_buffer_map(&client->buffer, vma);
+- if (retval < 0)
++ ret = fw_iso_buffer_map(&client->buffer, vma);
++ if (ret < 0)
+ fw_iso_buffer_destroy(&client->buffer, client->device->card);
+
+- return retval;
++ return ret;
++}
++
++static int shutdown_resource(int id, void *p, void *data)
++{
++ struct client_resource *r = p;
++ struct client *client = data;
++
++ r->release(client, r);
++ client_put(client);
++
++ return 0;
+ }
+
+ static int fw_device_op_release(struct inode *inode, struct file *file)
+ {
+ struct client *client = file->private_data;
+ struct event *e, *next_e;
+- struct client_resource *r, *next_r;
+- unsigned long flags;
+
+- if (client->buffer.pages)
+- fw_iso_buffer_destroy(&client->buffer, client->device->card);
++ mutex_lock(&client->device->client_list_mutex);
++ list_del(&client->link);
++ mutex_unlock(&client->device->client_list_mutex);
+
+ if (client->iso_context)
+ fw_iso_context_destroy(client->iso_context);
+
+- list_for_each_entry_safe(r, next_r, &client->resource_list, link)
+- r->release(client, r);
++ if (client->buffer.pages)
++ fw_iso_buffer_destroy(&client->buffer, client->device->card);
+
+- /*
+- * FIXME: We should wait for the async tasklets to stop
+- * running before freeing the memory.
+- */
++ /* Freeze client->resource_idr and client->event_list */
++ spin_lock_irq(&client->lock);
++ client->in_shutdown = true;
++ spin_unlock_irq(&client->lock);
++
++ idr_for_each(&client->resource_idr, shutdown_resource, client);
++ idr_remove_all(&client->resource_idr);
++ idr_destroy(&client->resource_idr);
+
+ list_for_each_entry_safe(e, next_e, &client->event_list, link)
+ kfree(e);
+
+- spin_lock_irqsave(&client->device->card->lock, flags);
+- list_del(&client->link);
+- spin_unlock_irqrestore(&client->device->card->lock, flags);
+-
+- fw_device_put(client->device);
+- kfree(client);
++ client_put(client);
+
+ return 0;
+ }
+diff -Naurp linux-2.6-git/drivers/firewire/fw-device.c firewire-git/drivers/firewire/fw-device.c
+--- linux-2.6-git/drivers/firewire/fw-device.c 2009-01-30 13:39:02.989651512 -0500
++++ firewire-git/drivers/firewire/fw-device.c 2009-01-30 13:35:51.860646788 -0500
+@@ -27,8 +27,10 @@
+ #include <linux/idr.h>
+ #include <linux/jiffies.h>
+ #include <linux/string.h>
++#include <linux/mutex.h>
+ #include <linux/rwsem.h>
+ #include <linux/semaphore.h>
++#include <linux/spinlock.h>
+ #include <asm/system.h>
+ #include <linux/ctype.h>
+ #include "fw-transaction.h"
+@@ -132,8 +134,7 @@ static int get_modalias(struct fw_unit *
+ vendor, model, specifier_id, version);
+ }
+
+-static int
+-fw_unit_uevent(struct device *dev, struct kobj_uevent_env *env)
++static int fw_unit_uevent(struct device *dev, struct kobj_uevent_env *env)
+ {
+ struct fw_unit *unit = fw_unit(dev);
+ char modalias[64];
+@@ -191,8 +192,8 @@ struct config_rom_attribute {
+ u32 key;
+ };
+
+-static ssize_t
+-show_immediate(struct device *dev, struct device_attribute *dattr, char *buf)
++static ssize_t show_immediate(struct device *dev,
++ struct device_attribute *dattr, char *buf)
+ {
+ struct config_rom_attribute *attr =
+ container_of(dattr, struct config_rom_attribute, attr);
+@@ -223,8 +224,8 @@ show_immediate(struct device *dev, struc
+ #define IMMEDIATE_ATTR(name, key) \
+ { __ATTR(name, S_IRUGO, show_immediate, NULL), key }
+
+-static ssize_t
+-show_text_leaf(struct device *dev, struct device_attribute *dattr, char *buf)
++static ssize_t show_text_leaf(struct device *dev,
++ struct device_attribute *dattr, char *buf)
+ {
+ struct config_rom_attribute *attr =
+ container_of(dattr, struct config_rom_attribute, attr);
+@@ -293,10 +294,9 @@ static struct config_rom_attribute confi
+ TEXT_LEAF_ATTR(hardware_version_name, CSR_HARDWARE_VERSION),
+ };
+
+-static void
+-init_fw_attribute_group(struct device *dev,
+- struct device_attribute *attrs,
+- struct fw_attribute_group *group)
++static void init_fw_attribute_group(struct device *dev,
++ struct device_attribute *attrs,
++ struct fw_attribute_group *group)
+ {
+ struct device_attribute *attr;
+ int i, j;
+@@ -319,9 +319,8 @@ init_fw_attribute_group(struct device *d
+ dev->groups = group->groups;
+ }
+
+-static ssize_t
+-modalias_show(struct device *dev,
+- struct device_attribute *attr, char *buf)
++static ssize_t modalias_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
+ {
+ struct fw_unit *unit = fw_unit(dev);
+ int length;
+@@ -332,9 +331,8 @@ modalias_show(struct device *dev,
+ return length + 1;
+ }
+
+-static ssize_t
+-rom_index_show(struct device *dev,
+- struct device_attribute *attr, char *buf)
++static ssize_t rom_index_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
+ {
+ struct fw_device *device = fw_device(dev->parent);
+ struct fw_unit *unit = fw_unit(dev);
+@@ -349,8 +347,8 @@ static struct device_attribute fw_unit_a
+ __ATTR_NULL,
+ };
+
+-static ssize_t
+-config_rom_show(struct device *dev, struct device_attribute *attr, char *buf)
++static ssize_t config_rom_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
+ {
+ struct fw_device *device = fw_device(dev);
+ size_t length;
+@@ -363,8 +361,8 @@ config_rom_show(struct device *dev, stru
+ return length;
+ }
+
+-static ssize_t
+-guid_show(struct device *dev, struct device_attribute *attr, char *buf)
++static ssize_t guid_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
+ {
+ struct fw_device *device = fw_device(dev);
+ int ret;
+@@ -383,8 +381,8 @@ static struct device_attribute fw_device
+ __ATTR_NULL,
+ };
+
+-static int
+-read_rom(struct fw_device *device, int generation, int index, u32 *data)
++static int read_rom(struct fw_device *device,
++ int generation, int index, u32 *data)
+ {
+ int rcode;
+
+@@ -1004,6 +1002,7 @@ void fw_node_event(struct fw_card *card,
+ device->node = fw_node_get(node);
+ device->node_id = node->node_id;
+ device->generation = card->generation;
++ mutex_init(&device->client_list_mutex);
+ INIT_LIST_HEAD(&device->client_list);
+
+ /*
+diff -Naurp linux-2.6-git/drivers/firewire/fw-device.h firewire-git/drivers/firewire/fw-device.h
+--- linux-2.6-git/drivers/firewire/fw-device.h 2009-01-30 13:39:02.989651512 -0500
++++ firewire-git/drivers/firewire/fw-device.h 2009-01-30 13:35:51.860646788 -0500
+@@ -23,6 +23,7 @@
+ #include <linux/cdev.h>
+ #include <linux/idr.h>
+ #include <linux/rwsem.h>
++#include <linux/mutex.h>
+ #include <asm/atomic.h>
+
+ enum fw_device_state {
+@@ -64,7 +65,10 @@ struct fw_device {
+ bool cmc;
+ struct fw_card *card;
+ struct device device;
++
++ struct mutex client_list_mutex;
+ struct list_head client_list;
++
+ u32 *config_rom;
+ size_t config_rom_length;
+ int config_rom_retries;
+@@ -176,8 +180,7 @@ struct fw_driver {
+ const struct fw_device_id *id_table;
+ };
+
+-static inline struct fw_driver *
+-fw_driver(struct device_driver *drv)
++static inline struct fw_driver *fw_driver(struct device_driver *drv)
+ {
+ return container_of(drv, struct fw_driver, driver);
+ }
+diff -Naurp linux-2.6-git/drivers/firewire/fw-iso.c firewire-git/drivers/firewire/fw-iso.c
+--- linux-2.6-git/drivers/firewire/fw-iso.c 2008-11-04 11:18:33.000000000 -0500
++++ firewire-git/drivers/firewire/fw-iso.c 2009-01-30 13:35:51.860646788 -0500
+@@ -1,5 +1,7 @@
+ /*
+- * Isochronous IO functionality
++ * Isochronous I/O functionality:
++ * - Isochronous DMA context management
++ * - Isochronous bus resource management (channels, bandwidth), client side
+ *
+ * Copyright (C) 2006 Kristian Hoegsberg <krh@bitplanet.net>
+ *
+@@ -18,21 +20,25 @@
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+-#include <linux/kernel.h>
+-#include <linux/module.h>
+ #include <linux/dma-mapping.h>
+-#include <linux/vmalloc.h>
++#include <linux/errno.h>
++#include <linux/firewire-constants.h>
++#include <linux/kernel.h>
+ #include <linux/mm.h>
++#include <linux/spinlock.h>
++#include <linux/vmalloc.h>
+
+-#include "fw-transaction.h"
+ #include "fw-topology.h"
+-#include "fw-device.h"
++#include "fw-transaction.h"
+
+-int
+-fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card,
+- int page_count, enum dma_data_direction direction)
++/*
++ * Isochronous DMA context management
++ */
++
++int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card,
++ int page_count, enum dma_data_direction direction)
+ {
+- int i, j, retval = -ENOMEM;
++ int i, j;
+ dma_addr_t address;
+
+ buffer->page_count = page_count;
+@@ -69,19 +75,19 @@ fw_iso_buffer_init(struct fw_iso_buffer
+ kfree(buffer->pages);
+ out:
+ buffer->pages = NULL;
+- return retval;
++ return -ENOMEM;
+ }
+
+ int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma)
+ {
+ unsigned long uaddr;
+- int i, retval;
++ int i, ret;
+
+ uaddr = vma->vm_start;
+ for (i = 0; i < buffer->page_count; i++) {
+- retval = vm_insert_page(vma, uaddr, buffer->pages[i]);
+- if (retval)
+- return retval;
++ ret = vm_insert_page(vma, uaddr, buffer->pages[i]);
++ if (ret)
++ return ret;
+ uaddr += PAGE_SIZE;
+ }
+
+@@ -105,14 +111,14 @@ void fw_iso_buffer_destroy(struct fw_iso
+ buffer->pages = NULL;
+ }
+
+-struct fw_iso_context *
+-fw_iso_context_create(struct fw_card *card, int type,
+- int channel, int speed, size_t header_size,
+- fw_iso_callback_t callback, void *callback_data)
++struct fw_iso_context *fw_iso_context_create(struct fw_card *card,
++ int type, int channel, int speed, size_t header_size,
++ fw_iso_callback_t callback, void *callback_data)
+ {
+ struct fw_iso_context *ctx;
+
+- ctx = card->driver->allocate_iso_context(card, type, header_size);
++ ctx = card->driver->allocate_iso_context(card,
++ type, channel, header_size);
+ if (IS_ERR(ctx))
+ return ctx;
+
+@@ -134,25 +140,186 @@ void fw_iso_context_destroy(struct fw_is
+ card->driver->free_iso_context(ctx);
+ }
+
+-int
+-fw_iso_context_start(struct fw_iso_context *ctx, int cycle, int sync, int tags)
++int fw_iso_context_start(struct fw_iso_context *ctx,
++ int cycle, int sync, int tags)
+ {
+ return ctx->card->driver->start_iso(ctx, cycle, sync, tags);
+ }
+
+-int
+-fw_iso_context_queue(struct fw_iso_context *ctx,
+- struct fw_iso_packet *packet,
+- struct fw_iso_buffer *buffer,
+- unsigned long payload)
++int fw_iso_context_queue(struct fw_iso_context *ctx,
++ struct fw_iso_packet *packet,
++ struct fw_iso_buffer *buffer,
++ unsigned long payload)
+ {
+ struct fw_card *card = ctx->card;
+
+ return card->driver->queue_iso(ctx, packet, buffer, payload);
+ }
+
+-int
+-fw_iso_context_stop(struct fw_iso_context *ctx)
++int fw_iso_context_stop(struct fw_iso_context *ctx)
+ {
+ return ctx->card->driver->stop_iso(ctx);
+ }
++
++/*
++ * Isochronous bus resource management (channels, bandwidth), client side
++ */
++
++static int manage_bandwidth(struct fw_card *card, int irm_id, int generation,
++ int bandwidth, bool allocate)
++{
++ __be32 data[2];
++ int try, new, old = allocate ? BANDWIDTH_AVAILABLE_INITIAL : 0;
++
++ /*
++ * On a 1394a IRM with low contention, try < 1 is enough.
++ * On a 1394-1995 IRM, we need at least try < 2.
++ * Let's just do try < 5.
++ */
++ for (try = 0; try < 5; try++) {
++ new = allocate ? old - bandwidth : old + bandwidth;
++ if (new < 0 || new > BANDWIDTH_AVAILABLE_INITIAL)
++ break;
++
++ data[0] = cpu_to_be32(old);
++ data[1] = cpu_to_be32(new);
++ switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP,
++ irm_id, generation, SCODE_100,
++ CSR_REGISTER_BASE + CSR_BANDWIDTH_AVAILABLE,
++ data, sizeof(data))) {
++ case RCODE_GENERATION:
++ /* A generation change frees all bandwidth. */
++ return allocate ? -EAGAIN : bandwidth;
++
++ case RCODE_COMPLETE:
++ if (be32_to_cpup(data) == old)
++ return bandwidth;
++
++ old = be32_to_cpup(data);
++ /* Fall through. */
++ }
++ }
++
++ return -EIO;
++}
++
++static int manage_channel(struct fw_card *card, int irm_id, int generation,
++ u32 channels_mask, u64 offset, bool allocate)
++{
++ __be32 data[2], c, all, old;
++ int i, retry = 5;
++
++ old = all = allocate ? cpu_to_be32(~0) : 0;
++
++ for (i = 0; i < 32; i++) {
++ if (!(channels_mask & 1 << i))
++ continue;
++
++ c = cpu_to_be32(1 << (31 - i));
++ if ((old & c) != (all & c))
++ continue;
++
++ data[0] = old;
++ data[1] = old ^ c;
++ switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP,
++ irm_id, generation, SCODE_100,
++ offset, data, sizeof(data))) {
++ case RCODE_GENERATION:
++ /* A generation change frees all channels. */
++ return allocate ? -EAGAIN : i;
++
++ case RCODE_COMPLETE:
++ if (data[0] == old)
++ return i;
++
++ old = data[0];
++
++ /* Is the IRM 1394a-2000 compliant? */
++ if ((data[0] & c) == (data[1] & c))
++ continue;
++
++ /* 1394-1995 IRM, fall through to retry. */
++ default:
++ if (retry--)
++ i--;
++ }
++ }
++
++ return -EIO;
++}
++
++static void deallocate_channel(struct fw_card *card, int irm_id,
++ int generation, int channel)
++{
++ u32 mask;
++ u64 offset;
++
++ mask = channel < 32 ? 1 << channel : 1 << (channel - 32);
++ offset = channel < 32 ? CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_HI :
++ CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_LO;
++
++ manage_channel(card, irm_id, generation, mask, offset, false);
++}
++
++/**
++ * fw_iso_resource_manage - Allocate or deallocate a channel and/or bandwidth
++ *
++ * In parameters: card, generation, channels_mask, bandwidth, allocate
++ * Out parameters: channel, bandwidth
++ * This function blocks (sleeps) during communication with the IRM.
++ *
++ * Allocates or deallocates at most one channel out of channels_mask.
++ * channels_mask is a bitfield with MSB for channel 63 and LSB for channel 0.
++ * (Note, the IRM's CHANNELS_AVAILABLE is a big-endian bitfield with MSB for
++ * channel 0 and LSB for channel 63.)
++ * Allocates or deallocates as many bandwidth allocation units as specified.
++ *
++ * Returns channel < 0 if no channel was allocated or deallocated.
++ * Returns bandwidth = 0 if no bandwidth was allocated or deallocated.
++ *
++ * If generation is stale, deallocations succeed but allocations fail with
++ * channel = -EAGAIN.
++ *
++ * If channel allocation fails, no bandwidth will be allocated either.
++ * If bandwidth allocation fails, no channel will be allocated either.
++ * But deallocations of channel and bandwidth are tried independently
++ * of each other's success.
++ */
++void fw_iso_resource_manage(struct fw_card *card, int generation,
++ u64 channels_mask, int *channel, int *bandwidth,
++ bool allocate)
++{
++ u32 channels_hi = channels_mask; /* channels 31...0 */
++ u32 channels_lo = channels_mask >> 32; /* channels 63...32 */
++ int irm_id, ret, c = -EINVAL;
++
++ spin_lock_irq(&card->lock);
++ irm_id = card->irm_node->node_id;
++ spin_unlock_irq(&card->lock);
++
++ if (channels_hi)
++ c = manage_channel(card, irm_id, generation, channels_hi,
++ CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_HI, allocate);
++ if (channels_lo && c < 0) {
++ c = manage_channel(card, irm_id, generation, channels_lo,
++ CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_LO, allocate);
++ if (c >= 0)
++ c += 32;
++ }
++ *channel = c;
++
++ if (allocate && channels_mask != 0 && c < 0)
++ *bandwidth = 0;
++
++ if (*bandwidth == 0)
++ return;
++
++ ret = manage_bandwidth(card, irm_id, generation, *bandwidth, allocate);
++ if (ret < 0)
++ *bandwidth = 0;
++
++ if (allocate && ret < 0 && c >= 0) {
++ deallocate_channel(card, irm_id, generation, c);
++ *channel = ret;
++ }
++}
+diff -Naurp linux-2.6-git/drivers/firewire/fw-ohci.c firewire-git/drivers/firewire/fw-ohci.c
+--- linux-2.6-git/drivers/firewire/fw-ohci.c 2009-01-30 13:39:02.990772025 -0500
++++ firewire-git/drivers/firewire/fw-ohci.c 2009-01-30 13:35:51.861646907 -0500
+@@ -205,6 +205,7 @@ struct fw_ohci {
+
+ u32 it_context_mask;
+ struct iso_context *it_context_list;
++ u64 ir_context_channels;
+ u32 ir_context_mask;
+ struct iso_context *ir_context_list;
+ };
+@@ -441,9 +442,8 @@ static inline void flush_writes(const st
+ reg_read(ohci, OHCI1394_Version);
+ }
+
+-static int
+-ohci_update_phy_reg(struct fw_card *card, int addr,
+- int clear_bits, int set_bits)
++static int ohci_update_phy_reg(struct fw_card *card, int addr,
++ int clear_bits, int set_bits)
+ {
+ struct fw_ohci *ohci = fw_ohci(card);
+ u32 val, old;
+@@ -658,8 +658,8 @@ static void ar_context_tasklet(unsigned
+ }
+ }
+
+-static int
+-ar_context_init(struct ar_context *ctx, struct fw_ohci *ohci, u32 regs)
++static int ar_context_init(struct ar_context *ctx,
++ struct fw_ohci *ohci, u32 regs)
+ {
+ struct ar_buffer ab;
+
+@@ -690,8 +690,7 @@ static void ar_context_run(struct ar_con
+ flush_writes(ctx->ohci);
+ }
+
+-static struct descriptor *
+-find_branch_descriptor(struct descriptor *d, int z)
++static struct descriptor *find_branch_descriptor(struct descriptor *d, int z)
+ {
+ int b, key;
+
+@@ -751,8 +750,7 @@ static void context_tasklet(unsigned lon
+ * Allocate a new buffer and add it to the list of free buffers for this
+ * context. Must be called with ohci->lock held.
+ */
+-static int
+-context_add_buffer(struct context *ctx)
++static int context_add_buffer(struct context *ctx)
+ {
+ struct descriptor_buffer *desc;
+ dma_addr_t uninitialized_var(bus_addr);
+@@ -781,9 +779,8 @@ context_add_buffer(struct context *ctx)
+ return 0;
+ }
+
+-static int
+-context_init(struct context *ctx, struct fw_ohci *ohci,
+- u32 regs, descriptor_callback_t callback)
++static int context_init(struct context *ctx, struct fw_ohci *ohci,
++ u32 regs, descriptor_callback_t callback)
+ {
+ ctx->ohci = ohci;
+ ctx->regs = regs;
+@@ -814,8 +811,7 @@ context_init(struct context *ctx, struct
+ return 0;
+ }
+
+-static void
+-context_release(struct context *ctx)
++static void context_release(struct context *ctx)
+ {
+ struct fw_card *card = &ctx->ohci->card;
+ struct descriptor_buffer *desc, *tmp;
+@@ -827,8 +823,8 @@ context_release(struct context *ctx)
+ }
+
+ /* Must be called with ohci->lock held */
+-static struct descriptor *
+-context_get_descriptors(struct context *ctx, int z, dma_addr_t *d_bus)
++static struct descriptor *context_get_descriptors(struct context *ctx,
++ int z, dma_addr_t *d_bus)
+ {
+ struct descriptor *d = NULL;
+ struct descriptor_buffer *desc = ctx->buffer_tail;
+@@ -912,8 +908,8 @@ struct driver_data {
+ * Must always be called with the ochi->lock held to ensure proper
+ * generation handling and locking around packet queue manipulation.
+ */
+-static int
+-at_context_queue_packet(struct context *ctx, struct fw_packet *packet)
++static int at_context_queue_packet(struct context *ctx,
++ struct fw_packet *packet)
+ {
+ struct fw_ohci *ohci = ctx->ohci;
+ dma_addr_t d_bus, uninitialized_var(payload_bus);
+@@ -1095,8 +1091,8 @@ static int handle_at_packet(struct conte
+ #define HEADER_GET_DATA_LENGTH(q) (((q) >> 16) & 0xffff)
+ #define HEADER_GET_EXTENDED_TCODE(q) (((q) >> 0) & 0xffff)
+
+-static void
+-handle_local_rom(struct fw_ohci *ohci, struct fw_packet *packet, u32 csr)
++static void handle_local_rom(struct fw_ohci *ohci,
++ struct fw_packet *packet, u32 csr)
+ {
+ struct fw_packet response;
+ int tcode, length, i;
+@@ -1122,8 +1118,8 @@ handle_local_rom(struct fw_ohci *ohci, s
+ fw_core_handle_response(&ohci->card, &response);
+ }
+
+-static void
+-handle_local_lock(struct fw_ohci *ohci, struct fw_packet *packet, u32 csr)
++static void handle_local_lock(struct fw_ohci *ohci,
++ struct fw_packet *packet, u32 csr)
+ {
+ struct fw_packet response;
+ int tcode, length, ext_tcode, sel;
+@@ -1164,8 +1160,7 @@ handle_local_lock(struct fw_ohci *ohci,
+ fw_core_handle_response(&ohci->card, &response);
+ }
+
+-static void
+-handle_local_request(struct context *ctx, struct fw_packet *packet)
++static void handle_local_request(struct context *ctx, struct fw_packet *packet)
+ {
+ u64 offset;
+ u32 csr;
+@@ -1205,11 +1200,10 @@ handle_local_request(struct context *ctx
+ }
+ }
+
+-static void
+-at_context_transmit(struct context *ctx, struct fw_packet *packet)
++static void at_context_transmit(struct context *ctx, struct fw_packet *packet)
+ {
+ unsigned long flags;
+- int retval;
++ int ret;
+
+ spin_lock_irqsave(&ctx->ohci->lock, flags);
+
+@@ -1220,10 +1214,10 @@ at_context_transmit(struct context *ctx,
+ return;
+ }
+
+- retval = at_context_queue_packet(ctx, packet);
++ ret = at_context_queue_packet(ctx, packet);
+ spin_unlock_irqrestore(&ctx->ohci->lock, flags);
+
+- if (retval < 0)
++ if (ret < 0)
+ packet->callback(packet, &ctx->ohci->card, packet->ack);
+
+ }
+@@ -1590,12 +1584,12 @@ static int ohci_enable(struct fw_card *c
+ return 0;
+ }
+
+-static int
+-ohci_set_config_rom(struct fw_card *card, u32 *config_rom, size_t length)
++static int ohci_set_config_rom(struct fw_card *card,
++ u32 *config_rom, size_t length)
+ {
+ struct fw_ohci *ohci;
+ unsigned long flags;
+- int retval = -EBUSY;
++ int ret = -EBUSY;
+ __be32 *next_config_rom;
+ dma_addr_t uninitialized_var(next_config_rom_bus);
+
+@@ -1649,7 +1643,7 @@ ohci_set_config_rom(struct fw_card *card
+
+ reg_write(ohci, OHCI1394_ConfigROMmap,
+ ohci->next_config_rom_bus);
+- retval = 0;
++ ret = 0;
+ }
+
+ spin_unlock_irqrestore(&ohci->lock, flags);
+@@ -1661,13 +1655,13 @@ ohci_set_config_rom(struct fw_card *card
+ * controller could need to access it before the bus reset
+ * takes effect.
+ */
+- if (retval == 0)
++ if (ret == 0)
+ fw_core_initiate_bus_reset(&ohci->card, 1);
+ else
+ dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
+ next_config_rom, next_config_rom_bus);
+
+- return retval;
++ return ret;
+ }
+
+ static void ohci_send_request(struct fw_card *card, struct fw_packet *packet)
+@@ -1689,7 +1683,7 @@ static int ohci_cancel_packet(struct fw_
+ struct fw_ohci *ohci = fw_ohci(card);
+ struct context *ctx = &ohci->at_request_ctx;
+ struct driver_data *driver_data = packet->driver_data;
+- int retval = -ENOENT;
++ int ret = -ENOENT;
+
+ tasklet_disable(&ctx->tasklet);
+
+@@ -1704,23 +1698,22 @@ static int ohci_cancel_packet(struct fw_
+ driver_data->packet = NULL;
+ packet->ack = RCODE_CANCELLED;
+ packet->callback(packet, &ohci->card, packet->ack);
+- retval = 0;
+-
++ ret = 0;
+ out:
+ tasklet_enable(&ctx->tasklet);
+
+- return retval;
++ return ret;
+ }
+
+-static int
+-ohci_enable_phys_dma(struct fw_card *card, int node_id, int generation)
++static int ohci_enable_phys_dma(struct fw_card *card,
++ int node_id, int generation)
+ {
+ #ifdef CONFIG_FIREWIRE_OHCI_REMOTE_DMA
+ return 0;
+ #else
+ struct fw_ohci *ohci = fw_ohci(card);
+ unsigned long flags;
+- int n, retval = 0;
++ int n, ret = 0;
+
+ /*
+ * FIXME: Make sure this bitmask is cleared when we clear the busReset
+@@ -1730,7 +1723,7 @@ ohci_enable_phys_dma(struct fw_card *car
+ spin_lock_irqsave(&ohci->lock, flags);
+
+ if (ohci->generation != generation) {
+- retval = -ESTALE;
++ ret = -ESTALE;
+ goto out;
+ }
+
+@@ -1748,12 +1741,12 @@ ohci_enable_phys_dma(struct fw_card *car
+ flush_writes(ohci);
+ out:
+ spin_unlock_irqrestore(&ohci->lock, flags);
+- return retval;
++
++ return ret;
+ #endif /* CONFIG_FIREWIRE_OHCI_REMOTE_DMA */
+ }
+
+-static u64
+-ohci_get_bus_time(struct fw_card *card)
++static u64 ohci_get_bus_time(struct fw_card *card)
+ {
+ struct fw_ohci *ohci = fw_ohci(card);
+ u32 cycle_time;
+@@ -1765,6 +1758,28 @@ ohci_get_bus_time(struct fw_card *card)
+ return bus_time;
+ }
+
++static void copy_iso_headers(struct iso_context *ctx, void *p)
++{
++ int i = ctx->header_length;
++
++ if (i + ctx->base.header_size > PAGE_SIZE)
++ return;
++
++ /*
++ * The iso header is byteswapped to little endian by
++ * the controller, but the remaining header quadlets
++ * are big endian. We want to present all the headers
++ * as big endian, so we have to swap the first quadlet.
++ */
++ if (ctx->base.header_size > 0)
++ *(u32 *) (ctx->header + i) = __swab32(*(u32 *) (p + 4));
++ if (ctx->base.header_size > 4)
++ *(u32 *) (ctx->header + i + 4) = __swab32(*(u32 *) p);
++ if (ctx->base.header_size > 8)
++ memcpy(ctx->header + i + 8, p + 8, ctx->base.header_size - 8);
++ ctx->header_length += ctx->base.header_size;
++}
++
+ static int handle_ir_dualbuffer_packet(struct context *context,
+ struct descriptor *d,
+ struct descriptor *last)
+@@ -1775,7 +1790,6 @@ static int handle_ir_dualbuffer_packet(s
+ __le32 *ir_header;
+ size_t header_length;
+ void *p, *end;
+- int i;
+
+ if (db->first_res_count != 0 && db->second_res_count != 0) {
+ if (ctx->excess_bytes <= le16_to_cpu(db->second_req_count)) {
+@@ -1788,25 +1802,14 @@ static int handle_ir_dualbuffer_packet(s
+ header_length = le16_to_cpu(db->first_req_count) -
+ le16_to_cpu(db->first_res_count);
+
+- i = ctx->header_length;
+ p = db + 1;
+ end = p + header_length;
+- while (p < end && i + ctx->base.header_size <= PAGE_SIZE) {
+- /*
+- * The iso header is byteswapped to little endian by
+- * the controller, but the remaining header quadlets
+- * are big endian. We want to present all the headers
+- * as big endian, so we have to swap the first
+- * quadlet.
+- */
+- *(u32 *) (ctx->header + i) = __swab32(*(u32 *) (p + 4));
+- memcpy(ctx->header + i + 4, p + 8, ctx->base.header_size - 4);
+- i += ctx->base.header_size;
++ while (p < end) {
++ copy_iso_headers(ctx, p);
+ ctx->excess_bytes +=
+ (le32_to_cpu(*(__le32 *)(p + 4)) >> 16) & 0xffff;
+- p += ctx->base.header_size + 4;
++ p += max(ctx->base.header_size, (size_t)8);
+ }
+- ctx->header_length = i;
+
+ ctx->excess_bytes -= le16_to_cpu(db->second_req_count) -
+ le16_to_cpu(db->second_res_count);
+@@ -1832,7 +1835,6 @@ static int handle_ir_packet_per_buffer(s
+ struct descriptor *pd;
+ __le32 *ir_header;
+ void *p;
+- int i;
+
+ for (pd = d; pd <= last; pd++) {
+ if (pd->transfer_status)
+@@ -1842,21 +1844,8 @@ static int handle_ir_packet_per_buffer(s
+ /* Descriptor(s) not done yet, stop iteration */
+ return 0;
+
+- i = ctx->header_length;
+- p = last + 1;
+-
+- if (ctx->base.header_size > 0 &&
+- i + ctx->base.header_size <= PAGE_SIZE) {
+- /*
+- * The iso header is byteswapped to little endian by
+- * the controller, but the remaining header quadlets
+- * are big endian. We want to present all the headers
+- * as big endian, so we have to swap the first quadlet.
+- */
+- *(u32 *) (ctx->header + i) = __swab32(*(u32 *) (p + 4));
+- memcpy(ctx->header + i + 4, p + 8, ctx->base.header_size - 4);
+- ctx->header_length += ctx->base.header_size;
+- }
++ p = last + 1;
++ copy_iso_headers(ctx, p);
+
+ if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) {
+ ir_header = (__le32 *) p;
+@@ -1888,21 +1877,24 @@ static int handle_it_packet(struct conte
+ return 1;
+ }
+
+-static struct fw_iso_context *
+-ohci_allocate_iso_context(struct fw_card *card, int type, size_t header_size)
++static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card,
++ int type, int channel, size_t header_size)
+ {
+ struct fw_ohci *ohci = fw_ohci(card);
+ struct iso_context *ctx, *list;
+ descriptor_callback_t callback;
++ u64 *channels, dont_care = ~0ULL;
+ u32 *mask, regs;
+ unsigned long flags;
+- int index, retval = -ENOMEM;
++ int index, ret = -ENOMEM;
+
+ if (type == FW_ISO_CONTEXT_TRANSMIT) {
++ channels = &dont_care;
+ mask = &ohci->it_context_mask;
+ list = ohci->it_context_list;
+ callback = handle_it_packet;
+ } else {
++ channels = &ohci->ir_context_channels;
+ mask = &ohci->ir_context_mask;
+ list = ohci->ir_context_list;
+ if (ohci->use_dualbuffer)
+@@ -1912,9 +1904,11 @@ ohci_allocate_iso_context(struct fw_card
+ }
+
+ spin_lock_irqsave(&ohci->lock, flags);
+- index = ffs(*mask) - 1;
+- if (index >= 0)
++ index = *channels & 1ULL << channel ? ffs(*mask) - 1 : -1;
++ if (index >= 0) {
++ *channels &= ~(1ULL << channel);
+ *mask &= ~(1 << index);
++ }
+ spin_unlock_irqrestore(&ohci->lock, flags);
+
+ if (index < 0)
+@@ -1932,8 +1926,8 @@ ohci_allocate_iso_context(struct fw_card
+ if (ctx->header == NULL)
+ goto out;
+
+- retval = context_init(&ctx->context, ohci, regs, callback);
+- if (retval < 0)
++ ret = context_init(&ctx->context, ohci, regs, callback);
++ if (ret < 0)
+ goto out_with_header;
+
+ return &ctx->base;
+@@ -1945,7 +1939,7 @@ ohci_allocate_iso_context(struct fw_card
+ *mask |= 1 << index;
+ spin_unlock_irqrestore(&ohci->lock, flags);
+
+- return ERR_PTR(retval);
++ return ERR_PTR(ret);
+ }
+
+ static int ohci_start_iso(struct fw_iso_context *base,
+@@ -2024,16 +2018,16 @@ static void ohci_free_iso_context(struct
+ } else {
+ index = ctx - ohci->ir_context_list;
+ ohci->ir_context_mask |= 1 << index;
++ ohci->ir_context_channels |= 1ULL << base->channel;
+ }
+
+ spin_unlock_irqrestore(&ohci->lock, flags);
+ }
+
+-static int
+-ohci_queue_iso_transmit(struct fw_iso_context *base,
+- struct fw_iso_packet *packet,
+- struct fw_iso_buffer *buffer,
+- unsigned long payload)
++static int ohci_queue_iso_transmit(struct fw_iso_context *base,
++ struct fw_iso_packet *packet,
++ struct fw_iso_buffer *buffer,
++ unsigned long payload)
+ {
+ struct iso_context *ctx = container_of(base, struct iso_context, base);
+ struct descriptor *d, *last, *pd;
+@@ -2128,11 +2122,10 @@ ohci_queue_iso_transmit(struct fw_iso_co
+ return 0;
+ }
+
+-static int
+-ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base,
+- struct fw_iso_packet *packet,
+- struct fw_iso_buffer *buffer,
+- unsigned long payload)
++static int ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base,
++ struct fw_iso_packet *packet,
++ struct fw_iso_buffer *buffer,
++ unsigned long payload)
+ {
+ struct iso_context *ctx = container_of(base, struct iso_context, base);
+ struct db_descriptor *db = NULL;
+@@ -2151,11 +2144,11 @@ ohci_queue_iso_receive_dualbuffer(struct
+ z = 2;
+
+ /*
+- * The OHCI controller puts the status word in the header
+- * buffer too, so we need 4 extra bytes per packet.
++ * The OHCI controller puts the isochronous header and trailer in the
++ * buffer, so we need at least 8 bytes.
+ */
+ packet_count = p->header_length / ctx->base.header_size;
+- header_size = packet_count * (ctx->base.header_size + 4);
++ header_size = packet_count * max(ctx->base.header_size, (size_t)8);
+
+ /* Get header size in number of descriptors. */
+ header_z = DIV_ROUND_UP(header_size, sizeof(*d));
+@@ -2173,7 +2166,8 @@ ohci_queue_iso_receive_dualbuffer(struct
+ db = (struct db_descriptor *) d;
+ db->control = cpu_to_le16(DESCRIPTOR_STATUS |
+ DESCRIPTOR_BRANCH_ALWAYS);
+- db->first_size = cpu_to_le16(ctx->base.header_size + 4);
++ db->first_size =
++ cpu_to_le16(max(ctx->base.header_size, (size_t)8));
+ if (p->skip && rest == p->payload_length) {
+ db->control |= cpu_to_le16(DESCRIPTOR_WAIT);
+ db->first_req_count = db->first_size;
+@@ -2208,11 +2202,10 @@ ohci_queue_iso_receive_dualbuffer(struct
+ return 0;
+ }
+
+-static int
+-ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base,
+- struct fw_iso_packet *packet,
+- struct fw_iso_buffer *buffer,
+- unsigned long payload)
++static int ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base,
++ struct fw_iso_packet *packet,
++ struct fw_iso_buffer *buffer,
++ unsigned long payload)
+ {
+ struct iso_context *ctx = container_of(base, struct iso_context, base);
+ struct descriptor *d = NULL, *pd = NULL;
+@@ -2223,11 +2216,11 @@ ohci_queue_iso_receive_packet_per_buffer
+ int page, offset, packet_count, header_size, payload_per_buffer;
+
+ /*
+- * The OHCI controller puts the status word in the
+- * buffer too, so we need 4 extra bytes per packet.
++ * The OHCI controller puts the isochronous header and trailer in the
++ * buffer, so we need at least 8 bytes.
+ */
+ packet_count = p->header_length / ctx->base.header_size;
+- header_size = ctx->base.header_size + 4;
++ header_size = max(ctx->base.header_size, (size_t)8);
+
+ /* Get header size in number of descriptors. */
+ header_z = DIV_ROUND_UP(header_size, sizeof(*d));
+@@ -2286,29 +2279,27 @@ ohci_queue_iso_receive_packet_per_buffer
+ return 0;
+ }
+
+-static int
+-ohci_queue_iso(struct fw_iso_context *base,
+- struct fw_iso_packet *packet,
+- struct fw_iso_buffer *buffer,
+- unsigned long payload)
++static int ohci_queue_iso(struct fw_iso_context *base,
++ struct fw_iso_packet *packet,
++ struct fw_iso_buffer *buffer,
++ unsigned long payload)
+ {
+ struct iso_context *ctx = container_of(base, struct iso_context, base);
+ unsigned long flags;
+- int retval;
++ int ret;
+
+ spin_lock_irqsave(&ctx->context.ohci->lock, flags);
+ if (base->type == FW_ISO_CONTEXT_TRANSMIT)
+- retval = ohci_queue_iso_transmit(base, packet, buffer, payload);
++ ret = ohci_queue_iso_transmit(base, packet, buffer, payload);
+ else if (ctx->context.ohci->use_dualbuffer)
+- retval = ohci_queue_iso_receive_dualbuffer(base, packet,
+- buffer, payload);
++ ret = ohci_queue_iso_receive_dualbuffer(base, packet,
++ buffer, payload);
+ else
+- retval = ohci_queue_iso_receive_packet_per_buffer(base, packet,
+- buffer,
+- payload);
++ ret = ohci_queue_iso_receive_packet_per_buffer(base, packet,
++ buffer, payload);
+ spin_unlock_irqrestore(&ctx->context.ohci->lock, flags);
+
+- return retval;
++ return ret;
+ }
+
+ static const struct fw_card_driver ohci_driver = {
+@@ -2357,8 +2348,8 @@ static void ohci_pmac_off(struct pci_dev
+ #define ohci_pmac_off(dev)
+ #endif /* CONFIG_PPC_PMAC */
+
+-static int __devinit
+-pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
++static int __devinit pci_probe(struct pci_dev *dev,
++ const struct pci_device_id *ent)
+ {
+ struct fw_ohci *ohci;
+ u32 bus_options, max_receive, link_speed, version;
+@@ -2440,6 +2431,7 @@ pci_probe(struct pci_dev *dev, const str
+ ohci->it_context_list = kzalloc(size, GFP_KERNEL);
+
+ reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, ~0);
++ ohci->ir_context_channels = ~0ULL;
+ ohci->ir_context_mask = reg_read(ohci, OHCI1394_IsoXmitIntMaskSet);
+ reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, ~0);
+ size = sizeof(struct iso_context) * hweight32(ohci->ir_context_mask);
+diff -Naurp linux-2.6-git/drivers/firewire/fw-sbp2.c firewire-git/drivers/firewire/fw-sbp2.c
+--- linux-2.6-git/drivers/firewire/fw-sbp2.c 2009-01-30 13:39:02.991771976 -0500
++++ firewire-git/drivers/firewire/fw-sbp2.c 2009-01-30 13:35:51.861646907 -0500
+@@ -392,20 +392,18 @@ static const struct {
+ }
+ };
+
+-static void
+-free_orb(struct kref *kref)
++static void free_orb(struct kref *kref)
+ {
+ struct sbp2_orb *orb = container_of(kref, struct sbp2_orb, kref);
+
+ kfree(orb);
+ }
+
+-static void
+-sbp2_status_write(struct fw_card *card, struct fw_request *request,
+- int tcode, int destination, int source,
+- int generation, int speed,
+- unsigned long long offset,
+- void *payload, size_t length, void *callback_data)
++static void sbp2_status_write(struct fw_card *card, struct fw_request *request,
++ int tcode, int destination, int source,
++ int generation, int speed,
++ unsigned long long offset,
++ void *payload, size_t length, void *callback_data)
+ {
+ struct sbp2_logical_unit *lu = callback_data;
+ struct sbp2_orb *orb;
+@@ -451,9 +449,8 @@ sbp2_status_write(struct fw_card *card,
+ fw_send_response(card, request, RCODE_COMPLETE);
+ }
+
+-static void
+-complete_transaction(struct fw_card *card, int rcode,
+- void *payload, size_t length, void *data)
++static void complete_transaction(struct fw_card *card, int rcode,
++ void *payload, size_t length, void *data)
+ {
+ struct sbp2_orb *orb = data;
+ unsigned long flags;
+@@ -482,9 +479,8 @@ complete_transaction(struct fw_card *car
+ kref_put(&orb->kref, free_orb);
+ }
+
+-static void
+-sbp2_send_orb(struct sbp2_orb *orb, struct sbp2_logical_unit *lu,
+- int node_id, int generation, u64 offset)
++static void sbp2_send_orb(struct sbp2_orb *orb, struct sbp2_logical_unit *lu,
++ int node_id, int generation, u64 offset)
+ {
+ struct fw_device *device = fw_device(lu->tgt->unit->device.parent);
+ unsigned long flags;
+@@ -531,8 +527,8 @@ static int sbp2_cancel_orbs(struct sbp2_
+ return retval;
+ }
+
+-static void
+-complete_management_orb(struct sbp2_orb *base_orb, struct sbp2_status *status)
++static void complete_management_orb(struct sbp2_orb *base_orb,
++ struct sbp2_status *status)
+ {
+ struct sbp2_management_orb *orb =
+ container_of(base_orb, struct sbp2_management_orb, base);
+@@ -542,10 +538,9 @@ complete_management_orb(struct sbp2_orb
+ complete(&orb->done);
+ }
+
+-static int
+-sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id,
+- int generation, int function, int lun_or_login_id,
+- void *response)
++static int sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id,
++ int generation, int function,
++ int lun_or_login_id, void *response)
+ {
+ struct fw_device *device = fw_device(lu->tgt->unit->device.parent);
+ struct sbp2_management_orb *orb;
+@@ -652,9 +647,8 @@ static void sbp2_agent_reset(struct sbp2
+ &d, sizeof(d));
+ }
+
+-static void
+-complete_agent_reset_write_no_wait(struct fw_card *card, int rcode,
+- void *payload, size_t length, void *data)
++static void complete_agent_reset_write_no_wait(struct fw_card *card,
++ int rcode, void *payload, size_t length, void *data)
+ {
+ kfree(data);
+ }
+@@ -1299,8 +1293,7 @@ static void sbp2_unmap_scatterlist(struc
+ sizeof(orb->page_table), DMA_TO_DEVICE);
+ }
+
+-static unsigned int
+-sbp2_status_to_sense_data(u8 *sbp2_status, u8 *sense_data)
++static unsigned int sbp2_status_to_sense_data(u8 *sbp2_status, u8 *sense_data)
+ {
+ int sam_status;
+
+@@ -1337,8 +1330,8 @@ sbp2_status_to_sense_data(u8 *sbp2_statu
+ }
+ }
+
+-static void
+-complete_command_orb(struct sbp2_orb *base_orb, struct sbp2_status *status)
++static void complete_command_orb(struct sbp2_orb *base_orb,
++ struct sbp2_status *status)
+ {
+ struct sbp2_command_orb *orb =
+ container_of(base_orb, struct sbp2_command_orb, base);
+@@ -1384,9 +1377,8 @@ complete_command_orb(struct sbp2_orb *ba
+ orb->done(orb->cmd);
+ }
+
+-static int
+-sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device,
+- struct sbp2_logical_unit *lu)
++static int sbp2_map_scatterlist(struct sbp2_command_orb *orb,
++ struct fw_device *device, struct sbp2_logical_unit *lu)
+ {
+ struct scatterlist *sg = scsi_sglist(orb->cmd);
+ int i, n;
+@@ -1584,9 +1576,8 @@ static int sbp2_scsi_abort(struct scsi_c
+ * This is the concatenation of target port identifier and logical unit
+ * identifier as per SAM-2...SAM-4 annex A.
+ */
+-static ssize_t
+-sbp2_sysfs_ieee1394_id_show(struct device *dev, struct device_attribute *attr,
+- char *buf)
++static ssize_t sbp2_sysfs_ieee1394_id_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
+ {
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct sbp2_logical_unit *lu;
+diff -Naurp linux-2.6-git/drivers/firewire/fw-topology.c firewire-git/drivers/firewire/fw-topology.c
+--- linux-2.6-git/drivers/firewire/fw-topology.c 2009-01-30 13:39:02.991771976 -0500
++++ firewire-git/drivers/firewire/fw-topology.c 2009-01-30 13:35:51.862647087 -0500
+@@ -314,9 +314,8 @@ typedef void (*fw_node_callback_t)(struc
+ struct fw_node * node,
+ struct fw_node * parent);
+
+-static void
+-for_each_fw_node(struct fw_card *card, struct fw_node *root,
+- fw_node_callback_t callback)
++static void for_each_fw_node(struct fw_card *card, struct fw_node *root,
++ fw_node_callback_t callback)
+ {
+ struct list_head list;
+ struct fw_node *node, *next, *child, *parent;
+@@ -349,9 +348,8 @@ for_each_fw_node(struct fw_card *card, s
+ fw_node_put(node);
+ }
+
+-static void
+-report_lost_node(struct fw_card *card,
+- struct fw_node *node, struct fw_node *parent)
++static void report_lost_node(struct fw_card *card,
++ struct fw_node *node, struct fw_node *parent)
+ {
+ fw_node_event(card, node, FW_NODE_DESTROYED);
+ fw_node_put(node);
+@@ -360,9 +358,8 @@ report_lost_node(struct fw_card *card,
+ card->bm_retries = 0;
+ }
+
+-static void
+-report_found_node(struct fw_card *card,
+- struct fw_node *node, struct fw_node *parent)
++static void report_found_node(struct fw_card *card,
++ struct fw_node *node, struct fw_node *parent)
+ {
+ int b_path = (node->phy_speed == SCODE_BETA);
+
+@@ -415,8 +412,7 @@ static void move_tree(struct fw_node *no
+ * found, lost or updated. Update the nodes in the card topology tree
+ * as we go.
+ */
+-static void
+-update_tree(struct fw_card *card, struct fw_node *root)
++static void update_tree(struct fw_card *card, struct fw_node *root)
+ {
+ struct list_head list0, list1;
+ struct fw_node *node0, *node1, *next1;
+@@ -497,8 +493,8 @@ update_tree(struct fw_card *card, struct
+ }
+ }
+
+-static void
+-update_topology_map(struct fw_card *card, u32 *self_ids, int self_id_count)
++static void update_topology_map(struct fw_card *card,
++ u32 *self_ids, int self_id_count)
+ {
+ int node_count;
+
+@@ -510,10 +506,8 @@ update_topology_map(struct fw_card *card
+ fw_compute_block_crc(card->topology_map);
+ }
+
+-void
+-fw_core_handle_bus_reset(struct fw_card *card,
+- int node_id, int generation,
+- int self_id_count, u32 * self_ids)
++void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation,
++ int self_id_count, u32 *self_ids)
+ {
+ struct fw_node *local_node;
+ unsigned long flags;
+diff -Naurp linux-2.6-git/drivers/firewire/fw-topology.h firewire-git/drivers/firewire/fw-topology.h
+--- linux-2.6-git/drivers/firewire/fw-topology.h 2008-11-04 11:18:33.000000000 -0500
++++ firewire-git/drivers/firewire/fw-topology.h 2009-01-30 13:35:51.862647087 -0500
+@@ -19,6 +19,11 @@
+ #ifndef __fw_topology_h
+ #define __fw_topology_h
+
++#include <linux/list.h>
++#include <linux/slab.h>
++
++#include <asm/atomic.h>
++
+ enum {
+ FW_NODE_CREATED,
+ FW_NODE_UPDATED,
+@@ -51,26 +56,22 @@ struct fw_node {
+ struct fw_node *ports[0];
+ };
+
+-static inline struct fw_node *
+-fw_node_get(struct fw_node *node)
++static inline struct fw_node *fw_node_get(struct fw_node *node)
+ {
+ atomic_inc(&node->ref_count);
+
+ return node;
+ }
+
+-static inline void
+-fw_node_put(struct fw_node *node)
++static inline void fw_node_put(struct fw_node *node)
+ {
+ if (atomic_dec_and_test(&node->ref_count))
+ kfree(node);
+ }
+
+-void
+-fw_destroy_nodes(struct fw_card *card);
+-
+-int
+-fw_compute_block_crc(u32 *block);
++struct fw_card;
++void fw_destroy_nodes(struct fw_card *card);
+
++int fw_compute_block_crc(u32 *block);
+
+ #endif /* __fw_topology_h */
+diff -Naurp linux-2.6-git/drivers/firewire/fw-transaction.c firewire-git/drivers/firewire/fw-transaction.c
+--- linux-2.6-git/drivers/firewire/fw-transaction.c 2009-01-30 13:39:02.991771976 -0500
++++ firewire-git/drivers/firewire/fw-transaction.c 2009-01-30 13:35:51.862647087 -0500
+@@ -64,10 +64,9 @@
+ #define PHY_CONFIG_ROOT_ID(node_id) ((((node_id) & 0x3f) << 24) | (1 << 23))
+ #define PHY_IDENTIFIER(id) ((id) << 30)
+
+-static int
+-close_transaction(struct fw_transaction *transaction,
+- struct fw_card *card, int rcode,
+- u32 *payload, size_t length)
++static int close_transaction(struct fw_transaction *transaction,
++ struct fw_card *card, int rcode,
++ u32 *payload, size_t length)
+ {
+ struct fw_transaction *t;
+ unsigned long flags;
+@@ -94,9 +93,8 @@ close_transaction(struct fw_transaction
+ * Only valid for transactions that are potentially pending (ie have
+ * been sent).
+ */
+-int
+-fw_cancel_transaction(struct fw_card *card,
+- struct fw_transaction *transaction)
++int fw_cancel_transaction(struct fw_card *card,
++ struct fw_transaction *transaction)
+ {
+ /*
+ * Cancel the packet transmission if it's still queued. That
+@@ -116,9 +114,8 @@ fw_cancel_transaction(struct fw_card *ca
+ }
+ EXPORT_SYMBOL(fw_cancel_transaction);
+
+-static void
+-transmit_complete_callback(struct fw_packet *packet,
+- struct fw_card *card, int status)
++static void transmit_complete_callback(struct fw_packet *packet,
++ struct fw_card *card, int status)
+ {
+ struct fw_transaction *t =
+ container_of(packet, struct fw_transaction, packet);
+@@ -151,8 +148,7 @@ transmit_complete_callback(struct fw_pac
+ }
+ }
+
+-static void
+-fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
++static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
+ int destination_id, int source_id, int generation, int speed,
+ unsigned long long offset, void *payload, size_t length)
+ {
+@@ -247,12 +243,10 @@ fw_fill_request(struct fw_packet *packet
+ * @param callback_data pointer to arbitrary data, which will be
+ * passed to the callback
+ */
+-void
+-fw_send_request(struct fw_card *card, struct fw_transaction *t,
+- int tcode, int destination_id, int generation, int speed,
+- unsigned long long offset,
+- void *payload, size_t length,
+- fw_transaction_callback_t callback, void *callback_data)
++void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode,
++ int destination_id, int generation, int speed,
++ unsigned long long offset, void *payload, size_t length,
++ fw_transaction_callback_t callback, void *callback_data)
+ {
+ unsigned long flags;
+ int tlabel;
+@@ -322,8 +316,8 @@ static void transaction_callback(struct
+ * Returns the RCODE.
+ */
+ int fw_run_transaction(struct fw_card *card, int tcode, int destination_id,
+- int generation, int speed, unsigned long long offset,
+- void *data, size_t length)
++ int generation, int speed, unsigned long long offset,
++ void *data, size_t length)
+ {
+ struct transaction_callback_data d;
+ struct fw_transaction t;
+@@ -399,9 +393,8 @@ void fw_flush_transactions(struct fw_car
+ }
+ }
+
+-static struct fw_address_handler *
+-lookup_overlapping_address_handler(struct list_head *list,
+- unsigned long long offset, size_t length)
++static struct fw_address_handler *lookup_overlapping_address_handler(
++ struct list_head *list, unsigned long long offset, size_t length)
+ {
+ struct fw_address_handler *handler;
+
+@@ -414,9 +407,8 @@ lookup_overlapping_address_handler(struc
+ return NULL;
+ }
+
+-static struct fw_address_handler *
+-lookup_enclosing_address_handler(struct list_head *list,
+- unsigned long long offset, size_t length)
++static struct fw_address_handler *lookup_enclosing_address_handler(
++ struct list_head *list, unsigned long long offset, size_t length)
+ {
+ struct fw_address_handler *handler;
+
+@@ -449,36 +441,44 @@ const struct fw_address_region fw_unit_s
+ #endif /* 0 */
+
+ /**
+- * Allocate a range of addresses in the node space of the OHCI
+- * controller. When a request is received that falls within the
+- * specified address range, the specified callback is invoked. The
+- * parameters passed to the callback give the details of the
+- * particular request.
++ * fw_core_add_address_handler - register for incoming requests
++ * @handler: callback
++ * @region: region in the IEEE 1212 node space address range
++ *
++ * region->start, ->end, and handler->length have to be quadlet-aligned.
++ *
++ * When a request is received that falls within the specified address range,
++ * the specified callback is invoked. The parameters passed to the callback
++ * give the details of the particular request.
+ *
+ * Return value: 0 on success, non-zero otherwise.
+ * The start offset of the handler's address region is determined by
+ * fw_core_add_address_handler() and is returned in handler->offset.
+- * The offset is quadlet-aligned.
+ */
+-int
+-fw_core_add_address_handler(struct fw_address_handler *handler,
+- const struct fw_address_region *region)
++int fw_core_add_address_handler(struct fw_address_handler *handler,
++ const struct fw_address_region *region)
+ {
+ struct fw_address_handler *other;
+ unsigned long flags;
+ int ret = -EBUSY;
+
++ if (region->start & 0xffff000000000003ULL ||
++ region->end & 0xffff000000000003ULL ||
++ region->start >= region->end ||
++ handler->length & 3 ||
++ handler->length == 0)
++ return -EINVAL;
++
+ spin_lock_irqsave(&address_handler_lock, flags);
+
+- handler->offset = roundup(region->start, 4);
++ handler->offset = region->start;
+ while (handler->offset + handler->length <= region->end) {
+ other =
+ lookup_overlapping_address_handler(&address_handler_list,
+ handler->offset,
+ handler->length);
+ if (other != NULL) {
+- handler->offset =
+- roundup(other->offset + other->length, 4);
++ handler->offset += other->length;
+ } else {
+ list_add_tail(&handler->link, &address_handler_list);
+ ret = 0;
+@@ -493,12 +493,7 @@ fw_core_add_address_handler(struct fw_ad
+ EXPORT_SYMBOL(fw_core_add_address_handler);
+
+ /**
+- * Deallocate a range of addresses allocated with fw_allocate. This
+- * will call the associated callback one last time with a the special
+- * tcode TCODE_DEALLOCATE, to let the client destroy the registered
+- * callback data. For convenience, the callback parameters offset and
+- * length are set to the start and the length respectively for the
+- * deallocated region, payload is set to NULL.
++ * fw_core_remove_address_handler - unregister an address handler
+ */
+ void fw_core_remove_address_handler(struct fw_address_handler *handler)
+ {
+@@ -518,9 +513,8 @@ struct fw_request {
+ u32 data[0];
+ };
+
+-static void
+-free_response_callback(struct fw_packet *packet,
+- struct fw_card *card, int status)
++static void free_response_callback(struct fw_packet *packet,
++ struct fw_card *card, int status)
+ {
+ struct fw_request *request;
+
+@@ -528,9 +522,8 @@ free_response_callback(struct fw_packet
+ kfree(request);
+ }
+
+-void
+-fw_fill_response(struct fw_packet *response, u32 *request_header,
+- int rcode, void *payload, size_t length)
++void fw_fill_response(struct fw_packet *response, u32 *request_header,
++ int rcode, void *payload, size_t length)
+ {
+ int tcode, tlabel, extended_tcode, source, destination;
+
+@@ -588,8 +581,7 @@ fw_fill_response(struct fw_packet *respo
+ }
+ EXPORT_SYMBOL(fw_fill_response);
+
+-static struct fw_request *
+-allocate_request(struct fw_packet *p)
++static struct fw_request *allocate_request(struct fw_packet *p)
+ {
+ struct fw_request *request;
+ u32 *data, length;
+@@ -649,8 +641,8 @@ allocate_request(struct fw_packet *p)
+ return request;
+ }
+
+-void
+-fw_send_response(struct fw_card *card, struct fw_request *request, int rcode)
++void fw_send_response(struct fw_card *card,
++ struct fw_request *request, int rcode)
+ {
+ /* unified transaction or broadcast transaction: don't respond */
+ if (request->ack != ACK_PENDING ||
+@@ -670,8 +662,7 @@ fw_send_response(struct fw_card *card, s
+ }
+ EXPORT_SYMBOL(fw_send_response);
+
+-void
+-fw_core_handle_request(struct fw_card *card, struct fw_packet *p)
++void fw_core_handle_request(struct fw_card *card, struct fw_packet *p)
+ {
+ struct fw_address_handler *handler;
+ struct fw_request *request;
+@@ -719,8 +710,7 @@ fw_core_handle_request(struct fw_card *c
+ }
+ EXPORT_SYMBOL(fw_core_handle_request);
+
+-void
+-fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
++void fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
+ {
+ struct fw_transaction *t;
+ unsigned long flags;
+@@ -793,12 +783,10 @@ static const struct fw_address_region to
+ { .start = CSR_REGISTER_BASE | CSR_TOPOLOGY_MAP,
+ .end = CSR_REGISTER_BASE | CSR_TOPOLOGY_MAP_END, };
+
+-static void
+-handle_topology_map(struct fw_card *card, struct fw_request *request,
+- int tcode, int destination, int source,
+- int generation, int speed,
+- unsigned long long offset,
+- void *payload, size_t length, void *callback_data)
++static void handle_topology_map(struct fw_card *card, struct fw_request *request,
++ int tcode, int destination, int source, int generation,
++ int speed, unsigned long long offset,
++ void *payload, size_t length, void *callback_data)
+ {
+ int i, start, end;
+ __be32 *map;
+@@ -832,12 +820,10 @@ static const struct fw_address_region re
+ { .start = CSR_REGISTER_BASE,
+ .end = CSR_REGISTER_BASE | CSR_CONFIG_ROM, };
+
+-static void
+-handle_registers(struct fw_card *card, struct fw_request *request,
+- int tcode, int destination, int source,
+- int generation, int speed,
+- unsigned long long offset,
+- void *payload, size_t length, void *callback_data)
++static void handle_registers(struct fw_card *card, struct fw_request *request,
++ int tcode, int destination, int source, int generation,
++ int speed, unsigned long long offset,
++ void *payload, size_t length, void *callback_data)
+ {
+ int reg = offset & ~CSR_REGISTER_BASE;
+ unsigned long long bus_time;
+@@ -939,11 +925,11 @@ static struct fw_descriptor model_id_des
+
+ static int __init fw_core_init(void)
+ {
+- int retval;
++ int ret;
+
+- retval = bus_register(&fw_bus_type);
+- if (retval < 0)
+- return retval;
++ ret = bus_register(&fw_bus_type);
++ if (ret < 0)
++ return ret;
+
+ fw_cdev_major = register_chrdev(0, "firewire", &fw_device_ops);
+ if (fw_cdev_major < 0) {
+@@ -951,19 +937,10 @@ static int __init fw_core_init(void)
+ return fw_cdev_major;
+ }
+
+- retval = fw_core_add_address_handler(&topology_map,
+- &topology_map_region);
+- BUG_ON(retval < 0);
+-
+- retval = fw_core_add_address_handler(&registers,
+- &registers_region);
+- BUG_ON(retval < 0);
+-
+- /* Add the vendor textual descriptor. */
+- retval = fw_core_add_descriptor(&vendor_id_descriptor);
+- BUG_ON(retval < 0);
+- retval = fw_core_add_descriptor(&model_id_descriptor);
+- BUG_ON(retval < 0);
++ fw_core_add_address_handler(&topology_map, &topology_map_region);
++ fw_core_add_address_handler(&registers, &registers_region);
++ fw_core_add_descriptor(&vendor_id_descriptor);
++ fw_core_add_descriptor(&model_id_descriptor);
+
+ return 0;
+ }
+diff -Naurp linux-2.6-git/drivers/firewire/fw-transaction.h firewire-git/drivers/firewire/fw-transaction.h
+--- linux-2.6-git/drivers/firewire/fw-transaction.h 2009-01-30 13:39:02.992772636 -0500
++++ firewire-git/drivers/firewire/fw-transaction.h 2009-01-30 13:35:51.862647087 -0500
+@@ -82,14 +82,14 @@
+ #define CSR_SPEED_MAP 0x2000
+ #define CSR_SPEED_MAP_END 0x3000
+
++#define BANDWIDTH_AVAILABLE_INITIAL 4915
+ #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)
+
+-static inline void
+-fw_memcpy_from_be32(void *_dst, void *_src, size_t size)
++static inline void fw_memcpy_from_be32(void *_dst, void *_src, size_t size)
+ {
+ u32 *dst = _dst;
+ __be32 *src = _src;
+@@ -99,8 +99,7 @@ fw_memcpy_from_be32(void *_dst, void *_s
+ dst[i] = be32_to_cpu(src[i]);
+ }
+
+-static inline void
+-fw_memcpy_to_be32(void *_dst, void *_src, size_t size)
++static inline void fw_memcpy_to_be32(void *_dst, void *_src, size_t size)
+ {
+ fw_memcpy_from_be32(_dst, _src, size);
+ }
+@@ -125,8 +124,7 @@ typedef void (*fw_packet_callback_t)(str
+ struct fw_card *card, int status);
+
+ typedef void (*fw_transaction_callback_t)(struct fw_card *card, int rcode,
+- void *data,
+- size_t length,
++ void *data, size_t length,
+ void *callback_data);
+
+ /*
+@@ -141,12 +139,6 @@ typedef void (*fw_address_callback_t)(st
+ void *data, size_t length,
+ void *callback_data);
+
+-typedef void (*fw_bus_reset_callback_t)(struct fw_card *handle,
+- int node_id, int generation,
+- u32 *self_ids,
+- int self_id_count,
+- void *callback_data);
+-
+ struct fw_packet {
+ int speed;
+ int generation;
+@@ -187,12 +179,6 @@ struct fw_transaction {
+ void *callback_data;
+ };
+
+-static inline struct fw_packet *
+-fw_packet(struct list_head *l)
+-{
+- return list_entry(l, struct fw_packet, link);
+-}
+-
+ struct fw_address_handler {
+ u64 offset;
+ size_t length;
+@@ -201,7 +187,6 @@ struct fw_address_handler {
+ struct list_head link;
+ };
+
+-
+ struct fw_address_region {
+ u64 start;
+ u64 end;
+@@ -315,10 +300,8 @@ struct fw_iso_packet {
+ struct fw_iso_context;
+
+ typedef void (*fw_iso_callback_t)(struct fw_iso_context *context,
+- u32 cycle,
+- size_t header_length,
+- void *header,
+- void *data);
++ u32 cycle, size_t header_length,
++ void *header, void *data);
+
+ /*
+ * An iso buffer is just a set of pages mapped for DMA in the
+@@ -344,36 +327,25 @@ struct fw_iso_context {
+ void *callback_data;
+ };
+
+-int
+-fw_iso_buffer_init(struct fw_iso_buffer *buffer,
+- struct fw_card *card,
+- int page_count,
+- enum dma_data_direction direction);
+-int
+-fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma);
+-void
+-fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, struct fw_card *card);
+-
+-struct fw_iso_context *
+-fw_iso_context_create(struct fw_card *card, int type,
+- int channel, int speed, size_t header_size,
+- fw_iso_callback_t callback, void *callback_data);
+-
+-void
+-fw_iso_context_destroy(struct fw_iso_context *ctx);
+-
+-int
+-fw_iso_context_queue(struct fw_iso_context *ctx,
+- struct fw_iso_packet *packet,
+- struct fw_iso_buffer *buffer,
+- unsigned long payload);
+-
+-int
+-fw_iso_context_start(struct fw_iso_context *ctx,
+- int cycle, int sync, int tags);
++int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card,
++ int page_count, enum dma_data_direction direction);
++int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma);
++void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, struct fw_card *card);
++
++struct fw_iso_context *fw_iso_context_create(struct fw_card *card,
++ int type, int channel, int speed, size_t header_size,
++ fw_iso_callback_t callback, void *callback_data);
++int fw_iso_context_queue(struct fw_iso_context *ctx,
++ struct fw_iso_packet *packet,
++ struct fw_iso_buffer *buffer,
++ unsigned long payload);
++int fw_iso_context_start(struct fw_iso_context *ctx,
++ int cycle, int sync, int tags);
++int fw_iso_context_stop(struct fw_iso_context *ctx);
++void fw_iso_context_destroy(struct fw_iso_context *ctx);
+
+-int
+-fw_iso_context_stop(struct fw_iso_context *ctx);
++void fw_iso_resource_manage(struct fw_card *card, int generation,
++ u64 channels_mask, int *channel, int *bandwidth, bool allocate);
+
+ struct fw_card_driver {
+ /*
+@@ -415,7 +387,7 @@ struct fw_card_driver {
+
+ struct fw_iso_context *
+ (*allocate_iso_context)(struct fw_card *card,
+- int type, size_t header_size);
++ int type, int channel, size_t header_size);
+ void (*free_iso_context)(struct fw_iso_context *ctx);
+
+ int (*start_iso)(struct fw_iso_context *ctx,
+@@ -429,24 +401,18 @@ struct fw_card_driver {
+ int (*stop_iso)(struct fw_iso_context *ctx);
+ };
+
+-int
+-fw_core_initiate_bus_reset(struct fw_card *card, int short_reset);
++int fw_core_initiate_bus_reset(struct fw_card *card, int short_reset);
+
+-void
+-fw_send_request(struct fw_card *card, struct fw_transaction *t,
++void fw_send_request(struct fw_card *card, struct fw_transaction *t,
+ int tcode, int destination_id, int generation, int speed,
+ unsigned long long offset, void *data, size_t length,
+ fw_transaction_callback_t callback, void *callback_data);
+-
+-int fw_run_transaction(struct fw_card *card, int tcode, int destination_id,
+- int generation, int speed, unsigned long long offset,
+- void *data, size_t length);
+-
+ int fw_cancel_transaction(struct fw_card *card,
+ struct fw_transaction *transaction);
+-
+ void fw_flush_transactions(struct fw_card *card);
+-
++int fw_run_transaction(struct fw_card *card, int tcode, int destination_id,
++ int generation, int speed, unsigned long long offset,
++ void *data, size_t length);
+ void fw_send_phy_config(struct fw_card *card,
+ int node_id, int generation, int gap_count);
+
+@@ -454,29 +420,18 @@ void fw_send_phy_config(struct fw_card *
+ * Called by the topology code to inform the device code of node
+ * activity; found, lost, or updated nodes.
+ */
+-void
+-fw_node_event(struct fw_card *card, struct fw_node *node, int event);
++void fw_node_event(struct fw_card *card, struct fw_node *node, int event);
+
+ /* API used by card level drivers */
+
+-void
+-fw_card_initialize(struct fw_card *card, const struct fw_card_driver *driver,
+- struct device *device);
+-int
+-fw_card_add(struct fw_card *card,
+- u32 max_receive, u32 link_speed, u64 guid);
+-
+-void
+-fw_core_remove_card(struct fw_card *card);
+-
+-void
+-fw_core_handle_bus_reset(struct fw_card *card,
+- int node_id, int generation,
+- int self_id_count, u32 *self_ids);
+-void
+-fw_core_handle_request(struct fw_card *card, struct fw_packet *request);
+-
+-void
+-fw_core_handle_response(struct fw_card *card, struct fw_packet *packet);
++void fw_card_initialize(struct fw_card *card,
++ const struct fw_card_driver *driver, struct device *device);
++int fw_card_add(struct fw_card *card,
++ u32 max_receive, u32 link_speed, u64 guid);
++void fw_core_remove_card(struct fw_card *card);
++void fw_core_handle_bus_reset(struct fw_card *card, int node_id,
++ int generation, int self_id_count, u32 *self_ids);
++void fw_core_handle_request(struct fw_card *card, struct fw_packet *request);
++void fw_core_handle_response(struct fw_card *card, struct fw_packet *packet);
+
+ #endif /* __fw_transaction_h */
+--- linux-2.6-git/include/linux/firewire-cdev.h 2008-11-04 11:19:21.000000000 -0500
++++ firewire-git/include/linux/firewire-cdev.h 2009-01-30 13:35:54.327647015 -0500
+@@ -25,10 +25,12 @@
+ #include <linux/types.h>
+ #include <linux/firewire-constants.h>
+
+-#define FW_CDEV_EVENT_BUS_RESET 0x00
+-#define FW_CDEV_EVENT_RESPONSE 0x01
+-#define FW_CDEV_EVENT_REQUEST 0x02
+-#define FW_CDEV_EVENT_ISO_INTERRUPT 0x03
++#define FW_CDEV_EVENT_BUS_RESET 0x00
++#define FW_CDEV_EVENT_RESPONSE 0x01
++#define FW_CDEV_EVENT_REQUEST 0x02
++#define FW_CDEV_EVENT_ISO_INTERRUPT 0x03
++#define FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED 0x04
++#define FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED 0x05
+
+ /**
+ * struct fw_cdev_event_common - Common part of all fw_cdev_event_ types
+@@ -136,7 +138,24 @@ struct fw_cdev_event_request {
+ * This event is sent when the controller has completed an &fw_cdev_iso_packet
+ * with the %FW_CDEV_ISO_INTERRUPT bit set. In the receive case, the headers
+ * stripped of all packets up until and including the interrupt packet are
+- * returned in the @header field.
++ * returned in the @header field. The amount of header data per packet is as
++ * specified at iso context creation by &fw_cdev_create_iso_context.header_size.
++ *
++ * In version 1 of this ABI, header data consisted of the 1394 isochronous
++ * packet header, followed by quadlets from the packet payload if
++ * &fw_cdev_create_iso_context.header_size > 4.
++ *
++ * In version 2 of this ABI, header data consist of the 1394 isochronous
++ * packet header, followed by a timestamp quadlet if
++ * &fw_cdev_create_iso_context.header_size > 4, followed by quadlets from the
++ * packet payload if &fw_cdev_create_iso_context.header_size > 8.
++ *
++ * Behaviour of ver. 1 of this ABI is no longer available since ABI ver. 2.
++ *
++ * Format of 1394 iso packet header: 16 bits len, 2 bits tag, 6 bits channel,
++ * 4 bits tcode, 4 bits sy, in big endian byte order. Format of timestamp:
++ * 16 bits invalid, 3 bits cycleSeconds, 13 bits cycleCount, in big endian byte
++ * order.
+ */
+ struct fw_cdev_event_iso_interrupt {
+ __u64 closure;
+@@ -147,12 +166,44 @@ struct fw_cdev_event_iso_interrupt {
+ };
+
+ /**
++ * struct fw_cdev_event_iso_resource - Iso resources were allocated or freed
++ * @closure: See &fw_cdev_event_common;
++ * set by %FW_CDEV_IOC_(DE)ALLOCATE_ISO_RESOURCE(_ONCE) ioctl
++ * @type: %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or
++ * %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED
++ * @handle: Reference by which an allocated resource can be deallocated
++ * @channel: Isochronous channel which was (de)allocated, if any
++ * @bandwidth: Bandwidth allocation units which were (de)allocated, if any
++ *
++ * An %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED event is sent after an isochronous
++ * resource was allocated at the IRM. The client has to check @channel and
++ * @bandwidth for whether the allocation actually succeeded.
++ *
++ * An %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event is sent after an isochronous
++ * resource was deallocated at the IRM. It is also sent when automatic
++ * reallocation after a bus reset failed.
++ *
++ * @channel is <0 if no channel was (de)allocated or if reallocation failed.
++ * @bandwidth is 0 if no bandwidth was (de)allocated or if reallocation failed.
++ */
++struct fw_cdev_event_iso_resource {
++ __u64 closure;
++ __u32 type;
++ __u32 handle;
++ __s32 channel;
++ __s32 bandwidth;
++};
++
++/**
+ * union fw_cdev_event - Convenience union of fw_cdev_event_ types
+ * @common: Valid for all types
+ * @bus_reset: Valid if @common.type == %FW_CDEV_EVENT_BUS_RESET
+ * @response: Valid if @common.type == %FW_CDEV_EVENT_RESPONSE
+ * @request: Valid if @common.type == %FW_CDEV_EVENT_REQUEST
+ * @iso_interrupt: Valid if @common.type == %FW_CDEV_EVENT_ISO_INTERRUPT
++ * @iso_resource: Valid if @common.type ==
++ * %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or
++ * %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED
+ *
+ * Convenience union for userspace use. Events could be read(2) into an
+ * appropriately aligned char buffer and then cast to this union for further
+@@ -163,13 +214,15 @@ struct fw_cdev_event_iso_interrupt {
+ * not fit will be discarded so that the next read(2) will return a new event.
+ */
+ union fw_cdev_event {
+- struct fw_cdev_event_common common;
+- struct fw_cdev_event_bus_reset bus_reset;
+- struct fw_cdev_event_response response;
+- struct fw_cdev_event_request request;
+- struct fw_cdev_event_iso_interrupt iso_interrupt;
++ struct fw_cdev_event_common common;
++ struct fw_cdev_event_bus_reset bus_reset;
++ struct fw_cdev_event_response response;
++ struct fw_cdev_event_request request;
++ struct fw_cdev_event_iso_interrupt iso_interrupt;
++ struct fw_cdev_event_iso_resource iso_resource;
+ };
+
++/* available since kernel version 2.6.22 */
+ #define FW_CDEV_IOC_GET_INFO _IOWR('#', 0x00, struct fw_cdev_get_info)
+ #define FW_CDEV_IOC_SEND_REQUEST _IOW('#', 0x01, struct fw_cdev_send_request)
+ #define FW_CDEV_IOC_ALLOCATE _IOWR('#', 0x02, struct fw_cdev_allocate)
+@@ -178,18 +231,29 @@ union fw_cdev_event {
+ #define FW_CDEV_IOC_INITIATE_BUS_RESET _IOW('#', 0x05, struct fw_cdev_initiate_bus_reset)
+ #define FW_CDEV_IOC_ADD_DESCRIPTOR _IOWR('#', 0x06, struct fw_cdev_add_descriptor)
+ #define FW_CDEV_IOC_REMOVE_DESCRIPTOR _IOW('#', 0x07, struct fw_cdev_remove_descriptor)
+-
+ #define FW_CDEV_IOC_CREATE_ISO_CONTEXT _IOWR('#', 0x08, struct fw_cdev_create_iso_context)
+ #define FW_CDEV_IOC_QUEUE_ISO _IOWR('#', 0x09, struct fw_cdev_queue_iso)
+ #define FW_CDEV_IOC_START_ISO _IOW('#', 0x0a, struct fw_cdev_start_iso)
+ #define FW_CDEV_IOC_STOP_ISO _IOW('#', 0x0b, struct fw_cdev_stop_iso)
++
++/* available since kernel version 2.6.24 */
+ #define FW_CDEV_IOC_GET_CYCLE_TIMER _IOR('#', 0x0c, struct fw_cdev_get_cycle_timer)
+
+-/* FW_CDEV_VERSION History
+- *
+- * 1 Feb 18, 2007: Initial version.
++/* available since kernel version 2.6.30 */
++#define FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE _IOWR('#', 0x0d, struct fw_cdev_allocate_iso_resource)
++#define FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE _IOW('#', 0x0e, struct fw_cdev_deallocate)
++#define FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE _IOW('#', 0x0f, struct fw_cdev_allocate_iso_resource)
++#define FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE_ONCE _IOW('#', 0x10, struct fw_cdev_allocate_iso_resource)
++#define FW_CDEV_IOC_GET_SPEED _IOR('#', 0x11, struct fw_cdev_get_speed)
++#define FW_CDEV_IOC_SEND_BROADCAST_REQUEST _IOW('#', 0x12, struct fw_cdev_send_request)
++
++/*
++ * FW_CDEV_VERSION History
++ * 1 (2.6.22) - initial version
++ * 2 (2.6.30) - changed &fw_cdev_event_iso_interrupt.header if
++ * &fw_cdev_create_iso_context.header_size is 8 or more
+ */
+-#define FW_CDEV_VERSION 1
++#define FW_CDEV_VERSION 2
+
+ /**
+ * struct fw_cdev_get_info - General purpose information ioctl
+@@ -201,7 +265,7 @@ union fw_cdev_event {
+ * case, @rom_length is updated with the actual length of the
+ * configuration ROM.
+ * @rom: If non-zero, address of a buffer to be filled by a copy of the
+- * local node's configuration ROM
++ * device's configuration ROM
+ * @bus_reset: If non-zero, address of a buffer to be filled by a
+ * &struct fw_cdev_event_bus_reset with the current state
+ * of the bus. This does not cause a bus reset to happen.
+@@ -229,7 +293,7 @@ struct fw_cdev_get_info {
+ * Send a request to the device. This ioctl implements all outgoing requests.
+ * Both quadlet and block request specify the payload as a pointer to the data
+ * in the @data field. Once the transaction completes, the kernel writes an
+- * &fw_cdev_event_request event back. The @closure field is passed back to
++ * &fw_cdev_event_response event back. The @closure field is passed back to
+ * user space in the response event.
+ */
+ struct fw_cdev_send_request {
+@@ -284,9 +348,9 @@ struct fw_cdev_allocate {
+ };
+
+ /**
+- * struct fw_cdev_deallocate - Free an address range allocation
+- * @handle: Handle to the address range, as returned by the kernel when the
+- * range was allocated
++ * struct fw_cdev_deallocate - Free a CSR address range or isochronous resource
++ * @handle: Handle to the address range or iso resource, as returned by the
++ * kernel when the range or resource was allocated
+ */
+ struct fw_cdev_deallocate {
+ __u32 handle;
+@@ -370,6 +434,9 @@ struct fw_cdev_remove_descriptor {
+ *
+ * If a context was successfully created, the kernel writes back a handle to the
+ * context, which must be passed in for subsequent operations on that context.
++ *
++ * Note that the effect of a @header_size > 4 depends on
++ * &fw_cdev_get_info.version, as documented at &fw_cdev_event_iso_interrupt.
+ */
+ struct fw_cdev_create_iso_context {
+ __u32 type;
+@@ -473,10 +540,73 @@ struct fw_cdev_stop_iso {
+ * The %FW_CDEV_IOC_GET_CYCLE_TIMER ioctl reads the isochronous cycle timer
+ * and also the system clock. This allows to express the receive time of an
+ * isochronous packet as a system time with microsecond accuracy.
++ *
++ * @cycle_timer consists of 7 bits cycleSeconds, 13 bits cycleCount, and
++ * 12 bits cycleOffset, in host byte order.
+ */
+ struct fw_cdev_get_cycle_timer {
+ __u64 local_time;
+ __u32 cycle_timer;
+ };
+
++/**
++ * struct fw_cdev_allocate_iso_resource - (De)allocate a channel or bandwidth
++ * @closure: Passed back to userspace in correponding iso resource events
++ * @channels: Isochronous channels of which one is to be (de)allocated
++ * @bandwidth: Isochronous bandwidth units to be (de)allocated
++ * @handle: Handle to the allocation, written by the kernel (only valid in
++ * case of %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE ioctls)
++ *
++ * The %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE ioctl initiates allocation of an
++ * isochronous channel and/or of isochronous bandwidth at the isochronous
++ * resource manager (IRM). Only one of the channels specified in @channels is
++ * allocated. An %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED is sent after
++ * communication with the IRM, indicating success or failure in the event data.
++ * The kernel will automatically reallocate the resources after bus resets.
++ * Should a reallocation fail, an %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event
++ * will be sent. The kernel will also automatically deallocate the resources
++ * when the file descriptor is closed.
++ *
++ * The %FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE ioctl can be used to initiate
++ * deallocation of resources which were allocated as described above.
++ * An %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event concludes this operation.
++ *
++ * The %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE ioctl is a variant of allocation
++ * without automatic re- or deallocation.
++ * An %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED event concludes this operation,
++ * indicating success or failure in its data.
++ *
++ * The %FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE_ONCE ioctl works like
++ * %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE except that resources are freed
++ * instead of allocated.
++ * An %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event concludes this operation.
++ *
++ * To summarize, %FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE allocates iso resources
++ * for the lifetime of the fd or handle.
++ * In contrast, %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE allocates iso resources
++ * for the duration of a bus generation.
++ *
++ * @channels is a host-endian bitfield with the least significant bit
++ * representing channel 0 and the most significant bit representing channel 63:
++ * 1ULL << c for each channel c that is a candidate for (de)allocation.
++ *
++ * @bandwidth is expressed in bandwidth allocation units, i.e. the time to send
++ * one quadlet of data (payload or header data) at speed S1600.
++ */
++struct fw_cdev_allocate_iso_resource {
++ __u64 closure;
++ __u64 channels;
++ __u32 bandwidth;
++ __u32 handle;
++};
++
++/**
++ * struct fw_cdev_get_speed - Query maximum speed to or from this device
++ * @max_speed: Speed code; minimum of the device's link speed, the local node's
++ * link speed, and all PHY port speeds between the two links
++ */
++struct fw_cdev_get_speed {
++ __u32 max_speed;
++};
++
+ #endif /* _LINUX_FIREWIRE_CDEV_H */
diff --git a/linux-2.6-hotfixes.patch b/linux-2.6-hotfixes.patch
new file mode 100644
index 000000000..494947322
--- /dev/null
+++ b/linux-2.6-hotfixes.patch
@@ -0,0 +1,15 @@
+fixes:
+implicit declaration of function kzalloc
+
+--- linux-2.6.34.noarch/drivers/usb/serial/qcserial.c~ 2010-06-08 15:19:41.000000000 -0400
++++ linux-2.6.34.noarch/drivers/usb/serial/qcserial.c 2010-06-08 15:19:47.000000000 -0400
+@@ -11,6 +11,7 @@
+ *
+ */
+
++#include <linux/slab.h>
+ #include <linux/tty.h>
+ #include <linux/tty_flip.h>
+ #include <linux/usb.h>
+
+
diff --git a/linux-2.6-i386-nx-emulation.patch b/linux-2.6-i386-nx-emulation.patch
new file mode 100644
index 000000000..a47e99d47
--- /dev/null
+++ b/linux-2.6-i386-nx-emulation.patch
@@ -0,0 +1,628 @@
+--- a/arch/x86/include/asm/desc.h
++++ b/arch/x86/include/asm/desc.h
+@@ -5,6 +5,7 @@
+ #include <asm/ldt.h>
+ #include <asm/mmu.h>
+ #include <linux/smp.h>
++#include <linux/mm_types.h>
+
+ static inline void fill_ldt(struct desc_struct *desc,
+ const struct user_desc *info)
+@@ -93,6 +94,9 @@ static inline int desc_empty(const void *ptr)
+
+ #define load_TLS(t, cpu) native_load_tls(t, cpu)
+ #define set_ldt native_set_ldt
++#ifdef CONFIG_X86_32
++#define load_user_cs_desc native_load_user_cs_desc
++#endif /*CONFIG_X86_32*/
+
+ #define write_ldt_entry(dt, entry, desc) \
+ native_write_ldt_entry(dt, entry, desc)
+@@ -392,4 +396,25 @@ static inline void set_system_intr_gate_ist(int n, void *addr, unsigned ist)
+ _set_gate(n, GATE_INTERRUPT, addr, 0x3, ist, __KERNEL_CS);
+ }
+
++#ifdef CONFIG_X86_32
++static inline void set_user_cs(struct desc_struct *desc, unsigned long limit)
++{
++ limit = (limit - 1) / PAGE_SIZE;
++ desc->a = limit & 0xffff;
++ desc->b = (limit & 0xf0000) | 0x00c0fb00;
++}
++
++static inline void native_load_user_cs_desc(int cpu, struct mm_struct *mm)
++{
++ get_cpu_gdt_table(cpu)[GDT_ENTRY_DEFAULT_USER_CS] = (mm)->context.user_cs;
++}
++
++#define arch_add_exec_range arch_add_exec_range
++#define arch_remove_exec_range arch_remove_exec_range
++#define arch_flush_exec_range arch_flush_exec_range
++extern void arch_add_exec_range(struct mm_struct *mm, unsigned long limit);
++extern void arch_remove_exec_range(struct mm_struct *mm, unsigned long limit);
++extern void arch_flush_exec_range(struct mm_struct *mm);
++#endif /* CONFIG_X86_32 */
++
+ #endif /* _ASM_X86_DESC_H */
+--- a/arch/x86/include/asm/mmu.h
++++ b/arch/x86/include/asm/mmu.h
+@@ -7,12 +7,19 @@
+ /*
+ * The x86 doesn't have a mmu context, but
+ * we put the segment information here.
++ *
++ * exec_limit is used to track the range PROT_EXEC
++ * mappings span.
+ */
+ typedef struct {
+ void *ldt;
+ int size;
+ struct mutex lock;
+ void *vdso;
++#ifdef CONFIG_X86_32
++ struct desc_struct user_cs;
++ unsigned long exec_limit;
++#endif
+ } mm_context_t;
+
+ #ifdef CONFIG_SMP
+--- a/arch/x86/include/asm/paravirt.h
++++ b/arch/x86/include/asm/paravirt.h
+@@ -289,6 +289,12 @@ static inline void set_ldt(const void *addr, unsigned entries)
+ {
+ PVOP_VCALL2(pv_cpu_ops.set_ldt, addr, entries);
+ }
++#ifdef CONFIG_X86_32
++static inline void load_user_cs_desc(unsigned int cpu, struct mm_struct *mm)
++{
++ PVOP_VCALL2(pv_cpu_ops.load_user_cs_desc, cpu, mm);
++}
++#endif /*CONFIG_X86_32*/
+ static inline void store_gdt(struct desc_ptr *dtr)
+ {
+ PVOP_VCALL1(pv_cpu_ops.store_gdt, dtr);
+--- a/arch/x86/include/asm/paravirt_types.h
++++ b/arch/x86/include/asm/paravirt_types.h
+@@ -118,6 +118,9 @@ struct pv_cpu_ops {
+ void (*store_gdt)(struct desc_ptr *);
+ void (*store_idt)(struct desc_ptr *);
+ void (*set_ldt)(const void *desc, unsigned entries);
++#ifdef CONFIG_X86_32
++ void (*load_user_cs_desc)(int cpu, struct mm_struct *mm);
++#endif
+ unsigned long (*store_tr)(void);
+ void (*load_tls)(struct thread_struct *t, unsigned int cpu);
+ #ifdef CONFIG_X86_64
+--- a/arch/x86/kernel/cpu/common.c
++++ b/arch/x86/kernel/cpu/common.c
+@@ -802,6 +802,22 @@ static void __cpuinit identify_cpu(struct cpuinfo_x86 *c)
+ /* Filter out anything that depends on CPUID levels we don't have */
+ filter_cpuid_features(c, true);
+
++#ifdef CONFIG_X86_32
++ /*
++ * emulation of NX with segment limits unfortunately means
++ * we have to disable the fast system calls, due to the way that
++ * sysexit clears the segment limits on return.
++ * If we have either disabled exec-shield on the boot command line,
++ * or we have NX, then we don't need to do this.
++ */
++ if (exec_shield != 0) {
++#ifdef CONFIG_X86_PAE
++ if (!test_cpu_cap(c, X86_FEATURE_NX))
++#endif
++ clear_cpu_cap(c, X86_FEATURE_SEP);
++ }
++#endif
++
+ /* If the model name is still unset, do table lookup. */
+ if (!c->x86_model_id[0]) {
+ const char *p;
+--- a/arch/x86/kernel/paravirt.c
++++ b/arch/x86/kernel/paravirt.c
+@@ -345,6 +345,9 @@ struct pv_cpu_ops pv_cpu_ops = {
+ .read_tscp = native_read_tscp,
+ .load_tr_desc = native_load_tr_desc,
+ .set_ldt = native_set_ldt,
++#ifdef CONFIG_X86_32
++ .load_user_cs_desc = native_load_user_cs_desc,
++#endif /*CONFIG_X86_32*/
+ .load_gdt = native_load_gdt,
+ .load_idt = native_load_idt,
+ .store_gdt = native_store_gdt,
+--- a/arch/x86/kernel/process_32.c
++++ b/arch/x86/kernel/process_32.c
+@@ -243,7 +243,10 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
+ void
+ start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
+ {
++ int cpu;
++
+ set_user_gs(regs, 0);
++
+ regs->fs = 0;
+ set_fs(USER_DS);
+ regs->ds = __USER_DS;
+@@ -252,6 +255,11 @@ start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
+ regs->cs = __USER_CS;
+ regs->ip = new_ip;
+ regs->sp = new_sp;
++
++ cpu = get_cpu();
++ load_user_cs_desc(cpu, current->mm);
++ put_cpu();
++
+ /*
+ * Free the old FP and other extended state
+ */
+@@ -311,6 +319,9 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
+ if (preload_fpu)
+ prefetch(next->fpu.state);
+
++ if (next_p->mm)
++ load_user_cs_desc(cpu, next_p->mm);
++
+ /*
+ * Reload esp0.
+ */
+@@ -404,3 +415,40 @@ unsigned long get_wchan(struct task_struct *p)
+ return 0;
+ }
+
++static void modify_cs(struct mm_struct *mm, unsigned long limit)
++{
++ mm->context.exec_limit = limit;
++ set_user_cs(&mm->context.user_cs, limit);
++ if (mm == current->mm) {
++ int cpu;
++
++ cpu = get_cpu();
++ load_user_cs_desc(cpu, mm);
++ put_cpu();
++ }
++}
++
++void arch_add_exec_range(struct mm_struct *mm, unsigned long limit)
++{
++ if (limit > mm->context.exec_limit)
++ modify_cs(mm, limit);
++}
++
++void arch_remove_exec_range(struct mm_struct *mm, unsigned long old_end)
++{
++ struct vm_area_struct *vma;
++ unsigned long limit = PAGE_SIZE;
++
++ if (old_end == mm->context.exec_limit) {
++ for (vma = mm->mmap; vma; vma = vma->vm_next)
++ if ((vma->vm_flags & VM_EXEC) && (vma->vm_end > limit))
++ limit = vma->vm_end;
++ modify_cs(mm, limit);
++ }
++}
++
++void arch_flush_exec_range(struct mm_struct *mm)
++{
++ mm->context.exec_limit = 0;
++ set_user_cs(&mm->context.user_cs, 0);
++}
+--- a/arch/x86/kernel/traps.c
++++ b/arch/x86/kernel/traps.c
+@@ -109,6 +109,78 @@ static inline void preempt_conditional_cli(struct pt_regs *regs)
+ dec_preempt_count();
+ }
+
++#ifdef CONFIG_X86_32
++static inline int
++__compare_user_cs_desc(const struct desc_struct *desc1,
++ const struct desc_struct *desc2)
++{
++ return ((desc1->limit0 != desc2->limit0) ||
++ (desc1->limit != desc2->limit) ||
++ (desc1->base0 != desc2->base0) ||
++ (desc1->base1 != desc2->base1) ||
++ (desc1->base2 != desc2->base2));
++}
++
++/*
++ * lazy-check for CS validity on exec-shield binaries:
++ *
++ * the original non-exec stack patch was written by
++ * Solar Designer <solar at openwall.com>. Thanks!
++ */
++static int
++check_lazy_exec_limit(int cpu, struct pt_regs *regs, long error_code)
++{
++ struct desc_struct *desc1, *desc2;
++ struct vm_area_struct *vma;
++ unsigned long limit;
++
++ if (current->mm == NULL)
++ return 0;
++
++ limit = -1UL;
++ if (current->mm->context.exec_limit != -1UL) {
++ limit = PAGE_SIZE;
++ spin_lock(&current->mm->page_table_lock);
++ for (vma = current->mm->mmap; vma; vma = vma->vm_next)
++ if ((vma->vm_flags & VM_EXEC) && (vma->vm_end > limit))
++ limit = vma->vm_end;
++ vma = get_gate_vma(current);
++ if (vma && (vma->vm_flags & VM_EXEC) && (vma->vm_end > limit))
++ limit = vma->vm_end;
++ spin_unlock(&current->mm->page_table_lock);
++ if (limit >= TASK_SIZE)
++ limit = -1UL;
++ current->mm->context.exec_limit = limit;
++ }
++ set_user_cs(&current->mm->context.user_cs, limit);
++
++ desc1 = &current->mm->context.user_cs;
++ desc2 = get_cpu_gdt_table(cpu) + GDT_ENTRY_DEFAULT_USER_CS;
++
++ if (__compare_user_cs_desc(desc1, desc2)) {
++ /*
++ * The CS was not in sync - reload it and retry the
++ * instruction. If the instruction still faults then
++ * we won't hit this branch next time around.
++ */
++ if (print_fatal_signals >= 2) {
++ printk(KERN_ERR "#GPF fixup (%ld[seg:%lx]) at %08lx, CPU#%d.\n",
++ error_code, error_code/8, regs->ip,
++ smp_processor_id());
++ printk(KERN_ERR "exec_limit: %08lx, user_cs: %08x/%08x, CPU_cs: %08x/%08x.\n",
++ current->mm->context.exec_limit,
++ desc1->a, desc1->b, desc2->a, desc2->b);
++ }
++
++ load_user_cs_desc(cpu, current->mm);
++
++ return 1;
++ }
++
++ return 0;
++}
++#endif
++
+ static void __kprobes
+ do_trap(int trapnr, int signr, char *str, struct pt_regs *regs,
+ long error_code, siginfo_t *info)
+@@ -265,6 +337,29 @@ do_general_protection(struct pt_regs *regs, long error_code)
+ if (!user_mode(regs))
+ goto gp_in_kernel;
+
++#ifdef CONFIG_X86_32
++{
++ int cpu;
++ int ok;
++
++ cpu = get_cpu();
++ ok = check_lazy_exec_limit(cpu, regs, error_code);
++ put_cpu();
++
++ if (ok)
++ return;
++
++ if (print_fatal_signals) {
++ printk(KERN_ERR "#GPF(%ld[seg:%lx]) at %08lx, CPU#%d.\n",
++ error_code, error_code/8, regs->ip, smp_processor_id());
++ printk(KERN_ERR "exec_limit: %08lx, user_cs: %08x/%08x.\n",
++ current->mm->context.exec_limit,
++ current->mm->context.user_cs.a,
++ current->mm->context.user_cs.b);
++ }
++}
++#endif /*CONFIG_X86_32*/
++
+ tsk->thread.error_code = error_code;
+ tsk->thread.trap_no = 13;
+
+@@ -792,19 +887,37 @@ do_device_not_available(struct pt_regs *regs, long error_code)
+ }
+
+ #ifdef CONFIG_X86_32
++/*
++ * The fixup code for errors in iret jumps to here (iret_exc). It loses
++ * the original trap number and erorr code. The bogus trap 32 and error
++ * code 0 are what the vanilla kernel delivers via:
++ * DO_ERROR_INFO(32, SIGSEGV, "iret exception", iret_error, ILL_BADSTK, 0, 1)
++ *
++ * NOTE: Because of the final "1" in the macro we need to enable interrupts.
++ *
++ * In case of a general protection fault in the iret instruction, we
++ * need to check for a lazy CS update for exec-shield.
++ */
+ dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code)
+ {
+- siginfo_t info;
++ int ok;
++ int cpu;
++
+ local_irq_enable();
+
+- info.si_signo = SIGILL;
+- info.si_errno = 0;
+- info.si_code = ILL_BADSTK;
+- info.si_addr = NULL;
+- if (notify_die(DIE_TRAP, "iret exception",
+- regs, error_code, 32, SIGILL) == NOTIFY_STOP)
+- return;
+- do_trap(32, SIGILL, "iret exception", regs, error_code, &info);
++ cpu = get_cpu();
++ ok = check_lazy_exec_limit(cpu, regs, error_code);
++ put_cpu();
++
++ if (!ok && notify_die(DIE_TRAP, "iret exception", regs,
++ error_code, 32, SIGSEGV) != NOTIFY_STOP) {
++ siginfo_t info;
++ info.si_signo = SIGSEGV;
++ info.si_errno = 0;
++ info.si_code = ILL_BADSTK;
++ info.si_addr = 0;
++ do_trap(32, SIGSEGV, "iret exception", regs, error_code, &info);
++ }
+ }
+ #endif
+
+--- a/arch/x86/mm/setup_nx.c
++++ b/arch/x86/mm/setup_nx.c
+@@ -1,3 +1,4 @@
++#include <linux/sched.h>
+ #include <linux/spinlock.h>
+ #include <linux/errno.h>
+ #include <linux/init.h>
+@@ -23,6 +24,7 @@ static int __init noexec_setup(char *str)
+ disable_nx = 0;
+ } else if (!strncmp(str, "off", 3)) {
+ disable_nx = 1;
++ exec_shield = 0;
+ }
+ x86_configure_nx();
+ return 0;
+@@ -40,6 +42,10 @@ void __cpuinit x86_configure_nx(void)
+ void __init x86_report_nx(void)
+ {
+ if (!cpu_has_nx) {
++ if (exec_shield)
++ printk(KERN_INFO "Using x86 segment limits to approximate NX protection\n");
++ else
++
+ printk(KERN_NOTICE "Notice: NX (Execute Disable) protection "
+ "missing in CPU or disabled in BIOS!\n");
+ } else {
+--- a/arch/x86/mm/tlb.c
++++ b/arch/x86/mm/tlb.c
+@@ -6,6 +6,7 @@
+ #include <linux/interrupt.h>
+ #include <linux/module.h>
+
++#include <asm/desc.h>
+ #include <asm/tlbflush.h>
+ #include <asm/mmu_context.h>
+ #include <asm/cache.h>
+@@ -131,6 +132,12 @@ void smp_invalidate_interrupt(struct pt_regs *regs)
+ union smp_flush_state *f;
+
+ cpu = smp_processor_id();
++
++#ifdef CONFIG_X86_32
++ if (current->active_mm)
++ load_user_cs_desc(cpu, current->active_mm);
++#endif
++
+ /*
+ * orig_rax contains the negated interrupt vector.
+ * Use that to determine where the sender put the data.
+--- a/arch/x86/xen/enlighten.c
++++ b/arch/x86/xen/enlighten.c
+@@ -335,6 +335,24 @@ static void xen_set_ldt(const void *addr, unsigned entries)
+ xen_mc_issue(PARAVIRT_LAZY_CPU);
+ }
+
++#ifdef CONFIG_X86_32
++static void xen_load_user_cs_desc(int cpu, struct mm_struct *mm)
++{
++ void *gdt;
++ xmaddr_t mgdt;
++ u64 descriptor;
++ struct desc_struct user_cs;
++
++ gdt = &get_cpu_gdt_table(cpu)[GDT_ENTRY_DEFAULT_USER_CS];
++ mgdt = virt_to_machine(gdt);
++
++ user_cs = mm->context.user_cs;
++ descriptor = (u64) user_cs.a | ((u64) user_cs.b) << 32;
++
++ HYPERVISOR_update_descriptor(mgdt.maddr, descriptor);
++}
++#endif /*CONFIG_X86_32*/
++
+ static void xen_load_gdt(const struct desc_ptr *dtr)
+ {
+ unsigned long va = dtr->address;
+@@ -961,6 +979,9 @@ static const struct pv_cpu_ops xen_cpu_ops __initdata = {
+
+ .load_tr_desc = paravirt_nop,
+ .set_ldt = xen_set_ldt,
++#ifdef CONFIG_X86_32
++ .load_user_cs_desc = xen_load_user_cs_desc,
++#endif /*CONFIG_X86_32*/
+ .load_gdt = xen_load_gdt,
+ .load_idt = xen_load_idt,
+ .load_tls = xen_load_tls,
+--- a/fs/binfmt_elf.c
++++ b/fs/binfmt_elf.c
+@@ -717,6 +722,15 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
+ if (retval)
+ goto out_free_dentry;
+
++#ifdef CONFIG_X86_32
++ /*
++ * Turn off the CS limit completely if exec-shield disabled or
++ * NX active:
++ */
++ if (!exec_shield || executable_stack != EXSTACK_DISABLE_X || (__supported_pte_mask & _PAGE_NX))
++ arch_add_exec_range(current->mm, -1);
++#endif
++
+ /* OK, This is the point of no return */
+ current->flags &= ~PF_FORKNOEXEC;
+ current->mm->def_flags = def_flags;
+--- a/include/linux/sched.h
++++ b/include/linux/sched.h
+@@ -101,6 +101,9 @@ struct bio_list;
+ struct fs_struct;
+ struct perf_event_context;
+
++extern int exec_shield;
++extern int print_fatal_signals;
++
+ /*
+ * List of flags we want to share for kernel threads,
+ * if only because they are not used by them anyway.
+--- a/kernel/sysctl.c
++++ b/kernel/sysctl.c
+@@ -101,6 +101,17 @@ extern int sysctl_nr_open_min, sysctl_nr_open_max;
+ #ifndef CONFIG_MMU
+ extern int sysctl_nr_trim_pages;
+ #endif
++
++int exec_shield = 1;
++
++static int __init setup_exec_shield(char *str)
++{
++ get_option(&str, &exec_shield);
++
++ return 1;
++}
++__setup("exec-shield=", setup_exec_shield);
++
+ #ifdef CONFIG_BLOCK
+ extern int blk_iopoll_enabled;
+ #endif
+@@ -428,6 +448,16 @@ static struct ctl_table kern_table[] = {
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ },
++#ifdef CONFIG_X86_32
++ {
++ .procname = "exec-shield",
++ .data = &exec_shield,
++ .maxlen = sizeof(int),
++ .mode = 0644,
++ .proc_handler = &proc_dointvec,
++ },
++#endif
++
+ #ifdef CONFIG_PROC_SYSCTL
+ {
+ .procname = "tainted",
+--- b/mm/mmap.c
++++ b/mm/mmap.c
+@@ -44,6 +45,18 @@
+ #define arch_rebalance_pgtables(addr, len) (addr)
+ #endif
+
++/* No sane architecture will #define these to anything else */
++#ifndef arch_add_exec_range
++#define arch_add_exec_range(mm, limit) do { ; } while (0)
++#endif
++#ifndef arch_flush_exec_range
++#define arch_flush_exec_range(mm) do { ; } while (0)
++#endif
++#ifndef arch_remove_exec_range
++#define arch_remove_exec_range(mm, limit) do { ; } while (0)
++#endif
++
++
+ static void unmap_region(struct mm_struct *mm,
+ struct vm_area_struct *vma, struct vm_area_struct *prev,
+ unsigned long start, unsigned long end);
+@@ -388,6 +401,8 @@
+ __vma_link_list(struct mm_struct *mm, struct vm_area_struct *vma,
+ struct vm_area_struct *prev, struct rb_node *rb_parent)
+ {
++ if (vma->vm_flags & VM_EXEC)
++ arch_add_exec_range(mm, vma->vm_end);
+ if (prev) {
+ vma->vm_next = prev->vm_next;
+ prev->vm_next = vma;
+@@ -489,6 +504,8 @@
+ rb_erase(&vma->vm_rb, &mm->mm_rb);
+ if (mm->mmap_cache == vma)
+ mm->mmap_cache = prev;
++ if (vma->vm_flags & VM_EXEC)
++ arch_remove_exec_range(mm, vma->vm_end);
+ }
+
+ /*
+@@ -790,6 +807,8 @@
+ } else /* cases 2, 5, 7 */
+ err = vma_adjust(prev, prev->vm_start,
+ end, prev->vm_pgoff, NULL);
++ if (prev->vm_flags & VM_EXEC)
++ arch_add_exec_range(mm, prev->vm_end);
+ if (err)
+ return NULL;
+ return prev;
+@@ -1966,10 +2075,14 @@
+ if (new->vm_ops && new->vm_ops->open)
+ new->vm_ops->open(new);
+
+- if (new_below)
++ if (new_below) {
++ unsigned long old_end = vma->vm_end;
++
+ err = vma_adjust(vma, addr, vma->vm_end, vma->vm_pgoff +
+ ((addr - new->vm_start) >> PAGE_SHIFT), new);
+- else
++ if (vma->vm_flags & VM_EXEC)
++ arch_remove_exec_range(mm, old_end);
++ } else
+ err = vma_adjust(vma, vma->vm_start, addr, vma->vm_pgoff, new);
+
+ /* Success. */
+@@ -2254,6 +2367,7 @@
+
+ free_pgtables(tlb, vma, FIRST_USER_ADDRESS, 0);
+ tlb_finish_mmu(tlb, 0, end);
++ arch_flush_exec_range(mm);
+
+ /*
+ * Walk the list again, actually closing and freeing it,
+--- a/mm/mprotect.c
++++ b/mm/mprotect.c
+@@ -25,9 +25,14 @@
+ #include <linux/perf_event.h>
+ #include <asm/uaccess.h>
+ #include <asm/pgtable.h>
++#include <asm/pgalloc.h>
+ #include <asm/cacheflush.h>
+ #include <asm/tlbflush.h>
+
++#ifndef arch_remove_exec_range
++#define arch_remove_exec_range(mm, limit) do { ; } while (0)
++#endif
++
+ #ifndef pgprot_modify
+ static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot)
+ {
+@@ -138,7 +143,7 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev,
+ struct mm_struct *mm = vma->vm_mm;
+ unsigned long oldflags = vma->vm_flags;
+ long nrpages = (end - start) >> PAGE_SHIFT;
+- unsigned long charged = 0;
++ unsigned long charged = 0, old_end = vma->vm_end;
+ pgoff_t pgoff;
+ int error;
+ int dirty_accountable = 0;
+@@ -203,6 +208,9 @@ success:
+ dirty_accountable = 1;
+ }
+
++ if (oldflags & VM_EXEC)
++ arch_remove_exec_range(current->mm, old_end);
++
+ mmu_notifier_invalidate_range_start(mm, start, end);
+ if (is_vm_hugetlb_page(vma))
+ hugetlb_change_protection(vma, start, end, vma->vm_page_prot);
diff --git a/linux-2.6-input-kill-stupid-messages.patch b/linux-2.6-input-kill-stupid-messages.patch
new file mode 100644
index 000000000..cc1dd7470
--- /dev/null
+++ b/linux-2.6-input-kill-stupid-messages.patch
@@ -0,0 +1,32 @@
+From b2c6d55b2351152696aafb8c9bf3ec8968acf77c Mon Sep 17 00:00:00 2001
+From: Kyle McMartin <kyle@phobos.i.jkkm.org>
+Date: Mon, 29 Mar 2010 23:59:58 -0400
+Subject: linux-2.6-input-kill-stupid-messages
+
+---
+ drivers/input/keyboard/atkbd.c | 5 +++++
+ 1 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
+index d358ef8..38db098 100644
+--- a/drivers/input/keyboard/atkbd.c
++++ b/drivers/input/keyboard/atkbd.c
+@@ -425,11 +426,15 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
+ goto out;
+ case ATKBD_RET_ACK:
+ case ATKBD_RET_NAK:
++#if 0
++ /* Quite a few key switchers and other tools trigger this
++ * and it confuses people who can do nothing about it */
+ if (printk_ratelimit())
+ dev_warn(&serio->dev,
+ "Spurious %s on %s. "
+ "Some program might be trying access hardware directly.\n",
+ data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys);
++#endif
+ goto out;
+ case ATKBD_RET_ERR:
+ atkbd->err_count++;
+--
+1.7.0.1
+
diff --git a/linux-2.6-intel-iommu-igfx.patch b/linux-2.6-intel-iommu-igfx.patch
new file mode 100644
index 000000000..44fd14186
--- /dev/null
+++ b/linux-2.6-intel-iommu-igfx.patch
@@ -0,0 +1,78 @@
+Subject: [PATCH] [intel_iommu] Default to igfx_off
+From: drago01 <drago01@gmail.com>
+To: fedora-kernel-list <fedora-kernel-list@redhat.com>
+
+This option seems to causes way to many issues, it is
+being investigated by Intel's chipset team for months now and
+we still don't have any outcome.
+
+The results so far are "black screen when starting X",
+"system hangs when using GL", "system does not resume".
+
+The patch adds an intel_iommu=igfx_on option, which makes it opt in,
+rather than opt out.
+
+Signed-off-by: Adel Gadllah <adel.gadllah@gmail.com>
+Reviewed-by: Adam Jackson <ajax@redhat.com>
+---
+ Documentation/kernel-parameters.txt | 11 +++++------
+ drivers/pci/intel-iommu.c | 9 +++++----
+ 2 files changed, 10 insertions(+), 10 deletions(-)
+
+diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
+index e7848a0..9914485 100644
+--- a/Documentation/kernel-parameters.txt
++++ b/Documentation/kernel-parameters.txt
+@@ -992,12 +992,11 @@ and is between 256 and 4096 characters. It is defined in the file
+ Enable intel iommu driver.
+ off
+ Disable intel iommu driver.
+- igfx_off [Default Off]
+- By default, gfx is mapped as normal device. If a gfx
+- device has a dedicated DMAR unit, the DMAR unit is
+- bypassed by not enabling DMAR with this option. In
+- this case, gfx device will use physical address for
+- DMA.
++ igfx_on [Default Off]
++ By default, the gfx's DMAR unit is bypassed by not enabling
++ DMAR with this option. So the gfx device will use physical
++ address for DMA. When this option is enabled it the gfx is
++ mapped as normal device.
+ forcedac [x86_64]
+ With this option iommu will not optimize to look
+ for io virtual address below 32 bit forcing dual
+diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c
+index 4173125..8f36786 100644
+--- a/drivers/pci/intel-iommu.c
++++ b/drivers/pci/intel-iommu.c
+@@ -340,7 +340,8 @@ int dmar_disabled = 0;
+ int dmar_disabled = 1;
+ #endif /*CONFIG_DMAR_DEFAULT_ON*/
+
+-static int dmar_map_gfx = 1;
++/* disabled by default; causes way too many issues */
++static int dmar_map_gfx = 0;
+ static int dmar_forcedac;
+ static int intel_iommu_strict;
+
+@@ -361,10 +362,10 @@ static int __init intel_iommu_setup(char *str)
+ } else if (!strncmp(str, "off", 3)) {
+ dmar_disabled = 1;
+ printk(KERN_INFO "Intel-IOMMU: disabled\n");
+- } else if (!strncmp(str, "igfx_off", 8)) {
+- dmar_map_gfx = 0;
++ } else if (!strncmp(str, "igfx_on", 7)) {
++ dmar_map_gfx = 1;
+ printk(KERN_INFO
+- "Intel-IOMMU: disable GFX device mapping\n");
++ "Intel-IOMMU: enabled GFX device mapping\n");
+ } else if (!strncmp(str, "forcedac", 8)) {
+ printk(KERN_INFO
+ "Intel-IOMMU: Forcing DAC for PCI devices\n");
+--
+1.6.6.1
+_______________________________________________
+kernel mailing list
+kernel@lists.fedoraproject.org
+https://admin.fedoraproject.org/mailman/listinfo/kernel
+
diff --git a/linux-2.6-makefile-after_link.patch b/linux-2.6-makefile-after_link.patch
new file mode 100644
index 000000000..94b71f9b1
--- /dev/null
+++ b/linux-2.6-makefile-after_link.patch
@@ -0,0 +1,57 @@
+diff --git a/Makefile b/Makefile
+index f908acc..960ff6f 100644
+--- a/Makefile
++++ b/Makefile
+@@ -746,6 +746,10 @@ quiet_cmd_vmlinux__ ?= LD $@
+ --start-group $(vmlinux-main) --end-group \
+ $(filter-out $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o FORCE ,$^)
+
++ifdef AFTER_LINK
++cmd_vmlinux__ += ; $(AFTER_LINK)
++endif
++
+ # Generate new vmlinux version
+ quiet_cmd_vmlinux_version = GEN .version
+ cmd_vmlinux_version = set -e; \
+diff --git a/arch/powerpc/kernel/vdso32/Makefile b/arch/powerpc/kernel/vdso32/Makefile
+index 51ead52..ad21273 100644
+--- a/arch/powerpc/kernel/vdso32/Makefile
++++ b/arch/powerpc/kernel/vdso32/Makefile
+@@ -41,7 +41,8 @@ $(obj-vdso32): %.o: %.S
+
+ # actual build commands
+ quiet_cmd_vdso32ld = VDSO32L $@
+- cmd_vdso32ld = $(CROSS32CC) $(c_flags) -Wl,-T $^ -o $@
++ cmd_vdso32ld = $(CROSS32CC) $(c_flags) -Wl,-T $^ -o $@ \
++ $(if $(AFTER_LINK),; $(AFTER_LINK))
+ quiet_cmd_vdso32as = VDSO32A $@
+ cmd_vdso32as = $(CROSS32CC) $(a_flags) -c -o $@ $<
+
+diff --git a/arch/powerpc/kernel/vdso64/Makefile b/arch/powerpc/kernel/vdso64/Makefile
+index 79da65d..f11c21b 100644
+--- a/arch/powerpc/kernel/vdso64/Makefile
++++ b/arch/powerpc/kernel/vdso64/Makefile
+@@ -36,7 +36,8 @@ $(obj-vdso64): %.o: %.S
+
+ # actual build commands
+ quiet_cmd_vdso64ld = VDSO64L $@
+- cmd_vdso64ld = $(CC) $(c_flags) -Wl,-T $^ -o $@
++ cmd_vdso64ld = $(CC) $(c_flags) -Wl,-T $^ -o $@ \
++ $(if $(AFTER_LINK),; $(AFTER_LINK))
+ quiet_cmd_vdso64as = VDSO64A $@
+ cmd_vdso64as = $(CC) $(a_flags) -c -o $@ $<
+
+diff --git a/arch/x86/vdso/Makefile b/arch/x86/vdso/Makefile
+index 6b4ffed..cbc3d05 100644
+--- a/arch/x86/vdso/Makefile
++++ b/arch/x86/vdso/Makefile
+@@ -120,7 +120,8 @@ $(obj)/vdso32-syms.lds: $(vdso32.so-y:%=$(obj)/vdso32-%-syms.lds) FORCE
+ quiet_cmd_vdso = VDSO $@
+ cmd_vdso = $(CC) -nostdlib -o $@ \
+ $(VDSO_LDFLAGS) $(VDSO_LDFLAGS_$(filter %.lds,$(^F))) \
+- -Wl,-T,$(filter %.lds,$^) $(filter %.o,$^)
++ -Wl,-T,$(filter %.lds,$^) $(filter %.o,$^) \
++ $(if $(AFTER_LINK),; $(AFTER_LINK))
+
+ VDSO_LDFLAGS = -fPIC -shared $(call cc-ldoption, -Wl$(comma)--hash-style=sysv)
+ GCOV_PROFILE := n
diff --git a/linux-2.6-selinux-mprotect-checks.patch b/linux-2.6-selinux-mprotect-checks.patch
new file mode 100644
index 000000000..010a63c43
--- /dev/null
+++ b/linux-2.6-selinux-mprotect-checks.patch
@@ -0,0 +1,124 @@
+This needs a fixed toolchain, and a userspace rebuild to work.
+For these reasons, it's had difficulty getting upstream.
+
+ie, Fedora has a new enough toolchain, and has been rebuilt, so we don't need
+the ifdefs. Other distros don't/haven't, and this patch would break them
+if pushed upstream.
+
+
+Subject: [Fwd: Re: [PATCH] Disable execmem for sparc]
+From: Stephen Smalley <sds@tycho.nsa.gov>
+To: Dave Jones <davej@redhat.com>
+Date: Wed, 28 Apr 2010 16:04:56 -0400
+Message-Id: <1272485096.6013.326.camel@moss-pluto.epoch.ncsc.mil>
+
+-------- Forwarded Message --------
+From: Stephen Smalley <sds@tycho.nsa.gov>
+To: David Miller <davem@davemloft.net>
+Cc: tcallawa@redhat.com, dennis@ausil.us, sparclinux@vger.kernel.org, dgilmore@redhat.com, jmorris@namei.org, eparis@parisplace.org
+Subject: Re: [PATCH] Disable execmem for sparc
+Date: Wed, 28 Apr 2010 15:57:57 -0400
+
+On Tue, 2010-04-27 at 11:47 -0700, David Miller wrote:
+> From: "Tom \"spot\" Callaway" <tcallawa@redhat.com>
+> Date: Tue, 27 Apr 2010 14:20:21 -0400
+>
+> > [root@apollo ~]$ cat /proc/2174/maps
+> > 00010000-00014000 r-xp 00000000 fd:00 15466577
+> > /sbin/mingetty
+> > 00022000-00024000 rwxp 00002000 fd:00 15466577
+> > /sbin/mingetty
+> > 00024000-00046000 rwxp 00000000 00:00 0
+> > [heap]
+>
+> SELINUX probably barfs on the executable heap, the PLT is in the HEAP
+> just like powerpc32 and that's why VM_DATA_DEFAULT_FLAGS has to set
+> both executable and writable.
+>
+> You also can't remove the CONFIG_PPC32 ifdefs in selinux, since
+> because of the VM_DATA_DEFAULT_FLAGS setting used still in that arch,
+> the heap will always have executable permission, just like sparc does.
+> You have to support those binaries forever, whether you like it or not.
+>
+> Let's just replace the CONFIG_PPC32 ifdef in SELINUX with CONFIG_PPC32
+> || CONFIG_SPARC as in Tom's original patch and let's be done with
+> this.
+>
+> In fact I would go through all the arch/ header files and check the
+> VM_DATA_DEFAULT_FLAGS settings and add the necessary new ifdefs to the
+> SELINUX code so that other platforms don't have the pain of having to
+> go through this process too.
+
+To avoid maintaining per-arch ifdefs, it seems that we could just
+directly use (VM_DATA_DEFAULT_FLAGS & VM_EXEC) as the basis for deciding
+whether to enable or disable these checks. VM_DATA_DEFAULT_FLAGS isn't
+constant on some architectures but instead depends on
+current->personality, but we want this applied uniformly. So we'll just
+use the initial task state to determine whether or not to enable these
+checks.
+
+Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>
+
+diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
+index ebee467..a03fd74 100644
+--- a/security/selinux/hooks.c
++++ b/security/selinux/hooks.c
+@@ -2999,13 +2999,15 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
+ return file_has_perm(cred, file, av);
+ }
+
++static int default_noexec;
++
+ static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
+ {
+ const struct cred *cred = current_cred();
+ int rc = 0;
+
+-#ifndef CONFIG_PPC32
+- if ((prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) {
++ if (default_noexec &&
++ (prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) {
+ /*
+ * We are making executable an anonymous mapping or a
+ * private file mapping that will also be writable.
+@@ -3015,7 +3017,6 @@ static int file_map_prot_check(struct file *file, unsigned long prot, int shared
+ if (rc)
+ goto error;
+ }
+-#endif
+
+ if (file) {
+ /* read access is always possible with a mapping */
+@@ -3076,8 +3077,8 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
+ if (selinux_checkreqprot)
+ prot = reqprot;
+
+-#ifndef CONFIG_PPC32
+- if ((prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
++ if (default_noexec &&
++ (prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
+ int rc = 0;
+ if (vma->vm_start >= vma->vm_mm->start_brk &&
+ vma->vm_end <= vma->vm_mm->brk) {
+@@ -3099,7 +3100,6 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
+ if (rc)
+ return rc;
+ }
+-#endif
+
+ return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED);
+ }
+@@ -5662,6 +5662,8 @@ static __init int selinux_init(void)
+ /* Set the security state for the initial task. */
+ cred_init_security();
+
++ default_noexec = !(VM_DATA_DEFAULT_FLAGS & VM_EXEC);
++
+ sel_inode_cache = kmem_cache_create("selinux_inode_security",
+ sizeof(struct inode_security_struct),
+ 0, SLAB_PANIC, NULL);
+
+--
+Stephen Smalley
+National Security Agency
+
diff --git a/linux-2.6-serial-460800.patch b/linux-2.6-serial-460800.patch
new file mode 100644
index 000000000..17d67ef64
--- /dev/null
+++ b/linux-2.6-serial-460800.patch
@@ -0,0 +1,70 @@
+diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
+index 2209620..659c1bb 100644
+--- a/drivers/serial/8250.c
++++ b/drivers/serial/8250.c
+@@ -7,6 +7,9 @@
+ *
+ * Copyright (C) 2001 Russell King.
+ *
++ * 2005/09/16: Enabled higher baud rates for 16C95x.
++ * (Mathias Adam <a2@adamis.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
+@@ -2227,6 +2230,14 @@ static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int
+ else if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
+ baud == (port->uartclk/8))
+ quot = 0x8002;
++ /*
++ * For 16C950s UART_TCR is used in combination with divisor==1
++ * to achieve baud rates up to baud_base*4.
++ */
++ else if ((port->type == PORT_16C950) &&
++ baud > (port->uartclk/16))
++ quot = 1;
++
+ else
+ quot = uart_get_divisor(port, baud);
+
+@@ -2240,7 +2251,7 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct uart_8250_port *up = (struct uart_8250_port *)port;
+ unsigned char cval, fcr = 0;
+ unsigned long flags;
+- unsigned int baud, quot;
++ unsigned int baud, quot, max_baud;
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+@@ -2272,9 +2283,10 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
+ /*
+ * Ask the core to calculate the divisor for us.
+ */
++ max_baud = (up->port.type == PORT_16C950 ? port->uartclk/4 : port->uartclk/16);
+ baud = uart_get_baud_rate(port, termios, old,
+ port->uartclk / 16 / 0xffff,
+- port->uartclk / 16);
++ max_baud);
+ quot = serial8250_get_divisor(port, baud);
+
+ /*
+@@ -2311,6 +2323,19 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
+ spin_lock_irqsave(&up->port.lock, flags);
+
+ /*
++ * 16C950 supports additional prescaler ratios between 1:16 and 1:4
++ * thus increasing max baud rate to uartclk/4.
++ */
++ if (up->port.type == PORT_16C950) {
++ if (baud == port->uartclk/4)
++ serial_icr_write(up, UART_TCR, 0x4);
++ else if (baud == port->uartclk/8)
++ serial_icr_write(up, UART_TCR, 0x8);
++ else
++ serial_icr_write(up, UART_TCR, 0);
++ }
++
++ /*
+ * Update the per-port timeout.
+ */
+ uart_update_timeout(port, termios->c_cflag, baud);
diff --git a/linux-2.6-silence-acpi-blacklist.patch b/linux-2.6-silence-acpi-blacklist.patch
new file mode 100644
index 000000000..c5997bb6e
--- /dev/null
+++ b/linux-2.6-silence-acpi-blacklist.patch
@@ -0,0 +1,25 @@
+diff -up linux-2.6.26.noarch/drivers/acpi/blacklist.c.jx linux-2.6.26.noarch/drivers/acpi/blacklist.c
+--- linux-2.6.26.noarch/drivers/acpi/blacklist.c.jx 2008-07-13 17:51:29.000000000 -0400
++++ linux-2.6.26.noarch/drivers/acpi/blacklist.c 2008-08-12 14:21:39.000000000 -0400
+@@ -81,18 +81,18 @@ static int __init blacklist_by_year(void
+
+ /* Doesn't exist? Likely an old system */
+ if (!dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL)) {
+- printk(KERN_ERR PREFIX "no DMI BIOS year, "
++ printk(KERN_INFO PREFIX "no DMI BIOS year, "
+ "acpi=force is required to enable ACPI\n" );
+ return 1;
+ }
+ /* 0? Likely a buggy new BIOS */
+ if (year == 0) {
+- printk(KERN_ERR PREFIX "DMI BIOS year==0, "
++ printk(KERN_INFO PREFIX "DMI BIOS year==0, "
+ "assuming ACPI-capable machine\n" );
+ return 0;
+ }
+ if (year < CONFIG_ACPI_BLACKLIST_YEAR) {
+- printk(KERN_ERR PREFIX "BIOS age (%d) fails cutoff (%d), "
++ printk(KERN_INFO PREFIX "BIOS age (%d) fails cutoff (%d), "
+ "acpi=force is required to enable ACPI\n",
+ year, CONFIG_ACPI_BLACKLIST_YEAR);
+ return 1;
diff --git a/linux-2.6-silence-fbcon-logo.patch b/linux-2.6-silence-fbcon-logo.patch
new file mode 100644
index 000000000..45ab73331
--- /dev/null
+++ b/linux-2.6-silence-fbcon-logo.patch
@@ -0,0 +1,42 @@
+diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
+index 1657b96..4c5c2be 100644
+--- a/drivers/video/console/fbcon.c
++++ b/drivers/video/console/fbcon.c
+@@ -631,13 +631,15 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
+ kfree(save);
+ }
+
+- if (logo_lines > vc->vc_bottom) {
+- logo_shown = FBCON_LOGO_CANSHOW;
+- printk(KERN_INFO
+- "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
+- } else if (logo_shown != FBCON_LOGO_DONTSHOW) {
+- logo_shown = FBCON_LOGO_DRAW;
+- vc->vc_top = logo_lines;
++ if (logo_shown != FBCON_LOGO_DONTSHOW) {
++ if (logo_lines > vc->vc_bottom) {
++ logo_shown = FBCON_LOGO_CANSHOW;
++ printk(KERN_INFO
++ "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
++ } else {
++ logo_shown = FBCON_LOGO_DRAW;
++ vc->vc_top = logo_lines;
++ }
+ }
+ }
+ #endif /* MODULE */
+@@ -3489,6 +3491,14 @@ static int __init fb_console_init(void)
+ return 0;
+ }
+
++static int __init quiet_logo(char *str)
++{
++ logo_shown = FBCON_LOGO_DONTSHOW;
++ return 0;
++}
++
++early_param("quiet", quiet_logo);
++
+ module_init(fb_console_init);
+
+ #ifdef MODULE
diff --git a/linux-2.6-silence-noise.patch b/linux-2.6-silence-noise.patch
new file mode 100644
index 000000000..d5de8f439
--- /dev/null
+++ b/linux-2.6-silence-noise.patch
@@ -0,0 +1,89 @@
+--- linux-2.6.26.noarch/drivers/base/power/main.c~ 2008-08-22 20:57:57.000000000 -0400
++++ linux-2.6.26.noarch/drivers/base/power/main.c 2008-08-22 20:58:05.000000000 -0400
+@@ -69,9 +69,6 @@ void device_pm_unlock(void)
+ */
+ void device_pm_add(struct device *dev)
+ {
+- pr_debug("PM: Adding info for %s:%s\n",
+- dev->bus ? dev->bus->name : "No Bus",
+- kobject_name(&dev->kobj));
+ mutex_lock(&dpm_list_mtx);
+ if (dev->parent) {
+ if (dev->parent->power.status >= DPM_SUSPENDING)
+From b4e96f34c17e5a79cd28774cc722bb33e7e02c6e Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 25 Sep 2008 16:23:33 -0400
+Subject: [PATCH] Don't print an error message just because there's no i8042 chip.
+
+Some systems, such as EFI-based Apple systems, won't necessarily have an
+i8042 to initialize. We shouldn't be printing an error message in this
+case, since not detecting the chip is the correct behavior.
+---
+ drivers/input/serio/i8042.c | 4 +---
+ 1 files changed, 1 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
+index 170f71e..4f3e632 100644
+--- a/drivers/input/serio/i8042.c
++++ b/drivers/input/serio/i8042.c
+@@ -701,10 +701,8 @@ static int __devinit i8042_check_aux(void)
+
+ static int i8042_controller_check(void)
+ {
+- if (i8042_flush() == I8042_BUFFER_SIZE) {
+- printk(KERN_ERR "i8042.c: No controller found.\n");
++ if (i8042_flush() == I8042_BUFFER_SIZE)
+ return -ENODEV;
+- }
+
+ return 0;
+ }
+--
+1.6.0.1
+
+Socket fuzzers like sfuzz will trigger this printk a lot, even though it's
+ratelimited. It isn't particularly useful, so just remove it.
+
+Signed-off-by: Dave Jones <davej@redhat.com>
+
+--- linux-2.6.27.noarch/net/can/af_can.c~ 2008-12-11 16:53:48.000000000 -0500
++++ linux-2.6.27.noarch/net/can/af_can.c 2008-12-11 16:54:42.000000000 -0500
+@@ -134,13 +134,9 @@ static int can_create(struct net *net, s
+ err = request_module("can-proto-%d", protocol);
+
+ /*
+- * In case of error we only print a message but don't
+- * return the error code immediately. Below we will
+- * return -EPROTONOSUPPORT
++ * In case of error we don't return the error code immediately.
++ * Below we will return -EPROTONOSUPPORT
+ */
+- if (err && printk_ratelimit())
+- printk(KERN_ERR "can: request_module "
+- "(can-proto-%d) failed.\n", protocol);
+ }
+ #endif
+
+Some devices (like the nuforce udac) spew this quite a lot.
+This patch kinda sucks, but it'll shut things up for now.
+
+Probably not an upstream candidate. I suspect the answer would be
+"don't turn SND_DEBUG then"
+
+--- linux-2.6.34.noarch/sound/usb/clock.c~ 2010-07-16 22:11:41.000000000 -0400
++++ linux-2.6.34.noarch/sound/usb/clock.c 2010-07-16 22:15:21.000000000 -0400
+@@ -212,8 +212,13 @@ static int set_sample_rate_v1(struct snd
+
+ /* if endpoint doesn't have sampling rate control, bail out */
+ if (!(fmt->attributes & UAC_EP_CS_ATTR_SAMPLE_RATE)) {
+- snd_printk(KERN_WARNING "%d:%d:%d: endpoint lacks sample rate attribute bit, cannot set.\n",
++ static int once;
++
++ if (!once) {
++ snd_printk(KERN_WARNING "%d:%d:%d: endpoint lacks sample rate attribute bit, cannot set.\n",
+ dev->devnum, iface, fmt->altsetting);
++ once = 1;
++ }
+ return 0;
+ }
+
diff --git a/linux-2.6-sparc-selinux-mprotect-checks.patch b/linux-2.6-sparc-selinux-mprotect-checks.patch
new file mode 100644
index 000000000..cc821e323
--- /dev/null
+++ b/linux-2.6-sparc-selinux-mprotect-checks.patch
@@ -0,0 +1,35 @@
+diff -up linux-2.6.24.sparc64/security/selinux/hooks.c.BAD linux-2.6.24.sparc64/security/selinux/hooks.c
+--- linux-2.6.24.sparc64/security/selinux/hooks.c.BAD 2008-03-21 14:28:06.000000000 -0400
++++ linux-2.6.24.sparc64/security/selinux/hooks.c 2008-03-21 14:29:10.000000000 -0400
+@@ -3018,6 +3018,7 @@ static int file_map_prot_check(struct fi
+ const struct cred *cred = current_cred();
+ int rc = 0;
+
++#ifndef CONFIG_SPARC
+ if ((prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) {
+ /*
+ * We are making executable an anonymous mapping or a
+@@ -3028,6 +3029,7 @@ static int file_map_prot_check(struct fi
+ if (rc)
+ goto error;
+ }
++#endif
+
+ if (file) {
+ /* read access is always possible with a mapping */
+@@ -3081,6 +3081,7 @@ static int selinux_file_mprotect(struct
+ if (selinux_checkreqprot)
+ prot = reqprot;
+
++#ifndef CONFIG_SPARC
+ if ((prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
+ rc = 0;
+ if (vma->vm_start >= vma->vm_mm->start_brk &&
+@@ -3103,6 +3103,7 @@ static int selinux_file_mprotect(struct
+ if (rc)
+ return rc;
+ }
++#endif
+
+ return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED);
+ }
diff --git a/linux-2.6-upstream-reverts.patch b/linux-2.6-upstream-reverts.patch
new file mode 100644
index 000000000..607602cfc
--- /dev/null
+++ b/linux-2.6-upstream-reverts.patch
@@ -0,0 +1 @@
+nil
diff --git a/linux-2.6-v4l-dvb-add-kworld-a340-support.patch b/linux-2.6-v4l-dvb-add-kworld-a340-support.patch
new file mode 100644
index 000000000..0c7d24117
--- /dev/null
+++ b/linux-2.6-v4l-dvb-add-kworld-a340-support.patch
@@ -0,0 +1,161 @@
+From c34c78838f02693a70808e38309629e85aa50266 Mon Sep 17 00:00:00 2001
+From: Jarod Wilson <jarod@redhat.com>
+Date: Thu, 20 May 2010 10:03:13 -0400
+Subject: [PATCH] dvb: add support for kworld 340u and ub435-q to em28xx-dvb
+
+This adds support for the KWorld PlusTV 340U and KWorld UB345-Q ATSC
+sticks, which are really the same device. The sticks have an eMPIA
+em2870 usb bridge chipset, an LG Electronics LGDT3304 ATSC/QAM
+demodulator and an NXP TDA18271HD tuner -- early versions of the 340U
+have a a TDA18271HD/C1, later models and the UB435-Q have a C2.
+
+The stick has been tested succesfully with both VSB_8 and QAM_256 signals.
+Its using lgdt3304 support added to the lgdt3305 driver by a prior patch,
+rather than the current lgdt3304 driver, as its severely lacking in
+functionality by comparison (see said patch for details).
+
+Signed-off-by: Jarod Wilson <jarod@redhat.com>
+---
+ drivers/media/video/em28xx/em28xx-cards.c | 28 ++++++++++++++++++++++++
+ drivers/media/video/em28xx/em28xx-dvb.c | 33 +++++++++++++++++++++++++++++
+ drivers/media/video/em28xx/em28xx.h | 1 +
+ 3 files changed, 62 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
+index b0fb083..6312e76 100644
+--- a/drivers/media/video/em28xx/em28xx-cards.c
++++ b/drivers/media/video/em28xx/em28xx-cards.c
+@@ -158,6 +158,22 @@ static struct em28xx_reg_seq evga_indtube_digital[] = {
+ { -1, -1, -1, -1},
+ };
+
++/*
++ * KWorld PlusTV 340U and UB435-Q (ATSC) GPIOs map:
++ * EM_GPIO_0 - currently unknown
++ * EM_GPIO_1 - LED disable/enable (1 = off, 0 = on)
++ * EM_GPIO_2 - currently unknown
++ * EM_GPIO_3 - currently unknown
++ * EM_GPIO_4 - TDA18271HD/C1 tuner (1 = active, 0 = in reset)
++ * EM_GPIO_5 - LGDT3304 ATSC/QAM demod (1 = active, 0 = in reset)
++ * EM_GPIO_6 - currently unknown
++ * EM_GPIO_7 - currently unknown
++ */
++static struct em28xx_reg_seq kworld_a340_digital[] = {
++ {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
++ { -1, -1, -1, -1},
++};
++
+ /* Pinnacle Hybrid Pro eb1a:2881 */
+ static struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = {
+ {EM28XX_R08_GPIO, 0xfd, ~EM_GPIO_4, 10},
+@@ -1649,6 +1665,16 @@ struct em28xx_board em28xx_boards[] = {
+ .tuner_gpio = reddo_dvb_c_usb_box,
+ .has_dvb = 1,
+ },
++ /* 1b80:a340 - Empia EM2870, NXP TDA18271HD and LG DT3304, sold
++ * initially as the KWorld PlusTV 340U, then as the UB435-Q.
++ * Early variants have a TDA18271HD/C1, later ones a TDA18271HD/C2 */
++ [EM2870_BOARD_KWORLD_A340] = {
++ .name = "KWorld PlusTV 340U or UB435-Q (ATSC)",
++ .tuner_type = TUNER_ABSENT, /* Digital-only TDA18271HD */
++ .has_dvb = 1,
++ .dvb_gpio = kworld_a340_digital,
++ .tuner_gpio = default_tuner_gpio,
++ },
+ };
+ const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
+
+@@ -1768,6 +1794,8 @@ struct usb_device_id em28xx_id_table[] = {
+ .driver_info = EM2820_BOARD_IODATA_GVMVP_SZ },
+ { USB_DEVICE(0xeb1a, 0x50a6),
+ .driver_info = EM2860_BOARD_GADMEI_UTV330 },
++ { USB_DEVICE(0x1b80, 0xa340),
++ .driver_info = EM2870_BOARD_KWORLD_A340 },
+ { },
+ };
+ MODULE_DEVICE_TABLE(usb, em28xx_id_table);
+diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
+index bcd3c37..ce8a9ee 100644
+--- a/drivers/media/video/em28xx/em28xx-dvb.c
++++ b/drivers/media/video/em28xx/em28xx-dvb.c
+@@ -30,11 +30,13 @@
+ #include "tuner-simple.h"
+
+ #include "lgdt330x.h"
++#include "lgdt3305.h"
+ #include "zl10353.h"
+ #include "s5h1409.h"
+ #include "mt352.h"
+ #include "mt352_priv.h" /* FIXME */
+ #include "tda1002x.h"
++#include "tda18271.h"
+
+ MODULE_DESCRIPTION("driver for em28xx based DVB cards");
+ MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
+@@ -231,6 +233,18 @@ static struct lgdt330x_config em2880_lgdt3303_dev = {
+ .demod_chip = LGDT3303,
+ };
+
++static struct lgdt3305_config em2870_lgdt3304_dev = {
++ .i2c_addr = 0x0e,
++ .demod_chip = LGDT3304,
++ .spectral_inversion = 1,
++ .deny_i2c_rptr = 1,
++ .mpeg_mode = LGDT3305_MPEG_PARALLEL,
++ .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE,
++ .tpvalid_polarity = LGDT3305_TP_VALID_HIGH,
++ .vsb_if_khz = 3250,
++ .qam_if_khz = 4000,
++};
++
+ static struct zl10353_config em28xx_zl10353_with_xc3028 = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+@@ -247,6 +261,17 @@ static struct s5h1409_config em28xx_s5h1409_with_xc3028 = {
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK
+ };
+
++static struct tda18271_std_map kworld_a340_std_map = {
++ .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 0,
++ .if_lvl = 1, .rfagc_top = 0x37, },
++ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 1,
++ .if_lvl = 1, .rfagc_top = 0x37, },
++};
++
++static struct tda18271_config kworld_a340_config = {
++ .std_map = &kworld_a340_std_map,
++};
++
+ static struct zl10353_config em28xx_zl10353_xc3028_no_i2c_gate = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+@@ -570,6 +595,14 @@ static int dvb_init(struct em28xx *dev)
+ }
+ }
+ break;
++ case EM2870_BOARD_KWORLD_A340:
++ dvb->frontend = dvb_attach(lgdt3305_attach,
++ &em2870_lgdt3304_dev,
++ &dev->i2c_adap);
++ if (dvb->frontend != NULL)
++ dvb_attach(tda18271_attach, dvb->frontend, 0x60,
++ &dev->i2c_adap, &kworld_a340_config);
++ break;
+ default:
+ em28xx_errdev("/2: The frontend of your DVB/ATSC card"
+ " isn't supported yet\n");
+diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
+index ba6fe5d..9f3fdad 100644
+--- a/drivers/media/video/em28xx/em28xx.h
++++ b/drivers/media/video/em28xx/em28xx.h
+@@ -112,6 +112,7 @@
+ #define EM2870_BOARD_REDDO_DVB_C_USB_BOX 73
+ #define EM2800_BOARD_VC211A 74
+ #define EM2882_BOARD_DIKOM_DK300 75
++#define EM2870_BOARD_KWORLD_A340 76
+
+ /* Limits minimum and default number of buffers */
+ #define EM28XX_MIN_BUF 4
+--
+1.7.0.1
+
diff --git a/linux-2.6-v4l-dvb-add-lgdt3304-support.patch b/linux-2.6-v4l-dvb-add-lgdt3304-support.patch
new file mode 100644
index 000000000..30c50434f
--- /dev/null
+++ b/linux-2.6-v4l-dvb-add-lgdt3304-support.patch
@@ -0,0 +1,350 @@
+From b71e18093e2e7f240797875c50c49552722f8825 Mon Sep 17 00:00:00 2001
+From: Jarod Wilson <jarod@redhat.com>
+Date: Mon, 15 Feb 2010 17:13:25 -0500
+Subject: [PATCH 1/2] dvb: add lgdt3304 support to lgdt3305 driver
+
+There's a currently-unused lgdt3304 demod driver, which leaves a lot to
+be desired as far as functionality. The 3304 is unsurprisingly quite
+similar to the 3305, and empirical testing yeilds far better results
+and more complete functionality by merging 3304 support into the 3305
+driver. (For example, the current lgdt3304 driver lacks support for
+signal strength, snr, ucblocks, etc., which we get w/the lgdt3305).
+
+For the moment, not dropping the lgdt3304 driver, and its still up to
+a given device's config setup to choose which demod driver to use, but
+I'd suggest dropping the 3304 driver entirely.
+
+As a follow-up to this patch, I've got another patch that adds support
+for the KWorld PlusTV 340U (ATSC) em2870-based tuner stick, driving
+its lgdt3304 demod via this lgdt3305 driver, which is what I used to
+successfully test this patch with both VSB_8 and QAM_256 signals.
+
+A few pieces are still a touch crude, but I think its a solid start,
+as well as much cleaner and more feature-complete than the existing
+lgdt3304 driver.
+
+Signed-off-by: Jarod Wilson <jarod@redhat.com>
+---
+ drivers/media/dvb/frontends/lgdt3305.c | 206 ++++++++++++++++++++++++++++++--
+ drivers/media/dvb/frontends/lgdt3305.h | 6 +
+ 2 files changed, 203 insertions(+), 9 deletions(-)
+
+diff --git a/drivers/media/dvb/frontends/lgdt3305.c b/drivers/media/dvb/frontends/lgdt3305.c
+index fde8c59..40695e6 100644
+--- a/drivers/media/dvb/frontends/lgdt3305.c
++++ b/drivers/media/dvb/frontends/lgdt3305.c
+@@ -1,5 +1,5 @@
+ /*
+- * Support for LGDT3305 - VSB/QAM
++ * Support for LG Electronics LGDT3304 and LGDT3305 - VSB/QAM
+ *
+ * Copyright (C) 2008, 2009 Michael Krufky <mkrufky@linuxtv.org>
+ *
+@@ -357,7 +357,10 @@ static int lgdt3305_rfagc_loop(struct lgdt3305_state *state,
+ case QAM_256:
+ agcdelay = 0x046b;
+ rfbw = 0x8889;
+- ifbw = 0x8888;
++ if (state->cfg->demod_chip == LGDT3305)
++ ifbw = 0x8888;
++ else
++ ifbw = 0x6666;
+ break;
+ default:
+ return -EINVAL;
+@@ -409,8 +412,18 @@ static int lgdt3305_agc_setup(struct lgdt3305_state *state,
+ lg_dbg("lockdten = %d, acqen = %d\n", lockdten, acqen);
+
+ /* control agc function */
+- lgdt3305_write_reg(state, LGDT3305_AGC_CTRL_4, 0xe1 | lockdten << 1);
+- lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 2, acqen);
++ switch (state->cfg->demod_chip) {
++ case LGDT3304:
++ lgdt3305_write_reg(state, 0x0314, 0xe1 | lockdten << 1);
++ lgdt3305_set_reg_bit(state, 0x030e, 2, acqen);
++ break;
++ case LGDT3305:
++ lgdt3305_write_reg(state, LGDT3305_AGC_CTRL_4, 0xe1 | lockdten << 1);
++ lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 2, acqen);
++ break;
++ default:
++ return -EINVAL;
++ }
+
+ return lgdt3305_rfagc_loop(state, param);
+ }
+@@ -543,6 +556,11 @@ static int lgdt3305_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+ enable ? 0 : 1);
+ }
+
++static int lgdt3304_sleep(struct dvb_frontend *fe)
++{
++ return 0;
++}
++
+ static int lgdt3305_sleep(struct dvb_frontend *fe)
+ {
+ struct lgdt3305_state *state = fe->demodulator_priv;
+@@ -571,6 +589,55 @@ static int lgdt3305_sleep(struct dvb_frontend *fe)
+ return 0;
+ }
+
++static int lgdt3304_init(struct dvb_frontend *fe)
++{
++ struct lgdt3305_state *state = fe->demodulator_priv;
++ int ret;
++
++ static struct lgdt3305_reg lgdt3304_init_data[] = {
++ { .reg = LGDT3305_GEN_CTRL_1, .val = 0x03, },
++ { .reg = 0x000d, .val = 0x02, },
++ { .reg = 0x000e, .val = 0x02, },
++ { .reg = LGDT3305_DGTL_AGC_REF_1, .val = 0x32, },
++ { .reg = LGDT3305_DGTL_AGC_REF_2, .val = 0xc4, },
++ { .reg = LGDT3305_CR_CTR_FREQ_1, .val = 0x00, },
++ { .reg = LGDT3305_CR_CTR_FREQ_2, .val = 0x00, },
++ { .reg = LGDT3305_CR_CTR_FREQ_3, .val = 0x00, },
++ { .reg = LGDT3305_CR_CTR_FREQ_4, .val = 0x00, },
++ { .reg = LGDT3305_CR_CTRL_7, .val = 0xf9, },
++ { .reg = 0x0112, .val = 0x17, },
++ { .reg = 0x0113, .val = 0x15, },
++ { .reg = 0x0114, .val = 0x18, },
++ { .reg = 0x0115, .val = 0xff, },
++ { .reg = 0x0116, .val = 0x3c, },
++ { .reg = 0x0214, .val = 0x67, },
++ { .reg = 0x0424, .val = 0x8d, },
++ { .reg = 0x0427, .val = 0x12, },
++ { .reg = 0x0428, .val = 0x4f, },
++ { .reg = LGDT3305_IFBW_1, .val = 0x80, },
++ { .reg = LGDT3305_IFBW_2, .val = 0x00, },
++ { .reg = 0x030a, .val = 0x08, },
++ { .reg = 0x030b, .val = 0x9b, },
++ { .reg = 0x030d, .val = 0x00, },
++ { .reg = 0x030e, .val = 0x1c, },
++ { .reg = 0x0314, .val = 0xe1, },
++ { .reg = 0x000d, .val = 0x82, },
++ { .reg = LGDT3305_TP_CTRL_1, .val = 0x5b, },
++ { .reg = LGDT3305_TP_CTRL_1, .val = 0x5b, },
++ };
++
++ lg_dbg("\n");
++
++ ret = lgdt3305_write_regs(state, lgdt3304_init_data,
++ ARRAY_SIZE(lgdt3304_init_data));
++ if (lg_fail(ret))
++ goto fail;
++
++ ret = lgdt3305_soft_reset(state);
++fail:
++ return ret;
++}
++
+ static int lgdt3305_init(struct dvb_frontend *fe)
+ {
+ struct lgdt3305_state *state = fe->demodulator_priv;
+@@ -639,6 +706,88 @@ fail:
+ return ret;
+ }
+
++static int lgdt3304_set_parameters(struct dvb_frontend *fe,
++ struct dvb_frontend_parameters *param)
++{
++ struct lgdt3305_state *state = fe->demodulator_priv;
++ int ret;
++
++ lg_dbg("(%d, %d)\n", param->frequency, param->u.vsb.modulation);
++
++ if (fe->ops.tuner_ops.set_params) {
++ ret = fe->ops.tuner_ops.set_params(fe, param);
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 0);
++ if (lg_fail(ret))
++ goto fail;
++ state->current_frequency = param->frequency;
++ }
++
++ ret = lgdt3305_set_modulation(state, param);
++ if (lg_fail(ret))
++ goto fail;
++
++ ret = lgdt3305_passband_digital_agc(state, param);
++ if (lg_fail(ret))
++ goto fail;
++
++ ret = lgdt3305_agc_setup(state, param);
++ if (lg_fail(ret))
++ goto fail;
++
++ /* reg 0x030d is 3304-only... seen in vsb and qam usbsnoops... */
++ switch (param->u.vsb.modulation) {
++ case VSB_8:
++ lgdt3305_write_reg(state, 0x030d, 0x00);
++#if 1
++ lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_1, 0x4f);
++ lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_2, 0x0c);
++ lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_3, 0xac);
++ lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_4, 0xba);
++#endif
++ break;
++ case QAM_64:
++ case QAM_256:
++ lgdt3305_write_reg(state, 0x030d, 0x14);
++#if 1
++ ret = lgdt3305_set_if(state, param);
++ if (lg_fail(ret))
++ goto fail;
++#endif
++ break;
++ default:
++ return -EINVAL;
++ }
++
++#if 0
++ /* the set_if vsb formula doesn't work for the 3304, we end up sending
++ * 0x40851e07 instead of 0x4f0cacba (which works back to 94050, rather
++ * than 3250, in the case of the kworld 340u) */
++ ret = lgdt3305_set_if(state, param);
++ if (lg_fail(ret))
++ goto fail;
++#endif
++
++ ret = lgdt3305_spectral_inversion(state, param,
++ state->cfg->spectral_inversion
++ ? 1 : 0);
++ if (lg_fail(ret))
++ goto fail;
++
++ state->current_modulation = param->u.vsb.modulation;
++
++ ret = lgdt3305_mpeg_mode(state, state->cfg->mpeg_mode);
++ if (lg_fail(ret))
++ goto fail;
++
++ /* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */
++ ret = lgdt3305_mpeg_mode_polarity(state,
++ state->cfg->tpclk_edge,
++ state->cfg->tpvalid_polarity);
++fail:
++ return ret;
++}
++
+ static int lgdt3305_set_parameters(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *param)
+ {
+@@ -847,6 +996,10 @@ static int lgdt3305_read_status(struct dvb_frontend *fe, fe_status_t *status)
+ switch (state->current_modulation) {
+ case QAM_256:
+ case QAM_64:
++#if 0 /* needed w/3304 to set FE_HAS_SIGNAL */
++ if (cr_lock)
++ *status |= FE_HAS_SIGNAL;
++#endif
+ ret = lgdt3305_read_fec_lock_status(state, &fec_lock);
+ if (lg_fail(ret))
+ goto fail;
+@@ -992,6 +1145,7 @@ static void lgdt3305_release(struct dvb_frontend *fe)
+ kfree(state);
+ }
+
++static struct dvb_frontend_ops lgdt3304_ops;
+ static struct dvb_frontend_ops lgdt3305_ops;
+
+ struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config,
+@@ -1012,11 +1166,21 @@ struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config,
+ state->cfg = config;
+ state->i2c_adap = i2c_adap;
+
+- memcpy(&state->frontend.ops, &lgdt3305_ops,
+- sizeof(struct dvb_frontend_ops));
++ switch (config->demod_chip) {
++ case LGDT3304:
++ memcpy(&state->frontend.ops, &lgdt3304_ops,
++ sizeof(struct dvb_frontend_ops));
++ break;
++ case LGDT3305:
++ memcpy(&state->frontend.ops, &lgdt3305_ops,
++ sizeof(struct dvb_frontend_ops));
++ break;
++ default:
++ goto fail;
++ }
+ state->frontend.demodulator_priv = state;
+
+- /* verify that we're talking to a lg dt3305 */
++ /* verify that we're talking to a lg dt3304/5 */
+ ret = lgdt3305_read_reg(state, LGDT3305_GEN_CTRL_2, &val);
+ if ((lg_fail(ret)) | (val == 0))
+ goto fail;
+@@ -1035,12 +1199,36 @@ struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config,
+
+ return &state->frontend;
+ fail:
+- lg_warn("unable to detect LGDT3305 hardware\n");
++ lg_warn("unable to detect %s hardware\n",
++ config->demod_chip ? "LGDT3304" : "LGDT3305");
+ kfree(state);
+ return NULL;
+ }
+ EXPORT_SYMBOL(lgdt3305_attach);
+
++static struct dvb_frontend_ops lgdt3304_ops = {
++ .info = {
++ .name = "LG Electronics LGDT3304 VSB/QAM Frontend",
++ .type = FE_ATSC,
++ .frequency_min = 54000000,
++ .frequency_max = 858000000,
++ .frequency_stepsize = 62500,
++ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
++ },
++ .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl,
++ .init = lgdt3304_init,
++ .sleep = lgdt3304_sleep,
++ .set_frontend = lgdt3304_set_parameters,
++ .get_frontend = lgdt3305_get_frontend,
++ .get_tune_settings = lgdt3305_get_tune_settings,
++ .read_status = lgdt3305_read_status,
++ .read_ber = lgdt3305_read_ber,
++ .read_signal_strength = lgdt3305_read_signal_strength,
++ .read_snr = lgdt3305_read_snr,
++ .read_ucblocks = lgdt3305_read_ucblocks,
++ .release = lgdt3305_release,
++};
++
+ static struct dvb_frontend_ops lgdt3305_ops = {
+ .info = {
+ .name = "LG Electronics LGDT3305 VSB/QAM Frontend",
+@@ -1064,7 +1252,7 @@ static struct dvb_frontend_ops lgdt3305_ops = {
+ .release = lgdt3305_release,
+ };
+
+-MODULE_DESCRIPTION("LG Electronics LGDT3305 ATSC/QAM-B Demodulator Driver");
++MODULE_DESCRIPTION("LG Electronics LGDT3304/5 ATSC/QAM-B Demodulator Driver");
+ MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
+ MODULE_LICENSE("GPL");
+ MODULE_VERSION("0.1");
+diff --git a/drivers/media/dvb/frontends/lgdt3305.h b/drivers/media/dvb/frontends/lgdt3305.h
+index 9cb11c9..a7f30c2 100644
+--- a/drivers/media/dvb/frontends/lgdt3305.h
++++ b/drivers/media/dvb/frontends/lgdt3305.h
+@@ -41,6 +41,11 @@ enum lgdt3305_tp_valid_polarity {
+ LGDT3305_TP_VALID_HIGH = 1,
+ };
+
++enum lgdt_demod_chip_type {
++ LGDT3305 = 0,
++ LGDT3304 = 1,
++};
++
+ struct lgdt3305_config {
+ u8 i2c_addr;
+
+@@ -65,6 +70,7 @@ struct lgdt3305_config {
+ enum lgdt3305_mpeg_mode mpeg_mode;
+ enum lgdt3305_tp_clock_edge tpclk_edge;
+ enum lgdt3305_tp_valid_polarity tpvalid_polarity;
++ enum lgdt_demod_chip_type demod_chip;
+ };
+
+ #if defined(CONFIG_DVB_LGDT3305) || (defined(CONFIG_DVB_LGDT3305_MODULE) && \
+--
+1.6.6
+
diff --git a/linux-2.6-v4l-dvb-experimental.patch b/linux-2.6-v4l-dvb-experimental.patch
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/linux-2.6-v4l-dvb-experimental.patch
diff --git a/linux-2.6-v4l-dvb-fixes.patch b/linux-2.6-v4l-dvb-fixes.patch
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/linux-2.6-v4l-dvb-fixes.patch
diff --git a/linux-2.6-v4l-dvb-ir-core-update.patch b/linux-2.6-v4l-dvb-ir-core-update.patch
new file mode 100644
index 000000000..c1105a330
--- /dev/null
+++ b/linux-2.6-v4l-dvb-ir-core-update.patch
@@ -0,0 +1,6741 @@
+Patch generated from the linuxtv staging/other branch, with a few
+additional pending fixes merged in, and just about everything not
+essential to the ir-core update chopped out.
+
+(Patch generated 2010.07.16)
+
+Signed-off-by: Jarod Wilson <jarod@redhat.com>
+
+---
+ Documentation/DocBook/media-entities.tmpl | 1
+ Documentation/DocBook/media.tmpl | 8
+ Documentation/DocBook/v4l/lirc_device_interface.xml | 235 ++++
+ Documentation/DocBook/v4l/remote_controllers.xml | 2
+ Documentation/dvb/get_dvb_firmware | 19
+ Documentation/video4linux/CARDLIST.cx23885 | 6
+ drivers/input/evdev.c | 39
+ drivers/input/input.c | 268 ++++
+ drivers/media/IR/Kconfig | 34
+ drivers/media/IR/Makefile | 3
+ drivers/media/IR/imon.c | 5
+ drivers/media/IR/ir-core-priv.h | 54
+ drivers/media/IR/ir-jvc-decoder.c | 152 --
+ drivers/media/IR/ir-lirc-codec.c | 283 ++++
+ drivers/media/IR/ir-nec-decoder.c | 151 --
+ drivers/media/IR/ir-raw-event.c | 167 +-
+ drivers/media/IR/ir-rc5-decoder.c | 167 --
+ drivers/media/IR/ir-rc6-decoder.c | 153 --
+ drivers/media/IR/ir-sony-decoder.c | 155 --
+ drivers/media/IR/ir-sysfs.c | 261 ++--
+ drivers/media/IR/keymaps/Makefile | 2
+ drivers/media/IR/keymaps/rc-lirc.c | 41
+ drivers/media/IR/keymaps/rc-rc6-mce.c | 105 +
+ drivers/media/IR/lirc_dev.c | 764 +++++++++++++
+ drivers/media/IR/mceusb.c | 1143 ++++++++++++++++++++
+ drivers/media/common/tuners/tda18271-fe.c | 8
+ drivers/media/dvb/mantis/Kconfig | 14
+ drivers/media/dvb/mantis/mantis_input.c | 5
+ drivers/media/video/cx23885/cx23885-cards.c | 40
+ drivers/media/video/cx23885/cx23885-core.c | 11
+ drivers/media/video/cx23885/cx23885-dvb.c | 2
+ drivers/media/video/cx23885/cx23885-input.c | 317 +----
+ drivers/media/video/cx23885/cx23885-ir.c | 2
+ drivers/media/video/cx23885/cx23885.h | 12
+ drivers/media/video/cx88/cx88-cards.c | 9
+ drivers/media/video/cx88/cx88-i2c.c | 6
+ drivers/media/video/cx88/cx88-input.c | 46
+ drivers/media/video/cx88/cx88.h | 1
+ drivers/media/video/em28xx/em28xx-input.c | 80 -
+ drivers/media/video/em28xx/em28xx-video.c | 4
+ drivers/media/video/em28xx/em28xx.h | 1
+ drivers/media/video/hdpvr/hdpvr-core.c | 5
+ drivers/media/video/ir-kbd-i2c.c | 14
+ drivers/media/video/pvrusb2/pvrusb2-ioread.c | 5
+ include/linux/input.h | 39
+ include/media/ir-core.h | 8
+ include/media/ir-kbd-i2c.h | 2
+ include/media/lirc.h | 165 ++
+ include/media/lirc_dev.h | 225 +++
+ include/media/rc-map.h | 7
+ 50 files changed, 3971 insertions(+), 1275 deletions(-)
+
+diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl
+index 5d4d40f..6ae9715 100644
+--- a/Documentation/DocBook/media-entities.tmpl
++++ b/Documentation/DocBook/media-entities.tmpl
+@@ -218,6 +218,7 @@
+ <!ENTITY sub-dev-teletext SYSTEM "v4l/dev-teletext.xml">
+ <!ENTITY sub-driver SYSTEM "v4l/driver.xml">
+ <!ENTITY sub-libv4l SYSTEM "v4l/libv4l.xml">
++<!ENTITY sub-lirc_device_interface SYSTEM "v4l/lirc_device_interface.xml">
+ <!ENTITY sub-remote_controllers SYSTEM "v4l/remote_controllers.xml">
+ <!ENTITY sub-fdl-appendix SYSTEM "v4l/fdl-appendix.xml">
+ <!ENTITY sub-close SYSTEM "v4l/func-close.xml">
+diff --git a/Documentation/DocBook/media.tmpl b/Documentation/DocBook/media.tmpl
+index eea564b..f11048d 100644
+--- a/Documentation/DocBook/media.tmpl
++++ b/Documentation/DocBook/media.tmpl
+@@ -28,7 +28,7 @@
+ <title>LINUX MEDIA INFRASTRUCTURE API</title>
+
+ <copyright>
+- <year>2009</year>
++ <year>2009-2010</year>
+ <holder>LinuxTV Developers</holder>
+ </copyright>
+
+@@ -61,7 +61,7 @@ Foundation. A copy of the license is included in the chapter entitled
+ in fact it covers several different video standards including
+ DVB-T, DVB-S, DVB-C and ATSC. The API is currently being updated
+ to documment support also for DVB-S2, ISDB-T and ISDB-S.</para>
+- <para>The third part covers other API's used by all media infrastructure devices</para>
++ <para>The third part covers Remote Controller API</para>
+ <para>For additional information and for the latest development code,
+ see: <ulink url="http://linuxtv.org">http://linuxtv.org</ulink>.</para>
+ <para>For discussing improvements, reporting troubles, sending new drivers, etc, please mail to: <ulink url="http://vger.kernel.org/vger-lists.html#linux-media">Linux Media Mailing List (LMML).</ulink>.</para>
+@@ -86,7 +86,7 @@ Foundation. A copy of the license is included in the chapter entitled
+ </author>
+ </authorgroup>
+ <copyright>
+- <year>2009</year>
++ <year>2009-2010</year>
+ <holder>Mauro Carvalho Chehab</holder>
+ </copyright>
+
+@@ -101,7 +101,7 @@ Foundation. A copy of the license is included in the chapter entitled
+ </revhistory>
+ </partinfo>
+
+-<title>Other API's used by media infrastructure drivers</title>
++<title>Remote Controller API</title>
+ <chapter id="remote_controllers">
+ &sub-remote_controllers;
+ </chapter>
+diff --git a/Documentation/DocBook/v4l/lirc_device_interface.xml b/Documentation/DocBook/v4l/lirc_device_interface.xml
+new file mode 100644
+index 0000000..0413234
+--- /dev/null
++++ b/Documentation/DocBook/v4l/lirc_device_interface.xml
+@@ -0,0 +1,235 @@
++<section id="lirc_dev">
++<title>LIRC Device Interface</title>
++
++
++<section id="lirc_dev_intro">
++<title>Introduction</title>
++
++<para>The LIRC device interface is a bi-directional interface for
++transporting raw IR data between userspace and kernelspace. Fundamentally,
++it is just a chardev (/dev/lircX, for X = 0, 1, 2, ...), with a number
++of standard struct file_operations defined on it. With respect to
++transporting raw IR data to and fro, the essential fops are read, write
++and ioctl.</para>
++
++<para>Example dmesg output upon a driver registering w/LIRC:</para>
++ <blockquote>
++ <para>$ dmesg |grep lirc_dev</para>
++ <para>lirc_dev: IR Remote Control driver registered, major 248</para>
++ <para>rc rc0: lirc_dev: driver ir-lirc-codec (mceusb) registered at minor = 0</para>
++ </blockquote>
++
++<para>What you should see for a chardev:</para>
++ <blockquote>
++ <para>$ ls -l /dev/lirc*</para>
++ <para>crw-rw---- 1 root root 248, 0 Jul 2 22:20 /dev/lirc0</para>
++ </blockquote>
++</section>
++
++<section id="lirc_read">
++<title>LIRC read fop</title>
++
++<para>The lircd userspace daemon reads raw IR data from the LIRC chardev. The
++exact format of the data depends on what modes a driver supports, and what
++mode has been selected. lircd obtains supported modes and sets the active mode
++via the ioctl interface, detailed at <xref linkend="lirc_ioctl"/>. The generally
++preferred mode is LIRC_MODE_MODE2, in which packets containing an int value
++describing an IR signal are read from the chardev.</para>
++
++<para>See also <ulink url="http://www.lirc.org/html/technical.html">http://www.lirc.org/html/technical.html</ulink> for more info.</para>
++</section>
++
++<section id="lirc_write">
++<title>LIRC write fop</title>
++
++<para>The data written to the chardev is a pulse/space sequence of integer
++values. Pulses and spaces are only marked implicitly by their position. The
++data must start and end with a pulse, therefore, the data must always include
++an unevent number of samples. The write function must block until the data has
++been transmitted by the hardware.</para>
++</section>
++
++<section id="lirc_ioctl">
++<title>LIRC ioctl fop</title>
++
++<para>The LIRC device's ioctl definition is bound by the ioctl function
++definition of struct file_operations, leaving us with an unsigned int
++for the ioctl command and an unsigned long for the arg. For the purposes
++of ioctl portability across 32-bit and 64-bit, these values are capped
++to their 32-bit sizes.</para>
++
++<para>The following ioctls can be used to change specific hardware settings.
++In general each driver should have a default set of settings. The driver
++implementation is expected to re-apply the default settings when the device
++is closed by user-space, so that every application opening the device can rely
++on working with the default settings initially.</para>
++
++<variablelist>
++ <varlistentry>
++ <term>LIRC_GET_FEATURES</term>
++ <listitem>
++ <para>Obviously, get the underlying hardware device's features. If a driver
++ does not announce support of certain features, calling of the corresponding
++ ioctls is undefined.</para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>LIRC_GET_SEND_MODE</term>
++ <listitem>
++ <para>Get supported transmit mode. Only LIRC_MODE_PULSE is supported by lircd.</para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>LIRC_GET_REC_MODE</term>
++ <listitem>
++ <para>Get supported receive modes. Only LIRC_MODE_MODE2 and LIRC_MODE_LIRCCODE
++ are supported by lircd.</para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>LIRC_GET_SEND_CARRIER</term>
++ <listitem>
++ <para>Get carrier frequency (in Hz) currently used for transmit.</para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>LIRC_GET_REC_CARRIER</term>
++ <listitem>
++ <para>Get carrier frequency (in Hz) currently used for IR reception.</para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>LIRC_{G,S}ET_{SEND,REC}_DUTY_CYCLE</term>
++ <listitem>
++ <para>Get/set the duty cycle (from 0 to 100) of the carrier signal. Currently,
++ no special meaning is defined for 0 or 100, but this could be used to switch
++ off carrier generation in the future, so these values should be reserved.</para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>LIRC_GET_REC_RESOLUTION</term>
++ <listitem>
++ <para>Some receiver have maximum resolution which is defined by internal
++ sample rate or data format limitations. E.g. it's common that signals can
++ only be reported in 50 microsecond steps. This integer value is used by
++ lircd to automatically adjust the aeps tolerance value in the lircd
++ config file.</para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>LIRC_GET_M{IN,AX}_TIMEOUT</term>
++ <listitem>
++ <para>Some devices have internal timers that can be used to detect when
++ there's no IR activity for a long time. This can help lircd in detecting
++ that a IR signal is finished and can speed up the decoding process.
++ Returns an integer value with the minimum/maximum timeout that can be
++ set. Some devices have a fixed timeout, in that case both ioctls will
++ return the same value even though the timeout cannot be changed.</para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>LIRC_GET_M{IN,AX}_FILTER_{PULSE,SPACE}</term>
++ <listitem>
++ <para>Some devices are able to filter out spikes in the incoming signal
++ using given filter rules. These ioctls return the hardware capabilities
++ that describe the bounds of the possible filters. Filter settings depend
++ on the IR protocols that are expected. lircd derives the settings from
++ all protocols definitions found in its config file.</para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>LIRC_GET_LENGTH</term>
++ <listitem>
++ <para>Retrieves the code length in bits (only for LIRC_MODE_LIRCCODE).
++ Reads on the device must be done in blocks matching the bit count.
++ The bit could should be rounded up so that it matches full bytes.</para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>LIRC_SET_{SEND,REC}_MODE</term>
++ <listitem>
++ <para>Set send/receive mode. Largely obsolete for send, as only
++ LIRC_MODE_PULSE is supported.</para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>LIRC_SET_{SEND,REC}_CARRIER</term>
++ <listitem>
++ <para>Set send/receive carrier (in Hz).</para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>LIRC_SET_TRANSMITTER_MASK</term>
++ <listitem>
++ <para>This enables the given set of transmitters. The first transmitter
++ is encoded by the least significant bit, etc. When an invalid bit mask
++ is given, i.e. a bit is set, even though the device does not have so many
++ transitters, then this ioctl returns the number of available transitters
++ and does nothing otherwise.</para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>LIRC_SET_REC_TIMEOUT</term>
++ <listitem>
++ <para>Sets the integer value for IR inactivity timeout (cf.
++ LIRC_GET_MIN_TIMEOUT and LIRC_GET_MAX_TIMEOUT). A value of 0 (if
++ supported by the hardware) disables all hardware timeouts and data should
++ be reported as soon as possible. If the exact value cannot be set, then
++ the next possible value _greater_ than the given value should be set.</para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>LIRC_SET_REC_TIMEOUT_REPORTS</term>
++ <listitem>
++ <para>Enable (1) or disable (0) timeout reports in LIRC_MODE_MODE2. By
++ default, timeout reports should be turned off.</para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>LIRC_SET_REC_FILTER_{,PULSE,SPACE}</term>
++ <listitem>
++ <para>Pulses/spaces shorter than this are filtered out by hardware. If
++ filters cannot be set independently for pulse/space, the corresponding
++ ioctls must return an error and LIRC_SET_REC_FILTER shall be used instead.</para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>LIRC_SET_MEASURE_CARRIER_MODE</term>
++ <listitem>
++ <para>Enable (1)/disable (0) measure mode. If enabled, from the next key
++ press on, the driver will send LIRC_MODE2_FREQUENCY packets. By default
++ this should be turned off.</para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>LIRC_SET_REC_{DUTY_CYCLE,CARRIER}_RANGE</term>
++ <listitem>
++ <para>To set a range use LIRC_SET_REC_DUTY_CYCLE_RANGE/LIRC_SET_REC_CARRIER_RANGE
++ with the lower bound first and later LIRC_SET_REC_DUTY_CYCLE/LIRC_SET_REC_CARRIER
++ with the upper bound.</para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>LIRC_NOTIFY_DECODE</term>
++ <listitem>
++ <para>This ioctl is called by lircd whenever a successful decoding of an
++ incoming IR signal could be done. This can be used by supporting hardware
++ to give visual feedback to the user e.g. by flashing a LED.</para>
++ </listitem>
++ </varlistentry>
++ <varlistentry>
++ <term>LIRC_SETUP_{START,END}</term>
++ <listitem>
++ <para>Setting of several driver parameters can be optimized by encapsulating
++ the according ioctl calls with LIRC_SETUP_START/LIRC_SETUP_END. When a
++ driver receives a LIRC_SETUP_START ioctl it can choose to not commit
++ further setting changes to the hardware until a LIRC_SETUP_END is received.
++ But this is open to the driver implementation and every driver must also
++ handle parameter changes which are not encapsulated by LIRC_SETUP_START
++ and LIRC_SETUP_END. Drivers can also choose to ignore these ioctls.</para>
++ </listitem>
++ </varlistentry>
++</variablelist>
++
++</section>
++</section>
+diff --git a/Documentation/DocBook/v4l/remote_controllers.xml b/Documentation/DocBook/v4l/remote_controllers.xml
+index 73f5eab..3c3b667 100644
+--- a/Documentation/DocBook/v4l/remote_controllers.xml
++++ b/Documentation/DocBook/v4l/remote_controllers.xml
+@@ -173,3 +173,5 @@ keymapping.</para>
+ <para>This program demonstrates how to replace the keymap tables.</para>
+ &sub-keytable-c;
+ </section>
++
++&sub-lirc_device_interface;
+diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware
+index 239cbdb..9ea94dc 100644
+--- a/Documentation/dvb/get_dvb_firmware
++++ b/Documentation/dvb/get_dvb_firmware
+@@ -26,7 +26,7 @@ use IO::Handle;
+ "dec3000s", "vp7041", "dibusb", "nxt2002", "nxt2004",
+ "or51211", "or51132_qam", "or51132_vsb", "bluebird",
+ "opera1", "cx231xx", "cx18", "cx23885", "pvrusb2", "mpc718",
+- "af9015", "ngene");
++ "af9015", "ngene", "az6027");
+
+ # Check args
+ syntax() if (scalar(@ARGV) != 1);
+@@ -567,6 +567,23 @@ sub ngene {
+ "$file1, $file2";
+ }
+
++sub az6027{
++ my $file = "AZ6027_Linux_Driver.tar.gz";
++ my $url = "http://linux.terratec.de/files/$file";
++ my $firmware = "dvb-usb-az6027-03.fw";
++
++ wgetfile($file, $url);
++
++ #untar
++ if( system("tar xzvf $file $firmware")){
++ die "failed to untar firmware";
++ }
++ if( system("rm $file")){
++ die ("unable to remove unnecessary files");
++ }
++
++ $firmware;
++}
+ # ---------------------------------------------------------------
+ # Utilities
+
+diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885
+index 16ca030..87c4634 100644
+--- a/Documentation/video4linux/CARDLIST.cx23885
++++ b/Documentation/video4linux/CARDLIST.cx23885
+@@ -17,9 +17,9 @@
+ 16 -> DVBWorld DVB-S2 2005 [0001:2005]
+ 17 -> NetUP Dual DVB-S2 CI [1b55:2a2c]
+ 18 -> Hauppauge WinTV-HVR1270 [0070:2211]
+- 19 -> Hauppauge WinTV-HVR1275 [0070:2215]
+- 20 -> Hauppauge WinTV-HVR1255 [0070:2251]
+- 21 -> Hauppauge WinTV-HVR1210 [0070:2291,0070:2295]
++ 19 -> Hauppauge WinTV-HVR1275 [0070:2215,0070:221d,0070:22f2]
++ 20 -> Hauppauge WinTV-HVR1255 [0070:2251,0070:2259,0070:22f1]
++ 21 -> Hauppauge WinTV-HVR1210 [0070:2291,0070:2295,0070:2299,0070:229d,0070:22f0,0070:22f3,0070:22f4,0070:22f5]
+ 22 -> Mygica X8506 DMB-TH [14f1:8651]
+ 23 -> Magic-Pro ProHDTV Extreme 2 [14f1:8657]
+ 24 -> Hauppauge WinTV-HVR1850 [0070:8541]
+diff --git a/Documentation/video4linux/extract_xc3028.pl b/Documentation/video4linux/extract_xc3028.pl
+old mode 100644
+new mode 100755
+diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
+index 2ee6c7a..b8a5673 100644
+--- a/drivers/input/evdev.c
++++ b/drivers/input/evdev.c
+@@ -515,6 +515,8 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
+ struct input_absinfo abs;
+ struct ff_effect effect;
+ int __user *ip = (int __user *)p;
++ struct keycode_table_entry kt, *kt_p = p;
++ char scancode[16];
+ unsigned int i, t, u, v;
+ int error;
+
+@@ -569,6 +571,43 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
+
+ return input_set_keycode(dev, t, v);
+
++ case EVIOCGKEYCODEBIG:
++ if (copy_from_user(&kt, kt_p, sizeof(kt)))
++ return -EFAULT;
++
++ if (kt.len > sizeof(scancode))
++ return -EINVAL;
++
++ kt.scancode = scancode;
++
++ error = input_get_keycode_big(dev, &kt);
++ if (error)
++ return error;
++
++ if (copy_to_user(kt_p, &kt, sizeof(kt)))
++ return -EFAULT;
++
++ /* FIXME: probably need some compat32 code */
++ if (copy_to_user(kt_p->scancode, kt.scancode, kt.len))
++ return -EFAULT;
++
++ return 0;
++
++ case EVIOCSKEYCODEBIG:
++ if (copy_from_user(&kt, kt_p, sizeof(kt)))
++ return -EFAULT;
++
++ if (kt.len > sizeof(scancode))
++ return -EINVAL;
++
++ kt.scancode = scancode;
++
++ /* FIXME: probably need some compat32 code */
++ if (copy_from_user(kt.scancode, kt_p->scancode, kt.len))
++ return -EFAULT;
++
++ return input_set_keycode_big(dev, &kt);
++
+ case EVIOCRMFF:
+ return input_ff_erase(dev, (int)(unsigned long) p, file);
+
+diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 9c79bd5..43aeb71 100644
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -568,6 +568,11 @@ static void input_disconnect_device(struct input_dev *dev)
+ spin_unlock_irq(&dev->event_lock);
+ }
+
++/*
++ * Those routines handle the default case where no [gs]etkeycode() is
++ * defined. In this case, an array indexed by the scancode is used.
++ */
++
+ static int input_fetch_keycode(struct input_dev *dev, int scancode)
+ {
+ switch (dev->keycodesize) {
+@@ -582,27 +587,74 @@ static int input_fetch_keycode(struct input_dev *dev, int scancode)
+ }
+ }
+
+-static int input_default_getkeycode(struct input_dev *dev,
+- unsigned int scancode,
+- unsigned int *keycode)
++/*
++ * Supports only 8, 16 and 32 bit scancodes. It wouldn't be that
++ * hard to write some machine-endian logic to support 24 bit scancodes,
++ * but it seemed overkill. It should also be noticed that, since there
++ * are, in general, less than 256 scancodes sparsed into the scancode
++ * space, even with 16 bits, the codespace is sparsed, with leads into
++ * memory and code ineficiency, when retrieving the entire scancode
++ * space.
++ * So, it is highly recommended to implement getkeycodebig/setkeycodebig
++ * instead of using a normal table approach, when more than 8 bits is
++ * needed for the scancode.
++ */
++static int input_fetch_scancode(struct keycode_table_entry *kt_entry,
++ u32 *scancode)
+ {
++ switch (kt_entry->len) {
++ case 1:
++ *scancode = *((u8 *)kt_entry->scancode);
++ break;
++ case 2:
++ *scancode = *((u16 *)kt_entry->scancode);
++ break;
++ case 4:
++ *scancode = *((u32 *)kt_entry->scancode);
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++
++static int input_default_getkeycode_from_index(struct input_dev *dev,
++ struct keycode_table_entry *kt_entry)
++{
++ u32 scancode = kt_entry->index;
++
+ if (!dev->keycodesize)
+ return -EINVAL;
+
+ if (scancode >= dev->keycodemax)
+ return -EINVAL;
+
+- *keycode = input_fetch_keycode(dev, scancode);
++ kt_entry->keycode = input_fetch_keycode(dev, scancode);
++ memcpy(kt_entry->scancode, &scancode, 4);
+
+ return 0;
+ }
+
++static int input_default_getkeycode_from_scancode(struct input_dev *dev,
++ struct keycode_table_entry *kt_entry)
++{
++ if (input_fetch_scancode(kt_entry, &kt_entry->index))
++ return -EINVAL;
++
++ return input_default_getkeycode_from_index(dev, kt_entry);
++}
++
++
+ static int input_default_setkeycode(struct input_dev *dev,
+- unsigned int scancode,
+- unsigned int keycode)
++ struct keycode_table_entry *kt_entry)
+ {
+- int old_keycode;
++ u32 old_keycode;
+ int i;
++ u32 scancode;
++
++ if (input_fetch_scancode(kt_entry, &scancode))
++ return -EINVAL;
+
+ if (scancode >= dev->keycodemax)
+ return -EINVAL;
+@@ -610,32 +662,33 @@ static int input_default_setkeycode(struct input_dev *dev,
+ if (!dev->keycodesize)
+ return -EINVAL;
+
+- if (dev->keycodesize < sizeof(keycode) && (keycode >> (dev->keycodesize * 8)))
++ if (dev->keycodesize < sizeof(dev->keycode) &&
++ (kt_entry->keycode >> (dev->keycodesize * 8)))
+ return -EINVAL;
+
+ switch (dev->keycodesize) {
+ case 1: {
+ u8 *k = (u8 *)dev->keycode;
+ old_keycode = k[scancode];
+- k[scancode] = keycode;
++ k[scancode] = kt_entry->keycode;
+ break;
+ }
+ case 2: {
+ u16 *k = (u16 *)dev->keycode;
+ old_keycode = k[scancode];
+- k[scancode] = keycode;
++ k[scancode] = kt_entry->keycode;
+ break;
+ }
+ default: {
+ u32 *k = (u32 *)dev->keycode;
+ old_keycode = k[scancode];
+- k[scancode] = keycode;
++ k[scancode] = kt_entry->keycode;
+ break;
+ }
+ }
+
+ __clear_bit(old_keycode, dev->keybit);
+- __set_bit(keycode, dev->keybit);
++ __set_bit(kt_entry->keycode, dev->keybit);
+
+ for (i = 0; i < dev->keycodemax; i++) {
+ if (input_fetch_keycode(dev, i) == old_keycode) {
+@@ -648,6 +701,110 @@ static int input_default_setkeycode(struct input_dev *dev,
+ }
+
+ /**
++ * input_get_keycode_big - retrieve keycode currently mapped to a given scancode
++ * @dev: input device which keymap is being queried
++ * @kt_entry: keytable entry
++ *
++ * This function should be called by anyone interested in retrieving current
++ * keymap. Presently evdev handlers use it.
++ */
++int input_get_keycode_big(struct input_dev *dev,
++ struct keycode_table_entry *kt_entry)
++{
++ if (dev->getkeycode) {
++ u32 scancode = kt_entry->index;
++
++ /*
++ * Support for legacy drivers, that don't implement the new
++ * ioctls
++ */
++ memcpy(kt_entry->scancode, &scancode, 4);
++ return dev->getkeycode(dev, scancode,
++ &kt_entry->keycode);
++ } else
++ return dev->getkeycodebig_from_index(dev, kt_entry);
++}
++EXPORT_SYMBOL(input_get_keycode_big);
++
++/**
++ * input_set_keycode_big - attribute a keycode to a given scancode
++ * @dev: input device which keymap is being queried
++ * @kt_entry: keytable entry
++ *
++ * This function should be called by anyone needing to update current
++ * keymap. Presently keyboard and evdev handlers use it.
++ */
++int input_set_keycode_big(struct input_dev *dev,
++ struct keycode_table_entry *kt_entry)
++{
++ unsigned long flags;
++ int old_keycode;
++ int retval = -EINVAL;
++ u32 uninitialized_var(scancode);
++
++ if (kt_entry->keycode < 0 || kt_entry->keycode > KEY_MAX)
++ return -EINVAL;
++
++ spin_lock_irqsave(&dev->event_lock, flags);
++
++ /*
++ * We need to know the old scancode, in order to generate a
++ * keyup effect, if the set operation happens successfully
++ */
++ if (dev->getkeycode) {
++ /*
++ * Support for legacy drivers, that don't implement the new
++ * ioctls
++ */
++ if (!dev->setkeycode)
++ goto out;
++
++ retval = input_fetch_scancode(kt_entry, &scancode);
++ if (retval)
++ goto out;
++
++ retval = dev->getkeycode(dev, scancode,
++ &old_keycode);
++ } else {
++ int new_keycode = kt_entry->keycode;
++
++ retval = dev->getkeycodebig_from_scancode(dev, kt_entry);
++ old_keycode = kt_entry->keycode;
++ kt_entry->keycode = new_keycode;
++ }
++
++ if (retval)
++ goto out;
++
++ if (dev->getkeycode)
++ retval = dev->setkeycode(dev, scancode,
++ kt_entry->keycode);
++ else
++ retval = dev->setkeycodebig(dev, kt_entry);
++ if (retval)
++ goto out;
++
++ /*
++ * Simulate keyup event if keycode is not present
++ * in the keymap anymore
++ */
++ if (test_bit(EV_KEY, dev->evbit) &&
++ !is_event_supported(old_keycode, dev->keybit, KEY_MAX) &&
++ __test_and_clear_bit(old_keycode, dev->key)) {
++
++ input_pass_event(dev, EV_KEY, old_keycode, 0);
++ if (dev->sync)
++ input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
++ }
++
++ out:
++ spin_unlock_irqrestore(&dev->event_lock, flags);
++
++ return retval;
++}
++EXPORT_SYMBOL(input_set_keycode_big);
++
++/**
+ * input_get_keycode - retrieve keycode currently mapped to a given scancode
+ * @dev: input device which keymap is being queried
+ * @scancode: scancode (or its equivalent for device in question) for which
+@@ -661,13 +818,35 @@ int input_get_keycode(struct input_dev *dev,
+ unsigned int scancode, unsigned int *keycode)
+ {
+ unsigned long flags;
+- int retval;
+
+- spin_lock_irqsave(&dev->event_lock, flags);
+- retval = dev->getkeycode(dev, scancode, keycode);
+- spin_unlock_irqrestore(&dev->event_lock, flags);
++ if (dev->getkeycode) {
++ /*
++ * Use the legacy calls
++ */
++ return dev->getkeycode(dev, scancode, keycode);
++ } else {
++ int retval;
++ struct keycode_table_entry kt_entry;
+
+- return retval;
++ /*
++ * Userspace is using a legacy call with a driver ported
++ * to the new way. This is a bad idea with long sparsed
++ * tables, since lots of the retrieved values will be in
++ * blank. Also, it makes sense only if the table size is
++ * lower than 2^32.
++ */
++ memset(&kt_entry, 0, sizeof(kt_entry));
++ kt_entry.len = 4;
++ kt_entry.index = scancode;
++ kt_entry.scancode = (char *)&scancode;
++
++ spin_lock_irqsave(&dev->event_lock, flags);
++ retval = dev->getkeycodebig_from_index(dev, &kt_entry);
++ spin_unlock_irqrestore(&dev->event_lock, flags);
++
++ *keycode = kt_entry.keycode;
++ return retval;
++ }
+ }
+ EXPORT_SYMBOL(input_get_keycode);
+
+@@ -692,13 +871,42 @@ int input_set_keycode(struct input_dev *dev,
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+
+- retval = dev->getkeycode(dev, scancode, &old_keycode);
+- if (retval)
+- goto out;
++ if (dev->getkeycode) {
++ /*
++ * Use the legacy calls
++ */
++ retval = dev->getkeycode(dev, scancode, &old_keycode);
++ if (retval)
++ goto out;
+
+- retval = dev->setkeycode(dev, scancode, keycode);
+- if (retval)
+- goto out;
++ retval = dev->setkeycode(dev, scancode, keycode);
++ if (retval)
++ goto out;
++ } else {
++ struct keycode_table_entry kt_entry;
++
++ /*
++ * Userspace is using a legacy call with a driver ported
++ * to the new way. This is a bad idea with long sparsed
++ * tables, since lots of the retrieved values will be in
++ * blank. Also, it makes sense only if the table size is
++ * lower than 2^32.
++ */
++ memset(&kt_entry, 0, sizeof(kt_entry));
++ kt_entry.len = 4;
++ kt_entry.scancode = (char *)&scancode;
++
++ retval = dev->getkeycodebig_from_scancode(dev, &kt_entry);
++ if (retval)
++ goto out;
++
++ old_keycode = kt_entry.keycode;
++ kt_entry.keycode = keycode;
++
++ retval = dev->setkeycodebig(dev, &kt_entry);
++ if (retval)
++ goto out;
++ }
+
+ /* Make sure KEY_RESERVED did not get enabled. */
+ __clear_bit(KEY_RESERVED, dev->keybit);
+@@ -1636,11 +1843,17 @@ int input_register_device(struct input_dev *dev)
+ dev->rep[REP_PERIOD] = 33;
+ }
+
+- if (!dev->getkeycode)
+- dev->getkeycode = input_default_getkeycode;
++ if (!dev->getkeycode) {
++ if (!dev->getkeycodebig_from_index)
++ dev->getkeycodebig_from_index = input_default_getkeycode_from_index;
++ if (!dev->getkeycodebig_from_scancode)
++ dev->getkeycodebig_from_scancode = input_default_getkeycode_from_scancode;
++ }
+
+- if (!dev->setkeycode)
+- dev->setkeycode = input_default_setkeycode;
++ if (!dev->setkeycode) {
++ if (!dev->setkeycodebig)
++ dev->setkeycodebig = input_default_setkeycode;
++ }
+
+ dev_set_name(&dev->dev, "input%ld",
+ (unsigned long) atomic_inc_return(&input_no) - 1);
+diff --git a/drivers/media/IR/Kconfig b/drivers/media/IR/Kconfig
+index d22a8ec..e557ae0 100644
+--- a/drivers/media/IR/Kconfig
++++ b/drivers/media/IR/Kconfig
+@@ -8,6 +8,17 @@ config VIDEO_IR
+ depends on IR_CORE
+ default IR_CORE
+
++config LIRC
++ tristate
++ default y
++
++ ---help---
++ Enable this option to build the Linux Infrared Remote
++ Control (LIRC) core device interface driver. The LIRC
++ interface passes raw IR to and from userspace, where the
++ LIRC daemon handles protocol decoding for IR reception ann
++ encoding for IR transmitting (aka "blasting").
++
+ source "drivers/media/IR/keymaps/Kconfig"
+
+ config IR_NEC_DECODER
+@@ -42,6 +53,7 @@ config IR_RC6_DECODER
+ config IR_JVC_DECODER
+ tristate "Enable IR raw decoder for the JVC protocol"
+ depends on IR_CORE
++ select BITREVERSE
+ default y
+
+ ---help---
+@@ -57,6 +69,16 @@ config IR_SONY_DECODER
+ Enable this option if you have an infrared remote control which
+ uses the Sony protocol, and you need software decoding support.
+
++config IR_LIRC_CODEC
++ tristate "Enable IR to LIRC bridge"
++ depends on IR_CORE
++ depends on LIRC
++ default y
++
++ ---help---
++ Enable this option to pass raw IR to and from userspace via
++ the LIRC interface.
++
+ config IR_IMON
+ tristate "SoundGraph iMON Receiver and Display"
+ depends on USB_ARCH_HAS_HCD
+@@ -68,3 +90,15 @@ config IR_IMON
+
+ To compile this driver as a module, choose M here: the
+ module will be called imon.
++
++config IR_MCEUSB
++ tristate "Windows Media Center Ed. eHome Infrared Transceiver"
++ depends on USB_ARCH_HAS_HCD
++ depends on IR_CORE
++ select USB
++ ---help---
++ Say Y here if you want to use a Windows Media Center Edition
++ eHome Infrared Transceiver.
++
++ To compile this driver as a module, choose M here: the
++ module will be called mceusb.
+diff --git a/drivers/media/IR/Makefile b/drivers/media/IR/Makefile
+index b998fcc..2ae4f3a 100644
+--- a/drivers/media/IR/Makefile
++++ b/drivers/media/IR/Makefile
+@@ -5,11 +5,14 @@ obj-y += keymaps/
+
+ obj-$(CONFIG_IR_CORE) += ir-core.o
+ obj-$(CONFIG_VIDEO_IR) += ir-common.o
++obj-$(CONFIG_LIRC) += lirc_dev.o
+ obj-$(CONFIG_IR_NEC_DECODER) += ir-nec-decoder.o
+ obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o
+ obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o
+ obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o
+ obj-$(CONFIG_IR_SONY_DECODER) += ir-sony-decoder.o
++obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o
+
+ # stand-alone IR receivers/transmitters
+ obj-$(CONFIG_IR_IMON) += imon.o
++obj-$(CONFIG_IR_MCEUSB) += mceusb.o
+diff --git a/drivers/media/IR/imon.c b/drivers/media/IR/imon.c
+index 4bbd45f..0195dd5 100644
+--- a/drivers/media/IR/imon.c
++++ b/drivers/media/IR/imon.c
+@@ -1943,7 +1943,7 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf)
+ return ictx;
+
+ urb_submit_failed:
+- input_unregister_device(ictx->idev);
++ ir_input_unregister(ictx->idev);
+ input_free_device(ictx->idev);
+ idev_setup_failed:
+ find_endpoint_failed:
+@@ -2067,6 +2067,7 @@ static void imon_get_ffdc_type(struct imon_context *ictx)
+ detected_display_type = IMON_DISPLAY_TYPE_VFD;
+ break;
+ /* iMON LCD, MCE IR */
++ case 0x9e:
+ case 0x9f:
+ dev_info(ictx->dev, "0xffdc iMON LCD, MCE IR");
+ detected_display_type = IMON_DISPLAY_TYPE_LCD;
+@@ -2306,7 +2307,7 @@ static void __devexit imon_disconnect(struct usb_interface *interface)
+ if (ifnum == 0) {
+ ictx->dev_present_intf0 = false;
+ usb_kill_urb(ictx->rx_urb_intf0);
+- input_unregister_device(ictx->idev);
++ ir_input_unregister(ictx->idev);
+ if (ictx->display_supported) {
+ if (ictx->display_type == IMON_DISPLAY_TYPE_LCD)
+ usb_deregister_dev(interface, &imon_lcd_class);
+diff --git a/drivers/media/IR/ir-core-priv.h b/drivers/media/IR/ir-core-priv.h
+index 9a5e65a..babd520 100644
+--- a/drivers/media/IR/ir-core-priv.h
++++ b/drivers/media/IR/ir-core-priv.h
+@@ -22,17 +22,62 @@
+ struct ir_raw_handler {
+ struct list_head list;
+
++ u64 protocols; /* which are handled by this handler */
+ int (*decode)(struct input_dev *input_dev, struct ir_raw_event event);
++
++ /* These two should only be used by the lirc decoder */
+ int (*raw_register)(struct input_dev *input_dev);
+ int (*raw_unregister)(struct input_dev *input_dev);
+ };
+
+ struct ir_raw_event_ctrl {
++ struct list_head list; /* to keep track of raw clients */
+ struct work_struct rx_work; /* for the rx decoding workqueue */
+ struct kfifo kfifo; /* fifo for the pulse/space durations */
+ ktime_t last_event; /* when last event occurred */
+ enum raw_event_type last_type; /* last event type */
+ struct input_dev *input_dev; /* pointer to the parent input_dev */
++ u64 enabled_protocols; /* enabled raw protocol decoders */
++
++ /* raw decoder state follows */
++ struct ir_raw_event prev_ev;
++ struct nec_dec {
++ int state;
++ unsigned count;
++ u32 bits;
++ } nec;
++ struct rc5_dec {
++ int state;
++ u32 bits;
++ unsigned count;
++ unsigned wanted_bits;
++ } rc5;
++ struct rc6_dec {
++ int state;
++ u8 header;
++ u32 body;
++ bool toggle;
++ unsigned count;
++ unsigned wanted_bits;
++ } rc6;
++ struct sony_dec {
++ int state;
++ u32 bits;
++ unsigned count;
++ } sony;
++ struct jvc_dec {
++ int state;
++ u16 bits;
++ u16 old_bits;
++ unsigned count;
++ bool first;
++ bool toggle;
++ } jvc;
++ struct lirc_codec {
++ struct ir_input_dev *ir_dev;
++ struct lirc_driver *drv;
++ int lircdata;
++ } lirc;
+ };
+
+ /* macros for IR decoders */
+@@ -74,6 +119,7 @@ void ir_unregister_class(struct input_dev *input_dev);
+ /*
+ * Routines from ir-raw-event.c to be used internally and by decoders
+ */
++u64 ir_raw_get_allowed_protocols(void);
+ int ir_raw_event_register(struct input_dev *input_dev);
+ void ir_raw_event_unregister(struct input_dev *input_dev);
+ int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler);
+@@ -123,4 +169,12 @@ void ir_raw_init(void);
+ #define load_sony_decode() 0
+ #endif
+
++/* from ir-lirc-codec.c */
++#ifdef CONFIG_IR_LIRC_CODEC_MODULE
++#define load_lirc_codec() request_module("ir-lirc-codec")
++#else
++#define load_lirc_codec() 0
++#endif
++
++
+ #endif /* _IR_RAW_EVENT */
+diff --git a/drivers/media/IR/ir-jvc-decoder.c b/drivers/media/IR/ir-jvc-decoder.c
+index 0b80494..8894d8b 100644
+--- a/drivers/media/IR/ir-jvc-decoder.c
++++ b/drivers/media/IR/ir-jvc-decoder.c
+@@ -25,10 +25,6 @@
+ #define JVC_TRAILER_PULSE (1 * JVC_UNIT)
+ #define JVC_TRAILER_SPACE (35 * JVC_UNIT)
+
+-/* Used to register jvc_decoder clients */
+-static LIST_HEAD(decoder_list);
+-DEFINE_SPINLOCK(decoder_lock);
+-
+ enum jvc_state {
+ STATE_INACTIVE,
+ STATE_HEADER_SPACE,
+@@ -38,87 +34,6 @@ enum jvc_state {
+ STATE_TRAILER_SPACE,
+ };
+
+-struct decoder_data {
+- struct list_head list;
+- struct ir_input_dev *ir_dev;
+- int enabled:1;
+-
+- /* State machine control */
+- enum jvc_state state;
+- u16 jvc_bits;
+- u16 jvc_old_bits;
+- unsigned count;
+- bool first;
+- bool toggle;
+-};
+-
+-
+-/**
+- * get_decoder_data() - gets decoder data
+- * @input_dev: input device
+- *
+- * Returns the struct decoder_data that corresponds to a device
+- */
+-static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev)
+-{
+- struct decoder_data *data = NULL;
+-
+- spin_lock(&decoder_lock);
+- list_for_each_entry(data, &decoder_list, list) {
+- if (data->ir_dev == ir_dev)
+- break;
+- }
+- spin_unlock(&decoder_lock);
+- return data;
+-}
+-
+-static ssize_t store_enabled(struct device *d,
+- struct device_attribute *mattr,
+- const char *buf,
+- size_t len)
+-{
+- unsigned long value;
+- struct ir_input_dev *ir_dev = dev_get_drvdata(d);
+- struct decoder_data *data = get_decoder_data(ir_dev);
+-
+- if (!data)
+- return -EINVAL;
+-
+- if (strict_strtoul(buf, 10, &value) || value > 1)
+- return -EINVAL;
+-
+- data->enabled = value;
+-
+- return len;
+-}
+-
+-static ssize_t show_enabled(struct device *d,
+- struct device_attribute *mattr, char *buf)
+-{
+- struct ir_input_dev *ir_dev = dev_get_drvdata(d);
+- struct decoder_data *data = get_decoder_data(ir_dev);
+-
+- if (!data)
+- return -EINVAL;
+-
+- if (data->enabled)
+- return sprintf(buf, "1\n");
+- else
+- return sprintf(buf, "0\n");
+-}
+-
+-static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled);
+-
+-static struct attribute *decoder_attributes[] = {
+- &dev_attr_enabled.attr,
+- NULL
+-};
+-
+-static struct attribute_group decoder_attribute_group = {
+- .name = "jvc_decoder",
+- .attrs = decoder_attributes,
+-};
+-
+ /**
+ * ir_jvc_decode() - Decode one JVC pulse or space
+ * @input_dev: the struct input_dev descriptor of the device
+@@ -128,14 +43,10 @@ static struct attribute_group decoder_attribute_group = {
+ */
+ static int ir_jvc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
+ {
+- struct decoder_data *data;
+ struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
++ struct jvc_dec *data = &ir_dev->raw->jvc;
+
+- data = get_decoder_data(ir_dev);
+- if (!data)
+- return -EINVAL;
+-
+- if (!data->enabled)
++ if (!(ir_dev->raw->enabled_protocols & IR_TYPE_JVC))
+ return 0;
+
+ if (IS_RESET(ev)) {
+@@ -188,9 +99,9 @@ static int ir_jvc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
+ if (ev.pulse)
+ break;
+
+- data->jvc_bits <<= 1;
++ data->bits <<= 1;
+ if (eq_margin(ev.duration, JVC_BIT_1_SPACE, JVC_UNIT / 2)) {
+- data->jvc_bits |= 1;
++ data->bits |= 1;
+ decrease_duration(&ev, JVC_BIT_1_SPACE);
+ } else if (eq_margin(ev.duration, JVC_BIT_0_SPACE, JVC_UNIT / 2))
+ decrease_duration(&ev, JVC_BIT_0_SPACE);
+@@ -223,13 +134,13 @@ static int ir_jvc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
+
+ if (data->first) {
+ u32 scancode;
+- scancode = (bitrev8((data->jvc_bits >> 8) & 0xff) << 8) |
+- (bitrev8((data->jvc_bits >> 0) & 0xff) << 0);
++ scancode = (bitrev8((data->bits >> 8) & 0xff) << 8) |
++ (bitrev8((data->bits >> 0) & 0xff) << 0);
+ IR_dprintk(1, "JVC scancode 0x%04x\n", scancode);
+ ir_keydown(input_dev, scancode, data->toggle);
+ data->first = false;
+- data->jvc_old_bits = data->jvc_bits;
+- } else if (data->jvc_bits == data->jvc_old_bits) {
++ data->old_bits = data->bits;
++ } else if (data->bits == data->old_bits) {
+ IR_dprintk(1, "JVC repeat\n");
+ ir_repeat(input_dev);
+ } else {
+@@ -249,54 +160,9 @@ out:
+ return -EINVAL;
+ }
+
+-static int ir_jvc_register(struct input_dev *input_dev)
+-{
+- struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+- struct decoder_data *data;
+- int rc;
+-
+- rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group);
+- if (rc < 0)
+- return rc;
+-
+- data = kzalloc(sizeof(*data), GFP_KERNEL);
+- if (!data) {
+- sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group);
+- return -ENOMEM;
+- }
+-
+- data->ir_dev = ir_dev;
+- data->enabled = 1;
+-
+- spin_lock(&decoder_lock);
+- list_add_tail(&data->list, &decoder_list);
+- spin_unlock(&decoder_lock);
+-
+- return 0;
+-}
+-
+-static int ir_jvc_unregister(struct input_dev *input_dev)
+-{
+- struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+- static struct decoder_data *data;
+-
+- data = get_decoder_data(ir_dev);
+- if (!data)
+- return 0;
+-
+- sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group);
+-
+- spin_lock(&decoder_lock);
+- list_del(&data->list);
+- spin_unlock(&decoder_lock);
+-
+- return 0;
+-}
+-
+ static struct ir_raw_handler jvc_handler = {
++ .protocols = IR_TYPE_JVC,
+ .decode = ir_jvc_decode,
+- .raw_register = ir_jvc_register,
+- .raw_unregister = ir_jvc_unregister,
+ };
+
+ static int __init ir_jvc_decode_init(void)
+diff --git a/drivers/media/IR/ir-lirc-codec.c b/drivers/media/IR/ir-lirc-codec.c
+new file mode 100644
+index 0000000..afb1ada
+--- /dev/null
++++ b/drivers/media/IR/ir-lirc-codec.c
+@@ -0,0 +1,283 @@
++/* ir-lirc-codec.c - ir-core to classic lirc interface bridge
++ *
++ * Copyright (C) 2010 by Jarod Wilson <jarod@redhat.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 of the License.
++ *
++ * 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.
++ */
++
++#include <linux/sched.h>
++#include <linux/wait.h>
++#include <media/lirc.h>
++#include <media/lirc_dev.h>
++#include <media/ir-core.h>
++#include "ir-core-priv.h"
++
++#define LIRCBUF_SIZE 256
++
++/**
++ * ir_lirc_decode() - Send raw IR data to lirc_dev to be relayed to the
++ * lircd userspace daemon for decoding.
++ * @input_dev: the struct input_dev descriptor of the device
++ * @duration: the struct ir_raw_event descriptor of the pulse/space
++ *
++ * This function returns -EINVAL if the lirc interfaces aren't wired up.
++ */
++static int ir_lirc_decode(struct input_dev *input_dev, struct ir_raw_event ev)
++{
++ struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
++
++ if (!(ir_dev->raw->enabled_protocols & IR_TYPE_LIRC))
++ return 0;
++
++ if (!ir_dev->raw->lirc.drv || !ir_dev->raw->lirc.drv->rbuf)
++ return -EINVAL;
++
++ IR_dprintk(2, "LIRC data transfer started (%uus %s)\n",
++ TO_US(ev.duration), TO_STR(ev.pulse));
++
++ ir_dev->raw->lirc.lircdata += ev.duration / 1000;
++ if (ev.pulse)
++ ir_dev->raw->lirc.lircdata |= PULSE_BIT;
++
++ lirc_buffer_write(ir_dev->raw->lirc.drv->rbuf,
++ (unsigned char *) &ir_dev->raw->lirc.lircdata);
++ wake_up(&ir_dev->raw->lirc.drv->rbuf->wait_poll);
++
++ ir_dev->raw->lirc.lircdata = 0;
++
++ return 0;
++}
++
++static ssize_t ir_lirc_transmit_ir(struct file *file, const char *buf,
++ size_t n, loff_t *ppos)
++{
++ struct lirc_codec *lirc;
++ struct ir_input_dev *ir_dev;
++ int *txbuf; /* buffer with values to transmit */
++ int ret = 0, count;
++
++ lirc = lirc_get_pdata(file);
++ if (!lirc)
++ return -EFAULT;
++
++ if (n % sizeof(int))
++ return -EINVAL;
++
++ count = n / sizeof(int);
++ if (count > LIRCBUF_SIZE || count % 2 == 0)
++ return -EINVAL;
++
++ txbuf = kzalloc(sizeof(int) * LIRCBUF_SIZE, GFP_KERNEL);
++ if (!txbuf)
++ return -ENOMEM;
++
++ if (copy_from_user(txbuf, buf, n)) {
++ ret = -EFAULT;
++ goto out;
++ }
++
++ ir_dev = lirc->ir_dev;
++ if (!ir_dev) {
++ ret = -EFAULT;
++ goto out;
++ }
++
++ if (ir_dev->props && ir_dev->props->tx_ir)
++ ret = ir_dev->props->tx_ir(ir_dev->props->priv, txbuf, (u32)n);
++
++out:
++ kfree(txbuf);
++ return ret;
++}
++
++static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
++{
++ struct lirc_codec *lirc;
++ struct ir_input_dev *ir_dev;
++ int ret = 0;
++ void *drv_data;
++ unsigned long val;
++
++ lirc = lirc_get_pdata(filep);
++ if (!lirc)
++ return -EFAULT;
++
++ ir_dev = lirc->ir_dev;
++ if (!ir_dev || !ir_dev->props || !ir_dev->props->priv)
++ return -EFAULT;
++
++ drv_data = ir_dev->props->priv;
++
++ switch (cmd) {
++ case LIRC_SET_TRANSMITTER_MASK:
++ ret = get_user(val, (unsigned long *)arg);
++ if (ret)
++ return ret;
++
++ if (ir_dev->props && ir_dev->props->s_tx_mask)
++ ret = ir_dev->props->s_tx_mask(drv_data, (u32)val);
++ else
++ return -EINVAL;
++ break;
++
++ case LIRC_SET_SEND_CARRIER:
++ ret = get_user(val, (unsigned long *)arg);
++ if (ret)
++ return ret;
++
++ if (ir_dev->props && ir_dev->props->s_tx_carrier)
++ ir_dev->props->s_tx_carrier(drv_data, (u32)val);
++ else
++ return -EINVAL;
++ break;
++
++ case LIRC_GET_SEND_MODE:
++ val = LIRC_CAN_SEND_PULSE & LIRC_CAN_SEND_MASK;
++ ret = put_user(val, (unsigned long *)arg);
++ break;
++
++ case LIRC_SET_SEND_MODE:
++ ret = get_user(val, (unsigned long *)arg);
++ if (ret)
++ return ret;
++
++ if (val != (LIRC_MODE_PULSE & LIRC_CAN_SEND_MASK))
++ return -EINVAL;
++ break;
++
++ default:
++ return lirc_dev_fop_ioctl(filep, cmd, arg);
++ }
++
++ return ret;
++}
++
++static int ir_lirc_open(void *data)
++{
++ return 0;
++}
++
++static void ir_lirc_close(void *data)
++{
++ return;
++}
++
++static struct file_operations lirc_fops = {
++ .owner = THIS_MODULE,
++ .write = ir_lirc_transmit_ir,
++ .unlocked_ioctl = ir_lirc_ioctl,
++ .read = lirc_dev_fop_read,
++ .poll = lirc_dev_fop_poll,
++ .open = lirc_dev_fop_open,
++ .release = lirc_dev_fop_close,
++};
++
++static int ir_lirc_register(struct input_dev *input_dev)
++{
++ struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
++ struct lirc_driver *drv;
++ struct lirc_buffer *rbuf;
++ int rc = -ENOMEM;
++ unsigned long features;
++
++ drv = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
++ if (!drv)
++ return rc;
++
++ rbuf = kzalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
++ if (!drv)
++ goto rbuf_alloc_failed;
++
++ rc = lirc_buffer_init(rbuf, sizeof(int), LIRCBUF_SIZE);
++ if (rc)
++ goto rbuf_init_failed;
++
++ features = LIRC_CAN_REC_MODE2;
++ if (ir_dev->props->tx_ir) {
++ features |= LIRC_CAN_SEND_PULSE;
++ if (ir_dev->props->s_tx_mask)
++ features |= LIRC_CAN_SET_TRANSMITTER_MASK;
++ if (ir_dev->props->s_tx_carrier)
++ features |= LIRC_CAN_SET_SEND_CARRIER;
++ }
++
++ snprintf(drv->name, sizeof(drv->name), "ir-lirc-codec (%s)",
++ ir_dev->driver_name);
++ drv->minor = -1;
++ drv->features = features;
++ drv->data = &ir_dev->raw->lirc;
++ drv->rbuf = rbuf;
++ drv->set_use_inc = &ir_lirc_open;
++ drv->set_use_dec = &ir_lirc_close;
++ drv->code_length = sizeof(struct ir_raw_event) * 8;
++ drv->fops = &lirc_fops;
++ drv->dev = &ir_dev->dev;
++ drv->owner = THIS_MODULE;
++
++ drv->minor = lirc_register_driver(drv);
++ if (drv->minor < 0) {
++ rc = -ENODEV;
++ goto lirc_register_failed;
++ }
++
++ ir_dev->raw->lirc.drv = drv;
++ ir_dev->raw->lirc.ir_dev = ir_dev;
++ ir_dev->raw->lirc.lircdata = PULSE_MASK;
++
++ return 0;
++
++lirc_register_failed:
++rbuf_init_failed:
++ kfree(rbuf);
++rbuf_alloc_failed:
++ kfree(drv);
++
++ return rc;
++}
++
++static int ir_lirc_unregister(struct input_dev *input_dev)
++{
++ struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
++ struct lirc_codec *lirc = &ir_dev->raw->lirc;
++
++ lirc_unregister_driver(lirc->drv->minor);
++ lirc_buffer_free(lirc->drv->rbuf);
++ kfree(lirc->drv);
++
++ return 0;
++}
++
++static struct ir_raw_handler lirc_handler = {
++ .protocols = IR_TYPE_LIRC,
++ .decode = ir_lirc_decode,
++ .raw_register = ir_lirc_register,
++ .raw_unregister = ir_lirc_unregister,
++};
++
++static int __init ir_lirc_codec_init(void)
++{
++ ir_raw_handler_register(&lirc_handler);
++
++ printk(KERN_INFO "IR LIRC bridge handler initialized\n");
++ return 0;
++}
++
++static void __exit ir_lirc_codec_exit(void)
++{
++ ir_raw_handler_unregister(&lirc_handler);
++}
++
++module_init(ir_lirc_codec_init);
++module_exit(ir_lirc_codec_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
++MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)");
++MODULE_DESCRIPTION("LIRC IR handler bridge");
+diff --git a/drivers/media/IR/ir-nec-decoder.c b/drivers/media/IR/ir-nec-decoder.c
+index ba79233..52e0f37 100644
+--- a/drivers/media/IR/ir-nec-decoder.c
++++ b/drivers/media/IR/ir-nec-decoder.c
+@@ -27,10 +27,6 @@
+ #define NEC_TRAILER_PULSE (1 * NEC_UNIT)
+ #define NEC_TRAILER_SPACE (10 * NEC_UNIT) /* even longer in reality */
+
+-/* Used to register nec_decoder clients */
+-static LIST_HEAD(decoder_list);
+-static DEFINE_SPINLOCK(decoder_lock);
+-
+ enum nec_state {
+ STATE_INACTIVE,
+ STATE_HEADER_SPACE,
+@@ -40,84 +36,6 @@ enum nec_state {
+ STATE_TRAILER_SPACE,
+ };
+
+-struct decoder_data {
+- struct list_head list;
+- struct ir_input_dev *ir_dev;
+- int enabled:1;
+-
+- /* State machine control */
+- enum nec_state state;
+- u32 nec_bits;
+- unsigned count;
+-};
+-
+-
+-/**
+- * get_decoder_data() - gets decoder data
+- * @input_dev: input device
+- *
+- * Returns the struct decoder_data that corresponds to a device
+- */
+-static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev)
+-{
+- struct decoder_data *data = NULL;
+-
+- spin_lock(&decoder_lock);
+- list_for_each_entry(data, &decoder_list, list) {
+- if (data->ir_dev == ir_dev)
+- break;
+- }
+- spin_unlock(&decoder_lock);
+- return data;
+-}
+-
+-static ssize_t store_enabled(struct device *d,
+- struct device_attribute *mattr,
+- const char *buf,
+- size_t len)
+-{
+- unsigned long value;
+- struct ir_input_dev *ir_dev = dev_get_drvdata(d);
+- struct decoder_data *data = get_decoder_data(ir_dev);
+-
+- if (!data)
+- return -EINVAL;
+-
+- if (strict_strtoul(buf, 10, &value) || value > 1)
+- return -EINVAL;
+-
+- data->enabled = value;
+-
+- return len;
+-}
+-
+-static ssize_t show_enabled(struct device *d,
+- struct device_attribute *mattr, char *buf)
+-{
+- struct ir_input_dev *ir_dev = dev_get_drvdata(d);
+- struct decoder_data *data = get_decoder_data(ir_dev);
+-
+- if (!data)
+- return -EINVAL;
+-
+- if (data->enabled)
+- return sprintf(buf, "1\n");
+- else
+- return sprintf(buf, "0\n");
+-}
+-
+-static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled);
+-
+-static struct attribute *decoder_attributes[] = {
+- &dev_attr_enabled.attr,
+- NULL
+-};
+-
+-static struct attribute_group decoder_attribute_group = {
+- .name = "nec_decoder",
+- .attrs = decoder_attributes,
+-};
+-
+ /**
+ * ir_nec_decode() - Decode one NEC pulse or space
+ * @input_dev: the struct input_dev descriptor of the device
+@@ -127,16 +45,12 @@ static struct attribute_group decoder_attribute_group = {
+ */
+ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev)
+ {
+- struct decoder_data *data;
+ struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
++ struct nec_dec *data = &ir_dev->raw->nec;
+ u32 scancode;
+ u8 address, not_address, command, not_command;
+
+- data = get_decoder_data(ir_dev);
+- if (!data)
+- return -EINVAL;
+-
+- if (!data->enabled)
++ if (!(ir_dev->raw->enabled_protocols & IR_TYPE_NEC))
+ return 0;
+
+ if (IS_RESET(ev)) {
+@@ -191,9 +105,9 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev)
+ if (ev.pulse)
+ break;
+
+- data->nec_bits <<= 1;
++ data->bits <<= 1;
+ if (eq_margin(ev.duration, NEC_BIT_1_SPACE, NEC_UNIT / 2))
+- data->nec_bits |= 1;
++ data->bits |= 1;
+ else if (!eq_margin(ev.duration, NEC_BIT_0_SPACE, NEC_UNIT / 2))
+ break;
+ data->count++;
+@@ -222,14 +136,14 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev)
+ if (!geq_margin(ev.duration, NEC_TRAILER_SPACE, NEC_UNIT / 2))
+ break;
+
+- address = bitrev8((data->nec_bits >> 24) & 0xff);
+- not_address = bitrev8((data->nec_bits >> 16) & 0xff);
+- command = bitrev8((data->nec_bits >> 8) & 0xff);
+- not_command = bitrev8((data->nec_bits >> 0) & 0xff);
++ address = bitrev8((data->bits >> 24) & 0xff);
++ not_address = bitrev8((data->bits >> 16) & 0xff);
++ command = bitrev8((data->bits >> 8) & 0xff);
++ not_command = bitrev8((data->bits >> 0) & 0xff);
+
+ if ((command ^ not_command) != 0xff) {
+ IR_dprintk(1, "NEC checksum error: received 0x%08x\n",
+- data->nec_bits);
++ data->bits);
+ break;
+ }
+
+@@ -256,54 +170,9 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev)
+ return -EINVAL;
+ }
+
+-static int ir_nec_register(struct input_dev *input_dev)
+-{
+- struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+- struct decoder_data *data;
+- int rc;
+-
+- rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group);
+- if (rc < 0)
+- return rc;
+-
+- data = kzalloc(sizeof(*data), GFP_KERNEL);
+- if (!data) {
+- sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group);
+- return -ENOMEM;
+- }
+-
+- data->ir_dev = ir_dev;
+- data->enabled = 1;
+-
+- spin_lock(&decoder_lock);
+- list_add_tail(&data->list, &decoder_list);
+- spin_unlock(&decoder_lock);
+-
+- return 0;
+-}
+-
+-static int ir_nec_unregister(struct input_dev *input_dev)
+-{
+- struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+- static struct decoder_data *data;
+-
+- data = get_decoder_data(ir_dev);
+- if (!data)
+- return 0;
+-
+- sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group);
+-
+- spin_lock(&decoder_lock);
+- list_del(&data->list);
+- spin_unlock(&decoder_lock);
+-
+- return 0;
+-}
+-
+ static struct ir_raw_handler nec_handler = {
++ .protocols = IR_TYPE_NEC,
+ .decode = ir_nec_decode,
+- .raw_register = ir_nec_register,
+- .raw_unregister = ir_nec_unregister,
+ };
+
+ static int __init ir_nec_decode_init(void)
+diff --git a/drivers/media/IR/ir-raw-event.c b/drivers/media/IR/ir-raw-event.c
+index ea68a3f..6f192ef 100644
+--- a/drivers/media/IR/ir-raw-event.c
++++ b/drivers/media/IR/ir-raw-event.c
+@@ -20,35 +20,13 @@
+ /* Define the max number of pulse/space transitions to buffer */
+ #define MAX_IR_EVENT_SIZE 512
+
++/* Used to keep track of IR raw clients, protected by ir_raw_handler_lock */
++static LIST_HEAD(ir_raw_client_list);
++
+ /* Used to handle IR raw handler extensions */
+-static LIST_HEAD(ir_raw_handler_list);
+ static DEFINE_SPINLOCK(ir_raw_handler_lock);
+-
+-/**
+- * RUN_DECODER() - runs an operation on all IR decoders
+- * @ops: IR raw handler operation to be called
+- * @arg: arguments to be passed to the callback
+- *
+- * Calls ir_raw_handler::ops for all registered IR handlers. It prevents
+- * new decode addition/removal while running, by locking ir_raw_handler_lock
+- * mutex. If an error occurs, it stops the ops. Otherwise, it returns a sum
+- * of the return codes.
+- */
+-#define RUN_DECODER(ops, ...) ({ \
+- struct ir_raw_handler *_ir_raw_handler; \
+- int _sumrc = 0, _rc; \
+- spin_lock(&ir_raw_handler_lock); \
+- list_for_each_entry(_ir_raw_handler, &ir_raw_handler_list, list) { \
+- if (_ir_raw_handler->ops) { \
+- _rc = _ir_raw_handler->ops(__VA_ARGS__); \
+- if (_rc < 0) \
+- break; \
+- _sumrc += _rc; \
+- } \
+- } \
+- spin_unlock(&ir_raw_handler_lock); \
+- _sumrc; \
+-})
++static LIST_HEAD(ir_raw_handler_list);
++static u64 available_protocols;
+
+ #ifdef MODULE
+ /* Used to load the decoders */
+@@ -58,57 +36,17 @@ static struct work_struct wq_load;
+ static void ir_raw_event_work(struct work_struct *work)
+ {
+ struct ir_raw_event ev;
++ struct ir_raw_handler *handler;
+ struct ir_raw_event_ctrl *raw =
+ container_of(work, struct ir_raw_event_ctrl, rx_work);
+
+- while (kfifo_out(&raw->kfifo, &ev, sizeof(ev)) == sizeof(ev))
+- RUN_DECODER(decode, raw->input_dev, ev);
+-}
+-
+-int ir_raw_event_register(struct input_dev *input_dev)
+-{
+- struct ir_input_dev *ir = input_get_drvdata(input_dev);
+- int rc;
+-
+- ir->raw = kzalloc(sizeof(*ir->raw), GFP_KERNEL);
+- if (!ir->raw)
+- return -ENOMEM;
+-
+- ir->raw->input_dev = input_dev;
+- INIT_WORK(&ir->raw->rx_work, ir_raw_event_work);
+-
+- rc = kfifo_alloc(&ir->raw->kfifo, sizeof(s64) * MAX_IR_EVENT_SIZE,
+- GFP_KERNEL);
+- if (rc < 0) {
+- kfree(ir->raw);
+- ir->raw = NULL;
+- return rc;
+- }
+-
+- rc = RUN_DECODER(raw_register, input_dev);
+- if (rc < 0) {
+- kfifo_free(&ir->raw->kfifo);
+- kfree(ir->raw);
+- ir->raw = NULL;
+- return rc;
++ while (kfifo_out(&raw->kfifo, &ev, sizeof(ev)) == sizeof(ev)) {
++ spin_lock(&ir_raw_handler_lock);
++ list_for_each_entry(handler, &ir_raw_handler_list, list)
++ handler->decode(raw->input_dev, ev);
++ spin_unlock(&ir_raw_handler_lock);
++ raw->prev_ev = ev;
+ }
+-
+- return rc;
+-}
+-
+-void ir_raw_event_unregister(struct input_dev *input_dev)
+-{
+- struct ir_input_dev *ir = input_get_drvdata(input_dev);
+-
+- if (!ir->raw)
+- return;
+-
+- cancel_work_sync(&ir->raw->rx_work);
+- RUN_DECODER(raw_unregister, input_dev);
+-
+- kfifo_free(&ir->raw->kfifo);
+- kfree(ir->raw);
+- ir->raw = NULL;
+ }
+
+ /**
+@@ -204,23 +142,103 @@ void ir_raw_event_handle(struct input_dev *input_dev)
+ }
+ EXPORT_SYMBOL_GPL(ir_raw_event_handle);
+
++/* used internally by the sysfs interface */
++u64
++ir_raw_get_allowed_protocols()
++{
++ u64 protocols;
++ spin_lock(&ir_raw_handler_lock);
++ protocols = available_protocols;
++ spin_unlock(&ir_raw_handler_lock);
++ return protocols;
++}
++
++/*
++ * Used to (un)register raw event clients
++ */
++int ir_raw_event_register(struct input_dev *input_dev)
++{
++ struct ir_input_dev *ir = input_get_drvdata(input_dev);
++ int rc;
++ struct ir_raw_handler *handler;
++
++ ir->raw = kzalloc(sizeof(*ir->raw), GFP_KERNEL);
++ if (!ir->raw)
++ return -ENOMEM;
++
++ ir->raw->input_dev = input_dev;
++ INIT_WORK(&ir->raw->rx_work, ir_raw_event_work);
++ ir->raw->enabled_protocols = ~0;
++ rc = kfifo_alloc(&ir->raw->kfifo, sizeof(s64) * MAX_IR_EVENT_SIZE,
++ GFP_KERNEL);
++ if (rc < 0) {
++ kfree(ir->raw);
++ ir->raw = NULL;
++ return rc;
++ }
++
++ spin_lock(&ir_raw_handler_lock);
++ list_add_tail(&ir->raw->list, &ir_raw_client_list);
++ list_for_each_entry(handler, &ir_raw_handler_list, list)
++ if (handler->raw_register)
++ handler->raw_register(ir->raw->input_dev);
++ spin_unlock(&ir_raw_handler_lock);
++
++ return 0;
++}
++
++void ir_raw_event_unregister(struct input_dev *input_dev)
++{
++ struct ir_input_dev *ir = input_get_drvdata(input_dev);
++ struct ir_raw_handler *handler;
++
++ if (!ir->raw)
++ return;
++
++ cancel_work_sync(&ir->raw->rx_work);
++
++ spin_lock(&ir_raw_handler_lock);
++ list_del(&ir->raw->list);
++ list_for_each_entry(handler, &ir_raw_handler_list, list)
++ if (handler->raw_unregister)
++ handler->raw_unregister(ir->raw->input_dev);
++ spin_unlock(&ir_raw_handler_lock);
++
++ kfifo_free(&ir->raw->kfifo);
++ kfree(ir->raw);
++ ir->raw = NULL;
++}
++
+ /*
+ * Extension interface - used to register the IR decoders
+ */
+
+ int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler)
+ {
++ struct ir_raw_event_ctrl *raw;
++
+ spin_lock(&ir_raw_handler_lock);
+ list_add_tail(&ir_raw_handler->list, &ir_raw_handler_list);
++ if (ir_raw_handler->raw_register)
++ list_for_each_entry(raw, &ir_raw_client_list, list)
++ ir_raw_handler->raw_register(raw->input_dev);
++ available_protocols |= ir_raw_handler->protocols;
+ spin_unlock(&ir_raw_handler_lock);
++
+ return 0;
+ }
+ EXPORT_SYMBOL(ir_raw_handler_register);
+
+ void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler)
+ {
++ struct ir_raw_event_ctrl *raw;
++
+ spin_lock(&ir_raw_handler_lock);
+ list_del(&ir_raw_handler->list);
++ if (ir_raw_handler->raw_unregister)
++ list_for_each_entry(raw, &ir_raw_client_list, list)
++ ir_raw_handler->raw_unregister(raw->input_dev);
++ available_protocols &= ~ir_raw_handler->protocols;
+ spin_unlock(&ir_raw_handler_lock);
+ }
+ EXPORT_SYMBOL(ir_raw_handler_unregister);
+@@ -235,6 +253,7 @@ static void init_decoders(struct work_struct *work)
+ load_rc6_decode();
+ load_jvc_decode();
+ load_sony_decode();
++ load_lirc_codec();
+
+ /* If needed, we may later add some init code. In this case,
+ it is needed to change the CONFIG_MODULE test at ir-core.h
+diff --git a/drivers/media/IR/ir-rc5-decoder.c b/drivers/media/IR/ir-rc5-decoder.c
+index 23cdb1b..df4770d 100644
+--- a/drivers/media/IR/ir-rc5-decoder.c
++++ b/drivers/media/IR/ir-rc5-decoder.c
+@@ -30,10 +30,6 @@
+ #define RC5_BIT_END (1 * RC5_UNIT)
+ #define RC5X_SPACE (4 * RC5_UNIT)
+
+-/* Used to register rc5_decoder clients */
+-static LIST_HEAD(decoder_list);
+-static DEFINE_SPINLOCK(decoder_lock);
+-
+ enum rc5_state {
+ STATE_INACTIVE,
+ STATE_BIT_START,
+@@ -42,87 +38,6 @@ enum rc5_state {
+ STATE_FINISHED,
+ };
+
+-struct decoder_data {
+- struct list_head list;
+- struct ir_input_dev *ir_dev;
+- int enabled:1;
+-
+- /* State machine control */
+- enum rc5_state state;
+- u32 rc5_bits;
+- struct ir_raw_event prev_ev;
+- unsigned count;
+- unsigned wanted_bits;
+-};
+-
+-
+-/**
+- * get_decoder_data() - gets decoder data
+- * @input_dev: input device
+- *
+- * Returns the struct decoder_data that corresponds to a device
+- */
+-
+-static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev)
+-{
+- struct decoder_data *data = NULL;
+-
+- spin_lock(&decoder_lock);
+- list_for_each_entry(data, &decoder_list, list) {
+- if (data->ir_dev == ir_dev)
+- break;
+- }
+- spin_unlock(&decoder_lock);
+- return data;
+-}
+-
+-static ssize_t store_enabled(struct device *d,
+- struct device_attribute *mattr,
+- const char *buf,
+- size_t len)
+-{
+- unsigned long value;
+- struct ir_input_dev *ir_dev = dev_get_drvdata(d);
+- struct decoder_data *data = get_decoder_data(ir_dev);
+-
+- if (!data)
+- return -EINVAL;
+-
+- if (strict_strtoul(buf, 10, &value) || value > 1)
+- return -EINVAL;
+-
+- data->enabled = value;
+-
+- return len;
+-}
+-
+-static ssize_t show_enabled(struct device *d,
+- struct device_attribute *mattr, char *buf)
+-{
+- struct ir_input_dev *ir_dev = dev_get_drvdata(d);
+- struct decoder_data *data = get_decoder_data(ir_dev);
+-
+- if (!data)
+- return -EINVAL;
+-
+- if (data->enabled)
+- return sprintf(buf, "1\n");
+- else
+- return sprintf(buf, "0\n");
+-}
+-
+-static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled);
+-
+-static struct attribute *decoder_attributes[] = {
+- &dev_attr_enabled.attr,
+- NULL
+-};
+-
+-static struct attribute_group decoder_attribute_group = {
+- .name = "rc5_decoder",
+- .attrs = decoder_attributes,
+-};
+-
+ /**
+ * ir_rc5_decode() - Decode one RC-5 pulse or space
+ * @input_dev: the struct input_dev descriptor of the device
+@@ -132,17 +47,13 @@ static struct attribute_group decoder_attribute_group = {
+ */
+ static int ir_rc5_decode(struct input_dev *input_dev, struct ir_raw_event ev)
+ {
+- struct decoder_data *data;
+ struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
++ struct rc5_dec *data = &ir_dev->raw->rc5;
+ u8 toggle;
+ u32 scancode;
+
+- data = get_decoder_data(ir_dev);
+- if (!data)
+- return -EINVAL;
+-
+- if (!data->enabled)
+- return 0;
++ if (!(ir_dev->raw->enabled_protocols & IR_TYPE_RC5))
++ return 0;
+
+ if (IS_RESET(ev)) {
+ data->state = STATE_INACTIVE;
+@@ -176,16 +87,15 @@ again:
+ if (!eq_margin(ev.duration, RC5_BIT_START, RC5_UNIT / 2))
+ break;
+
+- data->rc5_bits <<= 1;
++ data->bits <<= 1;
+ if (!ev.pulse)
+- data->rc5_bits |= 1;
++ data->bits |= 1;
+ data->count++;
+- data->prev_ev = ev;
+ data->state = STATE_BIT_END;
+ return 0;
+
+ case STATE_BIT_END:
+- if (!is_transition(&ev, &data->prev_ev))
++ if (!is_transition(&ev, &ir_dev->raw->prev_ev))
+ break;
+
+ if (data->count == data->wanted_bits)
+@@ -217,11 +127,11 @@ again:
+ if (data->wanted_bits == RC5X_NBITS) {
+ /* RC5X */
+ u8 xdata, command, system;
+- xdata = (data->rc5_bits & 0x0003F) >> 0;
+- command = (data->rc5_bits & 0x00FC0) >> 6;
+- system = (data->rc5_bits & 0x1F000) >> 12;
+- toggle = (data->rc5_bits & 0x20000) ? 1 : 0;
+- command += (data->rc5_bits & 0x01000) ? 0 : 0x40;
++ xdata = (data->bits & 0x0003F) >> 0;
++ command = (data->bits & 0x00FC0) >> 6;
++ system = (data->bits & 0x1F000) >> 12;
++ toggle = (data->bits & 0x20000) ? 1 : 0;
++ command += (data->bits & 0x01000) ? 0 : 0x40;
+ scancode = system << 16 | command << 8 | xdata;
+
+ IR_dprintk(1, "RC5X scancode 0x%06x (toggle: %u)\n",
+@@ -230,10 +140,10 @@ again:
+ } else {
+ /* RC5 */
+ u8 command, system;
+- command = (data->rc5_bits & 0x0003F) >> 0;
+- system = (data->rc5_bits & 0x007C0) >> 6;
+- toggle = (data->rc5_bits & 0x00800) ? 1 : 0;
+- command += (data->rc5_bits & 0x01000) ? 0 : 0x40;
++ command = (data->bits & 0x0003F) >> 0;
++ system = (data->bits & 0x007C0) >> 6;
++ toggle = (data->bits & 0x00800) ? 1 : 0;
++ command += (data->bits & 0x01000) ? 0 : 0x40;
+ scancode = system << 8 | command;
+
+ IR_dprintk(1, "RC5 scancode 0x%04x (toggle: %u)\n",
+@@ -252,54 +162,9 @@ out:
+ return -EINVAL;
+ }
+
+-static int ir_rc5_register(struct input_dev *input_dev)
+-{
+- struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+- struct decoder_data *data;
+- int rc;
+-
+- rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group);
+- if (rc < 0)
+- return rc;
+-
+- data = kzalloc(sizeof(*data), GFP_KERNEL);
+- if (!data) {
+- sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group);
+- return -ENOMEM;
+- }
+-
+- data->ir_dev = ir_dev;
+- data->enabled = 1;
+-
+- spin_lock(&decoder_lock);
+- list_add_tail(&data->list, &decoder_list);
+- spin_unlock(&decoder_lock);
+-
+- return 0;
+-}
+-
+-static int ir_rc5_unregister(struct input_dev *input_dev)
+-{
+- struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+- static struct decoder_data *data;
+-
+- data = get_decoder_data(ir_dev);
+- if (!data)
+- return 0;
+-
+- sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group);
+-
+- spin_lock(&decoder_lock);
+- list_del(&data->list);
+- spin_unlock(&decoder_lock);
+-
+- return 0;
+-}
+-
+ static struct ir_raw_handler rc5_handler = {
++ .protocols = IR_TYPE_RC5,
+ .decode = ir_rc5_decode,
+- .raw_register = ir_rc5_register,
+- .raw_unregister = ir_rc5_unregister,
+ };
+
+ static int __init ir_rc5_decode_init(void)
+diff --git a/drivers/media/IR/ir-rc6-decoder.c b/drivers/media/IR/ir-rc6-decoder.c
+index 2bf479f..f1624b8 100644
+--- a/drivers/media/IR/ir-rc6-decoder.c
++++ b/drivers/media/IR/ir-rc6-decoder.c
+@@ -36,10 +36,6 @@
+ #define RC6_STARTBIT_MASK 0x08 /* for the header bits */
+ #define RC6_6A_MCE_TOGGLE_MASK 0x8000 /* for the body bits */
+
+-/* Used to register rc6_decoder clients */
+-static LIST_HEAD(decoder_list);
+-static DEFINE_SPINLOCK(decoder_lock);
+-
+ enum rc6_mode {
+ RC6_MODE_0,
+ RC6_MODE_6A,
+@@ -58,89 +54,8 @@ enum rc6_state {
+ STATE_FINISHED,
+ };
+
+-struct decoder_data {
+- struct list_head list;
+- struct ir_input_dev *ir_dev;
+- int enabled:1;
+-
+- /* State machine control */
+- enum rc6_state state;
+- u8 header;
+- u32 body;
+- struct ir_raw_event prev_ev;
+- bool toggle;
+- unsigned count;
+- unsigned wanted_bits;
+-};
+-
+-
+-/**
+- * get_decoder_data() - gets decoder data
+- * @input_dev: input device
+- *
+- * Returns the struct decoder_data that corresponds to a device
+- */
+-static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev)
+-{
+- struct decoder_data *data = NULL;
+-
+- spin_lock(&decoder_lock);
+- list_for_each_entry(data, &decoder_list, list) {
+- if (data->ir_dev == ir_dev)
+- break;
+- }
+- spin_unlock(&decoder_lock);
+- return data;
+-}
+-
+-static ssize_t store_enabled(struct device *d,
+- struct device_attribute *mattr,
+- const char *buf,
+- size_t len)
++static enum rc6_mode rc6_mode(struct rc6_dec *data)
+ {
+- unsigned long value;
+- struct ir_input_dev *ir_dev = dev_get_drvdata(d);
+- struct decoder_data *data = get_decoder_data(ir_dev);
+-
+- if (!data)
+- return -EINVAL;
+-
+- if (strict_strtoul(buf, 10, &value) || value > 1)
+- return -EINVAL;
+-
+- data->enabled = value;
+-
+- return len;
+-}
+-
+-static ssize_t show_enabled(struct device *d,
+- struct device_attribute *mattr, char *buf)
+-{
+- struct ir_input_dev *ir_dev = dev_get_drvdata(d);
+- struct decoder_data *data = get_decoder_data(ir_dev);
+-
+- if (!data)
+- return -EINVAL;
+-
+- if (data->enabled)
+- return sprintf(buf, "1\n");
+- else
+- return sprintf(buf, "0\n");
+-}
+-
+-static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled);
+-
+-static struct attribute *decoder_attributes[] = {
+- &dev_attr_enabled.attr,
+- NULL
+-};
+-
+-static struct attribute_group decoder_attribute_group = {
+- .name = "rc6_decoder",
+- .attrs = decoder_attributes,
+-};
+-
+-static enum rc6_mode rc6_mode(struct decoder_data *data) {
+ switch (data->header & RC6_MODE_MASK) {
+ case 0:
+ return RC6_MODE_0;
+@@ -162,16 +77,12 @@ static enum rc6_mode rc6_mode(struct decoder_data *data) {
+ */
+ static int ir_rc6_decode(struct input_dev *input_dev, struct ir_raw_event ev)
+ {
+- struct decoder_data *data;
+ struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
++ struct rc6_dec *data = &ir_dev->raw->rc6;
+ u32 scancode;
+ u8 toggle;
+
+- data = get_decoder_data(ir_dev);
+- if (!data)
+- return -EINVAL;
+-
+- if (!data->enabled)
++ if (!(ir_dev->raw->enabled_protocols & IR_TYPE_RC6))
+ return 0;
+
+ if (IS_RESET(ev)) {
+@@ -223,12 +134,11 @@ again:
+ if (ev.pulse)
+ data->header |= 1;
+ data->count++;
+- data->prev_ev = ev;
+ data->state = STATE_HEADER_BIT_END;
+ return 0;
+
+ case STATE_HEADER_BIT_END:
+- if (!is_transition(&ev, &data->prev_ev))
++ if (!is_transition(&ev, &ir_dev->raw->prev_ev))
+ break;
+
+ if (data->count == RC6_HEADER_NBITS)
+@@ -244,12 +154,11 @@ again:
+ break;
+
+ data->toggle = ev.pulse;
+- data->prev_ev = ev;
+ data->state = STATE_TOGGLE_END;
+ return 0;
+
+ case STATE_TOGGLE_END:
+- if (!is_transition(&ev, &data->prev_ev) ||
++ if (!is_transition(&ev, &ir_dev->raw->prev_ev) ||
+ !geq_margin(ev.duration, RC6_TOGGLE_END, RC6_UNIT / 2))
+ break;
+
+@@ -259,7 +168,6 @@ again:
+ }
+
+ data->state = STATE_BODY_BIT_START;
+- data->prev_ev = ev;
+ decrease_duration(&ev, RC6_TOGGLE_END);
+ data->count = 0;
+
+@@ -291,13 +199,11 @@ again:
+ if (ev.pulse)
+ data->body |= 1;
+ data->count++;
+- data->prev_ev = ev;
+-
+ data->state = STATE_BODY_BIT_END;
+ return 0;
+
+ case STATE_BODY_BIT_END:
+- if (!is_transition(&ev, &data->prev_ev))
++ if (!is_transition(&ev, &ir_dev->raw->prev_ev))
+ break;
+
+ if (data->count == data->wanted_bits)
+@@ -348,54 +254,9 @@ out:
+ return -EINVAL;
+ }
+
+-static int ir_rc6_register(struct input_dev *input_dev)
+-{
+- struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+- struct decoder_data *data;
+- int rc;
+-
+- rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group);
+- if (rc < 0)
+- return rc;
+-
+- data = kzalloc(sizeof(*data), GFP_KERNEL);
+- if (!data) {
+- sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group);
+- return -ENOMEM;
+- }
+-
+- data->ir_dev = ir_dev;
+- data->enabled = 1;
+-
+- spin_lock(&decoder_lock);
+- list_add_tail(&data->list, &decoder_list);
+- spin_unlock(&decoder_lock);
+-
+- return 0;
+-}
+-
+-static int ir_rc6_unregister(struct input_dev *input_dev)
+-{
+- struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+- static struct decoder_data *data;
+-
+- data = get_decoder_data(ir_dev);
+- if (!data)
+- return 0;
+-
+- sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group);
+-
+- spin_lock(&decoder_lock);
+- list_del(&data->list);
+- spin_unlock(&decoder_lock);
+-
+- return 0;
+-}
+-
+ static struct ir_raw_handler rc6_handler = {
++ .protocols = IR_TYPE_RC6,
+ .decode = ir_rc6_decode,
+- .raw_register = ir_rc6_register,
+- .raw_unregister = ir_rc6_unregister,
+ };
+
+ static int __init ir_rc6_decode_init(void)
+diff --git a/drivers/media/IR/ir-sony-decoder.c b/drivers/media/IR/ir-sony-decoder.c
+index 9f440c5..b9074f0 100644
+--- a/drivers/media/IR/ir-sony-decoder.c
++++ b/drivers/media/IR/ir-sony-decoder.c
+@@ -23,10 +23,6 @@
+ #define SONY_BIT_SPACE (1 * SONY_UNIT)
+ #define SONY_TRAILER_SPACE (10 * SONY_UNIT) /* minimum */
+
+-/* Used to register sony_decoder clients */
+-static LIST_HEAD(decoder_list);
+-static DEFINE_SPINLOCK(decoder_lock);
+-
+ enum sony_state {
+ STATE_INACTIVE,
+ STATE_HEADER_SPACE,
+@@ -35,84 +31,6 @@ enum sony_state {
+ STATE_FINISHED,
+ };
+
+-struct decoder_data {
+- struct list_head list;
+- struct ir_input_dev *ir_dev;
+- int enabled:1;
+-
+- /* State machine control */
+- enum sony_state state;
+- u32 sony_bits;
+- unsigned count;
+-};
+-
+-
+-/**
+- * get_decoder_data() - gets decoder data
+- * @input_dev: input device
+- *
+- * Returns the struct decoder_data that corresponds to a device
+- */
+-static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev)
+-{
+- struct decoder_data *data = NULL;
+-
+- spin_lock(&decoder_lock);
+- list_for_each_entry(data, &decoder_list, list) {
+- if (data->ir_dev == ir_dev)
+- break;
+- }
+- spin_unlock(&decoder_lock);
+- return data;
+-}
+-
+-static ssize_t store_enabled(struct device *d,
+- struct device_attribute *mattr,
+- const char *buf,
+- size_t len)
+-{
+- unsigned long value;
+- struct ir_input_dev *ir_dev = dev_get_drvdata(d);
+- struct decoder_data *data = get_decoder_data(ir_dev);
+-
+- if (!data)
+- return -EINVAL;
+-
+- if (strict_strtoul(buf, 10, &value) || value > 1)
+- return -EINVAL;
+-
+- data->enabled = value;
+-
+- return len;
+-}
+-
+-static ssize_t show_enabled(struct device *d,
+- struct device_attribute *mattr, char *buf)
+-{
+- struct ir_input_dev *ir_dev = dev_get_drvdata(d);
+- struct decoder_data *data = get_decoder_data(ir_dev);
+-
+- if (!data)
+- return -EINVAL;
+-
+- if (data->enabled)
+- return sprintf(buf, "1\n");
+- else
+- return sprintf(buf, "0\n");
+-}
+-
+-static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled);
+-
+-static struct attribute *decoder_attributes[] = {
+- &dev_attr_enabled.attr,
+- NULL
+-};
+-
+-static struct attribute_group decoder_attribute_group = {
+- .name = "sony_decoder",
+- .attrs = decoder_attributes,
+-};
+-
+ /**
+ * ir_sony_decode() - Decode one Sony pulse or space
+ * @input_dev: the struct input_dev descriptor of the device
+@@ -122,16 +40,12 @@ static struct attribute_group decoder_attribute_group = {
+ */
+ static int ir_sony_decode(struct input_dev *input_dev, struct ir_raw_event ev)
+ {
+- struct decoder_data *data;
+ struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
++ struct sony_dec *data = &ir_dev->raw->sony;
+ u32 scancode;
+ u8 device, subdevice, function;
+
+- data = get_decoder_data(ir_dev);
+- if (!data)
+- return -EINVAL;
+-
+- if (!data->enabled)
++ if (!(ir_dev->raw->enabled_protocols & IR_TYPE_SONY))
+ return 0;
+
+ if (IS_RESET(ev)) {
+@@ -172,9 +86,9 @@ static int ir_sony_decode(struct input_dev *input_dev, struct ir_raw_event ev)
+ if (!ev.pulse)
+ break;
+
+- data->sony_bits <<= 1;
++ data->bits <<= 1;
+ if (eq_margin(ev.duration, SONY_BIT_1_PULSE, SONY_UNIT / 2))
+- data->sony_bits |= 1;
++ data->bits |= 1;
+ else if (!eq_margin(ev.duration, SONY_BIT_0_PULSE, SONY_UNIT / 2))
+ break;
+
+@@ -208,19 +122,19 @@ static int ir_sony_decode(struct input_dev *input_dev, struct ir_raw_event ev)
+
+ switch (data->count) {
+ case 12:
+- device = bitrev8((data->sony_bits << 3) & 0xF8);
++ device = bitrev8((data->bits << 3) & 0xF8);
+ subdevice = 0;
+- function = bitrev8((data->sony_bits >> 4) & 0xFE);
++ function = bitrev8((data->bits >> 4) & 0xFE);
+ break;
+ case 15:
+- device = bitrev8((data->sony_bits >> 0) & 0xFF);
++ device = bitrev8((data->bits >> 0) & 0xFF);
+ subdevice = 0;
+- function = bitrev8((data->sony_bits >> 7) & 0xFD);
++ function = bitrev8((data->bits >> 7) & 0xFD);
+ break;
+ case 20:
+- device = bitrev8((data->sony_bits >> 5) & 0xF8);
+- subdevice = bitrev8((data->sony_bits >> 0) & 0xFF);
+- function = bitrev8((data->sony_bits >> 12) & 0xFE);
++ device = bitrev8((data->bits >> 5) & 0xF8);
++ subdevice = bitrev8((data->bits >> 0) & 0xFF);
++ function = bitrev8((data->bits >> 12) & 0xFE);
+ break;
+ default:
+ IR_dprintk(1, "Sony invalid bitcount %u\n", data->count);
+@@ -241,54 +155,9 @@ out:
+ return -EINVAL;
+ }
+
+-static int ir_sony_register(struct input_dev *input_dev)
+-{
+- struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+- struct decoder_data *data;
+- int rc;
+-
+- rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group);
+- if (rc < 0)
+- return rc;
+-
+- data = kzalloc(sizeof(*data), GFP_KERNEL);
+- if (!data) {
+- sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group);
+- return -ENOMEM;
+- }
+-
+- data->ir_dev = ir_dev;
+- data->enabled = 1;
+-
+- spin_lock(&decoder_lock);
+- list_add_tail(&data->list, &decoder_list);
+- spin_unlock(&decoder_lock);
+-
+- return 0;
+-}
+-
+-static int ir_sony_unregister(struct input_dev *input_dev)
+-{
+- struct ir_input_dev *ir_dev = input_get_drvdata(input_dev);
+- static struct decoder_data *data;
+-
+- data = get_decoder_data(ir_dev);
+- if (!data)
+- return 0;
+-
+- sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group);
+-
+- spin_lock(&decoder_lock);
+- list_del(&data->list);
+- spin_unlock(&decoder_lock);
+-
+- return 0;
+-}
+-
+ static struct ir_raw_handler sony_handler = {
++ .protocols = IR_TYPE_SONY,
+ .decode = ir_sony_decode,
+- .raw_register = ir_sony_register,
+- .raw_unregister = ir_sony_unregister,
+ };
+
+ static int __init ir_sony_decode_init(void)
+diff --git a/drivers/media/IR/ir-sysfs.c b/drivers/media/IR/ir-sysfs.c
+index 2098dd1..a841e51 100644
+--- a/drivers/media/IR/ir-sysfs.c
++++ b/drivers/media/IR/ir-sysfs.c
+@@ -34,122 +34,186 @@ static struct class ir_input_class = {
+ };
+
+ /**
+- * show_protocol() - shows the current IR protocol
++ * show_protocols() - shows the current IR protocol(s)
+ * @d: the device descriptor
+ * @mattr: the device attribute struct (unused)
+ * @buf: a pointer to the output buffer
+ *
+- * This routine is a callback routine for input read the IR protocol type.
+- * it is trigged by reading /sys/class/rc/rc?/current_protocol.
+- * It returns the protocol name, as understood by the driver.
++ * This routine is a callback routine for input read the IR protocol type(s).
++ * it is trigged by reading /sys/class/rc/rc?/protocols.
++ * It returns the protocol names of supported protocols.
++ * Enabled protocols are printed in brackets.
+ */
+-static ssize_t show_protocol(struct device *d,
+- struct device_attribute *mattr, char *buf)
++static ssize_t show_protocols(struct device *d,
++ struct device_attribute *mattr, char *buf)
+ {
+- char *s;
+ struct ir_input_dev *ir_dev = dev_get_drvdata(d);
+- u64 ir_type = ir_dev->rc_tab.ir_type;
+-
+- IR_dprintk(1, "Current protocol is %lld\n", (long long)ir_type);
+-
+- /* FIXME: doesn't support multiple protocols at the same time */
+- if (ir_type == IR_TYPE_UNKNOWN)
+- s = "Unknown";
+- else if (ir_type == IR_TYPE_RC5)
+- s = "rc-5";
+- else if (ir_type == IR_TYPE_NEC)
+- s = "nec";
+- else if (ir_type == IR_TYPE_RC6)
+- s = "rc6";
+- else if (ir_type == IR_TYPE_JVC)
+- s = "jvc";
+- else if (ir_type == IR_TYPE_SONY)
+- s = "sony";
+- else
+- s = "other";
++ u64 allowed, enabled;
++ char *tmp = buf;
++
++ if (ir_dev->props->driver_type == RC_DRIVER_SCANCODE) {
++ enabled = ir_dev->rc_tab.ir_type;
++ allowed = ir_dev->props->allowed_protos;
++ } else {
++ enabled = ir_dev->raw->enabled_protocols;
++ allowed = ir_raw_get_allowed_protocols();
++ }
+
+- return sprintf(buf, "%s\n", s);
++ IR_dprintk(1, "allowed - 0x%llx, enabled - 0x%llx\n",
++ (long long)allowed,
++ (long long)enabled);
++
++ if (allowed & enabled & IR_TYPE_UNKNOWN)
++ tmp += sprintf(tmp, "[unknown] ");
++ else if (allowed & IR_TYPE_UNKNOWN)
++ tmp += sprintf(tmp, "unknown ");
++
++ if (allowed & enabled & IR_TYPE_RC5)
++ tmp += sprintf(tmp, "[rc5] ");
++ else if (allowed & IR_TYPE_RC5)
++ tmp += sprintf(tmp, "rc5 ");
++
++ if (allowed & enabled & IR_TYPE_NEC)
++ tmp += sprintf(tmp, "[nec] ");
++ else if (allowed & IR_TYPE_NEC)
++ tmp += sprintf(tmp, "nec ");
++
++ if (allowed & enabled & IR_TYPE_RC6)
++ tmp += sprintf(tmp, "[rc6] ");
++ else if (allowed & IR_TYPE_RC6)
++ tmp += sprintf(tmp, "rc6 ");
++
++ if (allowed & enabled & IR_TYPE_JVC)
++ tmp += sprintf(tmp, "[jvc] ");
++ else if (allowed & IR_TYPE_JVC)
++ tmp += sprintf(tmp, "jvc ");
++
++ if (allowed & enabled & IR_TYPE_SONY)
++ tmp += sprintf(tmp, "[sony] ");
++ else if (allowed & IR_TYPE_SONY)
++ tmp += sprintf(tmp, "sony ");
++
++ if (allowed & enabled & IR_TYPE_LIRC)
++ tmp += sprintf(tmp, "[lirc] ");
++ else if (allowed & IR_TYPE_LIRC)
++ tmp += sprintf(tmp, "lirc ");
++
++ if (tmp != buf)
++ tmp--;
++ *tmp = '\n';
++ return tmp + 1 - buf;
+ }
+
+ /**
+- * store_protocol() - shows the current IR protocol
++ * store_protocols() - changes the current IR protocol(s)
+ * @d: the device descriptor
+ * @mattr: the device attribute struct (unused)
+ * @buf: a pointer to the input buffer
+ * @len: length of the input buffer
+ *
+ * This routine is a callback routine for changing the IR protocol type.
+- * it is trigged by reading /sys/class/rc/rc?/current_protocol.
+- * It changes the IR the protocol name, if the IR type is recognized
+- * by the driver.
+- * If an unknown protocol name is used, returns -EINVAL.
++ * It is trigged by writing to /sys/class/rc/rc?/protocols.
++ * Writing "+proto" will add a protocol to the list of enabled protocols.
++ * Writing "-proto" will remove a protocol from the list of enabled protocols.
++ * Writing "proto" will enable only "proto".
++ * Returns -EINVAL if an invalid protocol combination or unknown protocol name
++ * is used, otherwise @len.
+ */
+-static ssize_t store_protocol(struct device *d,
+- struct device_attribute *mattr,
+- const char *data,
+- size_t len)
++static ssize_t store_protocols(struct device *d,
++ struct device_attribute *mattr,
++ const char *data,
++ size_t len)
+ {
+ struct ir_input_dev *ir_dev = dev_get_drvdata(d);
+- u64 ir_type = 0;
+- int rc = -EINVAL;
++ bool enable, disable;
++ const char *tmp;
++ u64 type;
++ u64 mask;
++ int rc;
+ unsigned long flags;
+- char *buf;
+-
+- while ((buf = strsep((char **) &data, " \n")) != NULL) {
+- if (!strcasecmp(buf, "rc-5") || !strcasecmp(buf, "rc5"))
+- ir_type |= IR_TYPE_RC5;
+- if (!strcasecmp(buf, "nec"))
+- ir_type |= IR_TYPE_NEC;
+- if (!strcasecmp(buf, "jvc"))
+- ir_type |= IR_TYPE_JVC;
+- if (!strcasecmp(buf, "sony"))
+- ir_type |= IR_TYPE_SONY;
++
++ tmp = skip_spaces(data);
++
++ if (*tmp == '+') {
++ enable = true;
++ disable = false;
++ tmp++;
++ } else if (*tmp == '-') {
++ enable = false;
++ disable = true;
++ tmp++;
++ } else {
++ enable = false;
++ disable = false;
+ }
+
+- if (!ir_type) {
++ if (!strncasecmp(tmp, "unknown", 7)) {
++ tmp += 7;
++ mask = IR_TYPE_UNKNOWN;
++ } else if (!strncasecmp(tmp, "rc5", 3)) {
++ tmp += 3;
++ mask = IR_TYPE_RC5;
++ } else if (!strncasecmp(tmp, "nec", 3)) {
++ tmp += 3;
++ mask = IR_TYPE_NEC;
++ } else if (!strncasecmp(tmp, "rc6", 3)) {
++ tmp += 3;
++ mask = IR_TYPE_RC6;
++ } else if (!strncasecmp(tmp, "jvc", 3)) {
++ tmp += 3;
++ mask = IR_TYPE_JVC;
++ } else if (!strncasecmp(tmp, "sony", 4)) {
++ tmp += 4;
++ mask = IR_TYPE_SONY;
++ } else if (!strncasecmp(tmp, "lirc", 4)) {
++ tmp += 4;
++ mask = IR_TYPE_LIRC;
++ } else {
+ IR_dprintk(1, "Unknown protocol\n");
+ return -EINVAL;
+ }
+
+- if (ir_dev->props && ir_dev->props->change_protocol)
+- rc = ir_dev->props->change_protocol(ir_dev->props->priv,
+- ir_type);
+-
+- if (rc < 0) {
+- IR_dprintk(1, "Error setting protocol to %lld\n",
+- (long long)ir_type);
++ tmp = skip_spaces(tmp);
++ if (*tmp != '\0') {
++ IR_dprintk(1, "Invalid trailing characters\n");
+ return -EINVAL;
+ }
+
+- spin_lock_irqsave(&ir_dev->rc_tab.lock, flags);
+- ir_dev->rc_tab.ir_type = ir_type;
+- spin_unlock_irqrestore(&ir_dev->rc_tab.lock, flags);
++ if (ir_dev->props->driver_type == RC_DRIVER_SCANCODE)
++ type = ir_dev->rc_tab.ir_type;
++ else
++ type = ir_dev->raw->enabled_protocols;
+
+- IR_dprintk(1, "Current protocol(s) is(are) %lld\n",
+- (long long)ir_type);
++ if (enable)
++ type |= mask;
++ else if (disable)
++ type &= ~mask;
++ else
++ type = mask;
+
+- return len;
+-}
++ if (ir_dev->props && ir_dev->props->change_protocol) {
++ rc = ir_dev->props->change_protocol(ir_dev->props->priv,
++ type);
++ if (rc < 0) {
++ IR_dprintk(1, "Error setting protocols to 0x%llx\n",
++ (long long)type);
++ return -EINVAL;
++ }
++ }
+
+-static ssize_t show_supported_protocols(struct device *d,
+- struct device_attribute *mattr, char *buf)
+-{
+- char *orgbuf = buf;
+- struct ir_input_dev *ir_dev = dev_get_drvdata(d);
++ if (ir_dev->props->driver_type == RC_DRIVER_SCANCODE) {
++ spin_lock_irqsave(&ir_dev->rc_tab.lock, flags);
++ ir_dev->rc_tab.ir_type = type;
++ spin_unlock_irqrestore(&ir_dev->rc_tab.lock, flags);
++ } else {
++ ir_dev->raw->enabled_protocols = type;
++ }
+
+- /* FIXME: doesn't support multiple protocols at the same time */
+- if (ir_dev->props->allowed_protos == IR_TYPE_UNKNOWN)
+- buf += sprintf(buf, "unknown ");
+- if (ir_dev->props->allowed_protos & IR_TYPE_RC5)
+- buf += sprintf(buf, "rc-5 ");
+- if (ir_dev->props->allowed_protos & IR_TYPE_NEC)
+- buf += sprintf(buf, "nec ");
+- if (buf == orgbuf)
+- buf += sprintf(buf, "other ");
+
+- buf += sprintf(buf - 1, "\n");
++ IR_dprintk(1, "Current protocol(s): 0x%llx\n",
++ (long long)type);
+
+- return buf - orgbuf;
++ return len;
+ }
+
+ #define ADD_HOTPLUG_VAR(fmt, val...) \
+@@ -159,7 +223,7 @@ static ssize_t show_supported_protocols(struct device *d,
+ return err; \
+ } while (0)
+
+-static int ir_dev_uevent(struct device *device, struct kobj_uevent_env *env)
++static int rc_dev_uevent(struct device *device, struct kobj_uevent_env *env)
+ {
+ struct ir_input_dev *ir_dev = dev_get_drvdata(device);
+
+@@ -174,34 +238,26 @@ static int ir_dev_uevent(struct device *device, struct kobj_uevent_env *env)
+ /*
+ * Static device attribute struct with the sysfs attributes for IR's
+ */
+-static DEVICE_ATTR(protocol, S_IRUGO | S_IWUSR,
+- show_protocol, store_protocol);
+-
+-static DEVICE_ATTR(supported_protocols, S_IRUGO | S_IWUSR,
+- show_supported_protocols, NULL);
++static DEVICE_ATTR(protocols, S_IRUGO | S_IWUSR,
++ show_protocols, store_protocols);
+
+-static struct attribute *ir_hw_dev_attrs[] = {
+- &dev_attr_protocol.attr,
+- &dev_attr_supported_protocols.attr,
++static struct attribute *rc_dev_attrs[] = {
++ &dev_attr_protocols.attr,
+ NULL,
+ };
+
+-static struct attribute_group ir_hw_dev_attr_grp = {
+- .attrs = ir_hw_dev_attrs,
++static struct attribute_group rc_dev_attr_grp = {
++ .attrs = rc_dev_attrs,
+ };
+
+-static const struct attribute_group *ir_hw_dev_attr_groups[] = {
+- &ir_hw_dev_attr_grp,
++static const struct attribute_group *rc_dev_attr_groups[] = {
++ &rc_dev_attr_grp,
+ NULL
+ };
+
+ static struct device_type rc_dev_type = {
+- .groups = ir_hw_dev_attr_groups,
+- .uevent = ir_dev_uevent,
+-};
+-
+-static struct device_type ir_raw_dev_type = {
+- .uevent = ir_dev_uevent,
++ .groups = rc_dev_attr_groups,
++ .uevent = rc_dev_uevent,
+ };
+
+ /**
+@@ -221,12 +277,7 @@ int ir_register_class(struct input_dev *input_dev)
+ if (unlikely(devno < 0))
+ return devno;
+
+- if (ir_dev->props) {
+- if (ir_dev->props->driver_type == RC_DRIVER_SCANCODE)
+- ir_dev->dev.type = &rc_dev_type;
+- } else
+- ir_dev->dev.type = &ir_raw_dev_type;
+-
++ ir_dev->dev.type = &rc_dev_type;
+ ir_dev->dev.class = &ir_input_class;
+ ir_dev->dev.parent = input_dev->dev.parent;
+ dev_set_name(&ir_dev->dev, "rc%d", devno);
+diff --git a/drivers/media/IR/keymaps/Makefile b/drivers/media/IR/keymaps/Makefile
+index aea649f..86d3d1f 100644
+--- a/drivers/media/IR/keymaps/Makefile
++++ b/drivers/media/IR/keymaps/Makefile
+@@ -37,6 +37,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
+ rc-kaiomy.o \
+ rc-kworld-315u.o \
+ rc-kworld-plus-tv-analog.o \
++ rc-lirc.o \
+ rc-manli.o \
+ rc-msi-tvanywhere.o \
+ rc-msi-tvanywhere-plus.o \
+@@ -57,6 +58,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
+ rc-pv951.o \
+ rc-rc5-hauppauge-new.o \
+ rc-rc5-tv.o \
++ rc-rc6-mce.o \
+ rc-real-audio-220-32-keys.o \
+ rc-tbs-nec.o \
+ rc-terratec-cinergy-xs.o \
+diff --git a/drivers/media/IR/keymaps/rc-lirc.c b/drivers/media/IR/keymaps/rc-lirc.c
+new file mode 100644
+index 0000000..43fcf90
+--- /dev/null
++++ b/drivers/media/IR/keymaps/rc-lirc.c
+@@ -0,0 +1,41 @@
++/* rc-lirc.c - Empty dummy keytable, for use when its preferred to pass
++ * all raw IR data to the lirc userspace decoder.
++ *
++ * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.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.
++ */
++
++#include <media/ir-core.h>
++
++static struct ir_scancode lirc[] = {
++ { },
++};
++
++static struct rc_keymap lirc_map = {
++ .map = {
++ .scan = lirc,
++ .size = ARRAY_SIZE(lirc),
++ .ir_type = IR_TYPE_LIRC,
++ .name = RC_MAP_LIRC,
++ }
++};
++
++static int __init init_rc_map_lirc(void)
++{
++ return ir_register_map(&lirc_map);
++}
++
++static void __exit exit_rc_map_lirc(void)
++{
++ ir_unregister_map(&lirc_map);
++}
++
++module_init(init_rc_map_lirc)
++module_exit(exit_rc_map_lirc)
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
+diff --git a/drivers/media/IR/keymaps/rc-rc6-mce.c b/drivers/media/IR/keymaps/rc-rc6-mce.c
+new file mode 100644
+index 0000000..c6726a8
+--- /dev/null
++++ b/drivers/media/IR/keymaps/rc-rc6-mce.c
+@@ -0,0 +1,105 @@
++/* rc-rc6-mce.c - Keytable for Windows Media Center RC-6 remotes for use
++ * with the Media Center Edition eHome Infrared Transceiver.
++ *
++ * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.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.
++ */
++
++#include <media/rc-map.h>
++
++static struct ir_scancode rc6_mce[] = {
++ { 0x800f0415, KEY_REWIND },
++ { 0x800f0414, KEY_FASTFORWARD },
++ { 0x800f041b, KEY_PREVIOUS },
++ { 0x800f041a, KEY_NEXT },
++
++ { 0x800f0416, KEY_PLAY },
++ { 0x800f0418, KEY_PAUSE },
++ { 0x800f0419, KEY_STOP },
++ { 0x800f0417, KEY_RECORD },
++
++ { 0x800f041e, KEY_UP },
++ { 0x800f041f, KEY_DOWN },
++ { 0x800f0420, KEY_LEFT },
++ { 0x800f0421, KEY_RIGHT },
++
++ { 0x800f040b, KEY_ENTER },
++ { 0x800f0422, KEY_OK },
++ { 0x800f0423, KEY_EXIT },
++ { 0x800f040a, KEY_DELETE },
++
++ { 0x800f040e, KEY_MUTE },
++ { 0x800f0410, KEY_VOLUMEUP },
++ { 0x800f0411, KEY_VOLUMEDOWN },
++ { 0x800f0412, KEY_CHANNELUP },
++ { 0x800f0413, KEY_CHANNELDOWN },
++
++ { 0x800f0401, KEY_NUMERIC_1 },
++ { 0x800f0402, KEY_NUMERIC_2 },
++ { 0x800f0403, KEY_NUMERIC_3 },
++ { 0x800f0404, KEY_NUMERIC_4 },
++ { 0x800f0405, KEY_NUMERIC_5 },
++ { 0x800f0406, KEY_NUMERIC_6 },
++ { 0x800f0407, KEY_NUMERIC_7 },
++ { 0x800f0408, KEY_NUMERIC_8 },
++ { 0x800f0409, KEY_NUMERIC_9 },
++ { 0x800f0400, KEY_NUMERIC_0 },
++
++ { 0x800f041d, KEY_NUMERIC_STAR },
++ { 0x800f041c, KEY_NUMERIC_POUND },
++
++ { 0x800f0446, KEY_TV },
++ { 0x800f0447, KEY_AUDIO }, /* My Music */
++ { 0x800f0448, KEY_PVR }, /* RecordedTV */
++ { 0x800f0449, KEY_CAMERA },
++ { 0x800f044a, KEY_VIDEO },
++ { 0x800f0424, KEY_DVD },
++ { 0x800f0425, KEY_TUNER }, /* LiveTV */
++ { 0x800f0450, KEY_RADIO },
++
++ { 0x800f044c, KEY_LANGUAGE },
++ { 0x800f0427, KEY_ZOOM }, /* Aspect */
++
++ { 0x800f045b, KEY_RED },
++ { 0x800f045c, KEY_GREEN },
++ { 0x800f045d, KEY_YELLOW },
++ { 0x800f045e, KEY_BLUE },
++
++ { 0x800f040f, KEY_INFO },
++ { 0x800f0426, KEY_EPG }, /* Guide */
++ { 0x800f045a, KEY_SUBTITLE }, /* Caption/Teletext */
++ { 0x800f044d, KEY_TITLE },
++
++ { 0x800f040c, KEY_POWER },
++ { 0x800f040d, KEY_PROG1 }, /* Windows MCE button */
++
++};
++
++static struct rc_keymap rc6_mce_map = {
++ .map = {
++ .scan = rc6_mce,
++ .size = ARRAY_SIZE(rc6_mce),
++ .ir_type = IR_TYPE_RC6,
++ .name = RC_MAP_RC6_MCE,
++ }
++};
++
++static int __init init_rc_map_rc6_mce(void)
++{
++ return ir_register_map(&rc6_mce_map);
++}
++
++static void __exit exit_rc_map_rc6_mce(void)
++{
++ ir_unregister_map(&rc6_mce_map);
++}
++
++module_init(init_rc_map_rc6_mce)
++module_exit(exit_rc_map_rc6_mce)
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
+diff --git a/drivers/media/IR/lirc_dev.c b/drivers/media/IR/lirc_dev.c
+new file mode 100644
+index 0000000..899891b
+--- /dev/null
++++ b/drivers/media/IR/lirc_dev.c
+@@ -0,0 +1,764 @@
++/*
++ * LIRC base driver
++ *
++ * by Artur Lipowski <alipowski@interia.pl>
++ *
++ * 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/module.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/ioctl.h>
++#include <linux/fs.h>
++#include <linux/poll.h>
++#include <linux/completion.h>
++#include <linux/errno.h>
++#include <linux/mutex.h>
++#include <linux/wait.h>
++#include <linux/unistd.h>
++#include <linux/kthread.h>
++#include <linux/bitops.h>
++#include <linux/device.h>
++#include <linux/cdev.h>
++
++#include <media/lirc.h>
++#include <media/lirc_dev.h>
++
++static int debug;
++
++#define IRCTL_DEV_NAME "BaseRemoteCtl"
++#define NOPLUG -1
++#define LOGHEAD "lirc_dev (%s[%d]): "
++
++static dev_t lirc_base_dev;
++
++struct irctl {
++ struct lirc_driver d;
++ int attached;
++ int open;
++
++ struct mutex irctl_lock;
++ struct lirc_buffer *buf;
++ unsigned int chunk_size;
++
++ struct task_struct *task;
++ long jiffies_to_wait;
++
++ struct cdev cdev;
++};
++
++static DEFINE_MUTEX(lirc_dev_lock);
++
++static struct irctl *irctls[MAX_IRCTL_DEVICES];
++
++/* Only used for sysfs but defined to void otherwise */
++static struct class *lirc_class;
++
++/* helper function
++ * initializes the irctl structure
++ */
++static void init_irctl(struct irctl *ir)
++{
++ dev_dbg(ir->d.dev, LOGHEAD "initializing irctl\n",
++ ir->d.name, ir->d.minor);
++ mutex_init(&ir->irctl_lock);
++ ir->d.minor = NOPLUG;
++}
++
++static void cleanup(struct irctl *ir)
++{
++ dev_dbg(ir->d.dev, LOGHEAD "cleaning up\n", ir->d.name, ir->d.minor);
++
++ device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor));
++
++ if (ir->buf != ir->d.rbuf) {
++ lirc_buffer_free(ir->buf);
++ kfree(ir->buf);
++ }
++ ir->buf = NULL;
++}
++
++/* helper function
++ * reads key codes from driver and puts them into buffer
++ * returns 0 on success
++ */
++static int add_to_buf(struct irctl *ir)
++{
++ if (ir->d.add_to_buf) {
++ int res = -ENODATA;
++ int got_data = 0;
++
++ /*
++ * service the device as long as it is returning
++ * data and we have space
++ */
++get_data:
++ res = ir->d.add_to_buf(ir->d.data, ir->buf);
++ if (res == 0) {
++ got_data++;
++ goto get_data;
++ }
++
++ if (res == -ENODEV)
++ kthread_stop(ir->task);
++
++ return got_data ? 0 : res;
++ }
++
++ return 0;
++}
++
++/* main function of the polling thread
++ */
++static int lirc_thread(void *irctl)
++{
++ struct irctl *ir = irctl;
++
++ dev_dbg(ir->d.dev, LOGHEAD "poll thread started\n",
++ ir->d.name, ir->d.minor);
++
++ do {
++ if (ir->open) {
++ if (ir->jiffies_to_wait) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(ir->jiffies_to_wait);
++ }
++ if (kthread_should_stop())
++ break;
++ if (!add_to_buf(ir))
++ wake_up_interruptible(&ir->buf->wait_poll);
++ } else {
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule();
++ }
++ } while (!kthread_should_stop());
++
++ dev_dbg(ir->d.dev, LOGHEAD "poll thread ended\n",
++ ir->d.name, ir->d.minor);
++
++ return 0;
++}
++
++
++static struct file_operations fops = {
++ .owner = THIS_MODULE,
++ .read = lirc_dev_fop_read,
++ .write = lirc_dev_fop_write,
++ .poll = lirc_dev_fop_poll,
++ .unlocked_ioctl = lirc_dev_fop_ioctl,
++ .open = lirc_dev_fop_open,
++ .release = lirc_dev_fop_close,
++};
++
++static int lirc_cdev_add(struct irctl *ir)
++{
++ int retval;
++ struct lirc_driver *d = &ir->d;
++
++ if (d->fops) {
++ cdev_init(&ir->cdev, d->fops);
++ ir->cdev.owner = d->owner;
++ } else {
++ cdev_init(&ir->cdev, &fops);
++ ir->cdev.owner = THIS_MODULE;
++ }
++ kobject_set_name(&ir->cdev.kobj, "lirc%d", d->minor);
++
++ retval = cdev_add(&ir->cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1);
++ if (retval)
++ kobject_put(&ir->cdev.kobj);
++
++ return retval;
++}
++
++int lirc_register_driver(struct lirc_driver *d)
++{
++ struct irctl *ir;
++ int minor;
++ int bytes_in_key;
++ unsigned int chunk_size;
++ unsigned int buffer_size;
++ int err;
++
++ if (!d) {
++ printk(KERN_ERR "lirc_dev: lirc_register_driver: "
++ "driver pointer must be not NULL!\n");
++ err = -EBADRQC;
++ goto out;
++ }
++
++ if (MAX_IRCTL_DEVICES <= d->minor) {
++ dev_err(d->dev, "lirc_dev: lirc_register_driver: "
++ "\"minor\" must be between 0 and %d (%d)!\n",
++ MAX_IRCTL_DEVICES-1, d->minor);
++ err = -EBADRQC;
++ goto out;
++ }
++
++ if (1 > d->code_length || (BUFLEN * 8) < d->code_length) {
++ dev_err(d->dev, "lirc_dev: lirc_register_driver: "
++ "code length in bits for minor (%d) "
++ "must be less than %d!\n",
++ d->minor, BUFLEN * 8);
++ err = -EBADRQC;
++ goto out;
++ }
++
++ dev_dbg(d->dev, "lirc_dev: lirc_register_driver: sample_rate: %d\n",
++ d->sample_rate);
++ if (d->sample_rate) {
++ if (2 > d->sample_rate || HZ < d->sample_rate) {
++ dev_err(d->dev, "lirc_dev: lirc_register_driver: "
++ "sample_rate must be between 2 and %d!\n", HZ);
++ err = -EBADRQC;
++ goto out;
++ }
++ if (!d->add_to_buf) {
++ dev_err(d->dev, "lirc_dev: lirc_register_driver: "
++ "add_to_buf cannot be NULL when "
++ "sample_rate is set\n");
++ err = -EBADRQC;
++ goto out;
++ }
++ } else if (!(d->fops && d->fops->read) && !d->rbuf) {
++ dev_err(d->dev, "lirc_dev: lirc_register_driver: "
++ "fops->read and rbuf cannot all be NULL!\n");
++ err = -EBADRQC;
++ goto out;
++ } else if (!d->rbuf) {
++ if (!(d->fops && d->fops->read && d->fops->poll &&
++ d->fops->unlocked_ioctl)) {
++ dev_err(d->dev, "lirc_dev: lirc_register_driver: "
++ "neither read, poll nor unlocked_ioctl can be NULL!\n");
++ err = -EBADRQC;
++ goto out;
++ }
++ }
++
++ mutex_lock(&lirc_dev_lock);
++
++ minor = d->minor;
++
++ if (minor < 0) {
++ /* find first free slot for driver */
++ for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++)
++ if (!irctls[minor])
++ break;
++ if (MAX_IRCTL_DEVICES == minor) {
++ dev_err(d->dev, "lirc_dev: lirc_register_driver: "
++ "no free slots for drivers!\n");
++ err = -ENOMEM;
++ goto out_lock;
++ }
++ } else if (irctls[minor]) {
++ dev_err(d->dev, "lirc_dev: lirc_register_driver: "
++ "minor (%d) just registered!\n", minor);
++ err = -EBUSY;
++ goto out_lock;
++ }
++
++ ir = kzalloc(sizeof(struct irctl), GFP_KERNEL);
++ if (!ir) {
++ err = -ENOMEM;
++ goto out_lock;
++ }
++ init_irctl(ir);
++ irctls[minor] = ir;
++ d->minor = minor;
++
++ if (d->sample_rate) {
++ ir->jiffies_to_wait = HZ / d->sample_rate;
++ } else {
++ /* it means - wait for external event in task queue */
++ ir->jiffies_to_wait = 0;
++ }
++
++ /* some safety check 8-) */
++ d->name[sizeof(d->name)-1] = '\0';
++
++ bytes_in_key = BITS_TO_LONGS(d->code_length) +
++ (d->code_length % 8 ? 1 : 0);
++ buffer_size = d->buffer_size ? d->buffer_size : BUFLEN / bytes_in_key;
++ chunk_size = d->chunk_size ? d->chunk_size : bytes_in_key;
++
++ if (d->rbuf) {
++ ir->buf = d->rbuf;
++ } else {
++ ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
++ if (!ir->buf) {
++ err = -ENOMEM;
++ goto out_lock;
++ }
++ err = lirc_buffer_init(ir->buf, chunk_size, buffer_size);
++ if (err) {
++ kfree(ir->buf);
++ goto out_lock;
++ }
++ }
++ ir->chunk_size = ir->buf->chunk_size;
++
++ if (d->features == 0)
++ d->features = LIRC_CAN_REC_LIRCCODE;
++
++ ir->d = *d;
++ ir->d.minor = minor;
++
++ device_create(lirc_class, ir->d.dev,
++ MKDEV(MAJOR(lirc_base_dev), ir->d.minor), NULL,
++ "lirc%u", ir->d.minor);
++
++ if (d->sample_rate) {
++ /* try to fire up polling thread */
++ ir->task = kthread_run(lirc_thread, (void *)ir, "lirc_dev");
++ if (IS_ERR(ir->task)) {
++ dev_err(d->dev, "lirc_dev: lirc_register_driver: "
++ "cannot run poll thread for minor = %d\n",
++ d->minor);
++ err = -ECHILD;
++ goto out_sysfs;
++ }
++ }
++
++ err = lirc_cdev_add(ir);
++ if (err)
++ goto out_sysfs;
++
++ ir->attached = 1;
++ mutex_unlock(&lirc_dev_lock);
++
++ dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n",
++ ir->d.name, ir->d.minor);
++ return minor;
++
++out_sysfs:
++ device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor));
++out_lock:
++ mutex_unlock(&lirc_dev_lock);
++out:
++ return err;
++}
++EXPORT_SYMBOL(lirc_register_driver);
++
++int lirc_unregister_driver(int minor)
++{
++ struct irctl *ir;
++
++ if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {
++ printk(KERN_ERR "lirc_dev: lirc_unregister_driver: "
++ "\"minor (%d)\" must be between 0 and %d!\n",
++ minor, MAX_IRCTL_DEVICES-1);
++ return -EBADRQC;
++ }
++
++ ir = irctls[minor];
++
++ mutex_lock(&lirc_dev_lock);
++
++ if (ir->d.minor != minor) {
++ printk(KERN_ERR "lirc_dev: lirc_unregister_driver: "
++ "minor (%d) device not registered!", minor);
++ mutex_unlock(&lirc_dev_lock);
++ return -ENOENT;
++ }
++
++ /* end up polling thread */
++ if (ir->task)
++ kthread_stop(ir->task);
++
++ dev_dbg(ir->d.dev, "lirc_dev: driver %s unregistered from minor = %d\n",
++ ir->d.name, ir->d.minor);
++
++ ir->attached = 0;
++ if (ir->open) {
++ dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n",
++ ir->d.name, ir->d.minor);
++ wake_up_interruptible(&ir->buf->wait_poll);
++ mutex_lock(&ir->irctl_lock);
++ ir->d.set_use_dec(ir->d.data);
++ module_put(ir->d.owner);
++ mutex_unlock(&ir->irctl_lock);
++ cdev_del(&ir->cdev);
++ } else {
++ cleanup(ir);
++ cdev_del(&ir->cdev);
++ kfree(ir);
++ irctls[minor] = NULL;
++ }
++
++ mutex_unlock(&lirc_dev_lock);
++
++ return 0;
++}
++EXPORT_SYMBOL(lirc_unregister_driver);
++
++int lirc_dev_fop_open(struct inode *inode, struct file *file)
++{
++ struct irctl *ir;
++ int retval = 0;
++
++ if (iminor(inode) >= MAX_IRCTL_DEVICES) {
++ printk(KERN_WARNING "lirc_dev [%d]: open result = -ENODEV\n",
++ iminor(inode));
++ return -ENODEV;
++ }
++
++ if (mutex_lock_interruptible(&lirc_dev_lock))
++ return -ERESTARTSYS;
++
++ ir = irctls[iminor(inode)];
++ if (!ir) {
++ retval = -ENODEV;
++ goto error;
++ }
++ file->private_data = ir;
++
++ dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor);
++
++ if (ir->d.minor == NOPLUG) {
++ retval = -ENODEV;
++ goto error;
++ }
++
++ if (ir->open) {
++ retval = -EBUSY;
++ goto error;
++ }
++
++ if (try_module_get(ir->d.owner)) {
++ ++ir->open;
++ retval = ir->d.set_use_inc(ir->d.data);
++
++ if (retval) {
++ module_put(ir->d.owner);
++ --ir->open;
++ } else {
++ lirc_buffer_clear(ir->buf);
++ }
++ if (ir->task)
++ wake_up_process(ir->task);
++ }
++
++error:
++ if (ir)
++ dev_dbg(ir->d.dev, LOGHEAD "open result = %d\n",
++ ir->d.name, ir->d.minor, retval);
++
++ mutex_unlock(&lirc_dev_lock);
++
++ return retval;
++}
++EXPORT_SYMBOL(lirc_dev_fop_open);
++
++int lirc_dev_fop_close(struct inode *inode, struct file *file)
++{
++ struct irctl *ir = irctls[iminor(inode)];
++
++ dev_dbg(ir->d.dev, LOGHEAD "close called\n", ir->d.name, ir->d.minor);
++
++ WARN_ON(mutex_lock_killable(&lirc_dev_lock));
++
++ --ir->open;
++ if (ir->attached) {
++ ir->d.set_use_dec(ir->d.data);
++ module_put(ir->d.owner);
++ } else {
++ cleanup(ir);
++ irctls[ir->d.minor] = NULL;
++ kfree(ir);
++ }
++
++ mutex_unlock(&lirc_dev_lock);
++
++ return 0;
++}
++EXPORT_SYMBOL(lirc_dev_fop_close);
++
++unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait)
++{
++ struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)];
++ unsigned int ret;
++
++ dev_dbg(ir->d.dev, LOGHEAD "poll called\n", ir->d.name, ir->d.minor);
++
++ if (!ir->attached) {
++ mutex_unlock(&ir->irctl_lock);
++ return POLLERR;
++ }
++
++ poll_wait(file, &ir->buf->wait_poll, wait);
++
++ if (ir->buf)
++ if (lirc_buffer_empty(ir->buf))
++ ret = 0;
++ else
++ ret = POLLIN | POLLRDNORM;
++ else
++ ret = POLLERR;
++
++ dev_dbg(ir->d.dev, LOGHEAD "poll result = %d\n",
++ ir->d.name, ir->d.minor, ret);
++
++ return ret;
++}
++EXPORT_SYMBOL(lirc_dev_fop_poll);
++
++long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
++{
++ unsigned long mode;
++ int result = 0;
++ struct irctl *ir = file->private_data;
++
++ dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n",
++ ir->d.name, ir->d.minor, cmd);
++
++ if (ir->d.minor == NOPLUG || !ir->attached) {
++ dev_dbg(ir->d.dev, LOGHEAD "ioctl result = -ENODEV\n",
++ ir->d.name, ir->d.minor);
++ return -ENODEV;
++ }
++
++ mutex_lock(&ir->irctl_lock);
++
++ switch (cmd) {
++ case LIRC_GET_FEATURES:
++ result = put_user(ir->d.features, (unsigned long *)arg);
++ break;
++ case LIRC_GET_REC_MODE:
++ if (!(ir->d.features & LIRC_CAN_REC_MASK)) {
++ result = -ENOSYS;
++ break;
++ }
++
++ result = put_user(LIRC_REC2MODE
++ (ir->d.features & LIRC_CAN_REC_MASK),
++ (unsigned long *)arg);
++ break;
++ case LIRC_SET_REC_MODE:
++ if (!(ir->d.features & LIRC_CAN_REC_MASK)) {
++ result = -ENOSYS;
++ break;
++ }
++
++ result = get_user(mode, (unsigned long *)arg);
++ if (!result && !(LIRC_MODE2REC(mode) & ir->d.features))
++ result = -EINVAL;
++ /*
++ * FIXME: We should actually set the mode somehow but
++ * for now, lirc_serial doesn't support mode changing either
++ */
++ break;
++ case LIRC_GET_LENGTH:
++ result = put_user(ir->d.code_length, (unsigned long *)arg);
++ break;
++ case LIRC_GET_MIN_TIMEOUT:
++ if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||
++ ir->d.min_timeout == 0) {
++ result = -ENOSYS;
++ break;
++ }
++
++ result = put_user(ir->d.min_timeout, (unsigned long *)arg);
++ break;
++ case LIRC_GET_MAX_TIMEOUT:
++ if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||
++ ir->d.max_timeout == 0) {
++ result = -ENOSYS;
++ break;
++ }
++
++ result = put_user(ir->d.max_timeout, (unsigned long *)arg);
++ break;
++ default:
++ result = -EINVAL;
++ }
++
++ dev_dbg(ir->d.dev, LOGHEAD "ioctl result = %d\n",
++ ir->d.name, ir->d.minor, result);
++
++ mutex_unlock(&ir->irctl_lock);
++
++ return result;
++}
++EXPORT_SYMBOL(lirc_dev_fop_ioctl);
++
++ssize_t lirc_dev_fop_read(struct file *file,
++ char *buffer,
++ size_t length,
++ loff_t *ppos)
++{
++ struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)];
++ unsigned char buf[ir->chunk_size];
++ int ret = 0, written = 0;
++ DECLARE_WAITQUEUE(wait, current);
++
++ dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor);
++
++ if (mutex_lock_interruptible(&ir->irctl_lock))
++ return -ERESTARTSYS;
++ if (!ir->attached) {
++ mutex_unlock(&ir->irctl_lock);
++ return -ENODEV;
++ }
++
++ if (length % ir->chunk_size) {
++ dev_dbg(ir->d.dev, LOGHEAD "read result = -EINVAL\n",
++ ir->d.name, ir->d.minor);
++ mutex_unlock(&ir->irctl_lock);
++ return -EINVAL;
++ }
++
++ /*
++ * we add ourselves to the task queue before buffer check
++ * to avoid losing scan code (in case when queue is awaken somewhere
++ * between while condition checking and scheduling)
++ */
++ add_wait_queue(&ir->buf->wait_poll, &wait);
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ /*
++ * while we didn't provide 'length' bytes, device is opened in blocking
++ * mode and 'copy_to_user' is happy, wait for data.
++ */
++ while (written < length && ret == 0) {
++ if (lirc_buffer_empty(ir->buf)) {
++ /* According to the read(2) man page, 'written' can be
++ * returned as less than 'length', instead of blocking
++ * again, returning -EWOULDBLOCK, or returning
++ * -ERESTARTSYS */
++ if (written)
++ break;
++ if (file->f_flags & O_NONBLOCK) {
++ ret = -EWOULDBLOCK;
++ break;
++ }
++ if (signal_pending(current)) {
++ ret = -ERESTARTSYS;
++ break;
++ }
++
++ mutex_unlock(&ir->irctl_lock);
++ schedule();
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (mutex_lock_interruptible(&ir->irctl_lock)) {
++ ret = -ERESTARTSYS;
++ remove_wait_queue(&ir->buf->wait_poll, &wait);
++ set_current_state(TASK_RUNNING);
++ goto out_unlocked;
++ }
++
++ if (!ir->attached) {
++ ret = -ENODEV;
++ break;
++ }
++ } else {
++ lirc_buffer_read(ir->buf, buf);
++ ret = copy_to_user((void *)buffer+written, buf,
++ ir->buf->chunk_size);
++ written += ir->buf->chunk_size;
++ }
++ }
++
++ remove_wait_queue(&ir->buf->wait_poll, &wait);
++ set_current_state(TASK_RUNNING);
++ mutex_unlock(&ir->irctl_lock);
++
++out_unlocked:
++ dev_dbg(ir->d.dev, LOGHEAD "read result = %s (%d)\n",
++ ir->d.name, ir->d.minor, ret ? "-EFAULT" : "OK", ret);
++
++ return ret ? ret : written;
++}
++EXPORT_SYMBOL(lirc_dev_fop_read);
++
++void *lirc_get_pdata(struct file *file)
++{
++ void *data = NULL;
++
++ if (file && file->f_dentry && file->f_dentry->d_inode &&
++ file->f_dentry->d_inode->i_rdev) {
++ struct irctl *ir;
++ ir = irctls[iminor(file->f_dentry->d_inode)];
++ data = ir->d.data;
++ }
++
++ return data;
++}
++EXPORT_SYMBOL(lirc_get_pdata);
++
++
++ssize_t lirc_dev_fop_write(struct file *file, const char *buffer,
++ size_t length, loff_t *ppos)
++{
++ struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)];
++
++ dev_dbg(ir->d.dev, LOGHEAD "write called\n", ir->d.name, ir->d.minor);
++
++ if (!ir->attached)
++ return -ENODEV;
++
++ return -EINVAL;
++}
++EXPORT_SYMBOL(lirc_dev_fop_write);
++
++
++static int __init lirc_dev_init(void)
++{
++ int retval;
++
++ lirc_class = class_create(THIS_MODULE, "lirc");
++ if (IS_ERR(lirc_class)) {
++ retval = PTR_ERR(lirc_class);
++ printk(KERN_ERR "lirc_dev: class_create failed\n");
++ goto error;
++ }
++
++ retval = alloc_chrdev_region(&lirc_base_dev, 0, MAX_IRCTL_DEVICES,
++ IRCTL_DEV_NAME);
++ if (retval) {
++ class_destroy(lirc_class);
++ printk(KERN_ERR "lirc_dev: alloc_chrdev_region failed\n");
++ goto error;
++ }
++
++
++ printk(KERN_INFO "lirc_dev: IR Remote Control driver registered, "
++ "major %d \n", MAJOR(lirc_base_dev));
++
++error:
++ return retval;
++}
++
++
++
++static void __exit lirc_dev_exit(void)
++{
++ class_destroy(lirc_class);
++ unregister_chrdev_region(lirc_base_dev, MAX_IRCTL_DEVICES);
++ printk(KERN_INFO "lirc_dev: module unloaded\n");
++}
++
++module_init(lirc_dev_init);
++module_exit(lirc_dev_exit);
++
++MODULE_DESCRIPTION("LIRC base driver module");
++MODULE_AUTHOR("Artur Lipowski");
++MODULE_LICENSE("GPL");
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Enable debugging messages");
+diff --git a/drivers/media/IR/mceusb.c b/drivers/media/IR/mceusb.c
+new file mode 100644
+index 0000000..78bf7f7
+--- /dev/null
++++ b/drivers/media/IR/mceusb.c
+@@ -0,0 +1,1143 @@
++/*
++ * Driver for USB Windows Media Center Ed. eHome Infrared Transceivers
++ *
++ * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.com>
++ *
++ * Based on the original lirc_mceusb and lirc_mceusb2 drivers, by Dan
++ * Conti, Martin Blatter and Daniel Melander, the latter of which was
++ * in turn also based on the lirc_atiusb driver by Paul Miller. The
++ * two mce drivers were merged into one by Jarod Wilson, with transmit
++ * support for the 1st-gen device added primarily by Patrick Calhoun,
++ * with a bit of tweaks by Jarod. Debugging improvements and proper
++ * support for what appears to be 3rd-gen hardware added by Jarod.
++ * Initial port from lirc driver to ir-core drivery by Jarod, based
++ * partially on a port to an earlier proposed IR infrastructure by
++ * Jon Smirl, which included enhancements and simplifications to the
++ * incoming IR buffer parsing routines.
++ *
++ *
++ * 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/device.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/usb.h>
++#include <linux/input.h>
++#include <media/ir-core.h>
++#include <media/ir-common.h>
++
++#define DRIVER_VERSION "1.91"
++#define DRIVER_AUTHOR "Jarod Wilson <jarod@wilsonet.com>"
++#define DRIVER_DESC "Windows Media Center Ed. eHome Infrared Transceiver " \
++ "device driver"
++#define DRIVER_NAME "mceusb"
++
++#define USB_BUFLEN 32 /* USB reception buffer length */
++#define USB_CTRL_MSG_SZ 2 /* Size of usb ctrl msg on gen1 hw */
++#define MCE_G1_INIT_MSGS 40 /* Init messages on gen1 hw to throw out */
++
++/* MCE constants */
++#define MCE_CMDBUF_SIZE 384 /* MCE Command buffer length */
++#define MCE_TIME_UNIT 50 /* Approx 50us resolution */
++#define MCE_CODE_LENGTH 5 /* Normal length of packet (with header) */
++#define MCE_PACKET_SIZE 4 /* Normal length of packet (without header) */
++#define MCE_PACKET_HEADER 0x84 /* Actual header format is 0x80 + num_bytes */
++#define MCE_CONTROL_HEADER 0x9F /* MCE status header */
++#define MCE_TX_HEADER_LENGTH 3 /* # of bytes in the initializing tx header */
++#define MCE_MAX_CHANNELS 2 /* Two transmitters, hardware dependent? */
++#define MCE_DEFAULT_TX_MASK 0x03 /* Val opts: TX1=0x01, TX2=0x02, ALL=0x03 */
++#define MCE_PULSE_BIT 0x80 /* Pulse bit, MSB set == PULSE else SPACE */
++#define MCE_PULSE_MASK 0x7F /* Pulse mask */
++#define MCE_MAX_PULSE_LENGTH 0x7F /* Longest transmittable pulse symbol */
++#define MCE_PACKET_LENGTH_MASK 0x1F /* Packet length mask */
++
++
++/* module parameters */
++#ifdef CONFIG_USB_DEBUG
++static int debug = 1;
++#else
++static int debug;
++#endif
++
++/* general constants */
++#define SEND_FLAG_IN_PROGRESS 1
++#define SEND_FLAG_COMPLETE 2
++#define RECV_FLAG_IN_PROGRESS 3
++#define RECV_FLAG_COMPLETE 4
++
++#define MCEUSB_RX 1
++#define MCEUSB_TX 2
++
++#define VENDOR_PHILIPS 0x0471
++#define VENDOR_SMK 0x0609
++#define VENDOR_TATUNG 0x1460
++#define VENDOR_GATEWAY 0x107b
++#define VENDOR_SHUTTLE 0x1308
++#define VENDOR_SHUTTLE2 0x051c
++#define VENDOR_MITSUMI 0x03ee
++#define VENDOR_TOPSEED 0x1784
++#define VENDOR_RICAVISION 0x179d
++#define VENDOR_ITRON 0x195d
++#define VENDOR_FIC 0x1509
++#define VENDOR_LG 0x043e
++#define VENDOR_MICROSOFT 0x045e
++#define VENDOR_FORMOSA 0x147a
++#define VENDOR_FINTEK 0x1934
++#define VENDOR_PINNACLE 0x2304
++#define VENDOR_ECS 0x1019
++#define VENDOR_WISTRON 0x0fb8
++#define VENDOR_COMPRO 0x185b
++#define VENDOR_NORTHSTAR 0x04eb
++#define VENDOR_REALTEK 0x0bda
++#define VENDOR_TIVO 0x105a
++
++static struct usb_device_id mceusb_dev_table[] = {
++ /* Original Microsoft MCE IR Transceiver (often HP-branded) */
++ { USB_DEVICE(VENDOR_MICROSOFT, 0x006d) },
++ /* Philips Infrared Transceiver - Sahara branded */
++ { USB_DEVICE(VENDOR_PHILIPS, 0x0608) },
++ /* Philips Infrared Transceiver - HP branded */
++ { USB_DEVICE(VENDOR_PHILIPS, 0x060c) },
++ /* Philips SRM5100 */
++ { USB_DEVICE(VENDOR_PHILIPS, 0x060d) },
++ /* Philips Infrared Transceiver - Omaura */
++ { USB_DEVICE(VENDOR_PHILIPS, 0x060f) },
++ /* Philips Infrared Transceiver - Spinel plus */
++ { USB_DEVICE(VENDOR_PHILIPS, 0x0613) },
++ /* Philips eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_PHILIPS, 0x0815) },
++ /* Realtek MCE IR Receiver */
++ { USB_DEVICE(VENDOR_REALTEK, 0x0161) },
++ /* SMK/Toshiba G83C0004D410 */
++ { USB_DEVICE(VENDOR_SMK, 0x031d) },
++ /* SMK eHome Infrared Transceiver (Sony VAIO) */
++ { USB_DEVICE(VENDOR_SMK, 0x0322) },
++ /* bundled with Hauppauge PVR-150 */
++ { USB_DEVICE(VENDOR_SMK, 0x0334) },
++ /* SMK eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_SMK, 0x0338) },
++ /* Tatung eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_TATUNG, 0x9150) },
++ /* Shuttle eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_SHUTTLE, 0xc001) },
++ /* Shuttle eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_SHUTTLE2, 0xc001) },
++ /* Gateway eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_GATEWAY, 0x3009) },
++ /* Mitsumi */
++ { USB_DEVICE(VENDOR_MITSUMI, 0x2501) },
++ /* Topseed eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0001) },
++ /* Topseed HP eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0006) },
++ /* Topseed eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0007) },
++ /* Topseed eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
++ /* Topseed eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_TOPSEED, 0x000a) },
++ /* Topseed eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0011) },
++ /* Ricavision internal Infrared Transceiver */
++ { USB_DEVICE(VENDOR_RICAVISION, 0x0010) },
++ /* Itron ione Libra Q-11 */
++ { USB_DEVICE(VENDOR_ITRON, 0x7002) },
++ /* FIC eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_FIC, 0x9242) },
++ /* LG eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_LG, 0x9803) },
++ /* Microsoft MCE Infrared Transceiver */
++ { USB_DEVICE(VENDOR_MICROSOFT, 0x00a0) },
++ /* Formosa eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_FORMOSA, 0xe015) },
++ /* Formosa21 / eHome Infrared Receiver */
++ { USB_DEVICE(VENDOR_FORMOSA, 0xe016) },
++ /* Formosa aim / Trust MCE Infrared Receiver */
++ { USB_DEVICE(VENDOR_FORMOSA, 0xe017) },
++ /* Formosa Industrial Computing / Beanbag Emulation Device */
++ { USB_DEVICE(VENDOR_FORMOSA, 0xe018) },
++ /* Formosa21 / eHome Infrared Receiver */
++ { USB_DEVICE(VENDOR_FORMOSA, 0xe03a) },
++ /* Formosa Industrial Computing AIM IR605/A */
++ { USB_DEVICE(VENDOR_FORMOSA, 0xe03c) },
++ /* Formosa Industrial Computing */
++ { USB_DEVICE(VENDOR_FORMOSA, 0xe03e) },
++ /* Fintek eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_FINTEK, 0x0602) },
++ /* Fintek eHome Infrared Transceiver (in the AOpen MP45) */
++ { USB_DEVICE(VENDOR_FINTEK, 0x0702) },
++ /* Pinnacle Remote Kit */
++ { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
++ /* Elitegroup Computer Systems IR */
++ { USB_DEVICE(VENDOR_ECS, 0x0f38) },
++ /* Wistron Corp. eHome Infrared Receiver */
++ { USB_DEVICE(VENDOR_WISTRON, 0x0002) },
++ /* Compro K100 */
++ { USB_DEVICE(VENDOR_COMPRO, 0x3020) },
++ /* Compro K100 v2 */
++ { USB_DEVICE(VENDOR_COMPRO, 0x3082) },
++ /* Northstar Systems, Inc. eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_NORTHSTAR, 0xe004) },
++ /* TiVo PC IR Receiver */
++ { USB_DEVICE(VENDOR_TIVO, 0x2000) },
++ /* Terminating entry */
++ { }
++};
++
++static struct usb_device_id gen3_list[] = {
++ { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
++ {}
++};
++
++static struct usb_device_id microsoft_gen1_list[] = {
++ { USB_DEVICE(VENDOR_MICROSOFT, 0x006d) },
++ {}
++};
++
++static struct usb_device_id std_tx_mask_list[] = {
++ { USB_DEVICE(VENDOR_MICROSOFT, 0x006d) },
++ { USB_DEVICE(VENDOR_PHILIPS, 0x060c) },
++ { USB_DEVICE(VENDOR_SMK, 0x031d) },
++ { USB_DEVICE(VENDOR_SMK, 0x0322) },
++ { USB_DEVICE(VENDOR_SMK, 0x0334) },
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0001) },
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0006) },
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0007) },
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
++ { USB_DEVICE(VENDOR_TOPSEED, 0x000a) },
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0011) },
++ { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
++ {}
++};
++
++/* data structure for each usb transceiver */
++struct mceusb_dev {
++ /* ir-core bits */
++ struct ir_input_dev *irdev;
++ struct ir_dev_props *props;
++ struct ir_raw_event rawir;
++
++ /* core device bits */
++ struct device *dev;
++ struct input_dev *idev;
++
++ /* usb */
++ struct usb_device *usbdev;
++ struct urb *urb_in;
++ struct usb_endpoint_descriptor *usb_ep_in;
++ struct usb_endpoint_descriptor *usb_ep_out;
++
++ /* buffers and dma */
++ unsigned char *buf_in;
++ unsigned int len_in;
++ u8 cmd; /* MCE command type */
++ u8 rem; /* Remaining IR data bytes in packet */
++ dma_addr_t dma_in;
++ dma_addr_t dma_out;
++
++ struct {
++ u32 connected:1;
++ u32 tx_mask_inverted:1;
++ u32 microsoft_gen1:1;
++ u32 reserved:29;
++ } flags;
++
++ /* transmit support */
++ int send_flags;
++ u32 carrier;
++ unsigned char tx_mask;
++
++ char name[128];
++ char phys[64];
++};
++
++/*
++ * MCE Device Command Strings
++ * Device command responses vary from device to device...
++ * - DEVICE_RESET resets the hardware to its default state
++ * - GET_REVISION fetches the hardware/software revision, common
++ * replies are ff 0b 45 ff 1b 08 and ff 0b 50 ff 1b 42
++ * - GET_CARRIER_FREQ gets the carrier mode and frequency of the
++ * device, with replies in the form of 9f 06 MM FF, where MM is 0-3,
++ * meaning clk of 10000000, 2500000, 625000 or 156250, and FF is
++ * ((clk / frequency) - 1)
++ * - GET_RX_TIMEOUT fetches the receiver timeout in units of 50us,
++ * response in the form of 9f 0c msb lsb
++ * - GET_TX_BITMASK fetches the transmitter bitmask, replies in
++ * the form of 9f 08 bm, where bm is the bitmask
++ * - GET_RX_SENSOR fetches the RX sensor setting -- long-range
++ * general use one or short-range learning one, in the form of
++ * 9f 14 ss, where ss is either 01 for long-range or 02 for short
++ * - SET_CARRIER_FREQ sets a new carrier mode and frequency
++ * - SET_TX_BITMASK sets the transmitter bitmask
++ * - SET_RX_TIMEOUT sets the receiver timeout
++ * - SET_RX_SENSOR sets which receiver sensor to use
++ */
++static char DEVICE_RESET[] = {0x00, 0xff, 0xaa};
++static char GET_REVISION[] = {0xff, 0x0b};
++static char GET_UNKNOWN[] = {0xff, 0x18};
++static char GET_UNKNOWN2[] = {0x9f, 0x05};
++static char GET_CARRIER_FREQ[] = {0x9f, 0x07};
++static char GET_RX_TIMEOUT[] = {0x9f, 0x0d};
++static char GET_TX_BITMASK[] = {0x9f, 0x13};
++static char GET_RX_SENSOR[] = {0x9f, 0x15};
++/* sub in desired values in lower byte or bytes for full command */
++/* FIXME: make use of these for transmit.
++static char SET_CARRIER_FREQ[] = {0x9f, 0x06, 0x00, 0x00};
++static char SET_TX_BITMASK[] = {0x9f, 0x08, 0x00};
++static char SET_RX_TIMEOUT[] = {0x9f, 0x0c, 0x00, 0x00};
++static char SET_RX_SENSOR[] = {0x9f, 0x14, 0x00};
++*/
++
++static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
++ int len, bool out)
++{
++ char codes[USB_BUFLEN * 3 + 1];
++ char inout[9];
++ int i;
++ u8 cmd, subcmd, data1, data2;
++ struct device *dev = ir->dev;
++ int idx = 0;
++
++ /* skip meaningless 0xb1 0x60 header bytes on orig receiver */
++ if (ir->flags.microsoft_gen1 && !out)
++ idx = 2;
++
++ if (len <= idx)
++ return;
++
++ for (i = 0; i < len && i < USB_BUFLEN; i++)
++ snprintf(codes + i * 3, 4, "%02x ", buf[i] & 0xFF);
++
++ dev_info(dev, "%sx data: %s (length=%d)\n",
++ (out ? "t" : "r"), codes, len);
++
++ if (out)
++ strcpy(inout, "Request\0");
++ else
++ strcpy(inout, "Got\0");
++
++ cmd = buf[idx] & 0xff;
++ subcmd = buf[idx + 1] & 0xff;
++ data1 = buf[idx + 2] & 0xff;
++ data2 = buf[idx + 3] & 0xff;
++
++ switch (cmd) {
++ case 0x00:
++ if (subcmd == 0xff && data1 == 0xaa)
++ dev_info(dev, "Device reset requested\n");
++ else
++ dev_info(dev, "Unknown command 0x%02x 0x%02x\n",
++ cmd, subcmd);
++ break;
++ case 0xff:
++ switch (subcmd) {
++ case 0x0b:
++ if (len == 2)
++ dev_info(dev, "Get hw/sw rev?\n");
++ else
++ dev_info(dev, "hw/sw rev 0x%02x 0x%02x "
++ "0x%02x 0x%02x\n", data1, data2,
++ buf[idx + 4], buf[idx + 5]);
++ break;
++ case 0xaa:
++ dev_info(dev, "Device reset requested\n");
++ break;
++ case 0xfe:
++ dev_info(dev, "Previous command not supported\n");
++ break;
++ case 0x18:
++ case 0x1b:
++ default:
++ dev_info(dev, "Unknown command 0x%02x 0x%02x\n",
++ cmd, subcmd);
++ break;
++ }
++ break;
++ case 0x9f:
++ switch (subcmd) {
++ case 0x03:
++ dev_info(dev, "Ping\n");
++ break;
++ case 0x04:
++ dev_info(dev, "Resp to 9f 05 of 0x%02x 0x%02x\n",
++ data1, data2);
++ break;
++ case 0x06:
++ dev_info(dev, "%s carrier mode and freq of "
++ "0x%02x 0x%02x\n", inout, data1, data2);
++ break;
++ case 0x07:
++ dev_info(dev, "Get carrier mode and freq\n");
++ break;
++ case 0x08:
++ dev_info(dev, "%s transmit blaster mask of 0x%02x\n",
++ inout, data1);
++ break;
++ case 0x0c:
++ /* value is in units of 50us, so x*50/100 or x/2 ms */
++ dev_info(dev, "%s receive timeout of %d ms\n",
++ inout, ((data1 << 8) | data2) / 2);
++ break;
++ case 0x0d:
++ dev_info(dev, "Get receive timeout\n");
++ break;
++ case 0x13:
++ dev_info(dev, "Get transmit blaster mask\n");
++ break;
++ case 0x14:
++ dev_info(dev, "%s %s-range receive sensor in use\n",
++ inout, data1 == 0x02 ? "short" : "long");
++ break;
++ case 0x15:
++ if (len == 2)
++ dev_info(dev, "Get receive sensor\n");
++ else
++ dev_info(dev, "Received pulse count is %d\n",
++ ((data1 << 8) | data2));
++ break;
++ case 0xfe:
++ dev_info(dev, "Error! Hardware is likely wedged...\n");
++ break;
++ case 0x05:
++ case 0x09:
++ case 0x0f:
++ default:
++ dev_info(dev, "Unknown command 0x%02x 0x%02x\n",
++ cmd, subcmd);
++ break;
++ }
++ break;
++ default:
++ break;
++ }
++}
++
++static void usb_async_callback(struct urb *urb, struct pt_regs *regs)
++{
++ struct mceusb_dev *ir;
++ int len;
++
++ if (!urb)
++ return;
++
++ ir = urb->context;
++ if (ir) {
++ len = urb->actual_length;
++
++ dev_dbg(ir->dev, "callback called (status=%d len=%d)\n",
++ urb->status, len);
++
++ if (debug)
++ mceusb_dev_printdata(ir, urb->transfer_buffer,
++ len, true);
++ }
++
++}
++
++/* request incoming or send outgoing usb packet - used to initialize remote */
++static void mce_request_packet(struct mceusb_dev *ir,
++ struct usb_endpoint_descriptor *ep,
++ unsigned char *data, int size, int urb_type)
++{
++ int res;
++ struct urb *async_urb;
++ struct device *dev = ir->dev;
++ unsigned char *async_buf;
++
++ if (urb_type == MCEUSB_TX) {
++ async_urb = usb_alloc_urb(0, GFP_KERNEL);
++ if (unlikely(!async_urb)) {
++ dev_err(dev, "Error, couldn't allocate urb!\n");
++ return;
++ }
++
++ async_buf = kzalloc(size, GFP_KERNEL);
++ if (!async_buf) {
++ dev_err(dev, "Error, couldn't allocate buf!\n");
++ usb_free_urb(async_urb);
++ return;
++ }
++
++ /* outbound data */
++ usb_fill_int_urb(async_urb, ir->usbdev,
++ usb_sndintpipe(ir->usbdev, ep->bEndpointAddress),
++ async_buf, size, (usb_complete_t) usb_async_callback,
++ ir, ep->bInterval);
++ memcpy(async_buf, data, size);
++
++ } else if (urb_type == MCEUSB_RX) {
++ /* standard request */
++ async_urb = ir->urb_in;
++ ir->send_flags = RECV_FLAG_IN_PROGRESS;
++
++ } else {
++ dev_err(dev, "Error! Unknown urb type %d\n", urb_type);
++ return;
++ }
++
++ dev_dbg(dev, "receive request called (size=%#x)\n", size);
++
++ async_urb->transfer_buffer_length = size;
++ async_urb->dev = ir->usbdev;
++
++ res = usb_submit_urb(async_urb, GFP_ATOMIC);
++ if (res) {
++ dev_dbg(dev, "receive request FAILED! (res=%d)\n", res);
++ return;
++ }
++ dev_dbg(dev, "receive request complete (res=%d)\n", res);
++}
++
++static void mce_async_out(struct mceusb_dev *ir, unsigned char *data, int size)
++{
++ mce_request_packet(ir, ir->usb_ep_out, data, size, MCEUSB_TX);
++}
++
++static void mce_sync_in(struct mceusb_dev *ir, unsigned char *data, int size)
++{
++ mce_request_packet(ir, ir->usb_ep_in, data, size, MCEUSB_RX);
++}
++
++/* Send data out the IR blaster port(s) */
++static int mceusb_tx_ir(void *priv, int *txbuf, u32 n)
++{
++ struct mceusb_dev *ir = priv;
++ int i, ret = 0;
++ int count, cmdcount = 0;
++ unsigned char *cmdbuf; /* MCE command buffer */
++ long signal_duration = 0; /* Singnal length in us */
++ struct timeval start_time, end_time;
++
++ do_gettimeofday(&start_time);
++
++ count = n / sizeof(int);
++
++ cmdbuf = kzalloc(sizeof(int) * MCE_CMDBUF_SIZE, GFP_KERNEL);
++ if (!cmdbuf)
++ return -ENOMEM;
++
++ /* MCE tx init header */
++ cmdbuf[cmdcount++] = MCE_CONTROL_HEADER;
++ cmdbuf[cmdcount++] = 0x08;
++ cmdbuf[cmdcount++] = ir->tx_mask;
++
++ /* Generate mce packet data */
++ for (i = 0; (i < count) && (cmdcount < MCE_CMDBUF_SIZE); i++) {
++ signal_duration += txbuf[i];
++ txbuf[i] = txbuf[i] / MCE_TIME_UNIT;
++
++ do { /* loop to support long pulses/spaces > 127*50us=6.35ms */
++
++ /* Insert mce packet header every 4th entry */
++ if ((cmdcount < MCE_CMDBUF_SIZE) &&
++ (cmdcount - MCE_TX_HEADER_LENGTH) %
++ MCE_CODE_LENGTH == 0)
++ cmdbuf[cmdcount++] = MCE_PACKET_HEADER;
++
++ /* Insert mce packet data */
++ if (cmdcount < MCE_CMDBUF_SIZE)
++ cmdbuf[cmdcount++] =
++ (txbuf[i] < MCE_PULSE_BIT ?
++ txbuf[i] : MCE_MAX_PULSE_LENGTH) |
++ (i & 1 ? 0x00 : MCE_PULSE_BIT);
++ else {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ } while ((txbuf[i] > MCE_MAX_PULSE_LENGTH) &&
++ (txbuf[i] -= MCE_MAX_PULSE_LENGTH));
++ }
++
++ /* Fix packet length in last header */
++ cmdbuf[cmdcount - (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH] =
++ 0x80 + (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH - 1;
++
++ /* Check if we have room for the empty packet at the end */
++ if (cmdcount >= MCE_CMDBUF_SIZE) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ /* All mce commands end with an empty packet (0x80) */
++ cmdbuf[cmdcount++] = 0x80;
++
++ /* Transmit the command to the mce device */
++ mce_async_out(ir, cmdbuf, cmdcount);
++
++ /*
++ * The lircd gap calculation expects the write function to
++ * wait the time it takes for the ircommand to be sent before
++ * it returns.
++ */
++ do_gettimeofday(&end_time);
++ signal_duration -= (end_time.tv_usec - start_time.tv_usec) +
++ (end_time.tv_sec - start_time.tv_sec) * 1000000;
++
++ /* delay with the closest number of ticks */
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(usecs_to_jiffies(signal_duration));
++
++out:
++ kfree(cmdbuf);
++ return ret ? ret : n;
++}
++
++/* Sets active IR outputs -- mce devices typically (all?) have two */
++static int mceusb_set_tx_mask(void *priv, u32 mask)
++{
++ struct mceusb_dev *ir = priv;
++
++ if (ir->flags.tx_mask_inverted)
++ ir->tx_mask = (mask != 0x03 ? mask ^ 0x03 : mask) << 1;
++ else
++ ir->tx_mask = mask;
++
++ return 0;
++}
++
++/* Sets the send carrier frequency and mode */
++static int mceusb_set_tx_carrier(void *priv, u32 carrier)
++{
++ struct mceusb_dev *ir = priv;
++ int clk = 10000000;
++ int prescaler = 0, divisor = 0;
++ unsigned char cmdbuf[4] = { 0x9f, 0x06, 0x00, 0x00 };
++
++ /* Carrier has changed */
++ if (ir->carrier != carrier) {
++
++ if (carrier == 0) {
++ ir->carrier = carrier;
++ cmdbuf[2] = 0x01;
++ cmdbuf[3] = 0x80;
++ dev_dbg(ir->dev, "%s: disabling carrier "
++ "modulation\n", __func__);
++ mce_async_out(ir, cmdbuf, sizeof(cmdbuf));
++ return carrier;
++ }
++
++ for (prescaler = 0; prescaler < 4; ++prescaler) {
++ divisor = (clk >> (2 * prescaler)) / carrier;
++ if (divisor <= 0xFF) {
++ ir->carrier = carrier;
++ cmdbuf[2] = prescaler;
++ cmdbuf[3] = divisor;
++ dev_dbg(ir->dev, "%s: requesting %u HZ "
++ "carrier\n", __func__, carrier);
++
++ /* Transmit new carrier to mce device */
++ mce_async_out(ir, cmdbuf, sizeof(cmdbuf));
++ return carrier;
++ }
++ }
++
++ return -EINVAL;
++
++ }
++
++ return carrier;
++}
++
++static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
++{
++ struct ir_raw_event rawir = { .pulse = false, .duration = 0 };
++ int i, start_index = 0;
++ u8 hdr = MCE_CONTROL_HEADER;
++
++ /* skip meaningless 0xb1 0x60 header bytes on orig receiver */
++ if (ir->flags.microsoft_gen1)
++ start_index = 2;
++
++ for (i = start_index; i < buf_len;) {
++ if (ir->rem == 0) {
++ /* decode mce packets of the form (84),AA,BB,CC,DD */
++ /* IR data packets can span USB messages - rem */
++ hdr = ir->buf_in[i];
++ ir->rem = (hdr & MCE_PACKET_LENGTH_MASK);
++ ir->cmd = (hdr & ~MCE_PACKET_LENGTH_MASK);
++ dev_dbg(ir->dev, "New data. rem: 0x%02x, cmd: 0x%02x\n",
++ ir->rem, ir->cmd);
++ i++;
++ }
++
++ /* don't process MCE commands */
++ if (hdr == MCE_CONTROL_HEADER || hdr == 0xff) {
++ ir->rem = 0;
++ return;
++ }
++
++ for (; (ir->rem > 0) && (i < buf_len); i++) {
++ ir->rem--;
++
++ rawir.pulse = ((ir->buf_in[i] & MCE_PULSE_BIT) != 0);
++ rawir.duration = (ir->buf_in[i] & MCE_PULSE_MASK)
++ * MCE_TIME_UNIT * 1000;
++
++ if ((ir->buf_in[i] & MCE_PULSE_MASK) == 0x7f) {
++ if (ir->rawir.pulse == rawir.pulse)
++ ir->rawir.duration += rawir.duration;
++ else {
++ ir->rawir.duration = rawir.duration;
++ ir->rawir.pulse = rawir.pulse;
++ }
++ continue;
++ }
++ rawir.duration += ir->rawir.duration;
++ ir->rawir.duration = 0;
++ ir->rawir.pulse = rawir.pulse;
++
++ dev_dbg(ir->dev, "Storing %s with duration %d\n",
++ rawir.pulse ? "pulse" : "space",
++ rawir.duration);
++
++ ir_raw_event_store(ir->idev, &rawir);
++ }
++
++ if (ir->buf_in[i] == 0x80 || ir->buf_in[i] == 0x9f)
++ ir->rem = 0;
++
++ dev_dbg(ir->dev, "calling ir_raw_event_handle\n");
++ ir_raw_event_handle(ir->idev);
++ }
++}
++
++static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs)
++{
++ struct mceusb_dev *ir;
++ int buf_len;
++
++ if (!urb)
++ return;
++
++ ir = urb->context;
++ if (!ir) {
++ usb_unlink_urb(urb);
++ return;
++ }
++
++ buf_len = urb->actual_length;
++
++ if (debug)
++ mceusb_dev_printdata(ir, urb->transfer_buffer, buf_len, false);
++
++ if (ir->send_flags == RECV_FLAG_IN_PROGRESS) {
++ ir->send_flags = SEND_FLAG_COMPLETE;
++ dev_dbg(&ir->irdev->dev, "setup answer received %d bytes\n",
++ buf_len);
++ }
++
++ switch (urb->status) {
++ /* success */
++ case 0:
++ mceusb_process_ir_data(ir, buf_len);
++ break;
++
++ case -ECONNRESET:
++ case -ENOENT:
++ case -ESHUTDOWN:
++ usb_unlink_urb(urb);
++ return;
++
++ case -EPIPE:
++ default:
++ break;
++ }
++
++ usb_submit_urb(urb, GFP_ATOMIC);
++}
++
++static void mceusb_gen1_init(struct mceusb_dev *ir)
++{
++ int ret;
++ int maxp = ir->len_in;
++ struct device *dev = ir->dev;
++ char *data;
++
++ data = kzalloc(USB_CTRL_MSG_SZ, GFP_KERNEL);
++ if (!data) {
++ dev_err(dev, "%s: memory allocation failed!\n", __func__);
++ return;
++ }
++
++ /*
++ * This is a strange one. Windows issues a set address to the device
++ * on the receive control pipe and expect a certain value pair back
++ */
++ ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
++ USB_REQ_SET_ADDRESS, USB_TYPE_VENDOR, 0, 0,
++ data, USB_CTRL_MSG_SZ, HZ * 3);
++ dev_dbg(dev, "%s - ret = %d\n", __func__, ret);
++ dev_dbg(dev, "%s - data[0] = %d, data[1] = %d\n",
++ __func__, data[0], data[1]);
++
++ /* set feature: bit rate 38400 bps */
++ ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
++ USB_REQ_SET_FEATURE, USB_TYPE_VENDOR,
++ 0xc04e, 0x0000, NULL, 0, HZ * 3);
++
++ dev_dbg(dev, "%s - ret = %d\n", __func__, ret);
++
++ /* bRequest 4: set char length to 8 bits */
++ ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
++ 4, USB_TYPE_VENDOR,
++ 0x0808, 0x0000, NULL, 0, HZ * 3);
++ dev_dbg(dev, "%s - retB = %d\n", __func__, ret);
++
++ /* bRequest 2: set handshaking to use DTR/DSR */
++ ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
++ 2, USB_TYPE_VENDOR,
++ 0x0000, 0x0100, NULL, 0, HZ * 3);
++ dev_dbg(dev, "%s - retC = %d\n", __func__, ret);
++
++ /* device reset */
++ mce_async_out(ir, DEVICE_RESET, sizeof(DEVICE_RESET));
++ mce_sync_in(ir, NULL, maxp);
++
++ /* get hw/sw revision? */
++ mce_async_out(ir, GET_REVISION, sizeof(GET_REVISION));
++ mce_sync_in(ir, NULL, maxp);
++
++ kfree(data);
++};
++
++static void mceusb_gen2_init(struct mceusb_dev *ir)
++{
++ int maxp = ir->len_in;
++
++ /* device reset */
++ mce_async_out(ir, DEVICE_RESET, sizeof(DEVICE_RESET));
++ mce_sync_in(ir, NULL, maxp);
++
++ /* get hw/sw revision? */
++ mce_async_out(ir, GET_REVISION, sizeof(GET_REVISION));
++ mce_sync_in(ir, NULL, maxp);
++
++ /* unknown what the next two actually return... */
++ mce_async_out(ir, GET_UNKNOWN, sizeof(GET_UNKNOWN));
++ mce_sync_in(ir, NULL, maxp);
++ mce_async_out(ir, GET_UNKNOWN2, sizeof(GET_UNKNOWN2));
++ mce_sync_in(ir, NULL, maxp);
++}
++
++static void mceusb_get_parameters(struct mceusb_dev *ir)
++{
++ int maxp = ir->len_in;
++
++ /* get the carrier and frequency */
++ mce_async_out(ir, GET_CARRIER_FREQ, sizeof(GET_CARRIER_FREQ));
++ mce_sync_in(ir, NULL, maxp);
++
++ /* get the transmitter bitmask */
++ mce_async_out(ir, GET_TX_BITMASK, sizeof(GET_TX_BITMASK));
++ mce_sync_in(ir, NULL, maxp);
++
++ /* get receiver timeout value */
++ mce_async_out(ir, GET_RX_TIMEOUT, sizeof(GET_RX_TIMEOUT));
++ mce_sync_in(ir, NULL, maxp);
++
++ /* get receiver sensor setting */
++ mce_async_out(ir, GET_RX_SENSOR, sizeof(GET_RX_SENSOR));
++ mce_sync_in(ir, NULL, maxp);
++}
++
++static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir)
++{
++ struct input_dev *idev;
++ struct ir_dev_props *props;
++ struct ir_input_dev *irdev;
++ struct device *dev = ir->dev;
++ int ret = -ENODEV;
++
++ idev = input_allocate_device();
++ if (!idev) {
++ dev_err(dev, "remote input dev allocation failed\n");
++ goto idev_alloc_failed;
++ }
++
++ ret = -ENOMEM;
++ props = kzalloc(sizeof(struct ir_dev_props), GFP_KERNEL);
++ if (!props) {
++ dev_err(dev, "remote ir dev props allocation failed\n");
++ goto props_alloc_failed;
++ }
++
++ irdev = kzalloc(sizeof(struct ir_input_dev), GFP_KERNEL);
++ if (!irdev) {
++ dev_err(dev, "remote ir input dev allocation failed\n");
++ goto ir_dev_alloc_failed;
++ }
++
++ snprintf(ir->name, sizeof(ir->name), "Media Center Ed. eHome "
++ "Infrared Remote Transceiver (%04x:%04x)",
++ le16_to_cpu(ir->usbdev->descriptor.idVendor),
++ le16_to_cpu(ir->usbdev->descriptor.idProduct));
++
++ idev->name = ir->name;
++ usb_make_path(ir->usbdev, ir->phys, sizeof(ir->phys));
++ strlcat(ir->phys, "/input0", sizeof(ir->phys));
++ idev->phys = ir->phys;
++
++ props->priv = ir;
++ props->driver_type = RC_DRIVER_IR_RAW;
++ props->allowed_protos = IR_TYPE_ALL;
++ props->s_tx_mask = mceusb_set_tx_mask;
++ props->s_tx_carrier = mceusb_set_tx_carrier;
++ props->tx_ir = mceusb_tx_ir;
++
++ ir->props = props;
++ ir->irdev = irdev;
++
++ input_set_drvdata(idev, irdev);
++
++ ret = ir_input_register(idev, RC_MAP_RC6_MCE, props, DRIVER_NAME);
++ if (ret < 0) {
++ dev_err(dev, "remote input device register failed\n");
++ goto irdev_failed;
++ }
++
++ return idev;
++
++irdev_failed:
++ kfree(irdev);
++ir_dev_alloc_failed:
++ kfree(props);
++props_alloc_failed:
++ input_free_device(idev);
++idev_alloc_failed:
++ return NULL;
++}
++
++static int __devinit mceusb_dev_probe(struct usb_interface *intf,
++ const struct usb_device_id *id)
++{
++ struct usb_device *dev = interface_to_usbdev(intf);
++ struct usb_host_interface *idesc;
++ struct usb_endpoint_descriptor *ep = NULL;
++ struct usb_endpoint_descriptor *ep_in = NULL;
++ struct usb_endpoint_descriptor *ep_out = NULL;
++ struct usb_host_config *config;
++ struct mceusb_dev *ir = NULL;
++ int pipe, maxp, i;
++ char buf[63], name[128] = "";
++ bool is_gen3;
++ bool is_microsoft_gen1;
++ bool tx_mask_inverted;
++
++ dev_dbg(&intf->dev, ": %s called\n", __func__);
++
++ config = dev->actconfig;
++ idesc = intf->cur_altsetting;
++
++ is_gen3 = usb_match_id(intf, gen3_list) ? 1 : 0;
++ is_microsoft_gen1 = usb_match_id(intf, microsoft_gen1_list) ? 1 : 0;
++ tx_mask_inverted = usb_match_id(intf, std_tx_mask_list) ? 0 : 1;
++
++ /* step through the endpoints to find first bulk in and out endpoint */
++ for (i = 0; i < idesc->desc.bNumEndpoints; ++i) {
++ ep = &idesc->endpoint[i].desc;
++
++ if ((ep_in == NULL)
++ && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
++ == USB_DIR_IN)
++ && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
++ == USB_ENDPOINT_XFER_BULK)
++ || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
++ == USB_ENDPOINT_XFER_INT))) {
++
++ ep_in = ep;
++ ep_in->bmAttributes = USB_ENDPOINT_XFER_INT;
++ ep_in->bInterval = 1;
++ dev_dbg(&intf->dev, ": acceptable inbound endpoint "
++ "found\n");
++ }
++
++ if ((ep_out == NULL)
++ && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
++ == USB_DIR_OUT)
++ && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
++ == USB_ENDPOINT_XFER_BULK)
++ || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
++ == USB_ENDPOINT_XFER_INT))) {
++
++ ep_out = ep;
++ ep_out->bmAttributes = USB_ENDPOINT_XFER_INT;
++ ep_out->bInterval = 1;
++ dev_dbg(&intf->dev, ": acceptable outbound endpoint "
++ "found\n");
++ }
++ }
++ if (ep_in == NULL) {
++ dev_dbg(&intf->dev, ": inbound and/or endpoint not found\n");
++ return -ENODEV;
++ }
++
++ pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress);
++ maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
++
++ ir = kzalloc(sizeof(struct mceusb_dev), GFP_KERNEL);
++ if (!ir)
++ goto mem_alloc_fail;
++
++ ir->buf_in = usb_alloc_coherent(dev, maxp, GFP_ATOMIC, &ir->dma_in);
++ if (!ir->buf_in)
++ goto buf_in_alloc_fail;
++
++ ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
++ if (!ir->urb_in)
++ goto urb_in_alloc_fail;
++
++ ir->usbdev = dev;
++ ir->dev = &intf->dev;
++ ir->len_in = maxp;
++ ir->flags.microsoft_gen1 = is_microsoft_gen1;
++ ir->flags.tx_mask_inverted = tx_mask_inverted;
++
++ /* Saving usb interface data for use by the transmitter routine */
++ ir->usb_ep_in = ep_in;
++ ir->usb_ep_out = ep_out;
++
++ if (dev->descriptor.iManufacturer
++ && usb_string(dev, dev->descriptor.iManufacturer,
++ buf, sizeof(buf)) > 0)
++ strlcpy(name, buf, sizeof(name));
++ if (dev->descriptor.iProduct
++ && usb_string(dev, dev->descriptor.iProduct,
++ buf, sizeof(buf)) > 0)
++ snprintf(name + strlen(name), sizeof(name) - strlen(name),
++ " %s", buf);
++
++ ir->idev = mceusb_init_input_dev(ir);
++ if (!ir->idev)
++ goto input_dev_fail;
++
++ /* flush buffers on the device */
++ mce_sync_in(ir, NULL, maxp);
++ mce_sync_in(ir, NULL, maxp);
++
++ /* wire up inbound data handler */
++ usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in,
++ maxp, (usb_complete_t) mceusb_dev_recv, ir, ep_in->bInterval);
++ ir->urb_in->transfer_dma = ir->dma_in;
++ ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
++
++ /* initialize device */
++ if (ir->flags.microsoft_gen1)
++ mceusb_gen1_init(ir);
++ else if (!is_gen3)
++ mceusb_gen2_init(ir);
++
++ mceusb_get_parameters(ir);
++
++ mceusb_set_tx_mask(ir, MCE_DEFAULT_TX_MASK);
++
++ usb_set_intfdata(intf, ir);
++
++ dev_info(&intf->dev, "Registered %s on usb%d:%d\n", name,
++ dev->bus->busnum, dev->devnum);
++
++ return 0;
++
++ /* Error-handling path */
++input_dev_fail:
++ usb_free_urb(ir->urb_in);
++urb_in_alloc_fail:
++ usb_free_coherent(dev, maxp, ir->buf_in, ir->dma_in);
++buf_in_alloc_fail:
++ kfree(ir);
++mem_alloc_fail:
++ dev_err(&intf->dev, "%s: device setup failed!\n", __func__);
++
++ return -ENOMEM;
++}
++
++
++static void __devexit mceusb_dev_disconnect(struct usb_interface *intf)
++{
++ struct usb_device *dev = interface_to_usbdev(intf);
++ struct mceusb_dev *ir = usb_get_intfdata(intf);
++
++ usb_set_intfdata(intf, NULL);
++
++ if (!ir)
++ return;
++
++ ir->usbdev = NULL;
++ ir_input_unregister(ir->idev);
++ usb_kill_urb(ir->urb_in);
++ usb_free_urb(ir->urb_in);
++ usb_free_coherent(dev, ir->len_in, ir->buf_in, ir->dma_in);
++
++ kfree(ir);
++}
++
++static int mceusb_dev_suspend(struct usb_interface *intf, pm_message_t message)
++{
++ struct mceusb_dev *ir = usb_get_intfdata(intf);
++ dev_info(ir->dev, "suspend\n");
++ usb_kill_urb(ir->urb_in);
++ return 0;
++}
++
++static int mceusb_dev_resume(struct usb_interface *intf)
++{
++ struct mceusb_dev *ir = usb_get_intfdata(intf);
++ dev_info(ir->dev, "resume\n");
++ if (usb_submit_urb(ir->urb_in, GFP_ATOMIC))
++ return -EIO;
++ return 0;
++}
++
++static struct usb_driver mceusb_dev_driver = {
++ .name = DRIVER_NAME,
++ .probe = mceusb_dev_probe,
++ .disconnect = mceusb_dev_disconnect,
++ .suspend = mceusb_dev_suspend,
++ .resume = mceusb_dev_resume,
++ .reset_resume = mceusb_dev_resume,
++ .id_table = mceusb_dev_table
++};
++
++static int __init mceusb_dev_init(void)
++{
++ int ret;
++
++ ret = usb_register(&mceusb_dev_driver);
++ if (ret < 0)
++ printk(KERN_ERR DRIVER_NAME
++ ": usb register failed, result = %d\n", ret);
++
++ return ret;
++}
++
++static void __exit mceusb_dev_exit(void)
++{
++ usb_deregister(&mceusb_dev_driver);
++}
++
++module_init(mceusb_dev_init);
++module_exit(mceusb_dev_exit);
++
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_AUTHOR(DRIVER_AUTHOR);
++MODULE_LICENSE("GPL");
++MODULE_DEVICE_TABLE(usb, mceusb_dev_table);
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Debug enabled or not");
+diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c
+index b2e1545..7955e49 100644
+--- a/drivers/media/common/tuners/tda18271-fe.c
++++ b/drivers/media/common/tuners/tda18271-fe.c
+@@ -1249,7 +1249,7 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
+ struct tda18271_config *cfg)
+ {
+ struct tda18271_priv *priv = NULL;
+- int instance;
++ int instance, ret;
+
+ mutex_lock(&tda18271_list_mutex);
+
+@@ -1268,10 +1268,12 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
+ priv->cal_initialized = false;
+ mutex_init(&priv->lock);
+
+- if (tda_fail(tda18271_get_id(fe)))
++ ret = tda18271_get_id(fe);
++ if (tda_fail(ret))
+ goto fail;
+
+- if (tda_fail(tda18271_assign_map_layout(fe)))
++ ret = tda18271_assign_map_layout(fe);
++ if (tda_fail(ret))
+ goto fail;
+
+ mutex_lock(&priv->lock);
+diff --git a/drivers/media/dvb/mantis/Kconfig b/drivers/media/dvb/mantis/Kconfig
+index f7b72a3..decdeda 100644
+--- a/drivers/media/dvb/mantis/Kconfig
++++ b/drivers/media/dvb/mantis/Kconfig
+@@ -10,9 +10,15 @@ config MANTIS_CORE
+ config DVB_MANTIS
+ tristate "MANTIS based cards"
+ depends on MANTIS_CORE && DVB_CORE && PCI && I2C
+- select DVB_MB86A16
+- select DVB_ZL10353
+- select DVB_STV0299
++ select DVB_MB86A16 if !DVB_FE_CUSTOMISE
++ select DVB_ZL10353 if !DVB_FE_CUSTOMISE
++ select DVB_STV0299 if !DVB_FE_CUSTOMISE
++ select DVB_LNBP21 if !DVB_FE_CUSTOMISE
++ select DVB_STB0899 if !DVB_FE_CUSTOMISE
++ select DVB_STB6100 if !DVB_FE_CUSTOMISE
++ select DVB_TDA665x if !DVB_FE_CUSTOMISE
++ select DVB_TDA10021 if !DVB_FE_CUSTOMISE
++ select DVB_TDA10023 if !DVB_FE_CUSTOMISE
+ select DVB_PLL
+ help
+ Support for PCI cards based on the Mantis PCI bridge.
+@@ -23,7 +29,7 @@ config DVB_MANTIS
+ config DVB_HOPPER
+ tristate "HOPPER based cards"
+ depends on MANTIS_CORE && DVB_CORE && PCI && I2C
+- select DVB_ZL10353
++ select DVB_ZL10353 if !DVB_FE_CUSTOMISE
+ select DVB_PLL
+ help
+ Support for PCI cards based on the Hopper PCI bridge.
+diff --git a/drivers/media/dvb/mantis/mantis_input.c b/drivers/media/dvb/mantis/mantis_input.c
+index 3d4e466..a99489b 100644
+--- a/drivers/media/dvb/mantis/mantis_input.c
++++ b/drivers/media/dvb/mantis/mantis_input.c
+@@ -19,7 +19,7 @@
+ */
+
+ #include <linux/input.h>
+-#include <media/ir-common.h>
++#include <media/ir-core.h>
+ #include <linux/pci.h>
+
+ #include "dmxdev.h"
+@@ -104,7 +104,6 @@ EXPORT_SYMBOL_GPL(ir_mantis);
+ int mantis_input_init(struct mantis_pci *mantis)
+ {
+ struct input_dev *rc;
+- struct ir_input_state rc_state;
+ char name[80], dev[80];
+ int err;
+
+@@ -120,8 +119,6 @@ int mantis_input_init(struct mantis_pci *mantis)
+ rc->name = name;
+ rc->phys = dev;
+
+- ir_input_init(rc, &rc_state, IR_TYPE_OTHER);
+-
+ rc->id.bustype = BUS_PCI;
+ rc->id.vendor = mantis->vendor_id;
+ rc->id.product = mantis->device_id;
+diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
+index d639186..2014dae 100644
+--- a/drivers/media/video/cx23885/cx23885-cards.c
++++ b/drivers/media/video/cx23885/cx23885-cards.c
+@@ -408,10 +408,18 @@ struct cx23885_subid cx23885_subids[] = {
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1275,
+ }, {
+ .subvendor = 0x0070,
++ .subdevice = 0x221d,
++ .card = CX23885_BOARD_HAUPPAUGE_HVR1275,
++ }, {
++ .subvendor = 0x0070,
+ .subdevice = 0x2251,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1255,
+ }, {
+ .subvendor = 0x0070,
++ .subdevice = 0x2259,
++ .card = CX23885_BOARD_HAUPPAUGE_HVR1255,
++ }, {
++ .subvendor = 0x0070,
+ .subdevice = 0x2291,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1210,
+ }, {
+@@ -419,6 +427,38 @@ struct cx23885_subid cx23885_subids[] = {
+ .subdevice = 0x2295,
+ .card = CX23885_BOARD_HAUPPAUGE_HVR1210,
+ }, {
++ .subvendor = 0x0070,
++ .subdevice = 0x2299,
++ .card = CX23885_BOARD_HAUPPAUGE_HVR1210,
++ }, {
++ .subvendor = 0x0070,
++ .subdevice = 0x229d,
++ .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */
++ }, {
++ .subvendor = 0x0070,
++ .subdevice = 0x22f0,
++ .card = CX23885_BOARD_HAUPPAUGE_HVR1210,
++ }, {
++ .subvendor = 0x0070,
++ .subdevice = 0x22f1,
++ .card = CX23885_BOARD_HAUPPAUGE_HVR1255,
++ }, {
++ .subvendor = 0x0070,
++ .subdevice = 0x22f2,
++ .card = CX23885_BOARD_HAUPPAUGE_HVR1275,
++ }, {
++ .subvendor = 0x0070,
++ .subdevice = 0x22f3,
++ .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */
++ }, {
++ .subvendor = 0x0070,
++ .subdevice = 0x22f4,
++ .card = CX23885_BOARD_HAUPPAUGE_HVR1210,
++ }, {
++ .subvendor = 0x0070,
++ .subdevice = 0x22f5,
++ .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */
++ }, {
+ .subvendor = 0x14f1,
+ .subdevice = 0x8651,
+ .card = CX23885_BOARD_MYGICA_X8506,
+diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c
+index 0dde57e..ff76f64 100644
+--- a/drivers/media/video/cx23885/cx23885-core.c
++++ b/drivers/media/video/cx23885/cx23885-core.c
+@@ -1142,7 +1142,7 @@ void cx23885_free_buffer(struct videobuf_queue *q, struct cx23885_buffer *buf)
+
+ BUG_ON(in_interrupt());
+ videobuf_waiton(&buf->vb, 0, 0);
+- videobuf_dma_unmap(q, dma);
++ videobuf_dma_unmap(q->dev, dma);
+ videobuf_dma_free(dma);
+ btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+@@ -1953,8 +1953,12 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev,
+ goto fail_irq;
+ }
+
+- err = request_irq(pci_dev->irq, cx23885_irq,
+- IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
++ if (!pci_enable_msi(pci_dev))
++ err = request_irq(pci_dev->irq, cx23885_irq,
++ IRQF_DISABLED, dev->name, dev);
++ else
++ err = request_irq(pci_dev->irq, cx23885_irq,
++ IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
+ if (err < 0) {
+ printk(KERN_ERR "%s: can't get IRQ %d\n",
+ dev->name, pci_dev->irq);
+@@ -2000,6 +2004,7 @@ static void __devexit cx23885_finidev(struct pci_dev *pci_dev)
+
+ /* unregister stuff */
+ free_irq(pci_dev->irq, dev);
++ pci_disable_msi(pci_dev);
+
+ cx23885_dev_unregister(dev);
+ v4l2_device_unregister(v4l2_dev);
+diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c
+index 0a199d7..3d70af2 100644
+--- a/drivers/media/video/cx23885/cx23885-dvb.c
++++ b/drivers/media/video/cx23885/cx23885-dvb.c
+@@ -991,7 +991,7 @@ static int dvb_register(struct cx23885_tsport *port)
+ ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port,
+ &dev->pci->dev, adapter_nr, 0,
+ cx23885_dvb_fe_ioctl_override);
+- if (!ret)
++ if (ret)
+ return ret;
+
+ /* init CI & MAC */
+diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c
+index 5de6ba9..d0b1613 100644
+--- a/drivers/media/video/cx23885/cx23885-input.c
++++ b/drivers/media/video/cx23885/cx23885-input.c
+@@ -37,161 +37,55 @@
+
+ #include <linux/input.h>
+ #include <linux/slab.h>
+-#include <media/ir-common.h>
++#include <media/ir-core.h>
+ #include <media/v4l2-subdev.h>
+
+ #include "cx23885.h"
+
+-#define RC5_BITS 14
+-#define RC5_HALF_BITS (2*RC5_BITS)
+-#define RC5_HALF_BITS_MASK ((1 << RC5_HALF_BITS) - 1)
+-
+-#define RC5_START_BITS_NORMAL 0x3 /* Command range 0 - 63 */
+-#define RC5_START_BITS_EXTENDED 0x2 /* Command range 64 - 127 */
+-
+-#define RC5_EXTENDED_COMMAND_OFFSET 64
+-
+ #define MODULE_NAME "cx23885"
+
+-static inline unsigned int rc5_command(u32 rc5_baseband)
++static void convert_measurement(u32 x, struct ir_raw_event *y)
+ {
+- return RC5_INSTR(rc5_baseband) +
+- ((RC5_START(rc5_baseband) == RC5_START_BITS_EXTENDED)
+- ? RC5_EXTENDED_COMMAND_OFFSET : 0);
+-}
+-
+-static void cx23885_input_process_raw_rc5(struct cx23885_dev *dev)
+-{
+- struct card_ir *ir_input = dev->ir_input;
+- unsigned int code, command;
+- u32 rc5;
+-
+- /* Ignore codes that are too short to be valid RC-5 */
+- if (ir_input->last_bit < (RC5_HALF_BITS - 1))
+- return;
+-
+- /* The library has the manchester coding backwards; XOR to adapt. */
+- code = (ir_input->code & RC5_HALF_BITS_MASK) ^ RC5_HALF_BITS_MASK;
+- rc5 = ir_rc5_decode(code);
+-
+- switch (RC5_START(rc5)) {
+- case RC5_START_BITS_NORMAL:
+- break;
+- case RC5_START_BITS_EXTENDED:
+- /* Don't allow if the remote only emits standard commands */
+- if (ir_input->start == RC5_START_BITS_NORMAL)
+- return;
+- break;
+- default:
++ if (x == V4L2_SUBDEV_IR_PULSE_RX_SEQ_END) {
++ y->pulse = false;
++ y->duration = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
+ return;
+ }
+
+- if (ir_input->addr != RC5_ADDR(rc5))
+- return;
+-
+- /* Don't generate a keypress for RC-5 auto-repeated keypresses */
+- command = rc5_command(rc5);
+- if (RC5_TOGGLE(rc5) != RC5_TOGGLE(ir_input->last_rc5) ||
+- command != rc5_command(ir_input->last_rc5) ||
+- /* Catch T == 0, CMD == 0 (e.g. '0') as first keypress after init */
+- RC5_START(ir_input->last_rc5) == 0) {
+- /* This keypress is differnet: not an auto repeat */
+- ir_input_nokey(ir_input->dev, &ir_input->ir);
+- ir_input_keydown(ir_input->dev, &ir_input->ir, command);
+- }
+- ir_input->last_rc5 = rc5;
+-
+- /* Schedule when we should do the key up event: ir_input_nokey() */
+- mod_timer(&ir_input->timer_keyup,
+- jiffies + msecs_to_jiffies(ir_input->rc5_key_timeout));
++ y->pulse = (x & V4L2_SUBDEV_IR_PULSE_LEVEL_MASK) ? true : false;
++ y->duration = x & V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
+ }
+
+-static void cx23885_input_next_pulse_width_rc5(struct cx23885_dev *dev,
+- u32 ns_pulse)
++static void cx23885_input_process_measurements(struct cx23885_dev *dev,
++ bool overrun)
+ {
+- const int rc5_quarterbit_ns = 444444; /* 32 cycles/36 kHz/2 = 444 us */
+- struct card_ir *ir_input = dev->ir_input;
+- int i, level, quarterbits, halfbits;
+-
+- if (!ir_input->active) {
+- ir_input->active = 1;
+- /* assume an initial space that we may not detect or measure */
+- ir_input->code = 0;
+- ir_input->last_bit = 0;
+- }
++ struct cx23885_kernel_ir *kernel_ir = dev->kernel_ir;
++ struct ir_raw_event kernel_ir_event;
+
+- if (ns_pulse == V4L2_SUBDEV_IR_PULSE_RX_SEQ_END) {
+- ir_input->last_bit++; /* Account for the final space */
+- ir_input->active = 0;
+- cx23885_input_process_raw_rc5(dev);
+- return;
+- }
+-
+- level = (ns_pulse & V4L2_SUBDEV_IR_PULSE_LEVEL_MASK) ? 1 : 0;
+-
+- /* Skip any leading space to sync to the start bit */
+- if (ir_input->last_bit == 0 && level == 0)
+- return;
+-
+- /*
+- * With valid RC-5 we can get up to two consecutive half-bits in a
+- * single pulse measurment. Experiments have shown that the duration
+- * of a half-bit can vary. Make sure we always end up with an even
+- * number of quarter bits at the same level (mark or space).
+- */
+- ns_pulse &= V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS;
+- quarterbits = ns_pulse / rc5_quarterbit_ns;
+- if (quarterbits & 1)
+- quarterbits++;
+- halfbits = quarterbits / 2;
+-
+- for (i = 0; i < halfbits; i++) {
+- ir_input->last_bit++;
+- ir_input->code |= (level << ir_input->last_bit);
+-
+- if (ir_input->last_bit >= RC5_HALF_BITS-1) {
+- ir_input->active = 0;
+- cx23885_input_process_raw_rc5(dev);
+- /*
+- * If level is 1, a leading mark is invalid for RC5.
+- * If level is 0, we scan past extra intial space.
+- * Either way we don't want to reactivate collecting
+- * marks or spaces here with any left over half-bits.
+- */
+- break;
+- }
+- }
+-}
+-
+-static void cx23885_input_process_pulse_widths_rc5(struct cx23885_dev *dev,
+- bool add_eom)
+-{
+- struct card_ir *ir_input = dev->ir_input;
+- struct ir_input_state *ir_input_state = &ir_input->ir;
+-
+- u32 ns_pulse[RC5_HALF_BITS+1];
+- ssize_t num = 0;
++ u32 sd_ir_data[64];
++ ssize_t num;
+ int count, i;
++ bool handle = false;
+
+ do {
+- v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ns_pulse,
+- sizeof(ns_pulse), &num);
++ num = 0;
++ v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) sd_ir_data,
++ sizeof(sd_ir_data), &num);
+
+ count = num / sizeof(u32);
+
+- /* Append an end of Rx seq, if the caller requested */
+- if (add_eom && count < ARRAY_SIZE(ns_pulse)) {
+- ns_pulse[count] = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END;
+- count++;
++ for (i = 0; i < count; i++) {
++ convert_measurement(sd_ir_data[i], &kernel_ir_event);
++ ir_raw_event_store(kernel_ir->inp_dev,
++ &kernel_ir_event);
++ handle = true;
+ }
+-
+- /* Just drain the Rx FIFO, if we're called, but not RC-5 */
+- if (ir_input_state->ir_type != IR_TYPE_RC5)
+- continue;
+-
+- for (i = 0; i < count; i++)
+- cx23885_input_next_pulse_width_rc5(dev, ns_pulse[i]);
+ } while (num != 0);
++
++ if (overrun)
++ ir_raw_event_reset(kernel_ir->inp_dev);
++ else if (handle)
++ ir_raw_event_handle(kernel_ir->inp_dev);
+ }
+
+ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
+@@ -230,7 +124,7 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
+ }
+
+ if (data_available)
+- cx23885_input_process_pulse_widths_rc5(dev, overrun);
++ cx23885_input_process_measurements(dev, overrun);
+
+ if (overrun) {
+ /* If there was a FIFO overrun, clear & restart the device */
+@@ -241,34 +135,15 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events)
+ }
+ }
+
+-static void cx23885_input_ir_start(struct cx23885_dev *dev)
++static int cx23885_input_ir_start(struct cx23885_dev *dev)
+ {
+- struct card_ir *ir_input = dev->ir_input;
+- struct ir_input_state *ir_input_state = &ir_input->ir;
+ struct v4l2_subdev_ir_parameters params;
+
+ if (dev->sd_ir == NULL)
+- return;
++ return -ENODEV;
+
+ atomic_set(&dev->ir_input_stopping, 0);
+
+- /* keyup timer set up, if needed */
+- switch (dev->board) {
+- case CX23885_BOARD_HAUPPAUGE_HVR1850:
+- case CX23885_BOARD_HAUPPAUGE_HVR1290:
+- setup_timer(&ir_input->timer_keyup,
+- ir_rc5_timer_keyup, /* Not actually RC-5 specific */
+- (unsigned long) ir_input);
+- if (ir_input_state->ir_type == IR_TYPE_RC5) {
+- /*
+- * RC-5 repeats a held key every
+- * 64 bits * (2 * 32/36000) sec/bit = 113.778 ms
+- */
+- ir_input->rc5_key_timeout = 115;
+- }
+- break;
+- }
+-
+ v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, &params);
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+@@ -299,11 +174,21 @@ static void cx23885_input_ir_start(struct cx23885_dev *dev)
+ break;
+ }
+ v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, &params);
++ return 0;
++}
++
++static int cx23885_input_ir_open(void *priv)
++{
++ struct cx23885_kernel_ir *kernel_ir = priv;
++
++ if (kernel_ir->cx == NULL)
++ return -ENODEV;
++
++ return cx23885_input_ir_start(kernel_ir->cx);
+ }
+
+ static void cx23885_input_ir_stop(struct cx23885_dev *dev)
+ {
+- struct card_ir *ir_input = dev->ir_input;
+ struct v4l2_subdev_ir_parameters params;
+
+ if (dev->sd_ir == NULL)
+@@ -327,21 +212,26 @@ static void cx23885_input_ir_stop(struct cx23885_dev *dev)
+ }
+
+ flush_scheduled_work();
++}
+
+- switch (dev->board) {
+- case CX23885_BOARD_HAUPPAUGE_HVR1850:
+- case CX23885_BOARD_HAUPPAUGE_HVR1290:
+- del_timer_sync(&ir_input->timer_keyup);
+- break;
+- }
++static void cx23885_input_ir_close(void *priv)
++{
++ struct cx23885_kernel_ir *kernel_ir = priv;
++
++ if (kernel_ir->cx != NULL)
++ cx23885_input_ir_stop(kernel_ir->cx);
+ }
+
+ int cx23885_input_init(struct cx23885_dev *dev)
+ {
+- struct card_ir *ir;
+- struct input_dev *input_dev;
+- char *ir_codes = NULL;
+- int ir_type, ir_addr, ir_start;
++ struct cx23885_kernel_ir *kernel_ir;
++ struct input_dev *inp_dev;
++ struct ir_dev_props *props;
++
++ char *rc_map;
++ enum rc_driver_type driver_type;
++ unsigned long allowed_protos;
++
+ int ret;
+
+ /*
+@@ -354,53 +244,59 @@ int cx23885_input_init(struct cx23885_dev *dev)
+ switch (dev->board) {
+ case CX23885_BOARD_HAUPPAUGE_HVR1850:
+ case CX23885_BOARD_HAUPPAUGE_HVR1290:
+- /* Parameters for the grey Hauppauge remote for the HVR-1850 */
+- ir_codes = RC_MAP_HAUPPAUGE_NEW;
+- ir_type = IR_TYPE_RC5;
+- ir_addr = 0x1e; /* RC-5 system bits emitted by the remote */
+- ir_start = RC5_START_BITS_NORMAL; /* A basic RC-5 remote */
++ /* Integrated CX23888 IR controller */
++ driver_type = RC_DRIVER_IR_RAW;
++ allowed_protos = IR_TYPE_ALL;
++ /* The grey Hauppauge RC-5 remote */
++ rc_map = RC_MAP_RC5_HAUPPAUGE_NEW;
+ break;
+- }
+- if (ir_codes == NULL)
++ default:
+ return -ENODEV;
+-
+- ir = kzalloc(sizeof(*ir), GFP_KERNEL);
+- input_dev = input_allocate_device();
+- if (!ir || !input_dev) {
+- ret = -ENOMEM;
+- goto err_out_free;
+ }
+
+- ir->dev = input_dev;
+- ir->addr = ir_addr;
+- ir->start = ir_start;
++ /* cx23885 board instance kernel IR state */
++ kernel_ir = kzalloc(sizeof(struct cx23885_kernel_ir), GFP_KERNEL);
++ if (kernel_ir == NULL)
++ return -ENOMEM;
+
+- /* init input device */
+- snprintf(ir->name, sizeof(ir->name), "cx23885 IR (%s)",
+- cx23885_boards[dev->board].name);
+- snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(dev->pci));
++ kernel_ir->cx = dev;
++ kernel_ir->name = kasprintf(GFP_KERNEL, "cx23885 IR (%s)",
++ cx23885_boards[dev->board].name);
++ kernel_ir->phys = kasprintf(GFP_KERNEL, "pci-%s/ir0",
++ pci_name(dev->pci));
+
+- ret = ir_input_init(input_dev, &ir->ir, ir_type);
+- if (ret < 0)
++ /* input device */
++ inp_dev = input_allocate_device();
++ if (inp_dev == NULL) {
++ ret = -ENOMEM;
+ goto err_out_free;
++ }
+
+- input_dev->name = ir->name;
+- input_dev->phys = ir->phys;
+- input_dev->id.bustype = BUS_PCI;
+- input_dev->id.version = 1;
++ kernel_ir->inp_dev = inp_dev;
++ inp_dev->name = kernel_ir->name;
++ inp_dev->phys = kernel_ir->phys;
++ inp_dev->id.bustype = BUS_PCI;
++ inp_dev->id.version = 1;
+ if (dev->pci->subsystem_vendor) {
+- input_dev->id.vendor = dev->pci->subsystem_vendor;
+- input_dev->id.product = dev->pci->subsystem_device;
++ inp_dev->id.vendor = dev->pci->subsystem_vendor;
++ inp_dev->id.product = dev->pci->subsystem_device;
+ } else {
+- input_dev->id.vendor = dev->pci->vendor;
+- input_dev->id.product = dev->pci->device;
++ inp_dev->id.vendor = dev->pci->vendor;
++ inp_dev->id.product = dev->pci->device;
+ }
+- input_dev->dev.parent = &dev->pci->dev;
+-
+- dev->ir_input = ir;
+- cx23885_input_ir_start(dev);
+-
+- ret = ir_input_register(ir->dev, ir_codes, NULL, MODULE_NAME);
++ inp_dev->dev.parent = &dev->pci->dev;
++
++ /* kernel ir device properties */
++ props = &kernel_ir->props;
++ props->driver_type = driver_type;
++ props->allowed_protos = allowed_protos;
++ props->priv = kernel_ir;
++ props->open = cx23885_input_ir_open;
++ props->close = cx23885_input_ir_close;
++
++ /* Go */
++ dev->kernel_ir = kernel_ir;
++ ret = ir_input_register(inp_dev, rc_map, props, MODULE_NAME);
+ if (ret)
+ goto err_out_stop;
+
+@@ -408,9 +304,12 @@ int cx23885_input_init(struct cx23885_dev *dev)
+
+ err_out_stop:
+ cx23885_input_ir_stop(dev);
+- dev->ir_input = NULL;
++ dev->kernel_ir = NULL;
++ /* TODO: double check clean-up of kernel_ir->inp_dev */
+ err_out_free:
+- kfree(ir);
++ kfree(kernel_ir->phys);
++ kfree(kernel_ir->name);
++ kfree(kernel_ir);
+ return ret;
+ }
+
+@@ -419,9 +318,11 @@ void cx23885_input_fini(struct cx23885_dev *dev)
+ /* Always stop the IR hardware from generating interrupts */
+ cx23885_input_ir_stop(dev);
+
+- if (dev->ir_input == NULL)
++ if (dev->kernel_ir == NULL)
+ return;
+- ir_input_unregister(dev->ir_input->dev);
+- kfree(dev->ir_input);
+- dev->ir_input = NULL;
++ ir_input_unregister(dev->kernel_ir->inp_dev);
++ kfree(dev->kernel_ir->phys);
++ kfree(dev->kernel_ir->name);
++ kfree(dev->kernel_ir);
++ dev->kernel_ir = NULL;
+ }
+diff --git a/drivers/media/video/cx23885/cx23885-ir.c b/drivers/media/video/cx23885/cx23885-ir.c
+index 9a677eb..6ceabd4 100644
+--- a/drivers/media/video/cx23885/cx23885-ir.c
++++ b/drivers/media/video/cx23885/cx23885-ir.c
+@@ -53,7 +53,7 @@ void cx23885_ir_rx_work_handler(struct work_struct *work)
+ if (events == 0)
+ return;
+
+- if (dev->ir_input)
++ if (dev->kernel_ir)
+ cx23885_input_rx_work_handler(dev, events);
+ }
+
+diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
+index 8d6a55e..a33f2b7 100644
+--- a/drivers/media/video/cx23885/cx23885.h
++++ b/drivers/media/video/cx23885/cx23885.h
+@@ -30,6 +30,7 @@
+ #include <media/tveeprom.h>
+ #include <media/videobuf-dma-sg.h>
+ #include <media/videobuf-dvb.h>
++#include <media/ir-core.h>
+
+ #include "btcx-risc.h"
+ #include "cx23885-reg.h"
+@@ -304,6 +305,15 @@ struct cx23885_tsport {
+ void *port_priv;
+ };
+
++struct cx23885_kernel_ir {
++ struct cx23885_dev *cx;
++ char *name;
++ char *phys;
++
++ struct input_dev *inp_dev;
++ struct ir_dev_props props;
++};
++
+ struct cx23885_dev {
+ atomic_t refcount;
+ struct v4l2_device v4l2_dev;
+@@ -363,7 +373,7 @@ struct cx23885_dev {
+ struct work_struct ir_tx_work;
+ unsigned long ir_tx_notifications;
+
+- struct card_ir *ir_input;
++ struct cx23885_kernel_ir *kernel_ir;
+ atomic_t ir_input_stopping;
+
+ /* V4l */
+diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
+index 2918a6e..e8416b7 100644
+--- a/drivers/media/video/cx88/cx88-cards.c
++++ b/drivers/media/video/cx88/cx88-cards.c
+@@ -45,6 +45,10 @@ static unsigned int latency = UNSET;
+ module_param(latency,int,0444);
+ MODULE_PARM_DESC(latency,"pci latency timer");
+
++static int disable_ir;
++module_param(disable_ir, int, 0444);
++MODULE_PARM_DESC(latency, "Disable IR support");
++
+ #define info_printk(core, fmt, arg...) \
+ printk(KERN_INFO "%s: " fmt, core->name , ## arg)
+
+@@ -3498,7 +3502,10 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr)
+ }
+
+ cx88_card_setup(core);
+- cx88_ir_init(core, pci);
++ if (!disable_ir) {
++ cx88_i2c_init_ir(core);
++ cx88_ir_init(core, pci);
++ }
+
+ return core;
+ }
+diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c
+index fb39f11..375ad53 100644
+--- a/drivers/media/video/cx88/cx88-i2c.c
++++ b/drivers/media/video/cx88/cx88-i2c.c
+@@ -181,6 +181,11 @@ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci)
+ } else
+ printk("%s: i2c register FAILED\n", core->name);
+
++ return core->i2c_rc;
++}
++
++void cx88_i2c_init_ir(struct cx88_core *core)
++{
+ /* Instantiate the IR receiver device, if present */
+ if (0 == core->i2c_rc) {
+ struct i2c_board_info info;
+@@ -207,7 +212,6 @@ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci)
+ }
+ }
+ }
+- return core->i2c_rc;
+ }
+
+ /* ----------------------------------------------------------------------- */
+diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c
+index e185289..eccc5e4 100644
+--- a/drivers/media/video/cx88/cx88-input.c
++++ b/drivers/media/video/cx88/cx88-input.c
+@@ -30,6 +30,7 @@
+ #include <linux/module.h>
+
+ #include "cx88.h"
++#include <media/ir-core.h>
+ #include <media/ir-common.h>
+
+ #define MODULE_NAME "cx88xx"
+@@ -39,8 +40,8 @@
+ struct cx88_IR {
+ struct cx88_core *core;
+ struct input_dev *input;
+- struct ir_input_state ir;
+ struct ir_dev_props props;
++ u64 ir_type;
+
+ int users;
+
+@@ -51,7 +52,6 @@ struct cx88_IR {
+ u32 sampling;
+ u32 samples[16];
+ int scount;
+- unsigned long release;
+
+ /* poll external decoder */
+ int polling;
+@@ -125,29 +125,21 @@ static void cx88_ir_handle_key(struct cx88_IR *ir)
+
+ data = (data << 4) | ((gpio_key & 0xf0) >> 4);
+
+- ir_input_keydown(ir->input, &ir->ir, data);
+- ir_input_nokey(ir->input, &ir->ir);
++ ir_keydown(ir->input, data, 0);
+
+ } else if (ir->mask_keydown) {
+ /* bit set on keydown */
+- if (gpio & ir->mask_keydown) {
+- ir_input_keydown(ir->input, &ir->ir, data);
+- } else {
+- ir_input_nokey(ir->input, &ir->ir);
+- }
++ if (gpio & ir->mask_keydown)
++ ir_keydown(ir->input, data, 0);
+
+ } else if (ir->mask_keyup) {
+ /* bit cleared on keydown */
+- if (0 == (gpio & ir->mask_keyup)) {
+- ir_input_keydown(ir->input, &ir->ir, data);
+- } else {
+- ir_input_nokey(ir->input, &ir->ir);
+- }
++ if (0 == (gpio & ir->mask_keyup))
++ ir_keydown(ir->input, data, 0);
+
+ } else {
+ /* can't distinguish keydown/up :-/ */
+- ir_input_keydown(ir->input, &ir->ir, data);
+- ir_input_nokey(ir->input, &ir->ir);
++ ir_keydown(ir->input, data, 0);
+ }
+ }
+
+@@ -439,9 +431,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
+ snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)", core->board.name);
+ snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(pci));
+
+- err = ir_input_init(input_dev, &ir->ir, ir_type);
+- if (err < 0)
+- goto err_out_free;
++ ir->ir_type = ir_type;
+
+ input_dev->name = ir->name;
+ input_dev->phys = ir->phys;
+@@ -516,8 +506,6 @@ void cx88_ir_irq(struct cx88_core *core)
+ }
+ if (!ir->scount) {
+ /* nothing to sample */
+- if (ir->ir.keypressed && time_after(jiffies, ir->release))
+- ir_input_nokey(ir->input, &ir->ir);
+ return;
+ }
+
+@@ -553,7 +541,7 @@ void cx88_ir_irq(struct cx88_core *core)
+
+ if (ircode == 0) { /* key still pressed */
+ ir_dprintk("pulse distance decoded repeat code\n");
+- ir->release = jiffies + msecs_to_jiffies(120);
++ ir_repeat(ir->input);
+ break;
+ }
+
+@@ -567,10 +555,8 @@ void cx88_ir_irq(struct cx88_core *core)
+ break;
+ }
+
+- ir_dprintk("Key Code: %x\n", (ircode >> 16) & 0x7f);
+-
+- ir_input_keydown(ir->input, &ir->ir, (ircode >> 16) & 0x7f);
+- ir->release = jiffies + msecs_to_jiffies(120);
++ ir_dprintk("Key Code: %x\n", (ircode >> 16) & 0xff);
++ ir_keydown(ir->input, (ircode >> 16) & 0xff, 0);
+ break;
+ case CX88_BOARD_HAUPPAUGE:
+ case CX88_BOARD_HAUPPAUGE_DVB_T1:
+@@ -606,16 +592,16 @@ void cx88_ir_irq(struct cx88_core *core)
+ if ( dev != 0x1e && dev != 0x1f )
+ /* not a hauppauge remote */
+ break;
+- ir_input_keydown(ir->input, &ir->ir, code);
+- ir->release = jiffies + msecs_to_jiffies(120);
++ ir_keydown(ir->input, code, toggle);
+ break;
+ case CX88_BOARD_PINNACLE_PCTV_HD_800i:
+ ircode = ir_decode_biphase(ir->samples, ir->scount, 5, 7);
+ ir_dprintk("biphase decoded: %x\n", ircode);
+ if ((ircode & 0xfffff000) != 0x3000)
+ break;
+- ir_input_keydown(ir->input, &ir->ir, ircode & 0x3f);
+- ir->release = jiffies + msecs_to_jiffies(120);
++ /* Note: bit 0x800 being the toggle is assumed, not checked
++ with real hardware */
++ ir_keydown(ir->input, ircode & 0x3f, ircode & 0x0800 ? 1 : 0);
+ break;
+ }
+
+diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
+index bdb03d3..33d161a 100644
+--- a/drivers/media/video/cx88/cx88.h
++++ b/drivers/media/video/cx88/cx88.h
+@@ -636,6 +636,7 @@ extern struct videobuf_queue_ops cx8800_vbi_qops;
+ /* cx88-i2c.c */
+
+ extern int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci);
++extern void cx88_i2c_init_ir(struct cx88_core *core);
+
+
+ /* ----------------------------------------------------------- */
+diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c
+index 5c3fd94..6759cd5 100644
+--- a/drivers/media/video/em28xx/em28xx-input.c
++++ b/drivers/media/video/em28xx/em28xx-input.c
+@@ -65,17 +65,14 @@ struct em28xx_ir_poll_result {
+ struct em28xx_IR {
+ struct em28xx *dev;
+ struct input_dev *input;
+- struct ir_input_state ir;
+ char name[32];
+ char phys[32];
+
+ /* poll external decoder */
+ int polling;
+ struct delayed_work work;
+- unsigned int last_toggle:1;
+ unsigned int full_code:1;
+ unsigned int last_readcount;
+- unsigned int repeat_interval;
+
+ int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *);
+
+@@ -291,67 +288,39 @@ static int em2874_polling_getkey(struct em28xx_IR *ir,
+ static void em28xx_ir_handle_key(struct em28xx_IR *ir)
+ {
+ int result;
+- int do_sendkey = 0;
+ struct em28xx_ir_poll_result poll_result;
+
+ /* read the registers containing the IR status */
+ result = ir->get_key(ir, &poll_result);
+- if (result < 0) {
++ if (unlikely(result < 0)) {
+ dprintk("ir->get_key() failed %d\n", result);
+ return;
+ }
+
+- dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x%02x\n",
+- poll_result.toggle_bit, poll_result.read_count,
+- ir->last_readcount, poll_result.rc_address,
+- poll_result.rc_data[0]);
+-
+- if (ir->dev->chip_id == CHIP_ID_EM2874) {
+- /* The em2874 clears the readcount field every time the
+- register is read. The em2860/2880 datasheet says that it
+- is supposed to clear the readcount, but it doesn't. So with
+- the em2874, we are looking for a non-zero read count as
+- opposed to a readcount that is incrementing */
+- ir->last_readcount = 0;
+- }
+-
+- if (poll_result.read_count == 0) {
+- /* The button has not been pressed since the last read */
+- } else if (ir->last_toggle != poll_result.toggle_bit) {
+- /* A button has been pressed */
+- dprintk("button has been pressed\n");
+- ir->last_toggle = poll_result.toggle_bit;
+- ir->repeat_interval = 0;
+- do_sendkey = 1;
+- } else if (poll_result.toggle_bit == ir->last_toggle &&
+- poll_result.read_count > 0 &&
+- poll_result.read_count != ir->last_readcount) {
+- /* The button is still being held down */
+- dprintk("button being held down\n");
+-
+- /* Debouncer for first keypress */
+- if (ir->repeat_interval++ > 9) {
+- /* Start repeating after 1 second */
+- do_sendkey = 1;
+- }
+- }
+-
+- if (do_sendkey) {
+- dprintk("sending keypress\n");
+-
++ if (unlikely(poll_result.read_count != ir->last_readcount)) {
++ dprintk("%s: toggle: %d, count: %d, key 0x%02x%02x\n", __func__,
++ poll_result.toggle_bit, poll_result.read_count,
++ poll_result.rc_address, poll_result.rc_data[0]);
+ if (ir->full_code)
+- ir_input_keydown(ir->input, &ir->ir,
+- poll_result.rc_address << 8 |
+- poll_result.rc_data[0]);
++ ir_keydown(ir->input,
++ poll_result.rc_address << 8 |
++ poll_result.rc_data[0],
++ poll_result.toggle_bit);
+ else
+- ir_input_keydown(ir->input, &ir->ir,
+- poll_result.rc_data[0]);
+-
+- ir_input_nokey(ir->input, &ir->ir);
++ ir_keydown(ir->input,
++ poll_result.rc_data[0],
++ poll_result.toggle_bit);
++
++ if (ir->dev->chip_id == CHIP_ID_EM2874)
++ /* The em2874 clears the readcount field every time the
++ register is read. The em2860/2880 datasheet says that it
++ is supposed to clear the readcount, but it doesn't. So with
++ the em2874, we are looking for a non-zero read count as
++ opposed to a readcount that is incrementing */
++ ir->last_readcount = 0;
++ else
++ ir->last_readcount = poll_result.read_count;
+ }
+-
+- ir->last_readcount = poll_result.read_count;
+- return;
+ }
+
+ static void em28xx_ir_work(struct work_struct *work)
+@@ -466,11 +435,6 @@ int em28xx_ir_init(struct em28xx *dev)
+ usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
+ strlcat(ir->phys, "/input0", sizeof(ir->phys));
+
+- /* Set IR protocol */
+- err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER);
+- if (err < 0)
+- goto err_out_free;
+-
+ input_dev->name = ir->name;
+ input_dev->phys = ir->phys;
+ input_dev->id.bustype = BUS_USB;
+diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
+index 20090e3..7b9ec6e 100644
+--- a/drivers/media/video/em28xx/em28xx-video.c
++++ b/drivers/media/video/em28xx/em28xx-video.c
+@@ -654,12 +654,12 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb)
+ }
+
+ if (buf != NULL && dev->capture_type == 2) {
+- if (len > 4 && p[0] == 0x88 && p[1] == 0x88 &&
++ if (len >= 4 && p[0] == 0x88 && p[1] == 0x88 &&
+ p[2] == 0x88 && p[3] == 0x88) {
+ p += 4;
+ len -= 4;
+ }
+- if (len > 4 && p[0] == 0x22 && p[1] == 0x5a) {
++ if (len >= 4 && p[0] == 0x22 && p[1] == 0x5a) {
+ em28xx_isocdbg("Video frame %d, len=%i, %s\n",
+ p[2], len, (p[2] & 1) ?
+ "odd" : "even");
+diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
+index b252d1b..6216786 100644
+--- a/drivers/media/video/em28xx/em28xx.h
++++ b/drivers/media/video/em28xx/em28xx.h
+@@ -32,6 +32,7 @@
+ #include <linux/i2c.h>
+ #include <linux/mutex.h>
+ #include <media/ir-kbd-i2c.h>
++#include <media/ir-core.h>
+ #if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE)
+ #include <media/videobuf-dvb.h>
+ #endif
+diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c
+index 830d47b..0cae5b8 100644
+--- a/drivers/media/video/hdpvr/hdpvr-core.c
++++ b/drivers/media/video/hdpvr/hdpvr-core.c
+@@ -286,6 +286,8 @@ static int hdpvr_probe(struct usb_interface *interface,
+ goto error;
+ }
+
++ dev->workqueue = 0;
++
+ /* register v4l2_device early so it can be used for printks */
+ if (v4l2_device_register(&interface->dev, &dev->v4l2_dev)) {
+ err("v4l2_device_register failed");
+@@ -380,6 +382,9 @@ static int hdpvr_probe(struct usb_interface *interface,
+
+ error:
+ if (dev) {
++ /* Destroy single thread */
++ if (dev->workqueue)
++ destroy_workqueue(dev->workqueue);
+ /* this frees allocated memory */
+ hdpvr_delete(dev);
+ }
+diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c
+index 29d4397..27ae8bb 100644
+--- a/drivers/media/video/ir-kbd-i2c.c
++++ b/drivers/media/video/ir-kbd-i2c.c
+@@ -47,7 +47,7 @@
+ #include <linux/i2c-id.h>
+ #include <linux/workqueue.h>
+
+-#include <media/ir-common.h>
++#include <media/ir-core.h>
+ #include <media/ir-kbd-i2c.h>
+
+ /* ----------------------------------------------------------------------- */
+@@ -272,11 +272,8 @@ static void ir_key_poll(struct IR_i2c *ir)
+ return;
+ }
+
+- if (0 == rc) {
+- ir_input_nokey(ir->input, &ir->ir);
+- } else {
+- ir_input_keydown(ir->input, &ir->ir, ir_key);
+- }
++ if (rc)
++ ir_keydown(ir->input, ir_key, 0);
+ }
+
+ static void ir_work(struct work_struct *work)
+@@ -439,10 +436,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
+ dev_name(&client->dev));
+
+ /* init + register input device */
+- err = ir_input_init(input_dev, &ir->ir, ir_type);
+- if (err < 0)
+- goto err_out_free;
+-
++ ir->ir_type = ir_type;
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->name = ir->name;
+ input_dev->phys = ir->phys;
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/drivers/media/video/pvrusb2/pvrusb2-ioread.c
+index b482478..bba6115 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-ioread.c
++++ b/drivers/media/video/pvrusb2/pvrusb2-ioread.c
+@@ -223,7 +223,10 @@ int pvr2_ioread_setup(struct pvr2_ioread *cp,struct pvr2_stream *sp)
+ " pvr2_ioread_setup (setup) id=%p",cp);
+ pvr2_stream_kill(sp);
+ ret = pvr2_stream_set_buffer_count(sp,BUFFER_COUNT);
+- if (ret < 0) return ret;
++ if (ret < 0) {
++ mutex_unlock(&cp->mutex);
++ return ret;
++ }
+ for (idx = 0; idx < BUFFER_COUNT; idx++) {
+ bp = pvr2_stream_get_buffer(sp,idx);
+ pvr2_buffer_set_buffer(bp,
+diff --git a/include/linux/input.h b/include/linux/input.h
+index 6fcc910..fe2633c 100644
+--- a/include/linux/input.h
++++ b/include/linux/input.h
+@@ -34,7 +34,7 @@ struct input_event {
+ * Protocol version.
+ */
+
+-#define EV_VERSION 0x010000
++#define EV_VERSION 0x010001
+
+ /*
+ * IOCTLs (0x00 - 0x7f)
+@@ -56,12 +56,22 @@ struct input_absinfo {
+ __s32 resolution;
+ };
+
++struct keycode_table_entry {
++ __u32 keycode; /* e.g. KEY_A */
++ __u32 index; /* Index for the given scan/key table, on EVIOCGKEYCODEBIG */
++ __u32 len; /* Length of the scancode */
++ __u32 reserved[2]; /* Reserved for future usage */
++ char *scancode; /* scancode, in machine-endian */
++};
++
+ #define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */
+ #define EVIOCGID _IOR('E', 0x02, struct input_id) /* get device ID */
+ #define EVIOCGREP _IOR('E', 0x03, unsigned int[2]) /* get repeat settings */
+ #define EVIOCSREP _IOW('E', 0x03, unsigned int[2]) /* set repeat settings */
+ #define EVIOCGKEYCODE _IOR('E', 0x04, unsigned int[2]) /* get keycode */
+ #define EVIOCSKEYCODE _IOW('E', 0x04, unsigned int[2]) /* set keycode */
++#define EVIOCGKEYCODEBIG _IOR('E', 0x04, struct keycode_table_entry) /* get keycode */
++#define EVIOCSKEYCODEBIG _IOW('E', 0x04, struct keycode_table_entry) /* set keycode */
+
+ #define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */
+ #define EVIOCGPHYS(len) _IOC(_IOC_READ, 'E', 0x07, len) /* get physical location */
+@@ -1066,13 +1076,22 @@ struct ff_effect {
+ * @keycodemax: size of keycode table
+ * @keycodesize: size of elements in keycode table
+ * @keycode: map of scancodes to keycodes for this device
+- * @setkeycode: optional method to alter current keymap, used to implement
++ * @setkeycode: optional legacy method to alter current keymap, used to
++ * implement sparse keymaps. Shouldn't be used on new drivers
++ * @getkeycode: optional legacy method to retrieve current keymap.
++ * Shouldn't be used on new drivers.
++ * @setkeycodebig: optional method to alter current keymap, used to implement
+ * sparse keymaps. If not supplied default mechanism will be used.
+ * The method is being called while holding event_lock and thus must
+ * not sleep
+- * @getkeycode: optional method to retrieve current keymap. If not supplied
+- * default mechanism will be used. The method is being called while
+- * holding event_lock and thus must not sleep
++ * @getkeycodebig_from_index: optional method to retrieve current keymap from
++ * an array index. If not supplied default mechanism will be used.
++ * The method is being called while holding event_lock and thus must
++ * not sleep
++ * @getkeycodebig_from_scancode: optional method to retrieve current keymap
++ * from an scancode. If not supplied default mechanism will be used.
++ * The method is being called while holding event_lock and thus must
++ * not sleep
+ * @ff: force feedback structure associated with the device if device
+ * supports force feedback effects
+ * @repeat_key: stores key code of the last key pressed; used to implement
+@@ -1147,6 +1166,12 @@ struct input_dev {
+ unsigned int scancode, unsigned int keycode);
+ int (*getkeycode)(struct input_dev *dev,
+ unsigned int scancode, unsigned int *keycode);
++ int (*setkeycodebig)(struct input_dev *dev,
++ struct keycode_table_entry *kt_entry);
++ int (*getkeycodebig_from_index)(struct input_dev *dev,
++ struct keycode_table_entry *kt_entry);
++ int (*getkeycodebig_from_scancode)(struct input_dev *dev,
++ struct keycode_table_entry *kt_entry);
+
+ struct ff_device *ff;
+
+@@ -1422,6 +1447,10 @@ int input_get_keycode(struct input_dev *dev,
+ unsigned int scancode, unsigned int *keycode);
+ int input_set_keycode(struct input_dev *dev,
+ unsigned int scancode, unsigned int keycode);
++int input_get_keycode_big(struct input_dev *dev,
++ struct keycode_table_entry *kt_entry);
++int input_set_keycode_big(struct input_dev *dev,
++ struct keycode_table_entry *kt_entry);
+
+ extern struct class input_class;
+
+diff --git a/include/media/ir-core.h b/include/media/ir-core.h
+index ad1303f..513e60d 100644
+--- a/include/media/ir-core.h
++++ b/include/media/ir-core.h
+@@ -47,15 +47,21 @@ enum rc_driver_type {
+ * is opened.
+ * @close: callback to allow drivers to disable polling/irq when IR input device
+ * is opened.
++ * @s_tx_mask: set transmitter mask (for devices with multiple tx outputs)
++ * @s_tx_carrier: set transmit carrier frequency
++ * @tx_ir: transmit IR
+ */
+ struct ir_dev_props {
+ enum rc_driver_type driver_type;
+ unsigned long allowed_protos;
+ u32 scanmask;
+- void *priv;
++ void *priv;
+ int (*change_protocol)(void *priv, u64 ir_type);
+ int (*open)(void *priv);
+ void (*close)(void *priv);
++ int (*s_tx_mask)(void *priv, u32 mask);
++ int (*s_tx_carrier)(void *priv, u32 carrier);
++ int (*tx_ir)(void *priv, int *txbuf, u32 n);
+ };
+
+ struct ir_input_dev {
+diff --git a/include/media/ir-kbd-i2c.h b/include/media/ir-kbd-i2c.h
+index 0506e45..5e96d7a 100644
+--- a/include/media/ir-kbd-i2c.h
++++ b/include/media/ir-kbd-i2c.h
+@@ -11,7 +11,7 @@ struct IR_i2c {
+ struct i2c_client *c;
+ struct input_dev *input;
+ struct ir_input_state ir;
+-
++ u64 ir_type;
+ /* Used to avoid fast repeating */
+ unsigned char old;
+
+diff --git a/include/media/lirc.h b/include/media/lirc.h
+new file mode 100644
+index 0000000..42c467c
+--- /dev/null
++++ b/include/media/lirc.h
+@@ -0,0 +1,165 @@
++/*
++ * lirc.h - linux infrared remote control header file
++ * last modified 2010/07/13 by Jarod Wilson
++ */
++
++#ifndef _LINUX_LIRC_H
++#define _LINUX_LIRC_H
++
++#include <linux/types.h>
++#include <linux/ioctl.h>
++
++#define PULSE_BIT 0x01000000
++#define PULSE_MASK 0x00FFFFFF
++
++#define LIRC_MODE2_SPACE 0x00000000
++#define LIRC_MODE2_PULSE 0x01000000
++#define LIRC_MODE2_FREQUENCY 0x02000000
++#define LIRC_MODE2_TIMEOUT 0x03000000
++
++#define LIRC_VALUE_MASK 0x00FFFFFF
++#define LIRC_MODE2_MASK 0xFF000000
++
++#define LIRC_SPACE(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_SPACE)
++#define LIRC_PULSE(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_PULSE)
++#define LIRC_FREQUENCY(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_FREQUENCY)
++#define LIRC_TIMEOUT(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_TIMEOUT)
++
++#define LIRC_VALUE(val) ((val)&LIRC_VALUE_MASK)
++#define LIRC_MODE2(val) ((val)&LIRC_MODE2_MASK)
++
++#define LIRC_IS_SPACE(val) (LIRC_MODE2(val) == LIRC_MODE2_SPACE)
++#define LIRC_IS_PULSE(val) (LIRC_MODE2(val) == LIRC_MODE2_PULSE)
++#define LIRC_IS_FREQUENCY(val) (LIRC_MODE2(val) == LIRC_MODE2_FREQUENCY)
++#define LIRC_IS_TIMEOUT(val) (LIRC_MODE2(val) == LIRC_MODE2_TIMEOUT)
++
++/* used heavily by lirc userspace */
++#define lirc_t int
++
++/*** lirc compatible hardware features ***/
++
++#define LIRC_MODE2SEND(x) (x)
++#define LIRC_SEND2MODE(x) (x)
++#define LIRC_MODE2REC(x) ((x) << 16)
++#define LIRC_REC2MODE(x) ((x) >> 16)
++
++#define LIRC_MODE_RAW 0x00000001
++#define LIRC_MODE_PULSE 0x00000002
++#define LIRC_MODE_MODE2 0x00000004
++#define LIRC_MODE_LIRCCODE 0x00000010
++
++
++#define LIRC_CAN_SEND_RAW LIRC_MODE2SEND(LIRC_MODE_RAW)
++#define LIRC_CAN_SEND_PULSE LIRC_MODE2SEND(LIRC_MODE_PULSE)
++#define LIRC_CAN_SEND_MODE2 LIRC_MODE2SEND(LIRC_MODE_MODE2)
++#define LIRC_CAN_SEND_LIRCCODE LIRC_MODE2SEND(LIRC_MODE_LIRCCODE)
++
++#define LIRC_CAN_SEND_MASK 0x0000003f
++
++#define LIRC_CAN_SET_SEND_CARRIER 0x00000100
++#define LIRC_CAN_SET_SEND_DUTY_CYCLE 0x00000200
++#define LIRC_CAN_SET_TRANSMITTER_MASK 0x00000400
++
++#define LIRC_CAN_REC_RAW LIRC_MODE2REC(LIRC_MODE_RAW)
++#define LIRC_CAN_REC_PULSE LIRC_MODE2REC(LIRC_MODE_PULSE)
++#define LIRC_CAN_REC_MODE2 LIRC_MODE2REC(LIRC_MODE_MODE2)
++#define LIRC_CAN_REC_LIRCCODE LIRC_MODE2REC(LIRC_MODE_LIRCCODE)
++
++#define LIRC_CAN_REC_MASK LIRC_MODE2REC(LIRC_CAN_SEND_MASK)
++
++#define LIRC_CAN_SET_REC_CARRIER (LIRC_CAN_SET_SEND_CARRIER << 16)
++#define LIRC_CAN_SET_REC_DUTY_CYCLE (LIRC_CAN_SET_SEND_DUTY_CYCLE << 16)
++
++#define LIRC_CAN_SET_REC_DUTY_CYCLE_RANGE 0x40000000
++#define LIRC_CAN_SET_REC_CARRIER_RANGE 0x80000000
++#define LIRC_CAN_GET_REC_RESOLUTION 0x20000000
++#define LIRC_CAN_SET_REC_TIMEOUT 0x10000000
++#define LIRC_CAN_SET_REC_FILTER 0x08000000
++
++#define LIRC_CAN_MEASURE_CARRIER 0x02000000
++
++#define LIRC_CAN_SEND(x) ((x)&LIRC_CAN_SEND_MASK)
++#define LIRC_CAN_REC(x) ((x)&LIRC_CAN_REC_MASK)
++
++#define LIRC_CAN_NOTIFY_DECODE 0x01000000
++
++/*** IOCTL commands for lirc driver ***/
++
++#define LIRC_GET_FEATURES _IOR('i', 0x00000000, __u32)
++
++#define LIRC_GET_SEND_MODE _IOR('i', 0x00000001, __u32)
++#define LIRC_GET_REC_MODE _IOR('i', 0x00000002, __u32)
++#define LIRC_GET_SEND_CARRIER _IOR('i', 0x00000003, __u32)
++#define LIRC_GET_REC_CARRIER _IOR('i', 0x00000004, __u32)
++#define LIRC_GET_SEND_DUTY_CYCLE _IOR('i', 0x00000005, __u32)
++#define LIRC_GET_REC_DUTY_CYCLE _IOR('i', 0x00000006, __u32)
++#define LIRC_GET_REC_RESOLUTION _IOR('i', 0x00000007, __u32)
++
++#define LIRC_GET_MIN_TIMEOUT _IOR('i', 0x00000008, __u32)
++#define LIRC_GET_MAX_TIMEOUT _IOR('i', 0x00000009, __u32)
++
++#define LIRC_GET_MIN_FILTER_PULSE _IOR('i', 0x0000000a, __u32)
++#define LIRC_GET_MAX_FILTER_PULSE _IOR('i', 0x0000000b, __u32)
++#define LIRC_GET_MIN_FILTER_SPACE _IOR('i', 0x0000000c, __u32)
++#define LIRC_GET_MAX_FILTER_SPACE _IOR('i', 0x0000000d, __u32)
++
++/* code length in bits, currently only for LIRC_MODE_LIRCCODE */
++#define LIRC_GET_LENGTH _IOR('i', 0x0000000f, __u32)
++
++#define LIRC_SET_SEND_MODE _IOW('i', 0x00000011, __u32)
++#define LIRC_SET_REC_MODE _IOW('i', 0x00000012, __u32)
++/* Note: these can reset the according pulse_width */
++#define LIRC_SET_SEND_CARRIER _IOW('i', 0x00000013, __u32)
++#define LIRC_SET_REC_CARRIER _IOW('i', 0x00000014, __u32)
++#define LIRC_SET_SEND_DUTY_CYCLE _IOW('i', 0x00000015, __u32)
++#define LIRC_SET_REC_DUTY_CYCLE _IOW('i', 0x00000016, __u32)
++#define LIRC_SET_TRANSMITTER_MASK _IOW('i', 0x00000017, __u32)
++
++/*
++ * when a timeout != 0 is set the driver will send a
++ * LIRC_MODE2_TIMEOUT data packet, otherwise LIRC_MODE2_TIMEOUT is
++ * never sent, timeout is disabled by default
++ */
++#define LIRC_SET_REC_TIMEOUT _IOW('i', 0x00000018, __u32)
++
++/* 1 enables, 0 disables timeout reports in MODE2 */
++#define LIRC_SET_REC_TIMEOUT_REPORTS _IOW('i', 0x00000019, __u32)
++
++/*
++ * pulses shorter than this are filtered out by hardware (software
++ * emulation in lirc_dev?)
++ */
++#define LIRC_SET_REC_FILTER_PULSE _IOW('i', 0x0000001a, __u32)
++/*
++ * spaces shorter than this are filtered out by hardware (software
++ * emulation in lirc_dev?)
++ */
++#define LIRC_SET_REC_FILTER_SPACE _IOW('i', 0x0000001b, __u32)
++/*
++ * if filter cannot be set independantly for pulse/space, this should
++ * be used
++ */
++#define LIRC_SET_REC_FILTER _IOW('i', 0x0000001c, __u32)
++
++/*
++ * if enabled from the next key press on the driver will send
++ * LIRC_MODE2_FREQUENCY packets
++ */
++#define LIRC_SET_MEASURE_CARRIER_MODE _IOW('i', 0x0000001d, __u32)
++
++/*
++ * to set a range use
++ * LIRC_SET_REC_DUTY_CYCLE_RANGE/LIRC_SET_REC_CARRIER_RANGE with the
++ * lower bound first and later
++ * LIRC_SET_REC_DUTY_CYCLE/LIRC_SET_REC_CARRIER with the upper bound
++ */
++
++#define LIRC_SET_REC_DUTY_CYCLE_RANGE _IOW('i', 0x0000001e, __u32)
++#define LIRC_SET_REC_CARRIER_RANGE _IOW('i', 0x0000001f, __u32)
++
++#define LIRC_NOTIFY_DECODE _IO('i', 0x00000020)
++
++#define LIRC_SETUP_START _IO('i', 0x00000021)
++#define LIRC_SETUP_END _IO('i', 0x00000022)
++
++#endif
+diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h
+new file mode 100644
+index 0000000..b1f6066
+--- /dev/null
++++ b/include/media/lirc_dev.h
+@@ -0,0 +1,225 @@
++/*
++ * LIRC base driver
++ *
++ * by Artur Lipowski <alipowski@interia.pl>
++ * This code is licensed under GNU GPL
++ *
++ */
++
++#ifndef _LINUX_LIRC_DEV_H
++#define _LINUX_LIRC_DEV_H
++
++#define MAX_IRCTL_DEVICES 4
++#define BUFLEN 16
++
++#define mod(n, div) ((n) % (div))
++
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include <linux/ioctl.h>
++#include <linux/poll.h>
++#include <linux/kfifo.h>
++#include <media/lirc.h>
++
++struct lirc_buffer {
++ wait_queue_head_t wait_poll;
++ spinlock_t fifo_lock;
++ unsigned int chunk_size;
++ unsigned int size; /* in chunks */
++ /* Using chunks instead of bytes pretends to simplify boundary checking
++ * And should allow for some performance fine tunning later */
++ struct kfifo fifo;
++ u8 fifo_initialized;
++};
++
++static inline void lirc_buffer_clear(struct lirc_buffer *buf)
++{
++ unsigned long flags;
++
++ if (buf->fifo_initialized) {
++ spin_lock_irqsave(&buf->fifo_lock, flags);
++ kfifo_reset(&buf->fifo);
++ spin_unlock_irqrestore(&buf->fifo_lock, flags);
++ } else
++ WARN(1, "calling %s on an uninitialized lirc_buffer\n",
++ __func__);
++}
++
++static inline int lirc_buffer_init(struct lirc_buffer *buf,
++ unsigned int chunk_size,
++ unsigned int size)
++{
++ int ret;
++
++ init_waitqueue_head(&buf->wait_poll);
++ spin_lock_init(&buf->fifo_lock);
++ buf->chunk_size = chunk_size;
++ buf->size = size;
++ ret = kfifo_alloc(&buf->fifo, size * chunk_size, GFP_KERNEL);
++ if (ret == 0)
++ buf->fifo_initialized = 1;
++
++ return ret;
++}
++
++static inline void lirc_buffer_free(struct lirc_buffer *buf)
++{
++ if (buf->fifo_initialized) {
++ kfifo_free(&buf->fifo);
++ buf->fifo_initialized = 0;
++ } else
++ WARN(1, "calling %s on an uninitialized lirc_buffer\n",
++ __func__);
++}
++
++static inline int lirc_buffer_len(struct lirc_buffer *buf)
++{
++ int len;
++ unsigned long flags;
++
++ spin_lock_irqsave(&buf->fifo_lock, flags);
++ len = kfifo_len(&buf->fifo);
++ spin_unlock_irqrestore(&buf->fifo_lock, flags);
++
++ return len;
++}
++
++static inline int lirc_buffer_full(struct lirc_buffer *buf)
++{
++ return lirc_buffer_len(buf) == buf->size * buf->chunk_size;
++}
++
++static inline int lirc_buffer_empty(struct lirc_buffer *buf)
++{
++ return !lirc_buffer_len(buf);
++}
++
++static inline int lirc_buffer_available(struct lirc_buffer *buf)
++{
++ return buf->size - (lirc_buffer_len(buf) / buf->chunk_size);
++}
++
++static inline unsigned int lirc_buffer_read(struct lirc_buffer *buf,
++ unsigned char *dest)
++{
++ unsigned int ret = 0;
++
++ if (lirc_buffer_len(buf) >= buf->chunk_size)
++ ret = kfifo_out_locked(&buf->fifo, dest, buf->chunk_size,
++ &buf->fifo_lock);
++ return ret;
++
++}
++
++static inline unsigned int lirc_buffer_write(struct lirc_buffer *buf,
++ unsigned char *orig)
++{
++ unsigned int ret;
++
++ ret = kfifo_in_locked(&buf->fifo, orig, buf->chunk_size,
++ &buf->fifo_lock);
++
++ return ret;
++}
++
++struct lirc_driver {
++ char name[40];
++ int minor;
++ unsigned long code_length;
++ unsigned int buffer_size; /* in chunks holding one code each */
++ int sample_rate;
++ unsigned long features;
++
++ unsigned int chunk_size;
++
++ void *data;
++ int min_timeout;
++ int max_timeout;
++ int (*add_to_buf) (void *data, struct lirc_buffer *buf);
++ struct lirc_buffer *rbuf;
++ int (*set_use_inc) (void *data);
++ void (*set_use_dec) (void *data);
++ struct file_operations *fops;
++ struct device *dev;
++ struct module *owner;
++};
++
++/* name:
++ * this string will be used for logs
++ *
++ * minor:
++ * indicates minor device (/dev/lirc) number for registered driver
++ * if caller fills it with negative value, then the first free minor
++ * number will be used (if available)
++ *
++ * code_length:
++ * length of the remote control key code expressed in bits
++ *
++ * sample_rate:
++ *
++ * data:
++ * it may point to any driver data and this pointer will be passed to
++ * all callback functions
++ *
++ * add_to_buf:
++ * add_to_buf will be called after specified period of the time or
++ * triggered by the external event, this behavior depends on value of
++ * the sample_rate this function will be called in user context. This
++ * routine should return 0 if data was added to the buffer and
++ * -ENODATA if none was available. This should add some number of bits
++ * evenly divisible by code_length to the buffer
++ *
++ * rbuf:
++ * if not NULL, it will be used as a read buffer, you will have to
++ * write to the buffer by other means, like irq's (see also
++ * lirc_serial.c).
++ *
++ * set_use_inc:
++ * set_use_inc will be called after device is opened
++ *
++ * set_use_dec:
++ * set_use_dec will be called after device is closed
++ *
++ * fops:
++ * file_operations for drivers which don't fit the current driver model.
++ *
++ * Some ioctl's can be directly handled by lirc_dev if the driver's
++ * ioctl function is NULL or if it returns -ENOIOCTLCMD (see also
++ * lirc_serial.c).
++ *
++ * owner:
++ * the module owning this struct
++ *
++ */
++
++
++/* following functions can be called ONLY from user context
++ *
++ * returns negative value on error or minor number
++ * of the registered device if success
++ * contents of the structure pointed by p is copied
++ */
++extern int lirc_register_driver(struct lirc_driver *d);
++
++/* returns negative value on error or 0 if success
++*/
++extern int lirc_unregister_driver(int minor);
++
++/* Returns the private data stored in the lirc_driver
++ * associated with the given device file pointer.
++ */
++void *lirc_get_pdata(struct file *file);
++
++/* default file operations
++ * used by drivers if they override only some operations
++ */
++int lirc_dev_fop_open(struct inode *inode, struct file *file);
++int lirc_dev_fop_close(struct inode *inode, struct file *file);
++unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait);
++long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
++ssize_t lirc_dev_fop_read(struct file *file, char *buffer, size_t length,
++ loff_t *ppos);
++ssize_t lirc_dev_fop_write(struct file *file, const char *buffer, size_t length,
++ loff_t *ppos);
++
++#endif
+diff --git a/include/media/rc-map.h b/include/media/rc-map.h
+index c78e99a..a329858 100644
+--- a/include/media/rc-map.h
++++ b/include/media/rc-map.h
+@@ -17,8 +17,13 @@
+ #define IR_TYPE_RC6 (1 << 2) /* Philips RC6 protocol */
+ #define IR_TYPE_JVC (1 << 3) /* JVC protocol */
+ #define IR_TYPE_SONY (1 << 4) /* Sony12/15/20 protocol */
++#define IR_TYPE_LIRC (1 << 30) /* Pass raw IR to lirc userspace */
+ #define IR_TYPE_OTHER (1u << 31)
+
++#define IR_TYPE_ALL (IR_TYPE_RC5 | IR_TYPE_NEC | IR_TYPE_RC6 | \
++ IR_TYPE_JVC | IR_TYPE_SONY | IR_TYPE_LIRC | \
++ IR_TYPE_OTHER)
++
+ struct ir_scancode {
+ u32 scancode;
+ u32 keycode;
+@@ -87,6 +92,7 @@ void rc_map_init(void);
+ #define RC_MAP_KAIOMY "rc-kaiomy"
+ #define RC_MAP_KWORLD_315U "rc-kworld-315u"
+ #define RC_MAP_KWORLD_PLUS_TV_ANALOG "rc-kworld-plus-tv-analog"
++#define RC_MAP_LIRC "rc-lirc"
+ #define RC_MAP_MANLI "rc-manli"
+ #define RC_MAP_MSI_TVANYWHERE_PLUS "rc-msi-tvanywhere-plus"
+ #define RC_MAP_MSI_TVANYWHERE "rc-msi-tvanywhere"
+@@ -107,6 +113,7 @@ void rc_map_init(void);
+ #define RC_MAP_PV951 "rc-pv951"
+ #define RC_MAP_RC5_HAUPPAUGE_NEW "rc-rc5-hauppauge-new"
+ #define RC_MAP_RC5_TV "rc-rc5-tv"
++#define RC_MAP_RC6_MCE "rc-rc6-mce"
+ #define RC_MAP_REAL_AUDIO_220_32_KEYS "rc-real-audio-220-32-keys"
+ #define RC_MAP_TBS_NEC "rc-tbs-nec"
+ #define RC_MAP_TERRATEC_CINERGY_XS "rc-terratec-cinergy-xs"
diff --git a/linux-2.6-v4l-dvb-update.patch b/linux-2.6-v4l-dvb-update.patch
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/linux-2.6-v4l-dvb-update.patch
diff --git a/linux-2.6-v4l-dvb-uvcvideo-update.patch b/linux-2.6-v4l-dvb-uvcvideo-update.patch
new file mode 100644
index 000000000..3251c7fa3
--- /dev/null
+++ b/linux-2.6-v4l-dvb-uvcvideo-update.patch
@@ -0,0 +1,362 @@
+From: Martin Rubli <martin_rubli@logitech.com>
+Date: Wed, 19 May 2010 22:51:56 +0000 (+0200)
+Subject: uvcvideo: Add support for absolute pan/tilt controls
+X-Git-Url: http://git.linuxtv.org/pinchartl/uvcvideo.git?a=commitdiff_plain;h=d3c2f664ec76aff14c3841c99e84cd78d7227f79
+
+uvcvideo: Add support for absolute pan/tilt controls
+
+Signed-off-by: Martin Rubli <martin_rubli@logitech.com>
+---
+
+diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
+index aa0720a..5ec2f4a 100644
+--- a/drivers/media/video/uvc/uvc_ctrl.c
++++ b/drivers/media/video/uvc/uvc_ctrl.c
+@@ -606,6 +606,26 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = {
+ .set = uvc_ctrl_set_zoom,
+ },
+ {
++ .id = V4L2_CID_PAN_ABSOLUTE,
++ .name = "Pan (Absolute)",
++ .entity = UVC_GUID_UVC_CAMERA,
++ .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
++ .size = 32,
++ .offset = 0,
++ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
++ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
++ },
++ {
++ .id = V4L2_CID_TILT_ABSOLUTE,
++ .name = "Tilt (Absolute)",
++ .entity = UVC_GUID_UVC_CAMERA,
++ .selector = UVC_CT_PANTILT_ABSOLUTE_CONTROL,
++ .size = 32,
++ .offset = 32,
++ .v4l2_type = V4L2_CTRL_TYPE_INTEGER,
++ .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED,
++ },
++ {
+ .id = V4L2_CID_PRIVACY,
+ .name = "Privacy",
+ .entity = UVC_GUID_UVC_CAMERA,
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Wed, 19 May 2010 23:15:00 +0000 (+0200)
+Subject: uvcvideo: Make button controls work properly
+X-Git-Url: http://git.linuxtv.org/pinchartl/uvcvideo.git?a=commitdiff_plain;h=2bd47ad4894bfaf1a97660b821cbc46439a614d6
+
+uvcvideo: Make button controls work properly
+
+According to the v4l2 spec, writing any value to a button control should
+result in the action belonging to the button control being triggered.
+UVC cams however want to see a 1 written, this patch fixes this by
+overriding whatever value user space passed in with -1 (0xffffffff) when
+the control is a button control.
+
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+---
+
+diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
+index 5ec2f4a..8bb825d 100644
+--- a/drivers/media/video/uvc/uvc_ctrl.c
++++ b/drivers/media/video/uvc/uvc_ctrl.c
+@@ -698,6 +698,14 @@ static void uvc_set_le_value(struct uvc_control_mapping *mapping,
+ int offset = mapping->offset;
+ __u8 mask;
+
++ /* According to the v4l2 spec, writing any value to a button control
++ * should result in the action belonging to the button control being
++ * triggered. UVC devices however want to see a 1 written -> override
++ * value.
++ */
++ if (mapping->v4l2_type == V4L2_CTRL_TYPE_BUTTON)
++ value = -1;
++
+ data += offset / 8;
+ offset &= 7;
+
+From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Date: Thu, 18 Feb 2010 19:38:52 +0000 (+0100)
+Subject: uvcvideo: Support menu controls in the control mapping API
+X-Git-Url: http://git.linuxtv.org/pinchartl/uvcvideo.git?a=commitdiff_plain;h=4930f2662e47d33e5baedac620da401a225bc3a8
+
+uvcvideo: Support menu controls in the control mapping API
+
+The UVCIOC_CTRL_MAP ioctl doesn't support menu entries for menu
+controls. As the uvc_xu_control_mapping structure has no reserved
+fields, this can't be fixed while keeping ABI compatibility.
+
+Modify the UVCIOC_CTRL_MAP ioctl to add menu entries support, and define
+UVCIOC_CTRL_MAP_OLD that supports the old ABI without any ability to add
+menu controls.
+
+Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+---
+
+diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
+index 8bb825d..c88d72e 100644
+--- a/drivers/media/video/uvc/uvc_ctrl.c
++++ b/drivers/media/video/uvc/uvc_ctrl.c
+@@ -1606,6 +1606,28 @@ void uvc_ctrl_cleanup_device(struct uvc_device *dev)
+ }
+ }
+
++void uvc_ctrl_cleanup(void)
++{
++ struct uvc_control_info *info;
++ struct uvc_control_info *ni;
++ struct uvc_control_mapping *mapping;
++ struct uvc_control_mapping *nm;
++
++ list_for_each_entry_safe(info, ni, &uvc_driver.controls, list) {
++ if (!(info->flags & UVC_CONTROL_EXTENSION))
++ continue;
++
++ list_for_each_entry_safe(mapping, nm, &info->mappings, list) {
++ list_del(&mapping->list);
++ kfree(mapping->menu_info);
++ kfree(mapping);
++ }
++
++ list_del(&info->list);
++ kfree(info);
++ }
++}
++
+ void uvc_ctrl_init(void)
+ {
+ struct uvc_control_info *ctrl = uvc_ctrls;
+diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
+index 838b56f..34818c1 100644
+--- a/drivers/media/video/uvc/uvc_driver.c
++++ b/drivers/media/video/uvc/uvc_driver.c
+@@ -2261,6 +2261,7 @@ static int __init uvc_init(void)
+ static void __exit uvc_cleanup(void)
+ {
+ usb_deregister(&uvc_driver.driver);
++ uvc_ctrl_cleanup();
+ }
+
+ module_init(uvc_init);
+diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
+index 7c9ab29..485a899 100644
+--- a/drivers/media/video/uvc/uvc_v4l2.c
++++ b/drivers/media/video/uvc/uvc_v4l2.c
+@@ -29,6 +29,71 @@
+ #include "uvcvideo.h"
+
+ /* ------------------------------------------------------------------------
++ * UVC ioctls
++ */
++static int uvc_ioctl_ctrl_map(struct uvc_xu_control_mapping *xmap, int old)
++{
++ struct uvc_control_mapping *map;
++ unsigned int size;
++ int ret;
++
++ map = kzalloc(sizeof *map, GFP_KERNEL);
++ if (map == NULL)
++ return -ENOMEM;
++
++ map->id = xmap->id;
++ memcpy(map->name, xmap->name, sizeof map->name);
++ memcpy(map->entity, xmap->entity, sizeof map->entity);
++ map->selector = xmap->selector;
++ map->size = xmap->size;
++ map->offset = xmap->offset;
++ map->v4l2_type = xmap->v4l2_type;
++ map->data_type = xmap->data_type;
++
++ switch (xmap->v4l2_type) {
++ case V4L2_CTRL_TYPE_INTEGER:
++ case V4L2_CTRL_TYPE_BOOLEAN:
++ case V4L2_CTRL_TYPE_BUTTON:
++ break;
++
++ case V4L2_CTRL_TYPE_MENU:
++ if (old) {
++ ret = -EINVAL;
++ goto done;
++ }
++
++ size = xmap->menu_count * sizeof(*map->menu_info);
++ map->menu_info = kmalloc(size, GFP_KERNEL);
++ if (map->menu_info == NULL) {
++ ret = -ENOMEM;
++ goto done;
++ }
++
++ if (copy_from_user(map->menu_info, xmap->menu_info, size)) {
++ ret = -EFAULT;
++ goto done;
++ }
++
++ map->menu_count = xmap->menu_count;
++ break;
++
++ default:
++ ret = -EINVAL;
++ goto done;
++ }
++
++ ret = uvc_ctrl_add_mapping(map);
++
++done:
++ if (ret < 0) {
++ kfree(map->menu_info);
++ kfree(map);
++ }
++
++ return ret;
++}
++
++/* ------------------------------------------------------------------------
+ * V4L2 interface
+ */
+
+@@ -974,7 +1039,8 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+ info->flags = xinfo->flags;
+
+ info->flags |= UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX |
+- UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF;
++ UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF |
++ UVC_CONTROL_EXTENSION;
+
+ ret = uvc_ctrl_add_info(info);
+ if (ret < 0)
+@@ -982,32 +1048,12 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+ break;
+ }
+
++ case UVCIOC_CTRL_MAP_OLD:
+ case UVCIOC_CTRL_MAP:
+- {
+- struct uvc_xu_control_mapping *xmap = arg;
+- struct uvc_control_mapping *map;
+-
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+- map = kzalloc(sizeof *map, GFP_KERNEL);
+- if (map == NULL)
+- return -ENOMEM;
+-
+- map->id = xmap->id;
+- memcpy(map->name, xmap->name, sizeof map->name);
+- memcpy(map->entity, xmap->entity, sizeof map->entity);
+- map->selector = xmap->selector;
+- map->size = xmap->size;
+- map->offset = xmap->offset;
+- map->v4l2_type = xmap->v4l2_type;
+- map->data_type = xmap->data_type;
+-
+- ret = uvc_ctrl_add_mapping(map);
+- if (ret < 0)
+- kfree(map);
+- break;
+- }
++ return uvc_ioctl_ctrl_map(arg, cmd == UVCIOC_CTRL_MAP_OLD);
+
+ case UVCIOC_CTRL_GET:
+ return uvc_xu_ctrl_query(chain, arg, 0);
+diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
+index d1f8840..14f77e4 100644
+--- a/drivers/media/video/uvc/uvcvideo.h
++++ b/drivers/media/video/uvc/uvcvideo.h
+@@ -27,6 +27,8 @@
+ #define UVC_CONTROL_RESTORE (1 << 6)
+ /* Control can be updated by the camera. */
+ #define UVC_CONTROL_AUTO_UPDATE (1 << 7)
++/* Control is an extension unit control. */
++#define UVC_CONTROL_EXTENSION (1 << 8)
+
+ #define UVC_CONTROL_GET_RANGE (UVC_CONTROL_GET_CUR | UVC_CONTROL_GET_MIN | \
+ UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | \
+@@ -40,6 +42,15 @@ struct uvc_xu_control_info {
+ __u32 flags;
+ };
+
++struct uvc_menu_info {
++ __u32 value;
++ __u8 name[32];
++};
++
++struct uvc_xu_control_mapping_old {
++ __u8 reserved[64];
++};
++
+ struct uvc_xu_control_mapping {
+ __u32 id;
+ __u8 name[32];
+@@ -50,6 +61,11 @@ struct uvc_xu_control_mapping {
+ __u8 offset;
+ enum v4l2_ctrl_type v4l2_type;
+ __u32 data_type;
++
++ struct uvc_menu_info __user *menu_info;
++ __u32 menu_count;
++
++ __u32 reserved[4];
+ };
+
+ struct uvc_xu_control {
+@@ -60,6 +76,7 @@ struct uvc_xu_control {
+ };
+
+ #define UVCIOC_CTRL_ADD _IOW('U', 1, struct uvc_xu_control_info)
++#define UVCIOC_CTRL_MAP_OLD _IOWR('U', 2, struct uvc_xu_control_mapping_old)
+ #define UVCIOC_CTRL_MAP _IOWR('U', 2, struct uvc_xu_control_mapping)
+ #define UVCIOC_CTRL_GET _IOWR('U', 3, struct uvc_xu_control)
+ #define UVCIOC_CTRL_SET _IOW('U', 4, struct uvc_xu_control)
+@@ -198,11 +215,6 @@ struct uvc_streaming_control {
+ __u8 bMaxVersion;
+ };
+
+-struct uvc_menu_info {
+- __u32 value;
+- __u8 name[32];
+-};
+-
+ struct uvc_control_info {
+ struct list_head list;
+ struct list_head mappings;
+@@ -625,6 +637,7 @@ extern int uvc_ctrl_init_device(struct uvc_device *dev);
+ extern void uvc_ctrl_cleanup_device(struct uvc_device *dev);
+ extern int uvc_ctrl_resume_device(struct uvc_device *dev);
+ extern void uvc_ctrl_init(void);
++extern void uvc_ctrl_cleanup(void);
+
+ extern int uvc_ctrl_begin(struct uvc_video_chain *chain);
+ extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback);
+From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+Date: Fri, 25 Jun 2010 07:58:43 +0000 (+0200)
+Subject: uvcvideo: Add support for Manta MM-353 Plako
+X-Git-Url: http://git.linuxtv.org/pinchartl/uvcvideo.git?a=commitdiff_plain;h=352e661e1f347390a86cf34bc5e41adbdd1caa41
+
+uvcvideo: Add support for Manta MM-353 Plako
+
+The camera requires the PROBE_MINMAX quirk. Add a corresponding entry
+in the device IDs list
+
+Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+---
+
+diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
+index 34818c1..1a89384 100644
+--- a/drivers/media/video/uvc/uvc_driver.c
++++ b/drivers/media/video/uvc/uvc_driver.c
+@@ -2174,6 +2174,15 @@ static struct usb_device_id uvc_ids[] = {
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS },
++ /* Manta MM-353 Plako */
++ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
++ | USB_DEVICE_ID_MATCH_INT_INFO,
++ .idVendor = 0x18ec,
++ .idProduct = 0x3188,
++ .bInterfaceClass = USB_CLASS_VIDEO,
++ .bInterfaceSubClass = 1,
++ .bInterfaceProtocol = 0,
++ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* FSC WebCam V30S */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
diff --git a/linux-2.6.29-sparc-IOC_TYPECHECK.patch b/linux-2.6.29-sparc-IOC_TYPECHECK.patch
new file mode 100644
index 000000000..d73c30adc
--- /dev/null
+++ b/linux-2.6.29-sparc-IOC_TYPECHECK.patch
@@ -0,0 +1,21 @@
+diff -up vanilla-2.6.29-rc7-git2/arch/sparc/include/asm/ioctl.h.BAD vanilla-2.6.29-rc7-git2/arch/sparc/include/asm/ioctl.h
+--- vanilla-2.6.29-rc7-git2/arch/sparc/include/asm/ioctl.h.BAD 2009-03-09 17:01:32.000000000 -0400
++++ vanilla-2.6.29-rc7-git2/arch/sparc/include/asm/ioctl.h 2009-03-09 16:52:27.000000000 -0400
+@@ -41,6 +41,17 @@
+ ((nr) << _IOC_NRSHIFT) | \
+ ((size) << _IOC_SIZESHIFT))
+
++#ifdef __KERNEL__
++/* provoke compile error for invalid uses of size argument */
++extern unsigned int __invalid_size_argument_for_IOC;
++#define _IOC_TYPECHECK(t) \
++ ((sizeof(t) == sizeof(t[1]) && \
++ sizeof(t) < (1 << _IOC_SIZEBITS)) ? \
++ sizeof(t) : __invalid_size_argument_for_IOC)
++#else
++#define _IOC_TYPECHECK(t) (sizeof(t))
++#endif
++
+ #define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
+ #define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
+ #define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
diff --git a/linux-2.6.30-no-pcspkr-modalias.patch b/linux-2.6.30-no-pcspkr-modalias.patch
new file mode 100644
index 000000000..c703b8844
--- /dev/null
+++ b/linux-2.6.30-no-pcspkr-modalias.patch
@@ -0,0 +1,11 @@
+diff -up linux-2.6.30.noarch/drivers/input/misc/pcspkr.c.jx linux-2.6.30.noarch/drivers/input/misc/pcspkr.c
+--- linux-2.6.30.noarch/drivers/input/misc/pcspkr.c.jx 2009-07-28 16:54:44.000000000 -0400
++++ linux-2.6.30.noarch/drivers/input/misc/pcspkr.c 2009-07-28 16:59:36.000000000 -0400
+@@ -23,7 +23,6 @@
+ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+ MODULE_DESCRIPTION("PC Speaker beeper driver");
+ MODULE_LICENSE("GPL");
+-MODULE_ALIAS("platform:pcspkr");
+
+ #if defined(CONFIG_MIPS) || defined(CONFIG_X86)
+ /* Use the global PIT lock ! */
diff --git a/lirc-staging-2.6.36.patch b/lirc-staging-2.6.36.patch
new file mode 100644
index 000000000..360ac45ab
--- /dev/null
+++ b/lirc-staging-2.6.36.patch
@@ -0,0 +1,12190 @@
+ drivers/staging/Kconfig | 2 +
+ drivers/staging/Makefile | 1 +
+ drivers/staging/lirc/Kconfig | 110 +++
+ drivers/staging/lirc/Makefile | 19 +
+ drivers/staging/lirc/TODO | 8 +
+ drivers/staging/lirc/lirc_bt829.c | 383 +++++++++
+ drivers/staging/lirc/lirc_ene0100.c | 646 ++++++++++++++
+ drivers/staging/lirc/lirc_ene0100.h | 169 ++++
+ drivers/staging/lirc/lirc_i2c.c | 536 ++++++++++++
+ drivers/staging/lirc/lirc_igorplugusb.c | 555 ++++++++++++
+ drivers/staging/lirc/lirc_imon.c | 1058 +++++++++++++++++++++++
+ drivers/staging/lirc/lirc_it87.c | 1019 +++++++++++++++++++++++
+ drivers/staging/lirc/lirc_it87.h | 116 +++
+ drivers/staging/lirc/lirc_ite8709.c | 542 ++++++++++++
+ drivers/staging/lirc/lirc_parallel.c | 705 ++++++++++++++++
+ drivers/staging/lirc/lirc_parallel.h | 26 +
+ drivers/staging/lirc/lirc_sasem.c | 933 +++++++++++++++++++++
+ drivers/staging/lirc/lirc_serial.c | 1313 +++++++++++++++++++++++++++++
+ drivers/staging/lirc/lirc_sir.c | 1282 ++++++++++++++++++++++++++++
+ drivers/staging/lirc/lirc_streamzap.c | 821 ++++++++++++++++++
+ drivers/staging/lirc/lirc_ttusbir.c | 397 +++++++++
+ drivers/staging/lirc/lirc_zilog.c | 1387 +++++++++++++++++++++++++++++++
+ 22 files changed, 12028 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
+index 984a754..9296517 100644
+--- a/drivers/staging/Kconfig
++++ b/drivers/staging/Kconfig
+@@ -147,5 +147,7 @@ source "drivers/staging/mrst-touchscreen/Kconfig"
+
+ source "drivers/staging/msm/Kconfig"
+
++source "drivers/staging/lirc/Kconfig"
++
+ endif # !STAGING_EXCLUDE_BUILD
+ endif # STAGING
+diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
+index 9fa2513..9e9b068 100644
+--- a/drivers/staging/Makefile
++++ b/drivers/staging/Makefile
+@@ -54,3 +54,4 @@ obj-$(CONFIG_ADIS16255) += adis16255/
+ obj-$(CONFIG_FB_XGI) += xgifb/
+ obj-$(CONFIG_TOUCHSCREEN_MRSTOUCH) += mrst-touchscreen/
+ obj-$(CONFIG_MSM_STAGING) += msm/
++obj-$(CONFIG_LIRC_STAGING) += lirc/
+diff --git a/drivers/staging/lirc/Kconfig b/drivers/staging/lirc/Kconfig
+new file mode 100644
+index 0000000..968c2ad
+--- /dev/null
++++ b/drivers/staging/lirc/Kconfig
+@@ -0,0 +1,110 @@
++#
++# LIRC driver(s) configuration
++#
++menuconfig LIRC_STAGING
++ bool "Linux Infrared Remote Control IR receiver/transmitter drivers"
++ help
++ Say Y here, and all supported Linux Infrared Remote Control IR and
++ RF receiver and transmitter drivers will be displayed. When paired
++ with a remote control and the lirc daemon, the receiver drivers
++ allow control of your Linux system via remote control.
++
++if LIRC_STAGING
++
++config LIRC_BT829
++ tristate "BT829 based hardware"
++ depends on LIRC_STAGING
++ help
++ Driver for the IR interface on BT829-based hardware
++
++config LIRC_ENE0100
++ tristate "ENE KB3924/ENE0100 CIR Port Reciever"
++ depends on LIRC_STAGING
++ help
++ This is a driver for CIR port handled by ENE KB3924 embedded
++ controller found on some notebooks.
++ It appears on PNP list as ENE0100.
++
++config LIRC_I2C
++ tristate "I2C Based IR Receivers"
++ depends on LIRC_STAGING
++ help
++ Driver for I2C-based IR receivers, such as those commonly
++ found onboard Hauppauge PVR-150/250/350 video capture cards
++
++config LIRC_IGORPLUGUSB
++ tristate "Igor Cesko's USB IR Receiver"
++ depends on LIRC_STAGING && USB
++ help
++ Driver for Igor Cesko's USB IR Receiver
++
++config LIRC_IMON
++ tristate "Legacy SoundGraph iMON Receiver and Display"
++ depends on LIRC_STAGING
++ help
++ Driver for the original SoundGraph iMON IR Receiver and Display
++
++ Current generation iMON devices use the input layer imon driver.
++
++config LIRC_IT87
++ tristate "ITE IT87XX CIR Port Receiver"
++ depends on LIRC_STAGING
++ help
++ Driver for the ITE IT87xx IR Receiver
++
++config LIRC_ITE8709
++ tristate "ITE8709 CIR Port Receiver"
++ depends on LIRC_STAGING && PNP
++ help
++ Driver for the ITE8709 IR Receiver
++
++config LIRC_PARALLEL
++ tristate "Homebrew Parallel Port Receiver"
++ depends on LIRC_STAGING && !SMP
++ help
++ Driver for Homebrew Parallel Port Receivers
++
++config LIRC_SASEM
++ tristate "Sasem USB IR Remote"
++ depends on LIRC_STAGING
++ help
++ Driver for the Sasem OnAir Remocon-V or Dign HV5 HTPC IR/VFD Module
++
++config LIRC_SERIAL
++ tristate "Homebrew Serial Port Receiver"
++ depends on LIRC_STAGING
++ help
++ Driver for Homebrew Serial Port Receivers
++
++config LIRC_SERIAL_TRANSMITTER
++ bool "Serial Port Transmitter"
++ default y
++ depends on LIRC_SERIAL
++ help
++ Serial Port Transmitter support
++
++config LIRC_SIR
++ tristate "Built-in SIR IrDA port"
++ depends on LIRC_STAGING
++ help
++ Driver for the SIR IrDA port
++
++config LIRC_STREAMZAP
++ tristate "Streamzap PC Receiver"
++ depends on LIRC_STAGING
++ help
++ Driver for the Streamzap PC Receiver
++
++config LIRC_TTUSBIR
++ tristate "Technotrend USB IR Receiver"
++ depends on LIRC_STAGING && USB
++ help
++ Driver for the Technotrend USB IR Receiver
++
++config LIRC_ZILOG
++ tristate "Zilog/Hauppauge IR Transmitter"
++ depends on LIRC_STAGING
++ help
++ Driver for the Zilog/Hauppauge IR Transmitter, found on
++ PVR-150/500, HVR-1200/1250/1700/1800, HD-PVR and other cards
++endif
+diff --git a/drivers/staging/lirc/Makefile b/drivers/staging/lirc/Makefile
+new file mode 100644
+index 0000000..a019182
+--- /dev/null
++++ b/drivers/staging/lirc/Makefile
+@@ -0,0 +1,19 @@
++# Makefile for the lirc drivers.
++#
++
++# Each configuration option enables a list of files.
++
++obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o
++obj-$(CONFIG_LIRC_ENE0100) += lirc_ene0100.o
++obj-$(CONFIG_LIRC_I2C) += lirc_i2c.o
++obj-$(CONFIG_LIRC_IGORPLUGUSB) += lirc_igorplugusb.o
++obj-$(CONFIG_LIRC_IMON) += lirc_imon.o
++obj-$(CONFIG_LIRC_IT87) += lirc_it87.o
++obj-$(CONFIG_LIRC_ITE8709) += lirc_ite8709.o
++obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o
++obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o
++obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o
++obj-$(CONFIG_LIRC_SIR) += lirc_sir.o
++obj-$(CONFIG_LIRC_STREAMZAP) += lirc_streamzap.o
++obj-$(CONFIG_LIRC_TTUSBIR) += lirc_ttusbir.o
++obj-$(CONFIG_LIRC_ZILOG) += lirc_zilog.o
+diff --git a/drivers/staging/lirc/TODO b/drivers/staging/lirc/TODO
+new file mode 100644
+index 0000000..b6cb593
+--- /dev/null
++++ b/drivers/staging/lirc/TODO
+@@ -0,0 +1,8 @@
++- All drivers should either be ported to ir-core, or dropped entirely
++ (see drivers/media/IR/mceusb.c vs. lirc_mceusb.c in lirc cvs for an
++ example of a previously completed port).
++
++Please send patches to:
++Jarod Wilson <jarod@wilsonet.com>
++Greg Kroah-Hartman <greg@kroah.com>
++
+diff --git a/drivers/staging/lirc/lirc_bt829.c b/drivers/staging/lirc/lirc_bt829.c
+new file mode 100644
+index 0000000..d0f34b5
+--- /dev/null
++++ b/drivers/staging/lirc/lirc_bt829.c
+@@ -0,0 +1,383 @@
++/*
++ * Remote control driver for the TV-card based on bt829
++ *
++ * by Leonid Froenchenko <lfroen@galileo.co.il>
++ *
++ * 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/kernel.h>
++#include <linux/module.h>
++#include <linux/threads.h>
++#include <linux/sched.h>
++#include <linux/ioport.h>
++#include <linux/pci.h>
++#include <linux/delay.h>
++
++#include <media/lirc_dev.h>
++
++static int poll_main(void);
++static int atir_init_start(void);
++
++static void write_index(unsigned char index, unsigned int value);
++static unsigned int read_index(unsigned char index);
++
++static void do_i2c_start(void);
++static void do_i2c_stop(void);
++
++static void seems_wr_byte(unsigned char al);
++static unsigned char seems_rd_byte(void);
++
++static unsigned int read_index(unsigned char al);
++static void write_index(unsigned char ah, unsigned int edx);
++
++static void cycle_delay(int cycle);
++
++static void do_set_bits(unsigned char bl);
++static unsigned char do_get_bits(void);
++
++#define DATA_PCI_OFF 0x7FFC00
++#define WAIT_CYCLE 20
++
++#define DRIVER_NAME "lirc_bt829"
++
++static int debug;
++#define dprintk(fmt, args...) \
++ do { \
++ if (debug) \
++ printk(KERN_DEBUG DRIVER_NAME ": "fmt, ## args); \
++ } while (0)
++
++static int atir_minor;
++static unsigned long pci_addr_phys;
++static unsigned char *pci_addr_lin;
++
++static struct lirc_driver atir_driver;
++
++static struct pci_dev *do_pci_probe(void)
++{
++ struct pci_dev *my_dev;
++ my_dev = pci_get_device(PCI_VENDOR_ID_ATI,
++ PCI_DEVICE_ID_ATI_264VT, NULL);
++ if (my_dev) {
++ printk(KERN_ERR DRIVER_NAME ": Using device: %s\n",
++ pci_name(my_dev));
++ pci_addr_phys = 0;
++ if (my_dev->resource[0].flags & IORESOURCE_MEM) {
++ pci_addr_phys = my_dev->resource[0].start;
++ printk(KERN_INFO DRIVER_NAME ": memory at 0x%08X \n",
++ (unsigned int)pci_addr_phys);
++ }
++ if (pci_addr_phys == 0) {
++ printk(KERN_ERR DRIVER_NAME ": no memory resource ?\n");
++ return NULL;
++ }
++ } else {
++ printk(KERN_ERR DRIVER_NAME ": pci_probe failed\n");
++ return NULL;
++ }
++ return my_dev;
++}
++
++static int atir_add_to_buf(void *data, struct lirc_buffer *buf)
++{
++ unsigned char key;
++ int status;
++ status = poll_main();
++ key = (status >> 8) & 0xFF;
++ if (status & 0xFF) {
++ dprintk("reading key %02X\n", key);
++ lirc_buffer_write(buf, &key);
++ return 0;
++ }
++ return -ENODATA;
++}
++
++static int atir_set_use_inc(void *data)
++{
++ dprintk("driver is opened\n");
++ return 0;
++}
++
++static void atir_set_use_dec(void *data)
++{
++ dprintk("driver is closed\n");
++}
++
++int init_module(void)
++{
++ struct pci_dev *pdev;
++
++ pdev = do_pci_probe();
++ if (pdev == NULL)
++ return 1;
++
++ if (!atir_init_start())
++ return 1;
++
++ strcpy(atir_driver.name, "ATIR");
++ atir_driver.minor = -1;
++ atir_driver.code_length = 8;
++ atir_driver.sample_rate = 10;
++ atir_driver.data = 0;
++ atir_driver.add_to_buf = atir_add_to_buf;
++ atir_driver.set_use_inc = atir_set_use_inc;
++ atir_driver.set_use_dec = atir_set_use_dec;
++ atir_driver.dev = &pdev->dev;
++ atir_driver.owner = THIS_MODULE;
++
++ atir_minor = lirc_register_driver(&atir_driver);
++ if (atir_minor < 0) {
++ printk(KERN_ERR DRIVER_NAME ": failed to register driver!\n");
++ return atir_minor;
++ }
++ dprintk("driver is registered on minor %d\n", atir_minor);
++
++ return 0;
++}
++
++
++void cleanup_module(void)
++{
++ lirc_unregister_driver(atir_minor);
++}
++
++
++static int atir_init_start(void)
++{
++ pci_addr_lin = ioremap(pci_addr_phys + DATA_PCI_OFF, 0x400);
++ if (pci_addr_lin == 0) {
++ printk(KERN_INFO DRIVER_NAME ": pci mem must be mapped\n");
++ return 0;
++ }
++ return 1;
++}
++
++static void cycle_delay(int cycle)
++{
++ udelay(WAIT_CYCLE*cycle);
++}
++
++
++static int poll_main()
++{
++ unsigned char status_high, status_low;
++
++ do_i2c_start();
++
++ seems_wr_byte(0xAA);
++ seems_wr_byte(0x01);
++
++ do_i2c_start();
++
++ seems_wr_byte(0xAB);
++
++ status_low = seems_rd_byte();
++ status_high = seems_rd_byte();
++
++ do_i2c_stop();
++
++ return (status_high << 8) | status_low;
++}
++
++static void do_i2c_start(void)
++{
++ do_set_bits(3);
++ cycle_delay(4);
++
++ do_set_bits(1);
++ cycle_delay(7);
++
++ do_set_bits(0);
++ cycle_delay(2);
++}
++
++static void do_i2c_stop(void)
++{
++ unsigned char bits;
++ bits = do_get_bits() & 0xFD;
++ do_set_bits(bits);
++ cycle_delay(1);
++
++ bits |= 1;
++ do_set_bits(bits);
++ cycle_delay(2);
++
++ bits |= 2;
++ do_set_bits(bits);
++ bits = 3;
++ do_set_bits(bits);
++ cycle_delay(2);
++}
++
++static void seems_wr_byte(unsigned char value)
++{
++ int i;
++ unsigned char reg;
++
++ reg = do_get_bits();
++ for (i = 0; i < 8; i++) {
++ if (value & 0x80)
++ reg |= 0x02;
++ else
++ reg &= 0xFD;
++
++ do_set_bits(reg);
++ cycle_delay(1);
++
++ reg |= 1;
++ do_set_bits(reg);
++ cycle_delay(1);
++
++ reg &= 0xFE;
++ do_set_bits(reg);
++ cycle_delay(1);
++ value <<= 1;
++ }
++ cycle_delay(2);
++
++ reg |= 2;
++ do_set_bits(reg);
++
++ reg |= 1;
++ do_set_bits(reg);
++
++ cycle_delay(1);
++ do_get_bits();
++
++ reg &= 0xFE;
++ do_set_bits(reg);
++ cycle_delay(3);
++}
++
++static unsigned char seems_rd_byte(void)
++{
++ int i;
++ int rd_byte;
++ unsigned char bits_2, bits_1;
++
++ bits_1 = do_get_bits() | 2;
++ do_set_bits(bits_1);
++
++ rd_byte = 0;
++ for (i = 0; i < 8; i++) {
++ bits_1 &= 0xFE;
++ do_set_bits(bits_1);
++ cycle_delay(2);
++
++ bits_1 |= 1;
++ do_set_bits(bits_1);
++ cycle_delay(1);
++
++ bits_2 = do_get_bits();
++ if (bits_2 & 2)
++ rd_byte |= 1;
++
++ rd_byte <<= 1;
++ }
++
++ bits_1 = 0;
++ if (bits_2 == 0)
++ bits_1 |= 2;
++
++ do_set_bits(bits_1);
++ cycle_delay(2);
++
++ bits_1 |= 1;
++ do_set_bits(bits_1);
++ cycle_delay(3);
++
++ bits_1 &= 0xFE;
++ do_set_bits(bits_1);
++ cycle_delay(2);
++
++ rd_byte >>= 1;
++ rd_byte &= 0xFF;
++ return rd_byte;
++}
++
++static void do_set_bits(unsigned char new_bits)
++{
++ int reg_val;
++ reg_val = read_index(0x34);
++ if (new_bits & 2) {
++ reg_val &= 0xFFFFFFDF;
++ reg_val |= 1;
++ } else {
++ reg_val &= 0xFFFFFFFE;
++ reg_val |= 0x20;
++ }
++ reg_val |= 0x10;
++ write_index(0x34, reg_val);
++
++ reg_val = read_index(0x31);
++ if (new_bits & 1)
++ reg_val |= 0x1000000;
++ else
++ reg_val &= 0xFEFFFFFF;
++
++ reg_val |= 0x8000000;
++ write_index(0x31, reg_val);
++}
++
++static unsigned char do_get_bits(void)
++{
++ unsigned char bits;
++ int reg_val;
++
++ reg_val = read_index(0x34);
++ reg_val |= 0x10;
++ reg_val &= 0xFFFFFFDF;
++ write_index(0x34, reg_val);
++
++ reg_val = read_index(0x34);
++ bits = 0;
++ if (reg_val & 8)
++ bits |= 2;
++ else
++ bits &= 0xFD;
++
++ reg_val = read_index(0x31);
++ if (reg_val & 0x1000000)
++ bits |= 1;
++ else
++ bits &= 0xFE;
++
++ return bits;
++}
++
++static unsigned int read_index(unsigned char index)
++{
++ unsigned char *addr;
++ unsigned int value;
++ /* addr = pci_addr_lin + DATA_PCI_OFF + ((index & 0xFF) << 2); */
++ addr = pci_addr_lin + ((index & 0xFF) << 2);
++ value = readl(addr);
++ return value;
++}
++
++static void write_index(unsigned char index, unsigned int reg_val)
++{
++ unsigned char *addr;
++ addr = pci_addr_lin + ((index & 0xFF) << 2);
++ writel(reg_val, addr);
++}
++
++MODULE_AUTHOR("Froenchenko Leonid");
++MODULE_DESCRIPTION("IR remote driver for bt829 based TV cards");
++MODULE_LICENSE("GPL");
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Debug enabled or not");
+diff --git a/drivers/staging/lirc/lirc_ene0100.c b/drivers/staging/lirc/lirc_ene0100.c
+new file mode 100644
+index 0000000..a152c52
+--- /dev/null
++++ b/drivers/staging/lirc/lirc_ene0100.c
+@@ -0,0 +1,646 @@
++/*
++ * driver for ENE KB3926 B/C/D CIR (also known as ENE0100)
++ *
++ * Copyright (C) 2009 Maxim Levitsky <maximlevitsky@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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
++ * USA
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/pnp.h>
++#include <linux/io.h>
++#include <linux/interrupt.h>
++#include <linux/sched.h>
++#include "lirc_ene0100.h"
++
++static int sample_period = 75;
++static int enable_idle = 1;
++static int enable_learning;
++
++static void ene_set_idle(struct ene_device *dev, int idle);
++static void ene_set_inputs(struct ene_device *dev, int enable);
++
++/* read a hardware register */
++static u8 ene_hw_read_reg(struct ene_device *dev, u16 reg)
++{
++ outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
++ outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
++ return inb(dev->hw_io + ENE_IO);
++}
++
++/* write a hardware register */
++static void ene_hw_write_reg(struct ene_device *dev, u16 reg, u8 value)
++{
++ outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
++ outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
++ outb(value, dev->hw_io + ENE_IO);
++}
++
++/* change specific bits in hardware register */
++static void ene_hw_write_reg_mask(struct ene_device *dev,
++ u16 reg, u8 value, u8 mask)
++{
++ u8 regvalue;
++
++ outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
++ outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
++
++ regvalue = inb(dev->hw_io + ENE_IO) & ~mask;
++ regvalue |= (value & mask);
++ outb(regvalue, dev->hw_io + ENE_IO);
++}
++
++/* read irq status and ack it */
++static int ene_hw_irq_status(struct ene_device *dev, int *buffer_pointer)
++{
++ u8 irq_status;
++ u8 fw_flags1, fw_flags2;
++
++ fw_flags2 = ene_hw_read_reg(dev, ENE_FW2);
++
++ if (buffer_pointer)
++ *buffer_pointer = 4 * (fw_flags2 & ENE_FW2_BUF_HIGH);
++
++ if (dev->hw_revision < ENE_HW_C) {
++ irq_status = ene_hw_read_reg(dev, ENEB_IRQ_STATUS);
++
++ if (!(irq_status & ENEB_IRQ_STATUS_IR))
++ return 0;
++ ene_hw_write_reg(dev, ENEB_IRQ_STATUS,
++ irq_status & ~ENEB_IRQ_STATUS_IR);
++
++ /* rev B support only recieving */
++ return ENE_IRQ_RX;
++ }
++
++ irq_status = ene_hw_read_reg(dev, ENEC_IRQ);
++
++ if (!(irq_status & ENEC_IRQ_STATUS))
++ return 0;
++
++ /* original driver does that twice - a workaround ? */
++ ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
++ ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
++
++ /* clear unknown flag in F8F9 */
++ if (fw_flags2 & ENE_FW2_IRQ_CLR)
++ ene_hw_write_reg(dev, ENE_FW2, fw_flags2 & ~ENE_FW2_IRQ_CLR);
++
++ /* check if this is a TX interrupt */
++ fw_flags1 = ene_hw_read_reg(dev, ENE_FW1);
++
++ if (fw_flags1 & ENE_FW1_TXIRQ) {
++ ene_hw_write_reg(dev, ENE_FW1, fw_flags1 & ~ENE_FW1_TXIRQ);
++ return ENE_IRQ_TX;
++ } else
++ return ENE_IRQ_RX;
++}
++
++static int ene_hw_detect(struct ene_device *dev)
++{
++ u8 chip_major, chip_minor;
++ u8 hw_revision, old_ver;
++ u8 tmp;
++ u8 fw_capabilities;
++
++ tmp = ene_hw_read_reg(dev, ENE_HW_UNK);
++ ene_hw_write_reg(dev, ENE_HW_UNK, tmp & ~ENE_HW_UNK_CLR);
++
++ chip_major = ene_hw_read_reg(dev, ENE_HW_VER_MAJOR);
++ chip_minor = ene_hw_read_reg(dev, ENE_HW_VER_MINOR);
++
++ ene_hw_write_reg(dev, ENE_HW_UNK, tmp);
++ hw_revision = ene_hw_read_reg(dev, ENE_HW_VERSION);
++ old_ver = ene_hw_read_reg(dev, ENE_HW_VER_OLD);
++
++ if (hw_revision == 0xFF) {
++
++ ene_printk(KERN_WARNING, "device seems to be disabled\n");
++ ene_printk(KERN_WARNING,
++ "send a mail to lirc-list@lists.sourceforge.net\n");
++ ene_printk(KERN_WARNING, "please attach output of acpidump\n");
++
++ return -ENODEV;
++ }
++
++ if (chip_major == 0x33) {
++ ene_printk(KERN_WARNING, "chips 0x33xx aren't supported yet\n");
++ return -ENODEV;
++ }
++
++ if (chip_major == 0x39 && chip_minor == 0x26 && hw_revision == 0xC0) {
++ dev->hw_revision = ENE_HW_C;
++ ene_printk(KERN_WARNING,
++ "KB3926C detected, driver support is not complete!\n");
++
++ } else if (old_ver == 0x24 && hw_revision == 0xC0) {
++ dev->hw_revision = ENE_HW_B;
++ ene_printk(KERN_NOTICE, "KB3926B detected\n");
++ } else {
++ dev->hw_revision = ENE_HW_D;
++ ene_printk(KERN_WARNING,
++ "unknown ENE chip detected, assuming KB3926D\n");
++ ene_printk(KERN_WARNING, "driver support incomplete");
++
++ }
++
++ ene_printk(KERN_DEBUG, "chip is 0x%02x%02x - 0x%02x, 0x%02x\n",
++ chip_major, chip_minor, old_ver, hw_revision);
++
++
++ /* detect features hardware supports */
++
++ if (dev->hw_revision < ENE_HW_C)
++ return 0;
++
++ fw_capabilities = ene_hw_read_reg(dev, ENE_FW2);
++
++ dev->hw_gpio40_learning = fw_capabilities & ENE_FW2_GP40_AS_LEARN;
++ dev->hw_learning_and_tx_capable = fw_capabilities & ENE_FW2_LEARNING;
++
++ dev->hw_fan_as_normal_input = dev->hw_learning_and_tx_capable &&
++ fw_capabilities & ENE_FW2_FAN_AS_NRML_IN;
++
++ ene_printk(KERN_NOTICE, "hardware features:\n");
++ ene_printk(KERN_NOTICE,
++ "learning and tx %s, gpio40_learn %s, fan_in %s\n",
++ dev->hw_learning_and_tx_capable ? "on" : "off",
++ dev->hw_gpio40_learning ? "on" : "off",
++ dev->hw_fan_as_normal_input ? "on" : "off");
++
++ if (!dev->hw_learning_and_tx_capable && enable_learning)
++ enable_learning = 0;
++
++ if (dev->hw_learning_and_tx_capable) {
++ ene_printk(KERN_WARNING,
++ "Device supports transmitting, but the driver doesn't\n");
++ ene_printk(KERN_WARNING,
++ "due to lack of hardware to test against.\n");
++ ene_printk(KERN_WARNING,
++ "Send a mail to: lirc-list@lists.sourceforge.net\n");
++ }
++ return 0;
++}
++
++/* hardware initialization */
++static int ene_hw_init(void *data)
++{
++ u8 reg_value;
++ struct ene_device *dev = (struct ene_device *)data;
++ dev->in_use = 1;
++
++ if (dev->hw_revision < ENE_HW_C) {
++ ene_hw_write_reg(dev, ENEB_IRQ, dev->irq << 1);
++ ene_hw_write_reg(dev, ENEB_IRQ_UNK1, 0x01);
++ } else {
++ reg_value = ene_hw_read_reg(dev, ENEC_IRQ) & 0xF0;
++ reg_value |= ENEC_IRQ_UNK_EN;
++ reg_value &= ~ENEC_IRQ_STATUS;
++ reg_value |= (dev->irq & ENEC_IRQ_MASK);
++ ene_hw_write_reg(dev, ENEC_IRQ, reg_value);
++ ene_hw_write_reg(dev, ENE_TX_UNK1, 0x63);
++ }
++
++ ene_hw_write_reg(dev, ENE_CIR_CONF2, 0x00);
++ ene_set_inputs(dev, enable_learning);
++
++ /* set sampling period */
++ ene_hw_write_reg(dev, ENE_CIR_SAMPLE_PERIOD, sample_period);
++
++ /* ack any pending irqs - just in case */
++ ene_hw_irq_status(dev, NULL);
++
++ /* enter idle mode */
++ ene_set_idle(dev, 1);
++
++ /* enable firmware bits */
++ ene_hw_write_reg_mask(dev, ENE_FW1,
++ ENE_FW1_ENABLE | ENE_FW1_IRQ,
++ ENE_FW1_ENABLE | ENE_FW1_IRQ);
++ /* clear stats */
++ dev->sample = 0;
++ return 0;
++}
++
++/* this enables gpio40 signal, used if connected to wide band input*/
++static void ene_enable_gpio40(struct ene_device *dev, int enable)
++{
++ ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, enable ?
++ 0 : ENE_CIR_CONF2_GPIO40DIS,
++ ENE_CIR_CONF2_GPIO40DIS);
++}
++
++/* this enables the classic sampler */
++static void ene_enable_normal_recieve(struct ene_device *dev, int enable)
++{
++ ene_hw_write_reg(dev, ENE_CIR_CONF1, enable ? ENE_CIR_CONF1_ADC_ON : 0);
++}
++
++/* this enables recieve via fan input */
++static void ene_enable_fan_recieve(struct ene_device *dev, int enable)
++{
++ if (!enable)
++ ene_hw_write_reg(dev, ENE_FAN_AS_IN1, 0);
++ else {
++ ene_hw_write_reg(dev, ENE_FAN_AS_IN1, ENE_FAN_AS_IN1_EN);
++ ene_hw_write_reg(dev, ENE_FAN_AS_IN2, ENE_FAN_AS_IN2_EN);
++ }
++ dev->fan_input_inuse = enable;
++}
++
++/* determine which input to use*/
++static void ene_set_inputs(struct ene_device *dev, int learning_enable)
++{
++ ene_enable_normal_recieve(dev, 1);
++
++ /* old hardware doesn't support learning mode for sure */
++ if (dev->hw_revision <= ENE_HW_B)
++ return;
++
++ /* reciever not learning capable, still set gpio40 correctly */
++ if (!dev->hw_learning_and_tx_capable) {
++ ene_enable_gpio40(dev, !dev->hw_gpio40_learning);
++ return;
++ }
++
++ /* enable learning mode */
++ if (learning_enable) {
++ ene_enable_gpio40(dev, dev->hw_gpio40_learning);
++
++ /* fan input is not used for learning */
++ if (dev->hw_fan_as_normal_input)
++ ene_enable_fan_recieve(dev, 0);
++
++ /* disable learning mode */
++ } else {
++ if (dev->hw_fan_as_normal_input) {
++ ene_enable_fan_recieve(dev, 1);
++ ene_enable_normal_recieve(dev, 0);
++ } else
++ ene_enable_gpio40(dev, !dev->hw_gpio40_learning);
++ }
++
++ /* set few additional settings for this mode */
++ ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, learning_enable ?
++ ENE_CIR_CONF1_LEARN1 : 0, ENE_CIR_CONF1_LEARN1);
++
++ ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, learning_enable ?
++ ENE_CIR_CONF2_LEARN2 : 0, ENE_CIR_CONF2_LEARN2);
++}
++
++/* deinitialization */
++static void ene_hw_deinit(void *data)
++{
++ struct ene_device *dev = (struct ene_device *)data;
++
++ /* disable samplers */
++ ene_enable_normal_recieve(dev, 0);
++
++ if (dev->hw_fan_as_normal_input)
++ ene_enable_fan_recieve(dev, 0);
++
++ /* disable hardware IRQ and firmware flag */
++ ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_ENABLE | ENE_FW1_IRQ);
++
++ ene_set_idle(dev, 1);
++ dev->in_use = 0;
++}
++
++/* sends current sample to userspace */
++static void send_sample(struct ene_device *dev)
++{
++ int value = abs(dev->sample) & PULSE_MASK;
++
++ if (dev->sample > 0)
++ value |= PULSE_BIT;
++
++ if (!lirc_buffer_full(dev->lirc_driver->rbuf)) {
++ lirc_buffer_write(dev->lirc_driver->rbuf, (void *)&value);
++ wake_up(&dev->lirc_driver->rbuf->wait_poll);
++ }
++ dev->sample = 0;
++}
++
++/* this updates current sample */
++static void update_sample(struct ene_device *dev, int sample)
++{
++ if (!dev->sample)
++ dev->sample = sample;
++ else if (same_sign(dev->sample, sample))
++ dev->sample += sample;
++ else {
++ send_sample(dev);
++ dev->sample = sample;
++ }
++}
++
++/* enable or disable idle mode */
++static void ene_set_idle(struct ene_device *dev, int idle)
++{
++ struct timeval now;
++ int disable = idle && enable_idle && (dev->hw_revision < ENE_HW_C);
++
++ ene_hw_write_reg_mask(dev, ENE_CIR_SAMPLE_PERIOD,
++ disable ? 0 : ENE_CIR_SAMPLE_OVERFLOW,
++ ENE_CIR_SAMPLE_OVERFLOW);
++ dev->idle = idle;
++
++ /* remember when we have entered the idle mode */
++ if (idle) {
++ do_gettimeofday(&dev->gap_start);
++ return;
++ }
++
++ /* send the gap between keypresses now */
++ do_gettimeofday(&now);
++
++ if (now.tv_sec - dev->gap_start.tv_sec > 16)
++ dev->sample = space(PULSE_MASK);
++ else
++ dev->sample = dev->sample +
++ space(1000000ull * (now.tv_sec - dev->gap_start.tv_sec))
++ + space(now.tv_usec - dev->gap_start.tv_usec);
++
++ if (abs(dev->sample) > PULSE_MASK)
++ dev->sample = space(PULSE_MASK);
++ send_sample(dev);
++}
++
++/* interrupt handler */
++static irqreturn_t ene_hw_irq(int irq, void *data)
++{
++ u16 hw_value;
++ int i, hw_sample;
++ int space;
++ int buffer_pointer;
++ int irq_status;
++
++ struct ene_device *dev = (struct ene_device *)data;
++ irq_status = ene_hw_irq_status(dev, &buffer_pointer);
++
++ if (!irq_status)
++ return IRQ_NONE;
++
++ /* TODO: only RX for now */
++ if (irq_status == ENE_IRQ_TX)
++ return IRQ_HANDLED;
++
++ for (i = 0; i < ENE_SAMPLES_SIZE; i++) {
++
++ hw_value = ene_hw_read_reg(dev,
++ ENE_SAMPLE_BUFFER + buffer_pointer + i);
++
++ if (dev->fan_input_inuse) {
++ /* read high part of the sample */
++ hw_value |= ene_hw_read_reg(dev,
++ ENE_SAMPLE_BUFFER_FAN + buffer_pointer + i) << 8;
++
++ /* test for _space_ bit */
++ space = !(hw_value & ENE_FAN_SMPL_PULS_MSK);
++
++ /* clear space bit, and other unused bits */
++ hw_value &= ENE_FAN_VALUE_MASK;
++ hw_sample = hw_value * ENE_SAMPLE_PERIOD_FAN;
++
++ } else {
++ space = hw_value & ENE_SAMPLE_SPC_MASK;
++ hw_value &= ENE_SAMPLE_VALUE_MASK;
++ hw_sample = hw_value * sample_period;
++ }
++
++ /* no more data */
++ if (!(hw_value))
++ break;
++
++ if (space)
++ hw_sample *= -1;
++
++ /* overflow sample recieved, handle it */
++
++ if (!dev->fan_input_inuse && hw_value == ENE_SAMPLE_OVERFLOW) {
++
++ if (dev->idle)
++ continue;
++
++ if (dev->sample > 0 || abs(dev->sample) <= ENE_MAXGAP)
++ update_sample(dev, hw_sample);
++ else
++ ene_set_idle(dev, 1);
++
++ continue;
++ }
++
++ /* normal first sample recieved */
++ if (!dev->fan_input_inuse && dev->idle) {
++ ene_set_idle(dev, 0);
++
++ /* discard first recieved value, its random
++ since its the time signal was off before
++ first pulse if idle mode is enabled, HW
++ does that for us */
++
++ if (!enable_idle)
++ continue;
++ }
++ update_sample(dev, hw_sample);
++ send_sample(dev);
++ }
++ return IRQ_HANDLED;
++}
++
++static int ene_probe(struct pnp_dev *pnp_dev,
++ const struct pnp_device_id *dev_id)
++{
++ struct ene_device *dev;
++ struct lirc_driver *lirc_driver;
++ int error = -ENOMEM;
++
++ dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL);
++
++ if (!dev)
++ goto err1;
++
++ dev->pnp_dev = pnp_dev;
++ pnp_set_drvdata(pnp_dev, dev);
++
++
++ /* prepare lirc interface */
++ error = -ENOMEM;
++ lirc_driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
++
++ if (!lirc_driver)
++ goto err2;
++
++ dev->lirc_driver = lirc_driver;
++
++ strcpy(lirc_driver->name, ENE_DRIVER_NAME);
++ lirc_driver->minor = -1;
++ lirc_driver->code_length = sizeof(int) * 8;
++ lirc_driver->features = LIRC_CAN_REC_MODE2;
++ lirc_driver->data = dev;
++ lirc_driver->set_use_inc = ene_hw_init;
++ lirc_driver->set_use_dec = ene_hw_deinit;
++ lirc_driver->dev = &pnp_dev->dev;
++ lirc_driver->owner = THIS_MODULE;
++
++ lirc_driver->rbuf = kzalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
++
++ if (!lirc_driver->rbuf)
++ goto err3;
++
++ if (lirc_buffer_init(lirc_driver->rbuf, sizeof(int), sizeof(int) * 256))
++ goto err4;
++
++ error = -ENODEV;
++ if (lirc_register_driver(lirc_driver))
++ goto err5;
++
++ /* validate resources */
++ if (!pnp_port_valid(pnp_dev, 0) ||
++ pnp_port_len(pnp_dev, 0) < ENE_MAX_IO)
++ goto err6;
++
++ if (!pnp_irq_valid(pnp_dev, 0))
++ goto err6;
++
++ dev->hw_io = pnp_port_start(pnp_dev, 0);
++ dev->irq = pnp_irq(pnp_dev, 0);
++
++ /* claim the resources */
++ error = -EBUSY;
++ if (!request_region(dev->hw_io, ENE_MAX_IO, ENE_DRIVER_NAME))
++ goto err6;
++
++ if (request_irq(dev->irq, ene_hw_irq,
++ IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev))
++ goto err7;
++
++ /* detect hardware version and features */
++ error = ene_hw_detect(dev);
++ if (error)
++ goto err8;
++
++ ene_printk(KERN_NOTICE, "driver has been succesfully loaded\n");
++ return 0;
++
++err8:
++ free_irq(dev->irq, dev);
++err7:
++ release_region(dev->hw_io, ENE_MAX_IO);
++err6:
++ lirc_unregister_driver(lirc_driver->minor);
++err5:
++ lirc_buffer_free(lirc_driver->rbuf);
++err4:
++ kfree(lirc_driver->rbuf);
++err3:
++ kfree(lirc_driver);
++err2:
++ kfree(dev);
++err1:
++ return error;
++}
++
++static void ene_remove(struct pnp_dev *pnp_dev)
++{
++ struct ene_device *dev = pnp_get_drvdata(pnp_dev);
++ ene_hw_deinit(dev);
++ free_irq(dev->irq, dev);
++ release_region(dev->hw_io, ENE_MAX_IO);
++ lirc_unregister_driver(dev->lirc_driver->minor);
++ lirc_buffer_free(dev->lirc_driver->rbuf);
++ kfree(dev->lirc_driver);
++ kfree(dev);
++}
++
++#ifdef CONFIG_PM
++
++/* TODO: make 'wake on IR' configurable and add .shutdown */
++/* currently impossible due to lack of kernel support */
++
++static int ene_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
++{
++ struct ene_device *dev = pnp_get_drvdata(pnp_dev);
++ ene_hw_write_reg_mask(dev, ENE_FW1, ENE_FW1_WAKE, ENE_FW1_WAKE);
++ return 0;
++}
++
++static int ene_resume(struct pnp_dev *pnp_dev)
++{
++ struct ene_device *dev = pnp_get_drvdata(pnp_dev);
++ if (dev->in_use)
++ ene_hw_init(dev);
++
++ ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_WAKE);
++ return 0;
++}
++
++#endif
++
++static const struct pnp_device_id ene_ids[] = {
++ {.id = "ENE0100",},
++ {},
++};
++
++static struct pnp_driver ene_driver = {
++ .name = ENE_DRIVER_NAME,
++ .id_table = ene_ids,
++ .flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
++
++ .probe = ene_probe,
++ .remove = __devexit_p(ene_remove),
++
++#ifdef CONFIG_PM
++ .suspend = ene_suspend,
++ .resume = ene_resume,
++#endif
++};
++
++static int __init ene_init(void)
++{
++ if (sample_period < 5) {
++ ene_printk(KERN_ERR, "sample period must be at\n");
++ ene_printk(KERN_ERR, "least 5 us, (at least 30 recommended)\n");
++ return -EINVAL;
++ }
++ return pnp_register_driver(&ene_driver);
++}
++
++static void ene_exit(void)
++{
++ pnp_unregister_driver(&ene_driver);
++}
++
++module_param(sample_period, int, S_IRUGO);
++MODULE_PARM_DESC(sample_period, "Hardware sample period (75 us default)");
++
++module_param(enable_idle, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(enable_idle,
++ "Enables turning off signal sampling after long inactivity time; "
++ "if disabled might help detecting input signal (default: enabled)");
++
++module_param(enable_learning, bool, S_IRUGO);
++MODULE_PARM_DESC(enable_learning, "Use wide band (learning) reciever");
++
++MODULE_DEVICE_TABLE(pnp, ene_ids);
++MODULE_DESCRIPTION
++ ("LIRC driver for KB3926B/KB3926C/KB3926D (aka ENE0100) CIR port");
++MODULE_AUTHOR("Maxim Levitsky");
++MODULE_LICENSE("GPL");
++
++module_init(ene_init);
++module_exit(ene_exit);
+diff --git a/drivers/staging/lirc/lirc_ene0100.h b/drivers/staging/lirc/lirc_ene0100.h
+new file mode 100644
+index 0000000..825045d
+--- /dev/null
++++ b/drivers/staging/lirc/lirc_ene0100.h
+@@ -0,0 +1,169 @@
++/*
++ * driver for ENE KB3926 B/C/D CIR (also known as ENE0100)
++ *
++ * Copyright (C) 2009 Maxim Levitsky <maximlevitsky@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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
++ * USA
++ */
++
++#include <media/lirc.h>
++#include <media/lirc_dev.h>
++
++/* hardware address */
++#define ENE_STATUS 0 /* hardware status - unused */
++#define ENE_ADDR_HI 1 /* hi byte of register address */
++#define ENE_ADDR_LO 2 /* low byte of register address */
++#define ENE_IO 3 /* read/write window */
++#define ENE_MAX_IO 4
++
++/* 8 bytes of samples, divided in 2 halfs*/
++#define ENE_SAMPLE_BUFFER 0xF8F0 /* regular sample buffer */
++#define ENE_SAMPLE_SPC_MASK (1 << 7) /* sample is space */
++#define ENE_SAMPLE_VALUE_MASK 0x7F
++#define ENE_SAMPLE_OVERFLOW 0x7F
++#define ENE_SAMPLES_SIZE 4
++
++/* fan input sample buffer */
++#define ENE_SAMPLE_BUFFER_FAN 0xF8FB /* this buffer holds high byte of */
++ /* each sample of normal buffer */
++
++#define ENE_FAN_SMPL_PULS_MSK 0x8000 /* this bit of combined sample */
++ /* if set, says that sample is pulse */
++#define ENE_FAN_VALUE_MASK 0x0FFF /* mask for valid bits of the value */
++
++/* first firmware register */
++#define ENE_FW1 0xF8F8
++#define ENE_FW1_ENABLE (1 << 0) /* enable fw processing */
++#define ENE_FW1_TXIRQ (1 << 1) /* TX interrupt pending */
++#define ENE_FW1_WAKE (1 << 6) /* enable wake from S3 */
++#define ENE_FW1_IRQ (1 << 7) /* enable interrupt */
++
++/* second firmware register */
++#define ENE_FW2 0xF8F9
++#define ENE_FW2_BUF_HIGH (1 << 0) /* which half of the buffer to read */
++#define ENE_FW2_IRQ_CLR (1 << 2) /* clear this on IRQ */
++#define ENE_FW2_GP40_AS_LEARN (1 << 4) /* normal input is used as */
++ /* learning input */
++#define ENE_FW2_FAN_AS_NRML_IN (1 << 6) /* fan is used as normal input */
++#define ENE_FW2_LEARNING (1 << 7) /* hardware supports learning and TX */
++
++/* fan as input settings - only if learning capable */
++#define ENE_FAN_AS_IN1 0xFE30 /* fan init reg 1 */
++#define ENE_FAN_AS_IN1_EN 0xCD
++#define ENE_FAN_AS_IN2 0xFE31 /* fan init reg 2 */
++#define ENE_FAN_AS_IN2_EN 0x03
++#define ENE_SAMPLE_PERIOD_FAN 61 /* fan input has fixed sample period */
++
++/* IRQ registers block (for revision B) */
++#define ENEB_IRQ 0xFD09 /* IRQ number */
++#define ENEB_IRQ_UNK1 0xFD17 /* unknown setting = 1 */
++#define ENEB_IRQ_STATUS 0xFD80 /* irq status */
++#define ENEB_IRQ_STATUS_IR (1 << 5) /* IR irq */
++
++/* IRQ registers block (for revision C,D) */
++#define ENEC_IRQ 0xFE9B /* new irq settings register */
++#define ENEC_IRQ_MASK 0x0F /* irq number mask */
++#define ENEC_IRQ_UNK_EN (1 << 4) /* always enabled */
++#define ENEC_IRQ_STATUS (1 << 5) /* irq status and ACK */
++
++/* CIR block settings */
++#define ENE_CIR_CONF1 0xFEC0
++#define ENE_CIR_CONF1_ADC_ON 0x7 /* reciever on gpio40 enabled */
++#define ENE_CIR_CONF1_LEARN1 (1 << 3) /* enabled on learning mode */
++#define ENE_CIR_CONF1_TX_ON 0x30 /* enabled on transmit */
++#define ENE_CIR_CONF1_TX_CARR (1 << 7) /* send TX carrier or not */
++
++#define ENE_CIR_CONF2 0xFEC1 /* unknown setting = 0 */
++#define ENE_CIR_CONF2_LEARN2 (1 << 4) /* set on enable learning */
++#define ENE_CIR_CONF2_GPIO40DIS (1 << 5) /* disable normal input via gpio40 */
++
++#define ENE_CIR_SAMPLE_PERIOD 0xFEC8 /* sample period in us */
++#define ENE_CIR_SAMPLE_OVERFLOW (1 << 7) /* interrupt on overflows if set */
++
++
++/* transmitter - not implemented yet */
++/* KB3926C and higher */
++/* transmission is very similiar to recieving, a byte is written to */
++/* ENE_TX_INPUT, in same manner as it is read from sample buffer */
++/* sample period is fixed*/
++
++
++/* transmitter ports */
++#define ENE_TX_PORT1 0xFC01 /* this enables one or both */
++#define ENE_TX_PORT1_EN (1 << 5) /* TX ports */
++#define ENE_TX_PORT2 0xFC08
++#define ENE_TX_PORT2_EN (1 << 1)
++
++#define ENE_TX_INPUT 0xFEC9 /* next byte to transmit */
++#define ENE_TX_SPC_MASK (1 << 7) /* Transmitted sample is space */
++#define ENE_TX_UNK1 0xFECB /* set to 0x63 */
++#define ENE_TX_SMPL_PERIOD 50 /* transmit sample period */
++
++
++#define ENE_TX_CARRIER 0xFECE /* TX carrier * 2 (khz) */
++#define ENE_TX_CARRIER_UNKBIT 0x80 /* This bit set on transmit */
++#define ENE_TX_CARRIER_LOW 0xFECF /* TX carrier / 2 */
++
++/* Hardware versions */
++#define ENE_HW_VERSION 0xFF00 /* hardware revision */
++#define ENE_HW_UNK 0xFF1D
++#define ENE_HW_UNK_CLR (1 << 2)
++#define ENE_HW_VER_MAJOR 0xFF1E /* chip version */
++#define ENE_HW_VER_MINOR 0xFF1F
++#define ENE_HW_VER_OLD 0xFD00
++
++#define same_sign(a, b) ((((a) > 0) && (b) > 0) || ((a) < 0 && (b) < 0))
++
++#define ENE_DRIVER_NAME "enecir"
++#define ENE_MAXGAP 250000 /* this is amount of time we wait
++ before turning the sampler, chosen
++ arbitry */
++
++#define space(len) (-(len)) /* add a space */
++
++/* software defines */
++#define ENE_IRQ_RX 1
++#define ENE_IRQ_TX 2
++
++#define ENE_HW_B 1 /* 3926B */
++#define ENE_HW_C 2 /* 3926C */
++#define ENE_HW_D 3 /* 3926D */
++
++#define ene_printk(level, text, ...) \
++ printk(level ENE_DRIVER_NAME ": " text, ## __VA_ARGS__)
++
++struct ene_device {
++ struct pnp_dev *pnp_dev;
++ struct lirc_driver *lirc_driver;
++
++ /* hw settings */
++ unsigned long hw_io;
++ int irq;
++
++ int hw_revision; /* hardware revision */
++ int hw_learning_and_tx_capable; /* learning capable */
++ int hw_gpio40_learning; /* gpio40 is learning */
++ int hw_fan_as_normal_input; /* fan input is used as regular input */
++
++ /* device data */
++ int idle;
++ int fan_input_inuse;
++
++ int sample;
++ int in_use;
++
++ struct timeval gap_start;
++};
+diff --git a/drivers/staging/lirc/lirc_i2c.c b/drivers/staging/lirc/lirc_i2c.c
+new file mode 100644
+index 0000000..6df2c0e
+--- /dev/null
++++ b/drivers/staging/lirc/lirc_i2c.c
+@@ -0,0 +1,536 @@
++/*
++ * lirc_i2c.c
++ *
++ * i2c IR driver for the onboard IR port on many TV tuner cards, including:
++ * -Flavors of the Hauppauge PVR-150/250/350
++ * -Hauppauge HVR-1300
++ * -PixelView (BT878P+W/FM)
++ * -KNC ONE TV Station/Anubis Typhoon TView Tuner
++ * -Asus TV-Box and Creative/VisionTek BreakOut-Box
++ * -Leadtek Winfast PVR2000
++ *
++ * Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
++ * modified for PixelView (BT878P+W/FM) by
++ * Michal Kochanowicz <mkochano@pld.org.pl>
++ * Christoph Bartelmus <lirc@bartelmus.de>
++ * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by
++ * Ulrich Mueller <ulrich.mueller42@web.de>
++ * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by
++ * Stefan Jahn <stefan@lkcc.org>
++ * modified for inclusion into kernel sources by
++ * Jerome Brock <jbrock@users.sourceforge.net>
++ * modified for Leadtek Winfast PVR2000 by
++ * Thomas Reitmayr (treitmayr@yahoo.com)
++ * modified for Hauppauge HVR-1300 by
++ * Jan Frey (jfrey@gmx.de)
++ *
++ * parts are cut&pasted from the old lirc_haup.c driver
++ *
++ * 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/version.h>
++#include <linux/module.h>
++#include <linux/kmod.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/string.h>
++#include <linux/timer.h>
++#include <linux/delay.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/i2c.h>
++#include <linux/i2c-algo-bit.h>
++
++#include <media/lirc_dev.h>
++
++struct IR {
++ struct lirc_driver l;
++ struct i2c_client c;
++ int nextkey;
++ unsigned char b[3];
++ unsigned char bits;
++ unsigned char flag;
++};
++
++#define DEVICE_NAME "lirc_i2c"
++
++/* module parameters */
++static int debug; /* debug output */
++static int minor = -1; /* minor number */
++
++#define dprintk(fmt, args...) \
++ do { \
++ if (debug) \
++ printk(KERN_DEBUG DEVICE_NAME ": " fmt, \
++ ## args); \
++ } while (0)
++
++static int reverse(int data, int bits)
++{
++ int i;
++ int c;
++
++ for (c = 0, i = 0; i < bits; i++)
++ c |= ((data & (1<<i)) ? 1 : 0) << (bits-1-i);
++
++ return c;
++}
++
++static int add_to_buf_adap(void *data, struct lirc_buffer *buf)
++{
++ struct IR *ir = data;
++ unsigned char keybuf[4];
++
++ keybuf[0] = 0x00;
++ i2c_master_send(&ir->c, keybuf, 1);
++ /* poll IR chip */
++ if (i2c_master_recv(&ir->c, keybuf, sizeof(keybuf)) != sizeof(keybuf)) {
++ dprintk("read error\n");
++ return -EIO;
++ }
++
++ dprintk("key (0x%02x%02x%02x%02x)\n",
++ keybuf[0], keybuf[1], keybuf[2], keybuf[3]);
++
++ /* key pressed ? */
++ if (keybuf[2] == 0xff)
++ return -ENODATA;
++
++ /* remove repeat bit */
++ keybuf[2] &= 0x7f;
++ keybuf[3] |= 0x80;
++
++ lirc_buffer_write(buf, keybuf);
++ return 0;
++}
++
++static int add_to_buf_pcf8574(void *data, struct lirc_buffer *buf)
++{
++ struct IR *ir = data;
++ int rc;
++ unsigned char all, mask;
++ unsigned char key;
++
++ /* compute all valid bits (key code + pressed/release flag) */
++ all = ir->bits | ir->flag;
++
++ /* save IR writable mask bits */
++ mask = i2c_smbus_read_byte(&ir->c) & ~all;
++
++ /* send bit mask */
++ rc = i2c_smbus_write_byte(&ir->c, (0xff & all) | mask);
++
++ /* receive scan code */
++ rc = i2c_smbus_read_byte(&ir->c);
++
++ if (rc == -1) {
++ dprintk("%s read error\n", ir->c.name);
++ return -EIO;
++ }
++
++ /* drop duplicate polls */
++ if (ir->b[0] == (rc & all))
++ return -ENODATA;
++
++ ir->b[0] = rc & all;
++
++ dprintk("%s key 0x%02X %s\n", ir->c.name, rc & ir->bits,
++ (rc & ir->flag) ? "released" : "pressed");
++
++ /* ignore released buttons */
++ if (rc & ir->flag)
++ return -ENODATA;
++
++ /* set valid key code */
++ key = rc & ir->bits;
++ lirc_buffer_write(buf, &key);
++ return 0;
++}
++
++/* common for Hauppauge IR receivers */
++static int add_to_buf_haup_common(void *data, struct lirc_buffer *buf,
++ unsigned char *keybuf, int size, int offset)
++{
++ struct IR *ir = data;
++ __u16 code;
++ unsigned char codes[2];
++ int ret;
++
++ /* poll IR chip */
++ ret = i2c_master_recv(&ir->c, keybuf, size);
++ if (ret == size) {
++ ir->b[0] = keybuf[offset];
++ ir->b[1] = keybuf[offset+1];
++ ir->b[2] = keybuf[offset+2];
++ if (ir->b[0] != 0x00 && ir->b[1] != 0x00)
++ dprintk("key (0x%02x/0x%02x)\n", ir->b[0], ir->b[1]);
++ } else {
++ dprintk("read error (ret=%d)\n", ret);
++ /* keep last successful read buffer */
++ }
++
++ /* key pressed ? */
++ if ((ir->b[0] & 0x80) == 0)
++ return -ENODATA;
++
++ /* look what we have */
++ code = (((__u16)ir->b[0]&0x7f)<<6) | (ir->b[1]>>2);
++
++ codes[0] = (code >> 8) & 0xff;
++ codes[1] = code & 0xff;
++
++ /* return it */
++ dprintk("sending code 0x%02x%02x to lirc\n", codes[0], codes[1]);
++ lirc_buffer_write(buf, codes);
++ return 0;
++}
++
++/* specific for the Hauppauge PVR150 IR receiver */
++static int add_to_buf_haup_pvr150(void *data, struct lirc_buffer *buf)
++{
++ unsigned char keybuf[6];
++ /* fetch 6 bytes, first relevant is at offset 3 */
++ return add_to_buf_haup_common(data, buf, keybuf, 6, 3);
++}
++
++/* used for all Hauppauge IR receivers but the PVR150 */
++static int add_to_buf_haup(void *data, struct lirc_buffer *buf)
++{
++ unsigned char keybuf[3];
++ /* fetch 3 bytes, first relevant is at offset 0 */
++ return add_to_buf_haup_common(data, buf, keybuf, 3, 0);
++}
++
++
++static int add_to_buf_pvr2000(void *data, struct lirc_buffer *buf)
++{
++ struct IR *ir = data;
++ unsigned char key;
++ s32 flags;
++ s32 code;
++
++ /* poll IR chip */
++ flags = i2c_smbus_read_byte_data(&ir->c, 0x10);
++ if (-1 == flags) {
++ dprintk("read error\n");
++ return -ENODATA;
++ }
++ /* key pressed ? */
++ if (0 == (flags & 0x80))
++ return -ENODATA;
++
++ /* read actual key code */
++ code = i2c_smbus_read_byte_data(&ir->c, 0x00);
++ if (-1 == code) {
++ dprintk("read error\n");
++ return -ENODATA;
++ }
++
++ key = code & 0xFF;
++
++ dprintk("IR Key/Flags: (0x%02x/0x%02x)\n", key, flags & 0xFF);
++
++ /* return it */
++ lirc_buffer_write(buf, &key);
++ return 0;
++}
++
++static int add_to_buf_pixelview(void *data, struct lirc_buffer *buf)
++{
++ struct IR *ir = data;
++ unsigned char key;
++
++ /* poll IR chip */
++ if (1 != i2c_master_recv(&ir->c, &key, 1)) {
++ dprintk("read error\n");
++ return -1;
++ }
++ dprintk("key %02x\n", key);
++
++ /* return it */
++ lirc_buffer_write(buf, &key);
++ return 0;
++}
++
++static int add_to_buf_pv951(void *data, struct lirc_buffer *buf)
++{
++ struct IR *ir = data;
++ unsigned char key;
++ unsigned char codes[4];
++
++ /* poll IR chip */
++ if (1 != i2c_master_recv(&ir->c, &key, 1)) {
++ dprintk("read error\n");
++ return -ENODATA;
++ }
++ /* ignore 0xaa */
++ if (key == 0xaa)
++ return -ENODATA;
++ dprintk("key %02x\n", key);
++
++ codes[0] = 0x61;
++ codes[1] = 0xD6;
++ codes[2] = reverse(key, 8);
++ codes[3] = (~codes[2])&0xff;
++
++ lirc_buffer_write(buf, codes);
++ return 0;
++}
++
++static int add_to_buf_knc1(void *data, struct lirc_buffer *buf)
++{
++ static unsigned char last_key = 0xFF;
++ struct IR *ir = data;
++ unsigned char key;
++
++ /* poll IR chip */
++ if (1 != i2c_master_recv(&ir->c, &key, 1)) {
++ dprintk("read error\n");
++ return -ENODATA;
++ }
++
++ /*
++ * it seems that 0xFE indicates that a button is still held
++ * down, while 0xFF indicates that no button is held
++ * down. 0xFE sequences are sometimes interrupted by 0xFF
++ */
++
++ dprintk("key %02x\n", key);
++
++ if (key == 0xFF)
++ return -ENODATA;
++
++ if (key == 0xFE)
++ key = last_key;
++
++ last_key = key;
++ lirc_buffer_write(buf, &key);
++
++ return 0;
++}
++
++static int set_use_inc(void *data)
++{
++ struct IR *ir = data;
++
++ dprintk("%s called\n", __func__);
++
++ /* lock bttv in memory while /dev/lirc is in use */
++ i2c_use_client(&ir->c);
++
++ return 0;
++}
++
++static void set_use_dec(void *data)
++{
++ struct IR *ir = data;
++
++ dprintk("%s called\n", __func__);
++
++ i2c_release_client(&ir->c);
++}
++
++static struct lirc_driver lirc_template = {
++ .name = "lirc_i2c",
++ .set_use_inc = set_use_inc,
++ .set_use_dec = set_use_dec,
++ .dev = NULL,
++ .owner = THIS_MODULE,
++};
++
++static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id);
++static int ir_remove(struct i2c_client *client);
++static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg);
++
++static const struct i2c_device_id ir_receiver_id[] = {
++ /* Generic entry for any IR receiver */
++ { "ir_video", 0 },
++ /* IR device specific entries could be added here */
++ { }
++};
++
++static struct i2c_driver driver = {
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = "i2c ir driver",
++ },
++ .probe = ir_probe,
++ .remove = ir_remove,
++ .id_table = ir_receiver_id,
++ .command = ir_command,
++};
++
++static void pcf_probe(struct i2c_client *client, struct IR *ir)
++{
++ int ret1, ret2, ret3, ret4;
++
++ ret1 = i2c_smbus_write_byte(client, 0xff);
++ ret2 = i2c_smbus_read_byte(client);
++ ret3 = i2c_smbus_write_byte(client, 0x00);
++ ret4 = i2c_smbus_read_byte(client);
++
++ /* in the Asus TV-Box: bit 1-0 */
++ if (((ret2 & 0x03) == 0x03) && ((ret4 & 0x03) == 0x00)) {
++ ir->bits = (unsigned char) ~0x07;
++ ir->flag = 0x04;
++ /* in the Creative/VisionTek BreakOut-Box: bit 7-6 */
++ } else if (((ret2 & 0xc0) == 0xc0) && ((ret4 & 0xc0) == 0x00)) {
++ ir->bits = (unsigned char) ~0xe0;
++ ir->flag = 0x20;
++ }
++
++ return;
++}
++
++static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
++{
++ struct IR *ir;
++ struct i2c_adapter *adap = client->adapter;
++ unsigned short addr = client->addr;
++ int retval;
++
++ ir = kzalloc(sizeof(struct IR), GFP_KERNEL);
++ if (!ir)
++ return -ENOMEM;
++ memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver));
++ memcpy(&ir->c, client, sizeof(struct i2c_client));
++
++ i2c_set_clientdata(client, ir);
++ ir->l.data = ir;
++ ir->l.minor = minor;
++ ir->l.sample_rate = 10;
++ ir->l.dev = &ir->c.dev;
++ ir->nextkey = -1;
++
++ switch (addr) {
++ case 0x64:
++ strlcpy(ir->c.name, "Pixelview IR", I2C_NAME_SIZE);
++ ir->l.code_length = 8;
++ ir->l.add_to_buf = add_to_buf_pixelview;
++ break;
++ case 0x4b:
++ strlcpy(ir->c.name, "PV951 IR", I2C_NAME_SIZE);
++ ir->l.code_length = 32;
++ ir->l.add_to_buf = add_to_buf_pv951;
++ break;
++ case 0x71:
++ if (adap->id == I2C_HW_B_CX2388x)
++ strlcpy(ir->c.name, "Hauppauge HVR1300", I2C_NAME_SIZE);
++ else /* bt8xx or cx2341x */
++ /*
++ * The PVR150 IR receiver uses the same protocol as
++ * other Hauppauge cards, but the data flow is
++ * different, so we need to deal with it by its own.
++ */
++ strlcpy(ir->c.name, "Hauppauge PVR150", I2C_NAME_SIZE);
++ ir->l.code_length = 13;
++ ir->l.add_to_buf = add_to_buf_haup_pvr150;
++ break;
++ case 0x6b:
++ strlcpy(ir->c.name, "Adaptec IR", I2C_NAME_SIZE);
++ ir->l.code_length = 32;
++ ir->l.add_to_buf = add_to_buf_adap;
++ break;
++ case 0x18:
++ case 0x1a:
++ if (adap->id == I2C_HW_B_CX2388x) {
++ strlcpy(ir->c.name, "Leadtek IR", I2C_NAME_SIZE);
++ ir->l.code_length = 8;
++ ir->l.add_to_buf = add_to_buf_pvr2000;
++ } else { /* bt8xx or cx2341x */
++ strlcpy(ir->c.name, "Hauppauge IR", I2C_NAME_SIZE);
++ ir->l.code_length = 13;
++ ir->l.add_to_buf = add_to_buf_haup;
++ }
++ break;
++ case 0x30:
++ strlcpy(ir->c.name, "KNC ONE IR", I2C_NAME_SIZE);
++ ir->l.code_length = 8;
++ ir->l.add_to_buf = add_to_buf_knc1;
++ break;
++ case 0x21:
++ case 0x23:
++ pcf_probe(client, ir);
++ strlcpy(ir->c.name, "TV-Box IR", I2C_NAME_SIZE);
++ ir->l.code_length = 8;
++ ir->l.add_to_buf = add_to_buf_pcf8574;
++ break;
++ default:
++ /* shouldn't happen */
++ printk("lirc_i2c: Huh? unknown i2c address (0x%02x)?\n", addr);
++ kfree(ir);
++ return -EINVAL;
++ }
++ printk(KERN_INFO "lirc_i2c: chip 0x%x found @ 0x%02x (%s)\n",
++ adap->id, addr, ir->c.name);
++
++ retval = lirc_register_driver(&ir->l);
++
++ if (retval < 0) {
++ printk(KERN_ERR "lirc_i2c: failed to register driver!\n");
++ kfree(ir);
++ return retval;
++ }
++
++ ir->l.minor = retval;
++
++ return 0;
++}
++
++static int ir_remove(struct i2c_client *client)
++{
++ struct IR *ir = i2c_get_clientdata(client);
++
++ /* unregister device */
++ lirc_unregister_driver(ir->l.minor);
++
++ /* free memory */
++ kfree(ir);
++ return 0;
++}
++
++static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg)
++{
++ /* nothing */
++ return 0;
++}
++
++static int __init lirc_i2c_init(void)
++{
++ i2c_add_driver(&driver);
++ return 0;
++}
++
++static void __exit lirc_i2c_exit(void)
++{
++ i2c_del_driver(&driver);
++}
++
++MODULE_DESCRIPTION("Infrared receiver driver for Hauppauge and "
++ "Pixelview cards (i2c stack)");
++MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, "
++ "Ulrich Mueller, Stefan Jahn, Jerome Brock");
++MODULE_LICENSE("GPL");
++
++module_param(minor, int, S_IRUGO);
++MODULE_PARM_DESC(minor, "Preferred minor device number");
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Enable debugging messages");
++
++module_init(lirc_i2c_init);
++module_exit(lirc_i2c_exit);
+diff --git a/drivers/staging/lirc/lirc_igorplugusb.c b/drivers/staging/lirc/lirc_igorplugusb.c
+new file mode 100644
+index 0000000..bce600e
+--- /dev/null
++++ b/drivers/staging/lirc/lirc_igorplugusb.c
+@@ -0,0 +1,555 @@
++/*
++ * lirc_igorplugusb - USB remote support for LIRC
++ *
++ * Supports the standard homebrew IgorPlugUSB receiver with Igor's firmware.
++ * See http://www.cesko.host.sk/IgorPlugUSB/IgorPlug-USB%20(AVR)_eng.htm
++ *
++ * The device can only record bursts of up to 36 pulses/spaces.
++ * Works fine with RC5. Longer commands lead to device buffer overrun.
++ * (Maybe a better firmware or a microcontroller with more ram can help?)
++ *
++ * Version 0.1 [beta status]
++ *
++ * Copyright (C) 2004 Jan M. Hochstein
++ * <hochstein@algo.informatik.tu-darmstadt.de>
++ *
++ * This driver was derived from:
++ * Paul Miller <pmiller9@users.sourceforge.net>
++ * "lirc_atiusb" module
++ * Vladimir Dergachev <volodya@minspring.com>'s 2002
++ * "USB ATI Remote support" (input device)
++ * Adrian Dewhurst <sailor-lk@sailorfrag.net>'s 2002
++ * "USB StreamZap remote driver" (LIRC)
++ * Artur Lipowski <alipowski@kki.net.pl>'s 2002
++ * "lirc_dev" and "lirc_gpio" LIRC modules
++ */
++
++/*
++ * 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/module.h>
++#include <linux/kernel.h>
++#include <linux/kmod.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/fs.h>
++#include <linux/usb.h>
++#include <linux/time.h>
++
++#include <media/lirc.h>
++#include <media/lirc_dev.h>
++
++
++/* module identification */
++#define DRIVER_VERSION "0.1"
++#define DRIVER_AUTHOR \
++ "Jan M. Hochstein <hochstein@algo.informatik.tu-darmstadt.de>"
++#define DRIVER_DESC "USB remote driver for LIRC"
++#define DRIVER_NAME "lirc_igorplugusb"
++
++/* debugging support */
++#ifdef CONFIG_USB_DEBUG
++static int debug = 1;
++#else
++static int debug;
++#endif
++
++#define dprintk(fmt, args...) \
++ do { \
++ if (debug) \
++ printk(KERN_DEBUG fmt, ## args); \
++ } while (0)
++
++/* One mode2 pulse/space has 4 bytes. */
++#define CODE_LENGTH sizeof(int)
++
++/* Igor's firmware cannot record bursts longer than 36. */
++#define DEVICE_BUFLEN 36
++
++/*
++ * Header at the beginning of the device's buffer:
++ * unsigned char data_length
++ * unsigned char data_start (!=0 means ring-buffer overrun)
++ * unsigned char counter (incremented by each burst)
++ */
++#define DEVICE_HEADERLEN 3
++
++/* This is for the gap */
++#define ADDITIONAL_LIRC_BYTES 2
++
++/* times to poll per second */
++#define SAMPLE_RATE 100
++static int sample_rate = SAMPLE_RATE;
++
++
++/**** Igor's USB Request Codes */
++
++#define SET_INFRABUFFER_EMPTY 1
++/**
++ * Params: none
++ * Answer: empty
++ */
++
++#define GET_INFRACODE 2
++/**
++ * Params:
++ * wValue: offset to begin reading infra buffer
++ *
++ * Answer: infra data
++ */
++
++#define SET_DATAPORT_DIRECTION 3
++/**
++ * Params:
++ * wValue: (byte) 1 bit for each data port pin (0=in, 1=out)
++ *
++ * Answer: empty
++ */
++
++#define GET_DATAPORT_DIRECTION 4
++/**
++ * Params: none
++ *
++ * Answer: (byte) 1 bit for each data port pin (0=in, 1=out)
++ */
++
++#define SET_OUT_DATAPORT 5
++/**
++ * Params:
++ * wValue: byte to write to output data port
++ *
++ * Answer: empty
++ */
++
++#define GET_OUT_DATAPORT 6
++/**
++ * Params: none
++ *
++ * Answer: least significant 3 bits read from output data port
++ */
++
++#define GET_IN_DATAPORT 7
++/**
++ * Params: none
++ *
++ * Answer: least significant 3 bits read from input data port
++ */
++
++#define READ_EEPROM 8
++/**
++ * Params:
++ * wValue: offset to begin reading EEPROM
++ *
++ * Answer: EEPROM bytes
++ */
++
++#define WRITE_EEPROM 9
++/**
++ * Params:
++ * wValue: offset to EEPROM byte
++ * wIndex: byte to write
++ *
++ * Answer: empty
++ */
++
++#define SEND_RS232 10
++/**
++ * Params:
++ * wValue: byte to send
++ *
++ * Answer: empty
++ */
++
++#define RECV_RS232 11
++/**
++ * Params: none
++ *
++ * Answer: byte received
++ */
++
++#define SET_RS232_BAUD 12
++/**
++ * Params:
++ * wValue: byte to write to UART bit rate register (UBRR)
++ *
++ * Answer: empty
++ */
++
++#define GET_RS232_BAUD 13
++/**
++ * Params: none
++ *
++ * Answer: byte read from UART bit rate register (UBRR)
++ */
++
++
++/* data structure for each usb remote */
++struct igorplug {
++
++ /* usb */
++ struct usb_device *usbdev;
++ struct urb *urb_in;
++ int devnum;
++
++ unsigned char *buf_in;
++ unsigned int len_in;
++ int in_space;
++ struct timeval last_time;
++
++ dma_addr_t dma_in;
++
++ /* lirc */
++ struct lirc_driver *d;
++
++ /* handle sending (init strings) */
++ int send_flags;
++ wait_queue_head_t wait_out;
++};
++
++static int unregister_from_lirc(struct igorplug *ir)
++{
++ struct lirc_driver *d = ir->d;
++ int devnum;
++
++ if (!ir->d)
++ return -EINVAL;
++
++ devnum = ir->devnum;
++ dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum);
++
++ lirc_unregister_driver(d->minor);
++
++ printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum);
++
++ kfree(d);
++ ir->d = NULL;
++ kfree(ir);
++ return 0;
++}
++
++static int set_use_inc(void *data)
++{
++ struct igorplug *ir = data;
++
++ if (!ir) {
++ printk(DRIVER_NAME "[?]: set_use_inc called with no context\n");
++ return -EIO;
++ }
++ dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum);
++
++ if (!ir->usbdev)
++ return -ENODEV;
++
++ return 0;
++}
++
++static void set_use_dec(void *data)
++{
++ struct igorplug *ir = data;
++
++ if (!ir) {
++ printk(DRIVER_NAME "[?]: set_use_dec called with no context\n");
++ return;
++ }
++ dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum);
++}
++
++
++/**
++ * Called in user context.
++ * return 0 if data was added to the buffer and
++ * -ENODATA if none was available. This should add some number of bits
++ * evenly divisible by code_length to the buffer
++ */
++static int usb_remote_poll(void *data, struct lirc_buffer *buf)
++{
++ int ret;
++ struct igorplug *ir = (struct igorplug *)data;
++
++ if (!ir->usbdev) /* Has the device been removed? */
++ return -ENODEV;
++
++ memset(ir->buf_in, 0, ir->len_in);
++
++ ret = usb_control_msg(
++ ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
++ GET_INFRACODE, USB_TYPE_VENDOR|USB_DIR_IN,
++ 0/* offset */, /*unused*/0,
++ ir->buf_in, ir->len_in,
++ /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
++ if (ret > 0) {
++ int i = DEVICE_HEADERLEN;
++ int code, timediff;
++ struct timeval now;
++
++ if (ret <= 1) /* ACK packet has 1 byte --> ignore */
++ return -ENODATA;
++
++ dprintk(DRIVER_NAME ": Got %d bytes. Header: %02x %02x %02x\n",
++ ret, ir->buf_in[0], ir->buf_in[1], ir->buf_in[2]);
++
++ if (ir->buf_in[2] != 0) {
++ printk(DRIVER_NAME "[%d]: Device buffer overrun.\n",
++ ir->devnum);
++ /* start at earliest byte */
++ i = DEVICE_HEADERLEN + ir->buf_in[2];
++ /* where are we now? space, gap or pulse? */
++ }
++
++ do_gettimeofday(&now);
++ timediff = now.tv_sec - ir->last_time.tv_sec;
++ if (timediff + 1 > PULSE_MASK / 1000000)
++ timediff = PULSE_MASK;
++ else {
++ timediff *= 1000000;
++ timediff += now.tv_usec - ir->last_time.tv_usec;
++ }
++ ir->last_time.tv_sec = now.tv_sec;
++ ir->last_time.tv_usec = now.tv_usec;
++
++ /* create leading gap */
++ code = timediff;
++ lirc_buffer_write(buf, (unsigned char *)&code);
++ ir->in_space = 1; /* next comes a pulse */
++
++ /* MODE2: pulse/space (PULSE_BIT) in 1us units */
++
++ while (i < ret) {
++ /* 1 Igor-tick = 85.333333 us */
++ code = (unsigned int)ir->buf_in[i] * 85
++ + (unsigned int)ir->buf_in[i] / 3;
++ if (ir->in_space)
++ code |= PULSE_BIT;
++ lirc_buffer_write(buf, (unsigned char *)&code);
++ /* 1 chunk = CODE_LENGTH bytes */
++ ir->in_space ^= 1;
++ ++i;
++ }
++
++ ret = usb_control_msg(
++ ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
++ SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN,
++ /*unused*/0, /*unused*/0,
++ /*dummy*/ir->buf_in, /*dummy*/ir->len_in,
++ /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
++ if (ret < 0)
++ printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: "
++ "error %d\n", ir->devnum, ret);
++ return 0;
++ } else if (ret < 0)
++ printk(DRIVER_NAME "[%d]: GET_INFRACODE: error %d\n",
++ ir->devnum, ret);
++
++ return -ENODATA;
++}
++
++
++
++static int usb_remote_probe(struct usb_interface *intf,
++ const struct usb_device_id *id)
++{
++ struct usb_device *dev = NULL;
++ struct usb_host_interface *idesc = NULL;
++ struct usb_host_endpoint *ep_ctl2;
++ struct igorplug *ir = NULL;
++ struct lirc_driver *driver = NULL;
++ int devnum, pipe, maxp;
++ int minor = 0;
++ char buf[63], name[128] = "";
++ int mem_failure = 0;
++ int ret;
++
++ dprintk(DRIVER_NAME ": usb probe called.\n");
++
++ dev = interface_to_usbdev(intf);
++
++ idesc = intf->cur_altsetting;
++
++ if (idesc->desc.bNumEndpoints != 1)
++ return -ENODEV;
++ ep_ctl2 = idesc->endpoint;
++ if (((ep_ctl2->desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK)
++ != USB_DIR_IN)
++ || (ep_ctl2->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
++ != USB_ENDPOINT_XFER_CONTROL)
++ return -ENODEV;
++ pipe = usb_rcvctrlpipe(dev, ep_ctl2->desc.bEndpointAddress);
++ devnum = dev->devnum;
++ maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
++
++ dprintk(DRIVER_NAME "[%d]: bytes_in_key=%lu maxp=%d\n",
++ devnum, CODE_LENGTH, maxp);
++
++
++ mem_failure = 0;
++ ir = kzalloc(sizeof(struct igorplug), GFP_KERNEL);
++ if (!ir) {
++ mem_failure = 1;
++ goto mem_failure_switch;
++ }
++ driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
++ if (!driver) {
++ mem_failure = 2;
++ goto mem_failure_switch;
++ }
++
++ ir->buf_in = usb_alloc_coherent(dev,
++ DEVICE_BUFLEN+DEVICE_HEADERLEN,
++ GFP_ATOMIC, &ir->dma_in);
++ if (!ir->buf_in) {
++ mem_failure = 3;
++ goto mem_failure_switch;
++ }
++
++ strcpy(driver->name, DRIVER_NAME " ");
++ driver->minor = -1;
++ driver->code_length = CODE_LENGTH * 8; /* in bits */
++ driver->features = LIRC_CAN_REC_MODE2;
++ driver->data = ir;
++ driver->chunk_size = CODE_LENGTH;
++ driver->buffer_size = DEVICE_BUFLEN + ADDITIONAL_LIRC_BYTES;
++ driver->set_use_inc = &set_use_inc;
++ driver->set_use_dec = &set_use_dec;
++ driver->sample_rate = sample_rate; /* per second */
++ driver->add_to_buf = &usb_remote_poll;
++ driver->dev = &intf->dev;
++ driver->owner = THIS_MODULE;
++
++ init_waitqueue_head(&ir->wait_out);
++
++ minor = lirc_register_driver(driver);
++ if (minor < 0)
++ mem_failure = 9;
++
++mem_failure_switch:
++
++ switch (mem_failure) {
++ case 9:
++ usb_free_coherent(dev, DEVICE_BUFLEN+DEVICE_HEADERLEN,
++ ir->buf_in, ir->dma_in);
++ case 3:
++ kfree(driver);
++ case 2:
++ kfree(ir);
++ case 1:
++ printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n",
++ devnum, mem_failure);
++ return -ENOMEM;
++ }
++
++ driver->minor = minor;
++ ir->d = driver;
++ ir->devnum = devnum;
++ ir->usbdev = dev;
++ ir->len_in = DEVICE_BUFLEN+DEVICE_HEADERLEN;
++ ir->in_space = 1; /* First mode2 event is a space. */
++ do_gettimeofday(&ir->last_time);
++
++ if (dev->descriptor.iManufacturer
++ && usb_string(dev, dev->descriptor.iManufacturer,
++ buf, sizeof(buf)) > 0)
++ strlcpy(name, buf, sizeof(name));
++ if (dev->descriptor.iProduct
++ && usb_string(dev, dev->descriptor.iProduct, buf, sizeof(buf)) > 0)
++ snprintf(name + strlen(name), sizeof(name) - strlen(name),
++ " %s", buf);
++ printk(DRIVER_NAME "[%d]: %s on usb%d:%d\n", devnum, name,
++ dev->bus->busnum, devnum);
++
++ /* clear device buffer */
++ ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
++ SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN,
++ /*unused*/0, /*unused*/0,
++ /*dummy*/ir->buf_in, /*dummy*/ir->len_in,
++ /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
++ if (ret < 0)
++ printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: error %d\n",
++ devnum, ret);
++
++ usb_set_intfdata(intf, ir);
++ return 0;
++}
++
++
++static void usb_remote_disconnect(struct usb_interface *intf)
++{
++ struct usb_device *dev = interface_to_usbdev(intf);
++ struct igorplug *ir = usb_get_intfdata(intf);
++ usb_set_intfdata(intf, NULL);
++
++ if (!ir || !ir->d)
++ return;
++
++ ir->usbdev = NULL;
++ wake_up_all(&ir->wait_out);
++
++ usb_free_coherent(dev, ir->len_in, ir->buf_in, ir->dma_in);
++
++ unregister_from_lirc(ir);
++}
++
++static struct usb_device_id usb_remote_id_table[] = {
++ /* Igor Plug USB (Atmel's Manufact. ID) */
++ { USB_DEVICE(0x03eb, 0x0002) },
++
++ /* Terminating entry */
++ { }
++};
++
++static struct usb_driver usb_remote_driver = {
++ .name = DRIVER_NAME,
++ .probe = usb_remote_probe,
++ .disconnect = usb_remote_disconnect,
++ .id_table = usb_remote_id_table
++};
++
++static int __init usb_remote_init(void)
++{
++ int i;
++
++ printk(KERN_INFO "\n"
++ DRIVER_NAME ": " DRIVER_DESC " v" DRIVER_VERSION "\n");
++ printk(DRIVER_NAME ": " DRIVER_AUTHOR "\n");
++ dprintk(DRIVER_NAME ": debug mode enabled\n");
++
++ i = usb_register(&usb_remote_driver);
++ if (i < 0) {
++ printk(DRIVER_NAME ": usb register failed, result = %d\n", i);
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++static void __exit usb_remote_exit(void)
++{
++ usb_deregister(&usb_remote_driver);
++}
++
++module_init(usb_remote_init);
++module_exit(usb_remote_exit);
++
++#include <linux/vermagic.h>
++MODULE_INFO(vermagic, VERMAGIC_STRING);
++
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_AUTHOR(DRIVER_AUTHOR);
++MODULE_LICENSE("GPL");
++MODULE_DEVICE_TABLE(usb, usb_remote_id_table);
++
++module_param(sample_rate, int, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(sample_rate, "Sampling rate in Hz (default: 100)");
++
+diff --git a/drivers/staging/lirc/lirc_imon.c b/drivers/staging/lirc/lirc_imon.c
+new file mode 100644
+index 0000000..43856d6
+--- /dev/null
++++ b/drivers/staging/lirc/lirc_imon.c
+@@ -0,0 +1,1058 @@
++/*
++ * lirc_imon.c: LIRC/VFD/LCD driver for SoundGraph iMON IR/VFD/LCD
++ * including the iMON PAD model
++ *
++ * Copyright(C) 2004 Venky Raju(dev@venky.ws)
++ * Copyright(C) 2009 Jarod Wilson <jarod@wilsonet.com>
++ *
++ * lirc_imon 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/errno.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/uaccess.h>
++#include <linux/usb.h>
++
++#include <media/lirc.h>
++#include <media/lirc_dev.h>
++
++
++#define MOD_AUTHOR "Venky Raju <dev@venky.ws>"
++#define MOD_DESC "Driver for SoundGraph iMON MultiMedia IR/Display"
++#define MOD_NAME "lirc_imon"
++#define MOD_VERSION "0.8"
++
++#define DISPLAY_MINOR_BASE 144
++#define DEVICE_NAME "lcd%d"
++
++#define BUF_CHUNK_SIZE 4
++#define BUF_SIZE 128
++
++#define BIT_DURATION 250 /* each bit received is 250us */
++
++/*** P R O T O T Y P E S ***/
++
++/* USB Callback prototypes */
++static int imon_probe(struct usb_interface *interface,
++ const struct usb_device_id *id);
++static void imon_disconnect(struct usb_interface *interface);
++static void usb_rx_callback(struct urb *urb);
++static void usb_tx_callback(struct urb *urb);
++
++/* suspend/resume support */
++static int imon_resume(struct usb_interface *intf);
++static int imon_suspend(struct usb_interface *intf, pm_message_t message);
++
++/* Display file_operations function prototypes */
++static int display_open(struct inode *inode, struct file *file);
++static int display_close(struct inode *inode, struct file *file);
++
++/* VFD write operation */
++static ssize_t vfd_write(struct file *file, const char *buf,
++ size_t n_bytes, loff_t *pos);
++
++/* LIRC driver function prototypes */
++static int ir_open(void *data);
++static void ir_close(void *data);
++
++/* Driver init/exit prototypes */
++static int __init imon_init(void);
++static void __exit imon_exit(void);
++
++/*** G L O B A L S ***/
++#define IMON_DATA_BUF_SZ 35
++
++struct imon_context {
++ struct usb_device *usbdev;
++ /* Newer devices have two interfaces */
++ int display; /* not all controllers do */
++ int display_isopen; /* display port has been opened */
++ int ir_isopen; /* IR port open */
++ int dev_present; /* USB device presence */
++ struct mutex ctx_lock; /* to lock this object */
++ wait_queue_head_t remove_ok; /* For unexpected USB disconnects */
++
++ int vfd_proto_6p; /* some VFD require a 6th packet */
++
++ struct lirc_driver *driver;
++ struct usb_endpoint_descriptor *rx_endpoint;
++ struct usb_endpoint_descriptor *tx_endpoint;
++ struct urb *rx_urb;
++ struct urb *tx_urb;
++ unsigned char usb_rx_buf[8];
++ unsigned char usb_tx_buf[8];
++
++ struct rx_data {
++ int count; /* length of 0 or 1 sequence */
++ int prev_bit; /* logic level of sequence */
++ int initial_space; /* initial space flag */
++ } rx;
++
++ struct tx_t {
++ unsigned char data_buf[IMON_DATA_BUF_SZ]; /* user data buffer */
++ struct completion finished; /* wait for write to finish */
++ atomic_t busy; /* write in progress */
++ int status; /* status of tx completion */
++ } tx;
++};
++
++static struct file_operations display_fops = {
++ .owner = THIS_MODULE,
++ .open = &display_open,
++ .write = &vfd_write,
++ .release = &display_close
++};
++
++/*
++ * USB Device ID for iMON USB Control Boards
++ *
++ * The Windows drivers contain 6 different inf files, more or less one for
++ * each new device until the 0x0034-0x0046 devices, which all use the same
++ * driver. Some of the devices in the 34-46 range haven't been definitively
++ * identified yet. Early devices have either a TriGem Computer, Inc. or a
++ * Samsung vendor ID (0x0aa8 and 0x04e8 respectively), while all later
++ * devices use the SoundGraph vendor ID (0x15c2).
++ */
++static struct usb_device_id imon_usb_id_table[] = {
++ /* TriGem iMON (IR only) -- TG_iMON.inf */
++ { USB_DEVICE(0x0aa8, 0x8001) },
++
++ /* SoundGraph iMON (IR only) -- sg_imon.inf */
++ { USB_DEVICE(0x04e8, 0xff30) },
++
++ /* SoundGraph iMON VFD (IR & VFD) -- iMON_VFD.inf */
++ { USB_DEVICE(0x0aa8, 0xffda) },
++
++ /* SoundGraph iMON SS (IR & VFD) -- iMON_SS.inf */
++ { USB_DEVICE(0x15c2, 0xffda) },
++
++ {}
++};
++
++/* Some iMON VFD models requires a 6th packet for VFD writes */
++static struct usb_device_id vfd_proto_6p_list[] = {
++ { USB_DEVICE(0x15c2, 0xffda) },
++ {}
++};
++
++/* Some iMON devices have no lcd/vfd, don't set one up */
++static struct usb_device_id ir_only_list[] = {
++ { USB_DEVICE(0x0aa8, 0x8001) },
++ { USB_DEVICE(0x04e8, 0xff30) },
++ {}
++};
++
++/* USB Device data */
++static struct usb_driver imon_driver = {
++ .name = MOD_NAME,
++ .probe = imon_probe,
++ .disconnect = imon_disconnect,
++ .suspend = imon_suspend,
++ .resume = imon_resume,
++ .id_table = imon_usb_id_table,
++};
++
++static struct usb_class_driver imon_class = {
++ .name = DEVICE_NAME,
++ .fops = &display_fops,
++ .minor_base = DISPLAY_MINOR_BASE,
++};
++
++/* to prevent races between open() and disconnect(), probing, etc */
++static DEFINE_MUTEX(driver_lock);
++
++static int debug;
++
++/*** M O D U L E C O D E ***/
++
++MODULE_AUTHOR(MOD_AUTHOR);
++MODULE_DESCRIPTION(MOD_DESC);
++MODULE_VERSION(MOD_VERSION);
++MODULE_LICENSE("GPL");
++MODULE_DEVICE_TABLE(usb, imon_usb_id_table);
++module_param(debug, int, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes(default: no)");
++
++static void free_imon_context(struct imon_context *context)
++{
++ struct device *dev = context->driver->dev;
++ usb_free_urb(context->tx_urb);
++ usb_free_urb(context->rx_urb);
++ lirc_buffer_free(context->driver->rbuf);
++ kfree(context->driver->rbuf);
++ kfree(context->driver);
++ kfree(context);
++
++ dev_dbg(dev, "%s: iMON context freed\n", __func__);
++}
++
++static void deregister_from_lirc(struct imon_context *context)
++{
++ int retval;
++ int minor = context->driver->minor;
++
++ retval = lirc_unregister_driver(minor);
++ if (retval)
++ err("%s: unable to deregister from lirc(%d)",
++ __func__, retval);
++ else
++ printk(KERN_INFO MOD_NAME ": Deregistered iMON driver "
++ "(minor:%d)\n", minor);
++
++}
++
++/**
++ * Called when the Display device (e.g. /dev/lcd0)
++ * is opened by the application.
++ */
++static int display_open(struct inode *inode, struct file *file)
++{
++ struct usb_interface *interface;
++ struct imon_context *context = NULL;
++ int subminor;
++ int retval = 0;
++
++ /* prevent races with disconnect */
++ mutex_lock(&driver_lock);
++
++ subminor = iminor(inode);
++ interface = usb_find_interface(&imon_driver, subminor);
++ if (!interface) {
++ err("%s: could not find interface for minor %d",
++ __func__, subminor);
++ retval = -ENODEV;
++ goto exit;
++ }
++ context = usb_get_intfdata(interface);
++
++ if (!context) {
++ err("%s: no context found for minor %d",
++ __func__, subminor);
++ retval = -ENODEV;
++ goto exit;
++ }
++
++ mutex_lock(&context->ctx_lock);
++
++ if (!context->display) {
++ err("%s: display not supported by device", __func__);
++ retval = -ENODEV;
++ } else if (context->display_isopen) {
++ err("%s: display port is already open", __func__);
++ retval = -EBUSY;
++ } else {
++ context->display_isopen = 1;
++ file->private_data = context;
++ dev_info(context->driver->dev, "display port opened\n");
++ }
++
++ mutex_unlock(&context->ctx_lock);
++
++exit:
++ mutex_unlock(&driver_lock);
++ return retval;
++}
++
++/**
++ * Called when the display device (e.g. /dev/lcd0)
++ * is closed by the application.
++ */
++static int display_close(struct inode *inode, struct file *file)
++{
++ struct imon_context *context = NULL;
++ int retval = 0;
++
++ context = (struct imon_context *)file->private_data;
++
++ if (!context) {
++ err("%s: no context for device", __func__);
++ return -ENODEV;
++ }
++
++ mutex_lock(&context->ctx_lock);
++
++ if (!context->display) {
++ err("%s: display not supported by device", __func__);
++ retval = -ENODEV;
++ } else if (!context->display_isopen) {
++ err("%s: display is not open", __func__);
++ retval = -EIO;
++ } else {
++ context->display_isopen = 0;
++ dev_info(context->driver->dev, "display port closed\n");
++ if (!context->dev_present && !context->ir_isopen) {
++ /*
++ * Device disconnected before close and IR port is not
++ * open. If IR port is open, context will be deleted by
++ * ir_close.
++ */
++ mutex_unlock(&context->ctx_lock);
++ free_imon_context(context);
++ return retval;
++ }
++ }
++
++ mutex_unlock(&context->ctx_lock);
++ return retval;
++}
++
++/**
++ * Sends a packet to the device -- this function must be called
++ * with context->ctx_lock held.
++ */
++static int send_packet(struct imon_context *context)
++{
++ unsigned int pipe;
++ int interval = 0;
++ int retval = 0;
++ struct usb_ctrlrequest *control_req = NULL;
++
++ /* Check if we need to use control or interrupt urb */
++ pipe = usb_sndintpipe(context->usbdev,
++ context->tx_endpoint->bEndpointAddress);
++ interval = context->tx_endpoint->bInterval;
++
++ usb_fill_int_urb(context->tx_urb, context->usbdev, pipe,
++ context->usb_tx_buf,
++ sizeof(context->usb_tx_buf),
++ usb_tx_callback, context, interval);
++
++ context->tx_urb->actual_length = 0;
++
++ init_completion(&context->tx.finished);
++ atomic_set(&(context->tx.busy), 1);
++
++ retval = usb_submit_urb(context->tx_urb, GFP_KERNEL);
++ if (retval) {
++ atomic_set(&(context->tx.busy), 0);
++ err("%s: error submitting urb(%d)", __func__, retval);
++ } else {
++ /* Wait for transmission to complete (or abort) */
++ mutex_unlock(&context->ctx_lock);
++ retval = wait_for_completion_interruptible(
++ &context->tx.finished);
++ if (retval)
++ err("%s: task interrupted", __func__);
++ mutex_lock(&context->ctx_lock);
++
++ retval = context->tx.status;
++ if (retval)
++ err("%s: packet tx failed (%d)", __func__, retval);
++ }
++
++ kfree(control_req);
++
++ return retval;
++}
++
++/**
++ * Writes data to the VFD. The iMON VFD is 2x16 characters
++ * and requires data in 5 consecutive USB interrupt packets,
++ * each packet but the last carrying 7 bytes.
++ *
++ * I don't know if the VFD board supports features such as
++ * scrolling, clearing rows, blanking, etc. so at
++ * the caller must provide a full screen of data. If fewer
++ * than 32 bytes are provided spaces will be appended to
++ * generate a full screen.
++ */
++static ssize_t vfd_write(struct file *file, const char *buf,
++ size_t n_bytes, loff_t *pos)
++{
++ int i;
++ int offset;
++ int seq;
++ int retval = 0;
++ struct imon_context *context;
++ const unsigned char vfd_packet6[] = {
++ 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF };
++ int *data_buf;
++
++ context = (struct imon_context *)file->private_data;
++ if (!context) {
++ err("%s: no context for device", __func__);
++ return -ENODEV;
++ }
++
++ mutex_lock(&context->ctx_lock);
++
++ if (!context->dev_present) {
++ err("%s: no iMON device present", __func__);
++ retval = -ENODEV;
++ goto exit;
++ }
++
++ if (n_bytes <= 0 || n_bytes > IMON_DATA_BUF_SZ - 3) {
++ err("%s: invalid payload size", __func__);
++ retval = -EINVAL;
++ goto exit;
++ }
++
++ data_buf = memdup_user(buf, n_bytes);
++ if (IS_ERR(data_buf)) {
++ retval = PTR_ERR(data_buf);
++ goto exit;
++ }
++
++ memcpy(context->tx.data_buf, data_buf, n_bytes);
++
++ /* Pad with spaces */
++ for (i = n_bytes; i < IMON_DATA_BUF_SZ - 3; ++i)
++ context->tx.data_buf[i] = ' ';
++
++ for (i = IMON_DATA_BUF_SZ - 3; i < IMON_DATA_BUF_SZ; ++i)
++ context->tx.data_buf[i] = 0xFF;
++
++ offset = 0;
++ seq = 0;
++
++ do {
++ memcpy(context->usb_tx_buf, context->tx.data_buf + offset, 7);
++ context->usb_tx_buf[7] = (unsigned char) seq;
++
++ retval = send_packet(context);
++ if (retval) {
++ err("%s: send packet failed for packet #%d",
++ __func__, seq/2);
++ goto exit;
++ } else {
++ seq += 2;
++ offset += 7;
++ }
++
++ } while (offset < IMON_DATA_BUF_SZ);
++
++ if (context->vfd_proto_6p) {
++ /* Send packet #6 */
++ memcpy(context->usb_tx_buf, &vfd_packet6, sizeof(vfd_packet6));
++ context->usb_tx_buf[7] = (unsigned char) seq;
++ retval = send_packet(context);
++ if (retval)
++ err("%s: send packet failed for packet #%d",
++ __func__, seq/2);
++ }
++
++exit:
++ mutex_unlock(&context->ctx_lock);
++
++ return (!retval) ? n_bytes : retval;
++}
++
++/**
++ * Callback function for USB core API: transmit data
++ */
++static void usb_tx_callback(struct urb *urb)
++{
++ struct imon_context *context;
++
++ if (!urb)
++ return;
++ context = (struct imon_context *)urb->context;
++ if (!context)
++ return;
++
++ context->tx.status = urb->status;
++
++ /* notify waiters that write has finished */
++ atomic_set(&context->tx.busy, 0);
++ complete(&context->tx.finished);
++
++ return;
++}
++
++/**
++ * Called by lirc_dev when the application opens /dev/lirc
++ */
++static int ir_open(void *data)
++{
++ int retval = 0;
++ struct imon_context *context;
++
++ /* prevent races with disconnect */
++ mutex_lock(&driver_lock);
++
++ context = (struct imon_context *)data;
++
++ /* initial IR protocol decode variables */
++ context->rx.count = 0;
++ context->rx.initial_space = 1;
++ context->rx.prev_bit = 0;
++
++ context->ir_isopen = 1;
++ dev_info(context->driver->dev, "IR port opened\n");
++
++ mutex_unlock(&driver_lock);
++ return retval;
++}
++
++/**
++ * Called by lirc_dev when the application closes /dev/lirc
++ */
++static void ir_close(void *data)
++{
++ struct imon_context *context;
++
++ context = (struct imon_context *)data;
++ if (!context) {
++ err("%s: no context for device", __func__);
++ return;
++ }
++
++ mutex_lock(&context->ctx_lock);
++
++ context->ir_isopen = 0;
++ dev_info(context->driver->dev, "IR port closed\n");
++
++ if (!context->dev_present) {
++ /*
++ * Device disconnected while IR port was still open. Driver
++ * was not deregistered at disconnect time, so do it now.
++ */
++ deregister_from_lirc(context);
++
++ if (!context->display_isopen) {
++ mutex_unlock(&context->ctx_lock);
++ free_imon_context(context);
++ return;
++ }
++ /*
++ * If display port is open, context will be deleted by
++ * display_close
++ */
++ }
++
++ mutex_unlock(&context->ctx_lock);
++ return;
++}
++
++/**
++ * Convert bit count to time duration (in us) and submit
++ * the value to lirc_dev.
++ */
++static void submit_data(struct imon_context *context)
++{
++ unsigned char buf[4];
++ int value = context->rx.count;
++ int i;
++
++ dev_dbg(context->driver->dev, "submitting data to LIRC\n");
++
++ value *= BIT_DURATION;
++ value &= PULSE_MASK;
++ if (context->rx.prev_bit)
++ value |= PULSE_BIT;
++
++ for (i = 0; i < 4; ++i)
++ buf[i] = value>>(i*8);
++
++ lirc_buffer_write(context->driver->rbuf, buf);
++ wake_up(&context->driver->rbuf->wait_poll);
++ return;
++}
++
++static inline int tv2int(const struct timeval *a, const struct timeval *b)
++{
++ int usecs = 0;
++ int sec = 0;
++
++ if (b->tv_usec > a->tv_usec) {
++ usecs = 1000000;
++ sec--;
++ }
++
++ usecs += a->tv_usec - b->tv_usec;
++
++ sec += a->tv_sec - b->tv_sec;
++ sec *= 1000;
++ usecs /= 1000;
++ sec += usecs;
++
++ if (sec < 0)
++ sec = 1000;
++
++ return sec;
++}
++
++/**
++ * Process the incoming packet
++ */
++static void imon_incoming_packet(struct imon_context *context,
++ struct urb *urb, int intf)
++{
++ int len = urb->actual_length;
++ unsigned char *buf = urb->transfer_buffer;
++ struct device *dev = context->driver->dev;
++ int octet, bit;
++ unsigned char mask;
++ int i, chunk_num;
++
++ /*
++ * just bail out if no listening IR client
++ */
++ if (!context->ir_isopen)
++ return;
++
++ if (len != 8) {
++ dev_warn(dev, "imon %s: invalid incoming packet "
++ "size (len = %d, intf%d)\n", __func__, len, intf);
++ return;
++ }
++
++ if (debug) {
++ printk(KERN_INFO "raw packet: ");
++ for (i = 0; i < len; ++i)
++ printk("%02x ", buf[i]);
++ printk("\n");
++ }
++
++ /*
++ * Translate received data to pulse and space lengths.
++ * Received data is active low, i.e. pulses are 0 and
++ * spaces are 1.
++ *
++ * My original algorithm was essentially similar to
++ * Changwoo Ryu's with the exception that he switched
++ * the incoming bits to active high and also fed an
++ * initial space to LIRC at the start of a new sequence
++ * if the previous bit was a pulse.
++ *
++ * I've decided to adopt his algorithm.
++ */
++
++ if (buf[7] == 1 && context->rx.initial_space) {
++ /* LIRC requires a leading space */
++ context->rx.prev_bit = 0;
++ context->rx.count = 4;
++ submit_data(context);
++ context->rx.count = 0;
++ }
++
++ for (octet = 0; octet < 5; ++octet) {
++ mask = 0x80;
++ for (bit = 0; bit < 8; ++bit) {
++ int curr_bit = !(buf[octet] & mask);
++ if (curr_bit != context->rx.prev_bit) {
++ if (context->rx.count) {
++ submit_data(context);
++ context->rx.count = 0;
++ }
++ context->rx.prev_bit = curr_bit;
++ }
++ ++context->rx.count;
++ mask >>= 1;
++ }
++ }
++
++ if (chunk_num == 10) {
++ if (context->rx.count) {
++ submit_data(context);
++ context->rx.count = 0;
++ }
++ context->rx.initial_space = context->rx.prev_bit;
++ }
++}
++
++/**
++ * Callback function for USB core API: receive data
++ */
++static void usb_rx_callback(struct urb *urb)
++{
++ struct imon_context *context;
++ unsigned char *buf;
++ int len;
++ int intfnum = 0;
++
++ if (!urb)
++ return;
++
++ context = (struct imon_context *)urb->context;
++ if (!context)
++ return;
++
++ buf = urb->transfer_buffer;
++ len = urb->actual_length;
++
++ switch (urb->status) {
++ case -ENOENT: /* usbcore unlink successful! */
++ return;
++
++ case 0:
++ imon_incoming_packet(context, urb, intfnum);
++ break;
++
++ default:
++ dev_warn(context->driver->dev, "imon %s: status(%d): ignored\n",
++ __func__, urb->status);
++ break;
++ }
++
++ usb_submit_urb(context->rx_urb, GFP_ATOMIC);
++
++ return;
++}
++
++/**
++ * Callback function for USB core API: Probe
++ */
++static int imon_probe(struct usb_interface *interface,
++ const struct usb_device_id *id)
++{
++ struct usb_device *usbdev = NULL;
++ struct usb_host_interface *iface_desc = NULL;
++ struct usb_endpoint_descriptor *rx_endpoint = NULL;
++ struct usb_endpoint_descriptor *tx_endpoint = NULL;
++ struct urb *rx_urb = NULL;
++ struct urb *tx_urb = NULL;
++ struct lirc_driver *driver = NULL;
++ struct lirc_buffer *rbuf = NULL;
++ struct device *dev = &interface->dev;
++ int ifnum;
++ int lirc_minor = 0;
++ int num_endpts;
++ int retval = 0;
++ int display_ep_found = 0;
++ int ir_ep_found = 0;
++ int alloc_status = 0;
++ int vfd_proto_6p = 0;
++ int code_length;
++ struct imon_context *context = NULL;
++ int i;
++ u16 vendor, product;
++
++ context = kzalloc(sizeof(struct imon_context), GFP_KERNEL);
++ if (!context) {
++ err("%s: kzalloc failed for context", __func__);
++ alloc_status = 1;
++ goto alloc_status_switch;
++ }
++
++ /*
++ * Try to auto-detect the type of display if the user hasn't set
++ * it by hand via the display_type modparam. Default is VFD.
++ */
++ if (usb_match_id(interface, ir_only_list))
++ context->display = 0;
++ else
++ context->display = 1;
++
++ code_length = BUF_CHUNK_SIZE * 8;
++
++ usbdev = usb_get_dev(interface_to_usbdev(interface));
++ iface_desc = interface->cur_altsetting;
++ num_endpts = iface_desc->desc.bNumEndpoints;
++ ifnum = iface_desc->desc.bInterfaceNumber;
++ vendor = le16_to_cpu(usbdev->descriptor.idVendor);
++ product = le16_to_cpu(usbdev->descriptor.idProduct);
++
++ dev_dbg(dev, "%s: found iMON device (%04x:%04x, intf%d)\n",
++ __func__, vendor, product, ifnum);
++
++ /* prevent races probing devices w/multiple interfaces */
++ mutex_lock(&driver_lock);
++
++ /*
++ * Scan the endpoint list and set:
++ * first input endpoint = IR endpoint
++ * first output endpoint = display endpoint
++ */
++ for (i = 0; i < num_endpts && !(ir_ep_found && display_ep_found); ++i) {
++ struct usb_endpoint_descriptor *ep;
++ int ep_dir;
++ int ep_type;
++ ep = &iface_desc->endpoint[i].desc;
++ ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
++ ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
++
++ if (!ir_ep_found &&
++ ep_dir == USB_DIR_IN &&
++ ep_type == USB_ENDPOINT_XFER_INT) {
++
++ rx_endpoint = ep;
++ ir_ep_found = 1;
++ dev_dbg(dev, "%s: found IR endpoint\n", __func__);
++
++ } else if (!display_ep_found && ep_dir == USB_DIR_OUT &&
++ ep_type == USB_ENDPOINT_XFER_INT) {
++ tx_endpoint = ep;
++ display_ep_found = 1;
++ dev_dbg(dev, "%s: found display endpoint\n", __func__);
++ }
++ }
++
++ /*
++ * Some iMON receivers have no display. Unfortunately, it seems
++ * that SoundGraph recycles device IDs between devices both with
++ * and without... :\
++ */
++ if (context->display == 0) {
++ display_ep_found = 0;
++ dev_dbg(dev, "%s: device has no display\n", __func__);
++ }
++
++ /* Input endpoint is mandatory */
++ if (!ir_ep_found) {
++ err("%s: no valid input (IR) endpoint found.", __func__);
++ retval = -ENODEV;
++ alloc_status = 2;
++ goto alloc_status_switch;
++ }
++
++ /* Determine if display requires 6 packets */
++ if (display_ep_found) {
++ if (usb_match_id(interface, vfd_proto_6p_list))
++ vfd_proto_6p = 1;
++
++ dev_dbg(dev, "%s: vfd_proto_6p: %d\n",
++ __func__, vfd_proto_6p);
++ }
++
++ driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
++ if (!driver) {
++ err("%s: kzalloc failed for lirc_driver", __func__);
++ alloc_status = 2;
++ goto alloc_status_switch;
++ }
++ rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
++ if (!rbuf) {
++ err("%s: kmalloc failed for lirc_buffer", __func__);
++ alloc_status = 3;
++ goto alloc_status_switch;
++ }
++ if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) {
++ err("%s: lirc_buffer_init failed", __func__);
++ alloc_status = 4;
++ goto alloc_status_switch;
++ }
++ rx_urb = usb_alloc_urb(0, GFP_KERNEL);
++ if (!rx_urb) {
++ err("%s: usb_alloc_urb failed for IR urb", __func__);
++ alloc_status = 5;
++ goto alloc_status_switch;
++ }
++ tx_urb = usb_alloc_urb(0, GFP_KERNEL);
++ if (!tx_urb) {
++ err("%s: usb_alloc_urb failed for display urb",
++ __func__);
++ alloc_status = 6;
++ goto alloc_status_switch;
++ }
++
++ mutex_init(&context->ctx_lock);
++ context->vfd_proto_6p = vfd_proto_6p;
++
++ strcpy(driver->name, MOD_NAME);
++ driver->minor = -1;
++ driver->code_length = sizeof(int) * 8;
++ driver->sample_rate = 0;
++ driver->features = LIRC_CAN_REC_MODE2;
++ driver->data = context;
++ driver->rbuf = rbuf;
++ driver->set_use_inc = ir_open;
++ driver->set_use_dec = ir_close;
++ driver->dev = &interface->dev;
++ driver->owner = THIS_MODULE;
++
++ mutex_lock(&context->ctx_lock);
++
++ context->driver = driver;
++ /* start out in keyboard mode */
++
++ lirc_minor = lirc_register_driver(driver);
++ if (lirc_minor < 0) {
++ err("%s: lirc_register_driver failed", __func__);
++ alloc_status = 7;
++ goto alloc_status_switch;
++ } else
++ dev_info(dev, "Registered iMON driver "
++ "(lirc minor: %d)\n", lirc_minor);
++
++ /* Needed while unregistering! */
++ driver->minor = lirc_minor;
++
++ context->usbdev = usbdev;
++ context->dev_present = 1;
++ context->rx_endpoint = rx_endpoint;
++ context->rx_urb = rx_urb;
++
++ /*
++ * tx is used to send characters to lcd/vfd, associate RF
++ * remotes, set IR protocol, and maybe more...
++ */
++ context->tx_endpoint = tx_endpoint;
++ context->tx_urb = tx_urb;
++
++ if (display_ep_found)
++ context->display = 1;
++
++ usb_fill_int_urb(context->rx_urb, context->usbdev,
++ usb_rcvintpipe(context->usbdev,
++ context->rx_endpoint->bEndpointAddress),
++ context->usb_rx_buf, sizeof(context->usb_rx_buf),
++ usb_rx_callback, context,
++ context->rx_endpoint->bInterval);
++
++ retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
++
++ if (retval) {
++ err("%s: usb_submit_urb failed for intf0 (%d)",
++ __func__, retval);
++ mutex_unlock(&context->ctx_lock);
++ goto exit;
++ }
++
++ usb_set_intfdata(interface, context);
++
++ if (context->display && ifnum == 0) {
++ dev_dbg(dev, "%s: Registering iMON display with sysfs\n",
++ __func__);
++
++ if (usb_register_dev(interface, &imon_class)) {
++ /* Not a fatal error, so ignore */
++ dev_info(dev, "%s: could not get a minor number for "
++ "display\n", __func__);
++ }
++ }
++
++ dev_info(dev, "iMON device (%04x:%04x, intf%d) on "
++ "usb<%d:%d> initialized\n", vendor, product, ifnum,
++ usbdev->bus->busnum, usbdev->devnum);
++
++alloc_status_switch:
++ mutex_unlock(&context->ctx_lock);
++
++ switch (alloc_status) {
++ case 7:
++ usb_free_urb(tx_urb);
++ case 6:
++ usb_free_urb(rx_urb);
++ case 5:
++ if (rbuf)
++ lirc_buffer_free(rbuf);
++ case 4:
++ kfree(rbuf);
++ case 3:
++ kfree(driver);
++ case 2:
++ kfree(context);
++ context = NULL;
++ case 1:
++ if (retval != -ENODEV)
++ retval = -ENOMEM;
++ break;
++ case 0:
++ retval = 0;
++ }
++
++exit:
++ mutex_unlock(&driver_lock);
++
++ return retval;
++}
++
++/**
++ * Callback function for USB core API: disconnect
++ */
++static void imon_disconnect(struct usb_interface *interface)
++{
++ struct imon_context *context;
++ int ifnum;
++
++ /* prevent races with ir_open()/display_open() */
++ mutex_lock(&driver_lock);
++
++ context = usb_get_intfdata(interface);
++ ifnum = interface->cur_altsetting->desc.bInterfaceNumber;
++
++ mutex_lock(&context->ctx_lock);
++
++ usb_set_intfdata(interface, NULL);
++
++ /* Abort ongoing write */
++ if (atomic_read(&context->tx.busy)) {
++ usb_kill_urb(context->tx_urb);
++ complete_all(&context->tx.finished);
++ }
++
++ context->dev_present = 0;
++ usb_kill_urb(context->rx_urb);
++ if (context->display)
++ usb_deregister_dev(interface, &imon_class);
++
++ if (!context->ir_isopen && !context->dev_present) {
++ deregister_from_lirc(context);
++ mutex_unlock(&context->ctx_lock);
++ if (!context->display_isopen)
++ free_imon_context(context);
++ } else
++ mutex_unlock(&context->ctx_lock);
++
++ mutex_unlock(&driver_lock);
++
++ printk(KERN_INFO "%s: iMON device (intf%d) disconnected\n",
++ __func__, ifnum);
++}
++
++static int imon_suspend(struct usb_interface *intf, pm_message_t message)
++{
++ struct imon_context *context = usb_get_intfdata(intf);
++
++ usb_kill_urb(context->rx_urb);
++
++ return 0;
++}
++
++static int imon_resume(struct usb_interface *intf)
++{
++ int rc = 0;
++ struct imon_context *context = usb_get_intfdata(intf);
++
++ usb_fill_int_urb(context->rx_urb, context->usbdev,
++ usb_rcvintpipe(context->usbdev,
++ context->rx_endpoint->bEndpointAddress),
++ context->usb_rx_buf, sizeof(context->usb_rx_buf),
++ usb_rx_callback, context,
++ context->rx_endpoint->bInterval);
++
++ rc = usb_submit_urb(context->rx_urb, GFP_ATOMIC);
++
++ return rc;
++}
++
++static int __init imon_init(void)
++{
++ int rc;
++
++ printk(KERN_INFO MOD_NAME ": " MOD_DESC ", v" MOD_VERSION "\n");
++
++ rc = usb_register(&imon_driver);
++ if (rc) {
++ err("%s: usb register failed(%d)", __func__, rc);
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++static void __exit imon_exit(void)
++{
++ usb_deregister(&imon_driver);
++ printk(KERN_INFO MOD_NAME ": module removed. Goodbye!\n");
++}
++
++module_init(imon_init);
++module_exit(imon_exit);
+diff --git a/drivers/staging/lirc/lirc_it87.c b/drivers/staging/lirc/lirc_it87.c
+new file mode 100644
+index 0000000..781abc3
+--- /dev/null
++++ b/drivers/staging/lirc/lirc_it87.c
+@@ -0,0 +1,1019 @@
++/*
++ * LIRC driver for ITE IT8712/IT8705 CIR port
++ *
++ * Copyright (C) 2001 Hans-Gunter Lutke Uphues <hg_lu@web.de>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
++ * USA
++ *
++ * ITE IT8705 and IT8712(not tested) and IT8720 CIR-port support for lirc based
++ * via cut and paste from lirc_sir.c (C) 2000 Milan Pikula
++ *
++ * Attention: Sendmode only tested with debugging logs
++ *
++ * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> :
++ * reimplemented read function
++ * 2005/06/05 Andrew Calkin implemented support for Asus Digimatrix,
++ * based on work of the following member of the Outertrack Digimatrix
++ * Forum: Art103 <r_tay@hotmail.com>
++ * 2009/12/24 James Edwards <jimbo-lirc@edwardsclan.net> implemeted support
++ * for ITE8704/ITE8718, on my machine, the DSDT reports 8704, but the
++ * chip identifies as 18.
++ */
++
++#include <linux/module.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/fs.h>
++#include <linux/interrupt.h>
++#include <linux/ioport.h>
++#include <linux/kernel.h>
++#include <linux/time.h>
++#include <linux/string.h>
++#include <linux/types.h>
++#include <linux/wait.h>
++#include <linux/mm.h>
++#include <linux/delay.h>
++#include <linux/poll.h>
++#include <asm/system.h>
++#include <linux/io.h>
++#include <linux/irq.h>
++#include <linux/fcntl.h>
++
++#include <linux/timer.h>
++#include <linux/pnp.h>
++
++#include <media/lirc.h>
++#include <media/lirc_dev.h>
++
++#include "lirc_it87.h"
++
++#ifdef LIRC_IT87_DIGIMATRIX
++static int digimatrix = 1;
++static int it87_freq = 36; /* kHz */
++static int irq = 9;
++#else
++static int digimatrix;
++static int it87_freq = 38; /* kHz */
++static int irq = IT87_CIR_DEFAULT_IRQ;
++#endif
++
++static unsigned long it87_bits_in_byte_out;
++static unsigned long it87_send_counter;
++static unsigned char it87_RXEN_mask = IT87_CIR_RCR_RXEN;
++
++#define RBUF_LEN 1024
++
++#define LIRC_DRIVER_NAME "lirc_it87"
++
++/* timeout for sequences in jiffies (=5/100s) */
++/* must be longer than TIME_CONST */
++#define IT87_TIMEOUT (HZ*5/100)
++
++/* module parameters */
++static int debug;
++#define dprintk(fmt, args...) \
++ do { \
++ if (debug) \
++ printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
++ fmt, ## args); \
++ } while (0)
++
++static int io = IT87_CIR_DEFAULT_IOBASE;
++/* receiver demodulator default: off */
++static int it87_enable_demodulator;
++
++static int timer_enabled;
++static DEFINE_SPINLOCK(timer_lock);
++static struct timer_list timerlist;
++/* time of last signal change detected */
++static struct timeval last_tv = {0, 0};
++/* time of last UART data ready interrupt */
++static struct timeval last_intr_tv = {0, 0};
++static int last_value;
++
++static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
++
++static DEFINE_SPINLOCK(hardware_lock);
++static DEFINE_SPINLOCK(dev_lock);
++
++static int rx_buf[RBUF_LEN];
++unsigned int rx_tail, rx_head;
++
++static struct pnp_driver it87_pnp_driver;
++
++/* SECTION: Prototypes */
++
++/* Communication with user-space */
++static int lirc_open(struct inode *inode, struct file *file);
++static int lirc_close(struct inode *inode, struct file *file);
++static unsigned int lirc_poll(struct file *file, poll_table *wait);
++static ssize_t lirc_read(struct file *file, char *buf,
++ size_t count, loff_t *ppos);
++static ssize_t lirc_write(struct file *file, const char *buf,
++ size_t n, loff_t *pos);
++static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
++static void add_read_queue(int flag, unsigned long val);
++static int init_chrdev(void);
++static void drop_chrdev(void);
++/* Hardware */
++static irqreturn_t it87_interrupt(int irq, void *dev_id);
++static void send_space(unsigned long len);
++static void send_pulse(unsigned long len);
++static void init_send(void);
++static void terminate_send(unsigned long len);
++static int init_hardware(void);
++static void drop_hardware(void);
++/* Initialisation */
++static int init_port(void);
++static void drop_port(void);
++
++
++/* SECTION: Communication with user-space */
++
++static int lirc_open(struct inode *inode, struct file *file)
++{
++ spin_lock(&dev_lock);
++ if (module_refcount(THIS_MODULE)) {
++ spin_unlock(&dev_lock);
++ return -EBUSY;
++ }
++ spin_unlock(&dev_lock);
++ return 0;
++}
++
++
++static int lirc_close(struct inode *inode, struct file *file)
++{
++ return 0;
++}
++
++
++static unsigned int lirc_poll(struct file *file, poll_table *wait)
++{
++ poll_wait(file, &lirc_read_queue, wait);
++ if (rx_head != rx_tail)
++ return POLLIN | POLLRDNORM;
++ return 0;
++}
++
++
++static ssize_t lirc_read(struct file *file, char *buf,
++ size_t count, loff_t *ppos)
++{
++ int n = 0;
++ int retval = 0;
++
++ while (n < count) {
++ if (file->f_flags & O_NONBLOCK && rx_head == rx_tail) {
++ retval = -EAGAIN;
++ break;
++ }
++ retval = wait_event_interruptible(lirc_read_queue,
++ rx_head != rx_tail);
++ if (retval)
++ break;
++
++ if (copy_to_user((void *) buf + n, (void *) (rx_buf + rx_head),
++ sizeof(int))) {
++ retval = -EFAULT;
++ break;
++ }
++ rx_head = (rx_head + 1) & (RBUF_LEN - 1);
++ n += sizeof(int);
++ }
++ if (n)
++ return n;
++ return retval;
++}
++
++
++static ssize_t lirc_write(struct file *file, const char *buf,
++ size_t n, loff_t *pos)
++{
++ int i = 0;
++ int *tx_buf;
++
++ if (n % sizeof(int))
++ return -EINVAL;
++ tx_buf = memdup_user(buf, n);
++ if (IS_ERR(tx_buf))
++ return PTR_ERR(tx_buf);
++ n /= sizeof(int);
++ init_send();
++ while (1) {
++ if (i >= n)
++ break;
++ if (tx_buf[i])
++ send_pulse(tx_buf[i]);
++ i++;
++ if (i >= n)
++ break;
++ if (tx_buf[i])
++ send_space(tx_buf[i]);
++ i++;
++ }
++ terminate_send(tx_buf[i - 1]);
++ return n;
++}
++
++
++static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
++{
++ int retval = 0;
++ unsigned long value = 0;
++ unsigned int ivalue;
++ unsigned long hw_flags;
++
++ if (cmd == LIRC_GET_FEATURES)
++ value = LIRC_CAN_SEND_PULSE |
++ LIRC_CAN_SET_SEND_CARRIER |
++ LIRC_CAN_REC_MODE2;
++ else if (cmd == LIRC_GET_SEND_MODE)
++ value = LIRC_MODE_PULSE;
++ else if (cmd == LIRC_GET_REC_MODE)
++ value = LIRC_MODE_MODE2;
++
++ switch (cmd) {
++ case LIRC_GET_FEATURES:
++ case LIRC_GET_SEND_MODE:
++ case LIRC_GET_REC_MODE:
++ retval = put_user(value, (unsigned long *) arg);
++ break;
++
++ case LIRC_SET_SEND_MODE:
++ case LIRC_SET_REC_MODE:
++ retval = get_user(value, (unsigned long *) arg);
++ break;
++
++ case LIRC_SET_SEND_CARRIER:
++ retval = get_user(ivalue, (unsigned int *) arg);
++ if (retval)
++ return retval;
++ ivalue /= 1000;
++ if (ivalue > IT87_CIR_FREQ_MAX ||
++ ivalue < IT87_CIR_FREQ_MIN)
++ return -EINVAL;
++
++ it87_freq = ivalue;
++
++ spin_lock_irqsave(&hardware_lock, hw_flags);
++ outb(((inb(io + IT87_CIR_TCR2) & IT87_CIR_TCR2_TXMPW) |
++ (it87_freq - IT87_CIR_FREQ_MIN) << 3),
++ io + IT87_CIR_TCR2);
++ spin_unlock_irqrestore(&hardware_lock, hw_flags);
++ dprintk("demodulation frequency: %d kHz\n", it87_freq);
++
++ break;
++
++ default:
++ retval = -EINVAL;
++ }
++
++ if (retval)
++ return retval;
++
++ if (cmd == LIRC_SET_REC_MODE) {
++ if (value != LIRC_MODE_MODE2)
++ retval = -ENOSYS;
++ } else if (cmd == LIRC_SET_SEND_MODE) {
++ if (value != LIRC_MODE_PULSE)
++ retval = -ENOSYS;
++ }
++ return retval;
++}
++
++static void add_read_queue(int flag, unsigned long val)
++{
++ unsigned int new_rx_tail;
++ int newval;
++
++ dprintk("add flag %d with val %lu\n", flag, val);
++
++ newval = val & PULSE_MASK;
++
++ /*
++ * statistically, pulses are ~TIME_CONST/2 too long. we could
++ * maybe make this more exact, but this is good enough
++ */
++ if (flag) {
++ /* pulse */
++ if (newval > TIME_CONST / 2)
++ newval -= TIME_CONST / 2;
++ else /* should not ever happen */
++ newval = 1;
++ newval |= PULSE_BIT;
++ } else
++ newval += TIME_CONST / 2;
++ new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1);
++ if (new_rx_tail == rx_head) {
++ dprintk("Buffer overrun.\n");
++ return;
++ }
++ rx_buf[rx_tail] = newval;
++ rx_tail = new_rx_tail;
++ wake_up_interruptible(&lirc_read_queue);
++}
++
++
++static struct file_operations lirc_fops = {
++ .owner = THIS_MODULE,
++ .read = lirc_read,
++ .write = lirc_write,
++ .poll = lirc_poll,
++ .unlocked_ioctl = lirc_ioctl,
++ .open = lirc_open,
++ .release = lirc_close,
++};
++
++static int set_use_inc(void *data)
++{
++ return 0;
++}
++
++static void set_use_dec(void *data)
++{
++}
++
++static struct lirc_driver driver = {
++ .name = LIRC_DRIVER_NAME,
++ .minor = -1,
++ .code_length = 1,
++ .sample_rate = 0,
++ .data = NULL,
++ .add_to_buf = NULL,
++ .set_use_inc = set_use_inc,
++ .set_use_dec = set_use_dec,
++ .fops = &lirc_fops,
++ .dev = NULL,
++ .owner = THIS_MODULE,
++};
++
++
++#ifdef MODULE
++static int init_chrdev(void)
++{
++ driver.minor = lirc_register_driver(&driver);
++
++ if (driver.minor < 0) {
++ printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n");
++ return -EIO;
++ }
++ return 0;
++}
++
++
++static void drop_chrdev(void)
++{
++ lirc_unregister_driver(driver.minor);
++}
++#endif
++
++
++/* SECTION: Hardware */
++static long delta(struct timeval *tv1, struct timeval *tv2)
++{
++ unsigned long deltv;
++
++ deltv = tv2->tv_sec - tv1->tv_sec;
++ if (deltv > 15)
++ deltv = 0xFFFFFF;
++ else
++ deltv = deltv*1000000 + tv2->tv_usec - tv1->tv_usec;
++ return deltv;
++}
++
++static void it87_timeout(unsigned long data)
++{
++ unsigned long flags;
++
++ /* avoid interference with interrupt */
++ spin_lock_irqsave(&timer_lock, flags);
++
++ if (digimatrix) {
++ /* We have timed out. Disable the RX mechanism. */
++
++ outb((inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN) |
++ IT87_CIR_RCR_RXACT, io + IT87_CIR_RCR);
++ if (it87_RXEN_mask)
++ outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN,
++ io + IT87_CIR_RCR);
++ dprintk(" TIMEOUT\n");
++ timer_enabled = 0;
++
++ /* fifo clear */
++ outb(inb(io + IT87_CIR_TCR1) | IT87_CIR_TCR1_FIFOCLR,
++ io+IT87_CIR_TCR1);
++
++ } else {
++ /*
++ * if last received signal was a pulse, but receiving stopped
++ * within the 9 bit frame, we need to finish this pulse and
++ * simulate a signal change to from pulse to space. Otherwise
++ * upper layers will receive two sequences next time.
++ */
++
++ if (last_value) {
++ unsigned long pulse_end;
++
++ /* determine 'virtual' pulse end: */
++ pulse_end = delta(&last_tv, &last_intr_tv);
++ dprintk("timeout add %d for %lu usec\n",
++ last_value, pulse_end);
++ add_read_queue(last_value, pulse_end);
++ last_value = 0;
++ last_tv = last_intr_tv;
++ }
++ }
++ spin_unlock_irqrestore(&timer_lock, flags);
++}
++
++static irqreturn_t it87_interrupt(int irq, void *dev_id)
++{
++ unsigned char data;
++ struct timeval curr_tv;
++ static unsigned long deltv;
++ unsigned long deltintrtv;
++ unsigned long flags, hw_flags;
++ int iir, lsr;
++ int fifo = 0;
++ static char lastbit;
++ char bit;
++
++ /* Bit duration in microseconds */
++ const unsigned long bit_duration = 1000000ul /
++ (115200 / IT87_CIR_BAUDRATE_DIVISOR);
++
++
++ iir = inb(io + IT87_CIR_IIR);
++
++ switch (iir & IT87_CIR_IIR_IID) {
++ case 0x4:
++ case 0x6:
++ lsr = inb(io + IT87_CIR_RSR) & (IT87_CIR_RSR_RXFTO |
++ IT87_CIR_RSR_RXFBC);
++ fifo = lsr & IT87_CIR_RSR_RXFBC;
++ dprintk("iir: 0x%x fifo: 0x%x\n", iir, lsr);
++
++ /* avoid interference with timer */
++ spin_lock_irqsave(&timer_lock, flags);
++ spin_lock_irqsave(&hardware_lock, hw_flags);
++ if (digimatrix) {
++ static unsigned long acc_pulse;
++ static unsigned long acc_space;
++
++ do {
++ data = inb(io + IT87_CIR_DR);
++ data = ~data;
++ fifo--;
++ if (data != 0x00) {
++ if (timer_enabled)
++ del_timer(&timerlist);
++ /*
++ * start timer for end of
++ * sequence detection
++ */
++ timerlist.expires = jiffies +
++ IT87_TIMEOUT;
++ add_timer(&timerlist);
++ timer_enabled = 1;
++ }
++ /* Loop through */
++ for (bit = 0; bit < 8; ++bit) {
++ if ((data >> bit) & 1) {
++ ++acc_pulse;
++ if (lastbit == 0) {
++ add_read_queue(0,
++ acc_space *
++ bit_duration);
++ acc_space = 0;
++ }
++ } else {
++ ++acc_space;
++ if (lastbit == 1) {
++ add_read_queue(1,
++ acc_pulse *
++ bit_duration);
++ acc_pulse = 0;
++ }
++ }
++ lastbit = (data >> bit) & 1;
++ }
++
++ } while (fifo != 0);
++ } else { /* Normal Operation */
++ do {
++ del_timer(&timerlist);
++ data = inb(io + IT87_CIR_DR);
++
++ dprintk("data=%02x\n", data);
++ do_gettimeofday(&curr_tv);
++ deltv = delta(&last_tv, &curr_tv);
++ deltintrtv = delta(&last_intr_tv, &curr_tv);
++
++ dprintk("t %lu , d %d\n",
++ deltintrtv, (int)data);
++
++ /*
++ * if nothing came in last 2 cycles,
++ * it was gap
++ */
++ if (deltintrtv > TIME_CONST * 2) {
++ if (last_value) {
++ dprintk("GAP\n");
++
++ /* simulate signal change */
++ add_read_queue(last_value,
++ deltv -
++ deltintrtv);
++ last_value = 0;
++ last_tv.tv_sec =
++ last_intr_tv.tv_sec;
++ last_tv.tv_usec =
++ last_intr_tv.tv_usec;
++ deltv = deltintrtv;
++ }
++ }
++ data = 1;
++ if (data ^ last_value) {
++ /*
++ * deltintrtv > 2*TIME_CONST,
++ * remember ? the other case is
++ * timeout
++ */
++ add_read_queue(last_value,
++ deltv-TIME_CONST);
++ last_value = data;
++ last_tv = curr_tv;
++ if (last_tv.tv_usec >= TIME_CONST)
++ last_tv.tv_usec -= TIME_CONST;
++ else {
++ last_tv.tv_sec--;
++ last_tv.tv_usec += 1000000 -
++ TIME_CONST;
++ }
++ }
++ last_intr_tv = curr_tv;
++ if (data) {
++ /*
++ * start timer for end of
++ * sequence detection
++ */
++ timerlist.expires =
++ jiffies + IT87_TIMEOUT;
++ add_timer(&timerlist);
++ }
++ outb((inb(io + IT87_CIR_RCR) &
++ ~IT87_CIR_RCR_RXEN) |
++ IT87_CIR_RCR_RXACT,
++ io + IT87_CIR_RCR);
++ if (it87_RXEN_mask)
++ outb(inb(io + IT87_CIR_RCR) |
++ IT87_CIR_RCR_RXEN,
++ io + IT87_CIR_RCR);
++ fifo--;
++ } while (fifo != 0);
++ }
++ spin_unlock_irqrestore(&hardware_lock, hw_flags);
++ spin_unlock_irqrestore(&timer_lock, flags);
++
++ return IRQ_RETVAL(IRQ_HANDLED);
++
++ default:
++ /* not our irq */
++ dprintk("unknown IRQ (shouldn't happen) !!\n");
++ return IRQ_RETVAL(IRQ_NONE);
++ }
++}
++
++
++static void send_it87(unsigned long len, unsigned long stime,
++ unsigned char send_byte, unsigned int count_bits)
++{
++ long count = len / stime;
++ long time_left = 0;
++ static unsigned char byte_out;
++ unsigned long hw_flags;
++
++ dprintk("%s: len=%ld, sb=%d\n", __func__, len, send_byte);
++
++ time_left = (long)len - (long)count * (long)stime;
++ count += ((2 * time_left) / stime);
++ while (count) {
++ long i = 0;
++ for (i = 0; i < count_bits; i++) {
++ byte_out = (byte_out << 1) | (send_byte & 1);
++ it87_bits_in_byte_out++;
++ }
++ if (it87_bits_in_byte_out == 8) {
++ dprintk("out=0x%x, tsr_txfbc: 0x%x\n",
++ byte_out,
++ inb(io + IT87_CIR_TSR) &
++ IT87_CIR_TSR_TXFBC);
++
++ while ((inb(io + IT87_CIR_TSR) &
++ IT87_CIR_TSR_TXFBC) >= IT87_CIR_FIFO_SIZE)
++ ;
++
++ spin_lock_irqsave(&hardware_lock, hw_flags);
++ outb(byte_out, io + IT87_CIR_DR);
++ spin_unlock_irqrestore(&hardware_lock, hw_flags);
++
++ it87_bits_in_byte_out = 0;
++ it87_send_counter++;
++ byte_out = 0;
++ }
++ count--;
++ }
++}
++
++
++/*TODO: maybe exchange space and pulse because it8705 only modulates 0-bits */
++
++static void send_space(unsigned long len)
++{
++ send_it87(len, TIME_CONST, IT87_CIR_SPACE, IT87_CIR_BAUDRATE_DIVISOR);
++}
++
++static void send_pulse(unsigned long len)
++{
++ send_it87(len, TIME_CONST, IT87_CIR_PULSE, IT87_CIR_BAUDRATE_DIVISOR);
++}
++
++
++static void init_send()
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&hardware_lock, flags);
++ /* RXEN=0: receiver disable */
++ it87_RXEN_mask = 0;
++ outb(inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN,
++ io + IT87_CIR_RCR);
++ spin_unlock_irqrestore(&hardware_lock, flags);
++ it87_bits_in_byte_out = 0;
++ it87_send_counter = 0;
++}
++
++
++static void terminate_send(unsigned long len)
++{
++ unsigned long flags;
++ unsigned long last = 0;
++
++ last = it87_send_counter;
++ /* make sure all necessary data has been sent */
++ while (last == it87_send_counter)
++ send_space(len);
++ /* wait until all data sent */
++ while ((inb(io + IT87_CIR_TSR) & IT87_CIR_TSR_TXFBC) != 0)
++ ;
++ /* then re-enable receiver */
++ spin_lock_irqsave(&hardware_lock, flags);
++ it87_RXEN_mask = IT87_CIR_RCR_RXEN;
++ outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN,
++ io + IT87_CIR_RCR);
++ spin_unlock_irqrestore(&hardware_lock, flags);
++}
++
++
++static int init_hardware(void)
++{
++ unsigned long flags;
++ unsigned char it87_rcr = 0;
++
++ spin_lock_irqsave(&hardware_lock, flags);
++ /* init cir-port */
++ /* enable r/w-access to Baudrate-Register */
++ outb(IT87_CIR_IER_BR, io + IT87_CIR_IER);
++ outb(IT87_CIR_BAUDRATE_DIVISOR % 0x100, io+IT87_CIR_BDLR);
++ outb(IT87_CIR_BAUDRATE_DIVISOR / 0x100, io+IT87_CIR_BDHR);
++ /* Baudrate Register off, define IRQs: Input only */
++ if (digimatrix) {
++ outb(IT87_CIR_IER_IEC | IT87_CIR_IER_RFOIE, io + IT87_CIR_IER);
++ /* RX: HCFS=0, RXDCR = 001b (33,75..38,25 kHz), RXEN=1 */
++ } else {
++ outb(IT87_CIR_IER_IEC | IT87_CIR_IER_RDAIE, io + IT87_CIR_IER);
++ /* RX: HCFS=0, RXDCR = 001b (35,6..40,3 kHz), RXEN=1 */
++ }
++ it87_rcr = (IT87_CIR_RCR_RXEN & it87_RXEN_mask) | 0x1;
++ if (it87_enable_demodulator)
++ it87_rcr |= IT87_CIR_RCR_RXEND;
++ outb(it87_rcr, io + IT87_CIR_RCR);
++ if (digimatrix) {
++ /* Set FIFO depth to 1 byte, and disable TX */
++ outb(inb(io + IT87_CIR_TCR1) | 0x00,
++ io + IT87_CIR_TCR1);
++
++ /*
++ * TX: it87_freq (36kHz), 'reserved' sensitivity
++ * setting (0x00)
++ */
++ outb(((it87_freq - IT87_CIR_FREQ_MIN) << 3) | 0x00,
++ io + IT87_CIR_TCR2);
++ } else {
++ /* TX: 38kHz, 13,3us (pulse-width) */
++ outb(((it87_freq - IT87_CIR_FREQ_MIN) << 3) | 0x06,
++ io + IT87_CIR_TCR2);
++ }
++ spin_unlock_irqrestore(&hardware_lock, flags);
++ return 0;
++}
++
++
++static void drop_hardware(void)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&hardware_lock, flags);
++ disable_irq(irq);
++ /* receiver disable */
++ it87_RXEN_mask = 0;
++ outb(0x1, io + IT87_CIR_RCR);
++ /* turn off irqs */
++ outb(0, io + IT87_CIR_IER);
++ /* fifo clear */
++ outb(IT87_CIR_TCR1_FIFOCLR, io+IT87_CIR_TCR1);
++ /* reset */
++ outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER);
++ enable_irq(irq);
++ spin_unlock_irqrestore(&hardware_lock, flags);
++}
++
++
++static unsigned char it87_read(unsigned char port)
++{
++ outb(port, IT87_ADRPORT);
++ return inb(IT87_DATAPORT);
++}
++
++
++static void it87_write(unsigned char port, unsigned char data)
++{
++ outb(port, IT87_ADRPORT);
++ outb(data, IT87_DATAPORT);
++}
++
++
++/* SECTION: Initialisation */
++
++static int init_port(void)
++{
++ unsigned long hw_flags;
++ int retval = 0;
++
++ unsigned char init_bytes[4] = IT87_INIT;
++ unsigned char it87_chipid = 0;
++ unsigned char ldn = 0;
++ unsigned int it87_io = 0;
++ unsigned int it87_irq = 0;
++
++ /* Enter MB PnP Mode */
++ outb(init_bytes[0], IT87_ADRPORT);
++ outb(init_bytes[1], IT87_ADRPORT);
++ outb(init_bytes[2], IT87_ADRPORT);
++ outb(init_bytes[3], IT87_ADRPORT);
++
++ /* 8712 or 8705 ? */
++ it87_chipid = it87_read(IT87_CHIP_ID1);
++ if (it87_chipid != 0x87) {
++ retval = -ENXIO;
++ return retval;
++ }
++ it87_chipid = it87_read(IT87_CHIP_ID2);
++ if ((it87_chipid != 0x05) &&
++ (it87_chipid != 0x12) &&
++ (it87_chipid != 0x18) &&
++ (it87_chipid != 0x20)) {
++ printk(KERN_INFO LIRC_DRIVER_NAME
++ ": no IT8704/05/12/18/20 found (claimed IT87%02x), "
++ "exiting..\n", it87_chipid);
++ retval = -ENXIO;
++ return retval;
++ }
++ printk(KERN_INFO LIRC_DRIVER_NAME
++ ": found IT87%02x.\n",
++ it87_chipid);
++
++ /* get I/O-Port and IRQ */
++ if (it87_chipid == 0x12 || it87_chipid == 0x18)
++ ldn = IT8712_CIR_LDN;
++ else
++ ldn = IT8705_CIR_LDN;
++ it87_write(IT87_LDN, ldn);
++
++ it87_io = it87_read(IT87_CIR_BASE_MSB) * 256 +
++ it87_read(IT87_CIR_BASE_LSB);
++ if (it87_io == 0) {
++ if (io == 0)
++ io = IT87_CIR_DEFAULT_IOBASE;
++ printk(KERN_INFO LIRC_DRIVER_NAME
++ ": set default io 0x%x\n",
++ io);
++ it87_write(IT87_CIR_BASE_MSB, io / 0x100);
++ it87_write(IT87_CIR_BASE_LSB, io % 0x100);
++ } else
++ io = it87_io;
++
++ it87_irq = it87_read(IT87_CIR_IRQ);
++ if (digimatrix || it87_irq == 0) {
++ if (irq == 0)
++ irq = IT87_CIR_DEFAULT_IRQ;
++ printk(KERN_INFO LIRC_DRIVER_NAME
++ ": set default irq 0x%x\n",
++ irq);
++ it87_write(IT87_CIR_IRQ, irq);
++ } else
++ irq = it87_irq;
++
++ spin_lock_irqsave(&hardware_lock, hw_flags);
++ /* reset */
++ outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER);
++ /* fifo clear */
++ outb(IT87_CIR_TCR1_FIFOCLR |
++ /* IT87_CIR_TCR1_ILE | */
++ IT87_CIR_TCR1_TXRLE |
++ IT87_CIR_TCR1_TXENDF, io+IT87_CIR_TCR1);
++ spin_unlock_irqrestore(&hardware_lock, hw_flags);
++
++ /* get I/O port access and IRQ line */
++ if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) {
++ printk(KERN_ERR LIRC_DRIVER_NAME
++ ": i/o port 0x%.4x already in use.\n", io);
++ /* Leaving MB PnP Mode */
++ it87_write(IT87_CFGCTRL, 0x2);
++ return -EBUSY;
++ }
++
++ /* activate CIR-Device */
++ it87_write(IT87_CIR_ACT, 0x1);
++
++ /* Leaving MB PnP Mode */
++ it87_write(IT87_CFGCTRL, 0x2);
++
++ retval = request_irq(irq, it87_interrupt, 0 /*IRQF_DISABLED*/,
++ LIRC_DRIVER_NAME, NULL);
++ if (retval < 0) {
++ printk(KERN_ERR LIRC_DRIVER_NAME
++ ": IRQ %d already in use.\n",
++ irq);
++ release_region(io, 8);
++ return retval;
++ }
++
++ printk(KERN_INFO LIRC_DRIVER_NAME
++ ": I/O port 0x%.4x, IRQ %d.\n", io, irq);
++
++ init_timer(&timerlist);
++ timerlist.function = it87_timeout;
++ timerlist.data = 0xabadcafe;
++
++ return 0;
++}
++
++
++static void drop_port(void)
++{
++#if 0
++ unsigned char init_bytes[4] = IT87_INIT;
++
++ /* Enter MB PnP Mode */
++ outb(init_bytes[0], IT87_ADRPORT);
++ outb(init_bytes[1], IT87_ADRPORT);
++ outb(init_bytes[2], IT87_ADRPORT);
++ outb(init_bytes[3], IT87_ADRPORT);
++
++ /* deactivate CIR-Device */
++ it87_write(IT87_CIR_ACT, 0x0);
++
++ /* Leaving MB PnP Mode */
++ it87_write(IT87_CFGCTRL, 0x2);
++#endif
++
++ del_timer_sync(&timerlist);
++ free_irq(irq, NULL);
++ release_region(io, 8);
++}
++
++
++static int init_lirc_it87(void)
++{
++ int retval;
++
++ init_waitqueue_head(&lirc_read_queue);
++ retval = init_port();
++ if (retval < 0)
++ return retval;
++ init_hardware();
++ printk(KERN_INFO LIRC_DRIVER_NAME ": Installed.\n");
++ return 0;
++}
++
++static int it87_probe(struct pnp_dev *pnp_dev,
++ const struct pnp_device_id *dev_id)
++{
++ int retval;
++
++ driver.dev = &pnp_dev->dev;
++
++ retval = init_chrdev();
++ if (retval < 0)
++ return retval;
++
++ retval = init_lirc_it87();
++ if (retval)
++ goto init_lirc_it87_failed;
++
++ return 0;
++
++init_lirc_it87_failed:
++ drop_chrdev();
++
++ return retval;
++}
++
++static int __init lirc_it87_init(void)
++{
++ return pnp_register_driver(&it87_pnp_driver);
++}
++
++
++static void __exit lirc_it87_exit(void)
++{
++ drop_hardware();
++ drop_chrdev();
++ drop_port();
++ pnp_unregister_driver(&it87_pnp_driver);
++ printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
++}
++
++/* SECTION: PNP for ITE8704/18 */
++
++static const struct pnp_device_id pnp_dev_table[] = {
++ {"ITE8704", 0},
++ {}
++};
++
++MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
++
++static struct pnp_driver it87_pnp_driver = {
++ .name = LIRC_DRIVER_NAME,
++ .id_table = pnp_dev_table,
++ .probe = it87_probe,
++};
++
++module_init(lirc_it87_init);
++module_exit(lirc_it87_exit);
++
++MODULE_DESCRIPTION("LIRC driver for ITE IT8704/05/12/18/20 CIR port");
++MODULE_AUTHOR("Hans-Gunter Lutke Uphues");
++MODULE_LICENSE("GPL");
++
++module_param(io, int, S_IRUGO);
++MODULE_PARM_DESC(io, "I/O base address (default: 0x310)");
++
++module_param(irq, int, S_IRUGO);
++#ifdef LIRC_IT87_DIGIMATRIX
++MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 9)");
++#else
++MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 7)");
++#endif
++
++module_param(it87_enable_demodulator, bool, S_IRUGO);
++MODULE_PARM_DESC(it87_enable_demodulator,
++ "Receiver demodulator enable/disable (1/0), default: 0");
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Enable debugging messages");
++
++module_param(digimatrix, bool, S_IRUGO | S_IWUSR);
++#ifdef LIRC_IT87_DIGIMATRIX
++MODULE_PARM_DESC(digimatrix,
++ "Asus Digimatrix it87 compat. enable/disable (1/0), default: 1");
++#else
++MODULE_PARM_DESC(digimatrix,
++ "Asus Digimatrix it87 compat. enable/disable (1/0), default: 0");
++#endif
++
++
++module_param(it87_freq, int, S_IRUGO);
++#ifdef LIRC_IT87_DIGIMATRIX
++MODULE_PARM_DESC(it87_freq,
++ "Carrier demodulator frequency (kHz), (default: 36)");
++#else
++MODULE_PARM_DESC(it87_freq,
++ "Carrier demodulator frequency (kHz), (default: 38)");
++#endif
+diff --git a/drivers/staging/lirc/lirc_it87.h b/drivers/staging/lirc/lirc_it87.h
+new file mode 100644
+index 0000000..cf021c8
+--- /dev/null
++++ b/drivers/staging/lirc/lirc_it87.h
+@@ -0,0 +1,116 @@
++/* lirc_it87.h */
++/* SECTION: Definitions */
++
++/********************************* ITE IT87xx ************************/
++
++/* based on the following documentation from ITE:
++ a) IT8712F Preliminary CIR Programming Guide V0.1
++ b) IT8705F Simple LPC I/O Preliminary Specification V0.3
++ c) IT8712F EC-LPC I/O Preliminary Specification V0.5
++*/
++
++/* IT8712/05 Ports: */
++#define IT87_ADRPORT 0x2e
++#define IT87_DATAPORT 0x2f
++#define IT87_INIT {0x87, 0x01, 0x55, 0x55}
++
++/* alternate Ports: */
++/*
++#define IT87_ADRPORT 0x4e
++#define IT87_DATAPORT 0x4f
++#define IT87_INIT {0x87, 0x01, 0x55, 0xaa}
++ */
++
++/* IT8712/05 Registers */
++#define IT87_CFGCTRL 0x2
++#define IT87_LDN 0x7
++#define IT87_CHIP_ID1 0x20
++#define IT87_CHIP_ID2 0x21
++#define IT87_CFG_VERSION 0x22
++#define IT87_SWSUSPEND 0x23
++
++#define IT8712_CIR_LDN 0xa
++#define IT8705_CIR_LDN 0x7
++
++/* CIR Configuration Registers: */
++#define IT87_CIR_ACT 0x30
++#define IT87_CIR_BASE_MSB 0x60
++#define IT87_CIR_BASE_LSB 0x61
++#define IT87_CIR_IRQ 0x70
++#define IT87_CIR_CONFIG 0xf0
++
++/* List of IT87_CIR registers: offset to BaseAddr */
++#define IT87_CIR_DR 0
++#define IT87_CIR_IER 1
++#define IT87_CIR_RCR 2
++#define IT87_CIR_TCR1 3
++#define IT87_CIR_TCR2 4
++#define IT87_CIR_TSR 5
++#define IT87_CIR_RSR 6
++#define IT87_CIR_BDLR 5
++#define IT87_CIR_BDHR 6
++#define IT87_CIR_IIR 7
++
++/* Bit Definition */
++/* IER: */
++#define IT87_CIR_IER_TM_EN 0x80
++#define IT87_CIR_IER_RESEVED 0x40
++#define IT87_CIR_IER_RESET 0x20
++#define IT87_CIR_IER_BR 0x10
++#define IT87_CIR_IER_IEC 0x8
++#define IT87_CIR_IER_RFOIE 0x4
++#define IT87_CIR_IER_RDAIE 0x2
++#define IT87_CIR_IER_TLDLIE 0x1
++
++/* RCR: */
++#define IT87_CIR_RCR_RDWOS 0x80
++#define IT87_CIR_RCR_HCFS 0x40
++#define IT87_CIR_RCR_RXEN 0x20
++#define IT87_CIR_RCR_RXEND 0x10
++#define IT87_CIR_RCR_RXACT 0x8
++#define IT87_CIR_RCR_RXDCR 0x7
++
++/* TCR1: */
++#define IT87_CIR_TCR1_FIFOCLR 0x80
++#define IT87_CIR_TCR1_ILE 0x40
++#define IT87_CIR_TCR1_FIFOTL 0x30
++#define IT87_CIR_TCR1_TXRLE 0x8
++#define IT87_CIR_TCR1_TXENDF 0x4
++#define IT87_CIR_TCR1_TXMPM 0x3
++
++/* TCR2: */
++#define IT87_CIR_TCR2_CFQ 0xf8
++#define IT87_CIR_TCR2_TXMPW 0x7
++
++/* TSR: */
++#define IT87_CIR_TSR_RESERVED 0xc0
++#define IT87_CIR_TSR_TXFBC 0x3f
++
++/* RSR: */
++#define IT87_CIR_RSR_RXFTO 0x80
++#define IT87_CIR_RSR_RESERVED 0x40
++#define IT87_CIR_RSR_RXFBC 0x3f
++
++/* IIR: */
++#define IT87_CIR_IIR_RESERVED 0xf8
++#define IT87_CIR_IIR_IID 0x6
++#define IT87_CIR_IIR_IIP 0x1
++
++/* TM: */
++#define IT87_CIR_TM_IL_SEL 0x80
++#define IT87_CIR_TM_RESERVED 0x40
++#define IT87_CIR_TM_TM_REG 0x3f
++
++#define IT87_CIR_FIFO_SIZE 32
++
++/* Baudratedivisor for IT87: power of 2: only 1,2,4 or 8) */
++#define IT87_CIR_BAUDRATE_DIVISOR 0x1
++#define IT87_CIR_DEFAULT_IOBASE 0x310
++#define IT87_CIR_DEFAULT_IRQ 0x7
++#define IT87_CIR_SPACE 0x00
++#define IT87_CIR_PULSE 0xff
++#define IT87_CIR_FREQ_MIN 27
++#define IT87_CIR_FREQ_MAX 58
++#define TIME_CONST (IT87_CIR_BAUDRATE_DIVISOR * 8000000ul / 115200ul)
++
++/********************************* ITE IT87xx ************************/
+diff --git a/drivers/staging/lirc/lirc_ite8709.c b/drivers/staging/lirc/lirc_ite8709.c
+new file mode 100644
+index 0000000..9352f45
+--- /dev/null
++++ b/drivers/staging/lirc/lirc_ite8709.c
+@@ -0,0 +1,542 @@
++/*
++ * LIRC driver for ITE8709 CIR port
++ *
++ * Copyright (C) 2008 Grégory Lardière <spmf2004-lirc@yahoo.fr>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; 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/module.h>
++#include <linux/interrupt.h>
++#include <linux/sched.h>
++#include <linux/delay.h>
++#include <linux/pnp.h>
++#include <linux/io.h>
++
++#include <media/lirc.h>
++#include <media/lirc_dev.h>
++
++#define LIRC_DRIVER_NAME "lirc_ite8709"
++
++#define BUF_CHUNK_SIZE sizeof(int)
++#define BUF_SIZE (128*BUF_CHUNK_SIZE)
++
++/*
++ * The ITE8709 device seems to be the combination of IT8512 superIO chip and
++ * a specific firmware running on the IT8512's embedded micro-controller.
++ * In addition of the embedded micro-controller, the IT8512 chip contains a
++ * CIR module and several other modules. A few modules are directly accessible
++ * by the host CPU, but most of them are only accessible by the
++ * micro-controller. The CIR module is only accessible by the micro-controller.
++ * The battery-backed SRAM module is accessible by the host CPU and the
++ * micro-controller. So one of the MC's firmware role is to act as a bridge
++ * between the host CPU and the CIR module. The firmware implements a kind of
++ * communication protocol using the SRAM module as a shared memory. The IT8512
++ * specification is publicly available on ITE's web site, but the communication
++ * protocol is not, so it was reverse-engineered.
++ */
++
++/* ITE8709 Registers addresses and values (reverse-engineered) */
++#define ITE8709_MODE 0x1a
++#define ITE8709_REG_ADR 0x1b
++#define ITE8709_REG_VAL 0x1c
++#define ITE8709_IIR 0x1e /* Interrupt identification register */
++#define ITE8709_RFSR 0x1f /* Receiver FIFO status register */
++#define ITE8709_FIFO_START 0x20
++
++#define ITE8709_MODE_READY 0X00
++#define ITE8709_MODE_WRITE 0X01
++#define ITE8709_MODE_READ 0X02
++#define ITE8709_IIR_RDAI 0x02 /* Receiver data available interrupt */
++#define ITE8709_IIR_RFOI 0x04 /* Receiver FIFO overrun interrupt */
++#define ITE8709_RFSR_MASK 0x3f /* FIFO byte count mask */
++
++/*
++ * IT8512 CIR-module registers addresses and values
++ * (from IT8512 E/F specification v0.4.1)
++ */
++#define IT8512_REG_MSTCR 0x01 /* Master control register */
++#define IT8512_REG_IER 0x02 /* Interrupt enable register */
++#define IT8512_REG_CFR 0x04 /* Carrier frequency register */
++#define IT8512_REG_RCR 0x05 /* Receive control register */
++#define IT8512_REG_BDLR 0x08 /* Baud rate divisor low byte register */
++#define IT8512_REG_BDHR 0x09 /* Baud rate divisor high byte register */
++
++#define IT8512_MSTCR_RESET 0x01 /* Reset registers to default value */
++#define IT8512_MSTCR_FIFOCLR 0x02 /* Clear FIFO */
++#define IT8512_MSTCR_FIFOTL_7 0x04 /* FIFO threshold level : 7 */
++#define IT8512_MSTCR_FIFOTL_25 0x0c /* FIFO threshold level : 25 */
++#define IT8512_IER_RDAIE 0x02 /* Enable data interrupt request */
++#define IT8512_IER_RFOIE 0x04 /* Enable FIFO overrun interrupt req */
++#define IT8512_IER_IEC 0x80 /* Enable interrupt request */
++#define IT8512_CFR_CF_36KHZ 0x09 /* Carrier freq : low speed, 36kHz */
++#define IT8512_RCR_RXDCR_1 0x01 /* Demodulation carrier range : 1 */
++#define IT8512_RCR_RXACT 0x08 /* Receiver active */
++#define IT8512_RCR_RXEN 0x80 /* Receiver enable */
++#define IT8512_BDR_6 6 /* Baud rate divisor : 6 */
++
++/* Actual values used by this driver */
++#define CFG_FIFOTL IT8512_MSTCR_FIFOTL_25
++#define CFG_CR_FREQ IT8512_CFR_CF_36KHZ
++#define CFG_DCR IT8512_RCR_RXDCR_1
++#define CFG_BDR IT8512_BDR_6
++#define CFG_TIMEOUT 100000 /* Rearm interrupt when a space is > 100 ms */
++
++static int debug;
++
++struct ite8709_device {
++ int use_count;
++ int io;
++ int irq;
++ spinlock_t hardware_lock;
++ unsigned long long acc_pulse;
++ unsigned long long acc_space;
++ char lastbit;
++ struct timeval last_tv;
++ struct lirc_driver driver;
++ struct tasklet_struct tasklet;
++ char force_rearm;
++ char rearmed;
++ char device_busy;
++};
++
++#define dprintk(fmt, args...) \
++ do { \
++ if (debug) \
++ printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
++ fmt, ## args); \
++ } while (0)
++
++
++static unsigned char ite8709_read(struct ite8709_device *dev,
++ unsigned char port)
++{
++ outb(port, dev->io);
++ return inb(dev->io+1);
++}
++
++static void ite8709_write(struct ite8709_device *dev, unsigned char port,
++ unsigned char data)
++{
++ outb(port, dev->io);
++ outb(data, dev->io+1);
++}
++
++static void ite8709_wait_device(struct ite8709_device *dev)
++{
++ int i = 0;
++ /*
++ * loop until device tells it's ready to continue
++ * iterations count is usually ~750 but can sometimes achieve 13000
++ */
++ for (i = 0; i < 15000; i++) {
++ udelay(2);
++ if (ite8709_read(dev, ITE8709_MODE) == ITE8709_MODE_READY)
++ break;
++ }
++}
++
++static void ite8709_write_register(struct ite8709_device *dev,
++ unsigned char reg_adr, unsigned char reg_value)
++{
++ ite8709_wait_device(dev);
++
++ ite8709_write(dev, ITE8709_REG_VAL, reg_value);
++ ite8709_write(dev, ITE8709_REG_ADR, reg_adr);
++ ite8709_write(dev, ITE8709_MODE, ITE8709_MODE_WRITE);
++}
++
++static void ite8709_init_hardware(struct ite8709_device *dev)
++{
++ spin_lock_irq(&dev->hardware_lock);
++ dev->device_busy = 1;
++ spin_unlock_irq(&dev->hardware_lock);
++
++ ite8709_write_register(dev, IT8512_REG_BDHR, (CFG_BDR >> 8) & 0xff);
++ ite8709_write_register(dev, IT8512_REG_BDLR, CFG_BDR & 0xff);
++ ite8709_write_register(dev, IT8512_REG_CFR, CFG_CR_FREQ);
++ ite8709_write_register(dev, IT8512_REG_IER,
++ IT8512_IER_IEC | IT8512_IER_RFOIE | IT8512_IER_RDAIE);
++ ite8709_write_register(dev, IT8512_REG_RCR, CFG_DCR);
++ ite8709_write_register(dev, IT8512_REG_MSTCR,
++ CFG_FIFOTL | IT8512_MSTCR_FIFOCLR);
++ ite8709_write_register(dev, IT8512_REG_RCR,
++ IT8512_RCR_RXEN | IT8512_RCR_RXACT | CFG_DCR);
++
++ spin_lock_irq(&dev->hardware_lock);
++ dev->device_busy = 0;
++ spin_unlock_irq(&dev->hardware_lock);
++
++ tasklet_enable(&dev->tasklet);
++}
++
++static void ite8709_drop_hardware(struct ite8709_device *dev)
++{
++ tasklet_disable(&dev->tasklet);
++
++ spin_lock_irq(&dev->hardware_lock);
++ dev->device_busy = 1;
++ spin_unlock_irq(&dev->hardware_lock);
++
++ ite8709_write_register(dev, IT8512_REG_RCR, 0);
++ ite8709_write_register(dev, IT8512_REG_MSTCR,
++ IT8512_MSTCR_RESET | IT8512_MSTCR_FIFOCLR);
++
++ spin_lock_irq(&dev->hardware_lock);
++ dev->device_busy = 0;
++ spin_unlock_irq(&dev->hardware_lock);
++}
++
++static int ite8709_set_use_inc(void *data)
++{
++ struct ite8709_device *dev;
++ dev = data;
++ if (dev->use_count == 0)
++ ite8709_init_hardware(dev);
++ dev->use_count++;
++ return 0;
++}
++
++static void ite8709_set_use_dec(void *data)
++{
++ struct ite8709_device *dev;
++ dev = data;
++ dev->use_count--;
++ if (dev->use_count == 0)
++ ite8709_drop_hardware(dev);
++}
++
++static void ite8709_add_read_queue(struct ite8709_device *dev, int flag,
++ unsigned long long val)
++{
++ int value;
++
++ dprintk("add a %llu usec %s\n", val, flag ? "pulse" : "space");
++
++ value = (val > PULSE_MASK) ? PULSE_MASK : val;
++ if (flag)
++ value |= PULSE_BIT;
++
++ if (!lirc_buffer_full(dev->driver.rbuf)) {
++ lirc_buffer_write(dev->driver.rbuf, (void *) &value);
++ wake_up(&dev->driver.rbuf->wait_poll);
++ }
++}
++
++static irqreturn_t ite8709_interrupt(int irq, void *dev_id)
++{
++ unsigned char data;
++ int iir, rfsr, i;
++ int fifo = 0;
++ char bit;
++ struct timeval curr_tv;
++
++ /* Bit duration in microseconds */
++ const unsigned long bit_duration = 1000000ul / (115200 / CFG_BDR);
++
++ struct ite8709_device *dev;
++ dev = dev_id;
++
++ /*
++ * If device is busy, we simply discard data because we are in one of
++ * these two cases : shutting down or rearming the device, so this
++ * doesn't really matter and this avoids waiting too long in IRQ ctx
++ */
++ spin_lock(&dev->hardware_lock);
++ if (dev->device_busy) {
++ spin_unlock(&dev->hardware_lock);
++ return IRQ_RETVAL(IRQ_HANDLED);
++ }
++
++ iir = ite8709_read(dev, ITE8709_IIR);
++
++ switch (iir) {
++ case ITE8709_IIR_RFOI:
++ dprintk("fifo overrun, scheduling forced rearm just in case\n");
++ dev->force_rearm = 1;
++ tasklet_schedule(&dev->tasklet);
++ spin_unlock(&dev->hardware_lock);
++ return IRQ_RETVAL(IRQ_HANDLED);
++
++ case ITE8709_IIR_RDAI:
++ rfsr = ite8709_read(dev, ITE8709_RFSR);
++ fifo = rfsr & ITE8709_RFSR_MASK;
++ if (fifo > 32)
++ fifo = 32;
++ dprintk("iir: 0x%x rfsr: 0x%x fifo: %d\n", iir, rfsr, fifo);
++
++ if (dev->rearmed) {
++ do_gettimeofday(&curr_tv);
++ dev->acc_space += 1000000ull
++ * (curr_tv.tv_sec - dev->last_tv.tv_sec)
++ + (curr_tv.tv_usec - dev->last_tv.tv_usec);
++ dev->rearmed = 0;
++ }
++ for (i = 0; i < fifo; i++) {
++ data = ite8709_read(dev, i+ITE8709_FIFO_START);
++ data = ~data;
++ /* Loop through */
++ for (bit = 0; bit < 8; ++bit) {
++ if ((data >> bit) & 1) {
++ dev->acc_pulse += bit_duration;
++ if (dev->lastbit == 0) {
++ ite8709_add_read_queue(dev, 0,
++ dev->acc_space);
++ dev->acc_space = 0;
++ }
++ } else {
++ dev->acc_space += bit_duration;
++ if (dev->lastbit == 1) {
++ ite8709_add_read_queue(dev, 1,
++ dev->acc_pulse);
++ dev->acc_pulse = 0;
++ }
++ }
++ dev->lastbit = (data >> bit) & 1;
++ }
++ }
++ ite8709_write(dev, ITE8709_RFSR, 0);
++
++ if (dev->acc_space > CFG_TIMEOUT) {
++ dprintk("scheduling rearm IRQ\n");
++ do_gettimeofday(&dev->last_tv);
++ dev->force_rearm = 0;
++ tasklet_schedule(&dev->tasklet);
++ }
++
++ spin_unlock(&dev->hardware_lock);
++ return IRQ_RETVAL(IRQ_HANDLED);
++
++ default:
++ /* not our irq */
++ dprintk("unknown IRQ (shouldn't happen) !!\n");
++ spin_unlock(&dev->hardware_lock);
++ return IRQ_RETVAL(IRQ_NONE);
++ }
++}
++
++static void ite8709_rearm_irq(unsigned long data)
++{
++ struct ite8709_device *dev;
++ unsigned long flags;
++ dev = (struct ite8709_device *) data;
++
++ spin_lock_irqsave(&dev->hardware_lock, flags);
++ dev->device_busy = 1;
++ spin_unlock_irqrestore(&dev->hardware_lock, flags);
++
++ if (dev->force_rearm || dev->acc_space > CFG_TIMEOUT) {
++ dprintk("rearming IRQ\n");
++ ite8709_write_register(dev, IT8512_REG_RCR,
++ IT8512_RCR_RXACT | CFG_DCR);
++ ite8709_write_register(dev, IT8512_REG_MSTCR,
++ CFG_FIFOTL | IT8512_MSTCR_FIFOCLR);
++ ite8709_write_register(dev, IT8512_REG_RCR,
++ IT8512_RCR_RXEN | IT8512_RCR_RXACT | CFG_DCR);
++ if (!dev->force_rearm)
++ dev->rearmed = 1;
++ dev->force_rearm = 0;
++ }
++
++ spin_lock_irqsave(&dev->hardware_lock, flags);
++ dev->device_busy = 0;
++ spin_unlock_irqrestore(&dev->hardware_lock, flags);
++}
++
++static int ite8709_cleanup(struct ite8709_device *dev, int stage, int errno,
++ char *msg)
++{
++ if (msg != NULL)
++ printk(KERN_ERR LIRC_DRIVER_NAME ": %s\n", msg);
++
++ switch (stage) {
++ case 6:
++ if (dev->use_count > 0)
++ ite8709_drop_hardware(dev);
++ case 5:
++ free_irq(dev->irq, dev);
++ case 4:
++ release_region(dev->io, 2);
++ case 3:
++ lirc_unregister_driver(dev->driver.minor);
++ case 2:
++ lirc_buffer_free(dev->driver.rbuf);
++ kfree(dev->driver.rbuf);
++ case 1:
++ kfree(dev);
++ case 0:
++ ;
++ }
++
++ return errno;
++}
++
++static int __devinit ite8709_pnp_probe(struct pnp_dev *dev,
++ const struct pnp_device_id *dev_id)
++{
++ struct lirc_driver *driver;
++ struct ite8709_device *ite8709_dev;
++ int ret;
++
++ /* Check resources validity */
++ if (!pnp_irq_valid(dev, 0))
++ return ite8709_cleanup(NULL, 0, -ENODEV, "invalid IRQ");
++ if (!pnp_port_valid(dev, 2))
++ return ite8709_cleanup(NULL, 0, -ENODEV, "invalid IO port");
++
++ /* Allocate memory for device struct */
++ ite8709_dev = kzalloc(sizeof(struct ite8709_device), GFP_KERNEL);
++ if (ite8709_dev == NULL)
++ return ite8709_cleanup(NULL, 0, -ENOMEM, "kzalloc failed");
++ pnp_set_drvdata(dev, ite8709_dev);
++
++ /* Initialize device struct */
++ ite8709_dev->use_count = 0;
++ ite8709_dev->irq = pnp_irq(dev, 0);
++ ite8709_dev->io = pnp_port_start(dev, 2);
++ ite8709_dev->hardware_lock =
++ __SPIN_LOCK_UNLOCKED(ite8709_dev->hardware_lock);
++ ite8709_dev->acc_pulse = 0;
++ ite8709_dev->acc_space = 0;
++ ite8709_dev->lastbit = 0;
++ do_gettimeofday(&ite8709_dev->last_tv);
++ tasklet_init(&ite8709_dev->tasklet, ite8709_rearm_irq,
++ (long) ite8709_dev);
++ ite8709_dev->force_rearm = 0;
++ ite8709_dev->rearmed = 0;
++ ite8709_dev->device_busy = 0;
++
++ /* Initialize driver struct */
++ driver = &ite8709_dev->driver;
++ strcpy(driver->name, LIRC_DRIVER_NAME);
++ driver->minor = -1;
++ driver->code_length = sizeof(int) * 8;
++ driver->sample_rate = 0;
++ driver->features = LIRC_CAN_REC_MODE2;
++ driver->data = ite8709_dev;
++ driver->add_to_buf = NULL;
++ driver->set_use_inc = ite8709_set_use_inc;
++ driver->set_use_dec = ite8709_set_use_dec;
++ driver->dev = &dev->dev;
++ driver->owner = THIS_MODULE;
++
++ /* Initialize LIRC buffer */
++ driver->rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
++ if (!driver->rbuf)
++ return ite8709_cleanup(ite8709_dev, 1, -ENOMEM,
++ "can't allocate lirc_buffer");
++ if (lirc_buffer_init(driver->rbuf, BUF_CHUNK_SIZE, BUF_SIZE))
++ return ite8709_cleanup(ite8709_dev, 1, -ENOMEM,
++ "lirc_buffer_init() failed");
++
++ /* Register LIRC driver */
++ ret = lirc_register_driver(driver);
++ if (ret < 0)
++ return ite8709_cleanup(ite8709_dev, 2, ret,
++ "lirc_register_driver() failed");
++
++ /* Reserve I/O port access */
++ if (!request_region(ite8709_dev->io, 2, LIRC_DRIVER_NAME))
++ return ite8709_cleanup(ite8709_dev, 3, -EBUSY,
++ "i/o port already in use");
++
++ /* Reserve IRQ line */
++ ret = request_irq(ite8709_dev->irq, ite8709_interrupt, 0,
++ LIRC_DRIVER_NAME, ite8709_dev);
++ if (ret < 0)
++ return ite8709_cleanup(ite8709_dev, 4, ret,
++ "IRQ already in use");
++
++ /* Initialize hardware */
++ ite8709_drop_hardware(ite8709_dev); /* Shutdown hw until first use */
++
++ printk(KERN_INFO LIRC_DRIVER_NAME ": device found : irq=%d io=0x%x\n",
++ ite8709_dev->irq, ite8709_dev->io);
++
++ return 0;
++}
++
++static void __devexit ite8709_pnp_remove(struct pnp_dev *dev)
++{
++ struct ite8709_device *ite8709_dev;
++ ite8709_dev = pnp_get_drvdata(dev);
++
++ ite8709_cleanup(ite8709_dev, 6, 0, NULL);
++
++ printk(KERN_INFO LIRC_DRIVER_NAME ": device removed\n");
++}
++
++#ifdef CONFIG_PM
++static int ite8709_pnp_suspend(struct pnp_dev *dev, pm_message_t state)
++{
++ struct ite8709_device *ite8709_dev;
++ ite8709_dev = pnp_get_drvdata(dev);
++
++ if (ite8709_dev->use_count > 0)
++ ite8709_drop_hardware(ite8709_dev);
++
++ return 0;
++}
++
++static int ite8709_pnp_resume(struct pnp_dev *dev)
++{
++ struct ite8709_device *ite8709_dev;
++ ite8709_dev = pnp_get_drvdata(dev);
++
++ if (ite8709_dev->use_count > 0)
++ ite8709_init_hardware(ite8709_dev);
++
++ return 0;
++}
++#else
++#define ite8709_pnp_suspend NULL
++#define ite8709_pnp_resume NULL
++#endif
++
++static const struct pnp_device_id pnp_dev_table[] = {
++ {"ITE8709", 0},
++ {}
++};
++
++MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
++
++static struct pnp_driver ite8709_pnp_driver = {
++ .name = LIRC_DRIVER_NAME,
++ .probe = ite8709_pnp_probe,
++ .remove = __devexit_p(ite8709_pnp_remove),
++ .suspend = ite8709_pnp_suspend,
++ .resume = ite8709_pnp_resume,
++ .id_table = pnp_dev_table,
++};
++
++static int __init ite8709_init_module(void)
++{
++ return pnp_register_driver(&ite8709_pnp_driver);
++}
++module_init(ite8709_init_module);
++
++static void __exit ite8709_cleanup_module(void)
++{
++ pnp_unregister_driver(&ite8709_pnp_driver);
++}
++module_exit(ite8709_cleanup_module);
++
++MODULE_DESCRIPTION("LIRC driver for ITE8709 CIR port");
++MODULE_AUTHOR("Grégory Lardière");
++MODULE_LICENSE("GPL");
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Enable debugging messages");
+diff --git a/drivers/staging/lirc/lirc_parallel.c b/drivers/staging/lirc/lirc_parallel.c
+new file mode 100644
+index 0000000..df12e7b
+--- /dev/null
++++ b/drivers/staging/lirc/lirc_parallel.c
+@@ -0,0 +1,705 @@
++/*
++ * lirc_parallel.c
++ *
++ * lirc_parallel - device driver for infra-red signal receiving and
++ * transmitting unit built by the author
++ *
++ * Copyright (C) 1998 Christoph Bartelmus <lirc@bartelmus.de>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++/*** Includes ***/
++
++#ifdef CONFIG_SMP
++#error "--- Sorry, this driver is not SMP safe. ---"
++#endif
++
++#include <linux/module.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/fs.h>
++#include <linux/kernel.h>
++#include <linux/ioport.h>
++#include <linux/time.h>
++#include <linux/mm.h>
++#include <linux/delay.h>
++
++#include <linux/io.h>
++#include <linux/signal.h>
++#include <linux/irq.h>
++#include <linux/uaccess.h>
++#include <asm/div64.h>
++
++#include <linux/poll.h>
++#include <linux/parport.h>
++
++#include <media/lirc.h>
++#include <media/lirc_dev.h>
++
++#include "lirc_parallel.h"
++
++#define LIRC_DRIVER_NAME "lirc_parallel"
++
++#ifndef LIRC_IRQ
++#define LIRC_IRQ 7
++#endif
++#ifndef LIRC_PORT
++#define LIRC_PORT 0x378
++#endif
++#ifndef LIRC_TIMER
++#define LIRC_TIMER 65536
++#endif
++
++/*** Global Variables ***/
++
++static int debug;
++static int check_pselecd;
++
++unsigned int irq = LIRC_IRQ;
++unsigned int io = LIRC_PORT;
++#ifdef LIRC_TIMER
++unsigned int timer;
++unsigned int default_timer = LIRC_TIMER;
++#endif
++
++#define RBUF_SIZE (256) /* this must be a power of 2 larger than 1 */
++
++static int rbuf[RBUF_SIZE];
++
++DECLARE_WAIT_QUEUE_HEAD(lirc_wait);
++
++unsigned int rptr;
++unsigned int wptr;
++unsigned int lost_irqs;
++int is_open;
++
++struct parport *pport;
++struct pardevice *ppdevice;
++int is_claimed;
++
++unsigned int tx_mask = 1;
++
++/*** Internal Functions ***/
++
++static unsigned int in(int offset)
++{
++ switch (offset) {
++ case LIRC_LP_BASE:
++ return parport_read_data(pport);
++ case LIRC_LP_STATUS:
++ return parport_read_status(pport);
++ case LIRC_LP_CONTROL:
++ return parport_read_control(pport);
++ }
++ return 0; /* make compiler happy */
++}
++
++static void out(int offset, int value)
++{
++ switch (offset) {
++ case LIRC_LP_BASE:
++ parport_write_data(pport, value);
++ break;
++ case LIRC_LP_CONTROL:
++ parport_write_control(pport, value);
++ break;
++ case LIRC_LP_STATUS:
++ printk(KERN_INFO "%s: attempt to write to status register\n",
++ LIRC_DRIVER_NAME);
++ break;
++ }
++}
++
++static unsigned int lirc_get_timer(void)
++{
++ return in(LIRC_PORT_TIMER) & LIRC_PORT_TIMER_BIT;
++}
++
++static unsigned int lirc_get_signal(void)
++{
++ return in(LIRC_PORT_SIGNAL) & LIRC_PORT_SIGNAL_BIT;
++}
++
++static void lirc_on(void)
++{
++ out(LIRC_PORT_DATA, tx_mask);
++}
++
++static void lirc_off(void)
++{
++ out(LIRC_PORT_DATA, 0);
++}
++
++static unsigned int init_lirc_timer(void)
++{
++ struct timeval tv, now;
++ unsigned int level, newlevel, timeelapsed, newtimer;
++ int count = 0;
++
++ do_gettimeofday(&tv);
++ tv.tv_sec++; /* wait max. 1 sec. */
++ level = lirc_get_timer();
++ do {
++ newlevel = lirc_get_timer();
++ if (level == 0 && newlevel != 0)
++ count++;
++ level = newlevel;
++ do_gettimeofday(&now);
++ } while (count < 1000 && (now.tv_sec < tv.tv_sec
++ || (now.tv_sec == tv.tv_sec
++ && now.tv_usec < tv.tv_usec)));
++
++ timeelapsed = ((now.tv_sec + 1 - tv.tv_sec)*1000000
++ + (now.tv_usec - tv.tv_usec));
++ if (count >= 1000 && timeelapsed > 0) {
++ if (default_timer == 0) {
++ /* autodetect timer */
++ newtimer = (1000000*count)/timeelapsed;
++ printk(KERN_INFO "%s: %u Hz timer detected\n",
++ LIRC_DRIVER_NAME, newtimer);
++ return newtimer;
++ } else {
++ newtimer = (1000000*count)/timeelapsed;
++ if (abs(newtimer - default_timer) > default_timer/10) {
++ /* bad timer */
++ printk(KERN_NOTICE "%s: bad timer: %u Hz\n",
++ LIRC_DRIVER_NAME, newtimer);
++ printk(KERN_NOTICE "%s: using default timer: "
++ "%u Hz\n",
++ LIRC_DRIVER_NAME, default_timer);
++ return default_timer;
++ } else {
++ printk(KERN_INFO "%s: %u Hz timer detected\n",
++ LIRC_DRIVER_NAME, newtimer);
++ return newtimer; /* use detected value */
++ }
++ }
++ } else {
++ printk(KERN_NOTICE "%s: no timer detected\n", LIRC_DRIVER_NAME);
++ return 0;
++ }
++}
++
++static int lirc_claim(void)
++{
++ if (parport_claim(ppdevice) != 0) {
++ printk(KERN_WARNING "%s: could not claim port\n",
++ LIRC_DRIVER_NAME);
++ printk(KERN_WARNING "%s: waiting for port becoming available"
++ "\n", LIRC_DRIVER_NAME);
++ if (parport_claim_or_block(ppdevice) < 0) {
++ printk(KERN_NOTICE "%s: could not claim port, giving"
++ " up\n", LIRC_DRIVER_NAME);
++ return 0;
++ }
++ }
++ out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
++ is_claimed = 1;
++ return 1;
++}
++
++/*** interrupt handler ***/
++
++static void rbuf_write(int signal)
++{
++ unsigned int nwptr;
++
++ nwptr = (wptr + 1) & (RBUF_SIZE - 1);
++ if (nwptr == rptr) {
++ /* no new signals will be accepted */
++ lost_irqs++;
++ printk(KERN_NOTICE "%s: buffer overrun\n", LIRC_DRIVER_NAME);
++ return;
++ }
++ rbuf[wptr] = signal;
++ wptr = nwptr;
++}
++
++static void irq_handler(void *blah)
++{
++ struct timeval tv;
++ static struct timeval lasttv;
++ static int init;
++ long signal;
++ int data;
++ unsigned int level, newlevel;
++ unsigned int timeout;
++
++ if (!module_refcount(THIS_MODULE))
++ return;
++
++ if (!is_claimed)
++ return;
++
++#if 0
++ /* disable interrupt */
++ disable_irq(irq);
++ out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ) & (~LP_PINTEN));
++#endif
++ if (check_pselecd && (in(1) & LP_PSELECD))
++ return;
++
++#ifdef LIRC_TIMER
++ if (init) {
++ do_gettimeofday(&tv);
++
++ signal = tv.tv_sec - lasttv.tv_sec;
++ if (signal > 15)
++ /* really long time */
++ data = PULSE_MASK;
++ else
++ data = (int) (signal*1000000 +
++ tv.tv_usec - lasttv.tv_usec +
++ LIRC_SFH506_DELAY);
++
++ rbuf_write(data); /* space */
++ } else {
++ if (timer == 0) {
++ /*
++ * wake up; we'll lose this signal, but it will be
++ * garbage if the device is turned on anyway
++ */
++ timer = init_lirc_timer();
++ /* enable_irq(irq); */
++ return;
++ }
++ init = 1;
++ }
++
++ timeout = timer/10; /* timeout after 1/10 sec. */
++ signal = 1;
++ level = lirc_get_timer();
++ do {
++ newlevel = lirc_get_timer();
++ if (level == 0 && newlevel != 0)
++ signal++;
++ level = newlevel;
++
++ /* giving up */
++ if (signal > timeout
++ || (check_pselecd && (in(1) & LP_PSELECD))) {
++ signal = 0;
++ printk(KERN_NOTICE "%s: timeout\n", LIRC_DRIVER_NAME);
++ break;
++ }
++ } while (lirc_get_signal());
++
++ if (signal != 0) {
++ /* ajust value to usecs */
++ unsigned long long helper;
++
++ helper = ((unsigned long long) signal)*1000000;
++ do_div(helper, timer);
++ signal = (long) helper;
++
++ if (signal > LIRC_SFH506_DELAY)
++ data = signal - LIRC_SFH506_DELAY;
++ else
++ data = 1;
++ rbuf_write(PULSE_BIT|data); /* pulse */
++ }
++ do_gettimeofday(&lasttv);
++#else
++ /* add your code here */
++#endif
++
++ wake_up_interruptible(&lirc_wait);
++
++ /* enable interrupt */
++ /*
++ enable_irq(irq);
++ out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ)|LP_PINTEN);
++ */
++}
++
++/*** file operations ***/
++
++static loff_t lirc_lseek(struct file *filep, loff_t offset, int orig)
++{
++ return -ESPIPE;
++}
++
++static ssize_t lirc_read(struct file *filep, char *buf, size_t n, loff_t *ppos)
++{
++ int result = 0;
++ int count = 0;
++ DECLARE_WAITQUEUE(wait, current);
++
++ if (n % sizeof(int))
++ return -EINVAL;
++
++ add_wait_queue(&lirc_wait, &wait);
++ set_current_state(TASK_INTERRUPTIBLE);
++ while (count < n) {
++ if (rptr != wptr) {
++ if (copy_to_user(buf+count, (char *) &rbuf[rptr],
++ sizeof(int))) {
++ result = -EFAULT;
++ break;
++ }
++ rptr = (rptr + 1) & (RBUF_SIZE - 1);
++ count += sizeof(int);
++ } else {
++ if (filep->f_flags & O_NONBLOCK) {
++ result = -EAGAIN;
++ break;
++ }
++ if (signal_pending(current)) {
++ result = -ERESTARTSYS;
++ break;
++ }
++ schedule();
++ set_current_state(TASK_INTERRUPTIBLE);
++ }
++ }
++ remove_wait_queue(&lirc_wait, &wait);
++ set_current_state(TASK_RUNNING);
++ return count ? count : result;
++}
++
++static ssize_t lirc_write(struct file *filep, const char *buf, size_t n,
++ loff_t *ppos)
++{
++ int count;
++ unsigned int i;
++ unsigned int level, newlevel;
++ unsigned long flags;
++ int counttimer;
++ int *wbuf;
++
++ if (!is_claimed)
++ return -EBUSY;
++
++ count = n / sizeof(int);
++
++ if (n % sizeof(int) || count % 2 == 0)
++ return -EINVAL;
++
++ wbuf = memdup_user(buf, n);
++ if (IS_ERR(wbuf))
++ return PTR_ERR(wbuf);
++
++#ifdef LIRC_TIMER
++ if (timer == 0) {
++ /* try again if device is ready */
++ timer = init_lirc_timer();
++ if (timer == 0)
++ return -EIO;
++ }
++
++ /* adjust values from usecs */
++ for (i = 0; i < count; i++) {
++ unsigned long long helper;
++
++ helper = ((unsigned long long) wbuf[i])*timer;
++ do_div(helper, 1000000);
++ wbuf[i] = (int) helper;
++ }
++
++ local_irq_save(flags);
++ i = 0;
++ while (i < count) {
++ level = lirc_get_timer();
++ counttimer = 0;
++ lirc_on();
++ do {
++ newlevel = lirc_get_timer();
++ if (level == 0 && newlevel != 0)
++ counttimer++;
++ level = newlevel;
++ if (check_pselecd && (in(1) & LP_PSELECD)) {
++ lirc_off();
++ local_irq_restore(flags);
++ return -EIO;
++ }
++ } while (counttimer < wbuf[i]);
++ i++;
++
++ lirc_off();
++ if (i == count)
++ break;
++ counttimer = 0;
++ do {
++ newlevel = lirc_get_timer();
++ if (level == 0 && newlevel != 0)
++ counttimer++;
++ level = newlevel;
++ if (check_pselecd && (in(1) & LP_PSELECD)) {
++ local_irq_restore(flags);
++ return -EIO;
++ }
++ } while (counttimer < wbuf[i]);
++ i++;
++ }
++ local_irq_restore(flags);
++#else
++ /* place code that handles write without external timer here */
++#endif
++ return n;
++}
++
++static unsigned int lirc_poll(struct file *file, poll_table *wait)
++{
++ poll_wait(file, &lirc_wait, wait);
++ if (rptr != wptr)
++ return POLLIN | POLLRDNORM;
++ return 0;
++}
++
++static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
++{
++ int result;
++ unsigned long features = LIRC_CAN_SET_TRANSMITTER_MASK |
++ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
++ unsigned long mode;
++ unsigned int ivalue;
++
++ switch (cmd) {
++ case LIRC_GET_FEATURES:
++ result = put_user(features, (unsigned long *) arg);
++ if (result)
++ return result;
++ break;
++ case LIRC_GET_SEND_MODE:
++ result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg);
++ if (result)
++ return result;
++ break;
++ case LIRC_GET_REC_MODE:
++ result = put_user(LIRC_MODE_MODE2, (unsigned long *) arg);
++ if (result)
++ return result;
++ break;
++ case LIRC_SET_SEND_MODE:
++ result = get_user(mode, (unsigned long *) arg);
++ if (result)
++ return result;
++ if (mode != LIRC_MODE_PULSE)
++ return -EINVAL;
++ break;
++ case LIRC_SET_REC_MODE:
++ result = get_user(mode, (unsigned long *) arg);
++ if (result)
++ return result;
++ if (mode != LIRC_MODE_MODE2)
++ return -ENOSYS;
++ break;
++ case LIRC_SET_TRANSMITTER_MASK:
++ result = get_user(ivalue, (unsigned int *) arg);
++ if (result)
++ return result;
++ if ((ivalue & LIRC_PARALLEL_TRANSMITTER_MASK) != ivalue)
++ return LIRC_PARALLEL_MAX_TRANSMITTERS;
++ tx_mask = ivalue;
++ break;
++ default:
++ return -ENOIOCTLCMD;
++ }
++ return 0;
++}
++
++static int lirc_open(struct inode *node, struct file *filep)
++{
++ if (module_refcount(THIS_MODULE) || !lirc_claim())
++ return -EBUSY;
++
++ parport_enable_irq(pport);
++
++ /* init read ptr */
++ rptr = 0;
++ wptr = 0;
++ lost_irqs = 0;
++
++ is_open = 1;
++ return 0;
++}
++
++static int lirc_close(struct inode *node, struct file *filep)
++{
++ if (is_claimed) {
++ is_claimed = 0;
++ parport_release(ppdevice);
++ }
++ is_open = 0;
++ return 0;
++}
++
++static struct file_operations lirc_fops = {
++ .owner = THIS_MODULE,
++ .llseek = lirc_lseek,
++ .read = lirc_read,
++ .write = lirc_write,
++ .poll = lirc_poll,
++ .unlocked_ioctl = lirc_ioctl,
++ .open = lirc_open,
++ .release = lirc_close
++};
++
++static int set_use_inc(void *data)
++{
++ return 0;
++}
++
++static void set_use_dec(void *data)
++{
++}
++
++static struct lirc_driver driver = {
++ .name = LIRC_DRIVER_NAME,
++ .minor = -1,
++ .code_length = 1,
++ .sample_rate = 0,
++ .data = NULL,
++ .add_to_buf = NULL,
++ .set_use_inc = set_use_inc,
++ .set_use_dec = set_use_dec,
++ .fops = &lirc_fops,
++ .dev = NULL,
++ .owner = THIS_MODULE,
++};
++
++static int pf(void *handle);
++static void kf(void *handle);
++
++static struct timer_list poll_timer;
++static void poll_state(unsigned long ignored);
++
++static void poll_state(unsigned long ignored)
++{
++ printk(KERN_NOTICE "%s: time\n",
++ LIRC_DRIVER_NAME);
++ del_timer(&poll_timer);
++ if (is_claimed)
++ return;
++ kf(NULL);
++ if (!is_claimed) {
++ printk(KERN_NOTICE "%s: could not claim port, giving up\n",
++ LIRC_DRIVER_NAME);
++ init_timer(&poll_timer);
++ poll_timer.expires = jiffies + HZ;
++ poll_timer.data = (unsigned long)current;
++ poll_timer.function = poll_state;
++ add_timer(&poll_timer);
++ }
++}
++
++static int pf(void *handle)
++{
++ parport_disable_irq(pport);
++ is_claimed = 0;
++ return 0;
++}
++
++static void kf(void *handle)
++{
++ if (!is_open)
++ return;
++ if (!lirc_claim())
++ return;
++ parport_enable_irq(pport);
++ lirc_off();
++ /* this is a bit annoying when you actually print...*/
++ /*
++ printk(KERN_INFO "%s: reclaimed port\n", LIRC_DRIVER_NAME);
++ */
++}
++
++/*** module initialization and cleanup ***/
++
++static int __init lirc_parallel_init(void)
++{
++ pport = parport_find_base(io);
++ if (pport == NULL) {
++ printk(KERN_NOTICE "%s: no port at %x found\n",
++ LIRC_DRIVER_NAME, io);
++ return -ENXIO;
++ }
++ ppdevice = parport_register_device(pport, LIRC_DRIVER_NAME,
++ pf, kf, irq_handler, 0, NULL);
++ parport_put_port(pport);
++ if (ppdevice == NULL) {
++ printk(KERN_NOTICE "%s: parport_register_device() failed\n",
++ LIRC_DRIVER_NAME);
++ return -ENXIO;
++ }
++ if (parport_claim(ppdevice) != 0)
++ goto skip_init;
++ is_claimed = 1;
++ out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
++
++#ifdef LIRC_TIMER
++ if (debug)
++ out(LIRC_PORT_DATA, tx_mask);
++
++ timer = init_lirc_timer();
++
++#if 0 /* continue even if device is offline */
++ if (timer == 0) {
++ is_claimed = 0;
++ parport_release(pport);
++ parport_unregister_device(ppdevice);
++ return -EIO;
++ }
++
++#endif
++ if (debug)
++ out(LIRC_PORT_DATA, 0);
++#endif
++
++ is_claimed = 0;
++ parport_release(ppdevice);
++ skip_init:
++ driver.minor = lirc_register_driver(&driver);
++ if (driver.minor < 0) {
++ printk(KERN_NOTICE "%s: register_chrdev() failed\n",
++ LIRC_DRIVER_NAME);
++ parport_unregister_device(ppdevice);
++ return -EIO;
++ }
++ printk(KERN_INFO "%s: installed using port 0x%04x irq %d\n",
++ LIRC_DRIVER_NAME, io, irq);
++ return 0;
++}
++
++static void __exit lirc_parallel_exit(void)
++{
++ parport_unregister_device(ppdevice);
++ lirc_unregister_driver(driver.minor);
++}
++
++module_init(lirc_parallel_init);
++module_exit(lirc_parallel_exit);
++
++MODULE_DESCRIPTION("Infrared receiver driver for parallel ports.");
++MODULE_AUTHOR("Christoph Bartelmus");
++MODULE_LICENSE("GPL");
++
++module_param(io, int, S_IRUGO);
++MODULE_PARM_DESC(io, "I/O address base (0x3bc, 0x378 or 0x278)");
++
++module_param(irq, int, S_IRUGO);
++MODULE_PARM_DESC(irq, "Interrupt (7 or 5)");
++
++module_param(tx_mask, int, S_IRUGO);
++MODULE_PARM_DESC(tx_maxk, "Transmitter mask (default: 0x01)");
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Enable debugging messages");
++
++module_param(check_pselecd, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Check for printer (default: 0)");
+diff --git a/drivers/staging/lirc/lirc_parallel.h b/drivers/staging/lirc/lirc_parallel.h
+new file mode 100644
+index 0000000..4bed6af
+--- /dev/null
++++ b/drivers/staging/lirc/lirc_parallel.h
+@@ -0,0 +1,26 @@
++/* lirc_parallel.h */
++
++#ifndef _LIRC_PARALLEL_H
++#define _LIRC_PARALLEL_H
++
++#include <linux/lp.h>
++
++#define LIRC_PORT_LEN 3
++
++#define LIRC_LP_BASE 0
++#define LIRC_LP_STATUS 1
++#define LIRC_LP_CONTROL 2
++
++#define LIRC_PORT_DATA LIRC_LP_BASE /* base */
++#define LIRC_PORT_TIMER LIRC_LP_STATUS /* status port */
++#define LIRC_PORT_TIMER_BIT LP_PBUSY /* busy signal */
++#define LIRC_PORT_SIGNAL LIRC_LP_STATUS /* status port */
++#define LIRC_PORT_SIGNAL_BIT LP_PACK /* ack signal */
++#define LIRC_PORT_IRQ LIRC_LP_CONTROL /* control port */
++
++#define LIRC_SFH506_DELAY 0 /* delay t_phl in usecs */
++
++#define LIRC_PARALLEL_MAX_TRANSMITTERS 8
++#define LIRC_PARALLEL_TRANSMITTER_MASK ((1<<LIRC_PARALLEL_MAX_TRANSMITTERS) - 1)
++
++#endif
+diff --git a/drivers/staging/lirc/lirc_sasem.c b/drivers/staging/lirc/lirc_sasem.c
+new file mode 100644
+index 0000000..9e516a1
+--- /dev/null
++++ b/drivers/staging/lirc/lirc_sasem.c
+@@ -0,0 +1,933 @@
++/*
++ * lirc_sasem.c - USB remote support for LIRC
++ * Version 0.5
++ *
++ * Copyright (C) 2004-2005 Oliver Stabel <oliver.stabel@gmx.de>
++ * Tim Davies <tim@opensystems.net.au>
++ *
++ * This driver was derived from:
++ * Venky Raju <dev@venky.ws>
++ * "lirc_imon - "LIRC/VFD driver for Ahanix/Soundgraph IMON IR/VFD"
++ * Paul Miller <pmiller9@users.sourceforge.net>'s 2003-2004
++ * "lirc_atiusb - USB remote support for LIRC"
++ * Culver Consulting Services <henry@culcon.com>'s 2003
++ * "Sasem OnAir VFD/IR USB driver"
++ *
++ *
++ * NOTE - The LCDproc iMon driver should work with this module. More info at
++ * http://www.frogstorm.info/sasem
++ */
++
++/*
++ * 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/errno.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/uaccess.h>
++#include <linux/usb.h>
++
++#include <media/lirc.h>
++#include <media/lirc_dev.h>
++
++
++#define MOD_AUTHOR "Oliver Stabel <oliver.stabel@gmx.de>, " \
++ "Tim Davies <tim@opensystems.net.au>"
++#define MOD_DESC "USB Driver for Sasem Remote Controller V1.1"
++#define MOD_NAME "lirc_sasem"
++#define MOD_VERSION "0.5"
++
++#define VFD_MINOR_BASE 144 /* Same as LCD */
++#define DEVICE_NAME "lcd%d"
++
++#define BUF_CHUNK_SIZE 8
++#define BUF_SIZE 128
++
++#define IOCTL_LCD_CONTRAST 1
++
++/*** P R O T O T Y P E S ***/
++
++/* USB Callback prototypes */
++static int sasem_probe(struct usb_interface *interface,
++ const struct usb_device_id *id);
++static void sasem_disconnect(struct usb_interface *interface);
++static void usb_rx_callback(struct urb *urb);
++static void usb_tx_callback(struct urb *urb);
++
++/* VFD file_operations function prototypes */
++static int vfd_open(struct inode *inode, struct file *file);
++static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg);
++static int vfd_close(struct inode *inode, struct file *file);
++static ssize_t vfd_write(struct file *file, const char *buf,
++ size_t n_bytes, loff_t *pos);
++
++/* LIRC driver function prototypes */
++static int ir_open(void *data);
++static void ir_close(void *data);
++
++/* Driver init/exit prototypes */
++static int __init sasem_init(void);
++static void __exit sasem_exit(void);
++
++/*** G L O B A L S ***/
++#define SASEM_DATA_BUF_SZ 32
++
++struct sasem_context {
++
++ struct usb_device *dev;
++ int vfd_isopen; /* VFD port has been opened */
++ unsigned int vfd_contrast; /* VFD contrast */
++ int ir_isopen; /* IR port has been opened */
++ int dev_present; /* USB device presence */
++ struct mutex ctx_lock; /* to lock this object */
++ wait_queue_head_t remove_ok; /* For unexpected USB disconnects */
++
++ struct lirc_driver *driver;
++ struct usb_endpoint_descriptor *rx_endpoint;
++ struct usb_endpoint_descriptor *tx_endpoint;
++ struct urb *rx_urb;
++ struct urb *tx_urb;
++ unsigned char usb_rx_buf[8];
++ unsigned char usb_tx_buf[8];
++
++ struct tx_t {
++ unsigned char data_buf[SASEM_DATA_BUF_SZ]; /* user data buffer */
++ struct completion finished; /* wait for write to finish */
++ atomic_t busy; /* write in progress */
++ int status; /* status of tx completion */
++ } tx;
++
++ /* for dealing with repeat codes (wish there was a toggle bit!) */
++ struct timeval presstime;
++ char lastcode[8];
++ int codesaved;
++};
++
++/* VFD file operations */
++static struct file_operations vfd_fops = {
++ .owner = THIS_MODULE,
++ .open = &vfd_open,
++ .write = &vfd_write,
++ .unlocked_ioctl = &vfd_ioctl,
++ .release = &vfd_close,
++};
++
++/* USB Device ID for Sasem USB Control Board */
++static struct usb_device_id sasem_usb_id_table[] = {
++ /* Sasem USB Control Board */
++ { USB_DEVICE(0x11ba, 0x0101) },
++ /* Terminating entry */
++ {}
++};
++
++/* USB Device data */
++static struct usb_driver sasem_driver = {
++ .name = MOD_NAME,
++ .probe = sasem_probe,
++ .disconnect = sasem_disconnect,
++ .id_table = sasem_usb_id_table,
++};
++
++static struct usb_class_driver sasem_class = {
++ .name = DEVICE_NAME,
++ .fops = &vfd_fops,
++ .minor_base = VFD_MINOR_BASE,
++};
++
++/* to prevent races between open() and disconnect() */
++static DEFINE_MUTEX(disconnect_lock);
++
++static int debug;
++
++
++/*** M O D U L E C O D E ***/
++
++MODULE_AUTHOR(MOD_AUTHOR);
++MODULE_DESCRIPTION(MOD_DESC);
++MODULE_LICENSE("GPL");
++module_param(debug, int, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes (default: no)");
++
++static void delete_context(struct sasem_context *context)
++{
++ usb_free_urb(context->tx_urb); /* VFD */
++ usb_free_urb(context->rx_urb); /* IR */
++ lirc_buffer_free(context->driver->rbuf);
++ kfree(context->driver->rbuf);
++ kfree(context->driver);
++ kfree(context);
++
++ if (debug)
++ printk(KERN_INFO "%s: context deleted\n", __func__);
++}
++
++static void deregister_from_lirc(struct sasem_context *context)
++{
++ int retval;
++ int minor = context->driver->minor;
++
++ retval = lirc_unregister_driver(minor);
++ if (retval)
++ err("%s: unable to deregister from lirc (%d)",
++ __func__, retval);
++ else
++ printk(KERN_INFO "Deregistered Sasem driver (minor:%d)\n",
++ minor);
++
++}
++
++/**
++ * Called when the VFD device (e.g. /dev/usb/lcd)
++ * is opened by the application.
++ */
++static int vfd_open(struct inode *inode, struct file *file)
++{
++ struct usb_interface *interface;
++ struct sasem_context *context = NULL;
++ int subminor;
++ int retval = 0;
++
++ /* prevent races with disconnect */
++ mutex_lock(&disconnect_lock);
++
++ subminor = iminor(inode);
++ interface = usb_find_interface(&sasem_driver, subminor);
++ if (!interface) {
++ err("%s: could not find interface for minor %d",
++ __func__, subminor);
++ retval = -ENODEV;
++ goto exit;
++ }
++ context = usb_get_intfdata(interface);
++
++ if (!context) {
++ err("%s: no context found for minor %d",
++ __func__, subminor);
++ retval = -ENODEV;
++ goto exit;
++ }
++
++ mutex_lock(&context->ctx_lock);
++
++ if (context->vfd_isopen) {
++ err("%s: VFD port is already open", __func__);
++ retval = -EBUSY;
++ } else {
++ context->vfd_isopen = 1;
++ file->private_data = context;
++ printk(KERN_INFO "VFD port opened\n");
++ }
++
++ mutex_unlock(&context->ctx_lock);
++
++exit:
++ mutex_unlock(&disconnect_lock);
++ return retval;
++}
++
++/**
++ * Called when the VFD device (e.g. /dev/usb/lcd)
++ * is closed by the application.
++ */
++static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg)
++{
++ struct sasem_context *context = NULL;
++
++ context = (struct sasem_context *) file->private_data;
++
++ if (!context) {
++ err("%s: no context for device", __func__);
++ return -ENODEV;
++ }
++
++ mutex_lock(&context->ctx_lock);
++
++ switch (cmd) {
++ case IOCTL_LCD_CONTRAST:
++ if (arg > 1000)
++ arg = 1000;
++ context->vfd_contrast = (unsigned int)arg;
++ break;
++ default:
++ printk(KERN_INFO "Unknown IOCTL command\n");
++ mutex_unlock(&context->ctx_lock);
++ return -ENOIOCTLCMD; /* not supported */
++ }
++
++ mutex_unlock(&context->ctx_lock);
++ return 0;
++}
++
++/**
++ * Called when the VFD device (e.g. /dev/usb/lcd)
++ * is closed by the application.
++ */
++static int vfd_close(struct inode *inode, struct file *file)
++{
++ struct sasem_context *context = NULL;
++ int retval = 0;
++
++ context = (struct sasem_context *) file->private_data;
++
++ if (!context) {
++ err("%s: no context for device", __func__);
++ return -ENODEV;
++ }
++
++ mutex_lock(&context->ctx_lock);
++
++ if (!context->vfd_isopen) {
++ err("%s: VFD is not open", __func__);
++ retval = -EIO;
++ } else {
++ context->vfd_isopen = 0;
++ printk(KERN_INFO "VFD port closed\n");
++ if (!context->dev_present && !context->ir_isopen) {
++
++ /* Device disconnected before close and IR port is
++ * not open. If IR port is open, context will be
++ * deleted by ir_close. */
++ mutex_unlock(&context->ctx_lock);
++ delete_context(context);
++ return retval;
++ }
++ }
++
++ mutex_unlock(&context->ctx_lock);
++ return retval;
++}
++
++/**
++ * Sends a packet to the VFD.
++ */
++static int send_packet(struct sasem_context *context)
++{
++ unsigned int pipe;
++ int interval = 0;
++ int retval = 0;
++
++ pipe = usb_sndintpipe(context->dev,
++ context->tx_endpoint->bEndpointAddress);
++ interval = context->tx_endpoint->bInterval;
++
++ usb_fill_int_urb(context->tx_urb, context->dev, pipe,
++ context->usb_tx_buf, sizeof(context->usb_tx_buf),
++ usb_tx_callback, context, interval);
++
++ context->tx_urb->actual_length = 0;
++
++ init_completion(&context->tx.finished);
++ atomic_set(&(context->tx.busy), 1);
++
++ retval = usb_submit_urb(context->tx_urb, GFP_KERNEL);
++ if (retval) {
++ atomic_set(&(context->tx.busy), 0);
++ err("%s: error submitting urb (%d)", __func__, retval);
++ } else {
++ /* Wait for transmission to complete (or abort) */
++ mutex_unlock(&context->ctx_lock);
++ wait_for_completion(&context->tx.finished);
++ mutex_lock(&context->ctx_lock);
++
++ retval = context->tx.status;
++ if (retval)
++ err("%s: packet tx failed (%d)", __func__, retval);
++ }
++
++ return retval;
++}
++
++/**
++ * Writes data to the VFD. The Sasem VFD is 2x16 characters
++ * and requires data in 9 consecutive USB interrupt packets,
++ * each packet carrying 8 bytes.
++ */
++static ssize_t vfd_write(struct file *file, const char *buf,
++ size_t n_bytes, loff_t *pos)
++{
++ int i;
++ int retval = 0;
++ struct sasem_context *context;
++ int *data_buf;
++
++ context = (struct sasem_context *) file->private_data;
++ if (!context) {
++ err("%s: no context for device", __func__);
++ return -ENODEV;
++ }
++
++ mutex_lock(&context->ctx_lock);
++
++ if (!context->dev_present) {
++ err("%s: no Sasem device present", __func__);
++ retval = -ENODEV;
++ goto exit;
++ }
++
++ if (n_bytes <= 0 || n_bytes > SASEM_DATA_BUF_SZ) {
++ err("%s: invalid payload size", __func__);
++ retval = -EINVAL;
++ goto exit;
++ }
++
++ data_buf = memdup_user(buf, n_bytes);
++ if (PTR_ERR(data_buf))
++ return PTR_ERR(data_buf);
++
++ memcpy(context->tx.data_buf, data_buf, n_bytes);
++
++ /* Pad with spaces */
++ for (i = n_bytes; i < SASEM_DATA_BUF_SZ; ++i)
++ context->tx.data_buf[i] = ' ';
++
++ /* Nine 8 byte packets to be sent */
++ /* NOTE: "\x07\x01\0\0\0\0\0\0" or "\x0c\0\0\0\0\0\0\0"
++ * will clear the VFD */
++ for (i = 0; i < 9; i++) {
++ switch (i) {
++ case 0:
++ memcpy(context->usb_tx_buf, "\x07\0\0\0\0\0\0\0", 8);
++ context->usb_tx_buf[1] = (context->vfd_contrast) ?
++ (0x2B - (context->vfd_contrast - 1) / 250)
++ : 0x2B;
++ break;
++ case 1:
++ memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
++ break;
++ case 2:
++ memcpy(context->usb_tx_buf, "\x0b\x01\0\0\0\0\0\0", 8);
++ break;
++ case 3:
++ memcpy(context->usb_tx_buf, context->tx.data_buf, 8);
++ break;
++ case 4:
++ memcpy(context->usb_tx_buf,
++ context->tx.data_buf + 8, 8);
++ break;
++ case 5:
++ memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
++ break;
++ case 6:
++ memcpy(context->usb_tx_buf, "\x0b\x02\0\0\0\0\0\0", 8);
++ break;
++ case 7:
++ memcpy(context->usb_tx_buf,
++ context->tx.data_buf + 16, 8);
++ break;
++ case 8:
++ memcpy(context->usb_tx_buf,
++ context->tx.data_buf + 24, 8);
++ break;
++ }
++ retval = send_packet(context);
++ if (retval) {
++
++ err("%s: send packet failed for packet #%d",
++ __func__, i);
++ goto exit;
++ }
++ }
++exit:
++
++ mutex_unlock(&context->ctx_lock);
++
++ return (!retval) ? n_bytes : retval;
++}
++
++/**
++ * Callback function for USB core API: transmit data
++ */
++static void usb_tx_callback(struct urb *urb)
++{
++ struct sasem_context *context;
++
++ if (!urb)
++ return;
++ context = (struct sasem_context *) urb->context;
++ if (!context)
++ return;
++
++ context->tx.status = urb->status;
++
++ /* notify waiters that write has finished */
++ atomic_set(&context->tx.busy, 0);
++ complete(&context->tx.finished);
++
++ return;
++}
++
++/**
++ * Called by lirc_dev when the application opens /dev/lirc
++ */
++static int ir_open(void *data)
++{
++ int retval = 0;
++ struct sasem_context *context;
++
++ /* prevent races with disconnect */
++ mutex_lock(&disconnect_lock);
++
++ context = (struct sasem_context *) data;
++
++ mutex_lock(&context->ctx_lock);
++
++ if (context->ir_isopen) {
++ err("%s: IR port is already open", __func__);
++ retval = -EBUSY;
++ goto exit;
++ }
++
++ usb_fill_int_urb(context->rx_urb, context->dev,
++ usb_rcvintpipe(context->dev,
++ context->rx_endpoint->bEndpointAddress),
++ context->usb_rx_buf, sizeof(context->usb_rx_buf),
++ usb_rx_callback, context, context->rx_endpoint->bInterval);
++
++ retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
++
++ if (retval)
++ err("%s: usb_submit_urb failed for ir_open (%d)",
++ __func__, retval);
++ else {
++ context->ir_isopen = 1;
++ printk(KERN_INFO "IR port opened\n");
++ }
++
++exit:
++ mutex_unlock(&context->ctx_lock);
++
++ mutex_unlock(&disconnect_lock);
++ return 0;
++}
++
++/**
++ * Called by lirc_dev when the application closes /dev/lirc
++ */
++static void ir_close(void *data)
++{
++ struct sasem_context *context;
++
++ context = (struct sasem_context *)data;
++ if (!context) {
++ err("%s: no context for device", __func__);
++ return;
++ }
++
++ mutex_lock(&context->ctx_lock);
++
++ usb_kill_urb(context->rx_urb);
++ context->ir_isopen = 0;
++ printk(KERN_INFO "IR port closed\n");
++
++ if (!context->dev_present) {
++
++ /*
++ * Device disconnected while IR port was
++ * still open. Driver was not deregistered
++ * at disconnect time, so do it now.
++ */
++ deregister_from_lirc(context);
++
++ if (!context->vfd_isopen) {
++
++ mutex_unlock(&context->ctx_lock);
++ delete_context(context);
++ return;
++ }
++ /* If VFD port is open, context will be deleted by vfd_close */
++ }
++
++ mutex_unlock(&context->ctx_lock);
++ return;
++}
++
++/**
++ * Process the incoming packet
++ */
++static void incoming_packet(struct sasem_context *context,
++ struct urb *urb)
++{
++ int len = urb->actual_length;
++ unsigned char *buf = urb->transfer_buffer;
++ long ms;
++ struct timeval tv;
++
++ if (len != 8) {
++ printk(KERN_WARNING "%s: invalid incoming packet size (%d)\n",
++ __func__, len);
++ return;
++ }
++
++#ifdef DEBUG
++ int i;
++ for (i = 0; i < 8; ++i)
++ printk(KERN_INFO "%02x ", buf[i]);
++ printk(KERN_INFO "\n");
++#endif
++
++ /*
++ * Lirc could deal with the repeat code, but we really need to block it
++ * if it arrives too late. Otherwise we could repeat the wrong code.
++ */
++
++ /* get the time since the last button press */
++ do_gettimeofday(&tv);
++ ms = (tv.tv_sec - context->presstime.tv_sec) * 1000 +
++ (tv.tv_usec - context->presstime.tv_usec) / 1000;
++
++ if (memcmp(buf, "\x08\0\0\0\0\0\0\0", 8) == 0) {
++ /*
++ * the repeat code is being sent, so we copy
++ * the old code to LIRC
++ */
++
++ /*
++ * NOTE: Only if the last code was less than 250ms ago
++ * - no one should be able to push another (undetected) button
++ * in that time and then get a false repeat of the previous
++ * press but it is long enough for a genuine repeat
++ */
++ if ((ms < 250) && (context->codesaved != 0)) {
++ memcpy(buf, &context->lastcode, 8);
++ context->presstime.tv_sec = tv.tv_sec;
++ context->presstime.tv_usec = tv.tv_usec;
++ }
++ } else {
++ /* save the current valid code for repeats */
++ memcpy(&context->lastcode, buf, 8);
++ /*
++ * set flag to signal a valid code was save;
++ * just for safety reasons
++ */
++ context->codesaved = 1;
++ context->presstime.tv_sec = tv.tv_sec;
++ context->presstime.tv_usec = tv.tv_usec;
++ }
++
++ lirc_buffer_write(context->driver->rbuf, buf);
++ wake_up(&context->driver->rbuf->wait_poll);
++}
++
++/**
++ * Callback function for USB core API: receive data
++ */
++static void usb_rx_callback(struct urb *urb)
++{
++ struct sasem_context *context;
++
++ if (!urb)
++ return;
++ context = (struct sasem_context *) urb->context;
++ if (!context)
++ return;
++
++ switch (urb->status) {
++
++ case -ENOENT: /* usbcore unlink successful! */
++ return;
++
++ case 0:
++ if (context->ir_isopen)
++ incoming_packet(context, urb);
++ break;
++
++ default:
++ printk(KERN_WARNING "%s: status (%d): ignored",
++ __func__, urb->status);
++ break;
++ }
++
++ usb_submit_urb(context->rx_urb, GFP_ATOMIC);
++ return;
++}
++
++
++
++/**
++ * Callback function for USB core API: Probe
++ */
++static int sasem_probe(struct usb_interface *interface,
++ const struct usb_device_id *id)
++{
++ struct usb_device *dev = NULL;
++ struct usb_host_interface *iface_desc = NULL;
++ struct usb_endpoint_descriptor *rx_endpoint = NULL;
++ struct usb_endpoint_descriptor *tx_endpoint = NULL;
++ struct urb *rx_urb = NULL;
++ struct urb *tx_urb = NULL;
++ struct lirc_driver *driver = NULL;
++ struct lirc_buffer *rbuf = NULL;
++ int lirc_minor = 0;
++ int num_endpoints;
++ int retval = 0;
++ int vfd_ep_found;
++ int ir_ep_found;
++ int alloc_status;
++ struct sasem_context *context = NULL;
++ int i;
++
++ printk(KERN_INFO "%s: found Sasem device\n", __func__);
++
++
++ dev = usb_get_dev(interface_to_usbdev(interface));
++ iface_desc = interface->cur_altsetting;
++ num_endpoints = iface_desc->desc.bNumEndpoints;
++
++ /*
++ * Scan the endpoint list and set:
++ * first input endpoint = IR endpoint
++ * first output endpoint = VFD endpoint
++ */
++
++ ir_ep_found = 0;
++ vfd_ep_found = 0;
++
++ for (i = 0; i < num_endpoints && !(ir_ep_found && vfd_ep_found); ++i) {
++
++ struct usb_endpoint_descriptor *ep;
++ int ep_dir;
++ int ep_type;
++ ep = &iface_desc->endpoint [i].desc;
++ ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
++ ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
++
++ if (!ir_ep_found &&
++ ep_dir == USB_DIR_IN &&
++ ep_type == USB_ENDPOINT_XFER_INT) {
++
++ rx_endpoint = ep;
++ ir_ep_found = 1;
++ if (debug)
++ printk(KERN_INFO "%s: found IR endpoint\n",
++ __func__);
++
++ } else if (!vfd_ep_found &&
++ ep_dir == USB_DIR_OUT &&
++ ep_type == USB_ENDPOINT_XFER_INT) {
++
++ tx_endpoint = ep;
++ vfd_ep_found = 1;
++ if (debug)
++ printk(KERN_INFO "%s: found VFD endpoint\n",
++ __func__);
++ }
++ }
++
++ /* Input endpoint is mandatory */
++ if (!ir_ep_found) {
++
++ err("%s: no valid input (IR) endpoint found.", __func__);
++ retval = -ENODEV;
++ goto exit;
++ }
++
++ if (!vfd_ep_found)
++ printk(KERN_INFO "%s: no valid output (VFD) endpoint found.\n",
++ __func__);
++
++
++ /* Allocate memory */
++ alloc_status = 0;
++
++ context = kzalloc(sizeof(struct sasem_context), GFP_KERNEL);
++ if (!context) {
++ err("%s: kzalloc failed for context", __func__);
++ alloc_status = 1;
++ goto alloc_status_switch;
++ }
++ driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
++ if (!driver) {
++ err("%s: kzalloc failed for lirc_driver", __func__);
++ alloc_status = 2;
++ goto alloc_status_switch;
++ }
++ rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
++ if (!rbuf) {
++ err("%s: kmalloc failed for lirc_buffer", __func__);
++ alloc_status = 3;
++ goto alloc_status_switch;
++ }
++ if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) {
++ err("%s: lirc_buffer_init failed", __func__);
++ alloc_status = 4;
++ goto alloc_status_switch;
++ }
++ rx_urb = usb_alloc_urb(0, GFP_KERNEL);
++ if (!rx_urb) {
++ err("%s: usb_alloc_urb failed for IR urb", __func__);
++ alloc_status = 5;
++ goto alloc_status_switch;
++ }
++ if (vfd_ep_found) {
++ tx_urb = usb_alloc_urb(0, GFP_KERNEL);
++ if (!tx_urb) {
++ err("%s: usb_alloc_urb failed for VFD urb",
++ __func__);
++ alloc_status = 6;
++ goto alloc_status_switch;
++ }
++ }
++
++ mutex_init(&context->ctx_lock);
++
++ strcpy(driver->name, MOD_NAME);
++ driver->minor = -1;
++ driver->code_length = 64;
++ driver->sample_rate = 0;
++ driver->features = LIRC_CAN_REC_LIRCCODE;
++ driver->data = context;
++ driver->rbuf = rbuf;
++ driver->set_use_inc = ir_open;
++ driver->set_use_dec = ir_close;
++ driver->dev = &interface->dev;
++ driver->owner = THIS_MODULE;
++
++ mutex_lock(&context->ctx_lock);
++
++ lirc_minor = lirc_register_driver(driver);
++ if (lirc_minor < 0) {
++ err("%s: lirc_register_driver failed", __func__);
++ alloc_status = 7;
++ mutex_unlock(&context->ctx_lock);
++ } else
++ printk(KERN_INFO "%s: Registered Sasem driver (minor:%d)\n",
++ __func__, lirc_minor);
++
++alloc_status_switch:
++
++ switch (alloc_status) {
++
++ case 7:
++ if (vfd_ep_found)
++ usb_free_urb(tx_urb);
++ case 6:
++ usb_free_urb(rx_urb);
++ case 5:
++ lirc_buffer_free(rbuf);
++ case 4:
++ kfree(rbuf);
++ case 3:
++ kfree(driver);
++ case 2:
++ kfree(context);
++ context = NULL;
++ case 1:
++ retval = -ENOMEM;
++ goto exit;
++ }
++
++ /* Needed while unregistering! */
++ driver->minor = lirc_minor;
++
++ context->dev = dev;
++ context->dev_present = 1;
++ context->rx_endpoint = rx_endpoint;
++ context->rx_urb = rx_urb;
++ if (vfd_ep_found) {
++ context->tx_endpoint = tx_endpoint;
++ context->tx_urb = tx_urb;
++ context->vfd_contrast = 1000; /* range 0 - 1000 */
++ }
++ context->driver = driver;
++
++ usb_set_intfdata(interface, context);
++
++ if (vfd_ep_found) {
++
++ if (debug)
++ printk(KERN_INFO "Registering VFD with sysfs\n");
++ if (usb_register_dev(interface, &sasem_class))
++ /* Not a fatal error, so ignore */
++ printk(KERN_INFO "%s: could not get a minor number "
++ "for VFD\n", __func__);
++ }
++
++ printk(KERN_INFO "%s: Sasem device on usb<%d:%d> initialized\n",
++ __func__, dev->bus->busnum, dev->devnum);
++
++ mutex_unlock(&context->ctx_lock);
++exit:
++ return retval;
++}
++
++/**
++ * Callback function for USB core API: disonnect
++ */
++static void sasem_disconnect(struct usb_interface *interface)
++{
++ struct sasem_context *context;
++
++ /* prevent races with ir_open()/vfd_open() */
++ mutex_lock(&disconnect_lock);
++
++ context = usb_get_intfdata(interface);
++ mutex_lock(&context->ctx_lock);
++
++ printk(KERN_INFO "%s: Sasem device disconnected\n", __func__);
++
++ usb_set_intfdata(interface, NULL);
++ context->dev_present = 0;
++
++ /* Stop reception */
++ usb_kill_urb(context->rx_urb);
++
++ /* Abort ongoing write */
++ if (atomic_read(&context->tx.busy)) {
++
++ usb_kill_urb(context->tx_urb);
++ wait_for_completion(&context->tx.finished);
++ }
++
++ /* De-register from lirc_dev if IR port is not open */
++ if (!context->ir_isopen)
++ deregister_from_lirc(context);
++
++ usb_deregister_dev(interface, &sasem_class);
++
++ mutex_unlock(&context->ctx_lock);
++
++ if (!context->ir_isopen && !context->vfd_isopen)
++ delete_context(context);
++
++ mutex_unlock(&disconnect_lock);
++}
++
++static int __init sasem_init(void)
++{
++ int rc;
++
++ printk(KERN_INFO MOD_DESC ", v" MOD_VERSION "\n");
++ printk(KERN_INFO MOD_AUTHOR "\n");
++
++ rc = usb_register(&sasem_driver);
++ if (rc < 0) {
++ err("%s: usb register failed (%d)", __func__, rc);
++ return -ENODEV;
++ }
++ return 0;
++}
++
++static void __exit sasem_exit(void)
++{
++ usb_deregister(&sasem_driver);
++ printk(KERN_INFO "module removed. Goodbye!\n");
++}
++
++
++module_init(sasem_init);
++module_exit(sasem_exit);
+diff --git a/drivers/staging/lirc/lirc_serial.c b/drivers/staging/lirc/lirc_serial.c
+new file mode 100644
+index 0000000..d2ea3f0
+--- /dev/null
++++ b/drivers/staging/lirc/lirc_serial.c
+@@ -0,0 +1,1313 @@
++/*
++ * lirc_serial.c
++ *
++ * lirc_serial - Device driver that records pulse- and pause-lengths
++ * (space-lengths) between DDCD event on a serial port.
++ *
++ * Copyright (C) 1996,97 Ralph Metzler <rjkm@thp.uni-koeln.de>
++ * Copyright (C) 1998 Trent Piepho <xyzzy@u.washington.edu>
++ * Copyright (C) 1998 Ben Pfaff <blp@gnu.org>
++ * Copyright (C) 1999 Christoph Bartelmus <lirc@bartelmus.de>
++ * Copyright (C) 2007 Andrei Tanas <andrei@tanas.ca> (suspend/resume support)
++ * 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
++ *
++ */
++
++/*
++ * Steve's changes to improve transmission fidelity:
++ * - for systems with the rdtsc instruction and the clock counter, a
++ * send_pule that times the pulses directly using the counter.
++ * This means that the LIRC_SERIAL_TRANSMITTER_LATENCY fudge is
++ * not needed. Measurement shows very stable waveform, even where
++ * PCI activity slows the access to the UART, which trips up other
++ * versions.
++ * - For other system, non-integer-microsecond pulse/space lengths,
++ * done using fixed point binary. So, much more accurate carrier
++ * frequency.
++ * - fine tuned transmitter latency, taking advantage of fractional
++ * microseconds in previous change
++ * - Fixed bug in the way transmitter latency was accounted for by
++ * tuning the pulse lengths down - the send_pulse routine ignored
++ * this overhead as it timed the overall pulse length - so the
++ * pulse frequency was right but overall pulse length was too
++ * long. Fixed by accounting for latency on each pulse/space
++ * iteration.
++ *
++ * Steve Davies <steve@daviesfam.org> July 2001
++ */
++
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/sched.h>
++#include <linux/fs.h>
++#include <linux/interrupt.h>
++#include <linux/ioport.h>
++#include <linux/kernel.h>
++#include <linux/serial_reg.h>
++#include <linux/time.h>
++#include <linux/string.h>
++#include <linux/types.h>
++#include <linux/wait.h>
++#include <linux/mm.h>
++#include <linux/delay.h>
++#include <linux/poll.h>
++#include <linux/platform_device.h>
++
++#include <asm/system.h>
++#include <linux/io.h>
++#include <linux/irq.h>
++#include <linux/fcntl.h>
++#include <linux/spinlock.h>
++
++#ifdef CONFIG_LIRC_SERIAL_NSLU2
++#include <asm/hardware.h>
++#endif
++/* From Intel IXP42X Developer's Manual (#252480-005): */
++/* ftp://download.intel.com/design/network/manuals/25248005.pdf */
++#define UART_IE_IXP42X_UUE 0x40 /* IXP42X UART Unit enable */
++#define UART_IE_IXP42X_RTOIE 0x10 /* IXP42X Receiver Data Timeout int.enable */
++
++#include <media/lirc.h>
++#include <media/lirc_dev.h>
++
++#define LIRC_DRIVER_NAME "lirc_serial"
++
++struct lirc_serial {
++ int signal_pin;
++ int signal_pin_change;
++ u8 on;
++ u8 off;
++ long (*send_pulse)(unsigned long length);
++ void (*send_space)(long length);
++ int features;
++ spinlock_t lock;
++};
++
++#define LIRC_HOMEBREW 0
++#define LIRC_IRDEO 1
++#define LIRC_IRDEO_REMOTE 2
++#define LIRC_ANIMAX 3
++#define LIRC_IGOR 4
++#define LIRC_NSLU2 5
++
++/*** module parameters ***/
++static int type;
++static int io;
++static int irq;
++static int iommap;
++static int ioshift;
++static int softcarrier = 1;
++static int share_irq;
++static int debug;
++static int sense = -1; /* -1 = auto, 0 = active high, 1 = active low */
++static int txsense; /* 0 = active high, 1 = active low */
++
++#define dprintk(fmt, args...) \
++ do { \
++ if (debug) \
++ printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
++ fmt, ## args); \
++ } while (0)
++
++/* forward declarations */
++static long send_pulse_irdeo(unsigned long length);
++static long send_pulse_homebrew(unsigned long length);
++static void send_space_irdeo(long length);
++static void send_space_homebrew(long length);
++
++static struct lirc_serial hardware[] = {
++ [LIRC_HOMEBREW] = {
++ .signal_pin = UART_MSR_DCD,
++ .signal_pin_change = UART_MSR_DDCD,
++ .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR),
++ .off = (UART_MCR_RTS | UART_MCR_OUT2),
++ .send_pulse = send_pulse_homebrew,
++ .send_space = send_space_homebrew,
++#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
++ .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
++ LIRC_CAN_SET_SEND_CARRIER |
++ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
++#else
++ .features = LIRC_CAN_REC_MODE2
++#endif
++ },
++
++ [LIRC_IRDEO] = {
++ .signal_pin = UART_MSR_DSR,
++ .signal_pin_change = UART_MSR_DDSR,
++ .on = UART_MCR_OUT2,
++ .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
++ .send_pulse = send_pulse_irdeo,
++ .send_space = send_space_irdeo,
++ .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
++ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
++ },
++
++ [LIRC_IRDEO_REMOTE] = {
++ .signal_pin = UART_MSR_DSR,
++ .signal_pin_change = UART_MSR_DDSR,
++ .on = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
++ .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
++ .send_pulse = send_pulse_irdeo,
++ .send_space = send_space_irdeo,
++ .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
++ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
++ },
++
++ [LIRC_ANIMAX] = {
++ .signal_pin = UART_MSR_DCD,
++ .signal_pin_change = UART_MSR_DDCD,
++ .on = 0,
++ .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
++ .send_pulse = NULL,
++ .send_space = NULL,
++ .features = LIRC_CAN_REC_MODE2
++ },
++
++ [LIRC_IGOR] = {
++ .signal_pin = UART_MSR_DSR,
++ .signal_pin_change = UART_MSR_DDSR,
++ .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR),
++ .off = (UART_MCR_RTS | UART_MCR_OUT2),
++ .send_pulse = send_pulse_homebrew,
++ .send_space = send_space_homebrew,
++#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
++ .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
++ LIRC_CAN_SET_SEND_CARRIER |
++ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
++#else
++ .features = LIRC_CAN_REC_MODE2
++#endif
++ },
++
++#ifdef CONFIG_LIRC_SERIAL_NSLU2
++ /*
++ * Modified Linksys Network Storage Link USB 2.0 (NSLU2):
++ * We receive on CTS of the 2nd serial port (R142,LHS), we
++ * transmit with a IR diode between GPIO[1] (green status LED),
++ * and ground (Matthias Goebl <matthias.goebl@goebl.net>).
++ * See also http://www.nslu2-linux.org for this device
++ */
++ [LIRC_NSLU2] = {
++ .signal_pin = UART_MSR_CTS,
++ .signal_pin_change = UART_MSR_DCTS,
++ .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR),
++ .off = (UART_MCR_RTS | UART_MCR_OUT2),
++ .send_pulse = send_pulse_homebrew,
++ .send_space = send_space_homebrew,
++#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
++ .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
++ LIRC_CAN_SET_SEND_CARRIER |
++ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
++#else
++ .features = LIRC_CAN_REC_MODE2
++#endif
++ },
++#endif
++
++};
++
++#define RS_ISR_PASS_LIMIT 256
++
++/*
++ * A long pulse code from a remote might take up to 300 bytes. The
++ * daemon should read the bytes as soon as they are generated, so take
++ * the number of keys you think you can push before the daemon runs
++ * and multiply by 300. The driver will warn you if you overrun this
++ * buffer. If you have a slow computer or non-busmastering IDE disks,
++ * maybe you will need to increase this.
++ */
++
++/* This MUST be a power of two! It has to be larger than 1 as well. */
++
++#define RBUF_LEN 256
++
++static struct timeval lasttv = {0, 0};
++
++static struct lirc_buffer rbuf;
++
++static unsigned int freq = 38000;
++static unsigned int duty_cycle = 50;
++
++/* Initialized in init_timing_params() */
++static unsigned long period;
++static unsigned long pulse_width;
++static unsigned long space_width;
++
++#if defined(__i386__)
++/*
++ * From:
++ * Linux I/O port programming mini-HOWTO
++ * Author: Riku Saikkonen <Riku.Saikkonen@hut.fi>
++ * v, 28 December 1997
++ *
++ * [...]
++ * Actually, a port I/O instruction on most ports in the 0-0x3ff range
++ * takes almost exactly 1 microsecond, so if you're, for example, using
++ * the parallel port directly, just do additional inb()s from that port
++ * to delay.
++ * [...]
++ */
++/* transmitter latency 1.5625us 0x1.90 - this figure arrived at from
++ * comment above plus trimming to match actual measured frequency.
++ * This will be sensitive to cpu speed, though hopefully most of the 1.5us
++ * is spent in the uart access. Still - for reference test machine was a
++ * 1.13GHz Athlon system - Steve
++ */
++
++/*
++ * changed from 400 to 450 as this works better on slower machines;
++ * faster machines will use the rdtsc code anyway
++ */
++#define LIRC_SERIAL_TRANSMITTER_LATENCY 450
++
++#else
++
++/* does anybody have information on other platforms ? */
++/* 256 = 1<<8 */
++#define LIRC_SERIAL_TRANSMITTER_LATENCY 256
++
++#endif /* __i386__ */
++/*
++ * FIXME: should we be using hrtimers instead of this
++ * LIRC_SERIAL_TRANSMITTER_LATENCY nonsense?
++ */
++
++/* fetch serial input packet (1 byte) from register offset */
++static u8 sinp(int offset)
++{
++ if (iommap != 0)
++ /* the register is memory-mapped */
++ offset <<= ioshift;
++
++ return inb(io + offset);
++}
++
++/* write serial output packet (1 byte) of value to register offset */
++static void soutp(int offset, u8 value)
++{
++ if (iommap != 0)
++ /* the register is memory-mapped */
++ offset <<= ioshift;
++
++ outb(value, io + offset);
++}
++
++static void on(void)
++{
++#ifdef CONFIG_LIRC_SERIAL_NSLU2
++ /*
++ * On NSLU2, we put the transmit diode between the output of the green
++ * status LED and ground
++ */
++ if (type == LIRC_NSLU2) {
++ gpio_line_set(NSLU2_LED_GRN, IXP4XX_GPIO_LOW);
++ return;
++ }
++#endif
++ if (txsense)
++ soutp(UART_MCR, hardware[type].off);
++ else
++ soutp(UART_MCR, hardware[type].on);
++}
++
++static void off(void)
++{
++#ifdef CONFIG_LIRC_SERIAL_NSLU2
++ if (type == LIRC_NSLU2) {
++ gpio_line_set(NSLU2_LED_GRN, IXP4XX_GPIO_HIGH);
++ return;
++ }
++#endif
++ if (txsense)
++ soutp(UART_MCR, hardware[type].on);
++ else
++ soutp(UART_MCR, hardware[type].off);
++}
++
++#ifndef MAX_UDELAY_MS
++#define MAX_UDELAY_US 5000
++#else
++#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
++#endif
++
++static void safe_udelay(unsigned long usecs)
++{
++ while (usecs > MAX_UDELAY_US) {
++ udelay(MAX_UDELAY_US);
++ usecs -= MAX_UDELAY_US;
++ }
++ udelay(usecs);
++}
++
++#ifdef USE_RDTSC
++/*
++ * This is an overflow/precision juggle, complicated in that we can't
++ * do long long divide in the kernel
++ */
++
++/*
++ * When we use the rdtsc instruction to measure clocks, we keep the
++ * pulse and space widths as clock cycles. As this is CPU speed
++ * dependent, the widths must be calculated in init_port and ioctl
++ * time
++ */
++
++/* So send_pulse can quickly convert microseconds to clocks */
++static unsigned long conv_us_to_clocks;
++
++static int init_timing_params(unsigned int new_duty_cycle,
++ unsigned int new_freq)
++{
++ unsigned long long loops_per_sec, work;
++
++ duty_cycle = new_duty_cycle;
++ freq = new_freq;
++
++ loops_per_sec = current_cpu_data.loops_per_jiffy;
++ loops_per_sec *= HZ;
++
++ /* How many clocks in a microsecond?, avoiding long long divide */
++ work = loops_per_sec;
++ work *= 4295; /* 4295 = 2^32 / 1e6 */
++ conv_us_to_clocks = (work >> 32);
++
++ /*
++ * Carrier period in clocks, approach good up to 32GHz clock,
++ * gets carrier frequency within 8Hz
++ */
++ period = loops_per_sec >> 3;
++ period /= (freq >> 3);
++
++ /* Derive pulse and space from the period */
++ pulse_width = period * duty_cycle / 100;
++ space_width = period - pulse_width;
++ dprintk("in init_timing_params, freq=%d, duty_cycle=%d, "
++ "clk/jiffy=%ld, pulse=%ld, space=%ld, "
++ "conv_us_to_clocks=%ld\n",
++ freq, duty_cycle, current_cpu_data.loops_per_jiffy,
++ pulse_width, space_width, conv_us_to_clocks);
++ return 0;
++}
++#else /* ! USE_RDTSC */
++static int init_timing_params(unsigned int new_duty_cycle,
++ unsigned int new_freq)
++{
++/*
++ * period, pulse/space width are kept with 8 binary places -
++ * IE multiplied by 256.
++ */
++ if (256 * 1000000L / new_freq * new_duty_cycle / 100 <=
++ LIRC_SERIAL_TRANSMITTER_LATENCY)
++ return -EINVAL;
++ if (256 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <=
++ LIRC_SERIAL_TRANSMITTER_LATENCY)
++ return -EINVAL;
++ duty_cycle = new_duty_cycle;
++ freq = new_freq;
++ period = 256 * 1000000L / freq;
++ pulse_width = period * duty_cycle / 100;
++ space_width = period - pulse_width;
++ dprintk("in init_timing_params, freq=%d pulse=%ld, "
++ "space=%ld\n", freq, pulse_width, space_width);
++ return 0;
++}
++#endif /* USE_RDTSC */
++
++
++/* return value: space length delta */
++
++static long send_pulse_irdeo(unsigned long length)
++{
++ long rawbits, ret;
++ int i;
++ unsigned char output;
++ unsigned char chunk, shifted;
++
++ /* how many bits have to be sent ? */
++ rawbits = length * 1152 / 10000;
++ if (duty_cycle > 50)
++ chunk = 3;
++ else
++ chunk = 1;
++ for (i = 0, output = 0x7f; rawbits > 0; rawbits -= 3) {
++ shifted = chunk << (i * 3);
++ shifted >>= 1;
++ output &= (~shifted);
++ i++;
++ if (i == 3) {
++ soutp(UART_TX, output);
++ while (!(sinp(UART_LSR) & UART_LSR_THRE))
++ ;
++ output = 0x7f;
++ i = 0;
++ }
++ }
++ if (i != 0) {
++ soutp(UART_TX, output);
++ while (!(sinp(UART_LSR) & UART_LSR_TEMT))
++ ;
++ }
++
++ if (i == 0)
++ ret = (-rawbits) * 10000 / 1152;
++ else
++ ret = (3 - i) * 3 * 10000 / 1152 + (-rawbits) * 10000 / 1152;
++
++ return ret;
++}
++
++#ifdef USE_RDTSC
++/* Version that uses Pentium rdtsc instruction to measure clocks */
++
++/*
++ * This version does sub-microsecond timing using rdtsc instruction,
++ * and does away with the fudged LIRC_SERIAL_TRANSMITTER_LATENCY
++ * Implicitly i586 architecture... - Steve
++ */
++
++static long send_pulse_homebrew_softcarrier(unsigned long length)
++{
++ int flag;
++ unsigned long target, start, now;
++
++ /* Get going quick as we can */
++ rdtscl(start);
++ on();
++ /* Convert length from microseconds to clocks */
++ length *= conv_us_to_clocks;
++ /* And loop till time is up - flipping at right intervals */
++ now = start;
++ target = pulse_width;
++ flag = 1;
++ /*
++ * FIXME: This looks like a hard busy wait, without even an occasional,
++ * polite, cpu_relax() call. There's got to be a better way?
++ *
++ * The i2c code has the result of a lot of bit-banging work, I wonder if
++ * there's something there which could be helpful here.
++ */
++ while ((now - start) < length) {
++ /* Delay till flip time */
++ do {
++ rdtscl(now);
++ } while ((now - start) < target);
++
++ /* flip */
++ if (flag) {
++ rdtscl(now);
++ off();
++ target += space_width;
++ } else {
++ rdtscl(now); on();
++ target += pulse_width;
++ }
++ flag = !flag;
++ }
++ rdtscl(now);
++ return ((now - start) - length) / conv_us_to_clocks;
++}
++#else /* ! USE_RDTSC */
++/* Version using udelay() */
++
++/*
++ * here we use fixed point arithmetic, with 8
++ * fractional bits. that gets us within 0.1% or so of the right average
++ * frequency, albeit with some jitter in pulse length - Steve
++ */
++
++/* To match 8 fractional bits used for pulse/space length */
++
++static long send_pulse_homebrew_softcarrier(unsigned long length)
++{
++ int flag;
++ unsigned long actual, target, d;
++ length <<= 8;
++
++ actual = 0; target = 0; flag = 0;
++ while (actual < length) {
++ if (flag) {
++ off();
++ target += space_width;
++ } else {
++ on();
++ target += pulse_width;
++ }
++ d = (target - actual -
++ LIRC_SERIAL_TRANSMITTER_LATENCY + 128) >> 8;
++ /*
++ * Note - we've checked in ioctl that the pulse/space
++ * widths are big enough so that d is > 0
++ */
++ udelay(d);
++ actual += (d << 8) + LIRC_SERIAL_TRANSMITTER_LATENCY;
++ flag = !flag;
++ }
++ return (actual-length) >> 8;
++}
++#endif /* USE_RDTSC */
++
++static long send_pulse_homebrew(unsigned long length)
++{
++ if (length <= 0)
++ return 0;
++
++ if (softcarrier)
++ return send_pulse_homebrew_softcarrier(length);
++ else {
++ on();
++ safe_udelay(length);
++ return 0;
++ }
++}
++
++static void send_space_irdeo(long length)
++{
++ if (length <= 0)
++ return;
++
++ safe_udelay(length);
++}
++
++static void send_space_homebrew(long length)
++{
++ off();
++ if (length <= 0)
++ return;
++ safe_udelay(length);
++}
++
++static void rbwrite(int l)
++{
++ if (lirc_buffer_full(&rbuf)) {
++ /* no new signals will be accepted */
++ dprintk("Buffer overrun\n");
++ return;
++ }
++ lirc_buffer_write(&rbuf, (void *)&l);
++}
++
++static void frbwrite(int l)
++{
++ /* simple noise filter */
++ static int pulse, space;
++ static unsigned int ptr;
++
++ if (ptr > 0 && (l & PULSE_BIT)) {
++ pulse += l & PULSE_MASK;
++ if (pulse > 250) {
++ rbwrite(space);
++ rbwrite(pulse | PULSE_BIT);
++ ptr = 0;
++ pulse = 0;
++ }
++ return;
++ }
++ if (!(l & PULSE_BIT)) {
++ if (ptr == 0) {
++ if (l > 20000) {
++ space = l;
++ ptr++;
++ return;
++ }
++ } else {
++ if (l > 20000) {
++ space += pulse;
++ if (space > PULSE_MASK)
++ space = PULSE_MASK;
++ space += l;
++ if (space > PULSE_MASK)
++ space = PULSE_MASK;
++ pulse = 0;
++ return;
++ }
++ rbwrite(space);
++ rbwrite(pulse | PULSE_BIT);
++ ptr = 0;
++ pulse = 0;
++ }
++ }
++ rbwrite(l);
++}
++
++static irqreturn_t irq_handler(int i, void *blah)
++{
++ struct timeval tv;
++ int counter, dcd;
++ u8 status;
++ long deltv;
++ int data;
++ static int last_dcd = -1;
++
++ if ((sinp(UART_IIR) & UART_IIR_NO_INT)) {
++ /* not our interrupt */
++ return IRQ_NONE;
++ }
++
++ counter = 0;
++ do {
++ counter++;
++ status = sinp(UART_MSR);
++ if (counter > RS_ISR_PASS_LIMIT) {
++ printk(KERN_WARNING LIRC_DRIVER_NAME ": AIEEEE: "
++ "We're caught!\n");
++ break;
++ }
++ if ((status & hardware[type].signal_pin_change)
++ && sense != -1) {
++ /* get current time */
++ do_gettimeofday(&tv);
++
++ /* New mode, written by Trent Piepho
++ <xyzzy@u.washington.edu>. */
++
++ /*
++ * The old format was not very portable.
++ * We now use an int to pass pulses
++ * and spaces to user space.
++ *
++ * If PULSE_BIT is set a pulse has been
++ * received, otherwise a space has been
++ * received. The driver needs to know if your
++ * receiver is active high or active low, or
++ * the space/pulse sense could be
++ * inverted. The bits denoted by PULSE_MASK are
++ * the length in microseconds. Lengths greater
++ * than or equal to 16 seconds are clamped to
++ * PULSE_MASK. All other bits are unused.
++ * This is a much simpler interface for user
++ * programs, as well as eliminating "out of
++ * phase" errors with space/pulse
++ * autodetection.
++ */
++
++ /* calc time since last interrupt in microseconds */
++ dcd = (status & hardware[type].signal_pin) ? 1 : 0;
++
++ if (dcd == last_dcd) {
++ printk(KERN_WARNING LIRC_DRIVER_NAME
++ ": ignoring spike: %d %d %lx %lx %lx %lx\n",
++ dcd, sense,
++ tv.tv_sec, lasttv.tv_sec,
++ tv.tv_usec, lasttv.tv_usec);
++ continue;
++ }
++
++ deltv = tv.tv_sec-lasttv.tv_sec;
++ if (tv.tv_sec < lasttv.tv_sec ||
++ (tv.tv_sec == lasttv.tv_sec &&
++ tv.tv_usec < lasttv.tv_usec)) {
++ printk(KERN_WARNING LIRC_DRIVER_NAME
++ ": AIEEEE: your clock just jumped "
++ "backwards\n");
++ printk(KERN_WARNING LIRC_DRIVER_NAME
++ ": %d %d %lx %lx %lx %lx\n",
++ dcd, sense,
++ tv.tv_sec, lasttv.tv_sec,
++ tv.tv_usec, lasttv.tv_usec);
++ data = PULSE_MASK;
++ } else if (deltv > 15) {
++ data = PULSE_MASK; /* really long time */
++ if (!(dcd^sense)) {
++ /* sanity check */
++ printk(KERN_WARNING LIRC_DRIVER_NAME
++ ": AIEEEE: "
++ "%d %d %lx %lx %lx %lx\n",
++ dcd, sense,
++ tv.tv_sec, lasttv.tv_sec,
++ tv.tv_usec, lasttv.tv_usec);
++ /*
++ * detecting pulse while this
++ * MUST be a space!
++ */
++ sense = sense ? 0 : 1;
++ }
++ } else
++ data = (int) (deltv*1000000 +
++ tv.tv_usec -
++ lasttv.tv_usec);
++ frbwrite(dcd^sense ? data : (data|PULSE_BIT));
++ lasttv = tv;
++ last_dcd = dcd;
++ wake_up_interruptible(&rbuf.wait_poll);
++ }
++ } while (!(sinp(UART_IIR) & UART_IIR_NO_INT)); /* still pending ? */
++ return IRQ_HANDLED;
++}
++
++
++static int hardware_init_port(void)
++{
++ u8 scratch, scratch2, scratch3;
++
++ /*
++ * This is a simple port existence test, borrowed from the autoconfig
++ * function in drivers/serial/8250.c
++ */
++ scratch = sinp(UART_IER);
++ soutp(UART_IER, 0);
++#ifdef __i386__
++ outb(0xff, 0x080);
++#endif
++ scratch2 = sinp(UART_IER) & 0x0f;
++ soutp(UART_IER, 0x0f);
++#ifdef __i386__
++ outb(0x00, 0x080);
++#endif
++ scratch3 = sinp(UART_IER) & 0x0f;
++ soutp(UART_IER, scratch);
++ if (scratch2 != 0 || scratch3 != 0x0f) {
++ /* we fail, there's nothing here */
++ printk(KERN_ERR LIRC_DRIVER_NAME ": port existence test "
++ "failed, cannot continue\n");
++ return -EINVAL;
++ }
++
++
++
++ /* Set DLAB 0. */
++ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
++
++ /* First of all, disable all interrupts */
++ soutp(UART_IER, sinp(UART_IER) &
++ (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
++
++ /* Clear registers. */
++ sinp(UART_LSR);
++ sinp(UART_RX);
++ sinp(UART_IIR);
++ sinp(UART_MSR);
++
++#ifdef CONFIG_LIRC_SERIAL_NSLU2
++ if (type == LIRC_NSLU2) {
++ /* Setup NSLU2 UART */
++
++ /* Enable UART */
++ soutp(UART_IER, sinp(UART_IER) | UART_IE_IXP42X_UUE);
++ /* Disable Receiver data Time out interrupt */
++ soutp(UART_IER, sinp(UART_IER) & ~UART_IE_IXP42X_RTOIE);
++ /* set out2 = interrupt unmask; off() doesn't set MCR
++ on NSLU2 */
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
++ }
++#endif
++
++ /* Set line for power source */
++ off();
++
++ /* Clear registers again to be sure. */
++ sinp(UART_LSR);
++ sinp(UART_RX);
++ sinp(UART_IIR);
++ sinp(UART_MSR);
++
++ switch (type) {
++ case LIRC_IRDEO:
++ case LIRC_IRDEO_REMOTE:
++ /* setup port to 7N1 @ 115200 Baud */
++ /* 7N1+start = 9 bits at 115200 ~ 3 bits at 38kHz */
++
++ /* Set DLAB 1. */
++ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
++ /* Set divisor to 1 => 115200 Baud */
++ soutp(UART_DLM, 0);
++ soutp(UART_DLL, 1);
++ /* Set DLAB 0 + 7N1 */
++ soutp(UART_LCR, UART_LCR_WLEN7);
++ /* THR interrupt already disabled at this point */
++ break;
++ default:
++ break;
++ }
++
++ return 0;
++}
++
++static int init_port(void)
++{
++ int i, nlow, nhigh;
++
++ /* Reserve io region. */
++ /*
++ * Future MMAP-Developers: Attention!
++ * For memory mapped I/O you *might* need to use ioremap() first,
++ * for the NSLU2 it's done in boot code.
++ */
++ if (((iommap != 0)
++ && (request_mem_region(iommap, 8 << ioshift,
++ LIRC_DRIVER_NAME) == NULL))
++ || ((iommap == 0)
++ && (request_region(io, 8, LIRC_DRIVER_NAME) == NULL))) {
++ printk(KERN_ERR LIRC_DRIVER_NAME
++ ": port %04x already in use\n", io);
++ printk(KERN_WARNING LIRC_DRIVER_NAME
++ ": use 'setserial /dev/ttySX uart none'\n");
++ printk(KERN_WARNING LIRC_DRIVER_NAME
++ ": or compile the serial port driver as module and\n");
++ printk(KERN_WARNING LIRC_DRIVER_NAME
++ ": make sure this module is loaded first\n");
++ return -EBUSY;
++ }
++
++ if (hardware_init_port() < 0)
++ return -EINVAL;
++
++ /* Initialize pulse/space widths */
++ init_timing_params(duty_cycle, freq);
++
++ /* If pin is high, then this must be an active low receiver. */
++ if (sense == -1) {
++ /* wait 1/2 sec for the power supply */
++ msleep(500);
++
++ /*
++ * probe 9 times every 0.04s, collect "votes" for
++ * active high/low
++ */
++ nlow = 0;
++ nhigh = 0;
++ for (i = 0; i < 9; i++) {
++ if (sinp(UART_MSR) & hardware[type].signal_pin)
++ nlow++;
++ else
++ nhigh++;
++ msleep(40);
++ }
++ sense = (nlow >= nhigh ? 1 : 0);
++ printk(KERN_INFO LIRC_DRIVER_NAME ": auto-detected active "
++ "%s receiver\n", sense ? "low" : "high");
++ } else
++ printk(KERN_INFO LIRC_DRIVER_NAME ": Manually using active "
++ "%s receiver\n", sense ? "low" : "high");
++
++ return 0;
++}
++
++static int set_use_inc(void *data)
++{
++ int result;
++ unsigned long flags;
++
++ /* initialize timestamp */
++ do_gettimeofday(&lasttv);
++
++ result = request_irq(irq, irq_handler,
++ IRQF_DISABLED | (share_irq ? IRQF_SHARED : 0),
++ LIRC_DRIVER_NAME, (void *)&hardware);
++
++ switch (result) {
++ case -EBUSY:
++ printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", irq);
++ return -EBUSY;
++ case -EINVAL:
++ printk(KERN_ERR LIRC_DRIVER_NAME
++ ": Bad irq number or handler\n");
++ return -EINVAL;
++ default:
++ dprintk("Interrupt %d, port %04x obtained\n", irq, io);
++ break;
++ };
++
++ spin_lock_irqsave(&hardware[type].lock, flags);
++
++ /* Set DLAB 0. */
++ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
++
++ soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
++
++ spin_unlock_irqrestore(&hardware[type].lock, flags);
++
++ return 0;
++}
++
++static void set_use_dec(void *data)
++{ unsigned long flags;
++
++ spin_lock_irqsave(&hardware[type].lock, flags);
++
++ /* Set DLAB 0. */
++ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
++
++ /* First of all, disable all interrupts */
++ soutp(UART_IER, sinp(UART_IER) &
++ (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
++ spin_unlock_irqrestore(&hardware[type].lock, flags);
++
++ free_irq(irq, (void *)&hardware);
++
++ dprintk("freed IRQ %d\n", irq);
++}
++
++static ssize_t lirc_write(struct file *file, const char *buf,
++ size_t n, loff_t *ppos)
++{
++ int i, count;
++ unsigned long flags;
++ long delta = 0;
++ int *wbuf;
++
++ if (!(hardware[type].features & LIRC_CAN_SEND_PULSE))
++ return -EBADF;
++
++ count = n / sizeof(int);
++ if (n % sizeof(int) || count % 2 == 0)
++ return -EINVAL;
++ wbuf = memdup_user(buf, n);
++ if (PTR_ERR(wbuf))
++ return PTR_ERR(wbuf);
++ spin_lock_irqsave(&hardware[type].lock, flags);
++ if (type == LIRC_IRDEO) {
++ /* DTR, RTS down */
++ on();
++ }
++ for (i = 0; i < count; i++) {
++ if (i%2)
++ hardware[type].send_space(wbuf[i] - delta);
++ else
++ delta = hardware[type].send_pulse(wbuf[i]);
++ }
++ off();
++ spin_unlock_irqrestore(&hardware[type].lock, flags);
++ return n;
++}
++
++static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
++{
++ int result;
++ unsigned long value;
++ unsigned int ivalue;
++
++ switch (cmd) {
++ case LIRC_GET_SEND_MODE:
++ if (!(hardware[type].features&LIRC_CAN_SEND_MASK))
++ return -ENOIOCTLCMD;
++
++ result = put_user(LIRC_SEND2MODE
++ (hardware[type].features&LIRC_CAN_SEND_MASK),
++ (unsigned long *) arg);
++ if (result)
++ return result;
++ break;
++
++ case LIRC_SET_SEND_MODE:
++ if (!(hardware[type].features&LIRC_CAN_SEND_MASK))
++ return -ENOIOCTLCMD;
++
++ result = get_user(value, (unsigned long *) arg);
++ if (result)
++ return result;
++ /* only LIRC_MODE_PULSE supported */
++ if (value != LIRC_MODE_PULSE)
++ return -ENOSYS;
++ break;
++
++ case LIRC_GET_LENGTH:
++ return -ENOSYS;
++ break;
++
++ case LIRC_SET_SEND_DUTY_CYCLE:
++ dprintk("SET_SEND_DUTY_CYCLE\n");
++ if (!(hardware[type].features&LIRC_CAN_SET_SEND_DUTY_CYCLE))
++ return -ENOIOCTLCMD;
++
++ result = get_user(ivalue, (unsigned int *) arg);
++ if (result)
++ return result;
++ if (ivalue <= 0 || ivalue > 100)
++ return -EINVAL;
++ return init_timing_params(ivalue, freq);
++ break;
++
++ case LIRC_SET_SEND_CARRIER:
++ dprintk("SET_SEND_CARRIER\n");
++ if (!(hardware[type].features&LIRC_CAN_SET_SEND_CARRIER))
++ return -ENOIOCTLCMD;
++
++ result = get_user(ivalue, (unsigned int *) arg);
++ if (result)
++ return result;
++ if (ivalue > 500000 || ivalue < 20000)
++ return -EINVAL;
++ return init_timing_params(duty_cycle, ivalue);
++ break;
++
++ default:
++ return lirc_dev_fop_ioctl(filep, cmd, arg);
++ }
++ return 0;
++}
++
++static struct file_operations lirc_fops = {
++ .owner = THIS_MODULE,
++ .write = lirc_write,
++ .unlocked_ioctl = lirc_ioctl,
++ .read = lirc_dev_fop_read,
++ .poll = lirc_dev_fop_poll,
++ .open = lirc_dev_fop_open,
++ .release = lirc_dev_fop_close,
++};
++
++static struct lirc_driver driver = {
++ .name = LIRC_DRIVER_NAME,
++ .minor = -1,
++ .code_length = 1,
++ .sample_rate = 0,
++ .data = NULL,
++ .add_to_buf = NULL,
++ .rbuf = &rbuf,
++ .set_use_inc = set_use_inc,
++ .set_use_dec = set_use_dec,
++ .fops = &lirc_fops,
++ .dev = NULL,
++ .owner = THIS_MODULE,
++};
++
++static struct platform_device *lirc_serial_dev;
++
++static int __devinit lirc_serial_probe(struct platform_device *dev)
++{
++ return 0;
++}
++
++static int __devexit lirc_serial_remove(struct platform_device *dev)
++{
++ return 0;
++}
++
++static int lirc_serial_suspend(struct platform_device *dev,
++ pm_message_t state)
++{
++ /* Set DLAB 0. */
++ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
++
++ /* Disable all interrupts */
++ soutp(UART_IER, sinp(UART_IER) &
++ (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
++
++ /* Clear registers. */
++ sinp(UART_LSR);
++ sinp(UART_RX);
++ sinp(UART_IIR);
++ sinp(UART_MSR);
++
++ return 0;
++}
++
++/* twisty maze... need a forward-declaration here... */
++static void lirc_serial_exit(void);
++
++static int lirc_serial_resume(struct platform_device *dev)
++{
++ unsigned long flags;
++
++ if (hardware_init_port() < 0) {
++ lirc_serial_exit();
++ return -EINVAL;
++ }
++
++ spin_lock_irqsave(&hardware[type].lock, flags);
++ /* Enable Interrupt */
++ do_gettimeofday(&lasttv);
++ soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
++ off();
++
++ lirc_buffer_clear(&rbuf);
++
++ spin_unlock_irqrestore(&hardware[type].lock, flags);
++
++ return 0;
++}
++
++static struct platform_driver lirc_serial_driver = {
++ .probe = lirc_serial_probe,
++ .remove = __devexit_p(lirc_serial_remove),
++ .suspend = lirc_serial_suspend,
++ .resume = lirc_serial_resume,
++ .driver = {
++ .name = "lirc_serial",
++ .owner = THIS_MODULE,
++ },
++};
++
++static int __init lirc_serial_init(void)
++{
++ int result;
++
++ /* Init read buffer. */
++ result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN);
++ if (result < 0)
++ return -ENOMEM;
++
++ result = platform_driver_register(&lirc_serial_driver);
++ if (result) {
++ printk("lirc register returned %d\n", result);
++ goto exit_buffer_free;
++ }
++
++ lirc_serial_dev = platform_device_alloc("lirc_serial", 0);
++ if (!lirc_serial_dev) {
++ result = -ENOMEM;
++ goto exit_driver_unregister;
++ }
++
++ result = platform_device_add(lirc_serial_dev);
++ if (result)
++ goto exit_device_put;
++
++ return 0;
++
++exit_device_put:
++ platform_device_put(lirc_serial_dev);
++exit_driver_unregister:
++ platform_driver_unregister(&lirc_serial_driver);
++exit_buffer_free:
++ lirc_buffer_free(&rbuf);
++ return result;
++}
++
++static void lirc_serial_exit(void)
++{
++ platform_device_unregister(lirc_serial_dev);
++ platform_driver_unregister(&lirc_serial_driver);
++ lirc_buffer_free(&rbuf);
++}
++
++static int __init lirc_serial_init_module(void)
++{
++ int result;
++
++ result = lirc_serial_init();
++ if (result)
++ return result;
++
++ switch (type) {
++ case LIRC_HOMEBREW:
++ case LIRC_IRDEO:
++ case LIRC_IRDEO_REMOTE:
++ case LIRC_ANIMAX:
++ case LIRC_IGOR:
++ /* if nothing specified, use ttyS0/com1 and irq 4 */
++ io = io ? io : 0x3f8;
++ irq = irq ? irq : 4;
++ break;
++#ifdef CONFIG_LIRC_SERIAL_NSLU2
++ case LIRC_NSLU2:
++ io = io ? io : IRQ_IXP4XX_UART2;
++ irq = irq ? irq : (IXP4XX_UART2_BASE_VIRT + REG_OFFSET);
++ iommap = iommap ? iommap : IXP4XX_UART2_BASE_PHYS;
++ ioshift = ioshift ? ioshift : 2;
++ break;
++#endif
++ default:
++ result = -EINVAL;
++ goto exit_serial_exit;
++ }
++ if (!softcarrier) {
++ switch (type) {
++ case LIRC_HOMEBREW:
++ case LIRC_IGOR:
++#ifdef CONFIG_LIRC_SERIAL_NSLU2
++ case LIRC_NSLU2:
++#endif
++ hardware[type].features &=
++ ~(LIRC_CAN_SET_SEND_DUTY_CYCLE|
++ LIRC_CAN_SET_SEND_CARRIER);
++ break;
++ }
++ }
++
++ result = init_port();
++ if (result < 0)
++ goto exit_serial_exit;
++ driver.features = hardware[type].features;
++ driver.dev = &lirc_serial_dev->dev;
++ driver.minor = lirc_register_driver(&driver);
++ if (driver.minor < 0) {
++ printk(KERN_ERR LIRC_DRIVER_NAME
++ ": register_chrdev failed!\n");
++ result = -EIO;
++ goto exit_release;
++ }
++ return 0;
++exit_release:
++ release_region(io, 8);
++exit_serial_exit:
++ lirc_serial_exit();
++ return result;
++}
++
++static void __exit lirc_serial_exit_module(void)
++{
++ lirc_serial_exit();
++ if (iommap != 0)
++ release_mem_region(iommap, 8 << ioshift);
++ else
++ release_region(io, 8);
++ lirc_unregister_driver(driver.minor);
++ dprintk("cleaned up module\n");
++}
++
++
++module_init(lirc_serial_init_module);
++module_exit(lirc_serial_exit_module);
++
++MODULE_DESCRIPTION("Infra-red receiver driver for serial ports.");
++MODULE_AUTHOR("Ralph Metzler, Trent Piepho, Ben Pfaff, "
++ "Christoph Bartelmus, Andrei Tanas");
++MODULE_LICENSE("GPL");
++
++module_param(type, int, S_IRUGO);
++MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo,"
++ " 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug,"
++ " 5 = NSLU2 RX:CTS2/TX:GreenLED)");
++
++module_param(io, int, S_IRUGO);
++MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
++
++/* some architectures (e.g. intel xscale) have memory mapped registers */
++module_param(iommap, bool, S_IRUGO);
++MODULE_PARM_DESC(iommap, "physical base for memory mapped I/O"
++ " (0 = no memory mapped io)");
++
++/*
++ * some architectures (e.g. intel xscale) align the 8bit serial registers
++ * on 32bit word boundaries.
++ * See linux-kernel/serial/8250.c serial_in()/out()
++ */
++module_param(ioshift, int, S_IRUGO);
++MODULE_PARM_DESC(ioshift, "shift I/O register offset (0 = no shift)");
++
++module_param(irq, int, S_IRUGO);
++MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
++
++module_param(share_irq, bool, S_IRUGO);
++MODULE_PARM_DESC(share_irq, "Share interrupts (0 = off, 1 = on)");
++
++module_param(sense, bool, S_IRUGO);
++MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit"
++ " (0 = active high, 1 = active low )");
++
++#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
++module_param(txsense, bool, S_IRUGO);
++MODULE_PARM_DESC(txsense, "Sense of transmitter circuit"
++ " (0 = active high, 1 = active low )");
++#endif
++
++module_param(softcarrier, bool, S_IRUGO);
++MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)");
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Enable debugging messages");
+diff --git a/drivers/staging/lirc/lirc_sir.c b/drivers/staging/lirc/lirc_sir.c
+new file mode 100644
+index 0000000..97146d1
+--- /dev/null
++++ b/drivers/staging/lirc/lirc_sir.c
+@@ -0,0 +1,1282 @@
++/*
++ * LIRC SIR driver, (C) 2000 Milan Pikula <www@fornax.sk>
++ *
++ * lirc_sir - Device driver for use with SIR (serial infra red)
++ * mode of IrDA on many notebooks.
++ *
++ * 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
++ *
++ *
++ * 2000/09/16 Frank Przybylski <mail@frankprzybylski.de> :
++ * added timeout and relaxed pulse detection, removed gap bug
++ *
++ * 2000/12/15 Christoph Bartelmus <lirc@bartelmus.de> :
++ * added support for Tekram Irmate 210 (sending does not work yet,
++ * kind of disappointing that nobody was able to implement that
++ * before),
++ * major clean-up
++ *
++ * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> :
++ * added support for StrongARM SA1100 embedded microprocessor
++ * parts cut'n'pasted from sa1100_ir.c (C) 2000 Russell King
++ */
++
++#include <linux/module.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/fs.h>
++#include <linux/interrupt.h>
++#include <linux/ioport.h>
++#include <linux/kernel.h>
++#include <linux/serial_reg.h>
++#include <linux/time.h>
++#include <linux/string.h>
++#include <linux/types.h>
++#include <linux/wait.h>
++#include <linux/mm.h>
++#include <linux/delay.h>
++#include <linux/poll.h>
++#include <asm/system.h>
++#include <linux/io.h>
++#include <asm/irq.h>
++#include <linux/fcntl.h>
++#ifdef LIRC_ON_SA1100
++#include <asm/hardware.h>
++#ifdef CONFIG_SA1100_COLLIE
++#include <asm/arch/tc35143.h>
++#include <asm/ucb1200.h>
++#endif
++#endif
++
++#include <linux/timer.h>
++
++#include <media/lirc.h>
++#include <media/lirc_dev.h>
++
++/* SECTION: Definitions */
++
++/*** Tekram dongle ***/
++#ifdef LIRC_SIR_TEKRAM
++/* stolen from kernel source */
++/* definitions for Tekram dongle */
++#define TEKRAM_115200 0x00
++#define TEKRAM_57600 0x01
++#define TEKRAM_38400 0x02
++#define TEKRAM_19200 0x03
++#define TEKRAM_9600 0x04
++#define TEKRAM_2400 0x08
++
++#define TEKRAM_PW 0x10 /* Pulse select bit */
++
++/* 10bit * 1s/115200bit in milliseconds = 87ms*/
++#define TIME_CONST (10000000ul/115200ul)
++
++#endif
++
++#ifdef LIRC_SIR_ACTISYS_ACT200L
++static void init_act200(void);
++#elif defined(LIRC_SIR_ACTISYS_ACT220L)
++static void init_act220(void);
++#endif
++
++/*** SA1100 ***/
++#ifdef LIRC_ON_SA1100
++struct sa1100_ser2_registers {
++ /* HSSP control register */
++ unsigned char hscr0;
++ /* UART registers */
++ unsigned char utcr0;
++ unsigned char utcr1;
++ unsigned char utcr2;
++ unsigned char utcr3;
++ unsigned char utcr4;
++ unsigned char utdr;
++ unsigned char utsr0;
++ unsigned char utsr1;
++} sr;
++
++static int irq = IRQ_Ser2ICP;
++
++#define LIRC_ON_SA1100_TRANSMITTER_LATENCY 0
++
++/* pulse/space ratio of 50/50 */
++static unsigned long pulse_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY);
++/* 1000000/freq-pulse_width */
++static unsigned long space_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY);
++static unsigned int freq = 38000; /* modulation frequency */
++static unsigned int duty_cycle = 50; /* duty cycle of 50% */
++
++#endif
++
++#define RBUF_LEN 1024
++#define WBUF_LEN 1024
++
++#define LIRC_DRIVER_NAME "lirc_sir"
++
++#define PULSE '['
++
++#ifndef LIRC_SIR_TEKRAM
++/* 9bit * 1s/115200bit in milli seconds = 78.125ms*/
++#define TIME_CONST (9000000ul/115200ul)
++#endif
++
++
++/* timeout for sequences in jiffies (=5/100s), must be longer than TIME_CONST */
++#define SIR_TIMEOUT (HZ*5/100)
++
++#ifndef LIRC_ON_SA1100
++#ifndef LIRC_IRQ
++#define LIRC_IRQ 4
++#endif
++#ifndef LIRC_PORT
++/* for external dongles, default to com1 */
++#if defined(LIRC_SIR_ACTISYS_ACT200L) || \
++ defined(LIRC_SIR_ACTISYS_ACT220L) || \
++ defined(LIRC_SIR_TEKRAM)
++#define LIRC_PORT 0x3f8
++#else
++/* onboard sir ports are typically com3 */
++#define LIRC_PORT 0x3e8
++#endif
++#endif
++
++static int io = LIRC_PORT;
++static int irq = LIRC_IRQ;
++static int threshold = 3;
++#endif
++
++static DEFINE_SPINLOCK(timer_lock);
++static struct timer_list timerlist;
++/* time of last signal change detected */
++static struct timeval last_tv = {0, 0};
++/* time of last UART data ready interrupt */
++static struct timeval last_intr_tv = {0, 0};
++static int last_value;
++
++static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
++
++static DEFINE_SPINLOCK(hardware_lock);
++
++static int rx_buf[RBUF_LEN];
++static unsigned int rx_tail, rx_head;
++
++static int debug;
++#define dprintk(fmt, args...) \
++ do { \
++ if (debug) \
++ printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
++ fmt, ## args); \
++ } while (0)
++
++/* SECTION: Prototypes */
++
++/* Communication with user-space */
++static unsigned int lirc_poll(struct file *file, poll_table *wait);
++static ssize_t lirc_read(struct file *file, char *buf, size_t count,
++ loff_t *ppos);
++static ssize_t lirc_write(struct file *file, const char *buf, size_t n,
++ loff_t *pos);
++static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
++static void add_read_queue(int flag, unsigned long val);
++static int init_chrdev(void);
++static void drop_chrdev(void);
++/* Hardware */
++static irqreturn_t sir_interrupt(int irq, void *dev_id);
++static void send_space(unsigned long len);
++static void send_pulse(unsigned long len);
++static int init_hardware(void);
++static void drop_hardware(void);
++/* Initialisation */
++static int init_port(void);
++static void drop_port(void);
++
++#ifdef LIRC_ON_SA1100
++static void on(void)
++{
++ PPSR |= PPC_TXD2;
++}
++
++static void off(void)
++{
++ PPSR &= ~PPC_TXD2;
++}
++#else
++static inline unsigned int sinp(int offset)
++{
++ return inb(io + offset);
++}
++
++static inline void soutp(int offset, int value)
++{
++ outb(value, io + offset);
++}
++#endif
++
++#ifndef MAX_UDELAY_MS
++#define MAX_UDELAY_US 5000
++#else
++#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
++#endif
++
++static void safe_udelay(unsigned long usecs)
++{
++ while (usecs > MAX_UDELAY_US) {
++ udelay(MAX_UDELAY_US);
++ usecs -= MAX_UDELAY_US;
++ }
++ udelay(usecs);
++}
++
++/* SECTION: Communication with user-space */
++
++static unsigned int lirc_poll(struct file *file, poll_table *wait)
++{
++ poll_wait(file, &lirc_read_queue, wait);
++ if (rx_head != rx_tail)
++ return POLLIN | POLLRDNORM;
++ return 0;
++}
++
++static ssize_t lirc_read(struct file *file, char *buf, size_t count,
++ loff_t *ppos)
++{
++ int n = 0;
++ int retval = 0;
++ DECLARE_WAITQUEUE(wait, current);
++
++ if (count % sizeof(int))
++ return -EINVAL;
++
++ add_wait_queue(&lirc_read_queue, &wait);
++ set_current_state(TASK_INTERRUPTIBLE);
++ while (n < count) {
++ if (rx_head != rx_tail) {
++ if (copy_to_user((void *) buf + n,
++ (void *) (rx_buf + rx_head),
++ sizeof(int))) {
++ retval = -EFAULT;
++ break;
++ }
++ rx_head = (rx_head + 1) & (RBUF_LEN - 1);
++ n += sizeof(int);
++ } else {
++ if (file->f_flags & O_NONBLOCK) {
++ retval = -EAGAIN;
++ break;
++ }
++ if (signal_pending(current)) {
++ retval = -ERESTARTSYS;
++ break;
++ }
++ schedule();
++ set_current_state(TASK_INTERRUPTIBLE);
++ }
++ }
++ remove_wait_queue(&lirc_read_queue, &wait);
++ set_current_state(TASK_RUNNING);
++ return n ? n : retval;
++}
++static ssize_t lirc_write(struct file *file, const char *buf, size_t n,
++ loff_t *pos)
++{
++ unsigned long flags;
++ int i, count;
++ int *tx_buf;
++
++ count = n / sizeof(int);
++ if (n % sizeof(int) || count % 2 == 0)
++ return -EINVAL;
++ tx_buf = memdup_user(buf, n);
++ if (IS_ERR(tx_buf))
++ return PTR_ERR(tx_buf);
++ i = 0;
++#ifdef LIRC_ON_SA1100
++ /* disable receiver */
++ Ser2UTCR3 = 0;
++#endif
++ local_irq_save(flags);
++ while (1) {
++ if (i >= count)
++ break;
++ if (tx_buf[i])
++ send_pulse(tx_buf[i]);
++ i++;
++ if (i >= count)
++ break;
++ if (tx_buf[i])
++ send_space(tx_buf[i]);
++ i++;
++ }
++ local_irq_restore(flags);
++#ifdef LIRC_ON_SA1100
++ off();
++ udelay(1000); /* wait 1ms for IR diode to recover */
++ Ser2UTCR3 = 0;
++ /* clear status register to prevent unwanted interrupts */
++ Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
++ /* enable receiver */
++ Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE;
++#endif
++ return count;
++}
++
++static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
++{
++ int retval = 0;
++ unsigned long value = 0;
++#ifdef LIRC_ON_SA1100
++ unsigned int ivalue;
++
++ if (cmd == LIRC_GET_FEATURES)
++ value = LIRC_CAN_SEND_PULSE |
++ LIRC_CAN_SET_SEND_DUTY_CYCLE |
++ LIRC_CAN_SET_SEND_CARRIER |
++ LIRC_CAN_REC_MODE2;
++ else if (cmd == LIRC_GET_SEND_MODE)
++ value = LIRC_MODE_PULSE;
++ else if (cmd == LIRC_GET_REC_MODE)
++ value = LIRC_MODE_MODE2;
++#else
++ if (cmd == LIRC_GET_FEATURES)
++ value = LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
++ else if (cmd == LIRC_GET_SEND_MODE)
++ value = LIRC_MODE_PULSE;
++ else if (cmd == LIRC_GET_REC_MODE)
++ value = LIRC_MODE_MODE2;
++#endif
++
++ switch (cmd) {
++ case LIRC_GET_FEATURES:
++ case LIRC_GET_SEND_MODE:
++ case LIRC_GET_REC_MODE:
++ retval = put_user(value, (unsigned long *) arg);
++ break;
++
++ case LIRC_SET_SEND_MODE:
++ case LIRC_SET_REC_MODE:
++ retval = get_user(value, (unsigned long *) arg);
++ break;
++#ifdef LIRC_ON_SA1100
++ case LIRC_SET_SEND_DUTY_CYCLE:
++ retval = get_user(ivalue, (unsigned int *) arg);
++ if (retval)
++ return retval;
++ if (ivalue <= 0 || ivalue > 100)
++ return -EINVAL;
++ /* (ivalue/100)*(1000000/freq) */
++ duty_cycle = ivalue;
++ pulse_width = (unsigned long) duty_cycle*10000/freq;
++ space_width = (unsigned long) 1000000L/freq-pulse_width;
++ if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
++ pulse_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
++ if (space_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
++ space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
++ break;
++ case LIRC_SET_SEND_CARRIER:
++ retval = get_user(ivalue, (unsigned int *) arg);
++ if (retval)
++ return retval;
++ if (ivalue > 500000 || ivalue < 20000)
++ return -EINVAL;
++ freq = ivalue;
++ pulse_width = (unsigned long) duty_cycle*10000/freq;
++ space_width = (unsigned long) 1000000L/freq-pulse_width;
++ if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
++ pulse_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
++ if (space_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
++ space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
++ break;
++#endif
++ default:
++ retval = -ENOIOCTLCMD;
++
++ }
++
++ if (retval)
++ return retval;
++ if (cmd == LIRC_SET_REC_MODE) {
++ if (value != LIRC_MODE_MODE2)
++ retval = -ENOSYS;
++ } else if (cmd == LIRC_SET_SEND_MODE) {
++ if (value != LIRC_MODE_PULSE)
++ retval = -ENOSYS;
++ }
++
++ return retval;
++}
++
++static void add_read_queue(int flag, unsigned long val)
++{
++ unsigned int new_rx_tail;
++ int newval;
++
++ dprintk("add flag %d with val %lu\n", flag, val);
++
++ newval = val & PULSE_MASK;
++
++ /*
++ * statistically, pulses are ~TIME_CONST/2 too long. we could
++ * maybe make this more exact, but this is good enough
++ */
++ if (flag) {
++ /* pulse */
++ if (newval > TIME_CONST/2)
++ newval -= TIME_CONST/2;
++ else /* should not ever happen */
++ newval = 1;
++ newval |= PULSE_BIT;
++ } else {
++ newval += TIME_CONST/2;
++ }
++ new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1);
++ if (new_rx_tail == rx_head) {
++ dprintk("Buffer overrun.\n");
++ return;
++ }
++ rx_buf[rx_tail] = newval;
++ rx_tail = new_rx_tail;
++ wake_up_interruptible(&lirc_read_queue);
++}
++
++static struct file_operations lirc_fops = {
++ .owner = THIS_MODULE,
++ .read = lirc_read,
++ .write = lirc_write,
++ .poll = lirc_poll,
++ .unlocked_ioctl = lirc_ioctl,
++ .open = lirc_dev_fop_open,
++ .release = lirc_dev_fop_close,
++};
++
++static int set_use_inc(void *data)
++{
++ return 0;
++}
++
++static void set_use_dec(void *data)
++{
++}
++
++static struct lirc_driver driver = {
++ .name = LIRC_DRIVER_NAME,
++ .minor = -1,
++ .code_length = 1,
++ .sample_rate = 0,
++ .data = NULL,
++ .add_to_buf = NULL,
++ .set_use_inc = set_use_inc,
++ .set_use_dec = set_use_dec,
++ .fops = &lirc_fops,
++ .dev = NULL,
++ .owner = THIS_MODULE,
++};
++
++
++static int init_chrdev(void)
++{
++ driver.minor = lirc_register_driver(&driver);
++ if (driver.minor < 0) {
++ printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n");
++ return -EIO;
++ }
++ return 0;
++}
++
++static void drop_chrdev(void)
++{
++ lirc_unregister_driver(driver.minor);
++}
++
++/* SECTION: Hardware */
++static long delta(struct timeval *tv1, struct timeval *tv2)
++{
++ unsigned long deltv;
++
++ deltv = tv2->tv_sec - tv1->tv_sec;
++ if (deltv > 15)
++ deltv = 0xFFFFFF;
++ else
++ deltv = deltv*1000000 +
++ tv2->tv_usec -
++ tv1->tv_usec;
++ return deltv;
++}
++
++static void sir_timeout(unsigned long data)
++{
++ /*
++ * if last received signal was a pulse, but receiving stopped
++ * within the 9 bit frame, we need to finish this pulse and
++ * simulate a signal change to from pulse to space. Otherwise
++ * upper layers will receive two sequences next time.
++ */
++
++ unsigned long flags;
++ unsigned long pulse_end;
++
++ /* avoid interference with interrupt */
++ spin_lock_irqsave(&timer_lock, flags);
++ if (last_value) {
++#ifndef LIRC_ON_SA1100
++ /* clear unread bits in UART and restart */
++ outb(UART_FCR_CLEAR_RCVR, io + UART_FCR);
++#endif
++ /* determine 'virtual' pulse end: */
++ pulse_end = delta(&last_tv, &last_intr_tv);
++ dprintk("timeout add %d for %lu usec\n", last_value, pulse_end);
++ add_read_queue(last_value, pulse_end);
++ last_value = 0;
++ last_tv = last_intr_tv;
++ }
++ spin_unlock_irqrestore(&timer_lock, flags);
++}
++
++static irqreturn_t sir_interrupt(int irq, void *dev_id)
++{
++ unsigned char data;
++ struct timeval curr_tv;
++ static unsigned long deltv;
++#ifdef LIRC_ON_SA1100
++ int status;
++ static int n;
++
++ status = Ser2UTSR0;
++ /*
++ * Deal with any receive errors first. The bytes in error may be
++ * the only bytes in the receive FIFO, so we do this first.
++ */
++ while (status & UTSR0_EIF) {
++ int bstat;
++
++ if (debug) {
++ dprintk("EIF\n");
++ bstat = Ser2UTSR1;
++
++ if (bstat & UTSR1_FRE)
++ dprintk("frame error\n");
++ if (bstat & UTSR1_ROR)
++ dprintk("receive fifo overrun\n");
++ if (bstat & UTSR1_PRE)
++ dprintk("parity error\n");
++ }
++
++ bstat = Ser2UTDR;
++ n++;
++ status = Ser2UTSR0;
++ }
++
++ if (status & (UTSR0_RFS | UTSR0_RID)) {
++ do_gettimeofday(&curr_tv);
++ deltv = delta(&last_tv, &curr_tv);
++ do {
++ data = Ser2UTDR;
++ dprintk("%d data: %u\n", n, (unsigned int) data);
++ n++;
++ } while (status & UTSR0_RID && /* do not empty fifo in order to
++ * get UTSR0_RID in any case */
++ Ser2UTSR1 & UTSR1_RNE); /* data ready */
++
++ if (status&UTSR0_RID) {
++ add_read_queue(0 , deltv - n * TIME_CONST); /*space*/
++ add_read_queue(1, n * TIME_CONST); /*pulse*/
++ n = 0;
++ last_tv = curr_tv;
++ }
++ }
++
++ if (status & UTSR0_TFS)
++ printk(KERN_ERR "transmit fifo not full, shouldn't happen\n");
++
++ /* We must clear certain bits. */
++ status &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
++ if (status)
++ Ser2UTSR0 = status;
++#else
++ unsigned long deltintrtv;
++ unsigned long flags;
++ int iir, lsr;
++
++ while ((iir = inb(io + UART_IIR) & UART_IIR_ID)) {
++ switch (iir&UART_IIR_ID) { /* FIXME toto treba preriedit */
++ case UART_IIR_MSI:
++ (void) inb(io + UART_MSR);
++ break;
++ case UART_IIR_RLSI:
++ (void) inb(io + UART_LSR);
++ break;
++ case UART_IIR_THRI:
++#if 0
++ if (lsr & UART_LSR_THRE) /* FIFO is empty */
++ outb(data, io + UART_TX)
++#endif
++ break;
++ case UART_IIR_RDI:
++ /* avoid interference with timer */
++ spin_lock_irqsave(&timer_lock, flags);
++ do {
++ del_timer(&timerlist);
++ data = inb(io + UART_RX);
++ do_gettimeofday(&curr_tv);
++ deltv = delta(&last_tv, &curr_tv);
++ deltintrtv = delta(&last_intr_tv, &curr_tv);
++ dprintk("t %lu, d %d\n", deltintrtv, (int)data);
++ /*
++ * if nothing came in last X cycles,
++ * it was gap
++ */
++ if (deltintrtv > TIME_CONST * threshold) {
++ if (last_value) {
++ dprintk("GAP\n");
++ /* simulate signal change */
++ add_read_queue(last_value,
++ deltv -
++ deltintrtv);
++ last_value = 0;
++ last_tv.tv_sec =
++ last_intr_tv.tv_sec;
++ last_tv.tv_usec =
++ last_intr_tv.tv_usec;
++ deltv = deltintrtv;
++ }
++ }
++ data = 1;
++ if (data ^ last_value) {
++ /*
++ * deltintrtv > 2*TIME_CONST, remember?
++ * the other case is timeout
++ */
++ add_read_queue(last_value,
++ deltv-TIME_CONST);
++ last_value = data;
++ last_tv = curr_tv;
++ if (last_tv.tv_usec >= TIME_CONST) {
++ last_tv.tv_usec -= TIME_CONST;
++ } else {
++ last_tv.tv_sec--;
++ last_tv.tv_usec += 1000000 -
++ TIME_CONST;
++ }
++ }
++ last_intr_tv = curr_tv;
++ if (data) {
++ /*
++ * start timer for end of
++ * sequence detection
++ */
++ timerlist.expires = jiffies +
++ SIR_TIMEOUT;
++ add_timer(&timerlist);
++ }
++
++ lsr = inb(io + UART_LSR);
++ } while (lsr & UART_LSR_DR); /* data ready */
++ spin_unlock_irqrestore(&timer_lock, flags);
++ break;
++ default:
++ break;
++ }
++ }
++#endif
++ return IRQ_RETVAL(IRQ_HANDLED);
++}
++
++#ifdef LIRC_ON_SA1100
++static void send_pulse(unsigned long length)
++{
++ unsigned long k, delay;
++ int flag;
++
++ if (length == 0)
++ return;
++ /*
++ * this won't give us the carrier frequency we really want
++ * due to integer arithmetic, but we can accept this inaccuracy
++ */
++
++ for (k = flag = 0; k < length; k += delay, flag = !flag) {
++ if (flag) {
++ off();
++ delay = space_width;
++ } else {
++ on();
++ delay = pulse_width;
++ }
++ safe_udelay(delay);
++ }
++ off();
++}
++
++static void send_space(unsigned long length)
++{
++ if (length == 0)
++ return;
++ off();
++ safe_udelay(length);
++}
++#else
++static void send_space(unsigned long len)
++{
++ safe_udelay(len);
++}
++
++static void send_pulse(unsigned long len)
++{
++ long bytes_out = len / TIME_CONST;
++ long time_left;
++
++ time_left = (long)len - (long)bytes_out * (long)TIME_CONST;
++ if (bytes_out == 0) {
++ bytes_out++;
++ time_left = 0;
++ }
++ while (bytes_out--) {
++ outb(PULSE, io + UART_TX);
++ /* FIXME treba seriozne cakanie z char/serial.c */
++ while (!(inb(io + UART_LSR) & UART_LSR_THRE))
++ ;
++ }
++#if 0
++ if (time_left > 0)
++ safe_udelay(time_left);
++#endif
++}
++#endif
++
++#ifdef CONFIG_SA1100_COLLIE
++static int sa1100_irda_set_power_collie(int state)
++{
++ if (state) {
++ /*
++ * 0 - off
++ * 1 - short range, lowest power
++ * 2 - medium range, medium power
++ * 3 - maximum range, high power
++ */
++ ucb1200_set_io_direction(TC35143_GPIO_IR_ON,
++ TC35143_IODIR_OUTPUT);
++ ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_LOW);
++ udelay(100);
++ } else {
++ /* OFF */
++ ucb1200_set_io_direction(TC35143_GPIO_IR_ON,
++ TC35143_IODIR_OUTPUT);
++ ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_HIGH);
++ }
++ return 0;
++}
++#endif
++
++static int init_hardware(void)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&hardware_lock, flags);
++ /* reset UART */
++#ifdef LIRC_ON_SA1100
++#ifdef CONFIG_SA1100_BITSY
++ if (machine_is_bitsy()) {
++ printk(KERN_INFO "Power on IR module\n");
++ set_bitsy_egpio(EGPIO_BITSY_IR_ON);
++ }
++#endif
++#ifdef CONFIG_SA1100_COLLIE
++ sa1100_irda_set_power_collie(3); /* power on */
++#endif
++ sr.hscr0 = Ser2HSCR0;
++
++ sr.utcr0 = Ser2UTCR0;
++ sr.utcr1 = Ser2UTCR1;
++ sr.utcr2 = Ser2UTCR2;
++ sr.utcr3 = Ser2UTCR3;
++ sr.utcr4 = Ser2UTCR4;
++
++ sr.utdr = Ser2UTDR;
++ sr.utsr0 = Ser2UTSR0;
++ sr.utsr1 = Ser2UTSR1;
++
++ /* configure GPIO */
++ /* output */
++ PPDR |= PPC_TXD2;
++ PSDR |= PPC_TXD2;
++ /* set output to 0 */
++ off();
++
++ /* Enable HP-SIR modulation, and ensure that the port is disabled. */
++ Ser2UTCR3 = 0;
++ Ser2HSCR0 = sr.hscr0 & (~HSCR0_HSSP);
++
++ /* clear status register to prevent unwanted interrupts */
++ Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
++
++ /* 7N1 */
++ Ser2UTCR0 = UTCR0_1StpBit|UTCR0_7BitData;
++ /* 115200 */
++ Ser2UTCR1 = 0;
++ Ser2UTCR2 = 1;
++ /* use HPSIR, 1.6 usec pulses */
++ Ser2UTCR4 = UTCR4_HPSIR|UTCR4_Z1_6us;
++
++ /* enable receiver, receive fifo interrupt */
++ Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE;
++
++ /* clear status register to prevent unwanted interrupts */
++ Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
++
++#elif defined(LIRC_SIR_TEKRAM)
++ /* disable FIFO */
++ soutp(UART_FCR,
++ UART_FCR_CLEAR_RCVR|
++ UART_FCR_CLEAR_XMIT|
++ UART_FCR_TRIGGER_1);
++
++ /* Set DLAB 0. */
++ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
++
++ /* First of all, disable all interrupts */
++ soutp(UART_IER, sinp(UART_IER) &
++ (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
++
++ /* Set DLAB 1. */
++ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
++
++ /* Set divisor to 12 => 9600 Baud */
++ soutp(UART_DLM, 0);
++ soutp(UART_DLL, 12);
++
++ /* Set DLAB 0. */
++ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
++
++ /* power supply */
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
++ safe_udelay(50*1000);
++
++ /* -DTR low -> reset PIC */
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
++ udelay(1*1000);
++
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
++ udelay(100);
++
++
++ /* -RTS low -> send control byte */
++ soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
++ udelay(7);
++ soutp(UART_TX, TEKRAM_115200|TEKRAM_PW);
++
++ /* one byte takes ~1042 usec to transmit at 9600,8N1 */
++ udelay(1500);
++
++ /* back to normal operation */
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
++ udelay(50);
++
++ udelay(1500);
++
++ /* read previous control byte */
++ printk(KERN_INFO LIRC_DRIVER_NAME
++ ": 0x%02x\n", sinp(UART_RX));
++
++ /* Set DLAB 1. */
++ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
++
++ /* Set divisor to 1 => 115200 Baud */
++ soutp(UART_DLM, 0);
++ soutp(UART_DLL, 1);
++
++ /* Set DLAB 0, 8 Bit */
++ soutp(UART_LCR, UART_LCR_WLEN8);
++ /* enable interrupts */
++ soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI);
++#else
++ outb(0, io + UART_MCR);
++ outb(0, io + UART_IER);
++ /* init UART */
++ /* set DLAB, speed = 115200 */
++ outb(UART_LCR_DLAB | UART_LCR_WLEN7, io + UART_LCR);
++ outb(1, io + UART_DLL); outb(0, io + UART_DLM);
++ /* 7N1+start = 9 bits at 115200 ~ 3 bits at 44000 */
++ outb(UART_LCR_WLEN7, io + UART_LCR);
++ /* FIFO operation */
++ outb(UART_FCR_ENABLE_FIFO, io + UART_FCR);
++ /* interrupts */
++ /* outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, io + UART_IER); */
++ outb(UART_IER_RDI, io + UART_IER);
++ /* turn on UART */
++ outb(UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2, io + UART_MCR);
++#ifdef LIRC_SIR_ACTISYS_ACT200L
++ init_act200();
++#elif defined(LIRC_SIR_ACTISYS_ACT220L)
++ init_act220();
++#endif
++#endif
++ spin_unlock_irqrestore(&hardware_lock, flags);
++ return 0;
++}
++
++static void drop_hardware(void)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&hardware_lock, flags);
++
++#ifdef LIRC_ON_SA1100
++ Ser2UTCR3 = 0;
++
++ Ser2UTCR0 = sr.utcr0;
++ Ser2UTCR1 = sr.utcr1;
++ Ser2UTCR2 = sr.utcr2;
++ Ser2UTCR4 = sr.utcr4;
++ Ser2UTCR3 = sr.utcr3;
++
++ Ser2HSCR0 = sr.hscr0;
++#ifdef CONFIG_SA1100_BITSY
++ if (machine_is_bitsy())
++ clr_bitsy_egpio(EGPIO_BITSY_IR_ON);
++#endif
++#ifdef CONFIG_SA1100_COLLIE
++ sa1100_irda_set_power_collie(0); /* power off */
++#endif
++#else
++ /* turn off interrupts */
++ outb(0, io + UART_IER);
++#endif
++ spin_unlock_irqrestore(&hardware_lock, flags);
++}
++
++/* SECTION: Initialisation */
++
++static int init_port(void)
++{
++ int retval;
++
++ /* get I/O port access and IRQ line */
++#ifndef LIRC_ON_SA1100
++ if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) {
++ printk(KERN_ERR LIRC_DRIVER_NAME
++ ": i/o port 0x%.4x already in use.\n", io);
++ return -EBUSY;
++ }
++#endif
++ retval = request_irq(irq, sir_interrupt, IRQF_DISABLED,
++ LIRC_DRIVER_NAME, NULL);
++ if (retval < 0) {
++# ifndef LIRC_ON_SA1100
++ release_region(io, 8);
++# endif
++ printk(KERN_ERR LIRC_DRIVER_NAME
++ ": IRQ %d already in use.\n",
++ irq);
++ return retval;
++ }
++#ifndef LIRC_ON_SA1100
++ printk(KERN_INFO LIRC_DRIVER_NAME
++ ": I/O port 0x%.4x, IRQ %d.\n",
++ io, irq);
++#endif
++
++ init_timer(&timerlist);
++ timerlist.function = sir_timeout;
++ timerlist.data = 0xabadcafe;
++
++ return 0;
++}
++
++static void drop_port(void)
++{
++ free_irq(irq, NULL);
++ del_timer_sync(&timerlist);
++#ifndef LIRC_ON_SA1100
++ release_region(io, 8);
++#endif
++}
++
++#ifdef LIRC_SIR_ACTISYS_ACT200L
++/* Crystal/Cirrus CS8130 IR transceiver, used in Actisys Act200L dongle */
++/* some code borrowed from Linux IRDA driver */
++
++/* Register 0: Control register #1 */
++#define ACT200L_REG0 0x00
++#define ACT200L_TXEN 0x01 /* Enable transmitter */
++#define ACT200L_RXEN 0x02 /* Enable receiver */
++#define ACT200L_ECHO 0x08 /* Echo control chars */
++
++/* Register 1: Control register #2 */
++#define ACT200L_REG1 0x10
++#define ACT200L_LODB 0x01 /* Load new baud rate count value */
++#define ACT200L_WIDE 0x04 /* Expand the maximum allowable pulse */
++
++/* Register 3: Transmit mode register #2 */
++#define ACT200L_REG3 0x30
++#define ACT200L_B0 0x01 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P) */
++#define ACT200L_B1 0x02 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P) */
++#define ACT200L_CHSY 0x04 /* StartBit Synced 0=bittime, 1=startbit */
++
++/* Register 4: Output Power register */
++#define ACT200L_REG4 0x40
++#define ACT200L_OP0 0x01 /* Enable LED1C output */
++#define ACT200L_OP1 0x02 /* Enable LED2C output */
++#define ACT200L_BLKR 0x04
++
++/* Register 5: Receive Mode register */
++#define ACT200L_REG5 0x50
++#define ACT200L_RWIDL 0x01 /* fixed 1.6us pulse mode */
++ /*.. other various IRDA bit modes, and TV remote modes..*/
++
++/* Register 6: Receive Sensitivity register #1 */
++#define ACT200L_REG6 0x60
++#define ACT200L_RS0 0x01 /* receive threshold bit 0 */
++#define ACT200L_RS1 0x02 /* receive threshold bit 1 */
++
++/* Register 7: Receive Sensitivity register #2 */
++#define ACT200L_REG7 0x70
++#define ACT200L_ENPOS 0x04 /* Ignore the falling edge */
++
++/* Register 8,9: Baud Rate Divider register #1,#2 */
++#define ACT200L_REG8 0x80
++#define ACT200L_REG9 0x90
++
++#define ACT200L_2400 0x5f
++#define ACT200L_9600 0x17
++#define ACT200L_19200 0x0b
++#define ACT200L_38400 0x05
++#define ACT200L_57600 0x03
++#define ACT200L_115200 0x01
++
++/* Register 13: Control register #3 */
++#define ACT200L_REG13 0xd0
++#define ACT200L_SHDW 0x01 /* Enable access to shadow registers */
++
++/* Register 15: Status register */
++#define ACT200L_REG15 0xf0
++
++/* Register 21: Control register #4 */
++#define ACT200L_REG21 0x50
++#define ACT200L_EXCK 0x02 /* Disable clock output driver */
++#define ACT200L_OSCL 0x04 /* oscillator in low power, medium accuracy mode */
++
++static void init_act200(void)
++{
++ int i;
++ __u8 control[] = {
++ ACT200L_REG15,
++ ACT200L_REG13 | ACT200L_SHDW,
++ ACT200L_REG21 | ACT200L_EXCK | ACT200L_OSCL,
++ ACT200L_REG13,
++ ACT200L_REG7 | ACT200L_ENPOS,
++ ACT200L_REG6 | ACT200L_RS0 | ACT200L_RS1,
++ ACT200L_REG5 | ACT200L_RWIDL,
++ ACT200L_REG4 | ACT200L_OP0 | ACT200L_OP1 | ACT200L_BLKR,
++ ACT200L_REG3 | ACT200L_B0,
++ ACT200L_REG0 | ACT200L_TXEN | ACT200L_RXEN,
++ ACT200L_REG8 | (ACT200L_115200 & 0x0f),
++ ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f),
++ ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE
++ };
++
++ /* Set DLAB 1. */
++ soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8);
++
++ /* Set divisor to 12 => 9600 Baud */
++ soutp(UART_DLM, 0);
++ soutp(UART_DLL, 12);
++
++ /* Set DLAB 0. */
++ soutp(UART_LCR, UART_LCR_WLEN8);
++ /* Set divisor to 12 => 9600 Baud */
++
++ /* power supply */
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
++ for (i = 0; i < 50; i++)
++ safe_udelay(1000);
++
++ /* Reset the dongle : set RTS low for 25 ms */
++ soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
++ for (i = 0; i < 25; i++)
++ udelay(1000);
++
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
++ udelay(100);
++
++ /* Clear DTR and set RTS to enter command mode */
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
++ udelay(7);
++
++ /* send out the control register settings for 115K 7N1 SIR operation */
++ for (i = 0; i < sizeof(control); i++) {
++ soutp(UART_TX, control[i]);
++ /* one byte takes ~1042 usec to transmit at 9600,8N1 */
++ udelay(1500);
++ }
++
++ /* back to normal operation */
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
++ udelay(50);
++
++ udelay(1500);
++ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
++
++ /* Set DLAB 1. */
++ soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7);
++
++ /* Set divisor to 1 => 115200 Baud */
++ soutp(UART_DLM, 0);
++ soutp(UART_DLL, 1);
++
++ /* Set DLAB 0. */
++ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
++
++ /* Set DLAB 0, 7 Bit */
++ soutp(UART_LCR, UART_LCR_WLEN7);
++
++ /* enable interrupts */
++ soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI);
++}
++#endif
++
++#ifdef LIRC_SIR_ACTISYS_ACT220L
++/*
++ * Derived from linux IrDA driver (net/irda/actisys.c)
++ * Drop me a mail for any kind of comment: maxx@spaceboyz.net
++ */
++
++void init_act220(void)
++{
++ int i;
++
++ /* DLAB 1 */
++ soutp(UART_LCR, UART_LCR_DLAB|UART_LCR_WLEN7);
++
++ /* 9600 baud */
++ soutp(UART_DLM, 0);
++ soutp(UART_DLL, 12);
++
++ /* DLAB 0 */
++ soutp(UART_LCR, UART_LCR_WLEN7);
++
++ /* reset the dongle, set DTR low for 10us */
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
++ udelay(10);
++
++ /* back to normal (still 9600) */
++ soutp(UART_MCR, UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2);
++
++ /*
++ * send RTS pulses until we reach 115200
++ * i hope this is really the same for act220l/act220l+
++ */
++ for (i = 0; i < 3; i++) {
++ udelay(10);
++ /* set RTS low for 10 us */
++ soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
++ udelay(10);
++ /* set RTS high for 10 us */
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
++ }
++
++ /* back to normal operation */
++ udelay(1500); /* better safe than sorry ;) */
++
++ /* Set DLAB 1. */
++ soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7);
++
++ /* Set divisor to 1 => 115200 Baud */
++ soutp(UART_DLM, 0);
++ soutp(UART_DLL, 1);
++
++ /* Set DLAB 0, 7 Bit */
++ /* The dongle doesn't seem to have any problems with operation at 7N1 */
++ soutp(UART_LCR, UART_LCR_WLEN7);
++
++ /* enable interrupts */
++ soutp(UART_IER, UART_IER_RDI);
++}
++#endif
++
++static int init_lirc_sir(void)
++{
++ int retval;
++
++ init_waitqueue_head(&lirc_read_queue);
++ retval = init_port();
++ if (retval < 0)
++ return retval;
++ init_hardware();
++ printk(KERN_INFO LIRC_DRIVER_NAME
++ ": Installed.\n");
++ return 0;
++}
++
++
++static int __init lirc_sir_init(void)
++{
++ int retval;
++
++ retval = init_chrdev();
++ if (retval < 0)
++ return retval;
++ retval = init_lirc_sir();
++ if (retval) {
++ drop_chrdev();
++ return retval;
++ }
++ return 0;
++}
++
++static void __exit lirc_sir_exit(void)
++{
++ drop_hardware();
++ drop_chrdev();
++ drop_port();
++ printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
++}
++
++module_init(lirc_sir_init);
++module_exit(lirc_sir_exit);
++
++#ifdef LIRC_SIR_TEKRAM
++MODULE_DESCRIPTION("Infrared receiver driver for Tekram Irmate 210");
++MODULE_AUTHOR("Christoph Bartelmus");
++#elif defined(LIRC_ON_SA1100)
++MODULE_DESCRIPTION("LIRC driver for StrongARM SA1100 embedded microprocessor");
++MODULE_AUTHOR("Christoph Bartelmus");
++#elif defined(LIRC_SIR_ACTISYS_ACT200L)
++MODULE_DESCRIPTION("LIRC driver for Actisys Act200L");
++MODULE_AUTHOR("Karl Bongers");
++#elif defined(LIRC_SIR_ACTISYS_ACT220L)
++MODULE_DESCRIPTION("LIRC driver for Actisys Act220L(+)");
++MODULE_AUTHOR("Jan Roemisch");
++#else
++MODULE_DESCRIPTION("Infrared receiver driver for SIR type serial ports");
++MODULE_AUTHOR("Milan Pikula");
++#endif
++MODULE_LICENSE("GPL");
++
++#ifdef LIRC_ON_SA1100
++module_param(irq, int, S_IRUGO);
++MODULE_PARM_DESC(irq, "Interrupt (16)");
++#else
++module_param(io, int, S_IRUGO);
++MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
++
++module_param(irq, int, S_IRUGO);
++MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
++
++module_param(threshold, int, S_IRUGO);
++MODULE_PARM_DESC(threshold, "space detection threshold (3)");
++#endif
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Enable debugging messages");
+diff --git a/drivers/staging/lirc/lirc_streamzap.c b/drivers/staging/lirc/lirc_streamzap.c
+new file mode 100644
+index 0000000..5b46ac4
+--- /dev/null
++++ b/drivers/staging/lirc/lirc_streamzap.c
+@@ -0,0 +1,821 @@
++/*
++ * Streamzap Remote Control driver
++ *
++ * Copyright (c) 2005 Christoph Bartelmus <lirc@bartelmus.de>
++ *
++ * This driver was based on the work of Greg Wickham and Adrian
++ * Dewhurst. It was substantially rewritten to support correct signal
++ * gaps and now maintains a delay buffer, which is used to present
++ * consistent timing behaviour to user space applications. Without the
++ * delay buffer an ugly hack would be required in lircd, which can
++ * cause sluggish signal decoding in certain situations.
++ *
++ * This driver is based on the USB skeleton driver packaged with the
++ * kernel; copyright (C) 2001-2003 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; 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/kernel.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/smp_lock.h>
++#include <linux/completion.h>
++#include <linux/uaccess.h>
++#include <linux/usb.h>
++
++#include <media/lirc.h>
++#include <media/lirc_dev.h>
++
++#define DRIVER_VERSION "1.28"
++#define DRIVER_NAME "lirc_streamzap"
++#define DRIVER_DESC "Streamzap Remote Control driver"
++
++static int debug;
++
++#define USB_STREAMZAP_VENDOR_ID 0x0e9c
++#define USB_STREAMZAP_PRODUCT_ID 0x0000
++
++/* Use our own dbg macro */
++#define dprintk(fmt, args...) \
++ do { \
++ if (debug) \
++ printk(KERN_DEBUG DRIVER_NAME "[%d]: " \
++ fmt "\n", ## args); \
++ } while (0)
++
++/* table of devices that work with this driver */
++static struct usb_device_id streamzap_table[] = {
++ /* Streamzap Remote Control */
++ { USB_DEVICE(USB_STREAMZAP_VENDOR_ID, USB_STREAMZAP_PRODUCT_ID) },
++ /* Terminating entry */
++ { }
++};
++
++MODULE_DEVICE_TABLE(usb, streamzap_table);
++
++#define STREAMZAP_PULSE_MASK 0xf0
++#define STREAMZAP_SPACE_MASK 0x0f
++#define STREAMZAP_TIMEOUT 0xff
++#define STREAMZAP_RESOLUTION 256
++
++/* number of samples buffered */
++#define STREAMZAP_BUF_LEN 128
++
++enum StreamzapDecoderState {
++ PulseSpace,
++ FullPulse,
++ FullSpace,
++ IgnorePulse
++};
++
++/* Structure to hold all of our device specific stuff
++ *
++ * some remarks regarding locking:
++ * theoretically this struct can be accessed from three threads:
++ *
++ * - from lirc_dev through set_use_inc/set_use_dec
++ *
++ * - from the USB layer throuh probe/disconnect/irq
++ *
++ * Careful placement of lirc_register_driver/lirc_unregister_driver
++ * calls will prevent conflicts. lirc_dev makes sure that
++ * set_use_inc/set_use_dec are not being executed and will not be
++ * called after lirc_unregister_driver returns.
++ *
++ * - by the timer callback
++ *
++ * The timer is only running when the device is connected and the
++ * LIRC device is open. Making sure the timer is deleted by
++ * set_use_dec will make conflicts impossible.
++ */
++struct usb_streamzap {
++
++ /* usb */
++ /* save off the usb device pointer */
++ struct usb_device *udev;
++ /* the interface for this device */
++ struct usb_interface *interface;
++
++ /* buffer & dma */
++ unsigned char *buf_in;
++ dma_addr_t dma_in;
++ unsigned int buf_in_len;
++
++ struct usb_endpoint_descriptor *endpoint;
++
++ /* IRQ */
++ struct urb *urb_in;
++
++ /* lirc */
++ struct lirc_driver *driver;
++ struct lirc_buffer *delay_buf;
++
++ /* timer used to support delay buffering */
++ struct timer_list delay_timer;
++ int timer_running;
++ spinlock_t timer_lock;
++
++ /* tracks whether we are currently receiving some signal */
++ int idle;
++ /* sum of signal lengths received since signal start */
++ unsigned long sum;
++ /* start time of signal; necessary for gap tracking */
++ struct timeval signal_last;
++ struct timeval signal_start;
++ enum StreamzapDecoderState decoder_state;
++ struct timer_list flush_timer;
++ int flush;
++ int in_use;
++ int timeout_enabled;
++};
++
++
++/* local function prototypes */
++static int streamzap_probe(struct usb_interface *interface,
++ const struct usb_device_id *id);
++static void streamzap_disconnect(struct usb_interface *interface);
++static void usb_streamzap_irq(struct urb *urb);
++static int streamzap_use_inc(void *data);
++static void streamzap_use_dec(void *data);
++static long streamzap_ioctl(struct file *filep, unsigned int cmd,
++ unsigned long arg);
++static int streamzap_suspend(struct usb_interface *intf, pm_message_t message);
++static int streamzap_resume(struct usb_interface *intf);
++
++/* usb specific object needed to register this driver with the usb subsystem */
++
++static struct usb_driver streamzap_driver = {
++ .name = DRIVER_NAME,
++ .probe = streamzap_probe,
++ .disconnect = streamzap_disconnect,
++ .suspend = streamzap_suspend,
++ .resume = streamzap_resume,
++ .id_table = streamzap_table,
++};
++
++static void stop_timer(struct usb_streamzap *sz)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&sz->timer_lock, flags);
++ if (sz->timer_running) {
++ sz->timer_running = 0;
++ spin_unlock_irqrestore(&sz->timer_lock, flags);
++ del_timer_sync(&sz->delay_timer);
++ } else {
++ spin_unlock_irqrestore(&sz->timer_lock, flags);
++ }
++}
++
++static void flush_timeout(unsigned long arg)
++{
++ struct usb_streamzap *sz = (struct usb_streamzap *) arg;
++
++ /* finally start accepting data */
++ sz->flush = 0;
++}
++static void delay_timeout(unsigned long arg)
++{
++ unsigned long flags;
++ /* deliver data every 10 ms */
++ static unsigned long timer_inc =
++ (10000/(1000000/HZ)) == 0 ? 1 : (10000/(1000000/HZ));
++ struct usb_streamzap *sz = (struct usb_streamzap *) arg;
++ int data;
++
++ spin_lock_irqsave(&sz->timer_lock, flags);
++
++ if (!lirc_buffer_empty(sz->delay_buf) &&
++ !lirc_buffer_full(sz->driver->rbuf)) {
++ lirc_buffer_read(sz->delay_buf, (unsigned char *) &data);
++ lirc_buffer_write(sz->driver->rbuf, (unsigned char *) &data);
++ }
++ if (!lirc_buffer_empty(sz->delay_buf)) {
++ while (lirc_buffer_available(sz->delay_buf) <
++ STREAMZAP_BUF_LEN / 2 &&
++ !lirc_buffer_full(sz->driver->rbuf)) {
++ lirc_buffer_read(sz->delay_buf,
++ (unsigned char *) &data);
++ lirc_buffer_write(sz->driver->rbuf,
++ (unsigned char *) &data);
++ }
++ if (sz->timer_running) {
++ sz->delay_timer.expires = jiffies + timer_inc;
++ add_timer(&sz->delay_timer);
++ }
++ } else {
++ sz->timer_running = 0;
++ }
++
++ if (!lirc_buffer_empty(sz->driver->rbuf))
++ wake_up(&sz->driver->rbuf->wait_poll);
++
++ spin_unlock_irqrestore(&sz->timer_lock, flags);
++}
++
++static void flush_delay_buffer(struct usb_streamzap *sz)
++{
++ int data;
++ int empty = 1;
++
++ while (!lirc_buffer_empty(sz->delay_buf)) {
++ empty = 0;
++ lirc_buffer_read(sz->delay_buf, (unsigned char *) &data);
++ if (!lirc_buffer_full(sz->driver->rbuf)) {
++ lirc_buffer_write(sz->driver->rbuf,
++ (unsigned char *) &data);
++ } else {
++ dprintk("buffer overflow", sz->driver->minor);
++ }
++ }
++ if (!empty)
++ wake_up(&sz->driver->rbuf->wait_poll);
++}
++
++static void push(struct usb_streamzap *sz, unsigned char *data)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&sz->timer_lock, flags);
++ if (lirc_buffer_full(sz->delay_buf)) {
++ int read_data;
++
++ lirc_buffer_read(sz->delay_buf,
++ (unsigned char *) &read_data);
++ if (!lirc_buffer_full(sz->driver->rbuf)) {
++ lirc_buffer_write(sz->driver->rbuf,
++ (unsigned char *) &read_data);
++ } else {
++ dprintk("buffer overflow", sz->driver->minor);
++ }
++ }
++
++ lirc_buffer_write(sz->delay_buf, data);
++
++ if (!sz->timer_running) {
++ sz->delay_timer.expires = jiffies + HZ/10;
++ add_timer(&sz->delay_timer);
++ sz->timer_running = 1;
++ }
++
++ spin_unlock_irqrestore(&sz->timer_lock, flags);
++}
++
++static void push_full_pulse(struct usb_streamzap *sz,
++ unsigned char value)
++{
++ int pulse;
++
++ if (sz->idle) {
++ long deltv;
++ int tmp;
++
++ sz->signal_last = sz->signal_start;
++ do_gettimeofday(&sz->signal_start);
++
++ deltv = sz->signal_start.tv_sec-sz->signal_last.tv_sec;
++ if (deltv > 15) {
++ /* really long time */
++ tmp = LIRC_SPACE(LIRC_VALUE_MASK);
++ } else {
++ tmp = (int) (deltv*1000000+
++ sz->signal_start.tv_usec -
++ sz->signal_last.tv_usec);
++ tmp -= sz->sum;
++ tmp = LIRC_SPACE(tmp);
++ }
++ dprintk("ls %u", sz->driver->minor, tmp);
++ push(sz, (char *)&tmp);
++
++ sz->idle = 0;
++ sz->sum = 0;
++ }
++
++ pulse = ((int) value) * STREAMZAP_RESOLUTION;
++ pulse += STREAMZAP_RESOLUTION / 2;
++ sz->sum += pulse;
++ pulse = LIRC_PULSE(pulse);
++
++ dprintk("p %u", sz->driver->minor, pulse & PULSE_MASK);
++ push(sz, (char *)&pulse);
++}
++
++static void push_half_pulse(struct usb_streamzap *sz,
++ unsigned char value)
++{
++ push_full_pulse(sz, (value & STREAMZAP_PULSE_MASK)>>4);
++}
++
++static void push_full_space(struct usb_streamzap *sz,
++ unsigned char value)
++{
++ int space;
++
++ space = ((int) value)*STREAMZAP_RESOLUTION;
++ space += STREAMZAP_RESOLUTION/2;
++ sz->sum += space;
++ space = LIRC_SPACE(space);
++ dprintk("s %u", sz->driver->minor, space);
++ push(sz, (char *)&space);
++}
++
++static void push_half_space(struct usb_streamzap *sz,
++ unsigned char value)
++{
++ push_full_space(sz, value & STREAMZAP_SPACE_MASK);
++}
++
++/**
++ * usb_streamzap_irq - IRQ handler
++ *
++ * This procedure is invoked on reception of data from
++ * the usb remote.
++ */
++static void usb_streamzap_irq(struct urb *urb)
++{
++ struct usb_streamzap *sz;
++ int len;
++ unsigned int i = 0;
++
++ if (!urb)
++ return;
++
++ sz = urb->context;
++ len = urb->actual_length;
++
++ switch (urb->status) {
++ case -ECONNRESET:
++ case -ENOENT:
++ case -ESHUTDOWN:
++ /*
++ * this urb is terminated, clean up.
++ * sz might already be invalid at this point
++ */
++ dprintk("urb status: %d", -1, urb->status);
++ return;
++ default:
++ break;
++ }
++
++ dprintk("received %d", sz->driver->minor, urb->actual_length);
++ if (!sz->flush) {
++ for (i = 0; i < urb->actual_length; i++) {
++ dprintk("%d: %x", sz->driver->minor,
++ i, (unsigned char) sz->buf_in[i]);
++ switch (sz->decoder_state) {
++ case PulseSpace:
++ if ((sz->buf_in[i]&STREAMZAP_PULSE_MASK) ==
++ STREAMZAP_PULSE_MASK) {
++ sz->decoder_state = FullPulse;
++ continue;
++ } else if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK)
++ == STREAMZAP_SPACE_MASK) {
++ push_half_pulse(sz, sz->buf_in[i]);
++ sz->decoder_state = FullSpace;
++ continue;
++ } else {
++ push_half_pulse(sz, sz->buf_in[i]);
++ push_half_space(sz, sz->buf_in[i]);
++ }
++ break;
++ case FullPulse:
++ push_full_pulse(sz, sz->buf_in[i]);
++ sz->decoder_state = IgnorePulse;
++ break;
++ case FullSpace:
++ if (sz->buf_in[i] == STREAMZAP_TIMEOUT) {
++ sz->idle = 1;
++ stop_timer(sz);
++ if (sz->timeout_enabled) {
++ int timeout =
++ LIRC_TIMEOUT
++ (STREAMZAP_TIMEOUT *
++ STREAMZAP_RESOLUTION);
++ push(sz, (char *)&timeout);
++ }
++ flush_delay_buffer(sz);
++ } else
++ push_full_space(sz, sz->buf_in[i]);
++ sz->decoder_state = PulseSpace;
++ break;
++ case IgnorePulse:
++ if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK) ==
++ STREAMZAP_SPACE_MASK) {
++ sz->decoder_state = FullSpace;
++ continue;
++ }
++ push_half_space(sz, sz->buf_in[i]);
++ sz->decoder_state = PulseSpace;
++ break;
++ }
++ }
++ }
++
++ usb_submit_urb(urb, GFP_ATOMIC);
++
++ return;
++}
++
++static struct file_operations streamzap_fops = {
++ .owner = THIS_MODULE,
++ .unlocked_ioctl = streamzap_ioctl,
++ .read = lirc_dev_fop_read,
++ .write = lirc_dev_fop_write,
++ .poll = lirc_dev_fop_poll,
++ .open = lirc_dev_fop_open,
++ .release = lirc_dev_fop_close,
++};
++
++
++/**
++ * streamzap_probe
++ *
++ * Called by usb-core to associated with a candidate device
++ * On any failure the return value is the ERROR
++ * On success return 0
++ */
++static int streamzap_probe(struct usb_interface *interface,
++ const struct usb_device_id *id)
++{
++ struct usb_device *udev = interface_to_usbdev(interface);
++ struct usb_host_interface *iface_host;
++ struct usb_streamzap *sz;
++ struct lirc_driver *driver;
++ struct lirc_buffer *lirc_buf;
++ struct lirc_buffer *delay_buf;
++ char buf[63], name[128] = "";
++ int retval = -ENOMEM;
++ int minor = 0;
++
++ /* Allocate space for device driver specific data */
++ sz = kzalloc(sizeof(struct usb_streamzap), GFP_KERNEL);
++ if (sz == NULL)
++ return -ENOMEM;
++
++ sz->udev = udev;
++ sz->interface = interface;
++
++ /* Check to ensure endpoint information matches requirements */
++ iface_host = interface->cur_altsetting;
++
++ if (iface_host->desc.bNumEndpoints != 1) {
++ err("%s: Unexpected desc.bNumEndpoints (%d)", __func__,
++ iface_host->desc.bNumEndpoints);
++ retval = -ENODEV;
++ goto free_sz;
++ }
++
++ sz->endpoint = &(iface_host->endpoint[0].desc);
++ if ((sz->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
++ != USB_DIR_IN) {
++ err("%s: endpoint doesn't match input device 02%02x",
++ __func__, sz->endpoint->bEndpointAddress);
++ retval = -ENODEV;
++ goto free_sz;
++ }
++
++ if ((sz->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
++ != USB_ENDPOINT_XFER_INT) {
++ err("%s: endpoint attributes don't match xfer 02%02x",
++ __func__, sz->endpoint->bmAttributes);
++ retval = -ENODEV;
++ goto free_sz;
++ }
++
++ if (sz->endpoint->wMaxPacketSize == 0) {
++ err("%s: endpoint message size==0? ", __func__);
++ retval = -ENODEV;
++ goto free_sz;
++ }
++
++ /* Allocate the USB buffer and IRQ URB */
++
++ sz->buf_in_len = sz->endpoint->wMaxPacketSize;
++ sz->buf_in = usb_alloc_coherent(sz->udev, sz->buf_in_len,
++ GFP_ATOMIC, &sz->dma_in);
++ if (sz->buf_in == NULL)
++ goto free_sz;
++
++ sz->urb_in = usb_alloc_urb(0, GFP_KERNEL);
++ if (sz->urb_in == NULL)
++ goto free_sz;
++
++ /* Connect this device to the LIRC sub-system */
++ driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
++ if (!driver)
++ goto free_sz;
++
++ lirc_buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
++ if (!lirc_buf)
++ goto free_driver;
++ if (lirc_buffer_init(lirc_buf, sizeof(int), STREAMZAP_BUF_LEN))
++ goto kfree_lirc_buf;
++
++ delay_buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
++ if (!delay_buf)
++ goto free_lirc_buf;
++ if (lirc_buffer_init(delay_buf, sizeof(int), STREAMZAP_BUF_LEN))
++ goto kfree_delay_buf;
++
++ sz->driver = driver;
++ strcpy(sz->driver->name, DRIVER_NAME);
++ sz->driver->minor = -1;
++ sz->driver->sample_rate = 0;
++ sz->driver->code_length = sizeof(int) * 8;
++ sz->driver->features = LIRC_CAN_REC_MODE2 |
++ LIRC_CAN_GET_REC_RESOLUTION |
++ LIRC_CAN_SET_REC_TIMEOUT;
++ sz->driver->data = sz;
++ sz->driver->min_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION;
++ sz->driver->max_timeout = STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION;
++ sz->driver->rbuf = lirc_buf;
++ sz->delay_buf = delay_buf;
++ sz->driver->set_use_inc = &streamzap_use_inc;
++ sz->driver->set_use_dec = &streamzap_use_dec;
++ sz->driver->fops = &streamzap_fops;
++ sz->driver->dev = &interface->dev;
++ sz->driver->owner = THIS_MODULE;
++
++ sz->idle = 1;
++ sz->decoder_state = PulseSpace;
++ init_timer(&sz->delay_timer);
++ sz->delay_timer.function = delay_timeout;
++ sz->delay_timer.data = (unsigned long) sz;
++ sz->timer_running = 0;
++ spin_lock_init(&sz->timer_lock);
++
++ init_timer(&sz->flush_timer);
++ sz->flush_timer.function = flush_timeout;
++ sz->flush_timer.data = (unsigned long) sz;
++ /* Complete final initialisations */
++
++ usb_fill_int_urb(sz->urb_in, udev,
++ usb_rcvintpipe(udev, sz->endpoint->bEndpointAddress),
++ sz->buf_in, sz->buf_in_len, usb_streamzap_irq, sz,
++ sz->endpoint->bInterval);
++ sz->urb_in->transfer_dma = sz->dma_in;
++ sz->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
++
++ if (udev->descriptor.iManufacturer
++ && usb_string(udev, udev->descriptor.iManufacturer,
++ buf, sizeof(buf)) > 0)
++ strlcpy(name, buf, sizeof(name));
++
++ if (udev->descriptor.iProduct
++ && usb_string(udev, udev->descriptor.iProduct,
++ buf, sizeof(buf)) > 0)
++ snprintf(name + strlen(name), sizeof(name) - strlen(name),
++ " %s", buf);
++
++ minor = lirc_register_driver(driver);
++
++ if (minor < 0)
++ goto free_delay_buf;
++
++ sz->driver->minor = minor;
++
++ usb_set_intfdata(interface, sz);
++
++ printk(KERN_INFO DRIVER_NAME "[%d]: %s on usb%d:%d attached\n",
++ sz->driver->minor, name,
++ udev->bus->busnum, sz->udev->devnum);
++
++ return 0;
++
++free_delay_buf:
++ lirc_buffer_free(sz->delay_buf);
++kfree_delay_buf:
++ kfree(delay_buf);
++free_lirc_buf:
++ lirc_buffer_free(sz->driver->rbuf);
++kfree_lirc_buf:
++ kfree(lirc_buf);
++free_driver:
++ kfree(driver);
++free_sz:
++ if (retval == -ENOMEM)
++ err("Out of memory");
++
++ if (sz) {
++ usb_free_urb(sz->urb_in);
++ usb_free_coherent(udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
++ kfree(sz);
++ }
++
++ return retval;
++}
++
++static int streamzap_use_inc(void *data)
++{
++ struct usb_streamzap *sz = data;
++
++ if (!sz) {
++ dprintk("%s called with no context", -1, __func__);
++ return -EINVAL;
++ }
++ dprintk("set use inc", sz->driver->minor);
++
++ lirc_buffer_clear(sz->driver->rbuf);
++ lirc_buffer_clear(sz->delay_buf);
++
++ sz->flush_timer.expires = jiffies + HZ;
++ sz->flush = 1;
++ add_timer(&sz->flush_timer);
++
++ sz->urb_in->dev = sz->udev;
++ if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
++ dprintk("open result = -EIO error submitting urb",
++ sz->driver->minor);
++ return -EIO;
++ }
++ sz->in_use++;
++
++ return 0;
++}
++
++static void streamzap_use_dec(void *data)
++{
++ struct usb_streamzap *sz = data;
++
++ if (!sz) {
++ dprintk("%s called with no context", -1, __func__);
++ return;
++ }
++ dprintk("set use dec", sz->driver->minor);
++
++ if (sz->flush) {
++ sz->flush = 0;
++ del_timer_sync(&sz->flush_timer);
++ }
++
++ usb_kill_urb(sz->urb_in);
++
++ stop_timer(sz);
++
++ sz->in_use--;
++}
++
++static long streamzap_ioctl(struct file *filep, unsigned int cmd,
++ unsigned long arg)
++{
++ int result = 0;
++ int val;
++ struct usb_streamzap *sz = lirc_get_pdata(filep);
++
++ switch (cmd) {
++ case LIRC_GET_REC_RESOLUTION:
++ result = put_user(STREAMZAP_RESOLUTION, (unsigned int *) arg);
++ break;
++ case LIRC_SET_REC_TIMEOUT:
++ result = get_user(val, (int *)arg);
++ if (result == 0) {
++ if (val == STREAMZAP_TIMEOUT * STREAMZAP_RESOLUTION)
++ sz->timeout_enabled = 1;
++ else if (val == 0)
++ sz->timeout_enabled = 0;
++ else
++ result = -EINVAL;
++ }
++ break;
++ default:
++ return lirc_dev_fop_ioctl(filep, cmd, arg);
++ }
++ return result;
++}
++
++/**
++ * streamzap_disconnect
++ *
++ * Called by the usb core when the device is removed from the system.
++ *
++ * This routine guarantees that the driver will not submit any more urbs
++ * by clearing dev->udev. It is also supposed to terminate any currently
++ * active urbs. Unfortunately, usb_bulk_msg(), used in streamzap_read(),
++ * does not provide any way to do this.
++ */
++static void streamzap_disconnect(struct usb_interface *interface)
++{
++ struct usb_streamzap *sz;
++ int errnum;
++ int minor;
++
++ sz = usb_get_intfdata(interface);
++
++ /* unregister from the LIRC sub-system */
++
++ errnum = lirc_unregister_driver(sz->driver->minor);
++ if (errnum != 0)
++ dprintk("error in lirc_unregister: (returned %d)",
++ sz->driver->minor, errnum);
++
++ lirc_buffer_free(sz->delay_buf);
++ lirc_buffer_free(sz->driver->rbuf);
++
++ /* unregister from the USB sub-system */
++
++ usb_free_urb(sz->urb_in);
++
++ usb_free_coherent(sz->udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
++
++ minor = sz->driver->minor;
++ kfree(sz->driver->rbuf);
++ kfree(sz->driver);
++ kfree(sz->delay_buf);
++ kfree(sz);
++
++ printk(KERN_INFO DRIVER_NAME "[%d]: disconnected\n", minor);
++}
++
++static int streamzap_suspend(struct usb_interface *intf, pm_message_t message)
++{
++ struct usb_streamzap *sz = usb_get_intfdata(intf);
++
++ printk(KERN_INFO DRIVER_NAME "[%d]: suspend\n", sz->driver->minor);
++ if (sz->in_use) {
++ if (sz->flush) {
++ sz->flush = 0;
++ del_timer_sync(&sz->flush_timer);
++ }
++
++ stop_timer(sz);
++
++ usb_kill_urb(sz->urb_in);
++ }
++ return 0;
++}
++
++static int streamzap_resume(struct usb_interface *intf)
++{
++ struct usb_streamzap *sz = usb_get_intfdata(intf);
++
++ lirc_buffer_clear(sz->driver->rbuf);
++ lirc_buffer_clear(sz->delay_buf);
++
++ if (sz->in_use) {
++ sz->flush_timer.expires = jiffies + HZ;
++ sz->flush = 1;
++ add_timer(&sz->flush_timer);
++
++ sz->urb_in->dev = sz->udev;
++ if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
++ dprintk("open result = -EIO error submitting urb",
++ sz->driver->minor);
++ return -EIO;
++ }
++ }
++ return 0;
++}
++
++/**
++ * usb_streamzap_init
++ */
++static int __init usb_streamzap_init(void)
++{
++ int result;
++
++ /* register this driver with the USB subsystem */
++ result = usb_register(&streamzap_driver);
++
++ if (result) {
++ err("usb_register failed. Error number %d",
++ result);
++ return result;
++ }
++
++ printk(KERN_INFO DRIVER_NAME " " DRIVER_VERSION " registered\n");
++ return 0;
++}
++
++/**
++ * usb_streamzap_exit
++ */
++static void __exit usb_streamzap_exit(void)
++{
++ usb_deregister(&streamzap_driver);
++}
++
++
++module_init(usb_streamzap_init);
++module_exit(usb_streamzap_exit);
++
++MODULE_AUTHOR("Christoph Bartelmus, Greg Wickham, Adrian Dewhurst");
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_LICENSE("GPL");
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Enable debugging messages");
+diff --git a/drivers/staging/lirc/lirc_ttusbir.c b/drivers/staging/lirc/lirc_ttusbir.c
+new file mode 100644
+index 0000000..1f1da47
+--- /dev/null
++++ b/drivers/staging/lirc/lirc_ttusbir.c
+@@ -0,0 +1,397 @@
++/*
++ * lirc_ttusbir.c
++ *
++ * lirc_ttusbir - LIRC device driver for the TechnoTrend USB IR Receiver
++ *
++ * Copyright (C) 2007 Stefan Macher <st_maker-lirc@yahoo.de>
++ *
++ * This LIRC driver provides access to the TechnoTrend USB IR Receiver.
++ * The receiver delivers the IR signal as raw sampled true/false data in
++ * isochronous USB packets each of size 128 byte.
++ * Currently the driver reduces the sampling rate by factor of 8 as this
++ * is still more than enough to decode RC-5 - others should be analyzed.
++ * But the driver does not rely on RC-5 it should be able to decode every
++ * IR signal that is not too fast.
++ */
++
++/*
++ * 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/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/usb.h>
++
++#include <media/lirc.h>
++#include <media/lirc_dev.h>
++
++MODULE_DESCRIPTION("TechnoTrend USB IR device driver for LIRC");
++MODULE_AUTHOR("Stefan Macher (st_maker-lirc@yahoo.de)");
++MODULE_LICENSE("GPL");
++
++/* #define DEBUG */
++#ifdef DEBUG
++#define DPRINTK printk
++#else
++#define DPRINTK(_x_, a...)
++#endif
++
++/* function declarations */
++static int probe(struct usb_interface *intf, const struct usb_device_id *id);
++static void disconnect(struct usb_interface *intf);
++static void urb_complete(struct urb *urb);
++static int set_use_inc(void *data);
++static void set_use_dec(void *data);
++
++static int num_urbs = 2;
++module_param(num_urbs, int, S_IRUGO);
++MODULE_PARM_DESC(num_urbs,
++ "Number of URBs in queue. Try to increase to 4 in case "
++ "of problems (default: 2; minimum: 2)");
++
++/* table of devices that work with this driver */
++static struct usb_device_id device_id_table[] = {
++ /* TechnoTrend USB IR Receiver */
++ { USB_DEVICE(0x0B48, 0x2003) },
++ /* Terminating entry */
++ { }
++};
++MODULE_DEVICE_TABLE(usb, device_id_table);
++
++/* USB driver definition */
++static struct usb_driver usb_driver = {
++ .name = "TTUSBIR",
++ .id_table = &(device_id_table[0]),
++ .probe = probe,
++ .disconnect = disconnect,
++};
++
++/* USB device definition */
++struct ttusbir_device {
++ struct usb_driver *usb_driver;
++ struct usb_device *udev;
++ struct usb_interface *interf;
++ struct usb_class_driver class_driver;
++ unsigned int ifnum; /* Interface number to use */
++ unsigned int alt_setting; /* alternate setting to use */
++ unsigned int endpoint; /* Endpoint to use */
++ struct urb **urb; /* num_urb URB pointers*/
++ char **buffer; /* 128 byte buffer for each URB */
++ struct lirc_buffer rbuf; /* Buffer towards LIRC */
++ struct lirc_driver driver;
++ int minor;
++ int last_pulse; /* remembers if last received byte was pulse or space */
++ int last_num; /* remembers how many last bytes appeared */
++ int opened;
++};
++
++/*** LIRC specific functions ***/
++static int set_use_inc(void *data)
++{
++ int i, retval;
++ struct ttusbir_device *ttusbir = data;
++
++ DPRINTK("Sending first URBs\n");
++ /* @TODO Do I need to check if I am already opened */
++ ttusbir->opened = 1;
++
++ for (i = 0; i < num_urbs; i++) {
++ retval = usb_submit_urb(ttusbir->urb[i], GFP_KERNEL);
++ if (retval) {
++ err("%s: usb_submit_urb failed on urb %d",
++ __func__, i);
++ return retval;
++ }
++ }
++ return 0;
++}
++
++static void set_use_dec(void *data)
++{
++ struct ttusbir_device *ttusbir = data;
++
++ DPRINTK("Device closed\n");
++
++ ttusbir->opened = 0;
++}
++
++/*** USB specific functions ***/
++
++/*
++ * This mapping table is used to do a very simple filtering of the
++ * input signal.
++ * For a value with at least 4 bits set it returns 0xFF otherwise
++ * 0x00. For faster IR signals this can not be used. But for RC-5 we
++ * still have about 14 samples per pulse/space, i.e. we sample with 14
++ * times higher frequency than the signal frequency
++ */
++const unsigned char map_table[] =
++{
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
++ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
++ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
++ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
++ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
++ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
++ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
++ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
++ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
++ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
++ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
++ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
++ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
++ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
++};
++
++static void urb_complete(struct urb *urb)
++{
++ struct ttusbir_device *ttusbir;
++ unsigned char *buf;
++ int i;
++ int l;
++
++ ttusbir = urb->context;
++
++ if (!ttusbir->opened)
++ return;
++
++ buf = (unsigned char *)urb->transfer_buffer;
++
++ for (i = 0; i < 128; i++) {
++ /* Here we do the filtering and some kind of down sampling */
++ buf[i] = ~map_table[buf[i]];
++ if (ttusbir->last_pulse == buf[i]) {
++ if (ttusbir->last_num < PULSE_MASK/63)
++ ttusbir->last_num++;
++ /*
++ * else we are in a idle period and do not need to
++ * increment any longer
++ */
++ } else {
++ l = ttusbir->last_num * 62; /* about 62 = us/byte */
++ if (ttusbir->last_pulse) /* pulse or space? */
++ l |= PULSE_BIT;
++ if (!lirc_buffer_full(&ttusbir->rbuf)) {
++ lirc_buffer_write(&ttusbir->rbuf, (void *)&l);
++ wake_up_interruptible(&ttusbir->rbuf.wait_poll);
++ }
++ ttusbir->last_num = 0;
++ ttusbir->last_pulse = buf[i];
++ }
++ }
++ usb_submit_urb(urb, GFP_ATOMIC); /* keep data rolling :-) */
++}
++
++/*
++ * Called whenever the USB subsystem thinks we could be the right driver
++ * to handle this device
++ */
++static int probe(struct usb_interface *intf, const struct usb_device_id *id)
++{
++ int alt_set, endp;
++ int found = 0;
++ int i, j;
++ int struct_size;
++ struct usb_host_interface *host_interf;
++ struct usb_interface_descriptor *interf_desc;
++ struct usb_host_endpoint *host_endpoint;
++ struct ttusbir_device *ttusbir;
++
++ DPRINTK("Module ttusbir probe\n");
++
++ /* To reduce memory fragmentation we use only one allocation */
++ struct_size = sizeof(struct ttusbir_device) +
++ (sizeof(struct urb *) * num_urbs) +
++ (sizeof(char *) * num_urbs) +
++ (num_urbs * 128);
++ ttusbir = kzalloc(struct_size, GFP_KERNEL);
++ if (!ttusbir)
++ return -ENOMEM;
++
++ ttusbir->urb = (struct urb **)((char *)ttusbir +
++ sizeof(struct ttusbir_device));
++ ttusbir->buffer = (char **)((char *)ttusbir->urb +
++ (sizeof(struct urb *) * num_urbs));
++ for (i = 0; i < num_urbs; i++)
++ ttusbir->buffer[i] = (char *)ttusbir->buffer +
++ (sizeof(char *)*num_urbs) + (i * 128);
++
++ ttusbir->usb_driver = &usb_driver;
++ ttusbir->alt_setting = -1;
++ /* @TODO check if error can be returned */
++ ttusbir->udev = usb_get_dev(interface_to_usbdev(intf));
++ ttusbir->interf = intf;
++ ttusbir->last_pulse = 0x00;
++ ttusbir->last_num = 0;
++
++ /*
++ * Now look for interface setting we can handle
++ * We are searching for the alt setting where end point
++ * 0x82 has max packet size 16
++ */
++ for (alt_set = 0; alt_set < intf->num_altsetting && !found; alt_set++) {
++ host_interf = &intf->altsetting[alt_set];
++ interf_desc = &host_interf->desc;
++ for (endp = 0; endp < interf_desc->bNumEndpoints; endp++) {
++ host_endpoint = &host_interf->endpoint[endp];
++ if ((host_endpoint->desc.bEndpointAddress == 0x82) &&
++ (host_endpoint->desc.wMaxPacketSize == 0x10)) {
++ ttusbir->alt_setting = alt_set;
++ ttusbir->endpoint = endp;
++ found = 1;
++ break;
++ }
++ }
++ }
++ if (ttusbir->alt_setting != -1)
++ DPRINTK("alt setting: %d\n", ttusbir->alt_setting);
++ else {
++ err("Could not find alternate setting\n");
++ kfree(ttusbir);
++ return -EINVAL;
++ }
++
++ /* OK lets setup this interface setting */
++ usb_set_interface(ttusbir->udev, 0, ttusbir->alt_setting);
++
++ /* Store device info in interface structure */
++ usb_set_intfdata(intf, ttusbir);
++
++ /* Register as a LIRC driver */
++ if (lirc_buffer_init(&ttusbir->rbuf, sizeof(int), 256) < 0) {
++ err("Could not get memory for LIRC data buffer\n");
++ usb_set_intfdata(intf, NULL);
++ kfree(ttusbir);
++ return -ENOMEM;
++ }
++ strcpy(ttusbir->driver.name, "TTUSBIR");
++ ttusbir->driver.minor = -1;
++ ttusbir->driver.code_length = 1;
++ ttusbir->driver.sample_rate = 0;
++ ttusbir->driver.data = ttusbir;
++ ttusbir->driver.add_to_buf = NULL;
++ ttusbir->driver.rbuf = &ttusbir->rbuf;
++ ttusbir->driver.set_use_inc = set_use_inc;
++ ttusbir->driver.set_use_dec = set_use_dec;
++ ttusbir->driver.dev = &intf->dev;
++ ttusbir->driver.owner = THIS_MODULE;
++ ttusbir->driver.features = LIRC_CAN_REC_MODE2;
++ ttusbir->minor = lirc_register_driver(&ttusbir->driver);
++ if (ttusbir->minor < 0) {
++ err("Error registering as LIRC driver\n");
++ usb_set_intfdata(intf, NULL);
++ lirc_buffer_free(&ttusbir->rbuf);
++ kfree(ttusbir);
++ return -EIO;
++ }
++
++ /* Allocate and setup the URB that we will use to talk to the device */
++ for (i = 0; i < num_urbs; i++) {
++ ttusbir->urb[i] = usb_alloc_urb(8, GFP_KERNEL);
++ if (!ttusbir->urb[i]) {
++ err("Could not allocate memory for the URB\n");
++ for (j = i - 1; j >= 0; j--)
++ kfree(ttusbir->urb[j]);
++ lirc_buffer_free(&ttusbir->rbuf);
++ lirc_unregister_driver(ttusbir->minor);
++ kfree(ttusbir);
++ usb_set_intfdata(intf, NULL);
++ return -ENOMEM;
++ }
++ ttusbir->urb[i]->dev = ttusbir->udev;
++ ttusbir->urb[i]->context = ttusbir;
++ ttusbir->urb[i]->pipe = usb_rcvisocpipe(ttusbir->udev,
++ ttusbir->endpoint);
++ ttusbir->urb[i]->interval = 1;
++ ttusbir->urb[i]->transfer_flags = URB_ISO_ASAP;
++ ttusbir->urb[i]->transfer_buffer = &ttusbir->buffer[i][0];
++ ttusbir->urb[i]->complete = urb_complete;
++ ttusbir->urb[i]->number_of_packets = 8;
++ ttusbir->urb[i]->transfer_buffer_length = 128;
++ for (j = 0; j < 8; j++) {
++ ttusbir->urb[i]->iso_frame_desc[j].offset = j*16;
++ ttusbir->urb[i]->iso_frame_desc[j].length = 16;
++ }
++ }
++ return 0;
++}
++
++/**
++ * Called when the driver is unloaded or the device is unplugged
++ */
++static void disconnect(struct usb_interface *intf)
++{
++ int i;
++ struct ttusbir_device *ttusbir;
++
++ DPRINTK("Module ttusbir disconnect\n");
++
++ ttusbir = (struct ttusbir_device *) usb_get_intfdata(intf);
++ usb_set_intfdata(intf, NULL);
++ lirc_unregister_driver(ttusbir->minor);
++ DPRINTK("unregistered\n");
++
++ for (i = 0; i < num_urbs; i++) {
++ usb_kill_urb(ttusbir->urb[i]);
++ usb_free_urb(ttusbir->urb[i]);
++ }
++ DPRINTK("URBs killed\n");
++ lirc_buffer_free(&ttusbir->rbuf);
++ kfree(ttusbir);
++}
++
++static int ttusbir_init_module(void)
++{
++ int result;
++
++ DPRINTK(KERN_DEBUG "Module ttusbir init\n");
++
++ /* register this driver with the USB subsystem */
++ result = usb_register(&usb_driver);
++ if (result)
++ err("usb_register failed. Error number %d", result);
++ return result;
++}
++
++static void ttusbir_exit_module(void)
++{
++ printk(KERN_DEBUG "Module ttusbir exit\n");
++ usb_deregister(&usb_driver);
++}
++
++module_init(ttusbir_init_module);
++module_exit(ttusbir_exit_module);
+diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c
+new file mode 100644
+index 0000000..1b013bf
+--- /dev/null
++++ b/drivers/staging/lirc/lirc_zilog.c
+@@ -0,0 +1,1387 @@
++/*
++ * i2c IR lirc driver for devices with zilog IR processors
++ *
++ * Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
++ * modified for PixelView (BT878P+W/FM) by
++ * Michal Kochanowicz <mkochano@pld.org.pl>
++ * Christoph Bartelmus <lirc@bartelmus.de>
++ * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by
++ * Ulrich Mueller <ulrich.mueller42@web.de>
++ * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by
++ * Stefan Jahn <stefan@lkcc.org>
++ * modified for inclusion into kernel sources by
++ * Jerome Brock <jbrock@users.sourceforge.net>
++ * modified for Leadtek Winfast PVR2000 by
++ * Thomas Reitmayr (treitmayr@yahoo.com)
++ * modified for Hauppauge PVR-150 IR TX device by
++ * Mark Weaver <mark@npsl.co.uk>
++ * changed name from lirc_pvr150 to lirc_zilog, works on more than pvr-150
++ * Jarod Wilson <jarod@redhat.com>
++ *
++ * parts are cut&pasted from the lirc_i2c.c driver
++ *
++ * 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/version.h>
++#include <linux/module.h>
++#include <linux/kmod.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/fs.h>
++#include <linux/poll.h>
++#include <linux/string.h>
++#include <linux/timer.h>
++#include <linux/delay.h>
++#include <linux/completion.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/i2c.h>
++#include <linux/firmware.h>
++#include <linux/vmalloc.h>
++
++#include <linux/mutex.h>
++#include <linux/kthread.h>
++
++#include <media/lirc_dev.h>
++#include <media/lirc.h>
++
++struct IR {
++ struct lirc_driver l;
++
++ /* Device info */
++ struct mutex ir_lock;
++ int open;
++
++ /* RX device */
++ struct i2c_client c_rx;
++ int have_rx;
++
++ /* RX device buffer & lock */
++ struct lirc_buffer buf;
++ struct mutex buf_lock;
++
++ /* RX polling thread data */
++ struct completion *t_notify;
++ struct completion *t_notify2;
++ int shutdown;
++ struct task_struct *task;
++
++ /* RX read data */
++ unsigned char b[3];
++
++ /* TX device */
++ struct i2c_client c_tx;
++ int need_boot;
++ int have_tx;
++};
++
++/* Minor -> data mapping */
++static struct IR *ir_devices[MAX_IRCTL_DEVICES];
++
++/* Block size for IR transmitter */
++#define TX_BLOCK_SIZE 99
++
++/* Hauppauge IR transmitter data */
++struct tx_data_struct {
++ /* Boot block */
++ unsigned char *boot_data;
++
++ /* Start of binary data block */
++ unsigned char *datap;
++
++ /* End of binary data block */
++ unsigned char *endp;
++
++ /* Number of installed codesets */
++ unsigned int num_code_sets;
++
++ /* Pointers to codesets */
++ unsigned char **code_sets;
++
++ /* Global fixed data template */
++ int fixed[TX_BLOCK_SIZE];
++};
++
++static struct tx_data_struct *tx_data;
++static struct mutex tx_data_lock;
++
++#define zilog_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, \
++ ## args)
++#define zilog_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args)
++
++#define ZILOG_HAUPPAUGE_IR_RX_NAME "Zilog/Hauppauge IR RX"
++#define ZILOG_HAUPPAUGE_IR_TX_NAME "Zilog/Hauppauge IR TX"
++
++/* module parameters */
++static int debug; /* debug output */
++static int disable_rx; /* disable RX device */
++static int disable_tx; /* disable TX device */
++static int minor = -1; /* minor number */
++
++#define dprintk(fmt, args...) \
++ do { \
++ if (debug) \
++ printk(KERN_DEBUG KBUILD_MODNAME ": " fmt, \
++ ## args); \
++ } while (0)
++
++static int add_to_buf(struct IR *ir)
++{
++ __u16 code;
++ unsigned char codes[2];
++ unsigned char keybuf[6];
++ int got_data = 0;
++ int ret;
++ int failures = 0;
++ unsigned char sendbuf[1] = { 0 };
++
++ if (lirc_buffer_full(&ir->buf)) {
++ dprintk("buffer overflow\n");
++ return -EOVERFLOW;
++ }
++
++ /*
++ * service the device as long as it is returning
++ * data and we have space
++ */
++ do {
++ /*
++ * Lock i2c bus for the duration. RX/TX chips interfere so
++ * this is worth it
++ */
++ mutex_lock(&ir->ir_lock);
++
++ /*
++ * Send random "poll command" (?) Windows driver does this
++ * and it is a good point to detect chip failure.
++ */
++ ret = i2c_master_send(&ir->c_rx, sendbuf, 1);
++ if (ret != 1) {
++ zilog_error("i2c_master_send failed with %d\n", ret);
++ if (failures >= 3) {
++ mutex_unlock(&ir->ir_lock);
++ zilog_error("unable to read from the IR chip "
++ "after 3 resets, giving up\n");
++ return ret;
++ }
++
++ /* Looks like the chip crashed, reset it */
++ zilog_error("polling the IR receiver chip failed, "
++ "trying reset\n");
++
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ schedule_timeout((100 * HZ + 999) / 1000);
++ ir->need_boot = 1;
++
++ ++failures;
++ mutex_unlock(&ir->ir_lock);
++ continue;
++ }
++
++ ret = i2c_master_recv(&ir->c_rx, keybuf, sizeof(keybuf));
++ mutex_unlock(&ir->ir_lock);
++ if (ret != sizeof(keybuf)) {
++ zilog_error("i2c_master_recv failed with %d -- "
++ "keeping last read buffer\n", ret);
++ } else {
++ ir->b[0] = keybuf[3];
++ ir->b[1] = keybuf[4];
++ ir->b[2] = keybuf[5];
++ dprintk("key (0x%02x/0x%02x)\n", ir->b[0], ir->b[1]);
++ }
++
++ /* key pressed ? */
++#ifdef I2C_HW_B_HDPVR
++ if (ir->c_rx.adapter->id == I2C_HW_B_HDPVR) {
++ if (got_data && (keybuf[0] == 0x80))
++ return 0;
++ else if (got_data && (keybuf[0] == 0x00))
++ return -ENODATA;
++ } else if ((ir->b[0] & 0x80) == 0)
++#else
++ if ((ir->b[0] & 0x80) == 0)
++#endif
++ return got_data ? 0 : -ENODATA;
++
++ /* look what we have */
++ code = (((__u16)ir->b[0] & 0x7f) << 6) | (ir->b[1] >> 2);
++
++ codes[0] = (code >> 8) & 0xff;
++ codes[1] = code & 0xff;
++
++ /* return it */
++ lirc_buffer_write(&ir->buf, codes);
++ ++got_data;
++ } while (!lirc_buffer_full(&ir->buf));
++
++ return 0;
++}
++
++/*
++ * Main function of the polling thread -- from lirc_dev.
++ * We don't fit the LIRC model at all anymore. This is horrible, but
++ * basically we have a single RX/TX device with a nasty failure mode
++ * that needs to be accounted for across the pair. lirc lets us provide
++ * fops, but prevents us from using the internal polling, etc. if we do
++ * so. Hence the replication. Might be neater to extend the LIRC model
++ * to account for this but I'd think it's a very special case of seriously
++ * messed up hardware.
++ */
++static int lirc_thread(void *arg)
++{
++ struct IR *ir = arg;
++
++ if (ir->t_notify != NULL)
++ complete(ir->t_notify);
++
++ dprintk("poll thread started\n");
++
++ do {
++ if (ir->open) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ /*
++ * This is ~113*2 + 24 + jitter (2*repeat gap +
++ * code length). We use this interval as the chip
++ * resets every time you poll it (bad!). This is
++ * therefore just sufficient to catch all of the
++ * button presses. It makes the remote much more
++ * responsive. You can see the difference by
++ * running irw and holding down a button. With
++ * 100ms, the old polling interval, you'll notice
++ * breaks in the repeat sequence corresponding to
++ * lost keypresses.
++ */
++ schedule_timeout((260 * HZ) / 1000);
++ if (ir->shutdown)
++ break;
++ if (!add_to_buf(ir))
++ wake_up_interruptible(&ir->buf.wait_poll);
++ } else {
++ /* if device not opened so we can sleep half a second */
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(HZ/2);
++ }
++ } while (!ir->shutdown);
++
++ if (ir->t_notify2 != NULL)
++ wait_for_completion(ir->t_notify2);
++
++ ir->task = NULL;
++ if (ir->t_notify != NULL)
++ complete(ir->t_notify);
++
++ dprintk("poll thread ended\n");
++ return 0;
++}
++
++static int set_use_inc(void *data)
++{
++ struct IR *ir = data;
++
++ if (ir->l.owner == NULL || try_module_get(ir->l.owner) == 0)
++ return -ENODEV;
++
++ /* lock bttv in memory while /dev/lirc is in use */
++ /*
++ * this is completely broken code. lirc_unregister_driver()
++ * must be possible even when the device is open
++ */
++ if (ir->c_rx.addr)
++ i2c_use_client(&ir->c_rx);
++ if (ir->c_tx.addr)
++ i2c_use_client(&ir->c_tx);
++
++ return 0;
++}
++
++static void set_use_dec(void *data)
++{
++ struct IR *ir = data;
++
++ if (ir->c_rx.addr)
++ i2c_release_client(&ir->c_rx);
++ if (ir->c_tx.addr)
++ i2c_release_client(&ir->c_tx);
++ if (ir->l.owner != NULL)
++ module_put(ir->l.owner);
++}
++
++/* safe read of a uint32 (always network byte order) */
++static int read_uint32(unsigned char **data,
++ unsigned char *endp, unsigned int *val)
++{
++ if (*data + 4 > endp)
++ return 0;
++ *val = ((*data)[0] << 24) | ((*data)[1] << 16) |
++ ((*data)[2] << 8) | (*data)[3];
++ *data += 4;
++ return 1;
++}
++
++/* safe read of a uint8 */
++static int read_uint8(unsigned char **data,
++ unsigned char *endp, unsigned char *val)
++{
++ if (*data + 1 > endp)
++ return 0;
++ *val = *((*data)++);
++ return 1;
++}
++
++/* safe skipping of N bytes */
++static int skip(unsigned char **data,
++ unsigned char *endp, unsigned int distance)
++{
++ if (*data + distance > endp)
++ return 0;
++ *data += distance;
++ return 1;
++}
++
++/* decompress key data into the given buffer */
++static int get_key_data(unsigned char *buf,
++ unsigned int codeset, unsigned int key)
++{
++ unsigned char *data, *endp, *diffs, *key_block;
++ unsigned char keys, ndiffs, id;
++ unsigned int base, lim, pos, i;
++
++ /* Binary search for the codeset */
++ for (base = 0, lim = tx_data->num_code_sets; lim; lim >>= 1) {
++ pos = base + (lim >> 1);
++ data = tx_data->code_sets[pos];
++
++ if (!read_uint32(&data, tx_data->endp, &i))
++ goto corrupt;
++
++ if (i == codeset)
++ break;
++ else if (codeset > i) {
++ base = pos + 1;
++ --lim;
++ }
++ }
++ /* Not found? */
++ if (!lim)
++ return -EPROTO;
++
++ /* Set end of data block */
++ endp = pos < tx_data->num_code_sets - 1 ?
++ tx_data->code_sets[pos + 1] : tx_data->endp;
++
++ /* Read the block header */
++ if (!read_uint8(&data, endp, &keys) ||
++ !read_uint8(&data, endp, &ndiffs) ||
++ ndiffs > TX_BLOCK_SIZE || keys == 0)
++ goto corrupt;
++
++ /* Save diffs & skip */
++ diffs = data;
++ if (!skip(&data, endp, ndiffs))
++ goto corrupt;
++
++ /* Read the id of the first key */
++ if (!read_uint8(&data, endp, &id))
++ goto corrupt;
++
++ /* Unpack the first key's data */
++ for (i = 0; i < TX_BLOCK_SIZE; ++i) {
++ if (tx_data->fixed[i] == -1) {
++ if (!read_uint8(&data, endp, &buf[i]))
++ goto corrupt;
++ } else {
++ buf[i] = (unsigned char)tx_data->fixed[i];
++ }
++ }
++
++ /* Early out key found/not found */
++ if (key == id)
++ return 0;
++ if (keys == 1)
++ return -EPROTO;
++
++ /* Sanity check */
++ key_block = data;
++ if (!skip(&data, endp, (keys - 1) * (ndiffs + 1)))
++ goto corrupt;
++
++ /* Binary search for the key */
++ for (base = 0, lim = keys - 1; lim; lim >>= 1) {
++ /* Seek to block */
++ unsigned char *key_data;
++ pos = base + (lim >> 1);
++ key_data = key_block + (ndiffs + 1) * pos;
++
++ if (*key_data == key) {
++ /* skip key id */
++ ++key_data;
++
++ /* found, so unpack the diffs */
++ for (i = 0; i < ndiffs; ++i) {
++ unsigned char val;
++ if (!read_uint8(&key_data, endp, &val) ||
++ diffs[i] >= TX_BLOCK_SIZE)
++ goto corrupt;
++ buf[diffs[i]] = val;
++ }
++
++ return 0;
++ } else if (key > *key_data) {
++ base = pos + 1;
++ --lim;
++ }
++ }
++ /* Key not found */
++ return -EPROTO;
++
++corrupt:
++ zilog_error("firmware is corrupt\n");
++ return -EFAULT;
++}
++
++/* send a block of data to the IR TX device */
++static int send_data_block(struct IR *ir, unsigned char *data_block)
++{
++ int i, j, ret;
++ unsigned char buf[5];
++
++ for (i = 0; i < TX_BLOCK_SIZE;) {
++ int tosend = TX_BLOCK_SIZE - i;
++ if (tosend > 4)
++ tosend = 4;
++ buf[0] = (unsigned char)(i + 1);
++ for (j = 0; j < tosend; ++j)
++ buf[1 + j] = data_block[i + j];
++ dprintk("%02x %02x %02x %02x %02x",
++ buf[0], buf[1], buf[2], buf[3], buf[4]);
++ ret = i2c_master_send(&ir->c_tx, buf, tosend + 1);
++ if (ret != tosend + 1) {
++ zilog_error("i2c_master_send failed with %d\n", ret);
++ return ret < 0 ? ret : -EFAULT;
++ }
++ i += tosend;
++ }
++ return 0;
++}
++
++/* send boot data to the IR TX device */
++static int send_boot_data(struct IR *ir)
++{
++ int ret;
++ unsigned char buf[4];
++
++ /* send the boot block */
++ ret = send_data_block(ir, tx_data->boot_data);
++ if (ret != 0)
++ return ret;
++
++ /* kick it off? */
++ buf[0] = 0x00;
++ buf[1] = 0x20;
++ ret = i2c_master_send(&ir->c_tx, buf, 2);
++ if (ret != 2) {
++ zilog_error("i2c_master_send failed with %d\n", ret);
++ return ret < 0 ? ret : -EFAULT;
++ }
++ ret = i2c_master_send(&ir->c_tx, buf, 1);
++ if (ret != 1) {
++ zilog_error("i2c_master_send failed with %d\n", ret);
++ return ret < 0 ? ret : -EFAULT;
++ }
++
++ /* Here comes the firmware version... (hopefully) */
++ ret = i2c_master_recv(&ir->c_tx, buf, 4);
++ if (ret != 4) {
++ zilog_error("i2c_master_recv failed with %d\n", ret);
++ return 0;
++ }
++ if (buf[0] != 0x80) {
++ zilog_error("unexpected IR TX response: %02x\n", buf[0]);
++ return 0;
++ }
++ zilog_notify("Zilog/Hauppauge IR blaster firmware version "
++ "%d.%d.%d loaded\n", buf[1], buf[2], buf[3]);
++
++ return 0;
++}
++
++/* unload "firmware", lock held */
++static void fw_unload_locked(void)
++{
++ if (tx_data) {
++ if (tx_data->code_sets)
++ vfree(tx_data->code_sets);
++
++ if (tx_data->datap)
++ vfree(tx_data->datap);
++
++ vfree(tx_data);
++ tx_data = NULL;
++ dprintk("successfully unloaded IR blaster firmware\n");
++ }
++}
++
++/* unload "firmware" for the IR TX device */
++static void fw_unload(void)
++{
++ mutex_lock(&tx_data_lock);
++ fw_unload_locked();
++ mutex_unlock(&tx_data_lock);
++}
++
++/* load "firmware" for the IR TX device */
++static int fw_load(struct IR *ir)
++{
++ int ret;
++ unsigned int i;
++ unsigned char *data, version, num_global_fixed;
++ const struct firmware *fw_entry;
++
++ /* Already loaded? */
++ mutex_lock(&tx_data_lock);
++ if (tx_data) {
++ ret = 0;
++ goto out;
++ }
++
++ /* Request codeset data file */
++ ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", &ir->c_tx.dev);
++ if (ret != 0) {
++ zilog_error("firmware haup-ir-blaster.bin not available "
++ "(%d)\n", ret);
++ ret = ret < 0 ? ret : -EFAULT;
++ goto out;
++ }
++ dprintk("firmware of size %zu loaded\n", fw_entry->size);
++
++ /* Parse the file */
++ tx_data = vmalloc(sizeof(*tx_data));
++ if (tx_data == NULL) {
++ zilog_error("out of memory\n");
++ release_firmware(fw_entry);
++ ret = -ENOMEM;
++ goto out;
++ }
++ tx_data->code_sets = NULL;
++
++ /* Copy the data so hotplug doesn't get confused and timeout */
++ tx_data->datap = vmalloc(fw_entry->size);
++ if (tx_data->datap == NULL) {
++ zilog_error("out of memory\n");
++ release_firmware(fw_entry);
++ vfree(tx_data);
++ ret = -ENOMEM;
++ goto out;
++ }
++ memcpy(tx_data->datap, fw_entry->data, fw_entry->size);
++ tx_data->endp = tx_data->datap + fw_entry->size;
++ release_firmware(fw_entry); fw_entry = NULL;
++
++ /* Check version */
++ data = tx_data->datap;
++ if (!read_uint8(&data, tx_data->endp, &version))
++ goto corrupt;
++ if (version != 1) {
++ zilog_error("unsupported code set file version (%u, expected"
++ "1) -- please upgrade to a newer driver",
++ version);
++ fw_unload_locked();
++ ret = -EFAULT;
++ goto out;
++ }
++
++ /* Save boot block for later */
++ tx_data->boot_data = data;
++ if (!skip(&data, tx_data->endp, TX_BLOCK_SIZE))
++ goto corrupt;
++
++ if (!read_uint32(&data, tx_data->endp,
++ &tx_data->num_code_sets))
++ goto corrupt;
++
++ dprintk("%u IR blaster codesets loaded\n", tx_data->num_code_sets);
++
++ tx_data->code_sets = vmalloc(
++ tx_data->num_code_sets * sizeof(char *));
++ if (tx_data->code_sets == NULL) {
++ fw_unload_locked();
++ ret = -ENOMEM;
++ goto out;
++ }
++
++ for (i = 0; i < TX_BLOCK_SIZE; ++i)
++ tx_data->fixed[i] = -1;
++
++ /* Read global fixed data template */
++ if (!read_uint8(&data, tx_data->endp, &num_global_fixed) ||
++ num_global_fixed > TX_BLOCK_SIZE)
++ goto corrupt;
++ for (i = 0; i < num_global_fixed; ++i) {
++ unsigned char pos, val;
++ if (!read_uint8(&data, tx_data->endp, &pos) ||
++ !read_uint8(&data, tx_data->endp, &val) ||
++ pos >= TX_BLOCK_SIZE)
++ goto corrupt;
++ tx_data->fixed[pos] = (int)val;
++ }
++
++ /* Filch out the position of each code set */
++ for (i = 0; i < tx_data->num_code_sets; ++i) {
++ unsigned int id;
++ unsigned char keys;
++ unsigned char ndiffs;
++
++ /* Save the codeset position */
++ tx_data->code_sets[i] = data;
++
++ /* Read header */
++ if (!read_uint32(&data, tx_data->endp, &id) ||
++ !read_uint8(&data, tx_data->endp, &keys) ||
++ !read_uint8(&data, tx_data->endp, &ndiffs) ||
++ ndiffs > TX_BLOCK_SIZE || keys == 0)
++ goto corrupt;
++
++ /* skip diff positions */
++ if (!skip(&data, tx_data->endp, ndiffs))
++ goto corrupt;
++
++ /*
++ * After the diffs we have the first key id + data -
++ * global fixed
++ */
++ if (!skip(&data, tx_data->endp,
++ 1 + TX_BLOCK_SIZE - num_global_fixed))
++ goto corrupt;
++
++ /* Then we have keys-1 blocks of key id+diffs */
++ if (!skip(&data, tx_data->endp,
++ (ndiffs + 1) * (keys - 1)))
++ goto corrupt;
++ }
++ ret = 0;
++ goto out;
++
++corrupt:
++ zilog_error("firmware is corrupt\n");
++ fw_unload_locked();
++ ret = -EFAULT;
++
++out:
++ mutex_unlock(&tx_data_lock);
++ return ret;
++}
++
++/* initialise the IR TX device */
++static int tx_init(struct IR *ir)
++{
++ int ret;
++
++ /* Load 'firmware' */
++ ret = fw_load(ir);
++ if (ret != 0)
++ return ret;
++
++ /* Send boot block */
++ ret = send_boot_data(ir);
++ if (ret != 0)
++ return ret;
++ ir->need_boot = 0;
++
++ /* Looks good */
++ return 0;
++}
++
++/* do nothing stub to make LIRC happy */
++static loff_t lseek(struct file *filep, loff_t offset, int orig)
++{
++ return -ESPIPE;
++}
++
++/* copied from lirc_dev */
++static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos)
++{
++ struct IR *ir = (struct IR *)filep->private_data;
++ unsigned char buf[ir->buf.chunk_size];
++ int ret = 0, written = 0;
++ DECLARE_WAITQUEUE(wait, current);
++
++ dprintk("read called\n");
++ if (ir->c_rx.addr == 0)
++ return -ENODEV;
++
++ if (mutex_lock_interruptible(&ir->buf_lock))
++ return -ERESTARTSYS;
++
++ if (n % ir->buf.chunk_size) {
++ dprintk("read result = -EINVAL\n");
++ mutex_unlock(&ir->buf_lock);
++ return -EINVAL;
++ }
++
++ /*
++ * we add ourselves to the task queue before buffer check
++ * to avoid losing scan code (in case when queue is awaken somewhere
++ * between while condition checking and scheduling)
++ */
++ add_wait_queue(&ir->buf.wait_poll, &wait);
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ /*
++ * while we didn't provide 'length' bytes, device is opened in blocking
++ * mode and 'copy_to_user' is happy, wait for data.
++ */
++ while (written < n && ret == 0) {
++ if (lirc_buffer_empty(&ir->buf)) {
++ /*
++ * According to the read(2) man page, 'written' can be
++ * returned as less than 'n', instead of blocking
++ * again, returning -EWOULDBLOCK, or returning
++ * -ERESTARTSYS
++ */
++ if (written)
++ break;
++ if (filep->f_flags & O_NONBLOCK) {
++ ret = -EWOULDBLOCK;
++ break;
++ }
++ if (signal_pending(current)) {
++ ret = -ERESTARTSYS;
++ break;
++ }
++ schedule();
++ set_current_state(TASK_INTERRUPTIBLE);
++ } else {
++ lirc_buffer_read(&ir->buf, buf);
++ ret = copy_to_user((void *)outbuf+written, buf,
++ ir->buf.chunk_size);
++ written += ir->buf.chunk_size;
++ }
++ }
++
++ remove_wait_queue(&ir->buf.wait_poll, &wait);
++ set_current_state(TASK_RUNNING);
++ mutex_unlock(&ir->buf_lock);
++
++ dprintk("read result = %s (%d)\n",
++ ret ? "-EFAULT" : "OK", ret);
++
++ return ret ? ret : written;
++}
++
++/* send a keypress to the IR TX device */
++static int send_code(struct IR *ir, unsigned int code, unsigned int key)
++{
++ unsigned char data_block[TX_BLOCK_SIZE];
++ unsigned char buf[2];
++ int i, ret;
++
++ /* Get data for the codeset/key */
++ ret = get_key_data(data_block, code, key);
++
++ if (ret == -EPROTO) {
++ zilog_error("failed to get data for code %u, key %u -- check "
++ "lircd.conf entries\n", code, key);
++ return ret;
++ } else if (ret != 0)
++ return ret;
++
++ /* Send the data block */
++ ret = send_data_block(ir, data_block);
++ if (ret != 0)
++ return ret;
++
++ /* Send data block length? */
++ buf[0] = 0x00;
++ buf[1] = 0x40;
++ ret = i2c_master_send(&ir->c_tx, buf, 2);
++ if (ret != 2) {
++ zilog_error("i2c_master_send failed with %d\n", ret);
++ return ret < 0 ? ret : -EFAULT;
++ }
++ ret = i2c_master_send(&ir->c_tx, buf, 1);
++ if (ret != 1) {
++ zilog_error("i2c_master_send failed with %d\n", ret);
++ return ret < 0 ? ret : -EFAULT;
++ }
++
++ /* Send finished download? */
++ ret = i2c_master_recv(&ir->c_tx, buf, 1);
++ if (ret != 1) {
++ zilog_error("i2c_master_recv failed with %d\n", ret);
++ return ret < 0 ? ret : -EFAULT;
++ }
++ if (buf[0] != 0xA0) {
++ zilog_error("unexpected IR TX response #1: %02x\n",
++ buf[0]);
++ return -EFAULT;
++ }
++
++ /* Send prepare command? */
++ buf[0] = 0x00;
++ buf[1] = 0x80;
++ ret = i2c_master_send(&ir->c_tx, buf, 2);
++ if (ret != 2) {
++ zilog_error("i2c_master_send failed with %d\n", ret);
++ return ret < 0 ? ret : -EFAULT;
++ }
++
++#ifdef I2C_HW_B_HDPVR
++ /*
++ * The sleep bits aren't necessary on the HD PVR, and in fact, the
++ * last i2c_master_recv always fails with a -5, so for now, we're
++ * going to skip this whole mess and say we're done on the HD PVR
++ */
++ if (ir->c_rx.adapter->id == I2C_HW_B_HDPVR)
++ goto done;
++#endif
++
++ /*
++ * This bit NAKs until the device is ready, so we retry it
++ * sleeping a bit each time. This seems to be what the windows
++ * driver does, approximately.
++ * Try for up to 1s.
++ */
++ for (i = 0; i < 20; ++i) {
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ schedule_timeout((50 * HZ + 999) / 1000);
++ ret = i2c_master_send(&ir->c_tx, buf, 1);
++ if (ret == 1)
++ break;
++ dprintk("NAK expected: i2c_master_send "
++ "failed with %d (try %d)\n", ret, i+1);
++ }
++ if (ret != 1) {
++ zilog_error("IR TX chip never got ready: last i2c_master_send "
++ "failed with %d\n", ret);
++ return ret < 0 ? ret : -EFAULT;
++ }
++
++ /* Seems to be an 'ok' response */
++ i = i2c_master_recv(&ir->c_tx, buf, 1);
++ if (i != 1) {
++ zilog_error("i2c_master_recv failed with %d\n", ret);
++ return -EFAULT;
++ }
++ if (buf[0] != 0x80) {
++ zilog_error("unexpected IR TX response #2: %02x\n", buf[0]);
++ return -EFAULT;
++ }
++
++done:
++ /* Oh good, it worked */
++ dprintk("sent code %u, key %u\n", code, key);
++ return 0;
++}
++
++/*
++ * Write a code to the device. We take in a 32-bit number (an int) and then
++ * decode this to a codeset/key index. The key data is then decompressed and
++ * sent to the device. We have a spin lock as per i2c documentation to prevent
++ * multiple concurrent sends which would probably cause the device to explode.
++ */
++static ssize_t write(struct file *filep, const char *buf, size_t n,
++ loff_t *ppos)
++{
++ struct IR *ir = (struct IR *)filep->private_data;
++ size_t i;
++ int failures = 0;
++
++ if (ir->c_tx.addr == 0)
++ return -ENODEV;
++
++ /* Validate user parameters */
++ if (n % sizeof(int))
++ return -EINVAL;
++
++ /* Lock i2c bus for the duration */
++ mutex_lock(&ir->ir_lock);
++
++ /* Send each keypress */
++ for (i = 0; i < n;) {
++ int ret = 0;
++ int command;
++
++ if (copy_from_user(&command, buf + i, sizeof(command))) {
++ mutex_unlock(&ir->ir_lock);
++ return -EFAULT;
++ }
++
++ /* Send boot data first if required */
++ if (ir->need_boot == 1) {
++ ret = send_boot_data(ir);
++ if (ret == 0)
++ ir->need_boot = 0;
++ }
++
++ /* Send the code */
++ if (ret == 0) {
++ ret = send_code(ir, (unsigned)command >> 16,
++ (unsigned)command & 0xFFFF);
++ if (ret == -EPROTO) {
++ mutex_unlock(&ir->ir_lock);
++ return ret;
++ }
++ }
++
++ /*
++ * Hmm, a failure. If we've had a few then give up, otherwise
++ * try a reset
++ */
++ if (ret != 0) {
++ /* Looks like the chip crashed, reset it */
++ zilog_error("sending to the IR transmitter chip "
++ "failed, trying reset\n");
++
++ if (failures >= 3) {
++ zilog_error("unable to send to the IR chip "
++ "after 3 resets, giving up\n");
++ mutex_unlock(&ir->ir_lock);
++ return ret;
++ }
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ schedule_timeout((100 * HZ + 999) / 1000);
++ ir->need_boot = 1;
++ ++failures;
++ } else
++ i += sizeof(int);
++ }
++
++ /* Release i2c bus */
++ mutex_unlock(&ir->ir_lock);
++
++ /* All looks good */
++ return n;
++}
++
++/* copied from lirc_dev */
++static unsigned int poll(struct file *filep, poll_table *wait)
++{
++ struct IR *ir = (struct IR *)filep->private_data;
++ unsigned int ret;
++
++ dprintk("poll called\n");
++ if (ir->c_rx.addr == 0)
++ return -ENODEV;
++
++ mutex_lock(&ir->buf_lock);
++
++ poll_wait(filep, &ir->buf.wait_poll, wait);
++
++ dprintk("poll result = %s\n",
++ lirc_buffer_empty(&ir->buf) ? "0" : "POLLIN|POLLRDNORM");
++
++ ret = lirc_buffer_empty(&ir->buf) ? 0 : (POLLIN|POLLRDNORM);
++
++ mutex_unlock(&ir->buf_lock);
++ return ret;
++}
++
++static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
++{
++ struct IR *ir = (struct IR *)filep->private_data;
++ int result;
++ unsigned long mode, features = 0;
++
++ if (ir->c_rx.addr != 0)
++ features |= LIRC_CAN_REC_LIRCCODE;
++ if (ir->c_tx.addr != 0)
++ features |= LIRC_CAN_SEND_PULSE;
++
++ switch (cmd) {
++ case LIRC_GET_LENGTH:
++ result = put_user((unsigned long)13,
++ (unsigned long *)arg);
++ break;
++ case LIRC_GET_FEATURES:
++ result = put_user(features, (unsigned long *) arg);
++ break;
++ case LIRC_GET_REC_MODE:
++ if (!(features&LIRC_CAN_REC_MASK))
++ return -ENOSYS;
++
++ result = put_user(LIRC_REC2MODE
++ (features&LIRC_CAN_REC_MASK),
++ (unsigned long *)arg);
++ break;
++ case LIRC_SET_REC_MODE:
++ if (!(features&LIRC_CAN_REC_MASK))
++ return -ENOSYS;
++
++ result = get_user(mode, (unsigned long *)arg);
++ if (!result && !(LIRC_MODE2REC(mode) & features))
++ result = -EINVAL;
++ break;
++ case LIRC_GET_SEND_MODE:
++ if (!(features&LIRC_CAN_SEND_MASK))
++ return -ENOSYS;
++
++ result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg);
++ break;
++ case LIRC_SET_SEND_MODE:
++ if (!(features&LIRC_CAN_SEND_MASK))
++ return -ENOSYS;
++
++ result = get_user(mode, (unsigned long *) arg);
++ if (!result && mode != LIRC_MODE_PULSE)
++ return -EINVAL;
++ break;
++ default:
++ return -EINVAL;
++ }
++ return result;
++}
++
++/*
++ * Open the IR device. Get hold of our IR structure and
++ * stash it in private_data for the file
++ */
++static int open(struct inode *node, struct file *filep)
++{
++ struct IR *ir;
++ int ret;
++
++ /* find our IR struct */
++ unsigned minor = MINOR(node->i_rdev);
++ if (minor >= MAX_IRCTL_DEVICES) {
++ dprintk("minor %d: open result = -ENODEV\n",
++ minor);
++ return -ENODEV;
++ }
++ ir = ir_devices[minor];
++
++ /* increment in use count */
++ mutex_lock(&ir->ir_lock);
++ ++ir->open;
++ ret = set_use_inc(ir);
++ if (ret != 0) {
++ --ir->open;
++ mutex_unlock(&ir->ir_lock);
++ return ret;
++ }
++ mutex_unlock(&ir->ir_lock);
++
++ /* stash our IR struct */
++ filep->private_data = ir;
++
++ return 0;
++}
++
++/* Close the IR device */
++static int close(struct inode *node, struct file *filep)
++{
++ /* find our IR struct */
++ struct IR *ir = (struct IR *)filep->private_data;
++ if (ir == NULL) {
++ zilog_error("close: no private_data attached to the file!\n");
++ return -ENODEV;
++ }
++
++ /* decrement in use count */
++ mutex_lock(&ir->ir_lock);
++ --ir->open;
++ set_use_dec(ir);
++ mutex_unlock(&ir->ir_lock);
++
++ return 0;
++}
++
++static struct lirc_driver lirc_template = {
++ .name = "lirc_zilog",
++ .set_use_inc = set_use_inc,
++ .set_use_dec = set_use_dec,
++ .owner = THIS_MODULE
++};
++
++static int ir_remove(struct i2c_client *client);
++static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id);
++static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg);
++
++static const struct i2c_device_id ir_transceiver_id[] = {
++ /* Generic entry for any IR transceiver */
++ { "ir_video", 0 },
++ /* IR device specific entries should be added here */
++ { "ir_tx_z8f0811_haup", 0 },
++ { "ir_rx_z8f0811_haup", 0 },
++ { }
++};
++
++static struct i2c_driver driver = {
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = "Zilog/Hauppauge i2c IR",
++ },
++ .probe = ir_probe,
++ .remove = ir_remove,
++ .command = ir_command,
++ .id_table = ir_transceiver_id,
++};
++
++static struct file_operations lirc_fops = {
++ .owner = THIS_MODULE,
++ .llseek = lseek,
++ .read = read,
++ .write = write,
++ .poll = poll,
++ .unlocked_ioctl = ioctl,
++ .open = open,
++ .release = close
++};
++
++static int ir_remove(struct i2c_client *client)
++{
++ struct IR *ir = i2c_get_clientdata(client);
++
++ mutex_lock(&ir->ir_lock);
++
++ if (ir->have_rx || ir->have_tx) {
++ DECLARE_COMPLETION(tn);
++ DECLARE_COMPLETION(tn2);
++
++ /* end up polling thread */
++ if (ir->task && !IS_ERR(ir->task)) {
++ ir->t_notify = &tn;
++ ir->t_notify2 = &tn2;
++ ir->shutdown = 1;
++ wake_up_process(ir->task);
++ complete(&tn2);
++ wait_for_completion(&tn);
++ ir->t_notify = NULL;
++ ir->t_notify2 = NULL;
++ }
++
++ } else {
++ mutex_unlock(&ir->ir_lock);
++ zilog_error("%s: detached from something we didn't "
++ "attach to\n", __func__);
++ return -ENODEV;
++ }
++
++ /* unregister lirc driver */
++ if (ir->l.minor >= 0 && ir->l.minor < MAX_IRCTL_DEVICES) {
++ lirc_unregister_driver(ir->l.minor);
++ ir_devices[ir->l.minor] = NULL;
++ }
++
++ /* free memory */
++ lirc_buffer_free(&ir->buf);
++ mutex_unlock(&ir->ir_lock);
++ kfree(ir);
++
++ return 0;
++}
++
++static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
++{
++ struct IR *ir = NULL;
++ struct i2c_adapter *adap = client->adapter;
++ char buf;
++ int ret;
++ int have_rx = 0, have_tx = 0;
++
++ dprintk("%s: adapter id=0x%x, client addr=0x%02x\n",
++ __func__, adap->id, client->addr);
++
++ /*
++ * The external IR receiver is at i2c address 0x71.
++ * The IR transmitter is at 0x70.
++ */
++ client->addr = 0x70;
++
++ if (!disable_tx) {
++ if (i2c_master_recv(client, &buf, 1) == 1)
++ have_tx = 1;
++ dprintk("probe 0x70 @ %s: %s\n",
++ adap->name, have_tx ? "success" : "failed");
++ }
++
++ if (!disable_rx) {
++ client->addr = 0x71;
++ if (i2c_master_recv(client, &buf, 1) == 1)
++ have_rx = 1;
++ dprintk("probe 0x71 @ %s: %s\n",
++ adap->name, have_rx ? "success" : "failed");
++ }
++
++ if (!(have_rx || have_tx)) {
++ zilog_error("%s: no devices found\n", adap->name);
++ goto out_nodev;
++ }
++
++ printk(KERN_INFO "lirc_zilog: chip found with %s\n",
++ have_rx && have_tx ? "RX and TX" :
++ have_rx ? "RX only" : "TX only");
++
++ ir = kzalloc(sizeof(struct IR), GFP_KERNEL);
++
++ if (!ir)
++ goto out_nomem;
++
++ ret = lirc_buffer_init(&ir->buf, 2, BUFLEN / 2);
++ if (ret)
++ goto out_nomem;
++
++ mutex_init(&ir->ir_lock);
++ mutex_init(&ir->buf_lock);
++ ir->need_boot = 1;
++
++ memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver));
++ ir->l.minor = -1;
++
++ /* I2C attach to device */
++ i2c_set_clientdata(client, ir);
++
++ /* initialise RX device */
++ if (have_rx) {
++ DECLARE_COMPLETION(tn);
++ memcpy(&ir->c_rx, client, sizeof(struct i2c_client));
++
++ ir->c_rx.addr = 0x71;
++ strlcpy(ir->c_rx.name, ZILOG_HAUPPAUGE_IR_RX_NAME,
++ I2C_NAME_SIZE);
++
++ /* try to fire up polling thread */
++ ir->t_notify = &tn;
++ ir->task = kthread_run(lirc_thread, ir, "lirc_zilog");
++ if (IS_ERR(ir->task)) {
++ ret = PTR_ERR(ir->task);
++ zilog_error("lirc_register_driver: cannot run "
++ "poll thread %d\n", ret);
++ goto err;
++ }
++ wait_for_completion(&tn);
++ ir->t_notify = NULL;
++ ir->have_rx = 1;
++ }
++
++ /* initialise TX device */
++ if (have_tx) {
++ memcpy(&ir->c_tx, client, sizeof(struct i2c_client));
++ ir->c_tx.addr = 0x70;
++ strlcpy(ir->c_tx.name, ZILOG_HAUPPAUGE_IR_TX_NAME,
++ I2C_NAME_SIZE);
++ ir->have_tx = 1;
++ }
++
++ /* set lirc_dev stuff */
++ ir->l.code_length = 13;
++ ir->l.rbuf = &ir->buf;
++ ir->l.fops = &lirc_fops;
++ ir->l.data = ir;
++ ir->l.minor = minor;
++ ir->l.dev = &adap->dev;
++ ir->l.sample_rate = 0;
++
++ /* register with lirc */
++ ir->l.minor = lirc_register_driver(&ir->l);
++ if (ir->l.minor < 0 || ir->l.minor >= MAX_IRCTL_DEVICES) {
++ zilog_error("ir_attach: \"minor\" must be between 0 and %d "
++ "(%d)!\n", MAX_IRCTL_DEVICES-1, ir->l.minor);
++ ret = -EBADRQC;
++ goto err;
++ }
++
++ /* store this for getting back in open() later on */
++ ir_devices[ir->l.minor] = ir;
++
++ /*
++ * if we have the tx device, load the 'firmware'. We do this
++ * after registering with lirc as otherwise hotplug seems to take
++ * 10s to create the lirc device.
++ */
++ if (have_tx) {
++ /* Special TX init */
++ ret = tx_init(ir);
++ if (ret != 0)
++ goto err;
++ }
++
++ return 0;
++
++err:
++ /* undo everything, hopefully... */
++ if (ir->c_rx.addr)
++ ir_remove(&ir->c_rx);
++ if (ir->c_tx.addr)
++ ir_remove(&ir->c_tx);
++ return ret;
++
++out_nodev:
++ zilog_error("no device found\n");
++ return -ENODEV;
++
++out_nomem:
++ zilog_error("memory allocation failure\n");
++ kfree(ir);
++ return -ENOMEM;
++}
++
++static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg)
++{
++ /* nothing */
++ return 0;
++}
++
++static int __init zilog_init(void)
++{
++ int ret;
++
++ zilog_notify("Zilog/Hauppauge IR driver initializing\n");
++
++ mutex_init(&tx_data_lock);
++
++ request_module("firmware_class");
++
++ ret = i2c_add_driver(&driver);
++ if (ret)
++ zilog_error("initialization failed\n");
++ else
++ zilog_notify("initialization complete\n");
++
++ return ret;
++}
++
++static void __exit zilog_exit(void)
++{
++ i2c_del_driver(&driver);
++ /* if loaded */
++ fw_unload();
++ zilog_notify("Zilog/Hauppauge IR driver unloaded\n");
++}
++
++module_init(zilog_init);
++module_exit(zilog_exit);
++
++MODULE_DESCRIPTION("Zilog/Hauppauge infrared transmitter driver (i2c stack)");
++MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, "
++ "Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver");
++MODULE_LICENSE("GPL");
++/* for compat with old name, which isn't all that accurate anymore */
++MODULE_ALIAS("lirc_pvr150");
++
++module_param(minor, int, 0444);
++MODULE_PARM_DESC(minor, "Preferred minor device number");
++
++module_param(debug, bool, 0644);
++MODULE_PARM_DESC(debug, "Enable debugging messages");
++
++module_param(disable_rx, bool, 0644);
++MODULE_PARM_DESC(disable_rx, "Disable the IR receiver device");
++
++module_param(disable_tx, bool, 0644);
++MODULE_PARM_DESC(disable_tx, "Disable the IR transmitter device");
diff --git a/merge.pl b/merge.pl
new file mode 100755
index 000000000..8c318156a
--- /dev/null
+++ b/merge.pl
@@ -0,0 +1,66 @@
+#! /usr/bin/perl
+
+my @args=@ARGV;
+my %configvalues;
+my @configoptions;
+my $configcounter = 0;
+
+# optionally print out the architecture as the first line of our output
+my $arch = $args[2];
+if (defined $arch) {
+ print "# $arch\n";
+}
+
+# first, read the override file
+
+open (FILE,"$args[0]") || die "Could not open $args[0]";
+while (<FILE>) {
+ my $str = $_;
+ my $configname;
+
+ if (/\# ([\w]+) is not set/) {
+ $configname = $1;
+ } elsif (/([\w]+)=/) {
+ $configname = $1;
+ }
+
+ if (defined($configname) && !exists($configvalues{$configname})) {
+ $configvalues{$configname} = $str;
+ $configoptions[$configcounter] = $configname;
+ $configcounter ++;
+ }
+};
+
+# now, read and output the entire configfile, except for the overridden
+# parts... for those the new value is printed.
+
+open (FILE2,"$args[1]") || die "Could not open $args[1]";
+while (<FILE2>) {
+ my $configname;
+
+ if (/\# ([\w]+) is not set/) {
+ $configname = $1;
+ } elsif (/([\w]+)=/) {
+ $configname = $1;
+ }
+
+ if (defined($configname) && exists($configvalues{$configname})) {
+ print "$configvalues{$configname}";
+ delete($configvalues{$configname});
+ } else {
+ print "$_";
+ }
+}
+
+# now print the new values from the overridden configfile
+my $counter = 0;
+
+while ($counter < $configcounter) {
+ my $configname = $configoptions[$counter];
+ if (exists($configvalues{$configname})) {
+ print "$configvalues{$configname}";
+ }
+ $counter++;
+}
+
+1;
diff --git a/neuter_intel_microcode_load.patch b/neuter_intel_microcode_load.patch
new file mode 100644
index 000000000..2766e439d
--- /dev/null
+++ b/neuter_intel_microcode_load.patch
@@ -0,0 +1,24 @@
+diff --git a/arch/x86/kernel/microcode_intel.c b/arch/x86/kernel/microcode_intel.c
+index ebd193e..c57024a 100644
+--- a/arch/x86/kernel/microcode_intel.c
++++ b/arch/x86/kernel/microcode_intel.c
+@@ -453,9 +453,18 @@ static void microcode_fini_cpu(int cpu)
+ uci->mc = NULL;
+ }
+
++/* we don't ship the broken out files...
++ * instead, we'll just fail here, and load it with microcode_ctl
++ */
++static enum ucode_state noop_request_microcode_fw(int cpu,
++ struct device *device)
++{
++ return UCODE_NFOUND;
++}
++
+ static struct microcode_ops microcode_intel_ops = {
+ .request_microcode_user = request_microcode_user,
+- .request_microcode_fw = request_microcode_fw,
++ .request_microcode_fw = noop_request_microcode_fw,
+ .collect_cpu_info = collect_cpu_info,
+ .apply_microcode = apply_microcode,
+ .microcode_fini_cpu = microcode_fini_cpu,
diff --git a/only-use-alpha2-regulatory-information-from-country-IE.patch b/only-use-alpha2-regulatory-information-from-country-IE.patch
new file mode 100644
index 000000000..5e7459cf8
--- /dev/null
+++ b/only-use-alpha2-regulatory-information-from-country-IE.patch
@@ -0,0 +1,788 @@
+From linville@tuxdriver.com Thu Jul 15 15:40:22 2010
+From: "John W. Linville" <linville@tuxdriver.com>
+Subject: [RFC] wireless: only use alpha2 regulatory information from country IE
+Date: Thu, 15 Jul 2010 15:06:47 -0400
+
+The meaning and/or usage of the country IE is somewhat poorly defined.
+In practice, this means that regulatory rulesets in a country IE are
+often incomplete and might be untrustworthy. This removes the code
+associated with interpreting those rulesets while preserving respect
+for country "alpha2" codes also contained in the country IE.
+
+Signed-off-by: John W. Linville <linville@tuxdriver.com>
+---
+This patch is compile-tested only! Please feel free to suggest that
+I have left something out or missed some nuance of our regulatory
+enforcement code...
+
+ include/net/regulatory.h | 1 -
+ net/wireless/reg.c | 625 +---------------------------------------------
+ 2 files changed, 12 insertions(+), 614 deletions(-)
+
+diff --git a/include/net/regulatory.h b/include/net/regulatory.h
+index f873ee3..9e103a4 100644
+--- a/include/net/regulatory.h
++++ b/include/net/regulatory.h
+@@ -54,7 +54,6 @@ struct regulatory_request {
+ enum nl80211_reg_initiator initiator;
+ char alpha2[2];
+ bool intersect;
+- u32 country_ie_checksum;
+ enum environment_cap country_ie_env;
+ struct list_head list;
+ };
+diff --git a/net/wireless/reg.c b/net/wireless/reg.c
+index 1ac2bdd..678d0bd 100644
+--- a/net/wireless/reg.c
++++ b/net/wireless/reg.c
+@@ -67,17 +67,9 @@ static struct platform_device *reg_pdev;
+ const struct ieee80211_regdomain *cfg80211_regdomain;
+
+ /*
+- * We use this as a place for the rd structure built from the
+- * last parsed country IE to rest until CRDA gets back to us with
+- * what it thinks should apply for the same country
+- */
+-static const struct ieee80211_regdomain *country_ie_regdomain;
+-
+-/*
+ * Protects static reg.c components:
+ * - cfg80211_world_regdom
+ * - cfg80211_regdom
+- * - country_ie_regdomain
+ * - last_request
+ */
+ static DEFINE_MUTEX(reg_mutex);
+@@ -275,25 +267,6 @@ static bool is_user_regdom_saved(void)
+ return true;
+ }
+
+-/**
+- * country_ie_integrity_changes - tells us if the country IE has changed
+- * @checksum: checksum of country IE of fields we are interested in
+- *
+- * If the country IE has not changed you can ignore it safely. This is
+- * useful to determine if two devices are seeing two different country IEs
+- * even on the same alpha2. Note that this will return false if no IE has
+- * been set on the wireless core yet.
+- */
+-static bool country_ie_integrity_changes(u32 checksum)
+-{
+- /* If no IE has been set then the checksum doesn't change */
+- if (unlikely(!last_request->country_ie_checksum))
+- return false;
+- if (unlikely(last_request->country_ie_checksum != checksum))
+- return true;
+- return false;
+-}
+-
+ static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd,
+ const struct ieee80211_regdomain *src_regd)
+ {
+@@ -506,471 +479,6 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range,
+ }
+
+ /*
+- * This is a work around for sanity checking ieee80211_channel_to_frequency()'s
+- * work. ieee80211_channel_to_frequency() can for example currently provide a
+- * 2 GHz channel when in fact a 5 GHz channel was desired. An example would be
+- * an AP providing channel 8 on a country IE triplet when it sent this on the
+- * 5 GHz band, that channel is designed to be channel 8 on 5 GHz, not a 2 GHz
+- * channel.
+- *
+- * This can be removed once ieee80211_channel_to_frequency() takes in a band.
+- */
+-static bool chan_in_band(int chan, enum ieee80211_band band)
+-{
+- int center_freq = ieee80211_channel_to_frequency(chan);
+-
+- switch (band) {
+- case IEEE80211_BAND_2GHZ:
+- if (center_freq <= 2484)
+- return true;
+- return false;
+- case IEEE80211_BAND_5GHZ:
+- if (center_freq >= 5005)
+- return true;
+- return false;
+- default:
+- return false;
+- }
+-}
+-
+-/*
+- * Some APs may send a country IE triplet for each channel they
+- * support and while this is completely overkill and silly we still
+- * need to support it. We avoid making a single rule for each channel
+- * though and to help us with this we use this helper to find the
+- * actual subband end channel. These type of country IE triplet
+- * scenerios are handled then, all yielding two regulaotry rules from
+- * parsing a country IE:
+- *
+- * [1]
+- * [2]
+- * [36]
+- * [40]
+- *
+- * [1]
+- * [2-4]
+- * [5-12]
+- * [36]
+- * [40-44]
+- *
+- * [1-4]
+- * [5-7]
+- * [36-44]
+- * [48-64]
+- *
+- * [36-36]
+- * [40-40]
+- * [44-44]
+- * [48-48]
+- * [52-52]
+- * [56-56]
+- * [60-60]
+- * [64-64]
+- * [100-100]
+- * [104-104]
+- * [108-108]
+- * [112-112]
+- * [116-116]
+- * [120-120]
+- * [124-124]
+- * [128-128]
+- * [132-132]
+- * [136-136]
+- * [140-140]
+- *
+- * Returns 0 if the IE has been found to be invalid in the middle
+- * somewhere.
+- */
+-static int max_subband_chan(enum ieee80211_band band,
+- int orig_cur_chan,
+- int orig_end_channel,
+- s8 orig_max_power,
+- u8 **country_ie,
+- u8 *country_ie_len)
+-{
+- u8 *triplets_start = *country_ie;
+- u8 len_at_triplet = *country_ie_len;
+- int end_subband_chan = orig_end_channel;
+-
+- /*
+- * We'll deal with padding for the caller unless
+- * its not immediate and we don't process any channels
+- */
+- if (*country_ie_len == 1) {
+- *country_ie += 1;
+- *country_ie_len -= 1;
+- return orig_end_channel;
+- }
+-
+- /* Move to the next triplet and then start search */
+- *country_ie += 3;
+- *country_ie_len -= 3;
+-
+- if (!chan_in_band(orig_cur_chan, band))
+- return 0;
+-
+- while (*country_ie_len >= 3) {
+- int end_channel = 0;
+- struct ieee80211_country_ie_triplet *triplet =
+- (struct ieee80211_country_ie_triplet *) *country_ie;
+- int cur_channel = 0, next_expected_chan;
+-
+- /* means last triplet is completely unrelated to this one */
+- if (triplet->ext.reg_extension_id >=
+- IEEE80211_COUNTRY_EXTENSION_ID) {
+- *country_ie -= 3;
+- *country_ie_len += 3;
+- break;
+- }
+-
+- if (triplet->chans.first_channel == 0) {
+- *country_ie += 1;
+- *country_ie_len -= 1;
+- if (*country_ie_len != 0)
+- return 0;
+- break;
+- }
+-
+- if (triplet->chans.num_channels == 0)
+- return 0;
+-
+- /* Monitonically increasing channel order */
+- if (triplet->chans.first_channel <= end_subband_chan)
+- return 0;
+-
+- if (!chan_in_band(triplet->chans.first_channel, band))
+- return 0;
+-
+- /* 2 GHz */
+- if (triplet->chans.first_channel <= 14) {
+- end_channel = triplet->chans.first_channel +
+- triplet->chans.num_channels - 1;
+- }
+- else {
+- end_channel = triplet->chans.first_channel +
+- (4 * (triplet->chans.num_channels - 1));
+- }
+-
+- if (!chan_in_band(end_channel, band))
+- return 0;
+-
+- if (orig_max_power != triplet->chans.max_power) {
+- *country_ie -= 3;
+- *country_ie_len += 3;
+- break;
+- }
+-
+- cur_channel = triplet->chans.first_channel;
+-
+- /* The key is finding the right next expected channel */
+- if (band == IEEE80211_BAND_2GHZ)
+- next_expected_chan = end_subband_chan + 1;
+- else
+- next_expected_chan = end_subband_chan + 4;
+-
+- if (cur_channel != next_expected_chan) {
+- *country_ie -= 3;
+- *country_ie_len += 3;
+- break;
+- }
+-
+- end_subband_chan = end_channel;
+-
+- /* Move to the next one */
+- *country_ie += 3;
+- *country_ie_len -= 3;
+-
+- /*
+- * Padding needs to be dealt with if we processed
+- * some channels.
+- */
+- if (*country_ie_len == 1) {
+- *country_ie += 1;
+- *country_ie_len -= 1;
+- break;
+- }
+-
+- /* If seen, the IE is invalid */
+- if (*country_ie_len == 2)
+- return 0;
+- }
+-
+- if (end_subband_chan == orig_end_channel) {
+- *country_ie = triplets_start;
+- *country_ie_len = len_at_triplet;
+- return orig_end_channel;
+- }
+-
+- return end_subband_chan;
+-}
+-
+-/*
+- * Converts a country IE to a regulatory domain. A regulatory domain
+- * structure has a lot of information which the IE doesn't yet have,
+- * so for the other values we use upper max values as we will intersect
+- * with our userspace regulatory agent to get lower bounds.
+- */
+-static struct ieee80211_regdomain *country_ie_2_rd(
+- enum ieee80211_band band,
+- u8 *country_ie,
+- u8 country_ie_len,
+- u32 *checksum)
+-{
+- struct ieee80211_regdomain *rd = NULL;
+- unsigned int i = 0;
+- char alpha2[2];
+- u32 flags = 0;
+- u32 num_rules = 0, size_of_regd = 0;
+- u8 *triplets_start = NULL;
+- u8 len_at_triplet = 0;
+- /* the last channel we have registered in a subband (triplet) */
+- int last_sub_max_channel = 0;
+-
+- *checksum = 0xDEADBEEF;
+-
+- /* Country IE requirements */
+- BUG_ON(country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN ||
+- country_ie_len & 0x01);
+-
+- alpha2[0] = country_ie[0];
+- alpha2[1] = country_ie[1];
+-
+- /*
+- * Third octet can be:
+- * 'I' - Indoor
+- * 'O' - Outdoor
+- *
+- * anything else we assume is no restrictions
+- */
+- if (country_ie[2] == 'I')
+- flags = NL80211_RRF_NO_OUTDOOR;
+- else if (country_ie[2] == 'O')
+- flags = NL80211_RRF_NO_INDOOR;
+-
+- country_ie += 3;
+- country_ie_len -= 3;
+-
+- triplets_start = country_ie;
+- len_at_triplet = country_ie_len;
+-
+- *checksum ^= ((flags ^ alpha2[0] ^ alpha2[1]) << 8);
+-
+- /*
+- * We need to build a reg rule for each triplet, but first we must
+- * calculate the number of reg rules we will need. We will need one
+- * for each channel subband
+- */
+- while (country_ie_len >= 3) {
+- int end_channel = 0;
+- struct ieee80211_country_ie_triplet *triplet =
+- (struct ieee80211_country_ie_triplet *) country_ie;
+- int cur_sub_max_channel = 0, cur_channel = 0;
+-
+- if (triplet->ext.reg_extension_id >=
+- IEEE80211_COUNTRY_EXTENSION_ID) {
+- country_ie += 3;
+- country_ie_len -= 3;
+- continue;
+- }
+-
+- /*
+- * APs can add padding to make length divisible
+- * by two, required by the spec.
+- */
+- if (triplet->chans.first_channel == 0) {
+- country_ie++;
+- country_ie_len--;
+- /* This is expected to be at the very end only */
+- if (country_ie_len != 0)
+- return NULL;
+- break;
+- }
+-
+- if (triplet->chans.num_channels == 0)
+- return NULL;
+-
+- if (!chan_in_band(triplet->chans.first_channel, band))
+- return NULL;
+-
+- /* 2 GHz */
+- if (band == IEEE80211_BAND_2GHZ)
+- end_channel = triplet->chans.first_channel +
+- triplet->chans.num_channels - 1;
+- else
+- /*
+- * 5 GHz -- For example in country IEs if the first
+- * channel given is 36 and the number of channels is 4
+- * then the individual channel numbers defined for the
+- * 5 GHz PHY by these parameters are: 36, 40, 44, and 48
+- * and not 36, 37, 38, 39.
+- *
+- * See: http://tinyurl.com/11d-clarification
+- */
+- end_channel = triplet->chans.first_channel +
+- (4 * (triplet->chans.num_channels - 1));
+-
+- cur_channel = triplet->chans.first_channel;
+-
+- /*
+- * Enhancement for APs that send a triplet for every channel
+- * or for whatever reason sends triplets with multiple channels
+- * separated when in fact they should be together.
+- */
+- end_channel = max_subband_chan(band,
+- cur_channel,
+- end_channel,
+- triplet->chans.max_power,
+- &country_ie,
+- &country_ie_len);
+- if (!end_channel)
+- return NULL;
+-
+- if (!chan_in_band(end_channel, band))
+- return NULL;
+-
+- cur_sub_max_channel = end_channel;
+-
+- /* Basic sanity check */
+- if (cur_sub_max_channel < cur_channel)
+- return NULL;
+-
+- /*
+- * Do not allow overlapping channels. Also channels
+- * passed in each subband must be monotonically
+- * increasing
+- */
+- if (last_sub_max_channel) {
+- if (cur_channel <= last_sub_max_channel)
+- return NULL;
+- if (cur_sub_max_channel <= last_sub_max_channel)
+- return NULL;
+- }
+-
+- /*
+- * When dot11RegulatoryClassesRequired is supported
+- * we can throw ext triplets as part of this soup,
+- * for now we don't care when those change as we
+- * don't support them
+- */
+- *checksum ^= ((cur_channel ^ cur_sub_max_channel) << 8) |
+- ((cur_sub_max_channel ^ cur_sub_max_channel) << 16) |
+- ((triplet->chans.max_power ^ cur_sub_max_channel) << 24);
+-
+- last_sub_max_channel = cur_sub_max_channel;
+-
+- num_rules++;
+-
+- if (country_ie_len >= 3) {
+- country_ie += 3;
+- country_ie_len -= 3;
+- }
+-
+- /*
+- * Note: this is not a IEEE requirement but
+- * simply a memory requirement
+- */
+- if (num_rules > NL80211_MAX_SUPP_REG_RULES)
+- return NULL;
+- }
+-
+- country_ie = triplets_start;
+- country_ie_len = len_at_triplet;
+-
+- size_of_regd = sizeof(struct ieee80211_regdomain) +
+- (num_rules * sizeof(struct ieee80211_reg_rule));
+-
+- rd = kzalloc(size_of_regd, GFP_KERNEL);
+- if (!rd)
+- return NULL;
+-
+- rd->n_reg_rules = num_rules;
+- rd->alpha2[0] = alpha2[0];
+- rd->alpha2[1] = alpha2[1];
+-
+- /* This time around we fill in the rd */
+- while (country_ie_len >= 3) {
+- int end_channel = 0;
+- struct ieee80211_country_ie_triplet *triplet =
+- (struct ieee80211_country_ie_triplet *) country_ie;
+- struct ieee80211_reg_rule *reg_rule = NULL;
+- struct ieee80211_freq_range *freq_range = NULL;
+- struct ieee80211_power_rule *power_rule = NULL;
+-
+- /*
+- * Must parse if dot11RegulatoryClassesRequired is true,
+- * we don't support this yet
+- */
+- if (triplet->ext.reg_extension_id >=
+- IEEE80211_COUNTRY_EXTENSION_ID) {
+- country_ie += 3;
+- country_ie_len -= 3;
+- continue;
+- }
+-
+- if (triplet->chans.first_channel == 0) {
+- country_ie++;
+- country_ie_len--;
+- break;
+- }
+-
+- reg_rule = &rd->reg_rules[i];
+- freq_range = &reg_rule->freq_range;
+- power_rule = &reg_rule->power_rule;
+-
+- reg_rule->flags = flags;
+-
+- /* 2 GHz */
+- if (band == IEEE80211_BAND_2GHZ)
+- end_channel = triplet->chans.first_channel +
+- triplet->chans.num_channels -1;
+- else
+- end_channel = triplet->chans.first_channel +
+- (4 * (triplet->chans.num_channels - 1));
+-
+- end_channel = max_subband_chan(band,
+- triplet->chans.first_channel,
+- end_channel,
+- triplet->chans.max_power,
+- &country_ie,
+- &country_ie_len);
+-
+- /*
+- * The +10 is since the regulatory domain expects
+- * the actual band edge, not the center of freq for
+- * its start and end freqs, assuming 20 MHz bandwidth on
+- * the channels passed
+- */
+- freq_range->start_freq_khz =
+- MHZ_TO_KHZ(ieee80211_channel_to_frequency(
+- triplet->chans.first_channel) - 10);
+- freq_range->end_freq_khz =
+- MHZ_TO_KHZ(ieee80211_channel_to_frequency(
+- end_channel) + 10);
+-
+- /*
+- * These are large arbitrary values we use to intersect later.
+- * Increment this if we ever support >= 40 MHz channels
+- * in IEEE 802.11
+- */
+- freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40);
+- power_rule->max_antenna_gain = DBI_TO_MBI(100);
+- power_rule->max_eirp = DBM_TO_MBM(triplet->chans.max_power);
+-
+- i++;
+-
+- if (country_ie_len >= 3) {
+- country_ie += 3;
+- country_ie_len -= 3;
+- }
+-
+- BUG_ON(i > NL80211_MAX_SUPP_REG_RULES);
+- }
+-
+- return rd;
+-}
+-
+-
+-/*
+ * Helper for regdom_intersect(), this does the real
+ * mathematical intersection fun
+ */
+@@ -1191,7 +699,6 @@ static int freq_reg_info_regd(struct wiphy *wiphy,
+
+ return -EINVAL;
+ }
+-EXPORT_SYMBOL(freq_reg_info);
+
+ int freq_reg_info(struct wiphy *wiphy,
+ u32 center_freq,
+@@ -1205,6 +712,7 @@ int freq_reg_info(struct wiphy *wiphy,
+ reg_rule,
+ NULL);
+ }
++EXPORT_SYMBOL(freq_reg_info);
+
+ /*
+ * Note that right now we assume the desired channel bandwidth
+@@ -1243,41 +751,8 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band,
+ desired_bw_khz,
+ &reg_rule);
+
+- if (r) {
+- /*
+- * This means no regulatory rule was found in the country IE
+- * with a frequency range on the center_freq's band, since
+- * IEEE-802.11 allows for a country IE to have a subset of the
+- * regulatory information provided in a country we ignore
+- * disabling the channel unless at least one reg rule was
+- * found on the center_freq's band. For details see this
+- * clarification:
+- *
+- * http://tinyurl.com/11d-clarification
+- */
+- if (r == -ERANGE &&
+- last_request->initiator ==
+- NL80211_REGDOM_SET_BY_COUNTRY_IE) {
+- REG_DBG_PRINT("cfg80211: Leaving channel %d MHz "
+- "intact on %s - no rule found in band on "
+- "Country IE\n",
+- chan->center_freq, wiphy_name(wiphy));
+- } else {
+- /*
+- * In this case we know the country IE has at least one reg rule
+- * for the band so we respect its band definitions
+- */
+- if (last_request->initiator ==
+- NL80211_REGDOM_SET_BY_COUNTRY_IE)
+- REG_DBG_PRINT("cfg80211: Disabling "
+- "channel %d MHz on %s due to "
+- "Country IE\n",
+- chan->center_freq, wiphy_name(wiphy));
+- flags |= IEEE80211_CHAN_DISABLED;
+- chan->flags = flags;
+- }
++ if (r)
+ return;
+- }
+
+ power_rule = &reg_rule->power_rule;
+ freq_range = &reg_rule->freq_range;
+@@ -2010,7 +1485,7 @@ EXPORT_SYMBOL(regulatory_hint);
+
+ /* Caller must hold reg_mutex */
+ static bool reg_same_country_ie_hint(struct wiphy *wiphy,
+- u32 country_ie_checksum)
++ char *alpha2, enum environment_cap env)
+ {
+ struct wiphy *request_wiphy;
+
+@@ -2026,13 +1501,17 @@ static bool reg_same_country_ie_hint(struct wiphy *wiphy,
+ return false;
+
+ if (likely(request_wiphy != wiphy))
+- return !country_ie_integrity_changes(country_ie_checksum);
++ return (last_request->alpha2[0] == alpha2[0] &&
++ last_request->alpha2[1] == alpha2[1] &&
++ last_request->country_ie_env == env);
+ /*
+ * We should not have let these through at this point, they
+ * should have been picked up earlier by the first alpha2 check
+ * on the device
+ */
+- if (WARN_ON(!country_ie_integrity_changes(country_ie_checksum)))
++ if (WARN_ON((last_request->alpha2[0] == alpha2[0] &&
++ last_request->alpha2[1] == alpha2[1] &&
++ last_request->country_ie_env == env )))
+ return true;
+ return false;
+ }
+@@ -2048,7 +1527,6 @@ void regulatory_hint_11d(struct wiphy *wiphy,
+ {
+ struct ieee80211_regdomain *rd = NULL;
+ char alpha2[2];
+- u32 checksum = 0;
+ enum environment_cap env = ENVIRON_ANY;
+ struct regulatory_request *request;
+
+@@ -2064,14 +1542,6 @@ void regulatory_hint_11d(struct wiphy *wiphy,
+ if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN)
+ goto out;
+
+- /*
+- * Pending country IE processing, this can happen after we
+- * call CRDA and wait for a response if a beacon was received before
+- * we were able to process the last regulatory_hint_11d() call
+- */
+- if (country_ie_regdomain)
+- goto out;
+-
+ alpha2[0] = country_ie[0];
+ alpha2[1] = country_ie[1];
+
+@@ -2090,12 +1560,6 @@ void regulatory_hint_11d(struct wiphy *wiphy,
+ wiphy_idx_valid(last_request->wiphy_idx)))
+ goto out;
+
+- rd = country_ie_2_rd(band, country_ie, country_ie_len, &checksum);
+- if (!rd) {
+- REG_DBG_PRINT("cfg80211: Ignoring bogus country IE\n");
+- goto out;
+- }
+-
+ /*
+ * This will not happen right now but we leave it here for the
+ * the future when we want to add suspend/resume support and having
+@@ -2105,24 +1569,17 @@ void regulatory_hint_11d(struct wiphy *wiphy,
+ * If we hit this before we add this support we want to be informed of
+ * it as it would indicate a mistake in the current design
+ */
+- if (WARN_ON(reg_same_country_ie_hint(wiphy, checksum)))
++ if (WARN_ON(reg_same_country_ie_hint(wiphy, alpha2, env)))
+ goto free_rd_out;
+
+ request = kzalloc(sizeof(struct regulatory_request), GFP_KERNEL);
+ if (!request)
+ goto free_rd_out;
+
+- /*
+- * We keep this around for when CRDA comes back with a response so
+- * we can intersect with that
+- */
+- country_ie_regdomain = rd;
+-
+ request->wiphy_idx = get_wiphy_idx(wiphy);
+- request->alpha2[0] = rd->alpha2[0];
+- request->alpha2[1] = rd->alpha2[1];
++ request->alpha2[0] = alpha2[0];
++ request->alpha2[1] = alpha2[1];
+ request->initiator = NL80211_REGDOM_SET_BY_COUNTRY_IE;
+- request->country_ie_checksum = checksum;
+ request->country_ie_env = env;
+
+ mutex_unlock(&reg_mutex);
+@@ -2383,33 +1840,6 @@ static void print_regdomain_info(const struct ieee80211_regdomain *rd)
+ print_rd_rules(rd);
+ }
+
+-#ifdef CONFIG_CFG80211_REG_DEBUG
+-static void reg_country_ie_process_debug(
+- const struct ieee80211_regdomain *rd,
+- const struct ieee80211_regdomain *country_ie_regdomain,
+- const struct ieee80211_regdomain *intersected_rd)
+-{
+- printk(KERN_DEBUG "cfg80211: Received country IE:\n");
+- print_regdomain_info(country_ie_regdomain);
+- printk(KERN_DEBUG "cfg80211: CRDA thinks this should applied:\n");
+- print_regdomain_info(rd);
+- if (intersected_rd) {
+- printk(KERN_DEBUG "cfg80211: We intersect both of these "
+- "and get:\n");
+- print_regdomain_info(intersected_rd);
+- return;
+- }
+- printk(KERN_DEBUG "cfg80211: Intersection between both failed\n");
+-}
+-#else
+-static inline void reg_country_ie_process_debug(
+- const struct ieee80211_regdomain *rd,
+- const struct ieee80211_regdomain *country_ie_regdomain,
+- const struct ieee80211_regdomain *intersected_rd)
+-{
+-}
+-#endif
+-
+ /* Takes ownership of rd only if it doesn't fail */
+ static int __set_regdom(const struct ieee80211_regdomain *rd)
+ {
+@@ -2521,34 +1951,6 @@ static int __set_regdom(const struct ieee80211_regdomain *rd)
+ return 0;
+ }
+
+- /*
+- * Country IE requests are handled a bit differently, we intersect
+- * the country IE rd with what CRDA believes that country should have
+- */
+-
+- /*
+- * Userspace could have sent two replies with only
+- * one kernel request. By the second reply we would have
+- * already processed and consumed the country_ie_regdomain.
+- */
+- if (!country_ie_regdomain)
+- return -EALREADY;
+- BUG_ON(rd == country_ie_regdomain);
+-
+- /*
+- * Intersect what CRDA returned and our what we
+- * had built from the Country IE received
+- */
+-
+- intersected_rd = regdom_intersect(rd, country_ie_regdomain);
+-
+- reg_country_ie_process_debug(rd,
+- country_ie_regdomain,
+- intersected_rd);
+-
+- kfree(country_ie_regdomain);
+- country_ie_regdomain = NULL;
+-
+ if (!intersected_rd)
+ return -EINVAL;
+
+@@ -2688,9 +2090,6 @@ void /* __init_or_exit */ regulatory_exit(void)
+
+ reset_regdomains();
+
+- kfree(country_ie_regdomain);
+- country_ie_regdomain = NULL;
+-
+ kfree(last_request);
+
+ platform_device_unregister(reg_pdev);
+--
+1.7.1.1
+
+
diff --git a/pci-acpi-disable-aspm-if-no-osc.patch b/pci-acpi-disable-aspm-if-no-osc.patch
new file mode 100644
index 000000000..044f38964
--- /dev/null
+++ b/pci-acpi-disable-aspm-if-no-osc.patch
@@ -0,0 +1,53 @@
+From: Matthew Garrett <mjg@redhat.com>
+Subject: ACPI: Disable ASPM if the platform won't provide _OSC control for PCIe
+
+ACPI: Disable ASPM if the platform won't provide _OSC control for PCIe
+
+The PCI SIG documentation for the _OSC OS/firmware handshaking interface
+states:
+
+"If the _OSC control method is absent from the scope of a host bridge
+device, then the operating system must not enable or attempt to use any
+features defined in this section for the hierarchy originated by the host
+bridge."
+
+The obvious interpretation of this is that the OS should not attempt to use
+PCIe hotplug, PME or AER - however, the specification also notes that an
+_OSC method is *required* for PCIe hierarchies, and experimental validation
+with An Alternative OS indicates that it doesn't use any PCIe functionality
+if the _OSC method is missing. That arguably means we shouldn't be using
+MSI or extended config space, but right now our problems seem to be limited
+to vendors being surprised when ASPM gets enabled on machines when other
+OSs refuse to do so. So, for now, let's just disable ASPM if the _OSC
+method doesn't exist or refuses to hand over PCIe capability control.
+
+Signed-off-by: Matthew Garrett <mjg@redhat.com>
+---
+
+diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
+index 4eac593..1f67057 100644
+--- a/drivers/acpi/pci_root.c
++++ b/drivers/acpi/pci_root.c
+@@ -33,6 +33,7 @@
+ #include <linux/pm_runtime.h>
+ #include <linux/pci.h>
+ #include <linux/pci-acpi.h>
++#include <linux/pci-aspm.h>
+ #include <linux/acpi.h>
+ #include <linux/slab.h>
+ #include <acpi/acpi_bus.h>
+@@ -543,6 +544,14 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
+ if (flags != base_flags)
+ acpi_pci_osc_support(root, flags);
+
++ status = acpi_pci_osc_control_set(root->device->handle,
++ 0);
++
++ if (status == AE_NOT_EXIST) {
++ printk(KERN_INFO "Unable to assume PCIe control: Disabling ASPM\n");
++ pcie_no_aspm();
++ }
++
+ pci_acpi_add_bus_pm_notifier(device, root->bus);
+ if (device->wakeup.flags.run_wake)
+ device_set_run_wake(root->bus->bridge, true);
diff --git a/pci-aspm-dont-enable-too-early.patch b/pci-aspm-dont-enable-too-early.patch
new file mode 100644
index 000000000..ea91a2554
--- /dev/null
+++ b/pci-aspm-dont-enable-too-early.patch
@@ -0,0 +1,50 @@
+From: Matthew Garrett <mjg@redhat.com>
+Date: Wed, 9 Jun 2010 20:05:07 +0000 (-0400)
+Subject: PCI: Don't enable aspm before drivers have had a chance to veto it
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Fjbarnes%2Fpci-2.6.git;a=commitdiff_plain;h=8f0b08c29f1df91315e48adce04462eb23671099
+
+PCI: Don't enable aspm before drivers have had a chance to veto it
+
+The aspm code will currently set the configured aspm policy before drivers
+have had an opportunity to indicate that their hardware doesn't support it.
+Unfortunately, putting some hardware in L0 or L1 can result in the hardware
+no longer responding to any requests, even after aspm is disabled. It makes
+more sense to leave aspm policy at the BIOS defaults at initial setup time,
+reconfiguring it after pci_enable_device() is called. This allows the
+driver to blacklist individual devices beforehand.
+
+Reviewed-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
+Signed-off-by: Matthew Garrett <mjg@redhat.com>
+Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
+---
+
+diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
+index be53d98..7122281 100644
+--- a/drivers/pci/pcie/aspm.c
++++ b/drivers/pci/pcie/aspm.c
+@@ -588,11 +588,23 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
+ * update through pcie_aspm_cap_init().
+ */
+ pcie_aspm_cap_init(link, blacklist);
+- pcie_config_aspm_path(link);
+
+ /* Setup initial Clock PM state */
+ pcie_clkpm_cap_init(link, blacklist);
+- pcie_set_clkpm(link, policy_to_clkpm_state(link));
++
++ /*
++ * At this stage drivers haven't had an opportunity to change the
++ * link policy setting. Enabling ASPM on broken hardware can cripple
++ * it even before the driver has had a chance to disable ASPM, so
++ * default to a safe level right now. If we're enabling ASPM beyond
++ * the BIOS's expectation, we'll do so once pci_enable_device() is
++ * called.
++ */
++ if (aspm_policy != POLICY_POWERSAVE) {
++ pcie_config_aspm_path(link);
++ pcie_set_clkpm(link, policy_to_clkpm_state(link));
++ }
++
+ unlock:
+ mutex_unlock(&aspm_lock);
+ out:
diff --git a/prevent-runtime-conntrack-changes.patch b/prevent-runtime-conntrack-changes.patch
new file mode 100644
index 000000000..59d62f3de
--- /dev/null
+++ b/prevent-runtime-conntrack-changes.patch
@@ -0,0 +1,74 @@
+Jon Masters correctly points out that conntrack hash sizes
+(nf_conntrack_htable_size) are global (not per-netns) and
+modifiable at runtime via /sys/module/nf_conntrack/hashsize .
+
+Steps to reproduce:
+ clone(CLONE_NEWNET)
+ [grow /sys/module/nf_conntrack/hashsize]
+ exit()
+
+At netns exit we are going to scan random memory for conntracks to be killed.
+
+Apparently there is a code which deals with hashtable resize for
+init_net (and it was there befode netns conntrack code), so prohibit
+hashsize modification if there is more than one netns exists.
+
+To change hashtable sizes, you need to reload module.
+
+Expectation hashtable size was simply glued to a variable with no code
+to rehash expectations, so it was a bug to allow writing to it.
+Make "expect_hashsize" readonly.
+
+This is temporarily until we figure out what to do.
+
+Signed-off-by: Alexey Dobriyan <adobriyan@gmail.com>
+Cc: stable@kernel.org
+---
+
+ net/netfilter/nf_conntrack_core.c | 15 +++++++++++++++
+ net/netfilter/nf_conntrack_expect.c | 2 +-
+ 2 files changed, 16 insertions(+), 1 deletion(-)
+
+--- a/net/netfilter/nf_conntrack_core.c
++++ b/net/netfilter/nf_conntrack_core.c
+@@ -21,6 +21,7 @@
+ #include <linux/stddef.h>
+ #include <linux/slab.h>
+ #include <linux/random.h>
++#include <linux/rtnetlink.h>
+ #include <linux/jhash.h>
+ #include <linux/err.h>
+ #include <linux/percpu.h>
+@@ -1198,6 +1199,20 @@ int nf_conntrack_set_hashsize(const char *val, struct kernel_param *kp)
+ if (!nf_conntrack_htable_size)
+ return param_set_uint(val, kp);
+
++ {
++ struct net *net;
++ unsigned int nr;
++
++ nr = 0;
++ rtnl_lock();
++ for_each_net(net)
++ nr++;
++ rtnl_unlock();
++ /* init_net always exists */
++ if (nr != 1)
++ return -EINVAL;
++ }
++
+ hashsize = simple_strtoul(val, NULL, 0);
+ if (!hashsize)
+ return -EINVAL;
+--- a/net/netfilter/nf_conntrack_expect.c
++++ b/net/netfilter/nf_conntrack_expect.c
+@@ -569,7 +569,7 @@ static void exp_proc_remove(struct net *net)
+ #endif /* CONFIG_PROC_FS */
+ }
+
+-module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0600);
++module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0400);
+
+ int nf_conntrack_expect_init(struct net *net)
+ {
+
diff --git a/revert-drm-kms-toggle-poll-around-switcheroo.patch b/revert-drm-kms-toggle-poll-around-switcheroo.patch
new file mode 100644
index 000000000..f83fc2fdf
--- /dev/null
+++ b/revert-drm-kms-toggle-poll-around-switcheroo.patch
@@ -0,0 +1,65 @@
+From 69b711c0c5e3d9cb3a5b9f741fb4cdc96b5739cb Mon Sep 17 00:00:00 2001
+From: Kyle McMartin <kyle@dreadnought.i.jkkm.org>
+Subject: Revert "drm/kms: disable/enable poll around switcheroo on/off"
+
+This reverts commit fbf81762e385d3d45acad057b654d56972acf58c, mostly.
+---
+ drivers/gpu/drm/i915/i915_dma.c | 4 +---
+ drivers/gpu/drm/nouveau/nouveau_state.c | 3 ---
+ drivers/gpu/drm/radeon/radeon_device.c | 2 --
+
+diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
+index 59a2bf8..2df3286 100644
+--- a/drivers/gpu/drm/i915/i915_dma.c
++++ b/drivers/gpu/drm/i915/i915_dma.c
+@@ -1320,14 +1320,12 @@ static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_
+ struct drm_device *dev = pci_get_drvdata(pdev);
+ pm_message_t pmm = { .event = PM_EVENT_SUSPEND };
+ if (state == VGA_SWITCHEROO_ON) {
+- printk(KERN_INFO "i915: switched on\n");
++ printk(KERN_INFO "i915: switched off\n");
+ /* i915 resume handler doesn't set to D0 */
+ pci_set_power_state(dev->pdev, PCI_D0);
+ i915_resume(dev);
+- drm_kms_helper_poll_enable(dev);
+ } else {
+ printk(KERN_ERR "i915: switched off\n");
+- drm_kms_helper_poll_disable(dev);
+ i915_suspend(dev, pmm);
+ }
+ }
+diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
+index b02a231..0c28266 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_state.c
++++ b/drivers/gpu/drm/nouveau/nouveau_state.c
+@@ -376,15 +376,12 @@ out_err:
+ static void nouveau_switcheroo_set_state(struct pci_dev *pdev,
+ enum vga_switcheroo_state state)
+ {
+- struct drm_device *dev = pci_get_drvdata(pdev);
+ pm_message_t pmm = { .event = PM_EVENT_SUSPEND };
+ if (state == VGA_SWITCHEROO_ON) {
+ printk(KERN_ERR "VGA switcheroo: switched nouveau on\n");
+ nouveau_pci_resume(pdev);
+- drm_kms_helper_poll_enable(dev);
+ } else {
+ printk(KERN_ERR "VGA switcheroo: switched nouveau off\n");
+- drm_kms_helper_poll_disable(dev);
+ nouveau_pci_suspend(pdev, pmm);
+ }
+ }
+diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
+index f10faed..225a9f2 100644
+--- a/drivers/gpu/drm/radeon/radeon_device.c
++++ b/drivers/gpu/drm/radeon/radeon_device.c
+@@ -546,10 +546,8 @@ static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switchero
+ /* don't suspend or resume card normally */
+ rdev->powered_down = false;
+ radeon_resume_kms(dev);
+- drm_kms_helper_poll_enable(dev);
+ } else {
+ printk(KERN_INFO "radeon: switched off\n");
+- drm_kms_helper_poll_disable(dev);
+ radeon_suspend_kms(dev, pmm);
+ /* don't suspend or resume card normally */
+ rdev->powered_down = true;
diff --git a/sources b/sources
index e69de29bb..1842efe0d 100644
--- a/sources
+++ b/sources
@@ -0,0 +1,3 @@
+10eebcb0178fb4540e2165bfd7efc7ad linux-2.6.34.tar.bz2
+ec36a4c6878e4638e2a78a317fef280d patch-2.6.35-rc6-git1.bz2
+1205481c8d1b5ccecad87840ddaeaf81 patch-2.6.35-rc6.bz2
diff --git a/ssb_check_for_sprom.patch b/ssb_check_for_sprom.patch
new file mode 100644
index 000000000..9415e1337
--- /dev/null
+++ b/ssb_check_for_sprom.patch
@@ -0,0 +1,155 @@
+From 4d9d1ff88f920e9fcdde155c0a1366b7e0462d14 Mon Sep 17 00:00:00 2001
+From: John W. Linville <linville@tuxdriver.com>
+Date: Fri, 19 Mar 2010 14:58:01 -0400
+Subject: [PATCH v4] ssb: do not read SPROM if it does not exist
+
+Attempting to read registers that don't exist on the SSB bus can cause
+hangs on some boxes. At least some b43 devices are 'in the wild' that
+don't have SPROMs at all. When the SSB bus support loads, it attempts
+to read these (non-existant) SPROMs and causes hard hangs on the box --
+no console output, etc.
+
+This patch adds some intelligence to determine whether or not the SPROM
+is present before attempting to read it. This avoids those hard hangs
+on those devices with no SPROM attached to their SSB bus. The
+SSB-attached devices (e.g. b43, et al.) won't work, but at least the box
+will survive to test further patches. :-)
+
+Signed-off-by: John W. Linville <linville@tuxdriver.com>
+Cc: Larry Finger <Larry.Finger@lwfinger.net>
+Cc: Michael Buesch <mb@bu3sch.de>
+Cc: stable@kernel.org
+---
+ drivers/ssb/driver_chipcommon.c | 3 +++
+ drivers/ssb/pci.c | 3 +++
+ drivers/ssb/sprom.c | 26 ++++++++++++++++++++++++++
+ include/linux/ssb/ssb.h | 3 +++
+ include/linux/ssb/ssb_driver_chipcommon.h | 15 +++++++++++++++
+ 5 files changed, 50 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/ssb/driver_chipcommon.c b/drivers/ssb/driver_chipcommon.c
+index 9681536..6cf288d 100644
+--- a/drivers/ssb/driver_chipcommon.c
++++ b/drivers/ssb/driver_chipcommon.c
+@@ -233,6 +233,9 @@ void ssb_chipcommon_init(struct ssb_chipcommon *cc)
+ {
+ if (!cc->dev)
+ return; /* We don't have a ChipCommon */
++ if (cc->dev->id.revision >= 11) {
++ cc->status = chipco_read32(cc, SSB_CHIPCO_CHIPSTAT);
++ }
+ ssb_pmu_init(cc);
+ chipco_powercontrol_init(cc);
+ ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST);
+diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c
+index a8dbb06..89d7ab1 100644
+--- a/drivers/ssb/pci.c
++++ b/drivers/ssb/pci.c
+@@ -621,6 +621,9 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus,
+ int err = -ENOMEM;
+ u16 *buf;
+
++ if (!ssb_is_sprom_available(bus))
++ return -ENODEV;
++
+ buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL);
+ if (!buf)
+ goto out;
+diff --git a/drivers/ssb/sprom.c b/drivers/ssb/sprom.c
+index f2f920f..c690f58 100644
+--- a/drivers/ssb/sprom.c
++++ b/drivers/ssb/sprom.c
+@@ -176,3 +176,29 @@ const struct ssb_sprom *ssb_get_fallback_sprom(void)
+ {
+ return fallback_sprom;
+ }
++
++bool ssb_is_sprom_available(struct ssb_bus *bus)
++{
++ /* some older devices don't have chipcommon, but they have sprom */
++ if (!bus->chipco.dev)
++ return true;
++
++ /* status register only exists on chipcomon rev >= 11 */
++ if (bus->chipco.dev->id.revision < 11)
++ return true;
++
++ switch (bus->chip_id) {
++ case 0x4312:
++ return SSB_CHIPCO_CHST_4312_SPROM_PRESENT(bus->chipco.status);
++ case 0x4322:
++ return SSB_CHIPCO_CHST_4322_SPROM_PRESENT(bus->chipco.status);
++ case 0x4325:
++ return SSB_CHIPCO_CHST_4325_SPROM_PRESENT(bus->chipco.status);
++ default:
++ break;
++ }
++ if (bus->chipco.dev->id.revision >= 31)
++ return bus->chipco.capabilities & SSB_CHIPCO_CAP_SPROM;
++
++ return true;
++}
+diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h
+index 24f9885..3b4da23 100644
+--- a/include/linux/ssb/ssb.h
++++ b/include/linux/ssb/ssb.h
+@@ -394,6 +394,9 @@ extern int ssb_bus_sdiobus_register(struct ssb_bus *bus,
+
+ extern void ssb_bus_unregister(struct ssb_bus *bus);
+
++/* Does the device have an SPROM? */
++extern bool ssb_is_sprom_available(struct ssb_bus *bus);
++
+ /* Set a fallback SPROM.
+ * See kdoc at the function definition for complete documentation. */
+ extern int ssb_arch_set_fallback_sprom(const struct ssb_sprom *sprom);
+diff --git a/include/linux/ssb/ssb_driver_chipcommon.h b/include/linux/ssb/ssb_driver_chipcommon.h
+index 4e27acf..2cdf249 100644
+--- a/include/linux/ssb/ssb_driver_chipcommon.h
++++ b/include/linux/ssb/ssb_driver_chipcommon.h
+@@ -53,6 +53,7 @@
+ #define SSB_CHIPCO_CAP_64BIT 0x08000000 /* 64-bit Backplane */
+ #define SSB_CHIPCO_CAP_PMU 0x10000000 /* PMU available (rev >= 20) */
+ #define SSB_CHIPCO_CAP_ECI 0x20000000 /* ECI available (rev >= 20) */
++#define SSB_CHIPCO_CAP_SPROM 0x40000000 /* SPROM present */
+ #define SSB_CHIPCO_CORECTL 0x0008
+ #define SSB_CHIPCO_CORECTL_UARTCLK0 0x00000001 /* Drive UART with internal clock */
+ #define SSB_CHIPCO_CORECTL_SE 0x00000002 /* sync clk out enable (corerev >= 3) */
+@@ -385,6 +386,7 @@
+
+
+ /** Chip specific Chip-Status register contents. */
++#define SSB_CHIPCO_CHST_4322_SPROM_EXISTS 0x00000040 /* SPROM present */
+ #define SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL 0x00000003
+ #define SSB_CHIPCO_CHST_4325_DEFCIS_SEL 0 /* OTP is powered up, use def. CIS, no SPROM */
+ #define SSB_CHIPCO_CHST_4325_SPROM_SEL 1 /* OTP is powered up, SPROM is present */
+@@ -398,6 +400,18 @@
+ #define SSB_CHIPCO_CHST_4325_RCAL_VALUE_SHIFT 4
+ #define SSB_CHIPCO_CHST_4325_PMUTOP_2B 0x00000200 /* 1 for 2b, 0 for to 2a */
+
++/** Macros to determine SPROM presence based on Chip-Status register. */
++#define SSB_CHIPCO_CHST_4312_SPROM_PRESENT(status) \
++ ((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \
++ SSB_CHIPCO_CHST_4325_OTP_SEL)
++#define SSB_CHIPCO_CHST_4322_SPROM_PRESENT(status) \
++ (status & SSB_CHIPCO_CHST_4322_SPROM_EXISTS)
++#define SSB_CHIPCO_CHST_4325_SPROM_PRESENT(status) \
++ (((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \
++ SSB_CHIPCO_CHST_4325_DEFCIS_SEL) && \
++ ((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \
++ SSB_CHIPCO_CHST_4325_OTP_SEL))
++
+
+
+ /** Clockcontrol masks and values **/
+@@ -564,6 +578,7 @@ struct ssb_chipcommon_pmu {
+ struct ssb_chipcommon {
+ struct ssb_device *dev;
+ u32 capabilities;
++ u32 status;
+ /* Fast Powerup Delay constant */
+ u16 fast_pwrup_delay;
+ struct ssb_chipcommon_pmu pmu;
+--
+1.7.0.1
+
diff --git a/thinkpad-acpi-fix-backlight.patch b/thinkpad-acpi-fix-backlight.patch
new file mode 100644
index 000000000..5ed2544fe
--- /dev/null
+++ b/thinkpad-acpi-fix-backlight.patch
@@ -0,0 +1,56 @@
+diff -up linux-2.6.34.noarch/drivers/platform/x86/thinkpad_acpi.c.orig linux-2.6.34.noarch/drivers/platform/x86/thinkpad_acpi.c
+--- linux-2.6.34.noarch/drivers/platform/x86/thinkpad_acpi.c.orig 2010-05-17 16:28:13.254200070 -0400
++++ linux-2.6.34.noarch/drivers/platform/x86/thinkpad_acpi.c 2010-05-17 16:29:56.471200083 -0400
+@@ -3397,7 +3397,7 @@ static int __init hotkey_init(struct ibm
+ /* update bright_acpimode... */
+ tpacpi_check_std_acpi_brightness_support();
+
+- if (tp_features.bright_acpimode && acpi_video_backlight_support()) {
++ if (acpi_video_backlight_support()) {
+ printk(TPACPI_INFO
+ "This ThinkPad has standard ACPI backlight "
+ "brightness control, supported by the ACPI "
+@@ -6189,26 +6189,24 @@ static int __init brightness_init(struct
+ * going to publish a backlight interface
+ */
+ b = tpacpi_check_std_acpi_brightness_support();
+- if (b > 0) {
+
+- if (acpi_video_backlight_support()) {
+- if (brightness_enable > 1) {
+- printk(TPACPI_NOTICE
+- "Standard ACPI backlight interface "
+- "available, not loading native one.\n");
+- return 1;
+- } else if (brightness_enable == 1) {
+- printk(TPACPI_NOTICE
+- "Backlight control force enabled, even if standard "
+- "ACPI backlight interface is available\n");
+- }
+- } else {
+- if (brightness_enable > 1) {
+- printk(TPACPI_NOTICE
+- "Standard ACPI backlight interface not "
+- "available, thinkpad_acpi native "
+- "brightness control enabled\n");
+- }
++ if (acpi_video_backlight_support()) {
++ if (brightness_enable > 1) {
++ printk(TPACPI_NOTICE
++ "Standard ACPI backlight interface "
++ "available, not loading native one.\n");
++ return 1;
++ } else if (brightness_enable == 1) {
++ printk(TPACPI_NOTICE
++ "Backlight control force enabled, even if standard "
++ "ACPI backlight interface is available\n");
++ }
++ } else {
++ if (brightness_enable > 1) {
++ printk(TPACPI_NOTICE
++ "Standard ACPI backlight interface not "
++ "available, thinkpad_acpi native "
++ "brightness control enabled\n");
+ }
+ }
+
diff --git a/utrace-ptrace-fix-build.patch b/utrace-ptrace-fix-build.patch
new file mode 100644
index 000000000..c0de8764d
--- /dev/null
+++ b/utrace-ptrace-fix-build.patch
@@ -0,0 +1,29 @@
+From 9b7ec0e07ce85c377b76626cef552d27d59cc405 Mon Sep 17 00:00:00 2001
+From: Kyle McMartin <kyle@dreadnought.i.jkkm.org>
+Date: Tue, 22 Jun 2010 13:57:47 +0100
+Subject: utrace-ptrace: removed defunct arch_ptrace_untrace call
+
+commit faa4602e removed the unused BTS code which had added
+this hook.
+
+Signed-off-by: Kyle McMartin <kyle@redhat.com>
+---
+ kernel/ptrace-utrace.c | 2 --
+ 1 files changed, 0 insertions(+), 2 deletions(-)
+
+diff --git a/kernel/ptrace-utrace.c b/kernel/ptrace-utrace.c
+index 86234ee..1a8ba5e 100644
+--- a/kernel/ptrace-utrace.c
++++ b/kernel/ptrace-utrace.c
+@@ -50,8 +50,6 @@ void __ptrace_unlink(struct task_struct *child)
+ child->ptrace = 0;
+ child->parent = child->real_parent;
+ list_del_init(&child->ptrace_entry);
+-
+- arch_ptrace_untrace(child);
+ }
+
+ struct ptrace_context {
+--
+1.7.0.1
+
diff --git a/utrace-remove-use-of-kref_set.patch b/utrace-remove-use-of-kref_set.patch
new file mode 100644
index 000000000..f696f81fb
--- /dev/null
+++ b/utrace-remove-use-of-kref_set.patch
@@ -0,0 +1,32 @@
+From ad778e66100e4b76bab6b939e3d0c781da82d980 Mon Sep 17 00:00:00 2001
+From: Kyle McMartin <kyle@dreadnought.i.jkkm.org>
+Date: Tue, 22 Jun 2010 14:09:30 +0100
+Subject: utrace: remove use of kref_set
+
+Unfortunatey db1afffa which removed kref_set did not anticipate
+anyone would actually like to use a kref which starts with a refcnt
+above 1. Replace kref_set in utrace with a kref_init + kref_get to
+immediately bump the reference count.
+
+Signed-off-by: Kyle McMartin <kyle@redhat.com>
+---
+ kernel/utrace.c | 3 ++-
+ 1 files changed, 2 insertions(+), 1 deletions(-)
+
+diff --git a/kernel/utrace.c b/kernel/utrace.c
+index f5a9e2c..cc864d5 100644
+--- a/kernel/utrace.c
++++ b/kernel/utrace.c
+@@ -304,7 +304,8 @@ struct utrace_engine *utrace_attach_task(
+ * Initialize the new engine structure. It starts out with two
+ * refs: one ref to return, and one ref for being attached.
+ */
+- kref_set(&engine->kref, 2);
++ kref_init(&engine->kref);
++ kref_get(&engine->kref);
+ engine->flags = 0;
+ engine->ops = ops;
+ engine->data = data;
+--
+1.7.0.1
+