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 +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 +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 +--- + 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 + #include + #include ++#include + + #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 + +@@ -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 ++ + /* 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 +-#include +- +-#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 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 +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 + + #include +-#include + + 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 ++ + 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 " +__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 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 +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 @@ ++ ++ ++ ++ ++ ++ The utrace User Debugging Infrastructure ++ ++ ++ ++ ++ utrace concepts ++ ++ Introduction ++ ++ ++ utrace is infrastructure code for tracing ++ and controlling user threads. This is the foundation for writing ++ tracing engines, which can be loadable kernel modules. ++ ++ ++ ++ The basic actors in utrace are the thread ++ and the tracing engine. A tracing engine is some body of code that ++ calls into the <linux/utrace.h> ++ interfaces, represented by a struct ++ utrace_engine_ops. (Usually it's a kernel module, ++ though the legacy ptrace support is a tracing ++ engine that is not in a kernel module.) The interface operates on ++ individual threads (struct task_struct). ++ If an engine wants to treat several threads as a group, that is up ++ to its higher-level code. ++ ++ ++ ++ Tracing begins by attaching an engine to a thread, using ++ utrace_attach_task or ++ utrace_attach_pid. If successful, it returns a ++ pointer that is the handle used in all other calls. ++ ++ ++ ++ ++ Events and Callbacks ++ ++ ++ An attached engine does nothing by default. An engine makes something ++ happen by requesting callbacks via utrace_set_events ++ and poking the thread with utrace_control. ++ The synchronization issues related to these two calls ++ are discussed further below in . ++ ++ ++ ++ Events are specified using the macro ++ UTRACE_EVENT(type). ++ Each event type is associated with a callback in struct ++ utrace_engine_ops. A tracing engine can leave unused ++ callbacks NULL. The only callbacks required ++ are those used by the event flags it sets. ++ ++ ++ ++ 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. ++ ++ ++ ++ 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 ++ kmalloc and the like, but never wait for i/o or ++ for user mode to do something. If you need the thread to wait, use ++ UTRACE_STOP and return from the callback ++ quickly. When your i/o finishes or whatever, you can use ++ utrace_control to resume the thread. ++ ++ ++ ++ The UTRACE_EVENT(SYSCALL_ENTRY) 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 report_syscall_entry callback is ++ special in two ways. For this event, engines are called in reverse of ++ the normal order (this includes the report_quiesce ++ call that precedes a report_syscall_entry 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 UTRACE_STOP (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. ++ ++ ++ ++ ++ Stopping Safely ++ ++ Writing well-behaved callbacks ++ ++ ++ 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 CLONE 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 CLONE ++ tracing callbacks return. (This allows engines tracing the parent to ++ attach to the child.) If a CLONE event callback ++ blocks the parent thread, it also prevents the child thread from ++ running (even to process a SIGKILL). If what you ++ want is to make both the parent and child block, then use ++ utrace_attach_task on the child and then use ++ UTRACE_STOP on both threads. A more crucial ++ problem with blocking in callbacks is that it can prevent ++ SIGKILL from working. A thread that is blocking ++ due to UTRACE_STOP will still wake up and die ++ immediately when sent a SIGKILL, as all threads ++ should. Relying on the utrace ++ infrastructure rather than on private synchronization calls in event ++ callbacks is an important way to help keep tracing robustly ++ noninvasive. ++ ++ ++ ++ ++ Using <constant>UTRACE_STOP</constant> ++ ++ ++ To control another thread and access its state, it must be stopped ++ with UTRACE_STOP. This means that it is ++ stopped and won't start running again while we access it. When a ++ thread is not already stopped, utrace_control ++ returns -EINPROGRESS 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 ++ UTRACE_EVENT(QUIESCE) event bit. To wake up an ++ interruptible wait, use UTRACE_INTERRUPT. ++ ++ ++ ++ As long as some engine has used UTRACE_STOP and ++ not called utrace_control to resume the thread, ++ then the thread will remain stopped. SIGKILL ++ will wake it up, but it will not run user code. When the stop is ++ cleared with utrace_control or a callback ++ return value, the thread starts running again. ++ (See also .) ++ ++ ++ ++ ++ ++ ++ Tear-down Races ++ ++ Primacy of <constant>SIGKILL</constant> ++ ++ Ordinarily synchronization issues for tracing engines are kept fairly ++ straightforward by using UTRACE_STOP. You ask a ++ thread to stop, and then once it makes the ++ report_quiesce callback it cannot do anything else ++ that would result in another callback, until you let it with a ++ utrace_control 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 ++ SIGKILL from making a thread die and release its ++ resources. To preserve this important property of ++ SIGKILL, it as a special case can break ++ UTRACE_STOP like nothing else normally can. This ++ includes both explicit SIGKILL signals and the ++ implicit SIGKILL sent to each other thread in the ++ same thread group by a thread doing an exec, or processing a fatal ++ signal, or making an exit_group system call. A ++ tracing engine can prevent a thread from beginning the exit or exec or ++ dying by signal (other than SIGKILL) 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. ++ ++ ++ ++ Final callbacks ++ ++ The report_reap 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 ++ report_death 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 report_death callback is the natural ++ place to clean up data structures and detach. To facilitate writing ++ such engines robustly, given the asynchrony of ++ SIGKILL, and without error-prone manual ++ implementation of synchronization schemes, the ++ utrace infrastructure provides some special ++ guarantees about the report_death and ++ report_reap 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. ++ ++ ++ ++ Engine and task pointers ++ ++ The first sort of guarantee concerns the core data structures ++ themselves. struct utrace_engine 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 utrace call. Each call to ++ utrace_attach_task or ++ utrace_attach_pid returns an engine pointer with a ++ reference belonging to the caller. You own that reference until you ++ drop it using utrace_engine_put. There is an ++ implicit reference on the engine while it is attached. So if you drop ++ your only reference, and then use ++ utrace_attach_task without ++ UTRACE_ATTACH_CREATE 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 utrace_engine_get. ++ When an engine has been detached, either explicitly with ++ UTRACE_DETACH or implicitly after ++ report_reap, then any references you hold are all ++ that keep the old engine pointer alive. ++ ++ ++ ++ There is nothing a kernel module can do to keep a struct ++ task_struct alive outside of ++ rcu_read_lock. 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. ++ utrace 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 ++ utrace 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 -ESRCH for a detached engine, which tells ++ you that the task pointer you passed could be invalid now. Since ++ utrace_control and ++ utrace_set_events do not block, you can call those ++ inside a rcu_read_lock section and be sure after ++ they don't return -ESRCH that the task pointer is ++ still valid until rcu_read_unlock. The ++ infrastructure never holds task references of its own. Though neither ++ rcu_read_lock nor any other lock is held while ++ making a callback, it's always guaranteed that the struct ++ task_struct and the struct ++ utrace_engine passed as arguments remain valid ++ until the callback function returns. ++ ++ ++ ++ The common means for safely holding task pointers that is available to ++ kernel modules is to use struct pid, which ++ permits put_pid from kernel modules. When using ++ that, the calls utrace_attach_pid, ++ utrace_control_pid, ++ utrace_set_events_pid, and ++ utrace_barrier_pid are available. ++ ++ ++ ++ ++ ++ Serialization of <constant>DEATH</constant> and <constant>REAP</constant> ++ ++ ++ The second guarantee is the serialization of ++ DEATH and REAP event ++ callbacks for a given thread. The actual reaping by the parent ++ (release_task call) can occur simultaneously ++ while the thread is still doing the final steps of dying, including ++ the report_death callback. If a tracing engine ++ has requested both DEATH and ++ REAP event reports, it's guaranteed that the ++ report_reap callback will not be made until ++ after the report_death callback has returned. ++ If the report_death callback itself detaches ++ from the thread, then the report_reap callback ++ will never be made. Thus it is safe for a ++ report_death callback to clean up data ++ structures and detach. ++ ++ ++ ++ Interlock with final callbacks ++ ++ The final sort of guarantee is that a tracing engine will know for sure ++ whether or not the report_death and/or ++ report_reap callbacks will be made for a certain ++ thread. These tear-down races are disambiguated by the error return ++ values of utrace_set_events and ++ utrace_control. Normally ++ utrace_control called with ++ UTRACE_DETACH returns zero, and this means that no ++ more callbacks will be made. If the thread is in the midst of dying, ++ it returns -EALREADY to indicate that the ++ report_death callback may already be in progress; ++ when you get this error, you know that any cleanup your ++ report_death callback does is about to happen or ++ has just happened--note that if the report_death ++ callback does not detach, the engine remains attached until the thread ++ gets reaped. If the thread is in the midst of being reaped, ++ utrace_control returns -ESRCH ++ to indicate that the report_reap 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 ++ report_death or report_reap ++ callback will try to do the same. utrace_detach ++ returns -ESRCH when the struct ++ utrace_engine 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. ++ ++ ++ ++ In the same vein, utrace_set_events 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 -EALREADY 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 the target is already ++ dead or dying. Like utrace_control, it returns ++ -ESRCH 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 ++ utrace_set_events 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. ++ ++ ++ ++ Using <function>utrace_barrier</function> ++ ++ When a thread is safely stopped, calling ++ utrace_control with UTRACE_DETACH ++ or calling utrace_set_events 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 ++ -EINPROGRESS 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 utrace_barrier. This is necessary if the ++ kernel module containing your callback code is going to be unloaded. ++ ++ ++ After using UTRACE_DETACH once, further calls to ++ utrace_control with the same engine pointer will ++ return -ESRCH. In contrast, after getting ++ -EINPROGRESS from ++ utrace_set_events, you can call ++ utrace_set_events again later and if it returns zero ++ then know the old callbacks have finished. ++ ++ ++ Unlike all other calls, utrace_barrier (and ++ utrace_barrier_pid) will accept any engine pointer you ++ hold a reference on, even if UTRACE_DETACH has already ++ been used. After any utrace_control or ++ utrace_set_events call (these do not block), you can ++ call utrace_barrier to block until callbacks have ++ finished. This returns -ESRCH 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 ++ -ERESTARTSYS if its wait is interrupted. ++ ++ ++ ++ ++ ++ ++ ++utrace core API ++ ++ ++ The utrace API is declared in <linux/utrace.h>. ++ ++ ++!Iinclude/linux/utrace.h ++!Ekernel/utrace.c ++ ++ ++ ++Machine State ++ ++ ++ The task_current_syscall function can be used on any ++ valid struct task_struct at any time, and does ++ not even require that utrace_attach_task was used at all. ++ ++ ++ ++ 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 utrace_set_events can ++ request callbacks (except for the DEATH and ++ REAP events). So at any event callback, it is safe to ++ examine current. ++ ++ ++ ++ One task can examine another only after a callback in the target task that ++ returns UTRACE_STOP 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 utrace_control, unless the ++ task dies suddenly. To examine safely, one must use a pair of calls to ++ utrace_prepare_examine and ++ utrace_finish_examine surrounding the calls to ++ struct user_regset functions or direct examination ++ of task data structures. utrace_prepare_examine returns ++ an error if the task is not properly stopped, or is dead. After a ++ successful examination, the paired utrace_finish_examine ++ 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. ++ ++ ++<structname>struct user_regset</structname> ++ ++ ++ The struct user_regset API ++ is declared in <linux/regset.h>. ++ ++ ++!Finclude/linux/regset.h ++ ++ ++ ++ ++ <filename>System Call Information</filename> ++ ++ ++ This function is declared in <linux/ptrace.h>. ++ ++ ++!Elib/syscall.c ++ ++ ++ ++<filename>System Call Tracing</filename> ++ ++ ++ The arch API for system call information is declared in ++ <asm/syscall.h>. ++ 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 ++ report_syscall_entry callback, ++ or any time after that callback has returned UTRACE_STOP. ++ ++ ++!Finclude/asm-generic/syscall.h ++ ++ ++ ++ ++ ++Kernel Internals ++ ++ ++ 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. ++ ++ ++Core Calls In ++ ++ ++ These calls are declared in <linux/tracehook.h>. ++ The core kernel calls these functions at various important places. ++ ++ ++!Finclude/linux/tracehook.h ++ ++ ++ ++Architecture Calls Out ++ ++ ++ An arch that has done all these things sets ++ CONFIG_HAVE_ARCH_TRACEHOOK. ++ This is required to enable the utrace code. ++ ++ ++<filename><asm/ptrace.h></filename> ++ ++ ++ An arch defines these in <asm/ptrace.h> ++ if it supports hardware single-step or block-step features. ++ ++ ++!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 ++ ++ ++ ++ ++ <filename><asm/syscall.h></filename> ++ ++ ++ An arch provides <asm/syscall.h> that ++ defines these as inlines, or declares them as exported functions. ++ These interfaces are described in . ++ ++ ++ ++ ++ ++ <filename><linux/tracehook.h></filename> ++ ++ ++ An arch must define TIF_NOTIFY_RESUME ++ and TIF_SYSCALL_TRACE ++ in its <asm/thread_info.h>. ++ The arch code must call the following functions, all declared ++ in <linux/tracehook.h> and ++ described in : ++ ++ ++ ++ tracehook_notify_resume ++ ++ ++ tracehook_report_syscall_entry ++ ++ ++ tracehook_report_syscall_exit ++ ++ ++ tracehook_signal_handler ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ +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 + #include + #include ++#include + + #include + #include +@@ -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 /* For unlikely. */ + #include /* 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 + #include + #include ++#include + 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 ++#include ++#include ++#include ++ ++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 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 ++ ++/* ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * 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(¤t->blocked, signr)) { ++ send_sig_info(signr, info, current); ++ return UTRACE_SIGNAL_IGN; ++ } ++ ++ spin_lock_irq(¤t->sighand->siglock); ++ *return_ka = current->sighand->action[signr - 1]; ++ spin_unlock_irq(¤t->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 ++ ++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 + #include + ++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 ++ ++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 +- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/* ++ * 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 +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 + #include + +@@ -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 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 +# It uses any kernel__conflicts and kernel__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--debuginfo package. +# %%kernel_debuginfo_package +# +%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--devel package. +# %%kernel_devel_package +# +%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- and its -devel and -debuginfo too. +# %%define variant_summary The Linux kernel compiled for +# %%kernel_variant_package [-n ] +# +%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 [] +# +%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 [] +# 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 ] [-r ] +# 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 +# +%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] +# +%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 +- 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 2.6.35-0.56.rc6.git1 +- Linux 2.6.35-rc6-git1 + +* Thu Jul 22 2010 Dave Jones +- 2.6.35-rc6 + +* Thu Jul 22 2010 Ben Skeggsb +- drm-nouveau-updates: bring back, most patches *weren't* upstream yet, + they're queued for 2.6.36. + +* Wed Jul 21 2010 Chuck Ebbert 2.6.35-0.53.rc5.git7 +- Linux 2.6.35-rc5-git7 + +* Wed Jul 21 2010 Dave Jones +- 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 +- Linux 2.6.35-rc5-git6 +- Removed drm-nouveau-updates.patch (upstreamed) + +* Mon Jul 19 2010 Chuck Ebbert +- Linux 2.6.35-rc5-git4 + +* Mon Jul 19 2010 Jarod Wilson 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 2.6.35-0.47.rc5.git2 +- Linux 2.6.35-rc5-git2 + +* Sun Jul 18 2010 Chuck Ebbert +- 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 +- Add support for dynamic menu controls to the uvcvideo driver (#576023) + +* Sun Jul 18 2010 Jarod Wilson 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 +- Limit some alsa spew that the nuforce udac makes happen a lot. + +* Fri Jul 16 2010 Jarod Wilson 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 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 +- Linux 2.6.35-rc5-git1 + +* Tue Jul 13 2010 Chuck Ebbert +- Linux 2.6.35-rc5 + +* Tue Jul 13 2010 Ben Skeggs +- nouveau: miscellanous fixes + +* Mon Jul 12 2010 Dave Jones +- 2.6.35-rc4-git5 + +* Mon Jul 12 2010 Dave Jones +- Remove a bunch of x86 options from config files that get set + automatically, and can't be overridden. + +* Fri Jul 9 2010 Roland McGrath +- Split execshield into two patches. + +* Thu Jul 08 2010 Chuck Ebbert 2.6.35-0.31.rc4.git4 +- Linux 2.6.35-rc4-git4 + +* Thu Jul 8 2010 Roland McGrath +- Remove exec-shield=2 setting, some other cruft from execshield. + +* Wed Jul 7 2010 Roland McGrath 2.6.35-0.29.rc4.git0.fc14 +- Revamp perf packaging. + +* Wed Jul 07 2010 Chuck Ebbert +- 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 +- nouveau: bring in nouveau upstream + +* Mon Jul 05 2010 Dave Jones +- 2.6.35-rc4 + +* Fri Jul 02 2010 Dave Jones +- 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 +- 2.6.35-rc3-git6 + +* Thu Jul 01 2010 Dave Jones +- Add a patch to debug an infinite loop warning from acpi. + +* Thu Jul 01 2010 Dave Jones +- 2.6.35-rc3-git5 + +* Thu Jul 01 2010 Chuck Ebbert +- Copy fix for BZ#220892 from F-13. + +* Wed Jun 30 2010 Kyle McMartin 2.6.35-0.19.rc3.git4 +- 2.6.35-rc3-git4 + +* Tue Jun 29 2010 Chuck Ebbert +- Disable Intel Moorestown support -- it breaks PC keyboard controllers. + +* Tue Jun 29 2010 Dave Jones +- Building external modules requires perl. (#608525) + +* Tue Jun 29 2010 Kyle McMartin 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 +- ppc64: enable active memory sharing and DLPAR memory remove (#607175) + +* Mon Jun 28 2010 Dave Jones +- 2.6.35-rc3-git2 + +* Mon Jun 28 2010 Dave Jones +- 2.6.35-rc3-git1 + +* Mon Jun 28 2010 Dave Airlie +- drm-i915-fix-edp-panels.patch: update to newer version + +* Fri Jun 25 2010 Kyle McMartin +- drm-i915-fix-edp-panels.patch: copy from rawhide. + +* Wed Jun 23 2010 Eric Sandeen +- Fix ext4 freeze deadlock (#607245) + +* Wed Jun 23 2010 Kyle McMartin +- Override generic's CONFIG_HZ_1000 on s390. + +* Tue Jun 22 2010 Dave Jones +- Fix localhost networking. + +* Tue Jun 22 2010 Kyle McMartin 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 +- 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 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 2.6.34-44 +- revert-drm-kms-toggle-poll-around-switcheroo.patch (rhbz#599190) + +* Thu Jun 17 2010 Kyle McMartin 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 +- 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 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 +- 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 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 2.6.34-37 +- btrfs ACL fixes from CVE-2010-2071. + +* Sun Jun 13 2010 Kyle McMartin 2.6.34-36 +- remunge and reapply hdpvr-ir-enable + +* Sun Jun 13 2010 Kyle McMartin 2.6.34-35 +- mac80211/iwlwifi fix connections to some APs (rhbz#558002) + patches from sgruszka@. + +* Sun Jun 13 2010 Kyle McMartin 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 2.6.34-33 +- Update drm-next to include fixes since 2.6.35-rc1. + +* Fri Jun 11 2010 Justin M. Forbes +- Disable xsave for so that kernel will boot on ancient EC2 hosts. + +* Wed Jun 09 2010 John W. Linville +- Disable rt20xx and rt35xx chipset support in rt2800 drivers (#570869) + +* Wed Jun 09 2010 David Woodhouse +- Include PHY modules in modules.networking (#602155) + +* Tue Jun 08 2010 Dave Jones +- Remove useless -kdump kernel support + +* Tue Jun 08 2010 Dave Jones +- Remove ia64 ata quirk which had no explanation, and still + isn't upstream. No-one cares. + +* Tue Jun 08 2010 Dave Jones +- 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 +- Remove crufty Xen remnants from specfile. + +* Tue Jun 08 2010 Dave Jones +- Remove mkinitrd ifdefs. Dracut or GTFO. + +* Thu Jun 03 2010 Kyle McMartin +- Build kernel headers on s390. + +* Wed Jun 02 2010 Kyle McMartin 2.6.34-20 +- Disable doc_build_fail because xmlto et al. are crud. + +* Wed Jun 02 2010 Kyle McMartin 2.6.34-19 +- Enable -debug flavour builds, until we branch for 2.6.35-rcX. + +* Wed Jun 02 2010 Kyle McMartin +- revert writeback fixes for now, there appear to be dragons + lurking there still. + +* Tue Jun 01 2010 Kyle McMartin 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 2.6.34-17 +- backport writeback fixes from Jens until stable@ picks them up. + +* Tue Jun 01 2010 Kyle McMartin 2.6.34-16 +- quiet-prove_RCU-in-cgroups.patch: shut RCU lockdep up + as in 8b08ca52f5942c21564bbb90ccfb61053f2c26a1. + +* Tue Jun 01 2010 Kyle McMartin +- disable radeon_pm for now. + +* Mon May 31 2010 Kyle McMartin 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 +- config-generic: Stop building i830.ko + +* Wed May 26 2010 Kyle McMartin +- iwlwifi-recover_from_tx_stall.patch: copy from F-13. + +* Fri May 21 2010 Roland McGrath 2.6.34-11 +- utrace update + +* Fri May 21 2010 Dave Jones +- Update the SELinux mprotect patch with a newer version from Stephen + +* Fri May 21 2010 Roland McGrath +- perf requires libdw now, not libdwarf + +* Fri May 21 2010 Kyle McMartin 2.6.34-6 +- Fixups for virt_console from Amit Shah, thanks! + +* Thu May 20 2010 Kyle McMartin 2.6.34-5 +- disable intel sdvo fixes until dependent code is backported. + +* Thu May 20 2010 Kyle McMartin 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 +- thinkpad-acpi-fix-backlight.patch: Fix backlight support on some recent + Thinkpads + +* Sun May 16 2010 Kyle McMartin 2.6.34-2 +- Disable strict copy_from_user checking until lirc is fixed. + +* Sun May 16 2010 Kyle McMartin 2.6.34-1 +- Linux 2.6.34 + +* Sun May 16 2010 Kyle McMartin +- 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 + #include + #include ++#include + + #include + #include +@@ -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 +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 ++#include ++#include ++ ++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 + EXPORT_SYMBOL(unw_init_running); + ++#include ++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 ++#include ++#include ++ ++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 ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 +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 +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 ++#include ++#include ++ + /* 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 +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 +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 +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 +Signed-off-by: Eric Sandeen +--- + +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 +-#include +-#include +-#include ++#include ++#include + #include +-#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + #include + #include ++#include + #include +-#include +-#include +-#include +-#include +-#include ++#include ++#include ++#include ++ + #include + #include +-#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, ®ion) < 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, ®ion); ++ 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 + #include + #include ++#include + #include + #include ++#include + #include + #include + #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 + #include + #include ++#include + #include + + 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 + * +@@ -18,21 +20,25 @@ + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +-#include +-#include + #include +-#include ++#include ++#include ++#include + #include ++#include ++#include + +-#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 ++#include ++ ++#include ++ + 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(®isters, +- ®isters_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(®isters, ®isters_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 + #include + +-#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 + #include + #include + #include + + 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 + #include + #include ++#include + + 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 . 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(¤t->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(¤t->mm->page_table_lock); ++ if (limit >= TASK_SIZE) ++ limit = -1UL; ++ current->mm->context.exec_limit = limit; ++ } ++ set_user_cs(¤t->mm->context.user_cs, limit); ++ ++ desc1 = ¤t->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 + #include + #include + #include +@@ -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 + #include + ++#include + #include + #include + #include +@@ -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 + #include + #include ++#include + #include + #include + ++#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 +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 +To: fedora-kernel-list + +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 +Reviewed-by: Adam Jackson +--- + 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 +To: Dave Jones +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 +To: David Miller +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" +> 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 + +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 ) ++ * + * 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 +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 + +--- 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 +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 +--- + 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 "); +@@ -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 +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 +--- + 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 + * +@@ -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 "); + 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 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 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 + +--- + 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 @@ + + + ++ + + + +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 @@ + LINUX MEDIA INFRASTRUCTURE API + + +- 2009 ++ 2009-2010 + LinuxTV Developers + + +@@ -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. +- The third part covers other API's used by all media infrastructure devices ++ The third part covers Remote Controller API + For additional information and for the latest development code, + see: http://linuxtv.org. + For discussing improvements, reporting troubles, sending new drivers, etc, please mail to: Linux Media Mailing List (LMML).. +@@ -86,7 +86,7 @@ Foundation. A copy of the license is included in the chapter entitled + + + +- 2009 ++ 2009-2010 + Mauro Carvalho Chehab + + +@@ -101,7 +101,7 @@ Foundation. A copy of the license is included in the chapter entitled + + + +-Other API's used by media infrastructure drivers ++Remote Controller API + + &sub-remote_controllers; + +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 @@ ++
++LIRC Device Interface ++ ++ ++
++Introduction ++ ++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. ++ ++Example dmesg output upon a driver registering w/LIRC: ++
++ $ dmesg |grep lirc_dev ++ lirc_dev: IR Remote Control driver registered, major 248 ++ rc rc0: lirc_dev: driver ir-lirc-codec (mceusb) registered at minor = 0 ++
++ ++What you should see for a chardev: ++
++ $ ls -l /dev/lirc* ++ crw-rw---- 1 root root 248, 0 Jul 2 22:20 /dev/lirc0 ++
++
++ ++
++LIRC read fop ++ ++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 . The generally ++preferred mode is LIRC_MODE_MODE2, in which packets containing an int value ++describing an IR signal are read from the chardev. ++ ++See also http://www.lirc.org/html/technical.html for more info. ++
++ ++
++LIRC write fop ++ ++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. ++
++ ++
++LIRC ioctl fop ++ ++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. ++ ++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. ++ ++ ++ ++ LIRC_GET_FEATURES ++ ++ 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. ++ ++ ++ ++ LIRC_GET_SEND_MODE ++ ++ Get supported transmit mode. Only LIRC_MODE_PULSE is supported by lircd. ++ ++ ++ ++ LIRC_GET_REC_MODE ++ ++ Get supported receive modes. Only LIRC_MODE_MODE2 and LIRC_MODE_LIRCCODE ++ are supported by lircd. ++ ++ ++ ++ LIRC_GET_SEND_CARRIER ++ ++ Get carrier frequency (in Hz) currently used for transmit. ++ ++ ++ ++ LIRC_GET_REC_CARRIER ++ ++ Get carrier frequency (in Hz) currently used for IR reception. ++ ++ ++ ++ LIRC_{G,S}ET_{SEND,REC}_DUTY_CYCLE ++ ++ 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. ++ ++ ++ ++ LIRC_GET_REC_RESOLUTION ++ ++ 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. ++ ++ ++ ++ LIRC_GET_M{IN,AX}_TIMEOUT ++ ++ 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. ++ ++ ++ ++ LIRC_GET_M{IN,AX}_FILTER_{PULSE,SPACE} ++ ++ 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. ++ ++ ++ ++ LIRC_GET_LENGTH ++ ++ 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. ++ ++ ++ ++ LIRC_SET_{SEND,REC}_MODE ++ ++ Set send/receive mode. Largely obsolete for send, as only ++ LIRC_MODE_PULSE is supported. ++ ++ ++ ++ LIRC_SET_{SEND,REC}_CARRIER ++ ++ Set send/receive carrier (in Hz). ++ ++ ++ ++ LIRC_SET_TRANSMITTER_MASK ++ ++ 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. ++ ++ ++ ++ LIRC_SET_REC_TIMEOUT ++ ++ 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. ++ ++ ++ ++ LIRC_SET_REC_TIMEOUT_REPORTS ++ ++ Enable (1) or disable (0) timeout reports in LIRC_MODE_MODE2. By ++ default, timeout reports should be turned off. ++ ++ ++ ++ LIRC_SET_REC_FILTER_{,PULSE,SPACE} ++ ++ 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. ++ ++ ++ ++ LIRC_SET_MEASURE_CARRIER_MODE ++ ++ 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. ++ ++ ++ ++ LIRC_SET_REC_{DUTY_CYCLE,CARRIER}_RANGE ++ ++ 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. ++ ++ ++ ++ LIRC_NOTIFY_DECODE ++ ++ 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. ++ ++ ++ ++ LIRC_SETUP_{START,END} ++ ++ 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. ++ ++ ++ ++ ++
++
+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. + This program demonstrates how to replace the keymap tables. + &sub-keytable-c; + ++ ++&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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#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 "); ++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 ++ * ++ * 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 ++ ++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 "); +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 ++ * ++ * 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 ++ ++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 "); +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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define DRIVER_VERSION "1.91" ++#define DRIVER_AUTHOR "Jarod Wilson " ++#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 +-#include ++#include + #include + + #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 + #include +-#include ++#include + #include + + #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, ¶ms); + 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, ¶ms); ++ 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 + #include + #include ++#include + + #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 + + #include "cx88.h" ++#include + #include + + #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 + #include + #include ++#include + #if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE) + #include + #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 + #include + +-#include ++#include + #include + + /* ----------------------------------------------------------------------- */ +@@ -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 ++#include ++ ++#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 ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++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 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 +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 +--- + +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 +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 +--- + +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 +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 +--- + +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 +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 +--- + +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 "); + 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 ++Greg Kroah-Hartman ++ +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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#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 ++ * ++ * 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 ++#include ++ ++/* 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 ++ * modified for PixelView (BT878P+W/FM) by ++ * Michal Kochanowicz ++ * Christoph Bartelmus ++ * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by ++ * Ulrich Mueller ++ * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by ++ * Stefan Jahn ++ * modified for inclusion into kernel sources by ++ * Jerome Brock ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++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<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 ++ * ++ * ++ * This driver was derived from: ++ * Paul Miller ++ * "lirc_atiusb" module ++ * Vladimir Dergachev 's 2002 ++ * "USB ATI Remote support" (input device) ++ * Adrian Dewhurst 's 2002 ++ * "USB StreamZap remote driver" (LIRC) ++ * Artur Lipowski '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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++ ++/* module identification */ ++#define DRIVER_VERSION "0.1" ++#define DRIVER_AUTHOR \ ++ "Jan M. Hochstein " ++#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 ++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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++ ++#define MOD_AUTHOR "Venky Raju " ++#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 ++ * ++ * 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 : ++ * 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 ++ * 2009/12/24 James Edwards implemeted support ++ * for ITE8704/ITE8718, on my machine, the DSDT reports 8704, but the ++ * chip identifies as 18. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++#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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++#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 ++ ++#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< ++ * Tim Davies ++ * ++ * This driver was derived from: ++ * Venky Raju ++ * "lirc_imon - "LIRC/VFD driver for Ahanix/Soundgraph IMON IR/VFD" ++ * Paul Miller 's 2003-2004 ++ * "lirc_atiusb - USB remote support for LIRC" ++ * Culver Consulting Services '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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++ ++#define MOD_AUTHOR "Oliver Stabel , " \ ++ "Tim Davies " ++#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 ++ * Copyright (C) 1998 Trent Piepho ++ * Copyright (C) 1998 Ben Pfaff ++ * Copyright (C) 1999 Christoph Bartelmus ++ * Copyright (C) 2007 Andrei Tanas (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 July 2001 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#ifdef CONFIG_LIRC_SERIAL_NSLU2 ++#include ++#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 ++#include ++ ++#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 ). ++ * 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 ++ * 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 ++ . */ ++ ++ /* ++ * 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 ++ * ++ * 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 : ++ * added timeout and relaxed pulse detection, removed gap bug ++ * ++ * 2000/12/15 Christoph Bartelmus : ++ * 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 : ++ * added support for StrongARM SA1100 embedded microprocessor ++ * parts cut'n'pasted from sa1100_ir.c (C) 2000 Russell King ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef LIRC_ON_SA1100 ++#include ++#ifdef CONFIG_SA1100_COLLIE ++#include ++#include ++#endif ++#endif ++ ++#include ++ ++#include ++#include ++ ++/* 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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#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 ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++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 ++ * modified for PixelView (BT878P+W/FM) by ++ * Michal Kochanowicz ++ * Christoph Bartelmus ++ * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by ++ * Ulrich Mueller ++ * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by ++ * Stefan Jahn ++ * modified for inclusion into kernel sources by ++ * Jerome Brock ++ * modified for Leadtek Winfast PVR2000 by ++ * Thomas Reitmayr (treitmayr@yahoo.com) ++ * modified for Hauppauge PVR-150 IR TX device by ++ * Mark Weaver ++ * changed name from lirc_pvr150 to lirc_zilog, works on more than pvr-150 ++ * Jarod Wilson ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++ ++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 () { + 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 () { + 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" +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 +--- +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 = ®_rule->freq_range; +- power_rule = ®_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, + ®_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 = ®_rule->power_rule; + freq_range = ®_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(®_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 +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 +--- + +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 + #include + #include ++#include + #include + #include + #include +@@ -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 +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 +Signed-off-by: Matthew Garrett +Signed-off-by: Jesse Barnes +--- + +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 +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 + #include + #include ++#include + #include + #include + #include +@@ -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 +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 +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 +Cc: Larry Finger +Cc: Michael Buesch +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 +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 +--- + 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 +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 +--- + 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 +